Перейти к содержанию
0x5ce3c

Anomaly Engine modded_exes addon | Патч многоядерности | Modified by Blue Eclipse Team

Рекомендуемые сообщения

 

Суть проблемы:
По умолчанию номер потоков неправильно захватывался, так же все создаваемые потоки принудительно присваивались на 1 логический процессор(1 ядро).

Это 'веселье' вы можете увидеть во вкладке "Производительность" в Диспетчере Задач во время запущеной игры 1-е ядро будет загружено на 100%, а при попытке задать сходство для процесса Anomaly, нагрузка перейдет для 2-го ЛП(второго ядра) и так по кругу.

Особенные особенности:

  • Модифицированный движок в аддоне modded_exes для Anomaly 1.5.2 и 1.5.3 с поддержкой многоядерности и оптимизацией многопоточности исправляет данную проблему.
  • Полная совместимость с стандартной Anomaly и со всеми сборками модов!
  • Благодаря модернизации движка для игроков со слабыми ПК восстановлены старые методы освещения и детализации.
  • Загрузка локаций, инвентаря, КПК и игровой процесс происходит заметно стабильнее и быстрее.

DirectX 11 Доступен! :u1F60A:

Минус:
DirectX 9 По прежнему кривой, по крайней мере может быть стабильная ошибка с указанием функции создания текстуры (D3DXCreateTexture)! :u1F92E:

Спойлер

Суть проблемы:
По умолчанию номер потоков неправильно захватывался, так же все создаваемые потоки принудительно присваивались на 1 логический процессор(1 ядро).

Как увидеть данную проблему?:
1) Открываем Диспетчер задач, далее вкладка Производительность, нажимаем на ЦП(справа будет график), нажимаем правой кнопкой мыши на него, выбираем "изменить график", выбираем "логические процессоры", Это и будут наши ядра процессора.
2) Запускаем игру на самом слабом рендере, DirectX 8, с режимом отладки(debug mode).
3) В меню игры нажимаем на F2, после загрузки тестового уровня, смотрим в диспетчер задач.

Разберём проблему более подробно в коде с момента запуска игры:
 

Спойлер


