Files
UnrealEngine/Engine/Source/Developer/Windows/ShaderFormatD3D/Private/D3DShaderCompilerDXC.cpp
2025-05-18 13:04:45 +08:00

1329 lines
41 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "ShaderFormatD3D.h"
#include "ShaderPreprocessor.h"
#include "ShaderCompilerCommon.h"
#include "ShaderCompileWorkerUtil.h"
#include "ShaderParameterParser.h"
#include "D3D12RHI.h"
#include "Misc/Paths.h"
#include "Misc/FileHelper.h"
#include "Misc/Fnv.h"
#include "HAL/FileManager.h"
#include "Serialization/MemoryWriter.h"
#include "ShaderPreprocessTypes.h"
#include "RayTracingDefinitions.h"
// D3D doesn't define a mask for this, so we do so here
#define SHADER_OPTIMIZATION_LEVEL_MASK (D3DCOMPILE_OPTIMIZATION_LEVEL0 | D3DCOMPILE_OPTIMIZATION_LEVEL1 | D3DCOMPILE_OPTIMIZATION_LEVEL2 | D3DCOMPILE_OPTIMIZATION_LEVEL3)
// Disable macro redefinition warning for compatibility with Windows SDK 8+
#pragma warning(push)
#pragma warning(disable : 4005) // macro redefinition
#include "Windows/AllowWindowsPlatformTypes.h"
#include <D3D12.h>
#include <D3Dcompiler.h>
#include <d3d11Shader.h>
#include "amd_ags.h"
#include "Windows/HideWindowsPlatformTypes.h"
#undef DrawText
#pragma warning(pop)
MSVC_PRAGMA(warning(push))
MSVC_PRAGMA(warning(disable : 4191)) // warning C4191: 'type cast': unsafe conversion from 'FARPROC' to 'DxcCreateInstanceProc'
#include <dxc/dxcapi.h>
#include <dxc/Support/dxcapi.use.h>
#include <dxc/Support/ErrorCodes.h>
#include <dxc/DXIL/DxilConstants.h>
#include <d3d12shader.h>
MSVC_PRAGMA(warning(pop))
THIRD_PARTY_INCLUDES_START
#include <string>
#include "ShaderConductor/ShaderConductor.hpp"
THIRD_PARTY_INCLUDES_END
#include "DXCUtils.inl"
#include "D3DShaderCompiler.inl"
FORCENOINLINE static void DXCFilterShaderCompileWarnings(const FString& CompileWarnings, TArray<FString>& FilteredWarnings)
{
CompileWarnings.ParseIntoArray(FilteredWarnings, TEXT("\n"), true);
}
static bool IsGlobalConstantBufferSupported(const FShaderTarget& Target)
{
switch (Target.Frequency)
{
case SF_RayGen:
case SF_RayMiss:
case SF_RayCallable:
// Global CB is not currently implemented for RayGen, Miss and Callable ray tracing shaders.
return false;
default:
return true;
}
}
static uint32 GetAutoBindingSpace(const FShaderTarget& Target)
{
switch (Target.Frequency)
{
case SF_RayGen:
return UE_HLSL_SPACE_RAY_TRACING_GLOBAL;
case SF_RayMiss:
case SF_RayHitGroup:
case SF_RayCallable:
return UE_HLSL_SPACE_RAY_TRACING_LOCAL;
case SF_WorkGraphRoot:
return UE_HLSL_SPACE_WORK_GRAPH_GLOBAL;
case SF_WorkGraphComputeNode:
return UE_HLSL_SPACE_WORK_GRAPH_LOCAL;
default:
return UE_HLSL_SPACE_DEFAULT;
}
}
// DXC specific error codes cannot be translated by FPlatformMisc::GetSystemErrorMessage, so do it manually.
// Codes defines in <DXC>/include/dxc/Support/ErrorCodes.h
static const TCHAR* DxcErrorCodeToString(HRESULT Code)
{
#define SWITCHCASE_TO_STRING(VALUE) case VALUE: return TEXT(#VALUE)
switch (Code)
{
SWITCHCASE_TO_STRING( DXC_E_OVERLAPPING_SEMANTICS );
SWITCHCASE_TO_STRING( DXC_E_MULTIPLE_DEPTH_SEMANTICS );
SWITCHCASE_TO_STRING( DXC_E_INPUT_FILE_TOO_LARGE );
SWITCHCASE_TO_STRING( DXC_E_INCORRECT_DXBC );
SWITCHCASE_TO_STRING( DXC_E_ERROR_PARSING_DXBC_BYTECODE );
SWITCHCASE_TO_STRING( DXC_E_DATA_TOO_LARGE );
SWITCHCASE_TO_STRING( DXC_E_INCOMPATIBLE_CONVERTER_OPTIONS);
SWITCHCASE_TO_STRING( DXC_E_IRREDUCIBLE_CFG );
SWITCHCASE_TO_STRING( DXC_E_IR_VERIFICATION_FAILED );
SWITCHCASE_TO_STRING( DXC_E_SCOPE_NESTED_FAILED );
SWITCHCASE_TO_STRING( DXC_E_NOT_SUPPORTED );
SWITCHCASE_TO_STRING( DXC_E_STRING_ENCODING_FAILED );
SWITCHCASE_TO_STRING( DXC_E_CONTAINER_INVALID );
SWITCHCASE_TO_STRING( DXC_E_CONTAINER_MISSING_DXIL );
SWITCHCASE_TO_STRING( DXC_E_INCORRECT_DXIL_METADATA );
SWITCHCASE_TO_STRING( DXC_E_INCORRECT_DDI_SIGNATURE );
SWITCHCASE_TO_STRING( DXC_E_DUPLICATE_PART );
SWITCHCASE_TO_STRING( DXC_E_MISSING_PART );
SWITCHCASE_TO_STRING( DXC_E_MALFORMED_CONTAINER );
SWITCHCASE_TO_STRING( DXC_E_INCORRECT_ROOT_SIGNATURE );
SWITCHCASE_TO_STRING( DXC_E_CONTAINER_MISSING_DEBUG );
SWITCHCASE_TO_STRING( DXC_E_MACRO_EXPANSION_FAILURE );
SWITCHCASE_TO_STRING( DXC_E_OPTIMIZATION_FAILED );
SWITCHCASE_TO_STRING( DXC_E_GENERAL_INTERNAL_ERROR );
SWITCHCASE_TO_STRING( DXC_E_ABORT_COMPILATION_ERROR );
SWITCHCASE_TO_STRING( DXC_E_EXTENSION_ERROR );
SWITCHCASE_TO_STRING( DXC_E_LLVM_FATAL_ERROR );
SWITCHCASE_TO_STRING( DXC_E_LLVM_UNREACHABLE );
SWITCHCASE_TO_STRING( DXC_E_LLVM_CAST_ERROR );
}
return nullptr;
#undef SWITCHCASE_TO_STRING
}
static void LogFailedHRESULT(const TCHAR* FailedExpressionStr, HRESULT Result)
{
if (Result == E_OUTOFMEMORY)
{
const FString ErrorReport = FString::Printf(TEXT("%s failed: Result=0x%08x (E_OUTOFMEMORY)"), FailedExpressionStr, (uint32)Result);
FSCWErrorCode::Report(FSCWErrorCode::OutOfMemory, ErrorReport);
UE_LOG(LogD3DShaderCompiler, Fatal, TEXT("%s"), *ErrorReport);
}
else if (const TCHAR* ErrorCodeStr = DxcErrorCodeToString(Result))
{
UE_LOG(LogD3DShaderCompiler, Fatal, TEXT("%s failed: Result=0x%08x (%s)"), FailedExpressionStr, Result, ErrorCodeStr);
}
else
{
// Turn HRESULT into human readable string for error report
TCHAR ResultStr[4096] = {};
FPlatformMisc::GetSystemErrorMessage(ResultStr, UE_ARRAY_COUNT(ResultStr), Result);
UE_LOG(LogD3DShaderCompiler, Fatal, TEXT("%s failed: Result=0x%08x (%s)"), FailedExpressionStr, Result, ResultStr);
}
}
#define VERIFYHRESULT(expr) \
{ \
const HRESULT HR##__LINE__ = expr; \
if (FAILED(HR##__LINE__)) \
{ \
LogFailedHRESULT(TEXT(#expr), HR##__LINE__); \
} \
}
class FDxcArguments
{
protected:
FString ShaderProfile;
FString EntryPoint;
FString Exports;
FString DumpDisasmFilename;
FString BatchBaseFilename;
FString DumpDebugInfoPath;
bool bKeepEmbeddedPDB = false;
bool bDump = false;
TArray<FString> ExtraArguments;
public:
FDxcArguments(
const FShaderCompilerInput& Input,
const FString& InEntryPoint,
const TCHAR* InShaderProfile,
ED3DShaderModel ShaderModel,
const FString& InExports
)
: ShaderProfile(InShaderProfile)
, EntryPoint(InEntryPoint)
, Exports(InExports)
, BatchBaseFilename(FPaths::GetBaseFilename(Input.GetSourceFilename()))
, DumpDebugInfoPath(Input.DumpDebugInfoPath)
, bDump(Input.DumpDebugInfoEnabled())
{
if (bDump)
{
DumpDisasmFilename = Input.DumpDebugInfoPath / TEXT("Output.d3dasm");
}
const bool bEnable16BitTypes =
// 16bit types are SM6.2, so their support at runtime is guaranteed in SM6.6.
(ShaderModel >= ED3DShaderModel::SM6_6 && Input.Environment.CompilerFlags.Contains(CFLAG_AllowRealTypes))
// Enable 16bit_types to reduce DXIL size (compiler bug - will be fixed)
|| Input.IsRayTracingShader();
const bool bHlslVersion2021 = Input.Environment.CompilerFlags.Contains(CFLAG_HLSL2021);
if (bHlslVersion2021)
{
ExtraArguments.Add(TEXT("-HV"));
ExtraArguments.Add(TEXT("2021"));
}
else
{
ExtraArguments.Add(TEXT("-HV"));
ExtraArguments.Add(TEXT("2018"));
}
// Unpack uniform matrices as row-major to match the CPU layout.
ExtraArguments.Add(TEXT("-Zpr"));
if (Input.Environment.CompilerFlags.Contains(CFLAG_SkipValidation))
{
ExtraArguments.Add(TEXT("-Vd"));
}
if (Input.Environment.CompilerFlags.Contains(CFLAG_Debug) || Input.Environment.CompilerFlags.Contains(CFLAG_SkipOptimizationsDXC))
{
ExtraArguments.Add(TEXT("-Od"));
}
else if (Input.Environment.CompilerFlags.Contains(CFLAG_StandardOptimization))
{
ExtraArguments.Add(TEXT("-O1"));
}
else
{
ExtraArguments.Add(TEXT("-O3"));
}
if (Input.Environment.CompilerFlags.Contains(CFLAG_PreferFlowControl))
{
ExtraArguments.Add(TEXT("-Gfp"));
}
if (Input.Environment.CompilerFlags.Contains(CFLAG_AvoidFlowControl))
{
ExtraArguments.Add(TEXT("-Gfa"));
}
if (Input.Environment.CompilerFlags.Contains(CFLAG_WarningsAsErrors))
{
ExtraArguments.Add(TEXT("-WX"));
}
const uint32 AutoBindingSpace = GetAutoBindingSpace(Input.Target);
{
ExtraArguments.Add(TEXT("-auto-binding-space"));
ExtraArguments.Add(FString::Printf(TEXT("%d"), AutoBindingSpace));
}
if (Exports.Len() > 0)
{
// Ensure that only the requested functions exists in the output DXIL.
// All other functions and their used resources must be eliminated.
ExtraArguments.Add(TEXT("-exports"));
ExtraArguments.Add(Exports);
}
if (bEnable16BitTypes)
{
ExtraArguments.Add(TEXT("-enable-16bit-types"));
}
if (Input.Environment.CompilerFlags.Contains(CFLAG_GenerateSymbols))
{
if (Input.Environment.CompilerFlags.Contains(CFLAG_AllowUniqueSymbols))
{
// -Zss Compute Shader Hash considering source information
ExtraArguments.Add(TEXT("-Zss"));
}
else
{
// -Zsb Compute Shader Hash considering only output binary
ExtraArguments.Add(TEXT("-Zsb"));
}
// generate the debug information (PDB)
ExtraArguments.Add(TEXT("-Zi"));
// always strip the PDB from the DXIL output blob, we retrieve from the compile result and save it manually in the PlatformDebugData
ExtraArguments.Add("-Qstrip_debug");
}
// disable undesired warnings
ExtraArguments.Add(TEXT("-Wno-parentheses-equality"));
// working around bindless conversion specific issue where globallycoherent on a function return type is flagged as ignored even though it is necessary.
// github issue: https://github.com/microsoft/DirectXShaderCompiler/issues/4537
if (Input.IsBindlessEnabled())
{
ExtraArguments.Add(TEXT("-Wno-ignored-attributes"));
}
// @lh-todo: This fixes a loop unrolling issue that showed up in DOFGatherKernel with cs_6_6 with the latest DXC revision
ExtraArguments.Add(TEXT("-disable-lifetime-markers"));
}
FString GetDumpDebugInfoPath() const
{
return DumpDebugInfoPath;
}
bool ShouldDump() const
{
return bDump;
}
FString GetEntryPointName() const
{
return Exports.Len() > 0 ? FString(TEXT("")) : EntryPoint;
}
const FString& GetShaderProfile() const
{
return ShaderProfile;
}
const FString& GetDumpDisassemblyFilename() const
{
return DumpDisasmFilename;
}
void GetCompilerArgsNoEntryNoProfileNoDisasm(TArray<const WCHAR*>& Out) const
{
for (const FString& Entry : ExtraArguments)
{
Out.Add(*Entry);
}
}
void GetCompilerArgs(TArray<const WCHAR*>& Out) const
{
GetCompilerArgsNoEntryNoProfileNoDisasm(Out);
if (Exports.Len() == 0)
{
Out.Add(TEXT("-E"));
Out.Add(*EntryPoint);
}
Out.Add(TEXT("-T"));
Out.Add(*ShaderProfile);
}
const FString& GetBatchBaseFilename() const
{
return BatchBaseFilename;
}
FString GetBatchCommandLineString() const
{
FString DXCCommandline;
for (const FString& Entry : ExtraArguments)
{
DXCCommandline += TEXT(" ");
DXCCommandline += Entry;
}
DXCCommandline += TEXT(" -T ");
DXCCommandline += ShaderProfile;
if (Exports.Len() == 0)
{
DXCCommandline += TEXT(" -E ");
DXCCommandline += EntryPoint;
}
DXCCommandline += TEXT(" -Fc ");
DXCCommandline += BatchBaseFilename + TEXT(".d3dasm");
DXCCommandline += TEXT(" -Fo ");
DXCCommandline += BatchBaseFilename + TEXT(".dxil");
return DXCCommandline;
}
};
class FDxcMalloc final : public IMalloc
{
std::atomic<ULONG> RefCount{ 1 };
public:
// IMalloc
void* STDCALL Alloc(SIZE_T cb) override
{
cb = FMath::Max(SIZE_T(1), cb);
return FMemory::Malloc(cb);
}
void* STDCALL Realloc(void* pv, SIZE_T cb) override
{
cb = FMath::Max(SIZE_T(1), cb);
return FMemory::Realloc(pv, cb);
}
void STDCALL Free(void* pv) override
{
return FMemory::Free(pv);
}
SIZE_T STDCALL GetSize(void* pv) override
{
return FMemory::GetAllocSize(pv);
}
int STDCALL DidAlloc(void* pv) override
{
return 1; // assume that all allocation queries coming from DXC belong to our allocator
}
void STDCALL HeapMinimize() override
{
// nothing
}
// IUnknown
ULONG STDCALL AddRef() override
{
return ++RefCount;
}
ULONG STDCALL Release() override
{
check(RefCount > 0);
return --RefCount;
}
HRESULT STDCALL QueryInterface(REFIID iid, void** ppvObject) override
{
checkNoEntry(); // We do not expect or support QI on DXC allocator replacement
return ERROR_NOINTERFACE;
}
};
static IMalloc* GetDxcMalloc()
{
static FDxcMalloc Instance;
return &Instance;
}
static dxc::DxcDllSupport& GetDxcDllHelper()
{
struct DxcDllHelper
{
DxcDllHelper()
{
VERIFYHRESULT(DxcDllSupport.Initialize());
}
dxc::DxcDllSupport DxcDllSupport;
};
static DxcDllHelper DllHelper;
return DllHelper.DxcDllSupport;
}
static FString DxcBlobEncodingToFString(TRefCountPtr<IDxcBlobEncoding> DxcBlob)
{
FString OutString;
if (DxcBlob && DxcBlob->GetBufferSize())
{
ANSICHAR* Chars = new ANSICHAR[DxcBlob->GetBufferSize() + 1];
FMemory::Memcpy(Chars, DxcBlob->GetBufferPointer(), DxcBlob->GetBufferSize());
Chars[DxcBlob->GetBufferSize()] = 0;
OutString = Chars;
delete[] Chars;
}
return OutString;
}
#if !PLATFORM_SEH_EXCEPTIONS_DISABLED && PLATFORM_WINDOWS
#include "Windows/WindowsPlatformCrashContext.h"
#include "HAL/PlatformStackWalk.h"
static char GDxcStackTrace[65536] = "";
static int32 HandleException(LPEXCEPTION_POINTERS ExceptionInfo)
{
constexpr int32 NumStackFramesToIgnore = 1;
GDxcStackTrace[0] = 0;
FPlatformStackWalk::StackWalkAndDump(GDxcStackTrace, UE_ARRAY_COUNT(GDxcStackTrace), NumStackFramesToIgnore, nullptr);
return EXCEPTION_EXECUTE_HANDLER;
}
#else
static const char* GDxcStackTrace = "";
#endif
static HRESULT InnerDXCCompileWrapper(
TRefCountPtr<IDxcCompiler3>& Compiler,
TRefCountPtr<IDxcBlobEncoding>& TextBlob,
LPCWSTR* Arguments,
uint32 NumArguments,
bool& bOutExceptionError,
TRefCountPtr<IDxcResult>& OutCompileResult)
{
bOutExceptionError = false;
#if !PLATFORM_SEH_EXCEPTIONS_DISABLED && PLATFORM_WINDOWS
__try
#endif
{
DxcBuffer SourceBuffer = { 0 };
SourceBuffer.Ptr = TextBlob->GetBufferPointer();
SourceBuffer.Size = TextBlob->GetBufferSize();
BOOL bKnown = 0;
uint32 Encoding = 0;
if (SUCCEEDED(TextBlob->GetEncoding(&bKnown, (uint32*)&Encoding)))
{
if (bKnown)
{
SourceBuffer.Encoding = Encoding;
}
}
return Compiler->Compile(
&SourceBuffer, // source text to compile
Arguments, // array of pointers to arguments
NumArguments, // number of arguments
nullptr, // user-provided interface to handle #include directives (optional)
IID_PPV_ARGS(OutCompileResult.GetInitReference()) // compiler output status, buffer, and errors
);
}
#if !PLATFORM_SEH_EXCEPTIONS_DISABLED && PLATFORM_WINDOWS
__except (HandleException(GetExceptionInformation()))
{
bOutExceptionError = true;
return E_FAIL;
}
#endif
}
static HRESULT DXCCompileWrapper(
TRefCountPtr<IDxcCompiler3>& Compiler,
TRefCountPtr<IDxcBlobEncoding>& TextBlob,
const FDxcArguments& Arguments,
TRefCountPtr<IDxcResult>& OutCompileResult)
{
bool bExceptionError = false;
TArray<const WCHAR*> CompilerArgs;
Arguments.GetCompilerArgs(CompilerArgs);
// Give a unique name to the d3dasm and dxil outputs (Must have same scope as CompilerArgs so the temporary strings remain valid)
FString AsmFilename = Arguments.GetBatchBaseFilename() + TEXT(".d3dasm");
FString DXILFilename = Arguments.GetBatchBaseFilename() + TEXT(".dxil");
CompilerArgs.Add(TEXT(" -Fc "));
CompilerArgs.Add(*AsmFilename);
CompilerArgs.Add(TEXT(" -Fo "));
CompilerArgs.Add(*DXILFilename);
HRESULT Result = InnerDXCCompileWrapper(Compiler, TextBlob,
CompilerArgs.GetData(), CompilerArgs.Num(), bExceptionError, OutCompileResult);
if (bExceptionError)
{
FSCWErrorCode::Report(FSCWErrorCode::CrashInsidePlatformCompiler);
FString ErrorMsg = TEXT("Internal error or exception inside dxcompiler.dll\n");
ErrorMsg += GDxcStackTrace;
FCString::Strcpy(GErrorExceptionDescription, *ErrorMsg);
#if !PLATFORM_SEH_EXCEPTIONS_DISABLED && PLATFORM_WINDOWS
// Throw an exception so SCW can send it back in the output file
FPlatformMisc::RaiseException(EXCEPTION_EXECUTE_HANDLER);
#endif
}
return Result;
}
static void SaveDxcBlobToFile(IDxcBlob* Blob, const FString& Filename)
{
const uint8* DxilData = (const uint8*)Blob->GetBufferPointer();
uint32 DxilSize = Blob->GetBufferSize();
TArrayView<const uint8> Contents(DxilData, DxilSize);
FFileHelper::SaveArrayToFile(Contents, *Filename);
}
static void DisassembleAndSave(TRefCountPtr<IDxcCompiler3>& Compiler, IDxcBlob* Dxil, const FString& DisasmFilename)
{
TRefCountPtr<IDxcResult> DisasmResult;
DxcBuffer DisasmBuffer = { 0 };
DisasmBuffer.Size = Dxil->GetBufferSize();
DisasmBuffer.Ptr = Dxil->GetBufferPointer();
if (SUCCEEDED(Compiler->Disassemble(&DisasmBuffer, IID_PPV_ARGS(DisasmResult.GetInitReference()))))
{
HRESULT DisasmCodeResult;
DisasmResult->GetStatus(&DisasmCodeResult);
if (SUCCEEDED(DisasmCodeResult))
{
checkf(DisasmResult->HasOutput(DXC_OUT_DISASSEMBLY), TEXT("Disasm part missing but container said it has one!"));
TRefCountPtr<IDxcBlobEncoding> DisasmBlob;
TRefCountPtr<IDxcBlobUtf16> Dummy;
VERIFYHRESULT(DisasmResult->GetOutput(DXC_OUT_DISASSEMBLY, IID_PPV_ARGS(DisasmBlob.GetInitReference()), Dummy.GetInitReference()));
FString String = DxcBlobEncodingToFString(DisasmBlob);
FFileHelper::SaveStringToFile(String, *DisasmFilename);
}
}
}
static HRESULT RemoveReflectionData(dxc::DxcDllSupport& DxcDllHelper, TRefCountPtr<IDxcBlob>& Dxil)
{
TRefCountPtr<IDxcOperationResult> Result;
TRefCountPtr<IDxcContainerBuilder> Builder;
TRefCountPtr<IDxcBlob> StrippedDxil;
VERIFYHRESULT(DxcDllHelper.CreateInstance2(GetDxcMalloc(), CLSID_DxcContainerBuilder, Builder.GetInitReference()));
VERIFYHRESULT(Builder->Load(Dxil));
HRESULT Res = Builder->RemovePart(DXC_PART_REFLECTION_DATA);
if (FAILED(Res))
{
return Res;
}
Res = Builder->SerializeContainer(Result.GetInitReference());
if (FAILED(Res))
{
return Res;
}
Res = Result->GetResult(StrippedDxil.GetInitReference());
if (SUCCEEDED(Res))
{
Dxil.SafeRelease();
Dxil = StrippedDxil;
}
return Res;
}
static HRESULT D3DCompileToDxil(const char* SourceText, const FDxcArguments& Arguments,
TRefCountPtr<IDxcBlob>& OutDxilBlob, TRefCountPtr<IDxcBlob>& OutReflectionBlob, TRefCountPtr<IDxcBlobEncoding>& OutErrorBlob, TRefCountPtr<IDxcBlob>& OutPdbBlob, FString& OutPdbName, DxcShaderHash& OutHash)
{
dxc::DxcDllSupport& DxcDllHelper = GetDxcDllHelper();
TRefCountPtr<IDxcCompiler3> Compiler;
VERIFYHRESULT(DxcDllHelper.CreateInstance2(GetDxcMalloc(), CLSID_DxcCompiler, Compiler.GetInitReference()));
TRefCountPtr<IDxcLibrary> Library;
VERIFYHRESULT(DxcDllHelper.CreateInstance2(GetDxcMalloc(), CLSID_DxcLibrary, Library.GetInitReference()));
TRefCountPtr<IDxcBlobEncoding> TextBlob;
VERIFYHRESULT(Library->CreateBlobWithEncodingFromPinned((LPBYTE)SourceText, FCStringAnsi::Strlen(SourceText), CP_UTF8, TextBlob.GetInitReference()));
TRefCountPtr<IDxcResult> CompileResult;
VERIFYHRESULT(DXCCompileWrapper(Compiler, TextBlob, Arguments, CompileResult));
if (!CompileResult.IsValid())
{
return E_FAIL;
}
HRESULT CompileResultCode;
CompileResult->GetStatus(&CompileResultCode);
if (SUCCEEDED(CompileResultCode))
{
TRefCountPtr<IDxcBlobUtf16> ObjectCodeNameBlob; // Dummy name blob to silence static analysis warning
checkf(CompileResult->HasOutput(DXC_OUT_OBJECT), TEXT("No object code found!"));
VERIFYHRESULT(CompileResult->GetOutput(DXC_OUT_OBJECT, IID_PPV_ARGS(OutDxilBlob.GetInitReference()), ObjectCodeNameBlob.GetInitReference()));
const bool bPostCompileSign = false;
if (bPostCompileSign)
{
// https://www.wihlidal.com/blog/pipeline/2018-09-16-dxil-signing-post-compile/
TRefCountPtr<IDxcValidator> Validator;
VERIFYHRESULT(DxcDllHelper.CreateInstance2(GetDxcMalloc(), CLSID_DxcValidator, Validator.GetInitReference()));
#if 0
struct FDxilMinimalHeader
{
uint32 FourCC;
uint32 HashDigest[4];
};
FDxilMinimalHeader BeforeSignHeader = *reinterpret_cast<FDxilMinimalHeader*>(OutDxilBlob->GetBufferPointer());
(void)BeforeSignHeader;
#endif
TRefCountPtr<IDxcOperationResult> ValidateResult;
VERIFYHRESULT(Validator->Validate(OutDxilBlob.GetReference(), DxcValidatorFlags_InPlaceEdit, ValidateResult.GetInitReference()));
#if 0
FDxilMinimalHeader AfterSignHeader = *reinterpret_cast<FDxilMinimalHeader*>(OutDxilBlob->GetBufferPointer());
(void)AfterSignHeader;
#endif
}
TRefCountPtr<IDxcBlobUtf16> ReflectionNameBlob; // Dummy name blob to silence static analysis warning
checkf(CompileResult->HasOutput(DXC_OUT_REFLECTION), TEXT("No reflection found!"));
VERIFYHRESULT(CompileResult->GetOutput(DXC_OUT_REFLECTION, IID_PPV_ARGS(OutReflectionBlob.GetInitReference()), ReflectionNameBlob.GetInitReference()));
RetrieveDebugNameAndBlob(CompileResult, OutPdbName, OutPdbBlob.GetInitReference(), OutHash);
if (Arguments.ShouldDump())
{
// Dump disassembly before we strip reflection out
const FString& DisasmFilename = Arguments.GetDumpDisassemblyFilename();
check(DisasmFilename.Len() > 0);
DisassembleAndSave(Compiler, OutDxilBlob, DisasmFilename);
}
HRESULT ReflectionStripResult = RemoveReflectionData(DxcDllHelper, OutDxilBlob);
if (FAILED(ReflectionStripResult))
{
return ReflectionStripResult;
}
}
CompileResult->GetErrorBuffer(OutErrorBlob.GetInitReference());
return CompileResultCode;
}
static FString D3DCreateDXCCompileBatchFile(const FDxcArguments& Args)
{
FString DxcPath = FPaths::ConvertRelativePathToFull(FPaths::EngineDir());
DxcPath = FPaths::Combine(DxcPath, TEXT("Binaries/ThirdParty/ShaderConductor/Win64"));
FPaths::MakePlatformFilename(DxcPath);
FString DxcFilename = FPaths::Combine(DxcPath, TEXT("dxc.exe"));
FPaths::MakePlatformFilename(DxcFilename);
const FString& BatchBaseFilename = Args.GetBatchBaseFilename();
const FString BatchCmdLineArgs = Args.GetBatchCommandLineString();
return FString::Printf(
TEXT(
"@ECHO OFF\n"
"SET DXC=\"%s\"\n"
"IF NOT EXIST %%DXC%% (\n"
"\tECHO Couldn't find dxc.exe under \"%s\"\n"
"\tGOTO :END\n"
")\n"
"%%DXC%%%s %s.usf\n"
":END\n"
"PAUSE\n"
),
*DxcFilename,
*DxcPath,
*BatchCmdLineArgs,
*BatchBaseFilename
);
}
inline bool IsCompatibleBinding(const D3D12_SHADER_INPUT_BIND_DESC& BindDesc, uint32 BindingSpace)
{
bool bIsCompatibleBinding = (BindDesc.Space == BindingSpace);
if (!bIsCompatibleBinding)
{
const bool bIsAMDExtensionDX12 = (FCStringAnsi::Strcmp(BindDesc.Name, "AmdExtD3DShaderIntrinsicsUAV") == 0);
bIsCompatibleBinding = bIsAMDExtensionDX12 && (BindDesc.Space == AGS_DX12_SHADER_INSTRINSICS_SPACE_ID);
}
if (!bIsCompatibleBinding)
{
const bool bIsUEDebugBuffer = (FCStringAnsi::Strcmp(BindDesc.Name, "UEDiagnosticBuffer") == 0);
bIsCompatibleBinding = bIsUEDebugBuffer && (BindDesc.Space == UE_HLSL_SPACE_DIAGNOSTIC);
}
if (!bIsCompatibleBinding)
{
const bool bIsUERootConstants = (FCStringAnsi::Strcmp(BindDesc.Name, "UERootConstants") == 0);
bIsCompatibleBinding = bIsUERootConstants && (BindDesc.Space == UE_HLSL_SPACE_SHADER_ROOT_CONSTANTS);
}
return bIsCompatibleBinding;
}
// Generate the dumped usf file; call the D3D compiler, gather reflection information and generate the output data
bool CompileAndProcessD3DShaderDXC(
const FShaderCompilerInput& Input,
const FString& PreprocessedShaderSource,
const FString& EntryPointName,
const FShaderParameterParser& ShaderParameterParser,
const TCHAR* ShaderProfile,
const ED3DShaderModel ShaderModel,
const bool bProcessingSecondTime,
FShaderCompilerOutput& Output)
{
TRACE_CPUPROFILER_EVENT_SCOPE(CompileAndProcessD3DShaderDXC);
auto AnsiSourceFile = StringCast<ANSICHAR>(*PreprocessedShaderSource);
const bool bIsRayTracingShader = Input.IsRayTracingShader();
const bool bIsWorkGraphShader = Input.IsWorkGraphShader();
const uint32 AutoBindingSpace = GetAutoBindingSpace(Input.Target);
FString RayEntryPoint; // Primary entry point for all ray tracing shaders
FString RayAnyHitEntryPoint; // Optional for hit group shaders
FString RayIntersectionEntryPoint; // Optional for hit group shaders
FString RayTracingExports;
if (bIsRayTracingShader)
{
UE::ShaderCompilerCommon::ParseRayTracingEntryPoint(Input.EntryPointName, RayEntryPoint, RayAnyHitEntryPoint, RayIntersectionEntryPoint);
RayTracingExports = RayEntryPoint;
if (!RayAnyHitEntryPoint.IsEmpty())
{
RayTracingExports += TEXT(";");
RayTracingExports += RayAnyHitEntryPoint;
}
if (!RayIntersectionEntryPoint.IsEmpty())
{
RayTracingExports += TEXT(";");
RayTracingExports += RayIntersectionEntryPoint;
}
}
FDxcArguments Args
(
Input,
EntryPointName,
ShaderProfile,
ShaderModel,
RayTracingExports
);
if (Args.ShouldDump())
{
const FString BatchFileContents = D3DCreateDXCCompileBatchFile(Args);
FFileHelper::SaveStringToFile(BatchFileContents, *(Args.GetDumpDebugInfoPath() / TEXT("CompileDXC.bat")));
}
TRefCountPtr<IDxcBlob> ShaderBlob;
TRefCountPtr<IDxcBlob> ReflectionBlob;
TRefCountPtr<IDxcBlobEncoding> DxcErrorBlob;
TRefCountPtr<IDxcBlob> PdbBlob;
FString PdbName;
DxcShaderHash ShaderHash;
const HRESULT D3DCompileToDxilResult = D3DCompileToDxil(AnsiSourceFile.Get(), Args, ShaderBlob, ReflectionBlob, DxcErrorBlob, PdbBlob, PdbName, ShaderHash);
Output.AddStatistic(UE::ShaderCompilerCommon::kPlatformHashStatName, BytesToHex(ShaderHash.HashDigest, sizeof(ShaderHash.HashDigest)), FGenericShaderStat::EFlags::Hidden);
// Populate the platform-specific debug data with the PDB name and/or data, if available and requested.
bool bWriteSymbolsInfo = Input.Environment.CompilerFlags.Contains(CFLAG_GenerateSymbolsInfo);
bool bWriteSymbols = Input.Environment.CompilerFlags.Contains(CFLAG_GenerateSymbols);
if ((bWriteSymbols || bWriteSymbolsInfo) && !PdbName.IsEmpty())
{
check(!bWriteSymbols || (PdbBlob.IsValid() && (PdbBlob->GetBufferSize() > 0)));
FD3DShaderDebugData DebugData;
FD3DShaderDebugData::FFile& PdbFile = DebugData.Files.AddDefaulted_GetRef();
PdbFile.Name = PdbName;
if (bWriteSymbols)
{
PdbFile.Contents = MakeArrayViewFromBlob(PdbBlob);
// also export the .dxil file alongside the .pdb if symbols are on
FD3DShaderDebugData::FFile& DxilFile = DebugData.Files.AddDefaulted_GetRef();
DxilFile.Name = FPaths::ChangeExtension(PdbName, TEXT(".dxil"));
DxilFile.Contents = MakeArrayViewFromBlob(ShaderBlob);
}
FMemoryWriter Ar(Output.ShaderCode.GetSymbolWriteAccess());
Ar << DebugData;
}
TArray<FString> FilteredErrors;
if (DxcErrorBlob && DxcErrorBlob->GetBufferSize())
{
FString ErrorString = DxcBlobEncodingToFString(DxcErrorBlob);
DXCFilterShaderCompileWarnings(ErrorString, FilteredErrors);
}
if (SUCCEEDED(D3DCompileToDxilResult))
{
// Gather reflection information
FD3DShaderCompileData CompileData;
CompileData.bBindlessEnabled = Input.IsBindlessEnabled();
if (Input.IsBindlessEnabled())
{
CompileData.MaxSamplers = D3D12_MAX_SHADER_VISIBLE_SAMPLER_HEAP_SIZE;
}
else if (ShaderModel == ED3DShaderModel::SM6_6)
{
CompileData.MaxSamplers = 32; // DDSPI: MaxSamplers=32
}
else
{
CompileData.MaxSamplers = D3D12_COMMONSHADER_SAMPLER_REGISTER_COUNT;
}
if (Input.IsBindlessEnabled())
{
CompileData.MaxSRVs = D3D12_MAX_SHADER_VISIBLE_DESCRIPTOR_HEAP_SIZE_TIER_2;
}
else
{
static_assert(MAX_SRVS <= D3D12_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT);
CompileData.MaxSRVs = MAX_SRVS; // Max for D3D12RHI bindful
}
CompileData.MaxCBs = MAX_CBS; // Max for D3D12RHI
CompileData.MaxUAVs = MAX_UAVS; // Max for D3D12RHI
uint64 ShaderRequiresFlags{};
dxc::DxcDllSupport& DxcDllHelper = GetDxcDllHelper();
TRefCountPtr<IDxcUtils> Utils;
VERIFYHRESULT(DxcDllHelper.CreateInstance2(GetDxcMalloc(), CLSID_DxcUtils, Utils.GetInitReference()));
DxcBuffer ReflBuffer = { 0 };
ReflBuffer.Ptr = ReflectionBlob->GetBufferPointer();
ReflBuffer.Size = ReflectionBlob->GetBufferSize();
bool bHasNoDerivativeOps = false;
if ((Input.Target.GetFrequency() == SF_Compute || Input.Target.GetFrequency() == SF_WorkGraphComputeNode) && Input.Environment.CompilerFlags.Contains(CFLAG_CheckForDerivativeOps))
{
TRefCountPtr<IDxcContainerReflection> ContainerRefl;
VERIFYHRESULT(DxcDllHelper.CreateInstance2(GetDxcMalloc(), CLSID_DxcContainerReflection, ContainerRefl.GetInitReference()));
VERIFYHRESULT(ContainerRefl->Load(ShaderBlob));
uint32 PartCount = 0;
VERIFYHRESULT(ContainerRefl->GetPartCount(&PartCount));
for (uint32 PartIndex = 0; PartIndex < PartCount; ++PartIndex)
{
uint32 PartKind;
VERIFYHRESULT(ContainerRefl->GetPartKind(PartIndex, &PartKind));
if (PartKind == DXC_PART_PRIVATE_DATA)
{
struct UE5CustomData
{
uint32_t FourCC;
uint64_t Data;
};
TRefCountPtr<IDxcBlob> UserPartBlob;
ContainerRefl->GetPartContent(PartIndex, UserPartBlob.GetInitReference());
if (UserPartBlob->GetBufferSize() == sizeof(UE5CustomData))
{
const UE5CustomData& CustomData = *(UE5CustomData*)UserPartBlob->GetBufferPointer();
if (CustomData.FourCC == DXC_PART_FEATURE_INFO)
{
bHasNoDerivativeOps = (CustomData.Data & hlsl::DXIL::OptFeatureInfo_UsesDerivatives) == 0;
}
}
break;
}
}
}
// Remove unused interpolators from pixel shader
// (propagated to corresponding VS from pipeline by later setting Output.bSupportsQueryingUsedAttributes and Output.UsedAttributes)
{
TRefCountPtr<ID3D12ShaderReflection> Reflector;
Utils->CreateReflection(&ReflBuffer, IID_PPV_ARGS(Reflector.GetInitReference()));
ShaderCompileLambdaType ShaderCompileLambda = [](
const FShaderCompilerInput& Input,
const FString& PreprocessedShaderSource,
const FString& EntryPointName,
const FShaderParameterParser& ShaderParameterParser,
const TCHAR* ShaderProfile,
const ED3DShaderModel ShaderModel,
const bool bProcessingSecondTime,
FShaderCompilerOutput& Output)
{
return CompileAndProcessD3DShaderDXC(
Input,
PreprocessedShaderSource,
EntryPointName,
ShaderParameterParser,
ShaderProfile,
ShaderModel,
bProcessingSecondTime,
Output);
};
bool CompileResult = false;
const bool RemovedUnusedInterpolatorsApplied =
RemoveUnusedInterpolators<ShaderCompilerType::DXC, ShaderCompileLambdaType,
ID3D12ShaderReflection, D3D12_SHADER_DESC, D3D12_SIGNATURE_PARAMETER_DESC>(
Input,
PreprocessedShaderSource,
EntryPointName,
ShaderParameterParser,
ShaderProfile,
ShaderModel,
bProcessingSecondTime,
CompileData,
Reflector,
ShaderCompileLambda,
Output,
CompileResult);
if (RemovedUnusedInterpolatorsApplied)
{
return CompileResult;
}
}
if (bIsWorkGraphShader || bIsRayTracingShader)
{
TRefCountPtr<ID3D12LibraryReflection> LibraryReflection;
VERIFYHRESULT(Utils->CreateReflection(&ReflBuffer, IID_PPV_ARGS(LibraryReflection.GetInitReference())));
D3D12_LIBRARY_DESC LibraryDesc = {};
LibraryReflection->GetDesc(&LibraryDesc);
ID3D12FunctionReflection* FunctionReflection = nullptr;
D3D12_FUNCTION_DESC FunctionDesc = {};
bool bEntryPointsAreMangled = false;
TArray<FString, TInlineAllocator<3>> EntryPoints;
if (bIsRayTracingShader)
{
// EntryPoints contains partial mangled entry point signatures in a the following form:
// ?QualifiedName@ (as described here: https://en.wikipedia.org/wiki/Name_mangling)
// Entry point parameters are currently not included in the partial mangling.
bEntryPointsAreMangled = true;
if (!RayEntryPoint.IsEmpty())
{
EntryPoints.Add(FString::Printf(TEXT("?%s@"), *RayEntryPoint));
}
if (!RayAnyHitEntryPoint.IsEmpty())
{
EntryPoints.Add(FString::Printf(TEXT("?%s@"), *RayAnyHitEntryPoint));
}
if (!RayIntersectionEntryPoint.IsEmpty())
{
EntryPoints.Add(FString::Printf(TEXT("?%s@"), *RayIntersectionEntryPoint));
}
}
else
{
EntryPoints.Add(Input.EntryPointName);
}
uint32 NumFoundEntryPoints = 0;
for (uint32 FunctionIndex = 0; FunctionIndex < LibraryDesc.FunctionCount; ++FunctionIndex)
{
FunctionReflection = LibraryReflection->GetFunctionByIndex(FunctionIndex);
FunctionReflection->GetDesc(&FunctionDesc);
ShaderRequiresFlags |= FunctionDesc.RequiredFeatureFlags;
bool bAddFunctionEntryPoint = false;
for (const FString& EntryPoint : EntryPoints)
{
// Entry point parameters are currently not included in the partial mangling, therefore partial substring match is used here.
if (bEntryPointsAreMangled && FCStringAnsi::Strstr(FunctionDesc.Name, TCHAR_TO_ANSI(*EntryPoint)))
{
bAddFunctionEntryPoint = true;
break;
}
else if (!bEntryPointsAreMangled && FunctionDesc.Name == EntryPoint)
{
bAddFunctionEntryPoint = true;
break;
}
}
if (bAddFunctionEntryPoint)
{
// Note: calling ExtractParameterMapFromD3DShader multiple times merges the reflection data for multiple functions
ExtractParameterMapFromD3DShader<ID3D12FunctionReflection, D3D12_FUNCTION_DESC, D3D12_SHADER_INPUT_BIND_DESC,
ID3D12ShaderReflectionConstantBuffer, D3D12_SHADER_BUFFER_DESC,
ID3D12ShaderReflectionVariable, D3D12_SHADER_VARIABLE_DESC>(
Input,
ShaderParameterParser,
AutoBindingSpace,
FunctionReflection,
FunctionDesc,
CompileData,
Output);
NumFoundEntryPoints++;
}
}
// @todo - working around DXC issue https://github.com/microsoft/DirectXShaderCompiler/issues/4715
if (LibraryDesc.FunctionCount > 0)
{
if (CompileData.bBindlessEnabled)
{
ShaderRequiresFlags |= D3D_SHADER_REQUIRES_RESOURCE_DESCRIPTOR_HEAP_INDEXING;
ShaderRequiresFlags |= D3D_SHADER_REQUIRES_SAMPLER_DESCRIPTOR_HEAP_INDEXING;
}
}
if (NumFoundEntryPoints == EntryPoints.Num())
{
Output.bSucceeded = true;
bool bGlobalUniformBufferAllowed = false;
if (CompileData.bGlobalUniformBufferUsed && !IsGlobalConstantBufferSupported(Input.Target))
{
const TCHAR* ShaderFrequencyString = GetShaderFrequencyString(Input.Target.GetFrequency(), false);
FString ErrorString = FString::Printf(TEXT("Global uniform buffer cannot be used in a %s shader."), ShaderFrequencyString);
uint32 NumLooseParameters = 0;
for (const auto& It : Output.ParameterMap.ParameterMap)
{
if (It.Value.Type == EShaderParameterType::LooseData)
{
NumLooseParameters++;
}
}
if (NumLooseParameters)
{
ErrorString += TEXT(" Global parameters: ");
uint32 ParameterIndex = 0;
for (const auto& It : Output.ParameterMap.ParameterMap)
{
if (It.Value.Type == EShaderParameterType::LooseData)
{
--NumLooseParameters;
ErrorString += FString::Printf(TEXT("%s%s"), *It.Key, NumLooseParameters ? TEXT(", ") : TEXT("."));
}
}
}
FilteredErrors.Add(ErrorString);
Output.bSucceeded = false;
}
}
else
{
UE_LOG(LogD3DShaderCompiler, Fatal, TEXT("Failed to find required points in the shader library."));
Output.bSucceeded = false;
}
}
else
{
Output.bSucceeded = true;
TRefCountPtr<ID3D12ShaderReflection> ShaderReflection;
VERIFYHRESULT(Utils->CreateReflection(&ReflBuffer, IID_PPV_ARGS(ShaderReflection.GetInitReference())));
D3D12_SHADER_DESC ShaderDesc = {};
ShaderReflection->GetDesc(&ShaderDesc);
ShaderRequiresFlags = ShaderReflection->GetRequiresFlags();
ExtractParameterMapFromD3DShader<ID3D12ShaderReflection, D3D12_SHADER_DESC, D3D12_SHADER_INPUT_BIND_DESC,
ID3D12ShaderReflectionConstantBuffer, D3D12_SHADER_BUFFER_DESC,
ID3D12ShaderReflectionVariable, D3D12_SHADER_VARIABLE_DESC>(
Input,
ShaderParameterParser,
AutoBindingSpace,
ShaderReflection,
ShaderDesc,
CompileData,
Output
);
}
if (!ValidateResourceCounts(CompileData, FilteredErrors))
{
Output.bSucceeded = false;
}
FShaderCodePackedResourceCounts PackedResourceCounts{};
if (Output.bSucceeded)
{
PackedResourceCounts = InitPackedResourceCounts(CompileData);
if (Input.Environment.CompilerFlags.Contains(CFLAG_RootConstants))
{
PackedResourceCounts.UsageFlags |= EShaderResourceUsageFlags::RootConstants;
}
if (bHasNoDerivativeOps)
{
PackedResourceCounts.UsageFlags |= EShaderResourceUsageFlags::NoDerivativeOps;
}
if (Input.Environment.CompilerFlags.Contains(CFLAG_ShaderBundle))
{
PackedResourceCounts.UsageFlags |= EShaderResourceUsageFlags::ShaderBundle;
}
Output.bSucceeded = UE::ShaderCompilerCommon::ValidatePackedResourceCounts(Output, PackedResourceCounts);
// Return code reflection if requested for shader analysis
if (Input.Environment.CompilerFlags.Contains(CFLAG_OutputAnalysisArtifacts))
{
FGenericShaderStat ShaderCodeReflection;
if (CrossCompiler::FShaderConductorContext::Disassemble(CrossCompiler::EShaderConductorIR::Dxil, ShaderBlob->GetBufferPointer(), ShaderBlob->GetBufferSize(), ShaderCodeReflection))
{
Output.ShaderStatistics.Add(MoveTemp(ShaderCodeReflection));
}
}
}
// Save results if compilation and reflection succeeded
if (Output.bSucceeded)
{
uint32 RayTracingPayloadType = 0;
uint32 RayTracingPayloadSize = 0;
if (bIsRayTracingShader)
{
bool bArgFound = Input.Environment.GetCompileArgument(TEXT("RT_PAYLOAD_TYPE"), RayTracingPayloadType);
checkf(bArgFound, TEXT("Ray tracing shaders must provide a payload type as this information is required for offline RTPSO compilation. Check that FShaderType::ModifyCompilationEnvironment correctly set this value."));
bArgFound = Input.Environment.GetCompileArgument(TEXT("RT_PAYLOAD_MAX_SIZE"), RayTracingPayloadSize);
checkf(bArgFound, TEXT("Ray tracing shaders must provide a payload size as this information is required for offline RTPSO compilation. Check that FShaderType::ModifyCompilationEnvironment correctly set this value."));
}
auto PostSRTWriterCallback = [&](FMemoryWriter& Ar)
{
if (bIsRayTracingShader)
{
Ar << RayEntryPoint;
Ar << RayAnyHitEntryPoint;
Ar << RayIntersectionEntryPoint;
Ar << RayTracingPayloadType;
Ar << RayTracingPayloadSize;
}
};
auto AddOptionalDataCallback = [&](FShaderCode& ShaderCode)
{
FShaderCodeFeatures CodeFeatures;
if ((ShaderRequiresFlags & D3D_SHADER_REQUIRES_WAVE_OPS) != 0)
{
EnumAddFlags(CodeFeatures.CodeFeatures, EShaderCodeFeatures::WaveOps);
}
if ((ShaderRequiresFlags & D3D_SHADER_REQUIRES_NATIVE_16BIT_OPS) != 0)
{
EnumAddFlags(CodeFeatures.CodeFeatures, EShaderCodeFeatures::SixteenBitTypes);
}
if ((ShaderRequiresFlags & D3D_SHADER_REQUIRES_TYPED_UAV_LOAD_ADDITIONAL_FORMATS) != 0)
{
EnumAddFlags(CodeFeatures.CodeFeatures, EShaderCodeFeatures::TypedUAVLoadsExtended);
}
if ((ShaderRequiresFlags & (D3D_SHADER_REQUIRES_ATOMIC_INT64_ON_TYPED_RESOURCE| D3D_SHADER_REQUIRES_ATOMIC_INT64_ON_GROUP_SHARED)) != 0)
{
EnumAddFlags(CodeFeatures.CodeFeatures, EShaderCodeFeatures::Atomic64);
}
if ((ShaderRequiresFlags & D3D_SHADER_REQUIRES_RESOURCE_DESCRIPTOR_HEAP_INDEXING) != 0)
{
EnumAddFlags(CodeFeatures.CodeFeatures, EShaderCodeFeatures::BindlessResources);
}
if ((ShaderRequiresFlags & D3D_SHADER_REQUIRES_SAMPLER_DESCRIPTOR_HEAP_INDEXING) != 0)
{
EnumAddFlags(CodeFeatures.CodeFeatures, EShaderCodeFeatures::BindlessSamplers);
}
if ((ShaderRequiresFlags & D3D_SHADER_REQUIRES_STENCIL_REF) != 0)
{
EnumAddFlags(CodeFeatures.CodeFeatures, EShaderCodeFeatures::StencilRef);
}
if ((ShaderRequiresFlags & D3D_SHADER_REQUIRES_BARYCENTRICS) != 0)
{
EnumAddFlags(CodeFeatures.CodeFeatures, EShaderCodeFeatures::BarycentricsSemantic);
}
// We only need this to appear when using a DXC shader
ShaderCode.AddOptionalData<FShaderCodeFeatures>(CodeFeatures);
if (ShaderModel >= ED3DShaderModel::SM6_0)
{
uint8 IsSM6 = 1;
ShaderCode.AddOptionalData(EShaderOptionalDataKey::ShaderModel6, &IsSM6, 1);
}
// Store EntryPointName for possible use in work graph state object creation.
if (bIsWorkGraphShader || Input.Target.GetFrequency() == SF_Pixel)
{
TArray<uint8> NameData;
FMemoryWriter NameWriter(NameData);
FString Name = Input.EntryPointName;
NameWriter << Name;
ShaderCode.AddOptionalData(EShaderOptionalDataKey::EntryPoint, NameData.GetData(), NameData.Num());
}
};
// Return a fraction of the number of instructions as DXIL is more verbose than DXBC.
// Ratio 119:307 was estimated by gathering average instruction count for D3D11 and D3D12 shaders in ShooterGame with result being ~ 357:921.
constexpr uint32 DxbcToDxilInstructionRatio[2] = { 119, 307 };
CompileData.NumInstructions = CompileData.NumInstructions * DxbcToDxilInstructionRatio[0] / DxbcToDxilInstructionRatio[1];
//#todo-rco: Should compress ShaderCode?
GenerateFinalOutput(
ShaderBlob,
Input,
ShaderModel,
bProcessingSecondTime,
CompileData,
PackedResourceCounts,
Output,
PostSRTWriterCallback,
AddOptionalDataCallback
);
}
}
else
{
// If we failed and didn't get any error messages back from the compile call try and get a system error message.
if (FilteredErrors.Num() == 0)
{
TCHAR ErrorMsg[1024];
FPlatformMisc::GetSystemErrorMessage(ErrorMsg, UE_ARRAY_COUNT(ErrorMsg), (int)D3DCompileToDxilResult);
const bool bKnownError = ErrorMsg[0] != TEXT('\0');
FString ErrorString = FString::Printf(TEXT("D3DCompileToDxil failed. Error code: %s (0x%08X)."), bKnownError ? ErrorMsg : TEXT("Unknown error"), (int)D3DCompileToDxilResult);
FilteredErrors.Add(ErrorString);
}
}
// Move intermediate filtered errors into compiler context for unification.
CrossCompiler::FShaderConductorContext::ConvertCompileErrors(MoveTemp(FilteredErrors), Output.Errors);
return Output.bSucceeded;
}
#undef VERIFYHRESULT