4.2 CPU와 GPU의 상호작용

  • 최적의 성능을 얻기 위해 CPU, GPU를 최대한 사용 <- 동기화를 최소화

4.2.1 명령 대기열과 명령 목룍

  • GPU명령 대기열(command queue) 1개 보유
  • CPU는 그리기 명령이 담긴 명령 목록(command list)를 D3D API를 통해서 명령 대기열에 제출
    • 명령 대기열에 제출 했다고, GPU가 즉시 실행 X
    • ID3D12CommandQueue : 명령 대기열을 대표하는 인터페이스
      1) D3D12_COMMAND_QUEUE_DESC 구조체를 채움
      2) ID3D12Device::CreateCommandQueue를 호출
        Microsoft::WRL::ComPtr<ID3D12CommandQueue> mCommandQueue;
        D3D12_COMMAND_QUEUE_DESC queueDesc = {};
        queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; 
        queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; 
        ThrowIfFailed(md3dDevice->CreateCommandQueue(
            &queueDesc, IID_PPV_ARGS(&mCommandQueue)));
      • #define IID_PPV_ARGS(ppType) __uuidof(**(ppType)), IID_PPV_ARGS_Helper(ppType)
        • COM 인터페이스에서 특정 인터페이스를 요청할 떄 사용
        • COM에서는 인터페이스를 식별하고 사용하기 위해 GUID(Globally Unique Identifier)
        • __uuidof(**(ppType)) : (**(ppType))의 가리키는 인터페이스 타입의 GUID를 반환
        • IID_PPV_ARGS_Helper(ppType) : ppType에 대해 추가적인 처리를 수행하는 도우미 매크로, *ppType을 `void`로 캐스팅***
      • ID3D12CommandQueue::ExecuteCommandLists : 명령 목록 요소들을 대기열에 추가/제출 <- CPU의 실행 차단 x
          void ID3D12CommandQueue::ExecuteCommandLists(
              UINT Count, // 배열에 있는 명령 목록들의 개수
              ID3D12CommandList *const *ppCommandLists // 명령 목록들의 배열 첫 원소 가리키는 포인터);
          )
      • CommandList에 대해
        • ID3D12GraphicsCommandList : ID3D12CommandList를 상속하는 실제 그래픽 작업을 위한 명령 목록 인터페이스
        • "CommandQueue"에 추가한 뒤에는 지워도 됨.
          • 실제 추가되는 명령은 "CommandAllocator"를 참조하기 때문
        • 뷰포트 설정 및 렌더 대상 뷰 지우고, 그리기 호출 ( 즉시 실행 x)
            // mCommandList는 ID3D12CommandList 포인터
            mCommandList->RSSetViewports(1, &mScreenViewport);
            mCommandList->ClearRederTargetView(mBackBufferView, Colors::LightSteelBlue, 0, nullptr);
            mCommandList->DrawIndexedInstanced(36, 1, 0, 0, 0);
        • 명령의 기록이 끝나면, 닫기 : mCommandList->Close();
        • ID3D12CommandAllocator형식의 메모리 할당자가 1 개 연관
          • '명령 목록'에 추가된 명령이 저장되는 곳
        • CreateCommandList : 생성
            HRESULT ID3D12Device::CreateCommandList(
                UNIT nodeMask, // GPU가 하나면 0, 아니면 연관시킬 어댑터 노드 지어 bit mask 값
                D3D12_COMMAND_LIST_TYPE type, // 아래 type 유형 참조
                ID3D12CommandAllocator *pCommandAllocator, // 할당자 종류는 목록 종류와 일치
                ID3D12PipelineState *pInitialState // 번들 및 초기화, 실제 그리기 명령 없으면 널 지정 가능
                REFIID riid,
                void **ppCommandList); // 생성된 명령 목록 가리키는 포인터
          • ID3D12Device::GetNodeCount : GPU 어댑터 노드 개수
        • Reset :
          • 초기화, 번거로움 없이 메모리 재사용
            • size는 0이 되어도, capacity는 그대로
          • 필요 렌더링 명령 모두 GPU 제출 후, 다음 프레임을 위해 재사용 용도 
          • Queue에 제출된 명령에는 영향x
            • 할당자의 메모리에 여전히 존재
          •  
          • HRESULT ID3D12CommandList::Reset( ID3D12CommandAllocator *pAllocator, ID3D12PipelineState *pInitialState);
        • 명령 메모리 할당자
            HRESULT ID3D12Device::CreateCommandAllocator(
                D3D!@_COMMAND_LIST_TYPE type,
                REFIID riid,
                void **ppCommandAllocator);
          • type : 할당자와 연관 시킬 수 있는 명령 목록 종류
            • D3D12_COMMAND_LIST_TYPE_DIRECT : GPU 직접 실행 명령 목록
            • D3D12_COMMAND_LIST_TYPE_BUNDLE :
              • CPU 부담을 줄이기 위한 일련의 명령들을 '묶음' 단위로 최적화
              • 렌더링 도중 실행 최적화를 위해, 번들 명령 전처리
              • 성능상 이득이 있는 경우만 사용
          • riid : ID3D12CommandAllocator인터페이스의 COM ID
          • ppCommandAllocator : 생성된 명령 할당자를 가리키는 포인터
          • 목록 생성 || 재설정 시, "열린" 상태가 됨 주의

