ケーススタディ-パフォーマンスが異なるデバイス間での Datascape スケーリングCase study - Scaling Datascape across devices with different performance

Datascape は、Microsoft 社内で開発された Windows Mixed Reality アプリケーションで、地形データの上に気象データを表示することに重点を置いています。Datascape is a Windows Mixed Reality application developed internally at Microsoft where we focused on displaying weather data on top of terrain data. このアプリケーションでは、ユーザーを holographic データ可視化で囲むことによって、ユーザーが混合現実のデータを検出することによって得られる一意の洞察について説明します。The application explores the unique insights users gain from discovering data in mixed reality by surrounding the user with holographic data visualization.

Datascape では、さまざまなハードウェア機能を備えたさまざまなプラットフォームを対象にしたいと考えています。これは、Microsoft HoloLens から Windows Mixed Reality のクロスヘッドヘッドセットや、低電力の Pc からハイエンド GPU を搭載した最新の Pc まで多岐にわたります。For Datascape we wanted to target a variety of platforms with different hardware capabilities ranging from Microsoft HoloLens to Windows Mixed Reality immersive headsets, and from lower-powered PCs to the very latest PCs with high-end GPU. 主な課題は、高いフレームレートで実行しながら、グラフィックス機能が大幅に異なるデバイスで、視覚的に魅力のあるシーンをレンダリングすることでした。The main challenge was rendering our scene in a visually appealing matter on devices with wildly different graphics capabilities while executing at a high framerate.

このケーススタディでは、GPU 集中型のシステムのいくつかを作成するために使用されるプロセスと手法について説明し、発生した問題とその克服について説明します。This case study will walk through the process and techniques used to create some of our more GPU-intensive systems, describing the problems we encountered and how we overcame them.

透明度とオーバードローTransparency and overdraw

GPU では透明度が高くなる可能性があるため、メインレンダリング格闘は透明度で処理されます。Our main rendering struggles dealt with transparency, since transparency can be expensive on a GPU.

深度バッファーへの書き込み中に、ソリッドジオメトリを前面にレンダリングして、そのピクセルの背後にある今後のピクセルを破棄しないようにすることができます。Solid geometry can be rendered front to back while writing to the depth buffer, stopping any future pixels located behind that pixel from being discarded. これにより、非表示のピクセルがピクセルシェーダーを実行できなくなり、プロセスが大幅に高速化されます。This prevents hidden pixels from executing the pixel shader, speeding up the process significantly. Geometry が最適に並べ替えられている場合は、画面上の各ピクセルが1回だけ描画されます。If geometry is sorted optimally, each pixel on the screen will be drawn only once.

透明なジオメトリは最前面に並べ替える必要があり、ピクセルシェーダーの出力を画面上の現在のピクセルにブレンドすることに依存します。Transparent geometry needs to be sorted back to front and relies on blending the output of the pixel shader to the current pixel on the screen. これにより、画面上の各ピクセルがフレームごとに複数回描画される可能性があります。これは、オーバードローと呼ばれます。This can result in each pixel on the screen being drawn to multiple times per frame, referred to as overdraw.

HoloLens およびメインストリーム Pc の場合、画面はほんの数回しか入力できないため、透明なレンダリングの問題が発生します。For HoloLens and mainstream PCs, the screen can only be filled a handful of times, making transparent rendering problematic.

Datascape コンポーネントの概要Introduction to Datascape scene components

シーンには3つの主要なコンポーネントがありました。UI、マップ、および天気We had three major components to our scene; the UI, the map, and the weather. 気象効果によって得られる GPU 時間がすべて必要になることが事前にわかっているので、UI や地形を意図的にオーバードローを減らすように設計しました。We knew early on that our weather effects would require all the GPU time it could get, so we purposely designed the UI and terrain in a way that would reduce any overdraw.

UI を何度か作り直すことで、生成されるオーバードローの量を最小限に抑えることができます。We reworked the UI several times to minimize the amount of overdraw it would produce. 光るボタンやマップの概要などのコンポーネントに対して、透明なアートを互いの上にオーバーレイするのではなく、より複雑なジオメトリの側面に誤りしています。We erred on the side of more complex geometry rather than overlaying transparent art on top of each other for components like glowing buttons and map overviews.

