diff --git a/.github/workflows/presubmit.yaml b/.github/workflows/presubmit.yaml index 79e42298e..ac368f37e 100644 --- a/.github/workflows/presubmit.yaml +++ b/.github/workflows/presubmit.yaml @@ -79,10 +79,43 @@ jobs: //host:gfxstream_backend_static //host:gfxstream_backend_shared //host:gfxstream_backend - # Nothing is run yet: this job exists to keep the macOS host build - # compiling. Tests need a graphics driver environment that macOS does not - # have here yet. - run-tests: false + # Use the default host driver environment: SwiftShader can not be built on + # macOS. Vulkan-backed tests instead run against MoltenVK (installed via + # brew), pointed at by the ANDROID_EMU_VK_LOADER_PATH / VK_*_FILES env + # vars below. + graphics-drivers: host + run-tests: true + # The GLES framebuffer test needs the ANGLE GLES driver and runs in a + # separate job below, as do the end2end tests (which additionally need the + # mesa guest driver and the kumquat server built on top of it). + test-targets: >- + //host:gfxstream_vsyncthread_tests + //host/vulkan:vk_format_utils_tests + //host/vulkan:gfxstream_emulatedphysicalmemory_tests + //host/vulkan:gfxstream_emulatedphysicalqueue_tests + //host/vulkan:vk_common_operations_tests + //host/vulkan:gfxstream_displayvk_tests + //host/vulkan:gfxstream_compositorvk_tests + test-bazel-args: >- + --test_env=ANDROID_EMU_VK_LOADER_PATH=/opt/homebrew/lib/libvulkan.dylib + --test_env=VK_DRIVER_FILES=/opt/homebrew/etc/vulkan/icd.d/MoltenVK_icd.json + --test_env=VK_ICD_FILENAMES=/opt/homebrew/etc/vulkan/icd.d/MoltenVK_icd.json + + # GLES tests use ANGLE (built on macOS) for the EGL/GLES driver, running on top + # of MoltenVK for Vulkan. This uses the gles_angle_vulkan_moltenvk test + # environment, which stages the ANGLE libraries and the system Vulkan loader + # together and points them at MoltenVK. + run-gfxstream-bazel-gles-build-macos: + uses: ./.github/workflows/presubmit_bazel.yml + with: + runner: macos-26 + build-targets: //host:gfxstream_framebuffer_tests + graphics-drivers: gles_angle_vulkan_moltenvk + run-tests: true + test-targets: //host:gfxstream_framebuffer_tests + test-bazel-args: >- + --test_env=GFXSTREAM_TESTING_VULKAN_LOADER=/opt/homebrew/lib/libvulkan.1.dylib + --test_env=GFXSTREAM_TESTING_VULKAN_ICD=/opt/homebrew/etc/vulkan/icd.d/MoltenVK_icd.json run-gfxstream-cmake-build: runs-on: ubuntu-22.04 diff --git a/.github/workflows/presubmit_bazel.yml b/.github/workflows/presubmit_bazel.yml index a972cb79f..c80573289 100644 --- a/.github/workflows/presubmit_bazel.yml +++ b/.github/workflows/presubmit_bazel.yml @@ -25,6 +25,31 @@ on: type: boolean default: true + # Which graphics drivers/test environment to use. Defaults to the ANGLE + + # SwiftShader environment. Platforms without a buildable SwiftShader (e.g. + # macOS) can override this with `host`. + graphics-drivers: + type: string + default: 'gles_angle_vulkan_swiftshader' + + # Extra flags passed only to the `bazel test` step (e.g. --test_env for + # pointing the tests at a Vulkan driver). Not passed to `bazel build`. + test-bazel-args: + type: string + default: '' + + # Which test targets to run when run-tests is true. Platforms that can only + # run a subset (e.g. macOS) can override this with a curated list. + test-targets: + type: string + default: >- + host:gfxstream_framebuffer_tests + host/vulkan:gfxstream_compositorvk_tests + host/vulkan:gfxstream_emulatedphysicalmemory_tests + host/vulkan:vk_common_operations_tests + host/vulkan:vk_format_utils_tests + tests/end2end:gfxstream_end2end_tests + permissions: contents: read @@ -57,9 +82,12 @@ jobs: if: runner.os == 'Linux' run: sudo apt-get install -y libvulkan1 - - name: Install bazel (macOS) + - name: Install dependencies (macOS) if: runner.os == 'macOS' - run: brew install bazelisk + # molten-vk provides a Vulkan-on-Metal driver and vulkan-loader provides + # libvulkan, which together give the host-driver tests a Vulkan + # implementation to run against. + run: brew install bazelisk molten-vk vulkan-loader - name: Load cache config run: cat .config/cache-config.env >> $GITHUB_ENV @@ -82,10 +110,11 @@ jobs: env: BUILD_TARGETS: ${{ inputs.build-targets }} ADDITIONAL_BAZEL_ARGS: ${{ inputs.additional-bazel-args }} + GRAPHICS_DRIVERS: ${{ inputs.graphics-drivers }} run: | bazel build $BUILD_TARGETS \ --disk_cache=$HOME/bazel-disk-cache \ - --graphics_drivers=gles_angle_vulkan_swiftshader \ + --graphics_drivers=$GRAPHICS_DRIVERS \ --verbose_failures \ $ADDITIONAL_BAZEL_ARGS @@ -93,16 +122,20 @@ jobs: if: ${{ inputs.run-tests }} env: ADDITIONAL_BAZEL_ARGS: ${{ inputs.additional-bazel-args }} + GRAPHICS_DRIVERS: ${{ inputs.graphics-drivers }} + TEST_TARGETS: ${{ inputs.test-targets }} + TEST_BAZEL_ARGS: ${{ inputs.test-bazel-args }} run: | + # The variables below are deliberately unquoted so that the shell splits + # them into separate arguments. Disable globbing so that a value such as + # --test_arg=--gtest_filter=*Foo* is passed through literally rather than + # being expanded against the files in the workspace. + set -f bazel test \ --disk_cache=$HOME/bazel-disk-cache \ - --graphics_drivers=gles_angle_vulkan_swiftshader \ + --graphics_drivers=$GRAPHICS_DRIVERS \ --test_output=streamed \ --verbose_failures \ - host:gfxstream_framebuffer_tests \ - host/vulkan:gfxstream_compositorvk_tests \ - host/vulkan:gfxstream_emulatedphysicalmemory_tests \ - host/vulkan:vk_common_operations_tests \ - host/vulkan:vk_format_utils_tests \ - tests/end2end:gfxstream_end2end_tests \ + $TEST_TARGETS \ + $TEST_BAZEL_ARGS \ $ADDITIONAL_BAZEL_ARGS diff --git a/common/testenv/BUILD.bazel b/common/testenv/BUILD.bazel index 8533dcd13..be0d887e0 100644 --- a/common/testenv/BUILD.bazel +++ b/common/testenv/BUILD.bazel @@ -14,6 +14,7 @@ string_flag( "host", "gles_angle_vulkan_lavapipe", "gles_angle_vulkan_swiftshader", + "gles_angle_vulkan_moltenvk", ], ) @@ -38,6 +39,13 @@ config_setting( }, ) +config_setting( + name = "graphics_test_environment_gles_angle_vulkan_moltenvk", + flag_values = { + ":graphics_test_environment": "gles_angle_vulkan_moltenvk", + }, +) + copy_to_directory( name = "graphics_test_environment_drivers", srcs = select({ @@ -55,6 +63,12 @@ copy_to_directory( "@angle//:libGLESv2", "@swiftshader//:vk_swiftshader", ], + # macOS: ANGLE for GLES, running on top of the system MoltenVK Vulkan + # driver (provided at runtime, not as a Bazel artifact). + ":graphics_test_environment_gles_angle_vulkan_moltenvk": [ + "@angle//:libEGL", + "@angle//:libGLESv2", + ], "//conditions:default": [ # Default does not change anything and uses host drivers. ], @@ -85,6 +99,9 @@ cc_library( ":graphics_test_environment_gles_angle_vulkan_swiftshader": [ ":graphics_test_environment_drivers", ], + ":graphics_test_environment_gles_angle_vulkan_moltenvk": [ + ":graphics_test_environment_drivers", + ], "//conditions:default": [], }), defines = select({ @@ -96,6 +113,10 @@ cc_library( "GFXSTREAM_TESTING_USE_GLES_ANGLE", "GFXSTREAM_TESTING_USE_VULKAN_SWIFTSHADER", ], + ":graphics_test_environment_gles_angle_vulkan_moltenvk": [ + "GFXSTREAM_TESTING_USE_GLES_ANGLE", + "GFXSTREAM_TESTING_USE_VULKAN_MOLTENVK", + ], "//conditions:default": [ # Default does not change anything and uses host drivers. ], @@ -108,6 +129,9 @@ cc_library( ":graphics_test_environment_gles_angle_vulkan_swiftshader": [ "@rules_cc//cc/runfiles", ], + ":graphics_test_environment_gles_angle_vulkan_moltenvk": [ + "@rules_cc//cc/runfiles", + ], "//conditions:default": [], }) + [ "//common/base:gfxstream_common_base", diff --git a/common/testenv/graphics_test_environment.cpp b/common/testenv/graphics_test_environment.cpp index 6d2c48171..f753c1828 100644 --- a/common/testenv/graphics_test_environment.cpp +++ b/common/testenv/graphics_test_environment.cpp @@ -19,6 +19,10 @@ #include #include +#if defined(__APPLE__) +#include +#endif + #ifdef BAZEL_CURRENT_REPOSITORY #include #endif @@ -95,14 +99,21 @@ bool SetupGraphicsTestEnvironment() { // gfxstream::base::setEnvironmentVariable("__EGL_VENDOR_LIBRARY_FILENAMES", driverEglIcd); // // For now, assume the ANGLE libs are directly used: - const auto driverGlesOpt = GetGraphicsDriverPath("libGLESv2.so"); +#if defined(__APPLE__) + static constexpr const char* kGlesDriverBasename = "libGLESv2.dylib"; + static constexpr const char* kEglDriverBasename = "libEGL.dylib"; +#else + static constexpr const char* kGlesDriverBasename = "libGLESv2.so"; + static constexpr const char* kEglDriverBasename = "libEGL.so"; +#endif + const auto driverGlesOpt = GetGraphicsDriverPath(kGlesDriverBasename); if (!driverGlesOpt) { - GFXSTREAM_ERROR("Failed to find libGLESv2.so."); + GFXSTREAM_ERROR("Failed to find %s.", kGlesDriverBasename); return false; } - const auto driverEglOpt = GetGraphicsDriverPath("libEGL.so"); + const auto driverEglOpt = GetGraphicsDriverPath(kEglDriverBasename); if (!driverEglOpt) { - GFXSTREAM_ERROR("Failed to find libEGL.so"); + GFXSTREAM_ERROR("Failed to find %s", kEglDriverBasename); return false; } const std::filesystem::path driverEgl = *driverEglOpt; @@ -111,6 +122,67 @@ bool SetupGraphicsTestEnvironment() { const std::string currentLdLibraryPath = gfxstream::base::getEnvironmentVariable("LD_LIBRARY_PATH"); const std::string updatedLdLibraryPath = driverDirectory.string() + ":" + currentLdLibraryPath; gfxstream::base::setEnvironmentVariable("LD_LIBRARY_PATH", updatedLdLibraryPath); + +#if defined(GFXSTREAM_TESTING_USE_VULKAN_MOLTENVK) + // ANGLE searches its own module directory for the Vulkan loader, and + // DYLD_LIBRARY_PATH is unavailable under macOS System Integrity Protection. + // The Bazel output directory holding the ANGLE libraries is read-only, so + // stage the ANGLE libraries and the system Vulkan loader together in a + // writable directory and load ANGLE from there. The loader path and the + // MoltenVK ICD are provided by the caller (see CI). + const std::string vulkanLoaderSource = + gfxstream::base::getEnvironmentVariable("GFXSTREAM_TESTING_VULKAN_LOADER"); + if (vulkanLoaderSource.empty()) { + GFXSTREAM_ERROR("GFXSTREAM_TESTING_VULKAN_LOADER is not set for the MoltenVK environment."); + return false; + } + std::error_code stagingError; + const std::filesystem::path temporaryDirectory = + std::filesystem::temp_directory_path(stagingError); + if (stagingError) { + GFXSTREAM_ERROR("Failed to find a temporary directory: %s.", + stagingError.message().c_str()); + return false; + } + const std::filesystem::path stagingDirectory = + temporaryDirectory / ("gfxstream_moltenvk_drivers_" + std::to_string(getpid())); + std::filesystem::create_directories(stagingDirectory, stagingError); + if (stagingError) { + GFXSTREAM_ERROR("Failed to create staging directory %s: %s.", + stagingDirectory.string().c_str(), stagingError.message().c_str()); + return false; + } + // The staged copies inherit the read-only permissions of their Bazel source, + // so a later call (SetupGraphicsTestEnvironment runs once per test) cannot + // truncate them to overwrite. Remove any existing destination first, which + // also keeps staging idempotent across tests in the same process. + const auto stageInto = [&](const std::filesystem::path& source, + const std::filesystem::path& destination) -> bool { + std::error_code removeError; + std::filesystem::remove(destination, removeError); + std::error_code copyError; + std::filesystem::copy_file(source, destination, + std::filesystem::copy_options::overwrite_existing, copyError); + if (copyError) { + GFXSTREAM_ERROR("Failed to stage %s into %s: %s.", source.string().c_str(), + destination.string().c_str(), copyError.message().c_str()); + return false; + } + return true; + }; + const auto stageFile = [&](const std::filesystem::path& source) -> bool { + return stageInto(source, stagingDirectory / source.filename()); + }; + if (!stageFile(*driverGlesOpt) || !stageFile(driverEgl)) { + return false; + } + // Stage the loader under the name ANGLE looks for. + if (!stageInto(vulkanLoaderSource, stagingDirectory / "libvulkan.dylib")) { + return false; + } + gfxstream::base::setEnvironmentVariable( + "LD_LIBRARY_PATH", stagingDirectory.string() + ":" + updatedLdLibraryPath); +#endif // defined(GFXSTREAM_TESTING_USE_VULKAN_MOLTENVK) #else GFXSTREAM_INFO("GraphicsTestEnvironment: not changing host EGL/GLES driver configuration."); #endif // defined(GFXSTREAM_TESTING_USE_GLES_ANGLE) @@ -149,6 +221,19 @@ bool SetupGraphicsTestEnvironment() { const std::string driverLavapipeIcd = driverLavapipeIcdOpt->string(); gfxstream::base::setEnvironmentVariable("VK_DRIVER_FILES", driverLavapipeIcd); gfxstream::base::setEnvironmentVariable("VK_ICD_FILENAMES", driverLavapipeIcd); +#elif defined(GFXSTREAM_TESTING_USE_VULKAN_MOLTENVK) + GFXSTREAM_INFO("GraphicsTestEnvironment: configuring MoltenVK as the Vulkan driver."); + + // MoltenVK is provided by the system (e.g. Homebrew), not as a build + // artifact. The caller provides the ICD via GFXSTREAM_TESTING_VULKAN_ICD. + const std::string moltenVkIcd = + gfxstream::base::getEnvironmentVariable("GFXSTREAM_TESTING_VULKAN_ICD"); + if (moltenVkIcd.empty()) { + GFXSTREAM_ERROR("GFXSTREAM_TESTING_VULKAN_ICD is not set for the MoltenVK environment."); + return false; + } + gfxstream::base::setEnvironmentVariable("VK_DRIVER_FILES", moltenVkIcd); + gfxstream::base::setEnvironmentVariable("VK_ICD_FILENAMES", moltenVkIcd); #else GFXSTREAM_INFO("GraphicsTestEnvironment: not changing host Vulkan driver configuration."); #endif // defined(GFXSTREAM_TESTING_USE_VULKAN_LAVAPIPE) || defined(GFXSTREAM_TESTING_USE_VULKAN_SWIFTSHADER) @@ -161,6 +246,8 @@ bool IsGraphicsTestEnvironmentProvidingVulkanDriver() { return true; #elif defined(GFXSTREAM_TESTING_USE_VULKAN_SWIFTSHADER) return true; +#elif defined(GFXSTREAM_TESTING_USE_VULKAN_MOLTENVK) + return true; #else return false; #endif diff --git a/host/library/shared_library.cpp b/host/library/shared_library.cpp index f8736fe56..12fcb29a5 100644 --- a/host/library/shared_library.cpp +++ b/host/library/shared_library.cpp @@ -185,6 +185,25 @@ SharedLibrary* SharedLibrary::do_open(const char* libraryName, libraryName, libPath); lib = dlopen(libPath, RTLD_NOW); } + if (lib == NULL) { + // DYLD_LIBRARY_PATH is stripped from the environment in some contexts + // (e.g. macOS System Integrity Protection), so a bare dlopen can not find + // libraries there. Search LD_LIBRARY_PATH manually with full paths, which + // is not subject to that stripping. + const std::vector libraryPaths = + gfxstream::Split(gfxstream::base::getEnvironmentVariable("LD_LIBRARY_PATH"), ":"); + for (const std::string& libraryPath : libraryPaths) { + if (libraryPath.empty()) { + continue; + } + const std::string fullpath = PathUtils::join(libraryPath, libPath); + GFXSTREAM_VERBOSE("Calling dlopen on %s.", fullpath.c_str()); + lib = dlopen(fullpath.c_str(), RTLD_NOW); + if (lib != nullptr) { + break; + } + } + } #else GFXSTREAM_VERBOSE("SharedLibrary::open for [%s] (posix,linux): call dlopen on [%s]", libraryName, libPath); void* lib = nullptr; diff --git a/host/testlibs/oswindow/BUILD.bazel b/host/testlibs/oswindow/BUILD.bazel index a4297aa4e..6798b393a 100644 --- a/host/testlibs/oswindow/BUILD.bazel +++ b/host/testlibs/oswindow/BUILD.bazel @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -load("@rules_cc//cc:defs.bzl", "cc_library") +load("@rules_cc//cc:defs.bzl", "cc_library", "objc_library") load("//:build_variables.bzl", "GFXSTREAM_HOST_COPTS", "GFXSTREAM_HOST_DEFINES") package( @@ -20,6 +20,43 @@ package( default_visibility = ["//:gfxstream"], ) +# The macOS OSWindow implementation (CreateOSWindow) is Objective-C++, which +# cc_library can not compile, so it lives in a dedicated objc_library that the +# main library depends on when targeting macOS. +objc_library( + name = "gfxstream_oswindow_test_support_darwin", + testonly = True, + srcs = [ + "osx/OSXWindow.h", + "osx/OSXWindow.mm", + ], + # OSXWindow.mm uses manual reference counting, so disable ARC. + copts = GFXSTREAM_HOST_COPTS + ["-fno-objc-arc"], + defines = GFXSTREAM_HOST_DEFINES, + # OSXWindow.mm includes "osx/OSXWindow.h" relative to this package. + includes = ["."], + sdk_frameworks = [ + "AppKit", + "QuartzCore", + ], + target_compatible_with = [ + "@platforms//os:macos", + ], + deps = [ + ":gfxstream_oswindow_test_support_headers", + "//common/logging:gfxstream_common_logging", + "//third_party/opengl:gfxstream_egl_headers", + ], + alwayslink = True, +) + +cc_library( + name = "gfxstream_oswindow_test_support_headers", + testonly = True, + hdrs = glob(["include/**/*.h"]), + strip_include_prefix = "include", +) + cc_library( name = "gfxstream_oswindow_test_support", testonly = True, @@ -27,7 +64,6 @@ cc_library( "OSWindow.cpp", ] + select({ "@platforms//os:macos": [ - "osx/OSXWindow.h", ], "@platforms//os:windows": [ "windows/Windows_system_utils.cpp", @@ -58,5 +94,10 @@ cc_library( deps = [ "//common/logging:gfxstream_common_logging", "//third_party/opengl:gfxstream_egl_headers", - ], + ] + select({ + "@platforms//os:macos": [ + ":gfxstream_oswindow_test_support_darwin", + ], + "//conditions:default": [], + }), ) diff --git a/host/vulkan/compositor_vk_unittest.cpp b/host/vulkan/compositor_vk_unittest.cpp index f119a7d25..ffbadeb74 100644 --- a/host/vulkan/compositor_vk_unittest.cpp +++ b/host/vulkan/compositor_vk_unittest.cpp @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -339,11 +340,20 @@ class CompositorVkTest : public ::testing::Test { .engineVersion = VK_MAKE_VERSION(1, 0, 0), .apiVersion = VK_API_VERSION_1_1, }; + std::vector instanceExtensions; + VkInstanceCreateFlags instanceCreateFlags = 0; +#if defined(__APPLE__) + // MoltenVK is a portability driver; the Vulkan loader only enumerates it + // when the portability enumeration extension and flag are set. + instanceExtensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); + instanceCreateFlags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR; +#endif const VkInstanceCreateInfo instanceCi = { .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, + .flags = instanceCreateFlags, .pApplicationInfo = &appInfo, - .enabledExtensionCount = 0, - .ppEnabledExtensionNames = nullptr, + .enabledExtensionCount = static_cast(instanceExtensions.size()), + .ppEnabledExtensionNames = instanceExtensions.data(), }; ASSERT_NE(k_vk->vkCreateInstance, nullptr); ASSERT_EQ(k_vk->vkCreateInstance(&instanceCi, nullptr, &m_vkInstance), VK_SUCCESS); @@ -385,6 +395,25 @@ class CompositorVkTest : public ::testing::Test { FAIL() << "Can't find a suitable VkPhysicalDevice."; } +#if defined(__APPLE__) + bool deviceSupportsExtension(const char* extensionName) { + uint32_t extensionCount = 0; + if (k_vk->vkEnumerateDeviceExtensionProperties(m_vkPhysicalDevice, nullptr, &extensionCount, + nullptr) != VK_SUCCESS) { + return false; + } + std::vector extensions(extensionCount); + if (k_vk->vkEnumerateDeviceExtensionProperties(m_vkPhysicalDevice, nullptr, &extensionCount, + extensions.data()) != VK_SUCCESS) { + return false; + } + return std::any_of(extensions.begin(), extensions.end(), + [extensionName](const VkExtensionProperties& extension) { + return strcmp(extension.extensionName, extensionName) == 0; + }); + } +#endif + void createLogicalDevice() { const float queuePriority = 1.0f; const VkDeviceQueueCreateInfo queueCi = { @@ -397,14 +426,27 @@ class CompositorVkTest : public ::testing::Test { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, .pNext = nullptr, }; + std::vector deviceExtensions; +#if defined(__APPLE__) + // A portability driver such as MoltenVK only exposes the Vulkan + // portability subset, which must be enabled explicitly when creating the + // device. Requesting it on a driver that does not advertise it fails with + // VK_ERROR_EXTENSION_NOT_PRESENT, so only enable it when it is present. + // The extension name macro is gated behind VK_ENABLE_BETA_EXTENSIONS, so + // use the literal string. + static constexpr const char* kPortabilitySubset = "VK_KHR_portability_subset"; + if (deviceSupportsExtension(kPortabilitySubset)) { + deviceExtensions.push_back(kPortabilitySubset); + } +#endif const VkDeviceCreateInfo deviceCi = { .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, .pNext = &features, .queueCreateInfoCount = 1, .pQueueCreateInfos = &queueCi, .enabledLayerCount = 0, - .enabledExtensionCount = 0, - .ppEnabledExtensionNames = nullptr, + .enabledExtensionCount = static_cast(deviceExtensions.size()), + .ppEnabledExtensionNames = deviceExtensions.data(), }; ASSERT_EQ(k_vk->vkCreateDevice(m_vkPhysicalDevice, &deviceCi, nullptr, &m_vkDevice), VK_SUCCESS); diff --git a/host/vulkan/vulkan_dispatch.cpp b/host/vulkan/vulkan_dispatch.cpp index 4a64b48df..d27a7b5c8 100644 --- a/host/vulkan/vulkan_dispatch.cpp +++ b/host/vulkan/vulkan_dispatch.cpp @@ -119,6 +119,16 @@ static void initIcdPaths(bool forTesting) { #endif if (androidIcd == "") { // Rely on user to set VK_DRIVER_FILES +#ifdef __APPLE__ + // The external driver selected via VK_DRIVER_FILES on macOS is normally + // MoltenVK. Apply the MoltenVK argument-buffer workaround here too: with + // Metal argument buffers enabled, pipelines that bind an immutable YCbCr + // sampler (the YUV compositor pipelines) fail SPIR-V-to-MSL conversion + // ("argument buffer resource base type could not be determined") and are + // never built, which later aborts compositing a YUV layer. MVK_CONFIG_* + // variables are ignored by non-MoltenVK drivers. See b/364055067. + gfxstream::base::setEnvironmentVariable("MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS", "0"); +#endif return; } diff --git a/third_party/angle/BUILD.angle.bazel b/third_party/angle/BUILD.angle.bazel index b28fcfd89..14a9beb34 100644 --- a/third_party/angle/BUILD.angle.bazel +++ b/third_party/angle/BUILD.angle.bazel @@ -1,11 +1,50 @@ load("@bazel_skylib//lib:selects.bzl", "selects") -load("@rules_cc//cc:defs.bzl", "cc_library", "cc_shared_library") +load("@rules_cc//cc:defs.bzl", "cc_library", "cc_shared_library", "objc_library") load("@rules_python//python:defs.bzl", "py_binary") package( default_visibility = ["@//:gfxstream"], ) +# The Vulkan window-system-integration (display/surface) sources are +# platform-specific. On Linux these are compiled directly; on macOS the +# equivalents are Objective-C++ and are built in the `angle_apple` objc_library +# below (cc_library can not compile .mm files). +ANGLE_VULKAN_LINUX_WSI_SRCS = [ + "src/libANGLE/renderer/vulkan/linux/DeviceVkLinux.cpp", + "src/libANGLE/renderer/vulkan/linux/DisplayVkLinux.cpp", + "src/libANGLE/renderer/vulkan/linux/DisplayVkOffscreen.cpp", + "src/libANGLE/renderer/vulkan/linux/DmaBufImageSiblingVkLinux.cpp", + "src/libANGLE/renderer/vulkan/linux/display/DisplayVkSimple.cpp", + "src/libANGLE/renderer/vulkan/linux/display/WindowSurfaceVkSimple.cpp", + "src/libANGLE/renderer/vulkan/linux/headless/DisplayVkHeadless.cpp", + "src/libANGLE/renderer/vulkan/linux/headless/WindowSurfaceVkHeadless.cpp", +] + +# Linux-only local_defines. On macOS ANGLE selects its platform via the +# compiler-provided __APPLE__ / ANGLE_PLATFORM_APPLE macros, uses the Metal/ +# IOSurface Vulkan display (not the Linux vulkan-display), and has no X11. +ANGLE_PLATFORM_LOCAL_DEFINES = select({ + "@platforms//os:macos": [], + "//conditions:default": [ + "ANGLE_IS_LINUX", + "ANGLE_USE_VULKAN_DISPLAY", + "ANGLE_VULKAN_DISPLAY_MODE_OFFSCREEN", + "EGL_NO_X11", + ], +}) + +# Frameworks required by ANGLE's macOS Objective-C++ sources. +ANGLE_APPLE_SDK_FRAMEWORKS = [ + "AppKit", + "Cocoa", + "CoreGraphics", + "IOKit", + "IOSurface", + "Metal", + "QuartzCore", +] + cc_library( name = "angle_common", srcs = [ @@ -31,13 +70,21 @@ cc_library( "src/common/platform_helpers.cpp", "src/common/string_utils.cpp", "src/common/system_utils.cpp", - "src/common/system_utils_linux.cpp", "src/common/system_utils_posix.cpp", "src/common/third_party/xxhash/xxhash.c", "src/common/tls.cpp", "src/common/uniform_type_info_autogen.cpp", "src/common/utilities.cpp", - ], + ] + select({ + "@platforms//os:macos": [ + "src/common/gl/cgl/FunctionsCGL.cpp", + "src/common/system_utils_apple.cpp", + "src/common/system_utils_mac.cpp", + ], + "//conditions:default": [ + "src/common/system_utils_linux.cpp", + ], + }), hdrs = [ "src/common/BinaryStream.h", "src/common/CircularBuffer.h", @@ -103,7 +150,15 @@ cc_library( "src/common/utilities.h", "src/common/vector_utils.h", "src/libANGLE/CLBitField.h", - ], + ] + select({ + "@platforms//os:macos": [ + "src/common/apple/ObjCPtr.h", + "src/common/apple/SoftLinking.h", + "src/common/apple/apple_platform.h", + "src/common/gl/cgl/FunctionsCGL.h", + ], + "//conditions:default": [], + }), defines = [ # TODO: use bazel `select({})` on debug build condition. # Useful for debugging: @@ -211,6 +266,9 @@ cc_library( "@platforms//os:linux": [ "third_party/glslang/src/glslang/OSDependent/Unix/ossource.cpp", ], + "@platforms//os:macos": [ + "third_party/glslang/src/glslang/OSDependent/Unix/ossource.cpp", + ], "@platforms//os:windows": [ "third_party/glslang/src/glslang/OSDependent/Windows/ossource.cpp", ], @@ -475,9 +533,14 @@ cc_library( name = "angle_gpu_info_util", srcs = [ "src/gpu_info_util/SystemInfo.cpp", - "src/gpu_info_util/SystemInfo_linux.cpp", "src/gpu_info_util/SystemInfo_vulkan.cpp", - ], + ] + select({ + # macOS SystemInfo (.mm) is built in :angle_apple. + "@platforms//os:macos": [], + "//conditions:default": [ + "src/gpu_info_util/SystemInfo_linux.cpp", + ], + }), hdrs = [ "src/gpu_info_util/SystemInfo.h", "src/gpu_info_util/SystemInfo_internal.h", @@ -676,6 +739,12 @@ cc_library( hdrs = [ "src/third_party/volk/volk.h", ], + # On macOS volk must define the Metal surface entry points (e.g. + # vkCreateMetalSurfaceEXT) that the mac WSI calls. + local_defines = select({ + "@platforms//os:macos": ["VK_USE_PLATFORM_METAL_EXT"], + "//conditions:default": [], + }), includes = [ "src/third_party/volk", "third_party/vulkan-headers/src/include", @@ -1156,7 +1225,6 @@ cc_library( ], linkstatic = True, local_defines = [ - 'ANGLE_DISPATCH_LIBRARY=\\\"libGLESv2.so\\\"', 'ANGLE_EGL_LIBRARY_NAME=\\"libEGL\\"', "ANGLE_ENABLE_CONTEXT_MUTEX=1", "ANGLE_ENABLE_SHARE_CONTEXT_LOCK=1", @@ -1175,7 +1243,16 @@ cc_library( "GL_GLES_PROTOTYPES=1", "GL_GLEXT_PROTOTYPES", "LIBEGL_IMPLEMENTATION", - ], + ] + select({ + # ANGLE appends the platform shared-library extension, so the name must + # not already include one (libGLESv2.dylib on macOS, libGLESv2.so else). + "@platforms//os:macos": [ + 'ANGLE_DISPATCH_LIBRARY=\\\"libGLESv2\\\"', + ], + "//conditions:default": [ + 'ANGLE_DISPATCH_LIBRARY=\\\"libGLESv2.so\\\"', + ], + }), deps = [ ":angle_common", ":includes", @@ -2112,14 +2189,6 @@ cc_library( "src/libANGLE/renderer/vulkan/VkImageImageSiblingVk.cpp", "src/libANGLE/renderer/vulkan/VulkanSecondaryCommandBuffer.cpp", "src/libANGLE/renderer/vulkan/android/vk_android_utils.cpp", - "src/libANGLE/renderer/vulkan/linux/DeviceVkLinux.cpp", - "src/libANGLE/renderer/vulkan/linux/DisplayVkLinux.cpp", - "src/libANGLE/renderer/vulkan/linux/DisplayVkOffscreen.cpp", - "src/libANGLE/renderer/vulkan/linux/DmaBufImageSiblingVkLinux.cpp", - "src/libANGLE/renderer/vulkan/linux/display/DisplayVkSimple.cpp", - "src/libANGLE/renderer/vulkan/linux/display/WindowSurfaceVkSimple.cpp", - "src/libANGLE/renderer/vulkan/linux/headless/DisplayVkHeadless.cpp", - "src/libANGLE/renderer/vulkan/linux/headless/WindowSurfaceVkHeadless.cpp", "src/libANGLE/renderer/vulkan/spv_utils.cpp", "src/libANGLE/renderer/vulkan/vk_cache_utils.cpp", "src/libANGLE/renderer/vulkan/vk_caps_utils.cpp", @@ -2132,7 +2201,11 @@ cc_library( "src/libANGLE/renderer/vulkan/vk_renderer.cpp", "src/libANGLE/renderer/vulkan/vk_resource.cpp", "src/libANGLE/renderer/vulkan/vk_utils.cpp", - ], + ] + select({ + # macOS uses the Metal/IOSurface Vulkan WSI, built in :angle_apple. + "@platforms//os:macos": [], + "//conditions:default": ANGLE_VULKAN_LINUX_WSI_SRCS, + }), hdrs = [ "src/libANGLE/renderer/vulkan/AllocatorHelperPool.h", "src/libANGLE/renderer/vulkan/AllocatorHelperRing.h", @@ -2387,19 +2460,98 @@ cc_library( alwayslink = True, ) +# ANGLE's macOS Objective-C++ sources: apple platform utilities, GPU info, and +# the Metal/IOSurface Vulkan window-system-integration. These can not live in the +# cc_library targets above (cc_library rejects .mm), so they are compiled here and +# linked into the final libEGL/libGLESv2 shared libraries. +objc_library( + name = "angle_apple", + srcs = [ + "src/common/apple_platform_utils.mm", + "src/gpu_info_util/SystemInfo_apple.mm", + "src/gpu_info_util/SystemInfo_macos.mm", + "src/libANGLE/renderer/driver_utils_mac.mm", + "src/libANGLE/renderer/vulkan/mac/DisplayVkMac.h", + "src/libANGLE/renderer/vulkan/mac/DisplayVkMac.mm", + "src/libANGLE/renderer/vulkan/mac/IOSurfaceSurfaceVkMac.h", + "src/libANGLE/renderer/vulkan/mac/IOSurfaceSurfaceVkMac.mm", + "src/libANGLE/renderer/vulkan/mac/WindowSurfaceVkMac.h", + "src/libANGLE/renderer/vulkan/mac/WindowSurfaceVkMac.mm", + ], + # ANGLE's Objective-C++ uses manual reference counting. The -D flags mirror + # the local_defines of the cc_library targets whose headers these sources use + # (objc_library has no local_defines attribute). + copts = [ + "-fno-objc-arc", + "-DANGLE_ENABLE_CONTEXT_MUTEX=1", + "-DANGLE_ENABLE_CRC_FOR_PIPELINE_CACHE", + "-DANGLE_ENABLE_SHARE_CONTEXT_LOCK=1", + "-DANGLE_ENABLE_VULKAN", + "-DANGLE_ENABLE_VULKAN_SHARED_RING_BUFFER_CMD_ALLOC=false", + "-DANGLE_OUTSIDE_WEBKIT", + "-DANGLE_PLATFORM_EXPORT=", + "-DANGLE_SHARED_LIBVULKAN=1", + "-DANGLE_USE_CUSTOM_VULKAN_OUTSIDE_RENDER_PASS_CMD_BUFFERS=1", + "-DANGLE_USE_CUSTOM_VULKAN_RENDER_PASS_CMD_BUFFERS=1", + "-DLIBANGLE_IMPLEMENTATION", + # Enable the Vulkan Metal surface extension (VkMetalSurfaceCreateInfoEXT, + # VK_EXT_METAL_SURFACE_EXTENSION_NAME, CAMetalLayer) used by the mac WSI. + "-DVK_USE_PLATFORM_METAL_EXT", + ], + includes = [ + "include/", + "src/", + "src/common/base/", + "src/common/third_party/xxhash", + "third_party/spirv-headers/src/include/", + "third_party/vulkan-headers/src/include", + ], + sdk_frameworks = ANGLE_APPLE_SDK_FRAMEWORKS, + target_compatible_with = [ + "@platforms//os:macos", + ], + deps = [ + ":angle_common", + ":angle_gpu_info_util", + ":angle_image_util", + ":angle_libvulkan_loader", + ":angle_vk_mem_alloc_wrapper", + ":angle_volk", + ":angle_vulkan_backend", + ":angle_vulkan_entry_points", + ":angle_vulkan_headers", + ":libangle_headers", + ":vulkan", + "@abseil-cpp//absl/container:flat_hash_map", + "@abseil-cpp//absl/container:flat_hash_set", + ], + alwayslink = True, +) + cc_shared_library( name = "libEGL", - additional_linker_inputs = [ - "@//third_party/angle:libEGL_angle.lds", - ], - shared_lib_name = "libEGL.so", - user_link_flags = [ - "-ldl", - "-Wl,--gc-sections", - "-Wl,-rpath=$$ORIGIN", - "-Wl,--version-script=$(location @//third_party/angle:libEGL_angle.lds)", - ] + selects.with_or({ + additional_linker_inputs = select({ + "@platforms//os:macos": [], + "//conditions:default": ["@//third_party/angle:libEGL_angle.lds"], + }), + shared_lib_name = select({ + "@platforms//os:macos": "libEGL.dylib", + "//conditions:default": "libEGL.so", + }), + user_link_flags = select({ + "@platforms//os:macos": [ + "-Wl,-dead_strip", + "-Wl,-rpath,@loader_path", + ], + "//conditions:default": [ + "-ldl", + "-Wl,--gc-sections", + "-Wl,-rpath=$$ORIGIN", + "-Wl,--version-script=$(location @//third_party/angle:libEGL_angle.lds)", + ], + }) + selects.with_or({ ( + "@platforms//os:macos", "@//toolchain/bazel:asan_build", "@//toolchain/bazel:msan_build", "@//toolchain/bazel:ubsan_build", @@ -2410,22 +2562,38 @@ cc_shared_library( }), deps = [ ":libegl", - ], + ] + select({ + "@platforms//os:macos": [ + ":angle_apple", + ], + "//conditions:default": [], + }), ) cc_shared_library( name = "libGLESv2", - additional_linker_inputs = [ - "@//third_party/angle:libGLESv2_angle.lds", - ], - shared_lib_name = "libGLESv2.so", - user_link_flags = [ - "-ldl", - "-Wl,--gc-sections", - "-Wl,-rpath=$$ORIGIN", - "-Wl,--version-script=$(location @//third_party/angle:libGLESv2_angle.lds)", - ] + selects.with_or({ + additional_linker_inputs = select({ + "@platforms//os:macos": [], + "//conditions:default": ["@//third_party/angle:libGLESv2_angle.lds"], + }), + shared_lib_name = select({ + "@platforms//os:macos": "libGLESv2.dylib", + "//conditions:default": "libGLESv2.so", + }), + user_link_flags = select({ + "@platforms//os:macos": [ + "-Wl,-dead_strip", + "-Wl,-rpath,@loader_path", + ], + "//conditions:default": [ + "-ldl", + "-Wl,--gc-sections", + "-Wl,-rpath=$$ORIGIN", + "-Wl,--version-script=$(location @//third_party/angle:libGLESv2_angle.lds)", + ], + }) + selects.with_or({ ( + "@platforms//os:macos", "@//toolchain/bazel:asan_build", "@//toolchain/bazel:msan_build", "@//toolchain/bazel:ubsan_build", @@ -2436,5 +2604,10 @@ cc_shared_library( }), deps = [ ":libglesv2", - ], + ] + select({ + "@platforms//os:macos": [ + ":angle_apple", + ], + "//conditions:default": [], + }), )