rendering_context_driver_metal.mm 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. /**************************************************************************/
  2. /* rendering_context_driver_metal.mm */
  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. #import "rendering_context_driver_metal.h"
  31. #import "rendering_device_driver_metal.h"
  32. @protocol MTLDeviceEx <MTLDevice>
  33. #if TARGET_OS_OSX && __MAC_OS_X_VERSION_MAX_ALLOWED < 130300
  34. - (void)setShouldMaximizeConcurrentCompilation:(BOOL)v;
  35. #endif
  36. @end
  37. RenderingContextDriverMetal::RenderingContextDriverMetal() {
  38. }
  39. RenderingContextDriverMetal::~RenderingContextDriverMetal() {
  40. }
  41. Error RenderingContextDriverMetal::initialize() {
  42. metal_device = MTLCreateSystemDefaultDevice();
  43. #if TARGET_OS_OSX
  44. if (@available(macOS 13.3, *)) {
  45. [id<MTLDeviceEx>(metal_device) setShouldMaximizeConcurrentCompilation:YES];
  46. }
  47. #endif
  48. device.type = DEVICE_TYPE_INTEGRATED_GPU;
  49. device.vendor = Vendor::VENDOR_APPLE;
  50. device.workarounds = Workarounds();
  51. MetalDeviceProperties props(metal_device);
  52. int version = (int)props.features.highestFamily - (int)MTLGPUFamilyApple1 + 1;
  53. device.name = vformat("%s (Apple%d)", metal_device.name.UTF8String, version);
  54. return OK;
  55. }
  56. const RenderingContextDriver::Device &RenderingContextDriverMetal::device_get(uint32_t p_device_index) const {
  57. DEV_ASSERT(p_device_index < 1);
  58. return device;
  59. }
  60. uint32_t RenderingContextDriverMetal::device_get_count() const {
  61. return 1;
  62. }
  63. RenderingDeviceDriver *RenderingContextDriverMetal::driver_create() {
  64. return memnew(RenderingDeviceDriverMetal(this));
  65. }
  66. void RenderingContextDriverMetal::driver_free(RenderingDeviceDriver *p_driver) {
  67. memdelete(p_driver);
  68. }
  69. class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) SurfaceLayer : public RenderingContextDriverMetal::Surface {
  70. CAMetalLayer *__unsafe_unretained layer = nil;
  71. LocalVector<MDFrameBuffer> frame_buffers;
  72. LocalVector<id<MTLDrawable>> drawables;
  73. uint32_t rear = -1;
  74. uint32_t front = 0;
  75. uint32_t count = 0;
  76. public:
  77. SurfaceLayer(CAMetalLayer *p_layer, id<MTLDevice> p_device) :
  78. Surface(p_device), layer(p_layer) {
  79. layer.allowsNextDrawableTimeout = YES;
  80. layer.framebufferOnly = YES;
  81. layer.opaque = OS::get_singleton()->is_layered_allowed() ? NO : YES;
  82. layer.pixelFormat = get_pixel_format();
  83. layer.device = p_device;
  84. }
  85. ~SurfaceLayer() override {
  86. layer = nil;
  87. }
  88. Error resize(uint32_t p_desired_framebuffer_count) override final {
  89. if (width == 0 || height == 0) {
  90. // Very likely the window is minimized, don't create a swap chain.
  91. return ERR_SKIP;
  92. }
  93. CGSize drawableSize = CGSizeMake(width, height);
  94. CGSize current = layer.drawableSize;
  95. if (!CGSizeEqualToSize(current, drawableSize)) {
  96. layer.drawableSize = drawableSize;
  97. }
  98. // Metal supports a maximum of 3 drawables.
  99. p_desired_framebuffer_count = MIN(3U, p_desired_framebuffer_count);
  100. layer.maximumDrawableCount = p_desired_framebuffer_count;
  101. #if TARGET_OS_OSX
  102. // Display sync is only supported on macOS.
  103. switch (vsync_mode) {
  104. case DisplayServer::VSYNC_MAILBOX:
  105. case DisplayServer::VSYNC_ADAPTIVE:
  106. case DisplayServer::VSYNC_ENABLED:
  107. layer.displaySyncEnabled = YES;
  108. break;
  109. case DisplayServer::VSYNC_DISABLED:
  110. layer.displaySyncEnabled = NO;
  111. break;
  112. }
  113. #endif
  114. drawables.resize(p_desired_framebuffer_count);
  115. frame_buffers.resize(p_desired_framebuffer_count);
  116. for (uint32_t i = 0; i < p_desired_framebuffer_count; i++) {
  117. // Reserve space for the drawable texture.
  118. frame_buffers[i].set_texture_count(1);
  119. }
  120. return OK;
  121. }
  122. RDD::FramebufferID acquire_next_frame_buffer() override final {
  123. if (count == frame_buffers.size()) {
  124. return RDD::FramebufferID();
  125. }
  126. rear = (rear + 1) % frame_buffers.size();
  127. count++;
  128. MDFrameBuffer &frame_buffer = frame_buffers[rear];
  129. frame_buffer.size = Size2i(width, height);
  130. id<CAMetalDrawable> drawable = layer.nextDrawable;
  131. ERR_FAIL_NULL_V_MSG(drawable, RDD::FramebufferID(), "no drawable available");
  132. drawables[rear] = drawable;
  133. frame_buffer.set_texture(0, drawable.texture);
  134. return RDD::FramebufferID(&frame_buffer);
  135. }
  136. void present(MDCommandBuffer *p_cmd_buffer) override final {
  137. if (count == 0) {
  138. return;
  139. }
  140. // Release texture and drawable.
  141. frame_buffers[front].unset_texture(0);
  142. id<MTLDrawable> drawable = drawables[front];
  143. drawables[front] = nil;
  144. count--;
  145. front = (front + 1) % frame_buffers.size();
  146. if (vsync_mode != DisplayServer::VSYNC_DISABLED) {
  147. [p_cmd_buffer->get_command_buffer() presentDrawable:drawable afterMinimumDuration:present_minimum_duration];
  148. } else {
  149. [p_cmd_buffer->get_command_buffer() presentDrawable:drawable];
  150. }
  151. }
  152. };
  153. RenderingContextDriver::SurfaceID RenderingContextDriverMetal::surface_create(const void *p_platform_data) {
  154. const WindowPlatformData *wpd = (const WindowPlatformData *)(p_platform_data);
  155. Surface *surface = memnew(SurfaceLayer(wpd->layer, metal_device));
  156. return SurfaceID(surface);
  157. }
  158. void RenderingContextDriverMetal::surface_set_size(SurfaceID p_surface, uint32_t p_width, uint32_t p_height) {
  159. Surface *surface = (Surface *)(p_surface);
  160. if (surface->width == p_width && surface->height == p_height) {
  161. return;
  162. }
  163. surface->width = p_width;
  164. surface->height = p_height;
  165. surface->needs_resize = true;
  166. }
  167. void RenderingContextDriverMetal::surface_set_vsync_mode(SurfaceID p_surface, DisplayServer::VSyncMode p_vsync_mode) {
  168. Surface *surface = (Surface *)(p_surface);
  169. if (surface->vsync_mode == p_vsync_mode) {
  170. return;
  171. }
  172. surface->vsync_mode = p_vsync_mode;
  173. surface->needs_resize = true;
  174. }
  175. DisplayServer::VSyncMode RenderingContextDriverMetal::surface_get_vsync_mode(SurfaceID p_surface) const {
  176. Surface *surface = (Surface *)(p_surface);
  177. return surface->vsync_mode;
  178. }
  179. uint32_t RenderingContextDriverMetal::surface_get_width(SurfaceID p_surface) const {
  180. Surface *surface = (Surface *)(p_surface);
  181. return surface->width;
  182. }
  183. uint32_t RenderingContextDriverMetal::surface_get_height(SurfaceID p_surface) const {
  184. Surface *surface = (Surface *)(p_surface);
  185. return surface->height;
  186. }
  187. void RenderingContextDriverMetal::surface_set_needs_resize(SurfaceID p_surface, bool p_needs_resize) {
  188. Surface *surface = (Surface *)(p_surface);
  189. surface->needs_resize = p_needs_resize;
  190. }
  191. bool RenderingContextDriverMetal::surface_get_needs_resize(SurfaceID p_surface) const {
  192. Surface *surface = (Surface *)(p_surface);
  193. return surface->needs_resize;
  194. }
  195. void RenderingContextDriverMetal::surface_destroy(SurfaceID p_surface) {
  196. Surface *surface = (Surface *)(p_surface);
  197. memdelete(surface);
  198. }