Quantcast
Channel: Статьи Intel Developer Zone
Viewing all 357 articles
Browse latest View live

Использование базы данных с приложением Android*

$
0
0

Аннотация

В мобильных приложениях в ряде случаев может быть полезно использование простой внутренней базы данных, такой как SQLite. В этой статье мы рассматриваем API Android* SQLite и вспомогательные классы для создания и обслуживания баз данных. Мы обсудим создание и использование заранее заполненной базы данных, а также будем использовать SQLite для создания внутренней базы данных для образца ресторанного приложения.

Содержание

Аннотация
Обзор
Образец приложения для ресторана – Little Chef.
Использование существующей базы данных
Доступ к элементам меню из базы данных ресторана
Заключение

Обзор

В Android существует встроенная поддержка базы данных SQLite. Поддерживаются все функции SQLite, предоставляется API оболочки с совместимым интерфейсом. Подробные сведения см. по приведенной ниже ссылке.
http://developer.android.com/guide/topics/data/data-storage.html#db

API Android SQLite является типичным; разработчику следует реализовать всю обработку базы данных, включая создание, управление версиями, обновления базы данных и прочие настройки. Если нужно использовать заранее заполненную базу данных SQLite, требуется дополнительная настройка.

Приведенное ниже учебное руководство содержит подробные сведения по использованию стандартного API Android SQLite.
http://developer.android.com/training/basics/data-storage/databases.html

Прямое использование API Android SQLite может привести к написанию большого количества шаблонного кода. Существует несколько библиотек Android, помогающих упростить этот процесс. Они обладают и рядом других возможностей в дополнение к удобному и эффективному использованию баз данных SQLite в приложениях Android.

SQLiteAssetHelper - одна из таких библиотек. Эта библиотека популярна в сообществе разработчиков Android. Полные сведения см. на следующем веб-сайте: https://github.com/jgilfelt/android-sqlite-asset-helper

Образец приложения для ресторана — Little Chef.

Мы будем использовать образец приложения для ресторана (Little Chef), чтобы продемонстрировать использование базы данных SQLite и библиотеки SQLiteAssetHelper library.

Это приложение позволяет пользователю просматривать различные категории меню и выбирать различные элементы.


Figure 1: A Restaurant Sample App - Little Chef

Это приложение для ресторана может быть использовано поваром или пользователем для просмотра различных категорий и элементов меню. В образце приложения используется жест прокрутки для переключения между категориями, а при выборе какого-либо элемента в меню отображаются подробные сведения.

Для сохранения всех категорий меню и подробных сведений об элементах можно использовать базу данных SQLite. В дальнейших версиях приложения можно расширить базу данных, чтобы поддерживать другие виды данных приложения: данные о продажах, о программах лояльности клиентов, индивидуальные настройки для каждого пользователя и т.д.

Использование существующей базы данных

В зависимости от требований к приложению может потребоваться заранее заполнить базу данных приложения начальным набором данных. В случае с ресторанным приложением заранее заполняется базовый набор стандартных категорий меню и сведений о каждом элементе меню.

Android API (например, SQLiteOpenHelper) можно использовать для заполнения первоначального набора данных при создании и инициализации базы данных. Тем не менее такой подход не всегда оптимален, особенно при большом объеме набора данных. Кроме того, не рекомендуется использовать некоторые вызовы SQLiteOpenHelperв главном потоке. В зависимости от мощности устройства пользователи могут столкнуться с длительной инициализацией и значительными задержками в работе пользовательского интерфейса при запуске. Еще один возможный подход — заранее заполнить базу данных и упаковать ее в составе ресурсов приложения.

Для образца ресторанного приложения мы создали базу данных SQLite автономно, используя API программирования python для SQLite. Существуют и клиенты с графическим пользовательским интерфейсом, позволяющие вручную добавлять данные в базы данных SQLite и редактировать эти данные. Согласно рекомендации в документации к API Android SQLite, мы добавили столбец «_id» для уникальной идентификации каждой строки. Это будет полезно при реализации абстракций поставщика содержимого и адаптера.

Для доступа к базе данных SQLite из папки assets приложения с помощью интерфейсов API Android SQLite требуется скопировать файл базы данных из папки assets в папку, соответствующую пути к базе данных приложения. Ситуация дополнительно усложняется, если нужно реализовать поддержку обновления базы данных и управления версиями.

Для образца ресторанного приложения мы используем библиотеку SQLiteAssetHelper*, чтобы получить доступ к заранее заполненной базе данных, упакованной в составе ресурсов приложения. Подробные инструкции по использованию см. в документе README для библиотеки SQLiteAssetHelper.
https://github.com/jgilfelt/android-sqlite-asset-helper

Мы создали папку databases внутри папки assets и скопировали заранее заполненный файл restaurant.sqliteв папку databases. Подробные сведения см. в следующем фрагменте кода.


package com.example.restaurant;

import android.content.Context;

	import android.database.Cursor;

	import android.database.sqlite.SQLiteDatabase;

	import android.database.sqlite.SQLiteQueryBuilder;

import com.readystatesoftware.sqliteasset.SQLiteAssetHelper;

/**

	* Database handler for restaurant app.

	*/

	public class RestaurantDatabase extends SQLiteAssetHelper {

	     private static final String TAG = SQLiteAssetHelper.class.getSimpleName();

	     private static final String DATABASE_NAME = "restaurant.sqlite";

	     private static final int DATABASE_VERSION = 1;

     public interface TABLES {

	         String MENU = "menu";

	         String USER = "user";

	         String CUSTOMER = "customer";

	}

     public interface MenuColumns {

	         String CATEGORY = "category";

	         String NAME = "name";

	         String DESCRIPTION = "description";

	         String NUTRITION = "nutrition";

	          String PRICE = "price";

	          String IMAGENAME = "imagename";

	     }

     public RestaurantDatabase(Context context) {

	          super(context, DATABASE_NAME, null, DATABASE_VERSION);

	     }

     public Cursor getMenuItems() {

	          SQLiteDatabase db = getReadableDatabase();

	          SQLiteQueryBuilder qb = new SQLiteQueryBuilder();

     qb.setTables(TABLES.MENU);

	         Cursor c = qb.query(db, null, null, null, null, null, null);

	          c.moveToFirst();

         return c;

	     }

	}

Фрагмент кода 1. Использование заранее заполненной базы данных ++

Доступ к элементам меню из базы данных ресторана

При инициализации SQLiteAssetHelperавтоматически скопирует заранее заполненную базу данных ресторана из папки assets по соответствующему пути к базе данных. Последующие вызовы будут снова использовать этот экземпляр базы данных до тех пор, пока не будет запрошено обновление. В приведенном выше фрагменте кода показан метод getMenuItems, возвращающий указатели для всех элементов меню в базе данных.

В следующем фрагменте кода показано создание экземпляра базы данных и разбор элементов меню от указателя.


mDb = new RestaurantDatabase(this);

mMenuItems = new ArrayList<MenuItem>();

Set<String> categories = new HashSet<String>();


	Cursor c = mDb.getMenuItems();

		while (c.moveToNext()) {


		String category = c.getString(c.getColumnIndexOrThrow(RestaurantDatabase.MenuColumns.CATEGORY));

			categories.add(category);


	MenuItem menuItem = new MenuItem();


		menuItem.setCategory(category);


			menuItem.setName(c.getString(c.getColumnIndexOrThrow(RestaurantDatabase.MenuColumns.NAME)));


			menuItem.setDescription(c.getString(c.getColumnIndexOrThrow(RestaurantDatabase.MenuColumns.DESCRIPTION)));


			menuItem.setNutrition(c.getString(c.getColumnIndexOrThrow(RestaurantDatabase.MenuColumns.NUTRITION)));


			menuItem.setPrice(c.getString(c.getColumnIndexOrThrow(RestaurantDatabase.MenuColumns.PRICE)));


			menuItem.setImageName(c.getString(c.getColumnIndexOrThrow(RestaurantDatabase.MenuColumns.IMAGENAME)));

			mMenuItems.add(menuItem);


	}


     c.close();

mCategoryList = new ArrayList<String>(categories);

Фрагмент кода 2. Доступ к элементам меню из базы данных ++

Не рекомендуется обрабатывать доступ к базе данных в главном потоке. Можно использовать SQLiteAssetHelperдля добавления дополнительных абстракций, таких как интерфейс поставщика содержимого Android.

В зависимости от требованиях к приложению и сценариев использования можно добавить в ресторанное приложение и другие возможности: поддержку обновления базы данных, управление версиями и даже поддержку серверов. При серверной реализации внутренней базы данных мы можем использовать локальную базу данных SQLite в качестве временного кеша и для возможности автономной работы.

Заключение

В этой статье мы обсудили использование базы данных SQLite в приложениях Android. Мы обсудили API Android SQLite и вспомогательные классы. Мы использовали образец ресторанного приложения для демонстрации использования заранее заполненных баз данных с библиотекой SQLiteAssetHelper.

Об авторе

Ашок Эмани (Ashok Emani) работает инженером по программному обеспечению в отделе Intel Software and Services Group. В настоящее время он занимается проектами по масштабированию систем на основе процессоров Intel® Atom™.

Примечания

ИНФОРМАЦИЯ В ДАННОМ ДОКУМЕНТЕ ПРИВЕДЕНА ТОЛЬКО В ОТНОШЕНИИ ПРОДУКТОВ INTEL. ДАННЫЙ ДОКУМЕНТ НЕ ПРЕДОСТАВЛЯЕТ ЯВНОЙ ИЛИ ПОДРАЗУМЕВАЕМОЙ ЛИЦЕНЗИИ, ЛИШЕНИЯ ПРАВА ВОЗРАЖЕНИЯ ИЛИ ИНЫХ ПРАВ НА ИНТЕЛЛЕКТУАЛЬНУЮ СОБСТВЕННОСТЬ. КРОМЕ СЛУЧАЕВ, УКАЗАННЫХ В УСЛОВИЯХ И ПРАВИЛАХ ПРОДАЖИ ТАКИХ ПРОДУКТОВ, INTEL НЕ НЕСЕТ НИКАКОЙ ОТВЕТСТВЕННОСТИ И ОТКАЗЫВАЕТСЯ ОТ ЯВНЫХ ИЛИ ПОДРАЗУМЕВАЕМЫХ ГАРАНТИЙ В ОТНОШЕНИИ ПРОДАЖИ И/ИЛИ ИСПОЛЬЗОВАНИЯ СВОИХ ПРОДУКТОВ, ВКЛЮЧАЯ ОТВЕТСТВЕННОСТЬ ИЛИ ГАРАНТИИ ОТНОСИТЕЛЬНО ИХ ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННОЙ ЦЕЛИ, ОБЕСПЕЧЕНИЯ ПРИБЫЛИ ИЛИ НАРУШЕНИЯ КАКИХ-ЛИБО ПАТЕНТОВ, АВТОРСКИХ ПРАВ ИЛИ ИНЫХ ПРАВ НА ИНТЕЛЛЕКТУАЛЬНУЮ СОБСТВЕННОСТЬ.

КРОМЕ СЛУЧАЕВ, СОГЛАСОВАННЫХ INTEL В ПИСЬМЕННОЙ ФОРМЕ, ПРОДУКТЫ INTEL НЕ ПРЕДНАЗНАЧЕНЫ ДЛЯ ИСПОЛЬЗОВАНИЯ В СИТУАЦИЯХ, КОГДА ИХ НЕИСПРАВНОСТЬ МОЖЕТ ПРИВЕСТИ К ТРАВМАМ ИЛИ ЛЕТАЛЬНОМУ ИСХОДУ.

Корпорация Intel оставляет за собой право вносить изменения в технические характеристики и описания своих продуктов без предварительного уведомления. Проектировщики не должны полагаться на отсутствующие характеристики, а также характеристики с пометками «зарезервировано» или «не определено». Эти характеристики резервируются Intel для будущего использования, поэтому отсутствие конфликтов совместимости для них не гарантируется. Информация в данном документе может быть изменена без предварительного уведомления. Не используйте эту информацию в окончательном варианте дизайна.

Продукты, описанные в данном документе, могут содержать ошибки и неточности, из-за чего реальные характеристики продуктов могут отличаться от приведенных здесь. Уже выявленные ошибки могут быть предоставлены по запросу.

Перед размещением заказа получите последние версии спецификаций в региональном офисе продаж Intel или у местного дистрибьютора.

Копии документов с порядковым номером, ссылки на которые содержатся в этом документе, а также другую литературу Intel можно получить, позвонив по телефону 1-800-548-4725 либо на сайте: http://www.intel.com/design/literature.htm

Программное обеспечение и нагрузки, использованные в тестах производительности, могли быть оптимизированы для достижения высокой производительности на микропроцессорах Intel. Тесты производительности, такие как SYSmark* и MobileMark*, проводятся на определенных компьютерных системах, компонентах, программах, операциях и функциях. Любые изменения любого из этих элементов могут привести к изменению результатов. При выборе приобретаемых продуктов следует обращаться к другой информации и тестам производительности, в том числе к тестам производительности определенного продукта в сочетании с другими продуктами.

Данный документ и описываемое в нем программное обеспечение предоставляются по лицензии и могут использоваться и распространяться только согласно условиям лицензии.


Сборка собственных приложений Android* с помощью компилятора Intel(R) C++ в Android Studio*

$
0
0

Содержание:

Введение

Текущей версией Android Studio* на момент написания этой статьи являлась версия 0.5.2. Она выпускается как «ранняя предварительная версия». Поэтому поддержка Android Studio* компилятором Intel(R) C++ 14.0 для Android также реализована на уровне предварительной версии. Никаких дополнительных изменений и действий не требуется, чтобы собирать собственные приложения в Android Studio.

Компилятор Intel(R) C++ для Android (ICC) настраивается как компилятор по умолчанию для платформ назначения x86 в системе сборки NDK при установке. Android Studio использует Gradle в качестве системы сборки. На момент написания этой статьи среда Gradle вызывала систему сборки NDK в ходе процесса сборки. Поэтому ICC также является компилятором по умолчанию для платформ назначения x86 в Android Studio. Никакой дополнительной настройки Android Studio не нужно.

Приведенные ниже действия описывают создание нового собственного приложения в Android Studio, его сборку и запуск.

Требуемые программные компоненты

Для разработки собственных приложений в Android Studio требуются следующие программные компоненты:

  1. Oracle* JDK 6 6 или более поздней версии (Intel-64 JDK для систем Windows x64):
    1. Установите переменную среды JAVA_HOME, чтобы она указывала на корневую папку JDK6, например, JAVA_HOME=C:\Program Files\Java\jdk1.6.0_45
    2. Обновите переменную среды PATH, чтобы она указывала на папку JAVA_HOME/bin, при этом javah будет доступна из окна командной строки.
  2. Android Studio:
    1. Установите Android Studio и последние обновления. Для получения обновлений откройте Android Studio, выберите меню [Help] и [Check for Update...], затем установите обновления.
    2. Android SDK
      1. Установите Android SDK из Android Studio, щелкнув приведенный ниже значок. Обязательно установите Intel x86 Atom System Image для Android x.x.x:
    3. Создайте новое виртуальное устройство Android Virtual Device для ЦП Intel ATOM/ABI, щелкнув указанный ниже значок:
      1. Установите свойства AVD, как показано ниже:
  3. Android NDK r9d: исходите из того, что этот компонент установлен в папку [ndk-dir]
  4. Intel C++ Compiler 14.0 для Android, обновление 2: выберите папку [ndk-dir] для Android NDK r9d.
    1. При наличии нескольких установленных пакетов Android NDK см. инструкции в статье Интеграция компилятора Intel(R) C++ для Android* с несколькими Android NDK.

Использование компилятора Intel(R) C++ в Android Studio**

После установки компилятора Intel C++ Compiler 14.0 для Android, обновление 2, следующие цепочки инструментов устанавливаются в папку инструментов NDK r9d (C:\android-ndk-r9d\toolchains):

  • x86-icc
  • x86-icc14.0.n.mmm

После установки компилятором C/C++ по умолчанию будет компилятор Intel C++. Для использования компилятора Intel C++ в Android Studio дополнительных действий не требуется.

Поскольку среда Android Studio находится на этапе предварительной версии, при прямом импорте образцов из NDK могут возникнуть определенные затруднения. Мы создадим новый проект Android с вызовом собственной функции для демонстрации использования компилятора Intel C++.

  1. Создайте новый проект Android с собственным интерфейсом:

    1. Откройте Android Studio, создайте новый проект Android с параметрами по умолчанию, например
    2. Откройте app\src\main\java\MainActivity.java и добавьте нативную функцию, как показано ниже, в конце класса MainActivity:
      1. public native String getStringFromNative();
    3. Сборка проекта nativeDemo: выберите меню Build > Make Project чтобы приготовиться использовать javah.
    4. Откройте окно терминала с помощью меню View > Other Windows > Terminal, затем выполните команду javah для создания заголовка jni:
      1. В окне терминала перейдите во вложенную папку src\main:
        1. cd src\main
      2. ii. Запустите следующую команду javah (она создает файл заголовка com_example_nativedemo_app_MainActivit.h в папке src\main\jni):
        1. javah -d jni classpath C:\Android\android-studio\sdk\platforms\android-19\android.jar;..\..\build\classes\debug com.example.nativedemo.app.MainActivity
      3. Пример
    5. Щелкните правой кнопкой мыши на папке src, выберите Sycnhronize 'src'. Теперь файл заголовка com_example_nativedemo_app_MainActivit.h находится в папке src\main\jni.
  2. Добавьте нативный исходный код: main.c, dummy.c.

    1. Создайте main.c: выберите файл com_example_nativedemo_app_MainActivit.h, используйте копирование в буфер и вставку из буфера для создания нового файла main.c со следующим кодом:
      1. #include "com_example_nativedemo_app_MainActivity.h"
        
        JNIEXPORT jstring JNICALL Java_com_example_nativedemo_app_MainActivity_getStringTextFromNative
          (JNIEnv * env, jobject obj)
        {
          #ifdef __INTEL_COMPILER_UPDATE
            return (*env)->NewStringUTF(env, "Hello from Intel C++ !");
          #else
            return (*env)->NewStringUTF(env, "Hello from default C++ !");
          #endif
        }
    2. Создайте файл dummy.c: выберите файл main.c и при помощи копирования в буфер и вставки из буфера создайте еще один пустой файл dummy.c. Этот файл требуется только в Windows и, по-видимому, не будет нужен в финальной версии Android Studio.
    3. c. Теперь у нас в папке jni находится три файла: com_example_nativedemo_app_MainActivity.h, main.c, dummy.c:
  3. Задайте папку NDK в проекте.

    1. Откройте файл проекта local.properties и добавьте ndk.dir:
      1. sdk.dir=C\:/Android/android-studio/sdk
        ndk.dir=C\:/android-ndk-r9d
  4. Измените выходную библиотеку: измените используемую по умолчанию "libapp.so"на "myNativeTest.lib"

    1. Откройте build.gradle: добавьте следующий раздел ndk в defaultConfig, как показано ниже.
    2.     defaultConfig {
              minSdkVersion 18
              targetSdkVersion 19
              versionCode 1
              versionName "1.0"
              ndk {
                  moduleName "myNativeTest"
              }
          }
    3. Теперь соберите проект: выберите [Build > Make Project].Вы увидите выходные папки и файлы libmyNativeTest.so в папках app\build\ndk\debug\lib и app\build\ndk\debug\obj\local.
  5. Добавьте идентификатор "hello_testview"в виджет textview
    1. Откройте res\layout\activity_main.xml и добавьте:
    2. <TextView
              android:text="@string/hello_world"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:id="@+id/hello_textview" />
  6. ​Обновите MainActivity.java для подключения вызова нативной библиотеки к виджету textview пользовательского интерфейса.

    1. public class MainActivity extends Activity {
          static { // load the native library "myNativeTest"
          System.loadLibrary("myNativeTest");
          }
          @Override
          protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_main);
      
                  // get the text string from native API, and display on the UI
              TextView tv = (TextView)findViewById(R.id.hello_textview);
              tv.setText(this.getStringTextFromNative());
          }
  7. Запустите виртуальную машину Android — Intel-Nexus4

  8. Запустите приложение, нажав кнопку Run.

    1. Это означает, что был использован компилятор Intel C++ для Android*.

Заключение

Теперь вы знаете, как собрать простое нативное приложение Android с помощью компилятора Intel C++ в Android Studio. Попробуйте проделать это с вашим приложением и сообщите нам, получится ли.




 

Использование новых инструкций шифрования Intel® стандарта AES в Android*

$
0
0

Введение

В этой статье описывается поддержка новых инструкций шифрования Intel® стандарта AES (Intel® AES-NI) в Android: что это, как это использовать и как измерять производительность. Также описывается модель использования и примеры в сочетании с двумя сценариями шифрования, демонстрирующими применение инструкций Intel AES-NI. Опытные разработчики могут пропустить первые четыре части этой статьи и перейти сразу к последней части, чтобы ознакомиться со сценариями использования. Менее опытным разработчикам, пожалуй, стоит прочесть всю статью.

Содержание

Введение
Новые инструкции шифрования стандарта AES (Intel AES-NI)
Проверка поддержки Intel AES-NI в Android
Использование Intel AES-NI в Android
 Использование библиотеки ассемблера
 Использование OpenSSL
Измерение производительности
 Средства тестирования
 Результаты тестирования
 Результаты теста, проведенного с помощью самодельной программы
Сценарии использования Intel AES-NI
Сводка
Об авторах
Справочные материалы

Новые инструкции шифрования стандарта AES (Intel AES-NI)

Инструкции Intel AES-NI были предложены в марте 2008 г. в качестве расширения набора инструкций архитектуры х86 для микропроцессоров Intel и AMD. Цель этого набора инструкций состоит в увеличении скорости приложений, выполняющих шифрование и расшифровку данных по стандарту AES.

Инструкции Intel AES-NI перечислены в таблице 1.

Таблица 1.Инструкции Intel® AES-NI

ИнструкцияОписание
AESENCВыполняет один цикл потока шифрования AES
AESENCLASTВыполняет последний цикл потока шифрования AES
AESDECВыполняет один цикл потока расшифровки AES
AESDECLASTВыполняет последний цикл потока расшифровки AES
AESKEYGENASSISTПомогает при создании раундового ключа AES
AESIMCПомогает с инверсными смешанными столбцами AES
PCLMULQDQУмножение

Проверка поддержки Intel AES-NI в Android

Проверить, поддерживается ли Intel AES-NI той или иной платформой, можно с помощью CPUID; проверьте значение CPUID.01H:ECX.AESNI[bit 25] = 1. Также можно использовать функцию check_for_aes_instructionsиз образца библиотеки Intel AES-NI.

Использование Intel AES-NI в Android

Использовать Intel AES-NI в Android можно разными способами:

  • • написать код C/C++ и использовать код ассемблера напрямую
  • • использовать существующую стороннюю библиотеку, например, OpenSSL*
  • • использовать API Java* Crypto из состава Android Kitkat

TДля компиляции собственной библиотеки/приложения для x86 в ОС Android требуется подготовить автономную цепочку инструментов с помощью команды make-standalone-toolchain.shв Android NDK:

	$NDK_PATH/build/tools/make-standalone-toolchain.sh –install-dir=$STANDALONE_TOOCHAIN_PATH –toolchain=x86-4.8 –platform=android-14 –ndk-dir=$NDK_PATH
	export PATH=$PATH:$STANDALONE_TOOCHAIN_PATH

Использование библиотеки сборки

Библиотека находится в папке intel_aes_lib. Также ее можно загрузить по адресу http://software.intel.com/en-us/articles/download-the-intel-aesni-sample-library. Используйте GCC версии 4.4 или более поздней, то есть версия NDK должна быть новее, чем NDK v3. (Здесь мы используем android-ndk-r9для тестирования.)

makefile/shellскрипт для сборки версий под Android отсутствует. Для компиляции можно изменить файл mk_lnx86.sh. Основное изменение состоит в использовании $STANDALONE_TOOCHAIN_PATH/bin/i686-linux-android-gccвместо GCC по умолчанию. Также следует использовать параметры pass –D__ANDROID__.

	export CC=”$STANDALONE_TOOLCHAIN_PATH/bin/i686-linux-android-gcc –D__ANDROID__  --sysroot=$STANDALONE_TOOLCHAIN_PATH/sysroot -I$STANDALONE_TOOLCHAIN_PATH/sysroot/include”

После компиляции можно перенести двоичную исполняемую программу в Android и протестировать ее. Также можно использовать исходный код непосредственно в ваших приложениях или использовать созданную двоичную библиотеку в NDK.

Использование OpenSSL

Инструкции Intel AES-NI используются многими библиотеками, например crypto++ polar SSL IPP OpenSSLи другими. (Мы используем OpenSSLв качестве эталона — поддерживаемые в OpenSSL инструкции Intel AES-NI из экспериментальной версии 1.0).

