// Copyright 2014 Intel Corporation All Rights Reserved // // Intel makes no representations about the suitability of this software for any purpose. // THIS SOFTWARE IS PROVIDED ""AS IS."" INTEL SPECIFICALLY DISCLAIMS ALL WARRANTIES, // EXPRESS OR IMPLIED, AND ALL LIABILITY, INCLUDING CONSEQUENTIAL AND OTHER INDIRECT DAMAGES, // FOR THE USE OF THIS SOFTWARE, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PROPRIETARY // RIGHTS, AND INCLUDING THE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. // Intel does not assume any responsibility for any errors which may appear in this software // nor any responsibility to update it. #define WIN32_LEAN_AND_MEAN #include #include #include #include // must be after windows.h #include #include #include #include #include #include #include "asteroids_d3d11.h" #include "asteroids_d3d12.h" #include "asteroids_DE.h" #include "camera.h" #include "gui.h" using namespace DirectX; namespace { // Global demo state Settings gSettings; OrbitCamera gCamera; IDXGIFactory2* gDXGIFactory = nullptr; AsteroidsD3D11::Asteroids* gWorkloadD3D11 = nullptr; AsteroidsD3D12::Asteroids* gWorkloadD3D12 = nullptr; AsteroidsDE::Asteroids* gWorkloadDE = nullptr; bool gd3d11Available = false; bool gd3d12Available = false; Settings::RenderMode gLastFrameRenderMode = static_cast(-1); GUI gGUI; GUIText* gFPSControl; enum { basicStyle = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VISIBLE, windowedStyle = basicStyle | WS_OVERLAPPEDWINDOW, fullscreenStyle = basicStyle }; bool CheckDll(char const* dllName) { auto hModule = LoadLibrary(dllName); if (hModule == NULL) { return false; } FreeLibrary(hModule); return true; } UINT SetupDPI() { // Just do system DPI awareness for now for simplicity... scale the 3D content SetProcessDpiAwareness(PROCESS_SYSTEM_DPI_AWARE); UINT dpiX = 0, dpiY; POINT pt = {1, 1}; auto hMonitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST); if (SUCCEEDED(GetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY))) { return dpiX; } else { return 96; // default } } void ResetCameraView() { auto center = XMVectorSet(0.0f, -0.4f*SIM_DISC_RADIUS, 0.0f, 0.0f); auto radius = SIM_ORBIT_RADIUS + SIM_DISC_RADIUS + 10.f; auto minRadius = SIM_ORBIT_RADIUS - 3.0f * SIM_DISC_RADIUS; auto maxRadius = SIM_ORBIT_RADIUS + 3.0f * SIM_DISC_RADIUS; auto longAngle = 4.50f; auto latAngle = 1.45f; gCamera.View(center, radius, minRadius, maxRadius, longAngle, latAngle); } void ToggleFullscreen(HWND hWnd) { static WINDOWPLACEMENT prevPlacement = { sizeof(prevPlacement) }; DWORD dwStyle = (DWORD)GetWindowLongPtr(hWnd, GWL_STYLE); if ((dwStyle & windowedStyle) == windowedStyle) { MONITORINFO mi = { sizeof(mi) }; if (GetWindowPlacement(hWnd, &prevPlacement) && GetMonitorInfo(MonitorFromWindow(hWnd, MONITOR_DEFAULTTOPRIMARY), &mi)) { SetWindowLong(hWnd, GWL_STYLE, fullscreenStyle); SetWindowPos(hWnd, HWND_TOP, mi.rcMonitor.left, mi.rcMonitor.top, mi.rcMonitor.right - mi.rcMonitor.left, mi.rcMonitor.bottom - mi.rcMonitor.top, SWP_NOOWNERZORDER | SWP_FRAMECHANGED); } } else { SetWindowLong(hWnd, GWL_STYLE, windowedStyle); SetWindowPlacement(hWnd, &prevPlacement); SetWindowPos(hWnd, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_FRAMECHANGED); } } } // namespace LRESULT CALLBACK WindowProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_DESTROY: PostQuitMessage(0); return 0; case WM_SIZE: { UINT ww = LOWORD(lParam); UINT wh = HIWORD(lParam); // Ignore resizing to minimized if (ww == 0 || wh == 0) return 0; gSettings.windowWidth = (int)ww; gSettings.windowHeight = (int)wh; gSettings.renderWidth = (UINT)(double(gSettings.windowWidth) * gSettings.renderScale); gSettings.renderHeight = (UINT)(double(gSettings.windowHeight) * gSettings.renderScale); // Update camera projection float aspect = (float)gSettings.renderWidth / (float)gSettings.renderHeight; gCamera.Projection(XM_PIDIV2 * 0.8f * 3 / 2, aspect); // Resize currently active swap chain switch (gSettings.mode) { case Settings::RenderMode::NativeD3D11: if(gWorkloadD3D11) gWorkloadD3D11->ResizeSwapChain(gDXGIFactory, hWnd, gSettings.renderWidth, gSettings.renderHeight); break; case Settings::RenderMode::NativeD3D12: if(gWorkloadD3D12) gWorkloadD3D12->ResizeSwapChain(gDXGIFactory, hWnd, gSettings.renderWidth, gSettings.renderHeight); break; case Settings::RenderMode::DiligentD3D11: case Settings::RenderMode::DiligentD3D12: case Settings::RenderMode::DiligentGL: if(gWorkloadDE) gWorkloadDE->ResizeSwapChain(hWnd, gSettings.renderWidth, gSettings.renderHeight); break; } return 0; } case WM_KEYDOWN: if (lParam & (1 << 30)) { // Ignore repeats return 0; } switch (wParam) { case VK_SPACE: gSettings.animate = !gSettings.animate; std::cout << "Animate: " << gSettings.animate << std::endl; return 0; /* Disabled for demo setup */ case 'V': gSettings.vsync = !gSettings.vsync; std::cout << "Vsync: " << gSettings.vsync << std::endl; return 0; case 'M': gSettings.multithreadedRendering = !gSettings.multithreadedRendering; std::cout << "Multithreaded Rendering: " << gSettings.multithreadedRendering << std::endl; return 0; case 'I': gSettings.executeIndirect = !gSettings.executeIndirect; std::cout << "ExecuteIndirect Rendering: " << gSettings.executeIndirect << std::endl; return 0; case 'S': gSettings.submitRendering = !gSettings.submitRendering; std::cout << "Submit Rendering: " << gSettings.submitRendering << std::endl; return 0; case 'B': if (gSettings.mode == Settings::RenderMode::DiligentD3D12) { gSettings.resourceBindingMode = (gSettings.resourceBindingMode + 1) % 3; gLastFrameRenderMode = static_cast(-1); } return 0; case VK_ADD: case VK_OEM_PLUS: gSettings.numThreads = std::min(gSettings.numThreads+1, 16); gLastFrameRenderMode = static_cast(-1); return 0; case VK_SUBTRACT: case VK_OEM_MINUS: gSettings.numThreads = std::max(gSettings.numThreads-1, 2); gLastFrameRenderMode = static_cast(-1); return 0; case '1': gSettings.mode = gd3d11Available ? Settings::RenderMode::NativeD3D11 : gSettings.mode; return 0; case '2': gSettings.mode = gd3d12Available ? Settings::RenderMode::NativeD3D12 : gSettings.mode; return 0; case '3': gSettings.mode = gd3d11Available ? Settings::RenderMode::DiligentD3D11 : gSettings.mode; return 0; case '4': gSettings.mode = gd3d12Available ? Settings::RenderMode::DiligentD3D12 : gSettings.mode; return 0; // '5': gSettings.mode = Settings::RenderMode::DiligentGL; return 0; case VK_ESCAPE: SendMessage(hWnd, WM_CLOSE, 0, 0); return 0; } // Switch on key code return 0; case WM_SYSKEYDOWN: if (lParam & (1 << 30)) { // Ignore repeats return 0; } switch (wParam) { case VK_RETURN: ToggleFullscreen(hWnd); break; } return 0; case WM_MOUSEWHEEL: { auto delta = GET_WHEEL_DELTA_WPARAM(wParam); gCamera.ZoomRadius(-0.07f * delta); return 0; } case WM_POINTERDOWN: case WM_POINTERUPDATE: case WM_POINTERUP: { auto pointerId = GET_POINTERID_WPARAM(wParam); POINTER_INFO pointerInfo; if (GetPointerInfo(pointerId, &pointerInfo)) { if (message == WM_POINTERDOWN) { // Compute pointer position in render units POINT p = pointerInfo.ptPixelLocation; ScreenToClient(hWnd, &p); RECT clientRect; GetClientRect(hWnd, &clientRect); p.x = p.x * gSettings.renderWidth / (clientRect.right - clientRect.left); p.y = p.y * gSettings.renderHeight / (clientRect.bottom - clientRect.top); auto guiControl = gGUI.HitTest(p.x, p.y); if (guiControl == gFPSControl) { gSettings.lockFrameRate = !gSettings.lockFrameRate; } else { // Camera manipulation gCamera.AddPointer(pointerId); } } // Otherwise send it to the camera controls gCamera.ProcessPointerFrames(pointerId, &pointerInfo); if (message == WM_POINTERUP) gCamera.RemovePointer(pointerId); } return 0; } default: return DefWindowProc(hWnd, message, wParam, lParam); } } int InitWorkload(HWND hWnd, AsteroidsSimulation &asteroids) { delete gWorkloadD3D11; gWorkloadD3D11 = nullptr; delete gWorkloadD3D12; gWorkloadD3D12 = nullptr; delete gWorkloadDE; gWorkloadDE = nullptr; switch (gSettings.mode) { case Settings::RenderMode::NativeD3D11: if (gd3d11Available) { gWorkloadD3D11 = new AsteroidsD3D11::Asteroids(&asteroids, &gGUI, gSettings.warp); } gWorkloadD3D11->ResizeSwapChain(gDXGIFactory, hWnd, gSettings.renderWidth, gSettings.renderHeight); break; case Settings::RenderMode::NativeD3D12: if (gd3d12Available) { // If requested, enumerate the warp adapter // TODO: Allow picking from multiple hardware adapters IDXGIAdapter1* adapter = nullptr; if (gSettings.warp) { IDXGIFactory4* DXGIFactory4 = nullptr; if (FAILED(gDXGIFactory->QueryInterface(&DXGIFactory4))) { fprintf(stderr, "error: WARP requires IDXGIFactory4 interface which is not present on this system!\n"); return -1; } auto hr = DXGIFactory4->EnumWarpAdapter(IID_PPV_ARGS(&adapter)); DXGIFactory4->Release(); if (FAILED(hr)) { fprintf(stderr, "error: WARP adapter not present on this system!\n"); return -1; } } gWorkloadD3D12 = new AsteroidsD3D12::Asteroids(&asteroids, &gGUI, gSettings.numThreads, adapter); } gWorkloadD3D12->ResizeSwapChain(gDXGIFactory, hWnd, gSettings.renderWidth, gSettings.renderHeight); break; case Settings::RenderMode::DiligentD3D11: gWorkloadDE = new AsteroidsDE::Asteroids(gSettings, &asteroids, &gGUI, hWnd, Diligent::DeviceType::D3D11); break; case Settings::RenderMode::DiligentD3D12: gWorkloadDE = new AsteroidsDE::Asteroids(gSettings, &asteroids, &gGUI, hWnd, Diligent::DeviceType::D3D12); break; case Settings::RenderMode::DiligentGL: gWorkloadDE = new AsteroidsDE::Asteroids(gSettings, &asteroids, &gGUI, hWnd, Diligent::DeviceType::OpenGL); break; } return 0; } int main(int argc, char** argv) { #if defined(_DEBUG) || defined(DEBUG) _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); #endif gd3d11Available = CheckDll("d3d11.dll"); gd3d12Available = CheckDll("d3d12.dll"); // Must be done before any windowing-system-like things or else virtualization will kick in auto dpi = SetupDPI(); // By default render at the lower resolution and scale up based on system settings gSettings.renderScale = 96.0 / double(dpi); // Scale default window size based on dpi gSettings.windowWidth *= dpi / 96; gSettings.windowHeight *= dpi / 96; for (int a = 1; a < argc; ++a) { if (_stricmp(argv[a], "-close_after") == 0 && a + 1 < argc) { gSettings.closeAfterSeconds = atof(argv[++a]); } else if (_stricmp(argv[a], "-nod3d11") == 0) { gd3d11Available = false; } else if (_stricmp(argv[a], "-singlethreaded") == 0) { gSettings.multithreadedRendering = false; } else if (_stricmp(argv[a], "-warp") == 0) { gSettings.warp = true; } else if (_stricmp(argv[a], "-nod3d12") == 0) { gd3d12Available = false; } else if (_stricmp(argv[a], "-indirect") == 0) { gSettings.executeIndirect = true; } else if (_stricmp(argv[a], "-fullscreen") == 0) { gSettings.windowed = false; } else if (_stricmp(argv[a], "-window") == 0 && a + 2 < argc) { gSettings.windowWidth = atoi(argv[++a]); gSettings.windowHeight = atoi(argv[++a]); } else if (_stricmp(argv[a], "-render_scale") == 0 && a + 1 < argc) { gSettings.renderScale = atof(argv[++a]); } else if (_stricmp(argv[a], "-locked_fps") == 0 && a + 1 < argc) { gSettings.lockedFrameRate = atoi(argv[++a]); } else if (_stricmp(argv[a], "-threads") == 0 && a + 1 < argc) { gSettings.numThreads = atoi(argv[++a]); } else { fprintf(stderr, "error: unrecognized argument '%s'\n", argv[a]); fprintf(stderr, "usage: asteroids_d3d12 [options]\n"); fprintf(stderr, "options:\n"); fprintf(stderr, " -close_after [seconds]\n"); fprintf(stderr, " -nod3d11\n"); fprintf(stderr, " -nod3d12\n"); fprintf(stderr, " -fullscreen\n"); fprintf(stderr, " -window [width] [height]\n"); fprintf(stderr, " -render_scale [scale]\n"); fprintf(stderr, " -locked_fps [fps]\n"); fprintf(stderr, " -warp\n"); return -1; } } if (gSettings.numThreads == 0) { gSettings.numThreads = std::max(std::thread::hardware_concurrency()-1, 2u); } //if (!d3d11Available && !d3d12Available) { // fprintf(stderr, "error: neither D3D11 nor D3D12 available.\n"); // return -1; //} // DXGI Factory ThrowIfFailed(CreateDXGIFactory2(0, IID_PPV_ARGS(&gDXGIFactory))); // Setup GUI gFPSControl = gGUI.AddText(150, 10); ResetCameraView(); // Camera projection set up in WM_SIZE AsteroidsSimulation asteroids(1337, NUM_ASTEROIDS, NUM_UNIQUE_MESHES, MESH_MAX_SUBDIV_LEVELS, NUM_UNIQUE_TEXTURES); gSettings.mode = Settings::RenderMode::DiligentD3D12; // init window class WNDCLASSEX windowClass; ZeroMemory(&windowClass, sizeof(WNDCLASSEX)); windowClass.cbSize = sizeof(WNDCLASSEX); windowClass.style = CS_HREDRAW | CS_VREDRAW; windowClass.lpfnWndProc = WindowProc; windowClass.hInstance = GetModuleHandle(NULL); windowClass.hCursor = LoadCursor(NULL, IDC_ARROW); windowClass.lpszClassName = "AsteroidsD3D12WindowClass"; RegisterClassEx(&windowClass); RECT windowRect = { 0, 0, gSettings.windowWidth, gSettings.windowHeight }; AdjustWindowRect(&windowRect, windowedStyle, FALSE); // create the window and store a handle to it auto hWnd = CreateWindowEx( WS_EX_APPWINDOW, "AsteroidsD3D12WindowClass", "Asteroids", windowedStyle, 0, // CW_USE_DEFAULT 0, // CW_USE_DEFAULT windowRect.right - windowRect.left, windowRect.bottom - windowRect.top, NULL, NULL, windowClass.hInstance, NULL); if (!gSettings.windowed) { ToggleFullscreen(hWnd); } SetForegroundWindow(hWnd); // Initialize performance counters UINT64 perfCounterFreq = 0; UINT64 lastPerfCount = 0; QueryPerformanceFrequency((LARGE_INTEGER*)&perfCounterFreq); QueryPerformanceCounter((LARGE_INTEGER*)&lastPerfCount); // main loop double elapsedTime = 0.0; double frameTime = 0.0; POINTER_INFO pointerInfo = {}; timeBeginPeriod(1); EnableMouseInPointer(TRUE); float filteredUpdateTime = 0.0f; float filteredRenderTime = 0.0f; float filteredFrameTime = 0.0f; for (;;) { MSG msg = {}; while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) { // Cleanup delete gWorkloadD3D11; delete gWorkloadD3D12; delete gWorkloadDE; SafeRelease(&gDXGIFactory); timeEndPeriod(1); EnableMouseInPointer(FALSE); return (int)msg.wParam; }; TranslateMessage(&msg); DispatchMessage(&msg); } // If we swap to a new API we need to recreate swap chains if (gLastFrameRenderMode != gSettings.mode) { InitWorkload(hWnd, asteroids); gLastFrameRenderMode = gSettings.mode; } // Still need to process inertia even when no interaction is happening gCamera.ProcessInertia(); // In D3D12 we'll wait on the GPU before taking the timestamp (more consistent) if (gSettings.mode == Settings::RenderMode::NativeD3D12) { gWorkloadD3D12->WaitForReadyToRender(); } // Get time delta UINT64 count; QueryPerformanceCounter((LARGE_INTEGER*)&count); auto rawFrameTime = (double)(count - lastPerfCount) / perfCounterFreq; elapsedTime += rawFrameTime; lastPerfCount = count; // Maintaining absolute time sync is not important in this demo so we can err on the "smoother" side double alpha = 0.2f; frameTime = alpha * rawFrameTime + (1.0f - alpha) * frameTime; // Update GUI { const char *ModeStr = nullptr; const char *resBindModeStr = ""; float updateTime = 0; float renderTime = 0; switch (gSettings.mode) { case Settings::RenderMode::NativeD3D11: ModeStr = "Native D3D11"; gWorkloadD3D11->GetPerfCounters(updateTime, renderTime); break; case Settings::RenderMode::NativeD3D12: ModeStr = "Native D3D12"; gWorkloadD3D12->GetPerfCounters(updateTime, renderTime); break; case Settings::RenderMode::DiligentD3D11: ModeStr = "Diligent D3D11"; gWorkloadDE->GetPerfCounters(updateTime, renderTime); break; case Settings::RenderMode::DiligentD3D12: ModeStr = "Diligent D3D12"; gWorkloadDE->GetPerfCounters(updateTime, renderTime); switch (gSettings.resourceBindingMode) { case 0: resBindModeStr = "-d";break; case 1: resBindModeStr = "-m";break; case 2: resBindModeStr = "-tm";break; } break; case Settings::RenderMode::DiligentGL: ModeStr = "Diligent GL"; gWorkloadDE->GetPerfCounters(updateTime, renderTime); break; } float filterScale = 0.02f; filteredUpdateTime = filteredUpdateTime * (1.f - filterScale) + filterScale * updateTime; filteredRenderTime = filteredRenderTime * (1.f - filterScale) + filterScale * renderTime; filteredFrameTime = filteredFrameTime * (1.f - filterScale) + filterScale * (float)frameTime; char buffer[256]; sprintf_s(buffer, "Asteroids %s%s (%dt) - %4.2f ms (%4.2f ms / %4.2f ms)", ModeStr, resBindModeStr, gSettings.multithreadedRendering ? gSettings.numThreads : 1, 1000.f * filteredFrameTime, 1000.f * filteredUpdateTime, 1000.f * filteredRenderTime); SetWindowText(hWnd, buffer); if (gSettings.lockFrameRate) { sprintf_s(buffer, "(Locked)"); } else { sprintf_s(buffer, "%.0f fps", 1.0f / filteredFrameTime); } gFPSControl->Text(buffer); } switch (gSettings.mode) { case Settings::RenderMode::NativeD3D11: if(gWorkloadD3D11) gWorkloadD3D11->Render((float)frameTime, gCamera, gSettings); break; case Settings::RenderMode::NativeD3D12: if(gWorkloadD3D12) gWorkloadD3D12->Render((float)frameTime, gCamera, gSettings); break; case Settings::RenderMode::DiligentD3D11: case Settings::RenderMode::DiligentD3D12: case Settings::RenderMode::DiligentGL: if(gWorkloadDE) gWorkloadDE->Render((float)frameTime, gCamera, gSettings); break; } if (gSettings.lockFrameRate) { UINT64 afterRenderCount; QueryPerformanceCounter((LARGE_INTEGER*)&afterRenderCount); double renderTime = (double)(afterRenderCount - count) / perfCounterFreq; double targetRenderTime = 1.0 / double(gSettings.lockedFrameRate); double deltaMs = (targetRenderTime - renderTime) * 1000.0; if (deltaMs > 1.0) { Sleep((DWORD)deltaMs); } } // All done? if (gSettings.closeAfterSeconds > 0.0 && elapsedTime > gSettings.closeAfterSeconds) { SendMessage(hWnd, WM_CLOSE, 0, 0); break; } } // Shouldn't get here return 1; }