diff --git a/BREAKING-CHANGES.txt b/BREAKING-CHANGES.txt
index c1902281f0..d1a426ed71 100644
--- a/BREAKING-CHANGES.txt
+++ b/BREAKING-CHANGES.txt
@@ -4,6 +4,35 @@ JUCE breaking changes
develop
=======
+Change
+------
+The optional JUCE_COREGRAPHICS_RENDER_WITH_MULTIPLE_PAINT_CALLS preprocessor
+flag will now use a new Metal layer renderer when running on macOS 10.14 or
+later. The minimum requirements for building macOS and iOS software are now
+macOS 10.13.6 and Xcode 10.1.
+
+Possible Issues
+---------------
+Previously enabling JUCE_COREGRAPHICS_RENDER_WITH_MULTIPLE_PAINT_CALLS had no
+negative effect on performance. Now it may slow rendering down.
+
+Workaround
+----------
+Disable JUCE_COREGRAPHICS_RENDER_WITH_MULTIPLE_PAINT_CALLS.
+
+Rationale
+---------
+JUCE_COREGRAPHICS_RENDER_WITH_MULTIPLE_PAINT_CALLS has been ineffective when
+running on macOS 10.13 or later. Enabling this flag, and hence using the new
+Metal layer renderer when running on macOS 10.14, restores the previous
+behaviour and fixes problems where Core Graphics will render much larger
+regions than necessary. However, the new renderer will may be slower than the
+recently introduced default of asynchronous Core Graphics rendering, depending
+on the regions that Core Graphcis is redrawing. Whether
+JUCE_COREGRAPHICS_RENDER_WITH_MULTIPLE_PAINT_CALLS improves or degrades
+performance is specific to an application.
+
+
Change
------
The optional JUCE_COREGRAPHICS_DRAW_ASYNC preprocessor flag has been removed
diff --git a/README.md b/README.md
index 0f0eaa8373..2bf83b76d7 100644
--- a/README.md
+++ b/README.md
@@ -56,7 +56,7 @@ of the target you wish to build.
#### Building JUCE Projects
-- __macOS/iOS__: Xcode 9.2 (macOS 10.12.6)
+- __macOS/iOS__: Xcode 10.1 (macOS 10.13.6)
- __Windows__: Windows 8.1 and Visual Studio 2015 Update 3 64-bit
- __Linux__: g++ 5.0 or Clang 3.4 (for a full list of dependencies, see
[here](/docs/Linux%20Dependencies.md)).
diff --git a/examples/DemoRunner/Builds/Android/app/CMakeLists.txt b/examples/DemoRunner/Builds/Android/app/CMakeLists.txt
index f85956f303..783e5a7d06 100644
--- a/examples/DemoRunner/Builds/Android/app/CMakeLists.txt
+++ b/examples/DemoRunner/Builds/Android/app/CMakeLists.txt
@@ -1598,6 +1598,7 @@ add_library( ${BINARY_NAME}
"../../../../../modules/juce_gui_basics/native/juce_ios_Windowing.mm"
"../../../../../modules/juce_gui_basics/native/juce_linux_FileChooser.cpp"
"../../../../../modules/juce_gui_basics/native/juce_linux_Windowing.cpp"
+ "../../../../../modules/juce_gui_basics/native/juce_mac_CGMetalLayerRenderer.h"
"../../../../../modules/juce_gui_basics/native/juce_mac_FileChooser.mm"
"../../../../../modules/juce_gui_basics/native/juce_mac_MainMenu.mm"
"../../../../../modules/juce_gui_basics/native/juce_mac_MouseCursor.mm"
@@ -3417,6 +3418,7 @@ set_source_files_properties("../../../../../modules/juce_gui_basics/native/juce_
set_source_files_properties("../../../../../modules/juce_gui_basics/native/juce_ios_Windowing.mm" PROPERTIES HEADER_FILE_ONLY TRUE)
set_source_files_properties("../../../../../modules/juce_gui_basics/native/juce_linux_FileChooser.cpp" PROPERTIES HEADER_FILE_ONLY TRUE)
set_source_files_properties("../../../../../modules/juce_gui_basics/native/juce_linux_Windowing.cpp" PROPERTIES HEADER_FILE_ONLY TRUE)
+set_source_files_properties("../../../../../modules/juce_gui_basics/native/juce_mac_CGMetalLayerRenderer.h" PROPERTIES HEADER_FILE_ONLY TRUE)
set_source_files_properties("../../../../../modules/juce_gui_basics/native/juce_mac_FileChooser.mm" PROPERTIES HEADER_FILE_ONLY TRUE)
set_source_files_properties("../../../../../modules/juce_gui_basics/native/juce_mac_MainMenu.mm" PROPERTIES HEADER_FILE_ONLY TRUE)
set_source_files_properties("../../../../../modules/juce_gui_basics/native/juce_mac_MouseCursor.mm" PROPERTIES HEADER_FILE_ONLY TRUE)
diff --git a/examples/DemoRunner/Builds/MacOSX/DemoRunner.xcodeproj/project.pbxproj b/examples/DemoRunner/Builds/MacOSX/DemoRunner.xcodeproj/project.pbxproj
index 49a4fe2cea..5c11719299 100644
--- a/examples/DemoRunner/Builds/MacOSX/DemoRunner.xcodeproj/project.pbxproj
+++ b/examples/DemoRunner/Builds/MacOSX/DemoRunner.xcodeproj/project.pbxproj
@@ -563,6 +563,7 @@
MTL_HEADER_SEARCH_PATHS = "$(SRCROOT)/../../../../modules/juce_audio_processors/format_types/VST3_SDK $(SRCROOT)/../../JuceLibraryCode $(SRCROOT)/../../../../modules";
OTHER_CFLAGS = "-Wall -Wstrict-aliasing -Wuninitialized -Wunused-parameter -Wswitch-enum -Wsign-conversion -Wsign-compare -Wunreachable-code -Wcast-align -Wno-ignored-qualifiers -Wshorten-64-to-32 -Wconversion -Wint-conversion -Wconditional-uninitialized -Wconstant-conversion -Wbool-conversion -Wextra-semi -Wshift-sign-overflow -Wno-missing-field-initializers -Wshadow-all -Wnullable-to-nonnull-conversion";
OTHER_CPLUSPLUSFLAGS = "-Woverloaded-virtual -Wreorder -Wzero-as-null-pointer-constant -Wunused-private-field -Winconsistent-missing-destructor-override -Wall -Wstrict-aliasing -Wuninitialized -Wunused-parameter -Wswitch-enum -Wsign-conversion -Wsign-compare -Wunreachable-code -Wcast-align -Wno-ignored-qualifiers -Wshorten-64-to-32 -Wconversion -Wint-conversion -Wconditional-uninitialized -Wconstant-conversion -Wbool-conversion -Wextra-semi -Wshift-sign-overflow -Wno-missing-field-initializers -Wshadow-all -Wnullable-to-nonnull-conversion";
+ OTHER_LDFLAGS = "-weak_framework Metal -weak_framework MetalKit";
PRODUCT_BUNDLE_IDENTIFIER = com.rmsl.jucedemorunner;
PRODUCT_NAME = "DemoRunner";
USE_HEADERMAP = NO;
@@ -644,6 +645,7 @@
MTL_HEADER_SEARCH_PATHS = "$(SRCROOT)/../../../../modules/juce_audio_processors/format_types/VST3_SDK $(SRCROOT)/../../JuceLibraryCode $(SRCROOT)/../../../../modules";
OTHER_CFLAGS = "-Wall -Wstrict-aliasing -Wuninitialized -Wunused-parameter -Wswitch-enum -Wsign-conversion -Wsign-compare -Wunreachable-code -Wcast-align -Wno-ignored-qualifiers -Wshorten-64-to-32 -Wconversion -Wint-conversion -Wconditional-uninitialized -Wconstant-conversion -Wbool-conversion -Wextra-semi -Wshift-sign-overflow -Wno-missing-field-initializers -Wshadow-all -Wnullable-to-nonnull-conversion";
OTHER_CPLUSPLUSFLAGS = "-Woverloaded-virtual -Wreorder -Wzero-as-null-pointer-constant -Wunused-private-field -Winconsistent-missing-destructor-override -Wall -Wstrict-aliasing -Wuninitialized -Wunused-parameter -Wswitch-enum -Wsign-conversion -Wsign-compare -Wunreachable-code -Wcast-align -Wno-ignored-qualifiers -Wshorten-64-to-32 -Wconversion -Wint-conversion -Wconditional-uninitialized -Wconstant-conversion -Wbool-conversion -Wextra-semi -Wshift-sign-overflow -Wno-missing-field-initializers -Wshadow-all -Wnullable-to-nonnull-conversion";
+ OTHER_LDFLAGS = "-weak_framework Metal -weak_framework MetalKit";
PRODUCT_BUNDLE_IDENTIFIER = com.rmsl.jucedemorunner;
PRODUCT_NAME = "DemoRunner";
USE_HEADERMAP = NO;
diff --git a/examples/DemoRunner/Builds/VisualStudio2015/DemoRunner_App.vcxproj b/examples/DemoRunner/Builds/VisualStudio2015/DemoRunner_App.vcxproj
index 31f527656b..f8d1e5f5da 100644
--- a/examples/DemoRunner/Builds/VisualStudio2015/DemoRunner_App.vcxproj
+++ b/examples/DemoRunner/Builds/VisualStudio2015/DemoRunner_App.vcxproj
@@ -3322,6 +3322,7 @@
+
diff --git a/examples/DemoRunner/Builds/VisualStudio2015/DemoRunner_App.vcxproj.filters b/examples/DemoRunner/Builds/VisualStudio2015/DemoRunner_App.vcxproj.filters
index 28ea61fd3e..01719a20d2 100644
--- a/examples/DemoRunner/Builds/VisualStudio2015/DemoRunner_App.vcxproj.filters
+++ b/examples/DemoRunner/Builds/VisualStudio2015/DemoRunner_App.vcxproj.filters
@@ -5601,6 +5601,9 @@
JUCE Modules\juce_gui_basics\native\x11
+
+ JUCE Modules\juce_gui_basics\native
+
JUCE Modules\juce_gui_basics\native
diff --git a/examples/DemoRunner/Builds/VisualStudio2017/DemoRunner_App.vcxproj b/examples/DemoRunner/Builds/VisualStudio2017/DemoRunner_App.vcxproj
index f977722847..08341d4149 100644
--- a/examples/DemoRunner/Builds/VisualStudio2017/DemoRunner_App.vcxproj
+++ b/examples/DemoRunner/Builds/VisualStudio2017/DemoRunner_App.vcxproj
@@ -3322,6 +3322,7 @@
+
diff --git a/examples/DemoRunner/Builds/VisualStudio2017/DemoRunner_App.vcxproj.filters b/examples/DemoRunner/Builds/VisualStudio2017/DemoRunner_App.vcxproj.filters
index f3907e60c5..f294d18152 100644
--- a/examples/DemoRunner/Builds/VisualStudio2017/DemoRunner_App.vcxproj.filters
+++ b/examples/DemoRunner/Builds/VisualStudio2017/DemoRunner_App.vcxproj.filters
@@ -5601,6 +5601,9 @@
JUCE Modules\juce_gui_basics\native\x11
+
+ JUCE Modules\juce_gui_basics\native
+
JUCE Modules\juce_gui_basics\native
diff --git a/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj b/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj
index d4ee8410a5..ad1fe5559b 100644
--- a/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj
+++ b/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj
@@ -3322,6 +3322,7 @@
+
diff --git a/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj.filters b/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj.filters
index 847b5bb6dd..b1474a9667 100644
--- a/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj.filters
+++ b/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj.filters
@@ -5601,6 +5601,9 @@
JUCE Modules\juce_gui_basics\native\x11
+
+ JUCE Modules\juce_gui_basics\native
+
JUCE Modules\juce_gui_basics\native
diff --git a/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj b/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj
index 3e9d6f6e0b..5417e09211 100644
--- a/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj
+++ b/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj
@@ -3322,6 +3322,7 @@
+
diff --git a/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj.filters b/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj.filters
index c03cb7c744..276dd80790 100644
--- a/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj.filters
+++ b/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj.filters
@@ -5601,6 +5601,9 @@
JUCE Modules\juce_gui_basics\native\x11
+
+ JUCE Modules\juce_gui_basics\native
+
JUCE Modules\juce_gui_basics\native
diff --git a/examples/DemoRunner/Builds/iOS/DemoRunner.xcodeproj/project.pbxproj b/examples/DemoRunner/Builds/iOS/DemoRunner.xcodeproj/project.pbxproj
index 7c66b55946..c04e8f6fed 100644
--- a/examples/DemoRunner/Builds/iOS/DemoRunner.xcodeproj/project.pbxproj
+++ b/examples/DemoRunner/Builds/iOS/DemoRunner.xcodeproj/project.pbxproj
@@ -566,6 +566,7 @@
MTL_HEADER_SEARCH_PATHS = "$(SRCROOT)/../../../../modules/juce_audio_processors/format_types/VST3_SDK $(SRCROOT)/../../JuceLibraryCode $(SRCROOT)/../../../../modules";
OTHER_CFLAGS = "-Wall -Wstrict-aliasing -Wuninitialized -Wunused-parameter -Wswitch-enum -Wsign-conversion -Wsign-compare -Wunreachable-code -Wcast-align -Wno-ignored-qualifiers -Wshorten-64-to-32 -Wconversion -Wint-conversion -Wconditional-uninitialized -Wconstant-conversion -Wbool-conversion -Wextra-semi -Wshift-sign-overflow -Wno-missing-field-initializers -Wshadow-all -Wnullable-to-nonnull-conversion";
OTHER_CPLUSPLUSFLAGS = "-Woverloaded-virtual -Wreorder -Wzero-as-null-pointer-constant -Wunused-private-field -Winconsistent-missing-destructor-override -Wall -Wstrict-aliasing -Wuninitialized -Wunused-parameter -Wswitch-enum -Wsign-conversion -Wsign-compare -Wunreachable-code -Wcast-align -Wno-ignored-qualifiers -Wshorten-64-to-32 -Wconversion -Wint-conversion -Wconditional-uninitialized -Wconstant-conversion -Wbool-conversion -Wextra-semi -Wshift-sign-overflow -Wno-missing-field-initializers -Wshadow-all -Wnullable-to-nonnull-conversion";
+ OTHER_LDFLAGS = "-weak_framework Metal -weak_framework MetalKit";
PRODUCT_BUNDLE_IDENTIFIER = com.rmsl.jucedemorunner;
PRODUCT_NAME = "DemoRunner";
USE_HEADERMAP = NO;
@@ -647,6 +648,7 @@
MTL_HEADER_SEARCH_PATHS = "$(SRCROOT)/../../../../modules/juce_audio_processors/format_types/VST3_SDK $(SRCROOT)/../../JuceLibraryCode $(SRCROOT)/../../../../modules";
OTHER_CFLAGS = "-Wall -Wstrict-aliasing -Wuninitialized -Wunused-parameter -Wswitch-enum -Wsign-conversion -Wsign-compare -Wunreachable-code -Wcast-align -Wno-ignored-qualifiers -Wshorten-64-to-32 -Wconversion -Wint-conversion -Wconditional-uninitialized -Wconstant-conversion -Wbool-conversion -Wextra-semi -Wshift-sign-overflow -Wno-missing-field-initializers -Wshadow-all -Wnullable-to-nonnull-conversion";
OTHER_CPLUSPLUSFLAGS = "-Woverloaded-virtual -Wreorder -Wzero-as-null-pointer-constant -Wunused-private-field -Winconsistent-missing-destructor-override -Wall -Wstrict-aliasing -Wuninitialized -Wunused-parameter -Wswitch-enum -Wsign-conversion -Wsign-compare -Wunreachable-code -Wcast-align -Wno-ignored-qualifiers -Wshorten-64-to-32 -Wconversion -Wint-conversion -Wconditional-uninitialized -Wconstant-conversion -Wbool-conversion -Wextra-semi -Wshift-sign-overflow -Wno-missing-field-initializers -Wshadow-all -Wnullable-to-nonnull-conversion";
+ OTHER_LDFLAGS = "-weak_framework Metal -weak_framework MetalKit";
PRODUCT_BUNDLE_IDENTIFIER = com.rmsl.jucedemorunner;
PRODUCT_NAME = "DemoRunner";
USE_HEADERMAP = NO;
diff --git a/extras/AudioPerformanceTest/Builds/Android/app/CMakeLists.txt b/extras/AudioPerformanceTest/Builds/Android/app/CMakeLists.txt
index 9eb85f99e3..c42821d270 100644
--- a/extras/AudioPerformanceTest/Builds/Android/app/CMakeLists.txt
+++ b/extras/AudioPerformanceTest/Builds/Android/app/CMakeLists.txt
@@ -1383,6 +1383,7 @@ add_library( ${BINARY_NAME}
"../../../../../modules/juce_gui_basics/native/juce_ios_Windowing.mm"
"../../../../../modules/juce_gui_basics/native/juce_linux_FileChooser.cpp"
"../../../../../modules/juce_gui_basics/native/juce_linux_Windowing.cpp"
+ "../../../../../modules/juce_gui_basics/native/juce_mac_CGMetalLayerRenderer.h"
"../../../../../modules/juce_gui_basics/native/juce_mac_FileChooser.mm"
"../../../../../modules/juce_gui_basics/native/juce_mac_MainMenu.mm"
"../../../../../modules/juce_gui_basics/native/juce_mac_MouseCursor.mm"
@@ -2900,6 +2901,7 @@ set_source_files_properties("../../../../../modules/juce_gui_basics/native/juce_
set_source_files_properties("../../../../../modules/juce_gui_basics/native/juce_ios_Windowing.mm" PROPERTIES HEADER_FILE_ONLY TRUE)
set_source_files_properties("../../../../../modules/juce_gui_basics/native/juce_linux_FileChooser.cpp" PROPERTIES HEADER_FILE_ONLY TRUE)
set_source_files_properties("../../../../../modules/juce_gui_basics/native/juce_linux_Windowing.cpp" PROPERTIES HEADER_FILE_ONLY TRUE)
+set_source_files_properties("../../../../../modules/juce_gui_basics/native/juce_mac_CGMetalLayerRenderer.h" PROPERTIES HEADER_FILE_ONLY TRUE)
set_source_files_properties("../../../../../modules/juce_gui_basics/native/juce_mac_FileChooser.mm" PROPERTIES HEADER_FILE_ONLY TRUE)
set_source_files_properties("../../../../../modules/juce_gui_basics/native/juce_mac_MainMenu.mm" PROPERTIES HEADER_FILE_ONLY TRUE)
set_source_files_properties("../../../../../modules/juce_gui_basics/native/juce_mac_MouseCursor.mm" PROPERTIES HEADER_FILE_ONLY TRUE)
diff --git a/extras/AudioPerformanceTest/Builds/MacOSX/AudioPerformanceTest.xcodeproj/project.pbxproj b/extras/AudioPerformanceTest/Builds/MacOSX/AudioPerformanceTest.xcodeproj/project.pbxproj
index dd7d19f3ad..71ba34262a 100644
--- a/extras/AudioPerformanceTest/Builds/MacOSX/AudioPerformanceTest.xcodeproj/project.pbxproj
+++ b/extras/AudioPerformanceTest/Builds/MacOSX/AudioPerformanceTest.xcodeproj/project.pbxproj
@@ -362,6 +362,7 @@
INSTALL_PATH = "$(HOME)/Applications";
MACOSX_DEPLOYMENT_TARGET = 10.11;
MTL_HEADER_SEARCH_PATHS = "$(SRCROOT)/../../JuceLibraryCode $(SRCROOT)/../../../../modules";
+ OTHER_LDFLAGS = "-weak_framework Metal -weak_framework MetalKit";
PRODUCT_BUNDLE_IDENTIFIER = com.juce.AudioPerformanceTest;
PRODUCT_NAME = "AudioPerformanceTest";
USE_HEADERMAP = NO;
@@ -425,6 +426,7 @@
LLVM_LTO = YES;
MACOSX_DEPLOYMENT_TARGET = 10.11;
MTL_HEADER_SEARCH_PATHS = "$(SRCROOT)/../../JuceLibraryCode $(SRCROOT)/../../../../modules";
+ OTHER_LDFLAGS = "-weak_framework Metal -weak_framework MetalKit";
PRODUCT_BUNDLE_IDENTIFIER = com.juce.AudioPerformanceTest;
PRODUCT_NAME = "AudioPerformanceTest";
USE_HEADERMAP = NO;
diff --git a/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj b/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj
index 18254d60d9..bff451bd68 100644
--- a/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj
+++ b/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj
@@ -2821,6 +2821,7 @@
+
diff --git a/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj.filters b/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj.filters
index 7e4197a5d6..1427384857 100644
--- a/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj.filters
+++ b/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj.filters
@@ -4716,6 +4716,9 @@
JUCE Modules\juce_gui_basics\native\x11
+
+ JUCE Modules\juce_gui_basics\native
+
JUCE Modules\juce_gui_basics\native
diff --git a/extras/AudioPerformanceTest/Builds/iOS/AudioPerformanceTest.xcodeproj/project.pbxproj b/extras/AudioPerformanceTest/Builds/iOS/AudioPerformanceTest.xcodeproj/project.pbxproj
index 30323f434a..245d81c0eb 100644
--- a/extras/AudioPerformanceTest/Builds/iOS/AudioPerformanceTest.xcodeproj/project.pbxproj
+++ b/extras/AudioPerformanceTest/Builds/iOS/AudioPerformanceTest.xcodeproj/project.pbxproj
@@ -374,6 +374,7 @@
INFOPLIST_PREPROCESS = NO;
INSTALL_PATH = "$(HOME)/Applications";
MTL_HEADER_SEARCH_PATHS = "$(SRCROOT)/../../JuceLibraryCode $(SRCROOT)/../../../../modules";
+ OTHER_LDFLAGS = "-weak_framework Metal -weak_framework MetalKit";
PRODUCT_BUNDLE_IDENTIFIER = com.juce.AudioPerformanceTest;
PRODUCT_NAME = "AudioPerformanceTest";
USE_HEADERMAP = NO;
@@ -436,6 +437,7 @@
INSTALL_PATH = "$(HOME)/Applications";
LLVM_LTO = YES;
MTL_HEADER_SEARCH_PATHS = "$(SRCROOT)/../../JuceLibraryCode $(SRCROOT)/../../../../modules";
+ OTHER_LDFLAGS = "-weak_framework Metal -weak_framework MetalKit";
PRODUCT_BUNDLE_IDENTIFIER = com.juce.AudioPerformanceTest;
PRODUCT_NAME = "AudioPerformanceTest";
USE_HEADERMAP = NO;
diff --git a/extras/AudioPluginHost/Builds/Android/app/CMakeLists.txt b/extras/AudioPluginHost/Builds/Android/app/CMakeLists.txt
index 1bf2397d9f..fe6cac9bc4 100644
--- a/extras/AudioPluginHost/Builds/Android/app/CMakeLists.txt
+++ b/extras/AudioPluginHost/Builds/Android/app/CMakeLists.txt
@@ -1505,6 +1505,7 @@ add_library( ${BINARY_NAME}
"../../../../../modules/juce_gui_basics/native/juce_ios_Windowing.mm"
"../../../../../modules/juce_gui_basics/native/juce_linux_FileChooser.cpp"
"../../../../../modules/juce_gui_basics/native/juce_linux_Windowing.cpp"
+ "../../../../../modules/juce_gui_basics/native/juce_mac_CGMetalLayerRenderer.h"
"../../../../../modules/juce_gui_basics/native/juce_mac_FileChooser.mm"
"../../../../../modules/juce_gui_basics/native/juce_mac_MainMenu.mm"
"../../../../../modules/juce_gui_basics/native/juce_mac_MouseCursor.mm"
@@ -3176,6 +3177,7 @@ set_source_files_properties("../../../../../modules/juce_gui_basics/native/juce_
set_source_files_properties("../../../../../modules/juce_gui_basics/native/juce_ios_Windowing.mm" PROPERTIES HEADER_FILE_ONLY TRUE)
set_source_files_properties("../../../../../modules/juce_gui_basics/native/juce_linux_FileChooser.cpp" PROPERTIES HEADER_FILE_ONLY TRUE)
set_source_files_properties("../../../../../modules/juce_gui_basics/native/juce_linux_Windowing.cpp" PROPERTIES HEADER_FILE_ONLY TRUE)
+set_source_files_properties("../../../../../modules/juce_gui_basics/native/juce_mac_CGMetalLayerRenderer.h" PROPERTIES HEADER_FILE_ONLY TRUE)
set_source_files_properties("../../../../../modules/juce_gui_basics/native/juce_mac_FileChooser.mm" PROPERTIES HEADER_FILE_ONLY TRUE)
set_source_files_properties("../../../../../modules/juce_gui_basics/native/juce_mac_MainMenu.mm" PROPERTIES HEADER_FILE_ONLY TRUE)
set_source_files_properties("../../../../../modules/juce_gui_basics/native/juce_mac_MouseCursor.mm" PROPERTIES HEADER_FILE_ONLY TRUE)
diff --git a/extras/AudioPluginHost/Builds/MacOSX/AudioPluginHost.xcodeproj/project.pbxproj b/extras/AudioPluginHost/Builds/MacOSX/AudioPluginHost.xcodeproj/project.pbxproj
index 4437f8df07..ecfff1ffe1 100644
--- a/extras/AudioPluginHost/Builds/MacOSX/AudioPluginHost.xcodeproj/project.pbxproj
+++ b/extras/AudioPluginHost/Builds/MacOSX/AudioPluginHost.xcodeproj/project.pbxproj
@@ -488,6 +488,7 @@
MTL_HEADER_SEARCH_PATHS = "$(SRCROOT)/../../../../modules/juce_audio_processors/format_types/VST3_SDK $(SRCROOT)/../../JuceLibraryCode $(SRCROOT)/../../../../modules";
OTHER_CFLAGS = "-Wall -Wstrict-aliasing -Wuninitialized -Wunused-parameter -Wswitch-enum -Wsign-conversion -Wsign-compare -Wunreachable-code -Wcast-align -Wno-ignored-qualifiers -Wshorten-64-to-32 -Wconversion -Wint-conversion -Wconditional-uninitialized -Wconstant-conversion -Wbool-conversion -Wextra-semi -Wshift-sign-overflow -Wno-missing-field-initializers -Wshadow-all -Wnullable-to-nonnull-conversion";
OTHER_CPLUSPLUSFLAGS = "-Woverloaded-virtual -Wreorder -Wzero-as-null-pointer-constant -Wunused-private-field -Winconsistent-missing-destructor-override -Wall -Wstrict-aliasing -Wuninitialized -Wunused-parameter -Wswitch-enum -Wsign-conversion -Wsign-compare -Wunreachable-code -Wcast-align -Wno-ignored-qualifiers -Wshorten-64-to-32 -Wconversion -Wint-conversion -Wconditional-uninitialized -Wconstant-conversion -Wbool-conversion -Wextra-semi -Wshift-sign-overflow -Wno-missing-field-initializers -Wshadow-all -Wnullable-to-nonnull-conversion";
+ OTHER_LDFLAGS = "-weak_framework Metal -weak_framework MetalKit";
PRODUCT_BUNDLE_IDENTIFIER = com.juce.pluginhost;
PRODUCT_NAME = "AudioPluginHost";
USE_HEADERMAP = NO;
@@ -617,6 +618,7 @@
MTL_HEADER_SEARCH_PATHS = "$(SRCROOT)/../../../../modules/juce_audio_processors/format_types/VST3_SDK $(SRCROOT)/../../JuceLibraryCode $(SRCROOT)/../../../../modules";
OTHER_CFLAGS = "-Wall -Wstrict-aliasing -Wuninitialized -Wunused-parameter -Wswitch-enum -Wsign-conversion -Wsign-compare -Wunreachable-code -Wcast-align -Wno-ignored-qualifiers -Wshorten-64-to-32 -Wconversion -Wint-conversion -Wconditional-uninitialized -Wconstant-conversion -Wbool-conversion -Wextra-semi -Wshift-sign-overflow -Wno-missing-field-initializers -Wshadow-all -Wnullable-to-nonnull-conversion";
OTHER_CPLUSPLUSFLAGS = "-Woverloaded-virtual -Wreorder -Wzero-as-null-pointer-constant -Wunused-private-field -Winconsistent-missing-destructor-override -Wall -Wstrict-aliasing -Wuninitialized -Wunused-parameter -Wswitch-enum -Wsign-conversion -Wsign-compare -Wunreachable-code -Wcast-align -Wno-ignored-qualifiers -Wshorten-64-to-32 -Wconversion -Wint-conversion -Wconditional-uninitialized -Wconstant-conversion -Wbool-conversion -Wextra-semi -Wshift-sign-overflow -Wno-missing-field-initializers -Wshadow-all -Wnullable-to-nonnull-conversion";
+ OTHER_LDFLAGS = "-weak_framework Metal -weak_framework MetalKit";
PRODUCT_BUNDLE_IDENTIFIER = com.juce.pluginhost;
PRODUCT_NAME = "AudioPluginHost";
USE_HEADERMAP = NO;
diff --git a/extras/AudioPluginHost/Builds/VisualStudio2015/AudioPluginHost_App.vcxproj b/extras/AudioPluginHost/Builds/VisualStudio2015/AudioPluginHost_App.vcxproj
index 0da54bedde..f96fb58475 100644
--- a/extras/AudioPluginHost/Builds/VisualStudio2015/AudioPluginHost_App.vcxproj
+++ b/extras/AudioPluginHost/Builds/VisualStudio2015/AudioPluginHost_App.vcxproj
@@ -3056,6 +3056,7 @@
+
diff --git a/extras/AudioPluginHost/Builds/VisualStudio2015/AudioPluginHost_App.vcxproj.filters b/extras/AudioPluginHost/Builds/VisualStudio2015/AudioPluginHost_App.vcxproj.filters
index f2d905b1d6..4359b5c838 100644
--- a/extras/AudioPluginHost/Builds/VisualStudio2015/AudioPluginHost_App.vcxproj.filters
+++ b/extras/AudioPluginHost/Builds/VisualStudio2015/AudioPluginHost_App.vcxproj.filters
@@ -5151,6 +5151,9 @@
JUCE Modules\juce_gui_basics\native\x11
+
+ JUCE Modules\juce_gui_basics\native
+
JUCE Modules\juce_gui_basics\native
diff --git a/extras/AudioPluginHost/Builds/VisualStudio2017/AudioPluginHost_App.vcxproj b/extras/AudioPluginHost/Builds/VisualStudio2017/AudioPluginHost_App.vcxproj
index a7eaa77354..c774055738 100644
--- a/extras/AudioPluginHost/Builds/VisualStudio2017/AudioPluginHost_App.vcxproj
+++ b/extras/AudioPluginHost/Builds/VisualStudio2017/AudioPluginHost_App.vcxproj
@@ -3056,6 +3056,7 @@
+
diff --git a/extras/AudioPluginHost/Builds/VisualStudio2017/AudioPluginHost_App.vcxproj.filters b/extras/AudioPluginHost/Builds/VisualStudio2017/AudioPluginHost_App.vcxproj.filters
index 1c128475c9..fa1a45863f 100644
--- a/extras/AudioPluginHost/Builds/VisualStudio2017/AudioPluginHost_App.vcxproj.filters
+++ b/extras/AudioPluginHost/Builds/VisualStudio2017/AudioPluginHost_App.vcxproj.filters
@@ -5151,6 +5151,9 @@
JUCE Modules\juce_gui_basics\native\x11
+
+ JUCE Modules\juce_gui_basics\native
+
JUCE Modules\juce_gui_basics\native
diff --git a/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj b/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj
index 928e7ffaa2..082fc45023 100644
--- a/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj
+++ b/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj
@@ -3056,6 +3056,7 @@
+
diff --git a/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj.filters b/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj.filters
index 1c3e3efd7f..8a92d923ca 100644
--- a/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj.filters
+++ b/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj.filters
@@ -5151,6 +5151,9 @@
JUCE Modules\juce_gui_basics\native\x11
+
+ JUCE Modules\juce_gui_basics\native
+
JUCE Modules\juce_gui_basics\native
diff --git a/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj b/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj
index be5627fce7..a80f41b775 100644
--- a/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj
+++ b/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj
@@ -3056,6 +3056,7 @@
+
diff --git a/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj.filters b/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj.filters
index 96eead24b2..93146206c1 100644
--- a/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj.filters
+++ b/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj.filters
@@ -5151,6 +5151,9 @@
JUCE Modules\juce_gui_basics\native\x11
+
+ JUCE Modules\juce_gui_basics\native
+
JUCE Modules\juce_gui_basics\native
diff --git a/extras/AudioPluginHost/Builds/iOS/AudioPluginHost.xcodeproj/project.pbxproj b/extras/AudioPluginHost/Builds/iOS/AudioPluginHost.xcodeproj/project.pbxproj
index 5895abd3e2..10f0744788 100644
--- a/extras/AudioPluginHost/Builds/iOS/AudioPluginHost.xcodeproj/project.pbxproj
+++ b/extras/AudioPluginHost/Builds/iOS/AudioPluginHost.xcodeproj/project.pbxproj
@@ -496,6 +496,7 @@
MTL_HEADER_SEARCH_PATHS = "$(SRCROOT)/../../../../modules/juce_audio_processors/format_types/VST3_SDK $(SRCROOT)/../../JuceLibraryCode $(SRCROOT)/../../../../modules";
OTHER_CFLAGS = "-Wall -Wstrict-aliasing -Wuninitialized -Wunused-parameter -Wswitch-enum -Wsign-conversion -Wsign-compare -Wunreachable-code -Wcast-align -Wno-ignored-qualifiers -Wshorten-64-to-32 -Wconversion -Wint-conversion -Wconditional-uninitialized -Wconstant-conversion -Wbool-conversion -Wextra-semi -Wshift-sign-overflow -Wno-missing-field-initializers -Wshadow-all -Wnullable-to-nonnull-conversion";
OTHER_CPLUSPLUSFLAGS = "-Woverloaded-virtual -Wreorder -Wzero-as-null-pointer-constant -Wunused-private-field -Winconsistent-missing-destructor-override -Wall -Wstrict-aliasing -Wuninitialized -Wunused-parameter -Wswitch-enum -Wsign-conversion -Wsign-compare -Wunreachable-code -Wcast-align -Wno-ignored-qualifiers -Wshorten-64-to-32 -Wconversion -Wint-conversion -Wconditional-uninitialized -Wconstant-conversion -Wbool-conversion -Wextra-semi -Wshift-sign-overflow -Wno-missing-field-initializers -Wshadow-all -Wnullable-to-nonnull-conversion";
+ OTHER_LDFLAGS = "-weak_framework Metal -weak_framework MetalKit";
PRODUCT_BUNDLE_IDENTIFIER = com.juce.pluginhost;
PRODUCT_NAME = "Plugin Host";
USE_HEADERMAP = NO;
@@ -626,6 +627,7 @@
MTL_HEADER_SEARCH_PATHS = "$(SRCROOT)/../../../../modules/juce_audio_processors/format_types/VST3_SDK $(SRCROOT)/../../JuceLibraryCode $(SRCROOT)/../../../../modules";
OTHER_CFLAGS = "-Wall -Wstrict-aliasing -Wuninitialized -Wunused-parameter -Wswitch-enum -Wsign-conversion -Wsign-compare -Wunreachable-code -Wcast-align -Wno-ignored-qualifiers -Wshorten-64-to-32 -Wconversion -Wint-conversion -Wconditional-uninitialized -Wconstant-conversion -Wbool-conversion -Wextra-semi -Wshift-sign-overflow -Wno-missing-field-initializers -Wshadow-all -Wnullable-to-nonnull-conversion";
OTHER_CPLUSPLUSFLAGS = "-Woverloaded-virtual -Wreorder -Wzero-as-null-pointer-constant -Wunused-private-field -Winconsistent-missing-destructor-override -Wall -Wstrict-aliasing -Wuninitialized -Wunused-parameter -Wswitch-enum -Wsign-conversion -Wsign-compare -Wunreachable-code -Wcast-align -Wno-ignored-qualifiers -Wshorten-64-to-32 -Wconversion -Wint-conversion -Wconditional-uninitialized -Wconstant-conversion -Wbool-conversion -Wextra-semi -Wshift-sign-overflow -Wno-missing-field-initializers -Wshadow-all -Wnullable-to-nonnull-conversion";
+ OTHER_LDFLAGS = "-weak_framework Metal -weak_framework MetalKit";
PRODUCT_BUNDLE_IDENTIFIER = com.juce.pluginhost;
PRODUCT_NAME = "Plugin Host";
USE_HEADERMAP = NO;
diff --git a/extras/NetworkGraphicsDemo/Builds/Android/app/CMakeLists.txt b/extras/NetworkGraphicsDemo/Builds/Android/app/CMakeLists.txt
index ba9a69d44e..8e220edddc 100644
--- a/extras/NetworkGraphicsDemo/Builds/Android/app/CMakeLists.txt
+++ b/extras/NetworkGraphicsDemo/Builds/Android/app/CMakeLists.txt
@@ -1402,6 +1402,7 @@ add_library( ${BINARY_NAME}
"../../../../../modules/juce_gui_basics/native/juce_ios_Windowing.mm"
"../../../../../modules/juce_gui_basics/native/juce_linux_FileChooser.cpp"
"../../../../../modules/juce_gui_basics/native/juce_linux_Windowing.cpp"
+ "../../../../../modules/juce_gui_basics/native/juce_mac_CGMetalLayerRenderer.h"
"../../../../../modules/juce_gui_basics/native/juce_mac_FileChooser.mm"
"../../../../../modules/juce_gui_basics/native/juce_mac_MainMenu.mm"
"../../../../../modules/juce_gui_basics/native/juce_mac_MouseCursor.mm"
@@ -2999,6 +3000,7 @@ set_source_files_properties("../../../../../modules/juce_gui_basics/native/juce_
set_source_files_properties("../../../../../modules/juce_gui_basics/native/juce_ios_Windowing.mm" PROPERTIES HEADER_FILE_ONLY TRUE)
set_source_files_properties("../../../../../modules/juce_gui_basics/native/juce_linux_FileChooser.cpp" PROPERTIES HEADER_FILE_ONLY TRUE)
set_source_files_properties("../../../../../modules/juce_gui_basics/native/juce_linux_Windowing.cpp" PROPERTIES HEADER_FILE_ONLY TRUE)
+set_source_files_properties("../../../../../modules/juce_gui_basics/native/juce_mac_CGMetalLayerRenderer.h" PROPERTIES HEADER_FILE_ONLY TRUE)
set_source_files_properties("../../../../../modules/juce_gui_basics/native/juce_mac_FileChooser.mm" PROPERTIES HEADER_FILE_ONLY TRUE)
set_source_files_properties("../../../../../modules/juce_gui_basics/native/juce_mac_MainMenu.mm" PROPERTIES HEADER_FILE_ONLY TRUE)
set_source_files_properties("../../../../../modules/juce_gui_basics/native/juce_mac_MouseCursor.mm" PROPERTIES HEADER_FILE_ONLY TRUE)
diff --git a/extras/NetworkGraphicsDemo/Builds/MacOSX/NetworkGraphicsDemo.xcodeproj/project.pbxproj b/extras/NetworkGraphicsDemo/Builds/MacOSX/NetworkGraphicsDemo.xcodeproj/project.pbxproj
index d9d37e82ef..c366e31cf9 100644
--- a/extras/NetworkGraphicsDemo/Builds/MacOSX/NetworkGraphicsDemo.xcodeproj/project.pbxproj
+++ b/extras/NetworkGraphicsDemo/Builds/MacOSX/NetworkGraphicsDemo.xcodeproj/project.pbxproj
@@ -406,6 +406,7 @@
LLVM_LTO = YES;
MACOSX_DEPLOYMENT_TARGET = 10.9;
MTL_HEADER_SEARCH_PATHS = "$(SRCROOT)/../../JuceLibraryCode $(SRCROOT)/../../../../modules";
+ OTHER_LDFLAGS = "-weak_framework Metal -weak_framework MetalKit";
PRODUCT_BUNDLE_IDENTIFIER = com.juce.NetworkGraphicsDemo;
PRODUCT_NAME = "JUCE Network Graphics Demo";
USE_HEADERMAP = NO;
@@ -569,6 +570,7 @@
INSTALL_PATH = "$(HOME)/Applications";
MACOSX_DEPLOYMENT_TARGET = 10.9;
MTL_HEADER_SEARCH_PATHS = "$(SRCROOT)/../../JuceLibraryCode $(SRCROOT)/../../../../modules";
+ OTHER_LDFLAGS = "-weak_framework Metal -weak_framework MetalKit";
PRODUCT_BUNDLE_IDENTIFIER = com.juce.NetworkGraphicsDemo;
PRODUCT_NAME = "JUCE Network Graphics Demo";
USE_HEADERMAP = NO;
diff --git a/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj b/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj
index 462b5dc005..d7f49167cb 100644
--- a/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj
+++ b/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj
@@ -2919,6 +2919,7 @@
+
diff --git a/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj.filters b/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj.filters
index 8b73614889..b18b5bc371 100644
--- a/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj.filters
+++ b/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj.filters
@@ -4878,6 +4878,9 @@
JUCE Modules\juce_gui_basics\native\x11
+
+ JUCE Modules\juce_gui_basics\native
+
JUCE Modules\juce_gui_basics\native
diff --git a/extras/NetworkGraphicsDemo/Builds/iOS/NetworkGraphicsDemo.xcodeproj/project.pbxproj b/extras/NetworkGraphicsDemo/Builds/iOS/NetworkGraphicsDemo.xcodeproj/project.pbxproj
index 4edd34810e..008d22fa09 100644
--- a/extras/NetworkGraphicsDemo/Builds/iOS/NetworkGraphicsDemo.xcodeproj/project.pbxproj
+++ b/extras/NetworkGraphicsDemo/Builds/iOS/NetworkGraphicsDemo.xcodeproj/project.pbxproj
@@ -418,6 +418,7 @@
INSTALL_PATH = "$(HOME)/Applications";
LLVM_LTO = YES;
MTL_HEADER_SEARCH_PATHS = "$(SRCROOT)/../../JuceLibraryCode $(SRCROOT)/../../../../modules";
+ OTHER_LDFLAGS = "-weak_framework Metal -weak_framework MetalKit";
PRODUCT_BUNDLE_IDENTIFIER = com.juce.NetworkGraphicsDemo;
PRODUCT_NAME = "JUCE Network Graphics Demo";
USE_HEADERMAP = NO;
@@ -584,6 +585,7 @@
INFOPLIST_PREPROCESS = NO;
INSTALL_PATH = "$(HOME)/Applications";
MTL_HEADER_SEARCH_PATHS = "$(SRCROOT)/../../JuceLibraryCode $(SRCROOT)/../../../../modules";
+ OTHER_LDFLAGS = "-weak_framework Metal -weak_framework MetalKit";
PRODUCT_BUNDLE_IDENTIFIER = com.juce.NetworkGraphicsDemo;
PRODUCT_NAME = "JUCE Network Graphics Demo";
USE_HEADERMAP = NO;
diff --git a/extras/Projucer/Builds/MacOSX/Projucer.xcodeproj/project.pbxproj b/extras/Projucer/Builds/MacOSX/Projucer.xcodeproj/project.pbxproj
index 0f3d07cc02..e349790d8e 100644
--- a/extras/Projucer/Builds/MacOSX/Projucer.xcodeproj/project.pbxproj
+++ b/extras/Projucer/Builds/MacOSX/Projucer.xcodeproj/project.pbxproj
@@ -1172,6 +1172,7 @@
MTL_HEADER_SEARCH_PATHS = "$(SRCROOT)/../../JuceLibraryCode $(SRCROOT)/../../../Build $(SRCROOT)/../../../../modules";
OTHER_CFLAGS = "-Wall -Wstrict-aliasing -Wuninitialized -Wunused-parameter -Wswitch-enum -Wsign-conversion -Wsign-compare -Wunreachable-code -Wcast-align -Wno-ignored-qualifiers -Wshorten-64-to-32 -Wconversion -Wint-conversion -Wconditional-uninitialized -Wconstant-conversion -Wbool-conversion -Wextra-semi -Wshift-sign-overflow -Wno-missing-field-initializers -Wshadow-all -Wnullable-to-nonnull-conversion";
OTHER_CPLUSPLUSFLAGS = "-Woverloaded-virtual -Wreorder -Wzero-as-null-pointer-constant -Wunused-private-field -Winconsistent-missing-destructor-override -Wall -Wstrict-aliasing -Wuninitialized -Wunused-parameter -Wswitch-enum -Wsign-conversion -Wsign-compare -Wunreachable-code -Wcast-align -Wno-ignored-qualifiers -Wshorten-64-to-32 -Wconversion -Wint-conversion -Wconditional-uninitialized -Wconstant-conversion -Wbool-conversion -Wextra-semi -Wshift-sign-overflow -Wno-missing-field-initializers -Wshadow-all -Wnullable-to-nonnull-conversion";
+ OTHER_LDFLAGS = "-weak_framework Metal -weak_framework MetalKit";
PRODUCT_BUNDLE_IDENTIFIER = com.juce.theprojucer;
PRODUCT_NAME = "Projucer";
USE_HEADERMAP = NO;
@@ -1241,6 +1242,7 @@
MTL_HEADER_SEARCH_PATHS = "$(SRCROOT)/../../JuceLibraryCode $(SRCROOT)/../../../Build $(SRCROOT)/../../../../modules";
OTHER_CFLAGS = "-Wall -Wstrict-aliasing -Wuninitialized -Wunused-parameter -Wswitch-enum -Wsign-conversion -Wsign-compare -Wunreachable-code -Wcast-align -Wno-ignored-qualifiers -Wshorten-64-to-32 -Wconversion -Wint-conversion -Wconditional-uninitialized -Wconstant-conversion -Wbool-conversion -Wextra-semi -Wshift-sign-overflow -Wno-missing-field-initializers -Wshadow-all -Wnullable-to-nonnull-conversion";
OTHER_CPLUSPLUSFLAGS = "-Woverloaded-virtual -Wreorder -Wzero-as-null-pointer-constant -Wunused-private-field -Winconsistent-missing-destructor-override -Wall -Wstrict-aliasing -Wuninitialized -Wunused-parameter -Wswitch-enum -Wsign-conversion -Wsign-compare -Wunreachable-code -Wcast-align -Wno-ignored-qualifiers -Wshorten-64-to-32 -Wconversion -Wint-conversion -Wconditional-uninitialized -Wconstant-conversion -Wbool-conversion -Wextra-semi -Wshift-sign-overflow -Wno-missing-field-initializers -Wshadow-all -Wnullable-to-nonnull-conversion";
+ OTHER_LDFLAGS = "-weak_framework Metal -weak_framework MetalKit";
PRODUCT_BUNDLE_IDENTIFIER = com.juce.theprojucer;
PRODUCT_NAME = "Projucer";
USE_HEADERMAP = NO;
diff --git a/extras/Projucer/Builds/VisualStudio2015/Projucer_App.vcxproj b/extras/Projucer/Builds/VisualStudio2015/Projucer_App.vcxproj
index e8f9dccb2b..e63ba2e8f1 100644
--- a/extras/Projucer/Builds/VisualStudio2015/Projucer_App.vcxproj
+++ b/extras/Projucer/Builds/VisualStudio2015/Projucer_App.vcxproj
@@ -2058,6 +2058,7 @@
+
diff --git a/extras/Projucer/Builds/VisualStudio2015/Projucer_App.vcxproj.filters b/extras/Projucer/Builds/VisualStudio2015/Projucer_App.vcxproj.filters
index 15a5ecd45a..ea2ebb5d59 100644
--- a/extras/Projucer/Builds/VisualStudio2015/Projucer_App.vcxproj.filters
+++ b/extras/Projucer/Builds/VisualStudio2015/Projucer_App.vcxproj.filters
@@ -3537,6 +3537,9 @@
JUCE Modules\juce_gui_basics\native\x11
+
+ JUCE Modules\juce_gui_basics\native
+
JUCE Modules\juce_gui_basics\native
diff --git a/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj b/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj
index 468c60158a..d4b497d1b7 100644
--- a/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj
+++ b/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj
@@ -2058,6 +2058,7 @@
+
diff --git a/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj.filters b/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj.filters
index 2be8d1126e..66ed6940c7 100644
--- a/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj.filters
+++ b/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj.filters
@@ -3537,6 +3537,9 @@
JUCE Modules\juce_gui_basics\native\x11
+
+ JUCE Modules\juce_gui_basics\native
+
JUCE Modules\juce_gui_basics\native
diff --git a/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj b/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj
index 4753e8f76e..ae8ccd4d8c 100644
--- a/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj
+++ b/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj
@@ -2058,6 +2058,7 @@
+
diff --git a/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj.filters b/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj.filters
index c92b2abf45..05cd0c4f60 100644
--- a/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj.filters
+++ b/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj.filters
@@ -3537,6 +3537,9 @@
JUCE Modules\juce_gui_basics\native\x11
+
+ JUCE Modules\juce_gui_basics\native
+
JUCE Modules\juce_gui_basics\native
diff --git a/extras/Projucer/Builds/VisualStudio2022/Projucer_App.vcxproj b/extras/Projucer/Builds/VisualStudio2022/Projucer_App.vcxproj
index 11fd5e5369..a49bcd0b7f 100644
--- a/extras/Projucer/Builds/VisualStudio2022/Projucer_App.vcxproj
+++ b/extras/Projucer/Builds/VisualStudio2022/Projucer_App.vcxproj
@@ -2058,6 +2058,7 @@
+
diff --git a/extras/Projucer/Builds/VisualStudio2022/Projucer_App.vcxproj.filters b/extras/Projucer/Builds/VisualStudio2022/Projucer_App.vcxproj.filters
index bc6960b02d..a554318303 100644
--- a/extras/Projucer/Builds/VisualStudio2022/Projucer_App.vcxproj.filters
+++ b/extras/Projucer/Builds/VisualStudio2022/Projucer_App.vcxproj.filters
@@ -3537,6 +3537,9 @@
JUCE Modules\juce_gui_basics\native\x11
+
+ JUCE Modules\juce_gui_basics\native
+
JUCE Modules\juce_gui_basics\native
diff --git a/extras/UnitTestRunner/Builds/MacOSX/UnitTestRunner.xcodeproj/project.pbxproj b/extras/UnitTestRunner/Builds/MacOSX/UnitTestRunner.xcodeproj/project.pbxproj
index d163bcd1ad..7db1976336 100644
--- a/extras/UnitTestRunner/Builds/MacOSX/UnitTestRunner.xcodeproj/project.pbxproj
+++ b/extras/UnitTestRunner/Builds/MacOSX/UnitTestRunner.xcodeproj/project.pbxproj
@@ -453,6 +453,7 @@
MTL_HEADER_SEARCH_PATHS = "$(SRCROOT)/../../../../modules/juce_audio_processors/format_types/VST3_SDK $(SRCROOT)/../../JuceLibraryCode $(SRCROOT)/../../../../modules";
OTHER_CFLAGS = "-Wall -Wstrict-aliasing -Wuninitialized -Wunused-parameter -Wswitch-enum -Wsign-conversion -Wsign-compare -Wunreachable-code -Wcast-align -Wno-ignored-qualifiers -Wshorten-64-to-32 -Wconversion -Wint-conversion -Wconditional-uninitialized -Wconstant-conversion -Wbool-conversion -Wextra-semi -Wshift-sign-overflow -Wno-missing-field-initializers -Wshadow-all -Wnullable-to-nonnull-conversion";
OTHER_CPLUSPLUSFLAGS = "-Woverloaded-virtual -Wreorder -Wzero-as-null-pointer-constant -Wunused-private-field -Winconsistent-missing-destructor-override -Wall -Wstrict-aliasing -Wuninitialized -Wunused-parameter -Wswitch-enum -Wsign-conversion -Wsign-compare -Wunreachable-code -Wcast-align -Wno-ignored-qualifiers -Wshorten-64-to-32 -Wconversion -Wint-conversion -Wconditional-uninitialized -Wconstant-conversion -Wbool-conversion -Wextra-semi -Wshift-sign-overflow -Wno-missing-field-initializers -Wshadow-all -Wnullable-to-nonnull-conversion";
+ OTHER_LDFLAGS = "-weak_framework Metal -weak_framework MetalKit";
PRODUCT_BUNDLE_IDENTIFIER = com.juce.UnitTestRunner;
PRODUCT_NAME = "UnitTestRunner";
USE_HEADERMAP = NO;
@@ -575,6 +576,7 @@
MTL_HEADER_SEARCH_PATHS = "$(SRCROOT)/../../../../modules/juce_audio_processors/format_types/VST3_SDK $(SRCROOT)/../../JuceLibraryCode $(SRCROOT)/../../../../modules";
OTHER_CFLAGS = "-Wall -Wstrict-aliasing -Wuninitialized -Wunused-parameter -Wswitch-enum -Wsign-conversion -Wsign-compare -Wunreachable-code -Wcast-align -Wno-ignored-qualifiers -Wshorten-64-to-32 -Wconversion -Wint-conversion -Wconditional-uninitialized -Wconstant-conversion -Wbool-conversion -Wextra-semi -Wshift-sign-overflow -Wno-missing-field-initializers -Wshadow-all -Wnullable-to-nonnull-conversion";
OTHER_CPLUSPLUSFLAGS = "-Woverloaded-virtual -Wreorder -Wzero-as-null-pointer-constant -Wunused-private-field -Winconsistent-missing-destructor-override -Wall -Wstrict-aliasing -Wuninitialized -Wunused-parameter -Wswitch-enum -Wsign-conversion -Wsign-compare -Wunreachable-code -Wcast-align -Wno-ignored-qualifiers -Wshorten-64-to-32 -Wconversion -Wint-conversion -Wconditional-uninitialized -Wconstant-conversion -Wbool-conversion -Wextra-semi -Wshift-sign-overflow -Wno-missing-field-initializers -Wshadow-all -Wnullable-to-nonnull-conversion";
+ OTHER_LDFLAGS = "-weak_framework Metal -weak_framework MetalKit";
PRODUCT_BUNDLE_IDENTIFIER = com.juce.UnitTestRunner;
PRODUCT_NAME = "UnitTestRunner";
USE_HEADERMAP = NO;
diff --git a/extras/UnitTestRunner/Builds/VisualStudio2017/UnitTestRunner_ConsoleApp.vcxproj b/extras/UnitTestRunner/Builds/VisualStudio2017/UnitTestRunner_ConsoleApp.vcxproj
index d592dd4b30..f26e348a14 100644
--- a/extras/UnitTestRunner/Builds/VisualStudio2017/UnitTestRunner_ConsoleApp.vcxproj
+++ b/extras/UnitTestRunner/Builds/VisualStudio2017/UnitTestRunner_ConsoleApp.vcxproj
@@ -3124,6 +3124,7 @@
+
diff --git a/extras/UnitTestRunner/Builds/VisualStudio2017/UnitTestRunner_ConsoleApp.vcxproj.filters b/extras/UnitTestRunner/Builds/VisualStudio2017/UnitTestRunner_ConsoleApp.vcxproj.filters
index 59074ad2e3..3ece8e4e8f 100644
--- a/extras/UnitTestRunner/Builds/VisualStudio2017/UnitTestRunner_ConsoleApp.vcxproj.filters
+++ b/extras/UnitTestRunner/Builds/VisualStudio2017/UnitTestRunner_ConsoleApp.vcxproj.filters
@@ -5247,6 +5247,9 @@
JUCE Modules\juce_gui_basics\native\x11
+
+ JUCE Modules\juce_gui_basics\native
+
JUCE Modules\juce_gui_basics\native
diff --git a/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj b/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj
index 87f8d2c8f1..e651cd4558 100644
--- a/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj
+++ b/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj
@@ -3124,6 +3124,7 @@
+
diff --git a/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj.filters b/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj.filters
index f4cc23b0b2..8d6d3f95a7 100644
--- a/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj.filters
+++ b/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj.filters
@@ -5247,6 +5247,9 @@
JUCE Modules\juce_gui_basics\native\x11
+
+ JUCE Modules\juce_gui_basics\native
+
JUCE Modules\juce_gui_basics\native
diff --git a/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj b/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj
index 58503efce2..4ec1ea6e95 100644
--- a/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj
+++ b/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj
@@ -3124,6 +3124,7 @@
+
diff --git a/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj.filters b/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj.filters
index d965ab0abd..3791df5098 100644
--- a/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj.filters
+++ b/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj.filters
@@ -5247,6 +5247,9 @@
JUCE Modules\juce_gui_basics\native\x11
+
+ JUCE Modules\juce_gui_basics\native
+
JUCE Modules\juce_gui_basics\native
diff --git a/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_StaticLibrary.vcxproj b/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_StaticLibrary.vcxproj
index 1440270e7e..1e9bbe6e3b 100644
--- a/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_StaticLibrary.vcxproj
+++ b/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_StaticLibrary.vcxproj
@@ -2895,6 +2895,7 @@
+
diff --git a/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_StaticLibrary.vcxproj.filters b/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_StaticLibrary.vcxproj.filters
index 1d5cfaaa27..2bb8f659e0 100644
--- a/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_StaticLibrary.vcxproj.filters
+++ b/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_StaticLibrary.vcxproj.filters
@@ -4845,6 +4845,9 @@
JUCE Modules\juce_gui_basics\native\x11
+
+ JUCE Modules\juce_gui_basics\native
+
JUCE Modules\juce_gui_basics\native
diff --git a/modules/juce_core/native/juce_mac_ObjCHelpers.h b/modules/juce_core/native/juce_mac_ObjCHelpers.h
index e91a0a02c2..cb29aca747 100644
--- a/modules/juce_core/native/juce_mac_ObjCHelpers.h
+++ b/modules/juce_core/native/juce_mac_ObjCHelpers.h
@@ -306,6 +306,9 @@ public:
bool operator== (const ObjCObjectHandle& other) const { return item == other.item; }
bool operator!= (const ObjCObjectHandle& other) const { return ! (*this == other); }
+ bool operator== (std::nullptr_t) const { return item == nullptr; }
+ bool operator!= (std::nullptr_t) const { return ! (*this == nullptr); }
+
private:
void swap (ObjCObjectHandle& other) noexcept { std::swap (other.item, item); }
diff --git a/modules/juce_core/system/juce_TargetPlatform.h b/modules/juce_core/system/juce_TargetPlatform.h
index 3e14e9b741..aca2e65676 100644
--- a/modules/juce_core/system/juce_TargetPlatform.h
+++ b/modules/juce_core/system/juce_TargetPlatform.h
@@ -144,8 +144,8 @@
#endif
#if JUCE_MAC
- #if ! defined (MAC_OS_X_VERSION_10_11)
- #error "The 10.11 SDK (Xcode 7.3.1+) is required to build JUCE apps. You can create apps that run on macOS 10.7+ by changing the deployment target."
+ #if ! defined (MAC_OS_X_VERSION_10_14)
+ #error "The 10.14 SDK (Xcode 10.1+) is required to build JUCE apps. You can create apps that run on macOS 10.7+ by changing the deployment target."
#elif MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7
#error "Building for OSX 10.6 is no longer supported!"
#endif
diff --git a/modules/juce_gui_basics/juce_gui_basics.cpp b/modules/juce_gui_basics/juce_gui_basics.cpp
index 217402c4ef..d9697568b7 100644
--- a/modules/juce_gui_basics/juce_gui_basics.cpp
+++ b/modules/juce_gui_basics/juce_gui_basics.cpp
@@ -42,6 +42,7 @@
#if JUCE_MAC
#import
#import
+ #import
#if JUCE_SUPPORT_CARBON
#import // still needed for SetSystemUIMode()
@@ -52,6 +53,7 @@
#import
#endif
+ #import
#import
//==============================================================================
diff --git a/modules/juce_gui_basics/juce_gui_basics.h b/modules/juce_gui_basics/juce_gui_basics.h
index 9d96b9580a..57d10f3ee1 100644
--- a/modules/juce_gui_basics/juce_gui_basics.h
+++ b/modules/juce_gui_basics/juce_gui_basics.h
@@ -36,8 +36,10 @@
minimumCppStandard: 14
dependencies: juce_graphics juce_data_structures
- OSXFrameworks: Cocoa Carbon QuartzCore
- iOSFrameworks: UIKit CoreServices
+ OSXFrameworks: Carbon Cocoa QuartzCore
+ WeakOSXFrameworks: Metal MetalKit
+ iOSFrameworks: CoreServices UIKit
+ WeakiOSFrameworks: Metal MetalKit
END_JUCE_MODULE_DECLARATION
diff --git a/modules/juce_gui_basics/native/juce_ios_UIViewComponentPeer.mm b/modules/juce_gui_basics/native/juce_ios_UIViewComponentPeer.mm
index b3e27c1233..765ecebe24 100644
--- a/modules/juce_gui_basics/native/juce_ios_UIViewComponentPeer.mm
+++ b/modules/juce_gui_basics/native/juce_ios_UIViewComponentPeer.mm
@@ -16,6 +16,13 @@
==============================================================================
*/
+#include "juce_mac_CGMetalLayerRenderer.h"
+
+#if TARGET_OS_SIMULATOR && JUCE_COREGRAPHICS_RENDER_WITH_MULTIPLE_PAINT_CALLS
+ #warning JUCE_COREGRAPHICS_RENDER_WITH_MULTIPLE_PAINT_CALLS uses parts of the Metal API that are currently unsupported in the simulator - falling back to JUCE_COREGRAPHICS_RENDER_WITH_MULTIPLE_PAINT_CALLS=0
+ #undef JUCE_COREGRAPHICS_RENDER_WITH_MULTIPLE_PAINT_CALLS
+#endif
+
#if defined (__IPHONE_13_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0
#define JUCE_HAS_IOS_POINTER_SUPPORT 1
#else
@@ -125,6 +132,8 @@ struct CADisplayLinkDeleter
- (JuceUIView*) initWithOwner: (UIViewComponentPeer*) owner withFrame: (CGRect) frame;
- (void) dealloc;
++ (Class) layerClass;
+
- (void) displayLinkCallback: (CADisplayLink*) dl;
- (void) drawRect: (CGRect) r;
@@ -197,8 +206,8 @@ struct UIViewPeerControllerReceiver
};
class UIViewComponentPeer : public ComponentPeer,
- public FocusChangeListener,
- public UIViewPeerControllerReceiver
+ private FocusChangeListener,
+ private UIViewPeerControllerReceiver
{
public:
UIViewComponentPeer (Component&, int windowStyleFlags, UIView* viewToAttachTo);
@@ -239,6 +248,7 @@ public:
void displayLinkCallback();
void drawRect (CGRect);
+ void drawRectWithContext (CGContextRef, CGRect);
bool canBecomeKeyWindow();
//==============================================================================
@@ -315,6 +325,7 @@ private:
}
};
+ std::unique_ptr metalRenderer;
RectangleList deferredRepaints;
//==============================================================================
@@ -514,6 +525,16 @@ MultiTouchMapper UIViewComponentPeer::currentTouches;
}
//==============================================================================
++ (Class) layerClass
+{
+ #if JUCE_COREGRAPHICS_RENDER_WITH_MULTIPLE_PAINT_CALLS
+ if (@available (iOS 12, *))
+ return [CAMetalLayer class];
+ #endif
+
+ return [CALayer class];
+}
+
- (void) displayLinkCallback: (CADisplayLink*) dl
{
if (owner != nullptr)
@@ -689,6 +710,11 @@ UIViewComponentPeer::UIViewComponentPeer (Component& comp, int windowStyleFlags,
view.opaque = component.isOpaque();
view.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent: 0];
+ #if JUCE_COREGRAPHICS_RENDER_WITH_MULTIPLE_PAINT_CALLS
+ if (@available (iOS 12, *))
+ metalRenderer = std::make_unique ((CAMetalLayer*) view.layer, comp);
+ #endif
+
if ((windowStyleFlags & ComponentPeer::windowRequiresSynchronousCoreGraphicsRendering) == 0)
[[view layer] setDrawsAsynchronously: YES];
@@ -1170,10 +1196,40 @@ void UIViewComponentPeer::globalFocusChanged (Component*)
//==============================================================================
void UIViewComponentPeer::displayLinkCallback()
{
- for (const auto& r : deferredRepaints)
- [view setNeedsDisplayInRect: convertToCGRect (r)];
+ if (deferredRepaints.isEmpty())
+ return;
+
+ auto dispatchRectangles = [this] ()
+ {
+ // We shouldn't need this preprocessor guard, but when running in the simulator
+ // CAMetalLayer is flagged as requiring iOS 13
+ #if JUCE_COREGRAPHICS_RENDER_WITH_MULTIPLE_PAINT_CALLS
+ if (metalRenderer != nullptr)
+ {
+ if (@available (iOS 12, *))
+ {
+ return metalRenderer->drawRectangleList ((CAMetalLayer*) view.layer,
+ (float) view.contentScaleFactor,
+ view.frame,
+ component,
+ [this] (CGContextRef ctx, CGRect r) { drawRectWithContext (ctx, r); },
+ deferredRepaints);
+ }
+
+ // The creation of metalRenderer should already be guarded with @available (iOS 12, *).
+ jassertfalse;
+ return false;
+ }
+ #endif
- deferredRepaints.clear();
+ for (const auto& r : deferredRepaints)
+ [view setNeedsDisplayInRect: convertToCGRect (r)];
+
+ return true;
+ };
+
+ if (dispatchRectangles())
+ deferredRepaints.clear();
}
//==============================================================================
@@ -1182,8 +1238,11 @@ void UIViewComponentPeer::drawRect (CGRect r)
if (r.size.width < 1.0f || r.size.height < 1.0f)
return;
- CGContextRef cg = UIGraphicsGetCurrentContext();
+ drawRectWithContext (UIGraphicsGetCurrentContext(), r);
+}
+void UIViewComponentPeer::drawRectWithContext (CGContextRef cg, CGRect)
+{
if (! component.isOpaque())
CGContextClearRect (cg, CGContextGetClipBoundingBox (cg));
diff --git a/modules/juce_gui_basics/native/juce_mac_CGMetalLayerRenderer.h b/modules/juce_gui_basics/native/juce_mac_CGMetalLayerRenderer.h
new file mode 100644
index 0000000000..10f5e4d42e
--- /dev/null
+++ b/modules/juce_gui_basics/native/juce_mac_CGMetalLayerRenderer.h
@@ -0,0 +1,338 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE 7 technical preview.
+ Copyright (c) 2022 - Raw Material Software Limited
+
+ You may use this code under the terms of the GPL v3
+ (see www.gnu.org/licenses).
+
+ For the technical preview this file cannot be licensed commercially.
+
+ JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
+ EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
+ DISCLAIMED.
+
+ ==============================================================================
+*/
+
+// The CoreGraphicsMetalLayerRenderer requires macOS 10.14 and iOS 12.
+JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wunguarded-availability", "-Wunguarded-availability-new")
+
+namespace juce
+{
+
+//==============================================================================
+class CoreGraphicsMetalLayerRenderer
+{
+public:
+ //==============================================================================
+ CoreGraphicsMetalLayerRenderer (CAMetalLayer* layer, const Component& comp)
+ {
+ device.reset (MTLCreateSystemDefaultDevice());
+
+ layer.device = device.get();
+ layer.framebufferOnly = NO;
+ layer.pixelFormat = MTLPixelFormatBGRA8Unorm_sRGB;
+ layer.opaque = comp.isOpaque();
+ layer.allowsNextDrawableTimeout = NO;
+
+ commandQueue.reset ([device.get() newCommandQueue]);
+
+ memoryBlitEvent.reset ([device.get() newSharedEvent]);
+ }
+
+ ~CoreGraphicsMetalLayerRenderer()
+ {
+ stopGpuCommandSubmission = true;
+ [memoryBlitCommandBuffer.get() waitUntilCompleted];
+ }
+
+ template
+ bool drawRectangleList (CAMetalLayer* layer,
+ float scaleFactor,
+ CGRect viewFrame,
+ const Component& comp,
+ Callback&& drawRectWithContext,
+ const RectangleList& dirtyRegions)
+ {
+ if (resources != nullptr)
+ {
+ // If we haven't finished blitting the CPU texture to the GPU then
+ // report that we have been unable to draw anything.
+ if (memoryBlitEvent.get().signaledValue != memoryBlitCounter + 1)
+ return false;
+
+ ++memoryBlitCounter;
+ }
+
+ layer.contentsScale = scaleFactor;
+ const auto drawableSizeTansform = CGAffineTransformMakeScale (layer.contentsScale,
+ layer.contentsScale);
+ const auto transformedFrameSize = CGSizeApplyAffineTransform (viewFrame.size, drawableSizeTansform);
+
+ const auto componentHeight = comp.getHeight();
+
+ if (! CGSizeEqualToSize (layer.drawableSize, transformedFrameSize))
+ {
+ layer.drawableSize = transformedFrameSize;
+ resources = std::make_unique (device.get(), layer, componentHeight);
+ }
+
+ auto gpuTexture = resources->getGpuTexture();
+
+ if (gpuTexture == nullptr)
+ {
+ jassertfalse;
+ return false;
+ }
+
+ auto cgContext = resources->getCGContext();
+
+ for (auto rect : dirtyRegions)
+ {
+ const auto cgRect = convertToCGRect (rect);
+
+ CGContextSaveGState (cgContext);
+
+ CGContextClipToRect (cgContext, cgRect);
+ drawRectWithContext (cgContext, cgRect);
+
+ CGContextRestoreGState (cgContext);
+ }
+
+ auto cpuTexture = resources->getCpuTexture();
+
+ memoryBlitCommandBuffer.reset ([commandQueue.get() commandBuffer]);
+
+ // Command buffers are usually considered temporary, and are automatically released by
+ // the operating system when the rendering pipeline is finsihed. However, we want to keep
+ // this one alive so that we can wait for pipeline completion in the destructor.
+ [memoryBlitCommandBuffer.get() retain];
+
+ auto blitCommandEncoder = [memoryBlitCommandBuffer.get() blitCommandEncoder];
+ [blitCommandEncoder copyFromTexture: cpuTexture
+ sourceSlice: 0
+ sourceLevel: 0
+ sourceOrigin: MTLOrigin{}
+ sourceSize: MTLSize { cpuTexture.width, cpuTexture.height, 1 }
+ toTexture: gpuTexture
+ destinationSlice: 0
+ destinationLevel: 0
+ destinationOrigin: MTLOrigin{}];
+ [blitCommandEncoder endEncoding];
+
+ // Signal that the GPU has finished using the CPU texture
+ [memoryBlitCommandBuffer.get() encodeSignalEvent: memoryBlitEvent.get()
+ value: memoryBlitCounter + 1];
+
+ [memoryBlitCommandBuffer.get() addScheduledHandler: ^(id)
+ {
+ // We're on a Metal thread, so we can make a blocking nextDrawable call
+ // without stalling the message thread.
+
+ // Check if we can do an early exit.
+ if (stopGpuCommandSubmission)
+ return;
+
+ @autoreleasepool
+ {
+ id drawable = [layer nextDrawable];
+
+ id presentationCommandBuffer = [commandQueue.get() commandBuffer];
+
+ auto presentationBlitCommandEncoder = [presentationCommandBuffer blitCommandEncoder];
+ [presentationBlitCommandEncoder copyFromTexture: gpuTexture
+ sourceSlice: 0
+ sourceLevel: 0
+ sourceOrigin: MTLOrigin{}
+ sourceSize: MTLSize { gpuTexture.width, gpuTexture.height, 1 }
+ toTexture: drawable.texture
+ destinationSlice: 0
+ destinationLevel: 0
+ destinationOrigin: MTLOrigin{}];
+ [presentationBlitCommandEncoder endEncoding];
+
+ [presentationCommandBuffer addScheduledHandler: ^(id)
+ {
+ [drawable present];
+ }];
+
+ [presentationCommandBuffer commit];
+ }
+ }];
+
+ [memoryBlitCommandBuffer.get() commit];
+
+ return true;
+ }
+
+private:
+ //==============================================================================
+ static auto alignTo (size_t n, size_t alignment)
+ {
+ return ((n + alignment - 1) / alignment) * alignment;
+ }
+
+ //==============================================================================
+ struct TextureDeleter
+ {
+ void operator() (id texture) const noexcept
+ {
+ [texture setPurgeableState: MTLPurgeableStateEmpty];
+ [texture release];
+ }
+ };
+
+ using TextureUniquePtr = std::unique_ptr>, TextureDeleter>;
+
+ //==============================================================================
+ class GpuTexturePool
+ {
+ public:
+ GpuTexturePool (id metalDevice, MTLTextureDescriptor* descriptor)
+ {
+ for (auto& t : textureCache)
+ t.reset ([metalDevice newTextureWithDescriptor: descriptor]);
+ }
+
+ id take() const
+ {
+ auto iter = std::find_if (textureCache.begin(), textureCache.end(),
+ [] (const TextureUniquePtr& t) { return [t.get() retainCount] == 1; });
+ return iter == textureCache.end() ? nullptr : (*iter).get();
+ }
+
+ private:
+ std::array textureCache;
+
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GpuTexturePool)
+ JUCE_DECLARE_NON_MOVEABLE (GpuTexturePool)
+ };
+
+ //==============================================================================
+ class Resources
+ {
+ public:
+ Resources (id metalDevice, CAMetalLayer* layer, int componentHeight)
+ {
+ const auto bytesPerRow = alignTo ((size_t) layer.drawableSize.width * 4, 256);
+
+ const auto allocationSize = cpuRenderMemory.ensureSize (bytesPerRow * (size_t) layer.drawableSize.height);
+
+ ObjCObjectHandle> buffer { [metalDevice newBufferWithBytesNoCopy: cpuRenderMemory.get()
+ length: allocationSize
+ options: MTLResourceStorageModeShared
+ deallocator: nullptr] };
+
+ auto* textureDesc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat: layer.pixelFormat
+ width: (NSUInteger) layer.drawableSize.width
+ height: (NSUInteger) layer.drawableSize.height
+ mipmapped: NO];
+ textureDesc.storageMode = buffer.get().storageMode;
+ textureDesc.usage = MTLTextureUsageShaderRead;
+
+ cpuTexture.reset ([buffer.get() newTextureWithDescriptor: textureDesc
+ offset: 0
+ bytesPerRow: bytesPerRow]);
+
+ cgContext.reset (CGBitmapContextCreate (cpuRenderMemory.get(),
+ (size_t) layer.drawableSize.width,
+ (size_t) layer.drawableSize.height,
+ 8, // Bits per component
+ bytesPerRow,
+ CGColorSpaceCreateWithName (kCGColorSpaceSRGB),
+ (uint32_t) kCGImageAlphaPremultipliedFirst | (uint32_t) kCGBitmapByteOrder32Host));
+
+ CGContextScaleCTM (cgContext.get(), layer.contentsScale, layer.contentsScale);
+ CGContextConcatCTM (cgContext.get(), CGAffineTransformMake (1, 0, 0, -1, 0, componentHeight));
+
+ textureDesc.storageMode = MTLStorageModePrivate;
+ gpuTexturePool = std::make_unique (metalDevice, textureDesc);
+ }
+
+ CGContextRef getCGContext() const noexcept { return cgContext.get(); }
+ id getCpuTexture() const noexcept { return cpuTexture.get(); }
+ id getGpuTexture() noexcept { return gpuTexturePool == nullptr ? nullptr : gpuTexturePool->take(); }
+
+ private:
+ class AlignedMemory
+ {
+ public:
+ AlignedMemory() = default;
+
+ void* get()
+ {
+ return allocation != nullptr ? allocation->data : nullptr;
+ }
+
+ size_t ensureSize (size_t newSize)
+ {
+ const auto alignedSize = alignTo (newSize, pagesize);
+
+ if (alignedSize > size)
+ {
+ size = std::max (alignedSize, alignTo ((size_t) (size * growthFactor), pagesize));
+ allocation = std::make_unique (pagesize, size);
+ }
+
+ return size;
+ }
+
+ private:
+ static constexpr float growthFactor = 1.3f;
+
+ const size_t pagesize = (size_t) getpagesize();
+
+ struct AllocationWrapper
+ {
+ AllocationWrapper (size_t alignment, size_t allocationSize)
+ {
+ if (posix_memalign (&data, alignment, allocationSize) != 0)
+ jassertfalse;
+ }
+
+ ~AllocationWrapper()
+ {
+ ::free (data);
+ }
+
+ void* data = nullptr;
+ };
+
+ std::unique_ptr allocation;
+ size_t size = 0;
+
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AlignedMemory)
+ JUCE_DECLARE_NON_MOVEABLE (AlignedMemory)
+ };
+
+ AlignedMemory cpuRenderMemory;
+
+ detail::ContextPtr cgContext;
+
+ TextureUniquePtr cpuTexture;
+ std::unique_ptr gpuTexturePool;
+
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Resources)
+ JUCE_DECLARE_NON_MOVEABLE (Resources)
+ };
+
+ //==============================================================================
+ std::unique_ptr resources;
+
+ ObjCObjectHandle> device;
+ ObjCObjectHandle> commandQueue;
+ ObjCObjectHandle> memoryBlitCommandBuffer;
+ ObjCObjectHandle> memoryBlitEvent;
+
+ uint64_t memoryBlitCounter = 0;
+ std::atomic stopGpuCommandSubmission { false };
+
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreGraphicsMetalLayerRenderer)
+ JUCE_DECLARE_NON_MOVEABLE (CoreGraphicsMetalLayerRenderer)
+};
+
+JUCE_END_IGNORE_WARNINGS_GCC_LIKE
+
+}
diff --git a/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm b/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm
index 4b2da33571..630e6d3270 100644
--- a/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm
+++ b/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm
@@ -16,6 +16,8 @@
==============================================================================
*/
+#include "juce_mac_CGMetalLayerRenderer.h"
+
@interface NSEvent (DeviceDelta)
- (float)deviceDeltaX;
- (float)deviceDeltaY;
@@ -24,14 +26,11 @@
//==============================================================================
namespace juce
{
- typedef void (*AppFocusChangeCallback)();
- extern AppFocusChangeCallback appFocusChangeCallback;
- typedef bool (*CheckEventBlockedByModalComps) (NSEvent*);
- extern CheckEventBlockedByModalComps isEventBlockedByModalComps;
-}
-namespace juce
-{
+using AppFocusChangeCallback = void (*)();
+extern AppFocusChangeCallback appFocusChangeCallback;
+using CheckEventBlockedByModalComps = bool (*) (NSEvent*);
+extern CheckEventBlockedByModalComps isEventBlockedByModalComps;
//==============================================================================
static constexpr int translateVirtualToAsciiKeyCode (int keyCode) noexcept
@@ -918,6 +917,11 @@ public:
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
}();
+ drawRectWithContext (cg, r);
+ }
+
+ void drawRectWithContext (CGContextRef cg, NSRect r)
+ {
if (! component.isOpaque())
CGContextClearRect (cg, CGContextGetClipBoundingBox (cg));
@@ -936,10 +940,12 @@ public:
};
#if USE_COREGRAPHICS_RENDERING && JUCE_COREGRAPHICS_RENDER_WITH_MULTIPLE_PAINT_CALLS
- // This option invokes a separate paint call for each rectangle of the clip region.
- // It's a long story, but this is a basically a workaround for a CGContext not having
- // a way of finding whether a rectangle falls within its clip region
- if (usingCoreGraphics)
+ // This was a workaround for a CGContext not having a way of finding whether a rectangle
+ // falls within its clip region. However Apple removed the capability of
+ // [view getRectsBeingDrawn: ...] sometime around 10.13, so on later versions of macOS
+ // numRects will always be 1 and you'll need to use a CoreGraphicsMetalLayerRenderer
+ // to avoid CoreGraphics consolidating disparate rects.
+ if (usingCoreGraphics && metalRenderer == nullptr)
{
const NSRect* rects = nullptr;
NSInteger numRects = 0;
@@ -952,7 +958,7 @@ public:
NSRect rect = rects[i];
CGContextSaveGState (cg);
CGContextClipToRect (cg, CGRectMake (rect.origin.x, rect.origin.y, rect.size.width, rect.size.height));
- drawRectWithContext (cg, rect, displayScale);
+ renderRect (cg, rect, displayScale);
CGContextRestoreGState (cg);
}
@@ -962,11 +968,11 @@ public:
}
#endif
- drawRectWithContext (cg, r, displayScale);
+ renderRect (cg, r, displayScale);
invalidateTransparentWindowShadow();
}
- void drawRectWithContext (CGContextRef cg, NSRect r, float displayScale)
+ void renderRect (CGContextRef cg, NSRect r, float displayScale)
{
#if USE_COREGRAPHICS_RENDERING
if (usingCoreGraphics)
@@ -1049,11 +1055,61 @@ public:
if (msSinceLastRepaint < minimumRepaintInterval && shouldThrottleRepaint())
return;
- for (auto& i : deferredRepaints)
- [view setNeedsDisplayInRect: makeNSRect (i)];
+ #if USE_COREGRAPHICS_RENDERING && JUCE_COREGRAPHICS_RENDER_WITH_MULTIPLE_PAINT_CALLS
+ // We require macOS 10.14 to use the Metal layer renderer
+ if (@available (macOS 10.14, *))
+ {
+ const auto& comp = getComponent();
+
+ // If we are resizing we need to fall back to synchronous drawing to avoid artefacts
+ if (areAnyWindowsInLiveResize())
+ {
+ if (metalRenderer != nullptr)
+ {
+ metalRenderer.reset();
+ view.wantsLayer = NO;
+ view.layer = nil;
+ deferredRepaints = comp.getLocalBounds().toFloat();
+ }
+ }
+ else
+ {
+ if (metalRenderer == nullptr)
+ {
+ view.wantsLayer = YES;
+ view.layerContentsRedrawPolicy = NSViewLayerContentsRedrawDuringViewResize;
+ view.layerContentsPlacement = NSViewLayerContentsPlacementTopLeft;
+ view.layer = [CAMetalLayer layer];
+ metalRenderer = std::make_unique ((CAMetalLayer*) view.layer, getComponent());
+ deferredRepaints = comp.getLocalBounds().toFloat();
+ }
+ }
+ }
+ #endif
+
+ auto dispatchRectangles = [this] ()
+ {
+ if (metalRenderer != nullptr)
+ {
+ return metalRenderer->drawRectangleList ((CAMetalLayer*) view.layer,
+ (float) [[view window] backingScaleFactor],
+ view.frame,
+ getComponent(),
+ [this] (CGContextRef ctx, CGRect r) { drawRectWithContext (ctx, r); },
+ deferredRepaints);
+ }
- lastRepaintTime = Time::getMillisecondCounter();
- deferredRepaints.clear();
+ for (auto& i : deferredRepaints)
+ [view setNeedsDisplayInRect: makeNSRect (i)];
+
+ return true;
+ };
+
+ if (dispatchRectangles())
+ {
+ lastRepaintTime = Time::getMillisecondCounter();
+ deferredRepaints.clear();
+ }
}
void performAnyPendingRepaintsNow() override
@@ -1157,6 +1213,7 @@ public:
void redirectMovedOrResized()
{
handleMovedOrResized();
+ setNeedsDisplayRectangles();
}
void windowDidChangeScreen()
@@ -1794,6 +1851,7 @@ private:
[window setMaxFullScreenContentSize: NSMakeSize (100000, 100000)];
}
+ //==============================================================================
void onDisplaySourceCallback()
{
setNeedsDisplayRectangles();
@@ -1836,6 +1894,8 @@ private:
CVDisplayLinkRef displayLink = nullptr;
dispatch_source_t displaySource = nullptr;
+ std::unique_ptr metalRenderer;
+
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NSViewComponentPeer)
};