diff --git a/.gitmodules b/.gitmodules index 4b22b9a..8238d8e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,6 @@ [submodule "contrib/D3D12MemoryAllocator"] path = contrib/D3D12MemoryAllocator url = https://github.com/GPUOpen-LibrariesAndSDKs/D3D12MemoryAllocator +[submodule "contrib/ios-cmake"] + path = contrib/ios-cmake + url = https://github.com/leetal/ios-cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 0789c20..e7d3c20 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,6 +21,7 @@ cmake_dependent_option(PLUME_SDL_VULKAN_ENABLED "Enable SDL Vulkan integration" cmake_dependent_option(PLUME_D3D12_AGILITY_SDK_ENABLED "Enable D3D12 Agility SDK" OFF WIN32 OFF) cmake_dependent_option(PLUME_APPLE_RETINA_ENABLED "Enable Apple Retina display support" ON APPLE ON) option(PLUME_BUILD_EXAMPLES "Build example applications" OFF) +cmake_dependent_option(PLUME_XCODE_TEAM_ID "Xcode team ID for code signing (e.g., ABCDE12345)" "" APPLE OFF) # Windows-specific definitions if(WIN32) @@ -29,7 +30,7 @@ endif() # Enable SDL Vulkan support if(PLUME_SDL_VULKAN_ENABLED) - if (NOT TARGET SDL2::SDL2) + if(NOT TARGET SDL2::SDL2) find_package(SDL2 REQUIRED) else() set(SDL2_INCLUDE_DIRS "$") @@ -50,6 +51,16 @@ message(STATUS "Plume - SDL Vulkan integration: ${PLUME_SDL_VULKAN_ENABLED}") message(STATUS "Plume - D3D12 Agility SDK: ${PLUME_D3D12_AGILITY_SDK_ENABLED}") message(STATUS "Plume - Building examples: ${PLUME_BUILD_EXAMPLES}") +if(APPLE AND PLUME_BUILD_EXAMPLES) + message(STATUS "Plume - Xcode examples configured with automatic code signing") + + if(PLUME_XCODE_TEAM_ID) + message(STATUS "Plume - Xcode Team ID: ${PLUME_XCODE_TEAM_ID}") + else() + message(STATUS "Plume - To set your Xcode Team ID, use: -DPLUME_XCODE_TEAM_ID=") + endif() +endif() + # Basic source files that are always included set(PLUME_SOURCES plume_vulkan.cpp diff --git a/contrib/ios-cmake b/contrib/ios-cmake new file mode 160000 index 0000000..21598aa --- /dev/null +++ b/contrib/ios-cmake @@ -0,0 +1 @@ +Subproject commit 21598aa550701d20654c032328f7e8710a14099b diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index a61f32b..8e36525 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -13,6 +13,17 @@ plume_find_sdl2() include(${CMAKE_SOURCE_DIR}/examples/cmake/PlumeShaders.cmake) plume_shaders_init() +# Load Xcode configuration helper for team ID and code signing +if(APPLE) + include(${CMAKE_SOURCE_DIR}/examples/cmake/modules/PlumeXcodeConfig.cmake) +endif() + # Add example subdirectories add_subdirectory(triangle) add_subdirectory(cube) + +# Apply Xcode configuration to example targets +if(APPLE) + plume_apply_xcode_config(plume_triangle) + plume_apply_xcode_config(plume_cube) +endif() diff --git a/examples/cmake/PlumeShaders.cmake b/examples/cmake/PlumeShaders.cmake index 85cd10e..58e07c7 100644 --- a/examples/cmake/PlumeShaders.cmake +++ b/examples/cmake/PlumeShaders.cmake @@ -15,13 +15,14 @@ include("${CMAKE_CURRENT_LIST_DIR}/modules/PlumeSpirvCross.cmake") function(_plume_embed TARGET_NAME INPUT_FILE VAR_NAME OUTPUT_C OUTPUT_H) plume_build_file_to_c() + plume_get_file_to_c_command(FILE_TO_C_CMD) get_filename_component(OUT_DIR "${OUTPUT_C}" DIRECTORY) file(MAKE_DIRECTORY "${OUT_DIR}") add_custom_command( OUTPUT "${OUTPUT_C}" "${OUTPUT_H}" - COMMAND plume_file_to_c "${INPUT_FILE}" "${VAR_NAME}" "${OUTPUT_C}" "${OUTPUT_H}" + COMMAND ${FILE_TO_C_CMD} "${INPUT_FILE}" "${VAR_NAME}" "${OUTPUT_C}" "${OUTPUT_H}" DEPENDS "${INPUT_FILE}" plume_file_to_c COMMENT "Embedding ${VAR_NAME} from ${INPUT_FILE}" VERBATIM @@ -62,6 +63,7 @@ function(_plume_compile_hlsl_impl TARGET_NAME SHADER_SOURCE SHADER_TYPE OUTPUT_N endif() set(SHADER_MODEL "6_0") + if(ARG_SHADER_MODEL) set(SHADER_MODEL "${ARG_SHADER_MODEL}") endif() @@ -71,10 +73,12 @@ function(_plume_compile_hlsl_impl TARGET_NAME SHADER_SOURCE SHADER_TYPE OUTPUT_N else() set(OUT_DIR "${CMAKE_BINARY_DIR}/shaders") endif() + file(MAKE_DIRECTORY "${OUT_DIR}") set(PROFILE "") set(DXC_TYPE_ARGS "") + if(SHADER_TYPE STREQUAL "vertex") set(PROFILE "vs_${SHADER_MODEL}") set(DXC_TYPE_ARGS "-fvk-invert-y") @@ -91,6 +95,7 @@ function(_plume_compile_hlsl_impl TARGET_NAME SHADER_SOURCE SHADER_TYPE OUTPUT_N endif() set(INCLUDE_FLAGS "") + foreach(DIR ${ARG_INCLUDE_DIRS}) list(APPEND INCLUDE_FLAGS "-I${DIR}") endforeach() @@ -126,12 +131,14 @@ endfunction() function(_plume_compile_spirv_to_metal_impl TARGET_NAME SPIRV_FILE OUTPUT_NAME) cmake_parse_arguments(PARSE_ARGV 3 ARG "" "OUTPUT_DIR" "") + plume_get_spirv_cross_msl_command(SPIRV_CROSS_MSL_CMD) if(ARG_OUTPUT_DIR) set(OUT_DIR "${ARG_OUTPUT_DIR}") else() set(OUT_DIR "${CMAKE_BINARY_DIR}/shaders") endif() + file(MAKE_DIRECTORY "${OUT_DIR}") set(METAL_SOURCE "${OUT_DIR}/${OUTPUT_NAME}.hlsl.metal") @@ -148,7 +155,7 @@ function(_plume_compile_spirv_to_metal_impl TARGET_NAME SPIRV_FILE OUTPUT_NAME) add_custom_command( OUTPUT "${METAL_SOURCE}" - COMMAND plume_spirv_cross_msl "${SPIRV_FILE}" "${METAL_SOURCE}" + COMMAND ${SPIRV_CROSS_MSL_CMD} "${SPIRV_FILE}" "${METAL_SOURCE}" DEPENDS "${SPIRV_FILE}" plume_spirv_cross_msl COMMENT "SPIRV-Cross: ${SPIRV_FILE} -> Metal source" VERBATIM @@ -175,6 +182,8 @@ function(_plume_compile_spirv_to_metal_impl TARGET_NAME SPIRV_FILE OUTPUT_NAME) endfunction() function(_plume_compile_metal_impl TARGET_NAME SHADER_SOURCE OUTPUT_NAME) + plume_get_file_to_c_command(FILE_TO_C_CMD) + set(IR_OUTPUT "${CMAKE_BINARY_DIR}/shaders/${OUTPUT_NAME}.ir") set(METALLIB_OUTPUT "${CMAKE_BINARY_DIR}/shaders/${OUTPUT_NAME}.metallib") set(C_OUTPUT "${CMAKE_BINARY_DIR}/shaders/${OUTPUT_NAME}.metal.c") @@ -204,7 +213,7 @@ function(_plume_compile_metal_impl TARGET_NAME SHADER_SOURCE OUTPUT_NAME) add_custom_command( OUTPUT "${C_OUTPUT}" "${H_OUTPUT}" - COMMAND plume_file_to_c "${METALLIB_OUTPUT}" "${OUTPUT_NAME}BlobMSL" "${C_OUTPUT}" "${H_OUTPUT}" + COMMAND ${FILE_TO_C_CMD} "${METALLIB_OUTPUT}" "${OUTPUT_NAME}BlobMSL" "${C_OUTPUT}" "${H_OUTPUT}" DEPENDS "${METALLIB_OUTPUT}" plume_file_to_c COMMENT "Generating C header for Metal shader ${OUTPUT_NAME}" VERBATIM @@ -215,7 +224,6 @@ function(_plume_compile_metal_impl TARGET_NAME SHADER_SOURCE OUTPUT_NAME) endfunction() # Public API - function(plume_compile_shader TARGET_NAME SHADER_SOURCE SHADER_TYPE OUTPUT_NAME ENTRY_POINT) cmake_parse_arguments(ARG "SPEC_CONSTANTS" "SHADER_MODEL;OUTPUT_DIR" "INCLUDE_DIRS;EXTRA_ARGS" ${ARGN}) @@ -227,15 +235,19 @@ function(plume_compile_shader TARGET_NAME SHADER_SOURCE SHADER_TYPE OUTPUT_NAME endif() elseif(SHADER_EXT MATCHES "\\.hlsl$") set(IMPL_ARGS "") + if(ARG_SHADER_MODEL) list(APPEND IMPL_ARGS SHADER_MODEL "${ARG_SHADER_MODEL}") endif() + if(ARG_INCLUDE_DIRS) list(APPEND IMPL_ARGS INCLUDE_DIRS ${ARG_INCLUDE_DIRS}) endif() + if(ARG_EXTRA_ARGS) list(APPEND IMPL_ARGS EXTRA_ARGS ${ARG_EXTRA_ARGS}) endif() + if(ARG_OUTPUT_DIR) list(APPEND IMPL_ARGS OUTPUT_DIR "${ARG_OUTPUT_DIR}") set(OUT_DIR "${ARG_OUTPUT_DIR}") diff --git a/examples/cmake/modules/PlumeDXC.cmake b/examples/cmake/modules/PlumeDXC.cmake index 9841a52..347ec6c 100644 --- a/examples/cmake/modules/PlumeDXC.cmake +++ b/examples/cmake/modules/PlumeDXC.cmake @@ -3,6 +3,24 @@ include(FetchContent) +function(_plume_get_dxc_host_arch OUT_VAR) + if(CMAKE_CROSSCOMPILING) + set(_plume_arch "${CMAKE_HOST_SYSTEM_PROCESSOR}") + else() + set(_plume_arch "${CMAKE_SYSTEM_PROCESSOR}") + endif() + + string(TOLOWER "${_plume_arch}" _plume_arch_lower) + + if(_plume_arch_lower MATCHES "^(x86_64|amd64)$") + set(${OUT_VAR} "x64" PARENT_SCOPE) + elseif(_plume_arch_lower MATCHES "^(arm64|aarch64)$") + set(${OUT_VAR} "arm64" PARENT_SCOPE) + else() + message(FATAL_ERROR "Unsupported host architecture for DXC: ${_plume_arch}") + endif() +endfunction() + # Set up common DXC options (called regardless of fetch) function(_plume_setup_dxc_options) if(DEFINED PLUME_DXC_COMMON_OPTS) @@ -29,6 +47,8 @@ function(plume_fetch_dxc) ) FetchContent_MakeAvailable(plume_dxc) + _plume_get_dxc_host_arch(PLUME_DXC_HOST_ARCH) + # Set up DXC paths based on platform if(WIN32) set(PLUME_DXC_EXECUTABLE "${plume_dxc_SOURCE_DIR}/bin/x64/dxc.exe" CACHE INTERNAL "DXC executable") @@ -38,17 +58,13 @@ function(plume_fetch_dxc) if(EXISTS "${plume_dxc_SOURCE_DIR}/bin/x64/dxcompiler.dll") configure_file("${plume_dxc_SOURCE_DIR}/bin/x64/dxcompiler.dll" "${CMAKE_BINARY_DIR}/bin/dxcompiler.dll" COPYONLY) endif() + if(EXISTS "${plume_dxc_SOURCE_DIR}/bin/x64/dxil.dll") configure_file("${plume_dxc_SOURCE_DIR}/bin/x64/dxil.dll" "${CMAKE_BINARY_DIR}/bin/dxil.dll" COPYONLY) endif() elseif(APPLE) - if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") - set(PLUME_DXC_EXECUTABLE "${plume_dxc_SOURCE_DIR}/bin/x64/dxc-macos" CACHE INTERNAL "DXC executable") - set(PLUME_DXC_LIB_DIR "${plume_dxc_SOURCE_DIR}/lib/x64" CACHE INTERNAL "DXC library directory") - else() - set(PLUME_DXC_EXECUTABLE "${plume_dxc_SOURCE_DIR}/bin/arm64/dxc-macos" CACHE INTERNAL "DXC executable") - set(PLUME_DXC_LIB_DIR "${plume_dxc_SOURCE_DIR}/lib/arm64" CACHE INTERNAL "DXC library directory") - endif() + set(PLUME_DXC_EXECUTABLE "${plume_dxc_SOURCE_DIR}/bin/${PLUME_DXC_HOST_ARCH}/dxc-macos" CACHE INTERNAL "DXC executable") + set(PLUME_DXC_LIB_DIR "${plume_dxc_SOURCE_DIR}/lib/${PLUME_DXC_HOST_ARCH}" CACHE INTERNAL "DXC library directory") # Ensure executable permission if(EXISTS "${PLUME_DXC_EXECUTABLE}") @@ -58,13 +74,8 @@ function(plume_fetch_dxc) endif() else() # Linux - if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") - set(PLUME_DXC_EXECUTABLE "${plume_dxc_SOURCE_DIR}/bin/x64/dxc-linux" CACHE INTERNAL "DXC executable") - set(PLUME_DXC_LIB_DIR "${plume_dxc_SOURCE_DIR}/lib/x64" CACHE INTERNAL "DXC library directory") - else() - set(PLUME_DXC_EXECUTABLE "${plume_dxc_SOURCE_DIR}/bin/arm64/dxc-linux" CACHE INTERNAL "DXC executable") - set(PLUME_DXC_LIB_DIR "${plume_dxc_SOURCE_DIR}/lib/arm64" CACHE INTERNAL "DXC library directory") - endif() + set(PLUME_DXC_EXECUTABLE "${plume_dxc_SOURCE_DIR}/bin/${PLUME_DXC_HOST_ARCH}/dxc-linux" CACHE INTERNAL "DXC executable") + set(PLUME_DXC_LIB_DIR "${plume_dxc_SOURCE_DIR}/lib/${PLUME_DXC_HOST_ARCH}" CACHE INTERNAL "DXC library directory") # Ensure executable permission if(EXISTS "${PLUME_DXC_EXECUTABLE}") diff --git a/examples/cmake/modules/PlumeFileToC.cmake b/examples/cmake/modules/PlumeFileToC.cmake index 1bd6152..8517ad1 100644 --- a/examples/cmake/modules/PlumeFileToC.cmake +++ b/examples/cmake/modules/PlumeFileToC.cmake @@ -1,6 +1,8 @@ # PlumeFileToC.cmake # Builds the file_to_c tool for embedding binary files as C arrays +include(${CMAKE_CURRENT_LIST_DIR}/PlumeHostTool.cmake) + # Build the file_to_c tool for the host system function(plume_build_file_to_c) if(TARGET plume_file_to_c) @@ -14,31 +16,67 @@ function(plume_build_file_to_c) message(FATAL_ERROR "plume file_to_c.cpp not found at ${FILE_TO_C_SOURCE}") endif() - add_executable(plume_file_to_c ${FILE_TO_C_SOURCE}) - set_target_properties(plume_file_to_c PROPERTIES - RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/plume_tools" - CXX_STANDARD 17 - CXX_STANDARD_REQUIRED ON - ) + if(IOS) + message(STATUS "Plume - Building file_to_c as HOST tool") + + set(HOST_PROJECT_DIR "${CMAKE_BINARY_DIR}/host-tools/file_to_c-src") + set(HOST_BUILD_DIR "${CMAKE_BINARY_DIR}/host-tools/file_to_c-build") + set(HOST_INSTALL_DIR "${CMAKE_BINARY_DIR}/host-tools/install") + + set(_host_file_to_c_cmake [=[ +cmake_minimum_required(VERSION 3.16) +project(plume_host_file_to_c LANGUAGES CXX) + +add_executable(plume_file_to_c "@FILE_TO_C_SOURCE@") +set_target_properties(plume_file_to_c PROPERTIES + CXX_STANDARD 17 + CXX_STANDARD_REQUIRED ON +) +install(TARGETS plume_file_to_c RUNTIME DESTINATION bin) +]=]) + string(REPLACE "@FILE_TO_C_SOURCE@" "${FILE_TO_C_SOURCE}" _host_file_to_c_cmake "${_host_file_to_c_cmake}") + file(WRITE "${HOST_PROJECT_DIR}/CMakeLists.txt" "${_host_file_to_c_cmake}") + plume_add_host_tool(HOST_FILE_TO_C_BIN plume_file_to_c "${FILE_TO_C_SOURCE}" "${HOST_PROJECT_DIR}" "${HOST_BUILD_DIR}" "${HOST_INSTALL_DIR}") - if(APPLE) + set(PLUME_FILE_TO_C_EXECUTABLE "${HOST_FILE_TO_C_BIN}" CACHE INTERNAL "Path to host file_to_c executable") + else() + message(STATUS "Plume - Building file_to_c") + add_executable(plume_file_to_c ${FILE_TO_C_SOURCE}) set_target_properties(plume_file_to_c PROPERTIES - XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "-" + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/plume_tools" + CXX_STANDARD 17 + CXX_STANDARD_REQUIRED ON ) + set(PLUME_FILE_TO_C_EXECUTABLE "$" CACHE INTERNAL "Path to file_to_c executable") + + if(APPLE AND TARGET plume_file_to_c) + set_target_properties(plume_file_to_c PROPERTIES + XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "-" + ) + endif() endif() endfunction() +function(plume_get_file_to_c_command OUT_VAR) + if(NOT DEFINED PLUME_FILE_TO_C_EXECUTABLE) + message(FATAL_ERROR "PLUME_FILE_TO_C_EXECUTABLE not set. Call plume_build_file_to_c() first.") + endif() + + set(${OUT_VAR} "${PLUME_FILE_TO_C_EXECUTABLE}" PARENT_SCOPE) +endfunction() + # Convert a binary file to a C header # Usage: plume_file_to_c_header(INPUT_FILE OUTPUT_C OUTPUT_H VARIABLE_NAME) function(plume_file_to_c_header INPUT_FILE VARIABLE_NAME OUTPUT_C OUTPUT_H) plume_build_file_to_c() + plume_get_file_to_c_command(FILE_TO_C_CMD) get_filename_component(OUTPUT_DIR "${OUTPUT_C}" DIRECTORY) file(MAKE_DIRECTORY "${OUTPUT_DIR}") add_custom_command( OUTPUT "${OUTPUT_C}" "${OUTPUT_H}" - COMMAND plume_file_to_c "${INPUT_FILE}" "${VARIABLE_NAME}" "${OUTPUT_C}" "${OUTPUT_H}" + COMMAND ${FILE_TO_C_CMD} "${INPUT_FILE}" "${VARIABLE_NAME}" "${OUTPUT_C}" "${OUTPUT_H}" DEPENDS "${INPUT_FILE}" plume_file_to_c COMMENT "Generating C header for ${VARIABLE_NAME}" VERBATIM diff --git a/examples/cmake/modules/PlumeHostTool.cmake b/examples/cmake/modules/PlumeHostTool.cmake new file mode 100644 index 0000000..35e5639 --- /dev/null +++ b/examples/cmake/modules/PlumeHostTool.cmake @@ -0,0 +1,61 @@ +# Shared helpers for building host tools from an iOS cross-compile + +function(_plume_get_host_build_type OUT_VAR) + if(CMAKE_BUILD_TYPE) + set(${OUT_VAR} "${CMAKE_BUILD_TYPE}" PARENT_SCOPE) + else() + set(${OUT_VAR} "Release" PARENT_SCOPE) + endif() +endfunction() + +function(_plume_host_tool_env_prefix OUT_VAR) + set(${OUT_VAR} + ${CMAKE_COMMAND} -E env + --unset=SDKROOT + --unset=ARCHS + --unset=IPHONEOS_DEPLOYMENT_TARGET + --unset=MACOSX_DEPLOYMENT_TARGET + --unset=PLATFORM_NAME + --unset=EFFECTIVE_PLATFORM_NAME + --unset=CC + --unset=CXX + --unset=CFLAGS + --unset=CXXFLAGS + --unset=LDFLAGS + PARENT_SCOPE + ) +endfunction() + +function(plume_add_host_tool OUT_HOST_OUTPUT_BIN TARGET_NAME TOOL_SOURCE HOST_PROJECT_DIR HOST_BUILD_DIR HOST_INSTALL_DIR) + _plume_get_host_build_type(HOST_BUILD_TYPE) + _plume_host_tool_env_prefix(HOST_ENV_PREFIX) + + file(MAKE_DIRECTORY "${HOST_PROJECT_DIR}") + + if(CMAKE_HOST_WIN32) + set(HOST_EXE_SUFFIX ".exe") + else() + set(HOST_EXE_SUFFIX "") + endif() + + set(HOST_OUTPUT_PATH "${HOST_INSTALL_DIR}/bin/${TARGET_NAME}${HOST_EXE_SUFFIX}") + + add_custom_command(OUTPUT "${HOST_OUTPUT_PATH}" + COMMAND ${HOST_ENV_PREFIX} + ${CMAKE_COMMAND} -S "${HOST_PROJECT_DIR}" -B "${HOST_BUILD_DIR}" -G "Unix Makefiles" + -DCMAKE_BUILD_TYPE=${HOST_BUILD_TYPE} + -DCMAKE_OSX_SYSROOT=macosx + -DCMAKE_OSX_ARCHITECTURES=${CMAKE_HOST_SYSTEM_PROCESSOR} + COMMAND ${HOST_ENV_PREFIX} + ${CMAKE_COMMAND} --build "${HOST_BUILD_DIR}" --config ${HOST_BUILD_TYPE} --target ${TARGET_NAME} + COMMAND ${CMAKE_COMMAND} --install "${HOST_BUILD_DIR}" --config ${HOST_BUILD_TYPE} --prefix "${HOST_INSTALL_DIR}" + DEPENDS "${TOOL_SOURCE}" + COMMENT "Building host ${TARGET_NAME} tool" + USES_TERMINAL + VERBATIM + ) + + add_custom_target(${TARGET_NAME} DEPENDS "${HOST_OUTPUT_PATH}") + + set(${OUT_HOST_OUTPUT_BIN} "${HOST_OUTPUT_PATH}" PARENT_SCOPE) +endfunction() diff --git a/examples/cmake/modules/PlumeSDL2.cmake b/examples/cmake/modules/PlumeSDL2.cmake index a5d9a69..82f3ed7 100644 --- a/examples/cmake/modules/PlumeSDL2.cmake +++ b/examples/cmake/modules/PlumeSDL2.cmake @@ -6,10 +6,6 @@ include(FetchContent) set(PLUME_SDL2_VERSION "2.30.10" CACHE STRING "SDL2 version for auto-download on Windows") function(plume_find_sdl2) - if(PLUME_SDL2_FOUND) - return() - endif() - if(NOT DEFINED PLUME_SDL2_DIR AND DEFINED ENV{PLUME_SDL2_DIR}) set(PLUME_SDL2_DIR "$ENV{PLUME_SDL2_DIR}") endif() @@ -30,19 +26,19 @@ function(plume_find_sdl2) if(TARGET SDL2::SDL2) if(TARGET SDL2::SDL2main) - set(SDL2_LIBRARIES SDL2::SDL2 SDL2::SDL2main CACHE INTERNAL "") + set(SDL2_LIBRARIES SDL2::SDL2 SDL2::SDL2main PARENT_SCOPE) else() - set(SDL2_LIBRARIES SDL2::SDL2 CACHE INTERNAL "") + set(SDL2_LIBRARIES SDL2::SDL2 PARENT_SCOPE) endif() else() - set(SDL2_LIBRARIES "${SDL2_LIBRARIES}" CACHE INTERNAL "") + set(SDL2_LIBRARIES "${SDL2_LIBRARIES}" PARENT_SCOPE) endif() if(DEFINED SDL2_INCLUDE_DIRS) - set(SDL2_INCLUDE_DIRS "${SDL2_INCLUDE_DIRS}" CACHE INTERNAL "") + set(SDL2_INCLUDE_DIRS "${SDL2_INCLUDE_DIRS}" PARENT_SCOPE) endif() - set(PLUME_SDL2_FOUND TRUE CACHE INTERNAL "") + set(PLUME_SDL2_FOUND TRUE PARENT_SCOPE) return() endif() @@ -61,6 +57,7 @@ function(plume_find_sdl2) FetchContent_MakeAvailable(plume_sdl2) set(_sdl2_src "${plume_sdl2_SOURCE_DIR}") + if(NOT EXISTS "${_sdl2_src}/include" AND EXISTS "${_sdl2_src}/SDL2-${PLUME_SDL2_VERSION}/include") set(_sdl2_src "${_sdl2_src}/SDL2-${PLUME_SDL2_VERSION}") endif() @@ -82,10 +79,14 @@ macro(_plume_sdl2_setup_from_dir _sdl2_root) set(_plume_sdl2_arch "x86") endif() - set(SDL2_INCLUDE_DIRS "${_sdl2_root}/include" CACHE INTERNAL "") - set(SDL2_BINDIR "${_sdl2_root}/lib/${_plume_sdl2_arch}" CACHE INTERNAL "") - set(SDL2_LIBRARIES "${SDL2_BINDIR}/SDL2.lib;${SDL2_BINDIR}/SDL2main.lib" CACHE INTERNAL "") - set(PLUME_SDL2_FOUND TRUE CACHE INTERNAL "") + set(SDL2_INCLUDE_DIRS "${_sdl2_root}/include" PARENT_SCOPE) + set(SDL2_BINDIR "${_sdl2_root}/lib/${_plume_sdl2_arch}" PARENT_SCOPE) + if(WIN32) + set(SDL2_LIBRARIES "${SDL2_BINDIR}/SDL2.lib;${SDL2_BINDIR}/SDL2main.lib" PARENT_SCOPE) + else() + set(SDL2_LIBRARIES "${SDL2_BINDIR}/libSDL2.a;${SDL2_BINDIR}/libSDL2main.a" PARENT_SCOPE) + endif() + set(PLUME_SDL2_FOUND TRUE PARENT_SCOPE) unset(_plume_sdl2_arch) endmacro() diff --git a/examples/cmake/modules/PlumeSpirvCross.cmake b/examples/cmake/modules/PlumeSpirvCross.cmake index 81ce019..8a2b332 100644 --- a/examples/cmake/modules/PlumeSpirvCross.cmake +++ b/examples/cmake/modules/PlumeSpirvCross.cmake @@ -2,6 +2,7 @@ # Fetches and builds SPIRV-Cross from source, then builds our spirv_cross_msl tool include(FetchContent) +include(${CMAKE_CURRENT_LIST_DIR}/PlumeHostTool.cmake) # Build the spirv_cross_msl tool, fetching and compiling SPIRV-Cross if not provided function(plume_fetch_spirv_cross) @@ -9,82 +10,143 @@ function(plume_fetch_spirv_cross) return() endif() - # Use provided paths or fetch and build from source - if(DEFINED PLUME_SPIRV_CROSS_LIB_DIR AND DEFINED PLUME_SPIRV_CROSS_INCLUDE_DIR) - # User provided prebuilt libraries - set(SPIRV_CROSS_LIB_DIR "${PLUME_SPIRV_CROSS_LIB_DIR}") - set(SPIRV_CROSS_INCLUDE_DIR "${PLUME_SPIRV_CROSS_INCLUDE_DIR}") - set(SPIRV_CROSS_USE_PREBUILT TRUE) - else() - # Fetch and build from source - set(SPIRV_CROSS_STATIC ON CACHE BOOL "" FORCE) - set(SPIRV_CROSS_SHARED OFF CACHE BOOL "" FORCE) - set(SPIRV_CROSS_CLI OFF CACHE BOOL "" FORCE) - set(SPIRV_CROSS_ENABLE_TESTS OFF CACHE BOOL "" FORCE) - set(SPIRV_CROSS_ENABLE_GLSL ON CACHE BOOL "" FORCE) - set(SPIRV_CROSS_ENABLE_MSL ON CACHE BOOL "" FORCE) - set(SPIRV_CROSS_ENABLE_HLSL OFF CACHE BOOL "" FORCE) - set(SPIRV_CROSS_ENABLE_CPP OFF CACHE BOOL "" FORCE) - set(SPIRV_CROSS_ENABLE_REFLECT OFF CACHE BOOL "" FORCE) - set(SPIRV_CROSS_ENABLE_UTIL OFF CACHE BOOL "" FORCE) - set(SPIRV_CROSS_ENABLE_C_API OFF CACHE BOOL "" FORCE) - set(SPIRV_CROSS_SKIP_INSTALL ON CACHE BOOL "" FORCE) - - FetchContent_Declare( - spirv_cross - GIT_REPOSITORY https://github.com/KhronosGroup/SPIRV-Cross.git - GIT_TAG vulkan-sdk-1.4.335.0 - GIT_SHALLOW TRUE - ) - FetchContent_MakeAvailable(spirv_cross) - - set(SPIRV_CROSS_INCLUDE_DIR "${spirv_cross_SOURCE_DIR}") - set(SPIRV_CROSS_USE_PREBUILT FALSE) - endif() - - # Build our custom spirv_cross_msl tool set(SPIRV_CROSS_MSL_SOURCE "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../tools/spirv_cross_msl.cpp") if(NOT EXISTS "${SPIRV_CROSS_MSL_SOURCE}") message(FATAL_ERROR "plume spirv_cross_msl.cpp not found at ${SPIRV_CROSS_MSL_SOURCE}") endif() - add_executable(plume_spirv_cross_msl ${SPIRV_CROSS_MSL_SOURCE}) - target_include_directories(plume_spirv_cross_msl PRIVATE ${SPIRV_CROSS_INCLUDE_DIR}) - set_target_properties(plume_spirv_cross_msl PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON) + if(IOS) + message(STATUS "Plume - Building plume_spirv_cross_msl as HOST tool") - if(SPIRV_CROSS_USE_PREBUILT) - # Link against pre-built static libraries - # Order matters: msl depends on glsl depends on core - if(WIN32) - target_link_libraries(plume_spirv_cross_msl PRIVATE - "${SPIRV_CROSS_LIB_DIR}/spirv-cross-msl.lib" - "${SPIRV_CROSS_LIB_DIR}/spirv-cross-glsl.lib" - "${SPIRV_CROSS_LIB_DIR}/spirv-cross-core.lib" + set(HOST_PROJECT_DIR "${CMAKE_BINARY_DIR}/host-tools/spirv_cross_msl-src") + set(HOST_BUILD_DIR "${CMAKE_BINARY_DIR}/host-tools/spirv_cross_msl-build") + set(HOST_INSTALL_DIR "${CMAKE_BINARY_DIR}/host-tools/install") + + set(_host_spirv_cross_cmake [=[ +cmake_minimum_required(VERSION 3.16) +project(plume_host_spirv_cross_msl LANGUAGES CXX) + +include(FetchContent) +set(SPIRV_CROSS_STATIC ON CACHE BOOL "" FORCE) +set(SPIRV_CROSS_SHARED OFF CACHE BOOL "" FORCE) +set(SPIRV_CROSS_CLI OFF CACHE BOOL "" FORCE) +set(SPIRV_CROSS_ENABLE_TESTS OFF CACHE BOOL "" FORCE) +set(SPIRV_CROSS_ENABLE_GLSL ON CACHE BOOL "" FORCE) +set(SPIRV_CROSS_ENABLE_MSL ON CACHE BOOL "" FORCE) +set(SPIRV_CROSS_ENABLE_HLSL OFF CACHE BOOL "" FORCE) +set(SPIRV_CROSS_ENABLE_CPP OFF CACHE BOOL "" FORCE) +set(SPIRV_CROSS_ENABLE_REFLECT OFF CACHE BOOL "" FORCE) +set(SPIRV_CROSS_ENABLE_UTIL OFF CACHE BOOL "" FORCE) +set(SPIRV_CROSS_ENABLE_C_API OFF CACHE BOOL "" FORCE) +set(SPIRV_CROSS_SKIP_INSTALL ON CACHE BOOL "" FORCE) + +FetchContent_Declare( + spirv_cross + GIT_REPOSITORY https://github.com/KhronosGroup/SPIRV-Cross.git + GIT_TAG vulkan-sdk-1.4.335.0 + GIT_SHALLOW TRUE +) +FetchContent_MakeAvailable(spirv_cross) + +add_executable(plume_spirv_cross_msl "@SPIRV_CROSS_MSL_SOURCE@") +target_include_directories(plume_spirv_cross_msl PRIVATE ${spirv_cross_SOURCE_DIR}) +target_link_libraries(plume_spirv_cross_msl PRIVATE + spirv-cross-msl + spirv-cross-glsl + spirv-cross-core +) +set_target_properties(plume_spirv_cross_msl PROPERTIES + CXX_STANDARD 17 + CXX_STANDARD_REQUIRED ON +) +install(TARGETS plume_spirv_cross_msl RUNTIME DESTINATION bin) +]=]) + string(REPLACE "@SPIRV_CROSS_MSL_SOURCE@" "${SPIRV_CROSS_MSL_SOURCE}" _host_spirv_cross_cmake "${_host_spirv_cross_cmake}") + file(WRITE "${HOST_PROJECT_DIR}/CMakeLists.txt" "${_host_spirv_cross_cmake}") + plume_add_host_tool(HOST_SPIRV_CROSS_MSL_BIN plume_spirv_cross_msl "${SPIRV_CROSS_MSL_SOURCE}" "${HOST_PROJECT_DIR}" "${HOST_BUILD_DIR}" "${HOST_INSTALL_DIR}") + + set(PLUME_SPIRV_CROSS_MSL_EXECUTABLE "${HOST_SPIRV_CROSS_MSL_BIN}" CACHE INTERNAL "Path to host plume_spirv_cross_msl executable") + else() + # Use provided paths or fetch and build from source + if(DEFINED PLUME_SPIRV_CROSS_LIB_DIR AND DEFINED PLUME_SPIRV_CROSS_INCLUDE_DIR) + # User provided prebuilt libraries + set(SPIRV_CROSS_LIB_DIR "${PLUME_SPIRV_CROSS_LIB_DIR}") + set(SPIRV_CROSS_INCLUDE_DIR "${PLUME_SPIRV_CROSS_INCLUDE_DIR}") + set(SPIRV_CROSS_USE_PREBUILT TRUE) + else() + # Fetch and build from source + set(SPIRV_CROSS_STATIC ON CACHE BOOL "" FORCE) + set(SPIRV_CROSS_SHARED OFF CACHE BOOL "" FORCE) + set(SPIRV_CROSS_CLI OFF CACHE BOOL "" FORCE) + set(SPIRV_CROSS_ENABLE_TESTS OFF CACHE BOOL "" FORCE) + set(SPIRV_CROSS_ENABLE_GLSL ON CACHE BOOL "" FORCE) + set(SPIRV_CROSS_ENABLE_MSL ON CACHE BOOL "" FORCE) + set(SPIRV_CROSS_ENABLE_HLSL OFF CACHE BOOL "" FORCE) + set(SPIRV_CROSS_ENABLE_CPP OFF CACHE BOOL "" FORCE) + set(SPIRV_CROSS_ENABLE_REFLECT OFF CACHE BOOL "" FORCE) + set(SPIRV_CROSS_ENABLE_UTIL OFF CACHE BOOL "" FORCE) + set(SPIRV_CROSS_ENABLE_C_API OFF CACHE BOOL "" FORCE) + set(SPIRV_CROSS_SKIP_INSTALL ON CACHE BOOL "" FORCE) + + FetchContent_Declare( + spirv_cross + GIT_REPOSITORY https://github.com/KhronosGroup/SPIRV-Cross.git + GIT_TAG vulkan-sdk-1.4.335.0 + GIT_SHALLOW TRUE ) + FetchContent_MakeAvailable(spirv_cross) + + set(SPIRV_CROSS_INCLUDE_DIR "${spirv_cross_SOURCE_DIR}") + set(SPIRV_CROSS_USE_PREBUILT FALSE) + endif() + + add_executable(plume_spirv_cross_msl ${SPIRV_CROSS_MSL_SOURCE}) + target_include_directories(plume_spirv_cross_msl PRIVATE ${SPIRV_CROSS_INCLUDE_DIR}) + set_target_properties(plume_spirv_cross_msl PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON) + + if(SPIRV_CROSS_USE_PREBUILT) + # Link against pre-built static libraries + # Order matters: msl depends on glsl depends on core + if(WIN32) + target_link_libraries(plume_spirv_cross_msl PRIVATE + "${SPIRV_CROSS_LIB_DIR}/spirv-cross-msl.lib" + "${SPIRV_CROSS_LIB_DIR}/spirv-cross-glsl.lib" + "${SPIRV_CROSS_LIB_DIR}/spirv-cross-core.lib" + ) + else() + target_link_libraries(plume_spirv_cross_msl PRIVATE + "${SPIRV_CROSS_LIB_DIR}/libspirv-cross-msl.a" + "${SPIRV_CROSS_LIB_DIR}/libspirv-cross-glsl.a" + "${SPIRV_CROSS_LIB_DIR}/libspirv-cross-core.a" + ) + endif() else() + # Link against freshly built targets target_link_libraries(plume_spirv_cross_msl PRIVATE - "${SPIRV_CROSS_LIB_DIR}/libspirv-cross-msl.a" - "${SPIRV_CROSS_LIB_DIR}/libspirv-cross-glsl.a" - "${SPIRV_CROSS_LIB_DIR}/libspirv-cross-core.a" + spirv-cross-msl + spirv-cross-glsl + spirv-cross-core ) endif() - else() - # Link against freshly built targets - target_link_libraries(plume_spirv_cross_msl PRIVATE - spirv-cross-msl - spirv-cross-glsl - spirv-cross-core - ) - endif() - - set_target_properties(plume_spirv_cross_msl PROPERTIES - RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/plume_tools" - ) - if(APPLE) set_target_properties(plume_spirv_cross_msl PROPERTIES - XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "-" + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/plume_tools" ) + set(PLUME_SPIRV_CROSS_MSL_EXECUTABLE "$" CACHE INTERNAL "Path to plume_spirv_cross_msl executable") + + if(APPLE) + set_target_properties(plume_spirv_cross_msl PROPERTIES + XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "-" + ) + endif() endif() endfunction() + +function(plume_get_spirv_cross_msl_command OUT_VAR) + if(NOT DEFINED PLUME_SPIRV_CROSS_MSL_EXECUTABLE) + message(FATAL_ERROR "PLUME_SPIRV_CROSS_MSL_EXECUTABLE not set. Call plume_fetch_spirv_cross() first.") + endif() + + set(${OUT_VAR} "${PLUME_SPIRV_CROSS_MSL_EXECUTABLE}" PARENT_SCOPE) +endfunction() diff --git a/examples/cmake/modules/PlumeXcodeConfig.cmake b/examples/cmake/modules/PlumeXcodeConfig.cmake new file mode 100644 index 0000000..38c3e8d --- /dev/null +++ b/examples/cmake/modules/PlumeXcodeConfig.cmake @@ -0,0 +1,19 @@ +# Configure Xcode-specific properties for code signing and team ID +# Usage: plume_apply_xcode_config(target_name) + +function(plume_apply_xcode_config TARGET_NAME) + if(NOT APPLE) + return() + endif() + + set_target_properties(${TARGET_NAME} PROPERTIES + XCODE_ATTRIBUTE_CODE_SIGN_STYLE "Automatic" + ) + + if(DEFINED PLUME_XCODE_TEAM_ID AND PLUME_XCODE_TEAM_ID) + set_target_properties(${TARGET_NAME} PROPERTIES + XCODE_ATTRIBUTE_DEVELOPMENT_TEAM "${PLUME_XCODE_TEAM_ID}" + ) + message(STATUS "Plume - Applied Team ID '${PLUME_XCODE_TEAM_ID}' to ${TARGET_NAME}") + endif() +endfunction() diff --git a/examples/cube/CMakeLists.txt b/examples/cube/CMakeLists.txt index 43c824b..81595dd 100644 --- a/examples/cube/CMakeLists.txt +++ b/examples/cube/CMakeLists.txt @@ -8,12 +8,26 @@ target_link_libraries(plume_cube PRIVATE plume ${SDL2_LIBRARIES}) # Platform-specific libraries if(APPLE) - target_link_libraries(plume_cube PRIVATE "-framework Metal -framework QuartzCore -framework CoreGraphics -framework Foundation -framework IOKit") + target_link_libraries(plume_cube PRIVATE "-framework Metal -framework QuartzCore -framework CoreGraphics -framework Foundation") + + if(NOT IOS) + target_link_libraries(plume_cube PRIVATE "-framework IOKit") + endif() set_target_properties(plume_cube PROPERTIES XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "-" XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS ${CMAKE_CURRENT_SOURCE_DIR}/../plume.entitlements ) + + if(IOS) + set_target_properties(plume_cube PROPERTIES + MACOSX_BUNDLE TRUE + MACOSX_BUNDLE_GUI_IDENTIFIER "io.github.renderbag.plume_cube" + MACOSX_BUNDLE_BUNDLE_NAME "Plume Cube" + MACOSX_BUNDLE_BUNDLE_VERSION "1.0" + MACOSX_BUNDLE_SHORT_VERSION_STRING "1.0" + ) + endif() elseif(WIN32) target_link_libraries(plume_cube PRIVATE d3d12 dxgi) diff --git a/examples/cube/main.cpp b/examples/cube/main.cpp index 9668fe5..0f5a716 100644 --- a/examples/cube/main.cpp +++ b/examples/cube/main.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -524,7 +525,12 @@ namespace plume { SDL_VERSION(&wmInfo.version); SDL_GetWindowWMInfo(window, &wmInfo); SDL_MetalView view = SDL_Metal_CreateView(window); - createContext(ctx, renderInterface, { wmInfo.info.cocoa.window, SDL_Metal_GetLayer(view) }, apiName); +#if TARGET_OS_OSX + auto windowHandle = wmInfo.info.cocoa.window; +#else + auto windowHandle = wmInfo.info.uikit.window; +#endif + createContext(ctx, renderInterface, { windowHandle, SDL_Metal_GetLayer(view) }, apiName); #elif defined(WIN32) SDL_SysWMinfo wmInfo; SDL_VERSION(&wmInfo.version); diff --git a/examples/triangle/CMakeLists.txt b/examples/triangle/CMakeLists.txt index 4b416c8..230f4cb 100644 --- a/examples/triangle/CMakeLists.txt +++ b/examples/triangle/CMakeLists.txt @@ -8,12 +8,26 @@ target_link_libraries(plume_triangle PRIVATE plume ${SDL2_LIBRARIES}) # Platform-specific libraries if(APPLE) - target_link_libraries(plume_triangle PRIVATE "-framework Metal -framework QuartzCore -framework CoreGraphics -framework Foundation -framework IOKit") + target_link_libraries(plume_triangle PRIVATE "-framework Metal -framework QuartzCore -framework CoreGraphics -framework Foundation") + + if(NOT IOS) + target_link_libraries(plume_triangle PRIVATE "-framework IOKit") + endif() set_target_properties(plume_triangle PROPERTIES XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "-" XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS ${CMAKE_CURRENT_SOURCE_DIR}/../plume.entitlements ) + + if(IOS) + set_target_properties(plume_triangle PROPERTIES + MACOSX_BUNDLE TRUE + MACOSX_BUNDLE_GUI_IDENTIFIER "io.github.renderbag.plume_triangle" + MACOSX_BUNDLE_BUNDLE_NAME "Plume Triangle" + MACOSX_BUNDLE_BUNDLE_VERSION "1.0" + MACOSX_BUNDLE_SHORT_VERSION_STRING "1.0" + ) + endif() elseif(WIN32) target_link_libraries(plume_triangle PRIVATE d3d12 dxgi) diff --git a/examples/triangle/main.cpp b/examples/triangle/main.cpp index fc34404..0cc59ae 100644 --- a/examples/triangle/main.cpp +++ b/examples/triangle/main.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -310,7 +311,12 @@ namespace plume { SDL_VERSION(&wmInfo.version); SDL_GetWindowWMInfo(window, &wmInfo); SDL_MetalView view = SDL_Metal_CreateView(window); - createContext(ctx, renderInterface, { wmInfo.info.cocoa.window, SDL_Metal_GetLayer(view) }, apiName); +#if TARGET_OS_OSX + auto windowHandle = wmInfo.info.cocoa.window; +#else + auto windowHandle = wmInfo.info.uikit.window; +#endif + createContext(ctx, renderInterface, { windowHandle, SDL_Metal_GetLayer(view) }, apiName); #elif defined(WIN32) SDL_SysWMinfo wmInfo; SDL_VERSION(&wmInfo.version); @@ -432,4 +438,4 @@ int main(int argc, char* argv[]) { SDL_DestroyWindow(window); SDL_Quit(); return 0; -} +} \ No newline at end of file diff --git a/plume_apple.h b/plume_apple.h index 731d3c4..fb11b3a 100644 --- a/plume_apple.h +++ b/plume_apple.h @@ -14,25 +14,25 @@ namespace plume { RenderDeviceVendor getRenderDeviceVendor(uint64_t registryID); - struct CocoaWindowAttributes { + struct AppleWindowAttributes { int x, y; int width, height; }; - class CocoaWindow { + class AppleWindow { void* windowHandle; - CocoaWindowAttributes cachedAttributes; + AppleWindowAttributes cachedAttributes; std::atomic cachedRefreshRate; mutable std::mutex attributesMutex; void updateWindowAttributesInternal(bool forceSync = false); void updateRefreshRateInternal(bool forceSync = false); public: - CocoaWindow(void* window); - ~CocoaWindow(); + AppleWindow(void* window); + ~AppleWindow(); // Get cached window attributes, may trigger async update - void getWindowAttributes(CocoaWindowAttributes* attributes) const; + void getWindowAttributes(AppleWindowAttributes* attributes) const; // Get cached refresh rate, may trigger async update int getRefreshRate() const; diff --git a/plume_apple.mm b/plume_apple.mm index 1384fe9..fca47a2 100644 --- a/plume_apple.mm +++ b/plume_apple.mm @@ -6,11 +6,16 @@ // #include "plume_apple.h" - +#include +#if TARGET_OS_OSX #import -#import #import +#else +#import +#endif +#import +#if TARGET_OS_OSX static uint32_t plumeGetEntryProperty(io_registry_entry_t entry, CFStringRef propertyName) { uint32_t value = 0; CFTypeRef cfProp = IORegistryEntrySearchCFProperty(entry, kIOServicePlane, propertyName, kCFAllocatorDefault, kIORegistryIterateRecursively | kIORegistryIterateParents); @@ -27,9 +32,10 @@ static uint32_t plumeGetEntryProperty(io_registry_entry_t entry, CFStringRef pro return value; } - +#endif namespace plume { RenderDeviceVendor getRenderDeviceVendor(uint64_t registryID) { + #if TARGET_OS_OSX io_service_t entry = IOServiceGetMatchingService(MACH_PORT_NULL, IORegistryEntryIDMatching(registryID)); if (entry) { @@ -42,10 +48,14 @@ RenderDeviceVendor getRenderDeviceVendor(uint64_t registryID) { } IOObjectRelease(entry); // Release the entry if we couldn't get parent } - + return RenderDeviceVendor::UNKNOWN; + #else + return RenderDeviceVendor::APPLE; + #endif } +#if TARGET_OS_OSX CGFloat getScaleFactor(NSWindow *nsWindow) { #ifdef PLUME_APPLE_RETINA_ENABLED return [nsWindow backingScaleFactor]; @@ -53,25 +63,40 @@ CGFloat getScaleFactor(NSWindow *nsWindow) { return 1.0f; #endif } +#else + CGFloat getScaleFactor(UIWindow *uiWindow) { +#ifdef PLUME_APPLE_RETINA_ENABLED + return [uiWindow screen].scale; +#else + return 1.0f; +#endif + } +#endif - // MARK: - CocoaWindow + // MARK: - AppleWindow - CocoaWindow::CocoaWindow(void* window) + AppleWindow::AppleWindow(void* window) : windowHandle(window), cachedRefreshRate(0) { cachedAttributes = {0, 0, 0, 0}; if ([NSThread isMainThread]) { + #if TARGET_OS_OSX NSWindow *nsWindow = (__bridge NSWindow *)windowHandle; NSRect contentFrame = [[nsWindow contentView] frame]; CGFloat scaleFactor = getScaleFactor(nsWindow); - + NSScreen *screen = [nsWindow screen]; + #else + UIWindow *uiWindow = (__bridge UIWindow *)windowHandle; + CGRect contentFrame = uiWindow.bounds; + CGFloat scaleFactor = getScaleFactor(uiWindow); + UIScreen *screen = [uiWindow screen]; + #endif cachedAttributes.x = (int)round(contentFrame.origin.x); cachedAttributes.y = (int)round(contentFrame.origin.y); cachedAttributes.width = (int)round(contentFrame.size.width * scaleFactor); cachedAttributes.height = (int)round(contentFrame.size.height * scaleFactor); - - NSScreen *screen = [nsWindow screen]; - if (@available(macOS 12.0, *)) { + + if (@available(macOS 12.0, iOS 10.3, *)) { cachedRefreshRate.store((int)[screen maximumFramesPerSecond]); } } else { @@ -80,14 +105,20 @@ CGFloat getScaleFactor(NSWindow *nsWindow) { } } - CocoaWindow::~CocoaWindow() {} + AppleWindow::~AppleWindow() {} - void CocoaWindow::updateWindowAttributesInternal(bool forceSync) { + void AppleWindow::updateWindowAttributesInternal(bool forceSync) { auto updateBlock = ^{ + #if TARGET_OS_OSX NSWindow *nsWindow = (__bridge NSWindow *)windowHandle; NSRect contentFrame = [[nsWindow contentView] frame]; CGFloat scaleFactor = getScaleFactor(nsWindow); - + #else + UIWindow *uiWindow = (__bridge UIWindow *)windowHandle; + CGRect contentFrame = uiWindow.bounds; + CGFloat scaleFactor = getScaleFactor(uiWindow); + #endif + std::lock_guard lock(attributesMutex); cachedAttributes.x = (int)round(contentFrame.origin.x); cachedAttributes.y = (int)round(contentFrame.origin.y); @@ -102,13 +133,21 @@ CGFloat getScaleFactor(NSWindow *nsWindow) { } } - void CocoaWindow::updateRefreshRateInternal(bool forceSync) { + void AppleWindow::updateRefreshRateInternal(bool forceSync) { auto updateBlock = ^{ + #if TARGET_OS_OSX NSWindow *nsWindow = (__bridge NSWindow *)windowHandle; NSScreen *screen = [nsWindow screen]; if (@available(macOS 12.0, *)) { cachedRefreshRate.store((int)[screen maximumFramesPerSecond]); } + #else + UIWindow *uiWindow = (__bridge UIWindow *)windowHandle; + UIScreen *screen = [uiWindow screen]; + if (@available(iOS 10.3, *)) { + cachedRefreshRate.store((int)[screen maximumFramesPerSecond]); + } + #endif }; if (forceSync) { @@ -118,18 +157,24 @@ CGFloat getScaleFactor(NSWindow *nsWindow) { } } - void CocoaWindow::getWindowAttributes(CocoaWindowAttributes* attributes) const { - if ([NSThread isMainThread]) { + void AppleWindow::getWindowAttributes(AppleWindowAttributes* attributes) const { + if ([NSThread isMainThread]) { + #if TARGET_OS_OSX NSWindow *nsWindow = (__bridge NSWindow *)windowHandle; NSRect contentFrame = [[nsWindow contentView] frame]; CGFloat scaleFactor = getScaleFactor(nsWindow); + #else + UIWindow* nsWindow = (__bridge UIWindow *)windowHandle; + CGRect contentFrame = nsWindow.bounds; + CGFloat scaleFactor = getScaleFactor(nsWindow); + #endif { std::lock_guard lock(attributesMutex); - const_cast(this)->cachedAttributes.x = (int)round(contentFrame.origin.x); - const_cast(this)->cachedAttributes.y = (int)round(contentFrame.origin.y); - const_cast(this)->cachedAttributes.width = (int)round(contentFrame.size.width * scaleFactor); - const_cast(this)->cachedAttributes.height = (int)round(contentFrame.size.height * scaleFactor); + const_cast(this)->cachedAttributes.x = (int)round(contentFrame.origin.x); + const_cast(this)->cachedAttributes.y = (int)round(contentFrame.origin.y); + const_cast(this)->cachedAttributes.width = (int)round(contentFrame.size.width * scaleFactor); + const_cast(this)->cachedAttributes.height = (int)round(contentFrame.size.height * scaleFactor); *attributes = cachedAttributes; } @@ -139,32 +184,42 @@ CGFloat getScaleFactor(NSWindow *nsWindow) { *attributes = cachedAttributes; } - const_cast(this)->updateWindowAttributesInternal(false); + const_cast(this)->updateWindowAttributesInternal(false); } } - int CocoaWindow::getRefreshRate() const { + int AppleWindow::getRefreshRate() const { if ([NSThread isMainThread]) { + #if TARGET_OS_OSX NSWindow *nsWindow = (__bridge NSWindow *)windowHandle; NSScreen *screen = [nsWindow screen]; if (@available(macOS 12.0, *)) { int freshRate = (int)[screen maximumFramesPerSecond]; - const_cast(this)->cachedRefreshRate.store(freshRate); + const_cast(this)->cachedRefreshRate.store(freshRate); return freshRate; } - + #else + UIWindow *uiWindow = (__bridge UIWindow *)windowHandle; + UIScreen *screen = [uiWindow screen]; + if (@available(iOS 10.3, *)) { + int freshRate = (int)[screen maximumFramesPerSecond]; + const_cast(this)->cachedRefreshRate.store(freshRate); + return freshRate; + } + #endif return cachedRefreshRate.load(); } else { int rate = cachedRefreshRate.load(); - const_cast(this)->updateRefreshRateInternal(false); + const_cast(this)->updateRefreshRateInternal(false); return rate; } } - void CocoaWindow::toggleFullscreen() { + void AppleWindow::toggleFullscreen() { + #if TARGET_OS_OSX if ([NSThread isMainThread]) { NSWindow *nsWindow = (__bridge NSWindow *)windowHandle; [nsWindow toggleFullScreen:NULL]; @@ -174,5 +229,6 @@ CGFloat getScaleFactor(NSWindow *nsWindow) { [nsWindow toggleFullScreen:NULL]; }); } + #endif } } diff --git a/plume_metal.cpp b/plume_metal.cpp index 2abe67c..400df1b 100644 --- a/plume_metal.cpp +++ b/plume_metal.cpp @@ -1916,7 +1916,7 @@ namespace plume { // Metal supports a maximum of 3 drawables. this->drawables.resize(MAX_DRAWABLES); - this->windowWrapper = std::make_unique(desc.renderWindow.window); + this->windowWrapper = std::make_unique(desc.renderWindow.window); getWindowSize(width, height); // Set the layer's drawable size to match the window size @@ -2100,7 +2100,7 @@ namespace plume { void MetalSwapChain::getWindowSize(uint32_t &dstWidth, uint32_t &dstHeight) const { MetalAutoreleasePool releasePool; - CocoaWindowAttributes attributes; + AppleWindowAttributes attributes; windowWrapper->getWindowAttributes(&attributes); dstWidth = attributes.width; dstHeight = attributes.height; @@ -3779,6 +3779,7 @@ namespace plume { this->renderInterface = renderInterface; // Device Selection +#if TARGET_OS_MAC NS::Array* devices = MTL::CopyAllDevices(); MTL::Device *preferredDevice = nullptr; for (NS::UInteger i = 0; i < devices->count(); i++) { @@ -3793,13 +3794,22 @@ namespace plume { mtl = preferredDevice ? preferredDevice : MTL::CreateSystemDefaultDevice(); mtl->retain(); devices->release(); +#else + mtl = MTL::CreateSystemDefaultDevice(); + mtl->retain(); +#endif const std::string deviceName(mtl->name()->utf8String()); description.name = deviceName; - description.type = mapDeviceType(mtl->location()); description.driverVersion = 1; // Unavailable - description.vendor = mtl->supportsFamily(MTL::GPUFamilyApple1) ? RenderDeviceVendor::APPLE : getRenderDeviceVendor(mtl->registryID()); description.dedicatedVideoMemory = mtl->recommendedMaxWorkingSetSize(); + #if TARGET_OS_MAC + description.vendor = mtl->supportsFamily(MTL::GPUFamilyApple1) ? RenderDeviceVendor::APPLE : getRenderDeviceVendor(mtl->registryID()); + description.type = mapDeviceType(mtl->location()); + #else + description.type = RenderDeviceType::INTEGRATED; + description.vendor = mtl->supportsFamily(MTL::GPUFamilyApple1) ? RenderDeviceVendor::APPLE : RenderDeviceVendor::UNKNOWN; + #endif timestampCounterSet = findTimestampCounterSet(); if (timestampCounterSet != nullptr) { @@ -4191,11 +4201,18 @@ namespace plume { capabilities.shaderFormat = RenderShaderFormat::METAL; // Fill device names. + #if TARGET_OS_MAC const NS::Array* devices = MTL::CopyAllDevices(); for (NS::UInteger i = 0; i < devices->count(); i++) { NS::String* deviceName = ((MTL::Device *)devices->object(i))->name(); deviceNames.push_back(std::string(deviceName->utf8String())); } + #else + MTL::Device* defaultDevice = MTL::CreateSystemDefaultDevice(); + if (defaultDevice) { + deviceNames.push_back(std::string(defaultDevice->name()->utf8String())); + } + #endif } MetalInterface::~MetalInterface() {} diff --git a/plume_metal.h b/plume_metal.h index 59ae129..671db0f 100644 --- a/plume_metal.h +++ b/plume_metal.h @@ -238,7 +238,7 @@ namespace plume { uint32_t refreshRate = 0; std::vector drawables; uint32_t currentAvailableDrawableIndex = 0; - std::unique_ptr windowWrapper; + std::unique_ptr windowWrapper; // Present wait uint64_t currentPresentId = 0; diff --git a/plume_vulkan.cpp b/plume_vulkan.cpp index f1dd503..1f3c7ad 100644 --- a/plume_vulkan.cpp +++ b/plume_vulkan.cpp @@ -2132,7 +2132,7 @@ namespace plume { assert(desc.renderWindow.window != 0); assert(desc.renderWindow.view != 0); // Creates a wrapper around the window for storing and fetching sizes. - this->windowWrapper = std::make_unique(desc.renderWindow.window); + this->windowWrapper = std::make_unique(desc.renderWindow.window); VkMetalSurfaceCreateInfoEXT surfaceCreateInfo = {}; surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT; @@ -2475,7 +2475,7 @@ namespace plume { dstWidth = attributes.width; dstHeight = attributes.height; # elif defined(__APPLE__) - CocoaWindowAttributes attributes; + AppleWindowAttributes attributes; windowWrapper->getWindowAttributes(&attributes); dstWidth = attributes.width; dstHeight = attributes.height; diff --git a/plume_vulkan.h b/plume_vulkan.h index 0a1edbe..0cb25ef 100644 --- a/plume_vulkan.h +++ b/plume_vulkan.h @@ -227,7 +227,7 @@ namespace plume { VulkanCommandQueue *commandQueue = nullptr; VkSurfaceKHR surface = VK_NULL_HANDLE; #if defined(__APPLE__) - std::unique_ptr windowWrapper; + std::unique_ptr windowWrapper; #endif uint64_t presentCount = 0; uint32_t width = 0;