Utilizing GPU compute with Geometries¶
導入部¶
GPUコンピュートは、現在比較的容易にジオメトリデフォメーションの計算のために使用することができます。
InlineDrawingシステムは、既にGPU上にあるジオメトリのレンダリングをサポートするように更新されました。
オブジェクトはまだGPUでサポートされておらず、単純な型の構造体と配列だけはGPUコンピュートカーネルで使用することができます。
ポリゴンメッシュ¶
PolygonMesh は、GPU上へ移動しGPUコンピュートカーネルに渡すことができる PolygonMeshTopology 型の ‘topology’ と呼ばれるメンバーを所有しています。
PolygonMesh をGPUカーネルで使用する前に、そのトポロジーデータをGPUに移動する必要があります。ジオメトリが所有する各 :kl-ref:`GeometryAttribute’ もまた使用する前にGPUに個別に移動する必要があります。
GPUコンピュートカーネルはオブジェクト群をサポートしておらず、ポリゴンメッシュやライン群、ポイント群、または様々なアトリビュート型をGPUカーネルへ渡せないことを意味します。代わりに、これらのオブジェクトに含まれるデータはGPUカーネルに渡すことができます。
Each of the GeometryAttributes owns data that can be passed into GPU Compute kernels. Most attributes have a member called ‘values’ which is an array of the data type supported by the GeometryAttribute.
/*
** Example: Moving a polygonMesh to the GPU
*/
require Geometry;
operator meshDataOnGPU<<<index>>>(io PolygonMeshTopology meshData){
report(meshData);
}
operator entry() {
PolygonMesh mesh = PolygonMesh();
Scalar length = 12.0;
Scalar width = 4.0;
Integer lengthSections = 3;
Integer widthSections = 4;
mesh.addPlane(Xfo(), length, width, lengthSections, widthSections, true, true);
mesh.convertToGPU();
meshDataOnGPU<<<1@true>>>(mesh.topology);
mesh.convertToCPU();
report(mesh.getDesc(true, true));
}
/*
** Output:
{polyData:{data:{data:[0,4,8,56,105,154,12,200,9,153,250,20,296,201,249,346,28,392,297,345,440,36,155,104,489,538,44,251,152,537,586,52,347,248,585,634,60,441,344,633,680,68,539,488,728,777,76,587,536,776,825,84,635,584,824,873,92,681,632,872,920],freeData:{data:[0,0],size:61},freeDataCount:0,freeBlocsListUpperPow2:[]},startIters:[1,6,11,16,21,26,31,36,41,46,51,56],freeIters:{data:[0],size:12},freeItersCount:0},pointData:{data:{data:[0,2147483658,8,49,5,4294967295,4294967295,2147483649,9,5,4294967295,4294967295,4294967295,2147483690,169,10,5,4294967295,4294967295,52,209,50,11,168,4294967295,2147483666,48,89,5,4294967295,4294967295,60,249,90,51,208,4294967295,2147483674,88,129,5,4294967295,4294967295,68,289,130,91,248,4294967295,2147483681,128,5,4294967295,4294967295,4294967295,2147483722,131,288,5,4294967295,4294967295,2147483730,329,170,5,4294967295,4294967295,92,369,210,171,328,4294967295,100,409,250,211,368,4294967295,108,449,290,251,408,4294967295,2147483762,291,448,5,4294967295,4294967295,2147483769,330,5,4294967295,4294967295,4294967295,2147483778,370,331,5,4294967295,4294967295,2147483786,410,371,5,4294967295,4294967295,2147483794,450,411,5,4294967295,4294967295,2147483801,451,5,4294967295,4294967295,4294967295],freeData:{data:[1627790432,410060304,3322953990,29759585],size:121},freeDataCount:38,freeBlocsListUpperPow2:[]},startIters:[7,1,25,37,49,13,19,31,43,55,61,67,73,79,85,91,97,103,109,115],freeIters:{data:[0],size:20},freeItersCount:0},unsharedAttrToPointSlidingArray:[],unsharedAttrToPointSlidingArrayBegin:0}
Mesh: pointCount: 20 polygonCount: 12 nbAttributeVectors: 20
Points (adjacent polygons as 'polygon.neighborPolygonIndex',
borders as '|', closed wing starts as '<<',
unshared attribute as value@attributeIndex ):
0: 1 polygons: |0.1
Attr positions:{x:-2.0,y:+0.0,z:-6.0}
Attr normals:{x:+0.0,y:+1.0,z:+0.0}
Attr uvs0:{x:+0.0,y:+0.0}
1: 2 polygons: |0.0, 1.1
Attr positions:{x:-1.0,y:+0.0,z:-6.0}
Attr normals:{x:+0.0,y:+1.0,z:+0.0}
Attr uvs0:{x:+0.25,y:+0.0}
2: 2 polygons: |1.0, 2.1
Attr positions:{x:+0.0,y:+0.0,z:-6.0}
Attr normals:{x:+0.0,y:+1.0,z:+0.0}
Attr uvs0:{x:+0.5,y:+0.0}
3: 2 polygons: |2.0, 3.1
Attr positions:{x:+1.0,y:+0.0,z:-6.0}
Attr normals:{x:+0.0,y:+1.0,z:+0.0}
Attr uvs0:{x:+0.75,y:+0.0}
4: 1 polygons: |3.0
Attr positions:{x:+2.0,y:+0.0,z:-6.0}
Attr normals:{x:+0.0,y:+1.0,z:+0.0}
Attr uvs0:{x:+1.0,y:+0.0}
5: 2 polygons: |4.1, 0.2
Attr positions:{x:-2.0,y:+0.0,z:-2.0}
Attr normals:{x:+0.0,y:+1.0,z:+0.0}
Attr uvs0:{x:+0.0,y:+0.333313}
6: 4 polygons: <<5.1, 1.2, 0.3, 4.0
Attr positions:{x:-1.0,y:+0.0,z:-2.0}
Attr normals:{x:+0.0,y:+1.0,z:+0.0}
Attr uvs0:{x:+0.25,y:+0.333313}
7: 4 polygons: <<6.1, 2.2, 1.3, 5.0
Attr positions:{x:+0.0,y:+0.0,z:-2.0}
Attr normals:{x:+0.0,y:+1.0,z:+0.0}
Attr uvs0:{x:+0.5,y:+0.333313}
8: 4 polygons: <<7.1, 3.2, 2.3, 6.0
Attr positions:{x:+1.0,y:+0.0,z:-2.0}
Attr normals:{x:+0.0,y:+1.0,z:+0.0}
Attr uvs0:{x:+0.75,y:+0.333313}
9: 2 polygons: |3.3, 7.0
Attr positions:{x:+2.0,y:+0.0,z:-2.0}
Attr normals:{x:+0.0,y:+1.0,z:+0.0}
Attr uvs0:{x:+1.0,y:+0.333313}
10: 2 polygons: |8.1, 4.2
Attr positions:{x:-2.0,y:+0.0,z:+2.0}
Attr normals:{x:+0.0,y:+1.0,z:+0.0}
Attr uvs0:{x:+0.0,y:+0.666626}
11: 4 polygons: <<9.1, 5.2, 4.3, 8.0
Attr positions:{x:-1.0,y:+0.0,z:+2.0}
Attr normals:{x:+0.0,y:+1.0,z:+0.0}
Attr uvs0:{x:+0.25,y:+0.666626}
12: 4 polygons: <<10.1, 6.2, 5.3, 9.0
Attr positions:{x:+0.0,y:+0.0,z:+2.0}
Attr normals:{x:+0.0,y:+1.0,z:+0.0}
Attr uvs0:{x:+0.5,y:+0.666626}
13: 4 polygons: <<11.1, 7.2, 6.3, 10.0
Attr positions:{x:+1.0,y:+0.0,z:+2.0}
Attr normals:{x:+0.0,y:+1.0,z:+0.0}
Attr uvs0:{x:+0.75,y:+0.666626}
14: 2 polygons: |7.3, 11.0
Attr positions:{x:+2.0,y:+0.0,z:+2.0}
Attr normals:{x:+0.0,y:+1.0,z:+0.0}
Attr uvs0:{x:+1.0,y:+0.666626}
15: 1 polygons: |8.2
Attr positions:{x:-2.0,y:+0.0,z:+6.0}
Attr normals:{x:+0.0,y:+1.0,z:+0.0}
Attr uvs0:{x:+0.0,y:+1.0}
16: 2 polygons: |9.2, 8.3
Attr positions:{x:-1.0,y:+0.0,z:+6.0}
Attr normals:{x:+0.0,y:+1.0,z:+0.0}
Attr uvs0:{x:+0.25,y:+1.0}
17: 2 polygons: |10.2, 9.3
Attr positions:{x:+0.0,y:+0.0,z:+6.0}
Attr normals:{x:+0.0,y:+1.0,z:+0.0}
Attr uvs0:{x:+0.5,y:+1.0}
18: 2 polygons: |11.2, 10.3
Attr positions:{x:+1.0,y:+0.0,z:+6.0}
Attr normals:{x:+0.0,y:+1.0,z:+0.0}
Attr uvs0:{x:+0.75,y:+1.0}
19: 1 polygons: |11.3
Attr positions:{x:+2.0,y:+0.0,z:+6.0}
Attr normals:{x:+0.0,y:+1.0,z:+0.0}
Attr uvs0:{x:+1.0,y:+1.0}
Polygons (connected points as 'point.polygonPointIndex', borders as '|'):
0: 4 points: 1.0 |, 0.0 |, 5.1, 6.2
1: 4 points: 2.0 |, 1.1, 6.1, 7.2
2: 4 points: 3.0 |, 2.1, 7.1, 8.2
3: 4 points: 4.0 |, 3.1, 8.1, 9.0 |
4: 4 points: 6.3, 5.0 |, 10.1, 11.2
5: 4 points: 7.3, 6.0, 11.1, 12.2
6: 4 points: 8.3, 7.0, 12.1, 13.2
7: 4 points: 9.1, 8.0, 13.1, 14.0 |
8: 4 points: 11.3, 10.0 |, 15.0 |, 16.1
9: 4 points: 12.3, 11.0, 16.0 |, 17.1
10: 4 points: 13.3, 12.0, 17.0 |, 18.1
11: 4 points: 14.1, 13.0, 18.0 |, 19.0 |
*/
データがGPU上に有れば、 CPUで普段よく使用されるものと同じカーネル群をGPU上でも実行できます。
/*
** Example: Performing a smooth mesh using the CPU and then again using the GPU
*/
require Geometry;
operator noiseOp<<<index>>>(io Vec3 positions[], Scalar height){
positions[index].y = mathRandomFloat32(54775, index) * height;
}
operator smoothMesh<<<index>>>(io PolygonMeshTopology mesh, io Vec3 positions[], Vec3 positionsDoubleBuffer[]) {
//Pseudo-gaussian: center weight = 0.5, neighbor weights sum = 0.5
Vec3 position = positionsDoubleBuffer[ index ];
LocalL16UInt32Array surroundingPoints;
mesh.getPointSurroundingPoints( index, false, surroundingPoints );
UInt32 nbNei = surroundingPoints.size();
if( nbNei ) {
Vec3 neiSum = Vec3(0,0,0);
for( UInt32 i = 0; i < nbNei; ++i ) {
UInt32 neiPt = surroundingPoints.get(i);
neiSum += positionsDoubleBuffer[neiPt];
}
neiSum /= Scalar(nbNei);
position = ( position + neiSum ) * 0.5;
mesh.setPointAttribute(index, positions, position );
}
}
operator entry() {
UInt32 iterations = 40;
Scalar length = 120.0;
Scalar width = 40.0;
Integer lengthSections = 1400;
Integer widthSections = 900;
Scalar height = 10.0;
// first smooth on the CPU.
{
PolygonMesh mesh = PolygonMesh();
mesh.addPlane(Xfo(), length, width, lengthSections, widthSections, true, true);
Ref<Vec3Attribute> positionsAttr = mesh.getAttributes().getPositions();
Vec3 positionsDoubleBuffer[];
positionsDoubleBuffer.resize(positionsAttr.values.size());
noiseOp<<<positionsAttr.values.size()@false>>>(positionsAttr.values, height);
UInt64 start = getCurrentTicks();
for(UInt32 i=0; i<iterations; i++){
smoothMesh<<<mesh.pointCount()@false>>>(mesh.topology, positionsDoubleBuffer, positionsAttr.values);
Vec3 tmp[] = positionsAttr.values;
positionsAttr.values = positionsDoubleBuffer;
positionsAttr.values = tmp;
}
UInt64 end = getCurrentTicks();
report("pointCount: " + mesh.pointCount() + " CPU Time: " + getSecondsBetweenTicks(start, end));
}
// then use the same code to smooth on the GPU.
{
PolygonMesh mesh = PolygonMesh();
mesh.addPlane(Xfo(), length, width, lengthSections, widthSections, true, true);
Ref<Vec3Attribute> positionsAttr = mesh.getAttributes().getPositions();
Vec3 positionsDoubleBuffer[];
positionsDoubleBuffer.resize(positionsAttr.values.size());
mesh.convertToGPU();
positionsAttr.convertToGPU();
positionsDoubleBuffer.convertToGPU();
noiseOp<<<positionsAttr.values.size()@true>>>(positionsAttr.values, height);
UInt64 start = getCurrentTicks();
for(UInt32 i=0; i<iterations; i++){
smoothMesh<<<mesh.pointCount()@true>>>(mesh.topology, positionsDoubleBuffer, positionsAttr.values);
Vec3 tmp[] = positionsAttr.values;
positionsAttr.values = positionsDoubleBuffer;
positionsAttr.values = tmp;
}
UInt64 end = getCurrentTicks();
report("pointCount: " + mesh.pointCount() + " GPU Time: " + getSecondsBetweenTicks(start, end));
}
}
/*
** Output:
pointCount: 1262301 CPU Time: +16.46950416309435
pointCount: 1262301 GPU Time: +16.51505684478995
*/
アトリビュート値の設定¶
通常CPU上でアトリビュート値を設定する際には、PolygonMeshヘルパーメソッドの”setPointAttribute”’を使用することができます。ポリゴンメッシュトポロジー構造は、アトリビュートオブジェクトオブジェクトの代わりにとアトリビュートの値を取得する同様のメソッド群をサポートしています。
/*
** Example: Setting Attribute values using setPointAttribute on the PolygonMeshTopology struct.
*/
require Geometry;
operator randomizeMesh<<<index>>>(io PolygonMeshTopology mesh, io Vec3 positions[]) {
Vec3 position = positionsDoubleBuffer[ index ];
positions[index].y = mathRandomFloat32(54775, index) * height;
mesh.setPointAttribute(index, positions, position );
}
operator entry() {
PolygonMesh mesh();
mesh.addPlane(Xfo(), 120.0, 40.0, 1400, 900, true, true);
Ref<Vec3Attribute> positionsAttr = mesh.getAttributes().getPositions();
mesh.convertToGPU();
positionsAttr.convertToGPU();
randomizeMesh<<<mesh.pointCount()@true>>>(mesh.topology, positionsAttr.values);
}
/*
** Output:
(stdin):9:19: error: 'positionsDoubleBuffer': symbol not found
(stdin):10:3: In assignment:
(stdin):10:24: In binary * expression:
(stdin):10:58: In right-hand side:
(stdin):10:58: error: 'height': symbol not found
(stdin):11:44: error: 'position': symbol not found
*/
スキニング・アトリビュート¶
SkinningAttribute はのGPUコンピュートカーネルに渡される SkinningAttributeData 型、または ‘data’ で呼びだされる構造体メンバーを持っています。
/*
** Example: Using the SkinningAttribute in a GPU compute kernel.
*/
require Geometry;
operator skinMeshPositions<<<index>>>(
io PolygonMeshTopology mesh,
io Vec3 positions[],
io SkinningAttributeData skinningAttr,
Mat44 skinningMatrices[]
){
Vec3 srcPos = positions[index];
LocalL16UInt32Array indices;
LocalL16ScalarArray weights;
skinningAttr.getPairs(index, indices, weights);
Scalar weighSum = 0.0;
Vec3 position(0,0,0);
for( UInt32 i = 0; i < indices.size(); ++i ) {
Scalar boneWeight = weights.get(i);
if( boneWeight == 0.0 )
break;
UInt32 boneId = indices.get(i);
position += (skinningMatrices[boneId] * srcPos) * boneWeight;
weighSum += boneWeight;
}
mesh.setPointAttribute( index, positions, position );
}
operator entry() {
PolygonMesh mesh = PolygonMesh();
mesh.addCuboid(Xfo(), 2.0, 2.0, 2.0);
Ref<Vec3Attribute> positionsAttribute = mesh.getAttributes().positionsAttribute;
Ref<SkinningAttribute> skinningAttribute = mesh.getOrCreateAttribute("skinningData", SkinningAttribute);
// Generate a random set of id/weight pairs per vertex in the mesh.
UInt32 numJoints = 5;
UInt32 maxNumJointerPerVertex = 3;
UInt32 seed = 8516;
UInt32 offset = 0;
for(Integer i=0; i<skinningAttribute.size; i++){
UInt16 numItems = mathRandomFloat32(seed, ++offset) * maxNumJointerPerVertex;
skinningAttribute.setPairCount( i, numItems );
for(Integer j=0; j<numItems; j++){
UInt16 index = mathRandomFloat32(seed, ++offset) * numJoints;
Float32 weight = mathRandomFloat32(seed, ++offset);
skinningAttribute.setPair( i, j, index, weight );
}
}
Mat44 skinningMatrices[];
skinningMatrices.resize(numJoints);
mesh.convertToGPU();
positionsAttribute.convertToGPU();
skinningAttribute.convertToGPU();
skinningMatrices.convertToGPU();
skinMeshPositions<<<mesh.pointCount()@true>>>(
mesh.topology,
positionsAttribute.values,
skinningAttribute.data,
skinningMatrices
);
positionsAttribute.convertToCPU();
skinningAttribute.convertToCPU();
mesh.convertToCPU();
report(mesh.getDesc(true, true));
}
/*
** Output:
(stdin):47:20: error: must use parentheses to call methods
(stdin):64:3: error: must use parentheses to call methods
(stdin):67:5: error: incorrect type (expected 'SkinningAttributeData', actual 'Data')
*/
現在の制限¶
現在のGPUコンピュートのインフラは、以下の制限があります。
- アトリビュートのサイズはGPUコンピュート動作中に変更することはできません。ポリゴンメッシュトポロジーでサポートしているメソッド群だけが値を変更できますが、アトリビュートの共有情報は変更しないでください。
- オブジェクト群はまだGPUでサポートされておらず、単純な型と構造体と配列だけがGPUコンピュートカーネルで使用することができます。