mirror of
https://github.com/jessicanataliagta/PSPRecomp
synced 2026-09-26 08:41:08 -04:00
mais otimizaçoes
mais otimizaçoes
This commit is contained in:
+10
-2
@@ -36,12 +36,16 @@ endif()
|
||||
# unbounded product exhausts physical memory. Cap the per-cl.exe degree here and
|
||||
# let the build scripts pick a memory-aware value.
|
||||
set(PSPRECOMP_MSVC_MP_JOBS "0" CACHE STRING
|
||||
"Max cl.exe front-ends per /MP invocation (0 = one per logical core)")
|
||||
"Max cl.exe front-ends (0 = logical-core default, 1 = disable /MP)")
|
||||
if(NOT PSPRECOMP_MSVC_MP_JOBS MATCHES "^[0-9]+$")
|
||||
message(FATAL_ERROR "PSPRECOMP_MSVC_MP_JOBS must be a non-negative integer")
|
||||
endif()
|
||||
if(PSPRECOMP_MSVC_MP_JOBS STREQUAL "0")
|
||||
set(PSPRECOMP_MSVC_MP_FLAG "/MP")
|
||||
elseif(PSPRECOMP_MSVC_MP_JOBS STREQUAL "1")
|
||||
# Omitting /MP is materially different from /MP1: the latter still creates
|
||||
# and communicates with a child compiler process and can fail with D8040.
|
||||
set(PSPRECOMP_MSVC_MP_FLAG "")
|
||||
else()
|
||||
set(PSPRECOMP_MSVC_MP_FLAG "/MP${PSPRECOMP_MSVC_MP_JOBS}")
|
||||
endif()
|
||||
@@ -99,7 +103,11 @@ add_executable(dump_function tools/dump_function.cpp)
|
||||
target_link_libraries(dump_function PRIVATE psprecomp_core)
|
||||
|
||||
function(psprecomp_enable_host_avx target)
|
||||
if(PSPRECOMP_HOST_AVX AND MSVC)
|
||||
# /arch:AVX appears after the directory-wide /arch:AVX2 option in MSVC's
|
||||
# command line and therefore silently downgrades the whole target. AVX2
|
||||
# already includes the AVX scalar instructions this compatibility switch
|
||||
# was introduced for, so do not emit the weaker override in an AVX2 build.
|
||||
if(PSPRECOMP_HOST_AVX AND MSVC AND NOT PSPRECOMP_NATIVE_AVX2)
|
||||
target_compile_options(${target} PRIVATE /arch:AVX)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
@@ -126,7 +126,7 @@ public:
|
||||
: owner_(owner), ram_data_(ram_data), ram_limit8_(limit8),
|
||||
ram_limit16_(limit16), ram_limit32_(limit32),
|
||||
write_watch_enabled_(write_watch) {}
|
||||
[[nodiscard]] static constexpr std::uint32_t ram_offset_of_fast(
|
||||
[[nodiscard]] PSPRECOMP_MEMORY_FAST_PATH static constexpr std::uint32_t ram_offset_of_fast(
|
||||
std::uint32_t address) noexcept {
|
||||
return (address & 0x1FFFFFFFu) - GuestMemory::kPhysicalBase;
|
||||
}
|
||||
@@ -261,20 +261,20 @@ private:
|
||||
// Canonicalize and rebase in one step. An address below kPhysicalBase --
|
||||
// EDRAM included -- wraps to a value far above any RAM size, so a single
|
||||
// unsigned compare rejects it along with every out-of-range access.
|
||||
[[nodiscard]] static std::uint32_t ram_offset_of(std::uint32_t address) noexcept {
|
||||
[[nodiscard]] PSPRECOMP_MEMORY_FAST_PATH static std::uint32_t ram_offset_of(std::uint32_t address) noexcept {
|
||||
return (address & 0x1FFFFFFFu) - kPhysicalBase;
|
||||
}
|
||||
|
||||
// Guest memory is little-endian, so on a little-endian host these are the
|
||||
// same bytes the previous per-byte assembly produced, in one access.
|
||||
[[nodiscard]] static std::uint16_t read_le16(const std::uint8_t *source) noexcept {
|
||||
[[nodiscard]] PSPRECOMP_MEMORY_FAST_PATH static std::uint16_t read_le16(const std::uint8_t *source) noexcept {
|
||||
std::uint16_t value{};
|
||||
std::memcpy(&value, source, sizeof(value));
|
||||
if constexpr (std::endian::native == std::endian::big)
|
||||
value = static_cast<std::uint16_t>((value >> 8u) | (value << 8u));
|
||||
return value;
|
||||
}
|
||||
[[nodiscard]] static std::uint32_t read_le32(const std::uint8_t *source) noexcept {
|
||||
[[nodiscard]] PSPRECOMP_MEMORY_FAST_PATH static std::uint32_t read_le32(const std::uint8_t *source) noexcept {
|
||||
std::uint32_t value{};
|
||||
std::memcpy(&value, source, sizeof(value));
|
||||
if constexpr (std::endian::native == std::endian::big)
|
||||
@@ -282,12 +282,12 @@ private:
|
||||
((value << 8u) & 0x00FF0000u) | ((value << 24u) & 0xFF000000u);
|
||||
return value;
|
||||
}
|
||||
static void write_le16(std::uint8_t *destination, std::uint16_t value) noexcept {
|
||||
PSPRECOMP_MEMORY_FAST_PATH static void write_le16(std::uint8_t *destination, std::uint16_t value) noexcept {
|
||||
if constexpr (std::endian::native == std::endian::big)
|
||||
value = static_cast<std::uint16_t>((value >> 8u) | (value << 8u));
|
||||
std::memcpy(destination, &value, sizeof(value));
|
||||
}
|
||||
static void write_le32(std::uint8_t *destination, std::uint32_t value) noexcept {
|
||||
PSPRECOMP_MEMORY_FAST_PATH static void write_le32(std::uint8_t *destination, std::uint32_t value) noexcept {
|
||||
if constexpr (std::endian::native == std::endian::big)
|
||||
value = ((value >> 24u) & 0x000000FFu) | ((value >> 8u) & 0x0000FF00u) |
|
||||
((value << 8u) & 0x00FF0000u) | ((value << 24u) & 0xFF000000u);
|
||||
|
||||
@@ -71,7 +71,7 @@ public:
|
||||
using RecompiledEntryFunction = void (*)(Runtime &, AllegrexContext &, std::uint16_t,
|
||||
GuestMemory::AotFastView &);
|
||||
using HleFunction = std::function<void(Runtime &, AllegrexContext &)>;
|
||||
using NativeFastPath = std::function<void(Runtime &, AllegrexContext &)>;
|
||||
using NativeFastPath = void (*)(Runtime &, AllegrexContext &);
|
||||
|
||||
explicit Runtime(std::uint32_t ram_size = 32u * 1024u * 1024u);
|
||||
|
||||
|
||||
+37
-17
@@ -9,7 +9,7 @@ option(PSPRECOMP_VCS_AOT_LTO
|
||||
"Include generated VCS AOT units in MSVC whole-program optimization" OFF)
|
||||
|
||||
option(PSPRECOMP_PROFILE_GUIDED_AOT
|
||||
"Optimize the measured VCS hot AOT units above the base generated level" OFF)
|
||||
"Optimize the measured VCS hot AOT units above the base generated level" ON)
|
||||
set(PSPRECOMP_HOT_GENERATED_OPT_LEVEL "2" CACHE STRING
|
||||
"Optimization level for profile-guided VCS AOT units (0, 1, 2 or 3)")
|
||||
set_property(CACHE PSPRECOMP_HOT_GENERATED_OPT_LEVEL PROPERTY STRINGS 0 1 2 3)
|
||||
@@ -17,24 +17,21 @@ if(NOT PSPRECOMP_HOT_GENERATED_OPT_LEVEL MATCHES "^[0-3]$")
|
||||
message(FATAL_ERROR "PSPRECOMP_HOT_GENERATED_OPT_LEVEL must be 0, 1, 2 or 3")
|
||||
endif()
|
||||
|
||||
# Inline expansion is what makes MSVC pathological on parts of this corpus.
|
||||
# Measured on generated_unit_0000/0002/0004/0005: /Ob3 and /Ob1 never finish (>90 s,
|
||||
# 0.6-2.5 GB each and still climbing), while /Ob0 compiles the same unit in ~10 s
|
||||
# at 0.18 GB. Healthy units are unaffected by the level (0104: 9.1 s at /Ob3,
|
||||
# 9.4 s at /Ob1). The blow-up therefore comes from expanding the explicitly
|
||||
# inline runtime helpers inside the very large generated entry functions, not
|
||||
# from the units' size or control-flow density.
|
||||
#
|
||||
# Default to /Ob0 so the build completes. This costs AOT runtime performance --
|
||||
# the object drops from 2.77 MB to 0.68 MB, so a lot of helper inlining is being
|
||||
# given up. Raise this once the offending helper is identified and marked
|
||||
# noinline individually.
|
||||
# Full /Ob3 is valuable at runtime but several large, cold units still make
|
||||
# MSVC spend minutes and close to a gigabyte per compiler process. Keep those
|
||||
# units buildable, then override this level only for the measured hot corpus.
|
||||
set(PSPRECOMP_GENERATED_INLINE_LEVEL "0" CACHE STRING
|
||||
"MSVC /Ob level for generated VCS AOT units (0, 1, 2 or 3)")
|
||||
set_property(CACHE PSPRECOMP_GENERATED_INLINE_LEVEL PROPERTY STRINGS 0 1 2 3)
|
||||
if(NOT PSPRECOMP_GENERATED_INLINE_LEVEL MATCHES "^[0-3]$")
|
||||
message(FATAL_ERROR "PSPRECOMP_GENERATED_INLINE_LEVEL must be 0, 1, 2 or 3")
|
||||
endif()
|
||||
set(PSPRECOMP_HOT_GENERATED_INLINE_LEVEL "3" CACHE STRING
|
||||
"MSVC /Ob level for measured hot VCS AOT units (0, 1, 2 or 3)")
|
||||
set_property(CACHE PSPRECOMP_HOT_GENERATED_INLINE_LEVEL PROPERTY STRINGS 0 1 2 3)
|
||||
if(NOT PSPRECOMP_HOT_GENERATED_INLINE_LEVEL MATCHES "^[0-3]$")
|
||||
message(FATAL_ERROR "PSPRECOMP_HOT_GENERATED_INLINE_LEVEL must be 0, 1, 2 or 3")
|
||||
endif()
|
||||
|
||||
file(GLOB VCS_GENERATED CONFIGURE_DEPENDS "${VCS_PROFILE_DIR}/generated/*.cpp")
|
||||
if(MSVC)
|
||||
@@ -61,10 +58,13 @@ else()
|
||||
endif()
|
||||
|
||||
if(PSPRECOMP_PROFILE_GUIDED_AOT)
|
||||
# This unit is hot in the current capture and finishes /Ob3 without
|
||||
# triggering MSVC's pathological optimizer growth. It also contains the
|
||||
# validated native collision leaf at guest PC 0x088B1554. Other measured
|
||||
# units remain /Ob0 until their oversized helpers are split/noinline.
|
||||
set(VCS_HOT_UNIT_IDS
|
||||
0018 0023 0024 0043 0044 0066 0067 0077 0080 0084 0087
|
||||
0152 0154 0155 0169 0170 0173 0197 0200 0205 0216 0219)
|
||||
set(VCS_HOT_SOURCES "${VCS_PROFILE_DIR}/generated/generated_registry.cpp")
|
||||
0043)
|
||||
set(VCS_HOT_SOURCES)
|
||||
foreach(VCS_UNIT_ID IN LISTS VCS_HOT_UNIT_IDS)
|
||||
set(VCS_UNIT_SOURCE "${VCS_PROFILE_DIR}/generated/generated_unit_${VCS_UNIT_ID}.cpp")
|
||||
if(NOT EXISTS "${VCS_UNIT_SOURCE}")
|
||||
@@ -81,7 +81,7 @@ if(PSPRECOMP_PROFILE_GUIDED_AOT)
|
||||
set(VCS_HOT_MSVC_OPT "/O${PSPRECOMP_HOT_GENERATED_OPT_LEVEL}")
|
||||
endif()
|
||||
set(VCS_HOT_MSVC_OPTIONS
|
||||
"${VCS_HOT_MSVC_OPT};/Ob${PSPRECOMP_GENERATED_INLINE_LEVEL};/bigobj;${PSPRECOMP_MSVC_MP_FLAG}")
|
||||
"${VCS_HOT_MSVC_OPT};/Ob${PSPRECOMP_HOT_GENERATED_INLINE_LEVEL};/bigobj;${PSPRECOMP_MSVC_MP_FLAG}")
|
||||
if(PSPRECOMP_LTO AND NOT PSPRECOMP_VCS_AOT_LTO)
|
||||
string(APPEND VCS_HOT_MSVC_OPTIONS ";/GL-")
|
||||
endif()
|
||||
@@ -93,6 +93,25 @@ if(PSPRECOMP_PROFILE_GUIDED_AOT)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(MSVC AND PSPRECOMP_NATIVE_AVX2)
|
||||
# MSVC 19.44's AVX2 optimizer crashes (C1001/c2.dll) on this translation
|
||||
# unit, while the identical /Ox /Ob0 compile completes in ~3 s with AVX.
|
||||
# Keep AVX2 for the host and the other AOT units; append the weaker ISA only
|
||||
# to the known compiler-bug trigger (the last /arch switch wins in cl.exe).
|
||||
set(PSPRECOMP_VCS_MSVC_AVX_FALLBACK_UNITS "0018;0022;0035;0089;0091;0117;0164" CACHE STRING
|
||||
"Generated VCS units forced to AVX because MSVC 19.44 crashes with AVX2")
|
||||
set(VCS_MSVC_AVX_FALLBACK_SOURCES)
|
||||
foreach(VCS_UNIT_ID IN LISTS PSPRECOMP_VCS_MSVC_AVX_FALLBACK_UNITS)
|
||||
set(VCS_UNIT_SOURCE "${VCS_PROFILE_DIR}/generated/generated_unit_${VCS_UNIT_ID}.cpp")
|
||||
if(NOT EXISTS "${VCS_UNIT_SOURCE}")
|
||||
message(FATAL_ERROR "VCS AVX fallback unit is missing: ${VCS_UNIT_SOURCE}")
|
||||
endif()
|
||||
list(APPEND VCS_MSVC_AVX_FALLBACK_SOURCES "${VCS_UNIT_SOURCE}")
|
||||
endforeach()
|
||||
set_property(SOURCE ${VCS_MSVC_AVX_FALLBACK_SOURCES} APPEND PROPERTY
|
||||
COMPILE_OPTIONS "/arch:AVX")
|
||||
endif()
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
|
||||
set_source_files_properties("${VCS_PROFILE_DIR}/host/vcs_profile.cpp" PROPERTIES COMPILE_OPTIONS "-O2")
|
||||
endif()
|
||||
@@ -224,6 +243,7 @@ if(PSPRECOMP_BUILD_PROFILE_TESTS)
|
||||
endif()
|
||||
add_executable(vcs_profile_tests tests/vcs_profile_tests.cpp ${VCS_HOST_SOURCES})
|
||||
vcs_target_common(vcs_profile_tests)
|
||||
vcs_set_runtime_output(vcs_profile_tests)
|
||||
add_test(NAME vcs_profile_tests COMMAND vcs_profile_tests)
|
||||
|
||||
add_executable(vcs_config_tests
|
||||
|
||||
@@ -48,7 +48,7 @@ GeometryDebugColors=false
|
||||
DumpGpuFrameVblank=0
|
||||
|
||||
[Timing]
|
||||
FrameRate=60
|
||||
FrameRate=120
|
||||
RealtimeSpeedDiagnostics=false
|
||||
RealtimeSpeedIntervalVblanks=240
|
||||
|
||||
|
||||
@@ -31,7 +31,10 @@ void collision_fast_path(psprecomp::Runtime &runtime, psprecomp::AllegrexContext
|
||||
static const bool enabled = [] {
|
||||
if (truthy(std::getenv("PSPRECOMP_NO_FAST_088B1554"))) return false;
|
||||
if (truthy(std::getenv("PSPRECOMP_VALIDATE_FAST_088B1554"))) return true;
|
||||
return truthy(std::getenv("PSPRECOMP_ENABLE_FAST_088B1554"));
|
||||
// This leaf has already matched the generated AOT reference across
|
||||
// real gameplay validation runs. Keep it on in production; the NO_
|
||||
// switch remains available for an immediate A/B or compatibility bisect.
|
||||
return true;
|
||||
}();
|
||||
|
||||
if (!enabled) {
|
||||
|
||||
@@ -32,7 +32,7 @@ echo VCS - PERFORMANCE INCREMENTAL BUILD
|
||||
echo Build pipeline restored to the last known-good pre-reorganization behavior.
|
||||
echo CMake: !CMAKE_EXE!
|
||||
echo Compile workers: %JOBS% ^| AOT /MP%JOBS% ^| MSBuild /m:1
|
||||
echo AOT inlining: /Ob0 ^(MSVC optimizer blow-up workaround, see profile CMakeLists^)
|
||||
echo AOT inlining: /Ob3 hot measured units ^| /Ob0 cold units
|
||||
echo Link: host/core LTCG only ^| generated AOT /GL- ^| LTCG status visible
|
||||
echo Build dir preserved: %BUILD%
|
||||
echo ================================================================
|
||||
@@ -47,7 +47,10 @@ echo [1/7] Configuring without deleting existing objects...
|
||||
-DPSPRECOMP_AOT_PRODUCTION_FASTPATHS=ON ^
|
||||
-DPSPRECOMP_MSVC_CGTHREADS=0 ^
|
||||
-DPSPRECOMP_MSVC_MP_JOBS=%JOBS% ^
|
||||
-DPSPRECOMP_PROFILE_GUIDED_AOT=ON ^
|
||||
-DPSPRECOMP_HOT_GENERATED_OPT_LEVEL=3 ^
|
||||
-DPSPRECOMP_GENERATED_INLINE_LEVEL=0 ^
|
||||
-DPSPRECOMP_HOT_GENERATED_INLINE_LEVEL=3 ^
|
||||
-DPSPRECOMP_VCS_AOT_LTO=OFF ^
|
||||
-DPSPRECOMP_BUILD_TESTS=ON ^
|
||||
-DPSPRECOMP_BUILD_PROFILE_TESTS=ON
|
||||
|
||||
@@ -3,7 +3,8 @@ setlocal EnableExtensions
|
||||
for %%I in ("%~dp0..\..\..") do set "REPO=%%~fI"
|
||||
for %%I in ("%~dp0..") do set "PROFILE=%%~fI"
|
||||
|
||||
set "BIN=%REPO%\out\vcs-release\bin\Release\VCSNative.exe"
|
||||
set "BIN=%REPO%\out\vcs-ninja\bin\Release\VCSNative.exe"
|
||||
if not exist "%BIN%" set "BIN=%REPO%\out\vcs-release\bin\Release\VCSNative.exe"
|
||||
if not exist "%BIN%" set "BIN=%REPO%\out\vcs-fast\bin\Release\VCSNative.exe"
|
||||
if not exist "%BIN%" (
|
||||
echo VCSNative.exe not found. Run build_release.bat or build_fast.bat first.
|
||||
@@ -13,6 +14,7 @@ if not exist "%BIN%" (
|
||||
set "GAME=%~1"
|
||||
if "%GAME%"=="" if exist "%PROFILE%\game\PSP_GAME\SYSDIR\EBOOT_DECRYPTED.ELF" set "GAME=%PROFILE%\game"
|
||||
if "%GAME%"=="" if exist "%~dp0PSP_DATA\PSP_GAME\SYSDIR\EBOOT_DECRYPTED.ELF" set "GAME=%~dp0PSP_DATA"
|
||||
if "%GAME%"=="" if exist "%REPO%\out\vcs-dev\bin\Release\PSP_DATA\PSP_GAME\SYSDIR\EBOOT_DECRYPTED.ELF" set "GAME=%REPO%\out\vcs-dev\bin\Release\PSP_DATA"
|
||||
if "%GAME%"=="" (
|
||||
echo Game root not found. Pass it as the first argument or run prepare_game.ps1.
|
||||
exit /b 4
|
||||
@@ -35,22 +37,20 @@ set "PSPRECOMP_CONFIG=%PROFILE%\config\VCSNative.ini"
|
||||
set "PSPRECOMP_GE_BACKEND=directx12"
|
||||
set "PSPRECOMP_GE_GPU_TELEMETRY=0"
|
||||
set "PSPRECOMP_GE_GPU_REPORT=0"
|
||||
set "PSPRECOMP_GE_GPU_SKIP_SOFTWARE_RASTER=1"
|
||||
set "PSPRECOMP_GE_ASYNC="
|
||||
set "PSPRECOMP_GE_GPU_SKIP_SOFTWARE_RASTER="
|
||||
set "PSPRECOMP_CHAIN_DEPTH="
|
||||
set "PSPRECOMP_TIME_TICK_DISPATCHES="
|
||||
set "PSPRECOMP_GE_GPU_HW_CULL=1"
|
||||
set "PSPRECOMP_TIME_TICK_DISPATCHES=4096"
|
||||
set "PSPRECOMP_DX12_DEBUG=0"
|
||||
set "PSPRECOMP_DX12_GE_READBACK=0"
|
||||
set "PSPRECOMP_DX12_GE_STRICT=0"
|
||||
set "PSPRECOMP_DX12_TEXTURE_UPLOAD_RING=1"
|
||||
set "PSPRECOMP_GE_ASYNC=1"
|
||||
set "PSPRECOMP_GE_PARALLEL_VERTEX_DECODE=1"
|
||||
set "PSPRECOMP_GE_PARALLEL_VERTEX_THRESHOLD=768"
|
||||
set "PSPRECOMP_GE_PARALLEL_VERTEX_MAX_WORKERS=6"
|
||||
set "PSPRECOMP_GE_PARALLEL_VERTEX_DECODE=0"
|
||||
set "PSPRECOMP_RASTER_THREADS=4"
|
||||
set "PSPRECOMP_GE_DIRECT_NONINDEXED_DRAW=1"
|
||||
set "PSPRECOMP_DX12_PACKED_0115=1"
|
||||
set "PSPRECOMP_DX12_NATIVE_INDEXED_DRAW=1"
|
||||
set "PSPRECOMP_DX12_BATCH_MERGE=1"
|
||||
set "PSPRECOMP_CHAIN_DEPTH=1024"
|
||||
set "PSPRECOMP_ENABLE_FAST_088B1554=1"
|
||||
set "PSPRECOMP_GE_GPU_DUAL_FRAME=0"
|
||||
|
||||
|
||||
+5
-12
@@ -254,8 +254,8 @@ bool Runtime::invoke_chained_call(AllegrexContext &ctx, GuestMemory::AotFastView
|
||||
// Generation alone is sufficient: it increments on every PSP thread
|
||||
// ownership change. Avoid constructing/checking a two-field token on every
|
||||
// dynamic native chain boundary in the city hot path.
|
||||
#if !defined(PSPRECOMP_AOT_PRODUCTION_FASTPATHS)
|
||||
const std::uint64_t caller_generation = g_runtime_thread_switch_generation_fast;
|
||||
#if !defined(PSPRECOMP_AOT_PRODUCTION_FASTPATHS)
|
||||
if (g_pre_chained_call_hook != nullptr)
|
||||
g_pre_chained_call_hook(*this, ctx, target_pc, native_depth);
|
||||
#endif
|
||||
@@ -275,21 +275,14 @@ bool Runtime::invoke_chained_call(AllegrexContext &ctx, GuestMemory::AotFastView
|
||||
if (track_dispatch_counters_) ++chained_dispatches_;
|
||||
#endif
|
||||
|
||||
#if defined(PSPRECOMP_AOT_PRODUCTION_FASTPATHS)
|
||||
// Any scheduler boundary that actually switches PSP ownership marks the
|
||||
// complete native chain invalid. The direct-chain path already uses this
|
||||
// byte; dynamic JR/JALR chains can use the same invariant and avoid two
|
||||
// process-global generation loads per call in production.
|
||||
if (chain_context_invalidated_) {
|
||||
(void)account_dispatch_work(ctx, false);
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
// Dynamic targets may enter a profile/native function which switches PSP
|
||||
// ownership without passing through run_starvation_boundary(). Keep the
|
||||
// generation guard here even in production; compile-time direct chains use
|
||||
// chain_context_invalidated_ and retain their cheaper hot path.
|
||||
if (caller_generation != g_runtime_thread_switch_generation_fast) {
|
||||
(void)account_dispatch_work(ctx, false);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return account_dispatch_work(ctx, true);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user