获取分辨率函数是什么_运行时动态分辨率

本文介绍了动态分辨率优化,以Unreal为例讲解实现方案。其基本原理是根据GPU使用情况调整Viewport大小。实现中可通过Query工具获取GPU运行时间。Unreal在viewport操作上封装良好,有多种拷贝策略,还可重写相关类设计动态分辨率策略,并给出了代码索引。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

199b02afd073ce628839f21c5a03ab27.png

动机

在使用unity的时候,隔壁项目组做了一个优化,就是用不同的分辨率分辨渲染UI和场景,由于UI对分辨率的敏感性要更高。当然了,这种优化是非常普遍的,Unreal默认的行为就是这种,后面会介绍它的实现方案。我在我们自己的项目中也去做了这个优化,在实现的过程中,当时我在玩《马里奥奥德赛》,我能明显感觉到分辨率在运行时在动态改变。我在网上也查了一下,看到了这篇文章,也验证了我的感觉。随即就产生了,既然场景和UI已经分开了,那么更进一步,让场景能够在GPU瓶颈的时候动态改变来获得稳定的帧数。 当时我们使用的unity版本还比较老,是我自己实现的,后来unity自带了动态分辨率,但只对xbox开放,unreal现在也有实现,但只支持某些平台,现在的做法只需要改一些引擎的代码来支持我们需要的平台。由于现在我使用的是unreal了,所以,实现的介绍以unreal的为例。

动态分辨率的基本原理

基本原理很简单,就是在渲染场景的时候,根据GPU的使用情况,调整Viewport的大小,然后再把场景的rendertarget上的内容blit到最终的rt上。

3531e73424a8be70d48f2ee7de4bce1f.png

当然,过程中一般是sub-sampling,你也可以让RT大于backbuffer的大小,这样可以super-sampling。

Query

在实现动态分辨率的中,很总要的一个点是获取GPU的运行时间,各个图形API都提供了叫Query的工具,用来获得GPU的时间。以vulkan为例,它以commands为单位计时,将结果保存到一个query pool里面,然后通过接口可以获取到结果,结果是一个timestamp,将结果相减可以得到时间。

Unreal的实现

在介绍Unreal的实现之前,先简单介绍一下unreal对于渲染分辨率viewport相关的一些东西。对于每一个scene render都有一个view的数组,也就是这个场景可能被渲染了多少次,在view中有个viewrect,就是存着viewport的信息,在每次draw的时候,就会使用这个数据设置viewport。动态分辨率就是,如何去设置这个viewport的策略之一。这是第一步,后面还需要把绘制的内容画到最终的RT上,过程就是上面提到的那样,把绘制的RT中viewport大小的像素采样拷贝到最终的RT上。 对于什么时候拷贝,Unreal有几个策略。之前提到的一个策略,就是把UI和场景的分辨率分开,unreal叫spatial upscale,过程如下图:

1abce4a6596c35acfea17e5f2240e852.png

拷贝(后面所说的拷贝都包含采样过程)发生在UI渲染之前。还有一个策略叫 Temporal Upsample,拷贝的时机是发生在做TAA之前,或者说是在TAA发生的时候同时做了upscale和TAA,这样是为了提升TAA的质量,当然也增加了TAA及之后后处理的消耗。

55725a62e56bb0172ad094495b4da2d0.png

在此基础上unreal更进一步,组合上面的两个让UI在更高的分辨率渲染,unreal叫secondary spatial upscale。

5a9d5899f098ac4982bda53225455a20.png

介绍这些的是为了说明,unreal在viewport操作上已经封装的很好,被广泛使用,而且动态分辨率是在这个基础上对分辨率的更改。 在以上的方案里面,动态分辨率是作用在view geometry上的,也就是在第一阶段的分辨率下作用的。在第一阶段的分辨率控制里,也就是scene的渲染中,有ISceneViewFamilyScreenPercentage的接口负责对分辨率的计算,动态分辨率是其中的一个实现,包括有FLegacyScreenPercentageDriverFDefaultDynamicResolutionDriverISceneViewFamilyScreenPercentage(Oculus)。默认情况下也有FLegacyScreenPercentageDriver来处理viewport的大小,主要处理可能后处理需要的对分辨率的修改。在渲染开始的时候,render函数里面会调用PrepareViewRectsForRendering方法来设置viewport,方法里面就会调用ISceneViewFamilyScreenPercentageComputePrimaryResolutionFractions_RenderThread方法来获得我们设置的viewport的大小。动态分辨率是在渲染之前计算好的,ComputePrimaryResolutionFractions_RenderThread只是取出来。 计算过程是这样的,首先需要N帧的GPU时间和CPU的game和render线程的时间,需要CPU时间是为了排除有可能是CPU的瓶颈导致了帧率下降,这个时候即便降低分辨率,对整体帧率也不会有影响。在排除CPU的瓶颈的可能下,对历史的记录每帧计算,unreal是假定GPU的时间正比于分辨率缩放的平方,

SuggestedResolutionFraction = (
                    FMath::Sqrt(TargetedGPUBusyTimeMs / FrameEntry.TotalFrameGPUBusyTimeMs)
                    * FrameEntry.ResolutionFraction);

简单就是这样计算的,然后对计算后的数值乘上权重,权重是随着计算帧离当前帧的距离,等比递减,然后再加权平均一下,得到最终的分辨率。 Unreal将策略封装在了一个FDynamicResolutionHeuristicProxy中,我们可以自己重写这个类来设计属于自己的动态分辨率的策略。我在使用过程中,用unreal自带的策略结果已经可以了,当然随着项目的需求改变,或许就不能满足了。 最近看到有同学说viewport是rendertarget,怕是对viewport产生了很大的误会,viewport只是需要跟一个backbuffer rendertarget做绑定罢了。

Unreal代码索引: 设置分辨率调整接口SetScreenPercentageInterface(GameViewportClient.cpp->UGameViewportClient::Draw) 设置视口大小(SceneRendering.cpp->FSceneRenderer::PrepareViewRectsForRendering) 视口大小计算(DynamicResolution.cpp->FDynamicResolutionHeuristicProxy::RefreshCurentFrameResolutionFraction_RenderThread)

引用:

https://docs.unrealengine.com/en-US/Engine/Rendering/DynamicResolution/index.html

https://docs.unrealengine.com/en-US/Engine/Rendering/ScreenPercentage/index.html#temporalanti-aliasingupsample

https://nintendoeverything.com/super-mario-odyssey-full-tech-analysis/

https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/chap17.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值