Поиск:

Ответ в темуСоздание новой темы Создание опроса
> CDC и много потоков 
:(
    Опции темы
mrgloom
Дата 23.8.2011, 16:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 829
Регистрация: 8.6.2011

Репутация: нет
Всего: нет



есть такой код.
Код

Draw(CDC& dc)
{
     for(unsigned int k=0;k<temp_lines.size();++k)
            {
                CPoint* pntArray= new CPoint[2];
                pntArray[0].x= temp_lines[k].p1.x;    
                pntArray[0].y= temp_lines[k].p1.y;
                pntArray[1].x= temp_lines[k].p2.x;
                pntArray[1].y= temp_lines[k].p2.y;
                Polyline(dc, pntArray, 2);
                delete [] pntArray;
            }
}

возможно ли рисовать несколькими потоками в одном CDC?
PM MAIL   Вверх
Earnest
Дата 23.8.2011, 16:31 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5962
Регистрация: 17.6.2005
Где: Рязань

Репутация: 87
Всего: 183



Не стоит. MFC-объекты вообще к конкретному потоку относятся.
Кроме того, Draw обычно происходит в результате обработки сообщения OnPaint (и делать иначе редко когда рекомендуется). А WM_PAINT всегда обрабатывается в том потоке которому принадлежит окно.
Короче, никакого смысла в многопоточном рисовании нет.


--------------------
...
PM   Вверх
mrgloom
Дата 23.8.2011, 16:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 829
Регистрация: 8.6.2011

Репутация: нет
Всего: нет



попробовал так.

Код

void boost_draw(CDC& dc, vector<Line> vec)
{
    for(unsigned int k=0;k<vec.size();++k)
    {
        CPoint* pntArray= new CPoint[2];
        pntArray[0].x= vec[k].p1.x;    
        pntArray[0].y= vec[k].p1.y;
        pntArray[1].x= vec[k].p2.x;
        pntArray[1].y= vec[k].p2.y;
        Polyline(dc, pntArray, 2);
        delete [] pntArray;
    }
}


Код

std::size_t thcount = boost::thread::hardware_concurrency();
            vector<vector<Line>> vec(thcount);
            int t= temp_lines.size()/thcount;
            for(int i=0;i<thcount;++i)
            {
                vec[i].insert(vec[i].end(),temp_lines.begin(),temp_lines.begin()+(i+1)*t);
                if (i==thcount)
                    vec[i].insert(vec[i].end(),temp_lines.begin()+(i)*t,temp_lines.end());
            }
            boost::thread_group tg;
            for ( std::size_t idx = 0; idx < thcount; ++idx ) 
            {
               tg.create_thread(boost::bind(&boost_draw/*, dc*/,vec[idx]));
            }
            tg.join_all();


error C2248: 'CObject::CObject' : cannot access private member declared in class 'CObject'
видимо все дело в CDC


ну а если я хочу рисовать быстрее, то как этого добиться?

Это сообщение отредактировал(а) mrgloom - 23.8.2011, 16:53
PM MAIL   Вверх
Earnest
Дата 24.8.2011, 07:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5962
Регистрация: 17.6.2005
Где: Рязань

Репутация: 87
Всего: 183



CObject не имеет конструктора копирования. Точнее, имеет, но закрытый - т.е. это концептуально не копируемый объект. Равно как и CDC и почти все MFC-объекты. Однако никто не мешает передавать dc по ссылке, а сейчас у тебя действительно копирование.
И вообще, судя по коду, ты слабо себе представляешь механизм передачи параметров.
И с чего ты вообще взял, что в потоках рисование будет быстрее? Если проблема тормозов на рисовании возникает, она наверняка в организации твоего кода - всяких глупостях типа передачи массива в функцию по значению, а также в функции Draw: выделение буфера под 2 точки динамически, да еще в цикле для каждой линии - это просто шедевр. Ты бы азы подучил, прежде чем в буст и потоки лезть... Ты уж не обижайся, но это какой-то кошмар, твой код...


--------------------
...
PM   Вверх
mrgloom
Дата 24.8.2011, 09:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 829
Регистрация: 8.6.2011

Репутация: нет
Всего: нет



так лучше?
Код

CPoint* pntArray= new CPoint[2*temp_lines.size()];
            for(unsigned int k=0;k<temp_lines.size();++k)
            {
                pntArray[k].x= temp_lines[k].p1.x;    
                pntArray[k].y= temp_lines[k].p1.y;
                pntArray[k+1].x= temp_lines[k].p2.x;
                pntArray[k+1].y= temp_lines[k].p2.y;
            }
            Polyline(dc, pntArray, 2*temp_lines.size());
            delete [] pntArray;

вроде бы еще говорили, что лучше рисовать через PolyPolyLine ну тогда надо знать, что из точек собрана связанная последовательная фигура.

Цитата(Earnest @  24.8.2011,  07:08 Найти цитируемый пост)
И с чего ты вообще взял, что в потоках рисование будет быстрее?


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

PM MAIL   Вверх
mrgloom
Дата 24.8.2011, 10:51 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 829
Регистрация: 8.6.2011

Репутация: нет
Всего: нет



хотя нет так как я написал выше не получится.
т.к. получаются лишние связи.
а мне надо вывести все независимыми линиями.

boost_draw(CDC* dc, vector<Line>& vec)
tg.create_thread(boost::bind(&boost_draw, &dc,vec[idx]));
так действительно работает, но быстрее (по ощущениям) вроде и не стало.
 

PM MAIL   Вверх
Earnest
Дата 24.8.2011, 11:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5962
Регистрация: 17.6.2005
Где: Рязань

Репутация: 87
Всего: 183



Цитата(mrgloom @  24.8.2011,  10:53 Найти цитируемый пост)
так лучше?

Вообще не нужно тут динамическое выделение памяти, под 2-то точки. Можно стековой переменной обойтись. 
Далее, я не знаю, что за объекты хранятся в temp_lines, но если это просто точки (пара точек), то можно вообще обойтись без копирования, а использовать просто указатель на первую точку (если, конечно, в памяти они образуют массив).
Если точек 2, то лучше использовать пару функций MoveTo\LineTo вместо Polyline.
PolyPolyline тоже можно использовать, но нужно заранее подготовить данные. Ты же уже знаешь, что точки попарно связаны, этого достаточно. PolyPolyline прекрасно рисует многосвязные фигуры. Кстати, это значительно ускорит рисование (подготовка массива точек и длин + один вызов PolyPolyline) , если линий действительно много. Дело в том, что вызовы функций GDI довольно дорого обходятся.
Цитата(mrgloom @  24.8.2011,  10:53 Найти цитируемый пост)
наблюдая за тем, что загружено только 1 ядро. наверно надо сделать, отдельный поток на рисование, и отдельный на все остальное. 

Но это вовсе не означает, что рисовать нужно в нескольких потоках;рисуй себе в главном (там, где окна живут), а считай в отдельном рабочем.



--------------------
...
PM   Вверх
mrgloom
Дата 24.8.2011, 14:51 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 829
Регистрация: 8.6.2011

Репутация: нет
Всего: нет



хмм оказалось, что даже автокад как то странно работает с потоками.(т.е. не использует все ядра)
а если все выводить в картинку\текстуру, то это будет как бы "рендеринг", и это уже можно делать в несколько потоков?(или глупая идея)

у меня была структура структур(прямоугольников и более сложных примитивов) которую я распарсил в линии и построил quadtree
счет идет на милионы линий.
вот теперь думаю, что не очень, то это была хорошая идея, лучше было оставить структурами, но в таком случае не очень понятно как создавать quadtree.
PM MAIL   Вверх
Earnest
Дата 24.8.2011, 20:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5962
Регистрация: 17.6.2005
Где: Рязань

Репутация: 87
Всего: 183



Цитата(mrgloom @  24.8.2011,  15:51 Найти цитируемый пост)
а если все выводить в картинку\текстуру, то это будет как бы "рендеринг", и это уже можно делать в несколько потоков?(или глупая идея)

Ну сам подумай, доступ к растру придется как-то делить - нельзя же позволять нескольким потокам писать в одну область памяти одновременно. А эта синхронизация может сожрать весь выигрыш... А делить пространство, типа первый поток рисует только в первую треть, второй - во вторую и т.д. - тоже неудобно - линии-то где угодно заканчиваются.
Миллионы линий - это серьезно, здесь действительно нужно оптимизировать вывод.
Навскидку, могу предложить след. варианты.
1) Если линии относительно постоянны, то можно один раз нарисовать их во внеэкранный буфер, а затем быстро бросать на экран по мере надобности (когда WM_PAINT приходят). Внеэкранный буфер обновлять по мере необходимости. Причем, чтобы не тормозило, делать это можно и в отдельном потоке, только организовать так, чтобы блокировка не сразу на весь вывод действовала, а как-то разбить (чтобы можно было частично нарисованные линии вывести на экран).
2) Можно попробовать выводить линии в несколько потоков, но в несколько отдельных растров (разные линии, разумеется), а потом эти растры быстренько объединить подходящей битовой операцией (ИЛИ, например, если фон черный). Но памяти, конечно, отожрется много...

