1449 lines
59 KiB
HLSL
1449 lines
59 KiB
HLSL
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#pragma once
|
|
|
|
#include "LargeWorldCoordinates.ush"
|
|
#include "OctahedralCommon.ush"
|
|
#include "WaveOpUtil.ush"
|
|
#include "ByteBuffer.ush"
|
|
#include "/Engine/Shared/LightSceneData.h"
|
|
#include "/Engine/Shared/SceneDefinitions.h"
|
|
|
|
// default to using the global Scene uniform buffer binding
|
|
#ifndef USE_EXPLICIT_SCENE_UB_MODULES
|
|
#define USE_EXPLICIT_SCENE_UB_MODULES 0
|
|
#endif
|
|
|
|
#if USE_EXPLICIT_SCENE_UB_MODULES
|
|
// Mangle the name into an explicit UB for SceneUB sub-module
|
|
#define SceneUB(FieldName) SceneUbEx##FieldName
|
|
#else
|
|
// Default, global, Scene uniform buffer binding
|
|
#define SceneUB(FieldName) Scene.FieldName
|
|
#endif
|
|
|
|
#ifndef USE_GLOBAL_GPU_SCENE_DATA
|
|
#define USE_GLOBAL_GPU_SCENE_DATA 0
|
|
#endif
|
|
|
|
#ifndef USE_GPU_SCENE_DATA_RW
|
|
#define USE_GPU_SCENE_DATA_RW 0
|
|
#endif
|
|
|
|
#ifndef USES_PER_INSTANCE_CUSTOM_DATA
|
|
#define USES_PER_INSTANCE_CUSTOM_DATA 0
|
|
#endif
|
|
|
|
#ifndef NEEDS_LIGHTMAP_COORDINATE
|
|
#define NEEDS_LIGHTMAP_COORDINATE 0
|
|
#endif
|
|
|
|
#ifndef USE_INSTANCE_CULLING_DATA
|
|
#define USE_INSTANCE_CULLING_DATA (!(RAYHITGROUPSHADER || RAYTRACING_DYNAMIC_GEOMETRY_CONVERTER))
|
|
#endif
|
|
|
|
#ifndef VF_REQUIRES_PER_INSTANCE_CUSTOM_DATA
|
|
#define VF_REQUIRES_PER_INSTANCE_CUSTOM_DATA 0
|
|
#endif
|
|
|
|
#ifndef ENABLE_PER_INSTANCE_CUSTOM_DATA
|
|
#define ENABLE_PER_INSTANCE_CUSTOM_DATA (USES_PER_INSTANCE_CUSTOM_DATA || VF_REQUIRES_PER_INSTANCE_CUSTOM_DATA)
|
|
#endif
|
|
|
|
#ifndef PLATFORM_USES_PRIMITIVE_UBO
|
|
#define PLATFORM_USES_PRIMITIVE_UBO (PLATFORM_SUPPORTS_UNIFORM_BUFFER_OBJECTS && FEATURE_LEVEL == FEATURE_LEVEL_ES3_1)
|
|
#endif
|
|
|
|
#ifndef SHADER_USES_PRIMITIVE_UBO
|
|
#define SHADER_USES_PRIMITIVE_UBO (PLATFORM_USES_PRIMITIVE_UBO && (PIXELSHADER || VERTEXSHADER) && !USE_GLOBAL_GPU_SCENE_DATA)
|
|
#endif
|
|
|
|
// Whether to fetch primitive values (eg LocalToWorld) by dynamically indexing a scene-wide buffer, or to reference a single Primitive uniform buffer
|
|
#if VF_SUPPORTS_PRIMITIVE_SCENE_DATA
|
|
#define VF_USE_PRIMITIVE_SCENE_DATA 1
|
|
#else
|
|
#define VF_USE_PRIMITIVE_SCENE_DATA 0
|
|
#endif
|
|
|
|
#ifndef USE_DITHERED_LOD_TRANSITION
|
|
#if USE_INSTANCING || USE_INSTANCE_CULLING
|
|
#ifdef USE_DITHERED_LOD_TRANSITION_FOR_INSTANCED
|
|
#define USE_DITHERED_LOD_TRANSITION USE_DITHERED_LOD_TRANSITION_FOR_INSTANCED
|
|
#endif
|
|
#else
|
|
#ifdef USE_DITHERED_LOD_TRANSITION_FROM_MATERIAL
|
|
#define USE_DITHERED_LOD_TRANSITION USE_DITHERED_LOD_TRANSITION_FROM_MATERIAL
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
#define INVALID_INSTANCE_PAYLOAD_OFFSET (0xFFFFFFFFu)
|
|
|
|
#if PLATFORM_ALLOW_SCENE_DATA_COMPRESSED_TRANSFORMS
|
|
static const uint InstanceTransformSizeFloat4Count = 2u; // compressed transform
|
|
#else
|
|
static const uint InstanceTransformSizeFloat4Count = 3u; // encoded scale/rotation (uint4) and translation (float3)
|
|
#endif
|
|
|
|
#define NUM_CUSTOM_PRIMITIVE_DATA 9u // Num float4s used for custom data. Must match FCustomPrimitiveData::NumCustomPrimitiveDataFloat4s in SceneTypes.h
|
|
|
|
struct FPrimitiveSceneData
|
|
{
|
|
uint Flags;
|
|
uint VisibilityFlags;
|
|
int InstanceSceneDataOffset; // Link to the range of instances that belong to this primitive
|
|
int NumInstanceSceneDataEntries;
|
|
int PersistentPrimitiveIndex;
|
|
uint SingleCaptureIndex; // TODO: Use 16 bits? 8 bits?
|
|
float3 PositionHigh;
|
|
uint PrimitiveComponentId; // TODO: Refactor to use PersistentPrimitiveIndex, ENGINE USE ONLY - will be removed
|
|
FDFMatrix LocalToWorld;
|
|
FDFInverseMatrix WorldToLocal;
|
|
FDFMatrix PreviousLocalToWorld;
|
|
FDFInverseMatrix PreviousWorldToLocal;
|
|
float4x4 WorldToPreviousWorld; // NOTE: Intentionally not LWC
|
|
float3 InvNonUniformScale;
|
|
float ObjectBoundsX;
|
|
FDFVector3 ObjectWorldPosition;
|
|
FDFVector3 ActorWorldPosition;
|
|
float MinMaterialDisplacement;
|
|
float MaxMaterialDisplacement;
|
|
#if PRIMITIVE_HAS_TILEOFFSET_DATA
|
|
FLWCVector3 ObjectWorldPositionTO;
|
|
FLWCVector3 ActorWorldPositionTO;
|
|
#endif
|
|
float ObjectRadius;
|
|
uint LightmapUVIndex; // TODO: Use 16 bits? // TODO: Move into associated array that disappears if static lighting is disabled
|
|
float3 ObjectOrientation; // TODO: More efficient representation?
|
|
uint LightmapDataIndex; // TODO: Use 16 bits? // TODO: Move into associated array that disappears if static lighting is disabled
|
|
float4 NonUniformScale;
|
|
float3 PreSkinnedLocalBoundsMin;
|
|
uint NaniteResourceID;
|
|
float3 PreSkinnedLocalBoundsMax;
|
|
uint NaniteHierarchyOffset;
|
|
uint NaniteAssemblyTransformOffset;
|
|
float3 LocalObjectBoundsMin;
|
|
float ObjectBoundsY;
|
|
float3 LocalObjectBoundsMax;
|
|
float ObjectBoundsZ;
|
|
uint InstancePayloadDataOffset;
|
|
uint InstancePayloadDataStride;
|
|
uint InstancePayloadExtensionSize;
|
|
float3 InstanceLocalBoundsCenter;
|
|
float3 InstanceLocalBoundsExtent;
|
|
float3 WireframeColor; // TODO: Should refactor out all editor data into a separate buffer
|
|
float3 PrimitiveColor; // TODO: Should refactor out all editor data into a separate buffer
|
|
uint PackedNaniteFlags;
|
|
float2 InstanceDrawDistanceMinMaxSquared;
|
|
float InstanceWPODisableDistanceSquared;
|
|
uint NaniteRayTracingDataOffset;
|
|
float MaxWPOExtent;
|
|
uint CustomStencilValueAndMask;
|
|
float PixelProgrammableDistanceSquared;
|
|
float MaterialDisplacementFadeOutSize;
|
|
uint2 MeshPaintTextureDescriptor;
|
|
uint2 MaterialCacheTextureDescriptor;
|
|
float4 CustomPrimitiveData[NUM_CUSTOM_PRIMITIVE_DATA]; // TODO: Move to associated array to shrink primitive data and pack cachelines more effectively
|
|
float AnimationMinScreenSize;
|
|
};
|
|
|
|
float3 UnpackColorRGB24(float PackedColorFloat)
|
|
{
|
|
uint PackedColor = asuint(PackedColorFloat);
|
|
return float3
|
|
(
|
|
float((PackedColor >> 24u) & 0xFF),
|
|
float((PackedColor >> 16u) & 0xFF),
|
|
float((PackedColor >> 8u) & 0xFF)
|
|
) * (1.0f / 255.0f);
|
|
}
|
|
|
|
// Fetch from Primitive uniform buffer
|
|
FPrimitiveSceneData GetPrimitiveDataFromUniformBuffer()
|
|
{
|
|
FPrimitiveSceneData PrimitiveData;
|
|
|
|
PrimitiveData.Flags = Primitive.Flags;
|
|
PrimitiveData.VisibilityFlags = Primitive.VisibilityFlags;
|
|
PrimitiveData.InstanceSceneDataOffset = Primitive.InstanceSceneDataOffset;
|
|
PrimitiveData.NumInstanceSceneDataEntries = Primitive.NumInstanceSceneDataEntries;
|
|
PrimitiveData.SingleCaptureIndex = Primitive.SingleCaptureIndex;
|
|
PrimitiveData.PositionHigh = Primitive.PositionHigh;
|
|
PrimitiveData.PrimitiveComponentId = Primitive.PrimitiveComponentId;
|
|
|
|
PrimitiveData.LocalToWorld = MakeDFMatrix4x3(Primitive.PositionHigh, Primitive.LocalToRelativeWorld);
|
|
PrimitiveData.WorldToLocal = MakeDFInverseMatrix4x3(Primitive.PositionHigh, Primitive.RelativeWorldToLocal);
|
|
PrimitiveData.PreviousLocalToWorld = MakeDFMatrix4x3(Primitive.PositionHigh, Primitive.PreviousLocalToRelativeWorld);
|
|
PrimitiveData.PreviousWorldToLocal = MakeDFInverseMatrix4x3(Primitive.PositionHigh, Primitive.PreviousRelativeWorldToLocal);
|
|
|
|
PrimitiveData.WorldToPreviousWorld = Primitive.WorldToPreviousWorld;
|
|
PrimitiveData.InvNonUniformScale = Primitive.InvNonUniformScale;
|
|
PrimitiveData.ObjectBoundsX = Primitive.ObjectBoundsX;
|
|
PrimitiveData.ObjectRadius = Primitive.ObjectWorldPositionHighAndRadius.w;
|
|
#if PRIMITIVE_HAS_TILEOFFSET_DATA
|
|
PrimitiveData.ObjectWorldPositionTO = MakeLWCVector3(Primitive.ObjectWorldPositionHighAndRadius.xyz, Primitive.ObjectWorldPositionLow.xyz);
|
|
PrimitiveData.ActorWorldPositionTO = MakeLWCVector3(Primitive.ActorWorldPositionHigh.xyz, Primitive.ActorWorldPositionLow.xyz);
|
|
PrimitiveData.ObjectWorldPosition = DFFromTileOffset(PrimitiveData.ObjectWorldPositionTO);
|
|
PrimitiveData.ActorWorldPosition = DFFromTileOffset(PrimitiveData.ActorWorldPositionTO);
|
|
#else
|
|
PrimitiveData.ObjectWorldPosition = MakeDFVector3(Primitive.ObjectWorldPositionHighAndRadius.xyz, Primitive.ObjectWorldPositionLow.xyz);
|
|
PrimitiveData.ActorWorldPosition = MakeDFVector3(Primitive.ActorWorldPositionHigh.xyz, Primitive.ActorWorldPositionLow.xyz);
|
|
#endif
|
|
PrimitiveData.LightmapUVIndex = Primitive.LightmapUVIndex;
|
|
PrimitiveData.ObjectOrientation = Primitive.ObjectOrientation;
|
|
PrimitiveData.LightmapDataIndex = Primitive.LightmapDataIndex;
|
|
PrimitiveData.NonUniformScale = Primitive.NonUniformScale;
|
|
PrimitiveData.PreSkinnedLocalBoundsMin = Primitive.PreSkinnedLocalBoundsMin;
|
|
PrimitiveData.NaniteResourceID = Primitive.NaniteResourceID;
|
|
PrimitiveData.PreSkinnedLocalBoundsMax = Primitive.PreSkinnedLocalBoundsMax;
|
|
PrimitiveData.NaniteHierarchyOffset = Primitive.NaniteHierarchyOffset;
|
|
PrimitiveData.NaniteAssemblyTransformOffset = Primitive.NaniteAssemblyTransformOffset;
|
|
PrimitiveData.LocalObjectBoundsMin = Primitive.LocalObjectBoundsMin;
|
|
PrimitiveData.ObjectBoundsY = Primitive.ObjectBoundsY;
|
|
PrimitiveData.LocalObjectBoundsMax = Primitive.LocalObjectBoundsMax;
|
|
PrimitiveData.ObjectBoundsZ = Primitive.ObjectBoundsZ;
|
|
PrimitiveData.InstancePayloadDataOffset = Primitive.InstancePayloadDataOffset;
|
|
PrimitiveData.InstancePayloadDataStride = Primitive.InstancePayloadDataStride;
|
|
PrimitiveData.InstancePayloadExtensionSize = Primitive.InstancePayloadExtensionSize;
|
|
PrimitiveData.InstanceLocalBoundsCenter = Primitive.InstanceLocalBoundsCenter;
|
|
PrimitiveData.InstanceLocalBoundsExtent = Primitive.InstanceLocalBoundsExtent;
|
|
PrimitiveData.WireframeColor = UnpackColorRGB24(Primitive.WireframeAndPrimitiveColor.x);
|
|
PrimitiveData.PrimitiveColor = UnpackColorRGB24(Primitive.WireframeAndPrimitiveColor.y);
|
|
PrimitiveData.PackedNaniteFlags = Primitive.PackedNaniteFlags;
|
|
PrimitiveData.InstanceDrawDistanceMinMaxSquared = Primitive.InstanceDrawDistanceMinMaxSquared;
|
|
PrimitiveData.InstanceWPODisableDistanceSquared = Primitive.InstanceWPODisableDistanceSquared;
|
|
PrimitiveData.PersistentPrimitiveIndex = Primitive.PersistentPrimitiveIndex;
|
|
PrimitiveData.NaniteRayTracingDataOffset = Primitive.NaniteRayTracingDataOffset;
|
|
PrimitiveData.MaxWPOExtent = Primitive.MaxWPOExtent;
|
|
PrimitiveData.MinMaterialDisplacement = Primitive.MinMaterialDisplacement;
|
|
PrimitiveData.MaxMaterialDisplacement = Primitive.MaxMaterialDisplacement;
|
|
PrimitiveData.CustomStencilValueAndMask = Primitive.CustomStencilValueAndMask;
|
|
PrimitiveData.PixelProgrammableDistanceSquared = Primitive.PixelProgrammableDistanceSquared;
|
|
PrimitiveData.MaterialDisplacementFadeOutSize = Primitive.MaterialDisplacementFadeOutSize;
|
|
PrimitiveData.MeshPaintTextureDescriptor = Primitive.MeshPaintTextureDescriptor;
|
|
PrimitiveData.MaterialCacheTextureDescriptor = Primitive.MaterialCacheTextureDescriptor;
|
|
PrimitiveData.AnimationMinScreenSize = Primitive.AnimationMinScreenSize;
|
|
|
|
UNROLL
|
|
for (uint DataIndex = 0; DataIndex < NUM_CUSTOM_PRIMITIVE_DATA; ++DataIndex)
|
|
{
|
|
PrimitiveData.CustomPrimitiveData[DataIndex] = Primitive.CustomPrimitiveData[DataIndex];
|
|
}
|
|
|
|
return PrimitiveData;
|
|
}
|
|
|
|
// Unpacked AoS layout - see FInstanceSceneShaderData::Setup() for SoA packed layout.
|
|
#if USE_EDITOR_SHADERS
|
|
struct FInstanceSceneEditorData
|
|
{
|
|
float3 HitProxyId;
|
|
uint HitProxyPacked;
|
|
bool bIsSelected;
|
|
};
|
|
#endif
|
|
|
|
struct FInstanceSceneData
|
|
{
|
|
uint InstanceId; // Note: derived data (not loaded from scene, but set to the ID used to load the data for convenience).
|
|
FDFMatrix LocalToWorld;
|
|
FDFMatrix PrevLocalToWorld;
|
|
FDFInverseMatrix WorldToLocal;
|
|
float4 NonUniformScale;
|
|
float3 InvNonUniformScale;
|
|
float DeterminantSign;
|
|
float3 LocalBoundsCenter;
|
|
uint PrimitiveId;
|
|
uint RelativeId;
|
|
uint PayloadDataOffset;
|
|
uint PayloadExtensionOffset;
|
|
uint PayloadExtensionSize;
|
|
float3 LocalBoundsExtent;
|
|
uint LastUpdateSceneFrameNumber;
|
|
uint NaniteRuntimeResourceID;
|
|
uint NaniteHierarchyOffset;
|
|
uint NaniteAssemblyTransformOffset;
|
|
uint SkinningData;
|
|
float RandomID;
|
|
#if ENABLE_PER_INSTANCE_CUSTOM_DATA
|
|
uint CustomDataOffset;
|
|
uint CustomDataCount;
|
|
#endif
|
|
#if 1 //NEEDS_LIGHTMAP_COORDINATE // TODO: Fix Me
|
|
float4 LightMapAndShadowMapUVBias;
|
|
#endif
|
|
bool ValidInstance;
|
|
uint Flags;
|
|
|
|
#if USE_EDITOR_SHADERS
|
|
FInstanceSceneEditorData EditorData;
|
|
#endif
|
|
};
|
|
|
|
#if FEATURE_LEVEL >= FEATURE_LEVEL_SM6 || PLATFORM_SUPPORTS_SM6_0_WAVE_OPERATIONS
|
|
FInstanceSceneData WaveReadLaneAt(FInstanceSceneData In, uint SrcIndex)
|
|
{
|
|
FInstanceSceneData Result;
|
|
|
|
Result.InstanceId = WaveReadLaneAt(In.InstanceId, SrcIndex);
|
|
Result.LocalToWorld = WaveReadLaneAt(In.LocalToWorld, SrcIndex);
|
|
Result.PrevLocalToWorld = WaveReadLaneAt(In.PrevLocalToWorld, SrcIndex);
|
|
Result.WorldToLocal = WaveReadLaneAt(In.WorldToLocal, SrcIndex);
|
|
Result.NonUniformScale = WaveReadLaneAt(In.NonUniformScale, SrcIndex);
|
|
Result.InvNonUniformScale = WaveReadLaneAt(In.InvNonUniformScale, SrcIndex);
|
|
Result.DeterminantSign = WaveReadLaneAt(In.DeterminantSign, SrcIndex);
|
|
Result.LocalBoundsCenter = WaveReadLaneAt(In.LocalBoundsCenter, SrcIndex);
|
|
Result.PrimitiveId = WaveReadLaneAt(In.PrimitiveId, SrcIndex);
|
|
Result.RelativeId = WaveReadLaneAt(In.RelativeId, SrcIndex);
|
|
Result.PayloadDataOffset = WaveReadLaneAt(In.PayloadDataOffset, SrcIndex);
|
|
Result.PayloadExtensionOffset = WaveReadLaneAt(In.PayloadExtensionOffset, SrcIndex);
|
|
Result.PayloadExtensionSize = WaveReadLaneAt(In.PayloadExtensionSize, SrcIndex);
|
|
Result.LocalBoundsExtent = WaveReadLaneAt(In.LocalBoundsExtent, SrcIndex);
|
|
Result.LocalBoundsCenter = WaveReadLaneAt(In.LocalBoundsCenter, SrcIndex);
|
|
Result.PrimitiveId = WaveReadLaneAt(In.PrimitiveId, SrcIndex);
|
|
Result.RelativeId = WaveReadLaneAt(In.RelativeId, SrcIndex);
|
|
Result.PayloadDataOffset = WaveReadLaneAt(In.PayloadDataOffset, SrcIndex);
|
|
Result.PayloadExtensionOffset = WaveReadLaneAt(In.PayloadExtensionOffset, SrcIndex);
|
|
Result.PayloadExtensionSize = WaveReadLaneAt(In.PayloadExtensionSize, SrcIndex);
|
|
Result.LocalBoundsExtent = WaveReadLaneAt(In.LocalBoundsExtent, SrcIndex);
|
|
Result.LastUpdateSceneFrameNumber = WaveReadLaneAt(In.LastUpdateSceneFrameNumber, SrcIndex);
|
|
Result.NaniteRuntimeResourceID = WaveReadLaneAt(In.NaniteRuntimeResourceID, SrcIndex);
|
|
Result.NaniteHierarchyOffset = WaveReadLaneAt(In.NaniteHierarchyOffset, SrcIndex);
|
|
Result.NaniteAssemblyTransformOffset = WaveReadLaneAt(In.NaniteAssemblyTransformOffset, SrcIndex);
|
|
Result.SkinningData = WaveReadLaneAt(In.SkinningData, SrcIndex);
|
|
Result.RandomID = WaveReadLaneAt(In.RandomID, SrcIndex);
|
|
#if ENABLE_PER_INSTANCE_CUSTOM_DATA
|
|
Result.CustomDataOffset = WaveReadLaneAt(In.CustomDataOffset, SrcIndex);
|
|
Result.CustomDataCount = WaveReadLaneAt(In.CustomDataCount, SrcIndex);
|
|
#endif
|
|
Result.LightMapAndShadowMapUVBias = WaveReadLaneAt(In.LightMapAndShadowMapUVBias, SrcIndex);
|
|
Result.ValidInstance = WaveReadLaneAt(In.ValidInstance, SrcIndex);
|
|
Result.Flags = WaveReadLaneAt(In.Flags, SrcIndex);
|
|
#if USE_EDITOR_SHADERS
|
|
//TODO
|
|
#endif
|
|
|
|
return Result;
|
|
}
|
|
#endif
|
|
|
|
#if VF_USE_PRIMITIVE_SCENE_DATA
|
|
|
|
#if SHADER_USES_PRIMITIVE_UBO
|
|
// no access to global buffers
|
|
#elif USE_GLOBAL_GPU_SCENE_DATA
|
|
StructuredBuffer<float4> GPUScenePrimitiveSceneData;
|
|
#elif USE_GPU_SCENE_DATA_RW
|
|
#if ENABLE_SCENE_DATA_DX11_UB_ERROR_WORKAROUND
|
|
RWStructuredBuffer<float4> GPUScenePrimitiveSceneDataRW;
|
|
#endif
|
|
|
|
#endif
|
|
|
|
float4 LoadPrimitivePrimitiveSceneDataElement(uint PrimitiveIndex, uint ItemIndex)
|
|
{
|
|
uint TargetIdx = PrimitiveIndex + ItemIndex;
|
|
|
|
#if SHADER_USES_PRIMITIVE_UBO
|
|
return 0; // FIXME LoadPrimitivePrimitiveSceneDataElement for UBO case
|
|
#elif USE_GLOBAL_GPU_SCENE_DATA
|
|
checkStructuredBufferAccessSlow(GPUScenePrimitiveSceneData, TargetIdx);
|
|
return GPUScenePrimitiveSceneData[TargetIdx];
|
|
#elif USE_GPU_SCENE_DATA_RW
|
|
#if ENABLE_SCENE_DATA_DX11_UB_ERROR_WORKAROUND
|
|
checkStructuredBufferAccessSlow(GPUScenePrimitiveSceneDataRW, TargetIdx);
|
|
return GPUScenePrimitiveSceneDataRW[TargetIdx];
|
|
#else
|
|
checkStructuredBufferAccessSlow(GPUSceneWriter.GPUScenePrimitiveSceneDataRW, TargetIdx);
|
|
return GPUSceneWriter.GPUScenePrimitiveSceneDataRW[TargetIdx];
|
|
#endif
|
|
|
|
#else
|
|
checkStructuredBufferAccessSlow(Scene.GPUScene.GPUScenePrimitiveSceneData, TargetIdx);
|
|
return Scene.GPUScene.GPUScenePrimitiveSceneData[TargetIdx];
|
|
#endif
|
|
}
|
|
|
|
#if SHADER_USES_PRIMITIVE_UBO
|
|
#include "SceneDataMobileLoader.ush"
|
|
#endif
|
|
|
|
// Fetch from scene primitive buffer
|
|
FPrimitiveSceneData GetPrimitiveData(uint PrimitiveId)
|
|
{
|
|
#if SHADER_USES_PRIMITIVE_UBO
|
|
#if USE_INSTANCE_CULLING
|
|
return GetPrimitiveDataFromUniformBuffer();
|
|
#else
|
|
return LoadPrimitiveDataUBO(PrimitiveId);
|
|
#endif
|
|
#elif (VERTEXSHADER && !PLATFORM_SUPPORTS_VERTEX_SHADER_SRVS)
|
|
// Vertex shaders may not have access to GPUScene on mobile. Use GetPrimitiveData(FVertexFactoryIntermediates Intermediates)
|
|
// TODO: need a way to report invalid usage, after all dead code elimination
|
|
return (FPrimitiveSceneData)0;
|
|
#else
|
|
FPrimitiveSceneData PrimitiveData = (FPrimitiveSceneData)0;
|
|
// Note: layout must match FPrimitiveSceneShaderData in C++
|
|
// Relying on optimizer to remove unused loads
|
|
uint PrimitiveIndex = PrimitiveId * PRIMITIVE_SCENE_DATA_STRIDE;
|
|
|
|
float3 PositionHigh = LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 1).xyz;
|
|
|
|
const float4x4 LocalToWorld = transpose(float4x4(
|
|
LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 2),
|
|
LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 3),
|
|
LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 4),
|
|
float4(0, 0, 0, 1)
|
|
));
|
|
|
|
const float4x4 PreviousLocalToWorld = transpose(float4x4(
|
|
LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 8),
|
|
LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 9),
|
|
LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 10),
|
|
float4(0, 0, 0, 1)
|
|
));
|
|
|
|
const float4x4 WorldToLocal = transpose(float4x4(
|
|
LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 5),
|
|
LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 6),
|
|
LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 7),
|
|
float4(0, 0, 0, 1)
|
|
));
|
|
|
|
const float4x4 PreviousWorldToLocal = transpose(float4x4(
|
|
LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 11),
|
|
LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 12),
|
|
LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 13),
|
|
float4(0, 0, 0, 1)
|
|
));
|
|
|
|
const float4x4 WorldToPreviousWorld = transpose(float4x4(
|
|
LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 14),
|
|
LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 15),
|
|
LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 16),
|
|
float4(0, 0, 0, 1)
|
|
));
|
|
|
|
float4 ObjectWorldPositionHighAndRadius = LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 18);
|
|
float4 ObjectWorldPositionLow = LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 19);
|
|
|
|
float4 ActorWorldPositionHigh = LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 20);
|
|
float4 ActorWorldPositionLow = LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 21);
|
|
|
|
PrimitiveData.Flags = asuint(LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 0).x);
|
|
PrimitiveData.InstanceSceneDataOffset = asuint(LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 0).y);
|
|
PrimitiveData.NumInstanceSceneDataEntries = asuint(LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 0).z);
|
|
PrimitiveData.SingleCaptureIndex = asuint(LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 0).w) & 0xFFFFu;
|
|
PrimitiveData.VisibilityFlags = asuint(LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 0).w) >> 16u;
|
|
PrimitiveData.PositionHigh = PositionHigh; // 1.xyz
|
|
PrimitiveData.PrimitiveComponentId = asuint(LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 1).w);
|
|
PrimitiveData.LocalToWorld = MakeDFMatrix4x3(PositionHigh, LocalToWorld);
|
|
PrimitiveData.WorldToLocal = MakeDFInverseMatrix4x3(PositionHigh, WorldToLocal);
|
|
PrimitiveData.PreviousLocalToWorld = MakeDFMatrix4x3(PositionHigh, PreviousLocalToWorld);
|
|
PrimitiveData.PreviousWorldToLocal = MakeDFInverseMatrix4x3(PositionHigh, PreviousWorldToLocal);
|
|
PrimitiveData.WorldToPreviousWorld = WorldToPreviousWorld;
|
|
PrimitiveData.InvNonUniformScale = LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 17).xyz;
|
|
PrimitiveData.ObjectBoundsX = LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 17).w;
|
|
PrimitiveData.ObjectRadius = ObjectWorldPositionHighAndRadius.w;
|
|
#if PRIMITIVE_HAS_TILEOFFSET_DATA
|
|
PrimitiveData.ObjectWorldPositionTO = MakeLWCVector3(ObjectWorldPositionHighAndRadius.xyz, ObjectWorldPositionLow.xyz);
|
|
PrimitiveData.ActorWorldPositionTO = MakeLWCVector3(ActorWorldPositionHigh.xyz, ActorWorldPositionLow.xyz);
|
|
PrimitiveData.ObjectWorldPosition = DFFromTileOffset(PrimitiveData.ObjectWorldPositionTO);
|
|
PrimitiveData.ActorWorldPosition = DFFromTileOffset(PrimitiveData.ActorWorldPositionTO);
|
|
#else
|
|
PrimitiveData.ObjectWorldPosition = MakeDFVector3(ObjectWorldPositionHighAndRadius.xyz, ObjectWorldPositionLow.xyz);
|
|
PrimitiveData.ActorWorldPosition = MakeDFVector3(ActorWorldPositionHigh.xyz, ActorWorldPositionLow.xyz);
|
|
#endif
|
|
PrimitiveData.MinMaterialDisplacement = LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 19).w;
|
|
PrimitiveData.MaxMaterialDisplacement = LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 20).w;
|
|
PrimitiveData.LightmapUVIndex = asuint(LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 21).w);
|
|
PrimitiveData.ObjectOrientation = LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 22).xyz;
|
|
PrimitiveData.LightmapDataIndex = asuint(LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 22).w);
|
|
PrimitiveData.NonUniformScale = LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 23);
|
|
PrimitiveData.PreSkinnedLocalBoundsMin = LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 24).xyz;
|
|
PrimitiveData.NaniteResourceID = asuint(LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 24).w);
|
|
PrimitiveData.PreSkinnedLocalBoundsMax = LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 25).xyz;
|
|
PrimitiveData.NaniteHierarchyOffset = asuint(LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 25).w);
|
|
PrimitiveData.LocalObjectBoundsMin = LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 26).xyz;
|
|
PrimitiveData.ObjectBoundsY = LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 26).w;
|
|
PrimitiveData.LocalObjectBoundsMax = LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 27).xyz;
|
|
PrimitiveData.ObjectBoundsZ = LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 27).w;
|
|
PrimitiveData.InstanceLocalBoundsCenter = LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 28).xyz;
|
|
PrimitiveData.InstancePayloadDataOffset = asuint(LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 28).w);
|
|
PrimitiveData.InstanceLocalBoundsExtent = LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 29).xyz;
|
|
PrimitiveData.InstancePayloadDataStride = asuint(LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 29).w) & 0x00FFFFFFu;
|
|
PrimitiveData.InstancePayloadExtensionSize = asuint(LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 29).w) >> 24;
|
|
PrimitiveData.WireframeColor = UnpackColorRGB24(LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 30).x);
|
|
PrimitiveData.PrimitiveColor = UnpackColorRGB24(LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 30).y);
|
|
PrimitiveData.PackedNaniteFlags = asuint(LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 30).z);
|
|
PrimitiveData.PersistentPrimitiveIndex = asint(LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 30).w);
|
|
PrimitiveData.InstanceDrawDistanceMinMaxSquared = LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 31).xy;
|
|
PrimitiveData.InstanceWPODisableDistanceSquared = LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 31).z;
|
|
PrimitiveData.NaniteRayTracingDataOffset = asuint(LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 31).w);
|
|
PrimitiveData.MaxWPOExtent = LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 32).x;
|
|
PrimitiveData.CustomStencilValueAndMask = asuint(LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 32).y);
|
|
PrimitiveData.PixelProgrammableDistanceSquared = LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 32).z;
|
|
PrimitiveData.MaterialDisplacementFadeOutSize = LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 32).w;
|
|
PrimitiveData.MeshPaintTextureDescriptor = asuint(LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 33).xy);
|
|
PrimitiveData.NaniteAssemblyTransformOffset = asuint(LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 33).z);
|
|
PrimitiveData.AnimationMinScreenSize = LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 33).w;
|
|
PrimitiveData.MaterialCacheTextureDescriptor = asuint(LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 34).xy);
|
|
|
|
// NOTE: Please make sure GetPrimitiveDataFromUniformBuffer() gets updated as well when adding new members here
|
|
|
|
// TODO: Move to associated array (and editor data) to shrink primitive data and better pack cachelines
|
|
UNROLL
|
|
for (uint DataIndex = 0; DataIndex < NUM_CUSTOM_PRIMITIVE_DATA; ++DataIndex)
|
|
{
|
|
PrimitiveData.CustomPrimitiveData[DataIndex] = LoadPrimitivePrimitiveSceneDataElement(PrimitiveIndex, 35 + DataIndex);
|
|
}
|
|
return PrimitiveData;
|
|
#endif
|
|
}
|
|
|
|
#else // !VF_USE_PRIMITIVE_SCENE_DATA
|
|
|
|
FPrimitiveSceneData GetPrimitiveData(uint PrimitiveId)
|
|
{
|
|
return GetPrimitiveDataFromUniformBuffer();
|
|
}
|
|
|
|
#endif // VF_USE_PRIMITIVE_SCENE_DATA
|
|
|
|
float GetPrimitive_DeterminantSign_FromFlags(uint Flags)
|
|
{
|
|
return CondMask(Flags & PRIMITIVE_SCENE_DATA_FLAG_DETERMINANT_SIGN, -1.0f, 1.0f);
|
|
}
|
|
|
|
float GetPrimitive_DeterminantSign(uint PrimitiveId)
|
|
{
|
|
return GetPrimitive_DeterminantSign_FromFlags(GetPrimitiveData(PrimitiveId).Flags);
|
|
}
|
|
|
|
#if VF_USE_PRIMITIVE_SCENE_DATA
|
|
float GetPrimitive_DeterminantSign(FPrimitiveSceneData Primitive)
|
|
{
|
|
return GetPrimitive_DeterminantSign_FromFlags(Primitive.Flags);
|
|
}
|
|
#endif
|
|
|
|
float GetPrimitive_PerObjectGBufferData_FromFlags(uint Flags)
|
|
{
|
|
const float CapsuleRepresentation = CondMask(Flags & PRIMITIVE_SCENE_DATA_FLAG_HAS_CAPSULE_REPRESENTATION, 1.0f, 0.0f);
|
|
const float CastContactShadow = CondMask(Flags & PRIMITIVE_SCENE_DATA_FLAG_HAS_CAST_CONTACT_SHADOW, 1.0f, 0.0f);
|
|
return (2.0f * CapsuleRepresentation + CastContactShadow) / 3.0f;
|
|
}
|
|
|
|
float GetPrimitive_PerObjectGBufferData(uint PrimitiveId)
|
|
{
|
|
return GetPrimitive_PerObjectGBufferData_FromFlags(GetPrimitiveData(PrimitiveId).Flags);
|
|
}
|
|
|
|
#if VF_USE_PRIMITIVE_SCENE_DATA
|
|
float GetPrimitive_PerObjectGBufferData(FPrimitiveSceneData Primitive)
|
|
{
|
|
return GetPrimitive_PerObjectGBufferData_FromFlags(Primitive.Flags);
|
|
}
|
|
#endif
|
|
|
|
uint GetPrimitive_LightingChannelMask_FromFlags(uint Flags)
|
|
{
|
|
const uint Channel0 = CondMask(Flags & PRIMITIVE_SCENE_DATA_FLAG_LIGHTING_CHANNEL_0, 1u, 0u);
|
|
const uint Channel1 = CondMask(Flags & PRIMITIVE_SCENE_DATA_FLAG_LIGHTING_CHANNEL_1, 1u, 0u);
|
|
const uint Channel2 = CondMask(Flags & PRIMITIVE_SCENE_DATA_FLAG_LIGHTING_CHANNEL_2, 1u, 0u);
|
|
return (Channel0 | (Channel1 << 1u) | (Channel2 << 2u));
|
|
}
|
|
|
|
uint GetPrimitive_LightingChannelMask(uint PrimitiveId)
|
|
{
|
|
return GetPrimitive_LightingChannelMask_FromFlags(GetPrimitiveData(PrimitiveId).Flags);
|
|
}
|
|
|
|
#if VF_USE_PRIMITIVE_SCENE_DATA
|
|
uint GetPrimitive_LightingChannelMask(FPrimitiveSceneData Primitive)
|
|
{
|
|
return GetPrimitive_LightingChannelMask_FromFlags(Primitive.Flags);
|
|
}
|
|
#endif
|
|
|
|
float3 UnpackHitProxyId(uint HitProxyPacked)
|
|
{
|
|
// BGR (dword) -> RGA (float)
|
|
return float3
|
|
(
|
|
float((HitProxyPacked ) & 0xFF),
|
|
float((HitProxyPacked >> 8u) & 0xFF),
|
|
float((HitProxyPacked >> 16u) & 0xFF)
|
|
) * (1.0f / 255.0f);
|
|
}
|
|
|
|
struct FInstancePayloadDataOffsets
|
|
{
|
|
uint HierarchyOffset;
|
|
uint EditorData;
|
|
uint LocalBounds;
|
|
uint DynamicData;
|
|
uint SkinningData;
|
|
uint LightShadowUVBias;
|
|
uint PayloadExtension;
|
|
uint CustomData;
|
|
};
|
|
|
|
#if SHADER_USES_PRIMITIVE_UBO
|
|
// no access to global buffers
|
|
#elif USE_GLOBAL_GPU_SCENE_DATA
|
|
StructuredBuffer<float4> GPUSceneInstanceSceneData;
|
|
StructuredBuffer<float4> GPUSceneInstancePayloadData;
|
|
uint GPUSceneFrameNumber;
|
|
uint GPUSceneMaxPersistentPrimitiveIndex;
|
|
uint GPUSceneInstanceDataTileSizeLog2;
|
|
uint GPUSceneInstanceDataTileSizeMask;
|
|
uint GPUSceneInstanceDataTileStride;
|
|
|
|
uint GPUSceneMaxAllocatedInstanceId;
|
|
uint GPUSceneNumLightmapDataItems;
|
|
#elif USE_GPU_SCENE_DATA_RW
|
|
#if ENABLE_SCENE_DATA_DX11_UB_ERROR_WORKAROUND
|
|
RWStructuredBuffer<float4> GPUSceneInstanceSceneDataRW;
|
|
RWStructuredBuffer<float4> GPUSceneInstancePayloadDataRW;
|
|
uint GPUSceneFrameNumber;
|
|
uint GPUSceneInstanceDataTileSizeLog2;
|
|
uint GPUSceneInstanceDataTileSizeMask;
|
|
uint GPUSceneInstanceDataTileStride;
|
|
uint GPUSceneMaxPersistentPrimitiveIndex;
|
|
uint GPUSceneMaxAllocatedInstanceId;
|
|
uint GPUSceneNumLightmapDataItems;
|
|
#endif
|
|
#endif
|
|
|
|
struct FSceneData
|
|
{
|
|
uint FrameNumber;
|
|
/** Upper bound that includes all the IDs of allocated primitives in the Scene (NOT dynamic). This is always >= the number of primitives in the scene as there may be holes. */
|
|
uint MaxPersistentPrimitiveIndex;
|
|
/** Upper bound that includes all the IDs of ALL allocated instances (scene & dynamic). This is always >= the number of instances in the scene as there may be holes. */
|
|
uint MaxAllocatedInstanceId;
|
|
|
|
uint NumLightmapDataItems;
|
|
|
|
// implementation details, use at own risk
|
|
uint InstanceDataTileSizeLog2;
|
|
uint InstanceDataTileSizeMask;
|
|
uint InstanceDataTileStride;
|
|
};
|
|
|
|
FSceneData GetSceneData()
|
|
{
|
|
FSceneData SceneData;
|
|
#if USE_GLOBAL_GPU_SCENE_DATA
|
|
SceneData.FrameNumber = GPUSceneFrameNumber;
|
|
SceneData.InstanceDataTileSizeLog2 = GPUSceneInstanceDataTileSizeLog2;
|
|
SceneData.InstanceDataTileSizeMask = GPUSceneInstanceDataTileSizeMask;
|
|
SceneData.InstanceDataTileStride = GPUSceneInstanceDataTileStride;
|
|
SceneData.MaxPersistentPrimitiveIndex = GPUSceneMaxPersistentPrimitiveIndex;
|
|
SceneData.MaxAllocatedInstanceId = GPUSceneMaxAllocatedInstanceId;
|
|
SceneData.NumLightmapDataItems = GPUSceneNumLightmapDataItems;
|
|
#elif USE_GPU_SCENE_DATA_RW
|
|
|
|
#if ENABLE_SCENE_DATA_DX11_UB_ERROR_WORKAROUND
|
|
SceneData.FrameNumber = GPUSceneFrameNumber;
|
|
SceneData.InstanceDataTileSizeLog2 = GPUSceneInstanceDataTileSizeLog2;
|
|
SceneData.InstanceDataTileSizeMask = GPUSceneInstanceDataTileSizeMask;
|
|
SceneData.InstanceDataTileStride = GPUSceneInstanceDataTileStride;
|
|
SceneData.MaxPersistentPrimitiveIndex = GPUSceneMaxPersistentPrimitiveIndex;
|
|
SceneData.MaxAllocatedInstanceId = GPUSceneMaxAllocatedInstanceId;
|
|
SceneData.NumLightmapDataItems = GPUSceneNumLightmapDataItems;
|
|
#else
|
|
SceneData.FrameNumber = GPUSceneWriter.GPUSceneFrameNumber;
|
|
SceneData.InstanceDataTileSizeLog2 = GPUSceneWriter.InstanceDataTileSizeLog2;
|
|
SceneData.InstanceDataTileSizeMask = GPUSceneWriter.InstanceDataTileSizeMask;
|
|
SceneData.InstanceDataTileStride = GPUSceneWriter.InstanceDataTileStride;
|
|
SceneData.MaxPersistentPrimitiveIndex = GPUSceneWriter.GPUSceneMaxPersistentPrimitiveIndex;
|
|
SceneData.MaxAllocatedInstanceId = GPUSceneWriter.GPUSceneMaxAllocatedInstanceId;
|
|
SceneData.NumLightmapDataItems = GPUSceneWriter.GPUSceneNumLightmapDataItems;
|
|
#endif
|
|
|
|
#else
|
|
SceneData.FrameNumber = Scene.GPUScene.GPUSceneFrameNumber;
|
|
SceneData.InstanceDataTileSizeLog2 = Scene.GPUScene.GPUSceneInstanceDataTileSizeLog2;
|
|
SceneData.InstanceDataTileSizeMask = Scene.GPUScene.GPUSceneInstanceDataTileSizeMask;
|
|
SceneData.InstanceDataTileStride = Scene.GPUScene.GPUSceneInstanceDataTileStride;
|
|
SceneData.MaxPersistentPrimitiveIndex = Scene.GPUScene.GPUSceneMaxPersistentPrimitiveIndex;
|
|
SceneData.MaxAllocatedInstanceId = Scene.GPUScene.GPUSceneMaxAllocatedInstanceId;
|
|
SceneData.NumLightmapDataItems = Scene.GPUScene.GPUSceneNumLightmapDataItems;
|
|
#endif
|
|
return SceneData;
|
|
}
|
|
|
|
uint GetGPUSceneFrameNumber()
|
|
{
|
|
return GetSceneData().FrameNumber;
|
|
}
|
|
|
|
/**
|
|
* Function to calculate the instance data index in the instance data buffer given the instance ID and array index
|
|
*/
|
|
uint CalcInstanceDataIndex(uint InstanceId, uint ArrayIndex)
|
|
{
|
|
FSceneData SceneData = GetSceneData();
|
|
uint TileId = InstanceId >> SceneData.InstanceDataTileSizeLog2;
|
|
uint IdInTile = InstanceId & SceneData.InstanceDataTileSizeMask;
|
|
return SceneData.InstanceDataTileStride * TileId + (ArrayIndex << SceneData.InstanceDataTileSizeLog2) + IdInTile;
|
|
}
|
|
|
|
float4 LoadInstanceSceneDataElement(uint Index)
|
|
{
|
|
#if SHADER_USES_PRIMITIVE_UBO
|
|
return 0; // FIXME LoadInstanceSceneDataElement for UBO case
|
|
#elif USE_GLOBAL_GPU_SCENE_DATA
|
|
return GPUSceneInstanceSceneData[Index];
|
|
#elif USE_GPU_SCENE_DATA_RW
|
|
|
|
#if ENABLE_SCENE_DATA_DX11_UB_ERROR_WORKAROUND
|
|
return GPUSceneInstanceSceneDataRW[Index];
|
|
#else
|
|
return GPUSceneWriter.GPUSceneInstanceSceneDataRW[Index];
|
|
#endif
|
|
#else
|
|
return Scene.GPUScene.GPUSceneInstanceSceneData[Index];
|
|
#endif
|
|
}
|
|
|
|
float4 LoadInstanceSceneDataElement(uint InstanceId, uint ArrayIndex)
|
|
{
|
|
return LoadInstanceSceneDataElement(CalcInstanceDataIndex(InstanceId, ArrayIndex));
|
|
}
|
|
|
|
float4 LoadInstancePayloadDataElement(uint Index)
|
|
{
|
|
#if SHADER_USES_PRIMITIVE_UBO
|
|
return 0; // FIXME LoadInstancePayloadDataElement for UBO case
|
|
#elif USE_GLOBAL_GPU_SCENE_DATA
|
|
return GPUSceneInstancePayloadData[Index];
|
|
#elif USE_GPU_SCENE_DATA_RW
|
|
|
|
#if ENABLE_SCENE_DATA_DX11_UB_ERROR_WORKAROUND
|
|
return GPUSceneInstancePayloadDataRW[Index];
|
|
#else
|
|
return GPUSceneWriter.GPUSceneInstancePayloadDataRW[Index];
|
|
#endif
|
|
#else
|
|
return Scene.GPUScene.GPUSceneInstancePayloadData[Index];
|
|
#endif
|
|
}
|
|
|
|
float4 LoadInstancePayloadExtensionElement(FInstanceSceneData InstanceData, uint Index)
|
|
{
|
|
checkSlow(InstanceData.PayloadExtensionOffset != INVALID_INSTANCE_PAYLOAD_OFFSET);
|
|
return LoadInstancePayloadDataElement(InstanceData.PayloadExtensionOffset + Index);
|
|
}
|
|
|
|
float4 LoadInstanceCustomDataElement(FInstanceSceneData SceneData, uint Float4Index)
|
|
{
|
|
#if ENABLE_PER_INSTANCE_CUSTOM_DATA
|
|
const uint NumCustomFloat4s = (SceneData.CustomDataCount + 3u) >> 2u;
|
|
if (SceneData.CustomDataOffset != 0xFFFFFFFFu && Float4Index < NumCustomFloat4s)
|
|
{
|
|
return LoadInstancePayloadDataElement(SceneData.CustomDataOffset + Float4Index);
|
|
}
|
|
#endif
|
|
|
|
return (float4)0.0f;
|
|
}
|
|
|
|
float LoadInstanceCustomDataFloat(FInstanceSceneData SceneData, uint FloatIndex)
|
|
{
|
|
#if ENABLE_PER_INSTANCE_CUSTOM_DATA
|
|
const uint Float4Index = FloatIndex >> 2u;
|
|
const uint ComponentIndex = FloatIndex % 4u;
|
|
const float4 Element = LoadInstanceCustomDataElement(SceneData, Float4Index);
|
|
return Element[ComponentIndex];
|
|
#else
|
|
return 0.0f;
|
|
#endif
|
|
}
|
|
|
|
// [Frisvad 2012, "Building an Orthonormal Basis from a 3D Unit Vector Without Normalization"]
|
|
void GetHemiOrthoBasis( inout float3 BasisX, inout float3 BasisY, float3 BasisZ )
|
|
{
|
|
float A = 1.0f / ( 1.0f + BasisZ.z );
|
|
float B = -BasisZ.x * BasisZ.y * A;
|
|
BasisX = float3( 1.0f - BasisZ.x * BasisZ.x * A, B, -BasisZ.x );
|
|
BasisY = float3( B, 1.0f - BasisZ.y * BasisZ.y * A, -BasisZ.y );
|
|
}
|
|
|
|
uint4 EncodeScaleAndRotation(float3 Scale, float3x3 Axis)
|
|
{
|
|
const uint ExpBits = 8;
|
|
const uint ExpBias = ( 1u << (ExpBits - 1) ) - 1;
|
|
const uint SignMantissaBits = 16;
|
|
const uint SignMantissaMask = (1u << SignMantissaBits) - 1;
|
|
const uint MantissaBits = SignMantissaBits - 1;
|
|
const float Sqrt2 = 1.41421356f;
|
|
|
|
uint4 Output;
|
|
|
|
// Rotation
|
|
{
|
|
if( Axis[2].z < 0.0f )
|
|
{
|
|
Axis[2] *= -1.0f;
|
|
Scale.z *= -1.0f;
|
|
}
|
|
|
|
float2 OctZ = UnitVectorToHemiOctahedron( Axis[2] );
|
|
|
|
float3 BasisX, BasisY;
|
|
GetHemiOrthoBasis( BasisX, BasisY, Axis[2] );
|
|
|
|
float X = dot(Axis[0], BasisX);
|
|
float Y = dot(Axis[0], BasisY);
|
|
|
|
float aX = abs( X );
|
|
float aY = abs( Y );
|
|
|
|
bool bSpinIsX = aX < aY;
|
|
float Spin0 = bSpinIsX ? X : Y;
|
|
float Spin1 = bSpinIsX ? Y : X;
|
|
float Sign1 = Spin1 < 0.0f ? -1.0f : 1.0f;
|
|
|
|
//Axis[0] *= Sign1;
|
|
Scale.x *= Sign1;
|
|
Spin0 *= Sign1;
|
|
|
|
float3 GeneratedY = cross(Axis[2], Axis[0]);
|
|
Scale.y *= dot( Axis[1], GeneratedY ) < 0.0f ? -Sign1 : Sign1;
|
|
|
|
// Avoid sign extension in shader by biasing
|
|
Output.x = (((int)round( OctZ.x * 32767.0f ) + 32768) & 0xFFFF) << 0;
|
|
Output.x |= (((int)round( OctZ.y * 32767.0f ) + 32768) & 0xFFFF) << 16;
|
|
|
|
// NOTE: Masking the bits with `& 0x7FFF` below causes the whole int to be optimized to 0 on some shader platforms.
|
|
// This is okay, as long as Spin0 is in [0, 1], which it should be.
|
|
Output.y = ((int)round( Spin0 * 16383.0f * Sqrt2 ) + 16384); // & 0x7FFF;
|
|
Output.y |= bSpinIsX ? (1u << 15) : 0;
|
|
}
|
|
|
|
// Scale
|
|
{
|
|
float MaxComponent = max3(abs(Scale.x), abs(Scale.y), abs(Scale.z));
|
|
uint MaxComponentExponent = (asuint(MaxComponent) & 0x7f800000u) >> 23;
|
|
|
|
// Need +1 because of losing the implicit leading bit of mantissa
|
|
// TODO assumes ExpBits == 8
|
|
// TODO clamp to expressable range
|
|
uint SharedExp = MaxComponentExponent + 1;
|
|
|
|
float ExpScale = asfloat(((127 + ExpBias + MantissaBits - SharedExp) & 0xFFu) << 23);
|
|
|
|
if( (uint)round( MaxComponent * ExpScale ) == (1u << MantissaBits) )
|
|
{
|
|
// Mantissa rounded up
|
|
SharedExp++;
|
|
ExpScale *= 0.5f;
|
|
}
|
|
|
|
Output.z = (((int)round( Scale.x * ExpScale ) + (1u << MantissaBits)) & 0xFFFFu) << 0;
|
|
Output.z |= (((int)round( Scale.y * ExpScale ) + (1u << MantissaBits)) & 0xFFFFu) << 16;
|
|
Output.w = (((int)round( Scale.z * ExpScale ) + (1u << MantissaBits)) & 0xFFFFu) << 0;
|
|
Output.w |= SharedExp << 16;
|
|
}
|
|
|
|
return Output;
|
|
}
|
|
|
|
uint4 EncodeScaleAndRotation( float3x3 InTransform )
|
|
{
|
|
float3 Scale = {
|
|
length(InTransform[0]),
|
|
length(InTransform[1]),
|
|
length(InTransform[2])
|
|
};
|
|
float3x3 Axis = {
|
|
InTransform[0] / Scale.x,
|
|
InTransform[1] / Scale.y,
|
|
InTransform[2] / Scale.z
|
|
};
|
|
return EncodeScaleAndRotation(Scale, Axis);
|
|
}
|
|
|
|
void EncodeTransform( float4x4 InTransform, inout uint4 OutRotationScale, inout float3 OutTranslation )
|
|
{
|
|
OutRotationScale = EncodeScaleAndRotation((float3x3)InTransform);
|
|
OutTranslation = InTransform[3].xyz;
|
|
}
|
|
|
|
float4x4 DecodeTransform( uint4 RotationScale, float3 Translation, inout float3 Scale )
|
|
{
|
|
float4x4 M = 0.0;
|
|
M[3].xyz = Translation;
|
|
M[3].w = 1.0;
|
|
|
|
// Rotation
|
|
{
|
|
float3 Rotation =
|
|
{
|
|
( RotationScale[0] >> 0 ) & 0xffff,
|
|
( RotationScale[0] >> 16 ) & 0xffff,
|
|
( RotationScale[1] >> 0 ) & 0x7fff
|
|
};
|
|
|
|
float2 OctZ = ( Rotation.xy - 32768 ) * (1.0f / 32767.0f);
|
|
float Spin0 = ( Rotation.z - 16384 ) * (0.70710678f / 16383.0f); // rsqrt(2)
|
|
bool bSpinIsX = RotationScale[1] & 0x8000;
|
|
|
|
M[2].xyz = HemiOctahedronToUnitVector( OctZ );
|
|
|
|
float3 BasisX, BasisY;
|
|
GetHemiOrthoBasis( BasisX, BasisY, M[2].xyz );
|
|
|
|
float Spin1 = sqrt( 1.0f - Spin0 * Spin0 );
|
|
float X = bSpinIsX ? Spin0 : Spin1;
|
|
float Y = bSpinIsX ? Spin1 : Spin0;
|
|
|
|
M[0].xyz = BasisX * X + BasisY * Y;
|
|
M[1].xyz = cross( M[2].xyz, M[0].xyz );
|
|
}
|
|
|
|
// Scale
|
|
{
|
|
const uint SignMantissaBits = 16;
|
|
const uint SignMantissaMask = (1u << SignMantissaBits) - 1;
|
|
const uint MantissaBits = SignMantissaBits - 1;
|
|
|
|
#if 0
|
|
uint SharedExp = RotationScale[3] >> 22;
|
|
|
|
float ExpScale = asfloat( ( SharedExp - MantissaBits ) << 23 );
|
|
|
|
int3 Mantissa =
|
|
{
|
|
( RotationScale[2] >> 0 ),
|
|
( RotationScale[2] >> 18 ) | ( RotationScale[3] << 14 ),
|
|
( RotationScale[3] >> 4 )
|
|
};
|
|
#else
|
|
uint SharedExp = RotationScale[3] >> 16;
|
|
|
|
float ExpScale = asfloat( ( SharedExp - MantissaBits ) << 23 );
|
|
|
|
uint3 Mantissa =
|
|
{
|
|
RotationScale[2] >> 0,
|
|
RotationScale[2] >> 16,
|
|
RotationScale[3] >> 0
|
|
};
|
|
|
|
#endif
|
|
Mantissa &= SignMantissaMask;
|
|
Scale = Mantissa;
|
|
Scale -= 1u << MantissaBits;
|
|
Scale *= ExpScale;
|
|
|
|
M[0] *= Scale[0];
|
|
M[1] *= Scale[1];
|
|
M[2] *= Scale[2];
|
|
}
|
|
|
|
return M;
|
|
}
|
|
|
|
// Helpers to pack/unpack the primitive ID and flags for the specified instance, which are packed together in a uint
|
|
void UnpackPrimitiveIdAndInstanceFlags(uint PackedPrimitiveIdAndFlags, inout uint OutPrimitiveId, inout uint OutInstanceFlags)
|
|
{
|
|
OutPrimitiveId = BitFieldExtractU32(PackedPrimitiveIdAndFlags, PRIMITIVE_ID_NUM_BITS, 0);
|
|
OutInstanceFlags = BitFieldExtractU32(PackedPrimitiveIdAndFlags, INSTANCE_SCENE_DATA_FLAGS_NUM_BITS, PRIMITIVE_ID_NUM_BITS);
|
|
}
|
|
|
|
uint PackPrimitiveIdAndInstanceFlags(uint PrimitiveId, uint InstanceFlags)
|
|
{
|
|
return (PrimitiveId & PRIMITIVE_ID_MASK) | (InstanceFlags << PRIMITIVE_ID_NUM_BITS);
|
|
}
|
|
|
|
void LoadInstancePrimitiveIdAndFlags(uint InstanceId, inout uint OutPrimitiveId, inout uint OutInstanceFlags)
|
|
{
|
|
const uint PackedPrimitiveIdAndFlags = asuint(LoadInstanceSceneDataElement(InstanceId, 0).x);
|
|
UnpackPrimitiveIdAndInstanceFlags(PackedPrimitiveIdAndFlags, OutPrimitiveId, OutInstanceFlags);
|
|
}
|
|
|
|
// Helpers to pack/unpack the instance relative ID and custom data count for the specified instance, which are packed together in a uint
|
|
void UnpackInstanceRelativeIdAndCustomDataCount(uint PackedRelativeIdAndCustomDataCount, inout uint OutRelativeId, inout uint OutCustomDataCount)
|
|
{
|
|
OutRelativeId = BitFieldExtractU32(PackedRelativeIdAndCustomDataCount, INSTANCE_RELATIVE_ID_NUM_BITS, 0);
|
|
OutCustomDataCount = BitFieldExtractU32(PackedRelativeIdAndCustomDataCount, INSTANCE_CUSTOM_DATA_COUNT_NUM_BITS, INSTANCE_RELATIVE_ID_NUM_BITS);
|
|
}
|
|
|
|
uint PackInstanceRelativeIdAndCustomDataCount(uint RelativeId, uint CustomDataCount)
|
|
{
|
|
return (RelativeId & INSTANCE_RELATIVE_ID_MASK) | (CustomDataCount << INSTANCE_RELATIVE_ID_NUM_BITS);
|
|
}
|
|
|
|
void LoadInstanceRelativeIdAndCustomDataCount(uint InstanceId, inout uint OutPrimitiveId, inout uint OutInstanceFlags)
|
|
{
|
|
const uint PackedRelativeIdAndCustomDataCount = asuint(LoadInstanceSceneDataElement(InstanceId, 0).y);
|
|
UnpackInstanceRelativeIdAndCustomDataCount(PackedRelativeIdAndCustomDataCount, OutPrimitiveId, OutInstanceFlags);
|
|
}
|
|
|
|
// Helpers for getting/setting the instance determinant sign from instance data flags
|
|
float GetInstanceDeterminantSignFromFlags(uint Flags)
|
|
{
|
|
// Scale.x * Scale.y * Scale.z < 0.0 ? -1.0 : 1.0;
|
|
return CondMask(Flags & INSTANCE_SCENE_DATA_FLAG_DETERMINANT_SIGN, -1.0f, 1.0f);
|
|
}
|
|
|
|
void SetInstanceDeterminantSignFlag(float Determinant, inout uint Flags)
|
|
{
|
|
if (Determinant < 0.0f)
|
|
{
|
|
Flags |= INSTANCE_SCENE_DATA_FLAG_DETERMINANT_SIGN;
|
|
}
|
|
else
|
|
{
|
|
Flags &= ~INSTANCE_SCENE_DATA_FLAG_DETERMINANT_SIGN;
|
|
}
|
|
}
|
|
|
|
// Determine the offsets into the payload data buffer for the given instance
|
|
FInstancePayloadDataOffsets GetInstancePayloadDataOffsets(uint PrimitiveId, uint Flags, uint InstanceRelativeId)
|
|
{
|
|
FPrimitiveSceneData PrimitiveData = GetPrimitiveData(PrimitiveId);
|
|
|
|
const uint PayloadDataRelativeOffset = InstanceRelativeId * PrimitiveData.InstancePayloadDataStride;
|
|
const uint PayloadDataGlobalOffset = PayloadDataRelativeOffset + PrimitiveData.InstancePayloadDataOffset;
|
|
|
|
const bool bHasHierarchyOffset = (Flags & INSTANCE_SCENE_DATA_FLAG_HAS_HIERARCHY_OFFSET) != 0u;
|
|
const bool bHasLocalBounds = (Flags & INSTANCE_SCENE_DATA_FLAG_HAS_LOCAL_BOUNDS) != 0u;
|
|
const bool bHasDynamicData = (Flags & INSTANCE_SCENE_DATA_FLAG_HAS_DYNAMIC_DATA) != 0u;
|
|
const bool bHasSkinningData = (Flags & INSTANCE_SCENE_DATA_FLAG_HAS_SKINNING_DATA) != 0u;
|
|
const bool bHasLightShadowUVBias = (Flags & INSTANCE_SCENE_DATA_FLAG_HAS_LIGHTSHADOW_UV_BIAS) != 0u;
|
|
const bool bHasCustomData = (Flags & INSTANCE_SCENE_DATA_FLAG_HAS_CUSTOM_DATA) != 0u;
|
|
const bool bHasPayloadExtension = (Flags & INSTANCE_SCENE_DATA_FLAG_HAS_PAYLOAD_EXTENSION) != 0u;
|
|
#if USE_EDITOR_SHADERS
|
|
const bool bHasEditorData = (Flags & INSTANCE_SCENE_DATA_FLAG_HAS_EDITOR_DATA) != 0u;
|
|
#else
|
|
const bool bHasEditorData = false;
|
|
#endif
|
|
|
|
uint CurOffset = PayloadDataGlobalOffset;
|
|
|
|
// Offsets are in float4s
|
|
FInstancePayloadDataOffsets Offsets;
|
|
Offsets.HierarchyOffset = INVALID_INSTANCE_PAYLOAD_OFFSET;
|
|
Offsets.EditorData = INVALID_INSTANCE_PAYLOAD_OFFSET;
|
|
Offsets.LocalBounds = INVALID_INSTANCE_PAYLOAD_OFFSET;
|
|
Offsets.DynamicData = INVALID_INSTANCE_PAYLOAD_OFFSET;
|
|
Offsets.SkinningData = INVALID_INSTANCE_PAYLOAD_OFFSET;
|
|
Offsets.LightShadowUVBias = INVALID_INSTANCE_PAYLOAD_OFFSET;
|
|
Offsets.PayloadExtension = INVALID_INSTANCE_PAYLOAD_OFFSET;
|
|
Offsets.CustomData = INVALID_INSTANCE_PAYLOAD_OFFSET;
|
|
|
|
// Hierarchy Offset -> float0.x
|
|
if (bHasHierarchyOffset)
|
|
{
|
|
Offsets.HierarchyOffset = CurOffset;
|
|
}
|
|
// SkinningData -> float0.y
|
|
if (bHasSkinningData)
|
|
{
|
|
Offsets.SkinningData = CurOffset;
|
|
}
|
|
// LocalBounds -> float0.zw & float1.xyzw
|
|
if (bHasLocalBounds)
|
|
{
|
|
Offsets.LocalBounds = CurOffset;
|
|
}
|
|
CurOffset += CondMask(bHasLocalBounds, 2u, CondMask(bHasHierarchyOffset || bHasSkinningData, 1u, 0u));
|
|
|
|
if (bHasDynamicData)
|
|
{
|
|
Offsets.DynamicData = CurOffset;
|
|
CurOffset += InstanceTransformSizeFloat4Count;
|
|
}
|
|
|
|
if (bHasEditorData)
|
|
{
|
|
Offsets.EditorData = CurOffset;
|
|
++CurOffset;
|
|
}
|
|
|
|
if (bHasLightShadowUVBias)
|
|
{
|
|
Offsets.LightShadowUVBias = CurOffset;
|
|
++CurOffset;
|
|
}
|
|
|
|
if (bHasPayloadExtension)
|
|
{
|
|
Offsets.PayloadExtension = CurOffset;
|
|
CurOffset += PrimitiveData.InstancePayloadExtensionSize;
|
|
}
|
|
|
|
if (bHasCustomData)
|
|
{
|
|
Offsets.CustomData = CurOffset;
|
|
}
|
|
|
|
return Offsets;
|
|
}
|
|
|
|
void ComputeInstanceDerivedData(inout FInstanceSceneData InstanceData, float3 PositionHigh, float4x4 LocalToRelativeWorld)
|
|
{
|
|
//
|
|
// Do not put any load operations here!
|
|
//
|
|
|
|
#if !PLATFORM_ALLOW_SCENE_DATA_COMPRESSED_TRANSFORMS && !SHADER_USES_PRIMITIVE_UBO
|
|
// Non-uniform scale must be computed from the transform because it was not already computed when decoding it (see below in GetInstanceSceneData)
|
|
float3 Scale2;
|
|
Scale2.x = length2(LocalToRelativeWorld[0].xyz);
|
|
Scale2.y = length2(LocalToRelativeWorld[1].xyz);
|
|
Scale2.z = length2(LocalToRelativeWorld[2].xyz);
|
|
|
|
InstanceData.InvNonUniformScale = rsqrt(Scale2);
|
|
InstanceData.NonUniformScale.xyz = Scale2 * InstanceData.InvNonUniformScale;
|
|
#endif
|
|
|
|
InstanceData.NonUniformScale.w = max3( InstanceData.NonUniformScale.x, InstanceData.NonUniformScale.y, InstanceData.NonUniformScale.z );
|
|
InstanceData.DeterminantSign = GetInstanceDeterminantSignFromFlags(InstanceData.Flags);
|
|
|
|
float4x4 RelativeWorldToLocal = LocalToRelativeWorld;
|
|
RelativeWorldToLocal[0].xyz *= Pow2(InstanceData.InvNonUniformScale.x);
|
|
RelativeWorldToLocal[1].xyz *= Pow2(InstanceData.InvNonUniformScale.y);
|
|
RelativeWorldToLocal[2].xyz *= Pow2(InstanceData.InvNonUniformScale.z);
|
|
RelativeWorldToLocal[3].xyz = 0.0f;
|
|
RelativeWorldToLocal = transpose(RelativeWorldToLocal);
|
|
RelativeWorldToLocal[3].xyz = mul(float4(-LocalToRelativeWorld[3].xyz, 0.0f), RelativeWorldToLocal).xyz;
|
|
InstanceData.WorldToLocal = MakeDFInverseMatrix(PositionHigh, RelativeWorldToLocal);
|
|
}
|
|
|
|
FInstanceSceneData GetInstanceSceneDataInternal(uint InstanceId, bool bCheckValid)
|
|
{
|
|
FInstanceSceneData InstanceData = (FInstanceSceneData)0;
|
|
|
|
InstanceData.InstanceId = InstanceId;
|
|
//
|
|
// NOTE: When changing the packed data layout, ensure that GPUScene/GPUSceneWriter.ush is kept in sync!
|
|
// Also, please update the GetInstanceSceneData function in GPUScene.cpp for validation purposes.
|
|
//
|
|
|
|
// Only process valid instances
|
|
LoadInstancePrimitiveIdAndFlags(InstanceId, InstanceData.PrimitiveId, InstanceData.Flags);
|
|
InstanceData.ValidInstance = InstanceData.PrimitiveId != INVALID_PRIMITIVE_ID &&
|
|
((InstanceData.Flags & INSTANCE_SCENE_DATA_FLAG_HIDDEN) == 0u);
|
|
|
|
BRANCH
|
|
if (!bCheckValid || InstanceData.ValidInstance)
|
|
{
|
|
uint CustomDataCount;
|
|
LoadInstanceRelativeIdAndCustomDataCount(InstanceId, InstanceData.RelativeId, CustomDataCount);
|
|
|
|
FInstancePayloadDataOffsets Offsets = GetInstancePayloadDataOffsets(InstanceData.PrimitiveId, InstanceData.Flags, InstanceData.RelativeId);
|
|
|
|
#if ENABLE_PER_INSTANCE_CUSTOM_DATA
|
|
InstanceData.CustomDataCount = CustomDataCount;
|
|
InstanceData.CustomDataOffset = Offsets.CustomData;
|
|
#endif
|
|
InstanceData.LastUpdateSceneFrameNumber = asuint(LoadInstanceSceneDataElement(InstanceId, 0).z);
|
|
InstanceData.RandomID = LoadInstanceSceneDataElement(InstanceId, 0).w;
|
|
|
|
FPrimitiveSceneData PrimitiveData = GetPrimitiveData(InstanceData.PrimitiveId);
|
|
|
|
float3 PositionHigh = PrimitiveData.PositionHigh;
|
|
|
|
#if PLATFORM_ALLOW_SCENE_DATA_COMPRESSED_TRANSFORMS
|
|
uint4 RotationScale = asuint(LoadInstanceSceneDataElement(InstanceId, 1));
|
|
float3 Translation = LoadInstanceSceneDataElement(InstanceId, 2).xyz;
|
|
float3 Scale = 0;
|
|
float4x4 LocalToRelativeWorld = DecodeTransform( RotationScale, Translation, Scale );
|
|
|
|
InstanceData.NonUniformScale.xyz = abs(Scale);
|
|
InstanceData.InvNonUniformScale = rcp(InstanceData.NonUniformScale.xyz);
|
|
#else
|
|
float4x4 LocalToRelativeWorld = transpose(
|
|
float4x4(
|
|
LoadInstanceSceneDataElement(InstanceId, 1),
|
|
LoadInstanceSceneDataElement(InstanceId, 2),
|
|
LoadInstanceSceneDataElement(InstanceId, 3),
|
|
float4(0.0f, 0.0f, 0.0f, 1.0f)
|
|
));
|
|
#endif
|
|
|
|
|
|
InstanceData.LocalToWorld = MakeDFMatrix(PositionHigh, LocalToRelativeWorld);
|
|
ComputeInstanceDerivedData(InstanceData, PositionHigh, LocalToRelativeWorld);
|
|
|
|
InstanceData.NaniteRuntimeResourceID = PrimitiveData.NaniteResourceID;
|
|
InstanceData.NaniteHierarchyOffset = PrimitiveData.NaniteHierarchyOffset;
|
|
InstanceData.NaniteAssemblyTransformOffset = PrimitiveData.NaniteAssemblyTransformOffset;
|
|
|
|
BRANCH
|
|
if (Offsets.HierarchyOffset != INVALID_INSTANCE_PAYLOAD_OFFSET)
|
|
{
|
|
const uint HierarchyRootOffset = asuint(LoadInstancePayloadDataElement(Offsets.HierarchyOffset)).x;
|
|
|
|
// Combine this instance's hierarchy offset with the primitive's root hierarchy offset
|
|
InstanceData.NaniteHierarchyOffset += HierarchyRootOffset;
|
|
}
|
|
|
|
InstanceData.SkinningData = 0;
|
|
BRANCH
|
|
if (Offsets.SkinningData != INVALID_INSTANCE_PAYLOAD_OFFSET)
|
|
{
|
|
InstanceData.SkinningData = asuint(LoadInstancePayloadDataElement(Offsets.SkinningData)).y;
|
|
}
|
|
|
|
#if USE_EDITOR_SHADERS
|
|
BRANCH
|
|
if (Offsets.EditorData != INVALID_INSTANCE_PAYLOAD_OFFSET)
|
|
{
|
|
const uint PackedEditorData = asuint(LoadInstancePayloadDataElement(Offsets.EditorData)).x;
|
|
// Note: .yzw are currently unused
|
|
|
|
InstanceData.EditorData.bIsSelected = (PackedEditorData >> 24u) != 0;
|
|
InstanceData.EditorData.HitProxyPacked = PackedEditorData & 0x00FFFFFFu;
|
|
InstanceData.EditorData.HitProxyId = UnpackHitProxyId(InstanceData.EditorData.HitProxyPacked);
|
|
}
|
|
#endif
|
|
|
|
BRANCH
|
|
if (Offsets.LocalBounds != INVALID_INSTANCE_PAYLOAD_OFFSET)
|
|
{
|
|
InstanceData.LocalBoundsCenter = float3(LoadInstancePayloadDataElement(Offsets.LocalBounds + 0).zw, LoadInstancePayloadDataElement(Offsets.LocalBounds + 1).x);
|
|
InstanceData.LocalBoundsExtent = LoadInstancePayloadDataElement(Offsets.LocalBounds + 1).yzw;
|
|
}
|
|
else
|
|
{
|
|
InstanceData.LocalBoundsCenter = PrimitiveData.InstanceLocalBoundsCenter;
|
|
InstanceData.LocalBoundsExtent = PrimitiveData.InstanceLocalBoundsExtent;
|
|
}
|
|
|
|
BRANCH
|
|
if (Offsets.DynamicData != INVALID_INSTANCE_PAYLOAD_OFFSET)
|
|
{
|
|
#if PLATFORM_ALLOW_SCENE_DATA_COMPRESSED_TRANSFORMS
|
|
uint4 PrevRotationScale = asuint(LoadInstancePayloadDataElement(Offsets.DynamicData + 0));
|
|
float3 PrevTranslation = LoadInstancePayloadDataElement(Offsets.DynamicData + 1).xyz;
|
|
float3 PrevScale = 0;
|
|
float4x4 PrevLocalToRelativeWorld = DecodeTransform(PrevRotationScale, PrevTranslation, PrevScale);
|
|
#else
|
|
float4x4 PrevLocalToRelativeWorld = transpose(
|
|
float4x4(
|
|
LoadInstancePayloadDataElement(Offsets.DynamicData + 0),
|
|
LoadInstancePayloadDataElement(Offsets.DynamicData + 1),
|
|
LoadInstancePayloadDataElement(Offsets.DynamicData + 2),
|
|
float4(0.0f, 0.0f, 0.0f, 1.0f)
|
|
));
|
|
#endif
|
|
|
|
InstanceData.PrevLocalToWorld = MakeDFMatrix(PositionHigh, PrevLocalToRelativeWorld);
|
|
}
|
|
else
|
|
{
|
|
// Since the instance is not dynamic, it cannot move relative to the primitive. However, the primitive itself can
|
|
// move. So we have to solve for previous Instance->World by taking primitive World->PrevWorld and using it to
|
|
// transform Instance->World into previous world space.
|
|
InstanceData.PrevLocalToWorld = MakeDFMatrix(PositionHigh, mul(LocalToRelativeWorld, PrimitiveData.WorldToPreviousWorld));
|
|
}
|
|
|
|
#if 1 //NEEDS_LIGHTMAP_COORDINATE
|
|
BRANCH
|
|
if (Offsets.LightShadowUVBias != INVALID_INSTANCE_PAYLOAD_OFFSET)
|
|
{
|
|
InstanceData.LightMapAndShadowMapUVBias = LoadInstancePayloadDataElement(Offsets.LightShadowUVBias);
|
|
}
|
|
#endif
|
|
|
|
InstanceData.PayloadExtensionOffset = Offsets.PayloadExtension;
|
|
InstanceData.PayloadExtensionSize = PrimitiveData.InstancePayloadExtensionSize;
|
|
}
|
|
|
|
return InstanceData;
|
|
}
|
|
|
|
FInstanceSceneData GetInstanceSceneData(uint InstanceId)
|
|
{
|
|
return GetInstanceSceneDataInternal(InstanceId, true);
|
|
}
|
|
|
|
FInstanceSceneData GetInstanceSceneDataUnchecked(uint InstanceId)
|
|
{
|
|
return GetInstanceSceneDataInternal(InstanceId, false);
|
|
}
|
|
|
|
// Instance culling flags
|
|
#define INSTANCE_CULLING_FLAG_EVALUATE_WPO (1u << 0u)
|
|
|
|
#define INSTANCE_CULLING_FLAGS_DEFAULT (INSTANCE_CULLING_FLAG_EVALUATE_WPO)
|
|
|
|
// NOTE: Currently limited to 4 bits to fit into instance culling data DWORD
|
|
#define INSTANCE_CULLING_FLAGS_NUM_BITS (4u)
|
|
#define INSTANCE_CULLING_FLAGS_MASK ((1u << INSTANCE_CULLING_FLAGS_NUM_BITS) - 1u)
|
|
|
|
uint PackInstanceCullingOutput(uint InstanceId, uint ViewIndex, uint CullingFlags)
|
|
{
|
|
// We store the view index (which can be used for instanced stereo or other multi-view in the top four bits)
|
|
// Note: this is an index of views for this render pass, not the view ID in the culling manager.
|
|
return (InstanceId & INSTANCE_ID_MASK) |
|
|
((CullingFlags & INSTANCE_CULLING_FLAGS_MASK) << INSTANCE_ID_NUM_BITS) |
|
|
(ViewIndex << (INSTANCE_ID_NUM_BITS + INSTANCE_CULLING_FLAGS_NUM_BITS));
|
|
}
|
|
|
|
void UnpackInstanceCullingOutput(uint PackedId, inout uint InstanceId, inout uint ViewIndex, inout uint CullingFlags)
|
|
{
|
|
InstanceId = PackedId & INSTANCE_ID_MASK;
|
|
ViewIndex = PackedId >> (INSTANCE_ID_NUM_BITS + INSTANCE_CULLING_FLAGS_NUM_BITS);
|
|
CullingFlags = (PackedId >> INSTANCE_ID_NUM_BITS) & INSTANCE_CULLING_FLAGS_MASK;
|
|
}
|
|
|
|
struct FSceneDataIntermediates
|
|
{
|
|
uint PrimitiveId;
|
|
uint InstanceId;
|
|
uint ViewIndex;
|
|
uint CullingFlags;
|
|
// Index from which we load the instance info, needed for the
|
|
uint InstanceIdLoadIndex;
|
|
FInstanceSceneData InstanceData;
|
|
FPrimitiveSceneData Primitive;
|
|
};
|
|
|
|
/**
|
|
* Load scene data once given the inputs require.
|
|
* InstanceIdOffset - supplied as a vertex stream with 0 instance step rate (constant for all instances)
|
|
* DrawInstanceId - the instance ID (SV_InstanceID) in the current draw
|
|
*/
|
|
#if VF_USE_PRIMITIVE_SCENE_DATA
|
|
FSceneDataIntermediates GetSceneDataIntermediates(uint InstanceIdOffset, uint DrawInstanceId)
|
|
{
|
|
FSceneDataIntermediates Intermediates = (FSceneDataIntermediates)0;
|
|
#if SHADER_USES_PRIMITIVE_UBO
|
|
#if USE_INSTANCE_CULLING
|
|
Intermediates.Primitive = GetPrimitiveDataFromUniformBuffer();
|
|
Intermediates.InstanceData = LoadInstanceDataUBO(DrawInstanceId);
|
|
ComputeInstanceDerivedData(Intermediates.InstanceData, Intermediates.InstanceData.LocalToWorld.PostTranslation, Intermediates.InstanceData.LocalToWorld.M);
|
|
#else
|
|
Intermediates.Primitive = LoadPrimitiveDataUBO(DrawInstanceId);
|
|
// Populate instance data from primitive data
|
|
Intermediates.InstanceData.LocalToWorld = Intermediates.Primitive.LocalToWorld;
|
|
Intermediates.InstanceData.PrevLocalToWorld = Intermediates.Primitive.PreviousLocalToWorld;
|
|
Intermediates.InstanceData.WorldToLocal = Intermediates.Primitive.WorldToLocal;
|
|
Intermediates.InstanceData.NonUniformScale = Intermediates.Primitive.NonUniformScale;
|
|
Intermediates.InstanceData.InvNonUniformScale = Intermediates.Primitive.InvNonUniformScale;
|
|
Intermediates.InstanceData.DeterminantSign = GetPrimitive_DeterminantSign_FromFlags(Intermediates.InstanceData.Flags);
|
|
#endif
|
|
Intermediates.InstanceData.LocalBoundsCenter = Intermediates.Primitive.InstanceLocalBoundsCenter;
|
|
Intermediates.InstanceData.LocalBoundsExtent = Intermediates.Primitive.InstanceLocalBoundsExtent;
|
|
Intermediates.InstanceData.ValidInstance = true;
|
|
Intermediates.CullingFlags = INSTANCE_CULLING_FLAGS_DEFAULT;
|
|
Intermediates.ViewIndex = 0U;
|
|
Intermediates.PrimitiveId = DrawInstanceId;
|
|
Intermediates.InstanceId = 0U;
|
|
#else
|
|
Intermediates.InstanceIdLoadIndex = InstanceIdOffset + DrawInstanceId;
|
|
// GPUCULL_TODO: workaround for the fact that DrawDynamicMeshPassPrivate et al. don't work with GPU-Scene instancing
|
|
// instead they mark the top bit in the primitive ID and disable auto instancing such that there is an 1:1:1
|
|
// drawcmd:primitive:instance. Then we can just look up the primitive and fetch the instance data index.
|
|
// GPUCULL_TODO: Workaround also used by HairStrandsMaterialCommon.ush
|
|
// We mark the PrimitiveID with the top bit in dynamic draw passes
|
|
if ((InstanceIdOffset & VF_TREAT_INSTANCE_ID_OFFSET_AS_PRIMITIVE_ID_FLAG) != 0U)
|
|
{
|
|
// mask off the flag
|
|
uint PrimitiveID = InstanceIdOffset & (VF_TREAT_INSTANCE_ID_OFFSET_AS_PRIMITIVE_ID_FLAG - 1U);
|
|
FPrimitiveSceneData PrimitiveData = GetPrimitiveData(PrimitiveID);
|
|
Intermediates.InstanceId = PrimitiveData.InstanceSceneDataOffset + DrawInstanceId;
|
|
Intermediates.ViewIndex = 0;
|
|
Intermediates.CullingFlags = INSTANCE_CULLING_FLAGS_DEFAULT;
|
|
}
|
|
#if RAYHITGROUPSHADER || RAYTRACING_DYNAMIC_GEOMETRY_CONVERTER
|
|
else
|
|
{
|
|
Intermediates.InstanceId = InstanceIdOffset + DrawInstanceId;
|
|
Intermediates.ViewIndex = 0;
|
|
Intermediates.CullingFlags = INSTANCE_CULLING_FLAGS_DEFAULT;
|
|
}
|
|
// Workaround for Vulkan SPIRV issue when "else" branch is not removed when it should, which leads to higher level code expecting InstanceCulling buffer to be bound.
|
|
// See: https://github.com/KhronosGroup/SPIRV-Tools/issues/4902
|
|
#elif USE_INSTANCE_CULLING_DATA
|
|
else
|
|
{
|
|
const uint PackedId = InstanceCulling.InstanceIdsBuffer[InstanceIdOffset + DrawInstanceId];
|
|
UnpackInstanceCullingOutput(PackedId, Intermediates.InstanceId, Intermediates.ViewIndex, Intermediates.CullingFlags);
|
|
}
|
|
#endif
|
|
|
|
Intermediates.InstanceData = GetInstanceSceneData(Intermediates.InstanceId);
|
|
Intermediates.PrimitiveId = Intermediates.InstanceData.PrimitiveId;
|
|
Intermediates.Primitive = GetPrimitiveData(Intermediates.PrimitiveId);
|
|
#endif //SHADER_USES_PRIMITIVE_UBO
|
|
|
|
return Intermediates;
|
|
}
|
|
#else
|
|
FSceneDataIntermediates GetSceneDataIntermediates()
|
|
{
|
|
FSceneDataIntermediates Intermediates = (FSceneDataIntermediates)0;
|
|
// Populate from Primitive uniform buffer
|
|
Intermediates.ViewIndex = 0U;
|
|
Intermediates.PrimitiveId = 0U;
|
|
Intermediates.InstanceId = 0U;
|
|
Intermediates.Primitive = GetPrimitiveDataFromUniformBuffer();
|
|
// Populate instance data from primitive data
|
|
Intermediates.InstanceData.LocalToWorld = Intermediates.Primitive.LocalToWorld;
|
|
Intermediates.InstanceData.PrevLocalToWorld = Intermediates.Primitive.PreviousLocalToWorld;
|
|
Intermediates.InstanceData.WorldToLocal = Intermediates.Primitive.WorldToLocal;
|
|
Intermediates.InstanceData.NonUniformScale = Intermediates.Primitive.NonUniformScale;
|
|
Intermediates.InstanceData.InvNonUniformScale = Intermediates.Primitive.InvNonUniformScale;
|
|
Intermediates.InstanceData.DeterminantSign = GetPrimitive_DeterminantSign_FromFlags(Intermediates.Primitive.Flags);
|
|
Intermediates.InstanceData.LocalBoundsCenter = (Intermediates.Primitive.LocalObjectBoundsMax + Intermediates.Primitive.LocalObjectBoundsMin) * 0.5f;
|
|
Intermediates.InstanceData.LocalBoundsExtent = (Intermediates.Primitive.LocalObjectBoundsMax - Intermediates.Primitive.LocalObjectBoundsMin) * 0.5f;
|
|
Intermediates.InstanceData.ValidInstance = true;
|
|
|
|
Intermediates.CullingFlags = INSTANCE_CULLING_FLAGS_DEFAULT;
|
|
|
|
return Intermediates;
|
|
}
|
|
#endif //VF_USE_PRIMITIVE_SCENE_DATA
|
|
|
|
//
|
|
// GPU Lights
|
|
//
|
|
|
|
#if USE_GLOBAL_GPU_SCENE_DATA
|
|
ByteAddressBuffer GPUSceneLights;
|
|
#endif
|
|
|
|
#if USE_GLOBAL_GPU_SCENE_DATA
|
|
#define GPUSceneLightsBuffer GPUSceneLights
|
|
#else
|
|
#define GPUSceneLightsBuffer Scene.GPUScene.GPUSceneLightData
|
|
#endif
|
|
|
|
FLightSceneData GetLightSceneData(uint LightId)
|
|
{
|
|
#if 0
|
|
return GPUSceneLightsBuffer.Load<FLightSceneData>(LightId * SIZEOF_LIGHT_SCENE_DATA);
|
|
#else
|
|
FLightSceneData Output;
|
|
|
|
uint Offset = LightId * (uint)SIZEOF_LIGHT_SCENE_DATA;
|
|
|
|
LoadAndIncrementOffset(Output.WorldPosition.High, GPUSceneLightsBuffer, Offset);
|
|
LoadAndIncrementOffset(Output.WorldPosition.Low, GPUSceneLightsBuffer, Offset);
|
|
LoadAndIncrementOffset(Output.InvRadius, GPUSceneLightsBuffer, Offset);
|
|
LoadAndIncrementOffset(Output.FalloffExponent, GPUSceneLightsBuffer, Offset);
|
|
LoadAndIncrementOffset(Output.Direction, GPUSceneLightsBuffer, Offset);
|
|
LoadAndIncrementOffset(Output.InverseExposureBlend, GPUSceneLightsBuffer, Offset);
|
|
LoadAndIncrementOffset(Output.Tangent, GPUSceneLightsBuffer, Offset);
|
|
LoadAndIncrementOffset(Output.SourceRadius, GPUSceneLightsBuffer, Offset);
|
|
LoadAndIncrementOffset(Output.SpotAngles, GPUSceneLightsBuffer, Offset);
|
|
LoadAndIncrementOffset(Output.SoftSourceRadius, GPUSceneLightsBuffer, Offset);
|
|
LoadAndIncrementOffset(Output.SourceLength, GPUSceneLightsBuffer, Offset);
|
|
LoadAndIncrementOffset(Output.RectLightBarnCosAngle, GPUSceneLightsBuffer, Offset);
|
|
LoadAndIncrementOffset(Output.RectLightBarnLength, GPUSceneLightsBuffer, Offset);
|
|
LoadAndIncrementOffset(Output.SpecularScale, GPUSceneLightsBuffer, Offset);
|
|
LoadAndIncrementOffset(Output.DiffuseScale, GPUSceneLightsBuffer, Offset);
|
|
#endif
|
|
|
|
return Output;
|
|
}
|