void xrCore::_initialize(LPCSTR _ApplicationName, LogCallback cb, BOOL init_fs, LPCSTR fs_fname)
{
	xr_strcpy(ApplicationName, _ApplicationName);
	if (0 == init_counter)
	{
#ifdef XRCORE_STATIC
        _clear87();
        _control87(_PC_53, MCW_PC);
        _control87(_RC_CHOP, MCW_RC);
        _control87(_RC_NEAR, MCW_RC);
        _control87(_MCW_EM, MCW_EM);
#endif
		// Init COM so we can use CoCreateInstance
		// HRESULT co_res =
		Params = xr_strdup(GetCommandLine());
		xr_strlwr(Params);
		if (!strstr(Params, "-editor"))
			CoInitializeEx(NULL, COINIT_MULTITHREADED);

		string_path fn, dr, di;

		// application path
		GetModuleFileName(GetModuleHandle(MODULE_NAME), fn, sizeof(fn));
		_splitpath(fn, dr, di, 0, 0);
		strconcat(sizeof(ApplicationPath), ApplicationPath, dr, di);

#ifndef _EDITOR
		//        xr_strcpy(g_application_path, sizeof(g_application_path), ApplicationPath);
#endif

#ifdef _EDITOR
        // working path
        if (strstr(Params, "-wf"))
        {
            string_path c_name;
            sscanf(strstr(Core.Params, "-wf ") + 4, "%[^ ] ", c_name);
            SetCurrentDirectory(c_name);
        }
#endif

		GetCurrentDirectory(sizeof(WorkingPath), WorkingPath);

		// User/Comp Name
		DWORD sz_user = sizeof(UserName);
		GetUserName(UserName, &sz_user);

		DWORD sz_comp = sizeof(CompName);
		GetComputerName(CompName, &sz_comp);

		// Mathematics & PSI detection
		CPU::Detect();

		Memory._initialize(strstr(Params, "-mem_debug") ? TRUE : FALSE);

		DUMP_PHASE;

		InitLog();
		_initialize_cpu();//Интересующая нас функция

		// Debug._initialize ();

		rtc_initialize();

		time_t _time = time(NULL);
		tm* time = localtime(&_time);
		april1 = time ? (time->tm_mday == 1 && time->tm_mon == 3) : false;

		xr_FS = xr_new<CLocatorAPI>();

		xr_EFS = xr_new<EFS_Utils>();
		//. R_ASSERT (co_res==S_OK);

		//Load cmd line from file if it exists
		std::ifstream cmdlineTxt;
		char path_A[MAX_PATH];
		strcpy(path_A, Core.ApplicationPath);
		strcat(path_A, "\\..\\commandline.txt");
		cmdlineTxt.open(path_A);
		
		if (!cmdlineTxt)
		{
			cmdlineTxt.close();
			strcpy(path_A, Core.WorkingPath);
			strcat(path_A, "\\commandline.txt");
			cmdlineTxt.open(path_A);
		}

		if (cmdlineTxt)
		{
			Msg("Found commandline file!");
			std::string line;
			char temp[2048];
			sprintf(temp, Params);
			strcat(temp, " ");
			while (std::getline(cmdlineTxt, line))
			{
				strcat(temp, line.c_str());
				strcat(temp, " ");
			}
			Params = xr_strdup(temp);
		}
		cmdlineTxt.close();
	}
	if (init_fs)
	{
		u32 flags = 0;
		if (0 != strstr(Params, "-build")) flags |= CLocatorAPI::flBuildCopy;
		if (0 != strstr(Params, "-ebuild")) flags |= CLocatorAPI::flBuildCopy | CLocatorAPI::flEBuildCopy;
#ifdef DEBUG
        if (strstr(Params, "-cache")) flags |= CLocatorAPI::flCacheFiles;
        else flags &= ~CLocatorAPI::flCacheFiles;
#endif // DEBUG
#ifdef _EDITOR // for EDITORS - no cache
        flags &= ~CLocatorAPI::flCacheFiles;
#endif // _EDITOR
		flags |= CLocatorAPI::flScanAppRoot;

#ifndef _EDITOR
#ifndef ELocatorAPIH
		if (0 != strstr(Params, "-file_activity")) flags |= CLocatorAPI::flDumpFileActivity;
#endif
#endif
		FS._initialize(flags, 0, fs_fname);
		Msg("'%s' build %d, %s\n", "xrCore", build_id, build_date);

		// demonized: Print modded exes version
		Msg("Modded exes: \nAuthor original version:\nthemrdemonized - github rep:  https://github.com/themrdemonized\n");
		// BLUE ECLIPSE: Print modified version
		Msg("This version modified by 0x5CE3C | Blue Eclipse Team | LDU: 18.05.25 | all rights reserved\n");
		Msg("Game started: %s\n", timeInDMYHMSMMM().c_str());
		Msg("MainThread_id: %d\n", get_thread_id());
		EFS._initialize();
#ifdef DEBUG
#ifndef _EDITOR
        Msg("Process heap 0x%08x", GetProcessHeap());
#endif
#endif // DEBUG
	}
	SetLogCB(cb);
	init_counter++;
}

 

Здесь нас интересует функция _initialize_cpu(); //Которая отвечает за инициализацию нашего процессора, перейдем к ней.
 

Спойлер