このマップでは、シャドウや複雑な光源などの標準的な Unity 機能を除去するカスタムシェーダーを使用し、単純な単一の sun 照明モデルとカスタムのフォグ計算で置き換えます。For the map, we used a custom shader that would strip out standard Unity features such as shadows and complex lighting, replacing them with a simple single sun lighting model and a custom fog calculation. これにより、単純なピクセルシェーダーが生成され、GPU サイクルが解放されます。This produced a simple pixel shader and free up GPU cycles.

UI とマップを、ハードウェアに応じて変更する必要がない予算でレンダリングするように管理されています。しかし、特にクラウドのレンダリングでは、気象の視覚化は課題の多くになることが実証されています。We managed to get both the UI and the map to rendering at budget where we did not need any changes to them depending on the hardware; however, the weather visualization, in particular the cloud rendering, proved to be more of a challenge!

クラウドデータの背景Background on cloud data

私たちのクラウドデータは、noaa サーバー http://nomads.ncep.noaa.gov/) からダウンロードされたもので、3つの異なる2d レイヤー (それぞれがクラウドの最上位および下位の高さを備えています) と、グリッドの各セルのクラウドの密度です。Our cloud data was downloaded from NOAA servers (http://nomads.ncep.noaa.gov/) and came to us in three distinct 2D layers, each with the top and bottom height of the cloud, as well as the density of the cloud for each cell of the grid. データは、GPU に簡単にアクセスできるように、各コンポーネントがテクスチャの赤、緑、および青のコンポーネントに格納されているクラウド情報テクスチャに処理されました。The data got processed into a cloud info texture where each component was stored in the red, green, and blue component of the texture for easy access on the GPU.

ジオメトリクラウドGeometry clouds

低電力のマシンがクラウドをレンダリングできるようにするために、ソリッドジオメトリを使用してオーバードローを最小化するアプローチから始めることを決定しました。To make sure our lower-powered machines could render our clouds we decided to start with an approach that would use solid geometry to minimize overdraw.

まず、図形を生成するために、頂点ごとにクラウド情報テクスチャの半径を使用して、レイヤーごとにソリッド高度マップメッシュを生成することで、クラウドの生成を試みました。We first tried producing clouds by generating a solid heightmap mesh for each layer using the radius of the cloud info texture per vertex to generate the shape. ここでは、ジオメトリシェーダーを使用して、ソリッドクラウドの形状を生成するクラウドの上部と下部の両方で頂点を生成しています。We used a geometry shader to produce the vertices both at the top and the bottom of the cloud generating solid cloud shapes. テクスチャの密度値を使用して、より密度の高いクラウドに対して暗い色でクラウドに色を付けています。We used the density value from the texture to color the cloud with darker colors for more dense clouds.

頂点を作成するためのシェーダー:Shader for creating the vertices:

v2g vert (appdata v)
{
    v2g o;
    o.height = tex2Dlod(_MainTex, float4(v.uv, 0, 0)).x;
    o.vertex = v.vertex;
    return o;
}
 
g2f GetOutput(v2g input, float heightDirection)
{
    g2f ret;
    float4 newBaseVert = input.vertex;
    newBaseVert.y += input.height * heightDirection * _HeigthScale;
    ret.vertex = UnityObjectToClipPos(newBaseVert);
    ret.height = input.height;
    return ret;
}
 
[maxvertexcount(6)]
void geo(triangle v2g p[3], inout TriangleStream<g2f> triStream)
{
    float heightTotal = p[0].height + p[1].height + p[2].height;
    if (heightTotal > 0)
    {
        triStream.Append(GetOutput(p[0], 1));
        triStream.Append(GetOutput(p[1], 1));
        triStream.Append(GetOutput(p[2], 1));
 
        triStream.RestartStrip();
 
        triStream.Append(GetOutput(p[2], -1));
        triStream.Append(GetOutput(p[1], -1));
        triStream.Append(GetOutput(p[0], -1));
    }
}
fixed4 frag (g2f i) : SV_Target
{
    clip(i.height - 0.1f);
 
    float3 finalColor = lerp(_LowColor, _HighColor, i.height);
    return float4(finalColor, 1);
}

