mirror of
https://git.citron-emu.org/citron/emulator
synced 2025-12-19 10:43:33 +00:00
feat: Add Profile-Guided Optimization (PGO) build support
Implements two-stage PGO build system with CMake integration and automated build scripts for Windows, Linux, and macOS. Supports MSVC, GCC, and Clang compilers. PGO enables runtime profiling-based optimizations for improved emulator performance. Includes helper scripts to streamline the build workflow and resolve platform-specific issues. Signed-off-by: Zephyron <zephyron@citron-emu.org>
This commit is contained in:
9
.gitignore
vendored
9
.gitignore
vendored
@@ -36,3 +36,12 @@ CMakeSettings.json
|
||||
# Windows global filetypes
|
||||
Thumbs.db
|
||||
|
||||
# PGO (Profile-Guided Optimization) files
|
||||
pgo-profiles-backup/
|
||||
*.pgd
|
||||
*.pgc
|
||||
*.profraw
|
||||
*.profdata
|
||||
*.gcda
|
||||
*.gcno
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externals/cmake-modul
|
||||
include(DownloadExternals)
|
||||
include(CMakeDependentOption)
|
||||
include(CTest)
|
||||
include(PGO)
|
||||
|
||||
# Disable Warnings as Errors for MSVC
|
||||
if (MSVC)
|
||||
@@ -78,6 +79,10 @@ option(CITRON_CHECK_SUBMODULES "Check if submodules are present" ON)
|
||||
|
||||
option(CITRON_ENABLE_LTO "Enable link-time optimization" OFF)
|
||||
|
||||
option(CITRON_ENABLE_PGO_GENERATE "Build with PGO instrumentation to generate profile data (Stage 1)" OFF)
|
||||
option(CITRON_ENABLE_PGO_USE "Build using PGO profile data for optimization (Stage 2)" OFF)
|
||||
set(CITRON_PGO_PROFILE_DIR "${CMAKE_BINARY_DIR}/pgo-profiles" CACHE PATH "Directory to store PGO profile data")
|
||||
|
||||
option(CITRON_DOWNLOAD_TIME_ZONE_DATA "Always download time zone binaries" OFF)
|
||||
|
||||
option(CITRON_ENABLE_PORTABLE "Allow citron to enable portable mode if a user folder is found in the CWD" ON)
|
||||
@@ -792,6 +797,22 @@ if(DEFINED HAS_BOOST_PROCESS_DEFINITION)
|
||||
target_compile_definitions(core PRIVATE ${HAS_BOOST_PROCESS_DEFINITION})
|
||||
endif()
|
||||
|
||||
# Apply PGO configuration to main targets
|
||||
if(CITRON_ENABLE_PGO_GENERATE OR CITRON_ENABLE_PGO_USE)
|
||||
if(TARGET citron)
|
||||
citron_configure_pgo(citron)
|
||||
endif()
|
||||
if(TARGET citron-cmd)
|
||||
citron_configure_pgo(citron-cmd)
|
||||
endif()
|
||||
if(TARGET citron-room)
|
||||
citron_configure_pgo(citron-room)
|
||||
endif()
|
||||
|
||||
# Print PGO instructions
|
||||
citron_print_pgo_instructions()
|
||||
endif()
|
||||
|
||||
# Set citron project or citron-cmd project as default StartUp Project in Visual Studio depending on whether QT is enabled or not
|
||||
if(ENABLE_QT)
|
||||
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT citron)
|
||||
|
||||
274
CMakeModules/PGO.cmake
Normal file
274
CMakeModules/PGO.cmake
Normal file
@@ -0,0 +1,274 @@
|
||||
# SPDX-FileCopyrightText: 2025 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# Profile-Guided Optimization (PGO) Support
|
||||
#
|
||||
# This module provides functions to enable Profile-Guided Optimization (PGO) for Citron.
|
||||
# PGO is a two-stage compiler optimization technique:
|
||||
# 1. GENERATE stage: Build with instrumentation to collect profiling data during runtime
|
||||
# 2. USE stage: Rebuild using the collected profiling data to optimize hot paths
|
||||
#
|
||||
# Usage:
|
||||
# set(CITRON_ENABLE_PGO_GENERATE ON) # First build: generate profiling data
|
||||
# set(CITRON_ENABLE_PGO_USE ON) # Second build: use profiling data
|
||||
# set(CITRON_PGO_PROFILE_DIR "${CMAKE_BINARY_DIR}/pgo-profiles") # Optional: custom profile directory
|
||||
|
||||
# PGO profile directory - where .pgd/.profraw/.profdata files are stored
|
||||
if(NOT DEFINED CITRON_PGO_PROFILE_DIR)
|
||||
set(CITRON_PGO_PROFILE_DIR "${CMAKE_BINARY_DIR}/pgo-profiles" CACHE PATH "Directory to store PGO profile data")
|
||||
endif()
|
||||
|
||||
# Create the profile directory if it doesn't exist
|
||||
file(MAKE_DIRECTORY "${CITRON_PGO_PROFILE_DIR}")
|
||||
|
||||
# Function to copy MSVC PGO runtime DLLs
|
||||
function(citron_copy_pgo_runtime_dlls target_name)
|
||||
if(NOT MSVC)
|
||||
return()
|
||||
endif()
|
||||
|
||||
# Find the Visual Studio installation directory
|
||||
get_filename_component(MSVC_DIR "${CMAKE_CXX_COMPILER}" DIRECTORY)
|
||||
get_filename_component(MSVC_DIR "${MSVC_DIR}" DIRECTORY)
|
||||
get_filename_component(MSVC_DIR "${MSVC_DIR}" DIRECTORY)
|
||||
|
||||
# Common locations for PGO runtime DLLs
|
||||
set(PGO_DLL_PATHS
|
||||
"${MSVC_DIR}/VC/Redist/MSVC/*/x64/Microsoft.VC*.CRT/pgort140.dll"
|
||||
"${MSVC_DIR}/VC/Redist/MSVC/*/x86/Microsoft.VC*.CRT/pgort140.dll"
|
||||
"${MSVC_DIR}/VC/Tools/MSVC/*/bin/Hostx64/x64/pgort140.dll"
|
||||
"${MSVC_DIR}/VC/Tools/MSVC/*/bin/Hostx64/x86/pgort140.dll"
|
||||
"${MSVC_DIR}/VC/Tools/MSVC/*/bin/Hostx86/x64/pgort140.dll"
|
||||
"${MSVC_DIR}/VC/Tools/MSVC/*/bin/Hostx86/x86/pgort140.dll"
|
||||
)
|
||||
|
||||
# Find the PGO runtime DLL
|
||||
set(PGO_DLL_FOUND FALSE)
|
||||
foreach(dll_pattern ${PGO_DLL_PATHS})
|
||||
file(GLOB PGO_DLL_CANDIDATES ${dll_pattern})
|
||||
if(PGO_DLL_CANDIDATES)
|
||||
list(GET PGO_DLL_CANDIDATES 0 PGO_DLL_PATH)
|
||||
set(PGO_DLL_FOUND TRUE)
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(PGO_DLL_FOUND)
|
||||
message(STATUS " [${target_name}] Found PGO runtime DLL: ${PGO_DLL_PATH}")
|
||||
|
||||
# Get the target's output directory
|
||||
get_target_property(TARGET_OUTPUT_DIR ${target_name} RUNTIME_OUTPUT_DIRECTORY)
|
||||
if(NOT TARGET_OUTPUT_DIR)
|
||||
get_target_property(TARGET_OUTPUT_DIR ${target_name} RUNTIME_OUTPUT_DIRECTORY_DEBUG)
|
||||
endif()
|
||||
if(NOT TARGET_OUTPUT_DIR)
|
||||
set(TARGET_OUTPUT_DIR "${CMAKE_BINARY_DIR}/bin")
|
||||
endif()
|
||||
|
||||
# Copy the DLL to the output directory
|
||||
add_custom_command(TARGET ${target_name} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"${PGO_DLL_PATH}"
|
||||
"${TARGET_OUTPUT_DIR}/"
|
||||
COMMENT "Copying PGO runtime DLL for ${target_name}"
|
||||
)
|
||||
else()
|
||||
message(WARNING "PGO runtime DLL (pgort140.dll) not found. The instrumented build may not run properly.")
|
||||
message(STATUS " Please ensure Visual Studio is properly installed with PGO support.")
|
||||
message(STATUS " You may need to install the 'MSVC v143 - VS 2022 C++ x64/x86 build tools' component.")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# Function to configure PGO for a specific target
|
||||
function(citron_configure_pgo target_name)
|
||||
if(NOT TARGET ${target_name})
|
||||
message(WARNING "Target ${target_name} does not exist, skipping PGO configuration")
|
||||
return()
|
||||
endif()
|
||||
|
||||
# Only configure PGO if either GENERATE or USE is enabled
|
||||
if(NOT CITRON_ENABLE_PGO_GENERATE AND NOT CITRON_ENABLE_PGO_USE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
# Ensure both stages are not enabled at the same time
|
||||
if(CITRON_ENABLE_PGO_GENERATE AND CITRON_ENABLE_PGO_USE)
|
||||
message(FATAL_ERROR "Cannot enable both CITRON_ENABLE_PGO_GENERATE and CITRON_ENABLE_PGO_USE simultaneously. Please build twice: first with GENERATE, then with USE.")
|
||||
endif()
|
||||
|
||||
message(STATUS "Configuring PGO for target: ${target_name}")
|
||||
|
||||
# MSVC-specific PGO
|
||||
if(MSVC)
|
||||
if(CITRON_ENABLE_PGO_GENERATE)
|
||||
message(STATUS " [${target_name}] MSVC PGO: GENERATE stage")
|
||||
# Use FASTGENPROFILE for faster profiling with similar accuracy
|
||||
# GENPROFILE provides more detailed profiling but is slower
|
||||
# You can change FASTGENPROFILE to GENPROFILE for more accuracy but slower profiling
|
||||
target_compile_options(${target_name} PRIVATE /GL)
|
||||
target_link_options(${target_name} PRIVATE
|
||||
/LTCG
|
||||
/FASTGENPROFILE
|
||||
/PGD:"${CITRON_PGO_PROFILE_DIR}/${target_name}.pgd"
|
||||
)
|
||||
|
||||
# Copy PGO runtime DLLs to output directory
|
||||
citron_copy_pgo_runtime_dlls(${target_name})
|
||||
elseif(CITRON_ENABLE_PGO_USE)
|
||||
message(STATUS " [${target_name}] MSVC PGO: USE stage")
|
||||
# Check if profile data exists in pgo-profiles directory
|
||||
set(PGD_FILE "${CITRON_PGO_PROFILE_DIR}/${target_name}.pgd")
|
||||
|
||||
# Also check in the output directory (where MSVC creates them during GENERATE)
|
||||
get_target_property(TARGET_OUTPUT_DIR ${target_name} RUNTIME_OUTPUT_DIRECTORY)
|
||||
if(NOT TARGET_OUTPUT_DIR)
|
||||
set(TARGET_OUTPUT_DIR "${CMAKE_BINARY_DIR}/bin")
|
||||
endif()
|
||||
set(PGD_FILE_OUTPUT "${TARGET_OUTPUT_DIR}/${target_name}.pgd")
|
||||
|
||||
if(EXISTS "${PGD_FILE}")
|
||||
target_compile_options(${target_name} PRIVATE /GL)
|
||||
# Use the profile directory path
|
||||
file(TO_NATIVE_PATH "${PGD_FILE}" PGD_FILE_NATIVE)
|
||||
target_link_options(${target_name} PRIVATE
|
||||
/LTCG
|
||||
"/USEPROFILE:PGD=${PGD_FILE_NATIVE}"
|
||||
)
|
||||
message(STATUS " [${target_name}] Using profile data: ${PGD_FILE}")
|
||||
elseif(EXISTS "${PGD_FILE_OUTPUT}")
|
||||
target_compile_options(${target_name} PRIVATE /GL)
|
||||
# Use the output directory path
|
||||
file(TO_NATIVE_PATH "${PGD_FILE_OUTPUT}" PGD_FILE_NATIVE)
|
||||
target_link_options(${target_name} PRIVATE
|
||||
/LTCG
|
||||
"/USEPROFILE:PGD=${PGD_FILE_NATIVE}"
|
||||
)
|
||||
message(STATUS " [${target_name}] Using profile data: ${PGD_FILE_OUTPUT}")
|
||||
else()
|
||||
message(WARNING "Profile data not found for ${target_name}. Checked:")
|
||||
message(STATUS " - ${PGD_FILE}")
|
||||
message(STATUS " - ${PGD_FILE_OUTPUT}")
|
||||
message(WARNING "PGO USE stage will be skipped.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# GCC-specific PGO
|
||||
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
if(CITRON_ENABLE_PGO_GENERATE)
|
||||
message(STATUS " [${target_name}] GCC PGO: GENERATE stage")
|
||||
target_compile_options(${target_name} PRIVATE
|
||||
-fprofile-generate="${CITRON_PGO_PROFILE_DIR}"
|
||||
)
|
||||
target_link_options(${target_name} PRIVATE
|
||||
-fprofile-generate="${CITRON_PGO_PROFILE_DIR}"
|
||||
)
|
||||
elseif(CITRON_ENABLE_PGO_USE)
|
||||
message(STATUS " [${target_name}] GCC PGO: USE stage")
|
||||
# Check if profile data exists
|
||||
file(GLOB profile_files "${CITRON_PGO_PROFILE_DIR}/*.gcda")
|
||||
if(profile_files)
|
||||
target_compile_options(${target_name} PRIVATE
|
||||
-fprofile-use="${CITRON_PGO_PROFILE_DIR}"
|
||||
-fprofile-correction # Handle inconsistencies in profile data
|
||||
)
|
||||
target_link_options(${target_name} PRIVATE
|
||||
-fprofile-use="${CITRON_PGO_PROFILE_DIR}"
|
||||
)
|
||||
message(STATUS " [${target_name}] Using profile data from: ${CITRON_PGO_PROFILE_DIR}")
|
||||
else()
|
||||
message(WARNING "No profile data found for ${target_name} in ${CITRON_PGO_PROFILE_DIR}. PGO USE stage will be skipped.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Clang-specific PGO
|
||||
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
if(CITRON_ENABLE_PGO_GENERATE)
|
||||
message(STATUS " [${target_name}] Clang PGO: GENERATE stage")
|
||||
target_compile_options(${target_name} PRIVATE
|
||||
-fprofile-generate="${CITRON_PGO_PROFILE_DIR}"
|
||||
)
|
||||
target_link_options(${target_name} PRIVATE
|
||||
-fprofile-generate="${CITRON_PGO_PROFILE_DIR}"
|
||||
)
|
||||
elseif(CITRON_ENABLE_PGO_USE)
|
||||
message(STATUS " [${target_name}] Clang PGO: USE stage")
|
||||
|
||||
# For Clang, we need to merge .profraw files into .profdata first
|
||||
set(PROFDATA_FILE "${CITRON_PGO_PROFILE_DIR}/default.profdata")
|
||||
|
||||
# Check if merged profile data exists, if not try to create it
|
||||
if(NOT EXISTS "${PROFDATA_FILE}")
|
||||
file(GLOB profraw_files "${CITRON_PGO_PROFILE_DIR}/*.profraw")
|
||||
if(profraw_files)
|
||||
find_program(LLVM_PROFDATA llvm-profdata)
|
||||
if(LLVM_PROFDATA)
|
||||
message(STATUS " [${target_name}] Merging .profraw files into ${PROFDATA_FILE}")
|
||||
execute_process(
|
||||
COMMAND ${LLVM_PROFDATA} merge -output=${PROFDATA_FILE} ${profraw_files}
|
||||
RESULT_VARIABLE merge_result
|
||||
OUTPUT_QUIET
|
||||
ERROR_QUIET
|
||||
)
|
||||
if(NOT merge_result EQUAL 0)
|
||||
message(WARNING "Failed to merge profile data for ${target_name}. PGO USE stage will be skipped.")
|
||||
return()
|
||||
endif()
|
||||
else()
|
||||
message(WARNING "llvm-profdata not found. Cannot merge profile data. PGO USE stage will be skipped.")
|
||||
message(STATUS " Please run: llvm-profdata merge -output=${PROFDATA_FILE} ${CITRON_PGO_PROFILE_DIR}/*.profraw")
|
||||
return()
|
||||
endif()
|
||||
else()
|
||||
message(WARNING "No .profraw files found in ${CITRON_PGO_PROFILE_DIR}. PGO USE stage will be skipped.")
|
||||
return()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(EXISTS "${PROFDATA_FILE}")
|
||||
target_compile_options(${target_name} PRIVATE
|
||||
-fprofile-use="${PROFDATA_FILE}"
|
||||
)
|
||||
target_link_options(${target_name} PRIVATE
|
||||
-fprofile-use="${PROFDATA_FILE}"
|
||||
)
|
||||
message(STATUS " [${target_name}] Using profile data: ${PROFDATA_FILE}")
|
||||
endif()
|
||||
endif()
|
||||
else()
|
||||
message(WARNING "PGO is not supported for compiler: ${CMAKE_CXX_COMPILER_ID}")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# Helper function to print PGO instructions
|
||||
function(citron_print_pgo_instructions)
|
||||
if(CITRON_ENABLE_PGO_GENERATE)
|
||||
message(STATUS "")
|
||||
message(STATUS "=================================================================")
|
||||
message(STATUS "PGO GENERATE Stage")
|
||||
message(STATUS "=================================================================")
|
||||
message(STATUS "Citron has been built with profiling instrumentation.")
|
||||
message(STATUS "")
|
||||
message(STATUS "Next steps:")
|
||||
message(STATUS " 1. Run the built citron executable")
|
||||
message(STATUS " 2. Play games/perform typical operations to generate profile data")
|
||||
message(STATUS " 3. Exit citron")
|
||||
message(STATUS " 4. Profile data will be saved to: ${CITRON_PGO_PROFILE_DIR}")
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
message(STATUS " 5. For Clang: Merge profile data with:")
|
||||
message(STATUS " llvm-profdata merge -output=${CITRON_PGO_PROFILE_DIR}/default.profdata ${CITRON_PGO_PROFILE_DIR}/*.profraw")
|
||||
endif()
|
||||
message(STATUS " 6. Rebuild with: cmake -DCITRON_ENABLE_PGO_GENERATE=OFF -DCITRON_ENABLE_PGO_USE=ON")
|
||||
message(STATUS "=================================================================")
|
||||
message(STATUS "")
|
||||
elseif(CITRON_ENABLE_PGO_USE)
|
||||
message(STATUS "")
|
||||
message(STATUS "=================================================================")
|
||||
message(STATUS "PGO USE Stage")
|
||||
message(STATUS "=================================================================")
|
||||
message(STATUS "Citron is being optimized using profile data from: ${CITRON_PGO_PROFILE_DIR}")
|
||||
message(STATUS "This build will be significantly faster than standard builds.")
|
||||
message(STATUS "=================================================================")
|
||||
message(STATUS "")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
174
fix-pgo-dll.ps1
Normal file
174
fix-pgo-dll.ps1
Normal file
@@ -0,0 +1,174 @@
|
||||
# SPDX-FileCopyrightText: 2025 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# Fix PGO DLL Script for Citron (Windows)
|
||||
# This script helps locate and copy the required pgort140.dll for MSVC PGO builds
|
||||
|
||||
param(
|
||||
[Parameter()]
|
||||
[string]$OutputDir = "build\bin\Release",
|
||||
|
||||
[Parameter()]
|
||||
[switch]$Help
|
||||
)
|
||||
|
||||
function Show-Usage {
|
||||
Write-Host @"
|
||||
Usage: .\fix-pgo-dll.ps1 [OPTIONS]
|
||||
|
||||
This script helps fix the "pgort140.DLL was not found" error by locating
|
||||
and copying the required PGO runtime DLL to your build output directory.
|
||||
|
||||
Options:
|
||||
-OutputDir PATH Target directory to copy DLL (default: build\bin\Release)
|
||||
-Help Show this help message
|
||||
|
||||
Example:
|
||||
.\fix-pgo-dll.ps1
|
||||
.\fix-pgo-dll.ps1 -OutputDir "C:\MyBuild\bin"
|
||||
"@
|
||||
}
|
||||
|
||||
function Write-Header {
|
||||
param([string]$Message)
|
||||
Write-Host "`n=================================================================" -ForegroundColor Cyan
|
||||
Write-Host $Message -ForegroundColor Cyan
|
||||
Write-Host "=================================================================`n" -ForegroundColor Cyan
|
||||
}
|
||||
|
||||
function Write-Info {
|
||||
param([string]$Message)
|
||||
Write-Host "[INFO] $Message" -ForegroundColor Green
|
||||
}
|
||||
|
||||
function Write-Warning {
|
||||
param([string]$Message)
|
||||
Write-Host "[WARNING] $Message" -ForegroundColor Yellow
|
||||
}
|
||||
|
||||
function Write-Error-Custom {
|
||||
param([string]$Message)
|
||||
Write-Host "[ERROR] $Message" -ForegroundColor Red
|
||||
}
|
||||
|
||||
if ($Help) {
|
||||
Show-Usage
|
||||
exit 0
|
||||
}
|
||||
|
||||
Write-Header "PGO DLL Fixer for Citron"
|
||||
|
||||
# Find Visual Studio installation
|
||||
$VSInstallPath = $null
|
||||
$VSWhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
|
||||
|
||||
if (Test-Path $VSWhere) {
|
||||
Write-Info "Searching for Visual Studio installations..."
|
||||
$VSInstallPath = & $VSWhere -latest -property installationPath
|
||||
if ($VSInstallPath) {
|
||||
Write-Info "Found Visual Studio at: $VSInstallPath"
|
||||
}
|
||||
} else {
|
||||
Write-Warning "vswhere.exe not found. Trying common installation paths..."
|
||||
}
|
||||
|
||||
# Common Visual Studio installation paths
|
||||
$CommonPaths = @(
|
||||
"${env:ProgramFiles}\Microsoft Visual Studio\2022\Community",
|
||||
"${env:ProgramFiles}\Microsoft Visual Studio\2022\Professional",
|
||||
"${env:ProgramFiles}\Microsoft Visual Studio\2022\Enterprise",
|
||||
"${env:ProgramFiles(x86)}\Microsoft Visual Studio\2019\Community",
|
||||
"${env:ProgramFiles(x86)}\Microsoft Visual Studio\2019\Professional",
|
||||
"${env:ProgramFiles(x86)}\Microsoft Visual Studio\2019\Enterprise"
|
||||
)
|
||||
|
||||
if (-not $VSInstallPath) {
|
||||
foreach ($path in $CommonPaths) {
|
||||
if (Test-Path $path) {
|
||||
$VSInstallPath = $path
|
||||
Write-Info "Found Visual Studio at: $VSInstallPath"
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (-not $VSInstallPath) {
|
||||
Write-Error-Custom "Visual Studio installation not found!"
|
||||
Write-Host "Please ensure Visual Studio 2019 or 2022 is installed with C++ support."
|
||||
Write-Host "You can download it from: https://visualstudio.microsoft.com/downloads/"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Search for pgort140.dll
|
||||
Write-Info "Searching for pgort140.dll..."
|
||||
|
||||
$DllPaths = @(
|
||||
"$VSInstallPath\VC\Redist\MSVC\*\x64\Microsoft.VC*.CRT\pgort140.dll",
|
||||
"$VSInstallPath\VC\Redist\MSVC\*\x86\Microsoft.VC*.CRT\pgort140.dll",
|
||||
"$VSInstallPath\VC\Tools\MSVC\*\bin\Hostx64\x64\pgort140.dll",
|
||||
"$VSInstallPath\VC\Tools\MSVC\*\bin\Hostx64\x86\pgort140.dll",
|
||||
"$VSInstallPath\VC\Tools\MSVC\*\bin\Hostx86\x64\pgort140.dll",
|
||||
"$VSInstallPath\VC\Tools\MSVC\*\bin\Hostx86\x86\pgort140.dll"
|
||||
)
|
||||
|
||||
$FoundDll = $null
|
||||
foreach ($pattern in $DllPaths) {
|
||||
$matches = Get-ChildItem -Path $pattern -ErrorAction SilentlyContinue
|
||||
if ($matches) {
|
||||
$FoundDll = $matches[0].FullName
|
||||
Write-Info "Found pgort140.dll at: $FoundDll"
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (-not $FoundDll) {
|
||||
Write-Error-Custom "pgort140.dll not found in Visual Studio installation!"
|
||||
Write-Host "This usually means:"
|
||||
Write-Host "1. Visual Studio was installed without PGO support"
|
||||
Write-Host "2. You need to install the 'MSVC v143 - VS 2022 C++ x64/x86 build tools' component"
|
||||
Write-Host "3. Try repairing your Visual Studio installation"
|
||||
Write-Host ""
|
||||
Write-Host "To fix this:"
|
||||
Write-Host "1. Open Visual Studio Installer"
|
||||
Write-Host "2. Click 'Modify' on your Visual Studio installation"
|
||||
Write-Host "3. Go to 'Individual components' tab"
|
||||
Write-Host "4. Search for 'PGO' and ensure it's checked"
|
||||
Write-Host "5. Click 'Modify' to install the component"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Create output directory if it doesn't exist
|
||||
if (-not (Test-Path $OutputDir)) {
|
||||
Write-Info "Creating output directory: $OutputDir"
|
||||
New-Item -ItemType Directory -Force -Path $OutputDir | Out-Null
|
||||
}
|
||||
|
||||
# Copy the DLL
|
||||
try {
|
||||
Write-Info "Copying pgort140.dll to: $OutputDir"
|
||||
Copy-Item -Path $FoundDll -Destination $OutputDir -Force
|
||||
Write-Info "Successfully copied pgort140.dll!"
|
||||
|
||||
# Verify the copy
|
||||
$CopiedDll = Join-Path $OutputDir "pgort140.dll"
|
||||
if (Test-Path $CopiedDll) {
|
||||
Write-Info "Verification: pgort140.dll is now available in $OutputDir"
|
||||
Write-Host ""
|
||||
Write-Host "You can now run your PGO instrumented Citron build!"
|
||||
} else {
|
||||
Write-Error-Custom "Failed to copy pgort140.dll"
|
||||
exit 1
|
||||
}
|
||||
} catch {
|
||||
Write-Error-Custom "Error copying pgort140.dll: $($_.Exception.Message)"
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Header "PGO DLL Fix Complete!"
|
||||
Write-Info "The pgort140.dll has been copied to your build output directory."
|
||||
Write-Info "Your PGO instrumented Citron build should now run without the DLL error."
|
||||
Write-Host ""
|
||||
Write-Info "Next steps:"
|
||||
Write-Host "1. Run your PGO instrumented build"
|
||||
Write-Host "2. Play games to collect profile data"
|
||||
Write-Host "3. Rebuild with PGO USE stage for optimization"
|
||||
209
pgo-build.ps1
Normal file
209
pgo-build.ps1
Normal file
@@ -0,0 +1,209 @@
|
||||
# SPDX-FileCopyrightText: 2025 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# PGO Build Script for Citron (Windows/PowerShell)
|
||||
# This script automates the Profile-Guided Optimization build process
|
||||
|
||||
param(
|
||||
[Parameter(Position=0, Mandatory=$true)]
|
||||
[ValidateSet('generate', 'use', 'clean')]
|
||||
[string]$Stage,
|
||||
|
||||
[Parameter()]
|
||||
[int]$Jobs = 0,
|
||||
|
||||
[Parameter()]
|
||||
[switch]$EnableLTO,
|
||||
|
||||
[Parameter()]
|
||||
[switch]$Help
|
||||
)
|
||||
|
||||
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||
$BuildDir = Join-Path $ScriptDir "build"
|
||||
$PgoProfilesDir = Join-Path $BuildDir "pgo-profiles"
|
||||
$BackupProfilesDir = Join-Path $ScriptDir "pgo-profiles-backup"
|
||||
|
||||
function Show-Usage {
|
||||
Write-Host @"
|
||||
Usage: .\pgo-build.ps1 [STAGE] [OPTIONS]
|
||||
|
||||
STAGE can be:
|
||||
generate - Build with PGO instrumentation (Stage 1)
|
||||
use - Build using PGO profile data (Stage 2)
|
||||
clean - Clean build directory but preserve profiles
|
||||
|
||||
Example:
|
||||
.\pgo-build.ps1 generate # Build instrumented version
|
||||
# Run citron.exe to collect profiles
|
||||
.\pgo-build.ps1 use # Build optimized version
|
||||
|
||||
Options:
|
||||
-Jobs N Number of parallel jobs (default: auto-detect)
|
||||
-EnableLTO Enable Link-Time Optimization
|
||||
-Help Show this help message
|
||||
"@
|
||||
}
|
||||
|
||||
function Write-Header {
|
||||
param([string]$Message)
|
||||
Write-Host "`n=================================================================" -ForegroundColor Cyan
|
||||
Write-Host $Message -ForegroundColor Cyan
|
||||
Write-Host "=================================================================`n" -ForegroundColor Cyan
|
||||
}
|
||||
|
||||
function Write-Info {
|
||||
param([string]$Message)
|
||||
Write-Host "[INFO] $Message" -ForegroundColor Green
|
||||
}
|
||||
|
||||
function Write-Warning {
|
||||
param([string]$Message)
|
||||
Write-Host "[WARNING] $Message" -ForegroundColor Yellow
|
||||
}
|
||||
|
||||
function Write-Error-Custom {
|
||||
param([string]$Message)
|
||||
Write-Host "[ERROR] $Message" -ForegroundColor Red
|
||||
}
|
||||
|
||||
if ($Help) {
|
||||
Show-Usage
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Auto-detect number of processors
|
||||
if ($Jobs -eq 0) {
|
||||
$Jobs = $env:NUMBER_OF_PROCESSORS
|
||||
if (-not $Jobs) { $Jobs = 4 }
|
||||
}
|
||||
|
||||
$LtoFlag = if ($EnableLTO) { "ON" } else { "OFF" }
|
||||
|
||||
# Clean stage
|
||||
if ($Stage -eq "clean") {
|
||||
Write-Header "Cleaning Build Directory"
|
||||
|
||||
if (Test-Path $PgoProfilesDir) {
|
||||
Write-Info "Backing up PGO profiles..."
|
||||
New-Item -ItemType Directory -Force -Path $BackupProfilesDir | Out-Null
|
||||
Copy-Item -Path "$PgoProfilesDir\*" -Destination $BackupProfilesDir -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
if (Test-Path $BuildDir) {
|
||||
Write-Info "Removing build directory..."
|
||||
Remove-Item -Path $BuildDir -Recurse -Force
|
||||
}
|
||||
|
||||
if (Test-Path $BackupProfilesDir) {
|
||||
Write-Info "Restoring PGO profiles..."
|
||||
New-Item -ItemType Directory -Force -Path $PgoProfilesDir | Out-Null
|
||||
Move-Item -Path "$BackupProfilesDir\*" -Destination $PgoProfilesDir -Force -ErrorAction SilentlyContinue
|
||||
Remove-Item -Path $BackupProfilesDir -Recurse -Force
|
||||
}
|
||||
|
||||
Write-Info "Clean complete!"
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Generate stage
|
||||
if ($Stage -eq "generate") {
|
||||
Write-Header "PGO Stage 1: Generate Profile Data"
|
||||
|
||||
# Create build directory
|
||||
New-Item -ItemType Directory -Force -Path $BuildDir | Out-Null
|
||||
Set-Location $BuildDir
|
||||
|
||||
# Configure
|
||||
Write-Info "Configuring CMake..."
|
||||
cmake .. `
|
||||
-DCITRON_ENABLE_PGO_GENERATE=ON `
|
||||
-DCITRON_ENABLE_LTO=$LtoFlag `
|
||||
-DCMAKE_BUILD_TYPE=Release
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Error-Custom "CMake configuration failed"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Build
|
||||
Write-Info "Building instrumented Citron (this may take a while)..."
|
||||
cmake --build . --config Release -j $Jobs
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Error-Custom "Build failed"
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Header "Build Complete!"
|
||||
Write-Info "Next steps:"
|
||||
Write-Host " 1. Run: .\bin\Release\citron.exe"
|
||||
Write-Host " 2. Play games for 15-30 minutes to collect profile data"
|
||||
Write-Host " 3. Exit citron"
|
||||
Write-Host " 4. Run: .\pgo-build.ps1 use"
|
||||
|
||||
Set-Location $ScriptDir
|
||||
}
|
||||
|
||||
# Use stage
|
||||
if ($Stage -eq "use") {
|
||||
Write-Header "PGO Stage 2: Build Optimized Binary"
|
||||
|
||||
# Check if profile data exists
|
||||
if (-not (Test-Path $PgoProfilesDir) -or -not (Get-ChildItem $PgoProfilesDir -ErrorAction SilentlyContinue)) {
|
||||
Write-Error-Custom "No profile data found in $PgoProfilesDir"
|
||||
Write-Info "Please run the generate stage first and collect profile data"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Backup profiles if build directory exists
|
||||
if (Test-Path $BuildDir) {
|
||||
Write-Info "Backing up PGO profiles..."
|
||||
New-Item -ItemType Directory -Force -Path $BackupProfilesDir | Out-Null
|
||||
Copy-Item -Path "$PgoProfilesDir\*" -Destination $BackupProfilesDir -Recurse -Force
|
||||
Remove-Item -Path $BuildDir -Recurse -Force
|
||||
}
|
||||
|
||||
# Create build directory and restore profiles
|
||||
New-Item -ItemType Directory -Force -Path $BuildDir | Out-Null
|
||||
if (Test-Path $BackupProfilesDir) {
|
||||
New-Item -ItemType Directory -Force -Path $PgoProfilesDir | Out-Null
|
||||
Move-Item -Path "$BackupProfilesDir\*" -Destination $PgoProfilesDir -Force
|
||||
Remove-Item -Path $BackupProfilesDir -Recurse -Force
|
||||
}
|
||||
|
||||
Set-Location $BuildDir
|
||||
|
||||
# Configure
|
||||
Write-Info "Configuring CMake..."
|
||||
cmake .. `
|
||||
-DCITRON_ENABLE_PGO_USE=ON `
|
||||
-DCITRON_ENABLE_LTO=$LtoFlag `
|
||||
-DCMAKE_BUILD_TYPE=Release
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Error-Custom "CMake configuration failed"
|
||||
Set-Location $ScriptDir
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Build
|
||||
Write-Info "Building optimized Citron (this may take a while)..."
|
||||
cmake --build . --config Release -j $Jobs
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Error-Custom "Build failed"
|
||||
Set-Location $ScriptDir
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Header "Build Complete!"
|
||||
Write-Info "Your optimized Citron binary is ready!"
|
||||
Write-Info "Location: $BuildDir\bin\Release\citron.exe"
|
||||
Write-Host ""
|
||||
Write-Info "This build is optimized for your specific usage patterns."
|
||||
Write-Info "Enjoy improved performance! 🚀"
|
||||
|
||||
Set-Location $ScriptDir
|
||||
}
|
||||
|
||||
203
pgo-build.sh
Normal file
203
pgo-build.sh
Normal file
@@ -0,0 +1,203 @@
|
||||
#!/bin/bash
|
||||
# SPDX-FileCopyrightText: 2025 citron Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# PGO Build Script for Citron (Linux/macOS)
|
||||
# This script automates the Profile-Guided Optimization build process
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
BUILD_DIR="${SCRIPT_DIR}/build"
|
||||
PGO_PROFILES_DIR="${BUILD_DIR}/pgo-profiles"
|
||||
BACKUP_PROFILES_DIR="${SCRIPT_DIR}/pgo-profiles-backup"
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
print_header() {
|
||||
echo -e "${BLUE}=================================================================${NC}"
|
||||
echo -e "${BLUE}$1${NC}"
|
||||
echo -e "${BLUE}=================================================================${NC}"
|
||||
}
|
||||
|
||||
print_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
print_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
show_usage() {
|
||||
echo "Usage: $0 [STAGE]"
|
||||
echo ""
|
||||
echo "STAGE can be:"
|
||||
echo " generate - Build with PGO instrumentation (Stage 1)"
|
||||
echo " use - Build using PGO profile data (Stage 2)"
|
||||
echo " clean - Clean build directory but preserve profiles"
|
||||
echo ""
|
||||
echo "Example:"
|
||||
echo " $0 generate # Build instrumented version"
|
||||
echo " # Run citron to collect profiles"
|
||||
echo " $0 use # Build optimized version"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " -j N Number of parallel jobs (default: auto-detect)"
|
||||
echo " -lto Enable Link-Time Optimization"
|
||||
echo " -h Show this help message"
|
||||
}
|
||||
|
||||
# Parse arguments
|
||||
STAGE=""
|
||||
JOBS=$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo "4")
|
||||
ENABLE_LTO="OFF"
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
generate|use|clean)
|
||||
STAGE="$1"
|
||||
shift
|
||||
;;
|
||||
-j)
|
||||
JOBS="$2"
|
||||
shift 2
|
||||
;;
|
||||
-lto)
|
||||
ENABLE_LTO="ON"
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
show_usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
print_error "Unknown option: $1"
|
||||
show_usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ -z "$STAGE" ]; then
|
||||
print_error "No stage specified"
|
||||
show_usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Clean stage
|
||||
if [ "$STAGE" == "clean" ]; then
|
||||
print_header "Cleaning Build Directory"
|
||||
|
||||
if [ -d "$PGO_PROFILES_DIR" ]; then
|
||||
print_info "Backing up PGO profiles..."
|
||||
mkdir -p "$BACKUP_PROFILES_DIR"
|
||||
cp -r "$PGO_PROFILES_DIR"/* "$BACKUP_PROFILES_DIR/" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
if [ -d "$BUILD_DIR" ]; then
|
||||
print_info "Removing build directory..."
|
||||
rm -rf "$BUILD_DIR"
|
||||
fi
|
||||
|
||||
if [ -d "$BACKUP_PROFILES_DIR" ]; then
|
||||
print_info "Restoring PGO profiles..."
|
||||
mkdir -p "$PGO_PROFILES_DIR"
|
||||
mv "$BACKUP_PROFILES_DIR"/* "$PGO_PROFILES_DIR/" 2>/dev/null || true
|
||||
rm -rf "$BACKUP_PROFILES_DIR"
|
||||
fi
|
||||
|
||||
print_info "Clean complete!"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Generate stage
|
||||
if [ "$STAGE" == "generate" ]; then
|
||||
print_header "PGO Stage 1: Generate Profile Data"
|
||||
|
||||
# Create build directory
|
||||
mkdir -p "$BUILD_DIR"
|
||||
cd "$BUILD_DIR"
|
||||
|
||||
# Configure
|
||||
print_info "Configuring CMake..."
|
||||
cmake .. \
|
||||
-DCITRON_ENABLE_PGO_GENERATE=ON \
|
||||
-DCITRON_ENABLE_LTO=$ENABLE_LTO \
|
||||
-DCMAKE_BUILD_TYPE=Release
|
||||
|
||||
# Build
|
||||
print_info "Building instrumented Citron (this may take a while)..."
|
||||
cmake --build . -j"$JOBS"
|
||||
|
||||
print_header "Build Complete!"
|
||||
print_info "Next steps:"
|
||||
echo " 1. Run: ./bin/citron"
|
||||
echo " 2. Play games for 15-30 minutes to collect profile data"
|
||||
echo " 3. Exit citron"
|
||||
|
||||
# Clang-specific instructions
|
||||
if [ "$(cmake --system-information | grep CMAKE_CXX_COMPILER_ID | grep -i clang)" ]; then
|
||||
echo " 4. Merge profiles: llvm-profdata merge -output=$PGO_PROFILES_DIR/default.profdata $PGO_PROFILES_DIR/*.profraw"
|
||||
echo " 5. Run: $0 use"
|
||||
else
|
||||
echo " 4. Run: $0 use"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Use stage
|
||||
if [ "$STAGE" == "use" ]; then
|
||||
print_header "PGO Stage 2: Build Optimized Binary"
|
||||
|
||||
# Check if profile data exists
|
||||
if [ ! -d "$PGO_PROFILES_DIR" ] || [ -z "$(ls -A $PGO_PROFILES_DIR 2>/dev/null)" ]; then
|
||||
print_error "No profile data found in $PGO_PROFILES_DIR"
|
||||
print_info "Please run the generate stage first and collect profile data"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Backup profiles if build directory exists
|
||||
if [ -d "$BUILD_DIR" ]; then
|
||||
print_info "Backing up PGO profiles..."
|
||||
mkdir -p "$BACKUP_PROFILES_DIR"
|
||||
cp -r "$PGO_PROFILES_DIR"/* "$BACKUP_PROFILES_DIR/"
|
||||
rm -rf "$BUILD_DIR"
|
||||
fi
|
||||
|
||||
# Create build directory and restore profiles
|
||||
mkdir -p "$BUILD_DIR"
|
||||
if [ -d "$BACKUP_PROFILES_DIR" ]; then
|
||||
mkdir -p "$PGO_PROFILES_DIR"
|
||||
mv "$BACKUP_PROFILES_DIR"/* "$PGO_PROFILES_DIR/"
|
||||
rm -rf "$BACKUP_PROFILES_DIR"
|
||||
fi
|
||||
|
||||
cd "$BUILD_DIR"
|
||||
|
||||
# Configure
|
||||
print_info "Configuring CMake..."
|
||||
cmake .. \
|
||||
-DCITRON_ENABLE_PGO_USE=ON \
|
||||
-DCITRON_ENABLE_LTO=$ENABLE_LTO \
|
||||
-DCMAKE_BUILD_TYPE=Release
|
||||
|
||||
# Build
|
||||
print_info "Building optimized Citron (this may take a while)..."
|
||||
cmake --build . -j"$JOBS"
|
||||
|
||||
print_header "Build Complete!"
|
||||
print_info "Your optimized Citron binary is ready!"
|
||||
print_info "Location: $BUILD_DIR/bin/citron"
|
||||
echo ""
|
||||
print_info "This build is optimized for your specific usage patterns."
|
||||
print_info "Enjoy improved performance! 🚀"
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user