diff --git a/src/engine/platform/win_glimp.c b/src/engine/platform/win_glimp.c index 006d0f8..3846cf3 100644 --- a/src/engine/platform/win_glimp.c +++ b/src/engine/platform/win_glimp.c @@ -409,10 +409,24 @@ static HWND create_main_window(int width, int height, qboolean fullscreen) } } + char window_name[1024]; + if (r_twinMode->integer == 0) { + strcpy(window_name, MAIN_WINDOW_CLASS_NAME); + } else { + const char* api_name = "invalid-render-api"; + if (r_renderAPI->integer == 0) + api_name = "OpenGL"; + else if (r_renderAPI->integer == 1) + api_name = "Vulkan"; + else if (r_renderAPI->integer == 2) + api_name = "DX12"; + sprintf(window_name, "%s [%s]", MAIN_WINDOW_CLASS_NAME, api_name); + } + HWND hwnd = CreateWindowEx( 0, MAIN_WINDOW_CLASS_NAME, - "Quake 3: Arena", + window_name, stylebits, x, y, w, h, NULL, @@ -431,7 +445,7 @@ static HWND create_main_window(int width, int height, qboolean fullscreen) return hwnd; } -static HWND create_twin_window(int width, int height, bool dx_window) +static HWND create_twin_window(int width, int height, int render_api) { // // register the window class if necessary @@ -478,8 +492,27 @@ static HWND create_twin_window(int width, int height, bool dx_window) cvar_t* vid_xpos = ri.Cvar_Get ("vid_xpos", "", 0); cvar_t* vid_ypos = ri.Cvar_Get ("vid_ypos", "", 0); - int x = vid_xpos->integer + width + 5; // offset to the right of the main window - int y = vid_ypos->integer; + int x, y; + + bool show_three_windows = (r_twinMode->integer | (1 << r_renderAPI->integer)) == 7; + + if (!show_three_windows) { // two windows + x = vid_xpos->integer + width + 5; // offset to the right of the main window + y = vid_ypos->integer; + } else { // three windows + bool first_twin_window = + (r_renderAPI->integer > 0 && render_api == 0) || + (r_renderAPI->integer == 0 && render_api == 1); + + if (first_twin_window) { + x = vid_xpos->integer + width + 5; + y = vid_ypos->integer; + } else { + x = vid_xpos->integer + 2*width + 10; + y = vid_ypos->integer; + } + + } // adjust window coordinates if necessary // so that the window is completely on screen @@ -499,10 +532,17 @@ static HWND create_twin_window(int width, int height, bool dx_window) y = ( desktop_height - h ); } - // If r_renderAPI = 0 (OpenGL) then twin window uses Vulkan API. - // If r_renderAPI = 1 (Vulkan) then twin window uses OpenGL API. char window_name[1024]; - sprintf(window_name, "%s [%s]", MAIN_WINDOW_CLASS_NAME, dx_window ? "DX12" : (r_renderAPI->integer == 0 ? "Vulkan" : "OpenGL")); + + const char* api_name = "invalid-render-api"; + if (render_api == 0) + api_name = "OpenGL"; + else if (render_api == 1) + api_name = "Vulkan"; + else if (render_api == 2) + api_name = "DX12"; + + sprintf(window_name, "%s [%s]", MAIN_WINDOW_CLASS_NAME, api_name); HWND hwnd = CreateWindowEx( 0, @@ -726,7 +766,7 @@ void GLimp_Init( void ) SetFocus(g_wv.hWnd); WG_CheckHardwareGamma(); } else { - g_wv.hWnd_opengl = create_twin_window(glConfig.vidWidth, glConfig.vidHeight, false); + g_wv.hWnd_opengl = create_twin_window(glConfig.vidWidth, glConfig.vidHeight, 0); } if (!GLW_InitDriver(g_wv.hWnd_opengl)) { @@ -831,16 +871,14 @@ void vk_imp_init() { // Create window. SetMode(r_mode->integer, (qboolean)r_fullscreen->integer); - if (r_renderAPI->integer != 0) { + if (r_renderAPI->integer == 1) { g_wv.hWnd_vulkan = create_main_window(glConfig.vidWidth, glConfig.vidHeight, (qboolean)r_fullscreen->integer); g_wv.hWnd = g_wv.hWnd_vulkan; SetForegroundWindow(g_wv.hWnd); SetFocus(g_wv.hWnd); WG_CheckHardwareGamma(); - - g_wv.hWnd_dx = create_twin_window(glConfig.vidWidth, glConfig.vidHeight, true); } else { - g_wv.hWnd_vulkan = create_twin_window(glConfig.vidWidth, glConfig.vidHeight, false); + g_wv.hWnd_vulkan = create_twin_window(glConfig.vidWidth, glConfig.vidHeight, 1); } } @@ -864,16 +902,6 @@ void vk_imp_shutdown() { } vkGetInstanceProcAddr = nullptr; - if (g_wv.hWnd_dx) { - ri.Printf(PRINT_ALL, "...destroying DX12 window\n"); - DestroyWindow(g_wv.hWnd_dx); - - if (g_wv.hWnd == g_wv.hWnd_dx) { - g_wv.hWnd_dx = NULL; - } - g_wv.hWnd_dx = NULL; - } - // For vulkan mode we still have qgl pointers initialized with placeholder values. // Reset them the same way as we do in opengl mode. QGL_Shutdown(); @@ -899,6 +927,58 @@ void vk_imp_create_surface() { VK_CHECK(vkCreateWin32SurfaceKHR(vk.instance, &desc, nullptr, &vk.surface)); } +void dx_imp_init() { + ri.Printf(PRINT_ALL, "Initializing DX12 subsystem\n"); + + // This will set qgl pointers to no-op placeholders. + if (!gl_active) { + QGL_Init(nullptr); + qglActiveTextureARB = [] (GLenum) {}; + qglClientActiveTextureARB = [](GLenum) {}; + } + + // Create window. + SetMode(r_mode->integer, (qboolean)r_fullscreen->integer); + + if (r_renderAPI->integer == 2) { + g_wv.hWnd_dx = create_main_window(glConfig.vidWidth, glConfig.vidHeight, (qboolean)r_fullscreen->integer); + g_wv.hWnd = g_wv.hWnd_dx; + SetForegroundWindow(g_wv.hWnd); + SetFocus(g_wv.hWnd); + WG_CheckHardwareGamma(); + } else { + g_wv.hWnd_dx = create_twin_window(glConfig.vidWidth, glConfig.vidHeight, 2); + } +} + +void dx_imp_shutdown() { + ri.Printf(PRINT_ALL, "Shutting down DX12 subsystem\n"); + + if (g_wv.hWnd_dx) { + ri.Printf(PRINT_ALL, "...destroying DX12 window\n"); + DestroyWindow(g_wv.hWnd_dx); + + if (g_wv.hWnd == g_wv.hWnd_dx) { + g_wv.hWnd = NULL; + } + g_wv.hWnd_dx = NULL; + } + + // For DX12 mode we still have qgl pointers initialized with placeholder values. + // Reset them the same way as we do in opengl mode. + QGL_Shutdown(); + + WG_RestoreGamma(); + + memset(&glConfig, 0, sizeof(glConfig)); + memset(&glState, 0, sizeof(glState)); + + if (log_fp) { + fclose(log_fp); + log_fp = 0; + } +} + /* =========================================================== diff --git a/src/engine/platform/win_local.h b/src/engine/platform/win_local.h index ee4f29a..aad1864 100644 --- a/src/engine/platform/win_local.h +++ b/src/engine/platform/win_local.h @@ -75,7 +75,7 @@ typedef struct HINSTANCE reflib_library; // Handle to refresh DLL qboolean reflib_active; - HWND hWnd; // main window, refers either to hWnd_opengl or to hWnd_vulkan + HWND hWnd; // main window, refers to one of the hWnd_XXX listed below HWND hWnd_opengl; HWND hWnd_vulkan; diff --git a/src/engine/renderer/tr_init.c b/src/engine/renderer/tr_init.c index 99c736d..57b20f3 100644 --- a/src/engine/renderer/tr_init.c +++ b/src/engine/renderer/tr_init.c @@ -191,7 +191,7 @@ static void InitRenderAPI( void ) if ( glConfig.vidWidth == 0 ) { // OpenGL - if (r_renderAPI->integer == 0 || r_twinMode->integer) { + if (r_renderAPI->integer == 0 || (r_twinMode->integer&1)) { GLimp_Init(); GLint temp; @@ -202,10 +202,14 @@ static void InitRenderAPI( void ) } // VULKAN - // DX12 - if (r_renderAPI->integer != 0 || r_twinMode->integer) { + if (r_renderAPI->integer == 1 || (r_twinMode->integer&2)) { vk_imp_init(); vk_initialize(); + } + + // DX12 + if (r_renderAPI->integer == 2 || (r_twinMode->integer&4)) { + dx_imp_init(); dx_initialize(); } } @@ -373,9 +377,9 @@ void RB_TakeScreenshot( int x, int y, int width, int height, char *fileName ) { if (r_renderAPI->integer == 0) { qglReadPixels( x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, buffer+18 ); - } else { - // VULKAN + } else if (r_renderAPI->integer == 1) { // VULKAN byte* buffer2 = (byte*) ri.Hunk_AllocateTempMemory(glConfig.vidWidth*glConfig.vidHeight*4); + vk_read_pixels(buffer2); byte* buffer_ptr = buffer + 18; @@ -418,8 +422,7 @@ void RB_TakeScreenshotJPEG( int x, int y, int width, int height, char *fileName if (r_renderAPI->integer == 0) { qglReadPixels( x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer ); - } else { - // VULKAN + } else if (r_renderAPI->integer == 1) { // VULKAN vk_read_pixels(buffer); } @@ -557,8 +560,7 @@ void R_LevelShot( void ) { if (r_renderAPI->integer == 0) { qglReadPixels( 0, 0, glConfig.vidWidth, glConfig.vidHeight, GL_RGB, GL_UNSIGNED_BYTE, source ); - } else { - // VULKAN + } else if (r_renderAPI->integer == 1) { // VULKAN byte* buffer2 = (byte*) ri.Hunk_AllocateTempMemory(glConfig.vidWidth*glConfig.vidHeight*4); vk_read_pixels(buffer2); @@ -1121,16 +1123,22 @@ void RE_Shutdown( qboolean destroyWindow ) { // VULKAN if (vk.active) { - dx_release_resources(); vk_release_resources(); if (destroyWindow) { - dx_shutdown(); - vk_shutdown(); vk_imp_shutdown(); } } + // DX12 + if (dx.active) { + dx_release_resources(); + if (destroyWindow) { + dx_shutdown(); + dx_imp_shutdown(); + } + } + tr.registered = qfalse; } diff --git a/src/engine/renderer/tr_local.h b/src/engine/renderer/tr_local.h index e2340f5..a6d8748 100644 --- a/src/engine/renderer/tr_local.h +++ b/src/engine/renderer/tr_local.h @@ -949,11 +949,16 @@ extern Dx_World dx_world; // this data is cleared during ref re-init // // cvars // -extern cvar_t *r_renderAPI; // 3D API to use: 0 - OpenGL, 1 - Vulkan. +extern cvar_t *r_renderAPI; // 3D API to use: 0 - OpenGL, 1 - Vulkan, 2 - DX12 -extern cvar_t *r_twinMode; // If enabled, renderer creates two separate windows. - // The first window uses rendering API specified by r_renderAPI, - // the second window uses rendering API corresponding to (1 - r_renderAPI). +extern cvar_t *r_twinMode; // Allows to render the same frame in different windows using different graphics APIs. + // This cvar specifies a bitmask that determines which APIs. + // 0 - regular rendering with single window using the graphics API specified by r_renderAPI. + // bit 0 - enables OpenGL backend + // bit 1 - enables Vulkan backend + // bit 2 - enables DX12 backend + // Combinations of the above values are allowed, for example, r_twinMode=7 creates three diffent + // windows using all the supported APIs. extern cvar_t *r_railWidth; extern cvar_t *r_railCoreWidth; @@ -1204,6 +1209,9 @@ void vk_imp_init(); void vk_imp_shutdown(); void vk_imp_create_surface(); +void dx_imp_init(); +void dx_imp_shutdown(); + // NOTE TTimo linux works with float gamma value, not the gamma table // the params won't be used, getting the r_gamma cvar directly void GLimp_SetGamma( unsigned char red[256],