//------------------------------------------------------------------------------------
void _initialize_cpu(void)
{
	Msg("* Detected CPU: %s [%s], F%d/M%d/S%d, %.2f mhz, %d-clk 'rdtsc'",
	    CPU::ID.model_name, CPU::ID.v_name,
	    CPU::ID.family, CPU::ID.model, CPU::ID.stepping,
	    float(CPU::clk_per_second / u64(1000000)),
	    u32(CPU::clk_overhead)
	);

	// DUMP_PHASE;

	if (strstr(Core.Params, "-x86"))
	{
		CPU::ID.feature &= ~_CPU_FEATURE_MMX;
		CPU::ID.feature &= ~_CPU_FEATURE_3DNOW;
		CPU::ID.feature &= ~_CPU_FEATURE_SSE;
		CPU::ID.feature &= ~_CPU_FEATURE_SSE2;
		CPU::ID.feature &= ~_CPU_FEATURE_SSE3;
		CPU::ID.feature &= ~_CPU_FEATURE_SSSE3;
		CPU::ID.feature &= ~_CPU_FEATURE_SSE4_1;
		CPU::ID.feature &= ~_CPU_FEATURE_SSE4_2;
	};

	string256 features;
	xr_strcpy(features, sizeof(features), "RDTSC");
	if (CPU::ID.feature & _CPU_FEATURE_MMX) xr_strcat(features, ", MMX");
	if (CPU::ID.feature & _CPU_FEATURE_3DNOW) xr_strcat(features, ", 3DNow!");
	if (CPU::ID.feature & _CPU_FEATURE_SSE) xr_strcat(features, ", SSE");
	if (CPU::ID.feature & _CPU_FEATURE_SSE2) xr_strcat(features, ", SSE2");
	if (CPU::ID.feature & _CPU_FEATURE_SSE3) xr_strcat(features, ", SSE3");
	if (CPU::ID.feature & _CPU_FEATURE_SSSE3) xr_strcat(features, ", SSSE3");
	if (CPU::ID.feature & _CPU_FEATURE_SSE4_1)xr_strcat(features, ", SSE4.1");
	if (CPU::ID.feature & _CPU_FEATURE_SSE4_2)xr_strcat(features, ", SSE4.2");
	if (CPU::ID.feature & _CPU_FEATURE_HTT) xr_strcat(features, ", HTT");

	Msg("* CPU features: %s", features);
	Msg("* CPU cores/threads: %d/%d\n", CPU::ID.n_cores, CPU::ID.n_threads);

	Fidentity.identity(); // Identity matrix
	Didentity.identity(); // Identity matrix
	pvInitializeStatics(); // Lookup table for compressed normals
	FPU::initialize();
	_initialize_cpu_thread();

	g_initialize_cpu_called = true;
}

 

Получили информацию о процессоре. Теперь важный момент! При получении информации о процессоре вызывается функция DWORD ttapi_Init(_processor_info* ID) в файле ttapi.cpp:
 

Спойлер


DWORD ttapi_Init(_processor_info* ID)
{
	if (ttapi_initialized)
		return ttapi_workers_count;

	// System Info
	ttapi_workers_count = ID->n_cores;

	SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);

	DWORD i, dwNumIter;
	volatile DWORD dwDummy = 1;
	LARGE_INTEGER liFrequency, liStart, liEnd;

	QueryPerformanceFrequency(&liFrequency);

	// Get fast spin-loop timings
	dwNumIter = 100000000;

	QueryPerformanceCounter(&liStart);
	for (i = 0; i < dwNumIter; ++i)
	{
		if (dwDummy == 0)
			goto process1;
		_mm_pause();
	}
process1:
	QueryPerformanceCounter(&liEnd);

	// We want 1/25 (40ms) fast spin-loop
	ttapi_dwFastIter = (DWORD)((dwNumIter * liFrequency.QuadPart) / ((liEnd.QuadPart - liStart.QuadPart) * 25));
	//Msg( "fast spin-loop iterations : %u" , ttapi_dwFastIter );

	// Get slow spin-loop timings
	dwNumIter = 10000000;

	QueryPerformanceCounter(&liStart);
	for (i = 0; i < dwNumIter; ++i)
	{
		if (dwDummy == 0)
			goto process2;
		SwitchToThread();
	}
