Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 29 additions & 4 deletions NativeApp/Apple/Data/OSX/Base.lproj/Main.storyboard
Original file line number Diff line number Diff line change
Expand Up @@ -174,11 +174,11 @@
<objects>
<viewController id="lYj-9Z-eqp" customClass="ModeSelectionViewController" sceneMemberID="viewController">
<view key="view" autoresizesSubviews="NO" id="SPq-vc-rSb">
<rect key="frame" x="0.0" y="0.0" width="350" height="354"/>
<rect key="frame" x="0.0" y="0.0" width="350" height="464"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="zEs-tf-G2z" userLabel="OpenGL">
<rect key="frame" x="59" y="143" width="233" height="82"/>
<rect key="frame" x="59" y="252" width="233" height="82"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="square" bezelStyle="shadowlessSquare" image="opengl-logo" imagePosition="only" alignment="center" borderStyle="border" imageScaling="proportionallyUpOrDown" inset="2" id="I5f-XW-9L1">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
Expand All @@ -189,7 +189,7 @@
</buttonCell>
</button>
<button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="mhf-V7-TKz" userLabel="Vulkan">
<rect key="frame" x="59" y="32" width="233" height="82"/>
<rect key="frame" x="59" y="143" width="233" height="82"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="square" bezelStyle="shadowlessSquare" image="vulkan-logo" imagePosition="only" alignment="center" borderStyle="border" imageScaling="proportionallyUpOrDown" inset="2" id="PWi-41-bCy">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
Expand All @@ -200,7 +200,7 @@
</connections>
</button>
<button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ysz-nM-ZkO" userLabel="Metal">
<rect key="frame" x="59" y="252" width="233" height="82"/>
<rect key="frame" x="59" y="362" width="233" height="82"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="square" bezelStyle="shadowlessSquare" image="metal-logo" imagePosition="only" alignment="center" borderStyle="border" imageScaling="proportionallyUpOrDown" inset="2" id="plw-eN-KbH">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
Expand All @@ -210,6 +210,17 @@
<action selector="goMetal:" target="lYj-9Z-eqp" id="dg7-nT-7dm"/>
</connections>
</button>
<button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="wGp-Ub-8Xq" userLabel="WebGPU">
<rect key="frame" x="59" y="32" width="233" height="82"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="square" bezelStyle="shadowlessSquare" image="webgpu-logo" imagePosition="only" alignment="center" borderStyle="border" imageScaling="proportionallyUpOrDown" inset="2" id="kLm-Wn-3Pq">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="goWebGPU:" target="lYj-9Z-eqp" id="Hj8-Kp-2Lm"/>
</connections>
</button>
</subviews>
</view>
</viewController>
Expand All @@ -230,10 +241,24 @@
</objects>
<point key="canvasLocation" x="74" y="762"/>
</scene>
<!--View Controller - WebGPU-->
<scene sceneID="wGp-Xz-9Qr">
<objects>
<viewController storyboardIdentifier="WebGPUViewControllerID" id="wGp-Vc-8Xq" customClass="ViewController" sceneMemberID="viewController">
<view key="view" id="wGp-Vw-7Pm" customClass="WebGPUView">
<rect key="frame" x="0.0" y="0.0" width="800" height="600"/>
<autoresizingMask key="autoresizingMask"/>
</view>
</viewController>
<customObject id="wGp-Fr-5Lk" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="74" y="870"/>
</scene>
</scenes>
<resources>
<image name="metal-logo" width="650" height="650"/>
<image name="opengl-logo" width="900" height="375"/>
<image name="vulkan-logo" width="975" height="375"/>
<image name="webgpu-logo" width="768" height="768"/>
</resources>
</document>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -24,6 +25,7 @@
#import "ModeSelectionViewController.h"
#import "GLView.h"
#import "MetalView.h"
#import "WebGPUView.h"
#import "ViewController.h"


Expand All @@ -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
Expand Down Expand Up @@ -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
2 changes: 2 additions & 0 deletions NativeApp/Apple/Source/Classes/OSX/ViewBase.mm
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ - (void) awakeFromNib
{
[super awakeFromNib];

appLock = [[NSRecursiveLock alloc] init];

std::vector<const char*> Args;
std::vector<std::string> ArgStr;
@autoreleasepool
Expand Down
30 changes: 30 additions & 0 deletions NativeApp/Apple/Source/Classes/OSX/WebGPUView.h
Original file line number Diff line number Diff line change
@@ -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 <AppKit/AppKit.h>

#include "ViewBase.h"

@interface WebGPUView : ViewBase

@end
153 changes: 153 additions & 0 deletions NativeApp/Apple/Source/Classes/OSX/WebGPUView.mm
Original file line number Diff line number Diff line change
@@ -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 <QuartzCore/CAMetalLayer.h>
#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
3 changes: 3 additions & 0 deletions NativeApp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down
3 changes: 2 additions & 1 deletion NativeApp/include/MacOS/MacOSAppBase.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ class MacOSAppBase : public AppBase
{
OpenGL,
MoltenVK,
Metal
Metal,
WebGPU
};
using AppBase::Update;
void Update();
Expand Down
Loading