実際のデータの上に詳細を表示するために、小さなノイズパターンを導入しました。We introduced a small noise pattern to get more detail on top of the real data. ラウンドクラウドのエッジを生成するために、補間半径の値がしきい値に達したときにピクセルシェーダーのピクセルをクリップし、ゼロの値を破棄します。To produce round cloud edges, we clipped the pixels in the pixel shader when the interpolated radius value hit a threshold to discard near-zero values.

ジオメトリクラウド

雲はソリッドジオメトリなので、地形の前にレンダリングして、低コストのマップピクセルを非表示にして、フレームレートをさらに向上させることができます。Since the clouds are solid geometry, they can be rendered before the terrain to hide any expensive map pixels underneath to further improve framerate. このソリューションは、完全なジオメトリレンダリングアプローチにより、最小仕様からハイエンドグラフィックスカード、または HoloLens で、すべてのグラフィックスカードで適切に実行されています。This solution ran well on all graphics cards from min-spec to high-end graphics cards, as well as on HoloLens, because of the solid geometry rendering approach.

ソリッドパーティクルクラウドSolid particle clouds

これで、クラウドデータの適正な表現を生成したバックアップソリューションが完成しましたが、"wow" 要素には少し lackluster があったので、ハイエンドのコンピューターに必要な容量の感覚が伝達されていませんでした。We now had a backup solution that produced a decent representation of our cloud data, but was a bit lackluster in the “wow” factor and did not convey the volumetric feel that we wanted for our high-end machines.

次のステップでは、約10万のパーティクルを使ってクラウドを作成し、より自然で容量な外観を生成しました。Our next step was creating the clouds by representing them with approximately 100,000 particles to produce a more organic and volumetric look.

パーティクルが手前に表示され、前方に並べ替えられる場合でも、以前にレンダリングされたパーティクルの背後にあるピクセルの深度バッファーカリングを利用して、オーバードローを減らすことができます。If particles stay solid and sort front-to-back, we can still benefit from depth buffer culling of the pixels behind previously rendered particles, reducing the overdraw. また、パーティクルベースのソリューションを使用すると、さまざまなハードウェアをターゲットにするために使用されるパーティクルの量を変更できます。Also, with a particle-based solution, we can alter the amount of particles used to target different hardware. ただし、すべてのピクセルを詳細にテストする必要があるため、追加のオーバーヘッドが発生します。However, all pixels still need to be depth tested, which results in some additional overhead.

まず、起動時に、エクスペリエンスの中心点を中心にしたパーティクル位置を作成しました。First, we created particle positions around the center point of the experience at startup. これらのパーティクルは、距離を中心としてより高密度に分布しています。We distributed the particles more densely around the center and less so in the distance. 最も近いパーティクルが先にレンダリングされるように、中心からすべてのパーティクルを事前に事前に並べ替えています。We pre-sorted all particles from the center to the back so that the closest particles would render first.

コンピューティングシェーダーでは、各パーティクルを正しい高さに配置し、密度に基づいて色を設定するために、クラウド情報テクスチャをサンプリングします。A compute shader would sample the cloud info texture to position each particle at a correct height and color it based on the density.

ここでは、 Drawproceduralを使用して、パーティクルごとにクワッドをレンダリングしています。これにより、パーティクルデータを常に GPU に維持できます。We used DrawProcedural to render a quad per particle allowing the particle data to stay on the GPU at all times.

各パーティクルには、高さと半径の両方が含まれていました。Each particle contained both a height and a radius. 高さは、クラウド情報テクスチャからサンプリングされたクラウドデータに基づいています。半径は、最も近い近隣ノードへの水平方向の距離を格納するために計算される初期分布に基づいていました。The height was based on the cloud data sampled from the cloud info texture, and the radius was based on the initial distribution where it would be calculated to store the horizontal distance to its closest neighbor. これらの四角形は、このデータを使用して、ユーザーが水平方向に見たときに高さが表示され、ユーザーが上から見たときに、その近くにある領域をカバーします。The quads would use this data to orient itself angled by the height so that when users look at it horizontally, the height would be shown, and when users looked at it top-down, the area between its neighbors would be covered.

パーティクル図形

分布を示すシェーダーコード:Shader code showing the distribution:

