Оптимизация AR-контента для мобильных: LOD, шейдеры, тестирование

TRUETECH занимается разработкой, поддержкой и обслуживанием мобильных приложений iOS, Android, PWA. Имеем большой опыт и экспертизу для публикации мобильных приложений в популярные маркеты Google Play, App Store, Amazon, AppGallery и другие.

Разработка и поддержка любых видов мобильных приложений:

Информационные и развлекательные мобильные приложения
Новостные приложения, игры, справочники, онлайн-каталоги, погодные, фитнес и здоровье, туристические, образовательные, социальные сети и мессенджеры, квиз, блоги и подкасты, форумы, агрегаторы
Мобильные приложения электронной коммерции
Интернет-магазины, B2B-приложения, маркетплейсы, онлайн-обменники, кэшбэк-сервисы, биржи, дропшиппинг-платформы, программы лояльности, доставка еды и товаров, платежные системы
Мобильные приложения для управления бизнес-процессами
CRM-системы, ERP-системы, управление проектами, инструменты для команды продаж, учет финансов, управление производством, логистика и доставка, управление персоналом, системы мониторинга данных
Мобильные приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, платформы предоставления электронных услуг, платформы кешбека, видеохостинги, тематические порталы, платформы онлайн-бронирования и записи, платформы онлайн-торговли

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

Услуги, которые мы предлагаем
Показано 1 из 1Все 1734 услуг
Оптимизация AR-контента для мобильных: LOD, шейдеры, тестирование
Средний
~3-5 дней
Часто задаваемые вопросы

Наши компетенции:

Этапы разработки

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

  • image_mobile-applications_feedme_467_0.webp
    Разработка мобильного приложения для компании FEEDME
    860
  • image_mobile-applications_xoomer_471_0.webp
    Разработка мобильного приложения для компании XOOMER
    747
  • image_mobile-applications_rhl_428_0.webp
    Разработка мобильного приложения для компании RHL
    1163
  • image_mobile-applications_zippy_411_0.webp
    Разработка мобильного приложения для компании ZIPPY
    1036
  • image_mobile-applications_affhome_429_0.webp
    Разработка мобильного приложения для компании Affhome
    970
  • image_mobile-applications_flavors_409_0.webp
    Разработка мобильного приложения для компании FLAVORS
    564

AR-приложение работает на A17 Pro без нареканий, а на Snapdragon 720G греется и падает до 20 FPS уже через две минуты. Это не баг устройства — это следствие того, что ARKit и ARCore имеют разные возможности, а 3D-контент, созданный без ограничений целевого железа, никогда не будет работать одинаково на всём парке устройств. Мы ежедневно сталкиваемся с такими задачами и разработали подход, который гарантирует стабильную работу AR даже на устройствах с ограниченными ресурсами. Более 7 лет опыта в AR-разработке и 30+ успешных проектов позволяют сократить расходы на доработки до 40% за счёт правильной классификации устройств на старте.

Как мы определяем целевые устройства?

Производительность AR напрямую зависит от класса устройства. Мы делим все смартфоны на три tier и для каждого подбираем оптимальные настройки.

Tier Примеры устройств Полигональный бюджет Текстуры Постэффекты
High iPhone 15 Pro, Pixel 8 до 10 000 1024×1024 Тени, bloom, рефлексии
Mid iPhone 12, Galaxy A54 до 5 000 512×512 Fake shadow, cubemap
Low iPhone SE 3, Redmi Note 11 до 2 000 256×256 Отключены

На iOS tier определяется через MTLCreateSystemDefaultDevice().supportsFamily(_:), на Android — через Build.VERSION.SDK_INT, ActivityManager.getMemoryClass() и Session.isDepthModeSupported(). Такой подход гарантирует, что каждый пользователь получит приемлемый опыт без перегрева и лагов. Device Tiers сокращает время тестирования в 3 раза по сравнению с ручной настройкой под каждое устройство.

Как проходит оптимизация AR-контента под ключ?

  1. Аудит текущих 3D-моделей и шейдеров: проверка полигонального бюджета, формата текстур, использования PBR.
  2. Настройка LOD и адаптивных текстур: автоматическое переключение на упрощённые модели при удалении от камеры, оптимизация текстур под ASTC.
  3. Реализация Device Tiers с динамическим переключением: кодовая логика для определения класса устройства и загрузки соответствующего контента.
  4. Тестирование на 5+ реальных устройствах разного класса: проверка FPS, теплового троттлинга, стабильности сессии.
  5. Документация и обучение команды: описание архитектуры, инструкции по добавлению новых моделей.
  6. Поддержка после внедрения: помощь в интеграции и доработки под новые устройства.