Автокад использует какую-то интересную схему кэширования чертежа: обновления, которые время от времени происходят, длятся довольно долго, но зато потом, между ними отрисовка такая быстрая, что больше напоминает выброс растра на экран... но тем не менее это дело масштабируется (так же быстро) в довольно широких пределах... вряд ли растр... Сама бы хотела знать, как они это делают...

Цитата(mrgloom @  24.8.2011,  15:51 Найти цитируемый пост)
вот теперь думаю, что не очень, то это была хорошая идея, лучше было оставить структурами, но в таком случае не очень понятно как создавать quadtree. 

Не надо путать структуры индексирования со структурами хранения... Никто ведь не мешает хранить структуры, а дерево строить для линий, используя какую-то ссылочную схему - т.е. линия представляется не как 2 точки, а как ссылка на i-е ребро структуры N.


--------------------
...
PM   Вверх
mrgloom
Дата 25.8.2011, 10:19 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 829
Регистрация: 8.6.2011

Репутация: нет
Всего: нет



Цитата

А делить пространство, типа первый поток рисует только в первую треть, второй - во вторую и т.д. - тоже неудобно - линии-то где угодно заканчиваются.

определить какие линии куда попадают я смогу с помощью quadtree, только вместо выполнения поиска для 1 области, у меня получится несколько таких операций.
ну если линии такие большие, что попадают в несколько кусков, то будут рисоваться по несколько раз.(тут только добавляется одна операция отсечения ненужной части.)
самый плохой вариант, это когда все линии попадают во все куски, т.е. рисуется такой "забор".
только не понятно как это реализовать\рисовать ибо я рисую через Polyline и у нее есть параметр CDC и всё, т.е. непонятно как образно выражаясь взять n кусков памяти под потоки транслировать туда линии, а потом склеить всё в 1 кусок памяти и вывести этот кусок на экран.

