Estudo de caso-dimensionamento de Datascape em dispositivos com desempenho diferenteCase study - Scaling Datascape across devices with different performance

O Datascape é um aplicativo de realidade mista do Windows desenvolvido internamente na Microsoft, no qual nos concentramos na exibição de dados meteorológicos sobre os dados do terreno.Datascape is a Windows Mixed Reality application developed internally at Microsoft where we focused on displaying weather data on top of terrain data. O aplicativo explora os usuários de insights exclusivos que aproveitam a descoberta de dados em realidade misturada ao redor do usuário com a visualização de dados do Holographic.The application explores the unique insights users gain from discovering data in mixed reality by surrounding the user with holographic data visualization.

Para o Datascape, queríamos ter como alvo uma variedade de plataformas com diferentes recursos de hardware, desde o Microsoft HoloLens até o Windows Mixed realness headsets e de PCs de baixo consumo até os computadores mais recentes com GPU de ponta.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. O principal desafio era renderizar nossa cena em uma questão visualmente atraente em dispositivos com recursos gráficos muito diferentes durante a execução em uma taxa de quadros alta.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.

Esse estudo de caso examinará o processo e as técnicas usadas para criar alguns de nossos sistemas mais intensivos de GPU, descrevendo os problemas encontrados e como superou-los.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.

Transparência e sobredesenhoTransparency and overdraw

Nosso principal esforço de renderização foi resolvido com transparência, pois a transparência pode ser cara em uma GPU.Our main rendering struggles dealt with transparency, since transparency can be expensive on a GPU.

A geometria sólida pode ser renderizada de frente para trás durante a gravação no buffer de profundidade, interrompendo os pixels futuros localizados atrás desse pixel de serem descartados.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. Isso impede que os pixels ocultos executem o sombreador de pixel, acelerando significativamente o processo.This prevents hidden pixels from executing the pixel shader, speeding up the process significantly. Se a geometria for classificada de forma ideal, cada pixel na tela será desenhado apenas uma vez.If geometry is sorted optimally, each pixel on the screen will be drawn only once.

A geometria transparente precisa ser classificada de volta para a frente e se baseia na mesclagem da saída do sombreador de pixel com o pixel atual na tela.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. Isso pode resultar em cada pixel na tela que está sendo desenhada várias vezes por quadro, chamado de sobreempate.This can result in each pixel on the screen being drawn to multiple times per frame, referred to as overdraw.

Para os computadores HoloLens e mainstream, a tela só pode ser preenchida algumas vezes, tornando o processo transparente de processamento.For HoloLens and mainstream PCs, the screen can only be filled a handful of times, making transparent rendering problematic.

Introdução aos componentes de cena do DatascapeIntroduction to Datascape scene components

Tínhamos três componentes principais para nossa cena; a interface do usuário, o mapa e o clima .We had three major components to our scene; the UI, the map , and the weather . Sabíamos no início que nossos efeitos meteorológicos exigiriam todo o tempo de GPU que poderia obter, então criamos de forma intencional a interface do usuário e o terreno de uma maneira que reduza qualquer sobreempate.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.

Retrabalhamos a interface do usuário várias vezes para minimizar a quantidade de sobreempates que ela produziria.We reworked the UI several times to minimize the amount of overdraw it would produce. Nós erramosmos o lado da geometria mais complexa em vez de sobrecarregar arte transparente sobre as outras para componentes como botões brilhantes e visões gerais de mapa.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.

Para o mapa, usamos um sombreador personalizado que distribuiva recursos padrão do Unity, como sombras e iluminação complexa, substituindo-os por um único modelo simples de iluminação de sol e um cálculo de neblina personalizado.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. Isso produziu um sombreador de pixel simples e libera ciclos de GPU.This produced a simple pixel shader and free up GPU cycles.