Ограничения платформ: ARCore vs ARKit

ARCore минимально требует OpenGL ES 3.0 или Vulkan. Depth API (получение карты глубины с сенсора) доступен только на устройствах из списка Wikipedia: ARCore — это примерно 30% активных Android-устройств. Instant Placement, Scene Semantics — ещё меньший процент.

ARKit на iOS однороднее, но и здесь есть деление: LiDAR доступен с iPhone 12 Pro. Scene Reconstruction (меш реального мира) требует LiDAR. Без него — только плоскостная детекция.

Ошибка, которую делают часто: приложение разрабатывается на Pro-устройстве с LiDAR, а потом выясняется, что 70% целевой аудитории — пользователи без LiDAR, и весь опыт нужно переделывать. Мы изначально ориентируемся на минимальные требования заказчика и тестируем на устройствах, которые реально используют ваши клиенты.

Оптимизация 3D-моделей для AR

Полигональный бюджет для AR на мобильных — жёстче, чем для игр, потому что AR-рендеринг добавляется поверх camera feed, который сам требует GPU-ресурсов.

Практические ориентиры:

  • Объект на переднем плане, детальный: до 10 000 полигонов
  • Объект среднего плана, вспомогательный: 1 000–3 000
  • Мелкие декоративные элементы: 100–500

LOD (Level of Detail) в AR — обязателен. SceneKit и RealityKit на iOS поддерживают LOD через LODComponent. В Unity с AR Foundation — стандартный LOD Group. Переключение на упрощённую модель при удалении объекта от камеры снижает нагрузку без видимых потерь. Сравнение: динамический LOD уменьшает нагрузку на GPU в 2–3 раза по сравнению с единым высокополигональным ассетом.

Текстуры: ASTC для iOS и Android. Для AR-объектов нормальных размеров 512×512 достаточно — пользователь смотрит на реальный мир, детали текстуры не так заметны. 2048×2048 для AR-объекта размером с чашку — избыточно.

Адаптивное качество по возможностям устройства

Стратегия Device Tiers: определяем класс устройства при запуске и настраиваем качество контента.

// iOS: определяем tier по GPU family
let device = MTLCreateSystemDefaultDevice()
if device?.supportsFamily(.apple7) == true {
    // A15+: максимальное качество, LiDAR-функции
    loadHighQualityAssets()
} else if device?.supportsFamily(.apple6) == true {
    // A14: среднее качество
    loadMediumQualityAssets()
} else {
    // A12-A13: базовое, без тяжёлых эффектов
    loadBaseQualityAssets()
}

На Android: Build.VERSION.SDK_INT + ActivityManager.getMemoryClass() + проверка поддержки ARCore Depth API через Session.isDepthModeSupported().

Шейдеры и постэффекты

Кастомные PBR-шейдеры в AR тяжелее стандартных, потому что AR-объекты должны визуально вписываться в сцену: ambient occlusion, тени на реальные поверхности, рефлексии от окружения.

На low-end устройствах отключаем:

  • Real-time тени (заменяем на fake shadow — спрайт под объектом)
  • Bloom и другие постэффекты
  • Environment reflections (заменяем на статичный cubemap)

В RealityKit эти параметры управляются через RenderOptions и Environment. В Unity AR Foundation — через Universal Render Pipeline с адаптивными Renderer Features.

Как оптимизировать шейдеры для разных устройств?

Шейдеры — основной потребитель GPU. Используйте Shader Graph с вариантными ветками под разные GPU family. Например, для low-end отключайте маты, сложные нормальные карты и прозрачность.

Почему важно тестировать на mid-range?

Mid-range устройства составляют >50% рынка. Тестирование только на флагмане даёт ложное чувство стабильности. На mid-range проверяем тепловой троттлинг: 5 минут активного AR → измеряем FPS через fps метрику или CADisplayLink callback. Если через 3 минуты FPS снижается — перегрев, нужно снижать качество динамически при ProcessInfo.thermalState >= .serious.

Минимальный набор для тестирования:

  • Флагман текущего года (iPhone 15, Pixel 8)
  • Mid-range 2–3 летней давности (iPhone 12, Samsung Galaxy A54)
  • Low-end без LiDAR/Depth API (iPhone SE 3rd gen, Xiaomi Redmi Note 11)

Свяжитесь с нами для аудита вашего AR-проекта — мы предложим оптимальный план работ и сроки. Закажите аудит AR-проекта уже сегодня. Получите консультацию: опишите задачи, и мы оценим объём оптимизации уже в первый день.

