[DX] Framework - Graphics

vector·2025년 11월 10일

Graphics

  • "DirectX11 초기화 + 프레임 시작/끝 관리"를 전담하는 그래픽스 매니저

수정코드

  • 기존 Game(Main)클래스에서 Graphics 클래스로 이동
    • 함수 : CreateDeviceAndSwapChaine(), CreateRenderTargetView(), SetViewport()
    • 변수 : hwnd, device, deviceContext, swapChain, renderTargetView, viewport, clearColor
Graphics.h

#pragma once
class Graphics
{
public:
	Graphics(HWND hwnd);
	~Graphics();

	void RenderBegin();
	void RenderEnd();

	ComPtr<ID3D11Device>GetDevice() { return _device; }
	ComPtr<ID3D11DeviceContext> GetDeviceContext() { return _deviceContext; }


private:
	void CreateDeviceAndSwapChain();
	void CreateRenderTargertView();
	void SetViewport();

private:
	HWND _hwnd;					// 윈도우 핸들

	// Device & SwapChain
	ComPtr<ID3D11Device> _device = nullptr;					//	GPU 디바이스 (리로스 생성 주체)
	ComPtr<ID3D11DeviceContext> _deviceContext = nullptr;	// 	디바이스 컨텍스트 (렌더링 명령 전달)
	ComPtr<IDXGISwapChain> _swapChain = nullptr;			//	spawChain (화면에 보여질 버퍼 교체 관리)

	// RTV 
	ComPtr<ID3D11RenderTargetView> _renderTargetView;		//	후면 버퍼를 그릴 수 있게 하는 뷰

	// Misc
	D3D11_VIEWPORT _viewport = { 0 };
	float _clearColor[4] = { 0.5f, 0.5f, 0.5f, 0.5f };
};

Graphics.cpp

#include "pch.h"
#include "Graphics.h"

Graphics::Graphics(HWND hwnd)
{
	_hwnd = hwnd;



	CreateDeviceAndSwapChain();
	CreateRenderTargertView();
	SetViewport();
}
Graphics::~Graphics()
{
}

void Graphics::CreateDeviceAndSwapChain()
{
	DXGI_SWAP_CHAIN_DESC desc;
	ZeroMemory(&desc, sizeof(desc)); // ::memset(&desc, 0, sizeof(desc));
	{
		// 출력 버퍼 크기를 윈도 크기에 맞춘다.
		desc.BufferDesc.Width = GWinSizeX;
		desc.BufferDesc.Height = GWinSizeY;
		desc.BufferDesc.RefreshRate.Numerator = 60; // 60Hz
		desc.BufferDesc.RefreshRate.Denominator = 1;
		desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; // 32bit RGBA (정규화)
		desc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
		desc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
		desc.SampleDesc.Count = 1;					// 멀티샘플링 비활성화 (MSAA x)
		desc.SampleDesc.Quality = 0;
		desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; // 렌더타겟으로 사용
		desc.BufferCount = 1;						// 후면 버퍼 개수 (레거시 Blt 모델 : 1이면 더블 버퍼링)
		desc.OutputWindow = _hwnd;					// 렌더링할 대상 윈도우
		desc.Windowed = TRUE;						// 창모드(True), 전체화면 (False)
		desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; // 스왑 후 버퍼 내용 폐기 (레거시 모델)
	}

	// comPtr 팁 : Get()은 원 포인터, GetAddressOf()는 포인터의 주소
	// 매개변수에서 *이면 Get(), **이면 GetAddressOf()
	//_device.Get(); // ID3D11Device*를 꺼내오고 싶을때
	//_device.GetAddressOf(); // ID3D11Device*의 주소를 꺼내오고 싶을때

	// device와 swapChain을 동시 생성(레거시 경로)
	HRESULT hr = ::D3D11CreateDeviceAndSwapChain(
		nullptr,						// 디폴트 어댑터
		D3D_DRIVER_TYPE_HARDWARE,		// 하드웨어 디바이스
		nullptr,						// 소프트웨어 래스터라이저(HAL 사용시 nullptr)
		0,								// 플래그(디버그 레이어는 디버그 빌드에서 D3D11_CREATE_DEVICE_DEBUG 권장)
		nullptr,						// 원하는 피처레벨 목록 (nullptr이면 내부 기본 목록)
		0,								// 피처레벨 개수
		D3D11_SDK_VERSION,				// SDK 버전
		&desc,							// 스왑체인 설명
		_swapChain.GetAddressOf(),		// [out] 스왑체인			// 화면에 보여질 버퍼 교체 관리
		_device.GetAddressOf(),			// [out] 디바이스			// 
		nullptr,						// [out] 선택된 피처레벨 (필요시 포인터 전달)
		_deviceContext.GetAddressOf()	// [out] 디바이스 컨텍스트	// 만든 리소스를 랜더링 명령으로 전달
	);

	CHECK(hr); // 성공 여부 매크로
}

