This commit is contained in:
xsl
2025-09-04 10:54:47 +08:00
commit 6bc8f61b18
1808 changed files with 208268 additions and 0 deletions
+206
View File
@@ -0,0 +1,206 @@
# Copyright (c) 2019-2024, Arm Limited and Contributors
#
# SPDX-License-Identifier: Apache-2.0
#
# 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.16)
include(android_package)
# create sample app project
project(vulkan_samples LANGUAGES C CXX)
if(IOS AND CMAKE_VERSION VERSION_LESS 3.20)
message(FATAL_ERROR "Configuring iOS apps requires a minimum CMake version of 3.20")
elseif(APPLE AND NOT IOS AND CMAKE_VERSION VERSION_LESS 3.17)
message(FATAL_ERROR "Configuring Xcode for macOS requires a minimum CMake version of 3.17")
endif()
add_subdirectory(plugins)
add_subdirectory(apps)
set(SRC
main.cpp
)
source_group("\\" FILES ${SRC})
# select target type based on platform
if(ANDROID)
if(CMAKE_VS_NsightTegra_VERSION)
list(APPEND SRC ${CMAKE_CURRENT_SOURCE_DIR}/android/AndroidManifest.xml)
endif()
add_library(${PROJECT_NAME} SHARED ${SRC})
elseif(IOS)
list(REMOVE_AT SRC 0)
list(APPEND SRC
${CMAKE_CURRENT_SOURCE_DIR}/ios/main.mm
${CMAKE_CURRENT_SOURCE_DIR}/ios/AppDelegate.h
${CMAKE_CURRENT_SOURCE_DIR}/ios/AppDelegate.m
${CMAKE_CURRENT_SOURCE_DIR}/ios/ViewController.h
${CMAKE_CURRENT_SOURCE_DIR}/ios/ViewController.mm
)
add_executable(${PROJECT_NAME} MACOSX_BUNDLE ${SRC})
else()
add_executable(${PROJECT_NAME} WIN32 ${SRC})
endif()
target_link_libraries(${PROJECT_NAME} PRIVATE vkb__core vkb__filesystem apps plugins)
# Create android project
if(ANDROID)
if(CMAKE_VS_NsightTegra_VERSION)
set_property(TARGET ${PROJECT_NAME} PROPERTY ANDROID_GUI ON)
set_property(TARGET ${PROJECT_NAME} PROPERTY ANDROID_ASSETS_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/../assets)
set_property(TARGET ${PROJECT_NAME} PROPERTY ANDROID_JAVA_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../bldsys/android/java)
endif()
# Add packaging project only if not using CMake's toolchain
if(CMAKE_SYSTEM_VERSION GREATER 1)
add_android_package_project(
NAME ${PROJECT_NAME}_package
DEPENDS ${PROJECT_NAME}
ASSET_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/../assets
JAVA_DIR ${CMAKE_CURRENT_SOURCE_DIR}/android/java
RES_DIR ${CMAKE_CURRENT_SOURCE_DIR}/android/res
MANIFEST_FILE ${CMAKE_CURRENT_SOURCE_DIR}/android/AndroidManifest.xml)
endif()
# Sync assets and shaders
android_sync_folder(PATH ${CMAKE_CURRENT_SOURCE_DIR}/../assets)
android_sync_folder(PATH ${CMAKE_CURRENT_SOURCE_DIR}/../shaders)
endif()
# Create MSVC project
if(MSVC)
# Set VS startup project, working directory to project root, and command line arguments to default sample
set_property(DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ${PROJECT_NAME})
set_property(TARGET ${PROJECT_NAME} PROPERTY VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
set_property(TARGET ${PROJECT_NAME} PROPERTY VS_DEBUGGER_COMMAND_ARGUMENTS "sample hello_triangle")
#Configure output paths
foreach(CONFIG_TYPE ${CMAKE_CONFIGURATION_TYPES})
string(TOUPPER ${CONFIG_TYPE} SUFFIX)
string(TOLOWER ${CONFIG_TYPE} CONFIG_DIR)
set_target_properties(${PROJECT_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_${SUFFIX} ${CMAKE_CURRENT_BINARY_DIR}/bin/${CONFIG_DIR}/${TARGET_ARCH})
set_target_properties(${PROJECT_NAME} PROPERTIES LIBRARY_OUTPUT_DIRECTORY_${SUFFIX} ${CMAKE_CURRENT_BINARY_DIR}/lib/${CONFIG_DIR}/${TARGET_ARCH})
set_target_properties(${PROJECT_NAME} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY_${SUFFIX} ${CMAKE_CURRENT_BINARY_DIR}/lib/${CONFIG_DIR}/${TARGET_ARCH})
endforeach()
endif()
# Enable building from XCode for IOS
if(IOS)
set(CMAKE_MACOSX_BUNDLE YES)
if(NOT DEFINED MACOSX_BUNDLE_GUI_IDENTIFIER)
if(${CMAKE_OSX_SYSROOT} STREQUAL "iphoneos" AND DEFINED CMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM)
message(FATAL_ERROR "Building for iOS device with a specified development team requires setting MACOSX_BUNDLE_GUI_IDENTIFIER")
else()
set(MACOSX_BUNDLE_GUI_IDENTIFIER com.khronos.vulkansamples)
endif()
endif()
if(NOT DEFINED CMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM)
if(NOT DEFINED CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED)
if(${CMAKE_OSX_SYSROOT} STREQUAL "iphoneos")
set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "YES")
else()
set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO")
endif()
endif()
if(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED)
message(FATAL_ERROR "Building for iOS with code signing requires setting CMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM; note it is safe to build for iOS Simulator without code signing")
else()
message(STATUS "Building for iOS device or iOS Simulator without a code signing identity; turning off code signing")
set_target_properties(${PROJECT_NAME} PROPERTIES
XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO"
XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY "NO"
)
endif ()
else()
message(STATUS "Building for iOS device or iOS Simulator with a code signing identity; turning on code signing")
set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "YES")
set_target_properties(${PROJECT_NAME} PROPERTIES
XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "YES"
XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY "YES"
)
endif ()
# No need to search for Vulkan package or MoltenVK library, Vulkan cache variables already defined on Apple platforms by global_options.cmake
if(Vulkan_LIBRARY AND ${Vulkan_VERSION} VERSION_GREATER_EQUAL 1.3.278)
target_sources(${PROJECT_NAME} PRIVATE
${Vulkan_Target_SDK}/iOS/share/vulkan
)
set_source_files_properties(
${Vulkan_Target_SDK}/iOS/share/vulkan
PROPERTIES
MACOSX_PACKAGE_LOCATION Resources
)
endif ()
target_sources(${PROJECT_NAME} PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/../assets
${CMAKE_CURRENT_SOURCE_DIR}/../shaders
${CMAKE_CURRENT_SOURCE_DIR}/ios/launch.storyboard
${CMAKE_CURRENT_SOURCE_DIR}/ios/Storyboard.storyboard
)
set_source_files_properties(
${CMAKE_CURRENT_SOURCE_DIR}/ios/launch.storyboard
${CMAKE_CURRENT_SOURCE_DIR}/ios/Storyboard.storyboard
${CMAKE_CURRENT_SOURCE_DIR}/../assets
${CMAKE_CURRENT_SOURCE_DIR}/../shaders
PROPERTIES
MACOSX_PACKAGE_LOCATION Resources
)
set(FRAMEWORKS_TO_EMBED)
if(Vulkan_MoltenVK_LIBRARY)
list(APPEND FRAMEWORKS_TO_EMBED "${Vulkan_MoltenVK_LIBRARY};")
else()
message(FATAL_ERROR "Can't find MoltenVK library. Please install the Vulkan SDK or MoltenVK project and set VULKAN_SDK.")
endif()
if(Vulkan_LIBRARY)
list(APPEND FRAMEWORKS_TO_EMBED "${Vulkan_LIBRARY};")
endif()
if(Vulkan_Layer_VALIDATION)
# trouble is can't turn this on/off if XCode decides to build debug and we're configured for release. Need to revist
# note the Vulkan validation layer must be present and enabled even in release mode for the shader_debugprintf sample
#if(("${VKB_DEBUG}" STREQUAL "ON") OR ("${VKB_VALIDATION_LAYERS}" STREQUAL "ON"))
list(APPEND FRAMEWORKS_TO_EMBED "${Vulkan_Layer_VALIDATION}")
#endif()
endif()
set_target_properties(${PROJECT_NAME} PROPERTIES
MACOSX_BUNDLE_INFO_PLIST ${PROJECT_SOURCE_DIR}/ios/Info.plist
# These are already defined by the ios Info.plist file specified above
#MACOSX_BUNDLE_SHORT_VERSION_STRING 1.0.0
#MACOSX_BUNDLE_BUNDLE_VERSION 1.0.0
#MACOSX_BUNDLE_BUNDLE_NAME "vulkan samples"
)
set_target_properties(${PROJECT_NAME} PROPERTIES
XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER ${MACOSX_BUNDLE_GUI_IDENTIFIER}
XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES "YES"
XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/Frameworks"
XCODE_ATTRIBUTE_CODE_SIGN_STYLE "Automatic" # already default value
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "iPhone Developer"
XCODE_EMBED_FRAMEWORKS "${FRAMEWORKS_TO_EMBED}"
XCODE_EMBED_FRAMEWORKS_REMOVE_HEADERS_ON_COPY "YES"
XCODE_ATTRIBUTE_SKIP_INSTALL NO
XCODE_ATTRIBUTE_INSTALL_PATH "$(LOCAL_APPS_DIR)"
XCODE_ATTRIBUTE_DEAD_CODE_STRIPPING NO
XCODE_SCHEME_ARGUMENTS "sample hello_triangle"
)
elseif(APPLE)
# Set Xcode working directory to project root, and command line arguments to default sample
set_target_properties(${PROJECT_NAME} PROPERTIES
XCODE_SCHEME_WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
XCODE_SCHEME_ARGUMENTS "sample hello_triangle"
)
endif()
+59
View File
@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (c) 2019-2024, Arm Limited and Contributors
-
- SPDX-License-Identifier: Apache-2.0
-
- 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.
-
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:versionCode="1"
android:versionName="1.0">
<application android:label="@string/app_name"
android:debuggable="true"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:theme="@style/AppTheme"
tools:ignore="GoogleAppIndexingWarning,HardcodedDebugMode">
<activity android:name="com.khronos.vulkan_samples.SampleLauncherActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="com.khronos.vulkan_samples.NativeSampleActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:screenOrientation="fullSensor">
<meta-data android:name="android.app.lib_name"
android:value="@string/native_lib_name" />
</activity>
<provider
android:authorities="${applicationId}.provider"
android:name="androidx.core.content.FileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"/>
</provider>
</application>
</manifest>
Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