process2:
	QueryPerformanceCounter(&liEnd);

	// We want 1/2 (500ms) slow spin-loop
	ttapi_dwSlowIter = (DWORD)((dwNumIter * liFrequency.QuadPart) / ((liEnd.QuadPart - liStart.QuadPart) * 2));
	//Msg( "slow spin-loop iterations : %u" , ttapi_dwSlowIter );

	SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);

	// Check for override from command line
	char szSearchFor[] = "-max-threads";
	char* pszTemp = strstr(GetCommandLine(), szSearchFor);
	DWORD dwOverride = 0;
	if (pszTemp)
		if (sscanf_s(pszTemp + strlen(szSearchFor), "%u", &dwOverride))
			if ((dwOverride >= 1) && (dwOverride < ttapi_workers_count))
				ttapi_workers_count = dwOverride;

	// Number of helper threads
	ttapi_threads_count = ttapi_workers_count - 1;

	// Creating control structures
	if ((ttapi_threads_handles = (LPHANDLE)malloc(sizeof(HANDLE) * ttapi_threads_count)) == NULL)
		return 0;
	if ((ttapi_worker_params = (PTTAPI_WORKER_PARAMS)malloc(sizeof(TTAPI_WORKER_PARAMS) * ttapi_workers_count)) == NULL)
		return 0;

	// Clearing params
	for (DWORD i = 0; i < ttapi_workers_count; i++)
		memset(&ttapi_worker_params[i], 0, sizeof(TTAPI_WORKER_PARAMS));

	char szThreadName[64];
	DWORD dwThreadId = 0;
	DWORD dwAffinitiMask = ID->affinity_mask;
	DWORD dwCurrentMask = 0x01;

	// Setting affinity
	while (! (dwAffinitiMask & dwCurrentMask))
		dwCurrentMask <<= 1;

	//SetThreadAffinityMask(GetCurrentThread(), dwCurrentMask);
	if(bMaskCalc == true) _SetThreadAffMask(GetCurrThreadHandle(), GetCalcMask());
	//Msg("Master Thread Affinity Mask : 0x%8.8X" , dwCurrentMask );

	// Creating threads
	for (DWORD i = 0; i < ttapi_threads_count; i++)
	{
		// Initializing "enter" "critical section"
		ttapi_worker_params[i].vlFlag = 1;

		if ((ttapi_threads_handles[i] = CreateThread(NULL, 0, &ttapiThreadProc, &ttapi_worker_params[i], 0, &dwThreadId)
		) == NULL)
			return 0;

		// Setting affinity
		do
			dwCurrentMask <<= 1;
		while (! (dwAffinitiMask & dwCurrentMask));

		//SetThreadAffinityMask(ttapi_threads_handles[i], dwCurrentMask);
		if(bMaskCalc == true) _SetThreadAffMask(GetCurrThreadHandle(), GetCalcMask());
		//Msg("Helper Thread #%u Affinity Mask : 0x%8.8X" , i + 1 , dwCurrentMask );

		// Setting thread name
		sprintf_s(szThreadName, "Helper Thread #%u", i);
		SetThreadName(dwThreadId, szThreadName);
	}

	ttapi_initialized = TRUE;

	return ttapi_workers_count;
}

 

Здесь мы видим целый ряд проблем:
1) DWORD dwCurrentMask = 0x01; Код маски процессора равен нулю, это означает что функция SetThreadAffinityMask будет всегда использовать 0-ое ядро, т.е. 1-ое ядро нашего процессора.
2) функция создания потока CreateThread работает в цикле, будет постоянная нагрузка на процессор, сильные лаги\фризы в приложении, возможна рассинхронизация потоков после чего возможен вылет при сильной нагрузке в любой момент времени, так же количество потоков, которые может создать процесс, ограничено доступной виртуальной памятью индивидуально для каждого ПК.
3) Все потоки присваиваются на 1-е ядро в цикле, что сильно снижает производительность.

Простое решение:
1) Передать управление потоками планировщику потоков в системе Windows, закомментировав\удалив функцию //SetThreadAffinityMask
или
Сложное решение:
2) Сделать свою функцию расчёта нагрузки на логические процессоры и таблицу масок процессора для распределения потоков(нагрузки) по ядрам процессора в зависимости от того, какое ядро свободнее через SetThreadAffinityMask.
(такая функция работает у меня, в противном случае управление предоставляется системе).

Теперь вернёмся к файлу _math.cpp, там есть очень интересная функция связанная с потоками:
 

Спойлер


template<typename func, class ... args_types>
void beginThreadex(func f, args_types ... args)
{
	std::thread* out = new std::thread(std::bind(f, args...));
}


void thread_spawn(thread_t* entry, const char* name, unsigned stack, void* arglist)
{
	Debug._initialize(false);

	THREAD_STARTUP* startup = xr_new<THREAD_STARTUP>();
	startup->entry = entry;
	startup->name = (char*)name;
	startup->args = arglist;
	//_beginthread(thread_entry, stack, startup);//было
	beginThreadex(thread_entry, startup);//Стало
}

 

 Функция thread_spawn создает внутриигровые потоки для различных игровых событий таких как:
