나 이나用法

나/이나的用法

(1)“나/이나”表示让步选择,或者最次的选择。有时还有虽然是最好的,但是语气上好像不满足的样态。“나”用在开音节的体词或状语后,“이나”用在闭音节的体词或状语后。

如:
우리는 참견말고 굿이나 보다 떡이나 먹자.
咱们别参与什么了,看看热闹等结果吧。

그것이나 가져라.
就拿那个吧。

아무것이나 가져 와라.
随便拿一个过来。

(2)“나/이나”表示好像真事一样,是最好的选择。此时有嗤之以鼻的意思。

如:
가보기나 한 것처럼 말하네.
说的好像是去过一样。

그는 일 등이나 한 것처럼 우쭐댄다
他好像得了第一名似的卖弄。

돈이나 주었나? 그렇게 기뻐하게.
捡到了钱是怎地?有什么那么高兴。

(3)“나/이나”用在表示数量或程度的体词或状语后,表示强调多,大,高。

如:
그는 악기라면 무엇이나 다 할 줄 안대요.
听说只要是乐器他都会。

그는 외국어를 여섯 나라나 할 줄 안다.
他会六国语言。

이렇게 많은 사람이 올 줄이야 상상도 못했어요
真没有想到来了这么多人。

(4)“나/이나”用在表示数量或程度的体词或状语后,表示猜测。

如:
몇 시나 됬을까?
现在到几点了呢?

그 여자애는 열 살이나 될까?
那个女孩有十岁?

몇이나 될까?
能有几个人呢?

(5)“나/이나”表示选择。

如:
너나 네 동생이나 한 사람만 오면 돼.
你或者你弟弟中只要来一个就行

아빠나 엄마한테 휴대폰 사 달라고 해.
让爸爸或妈妈给你买手机。

출근 때 버스나 택시를 타고 가요
上班的时,乘公交车或出租车。


14.든지的用法

“든지”用在开音节的体词后,表示选择。

如:
누가 오든지 한 사람만 오라고 하세요
不管是谁让来一个。

밥이든지 빵이든지 먹을 거리면 된다.
管它是饭,还是面包,是吃的就行。

오든지 말든지.
爱来不来。


15.커녕的用法

(1)“커녕” 用在体词后,表示姑且舍弃,相当于汉语的“不用说”、“别说”。

如:
밥은커녕 죽도 못 먹겠다
别说是饭,连粥都吃不起。

비는커녕 구름 한 점 없다
别说是下雨,连一片云彩都没有。

오기는커녕 소식도 없어요
别说是来,连个消息都没有。

(2)“커녕”表示“不仅不……,反而……” 的意思。

如:
칭찬은커녕 욕만 먹었다
不仅没有得到表扬,反而好一顿责骂。

찬성은커녕 반대를 받았다.
不仅没有得到赞成,反而遭到反对。

장사해서 벌기는커녕 밑지고 말았다
做生意不仅没有赚到钱,反而赔钱了。

