2023. 5. 17. 16:09ㆍDirectX12
갑자기 뜬금없이 번외가 올라와서 당황스러울 것이다. 다름이 아니고 프로젝트를 하다가 어이없는 상황을 만나게 되어서 이렇게 글을 쓰게 되었다. 인프런의 강의를 유료로 결제하면 강의 영상과 함께 예제 파일을 다운받을 수 있다. 거기에 있는 코드는 당연히 잘 돌아가야 한다. 실제로 다운받아서 실행시켰을 때, 잘 돌아갔다. 근데 문제는 내가 그걸 토대로 코드를 작성했는데 내가 짠 코드는 돌아가지 않는 것이었다.
코드가 돌아가지 않는 이유는 CreateSwapChain이라는 함수에서 SwapChain이 생성되지 않았기 때문이었다. 여러 개의 프로젝트를 만들고 예정에도 없던 틀린 그림 찾기를 하다가 깨닫게 된 것은 결국 내 실수로 발생되 버그였다는 것이다. 너무 어이없고 초보자들에게 발생하기 쉬운 실수인 것 같아서 적어보려고 한다.
Main.cpp
// TestMain.cpp : 애플리케이션에 대한 진입점을 정의합니다.
//
#include "pch.h"
#include "framework.h"
#include "TestMain.h"
#include "Game.h"
#define MAX_LOADSTRING 100
// 전역 변수:
WindowInfo GWindowInfo;
HINSTANCE hInst; // 현재 인스턴스입니다.
WCHAR szTitle[MAX_LOADSTRING]; // 제목 표시줄 텍스트입니다.
WCHAR szWindowClass[MAX_LOADSTRING]; // 기본 창 클래스 이름입니다.
// 이 코드 모듈에 포함된 함수의 선언을 전달합니다:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: 여기에 코드를 입력합니다.
// 전역 문자열을 초기화합니다.
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_TESTMAIN, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// 애플리케이션 초기화를 수행합니다:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_TESTMAIN));
MSG msg;
GWindowInfo.width = 800;
GWindowInfo.height = 600;
GWindowInfo.windowed = true;
unique_ptr<Game> game = make_unique<Game>();
game->Init(GWindowInfo);
// 기본 메시지 루프입니다:
while (true)
{
if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
break;
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
// TODO
game->Update();
}
return (int)msg.wParam;
}
//
// 함수: MyRegisterClass()
//
// 용도: 창 클래스를 등록합니다.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_TESTMAIN));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = nullptr;//MAKEINTRESOURCEW(IDC_TESTMAIN);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&wcex);
}
//
// 함수: InitInstance(HINSTANCE, int)
//
// 용도: 인스턴스 핸들을 저장하고 주 창을 만듭니다.
//
// 주석:
//
// 이 함수를 통해 인스턴스 핸들을 전역 변수에 저장하고
// 주 프로그램 창을 만든 다음 표시합니다.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // 인스턴스 핸들을 전역 변수에 저장합니다.
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
//GWindowInfo.hwnd = hWnd;
return TRUE;
}
//
// 함수: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// 용도: 주 창의 메시지를 처리합니다.
//
// WM_COMMAND - 애플리케이션 메뉴를 처리합니다.
// WM_PAINT - 주 창을 그립니다.
// WM_DESTROY - 종료 메시지를 게시하고 반환합니다.
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// 메뉴 선택을 구문 분석합니다:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: 여기에 hdc를 사용하는 그리기 코드를 추가합니다...
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// 정보 대화 상자의 메시지 처리기입니다.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
이 코드는 비주얼 스튜디오를 애플리게이션 프로젝트로 만들면 자동으로 생성되는 코드에 예제로 작성한 코드를 몇 자 추가한 것이다. 다 볼 필요는 없고 131번째 줄에 있는 즉, InitInstance함수에 주석이 있는 //GWindowInfo.hwnd = hWnd; 라는 부분이 보일 것이다. 저게 원래 없었다. 사실 이건 나도 좀 멍청했던 게 GWindowInfo에 4개의 변수가 들어있는데 hwnd에 값을 대입하는 부분이 wWinMain에 없다는 것을 진작에 이상하게 생각했어도 이렇게 틀린 그림 찾기를 하지 않아도 됐는데 그냥 "예제니까 맞겠지" 라는 안일한 생각이 이렇게 부메랑으로 돌아온 것이다.
이전 글에서 나무를 보지말고 숲을 보라는 식으로 하나하나 전부 짚으려 하지 말라고 글을 썼는데 앞으로는 적당히 하나하나 짚어나가는 자세가 있어야 할 것 같다. hwnd가 어떤 역할을 하는지 제대로 이해하고 어떤 값이 들어가야 그것이 유효한 값인지 이해하고 있었다면 이런 일은 일어나지 않았을테니 말이다.
결론: GWindowInfo.hwnd에 제대로 값을 대입하자. InitInstance함수에서 그것이 가능하다.
'DirectX12' 카테고리의 다른 글
DirectX12 개념 블로그 정주행 1회차 - Device와 CommandQueue (0) | 2023.06.08 |
---|---|
루키스 게임수학 정주행 3회차 - 장치 심화 (0) | 2023.06.06 |
루키스 게임수학 정주행 2회차 - 장치 초기화 (2) | 2023.05.10 |
루키스 게임수학 정주행 1회차 - 프로젝트 세팅 (0) | 2023.05.01 |
루키스 게임수학 정주행 - Intro (0) | 2023.04.17 |