10 KB、210行 生 Blame 履歴
1// Dear ImGui: standalone example application for SDL3 + Metal
2// (SDL is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan/Metal graphics context creation, etc.)
3
4// Learn about Dear ImGui:
5// - FAQ https://dearimgui.com/faq
6// - Getting Started https://dearimgui.com/getting-started
7// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
8// - Introduction, links and more at the top of imgui.cpp
9
10#include "imgui.h"
11#include "imgui_impl_sdl3.h"
12#include "imgui_impl_metal.h"
13#include <stdio.h> // printf, fprintf
14#include <SDL3/SDL.h>
15
16#import <Metal/Metal.h>
17#import <QuartzCore/QuartzCore.h>
18
19// Main code
20int main(int, char**)
21{
22 // Setup SDL
23 // [If using SDL_MAIN_USE_CALLBACKS: all code below until the main loop starts would likely be your SDL_AppInit() function]
24 if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD))
25 {
26 printf("Error: SDL_Init(): %s\n", SDL_GetError());
27 return 1;
28 }
29
30 // Create SDL window graphics context
31 float main_scale = SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay());
32 SDL_WindowFlags window_flags = SDL_WINDOW_METAL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIDDEN | SDL_WINDOW_HIGH_PIXEL_DENSITY;
33 SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL3+Metal example", (int)(1280 * main_scale), (int)(800 * main_scale), window_flags);
34 if (window == nullptr)
35 {
36 printf("Error: SDL_CreateWindow(): %s\n", SDL_GetError());
37 return 1;
38 }
39 SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
40 SDL_ShowWindow(window);
41
42 // Create Metal device _before_ creating the view/layer
43 id<MTLDevice> metalDevice = MTLCreateSystemDefaultDevice();
44 if (!metalDevice)
45 {
46 printf("Error: failed to create Metal device.\n");
47 SDL_DestroyWindow(window);
48 SDL_Quit();
49 return 1;
50 }
51 SDL_MetalView view = SDL_Metal_CreateView(window);
52 CAMetalLayer* layer = (__bridge CAMetalLayer*)SDL_Metal_GetLayer(view);
53 layer.device = metalDevice;
54 layer.pixelFormat = MTLPixelFormatBGRA8Unorm;
55
56 id<MTLCommandQueue> commandQueue = [layer.device newCommandQueue];
57 MTLRenderPassDescriptor* renderPassDescriptor = [MTLRenderPassDescriptor new];
58
59 // Setup Dear ImGui context
60 IMGUI_CHECKVERSION();
61 ImGui::CreateContext();
62 ImGuiIO& io = ImGui::GetIO(); (void)io;
63 io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
64 io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
65
66 // Setup Dear ImGui style
67 ImGui::StyleColorsDark();
68 //ImGui::StyleColorsLight();
69
70 // Setup scaling
71 ImGuiStyle& style = ImGui::GetStyle();
72 style.ScaleAllSizes(main_scale); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this requires resetting Style + calling this again)
73 style.FontScaleDpi = main_scale; // Set initial font scale. (in docking branch: using io.ConfigDpiScaleFonts=true automatically overrides this for every window depending on the current monitor)
74
75 // Setup Platform/Renderer backends
76 ImGui_ImplMetal_Init(layer.device);
77 ImGui_ImplSDL3_InitForMetal(window);
78
79 // Load Fonts
80 // - If fonts are not explicitly loaded, Dear ImGui will select an embedded font: either AddFontDefaultVector() or AddFontDefaultBitmap().
81 // This selection is based on (style.FontSizeBase * style.FontScaleMain * style.FontScaleDpi) reaching a small threshold.
82 // - You can load multiple fonts and use ImGui::PushFont()/PopFont() to select them.
83 // - If a file cannot be loaded, AddFont functions will return a nullptr. Please handle those errors in your code (e.g. use an assertion, display an error and quit).
84 // - Read 'docs/FONTS.md' for more instructions and details.
85 // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use FreeType for higher quality font rendering.
86 // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ !
87 //style.FontSizeBase = 20.0f;
88 //io.Fonts->AddFontDefaultVector();
89 //io.Fonts->AddFontDefaultBitmap();
90 //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf");
91 //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf");
92 //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf");
93 //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf");
94 //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf");
95 //IM_ASSERT(font != nullptr);
96
97 // Our state
98 bool show_demo_window = true;
99 bool show_another_window = false;
100 float clear_color[4] = { 0.45f, 0.55f, 0.60f, 1.00f };
101
102 // Main loop
103 bool done = false;
104 while (!done)
105 {
106 @autoreleasepool
107 {
108 // Poll and handle events (inputs, window resize, etc.)
109 // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
110 // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data.
111 // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.
112 // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
113 // [If using SDL_MAIN_USE_CALLBACKS: call ImGui_ImplSDL3_ProcessEvent() from your SDL_AppEvent() function]
114 SDL_Event event;
115 while (SDL_PollEvent(&event))
116 {
117 ImGui_ImplSDL3_ProcessEvent(&event);
118 if (event.type == SDL_EVENT_QUIT)
119 done = true;
120 if (event.type == SDL_EVENT_WINDOW_CLOSE_REQUESTED && event.window.windowID == SDL_GetWindowID(window))
121 done = true;
122 }
123
124 // [If using SDL_MAIN_USE_CALLBACKS: all code below would likely be your SDL_AppIterate() function]
125 if (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED)
126 {
127 SDL_Delay(10);
128 continue;
129 }
130
131 int width, height;
132 SDL_GetWindowSizeInPixels(window, &width, &height);
133
134 layer.drawableSize = CGSizeMake(width, height);
135 id<CAMetalDrawable> drawable = [layer nextDrawable];
136
137 id<MTLCommandBuffer> commandBuffer = [commandQueue commandBuffer];
138 renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(clear_color[0] * clear_color[3], clear_color[1] * clear_color[3], clear_color[2] * clear_color[3], clear_color[3]);
139 renderPassDescriptor.colorAttachments[0].texture = drawable.texture;
140 renderPassDescriptor.colorAttachments[0].loadAction = MTLLoadActionClear;
141 renderPassDescriptor.colorAttachments[0].storeAction = MTLStoreActionStore;
142 id <MTLRenderCommandEncoder> renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
143 [renderEncoder pushDebugGroup:@"ImGui demo"];
144
145 // Start the Dear ImGui frame
146 ImGui_ImplMetal_NewFrame(renderPassDescriptor);
147 ImGui_ImplSDL3_NewFrame();
148 ImGui::NewFrame();
149
150 // 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!).
151 if (show_demo_window)
152 ImGui::ShowDemoWindow(&show_demo_window);
153
154 // 2. Show a simple window that we create ourselves. We use a Begin/End pair to create a named window.
155 {
156 static float f = 0.0f;
157 static int counter = 0;
158
159 ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it.
160
161 ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too)
162 ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our window open/close state
163 ImGui::Checkbox("Another Window", &show_another_window);
164
165 ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f
166 ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color
167
168 if (ImGui::Button("Button")) // Buttons return true when clicked (most widgets return true when edited/activated)
169 counter++;
170 ImGui::SameLine();
171 ImGui::Text("counter = %d", counter);
172
173 ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
174 ImGui::End();
175 }
176
177 // 3. Show another simple window.
178 if (show_another_window)
179 {
180 ImGui::Begin("Another Window", &show_another_window); // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked)
181 ImGui::Text("Hello from another window!");
182 if (ImGui::Button("Close Me"))
183 show_another_window = false;
184 ImGui::End();
185 }
186
187 // Rendering
188 ImGui::Render();
189 ImDrawData* draw_data = ImGui::GetDrawData();
190 ImGui_ImplMetal_RenderDrawData(draw_data, commandBuffer, renderEncoder);
191
192 [renderEncoder popDebugGroup];
193 [renderEncoder endEncoding];
194
195 [commandBuffer presentDrawable:drawable];
196 [commandBuffer commit];
197 }
198 }
199
200 // Cleanup
201 // [If using SDL_MAIN_USE_CALLBACKS: all code below would likely be your SDL_AppQuit() function]
202 ImGui_ImplMetal_Shutdown();
203 ImGui_ImplSDL3_Shutdown();
204 ImGui::DestroyContext();
205
206 SDL_DestroyWindow(window);
207 SDL_Quit();
208
209 return 0;
210}