@@ -0,0 +1,152 @@
/* Copyright (c) 2019-2021, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* 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.khronos.vulkan_samples;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.fragment.app.DialogFragment;
import com.khronos.vulkan_samples.common.Utils;
import java.util.ArrayList;
import java.util.List;
public class FilterDialog extends DialogFragment {
// The adapter that is notified by a filter changing
private ViewPagerAdapter adapter;
// Saved raw filters that are used to update the adapter
private List<String> filters;
// Capitalised raw filters
private List<String> labels;
// Save state of the current applied filter
// This is used to initialise the filter every time it is opened
protected List<Boolean> values;
// Mutable copy of the saved state
// This is used when a dialog is open to track the state of the labels
// If a filter is applied values is replaced with this list; otherwise This is discarded
protected List<Boolean> alteredValues;
public void setup(ViewPagerAdapter adapter, List<String> filters) {
this.adapter = adapter;
this.filters = new ArrayList<>(filters);
this.labels = new ArrayList<>(filters.size());
this.values = new ArrayList<>(filters.size());
// Initialise labels only once
// Default set all filters to checked
for (int i = 0; i < filters.size(); ++i) {
this.labels.add(i, Utils.capitalize(filters.get(i)));
this.values.add(i, true);
}
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// Initial labels and values for the list of filters
final CharSequence[] labels = this.labels.toArray(new CharSequence[0]);
boolean[] values = Utils.toBoolArray(this.values);
// Initiate the mutable filter list
this.alteredValues = new ArrayList<>(this.values);
AlertDialog.Builder builder = new AlertDialog.Builder(getContext())
.setTitle("Filter Samples by Tag")
.setMultiChoiceItems(labels, values, new FilterMultiChoiceClickListener(this))
.setPositiveButton("Apply", new FilterClickListener(this));
return builder.create();
}
/**
* Notify the adapter that the current applied filter has changed
*/
protected void notifyAdapter() {
adapter.applyFilter(getFilter());
adapter.notifyDataSetChanged();
}
/**
* Get all raw filters that have been applied
*
* @return List of applied filters
*/
public List<String> getFilter() {
List<String> filters = new ArrayList<>();
for (int i = 0; i < this.filters.size(); i++) {
if (this.values.get(i)) {
filters.add(this.filters.get(i));
}
}
return filters;
}
}
/**
* Changes to a filter item's state must not persist if the dialog is closed without applying a filter.
* Update alteredValues, changes are discarded if the filter is not applied
*/
class FilterMultiChoiceClickListener implements DialogInterface.OnMultiChoiceClickListener {
private final FilterDialog dialog;
FilterMultiChoiceClickListener(FilterDialog dialog) {
this.dialog = dialog;
}
@Override
public void onClick(DialogInterface dialog, int which, boolean isChecked) {
if (which > this.dialog.alteredValues.size() || which < 0) {
// Which is not in the label array - should never happen
return;
}
this.dialog.alteredValues.set(which, isChecked);
}
}
/**
* When applying a filter override the current values and then notify the adapter that the current filter has been changed
*/
class FilterClickListener implements DialogInterface.OnClickListener {
private final FilterDialog dialog;
FilterClickListener(FilterDialog dialog) {
this.dialog = dialog;
}
@Override
public void onClick(DialogInterface dialog, int id) {
this.dialog.values = new ArrayList<>(this.dialog.alteredValues);
this.dialog.notifyAdapter();
}
}
@@ -0,0 +1,102 @@
/* Copyright (c) 2019-2024, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* 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.khronos.vulkan_samples;
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import androidx.core.app.ActivityCompat;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import androidx.core.content.FileProvider;
import android.view.View;
import com.google.androidgamesdk.GameActivity;
import com.khronos.vulkan_samples.common.Notifications;
import java.io.File;
import java.util.concurrent.Semaphore;
public class NativeSampleActivity extends GameActivity {
private Context context;
private final Semaphore semaphore = new Semaphore(0, true);
@Override
protected void onCreate(Bundle instance) {
super.onCreate(instance);
Notifications.initNotificationChannel(this);
context = this;
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus) {
View decorView = getWindow().getDecorView();
decorView.setSystemUiVisibility(
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY |
View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_FULLSCREEN);
}
}
/***
* Handle the application when an error is thrown and display a message
*/
public void fatalError(String message) {
final NativeSampleActivity activity = this;
ApplicationInfo applicationInfo = activity.getApplicationInfo();
this.runOnUiThread(new Runnable() {
public void run() {
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setTitle("Vulkan Samples");
builder.setMessage(message);
builder.setPositiveButton("Close", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
semaphore.release();
}
});
builder.setCancelable(false);
AlertDialog dialog = builder.create();
dialog.show();
}
});
try {
semaphore.acquire();
}
catch (InterruptedException e) { }
activity.finish();
}
}
@@ -0,0 +1,70 @@
/* Copyright (c) 2019-2021, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* 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.khronos.vulkan_samples;
import android.annotation.SuppressLint;
import android.content.Context;
import androidx.annotation.NonNull;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import com.khronos.vulkan_samples.model.Sample;
import java.util.List;
public class SampleArrayAdapter extends ArrayAdapter<Sample> {
SampleArrayAdapter(Context context, List<Sample> sample_list) {
super(context, R.layout.listview_item, sample_list);
}
/**
* Get a view for a position in the sample list
*
* @param position The position in the list
* @param view The view object that was previously at this position
* @param parent The parent view group
* @return A view object representing a given sample at a list position
*/
@NonNull
@SuppressLint("SetTextI18n")
@Override
public View getView(int position, View view, @NonNull ViewGroup parent) {
// Recycle view objects
if (view == null) {
view = LayoutInflater.from(getContext()).inflate(R.layout.listview_item, parent, false);
}
Sample sample = getItem(position);
assert sample != null;
TextView title = view.findViewById(R.id.title_text);
title.setText(sample.getName());
TextView description = view.findViewById(R.id.description_text);
description.setText(sample.getDescription());
TextView vendorTag = view.findViewById(R.id.vendor_text);
vendorTag.setText(sample.getTagText());
return view;
}
}
@@ -0,0 +1,336 @@
/* Copyright (c) 2019-2024, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* 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.khronos.vulkan_samples;
import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;
import androidx.appcompat.widget.Toolbar;
import com.khronos.vulkan_samples.common.Notifications;
import com.khronos.vulkan_samples.model.Permission;
import com.khronos.vulkan_samples.model.Sample;
import com.khronos.vulkan_samples.model.SampleStore;
import com.khronos.vulkan_samples.views.PermissionView;
import com.khronos.vulkan_samples.views.SampleListView;
public class SampleLauncherActivity extends AppCompatActivity {
SampleListView sampleListView;
PermissionView permissionView;
private boolean isBenchmarkMode = false;
private boolean isHeadless = false;
public SampleStore samples;
// Required sample permissions
List<Permission> permissions;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
if (loadNativeLibrary(getResources().getString(R.string.native_lib_name))) {
// Get sample info from cpp cmake generated file
samples = new SampleStore(Arrays.asList(getSamples()));
}
// Required Permissions
permissions = new ArrayList<>();
if (checkPermissions()) {
// Permissions previously granted skip requesting them
parseArguments();
showSamples();
} else {
// Chain request permissions
requestNextPermission();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.main_menu, menu);
return true;
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
MenuItem checkable = menu.findItem(R.id.menu_benchmark_mode);
checkable.setChecked(isBenchmarkMode);
checkable = menu.findItem(R.id.menu_headless);
checkable.setChecked(isHeadless);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if(item.getItemId() == R.id.filter_button) {
sampleListView.dialog.show(getSupportFragmentManager(), "filter");
return true;
} else if(item.getItemId() == R.id.menu_run_samples) {
String category = "";
ViewPagerAdapter adapter = ((ViewPagerAdapter) sampleListView.viewPager.getAdapter());
if (adapter != null) {
category = adapter.getCurrentFragment().getCategory();
}
List<String> arguments = new ArrayList<>();
arguments.add("batch");
arguments.add("--category");
arguments.add(category);
arguments.add("--tag");
arguments.addAll(sampleListView.dialog.getFilter());
String[] sa = {};
launchWithCommandArguments(arguments.toArray(sa));
return true;
} else if(item.getItemId() == R.id.menu_benchmark_mode) {
isBenchmarkMode = !item.isChecked();
item.setChecked(isBenchmarkMode);
return true;
} else if(item.getItemId() == R.id.menu_headless) {
isHeadless = !item.isChecked();
item.setChecked(isHeadless);
return true;
} else {
return super.onOptionsItemSelected(item);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
requestNextPermission();
}
/**
* Load a native library
*
* @param native_lib_name Native library to load
* @return True if loaded correctly; False otherwise
*/
private boolean loadNativeLibrary(String native_lib_name) {
boolean status = true;
try {
System.loadLibrary(native_lib_name);
} catch (UnsatisfiedLinkError e) {
Toast.makeText(getApplicationContext(), "Native code library failed to load.", Toast.LENGTH_SHORT).show();
status = false;
}
return status;
}
/**
* Chain request permissions from the required permission list. When called the
* next unrequested permission with be requested If all permission are requested
* but not all granted; requested states will be reset and the PermissionView
* will be shown
*/
public void requestNextPermission() {
boolean granted = true;
for (Permission permission : permissions) {
// Permission previously requested but not granted
if (!permission.granted(getApplication())) {
// Permission not previously requested - request it
if (!permission.isRequested()) {
permission.request(this);
return;
}
granted = false;
}
}
if (granted) {
parseArguments();
showSamples();
} else {
// Reset all permissions request state so they can be requested again
for (Permission permission : permissions) {
permission.setRequested(false);
}
showPermissionsMessage();
}
}
/**
* Check all required permissions have been granted
*
* @return True if all granted; False otherwise
*/
public boolean checkPermissions() {
for (Permission permission : permissions) {
if (!permission.granted(getApplicationContext())) {
return false;
}
}
return true;
}
/**
* Show/Create the Permission View. Hides all other views
*/
public void showPermissionsMessage() {
if (permissionView == null) {
permissionView = new PermissionView(this);
}
if (sampleListView != null) {
sampleListView.hide();
}
permissionView.show();
}
/**
* Show/Create the Sample View. Hides all other views
*/
public void showSamples() {
if (sampleListView == null) {
sampleListView = new SampleListView(this);
}
if (permissionView != null) {
permissionView.hide();
}
sampleListView.show();
}
// Allow Orientation locking for testing
@SuppressLint("SourceLockedOrientationActivity")
public void parseArguments() {
// Handle argument passing
Bundle extras = this.getIntent().getExtras();
if (extras != null) {
if (extras.containsKey("cmd")) {
String[] commands = null;
String[] command_arguments = extras.getStringArray("cmd");
String command = extras.getString("cmd");
if (command_arguments != null) {
commands = command_arguments;
} else if (command != null) {
commands = command.split("[ ]+");
}
if (commands != null) {
for (String cmd : commands) {
if (cmd.equals("test")) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
break;
}
}
launchWithCommandArguments(commands);
finishAffinity();
}
} else if (extras.containsKey("sample")) {
launchSample(extras.getString("sample"));
finishAffinity();
} else if (extras.containsKey("test")) {
launchTest(extras.getString("test"));
finishAffinity();
}
}
}
/**
* Set arguments of the Native Activity You may also use a String[]
*
* @param args A string array of arguments
*/
public void setArguments(String... args) {
List<String> arguments = new ArrayList<>(Arrays.asList(args));
if (isBenchmarkMode) {
arguments.add("--benchmark");
}
if (isHeadless) {
arguments.add("--headless_surface");
}
String[] argArray = new String[arguments.size()];
sendArgumentsToPlatform(arguments.toArray(argArray));
}
public void launchWithCommandArguments(String... args) {
setArguments(args);
Intent intent = new Intent(SampleLauncherActivity.this, NativeSampleActivity.class);
startActivity(intent);
}
public void launchTest(String testID) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
launchWithCommandArguments("test", testID);
}
public void launchSample(String sampleID) {
Sample sample = samples.findByID(sampleID);
if (sample == null) {
Notifications.toast(this, "Could not find sample " + sampleID);
showSamples();
return;
}
launchWithCommandArguments("sample", sampleID);
}
/**
* Get samples from the Native Application
*
* @return A list of Samples that are currently installed in the Native
* Application
*/
private native Sample[] getSamples();
/**
* Set the arguments of the Native Application
*
* @param args The arguments that are to be passed to the app
*/
private native void sendArgumentsToPlatform(String[] args);
}
@@ -0,0 +1,104 @@
/* Copyright (c) 2019-2021, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* 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.khronos.vulkan_samples;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ListView;
import com.khronos.vulkan_samples.model.Sample;
import java.util.ArrayList;
import java.util.List;
public class TabFragment extends Fragment {
private String category;
private List<Sample> sampleList = new ArrayList<>();
private AdapterView.OnItemClickListener clickListener;
/**
* Create a new Tab Fragment Instance
*
* @param category The Title of the TabFragment
* @param sampleList The Samples that this tab will render
* @param clickListener The listener used when a item is clicked
* @return A new Tab Fragment
*/
public static Fragment getInstance(String category, List<Sample> sampleList, AdapterView.OnItemClickListener clickListener) {
Bundle bundle = new Bundle();
bundle.putString("category", category);
TabFragment tabFragment = new TabFragment();
tabFragment.setArguments(bundle);
tabFragment.setSampleList(sampleList);
tabFragment.setClickListener(clickListener);
return tabFragment;
}
/**
* Set the sample list that the fragment should render
*
* @param list A list of samples
*/
public void setSampleList(List<Sample> list) {
this.sampleList = list;
}
/**
* Set the action to perform when a sample list item is clicked
*
* @param clickListener A OnItemClickListener
*/
public void setClickListener(AdapterView.OnItemClickListener clickListener) {
this.clickListener = clickListener;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
assert getArguments() != null;
category = getArguments().getString("category");
setRetainInstance(true);
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_tab, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ListView listView = view.findViewById(R.id.sample_list);
SampleArrayAdapter sampleArrayAdapter = new SampleArrayAdapter(view.getContext(), sampleList);
listView.setAdapter(sampleArrayAdapter);
listView.setOnItemClickListener(this.clickListener);
}
public String getCategory() {
return category;
}
}
@@ -0,0 +1,105 @@
/* Copyright (c) 2019-2021, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* 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.khronos.vulkan_samples;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentStatePagerAdapter;
import android.view.ViewGroup;
import android.widget.AdapterView;
import com.khronos.vulkan_samples.model.Sample;
import com.khronos.vulkan_samples.model.SampleStore;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public class ViewPagerAdapter extends FragmentStatePagerAdapter {
private TabFragment currentFragment;
private List<Sample> viewableSamples = new ArrayList<>();
private final SampleStore samples;
private final AdapterView.OnItemClickListener clickListener;
public ViewPagerAdapter(FragmentManager manager, SampleStore samples, AdapterView.OnItemClickListener clickListener) {
super(manager);
this.samples = samples;
this.clickListener = clickListener;
applyFilter(samples.getTags());
}
public void setDialog(FilterDialog dialog) {
dialog.setup(this, samples.getTags());
}
/**
* Modifies the filter so that the viewableSamples updates
*/
public void applyFilter(List<String> filterTags) {
viewableSamples = new ArrayList<>(samples.getByTags(filterTags));
}
@NonNull
@Override
public Fragment getItem(int position) {
List<Sample> fragmentSamples = new ArrayList<>();
String category = samples.getCategories().get(position);
assert category != null;
for (Sample sample : Objects.requireNonNull(samples.getByCategory(category))) {
for (Sample viewableSample : this.viewableSamples) {
if (sample.equals(viewableSample)) {
fragmentSamples.add(sample);
}
}
}
return TabFragment.getInstance(category, fragmentSamples, clickListener);
}
public int getItemPosition(@NonNull Object object) {
return POSITION_NONE;
}
@Override
public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
if (getCurrentFragment() != object) {
currentFragment = (TabFragment) object;
}
super.setPrimaryItem(container, position, object);
}
@Override
public int getCount() {
return samples.getCategoryIndex().size();
}
@Override
public CharSequence getPageTitle(int position) {
return samples.getCategories().get(position);
}
TabFragment getCurrentFragment() {
return currentFragment;
}
}
@@ -0,0 +1,95 @@
/* Copyright (c) 2021, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* 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.khronos.vulkan_samples.common;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import android.widget.Toast;
import android.content.Context;
import com.khronos.vulkan_samples.R;
public class Notifications {
private static final String NOTIFICATION_PREFS = "NOTIFICATION_PREFS";
private static final String NOTIFICATION_KEY = "NOTIFICATION_KEY";
public static final String CHANNEL_ID = "vkb";
private static int initialId = 0;
public static void initNotificationChannel(Context context) {
// Create Notification Channel API 26+
//
// API 26+ implements notifications using a channel where notifications are submitted to channel
// The following initialises the channel used by vkb to push notifications to.
// IMPORTANCE_HIGH = push notifications
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, context.getString(R.string.channel_name), NotificationManager.IMPORTANCE_HIGH);
channel.setDescription(context.getString(R.string.channel_desc));
NotificationManager nm = context.getSystemService(NotificationManager.class);
nm.createNotificationChannel(channel);
}
}
public static int getNotificationId(Context context) {
SharedPreferences prefs = context.getSharedPreferences(NOTIFICATION_PREFS, Context.MODE_PRIVATE);
int id = initialId;
if (prefs.contains(NOTIFICATION_KEY)) {
id = prefs.getInt(NOTIFICATION_KEY, initialId);
}
SharedPreferences.Editor editor = prefs.edit();
editor.putInt(NOTIFICATION_KEY, id + 1);
editor.apply();
return id;
}
/**
* Create a push notification from JNI with a message
*
* @param message A string of a message to be shown
*/
public static void notification(Context context, final String message) {
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(R.drawable.icon)
.setContentTitle("Vulkan Samples Error")
.setContentText(message)
.setStyle(new NotificationCompat.BigTextStyle()
.bigText(message))
.setPriority(NotificationCompat.PRIORITY_HIGH);
NotificationManagerCompat nm = NotificationManagerCompat.from(context);
nm.notify(getNotificationId(context), builder.build());
}
/**
* Create a toast from JNI on the android main UI thread with a message
*
* @param message A string of a message to be shown
*/
public static void toast(final Context context, final String message) {
new Handler(Looper.getMainLooper()).post(() -> Toast.makeText(context, message, Toast.LENGTH_LONG).show());
}
}
@@ -0,0 +1,96 @@
/* Copyright (c) 2021, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* 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.khronos.vulkan_samples.common;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class Utils {
/**
* A helper to capitalize a string
*
* @param str The string to be capitalized
* @return A capitalized string
*/
public static String capitalize(String str) {
return str.substring(0, 1).toUpperCase() + str.substring(1);
}
/**
* Convert List of Boolean to primitive boolean array
* @param list The list to be converted
* @return A primitive boolean array containing the lists values
*/
public static boolean[] toBoolArray(List<Boolean> list) {
boolean[] booleans = new boolean[list.size()];
for (int i = 0; i < list.size(); i++) {
booleans[i] = list.get(i);
}
return booleans;
}
/**
* Convert List of String to String array
* @param list The list to be converted
* @return A String array containing the lists values
*/
public static String[] toStringArray(List<String> list) {
String[] strings = new String[list.size()];
for (int i = 0; i < list.size(); i++) {
strings[i] = list.get(i);
}
return strings;
}
/**
* Comparator orders with a predefined list
*/
public static class PredefinedOrderComparator implements Comparator<String> {
private final List<String> defined_order;
public PredefinedOrderComparator(String... predefined_order) {
defined_order = Collections.unmodifiableList(Arrays.asList(predefined_order));
}
@Override
public int compare(String o1, String o2) {
// Remove duplicates
if (o1.equals(o2)) {
return 0;
}
int ret;
int o1_order = defined_order.indexOf(o1);
int o2_order = defined_order.indexOf(o2);
//if neither are found in the order list, then do natural sort - string comparison
if (o1_order < 0 && o2_order < 0) ret = o1.compareTo(o2);
//if only one is found in the order list, float it above the other
else if (o1_order < 0) ret = 1;
else if (o2_order < 0) ret = -1;
//if both are found in the order list, then do the index comparision
else ret = o1_order - o2_order;
return ret;
}
}
}
@@ -0,0 +1,91 @@
/* Copyright (c) 2021, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* 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.khronos.vulkan_samples.model;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
public class Permission {
private final String name;
private final int code;
private boolean requested = false;
public Permission(String name, int code) {
this.name = name;
this.code = code;
}
/**
* Set the requested state of the permission
*
* @param requested The desired requested state
*/
public void setRequested(boolean requested) {
this.requested = requested;
}
/**
* Query if the permission has already been requested
*
* @return True if previously requested; false otherwise
*/
public boolean isRequested() {
return requested;
}
/**
* Get the name of the permission
*
* @return The permissions name
*/
public String getName() {
return name;
}
/**
* Get the code of the permission
*
* @return The permissions code
*/
public int getCode() {
return code;
}
/**
* Query whether the permission has been granted for the App
*
* @param ctx The activity context that needs to be queried for the permission
* @return True if permission granted; false otherwise
*/
public boolean granted(Context ctx) {
return ContextCompat.checkSelfPermission(ctx, name) == PackageManager.PERMISSION_GRANTED;
}
/**
* Request the permission for a given Activity
*
* @param activity Activity to request the permission for
*/
public void request(Activity activity) {
ActivityCompat.requestPermissions(activity, new String[]{name}, code);
requested = true;
}
}
@@ -0,0 +1,164 @@
/* Copyright (c) 2019-2021, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* 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.khronos.vulkan_samples.model;
import android.text.TextUtils;
import com.khronos.vulkan_samples.common.Utils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
public class Sample implements Comparable<Sample> {
private final String id;
private final String category;
private final String author;
private final String name;
private final String description;
private String tagText;
private final List<String> tags;
public Sample(String id, String category, String author, String name, String description, String[] tags) {
this.id = id;
this.category = category;
this.author = author;
this.name = name;
this.description = description;
this.tags = new LinkedList<>(Arrays.asList(tags));
}
/**
* Generate the Tag Text that is shown in the sample list
*
* @return A concatenated string of a samples tags
*/
private String generateTagText() {
List<String> formatted_tags = new ArrayList<>(this.tags.size());
for (String tag : this.tags) {
formatted_tags.add(Utils.capitalize(tag));
}
// If the sample has a tag other than "Any" then "Any" can be removed
if (formatted_tags.size() > 1) {
formatted_tags.remove("Any");
}
return TextUtils.join(", ", formatted_tags);
}
/**
* Get the Sample's ID
*
* @return A Sample ID
*/
public String getId() {
return id;
}
/**
* Get the sample's category
*
* @return The sample's category
*/
public String getCategory() {
return category;
}
/**
* Get the sample's author
*
* @return The sample's author
*/
public String getAuthor() {
return author;
}
/**
* Get the sample's name
*
* @return The sample's name
*/
public String getName() {
return name;
}
/**
* Get the sample's description
*
* @return The sample's description
*/
public String getDescription() {
return description;
}
/**
* Get the sample's tags
*
* @return The sample's tags
*/
public List<String> getTags() {
return tags;
}
/**
* Get the sample's tag text
*
* @return The sample's tag text
*/
public String getTagText() {
if (tagText == null) {
tagText = generateTagText();
}
return tagText;
}
/**
* Checks whether two samples are equal
* Can be used with a HashMap
*
* @return True if samples are equal; false otherwise
*/
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (object instanceof Sample) {
Sample sample = (Sample) object;
return this.id.equals(sample.id);
}
return false;
}
/**
* Used when comparing two samples - Alphabetical Case Insensitive
* Sample id's are unique so this is used to compare
*
* @param sample To compare too
* @return - if less, 0 if equal + if greater than
*/
public int compareTo(Sample sample) {
return sample.id.compareToIgnoreCase(this.id);
}
}
@@ -0,0 +1,159 @@
/* Copyright (c) 2021, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* 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.khronos.vulkan_samples.model;
import com.khronos.vulkan_samples.common.Utils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
/**
* A one stop store of samples that are pre indexed for use in the App
*/
public class SampleStore {
private final Set<Sample> samples = new TreeSet<>();
private final Set<String> categories = new TreeSet<>(new Utils.PredefinedOrderComparator("api", "performance", "extensions"));
private final Map<String, List<Sample>> categoryIndex = new HashMap<>();
private final Set<String> tags = new TreeSet<>(new Utils.PredefinedOrderComparator("any"));
private final Map<String, List<Sample>> tagIndex = new HashMap<>();
public SampleStore(List<Sample> samples) {
if (samples == null) {
samples = new ArrayList<>();
}
// Index all samples by tag and by category
for (Sample sample : samples) {
if (sample == null) {
continue;
}
this.samples.add(sample);
if (!categoryIndex.containsKey(sample.getCategory())) {
categoryIndex.put(sample.getCategory(), new ArrayList<>());
}
List<Sample> category = categoryIndex.get(sample.getCategory());
if (category != null) {
category.add(sample);
}
for (String tag : sample.getTags()) {
if (!tagIndex.containsKey(tag)) {
tagIndex.put(tag, new ArrayList<>());
}
List<Sample> index = tagIndex.get(tag);
if (index != null) {
index.add(sample);
}
this.tags.add(tag);
}
this.categories.add(sample.getCategory());
}
}
/**
* Find a sample by its id
*
* @param id of a given sample
* @return A sample if it is found
*/
public Sample findByID(String id) {
for (Sample sample : samples) {
if (sample.getId().equals(id)) {
return sample;
}
}
return null;
}
/**
* Get the category index
*
* @return A map of category to samples
*/
public Map<String, List<Sample>> getCategoryIndex() {
return this.categoryIndex;
}
/**
* Get all category names
*
* @return A list of category names
*/
public List<String> getCategories() {
return new ArrayList<>(this.categories);
}
/**
* Get all tag names
*
* @return A list of tag names
*/
public List<String> getTags() {
return new ArrayList<>(this.tags);
}
/**
* Get a list of sample by tag name
*
* @return A list of samples
*/
public List<Sample> getByTag(String tag) {
return this.tagIndex.get(tag);
}
/**
* Get a list of sample by multiple tag names
*
* @return A list of samples
*/
public List<Sample> getByTags(List<String> tags) {
Set<Sample> samples = new HashSet<>();
for (String tag : tags) {
Collection<Sample> samplesByTag = getByTag(tag);
if (samplesByTag != null) {
samples.addAll(samplesByTag);
}
}
return new ArrayList<>(samples);
}
/**
* Get a list of sample by category name
*
* @return A list of samples
*/
public List<Sample> getByCategory(String category) {
return this.categoryIndex.get(category);
}
}
@@ -0,0 +1,70 @@
/* Copyright (c) 2021, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* 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.khronos.vulkan_samples.views;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.khronos.vulkan_samples.R;
import com.khronos.vulkan_samples.SampleLauncherActivity;
/**
* A container for all elements related to the permission view
*/
public class PermissionView {
private final Button buttonPermissions;
private final TextView textPermissions;
public PermissionView(SampleLauncherActivity activity) {
buttonPermissions = activity.findViewById(R.id.button_permissions);
buttonPermissions.setOnClickListener(new CheckPermissionClickListener(activity));
textPermissions = activity.findViewById(R.id.text_permissions);
}
/**
* Show the permission view
*/
public void show() {
buttonPermissions.setVisibility(View.VISIBLE);
textPermissions.setVisibility(View.VISIBLE);
}
/**
* Hide the permission view
*/
public void hide() {
buttonPermissions.setVisibility(View.INVISIBLE);
textPermissions.setVisibility(View.INVISIBLE);
}
}
/**
* Click listener for the Permission View button
*/
class CheckPermissionClickListener implements View.OnClickListener {
private final SampleLauncherActivity activity;
CheckPermissionClickListener(SampleLauncherActivity activity) {
this.activity = activity;
}
public void onClick(View v) {
activity.requestNextPermission();
}
}
@@ -0,0 +1,83 @@
/* Copyright (c) 2020-2021, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* 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.khronos.vulkan_samples.views;
import com.google.android.material.tabs.TabLayout;
import androidx.viewpager.widget.ViewPager;
import android.view.View;
import android.widget.AdapterView;
import com.khronos.vulkan_samples.FilterDialog;
import com.khronos.vulkan_samples.R;
import com.khronos.vulkan_samples.SampleLauncherActivity;
import com.khronos.vulkan_samples.ViewPagerAdapter;
import com.khronos.vulkan_samples.model.Sample;
/**
* A container for all elements related to the sample view
*/
public class SampleListView {
private final TabLayout tabLayout;
public ViewPager viewPager;
public FilterDialog dialog;
public SampleListView(SampleLauncherActivity activity) {
viewPager = activity.findViewById(R.id.viewpager);
ViewPagerAdapter adapter = new ViewPagerAdapter(activity.getSupportFragmentManager(), activity.samples, new SampleItemClickListener(activity));
viewPager.setAdapter(adapter);
dialog = new FilterDialog();
adapter.setDialog(dialog);
tabLayout = activity.findViewById(R.id.tabs);
tabLayout.setupWithViewPager(viewPager);
}
/**
* Show the sample view
*/
public void show() {
tabLayout.setVisibility(View.VISIBLE);
viewPager.setVisibility(View.VISIBLE);
}
/**
* Hide the sample view
*/
public void hide() {
tabLayout.setVisibility(View.INVISIBLE);
viewPager.setVisibility(View.INVISIBLE);
}
}
/**
* Click listener for a Sample List Item
* Start the Native Activity for the clicked Sample
*/
class SampleItemClickListener implements AdapterView.OnItemClickListener {
private final SampleLauncherActivity activity;
SampleItemClickListener(SampleLauncherActivity activity) {
this.activity = activity;
}
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
String sampleID = ((Sample) parent.getItemAtPosition(position)).getId();
activity.launchSample(sampleID);
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

+74
View File
@@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (c) 2019-2021, Arm Limited and Contributors
-
- SPDX-License-Identifier: Apache-2.0
-
- 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.
-
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:layout_scrollFlags="scroll|enterAlways"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:titleTextAppearance="@style/Toolbar.TitleText"/>
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabGravity="fill"
app:tabMode="fixed"/>
</com.google.android.material.appbar.AppBarLayout>
<androidx.viewpager.widget.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="104dp"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
<TextView
android:id="@+id/text_permissions"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:padding="50dp"
android:visibility="invisible"
android:text="@string/permissions_text" />
<Button
android:id="@+id/button_permissions"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:layout_alignBottom="@+id/text_permissions"
android:visibility="invisible"
android:text="@string/permissions_button" />
</RelativeLayout>
+39
View File
@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (c) 2019-2020, Arm Limited and Contributors
-
- SPDX-License-Identifier: Apache-2.0
-
- 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.
-
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:padding="10dp"
android:textSize="40sp"
android:textStyle="bold"/>
<ListView
android:id="@+id/sample_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true" />
</RelativeLayout>
+64
View File
@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (c) 2019-2020, Arm Limited and Contributors
-
- SPDX-License-Identifier: Apache-2.0
-
- 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.
-
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/title_text"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:paddingLeft="10dip"
android:paddingTop="10dip"
android:textSize="16dip"
android:textStyle="bold"
tools:text="A Sample Title">
</TextView>
<TextView
android:id="@+id/description_text"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_below="@id/title_text"
android:paddingLeft="10dip"
android:paddingBottom="10dip"
android:textSize="14dip"
android:textStyle="italic"
tools:text="A Sample Description">
</TextView>
<TextView
android:id="@+id/vendor_text"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:gravity="right"
android:paddingTop="4dip"
android:paddingRight="10dip"
android:textColor="@color/colorPrimary"
android:textSize="12dip"
android:textStyle="italic"
tools:text="Tag, Tag">
</TextView>
</RelativeLayout>
+37
View File
@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (c) 2019, Arm Limited and Contributors
-
- SPDX-License-Identifier: Apache-2.0
-
- 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.
-
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/filter_button"
android:title="@string/filter_button_text" />
<item
android:id="@+id/menu_benchmark_mode"
android:checkable="true"
android:title="@string/menu_benchmark_mode_title"/>
<item
android:id="@+id/menu_headless"
android:checkable="true"
android:title="@string/menu_headless_title" />
<item
android:id="@+id/menu_run_samples"
android:title="@string/menu_run_samples_title"/>
</menu>
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (c) 2019, Arm Limited and Contributors
-
- SPDX-License-Identifier: Apache-2.0
-
- 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.
-
-->
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (c) 2019, Arm Limited and Contributors
-
- SPDX-License-Identifier: Apache-2.0
-
- 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.
-
-->
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

+24
View File
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (c) 2019, Arm Limited and Contributors
-
- SPDX-License-Identifier: Apache-2.0
-
- 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.
-
-->
<resources>
<color name="colorPrimary">#008FBE</color>
<color name="colorPrimaryDark">#333E47</color>
<color name="colorAccent">#F26B21</color>
</resources>
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (c) 2019, Arm Limited and Contributors
-
- SPDX-License-Identifier: Apache-2.0
-
- 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.
-
-->
<resources>
<color name="ic_launcher_background">#000000</color>
</resources>
+32
View File
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (c) 2019-2023, Arm Limited and Contributors
-
- SPDX-License-Identifier: Apache-2.0
-
- 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.
-
-->
<resources>
<string name="app_name">Vulkan Samples</string>
<string name="channel_name">vkb_channel</string>
<string name="channel_desc">vkb_channel is a channel for the vulkan best practice application notifications</string>
<string name="native_lib_name">vulkan_samples</string>
<string name="permissions_text">Some permissions were not granted</string>
<string name="permissions_button">Grant Permissions</string>
<string name="menu_run_samples_title">Run All Samples</string>
<string name="open_file_with">Open File With</string>
<string name="menu_benchmark_mode_title">Benchmark Mode</string>
<string name="menu_headless_title">Headless</string>
<string name="filter_button_text">Filter</string>
</resources>
+39
View File
@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (c) 2019, Arm Limited and Contributors
-
- SPDX-License-Identifier: Apache-2.0
-
- 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.
-
-->
<resources>
<!-- Base application theme -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="buttonStyle">@style/ButtonStyle</item>
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
<style name="Toolbar.TitleText" parent="TextAppearance.Widget.AppCompat.Toolbar.Title">
<item name="android:textSize">16dip</item>
</style>
<style name="ButtonStyle" parent="Widget.AppCompat.Button">
<item name="android:textAllCaps">false</item>
</style>
</resources>
+22
View File
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (c) 2019, Arm Limited and Contributors
-
- SPDX-License-Identifier: Apache-2.0
-
- 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.
-
-->
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external_files" path="."/>
</paths>
+91
View File
@@ -0,0 +1,91 @@
# Copyright (c) 2020-2024, Arm Limited and Contributors
#
# SPDX-License-Identifier: Apache-2.0
#
# 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.16)
project(apps)
# Generate apps.cpp
set(APP_INFO_LIST)
set(SAMPLE_INCLUDE_FILES)
set(SAMPLE_INFO_LIST)
foreach(SAMPLE_ID ${TOTAL_SAMPLE_ID_LIST})
if ("${VKB_${SAMPLE_ID}}" AND TARGET ${SAMPLE_ID})
get_target_property(SAMPLE_CATEGORY ${SAMPLE_ID} SAMPLE_CATEGORY)
get_target_property(SAMPLE_AUTHOR ${SAMPLE_ID} SAMPLE_AUTHOR)
get_target_property(SAMPLE_NAME ${SAMPLE_ID} SAMPLE_NAME)
get_target_property(SAMPLE_DESCRIPTION ${SAMPLE_ID} SAMPLE_DESCRIPTION)
get_target_property(SAMPLE_TAGS ${SAMPLE_ID} SAMPLE_TAGS)
# Ensure we send in an empty C++ string as the vendor category, rather than a string with a space
set(INCLUDE_DIR ${SAMPLE_CATEGORY}/${SAMPLE_ID})
set(SAMPLE_TAGS_VECTOR)
list(JOIN SAMPLE_TAGS "\", \"" SAMPLE_TAGS_VECTOR)
list(APPEND SAMPLE_INCLUDE_FILES "#include \"${INCLUDE_DIR}/${SAMPLE_ID}.h\"")
list(APPEND SAMPLE_INFO_LIST "\tSampleInfo{\"${SAMPLE_ID}\", create_${SAMPLE_ID}, \"${SAMPLE_CATEGORY}\"\, \"${SAMPLE_AUTHOR}\"\, \"${SAMPLE_NAME}\"\, \"${SAMPLE_DESCRIPTION}\", {\"${SAMPLE_TAGS_VECTOR}\"}},")
list(APPEND APP_INFO_LIST "\tAppInfo{\"${SAMPLE_ID}\", create_${SAMPLE_ID}},")
endif()
endforeach()
list(JOIN SAMPLE_INCLUDE_FILES "\n" SAMPLE_INCLUDE_FILES)
list(JOIN SAMPLE_INFO_LIST "\n" SAMPLE_INFO_LIST)
list(JOIN APP_INFO_LIST "\n" APP_INFO_LIST)
configure_file(apps.cpp.in apps.cpp)
# Create apps library
set(SRC
apps.h
${CMAKE_CURRENT_BINARY_DIR}/apps.cpp
)
add_library(${PROJECT_NAME} STATIC ${SRC})
target_link_libraries(${PROJECT_NAME} PUBLIC framework)
target_include_directories(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_SOURCE_DIR}/samples)
# Link all samples
set(PROJECT_ID_LIST ${TOTAL_SAMPLE_ID_LIST})
foreach(ID ${PROJECT_ID_LIST})
if(TARGET ${ID})
get_target_property(TARGET_TYPE ${ID} TYPE)
if(TARGET_TYPE STREQUAL "OBJECT_LIBRARY" OR TARGET_TYPE STREQUAL "STATIC_LIBRARY")
target_link_libraries(${PROJECT_NAME} PUBLIC ${ID})
endif()
endif()
endforeach()
if (IOS)
target_link_libraries(${PROJECT_NAME} PRIVATE
"-framework UIKit"
"-framework Foundation"
"-framework CoreGraphics"
"-framework Metal"
"-framework QuartzCore"
"-framework IOKit"
"-framework IOSurface"
)
endif ()
+122
View File
@@ -0,0 +1,122 @@
/* Copyright (c) 2020-2024, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* 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.
*/
// Generated file by CMake. Don't edit.
#include "apps.h"
@SAMPLE_INCLUDE_FILES@
namespace apps
{
std::vector<AppInfo> apps = {
@APP_INFO_LIST@
};
std::vector<SampleInfo> samples = {
@SAMPLE_INFO_LIST@
};
AppInfo *get_app(const std::string &id)
{
for (auto &app : apps)
{
if (app.id == id)
{
return &app;
}
}
return nullptr;
}
std::vector<AppInfo *> get_apps()
{
std::vector<AppInfo *> app_ptrs;
for (auto &app : apps)
{
app_ptrs.push_back(&app);
}
return app_ptrs;
}
std::vector<AppInfo *> get_samples(const std::vector<std::string> &categories, const std::vector<std::string> &tags)
{
std::vector<AppInfo *> app_ptrs;
for (auto &app : samples)
{
app_ptrs.push_back(&app);
}
// No filters
if (categories.empty() && tags.empty())
{
return app_ptrs;
}
std::vector<AppInfo *> filtered;
for (auto app : app_ptrs)
{
auto *sample = reinterpret_cast<SampleInfo *>(app);
bool selected = false;
// Sample is in one of the desired categories
for (auto &category : categories)
{
if (category == sample->category)
{
selected = true;
break;
}
}
// Sample has one or more of the desired tags
for (auto &tag : tags)
{
for (auto &s_tag : sample->tags)
{
if (s_tag == tag)
{
selected = true;
break;
}
}
}
if (selected)
{
filtered.push_back(app);
}
}
return filtered;
}
SampleInfo * get_sample(const std::string &id) {
for (auto& sample : samples) {
if (sample.id == id) {
return &sample;
}
}
return nullptr;
}
} // namespace apps
+98
View File
@@ -0,0 +1,98 @@
/* Copyright (c) 2020-2024, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* 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 <functional>
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
#include "platform/application.h"
/*
*
* Vulkan Samples currently has two types of apps, Samples and Tests. These apps work from the same interface AppInfo.
* Samples and Tests are categorised into namespaces samples and tests respectively. Each name space has a method to retrieve
* the AppInfo interface version of the app (tests::get_apps, samples::get_apps).
*
*/
namespace apps
{
using CreateFunc = std::function<std::unique_ptr<vkb::Application>()>;
/*
* Apps - Used by Vulkan Samples to load a vkb::Applicaiton with the creation function (CreateFunc create).
*/
class AppInfo
{
public:
AppInfo(const std::string &id, const CreateFunc &create) :
id(id), create(create)
{}
std::string id;
CreateFunc create;
};
/*
* Samples - These are individual applications which show different usages and optimizations of the Vulkan API
*/
class SampleInfo : public AppInfo
{
public:
SampleInfo(const std::string &id, const CreateFunc &create, const std::string &category, const std::string &author, const std::string &name, const std::string &description, const std::vector<std::string> &tags = {}) :
AppInfo(id, create), category(category), author(author), name(name), description(description), tags(tags)
{}
std::string category;
std::string author;
std::string name;
std::string description;
std::vector<std::string> tags;
};
/**
* @brief Get a specific app
*
* @param id ID of a specific app
* @return const std::vector<AppInfo *>
*/
AppInfo *get_app(const std::string &id);
/**
* @brief Get all apps
*
* @return const std::vector<AppInfo *> A list of all apps
*/
std::vector<AppInfo *> get_apps();
/**
* @brief Get all samples
*
* @param categories If not empty the lists will include samples that match on of the categories requested
* @param tags If not empty the lists will include samples that match on of the tags requested
* @return std::vector<AppInfo *> A list of samples
*/
std::vector<AppInfo *> get_samples(const std::vector<std::string> &categories = {}, const std::vector<std::string> &tags = {});
SampleInfo *get_sample(const std::string &id);
} // namespace apps
+36
View File
@@ -0,0 +1,36 @@
/* Copyright (c) 2024, Holochip Inc.
*
* SPDX-License-Identifier: Apache-2.0
*
* 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 <TargetConditionals.h>
#if TARGET_OS_IOS
#import <UIKit/UIKit.h>
#else
#include <AppKit/AppKit.h>
#endif
#if TARGET_OS_IOS
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
#else
@interface AppDelegate : NSResponder <NSApplicationDelegate>
@property (strong, nonatomic) NSWindow *window;
#endif
@end
+55
View File
@@ -0,0 +1,55 @@
/* Copyright (c) 2024, Holochip Inc.
*
* SPDX-License-Identifier: Apache-2.0
*
* 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.
*/
#import "AppDelegate.h"
#if TARGET_OS_IOS
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
@end
#endif
+55
View File
@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2024, Holochip Inc.
SPDX-License-Identifier: Apache-2.0
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.
-->
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleGetInfoString</key>
<string></string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleLongVersionString</key>
<string></string>
<key>CFBundleShortVersionString</key>
<string>1.0.0</string>
<key>CFBundleVersion</key>
<string>1.0.0</string>
<key>CFBundleName</key>
<string>vulkan samples</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>UIMainStoryboardFile</key>
<string>Storyboard</string>
<key>UILaunchStoryboardName</key>
<string>launch</string>
<key>CSResourcesFileMapped</key>
<true/>
<key>NSHumanReadableCopyright</key>
<string></string>
</dict>
</plist>
+48
View File
@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2024, Holochip Inc.
SPDX-License-Identifier: Apache-2.0
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.
-->
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="Y6W-OH-hqX">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22684"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="s0d-6b-0kx">
<objects>
<viewController id="Y6W-OH-hqX" customClass="ViewController" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="5EZ-qb-Rvc" customClass="VulkanView">
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<viewLayoutGuide key="safeArea" id="vDu-zF-Fre"/>
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</view>
<connections>
<outlet property="vulkan_view" destination="5EZ-qb-Rvc" id="XCX-oU-713"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="Ief-a0-LHa" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-218" y="4"/>
</scene>
</scenes>
</document>
+21
View File
@@ -0,0 +1,21 @@
/* Copyright (c) 2024, Holochip Inc.
*
* SPDX-License-Identifier: Apache-2.0
*
* 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.
*/
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@end
+76
View File
@@ -0,0 +1,76 @@
/* Copyright (c) 2024, Holochip Inc.
*
* SPDX-License-Identifier: Apache-2.0
*
* 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 "ViewController.h"
#include "ios/context.hpp"
#include "core/platform/entrypoint.hpp"
#include "platform/ios/ios_platform.h"
#include "platform/ios/ios_window.h"
int platform_main(const vkb::PlatformContext &);
@interface ViewController(){
CADisplayLink* _displayLink;
std::unique_ptr<vkb::PlatformContext> context;
vkb::IosPlatformContext* _context;
vkb::ExitCode _code;
}
@property (retain, nonatomic) IBOutlet VulkanView *vulkan_view;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Convert incoming args to "C" argc/argv strings
NSArray *args = [[NSProcessInfo processInfo] arguments];
const char** argv = (const char**) alloca(sizeof(char*) * args.count);
for(unsigned int i = 0; i < args.count; i++) {
NSString *s = args[i];
argv[i] = s.UTF8String;
}
self.vulkan_view.contentScaleFactor = UIScreen.mainScreen.nativeScale;
context = create_platform_context((int)args.count, const_cast<char**>(argv));
_context = (vkb::IosPlatformContext*)context.get();
_context->view = self.vulkan_view;
_code = (vkb::ExitCode)platform_main(*context);
uint32_t fps = 60;
_displayLink = [CADisplayLink displayLinkWithTarget: self selector: @selector(renderLoop)];
[_displayLink setPreferredFramesPerSecond:fps];
[_displayLink addToRunLoop: NSRunLoop.currentRunLoop forMode: NSDefaultRunLoopMode];
}
-(void) renderLoop {
if(!_displayLink.isPaused && [UIApplication sharedApplication].applicationState == UIApplicationStateActive) {
if(_code == vkb::ExitCode::Success) {
_code = ((vkb::IosPlatform*)_context->userPlatform)->main_loop_frame();
}
else {
// On ExitCode error, remove displayLink from run loop and terminate any active sample or batch run
[_displayLink invalidate];
((vkb::IosPlatform*)_context->userPlatform)->terminate(_code);
// Not typically allowed for iOS apps, but exit here given this is an Xcode-controlled dev application
exit(0);
}
}
}
@end
+50
View File
@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2024, Holochip Inc.
SPDX-License-Identifier: Apache-2.0
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.
-->
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22684"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="s0d-6b-0kx">
<objects>
<viewController id="Y6W-OH-hqX" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="5EZ-qb-Rvc">
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<viewLayoutGuide key="safeArea" id="vDu-zF-Fre"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="Ief-a0-LHa" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="48" y="-2"/>
</scene>
</scenes>
<resources>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
</resources>
</document>
+58
View File
@@ -0,0 +1,58 @@
/* Copyright (c) 2019-2024, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* 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/util/logging.hpp"
#include "platform/platform.h"
#include "../plugins/plugins.h"
#include <core/platform/entrypoint.hpp>
#include <filesystem/filesystem.hpp>
#include <TargetConditionals.h>
#import <Foundation/Foundation.h>
#import "AppDelegate.h"
#include "platform/ios/ios_platform.h"
#include "ios/context.hpp"
#undef CUSTOM_MAIN
# define CUSTOM_MAIN(context_name) \
int platform_main(const vkb::PlatformContext &); \
int main(int argc, char *argv[]) \
{ \
NSString * appDelegateClassName; \
@autoreleasepool { \
appDelegateClassName = NSStringFromClass([AppDelegate class]); \
return UIApplicationMain(argc, argv, nil, appDelegateClassName); \
} \
} \
int platform_main(const vkb::PlatformContext &context_name)
std::unique_ptr<vkb::IosPlatform> platform;
CUSTOM_MAIN(context)
{
vkb::filesystem::init_with_context(context);
platform = std::make_unique<vkb::IosPlatform>(context);
auto code = platform->initialize(plugins::get_all());
((vkb::IosPlatformContext*)&context)->userPlatform = platform.get();
return (int)code;
}
+73
View File
@@ -0,0 +1,73 @@
/* Copyright (c) 2019-2024, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* 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/util/logging.hpp"
#include "platform/platform.h"
#include "plugins/plugins.h"
#include <core/platform/entrypoint.hpp>
#include <filesystem/filesystem.hpp>
#if defined(PLATFORM__MACOS)
# include <TargetConditionals.h>
#endif
#if defined(PLATFORM__ANDROID)
# include "platform/android/android_platform.h"
#elif defined(PLATFORM__WINDOWS)
# include "platform/windows/windows_platform.h"
#elif defined(PLATFORM__LINUX_D2D)
# include "platform/unix/unix_d2d_platform.h"
#elif defined(PLATFORM__LINUX) || defined(PLATFORM__MACOS) && !(TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE)
# include "platform/unix/unix_platform.h"
#elif defined(PLATFORM__MACOS) && (TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE)
# include "platform/ios/ios_platform.h"
#else
# error "Platform not supported"
#endif
CUSTOM_MAIN(context)
{
vkb::filesystem::init_with_context(context);
#if defined(PLATFORM__ANDROID)
vkb::AndroidPlatform platform{context};
#elif defined(PLATFORM__WINDOWS)
vkb::WindowsPlatform platform{context};
#elif defined(PLATFORM__LINUX_D2D)
vkb::UnixD2DPlatform platform{context};
#elif defined(PLATFORM__LINUX)
vkb::UnixPlatform platform{context, vkb::UnixType::Linux};
#elif defined(PLATFORM__MACOS) && !(TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE)
vkb::UnixPlatform platform{context, vkb::UnixType::Mac};
#elif (TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE)
vkb::IosPlatform platform{context};
#else
# error "Platform not supported"
#endif
auto code = platform.initialize(plugins::get_all());
if (code == vkb::ExitCode::Success)
{
code = platform.main_loop();
}
platform.terminate(code);
return 0;
}
+102
View File
@@ -0,0 +1,102 @@
#[[
Copyright (c) 2019-2023, Arm Limited and Contributors
SPDX-License-Identifier: Apache-2.0
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.16)
# Snake case to Pascal case helper
function(snake_case_to_pascal_case SNAKE PASCAL)
set(SNAKE_CASE ${SNAKE})
string(LENGTH "${SNAKE_CASE}" LEN)
string(REGEX MATCH "(^.)" FIRST_LETTER "${SNAKE_CASE}")
string(TOUPPER "${FIRST_LETTER}" FIRST_LETTER)
string(SUBSTRING "${SNAKE_CASE}" 1 ${LEN} REST)
set(SNAKE_CASE "${FIRST_LETTER}${REST}")
string(REGEX MATCH "_([a-zA-Z])[^_]+" HAS_UNDER_SCORES "${SNAKE_CASE}")
if(HAS_UNDER_SCORES)
while(true)
string(REGEX MATCH "_([a-zA-Z])" NEXT "${SNAKE_CASE}")
if(NEXT)
string(SUBSTRING "${NEXT}" 1 1 FIRST_LETTER)
string(TOUPPER "${FIRST_LETTER}" FIRST_LETTER)
string(REGEX REPLACE "${NEXT}" "${FIRST_LETTER}" SNAKE_CASE "${SNAKE_CASE}")
else()
break()
endif()
endwhile()
endif()
set(${PASCAL} ${SNAKE_CASE} PARENT_SCOPE)
endfunction()
# Plugins
file(GLOB PLUGINS_FILES CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/*")
set(PLUGINS)
foreach(DIR IN LISTS PLUGINS_FILES)
if (IS_DIRECTORY ${DIR})
string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/" "" PLUGIN ${DIR})
list(APPEND PLUGINS "${PLUGIN}")
endif()
endforeach()
# filter compiled plugins
if(NOT ${VKB_BUILD_TESTS})
list(REMOVE_ITEM PLUGINS "start_test")
endif()
# Generate plugins.cpp
set(PLUGIN_INCLUDE_FILES)
set(INIT_PLUGINS)
foreach(EXT_SNAKE IN LISTS PLUGINS)
message("-- Plugin `${EXT_SNAKE}` - BUILD")
snake_case_to_pascal_case("${EXT_SNAKE}" EXT_PASCAL)
list(APPEND PLUGIN_INCLUDE_FILES "#include \"${EXT_SNAKE}/${EXT_SNAKE}.h\"")
list(APPEND INIT_PLUGINS "\t\tADD_PLUGIN(${EXT_PASCAL})")
endforeach()
list(JOIN PLUGIN_INCLUDE_FILES "\n" PLUGIN_INCLUDE_FILES)
list(JOIN INIT_PLUGINS ";\n" INIT_PLUGINS)
set(INIT_PLUGINS "${INIT_PLUGINS};")
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/plugins.cpp.in ${CMAKE_CURRENT_BINARY_DIR}/plugins.cpp)
# Create plugins library
set(SRC_FILES
${CMAKE_CURRENT_BINARY_DIR}/plugins.cpp
plugins.h
)
foreach(PLUGIN IN LISTS PLUGINS)
list(APPEND SRC_FILES "${PLUGIN}/${PLUGIN}.h")
list(APPEND SRC_FILES "${PLUGIN}/${PLUGIN}.cpp")
endforeach()
add_library(plugins OBJECT ${SRC_FILES})
target_include_directories(plugins PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} $<TARGET_PROPERTY:apps,INTERFACE_INCLUDE_DIRECTORIES> $<TARGET_PROPERTY:framework,INTERFACE_INCLUDE_DIRECTORIES>)
target_compile_options(plugins PRIVATE $<TARGET_PROPERTY:apps,INTERFACE_COMPILE_OPTIONS> $<TARGET_PROPERTY:framework,INTERFACE_COMPILE_OPTIONS>)
target_compile_features(plugins PRIVATE $<TARGET_PROPERTY:apps,INTERFACE_COMPILE_FEATURES> $<TARGET_PROPERTY:framework,INTERFACE_COMPILE_FEATURES>)
target_compile_definitions(plugins PRIVATE $<TARGET_PROPERTY:apps,INTERFACE_COMPILE_DEFINITIONS> $<TARGET_PROPERTY:framework,INTERFACE_COMPILE_DEFINITIONS>)
+232
View File
@@ -0,0 +1,232 @@
/* Copyright (c) 2020-2025, Arm Limited and Contributors
* Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* 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 "batch_mode.h"
#include "vulkan_sample.h"
namespace plugins
{
BatchMode::BatchMode() :
BatchModeTags("Batch Mode",
"Run a collection of samples in sequence.",
{
vkb::Hook::OnUpdate,
vkb::Hook::OnAppError,
},
{{"batch", "Enable batch mode"}},
{{"category", "Filter samples by categories"},
{"duration", "The duration which a configuration should run for in seconds"},
{"skip", "Skip a sample by id"},
{"tag", "Filter samples by tags"},
{"wrap-to-start", "Once all configurations have run wrap to the start"}})
{
}
bool BatchMode::handle_command(std::deque<std::string> &arguments) const
{
assert(!arguments.empty());
if (arguments[0] == "batch")
{
arguments.pop_front();
return true;
}
return false;
}
bool BatchMode::handle_option(std::deque<std::string> &arguments)
{
assert(!arguments.empty() && (arguments[0].substr(0, 2) == "--"));
std::string option = arguments[0].substr(2);
if (option == "category")
{
if (arguments.size() < 2)
{
LOGE("Option \"category\" is missing the actual category!");
return false;
}
std::string category = arguments[1];
if (std::ranges::any_of(categories, [&category](auto const &c) { return c == category; }))
{
LOGW("Option \"category\" lists category \"{}\" multiple times!", category)
}
else
{
categories.push_back(category);
}
arguments.pop_front();
arguments.pop_front();
return true;
}
else if (option == "duration")
{
if (arguments.size() < 2)
{
LOGE("Option \"duration\" is missing the actual duration!");
return false;
}
duration = std::chrono::duration<float, vkb::Timer::Seconds>{std::stof(arguments[1])};
arguments.pop_front();
arguments.pop_front();
return true;
}
else if (option == "skip")
{
if (arguments.size() < 2)
{
LOGE("Option \"skip\" is missing the sample_id to skip!");
return false;
}
std::string sample_id = arguments[1];
if (!skips.insert(sample_id).second)
{
LOGW("Option \"skip\" lists sample_id \"{}\" multiple times!", sample_id)
}
arguments.pop_front();
arguments.pop_front();
return true;
}
else if (option == "tag")
{
if (arguments.size() < 2)
{
LOGE("Option \"tag\" is missing the actual to tag!");
return false;
}
std::string tag = arguments[1];
if (std::ranges::any_of(tags, [&tag](auto const &t) { return t == tag; }))
{
LOGW("Option \"tag\" lists tag \"{}\" multiple times!", tag)
}
else
{
tags.push_back(tag);
}
arguments.pop_front();
arguments.pop_front();
return true;
}
else if (option == "wrap-to-start")
{
wrap_to_start = true;
arguments.pop_front();
return true;
}
return false;
}
void BatchMode::trigger_command()
{
sample_list = apps::get_samples(categories, tags);
if (!skips.empty())
{
std::vector<apps::AppInfo *> filtered_list;
filtered_list.reserve(sample_list.size() - skips.size());
std::copy_if(
sample_list.begin(), sample_list.end(), std::back_inserter(filtered_list), [&](const apps::AppInfo *app) { return !skips.count(app->id); });
if (filtered_list.size() != sample_list.size())
{
sample_list.swap(filtered_list);
}
}
if (sample_list.empty())
{
LOGE("No samples found")
throw std::runtime_error{"Can not continue"};
}
sample_iter = sample_list.begin();
vkb::Window::OptionalProperties properties;
properties.resizable = false;
platform->set_window_properties(properties);
platform->disable_input_processing();
platform->force_render(true);
request_app();
}
void BatchMode::on_update(float delta_time)
{
elapsed_time += delta_time;
// When the runtime for the current configuration is reached, advance to the next config or next sample
if (elapsed_time >= duration.count())
{
elapsed_time = 0.0f;
// Only check and advance the config if the application is a vulkan sample
if (auto *vulkan_app = dynamic_cast<vkb::VulkanSampleC *>(&platform->get_app()))
{
auto &configuration = vulkan_app->get_configuration();
if (configuration.next())
{
configuration.set();
return;
}
}
// Cycled through all configs, load next app
load_next_app();
}
}
void BatchMode::on_app_error(const std::string &app_id)
{
// App failed, load next app
load_next_app();
}
void BatchMode::request_app()
{
LOGI("===========================================");
LOGI("Running {}", (*sample_iter)->id);
LOGI("===========================================");
platform->request_application((*sample_iter));
}
void BatchMode::load_next_app()
{
// Wrap it around to the start
++sample_iter;
if (sample_iter == sample_list.end())
{
if (wrap_to_start)
{
sample_iter = sample_list.begin();
}
else
{
platform->close();
return;
}
}
// App will be started before the next update loop
request_app();
}
} // namespace plugins
+70
View File
@@ -0,0 +1,70 @@
/* Copyright (c) 2020-2025, Arm Limited and Contributors
* Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* 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 <chrono>
#include <vector>
#include "apps.h"
#include "platform/plugins/plugin_base.h"
#include "timer.h"
using namespace std::chrono_literals;
namespace plugins
{
using BatchModeTags = vkb::PluginBase<vkb::tags::Entrypoint, vkb::tags::FullControl>;
/**
* @brief Batch Mode
*
* Run a subset of samples. The next sample in the set will start after the current sample being executed has finished. Using --wrap-to-start will start again from the first sample after the last sample is executed.
*
* Usage: vulkan_samples batch --duration 3 --category performance --tag arm
*
*/
class BatchMode : public BatchModeTags
{
public:
BatchMode();
virtual ~BatchMode() = default;
void on_update(float delta_time) override;
void on_app_error(const std::string &app_id) override;
bool handle_command(std::deque<std::string> &arguments) const override;
bool handle_option(std::deque<std::string> &arguments) override;
void trigger_command() override;
private:
void request_app();
void load_next_app();
private:
std::vector<std::string> categories;
std::chrono::duration<float, vkb::Timer::Seconds> duration = 3s;
float elapsed_time = 0.0f;
std::set<std::string> skips;
std::vector<apps::AppInfo *>::const_iterator sample_iter; // An iterator to the current batch mode sample info object
std::vector<apps::AppInfo *> sample_list; // The list of suitable samples to be run in conjunction with batch mode
std::vector<std::string> tags;
bool wrap_to_start = false;
};
} // namespace plugins
@@ -0,0 +1,68 @@
/* Copyright (c) 2020-2025, Arm Limited and Contributors
* Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* 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 "benchmark_mode.h"
#include "platform/platform.h"
namespace plugins
{
BenchmarkMode::BenchmarkMode() :
BenchmarkModeTags("Benchmark Mode",
"Log frame averages after running an app.",
{vkb::Hook::OnUpdate, vkb::Hook::OnAppStart, vkb::Hook::OnAppClose},
{},
{{"benchmark", "Enable benchmark mode"}})
{
}
bool BenchmarkMode::handle_option(std::deque<std::string> &arguments)
{
assert(!arguments.empty() && (arguments[0].substr(0, 2) == "--"));
std::string option = arguments[0].substr(2);
if (option == "benchmark")
{
// Whilst in benchmark mode fix the fps so that separate runs are consistently simulated
// This will effect the graph outputs of framerate
platform->force_simulation_fps(60.0f);
platform->force_render(true);
arguments.pop_front();
return true;
}
return false;
}
void BenchmarkMode::on_update(float delta_time)
{
elapsed_time += delta_time;
total_frames++;
}
void BenchmarkMode::on_app_start(const std::string &app_id)
{
elapsed_time = 0;
total_frames = 0;
LOGI("Starting Benchmark for {}", app_id);
}
void BenchmarkMode::on_app_close(const std::string &app_id)
{
LOGI("Benchmark for {} completed in {} seconds (ran {} frames, averaged {} fps)", app_id, elapsed_time, total_frames, total_frames / elapsed_time);
}
} // namespace plugins
@@ -0,0 +1,54 @@
/* Copyright (c) 2020-2025, Arm Limited and Contributors
* Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* 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 "platform/plugins/plugin_base.h"
namespace plugins
{
class BenchmarkMode;
using BenchmarkModeTags = vkb::PluginBase<BenchmarkMode, vkb::tags::Passive>;
/**
* @brief Benchmark Mode
*
* When enabled frame time statistics of a samples run will be printed to the console when an application closes. The simulation frame time (delta time) is also locked to 60FPS so that statistics can be compared more accurately across different devices.
*
* Usage: vulkan_samples sample afbc --benchmark
*
*/
class BenchmarkMode : public BenchmarkModeTags
{
public:
BenchmarkMode();
virtual ~BenchmarkMode() = default;
virtual void on_update(float delta_time) override;
virtual void on_app_start(const std::string &app_info) override;
virtual void on_app_close(const std::string &app_info) override;
bool handle_option(std::deque<std::string> &arguments) override;
private:
float elapsed_time = 0.0f;
uint32_t total_frames = 0;
};
} // namespace plugins
+56
View File
@@ -0,0 +1,56 @@
/* Copyright (c) 2022-2025, Arm Limited and Contributors
* Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* 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 "data_path.h"
#include "filesystem/filesystem.hpp"
namespace plugins
{
DataPath::DataPath() :
DataPathTags("Data Path Override",
"Specify the folder containing the sample data folders.",
{vkb::Hook::OnAppStart},
{},
{{"data-path", "Folder containing data files"}})
{
}
bool DataPath::handle_option(std::deque<std::string> &arguments)
{
assert(!arguments.empty() && (arguments[0].substr(0, 2) == "--"));
std::string option = arguments[0].substr(2);
if (option == "data-path")
{
if (arguments.size() < 2)
{
LOGE("Option \"data-path\" is missing the actual data path!");
return false;
}
std::string data_path = arguments[1];
auto fs = vkb::filesystem::get();
fs->set_external_storage_directory(data_path + "/");
arguments.pop_front();
arguments.pop_front();
return true;
}
return false;
}
} // namespace plugins
+44
View File
@@ -0,0 +1,44 @@
/* Copyright (c) 2022-2025, Arm Limited and Contributors
* Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* 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 "platform/plugins/plugin_base.h"
namespace plugins
{
using DataPathTags = vkb::PluginBase<vkb::tags::Passive>;
/**
* @brief Data path override
*
* Controls the root path used to find data files
*
* Usage: vulkan_sample sample afbc --data-path <folder>
*
*/
class DataPath : public DataPathTags
{
public:
DataPath();
virtual ~DataPath() = default;
bool handle_option(std::deque<std::string> &arguments) override;
};
} // namespace plugins
+56
View File
@@ -0,0 +1,56 @@
/* Copyright (c) 2021-2025, Arm Limited and Contributors
* Copyright (c) 2021-2025, Sascha Willems
* Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* 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 "file_logger.h"
#include "apps.h"
#include <fmt/format.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/spdlog.h>
namespace plugins
{
FileLogger::FileLogger() :
FileLoggerTags("File Logger", "Enable log output to a file.", {}, {}, {{"log-file", "Write log messages to the given file name"}})
{
}
bool FileLogger::handle_option(std::deque<std::string> &arguments)
{
assert(!arguments.empty() && (arguments[0].substr(0, 2) == "--"));
std::string option = arguments[0].substr(2);
if (option == "log-file")
{
if (arguments.size() < 2)
{
LOGE("Option \"log-file\" is missing the actual log file name!");
return false;
}
std::string log_file = arguments[1];
spdlog::default_logger()->sinks().push_back(std::make_shared<spdlog::sinks::basic_file_sink_mt>(log_file, true));
arguments.pop_front();
arguments.pop_front();
return true;
}
return false;
}
} // namespace plugins
+45
View File
@@ -0,0 +1,45 @@
/* Copyright (c) 2020-2025, Arm Limited and Contributors
* Copyright (c) 2021-2025, Sascha Willems
* Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* 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 "platform/plugins/plugin_base.h"
namespace plugins
{
using FileLoggerTags = vkb::PluginBase<vkb::tags::Passive>;
/**
* @brief File Logger
*
* Enables writing log messages to a file
*
* Usage: vulkan_sample --log-file filename.txt
*
*/
class FileLogger : public FileLoggerTags
{
public:
FileLogger();
virtual ~FileLogger() = default;
bool handle_option(std::deque<std::string> &arguments) override;
};
} // namespace plugins
+45
View File
@@ -0,0 +1,45 @@
/* Copyright (c) 2020-2025, Arm Limited and Contributors
* Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* 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 "force_close.h"
#include <iostream>
namespace plugins
{
ForceClose::ForceClose() :
ForceCloseTags("Force Close",
"Force the application to close if it has been halted before exiting",
{},
{},
{{"force-close", "Force the close of the application if halted before exiting"}})
{
}
bool ForceClose::handle_option(std::deque<std::string> &arguments)
{
assert(!arguments.empty() && (arguments[0].substr(0, 2) == "--"));
std::string option = arguments[0].substr(2);
if (option == "force-close")
{
arguments.pop_front();
return true;
}
return false;
}
} // namespace plugins
+49
View File
@@ -0,0 +1,49 @@
/* Copyright (c) 2020-2025, Arm Limited and Contributors
* Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* 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 "platform/plugins/plugin_base.h"
namespace plugins
{
class ForceClose;
// Passive behaviour
using ForceCloseTags = vkb::PluginBase<ForceClose, vkb::tags::Passive>;
/**
* @brief Force Close
*
* Force the close of the application if halted before exiting
*
* The plugin is used as a boolean with platform->using_plugin<ForceClose>();
*
* Usage: vulkan_sample sample afbc --force-close
*
*/
class ForceClose : public ForceCloseTags
{
public:
ForceClose();
virtual ~ForceClose() = default;
bool handle_option(std::deque<std::string> &arguments) override;
};
} // namespace plugins
+61
View File
@@ -0,0 +1,61 @@
/* Copyright (c) 2020-2025, Arm Limited and Contributors
* Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* 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 "fps_logger.h"
namespace plugins
{
FpsLogger::FpsLogger() :
FpsLoggerTags("FPS Logger", "Enable FPS logging.", {vkb::Hook::OnUpdate, vkb::Hook::OnAppStart}, {}, {{"log-fps", "Log FPS"}})
{
}
bool FpsLogger::handle_option(std::deque<std::string> &arguments)
{
assert(!arguments.empty() && (arguments[0].substr(0, 2) == "--"));
std::string option = arguments[0].substr(2);
if (option == "log-fps")
{
arguments.pop_front();
return true;
}
return false;
}
void FpsLogger::on_update(float delta_time)
{
if (!timer.is_running())
{
timer.start();
}
auto elapsed_time = static_cast<float>(timer.elapsed<vkb::Timer::Seconds>());
frame_count++;
if (elapsed_time > 0.5f)
{
auto fps = (frame_count - last_frame_count) / elapsed_time;
LOGI("FPS: {:.1f}", fps);
last_frame_count = frame_count;
timer.lap();
}
};
} // namespace plugins
+51
View File
@@ -0,0 +1,51 @@
/* Copyright (c) 2020-2025, Arm Limited and Contributors
* Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* 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 "platform/plugins/plugin_base.h"
namespace plugins
{
using FpsLoggerTags = vkb::PluginBase<vkb::tags::Passive>;
/**
* @brief FPS Logger
*
* Control when FPS should be logged. Declutters the log output by removing FPS logs when not enabled
*
* Usage: vulkan_sample sample afbc --log-fps
*
*/
class FpsLogger : public FpsLoggerTags
{
public:
FpsLogger();
virtual ~FpsLogger() = default;
void on_update(float delta_time) override;
bool handle_option(std::deque<std::string> &arguments) override;
private:
size_t frame_count = 0;
size_t last_frame_count = 0;
vkb::Timer timer;
};
} // namespace plugins
@@ -0,0 +1,57 @@
/* Copyright (c) 2023-2025, Sascha Willems
* Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* 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 "gpu_selection.h"
#include "core/instance.h"
#include <algorithm>
namespace plugins
{
GpuSelection::GpuSelection() :
GpuSelectionTags("GPU selection",
"A collection of flags to select the GPU to run the samples on",
{},
{},
{{"gpu", "Zero-based index of the GPU that the sample should use"}})
{
}
bool GpuSelection::handle_option(std::deque<std::string> &arguments)
{
assert(!arguments.empty() && (arguments[0].substr(0, 2) == "--"));
std::string option = arguments[0].substr(2);
if (option == "gpu")
{
if (arguments.size() < 2)
{
LOGE("Option \"gpu\" is missing the actual gpu index!");
return false;
}
uint32_t gpu_index = static_cast<uint32_t>(std::stoul(arguments[1]));
vkb::core::InstanceC::selected_gpu_index = gpu_index;
vkb::core::InstanceCpp::selected_gpu_index = gpu_index;
arguments.pop_front();
arguments.pop_front();
return true;
}
return false;
}
} // namespace plugins
+44
View File
@@ -0,0 +1,44 @@
/* Copyright (c) 2023-2025, Sascha Willems
* Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* 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 "platform/plugins/plugin_base.h"
namespace plugins
{
class GpuSelection;
using GpuSelectionTags = vkb::PluginBase<GpuSelection, vkb::tags::Passive>;
/**
* @brief GPU selection options
*
* Explicitly select a GPU to run the samples on
*
*/
class GpuSelection : public GpuSelectionTags
{
public:
GpuSelection();
virtual ~GpuSelection() = default;
bool handle_option(std::deque<std::string> &arguments) override;
};
} // namespace plugins
+52
View File
@@ -0,0 +1,52 @@
/* Copyright (c) 2020-2021, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* 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.
*/
// Generated file by CMake. Don't edit.
#include "plugins.h"
#include <memory>
@PLUGIN_INCLUDE_FILES@
namespace plugins
{
#define ADD_PLUGIN(name) \
plugins.emplace_back(std::make_unique<name>())
std::vector<vkb::Plugin *> get_all()
{
static bool once = true;
static std::vector<std::unique_ptr<vkb::Plugin>> plugins;
if (once) {
once = false;
@INIT_PLUGINS@
}
std::vector<vkb::Plugin *> ptrs;
ptrs.reserve(plugins.size());
for (auto &plugin : plugins)
{
ptrs.push_back(plugin.get());
}
return ptrs;
}
} // namespace plugins
+25
View File
@@ -0,0 +1,25 @@
/* Copyright (c) 2020-2021, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* 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 "platform/plugins/plugin.h"
namespace plugins
{
extern std::vector<vkb::Plugin *> get_all();
} // namespace plugins
+105
View File
@@ -0,0 +1,105 @@
/* Copyright (c) 2020-2025, Arm Limited and Contributors
* Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* 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 "screenshot.h"
#include <chrono>
#include <iomanip>
#include "rendering/render_context.h"
namespace plugins
{
Screenshot::Screenshot() :
ScreenshotTags("Screenshot",
"Save a screenshot of a specific frame",
{vkb::Hook::OnUpdate, vkb::Hook::OnAppStart, vkb::Hook::PostDraw},
{},
{{"screenshot", "Take a screenshot at a given frame"}, {"screenshot-output", "Declare an output name for the image"}})
{
}
bool Screenshot::handle_option(std::deque<std::string> &arguments)
{
assert(!arguments.empty() && (arguments[0].substr(0, 2) == "--"));
std::string option = arguments[0].substr(2);
if (option == "screenshot")
{
if (arguments.size() < 2)
{
LOGE("Option \"screenshot\" is missing the frame index to take a screenshot!");
return false;
}
frame_number = static_cast<uint32_t>(std::stoul(arguments[1]));
arguments.pop_front();
arguments.pop_front();
return true;
}
else if (option == "screenshot-output")
{
if (arguments.size() < 2)
{
LOGE("Option \"screenshot-output\" is missing the filename to store the screenshot!");
return false;
}
output_path = arguments[1];
output_path_set = true;
arguments.pop_front();
arguments.pop_front();
return true;
}
return false;
}
void Screenshot::on_update(float delta_time)
{
current_frame++;
}
void Screenshot::on_app_start(const std::string &name)
{
current_app_name = name;
current_frame = 0;
}
void Screenshot::on_post_draw(vkb::RenderContext &context)
{
if (current_frame == frame_number)
{
if (!output_path_set)
{
// Create generic image path. <app name>-<current timestamp>.png
auto timestamp = std::chrono::system_clock::now();
std::time_t now_tt = std::chrono::system_clock::to_time_t(timestamp);
std::tm tm = *std::localtime(&now_tt);
char buffer[30];
strftime(buffer, sizeof(buffer), "%G-%m-%d---%H-%M-%S", &tm);
std::stringstream stream;
stream << current_app_name << "-" << buffer;
output_path = stream.str();
}
screenshot(context, output_path);
}
}
} // namespace plugins
+59
View File
@@ -0,0 +1,59 @@
/* Copyright (c) 2020-2025, Arm Limited and Contributors
* Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* 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 "filesystem/legacy.h"
#include "platform/plugins/plugin_base.h"
namespace plugins
{
class Screenshot;
using ScreenshotTags = vkb::PluginBase<Screenshot, vkb::tags::Passive>;
/**
* @brief Screenshot
*
* Capture a screen shot of the last rendered image at a given frame. The output can also be named
*
* Usage: vulkan_sample sample afbc --screenshot 1 --screenshot-output afbc-screenshot
*
*/
class Screenshot : public ScreenshotTags
{
public:
Screenshot();
virtual ~Screenshot() = default;
void on_update(float delta_time) override;
void on_app_start(const std::string &app_info) override;
void on_post_draw(vkb::RenderContext &context) override;
bool handle_option(std::deque<std::string> &arguments) override;
private:
uint32_t current_frame = 0;
uint32_t frame_number;
std::string current_app_name;
bool output_path_set = false;
std::string output_path;
};
} // namespace plugins
@@ -0,0 +1,77 @@
/* Copyright (c) 2024-2025, Sascha Willems
* Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* 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 "shading_language_selection.h"
#include <algorithm>
#include "platform/application.h"
namespace plugins
{
ShadingLanguageSelection::ShadingLanguageSelection() :
ShadingLanguageSelectionTags("Shading language selection",
"A collection of flags to select shader from different shading languages (glsl, hlsl or slang)",
{},
{},
{{"shading-language", "Shading language to use (glsl, hlsl or slang)"}})
{
}
bool ShadingLanguageSelection::handle_option(std::deque<std::string> &arguments)
{
assert(!arguments.empty() && (arguments[0].substr(0, 2) == "--"));
std::string option = arguments[0].substr(2);
if (option == "shading-language")
{
if (arguments.size() < 2)
{
LOGE("Option \"shading-language\" is missing the actual shading language to use!");
return false;
}
// Make sure it's one of the supported shading languages
std::string shading_language = arguments[1];
std::transform(shading_language.begin(), shading_language.end(), shading_language.begin(), ::tolower);
if (shading_language == "glsl")
{
LOGI("Shading language selection: GLSL");
vkb::Application::set_shading_language(vkb::ShadingLanguage::GLSL);
}
else if (shading_language == "hlsl")
{
LOGI("Shading language selection: HLSL")
vkb::Application::set_shading_language(vkb::ShadingLanguage::HLSL);
}
else if (shading_language == "slang")
{
LOGI("Shading language selection: slang")
vkb::Application::set_shading_language(vkb::ShadingLanguage::SLANG);
}
else
{
LOGE("Invalid shading language selection, defaulting to glsl");
}
arguments.pop_front();
arguments.pop_front();
return true;
}
return false;
}
} // namespace plugins
@@ -0,0 +1,44 @@
/* Copyright (c) 2024-2025, Sascha Willems
* Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* 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 "platform/plugins/plugin_base.h"
namespace plugins
{
class ShadingLanguageSelection;
using ShadingLanguageSelectionTags = vkb::PluginBase<ShadingLanguageSelection, vkb::tags::Passive>;
/**
* @brief Shading language selection options
*
* Select what shading language to run the samples with (glsl, hlsl)
*
*/
class ShadingLanguageSelection : public ShadingLanguageSelectionTags
{
public:
ShadingLanguageSelection();
virtual ~ShadingLanguageSelection() = default;
bool handle_option(std::deque<std::string> &arguments) override;
};
} // namespace plugins
+99
View File
@@ -0,0 +1,99 @@
/* Copyright (c) 2020-2025, Arm Limited and Contributors
* Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* 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 "start_sample.h"
#include "apps.h"
namespace plugins
{
StartSample::StartSample() :
StartSampleTags("StartSample",
"A collection of flags to samples and apps.",
{},
{{"sample", "Run a specific sample"},
{"samples", "List available samples with descriptions"},
{"samples-oneline", "List available samples, one per line"}})
{
}
void StartSample::launch_sample(apps::SampleInfo const *sample) const
{
vkb::Window::OptionalProperties properties;
properties.title = "Vulkan Samples: " + sample->name;
platform->set_window_properties(properties);
platform->request_application(sample);
}
void StartSample::list_samples(bool one_per_line) const
{
auto samples = apps::get_samples();
LOGI("");
LOGI("Available Samples");
LOGI("");
for (auto *app : samples)
{
auto sample = reinterpret_cast<apps::SampleInfo *>(app);
if (one_per_line)
{
LOGI("{}", sample->id.c_str());
}
else
{
LOGI("{}", sample->name.c_str());
LOGI("\tid: {}", sample->id.c_str());
LOGI("\tdescription: {}", sample->description.c_str());
LOGI("");
}
}
platform->close();
}
bool StartSample::handle_command(std::deque<std::string> &arguments) const
{
assert(!arguments.empty());
if (arguments[0] == "sample")
{
if (arguments.size() < 2)
{
LOGE("Command \"sample\" is missing the actual sample_id to launch!");
return false;
}
auto *sample = apps::get_sample(arguments[1]);
if (!sample)
{
LOGE("Command \"sample\" is called with an unknown sample_id \"{}\"!", arguments[1]);
return false;
}
launch_sample(sample);
arguments.pop_front();
arguments.pop_front();
return true;
}
if ((arguments[0] == "samples") || (arguments[0] == "samples-oneline"))
{
list_samples(arguments[0] == "samples-oneline");
arguments.pop_front();
return true;
}
return false;
}
} // namespace plugins
+50
View File
@@ -0,0 +1,50 @@
/* Copyright (c) 2020-2025, Arm Limited and Contributors
* Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* 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 "platform/platform.h"
#include "platform/plugins/plugin_base.h"
namespace plugins
{
using StartSampleTags = vkb::PluginBase<vkb::tags::Entrypoint>;
/**
* @brief Start App
*
* Loads a given sample
*
* Usage: vulkan_sample sample afbc
*
* TODO: Could this be extended to allow configuring a sample from the command line? Currently options are set explicitly by the UI
*/
class StartSample : public StartSampleTags
{
public:
StartSample();
virtual ~StartSample() = default;
bool handle_command(std::deque<std::string> &arguments) const override;
private:
void launch_sample(apps::SampleInfo const *sample) const;
void list_samples(bool one_per_line) const;
};
} // namespace plugins
+54
View File
@@ -0,0 +1,54 @@
/* Copyright (c) 2020-2025, Arm Limited and Contributors
* Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* 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 "start_test.h"
#include "apps.h"
namespace plugins
{
StartTest::StartTest() :
StartTestTags("Tests", "A collection of flags to run tests.", {}, {{"test", "Run a specific test"}})
{
}
bool StartTest::handle_command(std::deque<std::string> &arguments) const
{
assert(!arguments.empty());
if (arguments[0] == "test")
{
if (arguments.size() < 2)
{
LOGE("Command \"test\" is missing the actual test_id to launch!");
return false;
}
auto *test = apps::get_app(arguments[1]);
if (!test)
{
LOGE("Command \"test\" is called with an unknown test_id \"{}\"!", arguments[1]);
return false;
}
platform->request_application(test);
arguments.pop_front();
arguments.pop_front();
return true;
}
return false;
}
} // namespace plugins
+44
View File
@@ -0,0 +1,44 @@
/* Copyright (c) 2020-2025, Arm Limited and Contributors
* Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* 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 "platform/plugins/plugin_base.h"
namespace plugins
{
using StartTestTags = vkb::PluginBase<vkb::tags::Entrypoint>;
/**
* @brief Start Test
*
* Start a given test. Used by system_test.py
*
* Usage: vulkan_sample test bonza
*
*/
class StartTest : public StartTestTags
{
public:
StartTest();
virtual ~StartTest() = default;
bool handle_command(std::deque<std::string> &arguments) const override;
};
} // namespace plugins
+61
View File
@@ -0,0 +1,61 @@
/* Copyright (c) 2020-2025, Arm Limited and Contributors
* Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* 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 "stop_after.h"
namespace plugins
{
StopAfter::StopAfter() :
StopAfterTags("Stop After X",
"A collection of flags to stop the running application after a set period.",
{vkb::Hook::OnUpdate},
{},
{{"stop-after-frame", "Stop the application after a certain number of frames"}})
{
}
bool StopAfter::handle_option(std::deque<std::string> &arguments)
{
assert(!arguments.empty() && (arguments[0].substr(0, 2) == "--"));
std::string option = arguments[0].substr(2);
if (option == "stop-after-frame")
{
if (arguments.size() < 2)
{
LOGE("Option \"stop-after-frame\" is missing the actual frame index to stop after!");
return false;
}
remaining_frames = static_cast<uint32_t>(std::stoul(arguments[1]));
arguments.pop_front();
arguments.pop_front();
return true;
}
return false;
}
void StopAfter::on_update(float delta_time)
{
remaining_frames--;
if (remaining_frames <= 0)
{
platform->close();
}
}
} // namespace plugins
+51
View File
@@ -0,0 +1,51 @@
/* Copyright (c) 2020-2025, Arm Limited and Contributors
* Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* 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 "platform/plugins/plugin_base.h"
namespace plugins
{
using StopAfterTags = vkb::PluginBase<vkb::tags::Stopping>;
/**
* @brief Stop After
*
* Stop the execution of the app after a specific frame.
*
* Usage: vulkan_sample sample afbc --stop-after-frame 100
*
* TODO: Add stop after duration
*
*/
class StopAfter : public StopAfterTags
{
public:
StopAfter();
virtual ~StopAfter() = default;
void on_update(float delta_time) override;
bool handle_option(std::deque<std::string> &arguments) override;
private:
uint32_t remaining_frames{0};
};
} // namespace plugins
@@ -0,0 +1,50 @@
/* Copyright (c) 2023-2025, Sascha Willems
* Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* 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 "user_interface_options.h"
#include <algorithm>
#include "gui.h"
namespace plugins
{
UserInterfaceOptions::UserInterfaceOptions() :
UserInterfaceOptionsTags("User interface options",
"A collection of flags to configure the user interface",
{},
{},
{{"hideui", "If flag is set, hides the user interface at startup"}})
{
}
bool UserInterfaceOptions::handle_option(std::deque<std::string> &arguments)
{
assert(!arguments.empty() && (arguments[0].substr(0, 2) == "--"));
std::string option = arguments[0].substr(2);
if (option == "hideui")
{
vkb::GuiC::visible = false;
vkb::GuiCpp::visible = false;
arguments.pop_front();
return true;
}
return false;
}
} // namespace plugins
@@ -0,0 +1,44 @@
/* Copyright (c) 2023-2025, Sascha Willems
* Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* 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 "platform/plugins/plugin_base.h"
namespace plugins
{
class UserInterfaceOptions;
using UserInterfaceOptionsTags = vkb::PluginBase<UserInterfaceOptions, vkb::tags::Passive>;
/**
* @brief User interface Options
*
* Configure the default user interface
*
*/
class UserInterfaceOptions : public UserInterfaceOptionsTags
{
public:
UserInterfaceOptions();
virtual ~UserInterfaceOptions() = default;
bool handle_option(std::deque<std::string> &arguments) override;
};
} // namespace plugins
@@ -0,0 +1,146 @@
/* Copyright (c) 2020-2025, Arm Limited and Contributors
* Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* 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 "window_options.h"
#include <algorithm>
#include "platform/platform.h"
#include "platform/window.h"
namespace plugins
{
WindowOptions::WindowOptions() :
WindowOptionsTags("Window Options",
"A collection of flags to configure window used when running the application. Implementation may differ between platforms",
{},
{},
{{"borderless", "Run in borderless mode"},
{"fullscreen", "Run in fullscreen mode"},
{"headless-surface", "Run in headless surface mode. A Surface and swap-chain is still created using VK_EXT_headless_surface."},
{"height", "Initial window height"},
{"stretch", "Stretch window to fullscreen (direct-to-display only)"},
{"vsync", "Force vsync {ON | OFF}. If not set samples decide how vsync is set"},
{"width", "Initial window width"}})
{
}
bool WindowOptions::handle_option(std::deque<std::string> &arguments)
{
assert(!arguments.empty() && (arguments[0].substr(0, 2) == "--"));
std::string option = arguments[0].substr(2);
vkb::Window::OptionalProperties properties;
if (option == "borderless")
{
properties.mode = vkb::Window::Mode::FullscreenBorderless;
platform->set_window_properties(properties);
arguments.pop_front();
return true;
}
else if (option == "fullscreen")
{
properties.mode = vkb::Window::Mode::Fullscreen;
platform->set_window_properties(properties);
arguments.pop_front();
return true;
}
else if (option == "headless-surface")
{
properties.mode = vkb::Window::Mode::Headless;
platform->set_window_properties(properties);
arguments.pop_front();
return true;
}
else if (option == "height")
{
if (arguments.size() < 2)
{
LOGE("Option \"height\" is missing the actual height!");
return false;
}
uint32_t height = static_cast<uint32_t>(std::stoul(arguments[1]));
if (height < platform->MIN_WINDOW_HEIGHT)
{
LOGD("[Window Options] {} is smaller than the minimum height {}, resorting to minimum height", height, platform->MIN_WINDOW_HEIGHT);
height = platform->MIN_WINDOW_HEIGHT;
}
properties.extent.height = height;
platform->set_window_properties(properties);
arguments.pop_front();
arguments.pop_front();
return true;
}
else if (option == "stretch")
{
properties.mode = vkb::Window::Mode::FullscreenStretch;
platform->set_window_properties(properties);
arguments.pop_front();
return true;
}
else if (option == "vsync")
{
if (arguments.size() < 2)
{
LOGE("Option \"vsync\" is missing the actual setting!");
return false;
}
std::string value = arguments[1];
std::transform(value.begin(), value.end(), value.begin(), ::tolower);
if (value == "on")
{
properties.vsync = vkb::Window::Vsync::ON;
}
else if (value == "off")
{
properties.vsync = vkb::Window::Vsync::OFF;
}
platform->set_window_properties(properties);
arguments.pop_front();
arguments.pop_front();
return true;
}
else if (option == "width")
{
if (arguments.size() < 2)
{
LOGE("Option \"width\" is missing the actual width!");
return false;
}
uint32_t width = static_cast<uint32_t>(std::stoul(arguments[1]));
if (width < platform->MIN_WINDOW_WIDTH)
{
LOGD("[Window Options] {} is smaller than the minimum width {}, resorting to minimum width", width, platform->MIN_WINDOW_WIDTH);
width = platform->MIN_WINDOW_WIDTH;
}
properties.extent.width = width;
platform->set_window_properties(properties);
arguments.pop_front();
arguments.pop_front();
return true;
}
return false;
}
} // namespace plugins
@@ -0,0 +1,46 @@
/* Copyright (c) 2020-2025, Arm Limited and Contributors
* Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* 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 "platform/plugins/plugin_base.h"
namespace plugins
{
class WindowOptions;
using WindowOptionsTags = vkb::PluginBase<WindowOptions, vkb::tags::Passive>;
/**
* @brief Window Options
*
* Configure the window used when running Vulkan Samples.
*
* Usage: vulkan_samples sample instancing --width 500 --height 500 --vsync OFF
*
*/
class WindowOptions : public WindowOptionsTags
{
public:
WindowOptions();
virtual ~WindowOptions() = default;
bool handle_option(std::deque<std::string> &arguments) override;
};
} // namespace plugins