Comments 33
Мсье знает толк. И это хорошо.
+3
UFO just landed and posted this here
Асм листинг :) Но я решил его не вставлять сюда — это нужно далеко не всем, тем кому это действительно надо не составит труда самому заглянуть в листинг — ссылка на проект в самом низу. Плюс ко всему статья и так обьемная получилась, около 8-ми страниц А4.
0
UFO just landed and posted this here
>почему ручной ассемблер такой неприлично медленный получился.
Компилятор лучше меня знает все о инструкциях — количество циклов, какие могут застоллить пайплайн, как лучше их перетасовать, что бы избежать столлов, как протолкнуть данные в регистры и из регистров — VFP и NEON разделяют между собой набор регистров. Вот и компилятор в асм листинге для интринсиков протаскивал некоторые данные через VFP регистры.
К тому же я не специалист по ассемблеру. Возможно, что кто-то более опытный, сразу заметит косяки в моем асме.
>Я думал, у вас под рукой есть
Тоже нету — я сейчас не за маком
Компилятор лучше меня знает все о инструкциях — количество циклов, какие могут застоллить пайплайн, как лучше их перетасовать, что бы избежать столлов, как протолкнуть данные в регистры и из регистров — VFP и NEON разделяют между собой набор регистров. Вот и компилятор в асм листинге для интринсиков протаскивал некоторые данные через VFP регистры.
К тому же я не специалист по ассемблеру. Возможно, что кто-то более опытный, сразу заметит косяки в моем асме.
>Я думал, у вас под рукой есть
Тоже нету — я сейчас не за маком
+1
Интересный тест.
Судя по результатам самым быстрым оказался GLKMath. Интересно было бы посмотреть какой код выдает компилятор при использовании GLKMatrix4Multiply.
Судя по результатам самым быстрым оказался GLKMath. Интересно было бы посмотреть какой код выдает компилятор при использовании GLKMatrix4Multiply.
0
Самым быстрым оказались интринсики. GLKMath — это просто узкоспециализировання библиотека с векторизированным кодом. Шаг в сторону — и прийдется писать самому. Я здесь её больше привел для сравнения и что бы показать, что новый Clang очень хорошо оптимизирует код. GCC 4.2 жутко сливал в этой задаче и на нем GLKMath давал крошечный прирост.
0
А как поведет себя cblas не смотрели? Я использовал его для несколько больших массивов, но здесь он тоже, кажется, может быть применим.
0
каждое ядро процессора снабжено своим NEON юнитом, когда же VFP — один на процессор.
Здрасьте, с чего это NEON-ов больше чем VFP?
Но у NEON’а есть одна киллер фича – он может параллельно обрабатывать 4 32-х битных флоата, в то время как PowerVR SGX – только один.
NEON в Cortex-A9 64х битный и умеет обрабатывать только ДВА * 32-х битных флоата параллельно.
C какого испуга USSE выполняет лишь 1 флоат операцию? Или речь идёт про пайп а не про ядро?
К сожалению, я не нашел в нем прифетча, что, видимо, и является причиной более медленного кода.
У A9 есть аппаратный префетч (на несколько стримов)
Здрасьте, с чего это NEON-ов больше чем VFP?
Но у NEON’а есть одна киллер фича – он может параллельно обрабатывать 4 32-х битных флоата, в то время как PowerVR SGX – только один.
NEON в Cortex-A9 64х битный и умеет обрабатывать только ДВА * 32-х битных флоата параллельно.
C какого испуга USSE выполняет лишь 1 флоат операцию? Или речь идёт про пайп а не про ядро?
К сожалению, я не нашел в нем прифетча, что, видимо, и является причиной более медленного кода.
У A9 есть аппаратный префетч (на несколько стримов)
+3
Здрасьте, с чего это NEON-ов больше чем VFP?
Моя ошибка :(
NEON в Cortex-A9 64х битный и умеет обрабатывать только ДВА * 32-х битных флоата параллельно.
128-ми битный. Пруф — infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0409e/Chdceejc.html
C какого испуга USSE выполняет лишь 1 флоат операцию? Или речь идёт про пайп а не про ядро?
USSE обрабатывает 32-х! битные флоаты на скалярном процессоре. Если флоаты 16-ти битные — тогда они выполняются на векторном движке. Это описано где-то в мануалах от Imagination. В Rogue этот недостаток будет исправлен. На этом делается акцент в спецификации OpenGL ES 3.0
У A9 есть аппаратный префетч (на несколько стримов)
У А8, который как раз в моем iPod Touch 4, нету апаратного прифетча
Моя ошибка :(
NEON в Cortex-A9 64х битный и умеет обрабатывать только ДВА * 32-х битных флоата параллельно.
128-ми битный. Пруф — infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0409e/Chdceejc.html
C какого испуга USSE выполняет лишь 1 флоат операцию? Или речь идёт про пайп а не про ядро?
USSE обрабатывает 32-х! битные флоаты на скалярном процессоре. Если флоаты 16-ти битные — тогда они выполняются на векторном движке. Это описано где-то в мануалах от Imagination. В Rogue этот недостаток будет исправлен. На этом делается акцент в спецификации OpenGL ES 3.0
У A9 есть аппаратный префетч (на несколько стримов)
У А8, который как раз в моем iPod Touch 4, нету апаратного прифетча
0
128-ми битный
Не все инструкции 128битные
infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0409e/Chdceejc.html
VMUL Dd,Dn,Dm[x] 1 такт
VMUL Qd,Qn,Dm[x] 2 такта
обьяснение остальных значений — чуть выше в п. 3.4.1. Instruction timing tables
Krait и другие современные ARM процессоры — имеют полноразмерный NEON
USSE обрабатывает 32-х! битные флоаты на скалярном процессоре.
Если брать в расчёт только SGX535, то да.
SGX543 это следующее поколение — USSE2, который обрабатывает 2 x FP32
Табличка имеет разные GPU, соотвественно чтобы не создавать путаницы, лучше уточнять какой именно описывается.
Не все инструкции 128битные
infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0409e/Chdceejc.html
VMUL Dd,Dn,Dm[x] 1 такт
VMUL Qd,Qn,Dm[x] 2 такта
обьяснение остальных значений — чуть выше в п. 3.4.1. Instruction timing tables
Krait и другие современные ARM процессоры — имеют полноразмерный NEON
USSE обрабатывает 32-х! битные флоаты на скалярном процессоре.
Если брать в расчёт только SGX535, то да.
SGX543 это следующее поколение — USSE2, который обрабатывает 2 x FP32
Табличка имеет разные GPU, соотвественно чтобы не создавать путаницы, лучше уточнять какой именно описывается.
+1
Не все инструкции 128битные
Я сразу не уловил замечание про размер инструкций. Думал, разговор идет о размере SIMD регистров.
Замечание учтено. Спасибо.
SGX543 это следующее поколение — USSE2, который обрабатывает 2 x FP32
К сожалению мне не удалось найти какой либо официальной информации на этот счет. Потому взял профайлер шейдеров, посмотрел на количество циклов с 535-м компилятором и 543-м. Получилось одно и то же. С чего я и сделал соответствующий вывод. Если вы сможете дать достоверную, подтвержденную информацию на этот счет — буду очень благодарен. Пока я оставлю статью так, как она есть.
Я сразу не уловил замечание про размер инструкций. Думал, разговор идет о размере SIMD регистров.
Замечание учтено. Спасибо.
SGX543 это следующее поколение — USSE2, который обрабатывает 2 x FP32
К сожалению мне не удалось найти какой либо официальной информации на этот счет. Потому взял профайлер шейдеров, посмотрел на количество циклов с 535-м компилятором и 543-м. Получилось одно и то же. С чего я и сделал соответствующий вывод. Если вы сможете дать достоверную, подтвержденную информацию на этот счет — буду очень благодарен. Пока я оставлю статью так, как она есть.
0
Если вы сможете дать достоверную, подтвержденную информацию на этот счет — буду очень благодарен
www.imgtec.com/powervr/sgx_series5XT.asp
USSE2 delivers twice the peak floating point and instruction throughput of Series5 USSE
en.wikipedia.org/wiki/PowerVR#Series_5_.28SGX.29
SGX535 2 pipes / 2 TMU
SGX543 4 pipes / 2 TMU
www.imgtec.com/powervr/sgx_series5XT.asp
USSE2 delivers twice the peak floating point and instruction throughput of Series5 USSE
en.wikipedia.org/wiki/PowerVR#Series_5_.28SGX.29
SGX535 2 pipes / 2 TMU
SGX543 4 pipes / 2 TMU
0
Зачем так делать? Зачем перечитывать матрицу из памяти?
Передавайте матрицу по значению. Компилятор не будет оптимизировать ваш асм код даже в случае инлайна.
Cortex A8 кстати поддерживает dual-issue для вычислителной инструкции и load-store
infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0344i/BABHBCCB.html
Передавайте матрицу по значению. Компилятор не будет оптимизировать ваш асм код даже в случае инлайна.
inline void Matrix4ByVec4(float32x4x4_t* __restrict__ mat, const float32x4_t* __restrict__ vec, float32x4_t* __restrict__ result)
{
asm
(
"vldmia %0, { d24-d31 } \n\t"
"vld1.32 {q1}, [%1]\n\t"
Cortex A8 кстати поддерживает dual-issue для вычислителной инструкции и load-store
infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0344i/BABHBCCB.html
+2
Зачем перечитывать матрицу из памяти?
Я НЕ перечитываю матрицу из памяти. Я загружаю её в NEON регистры
Я НЕ перечитываю матрицу из памяти. Я загружаю её в NEON регистры
0
for (int j = 0; j < 4; ++j) {
Matrix4ByVec4(&mvp, &squareVertices[j], &data[i + j].pos);
}
А это что? 4 раза перечитываете то, что можно загрузить 1 раз.
Я не в курсе, какой ABI у iOS, но если уж рассказыаваете об оптимизации,
то использовать функции вида «умножить 1 вектор на матрицу» — не лучший подход.
Нужно умножать сразу пачку векторов, тогда можно будет выполнять чтение данных параллельно
вычислениям и не создавать пузыри в конвеере.
0
Сделал тест. Код:
Результат изменился не значительно — было 5300, стало 4780, прирост — около 10%. Пытался перетасовать стор инструкции — особой разницы не заметил.
__attribute__((always_inline)) void CalculateSpriteVertsWorldPos(const float32x4x4_t* __restrict__ mvp, float32x4_t* __restrict__ v1, float32x4_t* __restrict__ v2, float32x4_t* __restrict__ v3, float32x4_t* __restrict__ v4)
{
__asm__ volatile
(
"vldmia %0, { q8-q11 }\n\t"
"vldmia %1, { q0-q3 } \n\t"
"vmul.f32 q12, q8, d0[0]\n\t"
"vmla.f32 q12, q9, d0[1]\n\t"
"vmla.f32 q12, q10, d1[0]\n\t"
"vmla.f32 q12, q11, d1[1]\n\t"
"vmul.f32 q13, q8, d2[0]\n\t"
"vmla.f32 q13, q9, d2[1]\n\t"
"vmla.f32 q13, q10, d3[0]\n\t"
"vmla.f32 q13, q11, d3[1]\n\t"
"vmul.f32 q14, q8, d4[0]\n\t"
"vmla.f32 q14, q9, d4[1]\n\t"
"vmla.f32 q14, q10, d5[0]\n\t"
"vmla.f32 q14, q11, d5[1]\n\t"
"vmul.f32 q15, q8, d6[0]\n\t"
"vmla.f32 q15, q9, d6[1]\n\t"
"vmla.f32 q15, q10, d7[0]\n\t"
"vmla.f32 q15, q11, d7[1]\n\t"
"vstmia %2, { q12 }\n\t"
"vstmia %3, { q13 }\n\t"
"vstmia %4, { q14 }\n\t"
"vstmia %5, { q15 }"
:
: "r" (mvp), "r" (squareVertices), "r" (v1), "r" (v2), "r" (v3), "r" (v4)
: "memory", "q0", "q1", "q2", "q3", "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15"
);
}
Результат изменился не значительно — было 5300, стало 4780, прирост — около 10%. Пытался перетасовать стор инструкции — особой разницы не заметил.
0
UFO just landed and posted this here
Во-первых вы создаёте очереди зависимых инструкций вместо того чтобы
использовать возможности конвеера.
У вас же столлы после каждой инструкции идут.
На A8:
«vmul.f32 q12, q8, d0[0]\n\t»
[ждём 5 тактов]
«vmla.f32 q12, q9, d0[1]\n\t»
[ждем 8 тактов]
…
«vmul.f32 q13, q8, d2[0]\n\t»
«vmla.f32 q13, q9, d2[1]\n\t»
=>
«vmul.f32 q12, q8, d0[0]\n\t»
«vmul.f32 q13, q8, d2[0]\n\t»
«vmul.f32 q14, q8, d4[0]\n\t»
«vmul.f32 q15, q8, d6[0]\n\t»
[ждём 1 такт]
…
«vmla.f32 q12, q9, d0[1]\n\t»
«vmla.f32 q13, q9, d2[1]\n\t»
Во-вторых, я бы на вашем месте, раз уж если вы раскрыли цикл, обьединил эту функцию с Matrix4ByMatrix4(), так как они всегда выпоняются в последовательно в одинаковой конфигурации,
тем самым убрав операции соседние чтения / записи матрицы, также создающие столл.
использовать возможности конвеера.
У вас же столлы после каждой инструкции идут.
На A8:
«vmul.f32 q12, q8, d0[0]\n\t»
[ждём 5 тактов]
«vmla.f32 q12, q9, d0[1]\n\t»
[ждем 8 тактов]
…
«vmul.f32 q13, q8, d2[0]\n\t»
«vmla.f32 q13, q9, d2[1]\n\t»
=>
«vmul.f32 q12, q8, d0[0]\n\t»
«vmul.f32 q13, q8, d2[0]\n\t»
«vmul.f32 q14, q8, d4[0]\n\t»
«vmul.f32 q15, q8, d6[0]\n\t»
[ждём 1 такт]
…
«vmla.f32 q12, q9, d0[1]\n\t»
«vmla.f32 q13, q9, d2[1]\n\t»
Во-вторых, я бы на вашем месте, раз уж если вы раскрыли цикл, обьединил эту функцию с Matrix4ByMatrix4(), так как они всегда выпоняются в последовательно в одинаковой конфигурации,
тем самым убрав операции соседние чтения / записи матрицы, также создающие столл.
+1
Изменения не значительные. Результат — 4677
0
Ну значит тормозит не там, а например рандом() % N =)
Дизассемблер смотрели версии с интринсиками? Профайлинг что говорит? в iOS тулсете же есть профайлер?
Всё различие версии с интринсиками должно заключаться в переупорядочивании команд.
Дизассемблер смотрели версии с интринсиками? Профайлинг что говорит? в iOS тулсете же есть профайлер?
Всё различие версии с интринсиками должно заключаться в переупорядочивании команд.
0
Рандом считается отдельно. Это можно на скриншоте с профайлера увидеть.
Дизассемблер смотрели версии с интринсиками?
Всё различие версии с интринсиками должно заключаться в переупорядочивании команд.
В этом-то и вся магия) Я не спец по ассемблеру, в отличии от ребят, которые писали Clang. По коду видно, что компилятор делает много хитрой работы — данные тянет напрямую в d\q регистры и через VFP (s регистры). Опять же у меня под рукой сейчас нет Мака. Да и суть статьи в другом — юзайте интринсики. Они без особых усилий помогут получить максимум производительности, все остальное сделает компилятор. Так же на них можно быстро строить быстрый код из готовых блоков:
при условии, что соответствующие методы векторизированы.
Против написания кастомного кода под каждый конкретный случай:
в который еще никто, кроме авторе не вникнет. Ну и для кроссплатформенности это еще один минус. У Эпиков, к примеру, в коде вообще нет асма, только интринсики вместо него.
Дизассемблер смотрели версии с интринсиками?
Всё различие версии с интринсиками должно заключаться в переупорядочивании команд.
В этом-то и вся магия) Я не спец по ассемблеру, в отличии от ребят, которые писали Clang. По коду видно, что компилятор делает много хитрой работы — данные тянет напрямую в d\q регистры и через VFP (s регистры). Опять же у меня под рукой сейчас нет Мака. Да и суть статьи в другом — юзайте интринсики. Они без особых усилий помогут получить максимум производительности, все остальное сделает компилятор. Так же на них можно быстро строить быстрый код из готовых блоков:
float32x4x4_t mvp;
Matrix4ByMatrix4((float32x4x4_t*)proj.m, (float32x4x4_t*)modelviewMat.m, &mvp);
for (int j = 0; j < 4; ++j) {
Matrix4ByVec4(&mvp, &squareVertices[j], &data[i + j].pos);
}
при условии, что соответствующие методы векторизированы.
Против написания кастомного кода под каждый конкретный случай:
__restrict__ mvp, float32x4_t* __restrict__ v1, float32x4_t* __restrict__ v2, float32x4_t* __restrict__ v3, float32x4_t* __restrict__ v4)
{
__asm__ volatile
(
"vldmia %0, { q8-q11 }\n\t"
"vldmia %1, { q0-q3 } \n\t"
"vmul.f32 q12, q8, d0[0]\n\t"
"vmla.f32 q12, q9, d0[1]\n\t"
"vmla.f32 q12, q10, d1[0]\n\t"
"vmla.f32 q12, q11, d1[1]\n\t"
"vmul.f32 q13, q8, d2[0]\n\t"
"vmla.f32 q13, q9, d2[1]\n\t"
"vmla.f32 q13, q10, d3[0]\n\t"
"vmla.f32 q13, q11, d3[1]\n\t"
"vmul.f32 q14, q8, d4[0]\n\t"
"vmla.f32 q14, q9, d4[1]\n\t"
"vmla.f32 q14, q10, d5[0]\n\t"
"vmla.f32 q14, q11, d5[1]\n\t"
"vmul.f32 q15, q8, d6[0]\n\t"
"vmla.f32 q15, q9, d6[1]\n\t"
"vmla.f32 q15, q10, d7[0]\n\t"
"vmla.f32 q15, q11, d7[1]\n\t"
"vstmia %2, { q12 }\n\t"
"vstmia %3, { q13 }\n\t"
"vstmia %4, { q14 }\n\t"
"vstmia %5, { q15 }"
:
: "r" (mvp), "r" (squareVertices), "r" (v1), "r" (v2), "r" (v3), "r" (v4)
: "memory", "q0", "q1", "q2", "q3", "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15"
);
}
в который еще никто, кроме авторе не вникнет. Ну и для кроссплатформенности это еще один минус. У Эпиков, к примеру, в коде вообще нет асма, только интринсики вместо него.
+1
С таймингами немного нагнал я, но посыл должен быть ясен.
infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0344k/BCGDCECC.html
У инструкции VMLA значение аккумулятора должно быть готово на 3-м такте выполнения.
Результат VMUL готов через 5 тактов. Т.е. «первый» столл, по-идее, получается не 5, а 2 такта.
Органичения VMLA:
If a VMLA.F is followed by an VADD.F or VMUL.F with no RAW hazard, the VADD.F or VMUL.F stalls 4 cycles before issue. The 4 cycle stall preserves the in-order retirement of the instructions.
Т.е.
vmla.f32 q14, ...
[4 такта столл даже без Read-After-Write зависимостей]
vmul.f32 q15, ...
A VMLA.F followed by any NEON floating-point instruction with RAW hazard stalls for 8 cycles.
В случае «второго» столла, последовательность зависимых VMLA / VMLA могла бы выполнится быстрее если бы не это ограничение.
infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0344k/BCGDCECC.html
У инструкции VMLA значение аккумулятора должно быть готово на 3-м такте выполнения.
Результат VMUL готов через 5 тактов. Т.е. «первый» столл, по-идее, получается не 5, а 2 такта.
Органичения VMLA:
If a VMLA.F is followed by an VADD.F or VMUL.F with no RAW hazard, the VADD.F or VMUL.F stalls 4 cycles before issue. The 4 cycle stall preserves the in-order retirement of the instructions.
Т.е.
vmla.f32 q14, ...
[4 такта столл даже без Read-After-Write зависимостей]
vmul.f32 q15, ...
A VMLA.F followed by any NEON floating-point instruction with RAW hazard stalls for 8 cycles.
В случае «второго» столла, последовательность зависимых VMLA / VMLA могла бы выполнится быстрее если бы не это ограничение.
0
Дело в дата лэйауте. У меня помимо позиции на каждую вершину лежит и цвет. То есть прийдется писать специальную ф-цию для этого случая. Опять же, хорошее замечание. Я постараюсь в ближайшее время изменить код и посмотреть на результат. Соответственно, проапдейчу пост
0
Всё таки не увидели в конце результата — на сколько это даёт прироста по филрейту.
На сколько раельные сцены можно считать на CPU.
Мы некогда делали гонки под iPhone 3g ещё.
Там после всех отсечений скармливалось видяхе 20к поликов.
У вас обсчёт их обсчёт (считаем спрайт как два треугольника) сьел 18-20% CPU.
Но сегодня бы я не стал делать в 3д игре 20к поликов — т.к. это выглядит довольно деревянно, по современным меркам.
А увеличить кол-во поликов — и процессор будет только этим и занят, а надо ещё физику считать, и ещё отсекать от огромной сцены обьекты, чтоб не кормить видяхе лишнее, иначе она тоже захлебнётся, иногда успевать декодировать музыку, считать звук и прочее.
Вот физику посчитать на неоне — это наверно самое то было бы.
На сколько раельные сцены можно считать на CPU.
Мы некогда делали гонки под iPhone 3g ещё.
Там после всех отсечений скармливалось видяхе 20к поликов.
У вас обсчёт их обсчёт (считаем спрайт как два треугольника) сьел 18-20% CPU.
Но сегодня бы я не стал делать в 3д игре 20к поликов — т.к. это выглядит довольно деревянно, по современным меркам.
А увеличить кол-во поликов — и процессор будет только этим и занят, а надо ещё физику считать, и ещё отсекать от огромной сцены обьекты, чтоб не кормить видяхе лишнее, иначе она тоже захлебнётся, иногда успевать декодировать музыку, считать звук и прочее.
Вот физику посчитать на неоне — это наверно самое то было бы.
0
Во-первых — это демо, а не реальный игровой проект.
У вас обсчёт их обсчёт (считаем спрайт как два треугольника) сьел 18-20% CPU.
Тогда попытайтесь нарисовать 10к динамичных спрайтов любым другим способом, к примеру по draw call'у на спрайт — боюсь, что в таком случае ваш фпс просядет раз в 10-20…
Всё таки не увидели в конце результата — на сколько это даёт прироста по филрейту.
Ровно на столько, на сколько мы освободили USSE от вершинного процессинга. Здесь замкнутый круг — если я буду сравнивать свой, так сказать, софтварный инстансинг с draw call'ом на спрайт — то я получу громадную разницу, но сравнение будет не объективным, так как при таком инстансинге я упираюсь именно в филлрейт, а при ДИПе на спрайт — в синхронизацию процессора с гпу. Так же, к сожалению, я не могу померить использование USSE на iOS девайсе — что бы знать, на сколько я освободил его от расчетов вершинных шейдеров. Это можно сделать имея не залоченное железо на Андроиде, Линуксе или Винде.
А увеличить кол-во поликов — и процессор будет только этим и занят, а надо ещё физику считать, и ещё отсекать от огромной сцены обьекты
Ну так современные девайсы имеют больше одного ядра — зачем же им простаивать-то?!
У вас обсчёт их обсчёт (считаем спрайт как два треугольника) сьел 18-20% CPU.
Тогда попытайтесь нарисовать 10к динамичных спрайтов любым другим способом, к примеру по draw call'у на спрайт — боюсь, что в таком случае ваш фпс просядет раз в 10-20…
Всё таки не увидели в конце результата — на сколько это даёт прироста по филрейту.
Ровно на столько, на сколько мы освободили USSE от вершинного процессинга. Здесь замкнутый круг — если я буду сравнивать свой, так сказать, софтварный инстансинг с draw call'ом на спрайт — то я получу громадную разницу, но сравнение будет не объективным, так как при таком инстансинге я упираюсь именно в филлрейт, а при ДИПе на спрайт — в синхронизацию процессора с гпу. Так же, к сожалению, я не могу померить использование USSE на iOS девайсе — что бы знать, на сколько я освободил его от расчетов вершинных шейдеров. Это можно сделать имея не залоченное железо на Андроиде, Линуксе или Винде.
А увеличить кол-во поликов — и процессор будет только этим и занят, а надо ещё физику считать, и ещё отсекать от огромной сцены обьекты
Ну так современные девайсы имеют больше одного ядра — зачем же им простаивать-то?!
0
Если игра 2д с кучей спрайтов, которые каждый кадр меняют своё положение — то да ваш способ действительно имеет смысл.
У меня и мысли не было что кому то в голову придёт скармливать видяхе по одному спрайту.
2д считаем на cpu, складываем в буфер и пачкой засылаем в GPU.
Но если игра 3д — то там же нет необходимости по паре полигонов гонять — засунули большой меш в GPU, оно его переварило и нарисовало.
Наверно я не совсем правильно воспринял, для чего всё это вы задумали. Пример со спрайтами должен был это подсказать :)
У меня и мысли не было что кому то в голову придёт скармливать видяхе по одному спрайту.
2д считаем на cpu, складываем в буфер и пачкой засылаем в GPU.
Но если игра 3д — то там же нет необходимости по паре полигонов гонять — засунули большой меш в GPU, оно его переварило и нарисовало.
Наверно я не совсем правильно воспринял, для чего всё это вы задумали. Пример со спрайтами должен был это подсказать :)
0
Все не ограничивается спрайтами. К примеру, если у тебя требование — OpenGL ES 1.1, то скининг мешей так же лучше делать на НЕОНе. Если множество низко полигональных объектов — их так же будет быстрее софтварно заинстансить, чем рисовать по одному.
0
Sign up to leave a comment.
Оптимизация игр под iOS платформу. Векторизация кода