diff --git a/NativeApp/Apple/Data/OSX/Base.lproj/Main.storyboard b/NativeApp/Apple/Data/OSX/Base.lproj/Main.storyboard index bc15944c..26161580 100644 --- a/NativeApp/Apple/Data/OSX/Base.lproj/Main.storyboard +++ b/NativeApp/Apple/Data/OSX/Base.lproj/Main.storyboard @@ -174,11 +174,11 @@ - + + @@ -230,10 +241,24 @@ + + + + + + + + + + + + + + diff --git a/NativeApp/Apple/Data/OSX/Images.xcassets/webgpu-logo.png b/NativeApp/Apple/Data/OSX/Images.xcassets/webgpu-logo.png new file mode 100644 index 00000000..7e826769 Binary files /dev/null and b/NativeApp/Apple/Data/OSX/Images.xcassets/webgpu-logo.png differ diff --git a/NativeApp/Apple/Source/Classes/OSX/ModeSelectionViewController.mm b/NativeApp/Apple/Source/Classes/OSX/ModeSelectionViewController.mm index b43d4386..7121647d 100644 --- a/NativeApp/Apple/Source/Classes/OSX/ModeSelectionViewController.mm +++ b/NativeApp/Apple/Source/Classes/OSX/ModeSelectionViewController.mm @@ -1,4 +1,5 @@ -/* Copyright 2015-2019 Egor Yusov +/* Copyright 2026 Diligent Graphics LLC + * Copyright 2015-2019 Egor Yusov * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,6 +25,7 @@ #import "ModeSelectionViewController.h" #import "GLView.h" #import "MetalView.h" +#import "WebGPUView.h" #import "ViewController.h" @@ -50,6 +52,36 @@ - (void)viewDidLoad #if !METAL_SUPPORTED ((NSButton*)self.view.subviews[2]).enabled = false; #endif + +#if !WEBGPU_SUPPORTED + ((NSButton*)self.view.subviews[3]).hidden = true; +#endif +} + +- (void)viewDidAppear +{ + [super viewDidAppear]; + +#if !WEBGPU_SUPPORTED + NSWindow* window = self.view.window; + if (window) + { + CGFloat reduction = self.view.subviews[1].frame.origin.y - + self.view.subviews[3].frame.origin.y; + + // Switch buttons from Auto Layout to autoresizing masks + // so flexibleMinY keeps them pinned to the top edge + for (NSView* subview in self.view.subviews) + subview.translatesAutoresizingMaskIntoConstraints = YES; + self.view.autoresizesSubviews = YES; + + // Shrink window from the bottom (keep top edge fixed) + NSRect frame = window.frame; + frame.origin.y += reduction; + frame.size.height -= reduction; + [window setFrame:frame display:YES]; + } +#endif } - (void) terminateApp:(NSString*) error @@ -111,4 +143,20 @@ - (IBAction)goMetal:(id)sender [self setWindowTitle:name]; } +- (IBAction)goWebGPU:(id)sender +{ + ViewController* webgpuViewController = [self.storyboard instantiateControllerWithIdentifier:@"WebGPUViewControllerID"]; + WebGPUView* webgpuView = (WebGPUView*)[webgpuViewController view]; + self.view.window.contentViewController = webgpuViewController; + + NSString* error = [webgpuView getError]; + if(error != nil) + { + [self terminateApp:error]; + } + + NSString* name = [webgpuView getAppName]; + [self setWindowTitle:name]; +} + @end diff --git a/NativeApp/Apple/Source/Classes/OSX/ViewBase.mm b/NativeApp/Apple/Source/Classes/OSX/ViewBase.mm index 65677a36..3641f8be 100644 --- a/NativeApp/Apple/Source/Classes/OSX/ViewBase.mm +++ b/NativeApp/Apple/Source/Classes/OSX/ViewBase.mm @@ -43,6 +43,8 @@ - (void) awakeFromNib { [super awakeFromNib]; + appLock = [[NSRecursiveLock alloc] init]; + std::vector Args; std::vector ArgStr; @autoreleasepool diff --git a/NativeApp/Apple/Source/Classes/OSX/WebGPUView.h b/NativeApp/Apple/Source/Classes/OSX/WebGPUView.h new file mode 100644 index 00000000..db2db40d --- /dev/null +++ b/NativeApp/Apple/Source/Classes/OSX/WebGPUView.h @@ -0,0 +1,30 @@ +/* Copyright 2026 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF ANY PROPRIETARY RIGHTS. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#import + +#include "ViewBase.h" + +@interface WebGPUView : ViewBase + +@end diff --git a/NativeApp/Apple/Source/Classes/OSX/WebGPUView.mm b/NativeApp/Apple/Source/Classes/OSX/WebGPUView.mm new file mode 100644 index 00000000..bfe9af5c --- /dev/null +++ b/NativeApp/Apple/Source/Classes/OSX/WebGPUView.mm @@ -0,0 +1,153 @@ +/* Copyright 2026 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF ANY PROPRIETARY RIGHTS. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#import +#import "WebGPUView.h" + +@implementation WebGPUView +{ +} + +- (id)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + if (self) + { + self.renderMode = Diligent::MacOSAppBase::RenderMode::WebGPU; + } + return self; +} + +- (id)initWithCoder:(NSCoder*)coder +{ + self = [super initWithCoder:coder]; + if (self) + { + self.renderMode = Diligent::MacOSAppBase::RenderMode::WebGPU; + } + return self; +} + + +- (void) awakeFromNib +{ + [super awakeFromNib]; + + // Back the view with a layer created by the makeBackingLayer method. + self.wantsLayer = YES; + + [self initApp:self]; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + + CVDisplayLinkRef displayLink; + CVDisplayLinkCreateWithActiveCGDisplays(&displayLink); + [self setDisplayLink:displayLink]; + CVDisplayLinkSetOutputCallback(displayLink, &DisplayLinkCallback, (__bridge void*)self); + CVDisplayLinkStart(displayLink); + +#pragma clang diagnostic pop + + [self setPostsBoundsChangedNotifications:YES]; + [self setPostsFrameChangedNotifications:YES]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(boundsDidChange:) name:NSViewBoundsDidChangeNotification object:self]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(boundsDidChange:) name:NSViewFrameDidChangeNotification object:self]; +} + +// Indicates that the view wants to draw using the backing +// layer instead of using drawRect:. +-(BOOL) wantsUpdateLayer +{ + return YES; +} + +// Returns a Metal-compatible layer. ++(Class) layerClass +{ + return [CAMetalLayer class]; +} + +// If the wantsLayer property is set to YES, this method will +// be invoked to return a layer instance. +-(CALayer*) makeBackingLayer +{ + CALayer* layer = [self.class.layerClass layer]; + CGSize viewScale = [self convertSizeToBacking: CGSizeMake(1.0, 1.0)]; + layer.contentsScale = MIN(viewScale.width, viewScale.height); + return layer; +} + +-(void)render +{ + auto* theApp = [self lockApp]; + if (theApp) + { + theApp->Update(); + theApp->Render(); + theApp->Present(); + } + [self unlockApp]; +} + + +- (CVReturn) getFrameForTime:(const CVTimeStamp*)outputTime +{ + // There is no autorelease pool when this method is called + // because it will be called from a background thread. + // It's important to create one or app can leak objects. + @autoreleasepool { + [self render]; + } + return kCVReturnSuccess; +} + +// Rendering loop callback function for use with a CVDisplayLink. +static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, + const CVTimeStamp* now, + const CVTimeStamp* outputTime, + CVOptionFlags flagsIn, + CVOptionFlags* flagsOut, + void* target) +{ + WebGPUView* view = (__bridge WebGPUView*)target; + CVReturn result = [view getFrameForTime:outputTime]; + return result; +} + +-(void)boundsDidChange:(NSNotification *)notification +{ + NSRect viewRectPoints = [self bounds]; + NSRect viewRectPixels = [self convertRectToBacking:viewRectPoints]; + auto* theApp = [self lockApp]; + if (theApp) + { + theApp->WindowResize(viewRectPixels.size.width, viewRectPixels.size.height); + theApp->Update(); + theApp->Render(); + theApp->Present(); + } + [self unlockApp]; +} + +@end diff --git a/NativeApp/CMakeLists.txt b/NativeApp/CMakeLists.txt index 778a57cf..e8963176 100644 --- a/NativeApp/CMakeLists.txt +++ b/NativeApp/CMakeLists.txt @@ -161,6 +161,7 @@ elseif(PLATFORM_MACOS) ${NATIVE_APP_SOURCE_DIR}/Apple/Source/Classes/OSX/GLView.mm ${NATIVE_APP_SOURCE_DIR}/Apple/Source/Classes/OSX/MetalView.mm ${NATIVE_APP_SOURCE_DIR}/Apple/Source/Classes/OSX/MVKView.mm + ${NATIVE_APP_SOURCE_DIR}/Apple/Source/Classes/OSX/WebGPUView.mm ${NATIVE_APP_SOURCE_DIR}/Apple/Source/Classes/OSX/ViewBase.mm ${NATIVE_APP_SOURCE_DIR}/Apple/Source/Classes/OSX/ViewController.mm ${NATIVE_APP_SOURCE_DIR}/Apple/Source/Classes/OSX/ModeSelectionViewController.mm @@ -173,6 +174,7 @@ elseif(PLATFORM_MACOS) ${NATIVE_APP_SOURCE_DIR}/Apple/Source/Classes/OSX/GLView.h ${NATIVE_APP_SOURCE_DIR}/Apple/Source/Classes/OSX/MetalView.h ${NATIVE_APP_SOURCE_DIR}/Apple/Source/Classes/OSX/MVKView.h + ${NATIVE_APP_SOURCE_DIR}/Apple/Source/Classes/OSX/WebGPUView.h ${NATIVE_APP_SOURCE_DIR}/Apple/Source/Classes/OSX/ViewBase.h ${NATIVE_APP_SOURCE_DIR}/Apple/Source/Classes/OSX/ViewController.h ${NATIVE_APP_SOURCE_DIR}/Apple/Source/Classes/OSX/ModeSelectionViewController.h @@ -184,6 +186,7 @@ elseif(PLATFORM_MACOS) ${NATIVE_APP_SOURCE_DIR}/Apple/Data/OSX/Images.xcassets/opengl-logo.png ${NATIVE_APP_SOURCE_DIR}/Apple/Data/OSX/Images.xcassets/vulkan-logo.png ${NATIVE_APP_SOURCE_DIR}/Apple/Data/OSX/Images.xcassets/metal-logo.png + ${NATIVE_APP_SOURCE_DIR}/Apple/Data/OSX/Images.xcassets/webgpu-logo.png ) set(APPLE_INFO_PLIST diff --git a/NativeApp/include/MacOS/MacOSAppBase.hpp b/NativeApp/include/MacOS/MacOSAppBase.hpp index 85400534..291c654b 100644 --- a/NativeApp/include/MacOS/MacOSAppBase.hpp +++ b/NativeApp/include/MacOS/MacOSAppBase.hpp @@ -39,7 +39,8 @@ class MacOSAppBase : public AppBase { OpenGL, MoltenVK, - Metal + Metal, + WebGPU }; using AppBase::Update; void Update();