[vlc-commits] [Git][videolan/vlc][master] 14 commits: macosx: Add rule to build metal files
Felix Paul Kühne (@fkuehne)
gitlab at videolan.org
Wed Jun 4 18:23:34 UTC 2025
Felix Paul Kühne pushed to branch master at VideoLAN / VLC
Commits:
f2a84fdb by Claudio Cambra at 2025-06-04T17:55:44+00:00
macosx: Add rule to build metal files
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
768e0a55 by Claudio Cambra at 2025-06-04T17:55:44+00:00
macosx: Add starter VLCSnowEffectView
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
011922e9 by Claudio Cambra at 2025-06-04T17:55:44+00:00
macosx: Add shader types
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
f508d0c1 by Claudio Cambra at 2025-06-04T17:55:44+00:00
macosx: Incorporate CoreGraphics, Metal, MetalKit
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
92d6a322 by Claudio Cambra at 2025-06-04T17:55:44+00:00
macosx: Add snow metal shader file
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
cf39653b by Claudio Cambra at 2025-06-04T17:55:44+00:00
macosx: Configure metal device and library
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
b9af8c7f by Claudio Cambra at 2025-06-04T17:55:44+00:00
macosx: Set up metal components in snow effect view
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
3517e34d by Claudio Cambra at 2025-06-04T17:55:44+00:00
macosx: Make the snow effect view see-through
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
40dce246 by Claudio Cambra at 2025-06-04T17:55:44+00:00
macosx: Do not attempt to load metal library if bundle path is nil
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
ec3ba46f by Claudio Cambra at 2025-06-04T17:55:44+00:00
macosx: Define winterHolidaysTheming property in VLCApplication
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
336d583a by Claudio Cambra at 2025-06-04T17:55:44+00:00
macosx: Instantiate and present snow view when relevant
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
1c9ed3b6 by Claudio Cambra at 2025-06-04T17:55:44+00:00
macosx: Check for Metal tool availability
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
macosx: Check for metallib availability
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
9a01b3e4 by Claudio Cambra at 2025-06-04T17:55:44+00:00
macosx: Make building snow shader toggleable
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
b6abafe5 by Claudio Cambra at 2025-06-04T17:55:44+00:00
macosx: Only instantiate metal library during winter holidays period
Signed-off-by: Claudio Cambra <developer at claudiocambra.com>
- - - - -
13 changed files:
- configure.ac
- extras/package/macosx/VLC.xcodeproj/project.pbxproj
- extras/package/macosx/package.mak
- modules/gui/macosx/Makefile.am
- modules/gui/macosx/library/VLCLibraryWindow.m
- modules/gui/macosx/main/VLCApplication.h
- modules/gui/macosx/main/VLCApplication.m
- modules/gui/macosx/main/VLCMain.h
- modules/gui/macosx/main/VLCMain.m
- + modules/gui/macosx/shaders/VLCShaderTypes.h
- + modules/gui/macosx/shaders/VLCSnowShader.metal
- + modules/gui/macosx/views/VLCSnowEffectView.h
- + modules/gui/macosx/views/VLCSnowEffectView.m
Changes:
=====================================
configure.ac
=====================================
@@ -4416,6 +4416,44 @@ then
])
])
+ dnl
+ dnl If possible, use xcrun to find the right metal tool
+ dnl
+
+ AS_IF([test ! "${XCRUN}" = "no"], [
+ AC_MSG_CHECKING([for metal (using xcrun)])
+ METAL="$(eval $XCRUN -f metal 2>/dev/null || echo no)"
+ AC_MSG_RESULT([${METAL}])
+ ], [
+ AC_MSG_WARN([Looking for tools without using xcrun])
+ ])
+
+ AS_IF([test "${METAL}" = "no"], [
+ AC_PATH_PROG(METAL, [metal], [no])
+ AS_IF([test "${METAL}" = "no"], [
+ AC_MSG_ERROR([metal was not found, but is required for --enable-macosx])
+ ])
+ ])
+
+ dnl
+ dnl If possible, use xcrun to find the right metallib tool
+ dnl
+
+ AS_IF([test ! "${XCRUN}" = "no"], [
+ AC_MSG_CHECKING([for metallib (using xcrun)])
+ METALLIB="$(eval $XCRUN -f metallib 2>/dev/null || echo no)"
+ AC_MSG_RESULT([${METALLIB}])
+ ], [
+ AC_MSG_WARN([Looking for tools without using xcrun])
+ ])
+
+ AS_IF([test "${METALLIB}" = "no"], [
+ AC_PATH_PROG(METALLIB, [metallib], [no])
+ AS_IF([test "${METALLIB}" = "no"], [
+ AC_MSG_ERROR([metallib was not found, but is required for --enable-macosx])
+ ])
+ ])
+
dnl
dnl If possible, use xcrun to find the right actool
dnl
@@ -4434,7 +4472,25 @@ then
AC_MSG_ERROR([actool was not found, but is required for --enable-macosx])
])
])
+
+ dnl
+ dnl MacOS X gui module metal shaders
+ dnl
+ _enable_macosx_ui_shaders_default="yes"
+
+ AC_ARG_ENABLE([macosx-ui-shaders],
+ AS_HELP_STRING([--disable-macosx-ui-shaders], [Disable macOS UI Metal shaders (default: enabled if macOS UI is active)]),
+ [], [enable_macosx_ui_shaders="${_enable_macosx_ui_shaders_default}"]
+ )
+
+ if test "${enable_macosx_ui_shaders}" != "no"
+ then
+ dnl Tool checks for metal and metallib are already performed above if enable_macosx is true.
+ dnl We assume they are found if we reach this point and shaders are enabled.
+ AC_DEFINE([ENABLE_MACOSX_UI_SHADERS], [1], [Define to 1 if macOS UI Metal shaders are enabled.])
+ fi
fi
+AM_CONDITIONAL([ENABLE_MACOSX_UI_SHADERS], [test "${enable_macosx_ui_shaders}" != "no" -a "${enable_macosx}" != "no" -a "${SYS}" = "darwin"])
AM_CONDITIONAL([ENABLE_MACOSX_UI], [test "$enable_macosx" != "no" -a "${SYS}" = "darwin"])
dnl
=====================================
extras/package/macosx/VLC.xcodeproj/project.pbxproj
=====================================
@@ -86,6 +86,7 @@
531343E72A8E7B94007AEDFA /* VLCLibraryWindowNavigationSidebarViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 531343E62A8E7B94007AEDFA /* VLCLibraryWindowNavigationSidebarViewController.m */; };
531343EA2A8E8965007AEDFA /* VLCLibrarySegment.m in Sources */ = {isa = PBXBuildFile; fileRef = 531343E92A8E8965007AEDFA /* VLCLibrarySegment.m */; };
5317FE04294E3DD3001702F0 /* VLCLibraryCollectionViewDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 5317FE03294E3DD3001702F0 /* VLCLibraryCollectionViewDelegate.m */; };
+ 5318F9332DBBB42D0008D9C8 /* VLCSnowShader.metal in Sources */ = {isa = PBXBuildFile; fileRef = 5318F9322DBBB42D0008D9C8 /* VLCSnowShader.metal */; };
532572032C3D79D80068DEC3 /* VLCLibrarySegmentBookmarkedLocation.m in Sources */ = {isa = PBXBuildFile; fileRef = 532572022C3D79D80068DEC3 /* VLCLibrarySegmentBookmarkedLocation.m */; };
532572062C3EF3710068DEC3 /* VLCLibraryWindowNavigationSidebarOutlineView.m in Sources */ = {isa = PBXBuildFile; fileRef = 532572052C3EF3710068DEC3 /* VLCLibraryWindowNavigationSidebarOutlineView.m */; };
5325720F2C4966630068DEC3 /* VLCLibraryShowsDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 5325720E2C4966630068DEC3 /* VLCLibraryShowsDataSource.m */; };
@@ -123,6 +124,7 @@
536EFBF5295BCB8300F4CB13 /* VLCLibraryUIUnits.m in Sources */ = {isa = PBXBuildFile; fileRef = 536EFBF4295BCB8300F4CB13 /* VLCLibraryUIUnits.m */; };
536EFC39295E521600F4CB13 /* VLCLibraryVideoViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 536EFC38295E521600F4CB13 /* VLCLibraryVideoViewController.m */; };
537040DF2DD848440030ABF5 /* VLCPlaybackEndViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 537040DE2DD848440030ABF5 /* VLCPlaybackEndViewController.m */; };
+ 53741A5A2DCA7B4C00E112CD /* VLCSnowEffectView.m in Sources */ = {isa = PBXBuildFile; fileRef = 53741A592DCA7B4B00E112CD /* VLCSnowEffectView.m */; };
5377B15C2BD12EEE00D660B8 /* QuickLookThumbnailing.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5377B15B2BD12EEE00D660B8 /* QuickLookThumbnailing.framework */; };
5377B15E2BD12F2900D660B8 /* QuickLook.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5377B15D2BD12F2900D660B8 /* QuickLook.framework */; };
537976BA2A4319330036827E /* VLCSettingTextField.m in Sources */ = {isa = PBXBuildFile; fileRef = 537976B82A4319330036827E /* VLCSettingTextField.m */; };
@@ -326,6 +328,7 @@
5317FE02294E3DD3001702F0 /* VLCLibraryCollectionViewDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VLCLibraryCollectionViewDelegate.h; sourceTree = "<group>"; };
5317FE03294E3DD3001702F0 /* VLCLibraryCollectionViewDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VLCLibraryCollectionViewDelegate.m; sourceTree = "<group>"; };
5317FE05294E8D1A001702F0 /* VLCLibraryCollectionViewDataSource.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VLCLibraryCollectionViewDataSource.h; sourceTree = "<group>"; };
+ 5318F9322DBBB42D0008D9C8 /* VLCSnowShader.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = VLCSnowShader.metal; sourceTree = "<group>"; };
53222C002CBBDBEE00CB9FA2 /* VLCLibraryWindowSidebarChildViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VLCLibraryWindowSidebarChildViewController.h; sourceTree = "<group>"; };
532572012C3D79D80068DEC3 /* VLCLibrarySegmentBookmarkedLocation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VLCLibrarySegmentBookmarkedLocation.h; sourceTree = "<group>"; };
532572022C3D79D80068DEC3 /* VLCLibrarySegmentBookmarkedLocation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VLCLibrarySegmentBookmarkedLocation.m; sourceTree = "<group>"; };
@@ -410,6 +413,9 @@
536EFC3A295F828000F4CB13 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
537040DD2DD848440030ABF5 /* VLCPlaybackEndViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VLCPlaybackEndViewController.h; sourceTree = "<group>"; };
537040DE2DD848440030ABF5 /* VLCPlaybackEndViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VLCPlaybackEndViewController.m; sourceTree = "<group>"; };
+ 53741A582DCA7B4B00E112CD /* VLCSnowEffectView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VLCSnowEffectView.h; sourceTree = "<group>"; };
+ 53741A592DCA7B4B00E112CD /* VLCSnowEffectView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VLCSnowEffectView.m; sourceTree = "<group>"; };
+ 53741A5B2DCD141D00E112CD /* VLCShaderTypes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VLCShaderTypes.h; sourceTree = "<group>"; };
5377B15B2BD12EEE00D660B8 /* QuickLookThumbnailing.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuickLookThumbnailing.framework; path = System/Library/PrivateFrameworks/QuickLookThumbnailing.framework; sourceTree = SDKROOT; };
5377B15D2BD12F2900D660B8 /* QuickLook.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuickLook.framework; path = System/Library/Frameworks/QuickLook.framework; sourceTree = SDKROOT; };
537976B82A4319330036827E /* VLCSettingTextField.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VLCSettingTextField.m; sourceTree = "<group>"; };
@@ -1048,6 +1054,7 @@
1C1ED5142205A96600811EC0 /* playqueue */,
1C1ED5102204B06700811EC0 /* preferences */,
6CEA55632C6BA14300CCC2E7 /* private */,
+ 5318F9312DBBB3B10008D9C8 /* shaders */,
1C1ED5062204AB7C00811EC0 /* views */,
1C1ED5072204AC5900811EC0 /* windows */,
);
@@ -1173,6 +1180,8 @@
6B13E2A71BC67678001AD24A /* VLCScrollingClipView.m */,
537976B92A4319330036827E /* VLCSettingTextField.h */,
537976B82A4319330036827E /* VLCSettingTextField.m */,
+ 53741A582DCA7B4B00E112CD /* VLCSnowEffectView.h */,
+ 53741A592DCA7B4B00E112CD /* VLCSnowEffectView.m */,
531062172CE4AEFB0087F863 /* VLCStatusNotifierView.h */,
531062182CE4AEFB0087F863 /* VLCStatusNotifierView.m */,
53628401291147C500640C15 /* VLCSubScrollView.h */,
@@ -1600,6 +1609,15 @@
path = iCarousel;
sourceTree = "<group>";
};
+ 5318F9312DBBB3B10008D9C8 /* shaders */ = {
+ isa = PBXGroup;
+ children = (
+ 53741A5B2DCD141D00E112CD /* VLCShaderTypes.h */,
+ 5318F9322DBBB42D0008D9C8 /* VLCSnowShader.metal */,
+ );
+ path = shaders;
+ sourceTree = "<group>";
+ };
5325C5742930026600B2B63A /* audio-library */ = {
isa = PBXGroup;
children = (
@@ -2339,12 +2357,14 @@
5325C57D29302E6800B2B63A /* VLCLibraryAudioViewController.m in Sources */,
1C3113981E508C6900D4DD76 /* VLCAudioEffectsWindowController.m in Sources */,
53ED472629C78FE700795DB1 /* VLCLibraryAudioGroupTableViewDelegate.m in Sources */,
+ 5318F9332DBBB42D0008D9C8 /* VLCSnowShader.metal in Sources */,
536283F9291146BC00640C15 /* VLCLibraryCollectionViewFlowLayout.m in Sources */,
6BBBF9851F7B257100B404CD /* VLCLogMessage.m in Sources */,
7D445D8E2203375100263D34 /* VLCPlayQueueMenuController.m in Sources */,
53903D3A29576ED500D0B308 /* VLCLibraryAudioGroupDataSource.m in Sources */,
1C31139A1E508C6900D4DD76 /* VLCBookmarksWindowController.m in Sources */,
6B0AB0F01F1AC8B3003A1B4E /* VLCPlaybackProgressSlider.m in Sources */,
+ 53741A5A2DCA7B4C00E112CD /* VLCSnowEffectView.m in Sources */,
7D28E6362275B4820098D30E /* NSColor+VLCAdditions.m in Sources */,
534D28B42DB2B3C0006869CD /* NSTableCellView+VLCAdditions.m in Sources */,
535F1BBA2B47ACCE00C78D98 /* VLCLibraryHomeViewVideoContainerViewDataSource.m in Sources */,
=====================================
extras/package/macosx/package.mak
=====================================
@@ -31,6 +31,8 @@ VLC.app: macos-install
cp -R "$(top_builddir)/modules/gui/macosx/UI/." $@/Contents/Resources/Base.lproj/
## Copy Asset catalog
cp "$(top_builddir)/modules/gui/macosx/Resources/Assets.car" $@/Contents/Resources/Assets.car
+ ## Copy Shaders metal library
+ cp "$(top_builddir)/modules/gui/macosx/Resources/Shaders.metallib" $@/Contents/Resources/Shaders.metallib
## Copy Info.plist and convert to binary
cp -R "$(top_builddir)/share/macosx/Info.plist" $@/Contents/
xcrun plutil -convert binary1 $@/Contents/Info.plist
=====================================
modules/gui/macosx/Makefile.am
=====================================
@@ -36,6 +36,37 @@ xib_verbose__0 = $(xib_verbose_0)
--minimum-deployment-target 10.11 --output-format human-readable-text \
--compile $@ $<
+if ENABLE_MACOSX_UI_SHADERS
+SUFFIXES += .air .metal
+
+metal_verbose = $(metal_verbose_$(V))
+metal_verbose_ = $(metal_verbose__$(AM_DEFAULT_VERBOSITY))
+metal_verbose_0 = @echo " METAL " $@;
+metal_verbose__0 = $(metal_verbose_0)
+
+# Rule to compile .metal to .air
+.metal.air:
+ $(AM_V_at)$(MKDIR_P) "gui/macosx/shaders"
+ $(metal_verbose)$(METAL) -c $< -o $@
+
+metallib_verbose = $(metallib_verbose_$(V))
+metallib_verbose_ = $(metallib_verbose__$(AM_DEFAULT_VERBOSITY))
+metallib_verbose_0 = @echo " METALLIB " $@;
+metallib_verbose__0 = $(metallib_verbose_0)
+
+libmacosx_plugin_la_METAL_SOURCES = gui/macosx/shaders/VLCSnowShader.metal
+libmacosx_plugin_la_METAL_AIR_FILES = $(libmacosx_plugin_la_METAL_SOURCES:.metal=.air)
+libmacosx_plugin_la_METAL_LIBRARY = gui/macosx/resources/Shaders.metallib
+
+$(libmacosx_plugin_la_METAL_LIBRARY): $(libmacosx_plugin_la_METAL_AIR_FILES)
+ $(AM_V_at)$(MKDIR_P) "gui/macosx/shaders"
+ $(metallib_verbose)$(METALLIB) $(libmacosx_plugin_la_METAL_AIR_FILES) -o $@
+
+EXTRA_DIST += $(libmacosx_plugin_la_METAL_SOURCES)
+BUILT_SOURCES += $(libmacosx_plugin_la_METAL_LIBRARY)
+CLEANFILES += $(libmacosx_plugin_la_METAL_AIR_FILES) $(libmacosx_plugin_la_METAL_LIBRARY)
+
+endif
libmacosx_plugin_la_OBJCFLAGS = $(AM_OBJCFLAGS) -fobjc-exceptions -fobjc-arc -I$(srcdir)/gui/macosx
libmacosx_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(guidir)' \
@@ -43,6 +74,7 @@ libmacosx_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(guidir)' \
-Wl,-framework,AVFoundation \
-Wl,-framework,Cocoa \
-Wl,-framework,CoreAudio \
+ -Wl,-framework,CoreGraphics \
-Wl,-framework,CoreVideo \
-Wl,-framework,IOKit \
-Wl,-framework,QuartzCore \
@@ -52,6 +84,11 @@ libmacosx_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(guidir)' \
-Wl,-framework,QuickLookThumbnailing \
-Wl,-weak_framework,MediaPlayer
+if ENABLE_MACOSX_UI_SHADERS
+libmacosx_plugin_la_LDFLAGS += -Wl,-framework,Metal \
+ -Wl,-framework,MetalKit
+endif
+
if HAVE_SPARKLE
libmacosx_plugin_la_LDFLAGS += -Wl,-framework,Sparkle
endif
@@ -369,6 +406,7 @@ libmacosx_plugin_la_SOURCES = \
gui/macosx/preferences/prefs.m \
gui/macosx/preferences/prefs_widgets.h \
gui/macosx/preferences/prefs_widgets.m \
+ gui/macosx/shaders/VLCShaderTypes.h \
gui/macosx/views/iCarousel/iCarousel.h \
gui/macosx/views/iCarousel/iCarousel.m \
gui/macosx/views/VLCBasicView.h \
@@ -407,6 +445,8 @@ libmacosx_plugin_la_SOURCES = \
gui/macosx/views/VLCScrollingClipView.m \
gui/macosx/views/VLCSettingTextField.h \
gui/macosx/views/VLCSettingTextField.m \
+ gui/macosx/views/VLCSnowEffectView.h \
+ gui/macosx/views/VLCSnowEffectView.m \
gui/macosx/views/VLCStatusNotifierView.h \
gui/macosx/views/VLCStatusNotifierView.m \
gui/macosx/views/VLCSubScrollView.h \
@@ -536,7 +576,6 @@ gui_LTLIBRARIES += libmacosx_plugin.la
BUILT_SOURCES += $(libmacosx_plugin_la_XIB_SOURCES:.xib=.nib)
endif
-
libmacosx_plugin_la_RES = \
gui/macosx/Resources/App-Icons/VLC-Xmas.icns \
gui/macosx/Resources/App-Icons/VLC.icns \
=====================================
modules/gui/macosx/library/VLCLibraryWindow.m
=====================================
@@ -31,6 +31,7 @@
#import "extensions/NSView+VLCAdditions.h"
#import "extensions/NSWindow+VLCAdditions.h"
+#import "main/VLCApplication.h"
#import "main/VLCMain.h"
#import "menus/VLCMainMenu.h"
@@ -78,6 +79,7 @@
#import "views/VLCNoResultsLabel.h"
#import "views/VLCPlaybackEndViewController.h"
#import "views/VLCRoundedCornerTextField.h"
+#import "views/VLCSnowEffectView.h"
#import "views/VLCTrackingView.h"
#import "windows/controlsbar/VLCMainWindowControlsBar.h"
@@ -289,6 +291,14 @@ static void addShadow(NSImageView *__unsafe_unretained imageView)
[view.leftAnchor constraintEqualToAnchor:self.libraryTargetView.leftAnchor],
[view.rightAnchor constraintEqualToAnchor:self.libraryTargetView.rightAnchor]
]];
+
+ if (VLCMain.sharedInstance.metalLibrary && ((VLCApplication *)NSApplication.sharedApplication).winterHolidaysTheming) {
+ VLCSnowEffectView * const snowView =
+ [[VLCSnowEffectView alloc] initWithFrame:self.contentView.bounds];
+ [self.libraryTargetView addSubview:snowView];
+ snowView.translatesAutoresizingMaskIntoConstraints = NO;
+ [snowView applyConstraintsToFillSuperview];
+ }
}
- (void)displayLibraryPlaceholderViewWithImage:(NSImage *)image
=====================================
modules/gui/macosx/main/VLCApplication.h
=====================================
@@ -43,5 +43,6 @@
* Must be called from the main thread only.
*/
@property(strong, readonly) NSImage *vlcAppIconImage;
+ at property(assign, readonly) BOOL winterHolidaysTheming;
@end
=====================================
modules/gui/macosx/main/VLCApplication.m
=====================================
@@ -58,6 +58,18 @@
* it ends-up after being relocated or rename */
_appLocationURL = [[[NSBundle mainBundle] bundleURL] fileReferenceURL];
+ if (config_GetInt("macosx-icon-change")) {
+ NSCalendar *const gregorian =
+ [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
+ const NSUInteger dayOfYear = [gregorian ordinalityOfUnit:NSCalendarUnitDay
+ inUnit:NSCalendarUnitYear
+ forDate:[NSDate date]];
+
+ if (dayOfYear >= 354) {
+ _winterHolidaysTheming = YES;
+ }
+ }
+
}
return self;
}
@@ -75,16 +87,12 @@
if (_vlcAppIconImage != nil)
return _vlcAppIconImage;
- if (config_GetInt("macosx-icon-change")) {
+ if (self.winterHolidaysTheming) {
/* After day 354 of the year, the usual VLC cone is replaced by another cone
* wearing a Father Xmas hat.
* Note: this icon doesn't represent an endorsement of The Coca-Cola Company.
*/
- NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
- NSUInteger dayOfYear = [gregorian ordinalityOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitYear forDate:[NSDate date]];
-
- if (dayOfYear >= 354)
- _vlcAppIconImage = [NSImage imageNamed:@"VLC-Xmas"];
+ _vlcAppIconImage = [NSImage imageNamed:@"VLC-Xmas"];
}
if (_vlcAppIconImage == nil)
=====================================
modules/gui/macosx/main/VLCMain.h
=====================================
@@ -28,6 +28,7 @@
#endif
#import <Cocoa/Cocoa.h>
+#import <MetalKit/MetalKit.h>
#import <vlc_common.h>
#import <vlc_interface.h>
@@ -91,5 +92,7 @@ extern NSString * const kVLCPreferencesVersion;
@property (readonly) VLCVideoEffectsWindowController *videoEffectsPanel;
@property (readonly) VLCVideoOutputProvider *voutProvider;
@property (readonly) VLCDetachedAudioWindow *detachedAudioWindow;
+ at property (readonly) id<MTLDevice> metalDevice;
+ at property (readonly) id<MTLLibrary> metalLibrary;
@end
=====================================
modules/gui/macosx/main/VLCMain.m
=====================================
@@ -44,6 +44,8 @@
#include <vlc_variables.h>
#include <vlc_preparser.h>
+#include <Metal/Metal.h>
+
#import "extensions/NSString+Helpers.h"
#import "library/VLCLibraryController.h"
@@ -286,6 +288,23 @@ static VLCMain *sharedInstance = nil;
- (void)applicationWillFinishLaunching:(NSNotification *)aNotification
{
+ // Only Metal shader in use at the moment is for holiday theming, so only load during this time.
+ // Change this if you are going to add new shaders!
+ if (((VLCApplication *)NSApplication.sharedApplication).winterHolidaysTheming) {
+ _metalDevice = MTLCreateSystemDefaultDevice();
+ NSString * const libraryPath =
+ [NSBundle.mainBundle pathForResource:@"Shaders" ofType:@"metallib"];
+ if (libraryPath) {
+ NSError *error = nil;
+ _metalLibrary = [_metalDevice newLibraryWithFile:libraryPath error:&error];
+ if (!_metalLibrary) {
+ NSLog(@"Error creating Metal library: %@", error);
+ }
+ } else {
+ NSLog(@"Error: Could not find Shaders.metallib in the bundle.");
+ }
+ }
+
_clickerManager = [[VLCClickerManager alloc] init];
[[NSBundle mainBundle] loadNibNamed:@"MainMenu" owner:_mainmenu topLevelObjects:nil];
=====================================
modules/gui/macosx/shaders/VLCShaderTypes.h
=====================================
@@ -0,0 +1,54 @@
+/*****************************************************************************
+ * VLCShaderTypes.h: MacOS X interface module
+ *****************************************************************************
+ * Copyright (C) 2025 VLC authors and VideoLAN
+ *
+ * Authors: Claudio Cambra <developer at claudiocambra.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifndef VLCShaderTypes_h
+#define VLCShaderTypes_h
+
+#include <simd/simd.h>
+
+// These structs are shared between C/Objective-C and Metal.
+
+typedef struct {
+ vector_float3 initialPosition; // x, y (initial screen position), z (depth/parallax factor 0.0-1.0)
+ float randomSeed; // (0.0-1.0)
+} Snowflake;
+
+typedef struct {
+ float time; // Current animation time
+ vector_float2 resolution; // Viewport width and height
+} Uniforms;
+
+typedef struct {
+ vector_float2 position; // Local offset for the quad vertex (e.g., -0.5 to 0.5)
+} VertexIn;
+
+
+#ifdef __METAL_VERSION__
+
+typedef struct {
+ float4 position [[position]]; // Clip-space position (required output for vertex shader)
+ float2 texCoord; // Texture coordinate (0-1) across the snowflake quad
+} VertexOut;
+
+#endif // __METAL_VERSION__
+
+#endif
=====================================
modules/gui/macosx/shaders/VLCSnowShader.metal
=====================================
@@ -0,0 +1,81 @@
+/*****************************************************************************
+ * VLCSnowShader.metal: MacOS X interface module
+ *****************************************************************************
+ * Copyright (C) 2025 VLC authors and VideoLAN
+ *
+ * Authors: Claudio Cambra <developer at claudiocambra.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#include <metal_stdlib>
+using namespace metal;
+
+#include "VLCShaderTypes.h"
+
+// It calculates the final screen position for that vertex of that snowflake.
+vertex VertexOut snowVertexShader(
+ uint vid [[vertex_id]], // Index of the current vertex in the input buffer (0-5 for a quad)
+ uint iid [[instance_id]], // Index of the current instance (snowflake)
+ constant VertexIn *vertices [[buffer(0)]], // Per-vertex data (quad corners)
+ constant Snowflake *snowflakes [[buffer(1)]], // Per-instance snowflake data
+ constant Uniforms &uniforms [[buffer(2)]] // Global uniforms
+) {
+ VertexOut out;
+
+ Snowflake currentSnowflake = snowflakes[iid];
+ const float fallSpeed = 0.15 + currentSnowflake.randomSeed * 0.1;
+ // Closer flakes (larger z) fall faster & are bigger
+ const float parallaxFactor = 0.5 + currentSnowflake.initialPosition.z * 1.5;
+
+ // Calculate current Y position (falling down, wrapping around)
+ float currentYNormalized = currentSnowflake.initialPosition.y - (uniforms.time * fallSpeed * parallaxFactor);
+ currentYNormalized = fmod(currentYNormalized, 2.0f); // Wrap around a range of 2.0 (e.g. +1 to -1)
+ if (currentYNormalized < -1.0f) { // Ensure it wraps from bottom to top correctly
+ currentYNormalized += 2.0f;
+ }
+
+ // Horizontal sway using sine wave based on time and random seed
+ const float swayAmount = 0.05 + currentSnowflake.randomSeed * 0.05;
+ const float swayFrequency = 1.0 + currentSnowflake.randomSeed * 0.5;
+ const float currentXNormalized = currentSnowflake.initialPosition.x + sin(uniforms.time * swayFrequency + currentSnowflake.randomSeed * 10.0) * swayAmount;
+
+ // Make closer flakes appear larger
+ const float flakeBaseSize = 0.02; // Base size in NDC
+ const float flakeScreenSize = flakeBaseSize * parallaxFactor;
+
+ const vector_float2 quadVertexOffset = vertices[vid].position; // e.g., from -0.5 to 0.5
+
+ const float aspectRatio = uniforms.resolution.x / uniforms.resolution.y;
+ vector_float2 finalPositionNDC;
+ finalPositionNDC.x = currentXNormalized + (quadVertexOffset.x * flakeScreenSize);
+ finalPositionNDC.y = currentYNormalized + (quadVertexOffset.y * flakeScreenSize * aspectRatio) ; // Apply aspect ratio to y scaling of quad
+
+ out.position = float4(finalPositionNDC.x, finalPositionNDC.y, currentSnowflake.initialPosition.z, 1.0);
+ out.texCoord = vertices[vid].position + 0.5; // Convert -0.5..0.5 to 0..1
+ return out;
+}
+
+// Takes data from the vertex shader (VertexOut) for the current pixel
+// and determines its final color.
+fragment float4 snowFragmentShader(
+ VertexOut in [[stage_in]],
+ constant Uniforms &uniforms [[buffer(0)]] // Uniforms can also be accessed here if needed
+) {
+ const float dist = distance(in.texCoord, float2(0.5));
+ // Create a smooth alpha falloff for a soft circle
+ const float alpha = 0.75 - smoothstep(0.0, 0.5, dist);
+ return float4(1.0, 1.0, 1.0, alpha);
+}
=====================================
modules/gui/macosx/views/VLCSnowEffectView.h
=====================================
@@ -0,0 +1,34 @@
+/*****************************************************************************
+ * VLCSnowEffectView.h: MacOS X interface module
+ *****************************************************************************
+ * Copyright (C) 2025 VLC authors and VideoLAN
+ *
+ * Authors: Claudio Cambra <developer at claudiocambra.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#import <Cocoa/Cocoa.h>
+#import <MetalKit/MetalKit.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+ at interface VLCSnowEffectView : NSView<MTKViewDelegate>
+
+ at property MTKView *mtkView;
+
+ at end
+
+NS_ASSUME_NONNULL_END
=====================================
modules/gui/macosx/views/VLCSnowEffectView.m
=====================================
@@ -0,0 +1,238 @@
+/*****************************************************************************
+ * VLCSnowEffectView.m: MacOS X interface module
+ *****************************************************************************
+ * Copyright (C) 2025 VLC authors and VideoLAN
+ *
+ * Authors: Claudio Cambra <developer at claudiocambra.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#import "VLCSnowEffectView.h"
+
+#import "main/VLCMain.h"
+#import "shaders/VLCShaderTypes.h"
+
+ at interface VLCSnowEffectView ()
+
+ at property (readonly) CFTimeInterval startTime;
+ at property (readonly) id<MTLCommandQueue> commandQueue;
+ at property (readonly) id<MTLFunction> vertexFunction;
+ at property (readonly) id<MTLFunction> fragmentFunction;
+ at property (readonly) id<MTLRenderPipelineState> pipelineState;
+ at property (readonly) id<MTLBuffer> vertexBuffer;
+ at property (readonly) id<MTLBuffer> uniformBuffer;
+ at property (readonly) id<MTLBuffer> snowflakeDataBuffer;
+ at property (readonly) NSUInteger snowflakeCount;
+
+ at end
+
+ at implementation VLCSnowEffectView
+
+- (instancetype)init
+{
+ self = [super init];
+ if (self) {
+ [self setup];
+ }
+ return self;
+}
+
+- (instancetype)initWithCoder:(NSCoder *)coder
+{
+ self = [super initWithCoder:coder];
+ if (self) {
+ [self setup];
+ }
+ return self;
+}
+
+- (void)encodeWithCoder:(nonnull NSCoder *)coder
+{
+ return [super encodeWithCoder:coder];
+}
+
+- (instancetype)initWithFrame:(NSRect)frameRect
+{
+ self = [super initWithFrame:frameRect];
+ if (self) {
+ [self setup];
+ }
+ return self;
+}
+
+- (void)setup
+{
+ self.wantsLayer = YES;
+ self.layer.backgroundColor = NSColor.clearColor.CGColor;
+
+ VLCMain * const main = VLCMain.sharedInstance;
+ const id<MTLDevice> metalDevice = main.metalDevice;
+ NSParameterAssert(metalDevice != nil);
+ NSParameterAssert(main.metalLibrary != nil);
+
+ _mtkView = [[MTKView alloc] initWithFrame:self.bounds device:metalDevice];
+ self.mtkView.delegate = self;
+ self.mtkView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
+ self.mtkView.colorPixelFormat = MTLPixelFormatBGRA8Unorm;
+ self.mtkView.clearColor = MTLClearColorMake(0, 0, 0, 0);
+ ((CAMetalLayer *)self.mtkView.layer).opaque = NO;
+ self.mtkView.layer.backgroundColor = NSColor.clearColor.CGColor;
+ [self addSubview:self.mtkView];
+
+ _commandQueue = [metalDevice newCommandQueue];
+ NSParameterAssert(self.commandQueue != nil);
+ _vertexFunction = [main.metalLibrary newFunctionWithName:@"snowVertexShader"];
+ NSParameterAssert(self.vertexFunction != nil);
+ _fragmentFunction = [main.metalLibrary newFunctionWithName:@"snowFragmentShader"];
+ NSParameterAssert(self.fragmentFunction != nil);
+
+ MTLRenderPipelineDescriptor * const pipelineDescriptor =
+ [[MTLRenderPipelineDescriptor alloc] init];
+ pipelineDescriptor.label = @"Snow Pipeline";
+ pipelineDescriptor.vertexFunction = self.vertexFunction;
+ pipelineDescriptor.fragmentFunction = self.fragmentFunction;
+ pipelineDescriptor.colorAttachments[0].pixelFormat = self.mtkView.colorPixelFormat;
+ pipelineDescriptor.colorAttachments[0].blendingEnabled = YES;
+ pipelineDescriptor.colorAttachments[0].rgbBlendOperation = MTLBlendOperationAdd;
+ pipelineDescriptor.colorAttachments[0].alphaBlendOperation = MTLBlendOperationAdd;
+ pipelineDescriptor.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorSourceAlpha;
+ pipelineDescriptor.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactorSourceAlpha;
+ pipelineDescriptor.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOne;
+ pipelineDescriptor.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorOne;
+
+ NSError *error;
+ _pipelineState =
+ [metalDevice newRenderPipelineStateWithDescriptor:pipelineDescriptor error:&error];
+ NSParameterAssert(error == nil);
+ NSParameterAssert(pipelineDescriptor != nil);
+
+ // --- Vertex Buffer Setup ---
+ // Example: Define vertices for a single small quad (to be instanced for snowflakes)
+ // You might draw points instead, which simplifies this.
+ // Format: {x, y, u, v} - position and texture coordinate
+ static const VertexIn quadVertices[] = {
+ // Triangle 1 (positions from -0.5 to 0.5 for a unit quad centered at origin)
+ { .position = {-0.5f, 0.5f} }, // Top-left
+ { .position = {-0.5f, -0.5f} }, // Bottom-left
+ { .position = { 0.5f, -0.5f} }, // Bottom-right
+ // Triangle 2
+ { .position = { 0.5f, -0.5f} }, // Bottom-right
+ { .position = { 0.5f, 0.5f} }, // Top-right
+ { .position = {-0.5f, 0.5f} } // Top-left
+ };
+ _vertexBuffer = [metalDevice newBufferWithBytes:quadVertices
+ length:sizeof(quadVertices)
+ options:MTLResourceStorageModeShared]; // CPU & GPU
+ NSParameterAssert(self.vertexBuffer != nil);
+
+ // --- Uniform Buffer Setup ---
+ // Create a buffer large enough for your Uniforms struct
+ // The struct definition would typically be in a shared header (.h)
+ // included by both Objective-C and your .metal file.
+ _uniformBuffer = [metalDevice newBufferWithLength:sizeof(Uniforms) // Assumes Uniforms struct exists
+ options:MTLResourceStorageModeShared];
+ NSParameterAssert(self.uniformBuffer != nil);
+
+ _snowflakeCount = 200; // Or however many you want
+ Snowflake * const snowflakesArray =
+ (Snowflake *)malloc(sizeof(Snowflake) * self.snowflakeCount);
+ for (NSUInteger i = 0; i < self.snowflakeCount; ++i) {
+ Snowflake flake;
+ flake.initialPosition.x = ((float)arc4random_uniform(2000) / 1000.0f) - 1.0f;
+ flake.initialPosition.y = ((float)arc4random_uniform(2000) / 1000.0f) - 1.0f;
+ flake.initialPosition.z = (float)arc4random_uniform(1000) / 1000.0f;
+ flake.randomSeed = (float)arc4random_uniform(1000) / 1000.0f;
+ snowflakesArray[i] = flake;
+ }
+ _snowflakeDataBuffer = [metalDevice newBufferWithBytes:snowflakesArray // Use self.
+ length:sizeof(Snowflake) * _snowflakeCount
+ options:MTLResourceStorageModeShared];
+ free(snowflakesArray);
+ NSParameterAssert(self.snowflakeDataBuffer != nil);
+
+ self.mtkView.paused = NO;
+ self.mtkView.enableSetNeedsDisplay = NO;
+ _startTime = CACurrentMediaTime();
+}
+
+- (BOOL)isOpaque
+{
+ return NO;
+}
+
+// MARK: - MTKViewDelegate
+
+- (void)mtkView:(MTKView *)view drawableSizeWillChange:(CGSize)size
+{
+ Uniforms * const uniforms = (Uniforms *)self.uniformBuffer.contents;
+ uniforms->resolution = (vector_float2){(float)size.width, (float)size.height};
+}
+
+- (void)drawInMTKView:(MTKView *)view
+{
+ // 1. Create Command Buffer
+ const id<MTLCommandBuffer> commandBuffer = [self.commandQueue commandBuffer];
+ commandBuffer.label = @"Snow Frame Command Buffer";
+
+ // 2. Get Render Pass Descriptor (specifies the drawing target)
+ MTLRenderPassDescriptor * const renderPassDescriptor = view.currentRenderPassDescriptor;
+ if (!renderPassDescriptor) {
+ NSLog(@"Failed to get render pass descriptor");
+ return; // Skip frame if view isn't ready
+ }
+
+ // 3. Create Render Command Encoder
+ const id<MTLRenderCommandEncoder> encoder =
+ [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
+ encoder.label = @"Snow Render Encoder";
+
+ // 4. Set Pipeline State
+ [encoder setRenderPipelineState:self.pipelineState];
+
+ // 5. Update and Set Uniforms
+ Uniforms * const uniforms = (Uniforms *)[self.uniformBuffer contents];
+ uniforms->time = (float)(CACurrentMediaTime() - _startTime); // Calculate elapsed time
+ uniforms->resolution = (vector_float2){(float)view.drawableSize.width, (float)view.drawableSize.height};
+
+ // Set buffers in indeces matching those defined in the shader
+ [encoder setVertexBuffer:self.uniformBuffer offset:0 atIndex:2];
+ [encoder setVertexBuffer:self.snowflakeDataBuffer offset:0 atIndex:1];
+ [encoder setVertexBuffer:_vertexBuffer offset:0 atIndex:0]; // Assuming buffer(0) for vertices
+
+ [encoder setFragmentBuffer:self.uniformBuffer offset:0 atIndex:0];
+
+ // 6. Draw Call
+ // Draw multiple instances of the quad/point, one for each snowflake.
+ // The vertex shader uses [[instance_id]] to position each one.
+ [encoder drawPrimitives:MTLPrimitiveTypeTriangle // Or MTLPrimitiveTypePoint
+ vertexStart:0
+ vertexCount:6 // 6 vertices for the quad example
+ instanceCount:self.snowflakeCount];
+
+ // 7. End Encoding
+ [encoder endEncoding];
+
+ // 8. Present Drawable
+ if (view.currentDrawable) {
+ [commandBuffer presentDrawable:view.currentDrawable];
+ }
+
+ // 9. Commit Command Buffer
+ [commandBuffer commit];
+ [commandBuffer waitUntilCompleted]; // Optional: Wait for completion (useful for debugging)
+}
+
+ at end
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/6642ae5ef4f0196db272109d36a748e9c634d4f0...b6abafe591117493148655b63256a65cf9616433
--
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/6642ae5ef4f0196db272109d36a748e9c634d4f0...b6abafe591117493148655b63256a65cf9616433
You're receiving this email because of your account on code.videolan.org.
VideoLAN code repository instance
More information about the vlc-commits
mailing list