Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/drivers/metal/rendering_context_driver_metal.cpp
20919 views
1
/**************************************************************************/
2
/* rendering_context_driver_metal.cpp */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10
/* */
11
/* Permission is hereby granted, free of charge, to any person obtaining */
12
/* a copy of this software and associated documentation files (the */
13
/* "Software"), to deal in the Software without restriction, including */
14
/* without limitation the rights to use, copy, modify, merge, publish, */
15
/* distribute, sublicense, and/or sell copies of the Software, and to */
16
/* permit persons to whom the Software is furnished to do so, subject to */
17
/* the following conditions: */
18
/* */
19
/* The above copyright notice and this permission notice shall be */
20
/* included in all copies or substantial portions of the Software. */
21
/* */
22
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
/**************************************************************************/
30
31
#include "rendering_context_driver_metal.h"
32
33
#include "metal3_objects.h"
34
#include "metal_objects_shared.h"
35
#include "rendering_device_driver_metal3.h"
36
37
#include "core/templates/sort_array.h"
38
39
#include <os/log.h>
40
#include <os/signpost.h>
41
42
#include <objc/message.h>
43
44
// Selector helper for calling ObjC methods from C++
45
#define _APPLE_PRIVATE_DEF_SEL(accessor, symbol) static SEL s_k##accessor = sel_registerName(symbol)
46
#define _APPLE_PRIVATE_SEL(accessor) (Private::Selector::s_k##accessor)
47
48
namespace Private::Selector {
49
50
_APPLE_PRIVATE_DEF_SEL(setOpaque_, "setOpaque:");
51
52
template <typename _Ret, typename... _Args>
53
_NS_INLINE _Ret sendMessage(const void *pObj, SEL selector, _Args... args) {
54
using SendMessageProc = _Ret (*)(const void *, SEL, _Args...);
55
const SendMessageProc pProc = reinterpret_cast<SendMessageProc>(&objc_msgSend);
56
return (*pProc)(pObj, selector, args...);
57
}
58
59
} // namespace Private::Selector
60
61
#pragma mark - Logging
62
63
os_log_t LOG_DRIVER;
64
// Used for dynamic tracing.
65
os_log_t LOG_INTERVALS;
66
67
__attribute__((constructor)) static void InitializeLogging(void) {
68
LOG_DRIVER = os_log_create("org.godotengine.godot.metal", OS_LOG_CATEGORY_POINTS_OF_INTEREST);
69
LOG_INTERVALS = os_log_create("org.godotengine.godot.metal", "events");
70
}
71
72
RenderingContextDriverMetal::RenderingContextDriverMetal() {
73
}
74
75
RenderingContextDriverMetal::~RenderingContextDriverMetal() {
76
}
77
78
Error RenderingContextDriverMetal::initialize() {
79
if (OS::get_singleton()->get_environment("MTL_CAPTURE_ENABLED") == "1" || OS::get_singleton()->get_environment("MTLCAPTURE_DESTINATION_DEVELOPER_TOOLS_ENABLE") == "1") {
80
capture_available = true;
81
}
82
83
metal_device = MTL::CreateSystemDefaultDevice();
84
#if TARGET_OS_OSX
85
if (__builtin_available(macOS 13.3, *)) {
86
metal_device->setShouldMaximizeConcurrentCompilation(true);
87
}
88
#endif
89
device.type = DEVICE_TYPE_INTEGRATED_GPU;
90
device.vendor = Vendor::VENDOR_APPLE;
91
device.workarounds = Workarounds();
92
93
MetalDeviceProperties props(metal_device);
94
int version = (int)props.features.highestFamily - (int)MTL::GPUFamilyApple1 + 1;
95
device.name = vformat("%s (Apple%d)", metal_device->name()->utf8String(), version);
96
97
return OK;
98
}
99
100
const RenderingContextDriver::Device &RenderingContextDriverMetal::device_get(uint32_t p_device_index) const {
101
DEV_ASSERT(p_device_index < 1);
102
return device;
103
}
104
105
uint32_t RenderingContextDriverMetal::device_get_count() const {
106
return 1;
107
}
108
109
RenderingDeviceDriver *RenderingContextDriverMetal::driver_create() {
110
return memnew(MTL3::RenderingDeviceDriverMetal(this));
111
}
112
113
void RenderingContextDriverMetal::driver_free(RenderingDeviceDriver *p_driver) {
114
memdelete(p_driver);
115
}
116
117
class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) SurfaceLayer : public RenderingContextDriverMetal::Surface {
118
CA::MetalLayer *layer = nullptr;
119
LocalVector<MDFrameBuffer> frame_buffers;
120
LocalVector<MTL::Drawable *> drawables;
121
uint32_t rear = -1;
122
uint32_t front = 0;
123
uint32_t count = 0;
124
125
public:
126
SurfaceLayer(CA::MetalLayer *p_layer, MTL::Device *p_device) :
127
Surface(p_device), layer(p_layer) {
128
layer->setAllowsNextDrawableTimeout(true);
129
layer->setFramebufferOnly(true);
130
Private::Selector::sendMessage<void>(layer, _APPLE_PRIVATE_SEL(setOpaque_), !OS::get_singleton()->is_layered_allowed());
131
layer->setPixelFormat(get_pixel_format());
132
layer->setDevice(p_device);
133
}
134
135
~SurfaceLayer() override {
136
layer = nullptr;
137
}
138
139
Error resize(uint32_t p_desired_framebuffer_count) override final {
140
if (width == 0 || height == 0) {
141
// Very likely the window is minimized, don't create a swap chain.
142
return ERR_SKIP;
143
}
144
145
CGSize drawableSize = CGSizeMake(width, height);
146
CGSize current = layer->drawableSize();
147
if (!CGSizeEqualToSize(current, drawableSize)) {
148
layer->setDrawableSize(drawableSize);
149
}
150
151
// Metal supports a maximum of 3 drawables.
152
p_desired_framebuffer_count = MIN(3U, p_desired_framebuffer_count);
153
layer->setMaximumDrawableCount(p_desired_framebuffer_count);
154
155
#if TARGET_OS_OSX
156
// Display sync is only supported on macOS.
157
switch (vsync_mode) {
158
case DisplayServer::VSYNC_MAILBOX:
159
case DisplayServer::VSYNC_ADAPTIVE:
160
case DisplayServer::VSYNC_ENABLED:
161
layer->setDisplaySyncEnabled(true);
162
break;
163
case DisplayServer::VSYNC_DISABLED:
164
layer->setDisplaySyncEnabled(false);
165
break;
166
}
167
#endif
168
drawables.resize(p_desired_framebuffer_count);
169
frame_buffers.resize(p_desired_framebuffer_count);
170
for (uint32_t i = 0; i < p_desired_framebuffer_count; i++) {
171
// Reserve space for the drawable texture.
172
frame_buffers[i].set_texture_count(1);
173
}
174
175
return OK;
176
}
177
178
RDD::FramebufferID acquire_next_frame_buffer() override final {
179
if (count == frame_buffers.size()) {
180
return RDD::FramebufferID();
181
}
182
183
rear = (rear + 1) % frame_buffers.size();
184
count++;
185
186
MDFrameBuffer &frame_buffer = frame_buffers[rear];
187
frame_buffer.size = Size2i(width, height);
188
189
CA::MetalDrawable *drawable = layer->nextDrawable();
190
ERR_FAIL_NULL_V_MSG(drawable, RDD::FramebufferID(), "no drawable available");
191
drawables[rear] = drawable;
192
frame_buffer.set_texture(0, drawable->texture());
193
194
return RDD::FramebufferID(&frame_buffer);
195
}
196
197
void present(MTL3::MDCommandBuffer *p_cmd_buffer) override final {
198
if (count == 0) {
199
return;
200
}
201
202
// Release texture and drawable.
203
frame_buffers[front].unset_texture(0);
204
MTL::Drawable *drawable = drawables[front];
205
drawables[front] = nullptr;
206
207
count--;
208
front = (front + 1) % frame_buffers.size();
209
210
if (vsync_mode != DisplayServer::VSYNC_DISABLED) {
211
p_cmd_buffer->get_command_buffer()->presentDrawableAfterMinimumDuration(drawable, present_minimum_duration);
212
} else {
213
p_cmd_buffer->get_command_buffer()->presentDrawable(drawable);
214
}
215
}
216
217
MTL::Drawable *next_drawable() override final {
218
if (count == 0) {
219
return nullptr;
220
}
221
222
// Release texture and drawable.
223
frame_buffers[front].unset_texture(0);
224
MTL::Drawable *drawable = drawables[front];
225
drawables[front] = nullptr;
226
227
count--;
228
front = (front + 1) % frame_buffers.size();
229
230
return drawable;
231
}
232
233
API_AVAILABLE(macos(26.0), ios(26.0))
234
MTL::ResidencySet *get_residency_set() const override final {
235
return layer->residencySet();
236
}
237
};
238
239
class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) SurfaceOffscreen : public RenderingContextDriverMetal::Surface {
240
int frame_buffer_size = 3;
241
MDFrameBuffer *frame_buffers;
242
LocalVector<MTL::Texture *> textures;
243
LocalVector<MTL::Drawable *> drawables;
244
245
int32_t rear = -1;
246
std::atomic_int count;
247
uint64_t target_time = 0;
248
CA::MetalLayer *layer;
249
250
public:
251
SurfaceOffscreen(CA::MetalLayer *p_layer, MTL::Device *p_device) :
252
Surface(p_device), layer(p_layer) {
253
layer->setAllowsNextDrawableTimeout(true);
254
layer->setFramebufferOnly(true);
255
Private::Selector::sendMessage<void>(layer, _APPLE_PRIVATE_SEL(setOpaque_), !OS::get_singleton()->is_layered_allowed());
256
layer->setPixelFormat(get_pixel_format());
257
layer->setDevice(p_device);
258
#if TARGET_OS_OSX
259
layer->setDisplaySyncEnabled(false);
260
#endif
261
target_time = OS::get_singleton()->get_ticks_usec();
262
263
textures.resize(frame_buffer_size);
264
drawables.resize(frame_buffer_size);
265
266
frame_buffers = memnew_arr(MDFrameBuffer, frame_buffer_size);
267
for (int i = 0; i < frame_buffer_size; i++) {
268
frame_buffers[i].set_texture_count(1);
269
}
270
}
271
272
~SurfaceOffscreen() override {
273
memdelete_arr(frame_buffers);
274
}
275
276
Error resize(uint32_t p_desired_framebuffer_count) override final {
277
if (width == 0 || height == 0) {
278
// Very likely the window is minimized, don't create a swap chain.
279
return ERR_SKIP;
280
}
281
282
CGSize drawableSize = CGSizeMake(width, height);
283
CGSize current = layer->drawableSize();
284
if (!CGSizeEqualToSize(current, drawableSize)) {
285
layer->setDrawableSize(drawableSize);
286
}
287
288
return OK;
289
}
290
291
RDD::FramebufferID acquire_next_frame_buffer() override final {
292
if (count.load(std::memory_order_relaxed) == 3) {
293
// Wait for a frame to be presented.
294
return RDD::FramebufferID();
295
}
296
297
rear = (rear + 1) % 3;
298
count.fetch_add(1, std::memory_order_relaxed);
299
300
MDFrameBuffer &frame_buffer = frame_buffers[rear];
301
302
if (textures[rear] == nullptr || textures[rear]->width() != width || textures[rear]->height() != height) {
303
MTL::TextureDescriptor *texture_descriptor = MTL::TextureDescriptor::texture2DDescriptor(get_pixel_format(), width, height, false);
304
texture_descriptor->setUsage(MTL::TextureUsageRenderTarget);
305
texture_descriptor->setHazardTrackingMode(MTL::HazardTrackingModeUntracked);
306
texture_descriptor->setStorageMode(MTL::StorageModePrivate);
307
textures[rear] = device->newTexture(texture_descriptor);
308
}
309
310
frame_buffer.size = Size2i(width, height);
311
uint64_t now = OS::get_singleton()->get_ticks_usec();
312
if (now >= target_time) {
313
target_time = now + 1'000'000; // 1 second into the future.
314
CA::MetalDrawable *drawable = layer->nextDrawable();
315
ERR_FAIL_NULL_V_MSG(drawable, RDD::FramebufferID(), "no drawable available");
316
drawables[rear] = drawable;
317
frame_buffer.set_texture(0, drawable->texture());
318
} else {
319
frame_buffer.set_texture(0, textures[rear]);
320
}
321
322
return RDD::FramebufferID(&frame_buffers[rear]);
323
}
324
325
void present(MTL3::MDCommandBuffer *p_cmd_buffer) override final {
326
MDFrameBuffer *frame_buffer = &frame_buffers[rear];
327
328
if (drawables[rear] != nullptr) {
329
p_cmd_buffer->get_command_buffer()->presentDrawable(drawables[rear]);
330
drawables[rear] = nullptr;
331
}
332
333
p_cmd_buffer->get_command_buffer()->addScheduledHandler([frame_buffer, this](MTL::CommandBuffer *) {
334
frame_buffer->unset_texture(0);
335
count.fetch_add(-1, std::memory_order_relaxed);
336
});
337
}
338
339
MTL::Drawable *next_drawable() override final {
340
if (count == 0) {
341
return nullptr;
342
}
343
344
MDFrameBuffer *frame_buffer = &frame_buffers[rear];
345
346
MTL::Drawable *next = drawables[rear];
347
drawables[rear] = nullptr;
348
349
frame_buffer->unset_texture(0);
350
count--;
351
352
return next;
353
}
354
355
API_AVAILABLE(macos(26.0), ios(26.0))
356
MTL::ResidencySet *get_residency_set() const override final {
357
return layer->residencySet();
358
}
359
};
360
361
RenderingContextDriver::SurfaceID RenderingContextDriverMetal::surface_create(const void *p_platform_data) {
362
const WindowPlatformData *wpd = (const WindowPlatformData *)(p_platform_data);
363
Surface *surface;
364
if (String v = OS::get_singleton()->get_environment("GODOT_MTL_OFF_SCREEN"); v == U"1") {
365
surface = memnew(SurfaceOffscreen(wpd->layer, metal_device));
366
} else {
367
surface = memnew(SurfaceLayer(wpd->layer, metal_device));
368
}
369
370
return SurfaceID(surface);
371
}
372
373
void RenderingContextDriverMetal::surface_set_size(SurfaceID p_surface, uint32_t p_width, uint32_t p_height) {
374
Surface *surface = (Surface *)(p_surface);
375
if (surface->width == p_width && surface->height == p_height) {
376
return;
377
}
378
surface->width = p_width;
379
surface->height = p_height;
380
surface->needs_resize = true;
381
}
382
383
void RenderingContextDriverMetal::surface_set_vsync_mode(SurfaceID p_surface, DisplayServer::VSyncMode p_vsync_mode) {
384
Surface *surface = (Surface *)(p_surface);
385
if (surface->vsync_mode == p_vsync_mode) {
386
return;
387
}
388
surface->vsync_mode = p_vsync_mode;
389
surface->needs_resize = true;
390
}
391
392
DisplayServer::VSyncMode RenderingContextDriverMetal::surface_get_vsync_mode(SurfaceID p_surface) const {
393
Surface *surface = (Surface *)(p_surface);
394
return surface->vsync_mode;
395
}
396
397
uint32_t RenderingContextDriverMetal::surface_get_width(SurfaceID p_surface) const {
398
Surface *surface = (Surface *)(p_surface);
399
return surface->width;
400
}
401
402
uint32_t RenderingContextDriverMetal::surface_get_height(SurfaceID p_surface) const {
403
Surface *surface = (Surface *)(p_surface);
404
return surface->height;
405
}
406
407
void RenderingContextDriverMetal::surface_set_needs_resize(SurfaceID p_surface, bool p_needs_resize) {
408
Surface *surface = (Surface *)(p_surface);
409
surface->needs_resize = p_needs_resize;
410
}
411
412
bool RenderingContextDriverMetal::surface_get_needs_resize(SurfaceID p_surface) const {
413
Surface *surface = (Surface *)(p_surface);
414
return surface->needs_resize;
415
}
416
417
void RenderingContextDriverMetal::surface_destroy(SurfaceID p_surface) {
418
Surface *surface = (Surface *)(p_surface);
419
memdelete(surface);
420
}
421
422