Nós gerenciamos para obter a interface do usuário e o mapa para renderizar no orçamento, onde não precisamos de nenhuma alteração para elas, dependendo do hardware; no entanto, a visualização do clima, em particular, a renderização na nuvem, provou ser mais um desafio!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!

Plano de fundo em dados de nuvemBackground on cloud data

Nossos dados de nuvem foram baixados de servidores NOAA ( https://nomads.ncep.noaa.gov/) e nos vieram em três camadas 2D distintas, cada uma com a altura superior e inferior da nuvem, bem como a densidade da nuvem para cada célula da grade.Our cloud data was downloaded from NOAA servers (https://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. Os dados foram processados em uma textura de informações de nuvem, em que cada componente foi armazenado no componente vermelho, verde e azul da textura para facilitar o acesso na 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.

Nuvens de geometriaGeometry clouds

Para garantir que nossos computadores com menor capacidade possam renderizar nossas nuvens, decidimos começar com uma abordagem que usaria uma geometria sólida para minimizar o sobreempate.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.

Primeiro, tentamos produzir nuvens gerando uma malha heightmap sólida para cada camada usando o raio da textura de informações de nuvem por vértice para gerar a forma.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. Usamos um sombreador Geometry para produzir os vértices na parte superior e a parte inferior da nuvem gerando formas de nuvem sólidas.We used a geometry shader to produce the vertices both at the top and the bottom of the cloud generating solid cloud shapes. Usamos o valor de densidade da textura para colorir a nuvem com cores mais escuras para nuvens mais densas.We used the density value from the texture to color the cloud with darker colors for more dense clouds.

Sombreador para criar os vértices: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);
}

Apresentamos um pequeno padrão de ruído para obter mais detalhes sobre os dados reais.We introduced a small noise pattern to get more detail on top of the real data. Para produzir bordas de nuvem arredondadas, cortamos os pixels no sombreador de pixel quando o valor de raio interpolado atinge um limite para descartar valores quase zero.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.

Nuvens de geometria

Como as nuvens são uma geometria sólida, elas podem ser renderizadas antes que o terreno oculte os pixels de mapa caros abaixo para melhorar ainda mais a taxa de quadros.Since the clouds are solid geometry, they can be rendered before the terrain to hide any expensive map pixels underneath to further improve framerate. Essa solução foi executada bem em todas as placas gráficas de min-spec a placas gráficas de ponta, bem como no HoloLens, devido à abordagem de renderização de geometria sólida.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.

Nuvens de partículas sólidasSolid particle clouds

Agora tivemos uma solução de backup que produzia uma representação razoável de nossos dados em nuvem, mas era um pouco Lackluster no fator "Wow" e não transmitiu a sensação de volumétricos que queríamos para nossos computadores de alto nível.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.

Nossa próxima etapa foi criar as nuvens, representando-as com aproximadamente 100.000 partículas para produzir uma aparência mais orgânica e volumétricos.Our next step was creating the clouds by representing them with approximately 100,000 particles to produce a more organic and volumetric look.

Se as partículas permanecerem sólidas e classificarem de volta para trás, ainda podemos nos beneficiar com a remoção do buffer de profundidade dos pixels por trás das partículas renderizadas anteriormente, reduzindo o sobreempate.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. Além disso, com uma solução baseada em partículas, podemos alterar a quantidade de partículas usadas para direcionar diferentes hardwares.Also, with a particle-based solution, we can alter the amount of particles used to target different hardware. No entanto, todos os pixels ainda precisam ser testados com profundidade, o que resulta em alguma sobrecarga adicional.However, all pixels still need to be depth tested, which results in some additional overhead.

Primeiro, criamos posições de partícula em todo o ponto central da experiência na inicialização.First, we created particle positions around the center point of the experience at startup. Distribuímos as partículas mais densas em todo o centro e menos para a distância.We distributed the particles more densely around the center and less so in the distance. Nós classificamos previamente todas as partículas do centro para trás para que as partículas mais próximas sejam renderizadas primeiro.We pre-sorted all particles from the center to the back so that the closest particles would render first.

