ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [18] - Debug Layer Diagnostics
    Graphics 2021. 7. 21. 16:55

    오류가 발생했는데 무슨 오류인지, 어디서 발생한 것인지 모르면 자살하고 싶을 수도 있습니다. 이를 막기 위해 디버그 기능을 구현할 수 있습니다. (8) C++ 3D DirectX Tutorial [Debug Layer Diagnostics] - YouTube

    지금껏 구현한 코드는 대체로 잘 동작했으나 치명적인 문제를 안고 있었습니다. 만약, 무언가를 초기화하는 함수가 하나라도 엇나갔을 경우에 알아차릴 수 있는 방법이 없다는 점입니다. 당장에 CreateDeviceSwapChain이나 Window Handle을 초기화하는 과정에 마음대로 적당한 상수를 넣으면 오류는 없지만 동작은 하지 않는 가슴 설레는 현상을 목격할 수 있습니다. 잠깐 다시 D3D11CreateDeviceAndSwapChain 함수를 다시 보겠습니다.

    지금까지 별로 사용할 일이 없었기 때문에 놓친 부분인데 이 함수는 반환으로 HRESULT라는 것을 토해냅니다. MSDN에 따르면 COM Object는 Error Handling을 할 때, HRESULT를 사용한다고 합니다. 조금 더 찾아보니 포괄적으로 그리고 전반적으로 Win32API는 어떤 함수나 서비스의 성공 여부를 HRESULT라는 값을 반환하면서 알린다고 합니다. 

    Window Exception이 있는데 뭐가 또 필요하다는 건지...

     

    사실은 이렇습니다. 원래는 DirectX SDK가 Windows SDK와 별개의 것이었습니다. 그래서, DirectX SDK의 HRESULT는 Windows SDK와 호환이 되지 않았습니다. 그래서 우린 DXError라는 별개의 라이브러리가 필요했습니다. 이것을 통해서 HRESULT를 받고 인간이 읽을 수 있는 String의 형태로 에러를 반환했습니다. 그랬던 것이 Windows SDK에 통합되면서 FormatMessage 함수가 DirectX의 HRESULT를 읽을 수 있게 되었습니다. 여기에 정보가!/

    핵심적으로 세 가지의 함수가 있습니다. 

    그래서 무슨 소리인가! 더 이상 DXERR.LIB이 필요가 없다는 뜻입니다. 단, Windows SDK 8.0 이상일 때만 적용되는 이야기 입니다. Windows 7에서 이 코드를 실행하면 안 될 수도 있다는 말입니다. 그럼에도 불구하고 여전히 위의 세 가지의 함수는 핵심적으로 사용해야만 합니다. 그래서 DXErr.h라는 Header file을 제공했지만 UNICODE 밖에 지원을 하지 않습니다. 원래는 UNICODE와 Narrow String(ANSI Code)을 모두 지원했음에도 불구하고. 그래서 누군가 Github에 ANSI를 포함해서 올렸고 그것을 다시 자잘한 버그를 고친 것이 바로 여기에 있습니다.

    코드를 조금 잘라온 것인데 dxerr.h에서 중요하게 봐야 하는 함수는 두 가지 입니다. DXGetErrorString는 어떤 에러를 대표하는 Macro의 이름을 반환해주고 DXGetErrorDescription은 그 에러가 뭔지를 상세히 설명해줍니다. 

     

     

     

    Graphics.h에 에러를 제어할 수 있는 Inner Class가 생성 되어있습니다
    Graphics.cpp에 에러를 편하게 관리하기 위해 몇 가지의 Macro를 정의한 모습입니다

    두 번째 표현을 보면 hrcall이 오면 해당 식의 실패 여부를 확인하여 Exception을 던져줍니다. 이제 해야 할 일은 여태 작성한 코드들의 예외나 오류를 탐지할 수 있도록 감싸 주는 일입니다.

    이런 식으로 코드를 감싸줍니다

    그러면 해당 함수나 표현식의 오류를 찾고 Throw를 해줄 것입니다. 다른 코드들도 예외 없이 해줍니다. 물론 HRESULT가 필요합니다. 위의 Macro를 살펴보면 hr이라고 표현된 부분이 있는데 HRESULT입니다. 따라서 이 Macro를 사용하려면 동일한 Scope 안에 HRESULT가 필요하다는 뜻입니다.

    End Frame은 조금 특별한 Case의 예외 사항이 존재합니다. DXGI_ERROR_DEVICE_REMOVED라는 예외입니다. 물론 해당 값 역시 HRESULT로 분석이 되기 때문에 만들어 둔 Macro를 사용하면 됩니다. 그런데 이 에러는 Device에서 검출할 수 있기 때문에 pDevice의 함수를 사용해야 합니다.

    그래픽 카드의 드라이버 충돌이나 GPU의 오버 클럭에 문제가 생겼을 때 이런 에러가 발생합니다. Window Class에도 새로운 Exception이 추가되었는데 다음과 같습니다. 그럼 인위적으로 오류를 발생 시켜보는 편이 좋겠습니다.

    OutputWindow를 엉망으로 설정하면 
    이런 오류를 받습니다.

    그런데 Line  67은 인위적으로 오류를 발생시킨 위치가 아닙니다.

    보는 것처럼 다른 위치입니다. C++이 늘 그러하듯 그냥 시키는 대로 하고 엉터리로 만든 값을 전달 받은 이 함수에서 오류가 생긴 겁니다. 

    이걸 해결하는 간단한 방법이 있습니다. 일단 좌측 코드는 완성이 된 코드이긴 합니다. swapCrateFlags가 기존엔 0이었을 겁니다. 이 부분을 D3D_CREATE_DEVICE_DEBUG값을 넣어주면 Console Output에 어떤 문제가 있는지 더 상세하게 설명해줍니다.

     

     

    DxgiInfoManager

    Error Message를 더 깨끗하게 관리할 수 있도록 만든 Class 입니다. 상당히 복잡하게 구성되어 있습니다. 천천히 따라갑니다. 해당 에러에 좀 더 Programmatically하게 접근하기 위해 다음과 같은 구조체를 만들었습니다.

    그리고 다음과 같이 정의 되어 있습니다.

    디버그를 위한 Interface를 만드는 함수입니다

    일단 에러 정보에 접근하기 위해서는 DXGIGetDebugInterface 함수가 필요합니다. 그런데 이 함수는 별도의 dll인 dxgidebug.dll에 존재합니다. 그래서 그 dll을 load하고 GetProcAddress함수로 해당 함수의 dll 내의 주소를 받으면 마침내 그 함수를 호출할 수 있게 됩니다. 

    여기에 관련된 개념 중 Function signature을 정의하는 부분이 있는데 WINAPI*의 존재 때문에 구조가 좀 이해가 안 가는 부분이 있습니다. HRESULT가 반환, DXGIGetDebugInterface가 함수 이름, REFIID, void**가 Parameter라면 도대체 WINAPI*는 뭐하는 놈일까요?

     

    실질적으로 Message를 받는 함수입니다.

    기본적으로 Queue 내부를 돌면서 Message를 찾습니다. 그런데 queue를 프로그램이 실행되는 내내 뒤지면서 메시지를 출력하라고 하면 모든 메시지들이 전부 출력될 것입니다. 따라서 for 문의 조건을 보면 index를 next부터 end까지로 제한을 했는데 next는 다음과 같은 함수를 통해 값을 설정합니다.

    이러면 queue의 메시지 개수를 next에 넣으면 다음 index에 존재하는 message부터 queue의 끝까지를 탐색하며 출력할 수 있게 됩니다. 모든 디버그 메시지를 받고 싶으니까 옵션으로 DXGI_DEBUG_ALL을 넣었습니다. 필요에 따라 바꾸면 됩니다.

    보는 바와 같이 Graphics.h에 DxgiInfoManager을 Include해주고 HrException에 약간의 수정을 가합니다. 

    추가적인 정보를 위한 string도 있고 에러의 정보를 받을 GetErrorInfo함수도 보입니다. 

    그런데 디버그 모드일때만 DxgiInfoManager을 사용하도록 만들 수 있습니다. 

     

    이러면 Release 모드일 때는 InfoManager가 Graphics 오브젝트의 일부가 되지 않을 겁니다. 그럼 다른 Macro도 손을 봐야 합니다. 왜냐하면 Debug mode인지 Release mode인지에 따라서 다른 행동을 정의해야 하기 때문입니다. 

    그러면 Debug mode이면 infoManager가 추가될 것이고 Dxgi의 message queue에서 현재 실행하고 있는 코드의 디버그 메시지를 받아야 하니까 Set함수를 호출해서 next의 값을 계속 갱신해야 합니다.

    상단이 Debug mode 하단이 Release mode

    이 그림에서 swapCreateFlags가 사용된 이유도 같습니다. 이 스위치를 

    #ifndef NDEBUG

    swapCreateFlags |= D3D11_CREATE_DEVICE_DEBUG;

    #endif

    로 정의한 겁니다.

     

    Chili의 멋진 D3D Hardware 시리즈 : (9) C++ 3D DirectX Tutorial [Debug Layer Diagnostics] - YouTube

    'Graphics' 카테고리의 다른 글

    [20] - My First Triangle - 1  (0) 2021.07.22
    [19] - ComPtr and Smart Pointer  (0) 2021.07.21
    [17] - Device Init  (0) 2021.07.20
    [16] - Swap Chain  (0) 2021.07.19
    [15] - COM object  (0) 2021.07.19
Designed by Tistory.