LMMS
Loading...
Searching...
No Matches
juce_mac_CGMetalLayerRenderer.h
Go to the documentation of this file.
1/*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 By using JUCE, you agree to the terms of both the JUCE 7 End-User License
11 Agreement and JUCE Privacy Policy.
12
13 End User License Agreement: www.juce.com/juce-7-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
15
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
18
19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21 DISCLAIMED.
22
23 ==============================================================================
24*/
25
26// The CoreGraphicsMetalLayerRenderer requires macOS 10.14 and iOS 12.
27JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wunguarded-availability", "-Wunguarded-availability-new")
28
29namespace juce
30{
31
32//==============================================================================
33template <typename ViewType>
35{
36public:
37 //==============================================================================
39 {
40 device.reset (MTLCreateSystemDefaultDevice());
41 commandQueue.reset ([device.get() newCommandQueue]);
42
43 attach (view, comp);
44 }
45
47 {
48 if (memoryBlitCommandBuffer != nullptr)
49 {
51 [memoryBlitCommandBuffer.get() waitUntilCompleted];
52 }
53 }
54
55 void attach (ViewType* view, const Component& comp)
56 {
57 #if JUCE_MAC
58 view.wantsLayer = YES;
59 view.layerContentsPlacement = NSViewLayerContentsPlacementTopLeft;
60 view.layer = [CAMetalLayer layer];
61 #endif
62
63 auto layer = (CAMetalLayer*) view.layer;
64
65 layer.device = device.get();
66 layer.framebufferOnly = NO;
67 layer.pixelFormat = MTLPixelFormatBGRA8Unorm_sRGB;
68 layer.opaque = comp.isOpaque();
69 layer.allowsNextDrawableTimeout = NO;
70
71 attachedView = view;
73 }
74
75 void detach()
76 {
77 #if JUCE_MAC
78 attachedView.wantsLayer = NO;
79 attachedView.layer = nil;
80 #endif
81
82 attachedView = nullptr;
83 }
84
85 bool isAttachedToView (ViewType* view) const
86 {
87 return view == attachedView && attachedView != nullptr;
88 }
89
90 template <typename Callback>
91 bool drawRectangleList (ViewType* view,
92 float scaleFactor,
93 CGRect viewFrame,
94 const Component& comp,
95 Callback&& drawRectWithContext,
96 const RectangleList<float>& dirtyRegions)
97 {
98 auto layer = (CAMetalLayer*) view.layer;
99
100 if (memoryBlitCommandBuffer != nullptr)
101 {
102 switch ([memoryBlitCommandBuffer.get() status])
103 {
104 case MTLCommandBufferStatusNotEnqueued:
105 case MTLCommandBufferStatusEnqueued:
106 case MTLCommandBufferStatusCommitted:
107 case MTLCommandBufferStatusScheduled:
108 // If we haven't finished blitting the CPU texture to the GPU then
109 // report that we have been unable to draw anything.
110 return false;
111 case MTLCommandBufferStatusCompleted:
112 case MTLCommandBufferStatusError:
113 break;
114 }
115 }
116
117 layer.contentsScale = scaleFactor;
118 const auto drawableSizeTansform = CGAffineTransformMakeScale (layer.contentsScale,
119 layer.contentsScale);
120 const auto transformedFrameSize = CGSizeApplyAffineTransform (viewFrame.size, drawableSizeTansform);
121
122 const auto componentHeight = comp.getHeight();
123
124 if (resources == nullptr || ! CGSizeEqualToSize (layer.drawableSize, transformedFrameSize))
125 {
126 layer.drawableSize = transformedFrameSize;
127 resources = std::make_unique<Resources> (device.get(), layer, componentHeight);
128 }
129
130 auto gpuTexture = resources->getGpuTexture();
131
132 if (gpuTexture == nullptr)
133 {
135 return false;
136 }
137
138 auto cgContext = resources->getCGContext();
139
140 for (auto rect : dirtyRegions)
141 {
142 const auto cgRect = convertToCGRect (rect);
143
144 CGContextSaveGState (cgContext);
145
146 CGContextClipToRect (cgContext, cgRect);
147 drawRectWithContext (cgContext, cgRect);
148
149 CGContextRestoreGState (cgContext);
150 }
151
152 resources->signalBufferModifiedByCpu();
153
154 auto sharedTexture = resources->getSharedTexture();
155
156 auto encodeBlit = [] (id<MTLCommandBuffer> commandBuffer,
157 id<MTLTexture> source,
158 id<MTLTexture> destination)
159 {
160 auto blitCommandEncoder = [commandBuffer blitCommandEncoder];
161 [blitCommandEncoder copyFromTexture: source
162 sourceSlice: 0
163 sourceLevel: 0
164 sourceOrigin: MTLOrigin{}
165 sourceSize: MTLSize { source.width, source.height, 1 }
166 toTexture: destination
167 destinationSlice: 0
168 destinationLevel: 0
169 destinationOrigin: MTLOrigin{}];
170 [blitCommandEncoder endEncoding];
171 };
172
174 {
175 @autoreleasepool
176 {
177 id<MTLCommandBuffer> commandBuffer = [commandQueue.get() commandBuffer];
178
179 id<CAMetalDrawable> drawable = [layer nextDrawable];
180 encodeBlit (commandBuffer, sharedTexture, drawable.texture);
181
182 [commandBuffer presentDrawable: drawable];
183 [commandBuffer commit];
184 }
185
186 doSynchronousRender = false;
187 }
188 else
189 {
190 // Command buffers are usually considered temporary, and are automatically released by
191 // the operating system when the rendering pipeline is finsihed. However, we want to keep
192 // this one alive so that we can wait for pipeline completion in the destructor.
193 memoryBlitCommandBuffer.reset ([[commandQueue.get() commandBuffer] retain]);
194
195 encodeBlit (memoryBlitCommandBuffer.get(), sharedTexture, gpuTexture);
196
197 [memoryBlitCommandBuffer.get() addScheduledHandler: ^(id<MTLCommandBuffer>)
198 {
199 // We're on a Metal thread, so we can make a blocking nextDrawable call
200 // without stalling the message thread.
201
202 // Check if we can do an early exit.
204 return;
205
206 @autoreleasepool
207 {
208 id<CAMetalDrawable> drawable = [layer nextDrawable];
209
210 id<MTLCommandBuffer> presentationCommandBuffer = [commandQueue.get() commandBuffer];
211
212 encodeBlit (presentationCommandBuffer, gpuTexture, drawable.texture);
213
214 [presentationCommandBuffer addScheduledHandler: ^(id<MTLCommandBuffer>)
215 {
216 [drawable present];
217 }];
218
219 [presentationCommandBuffer commit];
220 }
221 }];
222
223 [memoryBlitCommandBuffer.get() commit];
224 }
225
226 return true;
227 }
228
229private:
230 //==============================================================================
231 static auto alignTo (size_t n, size_t alignment)
232 {
233 return ((n + alignment - 1) / alignment) * alignment;
234 }
235
236 //==============================================================================
238 {
239 public:
240 GpuTexturePool (id<MTLDevice> metalDevice, MTLTextureDescriptor* descriptor)
241 {
242 for (auto& t : textureCache)
243 t.reset ([metalDevice newTextureWithDescriptor: descriptor]);
244 }
245
246 id<MTLTexture> take() const
247 {
248 auto iter = std::find_if (textureCache.begin(), textureCache.end(),
249 [] (const ObjCObjectHandle<id<MTLTexture>>& t) { return [t.get() retainCount] == 1; });
250 return iter == textureCache.end() ? nullptr : (*iter).get();
251 }
252
253 private:
254 std::array<ObjCObjectHandle<id<MTLTexture>>, 3> textureCache;
255
258 };
259
260 //==============================================================================
262 {
263 public:
264 Resources (id<MTLDevice> metalDevice, CAMetalLayer* layer, int componentHeight)
265 {
266 const auto bytesPerRow = alignTo ((size_t) layer.drawableSize.width * 4, 256);
267
268 const auto allocationSize = cpuRenderMemory.ensureSize (bytesPerRow * (size_t) layer.drawableSize.height);
269
270 buffer.reset ([metalDevice newBufferWithBytesNoCopy: cpuRenderMemory.get()
271 length: allocationSize
272 options:
273 #if JUCE_MAC
274 MTLResourceStorageModeManaged
275 #else
276 MTLResourceStorageModeShared
277 #endif
278 deallocator: nullptr]);
279
280 auto* textureDesc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat: layer.pixelFormat
281 width: (NSUInteger) layer.drawableSize.width
282 height: (NSUInteger) layer.drawableSize.height
283 mipmapped: NO];
284 textureDesc.storageMode =
285 #if JUCE_MAC
286 MTLStorageModeManaged;
287 #else
288 MTLStorageModeShared;
289 #endif
290 textureDesc.usage = MTLTextureUsageShaderRead;
291
292 sharedTexture.reset ([buffer.get() newTextureWithDescriptor: textureDesc
293 offset: 0
294 bytesPerRow: bytesPerRow]);
295
296 cgContext.reset (CGBitmapContextCreate (cpuRenderMemory.get(),
297 (size_t) layer.drawableSize.width,
298 (size_t) layer.drawableSize.height,
299 8, // Bits per component
300 bytesPerRow,
301 CGColorSpaceCreateWithName (kCGColorSpaceSRGB),
302 (uint32_t) kCGImageAlphaPremultipliedFirst | (uint32_t) kCGBitmapByteOrder32Host));
303
304 CGContextScaleCTM (cgContext.get(), layer.contentsScale, layer.contentsScale);
305 CGContextConcatCTM (cgContext.get(), CGAffineTransformMake (1, 0, 0, -1, 0, componentHeight));
306
307 textureDesc.storageMode = MTLStorageModePrivate;
308 gpuTexturePool = std::make_unique<GpuTexturePool> (metalDevice, textureDesc);
309 }
310
311 CGContextRef getCGContext() const noexcept { return cgContext.get(); }
312 id<MTLTexture> getSharedTexture() const noexcept { return sharedTexture.get(); }
313 id<MTLTexture> getGpuTexture() noexcept { return gpuTexturePool == nullptr ? nullptr : gpuTexturePool->take(); }
314
316 {
317 #if JUCE_MAC
318 [buffer.get() didModifyRange: { 0, buffer.get().length }];
319 #endif
320 }
321
322 private:
324 {
325 public:
326 AlignedMemory() = default;
327
328 void* get()
329 {
330 return allocation != nullptr ? allocation->data : nullptr;
331 }
332
333 size_t ensureSize (size_t newSize)
334 {
335 const auto alignedSize = alignTo (newSize, pagesize);
336
337 if (alignedSize > size)
338 {
339 size = std::max (alignedSize, alignTo ((size_t) (size * growthFactor), pagesize));
340 allocation = std::make_unique<AllocationWrapper> (pagesize, size);
341 }
342
343 return size;
344 }
345
346 private:
347 static constexpr float growthFactor = 1.3f;
348
349 const size_t pagesize = (size_t) getpagesize();
350
352 {
353 AllocationWrapper (size_t alignment, size_t allocationSize)
354 {
355 if (posix_memalign (&data, alignment, allocationSize) != 0)
357 }
358
360 {
361 ::free (data);
362 }
363
364 void* data = nullptr;
365 };
366
367 std::unique_ptr<AllocationWrapper> allocation;
368 size_t size = 0;
369
372 };
373
375
377
380 std::unique_ptr<GpuTexturePool> gpuTexturePool;
381
384 };
385
386 //==============================================================================
387 ViewType* attachedView = nullptr;
389
390 std::unique_ptr<Resources> resources;
391
395
396 std::atomic<bool> stopGpuCommandSubmission { false };
397
400};
401
403
404}
#define noexcept
Definition DistrhoDefines.h:72
#define nullptr
Definition DistrhoDefines.h:75
static const LV2_Descriptor descriptor
Definition bindings_test_plugin.c:165
Definition juce_Component.h:36
std::array< ObjCObjectHandle< id< MTLTexture > >, 3 > textureCache
Definition juce_mac_CGMetalLayerRenderer.h:254
id< MTLTexture > take() const
Definition juce_mac_CGMetalLayerRenderer.h:246
GpuTexturePool(id< MTLDevice > metalDevice, MTLTextureDescriptor *descriptor)
Definition juce_mac_CGMetalLayerRenderer.h:240
Definition juce_mac_CGMetalLayerRenderer.h:324
std::unique_ptr< AllocationWrapper > allocation
Definition juce_mac_CGMetalLayerRenderer.h:367
static constexpr float growthFactor
Definition juce_mac_CGMetalLayerRenderer.h:347
size_t ensureSize(size_t newSize)
Definition juce_mac_CGMetalLayerRenderer.h:333
void * get()
Definition juce_mac_CGMetalLayerRenderer.h:328
const size_t pagesize
Definition juce_mac_CGMetalLayerRenderer.h:349
size_t size
Definition juce_mac_CGMetalLayerRenderer.h:368
ObjCObjectHandle< id< MTLTexture > > sharedTexture
Definition juce_mac_CGMetalLayerRenderer.h:379
ObjCObjectHandle< id< MTLBuffer > > buffer
Definition juce_mac_CGMetalLayerRenderer.h:378
AlignedMemory cpuRenderMemory
Definition juce_mac_CGMetalLayerRenderer.h:374
void signalBufferModifiedByCpu()
Definition juce_mac_CGMetalLayerRenderer.h:315
std::unique_ptr< GpuTexturePool > gpuTexturePool
Definition juce_mac_CGMetalLayerRenderer.h:380
id< MTLTexture > getSharedTexture() const noexcept
Definition juce_mac_CGMetalLayerRenderer.h:312
Resources(id< MTLDevice > metalDevice, CAMetalLayer *layer, int componentHeight)
Definition juce_mac_CGMetalLayerRenderer.h:264
id< MTLTexture > getGpuTexture() noexcept
Definition juce_mac_CGMetalLayerRenderer.h:313
detail::ContextPtr cgContext
Definition juce_mac_CGMetalLayerRenderer.h:376
CGContextRef getCGContext() const noexcept
Definition juce_mac_CGMetalLayerRenderer.h:311
Definition juce_mac_CGMetalLayerRenderer.h:35
static auto alignTo(size_t n, size_t alignment)
Definition juce_mac_CGMetalLayerRenderer.h:231
std::unique_ptr< Resources > resources
Definition juce_mac_CGMetalLayerRenderer.h:390
void detach()
Definition juce_mac_CGMetalLayerRenderer.h:75
ViewType * attachedView
Definition juce_mac_CGMetalLayerRenderer.h:387
ObjCObjectHandle< id< MTLCommandBuffer > > memoryBlitCommandBuffer
Definition juce_mac_CGMetalLayerRenderer.h:394
ObjCObjectHandle< id< MTLCommandQueue > > commandQueue
Definition juce_mac_CGMetalLayerRenderer.h:393
bool isAttachedToView(ViewType *view) const
Definition juce_mac_CGMetalLayerRenderer.h:85
~CoreGraphicsMetalLayerRenderer()
Definition juce_mac_CGMetalLayerRenderer.h:46
ObjCObjectHandle< id< MTLDevice > > device
Definition juce_mac_CGMetalLayerRenderer.h:392
bool drawRectangleList(ViewType *view, float scaleFactor, CGRect viewFrame, const Component &comp, Callback &&drawRectWithContext, const RectangleList< float > &dirtyRegions)
Definition juce_mac_CGMetalLayerRenderer.h:91
CoreGraphicsMetalLayerRenderer(ViewType *view, const Component &comp)
Definition juce_mac_CGMetalLayerRenderer.h:38
bool doSynchronousRender
Definition juce_mac_CGMetalLayerRenderer.h:388
std::atomic< bool > stopGpuCommandSubmission
Definition juce_mac_CGMetalLayerRenderer.h:396
void attach(ViewType *view, const Component &comp)
Definition juce_mac_CGMetalLayerRenderer.h:55
Definition juce_mac_ObjCHelpers.h:261
Definition juce_RectangleList.h:43
struct huft * t
Definition inflate.c:943
static int int height
Definition pugl.h:1594
static int width
Definition pugl.h:1593
#define JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE(...)
Definition juce_CompilerWarnings.h:181
#define JUCE_END_IGNORE_WARNINGS_GCC_LIKE
Definition juce_CompilerWarnings.h:182
#define JUCE_DECLARE_NON_MOVEABLE(className)
#define JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(className)
#define jassertfalse
static int JUCE_CDECL comp(const void *a, const void *b)
Definition lsp.c:298
unsigned int uint32_t
Definition mid.cpp:100
std::unique_ptr< CGContext, ContextDelete > ContextPtr
Definition juce_mac_CoreGraphicsContext.h:58
Definition carla_juce.cpp:31
png_uint_32 length
Definition png.c:2247
AllocationWrapper(size_t alignment, size_t allocationSize)
Definition juce_mac_CGMetalLayerRenderer.h:353
int n
Definition crypt.c:458
#define const
Definition zconf.h:137