cpu start
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
# Makefile for example_apple_cpu, for macOS only
|
||||
CXX = clang++
|
||||
EXE = example_apple_cpu
|
||||
IMGUI_DIR = ../..
|
||||
SOURCES = main.mm
|
||||
SOURCES += $(IMGUI_DIR)/imgui.cpp $(IMGUI_DIR)/imgui_demo.cpp $(IMGUI_DIR)/imgui_draw.cpp $(IMGUI_DIR)/imgui_tables.cpp $(IMGUI_DIR)/imgui_widgets.cpp
|
||||
SOURCES += $(IMGUI_DIR)/backends/imgui_impl_osx.mm $(IMGUI_DIR)/backends/imgui_impl_cpu.cpp
|
||||
|
||||
OPTFLAGS ?= -O2
|
||||
CXXFLAGS = -std=c++11 -ObjC++ -fobjc-arc -g $(OPTFLAGS) -Wall -Wextra -I$(IMGUI_DIR) -I$(IMGUI_DIR)/backends
|
||||
FRAMEWORKS = -framework Cocoa -framework GameController
|
||||
|
||||
all: $(EXE)
|
||||
@echo Build complete for Mac OS X
|
||||
|
||||
$(EXE): $(SOURCES)
|
||||
$(CXX) $(CXXFLAGS) $(SOURCES) -o $(EXE) $(FRAMEWORKS)
|
||||
|
||||
run: all
|
||||
./$(EXE)
|
||||
|
||||
clean:
|
||||
rm -f $(EXE) *.o
|
||||
@@ -0,0 +1,14 @@
|
||||
# How to Build
|
||||
|
||||
## macOS
|
||||
|
||||
Use the provided `Makefile` or directly:
|
||||
```sh
|
||||
clang++ -std=c++11 -ObjC++ -fobjc-arc -I ../.. -I ../../backends \
|
||||
main.mm ../../backends/imgui_impl_osx.mm ../../backends/imgui_impl_cpu.cpp ../../imgui*.cpp \
|
||||
-framework Cocoa -framework GameController \
|
||||
-o example_apple_cpu
|
||||
```
|
||||
|
||||
This example uses Cocoa + `imgui_impl_osx` for windowing/input and `imgui_impl_cpu` for rendering.
|
||||
It renders to a native BGRA CPU framebuffer and does not use SDL.
|
||||
Executable
BIN
Binary file not shown.
@@ -0,0 +1,424 @@
|
||||
// Dear ImGui: standalone macOS example using Cocoa + CPU framebuffer rendering.
|
||||
|
||||
// Learn about Dear ImGui:
|
||||
// - FAQ https://dearimgui.com/faq
|
||||
// - Getting Started https://dearimgui.com/getting-started
|
||||
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
|
||||
// - Introduction, links and more at the top of imgui.cpp
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#include "imgui.h"
|
||||
#include "imgui_impl_cpu.h"
|
||||
#include "imgui_impl_osx.h"
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <string.h>
|
||||
|
||||
struct ExampleTimingHistory
|
||||
{
|
||||
double Samples[120];
|
||||
int Count;
|
||||
int Offset;
|
||||
|
||||
ExampleTimingHistory() : Count(0), Offset(0) { memset(Samples, 0, sizeof(Samples)); }
|
||||
|
||||
void AddSample(double value_ms)
|
||||
{
|
||||
Samples[Offset] = value_ms;
|
||||
Offset = (Offset + 1) % IM_ARRAYSIZE(Samples);
|
||||
if (Count < IM_ARRAYSIZE(Samples))
|
||||
Count++;
|
||||
}
|
||||
|
||||
double GetAverageMs() const
|
||||
{
|
||||
if (Count == 0)
|
||||
return 0.0;
|
||||
double total = 0.0;
|
||||
for (int i = 0; i < Count; i++)
|
||||
total += Samples[i];
|
||||
return total / (double)Count;
|
||||
}
|
||||
};
|
||||
|
||||
struct ExampleFramebuffer
|
||||
{
|
||||
ImVector<unsigned char> Storage;
|
||||
ImGui_ImplCPU_Framebuffer CPU;
|
||||
CGColorSpaceRef ColorSpace;
|
||||
CGDataProviderRef DataProvider;
|
||||
CGImageRef Image;
|
||||
|
||||
ExampleFramebuffer() : ColorSpace(nullptr), DataProvider(nullptr), Image(nullptr)
|
||||
{
|
||||
CPU.Pixels = nullptr;
|
||||
CPU.Width = 0;
|
||||
CPU.Height = 0;
|
||||
CPU.Pitch = 0;
|
||||
CPU.Format = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
static double ExampleGetTimeMs()
|
||||
{
|
||||
typedef std::chrono::steady_clock Clock;
|
||||
return std::chrono::duration<double, std::milli>(Clock::now().time_since_epoch()).count();
|
||||
}
|
||||
|
||||
static void DestroyFramebuffer(ExampleFramebuffer* framebuffer)
|
||||
{
|
||||
if (framebuffer->Image != nullptr)
|
||||
{
|
||||
CGImageRelease(framebuffer->Image);
|
||||
framebuffer->Image = nullptr;
|
||||
}
|
||||
if (framebuffer->DataProvider != nullptr)
|
||||
{
|
||||
CGDataProviderRelease(framebuffer->DataProvider);
|
||||
framebuffer->DataProvider = nullptr;
|
||||
}
|
||||
if (framebuffer->ColorSpace != nullptr)
|
||||
{
|
||||
CGColorSpaceRelease(framebuffer->ColorSpace);
|
||||
framebuffer->ColorSpace = nullptr;
|
||||
}
|
||||
|
||||
framebuffer->CPU.Pixels = nullptr;
|
||||
framebuffer->CPU.Width = 0;
|
||||
framebuffer->CPU.Height = 0;
|
||||
framebuffer->CPU.Pitch = 0;
|
||||
framebuffer->Storage.clear();
|
||||
}
|
||||
|
||||
static ImU32 PackBGRA(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
|
||||
{
|
||||
#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
|
||||
return ((ImU32)b << 24) | ((ImU32)g << 16) | ((ImU32)r << 8) | (ImU32)a;
|
||||
#else
|
||||
return (ImU32)b | ((ImU32)g << 8) | ((ImU32)r << 16) | ((ImU32)a << 24);
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool RecreateFramebuffer(ExampleFramebuffer* framebuffer, int width, int height)
|
||||
{
|
||||
DestroyFramebuffer(framebuffer);
|
||||
|
||||
framebuffer->Storage.resize(width * height * 4);
|
||||
framebuffer->CPU.Pixels = framebuffer->Storage.Data;
|
||||
framebuffer->CPU.Width = width;
|
||||
framebuffer->CPU.Height = height;
|
||||
framebuffer->CPU.Pitch = width * 4;
|
||||
framebuffer->CPU.Format = ImGui_ImplCPU_GetBuiltinPixelFormat(ImGui_ImplCPU_BuiltinFormat_BGRA8888);
|
||||
if (framebuffer->CPU.Pixels == nullptr)
|
||||
return false;
|
||||
|
||||
framebuffer->ColorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
if (framebuffer->ColorSpace == nullptr)
|
||||
{
|
||||
DestroyFramebuffer(framebuffer);
|
||||
return false;
|
||||
}
|
||||
|
||||
framebuffer->DataProvider = CGDataProviderCreateWithData(nullptr, framebuffer->CPU.Pixels, (size_t)framebuffer->CPU.Pitch * (size_t)framebuffer->CPU.Height, nullptr);
|
||||
if (framebuffer->DataProvider == nullptr)
|
||||
{
|
||||
DestroyFramebuffer(framebuffer);
|
||||
return false;
|
||||
}
|
||||
|
||||
framebuffer->Image = CGImageCreate((size_t)framebuffer->CPU.Width, (size_t)framebuffer->CPU.Height, 8, 32, (size_t)framebuffer->CPU.Pitch, framebuffer->ColorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst, framebuffer->DataProvider, nullptr, false, kCGRenderingIntentDefault);
|
||||
if (framebuffer->Image == nullptr)
|
||||
{
|
||||
DestroyFramebuffer(framebuffer);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ClearFramebuffer(ImGui_ImplCPU_Framebuffer* framebuffer, unsigned char r, unsigned char g, unsigned char b, unsigned char a)
|
||||
{
|
||||
const ImU32 pixel = PackBGRA(r, g, b, a);
|
||||
for (int y = 0; y < framebuffer->Height; y++)
|
||||
{
|
||||
ImU32* dst = (ImU32*)(void*)(framebuffer->Pixels + (size_t)y * (size_t)framebuffer->Pitch);
|
||||
for (int x = 0; x < framebuffer->Width; x++)
|
||||
dst[x] = pixel;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------
|
||||
// AppView
|
||||
//-----------------------------------------------------------------------------------
|
||||
|
||||
@interface AppView : NSView
|
||||
- (void)shutdownImGui;
|
||||
@end
|
||||
|
||||
@implementation AppView
|
||||
{
|
||||
ExampleFramebuffer _framebuffer;
|
||||
ExampleTimingHistory _rasterTimes;
|
||||
ExampleTimingHistory _presentTimes;
|
||||
BOOL _imguiInitialized;
|
||||
BOOL _frameScheduled;
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(NSRect)frame
|
||||
{
|
||||
self = [super initWithFrame:frame];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)isFlipped
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)viewDidMoveToWindow
|
||||
{
|
||||
[super viewDidMoveToWindow];
|
||||
if (self.window != nil && !_imguiInitialized)
|
||||
[self initializeImGui];
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[self shutdownImGui];
|
||||
}
|
||||
|
||||
- (void)initializeImGui
|
||||
{
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
ImGuiIO& io = ImGui::GetIO(); (void)io;
|
||||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
|
||||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
|
||||
|
||||
ImGui::StyleColorsDark();
|
||||
|
||||
ImGui_ImplOSX_Init(self);
|
||||
ImGui_ImplCPU_Init();
|
||||
|
||||
_imguiInitialized = YES;
|
||||
[self scheduleNextFrameAfter:0.0];
|
||||
}
|
||||
|
||||
- (void)shutdownImGui
|
||||
{
|
||||
if (!_imguiInitialized)
|
||||
return;
|
||||
|
||||
DestroyFramebuffer(&_framebuffer);
|
||||
ImGui_ImplCPU_Shutdown();
|
||||
ImGui_ImplOSX_Shutdown();
|
||||
ImGui::DestroyContext();
|
||||
_imguiInitialized = NO;
|
||||
}
|
||||
|
||||
// Queue the next draw on the main loop instead of pacing to display refresh.
|
||||
- (void)scheduleNextFrameAfter:(double)delay_seconds
|
||||
{
|
||||
if (_frameScheduled)
|
||||
return;
|
||||
_frameScheduled = YES;
|
||||
dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay_seconds * (double)NSEC_PER_SEC));
|
||||
dispatch_after(when, dispatch_get_main_queue(), ^{
|
||||
self->_frameScheduled = NO;
|
||||
[self setNeedsDisplay:YES];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)renderFrame
|
||||
{
|
||||
if (!_imguiInitialized)
|
||||
return;
|
||||
|
||||
if (self.window == nil || [self.window isMiniaturized])
|
||||
{
|
||||
[self scheduleNextFrameAfter:0.01];
|
||||
return;
|
||||
}
|
||||
|
||||
const CGFloat scale = self.window.backingScaleFactor > 0.0 ? self.window.backingScaleFactor : 1.0;
|
||||
const int logical_w = (int)llround(self.bounds.size.width);
|
||||
const int logical_h = (int)llround(self.bounds.size.height);
|
||||
const int framebuffer_w = (int)llround(self.bounds.size.width * scale);
|
||||
const int framebuffer_h = (int)llround(self.bounds.size.height * scale);
|
||||
if (framebuffer_w <= 0 || framebuffer_h <= 0)
|
||||
{
|
||||
[self scheduleNextFrameAfter:0.01];
|
||||
return;
|
||||
}
|
||||
|
||||
if (_framebuffer.CPU.Width != framebuffer_w || _framebuffer.CPU.Height != framebuffer_h)
|
||||
if (!RecreateFramebuffer(&_framebuffer, framebuffer_w, framebuffer_h))
|
||||
return;
|
||||
|
||||
// Start the Dear ImGui frame
|
||||
ImGui_ImplCPU_NewFrame();
|
||||
ImGui_ImplOSX_NewFrame(self);
|
||||
ImGui::NewFrame();
|
||||
|
||||
// Our state
|
||||
static bool show_demo_window = true;
|
||||
static bool show_another_window = false;
|
||||
static ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
|
||||
|
||||
// 1. Show the big demo window
|
||||
if (show_demo_window)
|
||||
ImGui::ShowDemoWindow(&show_demo_window);
|
||||
|
||||
// 2. Show a simple window that we create ourselves.
|
||||
{
|
||||
static float f = 0.0f;
|
||||
static int counter = 0;
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
ImGui::Begin("Hello, world!");
|
||||
|
||||
ImGui::Text("This is some useful text.");
|
||||
ImGui::Checkbox("Demo Window", &show_demo_window);
|
||||
ImGui::Checkbox("Another Window", &show_another_window);
|
||||
|
||||
ImGui::SliderFloat("float", &f, 0.0f, 1.0f);
|
||||
ImGui::ColorEdit3("clear color", (float*)&clear_color);
|
||||
|
||||
if (ImGui::Button("Button"))
|
||||
counter++;
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("counter = %d", counter);
|
||||
|
||||
ImGui::Text("Raster %.3f ms | Present %.3f ms", _rasterTimes.GetAverageMs(), _presentTimes.GetAverageMs());
|
||||
ImGui::Text("Window %d x %d | Framebuffer %d x %d (scale %.2f)", logical_w, logical_h, framebuffer_w, framebuffer_h, scale);
|
||||
ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
// 3. Show another simple window.
|
||||
if (show_another_window)
|
||||
{
|
||||
ImGui::Begin("Another Window", &show_another_window);
|
||||
ImGui::Text("Hello from another window!");
|
||||
if (ImGui::Button("Close Me"))
|
||||
show_another_window = false;
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
// Rendering
|
||||
ImGui::Render();
|
||||
const double raster_start_ms = ExampleGetTimeMs();
|
||||
ClearFramebuffer(&_framebuffer.CPU,
|
||||
(unsigned char)(clear_color.x * 255),
|
||||
(unsigned char)(clear_color.y * 255),
|
||||
(unsigned char)(clear_color.z * 255),
|
||||
(unsigned char)(clear_color.w * 255));
|
||||
ImGui_ImplCPU_RenderDrawData(ImGui::GetDrawData(), &_framebuffer.CPU);
|
||||
_rasterTimes.AddSample(ExampleGetTimeMs() - raster_start_ms);
|
||||
}
|
||||
|
||||
- (void)drawRect:(NSRect)dirtyRect
|
||||
{
|
||||
IM_UNUSED(dirtyRect);
|
||||
|
||||
[self renderFrame];
|
||||
|
||||
if (_framebuffer.Image != nullptr)
|
||||
{
|
||||
CGContextRef context = NSGraphicsContext.currentContext.CGContext;
|
||||
// CGImage draws with a bottom-up raster convention here, while the CPU framebuffer is top-down.
|
||||
const double present_start_ms = ExampleGetTimeMs();
|
||||
CGContextSaveGState(context);
|
||||
CGContextTranslateCTM(context, 0.0, self.bounds.size.height);
|
||||
CGContextScaleCTM(context, 1.0, -1.0);
|
||||
CGContextDrawImage(context, CGRectMake(0.0, 0.0, self.bounds.size.width, self.bounds.size.height), _framebuffer.Image);
|
||||
CGContextRestoreGState(context);
|
||||
_presentTimes.AddSample(ExampleGetTimeMs() - present_start_ms);
|
||||
}
|
||||
|
||||
[self scheduleNextFrameAfter:0.0];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
//-----------------------------------------------------------------------------------
|
||||
// AppDelegate
|
||||
//-----------------------------------------------------------------------------------
|
||||
|
||||
@interface AppDelegate : NSObject <NSApplicationDelegate>
|
||||
@property (nonatomic, readonly) NSWindow* window;
|
||||
@property (nonatomic, readonly) AppView* view;
|
||||
@end
|
||||
|
||||
@implementation AppDelegate
|
||||
{
|
||||
NSWindow* _window;
|
||||
AppView* _view;
|
||||
}
|
||||
|
||||
@synthesize window = _window;
|
||||
@synthesize view = _view;
|
||||
|
||||
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)application
|
||||
{
|
||||
IM_UNUSED(application);
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)setupMenu
|
||||
{
|
||||
NSMenu* main_menu_bar = [[NSMenu alloc] init];
|
||||
NSMenu* app_menu = [[NSMenu alloc] initWithTitle:@"Dear ImGui macOS+CPU Example"];
|
||||
NSMenuItem* menu_item = [app_menu addItemWithTitle:@"Quit Dear ImGui macOS+CPU Example" action:@selector(terminate:) keyEquivalent:@"q"];
|
||||
[menu_item setKeyEquivalentModifierMask:NSEventModifierFlagCommand];
|
||||
|
||||
menu_item = [[NSMenuItem alloc] init];
|
||||
[menu_item setSubmenu:app_menu];
|
||||
[main_menu_bar addItem:menu_item];
|
||||
[NSApp setMainMenu:main_menu_bar];
|
||||
}
|
||||
|
||||
- (void)applicationDidFinishLaunching:(NSNotification*)notification
|
||||
{
|
||||
IM_UNUSED(notification);
|
||||
|
||||
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
|
||||
[self setupMenu];
|
||||
|
||||
NSRect view_rect = NSMakeRect(100.0, 100.0, 1280.0, 720.0);
|
||||
_window = [[NSWindow alloc] initWithContentRect:view_rect styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable | NSWindowStyleMaskClosable backing:NSBackingStoreBuffered defer:NO];
|
||||
[_window setTitle:@"Dear ImGui macOS+CPU Example"];
|
||||
[_window setAcceptsMouseMovedEvents:YES];
|
||||
[_window setOpaque:YES];
|
||||
|
||||
_view = [[AppView alloc] initWithFrame:_window.contentView.bounds];
|
||||
[_view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
|
||||
[_window setContentView:_view];
|
||||
[_window makeKeyAndOrderFront:NSApp];
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
}
|
||||
|
||||
- (void)applicationWillTerminate:(NSNotification*)notification
|
||||
{
|
||||
IM_UNUSED(notification);
|
||||
[_view shutdownImGui];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
IM_UNUSED(argc);
|
||||
IM_UNUSED(argv);
|
||||
|
||||
@autoreleasepool
|
||||
{
|
||||
NSApplication* application = [NSApplication sharedApplication];
|
||||
AppDelegate* delegate = [AppDelegate new];
|
||||
[application setDelegate:delegate];
|
||||
[application run];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user