여기까지의 작업내용: https://github.com/hwi-middle/HimchanSoftwareRenderer/tree/dcd6f06b4da884d4ea3b921220752ecbfb601979
WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
CK 렌더러의 프리컴파일 헤더를 보다보니 이런 부분이 있어서 MSDN을 찾아보았다.
암호화, DDE, RPC, 셸 및 Windows 소켓과 같은 API를 제외하는 WIN32_LEAN_AND_MEAN 정의합니다.
그러니까, 암호화나 소켓같은 부분을 제외해주는 매크로라는 것이다. 이를 통해 빌드 시간의 단축을 기대할 수 있는 것이다. 이름이 되게... 직관적이다. Lean & Mean이라니...
#ifndef WIN32_LEAN_AND_MEAN
#include <cderr.h>
#include <dde.h>
#include <ddeml.h>
#include <dlgs.h>
#ifndef _MAC
#include <lzexpand.h>
#include <mmsystem.h>
#include <nb30.h>
#include <rpc.h>
#endif
#include <shellapi.h>
#ifndef _MAC
#include <winperf.h>
#include <winsock.h>
#endif
#ifndef NOCRYPT
#include <wincrypt.h>
#include <winefs.h>
#include <winscard.h>
#endif
#ifndef NOGDI
#ifndef _MAC
#include <winspool.h>
#ifdef INC_OLE1
#include <ole.h>
#else
#include <ole2.h>
#endif /* !INC_OLE1 */
#endif /* !MAC */
#include <commdlg.h>
#endif /* !NOGDI */
#endif /* WIN32_LEAN_AND_MEAN */
실제로 Windows.h를 직접 살펴보면 이렇게 선언되어있다. 저 많은 뭉치가 WIN32_LEAN_AND_MEAN 매크로의 #ifndef에 묶여있는거라서 WIN32_LEAN_AND_MEAN가 선언되면 다 생략된다.
다만 나의 렌더러가 딱히 빌드 시간을 단축할만한 프로젝트도 아니긴 하다. 하지만... 이런게 또 굉장히 고수같아 보이고... 달리 프로젝트에 side-effect가 있을만한 것도 아니라서 나도 WIN32_LEAN_AND_MEAN라는 매크로를 선언했다 ㅎㅎ
Resize 함수 구현
하핫, Resize 대응법은 알고 있다! WM_SIZE 메시지를 통해 구현할 수 있으니까.
그러면 Application의 Resize 함수부터 구현하자.
void Application::Resize(uint32 InWidth, uint32 InHeight)
{
Width = InWidth;
Height = InHeight;
Renderer->Resize(Width, Height);
}
그런 다음 Renderer의 Resize 함수도 구현해야한다.
void WinRenderer::Resize(uint32 InWidth, uint32 InHeight)
{
Release();
Initialize(InWidth, InHeight);
}
일단 DC 같은거 다 Release 해주고 다시 Initialize를 해준다.
이제 Application의 Resize를 호출해주는 것으로 내부에 저장하고 있던 Width와 Height 값을 수정하고 새로운 화면에 맞게 그릴 수 있다.
WM_SIZE 메시지 처리
이어서, WM_SIZE 메시지 처리를 진행해보자.
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
switch (iMessage)
{
case WM_SIZE:
{
uint32 Width = LOWORD(lParam);
uint32 Height = HIWORD(lParam);
// ...
// 여기서 Resize 어떻게 하지
어라, 여기는 WndProc이고 Application의 인스턴스는 WinMain에 있어서 접근할 수가 없다!
CK렌더러를 보니 std::function으로 일종의 콜백을 만들어서 대응하고 있어서 나도 좀 따라해봤다.
std::function<void(uint32 Width, uint32 Height)> g_OnResize;
g_라는 prefix가 있는걸 보면 알 수 있겠지만, global scope에 std::function을 하나 만들었다.
g_OnResize = std::bind(&Application::Resize, &appliction, std::placeholders::_1, std::placeholders::_2);
그런다음 std::bind를 통해 바인딩 해주었다. CK렌더러에서는 람다캡처를 통해서 함수를 호출하고 있었는데, 나는 std::function에 대해 찾아보다가 알게된 std::bind를 사용해보았다.
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
static bool bIsSizeMove = false;
switch (iMessage)
{
case WM_SIZE:
{
uint32 Width = LOWORD(lParam);
uint32 Height = HIWORD(lParam);
if (g_OnResize)
{
g_OnResize(Width, Height);
}
}
//...
}
// ...
}
그럼 이렇게 작성할 수 있다.
Resize 중의 렌더링 처리
그런데 문제가 있다. 창을 Resize 하는 중에는 이렇게 화면이 허옇게 뜬다는 것.
std::function<void(void)> g_Tick;
// ...
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdParam, int nCmdShow)
{
// ...
g_Tick = std::bind(&Application::Tick, &appliction);
// ...
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
switch (iMessage)
{
case WM_SIZE:
{
uint32 Width = LOWORD(lParam);
uint32 Height = HIWORD(lParam);
if (g_OnResize)
{
g_OnResize(Width, Height);
}
if (g_Tick)
{
g_Tick();
}
return 0;
}
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return (DefWindowProc(hWnd, iMessage, wParam, lParam));
}
그래서 WM_SIZE 메시지를 처리하는 동안에는 WndProc에서 Tick을 호출하도록 해주었다.
그런데 이렇게 하면 Resize 상태에서 마우스를 클릭한 채 창 크기를 조절하지 않고 가만히 있으면 Tick이 호출되지 않는 문제가 있다.
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
switch (iMessage)
{
case WM_SIZE:
{
uint32 Width = LOWORD(lParam);
uint32 Height = HIWORD(lParam);
if (g_OnResize)
{
g_OnResize(Width, Height);
}
return 0;
}
case WM_ENTERSIZEMOVE:
SetTimer(hWnd, 1, USER_TIMER_MINIMUM, NULL);
return 0;
case WM_EXITSIZEMOVE:
KillTimer(hWnd, 1);
return 0;
case WM_TIMER:
g_Tick();
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return (DefWindowProc(hWnd, iMessage, wParam, lParam));
}
그래서 최종적으로는 이렇게 구현했다. Resize 시작 시 Timer를 등록하고, 종료 시 Timer를 제거한다. WM_TIMER 메시지에서 타이머 핸들을 확인하지 않는 부분은 조만간 수정하겠다.
여기서 또 문제는, 이렇게 했더니 Resize 시 깜빡임이 심해졌다. 마침 성능 문제도 있었겠다, 최적화를 진행해보도록 하겠다. 일단 Delta Time을 구현하고 나서 성능 최적화를 진행할 예정이다. 자세한 문제와 해결 과정은 해당 게시물에서 다루도록 하겠다.
https://youtu.be/I3T1Tzm0B98?si=QDfDDdrvJ5Bpc_sC
그러면 영상을 첨부하고 마무리. 아직 갈 길이 멀다. 키 입력도 구현해야하고...
'개발일지 > 소프트 렌더러' 카테고리의 다른 글
소프트웨어 렌더러 만들기 - 11 (메모리 누수 해결 및 성능 최적화) (1) | 2024.07.17 |
---|---|
소프트웨어 렌더러 만들기 - 10 (DeltaTime 및 fps 측정 구현) (0) | 2024.07.17 |
소프트웨어 렌더러 만들기 - 8 (렌더러 구조 및 더블 버퍼링) (0) | 2024.07.14 |
소프트웨어 렌더러 만들기 - 7 (직선 그리기) (0) | 2024.02.25 |
소프트웨어 렌더러 만들기 - 6 (다양한 크기의 Vector와 Square Matrix) (0) | 2024.02.20 |