TECHARTNOMAD | TECHARTFLOWIO.COM

UNREAL ENGINE

언리얼 엔진 5.4 - DirectX12 → DirectX11 자동 Fallback 기능 분석

jplee 2025. 9. 15. 12:55

작성일: 2025년 9월 12일

📋 개요

언리얼 엔진 5.4에는 DirectX12가 지원되지 않는 하드웨어 환경에서 DirectX11로 자동 전환하는 강력한 호환성 시스템이 구현되어 있습니다. 이 문서는 해당 기능의 내부 구현을 상세히 분석합니다.

🏗️ 아키텍처 구조

핵심 컴포넌트

RHI (Render Hardware Interface)
├── WindowsDynamicRHI.cpp      # 메인 RHI 선택 및 fallback 로직
├── D3D12RHI Module            # DirectX12 구현체
│   └── WindowsD3D12Device.cpp # DX12 하드웨어 지원 검사
└── D3D11RHI Module            # DirectX11 구현체
    └── WindowsD3D11Device.cpp # DX11 하드웨어 지원 검사

📁 주요 구현 파일

파일 역할
Engine/Source/Runtime/RHI/Private/Windows/WindowsDynamicRHI.cpp RHI 선택, fallback 메커니즘
Engine/Source/Runtime/D3D12RHI/Private/Windows/WindowsD3D12Device.cpp DirectX12 하드웨어 지원 검사
Engine/Source/Runtime/Windows/D3D11RHI/Private/Windows/WindowsD3D11Device.cpp DirectX11 하드웨어 지원 검사

🔄 Fallback 메커니즘

1. RHI 우선순위 정의

// WindowsDynamicRHI.cpp:76-82
const EWindowsRHI GRHISearchOrder[] =
{
    EWindowsRHI::D3D12,  // 1순위: DirectX12
    EWindowsRHI::D3D11,  // 2순위: DirectX11 (fallback 대상)
    EWindowsRHI::Vulkan, // 3순위: Vulkan
    EWindowsRHI::OpenGL, // 4순위: OpenGL
};

엔진은 기본적으로 DirectX12를 우선 시도하고, 실패 시 DirectX11로 자동 전환합니다.

2. 핵심 Fallback 함수

// WindowsDynamicRHI.cpp:875-926
static bool HandleUnsupportedRHI(EWindowsRHI& WindowsRHI, 
                                 ERHIFeatureLevel::Type& FeatureLevel, 
                                 TOptional<EWindowsRHI> ForcedRHI, 
                                 const FParsedWindowsDynamicRHIConfig& Config)
{
    // 강제 지정된 RHI가 있고 DirectX12인 경우 에러 처리
    if (ForcedRHI && ForcedRHI == EWindowsRHI::D3D12)
    {
        // 사용자에게 오류 메시지 표시 후 종료
        FMessageDialog::Open(EAppMsgType::Ok, 
            LOCTEXT("RequiredDX12", "DirectX 12 is not supported on your system. "
                    "Try running without the -dx12 or -d3d12 command line argument."));
        return false;
    }

    // DirectX12에서 DirectX11로 자동 전환
    if (WindowsRHI == EWindowsRHI::D3D12)
    {
        if (TOptional<ERHIFeatureLevel::Type> D3D11FeatureLevel = 
            Config.GetHighestSupportedFeatureLevel(EWindowsRHI::D3D11))
        {
            WindowsRHI = EWindowsRHI::D3D11;                    // RHI 변경
            FeatureLevel = D3D11FeatureLevel.GetValue();       // Feature Level 조정
            return true;                                        // Fallback 성공
        }
    }

    return false; // Fallback 실패
}

3. 메인 로드 프로세스

// WindowsDynamicRHI.cpp:973-1011
do
{
    // RHI 모듈 로드 시도
    IDynamicRHIModule* DynamicRHIModule = 
        FModuleManager::LoadModulePtr<IDynamicRHIModule>(ModuleName);

    // 하드웨어 지원 여부 확인
    if (DynamicRHIModule && DynamicRHIModule->IsSupported(DesiredFeatureLevel))
    {
        UE_LOG(LogRHI, Log, TEXT("RHI %s with Feature Level %s is supported and will be used."), 
               GetLogName(ChosenRHI), GetLogName(DesiredFeatureLevel));
        return DynamicRHIModule; // 성공
    }

    // 현재 설정 백업
    const EWindowsRHI PreviousRHI = ChosenRHI;
    const ERHIFeatureLevel::Type PreviousFeatureLevel = DesiredFeatureLevel;

    // Fallback 시도
    bTryWithNewConfig = HandleUnsupportedRHI(ChosenRHI, DesiredFeatureLevel, 
                                           ForcedRHI, Config);

    if (bTryWithNewConfig)
    {
        UE_LOG(LogRHI, Log, 
               TEXT("RHI %s with Feature Level %s is not supported, "
                    "attempting to fall back to RHI %s with Feature Level %s"),
               GetLogName(PreviousRHI), GetLogName(PreviousFeatureLevel),
               GetLogName(ChosenRHI), GetLogName(DesiredFeatureLevel));
    }
} while (bTryWithNewConfig); // Fallback 성공 시 재시도