void Graphics::CreateRenderTargertView()
{
	HRESULT hr;

	// 후면 버퍼(텍스처) 임시 포인터
	ComPtr<ID3D11Texture2D> backBuffer = nullptr;

	// swapChain에서 후면 버퍼에 해당하는 리소스를 ID3D11Textrue2D로 가져오고
	hr = _swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)backBuffer.GetAddressOf());
	CHECK(hr);

	// 가져온 후면버퍼를 묘사하는 RenderTargetView로 만들어줬고
	hr = _device->CreateRenderTargetView(backBuffer.Get(), nullptr, _renderTargetView.GetAddressOf());
	CHECK(hr);
}

void Graphics::SetViewport()
{
	_viewport.TopLeftX = 0.f;
	_viewport.TopLeftY = 0.f;
	_viewport.Width = static_cast<float>(GWinSizeX);
	_viewport.Height = static_cast<float>(GWinSizeY);
	_viewport.MinDepth = 0.f;
	_viewport.MaxDepth = 1.f;
}

void Graphics::RenderBegin()
{
	// 1) 출력 병합 단계(OM)에 렌더 타깃을 묶는다. (DSV 없음)
	_deviceContext->OMSetRenderTargets(1, _renderTargetView.GetAddressOf(), nullptr);
	// 2) 현재 렌더 타깃을 지정한 색으로 초기화
	_deviceContext->ClearRenderTargetView(_renderTargetView.Get(), _clearColor);
	// 3) 레스터라이저 단계(RS)에 사용할 뷰포트 설정
	_deviceContext->RSSetViewports(1, &_viewport);
}

void Graphics::RenderEnd()
{
	// 더블 버퍼링 : 후면 버퍼에 그린 내용을 화면(전면)으로 표시
	// 첫번째 인자 1은 VSync를 1로 맞춰 화면 주사율에 동기화 (티어링 방지)
	// 0으로 하면 즉시 표시 (티어링 발생 가능)
	HRESULT hr = _swapChain->Present(1, 0);
	CHECK(hr);
}


알면 좋은 부분들

  • 왜 이렇게 클래스로 감쌌을까
    • 초기화/정리 코드가 한 곳에 모여 관리가 쉬움
    • DepthStencil, 여러 RenderTarget, 멀티 뷰포트를 추가하게 되면 Graphics클래스만 수정하면 됨
    • 다른 프로젝트나 샘플에도 재사용 가능
  • ComPtr 사용 이유
    • Com은 Release()를 수동으로 호출 해야함.
    • ComPtr을 사용하면서 스코프를 벗어나면 Release() 자동 호출
  • Get() vs GetAddressOf()
    • 매개변수에서 *이면 Get(), **이면 GetAddressOf()
  • CHECK(hr)
    • DirectX는 대부분 HRESULT를 리턴
    • 사용을 안할 경우 어디에서 터졌는지 찾기 힘듦
    • CHECK(hr) 실패 시 assert, 로그 출력, 메시지 박스, 프로그램 종료 등 공동 처리
profile
게임 클라이언트 프로그래머 준비중 (공부 및 기록용)

0개의 댓글