3207 lines
97 KiB
C++
3207 lines
97 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "D3D12EnhancedBarriers.h"
|
|
|
|
#if D3D12RHI_SUPPORTS_ENHANCED_BARRIERS
|
|
|
|
#include "RHIPipeline.h"
|
|
#include "RHICoreTransitions.h"
|
|
|
|
#include "D3D12Query.h"
|
|
#include "D3D12Resources.h"
|
|
#include "D3D12Util.h"
|
|
#include "D3D12CommandList.h"
|
|
#include "D3D12Texture.h"
|
|
#include "D3D12CommandContext.h"
|
|
#include "D3D12Adapter.h"
|
|
#include "D3D12RHIPrivate.h"
|
|
|
|
#include "Algo/Contains.h"
|
|
#include "Templates/Tuple.h"
|
|
#include "Containers/StaticArray.h"
|
|
|
|
#include <bit>
|
|
|
|
|
|
// @TODO - The EB spec has a hole in it where there's no valid layout to use for simultaneous read access
|
|
// from multiple pipes (e.g. async and gfx) that includes access bits that are gfx specific, like
|
|
// DEPTH_STENCIL_READ. Note that this makes the validation layers useless when the async pipe is used
|
|
// and it's questionable if any given driver will do something sane.
|
|
#define PLATFORM_REQUIRES_ENHANCED_BARRIERS_GFX_ONLY_READ_BITS_HACK 1
|
|
|
|
// @TODO - EB spec is inconsistent about whether D3D12_BARRIER_ACCESS_RAYTRACING_ACCELERATION_STRUCTURE_WRITE
|
|
// is compatible with D3D12_BARRIER_SYNC_RAYTRACING. Further, the validation layer in big windows
|
|
// doesn't like that pair. So, we'll omit it for windows for now.
|
|
#define PLATFORM_REQUIRES_SYNC_RAYTRACING_NOT_COMPATIBLE_WITH_ACCESS_AS_WRITE (PLATFORM_WINDOWS)
|
|
|
|
// @TODO - Validation layer incorrectly complains that D3D12_BARRIER_ACCESS_DEPTH_STENCIL_READ isn't
|
|
// compatible with D3D12_BARRIER_LAYOUT_DEPTH_STENCIL_WRITE. Until this is fixed we omit the
|
|
// read access bit if we're also setting the write access bit
|
|
#define PLATFORM_REQUIRES_LAYOUT_DEPTH_STENCIL_WRITE_NOT_COMPATIBLE_WITH_ACCESS_DEPTH_STENCIL_READ 1
|
|
|
|
// Debugging bits
|
|
#define D3D12_ENHANCED_BARRIERS_LOG_BARRIERS_WHEN_BATCHED 0
|
|
#define D3D12_ENHANCED_BARRIERS_LOG_BARRIERS_WHEN_FLUSHED 0
|
|
|
|
|
|
// Each platform must provide its own implementation of this
|
|
extern D3D12_BARRIER_LAYOUT GetSkipFastClearEliminateLayoutFlags();
|
|
|
|
struct FD3D12EnhancedBarriersTransitionData
|
|
{
|
|
ERHIPipeline SrcPipelines, DstPipelines;
|
|
ERHITransitionCreateFlags CreateFlags = ERHITransitionCreateFlags::None;
|
|
|
|
TArray<FRHITransitionInfo, TInlineAllocator<4, FConcurrentLinearArrayAllocator>> TransitionInfos;
|
|
TArray<TRHIPipelineArray<FD3D12SyncPointRef>, TInlineAllocator<MAX_NUM_GPUS>> SyncPoints;
|
|
};
|
|
|
|
struct FD3D12BarrierValues
|
|
{
|
|
D3D12_BARRIER_SYNC Sync;
|
|
D3D12_BARRIER_ACCESS Access;
|
|
D3D12_BARRIER_LAYOUT Layout;
|
|
|
|
bool operator==(const FD3D12BarrierValues& other) const
|
|
{
|
|
return Sync == other.Sync &&
|
|
Access == other.Access &&
|
|
Layout == other.Layout;
|
|
}
|
|
};
|
|
|
|
static bool operator==(
|
|
const D3D12_GLOBAL_BARRIER& BarrierA,
|
|
const D3D12_GLOBAL_BARRIER& BarrierB)
|
|
{
|
|
return BarrierA.SyncBefore == BarrierB.SyncBefore
|
|
&& BarrierA.SyncAfter == BarrierB.SyncAfter
|
|
&& BarrierA.AccessBefore == BarrierB.AccessBefore
|
|
&& BarrierA.AccessAfter == BarrierB.AccessAfter;
|
|
}
|
|
|
|
static bool operator==(
|
|
const D3D12_TEXTURE_BARRIER& BarrierA,
|
|
const D3D12_TEXTURE_BARRIER& BarrierB)
|
|
{
|
|
return BarrierA.SyncBefore == BarrierB.SyncBefore
|
|
&& BarrierA.SyncAfter == BarrierB.SyncAfter
|
|
&& BarrierA.AccessBefore == BarrierB.AccessBefore
|
|
&& BarrierA.AccessAfter == BarrierB.AccessAfter
|
|
&& BarrierA.LayoutBefore == BarrierB.LayoutBefore
|
|
&& BarrierA.LayoutAfter == BarrierB.LayoutAfter
|
|
&& BarrierA.pResource == BarrierB.pResource
|
|
&& BarrierA.Subresources.IndexOrFirstMipLevel == BarrierB.Subresources.IndexOrFirstMipLevel
|
|
&& BarrierA.Subresources.NumMipLevels == BarrierB.Subresources.NumMipLevels
|
|
&& BarrierA.Subresources.FirstArraySlice == BarrierB.Subresources.FirstArraySlice
|
|
&& BarrierA.Subresources.NumArraySlices == BarrierB.Subresources.NumArraySlices
|
|
&& BarrierA.Subresources.FirstPlane == BarrierB.Subresources.FirstPlane
|
|
&& BarrierA.Subresources.NumPlanes == BarrierB.Subresources.NumPlanes
|
|
&& BarrierA.Flags == BarrierB.Flags;
|
|
}
|
|
|
|
static bool operator==(
|
|
const D3D12_BUFFER_BARRIER& BarrierA,
|
|
const D3D12_BUFFER_BARRIER& BarrierB)
|
|
{
|
|
return BarrierA.SyncBefore == BarrierB.SyncBefore
|
|
&& BarrierA.SyncAfter == BarrierB.SyncAfter
|
|
&& BarrierA.AccessBefore == BarrierB.AccessBefore
|
|
&& BarrierA.AccessAfter == BarrierB.AccessAfter
|
|
&& BarrierA.pResource == BarrierB.pResource
|
|
&& BarrierA.Offset == BarrierB.Offset
|
|
&& BarrierA.Size == BarrierB.Size;
|
|
}
|
|
|
|
template <typename T, typename TString, typename ...TRest>
|
|
static constexpr void ConvertFlagsToStringImpl(
|
|
T InValue,
|
|
FString& InFlagString,
|
|
TPair<T, TString> InFirstFlag,
|
|
TRest... InRestFlags)
|
|
{
|
|
if (InValue & get<0>(InFirstFlag))
|
|
{
|
|
if (!InFlagString.IsEmpty())
|
|
{
|
|
InFlagString += TEXT("|");
|
|
}
|
|
InFlagString += get<1>(InFirstFlag);
|
|
}
|
|
|
|
if constexpr (sizeof...(InRestFlags))
|
|
{
|
|
ConvertFlagsToStringImpl(InValue, InFlagString, InRestFlags...);
|
|
}
|
|
}
|
|
|
|
template <typename T, typename TString, typename ...TRest>
|
|
static FString ConvertFlagsToString(
|
|
T InValue,
|
|
TPair<T, TString> InFirstFlag,
|
|
TRest... InRestFlags)
|
|
{
|
|
FString FlagString;
|
|
ConvertFlagsToStringImpl(InValue, FlagString, InFirstFlag, InRestFlags...);
|
|
if (FlagString.IsEmpty())
|
|
{
|
|
// Assume the first provided enum value is "None"
|
|
check(static_cast<std::underlying_type_t<T>>(get<0>(InFirstFlag)) == 0);
|
|
return get<1>(InFirstFlag);
|
|
}
|
|
return FlagString;
|
|
}
|
|
|
|
template <typename T, typename TString, typename ...TArgs>
|
|
static FString ConvertEnumToString(
|
|
T InValue,
|
|
TPair<T, TString> FirstArg,
|
|
TArgs... Args)
|
|
{
|
|
if (InValue == get<0>(FirstArg))
|
|
{
|
|
return get<1>(FirstArg);
|
|
}
|
|
|
|
if constexpr (sizeof...(Args))
|
|
{
|
|
return ConvertEnumToString<T>(InValue, Args...);
|
|
}
|
|
else
|
|
{
|
|
checkNoEntry();
|
|
return FString();
|
|
}
|
|
}
|
|
|
|
#define ENUMVAL(EnumVal) MakeTuple(EnumVal, TEXT(#EnumVal))
|
|
|
|
static FString ConvertToString(D3D12_BARRIER_SYNC InSync)
|
|
{
|
|
return ConvertFlagsToString(InSync,
|
|
ENUMVAL(D3D12_BARRIER_SYNC_NONE),
|
|
ENUMVAL(D3D12_BARRIER_SYNC_ALL),
|
|
ENUMVAL(D3D12_BARRIER_SYNC_DRAW),
|
|
ENUMVAL(D3D12_BARRIER_SYNC_INDEX_INPUT),
|
|
ENUMVAL(D3D12_BARRIER_SYNC_VERTEX_SHADING),
|
|
ENUMVAL(D3D12_BARRIER_SYNC_PIXEL_SHADING),
|
|
ENUMVAL(D3D12_BARRIER_SYNC_DEPTH_STENCIL),
|
|
ENUMVAL(D3D12_BARRIER_SYNC_RENDER_TARGET),
|
|
ENUMVAL(D3D12_BARRIER_SYNC_COMPUTE_SHADING),
|
|
ENUMVAL(D3D12_BARRIER_SYNC_RAYTRACING),
|
|
ENUMVAL(D3D12_BARRIER_SYNC_COPY),
|
|
ENUMVAL(D3D12_BARRIER_SYNC_RESOLVE),
|
|
ENUMVAL(D3D12_BARRIER_SYNC_EXECUTE_INDIRECT),
|
|
ENUMVAL(D3D12_BARRIER_SYNC_PREDICATION),
|
|
ENUMVAL(D3D12_BARRIER_SYNC_ALL_SHADING),
|
|
ENUMVAL(D3D12_BARRIER_SYNC_NON_PIXEL_SHADING),
|
|
ENUMVAL(D3D12_BARRIER_SYNC_EMIT_RAYTRACING_ACCELERATION_STRUCTURE_POSTBUILD_INFO),
|
|
ENUMVAL(D3D12_BARRIER_SYNC_CLEAR_UNORDERED_ACCESS_VIEW),
|
|
ENUMVAL(D3D12_BARRIER_SYNC_VIDEO_DECODE),
|
|
ENUMVAL(D3D12_BARRIER_SYNC_VIDEO_PROCESS),
|
|
ENUMVAL(D3D12_BARRIER_SYNC_VIDEO_ENCODE),
|
|
ENUMVAL(D3D12_BARRIER_SYNC_BUILD_RAYTRACING_ACCELERATION_STRUCTURE),
|
|
ENUMVAL(D3D12_BARRIER_SYNC_COPY_RAYTRACING_ACCELERATION_STRUCTURE),
|
|
ENUMVAL(D3D12_BARRIER_SYNC_SPLIT));
|
|
}
|
|
|
|
static FString ConvertToString(D3D12_BARRIER_ACCESS InAccess)
|
|
{
|
|
return ConvertFlagsToString(InAccess,
|
|
ENUMVAL(D3D12_BARRIER_ACCESS_COMMON),
|
|
ENUMVAL(D3D12_BARRIER_ACCESS_VERTEX_BUFFER),
|
|
ENUMVAL(D3D12_BARRIER_ACCESS_CONSTANT_BUFFER),
|
|
ENUMVAL(D3D12_BARRIER_ACCESS_INDEX_BUFFER),
|
|
ENUMVAL(D3D12_BARRIER_ACCESS_RENDER_TARGET),
|
|
ENUMVAL(D3D12_BARRIER_ACCESS_UNORDERED_ACCESS),
|
|
ENUMVAL(D3D12_BARRIER_ACCESS_DEPTH_STENCIL_WRITE),
|
|
ENUMVAL(D3D12_BARRIER_ACCESS_DEPTH_STENCIL_READ),
|
|
ENUMVAL(D3D12_BARRIER_ACCESS_SHADER_RESOURCE),
|
|
ENUMVAL(D3D12_BARRIER_ACCESS_STREAM_OUTPUT),
|
|
ENUMVAL(D3D12_BARRIER_ACCESS_INDIRECT_ARGUMENT),
|
|
ENUMVAL(D3D12_BARRIER_ACCESS_PREDICATION),
|
|
ENUMVAL(D3D12_BARRIER_ACCESS_COPY_DEST),
|
|
ENUMVAL(D3D12_BARRIER_ACCESS_COPY_SOURCE),
|
|
ENUMVAL(D3D12_BARRIER_ACCESS_RESOLVE_DEST),
|
|
ENUMVAL(D3D12_BARRIER_ACCESS_RESOLVE_SOURCE),
|
|
ENUMVAL(D3D12_BARRIER_ACCESS_RAYTRACING_ACCELERATION_STRUCTURE_READ),
|
|
ENUMVAL(D3D12_BARRIER_ACCESS_RAYTRACING_ACCELERATION_STRUCTURE_WRITE),
|
|
ENUMVAL(D3D12_BARRIER_ACCESS_SHADING_RATE_SOURCE),
|
|
ENUMVAL(D3D12_BARRIER_ACCESS_VIDEO_DECODE_READ),
|
|
ENUMVAL(D3D12_BARRIER_ACCESS_VIDEO_DECODE_WRITE),
|
|
ENUMVAL(D3D12_BARRIER_ACCESS_VIDEO_PROCESS_READ),
|
|
ENUMVAL(D3D12_BARRIER_ACCESS_VIDEO_PROCESS_WRITE),
|
|
ENUMVAL(D3D12_BARRIER_ACCESS_VIDEO_ENCODE_READ),
|
|
ENUMVAL(D3D12_BARRIER_ACCESS_VIDEO_ENCODE_WRITE),
|
|
ENUMVAL(D3D12_BARRIER_ACCESS_NO_ACCESS));
|
|
}
|
|
|
|
static FString ConvertToString(D3D12_BARRIER_LAYOUT InLayout)
|
|
{
|
|
return ConvertEnumToString(InLayout,
|
|
ENUMVAL(D3D12_BARRIER_LAYOUT_COMMON),
|
|
ENUMVAL(D3D12_BARRIER_LAYOUT_GENERIC_READ),
|
|
ENUMVAL(D3D12_BARRIER_LAYOUT_RENDER_TARGET),
|
|
ENUMVAL(D3D12_BARRIER_LAYOUT_UNORDERED_ACCESS),
|
|
ENUMVAL(D3D12_BARRIER_LAYOUT_DEPTH_STENCIL_WRITE),
|
|
ENUMVAL(D3D12_BARRIER_LAYOUT_DEPTH_STENCIL_READ),
|
|
ENUMVAL(D3D12_BARRIER_LAYOUT_SHADER_RESOURCE),
|
|
ENUMVAL(D3D12_BARRIER_LAYOUT_COPY_SOURCE),
|
|
ENUMVAL(D3D12_BARRIER_LAYOUT_COPY_DEST),
|
|
ENUMVAL(D3D12_BARRIER_LAYOUT_RESOLVE_SOURCE),
|
|
ENUMVAL(D3D12_BARRIER_LAYOUT_RESOLVE_DEST),
|
|
ENUMVAL(D3D12_BARRIER_LAYOUT_SHADING_RATE_SOURCE),
|
|
ENUMVAL(D3D12_BARRIER_LAYOUT_VIDEO_DECODE_READ),
|
|
ENUMVAL(D3D12_BARRIER_LAYOUT_VIDEO_DECODE_WRITE),
|
|
ENUMVAL(D3D12_BARRIER_LAYOUT_VIDEO_PROCESS_READ),
|
|
ENUMVAL(D3D12_BARRIER_LAYOUT_VIDEO_PROCESS_WRITE),
|
|
ENUMVAL(D3D12_BARRIER_LAYOUT_VIDEO_ENCODE_READ),
|
|
ENUMVAL(D3D12_BARRIER_LAYOUT_VIDEO_ENCODE_WRITE),
|
|
ENUMVAL(D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_COMMON),
|
|
ENUMVAL(D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_GENERIC_READ),
|
|
ENUMVAL(D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_UNORDERED_ACCESS),
|
|
ENUMVAL(D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_SHADER_RESOURCE),
|
|
ENUMVAL(D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_COPY_SOURCE),
|
|
ENUMVAL(D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_COPY_DEST),
|
|
ENUMVAL(D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_COMMON),
|
|
ENUMVAL(D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_GENERIC_READ),
|
|
ENUMVAL(D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_UNORDERED_ACCESS),
|
|
ENUMVAL(D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_SHADER_RESOURCE),
|
|
ENUMVAL(D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_COPY_SOURCE),
|
|
ENUMVAL(D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_COPY_DEST),
|
|
ENUMVAL(D3D12_BARRIER_LAYOUT_VIDEO_QUEUE_COMMON),
|
|
ENUMVAL(D3D12_BARRIER_LAYOUT_PRESENT),
|
|
ENUMVAL(D3D12_BARRIER_LAYOUT_UNDEFINED));
|
|
}
|
|
|
|
#undef ENUMVAL
|
|
|
|
template <typename TArray>
|
|
struct TNumAndArray
|
|
{
|
|
using ArrayType = TArray;
|
|
|
|
int32 Num;
|
|
ArrayType Array;
|
|
};
|
|
|
|
template <TNumAndArray InNumAndArray>
|
|
static consteval auto ResizeArray()
|
|
{
|
|
using OldArrayType = decltype(InNumAndArray)::ArrayType;
|
|
using ElementType = OldArrayType::ElementType;
|
|
|
|
using ArrayType = TStaticArray<ElementType, InNumAndArray.Num>;
|
|
|
|
ArrayType NewArray = {};
|
|
int32 ArrayIdx = 0;
|
|
for (int32 i = 0; i < InNumAndArray.Num; ++i)
|
|
{
|
|
NewArray[i] = InNumAndArray.Array[i];
|
|
}
|
|
return NewArray;
|
|
}
|
|
|
|
template <typename FilterFunc, typename TElement, uint32 InArrayNum>
|
|
static consteval auto FilterArray(
|
|
const TStaticArray<TElement, InArrayNum>& InArray,
|
|
const FilterFunc& InFilterFunc)
|
|
{
|
|
using OutArrayType = TStaticArray<TElement, InArrayNum>;
|
|
|
|
int32 NumElems = 0;
|
|
OutArrayType FilteredArray = {};
|
|
for (uint32 i = 0; i < InArrayNum; ++i)
|
|
{
|
|
const TElement& Value = InArray[i];
|
|
if (InFilterFunc(Value))
|
|
{
|
|
FilteredArray[NumElems++] = Value;
|
|
}
|
|
}
|
|
return TNumAndArray<OutArrayType> { NumElems, FilteredArray };
|
|
}
|
|
|
|
template <TStaticArray InArrayA, TStaticArray InArrayB>
|
|
static consteval auto GetElementsExclusiveToA()
|
|
{
|
|
using ElementType = decltype(InArrayA)::ElementType;
|
|
|
|
constexpr TNumAndArray NumAndArray = FilterArray(
|
|
InArrayA,
|
|
[](const ElementType& Value)
|
|
{
|
|
return !Algo::Contains(InArrayB, Value);
|
|
});
|
|
|
|
return ResizeArray<NumAndArray>();
|
|
}
|
|
|
|
template <TStaticArray InArrayA, TStaticArray InArrayB>
|
|
static consteval auto GetIntersectionOf()
|
|
{
|
|
using ElementType = decltype(InArrayA)::ElementType;
|
|
|
|
constexpr TNumAndArray NumAndArray = FilterArray(
|
|
InArrayA,
|
|
[](const ElementType& Value)
|
|
{
|
|
return Algo::Contains(InArrayB, Value);
|
|
});
|
|
|
|
return ResizeArray<NumAndArray>();
|
|
}
|
|
|
|
//
|
|
// These tables are all copied directly from the DX spec
|
|
// and are unmodified (except where specifically noted)
|
|
// All additional lookups of this information should use
|
|
// these tables or tables derived from these during compilation
|
|
// so that if the spec is updated, it's easy to incorporate and
|
|
// validate those updates by inspection alone.
|
|
//
|
|
|
|
static constexpr
|
|
D3D12_BARRIER_SYNC DirectQueueCompatibleSync =
|
|
D3D12_BARRIER_SYNC_ALL
|
|
| D3D12_BARRIER_SYNC_DRAW
|
|
| D3D12_BARRIER_SYNC_INDEX_INPUT
|
|
| D3D12_BARRIER_SYNC_VERTEX_SHADING
|
|
| D3D12_BARRIER_SYNC_PIXEL_SHADING
|
|
| D3D12_BARRIER_SYNC_DEPTH_STENCIL
|
|
| D3D12_BARRIER_SYNC_RENDER_TARGET
|
|
| D3D12_BARRIER_SYNC_COMPUTE_SHADING
|
|
| D3D12_BARRIER_SYNC_RAYTRACING
|
|
| D3D12_BARRIER_SYNC_COPY
|
|
| D3D12_BARRIER_SYNC_RESOLVE
|
|
| D3D12_BARRIER_SYNC_EXECUTE_INDIRECT
|
|
| D3D12_BARRIER_SYNC_PREDICATION
|
|
| D3D12_BARRIER_SYNC_ALL_SHADING
|
|
| D3D12_BARRIER_SYNC_NON_PIXEL_SHADING
|
|
| D3D12_BARRIER_SYNC_BUILD_RAYTRACING_ACCELERATION_STRUCTURE
|
|
| D3D12_BARRIER_SYNC_COPY_RAYTRACING_ACCELERATION_STRUCTURE
|
|
| D3D12_BARRIER_SYNC_EMIT_RAYTRACING_ACCELERATION_STRUCTURE_POSTBUILD_INFO
|
|
| D3D12_BARRIER_SYNC_CLEAR_UNORDERED_ACCESS_VIEW;
|
|
|
|
static constexpr
|
|
D3D12_BARRIER_SYNC ComputeQueueCompatibleSync =
|
|
D3D12_BARRIER_SYNC_ALL
|
|
| D3D12_BARRIER_SYNC_COMPUTE_SHADING
|
|
| D3D12_BARRIER_SYNC_RAYTRACING
|
|
| D3D12_BARRIER_SYNC_COPY
|
|
| D3D12_BARRIER_SYNC_EXECUTE_INDIRECT
|
|
| D3D12_BARRIER_SYNC_ALL_SHADING
|
|
| D3D12_BARRIER_SYNC_NON_PIXEL_SHADING
|
|
| D3D12_BARRIER_SYNC_BUILD_RAYTRACING_ACCELERATION_STRUCTURE
|
|
| D3D12_BARRIER_SYNC_COPY_RAYTRACING_ACCELERATION_STRUCTURE
|
|
| D3D12_BARRIER_SYNC_EMIT_RAYTRACING_ACCELERATION_STRUCTURE_POSTBUILD_INFO
|
|
| D3D12_BARRIER_SYNC_CLEAR_UNORDERED_ACCESS_VIEW
|
|
| D3D12_BARRIER_SYNC_SPLIT;
|
|
|
|
static constexpr
|
|
TStaticArray<D3D12_BARRIER_SYNC, 23> AccessCompatibleSync = {
|
|
//D3D12_BARRIER_ACCESS_VERTEX_BUFFER
|
|
D3D12_BARRIER_SYNC_ALL
|
|
| D3D12_BARRIER_SYNC_VERTEX_SHADING
|
|
| D3D12_BARRIER_SYNC_DRAW
|
|
| D3D12_BARRIER_SYNC_ALL_SHADING,
|
|
|
|
//D3D12_BARRIER_ACCESS_CONSTANT_BUFFER
|
|
D3D12_BARRIER_SYNC_ALL
|
|
| D3D12_BARRIER_SYNC_VERTEX_SHADING
|
|
| D3D12_BARRIER_SYNC_PIXEL_SHADING
|
|
| D3D12_BARRIER_SYNC_COMPUTE_SHADING
|
|
| D3D12_BARRIER_SYNC_DRAW
|
|
| D3D12_BARRIER_SYNC_ALL_SHADING,
|
|
|
|
//D3D12_BARRIER_ACCESS_INDEX_BUFFER
|
|
D3D12_BARRIER_SYNC_ALL
|
|
| D3D12_BARRIER_SYNC_INDEX_INPUT
|
|
| D3D12_BARRIER_SYNC_DRAW,
|
|
|
|
//D3D12_BARRIER_ACCESS_RENDER_TARGET
|
|
D3D12_BARRIER_SYNC_ALL
|
|
| D3D12_BARRIER_SYNC_DRAW
|
|
| D3D12_BARRIER_SYNC_RENDER_TARGET,
|
|
|
|
//D3D12_BARRIER_ACCESS_UNORDERED_ACCESS
|
|
D3D12_BARRIER_SYNC_ALL //-V501
|
|
| D3D12_BARRIER_SYNC_VERTEX_SHADING
|
|
| D3D12_BARRIER_SYNC_PIXEL_SHADING
|
|
| D3D12_BARRIER_SYNC_COMPUTE_SHADING
|
|
| D3D12_BARRIER_SYNC_VERTEX_SHADING
|
|
| D3D12_BARRIER_SYNC_DRAW
|
|
| D3D12_BARRIER_SYNC_ALL_SHADING
|
|
| D3D12_BARRIER_SYNC_CLEAR_UNORDERED_ACCESS_VIEW
|
|
| D3D12_BARRIER_SYNC_EMIT_RAYTRACING_ACCELERATION_STRUCTURE_POSTBUILD_INFO
|
|
| D3D12_BARRIER_SYNC_RAYTRACING,
|
|
|
|
//D3D12_BARRIER_ACCESS_DEPTH_STENCIL_WRITE
|
|
D3D12_BARRIER_SYNC_ALL
|
|
| D3D12_BARRIER_SYNC_DRAW
|
|
| D3D12_BARRIER_SYNC_DEPTH_STENCIL,
|
|
|
|
//D3D12_BARRIER_ACCESS_DEPTH_STENCIL_READ
|
|
D3D12_BARRIER_SYNC_ALL
|
|
| D3D12_BARRIER_SYNC_DRAW
|
|
| D3D12_BARRIER_SYNC_DEPTH_STENCIL,
|
|
|
|
//D3D12_BARRIER_ACCESS_SHADER_RESOURCE
|
|
D3D12_BARRIER_SYNC_ALL
|
|
| D3D12_BARRIER_SYNC_VERTEX_SHADING
|
|
| D3D12_BARRIER_SYNC_PIXEL_SHADING
|
|
| D3D12_BARRIER_SYNC_COMPUTE_SHADING
|
|
// @TODO - This one isn't listed in the spec, but logic and validation tells us it's compatible
|
|
| D3D12_BARRIER_SYNC_NON_PIXEL_SHADING
|
|
| D3D12_BARRIER_SYNC_DRAW
|
|
| D3D12_BARRIER_SYNC_ALL_SHADING
|
|
| D3D12_BARRIER_SYNC_BUILD_RAYTRACING_ACCELERATION_STRUCTURE
|
|
| D3D12_BARRIER_SYNC_RAYTRACING,
|
|
|
|
//D3D12_BARRIER_ACCESS_STREAM_OUTPUT
|
|
D3D12_BARRIER_SYNC_ALL
|
|
| D3D12_BARRIER_SYNC_VERTEX_SHADING
|
|
| D3D12_BARRIER_SYNC_DRAW
|
|
| D3D12_BARRIER_SYNC_ALL_SHADING,
|
|
|
|
//D3D12_BARRIER_ACCESS_INDIRECT_ARGUMENT
|
|
D3D12_BARRIER_SYNC_ALL
|
|
| D3D12_BARRIER_SYNC_EXECUTE_INDIRECT,
|
|
|
|
#if 0
|
|
// An Alias for INDIRECT_ARGUMENT
|
|
//D3D12_BARRIER_ACCESS_PREDICATION
|
|
D3D12_BARRIER_SYNC_ALL
|
|
| D3D12_BARRIER_SYNC_PREDICATION,
|
|
#endif
|
|
|
|
//D3D12_BARRIER_ACCESS_COPY_DEST
|
|
D3D12_BARRIER_SYNC_ALL
|
|
| D3D12_BARRIER_SYNC_COPY,
|
|
|
|
//D3D12_BARRIER_ACCESS_COPY_SOURCE
|
|
D3D12_BARRIER_SYNC_ALL
|
|
| D3D12_BARRIER_SYNC_COPY,
|
|
|
|
//D3D12_BARRIER_ACCESS_RESOLVE_DEST
|
|
D3D12_BARRIER_SYNC_ALL
|
|
| D3D12_BARRIER_SYNC_RESOLVE,
|
|
|
|
//D3D12_BARRIER_ACCESS_RESOLVE_SOURCE
|
|
D3D12_BARRIER_SYNC_ALL
|
|
| D3D12_BARRIER_SYNC_RESOLVE,
|
|
|
|
//D3D12_BARRIER_ACCESS_RAYTRACING_ACCELERATION_STRUCTURE_READ
|
|
D3D12_BARRIER_SYNC_ALL
|
|
| D3D12_BARRIER_SYNC_COMPUTE_SHADING
|
|
| D3D12_BARRIER_SYNC_RAYTRACING
|
|
| D3D12_BARRIER_SYNC_ALL_SHADING
|
|
| D3D12_BARRIER_SYNC_BUILD_RAYTRACING_ACCELERATION_STRUCTURE
|
|
| D3D12_BARRIER_SYNC_COPY_RAYTRACING_ACCELERATION_STRUCTURE
|
|
| D3D12_BARRIER_SYNC_EMIT_RAYTRACING_ACCELERATION_STRUCTURE_POSTBUILD_INFO,
|
|
|
|
//D3D12_BARRIER_ACCESS_RAYTRACING_ACCELERATION_STRUCTURE_WRITE
|
|
D3D12_BARRIER_SYNC_ALL
|
|
| D3D12_BARRIER_SYNC_COMPUTE_SHADING
|
|
#if !PLATFORM_REQUIRES_SYNC_RAYTRACING_NOT_COMPATIBLE_WITH_ACCESS_AS_WRITE
|
|
| D3D12_BARRIER_SYNC_RAYTRACING
|
|
#endif
|
|
| D3D12_BARRIER_SYNC_ALL_SHADING
|
|
| D3D12_BARRIER_SYNC_BUILD_RAYTRACING_ACCELERATION_STRUCTURE
|
|
| D3D12_BARRIER_SYNC_COPY_RAYTRACING_ACCELERATION_STRUCTURE,
|
|
|
|
//D3D12_BARRIER_ACCESS_SHADING_RATE_SOURCE
|
|
D3D12_BARRIER_SYNC_ALL
|
|
| D3D12_BARRIER_SYNC_PIXEL_SHADING
|
|
| D3D12_BARRIER_SYNC_ALL_SHADING,
|
|
|
|
//D3D12_BARRIER_ACCESS_VIDEO_DECODE_READ
|
|
D3D12_BARRIER_SYNC_ALL
|
|
| D3D12_BARRIER_SYNC_VIDEO_DECODE,
|
|
|
|
|
|
//D3D12_BARRIER_ACCESS_VIDEO_DECODE_WRITE
|
|
D3D12_BARRIER_SYNC_ALL
|
|
| D3D12_BARRIER_SYNC_VIDEO_DECODE,
|
|
|
|
//D3D12_BARRIER_ACCESS_VIDEO_PROCESS_READ
|
|
D3D12_BARRIER_SYNC_ALL
|
|
| D3D12_BARRIER_SYNC_VIDEO_PROCESS,
|
|
|
|
//D3D12_BARRIER_ACCESS_VIDEO_PROCESS_WRITE
|
|
D3D12_BARRIER_SYNC_ALL
|
|
| D3D12_BARRIER_SYNC_VIDEO_PROCESS,
|
|
|
|
//D3D12_BARRIER_ACCESS_VIDEO_ENCODE_READ
|
|
D3D12_BARRIER_SYNC_ALL
|
|
| D3D12_BARRIER_SYNC_VIDEO_ENCODE,
|
|
|
|
//D3D12_BARRIER_ACCESS_VIDEO_ENCODE_WRITE
|
|
D3D12_BARRIER_SYNC_ALL
|
|
| D3D12_BARRIER_SYNC_VIDEO_ENCODE,
|
|
|
|
// Omitted since it's non-contigious with the other bits
|
|
//D3D12_BARRIER_ACCESS_NO_ACCESS
|
|
};
|
|
|
|
static constexpr
|
|
TStaticArray<D3D12_BARRIER_ACCESS, 32> LayoutCompatibleAccess = {
|
|
//D3D12_BARRIER_LAYOUT_COMMON
|
|
D3D12_BARRIER_ACCESS_SHADER_RESOURCE
|
|
| D3D12_BARRIER_ACCESS_COPY_DEST
|
|
| D3D12_BARRIER_ACCESS_COPY_SOURCE,
|
|
|
|
//D3D12_BARRIER_LAYOUT_GENERIC_READ
|
|
D3D12_BARRIER_ACCESS_SHADER_RESOURCE
|
|
| D3D12_BARRIER_ACCESS_COPY_SOURCE,
|
|
|
|
//D3D12_BARRIER_LAYOUT_RENDER_TARGET
|
|
D3D12_BARRIER_ACCESS_RENDER_TARGET,
|
|
|
|
//D3D12_BARRIER_LAYOUT_UNORDERED_ACCESS
|
|
D3D12_BARRIER_ACCESS_UNORDERED_ACCESS,
|
|
|
|
//D3D12_BARRIER_LAYOUT_DEPTH_STENCIL_WRITE
|
|
D3D12_BARRIER_ACCESS_DEPTH_STENCIL_READ
|
|
| D3D12_BARRIER_ACCESS_DEPTH_STENCIL_WRITE,
|
|
|
|
//D3D12_BARRIER_LAYOUT_DEPTH_STENCIL_READ
|
|
D3D12_BARRIER_ACCESS_DEPTH_STENCIL_READ,
|
|
|
|
//D3D12_BARRIER_LAYOUT_SHADER_RESOURCE
|
|
D3D12_BARRIER_ACCESS_SHADER_RESOURCE,
|
|
|
|
//D3D12_BARRIER_LAYOUT_COPY_SOURCE
|
|
D3D12_BARRIER_ACCESS_COPY_SOURCE,
|
|
|
|
//D3D12_BARRIER_LAYOUT_COPY_DEST
|
|
D3D12_BARRIER_ACCESS_COPY_DEST,
|
|
|
|
//D3D12_BARRIER_LAYOUT_RESOLVE_SOURCE
|
|
D3D12_BARRIER_ACCESS_RESOLVE_SOURCE,
|
|
|
|
//D3D12_BARRIER_LAYOUT_RESOLVE_DEST
|
|
D3D12_BARRIER_ACCESS_RESOLVE_DEST,
|
|
|
|
//D3D12_BARRIER_LAYOUT_SHADING_RATE_SOURCE
|
|
D3D12_BARRIER_ACCESS_SHADING_RATE_SOURCE,
|
|
|
|
//D3D12_BARRIER_LAYOUT_VIDEO_DECODE_READ
|
|
D3D12_BARRIER_ACCESS_VIDEO_DECODE_READ,
|
|
|
|
//D3D12_BARRIER_LAYOUT_VIDEO_DECODE_WRITE
|
|
D3D12_BARRIER_ACCESS_VIDEO_DECODE_WRITE,
|
|
|
|
//D3D12_BARRIER_LAYOUT_VIDEO_PROCESS_READ
|
|
D3D12_BARRIER_ACCESS_VIDEO_PROCESS_READ,
|
|
|
|
//D3D12_BARRIER_LAYOUT_VIDEO_PROCESS_WRITE
|
|
D3D12_BARRIER_ACCESS_VIDEO_PROCESS_WRITE,
|
|
|
|
//D3D12_BARRIER_LAYOUT_VIDEO_ENCODE_READ
|
|
D3D12_BARRIER_ACCESS_VIDEO_ENCODE_READ,
|
|
|
|
//D3D12_BARRIER_LAYOUT_VIDEO_ENCODE_WRITE
|
|
D3D12_BARRIER_ACCESS_VIDEO_ENCODE_WRITE,
|
|
|
|
//D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_COMMON
|
|
D3D12_BARRIER_ACCESS_COPY_SOURCE
|
|
|D3D12_BARRIER_ACCESS_COPY_DEST
|
|
|D3D12_BARRIER_ACCESS_SHADER_RESOURCE
|
|
|D3D12_BARRIER_ACCESS_UNORDERED_ACCESS,
|
|
|
|
//D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_GENERIC_READ
|
|
D3D12_BARRIER_ACCESS_SHADER_RESOURCE
|
|
| D3D12_BARRIER_ACCESS_COPY_SOURCE
|
|
| D3D12_BARRIER_ACCESS_DEPTH_STENCIL_READ
|
|
| D3D12_BARRIER_ACCESS_SHADING_RATE_SOURCE
|
|
| D3D12_BARRIER_ACCESS_RESOLVE_SOURCE,
|
|
|
|
//D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_UNORDERED_ACCESS
|
|
D3D12_BARRIER_ACCESS_UNORDERED_ACCESS,
|
|
|
|
//D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_SHADER_RESOURCE
|
|
D3D12_BARRIER_ACCESS_SHADER_RESOURCE,
|
|
|
|
//D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_COPY_SOURCE
|
|
D3D12_BARRIER_ACCESS_COPY_SOURCE,
|
|
|
|
//D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_COPY_DEST
|
|
D3D12_BARRIER_ACCESS_COPY_DEST,
|
|
|
|
//D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_COMMON
|
|
D3D12_BARRIER_ACCESS_COPY_SOURCE
|
|
| D3D12_BARRIER_ACCESS_COPY_DEST
|
|
| D3D12_BARRIER_ACCESS_SHADER_RESOURCE
|
|
| D3D12_BARRIER_ACCESS_UNORDERED_ACCESS,
|
|
|
|
//D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_GENERIC_READ
|
|
D3D12_BARRIER_ACCESS_SHADER_RESOURCE
|
|
| D3D12_BARRIER_ACCESS_COPY_SOURCE,
|
|
|
|
//D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_UNORDERED_ACCESS
|
|
D3D12_BARRIER_ACCESS_UNORDERED_ACCESS,
|
|
|
|
//D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_SHADER_RESOURCE
|
|
D3D12_BARRIER_ACCESS_SHADER_RESOURCE,
|
|
|
|
//D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_COPY_SOURCE
|
|
D3D12_BARRIER_ACCESS_COPY_SOURCE,
|
|
|
|
//D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_COPY_DEST
|
|
D3D12_BARRIER_ACCESS_COPY_DEST,
|
|
|
|
//D3D12_BARRIER_LAYOUT_VIDEO_QUEUE_COMMON,
|
|
D3D12_BARRIER_ACCESS_COPY_SOURCE
|
|
| D3D12_BARRIER_ACCESS_COPY_DEST,
|
|
|
|
//D3D12_BARRIER_LAYOUT_PRESENT
|
|
D3D12_BARRIER_ACCESS_SHADER_RESOURCE
|
|
| D3D12_BARRIER_ACCESS_COPY_DEST
|
|
| D3D12_BARRIER_ACCESS_COPY_SOURCE,
|
|
};
|
|
|
|
// These are not the same layout on some platforms but the compatible access should be the same
|
|
static_assert(
|
|
LayoutCompatibleAccess[D3D12_BARRIER_LAYOUT_COMMON]
|
|
== LayoutCompatibleAccess[D3D12_BARRIER_LAYOUT_PRESENT]);
|
|
|
|
static constexpr
|
|
D3D12_BARRIER_SYNC AllQueueCompatibleSync =
|
|
DirectQueueCompatibleSync & ComputeQueueCompatibleSync;
|
|
|
|
static constexpr
|
|
D3D12_BARRIER_ACCESS DirectQueueCompatibleAccess =
|
|
D3D12_BARRIER_ACCESS_VERTEX_BUFFER
|
|
| D3D12_BARRIER_ACCESS_CONSTANT_BUFFER
|
|
| D3D12_BARRIER_ACCESS_INDEX_BUFFER
|
|
| D3D12_BARRIER_ACCESS_RENDER_TARGET
|
|
| D3D12_BARRIER_ACCESS_UNORDERED_ACCESS
|
|
| D3D12_BARRIER_ACCESS_DEPTH_STENCIL_WRITE
|
|
| D3D12_BARRIER_ACCESS_DEPTH_STENCIL_READ
|
|
| D3D12_BARRIER_ACCESS_SHADER_RESOURCE
|
|
| D3D12_BARRIER_ACCESS_STREAM_OUTPUT
|
|
| D3D12_BARRIER_ACCESS_INDIRECT_ARGUMENT
|
|
| D3D12_BARRIER_ACCESS_COPY_DEST
|
|
| D3D12_BARRIER_ACCESS_COPY_SOURCE
|
|
| D3D12_BARRIER_ACCESS_RESOLVE_DEST
|
|
| D3D12_BARRIER_ACCESS_RESOLVE_SOURCE
|
|
| D3D12_BARRIER_ACCESS_RAYTRACING_ACCELERATION_STRUCTURE_READ
|
|
| D3D12_BARRIER_ACCESS_RAYTRACING_ACCELERATION_STRUCTURE_WRITE
|
|
| D3D12_BARRIER_ACCESS_SHADING_RATE_SOURCE
|
|
| D3D12_BARRIER_ACCESS_PREDICATION
|
|
// Not in spec
|
|
| D3D12_BARRIER_ACCESS_NO_ACCESS;
|
|
|
|
static constexpr
|
|
D3D12_BARRIER_ACCESS ComputeQueueCompatibleAccess =
|
|
// @TODO - Spec lists this but logic and validation disagree
|
|
//D3D12_BARRIER_ACCESS_VERTEX_BUFFER
|
|
D3D12_BARRIER_ACCESS_CONSTANT_BUFFER
|
|
| D3D12_BARRIER_ACCESS_UNORDERED_ACCESS
|
|
| D3D12_BARRIER_ACCESS_SHADER_RESOURCE
|
|
| D3D12_BARRIER_ACCESS_INDIRECT_ARGUMENT
|
|
| D3D12_BARRIER_ACCESS_COPY_DEST
|
|
| D3D12_BARRIER_ACCESS_COPY_SOURCE
|
|
| D3D12_BARRIER_ACCESS_RAYTRACING_ACCELERATION_STRUCTURE_READ
|
|
| D3D12_BARRIER_ACCESS_RAYTRACING_ACCELERATION_STRUCTURE_WRITE
|
|
| D3D12_BARRIER_ACCESS_PREDICATION
|
|
// Not in spec
|
|
| D3D12_BARRIER_ACCESS_NO_ACCESS
|
|
#if PLATFORM_REQUIRES_ENHANCED_BARRIERS_GFX_ONLY_READ_BITS_HACK
|
|
// @TODO -
|
|
// !!!!!!!!!! HUGE HACK! !!!!!!!!!!!!
|
|
// This isn't officially compatible
|
|
// But we have no other way to set certain read-only gfx bits
|
|
// when a resource is being read by multiple pipes at once
|
|
| D3D12_BARRIER_ACCESS_DEPTH_STENCIL_READ
|
|
| D3D12_BARRIER_ACCESS_SHADING_RATE_SOURCE
|
|
| D3D12_BARRIER_ACCESS_RESOLVE_SOURCE
|
|
#endif
|
|
;
|
|
|
|
static constexpr
|
|
D3D12_BARRIER_ACCESS AllQueueCompatibleAccess =
|
|
DirectQueueCompatibleAccess & ComputeQueueCompatibleAccess;
|
|
|
|
static constexpr
|
|
TStaticArray<D3D12_BARRIER_LAYOUT, 19> DirectQueueCompatibleLayouts = {
|
|
D3D12_BARRIER_LAYOUT_COMMON,
|
|
D3D12_BARRIER_LAYOUT_GENERIC_READ,
|
|
D3D12_BARRIER_LAYOUT_RENDER_TARGET,
|
|
D3D12_BARRIER_LAYOUT_UNORDERED_ACCESS,
|
|
D3D12_BARRIER_LAYOUT_DEPTH_STENCIL_WRITE,
|
|
D3D12_BARRIER_LAYOUT_DEPTH_STENCIL_READ,
|
|
D3D12_BARRIER_LAYOUT_SHADER_RESOURCE,
|
|
D3D12_BARRIER_LAYOUT_COPY_SOURCE,
|
|
D3D12_BARRIER_LAYOUT_COPY_DEST,
|
|
D3D12_BARRIER_LAYOUT_RESOLVE_SOURCE,
|
|
D3D12_BARRIER_LAYOUT_RESOLVE_DEST,
|
|
D3D12_BARRIER_LAYOUT_SHADING_RATE_SOURCE,
|
|
D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_COMMON,
|
|
D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_GENERIC_READ,
|
|
D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_UNORDERED_ACCESS,
|
|
D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_SHADER_RESOURCE,
|
|
D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_COPY_SOURCE,
|
|
D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_COPY_DEST,
|
|
// Not in spec
|
|
D3D12_BARRIER_LAYOUT_UNDEFINED,
|
|
};
|
|
|
|
static constexpr
|
|
TStaticArray<D3D12_BARRIER_LAYOUT, 14> ComputeQueueCompatibleLayouts = {
|
|
D3D12_BARRIER_LAYOUT_COMMON,
|
|
D3D12_BARRIER_LAYOUT_GENERIC_READ,
|
|
D3D12_BARRIER_LAYOUT_UNORDERED_ACCESS,
|
|
D3D12_BARRIER_LAYOUT_SHADER_RESOURCE,
|
|
D3D12_BARRIER_LAYOUT_COPY_SOURCE,
|
|
D3D12_BARRIER_LAYOUT_COPY_DEST,
|
|
D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_COMMON,
|
|
D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_GENERIC_READ,
|
|
D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_UNORDERED_ACCESS,
|
|
D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_SHADER_RESOURCE,
|
|
D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_COPY_SOURCE,
|
|
D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_COPY_DEST,
|
|
// Not in spec
|
|
D3D12_BARRIER_LAYOUT_UNDEFINED,
|
|
#if PLATFORM_REQUIRES_ENHANCED_BARRIERS_GFX_ONLY_READ_BITS_HACK
|
|
// @TODO -
|
|
// !!!!!!!!!! HUGE HACK! !!!!!!!!!!!!
|
|
// This isn't officially compatible
|
|
// But we have no other way to set certain read-only gfx bits
|
|
// when a resource is being read by multiple pipes at once
|
|
D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_GENERIC_READ,
|
|
#endif
|
|
};
|
|
|
|
static constexpr
|
|
TStaticArray AllQueueCompatibleLayouts =
|
|
GetIntersectionOf<
|
|
DirectQueueCompatibleLayouts,
|
|
ComputeQueueCompatibleLayouts>();
|
|
|
|
static constexpr
|
|
TStaticArray DirectQueueSpecificLayouts =
|
|
GetElementsExclusiveToA<
|
|
DirectQueueCompatibleLayouts,
|
|
ComputeQueueCompatibleLayouts>();
|
|
|
|
static constexpr
|
|
TStaticArray ComputeQueueSpecificLayouts =
|
|
GetElementsExclusiveToA<
|
|
ComputeQueueCompatibleLayouts,
|
|
DirectQueueCompatibleLayouts>();
|
|
|
|
|
|
static bool LayoutIsCompatibleWithQueue(
|
|
D3D12_BARRIER_LAYOUT InLayout,
|
|
ERHIPipeline InPipe)
|
|
{
|
|
switch (InPipe)
|
|
{
|
|
case ERHIPipeline::Graphics:
|
|
return Algo::Contains(DirectQueueCompatibleLayouts, InLayout);
|
|
case ERHIPipeline::AsyncCompute:
|
|
return Algo::Contains(ComputeQueueCompatibleLayouts, InLayout);
|
|
case ERHIPipeline::All:
|
|
return Algo::Contains(AllQueueCompatibleLayouts, InLayout);
|
|
default:
|
|
checkNoEntry();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static bool LayoutIsQueueSpecific(
|
|
D3D12_BARRIER_LAYOUT InLayout,
|
|
ERHIPipeline InPipe)
|
|
{
|
|
check(LayoutIsCompatibleWithQueue(InLayout, InPipe));
|
|
|
|
switch (InPipe)
|
|
{
|
|
case ERHIPipeline::Graphics:
|
|
return Algo::Contains(DirectQueueSpecificLayouts, InLayout);
|
|
case ERHIPipeline::AsyncCompute:
|
|
return Algo::Contains(ComputeQueueSpecificLayouts, InLayout);
|
|
case ERHIPipeline::All:
|
|
return false;
|
|
default:
|
|
checkNoEntry();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static bool SyncIsCompatibleWithQueue(
|
|
D3D12_BARRIER_SYNC InSync,
|
|
ERHIPipeline InPipe)
|
|
{
|
|
if (InSync == D3D12_BARRIER_SYNC_NONE)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
switch (InPipe)
|
|
{
|
|
case ERHIPipeline::Graphics:
|
|
return EnumOnlyContainsFlags(InSync, DirectQueueCompatibleSync);
|
|
case ERHIPipeline::AsyncCompute:
|
|
return EnumOnlyContainsFlags(InSync, ComputeQueueCompatibleSync);
|
|
case ERHIPipeline::All:
|
|
return EnumOnlyContainsFlags(InSync, AllQueueCompatibleSync);
|
|
default:
|
|
checkNoEntry();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static D3D12_BARRIER_SYNC FilterToQueueCompatibleSync(
|
|
D3D12_BARRIER_SYNC InSync,
|
|
ERHIPipeline InPipe)
|
|
{
|
|
switch (InPipe)
|
|
{
|
|
case ERHIPipeline::Graphics:
|
|
return InSync & DirectQueueCompatibleSync;
|
|
case ERHIPipeline::AsyncCompute:
|
|
return InSync & ComputeQueueCompatibleSync;
|
|
case ERHIPipeline::All:
|
|
return InSync & AllQueueCompatibleSync;
|
|
default:
|
|
checkNoEntry();
|
|
return {};
|
|
}
|
|
}
|
|
|
|
static bool AccessIsCompatibleWithQueue(
|
|
D3D12_BARRIER_ACCESS InAccess,
|
|
ERHIPipeline InPipe)
|
|
{
|
|
if (InAccess == D3D12_BARRIER_ACCESS_NO_ACCESS)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
switch (InPipe)
|
|
{
|
|
case ERHIPipeline::Graphics:
|
|
return EnumOnlyContainsFlags(InAccess, DirectQueueCompatibleAccess);
|
|
case ERHIPipeline::AsyncCompute:
|
|
return EnumOnlyContainsFlags(InAccess, ComputeQueueCompatibleAccess);
|
|
case ERHIPipeline::All:
|
|
return EnumOnlyContainsFlags(InAccess, AllQueueCompatibleAccess);
|
|
default:
|
|
checkNoEntry();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static bool AccessIsCompatibleWithLayout(
|
|
D3D12_BARRIER_ACCESS InAccess,
|
|
D3D12_BARRIER_LAYOUT InLayout)
|
|
{
|
|
if (InAccess == D3D12_BARRIER_ACCESS_NO_ACCESS)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (static_cast<int32>(InLayout) > LayoutCompatibleAccess.Num())
|
|
{
|
|
return false;
|
|
}
|
|
return EnumOnlyContainsFlags(InAccess, LayoutCompatibleAccess[InLayout]);
|
|
}
|
|
|
|
static D3D12_BARRIER_ACCESS FilterToQueueCompatibleAccess(
|
|
D3D12_BARRIER_ACCESS InAccess,
|
|
ERHIPipeline InPipe)
|
|
{
|
|
switch (InPipe)
|
|
{
|
|
case ERHIPipeline::Graphics:
|
|
return InAccess & DirectQueueCompatibleAccess;
|
|
case ERHIPipeline::AsyncCompute:
|
|
return InAccess & ComputeQueueCompatibleAccess;
|
|
case ERHIPipeline::All:
|
|
return InAccess & AllQueueCompatibleAccess;
|
|
default:
|
|
checkNoEntry();
|
|
return {};
|
|
}
|
|
}
|
|
|
|
static D3D12_BARRIER_LAYOUT GetQueueAgnosticVersionOfLayout(
|
|
D3D12_BARRIER_LAYOUT InLayout)
|
|
{
|
|
switch (InLayout)
|
|
{
|
|
case D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_COMMON:
|
|
case D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_COMMON:
|
|
return D3D12_BARRIER_LAYOUT_COMMON;
|
|
case D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_GENERIC_READ:
|
|
case D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_GENERIC_READ:
|
|
return D3D12_BARRIER_LAYOUT_GENERIC_READ;
|
|
case D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_UNORDERED_ACCESS:
|
|
case D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_UNORDERED_ACCESS:
|
|
return D3D12_BARRIER_LAYOUT_UNORDERED_ACCESS;
|
|
case D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_SHADER_RESOURCE:
|
|
case D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_SHADER_RESOURCE:
|
|
return D3D12_BARRIER_LAYOUT_SHADER_RESOURCE;
|
|
case D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_COPY_SOURCE:
|
|
case D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_COPY_SOURCE:
|
|
return D3D12_BARRIER_LAYOUT_COPY_SOURCE;
|
|
case D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_COPY_DEST:
|
|
case D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_COPY_DEST:
|
|
return D3D12_BARRIER_LAYOUT_COPY_DEST;
|
|
default:
|
|
return InLayout;
|
|
}
|
|
}
|
|
|
|
static bool BarrierValuesAreCompatibleWithQueue(
|
|
D3D12_BARRIER_SYNC InSync,
|
|
D3D12_BARRIER_ACCESS InAccess,
|
|
D3D12_BARRIER_LAYOUT InLayout,
|
|
ERHIPipeline InPipe)
|
|
{
|
|
return SyncIsCompatibleWithQueue(InSync, InPipe)
|
|
&& AccessIsCompatibleWithQueue(InAccess, InPipe)
|
|
&& LayoutIsCompatibleWithQueue(InLayout, InPipe);
|
|
}
|
|
|
|
static bool BarrierValuesAreCompatibleWithQueue(
|
|
FD3D12BarrierValues InBarrierValues,
|
|
ERHIPipeline InPipe)
|
|
{
|
|
return BarrierValuesAreCompatibleWithQueue(
|
|
InBarrierValues.Sync,
|
|
InBarrierValues.Access,
|
|
InBarrierValues.Layout,
|
|
InPipe);
|
|
}
|
|
|
|
static constexpr
|
|
D3D12_BARRIER_SYNC GetAccessCompatibleSync(
|
|
D3D12_BARRIER_ACCESS InAccess)
|
|
{
|
|
using AccessUnsignedUnderlyingType =
|
|
std::make_unsigned_t<
|
|
std::underlying_type_t<D3D12_BARRIER_ACCESS>>;
|
|
|
|
using SyncUnsignedUnderlyingType =
|
|
std::make_unsigned_t<
|
|
std::underlying_type_t<D3D12_BARRIER_SYNC>>;
|
|
|
|
if (InAccess == D3D12_BARRIER_ACCESS_COMMON
|
|
|| InAccess == D3D12_BARRIER_ACCESS_NO_ACCESS)
|
|
{
|
|
return BitCast<D3D12_BARRIER_SYNC>(~AccessUnsignedUnderlyingType(0));
|
|
}
|
|
|
|
const uint32 Bits = sizeof(AccessUnsignedUnderlyingType) * 8;
|
|
const auto UnsignedAccessValue = static_cast<AccessUnsignedUnderlyingType>(InAccess);
|
|
|
|
SyncUnsignedUnderlyingType CompatibleSync = 0;
|
|
uint32 LeadingZeros = std::countl_zero(UnsignedAccessValue);
|
|
while (LeadingZeros < Bits)
|
|
{
|
|
const uint32 FirstSetBitIdx = Bits - LeadingZeros - 1;
|
|
const auto Mask = ((AccessUnsignedUnderlyingType(1)) << FirstSetBitIdx) - 1;
|
|
CompatibleSync |= AccessCompatibleSync[FirstSetBitIdx];
|
|
LeadingZeros = std::countl_zero(UnsignedAccessValue & Mask);
|
|
}
|
|
|
|
return static_cast<D3D12_BARRIER_SYNC>(CompatibleSync);
|
|
}
|
|
|
|
static bool AccessIsCompatibleWithSync(
|
|
D3D12_BARRIER_ACCESS InAccess,
|
|
D3D12_BARRIER_SYNC InSync)
|
|
{
|
|
// Note that this is more expensive than the other checks,
|
|
// try not to use it other than for validation
|
|
return EnumHasAllFlags(GetAccessCompatibleSync(InAccess), InSync);
|
|
}
|
|
|
|
static bool SyncAndAccessAreComputeWrite(
|
|
D3D12_BARRIER_SYNC InSync,
|
|
D3D12_BARRIER_ACCESS InAccess)
|
|
{
|
|
// These mean compute write access
|
|
static constexpr D3D12_BARRIER_ACCESS ComputeWriteAccessFlags =
|
|
D3D12_BARRIER_ACCESS_UNORDERED_ACCESS
|
|
| D3D12_BARRIER_ACCESS_RAYTRACING_ACCELERATION_STRUCTURE_WRITE;
|
|
|
|
static constexpr D3D12_BARRIER_SYNC ComputeSyncFlags =
|
|
GetAccessCompatibleSync(ComputeWriteAccessFlags);
|
|
|
|
|
|
return EnumHasAnyFlags(InSync, ComputeSyncFlags)
|
|
// ACCESS_COMMON is used to describe all access bits compatible with the sync scope
|
|
// so if it's used with one of the compute work sync'ing bits then it implies compute write
|
|
&& ((InAccess == D3D12_BARRIER_ACCESS_COMMON)
|
|
// Otherwise, we can explicitly look for the compute write access bits
|
|
|| EnumHasAnyFlags(InAccess, ComputeWriteAccessFlags));
|
|
}
|
|
|
|
template <typename T>
|
|
static bool CheckBarrierValuesAreCompatible(
|
|
const TArrayView<T>& InBarriers,
|
|
ERHIPipeline InPipeline)
|
|
{
|
|
for (const T& Barrier : InBarriers)
|
|
{
|
|
check(SyncIsCompatibleWithQueue(Barrier.SyncBefore, InPipeline));
|
|
check(SyncIsCompatibleWithQueue(Barrier.SyncAfter, InPipeline));
|
|
check(AccessIsCompatibleWithQueue(Barrier.AccessBefore, InPipeline));
|
|
check(AccessIsCompatibleWithQueue(Barrier.AccessAfter, InPipeline));
|
|
|
|
check(AccessIsCompatibleWithSync(Barrier.AccessBefore, Barrier.SyncBefore));
|
|
check(AccessIsCompatibleWithSync(Barrier.AccessAfter, Barrier.SyncAfter));
|
|
|
|
if constexpr (std::is_same_v<D3D12_TEXTURE_BARRIER, T>)
|
|
{
|
|
check(LayoutIsCompatibleWithQueue(Barrier.LayoutBefore, InPipeline));
|
|
check(LayoutIsCompatibleWithQueue(Barrier.LayoutAfter, InPipeline));
|
|
|
|
check(AccessIsCompatibleWithLayout(Barrier.AccessBefore, Barrier.LayoutBefore));
|
|
check(AccessIsCompatibleWithLayout(Barrier.AccessAfter, Barrier.LayoutAfter));
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool BarrierCanBeDiscarded(
|
|
D3D12_BARRIER_SYNC InBeforeSync,
|
|
D3D12_BARRIER_SYNC InAfterSync,
|
|
D3D12_BARRIER_ACCESS InBeforeAccess,
|
|
D3D12_BARRIER_ACCESS InAfterAccess,
|
|
D3D12_BARRIER_LAYOUT InBeforeLayout,
|
|
D3D12_BARRIER_LAYOUT InAfterLayout)
|
|
{
|
|
return (InBeforeSync == InAfterSync)
|
|
&& (InBeforeAccess == InAfterAccess)
|
|
&& (InBeforeLayout == InAfterLayout)
|
|
// ComputeWrite->ComputeWrite can't be skipped because
|
|
// each compute unit may have its own caches
|
|
&& !SyncAndAccessAreComputeWrite(InBeforeSync, InBeforeAccess);
|
|
}
|
|
|
|
static bool BarrierCanBeDiscarded(
|
|
const FD3D12BarrierValues& InBeforeValues,
|
|
const FD3D12BarrierValues& InAfterValues)
|
|
{
|
|
return BarrierCanBeDiscarded(
|
|
InBeforeValues.Sync,
|
|
InAfterValues.Sync,
|
|
InBeforeValues.Access,
|
|
InAfterValues.Access,
|
|
InBeforeValues.Layout,
|
|
InAfterValues.Layout);
|
|
}
|
|
|
|
static D3D12_BARRIER_SYNC GetEBSync(
|
|
ED3D12Access InD3D12Access,
|
|
ERHIPipeline InPipe)
|
|
{
|
|
if (InPipe == ERHIPipeline::None)
|
|
{
|
|
checkNoEntry();
|
|
return D3D12_BARRIER_SYNC_NONE;
|
|
}
|
|
|
|
if (InD3D12Access == ED3D12Access::Unknown)
|
|
{
|
|
return D3D12_BARRIER_SYNC_ALL;
|
|
}
|
|
|
|
if (EnumHasAnyFlags(InD3D12Access, ED3D12Access::Common))
|
|
{
|
|
return D3D12_BARRIER_SYNC_ALL;
|
|
}
|
|
|
|
if (EnumHasAnyFlags(InD3D12Access, ED3D12Access::GenericRead))
|
|
{
|
|
return D3D12_BARRIER_SYNC_ALL;
|
|
}
|
|
|
|
if (EnumHasAnyFlags(InD3D12Access, ED3D12Access::Discard))
|
|
{
|
|
check(InD3D12Access == ED3D12Access::Discard);
|
|
return D3D12_BARRIER_SYNC_NONE;
|
|
}
|
|
|
|
D3D12_BARRIER_SYNC EBSync = {};
|
|
if (EnumHasAnyFlags(InD3D12Access, ED3D12Access::CPURead))
|
|
{
|
|
EBSync |= D3D12_BARRIER_SYNC_NONE;
|
|
}
|
|
if (EnumHasAnyFlags(InD3D12Access, ED3D12Access::Present))
|
|
{
|
|
#if PLATFORM_WINDOWS
|
|
EBSync |= D3D12_BARRIER_SYNC_ALL;
|
|
#else
|
|
EBSync |= D3D12_BARRIER_SYNC_NONE;
|
|
#endif
|
|
}
|
|
if (EnumHasAnyFlags(InD3D12Access, ED3D12Access::IndirectArgs))
|
|
{
|
|
EBSync |= D3D12_BARRIER_SYNC_EXECUTE_INDIRECT;
|
|
}
|
|
if (EnumHasAnyFlags(InD3D12Access, ED3D12Access::VertexOrIndexBuffer))
|
|
{
|
|
// @TODO - This sucks... need more specific RHI bits or to pass in a resource description
|
|
EBSync |= D3D12_BARRIER_SYNC_VERTEX_SHADING
|
|
| D3D12_BARRIER_SYNC_INDEX_INPUT
|
|
// Needed to cover constant buffers
|
|
| D3D12_BARRIER_SYNC_ALL_SHADING;
|
|
}
|
|
if (EnumHasAnyFlags(InD3D12Access, ED3D12Access::SRVCompute))
|
|
{
|
|
EBSync |= D3D12_BARRIER_SYNC_COMPUTE_SHADING;
|
|
}
|
|
if (EnumHasAnyFlags(InD3D12Access, ED3D12Access::SRVGraphicsPixel))
|
|
{
|
|
EBSync |= D3D12_BARRIER_SYNC_PIXEL_SHADING;
|
|
}
|
|
if (EnumHasAnyFlags(InD3D12Access, ED3D12Access::SRVGraphicsNonPixel))
|
|
{
|
|
EBSync |= D3D12_BARRIER_SYNC_NON_PIXEL_SHADING;
|
|
}
|
|
if (EnumHasAnyFlags(InD3D12Access, ED3D12Access::CopySrc))
|
|
{
|
|
EBSync |= D3D12_BARRIER_SYNC_COPY;
|
|
}
|
|
if (EnumHasAnyFlags(InD3D12Access, ED3D12Access::ResolveSrc))
|
|
{
|
|
EBSync |= D3D12_BARRIER_SYNC_RESOLVE;
|
|
}
|
|
if (EnumHasAnyFlags(InD3D12Access, ED3D12Access::DSVRead))
|
|
{
|
|
EBSync |= D3D12_BARRIER_SYNC_DEPTH_STENCIL;
|
|
}
|
|
if (EnumHasAnyFlags(InD3D12Access, ED3D12Access::UAVCompute))
|
|
{
|
|
EBSync |= D3D12_BARRIER_SYNC_COMPUTE_SHADING;;
|
|
}
|
|
if (EnumHasAnyFlags(InD3D12Access, ED3D12Access::UAVGraphics))
|
|
{
|
|
EBSync |= D3D12_BARRIER_SYNC_VERTEX_SHADING
|
|
| D3D12_BARRIER_SYNC_PIXEL_SHADING;
|
|
}
|
|
if (EnumHasAnyFlags(InD3D12Access, ED3D12Access::RTV))
|
|
{
|
|
EBSync |= D3D12_BARRIER_SYNC_RENDER_TARGET;
|
|
}
|
|
if (EnumHasAnyFlags(InD3D12Access, ED3D12Access::CopyDest))
|
|
{
|
|
EBSync |= D3D12_BARRIER_SYNC_COPY;
|
|
}
|
|
if (EnumHasAnyFlags(InD3D12Access, ED3D12Access::ResolveDst))
|
|
{
|
|
EBSync |= D3D12_BARRIER_SYNC_RESOLVE;
|
|
}
|
|
if (EnumHasAnyFlags(InD3D12Access, ED3D12Access::DSVWrite))
|
|
{
|
|
EBSync |= D3D12_BARRIER_SYNC_DEPTH_STENCIL;
|
|
}
|
|
if (EnumHasAnyFlags(InD3D12Access, ED3D12Access::BVHRead))
|
|
{
|
|
EBSync |= D3D12_BARRIER_SYNC_RAYTRACING
|
|
| D3D12_BARRIER_SYNC_BUILD_RAYTRACING_ACCELERATION_STRUCTURE
|
|
| D3D12_BARRIER_SYNC_COPY_RAYTRACING_ACCELERATION_STRUCTURE
|
|
| D3D12_BARRIER_SYNC_EMIT_RAYTRACING_ACCELERATION_STRUCTURE_POSTBUILD_INFO;
|
|
}
|
|
if (EnumHasAnyFlags(InD3D12Access, ED3D12Access::BVHWrite))
|
|
{
|
|
EBSync |=
|
|
#if !PLATFORM_REQUIRES_SYNC_RAYTRACING_NOT_COMPATIBLE_WITH_ACCESS_AS_WRITE
|
|
D3D12_BARRIER_SYNC_RAYTRACING |
|
|
#endif
|
|
D3D12_BARRIER_SYNC_BUILD_RAYTRACING_ACCELERATION_STRUCTURE
|
|
| D3D12_BARRIER_SYNC_COPY_RAYTRACING_ACCELERATION_STRUCTURE;
|
|
}
|
|
if (EnumHasAnyFlags(InD3D12Access, ED3D12Access::ShadingRateSource))
|
|
{
|
|
EBSync |= D3D12_BARRIER_SYNC_PIXEL_SHADING;
|
|
}
|
|
|
|
// Make sure we at least set one of the bits or it's
|
|
// really only CPURead which requires no GPU sync
|
|
static_assert(D3D12_BARRIER_SYNC_NONE == 0);
|
|
static constexpr ED3D12Access AccessBitsThatCanBeNone =
|
|
ED3D12Access::Present |
|
|
ED3D12Access::CPURead;
|
|
|
|
check((EBSync != D3D12_BARRIER_SYNC_NONE)
|
|
|| EnumHasAnyOneFlag(InD3D12Access, AccessBitsThatCanBeNone));
|
|
|
|
if (InPipe != ERHIPipeline::All)
|
|
{
|
|
check(EnumHasOneFlag(InPipe));
|
|
return FilterToQueueCompatibleSync(EBSync, InPipe);
|
|
}
|
|
else
|
|
{
|
|
return EBSync;
|
|
}
|
|
}
|
|
|
|
static D3D12_BARRIER_SYNC GetEBSync(
|
|
ERHIAccess InRHIAccess,
|
|
ERHIPipeline InPipe)
|
|
{
|
|
return GetEBSync(ConvertToD3D12Access(InRHIAccess), InPipe);
|
|
}
|
|
|
|
static D3D12_BARRIER_ACCESS GetEBAccess(
|
|
ED3D12Access InD3D12Access,
|
|
ERHIPipeline InPipe)
|
|
{
|
|
if (InPipe == ERHIPipeline::None)
|
|
{
|
|
checkNoEntry();
|
|
return D3D12_BARRIER_ACCESS_NO_ACCESS;
|
|
}
|
|
|
|
if (InD3D12Access == ED3D12Access::Unknown)
|
|
{
|
|
return D3D12_BARRIER_ACCESS_COMMON;
|
|
}
|
|
|
|
if (EnumHasAnyFlags(InD3D12Access, ED3D12Access::Common))
|
|
{
|
|
return D3D12_BARRIER_ACCESS_COMMON;
|
|
}
|
|
|
|
if (EnumHasAnyFlags(InD3D12Access, ED3D12Access::GenericRead))
|
|
{
|
|
return D3D12_BARRIER_ACCESS_COMMON;
|
|
}
|
|
|
|
if (EnumHasAnyFlags(InD3D12Access, ED3D12Access::Discard))
|
|
{
|
|
check(InD3D12Access == ED3D12Access::Discard);
|
|
return D3D12_BARRIER_ACCESS_NO_ACCESS;
|
|
}
|
|
|
|
D3D12_BARRIER_ACCESS EBAccess = {};
|
|
if (EnumHasAnyFlags(InD3D12Access, ED3D12Access::CPURead))
|
|
{
|
|
EBAccess |= D3D12_BARRIER_ACCESS_COMMON;
|
|
}
|
|
if (EnumHasAnyFlags(InD3D12Access, ED3D12Access::Present))
|
|
{
|
|
EBAccess |= D3D12_BARRIER_ACCESS_COMMON;
|
|
}
|
|
if (EnumHasAnyFlags(InD3D12Access, ED3D12Access::IndirectArgs))
|
|
{
|
|
EBAccess |= D3D12_BARRIER_ACCESS_INDIRECT_ARGUMENT;
|
|
}
|
|
if (EnumHasAnyFlags(InD3D12Access, ED3D12Access::VertexOrIndexBuffer))
|
|
{
|
|
// @TODO - This sucks... need more specific RHI bits or to pass in a resource description
|
|
EBAccess |= D3D12_BARRIER_ACCESS_VERTEX_BUFFER
|
|
| D3D12_BARRIER_ACCESS_INDEX_BUFFER
|
|
| D3D12_BARRIER_ACCESS_CONSTANT_BUFFER;
|
|
}
|
|
if (EnumHasAnyFlags(InD3D12Access, ED3D12Access::SRVMask))
|
|
{
|
|
EBAccess |= D3D12_BARRIER_ACCESS_SHADER_RESOURCE;
|
|
}
|
|
if (EnumHasAnyFlags(InD3D12Access, ED3D12Access::CopySrc))
|
|
{
|
|
EBAccess |= D3D12_BARRIER_ACCESS_COPY_SOURCE;
|
|
}
|
|
if (EnumHasAnyFlags(InD3D12Access, ED3D12Access::ResolveSrc))
|
|
{
|
|
EBAccess |= D3D12_BARRIER_ACCESS_RESOLVE_SOURCE;
|
|
}
|
|
if (EnumHasAnyFlags(InD3D12Access, ED3D12Access::DSVRead))
|
|
{
|
|
EBAccess |= D3D12_BARRIER_ACCESS_DEPTH_STENCIL_READ;
|
|
}
|
|
if (EnumHasAnyFlags(InD3D12Access, ED3D12Access::UAVMask))
|
|
{
|
|
EBAccess |= D3D12_BARRIER_ACCESS_UNORDERED_ACCESS;
|
|
}
|
|
if (EnumHasAnyFlags(InD3D12Access, ED3D12Access::RTV))
|
|
{
|
|
EBAccess |= D3D12_BARRIER_ACCESS_RENDER_TARGET;
|
|
}
|
|
if (EnumHasAnyFlags(InD3D12Access, ED3D12Access::CopyDest))
|
|
{
|
|
EBAccess |= D3D12_BARRIER_ACCESS_COPY_DEST;
|
|
}
|
|
if (EnumHasAnyFlags(InD3D12Access, ED3D12Access::ResolveDst))
|
|
{
|
|
EBAccess |= D3D12_BARRIER_ACCESS_RESOLVE_DEST;
|
|
}
|
|
if (EnumHasAnyFlags(InD3D12Access, ED3D12Access::DSVWrite))
|
|
{
|
|
#if PLATFORM_REQUIRES_LAYOUT_DEPTH_STENCIL_WRITE_NOT_COMPATIBLE_WITH_ACCESS_DEPTH_STENCIL_READ
|
|
// @TODO - The validation layer claims READ isn't compatible with the WRITE layout
|
|
// so for now hack out the READ bit if we're also setting WRITE
|
|
EBAccess &= ~D3D12_BARRIER_ACCESS_DEPTH_STENCIL_READ;
|
|
#endif
|
|
EBAccess |= D3D12_BARRIER_ACCESS_DEPTH_STENCIL_WRITE;
|
|
}
|
|
if (EnumHasAnyFlags(InD3D12Access, ED3D12Access::BVHRead))
|
|
{
|
|
EBAccess |= D3D12_BARRIER_ACCESS_RAYTRACING_ACCELERATION_STRUCTURE_READ;
|
|
}
|
|
if (EnumHasAnyFlags(InD3D12Access, ED3D12Access::BVHWrite))
|
|
{
|
|
EBAccess |= D3D12_BARRIER_ACCESS_RAYTRACING_ACCELERATION_STRUCTURE_WRITE;
|
|
}
|
|
if (EnumHasAnyFlags(InD3D12Access, ED3D12Access::ShadingRateSource))
|
|
{
|
|
EBAccess |= D3D12_BARRIER_ACCESS_SHADING_RATE_SOURCE;
|
|
}
|
|
|
|
if (InPipe != ERHIPipeline::All)
|
|
{
|
|
check(EnumHasOneFlag(InPipe));
|
|
return FilterToQueueCompatibleAccess(EBAccess, InPipe);
|
|
}
|
|
else
|
|
{
|
|
return EBAccess;
|
|
}
|
|
}
|
|
|
|
static D3D12_BARRIER_ACCESS GetEBAccess(
|
|
ERHIAccess InRHIAccess,
|
|
ERHIPipeline InPipe)
|
|
{
|
|
return GetEBAccess(ConvertToD3D12Access(InRHIAccess), InPipe);
|
|
}
|
|
|
|
static D3D12_BARRIER_LAYOUT GetEBLayout(
|
|
ED3D12Access InD3D12Access,
|
|
ERHIPipeline InRHIPipe,
|
|
const FD3D12Texture* InTexture)
|
|
{
|
|
if (InD3D12Access == ED3D12Access::Unknown)
|
|
{
|
|
checkNoEntry();
|
|
return D3D12_BARRIER_LAYOUT_UNDEFINED;
|
|
}
|
|
|
|
if (EnumHasAnyFlags(InD3D12Access, ED3D12Access::IndirectArgs
|
|
| ED3D12Access::VertexOrIndexBuffer
|
|
| ED3D12Access::BVHRead
|
|
| ED3D12Access::BVHWrite))
|
|
{
|
|
// These are all buffer flags and the resource does not have a layout
|
|
checkNoEntry();
|
|
return D3D12_BARRIER_LAYOUT_UNDEFINED;
|
|
}
|
|
|
|
// Special cases
|
|
if (EnumHasAnyFlags(InD3D12Access, ED3D12Access::CPURead | ED3D12Access::Common))
|
|
{
|
|
return D3D12_BARRIER_LAYOUT_COMMON;
|
|
}
|
|
|
|
if (EnumHasAnyFlags(InD3D12Access, ED3D12Access::GenericRead))
|
|
{
|
|
switch (InRHIPipe)
|
|
{
|
|
case ERHIPipeline::Graphics:
|
|
return D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_GENERIC_READ;
|
|
case ERHIPipeline::AsyncCompute:
|
|
return D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_GENERIC_READ;
|
|
default:
|
|
return D3D12_BARRIER_LAYOUT_GENERIC_READ;
|
|
}
|
|
}
|
|
|
|
if (EnumHasOneFlag(InD3D12Access))
|
|
{
|
|
// First check the 1:1 translations
|
|
switch (InD3D12Access)
|
|
{
|
|
case ED3D12Access::Present:
|
|
return D3D12_BARRIER_LAYOUT_PRESENT;
|
|
case ED3D12Access::CopySrc:
|
|
switch (InRHIPipe)
|
|
{
|
|
case ERHIPipeline::Graphics:
|
|
return D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_COPY_SOURCE;
|
|
case ERHIPipeline::AsyncCompute:
|
|
return D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_COPY_SOURCE;
|
|
default:
|
|
return D3D12_BARRIER_LAYOUT_COPY_SOURCE;
|
|
}
|
|
case ED3D12Access::ResolveSrc:
|
|
return D3D12_BARRIER_LAYOUT_RESOLVE_SOURCE;
|
|
case ED3D12Access::DSVRead:
|
|
return D3D12_BARRIER_LAYOUT_DEPTH_STENCIL_READ;
|
|
case ED3D12Access::RTV:
|
|
return D3D12_BARRIER_LAYOUT_RENDER_TARGET;
|
|
case ED3D12Access::CopyDest:
|
|
switch (InRHIPipe)
|
|
{
|
|
case ERHIPipeline::Graphics:
|
|
return D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_COPY_DEST;
|
|
case ERHIPipeline::AsyncCompute:
|
|
return D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_COPY_DEST;
|
|
default:
|
|
return D3D12_BARRIER_LAYOUT_COPY_DEST;
|
|
}
|
|
case ED3D12Access::ResolveDst:
|
|
return D3D12_BARRIER_LAYOUT_RESOLVE_DEST;
|
|
case ED3D12Access::DSVWrite:
|
|
return D3D12_BARRIER_LAYOUT_DEPTH_STENCIL_WRITE;
|
|
case ED3D12Access::Discard:
|
|
return D3D12_BARRIER_LAYOUT_UNDEFINED;
|
|
}
|
|
}
|
|
|
|
// Special read+write case for depth stencil
|
|
if (InD3D12Access == (ED3D12Access::DSVRead | ED3D12Access::DSVWrite))
|
|
{
|
|
return D3D12_BARRIER_LAYOUT_DEPTH_STENCIL_WRITE;
|
|
}
|
|
|
|
// Now try the sets of flags that have the same layout translations
|
|
if (EnumOnlyContainsFlags(InD3D12Access, ED3D12Access::SRVMask))
|
|
{
|
|
D3D12_BARRIER_LAYOUT ExtraLayoutBits = {};
|
|
if (InTexture && InTexture->SkipsFastClearFinalize())
|
|
{
|
|
ExtraLayoutBits = GetSkipFastClearEliminateLayoutFlags();
|
|
}
|
|
|
|
switch (InRHIPipe)
|
|
{
|
|
case ERHIPipeline::Graphics:
|
|
return static_cast<D3D12_BARRIER_LAYOUT>(D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_SHADER_RESOURCE | ExtraLayoutBits);
|
|
case ERHIPipeline::AsyncCompute:
|
|
return static_cast<D3D12_BARRIER_LAYOUT>(D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_SHADER_RESOURCE | ExtraLayoutBits);
|
|
default:
|
|
return D3D12_BARRIER_LAYOUT_SHADER_RESOURCE;
|
|
}
|
|
}
|
|
if (EnumOnlyContainsFlags(InD3D12Access, ED3D12Access::UAVMask))
|
|
{
|
|
switch (InRHIPipe)
|
|
{
|
|
case ERHIPipeline::Graphics:
|
|
return D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_UNORDERED_ACCESS;
|
|
case ERHIPipeline::AsyncCompute:
|
|
return D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_UNORDERED_ACCESS;
|
|
default:
|
|
return D3D12_BARRIER_LAYOUT_UNORDERED_ACCESS;
|
|
}
|
|
}
|
|
|
|
// And finally check for multiple read states
|
|
if (EnumOnlyContainsFlags(InD3D12Access, ED3D12Access::ReadOnlyMask))
|
|
{
|
|
static constexpr
|
|
ED3D12Access GfxOnlyGenericReadBits =
|
|
// Other gfx-only bits excluded by the compute compatible versions are
|
|
// for buffer resources which have no defined layout so won't get here.
|
|
ED3D12Access::DSVRead
|
|
| ED3D12Access::ShadingRateSource
|
|
| ED3D12Access::ResolveSrc;
|
|
|
|
switch (InRHIPipe)
|
|
{
|
|
case ERHIPipeline::Graphics:
|
|
return D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_GENERIC_READ;
|
|
case ERHIPipeline::AsyncCompute:
|
|
check(!EnumHasAnyFlags(InD3D12Access, GfxOnlyGenericReadBits));
|
|
return D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_GENERIC_READ;
|
|
case ERHIPipeline::All:
|
|
#if PLATFORM_REQUIRES_ENHANCED_BARRIERS_GFX_ONLY_READ_BITS_HACK
|
|
// @TODO - This is to work around a hole in the API that won't allow DSVRead
|
|
// access with any compute queue compatible layout.
|
|
if (EnumHasAnyFlags(InD3D12Access, GfxOnlyGenericReadBits))
|
|
{
|
|
return D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_GENERIC_READ;
|
|
}
|
|
#endif
|
|
return D3D12_BARRIER_LAYOUT_GENERIC_READ;
|
|
default:
|
|
checkNoEntry();
|
|
return D3D12_BARRIER_LAYOUT_GENERIC_READ;
|
|
}
|
|
}
|
|
|
|
// Must be a combination of read and write flags
|
|
check(EnumHasAnyFlags(InD3D12Access, ED3D12Access::ReadableMask) &&
|
|
EnumHasAnyFlags(InD3D12Access, ED3D12Access::WritableMask));
|
|
|
|
switch (InRHIPipe)
|
|
{
|
|
case ERHIPipeline::Graphics:
|
|
return D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_COMMON;
|
|
case ERHIPipeline::AsyncCompute:
|
|
return D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_COMMON;
|
|
default:
|
|
check(!EnumHasAnyFlags(InD3D12Access, ED3D12Access::UAVMask));
|
|
return D3D12_BARRIER_LAYOUT_COMMON;
|
|
}
|
|
}
|
|
|
|
static D3D12_BARRIER_LAYOUT GetEBLayout(
|
|
ERHIAccess InRHIAccess,
|
|
ERHIPipeline InRHIPipe,
|
|
const FD3D12Texture* InTexture)
|
|
{
|
|
return GetEBLayout(ConvertToD3D12Access(InRHIAccess), InRHIPipe, InTexture);
|
|
}
|
|
|
|
static void LogGlobalBarrier(
|
|
D3D12_BARRIER_SYNC SyncBefore,
|
|
D3D12_BARRIER_SYNC SyncAfter,
|
|
D3D12_BARRIER_ACCESS AccessBefore,
|
|
D3D12_BARRIER_ACCESS AccessAfter)
|
|
{
|
|
#define ENUM_STRING_AND_VALUE(EnumVal) \
|
|
*ConvertToString(EnumVal), static_cast<uint32>(EnumVal)
|
|
|
|
UE_LOG(LogD3D12RHI, Log,
|
|
TEXT(R"(
|
|
D3D12 Global Barrier
|
|
| SyncBefore: %s (0x%08x)
|
|
| SyncAfter: %s (0x%08x)
|
|
| AccessBefore: %s (0x%08x)
|
|
| AccessAfter: %s (0x%08x)
|
|
)"),
|
|
ENUM_STRING_AND_VALUE(SyncBefore),
|
|
ENUM_STRING_AND_VALUE(SyncAfter),
|
|
ENUM_STRING_AND_VALUE(AccessBefore),
|
|
ENUM_STRING_AND_VALUE(AccessAfter));
|
|
|
|
#undef ENUM_STRING_AND_VALUE
|
|
}
|
|
|
|
template <typename TResourcePtr>
|
|
static const ID3D12Resource* GetID3D12ResourcePtr(
|
|
TResourcePtr ResourcePtr)
|
|
{
|
|
using NonCVResourceType = std::remove_cv_t<std::remove_pointer_t<TResourcePtr>>;
|
|
|
|
if constexpr (std::is_same_v<FD3D12Resource, NonCVResourceType>)
|
|
{
|
|
return ResourcePtr->GetResource();
|
|
}
|
|
else if constexpr (std::is_same_v<ID3D12Resource, NonCVResourceType>)
|
|
{
|
|
return ResourcePtr;
|
|
}
|
|
}
|
|
|
|
template <typename TResourcePtr>
|
|
static FString GetResourceName(
|
|
const TResourcePtr ResourcePtr)
|
|
{
|
|
using NonCVResourceType = std::remove_cv_t<std::remove_pointer_t<TResourcePtr>>;
|
|
|
|
if constexpr (std::is_same_v<FD3D12Resource, NonCVResourceType>)
|
|
{
|
|
return ResourcePtr->GetName()->ToString();
|
|
}
|
|
else if constexpr (std::is_same_v<ID3D12Resource, NonCVResourceType>)
|
|
{
|
|
return GetD312ObjectName(const_cast<NonCVResourceType*>(ResourcePtr));
|
|
}
|
|
}
|
|
|
|
template <typename TResourcePtr>
|
|
static void LogTextureBarrier(
|
|
const TResourcePtr* pResource,
|
|
D3D12_BARRIER_SYNC SyncBefore,
|
|
D3D12_BARRIER_SYNC SyncAfter,
|
|
D3D12_BARRIER_ACCESS AccessBefore,
|
|
D3D12_BARRIER_ACCESS AccessAfter,
|
|
D3D12_BARRIER_LAYOUT LayoutBefore,
|
|
D3D12_BARRIER_LAYOUT LayoutAfter,
|
|
uint32 Subresource,
|
|
bool bDiscard)
|
|
{
|
|
#define ENUM_STRING_AND_VALUE(EnumVal) \
|
|
*ConvertToString(EnumVal), static_cast<uint32>(EnumVal)
|
|
|
|
UE_LOG(LogD3D12RHI, Log,
|
|
TEXT(R"(
|
|
D3D12 Texture Barrier
|
|
| Resource: %s (0x%p)
|
|
| SyncBefore: %s (0x%08x)
|
|
| SyncAfter: %s (0x%08x)
|
|
| AccessBefore: %s (0x%08x)
|
|
| AccessAfter: %s (0x%08x)
|
|
| LayoutBefore: %s (0x%08x)
|
|
| LayoutAfter: %s (0x%08x)
|
|
| Subresource: %u
|
|
| Discard: %u
|
|
)"),
|
|
*GetResourceName(pResource), GetID3D12ResourcePtr(pResource),
|
|
ENUM_STRING_AND_VALUE(SyncBefore),
|
|
ENUM_STRING_AND_VALUE(SyncAfter),
|
|
ENUM_STRING_AND_VALUE(AccessBefore),
|
|
ENUM_STRING_AND_VALUE(AccessAfter),
|
|
ENUM_STRING_AND_VALUE(LayoutBefore),
|
|
ENUM_STRING_AND_VALUE(LayoutAfter),
|
|
Subresource,
|
|
static_cast<uint32>(bDiscard));
|
|
|
|
#undef ENUM_STRING_AND_VALUE
|
|
}
|
|
|
|
template <typename TResourcePtr>
|
|
static void LogBufferBarrier(
|
|
const TResourcePtr* pResource,
|
|
D3D12_BARRIER_SYNC SyncBefore,
|
|
D3D12_BARRIER_SYNC SyncAfter,
|
|
D3D12_BARRIER_ACCESS AccessBefore,
|
|
D3D12_BARRIER_ACCESS AccessAfter)
|
|
{
|
|
#define ENUM_STRING_AND_VALUE(EnumVal) \
|
|
*ConvertToString(EnumVal), static_cast<uint32>(EnumVal)
|
|
|
|
UE_LOG(LogD3D12RHI, Log,
|
|
TEXT(R"(
|
|
D3D12 Buffer Barrier
|
|
| Resource: %s (0x%p)
|
|
| SyncBefore: %s (0x%08x)
|
|
| SyncAfter: %s (0x%08x)
|
|
| AccessBefore: %s (0x%08x)
|
|
| AccessAfter: %s (0x%08x)
|
|
)"),
|
|
*GetResourceName(pResource), GetID3D12ResourcePtr(pResource),
|
|
ENUM_STRING_AND_VALUE(SyncBefore),
|
|
ENUM_STRING_AND_VALUE(SyncAfter),
|
|
ENUM_STRING_AND_VALUE(AccessBefore),
|
|
ENUM_STRING_AND_VALUE(AccessAfter));
|
|
|
|
#undef ENUM_STRING_AND_VALUE
|
|
}
|
|
|
|
template <typename TBarrierType>
|
|
static void LogBarriers(
|
|
TArrayView<const TBarrierType> Barriers)
|
|
{
|
|
using NonCVBarrierType = std::remove_cv_t<TBarrierType>;
|
|
|
|
for (int32 BarrierIdx = 0; BarrierIdx < Barriers.Num(); ++BarrierIdx)
|
|
{
|
|
const TBarrierType& Barrier = Barriers[BarrierIdx];
|
|
|
|
if constexpr (std::is_same_v<D3D12_BARRIER_GROUP, NonCVBarrierType>)
|
|
{
|
|
switch (Barrier.Type)
|
|
{
|
|
case D3D12_BARRIER_TYPE_GLOBAL:
|
|
LogBarriers(MakeArrayView(Barrier.pGlobalBarriers, Barrier.NumBarriers));
|
|
break;
|
|
case D3D12_BARRIER_TYPE_BUFFER:
|
|
LogBarriers(MakeArrayView(Barrier.pBufferBarriers, Barrier.NumBarriers));
|
|
break;
|
|
case D3D12_BARRIER_TYPE_TEXTURE:
|
|
LogBarriers(MakeArrayView(Barrier.pTextureBarriers, Barrier.NumBarriers));
|
|
break;
|
|
default:
|
|
checkNoEntry();
|
|
break;
|
|
}
|
|
}
|
|
else if constexpr (std::is_same_v<D3D12_GLOBAL_BARRIER, NonCVBarrierType>)
|
|
{
|
|
LogGlobalBarrier(
|
|
Barrier.SyncBefore,
|
|
Barrier.SyncAfter,
|
|
Barrier.AccessBefore,
|
|
Barrier.AccessAfter);
|
|
}
|
|
else if constexpr (std::is_same_v<D3D12_BUFFER_BARRIER, NonCVBarrierType>)
|
|
{
|
|
LogBufferBarrier(
|
|
Barrier.pResource,
|
|
Barrier.SyncBefore,
|
|
Barrier.SyncAfter,
|
|
Barrier.AccessBefore,
|
|
Barrier.AccessAfter);
|
|
}
|
|
else if constexpr (std::is_same_v<D3D12_TEXTURE_BARRIER, NonCVBarrierType>)
|
|
{
|
|
// If this isn't zero then the Subresources struct represents a range
|
|
// and we're not setup to log that. If you hit this, add the needed code.
|
|
check(Barrier.Subresources.NumMipLevels == 0);
|
|
|
|
LogTextureBarrier(
|
|
Barrier.pResource,
|
|
Barrier.SyncBefore,
|
|
Barrier.SyncAfter,
|
|
Barrier.AccessBefore,
|
|
Barrier.AccessAfter,
|
|
Barrier.LayoutBefore,
|
|
Barrier.LayoutAfter,
|
|
Barrier.Subresources.IndexOrFirstMipLevel,
|
|
!!(Barrier.Flags & D3D12_TEXTURE_BARRIER_FLAG_DISCARD));
|
|
}
|
|
else
|
|
{
|
|
static_assert(std::is_same_v<D3D12_TEXTURE_BARRIER, NonCVBarrierType>);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Use the top bit of the flags enum to mark transitions as "idle" time (used to remove the swapchain wait time for back buffers).
|
|
static const D3D12_BARRIER_TYPE BarrierType_CountAsIdleTime = D3D12_BARRIER_TYPE(1ull << ((sizeof(D3D12_BARRIER_TYPE) * 8) - 1));
|
|
static const D3D12_BARRIER_TYPE D3D12_BARRIER_TYPE_GLOBAL_COUNT_AS_IDLE = D3D12_BARRIER_TYPE(D3D12_BARRIER_TYPE_GLOBAL | BarrierType_CountAsIdleTime);
|
|
static const D3D12_BARRIER_TYPE D3D12_BARRIER_TYPE_TEXTURE_COUNT_AS_IDLE = D3D12_BARRIER_TYPE(D3D12_BARRIER_TYPE_TEXTURE | BarrierType_CountAsIdleTime);
|
|
static const D3D12_BARRIER_TYPE D3D12_BARRIER_TYPE_BUFFER_COUNT_AS_IDLE = D3D12_BARRIER_TYPE(D3D12_BARRIER_TYPE_BUFFER | BarrierType_CountAsIdleTime);
|
|
|
|
class FD3D12EnhancedBarriersBatcher
|
|
{
|
|
struct FD3D12BarrierGroup : public D3D12_BARRIER_GROUP
|
|
{
|
|
FD3D12BarrierGroup() = default;
|
|
FD3D12BarrierGroup(D3D12_BARRIER_GROUP&& BarrierGroup) : D3D12_BARRIER_GROUP(MoveTemp(BarrierGroup)) {}
|
|
|
|
bool HasIdleFlag() const { return !!(Type & BarrierType_CountAsIdleTime); }
|
|
void ClearIdleFlag() { Type = static_cast<D3D12_BARRIER_TYPE>(Type & ~BarrierType_CountAsIdleTime); }
|
|
};
|
|
static_assert(sizeof(FD3D12BarrierGroup) == sizeof(D3D12_BARRIER_GROUP), "FD3D12ResourceBarrier is a wrapper to add helper functions. Do not add members.");
|
|
|
|
public:
|
|
template <typename T>
|
|
void AddBarriers(
|
|
FD3D12ContextCommon& Context,
|
|
TArrayView<T>&& Barriers,
|
|
bool IdleTime);
|
|
|
|
void AddGlobalBarrier(
|
|
FD3D12ContextCommon& Context,
|
|
D3D12_BARRIER_SYNC SyncBefore,
|
|
D3D12_BARRIER_SYNC SyncAfter,
|
|
D3D12_BARRIER_ACCESS AccessBefore,
|
|
D3D12_BARRIER_ACCESS AccessAfter);
|
|
|
|
void AddTextureBarrier(
|
|
FD3D12ContextCommon& Context,
|
|
const FD3D12Resource* pResource,
|
|
D3D12_BARRIER_SYNC SyncBefore,
|
|
D3D12_BARRIER_SYNC SyncAfter,
|
|
D3D12_BARRIER_ACCESS AccessBefore,
|
|
D3D12_BARRIER_ACCESS AccessAfter,
|
|
D3D12_BARRIER_LAYOUT LayoutBefore,
|
|
D3D12_BARRIER_LAYOUT LayoutAfter,
|
|
uint32 Subresource,
|
|
bool bDiscard);
|
|
|
|
void AddBufferBarrier(
|
|
FD3D12ContextCommon& Context,
|
|
const FD3D12Resource* pResource,
|
|
D3D12_BARRIER_SYNC SyncBefore,
|
|
D3D12_BARRIER_SYNC SyncAfter,
|
|
D3D12_BARRIER_ACCESS AccessBefore,
|
|
D3D12_BARRIER_ACCESS AccessAfter);
|
|
|
|
void FlushIntoCommandList(
|
|
class FD3D12CommandList& CommandList,
|
|
class FD3D12QueryAllocator& TimestampAllocator);
|
|
|
|
int32 Num() const
|
|
{
|
|
return BarrierGroups.Num();
|
|
}
|
|
|
|
private:
|
|
template <D3D12_BARRIER_TYPE TBarrierType>
|
|
static auto GetMemberArrayForBarrierType();
|
|
|
|
TArray<FD3D12BarrierGroup> BarrierGroups;
|
|
|
|
// @TODO - Shared Allocation/Allocator?
|
|
TArray<D3D12_TEXTURE_BARRIER> TextureBarriers;
|
|
TArray<D3D12_BUFFER_BARRIER> BufferBarriers;
|
|
TArray<D3D12_GLOBAL_BARRIER> GlobalBarriers;
|
|
};
|
|
|
|
template <typename T>
|
|
static consteval
|
|
D3D12_BARRIER_TYPE GetBarrierType()
|
|
{
|
|
using TNonQualifiedType = std::remove_cv_t<T>;
|
|
if constexpr (std::is_same_v<D3D12_GLOBAL_BARRIER, TNonQualifiedType>)
|
|
{
|
|
return D3D12_BARRIER_TYPE_GLOBAL;
|
|
}
|
|
else if constexpr (std::is_same_v<D3D12_TEXTURE_BARRIER, TNonQualifiedType>)
|
|
{
|
|
return D3D12_BARRIER_TYPE_TEXTURE;
|
|
}
|
|
else if constexpr (std::is_same_v<D3D12_BUFFER_BARRIER, TNonQualifiedType>)
|
|
{
|
|
return D3D12_BARRIER_TYPE_BUFFER;
|
|
}
|
|
else
|
|
{
|
|
static_assert(std::is_same_v<D3D12_BUFFER_BARRIER, T>, "Unknown barrier type");
|
|
}
|
|
}
|
|
|
|
template <D3D12_BARRIER_TYPE TBarrierType>
|
|
static auto FD3D12EnhancedBarriersBatcher::GetMemberArrayForBarrierType()
|
|
{
|
|
if constexpr (TBarrierType == D3D12_BARRIER_TYPE_GLOBAL)
|
|
{
|
|
return &FD3D12EnhancedBarriersBatcher::GlobalBarriers;
|
|
}
|
|
else if constexpr (TBarrierType == D3D12_BARRIER_TYPE_TEXTURE)
|
|
{
|
|
return &FD3D12EnhancedBarriersBatcher::TextureBarriers;
|
|
}
|
|
else if constexpr (TBarrierType == D3D12_BARRIER_TYPE_BUFFER)
|
|
{
|
|
return &FD3D12EnhancedBarriersBatcher::BufferBarriers;
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
void FD3D12EnhancedBarriersBatcher::AddBarriers(
|
|
FD3D12ContextCommon& Context,
|
|
TArrayView<T>&& Barriers,
|
|
bool IdleTime)
|
|
{
|
|
using TNonQualifiedType = std::remove_cv_t<T>;
|
|
|
|
check(!Barriers.IsEmpty());
|
|
|
|
checkSlow(CheckBarrierValuesAreCompatible(Barriers, Context.GetRHIPipeline()));
|
|
|
|
#if D3D12_ENHANCED_BARRIERS_LOG_BARRIERS_WHEN_BATCHED
|
|
LogBarriers(Barriers);
|
|
#endif
|
|
|
|
static constexpr D3D12_BARRIER_TYPE BaseBarrierType = GetBarrierType<TNonQualifiedType>();
|
|
TArray<TNonQualifiedType>& BarrierArray = this->*GetMemberArrayForBarrierType<BaseBarrierType>();
|
|
|
|
const D3D12_BARRIER_TYPE AdditionalTypeFlags = static_cast<D3D12_BARRIER_TYPE>(IdleTime ? BarrierType_CountAsIdleTime : 0);
|
|
const D3D12_BARRIER_TYPE BarrierType = static_cast<D3D12_BARRIER_TYPE>(BaseBarrierType | AdditionalTypeFlags);
|
|
|
|
const uint32 NumBarriers = Barriers.Num();
|
|
const bool BarrierArrayIsEmpty = BarrierArray.IsEmpty();
|
|
|
|
BarrierArray.Append(MoveTemp(Barriers));
|
|
|
|
if (!BarrierArrayIsEmpty && (BarrierGroups.Last().Type == BarrierType))
|
|
{
|
|
BarrierGroups.Last().NumBarriers += NumBarriers;
|
|
}
|
|
else
|
|
{
|
|
// Smuggle an offset in the pointer field of each group. We'll fix it before submitting
|
|
const T* FirstBarrierInGroupIdx = reinterpret_cast<T*>(static_cast<uintptr_t>(BarrierArray.Num()) - NumBarriers);
|
|
if constexpr (std::is_same_v<D3D12_GLOBAL_BARRIER, TNonQualifiedType>)
|
|
{
|
|
BarrierGroups.Add(FD3D12BarrierGroup({
|
|
.Type = BarrierType,
|
|
.NumBarriers = NumBarriers,
|
|
.pGlobalBarriers = FirstBarrierInGroupIdx,
|
|
}));
|
|
}
|
|
else if constexpr (std::is_same_v<D3D12_TEXTURE_BARRIER, TNonQualifiedType>)
|
|
{
|
|
BarrierGroups.Add(FD3D12BarrierGroup({
|
|
.Type = BarrierType,
|
|
.NumBarriers = NumBarriers,
|
|
.pTextureBarriers = FirstBarrierInGroupIdx,
|
|
}));
|
|
}
|
|
else if constexpr (std::is_same_v<D3D12_BUFFER_BARRIER, TNonQualifiedType>)
|
|
{
|
|
BarrierGroups.Add(FD3D12BarrierGroup({
|
|
.Type = BarrierType,
|
|
.NumBarriers = NumBarriers,
|
|
.pBufferBarriers = FirstBarrierInGroupIdx,
|
|
}));
|
|
}
|
|
else
|
|
{
|
|
static_assert(std::is_same_v<D3D12_BUFFER_BARRIER, TNonQualifiedType>, "Unknown barrier type");
|
|
}
|
|
}
|
|
|
|
if (!GD3D12BatchResourceBarriers)
|
|
{
|
|
FlushIntoCommandList(Context.GetCommandList(), Context.GetTimestampQueries());
|
|
}
|
|
}
|
|
|
|
void FD3D12EnhancedBarriersBatcher::AddGlobalBarrier(
|
|
FD3D12ContextCommon& Context,
|
|
D3D12_BARRIER_SYNC SyncBefore,
|
|
D3D12_BARRIER_SYNC SyncAfter,
|
|
D3D12_BARRIER_ACCESS AccessBefore,
|
|
D3D12_BARRIER_ACCESS AccessAfter)
|
|
{
|
|
if (!BarrierCanBeDiscarded(
|
|
SyncBefore,
|
|
SyncAfter,
|
|
AccessBefore,
|
|
AccessAfter,
|
|
D3D12_BARRIER_LAYOUT_UNDEFINED,
|
|
D3D12_BARRIER_LAYOUT_UNDEFINED))
|
|
{
|
|
AddBarriers(
|
|
Context,
|
|
MakeArrayView<D3D12_GLOBAL_BARRIER>({{
|
|
.SyncBefore = SyncBefore,
|
|
.SyncAfter = SyncAfter,
|
|
.AccessBefore = AccessBefore,
|
|
.AccessAfter = AccessAfter,
|
|
}}),
|
|
false);
|
|
}
|
|
}
|
|
|
|
static bool IsBackBufferWriteTransition(
|
|
const FD3D12Resource* InResource,
|
|
D3D12_BARRIER_ACCESS AccessAfter,
|
|
D3D12_BARRIER_LAYOUT LayoutBefore)
|
|
{
|
|
static constexpr D3D12_BARRIER_ACCESS BackBufferBarrierWriteAccess =
|
|
D3D12_BARRIER_ACCESS_RENDER_TARGET
|
|
| D3D12_BARRIER_ACCESS_UNORDERED_ACCESS
|
|
| D3D12_BARRIER_ACCESS_STREAM_OUTPUT
|
|
| D3D12_BARRIER_ACCESS_COPY_DEST
|
|
| D3D12_BARRIER_ACCESS_RESOLVE_DEST;
|
|
|
|
static constexpr bool bCommonIsDistinctFromPresent =
|
|
D3D12_BARRIER_LAYOUT_COMMON != D3D12_BARRIER_LAYOUT_PRESENT;
|
|
|
|
const bool bIsBackBufferWriteTransition =
|
|
InResource->IsBackBuffer()
|
|
&& EnumHasAnyFlags(AccessAfter, BackBufferBarrierWriteAccess);
|
|
|
|
if constexpr (bCommonIsDistinctFromPresent)
|
|
{
|
|
return bIsBackBufferWriteTransition
|
|
&& (LayoutBefore == D3D12_BARRIER_LAYOUT_PRESENT);
|
|
}
|
|
else
|
|
{
|
|
return bIsBackBufferWriteTransition;
|
|
}
|
|
}
|
|
|
|
void FD3D12EnhancedBarriersBatcher::AddTextureBarrier(
|
|
FD3D12ContextCommon& Context,
|
|
const FD3D12Resource* pResource,
|
|
D3D12_BARRIER_SYNC SyncBefore,
|
|
D3D12_BARRIER_SYNC SyncAfter,
|
|
D3D12_BARRIER_ACCESS AccessBefore,
|
|
D3D12_BARRIER_ACCESS AccessAfter,
|
|
D3D12_BARRIER_LAYOUT LayoutBefore,
|
|
D3D12_BARRIER_LAYOUT LayoutAfter,
|
|
uint32 Subresource,
|
|
bool bDiscard)
|
|
{
|
|
// This is not the barrier you're looking for
|
|
check(pResource->GetDesc().Dimension != D3D12_RESOURCE_DIMENSION_BUFFER);
|
|
|
|
check(pResource->GetResource());
|
|
|
|
// EB spec says discard flag can only be used when LayoutBefore is UNDEFINED
|
|
check(!bDiscard || (LayoutBefore == D3D12_BARRIER_LAYOUT_UNDEFINED));
|
|
|
|
if (!BarrierCanBeDiscarded(
|
|
SyncBefore,
|
|
SyncAfter,
|
|
AccessBefore,
|
|
AccessAfter,
|
|
LayoutBefore,
|
|
LayoutAfter))
|
|
{
|
|
const D3D12_TEXTURE_BARRIER_FLAGS Flags =
|
|
bDiscard ?
|
|
D3D12_TEXTURE_BARRIER_FLAG_DISCARD :
|
|
D3D12_TEXTURE_BARRIER_FLAG_NONE;
|
|
|
|
const bool bIsBackBufferWriteTransition =
|
|
IsBackBufferWriteTransition(
|
|
pResource,
|
|
AccessAfter,
|
|
LayoutBefore);
|
|
|
|
AddBarriers(
|
|
Context,
|
|
MakeArrayView<D3D12_TEXTURE_BARRIER>({{
|
|
.SyncBefore = SyncBefore,
|
|
.SyncAfter = SyncAfter,
|
|
.AccessBefore = AccessBefore,
|
|
.AccessAfter = AccessAfter,
|
|
.LayoutBefore = LayoutBefore,
|
|
.LayoutAfter = LayoutAfter,
|
|
.pResource = pResource->GetResource(),
|
|
.Subresources = {
|
|
.IndexOrFirstMipLevel = Subresource,
|
|
.NumMipLevels = 0, // 0 indicates a subresource index
|
|
.FirstArraySlice = {},
|
|
.NumArraySlices = {},
|
|
.FirstPlane = {},
|
|
.NumPlanes = {},
|
|
},
|
|
.Flags = Flags,
|
|
}}),
|
|
bIsBackBufferWriteTransition);
|
|
}
|
|
}
|
|
|
|
void FD3D12EnhancedBarriersBatcher::AddBufferBarrier(
|
|
FD3D12ContextCommon& Context,
|
|
const FD3D12Resource* pResource,
|
|
D3D12_BARRIER_SYNC SyncBefore,
|
|
D3D12_BARRIER_SYNC SyncAfter,
|
|
D3D12_BARRIER_ACCESS AccessBefore,
|
|
D3D12_BARRIER_ACCESS AccessAfter)
|
|
{
|
|
check(pResource->GetDesc().Dimension == D3D12_RESOURCE_DIMENSION_BUFFER);
|
|
|
|
if (!BarrierCanBeDiscarded(
|
|
SyncBefore,
|
|
SyncAfter,
|
|
AccessBefore,
|
|
AccessAfter,
|
|
D3D12_BARRIER_LAYOUT_UNDEFINED,
|
|
D3D12_BARRIER_LAYOUT_UNDEFINED))
|
|
{
|
|
|
|
AddBarriers(
|
|
Context,
|
|
MakeArrayView<D3D12_BUFFER_BARRIER>({{
|
|
.SyncBefore = SyncBefore,
|
|
.SyncAfter = SyncAfter,
|
|
.AccessBefore = AccessBefore,
|
|
.AccessAfter = AccessAfter,
|
|
.pResource = pResource->GetResource(),
|
|
.Offset = 0,
|
|
.Size = UINT64_MAX,
|
|
}}),
|
|
false);
|
|
}
|
|
}
|
|
|
|
void FD3D12EnhancedBarriersBatcher::FlushIntoCommandList(
|
|
FD3D12CommandList& CommandList,
|
|
FD3D12QueryAllocator& TimestampAllocator)
|
|
{
|
|
auto InsertTimestamp = [&](bool bBegin)
|
|
{
|
|
#if RHI_NEW_GPU_PROFILER
|
|
if (bBegin)
|
|
{
|
|
auto& Event = CommandList.EmplaceProfilerEvent<UE::RHI::GPUProfiler::FEvent::FEndWork>();
|
|
CommandList.EndQuery(TimestampAllocator.Allocate(ED3D12QueryType::ProfilerTimestampBOP, &Event.GPUTimestampBOP));
|
|
}
|
|
else
|
|
{
|
|
// CPUTimestamp is filled in at submission time in FlushProfilerEvents
|
|
auto& Event = CommandList.EmplaceProfilerEvent<UE::RHI::GPUProfiler::FEvent::FBeginWork>(0);
|
|
CommandList.EndQuery(TimestampAllocator.Allocate(ED3D12QueryType::ProfilerTimestampTOP, &Event.GPUTimestampTOP));
|
|
}
|
|
#else
|
|
ED3D12QueryType Type = bBegin
|
|
? ED3D12QueryType::IdleBegin
|
|
: ED3D12QueryType::IdleEnd;
|
|
|
|
CommandList.EndQuery(TimestampAllocator.Allocate(Type, nullptr));
|
|
#endif
|
|
};
|
|
|
|
for (int32 BatchStart = 0, BatchEnd = 0; BatchStart < BarrierGroups.Num(); BatchStart = BatchEnd)
|
|
{
|
|
// Gather a range of barriers that all have the same idle flag
|
|
bool const bIdle = BarrierGroups[BatchEnd].HasIdleFlag();
|
|
|
|
while (BatchEnd < BarrierGroups.Num()
|
|
&& bIdle == BarrierGroups[BatchEnd].HasIdleFlag())
|
|
{
|
|
// Clear the idle flag since it's not a valid D3D bit.
|
|
BarrierGroups[BatchEnd++].ClearIdleFlag();
|
|
}
|
|
|
|
// Insert an idle begin/end timestamp around the barrier batch if required.
|
|
if (bIdle)
|
|
{
|
|
InsertTimestamp(true);
|
|
}
|
|
|
|
//
|
|
// Convert the offset in each barrier group to a pointer now that we can be sure the memory won't move around
|
|
//
|
|
|
|
for (int32 GroupIdx = BatchStart; GroupIdx < BatchEnd; ++GroupIdx)
|
|
{
|
|
D3D12_BARRIER_GROUP& BarrierGroup = BarrierGroups[GroupIdx];
|
|
uintptr_t FirstBarrierIdx = {};
|
|
switch (BarrierGroup.Type)
|
|
{
|
|
case D3D12_BARRIER_TYPE_GLOBAL:
|
|
FirstBarrierIdx = reinterpret_cast<uintptr_t>(BarrierGroup.pGlobalBarriers);
|
|
BarrierGroup.pGlobalBarriers = &GlobalBarriers[FirstBarrierIdx];
|
|
break;
|
|
case D3D12_BARRIER_TYPE_TEXTURE:
|
|
FirstBarrierIdx = reinterpret_cast<uintptr_t>(BarrierGroup.pTextureBarriers);
|
|
BarrierGroup.pTextureBarriers = &TextureBarriers[FirstBarrierIdx];
|
|
break;
|
|
case D3D12_BARRIER_TYPE_BUFFER:
|
|
FirstBarrierIdx = reinterpret_cast<uintptr_t>(BarrierGroup.pBufferBarriers);
|
|
BarrierGroup.pBufferBarriers = &BufferBarriers[FirstBarrierIdx];
|
|
break;
|
|
default:
|
|
checkNoEntry();
|
|
break;
|
|
}
|
|
}
|
|
|
|
const int32 BatchNumGroups = BatchEnd - BatchStart;
|
|
#if D3D12_ENHANCED_BARRIERS_LOG_BARRIERS_WHEN_FLUSHED
|
|
LogBarriers(MakeArrayView<const D3D12_BARRIER_GROUP>(&BarrierGroups[BatchStart], BatchNumGroups));
|
|
#endif
|
|
CommandList.GraphicsCommandList8()->Barrier(BatchNumGroups, &BarrierGroups[BatchStart]);
|
|
|
|
if (bIdle)
|
|
{
|
|
InsertTimestamp(false);
|
|
}
|
|
}
|
|
|
|
BarrierGroups.Reset();
|
|
GlobalBarriers.Reset();
|
|
TextureBarriers.Reset();
|
|
BufferBarriers.Reset();
|
|
}
|
|
|
|
D3D12_BARRIER_LAYOUT FD3D12EnhancedBarriersForAdapterImpl::GetInitialLayout(
|
|
ED3D12Access InD3D12Access,
|
|
const FD3D12ResourceDesc& InDesc)
|
|
{
|
|
// This makes the assumption that all resources begin life on the gfx pipe
|
|
const bool bIsBuffer = InDesc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER;
|
|
return bIsBuffer ?
|
|
D3D12_BARRIER_LAYOUT_UNDEFINED :
|
|
GetEBLayout(InD3D12Access, ERHIPipeline::Graphics, nullptr);
|
|
}
|
|
|
|
void FD3D12EnhancedBarriersForAdapterImpl::ConfigureDevice(
|
|
ID3D12Device* Device,
|
|
bool InWithD3DDebug)
|
|
{
|
|
FD3D12DynamicRHI::SetFormatAliasedTexturesMustBeCreatedUsingCommonLayout(false);
|
|
GRHIGlobals.NeedsTransientDiscardStateTracking = false;
|
|
GRHIGlobals.NeedsTransientDiscardOnGraphicsWorkaround = false;
|
|
}
|
|
|
|
uint64 FD3D12EnhancedBarriersForAdapterImpl::GetTransitionDataSizeBytes()
|
|
{
|
|
return sizeof(FD3D12EnhancedBarriersTransitionData);
|
|
}
|
|
|
|
uint64 FD3D12EnhancedBarriersForAdapterImpl::GetTransitionDataAlignmentBytes()
|
|
{
|
|
return alignof(FD3D12EnhancedBarriersTransitionData);
|
|
}
|
|
|
|
void FD3D12EnhancedBarriersForAdapterImpl::CreateTransition(
|
|
FRHITransition* Transition,
|
|
const FRHITransitionCreateInfo& CreateInfo)
|
|
{
|
|
// Construct the data in-place on the transition instance
|
|
FD3D12EnhancedBarriersTransitionData* Data =
|
|
new (Transition->GetPrivateData<FD3D12EnhancedBarriersTransitionData>())
|
|
FD3D12EnhancedBarriersTransitionData;
|
|
|
|
Data->SrcPipelines = CreateInfo.SrcPipelines;
|
|
Data->DstPipelines = CreateInfo.DstPipelines;
|
|
Data->CreateFlags = CreateInfo.Flags;
|
|
|
|
const bool bCreateFence =
|
|
(CreateInfo.SrcPipelines != CreateInfo.DstPipelines)
|
|
&& (!EnumHasAnyFlags(Data->CreateFlags, ERHITransitionCreateFlags::NoFence));
|
|
|
|
if (bCreateFence)
|
|
{
|
|
// Create one sync point per device, per source pipe
|
|
for (uint32 Index : FRHIGPUMask::All())
|
|
{
|
|
TRHIPipelineArray<FD3D12SyncPointRef>& DeviceSyncPoints = Data->SyncPoints.Emplace_GetRef();
|
|
for (ERHIPipeline Pipeline : MakeFlagsRange(CreateInfo.SrcPipelines))
|
|
{
|
|
DeviceSyncPoints[Pipeline] = FD3D12SyncPoint::Create(ED3D12SyncPointType::GPUOnly, TEXT("Transition"));
|
|
}
|
|
}
|
|
}
|
|
|
|
Data->TransitionInfos = CreateInfo.TransitionInfos;
|
|
}
|
|
|
|
void FD3D12EnhancedBarriersForAdapterImpl::ReleaseTransition(
|
|
FRHITransition* Transition)
|
|
{
|
|
// Destruct the transition data
|
|
Transition->GetPrivateData<FD3D12EnhancedBarriersTransitionData>()->~FD3D12EnhancedBarriersTransitionData();
|
|
}
|
|
|
|
HRESULT FD3D12EnhancedBarriersForAdapterImpl::CreateCommittedResource(
|
|
FD3D12Adapter& Adapter,
|
|
const D3D12_HEAP_PROPERTIES& InHeapProps,
|
|
D3D12_HEAP_FLAGS InHeapFlags,
|
|
const FD3D12ResourceDesc& InDesc,
|
|
ED3D12Access InInitialD3D12Access,
|
|
const D3D12_CLEAR_VALUE* InClearValue,
|
|
TRefCountPtr<ID3D12Resource>& OutResource)
|
|
{
|
|
// @TODO - Ask Intel if they want to provide Layout based extensions like we use for
|
|
// for the legacy barriers
|
|
|
|
// Convert the desc to the version required by CreateCommittedResource3
|
|
const CD3DX12_RESOURCE_DESC1 LocalDesc1(InDesc);
|
|
DXGI_FORMAT* CastableFormatsPtr = nullptr;
|
|
UINT32 NumCastableFormats = 0;
|
|
#if D3D12RHI_SUPPORTS_UNCOMPRESSED_UAV
|
|
const TArray<DXGI_FORMAT, TInlineAllocator<4>> CastableFormats = InDesc.GetCastableFormats();
|
|
CastableFormatsPtr =
|
|
CastableFormats.IsEmpty() ? nullptr : const_cast<DXGI_FORMAT*>(CastableFormats.GetData());
|
|
NumCastableFormats = CastableFormats.Num();
|
|
#endif
|
|
|
|
ID3D12ProtectedResourceSession* ProtectedSession = nullptr;
|
|
const D3D12_BARRIER_LAYOUT InitialLayout = GetInitialLayout(InInitialD3D12Access, InDesc);
|
|
|
|
return Adapter.GetD3DDevice10()->CreateCommittedResource3(
|
|
&InHeapProps,
|
|
InHeapFlags,
|
|
&LocalDesc1,
|
|
InitialLayout,
|
|
InClearValue,
|
|
ProtectedSession,
|
|
NumCastableFormats,
|
|
CastableFormatsPtr,
|
|
IID_PPV_ARGS(OutResource.GetInitReference()));
|
|
}
|
|
|
|
HRESULT FD3D12EnhancedBarriersForAdapterImpl::CreateReservedResource(
|
|
FD3D12Adapter& Adapter,
|
|
const FD3D12ResourceDesc& InDesc,
|
|
ED3D12Access InInitialD3D12Access,
|
|
const D3D12_CLEAR_VALUE* InClearValue,
|
|
TRefCountPtr<ID3D12Resource>& OutResource)
|
|
{
|
|
DXGI_FORMAT* CastableFormatsPtr = nullptr;
|
|
UINT32 NumCastableFormats = 0;
|
|
#if D3D12RHI_SUPPORTS_UNCOMPRESSED_UAV
|
|
const TArray<DXGI_FORMAT, TInlineAllocator<4>> CastableFormats = InDesc.GetCastableFormats();
|
|
CastableFormatsPtr =
|
|
CastableFormats.IsEmpty() ? nullptr : const_cast<DXGI_FORMAT*>(CastableFormats.GetData());
|
|
NumCastableFormats = CastableFormats.Num();
|
|
#endif
|
|
|
|
ID3D12ProtectedResourceSession* ProtectedSession = nullptr;
|
|
const D3D12_BARRIER_LAYOUT InitialLayout = GetInitialLayout(InInitialD3D12Access, InDesc);
|
|
|
|
return Adapter.GetD3DDevice10()->CreateReservedResource2(
|
|
&InDesc,
|
|
InitialLayout,
|
|
InClearValue,
|
|
ProtectedSession,
|
|
NumCastableFormats,
|
|
CastableFormatsPtr,
|
|
IID_PPV_ARGS(OutResource.GetInitReference()));
|
|
}
|
|
|
|
HRESULT FD3D12EnhancedBarriersForAdapterImpl::CreatePlacedResource(
|
|
FD3D12Adapter& Adapter,
|
|
ID3D12Heap* Heap,
|
|
uint64 InHeapOffset,
|
|
const FD3D12ResourceDesc& InDesc,
|
|
ED3D12Access InInitialD3D12Access,
|
|
const D3D12_CLEAR_VALUE* InClearValue,
|
|
TRefCountPtr<ID3D12Resource>& OutResource)
|
|
{
|
|
// @TODO - Ask Intel if they want to provide Layout based extensions like we use for
|
|
// for the legacy barriers
|
|
|
|
// Convert the desc to the version required by CreatePlacedResource2
|
|
CD3DX12_RESOURCE_DESC1 LocalDesc(InDesc);
|
|
DXGI_FORMAT* CastableFormatsPtr = nullptr;
|
|
UINT32 NumCastableFormats = 0;
|
|
#if D3D12RHI_SUPPORTS_UNCOMPRESSED_UAV
|
|
const TArray<DXGI_FORMAT, TInlineAllocator<4>> CastableFormats = InDesc.GetCastableFormats();
|
|
CastableFormatsPtr =
|
|
CastableFormats.IsEmpty() ? nullptr : const_cast<DXGI_FORMAT*>(CastableFormats.GetData());
|
|
NumCastableFormats = CastableFormats.Num();
|
|
#endif
|
|
|
|
const D3D12_BARRIER_LAYOUT InitialLayout = GetInitialLayout(InInitialD3D12Access, InDesc);
|
|
|
|
return Adapter.GetD3DDevice10()->CreatePlacedResource2(
|
|
Heap,
|
|
InHeapOffset,
|
|
&LocalDesc,
|
|
InitialLayout,
|
|
InClearValue,
|
|
NumCastableFormats,
|
|
CastableFormatsPtr,
|
|
IID_PPV_ARGS(OutResource.GetInitReference()));
|
|
}
|
|
|
|
FD3D12EnhancedBarriersForAdapter::~FD3D12EnhancedBarriersForAdapter()
|
|
{}
|
|
|
|
void FD3D12EnhancedBarriersForAdapter::ConfigureDevice(
|
|
ID3D12Device* Device,
|
|
bool InWithD3DDebug) const
|
|
|
|
{
|
|
return
|
|
FD3D12EnhancedBarriersForAdapterImpl::ConfigureDevice(
|
|
Device,
|
|
InWithD3DDebug);
|
|
}
|
|
|
|
uint64 FD3D12EnhancedBarriersForAdapter::GetTransitionDataSizeBytes() const
|
|
{
|
|
return FD3D12EnhancedBarriersForAdapterImpl::GetTransitionDataSizeBytes();
|
|
}
|
|
|
|
uint64 FD3D12EnhancedBarriersForAdapter::GetTransitionDataAlignmentBytes() const
|
|
{
|
|
return FD3D12EnhancedBarriersForAdapterImpl::GetTransitionDataAlignmentBytes();
|
|
}
|
|
|
|
void FD3D12EnhancedBarriersForAdapter::CreateTransition(
|
|
FRHITransition* Transition,
|
|
const FRHITransitionCreateInfo& CreateInfo) const
|
|
{
|
|
return
|
|
FD3D12EnhancedBarriersForAdapterImpl::CreateTransition(
|
|
Transition,
|
|
CreateInfo);
|
|
}
|
|
|
|
void FD3D12EnhancedBarriersForAdapter::ReleaseTransition(
|
|
FRHITransition* Transition) const
|
|
{
|
|
return
|
|
FD3D12EnhancedBarriersForAdapterImpl::ReleaseTransition(
|
|
Transition);
|
|
}
|
|
|
|
HRESULT FD3D12EnhancedBarriersForAdapter::CreateCommittedResource(
|
|
FD3D12Adapter& Adapter,
|
|
const D3D12_HEAP_PROPERTIES& InHeapProps,
|
|
D3D12_HEAP_FLAGS InHeapFlags,
|
|
const FD3D12ResourceDesc& InDesc,
|
|
ED3D12Access InInitialD3D12Access,
|
|
const D3D12_CLEAR_VALUE* InClearValue,
|
|
TRefCountPtr<ID3D12Resource>& OutResource) const
|
|
{
|
|
return
|
|
FD3D12EnhancedBarriersForAdapterImpl::CreateCommittedResource(
|
|
Adapter,
|
|
InHeapProps,
|
|
InHeapFlags,
|
|
InDesc,
|
|
InInitialD3D12Access,
|
|
InClearValue,
|
|
OutResource);
|
|
}
|
|
|
|
HRESULT FD3D12EnhancedBarriersForAdapter::CreateReservedResource(
|
|
FD3D12Adapter& Adapter,
|
|
const FD3D12ResourceDesc& InDesc,
|
|
ED3D12Access InInitialD3D12Access,
|
|
const D3D12_CLEAR_VALUE* InClearValue,
|
|
TRefCountPtr<ID3D12Resource>& OutResource) const
|
|
{
|
|
return
|
|
FD3D12EnhancedBarriersForAdapterImpl::CreateReservedResource(
|
|
Adapter,
|
|
InDesc,
|
|
InInitialD3D12Access,
|
|
InClearValue,
|
|
OutResource);
|
|
}
|
|
|
|
HRESULT FD3D12EnhancedBarriersForAdapter::CreatePlacedResource(
|
|
FD3D12Adapter& Adapter,
|
|
ID3D12Heap* Heap,
|
|
uint64 InHeapOffset,
|
|
const FD3D12ResourceDesc& InDesc,
|
|
ED3D12Access InInitialD3D12Access,
|
|
const D3D12_CLEAR_VALUE* InClearValue,
|
|
TRefCountPtr<ID3D12Resource>& OutResource) const
|
|
{
|
|
return
|
|
FD3D12EnhancedBarriersForAdapterImpl::CreatePlacedResource(
|
|
Adapter,
|
|
Heap,
|
|
InHeapOffset,
|
|
InDesc,
|
|
InInitialD3D12Access,
|
|
InClearValue,
|
|
OutResource);
|
|
}
|
|
|
|
const TCHAR* FD3D12EnhancedBarriersForAdapter::GetImplementationName() const
|
|
{
|
|
return TEXT("D3D12EnhancedBarriers");
|
|
}
|
|
|
|
|
|
static TTuple<const FD3D12Resource*, const FD3D12Buffer*, const FD3D12Texture*>
|
|
GetResourceBufferAndTexture(FD3D12CommandContext& Context, const FRHITransitionInfo& Info)
|
|
{
|
|
switch (Info.Type)
|
|
{
|
|
case FRHITransitionInfo::EType::UAV:
|
|
{
|
|
const FD3D12UnorderedAccessView* UAV = Context.RetrieveObject<FD3D12UnorderedAccessView_RHI>(Info.UAV);
|
|
check(UAV);
|
|
return {
|
|
UAV ? UAV->GetResource() : nullptr,
|
|
Info.UAV->IsBuffer() ? Context.RetrieveObject<FD3D12Buffer>(Info.UAV->GetBuffer()) : nullptr,
|
|
Info.UAV->IsTexture() ? Context.RetrieveTexture(Info.UAV->GetTexture()) : nullptr };
|
|
}
|
|
case FRHITransitionInfo::EType::Buffer:
|
|
{
|
|
// Resource may be null if this is a multi-GPU resource not present on the current GPU
|
|
const FD3D12Buffer* Buffer = Context.RetrieveObject<FD3D12Buffer>(Info.Buffer);
|
|
return { Buffer ? Buffer->GetResource() : nullptr, Buffer, nullptr };
|
|
}
|
|
case FRHITransitionInfo::EType::Texture:
|
|
{
|
|
// Resource may be null if this is a multi-GPU resource not present on the current GPU
|
|
const FD3D12Texture* Texture = Context.RetrieveTexture(Info.Texture);
|
|
return { Texture ? Texture->GetResource() : nullptr, nullptr, Texture };
|
|
}
|
|
case FRHITransitionInfo::EType::BVH:
|
|
// Nothing special required for BVH transitions - handled inside d3d12 raytracing directly via UAV barriers and don't need explicit state changes
|
|
return { nullptr, nullptr, nullptr };
|
|
default:
|
|
checkNoEntry();
|
|
return { nullptr, nullptr, nullptr };
|
|
}
|
|
}
|
|
|
|
class VARangeCollection
|
|
{
|
|
public:
|
|
using VARange = TInterval<D3D12_GPU_VIRTUAL_ADDRESS>;
|
|
|
|
void AddRange(
|
|
const VARangeCollection::VARange& InVARange)
|
|
{
|
|
check(InVARange.IsValid());
|
|
VARanges.Add(InVARange);
|
|
}
|
|
|
|
bool OverlapsAny(
|
|
const VARangeCollection::VARange& InVARange) const
|
|
{
|
|
check(InVARange.IsValid());
|
|
return VARanges.ContainsByPredicate(
|
|
[this, &InVARange] (const VARange& InExistingVARange) {
|
|
return Overlaps(InVARange, InExistingVARange);
|
|
});
|
|
}
|
|
|
|
private:
|
|
static bool Overlaps(
|
|
const VARangeCollection::VARange& A,
|
|
const VARangeCollection::VARange& B)
|
|
{
|
|
return (A.Min <= B.Max) && (B.Min <= A.Max);
|
|
}
|
|
|
|
TArray<VARange> VARanges;
|
|
};
|
|
|
|
static VARangeCollection BuildVARangeTable(
|
|
FD3D12CommandContext& Context,
|
|
TArrayView<const FRHITransition*> InTransitions)
|
|
{
|
|
VARangeCollection VARanges;
|
|
for (const FRHITransition* Transition : InTransitions)
|
|
{
|
|
const FD3D12EnhancedBarriersTransitionData* Data =
|
|
Transition->GetPrivateData<FD3D12EnhancedBarriersTransitionData>();
|
|
for (const FRHITransitionInfo& Info : Data->TransitionInfos)
|
|
{
|
|
if ((Info.AccessBefore == ERHIAccess::Discard)
|
|
&& (Info.AccessAfter != ERHIAccess::Discard))
|
|
{
|
|
const auto [Resource, Buffer, Texture] = GetResourceBufferAndTexture(Context, Info);
|
|
if (Texture)
|
|
{
|
|
VARanges.AddRange({
|
|
Texture->ResourceLocation.GetGPUVirtualAddress(),
|
|
Texture->ResourceLocation.GetGPUVirtualAddress() + Texture->ResourceLocation.GetSize() });
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return VARanges;
|
|
}
|
|
|
|
FD3D12EnhancedBarriersForContext::FD3D12EnhancedBarriersForContext()
|
|
: ID3D12BarriersForContext()
|
|
, Batcher(MakeUnique<FD3D12EnhancedBarriersBatcher>())
|
|
{}
|
|
|
|
FD3D12EnhancedBarriersForContext::~FD3D12EnhancedBarriersForContext()
|
|
{}
|
|
|
|
//
|
|
// The RHI takes an abstracted view of transitions and so will ask for transitions across
|
|
// pipes that can't be expressed via a single barrier. In those cases an intermediate
|
|
// state is necessary. The layout used for that intermediate state must be compatible
|
|
// with both the source and destination pipes as well as any sync and access bits that
|
|
// will compose the intermediate state. This function calculates which layout to use.
|
|
//
|
|
static FD3D12BarrierValues ChooseIntermediateBarrierValues(
|
|
D3D12_BARRIER_SYNC InSyncBefore,
|
|
D3D12_BARRIER_SYNC InSyncAfter,
|
|
D3D12_BARRIER_ACCESS InAccessBefore,
|
|
D3D12_BARRIER_ACCESS InAccessAfter,
|
|
D3D12_BARRIER_LAYOUT InLayoutBefore,
|
|
D3D12_BARRIER_LAYOUT InLayoutAfter,
|
|
ERHIPipeline InPipeBefore,
|
|
ERHIPipeline InPipeAfter)
|
|
{
|
|
// @TODO - Should we use other information to decide on which side of the
|
|
// transition to perform a layout change if we have multiple options?
|
|
|
|
check(EnumHasOneFlag(InPipeBefore));
|
|
check(!AccessIsCompatibleWithQueue(InAccessAfter, InPipeBefore)
|
|
|| !LayoutIsCompatibleWithQueue(InLayoutAfter, InPipeBefore));
|
|
|
|
if (LayoutIsCompatibleWithQueue(InLayoutBefore, InPipeAfter))
|
|
{
|
|
check(!AccessIsCompatibleWithQueue(InAccessBefore, InPipeAfter)
|
|
|| !LayoutIsCompatibleWithQueue(InLayoutAfter, InPipeBefore));
|
|
|
|
// In this case we only need to shed some access *or* we need to keep the
|
|
// current layout during the intermediate barrier for the transfer to the
|
|
// new pipe and perform the layout change on the destination pipe
|
|
return {
|
|
.Sync = FilterToQueueCompatibleSync(InSyncBefore, InPipeAfter),
|
|
.Access = FilterToQueueCompatibleAccess(InAccessBefore, InPipeAfter),
|
|
.Layout = InLayoutBefore,
|
|
};
|
|
}
|
|
|
|
// Layout must also be a problem so try and resolve that
|
|
check(!LayoutIsCompatibleWithQueue(InLayoutAfter, InPipeBefore)
|
|
|| !LayoutIsCompatibleWithQueue(InLayoutBefore, InPipeAfter));
|
|
|
|
const D3D12_BARRIER_LAYOUT QueueAgnosticLayoutAfter =
|
|
GetQueueAgnosticVersionOfLayout(InLayoutAfter);
|
|
if (LayoutIsCompatibleWithQueue(QueueAgnosticLayoutAfter, InPipeBefore))
|
|
{
|
|
check(QueueAgnosticLayoutAfter != InLayoutAfter);
|
|
return {
|
|
.Sync = FilterToQueueCompatibleSync(InSyncAfter, ERHIPipeline::All),
|
|
.Access = FilterToQueueCompatibleAccess(InAccessAfter, ERHIPipeline::All),
|
|
.Layout = QueueAgnosticLayoutAfter,
|
|
};
|
|
}
|
|
|
|
const D3D12_BARRIER_LAYOUT QueueAgnosticLayoutBefore =
|
|
GetQueueAgnosticVersionOfLayout(InLayoutBefore);
|
|
|
|
if (LayoutIsCompatibleWithQueue(QueueAgnosticLayoutBefore, InPipeAfter))
|
|
{
|
|
check (QueueAgnosticLayoutBefore != InLayoutBefore);
|
|
|
|
// This should always be the case but to document the assumption...
|
|
check(LayoutIsCompatibleWithQueue(QueueAgnosticLayoutBefore, InPipeBefore));
|
|
|
|
// In this case we can change the layout to a queue independent one
|
|
// and shed the sync+access incompatible with the new layout at the same time
|
|
return {
|
|
.Sync = FilterToQueueCompatibleSync(InSyncBefore, ERHIPipeline::All),
|
|
.Access = FilterToQueueCompatibleAccess(InAccessBefore, ERHIPipeline::All),
|
|
.Layout = QueueAgnosticLayoutBefore,
|
|
};
|
|
}
|
|
|
|
// @TODO - Do we ever get here? This will work but it's not great...
|
|
checkSlow(false);
|
|
return {
|
|
.Sync = D3D12_BARRIER_SYNC_ALL,
|
|
.Access = D3D12_BARRIER_ACCESS_COMMON,
|
|
.Layout = D3D12_BARRIER_LAYOUT_COMMON,
|
|
};
|
|
}
|
|
|
|
static bool ProcessTransitionDuringBegin(const FD3D12EnhancedBarriersTransitionData* Data)
|
|
{
|
|
// If we're entering a new pipe then we might need to do work in begin
|
|
return !EnumOnlyContainsFlags(Data->DstPipelines, Data->SrcPipelines);
|
|
}
|
|
|
|
static bool ProcessTransitionInfoDuringBegin(
|
|
const UE::RHICore::FResourceState& InResourceState,
|
|
const FD3D12Texture* InTexture)
|
|
{
|
|
if (!EnumOnlyContainsFlags(InResourceState.DstPipelines, InResourceState.SrcPipelines))
|
|
{
|
|
if (InResourceState.SrcPipelines != ERHIPipeline::All)
|
|
{
|
|
// Are we going to a pipe that can't deal with one or more of our sync bits?
|
|
// If that's the case we'll need to perform the incompatible sync before
|
|
// leaving the source pipe
|
|
const D3D12_BARRIER_SYNC CurrentSync = GetEBSync(InResourceState.AccessBefore, InResourceState.SrcPipelines);
|
|
if (!SyncIsCompatibleWithQueue(CurrentSync, InResourceState.DstPipelines))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// Are we going to a pipe that can't deal with one or more of our access bits?
|
|
// If that's the case we'll need to perform the incompatible access changes before
|
|
// leaving the source pipe
|
|
const D3D12_BARRIER_ACCESS CurrentAccess = GetEBAccess(InResourceState.AccessBefore, InResourceState.SrcPipelines);
|
|
if (!AccessIsCompatibleWithQueue(CurrentAccess, InResourceState.DstPipelines))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// Check if we have to make any layout changes for the cross-pipe barrier
|
|
// Only textures have layouts
|
|
if (InTexture)
|
|
{
|
|
const D3D12_BARRIER_LAYOUT CurrentLayout =
|
|
GetEBLayout(
|
|
InResourceState.AccessBefore,
|
|
InResourceState.SrcPipelines,
|
|
InTexture);
|
|
|
|
return !LayoutIsCompatibleWithQueue(CurrentLayout, InResourceState.DstPipelines);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// If we're already on all pipes then all bits should be compatible with the destination
|
|
// If not then something has changed or it's a bug somewhere else
|
|
check(SyncIsCompatibleWithQueue(GetEBSync(InResourceState.AccessBefore, InResourceState.DstPipelines), InResourceState.DstPipelines));
|
|
check(AccessIsCompatibleWithQueue(GetEBAccess(InResourceState.AccessBefore, InResourceState.DstPipelines), InResourceState.DstPipelines));
|
|
if (InTexture)
|
|
{
|
|
check(
|
|
LayoutIsCompatibleWithQueue(
|
|
GetEBLayout(
|
|
InResourceState.AccessBefore,
|
|
InResourceState.SrcPipelines,
|
|
InTexture),
|
|
InResourceState.DstPipelines));
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void FD3D12EnhancedBarriersForContext::BeginTransitions(
|
|
FD3D12CommandContext& Context,
|
|
TArrayView<const FRHITransition*> Transitions)
|
|
{
|
|
// Build barriers
|
|
AddBarriersForTransitions(Context, Transitions, EBarrierPhase::Begin);
|
|
|
|
// Signal fences
|
|
const ERHIPipeline CurrentPipeline = Context.GetPipeline();
|
|
for (const FRHITransition* Transition : Transitions)
|
|
{
|
|
const FD3D12EnhancedBarriersTransitionData* Data =
|
|
Transition->GetPrivateData<FD3D12EnhancedBarriersTransitionData>();
|
|
if (!Data->SyncPoints.IsEmpty())
|
|
{
|
|
const TRHIPipelineArray<FD3D12SyncPointRef>& DeviceSyncPoints = Data->SyncPoints[Context.GetGPUIndex()];
|
|
if (DeviceSyncPoints[CurrentPipeline])
|
|
{
|
|
Context.SignalSyncPoint(DeviceSyncPoints[CurrentPipeline]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FD3D12EnhancedBarriersForContext::EndTransitions(
|
|
FD3D12CommandContext& Context,
|
|
TArrayView<const FRHITransition*> Transitions)
|
|
{
|
|
const ERHIPipeline CurrentPipeline = Context.GetPipeline();
|
|
|
|
// Wait for fences
|
|
for (const FRHITransition* Transition : Transitions)
|
|
{
|
|
const FD3D12EnhancedBarriersTransitionData* Data =
|
|
Transition->GetPrivateData<FD3D12EnhancedBarriersTransitionData>();
|
|
if (!Data->SyncPoints.IsEmpty())
|
|
{
|
|
const TRHIPipelineArray<FD3D12SyncPointRef>& DeviceSyncPoints = Data->SyncPoints[Context.GetGPUIndex()];
|
|
for (ERHIPipeline SrcPipeline : MakeFlagsRange(Data->SrcPipelines))
|
|
{
|
|
if (SrcPipeline != CurrentPipeline && DeviceSyncPoints[SrcPipeline])
|
|
{
|
|
Context.WaitSyncPoint(DeviceSyncPoints[SrcPipeline]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update reserved resource memory mapping
|
|
for (const FRHITransition* Transition : Transitions)
|
|
{
|
|
const FD3D12EnhancedBarriersTransitionData* Data =
|
|
Transition->GetPrivateData<FD3D12EnhancedBarriersTransitionData>();
|
|
|
|
HandleReservedResourceCommits(Context, Data);
|
|
}
|
|
|
|
// Build barriers
|
|
AddBarriersForTransitions(Context, Transitions, EBarrierPhase::End);
|
|
}
|
|
|
|
void FD3D12EnhancedBarriersForContext::AddGlobalBarrier(
|
|
FD3D12ContextCommon& Context,
|
|
ED3D12Access D3D12AccessBefore,
|
|
ED3D12Access D3D12AccessAfter)
|
|
{
|
|
const ERHIPipeline Pipe = Context.GetRHIPipeline();
|
|
|
|
Batcher->AddGlobalBarrier(
|
|
Context,
|
|
GetEBSync(D3D12AccessBefore, Pipe),
|
|
GetEBSync(D3D12AccessAfter, Pipe),
|
|
GetEBAccess(D3D12AccessBefore, Pipe),
|
|
GetEBAccess(D3D12AccessAfter, Pipe));
|
|
}
|
|
|
|
void FD3D12EnhancedBarriersForContext::AddBarrier(
|
|
FD3D12ContextCommon& Context,
|
|
const FD3D12Resource* pResource,
|
|
ED3D12Access D3D12AccessBefore,
|
|
ED3D12Access D3D12AccessAfter,
|
|
uint32 Subresource)
|
|
{
|
|
check(pResource);
|
|
|
|
const ERHIPipeline Pipe = Context.GetRHIPipeline();
|
|
|
|
const D3D12_BARRIER_SYNC SyncBefore = GetEBSync(D3D12AccessBefore, Pipe);
|
|
const D3D12_BARRIER_SYNC SyncAfter = GetEBSync(D3D12AccessAfter, Pipe);
|
|
const D3D12_BARRIER_ACCESS AccessBefore = GetEBAccess(D3D12AccessBefore, Pipe);
|
|
const D3D12_BARRIER_ACCESS AccessAfter = GetEBAccess(D3D12AccessAfter, Pipe);
|
|
|
|
if (pResource->GetDesc().Dimension == D3D12_RESOURCE_DIMENSION_BUFFER)
|
|
{
|
|
Batcher->AddBufferBarrier(
|
|
Context,
|
|
pResource,
|
|
SyncBefore,
|
|
SyncAfter,
|
|
AccessBefore,
|
|
AccessAfter);
|
|
}
|
|
else
|
|
{
|
|
D3D12_BARRIER_LAYOUT LayoutBefore = {};
|
|
D3D12_BARRIER_LAYOUT LayoutAfter = {};
|
|
|
|
if (!pResource->RequiresResourceStateTracking()
|
|
&& (pResource->GetInitialAccess() == ED3D12Access::Common))
|
|
{
|
|
// Don't ever move an untracked resource out of a common layout if that's its initial access
|
|
// This fixes problems with upload textures that need to be left in the common layout for the copy queue
|
|
// See FD3D12DynamicRHI::RHIAsyncCreateTexture2D()
|
|
LayoutBefore = D3D12_BARRIER_LAYOUT_COMMON;
|
|
LayoutAfter = D3D12_BARRIER_LAYOUT_COMMON;
|
|
}
|
|
else
|
|
{
|
|
LayoutBefore = GetEBLayout(D3D12AccessBefore, Pipe, nullptr);
|
|
LayoutAfter = GetEBLayout(D3D12AccessAfter, Pipe, nullptr);
|
|
}
|
|
|
|
Batcher->AddTextureBarrier(
|
|
Context,
|
|
pResource,
|
|
SyncBefore,
|
|
SyncAfter,
|
|
AccessBefore,
|
|
AccessAfter,
|
|
LayoutBefore,
|
|
LayoutAfter,
|
|
Subresource,
|
|
false);
|
|
}
|
|
}
|
|
|
|
void FD3D12EnhancedBarriersForContext::FlushIntoCommandList(
|
|
FD3D12CommandList& CommandList,
|
|
FD3D12QueryAllocator& TimestampAllocator)
|
|
{
|
|
Batcher->FlushIntoCommandList(CommandList, TimestampAllocator);
|
|
}
|
|
|
|
int32 FD3D12EnhancedBarriersForContext::GetNumPendingBarriers() const
|
|
{
|
|
return Batcher->Num();
|
|
}
|
|
|
|
static bool NeedToProcessTransitionEarlyToAvoidVAConflicts(
|
|
ERHIAccess InRHIAccessBefore,
|
|
ERHIAccess InRHIAccessAfter,
|
|
const FD3D12Resource* InResource,
|
|
const FD3D12Buffer* InBuffer,
|
|
const FD3D12Texture* InTexture,
|
|
const VARangeCollection& InVARangesToBeInitialized)
|
|
{
|
|
// It's against the enhanced barriers rules to discard a resource
|
|
// (in the sense that it's put into the NO_ACCESS state) and then
|
|
// initialize an aliasing resource (using the DISCARD flag) in the
|
|
// same barrier batch. So here we check to see if we're both discarding
|
|
// and initializing overlapping VA ranges and if we are, we need to
|
|
// submit the discard to the driver separate from the initialization.
|
|
|
|
if ((InRHIAccessBefore != ERHIAccess::Discard)
|
|
&& (InRHIAccessAfter == ERHIAccess::Discard))
|
|
{
|
|
check(InBuffer || InTexture);
|
|
|
|
const D3D12_GPU_VIRTUAL_ADDRESS ResourceVARangeStart =
|
|
InTexture ?
|
|
InTexture->ResourceLocation.GetGPUVirtualAddress() :
|
|
InBuffer->ResourceLocation.GetGPUVirtualAddress();
|
|
|
|
const D3D12_GPU_VIRTUAL_ADDRESS ResourceVARangeEnd =
|
|
ResourceVARangeStart
|
|
+ (InTexture ?
|
|
InTexture->ResourceLocation.GetSize() :
|
|
InBuffer->ResourceLocation.GetSize());
|
|
|
|
const TInterval<D3D12_GPU_VIRTUAL_ADDRESS> ResourceVARange =
|
|
{ ResourceVARangeStart, ResourceVARangeEnd };
|
|
|
|
return InVARangesToBeInitialized.OverlapsAny(ResourceVARange);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void FD3D12EnhancedBarriersForContext::AddBarriersForTransitions(
|
|
FD3D12CommandContext& Context,
|
|
TArrayView<const FRHITransition*> InTransitions,
|
|
EBarrierPhase InBarrierPhase)
|
|
{
|
|
// @TODO - Experiment with this ordering. Is it better to do the "discard" or "acquire" portion
|
|
// of the operation on its own? This will totally depend on the engine behavior.
|
|
|
|
const bool bIsBegin = (InBarrierPhase == EBarrierPhase::Begin);
|
|
|
|
// Build a list of VA ranges that may be initialized by the barriers
|
|
const VARangeCollection VARanges = BuildVARangeTable(Context, InTransitions);
|
|
|
|
// Handle early discards first
|
|
bool bHadEarlyDiscards = false;
|
|
for (const FRHITransition* Transition : InTransitions)
|
|
{
|
|
const FD3D12EnhancedBarriersTransitionData* Data =
|
|
Transition->GetPrivateData<FD3D12EnhancedBarriersTransitionData>();
|
|
if (!bIsBegin || ProcessTransitionDuringBegin(Data))
|
|
{
|
|
bHadEarlyDiscards |=
|
|
AddBarriersForTransitionData(
|
|
Context,
|
|
Data,
|
|
VARanges,
|
|
EProcessEarlyTransitions::Yes,
|
|
InBarrierPhase);
|
|
}
|
|
}
|
|
|
|
if (bHadEarlyDiscards)
|
|
{
|
|
FlushIntoCommandList(Context.GetCommandList(), Context.GetTimestampQueries());
|
|
}
|
|
|
|
// Now everything else
|
|
for (const FRHITransition* Transition : InTransitions)
|
|
{
|
|
const FD3D12EnhancedBarriersTransitionData* Data = Transition->GetPrivateData<FD3D12EnhancedBarriersTransitionData>();
|
|
if (!bIsBegin || ProcessTransitionDuringBegin(Data))
|
|
{
|
|
AddBarriersForTransitionData(Context, Data, VARanges, EProcessEarlyTransitions::No, InBarrierPhase);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool FD3D12EnhancedBarriersForContext::AddBarriersForTransitionData(
|
|
FD3D12CommandContext& Context,
|
|
const FD3D12EnhancedBarriersTransitionData* InTransitionData,
|
|
const VARangeCollection& InVARangesToBeInitialized,
|
|
EProcessEarlyTransitions InProcessEarlyTransitions,
|
|
EBarrierPhase InBarrierPhase)
|
|
{
|
|
bool bAddedBarriers = false;
|
|
const bool bIsBegin = (InBarrierPhase == EBarrierPhase::Begin);
|
|
const bool bProcessEarlyTransitions = (InProcessEarlyTransitions == EProcessEarlyTransitions::Yes);
|
|
|
|
for (const FRHITransitionInfo& Info : InTransitionData->TransitionInfos)
|
|
{
|
|
if (Info.Resource)
|
|
{
|
|
const auto [Resource, Buffer, Texture] = GetResourceBufferAndTexture(Context, Info);
|
|
|
|
// @TODO - Why do we have to filter these out? Should we ever see this???
|
|
//check(Resource);
|
|
//check(Resource->RequiresResourceStateTracking());
|
|
if (Resource == nullptr || !Resource->RequiresResourceStateTracking())
|
|
{
|
|
check(Resource || Info.Type == FRHITransitionInfo::EType::BVH);
|
|
continue;
|
|
}
|
|
|
|
const UE::RHICore::FResourceState ResourceState(
|
|
Context,
|
|
InTransitionData->SrcPipelines,
|
|
InTransitionData->DstPipelines,
|
|
Info);
|
|
|
|
const bool bProcessDuringBegin =
|
|
ProcessTransitionInfoDuringBegin(
|
|
ResourceState,
|
|
Texture);
|
|
|
|
if (bIsBegin && !bProcessDuringBegin)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (NeedToProcessTransitionEarlyToAvoidVAConflicts(
|
|
ResourceState.AccessBefore,
|
|
ResourceState.AccessAfter,
|
|
Resource,
|
|
Buffer,
|
|
Texture,
|
|
InVARangesToBeInitialized)
|
|
!= bProcessEarlyTransitions)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
const bool bLeavingThisPipe =
|
|
!EnumHasAnyFlags(InTransitionData->DstPipelines, GetPipeline());
|
|
|
|
const bool bEnteringThisPipe =
|
|
!EnumHasAnyFlags(InTransitionData->SrcPipelines, GetPipeline());
|
|
*/
|
|
|
|
// @TODO - Ask MS to allow SYNC_NONE for cases like this?
|
|
// We know that the sync is already handled by fences
|
|
D3D12_BARRIER_SYNC SyncBefore =
|
|
/*
|
|
bEnteringThisPipe ?
|
|
D3D12_BARRIER_SYNC_NONE :
|
|
*/
|
|
GetEBSync(ResourceState.AccessBefore, ResourceState.SrcPipelines);
|
|
D3D12_BARRIER_SYNC SyncAfter =
|
|
/*
|
|
bLeavingThisPipe ?
|
|
D3D12_BARRIER_SYNC_NONE :
|
|
*/
|
|
GetEBSync(ResourceState.AccessAfter, ResourceState.DstPipelines);
|
|
|
|
D3D12_BARRIER_ACCESS AccessBefore =
|
|
GetEBAccess(ResourceState.AccessBefore, ResourceState.SrcPipelines);
|
|
D3D12_BARRIER_ACCESS AccessAfter =
|
|
GetEBAccess(ResourceState.AccessAfter, ResourceState.DstPipelines);
|
|
|
|
D3D12_BARRIER_LAYOUT LayoutBefore =
|
|
Texture ?
|
|
GetEBLayout(
|
|
ResourceState.AccessBefore,
|
|
ResourceState.SrcPipelines,
|
|
Texture) :
|
|
D3D12_BARRIER_LAYOUT_UNDEFINED;
|
|
|
|
D3D12_BARRIER_LAYOUT LayoutAfter =
|
|
Texture ?
|
|
GetEBLayout(
|
|
ResourceState.AccessAfter,
|
|
ResourceState.DstPipelines,
|
|
Texture) :
|
|
D3D12_BARRIER_LAYOUT_UNDEFINED;
|
|
|
|
// Need an intermediate barrier if we can't complete
|
|
// the entire barrier on the source pipe during begin
|
|
// Note that sync is taken care of by the following fence
|
|
// to move the resource to the new pipe(s) so we don't have
|
|
// to test for sync compat on the after side
|
|
const bool bCreateIntermediateBarrier =
|
|
bProcessDuringBegin
|
|
&& (!AccessIsCompatibleWithQueue(
|
|
AccessAfter,
|
|
ResourceState.SrcPipelines)
|
|
|| !LayoutIsCompatibleWithQueue(
|
|
LayoutAfter,
|
|
ResourceState.SrcPipelines));
|
|
|
|
if (!bIsBegin && bProcessDuringBegin && !bCreateIntermediateBarrier)
|
|
{
|
|
// In this case the barrier was completely handled in begin
|
|
continue;
|
|
}
|
|
|
|
if (bCreateIntermediateBarrier)
|
|
{
|
|
// If we're here, then we need to do some kind of intermediate
|
|
// transition before the resource gets fenced over to the other pipe
|
|
|
|
const FD3D12BarrierValues IntermediateBarrierValues =
|
|
ChooseIntermediateBarrierValues(
|
|
SyncBefore,
|
|
SyncAfter,
|
|
AccessBefore,
|
|
AccessAfter,
|
|
LayoutBefore,
|
|
LayoutAfter,
|
|
ResourceState.SrcPipelines,
|
|
ResourceState.DstPipelines);
|
|
|
|
if (bIsBegin)
|
|
{
|
|
SyncAfter = IntermediateBarrierValues.Sync;
|
|
AccessAfter = IntermediateBarrierValues.Access;
|
|
LayoutAfter = IntermediateBarrierValues.Layout;
|
|
}
|
|
else
|
|
{
|
|
SyncBefore = IntermediateBarrierValues.Sync;
|
|
AccessBefore = IntermediateBarrierValues.Access;
|
|
LayoutBefore = IntermediateBarrierValues.Layout;
|
|
}
|
|
}
|
|
|
|
const ERHIPipeline ThisPipe = Context.GetPipeline();
|
|
SyncBefore = FilterToQueueCompatibleSync(SyncBefore, ThisPipe);
|
|
SyncAfter = FilterToQueueCompatibleSync(SyncAfter, ThisPipe);
|
|
AccessBefore = FilterToQueueCompatibleAccess(AccessBefore, ThisPipe);
|
|
AccessAfter = FilterToQueueCompatibleAccess(AccessAfter, ThisPipe);
|
|
|
|
// Can't use SYNC_NONE when there's access work to be done so have to choose something
|
|
if (ResourceState.AccessBefore != ERHIAccess::Present
|
|
&& SyncBefore == D3D12_BARRIER_SYNC_NONE
|
|
&& AccessBefore != D3D12_BARRIER_ACCESS_NO_ACCESS)
|
|
{
|
|
SyncBefore = FilterToQueueCompatibleSync(GetAccessCompatibleSync(AccessBefore), ThisPipe);
|
|
}
|
|
#if PLATFORM_REQUIRES_ENHANCED_BARRIERS_GFX_ONLY_READ_BITS_HACK
|
|
// @TODO - This is necessary because of the hack that makes D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_GENERIC_READ
|
|
// compatible with the compute pipe in order to support gfx specific read access when a resource is
|
|
// available on multiple pipes at once. It should be removed when MS fixes the EB API.
|
|
// Although we've made the layout and access bits compatible, there's no change to the sync bits so
|
|
// we can end up in situations where the sync filters down to none when moving a resource to/from gfx
|
|
// to compute.
|
|
if (ResourceState.AccessAfter != ERHIAccess::Present
|
|
&& SyncAfter == D3D12_BARRIER_SYNC_NONE
|
|
&& AccessAfter != D3D12_BARRIER_ACCESS_NO_ACCESS)
|
|
{
|
|
SyncAfter = FilterToQueueCompatibleSync(GetAccessCompatibleSync(AccessAfter), ThisPipe);
|
|
}
|
|
#endif
|
|
check(!(ResourceState.AccessAfter != ERHIAccess::Present
|
|
&& SyncAfter == D3D12_BARRIER_SYNC_NONE
|
|
&& AccessAfter != D3D12_BARRIER_ACCESS_NO_ACCESS));
|
|
|
|
if (Texture)
|
|
{
|
|
const bool bIsReallyWholeResource =
|
|
Info.IsWholeResource() ||
|
|
(Resource->GetSubresourceCount() == 1);
|
|
|
|
if (!bIsReallyWholeResource)
|
|
{
|
|
// high level rendering is controlling transition ranges, at this level this is an index not a range
|
|
check(Info.MipIndex != FRHISubresourceRange::kAllSubresources);
|
|
check(Info.ArraySlice != FRHISubresourceRange::kAllSubresources);
|
|
check(Info.PlaneSlice != FRHISubresourceRange::kAllSubresources);
|
|
}
|
|
|
|
const uint32 SubresourceIdx =
|
|
bIsReallyWholeResource ?
|
|
D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES :
|
|
D3D12CalcSubresource(
|
|
Info.MipIndex,
|
|
Info.ArraySlice,
|
|
Info.PlaneSlice,
|
|
Resource->GetMipLevels(),
|
|
Resource->GetArraySize());
|
|
|
|
const bool bDiscard =
|
|
LayoutBefore == D3D12_BARRIER_LAYOUT_UNDEFINED
|
|
&& LayoutAfter != D3D12_BARRIER_LAYOUT_UNDEFINED
|
|
&& !(GD3D12DisableDiscardOfDepthResources && Resource->IsDepthStencilResource())
|
|
&& GD3D12AllowDiscardResources;
|
|
|
|
Batcher->AddTextureBarrier(
|
|
Context,
|
|
Resource,
|
|
SyncBefore,
|
|
SyncAfter,
|
|
AccessBefore,
|
|
AccessAfter,
|
|
LayoutBefore,
|
|
LayoutAfter,
|
|
SubresourceIdx,
|
|
bDiscard);
|
|
}
|
|
else
|
|
{
|
|
Batcher->AddBufferBarrier(
|
|
Context,
|
|
Resource,
|
|
SyncBefore,
|
|
SyncAfter,
|
|
AccessBefore,
|
|
AccessAfter);
|
|
}
|
|
|
|
bAddedBarriers = true;
|
|
}
|
|
}
|
|
|
|
return bAddedBarriers;
|
|
}
|
|
|
|
void FD3D12EnhancedBarriersForContext::HandleReservedResourceCommits(
|
|
FD3D12CommandContext& Context,
|
|
const FD3D12EnhancedBarriersTransitionData* TransitionData)
|
|
{
|
|
for (const FRHITransitionInfo& Info : TransitionData->TransitionInfos)
|
|
{
|
|
if (const FRHICommitResourceInfo* CommitInfo = Info.CommitInfo.GetPtrOrNull())
|
|
{
|
|
if (Info.Type == FRHITransitionInfo::EType::Buffer)
|
|
{
|
|
FD3D12Buffer* Buffer = Context.RetrieveObject<FD3D12Buffer>(Info.Buffer);
|
|
Context.SetReservedBufferCommitSize(Buffer, CommitInfo->SizeInBytes);
|
|
}
|
|
else
|
|
{
|
|
checkNoEntry();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif // D3D12RHI_SUPPORTS_ENHANCED_BARRIERS
|