ComputeBuffer cloudPointBuffer = new ComputeBuffer(6, quadPointsStride);
cloudPointBuffer.SetData(new[]
{
    new Vector2(-.5f, .5f),
    new Vector2(.5f, .5f),
    new Vector2(.5f, -.5f),
    new Vector2(.5f, -.5f),
    new Vector2(-.5f, -.5f),
    new Vector2(-.5f, .5f)
});
 
StructuredBuffer<float2> quadPoints;
StructuredBuffer<float3> particlePositions;
v2f vert(uint id : SV_VertexID, uint inst : SV_InstanceID)
{
    // Find the center of the quad, from local to world space
    float4 centerPoint = mul(unity_ObjectToWorld, float4(particlePositions[inst], 1));
 
    // Calculate y offset for each quad point
    float3 cameraForward = normalize(centerPoint - _WorldSpaceCameraPos);
    float y = dot(quadPoints[id].xy, cameraForward.xz);
 
    // Read out the particle data
    float radius = ...;
    float height = ...;
 
    // Set the position of the vert
    float4 finalPos = centerPoint + float4(quadPoints[id].x, y * height, quadPoints[id].y, 0) * radius;
    o.pos = mul(UNITY_MATRIX_VP, float4(finalPos.xyz, 1));
    o.uv = quadPoints[id].xy + 0.5;
 
    return o;
}

ここでは、パーティクルを前方から順に並べ替えて、透明なピクセルをクリップ (blend ではない) の透明なピクセルでクリップします。この手法では、驚くほどのパーティクルが処理されるため、低電力のコンピューターであってもコストがかかります。Since we sort the particles front-to-back and we still used a solid style shader to clip (not blend) transparent pixels, this technique handles a surprising amount of particles, avoiding costly over-draw even on the lower-powered machines.

透過的なパーティクルクラウドTransparent particle clouds

ソリッドなパーティクルは、雲の形に優れた有機感を与えましたが、クラウドの fluffiness を販売するために何かが必要でした。The solid particles provided a good organic feel to the shape of the clouds but still needed something to sell the fluffiness of clouds. ここでは、透明度を導入できるハイエンドグラフィックスカードのカスタムソリューションを試してみます。We decided to try a custom solution for the high-end graphics cards where we can introduce transparency.

これを行うには、単にパーティクルの初期並べ替え順序を切り替え、テクスチャアルファを使用するようにシェーダーを変更します。To do this we simply switched the initial sorting order of the particles and changed the shader to use the textures alpha.

Fluffy クラウド

これはすばらしいコンピューターであっても、画面に各ピクセルが何回もレンダリングされるようになったため、非常に多くの時間がかかりすぎることがわかっていました。It looked great but proved to be too heavy for even the toughest machines since it would result in rendering each pixel on the screen hundreds of times!

解像度を低くして画面を表示しないRender off-screen with lower resolution

クラウドによってレンダリングされるピクセル数を減らすために、(画面と比較して) 四半期ごとの解像度のバッファーにレンダリングを開始し、すべてのパーティクルが描画された後に、最終的な結果を画面上に拡大します。To reduce the number of pixels rendered by the clouds, we started rendering them in a quarter resolution buffer (compared to the screen) and stretching the end result back up onto the screen after all the particles had been drawn. これにより、約4倍の高速化が得られましたが、いくつかの注意事項がありました。This gave us roughly a 4x speedup, but came with a couple of caveats.

画面外に表示するコード:Code for rendering off-screen:

cloudBlendingCommand = new CommandBuffer();
Camera.main.AddCommandBuffer(whenToComposite, cloudBlendingCommand);
 
cloudCamera.CopyFrom(Camera.main);
cloudCamera.rect = new Rect(0, 0, 1, 1);    //Adaptive rendering can set the main camera to a smaller rect
cloudCamera.clearFlags = CameraClearFlags.Color;
cloudCamera.backgroundColor = new Color(0, 0, 0, 1);
 
currentCloudTexture = RenderTexture.GetTemporary(Camera.main.pixelWidth / 2, Camera.main.pixelHeight / 2, 0);
cloudCamera.targetTexture = currentCloudTexture;
 
