私は流体シミュレーションを実装したいと思います。何かthisのようなものです。アルゴリズムは重要ではありません。重要な問題は、それをピクセルシェーダに実装する場合は、複数のパスで実行する必要があるということです。マルチパスピクセルシェーダをシェーダを計算するために変換する技術はありますか?
私が使っていた技術の問題は、パフォーマンスが非常に悪いことです。私は何が起こっているのか、そして1回のパスで計算を解決するために使用されたテクニック、そしてタイミング情報について説明します。
概要:
私たちは地形を持っているし、我々はそれの上に雨や水の流れを見てみたいです。 1024x1024テクスチャに関するデータがあります。私たちは地形の高さと各地点の水の量を持っています。これは反復シミュレーションです。反復1は、地形と水のテクスチャを入力として取得し、結果を計算し、地形と水のテクスチャに結果を書き込みます。反復2が実行され、再度テクスチャが変更されます。これらの段階が起こるの各反復で
:
- は地形や水の高さを取得する反復の何百もの後、私たちはこのようなものを持っています。
- フローを計算します。
- フローの値をグループ共有メモリに書き込みます。
- 同期グループメモリ
- このスレッドのスレッド共有メモリからのフロー値と、現在のスレッドの左、右、上、下のスレッドを読み込みます。
- 前の手順で読み込んだFlow値に基づいて水の新しい値を計算します。
- 地形と水のテクスチャに結果を書き込みます。
だから基本的に私たちは、データをフェッチcalculate1は、共有メモリ、同期にcalculate1結果を入れてください、現在のスレッドや隣人のための共有メモリからフェッチcalculate2を行い、そして結果を書き込みます。
これは、非常に広い範囲の画像処理の問題で発生する明確なパターンです。従来のソリューションはマルチパスシェーダでしたが、帯域幅を節約するために1パスの計算シェーダで行いました。
テクニック:私は技術を使用し
がPractical Rendering and Computation with Direct3D 11第12章で説明したが、我々は、各スレッドグループは16x16x1スレッドになりたいとします。しかし、2番目の計算には近隣も必要なので、各方向にピクセルを塗りつぶします。つまり、18x18x1のスレッドグループを持つことになります。このパディングのために、2番目の計算では有効な近傍があります。ここにパディングを示す画像があります。黄色のスレッドは、計算が必要なスレッドで、赤のスレッドはパディングです。それらはスレッドグループの一部ですが、中間処理に使用して、テクスチャに保存しません。この図では、パディング付きのグループは10x10x1ですが、スレッドグループは18x18x1です。
処理が実行され、正しい結果を返します。唯一の問題はパフォーマンスです。
タイミング: Geforce GT 710のシステムでは、10000回の繰り返しでシミュレーションを実行します。
- 完全で正確なシミュレーションを実行するには60秒かかります。
- ボーダーをパディングせずに16x16x1スレッドグループを使用する場合、時間は40秒になります。明らかに結果は間違っています。
- 私はgroupsharedメモリを使用せず、2番目の計算にダミー値を入力すると、時間は19秒になります。結果はもちろん間違っています。
質問:
- これは、この問題を解決するための最良の手法ですか?代わりに、2つの異なるカーネルで計算すると、より高速になります。 2x19 < 60.
- なぜグループ共有メモリが遅いのですか?
ここに計算シェーダコードがあります。それは60秒かかる正しいバージョンです:
#pragma kernel CSMain
Texture2D<float> _waterAddTex;
Texture2D<float4> _flowTex;
RWTexture2D<float4> _watNormTex;
RWTexture2D<float4> _flowOutTex;
RWTexture2D<float> terrainFieldX;
RWTexture2D<float> terrainFieldY;
RWTexture2D<float> waterField;
SamplerState _LinearClamp;
SamplerState _LinearRepeat;
#define _gpSize 16
#define _padGPSize 18
groupshared float4 f4shared[_padGPSize * _padGPSize];
float _timeStep, _resolution, _groupCount, _pixelMeter, _watAddStrength, watDamping, watOutConstantParam, _evaporation;
int _addWater, _computeWaterNormals;
float2 _rainUV;
bool _usePrevOutflow,_useStava;
float terrHeight(float2 texData) {
return dot(texData, identity2);
}
[numthreads(_padGPSize, _padGPSize, 1)]
void CSMain(int2 groupID : SV_GroupID, uint2 dispatchIdx : SV_DispatchThreadID, uint2 padThreadID : SV_GroupThreadID)
{
int2 id = groupID * _gpSize + padThreadID - 1;
int gsmID = padThreadID.x + _padGPSize * padThreadID.y;
float2 uv = (id + 0.5)/_resolution;
bool outOfGroupBound = (padThreadID.x == 0 || padThreadID.y == 0 || padThreadID.x == _padGPSize - 1
|| padThreadID.y == _padGPSize - 1) ? true : false;
// -------------FETCH-------------
float2 cenTer, lTer, rTer, tTer, bTer;
sampleUavNei(terrainFieldX,terrainFieldY, id, cenTer, lTer, rTer, tTer, bTer);
float cenWat, lWat, rWat, tWat, bWat;
sampleUavNei(waterField, id, cenWat, lWat, rWat, tWat, bWat);
// -------------Calculate 1-------------
float cenTerHei = terrHeight(cenTer);
float cenTotHei = cenWat + cenTerHei;
float4 neisTerHei = float4(terrHeight(lTer), terrHeight(rTer), terrHeight(tTer), terrHeight(bTer));
float4 neisWat = float4(lWat, rWat, tWat, bWat);
float4 neisTotHei = neisWat + neisTerHei;
float4 neisTotHeiDiff = cenTotHei - neisTotHei;
float4 prevOutflow = _usePrevOutflow? _flowTex.SampleLevel(_LinearClamp, uv, 0):float4(0,0,0,0);
float4 watOutflow;
float4 flowFac = min(abs(neisTotHeiDiff), (cenWat + neisWat) * 0.5f);
flowFac = min(1, flowFac);
watOutflow = max(watDamping* prevOutflow + watOutConstantParam * neisTotHeiDiff * flowFac, 0);
float outWatFac = cenWat/max(dot(watOutflow, identity4) * _timeStep, 0.001f);
outWatFac = min(outWatFac, 1);
watOutflow *= outWatFac;
// -------------groupshared memory-------------
f4shared[gsmID] = watOutflow;
GroupMemoryBarrierWithGroupSync();
float4 cenFlow = f4shared[gsmID];
float4 lFlow = f4shared[gsmID - 1];
float4 rFlow = f4shared[gsmID + 1];
float4 tFlow = f4shared[gsmID + _padGPSize];
float4 bFlow = f4shared[gsmID - _padGPSize];
//float4 cenFlow = 0;
//float4 lFlow = 0;
//float4 rFlow = 0;
//float4 tFlow = 0;
//float4 bFlow = 0;
// -------------Calculate 2-------------
if (!outOfGroupBound) {
float watDiff = _timeStep *((lFlow.y + rFlow.x + tFlow.w + bFlow.z) - dot(cenFlow, identity4));
cenWat = cenWat + watDiff - _evaporation;
cenWat = max(cenWat, 0);
}
// -------------End of calculation-------------
//Water Addition
if (_addWater)
cenWat += _timeStep * _watAddStrength * _waterAddTex.SampleLevel(_LinearRepeat, uv + _rainUV, 0);
if (_computeWaterNormals)
_watNormTex[id] = float4(0, 1, 0, 0);
// -------------Write results-------------
if (!outOfGroupBound) {
_flowOutTex[id] = cenFlow;
waterField[id] = cenWat;
}
}