255 lines
9.7 KiB
C++
255 lines
9.7 KiB
C++
#include "WickedEngine.h"
|
|
#include "voxel/VoxelRenderer.h"
|
|
#include "wiHelper.h"
|
|
#include <fstream>
|
|
#include <DbgHelp.h>
|
|
#pragma comment(lib, "dbghelp.lib")
|
|
|
|
// ── BVLE Voxels - Prototype Application ─────────────────────────
|
|
// Wicked Engine based voxel engine prototype for performance validation.
|
|
|
|
// ── Crash handler: writes stack trace + minidump on unhandled exception ──
|
|
static LONG WINAPI CrashHandler(EXCEPTION_POINTERS* ep) {
|
|
std::ofstream crash("bvle_crash.log", std::ios::trunc);
|
|
crash << "=== BVLE CRASH REPORT ===" << std::endl;
|
|
|
|
DWORD code = ep->ExceptionRecord->ExceptionCode;
|
|
PVOID addr = ep->ExceptionRecord->ExceptionAddress;
|
|
crash << "Exception code: 0x" << std::hex << code << std::endl;
|
|
crash << "Crash address: 0x" << addr << std::endl;
|
|
|
|
if (code == EXCEPTION_ACCESS_VIOLATION && ep->ExceptionRecord->NumberParameters >= 2) {
|
|
ULONG_PTR type = ep->ExceptionRecord->ExceptionInformation[0];
|
|
ULONG_PTR target = ep->ExceptionRecord->ExceptionInformation[1];
|
|
crash << (type == 0 ? "Reading" : "Writing") << " address: 0x" << std::hex << target << std::endl;
|
|
}
|
|
|
|
HANDLE process = GetCurrentProcess();
|
|
HANDLE thread = GetCurrentThread();
|
|
SymInitialize(process, NULL, TRUE);
|
|
|
|
CONTEXT* ctx = ep->ContextRecord;
|
|
STACKFRAME64 frame = {};
|
|
frame.AddrPC.Offset = ctx->Rip; frame.AddrPC.Mode = AddrModeFlat;
|
|
frame.AddrFrame.Offset = ctx->Rbp; frame.AddrFrame.Mode = AddrModeFlat;
|
|
frame.AddrStack.Offset = ctx->Rsp; frame.AddrStack.Mode = AddrModeFlat;
|
|
|
|
crash << "\nStack trace:" << std::endl;
|
|
for (int i = 0; i < 32; i++) {
|
|
if (!StackWalk64(IMAGE_FILE_MACHINE_AMD64, process, thread, &frame,
|
|
ctx, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL))
|
|
break;
|
|
char symbolBuf[sizeof(SYMBOL_INFO) + 256];
|
|
SYMBOL_INFO* symbol = (SYMBOL_INFO*)symbolBuf;
|
|
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
|
|
symbol->MaxNameLen = 255;
|
|
DWORD64 disp64 = 0;
|
|
crash << " [" << i << "] 0x" << std::hex << frame.AddrPC.Offset;
|
|
if (SymFromAddr(process, frame.AddrPC.Offset, &disp64, symbol))
|
|
crash << " " << symbol->Name << " +0x" << disp64;
|
|
IMAGEHLP_LINE64 line = {}; line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
|
|
DWORD disp32 = 0;
|
|
if (SymGetLineFromAddr64(process, frame.AddrPC.Offset, &disp32, &line))
|
|
crash << " (" << line.FileName << ":" << std::dec << line.LineNumber << ")";
|
|
crash << std::endl;
|
|
}
|
|
|
|
HANDLE dumpFile = CreateFileA("bvle_crash.dmp", GENERIC_WRITE, 0, NULL,
|
|
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (dumpFile != INVALID_HANDLE_VALUE) {
|
|
MINIDUMP_EXCEPTION_INFORMATION mei;
|
|
mei.ThreadId = GetCurrentThreadId();
|
|
mei.ExceptionPointers = ep;
|
|
mei.ClientPointers = FALSE;
|
|
MiniDumpWriteDump(process, GetCurrentProcessId(), dumpFile,
|
|
MiniDumpWithDataSegs, &mei, NULL, NULL);
|
|
CloseHandle(dumpFile);
|
|
}
|
|
|
|
crash.close();
|
|
SymCleanup(process);
|
|
return EXCEPTION_EXECUTE_HANDLER;
|
|
}
|
|
|
|
static wi::Application application;
|
|
static voxel::VoxelRenderPath renderPath;
|
|
|
|
int APIENTRY wWinMain(
|
|
_In_ HINSTANCE hInstance,
|
|
_In_opt_ HINSTANCE hPrevInstance,
|
|
_In_ LPWSTR lpCmdLine,
|
|
_In_ int nCmdShow)
|
|
{
|
|
SetUnhandledExceptionFilter(CrashHandler);
|
|
|
|
// Win32 window setup
|
|
static auto WndProc = [](HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) -> LRESULT
|
|
{
|
|
switch (message)
|
|
{
|
|
case WM_SIZE:
|
|
case WM_DPICHANGED:
|
|
if (application.is_window_active)
|
|
application.SetWindow(hWnd);
|
|
break;
|
|
case WM_CHAR:
|
|
switch (wParam)
|
|
{
|
|
case VK_BACK:
|
|
wi::gui::TextInputField::DeleteFromInput();
|
|
break;
|
|
case VK_RETURN:
|
|
break;
|
|
default:
|
|
wi::gui::TextInputField::AddInput((const wchar_t)wParam);
|
|
break;
|
|
}
|
|
break;
|
|
case WM_INPUT:
|
|
wi::input::rawinput::ParseMessage((void*)lParam);
|
|
break;
|
|
case WM_KILLFOCUS:
|
|
application.is_window_active = false;
|
|
break;
|
|
case WM_SETFOCUS:
|
|
application.is_window_active = true;
|
|
break;
|
|
case WM_DESTROY:
|
|
PostQuitMessage(0);
|
|
break;
|
|
default:
|
|
return DefWindowProc(hWnd, message, wParam, lParam);
|
|
}
|
|
return 0;
|
|
};
|
|
|
|
SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
|
|
|
|
// Parse arguments early so we can adjust window creation
|
|
wi::arguments::Parse(lpCmdLine);
|
|
bool isScreenshot = wi::arguments::HasArgument("screenshot");
|
|
|
|
WNDCLASSEXW wcex = {};
|
|
wcex.cbSize = sizeof(WNDCLASSEX);
|
|
wcex.style = CS_HREDRAW | CS_VREDRAW;
|
|
wcex.lpfnWndProc = WndProc;
|
|
wcex.hInstance = hInstance;
|
|
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
|
|
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
|
|
wcex.lpszClassName = L"BVLEVoxels";
|
|
RegisterClassExW(&wcex);
|
|
|
|
// Compute window size so the client area is exactly 1920x1080
|
|
DWORD style = WS_OVERLAPPEDWINDOW;
|
|
int clientW = isScreenshot ? 640 : 1920;
|
|
int clientH = isScreenshot ? 480 : 1080;
|
|
RECT rc = { 0, 0, clientW, clientH };
|
|
AdjustWindowRect(&rc, style, FALSE);
|
|
int windowW = rc.right - rc.left;
|
|
int windowH = rc.bottom - rc.top;
|
|
|
|
// Center on screen
|
|
int screenW = GetSystemMetrics(SM_CXSCREEN);
|
|
int screenH = GetSystemMetrics(SM_CYSCREEN);
|
|
int posX = isScreenshot ? 0 : (screenW - windowW) / 2;
|
|
int posY = isScreenshot ? 0 : (screenH - windowH) / 2;
|
|
|
|
HWND hWnd = CreateWindowW(
|
|
wcex.lpszClassName,
|
|
isScreenshot ? L"BVLE Screenshot" : L"BVLE Voxels - Prototype",
|
|
style,
|
|
posX, posY, windowW, windowH,
|
|
nullptr, nullptr, hInstance, nullptr
|
|
);
|
|
ShowWindow(hWnd, isScreenshot ? SW_SHOWNOACTIVATE : SW_SHOW);
|
|
|
|
// Initialize Wicked Engine
|
|
application.SetWindow(hWnd);
|
|
|
|
// Redirect Wicked Engine log to file
|
|
wi::backlog::SetLogFile("bvle_backlog.txt");
|
|
|
|
// Info display (disabled in screenshot mode)
|
|
application.infoDisplay.active = !isScreenshot;
|
|
application.infoDisplay.watermark = false;
|
|
application.infoDisplay.resolution = !isScreenshot;
|
|
application.infoDisplay.fpsinfo = !isScreenshot;
|
|
application.infoDisplay.heap_allocation_counter = !isScreenshot;
|
|
|
|
// Check for "debug" argument to enable face-color debug mode
|
|
if (wi::arguments::HasArgument("debug")) {
|
|
renderPath.debugMode = true;
|
|
}
|
|
// Check for "debugsmooth" argument to enable smooth blending debug scene
|
|
if (wi::arguments::HasArgument("debugsmooth")) {
|
|
renderPath.debugSmooth = true;
|
|
}
|
|
// Screenshot mode: auto-position camera, wait for AO convergence, capture, quit
|
|
if (wi::arguments::HasArgument("screenshot")) {
|
|
renderPath.screenshotMode = true;
|
|
}
|
|
|
|
// Activate our custom voxel render path
|
|
application.ActivatePath(&renderPath);
|
|
|
|
// Main loop
|
|
MSG msg = { 0 };
|
|
static int screenshotFrameCounter = 0;
|
|
while (msg.message != WM_QUIT)
|
|
{
|
|
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
else {
|
|
application.Run();
|
|
|
|
// Screenshot mode: cycle through multiple camera views
|
|
if (renderPath.screenshotMode) {
|
|
struct CamView { float x, y, z, pitch, yaw; const char* name; };
|
|
static const CamView views[] = {
|
|
{ 220.f, 42.f, 258.f, -0.40f, 0.7f, "landscape" }, // higher overview
|
|
{ 220.f, 39.f, 258.f, -0.35f, 0.7f, "medium" }, // medium shot, terrain detail
|
|
{ 222.f, 37.f, 260.f, -0.20f, 0.5f, "closeup" }, // close-up ground level
|
|
{ 220.f, 120.f, 258.f, 1.0f, 0.7f, "birdseye" }, // bird's eye (LOD overview)
|
|
};
|
|
static const int numViews = sizeof(views) / sizeof(views[0]);
|
|
static int currentView = 0;
|
|
static int convergenceFrames = 0;
|
|
static bool waitingForRender = true;
|
|
|
|
screenshotFrameCounter++;
|
|
|
|
// Set camera for current view on first frame or after capture
|
|
if (screenshotFrameCounter == 1 || convergenceFrames == 0) {
|
|
renderPath.setCamera(views[currentView].x, views[currentView].y,
|
|
views[currentView].z, views[currentView].pitch, views[currentView].yaw);
|
|
}
|
|
|
|
bool hasRendered = renderPath.renderer.getGpuMeshQuadCount() > 0;
|
|
if (hasRendered) convergenceFrames++;
|
|
|
|
// Wait 50 frames for AO convergence per view
|
|
if (convergenceFrames >= 50) {
|
|
char filename[64];
|
|
snprintf(filename, sizeof(filename), "bvle_screenshot_%s.png",
|
|
views[currentView].name);
|
|
wi::helper::saveTextureToFile(renderPath.getVoxelRT(), filename);
|
|
|
|
currentView++;
|
|
if (currentView >= numViews) {
|
|
PostQuitMessage(0);
|
|
} else {
|
|
convergenceFrames = 0;
|
|
renderPath.setCamera(views[currentView].x, views[currentView].y,
|
|
views[currentView].z, views[currentView].pitch, views[currentView].yaw);
|
|
renderPath.resetAOHistory(); // invalidate temporal AO for new view
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
wi::jobsystem::ShutDown();
|
|
return (int)msg.wParam;
|
|
}
|