// Render clouds to the offscreen buffer
cloudCamera.Render();
cloudCamera.targetTexture = null;
 
// Blend low-res clouds to the main target
cloudBlendingCommand.Blit(currentCloudTexture, new RenderTargetIdentifier(BuiltinRenderTextureType.CurrentActive), blitMaterial);

まず、画面以外のバッファーにレンダリングすると、メインシーンからすべての深度情報が失われ、その結果、山の上に粒子が表示されます。First, when rendering into an off-screen buffer, we lost all depth information from our main scene, resulting in particles behind mountains rendering on top of the mountain.

2つ目の方法として、バッファーを拡張することで、解決の変化が明らかになったクラウドの端にも成果物が導入されました。Second, stretching the buffer also introduced artifacts on the edges of our clouds where the resolution change was noticeable. 次の2つのセクションでは、これらの問題の解決方法について説明します。The next two sections talk about how we resolved these issues.

パーティクル深度バッファーParticle depth buffer

粒子をワールドジオメトリと共存させ、マウンテンまたはオブジェクトがその背後にあるパーティクルに対応できるようにするために、スクリーンバッファーにメインシーンのジオメトリを含む深度バッファーを設定しました。To make the particles co-exist with the world geometry where a mountain or object could cover particles behind it, we populated the off-screen buffer with a depth buffer containing the geometry of the main scene. このような深度バッファーを生成するために、2つ目のカメラを作成しました。これにより、シーンのソリッドジオメトリと深度のみがレンダリングされます。To produce such depth buffer, we created a second camera, rendering only the solid geometry and depth of the scene.

次に、クラウドのピクセルシェーダーの新しいテクスチャを occlude ピクセルに使用します。We then used the new texture in the pixel shader of the clouds to occlude pixels. 同じテクスチャを使用して、クラウドピクセルの背後にあるジオメトリへの距離を計算しています。We used the same texture to calculate the distance to the geometry behind a cloud pixel. その距離を使用して、ピクセルのアルファに適用することで、雲の近くに近づいたときに、パーティクルや地形があるハードカットを取り除くことで、雲の効果が得られるようになりました。By using that distance and applying it to the alpha of the pixel, we now had the effect of clouds fading out as they get close to terrain, removing any hard cuts where particles and terrain meet.

地形にブレンドされる雲

枠をシャープにするSharpening the edges

拡張されたクラウドは、パーティクルの中心にある通常のサイズのクラウドとほぼ同じで、クラウドのエッジにいくつかのアーティファクトが示されていました。The stretched-up clouds looked almost identical to the normal size clouds at the center of the particles or where they overlapped, but showed some artifacts at the cloud edges. そうしないと、カメラの移動時にシャープの端がぼやけて表示されます。Otherwise sharp edges would appear blurry and alias effects were introduced when the camera moved.

これを解決するには、画面以外のバッファーで単純なシェーダーを実行して、大きな変化が発生した箇所を特定します (1)。We solved this by running a simple shader on the off-screen buffer to determine where big changes in contrast occurred (1). 大きな変化を持つピクセルを新しいステンシルバッファー (2) に配置します。We put the pixels with big changes into a new stencil buffer (2). 次に、スクリーンバッファーを画面に戻すときに、ステンシルバッファーを使用してこれらのハイコントラスト領域をマスクし、クラウドに穴を入れます (3)。We then used the stencil buffer to mask out these high contrast areas when applying the off-screen buffer back to the screen, resulting in holes in and around the clouds (3).

その後、すべてのパーティクルを全画面表示モードで再びレンダリングしましたが、今回はステンシルバッファーを使用してすべての辺をマスクしています。これにより、最小限のピクセルセット (4) が返されます。We then rendered all the particles again in full-screen mode, but this time used the stencil buffer to mask out everything but the edges, resulting in a minimal set of pixels touched (4). コマンドバッファーは既にパーティクル用に作成されているため、単に新しいカメラにレンダリングする必要がありました。Since the command buffer was already created for the particles, we simply had to render it again to the new camera.

レンダリングクラウドのエッジの進行状況

最終的な結果は、クラウドの安価なセンターセクションで鋭いエッジでした。The end result was sharp edges with cheap center sections of the clouds.

