Merge "Revert "Revert "Add missing SwappyVk_uninjectTracer function"""
diff --git a/GameActivity/build.gradle b/GameActivity/build.gradle
deleted file mode 100644
index 514a0bc..0000000
--- a/GameActivity/build.gradle
+++ /dev/null
@@ -1,50 +0,0 @@
-plugins {
- id 'com.android.library'
-}
-
-android {
- compileSdkVersion 28
-
- defaultConfig {
- minSdkVersion 16
- targetSdkVersion 28
- versionCode 1
- versionName "1.0"
-
- testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
- consumerProguardFiles "consumer-rules.pro"
- }
-
- buildTypes {
- release {
- minifyEnabled false
- proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
- }
- }
- compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_1_8
- }
-
-// TODO(florianrival@) Enabling this will trigger "Cannot query the value of this property because it has no value available." error.
-// buildFeatures {
-// prefabPublishing true
-// }
-// prefab {
-// gameactivity {
-// headers "src/main/cpp/include"
-// }
-// }
-}
-
-dependencies {
- implementation 'androidx.appcompat:appcompat:1.2.0'
- implementation 'androidx.core:core:1.5.0'
- implementation 'com.google.android.material:material:1.2.1'
- testImplementation 'junit:junit:4.+'
- androidTestImplementation 'androidx.test.ext:junit:1.1.2'
- androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
-}
-repositories {
- mavenCentral()
-}
diff --git a/GameActivity/prefab-src/modules/game-activity/include/game-text-input/gamecommon.h b/GameActivity/prefab-src/modules/game-activity/include/game-text-input/gamecommon.h
deleted file mode 120000
index 538d533..0000000
--- a/GameActivity/prefab-src/modules/game-activity/include/game-text-input/gamecommon.h
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../../GameTextInput/prefab-src/modules/game-text-input/include/game-text-input/gamecommon.h
\ No newline at end of file
diff --git a/GameActivity/prefab-src/modules/game-activity/include/game-text-input/gametextinput.cpp b/GameActivity/prefab-src/modules/game-activity/include/game-text-input/gametextinput.cpp
deleted file mode 120000
index 50085f1..0000000
--- a/GameActivity/prefab-src/modules/game-activity/include/game-text-input/gametextinput.cpp
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../../GameTextInput/prefab-src/modules/game-text-input/include/game-text-input/gametextinput.cpp
\ No newline at end of file
diff --git a/GameActivity/prefab-src/modules/game-activity/include/game-text-input/gametextinput.h b/GameActivity/prefab-src/modules/game-activity/include/game-text-input/gametextinput.h
deleted file mode 120000
index 7eccf87..0000000
--- a/GameActivity/prefab-src/modules/game-activity/include/game-text-input/gametextinput.h
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../../GameTextInput/prefab-src/modules/game-text-input/include/game-text-input/gametextinput.h
\ No newline at end of file
diff --git a/GameActivity/src/main/java/com/google/androidgamesdk/gametextinput b/GameActivity/src/main/java/com/google/androidgamesdk/gametextinput
deleted file mode 120000
index a2f27ca..0000000
--- a/GameActivity/src/main/java/com/google/androidgamesdk/gametextinput
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../../../GameTextInput/src/main/java/com/google/androidgamesdk/gametextinput
\ No newline at end of file
diff --git a/GameController/src/main/cpp/GameControllerMappingUtils.cpp b/GameController/src/main/cpp/GameControllerMappingUtils.cpp
deleted file mode 100644
index ca09e8d..0000000
--- a/GameController/src/main/cpp/GameControllerMappingUtils.cpp
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "GameControllerMappingUtils.h"
-
-#include "GameControllerManager.h"
-
-namespace paddleboat {
-MappingTableSearch::MappingTableSearch()
- : mappingRoot(nullptr),
- vendorId(0),
- productId(0),
- minApi(0),
- maxApi(0),
- tableIndex(0),
- mapEntryCount(0),
- tableEntryCount(0),
- tableMaxEntryCount(GameControllerManager::getRemapTableSize()) {}
-
-MappingTableSearch::MappingTableSearch(
- Paddleboat_Controller_Mapping_Data *mapRoot, int32_t entryCount)
- : mappingRoot(mapRoot),
- vendorId(0),
- productId(0),
- minApi(0),
- maxApi(0),
- tableIndex(0),
- mapEntryCount(0),
- tableEntryCount(entryCount),
- tableMaxEntryCount(GameControllerManager::getRemapTableSize()) {}
-
-void MappingTableSearch::initSearchParameters(const int32_t newVendorId,
- const int32_t newProductId,
- const int32_t newMinApi,
- const int32_t newMaxApi) {
- vendorId = newVendorId;
- productId = newProductId;
- minApi = newMinApi;
- maxApi = newMaxApi;
- tableIndex = 0;
-}
-
-bool GameControllerMappingUtils::findMatchingMapEntry(
- MappingTableSearch *searchEntry) {
- int32_t currentIndex = 0;
-
- // Starting out with a linear search. Updating the map table is something
- // that should only ever be done once at startup, if it actually takes an
- // appreciable time to execute when working with a big remap dictionary,
- // this is low-hanging fruit to optimize.
- const Paddleboat_Controller_Mapping_Data *mapRoot =
- searchEntry->mappingRoot;
- while (currentIndex < searchEntry->tableEntryCount) {
- const Paddleboat_Controller_Mapping_Data &mapEntry =
- mapRoot[currentIndex];
- if (mapEntry.vendorId > searchEntry->vendorId) {
- // Passed by the search vendorId value, so we don't already exist in
- // the table, set the current index as the insert point and bail
- searchEntry->tableIndex = currentIndex;
- return false;
- } else if (searchEntry->vendorId == mapEntry.vendorId) {
- if (mapEntry.productId > searchEntry->productId) {
- // Passed by the search productId value, so we don't already
- // exist in the table, set the current index as the insert point
- // and bail
- searchEntry->tableIndex = currentIndex;
- return false;
- } else if (searchEntry->productId == mapEntry.productId) {
- // Any overlap of the min/max API range is treated as matching
- // an existing entry
- if ((searchEntry->minApi >= mapEntry.minimumEffectiveApiLevel &&
- searchEntry->minApi <=
- mapEntry.maximumEffectiveApiLevel) ||
- (searchEntry->minApi >= mapEntry.minimumEffectiveApiLevel &&
- mapEntry.maximumEffectiveApiLevel == 0)) {
- searchEntry->tableIndex = currentIndex;
- return true;
- }
- }
- }
- ++currentIndex;
- }
- searchEntry->tableIndex = currentIndex;
- return false;
-}
-
-bool GameControllerMappingUtils::insertMapEntry(
- const Paddleboat_Controller_Mapping_Data *mappingData,
- MappingTableSearch *searchEntry) {
- bool insertSuccess = false;
- // Verify there is room in the table for another entry
- if (searchEntry->tableEntryCount < searchEntry->tableMaxEntryCount &&
- searchEntry->tableIndex < searchEntry->tableMaxEntryCount) {
- // Empty table, or inserting at the end, no relocation of existing data
- // required, otherwise shift existing data down starting at the insert
- // index.
- if (!(searchEntry->tableEntryCount == 0 ||
- searchEntry->tableIndex == searchEntry->tableEntryCount)) {
- const size_t copySize =
- (searchEntry->tableEntryCount - searchEntry->tableIndex) *
- sizeof(Paddleboat_Controller_Mapping_Data);
- memmove(&searchEntry->mappingRoot[searchEntry->tableIndex + 1],
- &searchEntry->mappingRoot[searchEntry->tableIndex],
- copySize);
- }
- // Insert the new data
- memcpy(&searchEntry->mappingRoot[searchEntry->tableIndex], mappingData,
- sizeof(Paddleboat_Controller_Mapping_Data));
- insertSuccess = true;
- }
- return insertSuccess;
-}
-
-const Paddleboat_Controller_Mapping_Data *
-GameControllerMappingUtils::validateMapTable(
- const Paddleboat_Controller_Mapping_Data *mappingRoot,
- const int32_t tableEntryCount) {
- // The map table is always assumed to be sorted by increasing vendorId. Each
- // sequence of entries with the same vendorId are sorted by increasing
- // productId. Multiple entries with the same vendorId/productId range are
- // sorted by increasing min/max API ranges. vendorId
- // productId
- // minApi
- int32_t currentIndex = 0;
- int32_t previousVendorId = -1;
- while (currentIndex < tableEntryCount) {
- if (mappingRoot[currentIndex].vendorId < previousVendorId) {
- // failure in vendorId order, return the offending entry
- return &mappingRoot[currentIndex];
- }
-
- int32_t previousProductId = mappingRoot[currentIndex].productId;
- int32_t previousMinApi =
- mappingRoot[currentIndex].minimumEffectiveApiLevel;
- int32_t previousMaxApi =
- mappingRoot[currentIndex].maximumEffectiveApiLevel;
- previousVendorId = mappingRoot[currentIndex++].vendorId;
-
- while (currentIndex < tableEntryCount &&
- mappingRoot[currentIndex].vendorId == previousVendorId) {
- while (currentIndex < tableEntryCount &&
- mappingRoot[currentIndex].productId == previousProductId) {
- if (mappingRoot[currentIndex].minimumEffectiveApiLevel <
- previousMinApi ||
- mappingRoot[currentIndex].minimumEffectiveApiLevel <
- previousMaxApi) {
- // failure in API order, return the offending entry
- return &mappingRoot[currentIndex];
- }
- previousMinApi =
- mappingRoot[currentIndex].minimumEffectiveApiLevel;
- previousMaxApi =
- mappingRoot[currentIndex++].maximumEffectiveApiLevel;
- }
- if (mappingRoot[currentIndex].productId < previousProductId) {
- // failure in productId order, return the offending entry
- return &mappingRoot[currentIndex];
- }
- previousProductId = mappingRoot[currentIndex++].productId;
- }
- }
-
- // Validation success, return nullptr (no offending entries to return)
- return nullptr;
-}
-} // namespace paddleboat
diff --git a/GameController/src/main/cpp/GameControllerMappingUtils.h b/GameController/src/main/cpp/GameControllerMappingUtils.h
deleted file mode 100644
index dbd5328..0000000
--- a/GameController/src/main/cpp/GameControllerMappingUtils.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-
-#ifndef PADDLEBOAT_H
-#include "paddleboat.h"
-#endif
-
-namespace paddleboat {
-class MappingTableSearch {
- public:
- MappingTableSearch();
-
- MappingTableSearch(Paddleboat_Controller_Mapping_Data *mapRoot,
- int32_t entryCount);
-
- void initSearchParameters(const int32_t newVendorId,
- const int32_t newProductId,
- const int32_t newMinApi, const int32_t newMaxApi);
-
- Paddleboat_Controller_Mapping_Data *mappingRoot;
- int32_t vendorId;
- int32_t productId;
- int32_t minApi;
- int32_t maxApi;
- int32_t tableIndex;
- int32_t mapEntryCount;
- int32_t tableEntryCount;
- int32_t tableMaxEntryCount;
-};
-
-class GameControllerMappingUtils {
- public:
- static bool findMatchingMapEntry(MappingTableSearch *searchEntry);
-
- static bool insertMapEntry(
- const Paddleboat_Controller_Mapping_Data *mappingData,
- MappingTableSearch *searchEntry);
-
- static const Paddleboat_Controller_Mapping_Data *validateMapTable(
- const Paddleboat_Controller_Mapping_Data *mappingRoot,
- const int32_t tableEntryCount);
-};
-} // namespace paddleboat
diff --git a/GameController/src/main/cpp/InternalControllerTable.cpp b/GameController/src/main/cpp/InternalControllerTable.cpp
deleted file mode 100644
index e970377..0000000
--- a/GameController/src/main/cpp/InternalControllerTable.cpp
+++ /dev/null
@@ -1,1005 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "InternalControllerTable.h"
-
-#include <android/input.h>
-#include <android/keycodes.h>
-
-#define ARRAY_COUNTOF(array) (sizeof(array) / sizeof(array[0]))
-
-#define PADDLEBOAT_AXIS_BUTTON_DPAD_UP 0
-#define PADDLEBOAT_AXIS_BUTTON_DPAD_LEFT 1
-#define PADDLEBOAT_AXIS_BUTTON_DPAD_DOWN 2
-#define PADDLEBOAT_AXIS_BUTTON_DPAD_RIGHT 3
-#define PADDLEBOAT_AXIS_BUTTON_L2 9
-#define PADDLEBOAT_AXIS_BUTTON_R2 12
-
-namespace paddleboat {
-static const Paddleboat_Controller_Mapping_Data pb_internal_controller_map[] = {
-
- // Microsoft Xbox 360 controller (usb)
- {16,
- 0,
- 0x0045e,
- 0x028e,
- PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD,
- {
- /* LX */ AMOTION_EVENT_AXIS_X,
- /* LY */ AMOTION_EVENT_AXIS_Y,
- /* RX */ AMOTION_EVENT_AXIS_Z,
- /* RY */ AMOTION_EVENT_AXIS_RZ,
- /* L1 */ PADDLEBOAT_AXIS_IGNORED,
- /* L2 */ AMOTION_EVENT_AXIS_BRAKE,
- /* R1 */ PADDLEBOAT_AXIS_IGNORED,
- /* R2 */ AMOTION_EVENT_AXIS_GAS,
- /* HX */ AMOTION_EVENT_AXIS_HAT_X,
- /* HY */ AMOTION_EVENT_AXIS_HAT_Y,
- },
- {
- /* LX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* LY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L2 */ PADDLEBOAT_AXIS_BUTTON_L2,
- /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* R2 */ PADDLEBOAT_AXIS_BUTTON_R2,
- /* HX */ PADDLEBOAT_AXIS_BUTTON_DPAD_RIGHT,
- /* HY */ PADDLEBOAT_AXIS_BUTTON_DPAD_DOWN,
- },
- {
- /* LX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* LY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* R2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* HX */ PADDLEBOAT_AXIS_BUTTON_DPAD_LEFT,
- /* HY */ PADDLEBOAT_AXIS_BUTTON_DPAD_UP,
- },
- {/* UP */ AKEYCODE_DPAD_UP,
- /* LEFT */ AKEYCODE_DPAD_LEFT,
- /* DOWN */ AKEYCODE_DPAD_DOWN,
- /* RIGHT */ AKEYCODE_DPAD_RIGHT,
- /* A */ AKEYCODE_BUTTON_A,
- /* B */ AKEYCODE_BUTTON_B,
- /* X */ AKEYCODE_BUTTON_X,
- /* Y */ AKEYCODE_BUTTON_Y,
- /* L1 */ AKEYCODE_BUTTON_L1,
- /* L2 */ AKEYCODE_BUTTON_L2,
- /* L3 */ AKEYCODE_BUTTON_THUMBL,
- /* R1 */ AKEYCODE_BUTTON_R1,
- /* R2 */ AKEYCODE_BUTTON_R2,
- /* R3 */ AKEYCODE_BUTTON_THUMBR,
- /* SELECT */ AKEYCODE_BUTTON_SELECT,
- /* START */ AKEYCODE_BUTTON_START,
- /* SYSTEM */ AKEYCODE_BUTTON_MODE,
- /* TOUCHP */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX1 */ AKEYCODE_MEDIA_RECORD,
- /* AUX2 */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX3 */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX4 */ PADDLEBOAT_BUTTON_IGNORED}},
- // Microsoft Xbox One non-BT controller (usb)
- {16,
- 0,
- 0x0045e,
- 0x02d1,
- PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD,
- {
- /* LX */ AMOTION_EVENT_AXIS_X,
- /* LY */ AMOTION_EVENT_AXIS_Y,
- /* RX */ AMOTION_EVENT_AXIS_Z,
- /* RY */ AMOTION_EVENT_AXIS_RZ,
- /* L1 */ PADDLEBOAT_AXIS_IGNORED,
- /* L2 */ AMOTION_EVENT_AXIS_BRAKE,
- /* R1 */ PADDLEBOAT_AXIS_IGNORED,
- /* R2 */ AMOTION_EVENT_AXIS_GAS,
- /* HX */ AMOTION_EVENT_AXIS_HAT_X,
- /* HY */ AMOTION_EVENT_AXIS_HAT_Y,
- },
- {
- /* LX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* LY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L2 */ PADDLEBOAT_AXIS_BUTTON_L2,
- /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* R2 */ PADDLEBOAT_AXIS_BUTTON_R2,
- /* HX */ PADDLEBOAT_AXIS_BUTTON_DPAD_RIGHT,
- /* HY */ PADDLEBOAT_AXIS_BUTTON_DPAD_DOWN,
- },
- {
- /* LX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* LY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* R2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* HX */ PADDLEBOAT_AXIS_BUTTON_DPAD_LEFT,
- /* HY */ PADDLEBOAT_AXIS_BUTTON_DPAD_UP,
- },
- {/* UP */ AKEYCODE_DPAD_UP,
- /* LEFT */ AKEYCODE_DPAD_LEFT,
- /* DOWN */ AKEYCODE_DPAD_DOWN,
- /* RIGHT */ AKEYCODE_DPAD_RIGHT,
- /* A */ AKEYCODE_BUTTON_A,
- /* B */ AKEYCODE_BUTTON_B,
- /* X */ AKEYCODE_BUTTON_X,
- /* Y */ AKEYCODE_BUTTON_Y,
- /* L1 */ AKEYCODE_BUTTON_L1,
- /* L2 */ AKEYCODE_BUTTON_L2,
- /* L3 */ AKEYCODE_BUTTON_THUMBL,
- /* R1 */ AKEYCODE_BUTTON_R1,
- /* R2 */ AKEYCODE_BUTTON_R2,
- /* R3 */ AKEYCODE_BUTTON_THUMBR,
- /* SELECT */ AKEYCODE_BUTTON_SELECT,
- /* START */ AKEYCODE_BUTTON_START,
- /* SYSTEM */ AKEYCODE_BUTTON_MODE,
- /* TOUCHP */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX1 */ AKEYCODE_MEDIA_RECORD,
- /* AUX2 */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX3 */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX4 */ PADDLEBOAT_BUTTON_IGNORED}},
- // 8BitDo Arcade Stick, XInput mode, Bluetooth
- {16,
- 0,
- 0x0045e,
- 0x02e0,
- PADDLEBOAT_CONTROLLER_LAYOUT_ARCADE_STICK,
- {
- /* LX */ AMOTION_EVENT_AXIS_X,
- /* LY */ AMOTION_EVENT_AXIS_Y,
- /* RX */ AMOTION_EVENT_AXIS_Z,
- /* RY */ AMOTION_EVENT_AXIS_RZ,
- /* L1 */ PADDLEBOAT_AXIS_IGNORED,
- /* L2 */ AMOTION_EVENT_AXIS_BRAKE,
- /* R1 */ PADDLEBOAT_AXIS_IGNORED,
- /* R2 */ AMOTION_EVENT_AXIS_GAS,
- /* HX */ AMOTION_EVENT_AXIS_HAT_X,
- /* HY */ AMOTION_EVENT_AXIS_HAT_Y,
- },
- {
- /* LX */ PADDLEBOAT_AXIS_BUTTON_DPAD_RIGHT,
- /* LY */ PADDLEBOAT_AXIS_BUTTON_DPAD_DOWN,
- /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L2 */ PADDLEBOAT_AXIS_BUTTON_L2,
- /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* R2 */ PADDLEBOAT_AXIS_BUTTON_R2,
- /* HX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* HY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- },
- {
- /* LX */ PADDLEBOAT_AXIS_BUTTON_DPAD_LEFT,
- /* LY */ PADDLEBOAT_AXIS_BUTTON_DPAD_UP,
- /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* R2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* HX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* HY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- },
- {/* UP */ AKEYCODE_DPAD_UP,
- /* LEFT */ AKEYCODE_DPAD_LEFT,
- /* DOWN */ AKEYCODE_DPAD_DOWN,
- /* RIGHT */ AKEYCODE_DPAD_RIGHT,
- /* A */ AKEYCODE_BUTTON_A,
- /* B */ AKEYCODE_BUTTON_B,
- /* X */ AKEYCODE_BUTTON_X,
- /* Y */ AKEYCODE_BUTTON_Y,
- /* L1 */ AKEYCODE_BUTTON_L1,
- /* L2 */ AKEYCODE_BUTTON_L2,
- /* L3 */ PADDLEBOAT_BUTTON_IGNORED,
- /* R1 */ AKEYCODE_BUTTON_R1,
- /* R2 */ AKEYCODE_BUTTON_R2,
- /* R3 */ PADDLEBOAT_BUTTON_IGNORED,
- /* SELECT */ AKEYCODE_BUTTON_SELECT,
- /* START */ AKEYCODE_BUTTON_START,
- /* SYSTEM */ AKEYCODE_BUTTON_MODE,
- /* TOUCHP */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX1 */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX2 */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX3 */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX4 */ PADDLEBOAT_BUTTON_IGNORED}},
- // Microsoft Xbox One BT controller (usb)
- {16,
- 0,
- 0x0045e,
- 0x02ea,
- PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD,
- {
- /* LX */ AMOTION_EVENT_AXIS_X,
- /* LY */ AMOTION_EVENT_AXIS_Y,
- /* RX */ AMOTION_EVENT_AXIS_Z,
- /* RY */ AMOTION_EVENT_AXIS_RZ,
- /* L1 */ PADDLEBOAT_AXIS_IGNORED,
- /* L2 */ AMOTION_EVENT_AXIS_BRAKE,
- /* R1 */ PADDLEBOAT_AXIS_IGNORED,
- /* R2 */ AMOTION_EVENT_AXIS_GAS,
- /* HX */ AMOTION_EVENT_AXIS_HAT_X,
- /* HY */ AMOTION_EVENT_AXIS_HAT_Y,
- },
- {
- /* LX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* LY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L2 */ PADDLEBOAT_AXIS_BUTTON_L2,
- /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* R2 */ PADDLEBOAT_AXIS_BUTTON_R2,
- /* HX */ PADDLEBOAT_AXIS_BUTTON_DPAD_RIGHT,
- /* HY */ PADDLEBOAT_AXIS_BUTTON_DPAD_DOWN,
- },
- {
- /* LX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* LY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* R2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* HX */ PADDLEBOAT_AXIS_BUTTON_DPAD_LEFT,
- /* HY */ PADDLEBOAT_AXIS_BUTTON_DPAD_UP,
- },
- {/* UP */ AKEYCODE_DPAD_UP,
- /* LEFT */ AKEYCODE_DPAD_LEFT,
- /* DOWN */ AKEYCODE_DPAD_DOWN,
- /* RIGHT */ AKEYCODE_DPAD_RIGHT,
- /* A */ AKEYCODE_BUTTON_A,
- /* B */ AKEYCODE_BUTTON_B,
- /* X */ AKEYCODE_BUTTON_X,
- /* Y */ AKEYCODE_BUTTON_Y,
- /* L1 */ AKEYCODE_BUTTON_L1,
- /* L2 */ AKEYCODE_BUTTON_L2,
- /* L3 */ AKEYCODE_BUTTON_THUMBL,
- /* R1 */ AKEYCODE_BUTTON_R1,
- /* R2 */ AKEYCODE_BUTTON_R2,
- /* R3 */ AKEYCODE_BUTTON_THUMBR,
- /* SELECT */ AKEYCODE_BUTTON_SELECT,
- /* START */ AKEYCODE_BUTTON_START,
- /* SYSTEM */ AKEYCODE_BUTTON_MODE,
- /* TOUCHP */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX1 */ AKEYCODE_MEDIA_RECORD,
- /* AUX2 */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX3 */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX4 */ PADDLEBOAT_BUTTON_IGNORED}},
- // Microsoft Xbox One BT controller (bluetooth)
- {16,
- 0,
- 0x0045e,
- 0x02fd,
- PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD,
- {
- /* LX */ AMOTION_EVENT_AXIS_X,
- /* LY */ AMOTION_EVENT_AXIS_Y,
- /* RX */ AMOTION_EVENT_AXIS_Z,
- /* RY */ AMOTION_EVENT_AXIS_RZ,
- /* L1 */ PADDLEBOAT_AXIS_IGNORED,
- /* L2 */ AMOTION_EVENT_AXIS_BRAKE,
- /* R1 */ PADDLEBOAT_AXIS_IGNORED,
- /* R2 */ AMOTION_EVENT_AXIS_GAS,
- /* HX */ AMOTION_EVENT_AXIS_HAT_X,
- /* HY */ AMOTION_EVENT_AXIS_HAT_Y,
- },
- {
- /* LX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* LY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L2 */ PADDLEBOAT_AXIS_BUTTON_L2,
- /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* R2 */ PADDLEBOAT_AXIS_BUTTON_R2,
- /* HX */ PADDLEBOAT_AXIS_BUTTON_DPAD_RIGHT,
- /* HY */ PADDLEBOAT_AXIS_BUTTON_DPAD_DOWN,
- },
- {
- /* LX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* LY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* R2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* HX */ PADDLEBOAT_AXIS_BUTTON_DPAD_LEFT,
- /* HY */ PADDLEBOAT_AXIS_BUTTON_DPAD_UP,
- },
- {/* UP */ AKEYCODE_DPAD_UP,
- /* LEFT */ AKEYCODE_DPAD_LEFT,
- /* DOWN */ AKEYCODE_DPAD_DOWN,
- /* RIGHT */ AKEYCODE_DPAD_RIGHT,
- /* A */ AKEYCODE_BUTTON_A,
- /* B */ AKEYCODE_BUTTON_B,
- /* X */ AKEYCODE_BUTTON_X,
- /* Y */ AKEYCODE_BUTTON_Y,
- /* L1 */ AKEYCODE_BUTTON_L1,
- /* L2 */ AKEYCODE_BUTTON_L2,
- /* L3 */ AKEYCODE_BUTTON_THUMBL,
- /* R1 */ AKEYCODE_BUTTON_R1,
- /* R2 */ AKEYCODE_BUTTON_R2,
- /* R3 */ AKEYCODE_BUTTON_THUMBR,
- /* SELECT */ AKEYCODE_BUTTON_SELECT,
- /* START */ AKEYCODE_BUTTON_START,
- /* SYSTEM */ AKEYCODE_BUTTON_MODE,
- /* TOUCHP */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX1 */ AKEYCODE_MEDIA_RECORD,
- /* AUX2 */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX3 */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX4 */ PADDLEBOAT_BUTTON_IGNORED}},
- // Microsoft Xbox Series controller (usb)
- {16,
- 0,
- 0x0045e,
- 0x0b12,
- PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD,
- {
- /* LX */ AMOTION_EVENT_AXIS_X,
- /* LY */ AMOTION_EVENT_AXIS_Y,
- /* RX */ AMOTION_EVENT_AXIS_Z,
- /* RY */ AMOTION_EVENT_AXIS_RZ,
- /* L1 */ PADDLEBOAT_AXIS_IGNORED,
- /* L2 */ AMOTION_EVENT_AXIS_BRAKE,
- /* R1 */ PADDLEBOAT_AXIS_IGNORED,
- /* R2 */ AMOTION_EVENT_AXIS_GAS,
- /* HX */ AMOTION_EVENT_AXIS_HAT_X,
- /* HY */ AMOTION_EVENT_AXIS_HAT_Y,
- },
- {
- /* LX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* LY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L2 */ PADDLEBOAT_AXIS_BUTTON_L2,
- /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* R2 */ PADDLEBOAT_AXIS_BUTTON_R2,
- /* HX */ PADDLEBOAT_AXIS_BUTTON_DPAD_RIGHT,
- /* HY */ PADDLEBOAT_AXIS_BUTTON_DPAD_DOWN,
- },
- {
- /* LX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* LY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* R2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* HX */ PADDLEBOAT_AXIS_BUTTON_DPAD_LEFT,
- /* HY */ PADDLEBOAT_AXIS_BUTTON_DPAD_UP,
- },
- {/* UP */ AKEYCODE_DPAD_UP,
- /* LEFT */ AKEYCODE_DPAD_LEFT,
- /* DOWN */ AKEYCODE_DPAD_DOWN,
- /* RIGHT */ AKEYCODE_DPAD_RIGHT,
- /* A */ AKEYCODE_BUTTON_A,
- /* B */ AKEYCODE_BUTTON_B,
- /* X */ AKEYCODE_BUTTON_X,
- /* Y */ AKEYCODE_BUTTON_Y,
- /* L1 */ AKEYCODE_BUTTON_L1,
- /* L2 */ AKEYCODE_BUTTON_L2,
- /* L3 */ AKEYCODE_BUTTON_THUMBL,
- /* R1 */ AKEYCODE_BUTTON_R1,
- /* R2 */ AKEYCODE_BUTTON_R2,
- /* R3 */ AKEYCODE_BUTTON_THUMBR,
- /* SELECT */ AKEYCODE_BUTTON_SELECT,
- /* START */ AKEYCODE_BUTTON_START,
- /* SYSTEM */ AKEYCODE_BUTTON_MODE,
- /* TOUCHP */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX1 */ AKEYCODE_MEDIA_RECORD,
- /* AUX2 */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX3 */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX4 */ PADDLEBOAT_BUTTON_IGNORED}},
- // Microsoft Xbox Series controller (bluetooth)
- {16,
- 0,
- 0x0045e,
- 0x0b13,
- PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD,
- {
- /* LX */ AMOTION_EVENT_AXIS_X,
- /* LY */ AMOTION_EVENT_AXIS_Y,
- /* RX */ AMOTION_EVENT_AXIS_Z,
- /* RY */ AMOTION_EVENT_AXIS_RZ,
- /* L1 */ PADDLEBOAT_AXIS_IGNORED,
- /* L2 */ AMOTION_EVENT_AXIS_BRAKE,
- /* R1 */ PADDLEBOAT_AXIS_IGNORED,
- /* R2 */ AMOTION_EVENT_AXIS_GAS,
- /* HX */ AMOTION_EVENT_AXIS_HAT_X,
- /* HY */ AMOTION_EVENT_AXIS_HAT_Y,
- },
- {
- /* LX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* LY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L2 */ PADDLEBOAT_AXIS_BUTTON_L2,
- /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* R2 */ PADDLEBOAT_AXIS_BUTTON_R2,
- /* HX */ PADDLEBOAT_AXIS_BUTTON_DPAD_RIGHT,
- /* HY */ PADDLEBOAT_AXIS_BUTTON_DPAD_DOWN,
- },
- {
- /* LX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* LY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* R2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* HX */ PADDLEBOAT_AXIS_BUTTON_DPAD_LEFT,
- /* HY */ PADDLEBOAT_AXIS_BUTTON_DPAD_UP,
- },
- {/* UP */ AKEYCODE_DPAD_UP,
- /* LEFT */ AKEYCODE_DPAD_LEFT,
- /* DOWN */ AKEYCODE_DPAD_DOWN,
- /* RIGHT */ AKEYCODE_DPAD_RIGHT,
- /* A */ AKEYCODE_BUTTON_A,
- /* B */ AKEYCODE_BUTTON_B,
- /* X */ AKEYCODE_BUTTON_X,
- /* Y */ AKEYCODE_BUTTON_Y,
- /* L1 */ AKEYCODE_BUTTON_L1,
- /* L2 */ AKEYCODE_BUTTON_L2,
- /* L3 */ AKEYCODE_BUTTON_THUMBL,
- /* R1 */ AKEYCODE_BUTTON_R1,
- /* R2 */ AKEYCODE_BUTTON_R2,
- /* R3 */ AKEYCODE_BUTTON_THUMBR,
- /* SELECT */ AKEYCODE_BUTTON_SELECT,
- /* START */ AKEYCODE_BUTTON_START,
- /* SYSTEM */ AKEYCODE_BUTTON_MODE,
- /* TOUCHP */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX1 */ AKEYCODE_MEDIA_RECORD,
- /* AUX2 */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX3 */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX4 */ PADDLEBOAT_BUTTON_IGNORED}},
-
- // Sony PlayStation 4 controller (usb/bluetooth)
- {16,
- 0,
- 0x054C,
- 0x05C4,
- PADDLEBOAT_CONTROLLER_LAYOUT_SHAPES | PADDLEBOAT_CONTROLLER_FLAG_TOUCHPAD,
- {
- /* LX */ AMOTION_EVENT_AXIS_X,
- /* LY */ AMOTION_EVENT_AXIS_Y,
- /* RX */ AMOTION_EVENT_AXIS_Z,
- /* RY */ AMOTION_EVENT_AXIS_RZ,
- /* L1 */ PADDLEBOAT_AXIS_IGNORED,
- /* L2 */ AMOTION_EVENT_AXIS_BRAKE,
- /* R1 */ PADDLEBOAT_AXIS_IGNORED,
- /* R2 */ AMOTION_EVENT_AXIS_GAS,
- /* HX */ AMOTION_EVENT_AXIS_HAT_X,
- /* HY */ AMOTION_EVENT_AXIS_HAT_Y,
- },
- {
- /* LX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* LY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L2 */ PADDLEBOAT_AXIS_BUTTON_L2,
- /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* R2 */ PADDLEBOAT_AXIS_BUTTON_R2,
- /* HX */ PADDLEBOAT_AXIS_BUTTON_DPAD_RIGHT,
- /* HY */ PADDLEBOAT_AXIS_BUTTON_DPAD_DOWN,
- },
- {
- /* LX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* LY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* R2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* HX */ PADDLEBOAT_AXIS_BUTTON_DPAD_LEFT,
- /* HY */ PADDLEBOAT_AXIS_BUTTON_DPAD_UP,
- },
- {/* UP */ AKEYCODE_DPAD_UP,
- /* LEFT */ AKEYCODE_DPAD_LEFT,
- /* DOWN */ AKEYCODE_DPAD_DOWN,
- /* RIGHT */ AKEYCODE_DPAD_RIGHT,
- /* A */ AKEYCODE_BUTTON_A,
- /* B */ AKEYCODE_BUTTON_B,
- /* X */ AKEYCODE_BUTTON_X,
- /* Y */ AKEYCODE_BUTTON_Y,
- /* L1 */ AKEYCODE_BUTTON_L1,
- /* L2 */ AKEYCODE_BUTTON_L2,
- /* L3 */ AKEYCODE_BUTTON_THUMBL,
- /* R1 */ AKEYCODE_BUTTON_R1,
- /* R2 */ AKEYCODE_BUTTON_R2,
- /* R3 */ AKEYCODE_BUTTON_THUMBR,
- /* SELECT */ AKEYCODE_BUTTON_SELECT,
- /* START */ AKEYCODE_BUTTON_START,
- /* SYSTEM */ AKEYCODE_BUTTON_MODE,
- /* TOUCHP */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX1 */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX2 */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX3 */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX4 */ PADDLEBOAT_BUTTON_IGNORED}},
- // Sony PlayStation 4 controller (usb/bluetooth) - alternate deviceId
- {16,
- 0,
- 0x054C,
- 0x09CC,
- PADDLEBOAT_CONTROLLER_LAYOUT_SHAPES | PADDLEBOAT_CONTROLLER_FLAG_TOUCHPAD,
- {
- /* LX */ AMOTION_EVENT_AXIS_X,
- /* LY */ AMOTION_EVENT_AXIS_Y,
- /* RX */ AMOTION_EVENT_AXIS_Z,
- /* RY */ AMOTION_EVENT_AXIS_RZ,
- /* L1 */ PADDLEBOAT_AXIS_IGNORED,
- /* L2 */ AMOTION_EVENT_AXIS_BRAKE,
- /* R1 */ PADDLEBOAT_AXIS_IGNORED,
- /* R2 */ AMOTION_EVENT_AXIS_GAS,
- /* HX */ AMOTION_EVENT_AXIS_HAT_X,
- /* HY */ AMOTION_EVENT_AXIS_HAT_Y,
- },
- {
- /* LX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* LY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L2 */ PADDLEBOAT_AXIS_BUTTON_L2,
- /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* R2 */ PADDLEBOAT_AXIS_BUTTON_R2,
- /* HX */ PADDLEBOAT_AXIS_BUTTON_DPAD_RIGHT,
- /* HY */ PADDLEBOAT_AXIS_BUTTON_DPAD_DOWN,
- },
- {
- /* LX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* LY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* R2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* HX */ PADDLEBOAT_AXIS_BUTTON_DPAD_LEFT,
- /* HY */ PADDLEBOAT_AXIS_BUTTON_DPAD_UP,
- },
- {/* UP */ AKEYCODE_DPAD_UP,
- /* LEFT */ AKEYCODE_DPAD_LEFT,
- /* DOWN */ AKEYCODE_DPAD_DOWN,
- /* RIGHT */ AKEYCODE_DPAD_RIGHT,
- /* A */ AKEYCODE_BUTTON_A,
- /* B */ AKEYCODE_BUTTON_B,
- /* X */ AKEYCODE_BUTTON_X,
- /* Y */ AKEYCODE_BUTTON_Y,
- /* L1 */ AKEYCODE_BUTTON_L1,
- /* L2 */ AKEYCODE_BUTTON_L2,
- /* L3 */ AKEYCODE_BUTTON_THUMBL,
- /* R1 */ AKEYCODE_BUTTON_R1,
- /* R2 */ AKEYCODE_BUTTON_R2,
- /* R3 */ AKEYCODE_BUTTON_THUMBR,
- /* SELECT */ AKEYCODE_BUTTON_SELECT,
- /* START */ AKEYCODE_BUTTON_START,
- /* SYSTEM */ AKEYCODE_BUTTON_MODE,
- /* TOUCHP */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX1 */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX2 */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX3 */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX4 */ PADDLEBOAT_BUTTON_IGNORED}},
- // Sony PlayStation 5 controller (usb/bluetooth) API <= 30
- {16,
- 30,
- 0x054C,
- 0x0ce6,
- PADDLEBOAT_CONTROLLER_LAYOUT_SHAPES,
- {
- /* LX */ AMOTION_EVENT_AXIS_X,
- /* LY */ AMOTION_EVENT_AXIS_Y,
- /* RX */ AMOTION_EVENT_AXIS_Z,
- /* RY */ AMOTION_EVENT_AXIS_RZ,
- /* L1 */ PADDLEBOAT_AXIS_IGNORED,
- /* L2 */ AMOTION_EVENT_AXIS_RX,
- /* R1 */ PADDLEBOAT_AXIS_IGNORED,
- /* R2 */ AMOTION_EVENT_AXIS_RY,
- /* HX */ AMOTION_EVENT_AXIS_HAT_X,
- /* HY */ AMOTION_EVENT_AXIS_HAT_Y,
- },
- {
- /* LX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* LY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L2 */ PADDLEBOAT_AXIS_BUTTON_L2,
- /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* R2 */ PADDLEBOAT_AXIS_BUTTON_R2,
- /* HX */ PADDLEBOAT_AXIS_BUTTON_DPAD_RIGHT,
- /* HY */ PADDLEBOAT_AXIS_BUTTON_DPAD_DOWN,
- },
- {
- /* LX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* LY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* R2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* HX */ PADDLEBOAT_AXIS_BUTTON_DPAD_LEFT,
- /* HY */ PADDLEBOAT_AXIS_BUTTON_DPAD_UP,
- },
- {/* UP */ AKEYCODE_DPAD_UP,
- /* LEFT */ AKEYCODE_DPAD_LEFT,
- /* DOWN */ AKEYCODE_DPAD_DOWN,
- /* RIGHT */ AKEYCODE_DPAD_RIGHT,
- /* A */ AKEYCODE_BUTTON_B,
- /* B */ AKEYCODE_BUTTON_C,
- /* X */ AKEYCODE_BUTTON_A,
- /* Y */ AKEYCODE_BUTTON_X,
- /* L1 */ AKEYCODE_BUTTON_Y,
- /* L2 */ PADDLEBOAT_BUTTON_IGNORED,
- /* L3 */ AKEYCODE_BUTTON_SELECT,
- /* R1 */ AKEYCODE_BUTTON_Z,
- /* R2 */ PADDLEBOAT_BUTTON_IGNORED,
- /* R3 */ AKEYCODE_BUTTON_START,
- /* SELECT */ AKEYCODE_BUTTON_L2,
- /* START */ AKEYCODE_BUTTON_R2,
- /* SYSTEM */ AKEYCODE_BUTTON_MODE,
- /* TOUCHP */ AKEYCODE_BUTTON_THUMBL,
- /* AUX1 */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX2 */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX3 */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX4 */ PADDLEBOAT_BUTTON_IGNORED}},
- // Sony PlayStation 5 controller (usb/bluetooth) API >= 31
- {31,
- 0,
- 0x054C,
- 0x0ce6,
- PADDLEBOAT_CONTROLLER_LAYOUT_SHAPES | PADDLEBOAT_CONTROLLER_FLAG_TOUCHPAD,
- {
- /* LX */ AMOTION_EVENT_AXIS_X,
- /* LY */ AMOTION_EVENT_AXIS_Y,
- /* RX */ AMOTION_EVENT_AXIS_Z,
- /* RY */ AMOTION_EVENT_AXIS_RZ,
- /* L1 */ PADDLEBOAT_AXIS_IGNORED,
- /* L2 */ AMOTION_EVENT_AXIS_BRAKE,
- /* R1 */ PADDLEBOAT_AXIS_IGNORED,
- /* R2 */ AMOTION_EVENT_AXIS_GAS,
- /* HX */ AMOTION_EVENT_AXIS_HAT_X,
- /* HY */ AMOTION_EVENT_AXIS_HAT_Y,
- },
- {
- /* LX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* LY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L2 */ PADDLEBOAT_AXIS_BUTTON_L2,
- /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* R2 */ PADDLEBOAT_AXIS_BUTTON_R2,
- /* HX */ PADDLEBOAT_AXIS_BUTTON_DPAD_RIGHT,
- /* HY */ PADDLEBOAT_AXIS_BUTTON_DPAD_DOWN,
- },
- {
- /* LX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* LY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* R2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* HX */ PADDLEBOAT_AXIS_BUTTON_DPAD_LEFT,
- /* HY */ PADDLEBOAT_AXIS_BUTTON_DPAD_UP,
- },
- {/* UP */ AKEYCODE_DPAD_UP,
- /* LEFT */ AKEYCODE_DPAD_LEFT,
- /* DOWN */ AKEYCODE_DPAD_DOWN,
- /* RIGHT */ AKEYCODE_DPAD_RIGHT,
- /* A */ AKEYCODE_BUTTON_A,
- /* B */ AKEYCODE_BUTTON_B,
- /* X */ AKEYCODE_BUTTON_X,
- /* Y */ AKEYCODE_BUTTON_Y,
- /* L1 */ AKEYCODE_BUTTON_L1,
- /* L2 */ AKEYCODE_BUTTON_L2,
- /* L3 */ AKEYCODE_BUTTON_THUMBL,
- /* R1 */ AKEYCODE_BUTTON_R1,
- /* R2 */ AKEYCODE_BUTTON_R2,
- /* R3 */ AKEYCODE_BUTTON_THUMBR,
- /* SELECT */ AKEYCODE_BUTTON_SELECT,
- /* START */ AKEYCODE_BUTTON_START,
- /* SYSTEM */ AKEYCODE_BUTTON_MODE,
- /* TOUCHP */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX1 */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX2 */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX3 */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX4 */ PADDLEBOAT_BUTTON_IGNORED}},
- // Nintendo Switch Pro controller (usb/bluetooth)
- {16,
- 0,
- 0x0057e,
- 0x2009,
- PADDLEBOAT_CONTROLLER_LAYOUT_REVERSE,
- {
- /* LX */ AMOTION_EVENT_AXIS_X,
- /* LY */ AMOTION_EVENT_AXIS_Y,
- /* RX */ AMOTION_EVENT_AXIS_Z,
- /* RY */ AMOTION_EVENT_AXIS_RZ,
- /* L1 */ PADDLEBOAT_AXIS_IGNORED,
- /* L2 */ AMOTION_EVENT_AXIS_BRAKE,
- /* R1 */ PADDLEBOAT_AXIS_IGNORED,
- /* R2 */ AMOTION_EVENT_AXIS_GAS,
- /* HX */ AMOTION_EVENT_AXIS_HAT_X,
- /* HY */ AMOTION_EVENT_AXIS_HAT_Y,
- },
- {
- /* LX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* LY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L2 */ PADDLEBOAT_AXIS_BUTTON_L2,
- /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* R2 */ PADDLEBOAT_AXIS_BUTTON_R2,
- /* HX */ PADDLEBOAT_AXIS_BUTTON_DPAD_RIGHT,
- /* HY */ PADDLEBOAT_AXIS_BUTTON_DPAD_DOWN,
- },
- {
- /* LX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* LY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* R2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* HX */ PADDLEBOAT_AXIS_BUTTON_DPAD_LEFT,
- /* HY */ PADDLEBOAT_AXIS_BUTTON_DPAD_UP,
- },
- {/* UP */ AKEYCODE_DPAD_UP,
- /* LEFT */ AKEYCODE_DPAD_LEFT,
- /* DOWN */ AKEYCODE_DPAD_DOWN,
- /* RIGHT */ AKEYCODE_DPAD_RIGHT,
- /* A */ AKEYCODE_BUTTON_B,
- /* B */ AKEYCODE_BUTTON_A,
- /* X */ AKEYCODE_BUTTON_Y,
- /* Y */ AKEYCODE_BUTTON_X,
- /* L1 */ AKEYCODE_BUTTON_L1,
- /* L2 */ AKEYCODE_BUTTON_L2,
- /* L3 */ AKEYCODE_BUTTON_THUMBL,
- /* R1 */ AKEYCODE_BUTTON_R1,
- /* R2 */ AKEYCODE_BUTTON_R2,
- /* R3 */ AKEYCODE_BUTTON_THUMBR,
- /* SELECT */ AKEYCODE_BUTTON_SELECT,
- /* START */ AKEYCODE_BUTTON_START,
- /* SYSTEM */ AKEYCODE_BUTTON_MODE,
- /* TOUCHP */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX1 */ AKEYCODE_MEDIA_RECORD,
- /* AUX2 */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX3 */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX4 */ PADDLEBOAT_BUTTON_IGNORED}},
- // Nvidia Shield TV controller (usb)
- {16,
- 0,
- 0x0955,
- 0x7210,
- PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD,
- {
- /* LX */ AMOTION_EVENT_AXIS_X,
- /* LY */ AMOTION_EVENT_AXIS_Y,
- /* RX */ AMOTION_EVENT_AXIS_Z,
- /* RY */ AMOTION_EVENT_AXIS_RZ,
- /* L1 */ PADDLEBOAT_AXIS_IGNORED,
- /* L2 */ AMOTION_EVENT_AXIS_BRAKE,
- /* R1 */ PADDLEBOAT_AXIS_IGNORED,
- /* R2 */ AMOTION_EVENT_AXIS_GAS,
- /* HX */ AMOTION_EVENT_AXIS_HAT_X,
- /* HY */ AMOTION_EVENT_AXIS_HAT_Y,
- },
- {
- /* LX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* LY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L2 */ PADDLEBOAT_AXIS_BUTTON_L2,
- /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* R2 */ PADDLEBOAT_AXIS_BUTTON_R2,
- /* HX */ PADDLEBOAT_AXIS_BUTTON_DPAD_RIGHT,
- /* HY */ PADDLEBOAT_AXIS_BUTTON_DPAD_DOWN,
- },
- {
- /* LX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* LY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* R2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* HX */ PADDLEBOAT_AXIS_BUTTON_DPAD_LEFT,
- /* HY */ PADDLEBOAT_AXIS_BUTTON_DPAD_UP,
- },
- {/* UP */ AKEYCODE_DPAD_UP,
- /* LEFT */ AKEYCODE_DPAD_LEFT,
- /* DOWN */ AKEYCODE_DPAD_DOWN,
- /* RIGHT */ AKEYCODE_DPAD_RIGHT,
- /* A */ AKEYCODE_BUTTON_A,
- /* B */ AKEYCODE_BUTTON_B,
- /* X */ AKEYCODE_BUTTON_X,
- /* Y */ AKEYCODE_BUTTON_Y,
- /* L1 */ AKEYCODE_BUTTON_L1,
- /* L2 */ AKEYCODE_BUTTON_L2,
- /* L3 */ AKEYCODE_BUTTON_THUMBL,
- /* R1 */ AKEYCODE_BUTTON_R1,
- /* R2 */ AKEYCODE_BUTTON_R2,
- /* R3 */ AKEYCODE_BUTTON_THUMBR,
- /* SELECT */ AKEYCODE_BUTTON_SELECT,
- /* START */ AKEYCODE_BUTTON_START,
- /* SYSTEM */ AKEYCODE_BUTTON_MODE,
- /* TOUCHP */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX1 */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX2 */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX3 */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX4 */ PADDLEBOAT_BUTTON_IGNORED}},
-
- // Google Stadia controller (usb)
- {16,
- 0,
- 0x18d1,
- 0x9400,
- PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD,
- {
- /* LX */ AMOTION_EVENT_AXIS_X,
- /* LY */ AMOTION_EVENT_AXIS_Y,
- /* RX */ AMOTION_EVENT_AXIS_Z,
- /* RY */ AMOTION_EVENT_AXIS_RZ,
- /* L1 */ PADDLEBOAT_AXIS_IGNORED,
- /* L2 */ AMOTION_EVENT_AXIS_BRAKE,
- /* R1 */ PADDLEBOAT_AXIS_IGNORED,
- /* R2 */ AMOTION_EVENT_AXIS_GAS,
- /* HX */ AMOTION_EVENT_AXIS_HAT_X,
- /* HY */ AMOTION_EVENT_AXIS_HAT_Y,
- },
- {
- /* LX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* LY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L2 */ PADDLEBOAT_AXIS_BUTTON_L2,
- /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* R2 */ PADDLEBOAT_AXIS_BUTTON_R2,
- /* HX */ PADDLEBOAT_AXIS_BUTTON_DPAD_RIGHT,
- /* HY */ PADDLEBOAT_AXIS_BUTTON_DPAD_DOWN,
- },
- {
- /* LX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* LY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* R2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* HX */ PADDLEBOAT_AXIS_BUTTON_DPAD_LEFT,
- /* HY */ PADDLEBOAT_AXIS_BUTTON_DPAD_UP,
- },
- {/* UP */ AKEYCODE_DPAD_UP,
- /* LEFT */ AKEYCODE_DPAD_LEFT,
- /* DOWN */ AKEYCODE_DPAD_DOWN,
- /* RIGHT */ AKEYCODE_DPAD_RIGHT,
- /* A */ AKEYCODE_BUTTON_A,
- /* B */ AKEYCODE_BUTTON_B,
- /* X */ AKEYCODE_BUTTON_X,
- /* Y */ AKEYCODE_BUTTON_Y,
- /* L1 */ AKEYCODE_BUTTON_L1,
- /* L2 */ AKEYCODE_BUTTON_L2,
- /* L3 */ AKEYCODE_BUTTON_THUMBL,
- /* R1 */ AKEYCODE_BUTTON_R1,
- /* R2 */ AKEYCODE_BUTTON_R2,
- /* R3 */ AKEYCODE_BUTTON_THUMBR,
- /* SELECT */ AKEYCODE_BUTTON_SELECT,
- /* START */ AKEYCODE_BUTTON_START,
- /* SYSTEM */ AKEYCODE_BUTTON_MODE,
- /* TOUCHP */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX1 */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX2 */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX3 */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX4 */ PADDLEBOAT_BUTTON_IGNORED}},
- // NeoGeo Pro, PC 2 setting, USB
- {16,
- 0,
- 0x20bc,
- 0x5501,
- PADDLEBOAT_CONTROLLER_LAYOUT_ARCADE_STICK,
- {
- /* LX */ AMOTION_EVENT_AXIS_X,
- /* LY */ AMOTION_EVENT_AXIS_Y,
- /* RX */ AMOTION_EVENT_AXIS_Z,
- /* RY */ AMOTION_EVENT_AXIS_RZ,
- /* L1 */ PADDLEBOAT_AXIS_IGNORED,
- /* L2 */ AMOTION_EVENT_AXIS_BRAKE,
- /* R1 */ PADDLEBOAT_AXIS_IGNORED,
- /* R2 */ AMOTION_EVENT_AXIS_GAS,
- /* HX */ AMOTION_EVENT_AXIS_HAT_X,
- /* HY */ AMOTION_EVENT_AXIS_HAT_Y,
- },
- {
- /* LX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* LY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* R2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* HX */ PADDLEBOAT_AXIS_BUTTON_DPAD_RIGHT,
- /* HY */ PADDLEBOAT_AXIS_BUTTON_DPAD_DOWN,
- },
- {
- /* LX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* LY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* L2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* R2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
- /* HX */ PADDLEBOAT_AXIS_BUTTON_DPAD_LEFT,
- /* HY */ PADDLEBOAT_AXIS_BUTTON_DPAD_UP,
- },
- {/* UP */ AKEYCODE_DPAD_UP,
- /* LEFT */ AKEYCODE_DPAD_LEFT,
- /* DOWN */ AKEYCODE_DPAD_DOWN,
- /* RIGHT */ AKEYCODE_DPAD_RIGHT,
- /* A */ AKEYCODE_BUTTON_A,
- /* B */ AKEYCODE_BUTTON_B,
- /* X */ AKEYCODE_BUTTON_X,
- /* Y */ AKEYCODE_BUTTON_Y,
- /* L1 */ AKEYCODE_BUTTON_L1,
- /* L2 */ AKEYCODE_BUTTON_R1,
- /* L3 */ PADDLEBOAT_BUTTON_IGNORED,
- /* R1 */ AKEYCODE_BUTTON_Z,
- /* R2 */ AKEYCODE_BUTTON_C,
- /* R3 */ PADDLEBOAT_BUTTON_IGNORED,
- /* SELECT */ AKEYCODE_BUTTON_SELECT,
- /* START */ AKEYCODE_BUTTON_START,
- /* SYSTEM */ PADDLEBOAT_BUTTON_IGNORED,
- /* TOUCHP */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX1 */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX2 */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX3 */ PADDLEBOAT_BUTTON_IGNORED,
- /* AUX4 */ PADDLEBOAT_BUTTON_IGNORED}}};
-
-const Paddleboat_Controller_Mapping_Data *GetInternalControllerData() {
- return pb_internal_controller_map;
-}
-
-int32_t GetInternalControllerDataCount() {
- const size_t count = ARRAY_COUNTOF(pb_internal_controller_map);
- return (static_cast<int32_t>(count));
-}
-} // namespace paddleboat
\ No newline at end of file
diff --git a/GameController/src/main/cpp/InternalControllerTable.h b/GameController/src/main/cpp/InternalControllerTable.h
deleted file mode 100644
index a88babd..0000000
--- a/GameController/src/main/cpp/InternalControllerTable.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-
-#include "paddleboat.h"
-
-namespace paddleboat {
-const Paddleboat_Controller_Mapping_Data *GetInternalControllerData();
-
-int32_t GetInternalControllerDataCount();
-} // namespace paddleboat
\ No newline at end of file
diff --git a/GameController/src/test/cpp/paddleboat_tests.cpp b/GameController/src/test/cpp/paddleboat_tests.cpp
deleted file mode 100644
index bb3f23e..0000000
--- a/GameController/src/test/cpp/paddleboat_tests.cpp
+++ /dev/null
@@ -1,1004 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gtest/gtest.h>
-
-#include "../../main/cpp/GameControllerMappingUtils.h"
-#include "paddleboat.h"
-
-using namespace paddleboat;
-using namespace std;
-
-#define ARRAY_COUNTOF(array) (sizeof(array) / sizeof(array[0]))
-
-// Validation failure test, improper ordering of vendorId
-static const Paddleboat_Controller_Mapping_Data pbtest_invalidmap_vendorid[] = {
- {16,
- 0,
- 0x0010,
- 0x0002,
- PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD,
- {/* LX */ 0, /* LY */ 0, /* RX */ 0, /* RY */ 0, /* L1 */ 0, /* L2 */ 0,
- /* R1 */ 0, /* R2 */ 0, /* HX */ 0, /* HY */ 0},
- {/* LX */ 0xFF, /* LY */ 0xFF, /* RX */ 0xFF, /* RY */ 0xFF, /* L1 */ 0xFF,
- /* L2 */ 0xFF, /* R1 */ 0xFF, /* R2 */ 0xFF, /* HX */ 0xFF,
- /* HY */ 0xFF},
- {/* LX */ 0xFF, /* LY */ 0xFF, /* RX */ 0xFF, /* RY */ 0xFF, /* L1 */ 0xFF,
- /* L2 */ 0xFF, /* R1 */ 0xFF, /* R2 */ 0xFF, /* HX */ 0xFF,
- /* HY */ 0xFF},
- {/* UP */ 0,
- /* LEFT */ 0,
- /* DOWN */ 0,
- /* RIGHT */ 0,
- /* A */ 0,
- /* B */ 0,
- /* X */ 0,
- /* Y */ 0,
- /* L1 */ 0,
- /* L2 */ 0,
- /* L3 */ 0,
- /* R1 */ 0,
- /* R2 */ 0,
- /* R3 */ 0,
- /* SELECT */ 0,
- /* START */ 0,
- /* SYSTEM */ 0,
- /* TOUCHP */ 0,
- /* AUX1-4 */ 0, 0, 0, 0}},
- {16,
- 0,
- 0x0020,
- 0x0002,
- PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD,
- {/* LX */ 0, /* LY */ 0, /* RX */ 0, /* RY */ 0, /* L1 */ 0, /* L2 */ 0,
- /* R1 */ 0, /* R2 */ 0, /* HX */ 0, /* HY */ 0},
- {/* LX */ 0xFF, /* LY */ 0xFF, /* RX */ 0xFF, /* RY */ 0xFF, /* L1 */ 0xFF,
- /* L2 */ 0xFF, /* R1 */ 0xFF, /* R2 */ 0xFF, /* HX */ 0xFF,
- /* HY */ 0xFF},
- {/* LX */ 0xFF, /* LY */ 0xFF, /* RX */ 0xFF, /* RY */ 0xFF, /* L1 */ 0xFF,
- /* L2 */ 0xFF, /* R1 */ 0xFF, /* R2 */ 0xFF, /* HX */ 0xFF,
- /* HY */ 0xFF},
- {/* UP */ 0,
- /* LEFT */ 0,
- /* DOWN */ 0,
- /* RIGHT */ 0,
- /* A */ 0,
- /* B */ 0,
- /* X */ 0,
- /* Y */ 0,
- /* L1 */ 0,
- /* L2 */ 0,
- /* L3 */ 0,
- /* R1 */ 0,
- /* R2 */ 0,
- /* R3 */ 0,
- /* SELECT */ 0,
- /* START */ 0,
- /* SYSTEM */ 0,
- /* TOUCHP */ 0,
- /* AUX1-4 */ 0, 0, 0, 0}},
- {16,
- 0,
- 0x0018,
- 0x0002,
- PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD,
- {/* LX */ 0, /* LY */ 0, /* RX */ 0, /* RY */ 0, /* L1 */ 0, /* L2 */ 0,
- /* R1 */ 0, /* R2 */ 0, /* HX */ 0, /* HY */ 0},
- {/* LX */ 0xFF, /* LY */ 0xFF, /* RX */ 0xFF, /* RY */ 0xFF, /* L1 */ 0xFF,
- /* L2 */ 0xFF, /* R1 */ 0xFF, /* R2 */ 0xFF, /* HX */ 0xFF,
- /* HY */ 0xFF},
- {/* LX */ 0xFF, /* LY */ 0xFF, /* RX */ 0xFF, /* RY */ 0xFF, /* L1 */ 0xFF,
- /* L2 */ 0xFF, /* R1 */ 0xFF, /* R2 */ 0xFF, /* HX */ 0xFF,
- /* HY */ 0xFF},
- {/* UP */ 0,
- /* LEFT */ 0,
- /* DOWN */ 0,
- /* RIGHT */ 0,
- /* A */ 0,
- /* B */ 0,
- /* X */ 0,
- /* Y */ 0,
- /* L1 */ 0,
- /* L2 */ 0,
- /* L3 */ 0,
- /* R1 */ 0,
- /* R2 */ 0,
- /* R3 */ 0,
- /* SELECT */ 0,
- /* START */ 0,
- /* SYSTEM */ 0,
- /* TOUCHP */ 0,
- /* AUX1-4 */ 0, 0, 0, 0}},
- {16,
- 0,
- 0x0030,
- 0x0002,
- PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD,
- {/* LX */ 0, /* LY */ 0, /* RX */ 0, /* RY */ 0, /* L1 */ 0, /* L2 */ 0,
- /* R1 */ 0, /* R2 */ 0, /* HX */ 0, /* HY */ 0},
- {/* LX */ 0xFF, /* LY */ 0xFF, /* RX */ 0xFF, /* RY */ 0xFF, /* L1 */ 0xFF,
- /* L2 */ 0xFF, /* R1 */ 0xFF, /* R2 */ 0xFF, /* HX */ 0xFF,
- /* HY */ 0xFF},
- {/* LX */ 0xFF, /* LY */ 0xFF, /* RX */ 0xFF, /* RY */ 0xFF, /* L1 */ 0xFF,
- /* L2 */ 0xFF, /* R1 */ 0xFF, /* R2 */ 0xFF, /* HX */ 0xFF,
- /* HY */ 0xFF},
- {/* UP */ 0,
- /* LEFT */ 0,
- /* DOWN */ 0,
- /* RIGHT */ 0,
- /* A */ 0,
- /* B */ 0,
- /* X */ 0,
- /* Y */ 0,
- /* L1 */ 0,
- /* L2 */ 0,
- /* L3 */ 0,
- /* R1 */ 0,
- /* R2 */ 0,
- /* R3 */ 0,
- /* SELECT */ 0,
- /* START */ 0,
- /* SYSTEM */ 0,
- /* TOUCHP */ 0,
- /* AUX1-4 */ 0, 0, 0, 0}}};
-
-// Validation failure test, improper ordering of productId
-static const Paddleboat_Controller_Mapping_Data pbtest_invalidmap_productid[] =
- {{16,
- 0,
- 0x0010,
- 0x0002,
- PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD,
- {/* LX */ 0, /* LY */ 0, /* RX */ 0, /* RY */ 0, /* L1 */ 0, /* L2 */ 0,
- /* R1 */ 0, /* R2 */ 0, /* HX */ 0, /* HY */ 0},
- {/* LX */ 0xFF, /* LY */ 0xFF, /* RX */ 0xFF, /* RY */ 0xFF,
- /* L1 */ 0xFF,
- /* L2 */ 0xFF, /* R1 */ 0xFF, /* R2 */ 0xFF, /* HX */ 0xFF,
- /* HY */ 0xFF},
- {/* LX */ 0xFF, /* LY */ 0xFF, /* RX */ 0xFF, /* RY */ 0xFF,
- /* L1 */ 0xFF,
- /* L2 */ 0xFF, /* R1 */ 0xFF, /* R2 */ 0xFF, /* HX */ 0xFF,
- /* HY */ 0xFF},
- {/* UP */ 0,
- /* LEFT */ 0,
- /* DOWN */ 0,
- /* RIGHT */ 0,
- /* A */ 0,
- /* B */ 0,
- /* X */ 0,
- /* Y */ 0,
- /* L1 */ 0,
- /* L2 */ 0,
- /* L3 */ 0,
- /* R1 */ 0,
- /* R2 */ 0,
- /* R3 */ 0,
- /* SELECT */ 0,
- /* START */ 0,
- /* SYSTEM */ 0,
- /* TOUCHP */ 0,
- /* AUX1-4 */ 0, 0, 0, 0}},
- {16,
- 0,
- 0x0018,
- 0x0002,
- PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD,
- {/* LX */ 0, /* LY */ 0, /* RX */ 0, /* RY */ 0, /* L1 */ 0, /* L2 */ 0,
- /* R1 */ 0, /* R2 */ 0, /* HX */ 0, /* HY */ 0},
- {/* LX */ 0xFF, /* LY */ 0xFF, /* RX */ 0xFF, /* RY */ 0xFF,
- /* L1 */ 0xFF,
- /* L2 */ 0xFF, /* R1 */ 0xFF, /* R2 */ 0xFF, /* HX */ 0xFF,
- /* HY */ 0xFF},
- {/* LX */ 0xFF, /* LY */ 0xFF, /* RX */ 0xFF, /* RY */ 0xFF,
- /* L1 */ 0xFF,
- /* L2 */ 0xFF, /* R1 */ 0xFF, /* R2 */ 0xFF, /* HX */ 0xFF,
- /* HY */ 0xFF},
- {/* UP */ 0,
- /* LEFT */ 0,
- /* DOWN */ 0,
- /* RIGHT */ 0,
- /* A */ 0,
- /* B */ 0,
- /* X */ 0,
- /* Y */ 0,
- /* L1 */ 0,
- /* L2 */ 0,
- /* L3 */ 0,
- /* R1 */ 0,
- /* R2 */ 0,
- /* R3 */ 0,
- /* SELECT */ 0,
- /* START */ 0,
- /* SYSTEM */ 0,
- /* TOUCHP */ 0,
- /* AUX1-4 */ 0, 0, 0, 0}},
- {16,
- 0,
- 0x0020,
- 0x0002,
- PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD,
- {/* LX */ 0, /* LY */ 0, /* RX */ 0, /* RY */ 0, /* L1 */ 0, /* L2 */ 0,
- /* R1 */ 0, /* R2 */ 0, /* HX */ 0, /* HY */ 0},
- {/* LX */ 0xFF, /* LY */ 0xFF, /* RX */ 0xFF, /* RY */ 0xFF,
- /* L1 */ 0xFF,
- /* L2 */ 0xFF, /* R1 */ 0xFF, /* R2 */ 0xFF, /* HX */ 0xFF,
- /* HY */ 0xFF},
- {/* LX */ 0xFF, /* LY */ 0xFF, /* RX */ 0xFF, /* RY */ 0xFF,
- /* L1 */ 0xFF,
- /* L2 */ 0xFF, /* R1 */ 0xFF, /* R2 */ 0xFF, /* HX */ 0xFF,
- /* HY */ 0xFF},
- {/* UP */ 0,
- /* LEFT */ 0,
- /* DOWN */ 0,
- /* RIGHT */ 0,
- /* A */ 0,
- /* B */ 0,
- /* X */ 0,
- /* Y */ 0,
- /* L1 */ 0,
- /* L2 */ 0,
- /* L3 */ 0,
- /* R1 */ 0,
- /* R2 */ 0,
- /* R3 */ 0,
- /* SELECT */ 0,
- /* START */ 0,
- /* SYSTEM */ 0,
- /* TOUCHP */ 0,
- /* AUX1-4 */ 0, 0, 0, 0}},
- {16,
- 0,
- 0x0020,
- 0x0001,
- PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD,
- {/* LX */ 0, /* LY */ 0, /* RX */ 0, /* RY */ 0, /* L1 */ 0, /* L2 */ 0,
- /* R1 */ 0, /* R2 */ 0, /* HX */ 0, /* HY */ 0},
- {/* LX */ 0xFF, /* LY */ 0xFF, /* RX */ 0xFF, /* RY */ 0xFF,
- /* L1 */ 0xFF,
- /* L2 */ 0xFF, /* R1 */ 0xFF, /* R2 */ 0xFF, /* HX */ 0xFF,
- /* HY */ 0xFF},
- {/* LX */ 0xFF, /* LY */ 0xFF, /* RX */ 0xFF, /* RY */ 0xFF,
- /* L1 */ 0xFF,
- /* L2 */ 0xFF, /* R1 */ 0xFF, /* R2 */ 0xFF, /* HX */ 0xFF,
- /* HY */ 0xFF},
- {/* UP */ 0,
- /* LEFT */ 0,
- /* DOWN */ 0,
- /* RIGHT */ 0,
- /* A */ 0,
- /* B */ 0,
- /* X */ 0,
- /* Y */ 0,
- /* L1 */ 0,
- /* L2 */ 0,
- /* L3 */ 0,
- /* R1 */ 0,
- /* R2 */ 0,
- /* R3 */ 0,
- /* SELECT */ 0,
- /* START */ 0,
- /* SYSTEM */ 0,
- /* TOUCHP */ 0,
- /* AUX1-4 */ 0, 0, 0, 0}},
- {16,
- 0,
- 0x0030,
- 0x0002,
- PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD,
- {/* LX */ 0, /* LY */ 0, /* RX */ 0, /* RY */ 0, /* L1 */ 0, /* L2 */ 0,
- /* R1 */ 0, /* R2 */ 0, /* HX */ 0, /* HY */ 0},
- {/* LX */ 0xFF, /* LY */ 0xFF, /* RX */ 0xFF, /* RY */ 0xFF,
- /* L1 */ 0xFF,
- /* L2 */ 0xFF, /* R1 */ 0xFF, /* R2 */ 0xFF, /* HX */ 0xFF,
- /* HY */ 0xFF},
- {/* LX */ 0xFF, /* LY */ 0xFF, /* RX */ 0xFF, /* RY */ 0xFF,
- /* L1 */ 0xFF,
- /* L2 */ 0xFF, /* R1 */ 0xFF, /* R2 */ 0xFF, /* HX */ 0xFF,
- /* HY */ 0xFF},
- {/* UP */ 0,
- /* LEFT */ 0,
- /* DOWN */ 0,
- /* RIGHT */ 0,
- /* A */ 0,
- /* B */ 0,
- /* X */ 0,
- /* Y */ 0,
- /* L1 */ 0,
- /* L2 */ 0,
- /* L3 */ 0,
- /* R1 */ 0,
- /* R2 */ 0,
- /* R3 */ 0,
- /* SELECT */ 0,
- /* START */ 0,
- /* SYSTEM */ 0,
- /* TOUCHP */ 0,
- /* AUX1-4 */ 0, 0, 0, 0}}};
-
-// Validation failure test, improper ordering of api levels
-static const Paddleboat_Controller_Mapping_Data pbtest_invalidmap_apilevel[] = {
- {16,
- 0,
- 0x0010,
- 0x0002,
- PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD,
- {/* LX */ 0, /* LY */ 0, /* RX */ 0, /* RY */ 0, /* L1 */ 0, /* L2 */ 0,
- /* R1 */ 0, /* R2 */ 0, /* HX */ 0, /* HY */ 0},
- {/* LX */ 0xFF, /* LY */ 0xFF, /* RX */ 0xFF, /* RY */ 0xFF, /* L1 */ 0xFF,
- /* L2 */ 0xFF, /* R1 */ 0xFF, /* R2 */ 0xFF, /* HX */ 0xFF,
- /* HY */ 0xFF},
- {/* LX */ 0xFF, /* LY */ 0xFF, /* RX */ 0xFF, /* RY */ 0xFF, /* L1 */ 0xFF,
- /* L2 */ 0xFF, /* R1 */ 0xFF, /* R2 */ 0xFF, /* HX */ 0xFF,
- /* HY */ 0xFF},
- {/* UP */ 0,
- /* LEFT */ 0,
- /* DOWN */ 0,
- /* RIGHT */ 0,
- /* A */ 0,
- /* B */ 0,
- /* X */ 0,
- /* Y */ 0,
- /* L1 */ 0,
- /* L2 */ 0,
- /* L3 */ 0,
- /* R1 */ 0,
- /* R2 */ 0,
- /* R3 */ 0,
- /* SELECT */ 0,
- /* START */ 0,
- /* SYSTEM */ 0,
- /* TOUCHP */ 0,
- /* AUX1-4 */ 0, 0, 0, 0}},
- {16,
- 0,
- 0x0018,
- 0x0002,
- PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD,
- {/* LX */ 0, /* LY */ 0, /* RX */ 0, /* RY */ 0, /* L1 */ 0, /* L2 */ 0,
- /* R1 */ 0, /* R2 */ 0, /* HX */ 0, /* HY */ 0},
- {/* LX */ 0xFF, /* LY */ 0xFF, /* RX */ 0xFF, /* RY */ 0xFF, /* L1 */ 0xFF,
- /* L2 */ 0xFF, /* R1 */ 0xFF, /* R2 */ 0xFF, /* HX */ 0xFF,
- /* HY */ 0xFF},
- {/* LX */ 0xFF, /* LY */ 0xFF, /* RX */ 0xFF, /* RY */ 0xFF, /* L1 */ 0xFF,
- /* L2 */ 0xFF, /* R1 */ 0xFF, /* R2 */ 0xFF, /* HX */ 0xFF,
- /* HY */ 0xFF},
- {/* UP */ 0,
- /* LEFT */ 0,
- /* DOWN */ 0,
- /* RIGHT */ 0,
- /* A */ 0,
- /* B */ 0,
- /* X */ 0,
- /* Y */ 0,
- /* L1 */ 0,
- /* L2 */ 0,
- /* L3 */ 0,
- /* R1 */ 0,
- /* R2 */ 0,
- /* R3 */ 0,
- /* SELECT */ 0,
- /* START */ 0,
- /* SYSTEM */ 0,
- /* TOUCHP */ 0,
- /* AUX1-4 */ 0, 0, 0, 0}},
- {19,
- 0,
- 0x0020,
- 0x0001,
- PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD,
- {/* LX */ 0, /* LY */ 0, /* RX */ 0, /* RY */ 0, /* L1 */ 0, /* L2 */ 0,
- /* R1 */ 0, /* R2 */ 0, /* HX */ 0, /* HY */ 0},
- {/* LX */ 0xFF, /* LY */ 0xFF, /* RX */ 0xFF, /* RY */ 0xFF, /* L1 */ 0xFF,
- /* L2 */ 0xFF, /* R1 */ 0xFF, /* R2 */ 0xFF, /* HX */ 0xFF,
- /* HY */ 0xFF},
- {/* LX */ 0xFF, /* LY */ 0xFF, /* RX */ 0xFF, /* RY */ 0xFF, /* L1 */ 0xFF,
- /* L2 */ 0xFF, /* R1 */ 0xFF, /* R2 */ 0xFF, /* HX */ 0xFF,
- /* HY */ 0xFF},
- {/* UP */ 0,
- /* LEFT */ 0,
- /* DOWN */ 0,
- /* RIGHT */ 0,
- /* A */ 0,
- /* B */ 0,
- /* X */ 0,
- /* Y */ 0,
- /* L1 */ 0,
- /* L2 */ 0,
- /* L3 */ 0,
- /* R1 */ 0,
- /* R2 */ 0,
- /* R3 */ 0,
- /* SELECT */ 0,
- /* START */ 0,
- /* SYSTEM */ 0,
- /* TOUCHP */ 0,
- /* AUX1-4 */ 0, 0, 0, 0}},
- {16,
- 0,
- 0x0020,
- 0x0001,
- PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD,
- {/* LX */ 0, /* LY */ 0, /* RX */ 0, /* RY */ 0, /* L1 */ 0, /* L2 */ 0,
- /* R1 */ 0, /* R2 */ 0, /* HX */ 0, /* HY */ 0},
- {/* LX */ 0xFF, /* LY */ 0xFF, /* RX */ 0xFF, /* RY */ 0xFF, /* L1 */ 0xFF,
- /* L2 */ 0xFF, /* R1 */ 0xFF, /* R2 */ 0xFF, /* HX */ 0xFF,
- /* HY */ 0xFF},
- {/* LX */ 0xFF, /* LY */ 0xFF, /* RX */ 0xFF, /* RY */ 0xFF, /* L1 */ 0xFF,
- /* L2 */ 0xFF, /* R1 */ 0xFF, /* R2 */ 0xFF, /* HX */ 0xFF,
- /* HY */ 0xFF},
- {/* UP */ 0,
- /* LEFT */ 0,
- /* DOWN */ 0,
- /* RIGHT */ 0,
- /* A */ 0,
- /* B */ 0,
- /* X */ 0,
- /* Y */ 0,
- /* L1 */ 0,
- /* L2 */ 0,
- /* L3 */ 0,
- /* R1 */ 0,
- /* R2 */ 0,
- /* R3 */ 0,
- /* SELECT */ 0,
- /* START */ 0,
- /* SYSTEM */ 0,
- /* TOUCHP */ 0,
- /* AUX1-4 */ 0, 0, 0, 0}},
- {16,
- 0,
- 0x0030,
- 0x0002,
- PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD,
- {/* LX */ 0, /* LY */ 0, /* RX */ 0, /* RY */ 0, /* L1 */ 0, /* L2 */ 0,
- /* R1 */ 0, /* R2 */ 0, /* HX */ 0, /* HY */ 0},
- {/* LX */ 0xFF, /* LY */ 0xFF, /* RX */ 0xFF, /* RY */ 0xFF, /* L1 */ 0xFF,
- /* L2 */ 0xFF, /* R1 */ 0xFF, /* R2 */ 0xFF, /* HX */ 0xFF,
- /* HY */ 0xFF},
- {/* LX */ 0xFF, /* LY */ 0xFF, /* RX */ 0xFF, /* RY */ 0xFF, /* L1 */ 0xFF,
- /* L2 */ 0xFF, /* R1 */ 0xFF, /* R2 */ 0xFF, /* HX */ 0xFF,
- /* HY */ 0xFF},
- {/* UP */ 0,
- /* LEFT */ 0,
- /* DOWN */ 0,
- /* RIGHT */ 0,
- /* A */ 0,
- /* B */ 0,
- /* X */ 0,
- /* Y */ 0,
- /* L1 */ 0,
- /* L2 */ 0,
- /* L3 */ 0,
- /* R1 */ 0,
- /* R2 */ 0,
- /* R3 */ 0,
- /* SELECT */ 0,
- /* START */ 0,
- /* SYSTEM */ 0,
- /* TOUCHP */ 0,
- /* AUX1-4 */ 0, 0, 0, 0}}};
-
-// Validation success test / find-insert tests baseline existing map
-static const Paddleboat_Controller_Mapping_Data pbtest_validmap[] = {
- {16,
- 0,
- 0x0010,
- 0x0002,
- PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD,
- {/* LX */ 0, /* LY */ 0, /* RX */ 0, /* RY */ 0, /* L1 */ 0, /* L2 */ 0,
- /* R1 */ 0, /* R2 */ 0, /* HX */ 0, /* HY */ 0},
- {/* LX */ 0xFF, /* LY */ 0xFF, /* RX */ 0xFF, /* RY */ 0xFF, /* L1 */ 0xFF,
- /* L2 */ 0xFF, /* R1 */ 0xFF, /* R2 */ 0xFF, /* HX */ 0xFF,
- /* HY */ 0xFF},
- {/* LX */ 0xFF, /* LY */ 0xFF, /* RX */ 0xFF, /* RY */ 0xFF, /* L1 */ 0xFF,
- /* L2 */ 0xFF, /* R1 */ 0xFF, /* R2 */ 0xFF, /* HX */ 0xFF,
- /* HY */ 0xFF},
- {/* UP */ 0,
- /* LEFT */ 0,
- /* DOWN */ 0,
- /* RIGHT */ 0,
- /* A */ 0,
- /* B */ 0,
- /* X */ 0,
- /* Y */ 0,
- /* L1 */ 0,
- /* L2 */ 0,
- /* L3 */ 0,
- /* R1 */ 0,
- /* R2 */ 0,
- /* R3 */ 0,
- /* SELECT */ 0,
- /* START */ 0,
- /* SYSTEM */ 0,
- /* TOUCHP */ 0,
- /* AUX1-4 */ 0, 0, 0, 0}},
- {16,
- 0,
- 0x0018,
- 0x0002,
- PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD,
- {/* LX */ 0, /* LY */ 0, /* RX */ 0, /* RY */ 0, /* L1 */ 0, /* L2 */ 0,
- /* R1 */ 0, /* R2 */ 0, /* HX */ 0, /* HY */ 0},
- {/* LX */ 0xFF, /* LY */ 0xFF, /* RX */ 0xFF, /* RY */ 0xFF, /* L1 */ 0xFF,
- /* L2 */ 0xFF, /* R1 */ 0xFF, /* R2 */ 0xFF, /* HX */ 0xFF,
- /* HY */ 0xFF},
- {/* LX */ 0xFF, /* LY */ 0xFF, /* RX */ 0xFF, /* RY */ 0xFF, /* L1 */ 0xFF,
- /* L2 */ 0xFF, /* R1 */ 0xFF, /* R2 */ 0xFF, /* HX */ 0xFF,
- /* HY */ 0xFF},
- {/* UP */ 0,
- /* LEFT */ 0,
- /* DOWN */ 0,
- /* RIGHT */ 0,
- /* A */ 0,
- /* B */ 0,
- /* X */ 0,
- /* Y */ 0,
- /* L1 */ 0,
- /* L2 */ 0,
- /* L3 */ 0,
- /* R1 */ 0,
- /* R2 */ 0,
- /* R3 */ 0,
- /* SELECT */ 0,
- /* START */ 0,
- /* SYSTEM */ 0,
- /* TOUCHP */ 0,
- /* AUX1-4 */ 0, 0, 0, 0}},
- {16,
- 0,
- 0x0020,
- 0x0002,
- PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD,
- {/* LX */ 0, /* LY */ 0, /* RX */ 0, /* RY */ 0, /* L1 */ 0, /* L2 */ 0,
- /* R1 */ 0, /* R2 */ 0, /* HX */ 0, /* HY */ 0},
- {/* LX */ 0xFF, /* LY */ 0xFF, /* RX */ 0xFF, /* RY */ 0xFF, /* L1 */ 0xFF,
- /* L2 */ 0xFF, /* R1 */ 0xFF, /* R2 */ 0xFF, /* HX */ 0xFF,
- /* HY */ 0xFF},
- {/* LX */ 0xFF, /* LY */ 0xFF, /* RX */ 0xFF, /* RY */ 0xFF, /* L1 */ 0xFF,
- /* L2 */ 0xFF, /* R1 */ 0xFF, /* R2 */ 0xFF, /* HX */ 0xFF,
- /* HY */ 0xFF},
- {/* UP */ 0,
- /* LEFT */ 0,
- /* DOWN */ 0,
- /* RIGHT */ 0,
- /* A */ 0,
- /* B */ 0,
- /* X */ 0,
- /* Y */ 0,
- /* L1 */ 0,
- /* L2 */ 0,
- /* L3 */ 0,
- /* R1 */ 0,
- /* R2 */ 0,
- /* R3 */ 0,
- /* SELECT */ 0,
- /* START */ 0,
- /* SYSTEM */ 0,
- /* TOUCHP */ 0,
- /* AUX1-4 */ 0, 0, 0, 0}},
- {19,
- 0,
- 0x0020,
- 0x0002,
- PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD,
- {/* LX */ 0, /* LY */ 0, /* RX */ 0, /* RY */ 0, /* L1 */ 0, /* L2 */ 0,
- /* R1 */ 0, /* R2 */ 0, /* HX */ 0, /* HY */ 0},
- {/* LX */ 0xFF, /* LY */ 0xFF, /* RX */ 0xFF, /* RY */ 0xFF, /* L1 */ 0xFF,
- /* L2 */ 0xFF, /* R1 */ 0xFF, /* R2 */ 0xFF, /* HX */ 0xFF,
- /* HY */ 0xFF},
- {/* LX */ 0xFF, /* LY */ 0xFF, /* RX */ 0xFF, /* RY */ 0xFF, /* L1 */ 0xFF,
- /* L2 */ 0xFF, /* R1 */ 0xFF, /* R2 */ 0xFF, /* HX */ 0xFF,
- /* HY */ 0xFF},
- {/* UP */ 0,
- /* LEFT */ 0,
- /* DOWN */ 0,
- /* RIGHT */ 0,
- /* A */ 0,
- /* B */ 0,
- /* X */ 0,
- /* Y */ 0,
- /* L1 */ 0,
- /* L2 */ 0,
- /* L3 */ 0,
- /* R1 */ 0,
- /* R2 */ 0,
- /* R3 */ 0,
- /* SELECT */ 0,
- /* START */ 0,
- /* SYSTEM */ 0,
- /* TOUCHP */ 0,
- /* AUX1-4 */ 0, 0, 0, 0}},
- {16,
- 0,
- 0x0020,
- 0x0004,
- PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD,
- {/* LX */ 0, /* LY */ 0, /* RX */ 0, /* RY */ 0, /* L1 */ 0, /* L2 */ 0,
- /* R1 */ 0, /* R2 */ 0, /* HX */ 0, /* HY */ 0},
- {/* LX */ 0xFF, /* LY */ 0xFF, /* RX */ 0xFF, /* RY */ 0xFF, /* L1 */ 0xFF,
- /* L2 */ 0xFF, /* R1 */ 0xFF, /* R2 */ 0xFF, /* HX */ 0xFF,
- /* HY */ 0xFF},
- {/* LX */ 0xFF, /* LY */ 0xFF, /* RX */ 0xFF, /* RY */ 0xFF, /* L1 */ 0xFF,
- /* L2 */ 0xFF, /* R1 */ 0xFF, /* R2 */ 0xFF, /* HX */ 0xFF,
- /* HY */ 0xFF},
- {/* UP */ 0,
- /* LEFT */ 0,
- /* DOWN */ 0,
- /* RIGHT */ 0,
- /* A */ 0,
- /* B */ 0,
- /* X */ 0,
- /* Y */ 0,
- /* L1 */ 0,
- /* L2 */ 0,
- /* L3 */ 0,
- /* R1 */ 0,
- /* R2 */ 0,
- /* R3 */ 0,
- /* SELECT */ 0,
- /* START */ 0,
- /* SYSTEM */ 0,
- /* TOUCHP */ 0,
- /* AUX1-4 */ 0, 0, 0, 0}},
- {16,
- 0,
- 0x0030,
- 0x0001,
- PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD,
- {/* LX */ 0, /* LY */ 0, /* RX */ 0, /* RY */ 0, /* L1 */ 0, /* L2 */ 0,
- /* R1 */ 0, /* R2 */ 0, /* HX */ 0, /* HY */ 0},
- {/* LX */ 0xFF, /* LY */ 0xFF, /* RX */ 0xFF, /* RY */ 0xFF, /* L1 */ 0xFF,
- /* L2 */ 0xFF, /* R1 */ 0xFF, /* R2 */ 0xFF, /* HX */ 0xFF,
- /* HY */ 0xFF},
- {/* LX */ 0xFF, /* LY */ 0xFF, /* RX */ 0xFF, /* RY */ 0xFF, /* L1 */ 0xFF,
- /* L2 */ 0xFF, /* R1 */ 0xFF, /* R2 */ 0xFF, /* HX */ 0xFF,
- /* HY */ 0xFF},
- {/* UP */ 0,
- /* LEFT */ 0,
- /* DOWN */ 0,
- /* RIGHT */ 0,
- /* A */ 0,
- /* B */ 0,
- /* X */ 0,
- /* Y */ 0,
- /* L1 */ 0,
- /* L2 */ 0,
- /* L3 */ 0,
- /* R1 */ 0,
- /* R2 */ 0,
- /* R3 */ 0,
- /* SELECT */ 0,
- /* START */ 0,
- /* SYSTEM */ 0,
- /* TOUCHP */ 0,
- /* AUX1-4 */ 0, 0, 0, 0}}};
-
-static const Paddleboat_Controller_Mapping_Data pbtest_add_entry1 = {
- 16,
- 0,
- 0x0001,
- 0x0001,
- PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD,
- {/* LX */ 0, /* LY */ 0, /* RX */ 0, /* RY */ 0, /* L1 */ 0, /* L2 */ 0,
- /* R1 */ 0, /* R2 */ 0, /* HX */ 0, /* HY */ 0},
- {/* LX */ 0xFF, /* LY */ 0xFF, /* RX */ 0xFF, /* RY */ 0xFF, /* L1 */ 0xFF,
- /* L2 */ 0xFF, /* R1 */ 0xFF, /* R2 */ 0xFF, /* HX */ 0xFF, /* HY */ 0xFF},
- {/* LX */ 0xFF, /* LY */ 0xFF, /* RX */ 0xFF, /* RY */ 0xFF, /* L1 */ 0xFF,
- /* L2 */ 0xFF, /* R1 */ 0xFF, /* R2 */ 0xFF, /* HX */ 0xFF, /* HY */ 0xFF},
- {/* UP */ 0,
- /* LEFT */ 0,
- /* DOWN */ 0,
- /* RIGHT */ 0,
- /* A */ 0,
- /* B */ 0,
- /* X */ 0,
- /* Y */ 0,
- /* L1 */ 0,
- /* L2 */ 0,
- /* L3 */ 0,
- /* R1 */ 0,
- /* R2 */ 0,
- /* R3 */ 0,
- /* SELECT */ 0,
- /* START */ 0,
- /* SYSTEM */ 0,
- /* TOUCHP */ 0,
- /* AUX1-4 */ 0, 0, 0, 0}};
-
-static const Paddleboat_Controller_Mapping_Data pbtest_add_entry2 = {
- 16,
- 0,
- 0x0020,
- 0x0010,
- PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD,
- {/* LX */ 0, /* LY */ 0, /* RX */ 0, /* RY */ 0, /* L1 */ 0,
- /* L2 */ 0, /* R1 */
- 0, /* R2 */ 0, /* HX */ 0, /* HY */ 0},
- {/* LX */ 0xFF, /* LY */ 0xFF, /* RX */ 0xFF, /* RY */ 0xFF,
- /* L1 */ 0xFF, /* L2 */
- 0xFF, /* R1 */ 0xFF, /* R2 */ 0xFF, /* HX */ 0xFF, /* HY */ 0xFF},
- {/* LX */ 0xFF, /* LY */ 0xFF, /* RX */ 0xFF, /* RY */ 0xFF,
- /* L1 */ 0xFF, /* L2 */
- 0xFF, /* R1 */ 0xFF, /* R2 */ 0xFF, /* HX */ 0xFF, /* HY */ 0xFF},
- {/* UP */ 0,
- /* LEFT */ 0,
- /* DOWN */ 0,
- /* RIGHT */ 0, /* A */
- 0,
- /* B */ 0,
- /* X */ 0,
- /* Y */ 0,
- /* L1 */ 0,
- /* L2 */ 0,
- /* L3 */ 0, /* R1 */
- 0,
- /* R2 */ 0,
- /* R3 */ 0,
- /* SELECT */ 0,
- /* START */ 0,
- /* SYSTEM */ 0,
- /* TOUCHP */ 0,
- /* AUX1-4 */ 0,
- 0,
- 0,
- 0}
-
-};
-
-static const Paddleboat_Controller_Mapping_Data pbtest_add_entry3 = {
- 16,
- 0,
- 0x0040,
- 0x0100,
- PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD,
- {/* LX */ 0, /* LY */ 0, /* RX */ 0, /* RY */ 0, /* L1 */ 0,
- /* L2 */ 0, /* R1 */
- 0, /* R2 */ 0, /* HX */ 0, /* HY */ 0},
- {/* LX */ 0xFF, /* LY */ 0xFF, /* RX */ 0xFF, /* RY */ 0xFF,
- /* L1 */ 0xFF, /* L2 */
- 0xFF, /* R1 */ 0xFF, /* R2 */ 0xFF, /* HX */ 0xFF, /* HY */ 0xFF},
- {/* LX */ 0xFF, /* LY */ 0xFF, /* RX */ 0xFF, /* RY */ 0xFF,
- /* L1 */ 0xFF, /* L2 */
- 0xFF, /* R1 */ 0xFF, /* R2 */ 0xFF, /* HX */ 0xFF, /* HY */ 0xFF},
- {/* UP */ 0,
- /* LEFT */ 0,
- /* DOWN */ 0,
- /* RIGHT */ 0, /* A */
- 0,
- /* B */ 0,
- /* X */ 0,
- /* Y */ 0,
- /* L1 */ 0,
- /* L2 */ 0,
- /* L3 */ 0, /* R1 */
- 0,
- /* R2 */ 0,
- /* R3 */ 0,
- /* SELECT */ 0,
- /* START */ 0,
- /* SYSTEM */ 0,
- /* TOUCHP */ 0,
- /* AUX1-4 */ 0,
- 0,
- 0,
- 0}
-
-};
-
-#define TEST_MAPPING_TABLE_SIZE 256
-
-static Paddleboat_Controller_Mapping_Data
- testMappingTable[TEST_MAPPING_TABLE_SIZE];
-
-static void InitSearchFromExistingEntry(
- const Paddleboat_Controller_Mapping_Data *sourceEntry,
- MappingTableSearch &mapSearch) {
- mapSearch.initSearchParameters(sourceEntry->vendorId,
- sourceEntry->productId,
- sourceEntry->minimumEffectiveApiLevel,
- sourceEntry->maximumEffectiveApiLevel);
-}
-
-// Make sure NotEmpty works
-TEST(PaddleboatTestNE, NotEmpty) {
- EXPECT_NE(sizeof(Paddleboat_Controller_Data), 0);
-}
-
-// Make sure Validity works
-TEST(PaddleboatTestValidity, Validity) {
- const size_t pcd_size = sizeof(Paddleboat_Controller_Data);
- EXPECT_EQ(pcd_size, 80);
- EXPECT_NE(pcd_size, 0);
-}
-
-// Test validateMapTable catches improper vendorId ordering
-TEST(PaddleboatValidateMapVendorId, Validity) {
- const int32_t entryCount =
- static_cast<const int32_t>(ARRAY_COUNTOF(pbtest_invalidmap_vendorid));
- const Paddleboat_Controller_Mapping_Data *errorEntry =
- GameControllerMappingUtils::validateMapTable(pbtest_invalidmap_vendorid,
- entryCount);
- EXPECT_NE(errorEntry, nullptr);
- EXPECT_EQ(errorEntry->vendorId, 0x0018);
-}
-
-// Test validateMapTable catches improper productId ordering
-TEST(PaddleboatValidateMapProductId, Validity) {
- const int32_t entryCount =
- static_cast<const int32_t>(ARRAY_COUNTOF(pbtest_invalidmap_productid));
- const Paddleboat_Controller_Mapping_Data *errorEntry =
- GameControllerMappingUtils::validateMapTable(
- pbtest_invalidmap_productid, entryCount);
- EXPECT_NE(errorEntry, nullptr);
- EXPECT_EQ(errorEntry->vendorId, 0x0020);
- EXPECT_EQ(errorEntry->productId, 0x0001);
-}
-
-// Test validateMapTable catches improper api level ordering
-TEST(PaddleboatValidateMapApiLevel, Validity) {
- const int32_t entryCount =
- static_cast<const int32_t>(ARRAY_COUNTOF(pbtest_invalidmap_apilevel));
- const Paddleboat_Controller_Mapping_Data *errorEntry =
- GameControllerMappingUtils::validateMapTable(pbtest_invalidmap_apilevel,
- entryCount);
- EXPECT_NE(errorEntry, nullptr);
- EXPECT_EQ(errorEntry->vendorId, 0x0020);
- EXPECT_EQ(errorEntry->productId, 0x0001);
- EXPECT_EQ(errorEntry->minimumEffectiveApiLevel, 16);
-}
-
-// Test validateMapTable returns nullptr on a successful validation
-TEST(PaddleboatValidateMapSuccess, Validity) {
- const int32_t entryCount =
- static_cast<const int32_t>(ARRAY_COUNTOF(pbtest_validmap));
- const Paddleboat_Controller_Mapping_Data *errorEntry =
- GameControllerMappingUtils::validateMapTable(pbtest_validmap,
- entryCount);
- EXPECT_EQ(errorEntry, nullptr);
-}
-
-// Test find functionality
-TEST(PaddleboatValidateFindMapEntry, Validity) {
- const int32_t entryCount =
- static_cast<const int32_t>(ARRAY_COUNTOF(pbtest_validmap));
- memcpy(testMappingTable, pbtest_validmap,
- entryCount * sizeof(Paddleboat_Controller_Mapping_Data));
- MappingTableSearch mapSearch(&testMappingTable[0], entryCount);
-
- // Should find a match for the first entry
- int32_t targetTableIndex = 0;
- InitSearchFromExistingEntry(&testMappingTable[targetTableIndex], mapSearch);
- bool foundEntry =
- GameControllerMappingUtils::findMatchingMapEntry(&mapSearch);
- EXPECT_EQ(foundEntry, true);
- EXPECT_EQ(mapSearch.tableIndex, targetTableIndex);
-
- // Should find a match for the fourth entry
- targetTableIndex = 3;
- InitSearchFromExistingEntry(&testMappingTable[targetTableIndex], mapSearch);
- foundEntry = GameControllerMappingUtils::findMatchingMapEntry(&mapSearch);
- EXPECT_EQ(foundEntry, true);
- EXPECT_EQ(mapSearch.tableIndex, targetTableIndex);
-
- // Should fail to find a match and put insert point at the first entry
- // (unique productId/vendorId)
- mapSearch.initSearchParameters(0x1, 0x1, 16, 0);
- foundEntry = GameControllerMappingUtils::findMatchingMapEntry(&mapSearch);
- EXPECT_EQ(foundEntry, false);
- EXPECT_EQ(mapSearch.tableIndex, 0);
-
- // Should fail to find a match and put insert point at the third entry
- // (matching productId/new vendorId)
- mapSearch.initSearchParameters(0x20, 0x1, 16, 0);
- foundEntry = GameControllerMappingUtils::findMatchingMapEntry(&mapSearch);
- EXPECT_EQ(foundEntry, false);
- EXPECT_EQ(mapSearch.tableIndex, 2);
-
- // Should fail to find a match and put insert point at the fifth entry
- // (matching productId/vendorId, new apiLevel)
- mapSearch.initSearchParameters(0x20, 0x2, 22, 0);
- foundEntry = GameControllerMappingUtils::findMatchingMapEntry(&mapSearch);
- EXPECT_EQ(foundEntry, false);
- EXPECT_EQ(mapSearch.tableIndex, 4);
-
- // Should fail to find a match and put insert point at the sixth entry
- // (unique productId/vendorId)
- mapSearch.initSearchParameters(0x21, 0x1, 16, 0);
- foundEntry = GameControllerMappingUtils::findMatchingMapEntry(&mapSearch);
- EXPECT_EQ(foundEntry, false);
- EXPECT_EQ(mapSearch.tableIndex, 5);
-
- // Should fail to find a match and put the insert point at the end of the
- // table (unique productId/vendorId)
- mapSearch.initSearchParameters(0x40, 0x1, 16, 0);
- foundEntry = GameControllerMappingUtils::findMatchingMapEntry(&mapSearch);
- EXPECT_EQ(foundEntry, false);
- EXPECT_EQ(mapSearch.tableIndex, entryCount);
-}
-
-// Test add functionality
-TEST(PaddleboatValidateAddMapEntry, Validity) {
- const int32_t entryCount =
- static_cast<const int32_t>(ARRAY_COUNTOF(pbtest_validmap));
- memset(
- testMappingTable, 0,
- TEST_MAPPING_TABLE_SIZE * sizeof(Paddleboat_Controller_Mapping_Data));
- memcpy(testMappingTable, pbtest_validmap,
- entryCount * sizeof(Paddleboat_Controller_Mapping_Data));
- MappingTableSearch mapSearch(&testMappingTable[0], entryCount);
-
- InitSearchFromExistingEntry(&pbtest_add_entry1, mapSearch);
- GameControllerMappingUtils::findMatchingMapEntry(&mapSearch);
- bool success = GameControllerMappingUtils::insertMapEntry(
- &pbtest_add_entry1, &mapSearch);
- EXPECT_EQ(success, true);
- EXPECT_EQ(testMappingTable[0].vendorId, pbtest_add_entry1.vendorId);
- EXPECT_EQ(testMappingTable[0].productId, pbtest_add_entry1.productId);
- EXPECT_EQ(testMappingTable[6].vendorId, 0x30);
- EXPECT_EQ(testMappingTable[6].productId, 0x1);
-
- // Reset table
- memset(
- testMappingTable, 0,
- TEST_MAPPING_TABLE_SIZE * sizeof(Paddleboat_Controller_Mapping_Data));
- memcpy(testMappingTable, pbtest_validmap,
- entryCount * sizeof(Paddleboat_Controller_Mapping_Data));
- mapSearch.tableEntryCount = entryCount;
-
- InitSearchFromExistingEntry(&pbtest_add_entry2, mapSearch);
- GameControllerMappingUtils::findMatchingMapEntry(&mapSearch);
- success = GameControllerMappingUtils::insertMapEntry(&pbtest_add_entry2,
- &mapSearch);
- EXPECT_EQ(success, true);
- EXPECT_EQ(testMappingTable[5].vendorId, pbtest_add_entry2.vendorId);
- EXPECT_EQ(testMappingTable[5].productId, pbtest_add_entry2.productId);
- EXPECT_EQ(testMappingTable[6].vendorId, 0x30);
- EXPECT_EQ(testMappingTable[6].productId, 0x1);
-
- // Reset table
- memset(
- testMappingTable, 0,
- TEST_MAPPING_TABLE_SIZE * sizeof(Paddleboat_Controller_Mapping_Data));
- memcpy(testMappingTable, pbtest_validmap,
- entryCount * sizeof(Paddleboat_Controller_Mapping_Data));
- mapSearch.tableEntryCount = entryCount;
-
- InitSearchFromExistingEntry(&pbtest_add_entry3, mapSearch);
- GameControllerMappingUtils::findMatchingMapEntry(&mapSearch);
- success = GameControllerMappingUtils::insertMapEntry(&pbtest_add_entry3,
- &mapSearch);
- EXPECT_EQ(success, true);
- EXPECT_EQ(testMappingTable[5].vendorId, 0x30);
- EXPECT_EQ(testMappingTable[5].productId, 0x1);
- EXPECT_EQ(testMappingTable[6].vendorId, pbtest_add_entry3.vendorId);
- EXPECT_EQ(testMappingTable[6].productId, pbtest_add_entry3.productId);
-
- // Reset table
- memset(
- testMappingTable, 0,
- TEST_MAPPING_TABLE_SIZE * sizeof(Paddleboat_Controller_Mapping_Data));
- memcpy(testMappingTable, pbtest_validmap,
- entryCount * sizeof(Paddleboat_Controller_Mapping_Data));
- mapSearch.tableEntryCount = entryCount;
-
- // Test failure on full table
- mapSearch.tableMaxEntryCount = entryCount;
- InitSearchFromExistingEntry(&pbtest_add_entry3, mapSearch);
- GameControllerMappingUtils::findMatchingMapEntry(&mapSearch);
- success = GameControllerMappingUtils::insertMapEntry(&pbtest_add_entry3,
- &mapSearch);
- EXPECT_EQ(success, false);
-}
\ No newline at end of file
diff --git a/GameController/src/test/java/com/google/android/games/paddleboat/ExampleUnitTest.java b/GameController/src/test/java/com/google/android/games/paddleboat/ExampleUnitTest.java
deleted file mode 100644
index 17be7b4..0000000
--- a/GameController/src/test/java/com/google/android/games/paddleboat/ExampleUnitTest.java
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (C) 2021 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-package com.google.android.games.paddleboat;
-
-import org.junit.Test;
-
-import static org.junit.Assert.*;
-
-/**
- * Example local unit test, which will execute on the development machine (host).
- *
- * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
- */
-public class ExampleUnitTest {
- @Test
- public void addition_isCorrect() {
- assertEquals(4, 2 + 2);
- }
-}
\ No newline at end of file
diff --git a/GameTextInput/build.gradle b/GameTextInput/build.gradle
deleted file mode 100644
index a3d1e16..0000000
--- a/GameTextInput/build.gradle
+++ /dev/null
@@ -1,47 +0,0 @@
-plugins {
- id 'com.android.library'
-}
-
-android {
- compileSdkVersion 28
-
- defaultConfig {
- minSdkVersion 14
- targetSdkVersion 28
- versionCode 1
- versionName "1.0"
-
- testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
- consumerProguardFiles "consumer-rules.pro"
- }
-
- buildTypes {
- release {
- minifyEnabled false
- proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
- }
- }
- compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_1_8
- }
-// TODO(florianrival@) Enabling this will trigger "Cannot query the value of this property because it has no value available." error.
-// buildFeatures {
-// prefabPublishing true
-// }
-// prefab {
-// gametextinput {
-// headers "src/main/cpp/include"
-// libraryName "game-text-input"
-// }
-// }
-}
-
-dependencies {
- implementation 'androidx.appcompat:appcompat:1.2.0'
- implementation 'androidx.core:core:1.5.0'
- implementation 'com.google.android.material:material:1.1.0'
- testImplementation 'junit:junit:4.+'
- androidTestImplementation 'androidx.test.ext:junit:1.1.2'
- androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
-}
diff --git a/OWNERS b/OWNERS
index e7d9e22..21d473e 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,8 +1,8 @@
jimblackler@google.com
-daitx@google.com
-willosborn@google.com
-kseniia@google.com
vrepets@google.com
jimyork@google.com
jeremyns@google.com
-florianrival@google.com
+bkaya@google.com
+tomnom@google.com
+artyompp@google.com
+gvamsi@google.com
\ No newline at end of file
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index b2724fd..760ffcf 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,8 +1,6 @@
[Hook Scripts]
-swappy_abi_hook = ${REPO_ROOT}/gamesdk/hooks/check_swappy_abi_version.sh ${PREUPLOAD_COMMIT} ${PREUPLOAD_FILES}
-tuningfork_abi_hook = ${REPO_ROOT}/gamesdk/hooks/check_tuningfork_abi_version.sh ${PREUPLOAD_COMMIT} ${PREUPLOAD_FILES}
-bugfix_hook = ${REPO_ROOT}/gamesdk/hooks/check_bugfix_version.sh ${PREUPLOAD_COMMIT} ${PREUPLOAD_FILES}
+versions_hook = ${REPO_ROOT}/gamesdk/hooks/check_library_versions.sh ${PREUPLOAD_COMMIT} ${PREUPLOAD_FILES}
[Builtin Hooks]
clang_format = true
@@ -16,4 +14,6 @@
[Builtin Hooks Options]
# Only turn on clang-format check for the following subfolders.
clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp,java
- src/ include/ test/collate test/istresser test/memoryadvice test/tuningfork
+ games-frame-pacing/ games-performance-tuner/ game-activity/ game-text-input/ games-controller/ \
+ include/ test/collate test/istresser test/memoryadvice test/tuningfork test/memory_advice \
+ third_party/cube
diff --git a/README.md b/README.md
index b80eb8b..5e21148 100644
--- a/README.md
+++ b/README.md
@@ -4,6 +4,10 @@
Unless you need to compile AGDK from sources, it's recommended that you use the package with the pre-compiled library. You can download it on https://developer.android.com/games/agdk.
+## Requirements
+
+AGDK requires Python executable named "python". A supported version of Python is supplied at `prebuilts/python/PLATFORM_NAME/bin/python`. The easiest way is to create a symlink to that executable and put it into any directory that is in your PATH.
+
## Build AGDK
In order to build AGDK, this project must be initialized using the [*repo* tool](https://gerrit.googlesource.com/git-repo/).
@@ -14,6 +18,11 @@
cd android-games-sdk
repo init -u https://android.googlesource.com/platform/manifest -b android-games-sdk
```
+Ninja binary must be in your PATH like below. Please replace PLATFORM_NAME with either linux-x86 for Linux or darwin-86 for MacOS or windows-x86 for Windows, and run the following command:
+
+```bash
+export PATH="$PATH:`pwd`/../prebuilts/ninja/PLATFORM_NAME"
+```
### Build with locally installed SDK/NDK
@@ -85,14 +94,12 @@
**Build tasks** are:
* `build`: build the libraries with prebuilt SDK/NDK.
-* `buildSpecific`: build the libraries with a specific prebuilt SDK/NDK/STL.
* `buildLocal`: build the libraries with your locally installed Android SDK and NDK.
* `buildUnity`: build the libraries with the (prebuilt) SDK/NDK for use in Unity.
* `buildAar`: build the libraries with prebuilt SDK/NDK for distribution in a AAR with prefab.
**Packaging tasks** are:
* `packageZip`: create a zip of the native libraries for distribution.
-* `packageSpecificZip`: create a zip with the libraries compiled for the specified SDK/NDK/STL.
* `packageLocalZip`: create a zip with the libraries compiled with your locally installed Android SDK and NDK.
* `packageUnityZip`: create a zip for integration in Unity.
* `packageMavenZip`: create a zip with the native libraries in a AAR file in Prefab format and a pom file. You can also use `packageAar` to only get the AAR file.
@@ -100,7 +107,6 @@
**Properties** are:
* `-Plibraries=swappy,tuningfork`: comma-separated list of libraries to build (for packaging/build tasks).
* `-PpackageName=gamesdk`: the name of the package, for packaging tasks. Defaults to "gamesdk".
-* `-Psdk=14 -Pndk=r16 -Pstl='c++_static'`: the SDK, NDK and STL to use for `buildSpecific` and `packageSpecificZip` tasks.
* `-PbuildType=Release`: the build type, "Release" (default) or "Debug".
* Sample related properties:
* `-PincludeSampleSources`: if specified, build tasks will include in their output the sources of the samples of the libraries that are built.
@@ -115,9 +121,6 @@
# All prebuilt SDKs, with sample sources and precompiled samples:
ANDROID_HOME=`pwd`/../prebuilts/sdk ./gradlew packageZip -Plibraries=swappy,tuningfork -PpackageName=fullsdk -PincludeSampleSources -PincludeSampleArtifacts
-# Using a specific prebuilt SDK:
-./gradlew packageSpecificZip -Plibraries=swappy -Psdk=14 -Pndk=r16 -Pstl='c++_static'
-
# Swappy or Swappy+TuningFork for Unity:
./gradlew buildUnity --Plibraries=swappy --PpackageName=swappyUnity
./gradlew buildUnity --Plibraries=swappy,tuningfork --PpackageName=unity
@@ -165,9 +168,9 @@
After opening a sample project using Android Studio, uncomment the line containing `add_gamesdk_sources()`.
This will add the Swappy/Tuning Fork sources as part of the project. You can then inspect the source code (with working auto completions) and run the app in debug mode (with working breakpoints and inspectors).
-#### Note for macOs Users
+#### Note for macOS Users
-You may find that the APT demos will not build on your machine beacuse protoc is being blocked by
+You may find that the APT demos will not build on your machine because protoc is being blocked by
security settings. A warning dialog message is displayed:
```"protoc" cannot be opened because the developer cannot be verified"```
diff --git a/RELEASE_NOTES b/RELEASE_NOTES
index c3d3626..da27867 100644
--- a/RELEASE_NOTES
+++ b/RELEASE_NOTES
@@ -1,62 +1,110 @@
RELEASE NOTES
-Version 1.0.0
-=============
-- This initial version of the Android Game SDK features the Android Frame Pacing library.
-- Features
- - Display buffer synchronization.
- - Auto refresh rate mode and pipelining support.
- - Collection of frame rendering statistics.
- - Graceful selection of behavior at runtime, depending on the presence of the Android, OpenGL,
- and Vulkan features needed by Swappy.
- - Static and dynamic linking of the library.
- - Support for devices with multiple refresh rates.
+Version 2022.3.0
+================
+-All libraries
+ -Decreased number of library permutations built for each library
+-GameActivity
+ -Add the missing historical axis values from the MotionEvent java class to GameActivityMotionEvent
+
+Version 2022.2.0
+================
+- GameActivity
+ -Fix issues with missing .aar in maven artifact for release 1.2.0
+- Memory Advice Library
+ -Added getTotalMemory to the memory advice native API
+
+
+Version 2022.1.0
+================
+This release adds new features to the GameActivity library and the AGDK Tunnel sample. It also
+ includes bug fixes for GameActivity, Tuning Fork and Swappy Libraries.
+- GameActivity
+ -Allow derived classes of GameActivity to handle native library loading.
+ -Always load the native library in GameActivity.onCreate.
+ -Fallback to loading library with name "main" if no other library found.
+- Android Performance Tuner (Tuning Fork)
+ -Fixed getpid returning 0 while trying to get memory telemetry.
+ -Stopped StopLoadingGroup from executing without an active loading group.
+- Android Frame Pacing Library (Swappy)
+ -Fix for swappy not building with ndk <23 and >17.
+ -Exposed API to retrieve the refresh rates supported by the display.
+- AGDK Tunnel sample app
+ -Added Memory Advice library support
+ -Added PGS v2 cloud save to AGDK Tunnel
+ -Integrate PGS sign-in with AGDK Tunnel
+ -Fixed storage error in AGDK Tunnel
+
+Version 2022.0.0
+================
+This major release includes the memory advice library and several fixes for other libraries.
+- GameActivity
+ - Added handling of insets for positioning of game UI around the IME and cutouts.
+ - Bug fixes to avoid input event losses and prevent consuming all input events.
+ - Add key and motion event filters.
+- Android Performance Tuner (Tuning Fork)
+ - Fix corrupted API key bug
+ - Memory reporting is now as time-series rather than histograms.
+ - Allow programmatically setting upload interval.
+- Android Frame Pacing Library (Swappy)
+ - Improvements to auto-mode
+ - Add 'uninjectTracer' functions.
+- Memory Advice Library
+ - Initial version. See the header at include/memory_advice/memory_advice.h for more
+ details.
+
+Version 2021.1.0
+================
+This major release includes several new libraries, a rebranding and minor bug fixes in the Android
+Frame Pacing and Android Performance Tuner libraries.
+- GameActivity is a new component that replaces and improves upon the NDK's NativeActivity.
+- GameTextInput is a new component that improves interaction with the Android soft keyboard.
+- GameController is a new component that allows easier interaction with and management of external
+ controllers.
+- Where we used to refer to this product as the Android Game SDK, it is now referred to as
+ the Android Game Development Kit.
+- See the individual libraries and the source code at
+ https://android.googlesource.com/platform/frameworks/opt/gamesdk for details of changes.
+
+For full documentation, see https://developer.android.com/games/agdk.
+
+Version 1.2.4
+=============
+This release is a bugfix release for APT.
+- Fix loading time and duration recording on devices with inconsistent CLOCK_BOOTTIME.
+
+Version 1.2.3
+=============
+This release adds Oboe to the Game SDK. There are no changes to Android Frame Pacing and
+Android Performance Tuner libraries.
+- Oboe is an open-source C++ library for high-performance audio on Android. Oboe provides
+ a single native API that works in Android 4.1 (API level 16) and higher.
+
+For more information, see https://developer.android.com/games/sdk/oboe/.
+
+Version 1.2.2
+=============
+This release adds new features to APT. There are no new features to Swappy.
+- Android Performance Tuner (Tuning Fork) v 1.3.3
+ - Battery reporting. APT now uploads battery level information as part of its telemetry.
+ - Low memory event reporting. APT records if a previous crash was caused by a low memory (LMK) event.
+ - Loading time groups
+ - New functions TuningFork_startLoadingGroup and TuningFork_stopLoadingGroup enable bracketing
+ of several loading events into a group, e.g. those associated with game initialization or with
+ a certain level loading. Only one group can be active at a time.
+ - Abandoned loading. Loading that is occuring when a player backgrounds the game is logged for
+ special treatment by the backend.
+
+Version 1.2.1
+=============
+This release is an internal feature release for APT (v 1.1.7)
+When a player backgrounds a game during loading, an event is sent for later analysis of loading
+abandonment.
+There are no new features to Swappy.
+
For more information, see https://developer.android.com/games/sdk/.
-Version 1.0.1
-=============
-- This is primarily a bug fix release.
-- Bugs fixed
- - Clean up resources at exit in Swappy Vulkan on the SwappyFallback code path.
- - SwappyVk_destroySwapchain now doesn't destroy device resources if there is more than one
- swapchain.
- - The Swappy version is now printed in logcat.
- - Fixes to crashing and deadlocking in Swappy_destroy for API<=24
-- New features
- - Support for custom thread managers (Swappy_setThreadFunctions)
- - Support for hooking of Vulkan functions (SwappyVk_setFunctionProvider)
-
-Version 1.0.2
-=============
-This release includes bug fixes and changes in the behaviour of auto-mode.
-- Bug fixes for swappy destruction and re-initialization.
-- Pipelining is now *on* by default. If auto-pipelining is on, Swappy can still decide to switch
- it off when the workload is very low.
-- When auto-mode is enabled, Swappy will decide to switch swap interval only after 15% of frames
- within a 2 second window are either faster or slower than expected.
- Note that Swappy will never swap slower than the user-specified swap interval.
-
-Version 1.1.0
-=============
-- This version features the Android Performance Tuner (AKA Tuning Fork) metric reporting and
- quality tuning library.
-- Features of Android Performance Tuner
- - Integration with the Android Frame Pacing Library to automatically record frame times and other
- rendering metrics.
- - Frame timing information is recorded in histograms and uploaded periodically.
- - Annotation of timing data with the current game state.
- - Device characteristics, annotations and quality settings are uploaded with the timing data to
- allow cross-sectional analysis.
- - Special treatment of annotations that signify level-loading time. Frame tick data is not recorded
- during loading, but the duration of each loading period is.
- - Ability to record custom timing information (startTrace/endTrace).
- - In expert mode, quality settings are remotely setable using the Play console.
-- A Unity plugin incorporating the Tuning Fork library is available separately.
-- Known Issues:
- - Incorrect frame pacing on Android 11 w/ multiple refresh rates. Fixed in version 1.2.0
-
-
Version 1.2.0
=============
This release includes several bug fixes for Swappy and new features for APT.
@@ -81,54 +129,58 @@
rather than allocating space for all possible annotation combinations.
See TuningFork_MetricLimits in tuningfork.h.
-
-Version 1.2.1
+Version 1.1.0
=============
-This release is an internal feature release for APT (v 1.1.7)
-When a player backgrounds a game during loading, an event is sent for later analysis of loading
-abandonment.
-There are no new features to Swappy.
-
+- This version features the Android Performance Tuner (AKA Tuning Fork) metric reporting and
+ quality tuning library.
+- Features of Android Performance Tuner
+ - Integration with the Android Frame Pacing Library to automatically record frame times and other
+ rendering metrics.
+ - Frame timing information is recorded in histograms and uploaded periodically.
+ - Annotation of timing data with the current game state.
+ - Device characteristics, annotations and quality settings are uploaded with the timing data to
+ allow cross-sectional analysis.
+ - Special treatment of annotations that signify level-loading time. Frame tick data is not recorded
+ during loading, but the duration of each loading period is.
+ - Ability to record custom timing information (startTrace/endTrace).
+ - In expert mode, quality settings are remotely setable using the Play console.
+- A Unity plugin incorporating the Tuning Fork library is available separately.
+- Known Issues:
+ - Incorrect frame pacing on Android 11 w/ multiple refresh rates. Fixed in version 1.2.0
+
+Version 1.0.2
+=============
+This release includes bug fixes and changes in the behaviour of auto-mode.
+- Bug fixes for swappy destruction and re-initialization.
+- Pipelining is now *on* by default. If auto-pipelining is on, Swappy can still decide to switch
+ it off when the workload is very low.
+- When auto-mode is enabled, Swappy will decide to switch swap interval only after 15% of frames
+ within a 2 second window are either faster or slower than expected.
+ Note that Swappy will never swap slower than the user-specified swap interval.
+
+Version 1.0.1
+=============
+- This is primarily a bug fix release.
+- Bugs fixed
+ - Clean up resources at exit in Swappy Vulkan on the SwappyFallback code path.
+ - SwappyVk_destroySwapchain now doesn't destroy device resources if there is more than one
+ swapchain.
+ - The Swappy version is now printed in logcat.
+ - Fixes to crashing and deadlocking in Swappy_destroy for API<=24
+- New features
+ - Support for custom thread managers (Swappy_setThreadFunctions)
+ - Support for hooking of Vulkan functions (SwappyVk_setFunctionProvider)
+
+Version 1.0.0
+=============
+- This initial version of the Android Game SDK features the Android Frame Pacing library.
+- Features
+ - Display buffer synchronization.
+ - Auto refresh rate mode and pipelining support.
+ - Collection of frame rendering statistics.
+ - Graceful selection of behavior at runtime, depending on the presence of the Android, OpenGL,
+ and Vulkan features needed by Swappy.
+ - Static and dynamic linking of the library.
+ - Support for devices with multiple refresh rates.
+
For more information, see https://developer.android.com/games/sdk/.
-
-Version 1.2.2
-=============
-This release adds new features to APT. There are no new features to Swappy.
-- Android Performance Tuner (Tuning Fork) v 1.3.3
- - Battery reporting. APT now uploads battery level information as part of its telemetry.
- - Low memory event reporting. APT records if a previous crash was caused by a low memory (LMK) event.
- - Loading time groups
- - New functions TuningFork_startLoadingGroup and TuningFork_stopLoadingGroup enable bracketing
- of several loading events into a group, e.g. those associated with game initialization or with
- a certain level loading. Only one group can be active at a time.
- - Abandoned loading. Loading that is occuring when a player backgrounds the game is logged for
- special treatment by the backend.
-
-Version 1.2.3
-=============
-This release adds Oboe to the Game SDK. There are no changes to Android Frame Pacing and
-Android Performance Tuner libraries.
-- Oboe is an open-source C++ library for high-performance audio on Android. Oboe provides
- a single native API that works in Android 4.1 (API level 16) and higher.
-
-For more information, see https://developer.android.com/games/sdk/oboe/.
-
-Version 1.2.4
-=============
-This release is a bugfix release for APT.
-- Fix loading time and duration recording on devices with inconsistent CLOCK_BOOTTIME.
-
-Version 2021.1.0
-================
-This major release includes several new libraries, a rebranding and minor bug fixes in the Android
-Frame Pacing and Android Performance Tuner libraries.
-- GameActivity is a new component that replaces and improves upon the NDK's NativeActivity.
-- GameTextInput is a new component that improves interaction with the Android soft keyboard.
-- GameController is a new component that allows easier interaction with and management of external
- controllers.
-- Where we used to refer to this product as the Android Game SDK, it is now referred to as
- the Android Game Development Kit.
-- See the individual libraries and the source code at
- https://android.googlesource.com/platform/frameworks/opt/gamesdk for details of changes.
-
-For full documentation, see https://developer.android.com/games/agdk.
diff --git a/THIRD_PARTY_NOTICES b/THIRD_PARTY_NOTICES
index b47100e..ad35b54 100644
--- a/THIRD_PARTY_NOTICES
+++ b/THIRD_PARTY_NOTICES
@@ -495,3 +495,290 @@
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
+
+# tensorflow (https://android.googlesource.com/platform/external/tensorflow/)
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+------------------
+Files: third_party/compute_library/...
+
+MIT License
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+------------------
+Files: ACKNOWLEDGEMENTS
+LICENSE
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+------------------
+Files: third_party/hexagon
+
+Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted (subject to the limitations in the
+disclaimer below) provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ * Neither the name of The Linux Foundation nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
+GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT
+HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/VERSIONS b/VERSIONS
index 53ee70e..2a2f1f4 100644
--- a/VERSIONS
+++ b/VERSIONS
@@ -3,13 +3,13 @@
# It is parsed by LibraryVersions.kt and used by the build.gradle file.
# ProjectName is the top-level project directory for AAR and Hybrid libraries.
#
-# Nickname JetPackName ProjectName Version JetPackSubscript
+# Nickname JetPackName ProjectName Version JetPackSubscript
#
-AGDK NA NA 2021.0.0
-swappy games-frame-pacing None 1.10.0 alpha01
-tuningfork games-performance-tuner None 1.5.0 beta02
-oboe oboe None 1.0.0
-game_activity games-activity GameActivity 1.1.0 beta02
-game_text_input games-text-input GameTextInput 1.1.0 beta01
-paddleboat games-controller GameController 1.1.0 alpha01
-memory_advice games-memory-advice None 1.0.0
+AGDK NA NA 2022.1.1 alpha01
+swappy games-frame-pacing games-frame-pacing 2.0.0 alpha01
+tuningfork games-performance-tuner games-performance-tuner 2.0.0 alpha01
+oboe oboe None 1.0.1 alpha01
+game_activity games-activity game-activity 2.0.0 alpha01
+game_text_input games-text-input game-text-input 2.0.0 alpha01
+paddleboat games-controller games-controller 2.0.0 alpha01
+memory_advice games-memory-advice games-memory-advice 2.0.0 alpha01
diff --git a/build.gradle b/build.gradle
index 3cad1cf..b0ad653 100644
--- a/build.gradle
+++ b/build.gradle
@@ -4,8 +4,6 @@
import com.google.androidgamesdk.ExternalAndroidProjectBuilder
import com.google.androidgamesdk.SpecificToolchain
import com.google.androidgamesdk.LocalToolchain
-import com.google.androidgamesdk.OboeDownloader
-import com.google.androidgamesdk.TensorflowPatcher
import com.google.androidgamesdk.BuildInfoFile
import com.google.androidgamesdk.Toolchain
import com.google.androidgamesdk.ToolchainSet
@@ -30,6 +28,7 @@
import static com.google.androidgamesdk.ToolchainEnumerator.filterBuiltLibraries;
import groovy.io.FileType
+import groovy.transform.Field
buildscript {
repositories {
@@ -37,14 +36,14 @@
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.3.2'
+ classpath 'com.android.tools.build:gradle:7.2.0'
}
}
// The following is needed for sub-projects that have their own gradle script and that are
// not using the general swappy/tuningfork multi-ndk build logic below and in buildSrc.
-def buildScript = System.getenv("BUILDBOT_SCRIPT")
-if (buildScript) {
+@Field isRunningOnBuildBot = System.getenv("BUILDBOT_SCRIPT")
+if (isRunningOnBuildBot) {
Properties properties = new Properties()
if (project.rootProject.file('local.properties').exists()) {
@@ -60,7 +59,7 @@
def versions = new LibraryVersions(project.rootProject.file('VERSIONS').getPath())
ext.getAGDKZipName = { ->
- return "agdk-libraries-"+ versions["AGDK"].version + ".zip"
+ return "agdk-libraries-"+ versions["AGDK"].version + (isExpressPackage() ? "_express" : "") + ".zip"
}
def getGitCommit() {
@@ -72,62 +71,11 @@
return sout.toString().trim()
}
-def swappyNativeLibrary = new NativeLibrary(versions['swappy'])
- .addSampleAndroidProject(
- new ExternalAndroidProject(joinPath('samples', "bouncyball"))
- .copySourcesTo("samples/bouncyball")
- .buildDebugApkTo("apks/samples", "bouncyball.apk"))
- .addSampleAndroidProject(
- new ExternalAndroidProject(joinPath('samples', "cube"))
- .copySourcesTo("samples/cube")
- .buildDebugApkTo("apks/samples", "cube.apk")
- .setDebugApkLocation("third_party/cube/app/build/outputs/apk/debug/app-debug.apk"))
- .addSampleAndroidProject(
- new ExternalAndroidProject(joinPath('third_party', "cube"))
- .copySourcesTo("third_party/cube"))
- .addSampleAndroidProject(
- new ExternalAndroidProject("test/swappy/testapp")
- .buildDebugApkTo("apks/test", "swappytest.apk")
- )
+def swappyNativeLibrary = new AndroidArchiveLibrary(versions['swappy'])
-def tuningForkNativeLibrary = new NativeLibrary(versions['tuningfork'])
- .setUsesProtobuf()
- .addSampleAndroidProject(
- new ExternalAndroidProject(joinPath('samples', 'tuningfork', 'insightsdemo'))
- .copySourcesTo("samples/tuningfork/insightsdemo")
- .buildDebugApkTo("apks/samples", "insightsdemo.apk"))
- .addSampleAndroidProject(
- new ExternalAndroidProject(joinPath('samples', 'tuningfork', 'experimentsdemo'))
- .copySourcesTo("samples/tuningfork/experimentsdemo")
- .buildDebugApkTo("apks/samples", "experimentsdemo.apk"))
- .addSampleAndroidProject(
- new ExternalAndroidProject(joinPath('src', 'tuningfork', 'tools', 'validation'))
- .copySourcesTo("src/tuningfork/tools/validation"))
- .addSampleAndroidProject(
- new ExternalAndroidProject("src/tuningfork/tools/TuningForkMonitor")
- .buildDebugApkTo("apks/tools", "TuningForkMonitor.apk"))
- .addSampleAndroidProject(
- new ExternalAndroidProject("test/tuningfork/testapp")
- .buildDebugApkTo("apks/test", "tuningforktest.apk"))
- .addSampleExtraFolder(
- new SampleFolder(joinPath('samples', 'tuningfork', "common")))
- .addSampleExtraFolder(
- new SampleFolder(joinPath('src', 'protobuf')))
- .addSampleExtraFolder(
- new SampleFolder(joinPath('third_party', 'protobuf-3.0.0')))
- .addSampleExtraFolder(
- new SampleFolder(joinPath('..', 'external', 'nanopb-c'), joinPath('external', 'nanopb-c')))
- .addSampleExtraFolder(
- new SampleFolder(joinPath('src', 'tuningfork', 'proto')).include('tuningfork.proto'))
+def tuningForkNativeLibrary = new AndroidArchiveLibrary(versions['tuningfork'])
-def memoryAdviceNativeLibrary = new NativeLibrary(versions['memory_advice'])
- .setUsesTensorflow()
-
-def oboeNativeLibrary = new NativeLibrary(versions['oboe'])
- .setThirdParty()
- .setMinimumAndroidApiLevel(16)
- .setMinimumNdkVersion(17)
- .setSupportedStlVersions(["c++_shared", "c++_static"])
+def memoryAdviceNativeLibrary = new AndroidArchiveLibrary(versions['memory_advice'])
def gameActivityLibrary = new AndroidArchiveLibrary(versions['game_activity'])
// This can be removed after migrating to prefabPublishing:
@@ -137,82 +85,7 @@
// This can be removed after migrating to prefabPublishing:
.setPrefabFolderName("prefab-src")
-def gameControllerLibrary = new NativeLibrary(versions['paddleboat'])
- .setHybridLibrary()
- .setMinimumAndroidApiLevel(16)
- .setMinimumNdkVersion(17)
- .setSupportedStlVersions(["c++_shared", "c++_static"])
- .addSampleAndroidProject(
- new ExternalAndroidProject(joinPath('samples', 'game_controller'))
- .copySourcesTo("samples/game_controller"))
- .addSampleAndroidProject(
- new ExternalAndroidProject(joinPath('samples', 'agdktunnel'))
- .copySourcesTo("samples/agdktunnel"))
-
-def protobufInstallDir() {
- return new File("$projectDir/third_party/protobuf-3.0.0/install/"
- + osFolderName(ExternalToolName.PROTOBUF)).getPath()
-}
-
-def getEnvironment() {
- def env = [:]
- def trans = IDENTITY
- if (OperatingSystem.current().isWindows()) {
- trans = { it.toUpperCase() }
- }
- System.getenv().each { entry -> env[trans(entry.key)] = entry.value }
- return env
-}
-
-task prepare_proto_before {
- def protocBinDir = protobufInstallDir() + "/bin"
- def sixDir = "$projectDir/../external/six"
- def env = getEnvironment()
- env['PATH'] = protocBinDir + System.getProperty("path.separator") + env['PATH']
- env['PYTHONPATH'] = sixDir + System.getProperty("path.separator") + env['PYTHONPATH']
- doLast {
- // Install python-protobuf
- exec {
- workingDir "./third_party/protobuf-3.0.0/python"
- setEnvironment env
- commandLine "python", "setup.py", "install", "--user"
- }
- // Generate nano-pb requirements
- exec {
- workingDir '../external/nanopb-c/generator/proto'
- setEnvironment env
- commandLine 'make'
- }
- }
-}
-
-task prepare_proto(dependsOn: prepare_proto_before) {
- doLast {
- exec {
- commandLine "python"
- args = ["ab_info.py"]
- }
- }
-}
-
-// Download third party libraries that are distributed in the Game SDK
-// but not part of the Game SDK sources.
-task prepare_third_party_libraries {
- outputs.dir("src/oboe")
- outputs.dir("include/oboe")
-
- doLast {
- def oboeDownloader = new OboeDownloader(project)
- oboeDownloader.fetchAndInstall();
- }
-}
-
-task prepare_tensorflow {
- doLast {
- def tensorflowPatcher = new TensorflowPatcher(project)
- tensorflowPatcher.patch();
- }
-}
+def gameControllerLibrary = new AndroidArchiveLibrary(versions['paddleboat'])
allprojects {
buildDir = getOutPath()
@@ -229,7 +102,6 @@
swappyNativeLibrary,
tuningForkNativeLibrary,
memoryAdviceNativeLibrary,
- oboeNativeLibrary,
gameActivityLibrary,
gameTextInputLibrary,
gameControllerLibrary,
@@ -243,6 +115,10 @@
return new File("$projectDir/../out").getPath()
}
+def getLibraryOutPath(library) {
+ return new File("$projectDir/../out_"+library.libraryInfo.nickname).getPath()
+}
+
def getPackagePath() {
return new File("$projectDir/../package").getPath();
}
@@ -303,17 +179,6 @@
delete getBuildPath()
}
-/**
- * Take the classes.jar from a hybrid library and copy it to the
- * Prefab AAR being generated
- */
-def copyHybridClassesFromAar(outFolder, library, doRemove) {
- def hybridAarPath = joinPath(getOutPath(), "outputs/aar/${library.hybridProjectDir}.aar");
- def buildFolder = joinPath(getPackagePath(), outFolder)
- def aarPrefabPatcher = new AarPrefabPatcher()
- aarPrefabPatcher.extractAarClasses(hybridAarPath, buildFolder, doRemove)
-}
-
def prefabBuildFolder(outFolder) {
return joinPath(getPackagePath(), outFolder)
}
@@ -337,11 +202,12 @@
def buildKey = buildInfo.buildKey
def cmakeFolder = joinPath(getTempPath(), buildKey, '.cmake', libraryName)
def buildFolder = prefabBuildFolder(outFolder)
+ def assetsFolder = joinPath(buildFolder, 'assets')
def prefabRoot = prefabDir(outFolder, libraryName)
def sharedLibBuildFolder = joinPath(prefabRoot, 'libs',
- 'android.' + buildKey)
+ 'android.' + buildKey.replace("_static_","_shared_"))
def staticLibBuildFolder = joinPath(prefabRoot + '_static', 'libs',
- 'android.' + buildKey)
+ 'android.' + buildKey.replace("_shared_","_static_"))
def staticsFolder = joinPath(getOutPath(), buildKey)
def sharedIncludeBuildFolder = joinPath(prefabRoot, 'include', libraryName)
def staticIncludeBuildFolder = joinPath(prefabRoot + '_static', 'include', libraryName)
@@ -424,6 +290,15 @@
def jsonStaticModuleDescription = new File(joinPath(buildFolder, 'prefab', 'modules', libraryName + '_static', 'module.json'))
jsonStaticModuleDescription.text = '{"library_name": "lib' + libraryName + '", "export_libraries": []}'
}
+ // 6. Create assets folder
+ if (library.getAssetsDirectory() != null) {
+ copy {
+ from file(library.getAssetsDirectory())
+ include "*.*"
+ into file(assetsFolder)
+ includeEmptyDirs = false
+ }
+ }
}
def deleteDir(File dir) {
@@ -559,6 +434,12 @@
into file(joinPath(buildFolder, 'samples'))
}
+ // Needed for samples that depend on proto preparation (agdktunnel).
+ copy {
+ from file('prepproto.gradle')
+ into file(buildFolder)
+ }
+
// All sample common files
copy {
from file(joinPath('samples', "common"))
@@ -598,17 +479,10 @@
* Also patch the AAR with a prefab folder if necessary.
*/
def copyAndroidArchiveLibrary(AndroidArchiveLibrary library, String outFolder) {
- def aarPath = joinPath(getOutPath(), "outputs", "aar", "${library.projectName}.aar");
+ def aarPath = joinPath(getLibraryOutPath(library), "outputs", "aar", "${library.projectName}-release.aar");
def packageFolder = joinPath(getPackagePath(), outFolder)
- // Inject the sources of the "header only" library, in Prefab format.
- // This can be removed once projects are upgraded to use prefabPublishing.
- if (!library.prefabFolderName.isEmpty()) {
- def prefabFolderPath = joinPath(getProjectDir().getPath(), library.projectName, library.prefabFolderName)
- def aarPrefabPatcher = new AarPrefabPatcher()
- aarPrefabPatcher.injectPrefabFolder(aarPath, prefabFolderPath)
- }
-
+ assert file(aarPath).exists()
copy {
from aarPath
into packageFolder
@@ -631,17 +505,26 @@
def threadChecks = false
def toolchainEnumerator = new ToolchainEnumerator()
def toolchains = toolchainEnumerator.enumerate(toolchainSet, project)
- return toolchainEnumerator.parallelMap(toolchains, { enumeratedToolchain ->
- buildNativeModules(
- new BuildOptions(
- buildType,
- threadChecks,
- enumeratedToolchain.stl,
- enumeratedToolchain.abi),
- enumeratedToolchain.toolchain,
- libraries,
- "src")
- })
+ def nLibraries = libraries.size()
+ def nToolchains = toolchains.size()
+ println("buildNativeLibraries: ${nLibraries} libraries x ${nToolchains} toolchains = ${nLibraries*nToolchains} variants")
+ return libraries.collect { library ->
+ def psize = parallelChunkSize(library);
+ def libraryName = library.libraryInfo.nickname
+ println("buildNativeLibraries: using ${psize} parallel tasks for ${libraryName}")
+ toolchainEnumerator.parallelMap(toolchains, {
+ enumeratedToolchain ->
+ buildNativeModules(
+ new BuildOptions(
+ buildType,
+ threadChecks,
+ enumeratedToolchain.stl,
+ enumeratedToolchain.abi),
+ enumeratedToolchain.toolchain,
+ [library],
+ "src")
+ }, psize, libraryName)
+ }.flatten()
}
def buildSpecificNativeLibraries(specificToolchainConfiguration, libraries,
@@ -740,12 +623,6 @@
}
}
-Collection<NativeLibrary> filterLibrariesByHybridStatus(allLibraries, isHybrid) {
- return filterLibraries(allLibraries).findAll { library ->
- library.isHybridLibrary == isHybrid
- }
-}
-
Collection<AndroidArchiveLibrary> filterAndroidArchiveLibraries(allLibraries) {
return filterLibraries(allLibraries).findAll { library ->
library instanceof AndroidArchiveLibrary
@@ -755,13 +632,56 @@
class BuildTask extends DefaultTask {
}
-task build(type: BuildTask) {
+def isExpressPackage() {
+ return project.hasProperty("express")
+}
+
+def getToolchainSet() {
+ if (isExpressPackage()) {
+ return ToolchainSet.EXPRESS
+ } else {
+ return ToolchainSet.ALL
+ }
+}
+
+def getAarToolchainSet() {
+ if (isExpressPackage()) {
+ return ToolchainSet.EXPRESS_AAR
+ } else {
+ return ToolchainSet.AAR
+ }
+}
+
+// How to chunk the toolchain list for parallel execution.
+def parallelChunkSize(library) {
+ if (project.hasProperty("parallelChunkSize")) {
+ return project.getProperty("parallelChunkSize").toInteger()
+ } else {
+ // By default, use less parallelism for memory_advice and oboe since they
+ // have parallelised sub-builds
+ def isCompoundBuild = library.libraryInfo.nickname=="memory_advice" ||
+ library.libraryInfo.nickname=="oboe"
+ if (isCompoundBuild) {
+ if (isRunningOnBuildBot)
+ return 16
+ else
+ return 4
+ } else {
+ if (isRunningOnBuildBot)
+ return 64
+ else
+ return 16
+ }
+ }
+}
+
+task buildAll(type: BuildTask) {
ext.flattenLibs = false
- ext.withSharedLibs = true
+ ext.withSharedLibs = !isExpressPackage()
ext.withStaticLibs = true
ext.withFullBuildKey = true
ext.nativeBuild = { ->
- buildNativeLibraries(filterNativeLibraries(allLibraries), getBuildType()) }
+ buildNativeLibraries(filterNativeLibraries(allLibraries), getBuildType(), getToolchainSet()) }
}
task buildUnity(type: BuildTask) {
@@ -777,7 +697,7 @@
ext.flattenLibs = false
ext.withSharedLibs = true
ext.withStaticLibs = true
- ext.withFullBuildKey = false
+ ext.withFullBuildKey = true
ext.nativeBuild = { ->
localNativeBuild(filterNativeLibraries(allLibraries), "src", getBuildType())
}
@@ -799,18 +719,16 @@
tasks.withType(BuildTask) {
def nativeLibraries = filterNativeLibraries(allLibraries)
def androidArchiveLibraries = filterAndroidArchiveLibraries(allLibraries)
-
- dependsOn ':extras:assembleRelease'
- nativeLibraries.each { nativeLibrary ->
- if (nativeLibrary.isThirdParty()) dependsOn prepare_third_party_libraries
- if (nativeLibrary.getUsesProtobuf()) dependsOn prepare_proto
- if (nativeLibrary.isHybridLibrary()) dependsOn ":${nativeLibrary.hybridProjectDir}:assembleRelease"
- if (nativeLibrary.getUsesTensorflow()) dependsOn prepare_tensorflow
- }
androidArchiveLibraries.each { androidLibrary ->
dependsOn ":${androidLibrary.projectName}:assembleRelease"
}
+ def packageName = getPackageName()
+
+ // Clear the package path to ensure the task zip is not polluted
+ // by previous build tasks.
+ delete getFullPackagePath()
+
doFirst {
// Ensure some libraries to build were set
if (nativeLibraries.isEmpty() && androidArchiveLibraries.isEmpty()) {
@@ -818,14 +736,10 @@
Must specify which libraries to build,
e.g. ./gradlew build -Plibraries=swappy,tuningfork,game_activity""")
}
+
}
doLast {
- def packageName = getPackageName()
-
- // Clear the package path to ensure the task zip is not polluted
- // by previous build tasks.
- delete getFullPackagePath()
// Android libraries are already built, just add the AARs to the package.
androidArchiveLibraries.each {
@@ -837,33 +751,6 @@
copyNativeLibs(it, packageName, nativeLibraries, withStaticLibs,
withFullBuildKey, flattenLibs, withSharedLibs)
}
- // We need to copy the classes.jar out of a hybrid library and make sure
- // it only has the relevant classes and also patch hybrid libraries with their native components.
- nativeLibraries.each { library ->
- if (library.isHybridLibrary()) {
- // Put classes.jar in 'libs' root and make sure it only has relevant classes
- def outFolder = joinPath(packageName, "libs")
- copyHybridClassesFromAar(outFolder, library, true)
- // Patch aar with native libraries.
- // Use a temporary dir to avoid pollution with other prefab libraries.
- def tempPrefabDir = getPackageName()+"_"+library.hybridProjectDir
- buildNativeLibraries([library], getBuildType(), ToolchainSet.AAR).each { buildInfo ->
- copyNativeLibraryToPrefabDir(buildInfo, tempPrefabDir, library)
- copyHybridClassesFromAar(tempPrefabDir, library, true)
- }
- removeUnneededPrefabDirs(tempPrefabDir, library)
- // Now add the prefab files to the aar.
- def aarPath = joinPath(getOutPath(), "outputs", "aar", "${library.hybridProjectDir}.aar")
- def prefabFolderPath = joinPath(getPackagePath(), tempPrefabDir, "prefab")
- def aarPrefabPatcher = new AarPrefabPatcher()
- aarPrefabPatcher.injectPrefabFolder(aarPath, prefabFolderPath)
- // Finally copy to zip root.
- copy {
- from aarPath
- into getFullPackagePath()
- }
- }
- }
copyNativeLibrariesIncludes(packageName, nativeLibraries)
copyDocs(packageName)
@@ -873,50 +760,6 @@
}
}
-// Build task for making the structure of an AAR that can be used with Prefab.
-// The actual zipping up into an AAR is done in packageAar.
-task buildAarStructure {
- def nativeLibraries = filterNativeLibraries(allLibraries)
- def androidArchiveLibraries = filterAndroidArchiveLibraries(allLibraries)
-
- dependsOn ':extras:assembleRelease'
- nativeLibraries.each { nativeLibrary ->
- if (nativeLibrary.isThirdParty()) dependsOn prepare_third_party_libraries
- if (nativeLibrary.getUsesProtobuf()) dependsOn prepare_proto
- if (nativeLibrary.isHybridLibrary()) dependsOn ":${nativeLibrary.hybridProjectDir}:assembleRelease"
- if (nativeLibrary.getUsesTensorflow()) dependsOn prepare_tensorflow
- }
- androidArchiveLibraries.each { androidLibrary ->
- dependsOn ":${androidLibrary.projectName}:assembleRelease"
- }
-
- doFirst {
- // Clear the package and output path to ensure AAR generation is not polluted
- // by previous build tasks.
- delete getFullPackagePath()
- }
-
- doLast {
- def packageName = getPackageName()
-
- // Android libraries are already built, just add the AARs to the package.
- androidArchiveLibraries.each {
- copyAndroidArchiveLibrary(it, packageName)
- }
-
- // Build native libraries, and add them to the AAR
- nativeLibraries.each { library ->
- buildNativeLibraries([library], getBuildType(), ToolchainSet.AAR).each { buildInfo ->
- copyNativeLibraryToPrefabDir(buildInfo, packageName, library)
- if (library.isHybridLibrary()) {
- copyHybridClassesFromAar(packageName, library, true)
- }
- }
- removeUnneededPrefabDirs(packageName, library)
- }
- }
-}
-
task localUnitTests {
// These unit tests require a single connected device with target architecture set by the
// project property 'arch'.
@@ -963,9 +806,9 @@
fileTree(dir: 'include', include: ['**/*.h'], excludes: ["third_party"]) +
fileTree(dir: 'test/swappy', include: ['**/*.cpp', '**/*.c', '**/*.h']) +
fileTree(dir: 'test/tuningfork', include: ['**/*.cpp', '**/*.c', '**/*.h']) +
- fileTree(dir: 'GameTextInput/prefab-src', include: ['**/*.cpp', '**/*.c', '**/*.h']) +
- fileTree(dir: 'GameActivity/prefab-src', include: ['**/*.cpp', '**/*.c', '**/*.h']) +
- fileTree(dir: 'GameController/src', include: ['**/*.cpp', '**/*.c', '**/*.h', '**/*.hpp'])
+ fileTree(dir: 'games-text-input/prefab-src', include: ['**/*.cpp', '**/*.c', '**/*.h']) +
+ fileTree(dir: 'game-activity/prefab-src', include: ['**/*.cpp', '**/*.c', '**/*.h']) +
+ fileTree(dir: 'games-controller/src', include: ['**/*.cpp', '**/*.c', '**/*.h', '**/*.hpp'])
formattedFiles.files.each { file ->
exec {
@@ -989,31 +832,34 @@
}
}
-/** Create a ZIP file for the entire "package" generated by a build task. */
-def addZipTask(name, buildTask, archName) {
- tasks.register(name, Zip) {
+/** Add a task called $zipTask that creates a ZIP or AAR file, $outputArchiveFileName,
+for the entire "package" generated by $buildTask.
+The task then copies the archive to the 'build' directory and to the 'package' directory. */
+def addZipTask(zipTask, buildTask, outputArchiveFileName) {
+ tasks.register(zipTask, Zip) {
dependsOn buildTask
if (shouldIncludeSampleArtifacts()) {
- dependsOn buildSampleArtifacts
+ buildTask.dependsOn buildSampleArtifacts
}
def packageFolder = getFullPackagePath()
from fileTree(packageFolder)
- exclude archName
+ exclude outputArchiveFileName
destinationDirectory = file(packageFolder)
- archiveFileName = archName
+ archiveFileName = outputArchiveFileName
doLast {
+ // Copy the output archive to 'build'
def outFolder = getBuildPath();
mkdir outFolder;
copy {
- from file(archiveName)
+ from file(outputArchiveFileName)
into outFolder
}
- // This copies across everything, including the zip.
+ // This copies across everything, including the zip, to the $distribution/$package directory.
// We then delete stuff we don't need in the build.sh script.
if (getDistPath() != getPackagePath()) {
copy {
@@ -1023,7 +869,7 @@
}
def out = services.get(StyledTextOutputFactory).create("output")
- out.style(Style.Identifier).text('\n' + archName + ' is in ')
+ out.style(Style.Identifier).text('\n' + outputArchiveFileName + ' is in ')
.style(Style.ProgressStatus)
.println(destinationDirectory.get());
}
@@ -1031,6 +877,50 @@
}
+addZipTask("packageUnityZip", buildUnity, "builds.zip")
+addZipTask("packageZip", buildAll, getAGDKZipName())
+addZipTask("packageLocalZip", buildLocal, "gamesdk.zip")
+addZipTask("packageSpecificZip", buildSpecific, "gamesdk.zip")
+
+// Build task for making the structure of an AAR that can be used with Prefab.
+// The actual zipping up into an AAR is done in packageAar.
+task buildAarStructure {
+ def nativeLibraries = filterNativeLibraries(allLibraries)
+ def androidArchiveLibraries = filterAndroidArchiveLibraries(allLibraries)
+ androidArchiveLibraries.each { androidLibrary ->
+ dependsOn ":${androidLibrary.projectName}:assembleRelease"
+ }
+
+ doFirst {
+ // Clear the package and output path to ensure AAR generation is not polluted
+ // by previous build tasks.
+ delete getFullPackagePath()
+ }
+
+ doLast {
+ def packageName = getPackageName()
+
+ // Android libraries are already built, just add the AARs to the package.
+ androidArchiveLibraries.each {
+ copyAndroidArchiveLibrary(it, packageName)
+ }
+
+ // Build native libraries, and add them to the AAR
+ nativeLibraries.each { library ->
+ buildNativeLibraries([library], getBuildType(), getAarToolchainSet()).each { buildInfo ->
+ copyNativeLibraryToPrefabDir(buildInfo, packageName, library)
+ if (library.isHybridLibrary()) {
+ copyHybridClassesFromAar(library, packageName)
+ }
+ }
+ removeUnneededPrefabDirs(packageName, library)
+ }
+ }
+}
+
+// Task performed before a maven file is prepared with prepareMavenZipContent.
+addZipTask("packageAar", buildAarStructure, "gamesdk.aar")
+
task prepareMavenZipContent {
description "Build the AAR and prepare the versioned pom file for the specified libraries"
dependsOn "packageAar"
@@ -1044,9 +934,10 @@
filterNativeLibraries(allLibraries).forEach { nativeLibrary ->
def libraryName = nativeLibrary.aarLibraryName
def aarVersion = nativeLibrary.aarVersion
-
+ def path = joinPath(packageFolder, "gamesdk.aar")
+ assert file(path).exists()
copy {
- from file(joinPath(packageFolder, "gamesdk.aar"))
+ from file(path)
into file(joinPath(mavenZipContentFolder))
rename "gamesdk.aar", libraryName + "-" + aarVersion + ".aar"
}
@@ -1062,11 +953,18 @@
filterAndroidArchiveLibraries(allLibraries).forEach { androidLibrary ->
def libraryName = androidLibrary.aarLibraryName
def aarVersion = androidLibrary.aarVersion
+ def path = joinPath(packageFolder, androidLibrary.projectName + "-release.aar")
+ assert file(path).exists()
+ copy {
+ from file(path)
+ into file(joinPath(mavenZipContentFolder))
+ rename androidLibrary.projectName + "-release.aar", libraryName + "-" + aarVersion + ".aar"
+ }
copy {
- from file(joinPath(packageFolder, androidLibrary.projectName + ".aar"))
+ from file(path)
into file(joinPath(mavenZipContentFolder))
- rename androidLibrary.projectName + ".aar", libraryName + "-" + aarVersion + ".aar"
+ rename androidLibrary.projectName + "-release.aar", libraryName + "-" + aarVersion + ".aar"
}
copy {
@@ -1104,12 +1002,6 @@
}
}
-addZipTask("packageUnityZip", buildUnity, "builds.zip")
-addZipTask("packageZip", build, getAGDKZipName())
-addZipTask("packageLocalZip", buildLocal, "gamesdk.zip")
-addZipTask("packageSpecificZip", buildSpecific, "gamesdk.zip")
-addZipTask("packageAar", buildAarStructure, "gamesdk.aar")
-
task jetpadJson {
doLast {
def buildInfoFile = new BuildInfoFile()
diff --git a/build.sh b/build.sh
index 9422fb3..ccda7d6 100755
--- a/build.sh
+++ b/build.sh
@@ -12,9 +12,11 @@
# Set up the environment
export ANDROID_HOME=$(pwd)/../prebuilts/sdk
-export ANDROID_NDK_HOME=$(pwd)/../prebuilts/ndk/r20
+export ANDROID_NDK_HOME=$(pwd)/../prebuilts/ndk/r23
export BUILDBOT_SCRIPT=true
export BUILDBOT_CMAKE=$(pwd)/../prebuilts/cmake/linux-x86
+export PATH="$PATH:$(pwd)/../prebuilts/ninja/linux-x86/"
+
cp -Rf samples/sdk_licenses ../prebuilts/sdk/licenses
# Use the distribution path given to the script by the build bot in DIST_DIR. Otherwise,
@@ -26,39 +28,169 @@
dist_dir=$DIST_DIR
fi
-# Build the Game SDK distribution zip and the zips for Maven AARs
+if [ "$(uname)" == "Darwin" ]; then
+ : # Do nothing but skip the next condition so we don't get a bash warning on macos
+elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then
+ # Do only for GNU/Linux platform
+ export JAVA_HOME=$(pwd)/../prebuilts/jdk/jdk11/linux-x86
+fi
+
+## Build the Game SDK distribution zip and the zips for Maven AARs
if [[ $1 == "full" ]]
then
package_name=fullsdk
- ./gradlew packageZip -Plibraries=swappy,tuningfork,oboe,game_activity,game_text_input,paddleboat -PincludeSampleSources -PincludeSampleArtifacts -PdistPath="$dist_dir" -PpackageName=$package_name
- ./gradlew packageMavenZip -Plibraries=swappy -PdistPath="$dist_dir" -PpackageName=$package_name
- ./gradlew packageMavenZip -Plibraries=tuningfork -PdistPath="$dist_dir" -PpackageName=$package_name
- ./gradlew packageMavenZip -Plibraries=oboe -PdistPath="$dist_dir" -PpackageName=$package_name
- ./gradlew packageMavenZip -Plibraries=game_activity -PdistPath="$dist_dir" -PpackageName=$package_name
+ ./gradlew packageZip -Plibraries=swappy,tuningfork,game_activity,game_text_input,paddleboat,memory_advice -PincludeSampleSources -PincludeSampleArtifacts -PdistPath="$dist_dir" -PpackageName=$package_name
+ ./gradlew packageMavenZip -Plibraries=swappy -PdistPath="$dist_dir" -PpackageName=$package_name
+ ./gradlew packageMavenZip -Plibraries=tuningfork -PdistPath="$dist_dir" -PpackageName=$package_name
+ ./gradlew packageMavenZip -Plibraries=game_activity -PdistPath="$dist_dir" -PpackageName=$package_name
./gradlew packageMavenZip -Plibraries=game_text_input -PdistPath="$dist_dir" -PpackageName=$package_name
- ./gradlew packageMavenZip -Plibraries=paddleboat -PdistPath="$dist_dir" -PpackageName=$package_name
- ./gradlew jetpadJson -Plibraries=swappy,tuningfork,game_activity,game_text_input,paddleboat -PdistPath="$dist_dir" -PpackageName=$package_name
+ ./gradlew packageMavenZip -Plibraries=paddleboat -PdistPath="$dist_dir" -PpackageName=$package_name
+ ./gradlew packageMavenZip -Plibraries=memory_advice -PdistPath="$dist_dir" -PpackageName=$package_name
+ ./gradlew jetpadJson -Plibraries=swappy,tuningfork,game_activity,game_text_input,paddleboat,memory_advice -PdistPath="$dist_dir" -PpackageName=$package_name
elif [[ $1 == "samples" ]]
then
package_name=gamesdk
- ./gradlew packageZip -Plibraries=swappy -PincludeSampleSources -PincludeSampleArtifacts -PdistPath="$dist_dir"
+ ./gradlew packageZip -Plibraries=swappy -PincludeSampleSources -PincludeSampleArtifacts -PdistPath="$dist_dir"
./gradlew packageMavenZip -Plibraries=swappy -PdistPath="$dist_dir"
-else
- package_name=gamesdk
- ./gradlew packageZip -Plibraries=swappy,tuningfork,oboe,game_activity,game_text_input,paddleboat -PincludeSampleSources -PdistPath="$dist_dir"
- ./gradlew packageMavenZip -Plibraries=swappy -PdistPath="$dist_dir" -PpackageName=$package_name
- ./gradlew packageMavenZip -Plibraries=tuningfork -PdistPath="$dist_dir" -PpackageName=$package_name
- ./gradlew packageMavenZip -Plibraries=oboe -PdistPath="$dist_dir" -PpackageName=$package_name
- ./gradlew packageMavenZip -Plibraries=game_activity -PdistPath="$dist_dir" -PpackageName=$package_name
+elif [[ $1 == "maven-only" ]]
+then
+ # Only the Maven artifacts for Jetpack
+ package_name=gamesdk-maven
+ ./gradlew packageMavenZip -Plibraries=swappy -PdistPath="$dist_dir" -PpackageName=$package_name
+ ./gradlew packageMavenZip -Plibraries=tuningfork -PdistPath="$dist_dir" -PpackageName=$package_name
+ ./gradlew packageMavenZip -Plibraries=game_activity -PdistPath="$dist_dir" -PpackageName=$package_name
./gradlew packageMavenZip -Plibraries=game_text_input -PdistPath="$dist_dir" -PpackageName=$package_name
- ./gradlew packageMavenZip -Plibraries=paddleboat -PdistPath="$dist_dir" -PpackageName=$package_name
- ./gradlew jetpadJson -Plibraries=swappy,tuningfork,game_activity,game_text_input,paddleboat -PdistPath="$dist_dir" -PpackageName=$package_name
+ ./gradlew packageMavenZip -Plibraries=paddleboat -PdistPath="$dist_dir" -PpackageName=$package_name
+ ./gradlew packageMavenZip -Plibraries=memory_advice -PdistPath="$dist_dir" -PpackageName=$package_name
+ ./gradlew jetpadJson -Plibraries=swappy,tuningfork,game_activity,game_text_input,paddleboat,memory_advice -PdistPath="$dist_dir" -PpackageName=$package_name
+else
+ # The default is to build the express zip
+ package_name=gamesdk-express
+ ./gradlew packageZip -Plibraries=swappy,tuningfork,game_activity,game_text_input,paddleboat,memory_advice -PincludeSampleSources -PincludeSampleArtifacts -PdistPath="$dist_dir" -PpackageName=$package_name
+ ./gradlew packageMavenZip -Plibraries=swappy -PdistPath="$dist_dir" -PpackageName=$package_name
+ ./gradlew packageMavenZip -Plibraries=tuningfork -PdistPath="$dist_dir" -PpackageName=$package_name
+ ./gradlew packageMavenZip -Plibraries=game_activity -PdistPath="$dist_dir" -PpackageName=$package_name
+ ./gradlew packageMavenZip -Plibraries=game_text_input -PdistPath="$dist_dir" -PpackageName=$package_name
+ ./gradlew packageMavenZip -Plibraries=paddleboat -PdistPath="$dist_dir" -PpackageName=$package_name
+ ./gradlew packageMavenZip -Plibraries=memory_advice -PdistPath="$dist_dir" -PpackageName=$package_name
+ ./gradlew jetpadJson -Plibraries=swappy,tuningfork,game_activity,game_text_input,paddleboat,memory_advice -PdistPath="$dist_dir" -PpackageName=$package_name
+fi
+
+if [[ $1 != "maven-only" ]]
+then
+ mkdir -p "$dist_dir/$package_name/apks/samples"
+ mkdir -p "$dist_dir/$package_name/apks/test"
+
+ # Add the tuningfork samples and apks into the Game SDK distribution zip
+ pushd ./samples/tuningfork/insightsdemo/
+ ./gradlew ":app:assembleDebug"
+ popd
+ pushd ./test/tuningfork/testapp/
+ ./gradlew ":app:assembleDebug"
+ popd
+ pushd ./samples/tuningfork/experimentsdemo/
+ ./gradlew ":app:assembleDebug"
+ popd
+
+ # Add the swappy samples
+ pushd samples/bouncyball
+ ./gradlew ":app:assembleDebug"
+ popd
+ pushd third_party/cube
+ ./gradlew ":app:assembleDebug"
+ popd
+
+ # Add the memory_advice samples
+ pushd samples/memory_advice/hogger/
+ ./gradlew ":app:assembleDebug"
+ popd
+
+ # Add the game controller samples
+ pushd samples/game_controller/
+ mkdir -p ./third-party
+ pushd third-party
+ if [ ! -d "imgui" ] ; then
+ git clone https://github.com/ocornut/imgui -b v1.89
+ fi
+ popd
+ popd
+
+ pushd samples/game_controller/gameactivity
+ ./gradlew ":app:assembleDebug"
+ popd
+ pushd samples/game_controller/nativeactivity
+ ./gradlew ":app:assembleDebug"
+ popd
+
+ pushd samples/agdktunnel/third-party/glm
+ if [ ! -d "glm" ] ; then
+ git clone https://github.com/g-truc/glm.git
+ fi
+ popd
+ pushd samples/agdktunnel/
+ ./gradlew ":app:assembleDebug"
+ popd
+
+ # Add the game text input samples
+ pushd samples/game_text_input/game_text_input_testbed
+ ./gradlew ":app:assembleDebug"
+ popd
+
+ cp samples/tuningfork/insightsdemo/app/build/outputs/apk/debug/app-debug.apk \
+ "$dist_dir/$package_name/apks/samples/insightsdemo.apk"
+ cp samples/tuningfork/experimentsdemo/app/build/outputs/apk/debug/app-debug.apk \
+ "$dist_dir/$package_name/apks/samples/experimentsdemo.apk"
+ cp test/tuningfork/testapp/app/build/outputs/apk/debug/app-debug.apk \
+ "$dist_dir/$package_name/apks/test/tuningforktest.apk"
+
+ cp samples/game_controller/nativeactivity/app/build/outputs/apk/debug/app-debug.apk \
+ "$dist_dir/$package_name/apks/samples/game_controller_nativeactivity.apk"
+ cp samples/game_controller/gameactivity/app/build/outputs/apk/debug/app-debug.apk \
+ "$dist_dir/$package_name/apks/samples/game_controller_gameactivity.apk"
+
+ cp samples/bouncyball/app/build/outputs/apk/debug/app-debug.apk \
+ "$dist_dir/$package_name/apks/samples/bouncyball.apk"
+ cp third_party/cube/app/build/outputs/apk/debug/app-debug.apk \
+ "$dist_dir/$package_name/apks/samples/cube.apk"
+
+ cp samples/memory_advice/hogger/app/build/outputs/apk/debug/app-debug.apk \
+ "$dist_dir/$package_name/apks/samples/hogger.apk"
+
+ cp samples/agdktunnel/app/build/outputs/apk/debug/app-debug.apk \
+ "$dist_dir/$package_name/apks/samples/agdktunnel.apk"
+
+ cp samples/game_text_input/game_text_input_testbed/app/build/outputs/apk/debug/app-debug.apk \
+ "$dist_dir/$package_name/apks/samples/game_text_input.apk"
+
+ pushd $dist_dir/$package_name
+ if [[ -z "$(ls -1 agdk-libraries-*.zip 2>/dev/null | grep agdk)" ]] ; then
+ echo 'Could not find the zip "agdk-libraries-*.zip".'
+ exit
+ fi
+ zip -ur agdk-libraries-*.zip "apks/samples/insightsdemo.apk"
+ zip -ur agdk-libraries-*.zip "apks/samples/experimentsdemo.apk"
+ zip -ur agdk-libraries-*.zip "apks/test/tuningforktest.apk"
+
+ zip -ur agdk-libraries-*.zip "apks/samples/game_controller_nativeactivity.apk"
+ zip -ur agdk-libraries-*.zip "apks/samples/game_controller_gameactivity.apk"
+ zip -ur agdk-libraries-*.zip "apks/samples/bouncyball.apk"
+ zip -ur agdk-libraries-*.zip "apks/samples/cube.apk"
+ zip -ur agdk-libraries-*.zip "apks/samples/hogger.apk"
+ zip -ur agdk-libraries-*.zip "apks/samples/agdktunnel.apk"
+ zip -ur agdk-libraries-*.zip "apks/samples/game_text_input.apk"
+ popd
fi
# Calculate hash of the zip file
pushd "$dist_dir/$package_name"
-ZIPNAME=$(ls agdk-libraries-*)
-sha256sum $ZIPNAME > $ZIPNAME.sha256
+for ZIPNAME in agdk-libraries-*
+do
+ if [[ -e $ZIPNAME ]]
+ then
+ sha256sum $ZIPNAME > $ZIPNAME.sha256
+ fi
+ break
+done
popd
pushd "$dist_dir/$package_name"
diff --git a/buildSrc/src/main/java/com/google/androidgamesdk/AarPrefabPatcher.kt b/buildSrc/src/main/java/com/google/androidgamesdk/AarPrefabPatcher.kt
index 8dcd91f..72a5c7e 100644
--- a/buildSrc/src/main/java/com/google/androidgamesdk/AarPrefabPatcher.kt
+++ b/buildSrc/src/main/java/com/google/androidgamesdk/AarPrefabPatcher.kt
@@ -12,12 +12,7 @@
*/
class AarPrefabPatcher {
fun injectPrefabFolder(aarPath: String, prefabFolderPath: String) {
- // Remove the game controller classes and put the new classes.jar
- // back in the .aar
- removeGameControllerClasses(aarPath, "com/google/android/")
-
val aarZipFile = ZipFile(aarPath)
-
val prefabFolderFile = File(prefabFolderPath)
val zipParameters = ZipParameters()
zipParameters.isIncludeRootFolder = false
@@ -25,24 +20,30 @@
aarZipFile.addFolder(prefabFolderFile, zipParameters)
}
- fun extractAarClasses(aarPath: String, prefabFolderPath: String, doRemove: Boolean) {
+ // Copy classes.jar from $aarPath to $prefabFolderPath, if it's not there already, filtering
+ // the classes to remove non-gamecontroller classes.
+ fun extractAarClasses(aarPath: String, prefabFolderPath: String): File {
val jarName = "classes.jar"
val jarPath = joinPath(prefabFolderPath, jarName)
- val checkExists = File(jarPath)
- if (!checkExists.exists()) {
+ val outputFile = File(jarPath)
+ if (!outputFile.exists()) {
val aarZipFile = ZipFile(aarPath)
aarZipFile.extractFile(jarName, prefabFolderPath)
aarZipFile.removeFile(jarName)
- // Remove the non game controller classes and
+ // Remove the non-GameController classes and
// put the modified classes.jar back in the .aar
- if (doRemove) {
- removeClasses(aarZipFile, jarPath, "com/google/androidgamesdk/")
- }
+ val directoryToRemove = "com/google/androidgamesdk"
+ val directoryNotToRemove = "com/google/android/games/paddleboat"
+ removeClasses(jarPath, directoryToRemove, directoryNotToRemove)
+ addClassesJarToAar(jarPath, aarZipFile)
}
+ return outputFile
}
- fun removeGameControllerClasses(aarPath: String, directoryToRemove: String) {
+ // Filter classes in aarPath->classes.jar.
+ fun filterClasses(aarPath: String, directoryToRemove: String,
+ directoryNotToRemove: String?) {
val temporaryDirectory =
createTempDir("gamesdk-remove-classes")
var aarZipFile = ZipFile(aarPath)
@@ -50,27 +51,33 @@
var jarPath = joinPath(temporaryDirectory.absolutePath, jarName)
aarZipFile.extractFile(jarName, temporaryDirectory.absolutePath)
aarZipFile.removeFile(jarName)
- removeClasses(aarZipFile, jarPath, directoryToRemove)
+ removeClasses(jarPath, directoryToRemove, directoryNotToRemove)
+ addClassesJarToAar(jarPath, aarZipFile)
}
- fun removeClasses(aarZipFile: ZipFile, jarPath: String, directoryToRemove: String) {
+ private fun removeClasses(jarPath: String, directoryToRemove: String,
+ directoryNotToRemove: String?) {
var jarZipFile = ZipFile(jarPath)
val fileHeaders = jarZipFile.getFileHeaders();
val removeList = mutableListOf<String>()
for (fileHeader in fileHeaders) {
val fileName = fileHeader.getFileName()
- if (fileName.startsWith(directoryToRemove)) {
- removeList.add(fileName)
+ if (directoryNotToRemove==null || !fileName.startsWith(directoryNotToRemove)) {
+ if ( fileName.startsWith(directoryToRemove)) {
+ removeList.add(fileName)
+ }
}
}
for (removeFileName in removeList) {
jarZipFile.removeFile(removeFileName)
}
+ }
+ private fun addClassesJarToAar(jarPath: String, aarZipFile: ZipFile) {
val zipParameters = ZipParameters()
zipParameters.isIncludeRootFolder = false
aarZipFile.addFile(jarPath, zipParameters)
}
-}
\ No newline at end of file
+}
diff --git a/buildSrc/src/main/java/com/google/androidgamesdk/NativeLibrary.kt b/buildSrc/src/main/java/com/google/androidgamesdk/NativeLibrary.kt
index ac6db3b..a5746fa 100644
--- a/buildSrc/src/main/java/com/google/androidgamesdk/NativeLibrary.kt
+++ b/buildSrc/src/main/java/com/google/androidgamesdk/NativeLibrary.kt
@@ -31,6 +31,8 @@
private set
var usesTensorflow = false
private set
+ var assetsDirectory: String? = null
+ private set
fun addSampleAndroidProject(
sampleAndroidProject: ExternalAndroidProject
@@ -86,6 +88,13 @@
return this
}
+ fun setAssetsDirectory(
+ assetsDirectory: String?
+ ): NativeLibrary {
+ this.assetsDirectory = assetsDirectory
+ return this
+ }
+
data class SampleFolder(
val sourcePath: String,
val destinationPath: String
diff --git a/buildSrc/src/main/java/com/google/androidgamesdk/OboeDownloader.kt b/buildSrc/src/main/java/com/google/androidgamesdk/OboeDownloader.kt
deleted file mode 100644
index bd625a2..0000000
--- a/buildSrc/src/main/java/com/google/androidgamesdk/OboeDownloader.kt
+++ /dev/null
@@ -1,124 +0,0 @@
-package com.google.androidgamesdk
-
-import net.lingala.zip4j.ZipFile
-import org.gradle.api.Project
-import java.io.File
-import java.io.FileOutputStream
-import java.net.URL
-
-private const val OBOE_VERSION: String = "1.5.0"
-
-/**
- * Downloads and patch Oboe (https://github.com/google/oboe) to be included
- * in the Game SDK.
- */
-class OboeDownloader(val project: Project) {
- fun fetchAndInstall() {
- download()
- unzip()
- patchCmakeLists()
- patchQuirksManager()
- }
-
- private fun download() {
- val link = "https://github.com/google/oboe/archive/${OBOE_VERSION}.zip"
- val path = "oboe-sources.zip"
- URL(link).openStream().use { input ->
- FileOutputStream(File(path)).use { output ->
- input.copyTo(output)
- }
- }
- }
-
- private fun unzip() {
- val zipFile = ZipFile("oboe-sources.zip")
- val temporaryDirectory =
- createTempDir("gamesdk-third-party-library-oboe")
- val oboeRootPath = temporaryDirectory.absolutePath +
- "/oboe-${OBOE_VERSION}"
- zipFile.extractAll(temporaryDirectory.absolutePath)
-
- // Import in the Game SDK only the sources, includes and build file.
- project.delete("./src/oboe")
- project.mkdir("./src/oboe")
- project.delete("./include/oboe")
- project.mkdir("./include/oboe")
- project.copy {
- from("$oboeRootPath/src")
- into("./src/oboe")
- }
- project.copy {
- from("$oboeRootPath/include/oboe")
- into("./include/oboe")
- }
- project.copy {
- from("$oboeRootPath/CMakeLists.txt")
- into("./src/oboe")
- }
-
- project.delete(temporaryDirectory.absolutePath)
- }
-
- private fun patchCmakeLists() {
- val cmakeListFile = project.file("./src/oboe/CMakeLists.txt")
- var cmakeListsContent = cmakeListFile.readText()
-
- // Patch the sources path, as the CMakeLists file is now in the sources.
- cmakeListsContent = cmakeListsContent.replace(
- "src/", ""
- )
-
- cmakeListsContent = cmakeListsContent.replace(
- "cmake_minimum_required(VERSION 3.4.1)",
- "cmake_minimum_required(VERSION 3.18.1)\n" +
- "set(CMAKE_CXX_STANDARD 14)\n"
- )
-
- // Add a static library version of Oboe and patch the include paths.
- cmakeListsContent = cmakeListsContent.replace(
- "add_library(oboe \${oboe_sources})",
- "add_library(oboe_static STATIC \${oboe_sources})\n" +
- "add_library(oboe SHARED \${oboe_sources})\n"
- )
- cmakeListsContent = cmakeListsContent.replace(
- "target_include_directories(oboe\n" +
- " PRIVATE src\n" +
- " PUBLIC include)\n",
- "target_include_directories(oboe_static\n" +
- " PUBLIC .\n" +
- " PUBLIC ../../include)\n"
- )
- cmakeListsContent = cmakeListsContent.replace(
- "target_compile_options(oboe",
- "target_compile_options(oboe_static"
- )
- cmakeListsContent = cmakeListsContent.replace(
- "target_link_libraries(oboe PRIVATE",
- "target_link_libraries(oboe PRIVATE oboe_static"
- )
-
- cmakeListFile.writeText(cmakeListsContent)
- }
-
- /**
- * Apply a patch to fix compilation with some SDK/NDK. See
- * https://github.com/google/oboe/issues/1148
- *
- * Can be removed once a new release of Oboe is published.
- */
- private fun patchQuirksManager() {
- val quirksManagerH = project.file("./src/oboe/common/QuirksManager.h")
- var quirksManagerHContent = quirksManagerH.readText()
-
- quirksManagerHContent = quirksManagerHContent.replace(
- "namespace oboe {",
- "#ifndef __ANDROID_API_R__\n" +
- "#define __ANDROID_API_R__ 30\n" +
- "#endif\n" +
- "\n" +
- "namespace oboe {"
- );
-
- quirksManagerH.writeText(quirksManagerHContent);
- }
-}
diff --git a/buildSrc/src/main/java/com/google/androidgamesdk/TensorflowPatcher.kt b/buildSrc/src/main/java/com/google/androidgamesdk/TensorflowPatcher.kt
deleted file mode 100644
index 07d6758..0000000
--- a/buildSrc/src/main/java/com/google/androidgamesdk/TensorflowPatcher.kt
+++ /dev/null
@@ -1,77 +0,0 @@
-package com.google.androidgamesdk
-
-import net.lingala.zip4j.ZipFile
-import org.gradle.api.Project
-import java.io.File
-import java.io.FileOutputStream
-import java.net.URL
-
-/**
- * Patches the Tensorflow library to work with the Game SDK.
- */
-class TensorflowPatcher(val project: Project) {
- fun patch() {
- patchAbseilCpp()
- patchTensorflow()
- }
-
- /**
- * Abseil-cpp library doesn't compile with clang++ provided because of
- * a wrong set of flags. Remove the incompatible flag.
- */
- private fun patchAbseilCpp() {
- val generatedCopts = project.file(
- "../external/abseil-cpp/absl/copts/GENERATED_AbseilCopts.cmake"
- )
- var generatedCoptsContent = generatedCopts.readText()
-
- generatedCoptsContent = generatedCoptsContent.replace(
- "\"-Wno-implicit-int-float-conversion\"",
- ""
- );
- generatedCopts.writeText(generatedCoptsContent);
- }
-
- /**
- * Tensorflow doesn't compile because it uses a different "farmhash"
- * library than the one provided during compilation. Fix the broken headers
- * and namespaces.
- */
- private fun patchTensorflow() {
- val nnapiDelegate = project.file(
- "../external/tensorflow/tensorflow/lite/" +
- "delegates/nnapi/nnapi_delegate.cc")
- var nnapiDelegateContent = nnapiDelegate.readText()
-
- nnapiDelegateContent = nnapiDelegateContent.replace(
- "\"utils/hash/farmhash.h\"",
- "\"farmhash.h\""
- );
- nnapiDelegate.writeText(nnapiDelegateContent);
-
- val lshProjection = project.file(
- "../external/tensorflow/tensorflow/lite/" +
- "kernels/lsh_projection.cc")
- var lshProjectionContent = lshProjection.readText()
-
- lshProjectionContent = lshProjectionContent.replace(
- "\"utils/hash/farmhash.h\"",
- "\"farmhash.h\""
- );
- lshProjectionContent = lshProjectionContent.replace(
- "farmhash::Fingerprint64",
- "::NAMESPACE_FOR_HASH_FUNCTIONS::Fingerprint64");
- lshProjection.writeText(lshProjectionContent);
-
- val serialization = project.file(
- "../external/tensorflow/tensorflow/lite/" +
- "delegates/serialization.cc")
- var serializationContent = serialization.readText()
-
- serializationContent = serializationContent.replace(
- "\"utils/hash/farmhash.h\"",
- "\"farmhash.h\""
- );
- serialization.writeText(serializationContent);
- }
-}
diff --git a/buildSrc/src/main/java/com/google/androidgamesdk/ToolchainEnumerator.kt b/buildSrc/src/main/java/com/google/androidgamesdk/ToolchainEnumerator.kt
index 130e3cc..3476e82 100644
--- a/buildSrc/src/main/java/com/google/androidgamesdk/ToolchainEnumerator.kt
+++ b/buildSrc/src/main/java/com/google/androidgamesdk/ToolchainEnumerator.kt
@@ -3,6 +3,8 @@
import java.lang.reflect.UndeclaredThrowableException
import java.util.stream.Collectors
import org.gradle.api.Project
+import java.time.LocalDateTime
+import java.time.Duration
/**
* Expose the toolchains to use to compile a library against all combinations
@@ -13,45 +15,19 @@
private val abis64Bits = listOf("arm64-v8a", "x86_64")
val allAbis = abis32Bits + abis64Bits
- private val pre17STLs = listOf(
- "c++_static", "c++_shared",
- "gnustl_static", "gnustl_shared"
+ private val stls = listOf(
+ "c++_static", "c++_shared"
)
- private val post17STLs = listOf("c++_static", "c++_shared")
// For the AAR, only build the dynamic libraries against shared STL.
- private val aarPre17STLs = listOf("c++_shared", "gnustl_shared")
- private val aarPost17STLs = listOf("c++_shared")
+ private val aarStls = listOf("c++_shared")
- private val pre17NdkToSdks = mapOf(
- "r14" to listOf(14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24),
- "r15" to listOf(14, 15, 16, 17, 18, 19, 21, 22, 23, 24, 26),
- "r16" to listOf(14, 15, 16, 17, 18, 19, 21, 22, 23, 24, 26, 27),
- "r17" to listOf(14, 15, 16, 17, 18, 19, 21, 22, 23, 24, 26, 27, 28)
- )
- private val post17NdkToSdks = mapOf(
- "r18" to listOf(16, 17, 18, 19, 21, 22, 23, 24, 26, 27, 28),
- "r19" to listOf(16, 17, 18, 19, 21, 22, 23, 24, 26, 27, 28),
- "r20" to listOf(16, 17, 18, 19, 21, 22, 23, 24, 26, 27, 28),
- "r21" to listOf(16, 17, 18, 19, 21, 22, 23, 24, 26, 27, 28, 29)
+ private val allNdkToSdkMap = mapOf(
+ "r23" to listOf(19, 21)
)
- // In the AAR, library search is handled by Prefab, that looks for API 21 for 64 bits architectures
- // even if a lower API level is requested. We need to build a different set of libraries for 32 and
- // 64 bits as a consequence.
- private val aar32BitsPre17NdkToSdks = pre17NdkToSdks
- private val aar32BitsPost17NdkToSdks = post17NdkToSdks
- private val aar64BitsPre17NdkToSdks = mapOf(
- "r14" to listOf(21, 22, 23, 24),
- "r15" to listOf(21, 22, 23, 24, 26),
- "r16" to listOf(21, 22, 23, 24, 26, 27),
- "r17" to listOf(21, 22, 23, 24, 26, 27, 28)
- )
- private val aar64BitsPost17NdkToSdks = mapOf(
- "r18" to listOf(21, 22, 23, 24, 26, 27, 28),
- "r19" to listOf(21, 22, 23, 24, 26, 27, 28),
- "r20" to listOf(21, 22, 23, 24, 26, 27, 28),
- "r21" to listOf(21, 22, 23, 24, 26, 27, 28, 29)
+ private val expressNdkToSdkMap = mapOf(
+ "r23" to listOf(19, 21)
)
fun enumerate(
@@ -59,39 +35,39 @@
project: Project
): List<EnumeratedToolchain> {
return when (toolchainSet) {
- ToolchainSet.ALL -> enumerateAllToolchains(project)
- ToolchainSet.AAR -> enumerateAllAarToolchains(project)
+ ToolchainSet.ALL -> enumerateAllAarToolchains(project, allNdkToSdkMap)
+ ToolchainSet.AAR -> enumerateAllAarToolchains(project, allNdkToSdkMap)
+ ToolchainSet.EXPRESS -> enumerateAllAarToolchains(project, allNdkToSdkMap)
+ ToolchainSet.EXPRESS_AAR -> enumerateAllAarToolchains(project, allNdkToSdkMap)
}
}
- private fun enumerateAllToolchains(project: Project): List<EnumeratedToolchain> {
- val pre17Toolchains: List<EnumeratedToolchain> =
- allAbis.flatMap { abi ->
- pre17STLs.flatMap { stl ->
- enumerateToolchains(project, abi, stl, pre17NdkToSdks)
+ private fun enumerateAllToolchains(project: Project,
+ ndkToSdkMap: Map<String, List<Int>>): List<EnumeratedToolchain> {
+ return allAbis.flatMap { abi ->
+ stls.flatMap { stl ->
+ enumerateToolchains(project, abi, stl, ndkToSdkMap)
}
}
- val post17Toolchains: List<EnumeratedToolchain> =
- allAbis.flatMap { abi ->
- post17STLs.flatMap { stl ->
- enumerateToolchains(project, abi, stl, post17NdkToSdks)
- }
- }
- return pre17Toolchains + post17Toolchains
}
- private fun enumerateAllAarToolchains(project: Project): List<EnumeratedToolchain> {
+ private fun enumerateAllAarToolchains(project: Project,
+ ndkToSdkMap: Map<String, List<Int>>): List<EnumeratedToolchain> {
+ // In the AAR, library search is handled by Prefab, that looks for API 21 for 64 bits architectures
+ // even if a lower API level is requested. We need to build a different set of libraries for 32 and
+ // 64 bits as a consequence.
+ val aar32BitsNdkToSdkMap = ndkToSdkMap.entries.associate {
+ it.key to it.value.filter { sdk -> sdk<21 } }
+ val aar64BitsNdkToSdkMap = ndkToSdkMap.entries.associate {
+ it.key to it.value.filter { sdk -> sdk>=21 } }
+
return abis32Bits.flatMap { abi ->
- aarPre17STLs.flatMap { stl ->
- enumerateToolchains(project, abi, stl, aar32BitsPre17NdkToSdks)
- } + aarPost17STLs.flatMap { stl ->
- enumerateToolchains(project, abi, stl, aar32BitsPost17NdkToSdks)
+ aarStls.flatMap { stl ->
+ enumerateToolchains(project, abi, stl, aar32BitsNdkToSdkMap)
}
} + abis64Bits.flatMap { abi ->
- aarPre17STLs.flatMap { stl ->
- enumerateToolchains(project, abi, stl, aar64BitsPre17NdkToSdks)
- } + aarPost17STLs.flatMap { stl ->
- enumerateToolchains(project, abi, stl, aar64BitsPost17NdkToSdks)
+ aarStls.flatMap { stl ->
+ enumerateToolchains(project, abi, stl, aar64BitsNdkToSdkMap)
}
}
}
@@ -119,7 +95,7 @@
* all tasks to finish).
*/
@Throws(Exception::class)
- fun <T> parallelMap(
+ fun <T> innerParallelMap(
toolchains: List<EnumeratedToolchain>,
f: (EnumeratedToolchain) -> T
): List<T> {
@@ -133,6 +109,32 @@
}
}.collect(Collectors.toList())
}
+ @Throws(Exception::class)
+ fun <T> parallelMap(
+ toolchains: List<EnumeratedToolchain>,
+ f: (EnumeratedToolchain) -> T,
+ chunkSize: Int,
+ debugMessage: String
+ ): List<T> {
+ val nChunkedToolchains = toolchains.size / chunkSize
+ var estDuration = Duration.ZERO
+ val weight = 0.9e0 // For exponentially weighted moving average of each chunk's duration
+ return toolchains.chunked(chunkSize).mapIndexed { i, ts ->
+ System.out.println("ToolchainEnumerator::parallelMap(${debugMessage}) "
+ + "${i+1}/${nChunkedToolchains}, chunkSize=${chunkSize}")
+ val startTime = LocalDateTime.now()
+ val result = innerParallelMap(ts, f)
+ val endTime = LocalDateTime.now()
+ val duration = Duration.between(startTime, endTime)
+ estDuration = Duration.ofMillis((estDuration.toMillis() * (1-weight)
+ + duration.toMillis() * weight).toLong())
+ val estTimeLeft = Duration.ofMillis(((nChunkedToolchains - i - 1)
+ * estDuration.toMillis()).toLong())
+ System.out.println("ToolchainEnumerator::parallelMap(${debugMessage}) took "
+ + "${duration.toString()}, est. time left: ${estTimeLeft}")
+ result
+ }.flatten()
+ }
data class EnumeratedToolchain(
val abi: String,
diff --git a/buildSrc/src/main/java/com/google/androidgamesdk/ToolchainSet.kt b/buildSrc/src/main/java/com/google/androidgamesdk/ToolchainSet.kt
index 049e70c..d538a1b 100644
--- a/buildSrc/src/main/java/com/google/androidgamesdk/ToolchainSet.kt
+++ b/buildSrc/src/main/java/com/google/androidgamesdk/ToolchainSet.kt
@@ -1,6 +1,7 @@
package com.google.androidgamesdk
-// Whether to use all possible toolchains or just those for AAR generation
+// Whether to use all possible toolchains or just those for AAR generation.
+// EXPRESS is a cut-down list to reduce download size.
enum class ToolchainSet {
- ALL, AAR
+ ALL, AAR, EXPRESS, EXPRESS_AAR
}
diff --git a/buildSrc/src/test/kotlin/com/google/androidgamesdk/ToolchainEnumeratorTest.kt b/buildSrc/src/test/kotlin/com/google/androidgamesdk/ToolchainEnumeratorTest.kt
index 27b9b29..61057e3 100644
--- a/buildSrc/src/test/kotlin/com/google/androidgamesdk/ToolchainEnumeratorTest.kt
+++ b/buildSrc/src/test/kotlin/com/google/androidgamesdk/ToolchainEnumeratorTest.kt
@@ -14,104 +14,6 @@
.build()
}
- @Test
- fun generateProperToolchains() {
- val project = createMockProject()
- val allToolchains =
- ToolchainEnumerator().enumerate(ToolchainSet.ALL, project)
-
- // Do a few sanity checks on the enumerated toolchains
- assertEquals(
- // 4 abis, 4 STLs, pre17 ndk/sdks:
- (11 + 11 + 12 + 13) * 4 * 4 +
- // 4 abis, 4 STLs, post17 ndk/sdks:
- (11 + 11 + 11 + 12) * 2 * 4,
- allToolchains.size
- )
- assertEquals(
- 278,
- allToolchains.filter { it.abi == "armeabi-v7a" }.count()
- )
- assertEquals(
- 368,
- allToolchains.filter { it.stl == "c++_static" }.count()
- )
- assertEquals(
- 368,
- allToolchains.filter { it.stl == "c++_shared" }.count()
- )
- assertEquals(
- 188,
- allToolchains.filter { it.stl == "gnustl_static" }.count()
- )
- assertEquals(
- 188,
- allToolchains.filter { it.stl == "gnustl_shared" }.count()
- )
- assertEquals(
- 11 * 4 * 4, // 4 abis, 4 STLs, 11 pre17 ndk/sdks
- allToolchains.filter { it.toolchain.getNdkVersion() == "r14" }
- .count()
- )
- assertEquals(
- 11 * 4 * 2, // 4 abis, 2 STLs, 11 post17 ndk/sdks
- allToolchains.filter { it.toolchain.getNdkVersion() == "r18" }
- .count()
- )
- }
-
- @Test
- fun generateProperAarToolchains() {
- val project = createMockProject()
- val allAarToolchains =
- ToolchainEnumerator().enumerate(ToolchainSet.AAR, project)
-
- // Do a few sanity checks on the enumerated toolchains
- assertEquals(
- // 2 32bits abis, 2 STLs, pre17 ndk/sdks:
- 2 * 2 * (11 + 11 + 12 + 13) +
- // 2 32bits abis, 1 STL, post17 ndk/sdks:
- 2 * 1 * (11 + 11 + 11 + 12) +
- // 2 64bits abis, 2 STLs, pre17 64 bits ndk/sdks:
- 2 * 2 * (4 + 5 + 6 + 7) +
- // 2 64bits abis, 1 STL, post17 64bits ndk/sdks:
- 2 * 1 * (7 + 7 + 7 + 8),
- allAarToolchains.size
- )
- assertEquals(
- 139,
- allAarToolchains.filter { it.abi == "armeabi-v7a" }.count()
- )
- assertEquals(
- 0,
- allAarToolchains.filter { it.stl == "c++_static" }.count()
- )
- assertEquals(
- 286,
- allAarToolchains.filter { it.stl == "c++_shared" }.count()
- )
- assertEquals(
- 0,
- allAarToolchains.filter { it.stl == "gnustl_static" }.count()
- )
- assertEquals(
- 138,
- allAarToolchains.filter { it.stl == "gnustl_shared" }.count()
- )
- assertEquals(
- // 2 abis, 2 STLs, 4 r14 ndk/sdks + 2 abis, 2 STLs, 11 r14 ndk/sdks
- 4 * 2 * 2 + 11 * 2 * 2,
- allAarToolchains.filter { it.toolchain.getNdkVersion() == "r14" }
- .count()
- )
- assertEquals(
- // 2 abis, 2 STLs, 7 r18 ndk/sdks + 2 abis, 1 STL, 11 r18 ndk/sdks
- 7 * 2 * 1 + 11 * 1 * 2,
- allAarToolchains.filter { it.toolchain.getNdkVersion() == "r18" }
- .count()
- )
- }
-
private fun makeUpLibraryInfo(name: String) =
LibraryInfo(name, name + "_j", null, SemanticVersion(1, 0, 0), "alpha")
diff --git a/cert/test/AndroidCertTest/.idea/runConfigurations/app.xml b/cert/test/AndroidCertTest/.idea/runConfigurations/app.xml
deleted file mode 100644
index 429d5e2..0000000
--- a/cert/test/AndroidCertTest/.idea/runConfigurations/app.xml
+++ /dev/null
@@ -1,55 +0,0 @@
-<component name="ProjectRunConfigurationManager">
- <configuration default="false" name="app" type="AndroidRunConfigurationType" factoryName="Android App" activateToolWindowBeforeRun="false">
- <module name="app" />
- <option name="DEPLOY" value="true" />
- <option name="DEPLOY_APK_FROM_BUNDLE" value="false" />
- <option name="DEPLOY_AS_INSTANT" value="false" />
- <option name="ARTIFACT_NAME" value="" />
- <option name="PM_INSTALL_OPTIONS" value="" />
- <option name="DYNAMIC_FEATURES_DISABLED_LIST" value="" />
- <option name="ACTIVITY_EXTRA_FLAGS" value="" />
- <option name="MODE" value="default_activity" />
- <option name="CLEAR_LOGCAT" value="false" />
- <option name="SHOW_LOGCAT_AUTOMATICALLY" value="false" />
- <option name="SKIP_NOOP_APK_INSTALLATIONS" value="true" />
- <option name="FORCE_STOP_RUNNING_APP" value="true" />
- <option name="TARGET_SELECTION_MODE" value="DEVICE_AND_SNAPSHOT_COMBO_BOX" />
- <option name="USE_LAST_SELECTED_DEVICE" value="false" />
- <option name="PREFERRED_AVD" value="" />
- <option name="SELECTED_CLOUD_MATRIX_CONFIGURATION_ID" value="-1" />
- <option name="SELECTED_CLOUD_MATRIX_PROJECT_ID" value="" />
- <option name="DEBUGGER_TYPE" value="Auto" />
- <Auto>
- <option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
- <option name="SHOW_STATIC_VARS" value="true" />
- <option name="WORKING_DIR" value="" />
- <option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
- <option name="SHOW_OPTIMIZED_WARNING" value="true" />
- </Auto>
- <Hybrid>
- <option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
- <option name="SHOW_STATIC_VARS" value="true" />
- <option name="WORKING_DIR" value="" />
- <option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
- <option name="SHOW_OPTIMIZED_WARNING" value="true" />
- </Hybrid>
- <Java />
- <Native>
- <option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
- <option name="SHOW_STATIC_VARS" value="true" />
- <option name="WORKING_DIR" value="" />
- <option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
- <option name="SHOW_OPTIMIZED_WARNING" value="true" />
- </Native>
- <Profilers>
- <option name="ADVANCED_PROFILING_ENABLED" value="false" />
- <option name="STARTUP_CPU_PROFILING_ENABLED" value="false" />
- <option name="STARTUP_CPU_PROFILING_CONFIGURATION_NAME" value="Sample Java Methods" />
- </Profilers>
- <option name="DEEP_LINK" value="" />
- <option name="ACTIVITY_CLASS" value="" />
- <method v="2">
- <option name="Android.Gradle.BeforeRunTask" enabled="true" />
- </method>
- </configuration>
-</component>
\ No newline at end of file
diff --git a/cert/test/AndroidCertTest/.idea/runConfigurations/app__By_Name_.xml b/cert/test/AndroidCertTest/.idea/runConfigurations/app__By_Name_.xml
deleted file mode 100644
index 386e10b..0000000
--- a/cert/test/AndroidCertTest/.idea/runConfigurations/app__By_Name_.xml
+++ /dev/null
@@ -1,62 +0,0 @@
-<component name="ProjectRunConfigurationManager">
- <configuration default="false" name="app (By Name)" type="AndroidRunConfigurationType" factoryName="Android App" activateToolWindowBeforeRun="false">
- <module name="app" />
- <option name="DEPLOY" value="true" />
- <option name="DEPLOY_APK_FROM_BUNDLE" value="false" />
- <option name="DEPLOY_AS_INSTANT" value="false" />
- <option name="ARTIFACT_NAME" value="" />
- <option name="PM_INSTALL_OPTIONS" value="" />
- <option name="DYNAMIC_FEATURES_DISABLED_LIST" value="" />
- <option name="ACTIVITY_EXTRA_FLAGS" value="-a "android.intent.action.ACTION_RUN" -d "Marching Cubes"" />
- <option name="MODE" value="default_activity" />
- <option name="CLEAR_LOGCAT" value="false" />
- <option name="SHOW_LOGCAT_AUTOMATICALLY" value="false" />
- <option name="SKIP_NOOP_APK_INSTALLATIONS" value="true" />
- <option name="FORCE_STOP_RUNNING_APP" value="true" />
- <option name="TARGET_SELECTION_MODE" value="DEVICE_AND_SNAPSHOT_COMBO_BOX" />
- <option name="USE_LAST_SELECTED_DEVICE" value="false" />
- <option name="PREFERRED_AVD" value="" />
- <option name="SELECTED_CLOUD_MATRIX_CONFIGURATION_ID" value="-1" />
- <option name="SELECTED_CLOUD_MATRIX_PROJECT_ID" value="" />
- <option name="DEBUGGER_TYPE" value="Auto" />
- <Auto>
- <option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
- <option name="SHOW_STATIC_VARS" value="true" />
- <option name="WORKING_DIR" value="" />
- <option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
- <option name="SHOW_OPTIMIZED_WARNING" value="true" />
- </Auto>
- <Blaze>
- <option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
- <option name="SHOW_STATIC_VARS" value="true" />
- <option name="WORKING_DIR" value="" />
- <option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
- <option name="SHOW_OPTIMIZED_WARNING" value="true" />
- </Blaze>
- <Hybrid>
- <option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
- <option name="SHOW_STATIC_VARS" value="true" />
- <option name="WORKING_DIR" value="" />
- <option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
- <option name="SHOW_OPTIMIZED_WARNING" value="true" />
- </Hybrid>
- <Java />
- <Native>
- <option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
- <option name="SHOW_STATIC_VARS" value="true" />
- <option name="WORKING_DIR" value="" />
- <option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
- <option name="SHOW_OPTIMIZED_WARNING" value="true" />
- </Native>
- <Profilers>
- <option name="ADVANCED_PROFILING_ENABLED" value="false" />
- <option name="STARTUP_CPU_PROFILING_ENABLED" value="false" />
- <option name="STARTUP_CPU_PROFILING_CONFIGURATION_NAME" value="Sample Java Methods" />
- </Profilers>
- <option name="DEEP_LINK" value="" />
- <option name="ACTIVITY_CLASS" value="" />
- <method v="2">
- <option name="Android.Gradle.BeforeRunTask" enabled="true" />
- </method>
- </configuration>
-</component>
\ No newline at end of file
diff --git a/cert/test/AndroidCertTest/.idea/runConfigurations/app__GAME_LOOP_.xml b/cert/test/AndroidCertTest/.idea/runConfigurations/app__GAME_LOOP_.xml
deleted file mode 100644
index df4062c..0000000
--- a/cert/test/AndroidCertTest/.idea/runConfigurations/app__GAME_LOOP_.xml
+++ /dev/null
@@ -1,62 +0,0 @@
-<component name="ProjectRunConfigurationManager">
- <configuration default="false" name="app (GAME_LOOP)" type="AndroidRunConfigurationType" factoryName="Android App" activateToolWindowBeforeRun="false">
- <module name="app" />
- <option name="DEPLOY" value="true" />
- <option name="DEPLOY_APK_FROM_BUNDLE" value="false" />
- <option name="DEPLOY_AS_INSTANT" value="false" />
- <option name="ARTIFACT_NAME" value="" />
- <option name="PM_INSTALL_OPTIONS" value="" />
- <option name="DYNAMIC_FEATURES_DISABLED_LIST" value="" />
- <option name="ACTIVITY_EXTRA_FLAGS" value="-a com.google.intent.action.TEST_LOOP" />
- <option name="MODE" value="default_activity" />
- <option name="CLEAR_LOGCAT" value="false" />
- <option name="SHOW_LOGCAT_AUTOMATICALLY" value="false" />
- <option name="SKIP_NOOP_APK_INSTALLATIONS" value="true" />
- <option name="FORCE_STOP_RUNNING_APP" value="true" />
- <option name="TARGET_SELECTION_MODE" value="DEVICE_AND_SNAPSHOT_COMBO_BOX" />
- <option name="USE_LAST_SELECTED_DEVICE" value="false" />
- <option name="PREFERRED_AVD" value="" />
- <option name="SELECTED_CLOUD_MATRIX_CONFIGURATION_ID" value="-1" />
- <option name="SELECTED_CLOUD_MATRIX_PROJECT_ID" value="" />
- <option name="DEBUGGER_TYPE" value="Auto" />
- <Auto>
- <option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
- <option name="SHOW_STATIC_VARS" value="true" />
- <option name="WORKING_DIR" value="" />
- <option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
- <option name="SHOW_OPTIMIZED_WARNING" value="true" />
- </Auto>
- <Blaze>
- <option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
- <option name="SHOW_STATIC_VARS" value="true" />
- <option name="WORKING_DIR" value="" />
- <option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
- <option name="SHOW_OPTIMIZED_WARNING" value="true" />
- </Blaze>
- <Hybrid>
- <option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
- <option name="SHOW_STATIC_VARS" value="true" />
- <option name="WORKING_DIR" value="" />
- <option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
- <option name="SHOW_OPTIMIZED_WARNING" value="true" />
- </Hybrid>
- <Java />
- <Native>
- <option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
- <option name="SHOW_STATIC_VARS" value="true" />
- <option name="WORKING_DIR" value="" />
- <option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
- <option name="SHOW_OPTIMIZED_WARNING" value="true" />
- </Native>
- <Profilers>
- <option name="ADVANCED_PROFILING_ENABLED" value="false" />
- <option name="STARTUP_CPU_PROFILING_ENABLED" value="false" />
- <option name="STARTUP_CPU_PROFILING_CONFIGURATION_NAME" value="Sample Java Methods" />
- </Profilers>
- <option name="DEEP_LINK" value="" />
- <option name="ACTIVITY_CLASS" value="" />
- <method v="2">
- <option name="Android.Gradle.BeforeRunTask" enabled="true" />
- </method>
- </configuration>
-</component>
\ No newline at end of file
diff --git a/cert/test/AndroidCertTest/README.md b/cert/test/AndroidCertTest/README.md
index cf5dc4d..e875b3e 100644
--- a/cert/test/AndroidCertTest/README.md
+++ b/cert/test/AndroidCertTest/README.md
@@ -1,13 +1,13 @@
# Game Certification Operation Runner
-The Game Certification Operation Runner is an android application which runs "operations" and gathers data as they run, for analysis after the fact.
+The Game Certification Operation Runner is an android application which runs "operations" and gathers data as they run, for analysis after the fact.
---
## Notes on Structure
-The application reads a configuration file at startup located at `app/src/main/res/raw/configuration.json` to enumerate "operations" to run. Operations are implemented in Java or CPP (via the native-lib) and each is meant to perform a specific piece of functionality (performance testing, conformance testing, pass/fail).
+The application reads a configuration file at startup located at `app/src/main/res/raw/configuration.json` to enumerate "operations" to run. Operations are implemented in Java or CPP (via the cert-lib) and each is meant to perform a specific piece of functionality (performance testing, conformance testing, pass/fail).
-Operations may require data to read or otherwise manipulate. In some cases this data is GLSL shader code, stored in `app/src/main/assets/Shaders` or textures stored in `app/src/main/assets/Textures`.
+Operations may require data to read or otherwise manipulate. In some cases this data is GLSL shader code, stored in `app/src/main/assets/Shaders` or textures stored in `app/src/main/assets/Textures`.
-Note: the `JsonManipulatorOperation` has a JSON file `app/src/main/assets/JsonManipulatorOperation.json` made up of generated artifical data coarsely resembling a social media feed, which is meant to be fairly expensive to serialize/deserialize. Future operations which need some packaged structured data to act on may put it in this location if it makes sense to do so.
\ No newline at end of file
+Note: the `JsonManipulatorOperation` has a JSON file `app/src/main/assets/JsonManipulatorOperation.json` made up of generated artifical data coarsely resembling a social media feed, which is meant to be fairly expensive to serialize/deserialize. Future operations which need some packaged structured data to act on may put it in this location if it makes sense to do so.
diff --git a/cert/test/AndroidCertTest/app/build.gradle b/cert/test/AndroidCertTest/app/build.gradle
index ee677ab..742f0a5 100644
--- a/cert/test/AndroidCertTest/app/build.gradle
+++ b/cert/test/AndroidCertTest/app/build.gradle
@@ -49,7 +49,7 @@
externalNativeBuild {
cmake {
- // compile native-lib optimized
+ // compile cert-lib optimized
arguments << '-DCMAKE_BUILD_TYPE:STRING=Release'
}
}
diff --git a/cert/test/AndroidCertTest/app/src/main/cpp/CMakeLists.txt b/cert/test/AndroidCertTest/app/src/main/cpp/CMakeLists.txt
index 153568f..3603010 100644
--- a/cert/test/AndroidCertTest/app/src/main/cpp/CMakeLists.txt
+++ b/cert/test/AndroidCertTest/app/src/main/cpp/CMakeLists.txt
@@ -11,7 +11,7 @@
#===================================================================================================
include("${GameSDKDir}/src/device_info/device_info.cmake")
-project(native-lib LANGUAGES C CXX)
+project(cert-lib LANGUAGES C CXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -fPIC")
# TODO: Need to see about getting the GameSDK to support Windows.
@@ -19,7 +19,7 @@
# single gamesdk lib. There may be other issues in code, but since we're theoretically building
# for Android and not Windows/Mac/etc., that seems like it shouldn't be an issue.
if (CMAKE_HOST_SYSTEM_NAME MATCHES "Windows")
- set(SWAPPY_LOCATION "${GameSDKDir}/src/swappy")
+ set(SWAPPY_LOCATION "${GameSDKDir}/games-frame-pacing")
set(SWAPPY_LOCATION_COMMON "${SWAPPY_LOCATION}/common")
set(SWAPPY_LOCATION_OPENGL "${SWAPPY_LOCATION}/opengl")
set(SWAPPY_LOCATION_VULKAN "${SWAPPY_LOCATION}/vulkan")
@@ -69,9 +69,6 @@
add_compile_options("-frtti")
-find_library(log-lib
- log)
-
#===================================================================================================
add_definitions(-DVK_USE_PLATFORM_ANDROID_KHR)
@@ -79,7 +76,7 @@
add_library(${PROJECT_NAME} SHARED
${ANDROID_NDK}/sources/third_party/vulkan/src/common/vulkan_wrapper.cpp
# Java-Native glue & super-fundamental code
- native-lib.cpp
+ cert-lib.cpp
# Framework & utilities
ancer/BaseOperation.cpp
@@ -211,7 +208,7 @@
target_link_libraries(${PROJECT_NAME}
GLESv3
android
- ${log-lib}
+ log
EGL
shaderc_lib
basis
diff --git a/cert/test/AndroidCertTest/app/src/main/cpp/ancer/BaseOperation.cpp b/cert/test/AndroidCertTest/app/src/main/cpp/ancer/BaseOperation.cpp
index 3105b18..3baa322 100644
--- a/cert/test/AndroidCertTest/app/src/main/cpp/ancer/BaseOperation.cpp
+++ b/cert/test/AndroidCertTest/app/src/main/cpp/ancer/BaseOperation.cpp
@@ -31,7 +31,7 @@
void *OpenSelfLibrary(void) {
// TODO: Have CMake generate this.
- constexpr const char *this_lib_name = "libnative-lib.so";
+ constexpr const char *this_lib_name = "libcert-lib.so";
void *lib = dlopen(this_lib_name, RTLD_LAZY);
if (lib == nullptr) {
FatalError(TAG, "Failed to load self library");
diff --git a/cert/test/AndroidCertTest/app/src/main/cpp/native-lib.cpp b/cert/test/AndroidCertTest/app/src/main/cpp/cert-lib.cpp
similarity index 100%
rename from cert/test/AndroidCertTest/app/src/main/cpp/native-lib.cpp
rename to cert/test/AndroidCertTest/app/src/main/cpp/cert-lib.cpp
diff --git a/cert/test/AndroidCertTest/app/src/main/java/com/google/gamesdk/gamecert/operationrunner/util/NativeInvoker.java b/cert/test/AndroidCertTest/app/src/main/java/com/google/gamesdk/gamecert/operationrunner/util/NativeInvoker.java
index ea9d7a1..8e2d8df 100644
--- a/cert/test/AndroidCertTest/app/src/main/java/com/google/gamesdk/gamecert/operationrunner/util/NativeInvoker.java
+++ b/cert/test/AndroidCertTest/app/src/main/java/com/google/gamesdk/gamecert/operationrunner/util/NativeInvoker.java
@@ -24,7 +24,7 @@
public class NativeInvoker {
static {
- System.loadLibrary("native-lib");
+ System.loadLibrary("cert-lib");
}
/**
@@ -91,7 +91,7 @@
public static native void setFilesDirectory(String path);
/**
- * Create an operation instance (from native-lib) by name.
+ * Create an operation instance (from cert-lib) by name.
*
* @param suiteId The name of the test suite the operation will run in
* @param description A description of the test suite
diff --git a/game-activity/CMakeLists.txt b/game-activity/CMakeLists.txt
new file mode 100644
index 0000000..9fda9b4
--- /dev/null
+++ b/game-activity/CMakeLists.txt
@@ -0,0 +1,44 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+cmake_minimum_required(VERSION 3.10.0)
+project(gameactivity C CXX)
+set(CMAKE_CXX_STANDARD 17)
+
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/prefab-src/modules/game-activity/include/)
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../include/)
+
+set(GAMEACTIVITY_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/prefab-src/modules/game-activity/include/")
+
+set(GAMEACTIVITY_SRCS
+ ${GAMEACTIVITY_SRC_DIR}/game-activity/GameActivity.cpp
+ ${GAMEACTIVITY_SRC_DIR}/game-activity/GameActivityEvents.cpp
+ ${GAMEACTIVITY_SRC_DIR}/game-activity/native_app_glue/android_native_app_glue.c
+ ${GAMEACTIVITY_SRC_DIR}/game-text-input/gametextinput.cpp)
+set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror -Os")
+set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions -fno-rtti")
+set( CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -g0")
+
+# We use this empty library to distribute source files in our .aar
+# See the build.gradle file
+add_library(game-activity STATIC ${CMAKE_CURRENT_SOURCE_DIR}/null.cpp)
+
+add_library(game-activity_static STATIC ${GAMEACTIVITY_SRCS})
+
+set_target_properties(game-activity PROPERTIES
+ LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/build )
+
+set_target_properties(game-activity_static PROPERTIES
+ LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/build )
diff --git a/GameActivity/README.md b/game-activity/README.md
similarity index 100%
rename from GameActivity/README.md
rename to game-activity/README.md
diff --git a/game-activity/build.gradle b/game-activity/build.gradle
new file mode 100644
index 0000000..07c9fbe
--- /dev/null
+++ b/game-activity/build.gradle
@@ -0,0 +1,79 @@
+plugins {
+ id 'com.android.library'
+}
+
+buildDir = "../../out_game_activity"
+
+android {
+ compileSdkVersion 31
+
+ defaultConfig {
+ minSdkVersion 19
+ targetSdkVersion 31
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ consumerProguardFiles "consumer-rules.pro"
+ externalNativeBuild {
+ cmake {
+ if (project.hasProperty("stl")) {
+ arguments '-DANDROID_STL='+ project.stl
+ } else {
+ arguments '-DANDROID_STL=c++_shared'
+ }
+ }
+ }
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ externalNativeBuild {
+ cmake {
+ path "CMakeLists.txt"
+ version "3.18.0+"
+ }
+ }
+ buildFeatures {
+ prefabPublishing true
+ }
+ prefab {
+ gameactivity {
+ name "game-activity"
+ headers "prefab-src/modules/game-activity/include/"
+ }
+ gameactivity_static {
+ name "game-activity_static"
+ headers "prefab-src/modules/game-activity/include/"
+ }
+ }
+
+ // If we don't include this line the created .aar contains the c++ std lib
+ // at <.aar_file>/jni/<abi>/libc++_shared.so. When we have multiple
+ // libraries containing libc++_shared.so the linker complains because it
+ // can't choose between them. Because we use prefab we don't need to
+ // contents of the <.aar_file>/jni/* folder so we can just exclude it here
+ // to prevent the jni folder from being created.
+ packagingOptions {
+ exclude("**.so")
+ }
+}
+
+dependencies {
+ implementation 'androidx.appcompat:appcompat:1.2.0'
+ implementation 'androidx.core:core:1.5.0'
+ implementation 'com.google.android.material:material:1.2.1'
+}
+
+repositories {
+ mavenCentral()
+}
diff --git a/game-activity/consumer-rules.pro b/game-activity/consumer-rules.pro
new file mode 100644
index 0000000..1d1864f
--- /dev/null
+++ b/game-activity/consumer-rules.pro
@@ -0,0 +1,3 @@
+-keep class androidx.core.graphics.Insets** { *; }
+-keep class androidx.core.view.WindowInsetsCompat** { *; }
+-keep class com.google.android.games.paddleboat.** { *; }
diff --git a/GameActivity/consumer-rules.pro b/game-activity/null.cpp
similarity index 100%
rename from GameActivity/consumer-rules.pro
rename to game-activity/null.cpp
diff --git a/game-activity/prefab-src/modules/game-activity/include/common b/game-activity/prefab-src/modules/game-activity/include/common
new file mode 120000
index 0000000..9388f34
--- /dev/null
+++ b/game-activity/prefab-src/modules/game-activity/include/common
@@ -0,0 +1 @@
+../../../../../include/common/
\ No newline at end of file
diff --git a/GameActivity/prefab-src/modules/game-activity/include/game-activity/GameActivity.cpp b/game-activity/prefab-src/modules/game-activity/include/game-activity/GameActivity.cpp
similarity index 63%
rename from GameActivity/prefab-src/modules/game-activity/include/game-activity/GameActivity.cpp
rename to game-activity/prefab-src/modules/game-activity/include/game-activity/GameActivity.cpp
index 30f65f7..3c0e762 100644
--- a/GameActivity/prefab-src/modules/game-activity/include/game-activity/GameActivity.cpp
+++ b/game-activity/prefab-src/modules/game-activity/include/game-activity/GameActivity.cpp
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#define LOG_TAG "GameActivity"
#include "GameActivity.h"
@@ -39,54 +38,10 @@
#include <mutex>
#include <string>
-// TODO(b/187147166): these functions were extracted from the Game SDK
-// (gamesdk/src/common/system_utils.h). system_utils.h/cpp should be used
-// instead.
+#include "GameActivityLog.h"
+
namespace {
-#if __ANDROID_API__ >= 26
-std::string getSystemPropViaCallback(const char *key,
- const char *default_value = "") {
- const prop_info *prop = __system_property_find(key);
- if (prop == nullptr) {
- return default_value;
- }
- std::string return_value;
- auto thunk = [](void *cookie, const char * /*name*/, const char *value,
- uint32_t /*serial*/) {
- if (value != nullptr) {
- std::string *r = static_cast<std::string *>(cookie);
- *r = value;
- }
- };
- __system_property_read_callback(prop, thunk, &return_value);
- return return_value;
-}
-#else
-std::string getSystemPropViaGet(const char *key,
- const char *default_value = "") {
- char buffer[PROP_VALUE_MAX + 1] = ""; // +1 for terminator
- int bufferLen = __system_property_get(key, buffer);
- if (bufferLen > 0)
- return buffer;
- else
- return "";
-}
-#endif
-
-std::string GetSystemProp(const char *key, const char *default_value = "") {
-#if __ANDROID_API__ >= 26
- return getSystemPropViaCallback(key, default_value);
-#else
- return getSystemPropViaGet(key, default_value);
-#endif
-}
-
-int GetSystemPropAsInt(const char *key, int default_value = 0) {
- std::string prop = GetSystemProp(key);
- return prop == "" ? default_value : strtoll(prop.c_str(), nullptr, 10);
-}
-
struct OwnedGameTextInputState {
OwnedGameTextInputState &operator=(const GameTextInputState &rhs) {
inner = rhs;
@@ -100,93 +55,6 @@
} // anonymous namespace
-#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__);
-#define ALOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__);
-#define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__);
-#ifdef NDEBUG
-#define ALOGV(...)
-#else
-#define ALOGV(...) \
- __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__);
-#endif
-
-/* Returns 2nd arg. Used to substitute default value if caller's vararg list
- * is empty.
- */
-#define __android_second(first, second, ...) second
-
-/* If passed multiple args, returns ',' followed by all but 1st arg, otherwise
- * returns nothing.
- */
-#define __android_rest(first, ...) , ##__VA_ARGS__
-
-#define android_printAssert(cond, tag, fmt...) \
- __android_log_assert(cond, tag, \
- __android_second(0, ##fmt, NULL) __android_rest(fmt))
-
-#define CONDITION(cond) (__builtin_expect((cond) != 0, 0))
-
-#ifndef LOG_ALWAYS_FATAL_IF
-#define LOG_ALWAYS_FATAL_IF(cond, ...) \
- ((CONDITION(cond)) \
- ? ((void)android_printAssert(#cond, LOG_TAG, ##__VA_ARGS__)) \
- : (void)0)
-#endif
-
-#ifndef LOG_ALWAYS_FATAL
-#define LOG_ALWAYS_FATAL(...) \
- (((void)android_printAssert(NULL, LOG_TAG, ##__VA_ARGS__)))
-#endif
-
-/*
- * Simplified macro to send a warning system log message using current LOG_TAG.
- */
-#ifndef SLOGW
-#define SLOGW(...) \
- ((void)__android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__))
-#endif
-
-#ifndef SLOGW_IF
-#define SLOGW_IF(cond, ...) \
- ((__predict_false(cond)) \
- ? ((void)__android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)) \
- : (void)0)
-#endif
-
-/*
- * Versions of LOG_ALWAYS_FATAL_IF and LOG_ALWAYS_FATAL that
- * are stripped out of release builds.
- */
-#if LOG_NDEBUG
-
-#ifndef LOG_FATAL_IF
-#define LOG_FATAL_IF(cond, ...) ((void)0)
-#endif
-#ifndef LOG_FATAL
-#define LOG_FATAL(...) ((void)0)
-#endif
-
-#else
-
-#ifndef LOG_FATAL_IF
-#define LOG_FATAL_IF(cond, ...) LOG_ALWAYS_FATAL_IF(cond, ##__VA_ARGS__)
-#endif
-#ifndef LOG_FATAL
-#define LOG_FATAL(...) LOG_ALWAYS_FATAL(__VA_ARGS__)
-#endif
-
-#endif
-
-/*
- * Assertion that generates a log message when the assertion fails.
- * Stripped out of release builds. Uses the current LOG_TAG.
- */
-#ifndef ALOG_ASSERT
-#define ALOG_ASSERT(cond, ...) LOG_FATAL_IF(!(cond), ##__VA_ARGS__)
-#endif
-
-#define LOG_TRACE(...)
-
#ifndef NELEM
#define NELEM(x) ((int)(sizeof(x) / sizeof((x)[0])))
#endif
@@ -213,6 +81,30 @@
} gInsetsClassInfo;
/*
+ * JNI fields of the Configuration Java class.
+ */
+static struct ConfigurationClassInfo {
+ jfieldID colorMode;
+ jfieldID densityDpi;
+ jfieldID fontScale;
+ jfieldID fontWeightAdjustment;
+ jfieldID hardKeyboardHidden;
+ jfieldID keyboard;
+ jfieldID keyboardHidden;
+ jfieldID mcc;
+ jfieldID mnc;
+ jfieldID navigation;
+ jfieldID navigationHidden;
+ jfieldID orientation;
+ jfieldID screenHeightDp;
+ jfieldID screenLayout;
+ jfieldID screenWidthDp;
+ jfieldID smallestScreenWidthDp;
+ jfieldID touchscreen;
+ jfieldID uiMode;
+} gConfigurationClassInfo;
+
+/*
* JNI methods of the WindowInsetsCompat.Type Java class.
*/
static struct {
@@ -228,6 +120,7 @@
int32_t cmd;
int64_t arg1;
int64_t arg2;
+ int64_t arg3;
};
/*
@@ -240,19 +133,46 @@
CMD_SET_WINDOW_FLAGS,
CMD_SHOW_SOFT_INPUT,
CMD_HIDE_SOFT_INPUT,
- CMD_SET_SOFT_INPUT_STATE
+ CMD_SET_SOFT_INPUT_STATE,
+ CMD_SET_IME_EDITOR_INFO
};
/*
+ * Last known Configuration values. They may be accessed from the different
+ * thread, this is why they are made atomic.
+ */
+static struct Configuration {
+ std::atomic_int colorMode;
+ std::atomic_int densityDpi;
+ std::atomic<float> fontScale;
+ std::atomic_int fontWeightAdjustment;
+ std::atomic_int hardKeyboardHidden;
+ std::atomic_int keyboard;
+ std::atomic_int keyboardHidden;
+ std::atomic_int mcc;
+ std::atomic_int mnc;
+ std::atomic_int navigation;
+ std::atomic_int navigationHidden;
+ std::atomic_int orientation;
+ std::atomic_int screenHeightDp;
+ std::atomic_int screenLayout;
+ std::atomic_int screenWidthDp;
+ std::atomic_int smallestScreenWidthDp;
+ std::atomic_int touchscreen;
+ std::atomic_int uiMode;
+} gConfiguration;
+
+/*
* Write a command to be executed by the GameActivity on the application main
* thread.
*/
-static void write_work(int fd, int32_t cmd, int64_t arg1 = 0,
- int64_t arg2 = 0) {
+static void write_work(int fd, int32_t cmd, int64_t arg1 = 0, int64_t arg2 = 0,
+ int64_t arg3 = 0) {
ActivityWork work;
work.cmd = cmd;
work.arg1 = arg1;
work.arg2 = arg2;
+ work.arg3 = arg3;
LOG_TRACE("write_work: cmd=%d", cmd);
restart:
@@ -291,12 +211,10 @@
* Native state for interacting with the GameActivity class.
*/
struct NativeCode : public GameActivity {
- NativeCode(void *_dlhandle, GameActivity_createFunc *_createFunc) {
+ NativeCode() {
memset((GameActivity *)this, 0, sizeof(GameActivity));
memset(&callbacks, 0, sizeof(callbacks));
memset(&insetsState, 0, sizeof(insetsState));
- dlhandle = _dlhandle;
- createActivityFunc = _createFunc;
nativeWindow = NULL;
mainWorkRead = mainWorkWrite = -1;
gameTextInput = NULL;
@@ -324,12 +242,6 @@
setSurface(NULL);
if (mainWorkRead >= 0) close(mainWorkRead);
if (mainWorkWrite >= 0) close(mainWorkWrite);
- if (dlhandle != NULL) {
- // for now don't unload... we probably should clean this
- // up and only keep one open dlhandle per proc, since there
- // is really no benefit to unloading the code.
- // dlclose(dlhandle);
- }
}
void setSurface(jobject _surface) {
@@ -345,9 +257,6 @@
GameActivityCallbacks callbacks;
- void *dlhandle;
- GameActivity_createFunc *createActivityFunc;
-
std::string internalDataPathObj;
std::string externalDataPathObj;
std::string obbPathObj;
@@ -374,6 +283,8 @@
ARect insetsState[GAMECOMMON_INSETS_TYPE_COUNT];
};
+static void readConfigurationValues(NativeCode *code, jobject javaConfig);
+
extern "C" void GameActivity_finish(GameActivity *activity) {
NativeCode *code = static_cast<NativeCode *>(activity);
write_work(code->mainWorkWrite, CMD_FINISH, 0);
@@ -476,6 +387,13 @@
case CMD_HIDE_SOFT_INPUT: {
GameTextInput_hideIme(code->gameTextInput, work.arg1);
} break;
+ case CMD_SET_IME_EDITOR_INFO: {
+ code->env->CallVoidMethod(
+ code->javaGameActivity,
+ gGameActivityClassInfo.setImeEditorInfoFields, work.arg1,
+ work.arg2, work.arg3);
+ checkAndClearException(code->env, "setImeEditorInfo");
+ } break;
default:
ALOGW("Unknown work command: %d", work.cmd);
break;
@@ -485,40 +403,16 @@
}
// ------------------------------------------------------------------------
-
static thread_local std::string g_error_msg;
-static jlong loadNativeCode_native(JNIEnv *env, jobject javaGameActivity,
- jstring path, jstring funcName,
- jstring internalDataDir, jstring obbDir,
- jstring externalDataDir, jobject jAssetMgr,
- jbyteArray savedState) {
- LOG_TRACE("loadNativeCode_native");
- const char *pathStr = env->GetStringUTFChars(path, NULL);
+static jlong initializeNativeCode_native(
+ JNIEnv *env, jobject javaGameActivity, jstring internalDataDir,
+ jstring obbDir, jstring externalDataDir, jobject jAssetMgr,
+ jbyteArray savedState, jobject javaConfig) {
+ LOG_TRACE("initializeNativeCode_native");
NativeCode *code = NULL;
- void *handle = dlopen(pathStr, RTLD_LAZY);
-
- env->ReleaseStringUTFChars(path, pathStr);
-
- if (handle == nullptr) {
- g_error_msg = dlerror();
- ALOGE("GameActivity dlopen(\"%s\") failed: %s", pathStr,
- g_error_msg.c_str());
- return 0;
- }
-
- const char *funcStr = env->GetStringUTFChars(funcName, NULL);
- code = new NativeCode(handle,
- (GameActivity_createFunc *)dlsym(handle, funcStr));
- env->ReleaseStringUTFChars(funcName, funcStr);
-
- if (code->createActivityFunc == nullptr) {
- g_error_msg = dlerror();
- ALOGW("GameActivity_onCreate not found: %s", g_error_msg.c_str());
- delete code;
- return 0;
- }
+ code = new NativeCode();
code->looper = ALooper_forThread();
if (code->looper == nullptr) {
@@ -588,7 +482,10 @@
rawSavedState = env->GetByteArrayElements(savedState, NULL);
rawSavedSize = env->GetArrayLength(savedState);
}
- code->createActivityFunc(code, rawSavedState, rawSavedSize);
+
+ readConfigurationValues(code, javaConfig);
+
+ GameActivity_onCreate(code, rawSavedState, rawSavedSize);
code->gameTextInput = GameTextInput_init(env, 0);
GameTextInput_setEventCallback(code->gameTextInput,
@@ -609,9 +506,9 @@
return result;
}
-static void unloadNativeCode_native(JNIEnv *env, jobject javaGameActivity,
+static void terminateNativeCode_native(JNIEnv *env, jobject javaGameActivity,
jlong handle) {
- LOG_TRACE("unloadNativeCode_native");
+ LOG_TRACE("terminateNativeCode_native");
if (handle != 0) {
NativeCode *code = (NativeCode *)handle;
delete code;
@@ -695,11 +592,50 @@
}
}
+static void readConfigurationValues(NativeCode *code, jobject javaConfig) {
+ gConfiguration.colorMode =
+ code->env->GetIntField(javaConfig, gConfigurationClassInfo.colorMode);
+ gConfiguration.densityDpi =
+ code->env->GetIntField(javaConfig, gConfigurationClassInfo.densityDpi);
+ gConfiguration.fontScale =
+ code->env->GetFloatField(javaConfig, gConfigurationClassInfo.fontScale);
+ gConfiguration.fontWeightAdjustment = code->env->GetIntField(
+ javaConfig, gConfigurationClassInfo.fontWeightAdjustment);
+ gConfiguration.hardKeyboardHidden = code->env->GetIntField(
+ javaConfig, gConfigurationClassInfo.hardKeyboardHidden);
+ gConfiguration.mcc =
+ code->env->GetIntField(javaConfig, gConfigurationClassInfo.mcc);
+ gConfiguration.mnc =
+ code->env->GetIntField(javaConfig, gConfigurationClassInfo.mnc);
+ gConfiguration.navigation =
+ code->env->GetIntField(javaConfig, gConfigurationClassInfo.navigation);
+ gConfiguration.navigationHidden = code->env->GetIntField(
+ javaConfig, gConfigurationClassInfo.navigationHidden);
+ gConfiguration.orientation =
+ code->env->GetIntField(javaConfig, gConfigurationClassInfo.orientation);
+ gConfiguration.screenHeightDp = code->env->GetIntField(
+ javaConfig, gConfigurationClassInfo.screenHeightDp);
+ gConfiguration.screenLayout = code->env->GetIntField(
+ javaConfig, gConfigurationClassInfo.screenLayout);
+ gConfiguration.screenWidthDp = code->env->GetIntField(
+ javaConfig, gConfigurationClassInfo.screenWidthDp);
+ gConfiguration.smallestScreenWidthDp = code->env->GetIntField(
+ javaConfig, gConfigurationClassInfo.smallestScreenWidthDp);
+ gConfiguration.touchscreen =
+ code->env->GetIntField(javaConfig, gConfigurationClassInfo.touchscreen);
+ gConfiguration.uiMode =
+ code->env->GetIntField(javaConfig, gConfigurationClassInfo.uiMode);
+
+ checkAndClearException(code->env, "Configuration.get");
+}
+
static void onConfigurationChanged_native(JNIEnv *env, jobject javaGameActivity,
- jlong handle) {
+ jlong handle, jobject javaNewConfig) {
LOG_TRACE("onConfigurationChanged_native");
if (handle != 0) {
NativeCode *code = (NativeCode *)handle;
+ readConfigurationValues(code, javaNewConfig);
+
if (code->callbacks.onConfigurationChanged != NULL) {
code->callbacks.onConfigurationChanged(code);
}
@@ -750,6 +686,11 @@
if (handle != 0) {
NativeCode *code = (NativeCode *)handle;
ANativeWindow *oldNativeWindow = code->nativeWindow;
+ // Fix for window being destroyed behind the scenes on older Android
+ // versions.
+ if (oldNativeWindow != NULL) {
+ ANativeWindow_acquire(oldNativeWindow);
+ }
code->setSurface(surface);
if (oldNativeWindow != code->nativeWindow) {
if (oldNativeWindow != NULL &&
@@ -771,14 +712,22 @@
// Maybe it was resized?
int32_t newWidth = ANativeWindow_getWidth(code->nativeWindow);
int32_t newHeight = ANativeWindow_getHeight(code->nativeWindow);
+
if (newWidth != code->lastWindowWidth ||
newHeight != code->lastWindowHeight) {
+ code->lastWindowWidth = newWidth;
+ code->lastWindowHeight = newHeight;
+
if (code->callbacks.onNativeWindowResized != NULL) {
code->callbacks.onNativeWindowResized(
code, code->nativeWindow, newWidth, newHeight);
}
}
}
+ // Release the window we acquired earlier.
+ if (oldNativeWindow != NULL) {
+ ANativeWindow_release(oldNativeWindow);
+ }
}
}
@@ -808,276 +757,102 @@
}
}
-static bool enabledAxes[GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT] = {
- /* AMOTION_EVENT_AXIS_X */ true,
- /* AMOTION_EVENT_AXIS_Y */ true,
- // Disable all other axes by default (they can be enabled using
- // `GameActivityPointerAxes_enableAxis`).
- false};
-
-extern "C" void GameActivityPointerAxes_enableAxis(int32_t axis) {
- if (axis < 0 || axis >= GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT) {
- return;
- }
-
- enabledAxes[axis] = true;
-}
-
-extern "C" void GameActivityPointerAxes_disableAxis(int32_t axis) {
- if (axis < 0 || axis >= GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT) {
- return;
- }
-
- enabledAxes[axis] = false;
-}
-
extern "C" void GameActivity_setImeEditorInfo(GameActivity *activity,
int inputType, int actionId,
int imeOptions) {
- JNIEnv *env;
- if (activity->vm->AttachCurrentThread(&env, NULL) == JNI_OK) {
- env->CallVoidMethod(activity->javaGameActivity,
- gGameActivityClassInfo.setImeEditorInfoFields,
- inputType, actionId, imeOptions);
- }
+ NativeCode *code = static_cast<NativeCode *>(activity);
+ write_work(code->mainWorkWrite, CMD_SET_IME_EDITOR_INFO, inputType,
+ actionId, imeOptions);
}
-static struct {
- jmethodID getDeviceId;
- jmethodID getSource;
- jmethodID getAction;
-
- jmethodID getEventTime;
- jmethodID getDownTime;
-
- jmethodID getFlags;
- jmethodID getMetaState;
-
- jmethodID getActionButton;
- jmethodID getButtonState;
- jmethodID getClassification;
- jmethodID getEdgeFlags;
-
- jmethodID getPointerCount;
- jmethodID getPointerId;
- jmethodID getRawX;
- jmethodID getRawY;
- jmethodID getXPrecision;
- jmethodID getYPrecision;
- jmethodID getAxisValue;
-} gMotionEventClassInfo;
-
-extern "C" void GameActivityMotionEvent_fromJava(
- JNIEnv *env, jobject motionEvent, GameActivityMotionEvent *out_event) {
- static bool gMotionEventClassInfoInitialized = false;
- if (!gMotionEventClassInfoInitialized) {
- int sdkVersion = GetSystemPropAsInt("ro.build.version.sdk");
- gMotionEventClassInfo = {0};
- jclass motionEventClass = env->FindClass("android/view/MotionEvent");
- gMotionEventClassInfo.getDeviceId =
- env->GetMethodID(motionEventClass, "getDeviceId", "()I");
- gMotionEventClassInfo.getSource =
- env->GetMethodID(motionEventClass, "getSource", "()I");
- gMotionEventClassInfo.getAction =
- env->GetMethodID(motionEventClass, "getAction", "()I");
- gMotionEventClassInfo.getEventTime =
- env->GetMethodID(motionEventClass, "getEventTime", "()J");
- gMotionEventClassInfo.getDownTime =
- env->GetMethodID(motionEventClass, "getDownTime", "()J");
- gMotionEventClassInfo.getFlags =
- env->GetMethodID(motionEventClass, "getFlags", "()I");
- gMotionEventClassInfo.getMetaState =
- env->GetMethodID(motionEventClass, "getMetaState", "()I");
- if (sdkVersion >= 23) {
- gMotionEventClassInfo.getActionButton =
- env->GetMethodID(motionEventClass, "getActionButton", "()I");
- }
- if (sdkVersion >= 14) {
- gMotionEventClassInfo.getButtonState =
- env->GetMethodID(motionEventClass, "getButtonState", "()I");
- }
- if (sdkVersion >= 29) {
- gMotionEventClassInfo.getClassification =
- env->GetMethodID(motionEventClass, "getClassification", "()I");
- }
- gMotionEventClassInfo.getEdgeFlags =
- env->GetMethodID(motionEventClass, "getEdgeFlags", "()I");
- gMotionEventClassInfo.getPointerCount =
- env->GetMethodID(motionEventClass, "getPointerCount", "()I");
- gMotionEventClassInfo.getPointerId =
- env->GetMethodID(motionEventClass, "getPointerId", "(I)I");
- if (sdkVersion >= 29) {
- gMotionEventClassInfo.getRawX =
- env->GetMethodID(motionEventClass, "getRawX", "(I)F");
- gMotionEventClassInfo.getRawY =
- env->GetMethodID(motionEventClass, "getRawY", "(I)F");
- }
- gMotionEventClassInfo.getXPrecision =
- env->GetMethodID(motionEventClass, "getXPrecision", "()F");
- gMotionEventClassInfo.getYPrecision =
- env->GetMethodID(motionEventClass, "getYPrecision", "()F");
- gMotionEventClassInfo.getAxisValue =
- env->GetMethodID(motionEventClass, "getAxisValue", "(II)F");
-
- gMotionEventClassInfoInitialized = true;
- }
-
- int pointerCount =
- env->CallIntMethod(motionEvent, gMotionEventClassInfo.getPointerCount);
- pointerCount =
- std::min(pointerCount, GAMEACTIVITY_MAX_NUM_POINTERS_IN_MOTION_EVENT);
- out_event->pointerCount = pointerCount;
- for (int i = 0; i < pointerCount; ++i) {
- out_event->pointers[i] = {
- /*id=*/env->CallIntMethod(motionEvent,
- gMotionEventClassInfo.getPointerId, i),
- /*axisValues=*/{0},
- /*rawX=*/gMotionEventClassInfo.getRawX
- ? env->CallFloatMethod(motionEvent,
- gMotionEventClassInfo.getRawX, i)
- : 0,
- /*rawY=*/gMotionEventClassInfo.getRawY
- ? env->CallFloatMethod(motionEvent,
- gMotionEventClassInfo.getRawY, i)
- : 0,
- };
-
- for (int axisIndex = 0;
- axisIndex < GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT; ++axisIndex) {
- if (enabledAxes[axisIndex]) {
- out_event->pointers[i].axisValues[axisIndex] =
- env->CallFloatMethod(motionEvent,
- gMotionEventClassInfo.getAxisValue,
- axisIndex, i);
- }
- }
- }
-
- out_event->deviceId =
- env->CallIntMethod(motionEvent, gMotionEventClassInfo.getDeviceId);
- out_event->source =
- env->CallIntMethod(motionEvent, gMotionEventClassInfo.getSource);
- out_event->action =
- env->CallIntMethod(motionEvent, gMotionEventClassInfo.getAction);
- out_event->eventTime =
- env->CallLongMethod(motionEvent, gMotionEventClassInfo.getEventTime) *
- 1000000;
- out_event->downTime =
- env->CallLongMethod(motionEvent, gMotionEventClassInfo.getDownTime) *
- 1000000;
- out_event->flags =
- env->CallIntMethod(motionEvent, gMotionEventClassInfo.getFlags);
- out_event->metaState =
- env->CallIntMethod(motionEvent, gMotionEventClassInfo.getMetaState);
- out_event->actionButton =
- gMotionEventClassInfo.getActionButton
- ? env->CallIntMethod(motionEvent,
- gMotionEventClassInfo.getActionButton)
- : 0;
- out_event->buttonState =
- gMotionEventClassInfo.getButtonState
- ? env->CallIntMethod(motionEvent,
- gMotionEventClassInfo.getButtonState)
- : 0;
- out_event->classification =
- gMotionEventClassInfo.getClassification
- ? env->CallIntMethod(motionEvent,
- gMotionEventClassInfo.getClassification)
- : 0;
- out_event->edgeFlags =
- env->CallIntMethod(motionEvent, gMotionEventClassInfo.getEdgeFlags);
- out_event->precisionX =
- env->CallFloatMethod(motionEvent, gMotionEventClassInfo.getXPrecision);
- out_event->precisionY =
- env->CallFloatMethod(motionEvent, gMotionEventClassInfo.getYPrecision);
+extern "C" int GameActivity_getColorMode(GameActivity *) {
+ return gConfiguration.colorMode;
}
-static struct {
- jmethodID getDeviceId;
- jmethodID getSource;
- jmethodID getAction;
+extern "C" int GameActivity_getDensityDpi(GameActivity *) {
+ return gConfiguration.densityDpi;
+}
- jmethodID getEventTime;
- jmethodID getDownTime;
+extern "C" float GameActivity_getFontScale(GameActivity *) {
+ return gConfiguration.fontScale;
+}
- jmethodID getFlags;
- jmethodID getMetaState;
+extern "C" int GameActivity_getFontWeightAdjustment(GameActivity *) {
+ return gConfiguration.fontWeightAdjustment;
+}
- jmethodID getModifiers;
- jmethodID getRepeatCount;
- jmethodID getKeyCode;
-} gKeyEventClassInfo;
+extern "C" int GameActivity_getHardKeyboardHidden(GameActivity *) {
+ return gConfiguration.hardKeyboardHidden;
+}
-extern "C" void GameActivityKeyEvent_fromJava(JNIEnv *env, jobject keyEvent,
- GameActivityKeyEvent *out_event) {
- static bool gKeyEventClassInfoInitialized = false;
- if (!gKeyEventClassInfoInitialized) {
- int sdkVersion = GetSystemPropAsInt("ro.build.version.sdk");
- gKeyEventClassInfo = {0};
- jclass keyEventClass = env->FindClass("android/view/KeyEvent");
- gKeyEventClassInfo.getDeviceId =
- env->GetMethodID(keyEventClass, "getDeviceId", "()I");
- gKeyEventClassInfo.getSource =
- env->GetMethodID(keyEventClass, "getSource", "()I");
- gKeyEventClassInfo.getAction =
- env->GetMethodID(keyEventClass, "getAction", "()I");
- gKeyEventClassInfo.getEventTime =
- env->GetMethodID(keyEventClass, "getEventTime", "()J");
- gKeyEventClassInfo.getDownTime =
- env->GetMethodID(keyEventClass, "getDownTime", "()J");
- gKeyEventClassInfo.getFlags =
- env->GetMethodID(keyEventClass, "getFlags", "()I");
- gKeyEventClassInfo.getMetaState =
- env->GetMethodID(keyEventClass, "getMetaState", "()I");
- if (sdkVersion >= 13) {
- gKeyEventClassInfo.getModifiers =
- env->GetMethodID(keyEventClass, "getModifiers", "()I");
- }
- gKeyEventClassInfo.getRepeatCount =
- env->GetMethodID(keyEventClass, "getRepeatCount", "()I");
- gKeyEventClassInfo.getKeyCode =
- env->GetMethodID(keyEventClass, "getKeyCode", "()I");
+extern "C" int GameActivity_getKeyboard(GameActivity *) {
+ return gConfiguration.keyboard;
+}
- gKeyEventClassInfoInitialized = true;
- }
+extern "C" int GameActivity_getKeyboardHidden(GameActivity *) {
+ return gConfiguration.keyboardHidden;
+}
- *out_event = {
- /*deviceId=*/env->CallIntMethod(keyEvent,
- gKeyEventClassInfo.getDeviceId),
- /*source=*/env->CallIntMethod(keyEvent, gKeyEventClassInfo.getSource),
- /*action=*/env->CallIntMethod(keyEvent, gKeyEventClassInfo.getAction),
- // TODO: introduce a millisecondsToNanoseconds helper:
- /*eventTime=*/
- env->CallLongMethod(keyEvent, gKeyEventClassInfo.getEventTime) *
- 1000000,
- /*downTime=*/
- env->CallLongMethod(keyEvent, gKeyEventClassInfo.getDownTime) * 1000000,
- /*flags=*/env->CallIntMethod(keyEvent, gKeyEventClassInfo.getFlags),
- /*metaState=*/
- env->CallIntMethod(keyEvent, gKeyEventClassInfo.getMetaState),
- /*modifiers=*/gKeyEventClassInfo.getModifiers
- ? env->CallIntMethod(keyEvent, gKeyEventClassInfo.getModifiers)
- : 0,
- /*repeatCount=*/
- env->CallIntMethod(keyEvent, gKeyEventClassInfo.getRepeatCount),
- /*keyCode=*/
- env->CallIntMethod(keyEvent, gKeyEventClassInfo.getKeyCode)};
+extern "C" int GameActivity_getMcc(GameActivity *) {
+ return gConfiguration.mcc;
+}
+
+extern "C" int GameActivity_getMnc(GameActivity *) {
+ return gConfiguration.mnc;
+}
+
+extern "C" int GameActivity_getNavigation(GameActivity *) {
+ return gConfiguration.navigation;
+}
+
+extern "C" int GameActivity_getNavigationHidden(GameActivity *) {
+ return gConfiguration.navigationHidden;
+}
+
+extern "C" int GameActivity_getOrientation(GameActivity *) {
+ return gConfiguration.orientation;
+}
+
+extern "C" int GameActivity_getScreenHeightDp(GameActivity *) {
+ return gConfiguration.screenHeightDp;
+}
+
+extern "C" int GameActivity_getScreenLayout(GameActivity *) {
+ return gConfiguration.screenLayout;
+}
+
+extern "C" int GameActivity_getScreenWidthDp(GameActivity *) {
+ return gConfiguration.screenWidthDp;
+}
+
+extern "C" int GameActivity_getSmallestScreenWidthDp(GameActivity *) {
+ return gConfiguration.smallestScreenWidthDp;
+}
+
+extern "C" int GameActivity_getTouchscreen(GameActivity *) {
+ return gConfiguration.touchscreen;
+}
+
+extern "C" int GameActivity_getUIMode(GameActivity *) {
+ return gConfiguration.uiMode;
}
static bool onTouchEvent_native(JNIEnv *env, jobject javaGameActivity,
jlong handle, jobject motionEvent) {
- if (handle == 0) return;
+ if (handle == 0) return false;
NativeCode *code = (NativeCode *)handle;
if (code->callbacks.onTouchEvent == nullptr) return false;
static GameActivityMotionEvent c_event;
GameActivityMotionEvent_fromJava(env, motionEvent, &c_event);
- return code->callbacks.onTouchEvent(code, &c_event);
+ auto result = code->callbacks.onTouchEvent(code, &c_event);
+ GameActivityMotionEvent_destroy(&c_event);
+ return result;
}
static bool onKeyUp_native(JNIEnv *env, jobject javaGameActivity, jlong handle,
jobject keyEvent) {
- if (handle == 0) return;
+ if (handle == 0) return false;
NativeCode *code = (NativeCode *)handle;
if (code->callbacks.onKeyUp == nullptr) return false;
@@ -1088,9 +863,9 @@
static bool onKeyDown_native(JNIEnv *env, jobject javaGameActivity,
jlong handle, jobject keyEvent) {
- if (handle == 0) return;
+ if (handle == 0) return false;
NativeCode *code = (NativeCode *)handle;
- if (code->callbacks.onKeyDown == nullptr) return;
+ if (code->callbacks.onKeyDown == nullptr) return false;
static GameActivityKeyEvent c_event;
GameActivityKeyEvent_fromJava(env, keyEvent, &c_event);
@@ -1148,19 +923,37 @@
GameTextInput_setInputConnection(code->gameTextInput, inputConnection);
}
+static void onContentRectChangedNative_native(JNIEnv *env, jobject activity,
+ jlong handle, jint x, jint y,
+ jint w, jint h) {
+ if (handle != 0) {
+ NativeCode *code = (NativeCode *)handle;
+
+ if (code->callbacks.onContentRectChanged != nullptr) {
+ ARect rect;
+ rect.left = x;
+ rect.top = y;
+ rect.right = x+w;
+ rect.bottom = y+h;
+ code->callbacks.onContentRectChanged(code, &rect);
+ }
+ }
+}
+
static const JNINativeMethod g_methods[] = {
- {"loadNativeCode",
- "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/"
- "String;Ljava/lang/String;Landroid/content/res/AssetManager;[B)J",
- (void *)loadNativeCode_native},
+ {"initializeNativeCode",
+ "(Ljava/lang/String;Ljava/lang/String;"
+ "Ljava/lang/String;Landroid/content/res/AssetManager;"
+ "[BLandroid/content/res/Configuration;)J",
+ (void *)initializeNativeCode_native},
{"getDlError", "()Ljava/lang/String;", (void *)getDlError_native},
- {"unloadNativeCode", "(J)V", (void *)unloadNativeCode_native},
+ {"terminateNativeCode", "(J)V", (void *)terminateNativeCode_native},
{"onStartNative", "(J)V", (void *)onStart_native},
{"onResumeNative", "(J)V", (void *)onResume_native},
{"onSaveInstanceStateNative", "(J)[B", (void *)onSaveInstanceState_native},
{"onPauseNative", "(J)V", (void *)onPause_native},
{"onStopNative", "(J)V", (void *)onStop_native},
- {"onConfigurationChangedNative", "(J)V",
+ {"onConfigurationChangedNative", "(JLandroid/content/res/Configuration;)V",
(void *)onConfigurationChanged_native},
{"onTrimMemoryNative", "(JI)V", (void *)onTrimMemory_native},
{"onWindowFocusChangedNative", "(JZ)V",
@@ -1185,13 +978,16 @@
{"setInputConnectionNative",
"(JLcom/google/androidgamesdk/gametextinput/InputConnection;)V",
(void *)setInputConnection_native},
+ {"onContentRectChangedNative", "(JIIII)V",
+ (void *)onContentRectChangedNative_native},
};
static const char *const kGameActivityPathName =
"com/google/androidgamesdk/GameActivity";
static const char *const kInsetsPathName = "androidx/core/graphics/Insets";
-
+static const char *const kConfigurationPathName =
+ "android/content/res/Configuration";
static const char *const kWindowInsetsCompatTypePathName =
"androidx/core/view/WindowInsetsCompat$Type";
@@ -1209,7 +1005,7 @@
#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
- LOG_FATAL_IF(!var, "Unable to find field %s" fieldName);
+ LOG_FATAL_IF(!var, "Unable to find field %s", fieldName);
static int jniRegisterNativeMethods(JNIEnv *env, const char *className,
const JNINativeMethod *methods,
@@ -1249,12 +1045,51 @@
"getWaterfallInsets", "()Landroidx/core/graphics/Insets;");
GET_METHOD_ID(gGameActivityClassInfo.setImeEditorInfoFields, activity_class,
"setImeEditorInfoFields", "(III)V");
+
jclass insets_class;
FIND_CLASS(insets_class, kInsetsPathName);
GET_FIELD_ID(gInsetsClassInfo.left, insets_class, "left", "I");
GET_FIELD_ID(gInsetsClassInfo.right, insets_class, "right", "I");
GET_FIELD_ID(gInsetsClassInfo.top, insets_class, "top", "I");
GET_FIELD_ID(gInsetsClassInfo.bottom, insets_class, "bottom", "I");
+
+ jclass configuration_class;
+ FIND_CLASS(configuration_class, kConfigurationPathName);
+ GET_FIELD_ID(gConfigurationClassInfo.colorMode, configuration_class,
+ "colorMode", "I");
+ GET_FIELD_ID(gConfigurationClassInfo.densityDpi, configuration_class,
+ "densityDpi", "I");
+ GET_FIELD_ID(gConfigurationClassInfo.fontScale, configuration_class,
+ "fontScale", "F");
+ GET_FIELD_ID(gConfigurationClassInfo.fontWeightAdjustment,
+ configuration_class, "fontWeightAdjustment", "I");
+ GET_FIELD_ID(gConfigurationClassInfo.hardKeyboardHidden,
+ configuration_class, "hardKeyboardHidden", "I");
+ GET_FIELD_ID(gConfigurationClassInfo.keyboard, configuration_class,
+ "keyboard", "I");
+ GET_FIELD_ID(gConfigurationClassInfo.keyboardHidden, configuration_class,
+ "keyboardHidden", "I");
+ GET_FIELD_ID(gConfigurationClassInfo.mcc, configuration_class, "mcc", "I");
+ GET_FIELD_ID(gConfigurationClassInfo.mnc, configuration_class, "mnc", "I");
+ GET_FIELD_ID(gConfigurationClassInfo.navigation, configuration_class,
+ "navigation", "I");
+ GET_FIELD_ID(gConfigurationClassInfo.navigationHidden, configuration_class,
+ "navigationHidden", "I");
+ GET_FIELD_ID(gConfigurationClassInfo.orientation, configuration_class,
+ "orientation", "I");
+ GET_FIELD_ID(gConfigurationClassInfo.screenHeightDp, configuration_class,
+ "screenHeightDp", "I");
+ GET_FIELD_ID(gConfigurationClassInfo.screenLayout, configuration_class,
+ "screenLayout", "I");
+ GET_FIELD_ID(gConfigurationClassInfo.screenWidthDp, configuration_class,
+ "screenWidthDp", "I");
+ GET_FIELD_ID(gConfigurationClassInfo.smallestScreenWidthDp,
+ configuration_class, "smallestScreenWidthDp", "I");
+ GET_FIELD_ID(gConfigurationClassInfo.touchscreen, configuration_class,
+ "touchscreen", "I");
+ GET_FIELD_ID(gConfigurationClassInfo.uiMode, configuration_class, "uiMode",
+ "I");
+
jclass windowInsetsCompatType_class;
FIND_CLASS(windowInsetsCompatType_class, kWindowInsetsCompatTypePathName);
gWindowInsetsCompatTypeClassInfo.clazz =
@@ -1281,15 +1116,14 @@
NELEM(g_methods));
}
-// Register this method so that GameActiviy_register does not need to be called
-// manually.
-extern "C" jlong Java_com_google_androidgamesdk_GameActivity_loadNativeCode(
- JNIEnv *env, jobject javaGameActivity, jstring path, jstring funcName,
- jstring internalDataDir, jstring obbDir, jstring externalDataDir,
- jobject jAssetMgr, jbyteArray savedState) {
+extern "C" JNIEXPORT jlong JNICALL
+Java_com_google_androidgamesdk_GameActivity_initializeNativeCode(
+ JNIEnv *env, jobject javaGameActivity, jstring internalDataDir,
+ jstring obbDir, jstring externalDataDir, jobject jAssetMgr,
+ jbyteArray savedState, jobject javaConfig) {
GameActivity_register(env);
- jlong nativeCode = loadNativeCode_native(
- env, javaGameActivity, path, funcName, internalDataDir, obbDir,
- externalDataDir, jAssetMgr, savedState);
+ jlong nativeCode = initializeNativeCode_native(
+ env, javaGameActivity, internalDataDir, obbDir, externalDataDir,
+ jAssetMgr, savedState, javaConfig);
return nativeCode;
}
diff --git a/GameActivity/prefab-src/modules/game-activity/include/game-activity/GameActivity.h b/game-activity/prefab-src/modules/game-activity/include/game-activity/GameActivity.h
similarity index 80%
rename from GameActivity/prefab-src/modules/game-activity/include/game-activity/GameActivity.h
rename to game-activity/prefab-src/modules/game-activity/include/game-activity/GameActivity.h
index da102bc..d43ab25 100644
--- a/GameActivity/prefab-src/modules/game-activity/include/game-activity/GameActivity.h
+++ b/game-activity/prefab-src/modules/game-activity/include/game-activity/GameActivity.h
@@ -36,12 +36,22 @@
#include <stdint.h>
#include <sys/types.h>
+#include "common/gamesdk_common.h"
+#include "game-activity/GameActivityEvents.h"
#include "game-text-input/gametextinput.h"
#ifdef __cplusplus
extern "C" {
#endif
+#define GAMEACTIVITY_MAJOR_VERSION 2
+#define GAMEACTIVITY_MINOR_VERSION 0
+#define GAMEACTIVITY_BUGFIX_VERSION 0
+#define GAMEACTIVITY_PACKED_VERSION \
+ ANDROID_GAMESDK_PACKED_VERSION(GAMEACTIVITY_MAJOR_VERSION, \
+ GAMEACTIVITY_MINOR_VERSION, \
+ GAMEACTIVITY_BUGFIX_VERSION)
+
/**
* {@link GameActivityCallbacks}
*/
@@ -116,155 +126,6 @@
} GameActivity;
/**
- * The maximum number of axes supported in an Android MotionEvent.
- * See https://developer.android.com/ndk/reference/group/input.
- */
-#define GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT 48
-
-/**
- * \brief Describe information about a pointer, found in a
- * GameActivityMotionEvent.
- *
- * You can read values directly from this structure, or use helper functions
- * (`GameActivityPointerAxes_getX`, `GameActivityPointerAxes_getY` and
- * `GameActivityPointerAxes_getAxisValue`).
- *
- * The X axis and Y axis are enabled by default but any other axis that you want
- * to read **must** be enabled first, using
- * `GameActivityPointerAxes_enableAxis`.
- *
- * \see GameActivityMotionEvent
- */
-typedef struct GameActivityPointerAxes {
- int32_t id;
- float axisValues[GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT];
- float rawX;
- float rawY;
-} GameActivityPointerAxes;
-
-/** \brief Get the current X coordinate of the pointer. */
-inline float GameActivityPointerAxes_getX(
- const GameActivityPointerAxes* pointerInfo) {
- return pointerInfo->axisValues[AMOTION_EVENT_AXIS_X];
-}
-
-/** \brief Get the current Y coordinate of the pointer. */
-inline float GameActivityPointerAxes_getY(
- const GameActivityPointerAxes* pointerInfo) {
- return pointerInfo->axisValues[AMOTION_EVENT_AXIS_Y];
-}
-
-/**
- * \brief Enable the specified axis, so that its value is reported in the
- * GameActivityPointerAxes structures stored in a motion event.
- *
- * You must enable any axis that you want to read, apart from
- * `AMOTION_EVENT_AXIS_X` and `AMOTION_EVENT_AXIS_Y` that are enabled by
- * default.
- *
- * If the axis index is out of range, nothing is done.
- */
-void GameActivityPointerAxes_enableAxis(int32_t axis);
-
-/**
- * \brief Disable the specified axis. Its value won't be reported in the
- * GameActivityPointerAxes structures stored in a motion event anymore.
- *
- * Apart from X and Y, any axis that you want to read **must** be enabled first,
- * using `GameActivityPointerAxes_enableAxis`.
- *
- * If the axis index is out of range, nothing is done.
- */
-void GameActivityPointerAxes_disableAxis(int32_t axis);
-
-/**
- * \brief Get the value of the requested axis.
- *
- * Apart from X and Y, any axis that you want to read **must** be enabled first,
- * using `GameActivityPointerAxes_enableAxis`.
- *
- * Find the valid enums for the axis (`AMOTION_EVENT_AXIS_X`,
- * `AMOTION_EVENT_AXIS_Y`, `AMOTION_EVENT_AXIS_PRESSURE`...)
- * in https://developer.android.com/ndk/reference/group/input.
- *
- * @param pointerInfo The structure containing information about the pointer,
- * obtained from GameActivityMotionEvent.
- * @param axis The axis to get the value from
- * @return The value of the axis, or 0 if the axis is invalid or was not
- * enabled.
- */
-inline float GameActivityPointerAxes_getAxisValue(
- GameActivityPointerAxes* pointerInfo, int32_t axis) {
- if (axis < 0 || axis >= GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT) {
- return 0;
- }
-
- return pointerInfo->axisValues[axis];
-}
-
-/**
- * The maximum number of pointers returned inside a motion event.
- */
-#if (defined GAMEACTIVITY_MAX_NUM_POINTERS_IN_MOTION_EVENT_OVERRIDE)
-#define GAMEACTIVITY_MAX_NUM_POINTERS_IN_MOTION_EVENT \
- GAMEACTIVITY_MAX_NUM_POINTERS_IN_MOTION_EVENT_OVERRIDE
-#else
-#define GAMEACTIVITY_MAX_NUM_POINTERS_IN_MOTION_EVENT 8
-#endif
-
-/**
- * \brief Describe a motion event that happened on the GameActivity SurfaceView.
- *
- * This is 1:1 mapping to the information contained in a Java `MotionEvent`
- * (see https://developer.android.com/reference/android/view/MotionEvent).
- */
-typedef struct GameActivityMotionEvent {
- int32_t deviceId;
- int32_t source;
- int32_t action;
-
- int64_t eventTime;
- int64_t downTime;
-
- int32_t flags;
- int32_t metaState;
-
- int32_t actionButton;
- int32_t buttonState;
- int32_t classification;
- int32_t edgeFlags;
-
- uint32_t pointerCount;
- GameActivityPointerAxes
- pointers[GAMEACTIVITY_MAX_NUM_POINTERS_IN_MOTION_EVENT];
-
- float precisionX;
- float precisionY;
-} GameActivityMotionEvent;
-
-/**
- * \brief Describe a key event that happened on the GameActivity SurfaceView.
- *
- * This is 1:1 mapping to the information contained in a Java `KeyEvent`
- * (see https://developer.android.com/reference/android/view/KeyEvent).
- */
-typedef struct GameActivityKeyEvent {
- int32_t deviceId;
- int32_t source;
- int32_t action;
-
- int64_t eventTime;
- int64_t downTime;
-
- int32_t flags;
- int32_t metaState;
-
- int32_t modifiers;
- int32_t repeatCount;
- int32_t keyCode;
-} GameActivityKeyEvent;
-
-/**
* A function the user should call from their callback with the data, its length
* and the library- supplied context.
*/
@@ -410,33 +271,15 @@
* Call GameActivity_getWindowInsets to retrieve the insets themselves.
*/
void (*onWindowInsetsChanged)(GameActivity* activity);
+
+ /**
+ * Callback called when the rectangle in the window where the content
+ * should be placed has changed.
+ */
+ void (*onContentRectChanged)(GameActivity *activity, const ARect *rect);
} GameActivityCallbacks;
/**
- * \brief Convert a Java `MotionEvent` to a `GameActivityMotionEvent`.
- *
- * This is done automatically by the GameActivity: see `onTouchEvent` to set
- * a callback to consume the received events.
- * This function can be used if you re-implement events handling in your own
- * activity.
- * Ownership of out_event is maintained by the caller.
- */
-void GameActivityMotionEvent_fromJava(JNIEnv* env, jobject motionEvent,
- GameActivityMotionEvent* out_event);
-
-/**
- * \brief Convert a Java `KeyEvent` to a `GameActivityKeyEvent`.
- *
- * This is done automatically by the GameActivity: see `onKeyUp` and `onKeyDown`
- * to set a callback to consume the received events.
- * This function can be used if you re-implement events handling in your own
- * activity.
- * Ownership of out_event is maintained by the caller.
- */
-void GameActivityKeyEvent_fromJava(JNIEnv* env, jobject motionEvent,
- GameActivityKeyEvent* out_event);
-
-/**
* This is the function that must be in the native code to instantiate the
* application's native activity. It is called with the activity instance (see
* above); if the code is being instantiated from a previously saved instance,
@@ -760,6 +603,30 @@
void GameActivity_setImeEditorInfo(GameActivity* activity, int inputType,
int actionId, int imeOptions);
+/**
+ * These are getters for Configuration class members. They may be called from
+ * any thread.
+ */
+int GameActivity_getOrientation(GameActivity* activity);
+int GameActivity_getColorMode(GameActivity* activity);
+int GameActivity_getDensityDpi(GameActivity* activity);
+float GameActivity_getFontScale(GameActivity* activity);
+int GameActivity_getFontWeightAdjustment(GameActivity* activity);
+int GameActivity_getHardKeyboardHidden(GameActivity* activity);
+int GameActivity_getKeyboard(GameActivity* activity);
+int GameActivity_getKeyboardHidden(GameActivity* activity);
+int GameActivity_getMcc(GameActivity* activity);
+int GameActivity_getMnc(GameActivity* activity);
+int GameActivity_getNavigation(GameActivity* activity);
+int GameActivity_getNavigationHidden(GameActivity* activity);
+int GameActivity_getOrientation(GameActivity* activity);
+int GameActivity_getScreenHeightDp(GameActivity* activity);
+int GameActivity_getScreenLayout(GameActivity* activity);
+int GameActivity_getScreenWidthDp(GameActivity* activity);
+int GameActivity_getSmallestScreenWidthDp(GameActivity* activity);
+int GameActivity_getTouchscreen(GameActivity* activity);
+int GameActivity_getUIMode(GameActivity* activity);
+
#ifdef __cplusplus
}
#endif
diff --git a/game-activity/prefab-src/modules/game-activity/include/game-activity/GameActivityEvents.cpp b/game-activity/prefab-src/modules/game-activity/include/game-activity/GameActivityEvents.cpp
new file mode 100644
index 0000000..2cf2356
--- /dev/null
+++ b/game-activity/prefab-src/modules/game-activity/include/game-activity/GameActivityEvents.cpp
@@ -0,0 +1,401 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "GameActivityEvents.h"
+
+#include <sys/system_properties.h>
+
+#include <string>
+
+#include "GameActivityLog.h"
+
+// TODO(b/187147166): these functions were extracted from the Game SDK
+// (gamesdk/src/common/system_utils.h). system_utils.h/cpp should be used
+// instead.
+namespace {
+
+std::string getSystemPropViaGet(const char *key,
+ const char *default_value = "") {
+ char buffer[PROP_VALUE_MAX + 1] = ""; // +1 for terminator
+ int bufferLen = __system_property_get(key, buffer);
+ if (bufferLen > 0)
+ return buffer;
+ else
+ return "";
+}
+
+std::string GetSystemProp(const char *key, const char *default_value = "") {
+ return getSystemPropViaGet(key, default_value);
+}
+
+int GetSystemPropAsInt(const char *key, int default_value = 0) {
+ std::string prop = GetSystemProp(key);
+ return prop == "" ? default_value : strtoll(prop.c_str(), nullptr, 10);
+}
+
+} // anonymous namespace
+
+#ifndef NELEM
+#define NELEM(x) ((int)(sizeof(x) / sizeof((x)[0])))
+#endif
+
+static bool enabledAxes[GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT] = {
+ /* AMOTION_EVENT_AXIS_X */ true,
+ /* AMOTION_EVENT_AXIS_Y */ true,
+ // Disable all other axes by default (they can be enabled using
+ // `GameActivityPointerAxes_enableAxis`).
+ false};
+
+extern "C" void GameActivityPointerAxes_enableAxis(int32_t axis) {
+ if (axis < 0 || axis >= GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT) {
+ return;
+ }
+
+ enabledAxes[axis] = true;
+}
+
+float GameActivityPointerAxes_getAxisValue(
+ const GameActivityPointerAxes *pointerInfo, int32_t axis) {
+ if (axis < 0 || axis >= GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT) {
+ return 0;
+ }
+
+ if (!enabledAxes[axis]) {
+ ALOGW("Axis %d must be enabled before it can be accessed.", axis);
+ return 0;
+ }
+
+ return pointerInfo->axisValues[axis];
+}
+
+extern "C" void GameActivityPointerAxes_disableAxis(int32_t axis) {
+ if (axis < 0 || axis >= GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT) {
+ return;
+ }
+
+ enabledAxes[axis] = false;
+}
+
+float GameActivityMotionEvent_getHistoricalAxisValue(
+ const GameActivityMotionEvent *event, int axis, int pointerIndex,
+ int historyPos) {
+ if (axis < 0 || axis >= GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT) {
+ return 0;
+ }
+
+ if (!enabledAxes[axis]) {
+ ALOGW("Axis %d must be enabled before it can be accessed.", axis);
+ return 0;
+ }
+
+ return event->historicalAxisValues[event->pointerCount * historyPos + axis];
+}
+
+static struct {
+ jmethodID getDeviceId;
+ jmethodID getSource;
+ jmethodID getAction;
+
+ jmethodID getEventTime;
+ jmethodID getDownTime;
+
+ jmethodID getFlags;
+ jmethodID getMetaState;
+
+ jmethodID getActionButton;
+ jmethodID getButtonState;
+ jmethodID getClassification;
+ jmethodID getEdgeFlags;
+
+ jmethodID getHistorySize;
+ jmethodID getHistoricalEventTime;
+
+ jmethodID getPointerCount;
+ jmethodID getPointerId;
+
+ jmethodID getToolType;
+
+ jmethodID getRawX;
+ jmethodID getRawY;
+ jmethodID getXPrecision;
+ jmethodID getYPrecision;
+ jmethodID getAxisValue;
+
+ jmethodID getHistoricalAxisValue;
+} gMotionEventClassInfo;
+
+extern "C" void GameActivityMotionEvent_destroy(
+ GameActivityMotionEvent *c_event) {
+ delete c_event->historicalAxisValues;
+ delete c_event->historicalEventTimesMillis;
+ delete c_event->historicalEventTimesNanos;
+}
+
+extern "C" void GameActivityMotionEvent_fromJava(
+ JNIEnv *env, jobject motionEvent, GameActivityMotionEvent *out_event) {
+ static bool gMotionEventClassInfoInitialized = false;
+ if (!gMotionEventClassInfoInitialized) {
+ int sdkVersion = GetSystemPropAsInt("ro.build.version.sdk");
+ gMotionEventClassInfo = {0};
+ jclass motionEventClass = env->FindClass("android/view/MotionEvent");
+ gMotionEventClassInfo.getDeviceId =
+ env->GetMethodID(motionEventClass, "getDeviceId", "()I");
+ gMotionEventClassInfo.getSource =
+ env->GetMethodID(motionEventClass, "getSource", "()I");
+ gMotionEventClassInfo.getAction =
+ env->GetMethodID(motionEventClass, "getAction", "()I");
+ gMotionEventClassInfo.getEventTime =
+ env->GetMethodID(motionEventClass, "getEventTime", "()J");
+ gMotionEventClassInfo.getDownTime =
+ env->GetMethodID(motionEventClass, "getDownTime", "()J");
+ gMotionEventClassInfo.getFlags =
+ env->GetMethodID(motionEventClass, "getFlags", "()I");
+ gMotionEventClassInfo.getMetaState =
+ env->GetMethodID(motionEventClass, "getMetaState", "()I");
+ if (sdkVersion >= 23) {
+ gMotionEventClassInfo.getActionButton =
+ env->GetMethodID(motionEventClass, "getActionButton", "()I");
+ }
+ if (sdkVersion >= 14) {
+ gMotionEventClassInfo.getButtonState =
+ env->GetMethodID(motionEventClass, "getButtonState", "()I");
+ }
+ if (sdkVersion >= 29) {
+ gMotionEventClassInfo.getClassification =
+ env->GetMethodID(motionEventClass, "getClassification", "()I");
+ }
+ gMotionEventClassInfo.getEdgeFlags =
+ env->GetMethodID(motionEventClass, "getEdgeFlags", "()I");
+
+ gMotionEventClassInfo.getHistorySize =
+ env->GetMethodID(motionEventClass, "getHistorySize", "()I");
+ gMotionEventClassInfo.getHistoricalEventTime = env->GetMethodID(
+ motionEventClass, "getHistoricalEventTime", "(I)J");
+
+ gMotionEventClassInfo.getPointerCount =
+ env->GetMethodID(motionEventClass, "getPointerCount", "()I");
+ gMotionEventClassInfo.getPointerId =
+ env->GetMethodID(motionEventClass, "getPointerId", "(I)I");
+ gMotionEventClassInfo.getToolType =
+ env->GetMethodID(motionEventClass, "getToolType", "(I)I");
+ if (sdkVersion >= 29) {
+ gMotionEventClassInfo.getRawX =
+ env->GetMethodID(motionEventClass, "getRawX", "(I)F");
+ gMotionEventClassInfo.getRawY =
+ env->GetMethodID(motionEventClass, "getRawY", "(I)F");
+ }
+ gMotionEventClassInfo.getXPrecision =
+ env->GetMethodID(motionEventClass, "getXPrecision", "()F");
+ gMotionEventClassInfo.getYPrecision =
+ env->GetMethodID(motionEventClass, "getYPrecision", "()F");
+ gMotionEventClassInfo.getAxisValue =
+ env->GetMethodID(motionEventClass, "getAxisValue", "(II)F");
+
+ gMotionEventClassInfo.getHistoricalAxisValue = env->GetMethodID(
+ motionEventClass, "getHistoricalAxisValue", "(III)F");
+ gMotionEventClassInfoInitialized = true;
+ }
+
+ int pointerCount =
+ env->CallIntMethod(motionEvent, gMotionEventClassInfo.getPointerCount);
+ pointerCount =
+ std::min(pointerCount, GAMEACTIVITY_MAX_NUM_POINTERS_IN_MOTION_EVENT);
+ out_event->pointerCount = pointerCount;
+ for (int i = 0; i < pointerCount; ++i) {
+ out_event->pointers[i] = {
+ /*id=*/env->CallIntMethod(motionEvent,
+ gMotionEventClassInfo.getPointerId, i),
+ /*toolType=*/
+ env->CallIntMethod(motionEvent, gMotionEventClassInfo.getToolType,
+ i),
+ /*axisValues=*/{0},
+ /*rawX=*/gMotionEventClassInfo.getRawX
+ ? env->CallFloatMethod(motionEvent,
+ gMotionEventClassInfo.getRawX, i)
+ : 0,
+ /*rawY=*/gMotionEventClassInfo.getRawY
+ ? env->CallFloatMethod(motionEvent,
+ gMotionEventClassInfo.getRawY, i)
+ : 0,
+ };
+
+ for (int axisIndex = 0;
+ axisIndex < GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT; ++axisIndex) {
+ if (enabledAxes[axisIndex]) {
+ out_event->pointers[i].axisValues[axisIndex] =
+ env->CallFloatMethod(motionEvent,
+ gMotionEventClassInfo.getAxisValue,
+ axisIndex, i);
+ }
+ }
+ }
+
+ int historySize =
+ env->CallIntMethod(motionEvent, gMotionEventClassInfo.getHistorySize);
+ out_event->historySize = historySize;
+ out_event->historicalAxisValues =
+ new float[historySize * pointerCount *
+ GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT];
+ out_event->historicalEventTimesMillis = new long[historySize];
+ out_event->historicalEventTimesNanos = new long[historySize];
+
+ for (int historyIndex = 0; historyIndex < historySize; historyIndex++) {
+ out_event->historicalEventTimesMillis[historyIndex] =
+ env->CallLongMethod(motionEvent,
+ gMotionEventClassInfo.getHistoricalEventTime,
+ historyIndex);
+ out_event->historicalEventTimesNanos[historyIndex] =
+ out_event->historicalEventTimesMillis[historyIndex] * 1000000;
+ for (int i = 0; i < pointerCount; ++i) {
+ int pointerOffset = i * GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT;
+ int historyAxisOffset = historyIndex * pointerCount *
+ GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT;
+ float *axisValues =
+ &out_event
+ ->historicalAxisValues[historyAxisOffset + pointerOffset];
+ for (int axisIndex = 0;
+ axisIndex < GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT;
+ ++axisIndex) {
+ if (enabledAxes[axisIndex]) {
+ axisValues[axisIndex] = env->CallFloatMethod(
+ motionEvent,
+ gMotionEventClassInfo.getHistoricalAxisValue, axisIndex,
+ i, historyIndex);
+ }
+ }
+ }
+ }
+
+ out_event->deviceId =
+ env->CallIntMethod(motionEvent, gMotionEventClassInfo.getDeviceId);
+ out_event->source =
+ env->CallIntMethod(motionEvent, gMotionEventClassInfo.getSource);
+ out_event->action =
+ env->CallIntMethod(motionEvent, gMotionEventClassInfo.getAction);
+ out_event->eventTime =
+ env->CallLongMethod(motionEvent, gMotionEventClassInfo.getEventTime) *
+ 1000000;
+ out_event->downTime =
+ env->CallLongMethod(motionEvent, gMotionEventClassInfo.getDownTime) *
+ 1000000;
+ out_event->flags =
+ env->CallIntMethod(motionEvent, gMotionEventClassInfo.getFlags);
+ out_event->metaState =
+ env->CallIntMethod(motionEvent, gMotionEventClassInfo.getMetaState);
+ out_event->actionButton =
+ gMotionEventClassInfo.getActionButton
+ ? env->CallIntMethod(motionEvent,
+ gMotionEventClassInfo.getActionButton)
+ : 0;
+ out_event->buttonState =
+ gMotionEventClassInfo.getButtonState
+ ? env->CallIntMethod(motionEvent,
+ gMotionEventClassInfo.getButtonState)
+ : 0;
+ out_event->classification =
+ gMotionEventClassInfo.getClassification
+ ? env->CallIntMethod(motionEvent,
+ gMotionEventClassInfo.getClassification)
+ : 0;
+ out_event->edgeFlags =
+ env->CallIntMethod(motionEvent, gMotionEventClassInfo.getEdgeFlags);
+ out_event->precisionX =
+ env->CallFloatMethod(motionEvent, gMotionEventClassInfo.getXPrecision);
+ out_event->precisionY =
+ env->CallFloatMethod(motionEvent, gMotionEventClassInfo.getYPrecision);
+}
+
+static struct {
+ jmethodID getDeviceId;
+ jmethodID getSource;
+ jmethodID getAction;
+
+ jmethodID getEventTime;
+ jmethodID getDownTime;
+
+ jmethodID getFlags;
+ jmethodID getMetaState;
+
+ jmethodID getModifiers;
+ jmethodID getRepeatCount;
+ jmethodID getKeyCode;
+ jmethodID getScanCode;
+ jmethodID getUnicodeChar;
+} gKeyEventClassInfo;
+
+extern "C" void GameActivityKeyEvent_fromJava(JNIEnv *env, jobject keyEvent,
+ GameActivityKeyEvent *out_event) {
+ static bool gKeyEventClassInfoInitialized = false;
+ if (!gKeyEventClassInfoInitialized) {
+ int sdkVersion = GetSystemPropAsInt("ro.build.version.sdk");
+ gKeyEventClassInfo = {0};
+ jclass keyEventClass = env->FindClass("android/view/KeyEvent");
+ gKeyEventClassInfo.getDeviceId =
+ env->GetMethodID(keyEventClass, "getDeviceId", "()I");
+ gKeyEventClassInfo.getSource =
+ env->GetMethodID(keyEventClass, "getSource", "()I");
+ gKeyEventClassInfo.getAction =
+ env->GetMethodID(keyEventClass, "getAction", "()I");
+ gKeyEventClassInfo.getEventTime =
+ env->GetMethodID(keyEventClass, "getEventTime", "()J");
+ gKeyEventClassInfo.getDownTime =
+ env->GetMethodID(keyEventClass, "getDownTime", "()J");
+ gKeyEventClassInfo.getFlags =
+ env->GetMethodID(keyEventClass, "getFlags", "()I");
+ gKeyEventClassInfo.getMetaState =
+ env->GetMethodID(keyEventClass, "getMetaState", "()I");
+ if (sdkVersion >= 13) {
+ gKeyEventClassInfo.getModifiers =
+ env->GetMethodID(keyEventClass, "getModifiers", "()I");
+ }
+ gKeyEventClassInfo.getRepeatCount =
+ env->GetMethodID(keyEventClass, "getRepeatCount", "()I");
+ gKeyEventClassInfo.getKeyCode =
+ env->GetMethodID(keyEventClass, "getKeyCode", "()I");
+ gKeyEventClassInfo.getScanCode =
+ env->GetMethodID(keyEventClass, "getScanCode", "()I");
+ gKeyEventClassInfo.getUnicodeChar =
+ env->GetMethodID(keyEventClass, "getUnicodeChar", "()I");
+
+ gKeyEventClassInfoInitialized = true;
+ }
+
+ *out_event = {
+ /*deviceId=*/env->CallIntMethod(keyEvent,
+ gKeyEventClassInfo.getDeviceId),
+ /*source=*/env->CallIntMethod(keyEvent, gKeyEventClassInfo.getSource),
+ /*action=*/env->CallIntMethod(keyEvent, gKeyEventClassInfo.getAction),
+ // TODO: introduce a millisecondsToNanoseconds helper:
+ /*eventTime=*/
+ env->CallLongMethod(keyEvent, gKeyEventClassInfo.getEventTime) *
+ 1000000,
+ /*downTime=*/
+ env->CallLongMethod(keyEvent, gKeyEventClassInfo.getDownTime) * 1000000,
+ /*flags=*/env->CallIntMethod(keyEvent, gKeyEventClassInfo.getFlags),
+ /*metaState=*/
+ env->CallIntMethod(keyEvent, gKeyEventClassInfo.getMetaState),
+ /*modifiers=*/gKeyEventClassInfo.getModifiers
+ ? env->CallIntMethod(keyEvent, gKeyEventClassInfo.getModifiers)
+ : 0,
+ /*repeatCount=*/
+ env->CallIntMethod(keyEvent, gKeyEventClassInfo.getRepeatCount),
+ /*keyCode=*/
+ env->CallIntMethod(keyEvent, gKeyEventClassInfo.getKeyCode),
+ /*scanCode=*/
+ env->CallIntMethod(keyEvent, gKeyEventClassInfo.getScanCode),
+ /*unicodeChar=*/
+ env->CallIntMethod(keyEvent, gKeyEventClassInfo.getUnicodeChar)};
+}
diff --git a/game-activity/prefab-src/modules/game-activity/include/game-activity/GameActivityEvents.h b/game-activity/prefab-src/modules/game-activity/include/game-activity/GameActivityEvents.h
new file mode 100644
index 0000000..4149dda
--- /dev/null
+++ b/game-activity/prefab-src/modules/game-activity/include/game-activity/GameActivityEvents.h
@@ -0,0 +1,336 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @addtogroup GameActivity Game Activity Events
+ * The interface to use Game Activity Events.
+ * @{
+ */
+
+/**
+ * @file GameActivityEvents.h
+ */
+#ifndef ANDROID_GAME_SDK_GAME_ACTIVITY_EVENTS_H
+#define ANDROID_GAME_SDK_GAME_ACTIVITY_EVENTS_H
+
+#include <android/input.h>
+#include <jni.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * The maximum number of axes supported in an Android MotionEvent.
+ * See https://developer.android.com/ndk/reference/group/input.
+ */
+#define GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT 48
+
+/**
+ * \brief Describe information about a pointer, found in a
+ * GameActivityMotionEvent.
+ *
+ * You can read values directly from this structure, or use helper functions
+ * (`GameActivityPointerAxes_getX`, `GameActivityPointerAxes_getY` and
+ * `GameActivityPointerAxes_getAxisValue`).
+ *
+ * The X axis and Y axis are enabled by default but any other axis that you want
+ * to read **must** be enabled first, using
+ * `GameActivityPointerAxes_enableAxis`.
+ *
+ * \see GameActivityMotionEvent
+ */
+typedef struct GameActivityPointerAxes {
+ int32_t id;
+ int32_t toolType;
+ float axisValues[GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT];
+ float rawX;
+ float rawY;
+} GameActivityPointerAxes;
+
+/** \brief Get the toolType of the pointer. */
+inline int32_t GameActivityPointerAxes_getToolType(
+ const GameActivityPointerAxes* pointerInfo) {
+ return pointerInfo->toolType;
+}
+
+/** \brief Get the current X coordinate of the pointer. */
+inline float GameActivityPointerAxes_getX(
+ const GameActivityPointerAxes* pointerInfo) {
+ return pointerInfo->axisValues[AMOTION_EVENT_AXIS_X];
+}
+
+/** \brief Get the current Y coordinate of the pointer. */
+inline float GameActivityPointerAxes_getY(
+ const GameActivityPointerAxes* pointerInfo) {
+ return pointerInfo->axisValues[AMOTION_EVENT_AXIS_Y];
+}
+
+/**
+ * \brief Enable the specified axis, so that its value is reported in the
+ * GameActivityPointerAxes structures stored in a motion event.
+ *
+ * You must enable any axis that you want to read, apart from
+ * `AMOTION_EVENT_AXIS_X` and `AMOTION_EVENT_AXIS_Y` that are enabled by
+ * default.
+ *
+ * If the axis index is out of range, nothing is done.
+ */
+void GameActivityPointerAxes_enableAxis(int32_t axis);
+
+/**
+ * \brief Disable the specified axis. Its value won't be reported in the
+ * GameActivityPointerAxes structures stored in a motion event anymore.
+ *
+ * Apart from X and Y, any axis that you want to read **must** be enabled first,
+ * using `GameActivityPointerAxes_enableAxis`.
+ *
+ * If the axis index is out of range, nothing is done.
+ */
+void GameActivityPointerAxes_disableAxis(int32_t axis);
+
+/**
+ * \brief Get the value of the requested axis.
+ *
+ * Apart from X and Y, any axis that you want to read **must** be enabled first,
+ * using `GameActivityPointerAxes_enableAxis`.
+ *
+ * Find the valid enums for the axis (`AMOTION_EVENT_AXIS_X`,
+ * `AMOTION_EVENT_AXIS_Y`, `AMOTION_EVENT_AXIS_PRESSURE`...)
+ * in https://developer.android.com/ndk/reference/group/input.
+ *
+ * @param pointerInfo The structure containing information about the pointer,
+ * obtained from GameActivityMotionEvent.
+ * @param axis The axis to get the value from
+ * @return The value of the axis, or 0 if the axis is invalid or was not
+ * enabled.
+ */
+float GameActivityPointerAxes_getAxisValue(
+ const GameActivityPointerAxes* pointerInfo, int32_t axis);
+
+inline float GameActivityPointerAxes_getPressure(
+ const GameActivityPointerAxes* pointerInfo) {
+ return GameActivityPointerAxes_getAxisValue(pointerInfo,
+ AMOTION_EVENT_AXIS_PRESSURE);
+}
+
+inline float GameActivityPointerAxes_getSize(
+ const GameActivityPointerAxes* pointerInfo) {
+ return GameActivityPointerAxes_getAxisValue(pointerInfo,
+ AMOTION_EVENT_AXIS_SIZE);
+}
+
+inline float GameActivityPointerAxes_getTouchMajor(
+ const GameActivityPointerAxes* pointerInfo) {
+ return GameActivityPointerAxes_getAxisValue(pointerInfo,
+ AMOTION_EVENT_AXIS_TOUCH_MAJOR);
+}
+
+inline float GameActivityPointerAxes_getTouchMinor(
+ const GameActivityPointerAxes* pointerInfo) {
+ return GameActivityPointerAxes_getAxisValue(pointerInfo,
+ AMOTION_EVENT_AXIS_TOUCH_MINOR);
+}
+
+inline float GameActivityPointerAxes_getToolMajor(
+ const GameActivityPointerAxes* pointerInfo) {
+ return GameActivityPointerAxes_getAxisValue(pointerInfo,
+ AMOTION_EVENT_AXIS_TOOL_MAJOR);
+}
+
+inline float GameActivityPointerAxes_getToolMinor(
+ const GameActivityPointerAxes* pointerInfo) {
+ return GameActivityPointerAxes_getAxisValue(pointerInfo,
+ AMOTION_EVENT_AXIS_TOOL_MINOR);
+}
+
+inline float GameActivityPointerAxes_getOrientation(
+ const GameActivityPointerAxes* pointerInfo) {
+ return GameActivityPointerAxes_getAxisValue(pointerInfo,
+ AMOTION_EVENT_AXIS_ORIENTATION);
+}
+
+/**
+ * The maximum number of pointers returned inside a motion event.
+ */
+#if (defined GAMEACTIVITY_MAX_NUM_POINTERS_IN_MOTION_EVENT_OVERRIDE)
+#define GAMEACTIVITY_MAX_NUM_POINTERS_IN_MOTION_EVENT \
+ GAMEACTIVITY_MAX_NUM_POINTERS_IN_MOTION_EVENT_OVERRIDE
+#else
+#define GAMEACTIVITY_MAX_NUM_POINTERS_IN_MOTION_EVENT 8
+#endif
+
+/**
+ * \brief Describe a motion event that happened on the GameActivity SurfaceView.
+ *
+ * This is 1:1 mapping to the information contained in a Java `MotionEvent`
+ * (see https://developer.android.com/reference/android/view/MotionEvent).
+ */
+typedef struct GameActivityMotionEvent {
+ int32_t deviceId;
+ int32_t source;
+ int32_t action;
+
+ int64_t eventTime;
+ int64_t downTime;
+
+ int32_t flags;
+ int32_t metaState;
+
+ int32_t actionButton;
+ int32_t buttonState;
+ int32_t classification;
+ int32_t edgeFlags;
+
+ uint32_t pointerCount;
+ GameActivityPointerAxes
+ pointers[GAMEACTIVITY_MAX_NUM_POINTERS_IN_MOTION_EVENT];
+
+ int historySize;
+ long* historicalEventTimesMillis;
+ long* historicalEventTimesNanos;
+ float* historicalAxisValues;
+
+ float precisionX;
+ float precisionY;
+} GameActivityMotionEvent;
+
+float GameActivityMotionEvent_getHistoricalAxisValue(
+ const GameActivityMotionEvent* event, int axis, int pointerIndex,
+ int historyPos);
+
+inline int GameActivityMotionEvent_getHistorySize(
+ const GameActivityMotionEvent* event) {
+ return event->historySize;
+}
+
+inline float GameActivityMotionEvent_getHistoricalX(
+ const GameActivityMotionEvent* event, int pointerIndex, int historyPos) {
+ return GameActivityMotionEvent_getHistoricalAxisValue(
+ event, AMOTION_EVENT_AXIS_X, pointerIndex, historyPos);
+}
+
+inline float GameActivityMotionEvent_getHistoricalY(
+ const GameActivityMotionEvent* event, int pointerIndex, int historyPos) {
+ return GameActivityMotionEvent_getHistoricalAxisValue(
+ event, AMOTION_EVENT_AXIS_Y, pointerIndex, historyPos);
+}
+
+inline float GameActivityMotionEvent_getHistoricalPressure(
+ const GameActivityMotionEvent* event, int pointerIndex, int historyPos) {
+ return GameActivityMotionEvent_getHistoricalAxisValue(
+ event, AMOTION_EVENT_AXIS_PRESSURE, pointerIndex, historyPos);
+}
+
+inline float GameActivityMotionEvent_getHistoricalSize(
+ const GameActivityMotionEvent* event, int pointerIndex, int historyPos) {
+ return GameActivityMotionEvent_getHistoricalAxisValue(
+ event, AMOTION_EVENT_AXIS_SIZE, pointerIndex, historyPos);
+}
+
+inline float GameActivityMotionEvent_getHistoricalTouchMajor(
+ const GameActivityMotionEvent* event, int pointerIndex, int historyPos) {
+ return GameActivityMotionEvent_getHistoricalAxisValue(
+ event, AMOTION_EVENT_AXIS_TOUCH_MAJOR, pointerIndex, historyPos);
+}
+
+inline float GameActivityMotionEvent_getHistoricalTouchMinor(
+ const GameActivityMotionEvent* event, int pointerIndex, int historyPos) {
+ return GameActivityMotionEvent_getHistoricalAxisValue(
+ event, AMOTION_EVENT_AXIS_TOUCH_MINOR, pointerIndex, historyPos);
+}
+
+inline float GameActivityMotionEvent_getHistoricalToolMajor(
+ const GameActivityMotionEvent* event, int pointerIndex, int historyPos) {
+ return GameActivityMotionEvent_getHistoricalAxisValue(
+ event, AMOTION_EVENT_AXIS_TOOL_MAJOR, pointerIndex, historyPos);
+}
+
+inline float GameActivityMotionEvent_getHistoricalToolMinor(
+ const GameActivityMotionEvent* event, int pointerIndex, int historyPos) {
+ return GameActivityMotionEvent_getHistoricalAxisValue(
+ event, AMOTION_EVENT_AXIS_TOOL_MINOR, pointerIndex, historyPos);
+}
+
+inline float GameActivityMotionEvent_getHistoricalOrientation(
+ const GameActivityMotionEvent* event, int pointerIndex, int historyPos) {
+ return GameActivityMotionEvent_getHistoricalAxisValue(
+ event, AMOTION_EVENT_AXIS_ORIENTATION, pointerIndex, historyPos);
+}
+
+/** \brief Handle the freeing of the GameActivityMotionEvent struct. */
+void GameActivityMotionEvent_destroy(GameActivityMotionEvent* c_event);
+
+/**
+ * \brief Convert a Java `MotionEvent` to a `GameActivityMotionEvent`.
+ *
+ * This is done automatically by the GameActivity: see `onTouchEvent` to set
+ * a callback to consume the received events.
+ * This function can be used if you re-implement events handling in your own
+ * activity.
+ * Ownership of out_event is maintained by the caller.
+ */
+void GameActivityMotionEvent_fromJava(JNIEnv* env, jobject motionEvent,
+ GameActivityMotionEvent* out_event);
+
+/**
+ * \brief Describe a key event that happened on the GameActivity SurfaceView.
+ *
+ * This is 1:1 mapping to the information contained in a Java `KeyEvent`
+ * (see https://developer.android.com/reference/android/view/KeyEvent).
+ * The only exception is the event times, which are reported as
+ * nanoseconds in this struct.
+ */
+typedef struct GameActivityKeyEvent {
+ int32_t deviceId;
+ int32_t source;
+ int32_t action;
+
+ int64_t eventTime;
+ int64_t downTime;
+
+ int32_t flags;
+ int32_t metaState;
+
+ int32_t modifiers;
+ int32_t repeatCount;
+ int32_t keyCode;
+ int32_t scanCode;
+ int32_t unicodeChar;
+} GameActivityKeyEvent;
+
+/**
+ * \brief Convert a Java `KeyEvent` to a `GameActivityKeyEvent`.
+ *
+ * This is done automatically by the GameActivity: see `onKeyUp` and `onKeyDown`
+ * to set a callback to consume the received events.
+ * This function can be used if you re-implement events handling in your own
+ * activity.
+ * Ownership of out_event is maintained by the caller.
+ */
+void GameActivityKeyEvent_fromJava(JNIEnv* env, jobject motionEvent,
+ GameActivityKeyEvent* out_event);
+
+#ifdef __cplusplus
+}
+#endif
+
+/** @} */
+
+#endif // ANDROID_GAME_SDK_GAME_ACTIVITY_EVENTS_H
diff --git a/game-activity/prefab-src/modules/game-activity/include/game-activity/GameActivityLog.h b/game-activity/prefab-src/modules/game-activity/include/game-activity/GameActivityLog.h
new file mode 100644
index 0000000..ba9a9e9
--- /dev/null
+++ b/game-activity/prefab-src/modules/game-activity/include/game-activity/GameActivityLog.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef ANDROID_GAME_SDK_GAME_ACTIVITY_LOG_H_
+#define ANDROID_GAME_SDK_GAME_ACTIVITY_LOG_H_
+
+#define LOG_TAG "GameActivity"
+#include <android/log.h>
+
+#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__);
+#define ALOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__);
+#define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__);
+#ifdef NDEBUG
+#define ALOGV(...)
+#else
+#define ALOGV(...) \
+ __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__);
+#endif
+
+/* Returns 2nd arg. Used to substitute default value if caller's vararg list
+ * is empty.
+ */
+#define __android_second(first, second, ...) second
+
+/* If passed multiple args, returns ',' followed by all but 1st arg, otherwise
+ * returns nothing.
+ */
+#define __android_rest(first, ...) , ##__VA_ARGS__
+
+#define android_printAssert(cond, tag, fmt...) \
+ __android_log_assert(cond, tag, \
+ __android_second(0, ##fmt, NULL) __android_rest(fmt))
+
+#define CONDITION(cond) (__builtin_expect((cond) != 0, 0))
+
+#ifndef LOG_ALWAYS_FATAL_IF
+#define LOG_ALWAYS_FATAL_IF(cond, ...) \
+ ((CONDITION(cond)) \
+ ? ((void)android_printAssert(#cond, LOG_TAG, ##__VA_ARGS__)) \
+ : (void)0)
+#endif
+
+#ifndef LOG_ALWAYS_FATAL
+#define LOG_ALWAYS_FATAL(...) \
+ (((void)android_printAssert(NULL, LOG_TAG, ##__VA_ARGS__)))
+#endif
+
+/*
+ * Simplified macro to send a warning system log message using current LOG_TAG.
+ */
+#ifndef SLOGW
+#define SLOGW(...) \
+ ((void)__android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef SLOGW_IF
+#define SLOGW_IF(cond, ...) \
+ ((__predict_false(cond)) \
+ ? ((void)__android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)) \
+ : (void)0)
+#endif
+
+/*
+ * Versions of LOG_ALWAYS_FATAL_IF and LOG_ALWAYS_FATAL that
+ * are stripped out of release builds.
+ */
+#if LOG_NDEBUG
+
+#ifndef LOG_FATAL_IF
+#define LOG_FATAL_IF(cond, ...) ((void)0)
+#endif
+#ifndef LOG_FATAL
+#define LOG_FATAL(...) ((void)0)
+#endif
+
+#else
+
+#ifndef LOG_FATAL_IF
+#define LOG_FATAL_IF(cond, ...) LOG_ALWAYS_FATAL_IF(cond, ##__VA_ARGS__)
+#endif
+#ifndef LOG_FATAL
+#define LOG_FATAL(...) LOG_ALWAYS_FATAL(__VA_ARGS__)
+#endif
+
+#endif
+
+/*
+ * Assertion that generates a log message when the assertion fails.
+ * Stripped out of release builds. Uses the current LOG_TAG.
+ */
+#ifndef ALOG_ASSERT
+#define ALOG_ASSERT(cond, ...) LOG_FATAL_IF(!(cond), ##__VA_ARGS__)
+#endif
+
+#define LOG_TRACE(...)
+
+#endif // ANDROID_GAME_SDK_GAME_ACTIVITY_LOG_H_
diff --git a/GameActivity/prefab-src/modules/game-activity/include/game-activity/native_app_glue/android_native_app_glue.c b/game-activity/prefab-src/modules/game-activity/include/game-activity/native_app_glue/android_native_app_glue.c
similarity index 84%
rename from GameActivity/prefab-src/modules/game-activity/include/game-activity/native_app_glue/android_native_app_glue.c
rename to game-activity/prefab-src/modules/game-activity/include/game-activity/native_app_glue/android_native_app_glue.c
index 375c9f8..ebc265e 100644
--- a/GameActivity/prefab-src/modules/game-activity/include/game-activity/native_app_glue/android_native_app_glue.c
+++ b/game-activity/prefab-src/modules/game-activity/include/game-activity/native_app_glue/android_native_app_glue.c
@@ -24,10 +24,23 @@
#include <time.h>
#include <unistd.h>
+#define NATIVE_APP_GLUE_MOTION_EVENTS_DEFAULT_BUF_SIZE 16
+#define NATIVE_APP_GLUE_KEY_EVENTS_DEFAULT_BUF_SIZE 4
+
#define LOGI(...) \
((void)__android_log_print(ANDROID_LOG_INFO, "threaded_app", __VA_ARGS__))
#define LOGE(...) \
((void)__android_log_print(ANDROID_LOG_ERROR, "threaded_app", __VA_ARGS__))
+#define LOGW(...) \
+ ((void)__android_log_print(ANDROID_LOG_WARN, "threaded_app", __VA_ARGS__))
+#define LOGW_ONCE(...) \
+ do { \
+ static bool alogw_once##__FILE__##__LINE__##__ = true; \
+ if (alogw_once##__FILE__##__LINE__##__) { \
+ alogw_once##__FILE__##__LINE__##__ = false; \
+ LOGW(__VA_ARGS__); \
+ } \
+ } while (0)
/* For debug builds, always enable the debug traces in this library */
#ifndef NDEBUG
@@ -179,6 +192,7 @@
// This is run on a separate thread (i.e: not the main thread).
static void* android_app_entry(void* param) {
struct android_app* android_app = (struct android_app*)param;
+ int input_buf_idx = 0;
LOGV("android_app_entry called");
android_app->config = AConfiguration_new();
@@ -191,6 +205,19 @@
print_cur_config(android_app);
+ /* initialize event buffers */
+ for (input_buf_idx = 0; input_buf_idx < NATIVE_APP_GLUE_MAX_INPUT_BUFFERS; input_buf_idx++) {
+ struct android_input_buffer *buf = &android_app->inputBuffers[input_buf_idx];
+
+ buf->motionEventsBufferSize = NATIVE_APP_GLUE_MOTION_EVENTS_DEFAULT_BUF_SIZE;
+ buf->motionEvents = (GameActivityMotionEvent *) malloc(sizeof(GameActivityMotionEvent) *
+ buf->motionEventsBufferSize);
+
+ buf->keyEventsBufferSize = NATIVE_APP_GLUE_KEY_EVENTS_DEFAULT_BUF_SIZE;
+ buf->keyEvents = (GameActivityKeyEvent *) malloc(sizeof(GameActivityKeyEvent) *
+ buf->keyEventsBufferSize);
+ }
+
android_app->cmdPollSource.id = LOOPER_ID_MAIN;
android_app->cmdPollSource.app = android_app;
android_app->cmdPollSource.process = process_cmd;
@@ -322,6 +349,8 @@
}
static void android_app_free(struct android_app* android_app) {
+ int input_buf_idx = 0;
+
pthread_mutex_lock(&android_app->mutex);
android_app_write_cmd(android_app, APP_CMD_DESTROY);
while (!android_app->destroyed) {
@@ -329,6 +358,13 @@
}
pthread_mutex_unlock(&android_app->mutex);
+ for (input_buf_idx = 0; input_buf_idx < NATIVE_APP_GLUE_MAX_INPUT_BUFFERS; input_buf_idx++) {
+ struct android_input_buffer *buf = &android_app->inputBuffers[input_buf_idx];
+
+ free(buf->motionEvents);
+ free(buf->keyEvents);
+ }
+
close(android_app->msgread);
close(android_app->msgwrite);
pthread_cond_destroy(&android_app->cond);
@@ -426,9 +462,10 @@
android_app_write_cmd(ToApp(activity), APP_CMD_WINDOW_REDRAW_NEEDED);
}
-static void onNativeWindowResized(GameActivity* activity,
- ANativeWindow* window) {
- LOGV("NativeWindowResized: %p -- %p", activity, window);
+static void onNativeWindowResized(GameActivity* activity, ANativeWindow* window,
+ int32_t width, int32_t height) {
+ LOGV("NativeWindowResized: %p -- %p ( %d x %d )", activity, window, width,
+ height);
android_app_write_cmd(ToApp(activity), APP_CMD_WINDOW_RESIZED);
}
@@ -454,13 +491,21 @@
&android_app->inputBuffers[android_app->currentInputBuffer];
// Add to the list of active motion events
- if (inputBuffer->motionEventsCount <
- NATIVE_APP_GLUE_MAX_NUM_MOTION_EVENTS) {
- int new_ix = inputBuffer->motionEventsCount;
- memcpy(&inputBuffer->motionEvents[new_ix], event,
- sizeof(GameActivityMotionEvent));
- ++inputBuffer->motionEventsCount;
+ if (inputBuffer->motionEventsCount >= inputBuffer->motionEventsBufferSize) {
+ inputBuffer->motionEventsBufferSize *= 2;
+ inputBuffer->motionEvents = (GameActivityMotionEvent *) realloc(inputBuffer->motionEvents,
+ sizeof(GameActivityMotionEvent) * inputBuffer->motionEventsBufferSize);
+
+ if (inputBuffer->motionEvents == NULL) {
+ LOGE("onTouchEvent: out of memory");
+ abort();
+ }
}
+
+ int new_ix = inputBuffer->motionEventsCount;
+ memcpy(&inputBuffer->motionEvents[new_ix], event, sizeof(GameActivityMotionEvent));
+ ++inputBuffer->motionEventsCount;
+
pthread_mutex_unlock(&android_app->mutex);
return true;
}
@@ -511,13 +556,21 @@
&android_app->inputBuffers[android_app->currentInputBuffer];
// Add to the list of active key down events
- if (inputBuffer->keyEventsCount < NATIVE_APP_GLUE_MAX_NUM_KEY_EVENTS) {
- int new_ix = inputBuffer->keyEventsCount;
- memcpy(&inputBuffer->keyEvents[new_ix], event,
- sizeof(GameActivityKeyEvent));
- ++inputBuffer->keyEventsCount;
+ if (inputBuffer->keyEventsCount >= inputBuffer->keyEventsBufferSize) {
+ inputBuffer->keyEventsBufferSize = inputBuffer->keyEventsBufferSize * 2;
+ inputBuffer->keyEvents = (GameActivityKeyEvent *) realloc(inputBuffer->keyEvents,
+ sizeof(GameActivityKeyEvent) * inputBuffer->keyEventsBufferSize);
+
+ if (inputBuffer->keyEvents == NULL) {
+ LOGE("onKey: out of memory");
+ abort();
+ }
}
+ int new_ix = inputBuffer->keyEventsCount;
+ memcpy(&inputBuffer->keyEvents[new_ix], event, sizeof(GameActivityKeyEvent));
+ ++inputBuffer->keyEventsCount;
+
pthread_mutex_unlock(&android_app->mutex);
return true;
}
@@ -540,6 +593,20 @@
android_app_write_cmd(ToApp(activity), APP_CMD_WINDOW_INSETS_CHANGED);
}
+static void onContentRectChanged(GameActivity* activity, const ARect *rect) {
+ LOGV("ContentRectChanged: %p -- (%d %d) (%d %d)", activity, rect->left, rect->top,
+ rect->right, rect->bottom);
+
+ struct android_app* android_app = ToApp(activity);
+
+ pthread_mutex_lock(&android_app->mutex);
+ android_app->contentRect = *rect;
+
+ android_app_write_cmd(android_app, APP_CMD_CONTENT_RECT_CHANGED);
+ pthread_mutex_unlock(&android_app->mutex);
+}
+
+
JNIEXPORT
void GameActivity_onCreate(GameActivity* activity, void* savedState,
size_t savedStateSize) {
@@ -563,6 +630,7 @@
onNativeWindowRedrawNeeded;
activity->callbacks->onNativeWindowResized = onNativeWindowResized;
activity->callbacks->onWindowInsetsChanged = onWindowInsetsChanged;
+ activity->callbacks->onContentRectChanged = onContentRectChanged;
LOGV("Callbacks set: %p", activity->callbacks);
activity->instance =
diff --git a/GameActivity/prefab-src/modules/game-activity/include/game-activity/native_app_glue/android_native_app_glue.h b/game-activity/prefab-src/modules/game-activity/include/game-activity/native_app_glue/android_native_app_glue.h
similarity index 95%
rename from GameActivity/prefab-src/modules/game-activity/include/game-activity/native_app_glue/android_native_app_glue.h
rename to game-activity/prefab-src/modules/game-activity/include/game-activity/native_app_glue/android_native_app_glue.h
index 9dfe3a5..e63026f 100644
--- a/GameActivity/prefab-src/modules/game-activity/include/game-activity/native_app_glue/android_native_app_glue.h
+++ b/game-activity/prefab-src/modules/game-activity/include/game-activity/native_app_glue/android_native_app_glue.h
@@ -30,20 +30,6 @@
#include "game-activity/GameActivity.h"
-#if (defined NATIVE_APP_GLUE_MAX_NUM_MOTION_EVENTS_OVERRIDE)
-#define NATIVE_APP_GLUE_MAX_NUM_MOTION_EVENTS \
- NATIVE_APP_GLUE_MAX_NUM_MOTION_EVENTS_OVERRIDE
-#else
-#define NATIVE_APP_GLUE_MAX_NUM_MOTION_EVENTS 16
-#endif
-
-#if (defined NATIVE_APP_GLUE_MAX_NUM_KEY_EVENTS_OVERRIDE)
-#define NATIVE_APP_GLUE_MAX_NUM_KEY_EVENTS \
- NATIVE_APP_GLUE_MAX_NUM_KEY_EVENTS_OVERRIDE
-#else
-#define NATIVE_APP_GLUE_MAX_NUM_KEY_EVENTS 4
-#endif
-
#ifdef __cplusplus
extern "C" {
#endif
@@ -119,10 +105,10 @@
struct android_input_buffer {
/**
- * Pointer to a read-only array of pointers to GameActivityMotionEvent.
+ * Pointer to a read-only array of GameActivityMotionEvent.
* Only the first motionEventsCount events are valid.
*/
- GameActivityMotionEvent motionEvents[NATIVE_APP_GLUE_MAX_NUM_MOTION_EVENTS];
+ GameActivityMotionEvent *motionEvents;
/**
* The number of valid motion events in `motionEvents`.
@@ -130,15 +116,25 @@
uint64_t motionEventsCount;
/**
- * Pointer to a read-only array of pointers to GameActivityKeyEvent.
+ * The size of the `motionEvents` buffer.
+ */
+ uint64_t motionEventsBufferSize;
+
+ /**
+ * Pointer to a read-only array of GameActivityKeyEvent.
* Only the first keyEventsCount events are valid.
*/
- GameActivityKeyEvent keyEvents[NATIVE_APP_GLUE_MAX_NUM_KEY_EVENTS];
+ GameActivityKeyEvent *keyEvents;
/**
* The number of valid "Key" events in `keyEvents`.
*/
uint64_t keyEventsCount;
+
+ /**
+ * The size of the `keyEvents` buffer.
+ */
+ uint64_t keyEventsBufferSize;
};
/**
diff --git a/game-activity/prefab-src/modules/game-activity/include/game-text-input/gamecommon.h b/game-activity/prefab-src/modules/game-activity/include/game-text-input/gamecommon.h
new file mode 120000
index 0000000..df9fe6d
--- /dev/null
+++ b/game-activity/prefab-src/modules/game-activity/include/game-text-input/gamecommon.h
@@ -0,0 +1 @@
+../../../../../../game-text-input/prefab-src/modules/game-text-input/include/game-text-input/gamecommon.h
\ No newline at end of file
diff --git a/game-activity/prefab-src/modules/game-activity/include/game-text-input/gametextinput.cpp b/game-activity/prefab-src/modules/game-activity/include/game-text-input/gametextinput.cpp
new file mode 120000
index 0000000..54db52e
--- /dev/null
+++ b/game-activity/prefab-src/modules/game-activity/include/game-text-input/gametextinput.cpp
@@ -0,0 +1 @@
+../../../../../../game-text-input/prefab-src/modules/game-text-input/include/game-text-input/gametextinput.cpp
\ No newline at end of file
diff --git a/game-activity/prefab-src/modules/game-activity/include/game-text-input/gametextinput.h b/game-activity/prefab-src/modules/game-activity/include/game-text-input/gametextinput.h
new file mode 120000
index 0000000..84e9337
--- /dev/null
+++ b/game-activity/prefab-src/modules/game-activity/include/game-text-input/gametextinput.h
@@ -0,0 +1 @@
+../../../../../../game-text-input/prefab-src/modules/game-text-input/include/game-text-input/gametextinput.h
\ No newline at end of file
diff --git a/GameActivity/prefab-src/modules/game-activity/module.json b/game-activity/prefab-src/modules/game-activity/module.json
similarity index 100%
rename from GameActivity/prefab-src/modules/game-activity/module.json
rename to game-activity/prefab-src/modules/game-activity/module.json
diff --git a/GameActivity/prefab-src/prefab.json b/game-activity/prefab-src/prefab.json
similarity index 100%
rename from GameActivity/prefab-src/prefab.json
rename to game-activity/prefab-src/prefab.json
diff --git a/GameActivity/proguard-rules.pro b/game-activity/proguard-rules.pro
similarity index 100%
rename from GameActivity/proguard-rules.pro
rename to game-activity/proguard-rules.pro
diff --git a/GameActivity/src/main/AndroidManifest.xml b/game-activity/src/main/AndroidManifest.xml
similarity index 100%
rename from GameActivity/src/main/AndroidManifest.xml
rename to game-activity/src/main/AndroidManifest.xml
diff --git a/GameActivity/src/main/java/com/google/androidgamesdk/GameActivity.java b/game-activity/src/main/java/com/google/androidgamesdk/GameActivity.java
similarity index 83%
rename from GameActivity/src/main/java/com/google/androidgamesdk/GameActivity.java
rename to game-activity/src/main/java/com/google/androidgamesdk/GameActivity.java
index 670c33e..00c2890 100644
--- a/GameActivity/src/main/java/com/google/androidgamesdk/GameActivity.java
+++ b/game-activity/src/main/java/com/google/androidgamesdk/GameActivity.java
@@ -24,6 +24,7 @@
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.content.res.Configuration;
+import android.content.Context;
import android.graphics.PixelFormat;
import android.os.Build;
import android.os.Bundle;
@@ -39,6 +40,7 @@
import android.view.WindowManager;
import android.view.inputmethod.EditorInfo;
import android.widget.FrameLayout;
+import androidx.annotation.Keep;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.DisplayCutoutCompat;
@@ -57,24 +59,19 @@
public class GameActivity
extends AppCompatActivity
- implements SurfaceHolder.Callback2, Listener, OnApplyWindowInsetsListener {
+ implements SurfaceHolder.Callback2, Listener, OnApplyWindowInsetsListener,
+ OnGlobalLayoutListener {
private static final String LOG_TAG = "GameActivity";
+ private static final String DEFAULT_NATIVE_LIB_NAME = "main";
+
/**
* Optional meta-that can be in the manifest for this component, specifying
* the name of the native shared library to load. If not specified,
- * "main" is used.
+ * {@link #DEFAULT_NATIVE_LIB_NAME} is used.
*/
public static final String META_DATA_LIB_NAME = "android.app.lib_name";
- /**
- * Optional meta-that can be in the manifest for this component, specifying
- * the name of the main entry point for this native activity in the
- * {@link #META_DATA_LIB_NAME} native code. If not specified,
- * "GameActivity_onCreate" is used.
- */
- public static final String META_DATA_FUNC_NAME = "android.app.func_name";
-
private static final String KEY_NATIVE_SAVED_STATE = "android:native_state";
protected int contentViewId;
@@ -115,6 +112,27 @@
onTextInputEventNative(mNativeHandle, newState);
}
+ @Override
+ public void onGlobalLayout() {
+ mSurfaceView.getLocationInWindow(mLocation);
+ int w = mSurfaceView.getWidth();
+ int h = mSurfaceView.getHeight();
+
+ if (mLocation[0] != mLastContentX || mLocation[1] != mLastContentY
+ || w != mLastContentWidth || h != mLastContentHeight)
+ {
+ mLastContentX = mLocation[0];
+ mLastContentY = mLocation[1];
+ mLastContentWidth = w;
+ mLastContentHeight = h;
+
+ if (!mDestroyed) {
+ onContentRectChangedNative(mNativeHandle, mLastContentX, mLastContentY,
+ mLastContentWidth, mLastContentHeight);
+ }
+ }
+ }
+
// Called when we want to set the input state, e.g. before first showing the IME
public void setTextInputState(State s) {
if (mSurfaceView == null) return;
@@ -130,15 +148,20 @@
private SurfaceHolder mCurSurfaceHolder;
protected final int[] mLocation = new int[2];
+ protected int mLastContentX;
+ protected int mLastContentY;
+ protected int mLastContentWidth;
+ protected int mLastContentHeight;
+
protected boolean mDestroyed;
- protected native long loadNativeCode(String path, String funcname, String internalDataPath,
- String obbPath, String externalDataPath, AssetManager assetMgr, byte[] savedState);
+ protected native long initializeNativeCode(String internalDataPath, String obbPath,
+ String externalDataPath, AssetManager assetMgr, byte[] savedState, Configuration config);
protected native String getDlError();
- protected native void unloadNativeCode(long handle);
+ protected native void terminateNativeCode(long handle);
protected native void onStartNative(long handle);
@@ -150,7 +173,7 @@
protected native void onStopNative(long handle);
- protected native void onConfigurationChangedNative(long handle);
+ protected native void onConfigurationChangedNative(long handle, Configuration newConfig);
protected native void onTrimMemoryNative(long handle, int level);
@@ -177,6 +200,8 @@
protected native void onWindowInsetsChangedNative(long handle);
+ protected native void onContentRectChangedNative(long handle, int x, int y, int w, int h);
+
/**
* Get the pointer to the C `GameActivity` struct associated to this activity.
* @return the pointer to the C `GameActivity` struct associated to this activity.
@@ -231,10 +256,14 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
onCreateSurfaceView();
+
+ if (mSurfaceView != null) {
+ mSurfaceView.getViewTreeObserver().addOnGlobalLayoutListener(this);
+ }
+
onSetUpWindow();
- String libname = "main";
- String funcname = "GameActivity_onCreate";
+ String libname = new String(DEFAULT_NATIVE_LIB_NAME);
ActivityInfo ai;
try {
ai = getPackageManager().getActivityInfo(
@@ -243,32 +272,44 @@
String ln = ai.metaData.getString(META_DATA_LIB_NAME);
if (ln != null)
libname = ln;
- ln = ai.metaData.getString(META_DATA_FUNC_NAME);
- if (ln != null)
- funcname = ln;
}
} catch (PackageManager.NameNotFoundException e) {
throw new RuntimeException("Error getting activity info", e);
}
+ String fullLibname = "lib" + libname + ".so";
+ Log.i(LOG_TAG, "Looking for library " + fullLibname);
+
BaseDexClassLoader classLoader = (BaseDexClassLoader) getClassLoader();
String path = classLoader.findLibrary(libname);
- if (path == null) {
- throw new IllegalArgumentException("Unable to find native library " + libname
- + " using classloader: " + classLoader.toString());
+ if (path != null) {
+ Log.i(LOG_TAG, "Found library " + fullLibname + ". Loading...");
+
+ // Load the native library so that native functions are registered, even if GameActivity
+ // is not sub-classing a Java activity that uses System.loadLibrary(<libname>).
+ System.loadLibrary(libname);
+ }
+ else if (!libname.equals(DEFAULT_NATIVE_LIB_NAME)) {
+ throw new IllegalArgumentException("unable to find native library " + fullLibname +
+ " using classloader: " + classLoader.toString());
+ } else {
+ // Assume the application already loads the library explicitly.
+ Log.i(LOG_TAG,
+ "Application should have loaded the native library " + fullLibname +
+ " explicitly by now. ");
}
byte[] nativeSavedState =
savedInstanceState != null ? savedInstanceState.getByteArray(KEY_NATIVE_SAVED_STATE) : null;
- mNativeHandle = loadNativeCode(path, funcname, getAbsolutePath(getFilesDir()),
- getAbsolutePath(getObbDir()), getAbsolutePath(getExternalFilesDir(null)),
- getAssets(), nativeSavedState);
+ mNativeHandle = initializeNativeCode(getAbsolutePath(getFilesDir()),
+ getAbsolutePath(getObbDir()), getAbsolutePath(getExternalFilesDir(null)), getAssets(),
+ nativeSavedState, getResources().getConfiguration());
if (mNativeHandle == 0) {
throw new UnsatisfiedLinkError(
- "Unable to load native library \"" + path + "\": " + getDlError());
+ "Unable to initialize native code \"" + path + "\": " + getDlError());
}
// Set up the input connection
@@ -291,7 +332,7 @@
mCurSurfaceHolder = null;
}
- unloadNativeCode(mNativeHandle);
+ terminateNativeCode(mNativeHandle);
super.onDestroy();
}
@@ -332,7 +373,7 @@
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (!mDestroyed) {
- onConfigurationChangedNative(mNativeHandle);
+ onConfigurationChangedNative(mNativeHandle, newConfig);
}
}
@@ -352,6 +393,7 @@
}
}
+ @Override
public void surfaceCreated(SurfaceHolder holder) {
if (!mDestroyed) {
mCurSurfaceHolder = holder;
@@ -359,6 +401,7 @@
}
}
+ @Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
if (!mDestroyed) {
mCurSurfaceHolder = holder;
@@ -366,6 +409,7 @@
}
}
+ @Override
public void surfaceRedrawNeeded(SurfaceHolder holder) {
if (!mDestroyed) {
mCurSurfaceHolder = holder;
@@ -373,6 +417,7 @@
}
}
+ @Override
public void surfaceDestroyed(SurfaceHolder holder) {
mCurSurfaceHolder = null;
if (!mDestroyed) {
@@ -380,6 +425,7 @@
}
}
+ @Keep
void setWindowFlags(int flags, int mask) {
getWindow().setFlags(flags, mask);
}
@@ -391,11 +437,14 @@
@Override
public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) {
onWindowInsetsChangedNative(mNativeHandle);
- // Pass through to the view - we don't want to handle the insets, just observe them.
- v.onApplyWindowInsets(insets.toWindowInsets());
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
+ // Pass through to the view - we don't want to handle the insets, just observe them.
+ v.onApplyWindowInsets(insets.toWindowInsets());
+ }
return insets;
}
+ @Keep
public Insets getWindowInsets(int type) {
WindowInsetsCompat allInsets = ViewCompat.getRootWindowInsets(mSurfaceView);
Insets insets = allInsets.getInsets(type);
@@ -405,6 +454,7 @@
return insets;
}
+ @Keep
public Insets getWaterfallInsets() {
WindowInsetsCompat insets = ViewCompat.getRootWindowInsets(mSurfaceView);
DisplayCutoutCompat cutout = insets.getDisplayCutout();
@@ -442,6 +492,7 @@
* Set the inputType and actionId in order to customize how the IME behaves.
* See https://developer.android.com/reference/android/view/inputmethod/EditorInfo.
*/
+ @Keep
public void setImeEditorInfo(EditorInfo info) {
imeEditorInfo = info;
}
@@ -452,6 +503,7 @@
* This is called from the native side by GameActivity_setImeEditorInfo.
* See https://developer.android.com/reference/android/view/inputmethod/EditorInfo.
*/
+ @Keep
public void setImeEditorInfoFields(int inputType, int actionId, int imeOptions) {
EditorInfo info = getImeEditorInfo();
info.inputType = inputType;
diff --git a/game-activity/src/main/java/com/google/androidgamesdk/gametextinput b/game-activity/src/main/java/com/google/androidgamesdk/gametextinput
new file mode 120000
index 0000000..52a67e8
--- /dev/null
+++ b/game-activity/src/main/java/com/google/androidgamesdk/gametextinput
@@ -0,0 +1 @@
+../../../../../../../game-text-input/src/main/java/com/google/androidgamesdk/gametextinput
\ No newline at end of file
diff --git a/game-text-input/CMakeLists.txt b/game-text-input/CMakeLists.txt
new file mode 100644
index 0000000..a59d2f5
--- /dev/null
+++ b/game-text-input/CMakeLists.txt
@@ -0,0 +1,39 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+cmake_minimum_required(VERSION 3.10.0)
+project(gametextinput C CXX)
+set(CMAKE_CXX_STANDARD 17)
+
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/prefab-src/modules/game-text-input/include/)
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../include/)
+
+set(GAMETEXTINPUT_SRCS
+ ./prefab-src/modules/game-text-input/include/game-text-input/gametextinput.cpp)
+set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror -Os")
+set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions -fno-rtti")
+set( CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -g0")
+
+# We use this empty library to distribute source files in our .aar
+# See the build.gradle file
+add_library(game-text-input STATIC ${CMAKE_CURRENT_SOURCE_DIR}/null.cpp)
+
+add_library(game-text-input_static STATIC ${GAMETEXTINPUT_SRCS})
+
+set_target_properties(game-text-input PROPERTIES
+ LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/build )
+
+set_target_properties(game-text-input_static PROPERTIES
+ LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/build )
diff --git a/GameTextInput/README.md b/game-text-input/README.md
similarity index 100%
rename from GameTextInput/README.md
rename to game-text-input/README.md
diff --git a/game-text-input/build.gradle b/game-text-input/build.gradle
new file mode 100644
index 0000000..edfdd57
--- /dev/null
+++ b/game-text-input/build.gradle
@@ -0,0 +1,75 @@
+plugins {
+ id 'com.android.library'
+}
+
+buildDir="../../out_game_text_input"
+
+android {
+ compileSdkVersion 31
+
+ defaultConfig {
+ minSdkVersion 19
+ targetSdkVersion 31
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ consumerProguardFiles "consumer-rules.pro"
+ externalNativeBuild {
+ cmake {
+ if (project.hasProperty("stl")) {
+ arguments '-DANDROID_STL='+ project.stl
+ } else {
+ arguments '-DANDROID_STL=c++_shared'
+ }
+ }
+ }
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ externalNativeBuild {
+ cmake {
+ path "CMakeLists.txt"
+ version "3.18.0+"
+ }
+ }
+ buildFeatures {
+ prefabPublishing true
+ }
+ prefab {
+ gametextinput {
+ name "game-text-input"
+ headers "prefab-src/modules/game-text-input/include/"
+ }
+ gametextinput_static {
+ name "game-text-input_static"
+ headers "prefab-src/modules/game-text-input/include/"
+ }
+ }
+
+ // If we don't include this line the created .aar contains the c++ std lib
+ // at <.aar_file>/jni/<abi>/libc++_shared.so. When we have multiple
+ // libraries containing libc++_shared.so the linker complains because it
+ // can't choose between them. Because we use prefab we don't need to
+ // contents of the <.aar_file>/jni/* folder so we can just exclude it here
+ // to prevent the jni folder from being created.
+ packagingOptions {
+ exclude("**.so")
+ }
+}
+
+dependencies {
+ implementation 'androidx.appcompat:appcompat:1.2.0'
+ implementation 'androidx.core:core:1.5.0'
+ implementation 'com.google.android.material:material:1.1.0'
+}
diff --git a/GameTextInput/consumer-rules.pro b/game-text-input/consumer-rules.pro
similarity index 100%
rename from GameTextInput/consumer-rules.pro
rename to game-text-input/consumer-rules.pro
diff --git a/GameTextInput/consumer-rules.pro b/game-text-input/null.cpp
similarity index 100%
copy from GameTextInput/consumer-rules.pro
copy to game-text-input/null.cpp
diff --git a/game-text-input/prefab-src/modules/game-text-input/include/common b/game-text-input/prefab-src/modules/game-text-input/include/common
new file mode 120000
index 0000000..9388f34
--- /dev/null
+++ b/game-text-input/prefab-src/modules/game-text-input/include/common
@@ -0,0 +1 @@
+../../../../../include/common/
\ No newline at end of file
diff --git a/GameTextInput/prefab-src/modules/game-text-input/include/game-text-input/gamecommon.h b/game-text-input/prefab-src/modules/game-text-input/include/game-text-input/gamecommon.h
similarity index 100%
rename from GameTextInput/prefab-src/modules/game-text-input/include/game-text-input/gamecommon.h
rename to game-text-input/prefab-src/modules/game-text-input/include/game-text-input/gamecommon.h
diff --git a/GameTextInput/prefab-src/modules/game-text-input/include/game-text-input/gametextinput.cpp b/game-text-input/prefab-src/modules/game-text-input/include/game-text-input/gametextinput.cpp
similarity index 96%
rename from GameTextInput/prefab-src/modules/game-text-input/include/game-text-input/gametextinput.cpp
rename to game-text-input/prefab-src/modules/game-text-input/include/game-text-input/gametextinput.cpp
index f25437a..b8244fb 100644
--- a/GameTextInput/prefab-src/modules/game-text-input/include/game-text-input/gametextinput.cpp
+++ b/game-text-input/prefab-src/modules/game-text-input/include/game-text-input/gametextinput.cpp
@@ -48,6 +48,7 @@
void processEvent(jobject textInputEvent);
void showIme(uint32_t flags);
void hideIme(uint32_t flags);
+ void restartInput();
void setEventCallback(GameTextInputEventCallback callback, void *context);
jobject stateToJava(const GameTextInputState &state) const;
void stateFromJava(jobject textInputEvent,
@@ -73,6 +74,7 @@
jobject inputConnection_ = nullptr;
jmethodID inputConnectionSetStateMethod_;
jmethodID setSoftKeyboardActiveMethod_;
+ jmethodID restartInputMethod_;
void (*eventCallback_)(void *context,
const struct GameTextInputState *state) = nullptr;
void *eventCallbackContext_ = nullptr;
@@ -164,6 +166,10 @@
input->hideIme(flags);
}
+void GameTextInput_restartInput(struct GameTextInput *input) {
+ input->restartInput();
+}
+
void GameTextInput_setEventCallback(struct GameTextInput *input,
GameTextInputEventCallback callback,
void *context) {
@@ -199,6 +205,8 @@
"(Lcom/google/androidgamesdk/gametextinput/State;)V");
setSoftKeyboardActiveMethod_ = env_->GetMethodID(
inputConnectionClass_, "setSoftKeyboardActive", "(ZI)V");
+ restartInputMethod_ =
+ env_->GetMethodID(inputConnectionClass_, "restartInput", "()V");
stateClassInfo_.text =
env_->GetFieldID(stateJavaClass_, "text", "Ljava/lang/String;");
@@ -210,8 +218,6 @@
env_->GetFieldID(stateJavaClass_, "composingRegionStart", "I");
stateClassInfo_.composingRegionEnd =
env_->GetFieldID(stateJavaClass_, "composingRegionEnd", "I");
-
- return s_gameTextInput.get();
}
GameTextInput::~GameTextInput() {
@@ -309,6 +315,11 @@
flags);
}
+void GameTextInput::restartInput() {
+ if (inputConnection_ == nullptr) return;
+ env_->CallVoidMethod(inputConnection_, restartInputMethod_, false);
+}
+
jobject GameTextInput::stateToJava(const GameTextInputState &state) const {
static jmethodID constructor = nullptr;
if (constructor == nullptr) {
diff --git a/GameTextInput/prefab-src/modules/game-text-input/include/game-text-input/gametextinput.h b/game-text-input/prefab-src/modules/game-text-input/include/game-text-input/gametextinput.h
similarity index 94%
rename from GameTextInput/prefab-src/modules/game-text-input/include/game-text-input/gametextinput.h
rename to game-text-input/prefab-src/modules/game-text-input/include/game-text-input/gametextinput.h
index a85265e..8c33da5 100644
--- a/GameTextInput/prefab-src/modules/game-text-input/include/game-text-input/gametextinput.h
+++ b/game-text-input/prefab-src/modules/game-text-input/include/game-text-input/gametextinput.h
@@ -26,12 +26,21 @@
#include <jni.h>
#include <stdint.h>
+#include "common/gamesdk_common.h"
#include "gamecommon.h"
#ifdef __cplusplus
extern "C" {
#endif
+#define GAMETEXTINPUT_MAJOR_VERSION 2
+#define GAMETEXTINPUT_MINOR_VERSION 0
+#define GAMETEXTINPUT_BUGFIX_VERSION 0
+#define GAMETEXTINPUT_PACKED_VERSION \
+ ANDROID_GAMESDK_PACKED_VERSION(GAMETEXTINPUT_MAJOR_VERSION, \
+ GAMETEXTINPUT_MINOR_VERSION, \
+ GAMETEXTINPUT_BUGFIX_VERSION)
+
/**
* This struct holds a span within a region of text from start (inclusive) to
* end (exclusive). An empty span or cursor position is specified with
@@ -174,6 +183,12 @@
void GameTextInput_hideIme(GameTextInput *input, uint32_t flags);
/**
+ * Restarts the input method. Calls InputMethodManager.restartInput().
+ * @param input A valid GameTextInput library handle.
+ */
+void GameTextInput_restartInput(GameTextInput *input);
+
+/**
* Call a callback with the current GameTextInput state, which may have been
* modified by changes in the IME and calls to GameTextInput_setState. We use a
* callback rather than returning the state in order to simplify ownership of
diff --git a/GameTextInput/prefab-src/modules/game-text-input/module.json b/game-text-input/prefab-src/modules/game-text-input/module.json
similarity index 100%
rename from GameTextInput/prefab-src/modules/game-text-input/module.json
rename to game-text-input/prefab-src/modules/game-text-input/module.json
diff --git a/GameTextInput/prefab-src/prefab.json b/game-text-input/prefab-src/prefab.json
similarity index 100%
rename from GameTextInput/prefab-src/prefab.json
rename to game-text-input/prefab-src/prefab.json
diff --git a/GameTextInput/proguard-rules.pro b/game-text-input/proguard-rules.pro
similarity index 100%
rename from GameTextInput/proguard-rules.pro
rename to game-text-input/proguard-rules.pro
diff --git a/GameTextInput/src/main/AndroidManifest.xml b/game-text-input/src/main/AndroidManifest.xml
similarity index 100%
rename from GameTextInput/src/main/AndroidManifest.xml
rename to game-text-input/src/main/AndroidManifest.xml
diff --git a/GameTextInput/src/main/java/com/google/androidgamesdk/gametextinput/GameTextInput.java b/game-text-input/src/main/java/com/google/androidgamesdk/gametextinput/GameTextInput.java
similarity index 100%
rename from GameTextInput/src/main/java/com/google/androidgamesdk/gametextinput/GameTextInput.java
rename to game-text-input/src/main/java/com/google/androidgamesdk/gametextinput/GameTextInput.java
diff --git a/GameTextInput/src/main/java/com/google/androidgamesdk/gametextinput/InputConnection.java b/game-text-input/src/main/java/com/google/androidgamesdk/gametextinput/InputConnection.java
similarity index 98%
rename from GameTextInput/src/main/java/com/google/androidgamesdk/gametextinput/InputConnection.java
rename to game-text-input/src/main/java/com/google/androidgamesdk/gametextinput/InputConnection.java
index 50c6ae7..0ac91ac 100644
--- a/GameTextInput/src/main/java/com/google/androidgamesdk/gametextinput/InputConnection.java
+++ b/game-text-input/src/main/java/com/google/androidgamesdk/gametextinput/InputConnection.java
@@ -26,6 +26,7 @@
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
+import androidx.annotation.Keep;
import androidx.core.graphics.Insets;
import androidx.core.view.OnApplyWindowInsetsListener;
import androidx.core.view.ViewCompat;
@@ -34,10 +35,11 @@
import com.google.androidgamesdk.gametextinput.GameTextInput.Pair;
import java.util.BitSet;
+@Keep
public class InputConnection
extends BaseInputConnection
implements View.OnKeyListener, OnApplyWindowInsetsListener {
- private static final String TAG = "gametextinput.InputConnection";
+ private static final String TAG = "gti.InputConnection";
// TODO: (b/183179971) We should react to most of these events rather than ignoring them? Plus
// there are others that should be ignored.
private static final int[] notInsertedKeyCodes = {KeyEvent.KEYCODE_DEL,
@@ -67,7 +69,7 @@
super(targetView, settings.mEditorInfo.inputType != 0);
this.targetView = targetView;
this.settings = settings;
- Object imm = ctx.getSystemService("input_method");
+ Object imm = ctx.getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm == null) {
throw new java.lang.RuntimeException("Can't get IMM");
} else {
diff --git a/GameTextInput/src/main/java/com/google/androidgamesdk/gametextinput/Listener.java b/game-text-input/src/main/java/com/google/androidgamesdk/gametextinput/Listener.java
similarity index 100%
rename from GameTextInput/src/main/java/com/google/androidgamesdk/gametextinput/Listener.java
rename to game-text-input/src/main/java/com/google/androidgamesdk/gametextinput/Listener.java
diff --git a/GameTextInput/src/main/java/com/google/androidgamesdk/gametextinput/Settings.java b/game-text-input/src/main/java/com/google/androidgamesdk/gametextinput/Settings.java
similarity index 100%
rename from GameTextInput/src/main/java/com/google/androidgamesdk/gametextinput/Settings.java
rename to game-text-input/src/main/java/com/google/androidgamesdk/gametextinput/Settings.java
diff --git a/GameTextInput/src/main/java/com/google/androidgamesdk/gametextinput/State.java b/game-text-input/src/main/java/com/google/androidgamesdk/gametextinput/State.java
similarity index 96%
rename from GameTextInput/src/main/java/com/google/androidgamesdk/gametextinput/State.java
rename to game-text-input/src/main/java/com/google/androidgamesdk/gametextinput/State.java
index cb43279..f6d1f48 100644
--- a/GameTextInput/src/main/java/com/google/androidgamesdk/gametextinput/State.java
+++ b/game-text-input/src/main/java/com/google/androidgamesdk/gametextinput/State.java
@@ -15,7 +15,10 @@
*/
package com.google.androidgamesdk.gametextinput;
+import androidx.annotation.Keep;
+
// The state of an editable text region.
+@Keep
public final class State {
public State(String text_in, int selectionStart_in, int selectionEnd_in,
int composingRegionStart_in, int composingRegionEnd_in) {
diff --git a/games-controller/build.gradle b/games-controller/build.gradle
new file mode 100644
index 0000000..2811c66
--- /dev/null
+++ b/games-controller/build.gradle
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+plugins {
+ id 'com.android.library'
+}
+
+buildDir="../../out_paddleboat"
+
+android {
+
+ defaultConfig {
+ minSdkVersion 19
+ compileSdkVersion 31
+ targetSdkVersion 31
+ versionCode 1
+ versionName "1.1"
+
+ consumerProguardFiles "consumer-rules.pro"
+ externalNativeBuild {
+ cmake {
+ if (project.hasProperty("stl")) {
+ arguments '-DANDROID_STL='+ project.stl
+ } else {
+ arguments '-DANDROID_STL=c++_shared'
+ }
+ }
+ }
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ externalNativeBuild {
+ cmake {
+ path "src/main/cpp/CMakeLists.txt"
+ version "3.18.0+"
+ }
+ }
+
+ buildFeatures {
+ prefabPublishing true
+ }
+
+ prefab {
+ paddleboat {
+ name "paddleboat"
+ headers "src/main/cpp/include"
+ }
+
+ paddleboat_static {
+ name "paddleboat_static"
+ headers "src/main/cpp/include"
+ }
+ }
+
+ // If we don't include this line the created .aar contains the c++ std lib
+ // at <.aar_file>/jni/<abi>/libc++_shared.so. When we have multiple
+ // libraries containing libc++_shared.so the linker complains because it
+ // can't choose between them. Because we use prefab we don't need to
+ // contents of the <.aar_file>/jni/* folder so we can just exclude it here
+ // to prevent the jni folder from being created.
+ packagingOptions {
+ exclude("**.so")
+ }
+}
+
+dependencies {
+ implementation "androidx.annotation:annotation:1.3.0"
+}
+repositories {
+ mavenCentral()
+}
diff --git a/GameController/consumer-rules.pro b/games-controller/consumer-rules.pro
similarity index 100%
rename from GameController/consumer-rules.pro
rename to games-controller/consumer-rules.pro
diff --git a/GameController/proguard-rules.pro b/games-controller/proguard-rules.pro
similarity index 100%
rename from GameController/proguard-rules.pro
rename to games-controller/proguard-rules.pro
diff --git a/GameController/src/androidTest/java/com/google/android/games/paddleboat/ExampleInstrumentedTest.java b/games-controller/src/androidTest/java/com/google/android/games/paddleboat/ExampleInstrumentedTest.java
similarity index 100%
rename from GameController/src/androidTest/java/com/google/android/games/paddleboat/ExampleInstrumentedTest.java
rename to games-controller/src/androidTest/java/com/google/android/games/paddleboat/ExampleInstrumentedTest.java
diff --git a/GameController/src/main/AndroidManifest.xml b/games-controller/src/main/AndroidManifest.xml
similarity index 100%
rename from GameController/src/main/AndroidManifest.xml
rename to games-controller/src/main/AndroidManifest.xml
diff --git a/GameController/src/main/cpp/CMakeLists.txt b/games-controller/src/main/cpp/CMakeLists.txt
similarity index 100%
rename from GameController/src/main/cpp/CMakeLists.txt
rename to games-controller/src/main/cpp/CMakeLists.txt
diff --git a/GameController/src/main/cpp/GameController.cpp b/games-controller/src/main/cpp/GameController.cpp
similarity index 88%
rename from GameController/src/main/cpp/GameController.cpp
rename to games-controller/src/main/cpp/GameController.cpp
index a0909f2..0901ce6 100644
--- a/GameController/src/main/cpp/GameController.cpp
+++ b/games-controller/src/main/cpp/GameController.cpp
@@ -106,9 +106,12 @@
}
void GameController::setupController(
- const Paddleboat_Controller_Mapping_Data *mappingData) {
+ const Paddleboat_Controller_Mapping_File_Controller_Entry *controllerEntry,
+ const Paddleboat_Controller_Mapping_File_Axis_Entry *axisEntry,
+ const Paddleboat_Controller_Mapping_File_Button_Entry *buttonEntry) {
+
const GameControllerDeviceInfo::InfoFields &infoFields =
- *(mDeviceInfo.getInfo());
+ *(mDeviceInfo.getInfo());
uint64_t axisLow = static_cast<uint64_t>(infoFields.mAxisBitsLow);
uint64_t axisHigh = static_cast<uint64_t>(infoFields.mAxisBitsHigh);
mControllerAxisMask = axisLow | (axisHigh << 32ULL);
@@ -118,62 +121,65 @@
mControllerInfo.productId = infoFields.mProductId;
mControllerInfo.vendorId = infoFields.mVendorId;
- if (mappingData != nullptr) {
- mControllerInfo.controllerFlags |= mappingData->flags;
+ if (controllerEntry != nullptr) {
+ mControllerInfo.controllerFlags |= controllerEntry->flags;
+ mAxisInversionBitmask = axisEntry->axisInversionBitmask;
for (int32_t i = 0; i < PADDLEBOAT_BUTTON_COUNT; ++i) {
- if (mappingData->buttonMapping[i] != PADDLEBOAT_BUTTON_IGNORED) {
- mButtonKeycodes[i] = mappingData->buttonMapping[i];
+ if (buttonEntry->buttonMapping[i] != PADDLEBOAT_BUTTON_IGNORED) {
+ mButtonKeycodes[i] = buttonEntry->buttonMapping[i];
}
}
for (int32_t i = 0; i < PADDLEBOAT_MAPPING_AXIS_COUNT; ++i) {
- if (mappingData->axisMapping[i] != PADDLEBOAT_AXIS_IGNORED) {
+ if (axisEntry->axisMapping[i] != PADDLEBOAT_AXIS_IGNORED) {
+ bool axisInvert = ((mAxisInversionBitmask & (1 << i)) != 0);
const GameControllerAxis gcAxis =
- static_cast<GameControllerAxis>(i);
- const int32_t nativeAxisId = mappingData->axisMapping[i];
+ static_cast<GameControllerAxis>(i);
+ const int32_t nativeAxisId = axisEntry->axisMapping[i];
const int32_t positiveButton =
- (mappingData->axisPositiveButtonMapping[i] ==
- PADDLEBOAT_AXIS_BUTTON_IGNORED)
+ (axisEntry->axisPositiveButtonMapping[i] ==
+ PADDLEBOAT_AXIS_BUTTON_IGNORED)
? 0
- : (1 << mappingData->axisPositiveButtonMapping[i]);
+ : (1 << axisEntry->axisPositiveButtonMapping[i]);
const int32_t negativeButton =
- (mappingData->axisNegativeButtonMapping[i] ==
- PADDLEBOAT_AXIS_BUTTON_IGNORED)
+ (axisEntry->axisNegativeButtonMapping[i] ==
+ PADDLEBOAT_AXIS_BUTTON_IGNORED)
? 0
- : (1 << mappingData->axisNegativeButtonMapping[i]);
+ : (1 << axisEntry->axisNegativeButtonMapping[i]);
setupAxis(gcAxis, nativeAxisId, nativeAxisId, positiveButton,
- negativeButton);
+ negativeButton, axisInvert);
}
}
adjustAxisConstants();
+
} else {
// Fallback defaults if there wasn't mapping data provided
initializeDefaultAxisMapping();
memcpy(mButtonKeycodes, &defaultButtonMap, sizeof(ControllerButtonMap));
mControllerInfo.controllerFlags |=
- PADDLEBOAT_CONTROLLER_FLAG_GENERIC_PROFILE;
+ PADDLEBOAT_CONTROLLER_FLAG_GENERIC_PROFILE;
}
}
void GameController::initializeDefaultAxisMapping() {
setupAxis(GAMECONTROLLER_AXIS_LSTICK_X, AMOTION_EVENT_AXIS_X,
- AMOTION_EVENT_AXIS_X, 0, 0);
+ AMOTION_EVENT_AXIS_X, 0, 0, false);
setupAxis(GAMECONTROLLER_AXIS_LSTICK_Y, AMOTION_EVENT_AXIS_Y,
- AMOTION_EVENT_AXIS_Y, 0, 0);
+ AMOTION_EVENT_AXIS_Y, 0, 0, false);
setupAxis(GAMECONTROLLER_AXIS_RSTICK_X, AMOTION_EVENT_AXIS_Z,
- AMOTION_EVENT_AXIS_RX, 0, 0);
+ AMOTION_EVENT_AXIS_RX, 0, 0, false);
setupAxis(GAMECONTROLLER_AXIS_RSTICK_Y, AMOTION_EVENT_AXIS_RZ,
- AMOTION_EVENT_AXIS_RY, 0, 0);
+ AMOTION_EVENT_AXIS_RY, 0, 0, false);
setupAxis(GAMECONTROLLER_AXIS_L2, AMOTION_EVENT_AXIS_LTRIGGER,
- AMOTION_EVENT_AXIS_BRAKE, PADDLEBOAT_BUTTON_L2, 0);
+ AMOTION_EVENT_AXIS_BRAKE, PADDLEBOAT_BUTTON_L2, 0, false);
setupAxis(GAMECONTROLLER_AXIS_R2, AMOTION_EVENT_AXIS_RTRIGGER,
- AMOTION_EVENT_AXIS_GAS, PADDLEBOAT_BUTTON_R2, 0);
+ AMOTION_EVENT_AXIS_GAS, PADDLEBOAT_BUTTON_R2, 0, false);
setupAxis(GAMECONTROLLER_AXIS_HAT_X, AMOTION_EVENT_AXIS_HAT_X,
AMOTION_EVENT_AXIS_HAT_X, PADDLEBOAT_BUTTON_DPAD_RIGHT,
- PADDLEBOAT_BUTTON_DPAD_LEFT);
+ PADDLEBOAT_BUTTON_DPAD_LEFT, false);
setupAxis(GAMECONTROLLER_AXIS_HAT_Y, AMOTION_EVENT_AXIS_HAT_Y,
AMOTION_EVENT_AXIS_HAT_Y, PADDLEBOAT_BUTTON_DPAD_DOWN,
- PADDLEBOAT_BUTTON_DPAD_UP);
+ PADDLEBOAT_BUTTON_DPAD_UP, false);
adjustAxisConstants();
}
@@ -230,13 +236,13 @@
// We are adjusting the raw axis values, so we also adjust the
// 'flat' and 'fuzz' values for the sticks
mControllerInfo.rightStickPrecision.stickFlatX *=
- mAxisInfo[GAMECONTROLLER_AXIS_RSTICK_X].axisMultiplier;
+ fabs(mAxisInfo[GAMECONTROLLER_AXIS_RSTICK_X].axisMultiplier);
mControllerInfo.rightStickPrecision.stickFlatY *=
- mAxisInfo[GAMECONTROLLER_AXIS_RSTICK_Y].axisMultiplier;
+ fabs(mAxisInfo[GAMECONTROLLER_AXIS_RSTICK_Y].axisMultiplier);
mControllerInfo.rightStickPrecision.stickFuzzX *=
- mAxisInfo[GAMECONTROLLER_AXIS_RSTICK_X].axisMultiplier;
+ fabs(mAxisInfo[GAMECONTROLLER_AXIS_RSTICK_X].axisMultiplier);
mControllerInfo.rightStickPrecision.stickFuzzY *=
- mAxisInfo[GAMECONTROLLER_AXIS_RSTICK_Y].axisMultiplier;
+ fabs(mAxisInfo[GAMECONTROLLER_AXIS_RSTICK_Y].axisMultiplier);
}
}
}
@@ -245,10 +251,12 @@
const int32_t preferredNativeAxisId,
const int32_t secondaryNativeAxisId,
const int32_t buttonMask,
- const int32_t buttonNegativeMask) {
+ const int32_t buttonNegativeMask,
+ const bool axisInvert) {
mAxisInfo[gcAxis].resetInfo();
mAxisInfo[gcAxis].axisButtonMask = buttonMask;
mAxisInfo[gcAxis].axisButtonNegativeMask = buttonNegativeMask;
+ mAxisInfo[gcAxis].axisInvert = axisInvert;
// Do we have a mapping for the preferred native axis?
if (preferredNativeAxisId < MAX_AXIS_COUNT &&
@@ -407,6 +415,9 @@
axisValue = ((axisValue * mAxisInfo[axis].axisMultiplier) +
mAxisInfo[axis].axisAdjust);
}
+ if (mAxisInfo[axis].axisInvert) {
+ axisValue = -axisValue;
+ }
// We take advantage of the GameControllerAxis matching the axis
// order in the Paddleboat_Controller_Data struct to use the current
diff --git a/GameController/src/main/cpp/GameController.h b/games-controller/src/main/cpp/GameController.h
similarity index 91%
rename from GameController/src/main/cpp/GameController.h
rename to games-controller/src/main/cpp/GameController.h
index eeb03c6..080a477 100644
--- a/GameController/src/main/cpp/GameController.h
+++ b/games-controller/src/main/cpp/GameController.h
@@ -20,6 +20,7 @@
#include "GameControllerDeviceInfo.h"
#include "GameControllerGameActivityMirror.h"
+#include "GameControllerMappingFile.h"
#include "paddleboat.h"
namespace paddleboat {
@@ -63,6 +64,8 @@
float axisMultiplier = 1.0f;
// Adjustment to bring center to 0.0 if necessary
float axisAdjust = 0.0f;
+ // Invert the axis data
+ bool axisInvert = false;
void resetInfo() {
axisIndex = -1;
@@ -71,12 +74,15 @@
axisButtonNegativeMask = 0;
axisMultiplier = 1.0f;
axisAdjust = 0.0f;
+ axisInvert = false;
}
};
GameController();
- void setupController(const Paddleboat_Controller_Mapping_Data *mappingData);
+ void setupController(const Paddleboat_Controller_Mapping_File_Controller_Entry
+ *controllerEntry, const Paddleboat_Controller_Mapping_File_Axis_Entry *axisEntry,
+ const Paddleboat_Controller_Mapping_File_Button_Entry *buttonEntry);
void initializeDefaultAxisMapping();
@@ -146,7 +152,8 @@
void setupAxis(const GameControllerAxis gcAxis,
const int32_t preferredNativeAxisId,
const int32_t secondaryNativeAxisId,
- const int32_t buttonMask, const int32_t buttonNegativeMask);
+ const int32_t buttonMask, const int32_t buttonNegativeMask,
+ const bool axisInvert);
void adjustAxisConstants();
@@ -154,6 +161,7 @@
Paddleboat_ControllerStatus mControllerStatus =
PADDLEBOAT_CONTROLLER_INACTIVE;
int32_t mConnectionIndex = -1;
+ uint32_t mAxisInversionBitmask = 0;
Paddleboat_Controller_Data mControllerData;
Paddleboat_Controller_Info mControllerInfo;
int32_t mButtonKeycodes[PADDLEBOAT_BUTTON_COUNT];
diff --git a/GameController/src/main/cpp/GameControllerDeviceInfo.cpp b/games-controller/src/main/cpp/GameControllerDeviceInfo.cpp
similarity index 100%
rename from GameController/src/main/cpp/GameControllerDeviceInfo.cpp
rename to games-controller/src/main/cpp/GameControllerDeviceInfo.cpp
diff --git a/GameController/src/main/cpp/GameControllerDeviceInfo.h b/games-controller/src/main/cpp/GameControllerDeviceInfo.h
similarity index 100%
rename from GameController/src/main/cpp/GameControllerDeviceInfo.h
rename to games-controller/src/main/cpp/GameControllerDeviceInfo.h
diff --git a/GameController/src/main/cpp/GameControllerGameActivityMirror.h b/games-controller/src/main/cpp/GameControllerGameActivityMirror.h
similarity index 100%
rename from GameController/src/main/cpp/GameControllerGameActivityMirror.h
rename to games-controller/src/main/cpp/GameControllerGameActivityMirror.h
diff --git a/GameController/src/main/cpp/GameControllerInternalConstants.h b/games-controller/src/main/cpp/GameControllerInternalConstants.h
similarity index 100%
rename from GameController/src/main/cpp/GameControllerInternalConstants.h
rename to games-controller/src/main/cpp/GameControllerInternalConstants.h
diff --git a/GameController/src/main/cpp/GameControllerLog.cpp b/games-controller/src/main/cpp/GameControllerLog.cpp
similarity index 100%
rename from GameController/src/main/cpp/GameControllerLog.cpp
rename to games-controller/src/main/cpp/GameControllerLog.cpp
diff --git a/GameController/src/main/cpp/GameControllerLog.h b/games-controller/src/main/cpp/GameControllerLog.h
similarity index 100%
rename from GameController/src/main/cpp/GameControllerLog.h
rename to games-controller/src/main/cpp/GameControllerLog.h
diff --git a/GameController/src/main/cpp/GameControllerLogStrings.h b/games-controller/src/main/cpp/GameControllerLogStrings.h
similarity index 100%
rename from GameController/src/main/cpp/GameControllerLogStrings.h
rename to games-controller/src/main/cpp/GameControllerLogStrings.h
diff --git a/GameController/src/main/cpp/GameControllerManager.cpp b/games-controller/src/main/cpp/GameControllerManager.cpp
similarity index 82%
rename from GameController/src/main/cpp/GameControllerManager.cpp
rename to games-controller/src/main/cpp/GameControllerManager.cpp
index 6811a63..a52b17c 100644
--- a/GameController/src/main/cpp/GameControllerManager.cpp
+++ b/games-controller/src/main/cpp/GameControllerManager.cpp
@@ -18,7 +18,11 @@
#include <android/api-level.h>
#include <cstdlib>
+#include <fcntl.h>
#include <memory>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
#include "GameControllerInternalConstants.h"
#include "GameControllerLog.h"
@@ -104,9 +108,9 @@
void Java_com_google_android_games_paddleboat_GameControllerManager_onMotionData(
JNIEnv *env, jobject gcmObject, jint deviceId, jint motionType,
- jlong timestamp, jfloat dataX, jfloat dataY, jfloat dataZ) {
- paddleboat::GameControllerManager::onMotionData(deviceId, motionType,
- timestamp, dataX, dataY, dataZ);
+ jlong timestamp, jfloat dataX, jfloat dataY, jfloat dataZ) {
+ paddleboat::GameControllerManager::onMotionData(
+ deviceId, motionType, timestamp, dataX, dataY, dataZ);
}
void Java_com_google_android_games_paddleboat_GameControllerManager_onMouseConnected(
@@ -119,6 +123,16 @@
paddleboat::GameControllerManager::onMouseDisconnection(deviceId);
}
+void Java_com_google_android_games_paddleboat_GameControllerManager_onKeyboardConnected(
+ JNIEnv *env, jobject gcmObject, jint deviceId) {
+ paddleboat::GameControllerManager::onKeyboardConnection(deviceId);
+}
+
+void Java_com_google_android_games_paddleboat_GameControllerManager_onKeyboardDisconnected(
+ JNIEnv *env, jobject gcmObject, jint deviceId) {
+ paddleboat::GameControllerManager::onKeyboardDisconnection(deviceId);
+}
+
} // extern "C"
namespace paddleboat {
@@ -143,6 +157,10 @@
constexpr const char *GCM_GETBATTERYLEVEL_METHOD_SIGNATURE = "(I)F";
constexpr const char *GCM_GETBATTERYSTATUS_METHOD_NAME = "getBatteryStatus";
constexpr const char *GCM_GETBATTERYSTATUS_METHOD_SIGNATURE = "(I)I";
+constexpr const char *GCM_GETINTEGRATED_METHOD_NAME = "getIntegratedSensorFlags";
+constexpr const char *GCM_GETINTEGRATED_METHOD_SIGNATURE = "()I";
+constexpr const char *GCM_SETACTIVESENSOR_METHOD_NAME = "setActiveIntegratedSensors";
+constexpr const char *GCM_SETACTIVESENSOR_METHOD_SIGNATURE = "(I)V";
constexpr const char *GCM_SETLIGHT_METHOD_NAME = "setLight";
constexpr const char *GCM_SETLIGHT_METHOD_SIGNATURE = "(III)V";
constexpr const char *GCM_SETNATIVEREADY_METHOD_NAME = "setNativeReady";
@@ -171,6 +189,12 @@
{"onMotionData", "(IIJFFF)V",
reinterpret_cast<void *>(
Java_com_google_android_games_paddleboat_GameControllerManager_onMotionData)},
+ {"onKeyboardConnected", "(I)V",
+ reinterpret_cast<void *>(
+ Java_com_google_android_games_paddleboat_GameControllerManager_onKeyboardConnected)},
+ {"onKeyboardDisconnected", "(I)V",
+ reinterpret_cast<void *>(
+ Java_com_google_android_games_paddleboat_GameControllerManager_onKeyboardDisconnected)},
{"onMouseConnected", "(I)V",
reinterpret_cast<void *>(
Java_com_google_android_games_paddleboat_GameControllerManager_onMouseConnected)},
@@ -300,6 +324,10 @@
&mGetBatteryLevelMethodId},
{GCM_GETBATTERYSTATUS_METHOD_NAME,
GCM_GETBATTERYSTATUS_METHOD_SIGNATURE, &mGetBatteryStatusMethodId},
+ {GCM_GETINTEGRATED_METHOD_NAME,
+ GCM_GETINTEGRATED_METHOD_SIGNATURE, &mGetIntegratedSensorMethodId},
+ {GCM_SETACTIVESENSOR_METHOD_NAME,
+ GCM_SETACTIVESENSOR_METHOD_SIGNATURE, &mSetActiveIntegratedSensorsMethodId},
{GCM_SETVIBRATION_METHOD_NAME, GCM_SETVIBRATION_METHOD_SIGNATURE,
&mSetVibrationMethodId},
{GCM_SETLIGHT_METHOD_NAME, GCM_SETLIGHT_METHOD_SIGNATURE,
@@ -307,15 +335,13 @@
{GCM_SETNATIVEREADY_METHOD_NAME, VOID_METHOD_SIGNATURE,
&mSetNativeReadyMethodId},
{GCM_SETREPORTMOTIONEVENTS_METHOD_NAME, VOID_METHOD_SIGNATURE,
- &mSetReportMotionEventsMethodId}
- };
+ &mSetReportMotionEventsMethodId}};
const size_t methodTableCount = ARRAY_COUNTOF(methodTable);
for (size_t i = 0; i < methodTableCount; ++i) {
const MethodTableEntry &entry = methodTable[i];
- jmethodID methodID = env->GetMethodID(mGameControllerClass,
- entry.methodName,
- entry.methodSignature);
+ jmethodID methodID = env->GetMethodID(
+ mGameControllerClass, entry.methodName, entry.methodSignature);
if (methodID == NULL) {
ALOGE("Failed to find %s init method", entry.methodName);
return PADDLEBOAT_ERROR_INIT_GCM_FAILURE;
@@ -336,13 +362,25 @@
mMouseData.mouseX = 0.0f;
mMouseData.mouseY = 0.0f;
mInitialized = true;
- memset(mMappingTable, 0, sizeof(mMappingTable));
- mRemapEntryCount = GetInternalControllerDataCount();
- const Paddleboat_Controller_Mapping_Data *mappingData =
- GetInternalControllerData();
- const size_t copySize =
- mRemapEntryCount * sizeof(Paddleboat_Controller_Mapping_Data);
- memcpy(mMappingTable, mappingData, copySize);
+
+ const Paddleboat_Internal_Mapping_Header *mappingHeader = GetInternalMappingHeader();
+ mMappingInfo.mAxisTableEntryCount = mappingHeader->axisTableEntryCount;
+ mMappingInfo.mButtonTableEntryCount = mappingHeader->buttonTableEntryCount;
+ mMappingInfo.mControllerTableEntryCount = mappingHeader->controllerTableEntryCount;
+ mMappingInfo.mStringTableEntryCount = mappingHeader->stringTableEntryCount;
+ memcpy(mMappingInfo.mAxisTable, mappingHeader->axisTable,
+ sizeof(Paddleboat_Controller_Mapping_File_Axis_Entry) *
+ mMappingInfo.mAxisTableEntryCount);
+ memcpy(mMappingInfo.mButtonTable, mappingHeader->buttonTable,
+ sizeof(Paddleboat_Controller_Mapping_File_Button_Entry) *
+ mMappingInfo.mButtonTableEntryCount);
+ memcpy(mMappingInfo.mControllerTable, mappingHeader->controllerTable,
+ sizeof(Paddleboat_Controller_Mapping_File_Controller_Entry) *
+ mMappingInfo.mControllerTableEntryCount);
+ memcpy(mMappingInfo.mStringTable, mappingHeader->stringTable,
+ sizeof(Paddleboat_Controller_Mapping_File_String_Entry) *
+ mMappingInfo.mStringTableEntryCount);
+
// Our minimum supported API level, we will retrieve the actual runtime API
// level later on calling getApiLevel
mApiLevel = 16;
@@ -727,6 +765,9 @@
if (!gcm->mGCMClassInitialized && gcm->mGameControllerObject != NULL) {
gcm->mApiLevel = env->CallIntMethod(gcm->mGameControllerObject,
gcm->mGetApiLevelMethodId);
+ gcm->mIntegratedSensorFlags = static_cast<uint32_t>(
+ env->CallIntMethod(gcm->mGameControllerObject,
+ gcm->mGetIntegratedSensorMethodId));
// Tell the GCM class we are ready to receive information about
// controllers
@@ -744,10 +785,20 @@
// If a motion data callback is registered, tell the managed side to
// start reporting motion event data
env->CallVoidMethod(gcm->mGameControllerObject,
- gcm->mSetReportMotionEventsMethodId);
+ gcm->mSetReportMotionEventsMethodId);
gcm->mMotionEventReporting = true;
}
+ if (gcm->mActiveSensorFlagsDirty) {
+ // Pass the integrated flags to the managed side, so it can determine whether
+ // to enable or disable listening to an integrated sensor
+ env->CallVoidMethod(
+ gcm->mGameControllerObject, gcm->mSetActiveIntegratedSensorsMethodId,
+ static_cast<jint>(gcm->mMotionDataCallbackFlags));
+ gcm->mActiveSensorFlagsDirty = false;
+ }
+
+
std::lock_guard<std::mutex> lock(gcm->mUpdateMutex);
// Process pending connections/disconnections
@@ -760,7 +811,7 @@
// device, if one does not exist, nullptr is passed and
// GameController will fallback to default axis and button
// mapping.
- const Paddleboat_Controller_Mapping_Data *mapData =
+ const Paddleboat_Controller_Mapping_File_Controller_Entry *mapData =
gcm->getMapForController(gcm->mGameControllers[i]);
#if defined LOG_INPUT_EVENTS
if (mapData != nullptr) {
@@ -787,7 +838,11 @@
->mProductId);
}
#endif
- gcm->mGameControllers[i].setupController(mapData);
+ const uint32_t axisIndex = mapData != nullptr ? mapData->axisTableIndex : 0;
+ const uint32_t buttonIndex = mapData != nullptr ? mapData->buttonTableIndex : 0;
+ gcm->mGameControllers[i].setupController(mapData,
+ &gcm->mMappingInfo.mAxisTable[axisIndex],
+ &gcm->mMappingInfo.mButtonTable[buttonIndex]);
// Update the active axis mask to include any new axis used
// by the new controller
gcm->mActiveAxisMask |=
@@ -947,16 +1002,15 @@
const Paddleboat_LightType lightType) {
bool isSupported = false;
- if (mGameControllerObject != NULL &&
- mSetLightMethodId != NULL) {
+ if (mGameControllerObject != NULL && mSetLightMethodId != NULL) {
if (lightType == PADDLEBOAT_LIGHT_RGB) {
if ((controllerInfo.controllerFlags &
- PADDLEBOAT_CONTROLLER_FLAG_LIGHT_RGB) != 0) {
+ PADDLEBOAT_CONTROLLER_FLAG_LIGHT_RGB) != 0) {
isSupported = true;
}
} else if (lightType == PADDLEBOAT_LIGHT_PLAYER_NUMBER) {
if ((controllerInfo.controllerFlags &
- PADDLEBOAT_CONTROLLER_FLAG_LIGHT_PLAYER) != 0) {
+ PADDLEBOAT_CONTROLLER_FLAG_LIGHT_PLAYER) != 0) {
isSupported = true;
}
}
@@ -979,10 +1033,9 @@
if (gcm->isLightTypeSupported(controllerInfo, lightType)) {
const jint jLightType = static_cast<jint>(lightType);
const jint jLightData = static_cast<jint>(lightData);
- env->CallVoidMethod(gcm->mGameControllerObject,
- gcm->mSetLightMethodId,
- controllerInfo.deviceId, jLightType,
- jLightData);
+ env->CallVoidMethod(
+ gcm->mGameControllerObject, gcm->mSetLightMethodId,
+ controllerInfo.deviceId, jLightType, jLightData);
} else {
errorCode = PADDLEBOAT_ERROR_FEATURE_NOT_SUPPORTED;
}
@@ -1138,21 +1191,27 @@
GameControllerManager *gcm = getInstance();
if (gcm) {
if (gcm->mMotionDataCallback != nullptr) {
- for (size_t i = 0; i < PADDLEBOAT_MAX_CONTROLLERS; ++i) {
- if (gcm->mGameControllers[i].getConnectionIndex() >= 0) {
- const GameControllerDeviceInfo &deviceInfo =
- gcm->mGameControllers[i].getDeviceInfo();
- if (deviceInfo.getInfo().mDeviceId == deviceId) {
- Paddleboat_Motion_Data motionData;
- motionData.motionType =
- static_cast<Paddleboat_Motion_Type>(motionType);
- motionData.timestamp = timestamp;
- motionData.motionX = dataX;
- motionData.motionY = dataY;
- motionData.motionZ = dataZ;
- gcm->mMotionDataCallback(i, &motionData,
- gcm->mMotionDataCallbackUserData);
- return;
+ Paddleboat_Motion_Data motionData;
+ motionData.motionType =
+ static_cast<Paddleboat_Motion_Type>(motionType);
+ motionData.timestamp = timestamp;
+ motionData.motionX = dataX;
+ motionData.motionY = dataY;
+ motionData.motionZ = dataZ;
+
+ if (deviceId == PADDLEBOAT_INTEGRATED_SENSOR_INDEX) {
+ gcm->mMotionDataCallback(PADDLEBOAT_INTEGRATED_SENSOR_INDEX, &motionData,
+ gcm->mMotionDataCallbackUserData);
+ } else {
+ for (size_t i = 0; i < PADDLEBOAT_MAX_CONTROLLERS; ++i) {
+ if (gcm->mGameControllers[i].getConnectionIndex() >= 0) {
+ const GameControllerDeviceInfo &deviceInfo =
+ gcm->mGameControllers[i].getDeviceInfo();
+ if (deviceInfo.getInfo().mDeviceId == deviceId) {
+ gcm->mMotionDataCallback(
+ i, &motionData, gcm->mMotionDataCallbackUserData);
+ return;
+ }
}
}
}
@@ -1160,6 +1219,34 @@
}
}
+bool GameControllerManager::getPhysicalKeyboardStatus() {
+ GameControllerManager *gcm = getInstance();
+ if (gcm) {
+ return gcm->mPhysicalKeyboardConnected;
+ }
+ return false;
+}
+
+void GameControllerManager::onKeyboardConnection(const int32_t /*deviceId*/) {
+ GameControllerManager *gcm = getInstance();
+ if (gcm) {
+ gcm->mPhysicalKeyboardConnected = true;
+ if (gcm->mKeyboardCallback != nullptr) {
+ gcm->mKeyboardCallback(true, gcm->mKeyboardCallbackUserData);
+ }
+ }
+}
+
+void GameControllerManager::onKeyboardDisconnection(const int32_t /*deviceId*/) {
+ GameControllerManager *gcm = getInstance();
+ if (gcm) {
+ gcm->mPhysicalKeyboardConnected = false;
+ if (gcm->mKeyboardCallback != nullptr) {
+ gcm->mKeyboardCallback(false, gcm->mKeyboardCallbackUserData);
+ }
+ }
+}
+
void GameControllerManager::onMouseConnection(const int32_t deviceId) {
GameControllerManager *gcm = getInstance();
if (gcm) {
@@ -1240,13 +1327,22 @@
}
}
-void GameControllerManager::setMotionDataCallback(
- Paddleboat_MotionDataCallback motionDataCallback, void *userData) {
+Paddleboat_ErrorCode GameControllerManager::setMotionDataCallback(
+ Paddleboat_MotionDataCallback motionDataCallback,
+ Paddleboat_Integrated_Motion_Sensor_Flags integratedFlags, void *userData) {
GameControllerManager *gcm = getInstance();
if (gcm) {
+ if ((integratedFlags & gcm->mIntegratedSensorFlags) != integratedFlags) {
+ // Return an error if requesting an unavailable integrated sensor
+ return PADDLEBOAT_ERROR_FEATURE_NOT_SUPPORTED;
+ }
gcm->mMotionDataCallback = motionDataCallback;
gcm->mMotionDataCallbackUserData = userData;
+ gcm->mMotionDataCallbackFlags = integratedFlags;
+ // Mark to call the managed side with the updated flags on the next update
+ gcm->mActiveSensorFlagsDirty = true;
}
+ return PADDLEBOAT_NO_ERROR;
}
void GameControllerManager::setMouseStatusCallback(
@@ -1258,6 +1354,24 @@
}
}
+void GameControllerManager::setPhysicalKeyboardStatusCallback(
+ Paddleboat_PhysicalKeyboardStatusCallback statusCallback, void *userData) {
+ GameControllerManager *gcm = getInstance();
+ if (gcm) {
+ gcm->mKeyboardCallback = statusCallback;
+ gcm->mKeyboardCallbackUserData = userData;
+ }
+}
+
+Paddleboat_Integrated_Motion_Sensor_Flags GameControllerManager::getIntegratedMotionSensorFlags() {
+ Paddleboat_Integrated_Motion_Sensor_Flags flags = PADDLEBOAT_INTEGRATED_SENSOR_NONE;
+ GameControllerManager *gcm = getInstance();
+ if (gcm) {
+ flags = static_cast<Paddleboat_Integrated_Motion_Sensor_Flags>(gcm->mIntegratedSensorFlags);
+ }
+ return flags;
+}
+
jclass GameControllerManager::getGameControllerClass() {
GameControllerManager *gcm = getInstance();
if (!gcm) {
@@ -1344,26 +1458,24 @@
for (size_t i = 0; i < PADDLEBOAT_MAX_CONTROLLERS; ++i) {
if (mGameControllers[i].getControllerStatus() ==
PADDLEBOAT_CONTROLLER_ACTIVE) {
- const Paddleboat_Controller_Info& controllerInfo =
- mGameControllers[i].getControllerInfo();
+ const Paddleboat_Controller_Info &controllerInfo =
+ mGameControllers[i].getControllerInfo();
if ((controllerInfo.controllerFlags &
- PADDLEBOAT_CONTROLLER_FLAG_BATTERY) != 0) {
- Paddleboat_Controller_Data& controllerData =
- mGameControllers[i].getControllerData();
+ PADDLEBOAT_CONTROLLER_FLAG_BATTERY) != 0) {
+ Paddleboat_Controller_Data &controllerData =
+ mGameControllers[i].getControllerData();
const jint deviceId = controllerInfo.deviceId;
jfloat batteryLevel = env->CallFloatMethod(
- mGameControllerObject,
- mGetBatteryLevelMethodId,
- deviceId);
- jint batteryStatus = env->CallIntMethod(
- mGameControllerObject,
- mGetBatteryStatusMethodId,
- deviceId);
+ mGameControllerObject, mGetBatteryLevelMethodId,
+ deviceId);
+ jint batteryStatus =
+ env->CallIntMethod(mGameControllerObject,
+ mGetBatteryStatusMethodId, deviceId);
controllerData.battery.batteryLevel = batteryLevel;
// Java 'enum' starts at 1, not 0.
controllerData.battery.batteryStatus =
- static_cast<Paddleboat_BatteryStatus>(
- batteryStatus - 1);
+ static_cast<Paddleboat_BatteryStatus>(batteryStatus -
+ 1);
}
}
}
@@ -1380,70 +1492,87 @@
mMouseData.timestamp = static_cast<uint64_t>(timestamp);
}
-void GameControllerManager::addControllerRemapData(
- const Paddleboat_Remap_Addition_Mode addMode,
- const int32_t remapTableEntryCount,
- const Paddleboat_Controller_Mapping_Data *mappingData) {
+Paddleboat_ErrorCode GameControllerManager::addControllerRemapDataFromFd(
+ const Paddleboat_Remap_Addition_Mode addMode, const int fileDescriptor) {
+ Paddleboat_ErrorCode result = PADDLEBOAT_ERROR_NOT_INITIALIZED;
GameControllerManager *gcm = getInstance();
if (gcm) {
- if (addMode == PADDLEBOAT_REMAP_ADD_MODE_REPLACE_ALL) {
- gcm->mRemapEntryCount =
- (remapTableEntryCount < MAX_REMAP_TABLE_SIZE)
- ? remapTableEntryCount
- : MAX_REMAP_TABLE_SIZE;
- const size_t copySize = gcm->mRemapEntryCount *
- sizeof(Paddleboat_Controller_Mapping_Data);
- memcpy(gcm->mMappingTable, mappingData, copySize);
- } else if (addMode == PADDLEBOAT_REMAP_ADD_MODE_DEFAULT) {
- for (int32_t i = 0; i < remapTableEntryCount; ++i) {
- MappingTableSearch mapSearch(&gcm->mMappingTable[0],
- gcm->mRemapEntryCount);
- mapSearch.initSearchParameters(
- mappingData[i].vendorId, mappingData[i].productId,
- mappingData[i].minimumEffectiveApiLevel,
- mappingData[i].maximumEffectiveApiLevel);
- GameControllerMappingUtils::findMatchingMapEntry(&mapSearch);
- bool success = GameControllerMappingUtils::insertMapEntry(
- &mappingData[i], &mapSearch);
- if (!success) {
- break;
+ struct stat fileStat;
+ // assume file i/o failed unless we reach addControllerRemapDataFromFileBuffer, which
+ // returns its own error code result
+ result = PADDLEBOAT_ERROR_FILE_IO;
+ if (fstat(fileDescriptor, &fileStat) == 0) {
+ const size_t fileSize = static_cast<size_t>(fileStat.st_size);
+ void *fileBuffer = malloc(fileStat.st_size);
+ if (fileBuffer != nullptr) {
+ if (read(fileDescriptor, fileBuffer, fileSize) == static_cast<ssize_t>(fileSize)) {
+ result = GameControllerManager::addControllerRemapDataFromFileBuffer(addMode,
+ reinterpret_cast<const Paddleboat_Controller_Mapping_File_Header *>
+ (fileBuffer), fileSize);
}
- gcm->mRemapEntryCount += 1;
+ free(fileBuffer);
}
}
}
+ return result;
+}
+
+Paddleboat_ErrorCode GameControllerManager::addControllerRemapDataFromFileBuffer(
+ const Paddleboat_Remap_Addition_Mode addMode,
+ const Paddleboat_Controller_Mapping_File_Header *mappingFileHeader,
+ const size_t mappingFileBufferSize) {
+ Paddleboat_ErrorCode result = PADDLEBOAT_ERROR_NOT_INITIALIZED;
+ GameControllerManager *gcm = getInstance();
+ if (gcm) {
+ result = GameControllerMappingUtils::validateMapFile(mappingFileHeader,
+ mappingFileBufferSize);
+ if (result == PADDLEBOAT_NO_ERROR) {
+ switch (addMode) {
+ case PADDLEBOAT_REMAP_ADD_MODE_DEFAULT:
+ result = GameControllerMappingUtils::mergeControllerRemapData(
+ mappingFileHeader, mappingFileBufferSize, gcm->mMappingInfo);
+ break;
+ case PADDLEBOAT_REMAP_ADD_MODE_REPLACE_ALL:
+ result = GameControllerMappingUtils::overwriteControllerRemapData(
+ mappingFileHeader, mappingFileBufferSize, gcm->mMappingInfo);
+ break;
+ }
+ }
+ }
+ return result;
+}
+
+void GameControllerManager::addControllerRemapData(
+ const Paddleboat_Remap_Addition_Mode /*addMode*/,
+ const int32_t /*remapTableEntryCount*/,
+ const Paddleboat_Controller_Mapping_Data */*mappingData*/) {
+ /*
+ * Paddleboat 1.2 deprecated this function in favor of addControllerRemapDataFromFileBuffer
+ * and addControllerRemapDataFromFd. The new functions use a more efficient system with
+ * additional features.
+ */
}
int32_t GameControllerManager::getControllerRemapTableData(
- const int32_t destRemapTableEntryCount,
- Paddleboat_Controller_Mapping_Data *mappingData) {
- GameControllerManager *gcm = getInstance();
- if (!gcm) {
- return 0;
- }
- if (mappingData != nullptr) {
- size_t copySize = (gcm->mRemapEntryCount < destRemapTableEntryCount)
- ? gcm->mRemapEntryCount
- : destRemapTableEntryCount;
- copySize *= sizeof(Paddleboat_Controller_Mapping_Data);
- memcpy(mappingData, gcm->mMappingTable, copySize);
- }
- return gcm->mRemapEntryCount;
+ const int32_t /*destRemapTableEntryCount*/,
+ Paddleboat_Controller_Mapping_Data */*mappingData*/) {
+ return 0;
}
-const Paddleboat_Controller_Mapping_Data *
-GameControllerManager::getMapForController(
- const GameController &gameController) {
- const Paddleboat_Controller_Mapping_Data *returnMap = nullptr;
+const Paddleboat_Controller_Mapping_File_Controller_Entry *
+GameControllerManager::getMapForController(const GameController &gameController) {
+ const Paddleboat_Controller_Mapping_File_Controller_Entry *returnEntry = nullptr;
const GameControllerDeviceInfo &deviceInfo = gameController.getDeviceInfo();
- MappingTableSearch mapSearch(&mMappingTable[0], mRemapEntryCount);
+ MappingTableSearch mapSearch(&mMappingInfo.mControllerTable[0],
+ mMappingInfo.mControllerTableEntryCount);
mapSearch.initSearchParameters(deviceInfo.getInfo().mVendorId,
deviceInfo.getInfo().mProductId, mApiLevel,
mApiLevel);
bool success = GameControllerMappingUtils::findMatchingMapEntry(&mapSearch);
if (success) {
- returnMap = &mapSearch.mappingRoot[mapSearch.tableIndex];
+ returnEntry = &mapSearch.mappingRoot[mapSearch.tableIndex];
}
- return returnMap;
+ return returnEntry;
}
+
} // namespace paddleboat
diff --git a/GameController/src/main/cpp/GameControllerManager.h b/games-controller/src/main/cpp/GameControllerManager.h
similarity index 81%
rename from GameController/src/main/cpp/GameControllerManager.h
rename to games-controller/src/main/cpp/GameControllerManager.h
index 764cb6d..90e22a8 100644
--- a/GameController/src/main/cpp/GameControllerManager.h
+++ b/games-controller/src/main/cpp/GameControllerManager.h
@@ -22,6 +22,7 @@
#include <mutex>
#include "GameController.h"
+#include "GameControllerMappingInfo.h"
#include "ThreadUtil.h"
namespace paddleboat {
@@ -35,7 +36,6 @@
static constexpr int32_t MAX_MOUSE_DEVICES = 2;
static constexpr int32_t INVALID_MOUSE_ID = -1;
- static constexpr int32_t MAX_REMAP_TABLE_SIZE = 256;
// Assuming update is getting called at 60Hz, wait one minute in between
// checking battery status
@@ -46,14 +46,16 @@
~GameControllerManager();
- static inline int32_t getRemapTableSize() { return MAX_REMAP_TABLE_SIZE; }
-
static Paddleboat_ErrorCode init(JNIEnv *env, jobject jcontext);
static void destroyInstance(JNIEnv *env);
static bool isInitialized();
+ static bool getPhysicalKeyboardStatus();
+
+ static Paddleboat_Integrated_Motion_Sensor_Flags getIntegratedMotionSensorFlags();
+
// Get/Set whether AKEYCODE_BACK is 'eaten' or allowed to pass through to
// the system This can be used to block the OS backing out of the game, or
// allowing it if the game is in an appropriate state (i.e. the title
@@ -69,12 +71,16 @@
static void setControllerStatusCallback(
Paddleboat_ControllerStatusCallback statusCallback, void *userData);
- static void setMotionDataCallback(
- Paddleboat_MotionDataCallback motionDataCallback, void *userData);
+ static Paddleboat_ErrorCode setMotionDataCallback(
+ Paddleboat_MotionDataCallback motionDataCallback,
+ Paddleboat_Integrated_Motion_Sensor_Flags integratedFlags, void *userData);
static void setMouseStatusCallback(
Paddleboat_MouseStatusCallback statusCallback, void *userData);
+ static void setPhysicalKeyboardStatusCallback(
+ Paddleboat_PhysicalKeyboardStatusCallback statusCallback, void *userData);
+
static void onStop(JNIEnv *env);
static void onStart(JNIEnv *env);
@@ -122,11 +128,24 @@
const int32_t destRemapTableEntryCount,
Paddleboat_Controller_Mapping_Data *mappingData);
+ static Paddleboat_ErrorCode addControllerRemapDataFromFd(
+ const Paddleboat_Remap_Addition_Mode addMode,
+ const int fileDescriptor);
+
+ static Paddleboat_ErrorCode addControllerRemapDataFromFileBuffer(
+ const Paddleboat_Remap_Addition_Mode addMode,
+ const Paddleboat_Controller_Mapping_File_Header *mappingFileHeader,
+ const size_t mappingFileBufferSize);
+
// Called from the JNI bridge functions
static GameControllerDeviceInfo *onConnection();
static void onDisconnection(const int32_t deviceId);
+ static void onKeyboardConnection(const int32_t deviceId);
+
+ static void onKeyboardDisconnection(const int32_t deviceId);
+
static void onMotionData(const int32_t deviceId, const int32_t motionType,
const uint64_t timestamp, const float dataX,
const float dataY, const float dataZ);
@@ -171,16 +190,19 @@
void releaseGlobals(JNIEnv *env);
- const Paddleboat_Controller_Mapping_Data *getMapForController(
+ const Paddleboat_Controller_Mapping_File_Controller_Entry *getMapForController(
const GameController &gameController);
bool mInitialized = false;
bool mGCMClassInitialized = false;
+ bool mActiveSensorFlagsDirty = false;
bool mBackButtonConsumed = true;
bool mMotionEventReporting = false;
+ bool mPhysicalKeyboardConnected = false;
int32_t mApiLevel = 16;
int32_t mBatteryWait = BATTERY_REFRESH_WAIT;
+ uint32_t mIntegratedSensorFlags = 0;
jobject mContext = NULL;
jclass mGameControllerClass = NULL;
jobject mGameControllerObject = NULL;
@@ -188,6 +210,8 @@
jmethodID mGetApiLevelMethodId = NULL;
jmethodID mGetBatteryLevelMethodId = NULL;
jmethodID mGetBatteryStatusMethodId = NULL;
+ jmethodID mGetIntegratedSensorMethodId = NULL;
+ jmethodID mSetActiveIntegratedSensorsMethodId = NULL;
jmethodID mSetLightMethodId = NULL;
jmethodID mSetNativeReadyMethodId = NULL;
jmethodID mSetReportMotionEventsMethodId = NULL;
@@ -195,11 +219,12 @@
uint64_t mActiveAxisMask = 0;
- int32_t mRemapEntryCount = 0;
- Paddleboat_Controller_Mapping_Data mMappingTable[MAX_REMAP_TABLE_SIZE];
+ GameControllerMappingInfo mMappingInfo;
- Paddleboat_MotionDataCallback mMotionDataCallback = nullptr;
+ Paddleboat_MotionDataCallback mMotionDataCallback = nullptr;
void *mMotionDataCallbackUserData = nullptr;
+ Paddleboat_Integrated_Motion_Sensor_Flags mMotionDataCallbackFlags =
+ PADDLEBOAT_INTEGRATED_SENSOR_NONE;
GameController mGameControllers[PADDLEBOAT_MAX_CONTROLLERS];
Paddleboat_ControllerStatusCallback mStatusCallback = nullptr;
@@ -207,6 +232,9 @@
// device debug helper
int32_t mLastKeyEventKeyCode = 0;
+ Paddleboat_PhysicalKeyboardStatusCallback mKeyboardCallback = nullptr;
+ void *mKeyboardCallbackUserData = nullptr;
+
Paddleboat_MouseStatus mMouseStatus = PADDLEBOAT_MOUSE_NONE;
int32_t mMouseDeviceIds[MAX_MOUSE_DEVICES] = {INVALID_MOUSE_ID,
INVALID_MOUSE_ID};
diff --git a/games-controller/src/main/cpp/GameControllerMappingFile.h b/games-controller/src/main/cpp/GameControllerMappingFile.h
new file mode 100644
index 0000000..96d617b
--- /dev/null
+++ b/games-controller/src/main/cpp/GameControllerMappingFile.h
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include "paddleboat.h"
+
+namespace paddleboat {
+
+/**
+ * @brief A structure that describes the header of a PaddleboatMappingTool
+ * configuration file. The ::Paddleboat_addControllerRemapDataFromFileBuffer
+ * function accepts this file data as an input to modify the internal
+ * controller mapping database.
+ * See ::Paddleboat_addControllerRemapDataFromFileBuffer
+ * for links to documentation on controller remapping configuration.
+ */
+typedef struct __attribute__((packed)) Paddleboat_Controller_Mapping_File_Header {
+ /** @brief Identifier value for the file, expected to be equal
+ * to the `PADDLEBOAT_MAPPING_FILE_IDENTIFIER` constant.
+ */
+ uint32_t fileIdentifier;
+ /** @brief Semantic version number of the earliest version
+ * of the Paddleboat library that is compatible with this file.
+ * (i.e. 0x010200 for 1.2.0)
+ */
+ uint32_t libraryMinimumVersion;
+ /** @brief The number of axis remapping table entries present in the file. The
+ * axis remapping table is a sequential array of
+ * `Paddleboat_Controller_Mapping_File_Axis_Entry` structures starting
+ * at `axisTableOffset` bytes from the beginning of this header.
+ */
+ uint32_t axisTableEntryCount;
+ /** @brief The number of button remapping table entries present in the file. The
+ * button remapping table is a sequential array of
+ * `Paddleboat_Controller_Mapping_File_Button_Entry` structures starting
+ * at `buttonTableOffset` bytes from the beginning of this header.
+ */
+ uint32_t buttonTableEntryCount;
+ /** @brief The number of controller mapping definition entries present
+ * in the file. The controller mapping definition table is a sequential array of
+ * `Paddleboat_Controller_Mapping_File_Controller_Entry` structures starting
+ * at `controllerTableOffset` bytes from the beginning of this header.
+ */
+ uint32_t controllerTableEntryCount;
+ /** @brief The number of string table entries present in the file.
+ * The string table is a sequential array of
+ * `Paddleboat_Controller_Mapping_File_String_Entry` structures starting
+ * at `stringTableOffset` bytes from the beginning of this header.
+ */
+ uint32_t stringTableEntryCount;
+ /** @brief The offset in bytes from the start of the header to the first
+ * `Paddleboat_Controller_Mapping_File_Axis_Entry` structure.
+ */
+ uint64_t axisTableOffset;
+ /** @brief The offset in bytes from the start of the header to the first
+ * `Paddleboat_Controller_Mapping_File_Button_Entry` structure.
+ */
+ uint64_t buttonTableOffset;
+ /** @brief The offset in bytes from the start of the header to the first
+ * `Paddleboat_Controller_Mapping_File_Controller_Entry` structure.
+ */
+ uint64_t controllerTableOffset;
+ /** @brief The offset in bytes from the start of the header to the first
+ * `Paddleboat_Controller_Mapping_File_String_Entry` structure.
+ */
+ uint64_t stringTableOffset;
+} Paddleboat_Controller_Mapping_File_Header;
+
+/**
+* @brief A structure that describes the axis mapping for a controller.
+* Axis mapping is used to translate AMOTION_EVENT_AXIS values to
+* PADDLEBOAT_AXIS values. The axis mapping table is also used to
+* specify button activation in response to axis inputs, and
+* optional axis value inversion on a per-axis basis.
+*/
+typedef struct __attribute__((packed)) Paddleboat_Controller_Mapping_File_Axis_Entry {
+ /** @brief Index into the file's string table for the entry
+ * containing the string with the name of this axis configuration.
+ * The name is used for identification and conflict resolution
+ * when updating the controller database with new or replacement entries.
+ */
+ uint32_t axisNameStringTableIndex;
+ /** @brief AMOTION_EVENT_AXIS value for
+ * the corresponding Paddleboat control axis, or PADDLEBOAT_AXIS_IGNORED if
+ * unsupported.
+ */
+ uint16_t axisMapping[PADDLEBOAT_MAPPING_AXIS_COUNT];
+ /** @brief Button to set on
+ * positive axis value, PADDLEBOAT_AXIS_BUTTON_IGNORED if none.
+ */
+ uint8_t axisPositiveButtonMapping[PADDLEBOAT_MAPPING_AXIS_COUNT];
+ /** @brief Button to set on
+ * negative axis value, PADDLEBOAT_AXIS_BUTTON_IGNORED if none.
+ */
+ uint8_t axisNegativeButtonMapping[PADDLEBOAT_MAPPING_AXIS_COUNT];
+ /** @brief Bitmask value, if a bit corresponding to
+ * an axis index (i.e. bit 1 for `PADDLEBOAT_MAPPING_AXIS_LEFTSTICK_Y`
+ * is set, then the incoming axis input will be inverted prior to
+ * reporting. This is to support workarounds for controllers where
+ * the axis values are reversed so pushing 'down' reads as 'up'.
+ */
+ uint32_t axisInversionBitmask;
+} Paddleboat_Controller_Mapping_File_Axis_Entry;
+
+/**
+* @brief A structure that describes the button mapping for a controller.
+* Button mapping is used to translate AKEYCODE values to
+* PADDLEBOAT_BUTTON values.
+*/
+typedef struct __attribute__((packed)) Paddleboat_Controller_Mapping_File_Button_Entry {
+ /** @brief Index into the file's string table for the entry
+ * containing the string with the name of this button configuration.
+ * The name is used for identification and conflict resolution
+ * when updating the controller database with new or replacement entries.
+ */
+ uint32_t buttonNameStringTableIndex;
+ /** @brief AKEYCODE_ value corresponding
+ * with the corresponding Paddleboat button.
+ * PADDLEBOAT_BUTTON_IGNORED if unsupported.
+ */
+ uint16_t buttonMapping[PADDLEBOAT_BUTTON_COUNT];
+} Paddleboat_Controller_Mapping_File_Button_Entry;
+
+/**
+* @brief A structure that defines controller mapping and
+* configuration data for a specific controller device.
+*/
+typedef struct __attribute__((packed)) Paddleboat_Controller_Mapping_File_Controller_Entry {
+ /** @brief Minimum API level required for this entry */
+ int16_t minimumEffectiveApiLevel;
+ /** @brief Maximum API level required for this entry, 0 = no max */
+ int16_t maximumEffectiveApiLevel;
+ /** @brief VendorID of the controller device for this entry */
+ int32_t vendorId;
+ /** @brief ProductID of the controller device for this entry */
+ int32_t productId;
+ /** @brief Flag bits, will be ORed with
+ * `Paddleboat_Controller_Info.controllerFlags` at setup
+ */
+ uint32_t flags;
+ /** @brief index into the array of
+ * `Paddleboat_Controller_Mapping_File_Axis_Entry` structs of the
+ * axis mapping table used by this controller
+ */
+ uint32_t axisTableIndex;
+ /** @brief index into the array of
+ * `Paddleboat_Controller_Mapping_File_Button_Entry` structs of the
+ * button mapping table used by this controller
+ */
+ uint32_t buttonTableIndex;
+ /** @brief Index into the file's string table for the entry
+ * containing the string with optional allowlist filtering
+ * information. If no filter is specified, the string value will be "None".
+ * If an allowlist filter is specified, this controller mapping
+ * will only be added to the database if the runtime filter criteria
+ * are met. See ::Paddleboat_addControllerRemapDataFromFileBuffer
+ * for links to documentation on controller remapping configuration.
+ */
+ uint32_t deviceAllowlistStringTableIndex;
+ /** @brief Index into the file's string table for the entry
+ * containing the string with optional denylist filtering
+ * information. If no filter is specified, the string value will be "None".
+ * If an denylist filter is specified, this controller mapping
+ * will not be added to the database if the runtime filter criteria
+ * are met. See ::Paddleboat_addControllerRemapDataFromFileBuffer
+ * for links to documentation on controller remapping configuration.
+ */
+ uint32_t deviceDenylistStringTableIndex;
+} Paddleboat_Controller_Mapping_File_Controller_Entry;
+
+/**
+* @brief A structure that defines a string table entry
+* in the PaddleboatMappingTool file.
+*/
+typedef struct __attribute__((packed)) Paddleboat_Controller_Mapping_File_String_Entry {
+ /** @brief An ASCII string, expected to be 0 terminated.
+ * Must fit, including termination in
+ * `PADDLEBOAT_STRING_TABLE_ENTRY_MAX_SIZE`
+ */
+ char stringTableEntry[PADDLEBOAT_STRING_TABLE_ENTRY_MAX_SIZE];
+} Paddleboat_Controller_Mapping_File_String_Entry;
+
+} // namespace paddleboat
diff --git a/games-controller/src/main/cpp/GameControllerMappingInfo.h b/games-controller/src/main/cpp/GameControllerMappingInfo.h
new file mode 100644
index 0000000..16561d7
--- /dev/null
+++ b/games-controller/src/main/cpp/GameControllerMappingInfo.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <memory>
+
+#include "paddleboat.h"
+#include "GameControllerMappingFile.h"
+
+namespace paddleboat {
+
+class GameControllerMappingInfo {
+public:
+ // Internal new table size limits
+ static constexpr int32_t MAX_CONTROLLER_TABLE_SIZE = 256;
+ static constexpr int32_t MAX_BUTTON_TABLE_SIZE = 32;
+ static constexpr int32_t MAX_AXIS_TABLE_SIZE = 32;
+ static constexpr int32_t MAX_STRING_TABLE_SIZE = 128;
+
+ GameControllerMappingInfo() {
+ memset(mAxisTable, 0, sizeof(mAxisTable));
+ memset(mButtonTable, 0, sizeof(mButtonTable));
+ memset(mControllerTable, 0, sizeof(mControllerTable));
+ memset(mStringTable, 0, sizeof(mStringTable));
+ }
+
+ uint32_t mAxisTableEntryCount = 0;
+ uint32_t mButtonTableEntryCount = 0;
+ uint32_t mControllerTableEntryCount = 0;
+ uint32_t mStringTableEntryCount = 0;
+ Paddleboat_Controller_Mapping_File_Axis_Entry mAxisTable[MAX_AXIS_TABLE_SIZE];
+ Paddleboat_Controller_Mapping_File_Button_Entry mButtonTable[MAX_BUTTON_TABLE_SIZE];
+ Paddleboat_Controller_Mapping_File_Controller_Entry mControllerTable[MAX_CONTROLLER_TABLE_SIZE];
+ Paddleboat_Controller_Mapping_File_String_Entry mStringTable[MAX_STRING_TABLE_SIZE];
+};
+
+}
diff --git a/games-controller/src/main/cpp/GameControllerMappingUtils.cpp b/games-controller/src/main/cpp/GameControllerMappingUtils.cpp
new file mode 100644
index 0000000..87b0f60
--- /dev/null
+++ b/games-controller/src/main/cpp/GameControllerMappingUtils.cpp
@@ -0,0 +1,535 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "GameControllerMappingUtils.h"
+
+#include "GameControllerManager.h"
+
+extern "C" {
+uint32_t Paddleboat_getVersion();
+}
+
+namespace paddleboat {
+
+MappingTableSearch::MappingTableSearch()
+ : mappingRoot(nullptr),
+ vendorId(0),
+ productId(0),
+ minApi(0),
+ maxApi(0),
+ tableIndex(0),
+ mapEntryCount(0),
+ tableEntryCount(0),
+ tableMaxEntryCount(GameControllerMappingInfo::MAX_CONTROLLER_TABLE_SIZE) {}
+
+MappingTableSearch::MappingTableSearch(
+ Paddleboat_Controller_Mapping_File_Controller_Entry *mapRoot, int32_t entryCount)
+ : mappingRoot(mapRoot),
+ vendorId(0),
+ productId(0),
+ minApi(0),
+ maxApi(0),
+ tableIndex(0),
+ mapEntryCount(0),
+ tableEntryCount(entryCount),
+ tableMaxEntryCount(GameControllerMappingInfo::MAX_CONTROLLER_TABLE_SIZE) {}
+
+void MappingTableSearch::initSearchParameters(const int32_t newVendorId,
+ const int32_t newProductId,
+ const int32_t newMinApi,
+ const int32_t newMaxApi) {
+ vendorId = newVendorId;
+ productId = newProductId;
+ minApi = newMinApi;
+ maxApi = newMaxApi;
+ tableIndex = 0;
+}
+
+bool GameControllerMappingUtils::findMatchingMapEntry(
+ MappingTableSearch *searchEntry) {
+ int32_t currentIndex = 0;
+
+ // Starting out with a linear search. Updating the map table is something
+ // that should only ever be done once at startup, if it actually takes an
+ // appreciable time to execute when working with a big remap dictionary,
+ // this is low-hanging fruit to optimize.
+ const Paddleboat_Controller_Mapping_File_Controller_Entry *mapRoot =
+ searchEntry->mappingRoot;
+ while (currentIndex < searchEntry->tableEntryCount) {
+ const Paddleboat_Controller_Mapping_File_Controller_Entry &mapEntry =
+ mapRoot[currentIndex];
+ if (mapEntry.vendorId > searchEntry->vendorId) {
+ // Passed by the search vendorId value, so we don't already exist in
+ // the table, set the current index as the insert point and bail
+ searchEntry->tableIndex = currentIndex;
+ return false;
+ } else if (searchEntry->vendorId == mapEntry.vendorId) {
+ if (mapEntry.productId > searchEntry->productId) {
+ // Passed by the search productId value, so we don't already
+ // exist in the table, set the current index as the insert point
+ // and bail
+ searchEntry->tableIndex = currentIndex;
+ return false;
+ } else if (searchEntry->productId == mapEntry.productId) {
+ // Any overlap of the min/max API range is treated as matching
+ // an existing entry
+ if ((searchEntry->minApi >= mapEntry.minimumEffectiveApiLevel &&
+ searchEntry->minApi <=
+ mapEntry.maximumEffectiveApiLevel) ||
+ (searchEntry->minApi >= mapEntry.minimumEffectiveApiLevel &&
+ mapEntry.maximumEffectiveApiLevel == 0)) {
+ searchEntry->tableIndex = currentIndex;
+ return true;
+ }
+ }
+ }
+ ++currentIndex;
+ }
+ searchEntry->tableIndex = currentIndex;
+ return false;
+}
+
+Paddleboat_ErrorCode GameControllerMappingUtils::insertMapEntry(
+ const Paddleboat_Controller_Mapping_File_Controller_Entry *mappingData,
+ MappingTableSearch *searchEntry,
+ const IndexTableRemap *axisRemapTable,
+ const IndexTableRemap *buttonRemapTable) {
+ Paddleboat_ErrorCode result = PADDLEBOAT_ERROR_FEATURE_NOT_SUPPORTED;
+ bool doCopy = false;
+
+ // If min/max match exactly on the same device, just replace inline instead of inserting
+ // otherwise verify there is room in the table for another entry
+ Paddleboat_Controller_Mapping_File_Controller_Entry &indexEntry =
+ searchEntry->mappingRoot[searchEntry->tableIndex];
+ const bool sameDevice = (mappingData->productId == indexEntry.productId &&
+ mappingData->vendorId == indexEntry.vendorId);
+ if (sameDevice &&
+ mappingData->minimumEffectiveApiLevel == indexEntry.minimumEffectiveApiLevel &&
+ mappingData->maximumEffectiveApiLevel == indexEntry.maximumEffectiveApiLevel) {
+ doCopy = true;
+ } else if (searchEntry->tableEntryCount < searchEntry->tableMaxEntryCount &&
+ searchEntry->tableIndex < searchEntry->tableMaxEntryCount) {
+ // Check for min/max API 'overlap' between the new entry and the insert point on the
+ // same device, we may need to 'patch' the max API of the insert point and do the actual
+ // insert one index higher.
+ if (sameDevice &&
+ ((mappingData->minimumEffectiveApiLevel >= indexEntry.minimumEffectiveApiLevel &&
+ mappingData->minimumEffectiveApiLevel <= indexEntry.maximumEffectiveApiLevel) ||
+ (mappingData->minimumEffectiveApiLevel >= indexEntry.minimumEffectiveApiLevel &&
+ indexEntry.maximumEffectiveApiLevel == 0))) {
+ indexEntry.maximumEffectiveApiLevel = mappingData->minimumEffectiveApiLevel - 1;
+ searchEntry->tableIndex += 1;
+ }
+
+ // Empty table, or inserting at the end, no relocation of existing data
+ // required, otherwise shift existing data down starting at the insert
+ // index.
+ if (!(searchEntry->tableEntryCount == 0 ||
+ searchEntry->tableIndex == searchEntry->tableEntryCount)) {
+ const size_t copySize =
+ (searchEntry->tableEntryCount - searchEntry->tableIndex) *
+ sizeof(Paddleboat_Controller_Mapping_File_Controller_Entry);
+ memmove(&searchEntry->mappingRoot[searchEntry->tableIndex + 1],
+ &searchEntry->mappingRoot[searchEntry->tableIndex],
+ copySize);
+ }
+ doCopy = true;
+ }
+ if (doCopy) {
+ // Copy the new data
+ Paddleboat_Controller_Mapping_File_Controller_Entry *newEntry =
+ &searchEntry->mappingRoot[searchEntry->tableIndex];
+ memcpy(newEntry, mappingData,
+ sizeof(Paddleboat_Controller_Mapping_File_Controller_Entry));
+ // Fix up the axis and button indices using the remap tables
+ const uint32_t newAxisIndex = axisRemapTable[newEntry->axisTableIndex].newIndex;
+ newEntry->axisTableIndex = newAxisIndex;
+ const uint32_t newButtonIndex = buttonRemapTable[newEntry->buttonTableIndex].newIndex;
+ newEntry->buttonTableIndex = newButtonIndex;
+ result = PADDLEBOAT_NO_ERROR;
+ }
+ return result;
+}
+
+const Paddleboat_Controller_Mapping_File_Controller_Entry *
+ GameControllerMappingUtils::validateMapTable(
+ const Paddleboat_Controller_Mapping_File_Controller_Entry *mappingRoot,
+ const int32_t tableEntryCount) {
+ // The map table is always assumed to be sorted by increasing vendorId. Each
+ // sequence of entries with the same vendorId are sorted by increasing
+ // productId. Multiple entries with the same vendorId/productId range are
+ // sorted by increasing min/max API ranges.
+ // vendorId
+ // productId
+ // minApi
+ int32_t currentIndex = 0;
+ int32_t previousVendorId = -1;
+ while (currentIndex < tableEntryCount) {
+ if (mappingRoot[currentIndex].vendorId < previousVendorId) {
+ // failure in vendorId order, return the offending entry
+ return &mappingRoot[currentIndex];
+ }
+
+ int32_t previousProductId = mappingRoot[currentIndex].productId;
+ int32_t previousMinApi =
+ mappingRoot[currentIndex].minimumEffectiveApiLevel;
+ int32_t previousMaxApi =
+ mappingRoot[currentIndex].maximumEffectiveApiLevel;
+ previousVendorId = mappingRoot[currentIndex++].vendorId;
+
+ while (currentIndex < tableEntryCount &&
+ mappingRoot[currentIndex].vendorId == previousVendorId) {
+ while (currentIndex < tableEntryCount &&
+ mappingRoot[currentIndex].productId == previousProductId) {
+ if (mappingRoot[currentIndex].minimumEffectiveApiLevel <
+ previousMinApi ||
+ mappingRoot[currentIndex].minimumEffectiveApiLevel <
+ previousMaxApi) {
+ // failure in API order, return the offending entry
+ return &mappingRoot[currentIndex];
+ }
+ previousMinApi =
+ mappingRoot[currentIndex].minimumEffectiveApiLevel;
+ previousMaxApi =
+ mappingRoot[currentIndex++].maximumEffectiveApiLevel;
+ }
+ if (mappingRoot[currentIndex].productId < previousProductId) {
+ // failure in productId order, return the offending entry
+ return &mappingRoot[currentIndex];
+ }
+ previousProductId = mappingRoot[currentIndex++].productId;
+ }
+ }
+
+ // Validation success, return nullptr (no offending entries to return)
+ return nullptr;
+}
+
+Paddleboat_ErrorCode GameControllerMappingUtils::validateMapFile(
+ const Paddleboat_Controller_Mapping_File_Header *mappingFileHeader,
+ const size_t mappingFileBufferSize) {
+ if (mappingFileHeader->fileIdentifier != PADDLEBOAT_MAPPING_FILE_IDENTIFIER) {
+ return PADDLEBOAT_INVALID_MAPPING_DATA;
+ }
+ if (mappingFileHeader->libraryMinimumVersion > Paddleboat_getVersion()) {
+ return PADDLEBOAT_INCOMPATIBLE_MAPPING_DATA;
+ }
+
+ // Must have at least one entry in each table
+ if (mappingFileHeader->axisTableEntryCount == 0 ||
+ mappingFileHeader->buttonTableEntryCount == 0 ||
+ mappingFileHeader->controllerTableEntryCount == 0 ||
+ mappingFileHeader->stringTableEntryCount == 0) {
+ return PADDLEBOAT_INVALID_MAPPING_DATA;
+ }
+ // Bounds check against specified buffer size, ensure all internal data
+ // ranges fit in the buffer
+ if (mappingFileBufferSize < sizeof(Paddleboat_Controller_Mapping_File_Header)) {
+ return PADDLEBOAT_INVALID_MAPPING_DATA;
+ }
+
+ const uint8_t* fileStart = reinterpret_cast<const uint8_t*>(mappingFileHeader);
+ const ptrdiff_t maxSize = mappingFileBufferSize;
+
+ const uint8_t *endAxis = fileStart + mappingFileHeader->axisTableOffset +
+ (mappingFileHeader->axisTableEntryCount *
+ sizeof(Paddleboat_Controller_Mapping_File_Axis_Entry));
+ if ((endAxis - fileStart) > maxSize) {
+ return PADDLEBOAT_INVALID_MAPPING_DATA;
+ }
+
+ const uint8_t *endButton = fileStart + mappingFileHeader->buttonTableOffset +
+ (mappingFileHeader->buttonTableEntryCount *
+ sizeof(Paddleboat_Controller_Mapping_File_Button_Entry));
+ if ((endButton - fileStart) > maxSize) {
+ return PADDLEBOAT_INVALID_MAPPING_DATA;
+ }
+
+ const uint8_t *endString = fileStart + mappingFileHeader->stringTableOffset +
+ (mappingFileHeader->stringTableEntryCount *
+ sizeof(Paddleboat_Controller_Mapping_File_String_Entry));
+ if ((endString - fileStart) > maxSize) {
+ return PADDLEBOAT_INVALID_MAPPING_DATA;
+ }
+ return PADDLEBOAT_NO_ERROR;
+}
+
+Paddleboat_ErrorCode GameControllerMappingUtils::mergeStringTable(
+ const Paddleboat_Controller_Mapping_File_String_Entry *newStrings,
+ const uint32_t newStringCount,
+ Paddleboat_Controller_Mapping_File_String_Entry *stringEntries,
+ uint32_t *stringEntryCount,
+ const uint32_t maxStringEntryCount,
+ IndexTableRemap *remapTable) {
+ Paddleboat_ErrorCode result = PADDLEBOAT_NO_ERROR;
+
+ // Check to see if a string in the incoming table already exists in the
+ // existing table or if it needs to be added
+ const uint32_t existingStringCount = *stringEntryCount;
+ uint32_t currentStringCount = existingStringCount;
+ for (uint32_t newIndex = 0; newIndex < newStringCount; ++newIndex) {
+ bool foundExistingMatch = false;
+ for (uint32_t existingIndex = 0; existingIndex < existingStringCount; ++existingIndex) {
+ if (strncmp(newStrings[newIndex].stringTableEntry,
+ stringEntries[existingIndex].stringTableEntry,
+ PADDLEBOAT_STRING_TABLE_ENTRY_MAX_SIZE) == 0) {
+ remapTable[newIndex].newIndex = existingIndex;
+ foundExistingMatch = true;
+ break;
+ }
+ }
+ if (!foundExistingMatch) {
+ if (currentStringCount >= maxStringEntryCount) {
+ // Return error if out of room in string table
+ result = PADDLEBOAT_ERROR_FEATURE_NOT_SUPPORTED;
+ break;
+ }
+ memcpy(&stringEntries[currentStringCount], &newStrings[newIndex],
+ sizeof(Paddleboat_Controller_Mapping_File_String_Entry));
+ remapTable[newIndex].newIndex = currentStringCount;
+ currentStringCount += 1;
+ *stringEntryCount = currentStringCount;
+ }
+ }
+ return result;
+}
+
+Paddleboat_ErrorCode GameControllerMappingUtils::mergeAxisTable(
+ const Paddleboat_Controller_Mapping_File_Axis_Entry *newAxis,
+ const uint32_t newAxisCount,
+ Paddleboat_Controller_Mapping_File_Axis_Entry *axisEntries,
+ uint32_t *axisEntryCount,
+ const uint32_t maxAxisEntryCount,
+ const IndexTableRemap *stringRemapTable,
+ IndexTableRemap *axisRemapTable) {
+ Paddleboat_ErrorCode result = PADDLEBOAT_NO_ERROR;
+
+ // Check if an axis table already exists in the existing table or if it needs to be added.
+ // Matching is done by name, so we have to use the string table via the remapped string index
+ const uint32_t existingAxisCount = *axisEntryCount;
+ uint32_t currentAxisCount = existingAxisCount;
+
+ for (uint32_t newIndex = 0; newIndex < newAxisCount; ++newIndex) {
+ bool foundExistingMatch = false;
+ const uint32_t newAxisStringTableIndex =
+ stringRemapTable[newAxis[newIndex].axisNameStringTableIndex].newIndex;
+ for (uint32_t existingIndex = 0; existingIndex < existingAxisCount; ++existingIndex) {
+ if (axisEntries[existingIndex].axisNameStringTableIndex == newAxisStringTableIndex) {
+ axisRemapTable[newIndex].newIndex = existingIndex;
+ foundExistingMatch = true;
+ break;
+ }
+ }
+ if (!foundExistingMatch) {
+ if (currentAxisCount >= maxAxisEntryCount) {
+ // Return error if out of room in axis table
+ result = PADDLEBOAT_ERROR_FEATURE_NOT_SUPPORTED;
+ break;
+ }
+ memcpy(&axisEntries[currentAxisCount], &newAxis[newIndex],
+ sizeof(Paddleboat_Controller_Mapping_File_Axis_Entry));
+ axisEntries[currentAxisCount].axisNameStringTableIndex = newAxisStringTableIndex;
+ axisRemapTable[newIndex].newIndex = currentAxisCount;
+ currentAxisCount += 1;
+ *axisEntryCount = currentAxisCount;
+ }
+ }
+ return result;
+}
+
+Paddleboat_ErrorCode GameControllerMappingUtils::mergeButtonTable(
+ const Paddleboat_Controller_Mapping_File_Button_Entry *newButton,
+ const uint32_t newButtonCount,
+ Paddleboat_Controller_Mapping_File_Button_Entry *buttonEntries,
+ uint32_t *buttonEntryCount,
+ const uint32_t maxButtonEntryCount,
+ const IndexTableRemap *stringRemapTable,
+ IndexTableRemap *buttonRemapTable) {
+ Paddleboat_ErrorCode result = PADDLEBOAT_NO_ERROR;
+
+ // Check if a button table already exists in the existing table or if it needs to be added.
+ // Matching is done by name, so we have to use the string table via the remapped string index
+ const uint32_t existingButtonCount = *buttonEntryCount;
+ uint32_t currentButtonCount = existingButtonCount;
+
+ for (uint32_t newIndex = 0; newIndex < newButtonCount; ++newIndex) {
+ bool foundExistingMatch = false;
+ const uint32_t newButtonStringTableIndex =
+ stringRemapTable[newButton[newIndex].buttonNameStringTableIndex].newIndex;
+ for (uint32_t existingIndex = 0; existingIndex < existingButtonCount; ++existingIndex) {
+ if (buttonEntries[existingIndex].buttonNameStringTableIndex ==
+ newButtonStringTableIndex) {
+ buttonRemapTable[newIndex].newIndex = existingIndex;
+ foundExistingMatch = true;
+ break;
+ }
+ }
+ if (!foundExistingMatch) {
+ if (currentButtonCount >= maxButtonEntryCount) {
+ // Return error if out of room in axis table
+ result = PADDLEBOAT_ERROR_FEATURE_NOT_SUPPORTED;
+ break;
+ }
+ memcpy(&buttonEntries[currentButtonCount], &newButton[newIndex],
+ sizeof(Paddleboat_Controller_Mapping_File_Axis_Entry));
+ buttonEntries[currentButtonCount].buttonNameStringTableIndex =
+ newButtonStringTableIndex;
+ buttonRemapTable[newIndex].newIndex = currentButtonCount;
+ currentButtonCount += 1;
+ *buttonEntryCount = currentButtonCount;
+ }
+ }
+ return result;
+}
+
+Paddleboat_ErrorCode GameControllerMappingUtils::mergeControllerRemapData(
+ const Paddleboat_Controller_Mapping_File_Header *mappingFileHeader,
+ const size_t mappingFileBufferSize,
+ GameControllerMappingInfo &mappingInfo) {
+ Paddleboat_ErrorCode mergeResult = PADDLEBOAT_NO_ERROR;
+ // The incoming file has its own internal indexes for its axis, button and
+ // string table entries. These indices will need to be remapped after this data is merged
+ // into the existing internal arrays. Allocate an array of remap structures large
+ // enough for all required indices and section it by index category.
+ const size_t totalIndexCount = mappingFileHeader->axisTableEntryCount +
+ mappingFileHeader->buttonTableEntryCount +
+ mappingFileHeader->stringTableEntryCount;
+ std::unique_ptr<IndexTableRemap[]> remapTable =
+ std::make_unique<IndexTableRemap[]>(totalIndexCount);
+ IndexTableRemap *axisIndexTableRemap = remapTable.get();
+ IndexTableRemap *buttonIndexTableRemap = axisIndexTableRemap +
+ mappingFileHeader->axisTableEntryCount;
+ IndexTableRemap *stringIndexTableRemap = buttonIndexTableRemap +
+ mappingFileHeader->buttonTableEntryCount;
+
+ // Merge the string, axis, and button tables from the file into the internal tables
+ const uint8_t *fileStart = reinterpret_cast<const uint8_t*>(mappingFileHeader);
+ // Merge string table first since axis and button depend on it
+ const Paddleboat_Controller_Mapping_File_String_Entry *fileStringTable =
+ reinterpret_cast<const Paddleboat_Controller_Mapping_File_String_Entry *>(
+ fileStart + mappingFileHeader->stringTableOffset);
+ mergeResult = GameControllerMappingUtils::mergeStringTable(
+ fileStringTable, mappingFileHeader->stringTableEntryCount,
+ mappingInfo.mStringTable, &mappingInfo.mStringTableEntryCount,
+ GameControllerMappingInfo::MAX_STRING_TABLE_SIZE, stringIndexTableRemap);
+ if (mergeResult != PADDLEBOAT_NO_ERROR) {
+ return mergeResult;
+ }
+ // Axis table
+ const Paddleboat_Controller_Mapping_File_Axis_Entry *fileAxisTable =
+ reinterpret_cast<const Paddleboat_Controller_Mapping_File_Axis_Entry *>(
+ fileStart + mappingFileHeader->axisTableOffset);
+ mergeResult = GameControllerMappingUtils::mergeAxisTable(
+ fileAxisTable, mappingFileHeader->axisTableEntryCount,
+ mappingInfo.mAxisTable, &mappingInfo.mAxisTableEntryCount,
+ GameControllerMappingInfo::MAX_AXIS_TABLE_SIZE, stringIndexTableRemap,
+ axisIndexTableRemap);
+ if (mergeResult != PADDLEBOAT_NO_ERROR) {
+ return mergeResult;
+ }
+ // Button table
+ const Paddleboat_Controller_Mapping_File_Button_Entry *fileButtonTable =
+ reinterpret_cast<const Paddleboat_Controller_Mapping_File_Button_Entry *>(
+ fileStart + mappingFileHeader->buttonTableOffset);
+ mergeResult = GameControllerMappingUtils::mergeButtonTable(
+ fileButtonTable, mappingFileHeader->buttonTableEntryCount,
+ mappingInfo.mButtonTable, &mappingInfo.mButtonTableEntryCount,
+ GameControllerMappingInfo::MAX_BUTTON_TABLE_SIZE, stringIndexTableRemap,
+ buttonIndexTableRemap);
+ if (mergeResult != PADDLEBOAT_NO_ERROR) {
+ return mergeResult;
+ }
+
+ // Loop through the controller list in the file and merge them into the internal
+ // table.
+ for (uint32_t i = 0; i < mappingFileHeader->controllerTableEntryCount; ++i) {
+ const Paddleboat_Controller_Mapping_File_Controller_Entry *fileControllerTable =
+ reinterpret_cast<const Paddleboat_Controller_Mapping_File_Controller_Entry *>(
+ fileStart + mappingFileHeader->controllerTableOffset);
+ MappingTableSearch mapSearch(mappingInfo.mControllerTable,
+ mappingInfo.mControllerTableEntryCount);
+ mapSearch.initSearchParameters(fileControllerTable[i].vendorId,
+ fileControllerTable[i].productId,
+ fileControllerTable[i].minimumEffectiveApiLevel,
+ fileControllerTable[i].maximumEffectiveApiLevel);
+ GameControllerMappingUtils::findMatchingMapEntry(&mapSearch);
+ mergeResult = GameControllerMappingUtils::insertMapEntry(&fileControllerTable[i],
+ &mapSearch,
+ axisIndexTableRemap,
+ buttonIndexTableRemap);
+ if (mergeResult != PADDLEBOAT_NO_ERROR) {
+ break;
+ }
+ mappingInfo.mControllerTableEntryCount += 1;
+ }
+ return mergeResult;
+}
+
+Paddleboat_ErrorCode GameControllerMappingUtils::overwriteControllerRemapData(
+ const Paddleboat_Controller_Mapping_File_Header *mappingFileHeader,
+ const size_t mappingFileBufferSize,
+ GameControllerMappingInfo &mappingInfo) {
+
+ // Bounds check of internal buffers
+ if (mappingFileHeader->stringTableEntryCount >
+ GameControllerMappingInfo::MAX_STRING_TABLE_SIZE ||
+ mappingFileHeader->buttonTableEntryCount >
+ GameControllerMappingInfo::MAX_BUTTON_TABLE_SIZE ||
+ mappingFileHeader->axisTableEntryCount >
+ GameControllerMappingInfo::MAX_AXIS_TABLE_SIZE ||
+ mappingFileHeader->controllerTableEntryCount >
+ GameControllerMappingInfo::MAX_CONTROLLER_TABLE_SIZE) {
+ return PADDLEBOAT_ERROR_FEATURE_NOT_SUPPORTED;
+ }
+
+ const uint8_t *fileStart = reinterpret_cast<const uint8_t*>(mappingFileHeader);
+
+ const Paddleboat_Controller_Mapping_File_String_Entry *fileStringTable =
+ reinterpret_cast<const Paddleboat_Controller_Mapping_File_String_Entry *>(
+ fileStart + mappingFileHeader->stringTableOffset);
+ memcpy(mappingInfo.mStringTable, fileStringTable,
+ mappingFileHeader->stringTableEntryCount *
+ sizeof(Paddleboat_Controller_Mapping_File_String_Entry));
+ mappingInfo.mStringTableEntryCount = mappingFileHeader->stringTableEntryCount;
+
+ const Paddleboat_Controller_Mapping_File_Axis_Entry *fileAxisTable =
+ reinterpret_cast<const Paddleboat_Controller_Mapping_File_Axis_Entry *>(
+ fileStart + mappingFileHeader->axisTableOffset);
+ memcpy(mappingInfo.mAxisTable, fileAxisTable,
+ mappingFileHeader->axisTableEntryCount *
+ sizeof(Paddleboat_Controller_Mapping_File_Axis_Entry));
+ mappingInfo.mAxisTableEntryCount = mappingFileHeader->axisTableEntryCount;
+
+ const Paddleboat_Controller_Mapping_File_Button_Entry *fileButtonTable =
+ reinterpret_cast<const Paddleboat_Controller_Mapping_File_Button_Entry *>(
+ fileStart + mappingFileHeader->buttonTableOffset);
+ memcpy(mappingInfo.mButtonTable, fileButtonTable,
+ mappingFileHeader->buttonTableEntryCount *
+ sizeof(Paddleboat_Controller_Mapping_File_Button_Entry));
+ mappingInfo.mButtonTableEntryCount = mappingFileHeader->buttonTableEntryCount;
+
+ const Paddleboat_Controller_Mapping_File_Controller_Entry *fileControllerTable =
+ reinterpret_cast<const Paddleboat_Controller_Mapping_File_Controller_Entry *>(
+ fileStart + mappingFileHeader->controllerTableOffset);
+ memcpy(mappingInfo.mControllerTable, fileControllerTable,
+ mappingFileHeader->controllerTableEntryCount *
+ sizeof(Paddleboat_Controller_Mapping_File_Controller_Entry));
+ mappingInfo.mControllerTableEntryCount = mappingFileHeader->controllerTableEntryCount;
+
+ return PADDLEBOAT_NO_ERROR;
+}
+
+} // namespace paddleboat
diff --git a/games-controller/src/main/cpp/GameControllerMappingUtils.h b/games-controller/src/main/cpp/GameControllerMappingUtils.h
new file mode 100644
index 0000000..439bf32
--- /dev/null
+++ b/games-controller/src/main/cpp/GameControllerMappingUtils.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+
+#ifndef PADDLEBOAT_H
+#include "paddleboat.h"
+#endif
+#include "GameControllerMappingFile.h"
+#include "GameControllerMappingInfo.h"
+
+namespace paddleboat {
+
+class IndexTableRemap {
+public:
+ IndexTableRemap() : newIndex(-1) {}
+ IndexTableRemap(int32_t i) : newIndex(i) {}
+ int32_t newIndex;
+};
+
+class MappingTableSearch {
+public:
+ MappingTableSearch();
+
+ MappingTableSearch(Paddleboat_Controller_Mapping_File_Controller_Entry *mapRoot,
+ int32_t entryCount);
+
+ void initSearchParameters(const int32_t newVendorId,
+ const int32_t newProductId,
+ const int32_t newMinApi, const int32_t newMaxApi);
+
+ Paddleboat_Controller_Mapping_File_Controller_Entry *mappingRoot;
+ int32_t vendorId;
+ int32_t productId;
+ int32_t minApi;
+ int32_t maxApi;
+ int32_t tableIndex;
+ int32_t mapEntryCount;
+ int32_t tableEntryCount;
+ int32_t tableMaxEntryCount;
+};
+
+class GameControllerMappingUtils {
+public:
+ static bool findMatchingMapEntry(MappingTableSearch *searchEntry);
+
+ static Paddleboat_ErrorCode insertMapEntry(
+ const Paddleboat_Controller_Mapping_File_Controller_Entry *mappingData,
+ MappingTableSearch *searchEntry,
+ const IndexTableRemap *axisRemapTable,
+ const IndexTableRemap *buttonRemapTable);
+
+ static const Paddleboat_Controller_Mapping_File_Controller_Entry *validateMapTable(
+ const Paddleboat_Controller_Mapping_File_Controller_Entry *mappingRoot,
+ const int32_t tableEntryCount);
+
+ static Paddleboat_ErrorCode validateMapFile(
+ const Paddleboat_Controller_Mapping_File_Header *mappingFileHeader,
+ const size_t mappingFileBufferSize);
+
+ static Paddleboat_ErrorCode mergeStringTable(
+ const Paddleboat_Controller_Mapping_File_String_Entry *newStrings,
+ const uint32_t newStringCount,
+ Paddleboat_Controller_Mapping_File_String_Entry *stringEntries,
+ uint32_t *stringEntryCount,
+ const uint32_t maxStringEntryCount,
+ IndexTableRemap *remapTable);
+
+ static Paddleboat_ErrorCode mergeAxisTable(
+ const Paddleboat_Controller_Mapping_File_Axis_Entry *newAxis,
+ const uint32_t newAxisCount,
+ Paddleboat_Controller_Mapping_File_Axis_Entry *axisEntries,
+ uint32_t *axisEntryCount,
+ const uint32_t maxAxisEntryCount,
+ const IndexTableRemap *stringRemapTable,
+ IndexTableRemap *axisRemapTable);
+
+ static Paddleboat_ErrorCode mergeButtonTable(
+ const Paddleboat_Controller_Mapping_File_Button_Entry *newButton,
+ const uint32_t newButtonCount,
+ Paddleboat_Controller_Mapping_File_Button_Entry *buttonEntries,
+ uint32_t *buttonEntryCount,
+ const uint32_t maxButtonEntryCount,
+ const IndexTableRemap *stringRemapTable,
+ IndexTableRemap *buttonRemapTable);
+
+ static Paddleboat_ErrorCode mergeControllerRemapData(
+ const Paddleboat_Controller_Mapping_File_Header *mappingFileHeader,
+ const size_t mappingFileBufferSize,
+ GameControllerMappingInfo &mappingInfo);
+
+ static Paddleboat_ErrorCode overwriteControllerRemapData(
+ const Paddleboat_Controller_Mapping_File_Header *mappingFileHeader,
+ const size_t mappingFileBufferSize,
+ GameControllerMappingInfo &mappingInfo);
+};
+
+} // namespace paddleboat
diff --git a/games-controller/src/main/cpp/InternalControllerTable.cpp b/games-controller/src/main/cpp/InternalControllerTable.cpp
new file mode 100644
index 0000000..8d92428
--- /dev/null
+++ b/games-controller/src/main/cpp/InternalControllerTable.cpp
@@ -0,0 +1,481 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "InternalControllerTable.h"
+
+#include <android/input.h>
+#include <android/keycodes.h>
+
+#define ARRAY_COUNTOF(array) (sizeof(array) / sizeof(array[0]))
+
+#define PADDLEBOAT_AXIS_BUTTON_DPAD_UP 0
+#define PADDLEBOAT_AXIS_BUTTON_DPAD_LEFT 1
+#define PADDLEBOAT_AXIS_BUTTON_DPAD_DOWN 2
+#define PADDLEBOAT_AXIS_BUTTON_DPAD_RIGHT 3
+#define PADDLEBOAT_AXIS_BUTTON_L2 9
+#define PADDLEBOAT_AXIS_BUTTON_R2 12
+
+namespace paddleboat {
+
+// New, interim
+const Paddleboat_Controller_Mapping_File_Axis_Entry pb_internal_axis_table[] = {
+ { // 00
+ 1, // "Generic_Axis"
+ {
+ /* LX */ AMOTION_EVENT_AXIS_X,
+ /* LY */ AMOTION_EVENT_AXIS_Y,
+ /* RX */ AMOTION_EVENT_AXIS_Z,
+ /* RY */ AMOTION_EVENT_AXIS_RZ,
+ /* L1 */ PADDLEBOAT_AXIS_IGNORED,
+ /* L2 */ AMOTION_EVENT_AXIS_BRAKE,
+ /* R1 */ PADDLEBOAT_AXIS_IGNORED,
+ /* R2 */ AMOTION_EVENT_AXIS_GAS,
+ /* HX */ AMOTION_EVENT_AXIS_HAT_X,
+ /* HY */ AMOTION_EVENT_AXIS_HAT_Y,
+ },
+ {
+ /* LX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* LY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* L2 */ PADDLEBOAT_AXIS_BUTTON_L2,
+ /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* R2 */ PADDLEBOAT_AXIS_BUTTON_R2,
+ /* HX */ PADDLEBOAT_AXIS_BUTTON_DPAD_RIGHT,
+ /* HY */ PADDLEBOAT_AXIS_BUTTON_DPAD_DOWN,
+ },
+ {
+ /* LX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* LY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* L2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* R2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* HX */ PADDLEBOAT_AXIS_BUTTON_DPAD_LEFT,
+ /* HY */ PADDLEBOAT_AXIS_BUTTON_DPAD_UP,
+ },
+ 0
+ },
+ { // 01
+ 8, // "DS5_Compat_Axis"
+ {
+ /* LX */ AMOTION_EVENT_AXIS_X,
+ /* LY */ AMOTION_EVENT_AXIS_Y,
+ /* RX */ AMOTION_EVENT_AXIS_Z,
+ /* RY */ AMOTION_EVENT_AXIS_RZ,
+ /* L1 */ PADDLEBOAT_AXIS_IGNORED,
+ /* L2 */ AMOTION_EVENT_AXIS_RX,
+ /* R1 */ PADDLEBOAT_AXIS_IGNORED,
+ /* R2 */ AMOTION_EVENT_AXIS_RY,
+ /* HX */ AMOTION_EVENT_AXIS_HAT_X,
+ /* HY */ AMOTION_EVENT_AXIS_HAT_Y,
+ },
+ {
+ /* LX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* LY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* L2 */ PADDLEBOAT_AXIS_BUTTON_L2,
+ /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* R2 */ PADDLEBOAT_AXIS_BUTTON_R2,
+ /* HX */ PADDLEBOAT_AXIS_BUTTON_DPAD_RIGHT,
+ /* HY */ PADDLEBOAT_AXIS_BUTTON_DPAD_DOWN,
+ },
+ {
+ /* LX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* LY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* L2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* R2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* HX */ PADDLEBOAT_AXIS_BUTTON_DPAD_LEFT,
+ /* HY */ PADDLEBOAT_AXIS_BUTTON_DPAD_UP,
+ },
+ 0
+ },
+ { // 02
+ 9, // "Nimbus_Axis"
+ {
+ /* LX */ AMOTION_EVENT_AXIS_X,
+ /* LY */ AMOTION_EVENT_AXIS_Y,
+ /* RX */ AMOTION_EVENT_AXIS_Z,
+ /* RY */ AMOTION_EVENT_AXIS_RZ,
+ /* L1 */ PADDLEBOAT_AXIS_IGNORED,
+ /* L2 */ PADDLEBOAT_AXIS_IGNORED,
+ /* R1 */ PADDLEBOAT_AXIS_IGNORED,
+ /* R2 */ PADDLEBOAT_AXIS_IGNORED,
+ /* HX */ PADDLEBOAT_AXIS_IGNORED,
+ /* HY */ PADDLEBOAT_AXIS_IGNORED,
+ },
+ {
+ /* LX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* LY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* L2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* R2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* HX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* HY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ },
+ {
+ /* LX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* LY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* L2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* R2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* HX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* HY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ },
+ 10
+ }
+};
+
+const Paddleboat_Controller_Mapping_File_Button_Entry pb_internal_button_table[] = {
+ { // 00
+ 2, // "XB_Button"
+ { /* UP */ AKEYCODE_DPAD_UP,
+ /* LEFT */ AKEYCODE_DPAD_LEFT,
+ /* DOWN */ AKEYCODE_DPAD_DOWN,
+ /* RIGHT */ AKEYCODE_DPAD_RIGHT,
+ /* A */ AKEYCODE_BUTTON_A,
+ /* B */ AKEYCODE_BUTTON_B,
+ /* X */ AKEYCODE_BUTTON_X,
+ /* Y */ AKEYCODE_BUTTON_Y,
+ /* L1 */ AKEYCODE_BUTTON_L1,
+ /* L2 */ AKEYCODE_BUTTON_L2,
+ /* L3 */ AKEYCODE_BUTTON_THUMBL,
+ /* R1 */ AKEYCODE_BUTTON_R1,
+ /* R2 */ AKEYCODE_BUTTON_R2,
+ /* R3 */ AKEYCODE_BUTTON_THUMBR,
+ /* SELECT */ AKEYCODE_BUTTON_SELECT,
+ /* START */ AKEYCODE_BUTTON_START,
+ /* SYSTEM */ AKEYCODE_BUTTON_MODE,
+ /* TOUCHP */ PADDLEBOAT_BUTTON_IGNORED,
+ /* AUX1 */ AKEYCODE_MEDIA_RECORD,
+ /* AUX2 */ PADDLEBOAT_BUTTON_IGNORED,
+ /* AUX3 */ PADDLEBOAT_BUTTON_IGNORED,
+ /* AUX4 */ PADDLEBOAT_BUTTON_IGNORED
+ }
+ },
+ { // 01
+ 3, // "DS_Button"
+ {
+ /* UP */ AKEYCODE_DPAD_UP,
+ /* LEFT */ AKEYCODE_DPAD_LEFT,
+ /* DOWN */ AKEYCODE_DPAD_DOWN,
+ /* RIGHT */ AKEYCODE_DPAD_RIGHT,
+ /* A */ AKEYCODE_BUTTON_A,
+ /* B */ AKEYCODE_BUTTON_B,
+ /* X */ AKEYCODE_BUTTON_X,
+ /* Y */ AKEYCODE_BUTTON_Y,
+ /* L1 */ AKEYCODE_BUTTON_L1,
+ /* L2 */ AKEYCODE_BUTTON_L2,
+ /* L3 */ AKEYCODE_BUTTON_THUMBL,
+ /* R1 */ AKEYCODE_BUTTON_R1,
+ /* R2 */ AKEYCODE_BUTTON_R2,
+ /* R3 */ AKEYCODE_BUTTON_THUMBR,
+ /* SELECT */ AKEYCODE_BUTTON_SELECT,
+ /* START */ AKEYCODE_BUTTON_START,
+ /* SYSTEM */ AKEYCODE_BUTTON_MODE,
+ /* TOUCHP */ PADDLEBOAT_BUTTON_IGNORED,
+ /* AUX1 */ PADDLEBOAT_BUTTON_IGNORED,
+ /* AUX2 */ PADDLEBOAT_BUTTON_IGNORED,
+ /* AUX3 */ PADDLEBOAT_BUTTON_IGNORED,
+ /* AUX4 */ PADDLEBOAT_BUTTON_IGNORED
+ }
+ },
+ { // 02
+ 4, // "DS5_Compat_Button"
+ {
+ /* UP */ AKEYCODE_DPAD_UP,
+ /* LEFT */ AKEYCODE_DPAD_LEFT,
+ /* DOWN */ AKEYCODE_DPAD_DOWN,
+ /* RIGHT */ AKEYCODE_DPAD_RIGHT,
+ /* A */ AKEYCODE_BUTTON_B,
+ /* B */ AKEYCODE_BUTTON_C,
+ /* X */ AKEYCODE_BUTTON_A,
+ /* Y */ AKEYCODE_BUTTON_X,
+ /* L1 */ AKEYCODE_BUTTON_Y,
+ /* L2 */ PADDLEBOAT_BUTTON_IGNORED,
+ /* L3 */ AKEYCODE_BUTTON_SELECT,
+ /* R1 */ AKEYCODE_BUTTON_Z,
+ /* R2 */ PADDLEBOAT_BUTTON_IGNORED,
+ /* R3 */ AKEYCODE_BUTTON_START,
+ /* SELECT */ AKEYCODE_BUTTON_L2,
+ /* START */ AKEYCODE_BUTTON_R2,
+ /* SYSTEM */ AKEYCODE_BUTTON_MODE,
+ /* TOUCHP */ AKEYCODE_BUTTON_THUMBL,
+ /* AUX1 */ PADDLEBOAT_BUTTON_IGNORED,
+ /* AUX2 */ PADDLEBOAT_BUTTON_IGNORED,
+ /* AUX3 */ PADDLEBOAT_BUTTON_IGNORED,
+ /* AUX4 */ PADDLEBOAT_BUTTON_IGNORED
+ }
+ },
+ { // 03
+ 5, // "NinSPro"
+ {
+ /* UP */ AKEYCODE_DPAD_UP,
+ /* LEFT */ AKEYCODE_DPAD_LEFT,
+ /* DOWN */ AKEYCODE_DPAD_DOWN,
+ /* RIGHT */ AKEYCODE_DPAD_RIGHT,
+ /* A */ AKEYCODE_BUTTON_B,
+ /* B */ AKEYCODE_BUTTON_A,
+ /* X */ AKEYCODE_BUTTON_Y,
+ /* Y */ AKEYCODE_BUTTON_X,
+ /* L1 */ AKEYCODE_BUTTON_L1,
+ /* L2 */ AKEYCODE_BUTTON_L2,
+ /* L3 */ AKEYCODE_BUTTON_THUMBL,
+ /* R1 */ AKEYCODE_BUTTON_R1,
+ /* R2 */ AKEYCODE_BUTTON_R2,
+ /* R3 */ AKEYCODE_BUTTON_THUMBR,
+ /* SELECT */ AKEYCODE_BUTTON_SELECT,
+ /* START */ AKEYCODE_BUTTON_START,
+ /* SYSTEM */ AKEYCODE_BUTTON_MODE,
+ /* TOUCHP */ PADDLEBOAT_BUTTON_IGNORED,
+ /* AUX1 */ AKEYCODE_MEDIA_RECORD,
+ /* AUX2 */ PADDLEBOAT_BUTTON_IGNORED,
+ /* AUX3 */ PADDLEBOAT_BUTTON_IGNORED,
+ /* AUX4 */ PADDLEBOAT_BUTTON_IGNORED
+ }
+ },
+ { // 04
+ 6, // "Generic_Button"
+ {
+ /* UP */ AKEYCODE_DPAD_UP,
+ /* LEFT */ AKEYCODE_DPAD_LEFT,
+ /* DOWN */ AKEYCODE_DPAD_DOWN,
+ /* RIGHT */ AKEYCODE_DPAD_RIGHT,
+ /* A */ AKEYCODE_BUTTON_A,
+ /* B */ AKEYCODE_BUTTON_B,
+ /* X */ AKEYCODE_BUTTON_X,
+ /* Y */ AKEYCODE_BUTTON_Y,
+ /* L1 */ AKEYCODE_BUTTON_L1,
+ /* L2 */ AKEYCODE_BUTTON_L2,
+ /* L3 */ AKEYCODE_BUTTON_THUMBL,
+ /* R1 */ AKEYCODE_BUTTON_R1,
+ /* R2 */ AKEYCODE_BUTTON_R2,
+ /* R3 */ AKEYCODE_BUTTON_THUMBR,
+ /* SELECT */ AKEYCODE_BUTTON_SELECT,
+ /* START */ AKEYCODE_BUTTON_START,
+ /* SYSTEM */ AKEYCODE_BUTTON_MODE,
+ /* TOUCHP */ PADDLEBOAT_BUTTON_IGNORED,
+ /* AUX1 */ PADDLEBOAT_BUTTON_IGNORED,
+ /* AUX2 */ PADDLEBOAT_BUTTON_IGNORED,
+ /* AUX3 */ PADDLEBOAT_BUTTON_IGNORED,
+ /* AUX4 */ PADDLEBOAT_BUTTON_IGNORED
+ }
+ },
+ { // 05
+ 6, // "NGPro_PC2"
+ {
+ /* UP */ AKEYCODE_DPAD_UP,
+ /* LEFT */ AKEYCODE_DPAD_LEFT,
+ /* DOWN */ AKEYCODE_DPAD_DOWN,
+ /* RIGHT */ AKEYCODE_DPAD_RIGHT,
+ /* A */ AKEYCODE_BUTTON_A,
+ /* B */ AKEYCODE_BUTTON_B,
+ /* X */ AKEYCODE_BUTTON_X,
+ /* Y */ AKEYCODE_BUTTON_Y,
+ /* L1 */ AKEYCODE_BUTTON_L1,
+ /* L2 */ AKEYCODE_BUTTON_R1,
+ /* L3 */ PADDLEBOAT_BUTTON_IGNORED,
+ /* R1 */ AKEYCODE_BUTTON_Z,
+ /* R2 */ AKEYCODE_BUTTON_C,
+ /* R3 */ PADDLEBOAT_BUTTON_IGNORED,
+ /* SELECT */ AKEYCODE_BUTTON_SELECT,
+ /* START */ AKEYCODE_BUTTON_START,
+ /* SYSTEM */ PADDLEBOAT_BUTTON_IGNORED,
+ /* TOUCHP */ PADDLEBOAT_BUTTON_IGNORED,
+ /* AUX1 */ PADDLEBOAT_BUTTON_IGNORED,
+ /* AUX2 */ PADDLEBOAT_BUTTON_IGNORED,
+ /* AUX3 */ PADDLEBOAT_BUTTON_IGNORED,
+ /* AUX4 */ PADDLEBOAT_BUTTON_IGNORED
+ }
+ },
+ { // 06
+ 10, // "Nimbus_Button"
+ {
+ /* UP */ PADDLEBOAT_BUTTON_IGNORED,
+ /* LEFT */ PADDLEBOAT_BUTTON_IGNORED,
+ /* DOWN */ PADDLEBOAT_BUTTON_IGNORED,
+ /* RIGHT */ PADDLEBOAT_BUTTON_IGNORED,
+ /* A */ AKEYCODE_BUTTON_A,
+ /* B */ AKEYCODE_BUTTON_B,
+ /* X */ AKEYCODE_BUTTON_C,
+ /* Y */ AKEYCODE_BUTTON_X,
+ /* L1 */ AKEYCODE_BUTTON_Y,
+ /* L2 */ AKEYCODE_BUTTON_L1,
+ /* L3 */ PADDLEBOAT_BUTTON_IGNORED,
+ /* R1 */ AKEYCODE_BUTTON_Z,
+ /* R2 */ AKEYCODE_BUTTON_R1,
+ /* R3 */ PADDLEBOAT_BUTTON_IGNORED,
+ /* SELECT */ PADDLEBOAT_BUTTON_IGNORED,
+ /* START */ PADDLEBOAT_BUTTON_IGNORED,
+ /* SYSTEM */ PADDLEBOAT_BUTTON_IGNORED,
+ /* TOUCHP */ PADDLEBOAT_BUTTON_IGNORED,
+ /* AUX1 */ PADDLEBOAT_BUTTON_IGNORED,
+ /* AUX2 */ PADDLEBOAT_BUTTON_IGNORED,
+ /* AUX3 */ PADDLEBOAT_BUTTON_IGNORED,
+ /* AUX4 */ PADDLEBOAT_BUTTON_IGNORED
+ }
+ }
+};
+
+const Paddleboat_Controller_Mapping_File_Controller_Entry pb_internal_controller_table[] = {
+ {
+ // Steelseries Nimbus (bluetooth)
+ 16,00,0x0111,0x1420,
+ PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD,
+ 2,6,0,0
+ },
+ {
+ // Microsoft Xbox 360 controller (usb)
+ 16,00,0x045e,0x028e,
+ PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD,
+ 0,0,0,0
+ },
+ {
+ // Microsoft Xbox One non-BT controller (usb)
+ 16,00,0x045e,0x02d1,
+ PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD,
+ 0,0,0,0
+ },
+ {
+ // 8BitDo Arcade Stick, XInput mode, Bluetooth
+ 16,00,0x045e,0x02e0,
+ PADDLEBOAT_CONTROLLER_LAYOUT_ARCADE_STICK,
+ 0,0,0,0
+ },
+ {
+ // Microsoft Xbox One BT controller (usb)
+ 16,00,0x045e,0x02ea,
+ PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD,
+ 0,0,0,0
+ },
+ {
+ // Microsoft Xbox One BT controller (bluetooth)
+ 16,00,0x045e,0x02fd,
+ PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD,
+ 0,0,0,0
+ },
+ {
+ // Microsoft Xbox Series controller (usb)
+ 16,00,0x045e,0x0b12,
+ PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD,
+ 0,0,0,0
+ },
+ {
+ // Microsoft Xbox Series controller (bluetooth)
+ 16,00,0x045e,0x0b13,
+ PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD,
+ 0,0,0,0
+ },
+ {
+ // Sony PlayStation 4 controller (usb/bluetooth)
+ 16,00,0x054C,0x05C4,
+ PADDLEBOAT_CONTROLLER_LAYOUT_SHAPES | PADDLEBOAT_CONTROLLER_FLAG_TOUCHPAD,
+ 0,1,0,0
+ },
+ {
+ // Sony PlayStation 4 controller (usb/bluetooth) - alternate deviceId
+ 16,00,0x054C,0x09CC,
+ PADDLEBOAT_CONTROLLER_LAYOUT_SHAPES | PADDLEBOAT_CONTROLLER_FLAG_TOUCHPAD,
+ 0,1,0,0
+ },
+ {
+ // Sony PlayStation 5 controller (usb/bluetooth) API <= 30
+ 16,30,0x054C,0x0CE6,
+ PADDLEBOAT_CONTROLLER_LAYOUT_SHAPES,
+ 1,2,0,0
+ },
+ {
+ // Sony PlayStation 5 controller (usb/bluetooth) API >= 31
+ 31,00,0x054C,0x0CE6,
+ PADDLEBOAT_CONTROLLER_LAYOUT_SHAPES | PADDLEBOAT_CONTROLLER_FLAG_TOUCHPAD,
+ 0,1,0,0
+ },
+ {
+ // Nintendo Switch Pro controller (usb/bluetooth)
+ 16,30,0x057e,0x2009,
+ PADDLEBOAT_CONTROLLER_LAYOUT_REVERSE,
+ 0,3,0,0
+ },
+ {
+ // Nvidia Shield TV controller (usb)
+ 16,00,0x0955,0x7210,
+ PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD,
+ 0,4,0,0
+ },
+ {
+ // Google Stadia controller (usb)
+ 16,00,0x18d1,0x9400,
+ PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD,
+ 0,4,0,0
+ },
+ {
+ // NeoGeo Pro, PC 2 setting, USB
+ 16,00,0x20bc,0x5501,
+ PADDLEBOAT_CONTROLLER_LAYOUT_ARCADE_STICK,
+ 0,5,0,0
+ },
+ {
+ // Razer Kishi v1 (usb)
+ 16,00,0x27f8,0x0bbf,
+ PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD,
+ 0,0,0,0
+ }
+};
+
+const Paddleboat_Controller_Mapping_File_String_Entry pb_internal_string_table[] = {
+ {"None"}, // 00
+ {"Generic_Axis"}, // 01
+ {"XB_Button"}, // 02
+ {"DS_Button"}, // 03
+ {"DS5_Compat_Button"}, // 04
+ {"NinSPro_Button"}, // 05
+ {"Generic_Button"}, // 06
+ {"NGPro_PC2_Button"}, // 07
+ {"DS5_Compat_Axis"}, // 08
+ {"Nimbus_Axis"}, // 09
+ {"Nimbus_Button"} // 10
+};
+
+const Paddleboat_Internal_Mapping_Header pb_internal_header = {
+ ARRAY_COUNTOF(pb_internal_axis_table),
+ ARRAY_COUNTOF(pb_internal_button_table),
+ ARRAY_COUNTOF(pb_internal_controller_table),
+ ARRAY_COUNTOF(pb_internal_string_table),
+ pb_internal_axis_table,
+ pb_internal_button_table,
+ pb_internal_controller_table,
+ pb_internal_string_table
+};
+
+const Paddleboat_Internal_Mapping_Header *GetInternalMappingHeader() {
+ return &pb_internal_header;
+}
+} // namespace paddleboat
\ No newline at end of file
diff --git a/games-controller/src/main/cpp/InternalControllerTable.h b/games-controller/src/main/cpp/InternalControllerTable.h
new file mode 100644
index 0000000..0e0165b
--- /dev/null
+++ b/games-controller/src/main/cpp/InternalControllerTable.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+
+#include "paddleboat.h"
+#include "GameControllerMappingFile.h"
+
+namespace paddleboat {
+// Interim until PaddleboatMappingTool is finished
+typedef struct Paddleboat_Internal_Mapping_Header {
+ uint32_t axisTableEntryCount;
+ uint32_t buttonTableEntryCount;
+ uint32_t controllerTableEntryCount;
+ uint32_t stringTableEntryCount;
+ const Paddleboat_Controller_Mapping_File_Axis_Entry *axisTable;
+ const Paddleboat_Controller_Mapping_File_Button_Entry *buttonTable;
+ const Paddleboat_Controller_Mapping_File_Controller_Entry *controllerTable;
+ const Paddleboat_Controller_Mapping_File_String_Entry *stringTable;
+} Paddleboat_Internal_Mapping_Header;
+
+const Paddleboat_Internal_Mapping_Header *GetInternalMappingHeader();
+
+} // namespace paddleboat
\ No newline at end of file
diff --git a/GameController/src/main/cpp/Log.h b/games-controller/src/main/cpp/Log.h
similarity index 100%
rename from GameController/src/main/cpp/Log.h
rename to games-controller/src/main/cpp/Log.h
diff --git a/GameController/src/main/cpp/ThreadUtil.h b/games-controller/src/main/cpp/ThreadUtil.h
similarity index 100%
rename from GameController/src/main/cpp/ThreadUtil.h
rename to games-controller/src/main/cpp/ThreadUtil.h
diff --git a/games-controller/src/main/cpp/include/common/gamesdk_common.h b/games-controller/src/main/cpp/include/common/gamesdk_common.h
new file mode 120000
index 0000000..e1d8f15
--- /dev/null
+++ b/games-controller/src/main/cpp/include/common/gamesdk_common.h
@@ -0,0 +1 @@
+../../../../../../include/common/gamesdk_common.h
\ No newline at end of file
diff --git a/games-controller/src/main/cpp/include/paddleboat/paddleboat.h b/games-controller/src/main/cpp/include/paddleboat/paddleboat.h
new file mode 120000
index 0000000..0f6c6a6
--- /dev/null
+++ b/games-controller/src/main/cpp/include/paddleboat/paddleboat.h
@@ -0,0 +1 @@
+../../paddleboat/include/paddleboat.h
\ No newline at end of file
diff --git a/games-controller/src/main/cpp/paddleboat/include/common b/games-controller/src/main/cpp/paddleboat/include/common
new file mode 120000
index 0000000..a3ff523
--- /dev/null
+++ b/games-controller/src/main/cpp/paddleboat/include/common
@@ -0,0 +1 @@
+../../../../../../include/common/
\ No newline at end of file
diff --git a/GameController/src/main/cpp/paddleboat/include/paddleboat.h b/games-controller/src/main/cpp/paddleboat/include/paddleboat.h
similarity index 77%
rename from GameController/src/main/cpp/paddleboat/include/paddleboat.h
rename to games-controller/src/main/cpp/paddleboat/include/paddleboat.h
index 2a02844..20a91e2 100644
--- a/GameController/src/main/cpp/paddleboat/include/paddleboat.h
+++ b/games-controller/src/main/cpp/paddleboat/include/paddleboat.h
@@ -34,21 +34,45 @@
#include <android/input.h>
#include <jni.h>
+#include <stdbool.h>
#include <stdint.h>
+#include "common/gamesdk_common.h"
+
#ifdef __cplusplus
extern "C" {
#endif
+#define PADDLEBOAT_MAJOR_VERSION 2
+#define PADDLEBOAT_MINOR_VERSION 0
+#define PADDLEBOAT_BUGFIX_VERSION 0
+#define PADDLEBOAT_PACKED_VERSION \
+ ANDROID_GAMESDK_PACKED_VERSION(PADDLEBOAT_MAJOR_VERSION, \
+ PADDLEBOAT_MINOR_VERSION, \
+ PADDLEBOAT_BUGFIX_VERSION)
+
/**
* @brief Maximum number of simultaneously connected controllers.
*/
#define PADDLEBOAT_MAX_CONTROLLERS 8
/**
+ * @brief The maximum number of characters, including the terminating
+ * character, allowed in a string table entry
+ */
+#define PADDLEBOAT_STRING_TABLE_ENTRY_MAX_SIZE 64
+
+/**
+ * @brief The expected value in the `fileIdentifier` field of the
+ * `Paddleboat_Controller_Mapping_File_Header` for a valid
+ * mapping file.
+ */
+#define PADDLEBOAT_MAPPING_FILE_IDENTIFIER 0xadd1eb0a
+
+/**
* @brief Paddleboat error code results.
*/
-enum Paddleboat_ErrorCode {
+enum Paddleboat_ErrorCode : int32_t {
/**
* @brief No error. Function call was successful.
*/
@@ -92,7 +116,22 @@
* @brief An invalid parameter was specified. This usually means NULL or
* nullptr was passed in a parameter that requires a valid pointer.
*/
- PADDLEBOAT_ERROR_INVALID_PARAMETER = -2007
+ PADDLEBOAT_ERROR_INVALID_PARAMETER = -2007,
+ /**
+ * @brief Invalid controller mapping data was provided. The data in the
+ * provided buffer does not match the expected mapping data format.
+ */
+ PADDLEBOAT_INVALID_MAPPING_DATA = -2008,
+ /**
+ * @brief Incompatible controller mapping data was provided. The data in
+ * the provided buffer is from an incompatible version of the mapping data format.
+ */
+ PADDLEBOAT_INCOMPATIBLE_MAPPING_DATA = -2009,
+ /**
+ * @brief A file I/O error occurred when trying to read mapping data from the
+ * file descriptor passed to ::Paddleboat_addControllerRemapDataFromFd
+ */
+ PADDLEBOAT_ERROR_FILE_IO = -2010
};
/**
@@ -100,7 +139,7 @@
* AND against `Paddleboat_Controller_Data.buttonsDown` to check for button
* status.
*/
-enum Paddleboat_Buttons {
+enum Paddleboat_Buttons : uint32_t {
/**
* @brief Bitmask for `Paddleboat_Controller_Data.buttonsDown` direction pad
* up button.
@@ -213,7 +252,7 @@
* AND against `Paddleboat_Controller_Info.controllerFlags` to determine feature
* availability.
*/
-enum Paddleboat_Controller_Flags {
+enum Paddleboat_Controller_Flags : uint32_t {
/**
* @brief Bitmask for `Paddleboat_Controller_Info.controllerFlags`
* If set, this controller device wasn't found in the internal
@@ -280,10 +319,55 @@
};
/**
+ * @brief Bitmask values to use with ::Paddleboat_getIntegratedMotionSensorFlags
+ * and ::Paddleboat_setMotionDataCallbackWithIntegratedFlags
+ * Bitmask values represent integrated sensor types on the main device instead
+ * of a controller device.
+ */
+enum Paddleboat_Integrated_Motion_Sensor_Flags : uint32_t {
+ /**
+ * @brief Bitmask for ::Paddleboat_getIntegratedMotionSensorFlags
+ * No present integrated motion sensors.
+ */
+ PADDLEBOAT_INTEGRATED_SENSOR_NONE = 0,
+ /**
+ * @brief Bitmask for ::Paddleboat_getIntegratedMotionSensorFlags
+ * If set, the main device supports reporting accelerometer
+ * motion axis data
+ */
+ PADDLEBOAT_INTEGRATED_SENSOR_ACCELEROMETER = (0x00000001),
+ /**
+ * @brief Bitmask for ::Paddleboat_getIntegratedMotionSensorFlags
+ * If set, the main device supports reporting gyroscope
+ * motion axis data
+ */
+ PADDLEBOAT_INTEGRATED_SENSOR_GYROSCOPE = (0x00000002),
+};
+
+/**
+ * @brief Bitmask flags to use with ::Paddleboat_getIntegratedMotionSensorFlags
+ * and ::Paddleboat_setMotionDataCallbackWithIntegratedFlags
+ * Flag values represent integrated sensor types on the main device instead
+ * of a controller device.
+ */
+ enum Paddleboat_Motion_Data_Callback_Sensor_Index : uint32_t {
+ /**
+ * @brief Value passed in the `controllerIndex` parameter of the
+ * `Paddleboat_MotionDataCallback` if integrated sensor data
+ * reporting is active and the motion data event came from
+ * an integrated sensor, rather than a controller sensor.
+ * This value will only be passed to the motion data callback
+ * if integrated sensor data has been requested using
+ * ::Paddleboat_setMotionDataCallbackWithIntegratedFlags
+ */
+ PADDLEBOAT_INTEGRATED_SENSOR_INDEX = (0x40000000)
+};
+
+/**
* @brief Paddleboat mouse buttons as bitmask values
* AND against `Paddleboat_Mouse_Data.buttonsDown` to determine button status.
*/
-enum Paddleboat_Mouse_Buttons {
+enum Paddleboat_Mouse_Buttons : uint32_t {
/**
* @brief Bitmask for `Paddleboat_Mouse_Data.buttonsDown` left mouse button.
*/
@@ -326,7 +410,7 @@
/**
* @brief Paddleboat axis mapping table axis order.
*/
-enum Paddleboat_Mapping_Axis {
+enum Paddleboat_Mapping_Axis : uint32_t {
/**
* @brief Paddleboat internal mapping index for left thumbstick X axis. */
PADDLEBOAT_MAPPING_AXIS_LEFTSTICK_X = 0,
@@ -368,7 +452,7 @@
* @brief Special constants to specify an axis or axis button mapping is ignored
* by the controller.
*/
-enum Paddleboat_Ignored_Axis {
+enum Paddleboat_Ignored_Axis : uint32_t {
/**
* @brief Constant that signifies an axis in the
* `Paddleboat_Controller_Mapping_Data.axisPositiveButtonMapping` array
@@ -387,7 +471,7 @@
/**
* @brief Special constant to specify a button is ignored by the controller.
*/
-enum Paddleboat_Ignored_Buttons {
+enum Paddleboat_Ignored_Buttons : uint32_t {
/**
* @brief Constant that signifies a button in the
* `Paddleboat_Controller_Mapping_Data.buttonMapping` array
@@ -399,18 +483,22 @@
/**
* @brief Battery status of a controller
*/
-enum Paddleboat_BatteryStatus {
- PADDLEBOAT_CONTROLLER_BATTERY_UNKNOWN = 0, ///< Battery status is unknown
- PADDLEBOAT_CONTROLLER_BATTERY_CHARGING = 1, ///< Controller battery is charging
- PADDLEBOAT_CONTROLLER_BATTERY_DISCHARGING = 2, ///< Controller battery is discharging
- PADDLEBOAT_CONTROLLER_BATTERY_NOT_CHARGING = 3, ///< Controller battery is not charging
- PADDLEBOAT_CONTROLLER_BATTERY_FULL = 4 ///< Controller battery is completely charged
+enum Paddleboat_BatteryStatus : uint32_t {
+ PADDLEBOAT_CONTROLLER_BATTERY_UNKNOWN = 0, ///< Battery status is unknown
+ PADDLEBOAT_CONTROLLER_BATTERY_CHARGING =
+ 1, ///< Controller battery is charging
+ PADDLEBOAT_CONTROLLER_BATTERY_DISCHARGING =
+ 2, ///< Controller battery is discharging
+ PADDLEBOAT_CONTROLLER_BATTERY_NOT_CHARGING =
+ 3, ///< Controller battery is not charging
+ PADDLEBOAT_CONTROLLER_BATTERY_FULL =
+ 4 ///< Controller battery is completely charged
};
/**
* @brief Current status of a controller (at a specified controller index)
*/
-enum Paddleboat_ControllerStatus {
+enum Paddleboat_ControllerStatus : uint32_t {
PADDLEBOAT_CONTROLLER_INACTIVE = 0, ///< No controller is connected
PADDLEBOAT_CONTROLLER_ACTIVE = 1, ///< Controller is connected and active
PADDLEBOAT_CONTROLLER_JUST_CONNECTED =
@@ -424,7 +512,7 @@
/**
* @brief The button layout and iconography of the controller buttons
*/
-enum Paddleboat_ControllerButtonLayout {
+enum Paddleboat_ControllerButtonLayout : uint32_t {
//! Y \n
//! X B\n
//! A
@@ -451,25 +539,26 @@
* @brief The type of light being specified by a call to
* ::Paddleboat_setControllerLight
*/
-enum Paddleboat_LightType {
- PADDLEBOAT_LIGHT_PLAYER_NUMBER = 0, ///< Light is a player index,
- ///< `lightData` is the player number
- PADDLEBOAT_LIGHT_RGB = 1 ///< Light is a color light,
- ///< `lightData` is a ARGB (8888) light value.
+enum Paddleboat_LightType : uint32_t {
+ PADDLEBOAT_LIGHT_PLAYER_NUMBER = 0, ///< Light is a player index,
+ ///< `lightData` is the player number
+ PADDLEBOAT_LIGHT_RGB = 1 ///< Light is a color light,
+ ///< `lightData` is a ARGB (8888) light value.
};
/**
- * @brief The type of motion data being reported in a Paddleboat_Motion_Data structure
+ * @brief The type of motion data being reported in a Paddleboat_Motion_Data
+ * structure
*/
-enum Paddleboat_Motion_Type {
- PADDLEBOAT_MOTION_ACCELEROMETER = 0, ///< Accelerometer motion data
- PADDLEBOAT_MOTION_GYROSCOPE = 1 ///< Gyroscope motion data
+enum Paddleboat_Motion_Type : uint32_t {
+ PADDLEBOAT_MOTION_ACCELEROMETER = 0, ///< Accelerometer motion data
+ PADDLEBOAT_MOTION_GYROSCOPE = 1 ///< Gyroscope motion data
};
/**
* @brief The status of the mouse device
*/
-enum Paddleboat_MouseStatus {
+enum Paddleboat_MouseStatus : uint32_t {
PADDLEBOAT_MOUSE_NONE = 0, ///< No mouse device is connected
PADDLEBOAT_MOUSE_CONTROLLER_EMULATED =
1, ///< A virtual mouse is connected
@@ -483,7 +572,7 @@
* @brief The addition mode to use when passing new controller mapping data
* to ::Paddleboat_addControllerRemapData
*/
-enum Paddleboat_Remap_Addition_Mode {
+enum Paddleboat_Remap_Addition_Mode : uint32_t {
PADDLEBOAT_REMAP_ADD_MODE_DEFAULT =
0, ///< If a vendorId/productId controller entry in the
///< new remap table exists in the current database,
@@ -500,13 +589,16 @@
};
/**
- * @brief A structure that describes the current battery state of a controller. This structure
- * will only be populated if a controller has `PADDLEBOAT_CONTROLLER_FLAG_BATTERY` set in
+ * @brief A structure that describes the current battery state of a controller.
+ * This structure will only be populated if a controller has
+ * `PADDLEBOAT_CONTROLLER_FLAG_BATTERY` set in
* `Paddleboat_Controller_Info.controllerFlags`
*/
typedef struct Paddleboat_Controller_Battery {
- Paddleboat_BatteryStatus batteryStatus; /** @brief The current status of the battery */
- float batteryLevel; /** @brief The current charge level of the battery, from 0.0 to 1.0 */
+ Paddleboat_BatteryStatus
+ batteryStatus; /** @brief The current status of the battery */
+ float batteryLevel; /** @brief The current charge level of the battery, from
+ 0.0 to 1.0 */
} Paddleboat_Controller_Battery;
/**
@@ -580,8 +672,8 @@
*/
Paddleboat_Controller_Pointer virtualPointer;
/**
- * @brief Battery status. This structure will only be populated if the controller
- * has `PADDLEBOAT_CONTROLLER_FLAG_BATTERY` set in
+ * @brief Battery status. This structure will only be populated if the
+ * controller has `PADDLEBOAT_CONTROLLER_FLAG_BATTERY` set in
* `Paddleboat_Controller_Info.controllerFlags`
*/
Paddleboat_Controller_Battery battery;
@@ -672,6 +764,8 @@
* for a specified controller device running on a specified range of Android API
* levels.\n See `Paddleboat_Mapping_Axis` for axis order. Hat axis should be
* mapped to dpad buttons.
+ * @deprecated Use the `Paddleboat_Controller_Mapping_File_Header` in combination
+ * with the ::Paddleboat_addControllerRemapDataFromFileBuffer function instead.
*/
typedef struct Paddleboat_Controller_Mapping_Data {
/** @brief Minimum API level required for this entry */
@@ -740,18 +834,37 @@
/**
* @brief Signature of a function that can be passed to
- * ::Paddleboat_setMotionDataCallback to receive information about motion data events
+ * ::Paddleboat_setMotionDataCallback to receive information about motion data
+ events
* sent by connected controllers
* @param controllerIndex Index of the controller reporting the motion event,
- * will range from 0 to PADDLEBOAT_MAX_CONTROLLERS - 1.
- * @param motionData The motion data. Pointer is only valid until the callback returns.
+ * will range from 0 to PADDLEBOAT_MAX_CONTROLLERS - 1. If integrated motion
+ * sensor reporting was enabled, this value will equal
+ * `PADDLEBOAT_INTEGRATED_SENSOR_INDEX` if the motion event came from
+ * the integrated sensors on the main device, instead of a controller
+ * @param motionData The motion data. Pointer is only valid until the callback
+ returns.
* @param userData The value of the userData parameter passed
* to ::Paddleboat_setMotionDataCallback
*
*/
-typedef void (*Paddleboat_MotionDataCallback)(const int32_t controllerIndex,
- const Paddleboat_Motion_Data *motionData, void *userData);
+typedef void (*Paddleboat_MotionDataCallback)(
+ const int32_t controllerIndex, const Paddleboat_Motion_Data *motionData,
+ void *userData);
+
+/**
+ * @brief Signature of a function that can be passed to
+ * ::Paddleboat_setPhysicalKeyboardStatusCallback to receive information about
+ * physical keyboard connection status changes.
+ * @param physicalKeyboardStatus Whether a physical keyboard is currently connected.
+ * @param userData The value of the userData parameter passed
+ * to ::Paddleboat_setPhysicalKeyboardStatusCallback
+ *
+ * Function will be called on the same thread that calls ::Paddleboat_update.
+ */
+typedef void (*Paddleboat_PhysicalKeyboardStatusCallback)(
+ const bool physicalKeyboardStatus, void *userData);
/**
* @brief Initialize Paddleboat, constructing internal resources via JNI. This
@@ -848,6 +961,14 @@
bool Paddleboat_getBackButtonConsumed();
/**
+ * @brief Get availability information for motion data sensors integrated
+ * directly on the main device, instead of attached to a controller.
+ * @return The bitmask of integrated motion data sensors.
+ */
+
+Paddleboat_Integrated_Motion_Sensor_Flags Paddleboat_getIntegratedMotionSensorFlags();
+
+/**
* @brief Set whether Paddleboat consumes AKEYCODE_BACK key events from devices
* being managed by Paddleboat. The default at initialization is true. This can
* be set to false to allow exiting the application from a back button press
@@ -880,12 +1001,38 @@
* @param userData optional pointer (may be NULL or nullptr) to user data
* that will be passed as a parameter to the status callback. A reference
* to this pointer will be retained internally until changed by a future
- * call to ::Paddleboat_setMotionDataCallback
+ * call to ::Paddleboat_setMotionDataCallback or
+ * ::Paddleboat_setMotionDataCallbackWithIntegratedFlags
*/
void Paddleboat_setMotionDataCallback(
Paddleboat_MotionDataCallback motionDataCallback, void *userData);
/**
+ * @brief Set a callback which is called whenever a controller managed by
+ * Paddleboat reports a motion data event.
+ * @param motionDataCallback function pointer to the motion data callback,
+ * passing NULL or nullptr will remove any currently registered callback.
+ * @param integratedSensorFlags specifies if integrated device sensor data
+ * will be reported in the motion data callback. If a sensor flag bit is
+ * set, and the main device has that sensor, the motion data will be
+ * reported in the motion data callback.
+ * The ::Paddleboat_getIntegratedMotionSensorFlags function can be used
+ * to determine availability of integrated sensors.
+ * @param userData optional pointer (may be NULL or nullptr) to user data
+ * that will be passed as a parameter to the status callback. A reference
+ * to this pointer will be retained internally until changed by a future
+ * call to ::Paddleboat_setMotionDataCallback
+ * @return `PADDLEBOAT_NO_ERROR` if the callback was successfully registered,
+ * otherwise an error code. Attempting to register integrated sensor reporting
+ * if the specified sensor is not present will result in a
+ * `PADDLEBOAT_ERROR_FEATURE_NOT_SUPPORTED` error code.
+ */
+Paddleboat_ErrorCode Paddleboat_setMotionDataCallbackWithIntegratedFlags(
+ Paddleboat_MotionDataCallback motionDataCallback,
+ Paddleboat_Integrated_Motion_Sensor_Flags integratedSensorFlags,
+ void *userData);
+
+/**
* @brief Set a callback to be called when the mouse status changes. This is
* used to inform of physical or virual mouse device connections and
* disconnections.
@@ -901,6 +1048,22 @@
Paddleboat_MouseStatusCallback statusCallback, void *userData);
/**
+ * @brief Set a callback to be called when the physical keyboard connection.
+ * status changes. This is used to inform of connections or disconnections
+ * of a physical keyboard to the primary device.
+ * @param statusCallback function pointer to the keyboard status change
+ * callback, passing NULL or nullptr will remove any currently registered
+ * callback.
+ * @param userData optional pointer (may be NULL or nullptr) to user data that
+ * will be passed as a parameter to the status callback. A reference to this
+ * pointer will be retained internally until changed by a future call to
+ * ::Paddleboat_setPhysicalKeyboardStatusCallback
+ */
+void Paddleboat_setPhysicalKeyboardStatusCallback(
+ Paddleboat_PhysicalKeyboardStatusCallback statusCallback,
+ void *userData);
+
+/**
* @brief Retrieve the current controller data from the controller with the
* specified index.
* @param controllerIndex The index of the controller to read from, must be
@@ -998,7 +1161,15 @@
Paddleboat_MouseStatus Paddleboat_getMouseStatus();
/**
+ * @brief Retrieve the physical keyboard connection status for the device.
+ * @return Whether a physical keyboard is currently connected, as boolean.
+ */
+bool Paddleboat_getPhysicalKeyboardStatus();
+
+/**
* @brief Add new controller remap information to the internal remapping table.
+ * Used to specify custom controller information or override default mapping
+ * for a given controller.
* @param addMode The addition mode for the new data. See the
* `Paddleboat_Remap_Addition_Mode` enum for details on each mode.
* @param remapTableEntryCount the number of remap elements in the mappingData
@@ -1006,6 +1177,8 @@
* @param mappingData An array of controller mapping structs to be added to the
* internal remapping table. The pointer passed in mappingData is not retained
* and does not need to persist after function return.
+ * @deprecated Use ::Paddleboat_addControllerRemapDataFromFd or
+ * ::Paddleboat_addControllerRemapDataFromFileBuffer instead.
*/
void Paddleboat_addControllerRemapData(
const Paddleboat_Remap_Addition_Mode addMode,
@@ -1013,6 +1186,43 @@
const Paddleboat_Controller_Mapping_Data *mappingData);
/**
+ * @brief Add new controller remap information to the internal remapping table.
+ * Used to specify custom controller information or override default mapping
+ * for a given controller. For more information on controller mapping, see the
+ * documentation at:
+ * https://developer.android.com/games/sdk/game-controller/custom-mapping
+ * @param addMode The addition mode for the new data. See the
+ * `Paddleboat_Remap_Addition_Mode` enum for details on each mode.
+ * @param mappingFileDescriptor A file descriptor returned by a call to `open`.
+ * Paddleboat does not call 'close' on the file descriptor before returning, closing
+ * the file is the responsibility of the caller.
+ * @return `PADDLEBOAT_NO_ERROR` if successful, otherwise an error code.
+ */
+Paddleboat_ErrorCode Paddleboat_addControllerRemapDataFromFd(
+ const Paddleboat_Remap_Addition_Mode addMode,
+ const int mappingFileDescriptor);
+
+/**
+ * @brief Add new controller remap information to the internal remapping table.
+ * Used to specify custom controller information or override default mapping
+ * for a given controller. For more information on controller mapping, see the
+ * documentation at:
+ * https://developer.android.com/games/sdk/game-controller/custom-mapping
+ * @param addMode The addition mode for the new data. See the
+ * `Paddleboat_Remap_Addition_Mode` enum for details on each mode.
+ * @param mappingFileBuffer A pointer to a buffer containing a PaddleboatMappingTool
+ * file compatible with this version of the Paddleboat library. The
+ * beginning of the file is a `Paddleboat_Controller_Mapping_File_Header`.
+ * @param mappingFileBufferSize the size of the file in bytes passed in
+ * `mappingFileHeader`.
+ * @return `PADDLEBOAT_NO_ERROR` if successful, otherwise an error code.
+ */
+Paddleboat_ErrorCode Paddleboat_addControllerRemapDataFromFileBuffer(
+ const Paddleboat_Remap_Addition_Mode addMode,
+ const void *mappingFileBuffer,
+ const size_t mappingFileBufferSize);
+
+/**
* @brief Retrieve the current table of controller remap entries.
* @param destRemapTableEntryCount the number of
* `Paddleboat_Controller_Mapping_Data` entries in the array passed in the
@@ -1023,6 +1233,7 @@
* destRemapTableEntryCount elements. Passing nullptr is valid, and can be used
* to get the number of elements in the internal remap table.
* @return The number of elements in the internal remap table.
+ * @deprecated The number of elements returned will always be zero.
*/
int32_t Paddleboat_getControllerRemapTableData(
const int32_t destRemapTableEntryCount,
diff --git a/GameController/src/main/cpp/paddleboat_c.cpp b/games-controller/src/main/cpp/paddleboat_c.cpp
similarity index 77%
rename from GameController/src/main/cpp/paddleboat_c.cpp
rename to games-controller/src/main/cpp/paddleboat_c.cpp
index e8f5166..5b4d443 100644
--- a/GameController/src/main/cpp/paddleboat_c.cpp
+++ b/games-controller/src/main/cpp/paddleboat_c.cpp
@@ -21,15 +21,6 @@
extern "C" {
-// Internal macros to track Paddleboat version, do not use directly.
-#define PADDLEBOAT_MAJOR_VERSION 1
-#define PADDLEBOAT_MINOR_VERSION 1
-#define PADDLEBOAT_BUGFIX_VERSION 0
-
-#define PADDLEBOAT_PACKED_VERSION \
- ((PADDLEBOAT_MAJOR_VERSION << 24) | (PADDLEBOAT_MINOR_VERSION << 16) | \
- (PADDLEBOAT_BUGFIX_VERSION))
-
#define PADDLEBOAT_VERSION_CONCAT_NX(PREFIX, MAJOR, MINOR, BUGFIX) \
PREFIX##_##MAJOR##_##MINOR##_##BUGFIX
#define PADDLEBOAT_VERSION_CONCAT(PREFIX, MAJOR, MINOR, BUGFIX) \
@@ -41,6 +32,11 @@
void PADDLEBOAT_VERSION_SYMBOL();
+// Private, used internally by mapping file check
+uint32_t Paddleboat_getVersion() {
+ return PADDLEBOAT_PACKED_VERSION;
+}
+
Paddleboat_ErrorCode Paddleboat_init(JNIEnv *env, jobject jcontext) {
PADDLEBOAT_VERSION_SYMBOL();
Paddleboat_ErrorCode errorCode = GameControllerManager::init(env, jcontext);
@@ -86,6 +82,10 @@
return GameControllerManager::getBackButtonConsumed();
}
+Paddleboat_Integrated_Motion_Sensor_Flags Paddleboat_getIntegratedMotionSensorFlags() {
+ return GameControllerManager::getIntegratedMotionSensorFlags();
+}
+
void Paddleboat_setBackButtonConsumed(bool consumeBackButton) {
GameControllerManager::setBackButtonConsumed(consumeBackButton);
}
@@ -98,7 +98,16 @@
void Paddleboat_setMotionDataCallback(
Paddleboat_MotionDataCallback motionDataCallback, void *userData) {
- GameControllerManager::setMotionDataCallback(motionDataCallback, userData);
+ GameControllerManager::setMotionDataCallback(motionDataCallback,
+ PADDLEBOAT_INTEGRATED_SENSOR_NONE, userData);
+}
+
+Paddleboat_ErrorCode Paddleboat_setMotionDataCallbackWithIntegratedFlags(
+ Paddleboat_MotionDataCallback motionDataCallback,
+ Paddleboat_Integrated_Motion_Sensor_Flags integratedSensorFlags,
+ void *userData) {
+ return GameControllerManager::setMotionDataCallback(motionDataCallback,
+ integratedSensorFlags, userData);
}
void Paddleboat_setMouseStatusCallback(
@@ -106,6 +115,12 @@
GameControllerManager::setMouseStatusCallback(statusCallback, userData);
}
+void Paddleboat_setPhysicalKeyboardStatusCallback(
+ Paddleboat_PhysicalKeyboardStatusCallback statusCallback,
+ void *userData) {
+ GameControllerManager::setPhysicalKeyboardStatusCallback(statusCallback, userData);
+}
+
Paddleboat_ErrorCode Paddleboat_getControllerData(
const int32_t controllerIndex, Paddleboat_Controller_Data *controllerData) {
return GameControllerManager::getControllerData(controllerIndex,
@@ -133,8 +148,8 @@
Paddleboat_ErrorCode Paddleboat_setControllerLight(
const int32_t controllerIndex, const Paddleboat_LightType lightType,
const uint32_t lightData, JNIEnv *env) {
- return GameControllerManager::setControllerLight(controllerIndex,
- lightType, lightData, env);
+ return GameControllerManager::setControllerLight(controllerIndex, lightType,
+ lightData, env);
}
Paddleboat_ErrorCode Paddleboat_setControllerVibrationData(
@@ -152,6 +167,10 @@
return GameControllerManager::getMouseStatus();
}
+bool Paddleboat_getPhysicalKeyboardStatus() {
+ return GameControllerManager::getPhysicalKeyboardStatus();
+}
+
void Paddleboat_addControllerRemapData(
const Paddleboat_Remap_Addition_Mode addMode,
const int32_t remapTableEntryCount,
@@ -160,6 +179,21 @@
mappingData);
}
+Paddleboat_ErrorCode Paddleboat_addControllerRemapDataFromFd(
+ const Paddleboat_Remap_Addition_Mode addMode,
+ const int mappingFileDescriptor) {
+ return GameControllerManager::addControllerRemapDataFromFd(addMode, mappingFileDescriptor);
+}
+
+Paddleboat_ErrorCode Paddleboat_addControllerRemapDataFromFileBuffer(
+ const Paddleboat_Remap_Addition_Mode addMode,
+ const void *mappingFileBuffer,
+ const size_t mappingFileBufferSize) {
+ return GameControllerManager::addControllerRemapDataFromFileBuffer(addMode,
+ reinterpret_cast<const Paddleboat_Controller_Mapping_File_Header *>(mappingFileBuffer),
+ mappingFileBufferSize);
+}
+
int32_t Paddleboat_getControllerRemapTableData(
const int32_t destRemapTableEntryCount,
Paddleboat_Controller_Mapping_Data *mappingData) {
@@ -179,4 +213,4 @@
// In case of mismatch, a linker error will be triggered because of an
// undefined symbol, as the name of the function depends on the version.
}
-} // extern "C" {
\ No newline at end of file
+} // extern "C" {
\ No newline at end of file
diff --git a/GameController/src/main/java/com/google/android/games/paddleboat/GameControllerInfo.java b/games-controller/src/main/java/com/google/android/games/paddleboat/GameControllerInfo.java
similarity index 100%
rename from GameController/src/main/java/com/google/android/games/paddleboat/GameControllerInfo.java
rename to games-controller/src/main/java/com/google/android/games/paddleboat/GameControllerInfo.java
diff --git a/GameController/src/main/java/com/google/android/games/paddleboat/GameControllerListener.java b/games-controller/src/main/java/com/google/android/games/paddleboat/GameControllerListener.java
similarity index 71%
rename from GameController/src/main/java/com/google/android/games/paddleboat/GameControllerListener.java
rename to games-controller/src/main/java/com/google/android/games/paddleboat/GameControllerListener.java
index 35bf277..bc43913 100644
--- a/GameController/src/main/java/com/google/android/games/paddleboat/GameControllerListener.java
+++ b/games-controller/src/main/java/com/google/android/games/paddleboat/GameControllerListener.java
@@ -35,7 +35,12 @@
public class GameControllerListener {
private static final String TAG = "GameControllerListener";
+ // Must match Paddleboat_Motion_Data_Callback_Sensor_Index definition
+ // in paddleboat.h
+ private static final int INTEGRATED_SENSOR_INDEX = (0x40000000);
private boolean reportMotionEvents;
+ private boolean integratedAccelerometerActive;
+ private boolean integratedGyroscopeActive;
private int inputDeviceFlags;
private int inputDeviceId;
private final GameControllerManager gameControllerManager;
@@ -56,15 +61,24 @@
gameControllerManager = gcManager;
inputDevice = newDevice;
inputDeviceFlags = newFlags;
- inputDeviceId = inputDevice.getId();
+ // If a null input device is specified, we are a listener for integrated sensor
+ // devices
+ if (inputDevice != null) {
+ inputDeviceId = inputDevice.getId();
+ sensorManager = null;
+ } else {
+ inputDeviceId = INTEGRATED_SENSOR_INDEX;
+ sensorManager = gameControllerManager.getAppSensorManager();
+ }
reportMotionEvents = motionEvents;
+ integratedAccelerometerActive = false;
+ integratedGyroscopeActive = false;
lightsManager = null;
lightsSession = null;
accelerometer = null;
accelerometerListener = null;
gyroscope = null;
gyroscopeListener = null;
- sensorManager = null;
configureMotion();
}
@@ -73,12 +87,17 @@
shutdownListener();
inputDevice = newDevice;
inputDeviceFlags = newFlags;
- inputDeviceId = newDevice.getId();
+ if (inputDevice != null) {
+ inputDeviceId = inputDevice.getId();
+ } else {
+ inputDeviceId = INTEGRATED_SENSOR_INDEX;
+ sensorManager = gameControllerManager.getAppSensorManager();
+ }
configureMotion();
}
public void shutdownListener() {
- // Called when the device sends a disconnected or changed event
+ // Called when the controller device sends a disconnected or changed event,
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
Log.d(TAG, "shutdownListener");
synchronized (mLightLock) {
@@ -105,13 +124,74 @@
sensorManager = null;
}
}
- inputDeviceFlags = 0;
- inputDevice = null;
+ if (inputDevice != null) {
+ // Only reset if we are not an integrated listener
+ inputDeviceFlags = 0;
+ inputDevice = null;
+ }
}
- public void setReportMotionEvents() {
- reportMotionEvents = true;
- configureMotion();
+ public void setIntegratedAccelerometerActive(boolean active) {
+ synchronized (mSensorLock) {
+ if (sensorManager != null) {
+ if (active && accelerometerListener == null) {
+ accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
+ if (accelerometer != null) {
+ if (gameControllerManager.getPrintControllerInfo()) {
+ printSensorInformation(accelerometer, "integratedAccelerometer");
+ }
+ accelerometerListener =
+ new GameControllerAccelerometerListener(accelerometer);
+ Log.d(TAG, "registering listener for integrated accelerometer");
+ sensorManager.registerListener(accelerometerListener, accelerometer,
+ SensorManager.SENSOR_DELAY_GAME);
+ }
+ } else if (!active && accelerometerListener != null) {
+ if (accelerometerListener != null) {
+ Log.d(TAG, "unregistering listener for integrated accelerometer");
+ sensorManager.unregisterListener(accelerometerListener);
+ accelerometerListener = null;
+ }
+ }
+ integratedAccelerometerActive = active;
+ }
+ }
+ }
+
+ public void setIntegratedGyroscopeActive(boolean active) {
+ synchronized (mSensorLock) {
+ if (sensorManager != null) {
+ if (active && gyroscopeListener == null) {
+ gyroscope = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
+ if (gyroscope != null) {
+ if (gameControllerManager.getPrintControllerInfo()) {
+ printSensorInformation(gyroscope, "integratedGyroscope");
+ }
+ gyroscopeListener =
+ new GameControllerGyroscopeListener(gyroscope);
+ Log.d(TAG, "registering listener for integrated gyroscope");
+ sensorManager.registerListener(gyroscopeListener, gyroscope,
+ SensorManager.SENSOR_DELAY_GAME);
+ }
+ } else if (!active && gyroscopeListener != null) {
+ if (gyroscopeListener != null) {
+ Log.d(TAG, "unregistering listener for integrated gyroscope");
+ sensorManager.unregisterListener(gyroscopeListener);
+ gyroscopeListener = null;
+ }
+ }
+ integratedGyroscopeActive = active;
+ }
+ }
+ }
+
+ public void setReportMotionEvents(boolean motionEventsActive) {
+ reportMotionEvents = motionEventsActive;
+ if (motionEventsActive) {
+ configureMotion();
+ } else {
+ shutdownListener();
+ }
}
public void setLight(int lightType, int lightValue) {
@@ -162,9 +242,17 @@
}
private void configureMotion() {
+ if (inputDevice == null) {
+ // Integrated sensor reporting gets configured by the setIntegrated calls
+ return;
+ }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && reportMotionEvents) {
synchronized (mSensorLock) {
- sensorManager = inputDevice.getSensorManager();
+ if (sensorManager == null) {
+ // Integrated sensor gets assigned at creation, otherwise
+ // we have to set from the device's own sensor manager
+ sensorManager = inputDevice.getSensorManager();
+ }
if ((inputDeviceFlags & (GameControllerManager.DEVICEFLAG_ACCELEROMETER |
GameControllerManager.DEVICEFLAG_GYROSCOPE)) != 0) {
accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
diff --git a/GameController/src/main/java/com/google/android/games/paddleboat/GameControllerManager.java b/games-controller/src/main/java/com/google/android/games/paddleboat/GameControllerManager.java
similarity index 77%
rename from GameController/src/main/java/com/google/android/games/paddleboat/GameControllerManager.java
rename to games-controller/src/main/java/com/google/android/games/paddleboat/GameControllerManager.java
index 42ec9f2..ffd8ac5 100644
--- a/GameController/src/main/java/com/google/android/games/paddleboat/GameControllerManager.java
+++ b/games-controller/src/main/java/com/google/android/games/paddleboat/GameControllerManager.java
@@ -56,6 +56,8 @@
public static final int DEVICEFLAG_VIBRATION = 0x08000000;
public static final int DEVICEFLAG_VIBRATION_DUAL_MOTOR = 0x10000000;
public static final int DEVICEFLAG_VIRTUAL_MOUSE = 0x40000000;
+ public static final int INTEGRATEDFLAG_ACCELEROMETER = 0x1;
+ public static final int INTEGRATEDFLAG_GYROSCOPE = 0x2;
public static final int LIGHT_TYPE_PLAYER = 0;
public static final int LIGHT_TYPE_RGB = 1;
public static final int MOTION_ACCELEROMETER = 0;
@@ -66,17 +68,52 @@
private static final int GAMECONTROLLER_SOURCE_MASK =
InputDevice.SOURCE_DPAD | InputDevice.SOURCE_JOYSTICK | InputDevice.SOURCE_GAMEPAD;
private static final int MOUSE_SOURCE_MASK = InputDevice.SOURCE_MOUSE;
+ private static final int KEYBOARD_SOURCE_MASK = InputDevice.SOURCE_KEYBOARD;
private boolean nativeReady;
private final boolean printControllerInfo;
private boolean reportMotionEvents;
+ private int activeIntegratedSensorMask;
private final InputManager inputManager;
+ private final SensorManager sensorManager;
+ private final Sensor integratedAccelerometer;
+ private final Sensor integratedGyroscope;
+ private final GameControllerListener integratedListener;
+ private final ArrayList<Integer> keyboardDeviceIds;
private final ArrayList<Integer> mouseDeviceIds;
private final ArrayList<Integer> pendingControllerDeviceIds;
+ private final ArrayList<Integer> pendingKeyboardDeviceIds;
private final ArrayList<Integer> pendingMouseDeviceIds;
private final ArrayList<GameControllerInfo> gameControllers;
private GameControllerThread gameControllerThread;
public GameControllerManager(Context appContext, boolean appPrintControllerInfo) {
+ nativeReady = false;
+ reportMotionEvents = false;
+ activeIntegratedSensorMask = 0;
+ inputManager = (InputManager) appContext.getSystemService(Context.INPUT_SERVICE);
+ sensorManager = (SensorManager)appContext.getSystemService(Context.SENSOR_SERVICE);
+ integratedAccelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
+ integratedGyroscope = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
+ int integratedDeviceFlags = 0;
+ // We use the deviceflag instead of integrated flag for the listener settings,
+ // since the controller path is reused internally
+ if (integratedAccelerometer != null) {
+ integratedDeviceFlags |= DEVICEFLAG_ACCELEROMETER;
+ }
+ if (integratedGyroscope != null) {
+ integratedDeviceFlags |= DEVICEFLAG_GYROSCOPE;
+ }
+ // Initialize a listener for integrated sensors, defaulting to off at startup
+ integratedListener = new GameControllerListener(this, null,
+ integratedDeviceFlags, false);
+ printControllerInfo = true; //appPrintControllerInfo;
+ keyboardDeviceIds = new ArrayList<Integer>(MAX_GAMECONTROLLERS);
+ mouseDeviceIds = new ArrayList<Integer>(MAX_GAMECONTROLLERS);
+ pendingControllerDeviceIds = new ArrayList<Integer>(MAX_GAMECONTROLLERS);
+ pendingKeyboardDeviceIds = new ArrayList<Integer>(MAX_GAMECONTROLLERS);
+ pendingMouseDeviceIds = new ArrayList<Integer>(MAX_GAMECONTROLLERS);
+ gameControllers = new ArrayList<GameControllerInfo>(MAX_GAMECONTROLLERS);
+
if (appPrintControllerInfo) {
Log.d(TAG, "device Info:" +
"\n BRAND: " + Build.BRAND +
@@ -84,18 +121,11 @@
"\n MANUF: " + Build.MANUFACTURER +
"\n MODEL: " + Build.MODEL +
"\nPRODUCT: " + Build.PRODUCT +
- "\n API: " + Build.VERSION.SDK_INT);
+ "\n API: " + Build.VERSION.SDK_INT +
+ "\n ACCEL: " + (integratedAccelerometer != null) +
+ "\n GYRO: " + (integratedGyroscope != null));
}
- nativeReady = false;
- reportMotionEvents = false;
- inputManager = (InputManager) appContext.getSystemService(Context.INPUT_SERVICE);
- printControllerInfo = appPrintControllerInfo;
- mouseDeviceIds = new ArrayList<Integer>(MAX_GAMECONTROLLERS);
- pendingControllerDeviceIds = new ArrayList<Integer>(MAX_GAMECONTROLLERS);
- pendingMouseDeviceIds = new ArrayList<Integer>(MAX_GAMECONTROLLERS);
- gameControllers = new ArrayList<GameControllerInfo>(MAX_GAMECONTROLLERS);
-
// Queue up initially connected devices to be processed when
// the native side signals it is ready
scanDevices();
@@ -104,7 +134,7 @@
static public int getControllerFlagsForDevice(InputDevice inputDevice) {
int controllerFlags = 0;
- boolean hasVirtualMouse = isDeviceOfSource(inputDevice.getId(), MOUSE_SOURCE_MASK);
+ boolean hasVirtualMouse = isDeviceOfSource(inputDevice.getId(), MOUSE_SOURCE_MASK, true);
if (hasVirtualMouse) {
controllerFlags |= DEVICEFLAG_VIRTUAL_MOUSE;
}
@@ -172,7 +202,8 @@
return 0;
}
- private static boolean isDeviceOfSource(int deviceId, int matchingSourceMask) {
+ private static boolean isDeviceOfSource(int deviceId, int matchingSourceMask,
+ boolean needsMotionRanges) {
boolean isSource = false;
InputDevice inputDevice = InputDevice.getDevice(deviceId);
int inputDeviceSources = inputDevice.getSources();
@@ -181,7 +212,11 @@
if (inputDevice.isVirtual() == false) {
if ((inputDeviceSources & sourceMask) != 0) {
List<InputDevice.MotionRange> motionRanges = inputDevice.getMotionRanges();
- if (motionRanges.size() > 0) {
+ if (needsMotionRanges) {
+ if (motionRanges.size() > 0) {
+ isSource = true;
+ }
+ } else {
isSource = true;
}
}
@@ -198,6 +233,10 @@
return inputManager;
}
+ public SensorManager getAppSensorManager() {
+ return sensorManager;
+ }
+
public void onStop() {
if (gameControllerThread != null) {
gameControllerThread.onStop();
@@ -215,6 +254,37 @@
}
}
+ void checkForDeviceRemovals(int[] deviceIds, ArrayList<Integer> pendingDeviceIds,
+ ArrayList<Integer> activeDeviceIds) {
+ if (!nativeReady) {
+ for (int index = 0; index < pendingDeviceIds.size(); ++index) {
+ boolean foundDevice = false;
+ for (int deviceId : deviceIds) {
+ if (pendingDeviceIds.get(index) == deviceId) {
+ foundDevice = true;
+ break;
+ }
+ }
+ if (!foundDevice) {
+ pendingDeviceIds.remove(index--);
+ }
+ }
+ }
+ for (int index = 0; index < activeDeviceIds.size(); ++index) {
+ int activeDeviceId = activeDeviceIds.get(index);
+ boolean foundDevice = false;
+ for (int deviceId : deviceIds) {
+ if (activeDeviceId == deviceId) {
+ foundDevice = true;
+ break;
+ }
+ }
+ if (!foundDevice) {
+ onInputDeviceRemoved(activeDeviceId);
+ }
+ }
+ }
+
void checkForControllerRemovals(int[] deviceIds) {
if (!nativeReady) {
for (int index = 0; index < pendingControllerDeviceIds.size(); ++index) {
@@ -244,36 +314,6 @@
}
}
- void checkForMouseRemovals(int[] deviceIds) {
- if (!nativeReady) {
- for (int index = 0; index < pendingMouseDeviceIds.size(); ++index) {
- boolean foundDevice = false;
- for (int deviceId : deviceIds) {
- if (pendingMouseDeviceIds.get(index) == deviceId) {
- foundDevice = true;
- break;
- }
- }
- if (!foundDevice) {
- pendingMouseDeviceIds.remove(index--);
- }
- }
- }
- for (int index = 0; index < mouseDeviceIds.size(); ++index) {
- int mouseDeviceId = mouseDeviceIds.get(index);
- boolean foundDevice = false;
- for (int deviceId : deviceIds) {
- if (mouseDeviceId == deviceId) {
- foundDevice = true;
- break;
- }
- }
- if (!foundDevice) {
- onInputDeviceRemoved(mouseDeviceId);
- }
- }
- }
-
void processControllerAddition(int deviceId) {
boolean foundDevice = false;
if (!nativeReady) {
@@ -299,6 +339,31 @@
}
}
+ void processKeyboardAddition(int deviceId) {
+ boolean foundDevice = false;
+ if (!nativeReady) {
+ for (int index = 0; index < pendingKeyboardDeviceIds.size(); ++index) {
+ if (pendingKeyboardDeviceIds.get(index) == deviceId) {
+ foundDevice = true;
+ break;
+ }
+ }
+ if (!foundDevice) {
+ pendingKeyboardDeviceIds.add(deviceId);
+ }
+ } else {
+ for (int index = 0; index < keyboardDeviceIds.size(); ++index) {
+ if (keyboardDeviceIds.get(index) == deviceId) {
+ foundDevice = true;
+ break;
+ }
+ }
+ if (!foundDevice) {
+ onKeyboardDeviceAdded(deviceId);
+ }
+ }
+ }
+
void processMouseAddition(int deviceId) {
boolean foundDevice = false;
if (!nativeReady) {
@@ -319,14 +384,14 @@
}
}
if (!foundDevice) {
- onMouseAdded(deviceId);
+ onMouseDeviceAdded(deviceId);
}
}
}
boolean getIsGameController(int deviceId) {
boolean isGameController = false;
- if (isDeviceOfSource(deviceId, GAMECONTROLLER_SOURCE_MASK)) {
+ if (isDeviceOfSource(deviceId, GAMECONTROLLER_SOURCE_MASK, true)) {
InputDevice inputDevice = InputDevice.getDevice(deviceId);
if (inputDevice != null) {
String deviceName = inputDevice.getName();
@@ -345,19 +410,15 @@
// Scan for additions
for (int deviceId : deviceIds) {
- boolean isGameController = getIsGameController(deviceId);
- boolean isMouse = isDeviceOfSource(deviceId, MOUSE_SOURCE_MASK);
-
- if (isMouse && !isGameController) {
- processMouseAddition(deviceId);
- } else if (isGameController) {
- processControllerAddition(deviceId);
- }
+ // Reuse the onInputDeviceAdded method, the internal logic accounts for existing
+ // ids matching the current pending/active lists
+ onInputDeviceAdded(deviceId);
}
- // Scan for controller and mouse removals
+ // Scan for controller, keyboard, and mouse removals
checkForControllerRemovals(deviceIds);
- checkForMouseRemovals(deviceIds);
+ checkForDeviceRemovals(deviceIds, pendingKeyboardDeviceIds, keyboardDeviceIds);
+ checkForDeviceRemovals(deviceIds, pendingMouseDeviceIds, mouseDeviceIds);
}
GameControllerInfo onGameControllerAdded(int deviceId) {
@@ -378,7 +439,49 @@
return gameControllerInfo;
}
- void onMouseAdded(int deviceId) {
+ void onKeyboardDeviceAdded(int deviceId) {
+ if (printControllerInfo) {
+ Log.d(TAG, "onKeyboardDeviceAdded id: " + deviceId + " name: " +
+ InputDevice.getDevice(deviceId).getName());
+ logControllerInfo(deviceId);
+ }
+ keyboardDeviceIds.add(deviceId);
+ // Only send a connection message for the first keyboard device,
+ // secondary keyboards shouldn't trigger connection messages
+ if (keyboardDeviceIds.size() == 1) {
+ onKeyboardConnected(deviceId);
+ }
+ }
+
+ boolean onKeyboardDeviceRemoved(int deviceId) {
+ boolean removed = false;
+ // Remove from pending connected if it hadn't been processed yet
+ for (int index = 0; index < pendingKeyboardDeviceIds.size(); ++index) {
+ if (pendingKeyboardDeviceIds.get(index) == deviceId) {
+ pendingKeyboardDeviceIds.remove(index);
+ removed = true;
+ break;
+ }
+ }
+
+ for (int index = 0; index < keyboardDeviceIds.size(); ++index) {
+ if (keyboardDeviceIds.get(index) == deviceId) {
+ keyboardDeviceIds.remove(index);
+ if (nativeReady) {
+ // Only send a disconnection message if all keyboards
+ // have been removed
+ if (keyboardDeviceIds.size() == 0) {
+ onKeyboardDisconnected(deviceId);
+ }
+ }
+ removed = true;
+ break;
+ }
+ }
+ return removed;
+ }
+
+ void onMouseDeviceAdded(int deviceId) {
if (mouseDeviceIds.size() < MAX_MICE) {
if (printControllerInfo) {
Log.d(TAG, "onMouseDeviceAdded id: " + deviceId + " name: " +
@@ -439,18 +542,38 @@
public void onInputDeviceAdded(int deviceId) {
boolean isGameController = getIsGameController(deviceId);
- boolean isMouse = isDeviceOfSource(deviceId, MOUSE_SOURCE_MASK);
+ boolean isMouse = isDeviceOfSource(deviceId, MOUSE_SOURCE_MASK, true);
+ boolean isKeyboard = isDeviceOfSource(deviceId, KEYBOARD_SOURCE_MASK, false);
- if (isMouse && !isGameController) {
- processMouseAddition(deviceId);
- } else if (isGameController) {
+ // A game controller source device must be a game controller, even if it
+ // also reports the keyboard or mouse source
+ if (isGameController) {
processControllerAddition(deviceId);
+ } else {
+ // Non game controllers can be keyboard, mouse, or both at once
+ if (isMouse) {
+ processMouseAddition(deviceId);
+ }
+ if (isKeyboard) {
+ InputDevice inputDevice = inputManager.getInputDevice(deviceId);
+ if (inputDevice != null) {
+ // Ignore 'keyboard' source devices that don't provide an actual alphabet,
+ // some Android devices and mice may report additional 'keyboard' device ids,
+ // which we don't want to count as a hardware keyboard.
+ // Some gaming mice do declare an additional alphabet keyboard device,
+ // but we can't control that.
+ if (inputDevice.getKeyboardType() == InputDevice.KEYBOARD_TYPE_ALPHABETIC) {
+ processKeyboardAddition(deviceId);
+ }
+ }
+ }
}
}
public void onInputDeviceRemoved(int deviceId) {
onMouseDeviceRemoved(deviceId);
onGameControllerDeviceRemoved(deviceId);
+ onKeyboardDeviceRemoved(deviceId);
}
public void onInputDeviceChanged(int deviceId) {
@@ -492,6 +615,40 @@
return Build.VERSION.SDK_INT;
}
+ public int getIntegratedSensorFlags() {
+ int sensorFlags = 0;
+
+ if (integratedAccelerometer != null) {
+ sensorFlags |= INTEGRATEDFLAG_ACCELEROMETER;
+ }
+ if (integratedGyroscope != null) {
+ sensorFlags |= INTEGRATEDFLAG_GYROSCOPE;
+ }
+ return sensorFlags;
+ }
+
+ public void setActiveIntegratedSensors(int sensorMask) {
+ boolean setAccelerometerActive = ((sensorMask & INTEGRATEDFLAG_ACCELEROMETER) != 0);
+ boolean isAccelerometerActive =
+ ((activeIntegratedSensorMask & INTEGRATEDFLAG_ACCELEROMETER) != 0);
+ if (setAccelerometerActive && !isAccelerometerActive) {
+ integratedListener.setIntegratedAccelerometerActive(true);
+ } else if (!setAccelerometerActive && isAccelerometerActive) {
+ integratedListener.setIntegratedAccelerometerActive(false);
+ }
+
+ boolean setGyroscopeActive = ((sensorMask & INTEGRATEDFLAG_GYROSCOPE) != 0);
+ boolean isGyroscopeActive =
+ ((activeIntegratedSensorMask & INTEGRATEDFLAG_GYROSCOPE) != 0);
+ if (setGyroscopeActive && !isGyroscopeActive) {
+ integratedListener.setIntegratedGyroscopeActive(true);
+ } else if (!setGyroscopeActive && isGyroscopeActive) {
+ integratedListener.setIntegratedGyroscopeActive(false);
+ }
+
+ activeIntegratedSensorMask = sensorMask;
+ }
+
public void setNativeReady() {
nativeReady = true;
Log.d(TAG, "setNativeReady");
@@ -508,14 +665,14 @@
pendingControllerDeviceIds.clear();
for (int deviceId : pendingMouseDeviceIds) {
- onMouseAdded(deviceId);
+ onMouseDeviceAdded(deviceId);
}
}
public void setReportMotionEvents() {
reportMotionEvents = true;
for (GameControllerInfo controller : gameControllers) {
- controller.GetListener().setReportMotionEvents();
+ controller.GetListener().setReportMotionEvents(true);
}
}
@@ -779,7 +936,9 @@
float axisMax = motionRange.getMax();
float axisMin = motionRange.getMin();
float axisRange = motionRange.getRange();
- float axisResolution = motionRange.getResolution();
+ float axisResolution = -1;
+ if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.JELLY_BEAN_MR2)
+ axisResolution = motionRange.getResolution();
Log.d(TAG, "MotionRange:" +
"\n" + axisString +
@@ -794,12 +953,18 @@
private void logControllerInfo(int deviceId) {
InputDevice inputDevice = InputDevice.getDevice(deviceId);
- int controllerNumber = inputDevice.getControllerNumber();
+ int controllerNumber = -1;
+ if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.KITKAT)
+ controllerNumber = inputDevice.getControllerNumber();
String deviceDescriptor = inputDevice.getDescriptor();
String deviceName = inputDevice.getName();
- int deviceProductId = inputDevice.getProductId();
+ int deviceProductId = -1;
+ if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.KITKAT)
+ deviceProductId = inputDevice.getProductId();
int deviceSources = inputDevice.getSources();
- int deviceVendorId = inputDevice.getVendorId();
+ int deviceVendorId = -1;
+ if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.KITKAT)
+ deviceVendorId = inputDevice.getVendorId();
boolean hasVibrator = inputDevice.getVibrator().hasVibrator();
boolean isVirtual = inputDevice.isVirtual();
@@ -828,10 +993,14 @@
public native void onControllerDisconnected(int deviceId);
+ public native void onKeyboardConnected(int deviceId);
+
+ public native void onKeyboardDisconnected(int deviceid);
+
public native void onMotionData(int deviceId, int motionType, long timestamp,
float dataX, float dataY, float dataZ);
public native void onMouseConnected(int deviceId);
public native void onMouseDisconnected(int deviceId);
-}
\ No newline at end of file
+}
diff --git a/GameController/src/main/java/com/google/android/games/paddleboat/GameControllerThread.java b/games-controller/src/main/java/com/google/android/games/paddleboat/GameControllerThread.java
similarity index 100%
rename from GameController/src/main/java/com/google/android/games/paddleboat/GameControllerThread.java
rename to games-controller/src/main/java/com/google/android/games/paddleboat/GameControllerThread.java
diff --git a/GameController/src/test/cpp/CMakeLists.txt b/games-controller/src/test/cpp/CMakeLists.txt
similarity index 100%
rename from GameController/src/test/cpp/CMakeLists.txt
rename to games-controller/src/test/cpp/CMakeLists.txt
diff --git a/games-controller/src/test/cpp/paddleboat_tests.cpp b/games-controller/src/test/cpp/paddleboat_tests.cpp
new file mode 100644
index 0000000..cea0ea0
--- /dev/null
+++ b/games-controller/src/test/cpp/paddleboat_tests.cpp
@@ -0,0 +1,1294 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// This file contains unit tests for the GameControllerMappingUtils class
+
+#include <gtest/gtest.h>
+#include <string.h>
+#include "paddleboat.h"
+#include "../../main/cpp/GameControllerMappingFile.h"
+#include "../../main/cpp/GameControllerMappingUtils.h"
+
+using namespace paddleboat;
+using namespace std;
+
+#define ARRAY_COUNTOF(array) (sizeof(array) / sizeof(array[0]))
+#define PADDLEBOAT_AXIS_BUTTON_DPAD_UP 0
+#define PADDLEBOAT_AXIS_BUTTON_DPAD_LEFT 1
+#define PADDLEBOAT_AXIS_BUTTON_DPAD_DOWN 2
+#define PADDLEBOAT_AXIS_BUTTON_DPAD_RIGHT 3
+#define PADDLEBOAT_AXIS_BUTTON_L2 9
+#define PADDLEBOAT_AXIS_BUTTON_R2 12
+
+const IndexTableRemap pb_test_fake_remap[] = {
+ {0}
+};
+
+static Paddleboat_Controller_Mapping_File_Controller_Entry
+ testMappingTable[GameControllerMappingInfo::MAX_CONTROLLER_TABLE_SIZE];
+
+static Paddleboat_Controller_Mapping_File_String_Entry
+ testStringTable[GameControllerMappingInfo::MAX_STRING_TABLE_SIZE];
+
+static Paddleboat_Controller_Mapping_File_Axis_Entry
+ testAxisTable[GameControllerMappingInfo::MAX_AXIS_TABLE_SIZE];
+
+static Paddleboat_Controller_Mapping_File_Button_Entry
+ testButtonTable[GameControllerMappingInfo::MAX_BUTTON_TABLE_SIZE];
+
+static void InitSearchFromExistingEntry(
+ const Paddleboat_Controller_Mapping_File_Controller_Entry *sourceEntry,
+ MappingTableSearch &mapSearch) {
+ mapSearch.initSearchParameters(sourceEntry->vendorId, sourceEntry->productId,
+ sourceEntry->minimumEffectiveApiLevel,
+ sourceEntry->maximumEffectiveApiLevel);
+}
+
+static bool ControllerEqual(const Paddleboat_Controller_Mapping_File_Controller_Entry &a,
+ const Paddleboat_Controller_Mapping_File_Controller_Entry &b) {
+ EXPECT_EQ(a.minimumEffectiveApiLevel, b.minimumEffectiveApiLevel);
+ EXPECT_EQ(a.maximumEffectiveApiLevel, b.maximumEffectiveApiLevel);
+ EXPECT_EQ(a.vendorId, b.vendorId);
+ EXPECT_EQ(a.productId, b.productId);
+ EXPECT_EQ(a.flags, b.flags);
+ EXPECT_EQ(a.axisTableIndex, b.axisTableIndex);
+ EXPECT_EQ(a.buttonTableIndex, b.buttonTableIndex);
+ EXPECT_EQ(a.deviceAllowlistStringTableIndex, b.deviceAllowlistStringTableIndex);
+ EXPECT_EQ(a.deviceDenylistStringTableIndex, b.deviceDenylistStringTableIndex);
+
+ return (a.minimumEffectiveApiLevel == b.minimumEffectiveApiLevel &&
+ a.maximumEffectiveApiLevel == b.maximumEffectiveApiLevel &&
+ a.vendorId == b.vendorId && a.productId == b.productId && a.flags == b.flags &&
+ a.axisTableIndex == b.axisTableIndex && a.buttonTableIndex == b.buttonTableIndex &&
+ a.deviceAllowlistStringTableIndex == b.deviceAllowlistStringTableIndex &&
+ a.deviceDenylistStringTableIndex == b.deviceDenylistStringTableIndex);
+}
+// Make sure NotEmpty works
+TEST(PaddleboatTestNE, NotEmpty) {
+ EXPECT_NE(sizeof(Paddleboat_Controller_Data), 0);
+}
+
+// Make sure Validity works
+// Also checks assumptions on data structure sizes
+TEST(PaddleboatTestValidity, Validity)
+{
+ const size_t pcd_size = sizeof(Paddleboat_Controller_Data);
+ EXPECT_EQ(pcd_size, 64);
+ EXPECT_NE(pcd_size, 0);
+}
+
+// Validate table tests --=========================================================================
+// Test data
+// Validation failure test, improper ordering of vendorId
+const Paddleboat_Controller_Mapping_File_Controller_Entry pbtest_invalidmap_vendorid[] = {
+ { 16, 00, 0x0010, 0x0002, PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD, 0, 0, 0, 0 },
+ { 16, 00, 0x0020, 0x0002, PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD, 0, 0, 0, 0 },
+ { 16, 00, 0x0018, 0x0002, PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD, 0, 0, 0, 0 },
+ { 16, 00, 0x0030, 0x0002, PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD, 0, 0, 0, 0 },
+};
+
+// Validation failure test, improper ordering of productId
+const Paddleboat_Controller_Mapping_File_Controller_Entry pbtest_invalidmap_productid[] = {
+ { 16, 00, 0x0010, 0x0002, PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD, 0, 0, 0, 0 },
+ { 16, 00, 0x0018, 0x0002, PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD, 0, 0, 0, 0 },
+ { 16, 00, 0x0020, 0x0002, PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD, 0, 0, 0, 0 },
+ { 16, 00, 0x0020, 0x0001, PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD, 0, 0, 0, 0 },
+ { 16, 00, 0x0030, 0x0002, PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD, 0, 0, 0, 0 }
+};
+
+// Validation failure test, improper ordering of api levels
+const Paddleboat_Controller_Mapping_File_Controller_Entry pbtest_invalidmap_apilevel[] = {
+ { 16, 00, 0x0010, 0x0002, PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD, 0, 0, 0, 0 },
+ { 16, 00, 0x0018, 0x0002, PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD, 0, 0, 0, 0 },
+ { 19, 00, 0x0020, 0x0001, PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD, 0, 0, 0, 0 },
+ { 16, 00, 0x0020, 0x0001, PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD, 0, 0, 0, 0 },
+ { 16, 00, 0x0030, 0x0002, PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD, 0, 0, 0, 0 }
+};
+
+// Validation success test / find-insert tests baseline existing map
+const Paddleboat_Controller_Mapping_File_Controller_Entry pbtest_validmap[] = {
+ { 16, 00, 0x0010, 0x0002, PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD, 0, 0, 0, 0 },
+ { 16, 00, 0x0018, 0x0002, PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD, 0, 0, 0, 0 },
+ { 16, 18, 0x0020, 0x0002, PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD, 0, 0, 0, 0 },
+ { 19, 21, 0x0020, 0x0002, PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD, 0, 0, 0, 0 },
+ { 16, 00, 0x0020, 0x0004, PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD, 0, 0, 0, 0 },
+ { 16, 00, 0x0030, 0x0001, PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD, 0, 0, 0, 0 }
+};
+
+// Test validateMapTable catches improper vendorId ordering
+TEST(PaddleboatValidateMapVendorId, Validity) {
+ const int32_t entryCount = static_cast<const int32_t>(ARRAY_COUNTOF(pbtest_invalidmap_vendorid));
+ const Paddleboat_Controller_Mapping_File_Controller_Entry *errorEntry =
+ GameControllerMappingUtils::validateMapTable(pbtest_invalidmap_vendorid, entryCount);
+ EXPECT_NE(errorEntry, nullptr);
+ EXPECT_EQ(errorEntry->vendorId, 0x0018);
+}
+
+// Test validateMapTable catches improper productId ordering
+TEST(PaddleboatValidateMapProductId, Validity) {
+ const int32_t entryCount = static_cast<const int32_t>(ARRAY_COUNTOF(pbtest_invalidmap_productid));
+ const Paddleboat_Controller_Mapping_File_Controller_Entry *errorEntry =
+ GameControllerMappingUtils::validateMapTable(pbtest_invalidmap_productid, entryCount);
+ EXPECT_NE(errorEntry, nullptr);
+ EXPECT_EQ(errorEntry->vendorId, 0x0020);
+ EXPECT_EQ(errorEntry->productId, 0x0001);
+}
+
+// Test validateMapTable catches improper api level ordering
+TEST(PaddleboatValidateMapApiLevel, Validity) {
+ const int32_t entryCount = static_cast<const int32_t>(ARRAY_COUNTOF(pbtest_invalidmap_apilevel));
+ const Paddleboat_Controller_Mapping_File_Controller_Entry *errorEntry =
+ GameControllerMappingUtils::validateMapTable(pbtest_invalidmap_apilevel, entryCount);
+ EXPECT_NE(errorEntry, nullptr);
+ EXPECT_EQ(errorEntry->vendorId, 0x0020);
+ EXPECT_EQ(errorEntry->productId, 0x0001);
+ EXPECT_EQ(errorEntry->minimumEffectiveApiLevel, 16);
+}
+
+// Test validateMapTable returns nullptr on a successful validation
+TEST(PaddleboatValidateMapSuccess, Validity) {
+ const int32_t entryCount = static_cast<const int32_t>(ARRAY_COUNTOF(pbtest_validmap));
+ const Paddleboat_Controller_Mapping_File_Controller_Entry *errorEntry =
+ GameControllerMappingUtils::validateMapTable(pbtest_validmap, entryCount);
+ EXPECT_EQ(errorEntry, nullptr);
+}
+
+// Controller find test ===========================================================================
+// Test data
+
+TEST(PaddleboatValidateFindMapEntry, Validity) {
+ const int32_t entryCount = static_cast<const int32_t>(ARRAY_COUNTOF(pbtest_validmap));
+ memcpy(testMappingTable, pbtest_validmap, entryCount
+ * sizeof(Paddleboat_Controller_Mapping_File_Controller_Entry));
+ MappingTableSearch mapSearch(&testMappingTable[0], entryCount);
+
+ // Should find a match for the first entry
+ int32_t targetTableIndex = 0;
+ InitSearchFromExistingEntry(&testMappingTable[targetTableIndex], mapSearch);
+ bool foundEntry = GameControllerMappingUtils::findMatchingMapEntry(&mapSearch);
+ EXPECT_EQ(foundEntry, true);
+ EXPECT_EQ(mapSearch.tableIndex, targetTableIndex);
+
+ // Should find a match for the fourth entry
+ targetTableIndex = 3;
+ InitSearchFromExistingEntry(&testMappingTable[targetTableIndex], mapSearch);
+ foundEntry = GameControllerMappingUtils::findMatchingMapEntry(&mapSearch);
+ EXPECT_EQ(foundEntry, true);
+ EXPECT_EQ(mapSearch.tableIndex, targetTableIndex);
+
+ // Should fail to find a match and put insert point at the first entry
+ // (unique productId/vendorId)
+ mapSearch.initSearchParameters(0x1, 0x1, 16, 0);
+ foundEntry = GameControllerMappingUtils::findMatchingMapEntry(&mapSearch);
+ EXPECT_EQ(foundEntry,false);
+ EXPECT_EQ(mapSearch.tableIndex, 0);
+
+ // Should fail to find a match and put insert point at the third entry
+ // (matching productId/new vendorId)
+ mapSearch.initSearchParameters(0x20, 0x1, 16, 0);
+ foundEntry = GameControllerMappingUtils::findMatchingMapEntry(&mapSearch);
+ EXPECT_EQ(foundEntry, false);
+ EXPECT_EQ(mapSearch.tableIndex, 2);
+
+ // Should fail to find a match and put insert point at the fifth entry
+ // (matching productId/vendorId, new apiLevel)
+ mapSearch.initSearchParameters(0x20, 0x2, 22, 0);
+ foundEntry = GameControllerMappingUtils::findMatchingMapEntry(&mapSearch);
+ EXPECT_EQ(foundEntry, false);
+ EXPECT_EQ(mapSearch.tableIndex, 4);
+
+ // Should fail to find a match and put insert point at the sixth entry
+ // (unique productId/vendorId)
+ mapSearch.initSearchParameters(0x21, 0x1, 16, 0);
+ foundEntry = GameControllerMappingUtils::findMatchingMapEntry(&mapSearch);
+ EXPECT_EQ(foundEntry, false);
+ EXPECT_EQ(mapSearch.tableIndex, 5);
+
+ // Should fail to find a match and put the insert point at the end of the table
+ // (unique productId/vendorId)
+ mapSearch.initSearchParameters(0x40, 0x1, 16, 0);
+ foundEntry = GameControllerMappingUtils::findMatchingMapEntry(&mapSearch);
+ EXPECT_EQ(foundEntry, false);
+ EXPECT_EQ(mapSearch.tableIndex, entryCount);
+}
+
+// Controller insert test =========================================================================
+// Test data
+const Paddleboat_Controller_Mapping_File_Controller_Entry pbtest_add_entry1 = {
+ 16, 00, 0x0001, 0x0001, PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD, 0, 0, 0, 0
+};
+
+const Paddleboat_Controller_Mapping_File_Controller_Entry pbtest_add_entry2 = {
+ 16, 00, 0x0020, 0x0010, PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD, 0, 0, 0, 0
+};
+
+const Paddleboat_Controller_Mapping_File_Controller_Entry pbtest_add_entry3 = {
+ 16, 00, 0x0040, 0x0100, PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD, 0, 0, 0, 0
+};
+
+TEST(PaddleboatValidateInsertMapEntry, Validity)
+{
+ Paddleboat_ErrorCode errorCode = PADDLEBOAT_NO_ERROR;
+ const int32_t entryCount = static_cast<const int32_t>(ARRAY_COUNTOF(pbtest_validmap));
+ memset(testMappingTable, 0, GameControllerMappingInfo::MAX_CONTROLLER_TABLE_SIZE *
+ sizeof(Paddleboat_Controller_Mapping_File_Controller_Entry));
+ memcpy(testMappingTable, pbtest_validmap, entryCount *
+ sizeof(Paddleboat_Controller_Mapping_File_Controller_Entry));
+ MappingTableSearch mapSearch(&testMappingTable[0], entryCount);
+
+ // Insert should happen at beginning of table
+ InitSearchFromExistingEntry(&pbtest_add_entry1, mapSearch);
+ GameControllerMappingUtils::findMatchingMapEntry(&mapSearch);
+ errorCode = GameControllerMappingUtils::insertMapEntry(&pbtest_add_entry1, &mapSearch,
+ pb_test_fake_remap, pb_test_fake_remap);
+ EXPECT_EQ(errorCode, PADDLEBOAT_NO_ERROR);
+ EXPECT_EQ(testMappingTable[0].vendorId, pbtest_add_entry1.vendorId);
+ EXPECT_EQ(testMappingTable[0].productId, pbtest_add_entry1.productId);
+ EXPECT_EQ(testMappingTable[6].vendorId, 0x30);
+ EXPECT_EQ(testMappingTable[6].productId, 0x1);
+
+ // Insert should happen in next to last entry
+ // reset mapping table
+ memset(testMappingTable, 0, GameControllerMappingInfo::MAX_CONTROLLER_TABLE_SIZE *
+ sizeof(Paddleboat_Controller_Mapping_File_Controller_Entry));
+ memcpy(testMappingTable, pbtest_validmap, entryCount
+ * sizeof(Paddleboat_Controller_Mapping_File_Controller_Entry));
+ mapSearch.tableEntryCount = entryCount;
+
+ InitSearchFromExistingEntry(&pbtest_add_entry2, mapSearch);
+ GameControllerMappingUtils::findMatchingMapEntry(&mapSearch);
+ errorCode = GameControllerMappingUtils::insertMapEntry(&pbtest_add_entry2, &mapSearch,
+ pb_test_fake_remap, pb_test_fake_remap);
+ EXPECT_EQ(errorCode, PADDLEBOAT_NO_ERROR);
+ EXPECT_EQ(testMappingTable[5].vendorId, pbtest_add_entry2.vendorId);
+ EXPECT_EQ(testMappingTable[5].productId, pbtest_add_entry2.productId);
+ EXPECT_EQ(testMappingTable[6].vendorId, 0x30);
+ EXPECT_EQ(testMappingTable[6].productId, 0x1);
+
+ // Insert should be at end of table
+ // reset mapping table
+ memset(testMappingTable, 0, GameControllerMappingInfo::MAX_CONTROLLER_TABLE_SIZE *
+ sizeof(Paddleboat_Controller_Mapping_File_Controller_Entry));
+ memcpy(testMappingTable, pbtest_validmap, entryCount
+ * sizeof(Paddleboat_Controller_Mapping_File_Controller_Entry));
+ mapSearch.tableEntryCount = entryCount;
+
+ InitSearchFromExistingEntry(&pbtest_add_entry3, mapSearch);
+ GameControllerMappingUtils::findMatchingMapEntry(&mapSearch);
+ errorCode = GameControllerMappingUtils::insertMapEntry(&pbtest_add_entry3, &mapSearch,
+ pb_test_fake_remap, pb_test_fake_remap);
+ EXPECT_EQ(errorCode, PADDLEBOAT_NO_ERROR);
+ EXPECT_EQ(testMappingTable[5].vendorId, 0x30);
+ EXPECT_EQ(testMappingTable[5].productId, 0x1);
+ EXPECT_EQ(testMappingTable[6].vendorId, pbtest_add_entry3.vendorId);
+ EXPECT_EQ(testMappingTable[6].productId, pbtest_add_entry3.productId);
+
+ // Test failure on full table
+ // reset mapping table
+ memset(testMappingTable, 0, GameControllerMappingInfo::MAX_CONTROLLER_TABLE_SIZE *
+ sizeof(Paddleboat_Controller_Mapping_File_Controller_Entry));
+ memcpy(testMappingTable, pbtest_validmap, entryCount
+ * sizeof(Paddleboat_Controller_Mapping_File_Controller_Entry));
+ mapSearch.tableEntryCount = entryCount;
+
+ mapSearch.tableMaxEntryCount = entryCount;
+ InitSearchFromExistingEntry(&pbtest_add_entry3, mapSearch);
+ GameControllerMappingUtils::findMatchingMapEntry(&mapSearch);
+ errorCode = GameControllerMappingUtils::insertMapEntry(&pbtest_add_entry3, &mapSearch,
+ pb_test_fake_remap, pb_test_fake_remap);
+ EXPECT_EQ(errorCode, PADDLEBOAT_ERROR_FEATURE_NOT_SUPPORTED);
+}
+
+// String remap test ==============================================================================
+// Test data
+const Paddleboat_Controller_Mapping_File_String_Entry pbtest_stringstart[] = {
+ {"None"}, // 00
+ {"Generic_Axis"}, // 01
+ {"XB_Button"}, // 02
+ {"DS_Button"}, // 03
+ {"DS5_Compat_Button"}, // 04
+};
+
+const Paddleboat_Controller_Mapping_File_String_Entry pbtest_stringadd1[] = {
+ {"NinSPro_Button"}, // 05
+ {"Generic_Button"}, // 06
+};
+
+const Paddleboat_Controller_Mapping_File_String_Entry pbtest_stringadd2[] = {
+ {"Generic_Axis"}, // 01
+ {"XB_Button"}, // 02
+ {"NGPro_PC2_Button"}, // 07
+ {"DS5_Compat_Axis"} // 08
+};
+
+const Paddleboat_Controller_Mapping_File_String_Entry pbtest_stringadd3[] = {
+ {"Placeholder_space"}
+};
+
+TEST(PaddleboatValidateStringTableMerge, Validity)
+{
+ Paddleboat_ErrorCode errorCode = PADDLEBOAT_NO_ERROR;
+ memset(testStringTable, 0, GameControllerMappingInfo::MAX_STRING_TABLE_SIZE *
+ sizeof(Paddleboat_Controller_Mapping_File_String_Entry));
+ const uint32_t entryCount = static_cast<const uint32_t>(ARRAY_COUNTOF(pbtest_stringstart));
+ EXPECT_EQ(entryCount, 5);
+ memcpy(testStringTable, pbtest_stringstart, entryCount *
+ sizeof(Paddleboat_Controller_Mapping_File_String_Entry));
+ IndexTableRemap stringTableRemap[GameControllerMappingInfo::MAX_STRING_TABLE_SIZE];
+
+ // Expect two new strings appended to end of existing table, with two remap
+ // table entries
+ memset(stringTableRemap, 0, GameControllerMappingInfo::MAX_STRING_TABLE_SIZE *
+ sizeof(IndexTableRemap));
+ uint32_t currentEntryCount = entryCount;
+ const uint32_t add1Count = static_cast<const uint32_t>(ARRAY_COUNTOF(pbtest_stringadd1));
+ errorCode = GameControllerMappingUtils::mergeStringTable(pbtest_stringadd1, add1Count,
+ testStringTable, ¤tEntryCount, GameControllerMappingInfo::MAX_STRING_TABLE_SIZE,
+ stringTableRemap);
+ EXPECT_EQ(errorCode, PADDLEBOAT_NO_ERROR);
+ EXPECT_EQ(currentEntryCount, 7);
+ EXPECT_EQ(stringTableRemap[0].newIndex, 5); // index 0 old table, 5 new table
+ EXPECT_EQ(stringTableRemap[1].newIndex, 6); // index 1 old table, 6 new table
+ EXPECT_EQ(strcmp(testStringTable[5].stringTableEntry, pbtest_stringadd1[0].stringTableEntry), 0);
+ EXPECT_EQ(strcmp(testStringTable[6].stringTableEntry, pbtest_stringadd1[1].stringTableEntry), 0);
+
+ // Expect two new strings appended to end of existing table, with two remap table entries,
+ // and two skipped duplicates
+ memset(stringTableRemap, 0, GameControllerMappingInfo::MAX_STRING_TABLE_SIZE *
+ sizeof(IndexTableRemap));
+ const uint32_t add2Count = static_cast<const uint32_t>(ARRAY_COUNTOF(pbtest_stringadd2));
+ errorCode = GameControllerMappingUtils::mergeStringTable(pbtest_stringadd2, add2Count,
+ testStringTable, ¤tEntryCount, GameControllerMappingInfo::MAX_STRING_TABLE_SIZE,
+ stringTableRemap);
+ EXPECT_EQ(errorCode, PADDLEBOAT_NO_ERROR);
+ EXPECT_EQ(currentEntryCount, 9);
+ EXPECT_EQ(stringTableRemap[0].newIndex, 1); // index 0 old table, 1 new table
+ EXPECT_EQ(stringTableRemap[1].newIndex, 2); // index 1 old table, 2 new table
+ EXPECT_EQ(stringTableRemap[2].newIndex, 7); // index 2 old table, 7 new table
+ EXPECT_EQ(stringTableRemap[3].newIndex, 8); // index 3 old table, 8 new table
+ EXPECT_EQ(strcmp(testStringTable[7].stringTableEntry, pbtest_stringadd2[2].stringTableEntry), 0);
+ EXPECT_EQ(strcmp(testStringTable[8].stringTableEntry, pbtest_stringadd2[3].stringTableEntry), 0);
+
+ // Expect out of space error condition
+ currentEntryCount = GameControllerMappingInfo::MAX_STRING_TABLE_SIZE;
+ memset(stringTableRemap, 0, GameControllerMappingInfo::MAX_STRING_TABLE_SIZE *
+ sizeof(IndexTableRemap));
+ const uint32_t add3Count = static_cast<const uint32_t>(ARRAY_COUNTOF(pbtest_stringadd3));
+ errorCode = GameControllerMappingUtils::mergeStringTable(pbtest_stringadd3, add3Count,
+ testStringTable, ¤tEntryCount, GameControllerMappingInfo::MAX_STRING_TABLE_SIZE,
+ stringTableRemap);
+ EXPECT_EQ(errorCode, PADDLEBOAT_ERROR_FEATURE_NOT_SUPPORTED);
+}
+
+// Axis remap test ================================================================================
+// Test data
+
+const Paddleboat_Controller_Mapping_File_Axis_Entry pb_test_axis_start[] = {
+{
+ 1, // "Generic_Axis"
+ {
+ /* LX */ AMOTION_EVENT_AXIS_X,
+ /* LY */ AMOTION_EVENT_AXIS_Y,
+ /* RX */ AMOTION_EVENT_AXIS_Z,
+ /* RY */ AMOTION_EVENT_AXIS_RZ,
+ /* L1 */ PADDLEBOAT_AXIS_IGNORED,
+ /* L2 */ AMOTION_EVENT_AXIS_BRAKE,
+ /* R1 */ PADDLEBOAT_AXIS_IGNORED,
+ /* R2 */ AMOTION_EVENT_AXIS_GAS,
+ /* HX */ AMOTION_EVENT_AXIS_HAT_X,
+ /* HY */ AMOTION_EVENT_AXIS_HAT_Y,
+ },
+ {
+ /* LX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* LY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* L2 */ PADDLEBOAT_AXIS_BUTTON_L2,
+ /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* R2 */ PADDLEBOAT_AXIS_BUTTON_R2,
+ /* HX */ PADDLEBOAT_AXIS_BUTTON_DPAD_RIGHT,
+ /* HY */ PADDLEBOAT_AXIS_BUTTON_DPAD_DOWN,
+ },
+ {
+ /* LX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* LY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* L2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* R2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* HX */ PADDLEBOAT_AXIS_BUTTON_DPAD_LEFT,
+ /* HY */ PADDLEBOAT_AXIS_BUTTON_DPAD_UP,
+ },
+ 0
+}
+};
+
+const Paddleboat_Controller_Mapping_File_Axis_Entry pb_test_axis_add1[] = {
+{
+ 0, // "Foo_Axis"
+ {
+ /* LX */ AMOTION_EVENT_AXIS_Y,
+ /* LY */ AMOTION_EVENT_AXIS_X,
+ /* RX */ AMOTION_EVENT_AXIS_Z,
+ /* RY */ AMOTION_EVENT_AXIS_RZ,
+ /* L1 */ PADDLEBOAT_AXIS_IGNORED,
+ /* L2 */ AMOTION_EVENT_AXIS_BRAKE,
+ /* R1 */ PADDLEBOAT_AXIS_IGNORED,
+ /* R2 */ AMOTION_EVENT_AXIS_GAS,
+ /* HX */ AMOTION_EVENT_AXIS_HAT_X,
+ /* HY */ AMOTION_EVENT_AXIS_HAT_Y,
+ },
+ {
+ /* LX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* LY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* L2 */ PADDLEBOAT_AXIS_BUTTON_L2,
+ /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* R2 */ PADDLEBOAT_AXIS_BUTTON_R2,
+ /* HX */ PADDLEBOAT_AXIS_BUTTON_DPAD_RIGHT,
+ /* HY */ PADDLEBOAT_AXIS_BUTTON_DPAD_DOWN,
+ },
+ {
+ /* LX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* LY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* L2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* R2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* HX */ PADDLEBOAT_AXIS_BUTTON_DPAD_LEFT,
+ /* HY */ PADDLEBOAT_AXIS_BUTTON_DPAD_UP,
+ },
+ 0
+}
+};
+
+const Paddleboat_Controller_Mapping_File_Axis_Entry pb_test_axis_add2[] = {
+{
+ 0, // "Generic_Axis"
+ {
+ /* LX */ AMOTION_EVENT_AXIS_X,
+ /* LY */ AMOTION_EVENT_AXIS_Y,
+ /* RX */ AMOTION_EVENT_AXIS_Z,
+ /* RY */ AMOTION_EVENT_AXIS_RZ,
+ /* L1 */ PADDLEBOAT_AXIS_IGNORED,
+ /* L2 */ AMOTION_EVENT_AXIS_BRAKE,
+ /* R1 */ PADDLEBOAT_AXIS_IGNORED,
+ /* R2 */ AMOTION_EVENT_AXIS_GAS,
+ /* HX */ AMOTION_EVENT_AXIS_HAT_X,
+ /* HY */ AMOTION_EVENT_AXIS_HAT_Y,
+ },
+ {
+ /* LX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* LY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* L2 */ PADDLEBOAT_AXIS_BUTTON_L2,
+ /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* R2 */ PADDLEBOAT_AXIS_BUTTON_R2,
+ /* HX */ PADDLEBOAT_AXIS_BUTTON_DPAD_RIGHT,
+ /* HY */ PADDLEBOAT_AXIS_BUTTON_DPAD_DOWN,
+ },
+ {
+ /* LX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* LY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* L2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* R2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* HX */ PADDLEBOAT_AXIS_BUTTON_DPAD_LEFT,
+ /* HY */ PADDLEBOAT_AXIS_BUTTON_DPAD_UP,
+ },
+ 0
+},
+{
+ 1, // "Bar_Axis"
+ {
+ /* LX */ AMOTION_EVENT_AXIS_Z,
+ /* LY */ AMOTION_EVENT_AXIS_Y,
+ /* RX */ AMOTION_EVENT_AXIS_X,
+ /* RY */ AMOTION_EVENT_AXIS_RZ,
+ /* L1 */ PADDLEBOAT_AXIS_IGNORED,
+ /* L2 */ AMOTION_EVENT_AXIS_BRAKE,
+ /* R1 */ PADDLEBOAT_AXIS_IGNORED,
+ /* R2 */ AMOTION_EVENT_AXIS_GAS,
+ /* HX */ AMOTION_EVENT_AXIS_HAT_X,
+ /* HY */ AMOTION_EVENT_AXIS_HAT_Y,
+ },
+ {
+ /* LX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* LY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* L2 */ PADDLEBOAT_AXIS_BUTTON_L2,
+ /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* R2 */ PADDLEBOAT_AXIS_BUTTON_R2,
+ /* HX */ PADDLEBOAT_AXIS_BUTTON_DPAD_RIGHT,
+ /* HY */ PADDLEBOAT_AXIS_BUTTON_DPAD_DOWN,
+ },
+ {
+ /* LX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* LY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* L2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* R2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* HX */ PADDLEBOAT_AXIS_BUTTON_DPAD_LEFT,
+ /* HY */ PADDLEBOAT_AXIS_BUTTON_DPAD_UP,
+ },
+ 0
+}
+};
+
+const Paddleboat_Controller_Mapping_File_String_Entry pbtest_axisstringstart[] = {
+ {"None"}, // 00
+ {"Generic_Axis"}, // 01
+};
+
+const Paddleboat_Controller_Mapping_File_String_Entry pb_test_axisstr_add1[] = {
+ {"Foo_Axis"}, // 00
+};
+
+const Paddleboat_Controller_Mapping_File_String_Entry pb_test_axisstr_add2[] = {
+ {"Generic_Axis"}, // 00
+ {"Bar_Axis"}, // 01
+};
+
+const Paddleboat_Controller_Mapping_File_String_Entry pb_test_axisstr_add3[] = {
+ {"Why_Axis"}, // 00
+};
+
+TEST(PaddleboatValidateAxisTableMerge, Validity)
+{
+ Paddleboat_ErrorCode errorCode = PADDLEBOAT_NO_ERROR;
+ // Init axis table
+ memset(testAxisTable, 0, GameControllerMappingInfo::MAX_AXIS_TABLE_SIZE *
+ sizeof(Paddleboat_Controller_Mapping_File_Axis_Entry));
+ const uint32_t entryCount = static_cast<const uint32_t>(ARRAY_COUNTOF(pb_test_axis_start));
+ EXPECT_EQ(entryCount, 1);
+ memcpy(testAxisTable, pb_test_axis_start, entryCount *
+ sizeof(Paddleboat_Controller_Mapping_File_Axis_Entry));
+ // Init string table
+ memset(testStringTable, 0, GameControllerMappingInfo::MAX_STRING_TABLE_SIZE *
+ sizeof(Paddleboat_Controller_Mapping_File_String_Entry));
+ const uint32_t stringEntryCount = static_cast<const uint32_t>(
+ ARRAY_COUNTOF(pbtest_axisstringstart));
+ EXPECT_EQ(stringEntryCount, 2);
+ memcpy(testStringTable, pbtest_axisstringstart, stringEntryCount *
+ sizeof(Paddleboat_Controller_Mapping_File_String_Entry));
+ // Init remap tables
+ IndexTableRemap stringTableRemap[GameControllerMappingInfo::MAX_STRING_TABLE_SIZE];
+ IndexTableRemap axisTableRemap[GameControllerMappingInfo::MAX_AXIS_TABLE_SIZE];
+ memset(stringTableRemap, 0, GameControllerMappingInfo::MAX_STRING_TABLE_SIZE *
+ sizeof(IndexTableRemap));
+ memset(axisTableRemap, 0, GameControllerMappingInfo::MAX_AXIS_TABLE_SIZE *
+ sizeof(IndexTableRemap));
+
+ // Test 1, have to merge string table first
+ uint32_t currentStringEntryCount = stringEntryCount;
+ const uint32_t strAdd1Count = static_cast<const uint32_t>(ARRAY_COUNTOF(pb_test_axisstr_add1));
+ errorCode = GameControllerMappingUtils::mergeStringTable(pb_test_axisstr_add1, strAdd1Count,
+ testStringTable, ¤tStringEntryCount, GameControllerMappingInfo::MAX_STRING_TABLE_SIZE,
+ stringTableRemap);
+ EXPECT_EQ(errorCode, PADDLEBOAT_NO_ERROR);
+ EXPECT_EQ(stringTableRemap[0].newIndex, 2); // index 0 old table, 2 new table
+
+ // Should append one axis entry and remap its axis and string indices
+ uint32_t currentEntryCount = entryCount;
+ const uint32_t add1Count = static_cast<const uint32_t>(ARRAY_COUNTOF(pb_test_axis_add1));
+ errorCode = GameControllerMappingUtils::mergeAxisTable(pb_test_axis_add1, add1Count,
+ testAxisTable, ¤tEntryCount, GameControllerMappingInfo::MAX_AXIS_TABLE_SIZE,
+ stringTableRemap, axisTableRemap);
+ EXPECT_EQ(errorCode, PADDLEBOAT_NO_ERROR);
+ EXPECT_EQ(currentEntryCount, 2);
+ EXPECT_EQ(axisTableRemap[0].newIndex, 1); // index 0 old table, 1 new table
+ EXPECT_EQ(testAxisTable[0].axisNameStringTableIndex, 1);
+ EXPECT_EQ(testAxisTable[0].axisMapping[0], AMOTION_EVENT_AXIS_X);
+ EXPECT_EQ(testAxisTable[1].axisNameStringTableIndex, 2);
+ EXPECT_EQ(testAxisTable[1].axisMapping[0], AMOTION_EVENT_AXIS_Y);
+
+ // Test 2, reset remap tables and merge string table first
+ memset(stringTableRemap, 0, GameControllerMappingInfo::MAX_STRING_TABLE_SIZE *
+ sizeof(IndexTableRemap));
+ memset(axisTableRemap, 0, GameControllerMappingInfo::MAX_AXIS_TABLE_SIZE *
+ sizeof(IndexTableRemap));
+ const uint32_t strAdd2Count = static_cast<const uint32_t>(ARRAY_COUNTOF(pb_test_axisstr_add2));
+ errorCode = GameControllerMappingUtils::mergeStringTable(pb_test_axisstr_add2, strAdd2Count,
+ testStringTable, ¤tStringEntryCount, GameControllerMappingInfo::MAX_STRING_TABLE_SIZE,
+ stringTableRemap);
+ EXPECT_EQ(errorCode, PADDLEBOAT_NO_ERROR);
+ EXPECT_EQ(stringTableRemap[0].newIndex, 1); // index 0 old table, 1 new table
+ EXPECT_EQ(stringTableRemap[1].newIndex, 3); // index 1 old table, 3 new table
+
+ // Should append one axis entry and remap its axis and string indices
+ // and ignore a duplicate
+ const uint32_t add2Count = static_cast<const uint32_t>(ARRAY_COUNTOF(pb_test_axis_add2));
+ errorCode = GameControllerMappingUtils::mergeAxisTable(pb_test_axis_add2, add2Count,
+ testAxisTable, ¤tEntryCount, GameControllerMappingInfo::MAX_AXIS_TABLE_SIZE,
+ stringTableRemap, axisTableRemap);
+ EXPECT_EQ(errorCode, PADDLEBOAT_NO_ERROR);
+ EXPECT_EQ(currentEntryCount, 3);
+ EXPECT_EQ(axisTableRemap[0].newIndex, 0); // index 0 old table, 0 new table
+ EXPECT_EQ(axisTableRemap[1].newIndex, 2); // index 1 old table, 2 new table
+ EXPECT_EQ(testAxisTable[0].axisNameStringTableIndex, 1);
+ EXPECT_EQ(testAxisTable[0].axisMapping[0], AMOTION_EVENT_AXIS_X);
+ EXPECT_EQ(testAxisTable[1].axisNameStringTableIndex, 2);
+ EXPECT_EQ(testAxisTable[1].axisMapping[0], AMOTION_EVENT_AXIS_Y);
+ EXPECT_EQ(testAxisTable[2].axisNameStringTableIndex, 3);
+ EXPECT_EQ(testAxisTable[2].axisMapping[0], AMOTION_EVENT_AXIS_Z);
+
+ // Test 3, reset remap tables and merge string table first
+ memset(stringTableRemap, 0, GameControllerMappingInfo::MAX_STRING_TABLE_SIZE *
+ sizeof(IndexTableRemap));
+ memset(axisTableRemap, 0, GameControllerMappingInfo::MAX_AXIS_TABLE_SIZE *
+ sizeof(IndexTableRemap));
+ const uint32_t strAdd3Count = static_cast<const uint32_t>(ARRAY_COUNTOF(pb_test_axisstr_add3));
+ errorCode = GameControllerMappingUtils::mergeStringTable(pb_test_axisstr_add3, strAdd3Count,
+ testStringTable, ¤tStringEntryCount, GameControllerMappingInfo::MAX_STRING_TABLE_SIZE,
+ stringTableRemap);
+ EXPECT_EQ(errorCode, PADDLEBOAT_NO_ERROR);
+ EXPECT_EQ(stringTableRemap[0].newIndex, 4); // index 0 old table, 4 new table
+
+ // Should return no free space error, reuse add1 since the string table remap
+ // counts as a 'new' axis
+ currentEntryCount = GameControllerMappingInfo::MAX_AXIS_TABLE_SIZE;
+ const uint32_t add3Count = static_cast<const uint32_t>(ARRAY_COUNTOF(pb_test_axis_add1));
+ errorCode = GameControllerMappingUtils::mergeAxisTable(pb_test_axis_add1, add3Count,
+ testAxisTable, ¤tEntryCount, GameControllerMappingInfo::MAX_AXIS_TABLE_SIZE,
+ stringTableRemap, axisTableRemap);
+ EXPECT_EQ(errorCode, PADDLEBOAT_ERROR_FEATURE_NOT_SUPPORTED);
+}
+
+// Button remap test ==============================================================================
+// Test data
+const Paddleboat_Controller_Mapping_File_Button_Entry pb_test_button_start[] = {
+{
+ 2, // "XB_Button"
+ {
+ /* UP */ AKEYCODE_DPAD_UP,
+ /* LEFT */ AKEYCODE_DPAD_LEFT,
+ /* DOWN */ AKEYCODE_DPAD_DOWN,
+ /* RIGHT */ AKEYCODE_DPAD_RIGHT,
+ /* A */ AKEYCODE_BUTTON_A,
+ /* B */ AKEYCODE_BUTTON_B,
+ /* X */ AKEYCODE_BUTTON_X,
+ /* Y */ AKEYCODE_BUTTON_Y,
+ /* L1 */ AKEYCODE_BUTTON_L1,
+ /* L2 */ AKEYCODE_BUTTON_L2,
+ /* L3 */ AKEYCODE_BUTTON_THUMBL,
+ /* R1 */ AKEYCODE_BUTTON_R1,
+ /* R2 */ AKEYCODE_BUTTON_R2,
+ /* R3 */ AKEYCODE_BUTTON_THUMBR,
+ /* SELECT */ AKEYCODE_BUTTON_SELECT,
+ /* START */ AKEYCODE_BUTTON_START,
+ /* SYSTEM */ AKEYCODE_BUTTON_MODE,
+ /* TOUCHP */ PADDLEBOAT_BUTTON_IGNORED,
+ /* AUX1 */ AKEYCODE_MEDIA_RECORD,
+ /* AUX2 */ PADDLEBOAT_BUTTON_IGNORED,
+ /* AUX3 */ PADDLEBOAT_BUTTON_IGNORED,
+ /* AUX4 */ PADDLEBOAT_BUTTON_IGNORED
+ }
+}
+};
+
+const Paddleboat_Controller_Mapping_File_Button_Entry pb_test_button_add1[] = {
+{
+ 0, // "DS_Button"
+ {
+ /* UP */ AKEYCODE_DPAD_DOWN,
+ /* LEFT */ AKEYCODE_DPAD_LEFT,
+ /* DOWN */ AKEYCODE_DPAD_UP,
+ /* RIGHT */ AKEYCODE_DPAD_RIGHT,
+ /* A */ AKEYCODE_BUTTON_A,
+ /* B */ AKEYCODE_BUTTON_B,
+ /* X */ AKEYCODE_BUTTON_X,
+ /* Y */ AKEYCODE_BUTTON_Y,
+ /* L1 */ AKEYCODE_BUTTON_L1,
+ /* L2 */ AKEYCODE_BUTTON_L2,
+ /* L3 */ AKEYCODE_BUTTON_THUMBL,
+ /* R1 */ AKEYCODE_BUTTON_R1,
+ /* R2 */ AKEYCODE_BUTTON_R2,
+ /* R3 */ AKEYCODE_BUTTON_THUMBR,
+ /* SELECT */ AKEYCODE_BUTTON_SELECT,
+ /* START */ AKEYCODE_BUTTON_START,
+ /* SYSTEM */ AKEYCODE_BUTTON_MODE,
+ /* TOUCHP */ PADDLEBOAT_BUTTON_IGNORED,
+ /* AUX1 */ AKEYCODE_MEDIA_RECORD,
+ /* AUX2 */ PADDLEBOAT_BUTTON_IGNORED,
+ /* AUX3 */ PADDLEBOAT_BUTTON_IGNORED,
+ /* AUX4 */ PADDLEBOAT_BUTTON_IGNORED
+ }
+}
+};
+
+const Paddleboat_Controller_Mapping_File_Button_Entry pb_test_button_add2[] = {
+{
+ 0, // "XB_Button"
+ {
+ /* UP */ AKEYCODE_DPAD_UP,
+ /* LEFT */ AKEYCODE_DPAD_LEFT,
+ /* DOWN */ AKEYCODE_DPAD_DOWN,
+ /* RIGHT */ AKEYCODE_DPAD_RIGHT,
+ /* A */ AKEYCODE_BUTTON_A,
+ /* B */ AKEYCODE_BUTTON_B,
+ /* X */ AKEYCODE_BUTTON_X,
+ /* Y */ AKEYCODE_BUTTON_Y,
+ /* L1 */ AKEYCODE_BUTTON_L1,
+ /* L2 */ AKEYCODE_BUTTON_L2,
+ /* L3 */ AKEYCODE_BUTTON_THUMBL,
+ /* R1 */ AKEYCODE_BUTTON_R1,
+ /* R2 */ AKEYCODE_BUTTON_R2,
+ /* R3 */ AKEYCODE_BUTTON_THUMBR,
+ /* SELECT */ AKEYCODE_BUTTON_SELECT,
+ /* START */ AKEYCODE_BUTTON_START,
+ /* SYSTEM */ AKEYCODE_BUTTON_MODE,
+ /* TOUCHP */ PADDLEBOAT_BUTTON_IGNORED,
+ /* AUX1 */ AKEYCODE_MEDIA_RECORD,
+ /* AUX2 */ PADDLEBOAT_BUTTON_IGNORED,
+ /* AUX3 */ PADDLEBOAT_BUTTON_IGNORED,
+ /* AUX4 */ PADDLEBOAT_BUTTON_IGNORED
+ }
+},
+{
+ 1, // "SP_Button"
+ {
+ /* UP */ AKEYCODE_DPAD_LEFT,
+ /* LEFT */ AKEYCODE_DPAD_DOWN,
+ /* DOWN */ AKEYCODE_DPAD_RIGHT,
+ /* RIGHT */ AKEYCODE_DPAD_UP,
+ /* A */ AKEYCODE_BUTTON_A,
+ /* B */ AKEYCODE_BUTTON_B,
+ /* X */ AKEYCODE_BUTTON_X,
+ /* Y */ AKEYCODE_BUTTON_Y,
+ /* L1 */ AKEYCODE_BUTTON_L1,
+ /* L2 */ AKEYCODE_BUTTON_L2,
+ /* L3 */ AKEYCODE_BUTTON_THUMBL,
+ /* R1 */ AKEYCODE_BUTTON_R1,
+ /* R2 */ AKEYCODE_BUTTON_R2,
+ /* R3 */ AKEYCODE_BUTTON_THUMBR,
+ /* SELECT */ AKEYCODE_BUTTON_SELECT,
+ /* START */ AKEYCODE_BUTTON_START,
+ /* SYSTEM */ AKEYCODE_BUTTON_MODE,
+ /* TOUCHP */ PADDLEBOAT_BUTTON_IGNORED,
+ /* AUX1 */ AKEYCODE_MEDIA_RECORD,
+ /* AUX2 */ PADDLEBOAT_BUTTON_IGNORED,
+ /* AUX3 */ PADDLEBOAT_BUTTON_IGNORED,
+ /* AUX4 */ PADDLEBOAT_BUTTON_IGNORED
+ }
+}
+};
+
+const Paddleboat_Controller_Mapping_File_String_Entry pbtest_buttonstringstart[] = {
+ {"None"}, // 00
+ {"Generic_Axis"}, // 01
+ {"XB_Button"} // 02
+};
+
+const Paddleboat_Controller_Mapping_File_String_Entry pb_test_buttonstr_add1[] = {
+ {"DS_Button"}, // 00
+};
+
+const Paddleboat_Controller_Mapping_File_String_Entry pb_test_buttonstr_add2[] = {
+ {"XB_Button"}, // 00
+ {"SP_Button"}, // 01
+};
+
+const Paddleboat_Controller_Mapping_File_String_Entry pb_test_buttonstr_add3[] = {
+ {"Foo_Button"}, // 00
+};
+
+TEST(PaddleboatValidateButtonTableMerge, Validity)
+{
+ Paddleboat_ErrorCode errorCode = PADDLEBOAT_NO_ERROR;
+ // Init button table
+ memset(testButtonTable, 0, GameControllerMappingInfo::MAX_BUTTON_TABLE_SIZE *
+ sizeof(Paddleboat_Controller_Mapping_File_Button_Entry));
+ const uint32_t entryCount = static_cast<const uint32_t>(ARRAY_COUNTOF(pb_test_button_start));
+ EXPECT_EQ(entryCount, 1);
+ memcpy(testButtonTable, pb_test_button_start, entryCount *
+ sizeof(Paddleboat_Controller_Mapping_File_Button_Entry));
+ // Init string table
+ memset(testStringTable, 0, GameControllerMappingInfo::MAX_STRING_TABLE_SIZE *
+ sizeof(Paddleboat_Controller_Mapping_File_String_Entry));
+ const uint32_t stringEntryCount = static_cast<const uint32_t>(
+ ARRAY_COUNTOF(pbtest_buttonstringstart));
+ EXPECT_EQ(stringEntryCount, 3);
+ memcpy(testStringTable, pbtest_buttonstringstart, stringEntryCount *
+ sizeof(Paddleboat_Controller_Mapping_File_String_Entry));
+ // Init remap tables
+ IndexTableRemap stringTableRemap[GameControllerMappingInfo::MAX_STRING_TABLE_SIZE];
+ IndexTableRemap buttonTableRemap[GameControllerMappingInfo::MAX_BUTTON_TABLE_SIZE];
+ memset(stringTableRemap, 0, GameControllerMappingInfo::MAX_STRING_TABLE_SIZE *
+ sizeof(IndexTableRemap));
+ memset(buttonTableRemap, 0, GameControllerMappingInfo::MAX_BUTTON_TABLE_SIZE *
+ sizeof(IndexTableRemap));
+
+ // Test 1, have to merge string table first
+ uint32_t currentStringEntryCount = stringEntryCount;
+ const uint32_t strAdd1Count = static_cast<const uint32_t>(ARRAY_COUNTOF(pb_test_buttonstr_add1));
+ errorCode = GameControllerMappingUtils::mergeStringTable(pb_test_buttonstr_add1, strAdd1Count,
+ testStringTable, ¤tStringEntryCount, GameControllerMappingInfo::MAX_STRING_TABLE_SIZE,
+ stringTableRemap);
+ EXPECT_EQ(errorCode, PADDLEBOAT_NO_ERROR);
+ EXPECT_EQ(stringTableRemap[0].newIndex, 3); // index 0 old table, 3 new table
+
+ // Should append one button entry and remap its button and string indices
+ uint32_t currentEntryCount = entryCount;
+ const uint32_t add1Count = static_cast<const uint32_t>(ARRAY_COUNTOF(pb_test_button_add1));
+ errorCode = GameControllerMappingUtils::mergeButtonTable(pb_test_button_add1, add1Count,
+ testButtonTable, ¤tEntryCount, GameControllerMappingInfo::MAX_BUTTON_TABLE_SIZE,
+ stringTableRemap, buttonTableRemap);
+ EXPECT_EQ(errorCode, PADDLEBOAT_NO_ERROR);
+ EXPECT_EQ(currentEntryCount, 2);
+ EXPECT_EQ(buttonTableRemap[0].newIndex, 1); // index 0 old table, 1 new table
+ EXPECT_EQ(testButtonTable[0].buttonNameStringTableIndex, 2);
+ EXPECT_EQ(testButtonTable[0].buttonMapping[0], AKEYCODE_DPAD_UP);
+ EXPECT_EQ(testButtonTable[1].buttonNameStringTableIndex, 3);
+ EXPECT_EQ(testButtonTable[1].buttonMapping[0], AKEYCODE_DPAD_DOWN);
+
+ // Test 2, reset remap tables and merge string table first
+ memset(stringTableRemap, 0, GameControllerMappingInfo::MAX_STRING_TABLE_SIZE *
+ sizeof(IndexTableRemap));
+ memset(buttonTableRemap, 0, GameControllerMappingInfo::MAX_BUTTON_TABLE_SIZE *
+ sizeof(IndexTableRemap));
+ const uint32_t strAdd2Count = static_cast<const uint32_t>(ARRAY_COUNTOF(pb_test_buttonstr_add2));
+ errorCode = GameControllerMappingUtils::mergeStringTable(pb_test_buttonstr_add2, strAdd2Count,
+ testStringTable, ¤tStringEntryCount, GameControllerMappingInfo::MAX_STRING_TABLE_SIZE,
+ stringTableRemap);
+ EXPECT_EQ(errorCode, PADDLEBOAT_NO_ERROR);
+ EXPECT_EQ(stringTableRemap[0].newIndex, 2); // index 0 old table, 2 new table
+ EXPECT_EQ(stringTableRemap[1].newIndex, 4); // index 1 old table, 4 new table
+
+ // Should append one button entry and remap its button and string indices
+ // and ignore a duplicate
+ const uint32_t add2Count = static_cast<const uint32_t>(ARRAY_COUNTOF(pb_test_button_add2));
+ errorCode = GameControllerMappingUtils::mergeButtonTable(pb_test_button_add2, add2Count,
+ testButtonTable, ¤tEntryCount, GameControllerMappingInfo::MAX_BUTTON_TABLE_SIZE,
+ stringTableRemap, buttonTableRemap);
+ EXPECT_EQ(errorCode, PADDLEBOAT_NO_ERROR);
+ EXPECT_EQ(currentEntryCount, 3);
+ EXPECT_EQ(buttonTableRemap[0].newIndex, 0); // index 0 old table, 0 new table
+ EXPECT_EQ(buttonTableRemap[1].newIndex, 2); // index 1 old table, 2 new table
+ EXPECT_EQ(testButtonTable[0].buttonNameStringTableIndex, 2);
+ EXPECT_EQ(testButtonTable[0].buttonMapping[0], AKEYCODE_DPAD_UP);
+ EXPECT_EQ(testButtonTable[1].buttonNameStringTableIndex, 3);
+ EXPECT_EQ(testButtonTable[1].buttonMapping[0], AKEYCODE_DPAD_DOWN);
+ EXPECT_EQ(testButtonTable[2].buttonNameStringTableIndex, 4);
+ EXPECT_EQ(testButtonTable[2].buttonMapping[0], AKEYCODE_DPAD_LEFT);
+
+ // Test 3, reset remap tables and merge string table first
+ memset(stringTableRemap, 0, GameControllerMappingInfo::MAX_STRING_TABLE_SIZE *
+ sizeof(IndexTableRemap));
+ memset(buttonTableRemap, 0, GameControllerMappingInfo::MAX_BUTTON_TABLE_SIZE *
+ sizeof(IndexTableRemap));
+ const uint32_t strAdd3Count = static_cast<const uint32_t>(ARRAY_COUNTOF(pb_test_buttonstr_add3));
+ errorCode = GameControllerMappingUtils::mergeStringTable(pb_test_buttonstr_add3, strAdd3Count,
+ testStringTable, ¤tStringEntryCount, GameControllerMappingInfo::MAX_STRING_TABLE_SIZE,
+ stringTableRemap);
+ EXPECT_EQ(errorCode, PADDLEBOAT_NO_ERROR);
+ EXPECT_EQ(stringTableRemap[0].newIndex, 5); // index 0 old table, 5 new table
+
+ // Should return no free space error, reuse add1 since the string table remap
+ // counts as a 'new' button
+ currentEntryCount = GameControllerMappingInfo::MAX_BUTTON_TABLE_SIZE;
+ const uint32_t add3Count = static_cast<const uint32_t>(ARRAY_COUNTOF(pb_test_button_add1));
+ errorCode = GameControllerMappingUtils::mergeButtonTable(pb_test_button_add1, add3Count,
+ testButtonTable, ¤tEntryCount, GameControllerMappingInfo::MAX_BUTTON_TABLE_SIZE,
+ stringTableRemap, buttonTableRemap);
+ EXPECT_EQ(errorCode, PADDLEBOAT_ERROR_FEATURE_NOT_SUPPORTED);
+}
+
+// Controller file merge test =====================================================================
+// Test data
+
+const Paddleboat_Controller_Mapping_File_Controller_Entry pbtest_ctrl_base[] = {
+ { 16, 00, 0x0010, 0x0002, PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD, 1, 0, 0, 0 },
+ { 16, 00, 0x0018, 0x0002, PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD, 1, 0, 0, 0 },
+ { 16, 18, 0x0020, 0x0002, PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD, 2, 0, 0, 0 },
+ { 19, 00, 0x0020, 0x0002, PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD, 2, 0, 0, 0 },
+ { 16, 00, 0x0020, 0x0004, PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD, 1, 0, 0, 0 },
+ { 16, 00, 0x0030, 0x0001, PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD, 1, 0, 0, 0 }
+};
+
+const Paddleboat_Controller_Mapping_File_Controller_Entry pbtest_ctrl_file[] = {
+ { 21, 00, 0x0020, 0x0002, PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD, 0, 0, 0, 0 },
+ { 24, 00, 0x0004, 0x0001, PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD, 1, 0, 0, 0 }
+};
+
+/* post-merge should look like:
+const Paddleboat_Controller_Mapping_File_Controller_Entry pbtest_ctrl_merge[] = {
+ { 24, 00, 0x0004, 0x0001, PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD, 2, 1, 0, 0 }
+ { 16, 00, 0x0010, 0x0002, PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD, 1, 0, 0, 0 },
+ { 16, 00, 0x0018, 0x0002, PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD, 1, 0, 0, 0 },
+ { 16, 18, 0x0020, 0x0002, PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD, 2, 0, 0, 0 },
+ { 19, 20, 0x0020, 0x0002, PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD, 2, 0, 0, 0 },
+ { 21, 00, 0x0020, 0x0002, PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD, 1, 1, 0, 0 },
+ { 16, 00, 0x0020, 0x0004, PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD, 1, 0, 0, 0 },
+ { 16, 00, 0x0030, 0x0001, PADDLEBOAT_CONTROLLER_LAYOUT_STANDARD, 1, 0, 0, 0 }
+};
+
+ */
+
+const Paddleboat_Controller_Mapping_File_Axis_Entry pbtest_axis_base[] = {
+{
+ 1, // "Generic_Axis"
+ {
+ /* LX */ AMOTION_EVENT_AXIS_X,
+ /* LY */ AMOTION_EVENT_AXIS_Y,
+ /* RX */ AMOTION_EVENT_AXIS_Z,
+ /* RY */ AMOTION_EVENT_AXIS_RZ,
+ /* L1 */ PADDLEBOAT_AXIS_IGNORED,
+ /* L2 */ AMOTION_EVENT_AXIS_BRAKE,
+ /* R1 */ PADDLEBOAT_AXIS_IGNORED,
+ /* R2 */ AMOTION_EVENT_AXIS_GAS,
+ /* HX */ AMOTION_EVENT_AXIS_HAT_X,
+ /* HY */ AMOTION_EVENT_AXIS_HAT_Y,
+ },
+ {
+ /* LX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* LY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* L2 */ PADDLEBOAT_AXIS_BUTTON_L2,
+ /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* R2 */ PADDLEBOAT_AXIS_BUTTON_R2,
+ /* HX */ PADDLEBOAT_AXIS_BUTTON_DPAD_RIGHT,
+ /* HY */ PADDLEBOAT_AXIS_BUTTON_DPAD_DOWN,
+ },
+ {
+ /* LX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* LY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* L2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* R2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* HX */ PADDLEBOAT_AXIS_BUTTON_DPAD_LEFT,
+ /* HY */ PADDLEBOAT_AXIS_BUTTON_DPAD_UP,
+ },
+ 0
+},
+{
+ 2, // "Silly_Axis"
+ {
+ /* LX */ AMOTION_EVENT_AXIS_Z,
+ /* LY */ AMOTION_EVENT_AXIS_Y,
+ /* RX */ AMOTION_EVENT_AXIS_X,
+ /* RY */ AMOTION_EVENT_AXIS_RZ,
+ /* L1 */ PADDLEBOAT_AXIS_IGNORED,
+ /* L2 */ AMOTION_EVENT_AXIS_BRAKE,
+ /* R1 */ PADDLEBOAT_AXIS_IGNORED,
+ /* R2 */ AMOTION_EVENT_AXIS_GAS,
+ /* HX */ AMOTION_EVENT_AXIS_HAT_X,
+ /* HY */ AMOTION_EVENT_AXIS_HAT_Y,
+ },
+ {
+ /* LX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* LY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* L2 */ PADDLEBOAT_AXIS_BUTTON_L2,
+ /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* R2 */ PADDLEBOAT_AXIS_BUTTON_R2,
+ /* HX */ PADDLEBOAT_AXIS_BUTTON_DPAD_RIGHT,
+ /* HY */ PADDLEBOAT_AXIS_BUTTON_DPAD_DOWN,
+ },
+ {
+ /* LX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* LY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* L2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* R2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* HX */ PADDLEBOAT_AXIS_BUTTON_DPAD_LEFT,
+ /* HY */ PADDLEBOAT_AXIS_BUTTON_DPAD_UP,
+ },
+ 0
+}
+};
+
+const Paddleboat_Controller_Mapping_File_Axis_Entry pbtest_axis_file[] = {
+{
+ 2, // "Silly_Axis"
+ {
+ /* LX */ AMOTION_EVENT_AXIS_Z,
+ /* LY */ AMOTION_EVENT_AXIS_Y,
+ /* RX */ AMOTION_EVENT_AXIS_X,
+ /* RY */ AMOTION_EVENT_AXIS_RZ,
+ /* L1 */ PADDLEBOAT_AXIS_IGNORED,
+ /* L2 */ AMOTION_EVENT_AXIS_BRAKE,
+ /* R1 */ PADDLEBOAT_AXIS_IGNORED,
+ /* R2 */ AMOTION_EVENT_AXIS_GAS,
+ /* HX */ AMOTION_EVENT_AXIS_HAT_X,
+ /* HY */ AMOTION_EVENT_AXIS_HAT_Y,
+ },
+ {
+ /* LX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* LY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* L2 */ PADDLEBOAT_AXIS_BUTTON_L2,
+ /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* R2 */ PADDLEBOAT_AXIS_BUTTON_R2,
+ /* HX */ PADDLEBOAT_AXIS_BUTTON_DPAD_RIGHT,
+ /* HY */ PADDLEBOAT_AXIS_BUTTON_DPAD_DOWN,
+ },
+ {
+ /* LX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* LY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* L2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* R2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* HX */ PADDLEBOAT_AXIS_BUTTON_DPAD_LEFT,
+ /* HY */ PADDLEBOAT_AXIS_BUTTON_DPAD_UP,
+ },
+ 0
+},
+{
+ 4, // "Foo_Axis"
+ {
+ /* LX */ AMOTION_EVENT_AXIS_Y,
+ /* LY */ AMOTION_EVENT_AXIS_Z,
+ /* RX */ AMOTION_EVENT_AXIS_X,
+ /* RY */ AMOTION_EVENT_AXIS_RZ,
+ /* L1 */ PADDLEBOAT_AXIS_IGNORED,
+ /* L2 */ AMOTION_EVENT_AXIS_BRAKE,
+ /* R1 */ PADDLEBOAT_AXIS_IGNORED,
+ /* R2 */ AMOTION_EVENT_AXIS_GAS,
+ /* HX */ AMOTION_EVENT_AXIS_HAT_X,
+ /* HY */ AMOTION_EVENT_AXIS_HAT_Y,
+ },
+ {
+ /* LX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* LY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* L2 */ PADDLEBOAT_AXIS_BUTTON_L2,
+ /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* R2 */ PADDLEBOAT_AXIS_BUTTON_R2,
+ /* HX */ PADDLEBOAT_AXIS_BUTTON_DPAD_RIGHT,
+ /* HY */ PADDLEBOAT_AXIS_BUTTON_DPAD_DOWN,
+ },
+ {
+ /* LX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* LY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* RX */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* RY */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* L1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* L2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* R1 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* R2 */ PADDLEBOAT_AXIS_BUTTON_IGNORED,
+ /* HX */ PADDLEBOAT_AXIS_BUTTON_DPAD_LEFT,
+ /* HY */ PADDLEBOAT_AXIS_BUTTON_DPAD_UP,
+ },
+ 0
+}
+};
+
+const Paddleboat_Controller_Mapping_File_Button_Entry pbtest_button_base[] = {
+{
+ 3, // "XB_Button"
+ {
+ /* UP */ AKEYCODE_DPAD_UP,
+ /* LEFT */ AKEYCODE_DPAD_LEFT,
+ /* DOWN */ AKEYCODE_DPAD_DOWN,
+ /* RIGHT */ AKEYCODE_DPAD_RIGHT,
+ /* A */ AKEYCODE_BUTTON_A,
+ /* B */ AKEYCODE_BUTTON_B,
+ /* X */ AKEYCODE_BUTTON_X,
+ /* Y */ AKEYCODE_BUTTON_Y,
+ /* L1 */ AKEYCODE_BUTTON_L1,
+ /* L2 */ AKEYCODE_BUTTON_L2,
+ /* L3 */ AKEYCODE_BUTTON_THUMBL,
+ /* R1 */ AKEYCODE_BUTTON_R1,
+ /* R2 */ AKEYCODE_BUTTON_R2,
+ /* R3 */ AKEYCODE_BUTTON_THUMBR,
+ /* SELECT */ AKEYCODE_BUTTON_SELECT,
+ /* START */ AKEYCODE_BUTTON_START,
+ /* SYSTEM */ AKEYCODE_BUTTON_MODE,
+ /* TOUCHP */ PADDLEBOAT_BUTTON_IGNORED,
+ /* AUX1 */ AKEYCODE_MEDIA_RECORD,
+ /* AUX2 */ PADDLEBOAT_BUTTON_IGNORED,
+ /* AUX3 */ PADDLEBOAT_BUTTON_IGNORED,
+ /* AUX4 */ PADDLEBOAT_BUTTON_IGNORED
+ }
+}
+};
+
+const Paddleboat_Controller_Mapping_File_Button_Entry pbtest_button_file[] = {
+{
+ 5, // "Foo_Button"
+ {
+ /* UP */ AKEYCODE_DPAD_DOWN,
+ /* LEFT */ AKEYCODE_DPAD_LEFT,
+ /* DOWN */ AKEYCODE_DPAD_UP,
+ /* RIGHT */ AKEYCODE_DPAD_RIGHT,
+ /* A */ AKEYCODE_BUTTON_A,
+ /* B */ AKEYCODE_BUTTON_B,
+ /* X */ AKEYCODE_BUTTON_X,
+ /* Y */ AKEYCODE_BUTTON_Y,
+ /* L1 */ AKEYCODE_BUTTON_L1,
+ /* L2 */ AKEYCODE_BUTTON_L2,
+ /* L3 */ AKEYCODE_BUTTON_THUMBL,
+ /* R1 */ AKEYCODE_BUTTON_R1,
+ /* R2 */ AKEYCODE_BUTTON_R2,
+ /* R3 */ AKEYCODE_BUTTON_THUMBR,
+ /* SELECT */ AKEYCODE_BUTTON_SELECT,
+ /* START */ AKEYCODE_BUTTON_START,
+ /* SYSTEM */ AKEYCODE_BUTTON_MODE,
+ /* TOUCHP */ PADDLEBOAT_BUTTON_IGNORED,
+ /* AUX1 */ AKEYCODE_MEDIA_RECORD,
+ /* AUX2 */ PADDLEBOAT_BUTTON_IGNORED,
+ /* AUX3 */ PADDLEBOAT_BUTTON_IGNORED,
+ /* AUX4 */ PADDLEBOAT_BUTTON_IGNORED
+ }
+}
+};
+
+const Paddleboat_Controller_Mapping_File_String_Entry pbtest_string_base[] = {
+ {"None"}, // 00
+ {"Generic_Axis"}, // 01
+ {"Silly_Axis"}, // 02
+ {"XB_Button"} // 03
+};
+
+const Paddleboat_Controller_Mapping_File_String_Entry pbtest_string_file[] = {
+ {"None"}, // 00
+ {"Generic_Axis"}, // 01
+ {"Silly_Axis"}, // 02
+ {"XB_Button"}, // 03
+ {"Foo_Axis"}, // 04
+ {"Foo_Button"} // 05
+};
+
+// Test controller file merge
+TEST(PaddleboatValidateControllerFileMerge, Validity)
+{
+ // Initialize the 'base' data
+ GameControllerMappingInfo mappingInfo;
+ mappingInfo.mAxisTableEntryCount = static_cast<const uint32_t>(
+ ARRAY_COUNTOF(pbtest_axis_base));
+ memcpy(&mappingInfo.mAxisTable[0], pbtest_axis_base, mappingInfo.mAxisTableEntryCount
+ * sizeof(Paddleboat_Controller_Mapping_File_Axis_Entry));
+ mappingInfo.mButtonTableEntryCount = static_cast<const uint32_t>(
+ ARRAY_COUNTOF(pbtest_button_base));
+ memcpy(&mappingInfo.mButtonTable[0], pbtest_button_base, mappingInfo.mButtonTableEntryCount
+ * sizeof(Paddleboat_Controller_Mapping_File_Button_Entry));
+ mappingInfo.mControllerTableEntryCount = static_cast<const uint32_t>(
+ ARRAY_COUNTOF(pbtest_ctrl_base));
+ memcpy(&mappingInfo.mControllerTable[0], pbtest_ctrl_base, mappingInfo.mControllerTableEntryCount
+ * sizeof(Paddleboat_Controller_Mapping_File_Controller_Entry));
+ mappingInfo.mStringTableEntryCount = static_cast<const uint32_t>(
+ ARRAY_COUNTOF(pbtest_string_base));
+ memcpy(&mappingInfo.mStringTable[0], pbtest_string_base, mappingInfo.mStringTableEntryCount
+ * sizeof(Paddleboat_Controller_Mapping_File_String_Entry));
+
+ // Generate a fake 'file'
+ const uint32_t axisTableCount = static_cast<const uint32_t>(
+ ARRAY_COUNTOF(pbtest_axis_file));
+ const uint32_t buttonTableCount = static_cast<const uint32_t>(
+ ARRAY_COUNTOF(pbtest_button_file));
+ const uint32_t controllerTableCount = static_cast<const uint32_t>(
+ ARRAY_COUNTOF(pbtest_ctrl_file));
+ const uint32_t stringTableCount = static_cast<const uint32_t>(
+ ARRAY_COUNTOF(pbtest_string_file));
+ const size_t axisTableSize = axisTableCount *
+ sizeof(Paddleboat_Controller_Mapping_File_Axis_Entry);
+ const size_t buttonTableSize = buttonTableCount *
+ sizeof(Paddleboat_Controller_Mapping_File_Button_Entry);
+ const size_t controllerTableSize = controllerTableCount *
+ sizeof(Paddleboat_Controller_Mapping_File_Controller_Entry);
+ const size_t stringTableSize = stringTableCount *
+ sizeof(Paddleboat_Controller_Mapping_File_String_Entry);
+ const size_t headerSize = sizeof(Paddleboat_Controller_Mapping_File_Header);
+ const size_t fileSize = headerSize +
+ axisTableSize + buttonTableSize + controllerTableSize + stringTableSize;
+ uint8_t *basePtr = reinterpret_cast<uint8_t*>(malloc(fileSize));
+ memset(basePtr, 0, fileSize);
+ Paddleboat_Controller_Mapping_File_Header *fileHeader =
+ reinterpret_cast<Paddleboat_Controller_Mapping_File_Header*>(basePtr);
+ fileHeader->fileIdentifier = PADDLEBOAT_MAPPING_FILE_IDENTIFIER;
+ fileHeader->libraryMinimumVersion = 0x010200;
+ fileHeader->axisTableEntryCount = axisTableCount;
+ fileHeader->buttonTableEntryCount = buttonTableCount;
+ fileHeader->controllerTableEntryCount = controllerTableCount;
+ fileHeader->stringTableEntryCount = stringTableCount;
+ uint64_t offset = headerSize;
+ fileHeader->axisTableOffset = offset;
+ memcpy(&basePtr[offset], pbtest_axis_file, axisTableSize);
+ offset += axisTableSize;
+ fileHeader->buttonTableOffset = offset;
+ memcpy(&basePtr[offset], pbtest_button_file, buttonTableSize);
+ offset += buttonTableSize;
+ fileHeader->controllerTableOffset = offset;
+ memcpy(&basePtr[offset], pbtest_ctrl_file, controllerTableSize);
+ offset += controllerTableSize;
+ fileHeader->stringTableOffset = offset;
+ memcpy(&basePtr[offset], pbtest_string_file, stringTableSize);
+
+ Paddleboat_ErrorCode errorCode = GameControllerMappingUtils::mergeControllerRemapData(
+ fileHeader, fileSize, mappingInfo);
+ EXPECT_EQ(errorCode, PADDLEBOAT_NO_ERROR);
+ EXPECT_EQ(mappingInfo.mAxisTableEntryCount, 3);
+ EXPECT_EQ(mappingInfo.mButtonTableEntryCount, 2);
+ EXPECT_EQ(mappingInfo.mControllerTableEntryCount, 8);
+ EXPECT_EQ(mappingInfo.mStringTableEntryCount, 6);
+ // First entry should be the second entry from the 'file' with remapped button and axis
+ EXPECT_EQ(mappingInfo.mControllerTable[0].vendorId, pbtest_ctrl_file[1].vendorId);
+ EXPECT_EQ(mappingInfo.mControllerTable[0].productId, pbtest_ctrl_file[1].productId);
+ EXPECT_EQ(mappingInfo.mControllerTable[0].axisTableIndex, 2);
+ EXPECT_EQ(mappingInfo.mControllerTable[0].buttonTableIndex, 1);
+ // Second entry should be the first entry from the 'base', unmodified
+ EXPECT_EQ(ControllerEqual(mappingInfo.mControllerTable[1], pbtest_ctrl_base[0]), true);
+ // Fifth entry should be a 'patched' version of the fourth 'base' entry
+ EXPECT_EQ(mappingInfo.mControllerTable[4].vendorId, pbtest_ctrl_base[3].vendorId);
+ EXPECT_EQ(mappingInfo.mControllerTable[4].productId, pbtest_ctrl_base[3].productId);
+ EXPECT_EQ(mappingInfo.mControllerTable[4].minimumEffectiveApiLevel,
+ pbtest_ctrl_base[3].minimumEffectiveApiLevel);
+ EXPECT_EQ(mappingInfo.mControllerTable[4].maximumEffectiveApiLevel, 20);
+ // Sixth entry should be the first entry from the 'file' with remapped button
+ EXPECT_EQ(mappingInfo.mControllerTable[5].vendorId, pbtest_ctrl_file[0].vendorId);
+ EXPECT_EQ(mappingInfo.mControllerTable[5].productId, pbtest_ctrl_file[0].productId);
+ EXPECT_EQ(mappingInfo.mControllerTable[5].axisTableIndex, 1);
+ EXPECT_EQ(mappingInfo.mControllerTable[5].buttonTableIndex, 1);
+ EXPECT_EQ(mappingInfo.mControllerTable[5].minimumEffectiveApiLevel,
+ pbtest_ctrl_file[0].minimumEffectiveApiLevel);
+ EXPECT_EQ(mappingInfo.mControllerTable[5].maximumEffectiveApiLevel,
+ pbtest_ctrl_file[0].maximumEffectiveApiLevel);
+ free(basePtr);
+}
\ No newline at end of file
diff --git a/src/swappy/CMakeLists.txt b/games-frame-pacing/CMakeLists.txt
similarity index 65%
rename from src/swappy/CMakeLists.txt
rename to games-frame-pacing/CMakeLists.txt
index a5f30c6..a0ed30c 100644
--- a/src/swappy/CMakeLists.txt
+++ b/games-frame-pacing/CMakeLists.txt
@@ -1,6 +1,7 @@
cmake_minimum_required(VERSION 3.18.1)
project(swappy C CXX)
set(CMAKE_CXX_STANDARD 14)
+set(IgnoreOldToolchainWarning "${ANDROID_UNIFIED_HEADERS}")
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror -Wthread-safety" )
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D _LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS -O3 -fPIC" )
@@ -21,8 +22,8 @@
set ( SOURCE_LOCATION_OPENGL "${SOURCE_LOCATION}/opengl" )
set ( SOURCE_LOCATION_VULKAN "${SOURCE_LOCATION}/vulkan" )
-include_directories( ../../include )
-include_directories( ../common )
+include_directories( ../include )
+include_directories( ../src/common )
include_directories( common )
include_directories( opengl )
include_directories( vulkan )
@@ -30,9 +31,33 @@
message( STATUS "Building swappy_static to ${CMAKE_CURRENT_BINARY_DIR}/build" )
+
+# In order to support ChoreographerCallbacks back on older android versions we have a hack here
+# which adds the dex file from :games-frame-pacing:extras to our binary.
+# Android studio seems to use two different linkers and only one of those linkers requires we pass
+# in the emulation option "-m".
+# This linker which will be found in a path similar to this:
+# 21.4.7075529/toolchains/llvm/prebuilt/linux-x86_64/bin/i686-linux-android-ld
+# does not require the "-m" option.
+# The more general linker which can be found in a path similar to this:
+# 21.4.7075529/toolchains/llvm/prebuilt/linux-x86_64/bin/ld.lld
+# does require the "-m" option
+if(CMAKE_LINKER MATCHES ".*/ld.lld" AND ANDROID_NDK_MAJOR GREATER 17)
+ if ( ANDROID_NDK_ABI_NAME MATCHES "armeabi-v7a")
+ set (LINKER_TARGET_EMULATION_OPTION "-m" "armelf_linux_eabi")
+ elseif(ANDROID_NDK_ABI_NAME MATCHES "arm64-v8a")
+ set (LINKER_TARGET_EMULATION_OPTION "-m" "aarch64linux")
+ elseif(ANDROID_NDK_ABI_NAME MATCHES "x86_64")
+ set (LINKER_TARGET_EMULATION_OPTION "-m" "elf_x86_64")
+ elseif(ANDROID_NDK_ABI_NAME MATCHES "x86")
+ set (LINKER_TARGET_EMULATION_OPTION "-m" "elf_i386")
+ endif()
+endif()
+
add_custom_command(OUTPUT classes_dex.o
- COMMAND cd ../intermediates/dex/release/mergeDexRelease/out && ${CMAKE_LINKER} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/classes_dex.o classes.dex
- WORKING_DIRECTORY ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}
+ COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR}/extras/out/intermediates/dex/release/mergeDexRelease
+ && ${CMAKE_LINKER} ${LINKER_TARGET_EMULATION_OPTION} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/classes_dex.o classes.dex
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
set_source_files_properties(
@@ -55,6 +80,7 @@
${SOURCE_LOCATION_COMMON}/swappy_c.cpp
${SOURCE_LOCATION_COMMON}/SwappyDisplayManager.cpp
${SOURCE_LOCATION_COMMON}/CPUTracer.cpp
+ ${SOURCE_LOCATION_COMMON}/FrameStatistics.cpp
${SOURCE_LOCATION_OPENGL}/EGL.cpp
${SOURCE_LOCATION_OPENGL}/swappyGL_c.cpp
${SOURCE_LOCATION_OPENGL}/SwappyGL.cpp
@@ -64,7 +90,7 @@
${SOURCE_LOCATION_VULKAN}/SwappyVkBase.cpp
${SOURCE_LOCATION_VULKAN}/SwappyVkFallback.cpp
${SOURCE_LOCATION_VULKAN}/SwappyVkGoogleDisplayTiming.cpp
- ${SOURCE_LOCATION}/../common/system_utils.cpp
+ ${SOURCE_LOCATION}/../src/common/system_utils.cpp
${CMAKE_CURRENT_BINARY_DIR}/classes_dex.o
# Add new source files here
)
diff --git a/games-frame-pacing/OWNERS b/games-frame-pacing/OWNERS
new file mode 100644
index 0000000..b71f51b
--- /dev/null
+++ b/games-frame-pacing/OWNERS
@@ -0,0 +1 @@
+tomnom@google.com
diff --git a/games-frame-pacing/build.gradle b/games-frame-pacing/build.gradle
new file mode 100644
index 0000000..fc84306
--- /dev/null
+++ b/games-frame-pacing/build.gradle
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+plugins {
+ id 'com.android.library'
+}
+
+buildDir="../../out_swappy"
+
+android {
+
+ defaultConfig {
+ minSdkVersion 19
+ compileSdkVersion 31
+ targetSdkVersion 31
+ versionCode 1
+ versionName "1.1"
+
+ consumerProguardFiles "consumer-rules.pro"
+ externalNativeBuild {
+ cmake {
+ if (project.hasProperty("stl")) {
+ arguments '-DANDROID_STL='+ project.stl
+ } else {
+ arguments '-DANDROID_STL=c++_shared'
+ }
+ }
+ }
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+
+ externalNativeBuild {
+ cmake {
+ path "CMakeLists.txt"
+ version "3.18.1+"
+ }
+ }
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ buildFeatures {
+ prefabPublishing true
+ }
+ prefab {
+ swappy {
+ headers "include"
+ }
+
+ swappy_static {
+ headers "include"
+ }
+ }
+
+ // If we don't include this line the created .aar contains the c++ std lib
+ // at <.aar_file>/jni/<abi>/libc++_shared.so. When we have multiple
+ // libraries containing libc++_shared.so the linker complains because it
+ // can't choose between them. Because we use prefab we don't need to
+ // contents of the <.aar_file>/jni/* folder so we can just exclude it here
+ // to prevent the jni folder from being created.
+ packagingOptions {
+ exclude("**.so")
+ }
+}
+
+dependencies {
+ implementation "androidx.annotation:annotation:1.3.0"
+}
+repositories {
+ mavenCentral()
+}
+
+
+tasks.whenTaskAdded {
+ if (it.name.startsWith('buildCMake')) {
+ it.dependsOn ":games-frame-pacing:extras:assembleRelease"
+ }
+}
diff --git a/src/swappy/common/CPUTracer.cpp b/games-frame-pacing/common/CPUTracer.cpp
similarity index 96%
rename from src/swappy/common/CPUTracer.cpp
rename to games-frame-pacing/common/CPUTracer.cpp
index f91de8b..2114d06 100644
--- a/src/swappy/common/CPUTracer.cpp
+++ b/games-frame-pacing/common/CPUTracer.cpp
@@ -18,8 +18,8 @@
#include <memory>
-#include "../../common/Log.h"
-#include "../../common/Trace.h"
+#include "../../src/common/Log.h"
+#include "../../src/common/Trace.h"
namespace swappy {
diff --git a/src/swappy/common/CPUTracer.h b/games-frame-pacing/common/CPUTracer.h
similarity index 100%
rename from src/swappy/common/CPUTracer.h
rename to games-frame-pacing/common/CPUTracer.h
diff --git a/src/swappy/common/ChoreographerFilter.cpp b/games-frame-pacing/common/ChoreographerFilter.cpp
similarity index 100%
rename from src/swappy/common/ChoreographerFilter.cpp
rename to games-frame-pacing/common/ChoreographerFilter.cpp
diff --git a/src/swappy/common/ChoreographerFilter.h b/games-frame-pacing/common/ChoreographerFilter.h
similarity index 100%
rename from src/swappy/common/ChoreographerFilter.h
rename to games-frame-pacing/common/ChoreographerFilter.h
diff --git a/src/swappy/common/ChoreographerThread.cpp b/games-frame-pacing/common/ChoreographerThread.cpp
similarity index 98%
rename from src/swappy/common/ChoreographerThread.cpp
rename to games-frame-pacing/common/ChoreographerThread.cpp
index ef5fbad..34bd97e 100644
--- a/src/swappy/common/ChoreographerThread.cpp
+++ b/games-frame-pacing/common/ChoreographerThread.cpp
@@ -215,9 +215,9 @@
pthread_setname_np(pthread_self(), name);
while (mThreadRunning) {
- // mutex should be unlocked before sleeping on pollAll
+ // mutex should be unlocked before sleeping on pollOnce
mWaitingMutex.unlock();
- ALooper_pollAll(-1, &outFd, &outEvents, &outData);
+ ALooper_pollOnce(-1, &outFd, &outEvents, &outData);
mWaitingMutex.lock();
}
if (mAChoreographer_unregisterRefreshRateCallback &&
diff --git a/src/swappy/common/ChoreographerThread.h b/games-frame-pacing/common/ChoreographerThread.h
similarity index 100%
rename from src/swappy/common/ChoreographerThread.h
rename to games-frame-pacing/common/ChoreographerThread.h
diff --git a/src/swappy/common/CpuInfo.cpp b/games-frame-pacing/common/CpuInfo.cpp
similarity index 100%
rename from src/swappy/common/CpuInfo.cpp
rename to games-frame-pacing/common/CpuInfo.cpp
diff --git a/src/swappy/common/CpuInfo.h b/games-frame-pacing/common/CpuInfo.h
similarity index 100%
rename from src/swappy/common/CpuInfo.h
rename to games-frame-pacing/common/CpuInfo.h
diff --git a/games-frame-pacing/common/FrameStatistics.cpp b/games-frame-pacing/common/FrameStatistics.cpp
new file mode 100644
index 0000000..14013f2
--- /dev/null
+++ b/games-frame-pacing/common/FrameStatistics.cpp
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "FrameStatistics.h"
+
+#include "SwappyCommon.h"
+
+#define LOG_TAG "FrameStatistics"
+#include "Log.h"
+
+namespace swappy {
+
+// NB This is only needed for C++14
+constexpr std::chrono::nanoseconds FrameStatistics::LOG_EVERY_N_NS;
+
+int32_t FrameStatistics::getFrameDelta(int64_t deltaTimeNS,
+ uint64_t refreshPeriod) {
+ int32_t numFrames = deltaTimeNS / refreshPeriod;
+ numFrames = std::max(
+ 0, std::min(numFrames, static_cast<int32_t>(MAX_FRAME_BUCKETS) - 1));
+ return numFrames;
+}
+
+void FrameStatistics::clearStats() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mStats.totalFrames = 0;
+
+ for (int i = 0; i < MAX_FRAME_BUCKETS; i++) {
+ mStats.idleFrames[i] = 0;
+ mStats.lateFrames[i] = 0;
+ mStats.offsetFromPreviousFrame[i] = 0;
+ mStats.latencyFrames[i] = 0;
+ }
+}
+
+void FrameStatistics::invalidateLastFrame() { mLast = {0, 0, 0, 0}; }
+
+void FrameStatistics::updateFrameStats(FrameTimings current,
+ uint64_t refreshPeriod) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ // Latency is always collected
+ int latency = getFrameDelta(
+ current.actualPresentTime - current.startFrameTime, refreshPeriod);
+
+ // Use incoming frame timings to build the histogram.
+ if (mFullStatsEnabled) {
+ int idle = getFrameDelta(current.presentMargin, refreshPeriod);
+ int late = getFrameDelta(
+ current.actualPresentTime - current.desiredPresentTime,
+ refreshPeriod);
+
+ mStats.totalFrames++;
+ mStats.idleFrames[idle]++;
+ mStats.lateFrames[late]++;
+ mStats.latencyFrames[latency]++;
+
+ // Update the previous frame only if last frame has valid data
+ if (mLast.actualPresentTime) {
+ int offset = getFrameDelta(
+ current.actualPresentTime - mLast.actualPresentTime,
+ refreshPeriod);
+
+ mStats.offsetFromPreviousFrame[offset]++;
+ }
+
+ logFrames();
+ }
+
+ mLastLatency = latency;
+ mLast = current;
+}
+void FrameStatistics::logFrames() {
+ static auto previousLogTime = std::chrono::steady_clock::now();
+
+ if (std::chrono::steady_clock::now() - previousLogTime < LOG_EVERY_N_NS) {
+ return;
+ }
+
+ std::string message;
+ ALOGI("== Frame statistics ==");
+ ALOGI("total frames: %" PRIu64, mStats.totalFrames);
+ message += "Buckets: ";
+ for (int i = 0; i < MAX_FRAME_BUCKETS; i++)
+ message += "\t[" + swappy::to_string(i) + "]";
+ ALOGI("%s", message.c_str());
+
+ message = "";
+ message += "idle frames: ";
+ for (int i = 0; i < MAX_FRAME_BUCKETS; i++)
+ message += "\t " + swappy::to_string(mStats.idleFrames[i]);
+ ALOGI("%s", message.c_str());
+
+ message = "";
+ message += "late frames: ";
+ for (int i = 0; i < MAX_FRAME_BUCKETS; i++)
+ message += "\t " + swappy::to_string(mStats.lateFrames[i]);
+ ALOGI("%s", message.c_str());
+
+ message = "";
+ message += "offset from previous frame: ";
+ for (int i = 0; i < MAX_FRAME_BUCKETS; i++)
+ message += "\t " + swappy::to_string(mStats.offsetFromPreviousFrame[i]);
+ ALOGI("%s", message.c_str());
+
+ message = "";
+ message += "frame latency: ";
+ for (int i = 0; i < MAX_FRAME_BUCKETS; i++)
+ message += "\t " + swappy::to_string(mStats.latencyFrames[i]);
+ ALOGI("%s", message.c_str());
+
+ previousLogTime = std::chrono::steady_clock::now();
+}
+
+void FrameStatistics::enableStats(bool enabled) { mFullStatsEnabled = enabled; }
+
+SwappyStats FrameStatistics::getStats() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return mStats;
+}
+
+} // namespace swappy
diff --git a/games-frame-pacing/common/FrameStatistics.h b/games-frame-pacing/common/FrameStatistics.h
new file mode 100644
index 0000000..9ac0f76
--- /dev/null
+++ b/games-frame-pacing/common/FrameStatistics.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <swappy/swappy_common.h>
+
+#include <atomic>
+#include <chrono>
+#include <mutex>
+
+#include "Thread.h"
+
+using namespace std::chrono_literals;
+
+namespace swappy {
+
+/* FrameTimings is defined so that EGL & vulkan can convert their timestamps
+ * into this common struct. Within the common frame statistics, this is all the
+ * information that is needed.
+ */
+typedef struct {
+ uint64_t startFrameTime;
+ uint64_t desiredPresentTime;
+ uint64_t actualPresentTime;
+ uint64_t presentMargin;
+} FrameTimings;
+
+class FrameStatistics {
+ public:
+ ~FrameStatistics() = default;
+
+ void enableStats(bool enabled);
+ void updateFrameStats(FrameTimings currentFrameTimings,
+ uint64_t refreshPeriod);
+ SwappyStats getStats();
+ void clearStats();
+ void invalidateLastFrame();
+
+ int32_t lastLatencyRecorded() { return mLastLatency; }
+
+ private:
+ static constexpr std::chrono::nanoseconds LOG_EVERY_N_NS = 1s;
+ void logFrames() REQUIRES(mMutex);
+
+ int32_t getFrameDelta(int64_t deltaTimeNS, uint64_t refreshPeriod);
+
+ std::mutex mMutex;
+ SwappyStats mStats GUARDED_BY(mMutex) = {};
+ std::atomic<int32_t> mLastLatency = {0};
+ FrameTimings mLast;
+
+ // A flag to enable or disable frame stats histogram update.
+ bool mFullStatsEnabled = false;
+};
+
+} // namespace swappy
diff --git a/src/swappy/common/Settings.cpp b/games-frame-pacing/common/Settings.cpp
similarity index 100%
rename from src/swappy/common/Settings.cpp
rename to games-frame-pacing/common/Settings.cpp
diff --git a/src/swappy/common/Settings.h b/games-frame-pacing/common/Settings.h
similarity index 100%
rename from src/swappy/common/Settings.h
rename to games-frame-pacing/common/Settings.h
diff --git a/src/swappy/common/SwappyCommon.cpp b/games-frame-pacing/common/SwappyCommon.cpp
similarity index 96%
rename from src/swappy/common/SwappyCommon.cpp
rename to games-frame-pacing/common/SwappyCommon.cpp
index e9ae061..69d56bd 100644
--- a/src/swappy/common/SwappyCommon.cpp
+++ b/games-frame-pacing/common/SwappyCommon.cpp
@@ -167,8 +167,8 @@
ALOGE("failed to initialize ChoreographerThread");
return;
}
- if (USE_DISPLAY_MANAGER && SwappyDisplayManager::useSwappyDisplayManager(
- mCommonSettings.sdkVersion)) {
+ if (USE_DISPLAY_MANAGER &&
+ SwappyDisplayManager::usesMinSdkOrLater(mCommonSettings.sdkVersion)) {
mDisplayManager =
std::make_unique<SwappyDisplayManager>(mJVM, jactivity);
@@ -289,6 +289,12 @@
int lateFrames = 0;
bool presentationTimeIsNeeded;
+ // We do not want to hold the mutex while waiting, so make a local copy of
+ // the flag.
+ mMutex.lock();
+ bool localAutoSwapIntervalEnabled = mAutoSwapIntervalEnabled;
+ mMutex.unlock();
+
const nanoseconds cpuTime =
(mStartFrameTime.time_since_epoch().count() == 0)
? 0ns
@@ -297,10 +303,12 @@
preWaitCallbacks();
- // if we are running slower than the threshold there is no point to sleep,
+ // if we are running slower than the threshold (if auto swap interval is
+ // enabled) there is no point to sleep,
// just let the app run as fast as it can
if (mCommonSettings.refreshPeriod * mAutoSwapInterval <=
- mAutoSwapIntervalThreshold.load()) {
+ mAutoSwapIntervalThreshold.load() ||
+ !localAutoSwapIntervalEnabled) {
waitUntilTargetFrame();
// wait for the previous frame to be rendered
@@ -838,8 +846,8 @@
const int intervals = (mPipelineMode == PipelineMode::On) ? 2 : 1;
// Use frame statistics to fix any buffer stuffing
- if (mBufferStuffingFixWait > 0 && mFrameStatistics) {
- int32_t lastLatency = mFrameStatistics->lastLatencyRecorded();
+ if (mBufferStuffingFixWait > 0 && mLastLatencyRecorded) {
+ int32_t lastLatency = mLastLatencyRecorded();
int expectedLatency = mAutoSwapInterval * intervals;
TRACE_INT("ExpectedLatency", expectedLatency);
if (mBufferStuffingFixCounter == 0) {
@@ -1031,4 +1039,18 @@
return false;
}
+int SwappyCommon::getSupportedRefreshPeriodsNS(uint64_t* out_refreshrates,
+ int allocated_entries) {
+ if (!mSupportedRefreshPeriods) return 0;
+ if (!out_refreshrates) return (*mSupportedRefreshPeriods).size();
+
+ int counter = 0;
+ for (const auto& pair : *mSupportedRefreshPeriods) {
+ out_refreshrates[counter] = pair.first.count();
+ ++counter;
+ }
+
+ return (*mSupportedRefreshPeriods).size();
+}
+
} // namespace swappy
diff --git a/src/swappy/common/SwappyCommon.h b/games-frame-pacing/common/SwappyCommon.h
similarity index 96%
rename from src/swappy/common/SwappyCommon.h
rename to games-frame-pacing/common/SwappyCommon.h
index a9496ab..ce03dfa 100644
--- a/src/swappy/common/SwappyCommon.h
+++ b/games-frame-pacing/common/SwappyCommon.h
@@ -28,7 +28,6 @@
#include "CPUTracer.h"
#include "ChoreographerFilter.h"
#include "ChoreographerThread.h"
-#include "FrameStatistics.h"
#include "SwappyDisplayManager.h"
#include "Thread.h"
#include "swappy/swappyGL.h"
@@ -118,15 +117,17 @@
void setANativeWindow(ANativeWindow* window);
- void setFrameStatistics(
- const std::shared_ptr<FrameStatistics>& frameStats) {
- mFrameStatistics = frameStats;
- }
-
void setBufferStuffingFixWait(int32_t nFrames) {
mBufferStuffingFixWait = std::max(0, nFrames);
}
+ int getSupportedRefreshPeriodsNS(uint64_t* out_refreshrates,
+ int allocated_entries);
+
+ void setLastLatencyRecordedCallback(std::function<int32_t()> callback) {
+ mLastLatencyRecorded = callback;
+ }
+
protected:
// Used for testing
SwappyCommon(const SwappyCommonSettings& settings);
@@ -351,6 +352,10 @@
float mLatestFrameRateVote GUARDED_BY(mMutex) = 0.f;
static constexpr float FRAME_RATE_VOTE_MARGIN = 1.f; // 1Hz
+ // Callback for last latency recorded - used for buffer stuffing fix.
+ // Latency is returned in number of V-syncs
+ std::function<int32_t()> mLastLatencyRecorded;
+
// If zero, don't apply the double buffering fix. If non-zero, apply
// the fix after this number of bad frames.
int mBufferStuffingFixWait = 0;
@@ -361,8 +366,6 @@
// Counts the number of consecutive missed frames (as judged by expected
// latency).
int mMissedFrameCounter = 0;
-
- std::shared_ptr<FrameStatistics> mFrameStatistics;
};
} // namespace swappy
diff --git a/src/swappy/common/SwappyDisplayManager.cpp b/games-frame-pacing/common/SwappyDisplayManager.cpp
similarity index 97%
rename from src/swappy/common/SwappyDisplayManager.cpp
rename to games-frame-pacing/common/SwappyDisplayManager.cpp
index a675610..344207e 100644
--- a/src/swappy/common/SwappyDisplayManager.cpp
+++ b/games-frame-pacing/common/SwappyDisplayManager.cpp
@@ -54,10 +54,14 @@
(void
*)&Java_com_google_androidgamesdk_SwappyDisplayManager_nOnRefreshPeriodChanged}};
+bool SwappyDisplayManager::usesMinSdkOrLater(SdkVersion sdkVersion) {
+ return sdkVersion.sdkInt >= MIN_SDK_VERSION;
+}
+
bool SwappyDisplayManager::useSwappyDisplayManager(SdkVersion sdkVersion) {
// SwappyDisplayManager uses APIs introduced in SDK 23 and we get spurious
// window messages for SDK < 28, so restrict here.
- if (sdkVersion.sdkInt < MIN_SDK_VERSION) {
+ if (!usesMinSdkOrLater(sdkVersion)) {
return false;
}
diff --git a/src/swappy/common/SwappyDisplayManager.h b/games-frame-pacing/common/SwappyDisplayManager.h
similarity index 96%
rename from src/swappy/common/SwappyDisplayManager.h
rename to games-frame-pacing/common/SwappyDisplayManager.h
index 783b375..8f3b63a 100644
--- a/src/swappy/common/SwappyDisplayManager.h
+++ b/games-frame-pacing/common/SwappyDisplayManager.h
@@ -39,6 +39,7 @@
static constexpr int MIN_SDK_VERSION = 28;
static bool useSwappyDisplayManager(SdkVersion sdkVersion);
+ static bool usesMinSdkOrLater(SdkVersion sdkVersion);
SwappyDisplayManager(JavaVM*, jobject mainActivity);
~SwappyDisplayManager();
diff --git a/src/swappy/common/Thread.cpp b/games-frame-pacing/common/Thread.cpp
similarity index 100%
rename from src/swappy/common/Thread.cpp
rename to games-frame-pacing/common/Thread.cpp
diff --git a/src/swappy/common/Thread.h b/games-frame-pacing/common/Thread.h
similarity index 100%
rename from src/swappy/common/Thread.h
rename to games-frame-pacing/common/Thread.h
diff --git a/src/swappy/common/swappy_c.cpp b/games-frame-pacing/common/swappy_c.cpp
similarity index 100%
rename from src/swappy/common/swappy_c.cpp
rename to games-frame-pacing/common/swappy_c.cpp
diff --git a/src/extras/aar/.gitignore b/games-frame-pacing/extras/aar/.gitignore
similarity index 100%
rename from src/extras/aar/.gitignore
rename to games-frame-pacing/extras/aar/.gitignore
diff --git a/src/extras/apk/.gitignore b/games-frame-pacing/extras/apk/.gitignore
similarity index 100%
rename from src/extras/apk/.gitignore
rename to games-frame-pacing/extras/apk/.gitignore
diff --git a/games-frame-pacing/extras/build.gradle b/games-frame-pacing/extras/build.gradle
new file mode 100644
index 0000000..0ec473b
--- /dev/null
+++ b/games-frame-pacing/extras/build.gradle
@@ -0,0 +1,25 @@
+apply plugin: 'com.android.application'
+
+
+buildDir="./out"
+
+android {
+ compileSdkVersion 31
+ defaultConfig {
+ minSdkVersion 19
+ targetSdkVersion 31
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ consumerProguardFiles 'lib-proguard-rules.txt'
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ }
+ }
+ lintOptions {
+ abortOnError false
+ checkReleaseBuilds false
+ }
+}
diff --git a/src/extras/lib-proguard-rules.txt b/games-frame-pacing/extras/lib-proguard-rules.txt
similarity index 100%
rename from src/extras/lib-proguard-rules.txt
rename to games-frame-pacing/extras/lib-proguard-rules.txt
diff --git a/src/extras/src/main/AndroidManifest.xml b/games-frame-pacing/extras/src/main/AndroidManifest.xml
similarity index 100%
rename from src/extras/src/main/AndroidManifest.xml
rename to games-frame-pacing/extras/src/main/AndroidManifest.xml
diff --git a/src/extras/src/main/java/com/google/androidgamesdk/ChoreographerCallback.java b/games-frame-pacing/extras/src/main/java/com/google/androidgamesdk/ChoreographerCallback.java
similarity index 100%
rename from src/extras/src/main/java/com/google/androidgamesdk/ChoreographerCallback.java
rename to games-frame-pacing/extras/src/main/java/com/google/androidgamesdk/ChoreographerCallback.java
diff --git a/src/extras/src/main/java/com/google/androidgamesdk/GameSdkDeviceInfoJni.java b/games-frame-pacing/extras/src/main/java/com/google/androidgamesdk/GameSdkDeviceInfoJni.java
similarity index 100%
rename from src/extras/src/main/java/com/google/androidgamesdk/GameSdkDeviceInfoJni.java
rename to games-frame-pacing/extras/src/main/java/com/google/androidgamesdk/GameSdkDeviceInfoJni.java
diff --git a/src/extras/src/main/java/com/google/androidgamesdk/SwappyDisplayManager.java b/games-frame-pacing/extras/src/main/java/com/google/androidgamesdk/SwappyDisplayManager.java
similarity index 100%
rename from src/extras/src/main/java/com/google/androidgamesdk/SwappyDisplayManager.java
rename to games-frame-pacing/extras/src/main/java/com/google/androidgamesdk/SwappyDisplayManager.java
diff --git a/games-frame-pacing/include/common/gamesdk_common.h b/games-frame-pacing/include/common/gamesdk_common.h
new file mode 120000
index 0000000..73ef675
--- /dev/null
+++ b/games-frame-pacing/include/common/gamesdk_common.h
@@ -0,0 +1 @@
+../../../include/common/gamesdk_common.h
\ No newline at end of file
diff --git a/games-frame-pacing/include/swappy b/games-frame-pacing/include/swappy
new file mode 120000
index 0000000..d2cac3e
--- /dev/null
+++ b/games-frame-pacing/include/swappy
@@ -0,0 +1 @@
+../../include/swappy/
\ No newline at end of file
diff --git a/src/swappy/opengl/EGL.cpp b/games-frame-pacing/opengl/EGL.cpp
similarity index 100%
rename from src/swappy/opengl/EGL.cpp
rename to games-frame-pacing/opengl/EGL.cpp
diff --git a/src/swappy/opengl/EGL.h b/games-frame-pacing/opengl/EGL.h
similarity index 100%
rename from src/swappy/opengl/EGL.h
rename to games-frame-pacing/opengl/EGL.h
diff --git a/games-frame-pacing/opengl/FrameStatisticsGL.cpp b/games-frame-pacing/opengl/FrameStatisticsGL.cpp
new file mode 100644
index 0000000..024a117
--- /dev/null
+++ b/games-frame-pacing/opengl/FrameStatisticsGL.cpp
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "FrameStatisticsGL.h"
+
+#define LOG_TAG "FrameStatisticsGL"
+
+#include <inttypes.h>
+
+#include <cmath>
+#include <string>
+
+#include "EGL.h"
+#include "Log.h"
+#include "SwappyCommon.h"
+#include "Trace.h"
+
+namespace swappy {
+
+FrameStatisticsGL::FrameStatisticsGL(const EGL& egl,
+ const SwappyCommon& swappyCommon)
+ : mEgl(egl), mSwappyCommon(swappyCommon) {}
+
+FrameStatisticsGL::ThisFrame FrameStatisticsGL::getThisFrame(
+ EGLDisplay dpy, EGLSurface surface) {
+ const TimePoint frameStartTime = std::chrono::steady_clock::now();
+
+ // first get the next frame id
+ std::pair<bool, EGLuint64KHR> nextFrameId =
+ mEgl.getNextFrameId(dpy, surface);
+ if (nextFrameId.first) {
+ mPendingFrames.push_back(
+ {dpy, surface, nextFrameId.second, frameStartTime});
+ }
+
+ if (mPendingFrames.empty()) {
+ return {};
+ }
+
+ EGLFrame frame = mPendingFrames.front();
+ // make sure we don't lag behind the stats too much
+ if (nextFrameId.first && nextFrameId.second - frame.id > MAX_FRAME_LAG) {
+ while (mPendingFrames.size() > 1)
+ mPendingFrames.erase(mPendingFrames.begin());
+ mFrameStatsCommon.invalidateLastFrame();
+ frame = mPendingFrames.front();
+ }
+ std::unique_ptr<EGL::FrameTimestamps> frameStats =
+ mEgl.getFrameTimestamps(frame.dpy, frame.surface, frame.id);
+
+ if (!frameStats) {
+ return {frame.startFrameTime};
+ }
+
+ mPendingFrames.erase(mPendingFrames.begin());
+
+ return {frame.startFrameTime, std::move(frameStats)};
+}
+
+// called once per swap
+void FrameStatisticsGL::capture(EGLDisplay dpy, EGLSurface surface) {
+ auto frame = getThisFrame(dpy, surface);
+
+ if (!frame.stats) return;
+
+ FrameTimings current = {
+ static_cast<uint64_t>(frame.startTime.time_since_epoch().count()),
+ static_cast<uint64_t>(frame.stats->requested),
+ static_cast<uint64_t>(frame.stats->presented),
+ static_cast<uint64_t>(frame.stats->compositionLatched -
+ frame.stats->renderingCompleted)};
+
+ mFrameStatsCommon.updateFrameStats(
+ current, mSwappyCommon.getRefreshPeriod().count());
+}
+
+void FrameStatisticsGL::enableStats(bool enabled) {
+ mFrameStatsCommon.enableStats(enabled);
+}
+
+SwappyStats FrameStatisticsGL::getStats() {
+ return mFrameStatsCommon.getStats();
+}
+
+void FrameStatisticsGL::clearStats() { mFrameStatsCommon.clearStats(); }
+
+int32_t FrameStatisticsGL::lastLatencyRecorded() {
+ return mFrameStatsCommon.lastLatencyRecorded();
+}
+} // namespace swappy
diff --git a/games-frame-pacing/opengl/FrameStatisticsGL.h b/games-frame-pacing/opengl/FrameStatisticsGL.h
new file mode 100644
index 0000000..f5a95e2
--- /dev/null
+++ b/games-frame-pacing/opengl/FrameStatisticsGL.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <array>
+#include <atomic>
+#include <map>
+#include <vector>
+
+#include "EGL.h"
+#include "FrameStatistics.h"
+#include "SwappyCommon.h"
+#include "Thread.h"
+
+using TimePoint = std::chrono::steady_clock::time_point;
+using namespace std::chrono_literals;
+
+namespace swappy {
+
+class FrameStatisticsGL {
+ public:
+ FrameStatisticsGL(const EGL& egl, const SwappyCommon& swappyCommon);
+ ~FrameStatisticsGL() = default;
+
+ void enableStats(bool enabled);
+ void capture(EGLDisplay dpy, EGLSurface surface);
+ SwappyStats getStats();
+ void clearStats();
+
+ int32_t lastLatencyRecorded();
+
+ protected:
+ static constexpr int MAX_FRAME_LAG = 10;
+ struct ThisFrame {
+ TimePoint startTime;
+ std::unique_ptr<EGL::FrameTimestamps> stats;
+ };
+ ThisFrame getThisFrame(EGLDisplay dpy, EGLSurface surface);
+
+ const EGL& mEgl;
+ const SwappyCommon& mSwappyCommon;
+
+ struct EGLFrame {
+ EGLDisplay dpy;
+ EGLSurface surface;
+ EGLuint64KHR id;
+ TimePoint startFrameTime;
+ };
+ std::vector<EGLFrame> mPendingFrames;
+ FrameStatistics mFrameStatsCommon;
+};
+
+} // namespace swappy
diff --git a/src/swappy/opengl/SwappyGL.cpp b/games-frame-pacing/opengl/SwappyGL.cpp
similarity index 88%
rename from src/swappy/opengl/SwappyGL.cpp
rename to games-frame-pacing/opengl/SwappyGL.cpp
index 1cea55a..2726610 100644
--- a/src/swappy/opengl/SwappyGL.cpp
+++ b/games-frame-pacing/opengl/SwappyGL.cpp
@@ -181,30 +181,9 @@
return;
}
- if (!swappy->enabled()) {
- return;
+ if (swappy->mFrameStatistics) {
+ swappy->mFrameStatistics->enableStats(enabled);
}
-
- if (!swappy->getEgl()->statsSupported()) {
- ALOGI("stats are not suppored on this platform");
- return;
- }
-
- if (enabled) {
- if (!swappy->mFrameStatistics ||
- swappy->mFrameStatistics->isEssential()) {
- swappy->mFrameStatistics = std::make_shared<FullFrameStatisticsGL>(
- *swappy->mEgl, swappy->mCommonBase);
- ALOGI("Enabling stats");
- } else {
- ALOGI("Stats already enabled");
- }
- } else {
- swappy->mFrameStatistics = std::make_shared<LatencyFrameStatisticsGL>(
- *swappy->mEgl, swappy->mCommonBase);
- ALOGI("Disabling stats");
- }
- swappy->mCommonBase.setFrameStatistics(swappy->mFrameStatistics);
}
void SwappyGL::recordFrameStart(EGLDisplay display, EGLSurface surface) {
@@ -214,8 +193,9 @@
return;
}
- if (swappy->mFrameStatistics)
+ if (swappy->mFrameStatistics) {
swappy->mFrameStatistics->capture(display, surface);
+ }
}
void SwappyGL::getStats(SwappyStats *stats) {
@@ -223,9 +203,19 @@
if (!swappy) {
return;
}
-
- if (swappy->mFrameStatistics && !swappy->mFrameStatistics->isEssential())
+ if (swappy->mFrameStatistics) {
*stats = swappy->mFrameStatistics->getStats();
+ }
+}
+
+void SwappyGL::clearStats() {
+ SwappyGL *swappy = getInstance();
+ if (!swappy) {
+ return;
+ }
+ if (swappy->mFrameStatistics) {
+ swappy->mFrameStatistics->clearStats();
+ }
}
SwappyGL *SwappyGL::getInstance() {
@@ -299,6 +289,14 @@
return;
}
+ if (mEgl->statsSupported()) {
+ mFrameStatistics =
+ std::make_unique<FrameStatisticsGL>(*mEgl, mCommonBase);
+ mCommonBase.setLastLatencyRecordedCallback(
+ [this]() { return this->mFrameStatistics->lastLatencyRecorded(); });
+ } else {
+ ALOGI("stats are not suppored on this platform");
+ }
ALOGI("SwappyGL initialized successfully");
}
@@ -330,4 +328,15 @@
swappy->mCommonBase.setBufferStuffingFixWait(n_frames);
}
+int SwappyGL::getSupportedRefreshPeriodsNS(uint64_t *out_refreshrates,
+ int allocated_entries) {
+ TRACE_CALL();
+ SwappyGL *swappy = getInstance();
+ if (!swappy) {
+ return -1;
+ }
+ return swappy->mCommonBase.getSupportedRefreshPeriodsNS(out_refreshrates,
+ allocated_entries);
+}
+
} // namespace swappy
diff --git a/src/swappy/opengl/SwappyGL.h b/games-frame-pacing/opengl/SwappyGL.h
similarity index 92%
rename from src/swappy/opengl/SwappyGL.h
rename to games-frame-pacing/opengl/SwappyGL.h
index 169ed56..0c4e94a 100644
--- a/src/swappy/opengl/SwappyGL.h
+++ b/games-frame-pacing/opengl/SwappyGL.h
@@ -67,6 +67,8 @@
static void enableStats(bool enabled);
static void recordFrameStart(EGLDisplay display, EGLSurface surface);
static void getStats(SwappyStats *stats);
+ static void clearStats();
+
static bool isEnabled();
static void destroyInstance();
@@ -75,6 +77,9 @@
static void setBufferStuffingFixWait(int32_t n_frames);
+ static int getSupportedRefreshPeriodsNS(uint64_t *out_refreshrates,
+ int allocated_entries);
+
private:
static SwappyGL *getInstance();
@@ -102,7 +107,7 @@
std::mutex mEglMutex;
std::unique_ptr<EGL> mEgl;
- std::shared_ptr<LatencyFrameStatisticsGL> mFrameStatistics;
+ std::unique_ptr<FrameStatisticsGL> mFrameStatistics;
SwappyCommon mCommonBase;
};
diff --git a/src/swappy/opengl/swappyGL_c.cpp b/games-frame-pacing/opengl/swappyGL_c.cpp
similarity index 92%
rename from src/swappy/opengl/swappyGL_c.cpp
rename to games-frame-pacing/opengl/swappyGL_c.cpp
index e0f89a6..31c50a2 100644
--- a/src/swappy/opengl/swappyGL_c.cpp
+++ b/games-frame-pacing/opengl/swappyGL_c.cpp
@@ -119,4 +119,10 @@
SwappyGL::removeTracer(t);
}
+int SwappyGL_getSupportedRefreshPeriodsNS(uint64_t *out_refreshrates,
+ int allocated_entries) {
+ return SwappyGL::getSupportedRefreshPeriodsNS(out_refreshrates,
+ allocated_entries);
+}
+
} // extern "C" {
diff --git a/games-frame-pacing/src/main/AndroidManifest.xml b/games-frame-pacing/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..e77f3c9
--- /dev/null
+++ b/games-frame-pacing/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="androidx.games" >
+</manifest>
diff --git a/src/swappy/tools/lib_test_swappy.py b/games-frame-pacing/tools/lib_test_swappy.py
similarity index 100%
rename from src/swappy/tools/lib_test_swappy.py
rename to games-frame-pacing/tools/lib_test_swappy.py
diff --git a/src/swappy/tools/test_swappy.py b/games-frame-pacing/tools/test_swappy.py
similarity index 100%
rename from src/swappy/tools/test_swappy.py
rename to games-frame-pacing/tools/test_swappy.py
diff --git a/src/swappy/vulkan/SwappyVk.cpp b/games-frame-pacing/vulkan/SwappyVk.cpp
similarity index 86%
rename from src/swappy/vulkan/SwappyVk.cpp
rename to games-frame-pacing/vulkan/SwappyVk.cpp
index dce4f81..5f940dc 100644
--- a/src/swappy/vulkan/SwappyVk.cpp
+++ b/games-frame-pacing/vulkan/SwappyVk.cpp
@@ -16,6 +16,8 @@
#include "SwappyVk.h"
+#define LOG_TAG "SwappyVk"
+
namespace swappy {
class DefaultSwappyVkFunctionProvider {
@@ -297,4 +299,42 @@
}
}
+int SwappyVk::GetSupportedRefreshPeriodsNS(uint64_t* out_refreshrates,
+ int allocated_entries,
+ VkSwapchainKHR swapchain) {
+ return (*perSwapchainImplementation[swapchain])
+ .getSupportedRefreshPeriodsNS(out_refreshrates, allocated_entries);
+}
+
+bool SwappyVk::IsEnabled(VkSwapchainKHR swapchain, bool* isEnabled) {
+ auto& pImplementation = perSwapchainImplementation[swapchain];
+ if (!pImplementation || !isEnabled) return false;
+ *isEnabled = pImplementation->isEnabled();
+ return true;
+}
+
+void SwappyVk::enableStats(VkSwapchainKHR swapchain, bool enabled) {
+ auto it = perSwapchainImplementation.find(swapchain);
+ if (it != perSwapchainImplementation.end())
+ it->second->enableStats(enabled);
+}
+
+void SwappyVk::getStats(VkSwapchainKHR swapchain, SwappyStats* swappyStats) {
+ auto it = perSwapchainImplementation.find(swapchain);
+ if (it != perSwapchainImplementation.end())
+ it->second->getStats(swappyStats);
+}
+
+void SwappyVk::recordFrameStart(VkQueue queue, VkSwapchainKHR swapchain,
+ uint32_t image) {
+ auto it = perSwapchainImplementation.find(swapchain);
+ if (it != perSwapchainImplementation.end())
+ it->second->recordFrameStart(queue, image);
+}
+
+void SwappyVk::clearStats(VkSwapchainKHR swapchain) {
+ auto it = perSwapchainImplementation.find(swapchain);
+ if (it != perSwapchainImplementation.end()) it->second->clearStats();
+}
+
} // namespace swappy
diff --git a/src/swappy/vulkan/SwappyVk.h b/games-frame-pacing/vulkan/SwappyVk.h
similarity index 87%
rename from src/swappy/vulkan/SwappyVk.h
rename to games-frame-pacing/vulkan/SwappyVk.h
index 7031266..16d9171 100644
--- a/src/swappy/vulkan/SwappyVk.h
+++ b/games-frame-pacing/vulkan/SwappyVk.h
@@ -79,6 +79,9 @@
void SetFenceTimeout(std::chrono::nanoseconds duration);
std::chrono::nanoseconds GetFenceTimeout() const;
std::chrono::nanoseconds GetSwapInterval(VkSwapchainKHR swapchain);
+ int GetSupportedRefreshPeriodsNS(uint64_t* out_refreshrates,
+ int allocated_entries,
+ VkSwapchainKHR swapchain);
void addTracer(const SwappyTracer* t);
void removeTracer(const SwappyTracer* t);
@@ -86,6 +89,15 @@
void SetFunctionProvider(const SwappyVkFunctionProvider* pFunctionProvider);
bool InitFunctions();
+ bool IsEnabled(VkSwapchainKHR swapchain, bool* isEnabled);
+
+ // Frame statistics.
+ void enableStats(VkSwapchainKHR swapchain, bool enabled);
+ void getStats(VkSwapchainKHR swapchain, SwappyStats* swappyStats);
+ void recordFrameStart(VkQueue queue, VkSwapchainKHR swapchain,
+ uint32_t image);
+ void clearStats(VkSwapchainKHR swapchain);
+
private:
std::map<VkPhysicalDevice, bool> doesPhysicalDeviceHaveGoogleDisplayTiming;
std::map<VkSwapchainKHR, std::shared_ptr<SwappyVkBase>>
diff --git a/src/swappy/vulkan/SwappyVkBase.cpp b/games-frame-pacing/vulkan/SwappyVkBase.cpp
similarity index 97%
rename from src/swappy/vulkan/SwappyVkBase.cpp
rename to games-frame-pacing/vulkan/SwappyVkBase.cpp
index 833f5cb..789f61a 100644
--- a/src/swappy/vulkan/SwappyVkBase.cpp
+++ b/games-frame-pacing/vulkan/SwappyVkBase.cpp
@@ -139,8 +139,8 @@
const VkCommandPoolCreateInfo cmd_pool_info = {
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
.pNext = NULL,
- .queueFamilyIndex = queueFamilyIndex,
.flags = 0,
+ .queueFamilyIndex = queueFamilyIndex,
};
VkResult res = vkCreateCommandPool(mDevice, &cmd_pool_info, NULL,
@@ -391,7 +391,7 @@
vkWaitForFences(mDevice, 1, &sync.fence, VK_TRUE,
mCommonBase.getFenceTimeout().count());
if (result) {
- ALOGE("Failed to wait for fence %d", result);
+ ALOGW_ONCE("Failed to wait for fence %d", result);
}
mLastFenceTime = std::chrono::steady_clock::now() - startTime;
@@ -431,4 +431,10 @@
if (tracer != nullptr) mCommonBase.removeTracerCallbacks(*tracer);
}
+int SwappyVkBase::getSupportedRefreshPeriodsNS(uint64_t* out_refreshrates,
+ int allocated_entries) {
+ return mCommonBase.getSupportedRefreshPeriodsNS(out_refreshrates,
+ allocated_entries);
+}
+
} // namespace swappy
diff --git a/src/swappy/vulkan/SwappyVkBase.h b/games-frame-pacing/vulkan/SwappyVkBase.h
similarity index 93%
rename from src/swappy/vulkan/SwappyVkBase.h
rename to games-frame-pacing/vulkan/SwappyVkBase.h
index f670a04..5705c8a 100644
--- a/src/swappy/vulkan/SwappyVkBase.h
+++ b/games-frame-pacing/vulkan/SwappyVkBase.h
@@ -57,23 +57,13 @@
#include <mutex>
#include "ChoreographerShim.h"
+#include "Log.h"
#include "Settings.h"
#include "SwappyCommon.h"
#include "Trace.h"
namespace swappy {
-#define ALOGE(...) \
- __android_log_print(ANDROID_LOG_ERROR, "SwappyVk", __VA_ARGS__)
-#define ALOGW(...) \
- __android_log_print(ANDROID_LOG_WARN, "SwappyVk", __VA_ARGS__)
-#define ALOGI(...) \
- __android_log_print(ANDROID_LOG_INFO, "SwappyVk", __VA_ARGS__)
-#define ALOGD(...) \
- __android_log_print(ANDROID_LOG_DEBUG, "SwappyVk", __VA_ARGS__)
-#define ALOGV(...) \
- __android_log_print(ANDROID_LOG_VERBOSE, "SwappyVk", __VA_ARGS__)
-
constexpr uint32_t kThousand = 1000;
constexpr uint32_t kMillion = 1000000;
constexpr uint32_t kBillion = 1000000000;
@@ -152,6 +142,14 @@
VkDevice getDevice() const { return mDevice; }
+ int getSupportedRefreshPeriodsNS(uint64_t* out_refreshrates,
+ int allocated_entries);
+
+ virtual void enableStats(bool enabled) = 0;
+ virtual void getStats(SwappyStats* swappyStats) = 0;
+ virtual void recordFrameStart(VkQueue queue, uint32_t image) = 0;
+ virtual void clearStats() = 0;
+
protected:
struct VkSync {
VkFence fence;
diff --git a/src/swappy/vulkan/SwappyVkFallback.cpp b/games-frame-pacing/vulkan/SwappyVkFallback.cpp
similarity index 87%
rename from src/swappy/vulkan/SwappyVkFallback.cpp
rename to games-frame-pacing/vulkan/SwappyVkFallback.cpp
index abf0f8b..741bd90 100644
--- a/src/swappy/vulkan/SwappyVkFallback.cpp
+++ b/games-frame-pacing/vulkan/SwappyVkFallback.cpp
@@ -99,4 +99,20 @@
return result;
}
+void SwappyVkFallback::enableStats(bool enabled) {
+ ALOGE("Frame Statistics Unsupported - API ignored");
+}
+
+void SwappyVkFallback::getStats(SwappyStats* swappyStats) {
+ ALOGE("Frame Statistics Unsupported - API ignored");
+}
+
+void SwappyVkFallback::recordFrameStart(VkQueue queue, uint32_t image) {
+ ALOGE("Frame Statistics Unsupported - API ignored");
+}
+
+void SwappyVkFallback::clearStats() {
+ ALOGE("Frame Statistics Unsupported - API ignored");
+}
+
} // namespace swappy
diff --git a/src/swappy/vulkan/SwappyVkFallback.h b/games-frame-pacing/vulkan/SwappyVkFallback.h
similarity index 74%
rename from src/swappy/vulkan/SwappyVkFallback.h
rename to games-frame-pacing/vulkan/SwappyVkFallback.h
index 5906fee..64518b5 100644
--- a/src/swappy/vulkan/SwappyVkFallback.h
+++ b/games-frame-pacing/vulkan/SwappyVkFallback.h
@@ -33,12 +33,17 @@
VkPhysicalDevice physicalDevice, VkDevice device,
const SwappyVkFunctionProvider* provider);
- virtual bool doGetRefreshCycleDuration(VkSwapchainKHR swapchain,
- uint64_t* pRefreshDuration) override;
+ bool doGetRefreshCycleDuration(VkSwapchainKHR swapchain,
+ uint64_t* pRefreshDuration) override final;
- virtual VkResult doQueuePresent(
+ VkResult doQueuePresent(
VkQueue queue, uint32_t queueFamilyIndex,
- const VkPresentInfoKHR* pPresentInfo) override;
+ const VkPresentInfoKHR* pPresentInfo) override final;
+
+ void enableStats(bool enabled) override final;
+ void recordFrameStart(VkQueue queue, uint32_t image) override final;
+ void getStats(SwappyStats* swappyStats) override final;
+ void clearStats() override final;
};
} // namespace swappy
diff --git a/games-frame-pacing/vulkan/SwappyVkGoogleDisplayTiming.cpp b/games-frame-pacing/vulkan/SwappyVkGoogleDisplayTiming.cpp
new file mode 100644
index 0000000..c450631
--- /dev/null
+++ b/games-frame-pacing/vulkan/SwappyVkGoogleDisplayTiming.cpp
@@ -0,0 +1,214 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if (not defined ANDROID_NDK_VERSION) || ANDROID_NDK_VERSION >= 15
+
+#include "SwappyVkGoogleDisplayTiming.h"
+
+#define LOG_TAG "SwappyVkGoogleDisplayTiming"
+
+using std::chrono::nanoseconds;
+
+namespace swappy {
+
+SwappyVkGoogleDisplayTiming::SwappyVkGoogleDisplayTiming(
+ JNIEnv* env, jobject jactivity, VkPhysicalDevice physicalDevice,
+ VkDevice device, const SwappyVkFunctionProvider* provider)
+ : SwappyVkBase(env, jactivity, physicalDevice, device, provider) {}
+
+bool SwappyVkGoogleDisplayTiming::doGetRefreshCycleDuration(
+ VkSwapchainKHR swapchain, uint64_t* pRefreshDuration) {
+ if (!isEnabled()) {
+ ALOGE("Swappy is disabled.");
+ return false;
+ }
+
+ VkRefreshCycleDurationGOOGLE refreshCycleDuration;
+ VkResult res = mpfnGetRefreshCycleDurationGOOGLE(mDevice, swapchain,
+ &refreshCycleDuration);
+ if (res != VK_SUCCESS) {
+ ALOGE("mpfnGetRefreshCycleDurationGOOGLE failed %d", res);
+ return false;
+ }
+
+ *pRefreshDuration = mCommonBase.getRefreshPeriod().count();
+
+ double refreshRate = 1000000000.0 / *pRefreshDuration;
+ ALOGI("Returning refresh duration of %" PRIu64 " nsec (approx %f Hz)",
+ *pRefreshDuration, refreshRate);
+
+ mSwapchain = swapchain;
+ return true;
+}
+
+VkResult SwappyVkGoogleDisplayTiming::doQueuePresent(
+ VkQueue queue, uint32_t queueFamilyIndex,
+ const VkPresentInfoKHR* pPresentInfo) {
+ if (!isEnabled()) {
+ ALOGE("Swappy is disabled.");
+ return VK_ERROR_INITIALIZATION_FAILED;
+ }
+
+ VkResult res = initializeVkSyncObjects(queue, queueFamilyIndex);
+ if (res) {
+ return res;
+ }
+
+ const SwappyCommon::SwapHandlers handlers = {
+ .lastFrameIsComplete = std::bind(
+ &SwappyVkGoogleDisplayTiming::lastFrameIsCompleted, this, queue),
+ .getPrevFrameGpuTime = std::bind(
+ &SwappyVkGoogleDisplayTiming::getLastFenceTime, this, queue),
+ };
+
+ VkSemaphore semaphore;
+ res = injectFence(queue, pPresentInfo, &semaphore);
+ if (res) {
+ ALOGE("Failed to vkQueueSubmit %d", res);
+ return res;
+ }
+
+ uint32_t waitSemaphoreCount;
+ const VkSemaphore* pWaitSemaphores;
+ if (semaphore != VK_NULL_HANDLE) {
+ waitSemaphoreCount = 1;
+ pWaitSemaphores = &semaphore;
+ } else {
+ waitSemaphoreCount = pPresentInfo->waitSemaphoreCount;
+ pWaitSemaphores = pPresentInfo->pWaitSemaphores;
+ }
+
+ mCommonBase.onPreSwap(handlers);
+
+ VkPresentTimeGOOGLE pPresentTimes[pPresentInfo->swapchainCount];
+ VkPresentInfoKHR replacementPresentInfo;
+ VkPresentTimesInfoGOOGLE presentTimesInfo;
+ // Set up the new structures to pass:
+ // if 0 is passed as desired present time, it is ignored by the loader.
+ uint64_t desiredPresentTime =
+ mCommonBase.needToSetPresentationTime()
+ ? mCommonBase.getPresentationTime().time_since_epoch().count()
+ : 0;
+ for (uint32_t i = 0; i < pPresentInfo->swapchainCount; i++) {
+ pPresentTimes[i].presentID = mPresentID;
+ pPresentTimes[i].desiredPresentTime = desiredPresentTime;
+ }
+
+ presentTimesInfo = {VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE,
+ pPresentInfo->pNext, pPresentInfo->swapchainCount,
+ pPresentTimes};
+
+ replacementPresentInfo = {
+ pPresentInfo->sType, &presentTimesInfo,
+ waitSemaphoreCount, pWaitSemaphores,
+ pPresentInfo->swapchainCount, pPresentInfo->pSwapchains,
+ pPresentInfo->pImageIndices, pPresentInfo->pResults};
+
+ mPresentID++;
+
+ res = mpfnQueuePresentKHR(queue, &replacementPresentInfo);
+ mCommonBase.onPostSwap(handlers);
+
+ return res;
+}
+
+void SwappyVkGoogleDisplayTiming::enableStats(bool enabled) {
+ mFrameStatisticsCommon.enableStats(enabled);
+}
+
+void SwappyVkGoogleDisplayTiming::recordFrameStart(VkQueue queue,
+ uint32_t image) {
+ uint64_t frameStartTime = static_cast<uint64_t>(
+ std::chrono::steady_clock::now().time_since_epoch().count());
+ mPendingFrames.push_back({mPresentID, frameStartTime});
+
+ // No point in querying if the history is too short, as vulkan loader does
+ // not return any history newer than 5 frames.
+ // See MIN_NUM_FRAMES_AGO in
+ // https://android.googlesource.com/platform/frameworks/native/+/refs/heads/master/vulkan/libvulkan/swapchain.cpp
+ if (mPendingFrames.size() < MIN_FRAME_LAG) return;
+
+ // The query for vulkan past presentation timings does not point to any
+ // specific id. Instead, the loader just returns whatever timings are
+ // available to the user. Query all the available timings, which is a max
+ // of 10 in the vulkan loader currently.
+ //
+ // Check through each of the timing if we have a pending frame id, if we do
+ // then populate the histogram with the available timings. There are a
+ // couple of assumptions made here.
+ // * The maximum size of the vectors here is 10, so simplicity is
+ // prioritized.
+ // * The frames are in order.
+ // * If any of the presentTimings ids are not present in mPendingFrames,
+ // those are frames that must have been cleared and we do not care about
+ // the timings anymore.
+ // * [Performance] Under normal smooth circumstances, this should be 1
+ // frame handled at a time, if there is a situation where several frames
+ // are pending, it implies that the gpu & presentation engine are not
+ // keeping up. So spending a few CPU cycles here to go through a few extra
+ // frames is not going to impact overall performance.
+ uint32_t pastTimingsCount = MAX_FRAME_LAG;
+ VkResult result = mpfnGetPastPresentationTimingGOOGLE(
+ mDevice, mSwapchain, &pastTimingsCount, &mPastTimes[0]);
+
+ if (result == VK_INCOMPLETE) {
+ ALOGI(
+ "More past presentation times available. Consider increasing "
+ "MAX_FRAME_LAG");
+ }
+ if (result != VK_SUCCESS && result != VK_INCOMPLETE) {
+ ALOGE("Error collecting past presentation times with result %d",
+ result);
+ return;
+ }
+
+ int i = 0;
+ while (i < pastTimingsCount && mPendingFrames.size() > 1) {
+ auto frame = mPendingFrames.front();
+
+ if (frame.id == mPastTimes[i].presentID) {
+ FrameTimings current = {
+ frame.startFrameTime, mPastTimes[i].desiredPresentTime,
+ mPastTimes[i].actualPresentTime, mPastTimes[i].presentMargin};
+
+ mFrameStatisticsCommon.updateFrameStats(
+ current, mCommonBase.getRefreshPeriod().count());
+ i++;
+ }
+ // If the past timings returned do not match, then the pending frame is
+ // too old. So remove it from the list.
+ mPendingFrames.erase(mPendingFrames.begin());
+ }
+
+ // Clear the pending frames if we are lagging too much.
+ if (mPendingFrames.size() > MAX_FRAME_LAG) {
+ while (mPendingFrames.size() > MIN_FRAME_LAG) {
+ mPendingFrames.erase(mPendingFrames.begin());
+ }
+ mFrameStatisticsCommon.invalidateLastFrame();
+ }
+}
+
+void SwappyVkGoogleDisplayTiming::getStats(SwappyStats* swappyStats) {
+ *swappyStats = mFrameStatisticsCommon.getStats();
+}
+
+void SwappyVkGoogleDisplayTiming::clearStats() {
+ mFrameStatisticsCommon.clearStats();
+}
+} // namespace swappy
+
+#endif // #if (not defined ANDROID_NDK_VERSION) || ANDROID_NDK_VERSION>=15
diff --git a/src/swappy/vulkan/SwappyVkGoogleDisplayTiming.h b/games-frame-pacing/vulkan/SwappyVkGoogleDisplayTiming.h
similarity index 76%
rename from src/swappy/vulkan/SwappyVkGoogleDisplayTiming.h
rename to games-frame-pacing/vulkan/SwappyVkGoogleDisplayTiming.h
index 06c91eb..6f6d153 100644
--- a/src/swappy/vulkan/SwappyVkGoogleDisplayTiming.h
+++ b/games-frame-pacing/vulkan/SwappyVkGoogleDisplayTiming.h
@@ -16,6 +16,7 @@
#pragma once
+#include "FrameStatistics.h"
#include "SwappyVkBase.h"
namespace swappy {
@@ -70,12 +71,37 @@
VkDevice device,
const SwappyVkFunctionProvider* provider);
- virtual bool doGetRefreshCycleDuration(VkSwapchainKHR swapchain,
- uint64_t* pRefreshDuration) override;
+ bool doGetRefreshCycleDuration(VkSwapchainKHR swapchain,
+ uint64_t* pRefreshDuration) override final;
- virtual VkResult doQueuePresent(
+ VkResult doQueuePresent(
VkQueue queue, uint32_t queueFamilyIndex,
- const VkPresentInfoKHR* pPresentInfo) override;
+ const VkPresentInfoKHR* pPresentInfo) override final;
+
+ void enableStats(bool enabled) override final;
+ void recordFrameStart(VkQueue queue, uint32_t image) override final;
+ void getStats(SwappyStats* swappyStats) override final;
+ void clearStats() override final;
+
+ private:
+ static constexpr int MAX_FRAME_LAG = 10;
+ // Vulkan loader does not give any frame timings unless they are 5 frames
+ // old. We use this internally to not waste calls.
+ static constexpr int MIN_FRAME_LAG = 5;
+ uint32_t mPresentID = 0;
+
+ VkSwapchainKHR mSwapchain;
+
+ struct VKFrame {
+ uint32_t id;
+ uint64_t startFrameTime;
+ int pastTimingIndex;
+ };
+
+ std::vector<VKFrame> mPendingFrames;
+ // Storage for querying past presentation frames, allocated upfront.
+ VkPastPresentationTimingGOOGLE mPastTimes[MAX_FRAME_LAG];
+ FrameStatistics mFrameStatisticsCommon;
};
} // namespace swappy
diff --git a/src/swappy/vulkan/swappyVk_c.cpp b/games-frame-pacing/vulkan/swappyVk_c.cpp
similarity index 77%
rename from src/swappy/vulkan/swappyVk_c.cpp
rename to games-frame-pacing/vulkan/swappyVk_c.cpp
index bdfde83..d025afc 100644
--- a/src/swappy/vulkan/swappyVk_c.cpp
+++ b/games-frame-pacing/vulkan/swappyVk_c.cpp
@@ -142,4 +142,44 @@
return swappy.GetSwapInterval(swapchain).count();
}
+int SwappyVk_getSupportedRefreshPeriodsNS(uint64_t* out_refreshrates,
+ int allocated_entries,
+ VkSwapchainKHR swapchain) {
+ TRACE_CALL();
+ swappy::SwappyVk& swappy = swappy::SwappyVk::getInstance();
+ return swappy.GetSupportedRefreshPeriodsNS(out_refreshrates,
+ allocated_entries, swapchain);
+}
+
+bool SwappyVk_isEnabled(VkSwapchainKHR swapchain, bool* isEnabled) {
+ TRACE_CALL();
+ swappy::SwappyVk& swappy = swappy::SwappyVk::getInstance();
+ return swappy.IsEnabled(swapchain, isEnabled);
+}
+
+void SwappyVk_enableStats(VkSwapchainKHR swapchain, bool enabled) {
+ TRACE_CALL();
+ swappy::SwappyVk& swappy = swappy::SwappyVk::getInstance();
+ swappy.enableStats(swapchain, enabled);
+}
+
+void SwappyVk_getStats(VkSwapchainKHR swapchain, SwappyStats* swappyStats) {
+ TRACE_CALL();
+ swappy::SwappyVk& swappy = swappy::SwappyVk::getInstance();
+ swappy.getStats(swapchain, swappyStats);
+}
+
+void SwappyVk_recordFrameStart(VkQueue queue, VkSwapchainKHR swapchain,
+ uint32_t image) {
+ TRACE_CALL();
+ swappy::SwappyVk& swappy = swappy::SwappyVk::getInstance();
+ swappy.recordFrameStart(queue, swapchain, image);
+}
+
+void SwappyVk_clearStats(VkSwapchainKHR swapchain) {
+ TRACE_CALL();
+ swappy::SwappyVk& swappy = swappy::SwappyVk::getInstance();
+ swappy.clearStats(swapchain);
+}
+
} // extern "C"
diff --git a/games-memory-advice/CMakeLists.txt b/games-memory-advice/CMakeLists.txt
new file mode 100644
index 0000000..08645ae
--- /dev/null
+++ b/games-memory-advice/CMakeLists.txt
@@ -0,0 +1,120 @@
+cmake_minimum_required(VERSION 3.18.1)
+project(memory-advice C CXX)
+set(CMAKE_CXX_STANDARD 14)
+set(IgnoreOldToolchainWarning "${ANDROID_UNIFIED_HEADERS}")
+
+include("../samples/gamesdk.cmake")
+
+set( MEMORYADVICE_ASSETS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../test/memoryadvice/memoryadvice/src/main/assets/memoryadvice")
+set( MEMORYADVICE_RESOURCES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../test/memoryadvice/memoryadvice/src/main/resources")
+set( THIRD_PARTY_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../third_party")
+set( TENSORFLOW_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../external/tensorflow")
+set( EXTERNAL_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../external")
+set( TFLITE_FLATBUFFERS_SCHEMA_DIR "${EXTERNAL_SOURCE_DIR}/flatbuffers/include")
+
+set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wthread-safety" )
+set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D _LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS -O3 -fPIC" )
+set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions" )
+set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti" )
+set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffunction-sections -fdata-sections" )
+set( CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -g0")
+
+set( CPUINFO_SOURCE_DIR "${EXTERNAL_SOURCE_DIR}/cpuinfo")
+set( CLOG_SOURCE_DIR "${EXTERNAL_SOURCE_DIR}/cpuinfo")
+set( PTHREADPOOL_SOURCE_DIR "${EXTERNAL_SOURCE_DIR}/pthreadpool")
+set( FXDIV_SOURCE_DIR "${EXTERNAL_SOURCE_DIR}/FXdiv")
+set( FP16_SOURCE_DIR "${EXTERNAL_SOURCE_DIR}/FP16")
+set( PSIMD_SOURCE_DIR "${EXTERNAL_SOURCE_DIR}/psimd")
+set( XNNPACK_SOURCE_DIR "${EXTERNAL_SOURCE_DIR}/xnnpack")
+set( EIGEN_SOURCE_DIR "${EXTERNAL_SOURCE_DIR}/eigen")
+
+if (${CMAKE_BUILD_TYPE} STREQUAL "Release")
+set( CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--gc-sections,-s")
+endif (${CMAKE_BUILD_TYPE} STREQUAL "Release")
+set( CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--hash-style=both" )
+
+file(READ "${MEMORYADVICE_ASSETS_DIR}/default.json" PARAMS_FILE)
+set(PARAMS_STRING "namespace memory_advice {\nconst char* parameters_string = R\"PARAMS(\n")
+string(APPEND PARAMS_STRING "${PARAMS_FILE}")
+string(APPEND PARAMS_STRING "\n )PARAMS\";\n}\n")
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/advisor_parameters.cpp" "${PARAMS_STRING}")
+
+include_directories( . )
+include_directories( ../include )
+include_directories( ../src/common )
+include_directories( ${CMAKE_CURRENT_BINARY_DIR} )
+include_directories( ${THIRD_PARTY_DIR} )
+
+set(TFLITE_INCLUDE_DIRS
+ "${TENSORFLOW_SOURCE_DIR}"
+ "${TFLITE_FLATBUFFERS_SCHEMA_DIR}"
+)
+include_directories(
+ BEFORE
+ ${TFLITE_INCLUDE_DIRS}
+)
+
+set(MEMORY_ADVICE_SRCS
+ c_header_check.c
+ core/memory_advice.cpp
+ core/memory_advice_impl.cpp
+ core/memory_advice_c.cpp
+ core/memory_advice_utils.cpp
+ core/metrics_provider.cpp
+ core/state_watcher.cpp
+ core/predictor.cpp
+ test/basic.cpp
+ ../src/common/jni/jni_helper.cpp
+ ../src/common/jni/jni_wrap.cpp
+ ../src/common/jni/jnictx.cpp
+ ../src/common/apk_utils.cpp
+ ../src/common/system_utils.cpp
+ ${THIRD_PARTY_DIR}/json11/json11.cpp
+ advisor_parameters.cpp
+ ${MEMORYADVICE_RESOURCES_DIR}/realtime.tflite
+ ${MEMORYADVICE_RESOURCES_DIR}/oom_features.json
+)
+
+add_library( tflite SHARED IMPORTED )
+set_target_properties( tflite PROPERTIES IMPORTED_LOCATION ${THIRD_PARTY_DIR}/tensorflow-lite/res/${CMAKE_ANDROID_ARCH_ABI}/libtensorflowlite_jni.so )
+
+add_library( tflite_static STATIC IMPORTED )
+set_target_properties( tflite_static PROPERTIES IMPORTED_LOCATION ${THIRD_PARTY_DIR}/tensorflow-lite/res/${CMAKE_ANDROID_ARCH_ABI}/libtensorflow-lite.a )
+
+
+add_library(memory_advice_static_pre STATIC ${MEMORY_ADVICE_SRCS})
+add_library(memory_advice SHARED ${MEMORY_ADVICE_SRCS})
+target_link_libraries(memory_advice
+ PUBLIC
+ android
+ log
+ tflite
+)
+
+target_link_libraries(memory_advice_static_pre
+ PUBLIC
+ android
+ log
+ tflite_static
+)
+
+#use the ar command to bundle the tensorflow lite static library into memory advice
+set(mri_file ${CMAKE_CURRENT_BINARY_DIR}/memory_advice_static.ar)
+
+file(WRITE ${mri_file} "create ${CMAKE_CURRENT_BINARY_DIR}/libmemory_advice_static.a\n")
+file(APPEND ${mri_file} "addlib ${CMAKE_CURRENT_BINARY_DIR}/libmemory_advice_static_pre.a\n")
+file(APPEND ${mri_file} "addlib ${THIRD_PARTY_DIR}/tensorflow-lite/res/${CMAKE_ANDROID_ARCH_ABI}/libtensorflow-lite.a\n")
+
+file(APPEND ${mri_file} "save\n")
+file(APPEND ${mri_file} "end\n")
+
+set(output_archive_placeholder_file ${CMAKE_CURRENT_BINARY_DIR}/${output_archive}.placeholder.cpp)
+add_custom_command(OUTPUT ${output_archive_placeholder_file}
+ COMMAND touch ${output_archive_placeholder_file}
+ DEPENDS memory_advice_static_pre)
+
+
+add_library(memory_advice_static STATIC ${output_archive_placeholder_file})
+add_custom_command(TARGET memory_advice_static
+ POST_BUILD
+ COMMAND ${CMAKE_AR} -M < ${mri_file})
diff --git a/GameController/build.gradle b/games-memory-advice/build.gradle
similarity index 66%
rename from GameController/build.gradle
rename to games-memory-advice/build.gradle
index f97de97..aa88ab1 100644
--- a/GameController/build.gradle
+++ b/games-memory-advice/build.gradle
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,10 +18,12 @@
id 'com.android.library'
}
+buildDir="../../out_memory_advice"
+
android {
defaultConfig {
- minSdkVersion 16
+ minSdkVersion 20
compileSdkVersion 31
targetSdkVersion 31
versionCode 1
@@ -31,21 +33,47 @@
}
buildTypes {
+ debug {
+ }
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
+ externalNativeBuild {
+ cmake {
+ path "CMakeLists.txt"
+ }
+ }
+
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
+
+ externalNativeBuild {
+ cmake {
+ path "CMakeLists.txt"
+ version "3.18.0+"
+ }
+ }
+
+ buildFeatures {
+ prefabPublishing true
+ }
+ prefab {
+ memory_advice {
+ headers "include"
+ }
+
+ memory_advice_static {
+ headers "include"
+ }
+ }
+
}
-dependencies {
- implementation "androidx.annotation:annotation:1.3.0"
-}
repositories {
mavenCentral()
-}
+}
\ No newline at end of file
diff --git a/src/memory_advice/c_header_check.c b/games-memory-advice/c_header_check.c
similarity index 100%
rename from src/memory_advice/c_header_check.c
rename to games-memory-advice/c_header_check.c
diff --git a/src/memory_advice/core/memory_advice.cpp b/games-memory-advice/core/memory_advice.cpp
similarity index 65%
rename from src/memory_advice/core/memory_advice.cpp
rename to games-memory-advice/core/memory_advice.cpp
index 55b9114..4cf71e8 100644
--- a/src/memory_advice/core/memory_advice.cpp
+++ b/games-memory-advice/core/memory_advice.cpp
@@ -33,11 +33,10 @@
extern const char* parameters_string;
static MemoryAdviceImpl* s_impl;
-static std::unique_ptr<StateWatcher> s_watcher;
MemoryAdvice_ErrorCode Init(const char* params) {
if (s_impl != nullptr) return MEMORYADVICE_ERROR_ALREADY_INITIALIZED;
- s_impl = new MemoryAdviceImpl(params);
+ s_impl = new MemoryAdviceImpl(params, nullptr, nullptr, nullptr);
return s_impl->InitializationErrorCode();
}
@@ -64,32 +63,41 @@
return MEMORYADVICE_ERROR_OK;
}
-MemoryAdvice_ErrorCode GetMemoryState(MemoryAdvice_MemoryState* state) {
- if (s_impl == nullptr) return MEMORYADVICE_ERROR_NOT_INITIALIZED;
- *state = s_impl->GetMemoryState();
- return MEMORYADVICE_ERROR_OK;
+MemoryAdvice_MemoryState GetMemoryState() {
+ if (s_impl == nullptr)
+ return static_cast<MemoryAdvice_MemoryState>(
+ MEMORYADVICE_ERROR_NOT_INITIALIZED);
+ return s_impl->GetMemoryState();
}
-MemoryAdvice_ErrorCode GetAvailableMemory(int64_t* estimate) {
- if (s_impl == nullptr) return MEMORYADVICE_ERROR_NOT_INITIALIZED;
- *estimate = s_impl->GetAvailableMemory();
- return MEMORYADVICE_ERROR_OK;
+float GetPercentageAvailableMemory() {
+ if (s_impl == nullptr)
+ return static_cast<float>(MEMORYADVICE_ERROR_NOT_INITIALIZED);
+ return s_impl->GetPercentageAvailableMemory();
}
-MemoryAdvice_ErrorCode SetWatcher(uint64_t intervalMillis,
- MemoryAdvice_WatcherCallback callback) {
- if (s_impl == nullptr) return MEMORYADVICE_ERROR_NOT_INITIALIZED;
- if (s_watcher.get() != nullptr)
- return MEMORYADVICE_ERROR_WATCHER_ALREADY_SET;
- s_watcher =
- std::make_unique<StateWatcher>(s_impl, callback, intervalMillis);
- return MEMORYADVICE_ERROR_OK;
+int64_t GetTotalMemory() {
+ if (s_impl == nullptr)
+ return static_cast<int64_t>(MEMORYADVICE_ERROR_NOT_INITIALIZED);
+ return s_impl->GetTotalMemory();
}
-MemoryAdvice_ErrorCode RemoveWatcher() {
+MemoryAdvice_ErrorCode RegisterWatcher(uint64_t intervalMillis,
+ MemoryAdvice_WatcherCallback callback,
+ void* user_data) {
if (s_impl == nullptr) return MEMORYADVICE_ERROR_NOT_INITIALIZED;
- if (s_watcher.get() != nullptr) s_watcher.reset();
- return MEMORYADVICE_ERROR_OK;
+ return s_impl->RegisterWatcher(intervalMillis, callback, user_data);
+}
+
+MemoryAdvice_ErrorCode UnregisterWatcher(
+ MemoryAdvice_WatcherCallback callback) {
+ if (s_impl == nullptr) return MEMORYADVICE_ERROR_NOT_INITIALIZED;
+ return s_impl->UnregisterWatcher(callback);
+}
+
+int32_t BaseTests() {
+ if (s_impl == nullptr) return MEMORYADVICE_ERROR_NOT_INITIALIZED;
+ return s_impl->BaseTests();
}
} // namespace memory_advice
diff --git a/games-memory-advice/core/memory_advice_c.cpp b/games-memory-advice/core/memory_advice_c.cpp
new file mode 100644
index 0000000..7d99289
--- /dev/null
+++ b/games-memory-advice/core/memory_advice_c.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "jni/jni_helper.h"
+#include "memory_advice/memory_advice.h"
+#include "memory_advice_internal.h"
+#include "metrics_provider.h"
+
+namespace jni = gamesdk::jni;
+
+extern "C" {
+
+MemoryAdvice_ErrorCode MemoryAdvice_init(JNIEnv *env, jobject context) {
+ MEMORY_ADVICE_VERSION_SYMBOL();
+ jni::Init(env, context);
+ return memory_advice::Init();
+}
+
+MemoryAdvice_ErrorCode MemoryAdvice_initWithParams(JNIEnv *env, jobject context,
+ const char *params) {
+ MEMORY_ADVICE_VERSION_SYMBOL();
+ jni::Init(env, context);
+ return memory_advice::Init(params);
+}
+
+MemoryAdvice_MemoryState MemoryAdvice_getMemoryState() {
+ return memory_advice::GetMemoryState();
+}
+
+MemoryAdvice_ErrorCode MemoryAdvice_getAdvice(
+ MemoryAdvice_JsonSerialization *advice) {
+ return memory_advice::GetAdvice(advice);
+}
+
+MemoryAdvice_ErrorCode MemoryAdvice_registerWatcher(
+ uint64_t intervalMillis, MemoryAdvice_WatcherCallback callback,
+ void *user_data) {
+ return memory_advice::RegisterWatcher(intervalMillis, callback, user_data);
+}
+
+MemoryAdvice_ErrorCode MemoryAdvice_unregisterWatcher(
+ MemoryAdvice_WatcherCallback callback) {
+ return memory_advice::UnregisterWatcher(callback);
+}
+
+float MemoryAdvice_getPercentageAvailableMemory() {
+ return memory_advice::GetPercentageAvailableMemory();
+}
+
+int64_t MemoryAdvice_getTotalMemory() {
+ return memory_advice::GetTotalMemory();
+}
+
+void MemoryAdvice_JsonSerialization_free(MemoryAdvice_JsonSerialization *ser) {
+ if (ser->dealloc) {
+ ser->dealloc(ser);
+ ser->dealloc = NULL;
+ }
+}
+
+int32_t MemoryAdvice_test() { return memory_advice::BaseTests(); }
+
+void MEMORY_ADVICE_VERSION_SYMBOL() {
+ // Intentionally empty: this function is used to ensure that the proper
+ // version of the library is linked against the proper headers.
+ // In case of mismatch, a linker error will be triggered because of an
+ // undefined symbol, as the name of the function depends on the version.
+}
+
+} // extern "C"
diff --git a/games-memory-advice/core/memory_advice_impl.cpp b/games-memory-advice/core/memory_advice_impl.cpp
new file mode 100644
index 0000000..3e08817
--- /dev/null
+++ b/games-memory-advice/core/memory_advice_impl.cpp
@@ -0,0 +1,304 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "memory_advice_impl.h"
+
+#include <algorithm>
+#include <chrono>
+
+#include "memory_advice_utils.h"
+#include "system_utils.h"
+
+constexpr double BYTES_IN_GB = 1024 * 1024 * 1024;
+
+namespace memory_advice {
+
+using namespace json11;
+
+MemoryAdviceImpl::MemoryAdviceImpl(const char* params,
+ IMetricsProvider* metrics_provider,
+ IPredictor* realtime_predictor,
+ IPredictor* available_predictor)
+ : metrics_provider_(metrics_provider),
+ realtime_predictor_(realtime_predictor),
+ available_predictor_(available_predictor) {
+ if (metrics_provider_ == nullptr) {
+ default_metrics_provider_ = std::make_unique<DefaultMetricsProvider>();
+ metrics_provider_ = default_metrics_provider_.get();
+ }
+ if (realtime_predictor_ == nullptr) {
+ default_realtime_predictor_ = std::make_unique<DefaultPredictor>();
+ realtime_predictor_ = default_realtime_predictor_.get();
+ }
+ if (available_predictor_ == nullptr) {
+ default_available_predictor_ = std::make_unique<DefaultPredictor>();
+ available_predictor_ = default_available_predictor_.get();
+ }
+
+ initialization_error_code_ =
+ realtime_predictor_->Init("realtime.tflite", "realtime_features.json");
+ if (initialization_error_code_ != MEMORYADVICE_ERROR_OK) {
+ return;
+ }
+ initialization_error_code_ = available_predictor_->Init(
+ "available.tflite", "available_features.json");
+ if (initialization_error_code_ != MEMORYADVICE_ERROR_OK) {
+ return;
+ }
+ initialization_error_code_ = ProcessAdvisorParameters(params);
+ if (initialization_error_code_ != MEMORYADVICE_ERROR_OK) {
+ return;
+ }
+ baseline_ = GenerateBaselineMetrics();
+ baseline_["constant"] = GenerateConstantMetrics();
+ build_ = utils::GetBuildInfo();
+}
+
+MemoryAdvice_ErrorCode MemoryAdviceImpl::ProcessAdvisorParameters(
+ const char* parameters) {
+ std::string err;
+ advisor_parameters_ = Json::parse(parameters, err).object_items();
+ if (!err.empty()) {
+ ALOGE("Error while parsing advisor parameters: %s", err.c_str());
+ return MEMORYADVICE_ERROR_ADVISOR_PARAMETERS_INVALID;
+ }
+ return MEMORYADVICE_ERROR_OK;
+}
+
+MemoryAdvice_MemoryState MemoryAdviceImpl::GetMemoryState() {
+ Json::object advice = GetAdvice();
+ if (advice.find("warnings") != advice.end()) {
+ Json::array warnings = advice["warnings"].array_items();
+ for (auto& it : warnings) {
+ if (it.object_items().at("level").string_value() == "red") {
+ return MEMORYADVICE_STATE_CRITICAL;
+ }
+ }
+ return MEMORYADVICE_STATE_APPROACHING_LIMIT;
+ }
+ return MEMORYADVICE_STATE_OK;
+}
+
+int64_t MemoryAdviceImpl::GetAvailableMemory() {
+ // TODO(b/219040574): this is not a reliable/available number currently
+ Json::object advice = GetAdvice();
+ if (advice.find("metrics") != advice.end()) {
+ Json::object metrics = advice["metrics"].object_items();
+ if (metrics.find("predictedAvailable") != metrics.end()) {
+ return static_cast<int64_t>(
+ metrics["predictedAvailable"].number_value());
+ }
+ }
+ return 0L;
+}
+
+float MemoryAdviceImpl::GetPercentageAvailableMemory() {
+ Json::object advice = GetAdvice();
+ if (advice.find("metrics") == advice.end()) return 0.0f;
+ Json::object metrics = advice["metrics"].object_items();
+ if (metrics.find("predictedUsage") == metrics.end()) return 0.0f;
+ return 100.0f * (1.0f - static_cast<float>(
+ metrics["predictedUsage"].number_value()));
+}
+
+int64_t MemoryAdviceImpl::GetTotalMemory() {
+ return static_cast<int64_t>(baseline_.at("constant")
+ .object_items()
+ .at("MemoryInfo")
+ .object_items()
+ .at("totalMem")
+ .number_value());
+}
+
+Json::object MemoryAdviceImpl::GetAdvice() {
+ CheckCancelledWatchers();
+
+ std::lock_guard<std::mutex> lock(advice_mutex_);
+ double start_time = MillisecondsSinceEpoch();
+ Json::object advice;
+ Json::object data;
+ Json::object variable_spec = advisor_parameters_.at("metrics")
+ .object_items()
+ .at("variable")
+ .object_items();
+ Json::object variable_metrics = GenerateVariableMetrics();
+
+ data["baseline"] = baseline_;
+ data["sample"] = variable_metrics;
+ data["build"] = build_;
+
+ if (variable_spec.find("predictRealtime") != variable_spec.end() &&
+ variable_spec.at("predictRealtime").bool_value()) {
+ variable_metrics["predictedUsage"] =
+ Json(realtime_predictor_->Predict(data));
+ }
+
+ if (variable_spec.find("availableRealtime") != variable_spec.end() &&
+ variable_spec.at("availableRealtime").bool_value()) {
+ variable_metrics["predictedAvailable"] =
+ Json(BYTES_IN_GB * available_predictor_->Predict(data));
+ }
+ Json::array warnings;
+ Json::object heuristics =
+ advisor_parameters_.at("heuristics").object_items();
+
+ if (heuristics.find("formulas") != heuristics.end()) {
+ for (auto& entry : heuristics["formulas"].object_items()) {
+ for (auto& formula_object : entry.second.array_items()) {
+ std::string formula = formula_object.string_value();
+ formula.erase(std::remove_if(formula.begin(), formula.end(),
+ (int (*)(int))std::isspace),
+ formula.end());
+ if (utils::EvaluateBoolean(formula, variable_metrics)) {
+ Json::object warning;
+ warning["formula"] = formula;
+ warning["level"] = entry.first;
+ warnings.push_back(warning);
+ }
+ }
+ }
+ }
+
+ if (!warnings.empty()) {
+ advice["warnings"] = warnings;
+ }
+
+ advice["metrics"] = variable_metrics;
+ return advice;
+}
+
+Json MemoryAdviceImpl::GetValue(Json::object object, std::string key) {
+ if (object.find(key) != object.end()) {
+ return object[key];
+ }
+ for (auto& it : object) {
+ Json value = GetValue(it.second.object_items(), key);
+ if (!value.is_null()) {
+ return value;
+ }
+ }
+ return Json();
+}
+
+Json::object MemoryAdviceImpl::GenerateMetricsFromFields(Json::object fields) {
+ Json::object metrics;
+ for (auto& it : metrics_provider_->metrics_categories_) {
+ if (fields.find(it.first) != fields.end()) {
+ metrics[it.first] = ExtractValues(it.second, fields[it.first]);
+ }
+ }
+ metrics["meta"] = (Json::object){{"time", MillisecondsSinceEpoch()}};
+ return metrics;
+}
+
+Json::object MemoryAdviceImpl::ExtractValues(
+ IMetricsProvider::MetricsFunction metrics_function, Json fields) {
+ double start_time = MillisecondsSinceEpoch();
+ Json::object metrics = (metrics_provider_->*metrics_function)();
+ Json::object extracted_metrics;
+ if (fields.bool_value()) {
+ extracted_metrics = metrics;
+ } else {
+ for (auto& it : fields.object_items()) {
+ if (it.second.bool_value() &&
+ metrics.find(it.first) != metrics.end()) {
+ extracted_metrics[it.first] = metrics[it.first];
+ }
+ }
+ }
+
+ extracted_metrics["_meta"] = {
+ {"duration", Json(MillisecondsSinceEpoch() - start_time)}};
+ return extracted_metrics;
+}
+
+double MemoryAdviceImpl::MillisecondsSinceEpoch() {
+ using namespace std::chrono;
+ return duration_cast<milliseconds>(system_clock::now().time_since_epoch())
+ .count();
+}
+
+Json::object MemoryAdviceImpl::GenerateVariableMetrics() {
+ return GenerateMetricsFromFields(advisor_parameters_.at("metrics")
+ .object_items()
+ .at("variable")
+ .object_items());
+}
+
+Json::object MemoryAdviceImpl::GenerateBaselineMetrics() {
+ return GenerateMetricsFromFields(advisor_parameters_.at("metrics")
+ .object_items()
+ .at("baseline")
+ .object_items());
+}
+
+Json::object MemoryAdviceImpl::GenerateConstantMetrics() {
+ return GenerateMetricsFromFields(advisor_parameters_.at("metrics")
+ .object_items()
+ .at("constant")
+ .object_items());
+}
+
+MemoryAdvice_ErrorCode MemoryAdviceImpl::RegisterWatcher(
+ uint64_t intervalMillis, MemoryAdvice_WatcherCallback callback,
+ void* user_data) {
+ std::lock_guard<std::mutex> guard(active_watchers_mutex_);
+ active_watchers_.push_back(std::make_unique<StateWatcher>(
+ this, callback, user_data, intervalMillis));
+ return MEMORYADVICE_ERROR_OK;
+}
+
+MemoryAdvice_ErrorCode MemoryAdviceImpl::UnregisterWatcher(
+ MemoryAdvice_WatcherCallback callback) {
+ // We can't simply erase the watcher because the callback thread might still
+ // be running which would mean blocking here for it to finish. So signal to
+ // the thread to exit and put the StateWatcher object on a cancelled list
+ // that is checked periodically.
+ std::lock_guard<std::mutex> guard(active_watchers_mutex_);
+ std::vector<WatcherContainer::iterator> to_move;
+ // Search for watchers with the same callback.
+ for (auto it = active_watchers_.begin(); it != active_watchers_.end();
+ ++it) {
+ if ((*it)->Callback() == callback) {
+ to_move.push_back(it);
+ }
+ }
+
+ if (to_move.empty()) return MEMORYADVICE_ERROR_WATCHER_NOT_FOUND;
+
+ std::lock_guard<std::mutex> guard2(cancelled_watchers_mutex_);
+ for (auto it : to_move) {
+ (*it)->Cancel();
+ cancelled_watchers_.push_back(
+ std::move(*it)); // Put StateWatcher on cancelled list.
+ active_watchers_.erase(it);
+ }
+ return MEMORYADVICE_ERROR_OK;
+}
+
+void MemoryAdviceImpl::CheckCancelledWatchers() {
+ std::lock_guard<std::mutex> guard(cancelled_watchers_mutex_);
+ auto it = cancelled_watchers_.begin();
+ while (it != cancelled_watchers_.end()) {
+ if (!(*it)->ThreadRunning())
+ it = cancelled_watchers_.erase(
+ it); // Calls destructor on StateWatcher
+ else
+ ++it;
+ }
+}
+
+} // namespace memory_advice
diff --git a/src/memory_advice/core/memory_advice_impl.h b/games-memory-advice/core/memory_advice_impl.h
similarity index 63%
rename from src/memory_advice/core/memory_advice_impl.h
rename to games-memory-advice/core/memory_advice_impl.h
index 284e979..6fcef4a 100644
--- a/src/memory_advice/core/memory_advice_impl.h
+++ b/games-memory-advice/core/memory_advice_impl.h
@@ -19,9 +19,9 @@
#include <memory>
#include <mutex>
-#include "device_profiler.h"
#include "metrics_provider.h"
#include "predictor.h"
+#include "state_watcher.h"
namespace memory_advice {
@@ -29,15 +29,28 @@
class MemoryAdviceImpl {
private:
- std::unique_ptr<MetricsProvider> metrics_provider_;
- std::unique_ptr<DeviceProfiler> device_profiler_;
- std::unique_ptr<Predictor> realtime_predictor_, available_predictor_,
- oom_predictor_;
+ IMetricsProvider* metrics_provider_;
+ /** @brief A predictor that tries to predict the current memory consumption
+ * as a percentage of total memory. */
+ IPredictor* realtime_predictor_;
+ /** @brief A predictor that attempts to predict the remanining memory that
+ * can be safely allocated. */
+ IPredictor* available_predictor_;
Json::object advisor_parameters_;
Json::object baseline_;
- Json::object device_profile_;
+ Json::object build_;
std::mutex advice_mutex_;
+ std::unique_ptr<IMetricsProvider> default_metrics_provider_;
+ std::unique_ptr<IPredictor> default_realtime_predictor_,
+ default_available_predictor_;
+
+ typedef std::vector<std::unique_ptr<StateWatcher>> WatcherContainer;
+ WatcherContainer active_watchers_;
+ std::mutex active_watchers_mutex_;
+ WatcherContainer cancelled_watchers_;
+ std::mutex cancelled_watchers_mutex_;
+
MemoryAdvice_ErrorCode initialization_error_code_ = MEMORYADVICE_ERROR_OK;
MemoryAdvice_ErrorCode ProcessAdvisorParameters(const char* parameters);
@@ -59,22 +72,23 @@
* returned object also includes how long it took to gather the metrics.
*/
Json::object ExtractValues(
- MetricsProvider::MetricsFunction metrics_function, Json fields);
+ IMetricsProvider::MetricsFunction metrics_function, Json fields);
double MillisecondsSinceEpoch();
/** @brief Find a value in a JSON object, even when it is nested in
* sub-dictionaries in the object. */
Json GetValue(Json::object object, std::string key);
+ /** @brief Delete any watchers that have finished their thread of execution
+ */
+ void CheckCancelledWatchers();
public:
- MemoryAdviceImpl(const char* params);
+ MemoryAdviceImpl(const char* params, IMetricsProvider* metrics_provider,
+ IPredictor* realtime_predictor,
+ IPredictor* available_predictor);
/** @brief Creates an advice object by reading variable metrics and
* feeding them into the provided machine learning model.
*/
Json::object GetAdvice();
- /** @brief Creates an advice object by reading variable metrics and
- * comparing them to baseline values and values provided by device profiler.
- */
- Json::object GetAdviceDeprecated();
/** @brief Evaluates information from the current metrics and returns a
* memory state.
*/
@@ -83,6 +97,15 @@
* estimate for how much more memory is available, in bytes.
*/
int64_t GetAvailableMemory();
+ /** @brief Evaluates information from the current metrics and returns an
+ * estimate for how much more memory is available, as a percentage of the
+ * total memory.
+ */
+ float GetPercentageAvailableMemory();
+ /** @brief Returns the total memory of the device, as reported by
+ * ActivityManager#getMemoryInfo()
+ */
+ int64_t GetTotalMemory();
/** @brief Reads the variable part of the advisor_parameters_ and reports
* metrics for those fields. */
Json::object GenerateVariableMetrics();
@@ -92,6 +115,19 @@
/** @brief Reads the constant part of the advisor_parameters_ and reports
* metrics for those fields. */
Json::object GenerateConstantMetrics();
+ /** @brief Register watcher callback
+ */
+ MemoryAdvice_ErrorCode RegisterWatcher(
+ uint64_t intervalMillis, MemoryAdvice_WatcherCallback callback,
+ void* user_data);
+ /** @brief Unregister watcher callback
+ */
+ MemoryAdvice_ErrorCode UnregisterWatcher(
+ MemoryAdvice_WatcherCallback callback);
+
+ /** @brief Perform basic checking
+ */
+ int32_t BaseTests();
MemoryAdvice_ErrorCode InitializationErrorCode() const {
return initialization_error_code_;
diff --git a/games-memory-advice/core/memory_advice_internal.h b/games-memory-advice/core/memory_advice_internal.h
new file mode 100644
index 0000000..12c84c2
--- /dev/null
+++ b/games-memory-advice/core/memory_advice_internal.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "memory_advice/memory_advice.h"
+#include "memory_advice/memory_advice_debug.h"
+
+// Internal macros to generate a symbol to track Memory Advice version, do not
+// use directly.
+#define MEMORY_ADVICE_VERSION_CONCAT_NX(PREFIX, MAJOR, MINOR) \
+ PREFIX##_##MAJOR##_##MINOR
+#define MEMORY_ADVICE_VERSION_CONCAT(PREFIX, MAJOR, MINOR) \
+ MEMORY_ADVICE_VERSION_CONCAT_NX(PREFIX, MAJOR, MINOR)
+#define MEMORY_ADVICE_VERSION_SYMBOL \
+ MEMORY_ADVICE_VERSION_CONCAT(MemoryAdvice_version, \
+ MEMORY_ADVICE_MAJOR_VERSION, \
+ MEMORY_ADVICE_MINOR_VERSION)
+
+// Internal function to track MemoryAdvice version bundled in a binary. Do not
+// call directly. If you are getting linker errors related to
+// MemoryAdvice_version_x_y, you probably have a mismatch between the header
+// used at compilation and the actually library used by the linker.
+extern "C" void MEMORY_ADVICE_VERSION_SYMBOL();
+
+// These functions are implemented in memory_advice.cpp.
+// They are mostly the same as the C interface, but take C++ types.
+
+namespace memory_advice {
+
+MemoryAdvice_ErrorCode Init();
+MemoryAdvice_ErrorCode Init(const char* params);
+MemoryAdvice_ErrorCode GetAdvice(MemoryAdvice_JsonSerialization* advice);
+MemoryAdvice_MemoryState GetMemoryState();
+float GetPercentageAvailableMemory();
+int64_t GetTotalMemory();
+MemoryAdvice_ErrorCode RegisterWatcher(uint64_t intervalMillis,
+ MemoryAdvice_WatcherCallback callback,
+ void* user_data);
+MemoryAdvice_ErrorCode UnregisterWatcher(MemoryAdvice_WatcherCallback callback);
+int32_t BaseTests();
+
+} // namespace memory_advice
diff --git a/games-memory-advice/core/memory_advice_utils.cpp b/games-memory-advice/core/memory_advice_utils.cpp
new file mode 100644
index 0000000..3dcfbee
--- /dev/null
+++ b/games-memory-advice/core/memory_advice_utils.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "memory_advice_utils.h"
+
+#include <stdlib.h>
+
+#include <algorithm>
+#include <cctype>
+#include <fstream>
+#include <iterator>
+#include <map>
+#include <sstream>
+#include <streambuf>
+#include <string>
+
+#include "Log.h"
+#include "jni/jni_wrap.h"
+#include "memory_advice/memory_advice.h"
+#include "system_utils.h"
+
+#define LOG_TAG "MemoryAdvice:DeviceProfiler"
+
+using namespace json11;
+
+namespace memory_advice {
+
+namespace utils {
+
+bool EvaluateBoolean(std::string formula, Json::object metrics) {
+ if (formula.find('>') != std::string::npos) {
+ return EvaluateNumber(formula.substr(0, formula.find('>')), metrics) >
+ EvaluateNumber(formula.substr(formula.find('>') + 1), metrics);
+ } else if (formula.find('<') != std::string::npos) {
+ return EvaluateNumber(formula.substr(0, formula.find('<')), metrics) <
+ EvaluateNumber(formula.substr(formula.find('<') + 1), metrics);
+ } else {
+ return false;
+ }
+}
+
+double EvaluateNumber(std::string formula, Json::object metrics) {
+ if (formula.find('/') != std::string::npos) {
+ return EvaluateNumber(formula.substr(0, formula.find('/')), metrics) /
+ EvaluateNumber(formula.substr(formula.find('/') + 1), metrics);
+ } else if (formula.find('*') != std::string::npos) {
+ return EvaluateNumber(formula.substr(0, formula.find('*')), metrics) *
+ EvaluateNumber(formula.substr(formula.find('*') + 1), metrics);
+ } else if (formula.find('+') != std::string::npos) {
+ return EvaluateNumber(formula.substr(0, formula.find('+')), metrics) +
+ EvaluateNumber(formula.substr(formula.find('+') + 1), metrics);
+ } else if (formula.find('-') != std::string::npos) {
+ return EvaluateNumber(formula.substr(0, formula.find('-')), metrics) -
+ EvaluateNumber(formula.substr(formula.find('-') + 1), metrics);
+ } else if (std::isdigit(formula[0])) {
+ return std::stod(formula);
+ } else {
+ return metrics[formula].number_value();
+ }
+}
+
+Json::object GetBuildInfo() {
+ // The current version of default.json only uses the sdk version from the
+ // build parameters; so having this function only return that value saves
+ // time during initialization of the library
+ Json::object build_info;
+ build_info["version"] = {
+ {"sdk_int", Json(gamesdk::GetSystemPropAsInt("ro.build.version.sdk"))}};
+
+ return build_info;
+}
+
+} // namespace utils
+
+} // namespace memory_advice
\ No newline at end of file
diff --git a/games-memory-advice/core/memory_advice_utils.h b/games-memory-advice/core/memory_advice_utils.h
new file mode 100644
index 0000000..7fc97cc
--- /dev/null
+++ b/games-memory-advice/core/memory_advice_utils.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <memory>
+#include <string>
+
+#include "Log.h"
+#include "json11/json11.hpp"
+
+using namespace json11;
+
+namespace memory_advice {
+
+namespace utils {
+
+/*
+ * A function that evaluates a given boolean expression and return the result.
+ * The formula should contain a single greater than or less than operator. The
+ * two sides of the operator can contain numbers, the four basic arithmetic
+ * operators, and literals. The value for the literals are evaluated by checking
+ * for them in the provided metrics.
+ */
+bool EvaluateBoolean(std::string formula, Json::object metrics);
+/*
+ * A function that evaluates a given arithmetic expression and returns the
+ * result. The formula can contain numbers, the four basic arithmetic operators,
+ * and literals. The value for the literals are evaluated by checking for them
+ * in the provided metrics.
+ */
+double EvaluateNumber(std::string formula, Json::object metrics);
+Json::object GetBuildInfo();
+
+} // namespace utils
+
+} // namespace memory_advice
\ No newline at end of file
diff --git a/src/memory_advice/core/metrics_provider.cpp b/games-memory-advice/core/metrics_provider.cpp
similarity index 78%
rename from src/memory_advice/core/metrics_provider.cpp
rename to games-memory-advice/core/metrics_provider.cpp
index fb873d9..63cbddb 100644
--- a/src/memory_advice/core/metrics_provider.cpp
+++ b/games-memory-advice/core/metrics_provider.cpp
@@ -42,38 +42,44 @@
using namespace json11;
-Json::object MetricsProvider::GetMeminfoValues() {
+Json::object DefaultMetricsProvider::GetMeminfoValues() {
return GetMemoryValuesFromFile("/proc/meminfo", MEMINFO_REGEX);
}
-Json::object MetricsProvider::GetStatusValues() {
+Json::object DefaultMetricsProvider::GetStatusValues() {
std::stringstream ss_path;
ss_path << "/proc/" << getpid() << "/status";
return GetMemoryValuesFromFile(ss_path.str(), STATUS_REGEX);
}
-Json::object MetricsProvider::GetProcValues() {
+Json::object DefaultMetricsProvider::GetProcValues() {
Json::object proc_map;
proc_map["oom_score"] = Json(GetOomScore());
return proc_map;
}
-Json::object MetricsProvider::GetActivityManagerValues() {
+Json::object DefaultMetricsProvider::GetActivityManagerValues() {
Json::object metrics_map;
+ java::Object obj = AppContext().getSystemService(
+ android::content::Context::ACTIVITY_SERVICE);
+ android::app::ActivityManager activity_manager(std::move(obj));
metrics_map["MemoryClass"] =
- Json(activity_manager_->getMemoryClass() * BYTES_IN_MB);
+ Json(activity_manager.getMemoryClass() * BYTES_IN_MB);
metrics_map["LargeMemoryClass"] =
- Json(activity_manager_->getLargeMemoryClass() * BYTES_IN_MB);
- metrics_map["LowRamDevice"] = Json(activity_manager_->isLowRamDevice());
+ Json(activity_manager.getLargeMemoryClass() * BYTES_IN_MB);
+ metrics_map["LowRamDevice"] = Json(activity_manager.isLowRamDevice());
return metrics_map;
}
-Json::object MetricsProvider::GetActivityManagerMemoryInfo() {
+Json::object DefaultMetricsProvider::GetActivityManagerMemoryInfo() {
android::app::MemoryInfo memory_info;
Json::object metrics_map;
- activity_manager_->getMemoryInfo(memory_info);
+ java::Object obj = AppContext().getSystemService(
+ android::content::Context::ACTIVITY_SERVICE);
+ android::app::ActivityManager activity_manager(std::move(obj));
+ activity_manager.getMemoryInfo(memory_info);
metrics_map["threshold"] = Json((double)memory_info.threshold());
metrics_map["availMem"] = Json((double)memory_info.availMem());
metrics_map["totalMem"] = Json((double)memory_info.totalMem());
@@ -82,7 +88,7 @@
return metrics_map;
}
-Json::object MetricsProvider::GetDebugValues() {
+Json::object DefaultMetricsProvider::GetDebugValues() {
Json::object metrics_map;
metrics_map["nativeHeapAllocatedSize"] =
Json((double)android_debug_.getNativeHeapAllocatedSize());
@@ -94,7 +100,7 @@
return metrics_map;
}
-Json::object MetricsProvider::GetMemoryValuesFromFile(
+Json::object DefaultMetricsProvider::GetMemoryValuesFromFile(
const std::string &path, const std::regex &pattern) {
std::ifstream file_stream(path);
Json::object metrics_map;
@@ -115,7 +121,7 @@
return metrics_map;
}
-int32_t MetricsProvider::GetOomScore() {
+int32_t DefaultMetricsProvider::GetOomScore() {
std::stringstream ss_path;
ss_path << "/proc/" << getpid() << "/oom_score";
std::ifstream oom_file(ss_path.str());
@@ -129,10 +135,4 @@
}
}
-MetricsProvider::MetricsProvider() {
- java::Object obj = AppContext().getSystemService(
- android::content::Context::ACTIVITY_SERVICE);
- activity_manager_ =
- std::make_unique<android::app::ActivityManager>(std::move(obj));
-}
} // namespace memory_advice
diff --git a/src/memory_advice/core/metrics_provider.h b/games-memory-advice/core/metrics_provider.h
similarity index 61%
rename from src/memory_advice/core/metrics_provider.h
rename to games-memory-advice/core/metrics_provider.h
index fb5e32b..421b76b 100644
--- a/src/memory_advice/core/metrics_provider.h
+++ b/games-memory-advice/core/metrics_provider.h
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#pragma once
+
#include <map>
#include <memory>
#include <regex>
@@ -34,43 +36,55 @@
/**
* @brief Provides memory info from various metrics
*/
-class MetricsProvider {
+class IMetricsProvider {
public:
- typedef Json::object (MetricsProvider::*MetricsFunction)();
+ typedef Json::object (IMetricsProvider::*MetricsFunction)();
/** @brief A map matching metrics category names to their functions */
std::map<std::string, MetricsFunction> metrics_categories_ = {
- {"meminfo", &MetricsProvider::GetMeminfoValues},
- {"status", &MetricsProvider::GetStatusValues},
- {"proc", &MetricsProvider::GetProcValues},
- {"debug", &MetricsProvider::GetDebugValues},
- {"MemoryInfo", &MetricsProvider::GetActivityManagerMemoryInfo},
- {"ActivityManager", &MetricsProvider::GetActivityManagerValues}};
+ {"meminfo", &IMetricsProvider::GetMeminfoValues},
+ {"status", &IMetricsProvider::GetStatusValues},
+ {"proc", &IMetricsProvider::GetProcValues},
+ {"debug", &IMetricsProvider::GetDebugValues},
+ {"MemoryInfo", &IMetricsProvider::GetActivityManagerMemoryInfo},
+ {"ActivityManager", &IMetricsProvider::GetActivityManagerValues}};
/** @brief Get a list of memory metrics stored in /proc/meminfo */
- Json::object GetMeminfoValues();
+ virtual Json::object GetMeminfoValues() = 0;
/** @brief Get a list of memory metrics stored in /proc/{pid}/status */
- Json::object GetStatusValues();
+ virtual Json::object GetStatusValues() = 0;
/**
* @brief Get a list of various memory metrics stored in /proc/{pid}
* folder.
*/
- Json::object GetProcValues();
+ virtual Json::object GetProcValues() = 0;
/**
* @brief Get a list of memory metrics available from ActivityManager
*/
- Json::object GetActivityManagerValues();
+ virtual Json::object GetActivityManagerValues() = 0;
/**
* @brief Get a list of memory metrics available from
* ActivityManager#getMemoryInfo().
*/
- Json::object GetActivityManagerMemoryInfo();
+ virtual Json::object GetActivityManagerMemoryInfo() = 0;
/**
* @brief Get a list of memory metrics available from android.os.Debug
*/
- Json::object GetDebugValues();
- MetricsProvider();
+ virtual Json::object GetDebugValues() = 0;
+
+ virtual ~IMetricsProvider() {}
+};
+
+// Implementation that reads files from /proc/ and uses JNI calls to
+// determine memory values
+class DefaultMetricsProvider : public IMetricsProvider {
+ public:
+ Json::object GetMeminfoValues() override;
+ Json::object GetStatusValues() override;
+ Json::object GetProcValues() override;
+ Json::object GetActivityManagerValues() override;
+ Json::object GetActivityManagerMemoryInfo() override;
+ Json::object GetDebugValues() override;
private:
- std::unique_ptr<android::app::ActivityManager> activity_manager_;
android::os::DebugClass android_debug_;
/**
* @brief Reads the given file and dumps the memory values within as a map
diff --git a/games-memory-advice/core/predictor.cpp b/games-memory-advice/core/predictor.cpp
new file mode 100644
index 0000000..11cf332
--- /dev/null
+++ b/games-memory-advice/core/predictor.cpp
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "predictor.h"
+
+#include <stdlib.h>
+
+#include <algorithm>
+#include <fstream>
+#include <iterator>
+#include <map>
+#include <sstream>
+#include <streambuf>
+#include <string>
+
+#include "Log.h"
+#include "apk_utils.h"
+#include "jni/jni_wrap.h"
+#include "memory_advice/memory_advice.h"
+#include "tensorflow/lite/c/c_api.h"
+#include "tensorflow/lite/c/c_api_experimental.h"
+#include "tensorflow/lite/c/c_api_types.h"
+#include "tensorflow/lite/c/common.h"
+
+#define LOG_TAG "MemoryAdvice:DeviceProfiler"
+
+namespace memory_advice {
+
+using namespace json11;
+
+MemoryAdvice_ErrorCode DefaultPredictor::Init(std::string model_file,
+ std::string features_file) {
+ apk_utils::NativeAsset features_asset(features_file.c_str());
+
+ // Get the features list from the corresponding asset,
+ // which is a list of strings denoted with quotation marks
+ std::string features_string(
+ static_cast<const char*>(AAsset_getBuffer(features_asset)));
+
+ // remove the extra bits from the beginning and end of the files
+ // including the brackets
+ features_string =
+ features_string.substr(features_string.find_first_of('\n') + 1);
+ features_string =
+ features_string.substr(0, features_string.find_first_of(']'));
+ int pos = 0;
+
+ // Iterate over the list, searching for quotation marks to figure out
+ // where each string begins and ends. This operation ends with all the
+ // features placed inside a vector<string>
+ while ((pos = features_string.find_first_of('\n')) != std::string::npos) {
+ std::string line(features_string.substr(0, pos));
+ features.push_back(
+ line.substr(line.find_first_of("/") + 1,
+ line.find_last_of("\"") - line.find_first_of("/") - 1));
+ features_string = features_string.substr(pos + 1);
+ }
+
+ // Read the tflite model from the given asset file
+ model_asset = std::make_unique<apk_utils::NativeAsset>(model_file.c_str());
+ const char* model_buffer =
+ static_cast<const char*>(AAsset_getBuffer(*model_asset));
+ const size_t model_capacity =
+ static_cast<size_t>(AAsset_getLength(*model_asset));
+
+ // Create a tensorflow lite model using the asset file
+
+ model = TfLiteModelCreate(model_buffer, model_capacity);
+
+ options = TfLiteInterpreterOptionsCreate();
+
+ // Create a tensorflow lite interpreter from the model
+
+ interpreter = TfLiteInterpreterCreate(model, options);
+
+ // Finally, resize the input of the model; which is just the number of
+ // available features
+
+ int sizes = features.size();
+ TfLiteInterpreterResizeInputTensor(interpreter, 0, &sizes, 1);
+ TfLiteInterpreterAllocateTensors(interpreter);
+
+ return MEMORYADVICE_ERROR_OK;
+}
+
+DefaultPredictor::~DefaultPredictor() {
+ // Dispose of the model and interpreter objects.
+ TfLiteInterpreterDelete(interpreter);
+ TfLiteInterpreterOptionsDelete(options);
+ TfLiteModelDelete(model);
+}
+
+float IPredictor::GetFromPath(std::string feature, Json::object data) {
+ int pos = 0;
+ const Json::object* search = &data;
+ while ((pos = feature.find_first_of("/")) != std::string::npos) {
+ search = &(search->at(feature.substr(0, pos)).object_items());
+ feature = feature.substr(pos + 1);
+ }
+
+ Json result = search->at(feature);
+
+ if (result.is_number()) {
+ return static_cast<float>(result.number_value());
+ } else if (result.is_bool()) {
+ return result.bool_value() ? 1.0f : 0.0f;
+ } else {
+ return 0.0f;
+ }
+}
+
+float DefaultPredictor::Predict(Json::object data) {
+ float input_data[features.size()];
+
+ for (int idx = 0; idx != features.size(); idx++) {
+ input_data[idx] = GetFromPath(features[idx], data);
+ }
+ TfLiteTensor* input_tensor =
+ TfLiteInterpreterGetInputTensor(interpreter, 0);
+ TfLiteTensorCopyFromBuffer(input_tensor, input_data,
+ features.size() * sizeof(float));
+
+ TfLiteInterpreterInvoke(interpreter);
+
+ float output_data;
+
+ const TfLiteTensor* output_tensor =
+ TfLiteInterpreterGetOutputTensor(interpreter, 0);
+ TfLiteTensorCopyToBuffer(output_tensor, &output_data, 1 * sizeof(float));
+
+ return output_data;
+}
+
+} // namespace memory_advice
\ No newline at end of file
diff --git a/games-memory-advice/core/predictor.h b/games-memory-advice/core/predictor.h
new file mode 100644
index 0000000..3f8fa58
--- /dev/null
+++ b/games-memory-advice/core/predictor.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/asset_manager.h>
+#include <android/asset_manager_jni.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "apk_utils.h"
+#include "json11/json11.hpp"
+#include "memory_advice/memory_advice.h"
+#include "tensorflow/lite/c/c_api.h"
+#include "tensorflow/lite/c/c_api_experimental.h"
+#include "tensorflow/lite/c/c_api_types.h"
+#include "tensorflow/lite/c/common.h"
+
+namespace memory_advice {
+
+using namespace json11;
+
+/**
+A class to help predict memory limits using tensorflow lite models.
+*/
+class IPredictor {
+ public:
+ /**
+ * Initializes the predictor with the given model.
+ *
+ * @param model_file the location of the asset containing a predictor model
+ * file
+ * @param features_file the location of the asset containing the feature
+ * list matching the model
+ * @return MEMORYADVICE_ERROR_TFLITE_MODEL_INVALID if the provided model was
+ * invalid, or MEMORYADVICE_ERROR_OK if there are no errors.
+ */
+ virtual MemoryAdvice_ErrorCode Init(std::string model_file,
+ std::string features_file) = 0;
+
+ /**
+ * Runs the tensorflow model with the provided data.
+ *
+ * @param data the memory data from the device.
+ * @return the result from the model.
+ */
+ virtual float Predict(Json::object data) = 0;
+
+ virtual ~IPredictor() {}
+
+ protected:
+ float GetFromPath(std::string feature, Json::object data);
+};
+
+class DefaultPredictor : public IPredictor {
+ private:
+ std::vector<std::string> features;
+ TfLiteModel* model;
+ TfLiteInterpreterOptions* options;
+ TfLiteInterpreter* interpreter;
+ std::unique_ptr<apk_utils::NativeAsset> model_asset;
+
+ public:
+ MemoryAdvice_ErrorCode Init(std::string model_file,
+ std::string features_file) override;
+ float Predict(Json::object data) override;
+ ~DefaultPredictor() override;
+};
+
+} // namespace memory_advice
\ No newline at end of file
diff --git a/src/memory_advice/core/state_watcher.cpp b/games-memory-advice/core/state_watcher.cpp
similarity index 60%
rename from src/memory_advice/core/state_watcher.cpp
rename to games-memory-advice/core/state_watcher.cpp
index 836cd68..30b0300 100644
--- a/src/memory_advice/core/state_watcher.cpp
+++ b/games-memory-advice/core/state_watcher.cpp
@@ -16,21 +16,32 @@
#include "state_watcher.h"
+#include "memory_advice_impl.h"
+
namespace memory_advice {
void StateWatcher::Looper() {
- while (looping_) {
- MemoryAdvice_MemoryState state = impl_->GetMemoryState();
- if (state != MEMORYADVICE_STATE_OK) {
- callback_(state);
- }
+ thread_running_ = true;
+ while (!do_cancel_) {
std::this_thread::sleep_for(std::chrono::milliseconds(interval_));
+ if (!do_cancel_) {
+ MemoryAdvice_MemoryState state = impl_->GetMemoryState();
+ if (state != MEMORYADVICE_STATE_OK && !do_cancel_) {
+ callback_(state, user_data_);
+ }
+ }
}
+ thread_running_ = false;
}
StateWatcher::~StateWatcher() {
- looping_ = false;
- thread_->detach();
+ if (!do_cancel_) {
+ ALOGV(
+ "memory_advice::StateWatcher::Cancel not called before delete? "
+ "This can cause blocking on the main thread.");
+ do_cancel_ = true;
+ }
+ thread_->join();
}
-} // namespace memory_advice
\ No newline at end of file
+} // namespace memory_advice
diff --git a/src/memory_advice/core/state_watcher.h b/games-memory-advice/core/state_watcher.h
similarity index 71%
rename from src/memory_advice/core/state_watcher.h
rename to games-memory-advice/core/state_watcher.h
index de8b4cb..8469124 100644
--- a/src/memory_advice/core/state_watcher.h
+++ b/games-memory-advice/core/state_watcher.h
@@ -20,26 +20,35 @@
#include <memory>
#include <thread>
-#include "memory_advice_impl.h"
+#include "memory_advice/memory_advice.h"
namespace memory_advice {
+class MemoryAdviceImpl;
+
class StateWatcher {
public:
StateWatcher(MemoryAdviceImpl* impl, MemoryAdvice_WatcherCallback callback,
- uint64_t interval)
+ void* user_data, uint64_t interval)
: callback_(callback),
+ user_data_(user_data),
interval_(interval),
- looping_(true),
+ do_cancel_(false),
+ thread_running_(true),
impl_(impl),
thread_(std::make_unique<std::thread>(&StateWatcher::Looper, this)) {}
virtual ~StateWatcher();
+ void Cancel() { do_cancel_ = true; }
+ bool ThreadRunning() const { return thread_running_; }
+ const MemoryAdvice_WatcherCallback Callback() const { return callback_; }
private:
MemoryAdviceImpl* impl_;
- std::atomic<bool> looping_;
+ std::atomic<bool> do_cancel_;
+ std::atomic<bool> thread_running_;
std::unique_ptr<std::thread> thread_;
MemoryAdvice_WatcherCallback callback_;
+ void* user_data_;
uint64_t interval_;
void Looper();
};
diff --git a/games-memory-advice/include/memory_advice b/games-memory-advice/include/memory_advice
new file mode 120000
index 0000000..8687ba1
--- /dev/null
+++ b/games-memory-advice/include/memory_advice
@@ -0,0 +1 @@
+../../include/memory_advice/
\ No newline at end of file
diff --git a/games-memory-advice/src/main/AndroidManifest.xml b/games-memory-advice/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..146a71a
--- /dev/null
+++ b/games-memory-advice/src/main/AndroidManifest.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.google.android.games.memory_advice">
+
+ <uses-permission android:name="android.permission.VIBRATE" />
+</manifest>
diff --git a/games-memory-advice/test/basic.cpp b/games-memory-advice/test/basic.cpp
new file mode 100644
index 0000000..4693ea4
--- /dev/null
+++ b/games-memory-advice/test/basic.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "../core/memory_advice_impl.h"
+
+namespace memory_advice {
+
+constexpr int32_t OK_TEST = 0;
+constexpr int32_t BAD_TEST = 1;
+constexpr int64_t ONE_TERABYTE = 1LL << 40;
+
+void watcher_callback(MemoryAdvice_MemoryState state, void* userData) {
+ ALOGE("Callback should be cancelled before being called");
+}
+
+int32_t MemoryAdviceImpl::BaseTests() {
+ MemoryAdvice_MemoryState state = GetMemoryState();
+ if (state == MEMORYADVICE_STATE_UNKNOWN) {
+ ALOGE("Expected state to not be UNKNOWN");
+ return BAD_TEST;
+ }
+ int64_t memory_bytes = GetAvailableMemory();
+ if (memory_bytes < 0) {
+ ALOGE("Expected available memory to be positive");
+ return BAD_TEST;
+ }
+ if (memory_bytes > ONE_TERABYTE) {
+ ALOGE("Expected available memory to be less than 1TB");
+ return BAD_TEST;
+ }
+ float percentage_avail = GetPercentageAvailableMemory();
+ if (percentage_avail < 0 || percentage_avail > 100.0f) {
+ ALOGE("Expected percentage available memory to be between 0 and 100");
+ return BAD_TEST;
+ }
+ auto err = RegisterWatcher(1000, watcher_callback, nullptr);
+ if (err != MEMORYADVICE_ERROR_OK) {
+ ALOGE("Error registering watcher");
+ return err;
+ }
+ err = UnregisterWatcher(watcher_callback);
+ if (err != MEMORYADVICE_ERROR_OK) {
+ ALOGE("Error unregistering watcher");
+ return err;
+ }
+ return OK_TEST;
+}
+
+} // namespace memory_advice
diff --git a/src/tuningfork/CMakeLists.txt b/games-performance-tuner/CMakeLists.txt
similarity index 85%
rename from src/tuningfork/CMakeLists.txt
rename to games-performance-tuner/CMakeLists.txt
index 9df186a..26f5cd3 100644
--- a/src/tuningfork/CMakeLists.txt
+++ b/games-performance-tuner/CMakeLists.txt
@@ -1,12 +1,13 @@
cmake_minimum_required(VERSION 3.4.1)
project(tuningfork C CXX)
set(CMAKE_CXX_STANDARD 14)
+set(IgnoreOldToolchainWarning "${ANDROID_UNIFIED_HEADERS}")
option(TUNINGFORK_TEST_OPTION "Set TUNINGFORK_TEST C++ flag")
-include("../protobuf/protobuf.cmake")
-set( MODPB64_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../external/modp_b64")
-set( THIRDPARTY_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../third_party")
+include("./protobuf/protobuf.cmake")
+set( MODPB64_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../external/modp_b64")
+set( THIRDPARTY_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../third_party")
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wthread-safety" )
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D _LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS -O3 -fPIC" )
@@ -33,9 +34,9 @@
include_directories( . )
include_directories(${PROTOBUF_NANO_SRC_DIR})
-include_directories( ../../include )
-include_directories( ../../src/swappy )
-include_directories( ../common )
+include_directories( ../include )
+include_directories( ../games-frame-pacing )
+include_directories( ../src/common )
include_directories( ${MODPB64_DIR}/modp_b64)
include_directories( ${THIRDPARTY_DIR} )
include_directories( ${THIRDPARTY_DIR}/date/include )
@@ -78,11 +79,11 @@
http_backend/http_request.cpp
http_backend/json_serializer.cpp
http_backend/ultimate_uploader.cpp
- ../common/apk_utils.cpp
- ../common/jni/jni_helper.cpp
- ../common/jni/jni_wrap.cpp
- ../common/jni/jnictx.cpp
- ../common/system_utils.cpp
+ ../src/common/apk_utils.cpp
+ ../src/common/jni/jni_helper.cpp
+ ../src/common/jni/jni_wrap.cpp
+ ../src/common/jni/jnictx.cpp
+ ../src/common/system_utils.cpp
proto/protobuf_util.cpp
unity/unity_tuningfork.cpp
${THIRDPARTY_DIR}/json11/json11.cpp
diff --git a/games-performance-tuner/build.gradle b/games-performance-tuner/build.gradle
new file mode 100644
index 0000000..e9183ae
--- /dev/null
+++ b/games-performance-tuner/build.gradle
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+plugins {
+ id 'com.android.library'
+}
+
+apply from: '../prepproto.gradle'
+
+buildDir="../../out_tuningfork"
+
+android {
+ defaultConfig {
+ minSdkVersion 19
+ compileSdkVersion 31
+ targetSdkVersion 31
+ versionCode 1
+ versionName "1.1"
+
+ consumerProguardFiles "consumer-rules.pro"
+ externalNativeBuild {
+ cmake {
+ if (project.hasProperty("stl")) {
+ arguments '-DANDROID_STL='+ project.stl
+ } else {
+ arguments '-DANDROID_STL=c++_shared'
+ }
+ }
+ }
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+
+ externalNativeBuild {
+ cmake {
+ path "CMakeLists.txt"
+ }
+ }
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ buildFeatures {
+ prefabPublishing true
+ }
+ prefab {
+ tuningfork {
+ headers "include"
+ }
+
+ tuningfork_static {
+ headers "include"
+ }
+
+ unitytuningfork {
+ headers "include"
+ }
+ }
+
+ // If we don't include this line the created .aar contains the c++ std lib
+ // at <.aar_file>/jni/<abi>/libc++_shared.so. When we have multiple
+ // libraries containing libc++_shared.so the linker complains because it
+ // can't choose between them. Because we use prefab we don't need to
+ // contents of the <.aar_file>/jni/* folder so we can just exclude it here
+ // to prevent the jni folder from being created.
+ packagingOptions {
+ exclude("**.so")
+ }
+}
+
+dependencies {
+ implementation "androidx.annotation:annotation:1.3.0"
+}
+repositories {
+ mavenCentral()
+}
+
+preBuild.dependsOn prepare_proto
diff --git a/src/tuningfork/c_header_check.c b/games-performance-tuner/c_header_check.c
similarity index 100%
rename from src/tuningfork/c_header_check.c
rename to games-performance-tuner/c_header_check.c
diff --git a/src/tuningfork/core/activity_lifecycle_state.cpp b/games-performance-tuner/core/activity_lifecycle_state.cpp
similarity index 100%
rename from src/tuningfork/core/activity_lifecycle_state.cpp
rename to games-performance-tuner/core/activity_lifecycle_state.cpp
diff --git a/src/tuningfork/core/activity_lifecycle_state.h b/games-performance-tuner/core/activity_lifecycle_state.h
similarity index 100%
rename from src/tuningfork/core/activity_lifecycle_state.h
rename to games-performance-tuner/core/activity_lifecycle_state.h
diff --git a/src/tuningfork/core/annotation_map.cpp b/games-performance-tuner/core/annotation_map.cpp
similarity index 100%
rename from src/tuningfork/core/annotation_map.cpp
rename to games-performance-tuner/core/annotation_map.cpp
diff --git a/src/tuningfork/core/annotation_map.h b/games-performance-tuner/core/annotation_map.h
similarity index 100%
rename from src/tuningfork/core/annotation_map.h
rename to games-performance-tuner/core/annotation_map.h
diff --git a/src/tuningfork/core/annotation_util.cpp b/games-performance-tuner/core/annotation_util.cpp
similarity index 100%
rename from src/tuningfork/core/annotation_util.cpp
rename to games-performance-tuner/core/annotation_util.cpp
diff --git a/src/tuningfork/core/annotation_util.h b/games-performance-tuner/core/annotation_util.h
similarity index 100%
rename from src/tuningfork/core/annotation_util.h
rename to games-performance-tuner/core/annotation_util.h
diff --git a/src/tuningfork/core/async_telemetry.cpp b/games-performance-tuner/core/async_telemetry.cpp
similarity index 100%
rename from src/tuningfork/core/async_telemetry.cpp
rename to games-performance-tuner/core/async_telemetry.cpp
diff --git a/src/tuningfork/core/async_telemetry.h b/games-performance-tuner/core/async_telemetry.h
similarity index 100%
rename from src/tuningfork/core/async_telemetry.h
rename to games-performance-tuner/core/async_telemetry.h
diff --git a/src/tuningfork/core/backend.h b/games-performance-tuner/core/backend.h
similarity index 100%
rename from src/tuningfork/core/backend.h
rename to games-performance-tuner/core/backend.h
diff --git a/src/tuningfork/core/battery_metric.h b/games-performance-tuner/core/battery_metric.h
similarity index 100%
rename from src/tuningfork/core/battery_metric.h
rename to games-performance-tuner/core/battery_metric.h
diff --git a/src/tuningfork/core/battery_provider.cpp b/games-performance-tuner/core/battery_provider.cpp
similarity index 100%
rename from src/tuningfork/core/battery_provider.cpp
rename to games-performance-tuner/core/battery_provider.cpp
diff --git a/src/tuningfork/core/battery_provider.h b/games-performance-tuner/core/battery_provider.h
similarity index 100%
rename from src/tuningfork/core/battery_provider.h
rename to games-performance-tuner/core/battery_provider.h
diff --git a/src/tuningfork/core/battery_reporting_task.cpp b/games-performance-tuner/core/battery_reporting_task.cpp
similarity index 100%
rename from src/tuningfork/core/battery_reporting_task.cpp
rename to games-performance-tuner/core/battery_reporting_task.cpp
diff --git a/src/tuningfork/core/battery_reporting_task.h b/games-performance-tuner/core/battery_reporting_task.h
similarity index 100%
rename from src/tuningfork/core/battery_reporting_task.h
rename to games-performance-tuner/core/battery_reporting_task.h
diff --git a/src/tuningfork/core/chrono_time_provider.cpp b/games-performance-tuner/core/chrono_time_provider.cpp
similarity index 100%
rename from src/tuningfork/core/chrono_time_provider.cpp
rename to games-performance-tuner/core/chrono_time_provider.cpp
diff --git a/src/tuningfork/core/common.h b/games-performance-tuner/core/common.h
similarity index 100%
rename from src/tuningfork/core/common.h
rename to games-performance-tuner/core/common.h
diff --git a/src/tuningfork/core/crash_handler.cpp b/games-performance-tuner/core/crash_handler.cpp
similarity index 100%
rename from src/tuningfork/core/crash_handler.cpp
rename to games-performance-tuner/core/crash_handler.cpp
diff --git a/src/tuningfork/core/crash_handler.h b/games-performance-tuner/core/crash_handler.h
similarity index 100%
rename from src/tuningfork/core/crash_handler.h
rename to games-performance-tuner/core/crash_handler.h
diff --git a/src/tuningfork/core/file_cache.cpp b/games-performance-tuner/core/file_cache.cpp
similarity index 100%
rename from src/tuningfork/core/file_cache.cpp
rename to games-performance-tuner/core/file_cache.cpp
diff --git a/src/tuningfork/core/file_cache.h b/games-performance-tuner/core/file_cache.h
similarity index 100%
rename from src/tuningfork/core/file_cache.h
rename to games-performance-tuner/core/file_cache.h
diff --git a/src/tuningfork/core/frametime_metric.cpp b/games-performance-tuner/core/frametime_metric.cpp
similarity index 90%
rename from src/tuningfork/core/frametime_metric.cpp
rename to games-performance-tuner/core/frametime_metric.cpp
index 4352dc3..fc449e8 100644
--- a/src/tuningfork/core/frametime_metric.cpp
+++ b/games-performance-tuner/core/frametime_metric.cpp
@@ -19,8 +19,8 @@
namespace tuningfork {
-void FrameTimeMetricData::Tick(TimePoint t) {
- if (last_time_ != TimePoint::min() && t > last_time_)
+void FrameTimeMetricData::Tick(TimePoint t, bool record) {
+ if (last_time_ != TimePoint::min() && t > last_time_ && record)
Record(t - last_time_);
last_time_ = t;
}
diff --git a/src/tuningfork/core/frametime_metric.h b/games-performance-tuner/core/frametime_metric.h
similarity index 96%
rename from src/tuningfork/core/frametime_metric.h
rename to games-performance-tuner/core/frametime_metric.h
index c5671ea..de51524 100644
--- a/src/tuningfork/core/frametime_metric.h
+++ b/games-performance-tuner/core/frametime_metric.h
@@ -41,7 +41,7 @@
Histogram<double> histogram_;
TimePoint last_time_;
Duration duration_;
- void Tick(TimePoint t);
+ void Tick(TimePoint t, bool record = true);
void Record(Duration dt);
virtual void Clear() override;
virtual size_t Count() const override { return histogram_.Count(); }
diff --git a/src/tuningfork/core/histogram.cpp b/games-performance-tuner/core/histogram.cpp
similarity index 100%
rename from src/tuningfork/core/histogram.cpp
rename to games-performance-tuner/core/histogram.cpp
diff --git a/src/tuningfork/core/histogram.h b/games-performance-tuner/core/histogram.h
similarity index 100%
rename from src/tuningfork/core/histogram.h
rename to games-performance-tuner/core/histogram.h
diff --git a/src/tuningfork/core/id_provider.h b/games-performance-tuner/core/id_provider.h
similarity index 100%
rename from src/tuningfork/core/id_provider.h
rename to games-performance-tuner/core/id_provider.h
diff --git a/src/tuningfork/core/lifecycle_upload_event.h b/games-performance-tuner/core/lifecycle_upload_event.h
similarity index 100%
rename from src/tuningfork/core/lifecycle_upload_event.h
rename to games-performance-tuner/core/lifecycle_upload_event.h
diff --git a/src/tuningfork/core/loadingtime_metric.cpp b/games-performance-tuner/core/loadingtime_metric.cpp
similarity index 100%
rename from src/tuningfork/core/loadingtime_metric.cpp
rename to games-performance-tuner/core/loadingtime_metric.cpp
diff --git a/src/tuningfork/core/loadingtime_metric.h b/games-performance-tuner/core/loadingtime_metric.h
similarity index 100%
rename from src/tuningfork/core/loadingtime_metric.h
rename to games-performance-tuner/core/loadingtime_metric.h
diff --git a/src/tuningfork/core/meminfo_provider.h b/games-performance-tuner/core/meminfo_provider.h
similarity index 100%
rename from src/tuningfork/core/meminfo_provider.h
rename to games-performance-tuner/core/meminfo_provider.h
diff --git a/src/tuningfork/core/memory_metric.h b/games-performance-tuner/core/memory_metric.h
similarity index 100%
rename from src/tuningfork/core/memory_metric.h
rename to games-performance-tuner/core/memory_metric.h
diff --git a/src/tuningfork/core/memory_record_type.h b/games-performance-tuner/core/memory_record_type.h
similarity index 100%
rename from src/tuningfork/core/memory_record_type.h
rename to games-performance-tuner/core/memory_record_type.h
diff --git a/src/tuningfork/core/memory_telemetry.cpp b/games-performance-tuner/core/memory_telemetry.cpp
similarity index 98%
rename from src/tuningfork/core/memory_telemetry.cpp
rename to games-performance-tuner/core/memory_telemetry.cpp
index f0b4a70..46ea0ea 100644
--- a/src/tuningfork/core/memory_telemetry.cpp
+++ b/games-performance-tuner/core/memory_telemetry.cpp
@@ -171,7 +171,7 @@
if (enabled && !memInfo.initialized) {
memInfo.initialized = true;
- memInfo.pid = (uint32_t)getpid();
+ memInfo.pid = (uint32_t)android_process_.myPid();
}
}
diff --git a/src/tuningfork/core/memory_telemetry.h b/games-performance-tuner/core/memory_telemetry.h
similarity index 98%
rename from src/tuningfork/core/memory_telemetry.h
rename to games-performance-tuner/core/memory_telemetry.h
index aac5d20..b892214 100644
--- a/src/tuningfork/core/memory_telemetry.h
+++ b/games-performance-tuner/core/memory_telemetry.h
@@ -58,6 +58,7 @@
bool enabled_ = true;
uint64_t device_memory_bytes = 0;
gamesdk::jni::android::os::DebugClass android_debug_;
+ gamesdk::jni::android::os::Process android_process_;
protected:
MemInfo memInfo;
diff --git a/src/tuningfork/core/metric.h b/games-performance-tuner/core/metric.h
similarity index 100%
rename from src/tuningfork/core/metric.h
rename to games-performance-tuner/core/metric.h
diff --git a/src/tuningfork/core/metricdata.h b/games-performance-tuner/core/metricdata.h
similarity index 100%
rename from src/tuningfork/core/metricdata.h
rename to games-performance-tuner/core/metricdata.h
diff --git a/src/tuningfork/core/process_time.h b/games-performance-tuner/core/process_time.h
similarity index 100%
rename from src/tuningfork/core/process_time.h
rename to games-performance-tuner/core/process_time.h
diff --git a/src/tuningfork/core/protobuf_util_internal.cpp b/games-performance-tuner/core/protobuf_util_internal.cpp
similarity index 100%
rename from src/tuningfork/core/protobuf_util_internal.cpp
rename to games-performance-tuner/core/protobuf_util_internal.cpp
diff --git a/src/tuningfork/core/protobuf_util_internal.h b/games-performance-tuner/core/protobuf_util_internal.h
similarity index 100%
rename from src/tuningfork/core/protobuf_util_internal.h
rename to games-performance-tuner/core/protobuf_util_internal.h
diff --git a/src/tuningfork/core/request_info.cpp b/games-performance-tuner/core/request_info.cpp
similarity index 93%
rename from src/tuningfork/core/request_info.cpp
rename to games-performance-tuner/core/request_info.cpp
index b9968ea..ba45462 100644
--- a/src/tuningfork/core/request_info.cpp
+++ b/games-performance-tuner/core/request_info.cpp
@@ -127,6 +127,14 @@
info.soc_manufacturer =
gamesdk::jni::android::os::Build::SOC_MANUFACTURER().C();
}
+
+ gamesdk::jni::android::util::DisplayMetrics display_metrics;
+ gamesdk::jni::AppContext()
+ .getWindowManager()
+ .getDefaultDisplay()
+ .getMetrics(display_metrics);
+ info.height_pixels = display_metrics.heightPixels();
+ info.width_pixels = display_metrics.widthPixels();
}
info.tuningfork_version = TUNINGFORK_PACKED_VERSION;
info.swappy_version = settings.c_settings.swappy_version;
diff --git a/src/tuningfork/core/request_info.h b/games-performance-tuner/core/request_info.h
similarity index 96%
rename from src/tuningfork/core/request_info.h
rename to games-performance-tuner/core/request_info.h
index 17db1c0..7bc8d63 100644
--- a/src/tuningfork/core/request_info.h
+++ b/games-performance-tuner/core/request_info.h
@@ -51,6 +51,8 @@
std::string soc_manufacturer;
int64_t swap_total_bytes;
uint32_t swappy_version;
+ int32_t height_pixels;
+ int32_t width_pixels;
// Note that this will include an empty experiment_id and
// current_fidelity_parameters.
diff --git a/src/tuningfork/core/runnable.cpp b/games-performance-tuner/core/runnable.cpp
similarity index 100%
rename from src/tuningfork/core/runnable.cpp
rename to games-performance-tuner/core/runnable.cpp
diff --git a/src/tuningfork/core/runnable.h b/games-performance-tuner/core/runnable.h
similarity index 100%
rename from src/tuningfork/core/runnable.h
rename to games-performance-tuner/core/runnable.h
diff --git a/src/tuningfork/core/session.cpp b/games-performance-tuner/core/session.cpp
similarity index 100%
rename from src/tuningfork/core/session.cpp
rename to games-performance-tuner/core/session.cpp
diff --git a/src/tuningfork/core/session.h b/games-performance-tuner/core/session.h
similarity index 100%
rename from src/tuningfork/core/session.h
rename to games-performance-tuner/core/session.h
diff --git a/src/tuningfork/core/settings.h b/games-performance-tuner/core/settings.h
similarity index 100%
rename from src/tuningfork/core/settings.h
rename to games-performance-tuner/core/settings.h
diff --git a/src/tuningfork/core/thermal_metric.h b/games-performance-tuner/core/thermal_metric.h
similarity index 100%
rename from src/tuningfork/core/thermal_metric.h
rename to games-performance-tuner/core/thermal_metric.h
diff --git a/src/tuningfork/core/thermal_reporting_task.cpp b/games-performance-tuner/core/thermal_reporting_task.cpp
similarity index 100%
rename from src/tuningfork/core/thermal_reporting_task.cpp
rename to games-performance-tuner/core/thermal_reporting_task.cpp
diff --git a/src/tuningfork/core/thermal_reporting_task.h b/games-performance-tuner/core/thermal_reporting_task.h
similarity index 100%
rename from src/tuningfork/core/thermal_reporting_task.h
rename to games-performance-tuner/core/thermal_reporting_task.h
diff --git a/src/tuningfork/core/time_provider.h b/games-performance-tuner/core/time_provider.h
similarity index 100%
rename from src/tuningfork/core/time_provider.h
rename to games-performance-tuner/core/time_provider.h
diff --git a/src/tuningfork/core/time_series.h b/games-performance-tuner/core/time_series.h
similarity index 100%
rename from src/tuningfork/core/time_series.h
rename to games-performance-tuner/core/time_series.h
diff --git a/src/tuningfork/core/tuningfork.cpp b/games-performance-tuner/core/tuningfork.cpp
similarity index 92%
rename from src/tuningfork/core/tuningfork.cpp
rename to games-performance-tuner/core/tuningfork.cpp
index f536141..507a9a4 100644
--- a/src/tuningfork/core/tuningfork.cpp
+++ b/games-performance-tuner/core/tuningfork.cpp
@@ -65,6 +65,9 @@
static std::unique_ptr<TuningForkImpl> s_impl;
static std::unique_ptr<SwappyTraceWrapper> s_swappy_tracer;
+// False by default to hide sensitive information in the logs
+bool g_verbose_logging_enabled = false;
+
TuningFork_ErrorCode Init(const Settings &settings,
const RequestInfo *request_info, IBackend *backend,
ITimeProvider *time_provider,
@@ -200,6 +203,27 @@
return s_impl->EnableMemoryRecording(enable);
}
+bool IsFrameTimeLoggingPaused() {
+ if (!s_impl)
+ return TUNINGFORK_ERROR_TUNINGFORK_NOT_INITIALIZED;
+ else
+ return s_impl->IsFrameTimeLoggingPaused();
+}
+
+TuningFork_ErrorCode PauseFrameTimeLogging() {
+ if (!s_impl)
+ return TUNINGFORK_ERROR_TUNINGFORK_NOT_INITIALIZED;
+ else
+ return s_impl->PauseFrameTimeLogging();
+}
+
+TuningFork_ErrorCode ResumeFrameTimeLogging() {
+ if (!s_impl)
+ return TUNINGFORK_ERROR_TUNINGFORK_NOT_INITIALIZED;
+ else
+ return s_impl->ResumeFrameTimeLogging();
+}
+
TuningFork_ErrorCode RecordLoadingTime(
Duration duration, const LoadingTimeMetadata &d,
const ProtobufSerialization &annotation) {
diff --git a/src/tuningfork/core/tuningfork_c.cpp b/games-performance-tuner/core/tuningfork_c.cpp
similarity index 95%
rename from src/tuningfork/core/tuningfork_c.cpp
rename to games-performance-tuner/core/tuningfork_c.cpp
index 8feeecb..f1eae23 100644
--- a/src/tuningfork/core/tuningfork_c.cpp
+++ b/games-performance-tuner/core/tuningfork_c.cpp
@@ -40,6 +40,7 @@
settings.c_settings = *c_settings_in;
}
jni::Init(env, context);
+ tf::g_verbose_logging_enabled = settings.c_settings.verbose_logging_enabled;
bool first_run = tf::CheckIfFirstRun();
TuningFork_ErrorCode err = tf::Settings::FindInApk(&settings);
if (err != TUNINGFORK_ERROR_OK) return err;
@@ -125,6 +126,18 @@
return tf::EnableMemoryRecording(enable);
}
+bool TuningFork_isFrameTimeLoggingPaused() {
+ return tf::IsFrameTimeLoggingPaused();
+}
+
+TuningFork_ErrorCode TuningFork_pauseFrameTimeLogging() {
+ return tf::PauseFrameTimeLogging();
+}
+
+TuningFork_ErrorCode TuningFork_resumeFrameTimeLogging() {
+ return tf::ResumeFrameTimeLogging();
+}
+
// Take the C metadata structure passed in and copy to the C++ structure,
// taking into account any version changes indicated by changes in the size.
// Currently tf::LoadingTimeMetadata is typedefed to
diff --git a/src/tuningfork/core/tuningfork_extra.cpp b/games-performance-tuner/core/tuningfork_extra.cpp
similarity index 100%
rename from src/tuningfork/core/tuningfork_extra.cpp
rename to games-performance-tuner/core/tuningfork_extra.cpp
diff --git a/src/tuningfork/core/tuningfork_extra.h b/games-performance-tuner/core/tuningfork_extra.h
similarity index 100%
rename from src/tuningfork/core/tuningfork_extra.h
rename to games-performance-tuner/core/tuningfork_extra.h
diff --git a/src/tuningfork/core/tuningfork_impl.cpp b/games-performance-tuner/core/tuningfork_impl.cpp
similarity index 95%
rename from src/tuningfork/core/tuningfork_impl.cpp
rename to games-performance-tuner/core/tuningfork_impl.cpp
index 6dc1034..49bdf98 100644
--- a/src/tuningfork/core/tuningfork_impl.cpp
+++ b/games-performance-tuner/core/tuningfork_impl.cpp
@@ -101,8 +101,11 @@
settings.aggregation_strategy.intervalms_or_count,
settings.aggregation_strategy.max_instrumentation_keys,
settings.aggregation_strategy.annotation_enum_size.size(),
- settings.histograms.size(), settings.base_uri.c_str(),
- settings.api_key.c_str(),
+ settings.histograms.size(),
+ g_verbose_logging_enabled ? settings.base_uri.c_str()
+ : LOGGING_PLACEHOLDER_TEXT,
+ g_verbose_logging_enabled ? settings.api_key.c_str()
+ : LOGGING_PLACEHOLDER_TEXT,
settings.default_fidelity_parameters_filename.c_str(),
settings.initial_request_timeout_ms,
settings.ultimate_request_timeout_ms);
@@ -216,8 +219,8 @@
ALOGV("Set annotation id to %" PRIu32, id);
bool changed = current_annotation_id_.detail.annotation != id;
if (!changed) return current_annotation_id_;
-#if __ANDROID_API__ >= 29
- if (ATrace_isEnabled()) {
+ if (trace_->isEnabled()) {
+ std::lock_guard<std::mutex> lock(trace_marker_cache_mutex_);
// Finish the last section if there was one and start a new one.
static constexpr int32_t kATraceAsyncCookie = 0x5eaf00d;
if (trace_started_) {
@@ -225,15 +228,14 @@
if (last_annotation == trace_marker_cache_.end()) {
ALOGE("Annotation %u has vanished!", last_id_);
} else {
- ATrace_endAsyncSection(last_annotation->second.c_str(),
- kATraceAsyncCookie);
+ trace_->endAsyncSection(last_annotation->second.c_str(),
+ kATraceAsyncCookie);
trace_marker_cache_.erase(last_annotation);
}
} else {
trace_started_ = true;
}
// Guard against concurrent access to the cache
- std::lock_guard<std::mutex> lock(trace_marker_cache_mutex_);
auto it = trace_marker_cache_.find(id);
if (it == trace_marker_cache_.end()) {
it = trace_marker_cache_
@@ -243,10 +245,9 @@
annotation)})
.first;
}
- ATrace_beginAsyncSection(it->second.c_str(), kATraceAsyncCookie);
+ trace_->beginAsyncSection(it->second.c_str(), kATraceAsyncCookie);
last_id_ = id;
}
-#endif
current_annotation_id_ = MetricId::FrameTime(id, 0);
battery_reporting_task_->UpdateMetricId(MetricId::Battery(id));
thermal_reporting_task_->UpdateMetricId(MetricId::Thermal(id));
@@ -415,7 +416,8 @@
// Find the appropriate histogram and add this time
auto p = current_session_->GetData<FrameTimeMetricData>(compound_id);
if (p) {
- p->Tick(t);
+ // Continue ticking even while logging is paused but don't record values
+ p->Tick(t, !logging_paused_ /*record*/);
if (pp != nullptr) *pp = p;
return TUNINGFORK_ERROR_OK;
} else {
@@ -431,7 +433,9 @@
// Find the appropriate histogram and add this time
auto h = current_session_->GetData<FrameTimeMetricData>(compound_id);
if (h) {
- h->Record(dt);
+ if (!logging_paused_) {
+ h->Record(dt);
+ }
if (pp != nullptr) *pp = h;
return TUNINGFORK_ERROR_OK;
} else {
@@ -603,6 +607,22 @@
return TUNINGFORK_ERROR_OK;
}
+bool TuningForkImpl::IsFrameTimeLoggingPaused() { return logging_paused_; }
+
+TuningFork_ErrorCode TuningForkImpl::PauseFrameTimeLogging() {
+ if (logging_paused_) return TUNINGFORK_ERROR_FRAME_LOGGING_ALREADY_PAUSED;
+
+ logging_paused_ = true;
+ return TUNINGFORK_ERROR_OK;
+}
+
+TuningFork_ErrorCode TuningForkImpl::ResumeFrameTimeLogging() {
+ if (!logging_paused_) return TUNINGFORK_ERROR_FRAME_LOGGING_ALREADY_RUNNING;
+
+ logging_paused_ = false;
+ return TUNINGFORK_ERROR_OK;
+}
+
void TuningForkImpl::InitAsyncTelemetry() {
async_telemetry_ = std::make_unique<AsyncTelemetry>(time_provider_);
battery_reporting_task_ = std::make_shared<BatteryReportingTask>(
@@ -769,7 +789,13 @@
}
TuningFork_ErrorCode TuningForkImpl::StopLoadingGroup(LoadingHandle handle) {
- if (handle == 0) handle = current_loading_group_metric_.base;
+ if (handle == 0) {
+ // This happens when there is no active loading group e.g.,
+ // because StartLoadingGroup failed.
+ if (current_loading_group_metric_.base == 0)
+ return TUNINGFORK_ERROR_NO_ACTIVE_LOADING_GROUP;
+ handle = current_loading_group_metric_.base;
+ }
if (current_loading_group_metric_.base != handle) {
return TUNINGFORK_ERROR_BAD_PARAMETER;
}
diff --git a/src/tuningfork/core/tuningfork_impl.h b/games-performance-tuner/core/tuningfork_impl.h
similarity index 97%
rename from src/tuningfork/core/tuningfork_impl.h
rename to games-performance-tuner/core/tuningfork_impl.h
index fa57ab9..8b226f7 100644
--- a/src/tuningfork/core/tuningfork_impl.h
+++ b/games-performance-tuner/core/tuningfork_impl.h
@@ -82,18 +82,17 @@
TuningFork_ErrorCode initialization_error_code_ = TUNINGFORK_ERROR_OK;
bool lifecycle_stop_event_sent_ = false;
+ bool logging_paused_ = false;
std::string current_loading_group_;
MetricId current_loading_group_metric_;
Duration current_loading_group_start_time_ = Duration::zero();
// Caching of ATrace markers
-#if __ANDROID_API__ >= 29
bool trace_started_ = false;
std::mutex trace_marker_cache_mutex_;
std::map<AnnotationId, std::string> trace_marker_cache_;
AnnotationId last_id_;
-#endif
public:
TuningForkImpl(const Settings &settings, IBackend *backend,
@@ -145,6 +144,12 @@
TuningFork_ErrorCode EnableMemoryRecording(bool enable);
+ bool IsFrameTimeLoggingPaused();
+
+ TuningFork_ErrorCode PauseFrameTimeLogging();
+
+ TuningFork_ErrorCode ResumeFrameTimeLogging();
+
TuningFork_ErrorCode RecordLoadingTime(
Duration duration, const LoadingTimeMetadata &metadata,
const ProtobufSerialization &annotation, bool relativeToStart);
diff --git a/src/tuningfork/core/tuningfork_internal.h b/games-performance-tuner/core/tuningfork_internal.h
similarity index 91%
rename from src/tuningfork/core/tuningfork_internal.h
rename to games-performance-tuner/core/tuningfork_internal.h
index a2c4df7..193a35f 100644
--- a/src/tuningfork/core/tuningfork_internal.h
+++ b/games-performance-tuner/core/tuningfork_internal.h
@@ -32,6 +32,13 @@
namespace tuningfork {
+// Replaces sensitive information when verbose logging is disabled
+#define LOGGING_PLACEHOLDER_TEXT "HIDDEN"
+
+// If false, sensitive information is removed from logging.
+// Default is false.
+extern bool g_verbose_logging_enabled;
+
// If no request_info is passed, the info for this device and game are used.
// If no backend is passed, the default backend, which uploads to the google
// http endpoint is used. If no timeProvider is passed,
@@ -96,6 +103,15 @@
// Enable or disable memory telemetry recording.
TuningFork_ErrorCode EnableMemoryRecording(bool enable);
+// Returns true if frame time logging is paused. False otherwise.
+bool IsFrameTimeLoggingPaused();
+
+// Pause frame time logging
+TuningFork_ErrorCode PauseFrameTimeLogging();
+
+// Resume frame time logging
+TuningFork_ErrorCode ResumeFrameTimeLogging();
+
// Record a loading time event
TuningFork_ErrorCode RecordLoadingTime(Duration duration,
const LoadingTimeMetadata& d,
diff --git a/src/tuningfork/core/tuningfork_settings.cpp b/games-performance-tuner/core/tuningfork_settings.cpp
similarity index 100%
rename from src/tuningfork/core/tuningfork_settings.cpp
rename to games-performance-tuner/core/tuningfork_settings.cpp
diff --git a/src/tuningfork/core/tuningfork_swappy.cpp b/games-performance-tuner/core/tuningfork_swappy.cpp
similarity index 100%
rename from src/tuningfork/core/tuningfork_swappy.cpp
rename to games-performance-tuner/core/tuningfork_swappy.cpp
diff --git a/src/tuningfork/core/tuningfork_swappy.h b/games-performance-tuner/core/tuningfork_swappy.h
similarity index 100%
rename from src/tuningfork/core/tuningfork_swappy.h
rename to games-performance-tuner/core/tuningfork_swappy.h
diff --git a/src/tuningfork/core/tuningfork_utils.cpp b/games-performance-tuner/core/tuningfork_utils.cpp
similarity index 98%
rename from src/tuningfork/core/tuningfork_utils.cpp
rename to games-performance-tuner/core/tuningfork_utils.cpp
index a86373b..0cd04bc 100644
--- a/src/tuningfork/core/tuningfork_utils.cpp
+++ b/games-performance-tuner/core/tuningfork_utils.cpp
@@ -249,7 +249,9 @@
{"soc_model", request_info.soc_model},
{"soc_manufacturer", request_info.soc_manufacturer},
{"swap_total_bytes",
- static_cast<double>(request_info.swap_total_bytes)}};
+ static_cast<double>(request_info.swap_total_bytes)},
+ {"height_pixels", request_info.height_pixels},
+ {"width_pixels", request_info.width_pixels}};
}
} // namespace json_utils
diff --git a/src/tuningfork/core/tuningfork_utils.h b/games-performance-tuner/core/tuningfork_utils.h
similarity index 100%
rename from src/tuningfork/core/tuningfork_utils.h
rename to games-performance-tuner/core/tuningfork_utils.h
diff --git a/src/tuningfork/core/uploadthread.cpp b/games-performance-tuner/core/uploadthread.cpp
similarity index 100%
rename from src/tuningfork/core/uploadthread.cpp
rename to games-performance-tuner/core/uploadthread.cpp
diff --git a/src/tuningfork/core/uploadthread.h b/games-performance-tuner/core/uploadthread.h
similarity index 100%
rename from src/tuningfork/core/uploadthread.h
rename to games-performance-tuner/core/uploadthread.h
diff --git a/src/tuningfork/http_backend/debugInfo.cpp b/games-performance-tuner/http_backend/debugInfo.cpp
similarity index 100%
rename from src/tuningfork/http_backend/debugInfo.cpp
rename to games-performance-tuner/http_backend/debugInfo.cpp
diff --git a/src/tuningfork/http_backend/generateTuningParameters.cpp b/games-performance-tuner/http_backend/generateTuningParameters.cpp
similarity index 96%
rename from src/tuningfork/http_backend/generateTuningParameters.cpp
rename to games-performance-tuner/http_backend/generateTuningParameters.cpp
index 64ad665..fcc1ed7 100644
--- a/src/tuningfork/http_backend/generateTuningParameters.cpp
+++ b/games-performance-tuner/http_backend/generateTuningParameters.cpp
@@ -76,7 +76,9 @@
experiment_id.clear();
return TUNINGFORK_ERROR_NO_FIDELITY_PARAMS;
} else {
- ALOGI("Response to generateTuningParameters: %s", response.c_str());
+ ALOGI("Response to generateTuningParameters: %s",
+ g_verbose_logging_enabled ? response.c_str()
+ : LOGGING_PLACEHOLDER_TEXT);
}
std::string err;
Json jresponse = Json::parse(response, err);
diff --git a/src/tuningfork/http_backend/http_backend.cpp b/games-performance-tuner/http_backend/http_backend.cpp
similarity index 100%
rename from src/tuningfork/http_backend/http_backend.cpp
rename to games-performance-tuner/http_backend/http_backend.cpp
diff --git a/src/tuningfork/http_backend/http_backend.h b/games-performance-tuner/http_backend/http_backend.h
similarity index 100%
rename from src/tuningfork/http_backend/http_backend.h
rename to games-performance-tuner/http_backend/http_backend.h
diff --git a/src/tuningfork/http_backend/http_request.cpp b/games-performance-tuner/http_backend/http_request.cpp
similarity index 72%
rename from src/tuningfork/http_backend/http_request.cpp
rename to games-performance-tuner/http_backend/http_request.cpp
index 2b7f50e..26e7b3d 100644
--- a/src/tuningfork/http_backend/http_request.cpp
+++ b/games-performance-tuner/http_backend/http_request.cpp
@@ -61,18 +61,21 @@
if ((!allow_metered_) && connection_is_metered)
return TUNINGFORK_ERROR_METERED_CONNECTION_DISALLOWED;
auto uri = GetURL(rpc_name);
- ALOGI("Connecting to: %s", uri.c_str());
+ ALOGI("Connecting to: %s",
+ g_verbose_logging_enabled ? uri.c_str() : LOGGING_PLACEHOLDER_TEXT);
using namespace gamesdk::jni;
auto url = java::net::URL(uri);
- CHECK_FOR_JNI_EXCEPTION_AND_RETURN(
- TUNINGFORK_ERROR_JNI_EXCEPTION); // Malformed URL
+ SAFE_LOGGING_CHECK_FOR_JNI_EXCEPTION_AND_RETURN(
+ TUNINGFORK_ERROR_JNI_EXCEPTION,
+ g_verbose_logging_enabled); // Malformed URL
// Open connection and set properties
java::net::HttpURLConnection connection(url.openConnection());
- CHECK_FOR_JNI_EXCEPTION_AND_RETURN(
- TUNINGFORK_ERROR_JNI_EXCEPTION); // IOException
+ SAFE_LOGGING_CHECK_FOR_JNI_EXCEPTION_AND_RETURN(
+ TUNINGFORK_ERROR_JNI_EXCEPTION,
+ g_verbose_logging_enabled); // IOException
connection.setRequestMethod("POST");
auto timeout_ms =
std::chrono::duration_cast<std::chrono::milliseconds>(timeout_).count();
@@ -96,42 +99,51 @@
// Write json request body
auto os = connection.getOutputStream();
- CHECK_FOR_JNI_EXCEPTION_AND_RETURN(
- TUNINGFORK_ERROR_JNI_EXCEPTION); // IOException
+ SAFE_LOGGING_CHECK_FOR_JNI_EXCEPTION_AND_RETURN(
+ TUNINGFORK_ERROR_JNI_EXCEPTION,
+ g_verbose_logging_enabled); // IOException
auto writer =
java::io::BufferedWriter(java::io::OutputStreamWriter(os, "UTF-8"));
writer.write(request_json);
- CHECK_FOR_JNI_EXCEPTION_AND_RETURN(
- TUNINGFORK_ERROR_JNI_EXCEPTION); // IOException
+ SAFE_LOGGING_CHECK_FOR_JNI_EXCEPTION_AND_RETURN(
+ TUNINGFORK_ERROR_JNI_EXCEPTION,
+ g_verbose_logging_enabled); // IOException
writer.flush();
- CHECK_FOR_JNI_EXCEPTION_AND_RETURN(
- TUNINGFORK_ERROR_JNI_EXCEPTION); // IOException
+ SAFE_LOGGING_CHECK_FOR_JNI_EXCEPTION_AND_RETURN(
+ TUNINGFORK_ERROR_JNI_EXCEPTION,
+ g_verbose_logging_enabled); // IOException
writer.close();
- CHECK_FOR_JNI_EXCEPTION_AND_RETURN(
- TUNINGFORK_ERROR_JNI_EXCEPTION); // IOException
+ SAFE_LOGGING_CHECK_FOR_JNI_EXCEPTION_AND_RETURN(
+ TUNINGFORK_ERROR_JNI_EXCEPTION,
+ g_verbose_logging_enabled); // IOException
os.close();
- CHECK_FOR_JNI_EXCEPTION_AND_RETURN(
- TUNINGFORK_ERROR_JNI_EXCEPTION); // IOException
+ SAFE_LOGGING_CHECK_FOR_JNI_EXCEPTION_AND_RETURN(
+ TUNINGFORK_ERROR_JNI_EXCEPTION,
+ g_verbose_logging_enabled); // IOException
// Connect and get response
connection.connect();
- CHECK_FOR_JNI_EXCEPTION_AND_RETURN(
- TUNINGFORK_ERROR_JNI_EXCEPTION); // IOException
+ SAFE_LOGGING_CHECK_FOR_JNI_EXCEPTION_AND_RETURN(
+ TUNINGFORK_ERROR_JNI_EXCEPTION,
+ g_verbose_logging_enabled); // IOException
response_code = connection.getResponseCode();
ALOGI("Response code: %d", response_code);
- CHECK_FOR_JNI_EXCEPTION_AND_RETURN(
- TUNINGFORK_ERROR_JNI_EXCEPTION); // IOException
+ SAFE_LOGGING_CHECK_FOR_JNI_EXCEPTION_AND_RETURN(
+ TUNINGFORK_ERROR_JNI_EXCEPTION,
+ g_verbose_logging_enabled); // IOException
auto resp = connection.getResponseMessage();
ALOGI("Response message: %s", resp.C());
- CHECK_FOR_JNI_EXCEPTION_AND_RETURN(
- TUNINGFORK_ERROR_JNI_EXCEPTION); // IOException
+ SAFE_LOGGING_CHECK_FOR_JNI_EXCEPTION_AND_RETURN(
+ TUNINGFORK_ERROR_JNI_EXCEPTION,
+ g_verbose_logging_enabled); // IOException
// Read body from input stream
auto is = connection.getInputStream();
- CHECK_FOR_JNI_EXCEPTION_AND_RETURN(
- TUNINGFORK_ERROR_JNI_EXCEPTION); // IOException
+ SAFE_LOGGING_CHECK_FOR_JNI_EXCEPTION_AND_RETURN(
+ TUNINGFORK_ERROR_JNI_EXCEPTION,
+ g_verbose_logging_enabled); // IOException
auto reader =
java::io::BufferedReader(java::io::InputStreamReader(is, "UTF-8"));
std::stringstream body;
diff --git a/src/tuningfork/http_backend/http_request.h b/games-performance-tuner/http_backend/http_request.h
similarity index 100%
rename from src/tuningfork/http_backend/http_request.h
rename to games-performance-tuner/http_backend/http_request.h
diff --git a/src/tuningfork/http_backend/json_serializer.cpp b/games-performance-tuner/http_backend/json_serializer.cpp
similarity index 100%
rename from src/tuningfork/http_backend/json_serializer.cpp
rename to games-performance-tuner/http_backend/json_serializer.cpp
diff --git a/src/tuningfork/http_backend/json_serializer.h b/games-performance-tuner/http_backend/json_serializer.h
similarity index 100%
rename from src/tuningfork/http_backend/json_serializer.h
rename to games-performance-tuner/http_backend/json_serializer.h
diff --git a/src/tuningfork/http_backend/ultimate_uploader.cpp b/games-performance-tuner/http_backend/ultimate_uploader.cpp
similarity index 100%
rename from src/tuningfork/http_backend/ultimate_uploader.cpp
rename to games-performance-tuner/http_backend/ultimate_uploader.cpp
diff --git a/src/tuningfork/http_backend/ultimate_uploader.h b/games-performance-tuner/http_backend/ultimate_uploader.h
similarity index 100%
rename from src/tuningfork/http_backend/ultimate_uploader.h
rename to games-performance-tuner/http_backend/ultimate_uploader.h
diff --git a/games-performance-tuner/include/common/gamesdk_common.h b/games-performance-tuner/include/common/gamesdk_common.h
new file mode 120000
index 0000000..73ef675
--- /dev/null
+++ b/games-performance-tuner/include/common/gamesdk_common.h
@@ -0,0 +1 @@
+../../../include/common/gamesdk_common.h
\ No newline at end of file
diff --git a/games-performance-tuner/include/tuningfork b/games-performance-tuner/include/tuningfork
new file mode 120000
index 0000000..8f24fe9
--- /dev/null
+++ b/games-performance-tuner/include/tuningfork
@@ -0,0 +1 @@
+../../include/tuningfork
\ No newline at end of file
diff --git a/games-performance-tuner/proto/descriptor.proto b/games-performance-tuner/proto/descriptor.proto
new file mode 120000
index 0000000..e29f2fe
--- /dev/null
+++ b/games-performance-tuner/proto/descriptor.proto
@@ -0,0 +1 @@
+../../../external/protobuf/src/google/protobuf/descriptor.proto
\ No newline at end of file
diff --git a/src/tuningfork/proto/example_tuningfork.proto b/games-performance-tuner/proto/example_tuningfork.proto
similarity index 100%
rename from src/tuningfork/proto/example_tuningfork.proto
rename to games-performance-tuner/proto/example_tuningfork.proto
diff --git a/src/tuningfork/proto/performanceparameters.proto b/games-performance-tuner/proto/performanceparameters.proto
similarity index 100%
rename from src/tuningfork/proto/performanceparameters.proto
rename to games-performance-tuner/proto/performanceparameters.proto
diff --git a/src/tuningfork/proto/protobuf_nano_util.h b/games-performance-tuner/proto/protobuf_nano_util.h
similarity index 100%
rename from src/tuningfork/proto/protobuf_nano_util.h
rename to games-performance-tuner/proto/protobuf_nano_util.h
diff --git a/src/tuningfork/proto/protobuf_util.cpp b/games-performance-tuner/proto/protobuf_util.cpp
similarity index 100%
rename from src/tuningfork/proto/protobuf_util.cpp
rename to games-performance-tuner/proto/protobuf_util.cpp
diff --git a/src/tuningfork/proto/protobuf_util.h b/games-performance-tuner/proto/protobuf_util.h
similarity index 100%
rename from src/tuningfork/proto/protobuf_util.h
rename to games-performance-tuner/proto/protobuf_util.h
diff --git a/src/tuningfork/proto/tuningfork.proto b/games-performance-tuner/proto/tuningfork.proto
similarity index 100%
rename from src/tuningfork/proto/tuningfork.proto
rename to games-performance-tuner/proto/tuningfork.proto
diff --git a/src/tuningfork/proto/tuningfork_clearcut_log.proto b/games-performance-tuner/proto/tuningfork_clearcut_log.proto
similarity index 100%
rename from src/tuningfork/proto/tuningfork_clearcut_log.proto
rename to games-performance-tuner/proto/tuningfork_clearcut_log.proto
diff --git a/src/protobuf/CMakeLists.txt b/games-performance-tuner/protobuf/CMakeLists.txt
similarity index 100%
rename from src/protobuf/CMakeLists.txt
rename to games-performance-tuner/protobuf/CMakeLists.txt
diff --git a/src/protobuf/full/CMakeLists.txt b/games-performance-tuner/protobuf/full/CMakeLists.txt
similarity index 100%
rename from src/protobuf/full/CMakeLists.txt
rename to games-performance-tuner/protobuf/full/CMakeLists.txt
diff --git a/src/protobuf/lite/CMakeLists.txt b/games-performance-tuner/protobuf/lite/CMakeLists.txt
similarity index 100%
rename from src/protobuf/lite/CMakeLists.txt
rename to games-performance-tuner/protobuf/lite/CMakeLists.txt
diff --git a/src/protobuf/nano/CMakeLists.txt b/games-performance-tuner/protobuf/nano/CMakeLists.txt
similarity index 100%
rename from src/protobuf/nano/CMakeLists.txt
rename to games-performance-tuner/protobuf/nano/CMakeLists.txt
diff --git a/src/protobuf/protobuf.cmake b/games-performance-tuner/protobuf/protobuf.cmake
similarity index 100%
rename from src/protobuf/protobuf.cmake
rename to games-performance-tuner/protobuf/protobuf.cmake
diff --git a/src/protobuf/protobuf_nano_version.script b/games-performance-tuner/protobuf/protobuf_nano_version.script
similarity index 100%
rename from src/protobuf/protobuf_nano_version.script
rename to games-performance-tuner/protobuf/protobuf_nano_version.script
diff --git a/src/protobuf/protobuf_version.script b/games-performance-tuner/protobuf/protobuf_version.script
similarity index 100%
rename from src/protobuf/protobuf_version.script
rename to games-performance-tuner/protobuf/protobuf_version.script
diff --git a/games-performance-tuner/src/main/AndroidManifest.xml b/games-performance-tuner/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..fef41a5
--- /dev/null
+++ b/games-performance-tuner/src/main/AndroidManifest.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.google.android.games.tuningfork">
+
+ <uses-permission android:name="android.permission.VIBRATE" />
+</manifest>
\ No newline at end of file
diff --git a/src/tuningfork/tools/TuningForkMonitor/.gitignore b/games-performance-tuner/tools/TuningForkMonitor/.gitignore
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/.gitignore
rename to games-performance-tuner/tools/TuningForkMonitor/.gitignore
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/.gitignore b/games-performance-tuner/tools/TuningForkMonitor/app/.gitignore
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/.gitignore
rename to games-performance-tuner/tools/TuningForkMonitor/app/.gitignore
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/build.gradle b/games-performance-tuner/tools/TuningForkMonitor/app/build.gradle
similarity index 97%
rename from src/tuningfork/tools/TuningForkMonitor/app/build.gradle
rename to games-performance-tuner/tools/TuningForkMonitor/app/build.gradle
index 3fc5008..4b6090e 100644
--- a/src/tuningfork/tools/TuningForkMonitor/app/build.gradle
+++ b/games-performance-tuner/tools/TuningForkMonitor/app/build.gradle
@@ -10,10 +10,9 @@
android {
compileSdkVersion 29
- buildToolsVersion "29.0.2"
defaultConfig {
applicationId "com.google.tfmonitor"
- minSdkVersion 16
+ minSdkVersion 19
targetSdkVersion 29
versionCode 4
versionName "1.1"
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/proguard-rules.pro b/games-performance-tuner/tools/TuningForkMonitor/app/proguard-rules.pro
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/proguard-rules.pro
rename to games-performance-tuner/tools/TuningForkMonitor/app/proguard-rules.pro
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/AndroidManifest.xml b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/AndroidManifest.xml
similarity index 69%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/AndroidManifest.xml
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/AndroidManifest.xml
index 2b8a0f7..e8b7b7d 100644
--- a/src/tuningfork/tools/TuningForkMonitor/app/src/main/AndroidManifest.xml
+++ b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/AndroidManifest.xml
@@ -19,6 +19,15 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
+ <provider
+ android:name="androidx.core.content.FileProvider"
+ android:authorities="com.google.tfmonitor.fileprovider"
+ android:exported="false"
+ android:grantUriPermissions="true">
+ <meta-data
+ android:name="android.support.FILE_PROVIDER_PATHS"
+ android:resource="@xml/filepaths" />
+ </provider>
</application>
<uses-permission android:name="android.permission.INTERNET" />
</manifest>
\ No newline at end of file
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/AnnotationsFragment.kt b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/AnnotationsFragment.kt
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/AnnotationsFragment.kt
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/AnnotationsFragment.kt
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/AppChoiceFragment.kt b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/AppChoiceFragment.kt
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/AppChoiceFragment.kt
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/AppChoiceFragment.kt
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/AppDetailFragment.kt b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/AppDetailFragment.kt
similarity index 98%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/AppDetailFragment.kt
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/AppDetailFragment.kt
index 36ee3b8..1df811d 100644
--- a/src/tuningfork/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/AppDetailFragment.kt
+++ b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/AppDetailFragment.kt
@@ -132,7 +132,7 @@
modeGroup.check(R.id.scaledButton)
}
- datastore = Datastore(this.context!!)
+ datastore = Datastore(this.context!!, getString(R.string.database_name))
getViewModel().getActiveAppTelemetry()?.observe(this, Observer<AppTelemetry> { data ->
updateFromDatabase(this.view!!)
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/FidelityParametersFragment.kt b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/FidelityParametersFragment.kt
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/FidelityParametersFragment.kt
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/FidelityParametersFragment.kt
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/FieldItemFragment.kt b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/FieldItemFragment.kt
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/FieldItemFragment.kt
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/FieldItemFragment.kt
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/FieldItemRecyclerViewAdapter.kt b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/FieldItemRecyclerViewAdapter.kt
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/FieldItemRecyclerViewAdapter.kt
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/FieldItemRecyclerViewAdapter.kt
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/HistogramBarMetaData.kt b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/HistogramBarMetaData.kt
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/HistogramBarMetaData.kt
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/HistogramBarMetaData.kt
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/HistogramWidget.kt b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/HistogramWidget.kt
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/HistogramWidget.kt
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/HistogramWidget.kt
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/LogFragment.kt b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/LogFragment.kt
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/LogFragment.kt
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/LogFragment.kt
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/MainActivity.kt b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/MainActivity.kt
similarity index 67%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/MainActivity.kt
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/MainActivity.kt
index 26933e5..87623e1 100644
--- a/src/tuningfork/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/MainActivity.kt
+++ b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/MainActivity.kt
@@ -17,24 +17,28 @@
package com.google.tfmonitor
import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager
import android.net.Uri
import android.os.AsyncTask
import android.os.Bundle
-import androidx.appcompat.app.AppCompatActivity
import android.view.Menu
import android.view.MenuItem
-
-import kotlinx.android.synthetic.main.activity_main.*
-import java.io.File
+import androidx.appcompat.app.AppCompatActivity
+import androidx.core.content.FileProvider
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.NavController
import androidx.navigation.findNavController
import com.google.tuningfork.AppKey
import com.google.tuningfork.Datastore
import com.google.tuningfork.Deserializer
+import java.io.File
+import kotlinx.android.synthetic.main.activity_main.toolbar
-private class AsyncWebServer(val applicationContext: Context, val port: Int,
- val requestListener: RequestListener) :
+private class AsyncWebServer(
+ val applicationContext: Context, val port: Int,
+ val requestListener: RequestListener
+) :
AsyncTask<Unit, Unit, Unit>() {
private var idir: File? = null;
@@ -57,32 +61,37 @@
}
class MainActivity : AppCompatActivity(), AppChoiceFragment.OnFragmentInteractionListener,
- AppDetailFragment.OnFragmentInteractionListener,
- FidelityParametersFragment.OnFragmentInteractionListener,
- AnnotationsFragment.OnFragmentInteractionListener, RequestListener {
+ AppDetailFragment.OnFragmentInteractionListener,
+ FidelityParametersFragment.OnFragmentInteractionListener,
+ AnnotationsFragment.OnFragmentInteractionListener, RequestListener {
+
+ private lateinit var databaseName: String
private var navController: NavController? = null
- private lateinit var datastore : Datastore
+ private lateinit var datastore: Datastore
private fun getViewModel(): MonitorViewModel {
val factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application)
return ViewModelProvider(viewModelStore, factory).get(MonitorViewModel::class.java)
}
- override fun generateTuningParameters(appKey: AppKey, requestString: String): Pair<Int,String> {
+ override fun generateTuningParameters(
+ appKey: AppKey,
+ requestString: String
+ ): Pair<Int, String> {
val deser = Deserializer()
val req = deser.parseGenerateTuningParametersRequest(requestString)
return getViewModel().generateTuningParameters(appKey, req)
}
- override fun uploadTelemetry(appKey: AppKey, requestString: String): Pair<Int,String> {
+ override fun uploadTelemetry(appKey: AppKey, requestString: String): Pair<Int, String> {
val deser = Deserializer()
val req = deser.parseUploadTelemetryRequest(requestString)
datastore.uploadTelemetryRequest(appKey, req)
return getViewModel().uploadTelemetry(appKey, req)
}
- override fun debugInfo(appKey: AppKey, requestString: String): Pair<Int,String> {
+ override fun debugInfo(appKey: AppKey, requestString: String): Pair<Int, String> {
val deser = Deserializer()
val req = deser.parseDebugInfoRequest(requestString)
datastore.debugInfoRequest(appKey, req)
@@ -94,7 +103,8 @@
setContentView(R.layout.activity_main)
setSupportActionBar(toolbar)
- datastore = Datastore(this.applicationContext)
+ databaseName = getString(R.string.database_name)
+ datastore = Datastore(this.applicationContext, databaseName)
loadStoredApps()
@@ -115,6 +125,12 @@
return true
}
+ fun getDatabaseURI(c: Context, dbname: String): Uri {
+ val exportFile = c.getDatabasePath(dbname)
+ return FileProvider.getUriForFile(c, "com.google.tfmonitor.fileprovider", exportFile)
+
+ }
+
override fun onOptionsItemSelected(item: MenuItem): Boolean {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
@@ -129,7 +145,10 @@
}
true
}
- R.id.action_settings -> true
+ R.id.action_export -> {
+ exportDatabase()
+ true
+ }
else -> super.onOptionsItemSelected(item)
}
}
@@ -145,4 +164,23 @@
}
}
+ fun exportDatabase() {
+ val intent = Intent(Intent.ACTION_SEND)
+ intent.type = "application/octet-stream"
+ val uri: Uri = getDatabaseURI(this, databaseName)
+ intent.putExtra(Intent.EXTRA_STREAM, uri)
+ val chooser = Intent.createChooser(intent, "Export to:")
+ // Without these permissions, there are errors in the log, although the export chooser
+ // still seems to work.
+ val uriPermissions =
+ Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+ intent.addFlags(uriPermissions)
+ val resInfoList =
+ packageManager.queryIntentActivities(chooser, PackageManager.MATCH_DEFAULT_ONLY)
+ for (resolveInfo in resInfoList) {
+ val packageName = resolveInfo.activityInfo.packageName
+ grantUriPermission(packageName, uri, uriPermissions)
+ }
+ startActivity(chooser)
+ }
}
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/MonitorViewModel.kt b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/MonitorViewModel.kt
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/MonitorViewModel.kt
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/MonitorViewModel.kt
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/RequestListener.kt b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/RequestListener.kt
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/RequestListener.kt
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/RequestListener.kt
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/SessionChoiceFragment.kt b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/SessionChoiceFragment.kt
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/SessionChoiceFragment.kt
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/SessionChoiceFragment.kt
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/TuningForkRequestServer.kt b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/TuningForkRequestServer.kt
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/TuningForkRequestServer.kt
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/java/com/google/tfmonitor/TuningForkRequestServer.kt
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/java/com/google/tuningfork/AppData.kt b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/java/com/google/tuningfork/AppData.kt
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/java/com/google/tuningfork/AppData.kt
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/java/com/google/tuningfork/AppData.kt
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/java/com/google/tuningfork/AppKey.kt b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/java/com/google/tuningfork/AppKey.kt
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/java/com/google/tuningfork/AppKey.kt
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/java/com/google/tuningfork/AppKey.kt
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/java/com/google/tuningfork/Datastore.kt b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/java/com/google/tuningfork/Datastore.kt
similarity index 84%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/java/com/google/tuningfork/Datastore.kt
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/java/com/google/tuningfork/Datastore.kt
index 37f5a30..8c84673 100644
--- a/src/tuningfork/tools/TuningForkMonitor/app/src/main/java/com/google/tuningfork/Datastore.kt
+++ b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/java/com/google/tuningfork/Datastore.kt
@@ -16,19 +16,18 @@
package com.google.tuningfork
+import android.content.ContentValues
import android.content.Context
+import android.database.Cursor
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper
-import android.content.ContentValues
-import android.database.Cursor
+import java.util.ArrayList
+import java.util.Date
import org.json.JSONArray
-import java.util.*
-
class Datastore : SQLiteOpenHelper {
companion object {
val DATABASE_VERSION = 7
- val DATABASE_NAME = "tuningfork_monitor"
val HIST_TABLE_NAME = "histograms"
val HIST_COLUMN_ID = "_id"
@@ -55,37 +54,37 @@
}
- constructor(context: Context) : super(context, DATABASE_NAME, null, DATABASE_VERSION) {
+ constructor(context: Context, dbname: String) : super(context, dbname, null, DATABASE_VERSION) {
}
override fun onCreate(db: SQLiteDatabase?) {
db?.execSQL(
"CREATE TABLE " + HIST_TABLE_NAME + " (" +
- HIST_COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
- HIST_COLUMN_TIME + " INTEGER, " +
- HIST_COLUMN_INSTRUMENT_KEY + " INTEGER, " +
- HIST_COLUMN_ANNOTATION + " BLOB," +
- HIST_COLUMN_FIDELITY_PARAMS + " BLOB," +
- HIST_COLUMN_COUNTS + " TEXT"
- + ")"
+ HIST_COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
+ HIST_COLUMN_TIME + " INTEGER, " +
+ HIST_COLUMN_INSTRUMENT_KEY + " INTEGER, " +
+ HIST_COLUMN_ANNOTATION + " BLOB," +
+ HIST_COLUMN_FIDELITY_PARAMS + " BLOB," +
+ HIST_COLUMN_COUNTS + " TEXT"
+ + ")"
)
db?.execSQL(
"CREATE TABLE " + APP_TABLE_NAME + " (" +
- APP_COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
- APP_COLUMN_TIME + " INTEGER, " +
- APP_COLUMN_NAME + " TEXT, " +
- APP_COLUMN_VERSION + " INTEGER," +
- APP_COLUMN_DESCRIPTOR + " BLOB," +
- APP_COLUMN_SETTINGS + " BLOB"
- + ")"
+ APP_COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
+ APP_COLUMN_TIME + " INTEGER, " +
+ APP_COLUMN_NAME + " TEXT, " +
+ APP_COLUMN_VERSION + " INTEGER," +
+ APP_COLUMN_DESCRIPTOR + " BLOB," +
+ APP_COLUMN_SETTINGS + " BLOB"
+ + ")"
)
db?.execSQL(
"CREATE INDEX " + HIST_INDEX_TIME + " ON " + HIST_TABLE_NAME + " ("
- + HIST_COLUMN_TIME + ")"
+ + HIST_COLUMN_TIME + ")"
)
db?.execSQL(
"CREATE INDEX " + APP_INDEX_TIME + " ON " + APP_TABLE_NAME + " ("
- + APP_COLUMN_TIME + ")"
+ + APP_COLUMN_TIME + ")"
)
}
@@ -153,7 +152,7 @@
}
private fun <T> makeArrayRequest(request: String, processItem: (cursor: Cursor) -> T)
- : ArrayList<T> {
+ : ArrayList<T> {
var result = ArrayList<T>()
makeRequest(request, { cursor ->
result.add(processItem(cursor))
@@ -163,7 +162,7 @@
}
private fun <T> makeSingleItemRequest(request: String, processItem: (cursor: Cursor) -> T)
- : T? {
+ : T? {
var ret: T? = null
makeRequest(request, { cursor ->
ret = processItem(cursor)
@@ -184,8 +183,8 @@
fun getLatestSettingsForApp(app: AppKey): Tuningfork.Settings? {
val request = "SELECT " + APP_COLUMN_SETTINGS + " FROM " + APP_TABLE_NAME +
- " WHERE " + APP_COLUMN_NAME + "='" + app.name + "' AND " + APP_COLUMN_VERSION +
- "=" + app.version.toString() + " ORDER BY " + APP_COLUMN_TIME + " DESC LIMIT 1"
+ " WHERE " + APP_COLUMN_NAME + "='" + app.name + "' AND " + APP_COLUMN_VERSION +
+ "=" + app.version.toString() + " ORDER BY " + APP_COLUMN_TIME + " DESC LIMIT 1"
return makeSingleItemRequest(request, { cursor ->
Tuningfork.Settings.parseFrom(
cursor.getBlob(
@@ -200,7 +199,7 @@
fun getLatestHistogramForInstrumentKey(instrumentKey: Int): Pair<Date, RenderTimeHistogram>? {
val request =
"SELECT * FROM " + HIST_TABLE_NAME + " WHERE " + HIST_COLUMN_INSTRUMENT_KEY + "=" +
- instrumentKey.toString() + " ORDER BY " + HIST_COLUMN_TIME + " DESC LIMIT 1"
+ instrumentKey.toString() + " ORDER BY " + HIST_COLUMN_TIME + " DESC LIMIT 1"
return makeSingleItemRequest(request, { cursor ->
val arr_json =
JSONArray(cursor.getString(cursor.getColumnIndex(HIST_COLUMN_COUNTS)))
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/java/com/google/tuningfork/Deserializer.kt b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/java/com/google/tuningfork/Deserializer.kt
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/java/com/google/tuningfork/Deserializer.kt
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/java/com/google/tuningfork/Deserializer.kt
diff --git a/games-performance-tuner/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/any.proto b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/any.proto
new file mode 120000
index 0000000..6bfbaa0
--- /dev/null
+++ b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/any.proto
@@ -0,0 +1 @@
+../../../../../../../../../third_party/protobuf-3.0.0/src/google/protobuf/any.proto
\ No newline at end of file
diff --git a/games-performance-tuner/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/any_test.proto b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/any_test.proto
new file mode 120000
index 0000000..fd5e210
--- /dev/null
+++ b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/any_test.proto
@@ -0,0 +1 @@
+../../../../../../../../../third_party/protobuf-3.0.0/src/google/protobuf/any_test.proto
\ No newline at end of file
diff --git a/games-performance-tuner/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/api.proto b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/api.proto
new file mode 120000
index 0000000..9a36e27
--- /dev/null
+++ b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/api.proto
@@ -0,0 +1 @@
+../../../../../../../../../third_party/protobuf-3.0.0/src/google/protobuf/api.proto
\ No newline at end of file
diff --git a/games-performance-tuner/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/descriptor.proto b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/descriptor.proto
new file mode 120000
index 0000000..88c75b5
--- /dev/null
+++ b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/descriptor.proto
@@ -0,0 +1 @@
+../../../../../../../../../third_party/protobuf-3.0.0/src/google/protobuf/descriptor.proto
\ No newline at end of file
diff --git a/games-performance-tuner/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/duration.proto b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/duration.proto
new file mode 120000
index 0000000..45f0e06
--- /dev/null
+++ b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/duration.proto
@@ -0,0 +1 @@
+../../../../../../../../../third_party/protobuf-3.0.0/src/google/protobuf/duration.proto
\ No newline at end of file
diff --git a/games-performance-tuner/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/empty.proto b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/empty.proto
new file mode 120000
index 0000000..2bc2865
--- /dev/null
+++ b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/empty.proto
@@ -0,0 +1 @@
+../../../../../../../../../third_party/protobuf-3.0.0/src/google/protobuf/empty.proto
\ No newline at end of file
diff --git a/games-performance-tuner/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/field_mask.proto b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/field_mask.proto
new file mode 120000
index 0000000..50278d9
--- /dev/null
+++ b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/field_mask.proto
@@ -0,0 +1 @@
+../../../../../../../../../third_party/protobuf-3.0.0/src/google/protobuf/field_mask.proto
\ No newline at end of file
diff --git a/games-performance-tuner/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/source_context.proto b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/source_context.proto
new file mode 120000
index 0000000..fa9c9c8
--- /dev/null
+++ b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/source_context.proto
@@ -0,0 +1 @@
+../../../../../../../../../third_party/protobuf-3.0.0/src/google/protobuf/source_context.proto
\ No newline at end of file
diff --git a/games-performance-tuner/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/struct.proto b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/struct.proto
new file mode 120000
index 0000000..59ff895c
--- /dev/null
+++ b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/struct.proto
@@ -0,0 +1 @@
+../../../../../../../../../third_party/protobuf-3.0.0/src/google/protobuf/struct.proto
\ No newline at end of file
diff --git a/games-performance-tuner/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/timestamp.proto b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/timestamp.proto
new file mode 120000
index 0000000..7639f82
--- /dev/null
+++ b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/timestamp.proto
@@ -0,0 +1 @@
+../../../../../../../../../third_party/protobuf-3.0.0/src/google/protobuf/timestamp.proto
\ No newline at end of file
diff --git a/games-performance-tuner/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/type.proto b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/type.proto
new file mode 120000
index 0000000..6455f4e
--- /dev/null
+++ b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/type.proto
@@ -0,0 +1 @@
+../../../../../../../../../third_party/protobuf-3.0.0/src/google/protobuf/type.proto
\ No newline at end of file
diff --git a/games-performance-tuner/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/wrappers.proto b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/wrappers.proto
new file mode 120000
index 0000000..1c66bfd
--- /dev/null
+++ b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/wrappers.proto
@@ -0,0 +1 @@
+../../../../../../../../../third_party/protobuf-3.0.0/src/google/protobuf/wrappers.proto
\ No newline at end of file
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/proto/performanceparameters.proto b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/proto/performanceparameters.proto
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/proto/performanceparameters.proto
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/proto/performanceparameters.proto
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/proto/tuningfork.proto b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/proto/tuningfork.proto
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/proto/tuningfork.proto
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/proto/tuningfork.proto
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/res/drawable-anydpi/ic_action_show_log.xml b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/drawable-anydpi/ic_action_show_log.xml
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/res/drawable-anydpi/ic_action_show_log.xml
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/drawable-anydpi/ic_action_show_log.xml
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/res/drawable-hdpi/ic_action_show_log.png b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/drawable-hdpi/ic_action_show_log.png
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/res/drawable-hdpi/ic_action_show_log.png
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/drawable-hdpi/ic_action_show_log.png
Binary files differ
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/res/drawable-mdpi/ic_action_show_log.png b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/drawable-mdpi/ic_action_show_log.png
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/res/drawable-mdpi/ic_action_show_log.png
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/drawable-mdpi/ic_action_show_log.png
Binary files differ
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/res/drawable-xhdpi/ic_action_show_log.png b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/drawable-xhdpi/ic_action_show_log.png
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/res/drawable-xhdpi/ic_action_show_log.png
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/drawable-xhdpi/ic_action_show_log.png
Binary files differ
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/res/drawable-xxhdpi/ic_action_show_log.png b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/drawable-xxhdpi/ic_action_show_log.png
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/res/drawable-xxhdpi/ic_action_show_log.png
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/drawable-xxhdpi/ic_action_show_log.png
Binary files differ
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/res/drawable/ic_launcher_background.xml b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/drawable/ic_launcher_background.xml
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/res/drawable/ic_launcher_background.xml
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/drawable/ic_launcher_background.xml
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/res/layout/activity_main.xml b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/layout/activity_main.xml
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/res/layout/activity_main.xml
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/layout/activity_main.xml
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/res/layout/content_main.xml b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/layout/content_main.xml
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/res/layout/content_main.xml
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/layout/content_main.xml
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/res/layout/fp_row_item.xml b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/layout/fp_row_item.xml
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/res/layout/fp_row_item.xml
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/layout/fp_row_item.xml
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/res/layout/fragment_annotations.xml b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/layout/fragment_annotations.xml
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/res/layout/fragment_annotations.xml
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/layout/fragment_annotations.xml
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/res/layout/fragment_app_choice.xml b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/layout/fragment_app_choice.xml
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/res/layout/fragment_app_choice.xml
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/layout/fragment_app_choice.xml
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/res/layout/fragment_app_detail.xml b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/layout/fragment_app_detail.xml
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/res/layout/fragment_app_detail.xml
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/layout/fragment_app_detail.xml
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/res/layout/fragment_fidelity_parameters.xml b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/layout/fragment_fidelity_parameters.xml
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/res/layout/fragment_fidelity_parameters.xml
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/layout/fragment_fidelity_parameters.xml
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/res/layout/fragment_item.xml b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/layout/fragment_item.xml
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/res/layout/fragment_item.xml
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/layout/fragment_item.xml
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/res/layout/fragment_item_list.xml b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/layout/fragment_item_list.xml
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/res/layout/fragment_item_list.xml
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/layout/fragment_item_list.xml
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/res/layout/fragment_log.xml b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/layout/fragment_log.xml
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/res/layout/fragment_log.xml
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/layout/fragment_log.xml
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/res/layout/fragment_session_choice.xml b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/layout/fragment_session_choice.xml
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/res/layout/fragment_session_choice.xml
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/layout/fragment_session_choice.xml
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/res/layout/heading_row_item.xml b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/layout/heading_row_item.xml
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/res/layout/heading_row_item.xml
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/layout/heading_row_item.xml
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/res/layout/text_row_item.xml b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/layout/text_row_item.xml
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/res/layout/text_row_item.xml
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/layout/text_row_item.xml
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/res/menu/menu_main.xml b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/menu/menu_main.xml
similarity index 84%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/res/menu/menu_main.xml
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/menu/menu_main.xml
index 5e3579a..12ecd86 100644
--- a/src/tuningfork/tools/TuningForkMonitor/app/src/main/res/menu/menu_main.xml
+++ b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/menu/menu_main.xml
@@ -8,8 +8,8 @@
android:title="@string/action_view_log"
app:showAsAction="ifRoom"/>
<item
- android:id="@+id/action_settings"
+ android:id="@+id/action_export"
android:orderInCategory="100"
- android:title="@string/action_settings"
+ android:title="@string/action_export"
app:showAsAction="never" />
</menu>
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/res/mipmap-hdpi/ic_launcher.png b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/mipmap-hdpi/ic_launcher.png
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/res/mipmap-hdpi/ic_launcher.png
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
Binary files differ
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/res/mipmap-mdpi/ic_launcher.png b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/mipmap-mdpi/ic_launcher.png
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/res/mipmap-mdpi/ic_launcher.png
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
Binary files differ
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/mipmap-xhdpi/ic_launcher.png
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/res/mipmap-xhdpi/ic_launcher.png
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
Binary files differ
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
Binary files differ
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
Binary files differ
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/res/navigation/nav_graph.xml b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/navigation/nav_graph.xml
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/res/navigation/nav_graph.xml
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/navigation/nav_graph.xml
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/res/values/attrs.xml b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/values/attrs.xml
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/res/values/attrs.xml
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/values/attrs.xml
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/res/values/colors.xml b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/values/colors.xml
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/res/values/colors.xml
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/values/colors.xml
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/res/values/dimens.xml b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/values/dimens.xml
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/res/values/dimens.xml
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/values/dimens.xml
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/res/values/strings.xml b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/values/strings.xml
similarity index 77%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/res/values/strings.xml
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/values/strings.xml
index 9130670..511080e 100644
--- a/src/tuningfork/tools/TuningForkMonitor/app/src/main/res/values/strings.xml
+++ b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/values/strings.xml
@@ -1,8 +1,10 @@
<resources>
<string name="app_name">Tuning Fork Monitor</string>
<string name="action_settings">Settings</string>
+ <string name="action_export">Export</string>
<string name="initial_log_message">Log messages will appear here</string>
<string name="action_view_log">Log</string>
+ <string name="database_name">tuningfork_monitor</string>
<!-- TODO: Remove or change this placeholder text -->
<string name="hello_blank_fragment">Hello blank fragment</string>
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/res/values/styles.xml b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/values/styles.xml
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/res/values/styles.xml
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/values/styles.xml
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/res/values/template_dimens.xml b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/values/template_dimens.xml
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/app/src/main/res/values/template_dimens.xml
rename to games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/values/template_dimens.xml
diff --git a/games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/xml/filepaths.xml b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/xml/filepaths.xml
new file mode 100644
index 0000000..1240e7e
--- /dev/null
+++ b/games-performance-tuner/tools/TuningForkMonitor/app/src/main/res/xml/filepaths.xml
@@ -0,0 +1,5 @@
+<paths>
+ <files-path
+ path="../databases/"
+ name="@string/database_name"/>
+</paths>
diff --git a/src/tuningfork/tools/TuningForkMonitor/build.gradle b/games-performance-tuner/tools/TuningForkMonitor/build.gradle
similarity index 93%
rename from src/tuningfork/tools/TuningForkMonitor/build.gradle
rename to games-performance-tuner/tools/TuningForkMonitor/build.gradle
index 8d8d8a4..3614314 100644
--- a/src/tuningfork/tools/TuningForkMonitor/build.gradle
+++ b/games-performance-tuner/tools/TuningForkMonitor/build.gradle
@@ -7,7 +7,7 @@
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.5.2'
+ classpath 'com.android.tools.build:gradle:4.2.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.8'
diff --git a/src/tuningfork/tools/TuningForkMonitor/gradle.properties b/games-performance-tuner/tools/TuningForkMonitor/gradle.properties
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/gradle.properties
rename to games-performance-tuner/tools/TuningForkMonitor/gradle.properties
diff --git a/src/tuningfork/tools/TuningForkMonitor/gradle/wrapper/gradle-wrapper.jar b/games-performance-tuner/tools/TuningForkMonitor/gradle/wrapper/gradle-wrapper.jar
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/gradle/wrapper/gradle-wrapper.jar
rename to games-performance-tuner/tools/TuningForkMonitor/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/src/tuningfork/tools/TuningForkMonitor/gradle/wrapper/gradle-wrapper.properties b/games-performance-tuner/tools/TuningForkMonitor/gradle/wrapper/gradle-wrapper.properties
similarity index 92%
rename from src/tuningfork/tools/TuningForkMonitor/gradle/wrapper/gradle-wrapper.properties
rename to games-performance-tuner/tools/TuningForkMonitor/gradle/wrapper/gradle-wrapper.properties
index b360a52..2ae7f32 100644
--- a/src/tuningfork/tools/TuningForkMonitor/gradle/wrapper/gradle-wrapper.properties
+++ b/games-performance-tuner/tools/TuningForkMonitor/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
diff --git a/src/tuningfork/tools/TuningForkMonitor/gradlew b/games-performance-tuner/tools/TuningForkMonitor/gradlew
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/gradlew
rename to games-performance-tuner/tools/TuningForkMonitor/gradlew
diff --git a/src/tuningfork/tools/TuningForkMonitor/gradlew.bat b/games-performance-tuner/tools/TuningForkMonitor/gradlew.bat
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/gradlew.bat
rename to games-performance-tuner/tools/TuningForkMonitor/gradlew.bat
diff --git a/src/tuningfork/tools/TuningForkMonitor/settings.gradle b/games-performance-tuner/tools/TuningForkMonitor/settings.gradle
similarity index 100%
rename from src/tuningfork/tools/TuningForkMonitor/settings.gradle
rename to games-performance-tuner/tools/TuningForkMonitor/settings.gradle
diff --git a/src/tuningfork/tools/plugin/README.md b/games-performance-tuner/tools/plugin/README.md
similarity index 100%
rename from src/tuningfork/tools/plugin/README.md
rename to games-performance-tuner/tools/plugin/README.md
diff --git a/src/tuningfork/tools/plugin/build.gradle b/games-performance-tuner/tools/plugin/build.gradle
similarity index 100%
rename from src/tuningfork/tools/plugin/build.gradle
rename to games-performance-tuner/tools/plugin/build.gradle
diff --git a/src/tuningfork/tools/plugin/gradle/wrapper/gradle-wrapper.jar b/games-performance-tuner/tools/plugin/gradle/wrapper/gradle-wrapper.jar
similarity index 100%
rename from src/tuningfork/tools/plugin/gradle/wrapper/gradle-wrapper.jar
rename to games-performance-tuner/tools/plugin/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/src/tuningfork/tools/plugin/gradle/wrapper/gradle-wrapper.properties b/games-performance-tuner/tools/plugin/gradle/wrapper/gradle-wrapper.properties
similarity index 100%
rename from src/tuningfork/tools/plugin/gradle/wrapper/gradle-wrapper.properties
rename to games-performance-tuner/tools/plugin/gradle/wrapper/gradle-wrapper.properties
diff --git a/src/tuningfork/tools/plugin/gradlew b/games-performance-tuner/tools/plugin/gradlew
similarity index 100%
rename from src/tuningfork/tools/plugin/gradlew
rename to games-performance-tuner/tools/plugin/gradlew
diff --git a/src/tuningfork/tools/plugin/gradlew.bat b/games-performance-tuner/tools/plugin/gradlew.bat
similarity index 100%
rename from src/tuningfork/tools/plugin/gradlew.bat
rename to games-performance-tuner/tools/plugin/gradlew.bat
diff --git a/src/tuningfork/tools/plugin/settings.gradle b/games-performance-tuner/tools/plugin/settings.gradle
similarity index 100%
rename from src/tuningfork/tools/plugin/settings.gradle
rename to games-performance-tuner/tools/plugin/settings.gradle
diff --git a/src/tuningfork/tools/plugin/src/main/java/Action/GenerateAnnotationAction.java b/games-performance-tuner/tools/plugin/src/main/java/Action/GenerateAnnotationAction.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/Action/GenerateAnnotationAction.java
rename to games-performance-tuner/tools/plugin/src/main/java/Action/GenerateAnnotationAction.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/Action/OpenPluginAction.java b/games-performance-tuner/tools/plugin/src/main/java/Action/OpenPluginAction.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/Action/OpenPluginAction.java
rename to games-performance-tuner/tools/plugin/src/main/java/Action/OpenPluginAction.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/Controller/Annotation/AnnotationTabController.java b/games-performance-tuner/tools/plugin/src/main/java/Controller/Annotation/AnnotationTabController.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/Controller/Annotation/AnnotationTabController.java
rename to games-performance-tuner/tools/plugin/src/main/java/Controller/Annotation/AnnotationTabController.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/Controller/Annotation/AnnotationTableModel.java b/games-performance-tuner/tools/plugin/src/main/java/Controller/Annotation/AnnotationTableModel.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/Controller/Annotation/AnnotationTableModel.java
rename to games-performance-tuner/tools/plugin/src/main/java/Controller/Annotation/AnnotationTableModel.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/Controller/DebugInfo/DebugInfoController.java b/games-performance-tuner/tools/plugin/src/main/java/Controller/DebugInfo/DebugInfoController.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/Controller/DebugInfo/DebugInfoController.java
rename to games-performance-tuner/tools/plugin/src/main/java/Controller/DebugInfo/DebugInfoController.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/Controller/Enum/EnumController.java b/games-performance-tuner/tools/plugin/src/main/java/Controller/Enum/EnumController.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/Controller/Enum/EnumController.java
rename to games-performance-tuner/tools/plugin/src/main/java/Controller/Enum/EnumController.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/Controller/Fidelity/FidelityTabController.java b/games-performance-tuner/tools/plugin/src/main/java/Controller/Fidelity/FidelityTabController.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/Controller/Fidelity/FidelityTabController.java
rename to games-performance-tuner/tools/plugin/src/main/java/Controller/Fidelity/FidelityTabController.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/Controller/Fidelity/FidelityTableModel.java b/games-performance-tuner/tools/plugin/src/main/java/Controller/Fidelity/FidelityTableModel.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/Controller/Fidelity/FidelityTableModel.java
rename to games-performance-tuner/tools/plugin/src/main/java/Controller/Fidelity/FidelityTableModel.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/Controller/FidelityChanger/FidelityChangerController.java b/games-performance-tuner/tools/plugin/src/main/java/Controller/FidelityChanger/FidelityChangerController.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/Controller/FidelityChanger/FidelityChangerController.java
rename to games-performance-tuner/tools/plugin/src/main/java/Controller/FidelityChanger/FidelityChangerController.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/Controller/InstrumentationSettings/InstrumentationSettingsTabController.java b/games-performance-tuner/tools/plugin/src/main/java/Controller/InstrumentationSettings/InstrumentationSettingsTabController.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/Controller/InstrumentationSettings/InstrumentationSettingsTabController.java
rename to games-performance-tuner/tools/plugin/src/main/java/Controller/InstrumentationSettings/InstrumentationSettingsTabController.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/Controller/InstrumentationSettings/InstrumentationSettingsTableModel.java b/games-performance-tuner/tools/plugin/src/main/java/Controller/InstrumentationSettings/InstrumentationSettingsTableModel.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/Controller/InstrumentationSettings/InstrumentationSettingsTableModel.java
rename to games-performance-tuner/tools/plugin/src/main/java/Controller/InstrumentationSettings/InstrumentationSettingsTableModel.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/Controller/Monitoring/HistogramTree.java b/games-performance-tuner/tools/plugin/src/main/java/Controller/Monitoring/HistogramTree.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/Controller/Monitoring/HistogramTree.java
rename to games-performance-tuner/tools/plugin/src/main/java/Controller/Monitoring/HistogramTree.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/Controller/Monitoring/InvalidMonitoringDataException.java b/games-performance-tuner/tools/plugin/src/main/java/Controller/Monitoring/InvalidMonitoringDataException.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/Controller/Monitoring/InvalidMonitoringDataException.java
rename to games-performance-tuner/tools/plugin/src/main/java/Controller/Monitoring/InvalidMonitoringDataException.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/Controller/Monitoring/MonitoringController.java b/games-performance-tuner/tools/plugin/src/main/java/Controller/Monitoring/MonitoringController.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/Controller/Monitoring/MonitoringController.java
rename to games-performance-tuner/tools/plugin/src/main/java/Controller/Monitoring/MonitoringController.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/Controller/Quality/QualityTabController.java b/games-performance-tuner/tools/plugin/src/main/java/Controller/Quality/QualityTabController.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/Controller/Quality/QualityTabController.java
rename to games-performance-tuner/tools/plugin/src/main/java/Controller/Quality/QualityTabController.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/Controller/Quality/QualityTableModel.java b/games-performance-tuner/tools/plugin/src/main/java/Controller/Quality/QualityTableModel.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/Controller/Quality/QualityTableModel.java
rename to games-performance-tuner/tools/plugin/src/main/java/Controller/Quality/QualityTableModel.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/Files/AssetsParser.java b/games-performance-tuner/tools/plugin/src/main/java/Files/AssetsParser.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/Files/AssetsParser.java
rename to games-performance-tuner/tools/plugin/src/main/java/Files/AssetsParser.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/Files/FolderConfig.java b/games-performance-tuner/tools/plugin/src/main/java/Files/FolderConfig.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/Files/FolderConfig.java
rename to games-performance-tuner/tools/plugin/src/main/java/Files/FolderConfig.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/Model/EnumDataModel.java b/games-performance-tuner/tools/plugin/src/main/java/Model/EnumDataModel.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/Model/EnumDataModel.java
rename to games-performance-tuner/tools/plugin/src/main/java/Model/EnumDataModel.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/Model/MessageDataModel.java b/games-performance-tuner/tools/plugin/src/main/java/Model/MessageDataModel.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/Model/MessageDataModel.java
rename to games-performance-tuner/tools/plugin/src/main/java/Model/MessageDataModel.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/Model/MonitorFilterModel.java b/games-performance-tuner/tools/plugin/src/main/java/Model/MonitorFilterModel.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/Model/MonitorFilterModel.java
rename to games-performance-tuner/tools/plugin/src/main/java/Model/MonitorFilterModel.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/Model/QualityDataModel.java b/games-performance-tuner/tools/plugin/src/main/java/Model/QualityDataModel.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/Model/QualityDataModel.java
rename to games-performance-tuner/tools/plugin/src/main/java/Model/QualityDataModel.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/Utils/Assets/AssetsFinder.java b/games-performance-tuner/tools/plugin/src/main/java/Utils/Assets/AssetsFinder.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/Utils/Assets/AssetsFinder.java
rename to games-performance-tuner/tools/plugin/src/main/java/Utils/Assets/AssetsFinder.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/Utils/Assets/AssetsParser.java b/games-performance-tuner/tools/plugin/src/main/java/Utils/Assets/AssetsParser.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/Utils/Assets/AssetsParser.java
rename to games-performance-tuner/tools/plugin/src/main/java/Utils/Assets/AssetsParser.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/Utils/Assets/AssetsWriter.java b/games-performance-tuner/tools/plugin/src/main/java/Utils/Assets/AssetsWriter.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/Utils/Assets/AssetsWriter.java
rename to games-performance-tuner/tools/plugin/src/main/java/Utils/Assets/AssetsWriter.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/Utils/DataModelTransformer.java b/games-performance-tuner/tools/plugin/src/main/java/Utils/DataModelTransformer.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/Utils/DataModelTransformer.java
rename to games-performance-tuner/tools/plugin/src/main/java/Utils/DataModelTransformer.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/Utils/Generation/CppFileUtils.java b/games-performance-tuner/tools/plugin/src/main/java/Utils/Generation/CppFileUtils.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/Utils/Generation/CppFileUtils.java
rename to games-performance-tuner/tools/plugin/src/main/java/Utils/Generation/CppFileUtils.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/Utils/Generation/TuningForkMethodsGeneration.java b/games-performance-tuner/tools/plugin/src/main/java/Utils/Generation/TuningForkMethodsGeneration.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/Utils/Generation/TuningForkMethodsGeneration.java
rename to games-performance-tuner/tools/plugin/src/main/java/Utils/Generation/TuningForkMethodsGeneration.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/Utils/Monitoring/RequestServer.java b/games-performance-tuner/tools/plugin/src/main/java/Utils/Monitoring/RequestServer.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/Utils/Monitoring/RequestServer.java
rename to games-performance-tuner/tools/plugin/src/main/java/Utils/Monitoring/RequestServer.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/Utils/Monitoring/TelemetryProcessing.java b/games-performance-tuner/tools/plugin/src/main/java/Utils/Monitoring/TelemetryProcessing.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/Utils/Monitoring/TelemetryProcessing.java
rename to games-performance-tuner/tools/plugin/src/main/java/Utils/Monitoring/TelemetryProcessing.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/Utils/Monitoring/proto/performanceparameters.proto b/games-performance-tuner/tools/plugin/src/main/java/Utils/Monitoring/proto/performanceparameters.proto
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/Utils/Monitoring/proto/performanceparameters.proto
rename to games-performance-tuner/tools/plugin/src/main/java/Utils/Monitoring/proto/performanceparameters.proto
diff --git a/src/tuningfork/tools/plugin/src/main/java/Utils/Proto/CompilationException.java b/games-performance-tuner/tools/plugin/src/main/java/Utils/Proto/CompilationException.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/Utils/Proto/CompilationException.java
rename to games-performance-tuner/tools/plugin/src/main/java/Utils/Proto/CompilationException.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/Utils/Proto/JarUtils.java b/games-performance-tuner/tools/plugin/src/main/java/Utils/Proto/JarUtils.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/Utils/Proto/JarUtils.java
rename to games-performance-tuner/tools/plugin/src/main/java/Utils/Proto/JarUtils.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/Utils/Proto/OsUtils.java b/games-performance-tuner/tools/plugin/src/main/java/Utils/Proto/OsUtils.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/Utils/Proto/OsUtils.java
rename to games-performance-tuner/tools/plugin/src/main/java/Utils/Proto/OsUtils.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/Utils/Proto/ProtoCompiler.java b/games-performance-tuner/tools/plugin/src/main/java/Utils/Proto/ProtoCompiler.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/Utils/Proto/ProtoCompiler.java
rename to games-performance-tuner/tools/plugin/src/main/java/Utils/Proto/ProtoCompiler.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/Utils/Resources/ResourceLoader.java b/games-performance-tuner/tools/plugin/src/main/java/Utils/Resources/ResourceLoader.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/Utils/Resources/ResourceLoader.java
rename to games-performance-tuner/tools/plugin/src/main/java/Utils/Resources/ResourceLoader.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/Utils/UI/UIUtils.java b/games-performance-tuner/tools/plugin/src/main/java/Utils/UI/UIUtils.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/Utils/UI/UIUtils.java
rename to games-performance-tuner/tools/plugin/src/main/java/Utils/UI/UIUtils.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/Utils/UI/UIValidator.java b/games-performance-tuner/tools/plugin/src/main/java/Utils/UI/UIValidator.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/Utils/UI/UIValidator.java
rename to games-performance-tuner/tools/plugin/src/main/java/Utils/UI/UIValidator.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/Utils/Validation/ValidationTool.java b/games-performance-tuner/tools/plugin/src/main/java/Utils/Validation/ValidationTool.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/Utils/Validation/ValidationTool.java
rename to games-performance-tuner/tools/plugin/src/main/java/Utils/Validation/ValidationTool.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/View/Annotation/AnnotationDecorator.java b/games-performance-tuner/tools/plugin/src/main/java/View/Annotation/AnnotationDecorator.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/View/Annotation/AnnotationDecorator.java
rename to games-performance-tuner/tools/plugin/src/main/java/View/Annotation/AnnotationDecorator.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/View/Annotation/AnnotationTab.java b/games-performance-tuner/tools/plugin/src/main/java/View/Annotation/AnnotationTab.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/View/Annotation/AnnotationTab.java
rename to games-performance-tuner/tools/plugin/src/main/java/View/Annotation/AnnotationTab.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/View/DebugInfo/debugInfoTab.java b/games-performance-tuner/tools/plugin/src/main/java/View/DebugInfo/debugInfoTab.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/View/DebugInfo/debugInfoTab.java
rename to games-performance-tuner/tools/plugin/src/main/java/View/DebugInfo/debugInfoTab.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/View/Decorator/DocumentFilters.java b/games-performance-tuner/tools/plugin/src/main/java/View/Decorator/DocumentFilters.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/View/Decorator/DocumentFilters.java
rename to games-performance-tuner/tools/plugin/src/main/java/View/Decorator/DocumentFilters.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/View/Decorator/LabelScrollPane.java b/games-performance-tuner/tools/plugin/src/main/java/View/Decorator/LabelScrollPane.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/View/Decorator/LabelScrollPane.java
rename to games-performance-tuner/tools/plugin/src/main/java/View/Decorator/LabelScrollPane.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/View/Decorator/RoundedCornerBorder.java b/games-performance-tuner/tools/plugin/src/main/java/View/Decorator/RoundedCornerBorder.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/View/Decorator/RoundedCornerBorder.java
rename to games-performance-tuner/tools/plugin/src/main/java/View/Decorator/RoundedCornerBorder.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/View/Decorator/TableRenderer.java b/games-performance-tuner/tools/plugin/src/main/java/View/Decorator/TableRenderer.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/View/Decorator/TableRenderer.java
rename to games-performance-tuner/tools/plugin/src/main/java/View/Decorator/TableRenderer.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/View/Decorator/TreeSelections.java b/games-performance-tuner/tools/plugin/src/main/java/View/Decorator/TreeSelections.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/View/Decorator/TreeSelections.java
rename to games-performance-tuner/tools/plugin/src/main/java/View/Decorator/TreeSelections.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/View/Decorator/ValidatingComboBoxTableCellWrapper.java b/games-performance-tuner/tools/plugin/src/main/java/View/Decorator/ValidatingComboBoxTableCellWrapper.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/View/Decorator/ValidatingComboBoxTableCellWrapper.java
rename to games-performance-tuner/tools/plugin/src/main/java/View/Decorator/ValidatingComboBoxTableCellWrapper.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/View/Decorator/ValidationComboBoxRenderer.java b/games-performance-tuner/tools/plugin/src/main/java/View/Decorator/ValidationComboBoxRenderer.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/View/Decorator/ValidationComboBoxRenderer.java
rename to games-performance-tuner/tools/plugin/src/main/java/View/Decorator/ValidationComboBoxRenderer.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/View/Decorator/ValidationTextFieldRendererWrapper.java b/games-performance-tuner/tools/plugin/src/main/java/View/Decorator/ValidationTextFieldRendererWrapper.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/View/Decorator/ValidationTextFieldRendererWrapper.java
rename to games-performance-tuner/tools/plugin/src/main/java/View/Decorator/ValidationTextFieldRendererWrapper.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/View/Dialog/EnumDialogWrapper.java b/games-performance-tuner/tools/plugin/src/main/java/View/Dialog/EnumDialogWrapper.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/View/Dialog/EnumDialogWrapper.java
rename to games-performance-tuner/tools/plugin/src/main/java/View/Dialog/EnumDialogWrapper.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/View/Dialog/MainDialogWrapper.java b/games-performance-tuner/tools/plugin/src/main/java/View/Dialog/MainDialogWrapper.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/View/Dialog/MainDialogWrapper.java
rename to games-performance-tuner/tools/plugin/src/main/java/View/Dialog/MainDialogWrapper.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/View/Dialog/MonitoringFilterDialogWrapper.java b/games-performance-tuner/tools/plugin/src/main/java/View/Dialog/MonitoringFilterDialogWrapper.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/View/Dialog/MonitoringFilterDialogWrapper.java
rename to games-performance-tuner/tools/plugin/src/main/java/View/Dialog/MonitoringFilterDialogWrapper.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/View/EnumTable.java b/games-performance-tuner/tools/plugin/src/main/java/View/EnumTable.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/View/EnumTable.java
rename to games-performance-tuner/tools/plugin/src/main/java/View/EnumTable.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/View/Fidelity/FidelityCellPanelValidationRendererWrapper.java b/games-performance-tuner/tools/plugin/src/main/java/View/Fidelity/FidelityCellPanelValidationRendererWrapper.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/View/Fidelity/FidelityCellPanelValidationRendererWrapper.java
rename to games-performance-tuner/tools/plugin/src/main/java/View/Fidelity/FidelityCellPanelValidationRendererWrapper.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/View/Fidelity/FidelityTab.java b/games-performance-tuner/tools/plugin/src/main/java/View/Fidelity/FidelityTab.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/View/Fidelity/FidelityTab.java
rename to games-performance-tuner/tools/plugin/src/main/java/View/Fidelity/FidelityTab.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/View/Fidelity/FidelityTableData.java b/games-performance-tuner/tools/plugin/src/main/java/View/Fidelity/FidelityTableData.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/View/Fidelity/FidelityTableData.java
rename to games-performance-tuner/tools/plugin/src/main/java/View/Fidelity/FidelityTableData.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/View/Fidelity/FidelityTableDecorators.java b/games-performance-tuner/tools/plugin/src/main/java/View/Fidelity/FidelityTableDecorators.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/View/Fidelity/FidelityTableDecorators.java
rename to games-performance-tuner/tools/plugin/src/main/java/View/Fidelity/FidelityTableDecorators.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/View/Fidelity/FidelityTablePanel.java b/games-performance-tuner/tools/plugin/src/main/java/View/Fidelity/FidelityTablePanel.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/View/Fidelity/FidelityTablePanel.java
rename to games-performance-tuner/tools/plugin/src/main/java/View/Fidelity/FidelityTablePanel.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/View/Fidelity/FidelityValidatablePanelWithTextField.java b/games-performance-tuner/tools/plugin/src/main/java/View/Fidelity/FidelityValidatablePanelWithTextField.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/View/Fidelity/FidelityValidatablePanelWithTextField.java
rename to games-performance-tuner/tools/plugin/src/main/java/View/Fidelity/FidelityValidatablePanelWithTextField.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/View/Fidelity/FieldType.java b/games-performance-tuner/tools/plugin/src/main/java/View/Fidelity/FieldType.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/View/Fidelity/FieldType.java
rename to games-performance-tuner/tools/plugin/src/main/java/View/Fidelity/FieldType.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/View/FidelityChanger/FidelityChanger.java b/games-performance-tuner/tools/plugin/src/main/java/View/FidelityChanger/FidelityChanger.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/View/FidelityChanger/FidelityChanger.java
rename to games-performance-tuner/tools/plugin/src/main/java/View/FidelityChanger/FidelityChanger.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/View/InstrumentationSettings/InstrumentationSettingsTab.java b/games-performance-tuner/tools/plugin/src/main/java/View/InstrumentationSettings/InstrumentationSettingsTab.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/View/InstrumentationSettings/InstrumentationSettingsTab.java
rename to games-performance-tuner/tools/plugin/src/main/java/View/InstrumentationSettings/InstrumentationSettingsTab.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/View/License/LicenseTab.java b/games-performance-tuner/tools/plugin/src/main/java/View/License/LicenseTab.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/View/License/LicenseTab.java
rename to games-performance-tuner/tools/plugin/src/main/java/View/License/LicenseTab.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/View/Monitoring/MonitoringTab.java b/games-performance-tuner/tools/plugin/src/main/java/View/Monitoring/MonitoringTab.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/View/Monitoring/MonitoringTab.java
rename to games-performance-tuner/tools/plugin/src/main/java/View/Monitoring/MonitoringTab.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/View/PluginLayout.java b/games-performance-tuner/tools/plugin/src/main/java/View/PluginLayout.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/View/PluginLayout.java
rename to games-performance-tuner/tools/plugin/src/main/java/View/PluginLayout.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/View/Quality/QualityDecorators.java b/games-performance-tuner/tools/plugin/src/main/java/View/Quality/QualityDecorators.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/View/Quality/QualityDecorators.java
rename to games-performance-tuner/tools/plugin/src/main/java/View/Quality/QualityDecorators.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/View/Quality/QualityTab.java b/games-performance-tuner/tools/plugin/src/main/java/View/Quality/QualityTab.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/View/Quality/QualityTab.java
rename to games-performance-tuner/tools/plugin/src/main/java/View/Quality/QualityTab.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/View/TabLayout.java b/games-performance-tuner/tools/plugin/src/main/java/View/TabLayout.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/View/TabLayout.java
rename to games-performance-tuner/tools/plugin/src/main/java/View/TabLayout.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/View/TableComboBox.java b/games-performance-tuner/tools/plugin/src/main/java/View/TableComboBox.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/View/TableComboBox.java
rename to games-performance-tuner/tools/plugin/src/main/java/View/TableComboBox.java
diff --git a/src/tuningfork/tools/plugin/src/main/java/View/ValidationSettings/ValidationSettingsTab.java b/games-performance-tuner/tools/plugin/src/main/java/View/ValidationSettings/ValidationSettingsTab.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/java/View/ValidationSettings/ValidationSettingsTab.java
rename to games-performance-tuner/tools/plugin/src/main/java/View/ValidationSettings/ValidationSettingsTab.java
diff --git a/src/tuningfork/tools/plugin/src/main/resources/META-INF/plugin.xml b/games-performance-tuner/tools/plugin/src/main/resources/META-INF/plugin.xml
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/resources/META-INF/plugin.xml
rename to games-performance-tuner/tools/plugin/src/main/resources/META-INF/plugin.xml
diff --git a/src/tuningfork/tools/plugin/src/main/resources/strings_en.properties b/games-performance-tuner/tools/plugin/src/main/resources/strings_en.properties
similarity index 100%
rename from src/tuningfork/tools/plugin/src/main/resources/strings_en.properties
rename to games-performance-tuner/tools/plugin/src/main/resources/strings_en.properties
diff --git a/src/tuningfork/tools/plugin/src/test/java/AssetsFinderTest.java b/games-performance-tuner/tools/plugin/src/test/java/AssetsFinderTest.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/test/java/AssetsFinderTest.java
rename to games-performance-tuner/tools/plugin/src/test/java/AssetsFinderTest.java
diff --git a/src/tuningfork/tools/plugin/src/test/java/AssetsParserTest.java b/games-performance-tuner/tools/plugin/src/test/java/AssetsParserTest.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/test/java/AssetsParserTest.java
rename to games-performance-tuner/tools/plugin/src/test/java/AssetsParserTest.java
diff --git a/src/tuningfork/tools/plugin/src/test/java/AssetsWriterTest.java b/games-performance-tuner/tools/plugin/src/test/java/AssetsWriterTest.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/test/java/AssetsWriterTest.java
rename to games-performance-tuner/tools/plugin/src/test/java/AssetsWriterTest.java
diff --git a/src/tuningfork/tools/plugin/src/test/java/DataModelTransformerTest.java b/games-performance-tuner/tools/plugin/src/test/java/DataModelTransformerTest.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/test/java/DataModelTransformerTest.java
rename to games-performance-tuner/tools/plugin/src/test/java/DataModelTransformerTest.java
diff --git a/src/tuningfork/tools/plugin/src/test/java/ProtoCompilerTest.java b/games-performance-tuner/tools/plugin/src/test/java/ProtoCompilerTest.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/test/java/ProtoCompilerTest.java
rename to games-performance-tuner/tools/plugin/src/test/java/ProtoCompilerTest.java
diff --git a/src/tuningfork/tools/plugin/src/test/java/ProtocBinary.java b/games-performance-tuner/tools/plugin/src/test/java/ProtocBinary.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/test/java/ProtocBinary.java
rename to games-performance-tuner/tools/plugin/src/test/java/ProtocBinary.java
diff --git a/src/tuningfork/tools/plugin/src/test/java/TestDataHelper.java b/games-performance-tuner/tools/plugin/src/test/java/TestDataHelper.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/test/java/TestDataHelper.java
rename to games-performance-tuner/tools/plugin/src/test/java/TestDataHelper.java
diff --git a/src/tuningfork/tools/plugin/src/test/java/ValidationToolTest.java b/games-performance-tuner/tools/plugin/src/test/java/ValidationToolTest.java
similarity index 100%
rename from src/tuningfork/tools/plugin/src/test/java/ValidationToolTest.java
rename to games-performance-tuner/tools/plugin/src/test/java/ValidationToolTest.java
diff --git a/src/tuningfork/tools/plugin/src/test/resources/testdata/annotation_complex.proto b/games-performance-tuner/tools/plugin/src/test/resources/testdata/annotation_complex.proto
similarity index 100%
rename from src/tuningfork/tools/plugin/src/test/resources/testdata/annotation_complex.proto
rename to games-performance-tuner/tools/plugin/src/test/resources/testdata/annotation_complex.proto
diff --git a/src/tuningfork/tools/plugin/src/test/resources/testdata/annotation_not_only_enums.proto b/games-performance-tuner/tools/plugin/src/test/resources/testdata/annotation_not_only_enums.proto
similarity index 100%
rename from src/tuningfork/tools/plugin/src/test/resources/testdata/annotation_not_only_enums.proto
rename to games-performance-tuner/tools/plugin/src/test/resources/testdata/annotation_not_only_enums.proto
diff --git a/src/tuningfork/tools/plugin/src/test/resources/testdata/annotation_valid.proto b/games-performance-tuner/tools/plugin/src/test/resources/testdata/annotation_valid.proto
similarity index 100%
rename from src/tuningfork/tools/plugin/src/test/resources/testdata/annotation_valid.proto
rename to games-performance-tuner/tools/plugin/src/test/resources/testdata/annotation_valid.proto
diff --git a/src/tuningfork/tools/plugin/src/test/resources/testdata/compile_invalid.proto b/games-performance-tuner/tools/plugin/src/test/resources/testdata/compile_invalid.proto
similarity index 100%
rename from src/tuningfork/tools/plugin/src/test/resources/testdata/compile_invalid.proto
rename to games-performance-tuner/tools/plugin/src/test/resources/testdata/compile_invalid.proto
diff --git a/src/tuningfork/tools/plugin/src/test/resources/testdata/compile_valid.proto b/games-performance-tuner/tools/plugin/src/test/resources/testdata/compile_valid.proto
similarity index 100%
rename from src/tuningfork/tools/plugin/src/test/resources/testdata/compile_valid.proto
rename to games-performance-tuner/tools/plugin/src/test/resources/testdata/compile_valid.proto
diff --git a/src/tuningfork/tools/plugin/src/test/resources/testdata/compile_with_deps.proto b/games-performance-tuner/tools/plugin/src/test/resources/testdata/compile_with_deps.proto
similarity index 100%
rename from src/tuningfork/tools/plugin/src/test/resources/testdata/compile_with_deps.proto
rename to games-performance-tuner/tools/plugin/src/test/resources/testdata/compile_with_deps.proto
diff --git a/src/tuningfork/tools/plugin/src/test/resources/testdata/dev_tuningfork.proto b/games-performance-tuner/tools/plugin/src/test/resources/testdata/dev_tuningfork.proto
similarity index 100%
rename from src/tuningfork/tools/plugin/src/test/resources/testdata/dev_tuningfork.proto
rename to games-performance-tuner/tools/plugin/src/test/resources/testdata/dev_tuningfork.proto
diff --git a/src/tuningfork/tools/plugin/src/test/resources/testdata/dev_tuningfork_fidelityparams_1.txt b/games-performance-tuner/tools/plugin/src/test/resources/testdata/dev_tuningfork_fidelityparams_1.txt
similarity index 100%
rename from src/tuningfork/tools/plugin/src/test/resources/testdata/dev_tuningfork_fidelityparams_1.txt
rename to games-performance-tuner/tools/plugin/src/test/resources/testdata/dev_tuningfork_fidelityparams_1.txt
diff --git a/src/tuningfork/tools/plugin/src/test/resources/testdata/fidelity_params_complex.proto b/games-performance-tuner/tools/plugin/src/test/resources/testdata/fidelity_params_complex.proto
similarity index 100%
rename from src/tuningfork/tools/plugin/src/test/resources/testdata/fidelity_params_complex.proto
rename to games-performance-tuner/tools/plugin/src/test/resources/testdata/fidelity_params_complex.proto
diff --git a/src/tuningfork/tools/plugin/src/test/resources/testdata/fidelity_params_not_scalar.proto b/games-performance-tuner/tools/plugin/src/test/resources/testdata/fidelity_params_not_scalar.proto
similarity index 100%
rename from src/tuningfork/tools/plugin/src/test/resources/testdata/fidelity_params_not_scalar.proto
rename to games-performance-tuner/tools/plugin/src/test/resources/testdata/fidelity_params_not_scalar.proto
diff --git a/src/tuningfork/tools/plugin/src/test/resources/testdata/fidelity_params_valid.proto b/games-performance-tuner/tools/plugin/src/test/resources/testdata/fidelity_params_valid.proto
similarity index 100%
rename from src/tuningfork/tools/plugin/src/test/resources/testdata/fidelity_params_valid.proto
rename to games-performance-tuner/tools/plugin/src/test/resources/testdata/fidelity_params_valid.proto
diff --git a/src/tuningfork/tools/validation/CONTRIBUTING.md b/games-performance-tuner/tools/validation/CONTRIBUTING.md
similarity index 100%
rename from src/tuningfork/tools/validation/CONTRIBUTING.md
rename to games-performance-tuner/tools/validation/CONTRIBUTING.md
diff --git a/src/tuningfork/tools/validation/LICENSE b/games-performance-tuner/tools/validation/LICENSE
similarity index 100%
rename from src/tuningfork/tools/validation/LICENSE
rename to games-performance-tuner/tools/validation/LICENSE
diff --git a/src/tuningfork/tools/validation/README.md b/games-performance-tuner/tools/validation/README.md
similarity index 100%
rename from src/tuningfork/tools/validation/README.md
rename to games-performance-tuner/tools/validation/README.md
diff --git a/src/tuningfork/tools/validation/build.gradle b/games-performance-tuner/tools/validation/build.gradle
similarity index 65%
rename from src/tuningfork/tools/validation/build.gradle
rename to games-performance-tuner/tools/validation/build.gradle
index f2823f4..2962a49 100644
--- a/src/tuningfork/tools/validation/build.gradle
+++ b/games-performance-tuner/tools/validation/build.gradle
@@ -1,4 +1,4 @@
-apply plugin: 'java'
+apply plugin: 'java-library'
apply plugin: 'com.google.protobuf'
repositories {
@@ -20,20 +20,20 @@
mavenCentral()
}
dependencies {
- classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.8'
+ classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.19'
}
}
dependencies {
- compile 'com.google.guava:guava:26.0-jre'
- compile 'com.google.protobuf:protobuf-java:3.0.0'
- compile 'com.google.flogger:flogger:0.3.1'
- compile 'com.google.flogger:flogger-system-backend:0.3.1'
- compile 'com.beust:jcommander:1.7'
+ implementation 'com.google.guava:guava:26.0-jre'
+ implementation 'com.google.protobuf:protobuf-java:3.0.0'
+ implementation 'com.google.flogger:flogger:0.3.1'
+ implementation 'com.google.flogger:flogger-system-backend:0.3.1'
+ implementation 'com.beust:jcommander:1.7'
- testCompile 'junit:junit:4.12'
- testCompile 'com.google.truth:truth:0.43'
- testCompile 'org.junit.jupiter:junit-jupiter:5.4.1'
+ testImplementation 'junit:junit:4.12'
+ testImplementation 'com.google.truth:truth:0.43'
+ testImplementation 'org.junit.jupiter:junit-jupiter:5.4.1'
protobuf files('../../proto/tuningfork.proto')
protobuf files('src/test/resources/testdata/dev_tuningfork.proto') //for tests
@@ -54,9 +54,9 @@
}
baseName = 'TuningforkApkValidationTool'
from {
- configurations.compile.collect {
+ configurations.runtimeClasspath.collect {
it.isDirectory()? it:zipTree(it)
}
}
with jar
-}
\ No newline at end of file
+}
diff --git a/src/tuningfork/tools/validation/gradle b/games-performance-tuner/tools/validation/gradle
similarity index 100%
rename from src/tuningfork/tools/validation/gradle
rename to games-performance-tuner/tools/validation/gradle
diff --git a/src/tuningfork/tools/validation/gradlew b/games-performance-tuner/tools/validation/gradlew
similarity index 100%
rename from src/tuningfork/tools/validation/gradlew
rename to games-performance-tuner/tools/validation/gradlew
diff --git a/src/tuningfork/tools/validation/gradlew.bat b/games-performance-tuner/tools/validation/gradlew.bat
similarity index 100%
rename from src/tuningfork/tools/validation/gradlew.bat
rename to games-performance-tuner/tools/validation/gradlew.bat
diff --git a/src/tuningfork/tools/validation/settings.gradle b/games-performance-tuner/tools/validation/settings.gradle
similarity index 100%
rename from src/tuningfork/tools/validation/settings.gradle
rename to games-performance-tuner/tools/validation/settings.gradle
diff --git a/src/tuningfork/tools/validation/src/main/java/com/google/tuningfork/validation/CompilationException.java b/games-performance-tuner/tools/validation/src/main/java/com/google/tuningfork/validation/CompilationException.java
similarity index 100%
rename from src/tuningfork/tools/validation/src/main/java/com/google/tuningfork/validation/CompilationException.java
rename to games-performance-tuner/tools/validation/src/main/java/com/google/tuningfork/validation/CompilationException.java
diff --git a/src/tuningfork/tools/validation/src/main/java/com/google/tuningfork/validation/DeveloperTuningforkParser.java b/games-performance-tuner/tools/validation/src/main/java/com/google/tuningfork/validation/DeveloperTuningforkParser.java
similarity index 100%
rename from src/tuningfork/tools/validation/src/main/java/com/google/tuningfork/validation/DeveloperTuningforkParser.java
rename to games-performance-tuner/tools/validation/src/main/java/com/google/tuningfork/validation/DeveloperTuningforkParser.java
diff --git a/src/tuningfork/tools/validation/src/main/java/com/google/tuningfork/validation/ErrorCollector.java b/games-performance-tuner/tools/validation/src/main/java/com/google/tuningfork/validation/ErrorCollector.java
similarity index 100%
rename from src/tuningfork/tools/validation/src/main/java/com/google/tuningfork/validation/ErrorCollector.java
rename to games-performance-tuner/tools/validation/src/main/java/com/google/tuningfork/validation/ErrorCollector.java
diff --git a/src/tuningfork/tools/validation/src/main/java/com/google/tuningfork/validation/ErrorType.java b/games-performance-tuner/tools/validation/src/main/java/com/google/tuningfork/validation/ErrorType.java
similarity index 100%
rename from src/tuningfork/tools/validation/src/main/java/com/google/tuningfork/validation/ErrorType.java
rename to games-performance-tuner/tools/validation/src/main/java/com/google/tuningfork/validation/ErrorType.java
diff --git a/src/tuningfork/tools/validation/src/main/java/com/google/tuningfork/validation/ExternalProtoCompiler.java b/games-performance-tuner/tools/validation/src/main/java/com/google/tuningfork/validation/ExternalProtoCompiler.java
similarity index 100%
rename from src/tuningfork/tools/validation/src/main/java/com/google/tuningfork/validation/ExternalProtoCompiler.java
rename to games-performance-tuner/tools/validation/src/main/java/com/google/tuningfork/validation/ExternalProtoCompiler.java
diff --git a/src/tuningfork/tools/validation/src/main/java/com/google/tuningfork/validation/FolderConfig.java b/games-performance-tuner/tools/validation/src/main/java/com/google/tuningfork/validation/FolderConfig.java
similarity index 100%
rename from src/tuningfork/tools/validation/src/main/java/com/google/tuningfork/validation/FolderConfig.java
rename to games-performance-tuner/tools/validation/src/main/java/com/google/tuningfork/validation/FolderConfig.java
diff --git a/src/tuningfork/tools/validation/src/main/java/com/google/tuningfork/validation/ParserErrorCollector.java b/games-performance-tuner/tools/validation/src/main/java/com/google/tuningfork/validation/ParserErrorCollector.java
similarity index 100%
rename from src/tuningfork/tools/validation/src/main/java/com/google/tuningfork/validation/ParserErrorCollector.java
rename to games-performance-tuner/tools/validation/src/main/java/com/google/tuningfork/validation/ParserErrorCollector.java
diff --git a/src/tuningfork/tools/validation/src/main/java/com/google/tuningfork/validation/TuningForkPaths.java b/games-performance-tuner/tools/validation/src/main/java/com/google/tuningfork/validation/TuningForkPaths.java
similarity index 100%
rename from src/tuningfork/tools/validation/src/main/java/com/google/tuningfork/validation/TuningForkPaths.java
rename to games-performance-tuner/tools/validation/src/main/java/com/google/tuningfork/validation/TuningForkPaths.java
diff --git a/src/tuningfork/tools/validation/src/main/java/com/google/tuningfork/validation/TuningforkApkValidationTool.java b/games-performance-tuner/tools/validation/src/main/java/com/google/tuningfork/validation/TuningforkApkValidationTool.java
similarity index 100%
rename from src/tuningfork/tools/validation/src/main/java/com/google/tuningfork/validation/TuningforkApkValidationTool.java
rename to games-performance-tuner/tools/validation/src/main/java/com/google/tuningfork/validation/TuningforkApkValidationTool.java
diff --git a/src/tuningfork/tools/validation/src/main/java/com/google/tuningfork/validation/ValidationUtil.java b/games-performance-tuner/tools/validation/src/main/java/com/google/tuningfork/validation/ValidationUtil.java
similarity index 100%
rename from src/tuningfork/tools/validation/src/main/java/com/google/tuningfork/validation/ValidationUtil.java
rename to games-performance-tuner/tools/validation/src/main/java/com/google/tuningfork/validation/ValidationUtil.java
diff --git a/src/tuningfork/tools/validation/src/test/java/com/google/tuningfork/validation/DeveloperTuningforkParserTest.java b/games-performance-tuner/tools/validation/src/test/java/com/google/tuningfork/validation/DeveloperTuningforkParserTest.java
similarity index 100%
rename from src/tuningfork/tools/validation/src/test/java/com/google/tuningfork/validation/DeveloperTuningforkParserTest.java
rename to games-performance-tuner/tools/validation/src/test/java/com/google/tuningfork/validation/DeveloperTuningforkParserTest.java
diff --git a/src/tuningfork/tools/validation/src/test/java/com/google/tuningfork/validation/ExternalProtoCompilerTest.java b/games-performance-tuner/tools/validation/src/test/java/com/google/tuningfork/validation/ExternalProtoCompilerTest.java
similarity index 100%
rename from src/tuningfork/tools/validation/src/test/java/com/google/tuningfork/validation/ExternalProtoCompilerTest.java
rename to games-performance-tuner/tools/validation/src/test/java/com/google/tuningfork/validation/ExternalProtoCompilerTest.java
diff --git a/src/tuningfork/tools/validation/src/test/java/com/google/tuningfork/validation/ProtoCompilerHelper.java b/games-performance-tuner/tools/validation/src/test/java/com/google/tuningfork/validation/ProtoCompilerHelper.java
similarity index 100%
rename from src/tuningfork/tools/validation/src/test/java/com/google/tuningfork/validation/ProtoCompilerHelper.java
rename to games-performance-tuner/tools/validation/src/test/java/com/google/tuningfork/validation/ProtoCompilerHelper.java
diff --git a/src/tuningfork/tools/validation/src/test/java/com/google/tuningfork/validation/ProtocBinary.java b/games-performance-tuner/tools/validation/src/test/java/com/google/tuningfork/validation/ProtocBinary.java
similarity index 100%
rename from src/tuningfork/tools/validation/src/test/java/com/google/tuningfork/validation/ProtocBinary.java
rename to games-performance-tuner/tools/validation/src/test/java/com/google/tuningfork/validation/ProtocBinary.java
diff --git a/src/tuningfork/tools/validation/src/test/java/com/google/tuningfork/validation/TestdataHelper.java b/games-performance-tuner/tools/validation/src/test/java/com/google/tuningfork/validation/TestdataHelper.java
similarity index 100%
rename from src/tuningfork/tools/validation/src/test/java/com/google/tuningfork/validation/TestdataHelper.java
rename to games-performance-tuner/tools/validation/src/test/java/com/google/tuningfork/validation/TestdataHelper.java
diff --git a/src/tuningfork/tools/validation/src/test/java/com/google/tuningfork/validation/ValidationUtilTest.java b/games-performance-tuner/tools/validation/src/test/java/com/google/tuningfork/validation/ValidationUtilTest.java
similarity index 100%
rename from src/tuningfork/tools/validation/src/test/java/com/google/tuningfork/validation/ValidationUtilTest.java
rename to games-performance-tuner/tools/validation/src/test/java/com/google/tuningfork/validation/ValidationUtilTest.java
diff --git a/src/tuningfork/tools/validation/src/test/resources/protoc b/games-performance-tuner/tools/validation/src/test/resources/protoc
similarity index 100%
rename from src/tuningfork/tools/validation/src/test/resources/protoc
rename to games-performance-tuner/tools/validation/src/test/resources/protoc
diff --git a/src/tuningfork/tools/validation/src/test/resources/testdata/annotation_complex.proto b/games-performance-tuner/tools/validation/src/test/resources/testdata/annotation_complex.proto
similarity index 100%
rename from src/tuningfork/tools/validation/src/test/resources/testdata/annotation_complex.proto
rename to games-performance-tuner/tools/validation/src/test/resources/testdata/annotation_complex.proto
diff --git a/src/tuningfork/tools/validation/src/test/resources/testdata/annotation_not_only_enums.proto b/games-performance-tuner/tools/validation/src/test/resources/testdata/annotation_not_only_enums.proto
similarity index 100%
rename from src/tuningfork/tools/validation/src/test/resources/testdata/annotation_not_only_enums.proto
rename to games-performance-tuner/tools/validation/src/test/resources/testdata/annotation_not_only_enums.proto
diff --git a/src/tuningfork/tools/validation/src/test/resources/testdata/annotation_valid.proto b/games-performance-tuner/tools/validation/src/test/resources/testdata/annotation_valid.proto
similarity index 100%
rename from src/tuningfork/tools/validation/src/test/resources/testdata/annotation_valid.proto
rename to games-performance-tuner/tools/validation/src/test/resources/testdata/annotation_valid.proto
diff --git a/src/tuningfork/tools/validation/src/test/resources/testdata/compile_invalid.proto b/games-performance-tuner/tools/validation/src/test/resources/testdata/compile_invalid.proto
similarity index 100%
rename from src/tuningfork/tools/validation/src/test/resources/testdata/compile_invalid.proto
rename to games-performance-tuner/tools/validation/src/test/resources/testdata/compile_invalid.proto
diff --git a/src/tuningfork/tools/validation/src/test/resources/testdata/compile_valid.proto b/games-performance-tuner/tools/validation/src/test/resources/testdata/compile_valid.proto
similarity index 100%
rename from src/tuningfork/tools/validation/src/test/resources/testdata/compile_valid.proto
rename to games-performance-tuner/tools/validation/src/test/resources/testdata/compile_valid.proto
diff --git a/src/tuningfork/tools/validation/src/test/resources/testdata/compile_with_deps.proto b/games-performance-tuner/tools/validation/src/test/resources/testdata/compile_with_deps.proto
similarity index 100%
rename from src/tuningfork/tools/validation/src/test/resources/testdata/compile_with_deps.proto
rename to games-performance-tuner/tools/validation/src/test/resources/testdata/compile_with_deps.proto
diff --git a/src/tuningfork/tools/validation/src/test/resources/testdata/dev_tuningfork.proto b/games-performance-tuner/tools/validation/src/test/resources/testdata/dev_tuningfork.proto
similarity index 100%
rename from src/tuningfork/tools/validation/src/test/resources/testdata/dev_tuningfork.proto
rename to games-performance-tuner/tools/validation/src/test/resources/testdata/dev_tuningfork.proto
diff --git a/src/tuningfork/tools/validation/src/test/resources/testdata/dev_tuningfork_fidelityparams_1.txt b/games-performance-tuner/tools/validation/src/test/resources/testdata/dev_tuningfork_fidelityparams_1.txt
similarity index 100%
rename from src/tuningfork/tools/validation/src/test/resources/testdata/dev_tuningfork_fidelityparams_1.txt
rename to games-performance-tuner/tools/validation/src/test/resources/testdata/dev_tuningfork_fidelityparams_1.txt
diff --git a/src/tuningfork/tools/validation/src/test/resources/testdata/dev_tuningfork_fidelityparams_valid.bin b/games-performance-tuner/tools/validation/src/test/resources/testdata/dev_tuningfork_fidelityparams_valid.bin
similarity index 100%
rename from src/tuningfork/tools/validation/src/test/resources/testdata/dev_tuningfork_fidelityparams_valid.bin
rename to games-performance-tuner/tools/validation/src/test/resources/testdata/dev_tuningfork_fidelityparams_valid.bin
Binary files differ
diff --git a/src/tuningfork/tools/validation/src/test/resources/testdata/fidelity_params_complex.proto b/games-performance-tuner/tools/validation/src/test/resources/testdata/fidelity_params_complex.proto
similarity index 100%
rename from src/tuningfork/tools/validation/src/test/resources/testdata/fidelity_params_complex.proto
rename to games-performance-tuner/tools/validation/src/test/resources/testdata/fidelity_params_complex.proto
diff --git a/src/tuningfork/tools/validation/src/test/resources/testdata/fidelity_params_not_scalar.proto b/games-performance-tuner/tools/validation/src/test/resources/testdata/fidelity_params_not_scalar.proto
similarity index 100%
rename from src/tuningfork/tools/validation/src/test/resources/testdata/fidelity_params_not_scalar.proto
rename to games-performance-tuner/tools/validation/src/test/resources/testdata/fidelity_params_not_scalar.proto
diff --git a/src/tuningfork/tools/validation/src/test/resources/testdata/fidelity_params_valid.proto b/games-performance-tuner/tools/validation/src/test/resources/testdata/fidelity_params_valid.proto
similarity index 100%
rename from src/tuningfork/tools/validation/src/test/resources/testdata/fidelity_params_valid.proto
rename to games-performance-tuner/tools/validation/src/test/resources/testdata/fidelity_params_valid.proto
diff --git a/src/tuningfork/unity/unity_tuningfork.cpp b/games-performance-tuner/unity/unity_tuningfork.cpp
similarity index 94%
rename from src/tuningfork/unity/unity_tuningfork.cpp
rename to games-performance-tuner/unity/unity_tuningfork.cpp
index 2ec4258..82a33d1 100644
--- a/src/tuningfork/unity/unity_tuningfork.cpp
+++ b/games-performance-tuner/unity/unity_tuningfork.cpp
@@ -135,6 +135,12 @@
settings->swappy_tracer_fn = s_swappy_tracer_fn;
}
settings->swappy_version = s_swappy_version;
+ // In API <= 23, the memory portion of the api_key sent from C#
+ // becomes corrupted for some reason and it results in failure
+ // to connect to the endpoint.
+ // Introducing this workaround to correctly read the api_key
+ // set in tuningfork_settings.bin.
+ settings->api_key = nullptr;
return TuningFork_init(settings, jni::Env(), jni::AppContextGlobalRef());
}
diff --git a/src/tuningfork/unity_version.script b/games-performance-tuner/unity_version.script
similarity index 100%
rename from src/tuningfork/unity_version.script
rename to games-performance-tuner/unity_version.script
diff --git a/src/tuningfork/version.script b/games-performance-tuner/version.script
similarity index 100%
rename from src/tuningfork/version.script
rename to games-performance-tuner/version.script
diff --git a/gradle.properties b/gradle.properties
index 8046488..0235eb3 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1 +1,2 @@
android.builder.sdkDownload=false
+android.useAndroidX=true
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 5916f98..f2c2436 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip
diff --git a/hooks/check_bugfix_version.sh b/hooks/check_bugfix_version.sh
deleted file mode 100755
index 843a622..0000000
--- a/hooks/check_bugfix_version.sh
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/bin/bash
-
-SWAPPY_PATTERN="src/swappy/.*[.](cpp|h)$"
-TUNINGFORK_PATTERN="src/tuningfork/.*[.](cpp|h)$"
-
-SWAPPY_COMMON_H="include/swappy/swappy_common.h"
-TUNINGFORK_H="include/tuningfork/tuningfork.h"
-
-files=$2
-swappyChanged=0
-tuningforkChanged=0
-for file in ${files[@]}; do
- echo $file
-
- if [[ $file =~ $SWAPPY_PATTERN ]]; then
- swappyChanged=1
- fi
- if [[ $file =~ $TUNINGFORK_PATTERN ]]; then
- tuningforkChanged=1
- fi
-done
-
-echo "Swappy changed: $swappyChanged"
-echo "TuningFork changed: $tuningforkChanged"
-
-# TODO (willosborn): check that swappy and tuningfork versions match those in VERSIONS
-
-exit 0
diff --git a/hooks/check_library_versions.sh b/hooks/check_library_versions.sh
new file mode 100755
index 0000000..ce2b95f
--- /dev/null
+++ b/hooks/check_library_versions.sh
@@ -0,0 +1,41 @@
+#!/bin/bash
+
+SWAPPY_COMMON_H="include/swappy/swappy_common.h"
+TUNINGFORK_H="include/tuningfork/tuningfork.h"
+
+VERSIONS="VERSIONS"
+
+library_names=(games-frame-pacing games-performance-tuner game-activity game-text-input games-controller games-memory-advice)
+header_files=(include/swappy/swappy_common.h include/tuningfork/tuningfork.h game-activity/prefab-src/modules/game-activity/include/game-activity/GameActivity.h game-text-input/prefab-src/modules/game-text-input/include/game-text-input/gametextinput.h include/paddleboat/paddleboat.h include/memory_advice/memory_advice.h)
+library_version_prefix=(SWAPPY TUNINGFORK GAMEACTIVITY GAMETEXTINPUT PADDLEBOAT MEMORY_ADVICE)
+
+
+for i in "${!library_names[@]}"; do
+ library="${library_names[i]}"
+ header="${header_files[i]}"
+ prefix="${library_version_prefix[i]}"
+ [[ `cat VERSIONS` =~ $library[[:space:]]*([0-9]+.[0-9]+.[0-9]+)[[:space:]]*([a-z]*) ]]
+ versions_version="${BASH_REMATCH[1]}"
+ version_suffix="${BASH_REMATCH[2]}"
+ [[ `cat $header` =~ ${prefix}_MAJOR_VERSION.([0-9]+) ]]
+ major="${BASH_REMATCH[1]}"
+ [[ `cat $header` =~ ${prefix}_MINOR_VERSION.([0-9]+) ]]
+ minor="${BASH_REMATCH[1]}"
+ [[ `cat $header` =~ ${prefix}_BUGFIX_VERSION.([0-9]+) ]]
+ bugfix="${BASH_REMATCH[1]}"
+ header_version="${major}.${minor}.${bugfix}"
+ if [[ "${major}.${minor}.${bugfix}" != $versions_version ]]; then
+ echo "Version mismatch! For ${library}, the version declared in its header is ${header_version} but the version declared in the VERSIONS file is ${versions_version}."
+ exit 1
+ fi
+ if [[ $version_suffix =~ "alpha" ]] && [[ $bugfix != "0" ]]; then
+ echo "Invalid version! ${library} is declared as an alpha release, but has a bugfix version."
+ exit 1
+ fi
+ if [[ $version_suffix =~ "beta" ]] && [[ $bugfix != "0" ]]; then
+ echo "Invalid version! ${library} is declared as a beta release, but has a bugfix version."
+ exit 1
+ fi
+done
+
+exit 0
diff --git a/hooks/check_swappy_abi_version.sh b/hooks/check_swappy_abi_version.sh
deleted file mode 100755
index 7c2957d..0000000
--- a/hooks/check_swappy_abi_version.sh
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/bin/bash
-
-SWAPPY_GL_H="include/swappy/swappyGL.h"
-SWAPPY_GL_EXTRA_H="include/swappy/swappyGL_extra.h"
-SWAPPY_VK_H="include/swappy/swappyVk.h"
-SWAPPY_VK_EXTRA_H="include/swappy/swappyVk_extra.h"
-SWAPPY_COMMON_H="include/swappy/swappy_common.h"
-
-SWAPPY_HEADERS=($SWAPPY_GL_H $SWAPPY_GL_EXTRA_H $SWAPPY_VK_H $SWAPPY_VK_EXTRA_H $SWAPPY_COMMON_H)
-
-files=$2
-headerChanged=0
-for file in ${files[@]}; do
- for header in ${SWAPPY_HEADERS[@]}; do
- if [[ $file =~ $header ]]; then
- headerChanged=1
- break
- fi
- done
-done
-
-if [ $headerChanged -eq "1" ]; then
- majorVersion=`git show $1 $SWAPPY_COMMON_H | grep "#define SWAPPY_MAJOR_VERSION" | wc -l`
- minorVersion=`git show $1 $SWAPPY_COMMON_H | grep "#define SWAPPY_MINOR_VERSION" | wc -l`
- # echo "version_files $version_files"
- if [[ $majorVersion -eq "0" && $minorVersion -eq "0" ]]; then
- echo "Warning: did you break the Swappy ABI without changing SWAPPY_MAJOR_VERSION or SWAPPY_MINOR_VERSION ?"
- fi
-
-fi
-
-exit 0
diff --git a/hooks/check_tuningfork_abi_version.sh b/hooks/check_tuningfork_abi_version.sh
deleted file mode 100755
index fdf30cb..0000000
--- a/hooks/check_tuningfork_abi_version.sh
+++ /dev/null
@@ -1,30 +0,0 @@
-#!/bin/bash
-
-TUNINGFORK_H="include/tuningfork/tuningfork.h"
-TUNINGFORK_EXTRA_H="include/tuningfork/tuningfork_extra.h"
-UNITY_TUNINGFORK_H="include/tuningfork/unity_tuningfork.h"
-TUNINGFORK_HEADERS=($TUNINGFORK_H $TUNINGFORK_EXTRA_H $UNITY_TUNINGFORK_H)
-
-files=$2
-headerChanged=0
-for file in ${files[@]}; do
- for header in ${TUNINGFORK_HEADERS[@]}; do
- if [[ $file =~ $header ]]; then
- headerChanged=1
- break
- fi
- done
-done
-
-if [ $headerChanged -eq "1" ]; then
-
- majorVersion=`git show $1 $TUNINGFORK_H | grep "#define TUNINGFORK_MAJOR_VERSION" | wc -l`
- minorVersion=`git show $1 $TUNINGFORK_H | grep "#define TUNINGFORK_MINOR_VERSION" | wc -l`
- # echo "version_files $version_files"
- if [[ $majorVersion -eq "0" && $minorVersion -eq "0" ]]; then
- echo "Warning: did you break the TuningFork ABI without changing TUNINGFORK_MAJOR_VERSION or TUNINGFORK_MINOR_VERSION ?"
- fi
-
-fi
-
-exit 0
diff --git a/include/common/gamesdk_common.h b/include/common/gamesdk_common.h
index 8512262..d29ac01 100644
--- a/include/common/gamesdk_common.h
+++ b/include/common/gamesdk_common.h
@@ -31,11 +31,11 @@
// There are separate versions for each GameSDK component that use this format:
#define ANDROID_GAMESDK_PACKED_VERSION(MAJOR, MINOR, BUGFIX) \
- ((MAJOR << 16) | (MINOR) | (BUGFIX << 8))
+ ((MAJOR << 16) | (MINOR << 8) | (BUGFIX))
// Accessors
#define ANDROID_GAMESDK_MAJOR_VERSION(PACKED) ((PACKED) >> 16)
-#define ANDROID_GAMESDK_MINOR_VERSION(PACKED) ((PACKED)&0xff)
-#define ANDROID_GAMESDK_BUGFIX_VERSION(PACKED) (((PACKED) >> 8) & 0xff)
+#define ANDROID_GAMESDK_MINOR_VERSION(PACKED) (((PACKED) >> 8) & 0xff)
+#define ANDROID_GAMESDK_BUGFIX_VERSION(PACKED) ((PACKED) & 0xff)
#define AGDK_STRING_VERSION(MAJOR, MINOR, BUGFIX, GIT) \
#MAJOR "." #MINOR "." #BUGFIX "." #GIT
diff --git a/include/memory_advice/memory_advice.h b/include/memory_advice/memory_advice.h
index 18aa11e..340744e 100644
--- a/include/memory_advice/memory_advice.h
+++ b/include/memory_advice/memory_advice.h
@@ -14,13 +14,6 @@
* limitations under the License.
*/
-/*
- * This is the main interface to the Android Games Memory Advice API. Currently
- * this is only the skeleton of the library, with minimum/no functionality.
- *
- * TODO: Complete this documentation as the library finalizes.
- */
-
/**
* @defgroup memory_advice Memory Advice main interface
* The main interface to use Memory Advice.
@@ -30,95 +23,56 @@
#pragma once
#include <jni.h>
+#include <stdint.h>
+
+#include "common/gamesdk_common.h"
#ifdef __cplusplus
extern "C" {
#endif
-/** @cond INTERNAL */
-
-#define MEMORY_ADVICE_MAJOR_VERSION 1
+#define MEMORY_ADVICE_MAJOR_VERSION 2
#define MEMORY_ADVICE_MINOR_VERSION 0
#define MEMORY_ADVICE_BUGFIX_VERSION 0
-#define MEMORY_ADVICE_PACKED_VERSION \
- ANDROID_GAMESDK_PACKED_VERSION(MEMORY_ADVICE_MAJOR_VERSION, \
- MEMORY_ADVICE_MINOR_VERSION, \
- MEMORY_ADVICE_BUGFIX_VERSION)
-
-// Internal macros to generate a symbol to track Memory Advice version, do not
-// use directly.
-#define MEMORY_ADVICE_VERSION_CONCAT_NX(PREFIX, MAJOR, MINOR) \
- PREFIX##_##MAJOR##_##MINOR
-#define MEMORY_ADVICE_VERSION_CONCAT(PREFIX, MAJOR, MINOR) \
- MEMORY_ADVICE_VERSION_CONCAT_NX(PREFIX, MAJOR, MINOR)
-#define MEMORY_ADVICE_VERSION_SYMBOL \
- MEMORY_ADVICE_VERSION_CONCAT(MemoryAdvice_version, \
- MEMORY_ADVICE_MAJOR_VERSION, \
- MEMORY_ADVICE_MINOR_VERSION)
+#define MEMORY_ADVICE_PACKED_VERSION \
+ ANDROID_GAMESDK_PACKED_VERSION(TUNINGFORK_MAJOR_VERSION, \
+ TUNINGFORK_MINOR_VERSION, \
+ TUNINGFORK_BUGFIX_VERSION)
/**
* @brief All the error codes that can be returned by MemoryAdvice functions.
*/
-typedef enum MemoryAdvice_ErrorCode {
+typedef enum MemoryAdvice_ErrorCode : int32_t {
MEMORYADVICE_ERROR_OK = 0, ///< No error
MEMORYADVICE_ERROR_NOT_INITIALIZED =
- 1, ///< A call was made before MemoryAdvice was initialized.
+ -1, ///< A call was made before MemoryAdvice was initialized.
MEMORYADVICE_ERROR_ALREADY_INITIALIZED =
- 2, ///< MemoryAdvice_init was called more than once.
+ -2, ///< MemoryAdvice_init was called more than once.
MEMORYADVICE_ERROR_LOOKUP_TABLE_INVALID =
- 3, ///< The provided lookup table was not a valid json object.
+ -3, ///< The provided lookup table was not a valid json object.
MEMORYADVICE_ERROR_ADVISOR_PARAMETERS_INVALID =
- 4, ///< The provided advisor parameters was not a valid json object.
- MEMORYADVICE_ERROR_WATCHER_ALREADY_SET =
- 5, ///< SetWatcher function was called while there was already a
- ///< watcher in place.
+ -4, ///< The provided advisor parameters was not a valid json object.
+ MEMORYADVICE_ERROR_WATCHER_NOT_FOUND =
+ -5, ///< UnregisterWatcher was called with an invalid callback.
MEMORYADVICE_ERROR_TFLITE_MODEL_INVALID =
- 6, ///< A correct TFLite model was not provided.
+ -6, ///< A correct TFLite model was not provided.
} MemoryAdvice_ErrorCode;
/**
* @brief All possible memory states that can be reported by the library.
*/
-typedef enum MemoryAdvice_MemoryState {
+typedef enum MemoryAdvice_MemoryState : int32_t {
MEMORYADVICE_STATE_UNKNOWN = 0, ///< The memory state cannot be determined.
- MEMORYADVICE_STATE_OK =
- 1, ///< The application can safely allocate significant memory.
+ MEMORYADVICE_STATE_OK = 1, ///< The application can safely allocate memory.
MEMORYADVICE_STATE_APPROACHING_LIMIT =
- 2, ///< The application should not allocate significant memory.
+ 2, ///< The application should minimize memory allocation.
MEMORYADVICE_STATE_CRITICAL =
3, ///< The application should free memory as soon as possible, until
///< the memory state changes.
} MemoryAdvice_MemoryState;
-/**
- * @brief A char* representing a serialized json object.
- * @see MemoryAdvice_JsonSerialization_free for how to deallocate
- * the memory once finished with the buffer.
- */
-typedef struct MemoryAdvice_JsonSerialization {
- char *json; ///< String for the json object.
- uint32_t size; ///< Size of the json string.
- ///< Deallocation callback (may be NULL if not owned).
- void (*dealloc)(struct MemoryAdvice_JsonSerialization *);
-} MemoryAdvice_JsonSerialization;
-
-typedef void (*MemoryAdvice_WatcherCallback)(MemoryAdvice_MemoryState);
-
-// Internal init function. Do not call directly.
-MemoryAdvice_ErrorCode MemoryAdvice_initDefaultParams_internal(JNIEnv *env,
- jobject context);
-
-// Internal init function. Do not call directly.
-MemoryAdvice_ErrorCode MemoryAdvice_init_internal(JNIEnv *env, jobject context,
- const char *params);
-
-// Internal function to track MemoryAdvice version bundled in a binary. Do not
-// call directly. If you are getting linker errors related to
-// MemoryAdvice_version_x_y, you probably have a mismatch between the header
-// used at compilation and the actually library used by the linker.
-void MEMORY_ADVICE_VERSION_SYMBOL();
-
-/** @endcond */
+typedef void (*MemoryAdvice_WatcherCallback)(MemoryAdvice_MemoryState state,
+ void *user_data);
/**
* @brief Initialize the Memory Advice library. This must be called before any
@@ -134,14 +88,7 @@
* @return MEMORYADVICE_ERROR_ALREADY_INITIALIZED if Memory Advice was already
* initialized.
*/
-static inline MemoryAdvice_ErrorCode MemoryAdvice_initDefaultParams(
- JNIEnv *env, jobject context) {
- // This call ensures that the header and the linked library are from the
- // same version (if not, a linker error will be triggered because of an
- // undefined symbol).
- MEMORY_ADVICE_VERSION_SYMBOL();
- return MemoryAdvice_initDefaultParams_internal(env, context);
-}
+MemoryAdvice_ErrorCode MemoryAdvice_init(JNIEnv *env, jobject context);
/**
* @brief Initialize the Memory Advice library. This must be called before any
@@ -160,15 +107,8 @@
* @return MEMORYADVICE_ERROR_ALREADY_INITIALIZED if Memory Advice was already
* initialized.
*/
-static inline MemoryAdvice_ErrorCode MemoryAdvice_init(JNIEnv *env,
- jobject context,
- const char *params) {
- // This call ensures that the header and the linked library are from the
- // same version (if not, a linker error will be triggered because of an
- // undefined symbol).
- MEMORY_ADVICE_VERSION_SYMBOL();
- return MemoryAdvice_init_internal(env, context, params);
-}
+MemoryAdvice_ErrorCode MemoryAdvice_initWithParams(JNIEnv *env, jobject context,
+ const char *params);
/**
* @brief Returns the current memory state.
@@ -176,42 +116,36 @@
* @param state a pointer to a MemoryAdvice_MemoryState, in which the
* memory state will be written
*
- * @return MEMORYADVICE_ERROR_OK if successful,
- * @return MEMORYADVICE_ERROR_NOT_INITIALIZED if Memory Advice was not yet
- * initialized.
+ * @return A MemoryAdvice_MemoryState, if successful,
+ * @return MEMORYADVICE_ERROR_NOT_INITIALIZED (a negative number) if Memory
+ * Advice was not yet initialized.
*/
-MemoryAdvice_ErrorCode MemoryAdvice_getMemoryState(
- MemoryAdvice_MemoryState *state);
-
-/**
- * @brief Returns the advice regarding the current memory state.
- *
- * @param advice a pointer to a struct, in which the memory advice will be
- * written.
- *
- * @return MEMORYADVICE_ERROR_OK if successful,
- * @return MEMORYADVICE_ERROR_NOT_INITIALIZED if Memory Advice was not yet
- * initialized.
- */
-MemoryAdvice_ErrorCode MemoryAdvice_getAdvice(
- MemoryAdvice_JsonSerialization *advice);
+MemoryAdvice_MemoryState MemoryAdvice_getMemoryState();
/**
* @brief Calculates an estimate for the amount of memory that can safely be
- * allocated, in bytes.
+ * allocated, as a percentage of the total memory.
*
- * @param estimate a pointer to an int64_t, in which the estimate will
- * be written
- *
- * @return MEMORYADVICE_ERROR_OK if successful,
- * @return MEMORYADVICE_ERROR_NOT_INITIALIZED if Memory Advice was not yet
- * initialized.
+ * @return A positive number between 0 and 100 with an estimate of the
+ * percentage memory available.
+ * @return MEMORYADVICE_ERROR_NOT_INITIALIZED (a negative number) if Memory
+ * Advice was not yet initialized.
*/
-MemoryAdvice_ErrorCode MemoryAdvice_getAvailableMemory(int64_t *estimate);
+float MemoryAdvice_getPercentageAvailableMemory();
/**
- * @brief Sets a watcher that polls the Memory Advice library periodically, and
- * invokes the watcher callback when the memory state goes critical.
+ * @brief Calculates the total memory available on the device, as reported by
+ * ActivityManager#getMemoryInfo()
+ *
+ * @return The total memory of the device, in bytes.
+ * @return MEMORYADVICE_ERROR_NOT_INITIALIZED (a negative number) if Memory
+ * Advice was not yet initialized.
+ */
+int64_t MemoryAdvice_getTotalMemory();
+
+/**
+ * @brief Registers a watcher that polls the Memory Advice library periodically,
+ * and invokes the watcher callback when the memory state goes critical.
*
* This function creates another thread that calls MemoryAdvice_getMemoryState
* every `intervalMillis` milliseconds. If the returned state is not
@@ -222,37 +156,29 @@
* polled
* @param callback the callback function that will be invoked if memory goes
* critical
+ * @param user_data context to pass to the callback function
*
* @return MEMORYADVICE_ERROR_OK if successful,
* @return MEMORYADVICE_ERROR_NOT_INITIALIZED if Memory Advice was not yet
* initialized,
- * @return MEMORYADVICE_ERROR_WATCHER_ALREADY_SET if there's already a watcher
- * in place.
*/
-MemoryAdvice_ErrorCode MemoryAdvice_setWatcher(
- uint64_t intervalMillis, MemoryAdvice_WatcherCallback callback);
+MemoryAdvice_ErrorCode MemoryAdvice_registerWatcher(
+ uint64_t intervalMillis, MemoryAdvice_WatcherCallback callback,
+ void *user_data);
/**
- * @brief Deallocate any memory owned by the json serialization.
- * @param ser A json serialization
- */
-inline void MemoryAdvice_JsonSerialization_free(
- MemoryAdvice_JsonSerialization *ser) {
- if (ser->dealloc) {
- ser->dealloc(ser);
- ser->dealloc = NULL;
- }
-}
-
-/**
- * @brief Removes the watcher that has been set by the MemoryAdvice_setWatcher
- * function
+ * @brief Removes all watchers with the given callback that were previously
+ * registered using
+ * {@link MemoryAdvice_registerWatcher}.
*
* @return MEMORYADVICE_ERROR_OK if successful,
* @return MEMORYADVICE_ERROR_NOT_INITIALIZED if Memory Advice was not yet
* initialized.
+ * @return MEMORYADVICE_ERROR_WATCHER_NOT_FOUND if the given callback wasn't
+ * previously registered.
*/
-MemoryAdvice_ErrorCode MemoryAdvice_removeWatcher();
+MemoryAdvice_ErrorCode MemoryAdvice_unregisterWatcher(
+ MemoryAdvice_WatcherCallback callback);
#ifdef __cplusplus
} // extern "C" {
diff --git a/include/memory_advice/memory_advice_debug.h b/include/memory_advice/memory_advice_debug.h
new file mode 100644
index 0000000..a96a078
--- /dev/null
+++ b/include/memory_advice/memory_advice_debug.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @defgroup memory_advice_debug Memory Advice debugging functions
+ * @{
+ */
+
+#pragma once
+
+#include <jni.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief A char* representing a serialized json object.
+ * @see MemoryAdvice_JsonSerialization_free for how to deallocate
+ * the memory once finished with the buffer.
+ */
+typedef struct MemoryAdvice_JsonSerialization {
+ char *json; ///< String for the json object.
+ uint32_t size; ///< Size of the json string.
+ ///< Deallocation callback (may be NULL if not owned).
+ void (*dealloc)(struct MemoryAdvice_JsonSerialization *);
+} MemoryAdvice_JsonSerialization;
+
+/**
+ * @brief Returns the advice regarding the current memory state.
+ *
+ * @param advice a pointer to a struct, in which the memory advice will be
+ * written.
+ *
+ * @return MEMORYADVICE_ERROR_OK if successful,
+ * @return MEMORYADVICE_ERROR_NOT_INITIALIZED if Memory Advice was not yet
+ * initialized.
+ */
+MemoryAdvice_ErrorCode MemoryAdvice_getAdvice(
+ MemoryAdvice_JsonSerialization *advice);
+
+/**
+ * @brief Deallocate any memory owned by the json serialization.
+ * @param ser A json serialization
+ */
+void MemoryAdvice_JsonSerialization_free(MemoryAdvice_JsonSerialization *ser);
+
+/**
+ * @brief Perform tests on the memory advice library.
+ *
+ * @return 0 if the tests pass.
+ * @return non-zero if the tests fail. Error messages will be printed to logcat
+ * in this case.
+ */
+int32_t MemoryAdvice_test();
+
+#ifdef __cplusplus
+} // extern "C" {
+#endif
diff --git a/include/paddleboat/paddleboat.h b/include/paddleboat/paddleboat.h
index df31c5a..2be8bc4 120000
--- a/include/paddleboat/paddleboat.h
+++ b/include/paddleboat/paddleboat.h
@@ -1 +1 @@
-../../GameController/src/main/cpp/paddleboat/include/paddleboat.h
\ No newline at end of file
+../../games-controller/src/main/cpp/paddleboat/include/paddleboat.h
\ No newline at end of file
diff --git a/include/swappy/OWNERS b/include/swappy/OWNERS
index d79d861..289ff34 100644
--- a/include/swappy/OWNERS
+++ b/include/swappy/OWNERS
@@ -1 +1 @@
-include ../../src/swappy/OWNERS
+include ../../games-frame-pacing/OWNERS
diff --git a/include/swappy/swappyGL.h b/include/swappy/swappyGL.h
index 4ad9a4c..b5ced7c 100644
--- a/include/swappy/swappyGL.h
+++ b/include/swappy/swappyGL.h
@@ -131,6 +131,16 @@
*/
void SwappyGL_setBufferStuffingFixWait(int32_t n_frames);
+/**
+ * @brief Get the supported refresh periods of this device. Call once with
+ * out_refreshrates set to nullptr to get the number of supported refresh
+ * periods, then call again passing that number as allocated_entries and
+ * an array of size equal to allocated_entries that will be filled with the
+ * refresh periods.
+ */
+int SwappyGL_getSupportedRefreshPeriodsNS(uint64_t *out_refreshrates,
+ int allocated_entries);
+
#ifdef __cplusplus
};
#endif
diff --git a/include/swappy/swappyGL_extra.h b/include/swappy/swappyGL_extra.h
index 155268f..676a4e6 100644
--- a/include/swappy/swappyGL_extra.h
+++ b/include/swappy/swappyGL_extra.h
@@ -29,11 +29,6 @@
#include "swappy_common.h"
-/**
- * The longest duration, in refresh periods, represented by the statistics.
- * @see SwappyStats
- */
-#define MAX_FRAME_BUCKETS 6
#ifdef __cplusplus
extern "C" {
@@ -94,52 +89,6 @@
void SwappyGL_enableStats(bool enabled);
/**
- * @brief Swappy statistics, collected if toggled on with
- * ::SwappyGL_enableStats.
- * @see SwappyGL_getStats
- */
-struct SwappyStats {
- /** @brief Total frames swapped by swappy */
- uint64_t totalFrames;
-
- /** @brief Histogram of the number of screen refreshes a frame waited in the
- * compositor queue after rendering was completed.
- *
- * For example:
- * if a frame waited 2 refresh periods in the compositor queue after
- * rendering was done, the frame will be counted in idleFrames[2]
- */
- uint64_t idleFrames[MAX_FRAME_BUCKETS];
-
- /** @brief Histogram of the number of screen refreshes passed between the
- * requested presentation time and the actual present time.
- *
- * For example:
- * if a frame was presented 2 refresh periods after the requested
- * timestamp swappy set, the frame will be counted in lateFrames[2]
- */
- uint64_t lateFrames[MAX_FRAME_BUCKETS];
-
- /** @brief Histogram of the number of screen refreshes passed between two
- * consecutive frames
- *
- * For example:
- * if frame N was presented 2 refresh periods after frame N-1
- * frame N will be counted in offsetFromPreviousFrame[2]
- */
- uint64_t offsetFromPreviousFrame[MAX_FRAME_BUCKETS];
-
- /** @brief Histogram of the number of screen refreshes passed between the
- * call to Swappy_recordFrameStart and the actual present time.
- *
- * For example:
- * if a frame was presented 2 refresh periods after the call to
- * `Swappy_recordFrameStart` the frame will be counted in latencyFrames[2]
- */
- uint64_t latencyFrames[MAX_FRAME_BUCKETS];
-};
-
-/**
* @brief Should be called if stats have been enabled with SwappyGL_enableStats.
*
* When stats collection is enabled with SwappyGL_enableStats, the app is
diff --git a/include/swappy/swappyVk.h b/include/swappy/swappyVk.h
index 291f2ab..847bc75 100644
--- a/include/swappy/swappyVk.h
+++ b/include/swappy/swappyVk.h
@@ -25,7 +25,9 @@
#include "jni.h"
#include "swappy_common.h"
+#ifndef VK_NO_PROTOTYPES
#define VK_NO_PROTOTYPES 1
+#endif
#include <vulkan/vulkan.h>
#ifdef __cplusplus
@@ -324,6 +326,66 @@
*/
uint64_t SwappyVk_getSwapIntervalNS(VkSwapchainKHR swapchain);
+/**
+ * @brief Get the supported refresh periods of this device. Call once with
+ * out_refreshrates set to nullptr to get the number of supported refresh
+ * periods, then call again passing that number as allocated_entries and
+ * an array of size equal to allocated_entries that will be filled with the
+ * refresh periods.
+ */
+int SwappyVk_getSupportedRefreshPeriodsNS(uint64_t* out_refreshrates,
+ int allocated_entries,
+ VkSwapchainKHR swapchain);
+/**
+ * @brief Check if Swappy is enabled for the specified swapchain.
+ *
+ * @return false if SwappyVk_initAndGetRefreshCycleDuration was not
+ * called for the specified swapchain, true otherwise.
+ */
+bool SwappyVk_isEnabled(VkSwapchainKHR swapchain, bool* isEnabled);
+
+/**
+ * @brief Toggle statistics collection on/off
+ *
+ * By default, stats collection is off and there is no overhead related to
+ * stats. An app can turn on stats collection by calling
+ * `SwappyVk_enableStats(swapchain, true)`. Then, the app is expected to call
+ * ::SwappyVk_recordFrameStart for each frame before starting to do any CPU
+ * related work. Stats will be logged to logcat with a 'FrameStatistics' tag. An
+ * app can get the stats by calling ::SwappyVk_getStats.
+ *
+ * @param[in] swapchain - The swapchain for which frame stat collection is
+ * configured.
+ * @param enabled - Whether to enable/disable frame stat collection.
+ */
+void SwappyVk_enableStats(VkSwapchainKHR swapchain, bool enabled);
+
+/**
+ * @brief Should be called if stats have been enabled with SwappyVk_enableStats.
+ *
+ * When stats collection is enabled with SwappyVk_enableStats, the app is
+ * expected to call this function for each frame before starting to do any CPU
+ * related work. It is assumed that this function will be called after a
+ * successful call to vkAcquireNextImageKHR.
+ *
+ * @param[in] queue - The VkQueue associated with the device and swapchain
+ * @param[in] swapchain - The swapchain where the frame is presented to.
+ * @param[in] image - The image in swapchain that corresponds to the frame.
+
+ * @see SwappyVk_enableStats.
+ */
+void SwappyVk_recordFrameStart(VkQueue queue, VkSwapchainKHR swapchain, uint32_t image);
+
+/**
+ * @brief Returns the stats collected, if statistics collection was toggled on.
+ *
+ * @param[in] swapchain - The swapchain for which stats are being queried.
+ * @param swappyStats - Pointer to a SwappyStats that will be populated with
+ * the collected stats.
+ * @see SwappyStats
+ */
+void SwappyVk_getStats(VkSwapchainKHR swapchain, SwappyStats *swappyStats);
+
#ifdef __cplusplus
} // extern "C"
#endif
diff --git a/include/swappy/swappy_common.h b/include/swappy/swappy_common.h
index ab7a133..ed2386f 100644
--- a/include/swappy/swappy_common.h
+++ b/include/swappy/swappy_common.h
@@ -36,13 +36,19 @@
/** @brief Swap interval for 20fps, in nanoseconds. */
#define SWAPPY_SWAP_20FPS (50000000L)
+/**
+ * The longest duration, in refresh periods, represented by the statistics.
+ * @see SwappyStats
+ */
+#define MAX_FRAME_BUCKETS 6
+
/** @cond INTERNAL */
#define SWAPPY_SYSTEM_PROP_KEY_DISABLE "swappy.disable"
// Internal macros to track Swappy version, do not use directly.
-#define SWAPPY_MAJOR_VERSION 1
-#define SWAPPY_MINOR_VERSION 10
+#define SWAPPY_MAJOR_VERSION 2
+#define SWAPPY_MINOR_VERSION 0
#define SWAPPY_BUGFIX_VERSION 0
#define SWAPPY_PACKED_VERSION \
ANDROID_GAMESDK_PACKED_VERSION(SWAPPY_MAJOR_VERSION, SWAPPY_MINOR_VERSION, \
@@ -122,6 +128,52 @@
*/
const char* Swappy_versionString();
+/**
+ * @brief Swappy frame statistics, collected if toggled on with
+ * ::SwappyGL_enableStats or ::SwappyVk_enableStats.
+ */
+typedef struct SwappyStats {
+ /** @brief Total frames swapped by swappy */
+ uint64_t totalFrames;
+
+ /** @brief Histogram of the number of screen refreshes a frame waited in the
+ * compositor queue after rendering was completed.
+ *
+ * For example:
+ * if a frame waited 2 refresh periods in the compositor queue after
+ * rendering was done, the frame will be counted in idleFrames[2]
+ */
+ uint64_t idleFrames[MAX_FRAME_BUCKETS];
+
+ /** @brief Histogram of the number of screen refreshes passed between the
+ * requested presentation time and the actual present time.
+ *
+ * For example:
+ * if a frame was presented 2 refresh periods after the requested
+ * timestamp swappy set, the frame will be counted in lateFrames[2]
+ */
+ uint64_t lateFrames[MAX_FRAME_BUCKETS];
+
+ /** @brief Histogram of the number of screen refreshes passed between two
+ * consecutive frames
+ *
+ * For example:
+ * if frame N was presented 2 refresh periods after frame N-1
+ * frame N will be counted in offsetFromPreviousFrame[2]
+ */
+ uint64_t offsetFromPreviousFrame[MAX_FRAME_BUCKETS];
+
+ /** @brief Histogram of the number of screen refreshes passed between the
+ * call to Swappy_recordFrameStart and the actual present time.
+ *
+ * For example:
+ * if a frame was presented 2 refresh periods after the call to
+ * `Swappy_recordFrameStart` the frame will be counted in latencyFrames[2]
+ */
+ uint64_t latencyFrames[MAX_FRAME_BUCKETS];
+} SwappyStats;
+
+
#ifdef __cplusplus
} // extern "C"
#endif
diff --git a/include/tuningfork/tuningfork.h b/include/tuningfork/tuningfork.h
index e1eacf9..1395c41 100644
--- a/include/tuningfork/tuningfork.h
+++ b/include/tuningfork/tuningfork.h
@@ -47,8 +47,8 @@
/** @cond INTERNAL */
-#define TUNINGFORK_MAJOR_VERSION 1
-#define TUNINGFORK_MINOR_VERSION 5
+#define TUNINGFORK_MAJOR_VERSION 2
+#define TUNINGFORK_MINOR_VERSION 0
#define TUNINGFORK_BUGFIX_VERSION 0
#define TUNINGFORK_PACKED_VERSION \
ANDROID_GAMESDK_PACKED_VERSION(TUNINGFORK_MAJOR_VERSION, \
@@ -193,6 +193,16 @@
36, ///< The loading state was not set as part of
///< TuningFork_LoadingTimeMetadata when calling a loading
///< function.
+ TUNINGFORK_ERROR_NO_ACTIVE_LOADING_GROUP =
+ 37, ///< There was no active loading group when
+ ///< TuningFork_stopLoadingGroup
+ ///< was called.
+ TUNINGFORK_ERROR_FRAME_LOGGING_ALREADY_PAUSED =
+ 38, ///< Cannot pause frame time logging because it is already paused.
+ TUNINGFORK_ERROR_FRAME_LOGGING_ALREADY_RUNNING =
+ 39, ///< Cannot resume frame time logging because it is already
+ ///< running.
+
// Error codes 100-150 are reserved for engines integrations.
} TuningFork_ErrorCode;
@@ -362,6 +372,11 @@
* tuningfork_settings.bin file. See tuningfork.proto for more information.
*/
const char* api_key;
+ /**
+ * If false, sensitive information is removed from logging.
+ * Default is false.
+ */
+ bool verbose_logging_enabled;
} TuningFork_Settings;
/**
@@ -539,6 +554,37 @@
TuningFork_ErrorCode TuningFork_enableMemoryRecording(bool enable);
/**
+ * @brief Check if frame time logging is paused
+ *
+ * @return true if frame time logging is paused. False otherwise.
+ */
+bool TuningFork_isFrameTimeLoggingPaused();
+
+/**
+ * @brief Pause the recording of frame times to the frame time histogram.
+ * This can be useful for disabling frame time recording during menus or
+ * loading.
+ *
+ * @return TUNINGFORK_ERROR_OK on success.
+ * @return TUNINGFORK_ERROR_FRAME_LOGGING_ALREADY_PAUSED if logging is already
+ * paused
+ * @return TUNINGFORK_ERROR_TUNINGFORK_NOT_INITIALIZED if Tuning Fork wasn't
+ * initialized.
+ */
+TuningFork_ErrorCode TuningFork_pauseFrameTimeLogging();
+
+/**
+ * @brief Resume the recording of frame times to the frame time histogram.
+ *
+ * @return TUNINGFORK_ERROR_OK on success.
+ * @return TUNINGFORK_ERROR_FRAME_LOGGING_ALREADY_RUNNING if logging isn't
+ * paused
+ * @return TUNINGFORK_ERROR_TUNINGFORK_NOT_INITIALIZED if Tuning Fork wasn't
+ * initialized.
+ */
+TuningFork_ErrorCode TuningFork_resumeFrameTimeLogging();
+
+/**
* @brief Metadata recorded with a loading time event
*/
typedef struct TuningFork_LoadingTimeMetadata {
diff --git a/prepproto.gradle b/prepproto.gradle
new file mode 100644
index 0000000..07f881b
--- /dev/null
+++ b/prepproto.gradle
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Common gradle functions for preparing protobuf definitions for use by APT.
+
+import org.gradle.internal.os.OperatingSystem;
+import static groovy.lang.Closure.IDENTITY;
+
+def protobufInstallDir() {
+ def gameSdkRoot = buildscript.sourceFile.getParent()
+ def protoDirName = 'linux-x86'
+ if (OperatingSystem.current().isWindows()) {
+ protoDirName = 'win'
+ } else if (OperatingSystem.current().isMacOsX()) {
+ protoDirName = 'mac'
+ }
+ return new File("${gameSdkRoot}/third_party/protobuf-3.0.0/install/"
+ + protoDirName).getPath()
+}
+
+def getEnvironment() {
+ def env = [:]
+ def trans = IDENTITY
+ if (OperatingSystem.current().isWindows()) {
+ trans = { it.toUpperCase() }
+ }
+ System.getenv().each { entry -> env[trans(entry.key)] = entry.value }
+ return env
+}
+
+task prepare_proto_before() {
+ def gameSdkRoot = buildscript.sourceFile.getParent()
+ def protocBinDir = protobufInstallDir() + "/bin"
+ def sixDir = "${gameSdkRoot}/../external/six"
+ def env = getEnvironment()
+ env['PATH'] = protocBinDir + System.getProperty("path.separator") + env['PATH']
+ env['PYTHONPATH'] = sixDir + System.getProperty("path.separator") + env['PYTHONPATH']
+ // Install python-protobuf
+ exec {
+ workingDir "${gameSdkRoot}/third_party/protobuf-3.0.0/python"
+ setEnvironment env
+ commandLine "python", "setup.py", "install", "--user"
+ }
+ // Generate nano-pb requirements.
+ // Nanopb provides a Makefile to generate the requirements, however on windows it
+ // is not trivial to work with make. Instead we just use protoc to generate the requirements
+ // which is what the Makefile does as well to begin with.
+ if (OperatingSystem.current().isWindows()) {
+ exec {
+ workingDir "${gameSdkRoot}/../external/nanopb-c/generator/proto"
+ setEnvironment env
+ commandLine "${protocBinDir}/protoc.exe", "nanopb.proto", "--python_out=."
+ }
+ exec {
+ workingDir "${gameSdkRoot}/../external/nanopb-c/generator/proto"
+ setEnvironment env
+ commandLine "${protocBinDir}/protoc.exe", "plugin.proto", "--python_out=."
+ }
+ }
+ else {
+ exec {
+ workingDir "${gameSdkRoot}/../external/nanopb-c/generator/proto"
+ setEnvironment env
+ commandLine "make"
+ }
+ }
+ outputs.upToDateWhen { false }
+}
+
+task prepare_proto(dependsOn: prepare_proto_before) {
+ def gameSdkRoot = buildscript.sourceFile.getParent()
+ exec {
+ workingDir "${gameSdkRoot}"
+ commandLine "python"
+ args = ["ab_info.py"]
+ }
+}
+
+
diff --git a/samples/agdktunnel/README.md b/samples/agdktunnel/README.md
index b7ffaf0..d7dd8a9 100644
--- a/samples/agdktunnel/README.md
+++ b/samples/agdktunnel/README.md
@@ -10,15 +10,15 @@
* GameActivity
* GameController
* GameTextInput
+* Memory Advice
* Oboe
+## Building
+
+Open the `samples/agdktunnel' directory in Android Studio 4.2 or higher.
+
## Prerequisites
-### Python
-
-Python is expected to be available in your `PATH`. The `protobuf` package is
-expected to have been installed via `pip`.
-
### GLM
The GLM library must be downloaded before building:
@@ -27,13 +27,112 @@
2. `cd third-party/glm`
3. `git clone https://github.com/g-truc/glm.git`
-## Building
+### Google Play Games for PC (optional)
-Open the `samples/agdktunnel' directory in Android Studio 4.2 or higher.
+You can build this application to run in Google Play Games for PC with features like the Input
+SDK to display keyboard controls and cloud saving using PGS. For an optimized experience consider
+enabling the following features:
-## Android Performance Tuner API Key
+1. (Optional) Google Play Games Services (PGS).
+2. (Optional) Play Asset Delivery API to delivery DXT1 compressed texture assets.
+
+### Google Play Games Services (optional)
+
+We use Play Games Services (PGS) to sign-in and cloud save to play in Play Games Services and mobile.
+To enable this feature following the next steps.
+
+1. Rename the package of AGDK Tunnel to a name of your choosing.
+2. Create an application on the [Google Play Console](https://play.google.com/console/about/?) and follow the steps to set up Play Games Services using your package name.
+3. Replace the **game_services_project_id** string value in `app/src/main/res/values/strings.xml` with the id of your project in the Google Play Console.
+
+### Google Play Asset Delivery (optional)
+
+We use the Play Asset Delivery (PAD) API with Texture Compression Format Targeting (TCFT) to
+delivery optimal compressed textures (ETC2 by default and DXT1 for Google Play Games for PC). To
+enable PAD:
+
+1. Edit the `gradle.properties` file and change: `PADEnabled=false` to `PADEnabled=true`.
+2. [Download the Play Core API into the project](https://developer.android.com/guide/playcore#native)
+ in the `apps/libs` directory.
+3. Play Asset Delivery requires building an Android App Bundle instead of an APK. Bundletool will
+ help you to test your Android App Bundle locally, download bundletool by visiting the
+ [bundletool releases page](https://github.com/google/bundletool/releases) and install it in the
+ root of the project.
+4. Using Android Studio build an App Bundle **Build > Build bundle(s)/APK(s) > Build bundle(s)**
+5. Install from the Android App Bundle file to your device using bundletool:
+ ```
+ java -jar bundletool-all-1.9.1.jar build-apks
+ --bundle=app/build/outputs/bundle/playGamesPCDebug/app-playGamesPC-debug.aab
+ --output=agdktunnel.apks
+ --local-testing
+
+ java -jar bundletool-all-1.9.1.jar install-apks --apks=agdktunnel.apks
+ ```
+
+For more information see the codelab: [Using Play Asset Delivery in native games](https://developer.android.com/codelabs/native-gamepad#0)
+
+## Memory Advice
+
+AGDKTunnel integrates the Memory Advice library to receive hints when memory usage is becoming
+critical and risks being terminated by the OS due to low available memory.
+
+Memory consumption is artificially inflated using the MemoryConsumer class, which also displays
+status information onscreen. This is off by default. The **Consume Memory** button in the title
+screen can be used to enable memory consumption.
+
+When active, the Memory Consumer allocates at random intervals blocks of variable sizes and
+places them into two memory pools, dubbed 'General' and 'Cache'. If the Memory Advice library
+signals a critical memory situation, the Memory Consumer frees all allocations from the Cache
+pool. After the first critical memory warning, the Memory Consumer ceases to allocate into the
+General pool and only allocates into the Cache pool.
+
+When active, the Memory Consumer displays statistics on screen in the UI and Play scenes. The
+information is formatted as follows:
+
+`(status) (crit warning count) (percent available) (General pool size) (Cache pool size)`
+
+**(status)**
+`OK|WARN|CRIT`
+Memory State reported by the Memory Advice library
+**(crit warning count)**
+The number of times Memory Advice has reported a critical memory shortage
+**(percent available)**
+The estimated percentage of available memory reported by Memory Advice, this
+is updated every five seconds
+**(General pool size)**
+Total allocation size of the General pool in megabytes
+**(Cache pool size)**
+Total allocation size of the Cache pool in megabytes
+
+## Android Performance Tuner (APT)
+
+Android Performance Tuner is disabled by default. To enable it, perform the following steps:
+
+1. Ensure APT build prerequisites are met
+2. Add an APT API key
+3. Enable the APT option in gradle.properties
+
+These steps are described in more detail in the following subsections.
+
+### APT Prerequisites
+
+#### Python
+
+Python is expected to be available in your `PATH`. The `protobuf` package is
+expected to have been installed via `pip`. In some cases, Android Studio may be using an internal
+install of Python rather than a system install. If this is the case, you may need to run
+`pip install protobuf` from the Terminal tab in Android Studio.
+
+#### APT API Key
The APT functionality requires a valid API key to operate. This is not
necessary to run the sample. For information on configure an API key
for APT, see the **Get an API key for the Android Performance Tuner**
-section of the Codelab [Integrating Android Performance Tuner into your native Android game](https://developer.android.com/codelabs/android-performance-tuner-native#1).
\ No newline at end of file
+section of the Codelab [Integrating Android Performance Tuner into your native Android game](https://developer.android.com/codelabs/android-performance-tuner-native#1).
+
+#### Enable the APT option
+
+To enable building the runtime APT assets and use the library at runtime, edit the
+`gradle.properties` file and change: `APTEnabled=false` to `APTEnabled=true`. When switching
+configurations, it is recommended to sync the gradle file, and run
+**Build -> Refresh Linked C++ Projects** and **Build -> Clean Project** before rebuilding.
diff --git a/samples/agdktunnel/app/build.gradle b/samples/agdktunnel/app/build.gradle
index c87462b..48a7015 100644
--- a/samples/agdktunnel/app/build.gradle
+++ b/samples/agdktunnel/app/build.gradle
@@ -18,9 +18,17 @@
apply plugin: 'com.android.application'
+// See README for details on enabling APT and PAD
+def useApt = APTEnabled
+def usePad = PADEnabled
+
+// Define a path to the extracted Play Core SDK files.
+// If using a relative path, wrap it with file() since CMake requires absolute paths.
+def playcoreDir = file('libs/play-core-native-sdk')
+
android {
compileSdkVersion 30
- ndkVersion '21.4.7075529'
+ ndkVersion '23.1.7779620'
defaultConfig {
applicationId 'com.google.sample.agdktunnel'
@@ -28,16 +36,31 @@
targetSdkVersion 30
versionCode 1
versionName '1.0.3'
+
+ externalNativeBuild {
+ cmake {
+ // Define the PLAYCORE_LOCATION directive.
+ arguments "-DANDROID_STL=c++_static",
+ "-DPLAYCORE_LOCATION=$playcoreDir"
+ }
+ }
+ ndk {
+ // Skip deprecated ABIs. Only required when using NDK 16 or earlier.
+ abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
+ }
}
buildTypes {
release {
minifyEnabled = false
proguardFiles getDefaultProguardFile('proguard-android.txt'),
- 'proguard-rules.pro'
+ 'proguard-rules.pro',
+ '$playcoreDir/proguard/common.pgcfg',
+ '$playcoreDir/proguard/per-feature-proguard-files'
externalNativeBuild {
cmake {
arguments "-DANDROID_STL=c++_shared",
- "-DUSE_ASSET_PACKS=false"
+ "-DUSE_ASSET_PACKS=$usePad",
+ "-DUSE_APT=$useApt"
}
}
}
@@ -48,7 +71,8 @@
'proguard-rules.pro'
cmake {
arguments "-DANDROID_STL=c++_shared",
- "-DUSE_ASSET_PACKS=false"
+ "-DUSE_ASSET_PACKS=$usePad",
+ "-DUSE_APT=$useApt"
}
}
multiDexEnabled true
@@ -62,6 +86,13 @@
buildFeatures {
prefab true
}
+ assetPacks = [":install_time_assets", ":on_demand_assets"]
+ // Enable the splitting of your asset packs per textures
+ bundle {
+ texture {
+ enableSplit true
+ }
+ }
}
dependencies {
@@ -79,16 +110,34 @@
// Be aware that when using libraries from Maven, the versions here may target
// a future release that does not exist yet, which is why a locally built
// library is used when agdktunnel depends on the latest features.
- implementation "androidx.games:games-frame-pacing:1.9.0"
- implementation "androidx.games:games-performance-tuner:1.5.0-beta01"
- implementation "androidx.games:games-controller:1.0.0"
- // implementation "androidx.games:games-activity:1.1.0-beta01"
+ // Uncomment to use maven artifacts instead of local code
+// implementation "androidx.games:games-frame-pacing:1.9.0"
+// implementation "androidx.games:games-performance-tuner:1.5.0-beta01"
+// implementation "androidx.games:games-controller:1.0.0"
+// implementation "androidx.games:games-activity:1.1.0"
+ implementation "androidx.games:games-memory-advice:1.0.0-beta01"
+ // Uncomment the line below (implementation fileTree...)
+ // and comment out "implementation project(":*")" lines
+ // to use a locally built .aar
+ // See ../settings.gradle as well.
+ //implementation fileTree(dir: '../../../', include: [
+ // 'game-activity-release.aar',
+ // 'games-performance-tuner-release.aar',
+ // 'games-frame-pacing-release.aar',
+ // 'games-controller-release.aar',
+ //])
+ implementation project(':game-activity')
+ implementation project(':games-performance-tuner')
+ implementation project(':games-frame-pacing')
+ implementation project(':games-controller')
- // Uncomment to use a locally-built library.
- implementation fileTree(dir: 'libs', include: ['GameActivity.aar'])
- // 'GameController.aar',
- // 'games-frame-pacing-1.9.0.aar',
- // 'games-performance-tuner-1.5.0.aar'])
+ // Google Play Games dependencies
+ implementation "com.google.android.gms:play-services-games-v2:17.0.0"
+ implementation 'com.google.android.libraries.play.games:inputmapping:1.0.0-beta'
+ // Use the Play Core AAR included with the SDK.
+ if(PADEnabled.toBoolean()) {
+ implementation files("$playcoreDir/playcore.aar")
+ }
}
android.lintOptions {
@@ -97,7 +146,7 @@
// Android Performance Tuner validation setup and protoc compile tasks
task createJar(type: GradleBuild) {
- buildFile = GameSDKPath + '/src/tuningfork/tools/validation/build.gradle'
+ buildFile = GameSDKPath + '/games-performance-tuner/tools/validation/build.gradle'
tasks = ['createJar']
}
@@ -109,15 +158,46 @@
return GameSDKPath + "/third_party/protobuf-3.0.0/install/" + platformName
}
-task buildTuningForkBinFiles(type: Exec) {
+task buildTuningForkBinFiles(type: JavaExec) {
dependsOn createJar
- commandLine "java",
- "-jar",
- GameSDKPath + "/src/tuningfork/tools/validation/build/libs/TuningforkApkValidationTool.jar",
+ main "-jar"
+ args (GameSDKPath + "/games-performance-tuner/tools/validation/build/libs/TuningforkApkValidationTool.jar",
"--tuningforkPath",
"src/main/assets/tuningfork",
"--protoCompiler",
- getProtocPath()
+ getProtocPath())
}
-tasks.preBuild.dependsOn("buildTuningForkBinFiles")
+if (APTEnabled.toBoolean()) {
+ apply from: GameSDKPath + '/prepproto.gradle'
+ preBuild.dependsOn prepare_proto
+ tasks.preBuild.dependsOn("buildTuningForkBinFiles")
+}
+
+task copyTexturesToAssets(type: Copy) {
+ def assetsDir = new File(projectDir, 'src/main/assets/no_asset_packs_textures')
+ from (rootProject.file('install_time_assets/src/main/assets/textures')) {
+ include('*.ktx')
+ }
+ into assetsDir
+ from (rootProject.file('on_demand_assets/src/main/assets/textures')) {
+ include('*.ktx')
+ }
+ into assetsDir
+}
+
+task deleteAssets(type: Delete) {
+ def assetsDir = new File(projectDir, 'src/main/assets/no_asset_packs_textures')
+ delete assetsDir
+}
+
+tasks.whenTaskAdded { task ->
+ if (!PADEnabled.toBoolean()) {
+ // Copy the ETC2 textures into directory
+ // src/main/assets/no_asset_packs_textures/
+ task.dependsOn copyTexturesToAssets
+ } else {
+ // Delete the src/main/assets/no_asset_packs_textures if using asset packs
+ task.dependsOn deleteAssets
+ }
+}
diff --git a/samples/agdktunnel/app/src/main/AndroidManifest.xml b/samples/agdktunnel/app/src/main/AndroidManifest.xml
index d4d9c38..662744c 100644
--- a/samples/agdktunnel/app/src/main/AndroidManifest.xml
+++ b/samples/agdktunnel/app/src/main/AndroidManifest.xml
@@ -15,15 +15,12 @@
limitations under the License.
-->
-<!-- TODO: when building this sample, replace the package name below
- (com.google.example.games.tunnel) by your own package name -->
-
<!-- NOTE: The android:usesCleartextTraffic="true" is included to
be able to communicate with the Tuning Fork Monitor app when
running a debug build. -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.google.sample.agdktunnel">
+ package="com.google.sample.agdktunnel">
<application
android:allowBackup="false"
@@ -31,13 +28,15 @@
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:usesCleartextTraffic="true">
+ <meta-data android:name="com.google.android.gms.games.APP_ID"
+ android:value="@string/game_services_project_id"/>
<activity android:name=".AGDKTunnelActivity"
- android:label="@string/app_name"
- android:configChanges="orientation|keyboardHidden|keyboard|screenSize"
- android:screenOrientation="sensorLandscape"
- android:theme="@style/AppTheme">
+ android:label="@string/app_name"
+ android:configChanges="orientation|keyboardHidden|keyboard|screenSize"
+ android:screenOrientation="sensorLandscape"
+ android:theme="@style/AppTheme">
<meta-data android:name="android.app.lib_name"
- android:value="game" />
+ android:value="agdktunnel" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
diff --git a/samples/agdktunnel/app/src/main/assets/tuningfork/dev_tuningfork.proto b/samples/agdktunnel/app/src/main/assets/tuningfork/dev_tuningfork.proto
index 66b84d4..ad48a35 100644
--- a/samples/agdktunnel/app/src/main/assets/tuningfork/dev_tuningfork.proto
+++ b/samples/agdktunnel/app/src/main/assets/tuningfork/dev_tuningfork.proto
@@ -1,7 +1,31 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
syntax = "proto3";
package com.google.tuningfork;
+/*
+ Protocol buffer defines for our Android Performance Tuner messages.
+ Specific fidelity parameter configurations are defined in:
+ dev_tuningfork_fidelityparams_1.txt
+ dev_tuningfork_fidelityparams_2.txt
+ General settings for Android Performance Tuner initialization are defined in:
+ tuningfork_settings.txt
+*/
+
enum InstrumentKey {
CPU = 0;
GPU = 1;
diff --git a/samples/agdktunnel/app/src/main/cpp/CMakeLists.for-samples-in-archive.txt b/samples/agdktunnel/app/src/main/cpp/CMakeLists.for-samples-in-archive.txt
index 228cfa7..f81af73 100644
--- a/samples/agdktunnel/app/src/main/cpp/CMakeLists.for-samples-in-archive.txt
+++ b/samples/agdktunnel/app/src/main/cpp/CMakeLists.for-samples-in-archive.txt
@@ -20,6 +20,7 @@
find_package(game-activity REQUIRED CONFIG)
find_package(games-controller REQUIRED CONFIG)
find_package(games-frame-pacing REQUIRED CONFIG)
+find_package(games-memory-advice REQUIRED CONFIG)
find_package(games-performance-tuner REQUIRED CONFIG)
# Set the base dir
@@ -30,13 +31,16 @@
include("${GAMESDK_BASE_DIR}/src/protobuf/protobuf.cmake")
# Directory of nano protobuf library source files
include_directories(${PROTOBUF_NANO_SRC_DIR})
-# generate runtime files using protoc
-protobuf_generate_nano_c( ${CMAKE_CURRENT_SOURCE_DIR}/../proto ../proto/dev_tuningfork.proto)
-protobuf_generate_nano_c( ${CMAKE_CURRENT_SOURCE_DIR}/../proto ../proto/tuningfork.proto)
-include_directories(${PROTO_GENS_DIR})
+if("${USE_APT}" STREQUAL "true")
+ # generate runtime files using protoc
+ protobuf_generate_nano_c( ${CMAKE_CURRENT_SOURCE_DIR}/../proto ../proto/dev_tuningfork.proto)
+ protobuf_generate_nano_c( ${CMAKE_CURRENT_SOURCE_DIR}/../proto ../proto/tuningfork.proto)
+ include_directories(${PROTO_GENS_DIR})
+endif()
# common include directory for all samples
set(COMMON_INCLUDE_DIR "../../../../../common/include")
+set(COMMON_SRC_DIR "../../../../../common/src")
# Export GameActivity_onCreate(),
# Refer to: https://github.com/android-ndk/ndk/issues/381.
@@ -52,15 +56,26 @@
add_definitions("-DNO_ASSET_PACKS")
endif()
+if("${USE_APT}" STREQUAL "true")
+ add_definitions("-DUSE_APT")
+endif()
+
set(THIRD_PARTY_DIR ../../../../third-party)
# Import the CMakeLists.txt for the glm library
add_subdirectory(${THIRD_PARTY_DIR}/glm ${CMAKE_CURRENT_BINARY_DIR}/glm)
+if("${USE_APT}" STREQUAL "true")
+ set(PROTOGEN_SRCS
+ ${PROTO_GENS_DIR}/nano/dev_tuningfork.pb.c
+ ${PROTO_GENS_DIR}/nano/tuningfork.pb.c)
+else()
+ set(PROTOGEN_SRCS "")
+endif()
+
# now build app's shared lib
-add_library(game SHARED
+add_library(${CMAKE_PROJECT_NAME} SHARED
${PROTOBUF_NANO_SRCS}
- ${PROTO_GENS_DIR}/nano/dev_tuningfork.pb.c
- ${PROTO_GENS_DIR}/nano/tuningfork.pb.c
+ ${PROTOGEN_SRCS}
android_main.cpp
anim.cpp
ascii_to_geom.cpp
@@ -74,6 +89,8 @@
jni_util.cpp
loader_scene.cpp
loading_thread.cpp
+ memory_consumer.cpp
+ data_loader_machine.cpp
native_app_glue_included.c
native_engine.cpp
obstacle.cpp
@@ -94,16 +111,17 @@
util.cpp
vertexbuf.cpp
welcome_scene.cpp
+ ${COMMON_SRC_DIR}/Versions.cpp
)
-target_include_directories(game PRIVATE
+target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE
${THIRD_PARTY_DIR}/glm/glm
${COMMON_INCLUDE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/data)
# add lib dependencies
-target_link_libraries(game
+target_link_libraries(${CMAKE_PROJECT_NAME}
android
atomic
EGL
@@ -112,6 +130,7 @@
games-controller::paddleboat_static
games-frame-pacing::swappy_static
games-performance-tuner::tuningfork_static
+ games-memory-advice::memory_advice
GLESv3
glm
log)
diff --git a/samples/agdktunnel/app/src/main/cpp/CMakeLists.txt b/samples/agdktunnel/app/src/main/cpp/CMakeLists.txt
index a912720..cd4ad7c 100644
--- a/samples/agdktunnel/app/src/main/cpp/CMakeLists.txt
+++ b/samples/agdktunnel/app/src/main/cpp/CMakeLists.txt
@@ -20,6 +20,7 @@
find_package(game-activity REQUIRED CONFIG)
find_package(games-controller REQUIRED CONFIG)
find_package(games-frame-pacing REQUIRED CONFIG)
+find_package(games-memory-advice REQUIRED CONFIG)
find_package(games-performance-tuner REQUIRED CONFIG)
# Set the base dir
@@ -27,13 +28,15 @@
set(PROTOBUF_NANO_SRC_DIR "${GAMESDK_BASE_DIR}/../external/nanopb-c")
# Include the protobuf utility file from the Android Game SDK
-include("${GAMESDK_BASE_DIR}/src/protobuf/protobuf.cmake")
+include("${GAMESDK_BASE_DIR}/games-performance-tuner/protobuf/protobuf.cmake")
# Directory of nano protobuf library source files
include_directories(${PROTOBUF_NANO_SRC_DIR})
-# generate runtime files using protoc
-protobuf_generate_nano_c( ${CMAKE_CURRENT_SOURCE_DIR}/../proto ../proto/dev_tuningfork.proto)
-protobuf_generate_nano_c( ${CMAKE_CURRENT_SOURCE_DIR}/../proto ../proto/tuningfork.proto)
-include_directories(${PROTO_GENS_DIR})
+if("${USE_APT}" STREQUAL "true")
+ # generate runtime files using protoc
+ protobuf_generate_nano_c( ${CMAKE_CURRENT_SOURCE_DIR}/../proto ../proto/dev_tuningfork.proto)
+ protobuf_generate_nano_c( ${CMAKE_CURRENT_SOURCE_DIR}/../proto ../proto/tuningfork.proto)
+ include_directories(${PROTO_GENS_DIR})
+endif()
# common include directory for all samples
set(COMMON_INCLUDE_DIR "../../../../../common/include")
@@ -45,37 +48,43 @@
"${CMAKE_SHARED_LINKER_FLAGS} -u GameActivity_onCreate")
# Set common compiler options
-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -Werror")
-set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Werror")
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -Werror -Wextra")
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Werror -Wextra")
add_definitions("-DGLM_FORCE_SIZE_T_LENGTH -DGLM_FORCE_RADIANS")
-if("${USE_ASSET_PACKS}" STREQUAL "false")
- add_definitions("-DNO_ASSET_PACKS")
+if("${USE_APT}" STREQUAL "true")
+ add_definitions("-DUSE_APT")
endif()
set(THIRD_PARTY_DIR ../../../../third-party)
# Import the CMakeLists.txt for the glm library
add_subdirectory(${THIRD_PARTY_DIR}/glm ${CMAKE_CURRENT_BINARY_DIR}/glm)
+if("${USE_APT}" STREQUAL "true")
+ set(PROTOGEN_SRCS
+ ${PROTO_GENS_DIR}/nano/dev_tuningfork.pb.c
+ ${PROTO_GENS_DIR}/nano/tuningfork.pb.c)
+else()
+ set(PROTOGEN_SRCS "")
+endif()
+
# now build app's shared lib
-add_library(game SHARED
+add_library(${CMAKE_PROJECT_NAME} SHARED
${PROTOBUF_NANO_SRCS}
- ${PROTO_GENS_DIR}/nano/dev_tuningfork.pb.c
- ${PROTO_GENS_DIR}/nano/tuningfork.pb.c
+ ${PROTOGEN_SRCS}
android_main.cpp
anim.cpp
ascii_to_geom.cpp
dialog_scene.cpp
- game_activity_included.cpp
game_asset_manager.cpp
game_asset_manifest.cpp
- game_text_input_included.cpp
indexbuf.cpp
input_util.cpp
jni_util.cpp
loader_scene.cpp
loading_thread.cpp
- native_app_glue_included.c
+ memory_consumer.cpp
+ data_loader_machine.cpp
native_engine.cpp
obstacle.cpp
obstacle_generator.cpp
@@ -98,22 +107,34 @@
${COMMON_SRC_DIR}/Versions.cpp
)
-target_include_directories(game PRIVATE
+target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE
${THIRD_PARTY_DIR}/glm/glm
${COMMON_INCLUDE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/data)
# add lib dependencies
-target_link_libraries(game
+target_link_libraries(${CMAKE_PROJECT_NAME}
android
atomic
EGL
- game-activity::game-activity
+ game-activity::game-activity_static
oboe::oboe
games-controller::paddleboat_static
games-frame-pacing::swappy_static
games-performance-tuner::tuningfork_static
+ games-memory-advice::memory_advice
GLESv3
glm
log)
+
+if("${USE_ASSET_PACKS}" STREQUAL "false")
+ add_definitions("-DNO_ASSET_PACKS")
+else()
+ # Add a static library called “playcore” built with the c++_static STL.
+ include(${PLAYCORE_LOCATION}/playcore.cmake)
+ add_playcore_static_library()
+ target_include_directories(game PRIVATE ${PLAYCORE_LOCATION}/include)
+
+ target_link_libraries(game playcore)
+endif()
diff --git a/samples/agdktunnel/app/src/main/cpp/android_main.cpp b/samples/agdktunnel/app/src/main/cpp/android_main.cpp
index a4f9622..8f12d06 100644
--- a/samples/agdktunnel/app/src/main/cpp/android_main.cpp
+++ b/samples/agdktunnel/app/src/main/cpp/android_main.cpp
@@ -18,12 +18,16 @@
#include "native_engine.hpp"
extern "C" {
-void android_main(struct android_app *state);
+ void android_main(struct android_app *app);
};
+/*
+ android_main (not main) is our game entry function, it is called from
+ the native app glue utility code as part of the onCreate handler.
+*/
+
void android_main(struct android_app *app) {
NativeEngine *engine = new NativeEngine(app);
engine->GameLoop();
delete engine;
}
-
diff --git a/samples/agdktunnel/app/src/main/cpp/anim.cpp b/samples/agdktunnel/app/src/main/cpp/anim.cpp
index abb938b..114f688 100644
--- a/samples/agdktunnel/app/src/main/cpp/anim.cpp
+++ b/samples/agdktunnel/app/src/main/cpp/anim.cpp
@@ -49,4 +49,3 @@
}
}
}
-
diff --git a/samples/agdktunnel/app/src/main/cpp/anim.hpp b/samples/agdktunnel/app/src/main/cpp/anim.hpp
index f4df7cd..5218ff9 100644
--- a/samples/agdktunnel/app/src/main/cpp/anim.hpp
+++ b/samples/agdktunnel/app/src/main/cpp/anim.hpp
@@ -24,4 +24,3 @@
void RenderBackgroundAnimation(ShapeRenderer *r);
#endif
-
diff --git a/samples/agdktunnel/app/src/main/cpp/ascii_to_geom.cpp b/samples/agdktunnel/app/src/main/cpp/ascii_to_geom.cpp
index af6ff4a..20f1def 100644
--- a/samples/agdktunnel/app/src/main/cpp/ascii_to_geom.cpp
+++ b/samples/agdktunnel/app/src/main/cpp/ascii_to_geom.cpp
@@ -17,12 +17,11 @@
#include "ascii_to_geom.hpp"
#define GEOM_DEBUG ALOGI
-//#define GEOM_DEBUG
SimpleGeom *AsciiArtToGeom(const char *art, float scale) {
// figure out width and height
ALOGI("Creating geometry from ASCII art.");
- GEOM_DEBUG("Ascii art source:\n%s", art);
+ GEOM_DEBUG("Ascii art source:\n%s", art);
int rows = 1;
int curCols = 0, cols = 0;
int r, c;
@@ -37,8 +36,8 @@
}
}
- GEOM_DEBUG("Ascii art has %d rows, %d cols.", rows, cols);
- GEOM_DEBUG("Making working array.");
+ GEOM_DEBUG("Ascii art has %d rows, %d cols.", rows, cols);
+ GEOM_DEBUG("Making working array.");
// allocate a rows x cols array that we will use as working space
unsigned int **v = new unsigned int *[rows];
@@ -59,7 +58,7 @@
}
}
- GEOM_DEBUG("Removing redundant line markers.");
+ GEOM_DEBUG("Removing redundant line markers.");
// remove redundant line markers
for (r = 0; r < rows; r++) {
@@ -92,7 +91,7 @@
}
}
- GEOM_DEBUG("Total vertices: %d, total indices %d", vertices, indices);
+ GEOM_DEBUG("Total vertices: %d, total indices %d", vertices, indices);
// allocate arrays for the vertices and lines
const int VERTICES_STRIDE = sizeof(GLfloat) * 7;
@@ -114,7 +113,7 @@
for (c = 0; c < cols; c++) {
unsigned t = v[r][c];
if (t == '+') {
- GEOM_DEBUG("Found vertex at %d,%d, index %d", r, c, vertices);
+ GEOM_DEBUG("Found vertex at %d,%d, index %d", r, c, vertices);
verticesArray[vertices * 7] = left + c * scale;
verticesArray[vertices * 7 + 1] = top - r * scale;
verticesArray[vertices * 7 + 2] = 0.0f; // z coord is always 0
@@ -133,25 +132,25 @@
int col_dir, row_dir;
int start_c, start_r, end_c, end_r;
- GEOM_DEBUG("Now processing lines.");
+ GEOM_DEBUG("Now processing lines.");
for (r = 0; r < rows; r++) {
for (c = 0; c < cols; c++) {
int t = v[r][c];
if (t == '-') {
// horizontal line
- GEOM_DEBUG("Horizontal line found at %d,%d", r, c);
+ GEOM_DEBUG("Horizontal line found at %d,%d", r, c);
col_dir = -1, row_dir = 0;
} else if (t == '|') {
// vertical line
- GEOM_DEBUG("Vertical line found at %d,%d", r, c);
+ GEOM_DEBUG("Vertical line found at %d,%d", r, c);
col_dir = 0, row_dir = -1;
} else if (t == '`') {
// horizontal line, slanting down
- GEOM_DEBUG("Downward diagonal line found at %d,%d", r, c);
+ GEOM_DEBUG("Downward diagonal line found at %d,%d", r, c);
col_dir = -1, row_dir = -1;
} else if (t == '/') {
// horizontal line, slanting down
- GEOM_DEBUG("Upward diagonal line found at %d,%d", r, c);
+ GEOM_DEBUG("Upward diagonal line found at %d,%d", r, c);
col_dir = -1, row_dir = 1;
} else {
continue;
@@ -168,8 +167,8 @@
ABORT_GAME;
}
}
- GEOM_DEBUG("Start vertex is at %d,%d, index %d", start_r, start_c,
- v[start_r][start_c] & VERTEX_INDEX_MASK);
+ GEOM_DEBUG("Start vertex is at %d,%d, index %d", start_r, start_c,
+ v[start_r][start_c] & VERTEX_INDEX_MASK);
// look for the vertex that ends the line
end_c = c;
@@ -183,17 +182,17 @@
}
}
- GEOM_DEBUG("End vertex is at %d,%d, index %d", end_r, end_c,
- v[end_r][end_c] & VERTEX_INDEX_MASK);
+ GEOM_DEBUG("End vertex is at %d,%d, index %d", end_r, end_c,
+ v[end_r][end_c] & VERTEX_INDEX_MASK);
indicesArray[indices] = static_cast<GLushort>(v[start_r][start_c] & VERTEX_INDEX_MASK);
indicesArray[indices + 1] = static_cast<GLushort>(v[end_r][end_c] & VERTEX_INDEX_MASK);
indices += 2;
- GEOM_DEBUG("We now have %d indices.", indices);
+ GEOM_DEBUG("We now have %d indices.", indices);
}
}
- GEOM_DEBUG("Deallocating working space.");
+ GEOM_DEBUG("Deallocating working space.");
// get rid of the working arrays
for (r = 0; r < rows; r++) {
delete v[r];
@@ -201,21 +200,21 @@
delete[] v;
for (int i = 0; i < indices; i++) {
- GEOM_DEBUG("indices[%d] = %d\n", i, indicesArray[i]);
+ GEOM_DEBUG("indices[%d] = %d\n", i, indicesArray[i]);
}
for (int i = 0; i < vertices; i++) {
- GEOM_DEBUG("vertices[%d]", i * 7);
+ GEOM_DEBUG("vertices[%d]", i * 7);
for (int j = 0; j < 7; j++) {
- GEOM_DEBUG("vertices[%d+%d=%d] = %f\n", i * 7, j, i * 7 + j,
- verticesArray[i * 7 + j]);
+ GEOM_DEBUG("vertices[%d+%d=%d] = %f\n", i * 7, j, i * 7 + j,
+ verticesArray[i * 7 + j]);
}
}
// create the buffers
- GEOM_DEBUG("Creating output VBO (%d vertices) and IBO (%d indices).", vertices,
- indices);
+ GEOM_DEBUG("Creating output VBO (%d vertices) and IBO (%d indices).", vertices,
+ indices);
SimpleGeom *out = new SimpleGeom(new VertexBuf(verticesArray, vertices * sizeof(GLfloat) *
- VERTICES_STRIDE, VERTICES_STRIDE),
+ VERTICES_STRIDE, VERTICES_STRIDE),
new IndexBuf(indicesArray, indices *
sizeof(GLushort)));
out->vbuf->SetPrimitive(GL_LINES); // draw as lines
@@ -231,4 +230,3 @@
return out;
}
-
diff --git a/samples/agdktunnel/app/src/main/cpp/ascii_to_geom.hpp b/samples/agdktunnel/app/src/main/cpp/ascii_to_geom.hpp
index d5c4b2e..ee6984d 100644
--- a/samples/agdktunnel/app/src/main/cpp/ascii_to_geom.hpp
+++ b/samples/agdktunnel/app/src/main/cpp/ascii_to_geom.hpp
@@ -40,4 +40,3 @@
SimpleGeom *AsciiArtToGeom(const char *art, float scale);
#endif
-
diff --git a/samples/agdktunnel/app/src/main/cpp/common.hpp b/samples/agdktunnel/app/src/main/cpp/common.hpp
index 8d3c202..87f709b 100644
--- a/samples/agdktunnel/app/src/main/cpp/common.hpp
+++ b/samples/agdktunnel/app/src/main/cpp/common.hpp
@@ -32,16 +32,17 @@
#include "glm/gtc/matrix_transform.hpp"
#define LOG_TAG "AGDKTunnel"
+
#include "Log.h"
+
#define ABORT_GAME { ALOGE("*** GAME ABORTING."); *((volatile char*)0) = 'a'; }
#define DEBUG_BLIP ALOGI("[ BLIP ]: %s:%d", __FILE__, __LINE__)
#define MY_ASSERT(cond) { if (!(cond)) { ALOGE("ASSERTION FAILED: %s", #cond); \
ABORT_GAME; } }
-#define BUFFER_OFFSET(i) ((char*)NULL + (i))
+#define BUFFER_OFFSET(i) ((void*)((size_t)i))
#include "our_key_codes.hpp"
#endif
-
diff --git a/samples/agdktunnel/app/src/main/cpp/data/strings.inl b/samples/agdktunnel/app/src/main/cpp/data/strings.inl
index f142194..c5b166c 100644
--- a/samples/agdktunnel/app/src/main/cpp/data/strings.inl
+++ b/samples/agdktunnel/app/src/main/cpp/data/strings.inl
@@ -30,6 +30,7 @@
#define S_QUIT "Quit"
#define S_START_OVER "Start Over"
#define S_RESUME "Start from checkpoint"
+#define S_RESUME_CLOUD "Resume from cloud"
#define S_TITLE "AGDKTunnel"
#define S_PLAY "Play!"
@@ -40,6 +41,7 @@
#define S_STORY "Story"
#define S_ABOUT "About"
#define S_TEST "Run tests"
+#define S_MEMORY "Consume Memory"
#define S_OK "OK"
diff --git a/samples/agdktunnel/app/src/main/cpp/data_loader_machine.cpp b/samples/agdktunnel/app/src/main/cpp/data_loader_machine.cpp
new file mode 100644
index 0000000..94de049
--- /dev/null
+++ b/samples/agdktunnel/app/src/main/cpp/data_loader_machine.cpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "data_loader_machine.hpp"
+
+DataLoaderStateMachine::DataLoaderStateMachine(bool isCloudSaveEnabled, char *savePath) {
+ mIsCloudSaveEnabled = isCloudSaveEnabled;
+ int len = strlen(savePath) + strlen(SAVE_FILE_NAME) + 2;
+ mSaveFileName = new char[len];
+ strcpy(mSaveFileName, savePath);
+ strcat(mSaveFileName, "/");
+ strcat(mSaveFileName, SAVE_FILE_NAME);
+ mLevelLoaded = 0;
+ mCurrentState = LOAD_NOT_STARTED;
+}
+
+DataLoaderStateMachine::~DataLoaderStateMachine() {
+ delete mSaveFileName;
+}
+
+int DataLoaderStateMachine::getTotalSteps() {
+ return DATA_LOADED;
+}
+
+int DataLoaderStateMachine::getStepsCompleted() {
+ return mCurrentState;
+}
+
+bool DataLoaderStateMachine::isLoadingDataCompleted() {
+ // At the beginning, we assume that the level 0 has been loaded
+ return mCurrentState == DATA_LOADED || mCurrentState == LOAD_NOT_STARTED;
+}
+
+int DataLoaderStateMachine::getLevelLoaded() {
+ return mLevelLoaded;
+}
+
+void DataLoaderStateMachine::init() {
+ MY_ASSERT(mCurrentState == LOAD_NOT_STARTED || mCurrentState == DATA_LOADED);
+ ALOGI("Loading data initialized.");
+ mCurrentState = DATA_INITIALIZED;
+}
+
+void DataLoaderStateMachine::authenticationCompleted() {
+ MY_ASSERT(mCurrentState == CLOUD_USER_AUTHENTICATED - 1);
+ ALOGI("Cloud authentication completed, searching saved cloud data.");
+ mCurrentState = CLOUD_USER_AUTHENTICATED;
+}
+
+void DataLoaderStateMachine::savedStateCloudDataFound() {
+ MY_ASSERT(mCurrentState == CLOUD_DATA_FOUND - 1);
+ ALOGI("Cloud saved data found, continuing reading data.");
+ mCurrentState = CLOUD_DATA_FOUND;
+}
+
+void DataLoaderStateMachine::savedStateLoadingCompleted(int level) {
+ MY_ASSERT(mIsCloudSaveEnabled && mCurrentState == DATA_LOADED - 1);
+ ALOGI("Using cloud save data. Level loaded: %d", level);
+ mLevelLoaded = level;
+ mCurrentState = DATA_LOADED;
+}
+
+void DataLoaderStateMachine::authenticationFailed() {
+ ALOGE("Error authenticating the user. Using local data instead.");
+ LoadLocalProgress();
+}
+
+void DataLoaderStateMachine::savedStateSnapshotNotFound() {
+ ALOGI("Cloud saved data not found. Using local data instead.");
+ LoadLocalProgress();
+}
+
+void DataLoaderStateMachine::savedStateLoadingFailed() {
+ ALOGE("Error on loading cloud data. Using local data instead.");
+ LoadLocalProgress();
+}
+
+void DataLoaderStateMachine::LoadLocalProgress() {
+ ALOGI("Attempting to load locally: %s", mSaveFileName);
+ mLevelLoaded = 0;
+ FILE *f = fopen(mSaveFileName, "r");
+ if (f) {
+ ALOGI("Local file found. Loading data.");
+ if (1 != fscanf(f, "v1 %d", &mLevelLoaded)) {
+ ALOGE("Error parsing save file.");
+ mLevelLoaded = 0;
+ } else {
+ ALOGI("Loaded. Level = %d", mLevelLoaded);
+ }
+ fclose(f);
+ } else {
+ ALOGI("Save file not present.");
+ }
+ mCurrentState = DATA_LOADED;
+}
+
+void DataLoaderStateMachine::SaveLocalProgress(int level) {
+ mLevelLoaded = level;
+ ALOGI("Saving progress (level %d) to file: %s", level, mSaveFileName);
+ FILE *f = fopen(mSaveFileName, "w");
+ if (!f) {
+ ALOGE("Error writing to save game file.");
+ return;
+ }
+ fprintf(f, "v1 %d", level);
+ fclose(f);
+ ALOGI("Save file written.");
+}
\ No newline at end of file
diff --git a/samples/agdktunnel/app/src/main/cpp/data_loader_machine.hpp b/samples/agdktunnel/app/src/main/cpp/data_loader_machine.hpp
new file mode 100644
index 0000000..59ef737
--- /dev/null
+++ b/samples/agdktunnel/app/src/main/cpp/data_loader_machine.hpp
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef agdktunnel_data_loader_machine_hpp
+#define agdktunnel_data_loader_machine_hpp
+
+#include "common.hpp"
+
+// save file name
+#define SAVE_FILE_NAME "tunnel.dat"
+
+// checkpoint (save progress) every how many levels?
+#define LEVELS_PER_CHECKPOINT 4
+
+class DataLoaderStateMachine {
+private:
+
+ enum DataLoadStates {
+ // loading has not been initialized
+ LOAD_NOT_STARTED,
+ // a new loading work has been initialized and is in progress
+ DATA_INITIALIZED,
+ // the user has been authenticated to Google Play Games Services
+ CLOUD_USER_AUTHENTICATED,
+ // the saved data has been found
+ CLOUD_DATA_FOUND,
+ // the loading data work has finished
+ DATA_LOADED
+ };
+
+ // pointer to the current status, initialized in LOAD_NOT_STARTED
+ DataLoadStates mCurrentState;
+
+ // level to start from in play scene
+ int mLevelLoaded;
+
+ // name of the save file
+ char *mSaveFileName;
+
+ // flag to know if cloud save is enabled
+ bool mIsCloudSaveEnabled;
+
+public:
+
+ DataLoaderStateMachine(bool isCloudSaveEnabled, char *savePath);
+
+ ~DataLoaderStateMachine();
+
+ // starts loading work by moving LOAD_NOT_STARTED or DATA_LOADED to
+ // DATA_INITIALIZED
+ void init();
+
+ // moves state DATA_INITIALIZED to CLOUD_USER_AUTHENTICATED
+ void authenticationCompleted();
+
+ // moves state CLOUD_USER_AUTHENTICATED to CLOUD_DATA_FOUND
+ void savedStateCloudDataFound();
+
+ // finishes loading work by setting the level found on cloud and
+ // moves state CLOUD_DATA_FOUND to DATA_LOADED
+ void savedStateLoadingCompleted(int level);
+
+ // finishes loading work when the user can't be authenticated, instead
+ // loads local data and moves state DATA_INITIALIZED to DATA_LOADED
+ void authenticationFailed();
+
+ // finishes loading work when cloud data can't be found, instead loads
+ // local data and moves state CLOUD_USER_AUTHENTICATED to DATA_LOADED
+ void savedStateSnapshotNotFound();
+
+ // finishes loading work when an error loading data occurs, instead
+ // loads local data and moves DATA_LOADED to DATA_LOADED
+ void savedStateLoadingFailed();
+
+ // retrieve the level loaded after a loading operation
+ int getLevelLoaded();
+
+ // query for the total steps to get done to finish with the load operation
+ int getTotalSteps();
+
+ // query for the steps completed of the current load operation
+ int getStepsCompleted();
+
+ // asks if a loading operation is in progress
+ bool isLoadingDataCompleted();
+
+ // load progress saved in internal storage and moves state to DATA_LOADED
+ void LoadLocalProgress();
+
+ // save progress to internal storage
+ void SaveLocalProgress(int level);
+
+};
+
+#endif //agdktunnel_data_loader_machine_hpp
diff --git a/samples/agdktunnel/app/src/main/cpp/dialog_scene.cpp b/samples/agdktunnel/app/src/main/cpp/dialog_scene.cpp
index bb169af..4d11cde 100644
--- a/samples/agdktunnel/app/src/main/cpp/dialog_scene.cpp
+++ b/samples/agdktunnel/app/src/main/cpp/dialog_scene.cpp
@@ -115,7 +115,7 @@
bool DialogScene::OnBackKeyPressed() {
SceneManager *mgr = SceneManager::GetInstance();
- mgr->RequestNewScene(new WelcomeScene());
+ mgr->RequestNewScene(new LoaderScene());
return true;
}
@@ -133,23 +133,22 @@
switch (action) {
case ACTION_RETURN:
- mgr->RequestNewScene(new WelcomeScene());
+ mgr->RequestNewScene(new LoaderScene());
break;
case ACTION_SIGN_IN:
// note: we can't start playing directly because PlayScene expects the cloud
// results to be ready when it constructs itself; therefore, WelcomeScene
// has to make sure of that. So we can't jump directly to PlayScene from here.
- mgr->RequestNewScene(new WelcomeScene());
+ mgr->RequestNewScene(new LoaderScene());
break;
case ACTION_PLAY_WITHOUT_SIGNIN:
- mgr->RequestNewScene(new PlayScene());
+ mgr->RequestNewScene(new PlayScene(/*savedCheckpoint=*/ 0));
break;
case ACTION_SIGN_OUT:
- mgr->RequestNewScene(new WelcomeScene());
+ mgr->RequestNewScene(new LoaderScene());
break;
default:
// do nothing.
break;
}
}
-
diff --git a/samples/agdktunnel/app/src/main/cpp/dialog_scene.hpp b/samples/agdktunnel/app/src/main/cpp/dialog_scene.hpp
index 3245c11..79fa4e3 100644
--- a/samples/agdktunnel/app/src/main/cpp/dialog_scene.hpp
+++ b/samples/agdktunnel/app/src/main/cpp/dialog_scene.hpp
@@ -19,7 +19,7 @@
#include "engine.hpp"
#include "ui_scene.hpp"
-#include "welcome_scene.hpp"
+#include "loader_scene.hpp"
/* Dialog Scene. Shows a message and buttons. When a button is clicked, performs
* a given action. */
@@ -94,4 +94,3 @@
};
#endif
-
diff --git a/samples/agdktunnel/app/src/main/cpp/game_asset_manager.cpp b/samples/agdktunnel/app/src/main/cpp/game_asset_manager.cpp
index ac88d10..85982df 100644
--- a/samples/agdktunnel/app/src/main/cpp/game_asset_manager.cpp
+++ b/samples/agdktunnel/app/src/main/cpp/game_asset_manager.cpp
@@ -22,6 +22,7 @@
#include "common.hpp"
#include "game_asset_manager.hpp"
#include "game_asset_manifest.hpp"
+
#if !defined(NO_ASSET_PACKS)
#include "play/asset_pack.h"
#endif
@@ -116,7 +117,7 @@
bool mAssetPackManagerInitialized;
};
-GameAssetManagerInternals::GameAssetManagerInternals(AAssetManager *assetManager, JavaVM *jvm,
+GameAssetManagerInternals::GameAssetManagerInternals(AAssetManager *assetManager, JavaVM */*jvm*/,
jobject nativeActivity) {
#if defined(NO_ASSET_PACKS)
mAssetPackErrorMessage = "No Error";
@@ -216,6 +217,8 @@
if (asset != NULL) {
assetSize = AAsset_getLength(asset);
AAsset_close(asset);
+ } else {
+ ALOGI("GameAssetManager: asset %s found to be NULL", assetName);
}
return assetSize;
}
@@ -323,7 +326,7 @@
bool GameAssetManagerInternals::GenerateFullAssetPath(const char *assetName,
const AssetPackInfo *packInfo,
- char *pathBuffer, const size_t bufferSize) {
+ char *pathBuffer, const size_t /*bufferSize*/) {
bool generatedPath = false;
const size_t requiredSize = strlen(assetName) + strlen(packInfo->mAssetPackBasePath) + 1;
if (requiredSize < MAX_ASSET_PATH_LENGTH) {
@@ -361,10 +364,10 @@
if (success) {
ChangeAssetPackStatus(GetAssetPackByName(assetPackName),
- GameAssetManager::GAMEASSET_DOWNLOADING);
+ GameAssetManager::GAMEASSET_DOWNLOADING);
} else {
SetAssetPackErrorStatus(assetPackErrorCode, GetAssetPackByName(assetPackName),
- "GameAssetManager: requestDownload");
+ "GameAssetManager: requestDownload");
}
return success;
}
@@ -795,7 +798,7 @@
}
GameAssetManager::GameAssetPackType GameAssetManager::GetGameAssetPackType(
- const char *assetPackName) {
+ const char */*assetPackName*/) {
GameAssetManager::GameAssetPackType assetPackType = GAMEASSET_PACKTYPE_INTERNAL;
#if !defined NO_ASSET_PACKS
@@ -827,13 +830,13 @@
return downloadStarted;
}
-void GameAssetManager::RequestDownloadCancellation(const char *assetPackName) {
+void GameAssetManager::RequestDownloadCancellation(const char */*assetPackName*/) {
#if !defined(NO_ASSET_PACKS)
mInternals->RequestAssetPackCancelDownload(assetPackName);
#endif
}
-bool GameAssetManager::RequestRemoval(const char *assetPackName) {
+bool GameAssetManager::RequestRemoval(const char */*assetPackName*/) {
#if !defined(NO_ASSET_PACKS)
return mInternals->RequestAssetPackRemoval(assetPackName);
#else
diff --git a/samples/agdktunnel/app/src/main/cpp/game_asset_manager.hpp b/samples/agdktunnel/app/src/main/cpp/game_asset_manager.hpp
index ac7f486..d6ed00b 100644
--- a/samples/agdktunnel/app/src/main/cpp/game_asset_manager.hpp
+++ b/samples/agdktunnel/app/src/main/cpp/game_asset_manager.hpp
@@ -143,4 +143,4 @@
GameAssetManagerInternals *mInternals;
};
-#endif
\ No newline at end of file
+#endif
diff --git a/samples/agdktunnel/app/src/main/cpp/game_asset_manifest.cpp b/samples/agdktunnel/app/src/main/cpp/game_asset_manifest.cpp
index 8a7d444..2998d50 100644
--- a/samples/agdktunnel/app/src/main/cpp/game_asset_manifest.cpp
+++ b/samples/agdktunnel/app/src/main/cpp/game_asset_manifest.cpp
@@ -25,12 +25,21 @@
* This is a rudimentary 'asset manifest' embedded in the source code for simplicity.
* A real world project might generate a manifest file as part of the asset pipeline.
*/
-
- const char *InstallFileList[] = {"textures/wall1.tex", "textures/wall2.tex"};
- const char *OnDemandFileList[] = {"textures/wall3.tex", "textures/wall4.tex",
- "textures/wall5.tex", "textures/wall6.tex",
- "textures/wall7.tex", "textures/wall8.tex"};
-
+#if defined(NO_ASSET_PACKS)
+ const char *InstallFileList[] = {"no_asset_packs_textures/wall1.ktx",
+ "no_asset_packs_textures/wall2.ktx"};
+ const char *OnDemandFileList[] = {"no_asset_packs_textures/wall3.ktx",
+ "no_asset_packs_textures/wall4.ktx",
+ "no_asset_packs_textures/wall5.ktx",
+ "no_asset_packs_textures/wall6.ktx",
+ "no_asset_packs_textures/wall7.ktx",
+ "no_asset_packs_textures/wall8.ktx"};
+#else // !NO_ASSET_PACKS
+ const char *InstallFileList[] = {"textures/wall1.ktx", "textures/wall2.ktx"};
+ const char *OnDemandFileList[] = {"textures/wall3.ktx", "textures/wall4.ktx",
+ "textures/wall5.ktx", "textures/wall6.ktx",
+ "textures/wall7.ktx", "textures/wall8.ktx"};
+#endif // NO_ASSET_PACKS
const GameAssetManifest::AssetPackDefinition AssetPacks[] = {
{
GameAssetManager::GAMEASSET_PACKTYPE_INTERNAL,
diff --git a/samples/agdktunnel/app/src/main/cpp/game_asset_manifest.hpp b/samples/agdktunnel/app/src/main/cpp/game_asset_manifest.hpp
index dd708ee..e5eddf1 100644
--- a/samples/agdktunnel/app/src/main/cpp/game_asset_manifest.hpp
+++ b/samples/agdktunnel/app/src/main/cpp/game_asset_manifest.hpp
@@ -23,8 +23,8 @@
namespace GameAssetManifest {
- static const char *MAIN_ASSETPACK_NAME = "TextureAssets";
- static const char *EXPANSION_ASSETPACK_NAME = "AdditionalTextureAssets";
+ static const char *MAIN_ASSETPACK_NAME = "install_time_assets";
+ static const char *EXPANSION_ASSETPACK_NAME = "on_demand_assets";
struct AssetPackDefinition {
GameAssetManager::GameAssetPackType mPackType;
@@ -61,4 +61,4 @@
const AssetPackDefinition *AssetManifest_GetAssetPackDefinitions();
}
-#endif
\ No newline at end of file
+#endif
diff --git a/samples/agdktunnel/app/src/main/cpp/game_consts.hpp b/samples/agdktunnel/app/src/main/cpp/game_consts.hpp
index 8b13118..a85fca3 100644
--- a/samples/agdktunnel/app/src/main/cpp/game_consts.hpp
+++ b/samples/agdktunnel/app/src/main/cpp/game_consts.hpp
@@ -17,6 +17,12 @@
#ifndef agdktunnel_game_consts_h
#define agdktunnel_game_consts_h
+// Cheats and test settings
+// #define GHOST_MODE
+// #define GOD_MODE
+// #define TOUCH_INDICATOR_MODE
+// #define SWAPPY_OFF_MODE
+
// Render settings
#define RENDER_FOV 45.0f
#define RENDER_NEAR_CLIP 0.1f
@@ -83,6 +89,10 @@
#define SCORE_POS_Y 0.92f
#define SCORE_FONT_SCALE 0.8f
+// settings for rendering memory stats to the screen
+#define MEMORY_POS_X 0.45f
+#define MEMORY_POS_Y 0.80f
+#define MEMORY_FONT_SCALE 0.4f
// scale of the signs that appear onscreen
#define SIGN_FONT_SCALE 0.9f
@@ -152,11 +162,4 @@
#define MENUITEM_PULSE_AMOUNT 1.1f
#define MENUITEM_PULSE_PERIOD 0.5f
-// save file name
-#define SAVE_FILE_NAME "tunnel.dat"
-
-// checkpoint (save progress) every how many levels?
-#define LEVELS_PER_CHECKPOINT 4
-
#endif
-
diff --git a/samples/agdktunnel/app/src/main/cpp/game_text_input_included.cpp b/samples/agdktunnel/app/src/main/cpp/game_text_input_included.cpp
deleted file mode 100644
index e3629c3..0000000
--- a/samples/agdktunnel/app/src/main/cpp/game_text_input_included.cpp
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "game-text-input/gametextinput.cpp"
diff --git a/samples/agdktunnel/app/src/main/cpp/indexbuf.cpp b/samples/agdktunnel/app/src/main/cpp/indexbuf.cpp
index 2e850d0..42c232b 100644
--- a/samples/agdktunnel/app/src/main/cpp/indexbuf.cpp
+++ b/samples/agdktunnel/app/src/main/cpp/indexbuf.cpp
@@ -37,4 +37,3 @@
void IndexBuf::UnbindBuffer() {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
-
diff --git a/samples/agdktunnel/app/src/main/cpp/indexbuf.hpp b/samples/agdktunnel/app/src/main/cpp/indexbuf.hpp
index 2e8e722..d5a72f9 100644
--- a/samples/agdktunnel/app/src/main/cpp/indexbuf.hpp
+++ b/samples/agdktunnel/app/src/main/cpp/indexbuf.hpp
@@ -38,4 +38,3 @@
};
#endif
-
diff --git a/samples/agdktunnel/app/src/main/cpp/input_util.cpp b/samples/agdktunnel/app/src/main/cpp/input_util.cpp
index 53d949f..57fb10d 100644
--- a/samples/agdktunnel/app/src/main/cpp/input_util.cpp
+++ b/samples/agdktunnel/app/src/main/cpp/input_util.cpp
@@ -77,6 +77,25 @@
return 0;
}
+static bool _cookEventForPointerIndex(GameActivityMotionEvent *motionEvent,
+ CookedEventCallback callback, struct CookedEvent &ev,
+ const uint32_t pointerIndex) {
+ if (pointerIndex < motionEvent->pointerCount) {
+ ev.motionPointerId = motionEvent->pointers[pointerIndex].id;
+ ev.motionX = GameActivityPointerAxes_getX(&motionEvent->pointers[pointerIndex]);
+ ev.motionY = GameActivityPointerAxes_getY(&motionEvent->pointers[pointerIndex]);
+ return callback(&ev);
+ }
+ return false;
+}
+
+bool isMovementKey(const int32_t keyCode) {
+ return keyCode == KEYCODE_W ||
+ keyCode == KEYCODE_A ||
+ keyCode == KEYCODE_S ||
+ keyCode == KEYCODE_D;
+}
+
bool CookGameActivityKeyEvent(GameActivityKeyEvent *keyEvent, CookedEventCallback callback) {
if (keyEvent->keyCode == AKEYCODE_BACK && 0 == keyEvent->action) {
// back key was pressed
@@ -84,51 +103,118 @@
memset(&ev, 0, sizeof(ev));
ev.type = COOKED_EVENT_TYPE_BACK;
return callback(&ev);
- }
- return false;
-}
+ } else if (isMovementKey(keyEvent->keyCode)) {
+ // cook movement key events
+ struct CookedEvent ev;
+ memset(&ev, 0, sizeof(ev));
+ ev.keyCode = keyEvent->keyCode;
-bool
-CookGameActivityMotionEvent(GameActivityMotionEvent *motionEvent, CookedEventCallback callback) {
- if (motionEvent->pointerCount > 0) {
- int action = motionEvent->action;
- int actionMasked = action & AMOTION_EVENT_ACTION_MASK;
- int ptrIndex = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >>
- AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
-
- if (ptrIndex < motionEvent->pointerCount) {
- struct CookedEvent ev;
- memset(&ev, 0, sizeof(ev));
-
- if (actionMasked == AMOTION_EVENT_ACTION_DOWN || actionMasked ==
- AMOTION_EVENT_ACTION_POINTER_DOWN) {
- ev.type = COOKED_EVENT_TYPE_POINTER_DOWN;
- } else if (actionMasked == AMOTION_EVENT_ACTION_UP || actionMasked ==
- AMOTION_EVENT_ACTION_POINTER_UP) {
- ev.type = COOKED_EVENT_TYPE_POINTER_UP;
- } else {
- ev.type = COOKED_EVENT_TYPE_POINTER_MOVE;
- }
-
- ev.motionPointerId = motionEvent->pointers[ptrIndex].id;
- ev.motionIsOnScreen = motionEvent->source == AINPUT_SOURCE_TOUCHSCREEN;
- ev.motionX = GameActivityPointerAxes_getX(&motionEvent->pointers[ptrIndex]);
- ev.motionY = GameActivityPointerAxes_getY(&motionEvent->pointers[ptrIndex]);
-
- if (ev.motionIsOnScreen) {
- // use screen size as the motion range
- ev.motionMinX = 0.0f;
- ev.motionMaxX = SceneManager::GetInstance()->GetScreenWidth();
- ev.motionMinY = 0.0f;
- ev.motionMaxY = SceneManager::GetInstance()->GetScreenHeight();
- }
-
+ if (keyEvent->action == KEY_ACTION_DOWN) {
+ ev.type = COOKED_EVENT_TYPE_KEY_DOWN;
+ return callback(&ev);
+ } else if (keyEvent->action == KEY_ACTION_UP) {
+ ev.type = COOKED_EVENT_TYPE_KEY_UP;
return callback(&ev);
}
}
return false;
}
+/*
+ * Process a GameActivityMotionEvent, creating CookedEvents and passing them
+ * to the provided callback. There are three types of events:
+ *
+ * 1. COOKED_EVENT_TYPE_POINTER_DOWN (triggered by AMOTION_EVENT_ACTION_DOWN and
+ * AMOTION_EVENT_ACTION_POINTER_DOWN)
+ * 2. COOKED_EVENT_TYPE_POINTER_UP (triggered by AMOTION_EVENT_ACTION_UP and
+ * AMOTION_EVENT_ACTION_POINTER_UP)
+ * 3. COOKED_EVENT_TYPE_POINTER_MOVE (triggered by AMOTION_EVENT_ACTION_MOVE)
+ *
+ * AMOTION_EVENT_ACTION_DOWN and AMOTION_EVENT_ACTION_POINTER_UP are sent for
+ * primary (first) pointer events. There is only one set of pointer data in
+ * the GameActivityMotionEvent.pointers array, in index 0.
+ *
+ * AMOTION_EVENT_ACTION_POINTER_DOWN and AMOTION_EVENT_ACTION_POINTER_UP are sent
+ * for secondary (second, third, fourth, etc.) pointer events. The pointer index
+ * into the GameActivityMotionEvent.pointers array is obtained by AND masking
+ * the GameActivityMotionEvent.action field with AMOTION_EVENT_ACTION_POINTER_INDEX_MASK
+ * and then shifting by AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT. This index is *only*
+ * provided on AMOTION_EVENT_ACTION_POINTER_DOWN and AMOTION_EVENT_ACTION_POINTER_UP events.
+ *
+ * AMOTION_EVENT_ACTION_MOVE includes the current coordinates of *all* currently active pointers,
+ * both primary and any secondary pointers. We use the GameActivityMotionEvent.pointerCount
+ * field to loop through and process the GameActivityMotionEvent.pointers array.
+ * Note that AMOTION_EVENT_ACTION_POINTER_INDEX_MASK is not valid for AMOTION_EVENT_ACTION_MOVE
+ * events.
+ */
+bool
+CookGameActivityMotionEvent(GameActivityMotionEvent *motionEvent, CookedEventCallback callback) {
+ bool callbackProcessed = false;
+
+ if (motionEvent->pointerCount > 0) {
+ const int action = motionEvent->action;
+ const int actionMasked = action & AMOTION_EVENT_ACTION_MASK;
+ // Initialize pointerIndex to the max size, we only cook an
+ // event at the end of the function if pointerIndex is set to a valid index range
+ uint32_t pointerIndex = GAMEACTIVITY_MAX_NUM_POINTERS_IN_MOTION_EVENT;
+ struct CookedEvent ev;
+ memset(&ev, 0, sizeof(ev));
+ ev.motionIsOnScreen = motionEvent->source == AINPUT_SOURCE_TOUCHSCREEN;
+ if (ev.motionIsOnScreen) {
+ // use screen size as the motion range
+ ev.motionMinX = 0.0f;
+ ev.motionMaxX = SceneManager::GetInstance()->GetScreenWidth();
+ ev.motionMinY = 0.0f;
+ ev.motionMaxY = SceneManager::GetInstance()->GetScreenHeight();
+ }
+
+ switch (actionMasked) {
+ case AMOTION_EVENT_ACTION_DOWN:
+ pointerIndex = 0;
+ ev.type = COOKED_EVENT_TYPE_POINTER_DOWN;
+ break;
+ case AMOTION_EVENT_ACTION_POINTER_DOWN:
+ pointerIndex = ((action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)
+ >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+ ev.type = COOKED_EVENT_TYPE_POINTER_DOWN;
+ break;
+ case AMOTION_EVENT_ACTION_UP:
+ pointerIndex = 0;
+ ev.type = COOKED_EVENT_TYPE_POINTER_UP;
+ break;
+ case AMOTION_EVENT_ACTION_POINTER_UP:
+ pointerIndex = ((action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)
+ >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+ ev.type = COOKED_EVENT_TYPE_POINTER_UP;
+ break;
+ case AMOTION_EVENT_ACTION_MOVE: {
+ // Move includes all active pointers, so loop and process them here,
+ // we do not set pointerIndex since we are cooking the events in
+ // this loop rather than at the bottom of the function
+ ev.type = COOKED_EVENT_TYPE_POINTER_MOVE;
+ for (uint32_t i = 0; i < motionEvent->pointerCount; ++i) {
+ bool moveProcessed = _cookEventForPointerIndex(motionEvent, callback, ev, i);
+ if (moveProcessed) {
+ // Set if any of the moves was processed by the callback
+ callbackProcessed = true;
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ // Only cook an event if we set the pointerIndex to a valid range, note that
+ // move events cook above in the switch statement.
+ if (pointerIndex != GAMEACTIVITY_MAX_NUM_POINTERS_IN_MOTION_EVENT) {
+ callbackProcessed = _cookEventForPointerIndex(motionEvent, callback,
+ ev, pointerIndex);
+ }
+ }
+ return callbackProcessed;
+}
+
bool CookGameControllerEvent(const int32_t gameControllerIndex, CookedEventCallback callback) {
int addedControllerEvent = 0;
if (gameControllerIndex >= 0) {
@@ -158,6 +244,3 @@
}
return (addedControllerEvent != 0);
}
-
-
-
diff --git a/samples/agdktunnel/app/src/main/cpp/input_util.hpp b/samples/agdktunnel/app/src/main/cpp/input_util.hpp
index 6aa6beb..496264f 100644
--- a/samples/agdktunnel/app/src/main/cpp/input_util.hpp
+++ b/samples/agdktunnel/app/src/main/cpp/input_util.hpp
@@ -30,6 +30,23 @@
#define COOKED_EVENT_TYPE_BACK 6
#define COOKED_EVENT_TYPE_TEXT_INPUT 7
+#define KEY_ACTION_DOWN 0
+#define KEY_ACTION_UP 1
+
+// Keycode for movement
+#define KEYCODE_W 51
+#define KEYCODE_A 29
+#define KEYCODE_S 47
+#define KEYCODE_D 32
+
+#define KEY_CONTROL_VERTICAL_SENSIVITY 20.0f
+#define KEY_CONTROL_HORIZONTAL_SENSIVITY 30.0f
+
+#define UP_MOVEMENT_BIT 1
+#define LEFT_MOVEMENT_BIT 2
+#define DOWN_MOVEMENT_BIT 4
+#define RIGHT_MOVEMENT_BIT 8
+
struct CookedEvent {
int type;
@@ -59,5 +76,6 @@
bool CookGameControllerEvent(const int32_t gameControllerIndex, CookedEventCallback callback);
-#endif
+bool isMovementKey(const int32_t keyCode);
+#endif
diff --git a/samples/agdktunnel/app/src/main/cpp/jni_util.cpp b/samples/agdktunnel/app/src/main/cpp/jni_util.cpp
index 4e62c5d..91431dd 100644
--- a/samples/agdktunnel/app/src/main/cpp/jni_util.cpp
+++ b/samples/agdktunnel/app/src/main/cpp/jni_util.cpp
@@ -18,7 +18,7 @@
#include "jni_util.hpp"
#include "native_engine.hpp"
-static struct JniSetup _jni_setup = {0};
+static struct JniSetup _jni_setup = {0, 0, 0};
struct JniSetup *GetJNISetup() {
if (!_jni_setup.env) {
@@ -28,5 +28,3 @@
}
return &_jni_setup;
}
-
-
diff --git a/samples/agdktunnel/app/src/main/cpp/jni_util.hpp b/samples/agdktunnel/app/src/main/cpp/jni_util.hpp
index f4b0d5b..de60652 100644
--- a/samples/agdktunnel/app/src/main/cpp/jni_util.hpp
+++ b/samples/agdktunnel/app/src/main/cpp/jni_util.hpp
@@ -27,4 +27,3 @@
struct JniSetup *GetJNISetup();
#endif
-
diff --git a/samples/agdktunnel/app/src/main/cpp/joystick-support.hpp b/samples/agdktunnel/app/src/main/cpp/joystick-support.hpp
index 6de1fc2..9d20eb5 100644
--- a/samples/agdktunnel/app/src/main/cpp/joystick-support.hpp
+++ b/samples/agdktunnel/app/src/main/cpp/joystick-support.hpp
@@ -31,4 +31,3 @@
static const int SOURCE_TOUCH_NAVIGATION = 0x00200000;
#endif
-
diff --git a/samples/agdktunnel/app/src/main/cpp/loader_scene.cpp b/samples/agdktunnel/app/src/main/cpp/loader_scene.cpp
index 3150e55..56bfeb6 100644
--- a/samples/agdktunnel/app/src/main/cpp/loader_scene.cpp
+++ b/samples/agdktunnel/app/src/main/cpp/loader_scene.cpp
@@ -35,6 +35,8 @@
int _totalLoadCount = 0;
int _currentLoadIndex = 0;
int _remainingLoadCount = 0;
+ bool _on_demand_assets_installed = false;
+ bool _install_time_assets_installed = false;
struct LoadedTextureData {
LoadedTextureData() : textureSize(0), textureData(NULL), textureName(NULL) {}
@@ -86,26 +88,68 @@
loader->LoadingCallback(message);
}
- void LoadTexturesFromAssetPack(const char *assetPackName) {
+ bool IsAssetPackInstalled(const char *assetPackName) {
+ if (strcmp(assetPackName, GameAssetManifest::EXPANSION_ASSETPACK_NAME) == 0) {
+ return _on_demand_assets_installed;
+ } else if (strcmp(assetPackName, GameAssetManifest::MAIN_ASSETPACK_NAME) == 0) {
+ return _install_time_assets_installed;
+ } else {
+ return false;
+ }
+ }
+
+ void FindTexturesFromAssetPack(const char *assetPackName) {
+ ALOGI("TextureLoader: counting assets of pack %s", assetPackName);
GameAssetManager *gameAssetManager = NativeEngine::GetInstance()->GetGameAssetManager();
int assetPackFileCount = 0;
const char **assetPackFiles = gameAssetManager->GetGameAssetPackFileList(assetPackName,
&assetPackFileCount);
if (assetPackFiles != NULL) {
+ _totalLoadCount += assetPackFileCount;
+ _remainingLoadCount += assetPackFileCount;
+ ALOGI("TextureLoader: found %d assets from pack %s", assetPackFileCount, assetPackName);
+ } else {
+ ALOGI("TextureLoader: could not retrieve the list from pack %s", assetPackName);
+ }
+ }
+
+ void LoadTexturesFromAssetPack(const char *assetPackName) {
+ GameAssetManager *gameAssetManager = NativeEngine::GetInstance()->GetGameAssetManager();
+ int assetPackFileCount = 0;
+ const char **assetPackFiles = gameAssetManager->GetGameAssetPackFileList(assetPackName,
+ &assetPackFileCount);
+ ALOGI("TextureLoader: loading textures from asset pack %s", assetPackName);
+ if (assetPackFiles != NULL) {
for (int i = 0; i < assetPackFileCount; ++i) {
uint64_t fileSize = gameAssetManager->GetGameAssetSize(assetPackFiles[i]);
+ ALOGI("TextureLoader: the size of asset %s is %d",
+ assetPackFiles[i], (int)fileSize);
if (fileSize > 0) {
uint8_t *fileBuffer = static_cast<uint8_t *>(malloc(fileSize));
if (gameAssetManager->LoadGameAssetAsync(assetPackFiles[i], fileSize,
fileBuffer, LoadingCallbackProxy,
this)) {
- ++_totalLoadCount;
- ++_remainingLoadCount;
- ALOGI("Started async load %s", assetPackFiles[i]);
+ ALOGI("TextureLoader: started async load %s", assetPackFiles[i]);
+ } else {
+ ALOGE("TextureLoader: can't load asset %s", assetPackFiles[i]);
+ --_remainingLoadCount;
}
}
}
+ } else {
+ ALOGI("LoaderScene: could not retrieve the list from pack %s", assetPackName);
}
+
+ if (strcmp(assetPackName, GameAssetManifest::EXPANSION_ASSETPACK_NAME) == 0) {
+ _on_demand_assets_installed = true;
+ } else if (strcmp(assetPackName, GameAssetManifest::MAIN_ASSETPACK_NAME) == 0) {
+ _install_time_assets_installed = true;
+ }
+ }
+
+ void InstallTexturesFromAssetPack(const char *assetPackName) {
+ GameAssetManager *gameAssetManager = NativeEngine::GetInstance()->GetGameAssetManager();
+ gameAssetManager->RequestDownload(assetPackName);
}
void CreateTextures() {
@@ -123,13 +167,37 @@
mLoadingWidget = NULL;
mTextBoxId = -1;
mStartTime = 0;
+ mDataStateMachine = NativeEngine::GetInstance()->BeginSavedGameLoad();
}
LoaderScene::~LoaderScene() {
}
void LoaderScene::DoFrame() {
- if (mTextureLoader->NumberRemainingToLoad() == 0) {
+ GameAssetManager *gameAssetManager = NativeEngine::GetInstance()->GetGameAssetManager();
+ if (!mTextureLoader->IsAssetPackInstalled(GameAssetManifest::MAIN_ASSETPACK_NAME) &&
+ gameAssetManager->GetGameAssetPackStatus(GameAssetManifest::MAIN_ASSETPACK_NAME) ==
+ GameAssetManager::GAMEASSET_READY) {
+ ALOGI("LoaderScene: attempting to install asset pack %s",
+ GameAssetManifest::MAIN_ASSETPACK_NAME);
+ mTextureLoader->LoadTexturesFromAssetPack(GameAssetManifest::MAIN_ASSETPACK_NAME);
+ }
+ if (gameAssetManager->GetGameAssetPackStatus(GameAssetManifest::EXPANSION_ASSETPACK_NAME) ==
+ GameAssetManager::GAMEASSET_NEEDS_DOWNLOAD) {
+ ALOGI("LoaderScene: downloading asset pack %s",
+ GameAssetManifest::EXPANSION_ASSETPACK_NAME);
+ mTextureLoader->InstallTexturesFromAssetPack(
+ GameAssetManifest::EXPANSION_ASSETPACK_NAME);
+ } else if (!mTextureLoader->IsAssetPackInstalled(GameAssetManifest::EXPANSION_ASSETPACK_NAME) &&
+ gameAssetManager->GetGameAssetPackStatus(GameAssetManifest::EXPANSION_ASSETPACK_NAME) ==
+ GameAssetManager::GAMEASSET_READY) {
+ ALOGI("LoaderScene: attempting to install asset pack %s",
+ GameAssetManifest::EXPANSION_ASSETPACK_NAME);
+ mTextureLoader->LoadTexturesFromAssetPack(GameAssetManifest::EXPANSION_ASSETPACK_NAME);
+ }
+
+ if (mTextureLoader->NumberRemainingToLoad() == 0 &&
+ mDataStateMachine->isLoadingDataCompleted()) {
mTextureLoader->CreateTextures();
// Inform performance tuner we are done loading
@@ -146,9 +214,12 @@
SceneManager *mgr = SceneManager::GetInstance();
mgr->RequestNewScene(new WelcomeScene());
} else {
- float totalLoad = mTextureLoader->TotalNumberToLoad();
- float completedLoad = mTextureLoader->NumberCompetedLoading();
- int loadingPercentage = static_cast<int>((completedLoad / totalLoad) * 100.0f);
+ float totalLoad = mTextureLoader->TotalNumberToLoad() + DATA_LOAD_DELTA *
+ mDataStateMachine->getTotalSteps();
+ float completedLoad = mTextureLoader->NumberCompetedLoading() + DATA_LOAD_DELTA *
+ mDataStateMachine->getStepsCompleted();
+
+ int loadingPercentage = static_cast<int>(completedLoad * 100 / totalLoad);
char progressString[64];
sprintf(progressString, "%s... %d%%", S_LOADING, loadingPercentage);
mLoadingWidget->SetText(progressString);
@@ -165,15 +236,12 @@
->SetCenter(TEXT_POS);
mTextBoxId = mLoadingWidget->GetId();
+ ALOGI("LoaderScene: starting loading work");
timespec currentTimeSpec;
clock_gettime(CLOCK_MONOTONIC, ¤tTimeSpec);
mStartTime = currentTimeSpec.tv_sec * 1000 + (currentTimeSpec.tv_nsec / 1000000);
- mTextureLoader->LoadTexturesFromAssetPack(GameAssetManifest::MAIN_ASSETPACK_NAME);
- GameAssetManager *gameAssetManager = NativeEngine::GetInstance()->GetGameAssetManager();
- if (gameAssetManager->GetGameAssetPackStatus(GameAssetManifest::EXPANSION_ASSETPACK_NAME) ==
- GameAssetManager::GAMEASSET_READY) {
- mTextureLoader->LoadTexturesFromAssetPack(GameAssetManifest::EXPANSION_ASSETPACK_NAME);
- }
+ mTextureLoader->FindTexturesFromAssetPack(GameAssetManifest::MAIN_ASSETPACK_NAME);
+ mTextureLoader->FindTexturesFromAssetPack(GameAssetManifest::EXPANSION_ASSETPACK_NAME);
}
void LoaderScene::RenderBackground() {
diff --git a/samples/agdktunnel/app/src/main/cpp/loader_scene.hpp b/samples/agdktunnel/app/src/main/cpp/loader_scene.hpp
index 4ab6840..861b77d 100644
--- a/samples/agdktunnel/app/src/main/cpp/loader_scene.hpp
+++ b/samples/agdktunnel/app/src/main/cpp/loader_scene.hpp
@@ -20,6 +20,8 @@
#include "engine.hpp"
#include "ui_scene.hpp"
+#define DATA_LOAD_DELTA 0.5f
+
/* Loader Scene, displays load progress at startup */
class LoaderScene : public UiScene {
private:
@@ -43,6 +45,8 @@
virtual void RenderBackground() override;
+ DataLoaderStateMachine *mDataStateMachine;
+
public:
LoaderScene();
@@ -55,7 +59,7 @@
mLoadingText = text;
return this;
}
+
};
#endif
-
diff --git a/samples/agdktunnel/app/src/main/cpp/memory_consumer.cpp b/samples/agdktunnel/app/src/main/cpp/memory_consumer.cpp
new file mode 100644
index 0000000..79a31f1
--- /dev/null
+++ b/samples/agdktunnel/app/src/main/cpp/memory_consumer.cpp
@@ -0,0 +1,269 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "common.hpp"
+#include "game_consts.hpp"
+#include "memory_consumer.hpp"
+#include "memory_advice/memory_advice.h"
+#include "text_renderer.hpp"
+#include <mutex>
+#include <vector>
+
+// Delay between periodic memory watcher status updates (milliseconds)
+static const uint64_t MEMORY_CALLBACK_PERIOD_MS = (5*1000);
+
+// Text color constants
+static const float RED_TEXT_COLOR[3] = {1.0f, 0.0f, 0.0f};
+static const float YELLOW_TEXT_COLOR[3] = {1.0f, 1.0f, 0.0f};
+static const float GREEN_TEXT_COLOR[3] = {0.0f, 1.0f, 0.0f};
+
+// Strings and colors mapping to the MemoryAdvice_MemoryState enum
+static const char *MEMORYSTATE_STRINGS[4] = {
+ "UNK ", // MEMORYADVICE_STATE_UNKNOWN
+ " OK ", // MEMORYADVICE_STATE_OK
+ "WARN", // MEMORYADVICE_STATE_APPROACHING_LIMIT
+ "CRIT" // MEMORYADVICE_STATE_CRITICAL
+};
+
+static const float *MEMORYSTATE_COLORS[4] = {
+ YELLOW_TEXT_COLOR, // MEMORYADVICE_STATE_UNKNOWN
+ GREEN_TEXT_COLOR, // MEMORYADVICE_STATE_OK
+ YELLOW_TEXT_COLOR, // MEMORYADVICE_STATE_APPROACHING_LIMIT
+ RED_TEXT_COLOR // MEMORYADVICE_STATE_CRITICAL
+};
+
+// Define some buckets of constants to randomly pick from
+// for allocation sizes and delays between allocs
+
+// This constant is assumed to be a power of two during random number selection
+static const size_t BUCKET_COUNT = 8;
+
+static const size_t ALLOC_SIZES[BUCKET_COUNT] = {
+ (64*1024),
+ (128*1024),
+ (256*1024),
+ (384*1024),
+ (512*1024),
+ (768*1024),
+ (1024*1024),
+ (2048*1024)
+};
+
+// Milliseconds to delay before next alloc
+static const uint64_t ALLOC_DELTA_TIME[BUCKET_COUNT] = {
+ 10,
+ 20,
+ 30,
+ 40,
+ 50,
+ 75,
+ 100,
+ 200
+};
+
+// Generate a current timestamp, milliseconds since epoch
+static uint64_t GetAllocationTimestampMS() {
+ const auto timestamp =
+ std::chrono::duration_cast<std::chrono::microseconds>(
+ std::chrono::steady_clock::now().time_since_epoch())
+ .count();
+ uint64_t timestampMS = (static_cast<uint64_t>(timestamp)) / 1000;
+ return timestampMS;
+}
+
+// Very basic structure and class to track pools of memory allocations, only aggregate
+// allocation count and size, and complete drain of all pool allocations are supported
+struct MemoryPoolAllocation {
+ MemoryPoolAllocation(void *ptr, const size_t size) {
+ mPtr = ptr;
+ mSize = size;
+ }
+
+ void *mPtr;
+ size_t mSize;
+};
+
+class MemoryConsumerPool {
+public:
+ MemoryConsumerPool() {
+ mAllocationCount = 0;
+ mLastAllocationTimestamp = 0;
+ mTotalBytesAllocated = 0;
+ mAllocations.reserve(4096);
+ }
+
+ ~MemoryConsumerPool() {
+ Purge();
+ }
+
+ void Purge() {
+ std::lock_guard<std::mutex> poolLock(mPoolMutex);
+ for (auto iter = mAllocations.begin(); iter != mAllocations.end(); ++iter) {
+ free(iter->mPtr);
+ }
+ mAllocations.clear();
+ mTotalBytesAllocated = 0;
+ mAllocationCount = 0;
+ }
+
+ uint64_t GetTotalBytesAllocated() const {
+ return mTotalBytesAllocated;
+ }
+
+ uint64_t GetTotalAllocationCount() const {
+ return mAllocationCount;
+ }
+
+ uint64_t GetLastAllocationTimestamp() const {
+ return mLastAllocationTimestamp;
+ }
+
+ // Ownership of the pointer passed in ptr will
+ // belong to the pool
+ void AddAllocation(void *ptr, const size_t size) {
+ std::lock_guard<std::mutex> poolLock(mPoolMutex);
+ mAllocations.push_back({ptr, size});
+ mTotalBytesAllocated += size;
+ ++mAllocationCount;
+ mLastAllocationTimestamp = GetAllocationTimestampMS();
+ }
+
+private:
+ uint64_t mAllocationCount;
+ uint64_t mLastAllocationTimestamp;
+ size_t mTotalBytesAllocated;
+ std::vector<MemoryPoolAllocation> mAllocations;
+ std::mutex mPoolMutex;
+};
+
+// Callback for Memory Advice
+static void MemoryWatcherCallback(MemoryAdvice_MemoryState state, void *user_data) {
+ MemoryConsumer *consumer = reinterpret_cast<MemoryConsumer *>(user_data);
+ consumer->SetMemoryState(static_cast<int32_t>(state));
+}
+
+MemoryConsumer::MemoryConsumer(bool startAsActive) {
+ mActive = startAsActive;
+ mCriticalMemoryWarningCount = 0;
+ mLastReportedMemoryPercentage = MemoryAdvice_getPercentageAvailableMemory();
+ mLastReportedMemoryState = MemoryAdvice_getMemoryState();
+ mPercentageAvailableCheckTimestamp = GetAllocationTimestampMS();
+ for (int i = 0; i < MEMORY_POOL_COUNT; ++i) {
+ mNextAllocationDeltaTime[i] = 0;
+ mPools[i] = new MemoryConsumerPool();
+ }
+ MemoryAdvice_ErrorCode error = MemoryAdvice_registerWatcher(MEMORY_CALLBACK_PERIOD_MS,
+ MemoryWatcherCallback, this);
+ if (error != MEMORYADVICE_ERROR_OK) {
+ ALOGE("Error registering memory watcher: %d", (int)error);
+ }
+}
+
+MemoryConsumer::~MemoryConsumer() {
+ MemoryAdvice_ErrorCode error = MemoryAdvice_unregisterWatcher(MemoryWatcherCallback);
+ if (error != MEMORYADVICE_ERROR_OK) {
+ ALOGE("Error unregistering memory watcher: %d", (int)error);
+ }
+ for (int i = 0; i < MEMORY_POOL_COUNT; ++i) {
+ delete mPools[i];
+ }
+}
+
+void MemoryConsumer::Update() {
+ if (mActive) {
+ const uint64_t currentTimestamp = GetAllocationTimestampMS();
+ // Only alloc into the general pool until we get a critical memory warning
+ if (mCriticalMemoryWarningCount == 0) {
+ UpdatePoolAllocations(MEMORY_POOL_GENERAL, currentTimestamp);
+ }
+ // Always update the cache pool
+ UpdatePoolAllocations(MEMORY_POOL_CACHE, currentTimestamp);
+
+ // Check if it is time to update the percentage available estimate
+ if (currentTimestamp - mPercentageAvailableCheckTimestamp > MEMORY_CALLBACK_PERIOD_MS) {
+ mPercentageAvailableCheckTimestamp = currentTimestamp;
+ mLastReportedMemoryPercentage = MemoryAdvice_getPercentageAvailableMemory();
+ // Refresh our memory state when we call the percentage check, in case
+ // our memory state was upgraded after a purge
+ SetMemoryState(MemoryAdvice_getMemoryState());
+ }
+ }
+}
+
+// Check if a memory pool is due for another allocation, attempt one if it
+// is and reset its delay timer
+void MemoryConsumer::UpdatePoolAllocations(const MemoryPools memoryPool,
+ const uint64_t currentTimestamp) {
+ const uint64_t deltaTime = currentTimestamp -
+ mPools[memoryPool]->GetLastAllocationTimestamp();
+ if (deltaTime > mNextAllocationDeltaTime[memoryPool]) {
+ // Pick a size using a random index into a predefined bucket
+ // of allocation sizes
+ const int sizeBucketIndex = rand() & (BUCKET_COUNT - 1);
+ const size_t allocSize = ALLOC_SIZES[sizeBucketIndex];
+ void *allocPtr = malloc(allocSize);
+ memset(allocPtr, 0xBA, allocSize);
+ if (allocPtr == NULL) {
+ ALOGE("Failed to alloc %zu bytes for pool %d", allocSize,
+ static_cast<int>(memoryPool));
+ } else {
+ mPools[memoryPool]->AddAllocation(allocPtr, allocSize);
+ }
+ // Pick a delay until the next alloc using a random index into
+ // a predefined bucket of delay intervals
+ const int delayBucketIndex = rand() & (BUCKET_COUNT - 1);
+ mNextAllocationDeltaTime[memoryPool] = ALLOC_DELTA_TIME[delayBucketIndex];
+ }
+}
+
+size_t MemoryConsumer::GetTotalPoolAllocatedBytes(const MemoryPools memoryPool) const {
+ return mPools[memoryPool]->GetTotalBytesAllocated();
+}
+
+void MemoryConsumer::SetMemoryState(const int32_t memoryState) {
+ if (memoryState >= 0 && memoryState <= MEMORYADVICE_STATE_CRITICAL) {
+ if (memoryState != mLastReportedMemoryState) {
+ if (memoryState == MEMORYADVICE_STATE_CRITICAL) {
+ ++mCriticalMemoryWarningCount;
+ // Purge the cache pool on a critical memory warning
+ mPools[MEMORY_POOL_CACHE]->Purge();
+ }
+ mLastReportedMemoryState = memoryState;
+ }
+ }
+}
+
+// Render memory statistics to the screen, using the provided TextRenderer
+void MemoryConsumer::RenderMemoryStatistics(TextRenderer *textRenderer) const {
+ if (mActive) {
+ const size_t generalPoolMB = GetTotalPoolAllocatedBytes(MEMORY_POOL_GENERAL) /
+ (1024 * 1024);
+ const size_t cachePoolMB = GetTotalPoolAllocatedBytes(MEMORY_POOL_CACHE) /
+ (1024 * 1024);
+ const int percentAvailable = static_cast<int>(mLastReportedMemoryPercentage);
+ const char *statusString = MEMORYSTATE_STRINGS[mLastReportedMemoryState];
+ const float *statusColor = MEMORYSTATE_COLORS[mLastReportedMemoryState];
+ char memoryString[64];
+ snprintf(memoryString, 63, "%s (%d) %d%% G: %zu MB C: %zu MB",
+ statusString, mCriticalMemoryWarningCount, percentAvailable,
+ generalPoolMB, cachePoolMB);
+
+ textRenderer->SetColor(statusColor);
+ textRenderer->SetFontScale(MEMORY_FONT_SCALE);
+ textRenderer->RenderText(memoryString, MEMORY_POS_X, MEMORY_POS_Y);
+ textRenderer->ResetColor();
+ }
+}
diff --git a/samples/agdktunnel/app/src/main/cpp/memory_consumer.hpp b/samples/agdktunnel/app/src/main/cpp/memory_consumer.hpp
new file mode 100644
index 0000000..e90d586
--- /dev/null
+++ b/samples/agdktunnel/app/src/main/cpp/memory_consumer.hpp
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef agdktunnel_memory_consumer_hpp
+#define agdktunnel_memory_consumer_hpp
+
+#include <cstdint>
+
+class MemoryConsumerPool;
+class TextRenderer;
+
+class MemoryConsumer
+{
+public:
+ enum MemoryPools
+ {
+ // The general memory pool occasionally allocs until the first
+ // critical memory warning is received
+ MEMORY_POOL_GENERAL = 0,
+ // The cache memory pool occasionally allocs, purging if a critical
+ // memory warning is received
+ MEMORY_POOL_CACHE,
+ MEMORY_POOL_COUNT
+ };
+
+ MemoryConsumer(bool startAsActive);
+
+ ~MemoryConsumer();
+
+ // Set whether continual memory consumption is active or not
+ void SetActive(bool active) { mActive = active; }
+
+ // Called from game frame, periodically makes new allocations if active
+ void Update();
+
+ // Return the number of times memory advice has reported a critical memory warning status
+ uint32_t GetCriticalMemoryWarningCount() const { return mCriticalMemoryWarningCount; }
+
+ // Return the memory state most recently reported by the memory advice library,
+ // this value can be cast to MemoryAdvice_MemoryState
+ int32_t GetLastReportedMemoryState() const { return mLastReportedMemoryState; }
+
+ // Returns total number of bytes allocated in a specified pool
+ size_t GetTotalPoolAllocatedBytes(const MemoryPools memoryPool) const;
+
+ // Update the current memory state (casts to MemoryAdvice_MemoryState)
+ void SetMemoryState(const int32_t memoryState);
+
+ // Render memory statistics to the screen, using the provided TextRenderer
+ void RenderMemoryStatistics(TextRenderer *textRenderer) const;
+
+private:
+ // Check if a pool is due to for a new memory allocation, and make
+ // an allocation if it is.
+ void UpdatePoolAllocations(const MemoryPools memoryPool, const uint64_t currentTimestamp);
+
+ // Whether memory consumption/statistics display is active
+ bool mActive;
+
+ // Number of critical memory warnings received
+ uint32_t mCriticalMemoryWarningCount;
+
+ // Last reported memory state (casts to MemoryAdvice_MemoryState)
+ int32_t mLastReportedMemoryState;
+
+ // Last reported percentage memory available returned by Memory Advice
+ float mLastReportedMemoryPercentage;
+
+ // Previous timestamp, in milliseconds, of the most recent call
+ // of MemoryAdvice_getPercentageAvailableMemory
+ uint64_t mPercentageAvailableCheckTimestamp;
+
+ // Elapsed time in milliseconds between allocations for each memory pool
+ uint64_t mNextAllocationDeltaTime[MemoryConsumer::MEMORY_POOL_COUNT];
+
+ // Memory allocation pools
+ MemoryConsumerPool *mPools[MemoryConsumer::MEMORY_POOL_COUNT];
+};
+
+#endif
diff --git a/samples/agdktunnel/app/src/main/cpp/native_app_glue_included.c b/samples/agdktunnel/app/src/main/cpp/native_app_glue_included.c
deleted file mode 100644
index 6aa43cb..0000000
--- a/samples/agdktunnel/app/src/main/cpp/native_app_glue_included.c
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// Include the GameActivity implementation
-#include "game-activity/native_app_glue/android_native_app_glue.c"
diff --git a/samples/agdktunnel/app/src/main/cpp/native_engine.cpp b/samples/agdktunnel/app/src/main/cpp/native_engine.cpp
index cc35e8d..ff2d24e 100644
--- a/samples/agdktunnel/app/src/main/cpp/native_engine.cpp
+++ b/samples/agdktunnel/app/src/main/cpp/native_engine.cpp
@@ -22,6 +22,7 @@
#include "native_engine.hpp"
#include "game-activity/GameActivity.h"
+#include "memory_advice/memory_advice.h"
#include "paddleboat/paddleboat.h"
#include "swappy/swappyGL.h"
#include "welcome_scene.hpp"
@@ -105,9 +106,24 @@
Paddleboat_init(GetJniEnv(), app->activity->javaGameActivity);
Paddleboat_setControllerStatusCallback(_GameControllerStatusCallback, NULL);
+ const MemoryAdvice_ErrorCode memoryError = MemoryAdvice_init(GetJniEnv(),
+ app->activity->javaGameActivity);
+ if (memoryError != MEMORYADVICE_ERROR_OK) {
+ ALOGE("MemoryAdvice_init failed with error: %d", memoryError);
+ } else {
+ ALOGI("Initialized MemoryAdvice");
+ }
+
+ // Initialize the memory consumer, used to exercise the
+ // Memory Advice library. Off by default.
+ mMemoryConsumer = new MemoryConsumer(false);
+
ALOGI("Calling SwappyGL_init");
SwappyGL_init(GetJniEnv(), mApp->activity->javaGameActivity);
SwappyGL_setSwapIntervalNS(SWAPPY_SWAP_60FPS);
+#ifdef SWAPPY_OFF_MODE
+ SwappyGL_setMaxAutoSwapIntervalNS(0);
+#endif // SWAPPY_OFF_MODE
mTuningManager = new TuningManager(GetJniEnv(), app->activity->javaGameActivity, app->config);
@@ -125,6 +141,33 @@
GameActivity_setImeEditorInfo(app->activity, InputType_dot_TYPE_CLASS_TEXT,
IME_ACTION_NONE, IME_FLAG_NO_FULLSCREEN);
+
+ // Set fields retrieved through JNI
+ // Find the Java class
+ jclass activityClass = GetJniEnv()->GetObjectClass(mApp->activity->javaGameActivity);
+
+ // Field that stores the path to save files to internal storage
+ jmethodID getInternalStoragePathID = GetJniEnv()->GetMethodID(
+ activityClass, "getInternalStoragePath", "()Ljava/lang/String;");
+ jobject jInternalStoragePath = GetJniEnv()->CallObjectMethod(
+ mApp->activity->javaGameActivity, getInternalStoragePathID);
+ jboolean isCopy;
+ const char * str = GetJniEnv()->GetStringUTFChars((jstring)jInternalStoragePath, &isCopy);
+ char *internalStoragePath = new char[strlen(str) + 2];
+ strcpy(internalStoragePath, str);
+
+ if (isCopy == JNI_TRUE) {
+ GetJniEnv()->ReleaseStringUTFChars((jstring)jInternalStoragePath, str);
+ }
+ GetJniEnv()->DeleteLocalRef(jInternalStoragePath);
+
+ // Flag to find if cloud save is enabled
+ jmethodID isPlayGamesServicesLinkedID =
+ GetJniEnv()->GetMethodID(activityClass, "isPlayGamesServicesLinked", "()Z");
+ mCloudSaveEnabled = (bool) GetJniEnv()->CallBooleanMethod(
+ mApp->activity->javaGameActivity, isPlayGamesServicesLinkedID);
+
+ mDataStateMachine = new DataLoaderStateMachine(mCloudSaveEnabled, internalStoragePath);
}
NativeEngine *NativeEngine::GetInstance() {
@@ -150,6 +193,7 @@
mJniEnv = NULL;
}
_singleton = NULL;
+ delete mDataStateMachine;
}
static void _handle_cmd_proxy(struct android_app *app, int32_t cmd) {
@@ -230,6 +274,7 @@
}
}
+ mMemoryConsumer->Update();
mGameAssetManager->UpdateGameAssetManager();
Paddleboat_update(GetJniEnv());
HandleGameActivityInput();
@@ -277,6 +322,101 @@
return mAppJniEnv;
}
+DataLoaderStateMachine *NativeEngine::BeginSavedGameLoad() {
+ if (IsCloudSaveEnabled()) {
+ ALOGI("Scheduling task to load cloud data through JNI");
+ jclass activityClass = GetJniEnv()->GetObjectClass(mApp->activity->javaGameActivity);
+ jmethodID loadCloudCheckpointID =
+ GetJniEnv()->GetMethodID(activityClass, "loadCloudCheckpoint", "()V");
+ GetJniEnv()->CallVoidMethod(mApp->activity->javaGameActivity, loadCloudCheckpointID);
+ } else {
+ mDataStateMachine->LoadLocalProgress();
+ }
+ return mDataStateMachine;
+}
+
+bool NativeEngine::SaveProgress(int level, bool forceSave) {
+ if (!forceSave) {
+ if (level <= mDataStateMachine->getLevelLoaded()) {
+ // nothing to do
+ ALOGI("No need to save level, current = %d, saved = %d",
+ level, mDataStateMachine->getLevelLoaded());
+ return false;
+ } else if (!IsCheckpointLevel(level)) {
+ ALOGI("Current level %d is not a checkpoint level. Nothing to save.", level);
+ return false;
+ }
+ }
+
+ // Save state locally and to the cloud if it is enabled
+ ALOGI("Saving progress to LOCAL FILE: level %d", level);
+ mDataStateMachine->SaveLocalProgress(level);
+ if (IsCloudSaveEnabled()) {
+ ALOGI("Saving progress to the cloud: level %d", level);
+ SaveGameToCloud(level);
+ }
+ return true;
+}
+
+void NativeEngine::SaveGameToCloud(int level) {
+ MY_ASSERT(GetJniEnv() && IsCloudSaveEnabled());
+ ALOGI("Scheduling task to save cloud data through JNI");
+ jclass activityClass = GetJniEnv()->GetObjectClass(mApp->activity->javaGameActivity);
+ jmethodID saveCloudCheckpointID =
+ GetJniEnv()->GetMethodID(activityClass, "saveCloudCheckpoint", "(I)V");
+ GetJniEnv()->CallVoidMethod(
+ mApp->activity->javaGameActivity, saveCloudCheckpointID, (jint)level);
+}
+
+// TODO: rename the methods according to your package name
+extern "C" jboolean Java_com_google_sample_agdktunnel_PGSManager_isLoadingWorkInProgress(
+ JNIEnv */*env*/, jobject /*pgsManager*/) {
+ NativeEngine *instance = NativeEngine::GetInstance();
+ return (jboolean)!instance->GetDataStateMachine()->isLoadingDataCompleted();
+}
+
+extern "C" void Java_com_google_sample_agdktunnel_PGSManager_savedStateInitLoading(
+ JNIEnv */*env*/, jobject /*pgsManager*/) {
+ NativeEngine *instance = NativeEngine::GetInstance();
+ instance->GetDataStateMachine()->init();
+}
+
+extern "C" void Java_com_google_sample_agdktunnel_PGSManager_authenticationCompleted(
+ JNIEnv */*env*/, jobject /*pgsManager*/) {
+ NativeEngine *instance = NativeEngine::GetInstance();
+ instance->GetDataStateMachine()->authenticationCompleted();
+}
+
+extern "C" void Java_com_google_sample_agdktunnel_PGSManager_authenticationFailed(
+ JNIEnv */*env*/, jobject /*pgsManager*/) {
+ NativeEngine *instance = NativeEngine::GetInstance();
+ instance->GetDataStateMachine()->authenticationFailed();
+}
+
+extern "C" void Java_com_google_sample_agdktunnel_PGSManager_savedStateSnapshotNotFound(
+ JNIEnv */*env*/, jobject /*pgsManager*/) {
+ NativeEngine *instance = NativeEngine::GetInstance();
+ instance->GetDataStateMachine()->savedStateSnapshotNotFound();
+}
+
+extern "C" void Java_com_google_sample_agdktunnel_PGSManager_savedStateCloudDataFound(
+ JNIEnv */*env*/, jobject /*pgsManagerl*/) {
+ NativeEngine *instance = NativeEngine::GetInstance();
+ instance->GetDataStateMachine()->savedStateCloudDataFound();
+}
+
+extern "C" void Java_com_google_sample_agdktunnel_PGSManager_savedStateLoadingFailed(
+ JNIEnv */*env*/, jobject /*pgsManager*/) {
+ NativeEngine *instance = NativeEngine::GetInstance();
+ instance->GetDataStateMachine()->savedStateLoadingFailed();
+}
+
+extern "C" void Java_com_google_sample_agdktunnel_PGSManager_savedStateLoadingCompleted(
+ JNIEnv */*env*/, jobject /*pgsManager*/, jint level) {
+ NativeEngine *instance = NativeEngine::GetInstance();
+ instance->GetDataStateMachine()->savedStateLoadingCompleted(level);
+}
+
static char sInsetsTypeName[][32] = {
"CAPTION_BAR",
"DISPLAY_CUTOUT",
@@ -319,7 +459,7 @@
}
}
VLOGD("HandleCommand(%d): hasWindow = %d, hasFocus = %d", cmd,
- mHasWindow ? 1 : 0, mHasFocus ? 1 : 0);
+ mHasWindow ? 1 : 0, mHasFocus ? 1 : 0);
break;
case APP_CMD_TERM_WINDOW:
// The window is going away -- kill the surface
@@ -396,12 +536,12 @@
break;
}
- VLOGD("NativeEngine: STATUS: F%d, V%d, W%d, EGL: D %p, S %p, CTX %p, CFG %p",
- mHasFocus, mIsVisible, mHasWindow, mEglDisplay, mEglSurface, mEglContext,
- mEglConfig);
+ VLOGD("NativeEngine: STATUS: F%d, V%d, W%d, EGL: D %p, S %p, CTX %p, CFG %p",
+ mHasFocus, mIsVisible, mHasWindow, mEglDisplay, mEglSurface, mEglContext,
+ mEglConfig);
}
-bool NativeEngine::HandleInput(AInputEvent *event) {
+bool NativeEngine::HandleInput(AInputEvent */*event*/) {
return false;
}
@@ -444,8 +584,7 @@
cookGameControllerEvent = true;
} else {
// Didn't belong to a game controller, process it ourselves if it is a touch event
- CookGameActivityMotionEvent(motionEvent,
- _cooked_event_callback);
+ CookGameActivityMotionEvent(motionEvent, _cooked_event_callback);
}
}
android_app_clear_motion_events(inputBuffer);
@@ -586,7 +725,7 @@
}
ALOGI("NativeEngine: binding surface and context (display %p, surface %p, context %p)",
- mEglDisplay, mEglSurface, mEglContext);
+ mEglDisplay, mEglSurface, mEglContext);
// bind them
if (EGL_FALSE == eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
@@ -718,7 +857,7 @@
// prepare to render (create context, surfaces, etc, if needed)
if (!PrepareToRender()) {
// not ready
- VLOGD("NativeEngine: preparation to render failed.");
+ VLOGD("NativeEngine: preparation to render failed.");
return;
}
@@ -733,7 +872,7 @@
if (width != mSurfWidth || height != mSurfHeight) {
// notify scene manager that the surface has changed size
ALOGI("NativeEngine: surface changed size %dx%d --> %dx%d", mSurfWidth, mSurfHeight,
- width, height);
+ width, height);
mSurfWidth = width;
mSurfHeight = height;
mgr->SetScreenSize(mSurfWidth, mSurfHeight);
diff --git a/samples/agdktunnel/app/src/main/cpp/native_engine.hpp b/samples/agdktunnel/app/src/main/cpp/native_engine.hpp
index 94d0976..0d90e10 100644
--- a/samples/agdktunnel/app/src/main/cpp/native_engine.hpp
+++ b/samples/agdktunnel/app/src/main/cpp/native_engine.hpp
@@ -19,8 +19,10 @@
#include "common.hpp"
#include "game_asset_manager.hpp"
+#include "memory_consumer.hpp"
#include "texture_manager.hpp"
#include "tuning_manager.hpp"
+#include "data_loader_machine.hpp"
struct NativeEngineSavedState {
bool mHasFocus;
@@ -51,12 +53,26 @@
// returns the tuning manager instance
TuningManager *GetTuningManager() { return mTuningManager; }
+ // returns the memory consumer instance
+ MemoryConsumer *GetMemoryConsumer() { return mMemoryConsumer; }
+
// returns the (singleton) instance
static NativeEngine *GetInstance();
// This is the env for the app thread. It's different to the main thread.
JNIEnv *GetAppJniEnv();
+ // Returns if cloud save is enabled
+ bool IsCloudSaveEnabled() { return mCloudSaveEnabled; }
+
+ // Load data from cloud if it is enabled, or from local data otherwise
+ DataLoaderStateMachine *BeginSavedGameLoad();
+
+ // Saves data to local storage and to cloud if it is enabled
+ bool SaveProgress(int level, bool forceSave = false);
+
+ DataLoaderStateMachine *GetDataStateMachine() { return mDataStateMachine; }
+
private:
// variables to track Android lifecycle:
bool mHasFocus, mIsVisible, mHasWindow;
@@ -103,9 +119,18 @@
// Tuning manager instance
TuningManager *mTuningManager;
+ // Memory consumer instance
+ MemoryConsumer *mMemoryConsumer;
+
// is this the first frame we're drawing?
bool mIsFirstFrame;
+ // is cloud save enabled
+ bool mCloudSaveEnabled;
+
+ // state machine instance to query the status of the current load of data
+ DataLoaderStateMachine *mDataStateMachine;
+
// initialize the display
bool InitDisplay();
@@ -140,6 +165,15 @@
void CheckForNewAxis();
+ // Save the checkpoint level in the cloud
+ void SaveGameToCloud(int level);
+
+ // returns whether or not this level is a "checkpoint level" (that is,
+ // where progress should be saved)
+ bool IsCheckpointLevel(int level) {
+ return 0 == level % LEVELS_PER_CHECKPOINT;
+ }
+
public:
// these are public for simplicity because we have internal static callbacks
void HandleCommand(int32_t cmd);
@@ -152,4 +186,3 @@
};
#endif
-
diff --git a/samples/agdktunnel/app/src/main/cpp/obstacle.cpp b/samples/agdktunnel/app/src/main/cpp/obstacle.cpp
index 16e41e0..64cb1c6 100644
--- a/samples/agdktunnel/app/src/main/cpp/obstacle.cpp
+++ b/samples/agdktunnel/app/src/main/cpp/obstacle.cpp
@@ -63,4 +63,3 @@
}
}
}
-
diff --git a/samples/agdktunnel/app/src/main/cpp/obstacle.hpp b/samples/agdktunnel/app/src/main/cpp/obstacle.hpp
index f52ad28..c5746f0 100644
--- a/samples/agdktunnel/app/src/main/cpp/obstacle.hpp
+++ b/samples/agdktunnel/app/src/main/cpp/obstacle.hpp
@@ -38,7 +38,7 @@
-TUNNEL_HALF_H + (gridRow + 0.5f) * OBS_CELL_SIZE);
}
- glm::vec3 GetBoxSize(int gridCol, int gridRow) {
+ glm::vec3 GetBoxSize(int /*gridCol*/, int /*gridRow*/) {
return glm::vec3(OBS_BOX_SIZE, OBS_BOX_SIZE, OBS_BOX_SIZE);
}
@@ -79,4 +79,3 @@
};
#endif
-
diff --git a/samples/agdktunnel/app/src/main/cpp/obstacle_generator.cpp b/samples/agdktunnel/app/src/main/cpp/obstacle_generator.cpp
index 2916374..261f771 100644
--- a/samples/agdktunnel/app/src/main/cpp/obstacle_generator.cpp
+++ b/samples/agdktunnel/app/src/main/cpp/obstacle_generator.cpp
@@ -182,4 +182,3 @@
break;
}
}
-
diff --git a/samples/agdktunnel/app/src/main/cpp/obstacle_generator.hpp b/samples/agdktunnel/app/src/main/cpp/obstacle_generator.hpp
index 6ec3d29..cb6b6dd 100644
--- a/samples/agdktunnel/app/src/main/cpp/obstacle_generator.hpp
+++ b/samples/agdktunnel/app/src/main/cpp/obstacle_generator.hpp
@@ -51,4 +51,3 @@
};
#endif
-
diff --git a/samples/agdktunnel/app/src/main/cpp/our_key_codes.hpp b/samples/agdktunnel/app/src/main/cpp/our_key_codes.hpp
index 8708431..3f2efce 100644
--- a/samples/agdktunnel/app/src/main/cpp/our_key_codes.hpp
+++ b/samples/agdktunnel/app/src/main/cpp/our_key_codes.hpp
@@ -27,4 +27,3 @@
#define OURKEY_COUNT 6 // how many keycodes there are
#endif
-
diff --git a/samples/agdktunnel/app/src/main/cpp/our_shader.cpp b/samples/agdktunnel/app/src/main/cpp/our_shader.cpp
index 7a38fb0..d8e3eab 100644
--- a/samples/agdktunnel/app/src/main/cpp/our_shader.cpp
+++ b/samples/agdktunnel/app/src/main/cpp/our_shader.cpp
@@ -129,4 +129,3 @@
const char *OurShader::GetShaderName() {
return "OurShader";
}
-
diff --git a/samples/agdktunnel/app/src/main/cpp/our_shader.hpp b/samples/agdktunnel/app/src/main/cpp/our_shader.hpp
index b795354..cbce21d 100644
--- a/samples/agdktunnel/app/src/main/cpp/our_shader.hpp
+++ b/samples/agdktunnel/app/src/main/cpp/our_shader.hpp
@@ -55,4 +55,3 @@
};
#endif
-
diff --git a/samples/agdktunnel/app/src/main/cpp/play_scene.cpp b/samples/agdktunnel/app/src/main/cpp/play_scene.cpp
index 5080a82..b5c538d 100644
--- a/samples/agdktunnel/app/src/main/cpp/play_scene.cpp
+++ b/samples/agdktunnel/app/src/main/cpp/play_scene.cpp
@@ -59,18 +59,23 @@
"d70 f550. f650. f750. f850."
};
-PlayScene::PlayScene() : Scene() {
+PlayScene::PlayScene(int savedLevel) : Scene() {
+ mSavedLevel = (savedLevel / LEVELS_PER_CHECKPOINT) * LEVELS_PER_CHECKPOINT;
+ ALOGI("Normalized check-point: level %d", mSavedLevel);
mOurShader = NULL;
mTrivialShader = NULL;
mTextRenderer = NULL;
mShapeRenderer = NULL;
+#ifdef TOUCH_INDICATOR_MODE
+ mRectRenderer = NULL;
+#endif // TOUCH_INDICATOR_MODE
mShipSteerX = mShipSteerZ = 0.0f;
mFilteredSteerX = mFilteredSteerZ = 0.0f;
+ mMotionKeyBitmask = 0;
mPlayerPos = glm::vec3(0.0f, 0.0f, 0.0f); // center
mPlayerDir = glm::vec3(0.0f, 1.0f, 0.0f); // forward
mDifficulty = 0;
- mUseCloudSave = false;
mCubeGeom = NULL;
mTunnelGeom = NULL;
@@ -87,10 +92,12 @@
}
memset(mMenuItemText, 0, sizeof(mMenuItemText));
- mMenuItemText[MENUITEM_UNPAUSE] = S_UNPAUSE;
- mMenuItemText[MENUITEM_QUIT] = S_QUIT;
- mMenuItemText[MENUITEM_START_OVER] = S_START_OVER;
- mMenuItemText[MENUITEM_RESUME] = S_RESUME;
+ mMenuItemText[MENUITEM_UNPAUSE] = (char *)S_UNPAUSE;
+ mMenuItemText[MENUITEM_QUIT] = (char *)S_QUIT;
+ mMenuItemText[MENUITEM_START_OVER] = (char *)S_START_OVER;
+ mMenuItemText[MENUITEM_RESUME] = (char *)S_RESUME;
+ mMenuItemText[MENUITEM_LOADING] = new char[64];
+ mMenuItemText[MENUITEM_RESUME_CLOUD] = (char *)S_RESUME_CLOUD;
memset(mMenuItems, 0, sizeof(mMenuItems));
mMenuItemCount = 0;
@@ -125,109 +132,13 @@
SetScore(0);
- /*
- * where do I put the program???
- */
- const char *savePath = "/mnt/sdcard/com.google.example.games.tunnel.fix";
- int len = strlen(savePath) + strlen(SAVE_FILE_NAME) + 3;
- mSaveFileName = new char[len];
- strcpy(mSaveFileName, savePath);
- strcat(mSaveFileName, "/");
- strcat(mSaveFileName, SAVE_FILE_NAME);
- ALOGI("Save file name: %s", mSaveFileName);
- LoadProgress();
-
- if (mSavedCheckpoint) {
+ if (mSavedLevel > 0) {
// start with the menu that asks whether or not to start from the saved level
// or start over from scratch
ShowMenu(MENU_LEVEL);
}
}
-void PlayScene::LoadProgress() {
- // try to load save file
- mSavedCheckpoint = 0;
-
- ALOGI("Attempting to load: %s", mSaveFileName);
- FILE *f = fopen(mSaveFileName, "r");
- bool hasLocalFile = false;
- if (f) {
- hasLocalFile = true;
- ALOGI("File found. Loading data.");
- if (1 != fscanf(f, "v1 %d", &mSavedCheckpoint)) {
- ALOGE("Error parsing save file.");
- mSavedCheckpoint = 0;
- } else {
- ALOGI("Loaded. Level = %d", mSavedCheckpoint);
- mSavedCheckpoint = (mSavedCheckpoint / LEVELS_PER_CHECKPOINT) * LEVELS_PER_CHECKPOINT;
- ALOGI("Normalized check-point: level %d", mSavedCheckpoint);
- }
- fclose(f);
- } else {
- ALOGI("Save file not present.");
- }
-
- // check cloud save.
- ALOGI("Checking cloud save data.");
- if (true) {
- ALOGI("No cloud save available because we are not signed in.");
- mUseCloudSave = false;
- }
-
- if (mUseCloudSave && hasLocalFile) {
- // since we're using cloud save, we can delete the local progress file
- ALOGI("Since we're using cloud save, deleting local progress file %s", mSaveFileName);
- if (0 != remove(mSaveFileName)) {
- ALOGW("WARNING: failed to remove local progress file.");
- }
- }
-
- ALOGI("Final decision on starting level: %d", mSavedCheckpoint);
- ALOGI("Final decision on whether to use cloud: %s", mUseCloudSave ? "USE CLOUD" :
- "DO NOT USE CLOUD (failed)");
-}
-
-void PlayScene::WriteSaveFile(int level) {
- ALOGI("Saving progress (level %d) to file: %s", level, mSaveFileName);
- FILE *f = fopen(mSaveFileName, "w");
- if (!f) {
- ALOGE("Error writing to save game file.");
- return;
- }
- fprintf(f, "v1 %d", level);
- fclose(f);
- ALOGI("Save file written.");
-}
-
-void PlayScene::SaveProgress() {
- if (mDifficulty <= mSavedCheckpoint) {
- // nothing to do
- ALOGI("No need to save level, current = %d, saved = %d", mDifficulty, mSavedCheckpoint);
- return;
- } else if (!IsCheckpointLevel()) {
- ALOGI("Current level %d is not a checkpoint level. Nothing to save.", mDifficulty);
- return;
- }
-
- mSavedCheckpoint = mDifficulty;
-
- // Save state locally or to the cloud, depending on configuration:
- if (mUseCloudSave) {
- ALOGI("Saving progress to the cloud: level %d", mDifficulty);
- /*
- * No where to save
- */
- } else {
- ALOGI("Saving progress to LOCAL FILE: level %d", mDifficulty);
- WriteSaveFile(mDifficulty);
- }
-
- // Show a "checkpoint saved" sign when possible. We don't show it right away
- // because will already be showing the "Level N" sign, so we just set this flag
- // to remind us to show it right after.
- mCheckpointSignPending = true;
-}
-
static unsigned char *_gen_wall_texture() {
static unsigned char pixel_data[WALL_TEXTURE_SIZE * WALL_TEXTURE_SIZE * 3];
unsigned char *p;
@@ -292,11 +203,17 @@
// create text renderer and shape renderer
mTextRenderer = new TextRenderer(mTrivialShader);
mShapeRenderer = new ShapeRenderer(mTrivialShader);
+#ifdef TOUCH_INDICATOR_MODE
+ mRectRenderer = new ShapeRenderer(mTrivialShader);
+#endif // TOUCH_INDICATOR_MODE
}
void PlayScene::OnKillGraphics() {
CleanUp(&mTextRenderer);
CleanUp(&mShapeRenderer);
+#ifdef TOUCH_INDICATOR_MODE
+ CleanUp(&mRectRenderer);
+#endif // TOUCH_INDICATOR_MODE
CleanUp(&mOurShader);
CleanUp(&mTrivialShader);
CleanUp(&mTunnelGeom);
@@ -330,6 +247,20 @@
RenderObstacles();
if (mMenu) {
+ if (mMenu == MENU_LOADING) {
+ DataLoaderStateMachine *dataStateMachine =
+ NativeEngine::GetInstance()->GetDataStateMachine();
+ if (dataStateMachine->isLoadingDataCompleted()) {
+ // resume from saved level
+ HandleMenu(MENUITEM_LOADING);
+ } else {
+ int loadingPercentage = dataStateMachine->getStepsCompleted()
+ * 100 / dataStateMachine->getTotalSteps();
+ snprintf(mMenuItemText[MENUITEM_LOADING], 64, "%s... %d%%",
+ S_LOADING, loadingPercentage);
+ }
+ }
+
RenderMenu();
// nothing more to do
return;
@@ -372,6 +303,11 @@
}
mPlayerSpeed = Approach(mPlayerSpeed, targetSpeed, deltaT * accel);
+ // apply movement if key is pressed
+ if (mSteering == STEERING_KEY) {
+ OnMovementKey();
+ }
+
// apply noise filter on steering
mFilteredSteerX = (mFilteredSteerX * (NOISE_FILTER_SAMPLES - 1) + mShipSteerX)
/ NOISE_FILTER_SAMPLES;
@@ -389,6 +325,10 @@
// joystick steering
mPlayerPos.x += deltaT * steerX;
mPlayerPos.z += deltaT * steerZ;
+ } else if (mSteering == STEERING_KEY) {
+ // keyboard steering
+ mPlayerPos.x += deltaT * steerX;
+ mPlayerPos.z += deltaT * steerZ;
}
}
mPlayerPos.y += deltaT * mPlayerSpeed;
@@ -403,8 +343,10 @@
// generate more obstacles!
GenObstacles();
+#ifndef GHOST_MODE
// detect collisions
DetectCollisions(previousY);
+#endif // GHOST_MODE
// update ship's roll speed according to level
static float roll_speeds[] = ROLL_SPEEDS;
@@ -420,8 +362,7 @@
// did the game expire?
if (mLives <= 0 && Clock() > mGameOverExpire) {
- SceneManager::GetInstance()->RequestNewScene(new WelcomeScene());
-
+ SceneManager::GetInstance()->RequestNewScene(new LoaderScene());
}
// produce the ambient sound
@@ -561,7 +502,7 @@
}
}
-void PlayScene::UpdateMenuSelFromTouch(float x, float y) {
+void PlayScene::UpdateMenuSelFromTouch(float /*x*/, float y) {
float sh = SceneManager::GetInstance()->GetScreenHeight();
int item = (int) floor((y / sh) * (mMenuItemCount));
mMenuSel = Clamp(item, 0, mMenuItemCount - 1);
@@ -633,6 +574,10 @@
mTextRenderer->SetFontScale(SCORE_FONT_SCALE);
mTextRenderer->RenderText(score_str, SCORE_POS_X, SCORE_POS_Y);
+ // Render memory statistics
+ NativeEngine::GetInstance()->GetMemoryConsumer()->RenderMemoryStatistics(
+ mTextRenderer);
+
// render current sign
if (mSignText) {
modelMat = glm::mat4(1.0f);
@@ -663,6 +608,15 @@
modelMat = glm::translate(modelMat, glm::vec3(LIFE_SPACING_X, 0.0f, 0.0f));
}
+#ifdef TOUCH_INDICATOR_MODE
+ if (mSteering == STEERING_TOUCH) {
+ mRectRenderer->SetColor(1.0f, 1.0f, 0.0f);
+ } else {
+ mRectRenderer->SetColor(0.0f, 0.0f, 1.0f);
+ }
+ mRectRenderer->RenderRect(0.18f, 0.18f, 0.3f, 0.3f);
+#endif // TOUCH_INDICATOR_MODE
+
glEnable(GL_DEPTH_TEST);
}
@@ -707,6 +661,7 @@
int row = o->GetRowAt(mPlayerPos.z);
if (o->grid[col][row]) {
+#ifndef GOD_MODE
// crashed against obstacle
mLives--;
if (mLives > 0) {
@@ -724,7 +679,7 @@
mBlinkingHeartExpire = Clock() + BLINKING_HEART_DURATION;
mLastCrashSection = mFirstSection;
-
+#endif // GOD_MODE
} else if (row == o->bonusRow && col == o->bonusCol) {
ShowSign(S_GOT_BONUS, SIGN_DURATION_BONUS);
o->DeleteBonus();
@@ -744,7 +699,12 @@
SfxMan::GetInstance()->PlayTone(TONE_LEVEL_UP);
// save progress, if needed
- SaveProgress();
+ if (NativeEngine::GetInstance()->SaveProgress(mDifficulty)) {
+ // Show a "checkpoint saved" sign when possible. We don't show it right away
+ // because will already be showing the "Level N" sign, so we just set this flag
+ // to remind us to show it right after.
+ mCheckpointSignPending = true;
+ }
} else {
int tone = (score % SCORE_PER_LEVEL) / BONUS_POINTS - 1;
tone = tone < 0 ? 0 :
@@ -810,7 +770,68 @@
}
}
+void PlayScene::OnMovementKey() {
+ float deltaX = 0, deltaY = 0;
+ if (mMotionKeyBitmask & UP_MOVEMENT_BIT) {
+ deltaY -= KEY_CONTROL_VERTICAL_SENSIVITY;
+ }
+ if (mMotionKeyBitmask & LEFT_MOVEMENT_BIT) {
+ deltaX -= KEY_CONTROL_HORIZONTAL_SENSIVITY;
+ }
+ if (mMotionKeyBitmask & DOWN_MOVEMENT_BIT) {
+ deltaY += KEY_CONTROL_VERTICAL_SENSIVITY;
+ }
+ if (mMotionKeyBitmask & RIGHT_MOVEMENT_BIT) {
+ deltaX += KEY_CONTROL_HORIZONTAL_SENSIVITY;
+ }
+
+ float rotatedDx = cos(-mRollAngle) * deltaX - sin(-mRollAngle) * deltaY;
+ float rotatedDy = sin(-mRollAngle) * deltaX + cos(-mRollAngle) * deltaY;
+ mShipSteerX = rotatedDx;
+ mShipSteerZ = -rotatedDy;
+
+ // If player is going faster than the reference speed, PLAYER_SPEED, adjust it.
+ // This makes the steering react faster as the ship accelerates in more difficult
+ // levels.
+ if (mPlayerSpeed > PLAYER_SPEED) {
+ mShipSteerX *= mPlayerSpeed / PLAYER_SPEED;
+ mShipSteerZ *= mPlayerSpeed / PLAYER_SPEED;
+ }
+}
+
+void PlayScene::OnKeyUp(int keyCode) {
+ if (isMovementKey(keyCode) && mSteering == STEERING_KEY) {
+ if (keyCode == KEYCODE_W) {
+ mMotionKeyBitmask &= ~UP_MOVEMENT_BIT;
+ } else if (keyCode == KEYCODE_A) {
+ mMotionKeyBitmask &= ~LEFT_MOVEMENT_BIT;
+ } else if (keyCode == KEYCODE_S) {
+ mMotionKeyBitmask &= ~DOWN_MOVEMENT_BIT;
+ } else if (keyCode == KEYCODE_D) {
+ mMotionKeyBitmask &= ~RIGHT_MOVEMENT_BIT;
+ }
+
+ if (!mMotionKeyBitmask) {
+ mSteering = STEERING_NONE;
+ }
+ }
+}
+
void PlayScene::OnKeyDown(int keyCode) {
+ if (isMovementKey(keyCode)) {
+ mSteering = STEERING_KEY;
+
+ if (keyCode == KEYCODE_W) {
+ mMotionKeyBitmask |= UP_MOVEMENT_BIT;
+ } else if (keyCode == KEYCODE_A) {
+ mMotionKeyBitmask |= LEFT_MOVEMENT_BIT;
+ } else if (keyCode == KEYCODE_S) {
+ mMotionKeyBitmask |= DOWN_MOVEMENT_BIT;
+ } else if (keyCode == KEYCODE_D) {
+ mMotionKeyBitmask |= RIGHT_MOVEMENT_BIT;
+ }
+ }
+
if (mMenu) {
if (keyCode == OURKEY_UP) {
mMenuSel = mMenuSel > 0 ? mMenuSel - 1 : mMenuSel;
@@ -825,17 +846,30 @@
void PlayScene::ShowMenu(int menu) {
mMenu = menu;
mMenuSel = 0;
+ NativeEngine *instance = NativeEngine::GetInstance();
+ DataLoaderStateMachine *dataStateMachine = instance->GetDataStateMachine();
switch (menu) {
case MENU_PAUSE:
mMenuItems[0] = MENUITEM_UNPAUSE;
mMenuItems[1] = MENUITEM_QUIT;
- mMenuItemCount = 2;
+
+ if (instance->IsCloudSaveEnabled() &&
+ dataStateMachine->getLevelLoaded() > mSavedLevel) {
+ mMenuItems[2] = MENUITEM_RESUME_CLOUD;
+ mMenuItemCount = 3;
+ } else {
+ mMenuItemCount = 2;
+ }
break;
case MENU_LEVEL:
mMenuItems[0] = MENUITEM_RESUME;
mMenuItems[1] = MENUITEM_START_OVER;
mMenuItemCount = 2;
break;
+ case MENU_LOADING:
+ mMenuItems[0] = MENUITEM_LOADING;
+ mMenuItemCount = 1;
+ break;
default:
// since we're leaving the menu, reset the frame clock to avoid a skip
// in the animation
@@ -846,14 +880,14 @@
void PlayScene::HandleMenu(int menuItem) {
switch (menuItem) {
case MENUITEM_QUIT:
- SceneManager::GetInstance()->RequestNewScene(new WelcomeScene());
+ SceneManager::GetInstance()->RequestNewScene(new LoaderScene());
break;
case MENUITEM_UNPAUSE:
ShowMenu(MENU_NONE);
break;
case MENUITEM_RESUME:
// resume from saved level
- mDifficulty = (mSavedCheckpoint / LEVELS_PER_CHECKPOINT) * LEVELS_PER_CHECKPOINT;
+ mDifficulty = (mSavedLevel / LEVELS_PER_CHECKPOINT) * LEVELS_PER_CHECKPOINT;
SetScore(SCORE_PER_LEVEL * mDifficulty);
mObstacleGen.SetDifficulty(mDifficulty);
ShowLevelSign();
@@ -861,6 +895,24 @@
break;
case MENUITEM_START_OVER:
// start over from scratch
+ NativeEngine::GetInstance()->SaveProgress(/* level = */ 0, /* forceSave = */ true);
+ ShowMenu(MENU_NONE);
+ break;
+ case MENUITEM_LOADING:
+ ShowMenu(MENU_PAUSE);
+ break;
+ case MENUITEM_RESUME_CLOUD:
+ DataLoaderStateMachine *dataStateMachine =
+ NativeEngine::GetInstance()->GetDataStateMachine();
+ mSavedLevel = (dataStateMachine->getLevelLoaded() / LEVELS_PER_CHECKPOINT)
+ * LEVELS_PER_CHECKPOINT;
+
+ if (mSavedLevel > mDifficulty) {
+ mDifficulty = mSavedLevel;
+ SetScore(SCORE_PER_LEVEL * mDifficulty);
+ mObstacleGen.SetDifficulty(mDifficulty);
+ }
+ ShowLevelSign();
ShowMenu(MENU_NONE);
break;
}
@@ -881,7 +933,14 @@
}
}
-void PlayScene::OnScreenResized(int width, int height) {
+void PlayScene::OnResume() {
+ if (NativeEngine::GetInstance()->IsCloudSaveEnabled() &&
+ (mMenu == MENU_NONE || mMenu == MENU_PAUSE)) {
+ ShowMenu(MENU_LOADING);
+ }
+}
+
+void PlayScene::OnScreenResized(int /*width*/, int /*height*/) {
UpdateProjectionMatrix();
}
@@ -890,4 +949,3 @@
mProjMat = glm::perspective(RENDER_FOV, mgr->GetScreenAspect(), RENDER_NEAR_CLIP,
RENDER_FAR_CLIP);
}
-
diff --git a/samples/agdktunnel/app/src/main/cpp/play_scene.hpp b/samples/agdktunnel/app/src/main/cpp/play_scene.hpp
index d700913..50befb1 100644
--- a/samples/agdktunnel/app/src/main/cpp/play_scene.hpp
+++ b/samples/agdktunnel/app/src/main/cpp/play_scene.hpp
@@ -24,6 +24,8 @@
#include "shape_renderer.hpp"
#include "text_renderer.hpp"
#include "util.hpp"
+#include "input_util.hpp"
+#include "loader_scene.hpp"
class OurShader;
@@ -35,6 +37,8 @@
public:
PlayScene();
+ PlayScene(int savedLevel);
+
virtual void OnStartGraphics();
virtual void OnKillGraphics();
@@ -55,8 +59,12 @@
virtual void OnKeyDown(int keyCode);
+ virtual void OnKeyUp(int keyCode);
+
virtual void OnPause();
+ virtual void OnResume();
+
protected:
// shaders
OurShader *mOurShader;
@@ -68,6 +76,10 @@
// shape and text renderers we use when rendering the HUD
ShapeRenderer *mShapeRenderer;
TextRenderer *mTextRenderer;
+#ifdef TOUCH_INDICATOR_MODE
+ // Flat color rectangle for latency measurement
+ ShapeRenderer *mRectRenderer;
+#endif // TOUCH_INDICATOR_MODE
// matrices
glm::mat4 mViewMat, mProjMat;
@@ -91,11 +103,8 @@
// current difficulty level
int mDifficulty;
- // should we use cloud save? If not, we will save progress to local data only.
- bool mUseCloudSave;
-
// greatest checkpoint level attained by player (loaded from file)
- int mSavedCheckpoint;
+ int mSavedLevel;
// vertex buffer and index buffer to render tunnel
SimpleGeom *mTunnelGeom;
@@ -120,7 +129,7 @@
ObstacleGenerator mObstacleGen;
// touch pointer ID and anchor position (where touch started)
- static const int STEERING_NONE = 0, STEERING_TOUCH = 1, STEERING_JOY = 2;
+ static const int STEERING_NONE = 0, STEERING_TOUCH = 1, STEERING_JOY = 2, STEERING_KEY = 3;
int mSteering; // is player steering at the moment? If so, how?
int mPointerId; // if so, what's the pointer ID
float mPointerAnchorX, mPointerAnchorY; // where the drag started
@@ -132,6 +141,9 @@
static const int NOISE_FILTER_SAMPLES = 5;
float mFilteredSteerX, mFilteredSteerZ;
+ // Bitmask for movement keys: W = 1, A = 2, S = 4, D = 8
+ int mMotionKeyBitmask;
+
// frame clock -- it computes the deltas between successive frames so we can
// update stuff properly
DeltaClock mFrameClock;
@@ -149,6 +161,7 @@
static const int MENU_NONE = 0;
static const int MENU_PAUSE = 1; // pause menu
static const int MENU_LEVEL = 2; // select starting level
+ static const int MENU_LOADING = 3; // loading menu
int mMenu;
// identifiers for each menu item
@@ -156,10 +169,12 @@
static const int MENUITEM_QUIT = 1;
static const int MENUITEM_START_OVER = 2;
static const int MENUITEM_RESUME = 3;
- static const int MENUITEM_COUNT = 4;
+ static const int MENUITEM_LOADING = 4;
+ static const int MENUITEM_RESUME_CLOUD = 5;
+ static const int MENUITEM_COUNT = 6;
// text for each menu item
- const char *mMenuItemText[MENUITEM_COUNT];
+ char *mMenuItemText[MENUITEM_COUNT];
// menu items on current menu
static const int MENUITEMS_MAX = 4;
@@ -200,9 +215,6 @@
// last subsection were an ambient sound was emitted
int mLastAmbientBeepEmitted;
- // name of the save file
- char *mSaveFileName;
-
// pending to show a "checkpoint saved" sign?
bool mCheckpointSignPending;
@@ -272,28 +284,15 @@
// updates which menu item is selected based on where the screen was touched
void UpdateMenuSelFromTouch(float x, float y);
- // writes to the local save file
- void WriteSaveFile(int level);
-
- // loads progress from the local save file and/or cloudsave
- void LoadProgress();
-
- // saves progress to the local save file and/or cloudsave
- void SaveProgress();
-
- // returns whether or not this level is a "checkpoint level" (that is,
- // where progress should be saved)
- bool IsCheckpointLevel() {
- return 0 == mDifficulty % LEVELS_PER_CHECKPOINT;
- }
-
// shows the sign that tells the player they've reached a new level.
// (like "LEVEL 5").
void ShowLevelSign();
// update projection matrix
void UpdateProjectionMatrix();
+
+ // apply movement if key is pressed
+ void OnMovementKey();
};
#endif
-
diff --git a/samples/agdktunnel/app/src/main/cpp/scene.cpp b/samples/agdktunnel/app/src/main/cpp/scene.cpp
index 019fff7..1f80efb 100644
--- a/samples/agdktunnel/app/src/main/cpp/scene.cpp
+++ b/samples/agdktunnel/app/src/main/cpp/scene.cpp
@@ -29,21 +29,21 @@
void Scene::OnKillGraphics() {}
-void Scene::OnPointerDown(int pointerId, const struct PointerCoords *coords) {}
+void Scene::OnPointerDown(int /*pointerId*/, const struct PointerCoords */*coords*/) {}
-void Scene::OnPointerUp(int pointerId, const struct PointerCoords *coords) {}
+void Scene::OnPointerUp(int /*pointerId*/, const struct PointerCoords */*coords*/) {}
-void Scene::OnPointerMove(int pointerId, const struct PointerCoords *coords) {}
+void Scene::OnPointerMove(int /*pointerId*/, const struct PointerCoords */*coords*/) {}
bool Scene::OnBackKeyPressed() { return false; }
-void Scene::OnKeyDown(int ourKeycode) {}
+void Scene::OnKeyDown(int /*ourKeycode*/) {}
-void Scene::OnKeyUp(int ourKeycode) {}
+void Scene::OnKeyUp(int /*ourKeycode*/) {}
-void Scene::OnJoy(float x, float y) {}
+void Scene::OnJoy(float /*x*/, float /*y*/) {}
-void Scene::OnScreenResized(int width, int height) {}
+void Scene::OnScreenResized(int /*width*/, int /*height*/) {}
void Scene::OnPause() {}
@@ -52,4 +52,3 @@
void Scene::OnTextInput() {}
Scene::~Scene() {}
-
diff --git a/samples/agdktunnel/app/src/main/cpp/scene.hpp b/samples/agdktunnel/app/src/main/cpp/scene.hpp
index 841f38c..d799bc9 100644
--- a/samples/agdktunnel/app/src/main/cpp/scene.hpp
+++ b/samples/agdktunnel/app/src/main/cpp/scene.hpp
@@ -80,4 +80,3 @@
};
#endif
-
diff --git a/samples/agdktunnel/app/src/main/cpp/scene_manager.cpp b/samples/agdktunnel/app/src/main/cpp/scene_manager.cpp
index abfe733..53b173e 100644
--- a/samples/agdktunnel/app/src/main/cpp/scene_manager.cpp
+++ b/samples/agdktunnel/app/src/main/cpp/scene_manager.cpp
@@ -142,23 +142,23 @@
return false;
}
-void SceneManager::OnKeyDown(int ourKeycode) {
- MY_ASSERT(ourKeycode >= 0 && ourKeycode < OURKEY_COUNT);
- if (mHasGraphics && mCurScene) {
- mCurScene->OnKeyDown(ourKeycode);
+void SceneManager::OnKeyDown(int keyCode) {
+ if (isMovementKey(keyCode) || (keyCode >= 0 && keyCode < OURKEY_COUNT &&
+ mHasGraphics && mCurScene)) {
+ mCurScene->OnKeyDown(keyCode);
// if our "escape" key (normally corresponding to joystick button B or Y)
// was pressed, handle it as a back key
- if (ourKeycode == OURKEY_ESCAPE) {
+ if (keyCode == OURKEY_ESCAPE) {
mCurScene->OnBackKeyPressed();
}
}
}
-void SceneManager::OnKeyUp(int ourKeycode) {
- MY_ASSERT(ourKeycode >= 0 && ourKeycode < OURKEY_COUNT);
- if (mHasGraphics && mCurScene) {
- mCurScene->OnKeyUp(ourKeycode);
+void SceneManager::OnKeyUp(int keyCode) {
+ if (isMovementKey(keyCode) || (keyCode >= 0 && keyCode < OURKEY_COUNT &&
+ mHasGraphics && mCurScene)) {
+ mCurScene->OnKeyUp(keyCode);
}
}
@@ -175,7 +175,7 @@
}
void SceneManager::OnResume() {
- if (mHasGraphics && mCurScene) {
+ if (mCurScene) {
mCurScene->OnResume();
}
}
@@ -184,4 +184,4 @@
if (mHasGraphics && mCurScene) {
mCurScene->OnTextInput();
}
-}
\ No newline at end of file
+}
diff --git a/samples/agdktunnel/app/src/main/cpp/scene_manager.hpp b/samples/agdktunnel/app/src/main/cpp/scene_manager.hpp
index 023ef06..01fe116 100644
--- a/samples/agdktunnel/app/src/main/cpp/scene_manager.hpp
+++ b/samples/agdktunnel/app/src/main/cpp/scene_manager.hpp
@@ -18,6 +18,7 @@
#define agdktunnel_scene_manager_h
#include "our_key_codes.hpp"
+#include "input_util.hpp"
class Scene;
@@ -113,4 +114,3 @@
};
#endif
-
diff --git a/samples/agdktunnel/app/src/main/cpp/sfxman.cpp b/samples/agdktunnel/app/src/main/cpp/sfxman.cpp
index 1008f7f..93ecc89 100644
--- a/samples/agdktunnel/app/src/main/cpp/sfxman.cpp
+++ b/samples/agdktunnel/app/src/main/cpp/sfxman.cpp
@@ -82,7 +82,7 @@
return s;
}
-static int _synth(int frequency, int duration, float amplitude, int16_t *sample_buf, int samples) {
+static int _synth(int frequency, int /*duration*/, float amplitude, int16_t *sample_buf, int samples) {
int i;
for (i = 0; i < samples; i++) {
@@ -194,7 +194,7 @@
}
oboe::DataCallbackResult
-SfxMan::onAudioReady(oboe::AudioStream *audioStream, void *audioData, int32_t numFrames) {
+SfxMan::onAudioReady(oboe::AudioStream */*audioStream*/, void *audioData, int32_t numFrames) {
if (_frameCount > 0) {
const int32_t remainingFrames = _frameCount - _frameCursor;
if (numFrames < remainingFrames) {
@@ -222,4 +222,4 @@
}
return oboe::DataCallbackResult::Continue;
-}
\ No newline at end of file
+}
diff --git a/samples/agdktunnel/app/src/main/cpp/sfxman.hpp b/samples/agdktunnel/app/src/main/cpp/sfxman.hpp
index 35b7f19..4809ee0 100644
--- a/samples/agdktunnel/app/src/main/cpp/sfxman.hpp
+++ b/samples/agdktunnel/app/src/main/cpp/sfxman.hpp
@@ -69,4 +69,3 @@
};
#endif
-
diff --git a/samples/agdktunnel/app/src/main/cpp/shader.cpp b/samples/agdktunnel/app/src/main/cpp/shader.cpp
index 80116fa..b27818d 100644
--- a/samples/agdktunnel/app/src/main/cpp/shader.cpp
+++ b/samples/agdktunnel/app/src/main/cpp/shader.cpp
@@ -283,5 +283,3 @@
MY_ASSERT(mTintLoc >= 0);
glUniform4f(mTintLoc, mTint[0], mTint[1], mTint[2], 1.0f);
}
-
-
diff --git a/samples/agdktunnel/app/src/main/cpp/shader.hpp b/samples/agdktunnel/app/src/main/cpp/shader.hpp
index 902238c..db06fbf 100644
--- a/samples/agdktunnel/app/src/main/cpp/shader.hpp
+++ b/samples/agdktunnel/app/src/main/cpp/shader.hpp
@@ -128,4 +128,3 @@
};
#endif
-
diff --git a/samples/agdktunnel/app/src/main/cpp/shape_renderer.cpp b/samples/agdktunnel/app/src/main/cpp/shape_renderer.cpp
index 1f9e15c..8dbb4b4 100644
--- a/samples/agdktunnel/app/src/main/cpp/shape_renderer.cpp
+++ b/samples/agdktunnel/app/src/main/cpp/shape_renderer.cpp
@@ -56,4 +56,3 @@
mTrivialShader->SetTintColor(mColor[0], mColor[1], mColor[2]);
mTrivialShader->RenderSimpleGeom(&mat, mGeom);
}
-
diff --git a/samples/agdktunnel/app/src/main/cpp/shape_renderer.hpp b/samples/agdktunnel/app/src/main/cpp/shape_renderer.hpp
index 413d38f..aa63cd2 100644
--- a/samples/agdktunnel/app/src/main/cpp/shape_renderer.hpp
+++ b/samples/agdktunnel/app/src/main/cpp/shape_renderer.hpp
@@ -46,4 +46,3 @@
};
#endif
-
diff --git a/samples/agdktunnel/app/src/main/cpp/simplegeom.hpp b/samples/agdktunnel/app/src/main/cpp/simplegeom.hpp
index 5af728b..4dc39d6 100644
--- a/samples/agdktunnel/app/src/main/cpp/simplegeom.hpp
+++ b/samples/agdktunnel/app/src/main/cpp/simplegeom.hpp
@@ -53,4 +53,3 @@
};
#endif
-
diff --git a/samples/agdktunnel/app/src/main/cpp/tex_quad.cpp b/samples/agdktunnel/app/src/main/cpp/tex_quad.cpp
index fdad3d4..7a9b70d 100644
--- a/samples/agdktunnel/app/src/main/cpp/tex_quad.cpp
+++ b/samples/agdktunnel/app/src/main/cpp/tex_quad.cpp
@@ -104,5 +104,3 @@
glEnable(GL_DEPTH_TEST);
}
}
-
-
diff --git a/samples/agdktunnel/app/src/main/cpp/tex_quad.hpp b/samples/agdktunnel/app/src/main/cpp/tex_quad.hpp
index f0f7953..2ae6479 100644
--- a/samples/agdktunnel/app/src/main/cpp/tex_quad.hpp
+++ b/samples/agdktunnel/app/src/main/cpp/tex_quad.hpp
@@ -109,4 +109,3 @@
};
#endif
-
diff --git a/samples/agdktunnel/app/src/main/cpp/text_renderer.cpp b/samples/agdktunnel/app/src/main/cpp/text_renderer.cpp
index 595d3e1..45127bb 100644
--- a/samples/agdktunnel/app/src/main/cpp/text_renderer.cpp
+++ b/samples/agdktunnel/app/src/main/cpp/text_renderer.cpp
@@ -51,9 +51,8 @@
}
}
-TextRenderer *TextRenderer::SetFontScale(float scale) {
+void TextRenderer::SetFontScale(float scale) {
mFontScale = scale;
- return this;
}
static void _count_rows_cols(const char *p, int *outCols, int *outRows) {
@@ -74,9 +73,8 @@
*outRows = textRows;
}
-TextRenderer *TextRenderer::SetMatrix(glm::mat4 m) {
+void TextRenderer::SetMatrix(glm::mat4 m) {
mMatrix = m;
- return this;
}
void TextRenderer::MeasureText(const char *str, float fontScale, float *outWidth,
@@ -91,7 +89,7 @@
}
}
-TextRenderer *TextRenderer::RenderText(const char *str, float centerX, float centerY) {
+void TextRenderer::RenderText(const char *str, float centerX, float centerY) {
float aspect = SceneManager::GetInstance()->GetScreenAspect();
glm::mat4 orthoMat = glm::ortho(0.0f, aspect, 0.0f, 1.0f);
glm::mat4 modelMat, mat, scaleMat;
@@ -126,7 +124,9 @@
modelMat = glm::translate(glm::mat4(1.0f), glm::vec3(startX, y, 0.0f));
} else {
int code = (int) *str;
- if (code >= 0 && code < CHAR_CODES && mCharGeom[code]) {
+ /** Unsupported characters to show "?" **/
+ code = (code >= 0 && code < CHAR_CODES) ? code : UNSUPPORTED_CODE;
+ if (mCharGeom[code]) {
mat = orthoMat * modelMat * scaleMat * mMatrix;
mTrivialShader->RenderSimpleGeom(&mat, mCharGeom[code]);
}
@@ -138,6 +138,4 @@
if (hadDepthTest) {
glEnable(GL_DEPTH_TEST);
}
- return this;
}
-
diff --git a/samples/agdktunnel/app/src/main/cpp/text_renderer.hpp b/samples/agdktunnel/app/src/main/cpp/text_renderer.hpp
index ac53892..030566c 100644
--- a/samples/agdktunnel/app/src/main/cpp/text_renderer.hpp
+++ b/samples/agdktunnel/app/src/main/cpp/text_renderer.hpp
@@ -24,6 +24,7 @@
class TextRenderer {
private:
static const int CHAR_CODES = 128;
+ static const int UNSUPPORTED_CODE = 63; /* "?" is rendered instead. */
SimpleGeom *mCharGeom[CHAR_CODES];
TrivialShader *mTrivialShader;
@@ -36,11 +37,11 @@
~TextRenderer();
- TextRenderer *SetMatrix(glm::mat4 mat);
+ void SetMatrix(glm::mat4 mat);
- TextRenderer *SetFontScale(float size);
+ void SetFontScale(float size);
- TextRenderer *RenderText(const char *str, float centerX, float centerY);
+ void RenderText(const char *str, float centerX, float centerY);
void SetColor(float r, float g, float b) {
mColor[0] = r, mColor[1] = g, mColor[2] = b;
@@ -54,7 +55,7 @@
SetColor(1.0f, 1.0f, 1.0f);
}
- TextRenderer *ResetMatrix() {
+ void ResetMatrix() {
return SetMatrix(glm::mat4(1.0f));
}
@@ -75,4 +76,3 @@
};
#endif
-
diff --git a/samples/agdktunnel/app/src/main/cpp/texture.cpp b/samples/agdktunnel/app/src/main/cpp/texture.cpp
index 7d40480..defa417 100644
--- a/samples/agdktunnel/app/src/main/cpp/texture.cpp
+++ b/samples/agdktunnel/app/src/main/cpp/texture.cpp
@@ -42,4 +42,3 @@
void Texture::Unbind() {
glBindTexture(GL_TEXTURE_2D, 0);
}
-
diff --git a/samples/agdktunnel/app/src/main/cpp/texture.hpp b/samples/agdktunnel/app/src/main/cpp/texture.hpp
index f062cc4..d91b989 100644
--- a/samples/agdktunnel/app/src/main/cpp/texture.hpp
+++ b/samples/agdktunnel/app/src/main/cpp/texture.hpp
@@ -46,4 +46,3 @@
};
#endif
-
diff --git a/samples/agdktunnel/app/src/main/cpp/texture_manager.cpp b/samples/agdktunnel/app/src/main/cpp/texture_manager.cpp
index b8fb6cf..0704fa8 100644
--- a/samples/agdktunnel/app/src/main/cpp/texture_manager.cpp
+++ b/samples/agdktunnel/app/src/main/cpp/texture_manager.cpp
@@ -197,7 +197,7 @@
// This is not a robust KTX loader, ala libktx. It is only intended to load the KTX 1.1
// ETC2 format, mip-mapped 2D texture files included with this example
static bool
-CreateFromKTXFile(const uint8_t *fileData, const size_t fileSize, GLuint *textureID,
+CreateFromKTXFile(const uint8_t *fileData, const size_t /*fileSize*/, GLuint *textureID,
uint32_t *textureMipCount) {
bool success = false;
const KTXHeader *header = reinterpret_cast<const KTXHeader *>(fileData);
diff --git a/samples/agdktunnel/app/src/main/cpp/tuning_manager.cpp b/samples/agdktunnel/app/src/main/cpp/tuning_manager.cpp
index 30bf1ac..a6f5085 100644
--- a/samples/agdktunnel/app/src/main/cpp/tuning_manager.cpp
+++ b/samples/agdktunnel/app/src/main/cpp/tuning_manager.cpp
@@ -28,16 +28,19 @@
#include "game_consts.hpp"
#include "tuning_manager.hpp"
+#define UNUSED(expr) do { (void)(expr); } while (0)
+
/** @cond INTERNAL */
/**
* Internal to this file - do not use.
*/
extern "C" void TuningFork_CProtobufSerialization_Dealloc(
- TuningFork_CProtobufSerialization* c);
+ TuningFork_CProtobufSerialization *c);
/** @endcond */
+#if defined(USE_APT)
namespace {
constexpr TuningFork_InstrumentKey TFTICK_CHOREOGRAPHER = TFTICK_USERDEFINED_BASE;
@@ -45,29 +48,30 @@
TuningFork_LoadingTimeMetadata startupLoadingMetadata;
typedef void (*func_AChoreographer_postFrameCallback64)(
- AChoreographer* choreographer, AChoreographer_frameCallback64 callback,
- void* data);
+ AChoreographer *choreographer, AChoreographer_frameCallback64 callback,
+ void *data);
+
func_AChoreographer_postFrameCallback64 pAChoreographer_postFrameCallback64 = nullptr;
- void choreographer_callback(long /*frameTimeNanos*/, void* data) {
- TuningManager* tuningManager = reinterpret_cast<TuningManager*>(data);
+ void choreographer_callback(long /*frameTimeNanos*/, void *data) {
+ TuningManager *tuningManager = reinterpret_cast<TuningManager *>(data);
tuningManager->HandleChoreographerFrame();
}
- void choreographer_callback64(int64_t /*frameTimeNanos*/, void* data) {
- TuningManager* tuningManager = reinterpret_cast<TuningManager*>(data);
+ void choreographer_callback64(int64_t /*frameTimeNanos*/, void *data) {
+ TuningManager *tuningManager = reinterpret_cast<TuningManager *>(data);
tuningManager->HandleChoreographerFrame();
}
- bool serialize_annotation(TuningFork_CProtobufSerialization& cser,
- const _com_google_tuningfork_Annotation* annotation) {
+ bool serialize_annotation(TuningFork_CProtobufSerialization &cser,
+ const _com_google_tuningfork_Annotation *annotation) {
bool success = false;
cser.bytes = NULL;
cser.size = 0;
size_t encodedSize = 0;
if (pb_get_encoded_size(&encodedSize, com_google_tuningfork_Annotation_fields,
- annotation)) {
+ annotation)) {
cser.bytes = (uint8_t *) ::malloc(encodedSize);
cser.size = encodedSize;
cser.dealloc = TuningFork_CProtobufSerialization_Dealloc;
@@ -79,11 +83,14 @@
return success;
}
}
+#endif
-TuningManager::TuningManager(JNIEnv* env, jobject activity, AConfiguration* config) {
+TuningManager::TuningManager(JNIEnv *env, jobject activity, AConfiguration *config) {
+ UNUSED(env); UNUSED(activity); UNUSED(config);
mTFInitialized = false;
- TuningFork_Settings settings {};
+#if defined(USE_APT)
+ TuningFork_Settings settings{};
// Performance Tuner can work with the Frame Pacing library to automatically
// record frame time via the tracer function
@@ -108,8 +115,8 @@
*/
TuningFork_CProtobufSerialization fps = {};
bool bHighDensity = (RENDER_TUNNEL_SECTION_COUNT == 8 && TUNNEL_SECTION_LENGTH == 75.0f);
- const char* filename = bHighDensity ? "dev_tuningfork_fidelityparams_2.bin" :
- "dev_tuningfork_fidelityparams_1.bin";
+ const char *filename = bHighDensity ? "dev_tuningfork_fidelityparams_2.bin" :
+ "dev_tuningfork_fidelityparams_1.bin";
if (TuningFork_findFidelityParamsInApk(env, activity, filename, &fps)
== TUNINGFORK_ERROR_OK) {
// This overrides the value in default_fidelity_parameters_filename
@@ -131,18 +138,23 @@
TuningFork_CProtobufSerialization_free(&fps);
InitializeChoreographerCallback(config);
+#endif
}
TuningManager::~TuningManager() {
+#if defined(USE_APT)
if (mTFInitialized) {
TuningFork_ErrorCode tfError = TuningFork_destroy();
if (tfError != TUNINGFORK_ERROR_OK) {
ALOGE("Error destroying TuningFork: %d", tfError);
}
}
+#endif
}
-void TuningManager::InitializeChoreographerCallback(AConfiguration* config) {
+void TuningManager::InitializeChoreographerCallback(AConfiguration *config) {
+ UNUSED(config);
+#if defined(USE_APT)
int32_t sdkVersion = AConfiguration_getSdkVersion(config);
if (sdkVersion >= 29) {
// The original postFrameCallback is deprecated in 29 and later, try and
@@ -164,11 +176,13 @@
}
} else {
AChoreographer_postFrameCallback(AChoreographer_getInstance(),
- choreographer_callback, this);
+ choreographer_callback, this);
}
+#endif
}
void TuningManager::HandleChoreographerFrame() {
+#if defined(USE_APT)
PostFrameTick(TFTICK_CHOREOGRAPHER);
if (pAChoreographer_postFrameCallback64 != nullptr) {
@@ -178,6 +192,7 @@
AChoreographer_postFrameCallback(AChoreographer_getInstance(),
choreographer_callback, this);
}
+#endif
}
void TuningManager::PostFrameTick(const TuningFork_InstrumentKey frameKey) {
@@ -189,7 +204,8 @@
}
}
-void TuningManager::SetCurrentAnnotation(const _com_google_tuningfork_Annotation* annotation) {
+#if defined(USE_APT)
+void TuningManager::SetCurrentAnnotation(const _com_google_tuningfork_Annotation *annotation) {
TuningFork_CProtobufSerialization cser;
if (serialize_annotation(cser, annotation)) {
if (TuningFork_setCurrentAnnotation(&cser) != TUNINGFORK_ERROR_OK) {
@@ -200,8 +216,10 @@
ALOGE("Failed to calculate annotation encode size");
}
}
+#endif
void TuningManager::StartLoading() {
+#if defined(USE_APT)
// Initial annotation of our state
_com_google_tuningfork_Annotation annotation;
annotation.loading = com_google_tuningfork_LoadingState_LOADING;
@@ -220,13 +238,16 @@
&startupLoadingHandle);
TuningFork_CProtobufSerialization_free(&cser);
}
+#endif
}
void TuningManager::FinishLoading() {
+#if defined(USE_APT)
TuningFork_stopRecordingLoadingTime(startupLoadingHandle);
_com_google_tuningfork_Annotation annotation;
annotation.loading = com_google_tuningfork_LoadingState_NOT_LOADING;
annotation.level = com_google_tuningfork_Level_LEVEL_1;
SetCurrentAnnotation(&annotation);
-}
\ No newline at end of file
+#endif
+}
diff --git a/samples/agdktunnel/app/src/main/cpp/tuning_manager.hpp b/samples/agdktunnel/app/src/main/cpp/tuning_manager.hpp
index a093e08..e5df3df 100644
--- a/samples/agdktunnel/app/src/main/cpp/tuning_manager.hpp
+++ b/samples/agdktunnel/app/src/main/cpp/tuning_manager.hpp
@@ -18,25 +18,37 @@
#define agdktunnel_tuning_manager_hpp
#include "common.hpp"
+
+#if defined(USE_APT)
+// Generated protobuf headers
#include "nano/dev_tuningfork.pb.h"
#include "nano/tuningfork.pb.h"
+#endif
struct AConfiguration;
class TuningManager {
- private:
- bool mTFInitialized;
+private:
+ bool mTFInitialized;
- void InitializeChoreographerCallback(AConfiguration* config);
- public:
- TuningManager(JNIEnv* env, jobject context, AConfiguration* config);
- ~TuningManager();
+ void InitializeChoreographerCallback(AConfiguration *config);
- void HandleChoreographerFrame();
- void PostFrameTick(const uint16_t frameKey);
- void SetCurrentAnnotation(const _com_google_tuningfork_Annotation* annotation);
- void StartLoading();
- void FinishLoading();
+public:
+ TuningManager(JNIEnv *env, jobject context, AConfiguration *config);
+
+ ~TuningManager();
+
+ void HandleChoreographerFrame();
+
+ void PostFrameTick(const uint16_t frameKey);
+
+#if defined(USE_APT)
+ void SetCurrentAnnotation(const _com_google_tuningfork_Annotation *annotation);
+#endif
+
+ void StartLoading();
+
+ void FinishLoading();
};
-#endif
\ No newline at end of file
+#endif
diff --git a/samples/agdktunnel/app/src/main/cpp/ui_scene.cpp b/samples/agdktunnel/app/src/main/cpp/ui_scene.cpp
index fe5752d..bef17e5 100644
--- a/samples/agdktunnel/app/src/main/cpp/ui_scene.cpp
+++ b/samples/agdktunnel/app/src/main/cpp/ui_scene.cpp
@@ -95,7 +95,7 @@
DeleteWidgets();
}
-void UiScene::OnScreenResized(int width, int height) {
+void UiScene::OnScreenResized(int /*width*/, int /*height*/) {
// screen got resized; if we have widgets and graphics, we have to recreate them
SceneManager *mgr = SceneManager::GetInstance();
if (mgr->HasGraphics() && mWidgetCount > 0) {
@@ -146,6 +146,10 @@
(mFocusWidget == i) ? UiWidget::FOCUS_YES : UiWidget::FOCUS_NO, tf);
}
+ // Render memory statistics
+ NativeEngine::GetInstance()->GetMemoryConsumer()->RenderMemoryStatistics(
+ mTextRenderer);
+
glEnable(GL_DEPTH_TEST);
}
@@ -153,7 +157,7 @@
// base classes override this to draw background
}
-void UiScene::OnButtonClicked(int buttonId) {
+void UiScene::OnButtonClicked(int /*buttonId*/) {
// base classes override this to react to button clicks
}
@@ -174,7 +178,7 @@
}
}
-void UiScene::OnPointerDown(int pointerId, const struct PointerCoords *coords) {
+void UiScene::OnPointerDown(int /*pointerId*/, const struct PointerCoords *coords) {
// If this event was generated by something that's not associated to the screen,
// (like a trackpad), ignore it, because our UI is not driven that way.
if (coords->isScreen && !mWaitScreen) {
@@ -183,13 +187,13 @@
}
}
-void UiScene::OnPointerMove(int pointerId, const struct PointerCoords *coords) {
+void UiScene::OnPointerMove(int /*pointerId*/, const struct PointerCoords *coords) {
if (coords->isScreen && mPointerDown && !mWaitScreen) {
UpdateTouchFocus(coords);
}
}
-void UiScene::OnPointerUp(int pointerId, const struct PointerCoords *coords) {
+void UiScene::OnPointerUp(int /*pointerId*/, const struct PointerCoords *coords) {
if (!coords->isScreen || mWaitScreen) {
return;
}
@@ -302,7 +306,7 @@
}
}
-void UiWidget::Render(TrivialShader *trivialShader, TextRenderer *textRenderer,
+void UiWidget::Render(TrivialShader */*trivialShader*/, TextRenderer *textRenderer,
ShapeRenderer *shapeRenderer, int focus, float transitionFactor) {
if (!mVisible) {
// that was easy.
@@ -349,4 +353,3 @@
}
void UiScene::OnCreateWidgets() {}
-
diff --git a/samples/agdktunnel/app/src/main/cpp/ui_scene.hpp b/samples/agdktunnel/app/src/main/cpp/ui_scene.hpp
index 5f03e0e..e161a55 100644
--- a/samples/agdktunnel/app/src/main/cpp/ui_scene.hpp
+++ b/samples/agdktunnel/app/src/main/cpp/ui_scene.hpp
@@ -265,7 +265,7 @@
return this;
}
- UiWidget *SetTransparent(bool transp) {
+ UiWidget *SetTransparent(bool /*transp*/) {
mTransparent = true;
return this;
}
@@ -322,4 +322,3 @@
};
#endif
-
diff --git a/samples/agdktunnel/app/src/main/cpp/util.cpp b/samples/agdktunnel/app/src/main/cpp/util.cpp
index 4fba5e2..b49b3f2 100644
--- a/samples/agdktunnel/app/src/main/cpp/util.cpp
+++ b/samples/agdktunnel/app/src/main/cpp/util.cpp
@@ -55,4 +55,3 @@
bool BlinkFunc(float period) {
return (int) (Clock() / period) & 1;
}
-
diff --git a/samples/agdktunnel/app/src/main/cpp/util.hpp b/samples/agdktunnel/app/src/main/cpp/util.hpp
index d778834..7a1ffb5 100644
--- a/samples/agdktunnel/app/src/main/cpp/util.hpp
+++ b/samples/agdktunnel/app/src/main/cpp/util.hpp
@@ -119,4 +119,3 @@
};
#endif
-
diff --git a/samples/agdktunnel/app/src/main/cpp/vertexbuf.cpp b/samples/agdktunnel/app/src/main/cpp/vertexbuf.cpp
index 14e34e2..e437600 100644
--- a/samples/agdktunnel/app/src/main/cpp/vertexbuf.cpp
+++ b/samples/agdktunnel/app/src/main/cpp/vertexbuf.cpp
@@ -44,5 +44,3 @@
glDeleteBuffers(1, &mVbo);
mVbo = 0;
}
-
-
diff --git a/samples/agdktunnel/app/src/main/cpp/vertexbuf.hpp b/samples/agdktunnel/app/src/main/cpp/vertexbuf.hpp
index 2d7df46..7778ef2 100644
--- a/samples/agdktunnel/app/src/main/cpp/vertexbuf.hpp
+++ b/samples/agdktunnel/app/src/main/cpp/vertexbuf.hpp
@@ -62,4 +62,3 @@
};
#endif
-
diff --git a/samples/agdktunnel/app/src/main/cpp/welcome_scene.cpp b/samples/agdktunnel/app/src/main/cpp/welcome_scene.cpp
index d94380d..759f8c3 100644
--- a/samples/agdktunnel/app/src/main/cpp/welcome_scene.cpp
+++ b/samples/agdktunnel/app/src/main/cpp/welcome_scene.cpp
@@ -56,6 +56,7 @@
// position of each side button (the buttons on the sides of the PLAY button)
#define BUTTON_STORY_POS 0.1f + 0.5f * BUTTON_SIDEBUTTON_WIDTH, 0.5f
+#define BUTTON_MEMORY_POS 0.1f + 0.5f * BUTTON_SIDEBUTTON_WIDTH, 0.25f
#define BUTTON_ABOUT_POS center + 0.3f + 0.5f * BUTTON_SIDEBUTTON_WIDTH, 0.5f
#define BUTTON_TEST_POS center + 0.3f + 0.5f * BUTTON_SIDEBUTTON_WIDTH, 0.75f
#define BUTTON_QUIT_POS center + 0.3f + 0.5f * BUTTON_SIDEBUTTON_WIDTH, 0.25f
@@ -127,7 +128,9 @@
SceneManager *mgr = SceneManager::GetInstance();
if (id == mPlayButtonId) {
- mgr->RequestNewScene(new PlayScene());
+ DataLoaderStateMachine *dataStateMachine =
+ NativeEngine::GetInstance()->GetDataStateMachine();
+ mgr->RequestNewScene(new PlayScene(dataStateMachine->getLevelLoaded()));
} else if (id == mStoryButtonId) {
mgr->RequestNewScene((new DialogScene())->SetText(BLURB_STORY)->SetSingleButton(S_OK,
DialogScene::ACTION_RETURN));
@@ -152,6 +155,8 @@
} else if (id == mQuitButtonId) {
auto activity = NativeEngine::GetInstance()->GetAndroidApp()->activity;
GameActivity_finish(activity);
+ } else if (id == mMemoryButtonId) {
+ NativeEngine::GetInstance()->GetMemoryConsumer()->SetActive(true);
}
}
@@ -187,7 +192,8 @@
AddNav(mPlayButtonId, UI_DIR_LEFT, mStoryButtonId);
AddNav(mPlayButtonId, UI_DIR_RIGHT, mAboutButtonId);
- AddNav(mStoryButtonId, UI_DIR_RIGHT, mPlayButtonId);
+ AddNav(mStoryButtonId, UI_DIR_RIGHT, mMemoryButtonId);
+ AddNav(mMemoryButtonId, UI_DIR_RIGHT, mPlayButtonId);
AddNav(mAboutButtonId, UI_DIR_LEFT, mPlayButtonId);
@@ -219,6 +225,12 @@
->SetFontScale(BUTTON_FONT_SCALE)->SetIsButton(true)
->SetTransition(UiWidget::TRANS_FROM_RIGHT)->GetId();
+ // memory button
+ mMemoryButtonId = NewWidget()->SetTextColor(BUTTON_COLOR)->SetText(S_MEMORY)
+ ->SetCenter(BUTTON_MEMORY_POS)->SetSize(BUTTON_SIDEBUTTON_SIZE)
+ ->SetFontScale(BUTTON_FONT_SCALE)->SetIsButton(true)
+ ->SetTransition(UiWidget::TRANS_FROM_RIGHT)->GetId();
+
// about button
mAboutButtonId = NewWidget()->SetTextColor(BUTTON_COLOR)->SetText(S_ABOUT)
->SetCenter(BUTTON_ABOUT_POS)->SetSize(BUTTON_SIDEBUTTON_SIZE)
diff --git a/samples/agdktunnel/app/src/main/cpp/welcome_scene.hpp b/samples/agdktunnel/app/src/main/cpp/welcome_scene.hpp
index 7c90c8c..c46f2bb 100644
--- a/samples/agdktunnel/app/src/main/cpp/welcome_scene.hpp
+++ b/samples/agdktunnel/app/src/main/cpp/welcome_scene.hpp
@@ -44,6 +44,7 @@
UiWidget *mNameEdit;
int mTestButtonId;
int mQuitButtonId;
+ int mMemoryButtonId;
OwnedGameTextInputState mTextInputState;
@@ -78,4 +79,3 @@
};
#endif
-
diff --git a/samples/agdktunnel/app/src/main/java/com/google/sample/agdktunnel/AGDKTunnelActivity.java b/samples/agdktunnel/app/src/main/java/com/google/sample/agdktunnel/AGDKTunnelActivity.java
index 6906405..ad4f905 100644
--- a/samples/agdktunnel/app/src/main/java/com/google/sample/agdktunnel/AGDKTunnelActivity.java
+++ b/samples/agdktunnel/app/src/main/java/com/google/sample/agdktunnel/AGDKTunnelActivity.java
@@ -18,6 +18,7 @@
import static android.view.inputmethod.EditorInfo.IME_ACTION_NONE;
import static android.view.inputmethod.EditorInfo.IME_FLAG_NO_FULLSCREEN;
+import android.content.pm.PackageManager;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
@@ -25,13 +26,24 @@
import android.text.InputType;
import android.view.View;
import android.view.WindowManager.LayoutParams;
+
+import androidx.annotation.Keep;
import androidx.core.view.WindowCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.core.view.WindowInsetsControllerCompat;
-import com.google.androidgamesdk.GameActivity;
+import com.google.androidgamesdk.GameActivity;
+import com.google.android.libraries.play.games.inputmapping.InputMappingClient;
+import com.google.android.libraries.play.games.inputmapping.InputMappingProvider;
+import com.google.android.libraries.play.games.inputmapping.Input;
+
+@Keep
public class AGDKTunnelActivity extends GameActivity {
+ private PGSManager mPGSManager;
+ private final String mPlayGamesPCSystemFeature =
+ "com.google.android.play.feature.HPE_EXPERIENCE";
+
// Some code to load our native library:
static {
// Load the STL first to workaround issues on old Android versions:
@@ -42,8 +54,62 @@
// See https://developer.android.com/ndk/guides/cpp-support#shared_runtimes
System.loadLibrary("c++_shared");
- // Load the game library:
- System.loadLibrary("game");
+ // Optional: reload the memory advice library explicitly (it will be loaded
+ // implicitly when loading agdktunnel library as a dependent library)
+ System.loadLibrary("memory_advice");
+
+ // Optional: reload the native library.
+ // However this is necessary when any of the following happens:
+ // - agdktunnel library is not configured to the following line in the manifest:
+ // <meta-data android:name="android.app.lib_name" android:value="agdktunnel" />
+ // - GameActivity derived class calls to the native code before calling
+ // the super.onCreate() function.
+ System.loadLibrary("agdktunnel");
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ // When true, the app will fit inside any system UI windows.
+ // When false, we render behind any system UI windows.
+ WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
+ hideSystemUI();
+ // You can set IME fields here or in native code using GameActivity_setImeEditorInfoFields.
+ // We set the fields in native_engine.cpp.
+ // super.setImeEditorInfoFields(InputType.TYPE_CLASS_TEXT,
+ // IME_ACTION_NONE, IME_FLAG_NO_FULLSCREEN );
+ super.onCreate(savedInstanceState);
+
+ if (isPlayGamesServicesLinked()) {
+ // Initialize Play Games Services
+ mPGSManager = new PGSManager(this);
+ }
+
+ if (isGooglePlayGames()) {
+ InputMappingProvider inputMappingProvider = new InputSDKProvider();
+ InputMappingClient inputMappingClient = Input.getInputMappingClient(this);
+ inputMappingClient.setInputMappingProvider(inputMappingProvider);
+ }
+ }
+
+ @Override
+ protected void onDestroy() {
+ if (isGooglePlayGames()) {
+ InputMappingClient inputMappingClient = Input.getInputMappingClient(this);
+ inputMappingClient.clearInputMappingProvider();
+ }
+
+ super.onDestroy();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ // To learn best practices to handle lifecycle events visit
+ // https://developer.android.com/topic/libraries/architecture/lifecycle
+ if (isPlayGamesServicesLinked()) {
+ mPGSManager.onResume();
+ }
}
private void hideSystemUI() {
@@ -64,16 +130,29 @@
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
}
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- // When true, the app will fit inside any system UI windows.
- // When false, we render behind any system UI windows.
- WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
- hideSystemUI();
- // You can set IME fields here or in native code using GameActivity_setImeEditorInfoFields.
- // We set the fields in native_engine.cpp.
- // super.setImeEditorInfoFields(InputType.TYPE_CLASS_TEXT,
- // IME_ACTION_NONE, IME_FLAG_NO_FULLSCREEN );
- super.onCreate(savedInstanceState);
+ private boolean isPlayGamesServicesLinked() {
+ String playGamesServicesPlaceholder = "0000000000";
+ return !getString(R.string.game_services_project_id).equals(playGamesServicesPlaceholder);
+ }
+
+ private void loadCloudCheckpoint() {
+ if (isPlayGamesServicesLinked()) {
+ mPGSManager.loadCheckpoint();
+ }
+ }
+
+ private void saveCloudCheckpoint(int level) {
+ if (isPlayGamesServicesLinked()) {
+ mPGSManager.saveCheckpoint(level);
+ }
+ }
+
+ private String getInternalStoragePath() {
+ return getFilesDir().getAbsolutePath();
+ }
+
+ private boolean isGooglePlayGames() {
+ PackageManager pm = getPackageManager();
+ return pm.hasSystemFeature(mPlayGamesPCSystemFeature);
}
}
diff --git a/samples/agdktunnel/app/src/main/java/com/google/sample/agdktunnel/InputSDKProvider.java b/samples/agdktunnel/app/src/main/java/com/google/sample/agdktunnel/InputSDKProvider.java
new file mode 100644
index 0000000..eb94be9
--- /dev/null
+++ b/samples/agdktunnel/app/src/main/java/com/google/sample/agdktunnel/InputSDKProvider.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.sample.agdktunnel;
+
+import android.view.KeyEvent;
+import com.google.android.libraries.play.games.inputmapping.datamodel.InputGroup;
+import com.google.android.libraries.play.games.inputmapping.InputMappingProvider;
+import com.google.android.libraries.play.games.inputmapping.datamodel.InputAction;
+import com.google.android.libraries.play.games.inputmapping.datamodel.InputControls;
+import com.google.android.libraries.play.games.inputmapping.datamodel.InputMap;
+import com.google.android.libraries.play.games.inputmapping.datamodel.MouseSettings;
+
+import java.util.Arrays;
+import java.util.Collections;
+
+public class InputSDKProvider implements InputMappingProvider {
+ public enum InputEventIds {
+ MOVE_UP,
+ MOVE_LEFT,
+ MOVE_DOWN,
+ MOVE_RIGHT,
+ MOUSE_MOVEMENT,
+ }
+
+ @Override
+ public InputMap onProvideInputMap() {
+ InputAction moveUpInputAction = InputAction.create(
+ "Move Up",
+ InputEventIds.MOVE_UP.ordinal(),
+ InputControls.create(
+ Collections.singletonList(KeyEvent.KEYCODE_W),
+ Collections.emptyList()
+ )
+ );
+
+ InputAction moveLeftInputAction = InputAction.create(
+ "Move Left",
+ InputEventIds.MOVE_LEFT.ordinal(),
+ InputControls.create(
+ Collections.singletonList(KeyEvent.KEYCODE_A),
+ Collections.emptyList()
+ )
+ );
+
+ InputAction moveDownInputAction = InputAction.create(
+ "Move Down",
+ InputEventIds.MOVE_DOWN.ordinal(),
+ InputControls.create(
+ Collections.singletonList(KeyEvent.KEYCODE_S),
+ Collections.emptyList()
+ )
+ );
+
+ InputAction moveRightInputAction = InputAction.create(
+ "Move Right",
+ InputEventIds.MOVE_RIGHT.ordinal(),
+ InputControls.create(
+ Collections.singletonList(KeyEvent.KEYCODE_D),
+ Collections.emptyList()
+ )
+ );
+
+ InputGroup movementInputGroup = InputGroup.create(
+ "Basic movement",
+ Arrays.asList(
+ moveUpInputAction,
+ moveLeftInputAction,
+ moveDownInputAction,
+ moveRightInputAction
+ )
+ );
+
+ InputAction mouseInputAction = InputAction.create(
+ "Move",
+ InputEventIds.MOUSE_MOVEMENT.ordinal(),
+ InputControls.create(
+ Collections.emptyList(),
+ Collections.singletonList(InputControls.MOUSE_LEFT_CLICK)
+ )
+ );
+
+ InputGroup mouseMovementInputGroup = InputGroup.create(
+ "Mouse movement",
+ Collections.singletonList(mouseInputAction)
+ );
+
+ return InputMap.create(
+ Arrays.asList(movementInputGroup, mouseMovementInputGroup),
+ MouseSettings.create(true, false)
+ );
+ }
+}
\ No newline at end of file
diff --git a/samples/agdktunnel/app/src/main/java/com/google/sample/agdktunnel/PGSManager.java b/samples/agdktunnel/app/src/main/java/com/google/sample/agdktunnel/PGSManager.java
new file mode 100644
index 0000000..2fd448f
--- /dev/null
+++ b/samples/agdktunnel/app/src/main/java/com/google/sample/agdktunnel/PGSManager.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.sample.agdktunnel;
+
+import android.app.Activity;
+import android.util.Log;
+
+import com.google.android.gms.common.api.ApiException;
+import com.google.android.gms.games.AuthenticationResult;
+import com.google.android.gms.games.GamesClientStatusCodes;
+import com.google.android.gms.games.GamesSignInClient;
+import com.google.android.gms.games.PlayGames;
+import com.google.android.gms.games.PlayGamesSdk;
+import com.google.android.gms.games.SnapshotsClient;
+import com.google.android.gms.games.snapshot.Snapshot;
+import com.google.android.gms.games.snapshot.SnapshotMetadata;
+import com.google.android.gms.games.snapshot.SnapshotMetadataChange;
+import com.google.android.gms.tasks.Task;
+import com.google.android.gms.tasks.Tasks;
+
+public class PGSManager {
+
+ private final Activity mActivity;
+ private final SnapshotsClient mSnapshotClient;
+ private final GamesSignInClient mGamesSignInClient;
+ private final String mSnapshotName = "AGDKTunnelLevel";
+ // In the case of a conflict, the snapshot version with the highest progress will be used
+ private static final int mConflictResolutionPolicy =
+ SnapshotsClient.RESOLUTION_POLICY_HIGHEST_PROGRESS;
+
+ private final String TAG = "AGDKTunnel (PGS)";
+
+ PGSManager(Activity activity) {
+ mActivity = activity;
+ mSnapshotClient = PlayGames.getSnapshotsClient(mActivity);
+ mGamesSignInClient = PlayGames.getGamesSignInClient(mActivity);
+ PlayGamesSdk.initialize(mActivity);
+ }
+
+ public void onResume() {
+ loadCheckpoint();
+ }
+
+ void loadCheckpoint() {
+ if (isLoadingWorkInProgress()) {
+ // Nothing to do
+ Log.i(TAG, "Loading data task in progress detected.");
+ return;
+ }
+ Log.i(TAG, "Initializing loading data task.");
+ savedStateInitLoading();
+ // Make sure the user is authenticated before loading data
+ Log.i(TAG, "authenticating user to load cloud data.");
+ Task<AuthenticationResult> task = mGamesSignInClient.isAuthenticated();
+ task.continueWithTask(authTask -> {
+ if (!authTask.isSuccessful() || !authTask.getResult().isAuthenticated()) {
+ // try to sign-in
+ return mGamesSignInClient.signIn();
+ }
+ return authTask;
+ }).addOnCompleteListener(mActivity, authTask -> {
+ if (!authTask.isSuccessful() || !authTask.getResult().isAuthenticated()) {
+ Log.e(TAG, "the user can't be authenticated.");
+ authenticationFailed();
+ return;
+ }
+ // Signal authentication completed and schedule loading data task
+ Log.i(TAG, "authentication completed, loading data.");
+ authenticationCompleted();
+ loadSnapshot();
+ });
+ }
+
+ void saveCheckpoint(int level) {
+ // Make sure the user is authenticated before saving data
+ Log.i(TAG, "authenticating user to save cloud data.");
+ Task<AuthenticationResult> task = mGamesSignInClient.isAuthenticated();
+ task.continueWithTask(authTask -> {
+ if (!authTask.isSuccessful() || !authTask.getResult().isAuthenticated()) {
+ // try to sign-in
+ return mGamesSignInClient.signIn();
+ }
+ return authTask;
+ }).addOnCompleteListener(mActivity, authTask -> {
+ if (!authTask.isSuccessful() || !authTask.getResult().isAuthenticated()) {
+ Log.e(TAG, "the user can't be authenticated.");
+ return;
+ }
+ Log.i(TAG, "authentication completed, loading data.");
+ saveSnapshot(level);
+ });
+ }
+
+ // Signal the game to update status on cloud load
+ private native void savedStateInitLoading();
+ private native void authenticationCompleted();
+ private native void authenticationFailed();
+ private native void savedStateSnapshotNotFound();
+ private native void savedStateCloudDataFound();
+ private native void savedStateLoadingFailed();
+ private native void savedStateLoadingCompleted(int level);
+ private native boolean isLoadingWorkInProgress();
+
+ private Task<Void> loadSnapshot() {
+ // Open Snapshot using its name
+ return mSnapshotClient.open(
+ mSnapshotName, /* createIfNotFound= */ false, mConflictResolutionPolicy)
+ .addOnFailureListener(mActivity, e -> {
+ // Check if snapshot was not found
+ if (e instanceof ApiException) {
+ int status = ((ApiException) e).getStatusCode();
+ if (status == GamesClientStatusCodes.SNAPSHOT_NOT_FOUND) {
+ Log.i(TAG, "Level hasn't been saved");
+ savedStateSnapshotNotFound();
+ return;
+ }
+ }
+ Log.e(TAG, "Error while opening Snapshot to load data.", e);
+ savedStateLoadingFailed();
+ }).addOnSuccessListener(mActivity, task-> {
+ Log.i(TAG, "Snapshot has been found.");
+ savedStateCloudDataFound();
+ }).continueWithTask(task -> {
+ if (!task.isSuccessful()) {
+ return Tasks.forResult(null);
+ }
+
+ Log.i(TAG, "Attempting to load level from the cloud.");
+ Snapshot snapshot = task.getResult().getData();
+ // Opening the snapshot was a success and any conflicts have been resolved.
+ try {
+ // Extract the level from the Snapshot metadata
+ Log.i(TAG, "Reading the Snapshot content.");
+ SnapshotMetadata snapshotMetadata = snapshot.getMetadata();
+ int level = (int)snapshotMetadata.getProgressValue();
+ Log.i(TAG, "level to load:" + level);
+ savedStateLoadingCompleted(level);
+ } catch (NullPointerException e) {
+ Log.e(TAG, "Error while reading Snapshot content.", e);
+ savedStateLoadingFailed();
+ }
+ return mSnapshotClient.discardAndClose(snapshot);
+ });
+ }
+
+ private Task<SnapshotMetadata> saveSnapshot(int level) {
+ // Open Snapshot using its name
+ return mSnapshotClient.open(
+ mSnapshotName, /* createIfNotFound= */true, mConflictResolutionPolicy)
+ .addOnFailureListener(mActivity, e ->
+ Log.e(TAG, "Error while opening Snapshot to save data.", e))
+ .continueWithTask(task -> {
+ if (!task.isSuccessful()) {
+ return Tasks.forResult(null);
+ }
+
+ Log.i(TAG, "Attempting to save level on the cloud.");
+ Snapshot snapshot = task.getResult().getData();
+ if (snapshot == null) {
+ // Snapshot not available
+ return Tasks.forResult(null);
+ }
+
+ String description = "AGDK Tunnel checkpoint level.";
+ // Create the change operation
+ SnapshotMetadataChange metadataChange =
+ new SnapshotMetadataChange.Builder()
+ .setDescription(description)
+ .setProgressValue(level)
+ .build();
+ // Commit the operation
+ Task<SnapshotMetadata> metadataTask =
+ mSnapshotClient.commitAndClose(snapshot, metadataChange);
+ metadataTask.addOnCompleteListener(mActivity, saveTask -> {
+ if (saveTask.isSuccessful()) {
+ Log.i(TAG, "Snapshot saved!");
+ } else {
+ Log.e(TAG, "Snapshot was not saved");
+ }
+ });
+ return metadataTask;
+ });
+ }
+}
diff --git a/samples/agdktunnel/app/src/main/res/values/strings.xml b/samples/agdktunnel/app/src/main/res/values/strings.xml
index 9da2a41..4e19da4 100644
--- a/samples/agdktunnel/app/src/main/res/values/strings.xml
+++ b/samples/agdktunnel/app/src/main/res/values/strings.xml
@@ -17,5 +17,7 @@
<resources>
<string name="app_name">AGDKTunnel</string>
+ <!-- Replace 0000000000 with your game’s project id. -->
+ <string translatable="false" name="game_services_project_id"> 0000000000 </string>
</resources>
diff --git a/samples/agdktunnel/build.gradle b/samples/agdktunnel/build.gradle
index fe222c3..3ddf72b 100644
--- a/samples/agdktunnel/build.gradle
+++ b/samples/agdktunnel/build.gradle
@@ -22,7 +22,7 @@
mavenCentral()
}
dependencies {
- classpath 'com.android.tools.build:gradle:4.2.2'
+ classpath 'com.android.tools.build:gradle:7.2.2'
}
}
diff --git a/samples/agdktunnel/gradle.properties b/samples/agdktunnel/gradle.properties
index d5209fd..33b1205 100644
--- a/samples/agdktunnel/gradle.properties
+++ b/samples/agdktunnel/gradle.properties
@@ -15,7 +15,13 @@
#android.enableJetifier=true
android.useAndroidX=true
-# Use Prefab 1.1.3 containing the fix for header only libraries:
-android.prefabVersion=1.1.3
+android.prefabVersion=2.0.0
-GameSDKPath=../../..
\ No newline at end of file
+GameSDKPath=../../..
+
+# Set to true to build Android Performance Tuner runtime assets
+# and enable the library at runtime
+APTEnabled=false
+
+# Set to true to delivery optimized compressed assets using the Play Asset Delivery API
+PADEnabled=false
\ No newline at end of file
diff --git a/samples/agdktunnel/gradle/wrapper/gradle-wrapper.properties b/samples/agdktunnel/gradle/wrapper/gradle-wrapper.properties
index 79e72fb..9c2b96e 100644
--- a/samples/agdktunnel/gradle/wrapper/gradle-wrapper.properties
+++ b/samples/agdktunnel/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https://services.gradle.org/distributions/gradle-6.7.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip
\ No newline at end of file
diff --git a/samples/agdktunnel/app/src/main/cpp/game_activity_included.cpp b/samples/agdktunnel/install_time_assets/build.gradle
similarity index 65%
copy from samples/agdktunnel/app/src/main/cpp/game_activity_included.cpp
copy to samples/agdktunnel/install_time_assets/build.gradle
index 5cf0bd9..4784a6f 100644
--- a/samples/agdktunnel/app/src/main/cpp/game_activity_included.cpp
+++ b/samples/agdktunnel/install_time_assets/build.gradle
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 The Android Open Source Project
+ * Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,5 +14,14 @@
* limitations under the License.
*/
-// Include the GameActivity implementation
-#include "game-activity/GameActivity.cpp"
+// In the asset pack’s build.gradle file:
+plugins {
+ id 'com.android.asset-pack'
+}
+
+assetPack {
+ packName = "install_time_assets" // Directory name for the asset pack
+ dynamicDelivery {
+ deliveryType = "install-time"
+ }
+}
diff --git "a/samples/agdktunnel/install_time_assets/src/main/assets/textures\043tcf_dxt1/wall1.ktx" "b/samples/agdktunnel/install_time_assets/src/main/assets/textures\043tcf_dxt1/wall1.ktx"
new file mode 100644
index 0000000..0d356ce
--- /dev/null
+++ "b/samples/agdktunnel/install_time_assets/src/main/assets/textures\043tcf_dxt1/wall1.ktx"
Binary files differ
diff --git "a/samples/agdktunnel/install_time_assets/src/main/assets/textures\043tcf_dxt1/wall2.ktx" "b/samples/agdktunnel/install_time_assets/src/main/assets/textures\043tcf_dxt1/wall2.ktx"
new file mode 100644
index 0000000..0d356ce
--- /dev/null
+++ "b/samples/agdktunnel/install_time_assets/src/main/assets/textures\043tcf_dxt1/wall2.ktx"
Binary files differ
diff --git a/samples/agdktunnel/app/src/main/assets/textures/wall1.tex b/samples/agdktunnel/install_time_assets/src/main/assets/textures/wall1.ktx
similarity index 100%
rename from samples/agdktunnel/app/src/main/assets/textures/wall1.tex
rename to samples/agdktunnel/install_time_assets/src/main/assets/textures/wall1.ktx
Binary files differ
diff --git a/samples/agdktunnel/app/src/main/assets/textures/wall2.tex b/samples/agdktunnel/install_time_assets/src/main/assets/textures/wall2.ktx
similarity index 100%
rename from samples/agdktunnel/app/src/main/assets/textures/wall2.tex
rename to samples/agdktunnel/install_time_assets/src/main/assets/textures/wall2.ktx
Binary files differ
diff --git a/samples/agdktunnel/app/src/main/cpp/game_activity_included.cpp b/samples/agdktunnel/on_demand_assets/build.gradle
similarity index 69%
rename from samples/agdktunnel/app/src/main/cpp/game_activity_included.cpp
rename to samples/agdktunnel/on_demand_assets/build.gradle
index 5cf0bd9..e96f971 100644
--- a/samples/agdktunnel/app/src/main/cpp/game_activity_included.cpp
+++ b/samples/agdktunnel/on_demand_assets/build.gradle
@@ -1,5 +1,6 @@
+
/*
- * Copyright 2021 The Android Open Source Project
+ * Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,5 +15,13 @@
* limitations under the License.
*/
-// Include the GameActivity implementation
-#include "game-activity/GameActivity.cpp"
+plugins {
+ id 'com.android.asset-pack'
+}
+
+assetPack {
+ packName = "on_demand_assets" // Directory name for the asset pack
+ dynamicDelivery {
+ deliveryType = "on-demand"
+ }
+}
diff --git "a/samples/agdktunnel/on_demand_assets/src/main/assets/textures\043tcf_dxt1/wall3.ktx" "b/samples/agdktunnel/on_demand_assets/src/main/assets/textures\043tcf_dxt1/wall3.ktx"
new file mode 100644
index 0000000..1d56306
--- /dev/null
+++ "b/samples/agdktunnel/on_demand_assets/src/main/assets/textures\043tcf_dxt1/wall3.ktx"
Binary files differ
diff --git "a/samples/agdktunnel/on_demand_assets/src/main/assets/textures\043tcf_dxt1/wall4.ktx" "b/samples/agdktunnel/on_demand_assets/src/main/assets/textures\043tcf_dxt1/wall4.ktx"
new file mode 100644
index 0000000..c889e71
--- /dev/null
+++ "b/samples/agdktunnel/on_demand_assets/src/main/assets/textures\043tcf_dxt1/wall4.ktx"
Binary files differ
diff --git "a/samples/agdktunnel/on_demand_assets/src/main/assets/textures\043tcf_dxt1/wall5.ktx" "b/samples/agdktunnel/on_demand_assets/src/main/assets/textures\043tcf_dxt1/wall5.ktx"
new file mode 100644
index 0000000..b01e7dc
--- /dev/null
+++ "b/samples/agdktunnel/on_demand_assets/src/main/assets/textures\043tcf_dxt1/wall5.ktx"
Binary files differ
diff --git "a/samples/agdktunnel/on_demand_assets/src/main/assets/textures\043tcf_dxt1/wall6.ktx" "b/samples/agdktunnel/on_demand_assets/src/main/assets/textures\043tcf_dxt1/wall6.ktx"
new file mode 100644
index 0000000..dbd085d
--- /dev/null
+++ "b/samples/agdktunnel/on_demand_assets/src/main/assets/textures\043tcf_dxt1/wall6.ktx"
Binary files differ
diff --git "a/samples/agdktunnel/on_demand_assets/src/main/assets/textures\043tcf_dxt1/wall7.ktx" "b/samples/agdktunnel/on_demand_assets/src/main/assets/textures\043tcf_dxt1/wall7.ktx"
new file mode 100644
index 0000000..a2ca120
--- /dev/null
+++ "b/samples/agdktunnel/on_demand_assets/src/main/assets/textures\043tcf_dxt1/wall7.ktx"
Binary files differ
diff --git "a/samples/agdktunnel/on_demand_assets/src/main/assets/textures\043tcf_dxt1/wall8.ktx" "b/samples/agdktunnel/on_demand_assets/src/main/assets/textures\043tcf_dxt1/wall8.ktx"
new file mode 100644
index 0000000..c141ae1
--- /dev/null
+++ "b/samples/agdktunnel/on_demand_assets/src/main/assets/textures\043tcf_dxt1/wall8.ktx"
Binary files differ
diff --git a/samples/agdktunnel/app/src/main/assets/textures/wall3.tex b/samples/agdktunnel/on_demand_assets/src/main/assets/textures/wall3.ktx
similarity index 100%
rename from samples/agdktunnel/app/src/main/assets/textures/wall3.tex
rename to samples/agdktunnel/on_demand_assets/src/main/assets/textures/wall3.ktx
Binary files differ
diff --git a/samples/agdktunnel/app/src/main/assets/textures/wall4.tex b/samples/agdktunnel/on_demand_assets/src/main/assets/textures/wall4.ktx
similarity index 100%
rename from samples/agdktunnel/app/src/main/assets/textures/wall4.tex
rename to samples/agdktunnel/on_demand_assets/src/main/assets/textures/wall4.ktx
Binary files differ
diff --git a/samples/agdktunnel/app/src/main/assets/textures/wall5.tex b/samples/agdktunnel/on_demand_assets/src/main/assets/textures/wall5.ktx
similarity index 100%
rename from samples/agdktunnel/app/src/main/assets/textures/wall5.tex
rename to samples/agdktunnel/on_demand_assets/src/main/assets/textures/wall5.ktx
Binary files differ
diff --git a/samples/agdktunnel/app/src/main/assets/textures/wall6.tex b/samples/agdktunnel/on_demand_assets/src/main/assets/textures/wall6.ktx
similarity index 100%
rename from samples/agdktunnel/app/src/main/assets/textures/wall6.tex
rename to samples/agdktunnel/on_demand_assets/src/main/assets/textures/wall6.ktx
Binary files differ
diff --git a/samples/agdktunnel/app/src/main/assets/textures/wall7.tex b/samples/agdktunnel/on_demand_assets/src/main/assets/textures/wall7.ktx
similarity index 100%
rename from samples/agdktunnel/app/src/main/assets/textures/wall7.tex
rename to samples/agdktunnel/on_demand_assets/src/main/assets/textures/wall7.ktx
Binary files differ
diff --git a/samples/agdktunnel/app/src/main/assets/textures/wall8.tex b/samples/agdktunnel/on_demand_assets/src/main/assets/textures/wall8.ktx
similarity index 100%
rename from samples/agdktunnel/app/src/main/assets/textures/wall8.tex
rename to samples/agdktunnel/on_demand_assets/src/main/assets/textures/wall8.ktx
Binary files differ
diff --git a/samples/agdktunnel/settings.gradle b/samples/agdktunnel/settings.gradle
index be564b8..0abe7ed 100644
--- a/samples/agdktunnel/settings.gradle
+++ b/samples/agdktunnel/settings.gradle
@@ -15,3 +15,17 @@
*/
include ':app'
+include ':install_time_assets'
+include ':on_demand_assets'
+// Comment all the lines below to use a locally built .aar
+// See app/build.gradle as well
+include ':game-activity'
+include ':games-performance-tuner'
+include ':games-frame-pacing:extras'
+include ':games-frame-pacing'
+include ':games-controller'
+project(':game-activity').projectDir = file('../../game-activity')
+project(':games-performance-tuner').projectDir = file('../../games-performance-tuner')
+project(':games-frame-pacing').projectDir = file('../../games-frame-pacing')
+project(':games-frame-pacing:extras').projectDir = file('../../games-frame-pacing/extras')
+project(':games-controller').projectDir = file('../../games-controller')
diff --git a/samples/bouncyball/OWNERS b/samples/bouncyball/OWNERS
index d79d861..289ff34 100644
--- a/samples/bouncyball/OWNERS
+++ b/samples/bouncyball/OWNERS
@@ -1 +1 @@
-include ../../src/swappy/OWNERS
+include ../../games-frame-pacing/OWNERS
diff --git a/samples/bouncyball/app/CMakeLists.for-samples-in-archive.txt b/samples/bouncyball/app/CMakeLists.for-samples-in-archive.txt
index 260cc63..2575ff0 100644
--- a/samples/bouncyball/app/CMakeLists.for-samples-in-archive.txt
+++ b/samples/bouncyball/app/CMakeLists.for-samples-in-archive.txt
@@ -1,20 +1,14 @@
cmake_minimum_required(VERSION 3.4.1)
+project(bouncyball)
+
+find_package(games-frame-pacing REQUIRED CONFIG)
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Werror -Wthread-safety -D _LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS" )
-# ============== Games SDK
-
-# This uses Swappy as a static library from the Game SDK package. Make sure the Android
-# SDK and NDK versions that you are using are supported by the Game SDK.
-include("../../gamesdk.cmake")
-add_gamesdk_target(PACKAGE_DIR "../../../" BUILD_TYPE "Release")
-
-# ============== Bouncy Ball
-
include_directories( ../../common/include )
include_directories( src/main/cpp )
-add_library( native-lib
+add_library( ${CMAKE_PROJECT_NAME}
SHARED
@@ -25,11 +19,10 @@
../../common/src/Thread.cpp
)
-target_link_libraries( native-lib
-
+target_link_libraries( ${CMAKE_PROJECT_NAME}
android
EGL
GLESv2
log
- swappy
+ games-frame-pacing::swappy_static
)
diff --git a/samples/bouncyball/app/CMakeLists.txt b/samples/bouncyball/app/CMakeLists.txt
index 05092dc..02bebc8 100644
--- a/samples/bouncyball/app/CMakeLists.txt
+++ b/samples/bouncyball/app/CMakeLists.txt
@@ -1,28 +1,15 @@
cmake_minimum_required(VERSION 3.4.1)
+project(bouncyball)
+
+find_package(games-frame-pacing REQUIRED CONFIG)
+
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Werror -Wthread-safety -D _LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS" )
-# ============== Games SDK
-
-# This builds and uses swappy as a static library from a gamesdk package
-include("../../gamesdk.cmake")
-add_gamesdk_target(
- DO_LOCAL_BUILD
- LIBRARIES "swappy"
- ROOT_DIR "../../.."
- PACKAGE_DIR "../../../../package/local"
-)
-
-# Uncomment to add the Game SDK sources as part of the project sources, allowing to develop
-# (with auto completions) and debug Swappy from Android Studio using this sample.
-#add_gamesdk_sources()
-
-# ============== Bouncy Ball
-
include_directories( ../../common/include ) # Samples Includes
include_directories( src/main/cpp )
-add_library( native-lib
+add_library( ${CMAKE_PROJECT_NAME}
SHARED
@@ -33,11 +20,10 @@
../../common/src/Thread.cpp
)
-target_link_libraries( native-lib
-
+target_link_libraries( ${CMAKE_PROJECT_NAME}
android
EGL
GLESv2
log
- swappy
+ games-frame-pacing::swappy_static
)
diff --git a/samples/bouncyball/app/build.gradle b/samples/bouncyball/app/build.gradle
index f7acdd0..b66cf72 100644
--- a/samples/bouncyball/app/build.gradle
+++ b/samples/bouncyball/app/build.gradle
@@ -1,12 +1,12 @@
apply plugin: 'com.android.application'
android {
- compileSdkVersion 29
- ndkVersion "20.0.5594570"
+ compileSdkVersion 31
+ ndkVersion "23.1.7779620"
defaultConfig {
applicationId "com.prefabulated.swappy"
- minSdkVersion 16
- targetSdkVersion 29
+ minSdkVersion 19
+ targetSdkVersion 31
versionCode 4
versionName "1.1.2"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@@ -17,16 +17,10 @@
}
buildTypes {
debug {
- ndk {
- abiFilters "arm64-v8a", "armeabi-v7a", "x86"
- }
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
- ndk {
- abiFilters "arm64-v8a", "armeabi-v7a", "x86"
- }
}
}
externalNativeBuild {
@@ -41,6 +35,9 @@
lintOptions {
abortOnError false
}
+ buildFeatures {
+ prefab true
+ }
}
allprojects {
@@ -52,8 +49,13 @@
}
dependencies {
- implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'androidx.preference:preference:1.1.1'
- testImplementation 'junit:junit:4.12'
+
+ // Uncomment the line below (implementation fileTree..)
+ // and comment out "implementation project(":games-frame-pacing")"
+ // to use a locally built .aar
+ // See ../settings.gradle as well
+ //implementation fileTree(dir: '../../../', include: ['games-frame-pacing-release.aar'])
+ implementation project(':games-frame-pacing')
}
diff --git a/samples/bouncyball/app/src/main/AndroidManifest.xml b/samples/bouncyball/app/src/main/AndroidManifest.xml
index 0c6456b..711cac4 100644
--- a/samples/bouncyball/app/src/main/AndroidManifest.xml
+++ b/samples/bouncyball/app/src/main/AndroidManifest.xml
@@ -11,9 +11,10 @@
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:ignore="GoogleAppIndexingWarning"
- tools:targetApi="29">
+ tools:targetApi="31">
<activity
android:name=".OrbitActivity"
+ android:exported="true"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
diff --git a/samples/bouncyball/app/src/main/cpp/Renderer.cpp b/samples/bouncyball/app/src/main/cpp/Renderer.cpp
index faf14a0..193ab1a 100644
--- a/samples/bouncyball/app/src/main/cpp/Renderer.cpp
+++ b/samples/bouncyball/app/src/main/cpp/Renderer.cpp
@@ -67,7 +67,7 @@
void Renderer::start() {
- mSwappyEnabled = SwappyGL_isEnabled();
+ mSwappyEnabled = SwappyGL_isEnabled() && mSwappyEnabled;
mWorkerThread.run([this](ThreadState *threadState) {
threadState->isStarted = true;
diff --git a/samples/bouncyball/app/src/main/java/com/prefabulated/bouncyball/OrbitActivity.java b/samples/bouncyball/app/src/main/java/com/prefabulated/bouncyball/OrbitActivity.java
index 8ae811c..d08bc8f 100644
--- a/samples/bouncyball/app/src/main/java/com/prefabulated/bouncyball/OrbitActivity.java
+++ b/samples/bouncyball/app/src/main/java/com/prefabulated/bouncyball/OrbitActivity.java
@@ -56,9 +56,9 @@
public class OrbitActivity extends AppCompatActivity implements Choreographer.FrameCallback, SurfaceHolder.Callback {
- // Used to load the 'native-lib' library on application startup.
+ // Used to load the 'bouncyball' library on application startup.
static {
- System.loadLibrary("native-lib");
+ System.loadLibrary("bouncyball");
}
private static final long ONE_MS_IN_NS = 1000000;
@@ -278,6 +278,7 @@
// Get display metrics
WindowManager wm = getWindowManager();
+ @SuppressWarnings( "deprecation" )
Display display = wm.getDefaultDisplay();
float refreshRateHz = display.getRefreshRate();
Log.i(LOG_TAG, String.format("Refresh rate: %.1f Hz", refreshRateHz));
diff --git a/samples/bouncyball/app/src/main/java/com/prefabulated/bouncyball/SettingsFragment.java b/samples/bouncyball/app/src/main/java/com/prefabulated/bouncyball/SettingsFragment.java
index 1644d0e..e77f971 100644
--- a/samples/bouncyball/app/src/main/java/com/prefabulated/bouncyball/SettingsFragment.java
+++ b/samples/bouncyball/app/src/main/java/com/prefabulated/bouncyball/SettingsFragment.java
@@ -54,6 +54,7 @@
@Override
public void onCreatePreferences(Bundle bundle, String s) {
+ @SuppressWarnings( "deprecation" )
Display display = getActivity().getWindowManager().getDefaultDisplay();
mSwapIntervalKey = getResources().getString(R.string.swap_interval_key);
@@ -72,6 +73,7 @@
TreeSet<Float> fpsSet = new TreeSet<Float>();
if (Build.VERSION.SDK_INT >= 23) {
mCurrentMode = display.getMode();
+ @SuppressWarnings( "deprecation" )
Display.Mode[] supportedModes =
getActivity().getWindowManager().getDefaultDisplay().getSupportedModes();
for (Display.Mode mode : supportedModes) {
@@ -80,10 +82,15 @@
}
float refreshRate = mode.getRefreshRate();
for (int interval = 1; refreshRate / interval >= 20; interval++) {
- fpsSet.add(refreshRate / interval);
+ float fps = refreshRate/interval;
+ //Round the precision to avoid values like 20.000000 & 20.000002 as
+ // different fps since we operate with 1ms noise ranges anyways.
+ fps = (float) (Math.floor(fps * 1000) / 1000);
+ fpsSet.add(fps);
}
}
} else {
+ @SuppressWarnings( "deprecation" )
float refreshRate =
getActivity().getWindowManager().getDefaultDisplay().getRefreshRate();
for (int interval = 1; refreshRate / interval >= 20; interval++) {
diff --git a/samples/bouncyball/build.gradle b/samples/bouncyball/build.gradle
index 123abac..d6751df 100644
--- a/samples/bouncyball/build.gradle
+++ b/samples/bouncyball/build.gradle
@@ -3,10 +3,10 @@
buildscript {
repositories {
google()
- jcenter()
+ mavenCentral()
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.5.2'
+ classpath 'com.android.tools.build:gradle:7.2.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
@@ -15,7 +15,7 @@
allprojects {
repositories {
google()
- jcenter()
+ mavenCentral()
}
}
diff --git a/samples/bouncyball/gradle.properties b/samples/bouncyball/gradle.properties
index aac7c9b..7e7e4f2 100644
--- a/samples/bouncyball/gradle.properties
+++ b/samples/bouncyball/gradle.properties
@@ -10,8 +10,12 @@
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
+android.useAndroidX=true
+
+android.prefabVersion=2.0.0
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
+
diff --git a/samples/bouncyball/settings.gradle b/samples/bouncyball/settings.gradle
index e7b4def..00b855e 100644
--- a/samples/bouncyball/settings.gradle
+++ b/samples/bouncyball/settings.gradle
@@ -1 +1,7 @@
include ':app'
+// Comment all the lines below to use a locally built .aar
+// See app/build.gradle as well
+include ':games-frame-pacing'
+project(":games-frame-pacing").projectDir = file("../../games-frame-pacing")
+include ':games-frame-pacing:extras'
+project(":games-frame-pacing:extras").projectDir = file("../../games-frame-pacing/extras")
diff --git a/samples/cube/OWNERS b/samples/cube/OWNERS
index d79d861..289ff34 100644
--- a/samples/cube/OWNERS
+++ b/samples/cube/OWNERS
@@ -1 +1 @@
-include ../../src/swappy/OWNERS
+include ../../games-frame-pacing/OWNERS
diff --git a/samples/cube/build.gradle b/samples/cube/build.gradle
index 123abac..008f8ca 100644
--- a/samples/cube/build.gradle
+++ b/samples/cube/build.gradle
@@ -6,7 +6,7 @@
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.5.2'
+ classpath 'com.android.tools.build:gradle:4.2.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
diff --git a/samples/cube/gradle.properties b/samples/cube/gradle.properties
index aac7c9b..6dd0218 100644
--- a/samples/cube/gradle.properties
+++ b/samples/cube/gradle.properties
@@ -10,6 +10,7 @@
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
+android.useAndroidX=true
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
diff --git a/samples/device_info_app/app/build.gradle b/samples/device_info_app/app/build.gradle
index 219e2f1..2ab1774 100644
--- a/samples/device_info_app/app/build.gradle
+++ b/samples/device_info_app/app/build.gradle
@@ -5,7 +5,7 @@
compileSdkVersion 26
defaultConfig {
applicationId "google.com.deviceinfotest"
- minSdkVersion 16
+ minSdkVersion 19
targetSdkVersion 26
versionCode 1
versionName "1.0"
diff --git a/samples/device_info_app/build.gradle b/samples/device_info_app/build.gradle
index 798ee14..e3f1a23 100644
--- a/samples/device_info_app/build.gradle
+++ b/samples/device_info_app/build.gradle
@@ -7,7 +7,7 @@
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.3.2'
+ classpath 'com.android.tools.build:gradle:4.2.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
diff --git a/samples/device_info_app/gradle/wrapper/gradle-wrapper.properties b/samples/device_info_app/gradle/wrapper/gradle-wrapper.properties
index 3b0ff41..ef073b5 100644
--- a/samples/device_info_app/gradle/wrapper/gradle-wrapper.properties
+++ b/samples/device_info_app/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
diff --git a/samples/game_controller/README.md b/samples/game_controller/README.md
index c8eb05c..35699c8 100644
--- a/samples/game_controller/README.md
+++ b/samples/game_controller/README.md
@@ -20,7 +20,10 @@
1. Open a terminal and set the working directory to `samples/game_controller/`
2. `cd third-party` (mkdir if it doesn't exist)
-3. `git clone -b v1.80 https://github.com/ocornut/imgui`
+3. `git clone https://github.com/ocornut/imgui`
+4. `cd imgui`
+5. `git fetch --all --tags`
+6. `git checkout tags/v1.80 -b v1.80`
## Building
diff --git a/samples/game_controller/common/demo_scene.cpp b/samples/game_controller/common/demo_scene.cpp
index 9ce5faf..af007ff 100644
--- a/samples/game_controller/common/demo_scene.cpp
+++ b/samples/game_controller/common/demo_scene.cpp
@@ -30,12 +30,12 @@
}
#define ARRAY_COUNTOF(array) (sizeof(array) / sizeof(array[0]))
-#define LOG_TAG "DemoScene"
namespace {
const char *controllerTabNames[PADDLEBOAT_MAX_CONTROLLERS] = {" #1 ", " #2 ", " #3 ", " #4 ",
" #5 ", " #6 ", " #7 ", " #8 "};
+ const char *integratedTabName = " #I ";
typedef void(DemoScene::*ControllerCategoryRenderFunction)(const int32_t,
const Paddleboat_Controller_Data&, const Paddleboat_Controller_Info&);
@@ -80,6 +80,27 @@
}
}
+ void KeyboardCallback(bool keyboardConnected, void *userData) {
+ if (userData != nullptr) {
+ DemoScene *scene = reinterpret_cast<DemoScene *>(userData);
+ scene->KeyboardStatusEvent(keyboardConnected);
+ }
+
+ }
+
+ void UpdateMotionData(const Paddleboat_Motion_Data *motionData, uint64_t *previousTimestamp,
+ uint32_t *timestampDelta, float *destMotionData) {
+ if (*previousTimestamp > 0) {
+ uint64_t deltaTime = motionData->timestamp - (*previousTimestamp);
+ // nanoseconds to milliseconds
+ *timestampDelta = static_cast<uint32_t>(deltaTime / 1000000);
+ }
+ destMotionData[0] = motionData->motionX;
+ destMotionData[1] = motionData->motionY;
+ destMotionData[2] = motionData->motionZ;
+ *previousTimestamp = motionData->timestamp;
+ }
+
void VibrationParameters(const char *labelText, const char *labelTag, const float vMin,
const float vMax, const float vStep, float *vValue) {
char plusString[16];
@@ -128,17 +149,25 @@
mActiveControllerPanelTab = 0;
mPreviousControllerDataTimestamp = 0;
mPreviousMouseDataTimestamp = 0;
+ mIntegratedSensorFlags = PADDLEBOAT_INTEGRATED_SENSOR_NONE;
mPreviousAccelerometerTimestamp = 0;
mAccelerometerTimestampDelta = 0;
mPreviousGyroscopeTimestamp = 0;
mGyroscopeTimestampDelta = 0;
- for (int i = 0; i < DemoScene::MOTION_AXIS_COUNT; ++i) {
+ mPreviousIntegratedAccelerometerTimestamp = 0;
+ mIntegratedAccelerometerTimestampDelta = 0;
+ mPreviousIntegratedGyroscopeTimestamp = 0;
+ mIntegratedGyroscopeTimestampDelta = 0;
+ for (size_t i = 0; i < DemoScene::MOTION_AXIS_COUNT; ++i) {
mAccelerometerData[i] = 0.0f;
mGyroscopeData[i] = 0.0f;
+ mIntegratedAccelerometerData[i] = 0.0f;
+ mIntegratedGyroscopeData[i] = 0.0f;
}
mDontTrimDeadzone = false;
mPreferencesActive = false;
mRegisteredStatusCallback = false;
+ mKeyboardConnected = false;
}
DemoScene::~DemoScene() {
@@ -151,7 +180,7 @@
void DemoScene::OnKillGraphics() {
}
-void DemoScene::OnScreenResized(int width, int height) {
+void DemoScene::OnScreenResized(int /*width*/, int /*height*/) {
}
void DemoScene::DoFrame() {
@@ -180,11 +209,11 @@
// base classes override this to draw background
}
-void DemoScene::OnButtonClicked(int buttonId) {
+void DemoScene::OnButtonClicked(int /*buttonId*/) {
// base classes override this to react to button clicks
}
-void DemoScene::OnPointerDown(int pointerId, const struct PointerCoords *coords) {
+void DemoScene::OnPointerDown(int /*pointerId*/, const struct PointerCoords *coords) {
// If this event was generated by something that's not associated to the screen,
// (like a trackpad), ignore it, because our UI is not driven that way.
if (coords->isScreen) {
@@ -194,14 +223,14 @@
}
}
-void DemoScene::OnPointerMove(int pointerId, const struct PointerCoords *coords) {
+void DemoScene::OnPointerMove(int /*pointerId*/, const struct PointerCoords *coords) {
if (coords->isScreen && mPointerDown) {
mPointerX = coords->x;
mPointerY = coords->y;
}
}
-void DemoScene::OnPointerUp(int pointerId, const struct PointerCoords *coords) {
+void DemoScene::OnPointerUp(int /*pointerId*/, const struct PointerCoords *coords) {
if (coords->isScreen) {
mPointerX = coords->x;
mPointerY = coords->y;
@@ -233,8 +262,9 @@
SetupUIWindow();
if (!RenderPreferences()) {
- // Display mouse data on the same line as the preferences button
+ // Display keyboard/mouse data on the same line as the preferences button
ImGui::SameLine(0.0f, 48.0f);
+ RenderKeyboardData();
RenderMouseData();
RenderControllerTabs();
}
@@ -357,19 +387,39 @@
ImGui::PopStyleColor(1);
}
}
+
+ // Add an integrated device stats tab item
+ RenderIntegratedTab();
ImGui::EndTabBar();
}
}
+void DemoScene::RenderIntegratedTab() {
+ if (ImGui::BeginTabItem(integratedTabName, NULL, ImGuiTabItemFlags_None)) {
+ mCurrentControllerIndex = PADDLEBOAT_MAX_CONTROLLERS;
+ RenderIntegratedPanel();
+ ImGui::EndTabItem();
+ }
+}
+
+void DemoScene::RenderKeyboardData() {
+ if (mKeyboardConnected) {
+ ImGui::Text("K:Y ");
+ } else {
+ ImGui::Text("K:N ");
+ }
+ ImGui::SameLine();
+}
+
void DemoScene::RenderMouseData() {
const Paddleboat_MouseStatus mouseStatus = Paddleboat_getMouseStatus();
if (mouseStatus == PADDLEBOAT_MOUSE_NONE) {
- ImGui::Text("Mouse: No");
+ ImGui::Text("M:No");
return;
} else if (mouseStatus == PADDLEBOAT_MOUSE_CONTROLLER_EMULATED) {
- ImGui::Text("Mouse: Vir ");
+ ImGui::Text("M:Vi ");
} else if (mouseStatus == PADDLEBOAT_MOUSE_PHYSICAL) {
- ImGui::Text("Mouse: Phy ");
+ ImGui::Text("M:Ph ");
}
ImGui::SameLine();
Paddleboat_Mouse_Data mouseData;
@@ -429,6 +479,72 @@
}
}
+void DemoScene::RenderIntegratedMotionData() {
+ // Motion axis
+ float motionData[6] = {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
+ const bool hasAccel =
+ ((mIntegratedSensorFlags & PADDLEBOAT_INTEGRATED_SENSOR_ACCELEROMETER) != 0);
+ const bool hasGyro =
+ ((mIntegratedSensorFlags & PADDLEBOAT_INTEGRATED_SENSOR_GYROSCOPE) != 0);;
+ if (hasAccel) {
+ motionData[0] = mIntegratedAccelerometerData[0];
+ motionData[2] = mIntegratedAccelerometerData[1];
+ motionData[4] = mIntegratedAccelerometerData[2];
+ }
+
+ if (hasGyro) {
+ motionData[1] = mIntegratedGyroscopeData[0];
+ motionData[3] = mIntegratedGyroscopeData[1];
+ motionData[5] = mIntegratedGyroscopeData[2];
+ }
+ ImGui::Text("%s", "Integrated sensors");
+ RenderMotionTableData(motionData, mIntegratedAccelerometerTimestampDelta,
+ mIntegratedGyroscopeTimestampDelta, hasAccel, hasGyro);
+}
+
+void DemoScene::RenderMotionTableData(const float *motionData, const uint32_t accelTimestampDelta,
+ const uint32_t gyroTimestampDelta,
+ bool hasAccel, bool hasGyro) {
+ if (ImGui::BeginTable("##motiontable", 2, ImGuiTableColumnFlags_WidthFixed,
+ ImVec2(0.0f, ImGui::GetTextLineHeightWithSpacing() * 4.5f))) {
+ ImGui::TableSetupColumn("Accelerometer ", 0);
+ ImGui::TableSetupColumn("Gyroscope ", 0);
+ ImGui::TableHeadersRow();
+ for (size_t i = 0; i < 4; ++i) {
+ ImGui::TableNextRow();
+ ImGui::TableNextColumn();
+ if (hasAccel) {
+ if (i == 0) {
+ ImGui::Text("%4u", accelTimestampDelta);
+ } else {
+ ImGui::Text("%.3f", motionData[((i - 1) * 2)]);
+ }
+ } else if (i == 0) {
+ ImGui::Text("None");
+ }
+ ImGui::TableNextColumn();
+ if (hasGyro) {
+ if (i == 0) {
+ ImGui::Text("%4u", gyroTimestampDelta);
+ } else {
+ ImGui::Text("%.3f", motionData[((i - 1) * 2) + 1]);
+ }
+ } else if (i == 0) {
+ ImGui::Text("None");
+ }
+ }
+ ImGui::EndTable();
+ }
+}
+
+void DemoScene::RenderIntegratedPanel() {
+ ImGui::Text("%s", "Integrated (non-controller) device info");
+ if (mIntegratedSensorFlags != PADDLEBOAT_INTEGRATED_SENSOR_NONE) {
+ ImGui::Text("%s", "Motion data:");
+ RenderIntegratedMotionData();
+ }
+}
+
void DemoScene::RenderPanel(const int32_t controllerIndex) {
if (mActiveControllers[controllerIndex]) {
Paddleboat_Controller_Data controllerData;
@@ -570,7 +686,7 @@
ControllerUIUtil::TriggerBar(panelParams, draw_list, UIBUTTON_R2, controllerData.triggerR2);
}
-void DemoScene::RenderPanel_ControlsTab_ArcadeStick(const int32_t controllerIndex,
+void DemoScene::RenderPanel_ControlsTab_ArcadeStick(const int32_t /*controllerIndex*/,
const Paddleboat_Controller_Data &controllerData,
const Paddleboat_Controller_Info &controllerInfo) {
const float leftX = controllerData.leftStick.stickX;
@@ -614,7 +730,7 @@
}
-void DemoScene::RenderPanel_InfoTab(const int32_t controllerIndex,
+void DemoScene::RenderPanel_InfoTab(const int32_t /*controllerIndex*/,
const Paddleboat_Controller_Data &controllerData,
const Paddleboat_Controller_Info &controllerInfo) {
// Render vendorId/deviceId in green if they matched with a controller map entry,
@@ -694,7 +810,7 @@
}
void DemoScene::RenderPanel_VibrationTab(const int32_t controllerIndex,
- const Paddleboat_Controller_Data &controllerData,
+ const Paddleboat_Controller_Data &/*controllerData*/,
const Paddleboat_Controller_Info &controllerInfo) {
if ((controllerInfo.controllerFlags & PADDLEBOAT_CONTROLLER_FLAG_VIBRATION) != 0) {
if ((controllerInfo.controllerFlags & PADDLEBOAT_CONTROLLER_FLAG_VIBRATION_DUAL_MOTOR)
@@ -732,8 +848,8 @@
}
}
-void DemoScene::RenderPanel_MotionTab(const int32_t controllerIndex,
- const Paddleboat_Controller_Data &controllerData,
+void DemoScene::RenderPanel_MotionTab(const int32_t /*controllerIndex*/,
+ const Paddleboat_Controller_Data &/*controllerData*/,
const Paddleboat_Controller_Info &controllerInfo) {
// Motion axis
float motionData[6] = {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
@@ -752,41 +868,12 @@
motionData[3] = mGyroscopeData[1];
motionData[5] = mGyroscopeData[2];
}
-
- if (ImGui::BeginTable("##motiontable", 2, ImGuiTableFlags_ColumnsWidthFixed,
- ImVec2(0.0f, ImGui::GetTextLineHeightWithSpacing() * 4.5f))) {
- ImGui::TableSetupColumn("Accelerometer ", ImGuiTableColumnFlags_WidthAutoResize);
- ImGui::TableSetupColumn("Gyroscope ", ImGuiTableColumnFlags_WidthAutoResize);
- ImGui::TableHeadersRow();
- for (size_t i = 0; i < 4; ++i) {
- ImGui::TableNextRow();
- ImGui::TableNextColumn();
- if (hasAccel) {
- if (i == 0) {
- ImGui::Text("%4u", mAccelerometerTimestampDelta);
- } else {
- ImGui::Text("%.3f", motionData[((i - 1) * 2)]);
- }
- } else if (i == 0) {
- ImGui::Text("None");
- }
- ImGui::TableNextColumn();
- if (hasGyro) {
- if (i == 0) {
- ImGui::Text("%4u", mGyroscopeTimestampDelta);
- } else {
- ImGui::Text("%.3f", motionData[((i - 1) * 2) + 1]);
- }
- } else if (i == 0) {
- ImGui::Text("None");
- }
- }
- ImGui::EndTable();
- }
+ RenderMotionTableData(motionData, mAccelerometerTimestampDelta,
+ mGyroscopeTimestampDelta, hasAccel, hasGyro);
}
void DemoScene::RenderPanel_LightsTab(const int32_t controllerIndex,
- const Paddleboat_Controller_Data &controllerData,
+ const Paddleboat_Controller_Data &/*controllerData*/,
const Paddleboat_Controller_Info &controllerInfo) {
if ((controllerInfo.controllerFlags & PADDLEBOAT_CONTROLLER_FLAG_LIGHT_PLAYER) != 0) {
static int playerIndex = 1;
@@ -917,41 +1004,48 @@
ALOGI("Paddleboat_ControllerStatusEvent index: %d status: %s", controllerIndex, statusString);
}
+void DemoScene::KeyboardStatusEvent(const bool keyboardConnected) {
+ mKeyboardConnected = keyboardConnected;
+}
+
void DemoScene::MotionDataEvent(const int32_t controllerIndex,
const Paddleboat_Motion_Data *motionData) {
- if (controllerIndex == mCurrentControllerIndex) {
+ if (controllerIndex == PADDLEBOAT_INTEGRATED_SENSOR_INDEX) {
if (motionData->motionType == PADDLEBOAT_MOTION_ACCELEROMETER) {
- if (mPreviousAccelerometerTimestamp > 0) {
- uint64_t deltaTime = motionData->timestamp - mPreviousAccelerometerTimestamp;
- // nanoseconds to milliseconds
- mAccelerometerTimestampDelta = static_cast<uint32_t>(deltaTime / 1000000);
- }
- mAccelerometerData[0] = motionData->motionX;
- mAccelerometerData[1] = motionData->motionY;
- mAccelerometerData[2] = motionData->motionZ;
- mPreviousAccelerometerTimestamp = motionData->timestamp;
+ UpdateMotionData(motionData, &mPreviousIntegratedAccelerometerTimestamp,
+ &mIntegratedAccelerometerTimestampDelta,
+ mIntegratedAccelerometerData);
} else if (motionData->motionType == PADDLEBOAT_MOTION_GYROSCOPE) {
- if (mPreviousGyroscopeTimestamp > 0) {
- uint64_t deltaTime = motionData->timestamp - mPreviousGyroscopeTimestamp;
- // nanoseconds to milliseconds
- mGyroscopeTimestampDelta = static_cast<uint32_t>(deltaTime / 1000000);
- }
- mGyroscopeData[0] = motionData->motionX;
- mGyroscopeData[1] = motionData->motionY;
- mGyroscopeData[2] = motionData->motionZ;
- mPreviousGyroscopeTimestamp = motionData->timestamp;
+ UpdateMotionData(motionData, &mPreviousIntegratedGyroscopeTimestamp,
+ &mIntegratedGyroscopeTimestampDelta,
+ mIntegratedGyroscopeData);
+ }
+ } else if (controllerIndex == mCurrentControllerIndex) {
+ if (motionData->motionType == PADDLEBOAT_MOTION_ACCELEROMETER) {
+ UpdateMotionData(motionData, &mPreviousAccelerometerTimestamp,
+ &mAccelerometerTimestampDelta,
+ mAccelerometerData);
+ } else if (motionData->motionType == PADDLEBOAT_MOTION_GYROSCOPE) {
+ UpdateMotionData(motionData, &mPreviousGyroscopeTimestamp,
+ &mGyroscopeTimestampDelta,
+ mGyroscopeData);
}
}
}
void DemoScene::OnInstall() {
+ mIntegratedSensorFlags = Paddleboat_getIntegratedMotionSensorFlags();
+
Paddleboat_setControllerStatusCallback(GameControllerCallback, this);
- Paddleboat_setMotionDataCallback(MotionDataCallback, this);
+ Paddleboat_setMotionDataCallbackWithIntegratedFlags(MotionDataCallback,
+ mIntegratedSensorFlags, this);
+ Paddleboat_setPhysicalKeyboardStatusCallback(KeyboardCallback, this);
mRegisteredStatusCallback = true;
}
void DemoScene::OnUninstall() {
Paddleboat_setControllerStatusCallback(nullptr, nullptr);
Paddleboat_setMotionDataCallback(nullptr, nullptr);
+ Paddleboat_setPhysicalKeyboardStatusCallback(nullptr, nullptr);
mRegisteredStatusCallback = false;
}
diff --git a/samples/game_controller/common/demo_scene.hpp b/samples/game_controller/common/demo_scene.hpp
index 28e16f9..26e930b 100644
--- a/samples/game_controller/common/demo_scene.hpp
+++ b/samples/game_controller/common/demo_scene.hpp
@@ -83,6 +83,9 @@
// Current mouse data timestamp delta
uint64_t mMouseDataTimestampDelta;
+ // Which integrated sensors are present
+ Paddleboat_Integrated_Motion_Sensor_Flags mIntegratedSensorFlags;
+
// Accelerometer data previous timestamp and delta
uint64_t mPreviousAccelerometerTimestamp;
uint32_t mAccelerometerTimestampDelta;
@@ -91,12 +94,26 @@
uint64_t mPreviousGyroscopeTimestamp;
uint32_t mGyroscopeTimestampDelta;
+ // Integrated accelerometer data previous timestamp and delta
+ uint64_t mPreviousIntegratedAccelerometerTimestamp;
+ uint32_t mIntegratedAccelerometerTimestampDelta;
+
+ // Integrated gyroscope data previous timestamp and delta
+ uint64_t mPreviousIntegratedGyroscopeTimestamp;
+ uint32_t mIntegratedGyroscopeTimestampDelta;
+
// Most recently reported accelerometer data
float mAccelerometerData[MOTION_AXIS_COUNT];
// Most recently reported gyroscope data
float mGyroscopeData[MOTION_AXIS_COUNT];
+ // Most recently reported integrated accelerometer data
+ float mIntegratedAccelerometerData[MOTION_AXIS_COUNT];
+
+ // Most recently reported integrated gyroscope data
+ float mIntegratedGyroscopeData[MOTION_AXIS_COUNT];
+
// Preferences display is active
bool mPreferencesActive;
@@ -106,6 +123,9 @@
// Registered controller status callback
bool mRegisteredStatusCallback;
+ // Keyboard is connected
+ bool mKeyboardConnected;
+
// must be implemented by subclass
virtual void OnButtonClicked(int buttonId);
@@ -122,10 +142,23 @@
bool RenderPreferences();
+ void RenderKeyboardData();
+
void RenderMouseData();
+ void RenderIntegratedMotionData();
+
+ void RenderMotionTableData(const float *motionData,
+ const uint32_t accelTimestampDelta,
+ const uint32_t gyroTimestampDelta,
+ bool hasAccel, bool hasGyro);
+
void RenderControllerTabs();
+ void RenderIntegratedTab();
+
+ void RenderIntegratedPanel();
+
void RenderPanel(const int32_t controllerIndex);
void RenderPanel_ControlsTab(const int32_t controllerIndex,
@@ -162,6 +195,8 @@
void GameControllerStatusEvent(const int32_t controllerIndex,
const Paddleboat_ControllerStatus status);
+ void KeyboardStatusEvent(const bool keyboardConnected);
+
void MotionDataEvent(const int32_t controllerIndex,
const Paddleboat_Motion_Data *motionData);
diff --git a/samples/game_controller/common/scene.cpp b/samples/game_controller/common/scene.cpp
index ffcc4bd..d2a84c1 100644
--- a/samples/game_controller/common/scene.cpp
+++ b/samples/game_controller/common/scene.cpp
@@ -29,13 +29,13 @@
void Scene::OnKillGraphics() {}
-void Scene::OnPointerDown(int pointerId, const struct PointerCoords *coords) {}
+void Scene::OnPointerDown(int /*pointerId*/, const struct PointerCoords */*coords*/) {}
-void Scene::OnPointerUp(int pointerId, const struct PointerCoords *coords) {}
+void Scene::OnPointerUp(int /*pointerId*/, const struct PointerCoords */*coords*/) {}
-void Scene::OnPointerMove(int pointerId, const struct PointerCoords *coords) {}
+void Scene::OnPointerMove(int /*pointerId*/, const struct PointerCoords */*coords*/) {}
-void Scene::OnScreenResized(int width, int height) {}
+void Scene::OnScreenResized(int /*width*/, int /*height*/) {}
void Scene::OnPause() {}
diff --git a/samples/game_controller/gameactivity/app/build.gradle b/samples/game_controller/gameactivity/app/build.gradle
index 4fe2c0a..8d12989 100644
--- a/samples/game_controller/gameactivity/app/build.gradle
+++ b/samples/game_controller/gameactivity/app/build.gradle
@@ -17,15 +17,15 @@
apply plugin: 'com.android.application'
android {
- compileSdkVersion 30
+ compileSdkVersion 31
ndkVersion '21.4.7075529'
defaultConfig {
applicationId 'com.google.android.games.paddleboat.gamecontrollersample'
- minSdkVersion 16
- targetSdkVersion 30
- versionCode 1
- versionName '1.0'
+ minSdkVersion 19
+ targetSdkVersion 31
+ versionCode 2
+ versionName '1.1'
externalNativeBuild {
cmake {
arguments '-DANDROID_STL=c++_shared'
@@ -41,8 +41,8 @@
}
externalNativeBuild {
cmake {
- version '3.10.2'
path 'src/main/cpp/CMakeLists.txt'
+ version "3.18.1+"
}
}
buildFeatures {
@@ -62,14 +62,12 @@
implementation 'androidx.fragment:fragment:1.2.5'
implementation "com.android.ndk.thirdparty:libpng:1.6.37-alpha-1"
- // A locally built GameController.aar and GameActivity.aar
- // are expected to be located in app/libs/
- // See the gamesdk README for information on generating a local build
- // You can comment out this line and uncomment the androidx
- // implementations to use Maven. Be aware that this may target a future
- // release which does not yet exist, which is why a locally build
- // library is the default option.
- implementation fileTree(dir: 'libs', include: ['GameActivity.aar', 'GameController.aar'])
- //implementation "androidx.games:games-activity:1.1.0"
- //implementation "androidx.games:games-controller:1.1.0"
-}
\ No newline at end of file
+ // Uncomment the line below (implementation fileTree...) and
+ // comment out "implementation project(":games-controller")"
+ // to use a locally built .aar
+ // See ../settings.gradle as well.
+ //implementation fileTree(dir: '../../../../', include: ['games-controller-release.aar',
+ // 'game-activity-release.aar'])
+ implementation project(":game-activity")
+ implementation project(":games-controller")
+}
diff --git a/samples/game_controller/gameactivity/app/src/main/AndroidManifest.xml b/samples/game_controller/gameactivity/app/src/main/AndroidManifest.xml
index 2374577..54d96c1 100644
--- a/samples/game_controller/gameactivity/app/src/main/AndroidManifest.xml
+++ b/samples/game_controller/gameactivity/app/src/main/AndroidManifest.xml
@@ -32,7 +32,7 @@
android:theme="@style/AppTheme"
android:exported="true">
<meta-data android:name="android.app.lib_name"
- android:value="game" />
+ android:value="paddleboat_demo" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
diff --git a/samples/game_controller/gameactivity/app/src/main/cpp/CMakeLists.for-samples-in-archive.txt b/samples/game_controller/gameactivity/app/src/main/cpp/CMakeLists.for-samples-in-archive.txt
index f08abba..261878e 100644
--- a/samples/game_controller/gameactivity/app/src/main/cpp/CMakeLists.for-samples-in-archive.txt
+++ b/samples/game_controller/gameactivity/app/src/main/cpp/CMakeLists.for-samples-in-archive.txt
@@ -52,30 +52,27 @@
${IMGUI_BASE_DIR})
# now build app's shared lib
-add_library(game SHARED
+add_library(${CMAKE_PROJECT_NAME} SHARED
${COMMON_SRC_DIR}/android_main.cpp
${COMMON_SRC_DIR}/controllerui_data.cpp
${COMMON_SRC_DIR}/controllerui_util.cpp
${COMMON_SRC_DIR}/demo_scene.cpp
- game_activity_included.cpp
- game_input_included.cpp
${COMMON_SRC_DIR}/imgui_manager.cpp
${COMMON_SRC_DIR}/input_util.cpp
jni_util.cpp
- native_app_glue_included.cpp
native_engine.cpp
${COMMON_SRC_DIR}/scene.cpp
${COMMON_SRC_DIR}/scene_manager.cpp
${COMMON_SRC_DIR}/texture_asset_loader.cpp
${COMMON_SRC_DIR}/util.cpp)
-target_include_directories(game PRIVATE
+target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${COMMON_SRC_DIR}
${COMMON_INCLUDE_DIR}
${IMGUI_BASE_DIR})
-target_compile_options(game
+target_compile_options(${CMAKE_PROJECT_NAME}
PRIVATE
-std=c++17
-Wall
@@ -86,10 +83,10 @@
"$<$<CONFIG:DEBUG>:-Werror>")
# add lib dependencies
-target_link_libraries(game
+target_link_libraries(${CMAKE_PROJECT_NAME}
android
imgui
- game-activity::game-activity
+ game-activity::game-activity_static
games-controller::paddleboat_static
png16::png16-static
atomic
diff --git a/samples/game_controller/gameactivity/app/src/main/cpp/CMakeLists.txt b/samples/game_controller/gameactivity/app/src/main/cpp/CMakeLists.txt
index f08abba..8baeed2 100644
--- a/samples/game_controller/gameactivity/app/src/main/cpp/CMakeLists.txt
+++ b/samples/game_controller/gameactivity/app/src/main/cpp/CMakeLists.txt
@@ -27,8 +27,8 @@
"${CMAKE_SHARED_LINKER_FLAGS} -u GameActivity_onCreate")
# Set common compiler options
-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -Wall")
-set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Werror")
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -Wall -Werror -Wextra")
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Werror -Wextra")
add_definitions("-DIMGUI_IMPL_OPENGL_ES2")
# source files common to both the GameActivity and NativeActivity versions
@@ -52,30 +52,27 @@
${IMGUI_BASE_DIR})
# now build app's shared lib
-add_library(game SHARED
+add_library(${CMAKE_PROJECT_NAME} SHARED
${COMMON_SRC_DIR}/android_main.cpp
${COMMON_SRC_DIR}/controllerui_data.cpp
${COMMON_SRC_DIR}/controllerui_util.cpp
${COMMON_SRC_DIR}/demo_scene.cpp
- game_activity_included.cpp
- game_input_included.cpp
${COMMON_SRC_DIR}/imgui_manager.cpp
${COMMON_SRC_DIR}/input_util.cpp
jni_util.cpp
- native_app_glue_included.cpp
native_engine.cpp
${COMMON_SRC_DIR}/scene.cpp
${COMMON_SRC_DIR}/scene_manager.cpp
${COMMON_SRC_DIR}/texture_asset_loader.cpp
${COMMON_SRC_DIR}/util.cpp)
-target_include_directories(game PRIVATE
+target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${COMMON_SRC_DIR}
${COMMON_INCLUDE_DIR}
${IMGUI_BASE_DIR})
-target_compile_options(game
+target_compile_options(${CMAKE_PROJECT_NAME}
PRIVATE
-std=c++17
-Wall
@@ -86,10 +83,10 @@
"$<$<CONFIG:DEBUG>:-Werror>")
# add lib dependencies
-target_link_libraries(game
+target_link_libraries(${CMAKE_PROJECT_NAME}
android
imgui
- game-activity::game-activity
+ game-activity::game-activity_static
games-controller::paddleboat_static
png16::png16-static
atomic
diff --git a/samples/game_controller/gameactivity/app/src/main/cpp/game_activity_included.cpp b/samples/game_controller/gameactivity/app/src/main/cpp/game_activity_included.cpp
deleted file mode 100644
index 5cf0bd9..0000000
--- a/samples/game_controller/gameactivity/app/src/main/cpp/game_activity_included.cpp
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// Include the GameActivity implementation
-#include "game-activity/GameActivity.cpp"
diff --git a/samples/game_controller/gameactivity/app/src/main/cpp/game_input_included.cpp b/samples/game_controller/gameactivity/app/src/main/cpp/game_input_included.cpp
deleted file mode 100644
index e3629c3..0000000
--- a/samples/game_controller/gameactivity/app/src/main/cpp/game_input_included.cpp
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "game-text-input/gametextinput.cpp"
diff --git a/samples/game_controller/gameactivity/app/src/main/cpp/jni_util.cpp b/samples/game_controller/gameactivity/app/src/main/cpp/jni_util.cpp
index 4e62c5d..e7b5260 100644
--- a/samples/game_controller/gameactivity/app/src/main/cpp/jni_util.cpp
+++ b/samples/game_controller/gameactivity/app/src/main/cpp/jni_util.cpp
@@ -18,7 +18,7 @@
#include "jni_util.hpp"
#include "native_engine.hpp"
-static struct JniSetup _jni_setup = {0};
+static struct JniSetup _jni_setup = {0, 0, 0};
struct JniSetup *GetJNISetup() {
if (!_jni_setup.env) {
diff --git a/samples/game_controller/gameactivity/app/src/main/cpp/native_app_glue_included.cpp b/samples/game_controller/gameactivity/app/src/main/cpp/native_app_glue_included.cpp
deleted file mode 100644
index 6aa43cb..0000000
--- a/samples/game_controller/gameactivity/app/src/main/cpp/native_app_glue_included.cpp
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// Include the GameActivity implementation
-#include "game-activity/native_app_glue/android_native_app_glue.c"
diff --git a/samples/game_controller/gameactivity/app/src/main/cpp/native_engine.cpp b/samples/game_controller/gameactivity/app/src/main/cpp/native_engine.cpp
index 19249e6..fb3d550 100644
--- a/samples/game_controller/gameactivity/app/src/main/cpp/native_engine.cpp
+++ b/samples/game_controller/gameactivity/app/src/main/cpp/native_engine.cpp
@@ -24,6 +24,8 @@
#include "paddleboat/paddleboat.h"
+#include <android/window.h>
+
// verbose debug logs on?
#define VERBOSE_LOGGING 1
@@ -36,6 +38,11 @@
// max # of GL errors to print before giving up
#define MAX_GL_ERRORS 200
+static bool all_motion_filter(const GameActivityMotionEvent* /*event*/) {
+ // Process all motion events
+ return true;
+}
+
static NativeEngine *_singleton = NULL;
// workaround for internal bug b/149866792
@@ -60,6 +67,8 @@
memset(&mState, 0, sizeof(mState));
mIsFirstFrame = true;
+ app->motionEventFilter = all_motion_filter;
+
if (app->savedState != NULL) {
// we are starting with previously saved state -- restore it
mState = *(struct NativeEngineSavedState *) app->savedState;
@@ -140,7 +149,7 @@
if (motionEvent->pointerCount > 0) {
int action = motionEvent->action;
int actionMasked = action & AMOTION_EVENT_ACTION_MASK;
- int ptrIndex = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >>
+ uint32_t ptrIndex = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >>
AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
if (ptrIndex < motionEvent->pointerCount) {
@@ -181,6 +190,13 @@
mApp->onAppCmd = _handle_cmd_proxy;
//mApp->onInputEvent = _handle_input_proxy;
+ auto activity = NativeEngine::GetInstance()->GetAndroidApp()->activity;
+ GameActivity_setWindowFlags(activity,
+ AWINDOW_FLAG_KEEP_SCREEN_ON | AWINDOW_FLAG_TURN_SCREEN_ON |
+ AWINDOW_FLAG_FULLSCREEN |
+ AWINDOW_FLAG_SHOW_WHEN_LOCKED,
+ 0);
+
while (1) {
int events;
struct android_poll_source *source;
@@ -327,7 +343,7 @@
mEglConfig);
}
-bool NativeEngine::HandleInput(AInputEvent *event) {
+bool NativeEngine::HandleInput(AInputEvent */*event*/) {
return false;
}
diff --git a/samples/game_controller/gameactivity/app/src/main/java/com/google/android/games/paddleboat/gamecontrollersample/PaddleboatDemoActivity.java b/samples/game_controller/gameactivity/app/src/main/java/com/google/android/games/paddleboat/gamecontrollersample/PaddleboatDemoActivity.java
index 64fdaee..2c1a085 100644
--- a/samples/game_controller/gameactivity/app/src/main/java/com/google/android/games/paddleboat/gamecontrollersample/PaddleboatDemoActivity.java
+++ b/samples/game_controller/gameactivity/app/src/main/java/com/google/android/games/paddleboat/gamecontrollersample/PaddleboatDemoActivity.java
@@ -15,12 +15,22 @@
*/
package com.google.android.games.paddleboat.gamecontrollersample;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
import android.os.Bundle;
+import android.view.View;
+import android.view.WindowManager.LayoutParams;
+import androidx.core.view.WindowCompat;
+import androidx.core.view.WindowInsetsCompat;
+import androidx.core.view.WindowInsetsControllerCompat;
+
import com.google.androidgamesdk.GameActivity;
+// A minimal extension of GameActivity. For this sample, it is only used to invoke
+// a workaround for loading the runtime shared library on old Android versions
public class PaddleboatDemoActivity extends GameActivity {
- // Some code to load our native library:
+ // Load our native library:
static {
// Load the STL first to workaround issues on old Android versions:
// "if your app targets a version of Android earlier than
@@ -30,12 +40,34 @@
// See https://developer.android.com/ndk/guides/cpp-support#shared_runtimes
System.loadLibrary("c++_shared");
- // Load the game library:
- System.loadLibrary("game");
+ // Load the 'paddleboat_demo' library:
+ System.loadLibrary("paddleboat_demo");
+ }
+
+ private void hideSystemUI() {
+ // This will put the game behind any cutouts and waterfalls on devices which have
+ // them, so the corresponding insets will be non-zero.
+ if (VERSION.SDK_INT >= VERSION_CODES.P) {
+ getWindow().getAttributes().layoutInDisplayCutoutMode
+ = LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ }
+ // From API 30 onwards, this is the recommended way to hide the system UI, rather than
+ // using View.setSystemUiVisibility.
+ View decorView = getWindow().getDecorView();
+ WindowInsetsControllerCompat controller = new WindowInsetsControllerCompat(getWindow(),
+ decorView);
+ controller.hide(WindowInsetsCompat.Type.systemBars());
+ controller.hide(WindowInsetsCompat.Type.displayCutout());
+ controller.setSystemBarsBehavior(
+ WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
+ // When true, the app will fit inside any system UI windows.
+ // When false, we render behind any system UI windows.
+ WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
+ hideSystemUI();
super.onCreate(savedInstanceState);
}
}
diff --git a/samples/game_controller/gameactivity/build.gradle b/samples/game_controller/gameactivity/build.gradle
index ba9011e..b9ff2a3 100644
--- a/samples/game_controller/gameactivity/build.gradle
+++ b/samples/game_controller/gameactivity/build.gradle
@@ -22,7 +22,7 @@
mavenCentral()
}
dependencies {
- classpath 'com.android.tools.build:gradle:4.2.2'
+ classpath 'com.android.tools.build:gradle:7.2.2'
}
}
diff --git a/samples/game_controller/gameactivity/gradle.properties b/samples/game_controller/gameactivity/gradle.properties
index 9d5d9e7..88e32d8 100644
--- a/samples/game_controller/gameactivity/gradle.properties
+++ b/samples/game_controller/gameactivity/gradle.properties
@@ -13,6 +13,4 @@
#Wed Nov 18 14:49:00 GMT 2020
android.enableJetifier=true
android.useAndroidX=true
-
-# Use Prefab 1.1.2 containing the fix for header only libraries:
-android.prefabVersion=1.1.2
\ No newline at end of file
+android.prefabVersion=2.0.0
\ No newline at end of file
diff --git a/samples/game_controller/gameactivity/gradle/wrapper/gradle-wrapper.properties b/samples/game_controller/gameactivity/gradle/wrapper/gradle-wrapper.properties
index ec84c97..8f1bae9 100644
--- a/samples/game_controller/gameactivity/gradle/wrapper/gradle-wrapper.properties
+++ b/samples/game_controller/gameactivity/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip
diff --git a/samples/game_controller/gameactivity/settings.gradle b/samples/game_controller/gameactivity/settings.gradle
index 78c78f3..c6b6b36 100644
--- a/samples/game_controller/gameactivity/settings.gradle
+++ b/samples/game_controller/gameactivity/settings.gradle
@@ -14,3 +14,9 @@
* limitations under the License.
*/
include ':app'
+// Comment all the lines below to use a locally built .aar
+// See app/build.gradle as well
+include ':game-activity'
+include ':games-controller'
+project(':game-activity').projectDir = file('../../../game-activity')
+project(':games-controller').projectDir = file('../../../games-controller')
diff --git a/samples/game_controller/nativeactivity/app/build.gradle b/samples/game_controller/nativeactivity/app/build.gradle
index 073c783..9a46fb7 100644
--- a/samples/game_controller/nativeactivity/app/build.gradle
+++ b/samples/game_controller/nativeactivity/app/build.gradle
@@ -17,15 +17,15 @@
apply plugin: 'com.android.application'
android {
- compileSdkVersion 30
+ compileSdkVersion 31
ndkVersion '21.4.7075529'
defaultConfig {
applicationId 'com.google.android.games.paddleboat.gamecontrollersample'
- minSdkVersion 16
- targetSdkVersion 30
- versionCode 1
- versionName '1.0'
+ minSdkVersion 19
+ targetSdkVersion 31
+ versionCode 2
+ versionName '1.1'
externalNativeBuild {
cmake {
arguments '-DANDROID_STL=c++_shared'
@@ -41,8 +41,8 @@
}
externalNativeBuild {
cmake {
- version '3.10.2'
path 'src/main/cpp/CMakeLists.txt'
+ version "3.18.0+"
}
}
buildFeatures {
@@ -54,17 +54,11 @@
}
dependencies {
- // A locally built GameController.aar is expected to be located
- // in app/libs/
- // See the gamesdk README for information on generating a local build
- // You can comment out this line and uncomment the androidx
- // implementation to use Maven. Be aware that this may target a future
- // release which does not yet exist, which is why a locally build
- // library is the default option.
- implementation fileTree(dir: 'libs', include: ['GameController.aar'])
- //implementation "androidx.games:games-controller:1.1.0"
+ // Uncomment this line and comment out "implementation project(":games-controller")"
+ // to use a locally built .aar
+ //implementation fileTree(dir: '../../../../', include: ['games-controller-release.aar'])
+ implementation project(":games-controller")
// libpng via maven
implementation "com.android.ndk.thirdparty:libpng:1.6.37-alpha-1"
-
}
diff --git a/samples/game_controller/nativeactivity/app/src/main/AndroidManifest.xml b/samples/game_controller/nativeactivity/app/src/main/AndroidManifest.xml
index 7f05a47..5ba7d69 100644
--- a/samples/game_controller/nativeactivity/app/src/main/AndroidManifest.xml
+++ b/samples/game_controller/nativeactivity/app/src/main/AndroidManifest.xml
@@ -32,7 +32,7 @@
android:theme="@style/AppTheme"
android:exported="true">
<meta-data android:name="android.app.lib_name"
- android:value="game" />
+ android:value="paddleboat_demo" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
diff --git a/samples/game_controller/nativeactivity/app/src/main/cpp/CMakeLists.for-samples-in-archive.txt b/samples/game_controller/nativeactivity/app/src/main/cpp/CMakeLists.for-samples-in-archive.txt
index 669fda4..00db27a 100644
--- a/samples/game_controller/nativeactivity/app/src/main/cpp/CMakeLists.for-samples-in-archive.txt
+++ b/samples/game_controller/nativeactivity/app/src/main/cpp/CMakeLists.for-samples-in-archive.txt
@@ -55,7 +55,7 @@
${IMGUI_BASE_DIR})
# now build app's shared lib
-add_library(game SHARED
+add_library(${CMAKE_PROJECT_NAME} SHARED
${COMMON_SRC_DIR}/android_main.cpp
${COMMON_SRC_DIR}/controllerui_data.cpp
${COMMON_SRC_DIR}/controllerui_util.cpp
@@ -69,14 +69,14 @@
${COMMON_SRC_DIR}/texture_asset_loader.cpp
${COMMON_SRC_DIR}/util.cpp)
-target_include_directories(game PRIVATE
+target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${COMMON_SRC_DIR}
${COMMON_INCLUDE_DIR}
${IMGUI_BASE_DIR}
${ANDROID_NDK}/sources/android/native_app_glue)
-target_compile_options(game
+target_compile_options(${CMAKE_PROJECT_NAME}
PRIVATE
-std=c++17
-Wall
@@ -87,7 +87,7 @@
"$<$<CONFIG:DEBUG>:-Werror>")
# add lib dependencies
-target_link_libraries(game
+target_link_libraries(${CMAKE_PROJECT_NAME}
android
imgui
native_app_glue
diff --git a/samples/game_controller/nativeactivity/app/src/main/cpp/CMakeLists.txt b/samples/game_controller/nativeactivity/app/src/main/cpp/CMakeLists.txt
index 669fda4..00db27a 100644
--- a/samples/game_controller/nativeactivity/app/src/main/cpp/CMakeLists.txt
+++ b/samples/game_controller/nativeactivity/app/src/main/cpp/CMakeLists.txt
@@ -55,7 +55,7 @@
${IMGUI_BASE_DIR})
# now build app's shared lib
-add_library(game SHARED
+add_library(${CMAKE_PROJECT_NAME} SHARED
${COMMON_SRC_DIR}/android_main.cpp
${COMMON_SRC_DIR}/controllerui_data.cpp
${COMMON_SRC_DIR}/controllerui_util.cpp
@@ -69,14 +69,14 @@
${COMMON_SRC_DIR}/texture_asset_loader.cpp
${COMMON_SRC_DIR}/util.cpp)
-target_include_directories(game PRIVATE
+target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${COMMON_SRC_DIR}
${COMMON_INCLUDE_DIR}
${IMGUI_BASE_DIR}
${ANDROID_NDK}/sources/android/native_app_glue)
-target_compile_options(game
+target_compile_options(${CMAKE_PROJECT_NAME}
PRIVATE
-std=c++17
-Wall
@@ -87,7 +87,7 @@
"$<$<CONFIG:DEBUG>:-Werror>")
# add lib dependencies
-target_link_libraries(game
+target_link_libraries(${CMAKE_PROJECT_NAME}
android
imgui
native_app_glue
diff --git a/samples/game_controller/nativeactivity/build.gradle b/samples/game_controller/nativeactivity/build.gradle
index d53d5e5..96172f2 100644
--- a/samples/game_controller/nativeactivity/build.gradle
+++ b/samples/game_controller/nativeactivity/build.gradle
@@ -21,7 +21,7 @@
mavenCentral()
}
dependencies {
- classpath 'com.android.tools.build:gradle:4.2.2'
+ classpath 'com.android.tools.build:gradle:7.2.2'
}
}
diff --git a/samples/game_controller/nativeactivity/gradle.properties b/samples/game_controller/nativeactivity/gradle.properties
index a26d07b..79abc25 100644
--- a/samples/game_controller/nativeactivity/gradle.properties
+++ b/samples/game_controller/nativeactivity/gradle.properties
@@ -12,3 +12,5 @@
# org.gradle.parallel=true
#Wed Nov 18 14:49:00 GMT 2020
android.useAndroidX=true
+
+android.prefabVersion=2.0.0
diff --git a/samples/game_controller/nativeactivity/gradle/wrapper/gradle-wrapper.properties b/samples/game_controller/nativeactivity/gradle/wrapper/gradle-wrapper.properties
index ec84c97..8f1bae9 100644
--- a/samples/game_controller/nativeactivity/gradle/wrapper/gradle-wrapper.properties
+++ b/samples/game_controller/nativeactivity/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip
diff --git a/samples/game_controller/nativeactivity/settings.gradle b/samples/game_controller/nativeactivity/settings.gradle
index 78c78f3..9f120fe 100644
--- a/samples/game_controller/nativeactivity/settings.gradle
+++ b/samples/game_controller/nativeactivity/settings.gradle
@@ -14,3 +14,5 @@
* limitations under the License.
*/
include ':app'
+include ':games-controller'
+project(':games-controller').projectDir = file('../../../games-controller')
\ No newline at end of file
diff --git a/samples/game_text_input/game_text_input_testbed/app/CMakeLists.txt b/samples/game_text_input/game_text_input_testbed/app/CMakeLists.txt
index 48a1304..1bc32db 100644
--- a/samples/game_text_input/game_text_input_testbed/app/CMakeLists.txt
+++ b/samples/game_text_input/game_text_input_testbed/app/CMakeLists.txt
@@ -1,15 +1,16 @@
cmake_minimum_required(VERSION 3.4.1)
+project(game-input)
+
include_directories(src/main/cpp)
# Find the Game Text Input package, exposed via Prefab.
find_package(game-text-input REQUIRED CONFIG)
-add_library(native-lib SHARED
- src/main/cpp/native-lib.cpp
+add_library(${CMAKE_PROJECT_NAME} SHARED
+ src/main/cpp/game-input.cpp
)
-find_library(log-lib log)
-target_link_libraries(native-lib
+target_link_libraries(${CMAKE_PROJECT_NAME}
game-text-input::game-text-input
- ${log-lib})
\ No newline at end of file
+ android log)
\ No newline at end of file
diff --git a/samples/game_text_input/game_text_input_testbed/app/build.gradle b/samples/game_text_input/game_text_input_testbed/app/build.gradle
index d3e51b8..7c77d69 100644
--- a/samples/game_text_input/game_text_input_testbed/app/build.gradle
+++ b/samples/game_text_input/game_text_input_testbed/app/build.gradle
@@ -4,11 +4,10 @@
android {
compileSdkVersion 30
- buildToolsVersion "30.0.2"
defaultConfig {
applicationId "com.gametextinput.testbed"
- minSdkVersion 16
+ minSdkVersion 19
targetSdkVersion 30
versionCode 1
versionName "1.0"
@@ -37,24 +36,16 @@
}
dependencies {
-
implementation "androidx.core:core:1.5.0"
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.3.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
- testImplementation 'junit:junit:4.+'
- androidTestImplementation 'androidx.test.ext:junit:1.1.2'
- androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
- // This is picked up from the directory specified below, so you must have built
- // game_text_input before compiling this app. See ../README.md.
- implementation(name: 'GameTextInput', ext: 'aar')
-}
+ // Uncomment the line below (implementation fileTree...)
+ // and comment out the "implementation project(":*")" lines
+ // to use a locally built .aar
+ // See ../settings.gradle as well.
+ //implementation fileTree(dir: '../../../../', include: ['game-text-input-release.aar'])
-allprojects {
- repositories {
- flatDir {
- dirs '../../../../../out/outputs/aar'
- }
- }
+ implementation project(":game-text-input")
}
diff --git a/samples/game_text_input/game_text_input_testbed/app/src/main/cpp/native-lib.cpp b/samples/game_text_input/game_text_input_testbed/app/src/main/cpp/game-input.cpp
similarity index 95%
rename from samples/game_text_input/game_text_input_testbed/app/src/main/cpp/native-lib.cpp
rename to samples/game_text_input/game_text_input_testbed/app/src/main/cpp/game-input.cpp
index 76de2ee..7fc1014 100644
--- a/samples/game_text_input/game_text_input_testbed/app/src/main/cpp/native-lib.cpp
+++ b/samples/game_text_input/game_text_input_testbed/app/src/main/cpp/game-input.cpp
@@ -18,7 +18,7 @@
#include <memory>
-#define LOG_TAG "NativeLib"
+#define LOG_TAG "game-input"
#define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__);
// Import the Game Text Input interfae:
@@ -82,6 +82,11 @@
}
extern "C" JNIEXPORT void JNICALL
+Java_com_gametextinput_testbed_MainActivity_restartInput(JNIEnv *env, jobject thiz) {
+ GameTextInput_restartInput(gameTextInput);
+}
+
+extern "C" JNIEXPORT void JNICALL
Java_com_gametextinput_testbed_MainActivity_sendSelectionToStart(JNIEnv *env,
jobject thiz) {
GameTextInput_getState(gameTextInput, [](void* context, const GameTextInputState* state) {
diff --git a/samples/game_text_input/game_text_input_testbed/app/src/main/java/com/gameinput/testbed/MainActivity.java b/samples/game_text_input/game_text_input_testbed/app/src/main/java/com/gameinput/testbed/MainActivity.java
index 1632ada..86015ab 100644
--- a/samples/game_text_input/game_text_input_testbed/app/src/main/java/com/gameinput/testbed/MainActivity.java
+++ b/samples/game_text_input/game_text_input_testbed/app/src/main/java/com/gameinput/testbed/MainActivity.java
@@ -27,7 +27,7 @@
public class MainActivity extends AppCompatActivity {
static {
- System.loadLibrary("native-lib");
+ System.loadLibrary("game-input");
}
InputEnabledTextView inputEnabledTextView;
@@ -37,6 +37,7 @@
native void setInputConnectionNative(InputConnection c);
native void showIme();
native void hideIme();
+ native void restartInput();
native void sendSelectionToStart();
native void sendSelectionToEnd();
@@ -66,21 +67,21 @@
EditorInfo editorInfo = new EditorInfo();
editorInfo.inputType = InputType.TYPE_NULL;
inputEnabledTextView.mInputConnection.setEditorInfo(editorInfo);
- inputEnabledTextView.mInputConnection.restartInput();
+ restartInput();
});
Button typeTextButton = (Button) findViewById(R.id.type_text_button);
typeTextButton.setOnClickListener(view -> {
EditorInfo editorInfo = new EditorInfo();
editorInfo.inputType = InputType.TYPE_CLASS_TEXT;
inputEnabledTextView.mInputConnection.setEditorInfo(editorInfo);
- inputEnabledTextView.mInputConnection.restartInput();
+ restartInput();
});
Button typeNumberButton = (Button) findViewById(R.id.type_number_button);
typeNumberButton.setOnClickListener(view -> {
EditorInfo editorInfo = new EditorInfo();
editorInfo.inputType = InputType.TYPE_CLASS_NUMBER;
inputEnabledTextView.mInputConnection.setEditorInfo(editorInfo);
- inputEnabledTextView.mInputConnection.restartInput();
+ restartInput();
});
onCreated();
diff --git a/samples/game_text_input/game_text_input_testbed/build.gradle b/samples/game_text_input/game_text_input_testbed/build.gradle
index 4d24be2..7be1929 100644
--- a/samples/game_text_input/game_text_input_testbed/build.gradle
+++ b/samples/game_text_input/game_text_input_testbed/build.gradle
@@ -5,7 +5,7 @@
jcenter()
}
dependencies {
- classpath "com.android.tools.build:gradle:4.1.1"
+ classpath "com.android.tools.build:gradle:7.2.2"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
@@ -21,4 +21,4 @@
task clean(type: Delete) {
delete rootProject.buildDir
-}
\ No newline at end of file
+}
diff --git a/samples/game_text_input/game_text_input_testbed/gradle.properties b/samples/game_text_input/game_text_input_testbed/gradle.properties
index 4a5b459..01b80d7 100644
--- a/samples/game_text_input/game_text_input_testbed/gradle.properties
+++ b/samples/game_text_input/game_text_input_testbed/gradle.properties
@@ -17,6 +17,3 @@
android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true
-
-# Use Prefab 1.1.2 containing the fix for header only libraries:
-android.prefabVersion=1.1.2
\ No newline at end of file
diff --git a/samples/game_text_input/game_text_input_testbed/gradle/wrapper/gradle-wrapper.properties b/samples/game_text_input/game_text_input_testbed/gradle/wrapper/gradle-wrapper.properties
index 2d41964..f74b3ad 100644
--- a/samples/game_text_input/game_text_input_testbed/gradle/wrapper/gradle-wrapper.properties
+++ b/samples/game_text_input/game_text_input_testbed/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip
diff --git a/samples/game_text_input/game_text_input_testbed/settings.gradle b/samples/game_text_input/game_text_input_testbed/settings.gradle
index 46be64c..c28ef34 100644
--- a/samples/game_text_input/game_text_input_testbed/settings.gradle
+++ b/samples/game_text_input/game_text_input_testbed/settings.gradle
@@ -1,2 +1,6 @@
include ':app'
-rootProject.name = "Game Text Input Testbed"
\ No newline at end of file
+rootProject.name = "Game Text Input Testbed"
+// Comment all the lines below to use a locally built .aar
+// See app/build.gradle as well
+include ':game-text-input'
+project(':game-text-input').projectDir = file('../../../game-text-input')
diff --git a/samples/gamesdk.cmake b/samples/gamesdk.cmake
index d748edd..f38e047 100644
--- a/samples/gamesdk.cmake
+++ b/samples/gamesdk.cmake
@@ -70,11 +70,11 @@
get_filename_component(SWAPPY_DEP_LIB "${GAMESDK_LIBS_DIR}/libswappy_static.a" REALPATH)
get_filename_component(TUNINGFORK_DEP_LIB "${GAMESDK_LIBS_DIR}/libtuningfork_static.a" REALPATH)
- get_filename_component(MEMORY_ADVICE_DEP_LIB "${GAMESDK_SHARED_LIBS_DIR}/lib/memory_advice/libmemory_advice.so" REALPATH)
+ get_filename_component(MEMORY_ADVICE_DEP_LIB "${GAMESDK_LIBS_DIR}/libmemory_advice_static.a" REALPATH)
get_filename_component(OBOE_DEP_LIB "${GAMESDK_LIBS_DIR}/liboboe_static.a" REALPATH)
add_library(swappy STATIC IMPORTED GLOBAL)
add_library(tuningfork STATIC IMPORTED GLOBAL)
- add_library(memory_advice SHARED IMPORTED GLOBAL)
+ add_library(memory_advice STATIC IMPORTED GLOBAL)
add_library(oboe STATIC IMPORTED GLOBAL)
if(GAMESDK_DO_LOCAL_BUILD)
@@ -100,7 +100,7 @@
OUTPUT
${SWAPPY_DEP_LIB} ${TUNINGFORK_DEP_LIB} ${MEMORY_ADVICE_DEP_LIB} ${OBOE_DEP_LIB}
COMMAND
- ${GAMESDK_GRADLE_BIN} buildLocal -Plibraries=${GAMESDK_LIBRARIES} -PandroidApiLevel=${GAMESDK_ANDROID_API_LEVEL} -PbuildType=${GAMESDK_BUILD_TYPE} -PpackageName=local -Pndk=${GAMESDK_ANDROID_NDK_VERSION}
+ ${GAMESDK_GRADLE_BIN} buildLocal -Plibraries=${GAMESDK_LIBRARIES} -PandroidApiLevel=${GAMESDK_ANDROID_API_LEVEL} -PbuildType=${GAMESDK_BUILD_TYPE} -PpackageName=local -Pndk=${GAMESDK_ANDROID_NDK_VERSION} --info
VERBATIM
WORKING_DIRECTORY
"${GAMESDK_ROOT_DIR}"
@@ -114,6 +114,17 @@
add_dependencies(memory_advice memory_advice_lib)
add_dependencies(oboe oboe_lib)
else()
+ if("${ANDROID_ABI}" STREQUAL "armeabi-v7a" OR "${ANDROID_ABI}" STREQUAL "x86")
+ set(BUILD_NAME ${ANDROID_ABI}_API19_NDK23_cpp_shared_${GAMESDK_BUILD_TYPE})
+ else()
+ set(BUILD_NAME ${ANDROID_ABI}_API21_NDK23_cpp_shared_${GAMESDK_BUILD_TYPE})
+ endif()
+ set(GAMESDK_LIBS_DIR "${GAMESDK_PACKAGE_DIR}/libs/${BUILD_NAME}")
+ set(GAMESDK_SHARED_LIBS_DIR "${GAMESDK_PACKAGE_DIR}/libs/${ANDROID_ABI}")
+ get_filename_component(SWAPPY_DEP_LIB "${GAMESDK_LIBS_DIR}/libswappy_static.a" REALPATH)
+ get_filename_component(TUNINGFORK_DEP_LIB "${GAMESDK_LIBS_DIR}/libtuningfork_static.a" REALPATH)
+ get_filename_component(MEMORY_ADVICE_DEP_LIB "${GAMESDK_LIBS_DIR}/libmemory_advice_static.a" REALPATH)
+ get_filename_component(OBOE_DEP_LIB "${GAMESDK_LIBS_DIR}/liboboe_static.a" REALPATH)
# Validity check to ensure that the library files exist
if(NOT EXISTS ${SWAPPY_DEP_LIB} AND
NOT EXISTS ${TUNINGFORK_DEP_LIB} AND
@@ -135,5 +146,5 @@
# sources to your project - allowing the IDE to provide autocompletions and
# debugging.
function(add_gamesdk_sources)
- add_subdirectory("${_MY_DIR}/../src")
+ add_subdirectory("${_MY_DIR}/../src" "${_MY_DIR}/../src")
endfunction()
diff --git a/samples/memory_advice/hogger/app/CMakeLists.for-samples-in-archive.txt b/samples/memory_advice/hogger/app/CMakeLists.for-samples-in-archive.txt
new file mode 100644
index 0000000..305c282
--- /dev/null
+++ b/samples/memory_advice/hogger/app/CMakeLists.for-samples-in-archive.txt
@@ -0,0 +1,36 @@
+cmake_minimum_required(VERSION 3.4.1)
+project(hogger)
+
+set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Werror -Wthread-safety" )
+set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D _LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS -Os -fPIC" )
+set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti" )
+set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DGOOGLE_PROTOBUF_NO_RTTI -DHAVE_PTHREAD")
+
+set( MEMORYADVICE_RESOURCES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../../test/memoryadvice/memoryadvice/src/main/resources")
+
+include("../../../gamesdk.cmake")
+
+# This uses memory advice as a static shared from the Game SDK package. Make sure the Android
+# SDK and NDK versions that you are using are supported by the Game SDK.
+add_gamesdk_target(PACKAGE_DIR "../../../.." BUILD_TYPE "Release")
+
+#get_target_property(DEP_LIB memory_advice IMPORTED_LOCATION)
+
+file(GLOB TF_MODEL_FILES "${MEMORYADVICE_RESOURCES_DIR}/*.*")
+file(COPY ${TF_MODEL_FILES} DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/src/main/assets)
+
+include_directories(../../../../include)
+include_directories(../../../common/include ) # Samples Includes
+
+
+add_library( ${CMAKE_PROJECT_NAME}
+ SHARED
+ src/main/cpp/hogger.cpp
+ )
+
+target_link_libraries( ${CMAKE_PROJECT_NAME}
+ memory_advice
+ android
+ GLESv2
+ log
+ )
diff --git a/samples/memory_advice/hogger/app/CMakeLists.txt b/samples/memory_advice/hogger/app/CMakeLists.txt
index 6f9c24e..677e23a 100644
--- a/samples/memory_advice/hogger/app/CMakeLists.txt
+++ b/samples/memory_advice/hogger/app/CMakeLists.txt
@@ -1,41 +1,25 @@
cmake_minimum_required(VERSION 3.4.1)
+project(hogger)
+
+find_package(games-memory-advice REQUIRED CONFIG)
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Werror -Wthread-safety" )
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D _LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS -Os -fPIC" )
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti" )
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DGOOGLE_PROTOBUF_NO_RTTI -DHAVE_PTHREAD")
-set( MEMORYADVICE_RESOURCES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../../test/memoryadvice/memoryadvice/src/main/resources")
-
-include("../../../gamesdk.cmake")
-
-# This builds the gamesdk in package/localtf and adds a gamesdk static library target
-add_gamesdk_target(
- DO_LOCAL_BUILD
- ROOT_DIR "../../../.."
- PACKAGE_DIR "../../../../../package/local"
- LIBRARIES "memory_advice"
-)
-
-get_target_property(DEP_LIB memory_advice IMPORTED_LOCATION)
-file(COPY ${DEP_LIB} DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI})
-
-file(GLOB TF_MODEL_FILES "${MEMORYADVICE_RESOURCES_DIR}/*.*")
-file(COPY ${TF_MODEL_FILES} DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/src/main/assets)
-
include_directories(../../../../include)
include_directories(../../../common/include ) # Samples Includes
-add_library( native-lib
+add_library(${CMAKE_PROJECT_NAME}
SHARED
- src/main/cpp/native-lib.cpp
+ src/main/cpp/hogger.cpp
)
-target_link_libraries( native-lib
- memory_advice
+target_link_libraries(${CMAKE_PROJECT_NAME}
+ games-memory-advice::memory_advice_static
android
- EGL
GLESv2
log
)
diff --git a/samples/memory_advice/hogger/app/build.gradle b/samples/memory_advice/hogger/app/build.gradle
index 029667f..20e4bfb 100644
--- a/samples/memory_advice/hogger/app/build.gradle
+++ b/samples/memory_advice/hogger/app/build.gradle
@@ -3,13 +3,13 @@
}
android {
- compileSdkVersion 28
- ndkVersion "20.0.5594570"
+ compileSdkVersion 29
+ ndkVersion "21.4.7075529"
defaultConfig {
applicationId "com.memory_advice.hogger"
- minSdkVersion 16
- targetSdkVersion 28
+ minSdkVersion 20
+ targetSdkVersion 29
versionCode 17
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@@ -20,9 +20,11 @@
}
buildTypes {
+ debug {
+ }
release {
minifyEnabled false
- proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
externalNativeBuild {
@@ -34,14 +36,24 @@
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
+ buildFeatures {
+ prefab true
+ }
}
+task copyAssets() {
+ copy {
+ from "../../../../test/memoryadvice/memoryadvice/src/main/resources"
+ into "src/main/assets"
+ }
+}
+
+tasks.preBuild.dependsOn('copyAssets')
+
dependencies {
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'com.google.android.material:material:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
- testImplementation 'junit:junit:4.+'
- androidTestImplementation 'androidx.test.ext:junit:1.1.1'
- androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
+ implementation project(':games-memory-advice')
}
\ No newline at end of file
diff --git a/samples/memory_advice/hogger/app/src/main/cpp/hogger.cpp b/samples/memory_advice/hogger/app/src/main/cpp/hogger.cpp
new file mode 100644
index 0000000..7a4a30e
--- /dev/null
+++ b/samples/memory_advice/hogger/app/src/main/cpp/hogger.cpp
@@ -0,0 +1,114 @@
+#include <cassert>
+#include <jni.h>
+#include <list>
+#include <mutex>
+#include <random>
+#include <string>
+#include <sstream>
+
+#include <memory_advice/memory_advice.h>
+#include <memory_advice/memory_advice_debug.h>
+
+#define LOG_TAG "Hogger"
+#include "Log.h"
+
+static int TEST_USER_DATA;
+static int TEST_USER_DATA2;
+constexpr int callback_waittime_ms = 2000;
+constexpr int callback2_waittime_ms = 5000;
+
+void callback(MemoryAdvice_MemoryState state, void* context) {
+ assert (context == &TEST_USER_DATA);
+ ALOGI("State is: %d", state);
+ // Test unregistering and registering.
+ MemoryAdvice_unregisterWatcher(callback);
+ MemoryAdvice_registerWatcher(callback_waittime_ms, callback, &TEST_USER_DATA);
+}
+
+void callback2(MemoryAdvice_MemoryState state, void* context) {
+ assert (context == &TEST_USER_DATA2);
+ ALOGI("State (callback 2) is: %d", state);
+ // Test unregistering and registering.
+ MemoryAdvice_unregisterWatcher(callback2);
+ MemoryAdvice_registerWatcher(callback2_waittime_ms, callback2, &TEST_USER_DATA2);
+}
+
+extern "C" JNIEXPORT jint JNICALL
+Java_com_memory_1advice_hogger_MainActivity_initMemoryAdvice(
+ JNIEnv* env,
+ jobject activity) {
+ auto init_error_code = MemoryAdvice_init(env, activity);
+ if (init_error_code == MEMORYADVICE_ERROR_OK) {
+ MemoryAdvice_registerWatcher(callback_waittime_ms, callback, &TEST_USER_DATA);
+ MemoryAdvice_registerWatcher(callback2_waittime_ms, callback2, &TEST_USER_DATA2);
+
+ assert(MemoryAdvice_test() == 0);
+ }
+ return init_error_code;
+}
+
+extern "C" JNIEXPORT jstring JNICALL
+Java_com_memory_1advice_hogger_MainActivity_getMemoryAdvice(
+ JNIEnv* env,
+ jobject activity) {
+ MemoryAdvice_JsonSerialization advice;
+ MemoryAdvice_getAdvice(&advice);
+ ALOGE("Advice is: %s", advice.json);
+ jstring ret = env->NewStringUTF(advice.json);
+ MemoryAdvice_JsonSerialization_free(&advice);
+ return ret;
+}
+extern "C" JNIEXPORT jfloat JNICALL
+Java_com_memory_1advice_hogger_MainActivity_getPercentageMemoryAvailable(
+ JNIEnv* env,
+ jobject activity) {
+ return MemoryAdvice_getPercentageAvailableMemory();
+}
+
+extern "C" JNIEXPORT jint JNICALL
+Java_com_memory_1advice_hogger_MainActivity_getMemoryState(
+ JNIEnv* env,
+ jobject activity) {
+ return MemoryAdvice_getMemoryState();
+}
+
+static std::list<std::unique_ptr<char[]>> allocated_bytes_list;
+static std::mutex allocated_mutex;
+
+static void FillRandom(char* bytes, size_t size) {
+ std::minstd_rand rng;
+ int32_t* ptr = reinterpret_cast<int32_t*>(bytes);
+ std::generate(ptr, ptr + size/4, [&]() -> int32_t { return rng(); });
+ // Don't worry about filling the last few bytes if size isn't a multiple of 4.
+}
+
+extern "C" JNIEXPORT bool JNICALL
+Java_com_memory_1advice_hogger_MainActivity_allocate(
+ JNIEnv* env,
+ jobject activity,
+ jlong nbytes) {
+ std::lock_guard<std::mutex> guard(allocated_mutex);
+ try {
+ auto allocated_bytes = new char[nbytes];
+ // Note that without setting the memory, the available memory figure doesn't go down.
+ FillRandom(allocated_bytes, nbytes);
+ allocated_bytes_list.push_back(std::unique_ptr<char[]>{allocated_bytes});
+ return true;
+ } catch(std::bad_alloc& ex) {
+ ALOGE("Can't allocate any more memory");
+ return false;
+ }
+}
+
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_memory_1advice_hogger_MainActivity_deallocate(
+ JNIEnv* env,
+ jobject activity) {
+ std::lock_guard<std::mutex> guard(allocated_mutex);
+ if (allocated_bytes_list.size() > 0) {
+ allocated_bytes_list.pop_back();
+ return true;
+ } else {
+ return false;
+ }
+}
diff --git a/samples/memory_advice/hogger/app/src/main/cpp/native-lib.cpp b/samples/memory_advice/hogger/app/src/main/cpp/native-lib.cpp
deleted file mode 100644
index 36c7d4a..0000000
--- a/samples/memory_advice/hogger/app/src/main/cpp/native-lib.cpp
+++ /dev/null
@@ -1,28 +0,0 @@
-#include <jni.h>
-#include <string>
-#include <sstream>
-
-#include <memory_advice/memory_advice.h>
-
-#define LOG_TAG "Hogger"
-#include "Log.h"
-
-void callback(MemoryAdvice_MemoryState state) {
- ALOGE("State is: %d", state);
- MemoryAdvice_removeWatcher();
-}
-
-extern "C" JNIEXPORT jstring JNICALL
-Java_com_memory_1advice_hogger_MainActivity_getAdvice(
- JNIEnv* env,
- jobject activity) {
- MemoryAdvice_initDefaultParams(env, activity);
- MemoryAdvice_JsonSerialization advice;
- MemoryAdvice_getAdvice(&advice);
- ALOGE("Advice is: %s", advice.json);
- jstring ret = env->NewStringUTF(advice.json);
- MemoryAdvice_JsonSerialization_free(&advice);
- // TODO(b/209602631): Reintroduce this line after state logic is fixed
- //MemoryAdvice_setWatcher(1000, callback);
- return ret;
-}
diff --git a/samples/memory_advice/hogger/app/src/main/java/com/memory_advice/hogger/MainActivity.java b/samples/memory_advice/hogger/app/src/main/java/com/memory_advice/hogger/MainActivity.java
index dd9ed87..859ed7b 100644
--- a/samples/memory_advice/hogger/app/src/main/java/com/memory_advice/hogger/MainActivity.java
+++ b/samples/memory_advice/hogger/app/src/main/java/com/memory_advice/hogger/MainActivity.java
@@ -3,29 +3,125 @@
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
+import android.view.View;
import android.widget.TextView;
+import java.util.Timer;
+import java.util.TimerTask;
public class MainActivity extends AppCompatActivity {
- // Used to load the 'native-lib' library on application startup.
+ // Used to load the 'hogger' library on application startup.
static {
- System.loadLibrary("memory_advice");
- System.loadLibrary("native-lib");
+ System.loadLibrary("hogger");
+
+ // Explicitly load the MemoryAdvice native library (Optional).
+ //System.loadLibrary("memory_advice");
}
+ private final class CheckMemoryAdviceTask extends TimerTask {
+ @Override public void run() {
+ // Post a task to update the memory on the UI thread.
+ getWindow().getDecorView().post(() -> {
+ showMemoryAdvice(getMemoryAdvice(), getMemoryState());
+ showMemoryAvailable(getPercentageMemoryAvailable());
+ });
+ }
+ };
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
- // Example of a call to a native method
- TextView tv = findViewById(R.id.sample_text);
- tv.setText(getAdvice());
+ int init_retcode = initMemoryAdvice();
+ if (init_retcode!=0) {
+ showMemoryAdvice(String.format("Error initializing memory_advice: %d", init_retcode),
+ 0);
+ return;
+ }
+
+ showMemoryAdvice(getMemoryAdvice(), getMemoryState());
+ showMemoryAvailable(getPercentageMemoryAvailable());
+
+ Timer timer = new Timer();
+ timer.scheduleAtFixedRate(new CheckMemoryAdviceTask(), 0, 1000);
+
+ findViewById(R.id.allocate_button).setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ onAllocate();
+ }
+ });
+ findViewById(R.id.deallocate_button).setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ onDeallocate();
+ }
+ });
}
- /**
- * A native method that is implemented by the 'native-lib' native library,
- * which is packaged with this application.
- */
- public native String getAdvice();
-}
\ No newline at end of file
+ static final long BYTES_PER_MEGABYTE = 1000000;
+ static final long BYTES_TO_ADD = 100*BYTES_PER_MEGABYTE;
+ long memory_allocated;
+
+ public void onAllocate() {
+ new Thread(() -> {
+ if (allocate(BYTES_TO_ADD)) {
+ memory_allocated += BYTES_TO_ADD;
+ updateMemoryAllocated();
+ }
+ }).start();
+ }
+
+ public void onDeallocate() {
+ new Thread(() -> {
+ if (deallocate()) {
+ memory_allocated -= BYTES_TO_ADD;
+ updateMemoryAllocated();
+ }
+ }).start();
+ }
+
+ private void updateMemoryAllocated() {
+ getWindow().getDecorView().post(() -> {
+ showMemoryAllocated();
+ });
+ }
+
+ private void showMemoryAdvice(String advice, int state) {
+ // Example of a call to a native method
+ TextView tv = findViewById(R.id.sample_text);
+ tv.setText(advice);
+ String stateString = "UNKNOWN";
+ switch(state) {
+ case 1:
+ stateString = "OK";
+ break;
+ case 2:
+ stateString = "APPROACHING_LIMIT";
+ break;
+ case 3:
+ stateString = "CRITICAL";
+ break;
+ }
+ TextView state_tv = findViewById(R.id.state_textview);
+ state_tv.setText("Memory State: " + stateString);
+ }
+
+ private void showMemoryAllocated() {
+ // Example of a call to a native method
+ TextView tv = findViewById(R.id.allocated_textview);
+ tv.setText(String.format("Memory Allocated: %d MB", memory_allocated/BYTES_PER_MEGABYTE));
+ }
+
+ private void showMemoryAvailable(float percentage) {
+ TextView tv = findViewById(R.id.available_textview);
+ tv.setText(String.format("Memory Available: %.2f%%", percentage));
+ }
+
+ public native int initMemoryAdvice();
+ public native String getMemoryAdvice();
+ public native int getMemoryState();
+ public native float getPercentageMemoryAvailable();
+ public native boolean allocate(long bytes);
+ public native boolean deallocate();
+
+}
diff --git a/samples/memory_advice/hogger/app/src/main/res/layout/activity_main.xml b/samples/memory_advice/hogger/app/src/main/res/layout/activity_main.xml
index a4e8d40..5585e31 100644
--- a/samples/memory_advice/hogger/app/src/main/res/layout/activity_main.xml
+++ b/samples/memory_advice/hogger/app/src/main/res/layout/activity_main.xml
@@ -11,9 +11,69 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
- app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintBottom_toTopOf="@+id/state_textview"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/textView3" />
+
+ <Button
+ android:id="@+id/allocate_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="28dp"
+ android:text="Allocate"
+ app:layout_constraintBottom_toTopOf="@+id/deallocate_button"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent" />
+
+ <Button
+ android:id="@+id/deallocate_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="24dp"
+ android:text="Deallocate"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent" />
+
+ <TextView
+ android:id="@+id/allocated_textview"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="24dp"
+ android:text="Memory Allocated: 0MB"
+ app:layout_constraintBottom_toTopOf="@+id/allocate_button"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent" />
+
+ <TextView
+ android:id="@+id/state_textview"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="24dp"
+ android:text="Memory State: OK"
+ app:layout_constraintBottom_toTopOf="@+id/available_textview"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent" />
+
+ <TextView
+ android:id="@+id/textView3"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="24dp"
+ android:text="Memory Advice:"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
+ <TextView
+ android:id="@+id/available_textview"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="24dp"
+ android:text="Memory Available: 0MB"
+ app:layout_constraintBottom_toTopOf="@+id/allocated_textview"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent" />
+
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/samples/memory_advice/hogger/build.gradle b/samples/memory_advice/hogger/build.gradle
index 3a48c17..ead751a 100644
--- a/samples/memory_advice/hogger/build.gradle
+++ b/samples/memory_advice/hogger/build.gradle
@@ -7,7 +7,7 @@
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.5.2'
+ classpath 'com.android.tools.build:gradle:7.2.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
diff --git a/samples/memory_advice/hogger/gradle.properties b/samples/memory_advice/hogger/gradle.properties
index 743d692..83e751e 100644
--- a/samples/memory_advice/hogger/gradle.properties
+++ b/samples/memory_advice/hogger/gradle.properties
@@ -11,3 +11,6 @@
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
+android.useAndroidX=true
+
+android.prefabVersion=2.0.0
\ No newline at end of file
diff --git a/samples/memory_advice/hogger/settings.gradle b/samples/memory_advice/hogger/settings.gradle
index 9f59ec9..42b72c8 100644
--- a/samples/memory_advice/hogger/settings.gradle
+++ b/samples/memory_advice/hogger/settings.gradle
@@ -1,2 +1,4 @@
include ':app'
-rootProject.name = "Hogger"
\ No newline at end of file
+rootProject.name = "Hogger"
+include ':games-memory-advice'
+project(":games-memory-advice").projectDir = file("../../../games-memory-advice")
\ No newline at end of file
diff --git a/samples/tuningfork/common/protobuf_nano_util.h b/samples/tuningfork/common/protobuf_nano_util.h
index ae11a0d..c2776e0 120000
--- a/samples/tuningfork/common/protobuf_nano_util.h
+++ b/samples/tuningfork/common/protobuf_nano_util.h
@@ -1 +1 @@
-../../../src/tuningfork/proto/protobuf_nano_util.h
\ No newline at end of file
+../../../games-performance-tuner/proto/protobuf_nano_util.h
\ No newline at end of file
diff --git a/samples/tuningfork/common/protobuf_util.cpp b/samples/tuningfork/common/protobuf_util.cpp
index 98220c0..c0c7ccc 120000
--- a/samples/tuningfork/common/protobuf_util.cpp
+++ b/samples/tuningfork/common/protobuf_util.cpp
@@ -1 +1 @@
-../../../src/tuningfork/proto/protobuf_util.cpp
\ No newline at end of file
+../../../games-performance-tuner/proto/protobuf_util.cpp
\ No newline at end of file
diff --git a/samples/tuningfork/common/protobuf_util.h b/samples/tuningfork/common/protobuf_util.h
index b1becf0..171ee82 120000
--- a/samples/tuningfork/common/protobuf_util.h
+++ b/samples/tuningfork/common/protobuf_util.h
@@ -1 +1 @@
-../../../src/tuningfork/proto/protobuf_util.h
\ No newline at end of file
+../../../games-performance-tuner/proto/protobuf_util.h
\ No newline at end of file
diff --git a/samples/tuningfork/experimentsdemo/app/CMakeLists.for-samples-in-archive.txt b/samples/tuningfork/experimentsdemo/app/CMakeLists.for-samples-in-archive.txt
index 1f935af..8f07a10 100644
--- a/samples/tuningfork/experimentsdemo/app/CMakeLists.for-samples-in-archive.txt
+++ b/samples/tuningfork/experimentsdemo/app/CMakeLists.for-samples-in-archive.txt
@@ -1,4 +1,8 @@
cmake_minimum_required(VERSION 3.4.1)
+project(tuningfork-demo)
+
+find_package(games-performance-tuner REQUIRED CONFIG)
+find_package(games-frame-pacing REQUIRED CONFIG)
# If you have protobuf installed from a different directory, set it here. The source version
# must match the protoc version.
@@ -10,12 +14,6 @@
set( PROTOBUF_NANO_SRC_DIR "${CMAKE_CURRENT_LIST_DIR}/../../external/nanopb-c")
set( GAMESDK_BUILD_TUNINGFORK ON)
-include("../../../gamesdk.cmake")
-
-# This uses Tuningfork as a static library from the Game SDK package. Make sure the Android
-# SDK and NDK versions that you are using are supported by the Game SDK.
-add_gamesdk_target(PACKAGE_DIR "../../../../" BUILD_TYPE "Release")
-
include("../../../../src/protobuf/protobuf.cmake")
protobuf_generate_full_cpp( ${CMAKE_CURRENT_SOURCE_DIR}/src/main/proto src/main/proto/dev_tuningfork.proto)
@@ -36,7 +34,7 @@
)
target_compile_options(protobuf-static PUBLIC "-Wno-tautological-constant-compare" "-Wno-enum-compare-switch")
-add_library( native-lib
+add_library( ${CMAKE_PROJECT_NAME}
SHARED
src/main/cpp/experimentsdemo.cpp
../../common/Renderer.cpp
@@ -45,9 +43,9 @@
${PROTO_GENS_DIR}/full/tuningfork.pb.cc
)
-target_link_libraries( native-lib
- swappy
- tuningfork
+target_link_libraries( ${CMAKE_PROJECT_NAME}
+ games-frame-pacing::swappy_static
+ games-performance-tuner::tuningfork_static
protobuf-static
android
EGL
diff --git a/samples/tuningfork/experimentsdemo/app/CMakeLists.txt b/samples/tuningfork/experimentsdemo/app/CMakeLists.txt
index e397a41..4f081f6 100644
--- a/samples/tuningfork/experimentsdemo/app/CMakeLists.txt
+++ b/samples/tuningfork/experimentsdemo/app/CMakeLists.txt
@@ -1,4 +1,8 @@
cmake_minimum_required(VERSION 3.4.1)
+project(tuningfork-demo)
+
+find_package(games-performance-tuner REQUIRED CONFIG)
+find_package(games-frame-pacing REQUIRED CONFIG)
# If you have protobuf installed from a different directory, set it here. The source version
# must match the protoc version.
@@ -8,21 +12,12 @@
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DGOOGLE_PROTOBUF_NO_RTTI -DHAVE_PTHREAD")
set( GAMESDK_BUILD_TUNINGFORK ON)
-include("../../../gamesdk.cmake")
-
-# This builds the gamesdk in package/localtf and adds a gamesdk static library target
-add_gamesdk_target(
- DO_LOCAL_BUILD
- ROOT_DIR "../../../.."
- PACKAGE_DIR "../../../../../package/local"
- LIBRARIES "swappy,tuningfork"
-)
# Uncomment to add the Game SDK sources as part of the project sources, allowing to develop
# (with auto completions) and debug Tuning Fork from Android Studio using this sample.
#add_gamesdk_sources()
-include("../../../../src/protobuf/protobuf.cmake")
+include("../../../../games-performance-tuner/protobuf/protobuf.cmake")
protobuf_generate_full_cpp( ${CMAKE_CURRENT_SOURCE_DIR}/src/main/proto src/main/proto/dev_tuningfork.proto)
protobuf_generate_full_cpp( ${CMAKE_CURRENT_SOURCE_DIR}/src/main/proto src/main/proto/tuningfork.proto)
@@ -41,7 +36,7 @@
)
target_compile_options(protobuf-static PUBLIC "-Wno-tautological-constant-compare" "-Wno-enum-compare-switch")
-add_library( native-lib
+add_library( ${CMAKE_PROJECT_NAME}
SHARED
src/main/cpp/experimentsdemo.cpp
../../common/Renderer.cpp
@@ -51,9 +46,9 @@
${PROTO_GENS_DIR}/full/tuningfork.pb.cc
)
-target_link_libraries( native-lib
- swappy
- tuningfork
+target_link_libraries( ${CMAKE_PROJECT_NAME}
+ games-frame-pacing::swappy_static
+ games-performance-tuner::tuningfork_static
protobuf-static
android
EGL
diff --git a/samples/tuningfork/experimentsdemo/app/build.gradle b/samples/tuningfork/experimentsdemo/app/build.gradle
index 2cef1ff..fa36b18 100644
--- a/samples/tuningfork/experimentsdemo/app/build.gradle
+++ b/samples/tuningfork/experimentsdemo/app/build.gradle
@@ -7,7 +7,7 @@
ndkVersion "20.0.5594570"
defaultConfig {
applicationId "com.tuningfork.experimentsdemo"
- minSdkVersion 16
+ minSdkVersion 19
targetSdkVersion 28
versionCode 17
versionName "1.0"
@@ -31,11 +31,21 @@
path "CMakeLists.txt"
}
}
+ buildFeatures {
+ prefab true
+ }
}
dependencies {
- implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:28.0.0'
+ // Uncomment the line below (implementation fileTree...)
+ // and comment out "implementation project("...")"
+ // to use a locally built .aar
+ // See ../settings.gradle as well.
+ //implementation fileTree(dir: '../../../../', include: ['games-performance-tuner-release.aar',
+ // 'games-frame-pacing-release.aar'])
+ implementation project(':games-performance-tuner')
+ implementation project(':games-frame-pacing')
// GMS Core dependencies
implementation "com.google.android.gms:play-services-clearcut:16.0.0"
@@ -44,7 +54,7 @@
}
task createJar(type: GradleBuild) {
- buildFile = '../../../../src/tuningfork/tools/validation/build.gradle'
+ buildFile = '../../../../games-performance-tuner/tools/validation/build.gradle'
tasks = ['createJar']
}
@@ -56,15 +66,14 @@
return "../../../../third_party/protobuf-3.0.0/install/" + platformName
}
-task buildTuningForkBinFiles(type: Exec) {
+task buildTuningForkBinFiles(type: JavaExec) {
dependsOn createJar
- commandLine "java",
- "-jar",
- "../../../../src/tuningfork/tools/validation/build/libs/TuningforkApkValidationTool.jar",
+ main "-jar"
+ args( "../../../../games-performance-tuner/tools/validation/build/libs/TuningforkApkValidationTool.jar",
"--tuningforkPath",
"src/main/assets/tuningfork",
"--protoCompiler",
- getProtocPath()
+ getProtocPath())
}
tasks.preBuild.dependsOn("buildTuningForkBinFiles")
diff --git a/samples/tuningfork/experimentsdemo/app/src/main/java/com/tuningfork/experimentsdemo/TFTestActivity.java b/samples/tuningfork/experimentsdemo/app/src/main/java/com/tuningfork/experimentsdemo/TFTestActivity.java
index 1a7f86b..b402445 100644
--- a/samples/tuningfork/experimentsdemo/app/src/main/java/com/tuningfork/experimentsdemo/TFTestActivity.java
+++ b/samples/tuningfork/experimentsdemo/app/src/main/java/com/tuningfork/experimentsdemo/TFTestActivity.java
@@ -29,7 +29,7 @@
public class TFTestActivity extends AppCompatActivity implements Choreographer.FrameCallback, SurfaceHolder.Callback {
static {
- System.loadLibrary("native-lib");
+ System.loadLibrary("tuningfork-demo");
}
public native void initTuningFork(boolean initFromNewThread);
public static native void resize(Surface surface, int width, int height);
diff --git a/samples/tuningfork/experimentsdemo/app/src/main/proto/tuningfork.proto b/samples/tuningfork/experimentsdemo/app/src/main/proto/tuningfork.proto
index b8c6d20..5ea3acc 120000
--- a/samples/tuningfork/experimentsdemo/app/src/main/proto/tuningfork.proto
+++ b/samples/tuningfork/experimentsdemo/app/src/main/proto/tuningfork.proto
@@ -1 +1 @@
-../../../../../../../src/tuningfork/proto/tuningfork.proto
\ No newline at end of file
+../../../../../../../games-performance-tuner/proto/tuningfork.proto
\ No newline at end of file
diff --git a/samples/tuningfork/experimentsdemo/build.gradle b/samples/tuningfork/experimentsdemo/build.gradle
index 3a48c17..ead751a 100644
--- a/samples/tuningfork/experimentsdemo/build.gradle
+++ b/samples/tuningfork/experimentsdemo/build.gradle
@@ -7,7 +7,7 @@
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.5.2'
+ classpath 'com.android.tools.build:gradle:7.2.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
diff --git a/samples/tuningfork/experimentsdemo/gradle.properties b/samples/tuningfork/experimentsdemo/gradle.properties
index 743d692..ae63651 100644
--- a/samples/tuningfork/experimentsdemo/gradle.properties
+++ b/samples/tuningfork/experimentsdemo/gradle.properties
@@ -7,6 +7,7 @@
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
+android.useAndroidX=true
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
diff --git a/samples/tuningfork/experimentsdemo/settings.gradle b/samples/tuningfork/experimentsdemo/settings.gradle
index 9d495b3..c385f27 100644
--- a/samples/tuningfork/experimentsdemo/settings.gradle
+++ b/samples/tuningfork/experimentsdemo/settings.gradle
@@ -1 +1,9 @@
-include ':app'
\ No newline at end of file
+include ':app'
+// Comment all the lines below to use a locally built .aar
+// See app/build.gradle as well
+include ':games-performance-tuner'
+include ':games-frame-pacing:extras'
+include ':games-frame-pacing'
+project(':games-performance-tuner').projectDir = file('../../../games-performance-tuner')
+project(':games-frame-pacing').projectDir = file('../../../games-frame-pacing')
+project(':games-frame-pacing:extras').projectDir = file('../../../games-frame-pacing/extras')
diff --git a/samples/tuningfork/insightsdemo/app/CMakeLists.for-samples-in-archive.txt b/samples/tuningfork/insightsdemo/app/CMakeLists.for-samples-in-archive.txt
index 269feef..b368553 100644
--- a/samples/tuningfork/insightsdemo/app/CMakeLists.for-samples-in-archive.txt
+++ b/samples/tuningfork/insightsdemo/app/CMakeLists.for-samples-in-archive.txt
@@ -1,4 +1,8 @@
cmake_minimum_required(VERSION 3.4.1)
+project(insights-demo)
+
+find_package(games-performance-tuner REQUIRED CONFIG)
+find_package(games-frame-pacing REQUIRED CONFIG)
# If you have protobuf installed from a different directory, set it here. The source version
# must match the protoc version.
@@ -10,11 +14,6 @@
set( PROTOBUF_NANO_SRC_DIR "${CMAKE_CURRENT_LIST_DIR}/../../external/nanopb-c")
set( GAMESDK_BUILD_TUNINGFORK ON)
-include("../../../gamesdk.cmake")
-
-# This uses Tuningfork as a static library from the Game SDK package. Make sure the Android
-# SDK and NDK versions that you are using are supported by the Game SDK.
-add_gamesdk_target(PACKAGE_DIR "../../../../" BUILD_TYPE "Release")
include("../../../../src/protobuf/protobuf.cmake")
@@ -36,7 +35,7 @@
)
target_compile_options(protobuf-static PUBLIC "-Wno-tautological-constant-compare" "-Wno-enum-compare-switch")
-add_library( native-lib
+add_library( ${CMAKE_PROJECT_NAME}
SHARED
src/main/cpp/insightsdemo.cpp
../../common/Renderer.cpp
@@ -45,9 +44,9 @@
${PROTO_GENS_DIR}/full/tuningfork.pb.cc
)
-target_link_libraries( native-lib
- swappy
- tuningfork
+target_link_libraries( ${CMAKE_PROJECT_NAME}
+ games-frame-pacing::swappy_static
+ games-performance-tuner::tuningfork_static
protobuf-static
android
EGL
diff --git a/samples/tuningfork/insightsdemo/app/CMakeLists.txt b/samples/tuningfork/insightsdemo/app/CMakeLists.txt
index 875cda4..c6be638 100644
--- a/samples/tuningfork/insightsdemo/app/CMakeLists.txt
+++ b/samples/tuningfork/insightsdemo/app/CMakeLists.txt
@@ -1,4 +1,8 @@
cmake_minimum_required(VERSION 3.4.1)
+project(insights-demo)
+
+find_package(games-performance-tuner REQUIRED CONFIG)
+find_package(games-frame-pacing REQUIRED CONFIG)
# If you have protobuf installed from a different directory, set it here. The source version
# must match the protoc version.
@@ -10,19 +14,11 @@
set( GAMESDK_BUILD_TUNINGFORK ON)
include("../../../gamesdk.cmake")
-# This builds the gamesdk in package/localtf and adds a gamesdk static library target
-add_gamesdk_target(
- DO_LOCAL_BUILD
- ROOT_DIR "../../../.."
- PACKAGE_DIR "../../../../../package/local"
- LIBRARIES "swappy,tuningfork"
-)
-
# Uncomment to add the Game SDK sources as part of the project sources, allowing to develop
# (with auto completions) and debug Tuning Fork from Android Studio using this sample.
#add_gamesdk_sources()
-include("../../../../src/protobuf/protobuf.cmake")
+include("../../../../games-performance-tuner/protobuf/protobuf.cmake")
protobuf_generate_full_cpp( ${CMAKE_CURRENT_SOURCE_DIR}/src/main/proto src/main/proto/dev_tuningfork.proto)
protobuf_generate_full_cpp( ${CMAKE_CURRENT_SOURCE_DIR}/src/main/proto src/main/proto/tuningfork.proto)
@@ -41,7 +37,7 @@
)
target_compile_options(protobuf-static PUBLIC "-Wno-tautological-constant-compare" "-Wno-enum-compare-switch")
-add_library( native-lib
+add_library( ${CMAKE_PROJECT_NAME}
SHARED
src/main/cpp/insightsdemo.cpp
../../common/Renderer.cpp
@@ -51,9 +47,9 @@
${PROTO_GENS_DIR}/full/tuningfork.pb.cc
)
-target_link_libraries( native-lib
- swappy
- tuningfork
+target_link_libraries( ${CMAKE_PROJECT_NAME}
+ games-frame-pacing::swappy_static
+ games-performance-tuner::tuningfork_static
protobuf-static
android
EGL
diff --git a/samples/tuningfork/insightsdemo/app/build.gradle b/samples/tuningfork/insightsdemo/app/build.gradle
index 4abe08a..204c037 100644
--- a/samples/tuningfork/insightsdemo/app/build.gradle
+++ b/samples/tuningfork/insightsdemo/app/build.gradle
@@ -2,18 +2,21 @@
apply plugin: 'com.android.application'
+def stlType = 'c++_static'
+
android {
- compileSdkVersion 29
- ndkVersion "20.0.5594570"
+ compileSdkVersion 31
+ ndkVersion '23.1.7779620'
defaultConfig {
applicationId "com.tuningfork.insightsdemo"
- minSdkVersion 16
- targetSdkVersion 29
+ minSdkVersion 19
+ targetSdkVersion 31
versionCode 31
versionName "1.0.2"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
+ arguments "-DANDROID_STL=${stlType}"
}
}
}
@@ -22,16 +25,10 @@
}
buildTypes {
debug {
- ndk {
- abiFilters "arm64-v8a", "armeabi-v7a", "x86"
- }
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
- ndk {
- abiFilters "arm64-v8a", "armeabi-v7a", "x86"
- }
}
}
externalNativeBuild {
@@ -39,12 +36,24 @@
path "CMakeLists.txt"
}
}
+ buildFeatures {
+ prefab true
+ }
}
dependencies {
- implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:28.0.0'
+ // Uncomment the line below (implementation fileTree...)
+ // and comment out "implementation project("...")"
+ // to use a locally built .aar
+ // See ../settings.gradle as well.
+ //implementation fileTree(dir: '../../../../', include: ['games-performance-tuner-release.aar',
+ // 'games-frame-pacing-release.aar'])
+ implementation project(':games-performance-tuner')
+ implementation project(':games-frame-pacing')
+
+
// GMS Core dependencies
implementation "com.google.android.gms:play-services-clearcut:16.0.0"
@@ -52,7 +61,7 @@
}
task createJar(type: GradleBuild) {
- buildFile = '../../../../src/tuningfork/tools/validation/build.gradle'
+ buildFile = '../../../../games-performance-tuner/tools/validation/build.gradle'
tasks = ['createJar']
}
@@ -64,15 +73,14 @@
return "../../../../third_party/protobuf-3.0.0/install/" + platformName
}
-task buildTuningForkBinFiles(type: Exec) {
+task buildTuningForkBinFiles(type: JavaExec) {
dependsOn createJar
- commandLine "java",
- "-jar",
- "../../../../src/tuningfork/tools/validation/build/libs/TuningforkApkValidationTool.jar",
+ main "-jar"
+ args( "../../../../games-performance-tuner/tools/validation/build/libs/TuningforkApkValidationTool.jar",
"--tuningforkPath",
"src/main/assets/tuningfork",
"--protoCompiler",
- getProtocPath()
+ getProtocPath())
}
tasks.preBuild.dependsOn("buildTuningForkBinFiles")
diff --git a/samples/tuningfork/insightsdemo/app/src/main/AndroidManifest.xml b/samples/tuningfork/insightsdemo/app/src/main/AndroidManifest.xml
index ee833e6..375ac9f 100644
--- a/samples/tuningfork/insightsdemo/app/src/main/AndroidManifest.xml
+++ b/samples/tuningfork/insightsdemo/app/src/main/AndroidManifest.xml
@@ -14,7 +14,8 @@
tools:targetApi="28">
<activity
android:name=".TFTestActivity"
- android:screenOrientation="portrait">
+ android:screenOrientation="portrait"
+ android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
diff --git a/samples/tuningfork/insightsdemo/app/src/main/java/com/tuningfork/insightsdemo/TFTestActivity.java b/samples/tuningfork/insightsdemo/app/src/main/java/com/tuningfork/insightsdemo/TFTestActivity.java
index 6414daf..6adbe49 100644
--- a/samples/tuningfork/insightsdemo/app/src/main/java/com/tuningfork/insightsdemo/TFTestActivity.java
+++ b/samples/tuningfork/insightsdemo/app/src/main/java/com/tuningfork/insightsdemo/TFTestActivity.java
@@ -29,7 +29,7 @@
public class TFTestActivity extends AppCompatActivity implements Choreographer.FrameCallback, SurfaceHolder.Callback {
static {
- System.loadLibrary("native-lib");
+ System.loadLibrary("insights-demo");
}
public native void initTuningFork(boolean initFromNewThread);
public static native void resize(Surface surface, int width, int height);
diff --git a/samples/tuningfork/insightsdemo/app/src/main/proto/tuningfork.proto b/samples/tuningfork/insightsdemo/app/src/main/proto/tuningfork.proto
index b8c6d20..5ea3acc 120000
--- a/samples/tuningfork/insightsdemo/app/src/main/proto/tuningfork.proto
+++ b/samples/tuningfork/insightsdemo/app/src/main/proto/tuningfork.proto
@@ -1 +1 @@
-../../../../../../../src/tuningfork/proto/tuningfork.proto
\ No newline at end of file
+../../../../../../../games-performance-tuner/proto/tuningfork.proto
\ No newline at end of file
diff --git a/samples/tuningfork/insightsdemo/build.gradle b/samples/tuningfork/insightsdemo/build.gradle
index 3a48c17..3b98696 100644
--- a/samples/tuningfork/insightsdemo/build.gradle
+++ b/samples/tuningfork/insightsdemo/build.gradle
@@ -4,10 +4,10 @@
repositories {
google()
- jcenter()
+ mavenCentral()
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.5.2'
+ classpath 'com.android.tools.build:gradle:7.2.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
@@ -17,7 +17,7 @@
allprojects {
repositories {
google()
- jcenter()
+ mavenCentral()
}
}
diff --git a/samples/tuningfork/insightsdemo/gradle.properties b/samples/tuningfork/insightsdemo/gradle.properties
index 743d692..ae63651 100644
--- a/samples/tuningfork/insightsdemo/gradle.properties
+++ b/samples/tuningfork/insightsdemo/gradle.properties
@@ -7,6 +7,7 @@
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
+android.useAndroidX=true
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
diff --git a/samples/tuningfork/insightsdemo/settings.gradle b/samples/tuningfork/insightsdemo/settings.gradle
index 9d495b3..b735105 100644
--- a/samples/tuningfork/insightsdemo/settings.gradle
+++ b/samples/tuningfork/insightsdemo/settings.gradle
@@ -1 +1,7 @@
-include ':app'
\ No newline at end of file
+include ':app'
+include ':games-performance-tuner'
+include ':games-frame-pacing:extras'
+include ':games-frame-pacing'
+project(':games-performance-tuner').projectDir = file('../../../games-performance-tuner')
+project(':games-frame-pacing').projectDir = file('../../../games-frame-pacing')
+project(':games-frame-pacing:extras').projectDir = file('../../../games-frame-pacing/extras')
diff --git a/samples/unitypackaging/app/CMakeLists.txt b/samples/unitypackaging/app/CMakeLists.txt
index 6057886..957e72c 100644
--- a/samples/unitypackaging/app/CMakeLists.txt
+++ b/samples/unitypackaging/app/CMakeLists.txt
@@ -1,4 +1,5 @@
cmake_minimum_required(VERSION 3.4.1)
+project(androidgamesdk)
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -Werror -Wthread-safety" )
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D _LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS -O3 -fPIC" )
@@ -12,7 +13,7 @@
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../../include)
-add_library( androidgamesdk
+add_library( ${CMAKE_PROJECT_NAME}
SHARED
@@ -25,6 +26,6 @@
include_directories( src/main/cpp )
-target_link_libraries( androidgamesdk
+target_link_libraries( ${CMAKE_PROJECT_NAME}
EGL
log )
diff --git a/samples/unitypackaging/build.gradle b/samples/unitypackaging/build.gradle
index de3535b..5c7c694 100644
--- a/samples/unitypackaging/build.gradle
+++ b/samples/unitypackaging/build.gradle
@@ -7,8 +7,7 @@
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.1.3'
-
+ classpath 'com.android.tools.build:gradle:4.2.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
diff --git a/samples/unitypackaging/gradle/wrapper/gradle-wrapper.properties b/samples/unitypackaging/gradle/wrapper/gradle-wrapper.properties
index c5b0313..b909065 100644
--- a/samples/unitypackaging/gradle/wrapper/gradle-wrapper.properties
+++ b/samples/unitypackaging/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
diff --git a/settings.gradle b/settings.gradle
index 4b77cc2..28462cc 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,8 +1,8 @@
rootProject.name = 'androidgamesdk'
-include ':GameActivity'
-include ':GameTextInput'
-include ':GameController'
-include ':extras'
-project(':extras').projectDir= new File('src/extras')
-include 'extras:aar'
-include 'extras:apk'
+include ':game-activity'
+include ':game-text-input'
+include ':games-controller'
+include ':games-performance-tuner'
+include ':games-frame-pacing'
+include ':games-frame-pacing:extras'
+include ':games-memory-advice'
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 6065a53..d63a562 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,17 +1,9 @@
-cmake_minimum_required(VERSION 3.18.1)
+cmake_minimum_required(VERSION 3.10.1)
+project(android_game_sdk C CXX)
-if ("swappy" IN_LIST GAMESDK_LIBRARIES)
- add_subdirectory( ./swappy )
-endif()
-if ("tuningfork" IN_LIST GAMESDK_LIBRARIES)
- add_subdirectory( ./tuningfork )
-endif()
+project(android_game_sdk C CXX)
+set(IgnoreOldToolchainWarning "${ANDROID_UNIFIED_HEADERS}")
+
if ("memory_advice" IN_LIST GAMESDK_LIBRARIES)
add_subdirectory( ./memory_advice )
endif()
-if ("oboe" IN_LIST GAMESDK_LIBRARIES)
- add_subdirectory( ./oboe )
-endif()
-if ("paddleboat" IN_LIST GAMESDK_LIBRARIES)
- add_subdirectory( ./paddleboat )
-endif()
\ No newline at end of file
diff --git a/src/common/JNIUtil.h b/src/common/JNIUtil.h
index 434675a..702e24d 100644
--- a/src/common/JNIUtil.h
+++ b/src/common/JNIUtil.h
@@ -25,13 +25,25 @@
#include "Log.h"
-// This data comes from a binary resource linked to the library.
-// It contains Java classes compressed into DEX format for dynamic loading.
+// The code in this file will dynamically load Java classes from a binary
+// resource linked to the library. The binary data is in DEX format, accessible
+// using the _binary_classes_dex_start and _binary_classes_dex_end linker
+// symbols.
+
+// If you make AGDK classes available on the Java classpath, e.g. by adding them
+// to your game/engine's own Java component, you do not need to add the binary
+// resource and can instead define ANDROIDGAMESDK_NO_BINARY_DEX_LINKAGE which
+// will avoid the linker requiring these symbols.
+
+#ifndef ANDROIDGAMESDK_NO_BINARY_DEX_LINKAGE
extern const char _binary_classes_dex_start;
extern const char _binary_classes_dex_end;
+#endif
namespace gamesdk {
+#ifndef ANDROIDGAMESDK_NO_BINARY_DEX_LINKAGE
+
static bool saveBytesToFile(std::string fileName, const char* bytes,
size_t size) {
std::ofstream save_file(fileName, std::ios::binary);
@@ -93,6 +105,8 @@
return result;
}
+#endif // #ifndef ANDROIDGAMESDK_NO_BINARY_DEX_LINKAGE
+
static jclass loadClass(JNIEnv* env, jobject activity, const char* name,
JNINativeMethod* nativeMethods,
size_t nativeMethodsSize) {
@@ -119,6 +133,12 @@
if (env->ExceptionCheck()) {
env->ExceptionClear();
+#ifdef ANDROIDGAMESDK_NO_BINARY_DEX_LINKAGE
+ ALOGE(
+ "Couldn't find class %s and ANDROIDGAMESDK_NO_BINARY_DEX_LINKAGE "
+ "is defined",
+ name);
+#else
jstring dexLoaderClassName =
env->NewStringUTF("dalvik/system/InMemoryDexClassLoader");
jclass imclassloaderClass = static_cast<jclass>(env->CallObjectMethod(
@@ -210,6 +230,11 @@
if (imclassloaderClass) {
env->DeleteLocalRef(imclassloaderClass);
}
+#endif // #ifdef ANDROIDGAMESDK_NO_BINARY_DEX_LINKAGE
+ } else {
+ // Register the native methods loaded by the classLoaderClass
+ env->RegisterNatives(targetClass, nativeMethods,
+ nativeMethodsSize);
}
env->DeleteLocalRef(className);
return targetClass;
diff --git a/src/common/Log.h b/src/common/Log.h
index cb93297..9a9d085 100644
--- a/src/common/Log.h
+++ b/src/common/Log.h
@@ -23,10 +23,16 @@
#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
#define ALOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
-#define ALOGW_ONCE_IF(cond, ...) \
+#define ALOGW_ONCE_IF(cond, ...) \
+ do { \
+ if (cond) { \
+ ALOGW_ONCE(__VA_ARGS__); \
+ } \
+ } while (0)
+#define ALOGW_ONCE(...) \
do { \
static bool alogw_once##__FILE__##__LINE__##__ = true; \
- if (cond && alogw_once##__FILE__##__LINE__##__) { \
+ if (alogw_once##__FILE__##__LINE__##__) { \
alogw_once##__FILE__##__LINE__##__ = false; \
ALOGW(__VA_ARGS__); \
} \
diff --git a/src/common/Trace.h b/src/common/Trace.h
index 9929b1a..dcee5df 100644
--- a/src/common/Trace.h
+++ b/src/common/Trace.h
@@ -29,6 +29,11 @@
using ATrace_beginSection_type = void (*)(const char *sectionName);
using ATrace_endSection_type = void (*)();
using ATrace_isEnabled_type = bool (*)();
+
+ using ATrace_beginAsyncSection_type = void (*)(const char *sectionName,
+ int32_t cookie);
+ using ATrace_endAsyncSection_type = void (*)(const char *sectionName,
+ int32_t cookie);
using ATrace_setCounter_type = void (*)(const char *counterName,
int64_t counterValue);
@@ -39,10 +44,14 @@
Trace(ATrace_beginSection_type beginSection,
ATrace_endSection_type endSection, ATrace_isEnabled_type isEnabled,
+ ATrace_beginAsyncSection_type beginAsyncSection,
+ ATrace_endAsyncSection_type endAsyncSection,
ATrace_setCounter_type setCounter)
: ATrace_beginSection(beginSection),
ATrace_endSection(endSection),
ATrace_isEnabled(isEnabled),
+ ATrace_beginAsyncSection(beginAsyncSection),
+ ATrace_endAsyncSection(endAsyncSection),
ATrace_setCounter(setCounter) {}
static std::unique_ptr<Trace> create() {
@@ -74,7 +83,19 @@
/* ATrace_setCounter was added in API 29, continue even if it is not
* available */
+ auto beginAsyncSection =
+ reinterpret_cast<ATrace_beginAsyncSection_type>(
+ dlsym(libandroid, "ATrace_beginAsyncSection"));
+ /* ATrace_beginAsyncSection was added in API 29, continue even if it is
+ * not available */
+
+ auto endAsyncSection = reinterpret_cast<ATrace_endAsyncSection_type>(
+ dlsym(libandroid, "ATrace_endAsyncSection"));
+ /* ATrace_endAsyncSection was added in API 29, continue even if it is
+ * not available */
+
return std::make_unique<Trace>(beginSection, endSection, isEnabled,
+ beginAsyncSection, endAsyncSection,
setCounter);
}
@@ -100,6 +121,22 @@
ATrace_endSection();
}
+ void beginAsyncSection(const char *name, int64_t value) {
+ if (!ATrace_beginAsyncSection || !isEnabled()) {
+ return;
+ }
+
+ ATrace_beginAsyncSection(name, value);
+ }
+
+ void endAsyncSection(const char *name, int64_t value) {
+ if (!ATrace_endAsyncSection || !isEnabled()) {
+ return;
+ }
+
+ ATrace_endAsyncSection(name, value);
+ }
+
void setCounter(const char *name, int64_t value) {
if (!ATrace_setCounter || !isEnabled()) {
return;
@@ -108,6 +145,15 @@
ATrace_setCounter(name, value);
}
+ bool supportsBasicATrace() const {
+ return ATrace_beginSection && ATrace_endSection;
+ }
+
+ bool supportsFullATrace() const {
+ return supportsBasicATrace() && ATrace_beginAsyncSection &&
+ ATrace_endAsyncSection && ATrace_setCounter;
+ }
+
static Trace *getInstance() {
static std::unique_ptr<Trace> trace = Trace::create();
return trace.get();
@@ -117,6 +163,9 @@
const ATrace_beginSection_type ATrace_beginSection = nullptr;
const ATrace_endSection_type ATrace_endSection = nullptr;
const ATrace_isEnabled_type ATrace_isEnabled = nullptr;
+
+ const ATrace_beginAsyncSection_type ATrace_beginAsyncSection = nullptr;
+ const ATrace_endAsyncSection_type ATrace_endAsyncSection = nullptr;
const ATrace_setCounter_type ATrace_setCounter = nullptr;
};
diff --git a/src/common/jni/jni_helper.cpp b/src/common/jni/jni_helper.cpp
index f3e8b0a..43c94a9 100644
--- a/src/common/jni/jni_helper.cpp
+++ b/src/common/jni/jni_helper.cpp
@@ -148,6 +148,15 @@
Env()->DeleteLocalRef(exception);
return msg;
}
+std::string RemoveSensitiveInfoFromExceptionMessage(
+ const std::string& exception_message) {
+ std::string exception_prefix = "Exception:";
+ std::size_t found = exception_message.find(exception_prefix);
+ if (found != std::string::npos) {
+ return exception_message.substr(0, found + exception_prefix.length());
+ }
+ return exception_message;
+}
bool CheckForException(std::string& msg) {
if (Env()->ExceptionCheck()) {
msg = GetExceptionMessage();
diff --git a/src/common/jni/jni_helper.h b/src/common/jni/jni_helper.h
index 859f87a..abc9983 100644
--- a/src/common/jni/jni_helper.h
+++ b/src/common/jni/jni_helper.h
@@ -28,6 +28,16 @@
return A; \
}
+#define SAFE_LOGGING_CHECK_FOR_JNI_EXCEPTION_AND_RETURN(A, B) \
+ if (RawExceptionCheck()) { \
+ std::string exception_msg = \
+ B ? GetExceptionMessage() \
+ : RemoveSensitiveInfoFromExceptionMessage( \
+ GetExceptionMessage()); \
+ ALOGW("%s", exception_msg.c_str()); \
+ return A; \
+ }
+
namespace gamesdk {
namespace jni {
@@ -210,6 +220,8 @@
inline bool RawExceptionCheck() { return Env()->ExceptionCheck(); }
// This will clear the exception and get the exception message.
std::string GetExceptionMessage();
+std::string RemoveSensitiveInfoFromExceptionMessage(
+ const std::string& exception_message);
// Do a RawExceptionCheck and return the result of it, filling in the msg with
// the
// exception message if one was thrown. Also clears the exception.
diff --git a/src/common/jni/jni_wrap.h b/src/common/jni/jni_wrap.h
index 95e548d..6e84cca 100644
--- a/src/common/jni/jni_wrap.h
+++ b/src/common/jni/jni_wrap.h
@@ -315,6 +315,41 @@
namespace android {
+namespace util {
+
+class DisplayMetrics : public java::Object {
+ public:
+ DisplayMetrics(java::Object&& o) : java::Object(std::move(o)) {}
+ DisplayMetrics()
+ : java::Object("android/util/DisplayMetrics", "()V") {}
+ int heightPixels() const { return obj_.GetIntField("heightPixels"); }
+ int widthPixels() const { return obj_.GetIntField("widthPixels"); }
+};
+
+} // namespace util
+
+namespace view {
+
+class Display : public java::Object {
+ public:
+ Display(java::Object&& o) : java::Object(std::move(o)) {}
+ void getMetrics(util::DisplayMetrics& displayMetrics) {
+ CallOVMethod("getMetrics", "android/util/DisplayMetrics",
+ displayMetrics);
+ }
+};
+
+class WindowManager : public java::Object {
+ public:
+ WindowManager(java::Object&& o) : java::Object(std::move(o)) {}
+ Display getDefaultDisplay() {
+ return CallVOMethod("getDefaultDisplay",
+ "android/view/Display");
+ }
+};
+
+} // namespace view
+
namespace content {
namespace pm {
@@ -443,6 +478,10 @@
return CallVOMethod("getPackageManager",
"android/content/pm/PackageManager");
}
+ android::view::WindowManager getWindowManager() {
+ return CallVOMethod("getWindowManager",
+ "android/view/WindowManager");
+ }
jni::String getPackageName() { return CallVSMethod("getPackageName"); }
res::AssetManager getAssets() {
return CallVOMethod("getAssets", "android/content/res/AssetManager");
@@ -553,6 +592,22 @@
}
}; // Class Build
+class Process {
+ public:
+ static int myPid() {
+ JNIEnv* env = Env();
+ if (env != nullptr) {
+ LocalObject obj;
+ obj.Cast("android/os/Process");
+ jclass clz = obj;
+ jmethodID method = env->GetStaticMethodID(clz, "myPid", "()I");
+ if (method != NULL)
+ return (uint64_t)env->CallStaticIntMethod(clz, method);
+ }
+ return 0;
+ }
+}; // Class Process
+
class BatteryManager : java::Object {
public:
static constexpr const char* EXTRA_LEVEL = "level";
diff --git a/src/extras/build.gradle b/src/extras/build.gradle
deleted file mode 100644
index bbb2312..0000000
--- a/src/extras/build.gradle
+++ /dev/null
@@ -1,52 +0,0 @@
-project('aar') {
- apply plugin: 'com.android.library'
-}
-
-project('apk') {
- apply plugin: 'com.android.application'
-}
-
-subprojects {
-
- android {
- compileSdkVersion 28
- defaultConfig {
- minSdkVersion 16
- targetSdkVersion 28
- versionCode 1
- versionName "1.0"
- testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
- externalNativeBuild {
- cmake {
- cppFlags ""
- }
- }
- consumerProguardFiles 'lib-proguard-rules.txt'
- sourceSets {
- main {
- manifest.srcFile '../src/main/AndroidManifest.xml'
- java.srcDirs = ['../src/main/java']
- }
- }
- }
- buildTypes {
- release {
- minifyEnabled false
- }
- }
- lintOptions {
- abortOnError false
- }
- }
-
- dependencies {
- implementation fileTree(dir: 'libs', include: ['*.jar'])
- testImplementation 'junit:junit:4.12'
- }
-
-}
-
-task assembleRelease {
- dependsOn 'aar:assembleRelease'
- dependsOn 'apk:assembleRelease'
-}
\ No newline at end of file
diff --git a/src/maven/games-memory-advice.pom b/src/maven/games-memory-advice.pom
new file mode 100644
index 0000000..aff29db
--- /dev/null
+++ b/src/maven/games-memory-advice.pom
@@ -0,0 +1,19 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
+ http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>androidx.games</groupId>
+ <artifactId>games-memory-advice</artifactId>
+ <version>@aarVersion@</version>
+ <packaging>aar</packaging>
+
+ <licenses>
+ <license>
+ <name>The Apache Software License, Version 2.0</name>
+ <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+ <distribution>repo</distribution>
+ </license>
+ </licenses>
+</project>
diff --git a/src/memory_advice/CMakeLists.txt b/src/memory_advice/CMakeLists.txt
deleted file mode 100644
index b03d396..0000000
--- a/src/memory_advice/CMakeLists.txt
+++ /dev/null
@@ -1,107 +0,0 @@
-cmake_minimum_required(VERSION 3.18.1)
-project(memory-advice C CXX)
-set(CMAKE_CXX_STANDARD 14)
-
-include("../../samples/gamesdk.cmake")
-
-set( MEMORYADVICE_ASSETS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../test/memoryadvice/memoryadvice/src/main/assets/memoryadvice")
-set( MEMORYADVICE_RESOURCES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../test/memoryadvice/memoryadvice/src/main/resources")
-set( THIRD_PARTY_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../third_party")
-set( TENSORFLOW_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../external/tensorflow")
-set( EXTERNAL_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../external")
-set( TFLITE_FLATBUFFERS_SCHEMA_DIR "${CMAKE_CURRENT_BINARY_DIR}/../flatbuffers/include")
-
-set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wthread-safety" )
-set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D _LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS -O3 -fPIC" )
-set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions" )
-set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti" )
-set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffunction-sections -fdata-sections" )
-set( CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -g0")
-
-set( CPUINFO_SOURCE_DIR "${EXTERNAL_SOURCE_DIR}/cpuinfo")
-set( CLOG_SOURCE_DIR "${EXTERNAL_SOURCE_DIR}/cpuinfo")
-set( PTHREADPOOL_SOURCE_DIR "${EXTERNAL_SOURCE_DIR}/pthreadpool")
-set( FXDIV_SOURCE_DIR "${EXTERNAL_SOURCE_DIR}/FXdiv")
-set( FP16_SOURCE_DIR "${EXTERNAL_SOURCE_DIR}/FP16")
-set( PSIMD_SOURCE_DIR "${EXTERNAL_SOURCE_DIR}/psimd")
-
-if (${CMAKE_BUILD_TYPE} STREQUAL "Release")
-set( CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--gc-sections,-s")
-endif (${CMAKE_BUILD_TYPE} STREQUAL "Release")
-set( CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--hash-style=both" )
-
-set(LOOKUP_STRING "namespace memory_advice {\nconst char* lookup_string = R\"LOOKUP(\n")
-string(APPEND LOOKUP_STRING "\n )LOOKUP\";\n}\n")
-file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/device_profiler_lookup.cpp" "${LOOKUP_STRING}")
-
-file(READ "${MEMORYADVICE_ASSETS_DIR}/default.json" PARAMS_FILE)
-set(PARAMS_STRING "namespace memory_advice {\nconst char* parameters_string = R\"PARAMS(\n")
-string(APPEND PARAMS_STRING "${PARAMS_FILE}")
-string(APPEND PARAMS_STRING "\n )PARAMS\";\n}\n")
-file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/advisor_parameters.cpp" "${PARAMS_STRING}")
-
-include_directories( . )
-include_directories( ../../include )
-include_directories( ../common )
-include_directories( ${CMAKE_CURRENT_BINARY_DIR} )
-include_directories( ${THIRD_PARTY_DIR} )
-
-add_subdirectory(
- "${EXTERNAL_SOURCE_DIR}/abseil-cpp"
- "${CMAKE_CURRENT_BINARY_DIR}/abseil-cpp"
- EXCLUDE_FROM_ALL
-)
-
-set(TFLITE_INCLUDE_DIRS
- "${TENSORFLOW_SOURCE_DIR}"
- "${TFLITE_FLATBUFFERS_SCHEMA_DIR}"
-)
-include_directories(
- BEFORE
- ${TFLITE_INCLUDE_DIRS}
-)
-
-add_subdirectory(
- "${TENSORFLOW_SOURCE_DIR}/tensorflow/lite"
- "${CMAKE_CURRENT_BINARY_DIR}/tensorflow-lite"
- EXCLUDE_FROM_ALL
-)
-
-
-set(MEMORY_ADVICE_SRCS
- c_header_check.c
- core/device_profiler.cpp
- core/memory_advice.cpp
- core/memory_advice_impl.cpp
- core/memory_advice_c.cpp
- core/metrics_provider.cpp
- core/state_watcher.cpp
- core/predictor.cpp
- ../common/jni/jni_helper.cpp
- ../common/jni/jni_wrap.cpp
- ../common/jni/jnictx.cpp
- ../common/apk_utils.cpp
- ${THIRD_PARTY_DIR}/json11/json11.cpp
- device_profiler_lookup.cpp
- advisor_parameters.cpp
- ${MEMORYADVICE_RESOURCES_DIR}/realtime.tflite
- ${MEMORYADVICE_RESOURCES_DIR}/oom_features.json
-)
-
-add_library(memory_advice_static STATIC ${MEMORY_ADVICE_SRCS})
-add_library(memory_advice SHARED ${MEMORY_ADVICE_SRCS})
-target_link_libraries(memory_advice
- PUBLIC
- android
- log
- absl::flags
- tensorflow-lite
-)
-
-target_link_libraries(memory_advice_static
- PUBLIC
- android
- log
- absl::flags
- tensorflow-lite
-)
diff --git a/src/memory_advice/core/device_profiler.cpp b/src/memory_advice/core/device_profiler.cpp
deleted file mode 100644
index f42efbe..0000000
--- a/src/memory_advice/core/device_profiler.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "device_profiler.h"
-
-#include <stdlib.h>
-
-#include <algorithm>
-#include <fstream>
-#include <iterator>
-#include <map>
-#include <sstream>
-#include <streambuf>
-#include <string>
-
-#include "Log.h"
-#include "jni/jni_wrap.h"
-#include "memory_advice/memory_advice.h"
-
-#define LOG_TAG "MemoryAdvice:DeviceProfiler"
-
-namespace memory_advice {
-
-extern const char* lookup_string;
-
-using namespace json11;
-
-MemoryAdvice_ErrorCode DeviceProfiler::Init() {
- std::string err;
- lookup_table_ = std::make_unique<Json::object>(
- Json::parse(lookup_string, err).object_items());
- if (!err.empty()) {
- ALOGE("Error while parsing lookup table: %s", err.c_str());
- return MEMORYADVICE_ERROR_LOOKUP_TABLE_INVALID;
- }
-
- fingerprint_ = gamesdk::jni::android::os::Build::FINGERPRINT().C();
-
- return MEMORYADVICE_ERROR_OK;
-}
-
-Json::object DeviceProfiler::GetDeviceProfile() const {
- Json::object profile;
- // TODO(b/209602631): implement match by baseline
- std::string best = MatchByFingerprint();
- profile["limits"] = lookup_table_->at(best);
- profile["matched"] = best;
- profile["fingerprint"] = fingerprint_;
-
- return profile;
-}
-
-std::string DeviceProfiler::MatchByFingerprint() const {
- int best_score = -1;
- std::string best;
-
- for (auto& it : *lookup_table_) {
- int mismatch_index = std::mismatch(fingerprint_.begin(),
- fingerprint_.end(), it.first.begin())
- .first -
- fingerprint_.begin();
- if (mismatch_index > best_score) {
- best_score = mismatch_index;
- best = it.first;
- }
- }
- return best;
-}
-
-} // namespace memory_advice
\ No newline at end of file
diff --git a/src/memory_advice/core/device_profiler.h b/src/memory_advice/core/device_profiler.h
deleted file mode 100644
index b15e67b..0000000
--- a/src/memory_advice/core/device_profiler.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <memory>
-#include <string>
-
-#include "json11/json11.hpp"
-#include "memory_advice/memory_advice.h"
-
-namespace memory_advice {
-
-using namespace json11;
-
-class DeviceProfiler {
- private:
- std::unique_ptr<Json::object> lookup_table_;
- std::string fingerprint_;
-
- public:
- MemoryAdvice_ErrorCode Init();
- Json::object GetDeviceProfile() const;
- std::string MatchByFingerprint() const;
-};
-
-} // namespace memory_advice
\ No newline at end of file
diff --git a/src/memory_advice/core/memory_advice_c.cpp b/src/memory_advice/core/memory_advice_c.cpp
deleted file mode 100644
index 1d30072..0000000
--- a/src/memory_advice/core/memory_advice_c.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "jni/jni_helper.h"
-#include "memory_advice/memory_advice.h"
-#include "memory_advice_internal.h"
-#include "metrics_provider.h"
-
-namespace jni = gamesdk::jni;
-
-extern "C" {
-
-MemoryAdvice_ErrorCode MemoryAdvice_initDefaultParams_internal(
- JNIEnv *env, jobject context) {
- jni::Init(env, context);
- return memory_advice::Init();
-}
-
-MemoryAdvice_ErrorCode MemoryAdvice_init_internal(JNIEnv *env, jobject context,
- const char *params) {
- jni::Init(env, context);
- return memory_advice::Init(params);
-}
-
-MemoryAdvice_ErrorCode MemoryAdvice_getMemoryState(
- MemoryAdvice_MemoryState *state) {
- return memory_advice::GetMemoryState(state);
-}
-
-MemoryAdvice_ErrorCode MemoryAdvice_getAdvice(
- MemoryAdvice_JsonSerialization *advice) {
- return memory_advice::GetAdvice(advice);
-}
-
-MemoryAdvice_ErrorCode MemoryAdvice_setWatcher(
- uint64_t intervalMillis, MemoryAdvice_WatcherCallback callback) {
- return memory_advice::SetWatcher(intervalMillis, callback);
-}
-
-MemoryAdvice_ErrorCode MemoryAdvice_removeWatcher() {
- return memory_advice::RemoveWatcher();
-}
-
-MemoryAdvice_ErrorCode MemoryAdvice_getAvailableMemory(int64_t *estimate) {
- return memory_advice::GetAvailableMemory(estimate);
-}
-
-void MEMORY_ADVICE_VERSION_SYMBOL() {
- // Intentionally empty: this function is used to ensure that the proper
- // version of the library is linked against the proper headers.
- // In case of mismatch, a linker error will be triggered because of an
- // undefined symbol, as the name of the function depends on the version.
-}
-
-} // extern "C"
diff --git a/src/memory_advice/core/memory_advice_impl.cpp b/src/memory_advice/core/memory_advice_impl.cpp
deleted file mode 100644
index 50f8cc1..0000000
--- a/src/memory_advice/core/memory_advice_impl.cpp
+++ /dev/null
@@ -1,381 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "memory_advice_impl.h"
-
-#include <chrono>
-
-namespace memory_advice {
-
-using namespace json11;
-
-MemoryAdviceImpl::MemoryAdviceImpl(const char* params) {
- metrics_provider_ = std::make_unique<MetricsProvider>();
- realtime_predictor_ = std::make_unique<Predictor>();
- available_predictor_ = std::make_unique<Predictor>();
- oom_predictor_ = std::make_unique<Predictor>();
-
- initialization_error_code_ =
- realtime_predictor_->Init("realtime.tflite", "realtime_features.json");
- if (initialization_error_code_ != MEMORYADVICE_ERROR_OK) {
- return;
- }
- initialization_error_code_ = available_predictor_->Init(
- "available.tflite", "available_features.json");
- if (initialization_error_code_ != MEMORYADVICE_ERROR_OK) {
- return;
- }
- initialization_error_code_ =
- oom_predictor_->Init("oom.tflite", "oom_features.json");
- if (initialization_error_code_ != MEMORYADVICE_ERROR_OK) {
- return;
- }
- initialization_error_code_ = ProcessAdvisorParameters(params);
- if (initialization_error_code_ != MEMORYADVICE_ERROR_OK) {
- return;
- }
- baseline_ = GenerateBaselineMetrics();
- baseline_["constant"] = GenerateConstantMetrics();
-}
-
-MemoryAdvice_ErrorCode MemoryAdviceImpl::ProcessAdvisorParameters(
- const char* parameters) {
- std::string err;
- advisor_parameters_ = Json::parse(parameters, err).object_items();
- if (!err.empty()) {
- ALOGE("Error while parsing advisor parameters: %s", err.c_str());
- return MEMORYADVICE_ERROR_ADVISOR_PARAMETERS_INVALID;
- }
-
- return MEMORYADVICE_ERROR_OK;
-}
-
-MemoryAdvice_MemoryState MemoryAdviceImpl::GetMemoryState() {
- Json::object advice = GetAdviceDeprecated();
- if (advice.find("warnings") != advice.end()) {
- Json::array warnings = advice["warnings"].array_items();
- for (auto& it : warnings) {
- if (it.object_items().at("level").string_value() == "red") {
- return MEMORYADVICE_STATE_CRITICAL;
- }
- }
- return MEMORYADVICE_STATE_APPROACHING_LIMIT;
- }
- return MEMORYADVICE_STATE_OK;
-}
-
-int64_t MemoryAdviceImpl::GetAvailableMemory() {
- Json::object advice = GetAdviceDeprecated();
- if (advice.find("predictions") != advice.end()) {
- int64_t smallest_estimate = 0;
- Json::object predictions = advice["predictions"].object_items();
- for (auto& it : predictions) {
- if (smallest_estimate == 0 ||
- it.second.number_value() < smallest_estimate) {
- smallest_estimate = it.second.number_value();
- }
- }
- return smallest_estimate;
- }
- return 0;
-}
-
-Json::object MemoryAdviceImpl::GetAdvice() {
- std::lock_guard<std::mutex> lock(advice_mutex_);
- double start_time = MillisecondsSinceEpoch();
- Json::object advice;
- Json::object data;
- Json::object variable_spec = advisor_parameters_.at("metrics")
- .object_items()
- .at("variable")
- .object_items();
- Json::object variable_metrics = GenerateVariableMetrics();
-
- data["baseline"] = baseline_;
- data["sample"] = variable_metrics;
- // TODO(b/209602631): add build info here
- // data["build"] = build_;
-
- if (variable_spec.find("predictRealtime") != variable_spec.end() &&
- variable_spec.at("predictRealtime").bool_value()) {
- variable_metrics["predictedUsage"] =
- Json(realtime_predictor_->Predict(data));
- }
-
- if (variable_spec.find("availableRealtime") != variable_spec.end() &&
- variable_spec.at("availableRealtime").bool_value()) {
- variable_metrics["predictedAvailable"] =
- Json(realtime_predictor_->Predict(data));
- }
-
- advice["metrics"] = variable_metrics;
- return advice;
-}
-
-Json::object MemoryAdviceImpl::GetAdviceDeprecated() {
- std::lock_guard<std::mutex> lock(advice_mutex_);
- double start_time = MillisecondsSinceEpoch();
- Json::object advice;
- advice["metrics"] = GenerateVariableMetrics();
- Json::object device_limit =
- device_profile_.at("limits").object_items().at("limit").object_items();
- Json::object device_baseline = device_profile_.at("limits")
- .object_items()
- .at("baseline")
- .object_items();
-
- if (advisor_parameters_.find("heuristics") != advisor_parameters_.end()) {
- Json::array warnings;
- Json::object heuristics =
- advisor_parameters_["heuristics"].object_items();
-
- for (auto& it : heuristics) {
- std::string key = it.first;
- Json::object heuristic = it.second.object_items();
-
- Json metric_value = GetValue(advice["metrics"].object_items(), key);
- Json device_limit_value = GetValue(device_limit, key);
- Json device_baseline_value = GetValue(device_baseline, key);
- Json baseline_value = GetValue(baseline_, key);
-
- if (metric_value.is_null() || device_limit_value.is_null() ||
- device_baseline_value.is_null() || baseline_value.is_null()) {
- continue;
- }
-
- bool increasing = (device_limit_value > device_baseline_value);
-
- // Fires warnings as baseline-relative metrics approach ratios of
- // the device's baseline- relative limit. Example: "oom_score":
- // {"deltaLimit": {"red": 0.85, "yellow": 0.75}}
- if (heuristic.find("deltaLimit") != heuristic.end()) {
- Json::object delta_limit =
- heuristic["deltaLimit"].object_items();
- double limit_value = device_limit_value.number_value() -
- device_baseline_value.number_value();
- double relative_value =
- metric_value.number_value() - baseline_value.number_value();
- std::string level;
- if (increasing
- ? relative_value >
- limit_value * delta_limit["red"].number_value()
- : relative_value <
- limit_value * delta_limit["red"].number_value()) {
- level = "red";
- } else if (increasing
- ? relative_value >
- limit_value *
- delta_limit["yellow"].number_value()
- : relative_value <
- limit_value *
- delta_limit["yellow"].number_value()) {
- level = "yellow";
- }
- if (!level.empty()) {
- Json::object warning;
- Json::object trigger = {{"deltaLimit", delta_limit}};
- warning[key] = trigger;
- warning["level"] = level;
- warnings.push_back(warning);
- }
- }
-
- // Fires warnings as metrics approach ratios of the device's limit.
- // Example: "VmRSS": {"limit": {"red": 0.90, "yellow": 0.75}}
- if (heuristic.find("limit") != heuristic.end()) {
- Json::object limit = heuristic["limit"].object_items();
- std::string level;
- if (increasing ? metric_value.number_value() >
- device_limit_value.number_value() *
- limit["red"].number_value()
- : metric_value.number_value() *
- limit["red"].number_value() <
- device_limit_value.number_value()) {
- level = "red";
- } else if (increasing ? metric_value.number_value() >
- device_limit_value.number_value() *
- limit["yellow"].number_value()
- : metric_value.number_value() *
- limit["yellow"].number_value() <
- device_limit_value.number_value()) {
- level = "yellow";
- }
- if (!level.empty()) {
- Json::object warning;
- Json::object trigger = {{"limit", limit}};
- warning[key] = trigger;
- warning["level"] = level;
- warnings.push_back(warning);
- }
- }
-
- // Fires warnings as metrics approach ratios of the device baseline.
- // Example: "availMem": {"baselineRatio": {"red": 0.30, "yellow":
- // 0.40}}
- if (heuristic.find("baselineRatio") != heuristic.end()) {
- Json::object baseline_ratio =
- heuristic["baselineRatio"].object_items();
- std::string level;
- if (increasing ? metric_value.number_value() >
- baseline_value.number_value() *
- baseline_ratio["red"].number_value()
- : metric_value.number_value() <
- baseline_value.number_value() *
- baseline_ratio["red"].number_value()) {
- level = "red";
- } else if (increasing
- ? metric_value.number_value() >
- baseline_value.number_value() *
- baseline_ratio["yellow"].number_value()
- : metric_value.number_value() <
- baseline_value.number_value() *
- baseline_ratio["yellow"]
- .number_value()) {
- level = "yellow";
- }
- if (!level.empty()) {
- Json::object warning;
- Json::object trigger = {{"baselineRatio", baseline_ratio}};
- warning[key] = trigger;
- warning["level"] = level;
- warnings.push_back(warning);
- }
- }
- }
- if (!warnings.empty()) {
- advice["warnings"] = warnings;
- }
- }
-
- if (device_limit.find("stressed") != device_limit.end()) {
- Json::object stressed = device_limit["stressed"].object_items();
- if (stressed.find("applicationAllocated") != stressed.end()) {
- double application_allocated =
- stressed["applicationAllocated"].number_value();
- Json::object predictions;
- Json::object predictions_params =
- advisor_parameters_["predictions"].object_items();
-
- for (auto& it : predictions_params) {
- std::string key = it.first;
-
- Json metric_value =
- GetValue(advice["metrics"].object_items(), key);
- Json device_limit_value = GetValue(device_limit, key);
- Json device_baseline_value = GetValue(device_baseline, key);
- Json baseline_value = GetValue(baseline_, key);
-
- if (metric_value.is_null() || device_limit_value.is_null() ||
- device_baseline_value.is_null() ||
- baseline_value.is_null()) {
- continue;
- }
-
- double delta =
- metric_value.number_value() - baseline_value.number_value();
- double device_delta = device_limit_value.number_value() -
- device_baseline_value.number_value();
- if (device_delta == 0) {
- continue;
- }
-
- double percentage_estimate = delta / device_delta;
-
- predictions[key] =
- application_allocated * (1 - percentage_estimate);
- }
-
- advice["predictions"] = predictions;
- }
- }
- advice["meta"] =
- (Json::object){{"duration", MillisecondsSinceEpoch() - start_time}};
- return advice;
-}
-
-Json MemoryAdviceImpl::GetValue(Json::object object, std::string key) {
- if (object.find(key) != object.end()) {
- return object[key];
- }
- for (auto& it : object) {
- Json value = GetValue(it.second.object_items(), key);
- if (!value.is_null()) {
- return value;
- }
- }
- return Json();
-}
-
-Json::object MemoryAdviceImpl::GenerateMetricsFromFields(Json::object fields) {
- Json::object metrics;
- for (auto& it : metrics_provider_->metrics_categories_) {
- if (fields.find(it.first) != fields.end()) {
- metrics[it.first] = ExtractValues(it.second, fields[it.first]);
- }
- }
- metrics["meta"] = (Json::object){{"time", MillisecondsSinceEpoch()}};
- return metrics;
-}
-
-Json::object MemoryAdviceImpl::ExtractValues(
- MetricsProvider::MetricsFunction metrics_function, Json fields) {
- double start_time = MillisecondsSinceEpoch();
- Json::object metrics = (metrics_provider_.get()->*metrics_function)();
- Json::object extracted_metrics;
- if (fields.bool_value()) {
- extracted_metrics = metrics;
- } else {
- for (auto& it : fields.object_items()) {
- if (it.second.bool_value() &&
- metrics.find(it.first) != metrics.end()) {
- extracted_metrics[it.first] = metrics[it.first];
- }
- }
- }
-
- extracted_metrics["_meta"] = {
- {"duration", Json(MillisecondsSinceEpoch() - start_time)}};
- return extracted_metrics;
-}
-
-double MemoryAdviceImpl::MillisecondsSinceEpoch() {
- using namespace std::chrono;
- return duration_cast<milliseconds>(system_clock::now().time_since_epoch())
- .count();
-}
-
-Json::object MemoryAdviceImpl::GenerateVariableMetrics() {
- return GenerateMetricsFromFields(advisor_parameters_.at("metrics")
- .object_items()
- .at("variable")
- .object_items());
-}
-
-Json::object MemoryAdviceImpl::GenerateBaselineMetrics() {
- return GenerateMetricsFromFields(advisor_parameters_.at("metrics")
- .object_items()
- .at("baseline")
- .object_items());
-}
-
-Json::object MemoryAdviceImpl::GenerateConstantMetrics() {
- return GenerateMetricsFromFields(advisor_parameters_.at("metrics")
- .object_items()
- .at("constant")
- .object_items());
-}
-
-} // namespace memory_advice
diff --git a/src/memory_advice/core/memory_advice_internal.h b/src/memory_advice/core/memory_advice_internal.h
deleted file mode 100644
index 3a84f78..0000000
--- a/src/memory_advice/core/memory_advice_internal.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "memory_advice/memory_advice.h"
-
-// These functions are implemented in memory_advice.cpp.
-// They are mostly the same as the C interface, but take C++ types.
-
-namespace memory_advice {
-
-MemoryAdvice_ErrorCode Init();
-MemoryAdvice_ErrorCode Init(const char* params);
-MemoryAdvice_ErrorCode GetAdvice(MemoryAdvice_JsonSerialization* advice);
-MemoryAdvice_ErrorCode GetMemoryState(MemoryAdvice_MemoryState* state);
-MemoryAdvice_ErrorCode GetAvailableMemory(int64_t* estimate);
-MemoryAdvice_ErrorCode SetWatcher(uint64_t intervalMillis,
- MemoryAdvice_WatcherCallback callback);
-MemoryAdvice_ErrorCode RemoveWatcher();
-
-} // namespace memory_advice
diff --git a/src/memory_advice/core/predictor.cpp b/src/memory_advice/core/predictor.cpp
deleted file mode 100644
index 7d2a64d..0000000
--- a/src/memory_advice/core/predictor.cpp
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "predictor.h"
-
-#include <android/asset_manager.h>
-#include <android/asset_manager_jni.h>
-#include <stdlib.h>
-
-#include <algorithm>
-#include <filesystem>
-#include <fstream>
-#include <iterator>
-#include <map>
-#include <sstream>
-#include <streambuf>
-#include <string>
-
-#include "Log.h"
-#include "apk_utils.h"
-#include "jni/jni_wrap.h"
-#include "memory_advice/memory_advice.h"
-
-#define LOG_TAG "MemoryAdvice:DeviceProfiler"
-
-namespace memory_advice {
-
-using namespace tflite;
-using namespace json11;
-
-MemoryAdvice_ErrorCode Predictor::Init(std::string model_file,
- std::string features_file) {
- StderrReporter error_reporter;
-
- apk_utils::NativeAsset features_asset(features_file.c_str());
- std::string features_string(
- static_cast<const char*>(AAsset_getBuffer(features_asset)));
-
- features_string =
- features_string.substr(features_string.find_first_of('\n') + 1);
- int pos = 0;
- while ((pos = features_string.find_first_of('\n')) != std::string::npos) {
- std::string line(features_string.substr(0, pos));
- features.push_back(
- line.substr(line.find_first_of("/") + 1,
- line.find_last_of("\"") - line.find_first_of("/") - 1));
- features_string = features_string.substr(pos + 1);
- }
-
- apk_utils::NativeAsset model_asset(model_file.c_str());
- const char* model_buffer =
- static_cast<const char*>(AAsset_getBuffer(model_asset));
- const size_t model_capacity =
- static_cast<size_t>(AAsset_getLength(model_asset));
- auto model = tflite::FlatBufferModel::BuildFromBuffer(
- model_buffer, model_capacity, &error_reporter);
- std::unique_ptr<OpResolver> resolver = tflite::CreateOpResolver();
-
- if (InterpreterBuilder(*model, *resolver)(&interpreter) != kTfLiteOk) {
- return MEMORYADVICE_ERROR_TFLITE_MODEL_INVALID;
- }
- std::vector<int> sizes;
- sizes.push_back(features.size());
- if (interpreter->ResizeInputTensor(0, sizes) != kTfLiteOk) {
- return MEMORYADVICE_ERROR_TFLITE_MODEL_INVALID;
- }
- if (interpreter->AllocateTensors() != kTfLiteOk) {
- return MEMORYADVICE_ERROR_TFLITE_MODEL_INVALID;
- }
-
- return MEMORYADVICE_ERROR_OK;
-}
-
-float Predictor::GetFromPath(std::string feature, Json::object data) {
- int pos = 0;
- const Json::object* search = &data;
- while ((pos = feature.find_first_of("/")) != std::string::npos) {
- search = &(search->at(feature.substr(0, pos)).object_items());
- feature = feature.substr(pos + 1);
- }
-
- Json result = search->at(feature);
-
- if (result.is_number()) {
- return static_cast<float>(result.number_value());
- } else if (result.is_bool()) {
- return result.bool_value() ? 1.0f : 0.0f;
- } else {
- return 0.0f;
- }
-}
-
-float Predictor::Predict(Json::object data) {
- for (int idx = 0; idx != features.size(); idx++) {
- interpreter->typed_input_tensor<float>(0)[idx] =
- GetFromPath(features[idx], data);
- }
- // TODO(b/209602631): Fix Invoke() returning incorrect values.
- interpreter->Invoke();
-
- return *(interpreter->typed_output_tensor<float>(0));
-}
-
-} // namespace memory_advice
\ No newline at end of file
diff --git a/src/memory_advice/core/predictor.h b/src/memory_advice/core/predictor.h
deleted file mode 100644
index f9b5d72..0000000
--- a/src/memory_advice/core/predictor.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "json11/json11.hpp"
-#include "memory_advice/memory_advice.h"
-#include "tensorflow/lite/create_op_resolver.h"
-#include "tensorflow/lite/interpreter.h"
-#include "tensorflow/lite/interpreter_builder.h"
-#include "tensorflow/lite/kernels/register.h"
-#include "tensorflow/lite/model_builder.h"
-#include "tensorflow/lite/op_resolver.h"
-
-namespace memory_advice {
-
-using namespace json11;
-
-/**
-A class to help predict memory limits using tensorflow lite models.
-*/
-class Predictor {
- private:
- std::unique_ptr<tflite::Interpreter> interpreter;
- std::vector<std::string> features;
- float GetFromPath(std::string feature, Json::object data);
-
- public:
- MemoryAdvice_ErrorCode Init(std::string model_file,
- std::string features_file);
- float Predict(Json::object data);
-};
-
-} // namespace memory_advice
\ No newline at end of file
diff --git a/src/paddleboat b/src/paddleboat
index 42a5a7c..1735c34 120000
--- a/src/paddleboat
+++ b/src/paddleboat
@@ -1 +1 @@
-../GameController/src/main/cpp
\ No newline at end of file
+../games-controller/src/main/cpp
\ No newline at end of file
diff --git a/src/swappy/OWNERS b/src/swappy/OWNERS
deleted file mode 100644
index a4adbc5..0000000
--- a/src/swappy/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-stoza@google.com
-adyabr@google.com
-ianelliott@google.com
-
diff --git a/src/swappy/common/FrameStatistics.h b/src/swappy/common/FrameStatistics.h
deleted file mode 100644
index 10e5ef8..0000000
--- a/src/swappy/common/FrameStatistics.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <swappy/swappyGL_extra.h>
-
-namespace swappy {
-
-class FrameStatistics {
- public:
- virtual ~FrameStatistics() {}
- virtual int32_t lastLatencyRecorded() const = 0;
- // Only the essential latency statistics, not full.
- virtual bool isEssential() const = 0;
- virtual SwappyStats getStats() = 0;
-};
-
-} // namespace swappy
diff --git a/src/swappy/opengl/FrameStatisticsGL.cpp b/src/swappy/opengl/FrameStatisticsGL.cpp
deleted file mode 100644
index f90d31d..0000000
--- a/src/swappy/opengl/FrameStatisticsGL.cpp
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "FrameStatisticsGL.h"
-
-#define LOG_TAG "FrameStatisticsGL"
-
-#include <inttypes.h>
-
-#include <cmath>
-#include <string>
-
-#include "EGL.h"
-#include "Log.h"
-#include "SwappyCommon.h"
-#include "Trace.h"
-
-namespace swappy {
-
-int32_t LatencyFrameStatisticsGL::getFrameDelta(EGLnsecsANDROID start,
- EGLnsecsANDROID end) {
- const int64_t deltaTimeNano = end - start;
-
- int32_t numFrames =
- deltaTimeNano / mSwappyCommon.getRefreshPeriod().count();
- numFrames = std::max(
- 0, std::min(numFrames, static_cast<int32_t>(MAX_FRAME_BUCKETS) - 1));
- return numFrames;
-}
-
-LatencyFrameStatisticsGL::LatencyFrameStatisticsGL(
- const EGL& egl, const SwappyCommon& swappyCommon)
- : mEgl(egl), mSwappyCommon(swappyCommon) {}
-
-void LatencyFrameStatisticsGL::updateLatency(
- swappy::EGL::FrameTimestamps& frameStats, TimePoint frameStartTime) {
- int latency = getFrameDelta(frameStartTime.time_since_epoch().count(),
- frameStats.compositionLatched);
- TRACE_INT("FrameLatency", latency);
- mLastLatency = latency;
-}
-
-LatencyFrameStatisticsGL::ThisFrame LatencyFrameStatisticsGL::getThisFrame(
- EGLDisplay dpy, EGLSurface surface) {
- const TimePoint frameStartTime = std::chrono::steady_clock::now();
-
- // first get the next frame id
- std::pair<bool, EGLuint64KHR> nextFrameId =
- mEgl.getNextFrameId(dpy, surface);
- if (nextFrameId.first) {
- mPendingFrames.push_back(
- {dpy, surface, nextFrameId.second, frameStartTime});
- }
-
- if (mPendingFrames.empty()) {
- return {};
- }
-
- EGLFrame frame = mPendingFrames.front();
- // make sure we don't lag behind the stats too much
- if (nextFrameId.first && nextFrameId.second - frame.id > MAX_FRAME_LAG) {
- while (mPendingFrames.size() > 1)
- mPendingFrames.erase(mPendingFrames.begin());
- mPrevFrameTime = 0;
- frame = mPendingFrames.front();
- }
-#if (not defined ANDROID_NDK_VERSION) || ANDROID_NDK_VERSION >= 14
- std::unique_ptr<EGL::FrameTimestamps> frameStats =
- mEgl.getFrameTimestamps(frame.dpy, frame.surface, frame.id);
-
- if (!frameStats) {
- return {frame.startFrameTime};
- }
-
- mPendingFrames.erase(mPendingFrames.begin());
-
- return {frame.startFrameTime, std::move(frameStats)};
-#else
- return {frame.startFrameTime};
-#endif
-}
-
-// called once per swap
-void LatencyFrameStatisticsGL::capture(EGLDisplay dpy, EGLSurface surface) {
- auto frame = getThisFrame(dpy, surface);
- if (!frame.stats) return;
- updateLatency(*frame.stats, frame.startTime);
-}
-
-// NB This is only needed for C++14
-constexpr std::chrono::nanoseconds FullFrameStatisticsGL::LOG_EVERY_N_NS;
-
-FullFrameStatisticsGL::FullFrameStatisticsGL(const EGL& egl,
- const SwappyCommon& swappyCommon)
- : LatencyFrameStatisticsGL(egl, swappyCommon) {}
-
-int32_t FullFrameStatisticsGL::updateFrames(EGLnsecsANDROID start,
- EGLnsecsANDROID end,
- uint64_t stat[]) {
- int32_t numFrames = getFrameDelta(start, end);
- stat[numFrames]++;
- return numFrames;
-}
-
-void FullFrameStatisticsGL::updateIdleFrames(EGL::FrameTimestamps& frameStats) {
- updateFrames(frameStats.renderingCompleted, frameStats.compositionLatched,
- mStats.idleFrames);
-}
-
-void FullFrameStatisticsGL::updateLatencyFrames(
- swappy::EGL::FrameTimestamps& frameStats, TimePoint frameStartTime) {
- int latency =
- updateFrames(frameStartTime.time_since_epoch().count(),
- frameStats.compositionLatched, mStats.latencyFrames);
- TRACE_INT("FrameLatency", latency);
- mLastLatency = latency;
-}
-
-void FullFrameStatisticsGL::updateLateFrames(EGL::FrameTimestamps& frameStats) {
- updateFrames(frameStats.requested, frameStats.presented, mStats.lateFrames);
-}
-
-void FullFrameStatisticsGL::updateOffsetFromPreviousFrame(
- swappy::EGL::FrameTimestamps& frameStats) {
- if (mPrevFrameTime != 0) {
- updateFrames(mPrevFrameTime, frameStats.presented,
- mStats.offsetFromPreviousFrame);
- }
- mPrevFrameTime = frameStats.presented;
-}
-
-// called once per swap
-void FullFrameStatisticsGL::capture(EGLDisplay dpy, EGLSurface surface) {
- auto frame = getThisFrame(dpy, surface);
-
- if (!frame.stats) return;
-
- std::lock_guard<std::mutex> lock(mMutex);
- mStats.totalFrames++;
- updateIdleFrames(*frame.stats);
- updateLateFrames(*frame.stats);
- updateOffsetFromPreviousFrame(*frame.stats);
- updateLatencyFrames(*frame.stats, frame.startTime);
-
- logFrames();
-}
-
-void FullFrameStatisticsGL::logFrames() {
- static auto previousLogTime = std::chrono::steady_clock::now();
-
- if (std::chrono::steady_clock::now() - previousLogTime < LOG_EVERY_N_NS) {
- return;
- }
-
- std::string message;
- ALOGI("== Frame statistics ==");
- ALOGI("total frames: %" PRIu64, mStats.totalFrames);
- message += "Buckets: ";
- for (int i = 0; i < MAX_FRAME_BUCKETS; i++)
- message += "\t[" + swappy::to_string(i) + "]";
- ALOGI("%s", message.c_str());
-
- message = "";
- message += "idle frames: ";
- for (int i = 0; i < MAX_FRAME_BUCKETS; i++)
- message += "\t " + swappy::to_string(mStats.idleFrames[i]);
- ALOGI("%s", message.c_str());
-
- message = "";
- message += "late frames: ";
- for (int i = 0; i < MAX_FRAME_BUCKETS; i++)
- message += "\t " + swappy::to_string(mStats.lateFrames[i]);
- ALOGI("%s", message.c_str());
-
- message = "";
- message += "offset from previous frame: ";
- for (int i = 0; i < MAX_FRAME_BUCKETS; i++)
- message += "\t " + swappy::to_string(mStats.offsetFromPreviousFrame[i]);
- ALOGI("%s", message.c_str());
-
- message = "";
- message += "frame latency: ";
- for (int i = 0; i < MAX_FRAME_BUCKETS; i++)
- message += "\t " + swappy::to_string(mStats.latencyFrames[i]);
- ALOGI("%s", message.c_str());
-
- previousLogTime = std::chrono::steady_clock::now();
-}
-
-SwappyStats FullFrameStatisticsGL::getStats() {
- std::lock_guard<std::mutex> lock(mMutex);
- return mStats;
-}
-
-} // namespace swappy
diff --git a/src/swappy/opengl/FrameStatisticsGL.h b/src/swappy/opengl/FrameStatisticsGL.h
deleted file mode 100644
index 8e73413..0000000
--- a/src/swappy/opengl/FrameStatisticsGL.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <array>
-#include <atomic>
-#include <map>
-#include <vector>
-
-#include "EGL.h"
-#include "FrameStatistics.h"
-#include "Thread.h"
-
-using TimePoint = std::chrono::steady_clock::time_point;
-using namespace std::chrono_literals;
-
-namespace swappy {
-
-class SwappyCommon;
-
-// Just records latency
-class LatencyFrameStatisticsGL : public FrameStatistics {
- public:
- LatencyFrameStatisticsGL(const EGL& egl, const SwappyCommon& swappyCommon);
- ~LatencyFrameStatisticsGL() = default;
- int32_t lastLatencyRecorded() const override { return mLastLatency; }
- bool isEssential() const override { return true; }
- virtual SwappyStats getStats() override { return {}; }
-
- virtual void capture(EGLDisplay dpy, EGLSurface surface);
-
- protected:
- static constexpr int MAX_FRAME_LAG = 10;
- int32_t getFrameDelta(EGLnsecsANDROID start, EGLnsecsANDROID end);
- struct ThisFrame {
- TimePoint startTime;
- std::unique_ptr<EGL::FrameTimestamps> stats;
- };
- ThisFrame getThisFrame(EGLDisplay dpy, EGLSurface surface);
- void updateLatency(EGL::FrameTimestamps& frameStats,
- TimePoint frameStartTime);
-
- const EGL& mEgl;
- const SwappyCommon& mSwappyCommon;
-
- struct EGLFrame {
- EGLDisplay dpy;
- EGLSurface surface;
- EGLuint64KHR id;
- TimePoint startFrameTime;
- };
- std::vector<EGLFrame> mPendingFrames;
- EGLnsecsANDROID mPrevFrameTime = 0;
- std::atomic<int32_t> mLastLatency = {0};
-};
-
-class FullFrameStatisticsGL : public LatencyFrameStatisticsGL {
- public:
- FullFrameStatisticsGL(const EGL& egl, const SwappyCommon& swappyCommon);
- ~FullFrameStatisticsGL() = default;
-
- void capture(EGLDisplay dpy, EGLSurface surface) override;
-
- SwappyStats getStats() override;
-
- bool isEssential() const override { return false; }
-
- private:
- static constexpr std::chrono::nanoseconds LOG_EVERY_N_NS = 1s;
-
- int updateFrames(EGLnsecsANDROID start, EGLnsecsANDROID end,
- uint64_t stat[]);
- void updateIdleFrames(EGL::FrameTimestamps& frameStats) REQUIRES(mMutex);
- void updateLateFrames(EGL::FrameTimestamps& frameStats) REQUIRES(mMutex);
- void updateOffsetFromPreviousFrame(EGL::FrameTimestamps& frameStats)
- REQUIRES(mMutex);
- void updateLatencyFrames(EGL::FrameTimestamps& frameStats,
- TimePoint frameStartTime) REQUIRES(mMutex);
- void logFrames() REQUIRES(mMutex);
-
- std::mutex mMutex;
- SwappyStats mStats GUARDED_BY(mMutex) = {};
-};
-
-} // namespace swappy
diff --git a/src/swappy/vulkan/SwappyVkGoogleDisplayTiming.cpp b/src/swappy/vulkan/SwappyVkGoogleDisplayTiming.cpp
deleted file mode 100644
index 69e268e..0000000
--- a/src/swappy/vulkan/SwappyVkGoogleDisplayTiming.cpp
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#if (not defined ANDROID_NDK_VERSION) || ANDROID_NDK_VERSION >= 15
-
-#include "SwappyVkGoogleDisplayTiming.h"
-
-#define LOG_TAG "SwappyVkGoogleDisplayTiming"
-
-using std::chrono::nanoseconds;
-
-namespace swappy {
-
-SwappyVkGoogleDisplayTiming::SwappyVkGoogleDisplayTiming(
- JNIEnv* env, jobject jactivity, VkPhysicalDevice physicalDevice,
- VkDevice device, const SwappyVkFunctionProvider* provider)
- : SwappyVkBase(env, jactivity, physicalDevice, device, provider) {}
-
-bool SwappyVkGoogleDisplayTiming::doGetRefreshCycleDuration(
- VkSwapchainKHR swapchain, uint64_t* pRefreshDuration) {
- if (!isEnabled()) {
- ALOGE("Swappy is disabled.");
- return false;
- }
-
- VkRefreshCycleDurationGOOGLE refreshCycleDuration;
- VkResult res = mpfnGetRefreshCycleDurationGOOGLE(mDevice, swapchain,
- &refreshCycleDuration);
- if (res != VK_SUCCESS) {
- ALOGE("mpfnGetRefreshCycleDurationGOOGLE failed %d", res);
- return false;
- }
-
- *pRefreshDuration = mCommonBase.getRefreshPeriod().count();
-
- double refreshRate = 1000000000.0 / *pRefreshDuration;
- ALOGI("Returning refresh duration of %" PRIu64 " nsec (approx %f Hz)",
- *pRefreshDuration, refreshRate);
-
- return true;
-}
-
-VkResult SwappyVkGoogleDisplayTiming::doQueuePresent(
- VkQueue queue, uint32_t queueFamilyIndex,
- const VkPresentInfoKHR* pPresentInfo) {
- if (!isEnabled()) {
- ALOGE("Swappy is disabled.");
- return VK_ERROR_INITIALIZATION_FAILED;
- }
-
- VkResult res = initializeVkSyncObjects(queue, queueFamilyIndex);
- if (res) {
- return res;
- }
-
- const SwappyCommon::SwapHandlers handlers = {
- .lastFrameIsComplete = std::bind(
- &SwappyVkGoogleDisplayTiming::lastFrameIsCompleted, this, queue),
- .getPrevFrameGpuTime = std::bind(
- &SwappyVkGoogleDisplayTiming::getLastFenceTime, this, queue),
- };
-
- VkSemaphore semaphore;
- res = injectFence(queue, pPresentInfo, &semaphore);
- if (res) {
- ALOGE("Failed to vkQueueSubmit %d", res);
- return res;
- }
-
- uint32_t waitSemaphoreCount;
- const VkSemaphore* pWaitSemaphores;
- if (semaphore != VK_NULL_HANDLE) {
- waitSemaphoreCount = 1;
- pWaitSemaphores = &semaphore;
- } else {
- waitSemaphoreCount = pPresentInfo->waitSemaphoreCount;
- pWaitSemaphores = pPresentInfo->pWaitSemaphores;
- }
-
- mCommonBase.onPreSwap(handlers);
-
- VkPresentTimeGOOGLE pPresentTimes[pPresentInfo->swapchainCount];
- VkPresentInfoKHR replacementPresentInfo;
- VkPresentTimesInfoGOOGLE presentTimesInfo;
- if (mCommonBase.needToSetPresentationTime()) {
- // Setup the new structures to pass:
- for (uint32_t i = 0; i < pPresentInfo->swapchainCount; i++) {
- pPresentTimes[i].presentID = mNextPresentID;
- pPresentTimes[i].desiredPresentTime =
- mCommonBase.getPresentationTime().time_since_epoch().count();
- }
-
- presentTimesInfo = {VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE,
- pPresentInfo->pNext, pPresentInfo->swapchainCount,
- pPresentTimes};
-
- replacementPresentInfo = {
- pPresentInfo->sType, &presentTimesInfo,
- waitSemaphoreCount, pWaitSemaphores,
- pPresentInfo->swapchainCount, pPresentInfo->pSwapchains,
- pPresentInfo->pImageIndices, pPresentInfo->pResults};
-
- } else {
- replacementPresentInfo = {
- pPresentInfo->sType, nullptr,
- waitSemaphoreCount, pWaitSemaphores,
- pPresentInfo->swapchainCount, pPresentInfo->pSwapchains,
- pPresentInfo->pImageIndices, pPresentInfo->pResults};
- }
- mNextPresentID++;
-
- res = mpfnQueuePresentKHR(queue, &replacementPresentInfo);
- mCommonBase.onPostSwap(handlers);
-
- return res;
-}
-
-} // namespace swappy
-
-#endif // #if (not defined ANDROID_NDK_VERSION) || ANDROID_NDK_VERSION>=15
diff --git a/src/tuningfork/proto/descriptor.proto b/src/tuningfork/proto/descriptor.proto
deleted file mode 120000
index f4fb3a2..0000000
--- a/src/tuningfork/proto/descriptor.proto
+++ /dev/null
@@ -1 +0,0 @@
-../../../../external/protobuf/src/google/protobuf/descriptor.proto
\ No newline at end of file
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/any.proto b/src/tuningfork/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/any.proto
deleted file mode 120000
index a5356cf..0000000
--- a/src/tuningfork/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/any.proto
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../../../../../../third_party/protobuf-3.0.0/src/google/protobuf/any.proto
\ No newline at end of file
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/any_test.proto b/src/tuningfork/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/any_test.proto
deleted file mode 120000
index 7c18473..0000000
--- a/src/tuningfork/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/any_test.proto
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../../../../../../third_party/protobuf-3.0.0/src/google/protobuf/any_test.proto
\ No newline at end of file
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/api.proto b/src/tuningfork/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/api.proto
deleted file mode 120000
index d67957c..0000000
--- a/src/tuningfork/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/api.proto
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../../../../../../third_party/protobuf-3.0.0/src/google/protobuf/api.proto
\ No newline at end of file
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/descriptor.proto b/src/tuningfork/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/descriptor.proto
deleted file mode 120000
index 5854aa9..0000000
--- a/src/tuningfork/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/descriptor.proto
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../../../../../../third_party/protobuf-3.0.0/src/google/protobuf/descriptor.proto
\ No newline at end of file
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/duration.proto b/src/tuningfork/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/duration.proto
deleted file mode 120000
index 937d879..0000000
--- a/src/tuningfork/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/duration.proto
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../../../../../../third_party/protobuf-3.0.0/src/google/protobuf/duration.proto
\ No newline at end of file
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/empty.proto b/src/tuningfork/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/empty.proto
deleted file mode 120000
index 64091e2..0000000
--- a/src/tuningfork/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/empty.proto
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../../../../../../third_party/protobuf-3.0.0/src/google/protobuf/empty.proto
\ No newline at end of file
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/field_mask.proto b/src/tuningfork/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/field_mask.proto
deleted file mode 120000
index cd66937..0000000
--- a/src/tuningfork/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/field_mask.proto
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../../../../../../third_party/protobuf-3.0.0/src/google/protobuf/field_mask.proto
\ No newline at end of file
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/source_context.proto b/src/tuningfork/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/source_context.proto
deleted file mode 120000
index ec7c7e8..0000000
--- a/src/tuningfork/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/source_context.proto
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../../../../../../third_party/protobuf-3.0.0/src/google/protobuf/source_context.proto
\ No newline at end of file
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/struct.proto b/src/tuningfork/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/struct.proto
deleted file mode 120000
index 4057687..0000000
--- a/src/tuningfork/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/struct.proto
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../../../../../../third_party/protobuf-3.0.0/src/google/protobuf/struct.proto
\ No newline at end of file
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/timestamp.proto b/src/tuningfork/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/timestamp.proto
deleted file mode 120000
index c3deb87..0000000
--- a/src/tuningfork/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/timestamp.proto
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../../../../../../third_party/protobuf-3.0.0/src/google/protobuf/timestamp.proto
\ No newline at end of file
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/type.proto b/src/tuningfork/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/type.proto
deleted file mode 120000
index 2599ef8..0000000
--- a/src/tuningfork/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/type.proto
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../../../../../../third_party/protobuf-3.0.0/src/google/protobuf/type.proto
\ No newline at end of file
diff --git a/src/tuningfork/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/wrappers.proto b/src/tuningfork/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/wrappers.proto
deleted file mode 120000
index 89b216b..0000000
--- a/src/tuningfork/tools/TuningForkMonitor/app/src/main/proto/google/protobuf/wrappers.proto
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../../../../../../third_party/protobuf-3.0.0/src/google/protobuf/wrappers.proto
\ No newline at end of file
diff --git a/test/JNITestApp/app/build.gradle b/test/JNITestApp/app/build.gradle
index 6143e59..19eae8b 100644
--- a/test/JNITestApp/app/build.gradle
+++ b/test/JNITestApp/app/build.gradle
@@ -4,7 +4,7 @@
compileSdkVersion 28
defaultConfig {
applicationId "com.google.jnitestapp"
- minSdkVersion 16
+ minSdkVersion 19
targetSdkVersion 28
versionCode 7
versionName "1.0"
diff --git a/test/JNITestApp/app/src/androidTest/java/com/google/jnitestapp/TuningForkJNIInstrumentedTest.java b/test/JNITestApp/app/src/androidTest/java/com/google/jnitestapp/TuningForkJNIInstrumentedTest.java
index 8ab363c..d019602 100644
--- a/test/JNITestApp/app/src/androidTest/java/com/google/jnitestapp/TuningForkJNIInstrumentedTest.java
+++ b/test/JNITestApp/app/src/androidTest/java/com/google/jnitestapp/TuningForkJNIInstrumentedTest.java
@@ -24,9 +24,9 @@
*/
@RunWith(AndroidJUnit4.class)
public class TuningForkJNIInstrumentedTest {
- // Used to load the 'native-lib' library on application startup.
+ // Used to load app's native library on application startup.
static {
- System.loadLibrary("native-lib");
+ System.loadLibrary("JNITestApp");
}
static String TAG = "TuningForkJNIInstrumentedTest";
diff --git a/test/JNITestApp/app/src/main/cpp/CMakeLists.txt b/test/JNITestApp/app/src/main/cpp/CMakeLists.txt
index 6abfe10..b9e914c 100644
--- a/test/JNITestApp/app/src/main/cpp/CMakeLists.txt
+++ b/test/JNITestApp/app/src/main/cpp/CMakeLists.txt
@@ -4,20 +4,15 @@
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.4.1)
+project(JNITestApp)
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
-add_library( # Sets the name of the library.
- native-lib
-
- # Sets the library as a shared library.
- SHARED
-
- # Provides a relative path to your source file(s).
- native-lib.cpp
+add_library(${PROJECT_NAME} SHARED
+ jni_test_app.cpp
../../../../../../src/tuningfork/jni_helper.cpp
../../../../../../src/tuningfork/jni_wrap.cpp
@@ -33,19 +28,7 @@
include_directories(../../../../../../third_party)
include_directories(../../../../../../../external/nanopb-c)
-find_library( # Sets the name of the path variable.
- log-lib
- # Specifies the name of the NDK library that
- # you want CMake to locate.
- log)
-
-
-target_link_libraries( # Specifies the target library.
- native-lib
-
+target_link_libraries( ${PROJECT_NAME}
android
- # Links the target library to the log library
- # included in the NDK.
- ${log-lib}
- )
\ No newline at end of file
+ log )
\ No newline at end of file
diff --git a/test/JNITestApp/app/src/main/cpp/native-lib.cpp b/test/JNITestApp/app/src/main/cpp/jni_test_app.cpp
similarity index 100%
rename from test/JNITestApp/app/src/main/cpp/native-lib.cpp
rename to test/JNITestApp/app/src/main/cpp/jni_test_app.cpp
diff --git a/test/JNITestApp/build.gradle b/test/JNITestApp/build.gradle
index e11a5b3..0998836 100644
--- a/test/JNITestApp/build.gradle
+++ b/test/JNITestApp/build.gradle
@@ -7,7 +7,7 @@
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.3.2'
+ classpath 'com.android.tools.build:gradle:4.2.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
diff --git a/test/JNITestApp/gradle/wrapper/gradle-wrapper.properties b/test/JNITestApp/gradle/wrapper/gradle-wrapper.properties
index 0a6f916..2b52e7e 100644
--- a/test/JNITestApp/gradle/wrapper/gradle-wrapper.properties
+++ b/test/JNITestApp/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
diff --git a/test/common/test_utils.cpp b/test/common/test_utils.cpp
new file mode 100644
index 0000000..cc1f07e
--- /dev/null
+++ b/test/common/test_utils.cpp
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "test_utils.h"
+
+#include <regex>
+#include <sstream>
+#include <list>
+#include <mutex>
+#include <random>
+
+#include "gtest/gtest.h"
+
+namespace gamesdk_test {
+
+static std::list<std::unique_ptr<char[]>> allocated_bytes_list;
+static std::mutex allocated_mutex;
+
+// Wind forward over any arrays ... [...] ...
+template<typename Iterator>
+bool WindForwardOverArrays(Iterator& i, const Iterator& end,
+ char begin_ch = '[', char end_ch = ']',
+ std::string* inner = nullptr) {
+ std::stringstream result;
+ if (*i != begin_ch) return false;
+ ++i;
+ int level = 1;
+ for (; i != end; ++i) {
+ if (*i == begin_ch) ++level;
+ if (*i == end_ch) {
+ --level;
+ if (level == 0) {
+ ++i;
+ if (inner != nullptr) *inner = result.str();
+ return true;
+ }
+ if (level < 0) return false;
+ }
+ if (inner != nullptr) result << *i;
+ }
+ return false;
+}
+
+template<typename Iterator>
+bool WindForwardOverRegex(const std::string& regex_str, Iterator& i,
+ const Iterator& end, std::string* inner = nullptr) {
+ std::regex regex(regex_str);
+ std::match_results<Iterator> result;
+ // Match at the beginning of the string only.
+ auto match_flags = std::regex_constants::match_continuous;
+ if (std::regex_search(i, end, result, regex, match_flags)) {
+ if (inner != nullptr) {
+ *inner = result[0];
+ }
+ i += result[0].length();
+ return true;
+ }
+ return false;
+}
+
+bool CompareIgnoringWhitespace(std::string s0, std::string s1,
+ std::string* error_msg) {
+ // Ignore all whitespace, except when in strings.
+ // '[**]' is a wildcard for any array.
+ // '!REGEX(<regex>) is a regex.
+ bool in_string = false;
+ bool in_wildcard = false;
+ auto a = s0.begin();
+ auto b = s1.begin();
+ auto produceErrorMessage = [&]() {
+ if (error_msg != nullptr) {
+ std::stringstream str;
+ str << "Error at position " << (a - s0.begin()) << std::endl;
+ str << std::string(std::max(s0.begin(), a - 20), a);
+ str << "<HERE>";
+ str << std::string(a, std::min(s0.end(), a + 20));
+ str << std::endl;
+ str << std::string(std::max(s1.begin(), b - 20), b);
+ str << "<HERE>";
+ str << std::string(b, std::min(s1.end(), b + 20));
+ *error_msg = str.str();
+ }
+ };
+ while (true) {
+ if (!in_string) {
+ while (a != s0.end() && isspace(*a)) a++;
+ while (b != s1.end() && isspace(*b)) b++;
+ }
+ if (a == s0.end()) break;
+ if (b == s1.end()) {
+ produceErrorMessage();
+ return false;
+ }
+ // Array wildcard
+ if ((s1.end() - b) >= kArrayWildcard.length() &&
+ std::string(b, b + kArrayWildcard.length()) == kArrayWildcard) {
+ if (!WindForwardOverArrays(a, s0.end())) break;
+ b += kArrayWildcard.length();
+ continue;
+ }
+ // Regex
+ if ((s1.end() - b) >= kRegexPattern.length() &&
+ std::string(b, b + kRegexPattern.length()) == kRegexPattern) {
+ b += kRegexPattern.length();
+ std::string regex;
+ if (!WindForwardOverArrays(b, s1.end(), '(', ')', ®ex)) break;
+ if (!WindForwardOverRegex(regex, a, s0.end())) break;
+ continue;
+ }
+ if (*a != *b) {
+ produceErrorMessage();
+ return false;
+ }
+ if (*a == '"') in_string = !in_string;
+ ++a;
+ ++b;
+ }
+ if (b == s1.end()) {
+ return true;
+ } else {
+ produceErrorMessage();
+ return false;
+ }
+}
+
+bool CheckStrings(const std::string& name, const std::string& result,
+ const std::string& expected) {
+ std::string error_message;
+ bool comp = CompareIgnoringWhitespace(result, expected, &error_message);
+ EXPECT_TRUE(comp) << "\nResult:\n"
+ << result << "\n!=\nExpected:" << expected << "\n\n"
+ << error_message;
+ return comp;
+}
+
+
+} // namespace gamesdk_test
diff --git a/test/tuningfork/tf_test_utils.h b/test/common/test_utils.h
similarity index 74%
rename from test/tuningfork/tf_test_utils.h
rename to test/common/test_utils.h
index 6ec5a45..7259704 100644
--- a/test/tuningfork/tf_test_utils.h
+++ b/test/common/test_utils.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 The Android Open Source Project
+ * Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,10 +18,11 @@
#include <string>
-namespace tuningfork_test {
+namespace gamesdk_test {
const std::string kArrayWildcard = "[**]";
const std::string kRegexPattern = "!REGEX";
+const uint64_t kBytesInMegabyte = 1000000;
// Compare two strings, ignoring any whitespace. Also, the following patterns in
// s1 can be used:
@@ -30,4 +31,10 @@
bool CompareIgnoringWhitespace(std::string s0, std::string s1,
std::string* error_msg = nullptr);
-} // namespace tuningfork_test
+// Compare the strings ignoring whitespace and EXPECT_TRUE that they're the
+// same.
+bool CheckStrings(const std::string& name, const std::string& result,
+ const std::string& expected);
+
+} // namespace gamesdk_test
+
diff --git a/test/devicefarm/hello/build.gradle b/test/devicefarm/hello/build.gradle
index 82438ad..d215ed5 100644
--- a/test/devicefarm/hello/build.gradle
+++ b/test/devicefarm/hello/build.gradle
@@ -7,7 +7,7 @@
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.4.0'
+ classpath 'com.android.tools.build:gradle:4.2.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
diff --git a/test/devicefarm/hello/gradle/wrapper/gradle-wrapper.properties b/test/devicefarm/hello/gradle/wrapper/gradle-wrapper.properties
index e8c46a8..a454ee6 100644
--- a/test/devicefarm/hello/gradle/wrapper/gradle-wrapper.properties
+++ b/test/devicefarm/hello/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
diff --git a/test/grabber/app/src/main/cpp/CMakeLists.txt b/test/grabber/app/src/main/cpp/CMakeLists.txt
index bfe3a63..85693ba 100644
--- a/test/grabber/app/src/main/cpp/CMakeLists.txt
+++ b/test/grabber/app/src/main/cpp/CMakeLists.txt
@@ -4,39 +4,10 @@
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.4.1)
+project(grabber)
-# Creates and names a library, sets it as either STATIC
-# or SHARED, and provides the relative paths to its source code.
-# You can define multiple libraries, and CMake builds them for you.
-# Gradle automatically packages shared libraries with your APK.
+# Build the grabber native library.
+add_library(${PROJECT_NAME} SHARED
+ grabber.cpp)
-add_library( # Sets the name of the library.
- native-lib
-
- # Sets the library as a shared library.
- SHARED
-
- # Provides a relative path to your source file(s).
- native-lib.cpp)
-
-# Searches for a specified prebuilt library and stores the path as a
-# variable. Because CMake includes system libraries in the search path by
-# default, you only need to specify the name of the public NDK library
-# you want to add. CMake verifies that the library exists before
-# completing its build.
-
-find_library( # Sets the name of the path variable.
- log-lib
-
- # Specifies the name of the NDK library that
- # you want CMake to locate.
- log)
-
-# Specifies libraries CMake should link to your target library.
-
-target_link_libraries( # Specifies the target library.
- native-lib
-
- # Links the target library to the log library
- # included in the NDK.
- ${log-lib})
+target_link_libraries(${PROJECT_NAME} log)
diff --git a/test/grabber/app/src/main/cpp/native-lib.cpp b/test/grabber/app/src/main/cpp/grabber.cpp
similarity index 100%
rename from test/grabber/app/src/main/cpp/native-lib.cpp
rename to test/grabber/app/src/main/cpp/grabber.cpp
diff --git a/test/grabber/app/src/main/java/com/google/gamesdk/grabber/MainActivity.java b/test/grabber/app/src/main/java/com/google/gamesdk/grabber/MainActivity.java
index 2b29a81..203f439 100644
--- a/test/grabber/app/src/main/java/com/google/gamesdk/grabber/MainActivity.java
+++ b/test/grabber/app/src/main/java/com/google/gamesdk/grabber/MainActivity.java
@@ -8,6 +8,7 @@
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
+import android.util.Log;
import android.widget.Button;
import android.widget.EditText;
@@ -22,9 +23,16 @@
Context context = this;
button.setOnClickListener(
v -> {
+ Integer memSize; // memory size in mb.
+ try {
+ memSize = Integer.valueOf(editText.getText().toString());
+ } catch (NumberFormatException e) {
+ Log.w("Grabber", "invalid memory size: " + e);
+ return;
+ }
Intent intent = new Intent(context, MemoryPressureService.class);
intent.putExtra(ACTION_TYPE, ALLOCATE_MEMORY_ACTION);
- intent.putExtra(TOTAL_MEMORY_MB, Integer.valueOf(editText.getText().toString()));
+ intent.putExtra(TOTAL_MEMORY_MB, memSize);
context.startService(intent);
});
}
diff --git a/test/grabber/app/src/main/java/com/google/gamesdk/grabber/MemoryPressureService.java b/test/grabber/app/src/main/java/com/google/gamesdk/grabber/MemoryPressureService.java
index 4d7d51d..31b3a57 100644
--- a/test/grabber/app/src/main/java/com/google/gamesdk/grabber/MemoryPressureService.java
+++ b/test/grabber/app/src/main/java/com/google/gamesdk/grabber/MemoryPressureService.java
@@ -84,6 +84,6 @@
public native void freeAll();
static {
- System.loadLibrary("native-lib");
+ System.loadLibrary("grabber");
}
}
diff --git a/test/grabber/build.gradle b/test/grabber/build.gradle
index 22f2053..008f8ca 100644
--- a/test/grabber/build.gradle
+++ b/test/grabber/build.gradle
@@ -6,7 +6,7 @@
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.5.1'
+ classpath 'com.android.tools.build:gradle:4.2.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
diff --git a/test/grabber/gradle/wrapper/gradle-wrapper.properties b/test/grabber/gradle/wrapper/gradle-wrapper.properties
index e4cdc8f..e36367a 100644
--- a/test/grabber/gradle/wrapper/gradle-wrapper.properties
+++ b/test/grabber/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
diff --git a/test/istresser/build.gradle b/test/istresser/build.gradle
index d25dff3..59a592b 100644
--- a/test/istresser/build.gradle
+++ b/test/istresser/build.gradle
@@ -7,7 +7,7 @@
}
dependencies {
- classpath 'com.android.tools.build:gradle:4.2.0'
+ classpath 'com.android.tools.build:gradle:4.2.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
diff --git a/test/memory_advice/CMakeLists.txt b/test/memory_advice/CMakeLists.txt
new file mode 100644
index 0000000..e9d87eb
--- /dev/null
+++ b/test/memory_advice/CMakeLists.txt
@@ -0,0 +1,100 @@
+#
+# Copyright 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+cmake_minimum_required(VERSION 3.4.1)
+
+message( STATUS "A CMAKE_BUILD_TYPE = ${CMAKE_BUILD_TYPE}")
+
+set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Werror" )
+set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D _LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS -Os -fPIC" )
+set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti" )
+set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DGOOGLE_PROTOBUF_NO_RTTI -DHAVE_PTHREAD")
+
+set( TENSORFLOW_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../external/tensorflow")
+set( TFLITE_FLATBUFFERS_SCHEMA_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../external/flatbuffers/include")
+
+set( MEMORYADVICE_ASSETS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../memoryadvice/memoryadvice/src/main/assets/memoryadvice")
+set( MEMORYADVICE_RESOURCES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../memoryadvice/memoryadvice/src/main/resources")
+
+file(READ "${MEMORYADVICE_ASSETS_DIR}/default.json" PARAMS_FILE)
+set(PARAMS_STRING "namespace memory_advice_test {\nconst char* parameters_string = R\"PARAMS(\n")
+string(APPEND PARAMS_STRING "${PARAMS_FILE}")
+string(APPEND PARAMS_STRING "\n )PARAMS\";\n}\n")
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/advisor_parameters.cpp" "${PARAMS_STRING}")
+
+set(ANDROID_GTEST_DIR "../../../external/googletest")
+set(BUILD_GMOCK OFF)
+set(INSTALL_GTEST OFF)
+add_subdirectory("${ANDROID_GTEST_DIR}"
+ googletest-build
+)
+
+
+option(TUNINGFORK_TEST_OPTION "" ON)
+
+include("../../samples/gamesdk.cmake")
+
+#This builds the gamesdk in package/localtf and adds a gamesdk static library target
+add_gamesdk_target(
+ DO_LOCAL_BUILD
+ ROOT_DIR "../.."
+ PACKAGE_DIR "../../../package/local"
+ LIBRARIES "memory_advice"
+)
+
+include_directories(
+ "${ANDROID_GTEST_DIR}/googletest/include"
+ ../../src/memory_advice
+ ../../src/common
+ ../common
+ ../../include
+ ../../../external/nanopb-c
+ ../../third_party
+ .
+)
+
+set(TFLITE_INCLUDE_DIRS
+ "${TENSORFLOW_SOURCE_DIR}"
+ "${TFLITE_FLATBUFFERS_SCHEMA_DIR}"
+)
+include_directories(
+ BEFORE
+ ${TFLITE_INCLUDE_DIRS}
+)
+
+set(TEST_SRCS
+ endtoend/endtoend.cpp
+ endtoend/withallocation.cpp
+ endtoend/withmockmetrics.cpp
+ memory_utils.cpp
+ ../common/test_utils.cpp
+ ${CMAKE_CURRENT_BINARY_DIR}/advisor_parameters.cpp
+)
+
+add_library(memory_advice_test_lib
+ STATIC
+ shared_main.cpp
+ ${TEST_SRCS}
+)
+
+target_link_libraries(memory_advice_test_lib
+ android
+ gtest
+ memory_advice
+ log
+ GLESv2
+)
+
diff --git a/test/memory_advice/crowdtester/.gitignore b/test/memory_advice/crowdtester/.gitignore
new file mode 100644
index 0000000..73c9243
--- /dev/null
+++ b/test/memory_advice/crowdtester/.gitignore
@@ -0,0 +1,17 @@
+*.iml
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+.cxx
+local.properties
+*.tflite
+*.json
\ No newline at end of file
diff --git a/test/memory_advice/crowdtester/app/.gitignore b/test/memory_advice/crowdtester/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/test/memory_advice/crowdtester/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/test/memory_advice/crowdtester/app/build.gradle b/test/memory_advice/crowdtester/app/build.gradle
new file mode 100644
index 0000000..b5327b4
--- /dev/null
+++ b/test/memory_advice/crowdtester/app/build.gradle
@@ -0,0 +1,53 @@
+plugins {
+ id 'com.android.application'
+}
+
+android {
+ compileSdk 31
+
+ defaultConfig {
+ applicationId "com.memory_advice.crowdtester"
+ minSdk 21
+ targetSdk 31
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ externalNativeBuild {
+ cmake {
+ cppFlags ''
+ }
+ }
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ externalNativeBuild {
+ cmake {
+ path file('src/main/cpp/CMakeLists.txt')
+ version '3.10.2'
+ }
+ }
+ buildFeatures {
+ viewBinding true
+ }
+}
+
+dependencies {
+
+ implementation 'androidx.appcompat:appcompat:1.4.1'
+ implementation 'com.google.android.material:material:1.5.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
+ testImplementation 'junit:junit:4.+'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.3'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+ implementation 'com.google.android.games:memory-advice:0.24'
+}
\ No newline at end of file
diff --git a/GameActivity/proguard-rules.pro b/test/memory_advice/crowdtester/app/proguard-rules.pro
similarity index 100%
copy from GameActivity/proguard-rules.pro
copy to test/memory_advice/crowdtester/app/proguard-rules.pro
diff --git a/test/memory_advice/crowdtester/app/src/main/AndroidManifest.xml b/test/memory_advice/crowdtester/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..b74f685
--- /dev/null
+++ b/test/memory_advice/crowdtester/app/src/main/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.memory_advice.crowdtester">
+
+ <application
+ android:allowBackup="true"
+ android:icon="@mipmap/ic_launcher"
+ android:label="@string/app_name"
+ android:roundIcon="@mipmap/ic_launcher_round"
+ android:supportsRtl="true"
+ android:theme="@style/Theme.MemoryTester">
+ <activity
+ android:name=".MainActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
\ No newline at end of file
diff --git a/test/memory_advice/crowdtester/app/src/main/cpp/CMakeLists.txt b/test/memory_advice/crowdtester/app/src/main/cpp/CMakeLists.txt
new file mode 100644
index 0000000..3dc6d78
--- /dev/null
+++ b/test/memory_advice/crowdtester/app/src/main/cpp/CMakeLists.txt
@@ -0,0 +1,42 @@
+cmake_minimum_required(VERSION 3.4.1)
+
+project(crowdtester)
+
+set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Werror -Wthread-safety" )
+set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D _LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS -Os -fPIC" )
+set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti" )
+set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DGOOGLE_PROTOBUF_NO_RTTI -DHAVE_PTHREAD")
+
+set( GAMESDK_ROOT "../../../../../../..")
+set( MEMORYADVICE_RESOURCES_DIR "${GAMESDK_ROOT}/test/memoryadvice/memoryadvice/src/main/resources")
+
+include("${GAMESDK_ROOT}/samples/gamesdk.cmake")
+
+# This builds the memory_advice in package/local and adds a memory_advice shared library target
+add_gamesdk_target(
+ DO_LOCAL_BUILD
+ ROOT_DIR "${GAMESDK_ROOT}"
+ PACKAGE_DIR "${GAMESDK_ROOT}/../package/local"
+ LIBRARIES "memory_advice"
+)
+
+#get_target_property(DEP_LIB memory_advice IMPORTED_LOCATION)
+
+file(GLOB TF_MODEL_FILES "${MEMORYADVICE_RESOURCES_DIR}/*.*")
+file(COPY ${TF_MODEL_FILES} DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/../assets)
+
+include_directories("${GAMESDK_ROOT}/include")
+include_directories("${GAMESDK_ROOT}/samples/common/include") # Samples Includes
+
+
+add_library( ${PROJECT_NAME}
+ SHARED
+ crowdtester.cpp
+ )
+
+target_link_libraries( ${PROJECT_NAME}
+ memory_advice
+ android
+ GLESv2
+ log
+ )
diff --git a/test/memory_advice/crowdtester/app/src/main/cpp/crowdtester.cpp b/test/memory_advice/crowdtester/app/src/main/cpp/crowdtester.cpp
new file mode 100644
index 0000000..890c838
--- /dev/null
+++ b/test/memory_advice/crowdtester/app/src/main/cpp/crowdtester.cpp
@@ -0,0 +1,51 @@
+#include <jni.h>
+#include <memory_advice/memory_advice.h>
+#include <memory_advice/memory_advice_debug.h>
+
+#include <list>
+#include <mutex>
+#include <random>
+#include <sstream>
+#include <string>
+
+extern "C" JNIEXPORT jint JNICALL
+Java_com_memory_1advice_crowdtester_MainActivity_initMemoryAdvice(
+ JNIEnv* env, jobject activity) {
+ auto init_error_code = MemoryAdvice_init(env, activity);
+ return init_error_code;
+}
+extern "C" JNIEXPORT jstring JNICALL
+Java_com_memory_1advice_crowdtester_MainActivity_getMemoryAdvice(
+ JNIEnv* env, jobject activity) {
+ MemoryAdvice_JsonSerialization advice;
+ MemoryAdvice_getAdvice(&advice);
+ jstring ret = env->NewStringUTF(advice.json);
+ MemoryAdvice_JsonSerialization_free(&advice);
+ return ret;
+}
+static std::list<std::unique_ptr<char[]>> allocated_bytes_list;
+static std::mutex allocated_mutex;
+static void FillRandom(char* bytes, size_t size) {
+ std::minstd_rand rng;
+ int32_t* ptr = reinterpret_cast<int32_t*>(bytes);
+ std::generate(ptr, ptr + size / 4, [&]() -> int32_t { return rng(); });
+ // Don't worry about filling the last few bytes if size isn't a multiple
+ // of 4.
+}
+extern "C" JNIEXPORT bool JNICALL
+Java_com_memory_1advice_crowdtester_MainActivity_allocate(JNIEnv* env,
+ jobject activity,
+ jlong nbytes) {
+ std::lock_guard<std::mutex> guard(allocated_mutex);
+ try {
+ auto allocated_bytes = new char[nbytes];
+ // Note that without setting the memory, the available memory figure
+ // doesn't go down.
+ FillRandom(allocated_bytes, nbytes);
+ allocated_bytes_list.push_back(
+ std::unique_ptr<char[]>{allocated_bytes});
+ return true;
+ } catch (std::bad_alloc& ex) {
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/test/memory_advice/crowdtester/app/src/main/java/com/memory_advice/crowdtester/MainActivity.java b/test/memory_advice/crowdtester/app/src/main/java/com/memory_advice/crowdtester/MainActivity.java
new file mode 100644
index 0000000..33ace95
--- /dev/null
+++ b/test/memory_advice/crowdtester/app/src/main/java/com/memory_advice/crowdtester/MainActivity.java
@@ -0,0 +1,67 @@
+package com.memory_advice.crowdtester;
+import android.os.Bundle;
+import android.widget.TextView;
+import androidx.appcompat.app.AppCompatActivity;
+import com.google.android.apps.internal.games.memoryadvice.MemoryAdvisor;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+public class MainActivity extends AppCompatActivity {
+ // Used to load app's native libraries on application startup.
+ static {
+ System.loadLibrary("memory_advice");
+ System.loadLibrary("crowdtester");
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+ initMemoryAdvice();
+ MemoryAdvisor javaAdvisor = new MemoryAdvisor(this);
+
+ double javaPrediction = 0;
+ double nativePrediction = 0;
+
+ int count = 0;
+ double difference = 0;
+
+ while (javaPrediction < 0.5) {
+ try {
+ javaPrediction = new JSONObject(javaAdvisor.getAdvice())
+ .getJSONObject("metrics")
+ .getDouble("predictedUsage");
+ nativePrediction = new JSONObject(getMemoryAdvice())
+ .getJSONObject("metrics")
+ .getDouble("predictedUsage");
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ onAllocate();
+ count++;
+ difference += Math.abs(javaPrediction - nativePrediction);
+ }
+
+ int percentage = (int) (100 * difference / count);
+
+ TextView tv = findViewById(R.id.text1);
+ TextView tv2 = findViewById(R.id.text2);
+ if (percentage == 0) {
+ tv.setText("Test: SUCCESSFUL");
+ } else {
+ tv.setText("Test: FAILED");
+ }
+ tv2.setText("Percent Difference: " + percentage);
+ }
+ static final long BYTES_PER_MEGABYTE = 1000000;
+ static final long BYTES_TO_ADD = 100 * BYTES_PER_MEGABYTE;
+ long memory_allocated;
+ public void onAllocate() {
+ if (allocate(BYTES_TO_ADD)) {
+ memory_allocated += BYTES_TO_ADD;
+ }
+ }
+ public native int initMemoryAdvice();
+ public native String getMemoryAdvice();
+ public native boolean allocate(long bytes);
+}
\ No newline at end of file
diff --git a/test/memory_advice/crowdtester/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/test/memory_advice/crowdtester/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 0000000..966abaf
--- /dev/null
+++ b/test/memory_advice/crowdtester/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt"
+ android:width="108dp"
+ android:height="108dp"
+ android:viewportHeight="108"
+ android:viewportWidth="108">
+ <path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
+ <aapt:attr name="android:fillColor">
+ <gradient
+ android:endX="85.84757"
+ android:endY="92.4963"
+ android:startX="42.9492"
+ android:startY="49.59793"
+ android:type="linear">
+ <item
+ android:color="#44000000"
+ android:offset="0.0" />
+ <item
+ android:color="#00000000"
+ android:offset="1.0" />
+ </gradient>
+ </aapt:attr>
+ </path>
+ <path
+ android:fillColor="#FFFFFF"
+ android:fillType="nonZero"
+ android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
+ android:strokeColor="#00000000"
+ android:strokeWidth="1" />
+</vector>
\ No newline at end of file
diff --git a/test/memory_advice/crowdtester/app/src/main/res/drawable/ic_launcher_background.xml b/test/memory_advice/crowdtester/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..61bb79e
--- /dev/null
+++ b/test/memory_advice/crowdtester/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="108dp"
+ android:height="108dp"
+ android:viewportHeight="108"
+ android:viewportWidth="108">
+ <path
+ android:fillColor="#3DDC84"
+ android:pathData="M0,0h108v108h-108z" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M9,0L9,108"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,0L19,108"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M29,0L29,108"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M39,0L39,108"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M49,0L49,108"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M59,0L59,108"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M69,0L69,108"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M79,0L79,108"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M89,0L89,108"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M99,0L99,108"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,9L108,9"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,19L108,19"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,29L108,29"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,39L108,39"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,49L108,49"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,59L108,59"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,69L108,69"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,79L108,79"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,89L108,89"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,99L108,99"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,29L89,29"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,39L89,39"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,49L89,49"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,59L89,59"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,69L89,69"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,79L89,79"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M29,19L29,89"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M39,19L39,89"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M49,19L49,89"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M59,19L59,89"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M69,19L69,89"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M79,19L79,89"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+</vector>
diff --git a/test/memory_advice/crowdtester/app/src/main/res/layout/activity_main.xml b/test/memory_advice/crowdtester/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..826f459
--- /dev/null
+++ b/test/memory_advice/crowdtester/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:layout_gravity="center_vertical"
+ tools:context=".MainActivity">
+ <TextView
+ android:id="@+id/textView3"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_marginTop="24dp"
+ android:text="Memory Advice:"/>
+ <TextView
+ android:id="@+id/text1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="12dp"
+ android:layout_gravity="center"
+ android:text="text1" />
+ <TextView
+ android:id="@+id/text2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="12dp"
+ android:layout_gravity="center"
+ android:text="text2"/>
+</androidx.appcompat.widget.LinearLayoutCompat>
\ No newline at end of file
diff --git a/test/memory_advice/crowdtester/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/test/memory_advice/crowdtester/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..03eed25
--- /dev/null
+++ b/test/memory_advice/crowdtester/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@drawable/ic_launcher_background" />
+ <foreground android:drawable="@drawable/ic_launcher_foreground" />
+</adaptive-icon>
\ No newline at end of file
diff --git a/test/memory_advice/crowdtester/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/test/memory_advice/crowdtester/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..03eed25
--- /dev/null
+++ b/test/memory_advice/crowdtester/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@drawable/ic_launcher_background" />
+ <foreground android:drawable="@drawable/ic_launcher_foreground" />
+</adaptive-icon>
\ No newline at end of file
diff --git a/test/memory_advice/crowdtester/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/test/memory_advice/crowdtester/app/src/main/res/mipmap-hdpi/ic_launcher.webp
new file mode 100644
index 0000000..c209e78
--- /dev/null
+++ b/test/memory_advice/crowdtester/app/src/main/res/mipmap-hdpi/ic_launcher.webp
Binary files differ
diff --git a/test/memory_advice/crowdtester/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/test/memory_advice/crowdtester/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..b2dfe3d
--- /dev/null
+++ b/test/memory_advice/crowdtester/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
Binary files differ
diff --git a/test/memory_advice/crowdtester/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/test/memory_advice/crowdtester/app/src/main/res/mipmap-mdpi/ic_launcher.webp
new file mode 100644
index 0000000..4f0f1d6
--- /dev/null
+++ b/test/memory_advice/crowdtester/app/src/main/res/mipmap-mdpi/ic_launcher.webp
Binary files differ
diff --git a/test/memory_advice/crowdtester/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/test/memory_advice/crowdtester/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..62b611d
--- /dev/null
+++ b/test/memory_advice/crowdtester/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
Binary files differ
diff --git a/test/memory_advice/crowdtester/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/test/memory_advice/crowdtester/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
new file mode 100644
index 0000000..948a307
--- /dev/null
+++ b/test/memory_advice/crowdtester/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
Binary files differ
diff --git a/test/memory_advice/crowdtester/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/test/memory_advice/crowdtester/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..1b9a695
--- /dev/null
+++ b/test/memory_advice/crowdtester/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
Binary files differ
diff --git a/test/memory_advice/crowdtester/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/test/memory_advice/crowdtester/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..28d4b77
--- /dev/null
+++ b/test/memory_advice/crowdtester/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
Binary files differ
diff --git a/test/memory_advice/crowdtester/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/test/memory_advice/crowdtester/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..9287f50
--- /dev/null
+++ b/test/memory_advice/crowdtester/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
Binary files differ
diff --git a/test/memory_advice/crowdtester/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/test/memory_advice/crowdtester/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..aa7d642
--- /dev/null
+++ b/test/memory_advice/crowdtester/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
Binary files differ
diff --git a/test/memory_advice/crowdtester/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/test/memory_advice/crowdtester/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..9126ae3
--- /dev/null
+++ b/test/memory_advice/crowdtester/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
Binary files differ
diff --git a/test/memory_advice/crowdtester/app/src/main/res/values-night/themes.xml b/test/memory_advice/crowdtester/app/src/main/res/values-night/themes.xml
new file mode 100644
index 0000000..7f5001b
--- /dev/null
+++ b/test/memory_advice/crowdtester/app/src/main/res/values-night/themes.xml
@@ -0,0 +1,18 @@
+<resources xmlns:tools="http://schemas.android.com/tools">
+ <!-- Base application theme. -->
+ <style name="Theme.MemoryTester" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
+ <!-- Primary brand color. -->
+ <item name="colorPrimary">@color/purple_200</item>
+ <item name="colorPrimaryVariant">@color/purple_700</item>
+ <item name="colorOnPrimary">@color/black</item>
+ <!-- Secondary brand color. -->
+ <item name="colorSecondary">@color/teal_200</item>
+ <item name="colorSecondaryVariant">@color/teal_200</item>
+ <item name="colorOnSecondary">@color/black</item>
+ <!-- Status bar color. -->
+ <item name="android:statusBarColor" tools:targetApi="l">
+ ?attr/colorPrimaryVariant
+ </item>
+ <!-- Customize your theme here. -->
+ </style>
+</resources>
\ No newline at end of file
diff --git a/test/memory_advice/crowdtester/app/src/main/res/values/colors.xml b/test/memory_advice/crowdtester/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..09837df
--- /dev/null
+++ b/test/memory_advice/crowdtester/app/src/main/res/values/colors.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <color name="purple_200">#FFBB86FC</color>
+ <color name="purple_500">#FF6200EE</color>
+ <color name="purple_700">#FF3700B3</color>
+ <color name="teal_200">#FF03DAC5</color>
+ <color name="teal_700">#FF018786</color>
+ <color name="black">#FF000000</color>
+ <color name="white">#FFFFFFFF</color>
+</resources>
\ No newline at end of file
diff --git a/test/memory_advice/crowdtester/app/src/main/res/values/strings.xml b/test/memory_advice/crowdtester/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..70d517f
--- /dev/null
+++ b/test/memory_advice/crowdtester/app/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+<resources>
+ <string name="app_name">Memory Tester</string>
+</resources>
\ No newline at end of file
diff --git a/test/memory_advice/crowdtester/app/src/main/res/values/themes.xml b/test/memory_advice/crowdtester/app/src/main/res/values/themes.xml
new file mode 100644
index 0000000..97fb161
--- /dev/null
+++ b/test/memory_advice/crowdtester/app/src/main/res/values/themes.xml
@@ -0,0 +1,18 @@
+<resources xmlns:tools="http://schemas.android.com/tools">
+ <!-- Base application theme. -->
+ <style name="Theme.MemoryTester" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
+ <!-- Primary brand color. -->
+ <item name="colorPrimary">@color/purple_500</item>
+ <item name="colorPrimaryVariant">@color/purple_700</item>
+ <item name="colorOnPrimary">@color/white</item>
+ <!-- Secondary brand color. -->
+ <item name="colorSecondary">@color/teal_200</item>
+ <item name="colorSecondaryVariant">@color/teal_700</item>
+ <item name="colorOnSecondary">@color/black</item>
+ <!-- Status bar color. -->
+ <item name="android:statusBarColor" tools:targetApi="l">
+ ?attr/colorPrimaryVariant
+ </item>
+ <!-- Customize your theme here. -->
+ </style>
+</resources>
\ No newline at end of file
diff --git a/test/memory_advice/crowdtester/build.gradle b/test/memory_advice/crowdtester/build.gradle
new file mode 100644
index 0000000..026daed
--- /dev/null
+++ b/test/memory_advice/crowdtester/build.gradle
@@ -0,0 +1,17 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+buildscript {
+ repositories {
+ google()
+ mavenCentral()
+ }
+ dependencies {
+ classpath "com.android.tools.build:gradle:7.0.4"
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
\ No newline at end of file
diff --git a/test/memory_advice/crowdtester/gradle.properties b/test/memory_advice/crowdtester/gradle.properties
new file mode 100644
index 0000000..52f5917
--- /dev/null
+++ b/test/memory_advice/crowdtester/gradle.properties
@@ -0,0 +1,19 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app"s APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Automatically convert third-party libraries to use AndroidX
+android.enableJetifier=true
\ No newline at end of file
diff --git a/test/memory_advice/crowdtester/gradle/wrapper/gradle-wrapper.jar b/test/memory_advice/crowdtester/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..e708b1c
--- /dev/null
+++ b/test/memory_advice/crowdtester/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/src/tuningfork/tools/TuningForkMonitor/gradle/wrapper/gradle-wrapper.properties b/test/memory_advice/crowdtester/gradle/wrapper/gradle-wrapper.properties
similarity index 79%
copy from src/tuningfork/tools/TuningForkMonitor/gradle/wrapper/gradle-wrapper.properties
copy to test/memory_advice/crowdtester/gradle/wrapper/gradle-wrapper.properties
index b360a52..473e623 100644
--- a/src/tuningfork/tools/TuningForkMonitor/gradle/wrapper/gradle-wrapper.properties
+++ b/test/memory_advice/crowdtester/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Wed Nov 06 12:29:03 GMT 2019
-distributionBase=GRADLE_USER_HOME
-distributionPath=wrapper/dists
-zipStoreBase=GRADLE_USER_HOME
-zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
+#Wed Apr 13 12:33:21 BST 2022
+distributionBase=GRADLE_USER_HOME
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
+distributionPath=wrapper/dists
+zipStorePath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
diff --git a/test/memory_advice/crowdtester/gradlew b/test/memory_advice/crowdtester/gradlew
new file mode 100755
index 0000000..4f906e0
--- /dev/null
+++ b/test/memory_advice/crowdtester/gradlew
@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=`expr $i + 1`
+ done
+ case $i in
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/test/memory_advice/crowdtester/gradlew.bat b/test/memory_advice/crowdtester/gradlew.bat
new file mode 100644
index 0000000..ac1b06f
--- /dev/null
+++ b/test/memory_advice/crowdtester/gradlew.bat
@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/test/memory_advice/crowdtester/settings.gradle b/test/memory_advice/crowdtester/settings.gradle
new file mode 100644
index 0000000..74bd109
--- /dev/null
+++ b/test/memory_advice/crowdtester/settings.gradle
@@ -0,0 +1,10 @@
+dependencyResolutionManagement {
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+ repositories {
+ google()
+ mavenCentral()
+ jcenter() // Warning: this repository is going to shut down soon
+ }
+}
+rootProject.name = "Memory Tester"
+include ':app'
diff --git a/test/memory_advice/endtoend/endtoend.cpp b/test/memory_advice/endtoend/endtoend.cpp
new file mode 100644
index 0000000..2aacd76
--- /dev/null
+++ b/test/memory_advice/endtoend/endtoend.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <memory_advice/memory_advice.h>
+#include <memory_advice/memory_advice_debug.h>
+
+#include <core/memory_advice_impl.h>
+#include <core/memory_advice_internal.h>
+#include <core/state_watcher.h>
+
+#include <regex>
+#include <sstream>
+#include <string>
+
+
+#define LOG_TAG "MemoryAdvice"
+#include "Log.h"
+#include "json11/json11.hpp"
+
+#include "gtest/gtest.h"
+#include "../memory_utils.h"
+#include "test_utils.h"
+
+namespace memory_advice_test {
+
+extern const char* parameters_string;
+
+std::string TestEndToEnd() {
+ memory_advice::MemoryAdviceImpl* s_impl;
+ s_impl = new memory_advice::MemoryAdviceImpl(parameters_string, nullptr, nullptr, nullptr);
+
+ return json11::Json(s_impl->GetAdvice()).dump();
+}
+
+TEST(EndToEndTest, Base) {
+ auto result = TestEndToEnd();
+ std::string expected = GetAdviceString("!REGEX(\\d+)", "0.0!REGEX(\\d+)", "!REGEX(\\d{1,3})");
+ gamesdk_test::CheckStrings("Base", result, expected);
+}
+
+} // memory_advice_test
diff --git a/test/memory_advice/endtoend/withallocation.cpp b/test/memory_advice/endtoend/withallocation.cpp
new file mode 100644
index 0000000..4060716
--- /dev/null
+++ b/test/memory_advice/endtoend/withallocation.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <memory_advice/memory_advice.h>
+#include <memory_advice/memory_advice_debug.h>
+
+#include <core/memory_advice_impl.h>
+#include <core/memory_advice_internal.h>
+#include <core/state_watcher.h>
+
+#include <regex>
+#include <sstream>
+#include <string>
+
+
+#define LOG_TAG "MemoryAdvice"
+#include "Log.h"
+#include "json11/json11.hpp"
+
+#include "gtest/gtest.h"
+#include "../memory_utils.h"
+#include "test_utils.h"
+
+namespace memory_advice_test {
+
+extern const char* parameters_string;
+
+std::string TestEndToEndWithAllocation() {
+ memory_advice::MemoryAdviceImpl* s_impl;
+
+ s_impl = new memory_advice::MemoryAdviceImpl(parameters_string, nullptr, nullptr, nullptr);
+
+ while (s_impl->GetAdvice()
+ .at("metrics")
+ .object_items()
+ .at("predictedUsage")
+ .number_value() < 0.5) {
+ AllocateMemory(100 * kBytesInMegabyte);
+ }
+ std::string result = json11::Json(s_impl->GetAdvice()).dump();
+ DeallocateAllMemory();
+
+ return result;
+}
+
+TEST(EndToEndTest, WithAllocation) {
+ auto result = TestEndToEndWithAllocation();
+ std::string expected = GetAdviceString("!REGEX(\\d+)", "0.5!REGEX(\\d+)", "!REGEX(\\d{1,3})");
+ gamesdk_test::CheckStrings("Base", result, expected);
+}
+
+} // memory_advice_test
diff --git a/test/memory_advice/endtoend/withmockmetrics.cpp b/test/memory_advice/endtoend/withmockmetrics.cpp
new file mode 100644
index 0000000..c8d9718
--- /dev/null
+++ b/test/memory_advice/endtoend/withmockmetrics.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <memory_advice/memory_advice.h>
+#include <memory_advice/memory_advice_debug.h>
+
+#include <core/memory_advice_impl.h>
+#include <core/metrics_provider.h>
+#include <core/memory_advice_internal.h>
+#include <core/state_watcher.h>
+
+#include <regex>
+#include <sstream>
+#include <string>
+
+
+#define LOG_TAG "MemoryAdvice"
+#include "Log.h"
+#include "json11/json11.hpp"
+
+#include "gtest/gtest.h"
+#include "../memory_utils.h"
+#include "test_utils.h"
+#include "../providers/test_metrics_provider.h"
+
+namespace memory_advice_test {
+
+extern const char* parameters_string;
+
+std::string TestEndToEndWithMockMetrics() {
+ memory_advice::MemoryAdviceImpl* s_impl;
+
+ TestMetricsProvider metrics_provider;
+
+ metrics_provider.setOomScore(500);
+ metrics_provider.setAvailMem(12341234);
+ metrics_provider.setTotalMem(1234123412);
+ metrics_provider.setSwapTotal(112233);
+
+ s_impl = new memory_advice::MemoryAdviceImpl(parameters_string, &metrics_provider, nullptr,
+ nullptr);
+
+ return json11::Json(s_impl->GetAdvice()).dump();
+}
+
+TEST(EndToEndTest, WithMockMetrics) {
+ auto result = TestEndToEndWithMockMetrics();
+ std::string expected = GetAdviceString("12341234", "0.97!REGEX(\\d+)", "500", true);
+ gamesdk_test::CheckStrings("Base", result, expected);
+}
+
+} // memory_advice_test
diff --git a/test/memory_advice/main.cpp b/test/memory_advice/main.cpp
new file mode 100644
index 0000000..3c3ef78
--- /dev/null
+++ b/test/memory_advice/main.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "gtest/gtest.h"
+
+// No jni tests when running on the command-line
+extern "C" bool init_jni_for_tests() { return false; }
+
+extern "C" void clear_jni_for_tests() {
+ // Do nothing
+}
+
+int main(int argc, char* argv[]) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/test/memory_advice/memory_advice_test.h b/test/memory_advice/memory_advice_test.h
new file mode 100644
index 0000000..7beea33
--- /dev/null
+++ b/test/memory_advice/memory_advice_test.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+// This function is exported by the test library and should be called to run the
+// tests if you have a Java env. messages is filled with a summary of the tests
+// run, including failure messages.
+extern "C" int shared_main(int argc, char* argv[], JNIEnv* env, jobject context,
+ std::string& messages);
diff --git a/test/memory_advice/memory_utils.cpp b/test/memory_advice/memory_utils.cpp
new file mode 100644
index 0000000..4fa2580
--- /dev/null
+++ b/test/memory_advice/memory_utils.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "test_utils.h"
+
+#include <regex>
+#include <sstream>
+#include <list>
+#include <mutex>
+#include <random>
+
+#include "gtest/gtest.h"
+
+namespace memory_advice_test {
+
+static std::list<std::unique_ptr<char[]>> allocated_bytes_list;
+static std::mutex allocated_mutex;
+
+std::string GetAdviceString(const std::string& avail_mem,
+ const std::string& predicted_usage,
+ const std::string& oom_score, bool with_warnings) {
+ if (with_warnings) {
+ return R"MA(
+ {
+ "metrics":{
+ "MemoryInfo":{
+ "_meta":["duration", !REGEX(\d)],
+ "availMem":)MA" +
+ avail_mem + R"MA(
+ },
+ "meta": {
+ "time": !REGEX(\d+)
+ },
+ "predictedUsage": )MA" +
+ predicted_usage + R"MA(,
+ "proc": {
+ "_meta":["duration", !REGEX(\d)],
+ "oom_score": )MA" +
+ oom_score + R"MA(
+ }
+ },
+ "warnings": [
+ {
+ "formula": "predictedUsage>0.75",
+ "level": "red"
+ },
+ {
+ "formula": "predictedUsage>0.65",
+ "level": "yellow"
+ }
+ ]
+ }
+ )MA";
+ } else {
+ return R"MA(
+ {
+ "metrics":{
+ "MemoryInfo":{
+ "_meta":["duration", !REGEX(\d)],
+ "availMem":)MA" +
+ avail_mem + R"MA(
+ },
+ "meta": {
+ "time": !REGEX(\d+)
+ },
+ "predictedUsage": )MA" +
+ predicted_usage + R"MA(,
+ "proc": {
+ "_meta":["duration", !REGEX(\d)],
+ "oom_score": )MA" +
+ oom_score + R"MA(
+ }
+ }
+ }
+ )MA";
+ }
+}
+
+void FillRandom(char* bytes, size_t size) {
+ std::minstd_rand rng;
+ int32_t* ptr = reinterpret_cast<int32_t*>(bytes);
+ std::generate(ptr, ptr + size/4, [&]() -> int32_t { return rng(); });
+ // Don't worry about filling the last few bytes if size isn't a multiple of 4.
+}
+
+void AllocateMemory(uint64_t nbytes) {
+ std::lock_guard<std::mutex> guard(allocated_mutex);
+ try {
+ auto allocated_bytes = new char[nbytes];
+ // Note that without setting the memory, the available memory figure doesn't go down.
+ FillRandom(allocated_bytes, nbytes);
+ allocated_bytes_list.push_back(std::unique_ptr<char[]>{allocated_bytes});
+ } catch(std::bad_alloc& ex) {
+ }
+}
+
+void DeallocateAllMemory() {
+ std::lock_guard<std::mutex> guard(allocated_mutex);
+ while (allocated_bytes_list.size() > 0) {
+ allocated_bytes_list.pop_back();
+ }
+}
+
+} // namespace memory_advice_test
diff --git a/test/memory_advice/memory_utils.h b/test/memory_advice/memory_utils.h
new file mode 100644
index 0000000..44d72cf
--- /dev/null
+++ b/test/memory_advice/memory_utils.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+#include <list>
+#include <mutex>
+
+namespace memory_advice_test {
+
+const std::string kArrayWildcard = "[**]";
+const std::string kRegexPattern = "!REGEX";
+const uint64_t kBytesInMegabyte = 1000000;
+
+// Compare two strings, ignoring any whitespace. Also, the following patterns in
+// s1 can be used:
+// '[**]' is an array wildcard - it matches nested arrays.
+// '!REGEX(.*) will match the regex in brackets.
+bool CompareIgnoringWhitespace(std::string s0, std::string s1,
+ std::string* error_msg = nullptr);
+
+// Compare the strings ignoring whitespace and EXPECT_TRUE that they're the
+// same.
+bool CheckStrings(const std::string& name, const std::string& result,
+ const std::string& expected);
+
+// Generates an advice string that satisfies the given parameters
+std::string GetAdviceString(const std::string& avail_mem,
+ const std::string& predicted_usage,
+ const std::string& oom_score,
+ bool with_warnings = false);
+
+// Allocates a given number of bytes
+void AllocateMemory(uint64_t nbytes);
+
+// Deallocates all memory that has currently been allocated
+void DeallocateAllMemory();
+} // namespace memory_advice_test
+
diff --git a/test/memory_advice/providers/test_metrics_provider.h b/test/memory_advice/providers/test_metrics_provider.h
new file mode 100644
index 0000000..d7a2134
--- /dev/null
+++ b/test/memory_advice/providers/test_metrics_provider.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <core/metrics_provider.h>
+
+using namespace json11;
+
+namespace memory_advice_test {
+
+class TestMetricsProvider : public memory_advice::IMetricsProvider {
+ double oom_score_ = 0;
+ double avail_mem_ = 0;
+ double swap_total_ = 0;
+ double total_mem_ = 0;
+ public:
+
+ void setOomScore(double oom_score) {
+ oom_score_ = oom_score;
+ }
+ void setSwapTotal(double swap_total) {
+ swap_total_ = swap_total;
+ }
+ void setAvailMem(double avail_mem) {
+ avail_mem_ = avail_mem;
+ }
+ void setTotalMem(double total_mem) {
+ total_mem_ = total_mem;
+ }
+
+ Json::object GetMeminfoValues() override {
+ Json::object metrics_map;
+ metrics_map["SwapTotal"] = swap_total_;
+ return metrics_map;
+ }
+
+ Json::object GetStatusValues() override {
+ Json::object metrics_map;
+ return metrics_map;
+ }
+
+ Json::object GetProcValues() override {
+ Json::object metrics_map;
+ metrics_map["oom_score"] = oom_score_;
+ return metrics_map;
+ }
+
+ Json::object GetActivityManagerValues() override {
+ Json::object metrics_map;
+ return metrics_map;
+ }
+
+ Json::object GetActivityManagerMemoryInfo() override {
+ Json::object metrics_map;
+ metrics_map["availMem"] = avail_mem_;
+ metrics_map["totalMem"] = total_mem_;
+ return metrics_map;
+ }
+
+ Json::object GetDebugValues() override {
+ Json::object metrics_map;
+ return metrics_map;
+ }
+};
+
+} // namespace memory_advice_test
diff --git a/test/memory_advice/shared_main.cpp b/test/memory_advice/shared_main.cpp
new file mode 100644
index 0000000..8bf9297
--- /dev/null
+++ b/test/memory_advice/shared_main.cpp
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <algorithm>
+#include <set>
+#include <sstream>
+#include <string>
+
+#include "gtest/gtest.h"
+#include "jni/jni_helper.h"
+
+using ::testing::EmptyTestEventListener;
+using ::testing::InitGoogleTest;
+using ::testing::Test;
+using ::testing::TestCase;
+using ::testing::TestEventListeners;
+using ::testing::TestInfo;
+using ::testing::TestPartResult;
+using ::testing::UnitTest;
+
+namespace {
+
+static JNIEnv* s_env = 0;
+static jobject s_context = 0;
+
+// Record the output of googletest tests
+class GTestRecorder : public EmptyTestEventListener {
+ std::vector<std::string> tests_started;
+ std::set<std::string> tests_completed;
+ std::vector<std::string> success_invocations; // Only from SUCCESS macros
+ std::vector<std::string>
+ failed_invocations; // From any failed EXPECT or ASSERT
+ bool overall_success;
+
+ private:
+ // Called before any test activity starts.
+ void OnTestProgramStart(const UnitTest& /* unit_test */) override {
+ overall_success = false;
+ }
+
+ // Called after all test activities have ended.
+ void OnTestProgramEnd(const UnitTest& unit_test) override {
+ overall_success = unit_test.Passed();
+ }
+
+ // Called before a test starts.
+ void OnTestStart(const TestInfo& test_info) override {
+ tests_started.push_back(std::string(test_info.test_case_name()) + "." +
+ test_info.name());
+ }
+
+ // Called after a failed assertion or a SUCCEED() invocation.
+ void OnTestPartResult(const TestPartResult& test_part_result) override {
+ std::stringstream record;
+ // This assumes we are not running tests in parallel, so the last one
+ // added to tests_started is the test for which we are getting the
+ // partial result.
+ record << tests_started.back() << '\n';
+ record << test_part_result.file_name() << ":"
+ << test_part_result.line_number() << '\n'
+ << test_part_result.summary() << '\n';
+ if (test_part_result.failed()) {
+ failed_invocations.push_back(record.str());
+ } else {
+ success_invocations.push_back(record.str());
+ }
+ }
+
+ // Called after a test ends.
+ void OnTestEnd(const TestInfo& test_info) override {
+ tests_completed.insert(std::string(test_info.test_case_name()) + "." +
+ test_info.name());
+ }
+
+ public:
+ std::string GetResult() const {
+ std::stringstream result;
+ result << "TESTS " << (overall_success ? "SUCCEEDED" : "FAILED")
+ << '\n';
+ result << "\nTests that ran to completion:\n";
+ for (auto s : tests_completed) {
+ result << s << '\n';
+ }
+ std::set<std::string> not_completed;
+ std::set<std::string> tests_started_set(tests_started.begin(),
+ tests_started.end());
+ std::set_difference(tests_started_set.begin(), tests_started_set.end(),
+ tests_completed.begin(), tests_completed.end(),
+ std::inserter(not_completed, not_completed.end()));
+ if (not_completed.size() > 0) {
+ result << "\nTests that started but failed to complete:\n";
+ for (auto s : not_completed) {
+ result << s << '\n';
+ }
+ }
+ if (!failed_invocations.empty()) {
+ result << "\nFailures:\n";
+ for (auto s : failed_invocations) result << s << '\n';
+ }
+ if (!success_invocations.empty()) {
+ result << "\nExplicitly recorded successes:\n";
+ for (auto s : success_invocations) result << s << '\n';
+ }
+ return result.str();
+ }
+}; // class GTestRecorder
+
+} // namespace
+
+extern "C" bool init_jni_for_tests() {
+ gamesdk::jni::Init(s_env, s_context);
+ return true;
+}
+extern "C" void clear_jni_for_tests() { gamesdk::jni::Destroy(); }
+
+extern "C" int shared_main(int argc, char* argv[], JNIEnv* env, jobject context,
+ std::string& messages) {
+ ::testing::InitGoogleTest(&argc, argv);
+
+ // Set up Java env
+ s_env = env;
+ s_context = context;
+ init_jni_for_tests();
+
+ // Set up result recorder
+ UnitTest& unit_test = *UnitTest::GetInstance();
+ TestEventListeners& listeners = unit_test.listeners();
+ delete listeners.Release(listeners.default_result_printer());
+ auto recorder = std::make_shared<GTestRecorder>();
+ listeners.Append(recorder.get());
+
+ // Run tests
+ int result = RUN_ALL_TESTS();
+ messages = recorder->GetResult();
+ return result;
+}
diff --git a/test/memory_advice/testapp/.gitignore b/test/memory_advice/testapp/.gitignore
new file mode 100644
index 0000000..73c9243
--- /dev/null
+++ b/test/memory_advice/testapp/.gitignore
@@ -0,0 +1,17 @@
+*.iml
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+.cxx
+local.properties
+*.tflite
+*.json
\ No newline at end of file
diff --git a/test/memory_advice/testapp/app/.gitignore b/test/memory_advice/testapp/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/test/memory_advice/testapp/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/test/memory_advice/testapp/app/build.gradle b/test/memory_advice/testapp/app/build.gradle
new file mode 100644
index 0000000..8a250c9
--- /dev/null
+++ b/test/memory_advice/testapp/app/build.gradle
@@ -0,0 +1,44 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 29
+ defaultConfig {
+ applicationId "com.memory_advice.testapp"
+ minSdkVersion 15
+ targetSdkVersion 29
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ externalNativeBuild {
+ cmake {
+ cppFlags ""
+ }
+ }
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ debug {
+ debuggable true
+ }
+ }
+ externalNativeBuild {
+ cmake {
+ path "src/main/cpp/CMakeLists.txt"
+ }
+ }
+}
+
+dependencies {
+
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ implementation 'androidx.appcompat:appcompat:1.1.0'
+ implementation 'com.google.android.material:material:1.1.0'
+ implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
+ testImplementation 'junit:junit:4.12'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.1'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
+}
diff --git a/GameActivity/proguard-rules.pro b/test/memory_advice/testapp/app/proguard-rules.pro
similarity index 100%
copy from GameActivity/proguard-rules.pro
copy to test/memory_advice/testapp/app/proguard-rules.pro
diff --git a/samples/memory_advice/hogger/app/src/androidTest/java/com/memory_advice/hogger/ExampleInstrumentedTest.java b/test/memory_advice/testapp/app/src/androidTest/java/com/memory_advice/testapp/ExampleInstrumentedTest.java
similarity index 60%
rename from samples/memory_advice/hogger/app/src/androidTest/java/com/memory_advice/hogger/ExampleInstrumentedTest.java
rename to test/memory_advice/testapp/app/src/androidTest/java/com/memory_advice/testapp/ExampleInstrumentedTest.java
index 36c5aaa..8a0bcc1 100644
--- a/samples/memory_advice/hogger/app/src/androidTest/java/com/memory_advice/hogger/ExampleInstrumentedTest.java
+++ b/test/memory_advice/testapp/app/src/androidTest/java/com/memory_advice/testapp/ExampleInstrumentedTest.java
@@ -1,7 +1,6 @@
-package com.memory_advice.hogger;
+package com.memory_advice.testapp;
import android.content.Context;
-
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -17,10 +16,11 @@
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
- @Test
- public void useAppContext() {
- // Context of the app under test.
- Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
- assertEquals("com.memory_advice.hogger", appContext.getPackageName());
- }
+
+ @Test
+ public void useAppContext() {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ assertEquals("com.memory_advice.testapp", appContext.getPackageName());
+ }
}
\ No newline at end of file
diff --git a/test/memory_advice/testapp/app/src/main/AndroidManifest.xml b/test/memory_advice/testapp/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..ff7fcbc
--- /dev/null
+++ b/test/memory_advice/testapp/app/src/main/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.memory_advice.testapp">
+
+ <application
+ android:allowBackup="true"
+ android:icon="@mipmap/ic_launcher"
+ android:label="@string/app_name"
+ android:roundIcon="@mipmap/ic_launcher_round"
+ android:supportsRtl="true"
+ android:theme="@style/Theme.Testapp">
+ <activity
+ android:name=".MainActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
\ No newline at end of file
diff --git a/test/memory_advice/testapp/app/src/main/cpp/CMakeLists.txt b/test/memory_advice/testapp/app/src/main/cpp/CMakeLists.txt
new file mode 100644
index 0000000..c835c93
--- /dev/null
+++ b/test/memory_advice/testapp/app/src/main/cpp/CMakeLists.txt
@@ -0,0 +1,42 @@
+#
+# Copyright 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+cmake_minimum_required(VERSION 3.4.1)
+project(testapp C CXX)
+set(CMAKE_CXX_STANDARD 14)
+
+set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Werror -Wthread-safety" )
+set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D _LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS -Os -fPIC" )
+set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti" )
+set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DGOOGLE_PROTOBUF_NO_RTTI -DHAVE_PTHREAD")
+
+set( MEMORYADVICE_RESOURCES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../memoryadvice/memoryadvice/src/main/resources")
+
+file(GLOB TF_MODEL_FILES "${MEMORYADVICE_RESOURCES_DIR}/*.*")
+file(COPY ${TF_MODEL_FILES} DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/../assets)
+
+set (TEST_DIR "${CMAKE_CURRENT_LIST_DIR}/../../../../..")
+add_subdirectory( "${TEST_DIR}" test_dir)
+
+include_directories("${TEST_DIR}"
+ "${TEST_DIR}/../../samples/common/include"
+ )
+
+add_library( ${PROJECT_NAME} SHARED testapp.cpp )
+
+target_link_libraries( ${PROJECT_NAME}
+ memory_advice_test_lib
+ log )
diff --git a/test/memory_advice/testapp/app/src/main/cpp/testapp.cpp b/test/memory_advice/testapp/app/src/main/cpp/testapp.cpp
new file mode 100644
index 0000000..4770edc
--- /dev/null
+++ b/test/memory_advice/testapp/app/src/main/cpp/testapp.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <jni.h>
+
+#include <sstream>
+#include <string>
+
+#include "memory_advice_test.h"
+
+extern "C" JNIEXPORT jstring JNICALL
+Java_com_memory_1advice_testapp_MainActivity_runTests(JNIEnv* env, jobject ctx) {
+ int argc = 1;
+ char appName[] = "testapp";
+ char* argv[] = {appName};
+ std::string full_record;
+ int ret_code = shared_main(argc, argv, env, ctx, full_record);
+ if (ret_code == 0) {
+ } else {
+ std::stringstream ss(full_record);
+ for (std::string record_line; std::getline(ss, record_line);) {
+ }
+ }
+ return env->NewStringUTF((full_record).c_str());
+}
diff --git a/test/memory_advice/testapp/app/src/main/java/com/memory_advice/testapp/MainActivity.java b/test/memory_advice/testapp/app/src/main/java/com/memory_advice/testapp/MainActivity.java
new file mode 100644
index 0000000..0af77a4
--- /dev/null
+++ b/test/memory_advice/testapp/app/src/main/java/com/memory_advice/testapp/MainActivity.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.memory_advice.testapp;
+
+import android.text.method.ScrollingMovementMethod;
+import androidx.appcompat.app.AppCompatActivity;
+import android.os.Bundle;
+import android.widget.TextView;
+
+public class MainActivity extends AppCompatActivity {
+
+ // Used to load the testapp's native library on application startup.
+ static {
+ System.loadLibrary("testapp");
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ TextView tv = findViewById(R.id.sample_text);
+ tv.setMovementMethod(new ScrollingMovementMethod());
+ tv.setText(runTests());
+ }
+
+ public native String runTests();
+}
diff --git a/test/memory_advice/testapp/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/test/memory_advice/testapp/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 0000000..966abaf
--- /dev/null
+++ b/test/memory_advice/testapp/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt"
+ android:width="108dp"
+ android:height="108dp"
+ android:viewportHeight="108"
+ android:viewportWidth="108">
+ <path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
+ <aapt:attr name="android:fillColor">
+ <gradient
+ android:endX="85.84757"
+ android:endY="92.4963"
+ android:startX="42.9492"
+ android:startY="49.59793"
+ android:type="linear">
+ <item
+ android:color="#44000000"
+ android:offset="0.0" />
+ <item
+ android:color="#00000000"
+ android:offset="1.0" />
+ </gradient>
+ </aapt:attr>
+ </path>
+ <path
+ android:fillColor="#FFFFFF"
+ android:fillType="nonZero"
+ android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
+ android:strokeColor="#00000000"
+ android:strokeWidth="1" />
+</vector>
\ No newline at end of file
diff --git a/test/memory_advice/testapp/app/src/main/res/drawable/ic_launcher_background.xml b/test/memory_advice/testapp/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..61bb79e
--- /dev/null
+++ b/test/memory_advice/testapp/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="108dp"
+ android:height="108dp"
+ android:viewportHeight="108"
+ android:viewportWidth="108">
+ <path
+ android:fillColor="#3DDC84"
+ android:pathData="M0,0h108v108h-108z" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M9,0L9,108"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,0L19,108"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M29,0L29,108"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M39,0L39,108"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M49,0L49,108"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M59,0L59,108"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M69,0L69,108"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M79,0L79,108"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M89,0L89,108"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M99,0L99,108"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,9L108,9"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,19L108,19"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,29L108,29"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,39L108,39"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,49L108,49"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,59L108,59"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,69L108,69"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,79L108,79"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,89L108,89"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,99L108,99"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,29L89,29"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,39L89,39"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,49L89,49"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,59L89,59"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,69L89,69"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,79L89,79"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M29,19L29,89"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M39,19L39,89"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M49,19L49,89"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M59,19L59,89"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M69,19L69,89"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M79,19L79,89"
+ android:strokeColor="#33FFFFFF"
+ android:strokeWidth="0.8" />
+</vector>
diff --git a/test/memory_advice/testapp/app/src/main/res/layout/activity_main.xml b/test/memory_advice/testapp/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..740559a
--- /dev/null
+++ b/test/memory_advice/testapp/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context=".MainActivity">
+ <TextView
+ android:id="@+id/sample_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Hello World!"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintLeft_toLeftOf="parent"
+ app:layout_constraintRight_toRightOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/test/memory_advice/testapp/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/test/memory_advice/testapp/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..03eed25
--- /dev/null
+++ b/test/memory_advice/testapp/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@drawable/ic_launcher_background" />
+ <foreground android:drawable="@drawable/ic_launcher_foreground" />
+</adaptive-icon>
\ No newline at end of file
diff --git a/test/memory_advice/testapp/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/test/memory_advice/testapp/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..03eed25
--- /dev/null
+++ b/test/memory_advice/testapp/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@drawable/ic_launcher_background" />
+ <foreground android:drawable="@drawable/ic_launcher_foreground" />
+</adaptive-icon>
\ No newline at end of file
diff --git a/test/memory_advice/testapp/app/src/main/res/mipmap-hdpi/ic_launcher.png b/test/memory_advice/testapp/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..e10003f
--- /dev/null
+++ b/test/memory_advice/testapp/app/src/main/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/test/memory_advice/testapp/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/test/memory_advice/testapp/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000..0867fb3
--- /dev/null
+++ b/test/memory_advice/testapp/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
Binary files differ
diff --git a/test/memory_advice/testapp/app/src/main/res/mipmap-mdpi/ic_launcher.png b/test/memory_advice/testapp/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..24a4d6f
--- /dev/null
+++ b/test/memory_advice/testapp/app/src/main/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/test/memory_advice/testapp/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/test/memory_advice/testapp/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 0000000..ba79fbf
--- /dev/null
+++ b/test/memory_advice/testapp/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
Binary files differ
diff --git a/test/memory_advice/testapp/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/test/memory_advice/testapp/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..a0ea661
--- /dev/null
+++ b/test/memory_advice/testapp/app/src/main/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/test/memory_advice/testapp/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/test/memory_advice/testapp/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..d2acc72
--- /dev/null
+++ b/test/memory_advice/testapp/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
Binary files differ
diff --git a/test/memory_advice/testapp/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/test/memory_advice/testapp/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..e102642
--- /dev/null
+++ b/test/memory_advice/testapp/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/test/memory_advice/testapp/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/test/memory_advice/testapp/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..a6f87c1
--- /dev/null
+++ b/test/memory_advice/testapp/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
Binary files differ
diff --git a/test/memory_advice/testapp/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/test/memory_advice/testapp/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..46d576a
--- /dev/null
+++ b/test/memory_advice/testapp/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/test/memory_advice/testapp/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/test/memory_advice/testapp/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..6601041
--- /dev/null
+++ b/test/memory_advice/testapp/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
Binary files differ
diff --git a/test/memory_advice/testapp/app/src/main/res/values-night/themes.xml b/test/memory_advice/testapp/app/src/main/res/values-night/themes.xml
new file mode 100644
index 0000000..2a18ca4
--- /dev/null
+++ b/test/memory_advice/testapp/app/src/main/res/values-night/themes.xml
@@ -0,0 +1,18 @@
+<resources xmlns:tools="http://schemas.android.com/tools">
+ <!-- Base application theme. -->
+ <style name="Theme.Testapp" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
+ <!-- Primary brand color. -->
+ <item name="colorPrimary">@color/purple_200</item>
+ <item name="colorPrimaryVariant">@color/purple_700</item>
+ <item name="colorOnPrimary">@color/black</item>
+ <!-- Secondary brand color. -->
+ <item name="colorSecondary">@color/teal_200</item>
+ <item name="colorSecondaryVariant">@color/teal_200</item>
+ <item name="colorOnSecondary">@color/black</item>
+ <!-- Status bar color. -->
+ <item name="android:statusBarColor" tools:targetApi="l">
+ ?attr/colorPrimaryVariant
+ </item>
+ <!-- Customize your theme here. -->
+ </style>
+</resources>
\ No newline at end of file
diff --git a/test/memory_advice/testapp/app/src/main/res/values/colors.xml b/test/memory_advice/testapp/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..09837df
--- /dev/null
+++ b/test/memory_advice/testapp/app/src/main/res/values/colors.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <color name="purple_200">#FFBB86FC</color>
+ <color name="purple_500">#FF6200EE</color>
+ <color name="purple_700">#FF3700B3</color>
+ <color name="teal_200">#FF03DAC5</color>
+ <color name="teal_700">#FF018786</color>
+ <color name="black">#FF000000</color>
+ <color name="white">#FFFFFFFF</color>
+</resources>
\ No newline at end of file
diff --git a/test/memory_advice/testapp/app/src/main/res/values/strings.xml b/test/memory_advice/testapp/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..7fdd871
--- /dev/null
+++ b/test/memory_advice/testapp/app/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+<resources>
+ <string name="app_name">testapp</string>
+</resources>
\ No newline at end of file
diff --git a/test/memory_advice/testapp/app/src/main/res/values/styles.xml b/test/memory_advice/testapp/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..0ee4f3f
--- /dev/null
+++ b/test/memory_advice/testapp/app/src/main/res/values/styles.xml
@@ -0,0 +1,18 @@
+<resources xmlns:tools="http://schemas.android.com/tools">
+ <!-- Base application theme. -->
+ <style name="Theme.Testapp" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
+ <!-- Primary brand color. -->
+ <item name="colorPrimary">@color/purple_500</item>
+ <item name="colorPrimaryVariant">@color/purple_700</item>
+ <item name="colorOnPrimary">@color/white</item>
+ <!-- Secondary brand color. -->
+ <item name="colorSecondary">@color/teal_200</item>
+ <item name="colorSecondaryVariant">@color/teal_700</item>
+ <item name="colorOnSecondary">@color/black</item>
+ <!-- Status bar color. -->
+ <item name="android:statusBarColor" tools:targetApi="l">
+ ?attr/colorPrimaryVariant
+ </item>
+ <!-- Customize your theme here. -->
+ </style>
+</resources>
\ No newline at end of file
diff --git a/samples/memory_advice/hogger/app/src/test/java/com/memory_advice/hogger/ExampleUnitTest.java b/test/memory_advice/testapp/app/src/test/java/com/memory_advice/testapp/ExampleUnitTest.java
similarity index 68%
rename from samples/memory_advice/hogger/app/src/test/java/com/memory_advice/hogger/ExampleUnitTest.java
rename to test/memory_advice/testapp/app/src/test/java/com/memory_advice/testapp/ExampleUnitTest.java
index 65f5adc..008476e 100644
--- a/samples/memory_advice/hogger/app/src/test/java/com/memory_advice/hogger/ExampleUnitTest.java
+++ b/test/memory_advice/testapp/app/src/test/java/com/memory_advice/testapp/ExampleUnitTest.java
@@ -1,4 +1,4 @@
-package com.memory_advice.hogger;
+package com.memory_advice.testapp;
import org.junit.Test;
@@ -10,8 +10,9 @@
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
- @Test
- public void addition_isCorrect() {
- assertEquals(4, 2 + 2);
- }
+
+ @Test
+ public void addition_isCorrect() {
+ assertEquals(4, 2 + 2);
+ }
}
\ No newline at end of file
diff --git a/test/memory_advice/testapp/build.gradle b/test/memory_advice/testapp/build.gradle
new file mode 100644
index 0000000..35abed4
--- /dev/null
+++ b/test/memory_advice/testapp/build.gradle
@@ -0,0 +1,27 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ google()
+ jcenter()
+
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:4.2.2'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/test/memory_advice/testapp/gradle b/test/memory_advice/testapp/gradle
new file mode 120000
index 0000000..c071f53
--- /dev/null
+++ b/test/memory_advice/testapp/gradle
@@ -0,0 +1 @@
+../../../gradle/
\ No newline at end of file
diff --git a/test/memory_advice/testapp/gradle.properties b/test/memory_advice/testapp/gradle.properties
new file mode 100644
index 0000000..52f5917
--- /dev/null
+++ b/test/memory_advice/testapp/gradle.properties
@@ -0,0 +1,19 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app"s APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Automatically convert third-party libraries to use AndroidX
+android.enableJetifier=true
\ No newline at end of file
diff --git a/test/memory_advice/testapp/gradlew b/test/memory_advice/testapp/gradlew
new file mode 100755
index 0000000..4f906e0
--- /dev/null
+++ b/test/memory_advice/testapp/gradlew
@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=`expr $i + 1`
+ done
+ case $i in
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/test/memory_advice/testapp/gradlew.bat b/test/memory_advice/testapp/gradlew.bat
new file mode 100644
index 0000000..ac1b06f
--- /dev/null
+++ b/test/memory_advice/testapp/gradlew.bat
@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/test/memory_advice/testapp/settings.gradle b/test/memory_advice/testapp/settings.gradle
new file mode 100644
index 0000000..666145e
--- /dev/null
+++ b/test/memory_advice/testapp/settings.gradle
@@ -0,0 +1,2 @@
+include ':app'
+rootProject.name = "testapp"
\ No newline at end of file
diff --git a/test/memoryadvice/build.gradle b/test/memoryadvice/build.gradle
index 9f70be3..cdac029 100644
--- a/test/memoryadvice/build.gradle
+++ b/test/memoryadvice/build.gradle
@@ -8,8 +8,7 @@
}
dependencies {
- classpath 'com.android.tools.build:gradle:4.2.0'
-
+ classpath 'com.android.tools.build:gradle:4.2.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
diff --git a/test/memoryadvice/gradle/wrapper/gradle-wrapper.properties b/test/memoryadvice/gradle/wrapper/gradle-wrapper.properties
index ef4fe0b..dd70d80 100644
--- a/test/memoryadvice/gradle/wrapper/gradle-wrapper.properties
+++ b/test/memoryadvice/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.9.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
diff --git a/test/memoryadvice/memorytest/src/main/cpp/CMakeLists.txt b/test/memoryadvice/memorytest/src/main/cpp/CMakeLists.txt
index 72368b9..701354d 100644
--- a/test/memoryadvice/memorytest/src/main/cpp/CMakeLists.txt
+++ b/test/memoryadvice/memorytest/src/main/cpp/CMakeLists.txt
@@ -4,19 +4,14 @@
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.4.1)
+project(memory-test)
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
-add_library( # Sets the name of the library.
- native-lib
-
- # Sets the library as a shared library.
- SHARED
-
- # Provides a relative path to your source file(s).
+add_library( ${PROJECT_NAME} SHARED
allocator.cc
consumer.cc
gl-memory.cc
@@ -29,32 +24,15 @@
vk-memory.cc
vulkan_wrapper/vulkan_wrapper.cpp
- native-lib.cc)
-
-# Searches for a specified prebuilt library and stores the path as a
-# variable. Because CMake includes system libraries in the search path by
-# default, you only need to specify the name of the public NDK library
-# you want to add. CMake verifies that the library exists before
-# completing its build.
-
-find_library( # Sets the name of the path variable.
- log-lib
-
- # Specifies the name of the NDK library that
- # you want CMake to locate.
- log)
+ memory-test.cc)
# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.
-target_link_libraries( # Specifies the target library.
- native-lib
+target_link_libraries( ${PROJECT_NAME}
android
GLESv3
-
- # Links the target library to the log library
- # included in the NDK.
- ${log-lib})
+ log)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DVK_USE_PLATFORM_ANDROID_KHR")
diff --git a/test/memoryadvice/memorytest/src/main/cpp/native-lib.cc b/test/memoryadvice/memorytest/src/main/cpp/memory-test.cc
similarity index 100%
rename from test/memoryadvice/memorytest/src/main/cpp/native-lib.cc
rename to test/memoryadvice/memorytest/src/main/cpp/memory-test.cc
diff --git a/test/memoryadvice/memorytest/src/main/java/com/google/android/apps/internal/games/memorytest/MemoryTest.java b/test/memoryadvice/memorytest/src/main/java/com/google/android/apps/internal/games/memorytest/MemoryTest.java
index e7988b7..b2ef14e 100644
--- a/test/memoryadvice/memorytest/src/main/java/com/google/android/apps/internal/games/memorytest/MemoryTest.java
+++ b/test/memoryadvice/memorytest/src/main/java/com/google/android/apps/internal/games/memorytest/MemoryTest.java
@@ -22,7 +22,7 @@
private static final String TAG = MemoryTest.class.getSimpleName();
static {
- System.loadLibrary("native-lib");
+ System.loadLibrary("memory-test");
}
private final boolean yellowLightTesting;
diff --git a/test/memoryadvice_exampleclient/build.gradle b/test/memoryadvice_exampleclient/build.gradle
index 910f7a7..ffc5b93 100644
--- a/test/memoryadvice_exampleclient/build.gradle
+++ b/test/memoryadvice_exampleclient/build.gradle
@@ -5,7 +5,7 @@
mavenCentral()
}
dependencies {
- classpath 'com.android.tools.build:gradle:4.2.0'
+ classpath 'com.android.tools.build:gradle:4.2.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
diff --git a/test/swappy/CMakeLists.txt b/test/swappy/CMakeLists.txt
index 35b96da..218d7e8 100644
--- a/test/swappy/CMakeLists.txt
+++ b/test/swappy/CMakeLists.txt
@@ -16,6 +16,10 @@
cmake_minimum_required(VERSION 3.4.1)
+set(CMAKE_CXX_STANDARD 17)
+
+find_package(games-frame-pacing REQUIRED CONFIG)
+
message( STATUS "A CMAKE_BUILD_TYPE = ${CMAKE_BUILD_TYPE}")
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -Werror" )
@@ -32,12 +36,12 @@
include_directories(
"${ANDROID_GTEST_DIR}/googletest/include"
- ../../src
+ ../../games-frame-pacing
../../src/common
../../include
)
-set ( SOURCE_LOCATION_COMMON "../../src/swappy/common" )
+set ( SOURCE_LOCATION_COMMON "../../games-frame-pacing/common" )
set(TEST_SRCS
${SOURCE_LOCATION_COMMON}/SwappyCommon.cpp
diff --git a/test/swappy/swappycommon_test.cpp b/test/swappy/swappycommon_test.cpp
index e22159e..f5a1aec 100644
--- a/test/swappy/swappycommon_test.cpp
+++ b/test/swappy/swappycommon_test.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "swappy/common/SwappyCommon.h"
+#include "common/SwappyCommon.h"
#include <functional>
#include <optional>
diff --git a/test/swappy/testapp/app/build.gradle b/test/swappy/testapp/app/build.gradle
index 616d29b..ae5a538 100644
--- a/test/swappy/testapp/app/build.gradle
+++ b/test/swappy/testapp/app/build.gradle
@@ -1,12 +1,11 @@
apply plugin: 'com.android.application'
android {
- compileSdkVersion 29
- buildToolsVersion "29.0.2"
+ compileSdkVersion 31
defaultConfig {
applicationId "com.swappy.testapp"
- minSdkVersion 15
- targetSdkVersion 29
+ minSdkVersion 19
+ targetSdkVersion 31
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@@ -21,42 +20,36 @@
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
- debug {
- debuggable true
- externalNativeBuild {
- cmake {
- cppFlags "-DDEBUG=1"
- }
- }
- }
+ debug {
+ debuggable true
+ externalNativeBuild {
+ cmake {
+ cppFlags "-DDEBUG=1"
+ }
+ }
+ }
}
externalNativeBuild {
cmake {
path "src/main/cpp/CMakeLists.txt"
+ version "3.18.1+"
}
}
compileOptions {
sourceCompatibility = 1.8
targetCompatibility = 1.8
}
+ buildFeatures {
+ prefab true
+ }
}
dependencies {
- implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
- testImplementation 'junit:junit:4.12'
- androidTestImplementation 'androidx.test.ext:junit:1.1.1'
- androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
+ // Uncomment this line and comment out "implementation project(":games-frame-pacing")"
+ // to use a locally built .aar
+ //implementation fileTree(dir: '../../../', include: ['games-frame-pacing-release.aar'])
+
+ implementation project(":games-frame-pacing")
}
-// Necessary to avoid failed resolution of Jetpack dependencies
-configurations.all {
- resolutionStrategy.eachDependency { DependencyResolveDetails details ->
- def requested = details.requested
- if (requested.group == "androidx") {
- if (!requested.name.startsWith("multidex")) {
- details.useVersion "${targetSdk}.+"
- }
- }
- }
-}
\ No newline at end of file
diff --git a/test/swappy/testapp/app/src/main/AndroidManifest.xml b/test/swappy/testapp/app/src/main/AndroidManifest.xml
index f3e9d33..cce39d5 100644
--- a/test/swappy/testapp/app/src/main/AndroidManifest.xml
+++ b/test/swappy/testapp/app/src/main/AndroidManifest.xml
@@ -9,7 +9,8 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
- <activity android:name=".MainActivity">
+ <activity android:name=".MainActivity"
+ android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
diff --git a/test/swappy/testapp/app/src/main/cpp/CMakeLists.txt b/test/swappy/testapp/app/src/main/cpp/CMakeLists.txt
index a6d4281..7405501 100644
--- a/test/swappy/testapp/app/src/main/cpp/CMakeLists.txt
+++ b/test/swappy/testapp/app/src/main/cpp/CMakeLists.txt
@@ -15,6 +15,7 @@
#
cmake_minimum_required(VERSION 3.4.1)
+project(swappy-test)
set (TEST_DIR "${CMAKE_CURRENT_LIST_DIR}/../../../../..")
add_subdirectory( "${TEST_DIR}" test_dir)
@@ -23,20 +24,10 @@
"${TEST_DIR}/../../samples/common/include"
)
-add_library( native-lib
+add_library( ${PROJECT_NAME} SHARED
+ swappy-test.cpp )
- SHARED
- native-lib.cpp )
-
-find_library( # Sets the name of the path variable.
- log-lib
-
- # Specifies the name of the NDK library that
- # you want CMake to locate.
- log )
-
-target_link_libraries( # Specifies the target library.
- native-lib
+target_link_libraries( ${PROJECT_NAME}
swappy_test_lib
- ${log-lib} )
+ log )
diff --git a/test/swappy/testapp/app/src/main/cpp/native-lib.cpp b/test/swappy/testapp/app/src/main/cpp/swappy-test.cpp
similarity index 100%
rename from test/swappy/testapp/app/src/main/cpp/native-lib.cpp
rename to test/swappy/testapp/app/src/main/cpp/swappy-test.cpp
diff --git a/test/swappy/testapp/app/src/main/java/com/swappy/testapp/MainActivity.java b/test/swappy/testapp/app/src/main/java/com/swappy/testapp/MainActivity.java
index ee9dcfb..f0e4881 100644
--- a/test/swappy/testapp/app/src/main/java/com/swappy/testapp/MainActivity.java
+++ b/test/swappy/testapp/app/src/main/java/com/swappy/testapp/MainActivity.java
@@ -31,9 +31,9 @@
Boolean mDone;
String mResult;
- // Used to load the 'native-lib' library on application startup.
+ // Used to load the test app's native library on application startup.
static {
- System.loadLibrary("native-lib");
+ System.loadLibrary("swappy-test");
}
@Override
@@ -89,8 +89,8 @@
/**
- * A native method that is implemented by the 'native-lib' native library, which is packaged with
- * this application.
+ * Native methods implemented in app's native library,
+ * which is packaged with this application.
*/
public native String runTests();
public native String testSummarySoFar();
diff --git a/test/swappy/testapp/build.gradle b/test/swappy/testapp/build.gradle
index 8d1998f..d6751df 100644
--- a/test/swappy/testapp/build.gradle
+++ b/test/swappy/testapp/build.gradle
@@ -3,30 +3,19 @@
buildscript {
repositories {
google()
- jcenter()
+ mavenCentral()
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.5.1'
+ classpath 'com.android.tools.build:gradle:7.2.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
- // Necessary to avoid failed resolution of Jetpack dependencies
- configurations.all {
- resolutionStrategy.eachDependency { DependencyResolveDetails details ->
- def requested = details.requested
- if (requested.group == "androidx") {
- if (!requested.name.startsWith("multidex")) {
- details.useVersion "${targetSdk}.+"
- }
- }
- }
- }
}
allprojects {
repositories {
google()
- jcenter()
+ mavenCentral()
}
}
diff --git a/test/swappy/testapp/gradle/wrapper/gradle-wrapper.properties b/test/swappy/testapp/gradle/wrapper/gradle-wrapper.properties
index 45e1f0e..b5382bb 100644
--- a/test/swappy/testapp/gradle/wrapper/gradle-wrapper.properties
+++ b/test/swappy/testapp/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip
\ No newline at end of file
diff --git a/test/swappy/testapp/settings.gradle b/test/swappy/testapp/settings.gradle
index 4b9b18d..99f136e 100644
--- a/test/swappy/testapp/settings.gradle
+++ b/test/swappy/testapp/settings.gradle
@@ -1,2 +1,6 @@
include ':app'
rootProject.name='SwappyTesting'
+include ':games-frame-pacing:extras'
+include ':games-frame-pacing'
+project(':games-frame-pacing').projectDir = file('../../../games-frame-pacing')
+project(':games-frame-pacing:extras').projectDir = file('../../../games-frame-pacing/extras')
diff --git a/test/tuningfork/CMakeLists.txt b/test/tuningfork/CMakeLists.txt
index 5a1ba6d..aab0114 100644
--- a/test/tuningfork/CMakeLists.txt
+++ b/test/tuningfork/CMakeLists.txt
@@ -16,6 +16,8 @@
cmake_minimum_required(VERSION 3.4.1)
+find_package(games-performance-tuner REQUIRED CONFIG)
+
message( STATUS "A CMAKE_BUILD_TYPE = ${CMAKE_BUILD_TYPE}")
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Werror" )
@@ -30,7 +32,7 @@
googletest-build
)
-include("../../src/protobuf/protobuf.cmake")
+include("../../games-performance-tuner/protobuf/protobuf.cmake")
# Save the generation dir as it gets overwritten when we add_subdirectory tuningfork
set(PGENS_DIR "${PROTO_GENS_DIR}")
@@ -44,15 +46,13 @@
)
option(TUNINGFORK_TEST_OPTION "" ON)
-add_subdirectory("../../src/tuningfork"
- tuningfork-build
-)
include_directories(
"${ANDROID_GTEST_DIR}/googletest/include"
- ../../src/tuningfork
+ ../../games-performance-tuner
../../src/common
../../include
+ ../common
../../../external/nanopb-c
../../third_party
${PGENS_DIR}
@@ -73,13 +73,14 @@
endtoend/loading.cpp
endtoend/loading_groups.cpp
endtoend/memory.cpp
+ endtoend/trace.cpp
endtoend/time_based.cpp
file_cache_test.cpp
histogram_test.cpp
jni_test.cpp
serialization_test.cpp
settings_test.cpp
- tf_test_utils.cpp
+ ../common/test_utils.cpp
${PGENS_DIR}/nano/dev_tuningfork.pb.c
${PGENS_DIR}/full/dev_tuningfork.pb.cc
${PGENS_DIR}/full/tuningfork.pb.cc
@@ -106,7 +107,7 @@
target_link_libraries(tuningfork_test
android
gtest
- tuningfork_static
+ games-performance-tuner::tuningfork_static
protobuf-static
log
GLESv2
@@ -115,7 +116,7 @@
target_link_libraries(tuningfork_test_lib
android
gtest
- tuningfork_static
+ games-performance-tuner::tuningfork_static
protobuf-static
log
GLESv2
diff --git a/test/tuningfork/endtoend/abandoned_loading.cpp b/test/tuningfork/endtoend/abandoned_loading.cpp
index 03b1d5f..9568c66 100644
--- a/test/tuningfork/endtoend/abandoned_loading.cpp
+++ b/test/tuningfork/endtoend/abandoned_loading.cpp
@@ -15,8 +15,11 @@
*/
#include "common.h"
+#include "test_utils.h"
#include "tuningfork_test.h"
+using namespace gamesdk_test;
+
namespace tuningfork_test {
static std::string AbandonedLoadingEvent(int type, const std::string& duration,
@@ -34,12 +37,14 @@
"device": "",
"fingerprint": "",
"gles_version": {"major": 0, "minor": 0},
+ "height_pixels": 0,
"model": "",
"product": "",
"soc_manufacturer": "",
"soc_model": "",
"swap_total_bytes": 123,
- "total_memory_bytes": 0
+ "total_memory_bytes": 0,
+ "width_pixels": 0
},
"game_sdk_info": {"session_id": "", "version": "1.0.0"},
"time_period": {"end_time": "1970-01-01T00:00:00.000000Z",
diff --git a/test/tuningfork/endtoend/annotation.cpp b/test/tuningfork/endtoend/annotation.cpp
index 0b2c2fe..1c1e466 100644
--- a/test/tuningfork/endtoend/annotation.cpp
+++ b/test/tuningfork/endtoend/annotation.cpp
@@ -15,8 +15,11 @@
*/
#include "common.h"
+#include "test_utils.h"
#include "tuningfork_test.h"
+using namespace gamesdk_test;
+
namespace tuningfork_test {
TuningForkLogEvent TestEndToEndWithAnnotation() {
diff --git a/test/tuningfork/endtoend/battery.cpp b/test/tuningfork/endtoend/battery.cpp
index ff93208..a69a7b1 100644
--- a/test/tuningfork/endtoend/battery.cpp
+++ b/test/tuningfork/endtoend/battery.cpp
@@ -15,8 +15,11 @@
*/
#include "common.h"
+#include "test_utils.h"
#include "tuningfork_test.h"
+using namespace gamesdk_test;
+
namespace tuningfork_test {
TuningForkLogEvent TestEndToEndWithBattery() {
@@ -59,12 +62,14 @@
"major": 0,
"minor": 0
},
+ "height_pixels": 0,
"model": "",
"product": "",
"soc_manufacturer": "",
"soc_model": "",
"swap_total_bytes": 123,
- "total_memory_bytes": 0
+ "total_memory_bytes": 0,
+ "width_pixels": 0
},
"game_sdk_info": {
"session_id": "",
diff --git a/test/tuningfork/endtoend/common.cpp b/test/tuningfork/endtoend/common.cpp
index be56b3f..d9925a7 100644
--- a/test/tuningfork/endtoend/common.cpp
+++ b/test/tuningfork/endtoend/common.cpp
@@ -32,12 +32,14 @@
"major": 0,
"minor": 0
},
+ "height_pixels": 0,
"model": "",
"product": "",
"soc_manufacturer": "",
"soc_model": "",
"swap_total_bytes": 123,
- "total_memory_bytes": 0
+ "total_memory_bytes": 0,
+ "width_pixels": 0
},
"game_sdk_info": {
"session_id": "",
@@ -63,12 +65,14 @@
"major": 0,
"minor": 0
},
+ "height_pixels": 0,
"model": "",
"product": "",
"soc_manufacturer": "",
"soc_model": "",
"swap_total_bytes": 123,
- "total_memory_bytes": 0
+ "total_memory_bytes": 0,
+ "width_pixels": 0
},
"game_sdk_info": {
"session_id": "",
@@ -113,16 +117,6 @@
return s;
}
-bool CheckStrings(const std::string& name, const std::string& result,
- const std::string& expected) {
- std::string error_message;
- bool comp = CompareIgnoringWhitespace(result, expected, &error_message);
- EXPECT_TRUE(comp) << "\nResult:\n"
- << result << "\n!=\nExpected:" << expected << "\n\n"
- << error_message;
- return comp;
-}
-
std::string ReplaceReturns(std::string in) {
std::replace(in.begin(), in.end(), '\n', ' ');
return in;
diff --git a/test/tuningfork/endtoend/common.h b/test/tuningfork/endtoend/common.h
index ed9bb7f..8526b2d 100644
--- a/test/tuningfork/endtoend/common.h
+++ b/test/tuningfork/endtoend/common.h
@@ -20,7 +20,6 @@
#include <utility>
#include <vector>
-#include "../tf_test_utils.h"
#include "core/memory_telemetry.h"
#include "core/tuningfork_internal.h"
#include "full/dev_tuningfork.pb.h"
@@ -56,11 +55,6 @@
const std::vector<tf::Settings::Histogram>& hists = {},
int num_frame_time_histograms = 0, int num_loading_time_histograms = 0);
-// Compare the strings ignoring whitespace and EXPECT_TRUE that they're the
-// same.
-bool CheckStrings(const std::string& name, const std::string& result,
- const std::string& expected);
-
// Return a string with all returns replaced with single spaces.
std::string ReplaceReturns(std::string in);
diff --git a/test/tuningfork/endtoend/endtoend.cpp b/test/tuningfork/endtoend/endtoend.cpp
index 8402c30..659cd27 100644
--- a/test/tuningfork/endtoend/endtoend.cpp
+++ b/test/tuningfork/endtoend/endtoend.cpp
@@ -15,8 +15,11 @@
*/
#include "common.h"
+#include "test_utils.h"
#include "tuningfork_test.h"
+using namespace gamesdk_test;
+
namespace tuningfork_test {
TuningForkLogEvent TestEndToEnd() {
diff --git a/test/tuningfork/endtoend/fidelityparam_download.cpp b/test/tuningfork/endtoend/fidelityparam_download.cpp
index be94598..a854d8a 100644
--- a/test/tuningfork/endtoend/fidelityparam_download.cpp
+++ b/test/tuningfork/endtoend/fidelityparam_download.cpp
@@ -15,8 +15,11 @@
*/
#include "common.h"
+#include "test_utils.h"
#include "tuningfork_test.h"
+using namespace gamesdk_test;
+
namespace tuningfork_test {
static std::condition_variable fp_cv;
@@ -125,12 +128,14 @@
"major": 0,
"minor": 0
},
+ "height_pixels": 0,
"model": "",
"product": "",
"soc_manufacturer": "",
"soc_model": "",
"swap_total_bytes": 123,
- "total_memory_bytes": 0
+ "total_memory_bytes": 0,
+ "width_pixels": 0
},
"name": "applications//apks/0"
})";
diff --git a/test/tuningfork/endtoend/limits.cpp b/test/tuningfork/endtoend/limits.cpp
index d3fadc5..75db9cb 100644
--- a/test/tuningfork/endtoend/limits.cpp
+++ b/test/tuningfork/endtoend/limits.cpp
@@ -15,8 +15,11 @@
*/
#include "common.h"
+#include "test_utils.h"
#include "tuningfork_test.h"
+using namespace gamesdk_test;
+
namespace tuningfork_test {
TuningForkLogEvent TestEndToEndWithLimits() {
diff --git a/test/tuningfork/endtoend/loading.cpp b/test/tuningfork/endtoend/loading.cpp
index 13b8a31..5c80378 100644
--- a/test/tuningfork/endtoend/loading.cpp
+++ b/test/tuningfork/endtoend/loading.cpp
@@ -15,8 +15,11 @@
*/
#include "common.h"
+#include "test_utils.h"
#include "tuningfork_test.h"
+using namespace gamesdk_test;
+
namespace tuningfork_test {
TuningForkLogEvent TestEndToEndWithLoadingTimes() {
diff --git a/test/tuningfork/endtoend/loading_groups.cpp b/test/tuningfork/endtoend/loading_groups.cpp
index baae33a..d696435 100644
--- a/test/tuningfork/endtoend/loading_groups.cpp
+++ b/test/tuningfork/endtoend/loading_groups.cpp
@@ -15,8 +15,11 @@
*/
#include "common.h"
+#include "test_utils.h"
#include "tuningfork_test.h"
+using namespace gamesdk_test;
+
namespace tuningfork_test {
TuningForkLogEvent TestEndToEndWithLoadingGroups(
diff --git a/test/tuningfork/endtoend/memory.cpp b/test/tuningfork/endtoend/memory.cpp
index dc856bb..5648f23 100644
--- a/test/tuningfork/endtoend/memory.cpp
+++ b/test/tuningfork/endtoend/memory.cpp
@@ -15,8 +15,11 @@
*/
#include "common.h"
+#include "test_utils.h"
#include "tuningfork_test.h"
+using namespace gamesdk_test;
+
namespace tuningfork_test {
TuningForkLogEvent TestEndToEndWithMemory() {
@@ -63,12 +66,14 @@
"major": 0,
"minor": 0
},
+ "height_pixels": 0,
"model": "",
"product": "",
"soc_manufacturer": "",
"soc_model": "",
"swap_total_bytes": 123,
- "total_memory_bytes": 0
+ "total_memory_bytes": 0,
+ "width_pixels": 0
},
"game_sdk_info": {
"session_id": "",
diff --git a/test/tuningfork/endtoend/time_based.cpp b/test/tuningfork/endtoend/time_based.cpp
index 7aebc47..f64e70f 100644
--- a/test/tuningfork/endtoend/time_based.cpp
+++ b/test/tuningfork/endtoend/time_based.cpp
@@ -15,8 +15,11 @@
*/
#include "common.h"
+#include "test_utils.h"
#include "tuningfork_test.h"
+using namespace gamesdk_test;
+
namespace tuningfork_test {
TuningForkLogEvent TestEndToEndTimeBased() {
@@ -70,4 +73,289 @@
CheckStrings("TimeBased", result, expected);
}
+TuningForkLogEvent TestEndToEndTimeBasedWithOnePause() {
+ const int NTICKS =
+ 101; // note the first tick doesn't add anything to the histogram
+ auto settings =
+ TestSettings(tf::Settings::AggregationStrategy::Submission::TIME_BASED,
+ 10100, 1, {}, {{TFTICK_RAW_FRAME_TIME, 50, 150, 10}});
+ TuningForkTest test(settings, milliseconds(100));
+ std::unique_lock<std::mutex> lock(*test.rmutex_);
+ for (int i = 0; i < NTICKS; ++i) {
+ if (i > 75) {
+ tf::ResumeFrameTimeLogging();
+ } else if (i > 50) {
+ tf::PauseFrameTimeLogging();
+ }
+
+ test.IncrementTime();
+ tf::FrameTick(TFTICK_RAW_FRAME_TIME);
+ }
+ // Wait for the upload thread to complete writing the string
+ EXPECT_TRUE(test.cv_->wait_for(lock, s_test_wait_time) ==
+ std::cv_status::no_timeout)
+ << "Timeout";
+
+ return test.Result();
+}
+
+TEST(EndToEndTest, TimeBasedWithOnePause) {
+ auto result = TestEndToEndTimeBasedWithOnePause();
+ TuningForkLogEvent expected = R"TF(
+{
+ "name": "applications//apks/0",
+ "session_context":)TF" + session_context +
+ R"TF(,
+ "telemetry": [{
+ "context": {
+ "annotations": "",
+ "duration": "7.5s",
+ "tuning_parameters": {
+ "experiment_id": "",
+ "serialized_fidelity_parameters": ""
+ }
+ },
+ "report": {
+ "rendering": {
+ "render_time_histogram": [{
+ "counts": [
+ 0, 0, 0, 0, 0, 0, 75, 0, 0, 0, 0, 0],
+ "instrument_id": 64000
+ }]
+ }
+ }
+ }]
+}
+)TF";
+ CheckStrings("TimeBased", result, expected);
+}
+
+TuningForkLogEvent TestEndToEndTimeBasedWithTwoPauses() {
+ const int NTICKS =
+ 101; // note the first tick doesn't add anything to the histogram
+ auto settings =
+ TestSettings(tf::Settings::AggregationStrategy::Submission::TIME_BASED,
+ 10100, 1, {}, {{TFTICK_RAW_FRAME_TIME, 50, 150, 10}});
+ TuningForkTest test(settings, milliseconds(90));
+ std::unique_lock<std::mutex> lock(*test.rmutex_);
+ EXPECT_FALSE(tf::IsFrameTimeLoggingPaused());
+
+ for (int i = 0; i < NTICKS; ++i) {
+ if (i > 40) {
+ tf::ResumeFrameTimeLogging();
+ EXPECT_FALSE(tf::IsFrameTimeLoggingPaused());
+ } else if (i > 30) {
+ tf::PauseFrameTimeLogging();
+ EXPECT_TRUE(tf::IsFrameTimeLoggingPaused());
+ test.time_provider_.tick_size = milliseconds(100);
+ } else if (i > 20) {
+ tf::ResumeFrameTimeLogging();
+ } else if (i > 10) {
+ tf::PauseFrameTimeLogging();
+ test.time_provider_.tick_size = milliseconds(110);
+ }
+
+ test.IncrementTime();
+ tf::FrameTick(TFTICK_RAW_FRAME_TIME);
+ }
+ // Wait for the upload thread to complete writing the string
+ EXPECT_TRUE(test.cv_->wait_for(lock, s_test_wait_time) ==
+ std::cv_status::no_timeout)
+ << "Timeout";
+
+ return test.Result();
+}
+
+TEST(EndToEndTest, TimeBasedWithTwoPauses) {
+ auto result = TestEndToEndTimeBasedWithTwoPauses();
+ TuningForkLogEvent expected = R"TF(
+{
+ "name": "applications//apks/0",
+ "session_context":)TF" + session_context +
+ R"TF(,
+ "telemetry": [{
+ "context": {
+ "annotations": "",
+ "duration": "8s",
+ "tuning_parameters": {
+ "experiment_id": "",
+ "serialized_fidelity_parameters": ""
+ }
+ },
+ "report": {
+ "rendering": {
+ "render_time_histogram": [{
+ "counts": [
+ 0, 0, 0, 0, 0, 10, 60, 10, 0, 0, 0, 0],
+ "instrument_id": 64000
+ }]
+ }
+ }
+ }]
+}
+)TF";
+ CheckStrings("TimeBased", result, expected);
+}
+
+TuningForkLogEvent TimeBasedCheckUploadWhenPaused() {
+ const int NTICKS =
+ 101; // note the first tick doesn't add anything to the histogram
+ auto settings =
+ TestSettings(tf::Settings::AggregationStrategy::Submission::TIME_BASED,
+ 10100, 1, {}, {{TFTICK_RAW_FRAME_TIME, 50, 150, 10}});
+ TuningForkTest test(settings, milliseconds(100));
+ std::unique_lock<std::mutex> lock(*test.rmutex_);
+ for (int i = 0; i < NTICKS; ++i) {
+ if (i > 40) {
+ tf::PauseFrameTimeLogging();
+ }
+ test.IncrementTime();
+ tf::FrameTick(TFTICK_RAW_FRAME_TIME);
+ }
+ // Wait for the upload thread to complete writing the string
+ EXPECT_TRUE(test.cv_->wait_for(lock, s_test_wait_time) ==
+ std::cv_status::no_timeout);
+ return test.Result();
+}
+
+// Verify uploading still works when paused
+TEST(EndToEndTest, TimeBasedCheckUploadWhenPaused) {
+ auto result = TimeBasedCheckUploadWhenPaused();
+ TuningForkLogEvent expected = R"TF(
+{
+ "name": "applications//apks/0",
+ "session_context":
+{
+ "device": {
+ "brand": "",
+ "build_version": "",
+ "cpu_core_freqs_hz": [],
+ "device": "",
+ "fingerprint": "",
+ "gles_version": {
+ "major": 0,
+ "minor": 0
+ },
+ "height_pixels": 0,
+ "model": "",
+ "product": "",
+ "soc_manufacturer": "",
+ "soc_model": "",
+ "swap_total_bytes": 123,
+ "total_memory_bytes": 0,
+ "width_pixels": 0
+ },
+ "game_sdk_info": {
+ "session_id": "",
+ "version": "1.0.0"
+ },
+ "time_period": {
+ "end_time": "!REGEX(.*?Z)",
+ "start_time": "1970-01-01T00:00:00.020000Z"
+ }
+},
+ "telemetry": [{
+ "context": {
+ "annotations": "",
+ "duration": "!REGEX(.*?s)",
+ "tuning_parameters": {
+ "experiment_id": "",
+ "serialized_fidelity_parameters": ""
+ }
+ },
+ "report": {
+ "rendering": {
+ "render_time_histogram": [{
+ "counts": [
+ 0, 0, 0, 0, 0, 0, 40, 0, 0, 0, 0, 0],
+ "instrument_id": 64000
+ }]
+ }
+ }
+ }]
+}
+)TF";
+ CheckStrings("TimeBased", result, expected);
+}
+
+TuningForkLogEvent TimeBasedCheckUploadWhenPausedAndFlushCalled() {
+ auto settings =
+ TestSettings(tf::Settings::AggregationStrategy::Submission::TIME_BASED,
+ 10100, 1, {}, {{TFTICK_RAW_FRAME_TIME, 50, 150, 10}});
+ TuningForkTest test(settings, milliseconds(100));
+ std::unique_lock<std::mutex> lock(*test.rmutex_);
+ for (int i = 0; i < 40; ++i) {
+ test.IncrementTime();
+ tf::FrameTick(TFTICK_RAW_FRAME_TIME);
+ }
+ // tf::PauseLogging(true);
+ tf::Flush(true);
+
+ // Wait for the upload thread to complete writing the string
+ EXPECT_TRUE(test.cv_->wait_for(lock, s_test_wait_time) ==
+ std::cv_status::no_timeout)
+ << "Timeout";
+ return test.Result();
+}
+
+// Verify flush still works when paused
+TEST(EndToEndTest, TimeBasedCheckUploadWhenPausedAndFlushCalled) {
+ auto result = TimeBasedCheckUploadWhenPausedAndFlushCalled();
+ TuningForkLogEvent expected = R"TF(
+{
+ "name": "applications//apks/0",
+ "session_context":
+{
+ "device": {
+ "brand": "",
+ "build_version": "",
+ "cpu_core_freqs_hz": [],
+ "device": "",
+ "fingerprint": "",
+ "gles_version": {
+ "major": 0,
+ "minor": 0
+ },
+ "height_pixels": 0,
+ "model": "",
+ "product": "",
+ "soc_manufacturer": "",
+ "soc_model": "",
+ "swap_total_bytes": 123,
+ "total_memory_bytes": 0,
+ "width_pixels": 0
+ },
+ "game_sdk_info": {
+ "session_id": "",
+ "version": "1.0.0"
+ },
+ "time_period": {
+ "end_time": "!REGEX(.*?Z)",
+ "start_time": "1970-01-01T00:00:00.020000Z"
+ }
+},
+ "telemetry": [{
+ "context": {
+ "annotations": "",
+ "duration": "!REGEX(.*?s)",
+ "tuning_parameters": {
+ "experiment_id": "",
+ "serialized_fidelity_parameters": ""
+ }
+ },
+ "report": {
+ "rendering": {
+ "render_time_histogram": [{
+ "counts": [
+ 0, 0, 0, 0, 0, 0, 39, 0, 0, 0, 0, 0],
+ "instrument_id": 64000
+ }]
+ }
+ }
+ }]
+}
+)TF";
+ CheckStrings("TimeBased", result, expected);
+}
+
} // namespace tuningfork_test
diff --git a/test/tuningfork/endtoend/trace.cpp b/test/tuningfork/endtoend/trace.cpp
new file mode 100644
index 0000000..3952bb0
--- /dev/null
+++ b/test/tuningfork/endtoend/trace.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Trace.h"
+
+#include <sys/system_properties.h>
+
+#include "common.h"
+#include "test_utils.h"
+#include "tuningfork_test.h"
+
+using namespace gamesdk_test;
+
+namespace tuningfork_test {
+
+/*test the Trace.h api*/
+TEST(EndToEndTest, systrace) {
+ char osVersionStr[PROP_VALUE_MAX + 1];
+ __system_property_get("ro.build.version.release", osVersionStr);
+ int osVersion = atoi(osVersionStr);
+ std::unique_ptr<gamesdk::Trace> trace_ = gamesdk::Trace::create();
+
+ if (osVersion >= 6 /*api level 23*/) {
+ ASSERT_TRUE(trace_->supportsBasicATrace());
+ } else {
+ ASSERT_FALSE(trace_->supportsBasicATrace());
+ }
+
+ if (osVersion >= 10 /*api level 29*/) {
+ ASSERT_TRUE(trace_->supportsFullATrace());
+ } else {
+ ASSERT_FALSE(trace_->supportsFullATrace());
+ }
+
+ // Check that calls don't fail even if full ATrace isn't supported
+ trace_->beginSection("test");
+ trace_->endSection();
+ trace_->beginAsyncSection("test", 101);
+ trace_->endAsyncSection("test", 101);
+}
+
+} // namespace tuningfork_test
diff --git a/test/tuningfork/file_cache_test.cpp b/test/tuningfork/file_cache_test.cpp
index b0c6902..85e98f9 100644
--- a/test/tuningfork/file_cache_test.cpp
+++ b/test/tuningfork/file_cache_test.cpp
@@ -23,7 +23,7 @@
#include "core/tuningfork_utils.h"
#include "jni/jni_helper.h"
#include "proto/protobuf_util.h"
-#include "tf_test_utils.h"
+#include "test_utils.h"
#include "tuningfork_test_c.h"
namespace test {
diff --git a/test/tuningfork/metrics/app/src/main/cpp/CMakeLists.txt b/test/tuningfork/metrics/app/src/main/cpp/CMakeLists.txt
index 3133da7..2b07438 100644
--- a/test/tuningfork/metrics/app/src/main/cpp/CMakeLists.txt
+++ b/test/tuningfork/metrics/app/src/main/cpp/CMakeLists.txt
@@ -4,42 +4,13 @@
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.4.1)
+project(metrics-test)
+
include_directories(../../../../../../../src/common)
-# Creates and names a library, sets it as either STATIC
-# or SHARED, and provides the relative paths to its source code.
-# You can define multiple libraries, and CMake builds them for you.
-# Gradle automatically packages shared libraries with your APK.
+# Build metrics-test native library.
+add_library(${PROJECT_NAME} SHARED
+ metrics-test.cpp)
-add_library( # Sets the name of the library.
- native-lib
-
- # Sets the library as a shared library.
- SHARED
-
- # Provides a relative path to your source file(s).
- native-lib.cpp)
-
-# Searches for a specified prebuilt library and stores the path as a
-# variable. Because CMake includes system libraries in the search path by
-# default, you only need to specify the name of the public NDK library
-# you want to add. CMake verifies that the library exists before
-# completing its build.
-
-find_library( # Sets the name of the path variable.
- log-lib
-
- # Specifies the name of the NDK library that
- # you want CMake to locate.
- log )
-
-# Specifies libraries CMake should link to your target library. You
-# can link multiple libraries, such as libraries you define in this
-# build script, prebuilt third-party libraries, or system libraries.
-
-target_link_libraries( # Specifies the target library.
- native-lib
-
- # Links the target library to the log library
- # included in the NDK.
- ${log-lib} )
+target_link_libraries(${PROJECT_NAME}
+ log )
diff --git a/test/tuningfork/metrics/app/src/main/cpp/native-lib.cpp b/test/tuningfork/metrics/app/src/main/cpp/metrics-test.cpp
similarity index 100%
rename from test/tuningfork/metrics/app/src/main/cpp/native-lib.cpp
rename to test/tuningfork/metrics/app/src/main/cpp/metrics-test.cpp
diff --git a/test/tuningfork/metrics/app/src/main/java/test/tuningfork/metricstest/MainActivity.kt b/test/tuningfork/metrics/app/src/main/java/test/tuningfork/metricstest/MainActivity.kt
index 5bbeedb..a8caf71 100644
--- a/test/tuningfork/metrics/app/src/main/java/test/tuningfork/metricstest/MainActivity.kt
+++ b/test/tuningfork/metrics/app/src/main/java/test/tuningfork/metricstest/MainActivity.kt
@@ -15,15 +15,15 @@
}
/**
- * A native method that is implemented by the 'native-lib' native library,
+ * A native method that is implemented by the 'metrics-test' native library,
* which is packaged with this application.
*/
external fun measureMetricsPerformance(d: Class<Debug>): String
companion object {
- // Used to load the 'native-lib' library on application startup.
+ // Used to load app's native library on application startup.
init {
- System.loadLibrary("native-lib")
+ System.loadLibrary("metrics-test")
}
}
}
diff --git a/test/tuningfork/metrics/build.gradle b/test/tuningfork/metrics/build.gradle
index 71b78e0..c5d45c1 100644
--- a/test/tuningfork/metrics/build.gradle
+++ b/test/tuningfork/metrics/build.gradle
@@ -6,7 +6,7 @@
jcenter()
}
dependencies {
- classpath "com.android.tools.build:gradle:4.0.0"
+ classpath "com.android.tools.build:gradle:4.2.2"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
@@ -23,4 +23,4 @@
task clean(type: Delete) {
delete rootProject.buildDir
-}
\ No newline at end of file
+}
diff --git a/test/tuningfork/metrics/gradle/wrapper/gradle-wrapper.properties b/test/tuningfork/metrics/gradle/wrapper/gradle-wrapper.properties
index 5b7bbf7..90c39bb 100644
--- a/test/tuningfork/metrics/gradle/wrapper/gradle-wrapper.properties
+++ b/test/tuningfork/metrics/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
diff --git a/test/tuningfork/proto/tuningfork.proto b/test/tuningfork/proto/tuningfork.proto
index 4c4e431..3060f3a 120000
--- a/test/tuningfork/proto/tuningfork.proto
+++ b/test/tuningfork/proto/tuningfork.proto
@@ -1 +1 @@
-../../../src/tuningfork/proto/tuningfork.proto
\ No newline at end of file
+../../../games-performance-tuner/proto/tuningfork.proto
\ No newline at end of file
diff --git a/test/tuningfork/proto/tuningfork_clearcut_log.proto b/test/tuningfork/proto/tuningfork_clearcut_log.proto
index 47bed98..2f1c254 120000
--- a/test/tuningfork/proto/tuningfork_clearcut_log.proto
+++ b/test/tuningfork/proto/tuningfork_clearcut_log.proto
@@ -1 +1 @@
-../../../src/tuningfork/proto/tuningfork_clearcut_log.proto
\ No newline at end of file
+../../../games-performance-tuner/proto/tuningfork_clearcut_log.proto
\ No newline at end of file
diff --git a/test/tuningfork/serialization_test.cpp b/test/tuningfork/serialization_test.cpp
index b22ad15..bb2a604 100644
--- a/test/tuningfork/serialization_test.cpp
+++ b/test/tuningfork/serialization_test.cpp
@@ -21,12 +21,12 @@
#include "common/gamesdk_common.h"
#include "core/tuningfork_utils.h"
#include "http_backend/json_serializer.h"
-#include "tf_test_utils.h"
+#include "test_utils.h"
namespace serialization_test {
using namespace tuningfork;
-using namespace tuningfork_test;
+using namespace gamesdk_test;
using namespace json11;
using namespace std::chrono;
@@ -50,7 +50,9 @@
"SOC_MODEL" /*soc_model*/,
"SOC_MANUFACTURER" /*soc_manufacturer*/,
234 /*swap_total_bytes*/,
- ANDROID_GAMESDK_PACKED_VERSION(2, 7, 0) /*swappy_version*/};
+ ANDROID_GAMESDK_PACKED_VERSION(2, 7, 0) /*swappy_version*/,
+ 1024 /*height_pixels*/,
+ 768 /*width_pixels*/};
std::string test_device_info_ser = R"TF({
"brand": "BRAND",
@@ -61,12 +63,14 @@
"gles_version": {
"major": 5, "minor": 21907
},
+ "height_pixels": 1024,
"model": "MODEL",
"product": "PRODUCT",
"soc_manufacturer": "SOC_MANUFACTURER",
"soc_model": "SOC_MODEL",
"swap_total_bytes": 234,
- "total_memory_bytes": 2387
+ "total_memory_bytes": 2387,
+ "width_pixels": 768
})TF";
void CheckDeviceInfo(const RequestInfo& info) {
@@ -92,12 +96,14 @@
"major": 5,
"minor": 21907
},
+ "height_pixels": 1024,
"model": "MODEL",
"product": "PRODUCT",
"soc_manufacturer": "SOC_MANUFACTURER",
"soc_model": "SOC_MODEL",
"swap_total_bytes": 234,
- "total_memory_bytes": 2387
+ "total_memory_bytes": 2387,
+ "width_pixels": 768
},
"game_sdk_info": {
"session_id": "sess",
diff --git a/test/tuningfork/testapp/app/build.gradle b/test/tuningfork/testapp/app/build.gradle
index 5dc8530..fe250f1 100644
--- a/test/tuningfork/testapp/app/build.gradle
+++ b/test/tuningfork/testapp/app/build.gradle
@@ -1,12 +1,11 @@
apply plugin: 'com.android.application'
android {
- compileSdkVersion 29
- buildToolsVersion "29.0.2"
+ compileSdkVersion 31
defaultConfig {
applicationId "com.tuningfork.testapp"
- minSdkVersion 15
- targetSdkVersion 29
+ minSdkVersion 19
+ targetSdkVersion 31
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@@ -21,22 +20,25 @@
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
- debug {
- debuggable true
- }
+ debug {
+ debuggable true
+ }
}
externalNativeBuild {
cmake {
path "src/main/cpp/CMakeLists.txt"
}
}
+ buildFeatures {
+ prefab true
+ }
}
dependencies {
- implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
- testImplementation 'junit:junit:4.12'
- androidTestImplementation 'androidx.test.ext:junit:1.1.1'
- androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
+ // Uncomment this line and comment out "implementation project(":games-performance-tuner")"
+ // to use a locally built .aar
+ //implementation fileTree(dir: '../../../../', include: ['games-performance-tuner-release.aar'])
+ implementation project(":games-performance-tuner")
}
diff --git a/test/tuningfork/testapp/app/src/main/AndroidManifest.xml b/test/tuningfork/testapp/app/src/main/AndroidManifest.xml
index 7e0f4b4..b49cc5d 100644
--- a/test/tuningfork/testapp/app/src/main/AndroidManifest.xml
+++ b/test/tuningfork/testapp/app/src/main/AndroidManifest.xml
@@ -9,7 +9,8 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
- <activity android:name=".MainActivity">
+ <activity android:name=".MainActivity"
+ android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
diff --git a/test/tuningfork/testapp/app/src/main/cpp/CMakeLists.txt b/test/tuningfork/testapp/app/src/main/cpp/CMakeLists.txt
index 93f275e..86ef7da 100644
--- a/test/tuningfork/testapp/app/src/main/cpp/CMakeLists.txt
+++ b/test/tuningfork/testapp/app/src/main/cpp/CMakeLists.txt
@@ -25,20 +25,9 @@
"${TEST_DIR}/../../samples/common/include"
)
-add_library( native-lib
+add_library( ${PROJECT_NAME} SHARED
+ test_app.cpp )
- SHARED
-
- native-lib.cpp )
-
-find_library( # Sets the name of the path variable.
- log-lib
-
- # Specifies the name of the NDK library that
- # you want CMake to locate.
- log )
-
-target_link_libraries( # Specifies the target library.
- native-lib
+target_link_libraries( ${PROJECT_NAME}
tuningfork_test_lib
- ${log-lib} )
+ log)
diff --git a/test/tuningfork/testapp/app/src/main/cpp/native-lib.cpp b/test/tuningfork/testapp/app/src/main/cpp/test_app.cpp
similarity index 100%
rename from test/tuningfork/testapp/app/src/main/cpp/native-lib.cpp
rename to test/tuningfork/testapp/app/src/main/cpp/test_app.cpp
diff --git a/test/tuningfork/testapp/app/src/main/java/com/tuningfork/testapp/MainActivity.java b/test/tuningfork/testapp/app/src/main/java/com/tuningfork/testapp/MainActivity.java
index d4b83fe..00f6de0 100644
--- a/test/tuningfork/testapp/app/src/main/java/com/tuningfork/testapp/MainActivity.java
+++ b/test/tuningfork/testapp/app/src/main/java/com/tuningfork/testapp/MainActivity.java
@@ -23,9 +23,9 @@
public class MainActivity extends AppCompatActivity {
- // Used to load the 'native-lib' library on application startup.
+ // Used to load the app's native library on application startup.
static {
- System.loadLibrary("native-lib");
+ System.loadLibrary("tuningfork-testing");
}
@Override
@@ -39,7 +39,7 @@
}
/**
- * A native method that is implemented by the 'native-lib' native library, which is packaged with
+ * A native method that is implemented by app's native library, which is packaged with
* this application.
*/
public native String runTests();
diff --git a/test/tuningfork/testapp/build.gradle b/test/tuningfork/testapp/build.gradle
index c14e88f..f817a65 100644
--- a/test/tuningfork/testapp/build.gradle
+++ b/test/tuningfork/testapp/build.gradle
@@ -7,7 +7,7 @@
}
dependencies {
- classpath 'com.android.tools.build:gradle:4.2.0'
+ classpath 'com.android.tools.build:gradle:7.2.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
diff --git a/test/tuningfork/testapp/gradle/wrapper/gradle-wrapper.properties b/test/tuningfork/testapp/gradle/wrapper/gradle-wrapper.properties
index f7c4ab5..b5382bb 100644
--- a/test/tuningfork/testapp/gradle/wrapper/gradle-wrapper.properties
+++ b/test/tuningfork/testapp/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip
\ No newline at end of file
diff --git a/test/tuningfork/testapp/settings.gradle b/test/tuningfork/testapp/settings.gradle
index 6fef711..bde3af9 100644
--- a/test/tuningfork/testapp/settings.gradle
+++ b/test/tuningfork/testapp/settings.gradle
@@ -1,2 +1,4 @@
include ':app'
rootProject.name='TuningForkTesting'
+include ':games-performance-tuner'
+project(':games-performance-tuner').projectDir = file('../../../games-performance-tuner')
diff --git a/test/tuningfork/tf_test_utils.cpp b/test/tuningfork/tf_test_utils.cpp
deleted file mode 100644
index 5679ec0..0000000
--- a/test/tuningfork/tf_test_utils.cpp
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "tf_test_utils.h"
-
-#include <regex>
-#include <sstream>
-
-namespace tuningfork_test {
-
-// Wind forward over any arrays ... [...] ...
-template <typename Iterator>
-bool WindForwardOverArrays(Iterator& i, const Iterator& end,
- char begin_ch = '[', char end_ch = ']',
- std::string* inner = nullptr) {
- std::stringstream result;
- if (*i != begin_ch) return false;
- ++i;
- int level = 1;
- for (; i != end; ++i) {
- if (*i == begin_ch) ++level;
- if (*i == end_ch) {
- --level;
- if (level == 0) {
- ++i;
- if (inner != nullptr) *inner = result.str();
- return true;
- }
- if (level < 0) return false;
- }
- if (inner != nullptr) result << *i;
- }
- return false;
-}
-
-template <typename Iterator>
-bool WindForwardOverRegex(const std::string& regex_str, Iterator& i,
- const Iterator& end, std::string* inner = nullptr) {
- std::regex regex(regex_str);
- std::match_results<Iterator> result;
- // Match at the beginning of the string only.
- auto match_flags = std::regex_constants::match_continuous;
- if (std::regex_search(i, end, result, regex, match_flags)) {
- if (inner != nullptr) {
- *inner = result[0];
- }
- i += result[0].length();
- return true;
- }
- return false;
-}
-
-bool CompareIgnoringWhitespace(std::string s0, std::string s1,
- std::string* error_msg) {
- // Ignore all whitespace, except when in strings.
- // '[**]' is a wildcard for any array.
- // '!REGEX(<regex>) is a regex.
- bool in_string = false;
- bool in_wildcard = false;
- auto a = s0.begin();
- auto b = s1.begin();
- auto produceErrorMessage = [&]() {
- if (error_msg != nullptr) {
- std::stringstream str;
- str << "Error at position " << (a - s0.begin()) << std::endl;
- str << std::string(std::max(s0.begin(), a - 20), a);
- str << "<HERE>";
- str << std::string(a, std::min(s0.end(), a + 20));
- str << std::endl;
- str << std::string(std::max(s1.begin(), b - 20), b);
- str << "<HERE>";
- str << std::string(b, std::min(s1.end(), b + 20));
- *error_msg = str.str();
- }
- };
- while (true) {
- if (!in_string) {
- while (a != s0.end() && isspace(*a)) a++;
- while (b != s1.end() && isspace(*b)) b++;
- }
- if (a == s0.end()) break;
- if (b == s1.end()) {
- produceErrorMessage();
- return false;
- }
- // Array wildcard
- if ((s1.end() - b) >= kArrayWildcard.length() &&
- std::string(b, b + kArrayWildcard.length()) == kArrayWildcard) {
- if (!WindForwardOverArrays(a, s0.end())) break;
- b += kArrayWildcard.length();
- continue;
- }
- // Regex
- if ((s1.end() - b) >= kRegexPattern.length() &&
- std::string(b, b + kRegexPattern.length()) == kRegexPattern) {
- b += kRegexPattern.length();
- std::string regex;
- if (!WindForwardOverArrays(b, s1.end(), '(', ')', ®ex)) break;
- if (!WindForwardOverRegex(regex, a, s0.end())) break;
- continue;
- }
- if (*a != *b) {
- produceErrorMessage();
- return false;
- }
- if (*a == '"') in_string = !in_string;
- ++a;
- ++b;
- }
- if (b == s1.end()) {
- return true;
- } else {
- produceErrorMessage();
- return false;
- }
-}
-
-} // namespace tuningfork_test
diff --git a/third_party/cube/OWNERS b/third_party/cube/OWNERS
index d79d861..289ff34 100644
--- a/third_party/cube/OWNERS
+++ b/third_party/cube/OWNERS
@@ -1 +1 @@
-include ../../src/swappy/OWNERS
+include ../../games-frame-pacing/OWNERS
diff --git a/third_party/cube/app/CMakeLists.for-samples-in-archive.txt b/third_party/cube/app/CMakeLists.for-samples-in-archive.txt
index 73cd06c..5d744b5 100644
--- a/third_party/cube/app/CMakeLists.for-samples-in-archive.txt
+++ b/third_party/cube/app/CMakeLists.for-samples-in-archive.txt
@@ -1,16 +1,10 @@
cmake_minimum_required(VERSION 3.4.1)
+project(cube)
+
+find_package(games-frame-pacing REQUIRED CONFIG)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -Werror -DVK_USE_PLATFORM_ANDROID_KHR")
-# ============== Games SDK
-
-# This uses Swappy as a static library from the Game SDK package. Make sure the Android
-# SDK and NDK versions that you are using are supported by the Game SDK.
-include("../../../samples/gamesdk.cmake")
-add_gamesdk_target(PACKAGE_DIR "../../../" BUILD_TYPE "Release")
-
-# ============== cube
-
# Build Util Lib
set(UTILS_NAME vsamputils)
set(GLSLANG_SPIRV_INCLUDE_DIR ${SHADERC_SRC}/third_party/glslang)
@@ -40,7 +34,7 @@
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -Werror -DVK_USE_PLATFORM_ANDROID_KHR")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Werror -DVK_USE_PLATFORM_ANDROID_KHR")
-add_library( native-lib
+add_library( ${CMAKE_PROJECT_NAME}
SHARED
@@ -51,7 +45,7 @@
src/main/cpp/common/vulkan_wrapper.cpp
)
-target_include_directories(native-lib
+target_include_directories(${CMAKE_PROJECT_NAME}
PRIVATE
@@ -61,9 +55,8 @@
src/main/cpp/common)
-target_link_libraries( native-lib
-
- swappy
+target_link_libraries( ${CMAKE_PROJECT_NAME}
+ games-frame-pacing::swappy_static
android
log
)
diff --git a/third_party/cube/app/CMakeLists.txt b/third_party/cube/app/CMakeLists.txt
index 33d551d..8b8f8d7 100644
--- a/third_party/cube/app/CMakeLists.txt
+++ b/third_party/cube/app/CMakeLists.txt
@@ -1,24 +1,11 @@
cmake_minimum_required(VERSION 3.4.1)
+project(cube)
+
+find_package(games-frame-pacing REQUIRED CONFIG)
+
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -Werror -DVK_USE_PLATFORM_ANDROID_KHR")
-# ============== Games SDK
-
-# This will use swappy from a packaged version
-include("../../../samples/gamesdk.cmake")
-add_gamesdk_target(
- DO_LOCAL_BUILD
- ROOT_DIR "../../.."
- PACKAGE_DIR "../../../../package/local"
- LIBRARIES "swappy"
-)
-
-# Uncomment to add the Game SDK sources as part of the project sources, allowing to develop
-# (with auto completions) and debug Swappy from Android Studio using this sample.
-#add_gamesdk_sources()
-
-# ============== cube
-
# Build Util Lib
set(UTILS_NAME vsamputils)
set(GLSLANG_SPIRV_INCLUDE_DIR ${SHADERC_SRC}/third_party/glslang)
@@ -48,7 +35,7 @@
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -Werror -DVK_USE_PLATFORM_ANDROID_KHR")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Werror -DVK_USE_PLATFORM_ANDROID_KHR")
-add_library( native-lib
+add_library( ${CMAKE_PROJECT_NAME}
SHARED
@@ -59,7 +46,7 @@
src/main/cpp/common/vulkan_wrapper.cpp
)
-target_include_directories(native-lib
+target_include_directories(${CMAKE_PROJECT_NAME}
PRIVATE
@@ -69,9 +56,8 @@
src/main/cpp/common)
-target_link_libraries( native-lib
-
- swappy
+target_link_libraries( ${CMAKE_PROJECT_NAME}
+ games-frame-pacing::swappy_static
android
log
)
diff --git a/third_party/cube/app/build.gradle b/third_party/cube/app/build.gradle
index fc4e630..dc7b939 100644
--- a/third_party/cube/app/build.gradle
+++ b/third_party/cube/app/build.gradle
@@ -21,13 +21,13 @@
def stlType = 'c++_static'
android {
- compileSdkVersion 26
- ndkVersion "20.0.5594570"
+ compileSdkVersion 31
+ ndkVersion "23.1.7779620"
defaultConfig {
applicationId 'com.samples.cube'
minSdkVersion 24 // Official vulkan support starts in version 24
- targetSdkVersion 26
+ targetSdkVersion 31
versionCode 2
versionName '0.0.2'
externalNativeBuild {
@@ -51,8 +51,14 @@
'proguard-rules.pro'
}
}
+ buildFeatures {
+ prefab true
+ }
}
dependencies {
- implementation fileTree(dir: 'libs', include: ['*.jar'])
+ // Uncomment this line and comment out "implementation project(":games-controller")"
+ // to use a locally built .aar
+ //implementation fileTree(dir: '../../../', include: ['games-frame-pacing-release.aar'])
+ implementation project(":games-frame-pacing")
}
diff --git a/third_party/cube/app/src/main/AndroidManifest.xml b/third_party/cube/app/src/main/AndroidManifest.xml
index ea51328..0eea6a0 100644
--- a/third_party/cube/app/src/main/AndroidManifest.xml
+++ b/third_party/cube/app/src/main/AndroidManifest.xml
@@ -2,12 +2,14 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.samples.cube">
- <application android:label="@string/app_name">
+<application android:label="@string/app_name"
+ android:theme="@style/AppTheme">
<activity
android:name=".CubeActivity"
android:launchMode="singleTop"
android:configChanges="orientation|screenSize"
- android:screenOrientation="portrait">
+ android:screenOrientation="portrait"
+ android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
@@ -19,4 +21,4 @@
</activity>
</application>
-</manifest>
\ No newline at end of file
+</manifest>
diff --git a/third_party/cube/app/src/main/cpp/common/vulkan_wrapper.cpp b/third_party/cube/app/src/main/cpp/common/vulkan_wrapper.cpp
index 876ae40..18830a3 100644
--- a/third_party/cube/app/src/main/cpp/common/vulkan_wrapper.cpp
+++ b/third_party/cube/app/src/main/cpp/common/vulkan_wrapper.cpp
@@ -625,15 +625,6 @@
PFN_vkCmdDrawIndexedIndirectCountAMD vkCmdDrawIndexedIndirectCountAMD;
PFN_vkGetShaderInfoAMD vkGetShaderInfoAMD;
PFN_vkGetPhysicalDeviceExternalImageFormatPropertiesNV vkGetPhysicalDeviceExternalImageFormatPropertiesNV;
-PFN_vkCmdProcessCommandsNVX vkCmdProcessCommandsNVX;
-PFN_vkCmdReserveSpaceForCommandsNVX vkCmdReserveSpaceForCommandsNVX;
-PFN_vkCreateIndirectCommandsLayoutNVX vkCreateIndirectCommandsLayoutNVX;
-PFN_vkDestroyIndirectCommandsLayoutNVX vkDestroyIndirectCommandsLayoutNVX;
-PFN_vkCreateObjectTableNVX vkCreateObjectTableNVX;
-PFN_vkDestroyObjectTableNVX vkDestroyObjectTableNVX;
-PFN_vkRegisterObjectsNVX vkRegisterObjectsNVX;
-PFN_vkUnregisterObjectsNVX vkUnregisterObjectsNVX;
-PFN_vkGetPhysicalDeviceGeneratedCommandsPropertiesNVX vkGetPhysicalDeviceGeneratedCommandsPropertiesNVX;
PFN_vkCmdSetViewportWScalingNV vkCmdSetViewportWScalingNV;
PFN_vkReleaseDisplayEXT vkReleaseDisplayEXT;
PFN_vkGetPhysicalDeviceSurfaceCapabilities2EXT vkGetPhysicalDeviceSurfaceCapabilities2EXT;
diff --git a/third_party/cube/app/src/main/cpp/cube.c b/third_party/cube/app/src/main/cpp/cube.c
index 2fa4169..a7c629f 100644
--- a/third_party/cube/app/src/main/cpp/cube.c
+++ b/third_party/cube/app/src/main/cpp/cube.c
@@ -56,6 +56,8 @@
#endif
#include <vulkan/vk_platform.h>
+//Ndks >= 23 have a vulkan header that removed this definition
+#define VK_DYNAMIC_STATE_RANGE_SIZE (VK_DYNAMIC_STATE_STENCIL_REFERENCE - VK_DYNAMIC_STATE_VIEWPORT + 1)
#include "linmath.h"
#include "object_type_string_helper.h"
@@ -544,6 +546,7 @@
} swappy_init_data;
bool tracer_injected;
+ bool swappy_enabled;
};
VKAPI_ATTR VkBool32 VKAPI_CALL debug_messenger_callback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
@@ -1156,6 +1159,11 @@
}
} while (err != VK_SUCCESS);
+ if (demo->swappy_enabled) {
+ SwappyVk_recordFrameStart(demo->present_queue,
+ demo->swapchain,
+ demo->current_buffer);
+ }
demo_update_data_buffer(demo);
if (demo->VK_GOOGLE_display_timing_enabled) {
@@ -1479,6 +1487,8 @@
.clipped = true,
};
uint32_t i;
+ // Reset swappy enabled.
+ demo->swappy_enabled = false;
err = demo->fpCreateSwapchainKHR(demo->device, &swapchain_ci, NULL, &demo->swapchain);
assert(!err);
@@ -1552,11 +1562,14 @@
demo->gpu, demo->device, demo->swapchain,
&demo->refresh_duration));
+ assert(SwappyVk_isEnabled(demo->swapchain, &demo->swappy_enabled));
+
SwappyVk_setWindow(demo->device, demo->swapchain, demo->window);
// Refresh rate of this demo is locked to 30 FPS.
SwappyVk_setSwapIntervalNS(demo->device, demo->swapchain, SWAPPY_SWAP_30FPS);
DbgMsg("Swappy swap interval = %" PRIu64 " ns", SwappyVk_getSwapIntervalNS(demo->swapchain));
+ SwappyVk_enableStats(demo->swapchain, true);
if (!demo->tracer_injected) {
SwappyTracer tracer;
@@ -4152,6 +4165,13 @@
}
}
+VkSwapchainKHR get_current_swapchain() {
+ if (demo_.swappy_enabled)
+ return demo_.swapchain;
+ else
+ return 0;
+}
+
#else
int main(int argc, char **argv) {
struct demo demo;
diff --git a/third_party/cube/app/src/main/cpp/include/cube.h b/third_party/cube/app/src/main/cpp/include/cube.h
index 58e4900..3fc3417 100644
--- a/third_party/cube/app/src/main/cpp/include/cube.h
+++ b/third_party/cube/app/src/main/cpp/include/cube.h
@@ -5,6 +5,10 @@
#include <android/native_activity.h>
#include <stdbool.h>
+#define VK_NO_PROTOTYPES 1
+#include <vulkan/vulkan.h>
+#undef VK_NO_PROTOTYPES
+
// Struct for passing state from java app to native app.
struct android_app_state {
ANativeWindow* window;
@@ -22,3 +26,6 @@
// Update the amount of CPU work done each frame.
void update_cpu_workload(int32_t new_workload);
+
+// Get the current active swapchain.
+VkSwapchainKHR get_current_swapchain();
diff --git a/third_party/cube/app/src/main/cpp/native-lib.c b/third_party/cube/app/src/main/cpp/native-lib.c
index e5c2737..6d22a3c 100644
--- a/third_party/cube/app/src/main/cpp/native-lib.c
+++ b/third_party/cube/app/src/main/cpp/native-lib.c
@@ -1,9 +1,12 @@
#include <jni.h>
#include <android/native_window_jni.h>
+#include <math.h>
#include <pthread.h>
+#include <swappy/swappyVk.h>
#include "cube.h"
+
static struct android_app_state state;
static pthread_t thread;
@@ -47,3 +50,52 @@
Java_com_samples_cube_CubeActivity_nUpdateCpuWorkload(JNIEnv *env, jobject clazz, jint new_workload) {
update_cpu_workload(new_workload);
}
+
+JNIEXPORT int JNICALL
+Java_com_samples_cube_CubeActivity_nGetSwappyStats(JNIEnv *env,
+ jobject clazz,
+ jint stat,
+ jint bin) {
+ static bool enabled = false;
+ VkSwapchainKHR swapchain = get_current_swapchain();
+ if (!swapchain) {
+ return -1;
+ }
+ if (!enabled) {
+ SwappyVk_enableStats(swapchain,true);
+ enabled = true;
+ }
+
+ // stats are read one by one, query once per stat
+ static SwappyStats stats;
+ static int stat_idx = -1;
+
+ if (stat_idx != stat) {
+ SwappyVk_getStats(swapchain, &stats);
+ stat_idx = stat;
+ }
+
+ int value = 0;
+
+ if (stats.totalFrames) {
+ switch (stat) {
+ case 0:
+ value = stats.idleFrames[bin];
+ break;
+ case 1:
+ value = stats.lateFrames[bin];
+ break;
+ case 2:
+ value = stats.offsetFromPreviousFrame[bin];
+ break;
+ case 3:
+ value = stats.latencyFrames[bin];
+ break;
+ default:
+ return stats.totalFrames;
+ }
+ value = round(value * 100.0f / stats.totalFrames);
+ }
+
+ return value;
+}
diff --git a/third_party/cube/app/src/main/cpp/object_type_string_helper.h b/third_party/cube/app/src/main/cpp/object_type_string_helper.h
index eb79ba3..c82e3d7 100644
--- a/third_party/cube/app/src/main/cpp/object_type_string_helper.h
+++ b/third_party/cube/app/src/main/cpp/object_type_string_helper.h
@@ -34,8 +34,6 @@
{
case VK_OBJECT_TYPE_QUERY_POOL:
return "VK_OBJECT_TYPE_QUERY_POOL";
- case VK_OBJECT_TYPE_OBJECT_TABLE_NVX:
- return "VK_OBJECT_TYPE_OBJECT_TABLE_NVX";
case VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION:
return "VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION";
case VK_OBJECT_TYPE_SEMAPHORE:
@@ -46,8 +44,6 @@
return "VK_OBJECT_TYPE_SWAPCHAIN_KHR";
case VK_OBJECT_TYPE_SAMPLER:
return "VK_OBJECT_TYPE_SAMPLER";
- case VK_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX:
- return "VK_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX";
case VK_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT:
return "VK_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT";
case VK_OBJECT_TYPE_IMAGE:
diff --git a/third_party/cube/app/src/main/java/com/samples/cube/CubeActivity.java b/third_party/cube/app/src/main/java/com/samples/cube/CubeActivity.java
index b058caf..bd685a6 100644
--- a/third_party/cube/app/src/main/java/com/samples/cube/CubeActivity.java
+++ b/third_party/cube/app/src/main/java/com/samples/cube/CubeActivity.java
@@ -2,21 +2,28 @@
import android.app.Activity;
import android.content.Intent;
+import android.graphics.text.LineBreaker;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
import android.os.Bundle;
+import android.text.Layout;
import android.util.Log;
-import android.view.MotionEvent;
+import android.view.Choreographer;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
-import android.view.View.OnTouchListener;
-import android.view.Window;
+import android.widget.GridLayout;
import android.widget.LinearLayout;
import android.widget.SeekBar;
import android.widget.TextView;
+import java.util.Locale;
-public class CubeActivity extends Activity implements SurfaceHolder.Callback {
-
+public class CubeActivity
+ extends Activity implements Choreographer.FrameCallback, SurfaceHolder.Callback {
private static final String GPU_WORKLOAD = "com.samples.GPU_WORKLOAD";
private static final String CPU_WORKLOAD = "com.samples.CPU_WORKLOAD";
private static final String APP_NAME = "CubeActivity";
@@ -28,10 +35,10 @@
private SeekBar cpuWorkSeekBar;
private TextView cpuWorkText;
- // Used to load the 'native-lib' library on application startup.
+ // Used to load the 'cube' library on application startup.
static {
try {
- System.loadLibrary("native-lib");
+ System.loadLibrary("cube");
} catch (Exception e) {
Log.e(APP_NAME, "Native code library failed to load.\n" + e);
}
@@ -40,7 +47,6 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_cube);
settingsLayout = findViewById(R.id.settingsLayout);
@@ -50,31 +56,24 @@
SurfaceView surfaceView = findViewById(R.id.surface_view);
surfaceView.getHolder().addCallback(this);
- surfaceView.setOnTouchListener(new OnTouchListener() {
- @Override
- public boolean onTouch(View view, MotionEvent event) {
- if (event.getActionMasked() == MotionEvent.ACTION_UP) {
- toggleOptionsPanel();
- }
- return true;
- }
- });
Intent intent = getIntent();
String action = intent.getAction();
- String type = intent.getType();
switch (action) {
- case Intent.ACTION_MAIN:
- updateGpuWork(0);
- updateCpuWork(0);
- break;
- case Intent.ACTION_SEND:
- handleSendIntent(intent);
- break;
- default:
- Log.e(APP_NAME, "Unknown intent received: " + action);
- break;
+ case Intent.ACTION_MAIN:
+ updateGpuWork(0);
+ updateCpuWork(0);
+ break;
+ case Intent.ACTION_SEND:
+ handleSendIntent(intent);
+ break;
+ default:
+ Log.e(APP_NAME, "Unknown intent received: " + action);
+ break;
}
+
+ mInfoOverlay = findViewById(R.id.info_overlay);
+ buildSwappyStatsGrid();
}
private void toggleOptionsPanel() {
@@ -148,25 +147,25 @@
}
private void setupCpuWorkSeekBar() {
- cpuWorkSeekBar = findViewById(R.id.seekBarCpuWork);
- cpuWorkText = findViewById(R.id.textViewCpuWork);
+ cpuWorkSeekBar = findViewById(R.id.seekBarCpuWork);
+ cpuWorkText = findViewById(R.id.textViewCpuWork);
- cpuWorkSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
- @Override
- public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
- if (!fromUser) {
- return;
- }
+ cpuWorkSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ if (!fromUser) {
+ return;
+ }
- int newCpuWork = linearToExponential(progress);
- updateCpuWork(newCpuWork, true);
- }
+ int newCpuWork = linearToExponential(progress);
+ updateCpuWork(newCpuWork, true);
+ }
- @Override
- public void onStartTrackingTouch(SeekBar seekBar) {}
- @Override
- public void onStopTrackingTouch(SeekBar seekBar) {}
- });
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {}
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {}
+ });
}
private void updateCpuWork(int newCpuWork) {
@@ -182,11 +181,11 @@
}
static private int linearToExponential(int linearValue) {
- return linearValue == 0 ? 0 : (int)Math.pow(10, (double)linearValue / 1000.0);
+ return linearValue == 0 ? 0 : (int) Math.pow(10, (double) linearValue / 1000.0);
}
static private int exponentialToLinear(int exponentialValue) {
- return exponentialValue == 0 ? 0 : (int)Math.log10(exponentialValue) * 1000;
+ return exponentialValue == 0 ? 0 : (int) Math.log10(exponentialValue) * 1000;
}
@Override
@@ -194,23 +193,179 @@
Log.d(APP_NAME, "Surface created.");
Surface surface = holder.getSurface();
nStartCube(surface);
+ mIsRunning = true;
+ Choreographer.getInstance().postFrameCallback(this);
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.d(APP_NAME, "Surface destroyed.");
nStopCube();
+ mIsRunning = false;
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}
/**
- * Native methods that are implemented by the 'native-lib' native library,
+ * Native methods that are implemented by the 'cube' native library,
* which is packaged with this application.
*/
public native void nStartCube(Surface holder);
public native void nStopCube();
public native void nUpdateGpuWorkload(int newWorkload);
public native void nUpdateCpuWorkload(int newWorkload);
+ public native int nGetSwappyStats(int stat, int bin);
+
+ private void infoOverlayToggle() {
+ if (mInfoOverlay == null) {
+ return;
+ }
+
+ mInfoOverlayEnabled = !mInfoOverlayEnabled;
+ if (mInfoOverlayEnabled) {
+ mInfoOverlay.setVisibility(View.VISIBLE);
+ mInfoOverlayButton.setIcon(R.drawable.ic_info_solid_white_24dp);
+ } else {
+ mInfoOverlay.setVisibility(View.INVISIBLE);
+ mInfoOverlayButton.setIcon(R.drawable.ic_info_outline_white_24dp);
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.menu_cube, menu);
+
+ mInfoOverlayButton = menu.findItem(R.id.info_overlay_button);
+ if (mInfoOverlayButton != null) {
+ mInfoOverlayButton.setOnMenuItemClickListener((MenuItem item) -> {
+ infoOverlayToggle();
+ return true;
+ });
+ }
+
+ MenuItem settingsButton = menu.findItem(R.id.settings_item);
+ if (settingsButton != null) {
+ settingsButton.setOnMenuItemClickListener((MenuItem) -> {
+ toggleOptionsPanel();
+ return true;
+ });
+ }
+
+ return true;
+ }
+
+ private void configureGridCell(TextView cell) {
+ // A bunch of optimizations to reduce the cost of setText
+ cell.setEllipsize(null);
+ cell.setMaxLines(1);
+ cell.setSingleLine(true);
+ if (VERSION.SDK_INT >= VERSION_CODES.Q) {
+ cell.setBreakStrategy(LineBreaker.BREAK_STRATEGY_SIMPLE);
+ }
+ cell.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE);
+
+ cell.setTextAppearance(R.style.InfoTextSmall);
+ cell.setText("0");
+ }
+
+ private void buildSwappyStatsGrid() {
+ GridLayout infoGrid = findViewById(R.id.swappy_stats_grid);
+
+ // Add the header row
+ GridLayout.Spec headerRowSpec = GridLayout.spec(0);
+ for (int column = 0; column < mSwappyGrid[0].length; ++column) {
+ TextView cell = new TextView(getApplicationContext());
+ GridLayout.Spec colSpec = GridLayout.spec(column, 1.0f);
+ cell.setLayoutParams(new GridLayout.LayoutParams(headerRowSpec, colSpec));
+ configureGridCell(cell);
+
+ if (column == 0) {
+ cell.setText("");
+ } else {
+ cell.setText(String.format(Locale.US, "%d", column - 1));
+ }
+ infoGrid.addView(cell);
+ mSwappyGrid[0][column] = cell;
+ }
+
+ // Add the data rows
+ for (int row = 1; row < mSwappyGrid.length; ++row) {
+ GridLayout.Spec rowSpec = GridLayout.spec(row);
+
+ for (int column = 0; column < mSwappyGrid[row].length; ++column) {
+ TextView cell = new TextView(getApplicationContext());
+ GridLayout.Spec colSpec = GridLayout.spec(column, 1.0f);
+ cell.setLayoutParams(new GridLayout.LayoutParams(rowSpec, colSpec));
+ cell.setTextAppearance(R.style.InfoTextSmall);
+ configureGridCell(cell);
+
+ if (column == 0) {
+ switch (row) {
+ case 1:
+ cell.setText(R.string.idle_frames);
+ break;
+ case 2:
+ cell.setText(R.string.late_frames);
+ break;
+ case 3:
+ cell.setText(R.string.offset_frames);
+ break;
+ case 4:
+ cell.setText(R.string.latency_frames);
+ break;
+ }
+ } else {
+ cell.setText("0%");
+ }
+ infoGrid.addView(cell);
+ mSwappyGrid[row][column] = cell;
+ }
+ }
+
+ for (TextView[] row : mSwappyGrid) {
+ for (TextView column : row) {
+ column.setWidth(infoGrid.getWidth() / infoGrid.getColumnCount());
+ }
+ }
+ }
+
+ private void updateSwappyStatsBin(int row, int bin, int value) {
+ if (value == mLastSwappyStatsValues[row - 1][bin]) {
+ return;
+ }
+ mLastSwappyStatsValues[row - 1][bin] = value;
+ mSwappyGrid[row][bin + 1].setText(String.valueOf(value) + "%");
+ }
+
+ @Override
+ public void doFrame(long frameTimeNanos) {
+ long now = System.nanoTime();
+ if (mIsRunning) {
+ Choreographer.getInstance().postFrameCallback(this);
+ }
+ if (now - mLastDumpTime > SWAPPY_GET_STATS_PERIOD) {
+ for (int stat = 0; stat < 4; ++stat) {
+ for (int bin = 0; bin < SWAPPY_STATS_BIN_COUNT; ++bin) {
+ updateSwappyStatsBin(stat + 1, bin, nGetSwappyStats(stat, bin));
+ }
+ }
+ TextView appOffsetView = findViewById(R.id.swappy_stats);
+ appOffsetView.setText(
+ String.format(Locale.US, "SwappyStats: %d Total Frames", nGetSwappyStats(-1, 0)));
+ // Trim off excess precision so we don't drift forward over time
+ mLastDumpTime = now - (now % SWAPPY_GET_STATS_PERIOD);
+ }
+ }
+
+ private boolean mInfoOverlayEnabled = false;
+ private MenuItem mInfoOverlayButton;
+ private LinearLayout mInfoOverlay;
+ private final TextView[][] mSwappyGrid = new TextView[5][7];
+ private final int[][] mLastSwappyStatsValues = new int[4][6];
+ private static final int SWAPPY_STATS_BIN_COUNT = 6;
+ private static final long SWAPPY_GET_STATS_PERIOD = 1000000000; // 1s in ns.
+ private long mLastDumpTime;
+ private boolean mIsRunning;
}
diff --git a/third_party/cube/app/src/main/res/drawable-anydpi/ic_info_outline_white_24dp.xml b/third_party/cube/app/src/main/res/drawable-anydpi/ic_info_outline_white_24dp.xml
new file mode 100644
index 0000000..9567ecd
--- /dev/null
+++ b/third_party/cube/app/src/main/res/drawable-anydpi/ic_info_outline_white_24dp.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M11,17h2v-6h-2v6zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM11,9h2L13,7h-2v2z"/>
+</vector>
diff --git a/third_party/cube/app/src/main/res/drawable-anydpi/ic_info_solid_white_24dp.xml b/third_party/cube/app/src/main/res/drawable-anydpi/ic_info_solid_white_24dp.xml
new file mode 100644
index 0000000..f9e842c
--- /dev/null
+++ b/third_party/cube/app/src/main/res/drawable-anydpi/ic_info_solid_white_24dp.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-6h2v6zM13,9h-2L11,7h2v2z"/>
+</vector>
diff --git a/third_party/cube/app/src/main/res/drawable-anydpi/ic_settings_white_24dp.xml b/third_party/cube/app/src/main/res/drawable-anydpi/ic_settings_white_24dp.xml
new file mode 100644
index 0000000..ce997a7
--- /dev/null
+++ b/third_party/cube/app/src/main/res/drawable-anydpi/ic_settings_white_24dp.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M19.43,12.98c0.04,-0.32 0.07,-0.64 0.07,-0.98s-0.03,-0.66 -0.07,-0.98l2.11,-1.65c0.19,-0.15 0.24,-0.42 0.12,-0.64l-2,-3.46c-0.12,-0.22 -0.39,-0.3 -0.61,-0.22l-2.49,1c-0.52,-0.4 -1.08,-0.73 -1.69,-0.98l-0.38,-2.65C14.46,2.18 14.25,2 14,2h-4c-0.25,0 -0.46,0.18 -0.49,0.42l-0.38,2.65c-0.61,0.25 -1.17,0.59 -1.69,0.98l-2.49,-1c-0.23,-0.09 -0.49,0 -0.61,0.22l-2,3.46c-0.13,0.22 -0.07,0.49 0.12,0.64l2.11,1.65c-0.04,0.32 -0.07,0.65 -0.07,0.98s0.03,0.66 0.07,0.98l-2.11,1.65c-0.19,0.15 -0.24,0.42 -0.12,0.64l2,3.46c0.12,0.22 0.39,0.3 0.61,0.22l2.49,-1c0.52,0.4 1.08,0.73 1.69,0.98l0.38,2.65c0.03,0.24 0.24,0.42 0.49,0.42h4c0.25,0 0.46,-0.18 0.49,-0.42l0.38,-2.65c0.61,-0.25 1.17,-0.59 1.69,-0.98l2.49,1c0.23,0.09 0.49,0 0.61,-0.22l2,-3.46c0.12,-0.22 0.07,-0.49 -0.12,-0.64l-2.11,-1.65zM12,15.5c-1.93,0 -3.5,-1.57 -3.5,-3.5s1.57,-3.5 3.5,-3.5 3.5,1.57 3.5,3.5 -1.57,3.5 -3.5,3.5z"/>
+</vector>
diff --git a/third_party/cube/app/src/main/res/layout/activity_cube.xml b/third_party/cube/app/src/main/res/layout/activity_cube.xml
index bd48e0e..48234eb 100644
--- a/third_party/cube/app/src/main/res/layout/activity_cube.xml
+++ b/third_party/cube/app/src/main/res/layout/activity_cube.xml
@@ -1,12 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools"
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- tools:context="com.samples.cube.CubeActivity">
+ android:layout_height="match_parent">
+ <SurfaceView
+ android:id="@+id/surface_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+
+ <LinearLayout
+ android:id="@+id/info_overlay"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:padding="24dp"
+ android:orientation="vertical"
+ android:visibility="invisible">
+ <TextView
+ android:id="@+id/swappy_stats"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="6dp"
+ android:text="@string/swappy_stats"
+ android:textAppearance="@style/InfoTextMedium" />
+
+ <GridLayout
+ android:id="@+id/swappy_stats_grid"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:columnCount="7">
+ </GridLayout>
+ </LinearLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
@@ -43,10 +66,4 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
-
- <SurfaceView
- android:id="@+id/surface_view"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1" />
-</LinearLayout>
+</RelativeLayout>
diff --git a/third_party/cube/app/src/main/res/menu/menu_cube.xml b/third_party/cube/app/src/main/res/menu/menu_cube.xml
new file mode 100644
index 0000000..a9be6f3
--- /dev/null
+++ b/third_party/cube/app/src/main/res/menu/menu_cube.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item
+ android:id="@+id/info_overlay_button"
+ android:icon="@drawable/ic_info_outline_white_24dp"
+ android:title="@string/info_overlay"
+ android:showAsAction="ifRoom" />
+
+ <item
+ android:id="@+id/settings_item"
+ android:icon="@drawable/ic_settings_white_24dp"
+ android:title="@string/settings"
+ android:showAsAction="ifRoom" />
+
+</menu>
\ No newline at end of file
diff --git a/third_party/cube/app/src/main/res/values/colors.xml b/third_party/cube/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..a0126b1
--- /dev/null
+++ b/third_party/cube/app/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <color name="colorPrimary">#3F51B5</color>
+ <color name="colorPrimaryDark">#303F9F</color>
+ <color name="colorAccent">#FF4081</color>
+</resources>
\ No newline at end of file
diff --git a/third_party/cube/app/src/main/res/values/strings.xml b/third_party/cube/app/src/main/res/values/strings.xml
index 417ec4a..8a93995 100644
--- a/third_party/cube/app/src/main/res/values/strings.xml
+++ b/third_party/cube/app/src/main/res/values/strings.xml
@@ -17,4 +17,12 @@
<resources>
<string name="app_name">cube</string>
+ <string name="info_overlay">Info Overlay</string>
+ <string name="settings">Settings</string>
+ <string name="swappy_stats">Swappy Frame Stats</string>
+ <string name="idle_frames">Idle</string>
+ <string name="late_frames">Late</string>
+ <string name="offset_frames">Offset</string>
+ <string name="latency_frames">Latency</string>
+
</resources>
diff --git a/third_party/cube/app/src/main/res/values/styles.xml b/third_party/cube/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..cb3dda5
--- /dev/null
+++ b/third_party/cube/app/src/main/res/values/styles.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <style name="AppTheme" parent="@android:style/Theme.DeviceDefault.Light.DarkActionBar">
+ <!-- Customize your theme here. -->
+ <item name="android:colorPrimary">@color/colorPrimary</item>
+ <item name="android:colorPrimaryDark">@color/colorPrimaryDark</item>
+ <item name="android:colorAccent">@color/colorAccent</item>
+ </style>
+
+ <style name="InfoTextSmall" parent="@android:style/TextAppearance.Material.Small">
+ <item name="android:textColor">#FFF</item>
+ </style>
+
+ <style name="InfoTextMedium" parent="@android:style/TextAppearance.Material.Medium">
+ <item name="android:textColor">#FFF</item>
+ </style>
+
+</resources>
\ No newline at end of file
diff --git a/third_party/cube/build.gradle b/third_party/cube/build.gradle
index 4699ea7..d6751df 100644
--- a/third_party/cube/build.gradle
+++ b/third_party/cube/build.gradle
@@ -3,10 +3,10 @@
buildscript {
repositories {
google()
- jcenter()
+ mavenCentral()
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.3.2'
+ classpath 'com.android.tools.build:gradle:7.2.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
@@ -15,7 +15,7 @@
allprojects {
repositories {
google()
- jcenter()
+ mavenCentral()
}
}
diff --git a/third_party/cube/gradle.properties b/third_party/cube/gradle.properties
index aac7c9b..6dd0218 100644
--- a/third_party/cube/gradle.properties
+++ b/third_party/cube/gradle.properties
@@ -10,6 +10,7 @@
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
+android.useAndroidX=true
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
diff --git a/third_party/cube/gradle/wrapper/gradle-wrapper.jar b/third_party/cube/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..7454180
--- /dev/null
+++ b/third_party/cube/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/src/tuningfork/tools/TuningForkMonitor/gradle/wrapper/gradle-wrapper.properties b/third_party/cube/gradle/wrapper/gradle-wrapper.properties
similarity index 79%
copy from src/tuningfork/tools/TuningForkMonitor/gradle/wrapper/gradle-wrapper.properties
copy to third_party/cube/gradle/wrapper/gradle-wrapper.properties
index b360a52..669386b 100644
--- a/src/tuningfork/tools/TuningForkMonitor/gradle/wrapper/gradle-wrapper.properties
+++ b/third_party/cube/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,5 @@
-#Wed Nov 06 12:29:03 GMT 2019
-distributionBase=GRADLE_USER_HOME
-distributionPath=wrapper/dists
-zipStoreBase=GRADLE_USER_HOME
-zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/third_party/cube/gradlew b/third_party/cube/gradlew
index 9d82f78..744e882 100755
--- a/third_party/cube/gradlew
+++ b/third_party/cube/gradlew
@@ -1,4 +1,20 @@
-#!/usr/bin/env bash
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
##############################################################################
##
@@ -6,42 +22,6 @@
##
##############################################################################
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS=""
-
-APP_NAME="Gradle"
-APP_BASE_NAME=`basename "$0"`
-
-# Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD="maximum"
-
-warn ( ) {
- echo "$*"
-}
-
-die ( ) {
- echo
- echo "$*"
- echo
- exit 1
-}
-
-# OS specific support (must be 'true' or 'false').
-cygwin=false
-msys=false
-darwin=false
-case "`uname`" in
- CYGWIN* )
- cygwin=true
- ;;
- Darwin* )
- darwin=true
- ;;
- MINGW* )
- msys=true
- ;;
-esac
-
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
@@ -60,8 +40,49 @@
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MSYS* | MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
@@ -85,7 +106,7 @@
fi
# Increase the maximum file descriptors if we can.
-if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
@@ -105,10 +126,11 @@
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
-# For Cygwin, switch paths to Windows format before running java
-if $cygwin ; then
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
@@ -134,27 +156,30 @@
else
eval `echo args$i`="\"$arg\""
fi
- i=$((i+1))
+ i=`expr $i + 1`
done
case $i in
- (0) set -- ;;
- (1) set -- "$args0" ;;
- (2) set -- "$args0" "$args1" ;;
- (3) set -- "$args0" "$args1" "$args2" ;;
- (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
- (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
- (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
- (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
- (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
- (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
-# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
-function splitJvmOpts() {
- JVM_OPTS=("$@")
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
}
-eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
-JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+APP_ARGS=`save "$@"`
-exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/third_party/cube/gradlew.bat b/third_party/cube/gradlew.bat
index aec9973..ac1b06f 100644
--- a/third_party/cube/gradlew.bat
+++ b/third_party/cube/gradlew.bat
@@ -1,3 +1,19 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@@ -8,20 +24,23 @@
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS=
-
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto init
+if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@@ -35,7 +54,7 @@
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-if exist "%JAVA_EXE%" goto init
+if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
@@ -45,34 +64,14 @@
goto fail
-:init
-@rem Get command-line arguments, handling Windowz variants
-
-if not "%OS%" == "Windows_NT" goto win9xME_args
-if "%@eval[2+2]" == "4" goto 4NT_args
-
-:win9xME_args
-@rem Slurp the command line arguments.
-set CMD_LINE_ARGS=
-set _SKIP=2
-
-:win9xME_args_slurp
-if "x%~1" == "x" goto execute
-
-set CMD_LINE_ARGS=%*
-goto execute
-
-:4NT_args
-@rem Get arguments from the 4NT Shell from JP Software
-set CMD_LINE_ARGS=%$
-
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
diff --git a/third_party/cube/settings.gradle b/third_party/cube/settings.gradle
index e7b4def..44824b1 100644
--- a/third_party/cube/settings.gradle
+++ b/third_party/cube/settings.gradle
@@ -1 +1,5 @@
include ':app'
+include ':games-frame-pacing'
+project(":games-frame-pacing").projectDir = file("../../games-frame-pacing")
+include ':games-frame-pacing:extras'
+project(":games-frame-pacing:extras").projectDir = file("../../games-frame-pacing/extras")
diff --git a/third_party/tensorflow-lite/res/arm64-v8a/libtensorflow-lite.a b/third_party/tensorflow-lite/res/arm64-v8a/libtensorflow-lite.a
new file mode 100644
index 0000000..3324897
--- /dev/null
+++ b/third_party/tensorflow-lite/res/arm64-v8a/libtensorflow-lite.a
Binary files differ
diff --git a/third_party/tensorflow-lite/res/arm64-v8a/libtensorflowlite_jni.so b/third_party/tensorflow-lite/res/arm64-v8a/libtensorflowlite_jni.so
new file mode 100644
index 0000000..cccb81b
--- /dev/null
+++ b/third_party/tensorflow-lite/res/arm64-v8a/libtensorflowlite_jni.so
Binary files differ
diff --git a/third_party/tensorflow-lite/res/armeabi-v7a/libtensorflow-lite.a b/third_party/tensorflow-lite/res/armeabi-v7a/libtensorflow-lite.a
new file mode 100644
index 0000000..fc09d3d
--- /dev/null
+++ b/third_party/tensorflow-lite/res/armeabi-v7a/libtensorflow-lite.a
Binary files differ
diff --git a/third_party/tensorflow-lite/res/armeabi-v7a/libtensorflowlite_jni.so b/third_party/tensorflow-lite/res/armeabi-v7a/libtensorflowlite_jni.so
new file mode 100644
index 0000000..b46af8d
--- /dev/null
+++ b/third_party/tensorflow-lite/res/armeabi-v7a/libtensorflowlite_jni.so
Binary files differ
diff --git a/third_party/tensorflow-lite/res/x86/libtensorflow-lite.a b/third_party/tensorflow-lite/res/x86/libtensorflow-lite.a
new file mode 100644
index 0000000..3d41ac4
--- /dev/null
+++ b/third_party/tensorflow-lite/res/x86/libtensorflow-lite.a
Binary files differ
diff --git a/third_party/tensorflow-lite/res/x86/libtensorflowlite_jni.so b/third_party/tensorflow-lite/res/x86/libtensorflowlite_jni.so
new file mode 100644
index 0000000..79c440f
--- /dev/null
+++ b/third_party/tensorflow-lite/res/x86/libtensorflowlite_jni.so
Binary files differ
diff --git a/third_party/tensorflow-lite/res/x86_64/libtensorflow-lite.a b/third_party/tensorflow-lite/res/x86_64/libtensorflow-lite.a
new file mode 100644
index 0000000..c05fa33
--- /dev/null
+++ b/third_party/tensorflow-lite/res/x86_64/libtensorflow-lite.a
Binary files differ
diff --git a/third_party/tensorflow-lite/res/x86_64/libtensorflowlite_jni.so b/third_party/tensorflow-lite/res/x86_64/libtensorflowlite_jni.so
new file mode 100644
index 0000000..66d07b7
--- /dev/null
+++ b/third_party/tensorflow-lite/res/x86_64/libtensorflowlite_jni.so
Binary files differ