문제 설명 당신은 비밀 조직의 보안 시스템을 뚫고 중요한 정보를 해독해야 합니다. 시스템은 1부터 까지의 서로 다른 정수 5개가 오름차순으로 정렬된 비밀 코드를 가지고 있으며, 당신은 이 비밀 코드를 맞혀야 합니다. n 당신은 비밀 코드를 알아내기 위해 암호 분석 도구를 사용하며, 번의 시도를 할 수 있습니다. 각 시도마다 서로 다른 5개의 정수를 입력하면, 시스템은 그 중 몇 개가 비밀 코드에 포함되어 있는지 알려줍니다.m 만약 비밀 코드가 [3, 5, 7, 9, 10]이고, 입력한 정수가 [1, 2, 3, 4, 5]라면 비밀 코드에 포함된 정수는 3, 5 두 개이므로 시스템은 2를 응답합니다. 당신은 번의 시도 후, 비밀 코드로 가능한 정수 조합의 개수를 알고 싶습니다.m 비밀 코드에 사용된 정수의 범위가 1~10일 때, 아래와 같이 5번의 시도를 했다고 가정해 보겠습니다. 입력한 정수 시스템 응답(일치하는 개수) [1, 2, 3, 4, 5] 2개 [6, 7, 8, 9, 10] 3개 [3, 7, 8, 9, 10] 4개 [2, 5, 7, 9, 10] 3개 [3, 4, 5, 6, 7] 3개 비밀 코드로 가능한 정수 조합은 아래와 같이 3개가 있습니다. [3, 4, 7, 9, 10] 첫 번째 시도에서 비밀 코드에 포함된 정수가 3, 4로 2개 있습니다. 두 번째 시도에서 비밀 코드에 포함된 정수가 7, 9, 10으로 3개 있습니다. 세 번째 시도에서 비밀 코드에 포함된 정수가 3, 7, 9, 10으로 4개 있습니다. 네 번째 시도에서 비밀 코드에 포함된 정수가 7, 9, 10으로 3개 있습니다. 다섯 번째 시도에서 비밀 코드에 포함된 정수가 3, 4, 7로 3개 있습니다. [3, 5, 7, 8, 9] 첫 번째 시도에서 비밀 코드에 포함된 정수가 3, 5로 2개 있습니다. 두 번째 시도에서 비밀 코드에 포함된 정수가 7, 8, 9로 3개 있습니다. 세 번째 시도에서 비밀 코드에 포함된 정수가 3, 7, 8, 9로 4개 있습니다. 네 번째 시도에서 비밀 코드에 포함된 정수가 5, 7, 9로 3개 있습니다. 다섯 번째 시도에서 비밀 코드에 포함된 정수가 3, 5, 7로 3개 있습니다. [3, 5, 7, 8, 10] 첫 번째 시도에서 비밀 코드에 포함된 정수가 3, 5로 2개 있습니다. 두 번째 시도에서 비밀 코드에 포함된 정수가 7, 8, 10으로 3개 있습니다. 세 번째 시도에서 비밀 코드에 포함된 정수가 3, 7, 8, 10으로 4개 있습니다. 네 번째 시도에서 비밀 코드에 포함된 정수가 5, 7, 10으로 3개 있습니다. 다섯 번째 시도에서 비밀 코드에 포함된 정수가 3, 5, 7로 3개 있습니다. 정수 , 입력한 정수를 담은 2차원 정수 배열 와 시스템 응답을 담은 1차원 정수 배열 가 매개변수로 주어집니다. 이때, 비밀 코드로 가능한 정수 조합 개수를 return 하도록 solution 함수를 완성해 주세요.nqans 제한사항 10 ≤ ≤ 30n 1 ≤ (의 길이 = ) ≤ 10 qm q[i]의 길이 = 5 q[i]는 번째 시도에서 입력한 5개의 서로 다른 정수를 담고 있으며, 오름차순으로 정렬되어 있습니다.i+1 1 ≤ ≤ q[i][j]n ans의 길이 = m ans[i]는 번째 시도에서 입력한 5개의 정수 중 비밀 코드에 포함된 정수의 개수를 타냅니다.i+1 0 ≤ ≤ 5ans[i] 비밀 코드가 존재하지 않는(답이 0인) 경우는 주어지지 않습니다. 테스트 케이스 구성 안내 아래는 테스트 케이스 구성을 타냅니다. 각 그룹 내의 테스트 케이스를 모두 통과하면 해당 그룹에 할당된 점수를 획득할 수 있습니다. 그룹 총점 추가 제한 사항 #1 20% m = 1 #2 80% 추가 제한 사항 없음 입출력 예 n q ans result 10 [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [3, 7, 8, 9, 10], [2, 5, 7, 9, 10], [3, 4, 5, 6, 7]] [2, 3, 4, 3, 3] 3 15 [[2, 3, 9, 12, 13], [1, 4, 6, 7, 9], [1, 2, 8, 10, 12], [6, 7, 11, 13, 15], [1, 4, 10, 11, 14]] [2, 1, 3, 0, 1] 5 입출력 예 설명 입출력 예 #1 문제 예시와 같습니다. 입출력 예 #2 비밀 코드로 가능한 정수 조합은 아래와 같이 5개가 있습니다. [1, 2, 3, 5, 8] [1, 3, 5, 8, 12] [2, 4, 5, 8, 12] [2, 5, 8, 9, 10] [5, 8, 9, 10, 12] 따라서 5를 return 해야 합니다. 用中文解答,用C语言编码
最新发布
08-15
解析下面这段代码,给出代码结构图和流程图 做工作 我们分析一下DoWork()函数,它是Streaming Mip计算的核心部分。 void FRenderAssetStreamingMipCalcTask::DoWork() { // 비동기 작업이 실행되는 동안 스트리밍 렌더 에셋은 재할당되지 않도록 보장됩니다. // 두 가지 일이 발생할 수 있습니다: // 1. 텍스처가 제거될 수 있으며, // 2. 이 경우 텍스처가 null이 되거 UpdateDynamicData()를 호출한 후 일부 멤버가 업데이트될 수 있습니다. // 모든 Streaming Mesh, Texture object 가져오기. TArray<FStreamingRenderAsset>& StreamingRenderAssets = StreamingManager.AsyncUnsafeStreamingRenderAssets; const FRenderAssetStreamingSettings& Settings = StreamingManager.Settings; // StreamingData.ComputeViewInfoExtras(Settings); // Update the distance and size for each bounds. StreamingData.UpdateBoundSizes_Async(Settings); ApplyPakStateChanges_Async(); for (FStreamingRenderAsset& StreamingRenderAsset : StreamingRenderAssets) { if (IsAborted()) break; StreamingRenderAsset.UpdateOptionalMipsState_Async(); StreamingData.UpdatePerfectWantedMips_Async(StreamingRenderAsset, Settings); StreamingRenderAsset.DynamicBoostFactor = 1.f; // Reset after every computation. } // According to budget, make relevant sacrifices and keep possible unwanted mips UpdateBudgetedMips_Async(); // Update load requests. UpdateLoadAndCancelationRequests_Async(); // Update bHasStreamingUpdatePending UpdatePendingStreamingStatus_Async(); StreamingData.OnTaskDone_Async(); #if STATS UpdateStats_Async(); #elif UE_BUILD_TEST UpdateCSVOnlyStats_Async(); #endif // STATS } FAsyncRenderAssetStreamingData::UpdateBoundSizes_Async 我们需要知道相机和物体之间的距离,因为相机离物体越远,所需的 mipmap 层就越少。 UpdateBoundSizes_Async 函数计算视图中渲染对象的边界大小。然后,该信息将根据视口大小确定对象的渲染优先级。 ▼ StaticInstanceView 和 DynamicInstanceView? 关闭 StaticInstancesViews 和 DynamicInstancesViews 代表两种主要类型的渲染资产实例。 StaticInstancesViews :表示静态渲染资源实例列表。这些实例是固定的,在游戏关卡中不会发生变化。通常,静态实例是放置在关卡中的对象,例如地板、墙壁、建筑物等。 DynamicInstancesView :表示动态渲染资源实例。这些实例可以在游戏过程中发生变化。 例如,移动的角色、车辆、基于物理的物体等。 InstancesView? 实例视图是一个包含渲染资产实例信息的类。 每个视图都包含用于计算渲染资产屏幕尺寸的数据和方法。 此类提供的信息有助于确定渲染资产的细节级别 (LOD)。每个实例视图都存储对特定渲染资产的引用,提供计算该渲染资产屏幕尺寸的函数,确定是否存在强制 LOD 等。此信息用于确定渲染资产所需的 MIP 级别。 ▼功能齐全 void FAsyncRenderAssetStreamingData::UpdateBoundSizes_Async(const FRenderAssetStreamingSettings& Settings) { for (int32 StaticViewIndex = 0; StaticViewIndex < StaticInstancesViews.Num(); ++StaticViewIndex) { FRenderAssetInstanceAsyncView& StaticInstancesView = StaticInstancesViews[StaticViewIndex]; StaticInstancesView.UpdateBoundSizes_Async(ViewInfos, ViewInfoExtras, LastUpdateTime, Settings); // Skip levels that can not contribute to resolution. if (StaticInstancesView.GetMaxLevelRenderAssetScreenSize() > Settings.MinLevelRenderAssetScreenSize || StaticInstancesView.HasAnyComponentWithForcedLOD()) { StaticInstancesViewIndices.Add(StaticViewIndex); } else { CulledStaticInstancesViewIndices.Add(StaticViewIndex); } } // Sort by max possible size, this allows early exit when iteration on many levels. if (Settings.MinLevelRenderAssetScreenSize > 0) { StaticInstancesViewIndices.Sort([&](int32 LHS, int32 RHS) { return StaticInstancesViews[LHS].GetMaxLevelRenderAssetScreenSize() > StaticInstancesViews[RHS].GetMaxLevelRenderAssetScreenSize(); }); } DynamicInstancesView.UpdateBoundSizes_Async(ViewInfos, ViewInfoExtras, LastUpdateTime, Settings); } FRenderAssetInstanceAsyncView::UpdateBoundSizes_Async :该函数更新渲染资源实例视图的边界体积信息。(异步) 为了快速计算,我们使用 SIMD 同时计算 ViewPoint 和四个 AABB BoundingBox。 ▼ 渲染资产实例视图中的边界? 关闭 渲染资源实例视图的边界体积,考虑到足够的精度和计算效率,一般以AABB(Axis-Aligned Bounding Box)或者Bounding Sphere的形式来表示,并且包含Transform/Rotate/Scaling信息。 引擎使用此边界体积进行视锥体剔除、LOD 确定和碰撞检测,以更有效地管理渲染过程。 总结一下,FRenderAssetInstanceAsyncView::UpdateBoundSizes_Async函数如下: 1. 获取 FRenderAssetInstanceView 的 Bounds4,以获取待渲染对象的边界体积 (Bounding Volume)。Bounds4 是一个结构体,用于一次性存储四个独立渲染资源实例 (Rendering Asset Instance) 的边界体积 (Bounding Volume)。这样存储四个实例的原因是为了使用 SIMD 指令执行操作。2 . 遍历所有 Bounds 并执行操作。2-1 ) 获取视图 (View) 和边界 (Bound) 之间距离的平方。(不包括 Extent 值) 2-2) 将距离的平方限制在 MinDistanceSq、MinRangeSq ~ MaxRangeSq 范围内。 ▼ 检查 MinRangeSq、MaxRangeSq 值的含义 2-3) 求相应值的倒数平方根,然后将其乘以 ScreenSize。 ------------> 则 ScreenSizeOverDistance = ScreenSize / Sqrt(DistSqMinusRadiusSq), 该值会是 ScreenSizeOverDistance = ScreenSize / DistanceMinusRadius。 随着距离的增加,它会变小;随着 ScreenSize 的增加,它会变大,因此可以方便地用于 Mip 计算。2-4 ) ViewMaxNormalizedSize 计算所有流的最大标准化大小。 计算出来的ScreenSizeOverDistance放在 BoundsViewInfo[Index] .MaxNormalizedSize中。 处理完物体不在边界内或者不是最近渲染的物体的情况后,将其放入BoundsVieWInfo[Index].MaxNormalizedSize_VisibleOnly中。 2-5) 当Bound Loop完成后,将所有Rendering Asset Instance中最大的一个乘以MaxTexelFactor。 将其添加到MaxLevelRenderAssetScreenSize后,退出。 3.将计算出来的值插入到你传入的BoundsViewInfo中。 ▼开启所有功能 关闭 void FRenderAssetInstanceAsyncView::UpdateBoundSizes_Async( const TArray<FStreamingViewInfo>& ViewInfos, const FStreamingViewInfoExtraArray& ViewInfoExtras, float LastUpdateTime, const FRenderAssetStreamingSettings& Settings) { check(ViewInfos.Num() == ViewInfoExtras.Num()); if (!View.IsValid()) return; const int32 NumViews = ViewInfos.Num(); const int32 NumBounds4 = View->NumBounds4(); const VectorRegister LastUpdateTime4 = VectorSet(LastUpdateTime, LastUpdateTime, LastUpdateTime, LastUpdateTime); BoundsViewInfo.Empty(NumBounds4 * 4); BoundsViewInfo.AddUninitialized(NumBounds4 * 4); // 모든 element의 최대 nomalize된 크기 VectorRegister ViewMaxNormalizedSize = VectorZero(); for (int32 Bounds4Index = 0; Bounds4Index < NumBounds4; ++Bounds4Index) { const FRenderAssetInstanceView::FBounds4& CurrentBounds4 = View->GetBounds4(Bounds4Index); // LWC_TODO - 원점 값은 double에서 로드하고 머지 값은 float에서 로드합니다. // 이러한 연산 중 일부를 float VectorRegisters로 수행할 수 있으며, 이는 잠재적으로 더 효율적일 수 있습니다. // (그렇지 않으면 로드 시 이 값들을 더블 VectorRegisters로 변환하는 데 비용이 듭니다.) // 대형 월드의 경우 오브젝트와 뷰 원점 사이의 거리가 float 용량을 초과할 수 있으므로 정밀도 관리가 까다롭습니다. // viewer에서 bounding sphere 까지의 거리를 계산합니다. const VectorRegister OriginX = VectorLoadAligned( &CurrentBounds4.OriginX ); const VectorRegister OriginY = VectorLoadAligned( &CurrentBounds4.OriginY ); const VectorRegister OriginZ = VectorLoadAligned( &CurrentBounds4.OriginZ ); const VectorRegister RangeOriginX = VectorLoadAligned( &CurrentBounds4.RangeOriginX ); const VectorRegister RangeOriginY = VectorLoadAligned( &CurrentBounds4.RangeOriginY ); const VectorRegister RangeOriginZ = VectorLoadAligned( &CurrentBounds4.RangeOriginZ ); const VectorRegister ExtentX = VectorLoadAligned( &CurrentBounds4.ExtentX ); const VectorRegister ExtentY = VectorLoadAligned( &CurrentBounds4.ExtentY ); const VectorRegister ExtentZ = VectorLoadAligned( &CurrentBounds4.ExtentZ ); const VectorRegister ComponentScale = VectorLoadAligned( &CurrentBounds4.RadiusOrComponentScale ); const VectorRegister PackedRelativeBox = VectorLoadAligned( reinterpret_cast<const FVector4f*>(&CurrentBounds4.PackedRelativeBox) ); const VectorRegister MinDistanceSq = VectorLoadAligned( &CurrentBounds4.MinDistanceSq ); const VectorRegister MinRangeSq = VectorLoadAligned( &CurrentBounds4.MinRangeSq ); const VectorRegister MaxRangeSq = VectorLoadAligned(&CurrentBounds4.MaxRangeSq); const VectorRegister LastRenderTime = VectorLoadAligned(&CurrentBounds4.LastRenderTime); VectorRegister MaxNormalizedSize = VectorZero(); VectorRegister MaxNormalizedSize_VisibleOnly = VectorZero(); for (int32 ViewIndex = 0; ViewIndex < NumViews; ++ViewIndex) { const FStreamingViewInfo& ViewInfo = ViewInfos[ViewIndex]; const FStreamingViewInfoExtra& ViewInfoExtra = ViewInfoExtras[ViewIndex]; const VectorRegister ScreenSize = VectorLoadFloat1( &ViewInfoExtra.ScreenSizeFloat ); const VectorRegister ExtraBoostForVisiblePrimitive = VectorLoadFloat1( &ViewInfoExtra.ExtraBoostForVisiblePrimitiveFloat ); const VectorRegister ViewOriginX = VectorLoadFloat1( &ViewInfo.ViewOrigin.X ); const VectorRegister ViewOriginY = VectorLoadFloat1( &ViewInfo.ViewOrigin.Y ); const VectorRegister ViewOriginZ = VectorLoadFloat1( &ViewInfo.ViewOrigin.Z ); VectorRegister DistSqMinusRadiusSq = VectorZero(); if (Settings.bUseNewMetrics) { // Settings.bUseNewMetrics가 True인 경우는 Extent 값은 제외하고 거리를 계산해줍니다. // = 바운딩박스 크기를 고려해서 계산한다는뜻 ~ // ViewOrigin으로부터 Box까지의 거리를 계산하는것입니다. VectorRegister Temp = VectorSubtract( ViewOriginX, OriginX ); Temp = VectorAbs( Temp ); VectorRegister BoxRef = VectorMin( Temp, ExtentX ); Temp = VectorSubtract( Temp, BoxRef ); DistSqMinusRadiusSq = VectorMultiply( Temp, Temp ); Temp = VectorSubtract( ViewOriginY, OriginY ); Temp = VectorAbs( Temp ); BoxRef = VectorMin( Temp, ExtentY ); Temp = VectorSubtract( Temp, BoxRef ); DistSqMinusRadiusSq = VectorMultiplyAdd( Temp, Temp, DistSqMinusRadiusSq ); Temp = VectorSubtract( ViewOriginZ, OriginZ ); Temp = VectorAbs( Temp ); BoxRef = VectorMin( Temp, ExtentZ ); Temp = VectorSubtract( Temp, BoxRef ); DistSqMinusRadiusSq = VectorMultiplyAdd( Temp, Temp, DistSqMinusRadiusSq ); } else { // 여기는 바운딩박스 크기를 고려하지 않고 ViewOrigin부터 BOX Origin까지의 거리 계산 VectorRegister Temp = VectorSubtract( ViewOriginX, OriginX ); VectorRegister DistSq = VectorMultiply( Temp, Temp ); Temp = VectorSubtract( ViewOriginY, OriginY ); DistSq = VectorMultiplyAdd( Temp, Temp, DistSq ); Temp = VectorSubtract( ViewOriginZ, OriginZ ); DistSq = VectorMultiplyAdd( Temp, Temp, DistSq ); DistSqMinusRadiusSq = VectorNegateMultiplyAdd( ExtentX, ExtentX, DistSq ); DistSqMinusRadiusSq = VectorNegateMultiplyAdd( ExtentY, ExtentY, DistSq ); DistSqMinusRadiusSq = VectorNegateMultiplyAdd( ExtentZ, ExtentZ, DistSq ); // This can be negative here!!! } // bound가 가까이서 보이지 않는 경우 가능한 최소 범위로 거리를 제한합니다. VectorRegister ClampedDistSq = VectorMax( MinDistanceSq, DistSqMinusRadiusSq ); // FBounds4.Origin는AABB 중심점 값입니다. // 이 값은 asset의 위치 정보를 저장하는 용도로 사용됩니다. // FBounds4.RangeOrigin는 AABB의 각 축들의 값 중 가장 작은 값(최소 범위)을 타냅니다. VectorRegister InRangeMask; { VectorRegister Temp = VectorSubtract( ViewOriginX, RangeOriginX ); VectorRegister RangeDistSq = VectorMultiply( Temp, Temp ); Temp = VectorSubtract( ViewOriginY, RangeOriginY ); RangeDistSq = VectorMultiplyAdd( Temp, Temp, RangeDistSq ); Temp = VectorSubtract( ViewOriginZ, RangeOriginZ ); RangeDistSq = VectorMultiplyAdd( Temp, Temp, RangeDistSq ); VectorRegister ClampedRangeDistSq = VectorMax( MinRangeSq, RangeDistSq ); ClampedRangeDistSq = VectorMin( MaxRangeSq, ClampedRangeDistSq ); InRangeMask = VectorCompareEQ( RangeDistSq, ClampedRangeDistSq); // If the clamp dist is equal, then it was in range. } ClampedDistSq = VectorMax(ClampedDistSq, VectorOne()); // Prevents / 0 VectorRegister ScreenSizeOverDistance = VectorReciprocalSqrt(ClampedDistSq); ScreenSizeOverDistance = VectorMultiply(ScreenSizeOverDistance, ScreenSize); MaxNormalizedSize = VectorMax(ScreenSizeOverDistance, MaxNormalizedSize); // 모든 뷰의 최대값을 누적합니다. PackedRelativeBox가 0이면 해당 항목은 유효하지 않으며 최대값에 영향을 미치지 않아야 합니다. const VectorRegister CulledMaxNormalizedSize = VectorSelect(VectorCompareNE(PackedRelativeBox, VectorZero()), MaxNormalizedSize, VectorZero()); ViewMaxNormalizedSize = VectorMax(ViewMaxNormalizedSize, CulledMaxNormalizedSize); // 범위 내에 있지 않거 최근에 본 적이 없는 경우 마스크를 0으로 설정합니다. ScreenSizeOverDistance = VectorMultiply(ScreenSizeOverDistance, ExtraBoostForVisiblePrimitive); ScreenSizeOverDistance = VectorSelect(InRangeMask, ScreenSizeOverDistance, VectorZero()); ScreenSizeOverDistance = VectorSelect(VectorCompareGT(LastRenderTime, LastUpdateTime4), ScreenSizeOverDistance, VectorZero()); MaxNormalizedSize_VisibleOnly = VectorMax(ScreenSizeOverDistance, MaxNormalizedSize_VisibleOnly); } // Store results FBoundsViewInfo* BoundsVieWInfo = &BoundsViewInfo[Bounds4Index * 4]; MS_ALIGN(16) float MaxNormalizedSizeScalar[4] GCC_ALIGN(16); VectorStoreAligned(MaxNormalizedSize, MaxNormalizedSizeScalar); MS_ALIGN(16) float MaxNormalizedSize_VisibleOnlyScalar[4] GCC_ALIGN(16); VectorStoreAligned(MaxNormalizedSize_VisibleOnly, MaxNormalizedSize_VisibleOnlyScalar); MS_ALIGN(16) float ComponentScaleScalar[4] GCC_ALIGN(16); VectorStoreAligned(ComponentScale, ComponentScaleScalar); for (int32 SubIndex = 0; SubIndex < 4; ++SubIndex) { BoundsVieWInfo[SubIndex].MaxNormalizedSize = MaxNormalizedSizeScalar[SubIndex]; BoundsVieWInfo[SubIndex].MaxNormalizedSize_VisibleOnly = MaxNormalizedSize_VisibleOnlyScalar[SubIndex]; BoundsVieWInfo[SubIndex].ComponentScale = ComponentScaleScalar[SubIndex]; } } if (Settings.MinLevelRenderAssetScreenSize > 0) { float ViewMaxNormalizedSizeResult = VectorGetComponent(ViewMaxNormalizedSize, 0); MS_ALIGN(16) float ViewMaxNormalizedSizeScalar[4] GCC_ALIGN(16); VectorStoreAligned(ViewMaxNormalizedSize, ViewMaxNormalizedSizeScalar); for (int32 SubIndex = 1; SubIndex < 4; ++SubIndex) { ViewMaxNormalizedSizeResult = FMath::Max(ViewMaxNormalizedSizeResult, ViewMaxNormalizedSizeScalar[SubIndex]); } MaxLevelRenderAssetScreenSize = View->GetMaxTexelFactor() * ViewMaxNormalizedSizeResult; } } FAsyncRenderAssetStreamingData::UpdatePerfectWantedMips_Async 这个函数主要作用是计算Rendering Asset的Max Size、Max Visible Size,并以此确定PerfectWantedMip。 通过几个条件判断和设置值,FRenderAssetInstanceAsyncView::GetRenderAssetScreenSize 函数,我们获取了与渲染资源最大尺寸相关的值(MaxSize、MaxSize_VisibleOnly)。这取决于渲染资源允许的最大尺寸(与分辨率大小成比例)、是否执行压力测试、是否完全使用纹理加载、是否按级别计算 MIP 等。根据计算出的最大尺寸和最大可见尺寸,调用 FAsyncRenderAssetStreamingData::GetRenderAssetScreenSiz 函数设置所需的 MIP 级别。 { DynamicInstancesView.GetRenderAssetScreenSize 静态实例视图.获取渲染资源屏幕大小 StreamingRenderAsset.SetPerfectWantedMips_Async } FRenderAssetInstanceAsyncView::GetRenderAssetScreenSize FRenderAssetInstanceAsyncView::ProcessElement FStreamingRenderAsset::SetPerfectWantedMips_Async 计算与渲染元素的屏幕尺寸相关的信息的函数。 * 屏幕尺寸是与该资产重叠的屏幕像素数。 MaxSize:待渲染元素的最大尺寸(相对于屏幕分辨率)。MaxSize_VisibleOnly :渲染元素的最大屏幕尺寸(在屏幕上实际可见的部分)。MaxForcedNumLODs :必须强制存在的最大 LOD 数量。仅适用于网格。 函数内容:通过 ProcessElement 函数计算渲染元素的尺寸信息。ProcessElement 函数通过 BoundsViewInfo 数组中的 BoundsIndex 值获取元素的边界框信息。利用边界框信息计算渲染元素的屏幕尺寸,并将结果存储在 MaxSize 和 MaxSize_VisibleOnly 变量中。(仅当渲染元素类型不是纹理时,才会计算强制加载的 LOD 数量。) ▼ 展开 FRenderAssetInstanceAsyncView::GetRenderAssetScreenSize 关闭 void FRenderAssetInstanceAsyncView::GetRenderAssetScreenSize( EStreamableRenderAssetType AssetType, const UStreamableRenderAsset* InAsset, float& MaxSize, float& MaxSize_VisibleOnly, int32& MaxNumForcedLODs, const TCHAR* LogPrefix) const { // No need to iterate more if texture is already at maximum resolution. // Meshes don't really fit into the concept of max resolution but current // max_res for texture is 8k which is large enough to let mesh screen // sizes be constrained by this value int32 CurrCount = 0; if (View.IsValid()) { // Use the fast path if available, about twice as fast when there are a lot of elements. if (View->HasCompiledElements() && !LogPrefix) { const TArray<FRenderAssetInstanceView::FCompiledElement>* CompiledElements = View->GetCompiledElements(InAsset); if (CompiledElements) { const int32 NumCompiledElements = CompiledElements->Num(); const FRenderAssetInstanceView::FCompiledElement* CompiledElementData = CompiledElements->GetData(); int32 CompiledElementIndex = 0; while (CompiledElementIndex < NumCompiledElements && MaxSize_VisibleOnly < MAX_TEXTURE_SIZE) { const FRenderAssetInstanceView::FCompiledElement& CompiledElement = CompiledElementData[CompiledElementIndex]; if (ensure(BoundsViewInfo.IsValidIndex(CompiledElement.BoundsIndex))) { // Texel factor wasn't available because the component wasn't registered. Lazy initialize it now. if (AssetType != EStreamableRenderAssetType::Texture && CompiledElement.TexelFactor == 0.f && ensure(CompiledElement.BoundsIndex < View->NumBounds4() * 4)) { FRenderAssetInstanceView::FCompiledElement* MutableCompiledElement = const_cast<FRenderAssetInstanceView::FCompiledElement*>(&CompiledElement); MutableCompiledElement->TexelFactor = View->GetBounds4(CompiledElement.BoundsIndex / 4).RadiusOrComponentScale.Component(CompiledElement.BoundsIndex % 4) * 2.f; } ProcessElement( AssetType, BoundsViewInfo[CompiledElement.BoundsIndex], CompiledElement.TexelFactor, CompiledElement.bForceLoad, MaxSize, MaxSize_VisibleOnly, MaxNumForcedLODs); } ++CompiledElementIndex; } if (MaxSize_VisibleOnly >= MAX_TEXTURE_SIZE && CompiledElementIndex > 1) { // This does not realloc anything but moves the closest element at head, making the next update find it immediately and early exit. FRenderAssetInstanceView::FCompiledElement* SwapElementData = const_cast<FRenderAssetInstanceView::FCompiledElement*>(CompiledElementData); Swap<FRenderAssetInstanceView::FCompiledElement>(SwapElementData[0], SwapElementData[CompiledElementIndex - 1]); } } } else { int32 IterationCount_DebuggingOnly = 0; for (auto It = View->GetElementIterator(InAsset); It && (AssetType != EStreamableRenderAssetType::Texture || MaxSize_VisibleOnly < MAX_TEXTURE_SIZE || LogPrefix); ++It, ++IterationCount_DebuggingOnly) { View->VerifyElementIdx_DebuggingOnly(It.GetCurElementIdx_ForDebuggingOnly(), IterationCount_DebuggingOnly); // Only handle elements that are in bounds. if (ensure(BoundsViewInfo.IsValidIndex(It.GetBoundsIndex()))) { const FBoundsViewInfo& BoundsVieWInfo = BoundsViewInfo[It.GetBoundsIndex()]; ProcessElement(AssetType, BoundsVieWInfo, AssetType != EStreamableRenderAssetType::Texture ? It.GetTexelFactor() : It.GetTexelFactor() * BoundsVieWInfo.ComponentScale, It.GetForceLoad(), MaxSize, MaxSize_VisibleOnly, MaxNumForcedLODs); if (LogPrefix) { It.OutputToLog(BoundsVieWInfo.MaxNormalizedSize, BoundsVieWInfo.MaxNormalizedSize_VisibleOnly, LogPrefix); } } } } } } 此时HiddenWantedMips和VisibleWantedMips代表了两种不同的加载优先级。 HiddenWantedMips:这决定了当前用户未查看区域的纹理加载优先级。这些区域中的纹理仍然可以加载,但会使用较低分辨率的 MIP 级别进行加载。这用于在保持高帧率的同时节省系统资源。 VisibleWantedMips:这决定了用户当前查看区域的纹理加载优先级。此区域中的纹理将使用更高分辨率的 MIP 级别加载。当系统资源充足时,这可以为用户提供更高质量的纹理。 在 ProcessElement 中,我们通过将 TexelFactor 添加到上面获得的 BoundsVieWInfo.MaxNormalizedSize( ScreenSize / DistanceMinusRadius)和BoundsVieWInfo.MaxNormalizedSize_VisibleOnly 来获得 MaxSize。 MaxSize = FMath::Max(MaxSize,TexelFactor * BoundsVieWInfo.MaxNormalizedSize); MaxSize_VisibleOnly = FMath::Max(MaxSize_VisibleOnly,TexelFactor * BoundsVieWInfo.MaxNormalizedSize_VisibleOnly); 在FStreamingRenderAsset::SetPerfectWantedMips_Async函数中,使用GetWantedMipsFromSize函数计算VisibleWantedMips和HiddenWantedMips。 虽然我们利用上面获取到的MaxSize 来计算Mip,但是如果查看MinAllowedMips和MaxAllowedMips函数中GetWantedMipsFromSize的内部计算部分, 可以发现我们在返回的时候对其进行了clamp。FMath::Clamp<int32>(WantedMipsInt, MinAllowedMips, MaxAllowedMips); 因此,即使元素在视图中不可见,VisibleWantedMips 和 HiddenWantedMips 也会通过 MinAllowedMips填充值 。VisibleWantedMips = FMath::Max(GetWantedMipsFromSize(MaxSize_VisibleOnly, InvMaxScreenSizeOverAllViews), NumForcedMips); HiddenWantedMips = FMath::Max(GetWantedMipsFromSize(MaxSize * Settings.HiddenPrimitiveScale, InvMaxScreenSizeOverAllViews), NumForcedMips); 关闭 FRenderAssetInstanceAsyncView::ProcessElement void FRenderAssetInstanceAsyncView::ProcessElement( EStreamableRenderAssetType AssetType, const FBoundsViewInfo& BoundsVieWInfo, float TexelFactor, bool bForceLoad, float& MaxSize, float& MaxSize_VisibleOnly, int32& MaxNumForcedLODs) const { if (TexelFactor == FLT_MAX) // 강제로드된 컴포넌트라면 { MaxSize = BoundsVieWInfo.MaxNormalizedSize > 0 ? FLT_MAX : MaxSize; MaxSize_VisibleOnly = BoundsVieWInfo.MaxNormalizedSize_VisibleOnly > 0 ? FLT_MAX : MaxSize_VisibleOnly; } else if (TexelFactor >= 0) { MaxSize = FMath::Max(MaxSize, TexelFactor * BoundsVieWInfo.MaxNormalizedSize); MaxSize_VisibleOnly = FMath::Max(MaxSize_VisibleOnly, TexelFactor * BoundsVieWInfo.MaxNormalizedSize_VisibleOnly); // 강제 로드는 즉시 보이는 부분만 로드하고 중에 전체 텍스처를 로드합니다. if (bForceLoad && (BoundsVieWInfo.MaxNormalizedSize > 0 || BoundsVieWInfo.MaxNormalizedSize_VisibleOnly > 0)) { MaxSize = FLT_MAX; } } else // 음수의 Texel Factor는 고정 해상도에 매핑됩니다. 현재 랜드스케이프에 사용됩니다. { if (AssetType == EStreamableRenderAssetType::Texture) { MaxSize = FMath::Max(MaxSize, -TexelFactor); MaxSize_VisibleOnly = FMath::Max(MaxSize_VisibleOnly, -TexelFactor); } else { check(AssetType == EStreamableRenderAssetType::StaticMesh || AssetType == EStreamableRenderAssetType::SkeletalMesh); check(-TexelFactor <= (float)MAX_MESH_LOD_COUNT); MaxNumForcedLODs = FMath::Max(MaxNumForcedLODs, static_cast<int32>(-TexelFactor)); } // Force load will load the immediatly visible part, and later the full texture. if (bForceLoad && (BoundsVieWInfo.MaxNormalizedSize > 0 || BoundsVieWInfo.MaxNormalizedSize_VisibleOnly > 0)) { MaxSize = FLT_MAX; MaxSize_VisibleOnly = FLT_MAX; } } } FStreamingRenderAsset::SetPerfectWantedMips_Async void FStreamingRenderAsset::SetPerfectWantedMips_Async( float MaxSize, float MaxSize_VisibleOnly, float MaxScreenSizeOverAllViews, int32 MaxNumForcedLODs, bool InLooksLowRes, const FRenderAssetStreamingSettings& Settings) { bForceFullyLoadHeuristic = (MaxSize == FLT_MAX || MaxSize_VisibleOnly == FLT_MAX); bLooksLowRes = InLooksLowRes; // Things like lightmaps, HLOD and close instances. NormalizedScreenSize = 0.f; if (MaxNumForcedLODs >= MaxAllowedMips) { VisibleWantedMips = HiddenWantedMips = NumForcedMips = MaxAllowedMips; NumMissingMips = 0; return; } float InvMaxScreenSizeOverAllViews = 1.f; if (IsMesh()) { InvMaxScreenSizeOverAllViews = 1.f / MaxScreenSizeOverAllViews; NormalizedScreenSize = FMath::Max(MaxSize, MaxSize_VisibleOnly) * InvMaxScreenSizeOverAllViews; } NumForcedMips = FMath::Min(MaxNumForcedLODs, MaxAllowedMips); VisibleWantedMips = FMath::Max(GetWantedMipsFromSize(MaxSize_VisibleOnly, InvMaxScreenSizeOverAllViews), NumForcedMips); // Terrain, Forced Fully Load and Things that already look bad are not affected by hidden scale. if (bIsTerrainTexture || bForceFullyLoadHeuristic || bLooksLowRes) { HiddenWantedMips = FMath::Max(GetWantedMipsFromSize(MaxSize, InvMaxScreenSizeOverAllViews), NumForcedMips); NumMissingMips = 0; // No impact for terrains as there are not allowed to drop mips. } else { HiddenWantedMips = FMath::Max(GetWantedMipsFromSize(MaxSize * Settings.HiddenPrimitiveScale, InvMaxScreenSizeOverAllViews), NumForcedMips); // NumMissingMips contains the number of mips not loaded because of HiddenPrimitiveScale. When out of budget, those texture will be considered as already sacrificed. NumMissingMips = FMath::Max<int32>(GetWantedMipsFromSize(MaxSize, InvMaxScreenSizeOverAllViews) - FMath::Max<int32>(VisibleWantedMips, HiddenWantedMips), 0); } } int32 FStreamingRenderAsset::GetWantedMipsFromSize(float Size, float InvMaxScreenSizeOverAllViews) const { if (IsTexture()) { float WantedMipsFloat = 1.0f + FMath::Log2(FMath::Max(1.f, Size)); int32 WantedMipsInt = FMath::CeilToInt(WantedMipsFloat); return FMath::Clamp<int32>(WantedMipsInt, MinAllowedMips, MaxAllowedMips); } FRenderAssetStreamingMipCalcTask::UpdateBudgetedMips_Async 1. 遍历StreamingRenderAsset并调用StreamingRenderAsset.UpdateRetentionPriority_Async。 获取纹理组、大小和渲染的优先级后,它会返回 PerpectWantedMip 的预算值。 关闭 void FRenderAssetStreamingMipCalcTask::UpdateBudgetedMips_Async() { //************************************* // Update Budget //************************************* TArray<FStreamingRenderAsset>& StreamingRenderAssets = StreamingManager.AsyncUnsafeStreamingRenderAssets; const FRenderAssetStreamingSettings& Settings = StreamingManager.Settings; TArray<int32> PrioritizedRenderAssets; TArray<int32> PrioritizedMeshes; int32 NumAssets = 0; int32 NumMeshes = 0; int64 MemoryBudgeted = 0; int64 MeshMemoryBudgeted = 0; int64 MemoryUsedByNonTextures = 0; int64 MemoryUsed = 0; for (FStreamingRenderAsset& StreamingRenderAsset : StreamingRenderAssets) { if (IsAborted()) break; const int64 AssetMemBudgeted = StreamingRenderAsset.UpdateRetentionPriority_Async(Settings.bPrioritizeMeshLODRetention); const int32 AssetMemUsed = StreamingRenderAsset.GetSize(StreamingRenderAsset.ResidentMips); MemoryUsed += AssetMemUsed; if (StreamingRenderAsset.IsTexture()) { MemoryBudgeted += AssetMemBudgeted; ++NumAssets; } else { MeshMemoryBudgeted += AssetMemBudgeted; MemoryUsedByNonTextures += AssetMemUsed; ++NumMeshes; } } //************************************* // Update Effective Budget //************************************* bool bResetMipBias = false; // 메모리 예산이 필요한 풀 크기보다 크게 감소한 경우, // 메모리 예산이 크게 줄어든 경우 예산의 임계값을 MemoryBudgeted로 재설정합니다.이 경우 bResetMipBias 변수가 true로 설정되어 mip 바이어스가 재설정됩니다. if (PerfectWantedMipsBudgetResetThresold - MemoryBudgeted - MeshMemoryBudgeted > TempMemoryBudget + MemoryMargin) { // Reset the budget tradeoffs if the required pool size shrinked significantly. PerfectWantedMipsBudgetResetThresold = MemoryBudgeted; bResetMipBias = true; } // 메모리 예산이 PerfectWantedMipsBudgetResetThresold보다 큰 경우 // 더 높은 요구 사항으로 인해 더 큰 상호작용이 발생하므로 임계값을 MemoryBudgeted + MeshMemoryBudgeted로 늘립니다. else if (MemoryBudgeted + MeshMemoryBudgeted > PerfectWantedMipsBudgetResetThresold) { // Keep increasing the threshold since higher requirements incurs bigger tradeoffs. PerfectWantedMipsBudgetResetThresold = MemoryBudgeted + MeshMemoryBudgeted; } const int64 NonStreamingRenderAssetMemory = AllocatedMemory - MemoryUsed + MemoryUsedByNonTextures; int64 AvailableMemoryForStreaming = PoolSize - NonStreamingRenderAssetMemory - MemoryMargin; // If the platform defines a max VRAM usage, check if the pool size must be reduced, // but also check if it would be safe to some of the NonStreamingRenderAssetMemory from the pool size computation. // The later helps significantly in low budget settings, where NonStreamingRenderAssetMemory would take too much of the pool. if (GPoolSizeVRAMPercentage > 0 && TotalGraphicsMemory > 0) { const int64 UsableVRAM = FMath::Max<int64>(TotalGraphicsMemory * GPoolSizeVRAMPercentage / 100, TotalGraphicsMemory - Settings.VRAMPercentageClamp * 1024ll * 1024ll); const int64 UsedVRAM = (int64)GCurrentRendertargetMemorySize * 1024ll + NonStreamingRenderAssetMemory; // Add any other... const int64 AvailableVRAMForStreaming = FMath::Min<int64>(UsableVRAM - UsedVRAM - MemoryMargin, PoolSize); if (Settings.bLimitPoolSizeToVRAM || AvailableVRAMForStreaming > AvailableMemoryForStreaming) { AvailableMemoryForStreaming = AvailableVRAMForStreaming; } } // Update EffectiveStreamingPoolSize, trying to stabilize it independently of temp memory, allocator overhead and non-streaming resources normal variation. // It's hard to know how much temp memory and allocator overhead is actually in AllocatedMemorySize as it is platform specific. // We handle it by not using all memory available. If temp memory and memory margin values are effectively bigger than the actual used values, the pool will stabilize. if (AvailableMemoryForStreaming < MemoryBudget) { // Reduce size immediately to avoid taking more memory. MemoryBudget = FMath::Max<int64>(AvailableMemoryForStreaming, 0); } else if (AvailableMemoryForStreaming - MemoryBudget > TempMemoryBudget + MemoryMargin) { // Increase size considering that the variation does not come from temp memory or allocator overhead (or other recurring cause). // It's unclear how much temp memory is actually in there, but the value will decrease if temp memory increases. MemoryBudget = AvailableMemoryForStreaming; bResetMipBias = true; } const int64 PrevMeshMemoryBudget = MeshMemoryBudget; MeshMemoryBudget = Settings.MeshPoolSize * 1024 * 1024; const bool bUseSeparatePoolForMeshes = MeshMemoryBudget >= 0; if (!bUseSeparatePoolForMeshes) { NumAssets += NumMeshes; NumMeshes = 0; MemoryBudgeted += MeshMemoryBudgeted; MeshMemoryBudgeted = 0; } else if (PrevMeshMemoryBudget < MeshMemoryBudget) { bResetMipBias = true; } //******************************************* // Reset per mip bias if not required anymore. //******************************************* // When using mip per texture/mesh, the BudgetMipBias gets reset when the required resolution does not get affected anymore by the BudgetMipBias. // This allows texture/mesh to reset their bias when the viewpoint gets far enough, or the primitive is not visible anymore. if (Settings.bUsePerTextureBias) { for (FStreamingRenderAsset& StreamingRenderAsset : StreamingRenderAssets) { if (IsAborted()) break; if (StreamingRenderAsset.BudgetMipBias > 0 && (bResetMipBias || FMath::Max<int32>( StreamingRenderAsset.VisibleWantedMips, StreamingRenderAsset.HiddenWantedMips + StreamingRenderAsset.NumMissingMips) < StreamingRenderAsset.MaxAllowedMips)) { StreamingRenderAsset.BudgetMipBias = 0; } } } //************************************* // Drop Mips //************************************* // If the budget is taking too much, drop some mips. if ((MemoryBudgeted > MemoryBudget || (bUseSeparatePoolForMeshes && MeshMemoryBudgeted > MeshMemoryBudget)) && !IsAborted()) { //************************************* // Get texture/mesh list in order of reduction //************************************* PrioritizedRenderAssets.Empty(NumAssets); PrioritizedMeshes.Empty(NumMeshes); for (int32 AssetIndex = 0; AssetIndex < StreamingRenderAssets.Num() && !IsAborted(); ++AssetIndex) { FStreamingRenderAsset& StreamingRenderAsset = StreamingRenderAssets[AssetIndex]; // Only consider non deleted textures/meshes (can change any time). if (!StreamingRenderAsset.RenderAsset) continue; // Ignore textures/meshes for which we are not allowed to reduce resolution. if (!StreamingRenderAsset.IsMaxResolutionAffectedByGlobalBias()) continue; // Ignore texture/mesh that can't drop any mips const int32 MinAllowedMips = FMath::Max(StreamingRenderAsset.MinAllowedMips, StreamingRenderAsset.NumForcedMips); if (StreamingRenderAsset.BudgetedMips > MinAllowedMips) { if (bUseSeparatePoolForMeshes && StreamingRenderAsset.IsMesh()) { PrioritizedMeshes.Add(AssetIndex); } else { PrioritizedRenderAssets.Add(AssetIndex); } } } // Sort texture/mesh, having those that should be dropped first. PrioritizedRenderAssets.Sort(FCompareRenderAssetByRetentionPriority(StreamingRenderAssets)); PrioritizedMeshes.Sort(FCompareRenderAssetByRetentionPriority(StreamingRenderAssets)); if (Settings.bUsePerTextureBias && AllowPerRenderAssetMipBiasChanges()) { //************************************* // Drop Max Resolution until in budget. //************************************* TryDropMaxResolutions(PrioritizedRenderAssets, MemoryBudgeted, MemoryBudget); if (bUseSeparatePoolForMeshes) { TryDropMaxResolutions(PrioritizedMeshes, MeshMemoryBudgeted, MeshMemoryBudget); } } //************************************* // Drop WantedMip until in budget. //************************************* TryDropMips(PrioritizedRenderAssets, MemoryBudgeted, MemoryBudget); if (bUseSeparatePoolForMeshes) { TryDropMips(PrioritizedMeshes, MeshMemoryBudgeted, MeshMemoryBudget); } } //************************************* // Keep Mips //************************************* // If there is some room left, try to keep as much as long as it won't bust budget. // This will run even after sacrificing to fit in budget since some small unwanted mips could still be kept. if ((MemoryBudgeted < MemoryBudget || (bUseSeparatePoolForMeshes && MeshMemoryBudgeted < MeshMemoryBudget)) && !IsAborted()) { PrioritizedRenderAssets.Empty(NumAssets); PrioritizedMeshes.Empty(NumMeshes); const int64 MaxAllowedDelta = MemoryBudget - MemoryBudgeted; const int64 MaxAllowedMeshDelta = MeshMemoryBudget - MeshMemoryBudgeted; for (int32 AssetIndex = 0; AssetIndex < StreamingRenderAssets.Num() && !IsAborted(); ++AssetIndex) { FStreamingRenderAsset& StreamingRenderAsset = StreamingRenderAssets[AssetIndex]; // Only consider non deleted textures/meshes (can change any time). if (!StreamingRenderAsset.RenderAsset) continue; // Only consider textures/meshes that won't bust budget nor generate new I/O requests if (StreamingRenderAsset.BudgetedMips < StreamingRenderAsset.ResidentMips) { const int32 Delta = StreamingRenderAsset.GetSize(StreamingRenderAsset.BudgetedMips + 1) - StreamingRenderAsset.GetSize(StreamingRenderAsset.BudgetedMips); const bool bUseMeshVariant = bUseSeparatePoolForMeshes && StreamingRenderAsset.IsMesh(); const int64 MaxDelta = bUseMeshVariant ? MaxAllowedMeshDelta : MaxAllowedDelta; TArray<int32>& AssetIndcies = bUseMeshVariant ? PrioritizedMeshes : PrioritizedRenderAssets; if (Delta <= MaxDelta) { AssetIndcies.Add(AssetIndex); } } } // Sort texture/mesh, having those that should be dropped first. PrioritizedRenderAssets.Sort(FCompareRenderAssetByRetentionPriority(StreamingRenderAssets)); PrioritizedMeshes.Sort(FCompareRenderAssetByRetentionPriority(StreamingRenderAssets)); TryKeepMips(PrioritizedRenderAssets, MemoryBudgeted, MemoryBudget); if (bUseSeparatePoolForMeshes) { TryKeepMips(PrioritizedMeshes, MeshMemoryBudgeted, MeshMemoryBudget); } } //************************************* // Handle drop mips debug option //************************************* #if !UE_BUILD_SHIPPING if (Settings.DropMips > 0) { for (FStreamingRenderAsset& StreamingRenderAsset : StreamingRenderAssets) { if (IsAborted()) break; if (Settings.DropMips == 1) { StreamingRenderAsset.BudgetedMips = FMath::Min<int32>(StreamingRenderAsset.BudgetedMips, StreamingRenderAsset.GetPerfectWantedMips()); } else { StreamingRenderAsset.BudgetedMips = FMath::Min<int32>(StreamingRenderAsset.BudgetedMips, StreamingRenderAsset.VisibleWantedMips); } } } #endif }
07-26
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值