すべてのパーティクルを全画面表示でレンダリングするよりもはるかに高速ですが、ステンシルバッファーに対するピクセルのテストにもコストがかかります。そのため、大量のオーバースペースには依然としてコストがかかります。While this was much faster than rendering all particles in full screen, there is still a cost associated with testing a pixel against the stencil buffer, so a massive amount of overdraw still came with a cost.

パーティクルをカリングするCulling particles

この風の効果のために、コンピューティングシェーダーに長い三角形ストリップを生成して、世界中に多くの wisps を作成しました。For our wind effect, we generated long triangle strips in a compute shader, creating many wisps of wind in the world. Skinny ストリップが生成されたため、風の効果は塗りつぶしの速度に重いものではありませんが、数百から数千の頂点が生成され、頂点シェーダーの負荷が高くなります。While the wind effect was not heavy on fill rate due to skinny strips generated, it produced many hundreds of thousands of vertices resulting in a heavy load for the vertex shader.

ここでは、描画する風力ストリップのサブセットをフィードするために、コンピューティングシェーダーに追加バッファーを導入しました。We introduced append buffers on the compute shader to feed a subset of the wind strips to be drawn. コンピューティングシェーダー内の単純なビューでの視錐のあるロジックを使用すると、ストリップがカメラビューの外部にあり、そのストリップがプッシュバッファーに追加されないようにすることができます。With some simple view frustum culling logic in the compute shader, we could determine if a strip was outside of camera view and prevent it from being added to the push buffer. これにより、ストリップの量が大幅に削減され、GPU で必要なサイクルが解放されます。This reduced the amount of strips significantly, freeing up some needed cycles on the GPU.

追加バッファーを示すコード:Code demonstrating an append buffer:

計算シェーダー:Compute shader:

AppendStructuredBuffer<int> culledParticleIdx;
 
if (show)
    culledParticleIdx.Append(id.x);

C#コードC# code:

protected void Awake() 
{
    // Create an append buffer, setting the maximum size and the contents stride length
    culledParticlesIdxBuffer = new ComputeBuffer(ParticleCount, sizeof(int), ComputeBufferType.Append);
 
    // Set up Args Buffer for Draw Procedural Indirect
    argsBuffer = new ComputeBuffer(4, sizeof(int), ComputeBufferType.IndirectArguments);
    argsBuffer.SetData(new int[] { DataVertCount, 0, 0, 0 });
}
 
protected void Update()
{
    // Reset the append buffer, and dispatch the compute shader normally
    culledParticlesIdxBuffer.SetCounterValue(0);
 
    computer.Dispatch(...)
 
    // Copy the append buffer count into the args buffer used by the Draw Procedural Indirect call
    ComputeBuffer.CopyCount(culledParticlesIdxBuffer, argsBuffer, dstOffset: 1);
    ribbonRenderCommand.DrawProceduralIndirect(Matrix4x4.identity, renderMaterial, 0, MeshTopology.Triangles, dataBuffer);
}

クラウドのパーティクルで同じ手法を使用しようとしました。ここでは、コンピューティングシェーダーでそれらをカリングし、レンダリングされる表示されるパーティクルだけをプッシュします。We tried using the same technique on the cloud particles, where we would cull them on the compute shader and only push the visible particles to be rendered. この手法では、最大のボトルネックが画面にレンダリングされる量ピクセルであり、頂点を計算するコストではないため、GPU ではあまり多くのことを行いませんでした。This technique actually did not save us much on the GPU since the biggest bottleneck was the amount pixels rendered on the screen, and not the cost of calculating the vertices.

この手法のもう1つの問題は、パーティクルの計算が並列化されているため、追加バッファーにランダムな順序で値が設定され、並べ替えられたパーティクルが並べ替えられないため、クラウドパーティクルがちらつくことでした。The other problem with this technique was that the append buffer populated in random order due to its parallelized nature of computing the particles, causing the sorted particles to be un-sorted, resulting in flickering cloud particles.

プッシュバッファーを並べ替える方法はありますが、カリングを使用した場合に得られるパフォーマンスの上限は、追加の並べ替えによって相殺される可能性があるため、この最適化を追求しないことにしました。There are techniques to sort the push buffer, but the limited amount of performance gain we got out of culling particles would likely be offset with an additional sort, so we decided to not pursue this optimization.

