Skip to content

Commit f2fe615

Browse files
MacOS App: Integrated WebGPUView
1 parent bcbd95c commit f2fe615

File tree

8 files changed

+268
-6
lines changed

8 files changed

+268
-6
lines changed

NativeApp/Apple/Data/OSX/Base.lproj/Main.storyboard

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -174,11 +174,11 @@
174174
<objects>
175175
<viewController id="lYj-9Z-eqp" customClass="ModeSelectionViewController" sceneMemberID="viewController">
176176
<view key="view" autoresizesSubviews="NO" id="SPq-vc-rSb">
177-
<rect key="frame" x="0.0" y="0.0" width="350" height="354"/>
177+
<rect key="frame" x="0.0" y="0.0" width="350" height="464"/>
178178
<autoresizingMask key="autoresizingMask"/>
179179
<subviews>
180180
<button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="zEs-tf-G2z" userLabel="OpenGL">
181-
<rect key="frame" x="59" y="143" width="233" height="82"/>
181+
<rect key="frame" x="59" y="252" width="233" height="82"/>
182182
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
183183
<buttonCell key="cell" type="square" bezelStyle="shadowlessSquare" image="opengl-logo" imagePosition="only" alignment="center" borderStyle="border" imageScaling="proportionallyUpOrDown" inset="2" id="I5f-XW-9L1">
184184
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
@@ -189,7 +189,7 @@
189189
</buttonCell>
190190
</button>
191191
<button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="mhf-V7-TKz" userLabel="Vulkan">
192-
<rect key="frame" x="59" y="32" width="233" height="82"/>
192+
<rect key="frame" x="59" y="143" width="233" height="82"/>
193193
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
194194
<buttonCell key="cell" type="square" bezelStyle="shadowlessSquare" image="vulkan-logo" imagePosition="only" alignment="center" borderStyle="border" imageScaling="proportionallyUpOrDown" inset="2" id="PWi-41-bCy">
195195
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
@@ -200,7 +200,7 @@
200200
</connections>
201201
</button>
202202
<button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ysz-nM-ZkO" userLabel="Metal">
203-
<rect key="frame" x="59" y="252" width="233" height="82"/>
203+
<rect key="frame" x="59" y="362" width="233" height="82"/>
204204
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
205205
<buttonCell key="cell" type="square" bezelStyle="shadowlessSquare" image="metal-logo" imagePosition="only" alignment="center" borderStyle="border" imageScaling="proportionallyUpOrDown" inset="2" id="plw-eN-KbH">
206206
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
@@ -210,6 +210,17 @@
210210
<action selector="goMetal:" target="lYj-9Z-eqp" id="dg7-nT-7dm"/>
211211
</connections>
212212
</button>
213+
<button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="wGp-Ub-8Xq" userLabel="WebGPU">
214+
<rect key="frame" x="59" y="32" width="233" height="82"/>
215+
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
216+
<buttonCell key="cell" type="square" bezelStyle="shadowlessSquare" image="webgpu-logo" imagePosition="only" alignment="center" borderStyle="border" imageScaling="proportionallyUpOrDown" inset="2" id="kLm-Wn-3Pq">
217+
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
218+
<font key="font" metaFont="system"/>
219+
</buttonCell>
220+
<connections>
221+
<action selector="goWebGPU:" target="lYj-9Z-eqp" id="Hj8-Kp-2Lm"/>
222+
</connections>
223+
</button>
213224
</subviews>
214225
</view>
215226
</viewController>
@@ -230,10 +241,24 @@
230241
</objects>
231242
<point key="canvasLocation" x="74" y="762"/>
232243
</scene>
244+
<!--View Controller - WebGPU-->
245+
<scene sceneID="wGp-Xz-9Qr">
246+
<objects>
247+
<viewController storyboardIdentifier="WebGPUViewControllerID" id="wGp-Vc-8Xq" customClass="ViewController" sceneMemberID="viewController">
248+
<view key="view" id="wGp-Vw-7Pm" customClass="WebGPUView">
249+
<rect key="frame" x="0.0" y="0.0" width="800" height="600"/>
250+
<autoresizingMask key="autoresizingMask"/>
251+
</view>
252+
</viewController>
253+
<customObject id="wGp-Fr-5Lk" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
254+
</objects>
255+
<point key="canvasLocation" x="74" y="870"/>
256+
</scene>
233257
</scenes>
234258
<resources>
235259
<image name="metal-logo" width="650" height="650"/>
236260
<image name="opengl-logo" width="900" height="375"/>
237261
<image name="vulkan-logo" width="975" height="375"/>
262+
<image name="webgpu-logo" width="768" height="768"/>
238263
</resources>
239264
</document>
34.2 KB
Loading