Оптимизация мобильных приложений: cold start, память, батарея, FPS, профилирование

Приложение с временем холодного старта 4+ секунды теряет пользователей ещё до первого экрана. Android Vitals в Google Play Console прямо влияют на ранжирование в поиске: приложения с плохими метриками получают меньший organic reach. Apple аналогично мониторит crash rate и время запуска через MetricKit. Оптимизация — это не «сделать быстрее», а понять где именно теряется время и что с этим делать.

Cold Start: где убивается время до первого кадра

Cold start — запуск приложения, когда процесс не существует в памяти. На Android это время от нажатия на иконку до Activity.onWindowFocusChanged(hasFocus = true). На iOS — от tap до viewDidAppear первого экрана.

Android: main thread перегружен при инициализации

Application.onCreate() — главный враг быстрого старта на Android. Разработчики инициализируют здесь всё подряд: Firebase, Analytics, базу данных, HTTP-клиент, DI-контейнер. Каждый SDK добавляет 20–200 мс на main thread.

Инструмент для диагностики: Android Studio Profiler → App Startup. Показывает граф инициализации с временем каждого компонента. Альтернатива — Tracing.beginSection("MyInitTag") в коде + systrace.

Решение: App Startup Library (Jetpack) с явным графом зависимостей инициализаторов. Компоненты, нужные только в конкретных сценариях, инициализируются лениво — by lazy {} или initializer с флагом lazyInit. Firebase Analytics, например, не нужен до первого пользовательского действия — его инициализацию можно отложить.

ContentProvider-ы, автоматически добавляемые SDK через AndroidManifest merge, тоже запускаются при старте. tools:node="remove" в манифесте позволяет отключить конкретный провайдер и инициализировать SDK вручную в нужный момент.

Ещё одна грабля: Room.databaseBuilder().build() на main thread. Это синхронная операция создания/открытия файла БД — на медленных устройствах занимает 50–300 мс. Переносим в coroutine с Dispatchers.IO, в ViewModel через viewModelScope.launch.

iOS: Dyld linking и +load

На iOS cold start делится на pre-main (до вызова main()) и post-main. Pre-main — время загрузки dylib, rebase/binding, Objective-C runtime initialization и выполнения +load методов.

Xcode Instruments → App Launch template показывает время pre-main и post-main раздельно. DYLD_PRINT_STATISTICS=1 в схеме запуска выводит детальное время загрузки в консоль.

Что убивает pre-main:

  • Много динамических библиотек (каждая dylib — накладные расходы на линковку). CocoaPods добавляет отдельную dylib на каждый pod. Решение: Swift Package Manager со статической линковкой (type: .static) или use_frameworks! :linkage => :static в CocoaPods.
  • +load методы в Objective-C — выполняются синхронно при загрузке класса, до main(). Сторонние SDK могут злоупотреблять этим. +initialize — ленивый аналог, вызывается при первом обращении к классу.

Post-main — application(_:didFinishLaunchingWithOptions:). Та же история что на Android: синхронная инициализация всего. lazy var для сервисов, которые не нужны немедленно. SwiftUI @StateObject инициализирует объект только когда View появляется — это уже встроенная ленивость.

Целевые метрики (App Store рекомендации): cold start < 400 мс для простых приложений, < 2 секунды для сложных. Warm start (процесс в памяти, но Activity/Scene пересоздаётся) — < 1 секунда.

Память: утечки, OOM, excessive pressure

Утечка памяти в iOS — retention cycle: объект A держит ссылку на B, B держит на A, ни один не освобождается. Классика: Timer с self в замыкании без [weak self]. Timer удерживает замыкание, замыкание удерживает self (ViewController), ViewController не освобождается при закрытии. Instruments → Leaks или Memory Graph Debugger в Xcode — находит живые объекты, которых не должно быть.

На Android garbage collector управляет памятью, но утечки всё равно случаются. Activity или Fragment, удерживаемые через статическую ссылку, singleton, или Handler/Runnable после onDestroy — классика. LeakCanary — обязательный инструмент в debug-сборке. Добавляется одной зависимостью debugImplementation "com.squareup.leakcanary:leakcanary-android" и автоматически детектирует утечки с полным стектрейсом.