Um sombreador de computação deve obter uma amostra da textura de informações de nuvem para posicionar cada partícula em uma altura correta e Colorá-la com base na densidade.A compute shader would sample the cloud info texture to position each particle at a correct height and color it based on the density.

Usamos DrawProcedural para renderizar um quad por partícula, permitindo que os dados de partícula permaneçam sempre na GPU.We used DrawProcedural to render a quad per particle allowing the particle data to stay on the GPU at all times.

Cada partícula continha uma altura e um raio.Each particle contained both a height and a radius. A altura foi baseada nos dados de nuvem amostrados da textura de informações de nuvem, e o raio era baseado na distribuição inicial em que seria calculado para armazenar a distância horizontal para o vizinho mais próximo.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. Os quatro processadores usariam esses dados para se orientarem em ângulo pela altura, de modo que, quando os usuários olharem isso horizontalmente, a altura seja mostrada e, quando os usuários olhassem de cima para baixo, a área entre seus vizinhos seria coberta.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.

Forma de partícula

Código do sombreador mostrando a distribuição: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;
}

Como classificamos as partículas front-to-back e ainda usamos um sombreador de estilo sólido para recortar (não misturar) pixels transparentes, essa técnica trata de uma quantidade surpreendente de partículas, evitando um dispendioso excesso de empates mesmo nos computadores com menor capacidade.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.

Nuvens de partículas transparentesTransparent particle clouds

As partículas sólidas forneciam uma boa sensação orgânica à forma das nuvens, mas ainda precisavam de algo para vender o fluffiness de nuvens.The solid particles provided a good organic feel to the shape of the clouds but still needed something to sell the fluffiness of clouds. Decidimos tentar uma solução personalizada para as placas gráficas de alto nível em que podemos introduzir transparência.We decided to try a custom solution for the high-end graphics cards where we can introduce transparency.

Para fazer isso, simplesmente alternamos a ordem de classificação inicial das partículas e alteramos o sombreador para usar as texturas alfa.To do this we simply switched the initial sorting order of the particles and changed the shader to use the textures alpha.

Nuvens Fluffys

Parece ótimo, mas provou ser muito pesado para até mesmo as máquinas mais difíceis, pois isso resultaria na renderização de cada pixel na tela centenas de vezes!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!

Renderizar fora da tela com resolução mais baixaRender off-screen with lower resolution

Para reduzir o número de pixels processados pelas nuvens, começamos a renderizá-los em um buffer de resolução de trimestre (em comparação à tela) e esticar o resultado final de volta para a tela depois que todas as partículas tivessem sido desenhadas.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. Isso nos deu aproximadamente um 4x aumento de velocidade, mas vem com algumas advertências.This gave us roughly a 4x speedup, but came with a couple of caveats.

Código para renderização fora da tela: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);

Primeiro, ao renderizar em um buffer fora da tela, perdemos todas as informações de profundidade de nossa cena principal, resultando em partículas por trás da renderização de montanhas sobre a montanha.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.

Em segundo lugar, o alongamento do buffer também introduziu artefatos nas bordas de nossas nuvens em que a alteração da resolução foi perceptível.Second, stretching the buffer also introduced artifacts on the edges of our clouds where the resolution change was noticeable. As próximas duas seções falam sobre como resolvemos esses problemas.The next two sections talk about how we resolved these issues.

Buffer de profundidade de partículaParticle depth buffer

Para fazer com que as partículas coexistam com a geometria mundial em que uma montanha ou um objeto possa cobrir partículas por trás dela, populamos o buffer fora da tela com um buffer de profundidade que contém a geometria da cena principal.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. Para produzir esse buffer de profundidade, criamos uma segunda câmera, Renderizando apenas a geometria sólida e a profundidade da cena.To produce such depth buffer, we created a second camera, rendering only the solid geometry and depth of the scene.

