Я до сих пор не могу точно объяснить все суммы или почему они появляются только тогда, когда я активирую цикл задержки, но кажется, что это решает все проблемы, которые у меня были. А пока
ВВЕДЕНИЕ
Я получаю нежелательную пульсацию и замедление при рендеринге 2d-частиц, которые должны рисоваться слабо (низкая альфа) и оставлять затухающий след под действием гравитации. Ниже приведено структурированное объяснение. Я думаю, что изолировал это довольно хорошо, но я не могу ни в малейшей степени объяснить, что происходит или какой процесс вызывает мои сбои, так что, если вы можете, это было бы здорово! К вашему сведению: я не думаю, что это гонка данных, я не понимаю, как это возможно здесь.
ЦЕЛЬ:
Я пытаюсь изучить C++ самостоятельно, написав 2D-симуляцию частиц с использованием SDL. Я новичок в этом, поэтому ошибка новичка невозможна. Моделирование должно иметь фиксированный временной шаг обновления физики для детерминированного поведения. Я реализовал это задолго до того, как начал использовать многопоточность, с помощью переменной «lag». Физика и рендеринг в проекте какое-то время не менялись и до сих пор всегда работали нормально.
Чтобы увеличить количество частиц, я начал использовать многопоточность. Сначала я запускал параллельные рабочие потоки физики, ждал обновления, а затем выполнял рендеринг. Это сработало без каких-либо проблем. Теперь я запускаю рабочие потоки физики и параллельно рендерю данные из предыдущего кадра. На самом деле это работает отлично, пока я не попытаюсь реализовать фиксированный физический временной шаг.
СТРУКТУРА КОДА:
это общий обзор:
во время (работа){
- вычисление времени, прошедшего с момента начала предыдущего кадра (задержка)
- вычисление количества физических кадров ( while(lag>= TIMESTEP){updates ++; lag -= TIMESTEP;} )
- вызов физического работника в нескольких потоках на частицахWrite (указатель)
- рендеринг всех частиц на частицахЧтение (указатель)
- дождаться потоков физического работника
- поменять местами частицыЧтение и частицыЗапись
- SDL_Delay до тех пор, пока кадрTime = TIMESTEP (если кадр был быстрее, чем TIMESTEP)
СИМПТОМЫ:
Когда я реализую цикл в обновлении физики, который выполняет «обновления» раз, частицы «пульсируют», их альфа-канал отображается неправильно. Иногда они, кажется, прыгают из позиции, но их общая модель движения правильная.
Некоторое замедление также нарастает, кадры становятся все медленнее и медленнее, пока я не смотрю Powerpoint, в течение 10-20 секунд.
ЧТО Я ПРОБОВАЛ:
Вот здесь это становится странным, и почему я вообще не понимаю, что происходит.
--> swap while для цикла структура цикла for для «обновлений» > проблема сохраняется.
--> передайте обновление в PhysicsWorker по ссылке, а не просто передавайте (также пробовал указатель) > проблема сохраняется.
--> убедитесь, что я не записываю обновления в физический цикл > проблема сохраняется.
--> полностью выведите цикл « while (updates >0)» из PhysicsWorker, обернув его вокруг всего кода кадра и используя if (счетчик == 0) для части рендеринга (т.е. убедитесь, что он визуализируется только один раз) > проблема сохраняется.
--> передайте значение обновлений в новую переменную в (или непосредственно перед вызовом) Physicsworker > проблема сохраняется.
--> измените тип обновлений переменной (Uint32, int, size_t) > проблема сохраняется.
--> задержка печати и обновление каждого 50-го кадра на убедитесь, что это разумные значения и не накапливаются странным образом (они кажутся совершенно нормальными и, как и ожидалось).
--> уменьшите количество частиц в 10 раз, чтобы все это не могло занять больше времени, чем TIMESTEP > проблема сохраняется (замедление занимает намного больше времени, но оно все еще существует).
--> поменяйте местами последовательность переменных в объявлении updateChunk void > проблема сохраняется.
НО когда я просто говорю "Uint32 update=1" (или 0. или 2. или 3. Не важно) проблема полностью решена и все работает нормально! Это то, что мне кажется совершенно странным, потому что, когда я включаю переменную задержки, обновления также имеют значение Uint32 = 1, но вычисляются только перед их использованием.
Кажется, имеет значение только то, использую ли я вычисленное значение обновлений или нет. Не имеет значения, где я поместил цикл (внутри или вне потоков), какова на самом деле длительность кадра или что-то еще. Я этого не понимаю. Как уже было сказано, я печатал переменную обновления каждый 50-й кадр, и она всегда равна 1 для меньшего количества частиц.
АКТИЧЕСКИЙ КОД
Вот соответствующие фрагменты кода, сначала обновление void:
void updateChunk(std::vector
& particles, const std::vector& planets, size_t start, size_t end, Uint32 updates)
{
Uint32 up = updates;
while (up > 0)
{
for (size_t i = start; i < end; ++i)
{
// create handles
Particle& p = particles;
// Update acceleration
p.ax = -p.vx*dampingConstant - signFunction(p.vx)*fixedDamping;
p.ay = -p.vy*dampingConstant + GRAV - signFunction(p.vy)*fixedDamping;
// Calculate planet acceleration
for (size_t s = 0; s < planets.size(); ++s)
{
// Get distances from planet
const Planet& planet = planets;
float dxp = p.x - float(planet.x);
float dyp = p.y - float(planet.y);
float dMag = fastSqrt(float(dxp * dxp + dyp * dyp));
// Sin and cos from fraction, not sin function. Small float in denominator to ensore no /0.
float axp = std::abs(dxp / (dMag + 0.0001f)) * (planet.gravity / (dMag + 4.0f));
float ayp = std::abs(dyp / (dMag + 0.0001f)) * (planet.gravity / (dMag + 4.0f));
p.ax -= signFunction(dxp)*axp;
p.ay -= signFunction(dyp)*ayp;
}
// Update velocity
p.vy += (p.ay * (float)TIMESTEP) / 1000.0f;
p.vx += (p.ax * (float)TIMESTEP) / 1000.0f;
// Update position
p.x += (p.vx*(float)TIMESTEP) / 1000.0f;
p.y += (p.vy*(float)TIMESTEP) / 1000.0f;
// Bounce of walls
if (p.x - EPS < 0) {
p.vx *= -1.0f;
p.x = EPS;
}
else if (p.x + EPS > WIDTH - 1) {
p.vx *= -1.0f;
p.x = WIDTH - 1 - EPS;
}
if (p.y - EPS = HEIGHT - 1) {
p.vy *= -1.0f;
p.y = HEIGHT - 1 - EPS;
}
}
up -= 1;
}
}
Остальное находится в int main(). Я, очевидно, опустил ненужные части, но включил всю часть рендеринга, поскольку вижу и визуальные сбои:
while (running)
{
// time logic, get current time, elapsed time, total elapsed time etc.
Uint32 currentTime = SDL_GetTicks();
Uint32 elapsedTime = currentTime - previousTime;
previousTime = currentTime;
lag += elapsedTime;
totalTime += elapsedTime;
// Calculate the amount of physics updates
Uint32 updates = 0;
while (lag >= TIMESTEP) {
updates++;
lag -= TIMESTEP;
}
// Call the physics update function on multiple threads.
std::vector threads;
for (int t = 0; t < NUMTHREADS; ++t) {
size_t start = t chunkSize;
size_t end = (t == NUMTHREADS - 1) ? particlesWrite->size() : start + chunkSize;
threads.emplace_back(updateChunk, std::ref(*(particlesWrite)), std::ref(planets), start, end, updates);
}
// Set Render Draw colour for all particles
SDL_SetRenderDrawColor(renderer, 0, 255, 255, 30);
// Render previous frame
// Render our moving pixel, if not equal to last known position
// Cast to int first
for (int u = 0; u < particlesA.size(); u++)
{
// create handles
Particle& p = (*particlesRead);
PrevPosition& pp = prevPositions;
int newX = int(p.x + 0.5f);
int newY = int(p.y + 0.5f);
int dx = abs(newX - pp.x);
int dy = abs(newY - pp.y);
if (newX != pp.x || newY != pp.y) {
if (abs(dx)
Подробнее здесь: https://stackoverflow.com/questions/798 ... ting-fixed
Мобильная версия