int i = f; // вызов __ftoi()
Пишем:
__inline void myftol(int *i, float f) {
__asm fld f;
__asm mov edx, I
__asm fistp[edx];
}
//…
MyFtoL(&i, f);
Либо используем недокументированный переключатель оптимизации /QIfist чтобы избежать вызовов __ftol().
2 Не используем fsqrt — 79 циклов это слишком долго.
Достаточно качественная реализация приведена в NVToolkit (MUST HAVE для всех кто программирует графику, но не хочет лезть в математику). Полный архив NVToolkit можно скачать с http://developer.nvidia.com/. Когда я ее выкачивал он лежал по адресу http://developer.nvidia.com/docs/IO/1208/ATT/NVTK_no_libs.zip.
/********************************************************************* ulSqrt.cpp
Copyright (C) 1999, 2000 NVIDIA Corporation
This file is provided without support, instruction, or implied warranty of any kind.
NVIDIA makes no guarantee of its fitness for a particular purpose and is not liable under any circumstances for any damages or loss whatsoever arising from the use or inability to use this file or items derived from it.
Comments:
*********************************************************************/
#include
#include
#include
static float _0_47 = 0.47f;
static float _1_47 = 1.47f;
float__fastcall ulrsqrt(float x) {
DWORD y;
float r;
_asm {
mov eax, 07F000000h+03F800000h // (ONE_AS_INTEGER<<1) + ONE_AS_INTEGER
sub eax, x
sar eax, 1
mov y, eax // y
fld _0_47 // 0.47
fmul DWORD PTR x // x*0.47
fld DWORD PTR y
fld st(0) // y y x*0.47
fmul st(0), st(1) // y*y y x*0.47
fld _1_47 // 1.47 y*y y x*0.47
fxch st(3) // x*0.47 y*y y 1.47
fmulp st(1), st(0) // x*0.47*y*y y 1.47
fsubp st(2), st(0) // y 1.47-x*0.47*y*y
fmulp st(1), st(0) // result
fstp y
and y, 07FFFFFFFh
}
r = *( float *)&y;
// optional
r = (3.0f - x * (r * r)) * r * 0.5f; // remove for low accuracy
return r;
}
/*
sqrt(x) = x / sqrt(x)
*/
float __fastcall ulsqrt(float x) {
return x * ulrsqrt(x);
}
3 Нормализация векторов. Обычно делают неправильно, но сначала код:
//Обычно делают так:
void normaliseNormalise(Vector *v)
{
float L, L_squared, one_over_L;
L_squared = (v->x * v->x) + (v->y * v->y) + (v->z * v->z);
L = sqrt(L_squared);
one_over_L = 1.0 / L;
v->x = v->x * one_over_L;
v->y = v->y * one_over_L;
v->z = v->z * one_over_L;
}
// А можно так:
#define ONE_AS_INTEGER ((DWORD)(0x3F800000))
float __fastcall InvSqrt(const float & x)
{
DWORD tmp = ((ONE_AS_INTEGER << 1) + ONE_AS_INTEGER - *(DWORD*)&x) >> 1;
float y = *(float*)&tmp;
return y * (1.47f - 0.47f * x * y * y);
}
void Normalise(Vector *v)
{
float L_squared, one_over_L;
L_squared = (v->x * v->x) + (v->y * v->y) + (v->z * v->z);
one_over_L = InvSqrt(L_squared);
v->x = v->x * one_over_L;
v->y = v->y * one_over_L;
v->z = v->z * one_over_L;
}
По-моему комментарии излишни :).
4 Разворачивание циклов
Обычно циклы разворачиваются. Наша цель максимально эффективно использовать кэш процессора, поэтому слишком глубокого разворачивания не нужно, достаточно повторений в цикле.
Для этого используем макросы, но оставляем возможность переключится на функции и не развернутые циклы для отладки (Отладка развернутых циклов сложна и неинформативна).
Опасайтесь разбухания кода!
Измеряйте производительность кода постоянно, причем желательно вести базу данных, в которой будут указываться не только изменения в коде, но и изменения в производительности. Особенно такие базы полезны при работе с несколькими программистами графического ядра приложения.
1. Оптимизация рендеринга
Благодатная тема для описания, существует огромное количество способов сделать неправильно и один способ сделать правильно (Это заявление не относится к операционной системе Windows, для нее правильнее другое: Существует огромное количество способов сделать правильно, но они устарели и их лучше не использовать, а самый лучший способ — это как раз тот, в который мы недавно добавили большое количество NOP'ов и он работает как раз так, чтобы чуть-чуть тормозить на средней системе :)).
DirectX 8 и, в частности, Direct3D8 - это безусловно самая лучшая разработка Корпорации (ведь мы уже смело можем ТАК ее называть).
Итак, следуйте следующим указаниям:
1. Не используйте "тяжелые" функции в цикле рендеринга. Всегда функции
ValidateDevice(), CreateVB(), CreateIB(), DestroyVB(), Optimize(), Clone(), CreateStateBlock(), AssembleVertexShader()
помещайте в загрузку сцены и НИКОГДА в цикл рендеринга приложения. Создание буфера вершин может занять до 100 ms!
2. Использование DrawPrimitiveUP() является ошибкой, вызывает задержки в работе процессора и всегда вызывает дополнительное копирование вершин.
3. Не позволяйте художникам контролировать ваш код. Если вам необходимо рисовать по 200+ вершин за проход, то геометрия должна удовлетворять этому требованию. Позволите себе рисовать по 2 вершины за вызов — и вы ТРУП :(.
Читать дальше