Em seguida, usamos a nova textura no sombreador de pixel das nuvens para occlude pixels.We then used the new texture in the pixel shader of the clouds to occlude pixels. Usamos a mesma textura para calcular a distância para a geometria por trás de um pixel de nuvem.We used the same texture to calculate the distance to the geometry behind a cloud pixel. Ao usar essa distância e aplicá-la ao alfa do pixel, agora tínhamos o efeito das nuvens desaparecendo à medida que elas ficam perto do terreno, removendo qualquer recorte fixo onde as partículas e o terreno se encontram.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.

Nuvens mescladas no terreno

Ajustando as bordasSharpening the edges

As nuvens ampliadas se parecevam quase idênticas às nuvens de tamanho normal no centro das partículas ou nas quais elas se sobrepostam, mas mostraram alguns artefatos nas bordas da nuvem.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. Caso contrário, as bordas nítidas aparecerão borradas e os efeitos de alias foram introduzidos quando a câmera fosse movida.Otherwise sharp edges would appear blurry and alias effects were introduced when the camera moved.

Resolvemos isso executando um sombreador simples no buffer fora da tela para determinar onde ocorreram grandes alterações (1).We solved this by running a simple shader on the off-screen buffer to determine where big changes in contrast occurred (1). Colocamos os pixels com grandes alterações em um novo buffer de estêncil (2).We put the pixels with big changes into a new stencil buffer (2). Em seguida, usamos o buffer de estêncil para mascarar essas áreas de alto contraste ao aplicar o buffer fora da tela de volta à tela, resultando em buracos em e em todas as nuvens (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).

Em seguida, renderizamos todas as partículas novamente no modo de tela inteira, mas desta vez usamos o buffer de estêncil para mascarar tudo, exceto as bordas, resultando em um conjunto mínimo de pixels tocadas (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). Como o buffer de comando já foi criado para as partículas, simplesmente tivemos que renderizá-lo novamente na nova câmera.Since the command buffer was already created for the particles, we simply had to render it again to the new camera.

Progressão do processamento de bordas de nuvem

O resultado final foi bordas nítidas com seções do centro barato das nuvens.The end result was sharp edges with cheap center sections of the clouds.

Embora isso fosse muito mais rápido do que renderizar todas as partículas em tela inteira, ainda há um custo associado ao teste de um pixel em relação ao buffer do estêncil, de modo que uma grande quantidade de sobreempates ainda veio com um custo.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.

Remoção de partículasCulling particles

Para nosso efeito de vento, geramos extensões de triângulo longos em um sombreador de computação, criando muitos WISPs de vento no mundo.For our wind effect, we generated long triangle strips in a compute shader, creating many wisps of wind in the world. Embora o efeito de vento não tenha sido pesado na taxa de preenchimento devido a faixas skinnis geradas, ele produziu muitas centenas de milhares de vértices, resultando em uma carga pesada para o sombreador de vértice.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.

Introduzimos buffers de acréscimo no sombreador de computação para alimentar um subconjunto das faixas de vento a serem desenhadas.We introduced append buffers on the compute shader to feed a subset of the wind strips to be drawn. Com alguns frustum de exibição simples de seleção de lógica no sombreador de computação, poderíamos determinar se uma tira estava fora do modo de exibição de câmera e impedir que ela fosse adicionada ao buffer de envio por push.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. Isso reduziu significativamente a quantidade de faixas, liberando alguns ciclos necessários na GPU.This reduced the amount of strips significantly, freeing up some needed cycles on the GPU.

Código que demonstra um buffer de acréscimo:Code demonstrating an append buffer:

Sombreador de computação:Compute shader:

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

Código 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);
}

Tentamos usar a mesma técnica nas partículas de nuvem, em que poderíamos alterá-las no sombreador de computação e apenas enviar por push as partículas visíveis a serem renderizadas.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. Na verdade, essa técnica não nos salvou muito na GPU, uma vez que o maior afunilamento era a quantidade de pixels renderizados na tela, e não o custo de calcular os vértices.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.