OutOfMemoryError чаще всего происходит из-за загрузки изображений. Bitmap в памяти занимает ширина × высота × 4 байта. Изображение 4000×3000 px — 48 МБ в памяти, независимо от размера файла на диске. Glide / Coil правильно обрабатывают это: загружают с даунсемплингом под размер View, кешируют в LRU-кеш. Загружать в ImageView без Glide/Coil через BitmapFactory.decodeFile — путь к OOM на устройствах с 2 ГБ RAM.

На Flutter Dart VM имеет свой GC, но нативные ресурсы (изображения, текстуры) не управляются Dart GC. Image.network кеширует изображения в памяти без автоматического освобождения при выходе из дерева виджетов — при длинных списках с картинками используем cached_network_image с правильным memCacheWidth/memCacheHeight.

FPS и UI Performance

60 FPS — 16.67 мс на кадр. 120 FPS (ProMotion) — 8.33 мс. Всё что занимает больше на main thread — джанк.

Типичные причины просадок FPS:

На iOS: синхронная декодировка изображений в cellForRowAt. Когда ячейка таблицы появляется, UIImage(contentsOfFile:) декодирует JPEG/PNG на main thread — видно как заторможенный скролл на длинных списках. Решение: UIImage.preparingForDisplay() (iOS 15+) или ImageIO с kCGImageSourceCreateThumbnailWithTransform в background queue, результат через DispatchQueue.main.async.

На Android: RecyclerView.Adapter.onBindViewHolder с синхронными операциями. Базы данных, файловая система, синхронные сетевые запросы на main thread — StrictMode.ThreadPolicy с detectAll().penaltyLog() в debug-сборке покажет все нарушения.

На Flutter: build() метод вызывается часто, он должен быть дешёвым. setState() на верхнем виджете пересобирует всё дерево. const конструкторы, RepaintBoundary, разбиение на мелкие виджеты с локальным стейтом — основные инструменты. Flutter DevTools → Performance показывает janky frames (красные) с причинами.

Профилирование Compose: Recomposition Highlighter и трассировка через Trace.beginSection в @Composable. remember для дорогих вычислений, derivedStateOf для computed values, LazyColumn вместо Column + forEach для длинных списков.

Батарея: Wake locks, WorkManager, сетевые запросы

Приложение в топе по расходу батареи — пользователь видит это в настройках и удаляет. Android Battery Historian (из ADB bug report) показывает детальный timeline: wake locks, wakeups, network activity, sensor usage.

Основные потребители энергии:

  • Постоянный GPS (разбираем в maps-geo)
  • Polling сети каждые N секунд вместо push
  • Holding wake lock дольше необходимого
  • Excessive AlarmManager wakeups

WorkManager с Constraints — правильный способ планировать фоновые задачи: setRequiredNetworkType, setRequiresBatteryNotLow, setRequiresCharging. ОС батчирует задачи и выполняет в удобное время.

На iOS BGTaskScheduler с BGProcessingTaskRequest (для тяжёлых задач при зарядке) и BGAppRefreshTaskRequest (для лёгких обновлений) — система решает когда выполнять, разработчик только регистрирует и реализует логику.

Батчинг сетевых запросов: вместо 10 отдельных запросов в течение минуты — один батч запрос. Меньше радио-активностей (LTE radio потребляет много при инициализации соединения), меньше wakeups.

Инструменты профилирования

Платформа Инструмент Что показывает
iOS Xcode Instruments (Time Profiler) CPU, call stack, горячие методы
iOS Allocations Живые объекты, пики памяти
iOS Leaks Retention cycles
iOS MetricKit Производственные метрики (crash rate, hang rate, launch time)
Android Android Profiler CPU, Memory, Network, Energy
Android Systrace / Perfetto System-level трейсы
Android LeakCanary Утечки памяти
Android Battery Historian Энергопотребление
Flutter Flutter DevTools Recomposition, frame rendering, memory
Flutter Dart Observatory Dart VM profiling

MetricKit на iOS — особенно ценен: реальные данные с устройств пользователей, а не симулятора. MXMetricManager получает агрегированные метрики раз в сутки: MXAppLaunchMetric, MXHangDiagnostic, MXCPUExceptionDiagnostic. Диагностики по hang и CPU-exceptions содержат стектрейс с реального устройства — золото для диагностики production-проблем.

Процесс оптимизации

Начинаем с измерения, не с предположений. Инструменты выше дают цифры: конкретное время cold start, конкретный объём памяти, конкретные кадры с просадкой. Потом — приоритизация по impact: что больше всего влияет на пользовательский опыт именно в этом приложении.

Аудит производительности существующего приложения: 3–5 рабочих дней. Реализация оптимизаций — от недели до двух месяцев в зависимости от запущенности проблем и архитектуры кода.