загрузка текстур, локаций(игрового уровня), создания матриц, игровой паузы, создание скриншотов и сохранений, и т.д.
Здесь важно обратить внимание на функцию _beginthread, которая была заменена на кастомную, после чего повысилась вся производительность внутриигровых потоков.

Последняя основная проблема заключается в задержках Sleep по всему коду движка, данный способ задержки останавливает игровой поток, тем самым создавая фризы\лаги при: сохранениях, спавне объектов\NPC, различных событий. Планируется другой способ создания задержек без видимого эффекта кроме игровой паузы.

Характеристики и описание функций(вся информация взята из интернета, описание функций и характеристик ниже с сайта Microsoft, характеристики дополнены информацией из различных интернет-форумов, исходя из опыта разных программистов):


=============================
функция SetThreadAffinityMask:
Смысл функции SetThreadAffinityMask заключается в том, что она задаёт маску сходства процессора для указанного потока.
Маска сходства потоков — это битовый вектор, в котором каждый бит представляет логический процессор, на котором разрешено выполнять поток. 
Установка такой маски может привести к тому, что потоки получат меньше времени процессора, так как система не сможет запускать потоки на определённых процессорах. 

минусы SetThreadAffinityMask:

Снижение производительности. Установка маски сходства может привести к тому, что потоки получат меньше времени процессора, так как система ограничена в запуске потоков на определённых процессорах.

Ошибки при использовании. Функция может задерживать переход на новое ядро или продолжать работу с текущим ядром до конца временного отрезка. В первом случае это снижает скорость работы программы, во втором — делает бесполезными данные профилирования.

Опасные ситуации. При изменении сходства процесса может возникнуть ситуация, когда поток не сможет работать ни на одном процессоре.

Проблемы с переносимостью. Функция может вызывать сложности при использовании на компьютерах с разным количеством сокетов, ядер и гиперпотоков. 

Примеры последствий:

Ограничение выбора процессора. Например, если в маске SetThreadAffinityMask указан только один процессор, а система выбирает для выполнения другой поток, первый не получает время процессора в течение этого периода.

Затрата времени ожидания. Если один процессор свободен, но система выбирает его для выполнения другого потока, первый поток должен ждать, пока процессор станет доступен снова.
=============================

Функция _beginthread имеет несколько минусов, которые связаны с проблемами с обработкой исключений и дескрипторами потоков:

Проблемы с обработкой исключений

Отсутствие явной обработки исключений. Функция _beginthread устанавливает фрейм исключения, но не вызывает обработчик. Если в потоке возникает необработанное исключение, система завершает всё приложение целиком.

Некорректное поведение в некоторых версиях. Например, в некоторых реализациях функции _beginthread исключение в потоке не обрабатывается должным образом, что может привести к сбоям в работе программы.
 

Проблемы с дескрипторами потоков

Автоматическое закрытие дескриптора. Дескриптор потока, созданный с помощью _beginthread, автоматически закрывается при завершении потока. Это может привести к тому, что вызывающий поток получит недопустимый дескриптор, который система может назначить другому потоку.

Некорректная работа с функциями ожидания. Wait-функции( например функция Sleep) могут некорректно работать с дескрипторами потоков, созданных с помощью _beginthread, из-за автоматического закрытия дескриптора.=============================

В совокупности всех проблемных участков получается следующее:

  1. Использование потоков только на одном логическом процессоре (ядре) может привести к снижению производительности. Это связано с тем, что ресурсы процессора, которые обычно используются для параллельного выполнения нескольких потоков, остаются недоступными для одного потока.
  2. Из-за рассинхронизации потоков мы можем ловить редкий "баг потери управления" - когда не работают клавиши кроме, ESC, возможен вылет при попытке загрузить сохранение.
  3. Из-за бесконечного создания потоков в цикле на 1 ядре процессора(речь про функцию CreateThread вместе с SetThreadAffinityMask) вылет на ровном месте при любой сильной нагрузке, по перегрузке памяти, не имеющей никакого отношения к скриптовой части.
Спойлер