4.2.2 CPU/GPU 동기화


  • 1) CPU가 'C' 명려을 GPU에 제출
    2) GPU 명령 수행중, R 업뎃 - CPU 'Resource'에 p1 저장
    3) GPU 명령 수행중 , R 업뎃- CPU 'Resource'에 p2 로 저장 변경
    • GPU 명령 수행중에 참조하는 R의 값이 변하는 문제가 발생 <- 비동기로 돌아가서
  • flush(방출한다) : 대기열의 모든 명령을 처리하는 것
    • ID3D12Fence가 필요 : GPU, CPU 동기화 수단
      • 울타리 생성
          HRESULT ID3D12Device::CreateFence(
              UINT64 InitialValue,
              D3D12_FENCE_FLAGS Flags,
              REFIID riid;
              void **ppFence);
      • 울타리를 이용한 명령 대기열 비우기
          UINT64 mCurrentFence = 0;
          void D3DApp::FlushCommandQueue()
          {
              // Advance the fence value to mark commands up to this fence point.
              mCurrentFence++;
              // Add an instruction to the command queue to set a new fence point.
              // Because we are on the GPU timeline, the new fence point won’t be 
              // set until the GPU finishes processing all the commands prior to 
              // this Signal().
              ThrowIfFailed(mCommandQueue->Signal(mFence.Get(), mCurrentFence));
              // Wait until the GPU has completed commands up to this fence point.
              if(mFence->GetCompletedValue() < mCurrentFence) 
              {
                  HANDLE eventHandle = CreateEventEx(nullptr, false, false, EVENT_ALL_ACCESS);
                  // Fire event when GPU hits current fence. 
                  ThrowIfFailed(mFence->    SetEventOnCompletion(mCurrentFence, eventHandle));
                  // Wait until the GPU hits current fence event is fired.
                  WaitForSingleObject(eventHandle, INFINITE); 
                  CloseHandle(eventHandle);
              }
          }

4.2.3 자원 상태 전이

  • 자원 위험 상황(resource hazard) : 자원의 기록이 제대로 안된 상태에서 자료를 읽을려 할 떄 생기는 문제
  • 임의의 상태 전이를 'D3D'에게 보고하는 것은 '응용 프로그램 몫'
  • 리소스 배리어는 후면 버퍼를 `제시` ->`렌더링` 상태로 전환 (쓰기모드)
  • 리소스 배리어는 후면 버퍼를 `렌더링`->`제시` 상태로 전환 (읽기모드)
  • 자원 상태 전이 :
    • 전이 자원 장벽(transition resource barrier)들의 배열을 설정해서 지정
      • D3D12_RESOURCE_BARRIER_DESC
        struct CD3DX12_RESOURCE_BARRIER : public D3D12_RESOURCE_BARRIER
        {
          // [...] convenience methods
          static inline CD3DX12_RESOURCE_BARRIER Transition(
          _In_ ID3D12Resource* pResource,
          D3D12_RESOURCE_STATES stateBefore,
          D3D12_RESOURCE_STATES stateAfter,
          UINT subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES,
          D3D12_RESOURCE_BARRIER_FLAGS flags = D3D12_RESOURCE_BARRIER_FLAG_NONE)
          {
              CD3DX12_RESOURCE_BARRIER result;
              ZeroMemory(&result, sizeof(result));
              D3D12_RESOURCE_BARRIER &barrier = result;
              result.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
              result.Flags = flags;
              barrier.Transition.pResource = pResource;
              barrier.Transition.StateBefore = stateBefore; 
              barrier.Transition.StateAfter = stateAfter; 
              barrier.Transition.Subresource = subresource; 
              return result;
          }
        // [...] more convenience methods 
        };
      • CDIDX12_ 확장 버전은 d3dx12.h에 정의
    • 텍스처 자원 제시 상태 -> 렌더 대상 상태 전이 예시
       mCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(
           CurrentBackBuffer(),
           D3D12_RESOURCE_STATE_PRESENT, 
           D3D12_RESOURCE_STATE_RENDER_TARGET));

4.2.4 명령 목록을 이용한 다중 스레드 활용

  • 명령 목록 구축에 다중 스레드 적용 주의점
    • 명령 목록은 자유 스레드(free-threaded) 모형을 따르지 않음;
      • 여러 스레드가 같은 명령 공유x, 동시에 호출 x, 각 스레드는 자신만의 명령 목록
    • 명령 할당자도 자유 스레드X, 각 스레드는 각자 자신만의 명령 할당자를 가짐
    • 명령 대기열은 자유 스레드 모형;
      • 여러 스레드가 동시 호출 , 스레드들이 각자 자신이 생성한 명령 목록을 동시에 명령 대기열에 제출 가능
    • 성능상 이유로, 응용 프로그램은 동시 기록 가능 명령 목록들의 최대 개수를 초기화 시점에 설정해야 함

 

'프레임워크 > DirectX' 카테고리의 다른 글

Direct3D의 초기화_게임프레임워크&타이머 코드 의문점  (0) 2025.03.07
04.Direct3D의_초기화03  (1) 2025.03.04
04.Direct3D의_초기화01  (1) 2025.03.01
02.Matrix  (0) 2025.02.25
01.Vector  (0) 2025.02.25

+ Recent posts

let textNodes = document.querySelectorAll("div.tt_article_useless_p_margin.contents_style > *:not(figure):not(pre)"); textNodes.forEach(function(a) { a.innerHTML = a.innerHTML.replace(/`(.*?)`/g, '$1'); });