🔍 하드웨어 지원 검사

DirectX12 지원 검사

// WindowsD3D12Device.cpp:673-729
bool FD3D12DynamicRHIModule::IsSupported(ERHIFeatureLevel::Type RequestedFeatureLevel)
{
    // 1. Windows 버전 검사 (최소 Windows 10 1703 필요)
    if (!FPlatformMisc::VerifyWindowsVersion(10, 0, 15063))
    {
        UE_LOG(LogD3D12RHI, Warning, 
               TEXT("Missing full support for Direct3D 12. "
                    "Update to Windows 1703 or newer for D3D12 support."));
        return false;
    }

    // 2. 어댑터 검색
    if (ChosenAdapters.Num() == 0)
    {
        FindAdapter();
    }

    // 3. 유효한 어댑터 확인
    if (ChosenAdapters.Num() == 0)
    {
        UE_LOG(LogD3D12RHI, Log, TEXT("No adapters were found."));
        return false;
    }

    const FD3D12Adapter* Adapter = ChosenAdapters[0].Get();
    if (!Adapter || !Adapter->GetDesc().IsValid())
    {
        UE_LOG(LogD3D12RHI, Log, TEXT("Adapter was not found"));
        return false;
    }

    // 4. 어댑터 차단 여부 확인
    if (IsAdapterBlocked(Adapter))
    {
        UE_LOG(LogD3D12RHI, Log, TEXT("Adapter was blocked by RHI.BlockIHVD3D12"));
        return false;
    }

    // 5. Feature Level 지원 여부 확인
    if (!IsAdapterSupported(Adapter, RequestedFeatureLevel))
    {
        UE_LOG(LogD3D12RHI, Log,
               TEXT("Adapter only supports up to Feature Level '%s', "
                    "requested Feature Level was '%s'"),
               *SupportedFeatureLevelName, *RequestedFeatureLevelName);
        return false;
    }

    return true; // 모든 검사 통과
}

DirectX11 지원 검사

// WindowsD3D11Device.cpp:631-642
bool FD3D11DynamicRHIModule::IsSupported()
{
    // 어댑터 검색 (필요시)
    if(!ChosenAdapter.IsValid())
    {
        FindAdapter();
    }

    // DirectX 11.0 이상 Feature Level 지원 필수
    return ChosenAdapter.IsValid() 
        && ChosenAdapter.MaxSupportedFeatureLevel >= D3D_FEATURE_LEVEL_11_0;
}

하드웨어 디바이스 테스트

// WindowsD3D12Device.cpp:479-504
static bool SafeTestD3D12CreateDevice(IDXGIAdapter* Adapter, 
                                     D3D_FEATURE_LEVEL MinFeatureLevel, 
                                     FD3D12DeviceBasicInfo& OutInfo)
{
    __try
    {
        ID3D12Device* Device = nullptr;

        // DirectX12 디바이스 생성 시도
        const HRESULT D3D12CreateDeviceResult = 
            D3D12CreateDevice(Adapter, MinFeatureLevel, IID_PPV_ARGS(&Device));

        if (SUCCEEDED(D3D12CreateDeviceResult))
        {
            // 지원되는 최고 Feature Level 확인
            OutInfo.MaxFeatureLevel = FindHighestFeatureLevel(Device, MinFeatureLevel);
            OutInfo.MaxShaderModel = FindHighestShaderModel(Device);
            GetResourceTiers(Device, OutInfo.ResourceBindingTier, OutInfo.ResourceHeapTier);

            // Wave Operations 및 64비트 Atomic 지원 확인
            OutInfo.bSupportsWaveOps = GetSupportsWaveOps(Device);
            OutInfo.bSupportsAtomic64 = GetSupportsAtomic64(Adapter, Device);

            Device->Release();
            return true; // 성공
        }
        else
        {
            UE_LOG(LogD3D12RHI, Log, 
                   TEXT("D3D12CreateDevice failed with code 0x%08X"), 
                   static_cast<int32>(D3D12CreateDeviceResult));
        }
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        // 예외 발생 시 실패 처리
    }

    return false; // 실패
}

⚠️ Fallback 발생 조건

시스템 레벨 조건

  1. Windows 버전 부족
    • Windows 10 버전 1703 (빌드 15063) 미만
    • DirectX12 API 지원 부족
  2. 하드웨어 미지원
    • GPU가 DirectX12 Feature Level 미지원
    • 필요한 하드웨어 기능 부재 (Wave Operations, 64-bit Atomics 등)
  3. 드라이버 문제
    • DirectX12 디바이스 생성 실패
    • 구형 그래픽 드라이버

