Switch to full screen with Direct3D12

While listening to Dr. ChatGPT, I implemented switching between full screen mode and windowed mode in Direct3D12. The idea is to switch between the two by pressing the F11 key.

SampleApp g_app;

extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
   // ImGui Window Processing
    if (ImGui_ImplWin32_WndProcHandler(hWnd, message, wParam, lParam)) {
        return true;
    }

    static bool isFullscreen = false;
    static bool isFullscreenInActive = false;
    static WINDOWPLACEMENT windowPlacement = { sizeof(WINDOWPLACEMENT) };// save position and size in window mode

    switch (message) {
    case WM_DESTROY:
        g_app.SetFullscreenState(FALSE);
        PostQuitMessage(0);
        return 0;
    case WM_KEYDOWN:
        if (wParam == VK_F11) {// Switch with F11 key
            if (isFullscreen) {
               // Return to window mode
                g_app.SetFullscreenState(FALSE);
                SetWindowLong(hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
                SetWindowPlacement(hWnd, &windowPlacement);// Restore previous window position and size
                SetWindowPos(hWnd, nullptr, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
                isFullscreen = false;
            }
            else {
               // Put the window in full screen mode
                GetWindowPlacement(hWnd, &windowPlacement);// Save current window position and size
                SetWindowLong(hWnd, GWL_STYLE, WS_POPUP | WS_VISIBLE);// Remove window border and title bar
                SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
//SetWindowPos(hWnd, HWND_TOP, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN), SWP_FRAMECHANGED);
                g_app.SetFullscreenState(TRUE);
                isFullscreen = true;
            }
        }
        return 0;
    case WM_SIZE:
        if (wParam != SIZE_MINIMIZED) {
            UINT width = LOWORD(lParam);
            UINT height = HIWORD(lParam);

            g_app.SetScreenSize(width, height);
        }
        break;
    case WM_ACTIVATE:
        if (wParam == WA_INACTIVE) {
           // When a window is deactivated
            if (isFullscreen) {
               // Return to windowed mode from full screen mode
                g_app.SetFullscreenState(FALSE);
                SetWindowLong(hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
                SetWindowPlacement(hWnd, &windowPlacement);// Restore previous position and size
                SetWindowPos(hWnd, nullptr, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
                isFullscreenInActive = true;
            }
        }
        else if (wParam == WA_ACTIVE || wParam == WA_CLICKACTIVE) {
           // If the window becomes active again
            if (isFullscreenInActive) {
               // Return to full screen if necessary
                SetWindowLong(hWnd, GWL_STYLE, WS_POPUP | WS_VISIBLE);
                SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
                g_app.SetFullscreenState(TRUE);
                isFullscreenInActive = false;
            }
        }
        return 0;
    default:
        break;
    }

    return DefWindowProc(hWnd, message, wParam, lParam);
}

g_app.SetFullscreenState()is defined as follows

    bool SetFullscreenState(BOOL is_fullscreen)
    {
        fullScreen = (is_fullscreen == TRUE);
        if (swapChain)
        {
           // Command queue flushing process
            WaitForFence();

           // Full screen setup
            swapChain->SetFullscreenState(is_fullscreen, nullptr);
            return true;
        }
        return false;
    }

I am not sure if WM_ACTIVATE processing is necessary, but I am writing a process to temporarily return to windowed mode when an app is no longer active in full screen mode, such as Alt+Tab .

The stuck point is this part.

SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
//SetWindowPos(hWnd, HWND_TOP, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN), SWP_FRAMECHANGED);

I tried to set the resolution of the monitor when going to full screen, but I was correct in not setting the size. You do not have to set it here, The resizing process is automatically called at the timing of SetFullscreenState.

In addition, SetScreenSize is the following process.

    void SetScreenSize(int width, int height)
    {
        viewport.Width = static_cast<float>(width); 
        viewport.Height = static_cast<float>(height); 
        scissorRect.right = width;
        scissorRect.bottom = height;

        if (swapChain) {
           // Command queue flushing process
            WaitForFence();

           // Resize back buffer
            for (UINT i = 0; i < FRAME_BUFFER_COUNT; ++i) {
                renderTargets[i].Reset();
            }

           // Resize swap chain buffer
            swapChain->ResizeBuffers(FRAME_BUFFER_COUNT, width, height, DXGI_FORMAT_R8G8B8A8_UNORM, 0);

           // Re-create descriptor heap
            D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc = {};
            rtvHeapDesc.NumDescriptors = FRAME_BUFFER_COUNT;
            rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
            rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
            device->CreateDescriptorHeap(&rtvHeapDesc, IID_PPV_ARGS(&rtvHeap));

           // Re-create RTV for new back buffer
            UINT rtvDescriptorSize = device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
            D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = rtvHeap->GetCPUDescriptorHandleForHeapStart();
            for (UINT i = 0; i < FRAME_BUFFER_COUNT; ++i) {
                swapChain->GetBuffer(i, IID_PPV_ARGS(&renderTargets[i]));
                device->CreateRenderTargetView(renderTargets[i].Get(), nullptr, rtvHandle);
                rtvHandle.ptr += rtvDescriptorSize;
            }
        }
    }