NativeApp/Apple/Source/Classes/OSX/ModeSelectionViewController.mm

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
/* Copyright 2015-2019 Egor Yusov
1+
/* Copyright 2026 Diligent Graphics LLC
2+
* Copyright 2015-2019 Egor Yusov
23
*
34
* Licensed under the Apache License, Version 2.0 (the "License");
45
* you may not use this file except in compliance with the License.
@@ -24,6 +25,7 @@
2425
#import "ModeSelectionViewController.h"
2526
#import "GLView.h"
2627
#import "MetalView.h"
28+
#import "WebGPUView.h"
2729
#import "ViewController.h"
2830

2931

@@ -50,6 +52,36 @@ - (void)viewDidLoad
5052
#if !METAL_SUPPORTED
5153
((NSButton*)self.view.subviews[2]).enabled = false;
5254
#endif
55+
56+
#if !WEBGPU_SUPPORTED
57+
((NSButton*)self.view.subviews[3]).hidden = true;
58+
#endif
59+
}
60+
61+
- (void)viewDidAppear
62+
{
63+
[super viewDidAppear];
64+
65+
#if !WEBGPU_SUPPORTED
66+
NSWindow* window = self.view.window;
67+
if (window)
68+
{
69+
CGFloat reduction = self.view.subviews[1].frame.origin.y -
70+
self.view.subviews[3].frame.origin.y;
71+
72+
// Switch buttons from Auto Layout to autoresizing masks
73+
// so flexibleMinY keeps them pinned to the top edge
74+
for (NSView* subview in self.view.subviews)
75+
subview.translatesAutoresizingMaskIntoConstraints = YES;
76+
self.view.autoresizesSubviews = YES;
77+
78+
// Shrink window from the bottom (keep top edge fixed)
79+
NSRect frame = window.frame;
80+
frame.origin.y += reduction;
81+
frame.size.height -= reduction;
82+
[window setFrame:frame display:YES];
83+
}
84+
#endif
5385
}
5486

5587
- (void) terminateApp:(NSString*) error
@@ -111,4 +143,20 @@ - (IBAction)goMetal:(id)sender
111143
[self setWindowTitle:name];
112144
}
113145

146+
- (IBAction)goWebGPU:(id)sender
147+
{
148+
ViewController* webgpuViewController = [self.storyboard instantiateControllerWithIdentifier:@"WebGPUViewControllerID"];
149+
WebGPUView* webgpuView = (WebGPUView*)[webgpuViewController view];
150+
self.view.window.contentViewController = webgpuViewController;
151+
152+
NSString* error = [webgpuView getError];
153+
if(error != nil)
154+
{
155+
[self terminateApp:error];
156+
}
157+
158+
NSString* name = [webgpuView getAppName];
159+
[self setWindowTitle:name];
160+
}
161+
114162
@end

NativeApp/Apple/Source/Classes/OSX/ViewBase.mm

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ - (void) awakeFromNib
4343
{
4444
[super awakeFromNib];
4545

46+
appLock = [[NSRecursiveLock alloc] init];
47+
4648
std::vector<const char*> Args;
4749
std::vector<std::string> ArgStr;
4850
@autoreleasepool
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/* Copyright 2026 Diligent Graphics LLC
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
10+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
11+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF ANY PROPRIETARY RIGHTS.
12+
*
13+
* In no event and under no legal theory, whether in tort (including negligence),
14+
* contract, or otherwise, unless required by applicable law (such as deliberate
15+
* and grossly negligent acts) or agreed to in writing, shall any Contributor be
16+
* liable for any damages, including any direct, indirect, special, incidental,
17+
* or consequential damages of any character arising as a result of this License or
18+
* out of the use or inability to use the software (including but not limited to damages
19+
* for loss of goodwill, work stoppage, computer failure or malfunction, or any and
20+
* all other commercial damages or losses), even if such Contributor has been advised
21+
* of the possibility of such damages.
22+
*/
23+
24+
#import <AppKit/AppKit.h>
25+
26+
#include "ViewBase.h"
27+
28+
@interface WebGPUView : ViewBase
29+
30+
@end
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
/* Copyright 2026 Diligent Graphics LLC
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
10+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
11+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF ANY PROPRIETARY RIGHTS.
12+
*
13+
* In no event and under no legal theory, whether in tort (including negligence),
14+
* contract, or otherwise, unless required by applicable law (such as deliberate
15+
* and grossly negligent acts) or agreed to in writing, shall any Contributor be
16+
* liable for any damages, including any direct, indirect, special, incidental,
17+
* or consequential damages of any character arising as a result of this License or
18+
* out of the use or inability to use the software (including but not limited to damages
19+
* for loss of goodwill, work stoppage, computer failure or malfunction, or any and
20+
* all other commercial damages or losses), even if such Contributor has been advised
21+
* of the possibility of such damages.
22+
*/
23+
24+
#import <QuartzCore/CAMetalLayer.h>
25+
#import "WebGPUView.h"
26+
27+
@implementation WebGPUView
28+
{
29+
}
30+
31+
- (id)initWithFrame:(CGRect)frame
32+
{
33+
self = [super initWithFrame:frame];
34+
if (self)
35+
{
36+
self.renderMode = Diligent::MacOSAppBase::RenderMode::WebGPU;
37+
}
38+
return self;
39+
}
40+
41+
- (id)initWithCoder:(NSCoder*)coder
42+
{
43+
self = [super initWithCoder:coder];
44+
if (self)
45+
{
46+
self.renderMode = Diligent::MacOSAppBase::RenderMode::WebGPU;
47+
}
48+
return self;
49+
}
50+
51+
52+
- (void) awakeFromNib
53+
{
54+
[super awakeFromNib];
55+
56+
// Back the view with a layer created by the makeBackingLayer method.
57+
self.wantsLayer = YES;
58+
59+
[self initApp:self];
60+
61+
#pragma clang diagnostic push
62+
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
63+
64+
CVDisplayLinkRef displayLink;
65+
CVDisplayLinkCreateWithActiveCGDisplays(&displayLink);
66+
[self setDisplayLink:displayLink];
67+
CVDisplayLinkSetOutputCallback(displayLink, &DisplayLinkCallback, (__bridge void*)self);
68+
CVDisplayLinkStart(displayLink);
69+
70+
#pragma clang diagnostic pop
71+
72+
[self setPostsBoundsChangedNotifications:YES];
73+
[self setPostsFrameChangedNotifications:YES];
74+
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(boundsDidChange:) name:NSViewBoundsDidChangeNotification object:self];
75+
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(boundsDidChange:) name:NSViewFrameDidChangeNotification object:self];
76+
}
77+
78+
// Indicates that the view wants to draw using the backing
79+
// layer instead of using drawRect:.
80+
-(BOOL) wantsUpdateLayer
81+
{
82+
return YES;
83+
}
84+
85+
// Returns a Metal-compatible layer.
86+
+(Class) layerClass
87+
{
88+
return [CAMetalLayer class];
89+
}
90+
91+
// If the wantsLayer property is set to YES, this method will
92+
// be invoked to return a layer instance.
93+
-(CALayer*) makeBackingLayer
94+
{
95+
CALayer* layer = [self.class.layerClass layer];
96+
CGSize viewScale = [self convertSizeToBacking: CGSizeMake(1.0, 1.0)];
97+
layer.contentsScale = MIN(viewScale.width, viewScale.height);
98+
return layer;
99+
}
100+
101+
-(void)render
102+
{
103+
auto* theApp = [self lockApp];
104+
if (theApp)
105+
{
106+
theApp->Update();
107+
theApp->Render();
108+
theApp->Present();
109+
}
110+
[self unlockApp];
111+
}
112+
113+
114+
- (CVReturn) getFrameForTime:(const CVTimeStamp*)outputTime
115+
{
116+
// There is no autorelease pool when this method is called
117+
// because it will be called from a background thread.
118+
// It's important to create one or app can leak objects.
119+
@autoreleasepool {
120+
[self render];
121+
}
122+
return kCVReturnSuccess;
123+
}
124+
125+
// Rendering loop callback function for use with a CVDisplayLink.
126+
static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink,
127+
const CVTimeStamp* now,
128+
const CVTimeStamp* outputTime,
129+
CVOptionFlags flagsIn,
130+
CVOptionFlags* flagsOut,
131+
void* target)
132+
{
133+
WebGPUView* view = (__bridge WebGPUView*)target;
134+
CVReturn result = [view getFrameForTime:outputTime];
135+
return result;
136+
}
137+
138+
-(void)boundsDidChange:(NSNotification *)notification
139+
{
140+
NSRect viewRectPoints = [self bounds];
141+
NSRect viewRectPixels = [self convertRectToBacking:viewRectPoints];
142+
auto* theApp = [self lockApp];
143+
if (theApp)
144+
{
145+
theApp->WindowResize(viewRectPixels.size.width, viewRectPixels.size.height);
146+
theApp->Update();
147+
theApp->Render();
148+
theApp->Present();
149+
}
150+
[self unlockApp];
151+
}
152+
153+
@end

