From de4365126d3ca76809d266367a07425b645a607e Mon Sep 17 00:00:00 2001 From: Doyle Thai Date: Tue, 4 Apr 2017 16:27:49 +1000 Subject: [PATCH] Separate dchip update from rendering --- dchip-8.vcxproj | 6 +- dchip-8.vcxproj.filters | 2 +- src/Common.h | 1 - src/build.bat | 5 +- src/dchip-8.cpp | 241 ---------------------------------------- src/dchip8.cpp | 27 +++++ src/dchip8.h | 8 ++ src/dchip8_platform.h | 14 +++ src/unity_build.cpp | 3 + src/win32_dchip8.cpp | 233 ++++++++++++++++++++++++++++++++++++++ 10 files changed, 292 insertions(+), 248 deletions(-) delete mode 100644 src/dchip-8.cpp create mode 100644 src/dchip8.cpp create mode 100644 src/dchip8.h create mode 100644 src/dchip8_platform.h create mode 100644 src/unity_build.cpp create mode 100644 src/win32_dchip8.cpp diff --git a/dchip-8.vcxproj b/dchip-8.vcxproj index 464bf8a..169c81f 100644 --- a/dchip-8.vcxproj +++ b/dchip-8.vcxproj @@ -18,6 +18,9 @@ x64 + + + {01B5A474-4BA5-48A3-B645-7116ADF5585D} dchip8 @@ -119,9 +122,6 @@ true - - - diff --git a/dchip-8.vcxproj.filters b/dchip-8.vcxproj.filters index 9120b0a..c412874 100644 --- a/dchip-8.vcxproj.filters +++ b/dchip-8.vcxproj.filters @@ -15,7 +15,7 @@ - + Source Files diff --git a/src/Common.h b/src/Common.h index 5011a99..a1c8569 100644 --- a/src/Common.h +++ b/src/Common.h @@ -4,7 +4,6 @@ #include "stdint.h" #define LOCAL_PERSIST static -#define GLOBAL_VAR static #define FILE_SCOPE static typedef uint32_t u32; diff --git a/src/build.bat b/src/build.bat index d22a7c2..2d67660 100644 --- a/src/build.bat +++ b/src/build.bat @@ -2,7 +2,8 @@ @REM vcvarsall.bat to setup command-line compiler. @echo OFF -set ProjectName=dchip-8 +set ProjectName=dchip8 +set CompileEntryPoint=..\src\unity_build.cpp ctime -begin %ProjectName%.ctm @@ -47,7 +48,7 @@ REM incrmenetal:no, turn incremental builds off REM opt:ref, try to remove functions from libs that are referenced at all set LinkFlags=-incremental:no -opt:ref -cl %CompileFlags% ..\src\%ProjectName%.cpp %IncludeFlags% /link -subsystem:WINDOWS,5.1 %LinkLibraries% %LinkFlags% /nologo /OUT:"%ProjectName%.exe" +cl %CompileFlags% %CompileEntryPoint% %IncludeFlags% /link -subsystem:WINDOWS,5.1 %LinkLibraries% %LinkFlags% /nologo /OUT:"%ProjectName%.exe" popd ctime -end %ProjectName%.ctm diff --git a/src/dchip-8.cpp b/src/dchip-8.cpp deleted file mode 100644 index d85ac8d..0000000 --- a/src/dchip-8.cpp +++ /dev/null @@ -1,241 +0,0 @@ -#define UNICODE -#define _UNICODE - -#ifndef WIN32_LEAN_AND_MEAN - #define WIN32_LEAN_AND_MEAN -#endif - -#include "Common.h" - -#include -#include -#include - -FILE_SCOPE HBITMAP bitmap; -FILE_SCOPE BITMAPINFO bitmapInfo; - -const i32 WIDTH = 64; -const i32 HEIGHT = 32; -const i32 RESOLUTION = WIDTH * HEIGHT; -FILE_SCOPE void *bitmapMemory; - -FILE_SCOPE bool globalRunning = false; - -FILE_SCOPE LRESULT CALLBACK mainWindowProcCallback(HWND window, UINT msg, - WPARAM wParam, LPARAM lParam) -{ - LRESULT result = 0; - switch (msg) - { - case WM_CLOSE: - case WM_DESTROY: - { - globalRunning = false; - } - break; - - default: - { - result = DefWindowProc(window, msg, wParam, lParam); - } - break; - } - - return result; -} - -GLOBAL_VAR LARGE_INTEGER globalQueryPerformanceFrequency; -inline FILE_SCOPE f32 getTimeFromQueryPerfCounter(LARGE_INTEGER start, - LARGE_INTEGER end) -{ - f32 result = (f32)(end.QuadPart - start.QuadPart) / - globalQueryPerformanceFrequency.QuadPart; - return result; -} - -inline FILE_SCOPE LARGE_INTEGER getWallClock() -{ - LARGE_INTEGER result; - QueryPerformanceCounter(&result); - - return result; -} - -#define ErrorBox(text, title) MessageBox(nullptr, text, title, MB_OK); -int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, - LPSTR lpCmdLine, int nShowCmd) -{ - WNDCLASSEX wc = - { - sizeof(WNDCLASSEX), - CS_HREDRAW | CS_VREDRAW | CS_OWNDC, - mainWindowProcCallback, - 0, // int cbClsExtra - 0, // int cbWndExtra - hInstance, - LoadIcon(NULL, IDI_APPLICATION), - LoadCursor(NULL, IDC_ARROW), - GetSysColorBrush(COLOR_3DFACE), - L"", // LPCTSTR lpszMenuName - L"dchip-8Class", - NULL, // HICON hIconSm - }; - - if (!RegisterClassEx(&wc)) { - ErrorBox(L"RegisterClassEx() failed.", nullptr); - return -1; - } - - globalRunning = true; - - // NOTE: Regarding Window Sizes - // If you specify a window size, e.g. 800x600, Windows regards the 800x600 - // region to be inclusive of the toolbars and side borders. So in actuality, - // when you blit to the screen blackness, the area that is being blitted to - // is slightly smaller than 800x600. Windows provides a function to help - // calculate the size you'd need by accounting for the window style. - RECT rect = {}; - rect.right = 128; - rect.bottom = 64; - - DWORD windowStyle = - WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; - AdjustWindowRect(&rect, windowStyle, FALSE); - - HWND mainWindow = CreateWindowEx( - WS_EX_COMPOSITED, wc.lpszClassName, L"dchip-8", windowStyle, - CW_USEDEFAULT, CW_USEDEFAULT, rect.right - rect.left, - rect.bottom - rect.top, nullptr, nullptr, hInstance, nullptr); - - if (!mainWindow) - { - ErrorBox(L"CreateWindowEx() failed.", nullptr); - return -1; - } - - QueryPerformanceFrequency(&globalQueryPerformanceFrequency); - LARGE_INTEGER startFrameTime; - const f32 targetFramesPerSecond = 16.0f; - f32 targetSecondsPerFrame = 1 / targetFramesPerSecond; - f32 frameTimeInS = 0.0f; - - { - BITMAPINFOHEADER header = {}; - header.biSize = sizeof(BITMAPINFOHEADER); - header.biWidth = WIDTH; - header.biHeight = HEIGHT; - header.biPlanes = 1; - header.biBitCount = 32; - header.biCompression = BI_RGB; // uncompressed bitmap - header.biSizeImage = 0; - header.biXPelsPerMeter = 0; - header.biYPelsPerMeter = 0; - header.biClrUsed = 0; - header.biClrImportant = 0; - - bitmapInfo.bmiHeader = header; - - const i32 numPixels = header.biWidth * header.biHeight; - const i32 bytesPerPixel = header.biBitCount / 8; -#if 0 - bitmapMemory = calloc(1, numPixels * bytesPerPixel); - - if (!bitmapMemory) - { - ErrorBox(L"malloc() failed.", nullptr); - return -1; - } - -#else - HDC deviceContext = GetDC(mainWindow); - bitmap = CreateDIBSection(deviceContext, &bitmapInfo, DIB_RGB_COLORS, - &bitmapMemory, NULL, NULL); - ReleaseDC(mainWindow, deviceContext); - - if (!bitmapMemory) - { - ErrorBox(L"CreateDIBSection() failed.", nullptr); - return -1; - } -#endif - - u32 *bitmapBuffer = (u32 *)bitmapMemory; - for (i32 i = 0; i < numPixels; i++) - { - // NOTE: Win32 AlphaBlend requires the RGB components to be - // premultiplied with alpha. - f32 normA = 1.0f; - f32 normR = (normA * 0.0f); - f32 normG = (normA * 0.0f); - f32 normB = (normA * 1.0f); - - u8 r = (u8)(normR * 255.0f); - u8 g = (u8)(normG * 255.0f); - u8 b = (u8)(normB * 255.0f); - u8 a = (u8)(normA * 255.0f); - - u32 color = (a << 24) | (r << 16) | (g << 8) | (b << 0); - bitmapBuffer[i] = color; - } - } - - MSG msg; - while (globalRunning) - { - startFrameTime = getWallClock(); - while (PeekMessage(&msg, mainWindow, 0, 0, PM_REMOVE)) - { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - - RECT clientRect = {}; - GetClientRect(mainWindow, &clientRect); - - LONG clientWidth = clientRect.right - clientRect.left; - LONG clientHeight = clientRect.bottom - clientRect.top; - - BLENDFUNCTION blend = {}; - blend.BlendOp = AC_SRC_OVER; - blend.SourceConstantAlpha = 255; - blend.AlphaFormat = AC_SRC_ALPHA; - - HDC deviceContext = GetDC(mainWindow); - HDC alphaBlendDC = CreateCompatibleDC(deviceContext); - SelectObject(alphaBlendDC, bitmap); - - AlphaBlend(deviceContext, 0, 0, WIDTH, HEIGHT, alphaBlendDC, 0, 0, - WIDTH, HEIGHT, blend); - StretchBlt(deviceContext, 0, 0, clientWidth, clientHeight, - deviceContext, 0, 0, WIDTH, HEIGHT, SRCCOPY); - - DeleteDC(alphaBlendDC); - ReleaseDC(mainWindow, deviceContext); - - //////////////////////////////////////////////////////////////////////// - // Frame Limiting - //////////////////////////////////////////////////////////////////////// - LARGE_INTEGER endWorkTime = getWallClock(); - f32 workTimeInS = - getTimeFromQueryPerfCounter(startFrameTime, endWorkTime); - if (workTimeInS < targetSecondsPerFrame) - { - DWORD remainingTimeInMs = - (DWORD)((targetSecondsPerFrame - workTimeInS) * 1000); - Sleep(remainingTimeInMs); - } - - LARGE_INTEGER endFrameTime = getWallClock(); - frameTimeInS = - getTimeFromQueryPerfCounter(startFrameTime, endFrameTime); - f32 msPerFrame = 1000.0f * frameTimeInS; - - wchar_t windowTitleBuffer[128] = {}; - _snwprintf_s(windowTitleBuffer, ARRAY_COUNT(windowTitleBuffer), - ARRAY_COUNT(windowTitleBuffer), L"dchip-8 | %5.2f ms/f", - msPerFrame); - SetWindowText(mainWindow, windowTitleBuffer); - } - - return 0; -} diff --git a/src/dchip8.cpp b/src/dchip8.cpp new file mode 100644 index 0000000..b68ad88 --- /dev/null +++ b/src/dchip8.cpp @@ -0,0 +1,27 @@ +#include "common.h" +#include "dchip8_platform.h" + +void dchip8_update(PlatformRenderBuffer *renderBuffer) +{ + ASSERT(renderBuffer.bytesPerPixel == 4); + + const i32 numPixels = renderBuffer->width * renderBuffer->height; + u32 *bitmapBuffer = (u32 *)renderBuffer->memory; + for (i32 i = 0; i < numPixels; i++) + { + // NOTE: Win32 AlphaBlend requires the RGB components to be + // premultiplied with alpha. + f32 normA = 1.0f; + f32 normR = (normA * 0.0f); + f32 normG = (normA * 0.0f); + f32 normB = (normA * 1.0f); + + u8 r = (u8)(normR * 255.0f); + u8 g = (u8)(normG * 255.0f); + u8 b = (u8)(normB * 255.0f); + u8 a = (u8)(normA * 255.0f); + + u32 color = (a << 24) | (r << 16) | (g << 8) | (b << 0); + bitmapBuffer[i] = color; + } +} diff --git a/src/dchip8.h b/src/dchip8.h new file mode 100644 index 0000000..4ed6d95 --- /dev/null +++ b/src/dchip8.h @@ -0,0 +1,8 @@ +#ifndef DCHIP8_H +#define DCHIP8_H + +#include "dchip8_platform.h" + +void dchip8_update(PlatformRenderBuffer *renderBuffer); + +#endif diff --git a/src/dchip8_platform.h b/src/dchip8_platform.h new file mode 100644 index 0000000..cfa1d20 --- /dev/null +++ b/src/dchip8_platform.h @@ -0,0 +1,14 @@ +#ifndef DCHIP_8_PLATFORM +#define DCHIP_8_PLATFORM + +#include "Common.h" + +typedef struct PlatformRenderBuffer +{ + i32 width; + i32 height; + i32 bytesPerPixel; + void *memory; +} PlatformRenderBuffer; + +#endif diff --git a/src/unity_build.cpp b/src/unity_build.cpp new file mode 100644 index 0000000..223d70b --- /dev/null +++ b/src/unity_build.cpp @@ -0,0 +1,3 @@ +#include "win32_dchip8.cpp" + +#include "dchip8.cpp" diff --git a/src/win32_dchip8.cpp b/src/win32_dchip8.cpp new file mode 100644 index 0000000..563b385 --- /dev/null +++ b/src/win32_dchip8.cpp @@ -0,0 +1,233 @@ +#define UNICODE +#define _UNICODE + +#ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN +#endif + +#include +#include +#include + +#include "common.h" +#include "dchip8.h" +#include "dchip8_platform.h" + +FILE_SCOPE bool globalRunning = false; +FILE_SCOPE LARGE_INTEGER globalQueryPerformanceFrequency; +#define win32_error_box(text, title) MessageBox(nullptr, text, title, MB_OK); + +FILE_SCOPE LRESULT CALLBACK win32_main_proc_callback(HWND window, UINT msg, + WPARAM wParam, + LPARAM lParam) +{ + LRESULT result = 0; + switch (msg) + { + case WM_CLOSE: + case WM_DESTROY: + { + globalRunning = false; + } + break; + + default: + { + result = DefWindowProc(window, msg, wParam, lParam); + } + break; + } + + return result; +} + +inline FILE_SCOPE f32 win32_query_perf_counter_get_time(LARGE_INTEGER start, + LARGE_INTEGER end) +{ + f32 result = (f32)(end.QuadPart - start.QuadPart) / + globalQueryPerformanceFrequency.QuadPart; + return result; +} + +inline FILE_SCOPE LARGE_INTEGER win32_query_perf_counter_time() +{ + LARGE_INTEGER result; + QueryPerformanceCounter(&result); + + return result; +} + +typedef struct Win32RenderBitmap +{ + BITMAPINFO info; + HBITMAP handle; + i32 width; + i32 height; + i32 bytesPerPixel; + void *memory; +} Win32RenderBitmap; + +FILE_SCOPE void win32_display_render_bitmap(Win32RenderBitmap renderBitmap, + HDC deviceContext, LONG width, + LONG height) +{ + BLENDFUNCTION blend = {}; + blend.BlendOp = AC_SRC_OVER; + blend.SourceConstantAlpha = 255; + blend.AlphaFormat = AC_SRC_ALPHA; + + HDC alphaBlendDC = CreateCompatibleDC(deviceContext); + SelectObject(alphaBlendDC, renderBitmap.handle); + + AlphaBlend(deviceContext, 0, 0, width, height, alphaBlendDC, 0, 0, + renderBitmap.width, renderBitmap.height, blend); + StretchBlt(deviceContext, 0, 0, width, height, deviceContext, 0, 0, + renderBitmap.width, renderBitmap.height, SRCCOPY); + + DeleteDC(alphaBlendDC); +} + +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, + LPSTR lpCmdLine, int nShowCmd) +{ + WNDCLASSEX wc = + { + sizeof(WNDCLASSEX), + CS_HREDRAW | CS_VREDRAW | CS_OWNDC, + win32_main_proc_callback, + 0, // int cbClsExtra + 0, // int cbWndExtra + hInstance, + LoadIcon(NULL, IDI_APPLICATION), + LoadCursor(NULL, IDC_ARROW), + NULL, // GetSysColorBrush(COLOR_3DFACE), + L"", // LPCTSTR lpszMenuName + L"dchip-8Class", + NULL, // HICON hIconSm + }; + + if (!RegisterClassEx(&wc)) { + win32_error_box(L"RegisterClassEx() failed.", nullptr); + return -1; + } + + // NOTE: Regarding Window Sizes + // If you specify a window size, e.g. 800x600, Windows regards the 800x600 + // region to be inclusive of the toolbars and side borders. So in actuality, + // when you blit to the screen blackness, the area that is being blitted to + // is slightly smaller than 800x600. Windows provides a function to help + // calculate the size you'd need by accounting for the window style. + RECT rect = {}; + rect.right = 128; + rect.bottom = 64; + + DWORD windowStyle = + WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; + AdjustWindowRect(&rect, windowStyle, FALSE); + + HWND mainWindow = CreateWindowEx( + WS_EX_COMPOSITED, wc.lpszClassName, L"dchip-8", windowStyle, + CW_USEDEFAULT, CW_USEDEFAULT, rect.right - rect.left, + rect.bottom - rect.top, nullptr, nullptr, hInstance, nullptr); + + if (!mainWindow) + { + win32_error_box(L"CreateWindowEx() failed.", nullptr); + return -1; + } + + Win32RenderBitmap renderBitmap = {}; + { // Initialise the renderbitmap + BITMAPINFOHEADER header = {}; + header.biSize = sizeof(BITMAPINFOHEADER); + header.biWidth = 64; + header.biHeight = 32; + header.biPlanes = 1; + header.biBitCount = 32; + header.biCompression = BI_RGB; // uncompressed bitmap + header.biSizeImage = 0; + header.biXPelsPerMeter = 0; + header.biYPelsPerMeter = 0; + header.biClrUsed = 0; + header.biClrImportant = 0; + + renderBitmap.info.bmiHeader = header; + renderBitmap.width = header.biWidth; + renderBitmap.height = header.biHeight; + renderBitmap.bytesPerPixel = header.biBitCount / 8; + ASSERT(renderBitmap.bytesPerPixel >= 1); + + HDC deviceContext = GetDC(mainWindow); + renderBitmap.handle = + CreateDIBSection(deviceContext, &renderBitmap.info, DIB_RGB_COLORS, + &renderBitmap.memory, NULL, NULL); + ReleaseDC(mainWindow, deviceContext); + } + + if (!renderBitmap.memory) + { + win32_error_box(L"CreateDIBSection() failed.", nullptr); + return -1; + } + + //////////////////////////////////////////////////////////////////////////// + // Update Loop + //////////////////////////////////////////////////////////////////////////// + QueryPerformanceFrequency(&globalQueryPerformanceFrequency); + const f32 TARGET_FRAMES_PER_S = 60.0f; + f32 targetSecondsPerFrame = 1 / TARGET_FRAMES_PER_S; + f32 frameTimeInS = 0.0f; + globalRunning = true; + + while (globalRunning) + { + LARGE_INTEGER startFrameTime = win32_query_perf_counter_time(); + + MSG msg; + while (PeekMessage(&msg, mainWindow, 0, 0, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + //////////////////////////////////////////////////////////////////////// + // Rendering + //////////////////////////////////////////////////////////////////////// + PlatformRenderBuffer renderBuffer = {}; + renderBuffer.memory = renderBitmap.memory; + renderBuffer.height = renderBitmap.height; + renderBuffer.width = renderBitmap.width; + dchip8_update(&renderBuffer); + + HDC deviceContext = GetDC(mainWindow); + win32_display_render_bitmap(renderBitmap, deviceContext, clientWidth, + clientHeight); + ReleaseDC(mainWindow, deviceContext); + + //////////////////////////////////////////////////////////////////////// + // Frame Limiting + //////////////////////////////////////////////////////////////////////// + LARGE_INTEGER endWorkTime = win32_query_perf_counter_time(); + f32 workTimeInS = + win32_query_perf_counter_get_time(startFrameTime, endWorkTime); + if (workTimeInS < targetSecondsPerFrame) + { + DWORD remainingTimeInMs = + (DWORD)((targetSecondsPerFrame - workTimeInS) * 1000); + Sleep(remainingTimeInMs); + } + + LARGE_INTEGER endFrameTime = win32_query_perf_counter_time(); + frameTimeInS = + win32_query_perf_counter_get_time(startFrameTime, endFrameTime); + f32 msPerFrame = 1000.0f * frameTimeInS; + + wchar_t windowTitleBuffer[128] = {}; + _snwprintf_s(windowTitleBuffer, ARRAY_COUNT(windowTitleBuffer), + ARRAY_COUNT(windowTitleBuffer), L"dchip-8 | %5.2f ms/f", + msPerFrame); + SetWindowText(mainWindow, windowTitleBuffer); + } + + return 0; +}