설정 레벨 조건

  1. 어댑터 차단
    • RHI.BlockIHVD3D12 콘솔 변수에 의한 차단
    • 특정 GPU 벤더/모델 차단 목록
  2. Feature Level 불일치
    • 요청된 Feature Level을 하드웨어가 지원하지 않음
    • SM6, SM5 등 셰이더 모델 호환성 문제

📊 로그 메시지 분석

정상 동작 시

LogRHI: Using Default RHI: D3D12
LogRHI: Loading RHI module D3D12RHI
LogRHI: Checking if RHI D3D12 with Feature Level SM5 is supported by your system.
LogRHI: RHI D3D12 with Feature Level SM5 is supported and will be used.

Fallback 발생 시

LogRHI: Using Default RHI: D3D12
LogRHI: Loading RHI module D3D12RHI
LogRHI: Checking if RHI D3D12 with Feature Level SM5 is supported by your system.
LogD3D12RHI: Warning: Missing full support for Direct3D 12. Update to Windows 1703 or newer for D3D12 support.
LogRHI: RHI D3D12 with Feature Level SM5 is not supported on your system, attempting to fall back to RHI D3D11 with Feature Level SM5
LogRHI: Loading RHI module D3D11RHI
LogRHI: RHI D3D11 with Feature Level SM5 is supported and will be used.

🎛️ 사용자 제어 옵션

명령줄 인수

  • -dx12 또는 -d3d12: DirectX12 강제 사용 (fallback 비활성화)
  • -dx11 또는 -d3d11: DirectX11 강제 사용
  • -sm5, -sm6: 특정 셰이더 모델 강제 사용

설정 파일

[/Script/WindowsTargetPlatform.WindowsTargetSettings]
DefaultGraphicsRHI=DefaultGraphicsRHI_DX12  ; 기본값
; DefaultGraphicsRHI_DX11, DefaultGraphicsRHI_Vulkan 등 선택 가능

[D3DRHIPreference]
PreferredRHI=dx12  ; 게임 내 RHI 선호도
PreferredFeatureLevel=sm5  ; Feature Level 선호도

🔧 개발자를 위한 참고사항

Feature Level 매핑

DirectX Feature Level 언리얼 RHI Feature Level 셰이더 모델
D3D_FEATURE_LEVEL_11_0 ERHIFeatureLevel::SM5 SM5.0
D3D_FEATURE_LEVEL_12_0 ERHIFeatureLevel::SM5 SM5.0
D3D_FEATURE_LEVEL_12_1 ERHIFeatureLevel::SM6 SM6.0+

커스텀 Fallback 로직 구현

// 커스텀 RHI 선택 로직 예제
EWindowsRHI ChooseCustomRHI(const FParsedWindowsDynamicRHIConfig& Config)
{
    // 1. 프로젝트 설정 확인
    if (TOptional<EWindowsRHI> ConfigDefault = Config.DefaultRHI)
    {
        return ConfigDefault.GetValue();
    }

    // 2. 하드웨어 기반 자동 선택
    for (EWindowsRHI RHI : GRHISearchOrder)
    {
        if (TOptional<ERHIFeatureLevel::Type> HighestFL = 
            ChooseDefaultFeatureLevel(RHI, Config))
        {
            return RHI;
        }
    }

    // 3. 기본값으로 DirectX11 사용
    return EWindowsRHI::D3D11;
}

📈 성능 및 호환성 고려사항

DirectX12 vs DirectX11 성능 차이

기능 DirectX12 DirectX11
CPU 오버헤드 낮음 높음
멀티스레딩 우수 제한적
메모리 관리 수동 (정밀) 자동
호환성 Windows 10+ Windows 7+

최적화 권장사항

  1. 프로젝트 설정
    • 타겟 플랫폼에 맞는 셰이더 컴파일
    • Feature Level별 에셋 최적화
  2. 런타임 감지
  3. // 현재 사용 중인 RHI 확인 const TCHAR* CurrentRHI = GetSelectedDynamicRHIModuleName(false); if (FCString::Strcmp(CurrentRHI, TEXT("D3D12RHI")) == 0) { // DirectX12 특화 최적화 }

🚀 결론

언리얼 엔진 5.4의 DirectX12 → DirectX11 자동 fallback 시스템은:

  • 완전 자동화: 사용자 개입 없이 투명한 전환
  • 다단계 검증: Windows 버전, 하드웨어, 드라이버 모든 레벨 검사
  • 상세한 로깅: 디버깅과 문제 해결을 위한 풍부한 로그
  • 유연한 설정: 개발자와 사용자 모두를 위한 제어 옵션
  • 안정성 보장: 예외 처리와 안전한 실패 처리

이 시스템 덕분에 언리얼 엔진으로 개발된 게임은 다양한 하드웨어 환경에서 안정적으로 실행될 수 있으며, 개발자는 호환성 문제에 대한 걱정 없이 최신 그래픽 기술을 활용할 수 있습니다.