Skip to content

Commit

Permalink
Examples: Comments + shallow coding convention tweak to be consistent…
Browse files Browse the repository at this point in the history
… across examples and with imgui_impl_osx
  • Loading branch information
ocornut committed Jul 8, 2018
1 parent 569e0f0 commit 89e2ddf
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 38 deletions.
4 changes: 2 additions & 2 deletions examples/README.txt
Original file line number Diff line number Diff line change
Expand Up @@ -166,12 +166,12 @@ example_win32_directx12/
example_apple_metal/
OSX & iOS + Metal.
It is based on the "cross-platform" game template provided with Xcode as of Xcode 9.
Note that instead of the OSX bindings, you may want to use GLFW or SDL which will also support Windows, Linux along with OSX.
(NB: you may still want to use GLFW or SDL which will also support Windows, Linux along with OSX.)
= game template + imgui_impl_osx.mm + imgui_impl_metal.mm

example_apple_opengl2/
OSX + OpenGL2.
Note that instead of the OSX bindings, you may want to use GLFW or SDL which will also support Windows, Linux along with OSX.
(NB: you may still want to use GLFW or SDL which will also support Windows, Linux along with OSX.)
= main.mm + imgui_impl_osx.mm + imgui_impl_opengl2.cpp

example_glfw_opengl2/
Expand Down
4 changes: 3 additions & 1 deletion examples/example_apple_metal/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@

## 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 example shows how to integrate Dear ImGui with Metal. It is based on the "cross-platform" game template provided with Xcode as of Xcode 9.

(NB: you may still want to use GLFW or SDL which will also support Windows, Linux along with OSX.)

97 changes: 62 additions & 35 deletions examples/imgui_impl_metal.mm
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,10 @@ void ImGui_ImplMetal_DestroyDeviceObjects()
#pragma mark - MetalBuffer implementation

