cmake_minimum_required(VERSION 3.16) project(demo64k LANGUAGES C CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) #-- - Configuration Options -- - option(DEMO_SIZE_OPT "Enable size optimization flags" OFF) option(DEMO_STRIP_ALL "Strip all unnecessary code for final build" OFF) option(DEMO_BUILD_TESTS "Build tests" OFF) option(DEMO_BUILD_TOOLS "Build tools" OFF) option(DEMO_ENABLE_COVERAGE "Enable code coverage generation (macOS only)" OFF) option(DEMO_ALL_OPTIONS "Activate all options at once" OFF) if (DEMO_ALL_OPTIONS) set(DEMO_SIZE_OPT ON) set(DEMO_STRIP_ALL ON) set(DEMO_BUILD_TESTS ON) set(DEMO_BUILD_TOOLS ON) endif() if (DEMO_STRIP_ALL) add_definitions(-DSTRIP_ALL) set(DEMO_SIZE_OPT ON) endif() if (DEMO_ENABLE_COVERAGE AND APPLE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --coverage") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage") # Coverage requires debug info set(CMAKE_BUILD_TYPE Debug) endif() #-- - Platform Detection and Core Setup -- - if(APPLE) add_definitions(-DGLFW_EXPOSE_NATIVE_COCOA) elseif(WIN32) add_definitions(-DGLFW_EXPOSE_NATIVE_WIN32) elseif(UNIX) if(DEFINED CMAKE_USE_WAYLAND) add_definitions(-DGLFW_EXPOSE_NATIVE_WAYLAND) else() add_definitions(-DGLFW_EXPOSE_NATIVE_X11) endif() endif() #-- - Dependencies & Includes -- - set(CORE_INCLUDES src third_party) if (DEMO_CROSS_COMPILE_WIN32) add_definitions(-DDEMO_CROSS_COMPILE_WIN32) set(WINDOWS_DEPS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/windows") set(WGPU_INCLUDE_DIR "${WINDOWS_DEPS_DIR}/include") set(WGPU_LIBRARY "${WINDOWS_DEPS_DIR}/lib/libwgpu_native.dll.a") set(GLFW3_INCLUDE_DIR "${WINDOWS_DEPS_DIR}/include") set(GLFW3_LIBRARY "${WINDOWS_DEPS_DIR}/lib/libglfw3.a") list(APPEND CORE_INCLUDES ${WGPU_INCLUDE_DIR} ${WGPU_INCLUDE_DIR}/webgpu ${GLFW3_INCLUDE_DIR}) set(DEMO_LIBS ${GLFW3_LIBRARY} ${WGPU_LIBRARY} -lgdi32 -lws2_32 -luser32 -lkernel32 -lshell32 -ladvapi32 -ldwmapi) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libgcc -static-libstdc++") else() find_library(WGPU_LIBRARY NAMES wgpu_native libwgpu_native REQUIRED) find_path(WGPU_ROOT_INCLUDE_DIR NAMES wgpu.h REQUIRED) list(APPEND CORE_INCLUDES ${WGPU_ROOT_INCLUDE_DIR} ${WGPU_ROOT_INCLUDE_DIR}/webgpu-headers) find_package(glfw3 REQUIRED) set(DEMO_LIBS glfw ${WGPU_LIBRARY}) endif() list(APPEND CORE_INCLUDES third_party/glfw3webgpu) include_directories(${CORE_INCLUDES}) if (APPLE) set_source_files_properties(src/platform.cc third_party/glfw3webgpu/glfw3webgpu.c PROPERTIES COMPILE_FLAGS "-x objective-c++") list(APPEND DEMO_LIBS "-framework Metal" "-framework Foundation" "-framework Cocoa" "-framework QuartzCore") elseif (NOT DEMO_CROSS_COMPILE_WIN32) list(APPEND DEMO_LIBS pthread m dl) endif() #-- - Source Groups -- - set(AUDIO_SOURCES src/audio/audio.cc src/audio/miniaudio_backend.cc src/audio/gen.cc src/audio/fdct.cc src/audio/idct.cc src/audio/window.cc src/audio/synth.cc src/audio/tracker.cc) set(PROCEDURAL_SOURCES src/procedural/generator.cc) set(GPU_SOURCES src/gpu/gpu.cc src/gpu/effect.cc src/gpu/effects/heptagon_effect.cc src/gpu/effects/particles_effect.cc src/gpu/effects/passthrough_effect.cc src/gpu/effects/moving_ellipse_effect.cc src/gpu/effects/particle_spray_effect.cc src/gpu/effects/gaussian_blur_effect.cc src/gpu/effects/solarize_effect.cc src/gpu/effects/distort_effect.cc src/gpu/effects/chroma_aberration_effect.cc src/gpu/effects/post_process_helper.cc src/gpu/effects/shaders.cc src/gpu/effects/hybrid_3d_effect.cc src/gpu/effects/shader_composer.cc src/gpu/texture_manager.cc ) set(3D_SOURCES src/3d/renderer.cc src/3d/visual_debug.cc ) set(PLATFORM_SOURCES src/platform.cc third_party/glfw3webgpu/glfw3webgpu.c) set(UTIL_SOURCES src/util/asset_manager.cc) #-- - Subsystem Libraries -- - add_library(util STATIC ${UTIL_SOURCES}) add_dependencies(util generate_demo_assets generate_test_assets) add_library(procedural STATIC ${PROCEDURAL_SOURCES}) add_library(audio STATIC ${AUDIO_SOURCES}) add_library(3d STATIC ${3D_SOURCES}) add_library(gpu STATIC ${GPU_SOURCES}) target_include_directories(util PUBLIC ${CORE_INCLUDES}) target_include_directories(procedural PUBLIC ${CORE_INCLUDES}) target_include_directories(audio PUBLIC ${CORE_INCLUDES}) target_include_directories(3d PUBLIC ${CORE_INCLUDES}) target_include_directories(gpu PUBLIC ${CORE_INCLUDES}) # Note: Static libraries do not strictly need to link dependencies, # but if they did, PRIVATE would propagate to the executable. # We will link them in the executable to be explicit and avoid order issues. #-- - Tools Setup -- - if (DEFINED ASSET_PACKER_PATH) set(ASSET_PACKER_CMD ${ASSET_PACKER_PATH}) set(ASSET_PACKER_DEPENDS ${ASSET_PACKER_PATH}) else() add_executable(asset_packer tools/asset_packer.cc) target_link_libraries(asset_packer PRIVATE procedural) set(ASSET_PACKER_CMD $) set(ASSET_PACKER_DEPENDS asset_packer) endif() if (DEFINED SEQ_COMPILER_PATH) set(SEQ_COMPILER_CMD ${SEQ_COMPILER_PATH}) set(SEQ_COMPILER_DEPENDS ${SEQ_COMPILER_PATH}) else() add_executable(seq_compiler tools/seq_compiler.cc) set(SEQ_COMPILER_CMD $) set(SEQ_COMPILER_DEPENDS seq_compiler) endif() if (DEFINED TRACKER_COMPILER_PATH) set(TRACKER_COMPILER_CMD ${TRACKER_COMPILER_PATH}) set(TRACKER_COMPILER_DEPENDS ${TRACKER_COMPILER_PATH}) else() add_executable(tracker_compiler tools/tracker_compiler.cc) set(TRACKER_COMPILER_CMD $) set(TRACKER_COMPILER_DEPENDS tracker_compiler) endif() # Always build a host-native tracker_compiler for code generation purposes add_executable(tracker_compiler_host tools/tracker_compiler.cc) set_target_properties(tracker_compiler_host PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/tools_host") # Determine which tracker compiler to use for code generation if (DEMO_CROSS_COMPILE_WIN32) set(TRACKER_COMPILER_FINAL_CMD "${CMAKE_SOURCE_DIR}/build_native/tools_host/tracker_compiler_host") set(TRACKER_COMPILER_FINAL_DEPENDS tracker_compiler_host) else() set(TRACKER_COMPILER_FINAL_CMD ${TRACKER_COMPILER_CMD}) set(TRACKER_COMPILER_FINAL_DEPENDS ${TRACKER_COMPILER_DEPENDS}) endif() #-- - Code Generation Helpers -- - function(pack_assets NAME INPUT_TXT HEADER_VAR DATA_CC_VAR TARGET_NAME) set(OUT_H ${CMAKE_CURRENT_SOURCE_DIR}/src/generated/${NAME}.h) set(OUT_CC ${CMAKE_CURRENT_SOURCE_DIR}/src/generated/${NAME}_data.cc) add_custom_command( OUTPUT ${OUT_H} ${OUT_CC} COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_SOURCE_DIR}/src/generated COMMAND ${ASSET_PACKER_CMD} ${INPUT_TXT} ${OUT_H} ${OUT_CC} DEPENDS ${ASSET_PACKER_DEPENDS} ${INPUT_TXT} COMMENT "Generating assets for ${NAME}..." ) set(${HEADER_VAR} ${OUT_H} PARENT_SCOPE) set(${DATA_CC_VAR} ${OUT_CC} PARENT_SCOPE) add_custom_target(${TARGET_NAME} DEPENDS ${OUT_H} ${OUT_CC}) endfunction() # A separate function for test assets to avoid polluting the source tree function(pack_test_assets NAME INPUT_TXT HEADER_VAR DATA_CC_VAR TARGET_NAME) set(OUT_H ${CMAKE_CURRENT_BINARY_DIR}/src/generated_test/${NAME}.h) set(OUT_CC ${CMAKE_CURRENT_BINARY_DIR}/src/generated_test/${NAME}_data.cc) add_custom_command( OUTPUT ${OUT_H} ${OUT_CC} COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/src/generated_test COMMAND ${ASSET_PACKER_CMD} ${INPUT_TXT} ${OUT_H} ${OUT_CC} DEPENDS ${ASSET_PACKER_DEPENDS} ${INPUT_TXT} COMMENT "Generating assets for test ${NAME}..." ) set(${HEADER_VAR} ${OUT_H} PARENT_SCOPE) set(${DATA_CC_VAR} ${OUT_CC} PARENT_SCOPE) add_custom_target(${TARGET_NAME} DEPENDS ${OUT_H} ${OUT_CC}) endfunction() #-- - Build Macros -- - macro(add_demo_executable NAME) add_executable(${NAME} ${ARGN}) # target_link_libraries must be called manually to ensure correct order endmacro() macro(add_demo_test NAME TEST_NAME) add_executable(${NAME} ${ARGN}) add_test(NAME ${TEST_NAME} COMMAND ${NAME}) endmacro() #-- - Generation Targets -- - set(DEMO_SEQ_PATH ${CMAKE_CURRENT_SOURCE_DIR}/assets/demo.seq) set(GENERATED_TIMELINE_CC ${CMAKE_CURRENT_SOURCE_DIR}/src/generated/timeline.cc) add_custom_command( OUTPUT ${GENERATED_TIMELINE_CC} COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_SOURCE_DIR}/src/generated COMMAND ${SEQ_COMPILER_CMD} ${DEMO_SEQ_PATH} ${GENERATED_TIMELINE_CC} DEPENDS ${SEQ_COMPILER_DEPENDS} ${DEMO_SEQ_PATH} src/gpu/demo_effects.h COMMENT "Compiling demo sequence..." ) add_custom_target(generate_timeline ALL DEPENDS ${GENERATED_TIMELINE_CC}) set(TRACKER_MUSIC_PATH ${CMAKE_CURRENT_SOURCE_DIR}/assets/music.track) set(GENERATED_MUSIC_DATA_CC ${CMAKE_CURRENT_SOURCE_DIR}/src/generated/music_data.cc) add_custom_command( OUTPUT ${GENERATED_MUSIC_DATA_CC} COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_SOURCE_DIR}/src/generated COMMAND ${TRACKER_COMPILER_FINAL_CMD} ${TRACKER_MUSIC_PATH} ${GENERATED_MUSIC_DATA_CC} DEPENDS ${TRACKER_COMPILER_FINAL_DEPENDS} ${TRACKER_MUSIC_PATH} tracker_compiler_host COMMENT "Compiling tracker music..." ) add_custom_target(generate_tracker_music ALL DEPENDS ${GENERATED_MUSIC_DATA_CC}) pack_assets(assets ${CMAKE_CURRENT_SOURCE_DIR}/assets/final/demo_assets.txt GEN_DEMO_H GEN_DEMO_CC generate_demo_assets) pack_test_assets(test_assets ${CMAKE_CURRENT_SOURCE_DIR}/assets/final/test_assets_list.txt GEN_TEST_H GEN_TEST_CC generate_test_assets) #-- - Main Demo -- - add_demo_executable(demo64k src/main.cc ${PLATFORM_SOURCES} ${GEN_DEMO_CC} ${GENERATED_TIMELINE_CC} ${GENERATED_MUSIC_DATA_CC}) add_dependencies(demo64k generate_demo_assets generate_timeline generate_tracker_music)# Link order: Internal libs first, then external libs (DEMO_LIBS). # gpu and 3d depend on WGPU (in DEMO_LIBS). if (APPLE) target_link_libraries(demo64k PRIVATE 3d gpu audio procedural util ${DEMO_LIBS}) else() target_link_libraries(demo64k PRIVATE -Wl,--start-group 3d gpu audio procedural util -Wl,--end-group ${DEMO_LIBS}) endif() #Size optimizations if (DEMO_SIZE_OPT) if (MSVC) target_compile_options(demo64k PRIVATE /Os /GS-) target_link_options(demo64k PRIVATE /OPT:REF /OPT:ICF /INCREMENTAL:NO) elseif (APPLE) target_compile_options(demo64k PRIVATE -Os) target_link_options(demo64k PRIVATE -Wl,-dead_strip) else () target_compile_options(demo64k PRIVATE -Os -ffunction-sections -fdata-sections) target_link_options(demo64k PRIVATE -Wl,--gc-sections -s) endif() endif() #-- - Tests -- - enable_testing() if(DEMO_BUILD_TESTS) add_demo_test(test_window HammingWindowTest src/tests/test_window.cc ${GEN_DEMO_CC}) target_link_libraries(test_window PRIVATE audio util procedural ${DEMO_LIBS}) add_dependencies(test_window generate_demo_assets) add_demo_test(test_maths MathUtilsTest src/tests/test_maths.cc) add_demo_test(test_synth SynthEngineTest src/tests/test_synth.cc ${GEN_DEMO_CC}) target_link_libraries(test_synth PRIVATE audio util procedural ${DEMO_LIBS}) add_dependencies(test_synth generate_demo_assets) add_demo_test(test_dct DctTest src/tests/test_dct.cc ${GEN_DEMO_CC}) target_link_libraries(test_dct PRIVATE audio util procedural ${DEMO_LIBS}) add_dependencies(test_dct generate_demo_assets) add_demo_test(test_audio_gen AudioGenTest src/tests/test_audio_gen.cc ${GEN_DEMO_CC}) target_link_libraries(test_audio_gen PRIVATE audio util procedural ${DEMO_LIBS}) add_dependencies(test_audio_gen generate_demo_assets) add_demo_test(test_audio_backend AudioBackendTest src/tests/test_audio_backend.cc ${GEN_DEMO_CC}) target_link_libraries(test_audio_backend PRIVATE audio util procedural ${DEMO_LIBS}) add_dependencies(test_audio_backend generate_demo_assets) add_demo_test(test_mock_backend MockAudioBackendTest src/tests/test_mock_backend.cc src/audio/mock_audio_backend.cc ${GEN_DEMO_CC}) target_link_libraries(test_mock_backend PRIVATE audio util procedural ${DEMO_LIBS}) add_dependencies(test_mock_backend generate_demo_assets) add_demo_test(test_tracker_timing TrackerTimingTest src/tests/test_tracker_timing.cc src/audio/mock_audio_backend.cc ${GEN_DEMO_CC} ${GENERATED_MUSIC_DATA_CC}) target_link_libraries(test_tracker_timing PRIVATE audio util procedural ${DEMO_LIBS}) add_dependencies(test_tracker_timing generate_demo_assets generate_tracker_music) add_demo_test(test_variable_tempo VariableTempoTest src/tests/test_variable_tempo.cc src/audio/mock_audio_backend.cc ${GEN_DEMO_CC} ${GENERATED_MUSIC_DATA_CC}) target_link_libraries(test_variable_tempo PRIVATE audio util procedural ${DEMO_LIBS}) add_dependencies(test_variable_tempo generate_demo_assets generate_tracker_music) add_demo_test(test_tracker TrackerSystemTest src/tests/test_tracker.cc ${GEN_DEMO_CC} ${GENERATED_MUSIC_DATA_CC}) target_link_libraries(test_tracker PRIVATE audio util procedural ${DEMO_LIBS}) add_dependencies(test_tracker generate_tracker_music) add_demo_test(test_shader_assets ShaderAssetValidation src/tests/test_shader_assets.cc ${GEN_DEMO_CC}) target_link_libraries(test_shader_assets PRIVATE util procedural ${DEMO_LIBS}) add_dependencies(test_shader_assets generate_demo_assets) add_demo_executable(test_spectool src/tests/test_spectool.cc ${PLATFORM_SOURCES} ${GEN_DEMO_CC} ${GENERATED_MUSIC_DATA_CC}) target_compile_definitions(test_spectool PRIVATE DEMO_BUILD_TOOLS) target_link_libraries(test_spectool PRIVATE audio util procedural ${DEMO_LIBS}) add_dependencies(test_spectool generate_tracker_music generate_demo_assets) add_demo_test(test_assets AssetManagerTest src/tests/test_assets.cc ${GEN_TEST_CC}) target_include_directories(test_assets PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src/generated_test) target_compile_definitions(test_assets PRIVATE USE_TEST_ASSETS) target_link_libraries(test_assets PRIVATE util procedural ${DEMO_LIBS}) add_dependencies(test_assets generate_test_assets) set_source_files_properties(src/tests/test_assets.cc PROPERTIES COMPILE_DEFINITIONS "USE_TEST_ASSETS") add_demo_test(test_sequence SequenceSystemTest src/tests/test_sequence.cc ${GEN_DEMO_CC} ${GENERATED_TIMELINE_CC} ${PLATFORM_SOURCES}) target_link_libraries(test_sequence PRIVATE 3d gpu util procedural ${DEMO_LIBS}) add_dependencies(test_sequence generate_timeline generate_demo_assets) add_demo_test(test_procedural ProceduralGenTest src/tests/test_procedural.cc) target_link_libraries(test_procedural PRIVATE procedural ${DEMO_LIBS}) add_demo_test(test_3d ThreeDSystemTest src/tests/test_3d.cc) add_demo_test(test_shader_composer ShaderComposerTest src/tests/test_shader_composer.cc ${GEN_TEST_CC}) target_compile_definitions(test_shader_composer PRIVATE USE_TEST_ASSETS) target_include_directories(test_shader_composer PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src/generated_test ${CORE_INCLUDES}) target_link_libraries(test_shader_composer PRIVATE gpu util procedural ${DEMO_LIBS}) add_dependencies(test_shader_composer generate_test_assets) add_demo_executable(test_texture_manager src/tests/test_texture_manager.cc ${PLATFORM_SOURCES} ${GENERATED_TIMELINE_CC} ${GEN_DEMO_CC} ${GENERATED_MUSIC_DATA_CC}) target_link_libraries(test_texture_manager PRIVATE 3d gpu audio procedural util ${DEMO_LIBS}) add_dependencies(test_texture_manager generate_timeline generate_demo_assets generate_tracker_music) add_demo_executable(test_3d_render src/tests/test_3d_render.cc ${PLATFORM_SOURCES} ${GENERATED_TIMELINE_CC} ${GEN_DEMO_CC} ${GENERATED_MUSIC_DATA_CC}) target_link_libraries(test_3d_render PRIVATE 3d gpu audio procedural util ${DEMO_LIBS}) add_dependencies(test_3d_render generate_timeline generate_demo_assets generate_tracker_music) endif() #-- - Extra Tools -- - if(DEMO_BUILD_TOOLS OR DEMO_BUILD_TESTS) add_demo_executable(spectool tools/spectool.cc ${PLATFORM_SOURCES} ${GEN_DEMO_CC} ${GENERATED_MUSIC_DATA_CC}) target_compile_definitions(spectool PRIVATE DEMO_BUILD_TOOLS) target_link_libraries(spectool PRIVATE audio util procedural ${DEMO_LIBS}) add_dependencies(spectool generate_tracker_music generate_demo_assets) add_executable(specview tools/specview.cc) endif() #-- - Global Target Configuration -- - add_custom_target(final COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/scripts/gen_assets.sh COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/scripts/crunch_demo.sh DEPENDS demo64k WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) add_custom_target(pack_source COMMAND tar -czf demo_all.tgz --exclude=.git --exclude=build* --exclude=.gemini* --exclude=*.tgz --exclude=*.zip --exclude=.DS_Store . WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} )