string_view 특징

  • 읽기 전용의 문자열 전달타입
  • 참조를 통해서 전달하며, 포인터와 길이만 관리하기에 매우 가볍고 빠름
  • 부분 문자열 처리에 복사 오버헤드가 없어 성능이 뛰어남
  • 참조한 문자열을 수정하면, string_view의 값도 변경
  • 참조한 문자열이 해제 되면 댕글링 이슈 발생
  • 로그 처리, 문자역 분석등에 유용

std::string_viewstd::string&의 차이점**

특징 std::string_view std::string&
목적 읽기 전용 참조로 사용. 문자열을 복사하지 않음. 문자열을 복사하지 않고 수정 가능.
메모리 관리 원본 문자열의 데이터만 참조(포인터 + 길이). 원본 문자열을 직접 참조.
성능 매우 가벼움. 복사 없이 빠르게 처리 가능. 가벼움. 하지만 수정 시 추가 작업이 발생.
유연성 부분 문자열을 처리하거나 null 종료 문자열을 요구하지 않음. 전체 문자열만 참조 가능.
수명 관리 원본 문자열이 파괴되면 참조가 유효하지 않음. 원본 문자열이 파괴되면 참조가 유효하지 않음.
수정 가능 여부 수정 불가능(읽기 전용). 원본 문자열 수정 가능.

'공부 > C++' 카테고리의 다른 글

union  (0) 2025.04.10
함수 포인터  (0) 2025.04.09
예외처리(simple)  (0) 2025.04.09
Attribute  (0) 2025.03.27
C++ IO스트림  (0) 2025.03.27

try_catch문

  • C# 과 동일하게 try_catch문을 통해 예외처리 가능
  • throw를 통해 예외 발생 가능
  • throw를 통해 던질 수 있는 익셉션 타입에 제한이 없음
    • const char* 형태로 익셉션 던질 수 있음
  • 익셉션 객체는 가능한 const 레퍼런스 로 받는 것이, 객체 슬라이싱이 발생하지 않아 좋다
  • 모든 익셉션을 받아들일려면 catch (...) { } 으로 사용

'공부 > C++' 카테고리의 다른 글

함수 포인터  (0) 2025.04.09
(cpp17)string_view  (2) 2025.04.09
Attribute  (0) 2025.03.27
C++ IO스트림  (0) 2025.03.27
C++스트림  (0) 2025.03.26
  • 가상 소멸자는 구조체의 크기를 증가시킴

  • 내부 바이트 정렬 기준에 따라 padding 값이 추가 될 수 있음 (부모에 가상 소멸자일 떄와, 자식에만 가상 소멸자일 떄, 오프셋이 다른 이유)

    • padding추가는 자식의 가상소멸자에 의해 부모의 멤버가 밀릴 때는 발생하지 않음.

      • 이미 부모 클래스 메모리 구조가 정렬규칙에 따라 배치되었기 때문
      //입력 조립기에게 정점 버퍼의 구조를 알려주기 위한 구조체를 반환한다. 
      D3D12_INPUT_LAYOUT_DESC CShader::CreateInputLayout()
      {
        UINT nInputElementDescs = 2;
        D3D12_INPUT_ELEMENT_DESC* pd3dInputElementDescs = new
            D3D12_INPUT_ELEMENT_DESC[nInputElementDescs];
        // 가상소멸자에 의해 8바이트 뒤로 밀림 (64비트 기준)
        pd3dInputElementDescs[0] = { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 8,
        D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 };
        // 자식만 가상 소멸자
        pd3dInputElementDescs[1] = { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 20,
       D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 };
       // 부모에 가상 소멸자
           pd3dInputElementDescs[1] = { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 24,
       D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 };
        D3D12_INPUT_LAYOUT_DESC d3dInputLayoutDesc;
        d3dInputLayoutDesc.pInputElementDescs = pd3dInputElementDescs;
        d3dInputLayoutDesc.NumElements = nInputElementDescs;
      
        return(d3dInputLayoutDesc);
      }

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

그리기_연산02  (2) 2025.04.14
06.그리기_연산01_2  (7) 2025.04.08
06.그리기_연산01_1  (0) 2025.04.05
06.그리기_연산01  (0) 2025.03.25
05. 렌더링파이프 개념문제 개인제작  (1) 2025.03.15