AnomalyDX8.exe | MD5 hash: 86897f24de2399d58964c58b0325eeed
AnomalyDX9.exe | MD5 hash: 2f0c2e38b51b0a2fc6cecd011cf18398
AnomalyDX10.exe | MD5 hash: 2b3cb973bc0a733f3cf67202c22872c3
AnomalyDX11.exe | MD5 hash: a07bbe4f09a1c75b953a0ec199a1c808

Спойлер

Автор оригинального аддона: themrdemonized - репозиторий на гитхабе: тык
Автор модификации аддона: 0x5CE3C

Помимо этого ссылки на авторские права указываются прямо при запуске в лог файле:
Log
Описание Лог Файла:
1) Номер сборки и дата компиляции.
2) Авторских права оригинальной модификации с ссылками на репозиторий github.
3) LDU — это Последняя Дата Обновления(Last Date Updated).
4) Авторские права измененного мода.
5) Функция, которая выводит идентификатор главного потока внутри активного процесса Anomaly(На данный момент удобный инструмент для детальной отладки ошибок движка и разработки 3d-прицелов\Дополнительных внутренних потоков: например, 3d-КПК).

Спойлер

- Вырезан Решейд для DirectX 8 из-за несовместимости с некоторыми сборками.
- На данный момент система распределения ресурсов отлично работает на DirectX 8 (AnomalyDX8.exe). От выбранного DirectX это не зависит (разница только в нагрузке из-за более современных технологий освещения: частиц, наложения визуальных эффектов и обработке данных).

- Исправлены функции создания множества потоков вместе с объектом, но после использования потока объект не удалялся, что со временем приводило к неизбежному вылету на любом ПК с пустым stack trace в логе.

- Заменен метод захвата тактов процессора на современный, что значительно сокращает время вычислительных процессов(будет заметно быстрее грузится локация).

- Исправлена функция SetThreadAffinityMask, которая отвечает за распределение нагрузки на разные логические процессоры внутри текущего процесса.

- Обновлен метод резервирования свободной памяти на SSD и Жестких дисках при нехватке памяти на более современный аналог, улучшен код для более быстрого доступа к кластерам L1, L2, L3 кэша процессора.

- Система распределения нагрузки работает в двух режимах:
1) Режим Одного Ядра - актуален для ПК с 1-2,4 ядрами(стандартный режим Anomaly), оптимизация достигается за счет многопоточности.

2) Многоядерный режим(использование функции SetThreadAffinityMask) - актуален для ПК с 4 и более ядрами. Второй режим включается автоматически, но можно принудительно включить с помощью задания параметра запуска "-max-threads" к вашему процессу Anomaly. Так же это действие можно сделать открыв файл commandline.txt, дописав "-max-threads" в новой строке.

пример:
-nointro
-noramtex
-dbg
-max-threads

- Изменения в DirectX11:

  •  Изменён способ отражения света от объектов из-за чего криво рассеивалась тень, отражения от света, тени от объектов были с неправильным углом.
  • Немного улучшен стандартный метод рисования кадров(немного ускоряет прогрузку динамических кадров, эффектов и частиц).
Спойлер

modded_exes_patched.zip | Yandex Disk
modded_exes_patched.zip | Google Disk
Установка чистую Anomaly:
Папку gamedata и папку bin скопировать в папку с игрой с заменой файлов.

Установка на сборку модов:
1) Папку bin в папку с игрой с заменой файлов.
2) Создайте в Мод Органайзере пустой мод(папку в каталоге с модами сборки) и скопируйте туда папку gamedata.
3) Перезапустите Мод Органайзер(если был запущен), переместите ваш мод в самый низ.

 

Изменено пользователем 0x5ce3c
  • Лайк 1
  • Дурка 1
  • Смущение 1
  • Хабар 1

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
 

круто. а где сорсы?

Изменено пользователем LVutner
  • Лайк 1

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
 
В 07.06.2025 в 21:23, 0x5ce3c сказал:

на DirectX8

У движка X-Ray никогда (кроме билдов S.T.A.L.K.E.R. Oblivion Lost до 2003 года) не было поддержки DirectX 8. Статическое освещение во всей трилогии (а соответственно и в модах по типу Anomaly) было на DirectX 9. Достаточно RivaTuner открыть, и истина предвстанет перед вами.

  • Жму руку 1

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
 