アダプティブレンダリングAdaptive rendering

曇りと明瞭なビューのようなさまざまなレンダリング条件でアプリのフレームレートを安定させるには、アプリにアダプティブレンダリングを導入しました。To ensure a steady framerate on an app with varying rendering conditions like a cloudy vs a clear view, we introduced adaptive rendering to our app.

アダプティブレンダリングの最初の手順は、GPU を測定することです。The first step of adaptive rendering is to measure GPU. これを行うには、レンダリングされたフレームの先頭および末尾にある GPU コマンドバッファーにカスタムコードを挿入し、左右両方の画面時間をキャプチャします。We did this by inserting custom code into the GPU command buffer at the beginning and the end of a rendered frame, capturing both the left and right eye screen time.

レンダリングに費やされた時間を測定し、目的のリフレッシュレートと比較することにより、フレームを破棄する方法がわかりました。By measuring the time spent rendering and comparing it to our desired refresh-rate we got a sense of how close we were to dropping frames.

フレームのドロップに近づいたときに、レンダリングをより速くするように調整します。When close to dropping frames, we adapt our rendering to make it faster. 簡単に適応する方法の1つは、画面のビューポートのサイズを変更することです。これにより、レンダリングに必要なピクセル数が少なくなります。One simple way of adapting is changing the viewport size of the screen, requiring less pixels to get rendered.

Unityengine. XRを使用すると、対象のビューポートが縮小され、結果が画面に合わせて自動的に拡大されます。By using UnityEngine.XR.XRSettings.renderViewportScale the system shrinks the targeted viewport and automatically stretches the result back up to fit the screen. 小数点以下桁数の小さな変化は、ワールドジオメトリではほとんどわかりません。また、0.7 のスケールファクターでは、レンダリングするピクセルの量が半分になります。A small change in scale is barely noticeable on world geometry, and a scale factor of 0.7 requires half the amount of pixels to be rendered.

70% のスケール、ピクセルの半分

フレームを削除しようとしていることが検出された場合は、スケールを固定数だけ小さくし、実行している時間を十分に増やしてください。When we detect that we are about to drop frames we lower the scale by a fixed number, and increase it back when we are running fast enough again.

起動時にハードウェアのグラフィックス機能に基づいて使用するクラウド手法を決定しましたが、GPU 測定値のデータを基にして、システムの解像度が低くなり、時間がかかっていないことを防ぐことができます。 を参照してください。While we decided what cloud technique to use based on graphics capabilities of the hardware at startup, it is possible to base it on data from the GPU measurement to prevent the system from staying at low resolution for a long time, but this is something we did not have time to explore in Datascape.

最終的な考えFinal thoughts

さまざまなハードウェアをターゲットにすることは困難であり、いくつかの計画が必要です。Targeting a variety of hardware is challenging and requires some planning.

低電力のマシンを対象として、問題の領域を把握し、すべてのコンピューターで実行されるバックアップソリューションを開発することをお勧めします。We recommend that you start targeting lower-powered machines to get familiar with the problem space and develop a backup solution that will run on all your machines. ピクセルが最も貴重なリソースであるため、fill rate を念頭に置いてソリューションを設計します。Design your solution with fill rate in mind, since pixels will be your most precious resource. 透明度のあるターゲットのソリッドジオメトリ。Target solid geometry over transparency.

バックアップソリューションを使用すると、ハイエンドのコンピューターの複雑さをさらに高めることができます。または、バックアップソリューションの解決を強化することもできます。With a backup solution, you can then start layering in more complexity for high end machines or maybe just enhance resolution of your backup solution.

最悪のシナリオに対応するように設計されており、多くの場合、アダプティブレンダリングの使用を検討してください。Design for worst case scenarios, and maybe consider using adaptive rendering for heavy situations.

作成者についてAbout the authors

Picture of Robert Ferrese Robert は、Robert Ferrese
ソフトウェアエンジニア@MicrosoftSoftware engineer @Microsoft
Picture of Dan Andersson Dan AnderssonDan Andersson
ソフトウェアエンジニア@MicrosoftSoftware engineer @Microsoft

関連項目See also