목차

  • [[#6.6 상수 버퍼|6.6 상수 버퍼]]
    • [[#6.6 상수 버퍼#6.6.4 상수 버퍼 서술자|6.6.4 상수 버퍼 서술자]]
    • [[#6.6 상수 버퍼#6.6.5 루트 서명과 서술자 테이블|6.6.5 루트 서명과 서술자 테이블]]
  • [[#6.7 셰이더의 컴파일|6.7 셰이더의 컴파일]]
    • [[#6.7 셰이더의 컴파일#6.7.1 오프라인 컴파일|6.7.1 오프라인 컴파일]]
  • [[#6.8 래스터화기 상태|6.8 래스터화기 상태]]
  • [[#6.9 파이프라인 상태 객체|6.9 파이프라인 상태 객체]]
  • [[#번외|번외]]
    • [[#번외#✅ 구성만 가능한 고정 기능(Fixed-function) 단계들(shader가 아닌 단계들)|✅ 구성만 가능한 고정 기능(Fixed-function) 단계들(shader가 아닌 단계들)]]

6.6 상수 버퍼

  • 상수 버퍼(contant buffer) : 셰이더 프로그램에서 참조하는 자료를 담는 GPU 자원(ID3DResource)의 예

    • 변하지 않는 작은 데이터를 저장하는 용도로 사용

    • HLSL에서의 상수 버퍼는 cbuffer 타입 <- "ShaderModel 4.0"; "5.1 이상에서는 ConstantBuffer<{type}>을 선호"

      • 최대 4096개의 벡터 보유가능 ( 최대 4개의 32비트 값 포함)

        • 256개의 float4x4 -> 64 KB
      • 파이프라인 단계당 최대 14개의 상수 버퍼 바인딩 가능(2개의 추가 슬롯은 내부용으로 예약되어 있음) { b0 ~ b13 }

          cbuffer cbPerObject : register(b0) <- b0 상수 버퍼 사용
          { ... }
    • CPU가 프레임당 한 번 갱신하는 것이 일반적 (업로드 힙 사용)

      • GPU가 빠르게 참조
      • CPU-GPU 동기화를 피하기 위해서 , 상수 버퍼에도 더블, 트리플 버퍼링 사용
      • 정적 데이터 사용 시에는 디폴트 힙 사용
    • 최소 하드웨어 할당 크기(256 Byte)의 배수여야 함

      • GPU는 256바이트 단위로 상수버퍼 로딩

      • 효율적인 캐시/버스 활용을 위해

      • HLSL에서 사용자의 정의 Byte가 256일 필요x (내부적으로 256 byte 배수로 처리)

        • Dx(c++)에서는 지정해야 함;
          (256 Byte 단위로 읽기 때문에 패딩을 맞춰주어야 하기 때문)

          cbuffer PerFrame : register(b0)
          {
            float4x4 view;          // 64 bytes
            float4x4 projection;    // 64 bytes
            float time;             // 4 bytes
            float3 padding;         // 12 bytes (패딩 맞춤)
          }
          // 총 크기 = 64 + 64 + 4 + 12 = 144 bytes → 내부적으로는 256 bytes로 할당됨
  • Dx 상수버퍼 생성 예제

      struct ObjectConstants
      {
          DirectX::XMFLOAT4X4 WorldViewProj = MathHelper::Identity4x4();
      };
    
      UINT mElementByteSize = d3dUtil::CalcConstantBufferByteSize(sizeof(ObjectConstants));
    
      // 업로드 힙에 상수 버퍼 생성
      ComPtr<ID3D12Resource> mUploadCBuffer;
      device->CreateCommitedResource(
          &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD),
          D3D12_HEAP_FLAG_NONE,
          &CD3DX12_RESOURCE_DESC::Buffer(mElementByteSize * NumElements),
          D3D12_RESOURCE_STATE_GENERIC_READ,
          nullptr,
          IID_PPV_ARGS(&mUploadCBuffer));
    
      // CBV(상수 버퍼 뷰) 생성
      D3D12_CONSTANT_BUFFER_VIEW_DESC cbvDesc;
      cbvDesc.BufferLocation = mUploadCBuffer->GetGPUVirtualAddress();
      cbvDesc.SizeInBytes = mElementByteSize;
    
      device->CreateConstantBufferView(&cbvDesc, cbvHeap->GetCPUDescriptorHandleForHeapStart());
    
      // CBV 루트 시그니처에 바인딩
      commandList->SetGraphicsRootDescriptorTable(0, cbvHeap->GetGPUDescriptorHandleForHeapStart());
    
      // 상수 버퍼 갱신
      ObjectConstants* mMappedData = nullptr;
      // (대응 부분자원 색인, 메모리 범위 서술(nullptr 전체대응). 대응된 자료 가리키는 포인터)
      mUploadCBuffer->Map(0, nullptr, reinterpret_cast<void**>(&mMappedData));
      // 쓰기만 할거기에 메모리 범위를 나타내는 CD3DX12_RANGE readRange(0, 0);가 범위
      // 그래서 nullptr을 쓸 수 있다 
    
      ObjectConstants objConstants;
      DirectX::XMMATRIX world = DirectX::XMMatrixIdentity();
      DirectX::XMMATRIX view = ...;   // 뷰 행렬
      DirectX::XMMATRIX proj = ...;   // 프로젝션 행렬
      DirectX::XMMATRIX wvp = world * view * proj;
    
      XMStoreFloat4x4(&objConstants.WorldViewProj, XMMatrixTranspose(wvp)); // HLSL과 행 우선/열 우선 일치
    
      // 프레임마다 갱신 시
      mMappedData[i] = objContants;
    
      // 리소스 해제는 선택 사항 : dx에서 알아서 해제해줌, 그래도 디버깅 및 예외 발생을 대비해 하는것이 좋음 (! 지속적으로 사용되는 리소스는 해제x )
      // 업로드 힙 더이상 사용안하면 리소스 해제
      if (mUploadBuffer != nullptr)
          mUploadBuffer->Unmap(0, nullptr);
    
      mMappedData = nullptr;

    -> d3dUtil::CalcConstantBufferByteSize() : 256 byte 배수 단위 버퍼 크기로 계산 반환
    -> 상수 버퍼에서 해당 물체를 위한 상수들이 있는 부분 영역을 서술하는 상수 버퍼 뷰를 파이프라인에 묶음

  • 256 Byte배수 단위 변환 구현

      #include <iostream>
      using namespace std;
    
      constexpr size_t ALIGNMENT = 256;
    
      size_t CeilAlign(size_t size) {
          return (size + (ALIGNMENT - 1)) & ~(ALIGNMENT - 1);
      }
    
      int main() {
          size_t size1 = 180; // 예제: 180바이트 데이터
          size_t size2 = 300; // 예제: 300바이트 데이터
    
          // 256
          cout << "Ceil Align  (180): " << CeilAlign(size1) << endl;
    
          // 512
          cout << "Ceil Align  (300): " << CeilAlign(size2) << endl;
    
          return 0;
      }

6.6.4 상수 버퍼 서술자

  • D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV 형식의 서술자 힙에 담김
D3D12_DESCRIPTOR_HEAP_DESC cbvHeapDesc;
cbvHeapDesc.NumDescriptors = 1;
cbvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
cbvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
cbvHeapDesc.NodeMask = 0;

ComPtr<ID3D12DescriptorHeap> mCbvHeap = nullptr;
md3dDevice->CreateDescriptorHeap(&cbvHeapDesc, IID_PPV_ARGS(&mCbvHeap);
-> 셰이더 프로그램에서 서술자에게 접근할 것임을 뜻하는 flag 지정

6.6.5 루트 서명과 서술자 테이블

  • 루트 서명(root signature) : 그리기 호출 전에 응용 프로그램이 필수로 바인딩 해야하는 자원 및 그 자원들에 대한 셰이더 입력 레지스터들에 어떻게 대응되는지를 정의

    • 반드시, 그리기 호출에 쓰이는 셰이더들과 호환되어야 함
    • 루트 서명 유효성은 파이프라인 상태 객체를 생성할 떄 검증
    • 정의를 할 뿐, 바인딩 하지는 않음
  • 대표 인터페이스 : ID3D12RootSignature

    • 주어진 그리기 호출에서 셰이더들이 기대하는 자원들을 서술하는 루트 매개변수들의 배열로 정의
    • 루트 매개변수 : 하나의 루트 상수 or 루트 서술자 or 서술자 테이블 일 수 있음
  • 루트 서명 생성 예시

      CD3DX12_ROOT_PARAMETER slotRootParameter[1];
    
      // CVB 하나를 담는 서술자 테이블 생성
      CD3DX12_DESCRIPTOR_RANGE cbvTable;
      cbvTable.Init(
          D3D12_DESCRIPTOR_RANGE_TYPE_CBV,
          1, // 테이블의 서술자 개수
          0 // 이루트 매개변수에 묶일 셰이더 인수들의 기준 레지스터 번호
      );
    
      slotRootParameter[0].InitAsDescriptorTable(
          1, // range 개수
          &cbvTable // 구간들의 배열을 가리키는 포인터
      );
    
      // 루트 서명은 루트 매개변수들의 배열
      CD3DX12_ROOT_SIGNATURE_DESC rootSigDesc(1, slotRootParameter, 0, nullptr,
          D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
    
      // 상수 버퍼 하나로 구성된 서술자 구간을 가리키는
      // 슬롯 하나로 이루어진 루트 서명 생성
      ComPtr<ID3DBlob> serializedRootSig = nullptr;
      ComPtr<ID3DBlob> errorBlob = nullptr;
      HRESULT hr = D3D12SerializeRootSignature(&rootSigDesc,
          D3D_ROOT_SIGANTURE_VERSION_1,
          serializedRootSig->GetAddressOf(),
          errorBlob.GetAddressOf());
    
      ThrowIfFailed(md3dDevice->CreateRootSignature(
          0,
          serializedRootSig->GetBufferPointer(),
          serializedRootSig->GetBufferSize(),
          IID_PPV_ARGS(&mRootSignature)));
  • 자원 바인딩 : ID3D12GraphicsCommandList::SetGraphicsRootDescriptorTable을 호출해서 서술자 테이블을 파이프라인에 묶음

      void ID3D12GraphicsCommandList::SetGraphicsRootDescriptorTable(
          UINT RootParameterIndex,
          D3D12_GPU_DESCRIPTOR_HANDLE BaseDescriptor);
    • RootParameterIndex : 설정하고자 하는 루트 서명의 색인
    • BaseDescriptor : 설정하고자 하는 서술자 테이블으 첫 서술자에 해당하는 서술자의 핸들
  • 루트 서명과 CBV 힙을 명력 목록에 설정하고, 파이프라인에 묶을 자원들을 지정하는 서술자 테이블 생성

     mCommandList->SetGraphicsRootSignature(mRootSignature.Get());
     ID3D12DescriptorHeap* descriptorHeaps[] = {mCbvHeap.Get()};
     mCommandList->SetDescriptorHeaps(_countof(descriptorHeaps), descriptorHeaps);
    
     // 이번 그리기 호출에서 사용할 CBV의 오프셋
     CD3DX12_GPU_DESCRIPTOR_HANDLE cbv(mCbvHeap->GetGPUDescriptorHandleForHeapStart());
     cbv.Offset(cbvIndex, mCbvSrvUavDescritoprSize);
    
     mCommandList->SetGraphicsRootDescriptorTable(0, cbv);
  • 성능을 위해서는 루트 서명을 최대한 작게 만들고, 렌더링 과정에서 루트 서명 변경 최소화

  • 응용 프로그램이 묶은 루트 서명의 구성물이 그리기/분배(dispatch) 호출들 사이에서 변할 때마다, D3D12 드라이버가 해당 내용물에 자동으로 버전 번호 부여.

    • 각각의 그리기/분배 호출은 고유한 루트 서명 상태들의 집합을 받게 됨
  • 루트 서명 변경시, 기존의 모든 바인딩이 삭제됨

6.7 셰이더의 컴파일

  • D3DCompileFromFile

      HRESULT D3DCompileFromFile (
          LPCWSTR pFileName,
          const D3D_SHADER_MACRO *pDefines,
          ID3DInclude *pInclude,
          LPCSTR pEntrypoint,
          LPCSTR pTarget,
          UINT Flags1,
          UINT Flags2,
          ID3DBlob **ppCode,
          ID3DBlob **ppErrorMsgs);
    • pFileName : 컴파일할 HLSL 소스 코드를 담은 .hlsl 파일 이름
    • pEntrypoint : 파일 내부에 여러개의 셰이더 있을 시, 진입점 명시
    • pTarget : 셰이더 프로그램의 종류와 대상 버전을 나타내는 문자열 (vs, hs, ds, ...)
    • Flags1 : 셰이더 코드의 세부적인 컴파일 방식 제어 플래그
      • D3DCOMPILE_DEBUG : 셰이더를 디버그 모드에서 컴파일
      • D3DCOMPILE_SKIP_OPTIMIZATION : 최적화 생략 (디버깅에 용이)
    • Flags2 : 효과(effect)의 컴파일 관한 고급옵션
    • ppCode : 컴파일된 셰이더 목적 바이트코드를 담은 II3DBlob 구조체의 포인터를 매개변수를 통해 돌려줌
    • ppErrorMsgs : 컴파일 오류 발생한 경우, 오류 메시지 문자열을 담은 ID3DBlob 구조체의 포인터를 이 매개변수를 통해 돌려줌
  • ID3DBlob 을 이용한 디버깅

    • LPVOID GetBufferPointer : 버퍼를 가리키는 void* 포인터를 돌려줌. (실제 사용하려면 적절한 형식으로 캐스팅)

    • SIZE_T GetBufferSize : 버퍼의 크기(바이트 개수)를 반환

    • 사용 예시

        UINT compileFlags = 0;
        #if defined(DEBUG) || defined(_DEBUF)
            compileFlags = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
        #endif
      
        HRESULT hr = S_OK;
        ComPtr<ID3DBlob> byteCode = nullptr;
        ComPtr<ID3DBlob> errors;
        hr = D3DCompileFromFile(filename.c_str(), defines,
            D3D_COMPILE_STANDARD_FILE_INCLUDE,
            entrypoint.c_str(), target.c_str(), compileFlags, 0, &byteCode, &errors);
      
        // 오류 메시지 디버그 창에 출력
        if (errors != nullptr)
            OutputDebugStringA((char*)errors->GetBufferPointer()));
      
        ThrowIfFailed(hr);
      
        return byteCode;
    • 셰이더의 컴파일은 파이프라인에 바인딩 되는 것을 의미하지 않는다

    • VS에서는 hlsl 확장자 파일이 HLSL컴파일러 타입 빌드로 되어있는데, 이를 빌드하지 않음으로 설정한다; (D3D에서 별도 컴파일 API 사용 or fxc.exe 도구로 사전 컴파일 선호)

6.7.1 오프라인 컴파일

  • 오프라인 컴파일 하는 이유

    • 복잡한 셰이더는 컴파일 시간이 오래 걸림; 오프라인 컴파일 시, 게임의 적재(loading)시간이 빨라짐
    • 셰이더 컴파일 오류들은 실행 시점이 아니라 빌드 과정에서 일찍 점검하는 것이 편함
    • Windows8 스토어 앱은 반드시 오프라인 컴파일 사용
  • 컴파일된 셰이더를 담는 파일의 확자자로는 .cso(compiled shader object)를 사용하는 것이 관례

  • 셰이더 오프라인 컴파일할 떄에는 DirectX에 포함된 FXC 도구 사용 (CL 도구)

    • 디버그 모드 컴파일
      • fxc "color.hlsl" /Od /Zi /T vs_5_0 /E "VS" /Fo "color_vs.cso" /Fc "color_vs.asm"
    • 명령줄 옵션
      • /Od : 최적화 비활성화
      • /Zi : 디버그 정보 활성화
      • /T <문자열> : 셰이더의 종류와 대상 버전
      • /E <문자열> : 셰이더 진입점
      • /Fo <문자열> : 컴파일된 셰이더 바이트코드 목적 파일(object file)
      • /Fc <문자열> : 어셈블리 코드 목록 출력 (디버깅, 명령 개수 점검, 생성된 코드 종류 확인 등에 유용)
  • 컴파일된 셰이더 파일 사용 예시

      bool LoadShader(const std::string& path, ComPtr<ID3DBlob>& outBlob)
      {
          std::ifstream file(path, std::ios::binary | std::ios::ate);
          if (!file) return false;
    
          std::streamsize size = file.tellg();
          if (size <= 0) return false;
          file.seekg(0, std::ios::beg);
    
          if (FAILED(D3DCreateBlob(size, &outBlob))) return false;
          if (!file.read((char*)outBlob->GetBufferPointer(), size)) return false;
    
          return true;
      }

6.8 래스터화기 상태

  • 렌더링 파이프라인 단계 중 구성(설정)만 가능한 단계

  • 래스터화기 상태(resterizer state) 를 통해서 구성

  • 대표 구조체 : D3D12_RASTERIZER_DESC 구조체

      typedef struct D3D12_RASTERIZER_DESC {
          D3D12_FILL_MODE FillMode; // 기본값 : D3D12_FILL_SOLID
          D3D12_CULL_MODE CullMode; // 기본값 : D3D12_CULL_BACK
          BOOL FrontCounterClockwise; // 기본값 : false
          INT DepthBias; // 기본값 : 0
          FLOAT DepthBiasClamp; // 기본값 : 0.0f
          FLOAT SlopeScaledDepthBias; // 기본값 : 0.0f
          BOOL DepthClipEnable; // 기본값 : true
          BOOL ScissorEnable; // 기본값 : false
          BOOL MultisampleEnable; // 기본값 : false
          BOOL AntialiasedLineEnable; // 기본값 : false
          UINT ForcedSampleCount; // 기본값 : 0
      }
    • FillMode : 와이어프레임 렌더링을 위해서는 D3D12_FILL_WIREFRAME 을, 면의 속을 채운 (solid) 렌더링을 위해서는 D3D12_FILL_SOLID 지정
    • CullMode : 선별을 끄려면 D3D12_CULL_NONE, 후면 삼각형들을 선별하려면 D3D12_CULL_BACK, 전면 선별은 D3D12_CULL_FRONT
    • FrontCounterClockwise : 정점들이 시계방향(카메라 기준)으로 감긴 삼각형을 전면 삼각형으로 취급하려면 true, 반시계방향(카메라 기준)으로 감긴 삼각형을 후면 삼각형으로 취급하려면 false
    • ScissorEnable : 가위 판정 활성화는 true

6.9 파이프라인 상태 객체

  • 파이프라인 상태 객체(pipeline state object, PSO) :

    • 렌더링 파이프라인의 상태를 제어하는 집합체
    • 그래픽스 파이프라인의 다양한 상태(예: 셰이더, 블렌드 상태, 래스터라이저 상태, 깊이 스텐실 상태 등)를 하나의 객체로 묶음
    • PSO는 생성 후 변경할 수 없는 불변 객체;
      따라서 렌더링 중에 빠르게 상태를 전환할 수 있음
    • 기존 D11에서 개별로 관리하던 파이프라인 상태를 D12에서 PSO로 통합관리
    • PSO는 검증과 생성에 많은 시간이 걸릴 수 있으므로 초기화 시점에서 생성
    • 다른 PSO로 교체할 떄를 대비해, 미리 PSO를 만들어 두고 사용
  • 대표 구조체 : ID3D12PipelineState

      typedef struct D3D12_GRAPHICS_PIPELINE_STATE_DESC
      {
          ID3D12RootSignature* pRootSignature;
          D3D12_SHADER_BYTECODE VS;
          D3D12_SHADER_BYTECODE PS;
          D3D12_SHADER_BYTECODE DS;
          D3D12_SHADER_BYTECODE HS;
          D3D12_SHADER_BYTECODE GS;
          D3D12_STREAM_OUTPUT_DESC StreamOutput;
          D3D12_BLEND_DESC BlendState;
          UINT SampleMask;
          D3D12_RASTERIZER_DESC RasterizerState;
          D3D12_DEPTH_STENCIL_DESC DepthStencilState;
          D3D12_INPUT_LAYOUT_DESC InputLayout;
          D3D12_PRIMITIVE_TOPOLOGY_TYPE
              PrimitiveTopologyType;
          UINT NumRenderTargets;
          DXGI_FORMAT RTVFormats[8];
          DXGI_FORMAT DSVFormat;
          DXGI_SAMPLE_DESC SampleDesc;
      } D3D12_GRAPHICS_PIPELINE_STATE_DESC;
    • pRootSignature :
      PSO(Pipeline State Object)와 함꼐 묶을 루트 서명을 가리키는 포인터, 
      루트 서명은 반드시 PSO로 묶는 셰이더들과 호환되어야 함
    • D3D12_SHADER_BYTE_CODE : 셰이더 컴파일 바이트 코드; (VS, PS, DS, HS, GS)
        typedef struct D3D12_SHADER_BYTE_CODE {
            const BYTE *pShaderBytecode;
            SIZE_T BytecodeLength;
        } D3D12_SHADER_BYTECODE;
    • StreamOutput :
      • 출력 정의, 주로 데이터 캡처 or 변환 작업에 사용
    • State & Layout :
      • 각각의 상태 정의
    • SampleMask :
      • 다중표본화 샘플 개수(최대 32개까지 가능);
      • SampleDesc의 Count보다 큰 비트는 무시됨
      • 기본 값 0xffffffff(모든 표본 활성)
    • NumRenderTargets :
      • 동시에 사용할 렌더 대상 개수
    • RTVFormats :
      • 렌더 타겟 포맷
      • PSO와 함께 사용할 렌더 타겟의 설정들과 부합해야 함
    • DSVFormats :
      • 깊이 스텐실 버퍼 포맷
      • PSO와 함께 사용할 깊이 스텐실 버퍼의 설정들과 부합해야 함
    • SampleDesc :
      • 멀티 샘플링 샘플 개수 및 품질 수준 서술
      • PSO와 함꼐 사용할 렌더 타겟의 설정들과 부합해야 함
  • 생성 예제

          D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc;
          ZeroMemory(&psoDesc, sizeof(D3D12_GRAPHICS_PIPELINE_STATE_DESC));
          psoDesc.InputLayout = { mInputLayout.data(), (UINT)mInputLayout.size() };
          psoDesc.pRootSignature = mRootSignature.Get();
          psoDesc.VS = 
          { 
              reinterpret_cast<BYTE*>(mvsByteCode->GetBufferPointer()), 
              mvsByteCode->GetBufferSize() 
          };
          psoDesc.PS = 
          { 
              reinterpret_cast<BYTE*>(mpsByteCode->GetBufferPointer()), 
              mpsByteCode->GetBufferSize() 
          };
          psoDesc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT);
          psoDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT);
          psoDesc.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC(D3D12_DEFAULT);
          psoDesc.SampleMask = UINT_MAX;
          psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
          psoDesc.NumRenderTargets = 1;
          psoDesc.RTVFormats[0] = mBackBufferFormat;
          psoDesc.SampleDesc.Count = m4xMsaaState ? 4 : 1;
          psoDesc.SampleDesc.Quality = m4xMsaaState ? (m4xMsaaQuality - 1) : 0;
          psoDesc.DSVFormat = mDepthStencilFormat;
          ThrowIfFailed(md3dDevice->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&mPSO)));
  • PSO 교체

    • 성능을 위해 상태 변경을 최소화
    • 같은 PSO 사용 가능 물체는 함께 그리기
  • PSO 교체 예제

      // Reset을 호출해서 초기 PSO 지정
      mCommandList->Reset(mDirectCmdListAlloc.Get(), mPSO1.Get());
      /* PSO1 물체 */
    
      // PSO 변경
      mCommandList->SetPipelineState(mPSO2.Get());

번외

✅ 구성만 가능한 고정 기능(Fixed-function) 단계들(shader가 아닌 단계들)

단계 구성만 가능? 설명
Input Assembler (IA) ✅ 예 정점 버퍼, 인덱스 버퍼, Input Layout 등 구성
Rasterizer Stage ✅ 예 뷰포트, 스카이솟, 카울링, 와이어프레임 설정 등
Output Merger (OM) ✅ 예 렌더 타겟, 깊이-스텐실 버퍼 설정, 블렌딩 옵션 등
Stream Output (SO) ✅ 예 정점 쉐이더 결과를 GPU 메모리로 출력할 수 있도록 설정만 함
Multisample Stage (MSAA) ✅ 예 샘플링 품질 및 해상도 등 구성만 가능

상수 버퍼의 업로드 힙, 디폴트 힙 사용 비교

힙 타입 CPU 접근 가능 여부 GPU 성능 사용 예시
업로드 힙 (D3D12_HEAP_TYPE_UPLOAD) ✅ 가능 🚀 중간 프레임마다 갱신되는 상수 버퍼 (예: 카메라 변환 행렬, 조명 데이터)
디폴트 힙 (D3D12_HEAP_TYPE_DEFAULT) ❌ 불가능 ⚡ 빠름 변경되지 않는 정적 데이터 (예: 모델 정점 데이터, 고정된 조명 설정)

목차

  • [[#6.1 정점과 입력 배치|6.1 정점과 입력 배치]]
  • [[#6.2 정점 버퍼|6.2 정점 버퍼]]
  • [[#6.2.2 기본 버퍼 생성 편의용 함수 작성|6.2.2 기본 버퍼 생성 편의용 함수 작성]]
  • [[#6.3 색인과 색인 버퍼|6.3 색인과 색인 버퍼]]
  • [[#6.4 예제 정점 셰이더|6.4 예제 정점 셰이더]]
    • [[#6.4 예제 정점 셰이더#6.4.1 입력 배치 서술과 입력 서명 연결|6.4.1 입력 배치 서술과 입력 서명 연결]]
  • [[#6.5 예제 픽셀 셰이더|6.5 예제 픽셀 셰이더]]
  • [[#번외|번외]]

6.1 정점과 입력 배치

  • 원하는 자료(특성)를 가진 커스텀 정점 형식을 만들려면 그러한 자료를 담을 구조체 정의
    • 사용자 정의 정점 예시
        struct Vertex1
        {
            XMFLOAT3 Pos;
            XMFLOAT3 Color;
        };
        struct Vertex2
        {
        XMFLOAT3 Pos;
        XMFLOAT3 Normal;
        XMFLOAT2 Tex0;
        }
  • 입력 배치 서술(input layout descrpitino) :
    • 정점의 각 성분으로 무엇을 해야 하는지를 D3D에 알려주어야 함
    • D3D12_INPUT_LAYOUT_DESC 구조체로 대표
        typedef struct D3D12_INPUT_LAYOUT_DESC
        {
            const D3D12_INPUT_ELEMENT_DESC *pInputElementDesccs;
            UINT NumElements;
        } D3D12_INPUT_LAYOUT_DESC;
      • D3D12_INPUT_ELEMENT_DESC 형식 원소들을 담은 배열과 개수를 나타내는 구조체
      • 각 원소는 정점 구조체의 각 성분을 서술
      • 배열의 원소들과 정점 구조체의 성분들 은 일대일로 대응
    • D3D12_INPUT_ELEMENT_DESC 정의
        typedef struct D3D12_INPUT_ELEMENT_DESC
        {
            LPCSTR SemanticName;
            UINT SematicIndex;
            DXGI_FORMAT Format;
            UINT InputSlot;
            UINT AlignedByteOffset;
            D3D12_INPUT_CLASSIFICATION InputSlotClass;
            UINT InstanceDataStepRate;    
        } D3D12_INPUT_ELEMENT_DESC;
      • SemanticName
        • 성분에 부여된 문자열 이름
        • 정점 셰이더에서 의미소(semantic) 이름으로 쓰이므로, 반드시 유효한 변수이름이어야 함
        • 정점 구조체의 성분을 정점 셰이더 입력 서명과 대응시키는 역할
      • SematicIndex
        • 의미소 색인
        • 하나의 정점 구조체에 텍스처 좌표가 여러 개 있을 수 있는데, 각 텍스처 좌표에 개별적인 의미소 이름을 부여하는 대신 색인을 통해 구별
        • 셰이더 코드에서 색인이 지정되지 않은 의미소는 0인 의미소로 간주
      • Format
        • DXGI_FORMAT 열거형의 한 멤버
        • 4.1.3 텍스쳐 형식 참고
      • InputSlot
        • 성분의 자료를 가져올 정점 버퍼 슬롯 색인
        • 총 16개의 정점 버퍼 슬롯(0~15)를 통해서 정점 자료 공급 가능
      • AlignedByteOffset
        • 지정 입력 슬롯에서 정점 구조체의 시작 위치와 정점 성분의 시작 위치 사이의 거리를 나타내는 오프셋(바이트 단위)
        • Pos 성분 바이트 시작 ~ 12byte 이므로, 다음 성분인 Normal은 12byte가 시작위치이므로 offset 값 설정
      • InputSlotClass
        • 입력 슬롯의 데이터 클래스를 정의
        • 셰이더가 처리하는 데이터가 정점 데이터인지 아니면 인스턴스 데이터인지 구분하는 데 사용
          • D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA :
            • 슬롯이 정점 데이터를 포함
            • 각 정점에 대해 데이터가 셰이더로 전달
            • Instance 수 = 0
          • D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA :
            • 슬롯이 인스턴스 데이터를 포함
            • 각 인스턴스에 대해 데이터를 전달할 때 사용
            • 여러 오브젝트를 인스턴스 렌더링할 때, 각각의 인스턴스에 고유한 데이터가 필요
      • InstanceDataStepRate
        • 인스턴스 데이터의 전달 주기를 제어
        • 셰이더가 데이터를 몇 개의 인스턴스마다 업데이트해야 하는지를 결정
          • 1 : 모든 인스턴스에 대해 새로운 데이터를 제공
          • N : 값이 2라면 두 개의 인스턴스가 동일한 데이터를 공유
        • 인스턴스 데이터 사용 시만 적용(PER_INSTANCE_DATA)

6.2 정점 버퍼

  • 정점버퍼 : 정점들을 저장하는 버퍼
    • GPU가 정점 배열에 접근하려면, 정점들을 버퍼라고 부르는 GPU자원 (ID3D12Resource)에 넣어 두어야 함
  • 버퍼는 텍스처보다 단순 자원
    • 다차원 X
    • 밉맵, 다중표본화 기능 X
  • 정점 같은 자료 원소들의 배열을 GPU에 제공할 떄, 항상 버퍼 사용
  • 버퍼 자원을 채우고, ID3D12Device::CreateCommittedResource 메서드를 호출해서
    ID3D12Resource 객체 생성
  • 버퍼 자원 서술 : D3D12_RESOURCE_DESC
    • D12에서 제공하는 해당 구조체를 상속해서 쓰는 편의용 생성자들과 메서드들을 추가한 C++ 래퍼 클래스 CD3DX12_RESOURCE_DESC 제공
        static inline CD3DX12_RESOURCE_DESC Buffer(
            UINT64 width,
            D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FALG_NONE,
            UINT64 alignment = 0 )
        {
            return CD3DX12_RESOURCE_DESC(
                D3D12_RESOURCE_DIMENSION_BUFFER,
                aligment, width, 1, 1, 1,
                DXGI_FORMAT_UNKNOWN, 1, 0,
                D3D12_TEXTURE_LAYOUT_ROW_MAJOR, flags );
        }
      • width :
        • 버퍼의 바이트 개수를 나타냄
        • flaot 64개 담는 버퍼 => 64*sizeof(flaot)
      • D3D12_RESOURCE_DESC::D3D12_RESOURCE_DIMENSION 필드에 의해서 자원의 구체적인 종류 지정
      • CD3DX12_RESOURCE_DESC bufferDesc = CD3DX12_RESOURCE_DESC::Buffer(1024); // 1024바이트 버퍼 생성
  • 정적 기하구조(프레임마다 변하지는 않는 기하구조)를 그릴 떄, 최적 성능을 위해 정점 버퍼들을 기본 힙(D3D12_HEAP_TYPE_DEFAULT)에 넣음
    • 정점 버퍼를 초기화한 후에는 GPU만 버퍼의 정점들을 읽으므로(기하구조 그리기위해), 기본 힙에 넣는 것이 합당
    • CPU는 기본 힙에 있는 정점 버퍼 수정 불가
      • 그렇기에 업로드 힙에서 값을 복사해서 와야함
      • D3D12__SUBRESOURCE_DATA 사용
        • 요즘에는 텍스쳐 같은 큰 데이터 외에는 사용x
      • uploadHeap에서 직접 Map한 후, defaultHeap에 memcopy
      • CopyBufferRegion()으로 GPU에서 복사
        • 가장 많이 쓰는 방식

6.2.2 기본 버퍼 생성 편의용 함수 작성

  • 기본 버퍼 생성

      Microsoft::WRL::ComPtr<ID3D12Resource> d3dUtil::CreateDefaultBuffer(
          ID3D12Device* device,
          ID3D12GraphicsCommandList* cmdList,
          const void* initData,
          UINT64 byteSize,
          Microsoft::WRL::ComPtr<ID3D12Resource>& uploadBuffer)
      {
          ComPtr<ID3D12Resource> defaultBuffer;
    
          // 실제 기본 버퍼 자원 생성
          ThrowIfFailed(device->CreateCommittedResource(
              &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
              D3D12_HEAP_FLAG_NONE,
              &CD3DX12_RESOURCE_DESC::Buffer(byteSize),
              D3D12_RESOURCE_STATE_COMMON,
              nullptr,
              IID_PPV_ARGS(defaultBuffer.GetAddressOf())));
    
          // CPU 메모리를 기본 버퍼에 복사하기 위해, 임시 업로드 힙 생성
          ThrowIfFailed(device->CreateCommittedResource(
              &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD),
              D3D12_HEAP_FLAG_NONE,
              &CD3DX12_RESOURCE_DESC::Buffer(byteSize),
              D3D12_RESOURCE_STATE_GENERIC_READ,
              nullptr,
              IID_PPV_ARGS(uploadBuffer.GetAddressOf())));
    
    
    // 기본 버퍼에 복사할 자료 서술
    D3D12_SUBRESOURCE_DATA subResourceData = {};
    subResourceData.pData = initData;
    subResourceData.RowPitch = byteSize;
    subResourceData.SlicePitch = subResourceData.RowPitch;

    // 기본 버퍼 자원으로의 자료 복사 요청
    // 보조 함수 UpdateSubresources는 CPU 메모리를 임시 업로드 힙에 복사,
    // ID3D12CommandList::CopySubresourceRegion을 이용해서 임시 업로드 힘의 자료를
    // mBuffer에 복사
    cmdList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(defaultBuffer.Get(), 
        D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_COPY_DEST));
    UpdateSubresources<1>(cmdList, defaultBuffer.Get(), uploadBuffer.Get(), 0, 0, 1, &subResourceData);
    cmdList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(defaultBuffer.Get(),
        D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_GENERIC_READ));

    // 주의 : 위의 함수 호출 이후에도 uploadBuffer를 계속 유지
    // 실제로 복사를 수행하는 명령 목록이 아직 실행 X
    // 복사 완료되었음이 확실해진 후에 호출자가 UploadBuffer 해제

    return defaultBuffer;
}
```
  • D3D12_SUBRESOURCE_DATA 구조체

      typedef struct D3D12_SUBRESOURCE_DATA
          {
          const void *pData;
          LONG_PTR RowPitch;
          LONG_PTR SlicePitch;
          }     D3D12_SUBRESOURCE_DATA;
    • pData :
      • 버퍼 초기화용 자료를 담은 시스템 메모리 배열 가리키는 포인터
      • 버퍼에 n 개의 정점을 담을 수 있을 떄, 전체를 초기화 하려면 시스템 메모리 배열에 최소 n개의 정점 필요
    • RowPitch :
      • 버퍼의 경우, 복사할 자료이 크기(바이트 개수)
    • SlicePitch :
      • 버퍼의 경우, 복사할 자료이 크기(바이트 개수)
  • 정점 버퍼 기본 버퍼 예시

      Vertex vertices[] = {
          { XMFLOAT3(-1.0f, -1.0f, -1.0f), XMFLOAT4(COLORS::White)},
          ...    
      };
    
      const UINT vbByteSize = 8 * sizeof(Vertex); // 8개 정점
    
      ComPtr<ID3D12Resource> VertexBufferGPU = nullptr;
      ComPtr<ID3D12Resource> VertexBufferUploader = nullptr;
    VertexBufferGPU = d3dUtil::CreateDefaultBuffer(md3dDevice.Get(),
          mCommandList.Get(), vertices, vbByteSize, VertexBufferUploader);
          // 사용자 정의 정점 버퍼 생성 메서드
      D3D12_VERTEX_BUFFER_VIEW vbv;
      vbv.BufferLocation = VertexBufferGPU->GetGPUVirtualAddress();
      vbv.StrideInBytes = sizeof(Vertex);
      vbv.SizeInByte = vbByteSize;
    
      D3D12_VERTEX_BUFFER_VIEW vertexBuffers[1] = { vbv };
      mCommandList->IASetVertexBuffers(0, 1, vertexBuffers);
    • RTV와 달리 정점 버퍼 뷰에는 서술자 힙이 필요x
  • 정점 버퍼 뷰 대표 형식 D3D12_VERTEX_BUFFER_VIEW_DESC 구조체

      typedef struct D3D12_VERTEX_BUFFER_VIEW
      {
          D3D12_GPU_VIRTUAL_ADDRESS BufferLocation;
          UINT SizeInBytes;
          UINT StrideInBytes;
      }     D3D12_VERTEX_BUFFER_VIEW;
    • BufferLocation :
      • 생성할 뷰의 대상이 되는 정점 버퍼 자원의 가상 주소
      • ID3D12Resource::GetGPUVirtualAddress 메서드로 얻을 수 있음
    • SizeInBytes:
      • BufferLocation에서 시작하는 정점 버퍼의 크기(바이트 개수)
    • StrideInBytes :
      • 버퍼에 담긴 한 정점의 원소의 크기 (바이트 개수)
  • 정점 버퍼를 IA 파이프라인에 묶는 메서드

      void ID3D12GraphicsCommandList::IASetVertexBuffers(
          UINT StartSlot,
          UINT NumBuffers,
          const D3D12_VERTEX_BUFFER_VIEW *pViews );
    • StartSlot :
      • 시작슬롯, 첫쨰 정점 버퍼를 묶을 입력 슬롯의 색인
      • 총 16개 (0~15)
    • NumBuffers :
      • 입력 슬롯들에 묶을 정점 버퍼 개수
    • pViews :
      • 정점 버퍼 뷰 배열의 첫 원소를 가리키는 포인터
  • 정점들을 이용해 기본도형을 그리려면 ID3D12GraphicsCommandList::DrawInstanced 메서드 사용

      void ID3D12GraphicsCommandList::DrawInstanced(
          UINT VertexCountPerInstance,
          UINT InstanceCount,
          UINT StartVertexLocation,
          UINT StartInstanceLocation);
    • VertexCountPerInstance : 그리기에 사용할 정점들의 개수 (인스턴스 당)
    • InstanceCount : 그릴 인스턴스 개수
    • StartVertexLocation :
      • 정점 버퍼의 시작 위치를 지정;
      • 정점 버퍼의 색인들 중 이 그리기 호출에서 사용할 첫 색인( 0 기반)
    • StartInstanceLocation :
      • 첫 번째 인스턴스의 시작 위치 지정
      • 특정 인스턴스부터 렌더링을 시작 가능
  • 위상구조 상태 결정은 5.5.2 참고

    • cmdList->IASetPrivitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST)

6.3 색인과 색인 버퍼

  • 색인 버퍼

    • GPU가 색인들의 배열에 접근 할 수 있을려면, 색인들을 GPU 자원에 넣어 두어야 함
    • 서술자 힙을 필요로 하지 않음
  • 색인 버퍼 뷰 대표 형식

    • D3D12_INDEX_BUFFER_VIEW 구조체
        typedef struct D3D12_INDEX_BUFFER_VIEW
        {
            D3D12_GPU_VIRTUAL_ADDRESS BufferLocation;
            UINT SizeInBytes;
            DXGI_FORMAT Format;
        } D3D12_INDEX_BUFFER_VIEW;
      • BufferLoation
        • 뷰의 대상이 되는 정점 버퍼 자원의 가상 주소
        • ID3D12Resource::GetGPUVirtualAddress 메서드로 얻을 수 있음
      • SizeInBytes
        • BufferLocation에서 시작하는 색인 버퍼 크기 (바이트 개수)
      • Format
        • 색인 형식 (R16_INT, R32_UINT) 사용
        • 메모리와 대역폭을 절약할려면 16비트 사용
  • ID3D12CommandList::IASetIndexBuffer 메서드를 통해서 입력 조립기 단계에 묶음

  • 사각형 색인 버퍼 IA바인드 예시

      std::uint16_t indices[] = {
          // 앞면
          0, 1, 2,
          0, 2, 3,
          // 뒷면
          ...
      };
    
      const UINT ibByteSize = 36 * sizeof(std::uint_16_t); // 36개 정점
    
      ComPtr<ID3D12Resource> IndexBufferGPU = nullptr;
      ComPtr<ID3D12Resource> IndexBufferUploader = nullptr;
      IndexBufferGPU = d3dUtil::CreateDefaultBuffer(md3dDevice.Get(),
          mCommandList.Get(), indices, ibByteSize, IndexBufferUploader);
          // 사용자 정의 정점 버퍼 생성 메서드
      D3D12_INDEX_BUFFER_VIEW ibv;
      ibv.BufferLocation = IndexBufferGPU->GetGPUVirtualAddress();
      ibv.Foramt = DXGI_FORMAT_R16_UINT;
      ibv.SizeInBytes = ibByteSize;
    
      mCommandList->IASetIndexBuffer(&ibv);
  • 색인들을 이용해 기본도형을 그리려면 ID3D12GraphicsCommandList::DrawIndexedInstanced 메서드 사용

      void ID3D12GraphicsCommandList::DrawIndexedInstanced(
          UINT IndexCountPerInstance,
          UINT InstanceCount,
          UINT StartIndexLocation,
          INT BaseVertexLocation,
          UINT StartInstanceLocation);
    • IndexCountPerInstance : 그리기에 사용할 색인들의 개수 (인스턴스 당)
    • InstanceCount : 그릴 인스턴스 개수
    • StartIndexLocation :
      • 인덱스 버퍼의 시작 위치를 지정
      • 색인 버퍼의 색인들 중 이 그리기 호출에서 사용할 첫 색인( 0 기반)
    • BaseVertexLocation :
      • 정점 버퍼에서의 기준 오프셋 지정
      • 호출에 쓰이는 색인들에 더할 정수 값, 더한 결과를 최종 색인으로사용해서 정점 버퍼에서 정점 가져옴, 음수 값 허용
    • StartInstanceLocation :
      • 첫 번째 인스턴스의 시작 위치 지정
      • 특정 인스턴스부터 렌더링을 시작 가능

6.4 예제 정점 셰이더

  • 셰이더는 HLSL(High Level Shading Language)로 작성
  • 정점 셰이더는 하나의 함수
  • 매개 변수 4 개 중 앞의 2개는 입력 매개변수, 뒤의 2개는 출력 매개변수
    • 입력 매개변수는 입력 서명(input signature) 을 형성
      • 현재의 그리기 작업에 쓰이는 커스텀 정점 구조체의 멤버들에 대응
    • 출력 매개변수
      • 정점 셰이더의 출력을 파이프라인 다음 단계(기하셰이더, 픽셀셰이더)의 해당 입력에 대응 시키는 역할
      • SV_POSITION
        • SV는 SystemValue 라는 의미를 담고 있음
        • GPU는 SV_POSITION으로 명시된 데이터를 하드웨어 수준에서 특별히 처리
        • 렌더링 엔진과 DirectX가 정점의 위치 데이터를 명확히 처리할 수 있도록 보장
      • COLOR
        • D3D12_INPUT_ELEMENT_DESC 배열을 통해 지정한 이름
        • HLSL의 유효 식별자이기만 하면, 아무 이름이나 상관 x
  • HLSL에는 참조나 포인터가 없어, 함수가 여러개의 값을 돌려주려면 구조체 사용 or out이 지정된 출력 매개변수 사용
  • HLSL에서 함수는 항상 인라인화
      cbuffer    cbPerObject : register(b0)
      {
          float4x4 gWorldViewProj;    
      }
      void VS(
          float iPosL : POSITION,
          float iColor : COLOR,
          out float4 oPOSH : SV_POSTIION,   // SV는 SystemValue 라는 의미를 담고 있음
          out float4 oColor : COLOR)
      {
          // 동차 절단 공간 변환
          oPosH = mul(float4(iPosL, 1.0f), gWorldViewProj);
          // 정점 색상을 그대로 픽셀 셰이더에 전달
          oColor = iColor;
      }
    • mul 함수 :
      • HLSL 내장 함수
      • 벡터 대 행렬 곱셈 수행
    • gWorldViewProj :
      • 상수 버퍼라고 부르는 버퍼에 들어 있는 것
  • 기하 셰이더 사용 시, 동차 절단 공간 위치의 출력을 기하 셰이더에 미룰 수 있음
  • 원근 나누기는 나중에 하드웨어가 수행하기에, 투영 핼렬 곱하는 부분만 책임

6.4.1 입력 배치 서술과 입력 서명 연결

  • 입력 배치 서술
    • 파이프라인에 공급되는 정점들의 특성들과 정점 셰이더의 매개변수들 사이의 연관 관계 정의
    • 정점 셰이더가 기대하는 모든 입력을 제공하지못하면 오류 발생
      • 추가 공급은 되지만, 적은 건 용납 x
      • D3D에서 입력 레지스터 비트들의 재해석을 허용하기에 float -> int 의 공급을 허용하지만 VC++의 디버그 출력 창은 경고 메시지
        //--------------
        // C++ app code
        //--------------
        struct Vertex
        {
        XMFLOAT3 Pos;
        XMFLOAT4 Color;
        XMFLOAT3 Normal;
        };
        D3D12_INPUT_ELEMENT_DESC desc[] =
        {
        {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0};
           D3D12_INPUT_PER_VERTEX_DATA, 0 },
        { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12,
          D3D12_INPUT_PER_VERTEX_DATA, 0 },
        { "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 28,
          D3D12_INPUT_PER_VERTEX_DATA, 0 }
        //--------------
        // Vertex shader
        //--------------
        struct VertexIn
        {
        float3 PosL : POSITION;
        float4 Color : COLOR;
        };
        struct VertexOut
        {
        float4 PosH : SV_POSITION;
        float4 Color : COLOR;
        };

6.5 예제 픽셀 셰이더

  • 정점 셰이더와는 달리 픽셀 단편마다 실행 : 이전 단계(정점, 기하)에서 생성된 출력된 정점 값을 바탕으로 레스터라이저가 내부를 픽셀로 채우기때문

번외

모델 행렬 변환

  • 로컬 좌표와 이에 대한 크기,회전,이동에 대한 행렬이 주어질 떄, SRT(크기,회전,이동) 연산을 진행해 modelMatrix 만들어보기.

      mul( mul( mul(
      translationMatrix, 
          rotationMatrix), 
              scaleMatrix), 
                  float4(localPostion, 1.0f))
      XMMATRIX CalculateModelMatrix(XMVECTOR localPosition, XMVECTOR scale, XMVECTOR rotationQuat, XMVECTOR translation) 
      {
          // Scale Matrix
          XMMATRIX scaleMatrix = XMMatrixScalingFromVector(scale);
    
          // Rotation Matrix (Quaternion to Matrix)
          XMMATRIX rotationMatrix = XMMatrixRotationQuaternion(rotationQuat);
    
          // Translation Matrix
          XMMATRIX translationMatrix = XMMatrixTranslationFromVector(translation);
    
          // modelMatrix = T * R * S
          XMMATRIX modelMatrix = XMMatrixMultiply(XMMatrixMultiply(scaleMatrix, rotationMatrix), translationMatrix);
    
          return modelMatrix;
      }

    -> XMMatrixMultiply(A, B)mul(B,A)행렬 A x B 연산입니다
    -> XMMatrixMultipy(A, B)는 행렬을 사전에 계산하여 셰이더로 넘길 때 사용되며, HLSL에서 계산하는 것 보다 빠릅니다. (CPU 사용, 고성능 SIMD 연산);
    -> 일반적인 물체의 이동, 회전에 대해서 셰이더에 이미 계산된 modelMatrix를 전달합니다
    ->mul(B, A)는 셰이더 내에서 각 프레임마다 변환하는 실시간 계산에 적합합니다. (GPU 사용, 비용 비쌈); 물체 표면 물결 파동 등

DirectX 12의 대표적인 힙 타입 3가지

힙 종류 특징 주 용도
DEFAULT 힙 (기본 힙) GPU 전용, CPU 접근 불가 정적 자원 (Mesh, Texture 등)
UPLOAD 힙 (업로드 힙) CPU가 쓰기 가능, GPU가 읽음 매 프레임 갱신되는 상수버퍼, UBO
READBACK GPU → CPU 데이터 복사 GPU 결과 읽기 (디버깅, 피킹 등)

어트리뷰트란?

  • 소스 코드에 벤더에서 제공하는 정보나 옵션을 추가하는 메커니즘
  • c++11에서 대괄호방식[[어트리뷰트]] 형식으로 표준화

[[fallthrough]]

  • 일반적으로 swith 문의 case가 비어져 있지 않은 상태에서 break문이 없으면 fallthrough라고 함.
    • 이 떄, 컴파일러는 일반적으로 경고 알림
    • [[fallthrough]] 어트리뷰트로 알려주는 것을 권장
      switch (mode) {
        case Mode::Custom:
            v = 3;
            [[fallthrough]];
        case Mode::Standard:
      }

[[nodiscard]]

  • 함수가 호출 될 떄, 리턴 값에 대한 처리가 없으면 경고 메시지 출력
  • c++20부터는 이유 메시지 추가 가능
    [[nodiscard]] int func()
    {
      return 1;
    }
    //c++20 부터는 설명 추가가능
    [[nodiscard("Some explanation")]] int func() {...}
    int main() { func(); // 경고 메시지 출력 };

[[noreturn]]

  • 함수에 지정 시, 호출 지점으로 다시 돌아가지 않음
  • 프로세스, 스레드 종료, 익셉션 같이 뭔가 끝나게 만들 떄 사용
    [[noreturn]] void forceProgramTermination() { std::exit(1); }
    int main()
    {
      forceProgramTermination(); // 프로그램 종료
      cout << "이 출력은 실행되지 않음" << endl;
    }

[[deprecated]]

  • 지원 중단된 대상 지정에 사용
    • 더 이상 사용 권장하지 않는 오래된 함수, 변수 등 표시할 떄
  • [[deprecated("Unsafe")]] void func()

[[likely]], [[unlikely]]

  • c++20부터 지원
  • if, switch 문 수행 가능성 브랜치 표시로 최적화 도움을 주는 것이나, 컴파일러 예측 능력이 뛰어나기에 사용이 드뭄

'공부 > C++' 카테고리의 다른 글

(cpp17)string_view  (2) 2025.04.09
예외처리(simple)  (0) 2025.04.09
C++ IO스트림  (0) 2025.03.27
C++스트림  (0) 2025.03.26
decltype  (0) 2025.03.26

참조 : Windows Debugging Tips | 학습 페이지, https://youtu.be/n5Pqn_wofmY?si=VtGqepbz9E60_DGk

디버깅에 좋은 작성법

  • 참조를 계속 타고들어가는 함수 호출 코드의 경우,
  • 변수 여러개로 나눠서 각 결과값을 알 수 있게하는게 디버깅에 용이함
    • 어차피 Release 빌드 시에 중복 코드가 제거되기때문에 디버깅모드에서
      디버깅에 용이하게 작성하는게 좋음

디버그 포인터 배열 변수 값 보기

  • 변수 값 볼 수 있는 창에서 pointer 변수 pp,10 으로 바꾸면, p[10] 배열로 간주되서 볼 수 있다

브레이크 포인트

  • 일반 중단점
    • 해당 지점에 멈춤
  • 조건 중단점
    • 해당 조건을 만족하면 멈춤
  • 작업 중단점
    • 중단 지점에서 해당 작업을 실행
    • 디버그 출력을 별도로 주석이나 지우지 않고 값 출력 디버깅에 용이
  • 임시 중단점
    • 한 번 실행되고 난 후에는 실행 안되게 가능
  • 의존 중단점
    • 특정 중단점이 실행되면 활성화되지 않게 가능

호출 스택(Call Stack)

  • 단축키 : Ctrl + Alt + C
  • 용도 : 현재 중단점의 함수 콜 스택을 확인 가능

출력 창

  • 단축키 : Ctrl + Alt + O
  • 용도 : Debug 및 Log 등을 텍스트 출력, 스레드 종료 및 프로그램 진행사항 디버깅에 용이

패러렐 스택

  • 여러 스레드의 값을 동시에 비주얼적으로 용이하게 볼 수 있음

스냅샷

  • 게임 로딩 화면이 오래걸리거나 버벅일 떄, 메모리 문제가 의심될 경우 용이
  • 초기 상태 메모리를 스냅샷 찍고, 로딩화면에서 찍어서 메모리 증가량을 확인한다

메모리 누수 감지 기능사용

  • MFC 프로젝트가 아닌 경우, 기본적으로 비활성화 상태

  • 활성화 방법

      #define _CRTDBG_MAP_ALLOC
      #include <cstdlib>
      #include <crtdbg.h>
    
      #ifdef _DEBUG
          #ifdef DBG_NEW
              #define DBG_NEW new ( _NORMAL_BLOCK, __FILE__, __LINE__ )
              #define new DBG_NEW
          #endif
      #endif
    
      int main()
      {
          _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
      }

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

MSVC_CL 명령  (0) 2025.03.27
__stdcall  (0) 2025.03.27
XML 문서화  (0) 2025.03.27
SAL 주석(Static Analysis Language)  (0) 2025.03.27
단축키  (0) 2025.03.27

CL 명령 파일

  • 컴파일러 옵션 및 파일 이름을 포함하는 텍스트 파일
  • MSVC의 자동화 빌드할 떄, 이용
  • 명령줄의 문자 수가 운영 체제에서 지정한 제한인 1024를 초과 불가
  • 명령줄에 입력하거나 환경 변수를 사용하여 지정하는 옵션을 제공
    • 환경 변수 지정
    • 명령줄 구문 (아래 참조)
      CL [option...] file... [option | file]... [lib...] [@command-file] [/link link-opt...]
항목 의미
옵션 하나 이상의 CL 옵션입니다. 모든 옵션은 지정된 모든 원본 파일에 적용됩니다. 옵션은 슬래시(/) 또는 대시(-)로 지정됩니다. 옵션이 인수를 사용하는 경우 옵션의 설명은 옵션과 인수 사이에 공백이 허용되는지 여부를 설명합니다. 옵션 이름(/HELP 옵션 제외)은 대/소문자를 구분합니다. 자세한 내용은 CL 옵션 순서를 참조 하세요.
file 하나 이상의 원본 파일, .obj 파일 또는 라이브러리의 이름입니다. CL은 원본 파일을 컴파일하고 .obj 파일 및 라이브러리의 이름을 링커에 전달합니다. 자세한 내용은 CL 파일 이름 구문을 참조 하세요.
lib 하나 이상의 라이브러리 이름입니다. CL은 이러한 이름을 링커에 전달합니다.
command-file 여러 옵션 및 파일 이름을 포함하는 파일입니다. 자세한 내용은 CL 명령 파일을 참조 하세요.
link-opt 하나 이상의 MSVC 링커 옵션입니다. CL은 이러한 옵션을 링커에 전달합니다.

CL명령 순서

  • /link : 마지막으로 발생해야 하기에, 마지막 옵션으로 사용
  • 나머지 옵션은 어디든 사용 가능
    • 옵션은 명령줄의 모든 파일에 적용
  • 읽는 방향 Left -> Right 순서
  • 충돌 옵션 발생 시, 가장 우측 옵션 사용
    • command -option1 -option2 -option3 : 충돌 시, 가장 끝의 option3 사용`

CL 주의점

  • /link 옵션이 나타나면, 나머지 옵션들이 링커에 전달됨.
  • 기본적으로 1 줄에 옵션을 모두 사용, \를 사용해 2 줄 간의 결합 가능.
  • command file@{file_name}의 결합
    • CL /Ob2 @RESP MYAPP.C

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

디버깅 팁  (1) 2025.03.27
__stdcall  (0) 2025.03.27
XML 문서화  (0) 2025.03.27
SAL 주석(Static Analysis Language)  (0) 2025.03.27
단축키  (0) 2025.03.27

+ 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'); });