diff --git a/Kits/ATGTK/imgui/imgui_atg.cpp b/Kits/ATGTK/imgui/imgui_atg.cpp index 80319e54..1a075d1c 100644 --- a/Kits/ATGTK/imgui/imgui_atg.cpp +++ b/Kits/ATGTK/imgui/imgui_atg.cpp @@ -16,18 +16,54 @@ #include #pragma comment(lib, "shcore.lib") // GetDpiForMonitor #endif +#include #include #include #include +#include namespace ImGuiAtg { //-------------------------------------------------------------------------------------- // Input helpers //-------------------------------------------------------------------------------------- - + + namespace + { + std::vector g_navigationGroups; + std::string g_currentNavigationGroup; + std::string g_requestedNavigationGroup; + int g_navigationGroupDepth = 0; + bool g_navigationGroupInputActive = false; + } + + void BeginNavigationGroup(const char* id) + { + IM_ASSERT(id != nullptr && id[0] != '\0'); + IM_ASSERT(g_navigationGroupDepth == 0); + IM_ASSERT(std::find(g_navigationGroups.cbegin(), g_navigationGroups.cend(), id) == g_navigationGroups.cend()); + g_navigationGroupDepth++; + g_navigationGroups.emplace_back(id); + + if (g_requestedNavigationGroup == id) + { + ImGui::SetNavCursorVisible(true); + ImGui::SetKeyboardFocusHere(); + g_currentNavigationGroup = id; + g_requestedNavigationGroup.clear(); + } + } + + void EndNavigationGroup() + { + IM_ASSERT(g_navigationGroupDepth > 0); + g_navigationGroupDepth--; + } + void HandleStandardInput() { + IM_ASSERT(g_navigationGroupDepth == 0); + if ((!ImGui::GetIO().WantTextInput && ImGui::IsKeyPressed(ImGuiKey_F2, false)) || (ImGui::IsKeyDown(ImGuiKey_GamepadL1) && ImGui::IsKeyDown(ImGuiKey_GamepadR1) && @@ -43,6 +79,57 @@ namespace ImGuiAtg { PostQuitMessage(0); } + + const bool groupNavigationChord = ImGui::IsKeyDown(ImGuiKey_GamepadL1) + && ImGui::IsKeyDown(ImGuiKey_GamepadR1); + const bool previousGroupPressed = groupNavigationChord + && ImGui::IsKeyPressed(ImGuiKey_GamepadDpadUp, false); + const bool nextGroupPressed = groupNavigationChord + && ImGui::IsKeyPressed(ImGuiKey_GamepadDpadDown, false); + const bool groupNavigationInputDown = groupNavigationChord + && (ImGui::IsKeyDown(ImGuiKey_GamepadDpadUp) + || ImGui::IsKeyDown(ImGuiKey_GamepadDpadDown)); + + if (!g_requestedNavigationGroup.empty() + && std::find( + g_navigationGroups.cbegin(), + g_navigationGroups.cend(), + g_requestedNavigationGroup) == g_navigationGroups.cend()) + { + g_requestedNavigationGroup.clear(); + } + + if (!g_navigationGroups.empty() && (previousGroupPressed || nextGroupPressed)) + { + auto currentGroup = std::find( + g_navigationGroups.cbegin(), + g_navigationGroups.cend(), + g_currentNavigationGroup); + + size_t requestedGroupIndex = 0; + if (currentGroup == g_navigationGroups.cend()) + { + requestedGroupIndex = previousGroupPressed ? g_navigationGroups.size() - 1 : 0; + } + else + { + const size_t currentGroupIndex = static_cast(currentGroup - g_navigationGroups.cbegin()); + requestedGroupIndex = previousGroupPressed + ? (currentGroupIndex + g_navigationGroups.size() - 1) % g_navigationGroups.size() + : (currentGroupIndex + 1) % g_navigationGroups.size(); + } + + g_requestedNavigationGroup = g_navigationGroups[requestedGroupIndex]; + g_navigationGroupInputActive = true; + ImGui::GetIO().ConfigFlags &= ~ImGuiConfigFlags_NavEnableGamepad; + } + else if (g_navigationGroupInputActive && !groupNavigationInputDown) + { + ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; + g_navigationGroupInputActive = false; + } + + g_navigationGroups.clear(); } //-------------------------------------------------------------------------------------- @@ -324,11 +411,11 @@ namespace ImGuiAtg // with FooterItem() before calling EndFooter(). } - bool FooterItem(const char* text) + bool FooterItem(const char* text, bool clickable) { ControllerText(text); - ImGui::SameLine(0, Scaled(80.0f)); - if (ImGui::IsItemHovered()) + ImGui::SameLine(0, Scaled(40.0f)); + if (clickable && ImGui::IsItemHovered()) { ImGui::SetMouseCursor(ImGuiMouseCursor_Hand); if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) diff --git a/Kits/ATGTK/imgui/imgui_atg.h b/Kits/ATGTK/imgui/imgui_atg.h index 44d337d9..9137b368 100644 --- a/Kits/ATGTK/imgui/imgui_atg.h +++ b/Kits/ATGTK/imgui/imgui_atg.h @@ -76,7 +76,7 @@ namespace ImGuiAtg void EndFooter(); // FooterItem() renders one footer hint between BeginFooter() and EndFooter(). - bool FooterItem(const char* text); + bool FooterItem(const char* text, bool clickable = true); // Handles standard sample input -- the same shortcuts that DrawFooter() advertises: // @@ -84,6 +84,17 @@ namespace ImGuiAtg // F2 / LB+RB+Y -- toggle light/dark theme void HandleStandardInput(); + // Ordered gamepad navigation groups. LB+RB+D-pad up/down moves between the groups + // submitted during Draw(), in submission order. When a group is selected, its first + // navigable item receives focus. + // + // Group IDs must be unique within a frame. Groups cannot be nested, and every + // BeginNavigationGroup() must have a matching EndNavigationGroup(). Conditional groups + // participate only on frames where they are submitted. The standard frame loop must call + // HandleStandardInput() once after ImGui::NewFrame() and before submitting groups. + void BeginNavigationGroup(const char* id); + void EndNavigationGroup(); + // Splitter -- splits the current region into two resizable panels. // // Usage (horizontal -- top/bottom): diff --git a/Kits/OpenSource/imgui/backends/imgui_impl_win32.cpp b/Kits/OpenSource/imgui/backends/imgui_impl_win32.cpp index 8376e14d..03e3e3a5 100644 --- a/Kits/OpenSource/imgui/backends/imgui_impl_win32.cpp +++ b/Kits/OpenSource/imgui/backends/imgui_impl_win32.cpp @@ -99,6 +99,8 @@ #include #include using Microsoft::WRL::ComPtr; +#include +#include #include #if GAMEINPUT_API_VERSION == 1 @@ -124,21 +126,25 @@ using namespace GameInput::v3; struct ImGui_ImplWin32_Data { - HWND hWnd; - HWND MouseHwnd; - int MouseTrackedArea; // 0: not tracked, 1: client area, 2: non-client area - int MouseButtonsDown; - INT64 Time; - INT64 TicksPerSecond; - ImGuiMouseCursor LastMouseCursor; - UINT32 KeyboardCodePage; + HWND hWnd = nullptr; + HWND MouseHwnd = nullptr; + int MouseTrackedArea = 0; // 0: not tracked, 1: client area, 2: non-client area + int MouseButtonsDown = 0; + INT64 Time = 0; + INT64 TicksPerSecond = 0; + ImGuiMouseCursor LastMouseCursor = ImGuiMouseCursor_Arrow; + UINT32 KeyboardCodePage = 0; #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD - bool HasGamepad; - ComPtr GameInput; + bool HasGamepad = false; + GameInputCallbackToken DeviceCallbackToken = 0; + + ComPtr GameInput{}; + std::vector> Gamepads{}; + std::mutex GamepadsMutex{}; #endif - ImGui_ImplWin32_Data() { memset((void*)this, 0, sizeof(*this)); } + ImGui_ImplWin32_Data() = default; }; // Backend data stored in io.BackendPlatformUserData to allow support for multiple Dear ImGui contexts @@ -169,6 +175,39 @@ static void ImGui_ImplWin32_UpdateKeyboardCodePage(ImGuiIO& io) #endif } +static void CALLBACK ImGui_ImplWin32_DeviceCallback( + _In_ GameInputCallbackToken, + _In_ void* context, + _In_ IGameInputDevice* device, + _In_ uint64_t, + _In_ GameInputDeviceStatus current, + _In_ GameInputDeviceStatus previous) noexcept +{ + auto* bd = static_cast(context); + + const bool wasConnected = (previous & GameInputDeviceConnected) != 0; + const bool isConnected = (current & GameInputDeviceConnected) != 0; + + std::lock_guard lock(bd->GamepadsMutex); + + // newly connected device, add to our list + if (isConnected && !wasConnected) + { + bd->Gamepads.emplace_back(device); + } + + // newly disconnected device, remove from our list + else if (wasConnected && !isConnected) + { + bd->Gamepads.erase( + std::remove_if(bd->Gamepads.begin(), bd->Gamepads.end(), [device](ComPtr const& entry) + { + return entry.Get() == device; + }), bd->Gamepads.end() + ); + } +} + static bool ImGui_ImplWin32_InitEx(void* hwnd, bool platform_has_own_dc) { ImGuiIO& io = ImGui::GetIO(); @@ -181,17 +220,44 @@ static bool ImGui_ImplWin32_InitEx(void* hwnd, bool platform_has_own_dc) if (!::QueryPerformanceCounter((LARGE_INTEGER*)&perf_counter)) return false; - // Setup backend capabilities flags ImGui_ImplWin32_Data* bd = IM_NEW(ImGui_ImplWin32_Data)(); + bd->hWnd = (HWND)hwnd; + bd->TicksPerSecond = perf_frequency; + bd->Time = perf_counter; + bd->LastMouseCursor = ImGuiMouseCursor_COUNT; + + // Setup GameInput +#ifndef IMGUI_IMPL_GDK_DISABLE_GAMEPAD + HRESULT hr = GameInputCreate(&bd->GameInput); + if (FAILED(hr)) + { + IM_DELETE(bd); + return false; + } + + hr = bd->GameInput->RegisterDeviceCallback( + nullptr, + GameInputKindGamepad, + GameInputDeviceConnected, + GameInputBlockingEnumeration, + bd, + ImGui_ImplWin32_DeviceCallback, + &bd->DeviceCallbackToken); + + if (FAILED(hr)) + { + IM_DELETE(bd); + return false; + } + +#endif // IMGUI_IMPL_GDK_DISABLE_GAMEPAD + + // Setup backend capabilities flags io.BackendPlatformUserData = (void*)bd; io.BackendPlatformName = "imgui_impl_win32"; io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) - bd->hWnd = (HWND)hwnd; - bd->TicksPerSecond = perf_frequency; - bd->Time = perf_counter; - bd->LastMouseCursor = ImGuiMouseCursor_COUNT; ImGui_ImplWin32_UpdateKeyboardCodePage(io); // Set platform dependent data in viewport @@ -199,12 +265,6 @@ static bool ImGui_ImplWin32_InitEx(void* hwnd, bool platform_has_own_dc) main_viewport->PlatformHandle = main_viewport->PlatformHandleRaw = (void*)bd->hWnd; IM_UNUSED(platform_has_own_dc); // Used in 'docking' branch - // Setup GameInput -#ifndef IMGUI_IMPL_GDK_DISABLE_GAMEPAD - if(GameInputCreate(&bd->GameInput) != S_OK) - return false; -#endif // IMGUI_IMPL_GDK_DISABLE_GAMEPAD - return true; } @@ -226,6 +286,18 @@ void ImGui_ImplWin32_Shutdown() ImGuiIO& io = ImGui::GetIO(); ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); +#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD + if (bd->GameInput != nullptr && bd->DeviceCallbackToken != 0) + { +#if defined(GAMEINPUT_API_VERSION) && (GAMEINPUT_API_VERSION >= 1) + bd->GameInput->UnregisterCallback(bd->DeviceCallbackToken); +#else + bd->GameInput->UnregisterCallback(bd->DeviceCallbackToken, UINT64_MAX); +#endif + bd->DeviceCallbackToken = 0; + } +#endif + io.BackendPlatformName = nullptr; io.BackendPlatformUserData = nullptr; io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasSetMousePos | ImGuiBackendFlags_HasGamepad); @@ -344,8 +416,14 @@ static void ImGui_ImplWin32_UpdateGamepads(ImGuiIO& io) #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(io); - if(bd->GameInput == nullptr) + std::lock_guard lock(bd->GamepadsMutex); + + if(bd->GameInput == nullptr || bd->Gamepads.empty() || bd->Gamepads.back() == nullptr) + { + io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; + bd->HasGamepad = false; return; + } #if GAMEINPUT_API_VERSION == 1 ComPtr reading; @@ -357,7 +435,7 @@ static void ImGui_ImplWin32_UpdateGamepads(ImGuiIO& io) ComPtr reading; #endif - HRESULT hr = bd->GameInput->GetCurrentReading(GameInputKindGamepad, nullptr, &reading); + HRESULT hr = bd->GameInput->GetCurrentReading(GameInputKindGamepad, bd->Gamepads.back().Get(), &reading); bd->HasGamepad = hr == S_OK; io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; diff --git a/Samples/Graphics/FramePacing/Assets/LOGO_ATG.png b/Samples/Graphics/FramePacing/Assets/LOGO_ATG.png new file mode 100644 index 00000000..bd455669 Binary files /dev/null and b/Samples/Graphics/FramePacing/Assets/LOGO_ATG.png differ diff --git a/Samples/Graphics/FramePacing/Assets/LargeLogo.png b/Samples/Graphics/FramePacing/Assets/LargeLogo.png new file mode 100644 index 00000000..ed4a935e Binary files /dev/null and b/Samples/Graphics/FramePacing/Assets/LargeLogo.png differ diff --git a/Samples/Graphics/FramePacing/Assets/Logo.png b/Samples/Graphics/FramePacing/Assets/Logo.png new file mode 100644 index 00000000..2ca6c2e9 Binary files /dev/null and b/Samples/Graphics/FramePacing/Assets/Logo.png differ diff --git a/Samples/Graphics/FramePacing/Assets/SmallLogo.png b/Samples/Graphics/FramePacing/Assets/SmallLogo.png new file mode 100644 index 00000000..40dba78d Binary files /dev/null and b/Samples/Graphics/FramePacing/Assets/SmallLogo.png differ diff --git a/Samples/Graphics/FramePacing/Assets/SplashScreen.png b/Samples/Graphics/FramePacing/Assets/SplashScreen.png new file mode 100644 index 00000000..4d4d8d33 Binary files /dev/null and b/Samples/Graphics/FramePacing/Assets/SplashScreen.png differ diff --git a/Samples/Graphics/FramePacing/Assets/StoreLogo.png b/Samples/Graphics/FramePacing/Assets/StoreLogo.png new file mode 100644 index 00000000..62cd3f2b Binary files /dev/null and b/Samples/Graphics/FramePacing/Assets/StoreLogo.png differ diff --git a/Samples/Graphics/FramePacing/Assets/UILayout.json b/Samples/Graphics/FramePacing/Assets/UILayout.json new file mode 100644 index 00000000..6f69afd3 --- /dev/null +++ b/Samples/Graphics/FramePacing/Assets/UILayout.json @@ -0,0 +1,509 @@ +{ + "styles": [ + { + "classId": "SpriteStyle", + "id": "background_panel_gray", + "colorUsage": "override", + "color": [ 25, 25, 25, 0.75 ] + }, + { + "classId": "SpriteStyle", + "id": "info_panel_gray", + "colorUsage": "override", + "color": [ 25, 25, 25, 0.80 ] + }, + { + "classId": "SpriteStyle", + "id": "legend_panel_gray", + "colorUsage": "override", + "color": [ 25, 25, 25, 0.85 ] + }, + { + "classId": "SpriteStyle", + "id": "graph_panel_gray", + "colorUsage": "override", + "color": [ 25, 25, 25, 0.95 ] + }, + { + "classId": "TextStyle", + "id": "label_text", + "colorUsage": "override", + "color": [ 140, 198, 199, 1.0 ], + "verticalAlign": "middle" + }, + { + "classId": "TextStyle", + "id": "highlight_label_text", + "colorUsage": "override", + "color": [ 219, 73, 28, 1.0 ], + "verticalAlign": "middle" + }, + { + "classId": "TextStyle", + "id": "legend_text", + "colorUsage": "override", + "color": [ 140, 198, 199, 1.0 ], + "verticalAlign": "middle", + "horizontalAlign": "center", + "legendFont": "Assets/Fonts/XboxOneControllerLegendSmall" + } + ], + "layout": { + "classId": "Panel", + "id": "root", + "positioningAnchor": [ "left", "top" ], + "position": [ 0, 0 ], + "sizingAnchor": [ "left", "top" ], + "size": [ 1920, 1080 ], + "style": { + "classId": "SpriteStyle", + "colorUsage": "override", + "color": [ 0, 0, 0, 0.0 ] + }, + "childElements": [ + { + "classId": "Panel", + "id": "stats_panel", + "positioningAnchor": [ "left", "top" ], + "position": [ 785, 0 ], + "sizingAnchor": [ "left", "top" ], + "size": [ 946, 460 ], + "styleId": "background_panel_gray", + "childElements": [ + { + "classId": "Panel", + "id": "frame_time_panel", + "positioningAnchor": [ "left", "top" ], + "position": [ 14, 14 ], + "sizingAnchor": [ "left", "top" ], + "size": [ 570, 134 ], + "styleId": "graph_panel_gray" + }, + { + "classId": "Panel", + "id": "fps_label_background", + "positioningAnchor": [ "right", "top" ], + "position": [ -199, 14 ], + "sizingAnchor": [ "right", "top" ], + "size": [ 149, 134 ], + "styleId": "info_panel_gray", + "childElements": [ + { + "classId": "StaticText", + "id": "fps_label", + "positioningAnchor": [ "right", "top" ], + "position": [ -10, 5 ], + "sizingAnchor": [ "right", "top" ], + "size": [ 100, 124 ], + "text": "average\nfps", + "style": { + "classId": "TextStyle", + "inheritsFromId": "label_text", + "horizontalAlign": "right" + } + } + ] + }, + { + "classId": "Panel", + "id": "fps_num_background", + "positioningAnchor": [ "right", "top" ], + "position": [ 0, 14 ], + "sizingAnchor": [ "right", "top" ], + "size": [ 199, 134 ], + "styleId": "graph_panel_gray", + "childElements": [ + { + "classId": "StaticText", + "id": "fps_num", + "positioningAnchor": [ "left", "top" ], + "position": [ 10, 5 ], + "sizingAnchor": [ "left", "top" ], + "size": [ 90, 124 ], + "text": "", + "style": { + "classId": "TextStyle", + "inheritsFromId": "highlight_label_text", + "size": 48 + } + } + ] + }, + { + "classId": "Panel", + "id": "latency_panel", + "positioningAnchor": [ "left", "top" ], + "position": [ 14, 162 ], + "sizingAnchor": [ "left", "top" ], + "size": [ 570, 134 ], + "styleId": "graph_panel_gray" + }, + { + "classId": "Panel", + "id": "latency_label_background", + "positioningAnchor": [ "right", "top" ], + "position": [ -199, 162 ], + "sizingAnchor": [ "right", "top" ], + "size": [ 149, 134 ], + "styleId": "info_panel_gray", + "childElements": [ + { + "classId": "StaticText", + "id": "latency_label", + "positioningAnchor": [ "right", "top" ], + "position": [ -10, 5 ], + "sizingAnchor": [ "right", "top" ], + "size": [ 100, 124 ], + "text": "ideal\nlatency (ms)", + "style": { + "classId": "TextStyle", + "inheritsFromId": "label_text", + "horizontalAlign": "right" + } + } + ] + }, + { + "classId": "Panel", + "id": "latency_num_background", + "positioningAnchor": [ "right", "top" ], + "position": [ 0, 162 ], + "sizingAnchor": [ "right", "top" ], + "size": [ 199, 134 ], + "styleId": "graph_panel_gray", + "childElements": [ + { + "classId": "StaticText", + "id": "latency_num", + "positioningAnchor": [ "left", "top" ], + "position": [ 10, 5 ], + "sizingAnchor": [ "left", "top" ], + "size": [ 90, 124 ], + "text": "", + "style": { + "classId": "TextStyle", + "inheritsFromId": "highlight_label_text", + "size": 48 + } + } + ] + }, + { + "classId": "Panel", + "id": "margin_panel", + "positioningAnchor": [ "left", "top" ], + "position": [ 14, 308 ], + "sizingAnchor": [ "left", "top" ], + "size": [ 570, 134 ], + "styleId": "graph_panel_gray" + }, + { + "classId": "Panel", + "id": "margin_label_background", + "positioningAnchor": [ "right", "top" ], + "position": [ -199, 308 ], + "sizingAnchor": [ "right", "top" ], + "size": [ 149, 134 ], + "styleId": "info_panel_gray", + "childElements": [ + { + "classId": "StaticText", + "id": "margin_label", + "positioningAnchor": [ "right", "top" ], + "position": [ -10, 5 ], + "sizingAnchor": [ "right", "top" ], + "size": [ 100, 124 ], + "text": "latency\nmargin (ms)", + "style": { + "classId": "TextStyle", + "inheritsFromId": "label_text", + "horizontalAlign": "right" + } + } + ] + }, + { + "classId": "Panel", + "id": "margin_num_background", + "positioningAnchor": [ "right", "top" ], + "position": [ 0, 308 ], + "sizingAnchor": [ "right", "top" ], + "size": [ 199, 134 ], + "styleId": "graph_panel_gray", + "childElements": [ + { + "classId": "StaticText", + "id": "margin_num", + "positioningAnchor": [ "left", "top" ], + "position": [ 10, 5 ], + "sizingAnchor": [ "left", "top" ], + "size": [ 90, 124 ], + "text": "", + "style": { + "classId": "TextStyle", + "inheritsFromId": "highlight_label_text", + "size": 48 + } + } + ] + } + ] + }, + { + "classId": "Panel", + "id": "timelines_panel", + "positioningAnchor": [ "left", "top" ], + "position": [ 15, 480 ], + "sizingAnchor": [ "left", "top" ], + "size": [ 1700, 515 ], + "styleId": "background_panel_gray", + "childElements": [ + { + "classId": "Panel", + "id": "cpu_update_panel", + "positioningAnchor": [ "left", "top" ], + "position": [ 15, 15 ], + "sizingAnchor": [ "left", "top" ], + "size": [ 1670, 85 ], + "styleId": "graph_panel_gray" + }, + { + "classId": "Panel", + "id": "cpu_render_panel", + "positioningAnchor": [ "left", "top" ], + "position": [ 15, 115 ], + "sizingAnchor": [ "left", "top" ], + "size": [ 1670, 85 ], + "styleId": "graph_panel_gray" + }, + { + "classId": "Panel", + "id": "gpu_graphics_panel", + "positioningAnchor": [ "left", "top" ], + "position": [ 15, 215 ], + "sizingAnchor": [ "left", "top" ], + "size": [ 1670, 85 ], + "styleId": "graph_panel_gray" + }, + { + "classId": "Panel", + "id": "gpu_compute_panel", + "positioningAnchor": [ "left", "top" ], + "position": [ 15, 315 ], + "sizingAnchor": [ "left", "top" ], + "size": [ 1670, 85 ], + "styleId": "graph_panel_gray" + }, + { + "classId": "Panel", + "id": "flip_panel", + "positioningAnchor": [ "left", "top" ], + "position": [ 15, 415 ], + "sizingAnchor": [ "left", "top" ], + "size": [ 1670, 85 ], + "styleId": "graph_panel_gray" + } + ] + }, + { + "classId": "Panel", + "id": "control_panel", + "positioningAnchor": [ "left", "top" ], + "position": [ 0, 0 ], + "sizingAnchor": [ "left", "top" ], + "size": [ 770, 460 ], + "styleId": "background_panel_gray", + "childElements": [ + { + "classId": "Panel", + "id": "x_legend", + "positioningAnchor": [ "left", "top" ], + "position": [ 15, 100 ], + "sizingAnchor": [ "left", "top" ], + "size": [ 175, 145 ], + "styleId": "legend_panel_gray", + "childElements": [ + { + "classId": "StaticText", + "id": "x_legend_text", + "positioningAnchor": [ "left", "top" ], + "position": [ 0, 0 ], + "text": "Frame load\nauto/manual\n[X]", + "isLegend": true, + "styleId": "legend_text" + }, + { + "classId": "Panel", + "id": "x_legend_border", + "positioningAnchor": [ "right", "top" ], + "position": [ 0, 0 ], + "sizingAnchor": [ "right", "top" ], + "size": [ 3, 145 ], + "style": { + "classId": "SpriteStyle", + "colorUsage": "override", + "color": [ 122, 104, 238, 1.0 ] + } + } + ] + }, + { + "classId": "Panel", + "id": "a_legend", + "positioningAnchor": [ "left", "top" ], + "position": [ 15, 250 ], + "sizingAnchor": [ "left", "top" ], + "size": [ 175, 145 ], + "styleId": "legend_panel_gray", + "childElements": [ + { + "classId": "StaticText", + "id": "a_legend_text", + "positioningAnchor": [ "left", "top" ], + "position": [ 0, 0 ], + "text": "Frame pacing\nauto/manual\n[A]", + "isLegend": true, + "styleId": "legend_text" + }, + { + "classId": "Panel", + "id": "a_legend_border", + "positioningAnchor": [ "right", "top" ], + "position": [ 0, 0 ], + "sizingAnchor": [ "right", "top" ], + "size": [ 3, 145 ], + "style": { + "classId": "SpriteStyle", + "colorUsage": "override", + "color": [ 34, 139, 34, 1.0 ] + } + } + ] + }, + { + "classId": "Panel", + "id": "menu_panel", + "positioningAnchor": [ "right", "bottom" ], + "position": [ -15, -15 ], + "sizingAnchor": [ "right", "bottom" ], + "size": [ 550, 380 ], + "styleId": "legend_panel_gray" + }, + { + "classId": "StaticText", + "id": "navigate_legend_text", + "positioningAnchor": [ "right", "top" ], + "position": [ -15, 10 ], + "sizingAnchor": [ "right", "top" ], + "size": [ 550, 50 ], + "text": "[LThumb] Navigate", + "isLegend": true, + "styleId": "legend_text" + } + ] + }, + { + "classId": "Panel", + "id": "rs_legend", + "positioningAnchor": [ "left", "top" ], + "position": [ 593, 1007 ], + "sizingAnchor": [ "left", "top" ], + "size": [ 224, 52 ], + "styleId": "legend_panel_gray", + "childElements": [ + { + "classId": "StaticText", + "id": "rs_legend_text", + "positioningAnchor": [ "left", "top" ], + "position": [ 0, 0 ], + "text": "[RThumb] Pan left/right", + "isLegend": true, + "styleId": "legend_text" + } + ] + }, + { + "classId": "Panel", + "id": "ltrt_legend", + "positioningAnchor": [ "left", "top" ], + "position": [ 905, 1007 ], + "sizingAnchor": [ "left", "top" ], + "size": [ 224, 52 ], + "styleId": "legend_panel_gray", + "childElements": [ + { + "classId": "StaticText", + "id": "ltrt_legend_text", + "positioningAnchor": [ "left", "top" ], + "position": [ 0, 0 ], + "text": "[LT]/[RT] Zoom out/in", + "isLegend": true, + "styleId": "legend_text" + } + ] + }, + { + "classId": "Panel", + "id": "b_legend", + "positioningAnchor": [ "left", "top" ], + "position": [ 1217, 1007 ], + "sizingAnchor": [ "left", "top" ], + "size": [ 224, 52 ], + "styleId": "legend_panel_gray", + "childElements": [ + { + "classId": "StaticText", + "id": "b_legend_text", + "positioningAnchor": [ "left", "top" ], + "position": [ 0, 0 ], + "text": "[B] Pause/Resume", + "isLegend": true, + "styleId": "legend_text" + } + ] + }, + { + "classId": "Panel", + "id": "exit_legend", + "positioningAnchor": [ "left", "top" ], + "position": [ 1529, 1007 ], + "sizingAnchor": [ "left", "top" ], + "size": [ 112, 52 ], + "styleId": "legend_panel_gray", + "childElements": [ + { + "classId": "StaticText", + "id": "exit_legend_text", + "positioningAnchor": [ "left", "top" ], + "position": [ 0, 0 ], + "text": "[View] Exit", + "isLegend": true, + "styleId": "legend_text" + } + ] + }, + { + "classId": "Panel", + "id": "tear_indicator_background", + "positioningAnchor": [ "right", "top" ], + "position": [ 0, 0 ], + "sizingAnchor": [ "right", "top" ], + "size": [ 189, 1080 ], + "styleId": "background_panel_gray" + }, + { + "classId": "Panel", + "id": "tear_indicator_border", + "positioningAnchor": [ "right", "top" ], + "position": [ -189, 0 ], + "sizingAnchor": [ "right", "top" ], + "size": [ 3, 1080 ], + "style": { + "classId": "SpriteStyle", + "colorUsage": "override", + "color": [ 140, 198, 199, 1.0 ] + } + } + ] + } +} \ No newline at end of file diff --git a/Samples/Graphics/FramePacing/EnforceGpuTime.hlsl b/Samples/Graphics/FramePacing/EnforceGpuTime.hlsl new file mode 100644 index 00000000..8f907785 --- /dev/null +++ b/Samples/Graphics/FramePacing/EnforceGpuTime.hlsl @@ -0,0 +1,41 @@ +//-------------------------------------------------------------------------------------- +// File: EnforceGpuTime.hlsl +// +// This compute shader creates a repeatable GPU workload by spinning until a specified +// time has passed. FramePacing uses it to demonstrate changing graphics and compute load. +// +// This should be compiled with /Od, or subsequent calls to __XB_MemTime() (in +// GetTimeStamp()) may be optimized away. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +//-------------------------------------------------------------------------------------- + +#include "Time.hlsli" + +#define RS \ +[\ + RootSignature\ + (\ + "RootConstants(num32BitConstants=2, b0),\ + UAV(u0)"\ + )\ +] + +cbuffer cb0 +{ + uint64_t g_desiredFrameTime; +} + +RWStructuredBuffer