Цитата

Если линии относительно постоянны, то можно один раз нарисовать их во внеэкранный буфер, а затем быстро бросать на экран по мере надобности (когда WM_PAINT приходят). Внеэкранный буфер обновлять по мере необходимости. 

что значит линии постоянны? 
их число постоянное, но вот для того чтобы их например сместить на экране, я к каждой точке прибавляю смещение(возможно это неправильный подход) и делаю это прямо в draw() и чтобы изменить масштаб соответственно тоже самое.
т.е. у меня есть init_lines где хранятся неизменяемые линии, которые я считал, а temp_lines это те которые я вывожу при каждом draw()
Код

vector<Line> temp_lines(init_lines.size());
for (unsigned int i=0;i<init_lines.size();++i)
            {
                temp_lines[i].p1.x= (1+scale_x)*init_lines[i].p1.x+shift_x;
                temp_lines[i].p1.y= (1+scale_y)*init_lines[i].p1.y+shift_y;
                temp_lines[i].p2.x= (1+scale_x)*init_lines[i].p2.x+shift_x;                
                                temp_lines[i].p2.y= (1+scale_y)*init_lines[i].p2.y+hift_y;
            }    



что такое внеэкранный буфер в терминах GDI?  (еще нашел такое http://www.rsdn.ru/forum/winapi/2246791.flat.aspx)
что такое растр в терминах GDI?


Цитата

Не надо путать структуры индексирования со структурами хранения... Никто ведь не мешает хранить структуры, а дерево строить для линий, используя какую-то ссылочную схему - т.е. линия представляется не как 2 точки, а как ссылка на i-е ребро структуры N. 

ну да идея ясна, работаем как с линиями, а когда выводить выводим как структуры по несколько точек,если структура попадает не полностью выводим как линии. я изначально сделал все для линий, чтобы было проще.
по идее даже можно у любой структуры описать её BoundingRect  и работать с ней целиком.

и еще уточнение я вывожу сначала картинки через dc.StretchBlt , а потом накладываю линии через PolyLine, если это важно.
и еще я уменьшаю кол-во выводимых линий во-первых отсекая по текущему view по quadtree и если малое увеличение не вывожу маленькие детали, т.е. у меня не просто quadtree, а quadtree+ level of detail.



PM MAIL   Вверх
Earnest
Дата 25.8.2011, 16:10 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5962
Регистрация: 17.6.2005
Где: Рязань

Репутация: 87
Всего: 183



Цитата(mrgloom @  25.8.2011,  11:19 Найти цитируемый пост)
что значит линии постоянны? 
их число постоянное, но вот для того чтобы их например сместить на экране, я к каждой точке прибавляю смещение(возможно это неправильный подход) и делаю это прямо в draw() и чтобы изменить масштаб соответственно тоже самое.

Т.е сами линии у тебя не изменяются.
Можно поступить, например, так. Завести внеэкранный буфер размером, скажем в 9 текущих экранов (или 4, или... в общем, в зависимости от масштаба - хоть на весь экстент, если размер растра позволит). И нарисовать туда соответствующую часть линий. 
Выбрасывать на экран соответствующую часть (в зависимости от текущего положения)
При скроллинге использовать сдвиг экрана (ScrollDC? это очень быстро) + дорисовывать недостающею часть из буфера.
При масштабировании вниз можно тоже не пересчитывать сразу, а использовать StretchBlt/
Пересчитывать буфер начинаем, когда масштаб \ смещение выйдут за пределы

Цитата(mrgloom @  25.8.2011,  11:19 Найти цитируемый пост)
что такое внеэкранный буфер в терминах GDI?

Это пара compatible DC + bitmap (обычно compatible, но не обязательно)

Цитата(mrgloom @  25.8.2011,  11:19 Найти цитируемый пост)
что такое растр в терминах GDI?

Растр == битмап, который выбирается в мемори-контекст

Отсекать отдельными линиями (ребрами) - очень неэффективно, лучше уж сразу фигуры.
Приведу пример из своего приложения. Хранится много графики (в основном полилинии и полигоны, хотя есть и текст и точки и окружности...) В каждой полилинии - до нескольких тысяч точек, полилиний-полигонов иногда сотни тысяч. Т.е., если считать в ребрах, то получаем миллионы линий. Хранится все в даблах, в некоторых логических координатах. Для каждого объекта хранится экстент. Все это добро разделено на слои, как в Автокаде. Есть структура, которая определяет текущее положение и масштаб экрана и, соответственно, преобразование координат.
При отрисовке происходит даже не отсечение, а просто проверка на попадание объекта в экран (по экстенту). Затем полилиния пересчитывается в координаты экрана (в целые). Удаляются точки-дубликаты (они возникают при масштабировании-округлении). Рисуем через Polyline или Polygon. Т.е. линейно просматриваются (на предмет экстента) ВСЕ объекты. И притормаживать начинает только при выводе близком к вью-ол, и только если объектов сотни тысяч, и линии длинные (не отсекаются). Внеэкранный буфер имеет размер экрана, без запаса, так проще за ним следить. Т.е. буфер обновляется при любом изменении позиции-масштаба, включении-выключении слоев, изменении объектов и т.д. При рабочем масштабе (т.е. когда на экране можно что-то редактировать) все летает (вместе с подложенным под векторные данные растром). Поэтому никакми дополнительными индексами и потоками не озабачиваюсь.
В общем, что хочу сказать... думаю, ты не там ищешь что оптимизировать. Действительно ли тебе нужно квадро-дерево? (Если ты ввел его только для оптимизации вывод, то имхо зря...) Тормоза могут возникать из-за неоптимальных вызово GDI-функций - я уже говорила, они страшно дорогие. Т.е. все эти смены перьев и прочая нужно оптимизировать очень жестко. Толстые линии и линии не солид тоже могут тормозить...


--------------------
...
PM   Вверх
mrgloom
Дата 8.9.2011, 13:56 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 829
Регистрация: 8.6.2011

Репутация: нет
Всего: нет



про вывод графики понял, его ускорить можно только если использовать что то типа двойного буфера.
у меня получается что перед вывоводом надо из общего кол-ва примитивов выделить те, которые попадают в область+те которые не видны на масштабе, это занимает процессорное время. (если выводить все, то тормозит)
Цитата

При отрисовке происходит даже не отсечение, а просто проверка на попадание объекта в экран (по экстенту).

не понял как это? как хранятся примитивы?

Это сообщение отредактировал(а) mrgloom - 8.9.2011, 13:56
PM MAIL   Вверх
mrgloom
Дата 8.9.2011, 14:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 829
Регистрация: 8.6.2011

Репутация: нет
Всего: нет



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

Добавлено через 1 минуту и 27 секунд
или из этих boundrect примитивов построить R-tree или просто взять сетку с константным шагом. 
PM MAIL   Вверх
Earnest
Дата 9.9.2011, 08:36 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5962
Регистрация: 17.6.2005
Где: Рязань

Репутация: 87
Всего: 183



Цитата(mrgloom @  8.9.2011,  15:13 Найти цитируемый пост)
а просто у каждого примитива построить его boundrect (ограничивающий прямоугольник)

Именно их я и называю экстентами. И не надо никакого R-дерева, попробуй сначала быть проще... И несколько потоков тоже не надо...
И что ты называешь примитивами? Отдельные отрезки или полные фигуры? Лучше второе - их меньше smile
На худой конец, используй самый простой пространственный индекс - кластеры постоянного размера (или сетку) - просто поддерживать, просто использовать, а выигрыш дает приличный. Я использую такой индекс в расчетных утилитах, когда нужно искать что-то рядом с чем-то; а для вывода на экран - только проверку экстентов (которые, разумеется, хранятся для каждого объекта).


--------------------
...
PM   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Visual C++/MFC/WTL | Следующая тема »


 




[ Время генерации скрипта: 0.0890 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.