Начиная с Android 4.3, в OpenSSL в AOSP присутствует поддержка Intel AES-NI, поэтому вам достаточно скомпилировать код с нужной конфигурацией. Также можно загрузить его с официального веб-сайта и скомпилировать самостоятельно, а затем использовать файл *.a/*.soнапрямую в вашем проекте.

1) Загрузка ‒ можно загрузить OpenSSL по адресу http://www.openssl.org/source/. В настоящее время в Android 4.2 используется openssl-1.0.1cа в Android 4.4 — openssl-1.0.1e.Нужно использовать такую же версию OpenSSL, как в целевой системе.

(2) Компиляция — выполните следующую команду в консоли:

	cd $OPENSSL_SRC_PATH
	export CC=”$STANDALONE_TOOCHAIN_PATH/bin/i686-linux-android-gcc –mtune=atome –march=atom –sysroot=$STANDALONE_TOOCHAIN_PATH/sysroot”
	export AR=$STANDALONE_TOOCHAIN_PATH/bin/i686-linux-android-ar
	export RANLIB=$STANDALONE_TOOCHAIN_PATH/bin/i686-linux-android-ranlib
	./Configure android-x86 –DOPENSSL_IA32_SSE2 –DAES_ASM –DVPAES_ASM
	make

После этого файл libcrypto.aпоявится в папке верхнего уровня. Для использования файла *.soвведите “Configure shared android-x86 ***”.

(3)Используйте OpenSSL через NDK в проекте Android: создайте проект Android, объявите связанную с OPENSSL функцию как нативную, затем используйте код в jni/*.c.

После этого нужно скопировать файлы *.a/*.soи файл заголовка в проект.

	mkdir jni/pre-compiled/
	mkdir jni/include
	cp $OPENSSL_PATH/libcrypto.a jni/pre-compiled
	cp –L -rf $OPENSSL_PATH/include/openssl jni/include

Затем добавьте следующую строку в файл jni/Android.mk:

…
	LOCAL_MODULE	:=	static
	LOCAL_SRC_FILES	:=	pre-compiled/libcrypto.a
	…
	LOCAL_C_INCLUDES	:=	include
	LOCAL_STATIC_LIBRARIES	:=	static –lcrypto
	…

Затем можно использовать функции, предоставленные в OpenSSL, для реализации ваших функций encrypt/decrypt/SSL. Чтобы использовать Intel AES-NI, используйте функцию серии EVP_*, как показано ниже. При этом аппаратный модуль Intel AES-NI будет автоматически задействован для шифрования и расшифровки AES, если ЦП это поддерживает.

	//declare
EVP_CIPHER_CTX en, de;
	//init
	EVP_CIPHER_CTX_init(&en);
	EVP_CIPHER_CTX_init(&de);
	EVP_DecryptInit_ex(&de, NULL, NULL, NULL, NULL);
	//decrypt & encrpyt
	EVP_DecryptUpdate(&de, plaintext, &bytes_written, ciphertext, len);
	EVP_EncryptUpdate(&en, ciphertext, &cipher_len, plaintext, len);
	//clean up
	EVP_CIPHER_CTX_cleanup(&en);
	EVP_CIPHER_CTX_cleanup(&de);

Затем используйте ndk-buildдля компиляции.

	ndk-build APP_ABI=x86

(4) Использование API Java Crypto: в Android Kitkatпоставщик OpenSSL с именем “AndroidOpenSSL” поддерживает Intel AES-NI, поэтому вы можете использовать javax.crypto.Cipherнапрямую.

Cipher cipher = Cipher.getInstance(AES_ALGORITHM, “AndroidOpenSSL”);
cipher.init(*);

Performance Measurement

Измерение производительности

Средства тестирования

(1) Используйте средства OPENSSL. Команда opensslв составе OpenSSL может выполнять множество задач. Ее можно применять для измерения производительности с помощью openssl speed. По умолчанию openssl ((при сборке с параметром AES_ASM) будет автоматически использовать аппаратное ускорение Intel AES-NI. Также можно без труда отключить аппаратное ускорение, задав одной переменной среды: OPENSSL_ia32cap значение ~0x200000200000000

	openssl speed –evp aes-256-cbc
	OPENSSL_ia32cap=~0x200000200000000 openssl speed –evp aes-256-cbc

(2) Используйте самодельную программу.


Рисунок 1.Созданная нами тестовая программа.

(3) Intel® Mobile Platform Monitor (Intel® MPM) — это средство отслеживания электропотребления для получения подробной информации о состоянии ЦП/ГП/питания и т. п. Дополнительные сведения см. по адресу http://software.intel.com/en-us/articles/intel-power-monitoring-tool-for-android-devices-a-power-and-performance-related-data


Рисунок 2.Программа Intel® MPM

stop profiling, затем сохраните данные резуль¬татов. Результаты отображаются в виде текстовой сводки или в виде графика.

 

(4) Используйте тестовое приложение из библиотеки Intel AES-NI, см http://software.intel.com/en-us/articles/download-the-intel-aesni-sample-library. Можно включить или отключить AESNIв BIOS в некоторых тестовых проектах, таких как aes_gladman_subset, для демонстрации результатов производительности.

Результаты тестирования

Мы запустили [укажите программу] на планшете с процессором Bay Trail под управлением Android 4.4. Результаты нашего тестирования показали повышение производительности шифрования и расшифровки в 4–11 раз, а также экономию свыше 40 % электроэнергии при использовании Intel AES-NI.

Подробные данные тестирования

  • Результаты выполнения команды openssl speed. на приведенном ниже снимке экрана показан запуск opensslдля оценки скорости.


    Рисунок 3.Результаты команды openssl

  • Результаты теста, проведенного с помощью самодельной программы

    Таблица 2.Результаты теста, проведенного с помощью программы, созданной автором

Оборудование: BAYTRAIL_FFRD8 PR1, ОС: ANDROID 4.4
РЕЖИМ
(CBC/256)
Размер файлаIntel® AES-NI включеноIntel® AES-NI отключено
Время (с) без ввода-выводаВремя (с) с вводом-выводомВремя (с) без ввода-выводаВремя (с) с вводом-выводом
Шифрование351M2.8915.414.5925.61
 56M0.482.352.634.55
Расшифровка351M1.7638.14419.7828.51
 56M0.291.93.164.62
  • Без ввода-выводаиз приведенной выше таблицы

    Шифрование: повышение производительности почти в 5 раз, аналогично результатам при использовании OPENSSL
    Расшифровка: повышение производительности почти в 11 раз

  • С вводом-выводомиз приведенной выше таблицы

    Шифрование: повышение производительности почти в 1,9 раза
    Расшифровка: повышение производительности почти в 2 раза

  • • Результаты теста, проведенного с помощью Intel MPM

    Мы использовали нашу программу в качестве целевого приложения для тестирования и получили следующие результаты:


    Рисунок 4.Результаты теста для AESNI_DISABLED с вводом-выводом (16 с. всего)


    Рисунок 5. Результаты теста для AESNI_ENABLED с вводом-выводом (30 с всего)

    Итоги результатов тестирования с помощью Intel MPM:

    	AESNI_ENABLED: использовано 20 мВт-ч; средняя нагрузка на ЦП 55 %
    	AESNI_DISABLED: использовано 34 мВт-ч; средняя нагрузка на ЦП 52 %

    ПРИМЕЧАНИЕ. За счет включения Intel AES-NI можно сэкономить до 40 % расходуемой электроэнергии по сравнению с программным решением.

Сценарии использования Intel AES-NI

Алгоритм AES широко используется в различных сценариях защиты данных, таких как шифрование при передаче данных в сети, шифрование целых дисков или отдельных файлов. Во всех этих сценариях можно использовать Intel AES-NI при условии поддержки нужных инструкций центральным процессором. На рис. 6 показана типичная передача данных в сети; все данные, передаваемые между пользователями и серверами, шифруются после прохождения проверки подлинности.


Рисунок 6.Типичная передача данных в сети

Еще один типичный сценарий использования — шифрование целых дисков или шифрование отдельных файлов, когда пользователи должны сохранять данные в локальном хранилище. На рис. 7 показан весь рабочий процесс от пользователя до хранилища:


Рисунок 7.Весь рабочий процесс шифрования

Аппаратные инструкции Intel AES-NI обеспечивают и высокую производи-тельность, и экономию электроэнергии, что особенно важно для мобильных устройств, таких как смартфоны и планшеты. Управление мобильными устройствами (MDM) и управление мобильным содержимым (MCM) — крайне важные факторы для корпоративных решений безопасности. В этом разделе описывается Intel AES-NI в сочетании с MDM и MCM.

Хороший пример MDM: администраторы отправляют сообщения или команды конечным пользователям, при этом все передаваемые конфиденциальные данные должны быть зашифрованы. Программное решение AES быстро расходует заряд аккумулятора при частой передаче данных, но решение Intel AES-NI использует аппаратные решения, экономящие электроэнергию и повыша-ющие производительность. На рис. 8 показан типовой сценарий, в котором администраторы отправляют команды пользователям с использованием шифрования на базе Intel AES-NI. Рабочий процесс включает шифрование команд перед их отправкой и расшифровку данных для выполнения команд на устройствах конечных пользователей.


Рисунок 8.Обмен данными MDM с использованием криптографии AES

Пример MCM: пользователи получают доступ к конфиденциальным данным, таким как документы, изображения, видео и т. п., с корпоративных серверов. Такие передаваемые данные нуждаются в надежной защите. Все данные должны быть зашифрованы перед отправкой конечным пользователям; на устройствах пользователей данные должны также храниться в зашифрованном формате. На рис. 9 показан типичный рабочий процесс MCM с начала и до конца при чтении и сохранении данных конечным пользователем. Инструкции Intel AES-NI поддерживают 256-разрядный алгоритм шифрования, поэтому это превосходное аппаратное решение для систем безопасности корпоративного уровня и для выполнения требований конфиденциальности.


Рисунок 9.Рабочий процесс MCM

Для задействования аппаратного ускорения Intel AES-NI разработчикам необхо-димо использовать программирование NDK. На рис. 10 показаны типичные взаимоотношения между уровнями приложений Android, модулем шифрования Android и модулем Intel AES-NI C/C++. Java Native Interface (JNI) используется для связывания функций C/C++ с функциями Java. Если программирование в NDK вам незнакомо, см. справочники по NDK для получения дополнительных сведений.


Рисунок 10.Типичные взаимосвязи между уровнями

Заключение

В этой статье описывается использование инструкций Intel AES-NI для ускорения шифрования на устройствах Android. Показано, каким образом разработчики могут использовать код ассемблера, сторонние библиотеки и Intel NDK для ускорения приложений и экономии электроэнергии. Два типовых сценария шифрования и расшифровки данных описаны для помощи разработчикам в использовании Intel AES-NI в своих приложениях.

Для получения дополнительных сведений о средствах Intel® разработчики Android могут посетить следующие сайты: Intel® Developer Zone for Android.

Об авторах

Чжань Ли (Zhang Li) — инженер по разработке приложений в отделе Intel® Software and Solutions Group (SSG) в подразделении Developer Relations Division в составе группы Mobile Enabling Team. Он занимается поддержкой приложений для Android.

Янчинь Вань (Yanqing Wang) — инженер по разработке приложений в отделе Intel® Software and Solutions Group (SSG) в подразделении Developer Relations Division в составе группы Mobile Enabling Team. В круг его ответственности входит управляемость и безопасность корпоративных решений. Он обладает статусом Intel® Developer Zone (IDZ) Black Belt.

Справочные материалы

[1] http://software.intel.com/en-us/articles/intel-advanced-encryption-standard-instructions-aes-ni/
[2] http://en.wikipedia.org/wiki/AES_instruction_set
[3] http://software.intel.com/en-us/articles/download-the-intel-aesni-sample-library
[4] http://ark.intel.com/search/advanced/?s=t&AESTech=true
[5] http://www.openssl.org/source/
[6] http://software.intel.com/en-us/articles/intel-power-monitoring-tool-for-android-devices-a-power-and-performance-related-data

Intel, эмблема Intel, Atom, Celeron, Core, Pentium и Xeon являются товарными знаками корпорации Intel в США и в других странах.
*Прочие наименования и товарные знаки могут быть собственностью третьих лиц.
© Intel Corporation, 2014. Все права защищены.

Перенос низкоуровневого кода нативных приложений Android* на платформы с архитектурой Intel®

$
0
0

Введение

Существует два типа приложений для Android. Первый тип — приложения Dalvik, представ-ляющие собой приложения на базе Java*, способные правильно выполняться на любой архитектуре без каких-либо изменений. Второй тип — приложения NDK, у которых часть кода написана на C/C++ или на ассемблере, вследствие чего требуется перекомпилировать код для каждой архитектуры CPU.

В этой статье рассматриваются приложения NDK. Для этих приложений обычно нужно всего лишь изменить параметр APPABI в файле application.mk и скомпилировать код NDK, после чего приложение можно будет запустить на соответствующем устройстве. Тем не менее определенные части некоторых приложений NDK невозможно просто перекомпилировать, если в них используется код определенного типа, например ассемблерный код или код SIMD (обработка нескольких фрагментов данных одной инструкцией). В этой статье поясняется, как справляться с такими затруднениями, и приводится полезная для разработчиков информация о переносе приложений с архитектуры, отличной от Intel® (x86), на платформу с архитектурой Intel. Также обсуждается преобразование порядка следования байтов между платформами x86 и отличными от x86.

SIMD (одна инструкция, множество данных)

SIMD — это класс параллельных компьютеров, согласно описанию в таксономии Флинна, с множеством процессорных элементов, одновременно выполняющих одну и ту же операцию с множеством точек данных. Такие машины используют параллельную обработку на уровне данных, поскольку вычисления осуществляются одновременно (параллельно). Вычисления SIMD особенно эффективны для распространенных задач, таких как изменение контрастности цифровых изображений или регулировка громкости цифрового звука. Большинство современных ЦП используют инструкции SIMD для повышения производительности при обработке мультимедиа. На мобильной платформе x86 инструкции SIMD называются потоковыми расширениями Intel® SIMD (Intel® SSE, SSE2, SSE3 и т. п.). На платформе ARM* инструкции SIMD называются технологией NEON*. Дополнительные сведения о NEON см. в документации производителя.

Потоковые расширения Intel® SIMD (Intel® SSE)

Для начала, что такое Intel SSE? По сути, это набор 128-разрядных регистров ЦП. В эти регистры можно поместить по четыре 32-разрядных скаляра, что дает возможность выполнить операцию над каждым из этих четырех элементов одновременно. Для сравнения в обычном случае для достижения такого же результата может потребоваться четыре операции или даже больше. На следующей схеме показаны два вектора (регистры Intel SSE) со скалярами. Производится умножение регистров операцией MULPS, после чего результат сохраняется. Умножение четырех значений производится всего за одну операцию. Преимущества Intel SSE слишком значительны, чтобы их упускать.


Рисунок 1. Два вектора (регистры Intel® SSE) со скалярами

Теперь рассмотрим несколько распространенных инструкций.

Инструкции перемещения данных

MOVUPS

Переместить 128 бит данных в регистр SIMD из памяти или регистра SIMD. Без выравнивания.

MOVAPS

Переместить 128 бит данных в регистр SIMD из памяти или регистра SIMD. С выравниванием.

MOVHPS

Переместить 64 бита в верхние биты регистра SIMD (верх).

MOVLPS

Переместить 64 бита в нижние биты регистра SIMD (низ).

MOVHLPS

Переместить верхние 64 бита исходного регистра в нижние 64 бита регистра назначения.

MOVLHPS

Переместить нижние 64 бита исходного регистра в верхние 64 бита регистра назначения.

MOVMSKPS

Переместить знаковые разряды каждого из четырех скаляров в целочисленный регистр x86.

MOVSS

Переместить 32 бита данных в регистр SIMD из памяти или регистра SIMD.

Арифметические инструкции  Примечание:  Скалярная версия выполняет операцию только для первых элементов. Параллельная версия выполняет операцию для всех элементов в регистре.

Параллельная

Скалярная

ADDPS

ADDSS — сложение операндов

SUBPS

SUBSS — вычитание операндов

MULPS

MULSS — умножение операндов

DIVPS

DIVSS — деление операндов

SQRTPS

SQRTSS — извлечение квадратного корня из операнда

MAXPS

MAXSS — максимум операндов

MINPS

MINSS — минимум операндов

RCPPS

RCPSS — вычисление величины, обратной операнду

RSQRTPS

RSQRTPS — вычисление величины, обратной квадратному корню из операнда

Инструкции сравнения

Параллельная

Скалярная

CMPPS, CMPSS

Сравнивает операнды и возвращает все единицы или все нули

Логические инструкции

ANDPS

Побитовое «логическое И» операндов

ANDNPS

Побитовое «логическое И НЕ» операндов

ORPS

Побитовое «логическое ИЛИ» операндов

XORPS

Побитовое «логическое исключающее ИЛИ» операндов

Инструкции перестановки

SHUFPS

Перестановка чисел из одного операнда в другой или в этот же

UNPCKHPS

Распаковка чисел старшего разряда в регистр SIMD

UNPCKLPS

Распаковка чисел младшего разряда в регистр SIMD

Прочие инструкции, не показанные здесь, включают преобразование данных между регистрами x86 и MMX, инструкции по управлению кешем и инструкции по управлению состоянием.

Преобразование NEON в Intel SSE

IИнструкции Intel SSE и NEON не полностью совпадают. Они основываются на одинаковом принципе, но методы их реализации различаются. Следует преобразовывать каждую инструкцию по отдельности. Ниже представлены два фрагмента кода с одной и той же функцией; в одном фрагменте используются инструкции NEON, а в другом — Intel SSE.

В следующем коде используются инструкции NEON:

int16x8_t q0 = vdupq_n_s16(-1000), q1 = vdupq_n_s16(1000);
int16x8_t zero = vdupq_n_s16(0);
for( k = 0; k < 16; k += 8 )
{
    int16x8_t v0 = vld1q_s16((const int16_t*)(d+k+1));
    int16x8_t v1 = vld1q_s16((const int16_t*)(d+k+2));
    int16x8_t a = vminq_s16(v0, v1);
    int16x8_t b = vmaxq_s16(v0, v1);
    v0 = vld1q_s16((const int16_t*)(d+k+3));
    a = vminq_s16(a, v0);
    b = vmaxq_s16(b, v0);
    v0 = vld1q_s16((const int16_t*)(d+k+4));
    a = vminq_s16(a, v0);
    b = vmaxq_s16(b, v0);
    v0 = vld1q_s16((const int16_t*)(d+k+5));
    a = vminq_s16(a, v0);
    b = vmaxq_s16(b, v0);
    v0 = vld1q_s16((const int16_t*)(d+k+6));
    a = vminq_s16(a, v0);
    b = vmaxq_s16(b, v0);
    v0 = vld1q_s16((const int16_t*)(d+k+7));
    a = vminq_s16(a, v0);
    b = vmaxq_s16(b, v0);
    v0 = vld1q_s16((const int16_t*)(d+k+8));
    a = vminq_s16(a, v0);
    b = vmaxq_s16(b, v0);
    v0 = vld1q_s16((const int16_t*)(d+k));
    q0 = vmaxq_s16(q0, vminq_s16(a, v0));
    q1 = vminq_s16(q1, vmaxq_s16(b, v0));
    v0 = vld1q_s16((const int16_t*)(d+k+9));
    q0 = vmaxq_s16(q0, vminq_s16(a, v0));
    q1 = vminq_s16(q1, vmaxq_s16(b, v0));
}
q0 = vmaxq_s16(q0, vsubq_s16(zero, q1));
// first mistake it produce wrong result
//q0 = vmaxq_s16(q0, vzipq_s16(q0, q0).val[1]);
// may be someone knows faster/better way?
int16x4_t a_hi = vget_high_s16(q0);
q1 = vcombine_s16(a_hi, a_hi);
q0 = vmaxq_s16(q0, q1);

// this is _mm_srli_si128(q0, 4)
q1 = vextq_s16(q0, zero, 2);
q0 = vmaxq_s16(q0, q1);

// this is _mm_srli_si128(q0, 2)
q1 = vextq_s16(q0, zero, 1);
q0 = vmaxq_s16(q0, q1);

// read the result
int16_t __attribute__ ((aligned (16))) x[8];
vst1q_s16(x, q0);
threshold = x[0] - 1;

В следующем коде используются инструкции SSE:

__m128i q0 = _mm_set1_epi16(-1000), q1 = _mm_set1_epi16(1000);
for( k = 0; k < 16; k += 8 )
{
    __m128i v0 = _mm_loadu_si128((__m128i*)(d+k+1));
    __m128i v1 = _mm_loadu_si128((__m128i*)(d+k+2));
    __m128i a = _mm_min_epi16(v0, v1);
    __m128i b = _mm_max_epi16(v0, v1);
    v0 = _mm_loadu_si128((__m128i*)(d+k+3));
    a = _mm_min_epi16(a, v0);
    b = _mm_max_epi16(b, v0);
    v0 = _mm_loadu_si128((__m128i*)(d+k+4));
    a = _mm_min_epi16(a, v0);
    b = _mm_max_epi16(b, v0);
    v0 = _mm_loadu_si128((__m128i*)(d+k+5));
    a = _mm_min_epi16(a, v0);
    b = _mm_max_epi16(b, v0);
    v0 = _mm_loadu_si128((__m128i*)(d+k+6));
    a = _mm_min_epi16(a, v0);
    b = _mm_max_epi16(b, v0);
    v0 = _mm_loadu_si128((__m128i*)(d+k+7));
    a = _mm_min_epi16(a, v0);
    b = _mm_max_epi16(b, v0);
    v0 = _mm_loadu_si128((__m128i*)(d+k+8));
    a = _mm_min_epi16(a, v0);
    b = _mm_max_epi16(b, v0);
    v0 = _mm_loadu_si128((__m128i*)(d+k));
    q0 = _mm_max_epi16(q0, _mm_min_epi16(a, v0));
    q1 = _mm_min_epi16(q1, _mm_max_epi16(b, v0));
    v0 = _mm_loadu_si128((__m128i*)(d+k+9));
    q0 = _mm_max_epi16(q0, _mm_min_epi16(a, v0));
    q1 = _mm_min_epi16(q1, _mm_max_epi16(b, v0));
}
q0 = _mm_max_epi16(q0, _mm_sub_epi16(_mm_setzero_si128(), q1));
q0 = _mm_max_epi16(q0, _mm_unpackhi_epi64(q0, q0));
q0 = _mm_max_epi16(q0, _mm_srli_si128(q0, 4));
q0 = _mm_max_epi16(q0, _mm_srli_si128(q0, 2));
threshold = (short)_mm_cvtsi128_si32(q0) - 1;

Дополнительные сведения о преобразовании инструкций NEON в Intel SSE см. в блоге, ссылка на который приводится в разделе справочных материалов[1]. Предоставляется файл заголовка, который можно использовать для автоматического сопоставления инструкций NEON и Intel SSE.

Поддержка ассемблера

Процессоры с архитектурой CISC, такие как процессоры Intel, располагают богатым набором инструкций, способных выполнять сложные операции одной инструкцией (этим такие процессоры принципиально отличаются от RISC-процессоров, архитектура которых оптимизирована для более эффективной обработки более общих инструкций). CISC-процессоры обладают сравнительно большим количеством регистров общего назначения и инструкций обработки данных, обычно использующих три регистра: один целевой регистр (регистр назначения) и два регистра операндов. Дополнительные сведения об архитектуре и инструкциях в процессорах ARM см. в документации производителя.

Процессоры Intel (т. е. процессоры 386 и последующие) имеют восемь 32-разрядных регистров общего назначения, как показано на следующей схеме. Имена регистров по большей части сложились исторически. Например, регистр EAX раньше называли накопителем, поскольку он использовался рядом арифметических операций, а регистр ECX назывался счетчиком, поскольку в нем содержался индекс цикла. В современном наборе инструкций большинство регистров утратили свое специализированное назначение, но, по соглашению, два следующих регистра зарезервированы для особых целей: указатель стека (ESP) и базовый указатель (EBP).


Рисунок 2. Процессоры Intel® x86 с восемью 32-разрядными регистрами общего назначения

Для регистров EAX, EBX, ECX и EDX могут быть использованы подразделы. Например, два младших байта регистра EAX могут обрабатываться как 16-разрядный регистр с именем AX. Младший байт регистра AX может использоваться как одиночный 8-разрядный регистр с именем AL, а старший байт регистра AX может использоваться как одиночный 8-разрядный регистр с именем AH. Эти имена относятся к одному и тому же физическому регистру. При помещении двухбайтового числа в регистр DX это изменение влияет на значения DH, DL и EDX. Эти «вложенные регистры» являются своего рода наследием более старых, 16-разрядных версий набора инструкций. Тем не менее иногда ими удобно пользоваться, если данные меньше 32 разрядов (например, однобайтовые символы ASCII).

Из-за различий между ассемблером ARM и x86 ассемблерный код ARM невозможно напрямую использовать на платформах x86. Тем не менее существует два способа использования ассемблерного кода ARM при переносе приложения Android для ARM на архитектуру x86:

  1. Реализация этой же функции на ассемблере x86.
  2. Замена ассемблерного кода на код на языке С.

Во многих программах с открытым исходным кодом ассемблерный код заменяется для повышения производительности, но в данном случае производительность не особенно важна, поскольку процессоры сейчас стали гораздо мощнее, чем были ранее. Тем не менее, в отличие от замененного ассемблерного кода, код C, реализующий эту же функцию, сохранился в исходном коде, и мы можем скомпилировать код C для платформы x86.

Например, разработчик создал игру, в которой используется формат сжатия звука Vorbis, — это программа с открытым исходным кодом, содержащая сегментированный ассемблерный код ARM. Разработчику не удалось преобразовать программу в приложение NDK для x86. Вместо замены этого фрагмента кода на ассемблер x86 разработчик переписал код на C, после чего код заработал на процессорах x86. Для решения этой проблемы нужно отключить Macro _ARM_ASSEM_и включить Macro _LOW_ACCURACY_.

Преобразование порядка следования байтов при переносе приложений между ARM и x86

В межплатформенных проектах мы зачастую сталкиваемся с одной из старейших проблем в истории программирования — с преобразованием порядка следования байтов. Если файл создан на машине с форматом little Endian (начиная с младшего байта), целое число 255 может быть записано следующим образом:

ff 00 00 00

Но при чтении в память значение будет различаться для разных платформ, из-за чего при переносе приложений возникнет неполадка.

int a;
fread(&a, sizeof(int), 1, file);
// on little endian machine, a = 0xff;
// but on big endian machine, a = 0xff000000; 

Эту проблему можно решить очень простым и эффективным образом: написать функцию под названием readInt():

void readInt(void* p, file)
{
    char buf[4];
    fread(buf, 4, 1, file);
    *((uint32*)p) = buf[0] << 24 | buf[1] << 16
                   | buf[2] << 8 | buf[3];}

Эта функция работает и на платформах с форматом big Endian (начиная со старшего байта), и на платформах с форматом little Endian. Но эта функция отличается от стандартного метода чтения структур.

fread(&header, sizeof(struct MyFileHeader), 1, file);

Если MyFileHeader содержит множество целочисленных значений, потребуется множество операций read(). При этом не только загромождается код, но и замедляется работа из-за увеличения количества операций ввода-вывода. Поэтому я предлагаю другой способ: оставим код без изменений и используем несколько макросов для постобработки данных.

fread(&header, sizeof(struct MyFileHeader), 1, file);
CQ_NTOHL(header.version);
CQ_NTOHL_ARRAY(&header.box, 4); // box is a RECT structure

Если порядок следования байтов в компьютере не совпадает с таким порядком в файле данных, эти макросы выполняют определенные функции; в противном случае эти макросы определяются как пустые:

#if defined(ENDIAN_CONVERSION)
#    define CQ_NTOHL(a) {a = ((a) >> 24) | (((a) & 0xff0000) >> 8) |
	(((a) & 0xff00) << 8) | ((a) << 24); }
#    define CQ_NTOHL_ARRAY(arr, num) {uint32 i;
	for(i = 0; i < num; i++) {CQ_NTOHL(arr[i]); }}
#else
#    define CQ_NTOHL(a)
#    define CQ_NTOHL_ARRAY(arr, num)
#endif

Преимущество этого подхода состоит в том, что циклы ЦП не растрачиваются впустую при неопределенном ENDIAN_CONVERSION, а код сохраняет естественный порядок чтения целых структур за один проход.

Заключение

Процессоры ARM и x86 обладают разной архитектурой и разными наборами инструкций. Определенные различия есть и на низком уровне. Надеюсь, что информация в этой статье поможет вам решить затруднения, связанные с этими различиями, при разработке приложений Android NDK для нескольких платформ.

Об авторе

Пэнь Тао (tao.peng@intel.com) работает инженером по программному обеспечению в отделе Intel Software and Services Group. В настоящее время он занимается поддержкой игр и мультимедиаприложений и оптимизацией производительности, в частности на мобильных платформах Android.

Ресурсы

[1] From ARM NEON to Intel SSE- The Automatic Porting Solution, Tips and Tricks

Анализ производительности Java на устройствах Android с помощью Intel® VTune™ Amplifier 2014 for Systems

$
0
0

Intel® VTune™ Amplifier 2014 for Systems поддерживает анализ функций Java и доступ к ассемблеру с JIT, Java Source и Dex* для функций, обработанных с помощью JIT на рутованных устройствах Android*, на которых запущена виртуальная машина Java/Dalvik* с оснасткой.  Прочтите эту статью позже, чтобы узнать, как запустить будущую версию VTune Amplifier for Systems для включения анализа Java на ART* JVM.

Если возникают следующие проблемы:

  • После сбора - time(samples) связано с функцией/модулем: [dalvik-jit-code-cache (удалено)] в "Basic Hotspots"
  • После сбора - time(samples) связано с функцией/модулем: [Вне любых известных модулей] в разделе "Advanced Hotspots"или в любых других типах выборки на основе событий оборудования
  • Функция Java связана с ассемблером (или с чем-то посторонним), —

то, по всей вероятности, нужно заново настроить устройство Android, чтобы компонент VTune Amplifier мог работать с кодом Java. Необходимы следующие 4 компонента:

  1. Рутованное устройство Android
  2. Dalvik JVM с оснасткой VTune Amplifier
  3. Запустите виртуальную машину Dalvik с включенной оснасткой
  4. Выполните анализ в VTune Amplifier и укажите процессы для просмотра сведений о Java.

Для получения дополнительных сведений см:

Руководство пользователя Intel® VTune™ AmplifierЗапуск удаленного анализаПодготовка целевой системы Android* для удаленного анализа

1) Устройство Android с корневым доступом

Убедитесь, что возможен рутовый доступ к устройству.  Выполните следующую команду на подключенной системе с adb.

adb root

Если вы получите сообщение:

adbd is already running as root

или

restarting adbd as root

то устройство рутовано. Отладочные и инженерные образы ОС Android обычно с легкостью рутуются.  Рутовый доступ к пользовательским образам обычно не удается получить.  Для получения широкодоступного устройства с возможностью рутования см. http://www.intel.com/mdk или http://01.org/android-ia.

2) Получение Dalvik JVM с оснасткой VTune Amplifier

Многие устройства Android с процессорами Intel® Atom поставляются с ядром Dalvik с подключенной оснасткой. Некоторые исключения, заслуживающие упоминания:

Определение наличия оснастки Dalvik на вашем устройстве

Выполните команду:

adb shell dalvikvm -get Help

найдите –Xjitvtuneinfoв списке возможных параметров.

Если этот параметр отсутствует, поддержка оснастки для VTune отсутствует в системе Dalvik на устройстве, и его нужно заменить.

Замена libdvm.so на вашем устройстве

Intel VTune Amplifier 2014 for Systems, обновление 1, содержит версию libdvm.so с оснасткой в папке <path-to-vtune>/target/android_v3.10/prebuilt//DellVeune8.  Для установки выполните следующие команды:

  1. adb root
  2. adb remount
  3. adb push <path-to-vtune>/target/android_v3.10/prebuilt/DellVeune8/libdvm.so /system/lib
  4. adb reboot

3) Запустите виртуальную машину Dalvik с включенной оснасткой VTune Amplifier

VTune Amplfier 2014 for Systems по умолчанию настраивает ВМ Dalvik для оснастки в следующих случаях:

При создании нового проекта в пользовательском интерфейсе, выбрав в качестве системы назначения устройство Android (ADB), затем в первый раз запустите сбор на этом устройстве. При этом оснастка будет переведена в режим JIT. Это дает вам возможность отслеживать все функции в графическом пользовательском интерфейсе, но при попытке открытия конкретных функций будет открыт ассемблер (а не исходный код Java и не байтовый код Dex).

Для изменения значений по умолчанию нужно вручную запустить установщик с основного устройства:

Linux: <путь к vtune>/bin[32|64]/amplxe-androidreg.sh --package-command=install --xjitvtuneinfo=src

Windows: <путь к vtune>\bin32\amplxe-androidreg.bat --package-command=install --xjitvtuneinfo=src

Где --xjitvtuneinfo=srcможет иметь одно из следующих значений:

  • Основные сведения о скомпилированной трассировке: -Xjitvtuneinfo=jit
  • Сопоставление от кода JIT к исходному коду Java и базовая информация о скомпилированной трассировке: -Xjitvtuneinfo=src
  • Сопоставление от кода JIT к коду DEX и базовая информация о скомпилированной трассировке: -Xjitvtuneinfo=dex
  • Сбор данных JIT. По умолчанию сбор данных JIT отключен, если не указать никакие параметры: -Xjitvtuneinfo=none

По умолчанию установщик изменит /data/local.prop следующим образом, чтобы включить JVM для VTune при всех последующих перезагрузках ОС.

root@android:/ # cat /data/local.prop
dalvik.vm.extra-opts=-Xjitvtuneinfo:src

Для Dell Venue 8 и устройств, которые не могут использовать /data/local.prop, следующая процедура сработает с главного устройства.

adb shell setprop dalvik.vm.extra-opts -Xjitvtuneinfo:src

Установите для свойства значение src, dex, jit или none

adb shell stop
adb shell start

Не перезагружайте устройство. Убедитесь, что это свойство задано, с помощью adb shell getprop.  Выходные данные должны содержать [dalvik.vm.extra-opts]: [-Xjitvtuneinfo:src]

4) Выполните анализ в VTune Amplifier и укажите процессы для просмотра сведений о Java

VTune Amplifier может связывать выборки с кодом JIT с помощью всех доступных типов анализа, но требуется заключительный шаг.  Необходимо запустить анализ для проекта с помощью Attach to Process или Launch Android Package. При этом будут получены только те JIT-файлы, которые были указаны в параметрах проекта.

Для двух типов анализа в VTune Amplifier может потребоваться вручную поместить JIT-файлы в папку результатов:  если в свойствах проекта вы указали Profile System либо если вы указали Analyze System-Wide для Attach to Process или Launch Android Package в разделе Advanced Settings.  Затем можно просмотреть JIT-файлы для нужных процессов, которые не были указаны.   См.:  Руководство пользователя Intel® VTune™ AmplifierПоддержка интерфейса командной строкиВыполнение анализа из командной строкидля получения дополнительных сведений.

Обмен текстурами между Intel Media SDK и OpenGL

$
0
0

Code Sample

Краткий обзор

Обычно в ОС Windows* для обработки видео используется Direct3D. Однако во многих приложениях неизменность графического интерфейса и внешнего вида при работе на разных платформах обеспечивается за счет возможностей OpenGL*. В последних версиях графических драйверов Intel поддерживается расширение NV_DX_interop, что обеспечи-вает возможность обмена поверхностями между D3D и OpenGL для их последующего использования в Intel® Media SDK. В Intel® Media SDK можно настроить использование Direct3D, а благодаря поддержке NV_DX_interop кадровый буфер Intel Media SDK может использоваться в OpenGL. При этом устраняется необходимость в ресурсоемком копировании текстур из графического процессора в ЦП и обратно для обработки. В данном примере кода и техническом документе приведена процедура настройки использования D3D в Intel® Media SDK для кодирования и декодирования видео, преобразования цветов из цветовой схемы NV12 (стандартный цветовой формат Media SDK) в схему RGBA (стандартный цветовой формат OpenGL) и сопоставления наложения поверхности D3D с на текстурой текстуру OpenGL. В данной процедуре отсутствует этап копирования текстур из графического процессора в ЦП для обработки, что всегда представляло большую сложность при использовании OpenGL с Intel® Media SDK.

Требования к системе

Пример кода написан с помощью Visual Studio* 2013. Он предназначен (1) для демонст-рации работы Miracast и (2) обмена текстурами между Intel® Media SDK и OpenGL. В процессе обмена декодированные поверхности Intel® Media SDK сопоставляются с текстурами OpenGL без необходимости в выполнении копирования, что значительно повышает эффективность работы. При использовании процессоров Haswell и более поздних версий выполняется аппаратное ускорение декодера MJPEG. При использовании процессоров более ранних версий в Media SDK автоматически применяется программный декодер. Необходимо использовать камеру с поддержкой MJPEG (это может быть как встроенная камера, так и камера с подключением через USB).
Большая часть процедур, используемых в примере кода и техническом документе, применимы и для Visual Studio 2012 (за исключением идентификации типа подключения Miracast). Пример кода основан на Intel® Media SDK 2014 для клиентских систем. Загрузить пример можно по следующей ссылке: (https://software.intel.com/sites/default/files/MediaSDK2014Clients.zip.) После установки SDK создается набор переменных среды для поиска правильных путей к файлам заголовков и библиотекам в Visual Studio.

Обзор приложения

Приложение распознает камеру как устройство ввода MJPEG, выполняет декодирование этого видео, затем кодирование потока в формат H264 и, наконец, его декодирование и завершение обработки. Поток видео формата MJPEG с камеры (после декодирования) и полностью обработанные потоки отображаются в графическом интерфейсе на базе MFC. В системах Haswell для обеспечения удобочитаемости выполняется последовательный запуск двух декодеров и одного кодировщика (с разрешением 1080p). Благодаря аппаратному ускорению эта процедура не занимает много времени. Единственным ограничением количества передаваемых кадров в секунду является скорость работы камеры. В реальных условиях кодировщики и декодеры запускаются в отдельных потоках, поэтому проблем с производительностью возникать не должно.

При использовании одного монитора в графическом интерфейсе на базе OpenGL потоковое видео с камеры отображается в режиме «картинка в картинке» поверх обработанного видео (рис. 1). При использовании технологии Miracast программа автоматически определяет монитор с поддержкой Miracast , и на нем во весь экран отображается окно с обработанным видео, при этом в основном графическом интерфейсе отображается необработанное видео с камеры. Такой режим позволяет с легкостью сравнить исходное и кодированное видео. Кроме того, в меню View -> Monitor Topology можно отслеживать текущую топологию мониторов, а также изменять ее. К сожалению, запустить подключение Miracast в этом меню невозможно. Это можно сделать только в меню чудо-кнопок ОС (меню чудо-кнопок справа -> «Устройства» -> «Проект»). На настоящий момент API для запуска подключения Miracast не существует. При этом отключить Miracast-монитор можно, изменив топологию мониторов на «только внутренние». При наличии нескольких мониторов, подключенных с помощью проводов, их топологию можно в любой момент изменить в этом меню.

Рисунок 1. Топология с использованием одного монитора. Видео с камеры MJPEG отображается в правом нижнем углу. Обработанное видео отображается во весь экран. При включении режима использования нескольких мониторов (например, в режиме Miracast) программа обнаруживает данное изменение, в результате чего видео с камеры MJPEG и обработанное видео автоматически выводятся на разные мониторы.

Главная точка входа для настройки процесса обработки

Пример кода выполнен на базе MFC. Главная точка входа для настройки процесса обработки — CChildView::OnCreate (). Здесь выполняется инициализация камеры с последующим транскодированием видео из формата MJPEG в H264, декодированием формата H264 и связыванием текстур из транскодера и декодера в модуле визуализации OpenGL. Транскодер представляет собой подкласс декодера с добавлением кодировщика поверх базового декодера. Событие OnCreate запускает поток, в рамках которого осуществляется и упорядочивается потоковое вещание с камеры. При считывании потокового вещания рабочий поток отправляет сообщение функции OnCamRead, которая выполняет декодирование видео в формате MJPEG, кодирование в формат H264, его декодирование и обновление текстур в модуле визуализации OpenGL. На верхнем уровне весь процесс очень прозрачен и прост.

Инициализация декодера/транскодера

Для использования D3D9Ex необходимо выполнить инициализацию декодера и транс-кодера. Intel® Media SDK может быть настроен на использование программного метода, D3D9 или D3D11. В этом примере для упрощения преобразования цветов используется D3D9. Стандартным цветовым форматом Intel® Media SDK является NV12. Для преобразования цветовой схемы в формат RGBA можно использовать функцию IDirect3DDevice9::StretchRect или функцию IDirectXVideoProcessor::VideoProcessBlt. В целях упрощения в этом техническом документе используется функция StretchRect, однако на практике рекомендуется использовать функцию VideoProcessBlt, так как в нее включена дополнительная возможность последующей обработки. К сожалению, D3D11 не поддерживает StretchRect, что может усложнить процесс преобразования цветов. Кроме того, в этом документе для выполнения различных опытов (таких как сочетание различных типов программного и аппаратного обеспечения) в декодере и транскодере используются отдельные устройства D3D. Однако для экономии ресурсов памяти в декодере и транскодере может использоваться одно устройство D3D. В результате такой настройки процесса обработки результатам, получаемым на выходе после декодирования, задается тип (mfxFrameSurface1 *). Это оболочка для D3D9. Тип mfxFrameSurface1 -> Data. MemId можно преобразовать в тип (IDirect3DSurface9 *). После декодирования этот тип можно использовать в StretchRect или VideoProcessBlt функции CDecodeD3d9::ColorConvert. Полученные поверхности Media SDK нельзя сделать общими, однако преобразование цветов все равно является обязательным в OpenGL. Для хранения результатов преобразования создаются общие поверхности.

Инициализация транскодера

Декодированные транскодером данные направляются непосредственно в кодировщик. Убедитесь, что при выделении поверхностей используется MFX_MEMTYPE_FROM_DECODE.

Связывание текстур в D3D и OpenGL

Код для связывания текстуры можно найти в функции CRenderOpenGL::BindTexture. Убедитесь, что расширение WGLEW_NV_DX_interop определено, затем последовательно используйте функции wglDxOpenDeviceNV, wglDXSetResourceShareHandleNV и wglDXRegisterObjectNV. Поверхность D3D связывается с текстурой OpenGL. Текстуры не обновляются автоматически. Их можно обновить, вызвав функции wglDXLockObjectsNV/wglDXUnlockObjectsNV (CRenderOpenGL::UpdateCamTexture и CRenderOpenGL::UpdateDecoderTexture). После обновления текстуру можно использовать, как любую другую текстуру в OpenGL.

Важные моменты при изменении топологии для нескольких мониторов

Может показаться, что вывести еще одно окно на внешний монитор и управлять им посредством обнаружения изменений в топологии довольно просто. Однако на деле ОС может потребоваться некоторое время для инициализации переключения между режимами, завершения настройки монитора и отображения содержимого. Учитывая использование кодировщика/декодера/D3D/OpenGL и всех сопутствующих компонентов, отладка этого процесса может быть довольно сложным делом. В примере кода при переключении между режимами повторно используется большая часть процедуры, однако более простым вариантом может быть завершение процесса и его повторная инициализация, так как если процесс добавления монитора занимает более 10 секунд, могут возникнуть различные проблемы — даже при подключении с помощью кабелей HDMI или VGA.

Задачи на будущее

Пример кода для этого технического документа написан для D3D9 и не включает поддержку D3D11. Пока трудно с точностью сказать, какой способ преобразования цветовой схемы NV12 в схему RGBA при отсутствии StretchRect или VideoProcessBlt является наиболее эффективным. Документ и пример кода будут обновлены после разрешения вопроса с использованием D3D11.

Благодарности

Выражаем благодарность Петеру Ларссону (Petter Larsson), Михелю Джеронимо (Michel Jeronimo), Томасу Итону (Thomas Eaton) и Петру Биалеки (Piotr Bialecki) за их помощь в создании этого документа.



 

Intel, эмблема Intel и Xeon являются товарными знаками корпорации Intel в США и в других странах.
*Прочие наименования и товарные знаки могут быть собственностью третьих лиц.
© Корпорация Intel, 2013. Все права защищены.

Использование стандартного GUI Unity* 3D в сочетании с ресурсами TouchScript

$
0
0

Линн Томпсон

Download PDF

Виджеты стандартного графического пользовательского интерфейса (GUI) Unity* 3D реагируют на касание так же, как на щелчок мыши c включенной функцией Windows* 8 «Перо и сенсорный ввод». В настоящее время для них невозможно настроить поддержку мультисенсорного ввода и жестов. В этой статье рассказывается о том, как TouchScript расширяет возможности стандартных объектов GUI с помощью функции Pan Gesture. Полученные элементы сохраняют внешний вид и функционал обычных виджетов интерфейса Unity 3D, но при этом поддерживают перетаскивание по экрану (функция Pan Gesture). В приведенном примере Unity 3D работает под управлением ОС Windows 8. Эта платформа обладает множеством средств для создания настраиваемых виджетов графического пользовательского интерфейса.

Создание примера

Пример начинается с создания нескольких сфер и кубов в поле обзора основной камеры сцены. Сцена проста и состоит из нескольких трехмерных объектов, которые можно изменять с помощью стандартных виджетов интерфейса Unity 3D. В основном интерфейс состоит из кнопок, горизонтальных ползунков и переключателей. После настройки каждого виджета мы располагаем в том же месте четырехугольник Unity 3D с аналогичными размерами. В настройках этого четырехугольника мы выбираем функцию TouchScript Pan Gesture, которая дает пользователям возможность перемещать данный объект. При перемещении четырехугольника его координаты присваиваются соответствующему виджету интерфейса Unity 3D. В результате мы получаем стандартный виджет интерфейса Unity 3D, который можно перемещать по экрану с помощью жестов TouchScript Pan Gesture. На рис. 1 показан пример такого интерфейса.

Three Unity* 3D standard GUI widgets
Рисунок 1. Три стандартных виджета графического интерфейса Unity* 3D

Добавление стандартного виджета

Для начала добавим три виджета графического интерфейса с помощью функции OnGUI. Первый виджет состоит из панели и нескольких кнопок, изменяющих масштаб примитивов на сцене. На втором виджете располагаются переключатели, которые присваивают геометрическим объектам фиксированные значения масштаба. Горизонтальные ползунки на третьем виджете вращают объекты на сцене по осям X, Y и Z. Расположим эти виджеты в левой, правой и центральной части верхней области экрана (эти объекты будут размещены с учетом разрешения 1024 x 768).

Существует множество справочных материалов, посвященных настройке стандартных элементов графического интерфейса в Unity 3D. Исходный код этих виджетов можно найти в прилагаемом проекте Unity 3D.

Настройка TouchScript

В данном примере четырехугольники Unity 3D добавляются в качестве целей TouchScript, упрощающих сенсорное управление стандартными виджетами графического интерфейса. Эти примитивы добавляются не через редактор Unity 3D, а программно. Ниже приведен код для создания четырехугольника, перемещающего левый виджет с кнопками:

Public Class:
.
.
.
private GameObject buttonQuad;//Создаем ресурс, обрабатывающий жесты
//Объявляем переменные для обработки жестов, с помощью которых будет изменяться
//стандартное положение GUI
private Vector3 buttonQuadStartPosition;
private float buttonQuadDeltaX;
private float buttonQuadDeltaY;
.
.
.
Start Function:
.
.
//Создаем ресурс, обрабатывающий жесты
buttonQuad = GameObject.CreatePrimitive (PrimitiveType.Quad);

//Приводим вектор положения ресурса в соответствие со стандартным расположением GUI
buttonQuad.transform.position = new Vector3(-0.7f,2.25f,-10.0f);

//Добавляем компоненты TouchScript, чтобы ресурс мог реагировать на сенсорные команды
buttonQuad.AddComponent ("PanGesture");
buttonQuad.AddComponent ("PanScript");

//Задаем начальное положение для ресурса, обрабатывающего сенсорные команды
buttonQuadStartPosition = buttonQuad.transform.position;
//Инициализируем переменные изменения положения
buttonQuadDeltaX = 0.0f;
buttonQuadDeltaY = 0.0f;

//Делаем ресурс для обработки сенсорных команд невидимым
MeshRenderer buttonQuadRenderer = (MeshRenderer) buttonQuad.GetComponent ("MeshRenderer");
buttonQuadRenderer.enabled = false;
.
.
.
Update function:
.
.
//Задаем переменные смены положения. Значение 235 — фактор масштабирования
//для разрешения 1024x768. В полноценном приложении этот фактор будет
//зависеть от разрешения, установленного программой или пользователем.
buttonQuadDeltaX = -235*(buttonQuadStartPosition.x - 	buttonQuad.transform.localPosition.x);
buttonQuadDeltaY = 235*(buttonQuadStartPosition.y - buttonQuad.transform.localPosition.y);
.
.
OnGUI function:
.
.
////////////////////// Меню с кнопками: начало ////////////////////////////////////
//Создаем стандартное окно GUI, расположение которого будет зависеть от положения //ресурса, обрабатывающего команды сенсорного управления
		GUI.Box(new Rect(10+buttonQuadDeltaX,10+buttonQuadDeltaY,240,160), "Button Menu");

//Создаем стандартную кнопку, расположение которой будет зависеть от положения //ресурса, обрабатывающего команды сенсорного управления

		if(GUI.Button(new Rect(20+buttonQuadDeltaX,40+buttonQuadDeltaY,220,20), "Increase Scale (4x Maximum)"))
		{
			//While increasing the cube scale, limit the cube scaling
			//to be between 0.25 and 4

			if (scale < 4.0)
			{
				scale += 0.1f;
				cube01.transform.localScale += (new Vector3(0.1f,0.1f,0.1f));
				cube02.transform.localScale += (new Vector3(0.1f,0.1f,0.1f));
				sphere01.transform.localScale += (new Vector3(0.1f,0.1f,0.1f));
				sphere02.transform.localScale += (new Vector3(0.1f,0.1f,0.1f));

			}
			if (scale == 4.0f)
			{
				maxscale = true;
				minscale = false;
				defaultscale = false;
			}
			else
			{
				maxscale = false;
			}
			if (scale == 0.25f)
			{
				minscale = true;
				maxscale = false;
				defaultscale = false;
			}
			else
			{
				minscale = false;
			}
			if (scale == 1.0f)
			{
				defaultscale = true;
				maxscale = false;
				minscale = false;
			}
			else
			{
				defaultscale = false;
			}
		}

		if(GUI.Button(new Rect(20+buttonQuadDeltaX,80+buttonQuadDeltaY,220,20), "Decrease Scale (0.25x Minimum)"))
		{
			if (scale > 0.25)
			{
			//While decreasing the cube scale, limit the cube scaling
			//to be between 0.25 and 4

				scale -= 0.1f;

				cube01.transform.localScale -= (new Vector3(0.1f,0.1f,0.1f));
				cube02.transform.localScale -= (new Vector3(0.1f,0.1f,0.1f));
				sphere01.transform.localScale -= (new Vector3(0.1f,0.1f,0.1f));
				sphere02.transform.localScale -= (new Vector3(0.1f,0.1f,0.1f));
			}
			if (scale == 4.0f)
			{
				maxscale = true;
				minscale = false;
				defaultscale = false;
			}
			else
			{
				maxscale = false;
			}
			if (scale == 0.25f)
			{
				minscale = true;
				maxscale = false;
				defaultscale = false;
			}
			else
			{
				minscale = false;
			}
			if (scale == 1.0f)
			{
				defaultscale = true;
				maxscale = false;
				minscale = false;
			}
			else
			{
				defaultscale = false;
			}
		}

		//Создаем кнопку для выхода из приложения
		if(GUI.Button(new Rect(20+buttonQuadDeltaX,120+buttonQuadDeltaY,220,20), "Exit Application"))
		{
			Application.Quit();
		}

		GUI.Label (new Rect(20,180,220,20),scale.ToString());
		////////////////////// Меню с кнопками: конец//////////////////////////////////
.
.
.

Сценарий PanScript программно добавляется к четырехугольникам и позволяет сдвигать или перетаскивать их. Работу этих функций можно увидеть в прилагаемом к данной статье примере или в примере под названием Everything, который поставляется вместе с пакетом TouchScript. Прилагаемое видео SGwTS.wmv демонстрирует работу виджетов интерфейса.

Возможности усовершенствования

Самым трудоемким процессом в создании этого примера является размещение четырех-угольника за виджетом GUI. Для этого приходится вручную рассчитывать точные значения x, y, и z и выравнивать положение четырехугольника, совмещая его со стандартным виджетом интерфейса Unity 3D. Кроме того, если пользователь установит разрешение экрана, отличающееся от 1024 x 768, это выравнивание окажется бесполезным. Более совершенным методом будет использование макетов интерфейса и автоматическое размещение четырехугольника с функцией TouchScript Pan Gesture, являющегося целью сенсорной команды.

Ширина четырехугольника в прилагаемом примере совпадает с шириной стандартного виджета графического интерфейса Unity 3D, а высота несколько отличается от него в большую сторону. В результате над виджетом и под ним появляются «поля», с помощью которых пользователь может перемещать виджет по экрану. Передвинув точку сенсорного управления, можно изменить положение четырехугольника относительно стандартного виджета GUI Unity 3D. Например, габариты четырехугольника можно совместить с виджетом внизу и по бокам, тем самым переместив предполагаемую точку управления в верхнюю часть виджета.

В приведенном примере пользователь может перемещать четырехугольник и связанный с ним виджет за любую точку (даже если она находится в габаритах виджета). Чтобы сделать активными только те области, которые выходят за пределы виджета, можно добавить еще один «блокирующий» четырехугольник. Размеры и положение этого четырехугольника должны совпадать с габаритами стандартного виджета графического интерфейса Unity 3D. Размещение блокирующего четырехугольника будет определяться другим четырехугольником, который изменяет положение стандартного виджета. Сама блокировка производится с помощью функции поведения TouchScript Untouchable. Такое поведения можно увидеть в примере Hit. Также это поможет избежать ошибки, при которой ползунок виджета двигется, когда передвигается горизонтальный ползунок.

С помощью сочетания блокирующих и активных четырехугольников можно добиться перемещения настраиваемых виджетов. В совокупности эти два вида четырехугольников позволяют перемещать отдельные компоненты графического интерфейса без изменения позиции самого виджета. Например, пользователь сможет по отдельности независимо перемещать горизонтальные ползунки, находящиеся на одной неподвижной базе. Также с помощью настраива¬емой функции Pan Gesture можно ограничить перемещение компонентов виджета рамками самого элемента интерфейса.

Порядок касания

Напомним, что один из настроенных нами виджетов GUI позволяет масштабировать геометрические примитивы, расположенные на сцене. Левый куб и правую сферу можно свободно перемещать по сцене, так как они были настроены с помощью функции Pan Gesture. В то же время куб и сфера, расположенные в центре сцены, не связаны с TouchScript и не реагируют на сенсорное управление. Из-за этого масштабируемые примитивы могут закрыть собой четырехугольники, предназначенные для перемещения виджетов интер¬фейса. Если пользователь увеличит размер примитива, а затем перетащит на него виджет, следующая сенсорная команда в этой области экрана затронет не виджет, а примитив. Разумеется, можно использовать ту же команду-жест, чтобы вывести примитив из области виджета и снова сделать этот элемент интерфейса доступным для сенсорного управления.

Тем не менее, если пользователь перетащит виджет на один из центральных примитивов, которые не поддерживают сенсорное управление, этот виджет будет заблокирован. Эту проблему можно решить, назначив через TouchScript примитиву поведение Untouchable с помощью кнопки Add Component («Добавить компонент») (см. рис. 2). Такая конфигурация позволит виджетам GUI, настроенным с помощью функции Pan Gesture, считывать сенсорные команды даже через геометрические примитивы. Эту схему можно наблюдать в прилагаемом видео: при перемещении виджета интерфейса на левый куб или правую сферу следующий жест передвигает соответствующий примитив. В то же время после переноса виджета на среднюю сферу (которой назначено поведение Untouchable) следующая команда перемещает сам виджет. Если из-за фактора масштабирования средний куб закроет собой не прошедшие рендеринг четырехугольники, перенесенный на этот куб виджет окажется заблокированным.

Во всех случаях, когда один из ресурсов сцены может помешать элементу интерфейса, следует назначить этому элементу поведение Untouchable. Для шутеров от первого лица (FPS), в которых используются виджеты интерфейса, описанные в этой статье, следует настраивать в TouchScript поведение Untouchable для каждого ресурса, с которым может войти в контакт главная камера.

Configuring a Unity* 3D scene asset to allow touch gestures
Рисунок 2. Настройка сенсорного управления для объектов, находящихся за ресурсом Unity* 3D

Последовательность рендеринга

Видимые компоненты виджетов GUI, упоминающихся в этой статье, созданы с помощью встроенной функции Unity 3D OnGUI. Они будут отображаться всегда, и настраивать отдельную камеру для рендеринга отдельного слоя виджетов не требуется.

Заключение

Функции пакета TouchScript хорошо подходят для совместной работы со стандартными интерфейсными виджетами Unity 3D. Сочетание этих функций позволяет создавать традиционные элементы интерфейса, которые можно перемещать по экрану с помощью стандартного управления. При этом нет необходимости разрабатывать виджеты с нуля и создавать геометрические объекты для работы с жестами TouchScript. Полученный в результате код отличается стабильностью работы на платформе Unity 3D для Windows 8. Описанный метод идеально подходит для быстрой разработки стандартных виджетов графического интерфейса с поддержкой перетаскивания.

Другие материалы по этой теме:

Об авторе

Линн Томпсон — специалист в области ИТ, более 20 лет проработавший в области компьютеризации предпринимательской и производственной сферы. Одной из первых его работ стало использование САПР для создания и редактирования чертежей контрольных приборов для энергосистем. Тогда же он получил степень бакалавра электротехники в Университете Небраски (г. Линкольн). В эпоху бума доткомов Линн занимался системным администрированием операционных систем, баз данных и приложений на различных платформах в одном из ИТ-интеграторов. Позже, после «краха доткомов», он участвовал во множестве проектов в роли ИТ-консультанта. Линн работал с компаниями, работающими в сфере легкой промышленности, а также нефтегазовой и оборонной индустрии. Сейчас он вновь вернулся к своей специальности и работает инженером-энергетиком. Линн получил магистерскую степень инженера со специализацией в области управления техническими системами (также в Университете Небраски).

Intel, эмблема Intel и Xeon являются товарными знаками корпорации Intel в США и в других странах.
*Прочие наименования и товарные знаки могут быть собственностью третьих лиц.
© Корпорация Intel, 2013. Все права защищены.

Реализация последовательностей жестов в Unity* 3D с помощью библиотеки TouchScript

$
0
0

Download PDF

Линн Томпсон

Если вы настраиваете цели касания, которые управляют элементами на сцене, важно уменьшить область управления, насколько это возможно. Так вы сможете максимально расширить область экрана на устройстве Ultrabook™, предназначенную для отображения привлекательного контента. Для этого можно настроить цели касания так, чтобы они обрабатывали различные комбинации жестов. Таким образом, количество целей касания на экране сократится до минимума. Например, два элемента интерфейса, один из которых заставляет пушку стрелять, а второй — вращаться, можно заменить на один, позволяющий выполнять оба действия одним непрерывным касанием.

В этой статье я расскажу о том, как настроить сцену для управления контроллером от первого лица при помощи целей касания. Прежде всего необходимо настроить цели касания для базовой позиции контроллера и вращения, а затем расширить набор их функций. Послед¬него можно достичь за счет существующих элементов интерфейса, не добавляя новые объекты. Сцена, которая у нас получится, продемонстрирует широкие возможности Unity 3D в ОС Windows* 8 как платформы для обработки различных последовательностей жестов.

Настройка сцены в Unity* 3D

Для начала необходимо настроить сцену. Для этого импортируем в Unity* 3D ресурс ландшафта в формате .fbx с горами и деревьями, экспортированный из Autodesk 3D Studio Max*. Поместим контроллер в центре ландшафта.

Установим показатель глубины основной камеры (она входит в состав контроллера) на уровень -1. Создадим отдельный элемент интерфейса камеры с поддержкой ортогональной проекции, шириной 1 и высотой 0,5, а также флагами Don’t Clear. Затем создадим слой GUIWidget и сделаем его маской интерфейса камеры.

Расположим основные элементы интерфейса, управляющие контроллером, на сцене в поле обзора ортогональной камеры. Добавим сферу для каждого пальца левой руки. Сфера мизинца заставляет контроллер двигаться влево, сфера безымянного пальца — вперед, среднего пальца — вправо, а указательного пальца — назад. Сфера большого пальца позво-ляет прыгать и запускать сферические снаряды под углом 30 градусов по часовой стрелке.

Для элемента интерфейса правой руки создадим куб (квадрат в ортогональной проекции). Настроим для этого куба поддержку жеста сдвига и привяжем его к скрипту MouseLook.cs. Этот элемент интерфейса обеспечивает такие же возможности, как и сенсорная панель устройства Ultrabook.

Эти элементы интерфейса расположим вне поля обзора основной камеры, а в качестве слоя установим GUIWidget. На рис. 1 можно увидеть, как элементы интерфейса позволяют запускать снаряды и управлять позицией контроллера на сцене.


Рисунок 1. Сцена контроллера от первого лица с ландшафтом и запущенными сферическими снарядами.

На этой сцене снаряды, запущенные из контроллера, пролетают сквозь деревья. Чтобы это исправить, необходимо добавить сетку или коллайдер к каждому дереву. Другая проблема на этой сцене — низкая скорость движения вперед — возникает при попытке посмотреть вниз с помощью сенсорной панели одновременно с движением вперед с помощью сферы безымянного пальца. Чтобы устранить эту неполадку, можно ограничить угол обзора при взгляде вниз, когда удерживается кнопка движения вперед.

Множественные касания

На базовой сцене находится контроллер от первого лица, который запускает снаряды под определенным углом по направлению от центра (см. Рисунок 1). По умолчанию установлен угол в 30 градусов по часовой стрелке.

Настроим сцену для поддержки множественных касаний, выполняемых чаще, чем установленный период, изменим угол запуска снарядов и попробуем запустить снаряд. В этом случае можно настроить угол на возрастание в геометрической прогрессии в зависимости от числа касаний при помощи переменных типа float в скрипте для сферы большого пальца слева. Эти переменные регулируют угол и время с момента запуска последнего снаряда:

	private float timeSinceFire = 0.0f;
	private float firingAngle = 30.0f;

Далее настроим цикл Update в скрипте для сферы большого пальца так, чтобы угол запуска снарядов снижался, если касания сферы большого пальца выполняются чаще, чем раз в полсекунды. В том случае, если касания последуют реже, чем раз в полсекунды, или угол запуска снарядов снизится до 0 градусов, величина угла запуска снарядов вернется к показателю 30 градусов. Получится следующий код:

		timeSinceFire += Time.deltaTime;

			if(timeSinceFire <= 0.5f)
			{
				firingAngle += -1.0f;

			}
			else
			{
				firingAngle = 30.0f;
			}

			timeSinceFire = 0.0f;

			if(firingAngle <= 0)
			{
				firingAngle = 30;
			}


			projectileSpawnRotation = Quaternion.AngleAxis(firingAngle,CH.transform.up);

Такой код произведет эффект обстрела, при котором непрерывные касания приведут к запуску снарядов под постоянно уменьшающимся углом (см. рис. 2). Этот эффект можно позволить настраивать пользователям либо сделать доступным при соблюдении определенных условий в игре или режиме симуляции.


Рисунок 2. Непрерывные касания приводят к изменению направления запуска снарядов.

Масштабирование прокруткой

Мы настроили квадрат в правой нижней части экрана на рис. 1 на работу в режиме, аналогичном сенсорной панели на клавиатуре. При жесте сдвига квадрат не двигается, а поворачивает основную камеру сцены вверх, вниз, влево и вправо с помощью скрипта контроллера MouseLook. Аналогичным образом жест масштабирования (схожий со растягиванием/сжатием на других платформах) приводит не к масштабированию квадрата, а к изменению поля зрения основной камеры, благодаря которому пользователь может приблизить или отдалить объект на основной камере (см. Рисунок 3). Настроим контроллер таким образом, чтобы жест сдвига сразу после масштабирования возвращал поле зрения камеры к значению по умолчанию 60 градусов.

Для этого нужно запрограммировать логическую переменную (panned) и переменную типа float, чтобы они отмечали время, прошедшее от последнего жеста масштабирования:

	private float timeSinceScale;
	private float timeSincePan;
	private bool panned;

Установим для переменной timeSinceScale значение 0.0f при выполнении жеста масштабирования, а для переменной panned — значение True при выполнении жеста сдвига. Поле зрения основной камеры сцены настраивается в цикле Update, как можно увидеть в скрипте для прямоугольника-сенсорной панели:

		timeSinceScale += Time.deltaTime;
		timeSincePan += Time.deltaTime;

		if(panned && timeSinceScale >= 0.5f && timeSincePan >= 0.5f)
		{
			fieldOfView += 5.0f;
			panned = false;
		}

		if(panned && timeSinceScale <= 0.5f)
		{
			fieldOfView = 60.0f;
			panned = false;
		}

		Camera.main.fieldOfView = fieldOfView;

Установим для переменной timeSinceScale значение 0.0f при выполнении жеста масштабирования, а для переменной panned — значение True при выполнении жеста сдвига. Поле зрения основной камеры сцены настраивается в цикле Update, как можно увидеть в скрипте для прямоугольника-сенсорной панели:

	private void onPanStateChanged(object sender, GestureStateChangeEventArgs e)
    {
        switch (e.State)
        {
            case Gesture.GestureState.Began:
            case Gesture.GestureState.Changed:
                var target = sender as PanGesture;
                Debug.DrawRay(transform.position, target.WorldTransformPlane.normal);
                Debug.DrawRay(transform.position, target.WorldDeltaPosition.normalized);

                var local = new Vector3(transform.InverseTransformDirection(target.WorldDeltaPosition).x, transform.InverseTransformDirection(target.WorldDeltaPosition).y, 0);
                targetPan += transform.InverseTransformDirection(transform.TransformDirection(local));

                //if (transform.InverseTransformDirection(transform.parent.TransformDirection(targetPan - startPos)).y < 0) targetPan = startPos;
                timeSincePan = 0.0f;
				panned = true;
				break;

        }

    }

	private void onScaleStateChanged(object sender, GestureStateChangeEventArgs e)
    {
        switch (e.State)
        {
            case Gesture.GestureState.Began:
            case Gesture.GestureState.Changed:
                var gesture = (ScaleGesture)sender;

                if (Math.Abs(gesture.LocalDeltaScale) > 0.01 )
                {
					fieldOfView *= gesture.LocalDeltaScale;

					if(fieldOfView >= 170){fieldOfView = 170;}
					if(fieldOfView <= 1){fieldOfView = 1;}

					timeSinceScale = 0.0f;


                }
                break;
        }
    }


Рисунок 3. Основная камера сцены с изображением, приближенным при помощи прямоугольника сенсорной панели справа

Нажатие, отпускание с щелчком

Если нажать и отпустить сферу мизинца, а затем в течение полсекунды провести по ней, можно увеличить горизонтальную скорость контроллера.

Для поддержки этой функции добавим переменную типа float и логическую переменную, которые позволят отмечать время с жестов отпускания сферы мизинца и жеста щелчка по ней:

	private float timeSinceRelease;
	private bool flicked;

При первоначальной установке сцены я предоставил скрипту сферы мизинца доступ к скрипту контроллера InputController, чтобы сфера мизинца слева могла заставлять контроллер двигаться влево. Переменная, управляющая горизонтальной скоростью контроллера, находится не в скрипте InputController, а в скрипте CharacterMotor. Передать скрипт сферы левого мизинца скрипту CharacterMotor можно аналогичным образом:

		CH = GameObject.Find("First Person Controller");
		CHFPSInputController = (FPSInputController)CH.GetComponent("FPSInputController");
		CHCharacterMotor = (CharacterMotor)CH.GetComponent ("CharacterMotor");

Функция onFlick в нашем скрипте лишь позволяет установить для логической переменной flicked значение, равное True.

Функция Update в скрипте вызывается один раз за кадр и изменяет движение контроллера по горизонтали следующим образом:

		if(flicked && timeSinceRelease <= 0.5f)
		{
			CHCharacterMotor.movement.maxSidewaysSpeed += 2.0f;
			flicked = false;
		}

		timeSinceRelease += Time.deltaTime;
	}

Благодаря этому коду можно увеличивать скорость движения по горизонтали. Для этого нужно нажать и отпустить сферу мизинца, а затем провести по ней в течение полсекунды. Настроить замедление движения по горизонтали можно различными способами. Например, для этого можно использовать нажатие и отпускание сферы указательного пальца, а затем щелчок по ней. Обратите внимание на то, что способ CHCharacterMotor.movement содержит не только параметр maxSidewaysSpeed, но также gravity, maxForwardsSpeed, maxBackwardsSpeed, и другие. Использование разнообразных жестов библиотеки TouchScript и объектов, которые обрабатывают жесты, в сочетании с этими параметрами обеспечивает широкие возможности и различные стратегии разработки сенсорных интерфейсов в сценах Unity 3D. При создании сенсорных интерфейсов для таких приложений можно пробовать различные варианты и выбирать наиболее эффективные и эргономичные.

Проблемы с последовательностями жестов

Последовательности жестов, которые мы использовали в этой статье, существенно зависят от функции Time.deltaTime. Я применяю эту функцию в сочетании с разными жестами до и после того, как функция определит действие. Две основные проблемы, с которыми я столкнулся при настройке этих случаев, заключаются в величине временного интервала и характере жестов.

Временной интервал

При написании этой статьи я использовал интервал в полсекунды. Если я выбирал интервал равный одной десятой секунды, устройство не могло распознать последовательности жестов. Хотя мне казалось, что темп касаний был достаточно высок, это не приводило к нужным действиям на экране. Возможно, это связано с задержками в работе устройства и ПО, поэтому рекомендую при разработке последовательностей жестов учитывать производительность целевой платформы.

Жесты

При работе над этим примером я изначально собирался использовать жесты масштаби-рования и сдвига, а затем касания и щелчка. Масштабирование и сдвиг работали правильно, но перестали, как только я добавил жест касания. Хотя мне удалось настроить последовательности из жестов масштабирования и сдвига, она не очень удобна для пользователя. Более удачным вариантом будет настроить другую цель касания в элементе интерфейса на обработку жестов касания и проведения после масштабирования и сдвига.

В этом примере я использую интервал времени в полсекунды, чтобы определять, выпол-нено действие или нет. Также можно настроить несколько временных интервалов, хотя это и приведет к усложнению интерфейса. Например, последовательность жестов нажатия и отпускания и следующего в течение полсекунды за ними проведения может увеличивать скорость движения по горизонтали, а аналогичная последовательность с интервалом от полсекунды до секунды — снижать скорость. Используя временные интервалы таким образом, можно не только более гибко настроить пользовательский интерфейс, но и добавить на сцену скрытые секреты.

Заключение

В этой статье я настроил сцену с различными последовательностями жестов в Unity* 3D с помощью библиотеки TouchScript на устройстве Ultrabook с ОС Windows 8. Цель реализа-ции этих последовательностей — сократить область экрана, с помощью которой пользова-тель управляет приложением. В этом случае можно выделить большую площадь экрана для показа привлекательного контента.

В тех случаях, когда последовательности жестов не работали правильно, нам удавалось найти подходящее альтернативное решение. Одной из задач настройки было добиться правильной работы последовательности действий с помощью функции Time.deltaTime на имеющемся устройстве. Таким образом, сцена, которую мы создали в Unity 3D для этой статьи, подтверждает жизнеспособность ОС Windows 8 на устройствах Ultrabook как платформы для разработки приложений с использованием последовательностей жестов.

Другие материалы по данной теме

Об авторе

Линн Томпсон — специалист в области ИТ, более 20 лет проработавший в области компьютеризации предпринимательской и производственной сферы. Одной из первых его работ стало использование САПР для создания и редактирования чертежей контрольных приборов для энергосистем. Тогда же он получил степень бакалавра электротехники в Университете Небраски (г. Линкольн). В эпоху бума доткомов Линн занимался системным администрированием операционных систем, баз данных и приложений на различных платформах в одном из ИТ-интеграторов. Позже, после «краха доткомов», он участвовал во множестве проектов в роли ИТ-консультанта. Линн сотрудничал с компаниями, работающими в легкой промышленности, а также нефтегазовой и оборонной индустрии. Сейчас он вернулся к своей специальности и работает инженером-энергетиком. Линн получил магистерскую степень инженера со специализацией в области управления техническими системами (также в Университете Небраски).



 

Intel, эмблема Intel, Ultrabook и VTune являются товарными знаками корпорации Intel в США и в других странах.
*Прочие наименования и товарные знаки могут быть собственностью третьих лиц
© Корпорация Intel, 2014. Все права защищены.


Оптимизация игр для Ultrabook™

$
0
0

Download as PDF

Автор: Ли Бэмбер (Lee Bamber)

1. Введение

Недавно я занимался подготовкой игрового движка, над которым я работал для конференции Games Developer Conference. Учитывая значимость этого мероприятия, было важно, чтобы игра достаточно быстро работала на трех устройствах, которые были у меня под рукой: от современного Ultrabook™до системы на два поколения старше.

В этой статье вы узнаете, как увеличить скорость трехмерной игры, и поймете, на что нужно обращать внимание при переносе своих приложений на Ultrabook. Будь вы опытным разработчиком игр, или же программирование для вас просто хобби — в любом случае вы наверняка понимаете важность высокой производительности. Игра, идущая плавно и с высокой кадровой скоростью, будет восприниматься как намного более качественная и профессиональная, нежели игра, спотыкающаяся на несчастных пяти кадрах в секунду. Никакая сколь угодно захватывающая графика не скроет тот факт, что игра просто тормозит, дергается на экране (из-за постоянного рассогласования с вертикальной разверткой монитора), а о сколько-нибудь приемлемой реализации физики и говорить нечего. В этом примере с настоящим игровым проектом я постараюсь рассказать о возможных проблемах при переносе игры на разные платформы и способах решения таких проблем.


Рисунок 1. Если у вашей игры красивые снимки экрана, это поможет поднять продажи, но за низкую кадровую скорость расплачиваться придется уже вам!

В этой статье описывается несколько распространенных причин потери производительности. Разработчикам игр предлагается полезная информация по переносу высококачественных трехмерных игр высшего уровня на Ultrabook с нужным уровнем производительности. Для работы таких игр с комфортной скоростью зачастую требуется мощный дискретный видеоадаптер, а нагрузка на GPU весьма высока. Здесь может пригодиться понимание архитектурных различий между выделенными и интегрированными GPU, но лучший способ повышения производитель¬ности графической подсистемы — анализ конвейера обработки графики для выявления узких мест с дальнейшей оптимизацией этих областей без снижения качества изображения.

Требуется базовое понимание вызовов графических API в целом, знание компонентов, образую¬щих типичные трехмерные игры, а также некоторое знание или опыт использования Ultrabook.

2. Почему важна производительность?

Число игроков на рынке приложений и игр постоянно растет, поэтому уникальные полезные особенности ваших продуктов становятся все более важными для коммерческого успеха, а хорошая производительность сейчас рассматривается не как некий желаемый результат, а как строго необходимый атрибут любой программы или игры. Современные пользователи даже не будут считать вашу игру законченной, если она не будет работать достаточно гладко и комфортно на их устройстве, и не будут возвращаться к игре, если с самого начала о ней сложится отрицательное впечатление.

Это обстоятельство крайне важно. Если же учесть еще и быстрый рост рынка мобильных телефонов, планшетов и ноутбуков, окажется, что производительность имеет первостепенное значение. Вы можете удовольствоваться адаптацией игры к Ultrabook, учитывая высокую вычислительную мощность этих устройств по сравнению с остальными, но пользователи всегда будут требовать высших стандартов и ожидать максимального удобства.


Рисунок 2. Ultrabook раскрывает немалую мощь в умелых руках.

С точки зрения выработки полезных навыков все, что вы сейчас можете сделать для оптимиза¬ции и улучшения кода игры, вы сможете применять и во всех дальнейших проектах, повысив свою квалификацию разработчика.

3. Зачем оптимизировать?

Многие разработчики используют для создания и тестирования трехмерных игр настольные ПК. Из-за наличия выделенного графического адаптера иногда возникает ощущение безграничности вычислительных ресурсов, а это приводит к появлению в коде игр таких алгоритмов и шейдеров, которые работают на самом пределе возможностей GPU. Если же запустить такую игру на менее мощной платформе, она может работать гораздо медленнее. Ultrabook — это исключительно мощные мобильные устройства, но по «грубой силе» обработки графики они все же немало уступают мощным современным графическим процессорам. Кроме того, Ultrabook предназначены для мобильного использования, поэтому может оказаться, что в процессе игры Ultrabook будет работать от аккумулятора: конвейер отрисовки должен быть очень эффективным, чтобы избежать быстрого израсходования всей электроэнергии. Все эти факты необходимо учитывать при создании игровой графики.


Рисунок 3. Варианты использования успешного приложения.

При разработке приложений программисты обычно двигаются «сверху вниз»: сначала создают приложение, а потом стараются адаптировать его для как можно большего количества устройств в рамках отведенного на разработку времени.

Простейший подход — разрабатывать игру на Ultrabook, а затем переносить ее на настольный компьютер с выделенным графическим адаптером: при этом для переноса игры не требуется практически никаких действий. Тем не менее, вашими конкурентами могут стать игры, для которых планка качества установлена гораздо выше. Преимущество у такого подхода только одно: вы с самого начала учитываете время работы системы от аккумулятора и с большей вероятностью разработаете игру, которая будет снижать потребление ресурсов в определенные моменты, например во время заставок или на экранах настроек. Более распространенный подход — разработка на настольном ПК с последующей оптимизацией для Ultrabook: в этом случае, как правило, достигается более высокое качество, поскольку изначально концепция разработки нацелена именно на это.

4. С настольного компьютера на Ultrabook: пример битвы за производительность.

Мой рассказ начинается за много недель до крупного мероприятия — конференции GDC. Моя игра работает на достаточно современном графическом адаптере стоимостью около двухсот долларов с интерфейсом PCI Express* 3.0. При наивысшем качестве графики моя игра выдавала порядка 60 кадров в секунду. Это был вовсе не сверхмощный игровой компьютер, но я мог запускать любые трехмерные игры с самым высоким качеством графики без сколько-нибудь ощутимых проблем с производительностью. В моем распоряжении было шесть ядер, 6 гигабайт системной памяти и массив крайне быстрых твердотельных дисков. Я знал, что на конфе¬ренции в моем распоряжении не будет настольных компьютеров, и не собирался везти с собой через полмира здоровенный стационарный компьютер. Следующим по мощности устройством был мой Ultrabook, который вполне годился для поездки: именно его я и решил взять с собой.


Рисунок 4. GDC 2014 — одна из крупнейших конференций разработчиков.

Мой Ultrabook оснащен процессором Intel® Core™ 4-го поколения с графикой Intel® HD Graphics 4000™: это мое любимое устройство, когда я не на работе. Результаты первого теста оказались, прямо скажем, катастрофическими: кадровая скорость упала настолько, что вообще вся затея начала казаться мне чересчур смелой. Используемая сборка трехмерного игрового движка широко использовала шейдеры и отрисовку множества объектов одновременно. Игра просто глотала циклы CPU, как конфетки, стараясь дотянуться до всех доступных ресурсов. Разумеется, с таким подходом моей игре было крайне далеко до экономных и дружественных приложений, подходящих мобильным устройствам.

Несмотря на очевидную наглость моего плана, я все же знал, что современные Ultrabook — это вполне мощные игровые системы, при правильном подходе они не отстают от настольных компьютеров по производительности, существенно опережая их в удобстве. Кроме того, мне приходилось играть во множество игр на Ultrabook, поэтому я знал, что моя задача вполне выполнима: я решил добиться от игры 60 кадров в секунду.

Написанием кодов я занимаюсь очень давно: я научился программировать задолго до появле-ния анализаторов производительности и графических отладчиков, поэтому основной способ обнаружения узких мест состоял в том, чтобы удалять огромные фрагменты движка до тех пор, пока производительность не повысилась бы. Затем, возвращая по очереди важные фрагменты кода обратно в игру, я мог определить, какие части движка работали медленнее всего. После определения узких мест (а просто взять и удалить их было невозможно) начинался осторожный процесс снижения ресурсоемкости компонентов. Примеры такого подхода: исключение обычного расчета карты в шейдерах для пикселей за пределом определенного расстояния от игрока; пропуск вызовов обновления искусственного интеллекта в каждом втором цикле, чтобы снизить издержки на эти процессы. Это незначительные улучшения, но в сумме они дают весомый эффект, и уже довольно скоро игровой движок стал работать намного быстрее практически без потерь в качестве изображения.

Программистам, для которых отладка производительности является относительно новой областью, я искренне рекомендую не прибегать к такому способу обнаружения узких мест. Можно исполь¬зовать множество инструментов, помогающих выявлять проблемы производитель¬ности вашего приложения, причем эти средства не только выявляют узкие места, но и указывают на природу проблемы. Один из таких бесплатных инструментов — пакет Intel® Graphics Performance Analyzers. Это решение профилирует ваше приложения в ходе его работы и предоставляет снимок всей деятельности программы: что именно она делает и сколько времени это занимает. При демонстрации игры на конференции я обнаружил несколько проблем, которые я впослед¬ствии исправил, чтобы повысить производительность и плавность игры.


Рисунок 5. До и после: снимки экрана игры до и после оптимизации.

Как видно на рисунке 5, мне удалось повысить скорость с 20 до 62 кадров в секунду при крайне незначительных различиях в качестве графики. На снимке экрана «после» удалено мощное динамическое освещение вокруг игрока, а также применен менее агрессивный шейдер фрагментов.

«Голодные» шейдеры

Мы быстро определили, что наибольшее снижение производительности было на этапе отрисовки графики.


Рисунок 6. Панель метрики производительности из начальной версии с низкой кадровой скоростью.

Как видно на рисунке 6, горизонтальная строка, отмеченная как «Отрисовка», потребляла большую часть доступных циклов. При более подробном анализе выяснилось, что крайне много ресурсов расходовалось на отрисовку объектов на экране. После этого стало понятно, что сцена, состоящая из сотен тысяч полигонов (причем для каждого используется ресурсо-емкий шейдер фрагментов), резко снижает производительность. Но насколько именно? Добавив к шейдерам параметры MEDIUM и LOWEST, а также несколько ограничив «аппетит» отрисовки пикселей, нам удалось добиться шестикратного повышения производительности.

Чтобы определить, как на самом деле работают параметры LOWEST и MEDIUM, сначала нужно было определить «наименьший общий знаменатель» для компонентов игры. Определив, какие компоненты для игры совершенно необходимы, и убрав все остальное, я создал в шейдере новый параметр LOWEST. Были удалены почти все элементы: все тени, обычные карты, динамическое освещение, перекрытие текстур, зеркальное отображение и т. п. Поскольку я «обрубил все под корень», можно было запустить игру и понять, какой производительности можно добиться на Ultrabook в наилучшем для этого шейдера случае. Сравнив снимки экрана с параметром HIGHEST и с параметром LOWEST, я обнаружил самый важный отсутствующий компонент, который вызвал бы отрицательную реакцию пользователей при снижении качества графики. Шейдер содержал тени и перекрытия текстур. При отсутствии каждого из этих элементов качество изображения резко снижалось. Вернуть перекрытие текстур можно было без особых затрат. Чтобы протестировать, насколько больше ресурсов будет потреблять игра, я просто вернул код шейдера для этого элемента и снова запустил игру. С тенями все было гораздо сложнее: это касалось и их создания в другой части игрового движка, и их использования в самом шейдере. Учитывая важность этого аспекта для сохранения высокого качества изображения, я потратил достаточное количество времени на изучение различных подходов и наконец, обнаружил достаточно быстрое решение, которое я описываю ниже.

Создать параметр MEDIUM для шейдера было довольно просто: нужно было написать шейдер, промежуточный по качеству между самым высоким и самым низким качеством графики, но всегда склоняясь в пользу производительности. Цель этого параметра — достижение всех преимуществ по скорости, присущих параметру LOWEST, но с добавлением наименее ресурсоемких эффектов, таких как фонарик игрока, динамическое освещение и чуть более высокое качество теней.

Если бы я просто снизил качество всех графических элементов до минимума в режиме LOWEST, мне бы за один прием удалось добиться наибольшей производительности, но игроки относятся к плохой графике столь же отрицательно, как к плохой производительности. Я постарался сохранить 90 % качества изображения на самых высоких настройках и выделить элементы, которые можно было бы исключить или ограничить. Так мне удалось добиться значительного повышения скорости с минимальными потерями качества. С 5 кадров в секунду мне удалось перешагнуть за 40 — неплохое достижение.

Чтобы узнать, почему ваша игра для настольного ПК так медленно работает на Ultrabook, я настоятельно рекомендую разложить по полочкам весь ваш конвейер отрисовки графики и постараться понять, на что тратится время. Для этого можно использовать мой «хирурги-ческий» метод и удалять целые уровни функциональности до тех пор, пока не повысится скорость, или же применить более современный подход и задействовать средства анализа производительности. Какой бы метод вы ни выбрали, после обнаружения проблемы следующая задача — придумать такое решение, при котором возрастает скорость работы, но не снижается качество изображения.

Вот некоторые методики, которые я применил для устранения обнаруженных узких мест производительности.

Тени без затрат

Чтобы решить упомянутую выше проблему с тенями, мне пришлось найти альтернативные решения методики так называемых «каскадных карт теней». Я не стану подробно описывать эту методику, но дополнительные сведения о ней вы найдете здесь: http://msdn.microsoft.com/en-gb/library/windows/desktop/ee416307(v=vs.85).aspx. Работает она примерно так: в поле зрения камеры игрока немедленно рисуются четыре цели отрисовки с тенями всех объектов, каждая с разным уровнем детализации.


Рисунок 7. Каскадные карты теней — представление отладчика игрового движка.

Затем шейдер получает инструкции изменить цвет того или иного пикселя на экране на основании того, попадает ли этот пиксель в область предварительно вычисленной тени. Проблема в том, что это очень ресурсоемкий эффект шейдера, потребляющий много видеопамяти. Ниже приведен код шейдера фрагментов. Обратите внимание, что несколько раз используется ветвление IF, а производительность некоторых графических процессоров ощутимо падает для каждой новой ветви IF. В крайних случаях некоторые системы вычисляют все изменения каждого выходного пикселя, то есть ветвление кода не дает вообще никаких преимуществ.

fPercentLit = 0.0f;
if ( iCurrentCascadeIndex==0 )
{
fPercentLit += vShadowTexCoord.z > tex2D(DepthMap1,float2(vShadowTexCoord.x,vShadowTexCoord.y)).x ? 1.0f : 0.0f;
}
else
{
	if ( iCurrentCascadeIndex==1 )
	{
		fPercentLit += vShadowTexCoord.z > tex2D(DepthMap2,float2(vShadowTexCoord.x,vShadowTexCoord.y)).x ? 1.0f : 0.0f;
	}
	else
	{
		if ( iCurrentCascadeIndex==2 )
		{
			fPercentLit += vShadowTexCoord.z > tex2D(DepthMap3,float2(vShadowTexCoord.x,vShadowTexCoord.y)).x ? 1.0f : 0.0f;
		}
		else
		{
			if ( iCurrentCascadeIndex==3 && vShadowTexCoord.z<1.0 )
			{
				fPercentLit += vShadowTexCoord.z > tex2D(DepthMap4,float2(vShadowTexCoord.x,vShadowTexCoord.y)).x ? 1.0f : 0.0f;
			}
		}
	}
}

Необходимо снизить нагрузку на видеопамять и зависимость от ветвей IF. Решение (одно из возможных) — создать единую очень большую текстуру теней и поместить в эту цель отрисовки результаты самого низкого уровня детализации.

Я написал новую, менее ресурсоемкую методику, которая просто считывала данные из этой большой текстуры без всяких ветвлений. Особенности такого подхода выходят за рамки этой статьи, но общий смысл в том, что сначала нужно выявить причину снижения производительности, а затем придумать такое решение, которое работало бы быстрее, но давало бы изображение такого же качества.

Поддержание качества изображения

При оптимизации игрового движка необходимо следить за сохранением качества изображения на всех этапах разработки игры. Нетрудно просто вырезать все красивые, но ресурсоемкие эффекты ради прироста производительности, но лучше рассматривать каждую проблему как возможность повышения производительности с сохранением требуемого качества картинки. При этом вы не только добьетесь желаемых результатов, но и игра будет еще лучше работать на мощных компьютерах, что дает вам возможность добавлять новые компоненты и элементы при дальнейшем развитии игры.


Рисунок 8. Сравнение игровой сцены при чрезмерном снижении качества изображения.

Разрабатывая игру на настольном ПК, вы наверняка захотите использовать сложные шейдеры фрагментов для создания разнообразных эффектов поверхностей. Если просто удалить их из игры, изображение ухудшится настолько, что станет совсем не похоже на исходное. Для сохранения целостности игры важно поддерживать однородный визуальный стиль для всех методик работы шейдеров. Новые пользователи могут сначала впечатлиться великолепным снимком экрана в интернет-обзоре игры, но будут разочарованы, когда запустят вашу игру и увидят на экране что-то совсем иное.

Во всех возможных случаях старайтесь применять методики, воспроизводящие эффекты ресурсоемких шейдеров с меньшими затратами. Например, можно использовать заранее наложенные текстуры или применять ресурсоемкие пиксельные эффекты только в самой близкой к игроку области.

Уделяйте больше внимания своим близким.

Звучит как хороший жизненный совет, но это еще и неплохая стратегия для оптимизации шейдеров на Ultrabook. Одной инструкции ветвления IF достаточно, чтобы определить, насколько близко к игроку находится вычисляемый пиксель. Если близко, то можно, как и ранее, использовать ресурсоемкий эффект пиксельного шейдера, но если далеко, можно использовать намного менее затратную имитацию или заранее просчитанные эффекты.


Рисунок 9. Эффект смешения в действии: обратите внимание на эффекты карты нормалей вблизи.

Вместе с описанной выше методикой также можно использовать смешение, а если добавить одну дополнительную ветвь IF, можно проверять, на каком расстоянии между двумя точками удаления от игрока находится каждый пиксель. Вблизи игрока (ближе первой точки) можно использовать ресурсоемкие эффекты, а за пределами второй точки можно применять эффекты попроще. Между первой и второй точками следует вычислять смешанный эффект, промежуточный между первым и вторым подходами. Важно, чтобы расстояние между этими двумя точками было сравнительно небольшим, чтобы избежать удвоенного расхода вычислительных ресурсов. Расстояние смешения должно быть настолько большим, чтобы переход остался не замеченным игроком. В приведенном ниже фрагменте кода каждый пиксель обрабатывается в зависимости от его расстояния от камеры обзора: если расстояние составляет от 400 до 600 единиц, вычисляются обе ветви кода.

float4 lighting = float4(0,0,0,0);
float4 viewspacePos = mul(IN.WPos, View);
if ( viewspacePos.z < 600.0f )
{
	// work out surface normal per pixel
	lighting = lit(pow(0.5*(dot(Ln,Nb))+0.5,2),dot(Hn,Nb),24);
}
if ( viewspacePos.z > 400.0f )
{
	// cheapest directional lighting
	lighting = lerp ( lighting, cheaplighting, min((viewspacePos.z-400.0f)/200.0f,1.0f) );
}

Результат получается на удивление хороший, а переход очень плавный и совершенно незаметный при отрисовке. При этом в 90 % сцены теперь используется весьма экономный с точки зрения нагрузки эффект, поэтому скорость игры значительно повышается.

Обработка на лету и предварительная обработка

На оптимизацию игры было затрачено немало времени и усилий, но все равно скорость пока еще отставала от заветных 60 кадров в секунду. Баланс между качеством изображения и возможным повышением производительности был достигнут, но другие части игрового движка, помимо системы шейдеров, образовывали немалый объем вычислительных издержек и снижали скорость игры.

В игровом движке была внутренняя система оценки производительности, которая грубо измеряла каждый крупный сегмент всего конвейера отрисовки. В дополнение к графике, движок также измеряет ресурсы, затрачиваемые на вычисление искусственного интеллекта, физики, оружия, отладки, объемного света и т. п. Один из счетчиков следил за отрисовкой травы в реальном времени: такая методика использовалась в игре для формирования иллюзии бесконечного травяного поля. Снизив ресурсоемкость обработки графики, мы обнаружили, что относительная ресурсоемкость процесса отрисовки травы резко возросла и стала следующей по величине во всем конвейере. При оптимизации нужно всегда следить за такими пиками, а если обнаружится, что они используют слишком много игровых циклов, нужно повнимательнее их изучить. Следует знать, какой уровень потребления ресурсов для того или иного компонента графики является разумным, а какой — явно чрезмерным. В данном случае трава явно не должна была расходовать свыше 10 % всех циклов, особенно с учетом крайнего их дефицита. На настольном ПК это было не столь заметно, но на Ultrabook это существенно повлияло на скорость. Помимо роста потребления ресурсов было замечено, что при игре, всякий раз, когда перед игроком происходила отрисовка новой травы, кадровая скорость резко падала, поскольку пиковая нагрузка мешала обычному плавному выполнению игры.


Рисунок 10. Зеленая, зеленая трава: отрисовка травы в реальном времени может оказаться чрезвычайно ресурсоемкой.

Решение состояло в том, чтобы перенести всю систему отрисовки травы на этап предвари-тельной обработки, происходящий еще до запуска самой игры. Вместо формирования травы «на лету» ее просто переместили перед игроком, причем на глаз практически никакой разницы не было заметно. Теперь ничего не требовалось создавать на лету: траву нужно было всего лишь передвигать. Мой Ultrabook вздохнул с облегчением, поскольку удалось высвободить очень много драгоценных циклов CPU для всего остального игрового движка. Я, в свою очередь, тоже вздохнул с облегчением, поскольку удалось достичь волшебных 60 кадров в секунду, игра пошла с нужной скоростью.

Загадочное дело таинственной неравномерности

Добившись идеальной скорости игры и проехав через полмира, чтобы представить игру и ее движок на конференции, я обнаружил, что на демонстрационных устройствах игра работает неравно¬мерно: то и дело происходят рывки, игра словно спотыкается. На настольных компьютерах этих рывков не было, как не было их и на Ultrabook, который я использовал для тестирования. Рывки возникали только на этих устройствах, причем, что интересно, они были мощнее, чем те устройства, на которых я вполне успешно тестировал игру.

После длительных обсуждений и изучения эту проблему удалось связать с так называемым «разрешением внутреннего таймера». Коротко говоря, во всех играх, скорость действия в которых не зависит от компьютера (то есть, к примеру, персонажу игры нужно одинаковое количество времени, чтобы пробежать из точки А в точку Б вне зависимости от того, на каком компьютере запущена игра), так вот, во всех таких играх требуется доступ к команде GetTime(). Существует несколько команд этого семейства, но самой популярной является команда timeGetTime(), возвращающая количество миллисекунд с момента включения компьютера. При этом предполагается, что результат дается с разрешением в 1 миллисекунду, и действи¬тельно, на многих настольных компьютерах время передается именно с такой точностью. Оказывается, на Ultrabook и других мобильных устройствах, где во главу угла ставится экономия электроэнергии, это разрешение не фиксировано, оно может составлять не 1 миллисекунду, а 10—15. Если вы используете этот таймер для управления физикой в игре, как это было в нашем игровом движке, игра начнет «дергаться» и «спотыкаться» совершенно произвольным образом, поскольку вызовы обновления физики будут хаотично перепрыгивать с одного полученного времени к другому.

Причина перехода от разрешения в 1 мс к 10—15 мс в том, что некоторые системы могут сократить потребление электроэнергии, снизив частоту работы процессора, а из-за этого и частота тактовых импульсов может непредсказуемо измениться. Для этой проблемы существует несколько решений. Мы рекомендуем использовать команду QueryPerformanceTimer(), гарантирующую точность возвращаемого значения времени за счет второй команды, возвращающей частоту работы таймера.

5. Подсказки и советы

Что нужно сделать

  • Дополняйте шейдеры добавочными технологиями, а не заменяйте их при оптимизации для Ultrabook. Ваша игра должна работать и на настольных компьютерах, и на Ultrabook; процесс распространения игры намного проще, если вся игра упакована в один двоичный файл. Шейдеры DirectX* и OpenGL* позволяют создавать разные технологии в рамках одного шейдера. Поскольку ваша игра содержит все необходимое, код игры может определять, на какой платформе игра запущена в данный момент, и выбирать оптимальную процедуру отрисовки, которая на этой платформе даст наибольшую скорость и наивысшее возможное качество.
  • Предложите пользователям экран настроек, где можно выбрать нужный уровень производительности и качества. В подавляющем большинстве случаев компьютерные игроки рассчитывают на наличие таких настроек. Всегда хорошо определять и заранее устанавливать настройки, оптимальные для данной конкретной системы, но у пользователя должна быть возможность изменять эти настройки; кроме того, выбранные вами настройки по умолчанию должны всегда быть работоспособными в системе пользователя.

Чего не стоит делать

  • Не исходите из того, что ваша игра обязана работать со скоростью 60 кадров в секунду. На большинстве современных устройств можно настроить интервал обновления экрана, чтобы пропускать один или даже три сигнала вертикальной развертки: при этом сохранится нужная плавность игры, но при скорости в 30 кадров в секунду. Разумеется, все будет не настолько чудесно, как при 60 кадрах в секунду, но если правильно отрегули¬ровать синхронизацию, игра все же будет восприниматься очень хорошо.
  • При разработке игры не следует недооценивать ресурсоемкость шейдеров фрагментов, особенно если предполагается использовать не самое мощное графическое оборудование. Если скорость игры недопустимо низка, откажитесь от использования шейдеров или ограничьте их использование, действуя методом исключения.
  • Не следует заранее выбирать разрешение экрана вместо пользователя: выбранное вами разрешение может не поддерживаться экраном устройства. Используйте API Windows* API для опроса устройства и получения совместимого разрешения по умолчанию.
  • • Не полагайтесь на то, что timeGetTime() возвращает время с интервалами в 1 милли-секунду. На Ultrabook при включенных технологиях экономии электроэнергии интервалы могут составлять 10—15 мс!

6. Краткие советы по работе с Ultrabook

Дальнейший текст может показаться цитированием очевидного, но тем не менее: предлагаю краткое и полезное руководство по тестированию, запуску и демонстрации ваших игр и трехмерных приложений на Ultrabook.

Экономия электроэнергии

При демонстрации для большой аудитории, если вы хотите показать свою игру в лучшем виде, обязательно подключите Ultrabook к электросети. Не работайте от аккумулятора: для экономии электроэнергии система будет ограничивать производительность оборудования, тогда как вам требуется полная мощность.


Рисунок 11. Управление электропитанием на Ultrabook.

На всякий случай найдите «Управление электропитанием» на панели управления и убедитесь, что при питании компьютера от сети все меры экономии электроэнергии отключены, а все возможные параметры установлены на максимум.

Графика

На панели управления есть еще один раздел, где вы можете управлять ускорением графичес-кого адаптера устройства. Вы найдете параметры, отвечающие за работу GPU и драйвера в режиме экономии электроэнергии. Нужно выбрать режим наибольшей производительности (или эквивалентный ему), чтобы гарантировать наиболее быструю работу графического процессора.


Рисунок 12. :Параметры ускорения обработки графики на Ultrabook™

Необходимость таких действий может вызвать недоумение, но помните, что во главу угла при разработке Ultrabook ставится экономия электроэнергии везде и всегда, где это возможно, что позволяет таким устройствам подолгу работать без подзарядки. Самый простой и действен¬ный способ достижения максимальной производительности Ultrabook — включить его в розетку и выставить все настройки на максимум.

Фоновые задачи

Опытные разработчики, да и опытные пользователи согласятся с этим простым, но эффектив-ным советом: нужно посмотреть, какие фоновые задачи запускаются на Ultrabook при загрузке Windows. Предполагалось, что фоновые программы будут полезными, и при этом не будут потреблять слишком много ресурсов, но когда их много, они имеют нехорошую тенденцию довольно серьезно загружать CPU.

Сколь бы важными ни были эти задачи, когда вы демонстрируете, насколько быстро ваша трехмерная игра может работать на Ultrabook, разумно отключить вообще все задачи, которые не нужны непосредственно для этой цели. За фоновые задачи не беспокойтесь, при следующей загрузке Ultrabook они снова появятся, но зато сейчас в течение всего оставшегося сеанса Windows все ресурсы устройства будут предоставлены одному-единственному приложению — вашему!

7. Выводы

Тема оптимизации игр весьма обширна. Разработчики должны рассматривать оптимизацию как неотъемлемую часть своей повседневной работы. Цель в том, чтобы вашу игру можно было запускать на как можно более широком наборе оборудования. Для достижения этой цели потребуется весь ваш опыт, все знания и умения. Применяя такие средства Intel®, как VTune™ Analyzer и Intel Graphics Performance Analyzers, можно ускорить решение этой задачи. Статьи, подобные этой, подскажут возможные решения, но в конечном итоге все зависит от вашей сообразительности. Как можно добиться этого же результата другим способом? Можно ли сделать это быстрее? Можно ли сделать это умнее? Вот с таких вопросов следует начинать, и чем чаще вы станете их задавать, тем эффективнее сможете оптимизировать свои игры и приложения. Как я предположил в начале этой статьи, вы не только станете выпускать более качественный код, но и расширите свои позиции на стремительно растущем рынке!

Другие материалы по теме

Codemasters GRID 2* on 4th Generation Intel® Core™ Processors - Game development case study
Not built in a day - lessons learned on Total War: ROME II
Developer's Guide for Intel® Processor Graphics for 4th Generation Intel® Core™ Processors
PERCEPTUAL COMPUTING: Augmenting the FPS Experience

Об авторе

Во время, свободное от написания статей, Ли Бэмбер руководит британской компанией The Game Creators (http://www.thegamecreators.com)), которая специализируется на разработке и распространении средств создания компьютерных игр. Эта компания ведет свою историю с 1999 года. Среди плодов работы этой компании вместе с окружающим ее разработчиком игр — множество популярных брендов, в том числе Dark Basic, FPS Creator, а также App Game Kit (AGK). Ли также ведет хронику своей повседневной работы разработчика вместе со снимками экрана и видеороликами: http://fpscreloaded.blogspot.co.uk




 

Intel®Developer Zone offers tools and how-to information for cross-platform app development, platform and technology information, code samples, and peer expertise to help developers innovate and succeed.  Join our communities for the Internet of Things, Android*, Intel® RealSense™ Technology and Windows* to download tools, access dev kits, share ideas with like-minded developers, and participate in hackathons, contests, roadshows, and local events.

Any software source code reprinted in this document is furnished under a software license and may only be used or copied in accordance with the terms of that license.
Intel, the Intel logo, Ultrabook, and VTune are trademarks of Intel Corporation in the U.S. and/or other countries.
Copyright © 2014 Intel Corporation. All rights reserved.
*Other names and brands may be claimed as the property of others.

Список инструментальных средств Intel® для разработчиков ПО

$
0
0

Корпорация Intel предлагает различные варианты лицензирования продуктов, предназначенных для разработки программного обеспечения. Ознакомьтесь со следующими доступными для приобретения вариантами, обновите свое ПО Intel® или скачайте 30-дневную оценочную версию1. Вы можете перейти на страницу продления подписки или обновления услуг поддержкидля идентификации вариантов возобновления или обновления услуг поддержки для вашей продукции.

Все указанные далее цены соответствуют именным пользовательским лицензиям. Все указанные цены являются розничными, рекомендуемыми производителем, и могут быть изменены без предварительного уведомления. Цены указаны БЕЗ учета НДС и прочих применимых налогов и сборов.

Intel® Parallel Studio XE

Intel® Parallel Studio XE 2015

Оперативно создавайте быстро исполняемый код, пользуясь полнофункциональным комплектом разработки ПО для параллельных вычислений

Комплект Intel® Parallel Studio XE был разделен на три части - Composer, Professional и Cluster.  Узнайте подробнее.

 ОценитьКупитьПродлить подписку
Редакция Composer – утилиты для сборки включают компиляторы, библиотеки и модели для параллельного программирования
   Fortran и C++Загрузить2299 долл. США799 долл. США**
   C++Загрузить1599 долл. США599 долл. США**
   FortranЗагрузить849 долл. США$399**
Редакция Professional – включает редакцию Composer + средства для разработки, отладки и настройки приложений
   C++ и FortranЗагрузить2299 долл. США799 долл. США**
   C++Загрузить1599 долл. США599 долл. США**
   FortranЗагрузить1899 долл. США699 долл. США**
Редакция Cluster – включает редакцию Professional + средства сборки MPI, отладки и настройки приложений
   C++ и FortranЗагрузить2949 долл. США1049 долл. США**

Intel® System Studio

Intel® System Studio

Встраиваемые и мобильные вычисления

 ОценитьКупитьПродлить подписку
Стандартная редакцияЗагрузить$1649599 долл. США
   Включая отладчик JTAG DebuggerЗагрузить$2399849 долл. США

Независимые продукты

ПродуктОценитьКупитьПродлить подписку
ПОДПРОГРАММА ПРОТОКОЛИРОВАНИЯ ПРОИЗВОДИТЕЛЬНОСТИ
Intel® VTune™ Amplifier XEЗагрузить899 долл. США349 долл. США**
БИБЛИОТЕКИ ДЛЯ ПОВЫШЕНИЯ ПРОИЗВОДИТЕЛЬНОСТИ
Библиотека Intel® MPIЗагрузить499 долл. США179 долл. США**
Библиотека Rogue Wave* IMSL* Fortran Numerical Library 7.0***н/д999 долл. США499 долл. США
СПЕЦИАЛИЗИРОВАННЫЕ КОМПИЛЯТОРЫ
Компилятор Intel® C++ для ОС Android*Загрузить$79,95н/д
Intel® C++ Compiler Professional Edition с поддержкой ОС реального времени QNX Neutrino*н/д599 долл. США240 долл. США
Компилятор Intel® C для байтового кода EFIн/д995 долл. США398 долл. США
СРЕДСТВА ДЛЯ РАЗРАБОТКИ ГРАФИЧЕСКИХ ПРИЛОЖЕНИЙ
Intel® Graphics Performance AnalyzersЗагрузитьн/дн/д
Комплект Intel® SDK для приложений OpenCL™Загрузитьн/дн/д
Комплект для разработки Intel® Media SDKЗагрузитьн/дн/д
Intel® Media Server StudioЗагрузить499 долл. СШАн/д
Бета-версия Intel® Integrated Native Developer ExperienceЗагрузитьн/дн/д
Версия Intel® Integrated Native Developer Experience 2015 для OS X*Загрузить499 долл. СШАн/д
Бета-версия Intel® RealSense™ SDK для WindowsЗагрузить
(когда станет доступно)
н/дн/д
Intel® Perceptual Computing SDKЗагрузитьн/дн/д
ВЕБ-ПРИЛОЖЕНИЯ
Intel® XDKЗагрузитьн/дн/д

  • 1 Для получения информации о передаваемых лицензиях, лицензиях с возможностью использования только на определенных узлах и о прочих вариантах лицензирования обратитесь к торговому посреднику или к представителю корпорации Intel по адресу intel.software.sales@intel.com.
  • Для приобретения лицензии для научных, исследовательских и учебных заведений, выберите нужный продукт, и при оформлении покупки будет показана цена со скидкой. Для получения дополнительных сведений обо всех наших продуктах для учебных заведений посетите сайт центра предложений для учебных заведенийили обратитесь к представителю корпорации Intel по адресу academicdevelopersinfo@intel.com.
  • Продление поддержки — поддержка в течение одного года с даты истечения срока действия текущего соглашения о поддержке.
  • Существующим клиентам предоставляются специальные цены на обновление для продуктов Intel® Parallel Studio XE, Intel® C++ Studio XE или Intel® Fortran Studio XE. Подробную информацию см. в предложении об обновлении.

1 Можно бесплатно загрузить 30-дневные ознакомительные версии решений Intel® для разработчиков ПО. В течение ознакомительного периода можно бесплатно пользоваться поддержкой, создав учетную запись в программе Intel® Premier Support после запроса ознакомительной лицензии или посетив форумы Intel® Developer Zone.

Обратите внимание, что по окончании ознакомительного периода продукты перестанут работать, и продлить ознакомительную лицензию невозможно.
 Ознакомьтесь с требованиями к системепродуктов, которые вы хотели бы оценить.

**Вы можете обновить лицензии по минимальной цене только в том случае, если сделаете это до истечения срока предыдущей подписки. Для получения дополнительной информации о продлении лицензий нажмите здесь.

***Доступно в качестве дополнения к любому комплекту Windows Fortran* или в поставке редакции Composer.

Корпорация Intel прилагает все необходимые усилия для соблюдения конфиденциальности. Ознакомиться с действующими правилами сбора и обработки личных сведений о заказчиках, данных о серийных номерах продуктов Intel и прочих данных можно в нашем уведомлении о конфиденциальностии в уведомлении о проверке серийных номеров.

Развивающие игры для детей на основе Intel® Perceptual Computing. Разработка «Приключений Клифорда» для сенсорного компьютера-моноблока (AIO).

$
0
0

Download PDF

Введение

В этой статье освещается разработка серии интерактивных развивающих игр для детей младшего возраста «Приключения Клифорда»от компании Scholastic Interactive. Разноплановое использование жестов и голоса в этой игре стали возможными благодаря технологии Intel® Perceptual Computing SDK 2013. Здесь обсуждаются новые методы распознавания жестов и голоса с помощью технологий Perceptual Computing (естественные методы взаимодействия с компьютером), методика решения проблем с SDK, а также вопросы поддержки портативных компьютеров-моноблоков (AIO).


Рисунок 1. Пес Клифорд*

Концепция развивающей игры.

Scholastic Interactive — это подразделение Scholastic, международной медиа-, образовательной и издательской компании. Миссия Scholastic Interactive — создание игр, которые не просто были бы развлечением для детей, но и способствовали бы их развитию. Для компании методы естественных взаимодействий с компьютером — это новое направление в развива¬ющих играх для малышей, ведь ребенок осваивает их сам интуитивно, а не через предвари¬тельное обучение. Интеграция платформы Perceptual Computing со средствами распознавания голоса и жестов дает детям от трех лет возможность участвовать в приклю¬чениях вместе с Клифордом и его друзьями.

В этой серии из четырех интерактивных эпизодов про Клифордаигроки просматривают сюжет и взаимодействуют с ним посредством речи и сенсорного экрана своего компьютера. Игра вовлекает детей в сюжет, предлагая различными способами «помогать» Клифорду посредством определенных жестов и высказываний.


Рисунок 2. Приключения Клифорда. Меню.

Интерактивное эмпирическое обучение.

Благодаря интерактивной технологии Scholastic Клифордреагирует на голос и движения детей. В ходе сюжета игры они смотрят анимированные отрывки каждого приключения и активно содействуют героям, прикасаясь к экрану или произнося ответы на вопросы. Сюжет развивается по мере взаимодействия ребенка с игрой. Каждая игра рассчитана на развитие базовых навыков грамотности и может повторяться сколько угодно раз.

Intel Perceptual Computing SDK 2013 включает в себя API, образцы кода, а также руководства по программной интерпретации жестов и речи ребенка, взаимодействующего с игрой. Разработчики могут без труда сочетать возможности SDK по распознаванию речи, жестов руки и пальцев, мимики, технологии дополненной реальности и вычитания фона, создавая ПО для новейших планшетов, компьютеров Intel Ultrabook™ и сенсорных моноблоков. Использование микрофона, камеры, сенсорного экрана, функций определения положения в пространстве и геолокации, широко распространенных на планшетах, ноутбуках трансформерах и компьютерах-моноблоках повышает многомерность восприятия новых приложений.


Рисунок 3. Intel® Perceptual Computing SDK.

Команда разработчиков.

Специалисты Scholastic изучили видение нескольких коллективов разработчиков относительно концепции игры, интерактивного взаимодействия и основных вопросов пригодности программных продуктов для детей. В итоге Scholastic установила партнерские отношения с Symbio, чьи специалисты имеют большой опыт в создании средств распознавания голоса и жестов, игр, образовательных программ для детей, а также в вопросах детской эргономики.

Разработки на основе Intel® Perceptual Computing Platform.

Адаптация технологий Perceptual Computing к анализу движений и голоса детей несет в себе ряд сложностей. Scholastic всесторонне протестировала каждый прототип, чтобы оценить дизайн игры и реальность прохождения ее уровней. Это помогло выявить потенциальные проблемы, с которыми могла столкнуться целевая аудитория, и найти для них решения.

Некоторые аспекты проведенной работы могут представить особый интерес с точки зрения технологии Perceptual computing. Они приводятся ниже.

 

Калибровка распознавания голоса.

Чтобы обеспечить приемлемое качество распознавания голоса, потребовалось провести ряд проверок. Голос ребенка изменяется по мере взросления, особенно в том возрасте, на который рассчитана серия «Клифорд». Поэтому необходимо было добиться такого уровня калибровки, чтобы детский голос и речевые конструкции распознавались правильно.


Рисунок 4. Эпизод игры, требующий речевого участия игрока.

Распознавание и локализация жестов.

В одной из игр «Приключений Клифорда»от ребенка требуется помочь собаке ловить игрушки, падающие с дерева. Для этого нужно касанием руки «схватить» корзину на экране и двигать ее в разные стороны. 


Рисунок 5. Дерево игрушек Клифорда.

Были разработаны специальные алгоритмы, распознающие жесты и сопоставляющие их с координатами касания, чтобы корзина на экране двигалась вслед за рукой ребенка. В тестировании с удовольствием принимали участие маленькие игроки. Ранее разработчики ошибочно полагали, что у ребенка жесты удержания объекта на экране не будут сильно отличаться от жестов взрослого. Но работа с детьми заставила пересмотреть дизайн игры таким образом, чтобы она воспринимала их нечеткие движения. Научить сенсоры понимать размашистые, часто ошибочные и хаотичные жесты ребенка, состоящие из множества касаний, было непросто. Требовалось много работы по определению прототипов жестов и отбору их наиболее общих конфигураций. Область регистрации касаний была расширена, чтобы даже неточный жест распознавался и вызывал нужную реакцию приложения.

К примеру, в другой мини-игре дети помогают Клифорду убрать из сада сорняки. Вместо того, чтобы заставлять игроков брать сорняк и двигать руку вверх, вытягивая его, разработчики выбрали движения захвата и раскрытия ладони, обозначающие выдергивание и выбрасывание. Адаптация к уровню развития детской моторики позволила сделать игровое приложение более успешным.


Рисунок 6. Ребенок помогает Клифорду убирать сорняки.

Ниже приведен фрагмент кода игры, калибрующий жесты игрока в обучающем упражнении, где требуется вращать руками мячик. В эпизоде, изображенном на рисунке 7, для более точного контроля объекта и легкости движений применили экспоненциальное сглаживание (exponential smoothing). Оно вычленяет или, по крайней мере, приблизительно вычисляет случайные движения игрока, которые программа должна игнорировать.


Рисунок 7:Вращение мяча.


Figure 8:Пример кода для вращения мяча.

Решение проблем с Intel® Perceptual Computing SDK

Благодаря многомерному восприятию, которое возможно с SDK от Intel, игроки получают немедленную реакцию программы на их действия. Это создает ощущение физического участия в происходящем. Однако разработчики столкнулись с некоторыми ограничениями в возможностях распознавания сложных движений и голосовых реакций детей.

 

Жесты

Камера, воспринимающая жесты, сфокусирована на расстоянии около 60–90 см. Поэтому мелкие движения регистрируются лучше, чем размашистые или комплексные, выходящие за пределы данного диапазона.

Оптимальный набор жестов был определен методом проб и ошибок. Специалистам пришлось подумать о различных условиях окружающей среды, освещении и расстоянии до камеры.

С точки зрения SDK, API и других используемых технологий, разработать первоначальные вари-анты жестов несложно, потому что обучающие упражнения, образцы кода и используемые структуры имеются в SDK. После настройки среды разработки можно выполнять обучающее упражнение, например отслеживание пальцев, чтобы изучить взаимодействие сенсоров и кода в SDK.


Рисунок 9. Взаимодействие сенсоров жестов и кода в Intel® Perceptual Computing SDK 2013.

Программисты обнаружили, что в SDK не хватает различных систем координат для жестов. Это пришлось восполнить собственными разработками. 


Рисунок 10:Визуальная схема координат жестов.

Изначально команда разработчиков использовала подход node[8].positionImage.x/y, игнорируя данные о глубине, т. к. они не требовались для интерпретации жестов. Но впоследствии был найден более оптимальный подход. Использовалось «глубинное изображение» и отыскивался ближайший пиксель, на основе чего эффективно определялся жест. Затем было добавлено экспоненциальное сглаживание.

Распознавание голоса.

Распознавание голоса в игре сильно зависело от устройств и сюжета. На одних устройствах и в одних ситуациях оно работало хорошо, в других условиях не работало совсем.

Игра должна подсказывать детям команду, которую нужно произнести, чтобы она была записана с помощью микрофона. Функция должна работать даже на фоне посторонних звуков и музыкального сопровождения игры. Распознавание голоса может работать в режиме детектирования речи, когда программа пытается определить, что вы сказали, или в режиме словаря, когда сказанное сопоставляется с вашим словарем, который определяется в случае данной игры пользователем.

Сначала специалисты попробовали первый режим и настроили его на учет любых звуков, основываясь на том, что речь маленьких детей не всегда четко артикулируется. Но результаты оказались неудовлетворительными. Тогда было решено перейти к режиму словаря. Он хорошо работает, если слова произносятся отчетливо. Разработчики попробовали добавить в словарь варианты слов, чтобы увеличить вероятность их распознавания (например, трактор — тлактол — тъяктол). Однако режим словаря не дал ожидаемых результатов, потому что чем больше в словаре единиц, тем выше вероятность ошибки. Пришлось искать компромисс между величиной списка слов и потенциальной долей ошибок. В конечном варианте список допустимых слов был сведен к минимуму, чтобы оставить возможность простого взаимодействия ребенка с игрой.

Влияние размера экрана. Сенсорные компьютеры-моноблоки.

С развитием сенсорных экранов на рынке начинают появляться модели с большой диагональю. «Приключения Клифорда» созданы с учетом этой тенденции. Большие сенсорные экраны применяются, в частности, в компьютерах-моноблоках AIO.

Такие компьютеры состоят из монитора (от 18 до 55 дюймов) и материнской платы, встроенной позади него. У них высокопроизводительные процессоры, высокое разрешение (1080p/720p), они снабжаются беспроводными (Bluetooth*) клавиатурой и мышью и емкой встроенной батареей, что делает устройство портативным и способным к автономной работе. Это одна из самых быстроразвивающихся разновидностей компьютеров. Моноблоки популярны благодаря тому, что на них можно делать все, что возможно на обычном компьютере: играть в игры, просматривать веб-сайты, телепрограммы и фильмы, делать домашнюю работу, вести семейный бюджет, общаться с друзьями и т. д.

Многие из современных моноблоков являются портативными, что еще более расширяет область их применения. Портативные моноблоки имеют огромные преимущества для игр благодаря своему большому экрану, поддерживающему множественные касания, высокопроизводительному сетевому подключению, а также компактному корпусу. Они могут устанавливаться как под наклоном, так и горизонтально. Встроенные батарея и адаптер Wi Fi и позволяют пользоваться компьютером из любой точки квартиры или дома. Большой HD монитор с поддержкой множественных касаний и графический процессор высокого класса превращают работу с таким компьютером в удовольствие. Все эти качества делают моноблок очень привлекательной базой для разработчиков, желающих выйти за рамки мобильных устройств для одного пользователя.

Создателям «Клифорда» очень хотелось видеть свою игру на больших экранах, и поэтому они позаботились о том, чтобы она подходила для разрешения 1920 x 1080.

Заключение

Стадия тестирования прошла весело. Разработчики получили ценный опыт, работая с детьми, конечными пользователями приложения. И еще приятнее было увидеть готовую игру в использовании. Один из наших старших специалистов показал ее своей трехлетней дочери, и все мы были очень рады услышать, что девочка играла в «Приключения Клифорда» с огромным интересом и азартом. Ура!


Рисунок 11.Клифорд и его друзья.

Теперь Scholastic не терпится применить свои технологии в новых проектах. Совместно с Symbio ведется работа над новой игрой на основе Intel® RealSense™ 3D SDK, которую планируется выпустить осенью 2014 года.


Рисунок 12:Игра готова.

Технология Intel® Real-Sense™

Анонсированная на выставке CES 2014, технология Intel® RealSense™ — это новый образ Intel® Perceptual Computing, SDK с интуитивным пользовательским интерфейсом и функциями распознавания речи, жестов, движений руки и мимики. Эти технологии компания Intel представила еще в 2013 г. Intel RealSense предоставляет разработчикам дополнительные возможности, такие как сканирование, редактирование, 3D-печать, распространение, а также технологии дополненной реальности. Благодаря им пользователи могут манипулировать отсканированными 3D объектами с помощью новейшей технологии сенсорного управления.

Ссылки и полезные материалы

Об авторе

Тим Дункан — один из наших инженеров. Друзья называют его «мистер Гаджет». В настоящее время он помогает разработчикам применять в их продуктах новые технологии Intel. Тим обладает многолетним опытом работы в отрасли и знаком со многими ее сторонами: от производства микросхем до интеграции целых систем. Вы можете найти его на сайте Intel® Developer Zone: Tim Duncan (Intel).

 

Примечания

Исходный код предоставлен Scholastic Interactive LLC для метода экспоненциального сглаживания для приложений, использующих технологию Intel Perceptual Computing, созданных для платформы Windows 8. 

Scholastic Sample Source License

Copyright (c) 2014, Scholastic Interactive LLC. Code pertaining to exponential smoothing functionality contained in the Clifford’s Reading Adventures 1.0 game (“Sample Code”). All rights reserved.

Redistribution and use in source and binary forms of the Sample Code, with or without modification, are permitted provided that the following conditions are met:

  • Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  • Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  • The name of the Sample Code, Clifford’s Reading Adventures or any names or trademarks contained therein, the name of the copyright holder or its affiliates or the names of contributors to the Sample Code may be used to endorse or promote products derived from this software, or in any other manner, without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  FOR THE AVOIDANCE OF DOUBT, THE ONLY RIGHTS GRANTED UNDER THIS LICENSE ARE LIMITED TO THE SOFTWARE SPECIFICALLY DESCRIBED ABOVE, AND ANY USERS OF THE SAMPLE CODE SHALL HAVE NO LICENSE OR RIGHTS IN OR TO (A) ANY OTHER SOURCE OR BINARY CODE, OR ANY OTHER SOFTWARE OR TOOLS, THAT MAKES UP OR IS EMBEDDED IN THE CLIFFORD’S READING ADVENTURES GAME, OR (B) ANY OTHER INTELLECTUAL PROPERTY OF THE COPYRIGHT HOLDER OR ITS AFFILIATES.

Clifford Artwork © Scholastic Entertainment Inc.  CLIFFORD THE BIG RED DOG and associated logos are trademarks of Norman Bridwell.  All rights reserved.

 

Intel, the Intel logo, and RealSense are trademarks of Intel Corporation in the U.S. and/or other countries.
Copyright © 2014 Intel Corporation. All rights reserved.
*Other names and brands may be claimed as the property of others.

 

Разработка многопользовательских приложений для моноблоков и планшетов

$
0
0

Создание многопользовательского интерфейса с поддержкой сенсорного управления для Windows*






 

Скачать PDF

Введение

Моноблоки впервые появились в середине 1990-х годов и с тех пор широко используются в различных сферах. Эти устройства были призваны освободить рабочее пространство от громоздкого монитора, системного блока и устройств ввода, и потому на них возлагали большие надежды. Однако поначалу они не были слишком популярными, так как сильно уступали в производительности традиционным настольным ПК и отличались высокой стоимостью. К счастью, современные моноблочные компьютеры избавлены от этих недостатков.

В качестве устройства для дома пользователи, как правило, выбирают модели с экраном диагональю 55 дюймов. Их легко разместить на рабочем столе, и они удобны в работе. Благодаря моноблокам пользователи открыли для себя новые возможности цифрового мира, чем не преминули воспользоваться разработчики приложений, предлагая новые уникальные приложения, рассчитанные на несколько игроков.

К сожалению, в настоящий момент интерфейс прикладного программирования (API) Windows не располагает достаточными средствами для создания приложений для двух или более пользователей. Однако это не значит, что разработчики не могут использовать свои собственные. При разработке интерфейса самое главное сделать так, чтобы приложением было легко пользоваться и чтобы оно правильно воспринимало команды ввода.

Ниже мы рассмотрим, какие средства API Windows можно использовать для разработки приложений с сенсорным управлением, а также продемонстрируем, как с их помощью создать простую многопользовательскую игру.

Создание пользовательского интерфейса

Современные сенсорные экраны не способны определить, кто из пользователей (если их нес-колько) коснулся дисплея. В каких-то случаях этим можно пренебречь. Например, в настольной игре совсем не важно, кто из игроков перемещает фишки по полю, так как за этим следят сами пользователи.

Другое дело, когда необходимо правильно определять команды, поступающие от каждого игрока (как на рис. 1), и обеспечить одновременное взаимодействие с интерфейсом. В этом случае разработчик должен создать наиболее эффективный способ определения того, кто из игроков коснулся экрана. Как правило, для этого делят экран на несколько частей (каждая часть для одного игрока). Пользователи должны сами следить за тем, чтобы не выходить за границы своей зоны. Естественно, существуют и другие методы создания многопользовательского интерфейса (например, с помощью эвристических средств), однако в этой статье они не освещаются.

API Windows

Windows поддерживает два основных формата сенсорных команд. Первый — это сообщения WM_TOUCH. Они отвечают только за первичные данные, поэтому приложение должно само обработать и выполнить поступающие команды.

Второй формат — WM_GESTURE. Это сообщение генерируется при использовании одного из жестов управления Windows, например масштабирования с помощью разведения и сведения пальцев, перетаскивания элементов и т. д. Благодаря их наличию задача разработчика существенно упрощается, поскольку операционная система может сама распознавать жесты управления и касания экрана и передавать их приложению в удобном для обработки виде.

В настоящий момент в Windows поддерживаются только сенсорные команды, поступающие от одного пользователя (руки), поэтому использовать сообщения WM_GESTURE для распознава¬ния касаний нескольких игроков в нашем приложении не получится.

Кроме того, важно помнить, что API-интерфейс определяет координаты касаний по отношению ко всему экрану (они измеряются в сотых долях пикселя), а не только внутри окна программы. Если нужно получить именно координаты в окне, следует конвертировать их с помощью функции ScreenToClient().

Пример приложения

В качестве примера мы решили воссоздать классический симулятор настольного тенниса — игру Pong, и наделить ее сенсорным управлением. Игровой процесс состоит в том, что игроки передвигают ракетки по вертикали, отбивая мячик на сторону соперника. Когда мячик достигает одной из границ поля (по вертикали), игрок на противоположной стороне получает очко.

Мячик двигается по экрану по диагонали. После каждого движения приложение проверяет, не встретил ли он препятствие. Если мячик ударяется о верхнюю или нижнюю границы игрового поля, его вертикальная траектория изменяется в соответствии с углом столкновения. Если он ударяется о ракетку или о вертикальную границу окна, его горизонтальная траектория также изменяется на противоположную.

Обработка перемещения мяча происходит в отдельном потоке самого приложения для обеспечения плавности движения во время выполнения сенсорных команд.

Рендеринг графики осуществляется стандартными вызовами интерфейса графических устройств (GDI) Windows. В качестве ракеток служат обычные прямоугольники, в качестве мяча — круг. Счет матча отображается как текст на фоне. При создании этого приложения не использовались растровые отображения объектов или другие графические средства. Пример интерфейса показан на рисунке ниже. Нажмите на него, чтобы просмотреть видеоролик о работе приложения.

Рисунок 1.Стандартный интерфейс для двух пользователей.

Интерфейс для двух пользователей с поддержкой сенсорного управления

Чтобы игра воспринимала касания каждого пользователя, мы разделили экран на две части: игрок 1 находится слева, игрок 2 — справа.

Каждый раз, когда происходит событие WM_TOUCH, программа получает полный список точек касания (пример кода 1). В нем может содержаться от одной до нескольких записей (максимальное количество точек зависит от возможностей устройства). Затем производится итерация, которая позволяет определить координаты каждого касания по осям X и Y:

…
	                bret = GetTouchInputInfo(
				(HTOUCHINPUT)lparam,
				data.geometry_data.touch_inputs_count,
				data.geometry_data.touch_inputs,
				sizeof(TOUCHINPUT)
				);
			assert(bret != FALSE);
	//-----------------------------------------------------------------
	// обработка сенсорных команд
	//-----------------------------------------------------------------
			for(i = 0; i < data.geometry_data.touch_inputs_count; i++)
	                   {
				touch_input = &data.geometry_data.touch_inputs[i];
	//-----------------------------------------------------------------
	//преобразование сотых долей пикселя в пиксели
	//-----------------------------------------------------------------
				x = touch_input->x / 100;
				y = touch_input->y / 100;)
	                   {…

Пример кода 1.

Далее необходимо определить, с какой стороны произошло касание, после чего обновить вертикальное расположение ракетки так, чтобы оно совпадало с положением пальца игрока (пример кода 2). Таким образом будет происходить перемещение ракетки по полю.

…
	if (x < (ULONG)((data.graphic_data.rect.right – data.graphic_data.rect.left) /          2))
	   {
	//-----------------------------------------------------------------
	// разделение экрана для двух пользователей
	//-----------------------------------------------------------------
	   Data.geometry_data.p1y = y;
	   }else{
	   Data.geometry_data.p2y = y;
	   }
	bret = move_paddles(&data);
	assert(bret == TRUE);
	   {

Пример кода 2.

Так как приложение обрабатывает все команды ввода, игроки могут не только одновременно нажимать на экран, но и выполнять несколько касаний. При этом положение ракетки будет определяться по последнему зарегистрированному касанию.

Адаптация приложения для четырех пользователей

Чтобы адаптировать приложение для четырех пользователей, необходимо изменить игровое поле и активные зоны игроков.

Мы добавили две ракетки и два табло со счетом внизу и вверху экрана. Разграничительные линии игровых зон приобрели форму буквы «Х», разделив окно приложения на четыре треугольника (рисунок 2).

Рисунок 2.Разделение игровых зон для четырех игроков.

На рисунке 3 показано, как приложение определяет, в какой из зон произошло касание экрана.



 

Рисунок 3.Определение зоны сенсорной команды.

При разделении экрана на четыре зоны определить, в какой из них произошло касание, становится сложнее (пример кода 3).

…
	   if(touch_point.y > (LONG)(((float)data.graphic_data.rect.bottom / data.graphic_data.rect.right) * touch_point.x)) {
	     if(touch_point.y > (LONG)((((float)data.graphic_data.rect.bottom /  data.graphic_data.rect.right) * -1) * touch_point.x) + data.graphic_data.rect.bottom){
	       data.geometry_data.p4x = touch_point.x;
	       }else{
	       data.geometry_data.p1y = touch_point.y;}
	   }else{
	   if(touch_point.y < (LONG)((((float)data.graphic_data.rect.bottom / data.graphic_data.rect.right) * -1) * touch_point.x) + data.graphic_data.rect.bottom)
	   {
	   data.geometry_data.p3x = touch_point.x;
	   }else{
	   data.geometry_data.p2y = touch_point.y;
	   }
	}…

Пример кода 3.

Пример интерфейса игры для четырех пользователей показан на рисунке ниже. Нажмите на него, чтобы просмотреть видеоролик о работе приложения.

Рисунок 4.Игра для четырех пользователей, запущенная на устройстве с 55-дюймовом экраном.

Заключение

При создании многопользовательских приложений с сенсорным управлением для Windows 7 и более поздних версий используйте сообщения WM_TOUCH и WM_GESTURES. Если вы найдете наиболее эффективный способ, как определить, в какой из игровых зон происходит касание, разработка приложения даже для четырех одновременно играющих пользователей не составит большого труда.

Об авторах

Стивен Роджерс работает в компании Intel более 20 лет. Он специализируется на настройке систем и управлении лабораториями. Стивен является техническим инженером-маркетологом в группе PLSE (развертывание платформ и проектирование масштабирования), где отвечает за отраслевые приложения.

Олден Сотелл участвует в программе «Компьютерные и информационные науки» в Портленд¬ском общинном колледже. В Intel работает по договору. Специализируется на настройке и обс¬луживании аппаратного обеспечения серверов для предприятий, сетевых устройств и ПО в лаборатории PLSE.

Жамель Тейебработает в группе Intel по разработке программного обеспечения и служб. Он получил степень кандидата компьютерных наук в Университете Валансьена и теперь занимается разработкой ПО.






 

Intel®Developer Zone offers tools and how-to information for cross-platform app development, platform and technology information, code samples, and peer expertise to help developers innovate and succeed.  Join our communities for the Internet of Things, Android*, Intel® RealSense™ Technology and Windows* to download tools, access dev kits, share ideas with like-minded developers, and participate in hackathons, contests, roadshows, and local events.






 

Intel, the Intel logo, and Ultrabook are trademarks of Intel Corporation in the U.S. and/or other countries.
Copyright © 2014 Intel Corporation. All rights reserved.
*Other names and brands may be claimed as the property of others.






 

Пример кода Android* MediaPlayer для архитектуры Intel®

$
0
0

Введение

Воспроизведение мультимедиа становится одной из наиболее популярных моделей использования на мобильных устройствах. Пользователи ожидают, что мобильные устройства должны быть способны воспроизводить на ходу распространенные типы мультимедиа и воспроизводить видео. В этом документе рассматриваются основы создания мультимедиа приложений Android и предоставляется образец кода, использующего API MediaPlayer, на платформах с архитектурой Intel®.

Мультимедиа платформа Android: API MediaPlayer

Мультимедиа платформа Android дает разработчикам возможность удобно интегрировать возможности воспроизведения звука и видео в приложения с поддержкой распространенных форматов мультимедиа. Основой платформы мультимедиа Android является класс MediaPlayer. Этот класс можно использовать для воспроизведения данных мультимедиа, находящихся в локальной файловой системе, хранящихся в ресурсах приложения или поступающих в виде потока по сетевому подключению.

Управление состоянием

Класс MediaPlayer работает на основе состояний. Он обладает внутренним конечным автоматом — машиной состояний, которая управляет всеми состояниями жизненного цикла объекта MediaPlayer. На приведенной ниже схеме показано изменение состояния объекта MediaPlayer для элемента управления воспроизведением. На этой схеме одиночная стрелка означает синхронные вызовы метода, а двойная стрелка означает асинхронные вызовы метода и обратные вызовы.


Рисунок 1. Изменения состояния объекта MediaPlayer. (Одиночная стрелка означает синхронные вызовы метода, двойная стрелка означает асинхронные вызовы метода и обратные вызовы)

На схеме видно, что жизненный цикл объекта MediaPlayer содержит несколько состояний. Вначале при создании нового объекта MediaPlayer или при вызове метода reset() объект MediaPlayer переходит в состояние Idle (бездействие). Это начальное состояние, но воспроизведение пока невозможно. Если вызвать какие-либо методы управления воспроизведением, например start(), stop(), seekTo(int) и т. д., возникнет программная ошибка.

Приложение должно вызвать метод setDataSource(), чтобы указать на допустимый источник мультимедиа. В результате этого проигрыватель переходит в состояние Initialized (Инициализирован). Источником может быть локальный файл или потоковые данные, поступающие по сетевому подключению.

В состоянии Initialized перед тем, как можно будет начать воспроизведение, приложение может вызвать метод prepare() или prepareAsync() для перехода в состояние Prepared (Подготовлен). Метод prepare() осуществляет получение данных, их буферизацию и декодирование файла мультимедиа. Однако, возврат метода prepare() может занять весьма длительное время при получении данных мультимедиа с сетевого URL-адреса, особенно при низкоскоростном подключении к сети. Поэтому запускать метод prepare() в потоке пользовательского интерфейса приложения не рекомендуется: из-за этого приложение может перестать реагировать на действия пользователя. Вместо этого следует использовать метод prepareAsync(), разработанный, чтобы справиться с этой проблемой и обеспечить удобный способ подготовки данных мультимедиа, сохраняя способность пользовательского интерфейса реагировать на действия пользователя. Метод prepareAsync() выполняется в фоновом режиме и возвращается сразу же после завершения, отправляя обратный вызов OnPreparedListener.onPrepared(), чтобы привести объект MediaPlayer в состояние Prepared.

Из состояния Prepared можно запустить воспроизведение и управлять им, вызывая методы start() и seekTo(). После начала воспроизведения мультимедиа объект MediaPlayer переходит в состояние Started (Запущен).

После начала воспроизведения можно управлять им, вызывая метод pause() для перевода объекта в состояние Paused (Приостановлен) или вызывая метод start() для возврата объекта в состояние Started. Если воспроизведение доходит до конца и при этом не включен повтор воспроизведения с начала, объект MediaPlayer переходит в состояние PlaybackCompleted (Воспроизведение завершено). На этом этапе также можно вызвать метод start(), чтобы снова начать воспроизведение. В этом случае состояние изменится на Started. Если же вызвать метод stop() из состояния Started, Paused или PlaybackCompleted, конечный автомат перейдет в состояние Stopped (Остановлен). Из этого состояния можно перейти в состояние End (Конец) и высвободить объект MediaPlayer, либо, если нужно повторно воспроизвести мультимедиа, потребуется вновь подготовить данные перед вызовом метода start().

Помните, что следует всегда вызывать метод release() после каждого использования, чтобы переводить объект MediaPlayer в состояние End. В противном случае объект будет по-прежнему расходовать ресурсы системы. Если продолжать создавать новые экземпляры MediaPlayer, не вызывая метод release(), ваше приложение может очень быстро исчерпать все ресурсы системы.

Если зарегистрирован обработчик OnErrorListener, метод обратного вызова OnErrorListener.onError() будет вызван при любых ошибках, чтобы можно было их обрабатывать надлежащим образом.

Использование MediaPlayer для воспроизведения звука и видео

Теперь посмотрим, как выглядит код для воспроизведения звука и видео с помощью MediaPlayer.

Для звука:

private void playAudio(Integer media) {
    try {
        switch (media) {
            case LOCAL_AUDIO:
                path = "/sdcard/Download/music/1.mp3";
                mMediaPlayer = new MediaPlayer();
                mMediaPlayer.setDataSource(path);
                mMediaPlayer.prepare();
                mMediaPlayer.start();
                break;
            case RESOURCES_AUDIO:
                mMediaPlayer = MediaPlayer.create(this, R.raw.test_cbr);
                mMediaPlayer.start();

        }
    } catch (Exception e) {
        Log.e(TAG, "error: " + e.getMessage(), e);
    }

}

@Override
protected void onDestroy() {
    super.onDestroy();
    // TODO Auto-generated method stub
    if (mMediaPlayer != null) {
        mMediaPlayer.release();
        mMediaPlayer = null;
    }

}

Для видео:

private void playVideo(Integer Media) {
    doCleanUp();
    try {

        switch (Media) {
            case LOCAL_VIDEO:
                path = "/sdcard/Download/video/2.mp4";
                break;
            case STREAM_VIDEO:
                path = "Your URL here";
                break;
        }
        mMediaPlayer = new MediaPlayer();
        mMediaPlayer.setDataSource(path);
        mMediaPlayer.setDisplay(holder);
        mMediaPlayer.prepare();
        mMediaPlayer.setOnBufferingUpdateListener(this);
        mMediaPlayer.setOnCompletionListener(this);
        mMediaPlayer.setOnPreparedListener(this);
        mMediaPlayer.setOnVideoSizeChangedListener(this);
        mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);

    } catch (Exception e) {
        Log.e(TAG, "error: " + e.getMessage(), e);
    }
}
public void onBufferingUpdate(MediaPlayer arg0, int percent) {
    Log.d(TAG, "onBufferingUpdate percent:" + percent);
}
public void onCompletion(MediaPlayer arg0) {
    Log.d(TAG, "onCompletion called");
}
public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
    Log.v(TAG, "onVideoSizeChanged called");
    if (width == 0 || height == 0) {
        Log.e(TAG, "invalid video width(" + width + ") or height(" + height + ")");
        return;
    }
    mIsVideoSizeKnown = true;
    mVideoWidth = width;
    mVideoHeight = height;
    if (mIsVideoReadyToBePlayed && mIsVideoSizeKnown) {
		startVideoPlayback();
    }
}
public void onPrepared(MediaPlayer mediaplayer) {
    Log.d(TAG, "onPrepared called");
    mIsVideoReadyToBePlayed = true;
    if (mIsVideoReadyToBePlayed && mIsVideoSizeKnown) {
        startVideoPlayback();
    }
}
@Override
protected void onPause() {
    super.onPause();
    releaseMediaPlayer();
    doCleanUp();
}
@Override
protected void onDestroy() {
    super.onDestroy();
    releaseMediaPlayer();
    doCleanUp();
}
private void releaseMediaPlayer() {
    if (mMediaPlayer != null) {
        mMediaPlayer.release();
        mMediaPlayer = null;
    }
}
private void doCleanUp() {
    mVideoWidth = 0;
    mVideoHeight = 0;
    mIsVideoReadyToBePlayed = false;
    mIsVideoSizeKnown = false;
}
private void startVideoPlayback() {
    Log.v(TAG, "startVideoPlayback");
    holder.setFixedSize(mVideoWidth, mVideoHeight);
    mMediaPlayer.start();
}

Как видно в коде, и для звука, и для видео MediaPlayer перемещается между состояниями жизненного цикла согласно рассмотренной выше схеме изменений состояния.

 Visit Intel Android для получения дополнительных ресурсов для ваших приложений в рамках проектов для ОС Android с архитектурой Intel.

Справочные материалы

  1. http://developer.android.com/reference/android/media/MediaPlayer.html
  2. http://developer.android.com/reference/android/widget/MediaController.html

IIntel и эмблема Intel являются товарными знаками корпорации Intel в США и в других странах. © Intel Corporation, 2013. Все права защищены. * Прочие наименования и товарные знаки могут быть собственностью третьих лиц.

Кодирование видео с использованием встроенного видео Intel HD Graphics

$
0
0

В этой статье речь пойдет о кодировании видео с использованием видеокодека h264 на GPU, интегрированном в современные процессоры Intel и о том опыте, которая приобрела наша компания Inventos в процессе создания и оптимизации медиа сервера Streambuilderдля обработки потокового видео.

 

Введение

Задача, поставленная перед нами заключалось в том, что бы в наш медиа сервер Streambuilder добавить поддержку технологии Intel Quick Sync. Наш медиасервер представляет собой эдакий «комбайн» на все случаи жизни и умеет следующее:

  • Кодирование/ресемплинг аудио/видео из почти всех популярных потоковых форматов в HLS и RTMP;

  • Поддержка съема сигнала с SDI, DVB;

  • Резервированиеи масштабирование серверов кодирования;

  • Описание конфигурации кодировщика на встроенном языке;

  • Различные модули для нормализации звука, усиления, деинтерлейсинга видео и т.д

 

Этот продукт является бэкендом медиа платформы Webcaster.pro. Решение написано на C++ с использованием библиотек libavcodec, в которые имплементированы многие известные кодеки, такие как h264, mpeg4 и т.д. Такое решение позволяет достаточно быстро разворачивать инфраструктуру доставки контента любой сложности и является гибким в плане конфигурирования.

 

При помощи конфигурационных файлов мы можем описывать графы, удовлетворяющие почти любым потребностям. В сильно упрощенном виде этот граф будет выглядеть следующим образом:

005f6148a18db135a085d18b149352eb.jpg

 

Несмотря на то, что код библиотек libavcodec хорошо оптимизирован, он рассчитан на работу на CPU, содержащим конечное число исполнительных устройств, таком, как x86 based. Увеличение количества ядер только отчасти решает проблему, так как обходится это недешево, да и ядра всегда есть чем загрузить, помимо кодирования видео. И логичным шагом была попытка использовать возможности графических ускорителей для решения данной задачи.

 

Решение от Intel

В качестве эксперимента мы решили попробовать ускорить кодирование видео при помощи графических сопроцессоров Intel HD Graphics, встроенных в современные процессоры Intel. Intel любезно предоставляет свой Media SDK для кодирования, декодирования, ресемплинга и других алгоритмов видео обработки. Данный SDK, к нашей большой радости, теперь доступен и для Linux, что крайне важно для промышленного использования. Именно благодаря появлению поддержки Linux мы заинтересовались этим решением. Коллег из Intel тоже заинтересовали результаты практического использования данного SDK в условиях промышленного использования. При этом, должен отметить, на протяжении всего периода разработки сотрудники Intel нам очень сильно помогали, отвечали на вопросы (которых было много поначалу) и давали действительно ценные советы.

 

В комплекте вместе с Media SDK идет хорошая документация и примеры почти на все случаи жизни. Процесс интеграции Intel Media SDK сильно упростило наличие примеров, без них он, надо сказать, покажется не самым тривиальным. Суть интеграции заключалась в замены наиболее требовательных к аппаратной части софтверных модулей кодирования/декодирования/ресемплинга на соответствующие модули, использующие аппаратные возможности Intel HD Graphics

 

Тестирование

Для тестирования нашего ПО был выбран 1U сервер в следующей конфигурации:


 

M/B

Supermicro X10SLH-F

Процессор

Intel® Xeon® CPU E3-1225 v3 @ 3.20GHz

Память

16 Гб

 

Версия OS на сервере Ubuntu 12.04.4 LTS 3.8.0-23-generic. Главным условием работы Quick Sync является наличие строки С226 в спецификации чипсета. Только чипы с такой маркировкой умеют работать с аппаратным кодированием видео. Кроме этого, желательно отсутствие встроенного видео на материнской плате, в противном случае могут возникнуть проблемы с определением, а значит, и использованием Intel GPU средствами Intel Media SDK.

Материнская плата, описанная выше, имеет интегрированную графику (встроенное видео) на борту, и нам пришлось повозиться для того, что бы заставить работать SDK на этом железе. При установке SDK на новый сервер скрипт установки Media SDK не увидел ID устройства. При этом, нам не удалось включить встроенную в процессор графику из BIOS. Поиск решения привел к необходимости обновить BIOS. После этого в BIOS появился заветный пункт. Однако, пришлось еще отключить встроенное на материнской плате видео путем переключения перемычки. В такой конфигурации не работает IPMI и выход на монитор, но мы работаем с сервером через SSH и это не так критично.

Кроме этого, есть некоторые ограничения на используемое в системе ядро Linux. Для серверов это Ubuntu 12.04 LTS с ядрами 3.2.0-41 и 3.8.0-23 или SUSE Linux Enterprise Server 11 с ядром SP3 3.0.76-11.

 

Процессор: E3-1225 V3, 16 Гб ОЗУ, Intel® HD Graphics P4600

 

ffmpeg

sample_full_transcode

streambuilder (no optimization)

streambuilder (optimization)

time

8 мин. 42 с

1 мин. 19 с

2 мин.19 с

1 мин. 40 с

cpu (max)

750%

55%

125%

50%

mem (max)

3,3%

4,6%

0.5%

0.4%

PSNR

48,107

46,68

  

Average PSNR

51,204

49,52

  

SSIM

0,99934

0,9956

  

MSE

1,623

2,969

  

 

Процессор: I7-3770, 3 Гб ОЗУ, Intel® HD Graphics 4000

 

ffmpeg

sample_full_transcode

streambuilder (no optimization)

streambuilder (optimization)

time

8 мин. 48 с

1 мин. 24 с

2 мин. 31 с

1 мин. 23 с

cpu (max)

750%

19%

150%

45%

mem (max)

18%

20%

2.8%

2.3%

PSNR

48,107

46,495

  

Average PSNR

51,204

49,27

  

SSIM

0,99934

0,991

  

MSE

1,623

3,036

  

 

Процессор E3-1285 v3, 16 Гб, Intel® HD Graphics P4700

 

ffmpeg

sample_full_transcode

streambuilder (no optimization)

streambuilder (optimization)

time

8 мин. 1 с

1 мин. 11 с

2 мин. 11 с

1 мин. 34 с

cpu (max)

750%

55%

130%

55%

mem (max)

3,3%

4,6%

0.5%

0,4%

PSNR

48,107

46,68

  

Average PSNR

51,204

49,52

  

SSIM

0,99934

0,9956

  

MSE

1,623

2,969

  

 

Анализ результатов

Метрики для streambuilder соответствуют полученным метрикам для тестовой утилиты sample_full_transcode и я их опустил.

Из этих таблиц видно, что серверные процессоры с Intel® HD Graphics P4700/P4600 в данном эксперименте работают быстрее и дают лучшее качество кодирования, чем I7-3770, Intel® HD Graphics 4000. Однако этот тезис не всегда верен, так как Intel совершенствует качество кодирования с каждой новой версией чипа и SDK и скорость на новых чипах может быть меньше. При этом нагрузка на CPU у первых немного больше. С чем это связано, пока непонятно.

Кроме этого, оптимизация работы с памятью дала прирост примерно в 2 раза в плане производительности.

 

Качество кодирования на Intel® HD Graphics P4700 получилось таким же, как и на Intel® HD Graphics P4600, но E3-1285 v3 работает быстрее примерно на 14% при той же загрузке ресурсов. Кроме этого, E3-1285 v3 быстрее E3-1225 V3 в кодировании при помощи ffmpeg примерно на 10%.

Сервер с установленным streambuilder с поддержкой Quick Sync позволяет кодировать один источник в 12 качеств Full HD (1080p), 24 качества HD (720p) и 46 качеств SD (480p) с нарезкой в HLS. Если это съем «сырого» сигнала с SDI, то число одновременно кодируемых качеств немного больше.

Поэкспериментировать со streambuilder (пока только libavcodec based версия) можно скачавc сайта продукта: http://streambuilder.pro. С ним в комплекте идет стандартный конфиг, позволяющий записывать в формат HLS любой источник.

 

Итоги

Технология Intel Quick Sync позволяет собрать сравнительно недорогой производительный сервер для кодирования видео с приемлемым качеством. В процессе внедрения этой технологии мы столкнулись с некоторыми техническими проблемами, связанными с наличием интегрированного в материнскую плату видео, которые, впрочем вполне решаемы. Напомним, главное при выборе железа для этих целей — это чип со спецификацией С226 и материнская плата без интегрированного видео, так как с ним может не работать IPMI и VGA выход).

Плюсы такого решения, на мой взгляд, это то, что почти не задействован CPU, а также небольшое потребление памяти. При этом, свободные ресурсы можно использовать для других задач или для дополнительногокодирования средствами CPU.

Игра World of Goo под управлением Android* на платформе x86

$
0
0

Введение

World of Goo — это игра-головоломка, основанная на законах физики. Выпускает эту игру компания 2D Boy из Сан-Франциско. Ведущие разработчики — Рон Кармел и Кайл Гэблер, ранее работавшие разработчиками игр в EA. Игра World of Goo сочетает простоту с глубоким игровым процессом и захватывающей атмосферой. Помимо прочих наград и премий, игра World of Goo занесена в Книгу рекордов Гиннеса как игра с самым высоким рейтингом для iOS*;

Игра World of Goo была выпущена для Windows* и WiiWare* в октябре 2008 года, а вскоре после этого появились версии игры для OS X* и Linux*. Пользовательская база постоянно росла, но оставался еще обширнейший не охваченный рынок — мобильные устройства. В апреле 2011 года вышла версия этой игры для iOS, а в ноябре этого же года — для Android. Добавление поддержки устройств Intel® оказалось чрезвычайно простым и позволило разработчикам 2D Boy расширить аудиторию еще на несколько миллионов пользователей.

Прошлогодний мобильный мир

Игра компании 2D Boy получила распространение на всех домашних развлекательных платформах к 2010 году. Нужно было искать способы привлечения новых пользователей. В этом же году была сделана попытка перенести игру на платформу iOS. Из-за меньшей мощности этой платформы пришлось существенно переработать игру, чтобы сохранить ее работоспособность. Это было всего несколько лет назад, но с тех пор мобильные устройства значительно изменились. Устройства, для которых разработчики 2D Boy оптимизировали код в 2010 году, по производительности уступают современным устройствам с процессорами Intel более чем вдвое. Для такого изменения производительности потребовалось внедрить новые решения и принять во внимание различия, свойственные мобильным устройствам.

Рисунок 1:Проблемы маячат над головой.

Переработка текстур для iOS

При переносе для iOS выяснилось, что обработка текстур расходует слишком много ресурсов системы. Чтобы снизить ресурсоемкость отрисовки, было принято решение объединять полигоны в более крупные пакеты. За счет этого можно было сократить количество вызовов процедур отрисовки. Разработчики 2D Boy использовали динамические текстуры OpenGL*, благодаря совместимости с множеством платформ их удалось использовать в разных версиях игры. Тем не менее потребовалось снизить объем текстур, поскольку чтение большого количества файлов из памяти достаточно ресурсоемкая операция. Кроме того, нужно было реализовать возможность перезагрузки текстур, поскольку они сбрасывались из памяти при возобновлении приложения из приостановленного состояния. При этом также возрастала нагрузка на ресурсы. Загрузку текстур удалось существенно ускорить за счет объединения текстур в так называемый «атлас» — крупную объединенную текстуру; внутри нее можно ссылаться на отдельные части, указывая их адреса. Такой подход стал стандартным для разработки мобильных приложений и игр.

Перенос World of Goo на iOS обеспечил разработчикам еще миллион загрузок игры. Но специалисты 2D Boy, не останавливаясь на достигнутом, решили охватить и другие мобильные платформы.

Рисунок 2:Пользователи ждут!

На пути к Android

По сравнению со всесторонней оптимизацией, которая потребовалась для первоначального перехода на мобильные устройства (для платформы iOS), выпуск игры для устройств Android стал возможным при достаточно незначительной доработке кода. Понадобилось слегка настроить контекст OpenGL, чтобы такие элементы, как атлас текстур, не исчезали при возобновлении работы из памяти (множество статей, посвященных OpenGL на Android, см. на портале Intel Developer Zone).

После переноса игры на Android для компиляции для устройств с процессором Intel оказалось достаточно всего лишь добавить один флаг в сценарий компиляции. Благодаря высокой мощности мобильных устройств с процессорами Intel всех прежних мер по оптимизации игры оказалось более чем достаточно для высокой производительности игры, никаких прочих доработок не потребовалось. Действия по компиляции приложения для платформ с процессорами Intel:

  1. В папке <project root>/jni/ fприложения для Android создайте или откройте файл Application.mk (это мейк-файл с параметрами для всего приложения).
  2. Если этот файл был создан автоматически, в нем будет переменная APP_ABI, определяющая платформы назначения для сборки. В большинстве случаев по умолчанию будет задана платформа назначения ARM* v7 с помощью флага «APP_ABI := armeabi-v7a».
  3. Добавить поддержку платформы x86 можно двумя способами: либо создать универсальную сборку для всех платформ, либо выпустить специализированную сборку только для платформы x86:
    1. Создание «толстого» двоичного файла, содержащего сборки для всех целевых платформ:
      1. Замените ‘APP_ABI := armeabi-v7a’ на ‘APP_ABI := all’ ИЛИ
      2. Замените ‘APP_ABI := armeabi-v7a’ на ‘APP_ABI := x86 armeabi-v7a’
         

       
    2. Чтобы собрать код только для платформы x86, просто замените ‘APP_ABI := armeabi-v7a’ на ‘APP_ABI := x86’

TНедостаток «толстого» двоичного файла состоит в том, что увеличивается размер файла; для крупных приложений это может оказаться серьезной проблемой. После того как пользователь загрузит программу, операционная система автоматически выбирает нужный сегмент двоичного файла. Если приложение уже приближается к ограничению в 50 мегабайт, следует создать отдельные двоичные файлы для каждой платформы и выложить их по отдельности в магазине приложений, чтобы портал магазина определял, какая версия файла требуется каждому конкретному пользователю.

В случае с игрой World of Goo оказалось возможно использовать универсальный «толстый» двоичный файл, поскольку объем всего пакета не превышал 50 МБ.

Примечание. Если в вашей сборке используется нестандартная цепочка инструментов, для переноса приложения на платформу x86 следует выполнить стандартную последовательность сборки в папке x86 вместо папки ARM.

Рисунок 3:Восполнение пробела.

Окупаемость мобильных приложений.

За счет охвата мобильных сегментов и выпуска различных версий своей игры для всех основных рынков компания 2D Boy занимает первые (или близкие к таковым) места во всех рейтингах. Для переноса игры на мобильные платформы потребовались значительные усилия, но популярность World of Goo является превосходным примером того, как эти усилия в конечном итоге с лихвой окупились. Если игра уже хорошо подходит для сенсорного управления, требуется совсем немного действий, чтобы играть в нее можно было и на смартфонах. Если игровой движок не использует ресурсоемкую графику, мобильная версия игры не утратит внешней привлекательности и не будет слишком активно расходовать электроэнергию. Для этой задачи игра World of Goo оказалась идеальным кандидатом, о чем свидетельствует отличный рейтинг этой игры и множество полученных ей наград и премий.

Справочные материалы

Об авторе

Брэд Хилл (Brad Hill) — инженер по программному обеспечению в подразделении Developer Relations корпорации Intel. Брэд занимается изучением новых технологий на оборудовании Intel и обменивается лучшими методиками с разработчиками ПО на форумах Intel Developer Zone и на конференциях разработчиков. Кроме того, он исполняет обязанности технического директора на студенческих конкурсах разработчиков Hackathon, возглавляет инициативу Code for Good в высших учебных заведениях по всей стране.

NOTICES

INFORMATION IN THIS DOCUMENT IS PROVIDED IN CONNECTION WITH INTEL PRODUCTS. NO LICENSE, EXPRESS OR IMPLIED, BY ESTOPPEL OR OTHERWISE, TO ANY INTELLECTUAL PROPERTY RIGHTS IS GRANTED BY THIS DOCUMENT. EXCEPT AS PROVIDED IN INTEL'S TERMS AND CONDITIONS OF SALE FOR SUCH PRODUCTS, INTEL ASSUMES NO LIABILITY WHATSOEVER AND INTEL DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTY, RELATING TO SALE AND/OR USE OF INTEL PRODUCTS INCLUDING LIABILITY OR WARRANTIES RELATING TO FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR INFRINGEMENT OF ANY PATENT, COPYRIGHT OR OTHER INTELLECTUAL PROPERTY RIGHT.

UNLESS OTHERWISE AGREED IN WRITING BY INTEL, THE INTEL PRODUCTS ARE NOT DESIGNED NOR INTENDED FOR ANY APPLICATION IN WHICH THE FAILURE OF THE INTEL PRODUCT COULD CREATE A SITUATION WHERE PERSONAL INJURY OR DEATH MAY OCCUR.

Intel may make changes to specifications and product descriptions at any time, without notice. Designers must not rely on the absence or characteristics of any features or instructions marked "reserved" or "undefined." Intel reserves these for future definition and shall have no responsibility whatsoever for conflicts or incompatibilities arising from future changes to them. The information here is subject to change without notice. Do not finalize a design with this information.

The products described in this document may contain design defects or errors known as errata which may cause the product to deviate from published specifications. Current characterized errata are available on request.

Contact your local Intel sales office or your distributor to obtain the latest specifications and before placing your product order.

Copies of documents which have an order number and are referenced in this document, or other Intel literature, may be obtained by calling 1-800-548-4725, or go to: http://www.intel.com/design/literature.htm

Software and workloads used in performance tests may have been optimized for performance only on Intel microprocessors. Performance tests, such as SYSmark* and MobileMark*, are measured using specific computer systems, components, software, operations, and functions. Any change to any of those factors may cause the results to vary. You should consult other information and performance tests to assist you in fully evaluating your contemplated purchases, including the performance of that product when combined with other products.

Any software source code reprinted in this document is furnished under a software license and may only be used or copied in accordance with the terms of that license.

Intel and the Intel logo are trademarks of Intel Corporation in the U.S. and/or other countries.

Copyright © 2014 Intel Corporation. All rights reserved.

*Other names and brands may be claimed as the property of others.


Управление режимами вычислений с плавающей запятой при использовании Intel® Threading Building Blocks

$
0
0

В Intel® Threading Building Blocks (Intel® TBB) 4.2, обновление 4, появилась расширенная поддержка управления параметрами вычислений с плавающей запятой. Теперь эти параметры можно указать при вызове большинства параллельных алгоритмов (включая flow::graph). В этом блоге мне бы хотелось рассказать о некоторых особенностях, новых функциях и общей поддержке вычислений с плавающей запятой в Intel TBB. Этот блог не посвящен общей поддержке вычислений с плавающей запятой в ЦП. Если вы незнакомы с поддержкой вычислений с плавающей запятой в ЦП, рекомендую начать с раздела Операции с плавающей запятойв справочном руководстве Intel® C++ Compiler. Для получения дополнительных сведений о сложностях арифметики с плавающей запятой рекомендую классику “Все, что необходимо знать об арифметике с плавающей запятой”.

В Intel TBB предлагается два способа, при помощи которых можно задать нужные параметры вычислений с плавающей запятой для задач, выполняемых планировщиком задач Intel TBB:

  1. Когда планировщик задач инициализируется для заданного потока приложения, он получает текущие параметры вычислений с плавающей запятой этого потока;
  2. У класса task_group_context есть метод для получения текущих параметров вычислений с плавающей запятой.

Рассмотрим первый подход. Такой подход, по сути, является неявным: планировщик задач всегда и безусловно получает параметры вычислений с плавающей запятой в момент своей инициализации. Затем сохраненные параметры используются для всех задач, связанных с этим планировщиком задач. Другими словами, такой подход можно рассматривать как свойство планировщика задач. Поскольку это свойство планировщика задач, мы можем применять и управлять параметрами вычислений с плавающей запятой в нашем приложении двумя способами:

  1. Планировщик задач создается для каждого потока, поэтому мы можем запустить новый поток, задать нужные параметры, а затем инициализировать для этого потока новый планировщик задач (явно или неявно), который получит параметры вычислений с плавающей запятой;
  2. Если поток уничтожает планировщик задач и инициализирует новый, можно будет получить новые параметры. Можно указать новые параметры вычислений с плавающей запятой перед повторным созданием планировщика. При создании нового планировщик задач параметры будут применены для всех задач.

Я попробую показать некоторые особенности в следующих примерах:

Обозначения:

  • “fp0”, “fp1” and “fpx” – состояния, описывающие параметры вычислений с плавающей запятой;
  • “set_fp_settings( fp0 )” и “set_fp_settings( fp1 )” – указание параметров вычислений с плавающей запятой для текущего потока;
  • “get_fp_settings( fpx )” – получение параметров вычислений с плавающей запятой из текущего потока и сохранение этих параметров в “fpx”.

Пример #1. Планировщик задач по умолчанию.

// Suppose fp0 is used here.
// Every Intel TBB algorithm creates a default task scheduler which also captures floating-point
// settings when initialized.
tbb::parallel_for( tbb::blocked_range<int>( 1, 10 ), []( const tbb::blocked_range<int> & ) {
    // fp0 will be used for all iterations on any Intel TBB worker thread.
} );
// There is no way anymore to destroy the task scheduler on this thread.

Пример #2. Настраиваемый планировщик задач.

// Suppose fp0 is used here.
tbb::task_scheduler_init tbb_scope;
tbb::parallel_for( tbb::blocked_range<int>( 1, 10 ), []( const tbb::blocked_range<int> & ) {
    // fp0 will be used for all iterations on any Intel TBB worker thread.
} );

В целом, пример 2 действует так же, как пример 1, но зато предоставляет способ вручную завершить работу планировщика задач.

Пример #3. Повторная инициализация планировщика задач.

// Suppose fp0 is used here.
{
    tbb::task_scheduler_init tbb_scope;
    tbb::parallel_for( tbb::blocked_range<int>( 1, 10 ), []( const tbb::blocked_range<int> & ) {
        // fp0 will be used for all iteration on any Intel TBB worker thread.
    } );
} // the destructor calls task_scheduler_init::terminate() to destroy the task scheduler
set_fp_settings( fp1 );
{
    // A new task scheduler will capture fp1.
    tbb::task_scheduler_init tbb_scope;
    tbb::parallel_for( tbb::blocked_range<int>( 1, 10 ), []( const tbb::blocked_range<int> & ) {
        // fp1 will be used for all iterations on any Intel TBB worker
        // thread.
    } );
}

Пример #4. Еще один поток.

void thread_func();
int main() {
    // Suppose fp0 is used here.
    std::thread thr( thread_func );
    // A default task scheduler will capture fp0
    tbb::parallel_for( tbb::blocked_range<int>( 1, 10 ), []( const tbb::blocked_range<int> & ) {
        // fp0 will be used for all iterations on any Intel TBB worker thread.
    }
    thr.join();
}
void thread_func() {
    set_fp_settings( fp1 );
    // Since it is another thread, Intel TBB will create another default task scheduler which will
    // capture fp1 here. The new task scheduler will not affect floating-point settings captured by
    // the task scheduler created on the main thread.
    tbb::parallel_for( tbb::blocked_range<int>( 1, 10 ), []( const tbb::blocked_range<int> & ) {
        // fp1 will be used for all iterations on any Intel TBB worker thread.
    }
}

Обратите внимание, что Intel TBB может повторно использовать одни и те же рабочие потоки для обоих parallel_for, несмотря на то, что они вызваны из разных потоков. При этом гарантируется, что все итерации parallel_for в главном потоке будут использовать fp0, а все итерации второго parallel_for — fp1.

Пример #5. Изменение параметров вычислений с плавающей запятой в потоке пользователя.

// Suppose fp0 is used here.
// A default task scheduler will capture fp0.
tbb::parallel_for( tbb::blocked_range<int>( 1, 10 ), []( const tbb::blocked_range<int> & ) {
    // fp0 will be used for all iterations on any Intel TBB worker thread.
} );
set_fp_settings( fp1 );
tbb::parallel_for( tbb::blocked_range<int>( 1, 10 ), []( const tbb::blocked_range<int> & ) {
    // fp0 will be used despite the floating-point settings are changed before Intel TBB parallel
    // algorithm invocation since the task scheduler has already captured fp0 and these settings
    // will be applied to all Intel TBB tasks.
} );
// fp1 is guaranteed here.

Второй parallel_for оставит fp1 неизменным в пользовательском потоке (несмотря на то, что для всех итераций используется fp0), поскольку в Intel TBB гарантируется отсутствие изменений параметров вызывающего потока вызовом любого параллельного алгоритма Intel TBB, даже если алгоритм выполняется с другими параметрами.

Пример #6. Изменение параметров вычислений с плавающей запятой в задаче Intel TBB.

// Suppose fp0 is used here.
// A default task scheduler will capture fp0
tbb::parallel_for( tbb::blocked_range<int>( 1, 10 ), []( const tbb::blocked_range<int> & ) {
    set_fp_settings( fp1 );
    // Setting fp1 inside the task will lead undefined behavior. There are no guarantees about
    // floating-point settings for any following tasks of this parallel_for and other algorithms.
} );
// No guarantees about floating-point settings here and following algorithms.

Если вам очень нужно использовать внутри задачи другие параметров вычислений с плавающей запятой, следует записать предыдущие параметры, а в конце задачи восстановить их:

// Suppose fp0 is used here.
// A default task scheduler will capture fp0
tbb::parallel_for( tbb::blocked_range<int>( 1, 10 ), []( const tbb::blocked_range<int> & ) {
    get_fp_settings( fpx );
    set_fp_settings( fp1 );
    // ... some calculations.
    // Restore captured floating-point settings before the end of the task.
    set_fp_settings( fpx );
}
// fp0 is guaranteed here.

Для решения большей части проблем можно использовать подход с применением планировщика задач для управления параметров вычислений с плавающей запятой. Но представьте себе ситуацию, когда для каждой из двух частей вычисления требуются разные параметры. Разумеется, можно использовать подходы, показанные в примерах 3 и 4. Но при этом могут возникнуть определенные затруднения:

  1. Реализация затруднена: в примере 3 невозможно управлять жизненным циклом объекта планировщика задач, а в примере 4 может потребоваться синхронизация между потоками;
  2. Влияние на производительность: в примере 3 нужно заново инициализировать планировщик задач, тогда как ранее этого не требовалось, а в примере 4 может возникнуть проблема избыточной подписки.

Что происходит с вложенными вычислениями при разных параметрах вычислений с плавающей запятой? Применение планировщика задач в этом случае затрудняется, поскольку потребуется писать много бесполезного кода.

В Intel TBB 4.2 U4 появился новый подход на основе task_group_context: функциональность task_group_context была расширена для управления параметрами вычислений с плавающей запятой для задач, связанных с ним, с помощью нового метода

void task_group_context::capture_fp_settings();

получающего эти параметры из вызывающего потока и передающего их своим задачам. Таким образом, вы можете без труда задать нужные параметры для того или иного параллельного алгоритма:

Пример #7. Задание параметров вычислений с плавающей запятой для определенного алгоритма.

// Suppose fp0 is used here.
// The task scheduler will capture fp0.
task_scheduler_init tbb_scope;
tbb::task_group_context ctx;
set_fp_settings( fp1 );
ctx.capture_fp_settings();
set_fp_settings( fp0 );
tbb::parallel_for( tbb::blocked_range<int>( 1, 10 ), []( const tbb::blocked_range<int> & ) {
    // In spite of the fact the task scheduler captured fp0 when initialized and the parallel
    // algorithm is called from thread with fp0, fp1 will be here for all iterations on any
    // Intel TBB worker  thread since task group context (with captured fp1) is specified for this
    // parallel algorithm.
}, ctx );

Пример 7 не особенно интересен, поскольку можно добиться такого же результата, если указать fp1 до инициализации планировщика задач. Рассмотрим гипотетическую проблему, когда для двух частей вычисления требуются разные параметры. Эту проблему можно решить так:

Пример #8. Задание параметров вычислений с плавающей запятой для разных частей вычислений.

// Suppose fp0 is used here.
// The task scheduler will capture fp0.
task_scheduler_init tbb_scope;
tbb::task_group_context ctx;
set_fp_settings( fp1 );
ctx.capture_fp_settings();
tbb::parallel_for( tbb::blocked_range<int>( 1, 10 ), []( const tbb::blocked_range<int> & ) {
    // In spite of the fact that floating-point settings are fp1 on the main thread, fp0 will be
    // here for all iterations on any Intel TBB worker thread since the task scheduler captured fp0
    // when initialized.
} );
// fp1 will be used here since TBB algorithms do not change floating-point settings which were set
// before calling.
tbb::parallel_for( tbb::blocked_range<int>( 1, 10 ), []( const tbb::blocked_range<int> & ) {
    // fp1 will be here since the task group context with captured fp1 is specified for this
    // parallel algorithm.
}, ctx );
// fp1 will be used here.

Я уже продемонстрировал одно свойство подхода на основе контекста группы задач в примерах 7 и 8: заданные таким способом параметры имеют более высокий приоритет, чем параметры, заданные с помощью планировщика задач, когда контекст указывается для параллельного алгоритма Intel TBB. При этом подходе наследуется еще одно свойство: вложенные параллельные алгоритмы наследуют параметры вычислений с плавающей запятой из контекста группы задач, указанного для внешнего параллельного алгоритма.

Пример #9. Вложенные параллельные алгоритмы.

// Suppose fp0 is used.
// The task scheduler will capture fp0.
task_scheduler_init tbb_scope;
tbb::task_group_context ctx;
set_fp_settings( fp1 );
ctx.capture_fp_settings();
tbb::parallel_for( tbb::blocked_range<int>( 1, 10 ), []( const tbb::blocked_range<int> & ) {
    // fp1 will be here
    tbb::parallel_for( tbb::blocked_range<int>( 1, 10 ), []( const tbb::blocked_range<int> & ) {
        // Although the task group context is not specified for the nested parallel algorithm and
        // the task scheduler has captured fp0, fp1 will be here.
    }, ctx );
} );
// fp1 will be used here.

Если нужно использовать планировщик задач внутри вложенного алгоритма, можно использовать контекст изолированной группы задач:

Пример #10. Вложенный параллельный алгоритм с изолированным контекстом группы задач.

// Suppose fp0 is used.
// The task scheduler will capture fp0.
task_scheduler_init tbb_scope;
tbb::task_group_context ctx;
set_fp_settings( fp1 );
ctx.capture_fp_settings();
tbb::parallel_for( tbb::blocked_range<int>( 1, 10 ), []( const tbb::blocked_range<int> & ) {
    // fp1 will be used here.
    tbb::task_group_context ctx2( tbb::task_group_context::isolated );
    tbb::parallel_for( tbb::blocked_range<int>( 1, 10 ), []( const tbb::blocked_range<int> & ) {
        // ctx2 is an isolated task group context so it will have fp0 inherited from the task
        // scheduler. That’s why fp0 will be used here.
    }, ctx2 );
}, ctx );
// fp1 will be used here.

В одном блоге невозможно продемонстрировать все возможности поддержки вычислений с плавающей запятой в Intel TBB. Но приведенные примеры демонстрируют основные идеи управления параметрами таких вычислений в Intel TBB для применения в реальных приложениях.

Основные принципы параметров вычислений с плавающей запятой можно объединить в следующий список:

  • Эти параметры можно задать для всех параллельных алгоритмов Intel TBB с помощью планировщика задач или для отдельных алгоритмов Intel TBB с помощью контекста группы задач;
  • Параметры, полученные контекстом группы задач, имеют приоритет над параметрами, полученными при инициализации планировщика задач;
  • По умолчанию все вложенные алгоритмы наследуют такие параметры с внешнего уровня, если не указан ни контекст группы задач с полученными параметрами, ни изолированный контекст группы задач;
  • При вызове параллельного алгоритма Intel TBB параметры вычислений с плавающей запятой вызывающего потока не изменяются, даже если алгоритм выполняется с другими параметрами;
  • Параметры, заданные после инициализации планировщика задач, недоступны для параллельных алгоритмов Intel TBB, если не используется подход с контекстом группы задач или не выполняется повторная инициализация планировщика задач;
  • Пользовательский код в задаче не должен менять эти параметры либо должен восстанавливать прежние параметры до окончания задачи.

P.S. Отложенный планировщик задач получает параметры с плавающей запятой при вызове метода инициализации.

Пример #11: Явная инициализация планировщика задач.

set_fp_settings( fp0 );
tbb::task_scheduler_init tbb_scope( task_scheduler_init::deferred );
set_fp_settings( fp1 );
init.initialize();
tbb::parallel_for( tbb::blocked_range<int>( 1, 10 ), []( const tbb::blocked_range<int> & ) {
    // The task scheduler is declared when fp0 is set but it will capture fp1 since it is
    // initialized when fp1 is set.
} );
// fp1 will used be here.

P.P.S. Будьте осторожны, если вы используете функцию автоматической записи в планировщике задач. Она не будет работать, если вызов вашей функции осуществляется внутри другого параллельного алгоритма Intel TBB.

Пример #12. Еще одно предупреждение: берегитесь библиотечных функций.

Фрагмент кода 1. Слегка измененный пример 1. Это работоспособный код, здесь нет ошибок.

set_fp_settings( fp0 );
// Run with the hope that Intel TBB parallel algorithm will create a default task scheduler which
// will also capture floating-point settings when initialized.
tbb::parallel_for( tbb::blocked_range<int>( 1, 10 ), []( const tbb::blocked_range<int> & ) {...} );

Фрагмент кода 2. Достаточно вызвать фрагмент кода 1 как библиотечную функцию.

set_fp_settings( fp1 );
tbb::parallel_for( tbb::blocked_range<int>( 1, 10 ), []( const tbb::blocked_range<int> & ) {
    call “code snippet 1”;
}
// Possibly, you will want to have fp1 here but see the second bullet below.

Этот пример выглядит вполне безобидным, поскольку фрагмент кода 1 задаст нужные параметры и будет выполнять вычисления с fp0. Но в этом примере есть две проблемы:

  1. К моменту вызова фрагмента кода 1 планировщик задач уже будет инициализирован и уже получит fp1. Поэтому фрагмент кода 1 будет выполнять вычисления с fp1, не учитывая параметры fp0;
  2. Изоляция пользовательских параметров вычислений с плавающей запятой нарушается, поскольку фрагмент кода 1 изменяет эти параметры внутри задачи Intel TBB, но не восстанавливает первоначальные параметры. Поэтому нет никаких гарантий относительно параметров вычислений с плавающей запятой после выполнения параллельного алгоритма Intel TBB во фрагменте кода 2.

Фрагмент кода 3. Исправленное решение.

Исправим фрагмент кода 1:

// Capture the incoming fp settings.
get_fp_settings( fpx );
set_fp_settings( fp0 );
tbb::task_group_context ctx;
ctx.capture_fp_settings();
tbb::parallel_for( tbb::blocked_range<int>( 1, 10 ), []( const tbb::blocked_range<int> &r ) {
    // Here fp0 will be used for all iterations on any Intel TBB worker thread.
}, ctx );
// Restore fp settings captured before setting fp0.
set_fp_settings( fpx );

Фрагмент кода 2 остается неизменным.

set_fp_settings( fp1 );
tbb::parallel_for( tbb::blocked_range<int>( 1, 10 ), []( const tbb::blocked_range<int> &r ) {
    call “fixed code snip 1”.
} );
// fp1 will be used here since the “fixed code snippet 1” does not change the floating-point
// settings visible to “code snippet 2”.

 

Игра Legend of Xuan Yuan: высшее качество игры на трансформерах, с сенсорным управлением и акселерометром

$
0
0

Download PDF

Аннотация

Компания Tencent решила повысить удобство игры на ультрабуках и трансформерах. Игра Legend of Xuan Yuan и так была весьма успешной, но в использовании этих систем разработчики Tencent увидели новую возможность. Сейчас на рынке представлено немало трансформеров, то есть систем, которые могут работать и в режиме обычного ноутбука, и в режиме планшета. Разработчики Tencent совместно с инженерами Intel работали над отбором моделей планшетов и ноутбуков, поддерживающих изменение состояния игры. Пользовательский интерфейс игры был обновлен для поддержки сенсорного управления, которое стало одной из важнейших и удобнейших функций на планшетах. И наконец, благодаря датчикам появилась возможность по-новому управлять игрой: для выполнения определенных действий можно было встряхнуть планшет.

Первая трехмерная многопользовательская сетевая игра MMOPRG на китайском рынке

Компания Tencent — крупнейший разработчик игр в Китае. Поскольку количество трансформеров на китайском рынке постоянно растет, разработчики Tencent решили предоставить владельцам таких устройств уникальные игровые возможности. Всего через два года после выпуска игра Legend of Xuan Yuan уже стала весьма популярной. В силу широкого распространения ультрабуков и трансформеров было решено добавить в игру поддержку сенсорного управления и акселерометров. Трехмерные массовые многопользовательские сетевые ролевые игры (MMORPG) очень популярны в Китае, но до Legend of Xuan Yuan ни в одной из них не была реализована поддержка сенсорного управления. Компания Tencent получила возможность первой внедрить новую технологию, но при этом возник и риск: окажется ли новинка успешной? В этом примере описывается, каким образом разработчики Tencent при поддержке инженеров Intel изменили игру так, чтобы повысить ее удобство на трансформерах и ультрабуках под управлением Windows* 8.

В Legend of Xuan Yuan используется два разных пользовательских интерфейса для режимов планшета и ноутбука. На трансформерах игра определяет, в каком из этих режимов устройство работает в данный момент. В режиме ноутбука игра использует клавиатуру и мышь. В режиме планшета игра переключается на сенсорный интерфейс. Разработчики Tencent стремились добиться плавного перехода между традиционным режимом ноутбука и сенсорным управлением в игре. Игрок не испытывает неудобства, поскольку интерфейс автоматически переключается между режимами. В этом примере мы рассмотрим обнаружение режима работы трансформера и переключение интерфейса в соответствии с текущим режимом.

Преобразование существующего пользовательского интерфейса в сенсорный может быть непростой задачей. Это особенно трудно для игр со сложным интерфейсом, где используются щелчки левой и правой кнопками мыши, а также множество командных клавиш. Не существует единственно верного подхода для адаптации таких интерфейсов. Реализация плавного и точного сенсорного управления требует немалого внимания. Поскольку игра уже достаточно популярна, нужно делать как можно меньше изменений, чтобы не вызывать отторжения у существующих игроков. Мы рассмотрим устройство интерфейса.

Поскольку трансформеры оборудованы акселерометрами, разработчики Tencent также добавили поддержку особо мощной атаки против врагов, если встряхнуть устройство при игре.

Изменение режима игры в соответствии с режимом трансформера

Игра Legend of Xuan Yuan содержит два интерфейса и динамически переключается между ними в зависимости от режима работы трансформера. Когда трансформер работает в режиме ноутбука, игра Legend of Xuan Yuan ведет себя совершенно обычным образом, для ее управления используется клавиатура и мышь. В режиме планшета применяется сенсорное управление. Как это работает?

Вот как мы это сделали: определение состояния трансформера и переключение режима интерфейса

Игра Legend of Xuan Yuan ожидает сообщение WM_SETTINGCHANGE. Это сообщение оповещает приложения об изменениях состояния системы. При изменении состояния трансформера поступает сообщение WM_SETTINGCHANGE с LPARAM, указывающим на строку со значением «ConvertibleSlateMode». Вызов GetSystemMetrics (SM_CONVERTIBLESLATEMODE) возвращает текущее состояние.

В планшетном режиме игры на экран накладывается интерфейс с сенсорными кнопками для различных действий. В режиме ноутбука этот интерфейс скрывается.

В игре Legend of Xuan Yuan используется следующий код обнаружения:

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  case WM_SETTINGCHANGE:
    if (lParam != NULL && wcscmp(TEXT("ConvertibleSlateMode"), (TCHAR *)lParam) == 0)
    {
      BOOL bSlateMode = (GetSystemMetrics(SM_CONVERTIBLESLATEMODE) == 0);
      if (bSlateMode == True) //Slate mode, display the Touch overlay UI
        …
      else //Laptop mode, hide the touch overlay UI
        …
    }

Рисунок 1. Код для обнаружения изменения состояния системы и для проверки режима системы.

Дополнительные сведения см. в примере кода с поддержкой трансформеров.

Чтобы такой подход работал, он должен поддерживаться изготовителем системы; должен быть установлен соответствующий драйвер. В некоторых системах драйвер не всегда работает правильно, поэтому мы добавили возможность переключения интерфейса вручную. Далее описывается, как работает интерфейс.

Теперь ваша очередь: определите, в каком режиме используется устройство: ноутбука или планшета

Как определить состояние устройства в игре и как изменить интерфейс? Для наибольшего удобства на трансформерах ваша игра должна быть «двуличной»: она должна динамически переключать интерфейс в зависимости от состояния устройства.

Прежде всего, спланируйте интерфейс для режима ноутбука и режима планшета. Подумайте о том, как устройство будут держать в руках или как оно будет стоять на столе. Выберите наиболее удобный для игроков способ взаимодействия с интерфейсом. При проектировании сенсорного интерфейса оставьте на экране больше места для сенсорных элементов управления, чем обычно требуется для кнопок, нажимаемых мышью. В противном случае игрокам будет непросто нажимать нужные элементы управления.

Кроме того, сенсорное взаимодействие зачастую медленнее, чем управление с помощью клавиатуры и мыши, не забывайте об этом. Игровым меню также нужен и интерфейс для клавиатуры с мышью, и сенсорный интерфейс.

Неплохо бы при запуске игры проверять состояние устройства с помощью GetSystemMetrics и соответствующим образом выбирать интерфейс. Помните, что не все системы правильно передают сведения о своем состоянии и не всегда уведомляют вашу игру об изменениях состояния, поэтому выберите начальный интерфейс игры по умолчанию на случай, если текущее состояние устройства не удастся определить.

Следите за сообщением WM_SETTING CHANGE после запуска игры. Когда такое сообщение появится, проверьте его содержимое: LPARAM должен указывать на строку со значением «ConvertibleSlateMode». Это значение указывает, что игра должна вызвать GetSystemMetrics(SM_CONVERTlBLESLATEMODE) и проверить, следует ли сменить интерфейс.

Обнаружение может не всегда действовать, поскольку не все системы могут правильно сообщать об изменении состояния. Пожалуй, если обнаружение не дает достоверный результат, по умолчанию игра должна использовать режим ноутбука. И в любом случае должен быть способ вручную переключить интерфейс между режимом мыши и клавиатуры и режимом планшета.

Полный пример, обнаруживающий изменение состояния системы и меняющий интерфейс, см. в образце с поддержкой трансформеров. Более сложный код с обнаружением пристыковки клавиатуры, изменения ориентации экрана и т. д. см. в примере с обнаружением состояния.

Выбор типа сенсорных сообщений

Перед добавлением поддержки сенсорного управления в существующее приложение необходимо выбрать, какой тип сообщений Window вы собираетесь поддерживать. Выберите один из трех наборов сообщений Window: WM_POINTER, WM_GESTURE или WM_TOUCH. Давайте посмотрим, каким образом мы делали этот выбор для игры Legend of Xuan, чтобы помочь вам принять верное решение.

Как мы это сделали: сравнение типов сенсорных сообщений

Поддержка сенсорного управления — основная «изюминка» новой версии игры Legend of Xuan Yuan. Когда игроки используют сенсорный экран, они видят новый интерфейс с набором сенсорных элементов управления.

WM_POINTER — простейший с точки зрения написания кода тип сообщений, он поддерживает широкий набор жестов. Но работает WM_POINTER только в Windows 8 и более поздних версиях. Разработчикам Tencent требовалось поддерживать огромную базу существующих пользователей, применяющих Windows 7, поэтому от WM_POINTER сразу же отказались.

Перед обсуждением остальных типов сообщений давайте рассмотрим основные элементы интерфейса игры Legend of Xuan Yuan. Сенсорный интерфейс игры использует экранные кнопки в качестве элементов управления. Эти элементы управления можно использовать и для движения, и для действий. Элементы управления движением и действиями находятся на противоположных сторонах экрана, чтобы можно было использовать для управления обе руки. Элементы управления расположены в нижней части экрана. Кроме того, в верхней части экрана находится значок, раскрывающий каскадное меню для более сложных элементов интерфейса. Устройство интерфейса мы обсудим позже, но уже понятно, как именно элементы интерфейса должны работать.


Рисунок 2: Экранный сенсорный интерфейс в правом и левом нижних углах.

Игра должна распознавать одновременное касание в разных местах экрана. Поскольку несколько касаний должны работать одновременно, такое управление называется мультисенсорным.

Теперь, когда мы изучили основные компоненты мультисенсорного интерфейса, можно сравнить оставшиеся типы сенсорных сообщений: WM_GESTURE и WM_TOUCH. Проще всего создавать код для WM_GESTURE — здесь поддерживаются типовые жесты, такие как масштабирование двумя пальцами и боковая прокрутка. Сообщения этого типа скрывают некоторые детали сенсорного взаимодействия и передают коду вашей игры полный жест после его завершения. События простых касаний по-прежнему отправляются в игру как сообщения мыши. Это означает, что типовой сенсорный интерфейс можно реализовать с помощью сообщений мыши для событий простых касаний и с помощью WM_GESTURE для сложных жестов.

WM_GESTURE поддерживает только жесты, включающие один набор связанных точек касания. Из-за этого затрудняется поддержка жестов мультисенсорных интерфейсов, где игрок касается экрана в нескольких местах. WM_GESTURE плохо подходит для этой игры.

WM_TOUCH — это сообщение сенсорного управления самого низкого уровня. Оно предоставляет полный доступ к всем событиям касания (например, «палец коснулся экрана»). Для WM_TOUCH вам потребуется проделать больше работы по сравнению с другими типами сообщений, поскольку вы должны написать код для всех высокоуровневых сенсорных событий и жестов, составив этот код из низкоуровневых сенсорных событий. Несмотря на необходимую дополнительную работу, сообщения WM_TOUCH стали очевидным выбором для игры Legend of Xuan Yuan. Сообщения WM_TOUCH предоставляют полный контроль над всем сенсорным управлением, включая мультисенсорный ввод.

При физическом касании экрана система отправляет игре сообщения WM_TOUCH. Одновременно игра получает сообщение о щелчке мыши. Благодаря этому приложения без полной сенсорной поддержки могут работать с сенсорным управлением. Эти два сообщения разных типов описывают одно и то же физическое событие. Это может усложнить код обработки сообщений. В игре Legend of Xuan Yuan во всех возможных случаях используются сообщения щелчка мыши, а дублирующиеся сообщения удаляются.

Ваша очередь: выбор нужного типа сенсорного сообщения для вашей игры

WM_POINTER — отличный вариант, если ваша игра предназначена только для Windows 8. Если же нужна обратная совместимость, используйте сообщения WM_GESTURE и WM_TOUCH.

При сравнении типов сообщений учитывайте устройство интерфейса вашей игры. Если в нем широко используются жесты, а вы можете без труда написать обработчики щелчков мышью для событий одиночного касания, не являющихся жестами, то, пожалуй, для вашей игры лучше подойдет WM_GESTURE. В противном случае используйте WM_TOUCH. В большинстве игр с полнофункциональным интерфейсом используются именно сообщения WM_TOUCH, особенно при наличии множества элементов управления, которых игроки касаются одновременно.

Рассматривая сенсорные сообщения, не забывайте и о системе меню. Не забудьте удалить лишние сообщения, поступающие при щелчках мыши.

Дополнительные сведения об этих трех типах сообщений см. в этой статье. Дополнительные сведения о выборе между типами сообщений WM_TOUCH и WM_GESTURE с учетом обратной совместимости см. в статье https://software.intel.com/en-us/articles/touch-samples.

Адаптация интерфейса к сенсорному управлению

Адаптация существующего интерфейса игры к сенсорному управлению может быть непростой задачей, причем не существует единственно верного подхода к решению этой задачи.

Как мы это сделали: новый сенсорный интерфейс

Интерфейс с клавиатурой и мышью общеизвестен и привычен. В нем для движения игрового персонажа используются клавиши W, A, S, D. Настраиваемые кнопки действий в нижней части экрана и клавиши с 1 по 9 соответствуют напиткам, навыкам нападения и дополнительным элементам интерфейса. Эти элементы пользовательского интерфейса включают инвентарь, дерево навыков, задачи и карту. По щелчку правой кнопкой мыши персонаж берет оружие, надевает доспехи или открывает сундук с сокровищами.

Сенсорный экран доступен постоянно, но сенсорный интерфейс по умолчанию скрыт при использовании мыши и клавиатуры. В этом режиме на экране отображается сенсорная кнопка.


Рисунок 3. При нажатии на эту сенсорную кнопку в этом режиме открывается сенсорный интерфейс.

Если игрок переводит систему в режим планшета или касается этой кнопки, на экране появляется полный сенсорный интерфейс.

Как мы это сделали: элементы сенсорного пользовательского интерфейса

В режиме планшета игрок обычно держит устройство обеими руками. В интерфейсе для управления используются большие пальцы обеих рук, чтобы свести к минимуму изменения хвата устройства. Перемещения и действия атаки сгруппированы для удобного доступа большими пальцами левой и правой рук игрока

Сначала мы придумали элемент управления в виде колеса для перемещения игрового персонажа. Кольцо накладывается на левую часть экрана. Схожий принцип используется на игровых контроллерах; привычное расположение делает управление более удобным и интуитивным. Большой палец левой руки игрока обычно постоянно касается экрана. Когда игрок перемещает палец по экрану, персонаж движется в ту сторону, куда переместился палец.

Обычная игровая панель действий находится в нижней части экрана, но ей неудобно пользоваться большими пальцами. Мы добавили группу из 4 крупных кнопок действий в правом углу, где на эти кнопки будет удобно нажимать большим пальцем правой руки. Пользователь может настроить эти кнопки для наиболее часто используемых действий, перетащив на них навыки нападения или напитки.

Перед нападением игрок должен навести прицел на противника. В традиционном интерфейсе с клавиатурой и мышью щелчок левой кнопкой используется для нацеливания на одного противника, а клавиша TAB — для перенацеливания на следующего противника, находящегося в пределах радиуса атаки. В сенсорном режиме есть большая кнопка, чтобы нацелиться на самого близкого противника. Игрок также может коснуться противника, чтобы напрямую нацелиться на него, но это используется редко, поскольку при этом нарушается хват устройства игроком.

В интерфейсе для мыши и клавиатуры щелчок правой кнопкой мыши используется, чтобы открыть сундук с сокровищами, взять оружие, надеть броню или выпить напиток. Касание с удержанием является наилучшей заменой щелчку правой кнопкой мыши в сенсорном интерфейсе.

При использовании клавиатуры и мыши на экране есть маленький значок, чтобы открыть каскадные окна. При сенсорном управлении это неудобно, поскольку значки слишком мелкие. Сенсорный интерфейс включает экранный значок для раскрытия остальных элементов интерфейса с помощью каскадного набора значков. Эти значки открывают более сложные компоненты интерфейса, такие как инвентарь, дерево навыков, задачи и пр. Также предусмотрена кнопка для переключения интерфейса между традиционным (клавиатура и мышь) и сенсорным. За счет этого игрок может без труда переключаться между двумя интерфейсами.


Рисунок 4. Полный сенсорный интерфейс с колесом перемещения, кнопками действий и цели, а также каскадными значками.

Вот полный сенсорный интерфейс с открытыми каскадными значками.

Как мы это сделали: обработка сообщений сенсорного интерфейса

Как происходит обработка сообщений? По-разному для разных компонентов интерфейса. Используются сообщения WM_TOUCH и сообщения мыши. Действия, прицеливание и каскадные кнопки интерфейса используют сообщения щелчков мыши. Колесо перемещения и основная часть игрового экрана используют сообщения WM_TOUCH.

Игровой процесс обычно включает постоянное касание колеса для перемещения, повторяющееся использование кнопок выбора противника и навыков атаки. Это означает, что необходима поддержка мультисенсорного ввода. К счастью, WM_TOUCH хорошо поддерживает мультисенсорное управление.

При появлении сообщения WM_TOUCH игра сохраняет определенный контекст. Код сравнивает это касание с другими недавними сообщениями WM_TOUCH, проверяет, насколько долго удерживалось текущее сочетание касаний и определяет расположение касания.

Если область сообщения WM_TOUCH приходилась на колесо перемещения или была рядом с ним, код проверяет место касания относительно центра колеса и относительно предыдущего касания. Если касание было рядом с предыдущим, причем текущий жест был начат на колесе, то игра передвигает персонажа в нужном направлении. При разработке потребовалась достаточно точная настройка для определения различий между длительным непрерывным касанием колеса перемещения и другими касаниями остальной части экрана.

Если сообщение WM_TOUCH находится на экране далеко от других элементов управления, то, вероятно, это часть жеста, такого как масштабирование или прокрутка, либо часть касания с удержанием. Сообщения WM_TOUCH сравниваются с прежними, чтобы определить нужное действие. Если оно достаточно близко к предыдущему и удерживается свыше 0,2 секунд, оно рассматривается как касание и удержание. В противном случае это жест, и экран настраивается соответствующим образом.

Система автоматически формирует сообщения мыши для всех сенсорных сообщений. Каждое сообщение мыши содержит дополнительную информацию с указанием источника этого сообщения. Вызов GetMessageExtralnfo определяет различие.

#define MOUSEEVENTF_FROMTOUCH 0xFF515700

if ((GetMessageExtraInfo() & MOUSEEVENTF_FROMTOUCH) == MOUSEEVENTF_FROMTOUCH) {
  // Click was generated by wisptis / Windows Touch
}else{
  // Click was generated by the mouse.
}

Рисунок 5. Проверка, поступают ли от сенсорного экрана сообщения мыши.

Если сенсорный экран создал событие мыши, а игра уже обработала физическое касание с помощью WM_TOUCH, игра удаляет сообщение мыши.

Если же сенсорное сообщение приходится на один из прочих элементов управления, то оно не обрабатывается, а вместо него используется сообщение мыши.

Когда все элементы интерфейса будут на месте, игра получит удобное сенсорное управление.

В этой статье приводится еще один пример добавления сложного сенсорного интерфейса в игре Wargame: European Escalation: https://software.intel.com/en-us/articles/wargame-european-escalation-performance-and-touch-case-study

Ваша очередь: построение вашего сенсорного пользовательского интерфейса

Перед тем, как создать сенсорный пользовательский интерфейс для вашей игры, продумайте все возможные действия игрока. Затем подумайте, как реализовать эти действия с помощью сенсорного экрана (или других датчиков, например, акселерометра). Обращайте особое внимание на различия между касаниями и щелчками, непрерывными действиями (нажатие и удержание) и жестами, такими как перетаскивание.

Решите, каким образом игрок будет выполнять все эти действия с помощью пользовательского интерфейса. Продумайте, где должны находиться видимые элементы управления. Любые экранные элементы управления и каскадные меню должны быть достаточно крупными, чтобы ими было удобно управлять кончиками пальцев. Подумайте, как игрок будет держать устройство в руках, и учитывайте это при проектировании интерфейса для удобного касания.

Теперь, когда интерфейс спланирован, используйте самые простые сообщения. Определяйте, когда касание попадает в каждый элемент управления. Спланируйте, какие типы сообщений использовать для этих элементов управления (мышь или сенсорные) и убирайте дублирующиеся сообщения.

Для сенсорных сообщений нужно сохранять контекст. Расположение, элемент управления и время будут полезны при составлении жестов из мультисенсорных сообщений. Подумайте о фрагментах игры, где требуется постоянный сенсорный контакт. Тщательно тестируйте это при разработке, чтобы убедиться в удобстве игры при использовании типовых расхождений в жестах. Проверяйте различные направления жестов, расположения касаний, близость к предыдущим касаниям и длительность касаний.

Запускайте интерфейс в режиме, который соответствует текущему состоянию устройства. Переключайте интерфейс между клавиатурой/мышью и сенсорным режимом при каждом изменении состояния устройства. Не забудьте предоставить игроку возможность переключения режима интерфейса вручную на случай, если система не предоставляет нужных уведомлений.

Дополнительные советы по проектированию сенсорных интерфейсов см. в руководстве разработчика сенсорных приложений для ультрабуков и планшетов под управлением Windows..

Датчики

Ультрабуки и трансформеры оборудованы датчиками, такими как гироскоп, акселерометр, GPS и пр. С помощью этих датчиков можно расширить игровые возможности.

Как мы это сделали: встряхните устройство

В игре Legend of Xuan Yuan используется акселерометр, чтобы определить, когда пользователь встряхивает устройство. Персонаж накапливает энергию в ходе игры, затем расходует ее в ходе супер-атаки. Игрок может встряхнуть устройство, чтобы запустить супер-атаку: при этом все ближайшие противники подвергаются атаке в течение 10—20 секун.

Мы протестировали различные виды встряхивания, чтобы измерить типовые значения акселерометра


Рисунок 6. Четыре встряхивания с отображением интенсивности и длительности в трех измерениях.

Любое ускорение свыше 1,6 по одной оси является встряхиванием. Мы также могли использовать сумму абсолютных значений ускорения по каждой оси.

Поскольку это события реального мира, такие данные будут иметь много помех и будут всякий раз различаться. Значения включают и краткое, и длительное встряхивание. В большинстве тестовых случаев на одно встряхивание приходилось одно пиковое значение, но в одном случае встряхивание включало несколько пиковых значений. В этой игре используется встряхивание в любом направлении с ускорением свыше 1,6 по любой оси. Несколько встряхиваний в течение 1,5 секунд считаются одним.

При наличии в игре такого кода любое встряхивание устройства приведет к срабатыванию действия супер-атаки.

Ваша очередь: использование датчиков устройства

Ультрабуки и трансформеры содержат различные датчики. Подумайте, как можно использовать каждый из них в игре.

Какие бы датчики вы ни использовали, откалибруйте их, чтобы узнать, как они реагируют на реальные условия. Учтите различные типовые условия, с которыми могут столкнуться игроки.

Заключение

Мы показали, как адаптировать существующую игру для обнаружения состояния трансформера (ноутбук или планшет). Также мы продемонстрировали, как интерфейс может поддерживать сенсорное управление, и как переключаться между интерфейсами в зависимости от режима работы трансформера. Вместе с акселерометром, запускающим в игре уникальное действие, пользователь получает исключительное удобство в игре.

Компания Tencent пошла на риск, реализовав поддержку сенсорного управления в первой китайской MMORPG-игре. И этот риск оправдал себя. Игра Legend of Xuan Yuan великолепно работает на ноутбуках, планшетах и трансформерах. Надеемся, что и вашу игру ждет не меньший успех!

Авторы

Мак Хан (Mack Han) — инженер по разработке клиентского игрового программного обеспечения в компании Tencent, обладающий 10-летним опытом разработки игр. Он создавал игры для консолей, ПК и мобильных телефонов. Много лет он работал в области крупных трехмерных MMORPG, его области специализации — отрисовка трехмерных изображений и оптимизация.

Кейдж Лю (Cage Lu) — инженер по разработке приложений в корпорации Intel. Он уже несколько лет работает с крупными разработчиками игр в Китае, помогая им оптимизировать производительность игровых клиентов и улучшить пользовательские интерфейсы на платформах Intel.

Пол Линдберг (Paul Lindberg) — старший инженер по программному обеспечению в отделе Developer Relations корпорации Intel. Он помогает разработчикам игр во всем мире создавать великолепные игры и другие приложения, демонстрирующие непревзойденные результаты на платформах Intel.

Справочные материалы

Определение режимов планшета и ноутбука, а также ориентации экрана на трансформерах: http://software.intel.com/en-us/articles/detecting-slateclamshell-mode-screen-orientation-in-convertible-pc

Дополнительные сведения об адаптации для трансформеров:
http://software.intel.com/en-us/articles/how-to-write-a-2 in 1aware-application

Сравнение типов сенсорных сообщений в Windows 8:
http://software.intel.com/en-us/articles/comparing-touch-coding-techniques-windows-8-desktop-touch-sample

<Пример с реализацией сенсорного управления в игре Wargame: European Escalation:
http://software.intel.com/en-us/articles/wargame-european-escalation-performance-and-touch-case-study

Руководство по сенсорному управлению для разработчиков:
http://software.intel.com/en-us/articles/ultrabook-device-and-tablet-windows-touch-developer-guide

Более сложный пример внедрения:
http://software.intel.com/en-us/articles/hot-shots-warps-conventional-touch-gaming

Лицензия

Пример исходного кода распространяется на условиях соглашения Intel Sample Source License Agreement.  Фрагменты этого документа распространяется на условиях ограниченной публичной лицензии корпорации Майкрософт.

 

Intel®Developer Zone offers tools and how-to information for cross-platform app development, platform and technology information, code samples, and peer expertise to help developers innovate and succeed.  Join our communities for the Internet of Things, Android*, Intel® RealSense™ Technology and Windows* to download tools, access dev kits, share ideas with like-minded developers, and participate in hackathons, contests, roadshows, and local events.

 

Intel, the Intel logo, and Ultrabook are trademarks of Intel Corporation in the U.S. and/or other countries.
Copyright © 2014 Intel Corporation. All rights reserved.
*Other names and brands may be claimed as the property of others.

 

Использование OpenCL™ API Debugger

$
0
0

Скачать PDF

1. Введение

OpenCL™ API Debugger — это подключаемый модуль для Microsoft Visual Studio*, позволяющий отлаживать приложения OpenCL, отслеживая среду OpenCL. API Debugger — это новый компонент пакета Compute Code Builder в составе решений Intel® Integrated Native Developer Experience (Intel® INDE) и бета-версии Intel® SDK for OpenCL™ Applications 2014.

API Debugger дает разработчикам возможность просматривать список всех вызовов API OpenCL в своих приложениях; можно просматривать параметры функций, возвращаемые значения и время выполнения. Этот подключаемый модуль также позволяет просматривать различные объекты OpenCL, существующие в памяти при выполнении приложения. Дополнительные сведения о подключаемом модуле API Debugger и его возможностях см. в Руководстве пользователя бета-версии Intel® SDK для OpenCL™ Applications 2014..

В этом руководстве мы демонстрируем одну из областей применения API Debugger: отладку и обнаружение причин сбоя приложений при наличии только исходного кода ядра и двоичного файла приложения (без исходного кода со стороны хоста). Полезнейшая особенность API Debugger — способность показывать исходный код ядра, даже если оно встроено в отлаживаемое приложение. В последнем случае можно выявить источник ошибки ядра, но исправить его в отсутствие полного исходного кода приложения нельзя.

В целях демонстрации мы внесем ошибку в ядро фильтра sobel из раздела Работа с JumpStart  и покажем, как выявить эту ошибку, не имея доступа к исходному коду хоста.

2. Включение Api Debugger в Visual Studio*

Прежде всего нужно включить API Debugger в Visual Studio. Для этого щелкните Tools: Code: Builder – Optionsв Visual Studio или нажмите сочетание клавиш Ctrl+1чтобы открыть диалоговое окно конфигурации отладчика, как показано ниже.

Убедитесь, что флажок Enable OpenCL API Debuggerустановлен, затем нажмите кнопку «ОК». После включения этого подключаемого модуля можно приступить к отладке приложения.

3. Запуск приложения

Как было сказано выше, мы внесли ошибку в приложение sobel, изменив типы аргументов ядра для ширины и высоты, как показано ниже.


kernel void
sobel(__global uchar4* src, __global uchar4* dst, ushort width, ushort height)
{

При попытке запуска приложения с этим новым ядром происходит сбой приложения в момент задания аргументов ядра. При отсутствии подробной обработки ошибок такие проблемы очень сложно обнаружить без полного исходного кода. Посмотрим, как можно выполнить отладку в этом случае.

Поскольку у нас в наличии только двоичный файл приложения и ядро OpenCL, нужно каким-то образом запустить приложение для его отладки в Visual Studio. Запустите командную строку Visual Studio из меню «Пуск» или добавьте %VSINSTALLDIR%\Common7\IDE\ (где %VSINSTALLDIR% — папка, в которую установлена среда Visual Studio) в переменную среды PATH и запустите командную строку. Запустите приложение из командной строки следующим образом:


devenv <приложение>

where <приложение>полный или относительный путь к отлаживаемому приложению, например, sobel.exe.

При этом приложение должно запуститься в Visual Studio для отладки. Убедитесь, что компонент API Debugger включен, как было показано в предыдущем разделе. Запустите отладку, нажав клавишу F5. Приложение запустится, но не сможет выполняться без выполнения ядра OpenCL.

4. Представление проблем и представление трассировки

Откройте представление проблем Tools: Code: Builder: OpenCL Debugger: Problems View. В окне Problems Viewперечисляются все предупреждения и ошибки, возникшие при запуске приложения OpenCL. Наибольший интерес представляют именно ошибки (хотя стоит обращать внимание и на предупреждения). Вы увидите две ошибки, вызванные вызовом clSetKernelArg(), как показано ниже.

Но верхнее представление дает только половину информации: мы всего лишь узнаем о сбое вызова с CL_INVALID_ARG_SIZE. Теперь щелкните правой кнопкой мыши одну из ошибок и выберите Show in Trace View.

При этом появится Trace Viewс трассировкой API OpenCL.

В окне трассировки выберите Functions with arguments names and valuesв списке API Display Mode как показано ниже.

Это подробный режим отображения, в котором вы видите дополнительные сведения об API, включая количество аргументов и их размер.

Если рассмотреть ошибочные вызовы и возвращаемое значение clSetKernelArg(), становится очевидно, что при задании аргументов ядра 2 и 3 возникает какая-то проблема. Снова рассмотрим сигнатуру ядра.


kernel void
sobel(__global uchar4* src, __global uchar4* dst, ushort width, ushort height)
{



В данных представления трассировкивидно, что код задает аргументы ядра размером 4 (поскольку arg_size = 0x4 для обоих вызовов с ошибками), тогда как ядро OpenCL принимает тип данных ushort размером 2. Теперь мы знаем, что нужно исправить либо код системы, либо ядро, чтобы типы данных совпадали.

4.1 Просмотр встроенного исходного кода ядра

Как было упомянуто выше, API Debugger может отображать исходный код ядра, даже если он встроен в двоичный файл. Как и ранее, включите API Debugger и запустите приложение в Visual Studio. После перехода в отладчик откройте представление дерева объектов (Tools→Code Builder — OpenCL Debugger→Objects Tree View) и найдите узел собранной программы (например, «Program [1] (Built)»). Затем щелкните узел программы правой кнопкой мыши и выберите «Open Source Code in a new tab» для просмотра исходного кода ядра.

Обратите внимание, что показанный выше двоичный файл был собран без отладочной информации. Можно изучить исходный код ядра даже в случае, когда двоичный файл собран в режиме выпуска.

5. Заключение

В этом руководстве показано, каким образом API Debuggerпомогает выявлять причины определенных ошибок в приложениях OpenCL, особенно при недоступности исходного кода. Разумеется, API Debugger обладает гораздо более широкой функциональностью и расширяет возможности отладки Visual Studio, предоставляя средства отладки приложений OpenCL непосредственно в этой среде разработки.

 

 

Intel and the Intel logo are trademarks of Intel Corporation in the U.S. and/or other countries.
Copyright © 2014 Intel Corporation. All rights reserved.
*Other names and brands may be claimed as the property of others.

OpenCL and the OpenCL logo are trademarks of Apple Inc and are used by permission by Khronos.

События наблюдения за производительностью внеядерных компонентов платформы Merrifield

$
0
0

Использование событий наблюдения за производительностью внеядерных компонентов платформы Merrifield

В этой статье рассматриваются события наблюдения за производительностью платформы «система на кристалле» Merrifield. Введение в наблюдение за производительностью внеядерных компонентов платформ «система на кристалле» см. в статье: Руководство по наблюдению за производительностью внеядерных компонентов платформы «система на кристалле» Silvermont

Введение в платформу Merrifield с архитектурой «система на кристалле»

Ниже на блок-схеме показано типовое устройство Merrifield. Зеленые стрелки, соединяющие каждый блок, представляют собой интерфейсы; можно отслеживать запросы в интерфейсах для вычисления пропускной способности. Серыми стрелками в южном кластере показаны интерфейсы, для которых не поддерживается отслеживание производительности внеядерных компонентов. Как видно на схеме ниже, анализ будет касаться только северного кластера.

Доступные группы

В следующей таблице описаны доступные группы для Merrifield. Имя группы означает заранее заданный набор событий, которые будут программироваться программными средствами мониторинга. В столбце событий указывается, сколько событий содержится в группе. В столбце «Тактовые импульсы» указывается, включает ли группа отсчеты источника тактовых импульсов SoC.

Таблица групп событий внеядерных компонентов платформы Merrifield

Имя группы

События

Тактовые импульсы

Описание

UNC_SOC_Memory_DDR_BW

5

Да

Подсчитывает количество запросов чтения и записи памяти в канал памяти 0 и 1. Для определения пропускной способности памяти нужно умножить количество событий на 32 байта.

UNC_SOC_DDR_Self_Refresh

5

Да

Подсчитывает количество циклов, в течение которых каналы памяти 0 и 1 находятся в состоянии самообновления.

UNC_SOC_All_Reqs

6

Да

Подсчитывает количество запросов на каждый агент памяти. С помощью счетчиков можно определять ресурсоемкие агенты или оценивать пропускную способность для агента, умножив количество запросов на 64 байта.

UNC_SOC_Module0_BW

7

Да

Подсчитывает количество событий пропускной способности для модуля Silvermont 0. Для определения пропускной способности между модулем Silvermont 0 и памятью нужно умножить количество событий на размер запроса (32 или 64 байта).

UNC_SOC_Module0_Snoops

3

Да

Подсчитывает количество запросов и ответов отслеживания для модуля Silvermont 0.

UNC_SOC_Graphics_BW

7

Да

Подсчитывает количество событий пропускной способности для графического контроллера. Для определения пропускной способности между графическим контроллером и памятью нужно умножить количество событий на размер запроса (32 или 64 байта).

UNC_SOC_Display_BW

7

Да

Подсчитывает количество событий пропускной способности для контроллера экрана. Для определения пропускной способности между контроллером экрана и памятью нужно умножить количество событий на размер запроса (32 или 64 байта).

UNC_SOC_Imaging_BW

7

Да

Подсчитывает количество событий пропускной способности для контроллера изображений. Для определения пропускной способности между контроллером изображений и памятью нужно умножить количество событий на размер запроса (32 или 64 байта).

UNC_SOC_LowSpeedPF_BW

7

Да

Подсчитывает события пропускной способности для структуры низкоскоростной периферии. Для определения совокупной пропускной способности памяти нужно умножить количество событий на размер запроса (32 или 64 байта).

 

UNC_SOC_Memory_DDR_BW

Группа UNC_VISA_Memory_DDR_BW предоставляет счетчики для вычисления пропускной способности всей памяти с точки зрения контроллера памяти «системы на кристалле». С помощью событий можно разбить на части пропускную способность каждого канала. Эти события не предоставляют информации о том, какой агент потребляет больше памяти, но это наиболее точный способ определения фактической используемой пропускной способности памяти.

TКоличество каналов памяти на платформе Merrifield зависит от ее выпуска: может быть либо один, либо два канала. Если существует только один канал, все счетчики, связанные со вторым каналом, будут выдавать нулевые значения. Дополнительные сведения об архитектуре каналов памяти: http://en.wikipedia.org/wiki/Multi-channel_memory_architecture.

На приведенном ниже рисунке показан отслеживаемый поток трафика для этой группы.

В приведенной ниже таблице перечислены события, содержащиеся в группе UNC_VISA_Memory_DDR_BW.

Имя

Счетчик

Описание

DDR_Chan0-Read32B

0

Подсчитывает количество запросов чтения, обращенных к каналу памяти 0.

DDR_Chan0-Write32B

1

Подсчитывает количество запросов записи, обращенных к каналу памяти 0.

DDR_Chan1-Read32B

2

Подсчитывает количество запросов чтения, обращенных к каналу памяти 1.

DDR_Chan1-Write32B

3

Подсчитывает количество запросов записи, обращенных к каналу памяти 1.

Clock_Counter

4

Тактовый счетчик «системы на кристалле»

Анализ результатов

Пропускную способность в МБ/с можно вычислить для всех перечисленных выше событий следующим образом:

Формула метрики событий: количество событий / длительность выборки в секунду * 32 байта / 1000000 байт = МБ/с.

События можно суммировать для получения нужных метрик, например:

  • суммарная пропускная способность памяти = сумма всех событий, МБ/с
  • суммарная пропускная способность чтения = сумма всех событий чтения, МБ/с
  • пропускная способность канала 0 = сумма событий канала 0, МБ/с

Известное поведение

  1. Если платформа Merrifield не имеет двух каналов, показатели счетчика второго канала будут нулевыми.

UNC_SOC_DDR_Self_Refresh

Группа UNC_VISA_DDR_Self_Refresh содержит счетчики для аппаратных событий самообновления памяти. Самообновление представляет собой состояние пониженного потребления электроэнергии, его можно использовать для оптимизации электропитания «системы на кристалле» и приложений.

В приведенной ниже таблице перечислены события, содержащиеся в группе UNC_VISA_DDR_Self_Refresh.

Имя

Счетчик

Описание

DDR_Chan0_Deep_Self_Refresh

0

Подсчитывает количество циклов, в течение которых канал памяти 0 находится в состоянии глубокого самообновления.

DDR_Chan0_Shallow_Self_Refresh

1

Подсчитывает количество циклов, течение которых канал памяти 0 находится в состоянии мелкого самообновления.

DDR_Chan1_Deep_Self_Refresh

2

Подсчитывает количество циклов, в течение которых канал памяти 1 находится в состоянии глубокого самообновления.

DDR_Chan1_Shallow_Self_Refresh

3

Подсчитывает количество циклов, в течение которых канал памяти 1 находится в состоянии мелкого самообновления.

Clock_Counter

4

Тактовый счетчик «системы на кристалле»

Анализ результатов

Формула метрики событий: (количество событий * 100) / (интервал времени * базовая частота DRAM) = резидентность самообновления DDR

Известное поведение

  1. Если платформа Merrifield не имеет двух каналов, показатели счетчика второго канала будут нулевыми.
  2. Счетчики 0, 1, 2, 3 могут работать с исходной тактовой частотой, отличной от частоты счетчика 4.

UNC_SOC_All_Reqs

События счетчиков запросов на каждый агент, содержащиеся в группе UNC_VISA_All_Reqs, измеряют общее количество запросов на доступные агенты «системы на кристалле» в ходе однократной единовременной выборки. Этот показатель учитывает одновременно все агенты, поэтому это важная метрика для изучения всей нестатической, то есть пиковой нагрузки. В отличие от других событий пропускной способности, учет которых происходит по одному или по два одновременно, эта метрика получает данные от всех агентов одновременно.

Пропускную способность на каждого агента можно вычислить, умножив количество запросов для каждого агента на 64 байта, а общая пропускная способность равна сумме значений пропускной способности всех агентов. Важно понимать, что конечный результат имеет приблизительный характер и основывается на предположении, что размер каждого запроса составляет 64 байта. Результат вычисления превысит факти-ческий для транзакций с 32-байтовыми и частичными запросами. Другой недостаток состоит в том, что невозможно отделить данные чтения от данных записи для каждого агента.

Для точного измерения пропускной способности с разделением по чтению и записи следует использовать метрику пропускной способности по агентам для одного или двух агентов одновременно.

Имя

Счетчик

Описание

Mod0_Reqs

0

Подсчитывает количество запросов от модуля Silvermont 0.

Disp_Reqs

1

Подсчитывает количество запросов от контроллера экрана.

GFX_Reqs

2

Подсчитывает количество запросов от графического контроллера.

Imaging_Reqs

3

Подсчитывает количество запросов от контроллера изображений.

LowSpeedPF_Reqs

4

Подсчитывает совокупное количество запросов от структуры низкоскоростной периферии.

Clock_Counter

5

Тактовый счетчик «системы на кристалле»

 

Анализ результатов

Формула метрики событий:

  • количество событий / длительность выборки в секундах * 64 байта / 1000000 байт = оцениваемая пропускная способность агента, МБ/с
  • сумма всех счетчиков событий / длительность выборки в секундах * 64 байта / 1000000 байт = оцениваемая пропускная способность памяти DDR, МБ/с

Известное поведение

  1. Важно помнить, что эти события подсчитывают транзакции с любыми размерами запросов; умножение их на 64 байта для вычисления метрики в МБ/с является огрублением, результат такого вычисления может превышать фактическую пропускную способность каналов памяти.

UNC_SOC_Module0_BW

Группа UNC_VISA_Module0_BW предоставляет счетчики для вычисления пропускной способности модуля 0 процессора с точки зрения системного агента. События можно разделить по типам запросов.

На приведенном ниже рисунке показан отслеживаемый поток трафика для этой группы.

Имя

Счетчик

Описание

Mod0_ReadPartial

0

Подсчитывает все транзакции чтения модуля 0 для запросов любого размера данных. Этот счетчик событий включает частичные, 32-байтовые и 64-байтовые транзакции.

Mod0_Read32B

1

Подсчитывает количество запросов чтения размером 32 байта от модуля 0 Silvermont.

Mod0_Read64B

2

Подсчитывает количество запросов чтения размером 64 байта от модуля 0 Silvermont.

Mod0_WritePartial

3

Подсчитывает все транзакции записи модуля 0 для запросов любого размера данных. Этот счетчик событий включает частичные, 32-байтовые и 64-байтовые транзакции.

Mod0_Write32B

4

Подсчитывает количество запросов записи размером 32 байта от модуля 0 Silvermont.

Mod0_Write64B

5

Подсчитывает количество запросов записи размером 64 байта от модуля 0 Silvermont.

Clock_Counter

6

Тактовый счетчик «системы на кристалле»

 

Анализ результатов

Пропускную способность чтения и записи можно вычислить для 32- и 64-байтовых событий, но вычисление частичных событий затруднено, поскольку у частичных запросов неизвестен объем полезной нагрузки. Также следует понимать, что частичные события для этой группы представляют собой сумму 64-байтовых, 32-байтовых и частичных запросов. Это частичное событие также можно рассматривать как сумму всех запросов чтения или записи.

Формула метрики событий:

  • частичные запросы - 32-байтовые запросы - 64-байтовые запросы = фактическое количество частичных запросов
  • (счетчик Mod0_Read32B * 32 байта / длительность выборки в секундах) + (счетчик Mod0_Read64B * 64 байта / длительность выборки в секундах) = чтение, МБ/с
  • (счетчик Mod0_Write32B * 32 байта / длительность выборки в секундах) + (счетчик Mod0_Write64B * 64 байта / длительность выборки в секундах) = запись МБ/с

Известное поведение

  1. Счетчик частичных событий модулей 0 и 1 включает 32-байтовые, 64-байтовые и частичные запросы. Его можно считать суммарным счетчиком запросов.

UNC_SOC_Module0_Snoops

UNC_VISA_Module0_Snoops подсчитывает количество запросов отслеживания и отчетов отслеживания для модуля Silvermont 0 с точки зрения системного агента. Данные этих счетчиков можно использовать для подтверждения показателей других счетчиков трафика и для выявления взаимосвязей в счетчиках событий отслеживания ядер. В отличие от событий отслеживания, связанных с ядрами, счетчики отслеживания внеядерных компонентов не могут различать показатели для ядер внутри модуля; они считают общее значение для целого модуля, а не для каждого ядра.

Имя

Событие

Описание

Mod0_Snoop_Replies

0

Подсчитывает количество ответов отслеживания, полученных от модуля 0.

Mod0_Snoop_Reqs

1

Подсчитывает количество запросов отслеживания, отправленных в модуль 0.

Clock_Counter

2

Тактовый счетчик «системы на кристалле»

Анализ результатов

Анализ результатов отслеживания зависит от модели использования.

Известное поведение

  1. Показатели счетчиков отслеживания подсчитываются для всего процессорного модуля без возможности разделения по каждому ядру.

UNC_SOC_Graphics_BW

Группа UNC_VISA_Graphics_BW предоставляет счетчики для вычисления пропускной способности графического контроллера с точки зрения системного агента. События можно разделить по типам запросов.

На приведенном ниже рисунке показан поток трафика, отслеживаемый этой группой.

Имя

Счетчик

Описание

GFX_ReadPartial

0

Подсчитывает все транзакции чтения графического контроллера с запросами с частичным размером данных.

GFX_Read32B

1

Подсчитывает количество запросов чтения памяти размером 32 байта от графического контроллера.

GFX_Read64B

2

Подсчитывает количество запросов чтения памяти размером 64 байта от графического контроллера.

GFX_WritePartial

3

Подсчитывает все транзакции записи графического контроллера с запросами с частичным размером данных.

GFX_Write32B

4

Подсчитывает количество запросов записи в память размером 32 байта от графического контроллера.

GFX_Write64B

5

Подсчитывает количество запросов записи в память размером 64 байта от графического контроллера.

Clock_Counter

6

Тактовый счетчик «системы на кристалле»

Анализ результатов

Пропускную способность чтения и записи можно вычислить для 32- и 64-байтовых событий, но вычисление частичных событий затруднено, поскольку у частичных запросов неизвестен объем полезной нагрузки.

Формула метрики событий:

  • (счетчик GFX_Read32B * 32 байта / длительность выборки в секундах) + (счетчик GFX_Read64B * 64 байта / длительность выборки в секундах) = чтение, МБ/с
  • (счетчик GFX_Write32B * 32 байта / длительность выборки в секундах) + (счетчик GFX_Write64B * 64 байта / длительность выборки в секундах) = запись МБ/с

Известное поведение

  1. Вычисление пропускной способности графики затруднено, поскольку графическая подсистема Merrifield в большинстве случаев использует частичную запись с неизвестным размером полезной нагрузки.

UNC_SOC_Display_BW

Группа UNC_VISA_Display_BW содержит события для определения пропускной способности контроллера дисплея и идентична группе UNC_VISA_Graphics_BW по конфигурации счетчиков событий и по метрике анализа. Имена событий изменены с GFX на Disp.

UNC_SOC_Imaging_BW

Группа UNC_VISA_Imaging_BW содержит события для определения пропускной способности контроллера изображений (сигнального процессора изображений) и идентична группе UNC_VISA_Graphics_BW по конфигурации счетчиков событий и по метрике анализа. Имена событий изменены с GFX на Imaging.

UNC_SOC_LowSpeedPF_BW

Группа UNC_VISA_LowSpeedPF_BW содержит события для определения пропускной способности структуры низкоскоростной периферии и представляет совокупную пропускную способность для всех подключаемых компонентов южного кластера: USB3, USB2, EMMC, сетевых адаптеров, шифрования и звукового адаптера. Эта группа идентична группе UNC_VISA_Graphics_BW по конфигурации счетчиков событий и по метрикам анализа. Имена событий изменены с GFX на LowSpeedPF.

Разработка трехмерных игр для Windows* 8 с помощью C++ и Microsoft DirectX*

$
0
0

Скачать PDF

Автор: Бруно Соннино (Bruno Sonnino)

Разработка игр — постоянно актуальная тема: всем нравится играть в игры, их охотно покупают, поэтому их выгодно продавать. Но при разработке хороших игр следует обращать немало внимания на производительность. Никому не понравится игра, «тормозящая» или работающая рывками даже на не самых мощных устройствах.

Для разработки игр можно применять различные языки и платформы, но если в игре для Windows* нужно добиться производительности, рецепт однозначен: Microsoft DirectX* и C++. Эти технологии обеспечивают доступ к оборудованию на самом низком уровне, благодаря чему можно использовать все возможности «железа» и добиться исключительной производительности.

Я решил разработать именно такую игру, хотя главным образом я занимаюсь разработкой на C#. В прошлом я довольно много работал с C++, но теперь этот язык для меня уже не столь прост. Кроме того, DirectX для меня является новинкой, поэтому эту статью можно считать точкой зрения новичка на разработку игр. Прошу опытных разработчиков простить меня за возможные ошибки.

В этой статье я покажу, как разработать игру в футбол с пробитием пенальти по воротам. Игра бьет по мячу, а пользователь управляет вратарем, который должен поймать мяч. Начинать будем не с нуля. Мы будем использовать пакет Microsoft Visual Studio* 3D Starter Kit — это естественный начальный ресурс для всех желающих разрабатывать игры для Windows 8.1.

Microsoft Visual Studio* 3D Starter Kit

После загрузки пакета Starter Kit, можно распаковать его в папку и открыть файл StarterKit.sln. В этом решении есть уже готовый проект C++ для Windows 8.1. При его запуске появится изображение, похожее на рис. 1.


Рисунок 1.Начальное состояние Microsoft Visual Studio* 3D Starter Kit.

Эта программа в составе Starter Kit демонстрирует несколько полезных элементов:

  • Анимировано пять объектов: четыре фигуры вращаются вокруг чайника, а чайник, в свою очередь, «танцует».
  • Каждый предмет сделан из отдельного материала; некоторые имеют сплошной цвет, а поверхность куба представляет собой растровый рисунок.
  • Источник света находится в верхнем левом углу сцены.
  • В правом нижнем углу экрана расположен счетчик кадровой скорости (количество кадров в секунду).
  • Сверху находится индикатор очков.
  • Если щелкнуть какой-либо предмет, он выделяется и увеличивается количество очков.
  • Если щелкнуть экран игры правой кнопкой мыши или провести по экрану от нижнего края к середине, появятся две кнопки для последовательного переключения цвета чайника.

Эти функции можно использовать для создания любых игр, но сначала рассмотрим файлы в составе пакета.

Начнем с файла App.xaml и его аналогов cpp/h. При запуске приложения App.xaml запускается DirectXPage. В DirectXPage.xaml находится элемент SwapChainPanel и панель приложения. Элемент SwapChainPanel служит поверхностью для размещения графики DirectX на странице XAML. Здесь можно добавлять объекты XAML, которые будут отображаться в сцене Microsoft Direct3D* — это удобно для добавления кнопок, подписей и других объектов XAML в игру DirectX без необходимости создания собственных элементов управления с нуля. Пакет Starter Kit также включает элемент StackPanel, который мы будем использовать для подсчета очков.

В Directxpage.xaml.cpp происходит инициализация переменных, подключение обработчиков событий для изменения размера и ориентации, обработчиков событий щелчков мышью и нажатия кнопок на панели приложения. Кроме того, в этом файле содержится и цикл отрисовки. Все объекты XAML обрабатываются как любые другие программы для Windows 8. Файл также обрабатывает событие Tapped, проверяя, приходится ли касание (или щелчок мышью) на объект. Если да, то событие увеличивает счет для этого объекта.

Необходимо сообщить программе, что SwapChainPanel должен отрисовывать содержимое DirectX. Для этого, согласно документации, нужно «вызвать экземпляр SwapChainPanel для IInspectable или IUnknown, затем вызвать Querylnterface для получения ссылки на интерфейс ISwapchainPanelNative (это собственная реализация интерфейса, дополняющая SwapChainPanel и поддерживающая обмен информацией). Затем следует вызвать ISwapchainPanelNative::SetSwapChain для этой ссылки, чтобы связать реализованную цепочку с экземпляром SwapChainPanel». Это осущест¬вляется в методе CreateWindowSizeDependentResources в файле DeviceResources.cpp.

Основной цикл игры находится в файле StarterKitMain.cpp, где отрисовывается страница и счетчик количества кадров в секунду.

Game.cpp содержит игровой цикл и проверку нажатий. В этом файле в методе Update вычисляется анимация, а в методе Render происходит отрисовка всех объектов. Счетчик кадровой скорости отрисовывается в SampleFpsTextRenderer.cpp.

Объекты игры находятся в папке Assets. Teapot.fbx — это чайник, а файл GameLevel.fbx содержит четыре фигуры, которые вращаются вокруг танцующего чайника.

Теперь, ознакомившись с образцом приложения в пакете Starter Kit, можно перейти к созданию собственной игры.

Добавление ресурсов в игру

Мы разрабатываем игру в футбол, поэтому самым первым нашим ресурсом должен быть футбольный мяч, который мы добавим в Gamelevel.fbx. Сначала нужно удалить из этого файла четыре фигуры, выделив каждую из них и нажав кнопку Delete. В обозревателе решений удалите и файл CubeUVImage.png, поскольку он нам не нужен: это текстура для куба, который мы только что удалили.

Теперь добавляем сферу в модель. Откройте инструменты (если их не видно, щелкните View > Toolbox) и дважды щелкните сферу, чтобы добавить ее в модель. Если мяч кажется слишком маленьким, можно увеличить масштаб, нажав вторую кнопку на панели инструментов в верхней части окна редактора, нажав клавишу Z на клавиатуре, с помощью мыши (нажмите и перетащите к середине экрана, чтобы увеличить изображение) или с помощью клавиш со стрелками вверх и вниз. Также для управления масштабированием можно использовать колесико мыши при нажатой клавише Ctrl. Результат должен быть примерно таким, как на рис. 2.


Рисунок 2. Редактор моделей с фигурой сферы.

Пока это просто ровная белая сфера, подсвеченная определенным образом. Ей нужна текстура футбольного мяча. Сначала я попытался использовать в качестве текстуры сетку с шестиугольными ячейками, как показано на рис. 3.


Рисунок 3.Текстура мяча в виде шестиугольной сетки: первая попытка.

Чтобы наложить текстуру на сферу, выберите ее, затем в окне свойств назначьте файл .png свойству Texture1. Идея вроде была неплохая, но результат не особенно удался, как видно на рис. 4.


Рисунок 4.Сфера с наложенной текстурой.

Шестиугольники растянуты из-за проекции точек текстуры на сферу. Нам требуется растянутая текстура, такая как на рис. 5.


Рисунок 5. Текстура футбольного мяча, приспособленная к сфере.

При наложении этой текстуры сфера уже больше похожа на футбольный мяч. Чтобы изображение было более реалистичным, нужно настроить некоторые свойства. Для этого нужно выбрать мяч и изменить эффект Phong в окне свойств. Модель освещения Phong включает направленное и рассеянное освещение и моделирует отражающие свойства объекта. Это шейдер, входящий в состав Visual Studio, его можно перетащить из набора инструментов. Для получения дополнительных сведений о шейдерах и об их создании с помощью конструктора шейдеров Visual Studio щелкните ссылку «Дополнительные сведения». Установите для свойств Red, Green, и Blue в разделе MaterialSpecular значение 0.2, а для свойства MaterialSpecularPower — значение 16. Теперь наш футбольный мяч выглядит лучше (рис. 6).


Рисунок 6. Готовый футбольный мяч.

Если вы не хотите создавать собственные модели в Visual Studio, можно найти готовые модели в Интернете. Visual Studio поддерживает любые модели в формате FBX, DAE и OBJ: достаточно добавить их в состав ресурсов решения. Например, можно использовать файл .obj, подобный показанному на рис. 7 (бесплатная модель с сайта http://www.turbosquid.com).


Рисунок 7. Трехмерная OBJ-модель мяча.

Анимация модели

Модель готова, теперь пора ее анимировать. Но сначала нужно убрать чайник, поскольку он нам не понадобится. В папке Assets удалите файл teapot.fbx. Теперь удалите его загрузку и анимацию. В файле Game.cpp загрузка моделей происходит асинхронно в CreateDeviceDependentResources:


// Load the scene objects.
auto loadMeshTask = Mesh::LoadFromFileAsync(
	m_graphics,
	L"gamelevel.cmo",
	L"",
	L"",
	m_meshModels)
	.then([this]()
{
	// Load the teapot from a separate file and add it to the vector of meshes.
	return Mesh::LoadFromFileAsync(

Нужно изменить модель и удалить продолжение задачи, чтобы загружался только мяч:


void Game::CreateDeviceDependentResources()
{
	m_graphics.Initialize(m_deviceResources->GetD3DDevice(), m_deviceResources->GetD3DDeviceContext(), m_deviceResources->GetDeviceFeatureLevel());

	// Set DirectX to not cull any triangles so the entire mesh will always be shown.
	CD3D11_RASTERIZER_DESC d3dRas(D3D11_DEFAULT);
	d3dRas.CullMode = D3D11_CULL_NONE;
	d3dRas.MultisampleEnable = true;
	d3dRas.AntialiasedLineEnable = true;

	ComPtr<ID3D11RasterizerState> p3d3RasState;
	m_deviceResources->GetD3DDevice()->CreateRasterizerState(&d3dRas, &p3d3RasState);
	m_deviceResources->GetD3DDeviceContext()->RSSetState(p3d3RasState.Get());

	// Load the scene objects.
	auto loadMeshTask = Mesh::LoadFromFileAsync(
		m_graphics,
		L"gamelevel.cmo",
		L"",
		L"",
		m_meshModels);


	(loadMeshTask).then([this]()
	{
		// Scene is ready to be rendered.
		m_loadingComplete = true;
	});
}



Методу ReleaseDeviceDependentResources нужно лишь очистить сетки:


void Game::ReleaseDeviceDependentResources()
{
	for (Mesh* m : m_meshModels)
	{
		delete m;
	}
	m_meshModels.clear();

	m_loadingComplete = false;
}



Теперь нужно изменить метод Update, чтобы вращался только мяч:


void Game::Update(DX::StepTimer const& timer)
{
	// Rotate scene.
	m_rotation = static_cast<float>(timer.GetTotalSeconds()) * 0.5f;
}

Для управления скоростью вращения используется множитель (0.5f). Чтобы мяч вращался быстрее, нужно просто увеличить этот множитель. За каждую секунду мяч будет поворачиваться на угол 0,5/(2 * Пи) радиан. Метод Render отрисовывает мяч с нужным углом вращения:


void Game::Render()
{
	// Loading is asynchronous. Only draw geometry after it's loaded.
	if (!m_loadingComplete)
	{
		return;
	}

	auto context = m_deviceResources->GetD3DDeviceContext();

	// Set render targets to the screen.
	auto rtv = m_deviceResources->GetBackBufferRenderTargetView();
	auto dsv = m_deviceResources->GetDepthStencilView();
	ID3D11RenderTargetView *const targets[1] = { rtv };
	context->OMSetRenderTargets(1, targets, dsv);

	// Draw our scene models.
	XMMATRIX rotation = XMMatrixRotationY(m_rotation);
	for (UINT i = 0; i < m_meshModels.size(); i++)
	{
		XMMATRIX modelTransform = rotation;

		String^ meshName = ref new String(m_meshModels[i]->Name());

		m_graphics.UpdateMiscConstants(m_miscConstants);

		m_meshModels[i]->Render(m_graphics, modelTransform);
	}
}

ToggleHitEffect здесь не будет работать: свечение мяча не изменится при его нажатии:


void Game::ToggleHitEffect(String^ object)
{

}



Нам не нужно, чтобы изменялась подсветка мяча, но нужно получать данные о его касании. Для этого используем измененный метод onHitobject:


String^ Game::OnHitObject(int x, int y)
{
	String^ result = nullptr;

	XMFLOAT3 point;
	XMFLOAT3 dir;
	m_graphics.GetCamera().GetWorldLine(x, y, &point, &dir);

	XMFLOAT4X4 world;
	XMMATRIX worldMat = XMMatrixRotationY(m_rotation);
	XMStoreFloat4x4(&world, worldMat);

	float closestT = FLT_MAX;
	for (Mesh* m : m_meshModels)
	{
		XMFLOAT4X4 meshTransform = world;

		auto name = ref new String(m->Name());

		float t = 0;
		bool hit = HitTestingHelpers::LineHitTest(*m, &point, &dir, &meshTransform, &t);
		if (hit && t < closestT)
		{
			result = name;
		}
	}

	return result;
}



Если сейчас запустить проект, вы увидите, что мяч вращается вокруг своей оси Y. Теперь приведем мяч в движение.

Движение мяча

Чтобы мяч двигался, нужно перемещать его, например, вверх и вниз. Сначала нужно объявить переменную для текущего положения мяча в Game.h:


class Game
{
public:
	// snip
private:
       // snip
       float m_translation;



Затем в методе Update нужно вычислить текущее положение:


void Game::Update(DX::StepTimer const& timer)
{
	// Rotate scene.
	m_rotation = static_cast<float>(timer.GetTotalSeconds()) * 0.5f;
	const float maxHeight = 7.0f;
	auto totalTime = (float) fmod(timer.GetTotalSeconds(), 2.0f);
	m_translation = totalTime > 1.0f ?
		maxHeight - (maxHeight * (totalTime - 1.0f)) : maxHeight *totalTime;
}

Теперь мяч будет подниматься и опускаться каждые 2 секунды. В течение первой секунды мяч будет подниматься, в течение следующей секунды — опускаться. Метод Render вычисляет получившуюся матрицу и отрисовывает мяч в новом положении:


void Game::Render()
{
	// snip

	// Draw our scene models.
	XMMATRIX rotation = XMMatrixRotationY(m_rotation);
	rotation *= XMMatrixTranslation(0, m_translation, 0);



Если сейчас запустить проект, вы увидите, что мяч движется вверх и вниз с постоянной скоростью. Теперь нужно придать мячу физические свойства.

Добавление физики мяча.

Чтобы придать мячу физические эффекты, нужно сымитировать воздействие на него силы, представляющей гравитацию. Если вы помните школьный курс физики, то знаете, что ускоренное движение тела описывается следующими уравнениями:

s = s0 + v0t + 1/2at2

v = v0 + at

Где s — положение тела в момент t, s0 — начальное положение, v0 — начальная скорость, a — ускорение. Для движения по вертикали a — ускорение свободного падения (-10 м/с2), а s0 = 0 (сначала мяч находится на земле, то есть на нулевой высоте). Уравнения превращаются в следующие:

s = v0t -5t2

v = v0 -10t

Мы хотим достигнуть максимальной высоты за 1 секунду. На максимальной высоте скорость равна 0. Поэтому второе уравнение позволяет найти начальную скорость:

0 = v0– 10 * 1 => v0 = 10 m/s

Это дает нам перемещение мяча:

s = 10t – 5t2

Нужно изменить метод Update, чтобы использовать это уравнение:


void Game::Update(DX::StepTimer const& timer)
{
	// Rotate scene.
	m_rotation = static_cast<float>(timer.GetTotalSeconds()) * 0.5f;
	auto totalTime = (float) fmod(timer.GetTotalSeconds(), 2.0f);
	m_translation = 10*totalTime - 5 *totalTime*totalTime;
}

Теперь, когда мяч реалистично движется вверх и вниз, пора добавить футбольное поле.

Добавление футбольного поля

Чтобы добавить футбольное поле, нужно создать новую сцену. В папке Assets щелкните правой кнопкой мыши, чтобы добавить новую трехмерную сцену, и назовите ее field.fbx. Из набора инструментов добавьте плоскость и выберите ее, измените ее размер по оси X на 107, а по оси Z на 60. Задайте для свойства этой плоскости Texture1 изображение футбольного поля. Теперь можно использовать средство масштабирования (или нажимать клавишу Z) для уменьшения изображения.

Затем нужно загрузить модель в CreateDeviceDependentResources в Game.cpp:


void Game::CreateDeviceDependentResources()
{
	// snip

	// Load the scene objects.
	auto loadMeshTask = Mesh::LoadFromFileAsync(
		m_graphics,
		L"gamelevel.cmo",
		L"",
		L"",
		m_meshModels)
		.then([this]()
	{
		return Mesh::LoadFromFileAsync(
			m_graphics,
			L"field.cmo",
			L"",
			L"",
			m_meshModels,
			false  // Do not clear the vector of meshes
			);
	});

	(loadMeshTask).then([this]()
	{
		// Scene is ready to be rendered.
		m_loadingComplete = true;
	});
}



При запуске программы вы увидите, что поле прыгает вместе с мячом. Чтобы поле перестало двигаться, нужно изменить метод Render:


// Renders one frame using the Starter Kit helpers.
void Game::Render()
{
	// snip

	for (UINT i = 0; i < m_meshModels.size(); i++)
	{
		XMMATRIX modelTransform = rotation;

		String^ meshName = ref new String(m_meshModels[i]->Name());

		m_graphics.UpdateMiscConstants(m_miscConstants);

		if (String::CompareOrdinal(meshName, L"Sphere_Node") == 0)
			m_meshModels[i]->Render(m_graphics, modelTransform);
		else
			m_meshModels[i]->Render(m_graphics, XMMatrixIdentity());
	}
}

При этом изменении преобразование применяется только к мячу. Поле отрисовывается без преобразования. Если запустить код сейчас, вы увидите, что мяч отскакивает от поля, но «проваливается» в него в нижней части. Для исправления этой ошибки нужно перенести поле на -0,5 по оси Y. Выберите поле и измените его перенос по оси Y на -0,5. Теперь при запуске приложения мяч будет отскакивать от поля, как на рис. 8.


Рисунок 8.Мяч отскакивает от поля.

Задание положения камеры и мяча

Мяч расположен в центре поля, но нам он там не нужен. В этой игре мяч должен находиться на 11-метровой отметке. Если посмотреть на редактор сцены на рис. 9, видно, что следует переместить мяч по оси X, изменив перемещение мяча в методе Render в Game.cpp:


rotation *= XMMatrixTranslation(63.0, m_translation, 0);



Мяч перемещается на 63 единицы по оси X, то есть устанавливается на 11-метровую отметку.


Figure 9.Field with Axis – X (red) and Z (blue)

После этого изменения вы перестанете видеть мяч, поскольку он вне поля зрения камеры: камера установлена в центре поля и направлена на середину. Нужно изменить положение камеры, чтобы она была направлена на линию ворот. Это нужно сделать в CreateWindowSizeDependentResources в файле Game.cpp:


m_graphics.GetCamera().SetViewport((UINT) outputSize.Width, (UINT) outputSize.Height);
m_graphics.GetCamera().SetPosition(XMFLOAT3(25.0f, 10.0f, 0.0f));
m_graphics.GetCamera().SetLookAt(XMFLOAT3(100.0f, 0.0f, 0.0f));
float aspectRatio = outputSize.Width / outputSize.Height;
float fovAngleY = 30.0f * XM_PI / 180.0f;

if (aspectRatio < 1.0f)
{
	// Portrait or snap view
	m_graphics.GetCamera().SetUpVector(XMFLOAT3(1.0f, 0.0f, 0.0f));
	fovAngleY = 120.0f * XM_PI / 180.0f;
}
else
{
	// Landscape view.
	m_graphics.GetCamera().SetUpVector(XMFLOAT3(0.0f, 1.0f, 0.0f));
}
m_graphics.GetCamera().SetProjection(fovAngleY, aspectRatio, 1.0f, 100.0f);

Теперь камера находится между отметкой середины поля и 11-метровой отметкой и направлена в сторону линии ворот. Новое представление показано на рис. 10.


Рисунок 10. Измененное положение мяча и новое положение камеры.

Теперь нужно добавить ворота.

Добавление штанги ворот

Чтобы добавить на поле ворота, понадобится новая трехмерная сцена с воротами. Можно создать собственную модель или использовать готовую. Эту модель следует добавить в папку Assets, чтобы ее можно было скомпилировать и использовать.

Эту модель нужно загрузить в методе CreateDeviceDependentResources в файле Game.cpp:


auto loadMeshTask = Mesh::LoadFromFileAsync(
	m_graphics,
	L"gamelevel.cmo",
	L"",
	L"",
	m_meshModels)
	.then([this]()
{
	return Mesh::LoadFromFileAsync(
		m_graphics,
		L"field.cmo",
		L"",
		L"",
		m_meshModels,
		false  // Do not clear the vector of meshes
		);
}).then([this]()
{
	return Mesh::LoadFromFileAsync(
		m_graphics,
		L"soccer_goal.cmo",
		L"",
		L"",
		m_meshModels,
		false  // Do not clear the vector of meshes
		);
});

После загрузки задайте положение и отрисуйте в методе Render в Game.cpp:


auto goalTransform = XMMatrixScaling(2.0f, 2.0f, 2.0f) * XMMatrixRotationY(-XM_PIDIV2)* XMMatrixTranslation(85.5f, -0.5, 0);

for (UINT i = 0; i < m_meshModels.size(); i++)
{
	XMMATRIX modelTransform = rotation;

	String^ meshName = ref new String(m_meshModels[i]->Name());

	m_graphics.UpdateMiscConstants(m_miscConstants);

	if (String::CompareOrdinal(meshName, L"Sphere_Node") == 0)
		m_meshModels[i]->Render(m_graphics, modelTransform);
	else if (String::CompareOrdinal(meshName, L"Plane_Node") == 0)
		m_meshModels[i]->Render(m_graphics, XMMatrixIdentity());
	else
		m_meshModels[i]->Render(m_graphics, goalTransform);
}

Это изменение применяет преобразование к воротам и отрисовывает их. Это преобразование является сочетанием трех преобразований: масштабированием (увеличение исходного размера в 2 раза), поворотом на 90 градусов и перемещением на 85,5 единиц по оси X и на -0,5 единиц по оси Y из-за глубины поля. После этого ворота устанавливаются лицом к полю на линии ворот, как показано на рис. 11. Обратите внимание, что важен порядок преобразований: если применить вращение после перемещения, то ворота будут отрисованы совсем в другом месте, и вы их не увидите.


Рисунок 11. Поле с установленными воротами.

Удар по мячу

Все элементы установлены на свои места, но мяч все еще подпрыгивает. Пора по нему ударить. Для этого нужно снова применить физические навыки. Удар по мячу выглядит примерно так, как показано на рис. 12.


Рисунок 12. Схема удара по мячу.

Удар по мячу осуществляется с начальной скоростью v0 под углом α (если не помните школьные уроки физики, поиграйте немного в Angry Birds, чтобы увидеть этот принцип в действии). Движение мяча можно разложить на два разных движения: по горизонтали — это движение с постоянной скоростью (исходим из того, что отсутствует сопротивление воздуха и воздействие ветра), а также вертикальное движение — такое же, как мы использовали раньше. Уравнение движения по горизонтали:

sX = s0 + v0*cos(α)*t

Уравнение движения по вертикали:

sY = s0 + v0*sin(α)*t – ½*g*t2

Таким образом, у нас два перемещения: одно по оси X, другое по оси Y. Если удар нанесен под углом 45 градусов, то cos(α) = sin(α) = sqrt(2)/2, поэтому v0*cos(α) = v0*sin(a)*t. Нужно, чтобы мяч попал в ворота, поэтому дальность удара должна превышать 86 единиц (расстояние до линии ворот равно 85,5). Нужно, чтобы полет мяча занимал 2 секунды. При подстановке этих значений в первое уравнение получим:

86 = 63 + v0 * cos(α) * 2 ≥ v0 * cos(α) = 23/2 = 11.5

Если заменить значения в уравнении, то уравнение перемещения по оси Y будет таким:

sY = 0 + 11.5 * t – 5 * t2

. . . and for the x-axis:

sX = 63 + 11.5 * t

Уравнение для оси Y дает нам время, когда мяч снова ударится о землю. Для этого нужно решить квадратное уравнение (да, я понимаю, что вы надеялись навсегда распрощаться с ними после школьного курса алгебры, но тем не менее):

(−b ± sqrt(b2− 4*a*c))/2*a ≥ (−11.5 ± sqrt(11.52– 4 * −5 * 0)/2 * −5 ≥ 0 or 23/10 ≥ 2.3s

Этими уравнениями можно заменить перемещение для мяча. Сначала в Game.h создайте переменные для сохранения перемещения по трем осям:


float m_translationX, m_translationY, m_translationZ;



Затем в методе Update в Game.cpp добавьте уравнения:


void Game::Update(DX::StepTimer const& timer)
{
	// Rotate scene.
	m_rotation = static_cast<float>(timer.GetTotalSeconds()) * 0.5f;
	auto totalTime = (float) fmod(timer.GetTotalSeconds(), 2.3f);
	m_translationX = 63.0 + 11.5 * totalTime;
	m_translationY = 11.5 * totalTime - 5 * totalTime*totalTime;
}

Метод Render использует эти новые перемещения:


rotation *= XMMatrixTranslation(m_translationX, m_translationY, 0);



Если запустить программу сейчас, вы увидите, как мяч влетает в середину ворот. Если нужно, чтобы мяч двигался в других направлениях, нужно добавить горизон-тальный угол удара. Для этого мы используем перемещение по оси Z.

На рис. 13 видно, что расстояние от 11-метровой отметки до ворот составляет 22,5 единицы, а расстояние между штангами ворот — 14 единиц. Это дает нам угол α = atan(7/22.5), то есть 17 градусов. Можно вычислить и перемещение по оси Z, но можно сделать и проще: мяч должен переместиться до линии в тот же момент, когда он достигнет штанги. Это означает, что мяч должен переместиться на 7/22,5 единицы по оси Z и на 1 единицу по оси X. Уравнение для оси Z будет таким:

sz = 11.5 * t/3.2 ≥ sz = 3.6 * t


Рисунок 13. Схема расстояния до ворот.

Это перемещение до штанги ворот. У любого перемещения с меньшей скоростью угол будет меньше. Чтобы мяч достиг ворот, скорость должна составлять от -3,6 (левая штанга) до 3,6 (правая штанга). Если учесть, что мяч должен полностью попасть в ворота, максимальное расстояние составляет 6/22,5, а скорость — от 3 до -3. Имея эти цифры, можно задать угол удара в методе Update:


void Game::Update(DX::StepTimer const& timer)
{
	// Rotate scene.
	m_rotation = static_cast<float>(timer.GetTotalSeconds()) * 0.5f;
	auto totalTime = (float) fmod(timer.GetTotalSeconds(), 2.3f);
	m_translationX = 63.0 + 11.5 * totalTime;
	m_translationY = 11.5 * totalTime - 5 * totalTime*totalTime;
	m_translationZ = 3 * totalTime;
}

Перемещение по оси Z будет использовано в методе Render:


rotation *= XMMatrixTranslation(m_translationX, m_translationY, m_translationZ);
       ….



Результат должен быть примерно таким, как на рис. 14.


Рисунок 14. Удар под углом.

Добавление вратаря

Движение мяча уже готово, ворота на месте, теперь нужно добавить вратаря, который будет ловить мяч. В роли вратаря у нас будет искаженный куб. В папке Assets добавьте новый элемент (новую трехмерную сцену) и назовите его goalkeeper.fbx.

Добавьте куб из набора инструментов и выберите его. Задайте масштаб: 0,3 по оси X, 1,9 по оси Y и 1 по оси Z. Для свойства MaterialAmbient установите значение 1 для красного цвета и значение 0 для синего и зеленого цвета, чтобы сделать объект красным. Измените значение свойства Red в разделе MaterialSpecular на 1 и значение свойства MaterialSpecularPower на 0,2.

Загрузите новый ресурс в методе CreateDeviceDependentResources:


auto loadMeshTask = Mesh::LoadFromFileAsync(
	m_graphics,
	L"gamelevel.cmo",
	L"",
	L"",
	m_meshModels)
	.then([this]()
{
	return Mesh::LoadFromFileAsync(
		m_graphics,
		L"field.cmo",
		L"",
		L"",
		m_meshModels,
		false  // Do not clear the vector of meshes
		);
}).then([this]()
{
	return Mesh::LoadFromFileAsync(
		m_graphics,
		L"soccer_goal.cmo",
		L"",
		L"",
		m_meshModels,
		false  // Do not clear the vector of meshes
		);
}).then([this]()
{
	return Mesh::LoadFromFileAsync(
		m_graphics,
		L"goalkeeper.cmo",
		L"",
		L"",
		m_meshModels,
		false  // Do not clear the vector of meshes
		);
});

Теперь нужно расположить вратаря в середине ворот и отрисовать его. Это нужно сделать в методе Render в Game.cpp:


void Game::Render()
{
	// snip

	auto goalTransform = XMMatrixScaling(2.0f, 2.0f, 2.0f) * XMMatrixRotationY(-XM_PIDIV2)* XMMatrixTranslation(85.5f, -0.5f, 0);
	auto goalkeeperTransform = XMMatrixTranslation(85.65f, 1.4f, 0);

	for (UINT i = 0; i < m_meshModels.size(); i++)
	{
		XMMATRIX modelTransform = rotation;

		String^ meshName = ref new String(m_meshModels[i]->Name());

		m_graphics.UpdateMiscConstants(m_miscConstants);

		if (String::CompareOrdinal(meshName, L"Sphere_Node") == 0)
			m_meshModels[i]->Render(m_graphics, modelTransform);
		else if (String::CompareOrdinal(meshName, L"Plane_Node") == 0)
			m_meshModels[i]->Render(m_graphics, XMMatrixIdentity());
		else if (String::CompareOrdinal(meshName, L"Cube_Node") == 0)
			m_meshModels[i]->Render(m_graphics, goalkeeperTransform);
		else
			m_meshModels[i]->Render(m_graphics, goalTransform);
	}
}

Этот код размещает вратаря в середине ворот, как показано на рис. 15 (обратите внимание, что положение камеры на снимке экрана отличается).


Рисунок 15. Вратарь в середине ворот.

Теперь нужно сделать так, чтобы вратарь мог перемещаться влево и вправо, чтобы ловить мяч. Для управления движением вратаря пользователь будет нажимать на клавиши со стрелками влево и вправо.

Движение вратаря ограничено штангами ворот, расположенными на расстоянии +7 и -7 единиц по оси Z. Ширина вратаря составляет 1 единицу в каждую сторону, поэтому он может перемещаться на 6 единиц влево или вправо.

Нажатие клавиши перехватывается на странице XAML (Directxpage.xaml) и перена-правляется в класс Game. Добавляем обработчик событий KeyDown в Directxpage.xaml:

<Page
    x:Class="StarterKit.DirectXPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:StarterKit"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d" KeyDown="OnKeyDown">

Обработчик событий в DirectXPage.xaml.cpp:


void DirectXPage::OnKeyDown(Platform::Object^ sender, Windows::UI::Xaml::Input::KeyRoutedEventArgs^ e)
{
	m_main->OnKeyDown(e->Key);
}

m_main является экземпляром класса StarterKitMain, который отрисовывает сцены игры и счетчик кадровой скорости. Нужно объявить публичный метод в StarterKitMain.h:


class StarterKitMain : public DX::IDeviceNotify
{
public:
	StarterKitMain(const std::shared_ptr<DX::DeviceResources>& deviceResources);
	~StarterKitMain();

	// Public methods passed straight to the Game renderer.
	Platform::String^ OnHitObject(int x, int y) {
            return m_sceneRenderer->OnHitObject(x, y); }
	void OnKeyDown(Windows::System::VirtualKey key) {
            m_sceneRenderer->OnKeyDown(key); }….



Этот метод перенаправляет клавишу методу OnKeyDown в классе Game. Теперь нужно объявить метод OnKeyDown в файле Game.h:


class Game
{
public:
	Game(const std::shared_ptr<DX::DeviceResources>& deviceResources);
	void CreateDeviceDependentResources();
	void CreateWindowSizeDependentResources();
	void ReleaseDeviceDependentResources();
	void Update(DX::StepTimer const& timer);
	void Render();
	void OnKeyDown(Windows::System::VirtualKey key);….



Этот метод обрабатывает нажатие клавиш и перемещает вратаря в соответствующую сторону. Перед созданием этого метода нужно объявить частное поле в файле Game.h для сохранения положения вратаря:


class Game
{
       // snip

private:
	// snip

	float m_goalkeeperPosition;



Изначально вратарь занимает положение 0. Это значение будет увеличиваться или уменьшаться при нажатии пользователем клавиши со стрелкой. Если положение больше 6 или меньше -6, положение вратаря не изменяется. Это нужно сделать в методе OnKeyDown в Game.cpp:


void Game::OnKeyDown(Windows::System::VirtualKey key)
{
	const float MaxGoalkeeperPosition = 6.0;
	const float MinGoalkeeperPosition = -6.0;
	if (key == Windows::System::VirtualKey::Right)
		m_goalkeeperPosition = m_goalkeeperPosition >= MaxGoalkeeperPosition ?
	m_goalkeeperPosition : m_goalkeeperPosition + 0.1f;
	else if (key == Windows::System::VirtualKey::Left)
		m_goalkeeperPosition = m_goalkeeperPosition <= MinGoalkeeperPosition ?
	m_goalkeeperPosition : m_goalkeeperPosition - 0.1f;
}

Новое положение вратаря используется в методе Render файла Game.cpp, где вычисляется перемещение вратаря:


auto goalkeeperTransform = XMMatrixTranslation(85.65f, 1.40f, m_goalkeeperPosition);



Применив эти изменения, можно запустить игру: вы увидите, что вратарь движется вправо или влево при нажатии соответствующих клавиш со стрелками (см. рис. 16).


Рисунок 16. Игра с вратарем в нужном положении.

До сих пор мяч двигался постоянно, но это нам не нужно. Мяч должен начинать движение непосредственно после удара и останавливаться при достижении ворот. Вратарь также не должен двигаться до удара по мячу.

Необходимо объявить частное поле m_isAnimating в файле Game.h, чтобы игра «знала», когда мяч движется:


class Game
{
public:
	// snip

private:
	// snip
	bool m_isAnimating;



Эта переменная используется в методах Update и Render в Game.cpp, поэтому мяч перемещается, только когда m_isAnimating имеет значение true:


void Game::Update(DX::StepTimer const& timer)
{
	if (m_isAnimating)
	{
		m_rotation = static_cast<float>(timer.GetTotalSeconds()) * 0.5f;
		auto totalTime = (float) fmod(timer.GetTotalSeconds(), 2.3f);
		m_translationX = 63.0f + 11.5f * totalTime;
		m_translationY = 11.5f * totalTime - 5.0f * totalTime*totalTime;
		m_translationZ = 3.0f * totalTime;
	}
}

void Game::Render()
{
	// snip

	XMMATRIX modelTransform;
	if (m_isAnimating)
	{
		modelTransform = XMMatrixRotationY(m_rotation);
		modelTransform *= XMMatrixTranslation(m_translationX,
                  m_translationY, m_translationZ);
	}
	else
		modelTransform = XMMatrixTranslation(63.0f, 0.0f, 0.0f);
       ….



Переменная modelTransform перемещается из цикла к началу. Нажатие клавиш со стрелками следует обрабатывать в методе OnKeyDown, только когда m_isAnimating имеет значение true:


void Game::OnKeyDown(Windows::System::VirtualKey key)
{
	const float MaxGoalkeeperPosition = 6.0f;

	if (m_isAnimating)
	{
		auto goalKeeperVelocity = key == Windows::System::VirtualKey::Right ?
			0.1f : -0.1f;

		m_goalkeeperPosition = fabs(m_goalkeeperPosition) >= MaxGoalkeeperPosition ?
		m_goalkeeperPosition :
							 m_goalkeeperPosition + goalKeeperVelocity;
	}
}

Теперь нужно ударить по мячу. Это происходит, когда пользователь нажимает пробел. Объявите новое частное поле m_isKick в файле Game.h:


class Game
{
public:
	// snip

private:
	// snip
	bool m_isKick;



Установите для этого поля значение true в методе OnKeyDown в Game.cpp:


void Game::OnKeyDown(Windows::System::VirtualKey key)
{
	const float MaxGoalkeeperPosition = 6.0f;

	if (m_isAnimating)
	{
		auto goalKeeperVelocity = key == Windows::System::VirtualKey::Right ?
			0.1f : -0.1f;

		m_goalkeeperPosition = fabs(m_goalkeeperPosition) >= MaxGoalkeeperPosition ?
		m_goalkeeperPosition :
							 m_goalkeeperPosition + goalKeeperVelocity;
	}
	else if (key == Windows::System::VirtualKey::Space)
		m_isKick = true;
}

Когда m_isKick имеет значение true, в методе Update запускается анимация:


void Game::Update(DX::StepTimer const& timer)
{
	if (m_isKick)
	{
		m_startTime = static_cast<float>(timer.GetTotalSeconds());
		m_isAnimating = true;
		m_isKick = false;
	}
	if (m_isAnimating)
	{
		auto totalTime = static_cast<float>(timer.GetTotalSeconds()) - m_startTime;
		m_rotation = totalTime * 0.5f;
		m_translationX = 63.0f + 11.5f * totalTime;
		m_translationY = 11.5f * totalTime - 5.0f * totalTime*totalTime;
		m_translationZ = 3.0f * totalTime;
		if (totalTime > 2.3f)
			ResetGame();
	}
}

Начальное время удара хранится в переменной m_startTime (объявленной как приватное поле в файле Game.h), которая используется для вычисления времени удара. Если оно превышает 2,3 секунды, игра сбрасывается (за это время мяч уже должен был достигнуть ворот). Метод ResetGame объявляется как частный в Game.h:


void Game::ResetGame()
{
	m_isAnimating = false;
	m_goalkeeperPosition = 0;
}



Этот метод устанавливает для m_isAnimating значение false и сбрасывает положение вратаря. Положение мяча изменять не нужно: мяч будет отрисован на 11-метровой отметке, если m_isAnimating имеет значение false. Также нужно изменить угол удара. Этот код направляет удар вблизи правой штанги:


m_translationZ = 3.0f * totalTime;



Нужно изменить этот подход, чтобы удары были случайными и пользователь не знал, куда будет направлен следующий удар. Необходимо объявить приватное поле m_ballAngle в файле Game.h и инициализировать его при ударе по мячу в методе Update:


void Game::Update(DX::StepTimer const& timer)
{
	if (m_isKick)
	{
		m_startTime = static_cast<float>(timer.GetTotalSeconds());
		m_isAnimating = true;
		m_isKick = false;
		m_ballAngle = (static_cast <float> (rand()) /
			static_cast <float> (RAND_MAX) -0.5f) * 6.0f;
	}
…



Rand()/RAND_MAX дает результат от 0 до 1. Нужно вычесть из результата 0,5, чтобы получить число от -0,5 до 0,5, а затем умножить на 6, чтобы получить итоговый угол до -3 до 3. Чтобы в каждой игре использовать разные последовательности, нужно инициализировать генератор, вызвав srand в методе CreateDeviceDependentResources:


void Game::CreateDeviceDependentResources()
{
	srand(static_cast <unsigned int> (time(0)));
…



Чтобы вызвать функцию времени, нужно включить . Чтобы использовать новый угол для мяча, нужно применить m_ballAngle в методе Update:


m_translationZ = m_ballAngle * totalTime;



Теперь почти весь код готов, но нужно понять, поймал ли вратарь мяч, или же пользователь забил гол. Это можно определить простым способом: проверить, пересекается ли прямоугольник мяча с прямоугольником вратаря в момент достижения мячом линии ворот. Разумеется, для определения забитых голов можно использовать и более сложные методики, но для нашего случая описанного способа вполне достаточно. Все вычисления осуществляются в методе Update:


void Game::Update(DX::StepTimer const& timer)
{
	if (m_isKick)
	{
		m_startTime = static_cast<float>(timer.GetTotalSeconds());
		m_isAnimating = true;
		m_isKick = false;
		m_isGoal = m_isCaught = false;
		m_ballAngle = (static_cast <float> (rand()) /
			static_cast <float> (RAND_MAX) -0.5f) * 6.0f;
	}
	if (m_isAnimating)
	{
		auto totalTime = static_cast<float>(timer.GetTotalSeconds()) - m_startTime;
		m_rotation = totalTime * 0.5f;
		if (!m_isCaught)
		{
			// ball traveling
			m_translationX = 63.0f + 11.5f * totalTime;
			m_translationY = 11.5f * totalTime - 5.0f * totalTime*totalTime;
			m_translationZ = m_ballAngle * totalTime;
		}
		else
		{
			// if ball is caught, position it in the center of the goalkeeper
			m_translationX = 83.35f;
			m_translationY = 1.8f;
			m_translationZ = m_goalkeeperPosition;
		}
		if (!m_isGoal && !m_isCaught && m_translationX >= 85.5f)
		{
			// ball passed the goal line - goal or caught
			auto ballMin = m_translationZ - 0.5f + 7.0f;
			auto ballMax = m_translationZ + 0.5f + 7.0f;
			auto goalkeeperMin = m_goalkeeperPosition - 1.0f + 7.0f;
			auto goalkeeperMax = m_goalkeeperPosition + 1.0f + 7.0f;
			m_isGoal = (goalkeeperMax < ballMin || goalkeeperMin > ballMax);
			m_isCaught = !m_isGoal;
		}

		if (totalTime > 2.3f)
			ResetGame();
	}
}

Объявляем два частных поля в файле Game.h: m_isGoal и m_IsCaught. Эти поля говорят нам о том, что произошло: пользователь забил гол или вратарь поймал мяч. Если оба поля имеют значение false, мяч еще летит. Когда мяч достигает вратаря, программа вычисляет границы мяча и вратаря и определяет, налагаются ли границы мяча на границы вратаря. Если посмотрите в код, то увидите, что я добавил 7.0 f к каждой границе. Я сделал это, поскольку границы могут быть положительными или отрицательными, а это усложнит вычисление наложения. Добавив 7.0 f, я добился того, что все значения стали положительными, чтобы упростить вычисление. Если мяч пойман, его положение устанавливается по центру вратаря. m_isGoal и m_IsCaught сбрасываются при ударе. Теперь добавим в игру счет.

Добавление счета

В игре DirectX можно отрисовывать счет с помощью Direct2D, но поскольку мы разрабатываем игру для Windows 8, то можно использовать и XAML. Можно подключать элементы XAML в игре и создавать связи между элементами XAML и игровой логикой. Это удобный способ отображения информации и взаимодействия с пользователем, поскольку не придется иметь дело с положениями элементов, отрисовщиками и циклами обновления.

В состав Starter Kit входит табло результатов XAML (то самое, которое используется для подсчета нажатий на элементы в сцене с чайником). Нужно просто изменить его, чтобы на нем отображался футбольный счет. Сначала нужно изменить DirectXPage.xaml, чтобы изменить табло счета:

<SwapChainPanel x:Name="swapChainPanel" Tapped="OnTapped"><Border VerticalAlignment="Top" HorizontalAlignment="Center" Padding="10" Background="Black"
          Opacity="0.7"><StackPanel Orientation="Horizontal"><TextBlock x:Name="ScoreUser" Text="0" Style="{StaticResource HudCounter}"/><TextBlock Text="x" Style="{StaticResource HudCounter}"/><TextBlock x:Name="ScoreMachine" Text="0" Style="{StaticResource HudCounter}"/></StackPanel></Border></SwapChainPanel>

Пока мы здесь, можно изменить нижнюю панель приложения, поскольку в этой игре она нам не понадобится. Мы удалили все счетчики нажатий, поэтому теперь нужно удалить код, ссылающийся на них в обработчике OnTapped в файле DirectXPage.xaml.cpp:


void DirectXPage::OnTapped(Object^ sender, TappedRoutedEventArgs^ e)
{

}



Также можно удалить OnPreviousColorPressed, OnNextcolorPressed и ChangeObjectColor из cpp- и h-страниц, поскольку они использовались в удаленных кнопках панели приложения.

Для обновления счета в игре должен быть какой-либо способ обмена информацией между классом Game и страницей XAML. Игровой счет обновляется в классе Game, тогда как счет отображается на странице XAML. Это можно сделать, создав событие в классе Game, но у этого подхода есть недостаток. При добавлении события в класс Game возникает ошибка компиляции: «объявление события WinRT должно быть включено в класс WinRT». Причина заключается в том, что Game не является классом WinRT(ref). Чтобы использовать класс WinRT, нужно определить это событие как публичный класс ref и запечатать его:


public ref class Game sealed



Для этого можно было бы изменить класс, но мы пойдем другим путем: создадим новый класс — в данном случае класс WinRT — и будем использовать его для обмена информацией между классом Game и страницей XAML. Создайте новый класс и назовите его ViewModel:


#pragma once
ref class ViewModel sealed
{
public:
	ViewModel();
};



В файле ViewModel.h добавляем событие и свойства, необходимые для обновления результатов:


#pragma once
namespace StarterKit
{
	ref class ViewModel sealed
	{
	private:
		int m_scoreUser;
		int m_scoreMachine;
	public:
		ViewModel();
		event Windows::Foundation::TypedEventHandler<Object^, Platform::String^>^ PropertyChanged;

		property int ScoreUser
		{
			int get()
			{
				return m_scoreUser;
			}

			void set(int value)
			{
				if (m_scoreUser != value)
				{
					m_scoreUser = value;
					PropertyChanged(this, L"ScoreUser");
				}
			}
		};

		property int ScoreMachine
		{
			int get()
			{
				return m_scoreMachine;
			}

			void set(int value)
			{
				if (m_scoreMachine != value)
				{
					m_scoreMachine = value;
					PropertyChanged(this, L"ScoreMachine");
				}
			}
		};
	};

}



Объявите частное поле типа ViewModel в Game.h (нужно включить ViewModel.h в Game.h). Также нужно объявить публичный метод получения для этого поля:


class Game
{
public:
       // snip
       StarterKit::ViewModel^ GetViewModel();
private:
	StarterKit::ViewModel^ m_viewModel;



Это поле инициализируется в конструкторе Game.cpp:


Game::Game(const std::shared_ptr<DX::DeviceResources>& deviceResources) :
m_loadingComplete(false),
m_deviceResources(deviceResources)
{
	CreateDeviceDependentResources();
	CreateWindowSizeDependentResources();
	m_viewModel = ref new ViewModel();
}

Код метода получения:


StarterKit::ViewModel^ Game::GetViewModel()
{
	return m_viewModel;
}



Когда текущий удар завершается, переменные обновляются в ResetGame в файле Game.cpp:


void Game::ResetGame()
{
	if (m_isCaught)
		m_viewModel->ScoreUser++;
	if (m_isGoal)
		m_viewModel->ScoreMachine++;
	m_isAnimating = false;
	m_goalkeeperPosition = 0;
}

При изменении одного из этих двух свойств возникает событие PropertyChanged, которое можно обработать на странице XAML. Здесь есть одна недоработка: у страницы XAML нет прямого доступа к классу Game (это не ref-класс), но вместо этого осуществляется вызов StarterKitMain. Нужно создать метод получения для ViewModel в StarterKitMain.h:


class StarterKitMain : public DX::IDeviceNotify
{
public:
	// snip
	StarterKit::ViewModel^ GetViewModel() { return m_sceneRenderer->GetViewModel(); }

При наличии этой инфраструктуры можно обрабатывать событие PropertyChanged для ViewModel в конструкторе DirectXPage.xaml.cpp:


DirectXPage::DirectXPage():
	m_windowVisible(true),
	m_hitCountCube(0),
	m_hitCountCylinder(0),
	m_hitCountCone(0),
	m_hitCountSphere(0),
	m_hitCountTeapot(0),
	m_colorIndex(0)
{
	// snip

	m_main = std::unique_ptr<StarterKitMain>(new StarterKitMain(m_deviceResources));
	m_main->GetViewModel()->PropertyChanged += ref new
           TypedEventHandler<Object^, String^>(this, &DirectXPage::OnPropertyChanged);
	m_main->StartRenderLoop();
}

Этот обработчик обновляет счет (его также нужно объявить в DirectXPage.xaml.cpp.h):


void StarterKit::DirectXPage::OnPropertyChanged(Platform::Object ^sender, Platform::String ^propertyName)
{

		if (propertyName == "ScoreUser")
		{
			auto scoreUser = m_main->GetViewModel()->ScoreUser;
			Dispatcher->RunAsync(CoreDispatcherPriority::Normal, ref new DispatchedHandler([this, scoreUser]()
			{
				ScoreUser->Text = scoreUser.ToString();
			}));
		}
		if (propertyName == "ScoreMachine")
		{
			auto scoreMachine= m_main->GetViewModel()->ScoreMachine;
			Dispatcher->RunAsync(CoreDispatcherPriority::Normal, ref new DispatchedHandler([this, scoreMachine]()
			{
				ScoreMachine->Text = scoreMachine.ToString();
			}));
		}

}



Теперь счет обновляется каждый раз, когда пользователь забивает гол и когда вратарь ловит мяч (рис. 17):


Рисунок 17. Игра с обновлением счета.

Использование сенсорного управления и датчиков в игре

Игра уже вполне работоспособна, но почему бы ее не улучшить? Новые Ultrabook™ оборудованы датчиками и сенсорными экранами, с помощью которых можно расширить возможности игры. Вместо использования клавиатуры для ударов по мячу и перемещения вратаря пользователь может ударить по мячу, коснувшись экрана, и перемещать вратаря, наклоняя экран вправо или влево.

Чтобы ударять по мячу касанием экрана, используйте событие OnTapped в DirectXPage.cpp:


void DirectXPage::OnTapped(Object^ sender, TappedRoutedEventArgs^ e)
{
	m_main->OnKeyDown(VirtualKey::Space);
}

Этот код вызывает метод OnKeyDown, передавая в качестве параметра клавишу пробела — такой же код, как если бы пользователь нажал клавишу пробела. Если хотите, можно усовершенствовать этот код: получать положение касания и запускать удар по мячу только в том случае, если касание приходится на мяч. Пусть это будет вашим заданием на дом. В качестве начальной точки Starter Kit содержит код, чтобы обнаруживать касание пользователем объекта в сцене.

Теперь нужно сделать, чтобы вратарь перемещался, когда пользователь наклоняет экран. Для того нужно использовать измеритель угла наклона, определяющий все движения экрана. Этот датчик возвращает три значения: показатели поворота вокруг поперечной оси (X), продольной оси (Y) и вертикальной оси (Z). Для этой игры нужно считывать только поворот вокруг продольной оси.

Чтобы использовать этот датчик, нужно получить его экземпляр, для чего применяется метод GetDefault. Затем нужно задать интервал считывания, как в этом коде в void Game::CreateDeviceDependentResources в файле Game.cpp:


void Game::CreateDeviceDependentResources()
{
	m_inclinometer = Windows::Devices::Sensors::Inclinometer::GetDefault();
	if (m_inclinometer != nullptr)
	{
		// Establish the report interval for all scenarios
		uint32 minReportInterval = m_inclinometer->MinimumReportInterval;
		uint32 reportInterval = minReportInterval > 16 ? minReportInterval : 16;
		m_inclinometer->ReportInterval = reportInterval;
	}
...

m_inclinometer — частное поле, объявляемое в Game.h. В методе Update изменяем положение вратаря:


void Game::Update(DX::StepTimer const& timer)
{
	// snip
		SetGoalkeeperPosition();
		if (totalTime > 2.3f)
			ResetGame();
	}
}

SetGoalkeeperPosition изменяет положение вратаря в зависимости от значения, которое выдает датчик угла наклона:


void StarterKit::Game::SetGoalkeeperPosition()
{

	if (m_isAnimating && m_inclinometer != nullptr)
	{
		Windows::Devices::Sensors::InclinometerReading^ reading =
                    m_inclinometer->GetCurrentReading();
		auto goalkeeperVelocity = reading->RollDegrees / 100.0f;
		if (goalkeeperVelocity > 0.3f)
			goalkeeperVelocity = 0.3f;
		if (goalkeeperVelocity < -0.3f)
			goalkeeperVelocity = -0.3f;
		m_goalkeeperPosition = fabs(m_goalkeeperPosition) >= 6.0f ?
                 m_goalkeeperPosition : m_goalkeeperPosition + goalkeeperVelocity;
	}
}

После этого изменения можно будет двигать вратаря, наклоняя экран. Теперь игра готова.

Оценка производительности

Игра хорошо работает в системе разработки, но нужно попробовать ее и на менее мощном устройстве. Одно дело — разрабатывать игру на полноценном настольном компьютере с современным графическим процессором и 60 кадрами в секунду. Совсем другое дело — запускать игру на устройстве с процессором Intel® Atom™ и встроенным графическим адаптером.

Ваша игра должна показать хорошую производительность на обоих устройствах. Для измерения производительности можно использовать средства, входящие в состав Visual Studio или пакета Intel® Graphics Performance Analyzers (Intel® GPA), содержащего анализаторы производительности графической подсистемы, помогающие выявлять узкие места и повышать производительность игр. В Intel GPA можно получить наглядный анализ производительности игры и добиться повышения кадровой скорости.

Заключение

Итак, дело сделано. От танцующего чайника мы пришли к игре на DirectX с сенсорным управлением и управлением с помощью клавиатуры. Языки программирования становятся все более похожими, поэтому использование C++/DX не вызвало особых затруднений у разработчика, привыкшего пользоваться C#.

Основное затруднение состоит в освоении трехмерных моделей, в их движении и расположении привычным образом. Для этого потребовалось применить знания физики, геометрии, тригонометрии и математики.

Как бы то ни было, можно заключить, что разработка игры не является непосильной задачей. При наличии терпения и нужных инструментов можно создать великолепные игры с превосходной производительностью.

Благодарности

Большое спасибо Роберто Соннино (Roberto Sonnino) за его советы и техническое рецензирование этой статьи.

Изображения

Дополнительные сведения

Об авторе

Бруно Соннино — сотрудник корпорации Майкрософт уровня Most Valuable Professional (MVP), работает в Бразилии. Он выполняет обязанности разработчика и консультанта, а также является автором пяти книг про Delphi, опубликованных на португальском языке издательством Pearson Education Brazil, и ряда статей для бразильских и американских журналов и веб-сайтов.

 

Intel®Developer Zone offers tools and how-to information for cross-platform app development, platform and technology information, code samples, and peer expertise to help developers innovate and succeed.  Join our communities for the Internet of Things, Android*, Intel® RealSense™ Technology and Windows* to download tools, access dev kits, share ideas with like-minded developers, and participate in hackathons, contests, roadshows, and local events.

 

Intel, the Intel logo, Intel Atom, and Ultrabook are trademarks of Intel Corporation in the U.S. and/or other countries.
*Other names and brands may be claimed as the property of others.
Copyright © 2014. Intel Corporation. All rights reserved.

Viewing all 357 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>