В 07.06.2025 в 21:23, 0x5ce3c сказал:

Многоядерный режим(использование функции SetThreadAffinityMask)

Ладно


Дополнено 2 минуты спустя

image.thumb.png.86d1f2decdf7c0fa25863f357c09cfda.png

Вот бы ещё нагрузку хотя б такую на эти ядра 

  • Сталкерский лайк 1
  • Хабар 1

Мастер по созданию багов в ваших проектах. Обращайтесь :)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
 

ForserX Хотелось бы, но нагрузка (при старте локации или загрузке сохранения после запуска игры) может быть как в стандартной Anomaly, потому что создание потоков я не менял, оно работает в цикле, но затем потоки распределяются по ядрам процессора, в зависимости от того какое ядро нагружено меньше всех.


Дополнено 4 минуты спустя
В 09.06.2025 в 01:07, LVutner сказал:

круто. а где сорсы?

Если движковые правки покажут хорошие результаты у игроков с абсолютно разной конфигурацией длительное время, то когда-нибудь выложу.

  • Смущение 1

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
 

Привет 0x5ce3c. Попробовал установить твой фикс на сборку Аномали-медиум. Та сборка просто распаковывается и играется, без Мод-оргонайзера (его нет в корне игры). Соответственно замена папок "геймдата и бин" не помогла, игра не запускается. Подскажи пожалуйста, что мне надо сделать. Уж больно интересна идея поиграть в многоядерном режиме игры. Заранее спасибо.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
 
5 часов назад, KREPATURA сказал:

Привет 0x5ce3c. Попробовал установить твой фикс на сборку Аномали-медиум. Та сборка просто распаковывается и играется, без Мод-оргонайзера (его нет в корне игры). Соответственно замена папок "геймдата и бин" не помогла, игра не запускается. Подскажи пожалуйста, что мне надо сделать. Уж больно интересна идея поиграть в многоядерном режиме игры. Заранее спасибо.

KREPATURA Приветствую! Хотелось бы уточнить: какая версия сборки, так как автор предлагает 2-ю и 3-ю, и были ли в папке gamedata файлы требующие замену? Что касается запуска, можно попробовать заменить только файлы AnomalyDX*.exe из папки bin и запустить так. Если проблема все равно происходит, то надо делать совместимость.

Изменено пользователем 0x5ce3c

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
 

Как я понял вся соль в задании соответствия логическим процессорам? Сие не дает никакого прироста в производительности. Можно ведь просто зайти в тот же диспетчер задач, ткнуть галочку соответствия процессоров будет идентичный результат, но в этом нет абсолютно никакого смысла.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
 
В 13.06.2025 в 13:36, Коропасий сказал:

Как я понял вся соль в задании соответствия логическим процессорам? Сие не дает никакого прироста в производительности. Можно ведь просто зайти в тот же диспетчер задач, ткнуть галочку соответствия процессоров будет идентичный результат, но в этом нет абсолютно никакого смысла.

Коропасий Возможно вы невнимательно прочли первый абзац темы, а именно:
Суть проблемы:
По умолчанию номер потоков неправильно захватывался, так же все создаваемые потоки принудительно присваивались на 1 логический процессор(1 ядро).


Даже если вы задаете через диспетчер задач сходство, нагрузка всё ровно перейдёт на следующее ядро!!! Проблема кроется внутри самой программы AnomalyDX*.exe, потоки принудительно задаются на 1-е ядро через внутренний планировщик потоков, функция SetThreadAffinityMask, в то время как, вы через внешнюю программу, Диспетчер задач, по простому, отключаете видимость определенных ядер для процесса AnomalyDX*.exe.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Для публикации сообщений создайте учётную запись или авторизуйтесь

Вы должны быть пользователем, чтобы оставить комментарий

Создать учетную запись

Зарегистрируйте новую учётную запись в нашем сообществе. Это очень просто!

Регистрация нового пользователя

Войти

Уже есть аккаунт? Войти в систему.

Войти

  • Последние посетители   0 пользователей онлайн

    Ни одного зарегистрированного пользователя не просматривает данную страницу