Skip to content

Commit 7650c0a

Browse files
cocoaui: use setVertexBuffer and a ring buffer of buffers instead of setVertexBytes and re-creating buffers on every frame
1 parent 05ccc7b commit 7650c0a

File tree

7 files changed

+220
-46
lines changed

7 files changed

+220
-46
lines changed

osx/deadbeef.xcodeproj/project.pbxproj

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -742,6 +742,8 @@
742742
2D1E1DCD27B90051004DEF1D /* libavutil.56.dylib in Copy Plugins */ = {isa = PBXBuildFile; fileRef = 2DD376FD2739B854007AD315 /* libavutil.56.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
743743
2D1E215724F9826C00E2895D /* DdbPlayItemPasteboardSerializer.h in Headers */ = {isa = PBXBuildFile; fileRef = 2D1E215524F9826C00E2895D /* DdbPlayItemPasteboardSerializer.h */; };
744744
2D1E215824F9826C00E2895D /* DdbPlayItemPasteboardSerializer.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D1E215624F9826C00E2895D /* DdbPlayItemPasteboardSerializer.m */; };
745+
2D208EAF2CE8D5FA002E4892 /* MetalBufferLoop.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D208EAE2CE8D5FA002E4892 /* MetalBufferLoop.m */; };
746+
2D208EB02CE8D5FA002E4892 /* MetalBufferLoop.h in Headers */ = {isa = PBXBuildFile; fileRef = 2D208EAD2CE8D5FA002E4892 /* MetalBufferLoop.h */; };
745747
2D2092502A9A82E600CDBB2B /* libjansson.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 2DA84F7B24F58894003507A2 /* libjansson.dylib */; };
746748
2D20D12423F099F4008ACBE6 /* NSImage+Additions.h in Headers */ = {isa = PBXBuildFile; fileRef = 2D20D12223F099F4008ACBE6 /* NSImage+Additions.h */; };
747749
2D20D12523F099F4008ACBE6 /* NSImage+Additions.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D20D12323F099F4008ACBE6 /* NSImage+Additions.m */; };
@@ -5448,6 +5450,8 @@
54485450
2D1E1D8527AD9B25004DEF1D /* buffered_file_writer.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = buffered_file_writer.c; sourceTree = "<group>"; };
54495451
2D1E215524F9826C00E2895D /* DdbPlayItemPasteboardSerializer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DdbPlayItemPasteboardSerializer.h; sourceTree = "<group>"; };
54505452
2D1E215624F9826C00E2895D /* DdbPlayItemPasteboardSerializer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DdbPlayItemPasteboardSerializer.m; sourceTree = "<group>"; };
5453+
2D208EAD2CE8D5FA002E4892 /* MetalBufferLoop.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MetalBufferLoop.h; sourceTree = "<group>"; };
5454+
2D208EAE2CE8D5FA002E4892 /* MetalBufferLoop.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MetalBufferLoop.m; sourceTree = "<group>"; };
54515455
2D20924C2A9A077800CDBB2B /* scriptable_tfquery.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = scriptable_tfquery.h; sourceTree = "<group>"; };
54525456
2D20924D2A9A077800CDBB2B /* scriptable_tfquery.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = scriptable_tfquery.c; sourceTree = "<group>"; };
54535457
2D20D12223F099F4008ACBE6 /* NSImage+Additions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSImage+Additions.h"; sourceTree = "<group>"; };
@@ -8436,6 +8440,10 @@
84368440
0D568539271DC6550026F700 /* Shared */ = {
84378441
isa = PBXGroup;
84388442
children = (
8443+
2D208EAD2CE8D5FA002E4892 /* MetalBufferLoop.h */,
8444+
2D208EAE2CE8D5FA002E4892 /* MetalBufferLoop.m */,
8445+
2D5F86AD2C480776000457B3 /* MetalView.h */,
8446+
2D5F86AE2C480776000457B3 /* MetalView.m */,
84398447
2D98B66E272B272300E655AE /* ScopeShaderTypes.h */,
84408448
2D7C1FAB2946001B00163538 /* ShaderRenderer.h */,
84418449
2D7C1FAC2946001B00163538 /* ShaderRenderer.m */,
@@ -8446,8 +8454,6 @@
84468454
2D98B67E272B272300E655AE /* VisualizationShaders.metal */,
84478455
0D56853A271DC6670026F700 /* VisualizationViewController.h */,
84488456
0D56853B271DC6670026F700 /* VisualizationViewController.m */,
8449-
2D5F86AD2C480776000457B3 /* MetalView.h */,
8450-
2D5F86AE2C480776000457B3 /* MetalView.m */,
84518457
);
84528458
path = Shared;
84538459
sourceTree = "<group>";
@@ -14838,6 +14844,7 @@
1483814844
2DD8865A2B71056E000434FB /* KeyboardShortcutConverter.h in Headers */,
1483914845
2D72F6FF277BA9D800868136 /* DdbVolumeBar.h in Headers */,
1484014846
2DD6D0A519AE609F009308A5 /* DdbSeekBar.h in Headers */,
14847+
2D208EB02CE8D5FA002E4892 /* MetalBufferLoop.h in Headers */,
1484114848
2DB1641A24FAB13400034E11 /* MediaLibraryManager.h in Headers */,
1484214849
2D30D0B425E2A5DD0023A299 /* DesignModeState.h in Headers */,
1484314850
2DA2A0EE1BE7FE4700601670 /* u8_uc_map.h in Headers */,
@@ -18594,6 +18601,7 @@
1859418601
2D6D81F01CCF9E0B00028788 /* DdbTableViewRightClickActivate.m in Sources */,
1859518602
2D29ADFA2B72473B001648B0 /* keyboard_shortcut_serializer.c in Sources */,
1859618603
2DC6583F274C361C00583E14 /* HolderWidget.m in Sources */,
18604+
2D208EAF2CE8D5FA002E4892 /* MetalBufferLoop.m in Sources */,
1859718605
2D71C26B1DC88E5C00247CEF /* ScriptableTableDataSource.m in Sources */,
1859818606
2DD3776127414BF6007AD315 /* ScopePreferencesViewController.m in Sources */,
1859918607
2D01D7EE1AB222BF00BCD3C4 /* plugins.c in Sources */,

plugins/cocoaui/Visualization/Scope/ScopeVisualizationViewController.m

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <deadbeef/deadbeef.h>
1010
#import <QuartzCore/CAMetalLayer.h>
1111
#import "MetalView.h"
12+
#import "MetalBufferLoop.h"
1213
#import "ScopePreferencesViewController.h"
1314
#import "ScopePreferencesWindowController.h"
1415
#import "ScopeShaderTypes.h"
@@ -34,6 +35,8 @@ @interface ScopeVisualizationViewController() <MetalViewDelegate, CALayerDelegat
3435

3536
@property (atomic) BOOL isVisible;
3637

38+
@property (nonatomic) MetalBufferLoop *bufferLoop;
39+
3740
@end
3841

3942
@implementation ScopeVisualizationViewController {
@@ -87,6 +90,7 @@ - (void)setupMetalRenderer {
8790
fragmentShaderName:@"scopeFragmentShader"
8891
];
8992
_renderer.delegate = self;
93+
self.bufferLoop = [[MetalBufferLoop alloc] initWithMetalDevice:metalLayer.device bufferCount:3];
9094
}
9195

9296
- (void)viewDidLoad {
@@ -392,7 +396,7 @@ - (void)displayLayer:(CALayer *)layer {
392396

393397
#pragma mark - ShaderRendererDelegate
394398

395-
- (BOOL)applyFragParamsWithViewport:(vector_uint2)viewport device:(id<MTLDevice>)device encoder:(id<MTLRenderCommandEncoder>)encoder viewParams:(ShaderRendererParams)viewParams {
399+
- (BOOL)applyFragParamsWithViewport:(vector_uint2)viewport device:(id<MTLDevice>)device commandBuffer:(id<MTLCommandBuffer>)commandBuffer encoder:(id<MTLRenderCommandEncoder>)encoder viewParams:(ShaderRendererParams)viewParams {
396400
float scale = (float)(viewParams.backingScaleFactor / [self scaleFactorForBackingScaleFactor:viewParams.backingScaleFactor]);
397401

398402
struct ScopeFragParams params;
@@ -413,17 +417,20 @@ - (BOOL)applyFragParamsWithViewport:(vector_uint2)viewport device:(id<MTLDevice>
413417
[encoder setFragmentBytes:&params length:sizeof (params) atIndex:0];
414418

415419
if (_draw_data.points == NULL) {
416-
char bytes[12] = {0};
417-
[encoder setFragmentBytes:bytes length:12 atIndex:1];
420+
id<MTLBuffer> buffer = [self.bufferLoop nextBufferForSize:12];
421+
[encoder setFragmentBuffer:buffer offset:0 atIndex:1];
418422
}
419423
else {
420-
// Metal documentation states that MTLBuffer should be used for buffers larger than 4K in size.
421-
// Alternative is to use setFragmentBytes, which also works, but could have compatibility issues on older hardware.
422-
id<MTLBuffer> buffer = [device newBufferWithBytes:_draw_data.points length:_draw_data.point_count * sizeof (ddb_scope_point_t) * params.channels options:0];
423-
424+
NSUInteger size = _draw_data.point_count * sizeof (ddb_scope_point_t) * params.channels;
425+
id<MTLBuffer> buffer = [self.bufferLoop nextBufferForSize:size];
426+
memcpy (buffer.contents, _draw_data.points, size);
424427
[encoder setFragmentBuffer:buffer offset:0 atIndex:1];
425428
}
426429

430+
[commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> _Nonnull completedCommandBuffer) {
431+
[self.bufferLoop signalCompletion];
432+
}];
433+
427434
return YES;
428435
}
429436

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
DeaDBeeF -- the music player
3+
Copyright (C) 2009-2024 Oleksiy Yakovenko and other contributors
4+
5+
This software is provided 'as-is', without any express or implied
6+
warranty. In no event will the authors be held liable for any damages
7+
arising from the use of this software.
8+
9+
Permission is granted to anyone to use this software for any purpose,
10+
including commercial applications, and to alter it and redistribute it
11+
freely, subject to the following restrictions:
12+
13+
1. The origin of this software must not be misrepresented; you must not
14+
claim that you wrote the original software. If you use this software
15+
in a product, an acknowledgment in the product documentation would be
16+
appreciated but is not required.
17+
18+
2. Altered source versions must be plainly marked as such, and must not be
19+
misrepresented as being the original software.
20+
21+
3. This notice may not be removed or altered from any source distribution.
22+
*/
23+
24+
#import <Foundation/Foundation.h>
25+
#import <Metal/Metal.h>
26+
27+
NS_ASSUME_NONNULL_BEGIN
28+
29+
@interface MetalBufferLoop : NSObject
30+
31+
+ (instancetype)new NS_UNAVAILABLE;
32+
- (instancetype)init NS_UNAVAILABLE;
33+
- (instancetype)initWithMetalDevice:(id<MTLDevice>)device bufferCount:(NSUInteger)bufferCount NS_DESIGNATED_INITIALIZER;
34+
- (id<MTLBuffer>)nextBufferForSize:(NSUInteger)size;
35+
- (void)signalCompletion;
36+
37+
@end
38+
39+
NS_ASSUME_NONNULL_END
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
DeaDBeeF -- the music player
3+
Copyright (C) 2009-2024 Oleksiy Yakovenko and other contributors
4+
5+
This software is provided 'as-is', without any express or implied
6+
warranty. In no event will the authors be held liable for any damages
7+
arising from the use of this software.
8+
9+
Permission is granted to anyone to use this software for any purpose,
10+
including commercial applications, and to alter it and redistribute it
11+
freely, subject to the following restrictions:
12+
13+
1. The origin of this software must not be misrepresented; you must not
14+
claim that you wrote the original software. If you use this software
15+
in a product, an acknowledgment in the product documentation would be
16+
appreciated but is not required.
17+
18+
2. Altered source versions must be plainly marked as such, and must not be
19+
misrepresented as being the original software.
20+
21+
3. This notice may not be removed or altered from any source distribution.
22+
*/
23+
24+
#import "MetalBufferLoop.h"
25+
26+
@interface MetalBufferLoop()
27+
28+
// Parameters
29+
@property (nonatomic) id<MTLDevice> device;
30+
@property (nonatomic) NSUInteger bufferCount;
31+
@property (nonatomic) dispatch_semaphore_t semaphore;
32+
33+
// State
34+
@property (nonatomic) NSArray<id<MTLBuffer>> *buffers;
35+
@property (nonatomic) NSUInteger currentBufferSize;
36+
@property (nonatomic) NSUInteger currentBuffer;
37+
38+
@end
39+
40+
41+
@implementation MetalBufferLoop
42+
43+
- (instancetype)initWithMetalDevice:(id<MTLDevice>)device bufferCount:(NSUInteger)bufferCount {
44+
self = [super init];
45+
if (self == nil) {
46+
return nil;
47+
}
48+
49+
_device = device;
50+
_bufferCount = bufferCount;
51+
_semaphore = dispatch_semaphore_create(bufferCount);
52+
53+
return self;
54+
}
55+
56+
- (void)ensureBufferSize:(NSUInteger)size {
57+
if (size == self.currentBufferSize) {
58+
return;
59+
}
60+
61+
self.currentBufferSize = size;
62+
NSMutableArray<id<MTLBuffer>> *array = [[NSMutableArray alloc] initWithCapacity:self.bufferCount];
63+
for (NSUInteger i = 0; i < self.bufferCount; i++) {
64+
[array addObject:[self.device newBufferWithLength:size options:MTLResourceStorageModeShared]];
65+
}
66+
67+
self.buffers = array.copy;
68+
}
69+
70+
- (id<MTLBuffer>)nextBufferForSize:(NSUInteger)size {
71+
dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
72+
73+
[self ensureBufferSize:size];
74+
75+
id<MTLBuffer> buffer = self.buffers[self.currentBuffer];
76+
self.currentBuffer += 1;
77+
if (self.currentBuffer >= self.bufferCount) {
78+
self.currentBuffer = 0;
79+
}
80+
81+
return buffer;
82+
}
83+
84+
- (void)signalCompletion {
85+
dispatch_semaphore_signal(self.semaphore);
86+
}
87+
88+
89+
@end

plugins/cocoaui/Visualization/Shared/ShaderRenderer.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ typedef struct {
3232
} ShaderRendererParams;
3333

3434
@protocol ShaderRendererDelegate
35-
- (BOOL)applyFragParamsWithViewport:(vector_uint2)viewport device:(nonnull id <MTLDevice>)device encoder:(nonnull id <MTLRenderCommandEncoder>)encoder viewParams:(ShaderRendererParams)params;
35+
- (BOOL)applyFragParamsWithViewport:(vector_uint2)viewport device:(nonnull id <MTLDevice>)device commandBuffer:(nonnull id<MTLCommandBuffer>)commandBuffer encoder:(nonnull id <MTLRenderCommandEncoder>)encoder viewParams:(ShaderRendererParams)params;
3636
@end
3737

3838
@interface ShaderRenderer : NSObject

plugins/cocoaui/Visualization/Shared/ShaderRenderer.m

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -23,24 +23,32 @@
2323

2424
#import <Metal/Metal.h>
2525
#import <QuartzCore/CAMetalLayer.h>
26+
#import "MetalBufferLoop.h"
2627
#import "ShaderRenderer.h"
2728
#import "ShaderRendererTypes.h"
2829

29-
@implementation ShaderRenderer {
30-
// renderer global ivars
31-
id <MTLDevice> _device;
32-
id <MTLCommandQueue> _commandQueue;
33-
id <MTLRenderPipelineState> _pipelineState;
30+
@interface ShaderRenderer() {
31+
// renderer global ivars
32+
id <MTLDevice> _device;
33+
id <MTLCommandQueue> _commandQueue;
34+
id <MTLRenderPipelineState> _pipelineState;
3435

35-
// Render pass descriptor which creates a render command encoder to draw to the drawable
36-
// textures
37-
MTLRenderPassDescriptor *_drawableRenderDescriptor;
36+
// Render pass descriptor which creates a render command encoder to draw to the drawable
37+
// textures
38+
MTLRenderPassDescriptor *_drawableRenderDescriptor;
3839

39-
CGSize _viewportSize;
40+
CGSize _viewportSize;
4041

41-
NSUInteger _frameNum;
42+
NSUInteger _frameNum;
4243
}
4344

45+
@property (nonatomic) MetalBufferLoop *bufferLoop;
46+
@property (nonatomic) MetalBufferLoop *vpBufferLoop;
47+
48+
@end
49+
50+
@implementation ShaderRenderer
51+
4452
- (nonnull instancetype)initWithMetalDevice:(nonnull id<MTLDevice>)device
4553
drawablePixelFormat:(MTLPixelFormat)drawablePixelFormat
4654
fragmentShaderName:(NSString *)fragmentShaderName
@@ -52,6 +60,9 @@ - (nonnull instancetype)initWithMetalDevice:(nonnull id<MTLDevice>)device
5260

5361
_device = device;
5462

63+
_bufferLoop = [[MetalBufferLoop alloc] initWithMetalDevice:device bufferCount:3];
64+
_vpBufferLoop = [[MetalBufferLoop alloc] initWithMetalDevice:device bufferCount:3];
65+
5566
_commandQueue = [_device newCommandQueue];
5667

5768
_drawableRenderDescriptor = [MTLRenderPassDescriptor new];
@@ -157,15 +168,20 @@ - (void)renderToMetalLayer:(nonnull CAMetalLayer*)metalLayer viewParams:(ShaderR
157168
[renderEncoder setRenderPipelineState:_pipelineState];
158169

159170
vector_uint2 vp = { (uint)_viewportSize.width, (uint)_viewportSize.height };
160-
if ([self.delegate applyFragParamsWithViewport:vp device:_device encoder:renderEncoder viewParams:viewParams]) {
161-
// Pass in the parameter data.
162-
[renderEncoder setVertexBytes:quadVertices
163-
length:sizeof(quadVertices)
164-
atIndex:ShaderRendererVertexInputIndexVertices];
165-
166-
[renderEncoder setVertexBytes:&vp
167-
length:sizeof(vp)
168-
atIndex:ShaderRendererVertexInputIndexViewportSize];
171+
if ([self.delegate applyFragParamsWithViewport:vp device:_device commandBuffer:commandBuffer encoder:renderEncoder viewParams:viewParams]) {
172+
id<MTLBuffer> buffer = [self.bufferLoop nextBufferForSize:sizeof(quadVertices)];
173+
memcpy (buffer.contents, quadVertices, sizeof(quadVertices));
174+
[renderEncoder setVertexBuffer:buffer offset:0 atIndex:ShaderRendererVertexInputIndexVertices];
175+
176+
id<MTLBuffer> vpBuffer = [self.vpBufferLoop nextBufferForSize:sizeof(vp)];
177+
memcpy (vpBuffer.contents, &vp, sizeof(vp));
178+
179+
[renderEncoder setVertexBuffer:vpBuffer offset:0 atIndex:ShaderRendererVertexInputIndexViewportSize];
180+
181+
[commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> _Nonnull completedCommandBuffer) {
182+
[self.bufferLoop signalCompletion];
183+
[self.vpBufferLoop signalCompletion];
184+
}];
169185

170186
// Draw the triangle.
171187
[renderEncoder drawPrimitives:MTLPrimitiveTypeTriangleStrip

0 commit comments

Comments
 (0)