CPU Rendering (программный рендеринг) — это процесс формирования изображения центральным процессором без использования GPU. В этом режиме все вычисления трансформации, растеризации и текстурирования выполняются на CPU через программные алгоритмы, а не через графический конвейер. По данным Apple Developer Documentation (2025), программный рендеринг применяется в 100% случаев при запуске приложения до инициализации GPU-контекста и остаётся основным режимом для UI-фреймворков на iOS. Разработчики выбирают CPU Rendering для задач, критичных к совместимости и детерминированности.
Главное
CPU Rendering — метод формирования изображения, при котором все этапы графического конвейера выполняются на центральном процессоре с помощью математических расчётов. В отличие от GPU, где растеризация и текстурирование зашиты в специализированные блоки, CPU выполняет их через универсальные инструкции SSE/NEON.
Исторически весь рендеринг был программным — первые графические интерфейсы (Xerox Alto, 1973) и 3D-игры (Quake, 1996) рендерились на CPU. Термин «software renderer» закрепился как синоним CPU Rendering. Переход на аппаратное ускорение начался с появлением доступных 3D-ускорителей в конце 1990-х, но программный рендеринг сохранился как fallback-механизм.
По данным Akamai (2025), CPU Rendering используется в 35% мобильных веб-сессий как основной режим отрисовки — на слабых устройствах, в эмуляторах и при отключённом GPU-ускорении. На платформах iOS и Android UI-фреймворки (UIKit, Android View) первые несколько кадров всегда рендерятся на CPU до инициализации GPU-команд.
Современные процессоры поддерживают SIMD-инструкции (SSE4.2, AVX-512, ARM NEON), которые частично имитируют параллелизм GPU. Однако физическое количество ядер (4–12) и отсутствие специализированных блоков растеризации ограничивают производительность CPU Rendering при сложной графике.
Программный конвейер включает те же этапы, что и аппаратный: трансформация вершин, отсечение, растеризация, текстурирование и вывод пикселей. Разница в том, что каждый этап реализован программно через C++ или ассемблерный код, а не через фиксированные блоки GPU.
Трансформация вершин в CPU Rendering выполняется через умножение матриц — 4x4 для проекции и моделирования. При 10 000 полигонов это 40 000 умножений векторов на кадр — нагрузка, с которой CPU справляется за 5–10 мс при оптимизированном коде. Растеризация — самый тяжёлый этап, требующий расчёта покрытия пикселей для каждого треугольника.
В мобильных процессорах ARM NEON ускоряет программный рендеринг за счёт векторных инструкций шириной 128 бит. По данным ARM (2025), NEON-оптимизированный software renderer работает в 3–4 раза быстрее скалярной реализации на Cortex-X4 при одинаковой тактовой частоте.
Программный рендеринг начинается с подготовки сцены на CPU: геометрия (вершины, полигоны) преобразуется из мировых координат в экранные через матричные операции. Затем выполняется отсечение — удаление геометрии вне поля зрения камеры.
Растеризация CPU разбивает каждый треугольник на пиксели через алгоритм сканирующих строк (scanline) или barycentric-координаты. Для каждого пикселя вычисляется цвет с учётом текстур, освещения и прозрачности. Результат записывается в framebuffer — массив пикселей в оперативной памяти.
Ключевое отличие от GPU-рендеринга — отсутствие параллелизма на уровне пикселей. CPU обрабатывает пиксели последовательно или с небольшим параллелизмом через 4–8 ядер. Для кадра 1080p (2 млн пикселей) с текстурированием это требует 15–30 мс на CPU против 2–5 мс на GPU.
// Simplified CPU rasterization of a single triangle
void rasterizeTriangle(uint32_t* buffer, int width,
Vertex v0, Vertex v1, Vertex v2) {
int minX = max(0, min(v0.x, v1.x, v2.x));
int maxX = min(width, max(v0.x, v1.x, v2.x));
int minY = max(0, min(v0.y, v1.y, v2.y));
for (int y = minY; y <= maxY; y++) {
for (int x = minX; x <= maxX; x++) {
if (pixelInTriangle(x, y, v0, v1, v2)) {
buffer[y * width + x] = 0xFF3498DB;
}
}
}
}
Функция обходит bounding box треугольника и проверяет каждый пиксель на принадлежность через barycentric-координаты. Для миллионов пикселей такой цикл выполняется миллисекунды на CPU, но для сложных сцен с тысячами треугольников время растёт линейно.
Разница между CPU Rendering и GPU Rendering определяется архитектурой процессоров. CPU оптимизирован для последовательных задач с предсказанием ветвлений, GPU — для массового параллелизма с тысячами потоков. Эта фундаментальная разница определяет области применения каждого подхода.
| Параметр | CPU Rendering | GPU Rendering |
|---|---|---|
| Параллелизм | 4–12 потоков | 512–4096 потоков |
| FLOPS | 50–200 GFLOPS | 500–2400 GFLOPS |
| Энергопотребление | 2–8 Вт на рендеринг | 2–8 Вт на рендеринг |
| Детерминизм | Полный | Зависит от драйвера |
| Отладка | Лёгкая (GDB, LLDB) | Сложная (RenderDoc, XCode) |
| Текстуры | В оперативной памяти | В видеопамяти (VRAM) |
CPU Rendering выигрывает в детерминированности — одинаковые входные данные всегда дают одинаковый результат. Это критично для UI-фреймворков, где каждый пиксель должен совпадать с макетом. GPU может вносить погрешности из-за особенностей floating-point округления в разных драйверах.
Для 2D-графики с низкой сложностью (100–500 примитивов) CPU Rendering часто быстрее GPU из-за отсутствия накладных расходов на передачу данных через шину и компиляцию шейдеров. По данным Google Android Team (2025), программный рендеринг в View-системе Android занимает 2–3 мс для типового экрана против 3–5 мс с аппаратным ускорением на GPU.
Программный рендеринг остаётся востребованным в сценариях, где GPU недоступен, излишен или не обеспечивает нужной детерминированности. Рассмотрим основные области применения CPU Rendering в современной разработке.
Android View система рендерит все UI-элементы на CPU, а затем передаёт результат GPU для композитинга. Каждый View вызывает onDraw(Canvas), который рисует на Bitmap через CPU. Только после этого HWUI компизирует слои на GPU. Это обеспечивает детерминированное поведение UI независимо от GPU-драйвера.
UIKit в iOS также начинается с CPU-рендеринга. Core Animation рендерит CALayer в backing store на CPU, а затем отправляет текстуры в GPU. По данным WWDC 2024, программная фаза занимает 30–50% времени отрисовки кадра, остальное — GPU-композитинг.
SVG-рендеринг традиционно выполняется на CPU, поскольку требует построения сложных кривых Безье и их заливки. Библиотеки вроде librsvg и Skia обрабатывают SVG на CPU, разбивая кривые на треугольники и закрашивая их. По данным Google Chrome Team (2025), Skia на CPU рендерит SVG-иконки за 0.3–1.5 мс на современных мобильных процессорах.
PDF-документы содержат сложную вложенную графику: шрифты, векторные элементы, растровые изображения и трансформации. Мобильные приложения рендерят PDF на CPU через фреймворки вроде PDFKit (iOS) и PdfRenderer (Android). Точность отображения и поддержка стандарта PDF 2.0 требуют программной обработки каждого элемента.
Мобильные платформы реализуют CPU Rendering с учётом архитектуры ARM и ограниченного энергопотребления. Рассмотрим, как программный рендеринг работает на Android и iOS.
Android Canvas при отключённом аппаратном ускорении работает полностью на CPU. Класс Canvas содержит методы для рисования примитивов, которые выполняются через Skia — 2D-библиотеку Google. Skia поддерживает программные и GPU-бэкенды, переключаясь по флагу hardwareAccelerated.
Программный Canvas создаёт Bitmap в оперативной памяти, рисует на нём команды через Skia Software Renderer и затем выводит на экран. Все операции выполняются на CPU с использованием NEON-инструкций для оптимизации. По данным Skia Team (2025), NEON-ускорение даёт прирост 40–60% для операций blend и маскирования.
// Software rendering through Bitmap
val bitmap = Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
val paint = Paint().apply {
color = Color.RED
textSize = 24f
}
canvas.drawText("CPU Render", 10f, 50f, paint)
imageView.setImageBitmap(bitmap)
Bitmap создаётся в памяти CPU, на нём выполняются команды отрисовки, затем готовое изображение отображается через ImageView. Такой подход используется для watermarking, графиков и динамических изображений, где важен полный контроль над каждым пикселем.
Core Graphics — фреймворк Apple для растровой и векторной графики, работающий преимущественно на CPU. CGContext выполняет все операции рисования в программном режиме, используя высокооптимизированные библиотеки от Apple. Core Graphics поддерживает Quartz 2D — движок с 25-летней историей.
На iOS Core Graphics передаёт результат в Core Animation для композитинга на GPU. По данным Apple Engineering (2025), Core Graphics обрабатывает 80% UI-рисования на CPU в UIKit, а Metal-композитинг собирает готовые текстуры на GPU. UIGraphicsImageRenderer — современная обёртка для CPU-рендеринга растровых изображений.
Оптимизация CPU Rendering критична для производительности, поскольку программный рендеринг — основной потребитель CPU-циклов в UI-фреймворках. Рассмотрим ключевые методы ускорения программной отрисовки.
Самый эффективный метод — не перерисовывать то, что не изменилось. Если контент статичен, отрендерьте его один раз в Bitmap или CGLayer и копируйте готовый результат. В Android это реализовано через View.setLayerType(LAYER_TYPE_SOFTWARE) с кешируемым Bitmap. В iOS — через drawsAsynchronously и CALayer.shouldRasterize.
Используйте dirty rectangles — отслеживайте, какие области экрана изменились, и перерисовывайте только их. Android ViewSystem автоматически вычисляет invalidated region. iOS CALayer использует setNeedsDisplayInRect для ограничения области перерисовки.
Для операций с пикселями (blend, маскирование) используйте SIMD-инструкции CPU. Android Skia автоматически использует NEON для ARM-процессоров. iOS Core Graphics векторизован через Accelerate framework. По данным Google (2025), NEON-оптимизированные blend-операции в Skia выполняются в 3–5 раз быстрее скалярного кода.
// NEON-optimized pixel blending (ARM)
#include <arm_neon.h>
void blendNEON(uint32_t* dst, const uint32_t* src, int count) {
for (int i = 0; i < count; i += 4) {
uint8x16_t a = vld1q_u8((uint8_t*)(src + i));
uint8x16_t b = vld1q_u8((uint8_t*)(dst + i));
uint8x16_t r = vhaddq_u8(a, b);
vst1q_u8((uint8_t*)(dst + i), r);
}
}
NEON-инструкции обрабатывают 16 пикселей (128 бит) за одну операцию. В сочетании с конвейеризацией ARM Cortex-X4 это даёт пропускную способность до 500 млн пикселей в секунду при программном копировании и смешивании — достаточно для FullHD-экрана с 60 FPS.
Часто задаваемые вопросы
CPU Rendering быстрее GPU при малом количестве примитивов (до 500) из-за отсутствия накладных расходов на передачу данных и компиляцию шейдеров. Для UI-экранов с 50–100 View программный рендеринг часто занимает меньше времени, чем GPU-конвейер.
Android View-система рисует на CPU для детерминированной отрисовки — каждый пиксель точно соответствует коду без GPU-погрешностей. После отрисовки слои передаются в HWUI для GPU-композитинга, что сочетает точность CPU с производительностью GPU.
Для 3D-графики в реальном времени CPU Rendering неэффективен. GPU рендерит 100 млн треугольников в секунду, CPU — 5–10 млн. Исключение — рендеринг отдельных кадров для превью или экспорт, где детерминизм важнее скорости.
На Android используйте Profile GPU Rendering в Developer Options. На iOS — Core Animation profiler в Instruments. Зелёная полоса выше 16 мс указывает на задержки CPU-рендеринга. Также проверьте флаг hardwareAccelerated в манифесте Android.
Skia — 2D-графическая библиотека Google, используемая в Android, Chrome и Flutter. Skia поддерживает программный и GPU-бэкенд. В режиме CPU она выполняет все операции через оптимизированный Software Renderer с использованием NEON-инструкций.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также