@implementation MetalBuffer
- (instancetype)initWithBuffer:(id<MTLBuffer>)buffer {
if ((self = [super init])) {
- (instancetype)initWithBuffer:(id<MTLBuffer>)buffer
{
if ((self = [super init]))
{
_buffer = buffer;
_lastReuseTime = [NSDate date].timeIntervalSince1970;
}
Expand All @@ -140,8 +142,10 @@ - (instancetype)initWithBuffer:(id<MTLBuffer>)buffer {
#pragma mark - FramebufferDescriptor implementation

@implementation FramebufferDescriptor
- (instancetype)initWithRenderPassDescriptor:(MTLRenderPassDescriptor *)renderPassDescriptor {
if ((self = [super init])) {
- (instancetype)initWithRenderPassDescriptor:(MTLRenderPassDescriptor *)renderPassDescriptor
{
if ((self = [super init]))
{
_sampleCount = renderPassDescriptor.colorAttachments[0].texture.sampleCount;
_colorPixelFormat = renderPassDescriptor.colorAttachments[0].texture.pixelFormat;
_depthPixelFormat = renderPassDescriptor.depthAttachment.texture.pixelFormat;
Expand All @@ -150,7 +154,8 @@ - (instancetype)initWithRenderPassDescriptor:(MTLRenderPassDescriptor *)renderPa
return self;
}

- (nonnull id)copyWithZone:(nullable NSZone *)zone {
- (nonnull id)copyWithZone:(nullable NSZone *)zone
{
FramebufferDescriptor *copy = [[FramebufferDescriptor allocWithZone:zone] init];
copy.sampleCount = self.sampleCount;
copy.colorPixelFormat = self.colorPixelFormat;
Expand All @@ -159,7 +164,8 @@ - (nonnull id)copyWithZone:(nullable NSZone *)zone {
return copy;
}

- (NSUInteger)hash {
- (NSUInteger)hash
{
NSUInteger sc = _sampleCount & 0x3;
NSUInteger cf = _colorPixelFormat & 0x3FF;
NSUInteger df = _depthPixelFormat & 0x3FF;
Expand All @@ -168,12 +174,12 @@ - (NSUInteger)hash {
return hash;
}

- (BOOL)isEqual:(id)object {
- (BOOL)isEqual:(id)object
{
FramebufferDescriptor *other = object;
if (![other isKindOfClass:[FramebufferDescriptor class]]) {
if (![other isKindOfClass:[FramebufferDescriptor class]])
return NO;
}
return other.sampleCount == self.sampleCount &&
return other.sampleCount == self.sampleCount &&
other.colorPixelFormat == self.colorPixelFormat &&
other.depthPixelFormat == self.depthPixelFormat &&
other.stencilPixelFormat == self.stencilPixelFormat;
Expand All @@ -185,22 +191,29 @@ - (BOOL)isEqual:(id)object {

@implementation MetalContext
- (instancetype)init {
if ((self = [super init])) {
if ((self = [super init]))
{
_renderPipelineStateCache = [NSMutableDictionary dictionary];
_bufferCache = [NSMutableArray array];
_lastBufferCachePurge = [NSDate date].timeIntervalSince1970;
}
return self;
}

- (void)makeDeviceObjectsWithDevice:(id<MTLDevice>)device {
- (void)makeDeviceObjectsWithDevice:(id<MTLDevice>)device
{
MTLDepthStencilDescriptor *depthStencilDescriptor = [[MTLDepthStencilDescriptor alloc] init];
depthStencilDescriptor.depthWriteEnabled = NO;
depthStencilDescriptor.depthCompareFunction = MTLCompareFunctionAlways;
self.depthStencilState = [device newDepthStencilStateWithDescriptor:depthStencilDescriptor];
}

- (void)makeFontTextureWithDevice:(id<MTLDevice>)device {
// We are retrieving and uploading the font atlas as a 4-channels RGBA texture here.
// In theory we could call GetTexDataAsAlpha8() and upload a 1-channel texture to save on memory access bandwidth.
// However, using a shader designed for 1-channel texture would make it less obvious to use the ImTextureID facility to render users own textures.
// You can make that change in your implementation.
- (void)makeFontTextureWithDevice:(id<MTLDevice>)device
{
ImGuiIO &io = ImGui::GetIO();
unsigned char* pixels;
int width, height;
Expand All @@ -220,14 +233,18 @@ - (void)makeFontTextureWithDevice:(id<MTLDevice>)device {
self.fontTexture = texture;
}

- (MetalBuffer *)dequeueReusableBufferOfLength:(NSUInteger)length device:(id<MTLDevice>)device {
- (MetalBuffer *)dequeueReusableBufferOfLength:(NSUInteger)length device:(id<MTLDevice>)device
{
NSTimeInterval now = [NSDate date].timeIntervalSince1970;

// Purge old buffers that haven't been useful for a while
if (now - self.lastBufferCachePurge > 1.0) {
if (now - self.lastBufferCachePurge > 1.0)
{
NSMutableArray *survivors = [NSMutableArray array];
for (MetalBuffer *candidate in self.bufferCache) {
if (candidate.lastReuseTime > self.lastBufferCachePurge) {
for (MetalBuffer *candidate in self.bufferCache)
{
if (candidate.lastReuseTime > self.lastBufferCachePurge)
{
[survivors addObject:candidate];
}
}
Expand All @@ -237,13 +254,12 @@ - (MetalBuffer *)dequeueReusableBufferOfLength:(NSUInteger)length device:(id<MTL

// See if we have a buffer we can reuse
MetalBuffer *bestCandidate = nil;
for (MetalBuffer *candidate in self.bufferCache) {
if (candidate.buffer.length >= length && (bestCandidate == nil || bestCandidate.lastReuseTime > candidate.lastReuseTime)) {
for (MetalBuffer *candidate in self.bufferCache)
if (candidate.buffer.length >= length && (bestCandidate == nil || bestCandidate.lastReuseTime > candidate.lastReuseTime))
bestCandidate = candidate;
}
}

if (bestCandidate != nil) {
if (bestCandidate != nil)
{
[self.bufferCache removeObject:bestCandidate];
bestCandidate.lastReuseTime = now;
return bestCandidate;
Expand All @@ -254,16 +270,19 @@ - (MetalBuffer *)dequeueReusableBufferOfLength:(NSUInteger)length device:(id<MTL
return [[MetalBuffer alloc] initWithBuffer:backing];
}

- (void)enqueueReusableBuffer:(MetalBuffer *)buffer {
- (void)enqueueReusableBuffer:(MetalBuffer *)buffer
{
[self.bufferCache addObject:buffer];
}

- (_Nullable id<MTLRenderPipelineState>)renderPipelineStateForFrameAndDevice:(id<MTLDevice>)device {
- (_Nullable id<MTLRenderPipelineState>)renderPipelineStateForFrameAndDevice:(id<MTLDevice>)device
{
// Try to retrieve a render pipeline state that is compatible with the framebuffer config for this frame
// Thie hit rate for this cache should be very near 100%.
id<MTLRenderPipelineState> renderPipelineState = self.renderPipelineStateCache[self.framebufferDescriptor];

if (renderPipelineState == nil) {
if (renderPipelineState == nil)
{
// No luck; make a new render pipeline state
renderPipelineState = [self _renderPipelineStateForFramebufferDescriptor:self.framebufferDescriptor device:device];
// Cache render pipeline state for later reuse
Expand Down Expand Up @@ -314,15 +333,17 @@ - (void)enqueueReusableBuffer:(MetalBuffer *)buffer {
"}\n";

id<MTLLibrary> library = [device newLibraryWithSource:shaderSource options:nil error:&error];
if (library == nil) {
if (library == nil)
{
NSLog(@"Error: failed to create Metal library: %@", error);
return nil;
}

id<MTLFunction> vertexFunction = [library newFunctionWithName:@"vertex_main"];
id<MTLFunction> fragmentFunction = [library newFunctionWithName:@"fragment_main"];

if (vertexFunction == nil || fragmentFunction == nil) {
if (vertexFunction == nil || fragmentFunction == nil)
{
NSLog(@"Error: failed to find Metal shader functions in library: %@", error);
return nil;
}
Expand Down Expand Up @@ -358,14 +379,16 @@ - (void)enqueueReusableBuffer:(MetalBuffer *)buffer {
pipelineDescriptor.stencilAttachmentPixelFormat = self.framebufferDescriptor.stencilPixelFormat;

id<MTLRenderPipelineState> renderPipelineState = [device newRenderPipelineStateWithDescriptor:pipelineDescriptor error:&error];
if (error != nil) {
if (error != nil)
{
NSLog(@"Error: failed to create Metal pipeline state: %@", error);
}

return renderPipelineState;
}

- (void)emptyRenderPipelineStateCache {
- (void)emptyRenderPipelineStateCache
{
[self.renderPipelineStateCache removeAllObjects];
}

Expand All @@ -387,12 +410,15 @@ - (void)renderDrawData:(ImDrawData *)drawData
// Setup viewport, orthographic projection matrix
// Our visible imgui space lies from draw_data->DisplayPps (top left) to
// draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayMin is typically (0,0) for single viewport apps.
MTLViewport viewport = { .originX = 0.0,
MTLViewport viewport =
{
.originX = 0.0,
.originY = 0.0,
.width = double(fb_width),
.height = double(fb_height),
.znear = 0.0,
.zfar = 1.0 };
.zfar = 1.0
};
[commandEncoder setViewport:viewport];
float L = drawData->DisplayPos.x;
float R = drawData->DisplayPos.x + drawData->DisplaySize.x;
Expand All @@ -412,7 +438,8 @@ - (void)renderDrawData:(ImDrawData *)drawData

size_t vertexBufferLength = 0;
size_t indexBufferLength = 0;
for (int n = 0; n < drawData->CmdListsCount; n++) {
for (int n = 0; n < drawData->CmdListsCount; n++)
{
const ImDrawList* cmd_list = drawData->CmdLists[n];
vertexBufferLength += cmd_list->VtxBuffer.Size * sizeof(ImDrawVert);
indexBufferLength += cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx);
Expand Down Expand Up @@ -461,9 +488,8 @@ - (void)renderDrawData:(ImDrawData *)drawData


// Bind texture, Draw
if (pcmd->TextureId != NULL) {
if (pcmd->TextureId != NULL)
[commandEncoder setFragmentTexture:(__bridge id<MTLTexture>)(pcmd->TextureId) atIndex:0];
}
[commandEncoder drawIndexedPrimitives:MTLPrimitiveTypeTriangle
indexCount:pcmd->ElemCount
indexType:sizeof(ImDrawIdx) == 2 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32
Expand All @@ -479,7 +505,8 @@ - (void)renderDrawData:(ImDrawData *)drawData
}

__weak id weakSelf = self;
[commandBuffer addCompletedHandler:^(id<MTLCommandBuffer>) {
[commandBuffer addCompletedHandler:^(id<MTLCommandBuffer>)
{
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf enqueueReusableBuffer:vertexBuffer];
[weakSelf enqueueReusableBuffer:indexBuffer];
Expand Down

0 comments on commit 89e2ddf

Please sign in to comment.