Russian Qt Forum
Март 28, 2024, 11:34 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: 1 [2]   Вниз
  Печать  
Автор Тема: Большие тормоза из-за QVector?  (Прочитано 13751 раз)
Old
Джедай : наставник для всех
*******
Online Online

Сообщений: 4349



Просмотр профиля
« Ответ #15 : Январь 29, 2018, 11:56 »

Вектор реально тормозит.
Скорее это называется "неправильное использование вектора".
Если вы знаете, как устроен вектор, то никогда не будете расширять его по-элементно. Он для этого не предназначен, несмотря на то, что такая возможность есть.
Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3257


Просмотр профиля
« Ответ #16 : Январь 29, 2018, 14:17 »

Эта проблема известна (есть тикеты ...
Как быстро все "обрастает легендами" Улыбающийся Я знаю эту задачу, поверьте, какие-то нюансы контейнеров здесь ну совершенно ни при чем. Нужно заниматься алгоритмом (а не синтаксическим сахарином)



Вектор реально тормозит. Избавление от push-ей сократило время раз в 15.
Чисто переписать полученые 2000-3000 треугольников в массив для рисования занимает 30 мс. Вот и что с этим делать?

Ну и опять же проблема не в тормозах, а в функционале - отсутствие range insert.
Его кстати ничего не мешает добавить
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #17 : Январь 29, 2018, 14:41 »

Вектор реально тормозит. Избавление от push-ей сократило время раз в 15.
Чисто переписать полученые 2000-3000 треугольников в массив для рисования занимает 30 мс. Вот и что с этим делать?
Там на каждой итерации всего 2 resize (один для вертексов, др для фейсов). Наведите порядок в структурах и алгоритме, все за полчаса ляжет. Схемка выше, если что неясно - спрашивайте, попробую помочь
Записан
qtkoder777
Частый гость
***
Offline Offline

Сообщений: 245


Просмотр профиля
« Ответ #18 : Январь 30, 2018, 14:41 »

Надо избежать повторного добавления точек деления у соседних треугольников.
В этом есть смысл если есть фейсы (индексы) которых в приведенном коде не видно. Тогда структуры примерно такие
Код
C++ (Qt)
struct Triangle {
int m_index[3];  // индексы вертексов  
int m_iEdges[3];  // индексы ребер  
};
 
struct Edge {
 bool operator < ( const Edge & other ) const { return m_ver < other.m_ver; }
 
// data
 typedef std::pair<int> TPair;
 TPair m_ver;    // индексы вертексов ребра (first < second)
 TPair m_fac;    // индексы фейсов (Triangle) шарящих ребро (second = -1 если ребро открыто)
};
Ну и дальше все очевидно: проходитесь по исходным фейсам, строите контейнер ребер и вписываете их индексы в m_iEdges. Потребуется мапа о которой потом можно забыть. Потом добавляете новые вертексы (по числу ребер) и выдаете на гора новые фейсы и вертексы

Делаю несколько иначе, зато мапы нет. Функция IsVisible замедляет, но не радикально. Если дробить все фейсы без проверки тоже медленно. Все вектора зарезервированы заранее. Что в этом алгоритме не так?


Код
C++ (Qt)
                       class Vertex
{
public:
Vector3D coord;
Vector3D normal;
Vector3D texCoord;
};
 
class Edge
{
public:
int p1; //начало в правильном порядке обхода
int p2; //конец
int mid; //середина, или -1 если пока не делили
int e1;  //ребра полученные при делении
int e2;  //ребра полученные при делении
                                       Edge(int _p1 = -1, int _p2 = -1, int _mid = -1, int _e1m = -1, int _e2m = -1);
};
 
class Triangle
{
public:
Triangle(const Triangle &);
Triangle(Mesh * m = 0, int _p1 = -1, int _p2 = -1, int _p3 = -1, int _e12 = -1, int _e23 = -1, int _e31 = -1);
virtual ~Triangle();
int p1, p2, p3; //индексы вершин
int e12, e23, e31; //индексы рёбер
Vector3D  P1() const; //вершина 1
Vector3D  P2() const; //вершина 2
Vector3D P3() const; //вершина 3
                               bool IsVisible(float frustum[6][4], double r, Vector3D eye);
};
 
void Mesh::Subdivine(int polygons, const QVector<Triangle> & triangles, float frustum[6][4])
{
 
int iTr = triangles.size();
 
int vertex = vertices.size();//начальное число вертексов
 
 
for(int i=0;i<iTr;++i)
{
newTriangles[i] = triangles[i]; //записываем начальные фейсы
}
 
QVector<Triangle> * pNewTriangles = &newTriangles, * pNewTriangles1 = &newTriangles1;
QVector<Edge> * pEdges = &iEdges, * pNewEdges = &newEdges;
 
int iEdge; //номер добавляемого ребра
 
while(iTr < polygons) //мы хотим polygons фейсов
{
int iVisible = 0;
for(int in = 0; in < iTr; ++in)
{
Triangle t = (*pNewTriangles)[in];
if(t.IsVisible(frustum, eye, R)) //стоит ли дяльше разбивать этот треугольник
{
visibleTriangles[iVisible] = t;
++iVisible;
}
}
 
if(iVisible == 0) break;
 
iTr = 0;
iEdge=0;
 
                               //новых ребер будет не больше 9*iVisible
for(int iv = 0; iv < 9*iVisible; ++iv)
{
(*pNewEdges)[iv] = Edge();
}
 
for(int iv = 0; iv < iVisible; ++iv)
{
Triangle t = visibleTriangles[iv];
Vertex p12, p13, p23;
                                       //новые вершины
int i12, i13, i23,
e1m12,e2m12,e1m31,e3m31,e2m23,e3m23; //новые ребра
 
                                       //выбираем индексы поделенного ребра или делим ребро
if((*pEdges)[t.e12].mid >=0)
{
i12 = (*pEdges)[t.e12].mid; //точка середины
 
if((*pNewEdges)[(*pEdges)[t.e12].e1m].p1 == t.p1)
{
e1m12 = (*pEdges)[t.e12].e1m; //ребра нового фейса
e2m12 = (*pEdges)[t.e12].e2m;
}
else
{
e1m12 = (*pEdges)[t.e12].e2m;
e2m12 = (*pEdges)[t.e12].e1m;
}
}
else
{
p12.coord = (t.P1() + (t.P2() - t.P1())/2.).Normalized()*R; //проецируем середину на сферу
vertices[vertex] = p12; //новый вертекс
i12 = vertex;
++vertex;
 
                                               //новые 2 ребра
(*pEdges)[t.e12].mid = i12;
Edge e1(t.p1, i12), e2(t.p2, i12);
(*pNewEdges)[iEdge] = e1;
e1m12 = iEdge;
++iEdge;
(*pEdges)[t.e12].e1m = e1m12;
(*pNewEdges)[iEdge] = e2;
e2m12 = iEdge;
++iEdge;
(*pEdges)[t.e12].e2m = e2m12;
}
 
 
                                       //
                                       //тоже самое для оставшихся ребер
                                       //
 
                                       //добавляем 4 фейса
(*pNewTriangles1)[iTr] = Triangle(this, t.p1, i12, i13, e1m12, e1312, e1m31);
(*pNewTriangles1)[iTr+1] = Triangle(this, t.p2, i23, i12, e2m23, e1223, e2m12);
(*pNewTriangles1)[iTr+2] = Triangle(this, t.p3, i13, i23, e3m31, e2313, e3m23);
(*pNewTriangles1)[iTr+3] = Triangle(this, i12, i23, i13, e1223, e2313, e1312);
iTr += 4;
}
 
nEdges = iEdge;
 
qSwap(pNewTriangles, pNewTriangles1);
qSwap(pNewEdges, pEdges);
}
 
                       //переписываем результат
edges = *pEdges;
newTriangles = *pNewTriangles;
nTriangles = iTr;
nVertices = vertices.size();
}
« Последнее редактирование: Январь 30, 2018, 14:53 от qtkoder777 » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #19 : Январь 31, 2018, 12:31 »

Делаю несколько иначе, зато мапы нет. Функция IsVisible замедляет, но не радикально. Если дробить все фейсы без проверки тоже медленно. Все вектора зарезервированы заранее. Что в этом алгоритме не так?
Ну неплохо бы сформулировать задачу, хотя бы кратко, чтобы не приходилось о ней догадываться по фрагментам кода Улыбающийся  Насколько я понял, у Вас бессмертная "отсечка по фрустуму", т.е. хотите отбросить полигоны что камера не видит напрямую. Если так, то нужно выжать макс скорости - и зачем чего-то делить, создавать новые вертексы?  Просто если тр-к видим - оставляем его, иначе удаляем, всех делов на 5 минут. Не нужно даже создавать второй контейнер, просто перемещаем видимые в начало и выдаем их число для отрисовки. Если же по каким-то причинам (мне неизвестным) все-таки нужно рэзать, то:

Здесь точно порылась собака
Код
C++ (Qt)
bool IsVisible(float frustum[6][4], double r, Vector3D eye);
 
Если есть frustum, то eye избыточен, а что делает r (и что за "проецирование на сферу") - не понял. Фрустум делит каждое ребро создавая на нем 0, 1 или 2 новых вертекса. Поэтому Edge лучше нарисовать так
Код
C++ (Qt)
class Edge {
public:
   int p1; // начало в правильном порядке обхода
   int p2; // конец
   int p3; // новое начало если ребро было обрезано фрустумом (p3 == p1 если нет, -1 если ребро невидимо)
   int p4; // новый конец... (p4 == p2 если нет, -1 если невидимо)
 
   bool operator < ( const Edge & other ) const
   {
      if (qMin(p1, p2) < qMin(other.p1, other.p2)) return triue;
      if (qMin(p1, p2) > qMin(other.p1, other.p2)) return false;
 
      return qMax(p1, p2) < qMax(other.p1, other.p2);
   }
  ...
};
На этапе предрасчета все равно надо построить все ребра и вписать их индексы в фейсы как говорилось выше. Отсечка под конкретный фрустум сводится к проходу по всем ребрам: если ребро пересекает фрустум, то создаем новые вертексы (1 или 2) и вписываем их индексы в p3 и p4. После этого проходим по всем тр-кам и строим новые - у нас для этого все есть.
Записан
qtkoder777
Частый гость
***
Offline Offline

Сообщений: 245


Просмотр профиля
« Ответ #20 : Январь 31, 2018, 13:08 »

Делаю несколько иначе, зато мапы нет. Функция IsVisible замедляет, но не радикально. Если дробить все фейсы без проверки тоже медленно. Все вектора зарезервированы заранее. Что в этом алгоритме не так?
Ну неплохо бы сформулировать задачу, хотя бы кратко, чтобы не приходилось о ней догадываться по фрагментам кода Улыбающийся  Насколько я понял, у Вас бессмертная "отсечка по фрустуму", т.е. хотите отбросить полигоны что камера не видит напрямую. Если так, то нужно выжать макс скорости - и зачем чего-то делить, создавать новые вертексы?  

Задача нарисовать планету. Сначала строится меш для шара, разбивая икосаэдр, потом к вершинам применяется карта высот из текстуры. Получается планета с горами.
Отсекаю по фрустуму и по касательному конусу с помощью ограничивающих сфер.

Вот зачем оператор сравнения рёбер так и не понял.
« Последнее редактирование: Январь 31, 2018, 13:12 от qtkoder777 » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #21 : Январь 31, 2018, 14:44 »

Отсекаю по фрустуму и по касательному конусу с помощью ограничивающих сфер.
На всякий случай напомню что в обоих случаях если даже оба вертекса ребра невидимы - это еще ничего не значит, ребро может пересекать как фрустум, так и конус. Поэтому проверка на конус выглядит избыточной. Также есть мерзкий частный случай - ни одно ребро тр-ка не пересекает фрустум, но часть тр-ка все-таки видима (ну как бы тр-к насажен на угол фрустума)
Вот зачем оператор сравнения рёбер так и не понял.
В любом случае если резать - все начинается с построения ребер, напр так
Код
C++ (Qt)
QVector<Edge> BuildEdges( QVector<Triangle> & tri )
{
QVector<Edge> dst;
QMap<Edge, int> eMap;
 
for (int i = 0; i < tri.size(); ++i) {
 Triangle & t = tri[i];
 for (int j = 0; j < 3; ++j) {
   Edge edge = t.GetEdge(j);
   int & val = eMap[edge];
   if (!val) {    // ребро еще не добавлено в мапу
     dst.push_back(edge);
     val = edge.size();     // сохраняем в мапе индекс ребра + 1
   }
   t.SetEdgeIndex(j, val - 1);  // вписываем индекс ребра в фейс
 }
}
return dst;
}
Лучше хранить в фейсе p[3] и e[3]

[off]Да, а что ж такая тишина? Ведь совсем недавно подробности QVector обсуждались весьма охотно (хотя они здесь как корове седло). А вот содержательная часть почему-то никакого интереса не вызыват  Улыбающийся[/off]
Записан
qtkoder777
Частый гость
***
Offline Offline

Сообщений: 245


Просмотр профиля
« Ответ #22 : Январь 31, 2018, 15:13 »

На всякий случай напомню что в обоих случаях если даже оба вертекса ребра невидимы - это еще ничего не значит, ребро может пересекать как фрустум, так и конус. Поэтому проверка на конус выглядит избыточной. Также есть мерзкий частный случай - ни одно ребро тр-ка не пересекает фрустум, но часть тр-ка все-таки видима (ну как бы тр-к насажен на угол фрустума)
Проверяется попадает ли во фрустум и конус описанный вокруг треугольника шар. А в этот шар попадут и все треугольники, на которые мы его можем раздробить. Будут некоторые лишние фейсы - по краям фрустума, ну и пусть рисуются. Проверка на конус предотвращает расчет треугольников-антиподов.

Сейчас думаю что дело в Vector3D.
Каждый раз когда создаётся временный Vector3D, создаются же и все функции-члены, а их много.
То есть для производительности придётся переписывать в процедурном стиле?
« Последнее редактирование: Январь 31, 2018, 17:06 от qtkoder777 » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #23 : Февраль 01, 2018, 05:41 »

Ага, ну вроде так: Вы хотите делать триангуляцию только для тех фейсов что прошли тест Visible. Причем хотите делать 2 триангуляции за 1 проход (т.е. фактически тр-ки делятся на 4 * 4 = 16 новых). Все ли я верно понял?

Сейчас думаю что дело в Vector3D.
Каждый раз когда создаётся временный Vector3D, создаются же и все функции-члены, а их много.
То есть для производительности придётся переписывать в процедурном стиле?
Нет. Инициализируются только данные что этого требуют (здесь конкретно 3 флота x, y, z) - и все. А сколько ф-ций членов (хоть тыщи) никак на производительность не влияет.
Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3257


Просмотр профиля
« Ответ #24 : Февраль 01, 2018, 20:46 »

Господи, ну есть же десятки профилировщиков.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #25 : Февраль 02, 2018, 12:38 »

Забегая чуть вперед: можно хорошо выжать, даже подключить все ядра, но все-таки для real-time нужна реализация на GPU, Но там такой геморрой  Плачущий
Записан
Страниц: 1 [2]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.169 секунд. Запросов: 22.