O outro problema dessa técnica era que o buffer de acréscimo foi populado em ordem aleatória devido à sua natureza paralelizada de computação das partículas, fazendo com que as partículas classificadas não sejam classificadas, resultando na cintilação de partículas de nuvem.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.

Há técnicas para classificar o buffer de push, mas a quantidade limitada de acertos de desempenho que acabamos de escolher partículas provavelmente seria compensada com uma classificação adicional, portanto, decidimos não buscar essa otimização.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.

Renderização adaptávelAdaptive rendering

Para garantir uma taxa de quadros constante em um aplicativo com condições de renderização diferentes, como uma nuvem versus uma exibição clara, introduzimos a renderização adaptável em nosso aplicativo.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.

A primeira etapa da renderização adaptável é medir a GPU.The first step of adaptive rendering is to measure GPU. Fizemos isso inserindo código personalizado no buffer de comando da GPU no início e no final de um quadro renderizado, capturando o horário da tela de olho à esquerda e à direita.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.

Medindo o tempo gasto renderizando e comparando-o à nossa taxa de atualização desejada, temos uma noção de como devemos descartar os quadros.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.

Ao fechar os quadros, adaptamos nossa renderização para torná-lo mais rápido.When close to dropping frames, we adapt our rendering to make it faster. Uma maneira simples de adaptar-se é alterar o tamanho do visor da tela, exigindo menos pixels para ser renderizado.One simple way of adapting is changing the viewport size of the screen, requiring less pixels to get rendered.

Usando UnityEngine. XR. XRSettings. renderViewportScale , o sistema reduz o visor de destino e alonga automaticamente o resultado de volta para o ajuste à tela.By using UnityEngine.XR.XRSettings.renderViewportScale the system shrinks the targeted viewport and automatically stretches the result back up to fit the screen. Uma pequena alteração na escala é mal perceptível na geometria mundial, e um fator de escala de 0,7 requer metade da quantidade de pixels a serem renderizados.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% de escala, metade dos pixels

Quando detectamos que estamos prestes a descartar os quadros, reduzimos a escala por um número fixo e o aumentamos quando estamos executando rápido o suficiente novamente.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.

Embora decidimos qual técnica de nuvem usar com base nos recursos gráficos do hardware na inicialização, é possível baseá-la nos dados da medição da GPU para impedir que o sistema permaneça em baixa resolução por um longo tempo, mas isso é algo que não temos tempo para explorar no Datascape.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.

Considerações finaisFinal thoughts

O direcionamento a uma variedade de hardware é desafiador e requer algum planejamento.Targeting a variety of hardware is challenging and requires some planning.

Recomendamos que você comece a direcionar computadores com menor capacidade para se familiarizar com o espaço do problema e desenvolver uma solução de backup que será executada em todos os seus computadores.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. Projete sua solução com a taxa de preenchimento em mente, pois os pixels serão seus recursos mais preciosos.Design your solution with fill rate in mind, since pixels will be your most precious resource. Direcionar a geometria sólida sobre transparência.Target solid geometry over transparency.

Com uma solução de backup, você pode iniciar a disposição em camadas em mais complexidade para máquinas de alto nível ou talvez apenas aprimorar a resolução da solução de backup.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 para cenários de pior caso e talvez considere o uso de processamento adaptável para situações pesadas.Design for worst case scenarios, and maybe consider using adaptive rendering for heavy situations.

Sobre os autoresAbout the authors

Picture of Robert Ferrese Robert FerreseRobert Ferrese
Engenheiro de software @MicrosoftSoftware engineer @Microsoft
Picture of Dan Andersson Dan AnderssonDan Andersson
Engenheiro de software @MicrosoftSoftware engineer @Microsoft

Consulte tambémSee also