最近、ソフトラスターレンダラーを書いていますが、速度はとても遅いです。パフォーマンステストでは、float lerp関数がボトルネックであることがわかりました。どのようにこの機能の速度を向上させるには? simdを使用しますか?何か案が?どのようにフロートlerp機能の速度を向上させるには?
inline float MathUtil::Lerp(float x1, float x2, float t)
{
return x1 + (x2 - x1)*t;
}
//lerp vector
ZCVector MathUtil::Lerp(const ZCVector& v1, const ZCVector& v2, float t)
{
return ZCVector(
Lerp(v1.x, v2.x, t),
Lerp(v1.y, v2.y, t),
Lerp(v1.z, v2.z, t),
v1.w
);
}
//lerp ZCFLOAT2
ZCFLOAT2 MathUtil::Lerp(const ZCFLOAT2& v1, const ZCFLOAT2& v2, float t)
{
return ZCFLOAT2(
Lerp(v1.u, v2.u, t),
Lerp(v1.v, v2.v, t)
);
}
//lerp ZCFLOAT3
ZCFLOAT3 MathUtil::Lerp(const ZCFLOAT3& v1, const ZCFLOAT3& v2, float t)
{
return ZCFLOAT3(
Lerp(v1.x, v2.x, t),
Lerp(v1.y, v2.y, t),
Lerp(v1.z, v2.z, t)
);
}
//lerp VertexOut
VertexOut MathUtil::Lerp(const VertexOut& v1, const VertexOut& v2, float t)
{
return VertexOut(
Lerp(v1.posTrans, v2.posTrans, t),
Lerp(v1.posH, v2.posH, t),
Lerp(v1.tex, v2.tex, t),
Lerp(v1.normal, v2.normal, t),
Lerp(v1.color, v2.color, t),
Lerp(v1.oneDivZ, v2.oneDivZ, t)
);
}
VertexOutの構造:
class VertexOut
{
public:
ZCVector posTrans;
ZCVector posH;
ZCFLOAT2 tex;
ZCVector normal;
ZCFLOAT3 color;
float oneDivZ;
}
scanlinefill
関数は三角形を埋めるためには、すべての頂点はlerp機能を使用する必要があるので、それはそう何度も呼び出されます。整数xIndex
を増分することによって、ループ(より正確に)、代わり
for (float x = left.posH.x; x <= right.posH.x; x += 0.5f) {
int xIndex = static_cast<int>(x + .5f);
...
}
を、各xIndex
ための右float x
を計算する:
void Tiny3DDeviceContext::ScanlineFill(const VertexOut& left, const VertexOut& right, int yIndex)
{
float dx = right.posH.x - left.posH.x;
for (float x = left.posH.x; x <= right.posH.x; x += 0.5f)
{
int xIndex = static_cast<int>(x + .5f);
if(xIndex >= 0 && xIndex < m_pDevice->GetClientWidth())
{
float lerpFactor = 0;
if (dx != 0)
{
lerpFactor = (x - left.posH.x)/dx;
}
float oneDivZ = MathUtil::Lerp(left.oneDivZ, right.oneDivZ, lerpFactor);
if (oneDivZ >= m_pDevice->GetZ(xIndex,yIndex))
{
m_pDevice->SetZ(xIndex, yIndex, oneDivZ);
//lerp get vertex
VertexOut out = MathUtil::Lerp(left, right, lerpFactor);
out.posH.y = yIndex;
m_pDevice->DrawPixel(xIndex, yIndex, m_pShader->PS(out));
}
}
}
}
おそらくビット単位での手探りを試してください。早すぎる最適化を間違った場所で試してはいけません。 –
あなたはどのプラットフォームを最適化していますか? x86?古いCPUで動作するバイナリが必要ですか、またはAVXとFMAを使用できますか?どのようなコンパイラとオプション?さらに重要なのは、周囲のコードは何ですか?それは実際に自動ベクトル化しますか?この関数自体は、SSEまたは他のSIMD ISAを用いて自明にベクトル化する必要があります。これが埋め込む周囲のコードは明らかに重要です。遅延はスループットの制限要因ですか? –
@πάνταῥεῖ:FP命令間の整数ビット・バイディングは、通常、現代のCPUでは良い考えではありません。 x86では、スカラー浮動小数点でもXMMレジスタを使用するため、SIMD整数命令で処理するか、XMMと整数レジスタ間でゆっくりと往復させる必要があります。 FP insns間で整数ベクトル命令を使用することにより余分なバイパス遅延があるので、1つまたは2つの整数命令で有用なことができたとしても、単にFPを使用するよりもレイテンシが悪くなります。さらに、ベクトルFP命令は非常に高いスループットです。例えばHaswellのクロックごとに2つのベクトルFMA。 –