NativeApp/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ elseif(PLATFORM_MACOS)
161161
${NATIVE_APP_SOURCE_DIR}/Apple/Source/Classes/OSX/GLView.mm
162162
${NATIVE_APP_SOURCE_DIR}/Apple/Source/Classes/OSX/MetalView.mm
163163
${NATIVE_APP_SOURCE_DIR}/Apple/Source/Classes/OSX/MVKView.mm
164+
${NATIVE_APP_SOURCE_DIR}/Apple/Source/Classes/OSX/WebGPUView.mm
164165
${NATIVE_APP_SOURCE_DIR}/Apple/Source/Classes/OSX/ViewBase.mm
165166
${NATIVE_APP_SOURCE_DIR}/Apple/Source/Classes/OSX/ViewController.mm
166167
${NATIVE_APP_SOURCE_DIR}/Apple/Source/Classes/OSX/ModeSelectionViewController.mm
@@ -173,6 +174,7 @@ elseif(PLATFORM_MACOS)
173174
${NATIVE_APP_SOURCE_DIR}/Apple/Source/Classes/OSX/GLView.h
174175
${NATIVE_APP_SOURCE_DIR}/Apple/Source/Classes/OSX/MetalView.h
175176
${NATIVE_APP_SOURCE_DIR}/Apple/Source/Classes/OSX/MVKView.h
177+
${NATIVE_APP_SOURCE_DIR}/Apple/Source/Classes/OSX/WebGPUView.h
176178
${NATIVE_APP_SOURCE_DIR}/Apple/Source/Classes/OSX/ViewBase.h
177179
${NATIVE_APP_SOURCE_DIR}/Apple/Source/Classes/OSX/ViewController.h
178180
${NATIVE_APP_SOURCE_DIR}/Apple/Source/Classes/OSX/ModeSelectionViewController.h
@@ -184,6 +186,7 @@ elseif(PLATFORM_MACOS)
184186
${NATIVE_APP_SOURCE_DIR}/Apple/Data/OSX/Images.xcassets/opengl-logo.png
185187
${NATIVE_APP_SOURCE_DIR}/Apple/Data/OSX/Images.xcassets/vulkan-logo.png
186188
${NATIVE_APP_SOURCE_DIR}/Apple/Data/OSX/Images.xcassets/metal-logo.png
189+
${NATIVE_APP_SOURCE_DIR}/Apple/Data/OSX/Images.xcassets/webgpu-logo.png
187190
)
188191

189192
set(APPLE_INFO_PLIST

NativeApp/include/MacOS/MacOSAppBase.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ class MacOSAppBase : public AppBase
3939
{
4040
OpenGL,
4141
MoltenVK,
42-
Metal
42+
Metal,
43+
WebGPU
4344
};
4445
using AppBase::Update;
4546
void Update();

0 commit comments

Comments
 (0)