forked from ocornut/imgui
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introduced a new Metal renderer implementation and a new example illu…
…strating usage of Metal on macOS and iOS (partially addresses ocornut#1873)
- Loading branch information
Showing
18 changed files
with
1,678 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
# iOS / OSX Metal example | ||
|
||
## Introduction | ||
|
||
This example shows how to render ImGui with Metal. It is based on the cross-platform game template provided with Xcode as of Xcode 9. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
|
||
#import <TargetConditionals.h> | ||
|
||
#if TARGET_OS_IPHONE | ||
|
||
#import <UIKit/UIKit.h> | ||
|
||
@interface AppDelegate : UIResponder <UIApplicationDelegate> | ||
@property (strong, nonatomic) UIWindow *window; | ||
@end | ||
|
||
#else | ||
|
||
#import <Cocoa/Cocoa.h> | ||
|
||
@interface AppDelegate : NSObject <NSApplicationDelegate> | ||
@end | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
|
||
#import "AppDelegate.h" | ||
|
||
@implementation AppDelegate | ||
|
||
#if TARGET_OS_OSX | ||
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender { | ||
return YES; | ||
} | ||
#endif | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
|
||
#import <MetalKit/MetalKit.h> | ||
|
||
@interface Renderer : NSObject <MTKViewDelegate> | ||
|
||
-(nonnull instancetype)initWithView:(nonnull MTKView *)view; | ||
|
||
@end | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
|
||
#import "Renderer.h" | ||
#import <Metal/Metal.h> | ||
|
||
#include "imgui.h" | ||
#include "imgui_impl_metal.h" | ||
|
||
#if TARGET_OS_OSX | ||
#include "imgui_impl_osx.h" | ||
#endif | ||
|
||
@interface Renderer () | ||
@property (nonatomic, strong) id <MTLDevice> device; | ||
@property (nonatomic, strong) id <MTLCommandQueue> commandQueue; | ||
@end | ||
|
||
@implementation Renderer | ||
|
||
-(nonnull instancetype)initWithView:(nonnull MTKView *)view; | ||
{ | ||
self = [super init]; | ||
if(self) | ||
{ | ||
_device = view.device; | ||
_commandQueue = [_device newCommandQueue]; | ||
|
||
IMGUI_CHECKVERSION(); | ||
ImGui::CreateContext(); | ||
(void)ImGui::GetIO(); | ||
|
||
ImGui_ImplMetal_Init(_device); | ||
|
||
ImGui::StyleColorsDark(); | ||
} | ||
|
||
return self; | ||
} | ||
|
||
- (void)drawInMTKView:(MTKView *)view | ||
{ | ||
ImGuiIO &io = ImGui::GetIO(); | ||
io.DisplaySize.x = view.bounds.size.width; | ||
io.DisplaySize.y = view.bounds.size.height; | ||
|
||
#if TARGET_OS_OSX | ||
CGFloat framebufferScale = view.window.screen.backingScaleFactor ?: NSScreen.mainScreen.backingScaleFactor; | ||
#else | ||
CGFloat framebufferScale = view.window.screen.scale ?: UIScreen.mainScreen.scale; | ||
#endif | ||
io.DisplayFramebufferScale = ImVec2(framebufferScale, framebufferScale); | ||
|
||
io.DeltaTime = 1 / float(view.preferredFramesPerSecond ?: 60); | ||
|
||
id<MTLCommandBuffer> commandBuffer = [self.commandQueue commandBuffer]; | ||
|
||
static bool show_demo_window = true; | ||
static bool show_another_window = false; | ||
static float clear_color[4] = { 0.28f, 0.36f, 0.5f, 1.0f }; | ||
|
||
MTLRenderPassDescriptor *renderPassDescriptor = view.currentRenderPassDescriptor; | ||
if(renderPassDescriptor != nil) | ||
{ | ||
renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(clear_color[0], clear_color[1], clear_color[2], clear_color[3]); | ||
|
||
// Here, you could do additional rendering work, including other passes as necessary. | ||
|
||
id <MTLRenderCommandEncoder> renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor]; | ||
|
||
[renderEncoder pushDebugGroup:@"Draw ImGui"]; | ||
|
||
ImGui_ImplMetal_NewFrame(renderPassDescriptor); | ||
#if TARGET_OS_OSX | ||
ImGui_ImplOSX_NewFrame(view); | ||
#endif | ||
ImGui::NewFrame(); | ||
|
||
{ | ||
static float f = 0.0f; | ||
static int counter = 0; | ||
ImGui::Text("Hello, world!"); // Display some text (you can use a format string too) | ||
ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f | ||
ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color | ||
|
||
ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our windows open/close state | ||
ImGui::Checkbox("Another Window", &show_another_window); | ||
|
||
if (ImGui::Button("Button")) // Buttons return true when clicked (NB: most widgets return true when edited/activated) | ||
counter++; | ||
ImGui::SameLine(); | ||
ImGui::Text("counter = %d", counter); | ||
|
||
ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate); | ||
} | ||
|
||
// 2. Show another simple window. In most cases you will use an explicit Begin/End pair to name your windows. | ||
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(); | ||
} | ||
|
||
// 3. Show the ImGui demo window. Most of the sample code is in ImGui::ShowDemoWindow(). Read its code to learn more about Dear ImGui! | ||
if (show_demo_window) | ||
{ | ||
// Normally user code doesn't need/want to call this because positions are saved in .ini file anyway. | ||
// Here we just want to make the demo initial state a bit more friendly! | ||
ImGui::SetNextWindowPos(ImVec2(650, 20), ImGuiCond_FirstUseEver); | ||
ImGui::ShowDemoWindow(&show_demo_window); | ||
} | ||
|
||
ImGui::Render(); | ||
ImDrawData *drawData = ImGui::GetDrawData(); | ||
ImGui_ImplMetal_RenderDrawData(drawData, commandBuffer, renderEncoder); | ||
|
||
[renderEncoder popDebugGroup]; | ||
|
||
[renderEncoder endEncoding]; | ||
|
||
[commandBuffer presentDrawable:view.currentDrawable]; | ||
} | ||
|
||
[commandBuffer commit]; | ||
} | ||
|
||
- (void)mtkView:(MTKView *)view drawableSizeWillChange:(CGSize)size | ||
{ | ||
} | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
|
||
#import <Metal/Metal.h> | ||
#import <MetalKit/MetalKit.h> | ||
#import "Renderer.h" | ||
|
||
#if TARGET_OS_IPHONE | ||
|
||
#import <UIKit/UIKit.h> | ||
|
||
@interface ViewController : UIViewController | ||
@end | ||
|
||
#else | ||
|
||
#import <Cocoa/Cocoa.h> | ||
|
||
@interface ViewController : NSViewController | ||
@end | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
|
||
#import "ViewController.h" | ||
#import "Renderer.h" | ||
#include "imgui.h" | ||
|
||
#if TARGET_OS_OSX | ||
#include "imgui_impl_osx.h" | ||
#endif | ||
|
||
@interface ViewController () | ||
@property (nonatomic, readonly) MTKView *mtkView; | ||
@property (nonatomic, strong) Renderer *renderer; | ||
@end | ||
|
||
@implementation ViewController | ||
|
||
- (MTKView *)mtkView { | ||
return (MTKView *)self.view; | ||
} | ||
|
||
- (void)viewDidLoad | ||
{ | ||
[super viewDidLoad]; | ||
|
||
self.mtkView.device = MTLCreateSystemDefaultDevice(); | ||
|
||
if (!self.mtkView.device) { | ||
NSLog(@"Metal is not supported"); | ||
abort(); | ||
} | ||
|
||
self.renderer = [[Renderer alloc] initWithView:self.mtkView]; | ||
|
||
[self.renderer mtkView:self.mtkView drawableSizeWillChange:self.mtkView.bounds.size]; | ||
|
||
self.mtkView.delegate = self.renderer; | ||
|
||
#if TARGET_OS_OSX | ||
// Add a tracking area in order to receive mouse events whenever the mouse is within the bounds of our view | ||
NSTrackingArea *trackingArea = [[NSTrackingArea alloc] initWithRect:NSZeroRect | ||
options:NSTrackingMouseMoved | NSTrackingInVisibleRect | NSTrackingActiveAlways | ||
owner:self | ||
userInfo:nil]; | ||
[self.view addTrackingArea:trackingArea]; | ||
|
||
// If we want to receive key events, we either need to be in the responder chain of the key view, | ||
// or else we can install a local monitor. The consequence of this heavy-handed approach is that | ||
// we receive events for all controls, not just ImGui widgets. If we had native controls in our | ||
// window, we'd want to be much more careful than just ingesting the complete event stream, though | ||
// we do make an effort to be good citizens by passing along events when ImGui doesn't want to capture. | ||
NSEventMask eventMask = NSEventMaskKeyDown | NSEventMaskKeyUp | NSEventMaskFlagsChanged | NSEventTypeScrollWheel; | ||
[NSEvent addLocalMonitorForEventsMatchingMask:eventMask handler:^NSEvent * _Nullable(NSEvent *event) { | ||
BOOL wantsCapture = ImGui_ImplOSX_HandleEvent(event, self.view); | ||
if (event.type == NSEventTypeKeyDown && wantsCapture) { | ||
return nil; | ||
} else { | ||
return event; | ||
} | ||
|
||
}]; | ||
|
||
ImGui_ImplOSX_Init(); | ||
#endif | ||
} | ||
|
||
#if TARGET_OS_OSX | ||
|
||
- (void)mouseMoved:(NSEvent *)event { | ||
ImGui_ImplOSX_HandleEvent(event, self.view); | ||
} | ||
|
||
- (void)mouseDown:(NSEvent *)event { | ||
ImGui_ImplOSX_HandleEvent(event, self.view); | ||
} | ||
|
||
- (void)mouseUp:(NSEvent *)event { | ||
ImGui_ImplOSX_HandleEvent(event, self.view); | ||
} | ||
|
||
- (void)mouseDragged:(NSEvent *)event { | ||
ImGui_ImplOSX_HandleEvent(event, self.view); | ||
} | ||
|
||
- (void)scrollWheel:(NSEvent *)event { | ||
ImGui_ImplOSX_HandleEvent(event, self.view); | ||
} | ||
|
||
#elif TARGET_OS_IOS | ||
|
||
// This touch mapping is super cheesy/hacky. We treat any touch on the screen | ||
// as if it were a depressed left mouse button, and we don't bother handling | ||
// multitouch correctly at all. This causes the "cursor" to behave very erratically | ||
// when there are multiple active touches. But for demo purposes, single-touch | ||
// interaction actually works surprisingly well. | ||
- (void)updateIOWithTouchEvent:(UIEvent *)event { | ||
UITouch *anyTouch = event.allTouches.anyObject; | ||
CGPoint touchLocation = [anyTouch locationInView:self.view]; | ||
ImGuiIO &io = ImGui::GetIO(); | ||
io.MousePos = ImVec2(touchLocation.x, touchLocation.y); | ||
|
||
BOOL hasActiveTouch = NO; | ||
for (UITouch *touch in event.allTouches) { | ||
if (touch.phase != UITouchPhaseEnded && touch.phase != UITouchPhaseCancelled) { | ||
hasActiveTouch = YES; | ||
break; | ||
} | ||
} | ||
io.MouseDown[0] = hasActiveTouch; | ||
} | ||
|
||
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { | ||
[self updateIOWithTouchEvent:event]; | ||
} | ||
|
||
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { | ||
[self updateIOWithTouchEvent:event]; | ||
} | ||
|
||
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { | ||
[self updateIOWithTouchEvent:event]; | ||
} | ||
|
||
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { | ||
[self updateIOWithTouchEvent:event]; | ||
} | ||
|
||
#endif | ||
|
||
@end | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
|
||
#import <TargetConditionals.h> | ||
|
||
#if TARGET_OS_IPHONE | ||
|
||
#import <UIKit/UIKit.h> | ||
#import "AppDelegate.h" | ||
|
||
int main(int argc, char * argv[]) { | ||
@autoreleasepool { | ||
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); | ||
} | ||
} | ||
|
||
#else | ||
|
||
#import <Cocoa/Cocoa.h> | ||
|
||
int main(int argc, const char * argv[]) { | ||
return NSApplicationMain(argc, argv); | ||
} | ||
|
||
#endif |
Oops, something went wrong.