You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
- Avoid gotchas with max texture size when static separate enabled - Simplify addressing logic in a number of places - Avoid allocating extra HZB that we never use Details: - Support rendering/sampling to 2D depth texture array in Nanite and virtual shadow map pass - Remove some unnecessary HZB-related cvars - Remove unused permutations from VSM HW raster #preflight 624f4e5611261bc7b2171208 #rb jamie.hayes #ROBOMERGE-AUTHOR: andrew.lauritzen #ROBOMERGE-SOURCE: CL 19679616 via CL 19679656 via CL 19679706 #ROBOMERGE-BOT: UE5 (Release-Engine-Staging -> Main) (v938-19570697) [CL 19680680 by andrew lauritzen in ue5-main branch]
200 lines
7.0 KiB
Plaintext
200 lines
7.0 KiB
Plaintext
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#pragma once
|
|
|
|
#include "../Common.ush"
|
|
#include "../Nanite/NaniteHZBCull.ush"
|
|
#include "../SceneData.ush"
|
|
#include "VirtualShadowMapPageAccessCommon.ush"
|
|
#include "VirtualShadowMapStaticCaching.ush"
|
|
|
|
StructuredBuffer<uint> HZBPageTable;
|
|
StructuredBuffer<uint4> HZBPageRectBounds;
|
|
StructuredBuffer<uint> HZBPageFlags;
|
|
|
|
// NOTE: HMipLevel = 0 means the regular page flags
|
|
uint ShadowGetPageFlags(uint ShadowMapID, uint MipLevel, uint HMipLevel, uint2 PageAddress)
|
|
{
|
|
uint MipToSample = MipLevel + HMipLevel;
|
|
uint HPageLevelOffset = CalcPageTableLevelOffset(ShadowMapID, MipToSample);
|
|
uint RawFlags = VirtualShadowMap.PageFlags[CalcPageOffset(ShadowMapID, MipToSample, PageAddress)];
|
|
|
|
// Extract the flags for the given HMip
|
|
uint HMipBitShift = VSM_PAGE_FLAGS_BITS_PER_HMIP * HMipLevel;
|
|
return (RawFlags >> HMipBitShift) & VSM_PAGE_FLAGS_BITS_MASK;
|
|
}
|
|
|
|
|
|
uint4 GetPageRect(FScreenRect Rect)
|
|
{
|
|
uint4 RectPages = uint4(Rect.Pixels) >> VSM_LOG2_PAGE_SIZE;
|
|
|
|
return RectPages;
|
|
}
|
|
|
|
uint4 ClipPageRect(uint4 RectPages, uint VirtualShadowMapId, uint MipLevel, StructuredBuffer<uint4> PageRectBoundsBuffer)
|
|
{
|
|
uint4 AllocatedBounds = PageRectBoundsBuffer[VirtualShadowMapId * VSM_MAX_MIP_LEVELS + MipLevel];
|
|
|
|
return uint4(max(RectPages.xy, AllocatedBounds.xy), min(RectPages.zw, AllocatedBounds.zw));
|
|
}
|
|
|
|
uint4 GetPageRect(FScreenRect Rect, uint VirtualShadowMapId, uint MipLevel)
|
|
{
|
|
return ClipPageRect(GetPageRect(Rect), VirtualShadowMapId, MipLevel, VirtualShadowMap.PageRectBounds);
|
|
}
|
|
|
|
// Returns true if *all* flags in FlagMask are set on at least one overlapped page (see GetPageFlagMaskForRendering below)
|
|
bool OverlapsAnyValidPage(uint ShadowMapID, uint MipLevel, uint4 RectPages, uint FlagMask = VSM_ALLOCATED_FLAG)
|
|
{
|
|
// Skip empty rectangles (inclusive).
|
|
if (any(RectPages.zw < RectPages.xy))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
uint HMipLevel = MipLevelForRect(RectPages, 2);
|
|
|
|
RectPages >>= HMipLevel;
|
|
for (uint y = RectPages.y; y <= RectPages.w; y++)
|
|
{
|
|
for (uint x = RectPages.x; x <= RectPages.z; x++)
|
|
{
|
|
uint PageFlags = ShadowGetPageFlags(ShadowMapID, MipLevel, HMipLevel, uint2(x, y));
|
|
if ((PageFlags & FlagMask) == FlagMask)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Wrapper type to make misuse slightly harder.
|
|
*/
|
|
struct FPageTestScreenRect
|
|
{
|
|
FScreenRect ScreenRect;
|
|
uint HZBLevelPageSizeShift;
|
|
int HZBLevelPageSizeInclusive;
|
|
uint4 RectPages;
|
|
bool bWasPageRectClipped;
|
|
};
|
|
|
|
|
|
/**
|
|
* Set up a screen rect and pre-computed data for testing pages against HZB, this assumes a 4x4-HZB FScreenRect
|
|
* as input. The resulting rect has been clamped to the mip level where a page is 4x4 texels, as higher mips are meaningless.
|
|
*/
|
|
FPageTestScreenRect SetupPageHZBRect(FScreenRect ScreenRect, uint ShadowMapID, uint MipLevel)
|
|
{
|
|
FPageTestScreenRect Result;
|
|
Result.ScreenRect = ScreenRect;
|
|
// Clamp to level where a page is 4x4 (HZB mip 0 is half-size)
|
|
if (Result.ScreenRect.HZBLevel > (VSM_LOG2_PAGE_SIZE - 3))
|
|
{
|
|
// Adjust HZB texel rect to match new mip level, this will be too large, but is clipped below.
|
|
Result.ScreenRect.HZBTexels = int4(Result.ScreenRect.Pixels.xy, max(Result.ScreenRect.Pixels.xy, Result.ScreenRect.Pixels.zw)) >> (VSM_LOG2_PAGE_SIZE - 2U);
|
|
Result.ScreenRect.HZBLevel = VSM_LOG2_PAGE_SIZE - 3U;
|
|
}
|
|
Result.HZBLevelPageSizeShift = VSM_LOG2_PAGE_SIZE - 1U - Result.ScreenRect.HZBLevel;
|
|
Result.HZBLevelPageSizeInclusive = (1U << Result.HZBLevelPageSizeShift) - 1;
|
|
uint4 UnClippedRectPages = GetPageRect(ScreenRect);
|
|
// If the clipped page rect is smaller than the unclipped rect, there are unmapped pages in the footprint and we return
|
|
// that it is visible.
|
|
Result.RectPages = ClipPageRect(UnClippedRectPages, ShadowMapID, MipLevel, HZBPageRectBounds);
|
|
Result.bWasPageRectClipped = any(Result.RectPages.xy > UnClippedRectPages.xy) || any(Result.RectPages.zw < UnClippedRectPages.zw);
|
|
|
|
return Result;
|
|
}
|
|
|
|
|
|
bool IsPageVisibleHZB(uint2 vPage, uint PageFlagOffset, FPageTestScreenRect PageTestScreenRect)
|
|
{
|
|
FShadowPhysicalPage pPage = ShadowDecodePageTable(HZBPageTable[PageFlagOffset]);
|
|
|
|
if (pPage.bThisLODValid)
|
|
{
|
|
uint2 PhysicalAddress = pPage.PhysicalAddress;
|
|
|
|
FScreenRect HZBTestRect = PageTestScreenRect.ScreenRect;
|
|
|
|
// Move to page local (in mip level) space and clamp rect to page size.
|
|
HZBTestRect.HZBTexels -= (vPage << PageTestScreenRect.HZBLevelPageSizeShift).xyxy;
|
|
HZBTestRect.HZBTexels = clamp(HZBTestRect.HZBTexels, 0, PageTestScreenRect.HZBLevelPageSizeInclusive);
|
|
// Translate to physical address space
|
|
HZBTestRect.HZBTexels += (PhysicalAddress << PageTestScreenRect.HZBLevelPageSizeShift).xyxy;
|
|
|
|
// Skip invalidation if the instance is occluded
|
|
return IsVisibleHZB(HZBTestRect, true);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* Perform HZB-Test for a rectangle of pages, returning true if the Rect is visible in at least one page.
|
|
* @param TestPageMask - page flags to inlcude in occlusion test, e.g., VSM_ALLOCATED_FLAG will test any allocated page, but ignore unallocated ones.
|
|
* @param VisiblePageMask - page flags to treat as un-occluded, teted after the above mask, e.g., use VSM_UNCACHED_FLAG to treat any uncached page as visible.
|
|
*/
|
|
bool IsVisibleMaskedHZB(uint PrevShadowMapID, uint MipLevel, FScreenRect Rect, bool bClampToPageLevel, bool bTreatUnmappedAsOccluded, uint VisiblePageMask, uint TestPageMask = 0xFFFFFFFFu)
|
|
{
|
|
// Don't have an HZB to test.
|
|
if (PrevShadowMapID == ~0u)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// Don't go past mip level of 4x4 for a 4x4 test without possibly covering more than 4 pages.
|
|
if (!bClampToPageLevel && Rect.HZBLevel > VSM_LOG2_PAGE_SIZE - 3)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
FPageTestScreenRect HZBTestRect = SetupPageHZBRect(Rect, PrevShadowMapID, MipLevel);
|
|
|
|
// Allow treating unmapped pages as visible, such that
|
|
if (!bTreatUnmappedAsOccluded && HZBTestRect.bWasPageRectClipped)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
|
|
uint4 RectPages = HZBTestRect.RectPages;
|
|
uint PageTableLevelOffset = CalcPageTableLevelOffset(PrevShadowMapID, MipLevel);
|
|
|
|
for (uint y = RectPages.y; y <= RectPages.w; y++)
|
|
{
|
|
for (uint x = RectPages.x; x <= RectPages.z; x++)
|
|
{
|
|
uint PageFlagOffset = PageTableLevelOffset + CalcPageOffsetInLevel(MipLevel, uint2(x, y));
|
|
uint PageFlag = HZBPageFlags[PageFlagOffset];
|
|
|
|
// Skip unallocated pages if bTreatUnmappedAsOccluded is true, otherwise test everything
|
|
if (!bTreatUnmappedAsOccluded || ((PageFlag & TestPageMask) != 0U))
|
|
{
|
|
// Treat pages with the VisiblePageMask as visible - can be used to select only cached pages
|
|
if ((PageFlag & VisiblePageMask) != 0U || IsPageVisibleHZB(uint2(x, y), PageFlagOffset, HZBTestRect))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
uint GetPageFlagMaskForRendering(FInstanceSceneData InstanceData, bool bHasMoved)
|
|
{
|
|
// Clustered that have moved must be rendered into any allocated page
|
|
// Otherwise they must be rendered only into uncached pages that match their "static/dynamic" instance type
|
|
const uint PageFlagMask =
|
|
bHasMoved ? VSM_ALLOCATED_FLAG :
|
|
(ShouldCacheInstanceAsStatic(InstanceData) ? VSM_STATIC_UNCACHED_FLAG : VSM_DYNAMIC_UNCACHED_FLAG);
|
|
return PageFlagMask;
|
|
}
|