forked from organicmaps/organicmaps
refactored TileRenderer and CoverageGenerator to use core::CommandsQueue
This commit is contained in:
parent
bef00e3fc5
commit
dbce8bcba4
26 changed files with 650 additions and 516 deletions
|
@ -32,7 +32,7 @@ namespace my
|
|||
typename list_t::iterator m_it;
|
||||
};
|
||||
|
||||
typedef unordered_map<KeyT, MapEntry> map_t;
|
||||
typedef map<KeyT, MapEntry> map_t;
|
||||
|
||||
map_t m_map;
|
||||
list_t m_list;
|
||||
|
|
|
@ -14,6 +14,21 @@ private:
|
|||
|
||||
public:
|
||||
|
||||
template <typename Fn>
|
||||
void ProcessList(Fn const & fn)
|
||||
{
|
||||
threads::ConditionGuard g(m_Cond);
|
||||
|
||||
bool hadElements = !m_list.empty();
|
||||
|
||||
fn(m_list);
|
||||
|
||||
bool hasElements = !m_list.empty();
|
||||
|
||||
if (!hadElements && hasElements)
|
||||
m_Cond.Signal();
|
||||
}
|
||||
|
||||
void PushBack(T const & t)
|
||||
{
|
||||
threads::ConditionGuard g(m_Cond);
|
||||
|
|
|
@ -23,7 +23,7 @@ void BenchmarkTilingRenderPolicyMT::DrawFrame(shared_ptr<PaintEvent> const & e,
|
|||
{
|
||||
TilingRenderPolicyMT::DrawFrame(e, s);
|
||||
/// waiting for render queue to empty
|
||||
GetRenderQueue().WaitForEmptyAndFinished();
|
||||
GetTileRenderer().WaitForEmptyAndFinished();
|
||||
/// current screen should be fully covered, so the following call will only blit results
|
||||
TilingRenderPolicyMT::DrawFrame(e, s);
|
||||
}
|
||||
|
|
|
@ -1,149 +1,31 @@
|
|||
#include "../base/SRC_FIRST.hpp"
|
||||
|
||||
#include "coverage_generator.hpp"
|
||||
#include "render_queue.hpp"
|
||||
#include "screen_coverage.hpp"
|
||||
#include "tile_renderer.hpp"
|
||||
|
||||
#include "../base/logging.hpp"
|
||||
|
||||
#include "../std/bind.hpp"
|
||||
|
||||
ScreenCoverage::ScreenCoverage()
|
||||
{}
|
||||
|
||||
struct unlock_synchronized
|
||||
{
|
||||
TileCache * m_c;
|
||||
|
||||
unlock_synchronized(TileCache * c) : m_c(c)
|
||||
{}
|
||||
|
||||
void operator()(Tile const * t)
|
||||
{
|
||||
m_c->readLock();
|
||||
m_c->unlockTile(t->m_rectInfo);
|
||||
m_c->readUnlock();
|
||||
}
|
||||
};
|
||||
|
||||
void ScreenCoverage::Clear()
|
||||
{
|
||||
m_tileCache->writeLock();
|
||||
|
||||
for (unsigned i = 0; i < m_tiles.size(); ++i)
|
||||
m_tileCache->unlockTile(m_tiles[i]->m_rectInfo);
|
||||
|
||||
m_tileCache->writeUnlock();
|
||||
|
||||
m_tiles.clear();
|
||||
m_infoLayer.clear();
|
||||
}
|
||||
|
||||
CoverageGenerator::CoverageTask::CoverageTask(ScreenBase const & screen)
|
||||
: m_screen(screen)
|
||||
{}
|
||||
|
||||
void CoverageGenerator::CoverageTask::execute(CoverageGenerator * generator)
|
||||
{
|
||||
unsigned tilesCount = 0;
|
||||
generator->m_tiler.seed(m_screen, m_screen.GlobalRect().Center());
|
||||
generator->m_sequenceID++;
|
||||
|
||||
{
|
||||
threads::MutexGuard g(generator->m_mutex);
|
||||
LOG(LINFO, ("clearing workCoverage"));
|
||||
generator->m_workCoverage->Clear();
|
||||
}
|
||||
|
||||
generator->m_workCoverage->m_screen = m_screen;
|
||||
|
||||
while (generator->m_tiler.hasTile())
|
||||
{
|
||||
++tilesCount;
|
||||
Tiler::RectInfo rectInfo = generator->m_tiler.nextTile();
|
||||
|
||||
TileCache & tileCache = generator->m_renderQueue->GetTileCache();
|
||||
|
||||
tileCache.readLock();
|
||||
|
||||
if (tileCache.hasTile(rectInfo))
|
||||
{
|
||||
tileCache.lockTile(rectInfo);
|
||||
|
||||
Tile const * tile = &tileCache.getTile(rectInfo);
|
||||
|
||||
shared_ptr<Tile const> lockedTile = make_shared_ptr(tile, unlock_synchronized(&tileCache));
|
||||
|
||||
generator->m_workCoverage->m_tiles.push_back(lockedTile);
|
||||
generator->m_workCoverage->m_infoLayer.merge(*tile->m_infoLayer.get(),
|
||||
tile->m_tileScreen.PtoGMatrix() * generator->m_workCoverage->m_screen.GtoPMatrix());
|
||||
|
||||
tileCache.readUnlock();
|
||||
}
|
||||
else
|
||||
{
|
||||
tileCache.readUnlock();
|
||||
generator->m_renderQueue->AddCommand(
|
||||
generator->m_renderFn,
|
||||
rectInfo,
|
||||
generator->m_sequenceID,
|
||||
bind(&CoverageGenerator::AddMergeTileTask, generator, _1, _2));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
threads::MutexGuard g(generator->Mutex());
|
||||
swap(generator->m_currentCoverage, generator->m_workCoverage);
|
||||
}
|
||||
}
|
||||
|
||||
CoverageGenerator::MergeTileTask::MergeTileTask(shared_ptr<Tile const> const & tile)
|
||||
: m_tile(tile)
|
||||
{}
|
||||
|
||||
void CoverageGenerator::MergeTileTask::execute(CoverageGenerator * generator)
|
||||
{
|
||||
{
|
||||
threads::MutexGuard g(generator->m_mutex);
|
||||
LOG(LINFO, ("making a working copy of currentCoverage"));
|
||||
*generator->m_mergeCoverage = *generator->m_currentCoverage;
|
||||
}
|
||||
|
||||
LOG(LINFO, (generator->m_mergeCoverage->m_tiles.size()));
|
||||
|
||||
generator->m_mergeCoverage->m_tiles.push_back(m_tile);
|
||||
generator->m_mergeCoverage->m_infoLayer.merge(*m_tile->m_infoLayer.get(),
|
||||
m_tile->m_tileScreen.PtoGMatrix() * generator->m_mergeCoverage->m_screen.GtoPMatrix());
|
||||
|
||||
{
|
||||
threads::MutexGuard g(generator->m_mutex);
|
||||
LOG(LINFO, ("saving merged coverage into workCoverage"));
|
||||
*generator->m_workCoverage = *generator->m_mergeCoverage;
|
||||
LOG(LINFO, ("saving merged coverage into currentCoverage"));
|
||||
*generator->m_currentCoverage = *generator->m_mergeCoverage;
|
||||
}
|
||||
|
||||
generator->m_renderQueue->Invalidate();
|
||||
}
|
||||
|
||||
CoverageGenerator::CoverageGenerator(
|
||||
size_t tileSize,
|
||||
size_t scaleEtalonSize,
|
||||
RenderPolicy::TRenderFn renderFn,
|
||||
RenderQueue * renderQueue)
|
||||
: m_tiler(tileSize, scaleEtalonSize),
|
||||
TileRenderer * tileRenderer,
|
||||
shared_ptr<WindowHandle> const & windowHandle)
|
||||
: m_queue(1),
|
||||
m_tileRenderer(tileRenderer),
|
||||
m_workCoverage(new ScreenCoverage(tileRenderer, this, tileSize, scaleEtalonSize)),
|
||||
m_mergeCoverage(new ScreenCoverage(tileRenderer, this, tileSize, scaleEtalonSize)),
|
||||
m_currentCoverage(new ScreenCoverage(tileRenderer, this, tileSize, scaleEtalonSize)),
|
||||
m_sequenceID(0),
|
||||
m_renderFn(renderFn),
|
||||
m_renderQueue(renderQueue),
|
||||
m_workCoverage(new ScreenCoverage()),
|
||||
m_mergeCoverage(new ScreenCoverage()),
|
||||
m_currentCoverage(new ScreenCoverage())
|
||||
m_windowHandle(windowHandle)
|
||||
{
|
||||
m_routine = new Routine(this);
|
||||
}
|
||||
|
||||
void CoverageGenerator::Initialize()
|
||||
{
|
||||
m_thread.Create(m_routine);
|
||||
m_queue.Start();
|
||||
}
|
||||
|
||||
CoverageGenerator::~CoverageGenerator()
|
||||
|
@ -155,61 +37,64 @@ CoverageGenerator::~CoverageGenerator()
|
|||
|
||||
void CoverageGenerator::Cancel()
|
||||
{
|
||||
m_thread.Cancel();
|
||||
m_queue.Cancel();
|
||||
}
|
||||
|
||||
void CoverageGenerator::AddCoverageTask(ScreenBase const & screen)
|
||||
void CoverageGenerator::AddCoverScreenTask(ScreenBase const & screen)
|
||||
{
|
||||
if (screen == m_currentScreen)
|
||||
return;
|
||||
|
||||
m_currentScreen = screen;
|
||||
m_tasks.PushBack(make_shared_ptr(new CoverageTask(screen)));
|
||||
m_sequenceID++;
|
||||
|
||||
m_queue.AddCommand(bind(&CoverageGenerator::CoverScreen, this, screen, m_sequenceID));
|
||||
}
|
||||
|
||||
void CoverageGenerator::AddMergeTileTask(Tiler::RectInfo const & rectInfo, Tile const &)
|
||||
void CoverageGenerator::CoverScreen(ScreenBase const & screen, int sequenceID)
|
||||
{
|
||||
TileCache & tileCache = m_renderQueue->GetTileCache();
|
||||
tileCache.readLock();
|
||||
if (sequenceID < m_sequenceID)
|
||||
return;
|
||||
|
||||
if (tileCache.hasTile(rectInfo))
|
||||
{
|
||||
tileCache.lockTile(rectInfo);
|
||||
|
||||
Tile const * tile = &tileCache.getTile(rectInfo);
|
||||
|
||||
shared_ptr<Tile const> lockedTile = make_shared_ptr(tile, unlock_synchronized(&tileCache));
|
||||
|
||||
tileCache.readUnlock();
|
||||
|
||||
m_tasks.PushBack(make_shared_ptr(new MergeTileTask(lockedTile)));
|
||||
threads::MutexGuard g(m_mutex);
|
||||
*m_workCoverage = *m_currentCoverage;
|
||||
}
|
||||
else
|
||||
tileCache.readUnlock();
|
||||
}
|
||||
|
||||
CoverageGenerator::Routine::Routine(CoverageGenerator * parent)
|
||||
: m_parent(parent)
|
||||
{}
|
||||
m_workCoverage->SetScreen(screen);
|
||||
|
||||
void CoverageGenerator::Routine::Do()
|
||||
{
|
||||
while (!IsCancelled())
|
||||
{
|
||||
shared_ptr<Task> task = m_parent->m_tasks.Front(true);
|
||||
|
||||
if (m_parent->m_tasks.IsCancelled())
|
||||
break;
|
||||
|
||||
task->execute(m_parent);
|
||||
threads::MutexGuard g(m_mutex);
|
||||
*m_currentCoverage = *m_workCoverage;
|
||||
m_workCoverage->Clear();
|
||||
}
|
||||
}
|
||||
|
||||
threads::Mutex & CoverageGenerator::Mutex()
|
||||
void CoverageGenerator::AddMergeTileTask(Tiler::RectInfo const & rectInfo)
|
||||
{
|
||||
return m_mutex;
|
||||
m_queue.AddCommand(bind(&CoverageGenerator::MergeTile, this, rectInfo));
|
||||
}
|
||||
|
||||
ScreenCoverage * CoverageGenerator::CurrentCoverage()
|
||||
void CoverageGenerator::MergeTile(Tiler::RectInfo const & rectInfo)
|
||||
{
|
||||
return m_currentCoverage;
|
||||
{
|
||||
threads::MutexGuard g(m_mutex);
|
||||
*m_workCoverage = *m_currentCoverage;
|
||||
}
|
||||
|
||||
m_workCoverage->Merge(rectInfo);
|
||||
|
||||
{
|
||||
threads::MutexGuard g(m_mutex);
|
||||
*m_currentCoverage = *m_workCoverage;
|
||||
m_workCoverage->Clear();
|
||||
}
|
||||
|
||||
m_windowHandle->invalidate();
|
||||
}
|
||||
|
||||
void CoverageGenerator::CopyCurrentCoverage(ScreenCoverage & dst)
|
||||
{
|
||||
threads::MutexGuard g(m_mutex);
|
||||
dst = *m_currentCoverage;
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "render_policy.hpp"
|
||||
#include "tiler.hpp"
|
||||
#include "tile.hpp"
|
||||
#include "window_handle.hpp"
|
||||
|
||||
#include "../geometry/screenbase.hpp"
|
||||
|
||||
|
@ -11,100 +12,52 @@
|
|||
#include "../base/thread.hpp"
|
||||
#include "../base/threaded_list.hpp"
|
||||
#include "../base/mutex.hpp"
|
||||
#include "../base/commands_queue.hpp"
|
||||
|
||||
#include "../std/vector.hpp"
|
||||
#include "../std/shared_ptr.hpp"
|
||||
|
||||
class RenderQueue;
|
||||
class TileRenderer;
|
||||
class TileCache;
|
||||
|
||||
/// holds the tile coverage for a specific screen
|
||||
struct ScreenCoverage
|
||||
{
|
||||
TileCache * m_tileCache;
|
||||
ScreenBase m_screen;
|
||||
vector<shared_ptr<Tile const> > m_tiles;
|
||||
yg::InfoLayer m_infoLayer;
|
||||
|
||||
bool m_IsRendering; //< while this flag is set - the object shouldn't
|
||||
//be altered, 'cause GUI thread is rendering from it
|
||||
|
||||
ScreenCoverage();
|
||||
|
||||
void Merge(Tiler::RectInfo const & rectInfo, Tile const & tile);
|
||||
void Compute(ScreenBase const & screen);
|
||||
void Clear();
|
||||
};
|
||||
class ScreenCoverage;
|
||||
|
||||
class CoverageGenerator
|
||||
{
|
||||
private:
|
||||
|
||||
struct Task
|
||||
{
|
||||
virtual void execute(CoverageGenerator * generator) = 0;
|
||||
};
|
||||
core::CommandsQueue m_queue;
|
||||
|
||||
struct CoverageTask : Task
|
||||
{
|
||||
ScreenBase m_screen;
|
||||
CoverageTask(ScreenBase const & screen);
|
||||
void execute(CoverageGenerator * generator);
|
||||
};
|
||||
|
||||
struct MergeTileTask : Task
|
||||
{
|
||||
shared_ptr<Tile const> m_tile;
|
||||
MergeTileTask(shared_ptr<Tile const> const & tile);
|
||||
void execute(CoverageGenerator * generator);
|
||||
};
|
||||
|
||||
ThreadedList<shared_ptr<Task> > m_tasks;
|
||||
|
||||
struct Routine : public threads::IRoutine
|
||||
{
|
||||
CoverageGenerator * m_parent;
|
||||
void Do();
|
||||
Routine(CoverageGenerator * parent);
|
||||
};
|
||||
|
||||
friend struct Routine;
|
||||
|
||||
Routine * m_routine;
|
||||
threads::Thread m_thread;
|
||||
|
||||
threads::Mutex m_mutex;
|
||||
|
||||
Tiler m_tiler;
|
||||
|
||||
RenderPolicy::TRenderFn m_renderFn;
|
||||
RenderQueue * m_renderQueue;
|
||||
TileRenderer * m_tileRenderer;
|
||||
|
||||
ScreenCoverage * m_workCoverage;
|
||||
ScreenCoverage * m_mergeCoverage;
|
||||
ScreenCoverage * m_currentCoverage;
|
||||
|
||||
ScreenBase m_currentScreen;
|
||||
size_t m_sequenceID;
|
||||
int m_sequenceID;
|
||||
|
||||
shared_ptr<WindowHandle> m_windowHandle;
|
||||
|
||||
threads::Mutex m_mutex;
|
||||
|
||||
public:
|
||||
|
||||
CoverageGenerator(size_t tileSize,
|
||||
size_t scaleEtalonSize,
|
||||
RenderPolicy::TRenderFn renderFn,
|
||||
RenderQueue * renderQueue);
|
||||
TileRenderer * tileRenderer,
|
||||
shared_ptr<WindowHandle> const & windowHandle);
|
||||
|
||||
~CoverageGenerator();
|
||||
|
||||
void AddCoverageTask(ScreenBase const & screen);
|
||||
void AddCoverScreenTask(ScreenBase const & screen);
|
||||
void AddMergeTileTask(Tiler::RectInfo const & rectInfo);
|
||||
|
||||
void AddMergeTileTask(Tiler::RectInfo const & rectInfo, Tile const &);
|
||||
void CoverScreen(ScreenBase const & screen, int sequenceID);
|
||||
void MergeTile(Tiler::RectInfo const & rectInfo);
|
||||
|
||||
void Cancel();
|
||||
|
||||
void Initialize();
|
||||
|
||||
threads::Mutex & Mutex();
|
||||
|
||||
ScreenCoverage * CurrentCoverage();
|
||||
void CopyCurrentCoverage(ScreenCoverage & dst);
|
||||
};
|
||||
|
|
|
@ -208,14 +208,13 @@ namespace fwork
|
|||
|
||||
DrawProcessor::DrawProcessor( m2::RectD const & r,
|
||||
ScreenBase const & convertor,
|
||||
shared_ptr<PaintEvent> const & paintEvent,
|
||||
int scaleLevel,
|
||||
yg::GlyphCache * glyphCache)
|
||||
shared_ptr<PaintEvent> const & e,
|
||||
int scaleLevel)
|
||||
: m_rect(r),
|
||||
m_convertor(convertor),
|
||||
m_paintEvent(paintEvent),
|
||||
m_paintEvent(e),
|
||||
m_zoom(scaleLevel),
|
||||
m_glyphCache(glyphCache)
|
||||
m_glyphCache(e->drawer()->screen()->glyphCache())
|
||||
#ifdef PROFILER_DRAWING
|
||||
, m_drawCount(0)
|
||||
#endif
|
||||
|
|
|
@ -329,7 +329,7 @@ namespace fwork
|
|||
size_t m_drawCount;
|
||||
#endif
|
||||
|
||||
inline DrawerYG * GetDrawer() const { return m_paintEvent->drawer().get(); }
|
||||
inline DrawerYG * GetDrawer() const { return m_paintEvent->drawer(); }
|
||||
|
||||
void PreProcessKeys(vector<drule::Key> & keys) const;
|
||||
|
||||
|
@ -340,8 +340,7 @@ namespace fwork
|
|||
DrawProcessor(m2::RectD const & r,
|
||||
ScreenBase const & convertor,
|
||||
shared_ptr<PaintEvent> const & paintEvent,
|
||||
int scaleLevel,
|
||||
yg::GlyphCache * glyphCache);
|
||||
int scaleLevel);
|
||||
|
||||
bool operator() (FeatureType const & f) const;
|
||||
};
|
||||
|
|
|
@ -21,7 +21,8 @@
|
|||
DrawerYG::Params::Params()
|
||||
: m_dynamicPagesCount(2),
|
||||
m_textPagesCount(2),
|
||||
m_threadID(0)
|
||||
m_threadID(0),
|
||||
m_visualScale(1)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -35,12 +36,12 @@ void di::DrawRule::SetID(size_t threadID, uint32_t id) const
|
|||
m_transparent ? m_rule->SetID2(threadID, id) : m_rule->SetID(threadID, id);
|
||||
}
|
||||
|
||||
DrawerYG::DrawerYG(string const & skinName, params_t const & params)
|
||||
: m_threadID(params.m_threadID)
|
||||
DrawerYG::DrawerYG(params_t const & params)
|
||||
: m_visualScale(params.m_visualScale), m_threadID(params.m_threadID)
|
||||
{
|
||||
m_pScreen = shared_ptr<yg::gl::Screen>(new yg::gl::Screen(params));
|
||||
m_pSkin = shared_ptr<yg::Skin>(loadSkin(params.m_resourceManager,
|
||||
skinName,
|
||||
params.m_skinName,
|
||||
params.m_dynamicPagesCount,
|
||||
params.m_textPagesCount));
|
||||
m_pScreen->setSkin(m_pSkin);
|
||||
|
@ -313,9 +314,9 @@ shared_ptr<yg::gl::Screen> DrawerYG::screen() const
|
|||
return m_pScreen;
|
||||
}
|
||||
|
||||
void DrawerYG::SetVisualScale(double visualScale)
|
||||
int DrawerYG::VisualScale() const
|
||||
{
|
||||
m_visualScale = visualScale;
|
||||
return m_visualScale;
|
||||
}
|
||||
|
||||
void DrawerYG::SetScale(int level)
|
||||
|
|
|
@ -94,12 +94,14 @@ public:
|
|||
size_t m_dynamicPagesCount;
|
||||
size_t m_textPagesCount;
|
||||
size_t m_threadID;
|
||||
string m_skinName;
|
||||
double m_visualScale;
|
||||
Params();
|
||||
};
|
||||
|
||||
typedef Params params_t;
|
||||
|
||||
DrawerYG(string const & skinName, params_t const & params = params_t());
|
||||
DrawerYG(params_t const & params = params_t());
|
||||
|
||||
void drawSymbol(m2::PointD const & pt, string const & symbolName, yg::EPosition pos, int depth);
|
||||
|
||||
|
@ -112,7 +114,7 @@ public:
|
|||
|
||||
shared_ptr<yg::gl::Screen> screen() const;
|
||||
|
||||
void SetVisualScale(double visualScale);
|
||||
int VisualScale() const;
|
||||
void SetScale(int level);
|
||||
|
||||
void Draw(di::DrawInfo const * pInfo, di::DrawRule const * rules, size_t count);
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
#include "../std/shared_ptr.hpp"
|
||||
|
||||
#include "../base/commands_queue.hpp"
|
||||
|
||||
#include "../base/start_mem_debug.hpp"
|
||||
|
||||
|
||||
|
@ -43,25 +45,24 @@ class DrawerYG;
|
|||
|
||||
class PaintEvent : public Event
|
||||
{
|
||||
shared_ptr<DrawerYG> m_drawer;
|
||||
bool m_isCancelled;
|
||||
|
||||
DrawerYG * m_drawer;
|
||||
core::CommandsQueue::Environment const * m_env;
|
||||
|
||||
public:
|
||||
PaintEvent(shared_ptr<DrawerYG> drawer)
|
||||
: m_drawer(drawer), m_isCancelled(false) {}
|
||||
|
||||
shared_ptr<DrawerYG> drawer() const
|
||||
PaintEvent(DrawerYG * drawer, core::CommandsQueue::Environment const * env = 0)
|
||||
: m_drawer(drawer), m_env(env)
|
||||
{}
|
||||
|
||||
DrawerYG * drawer() const
|
||||
{
|
||||
return m_drawer;
|
||||
}
|
||||
|
||||
void setIsCancelled(bool isCancelled)
|
||||
{
|
||||
m_isCancelled = isCancelled;
|
||||
}
|
||||
bool isCancelled() const
|
||||
{
|
||||
return m_isCancelled;
|
||||
return (m_env && m_env->IsCancelled());
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -287,12 +287,12 @@ double Framework<TModel>::GetCurrentScale() const
|
|||
|
||||
/// Actual rendering function.
|
||||
template <typename TModel>
|
||||
void Framework<TModel>::DrawModel(shared_ptr<PaintEvent> e,
|
||||
ScreenBase const & screen,
|
||||
m2::RectD const & selectRect,
|
||||
int scaleLevel)
|
||||
void Framework<TModel>::DrawModel(shared_ptr<PaintEvent> const & e,
|
||||
ScreenBase const & screen,
|
||||
m2::RectD const & selectRect,
|
||||
int scaleLevel)
|
||||
{
|
||||
fwork::DrawProcessor doDraw(selectRect, screen, e, scaleLevel, e->drawer()->screen()->glyphCache());
|
||||
fwork::DrawProcessor doDraw(selectRect, screen, e, scaleLevel);
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -313,8 +313,7 @@ void Framework<TModel>::DrawModel(shared_ptr<PaintEvent> e,
|
|||
template <typename TModel>
|
||||
void Framework<TModel>::Paint(shared_ptr<PaintEvent> e)
|
||||
{
|
||||
DrawerYG * pDrawer = e->drawer().get();
|
||||
pDrawer->SetVisualScale(GetPlatform().VisualScale());
|
||||
DrawerYG * pDrawer = e->drawer();
|
||||
|
||||
m_informationDisplay.setScreen(m_navigator.Screen());
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
#include "events.hpp"
|
||||
#include "drawer_yg.hpp"
|
||||
#include "render_queue.hpp"
|
||||
#include "tile_renderer.hpp"
|
||||
#include "information_display.hpp"
|
||||
#include "window_handle.hpp"
|
||||
#include "location_state.hpp"
|
||||
|
@ -181,7 +181,7 @@ public:
|
|||
|
||||
public:
|
||||
|
||||
void DrawModel(shared_ptr<PaintEvent> e,
|
||||
void DrawModel(shared_ptr<PaintEvent> const & e,
|
||||
ScreenBase const & screen,
|
||||
m2::RectD const & selectRect,
|
||||
int scaleLevel);
|
||||
|
|
12
map/map.pro
12
map/map.pro
|
@ -18,8 +18,7 @@ HEADERS += \
|
|||
draw_processor.hpp \
|
||||
draw_info.hpp \
|
||||
window_handle.hpp \
|
||||
render_queue.hpp \
|
||||
render_queue_routine.hpp \
|
||||
tile_renderer.hpp \
|
||||
information_display.hpp \
|
||||
location_state.hpp \
|
||||
benchmark_provider.hpp \
|
||||
|
@ -34,7 +33,8 @@ HEADERS += \
|
|||
coverage_generator.hpp \
|
||||
tiler.hpp \
|
||||
tile.hpp \
|
||||
tile_cache.hpp
|
||||
tile_cache.hpp \
|
||||
screen_coverage.hpp
|
||||
|
||||
SOURCES += \
|
||||
feature_vec_model.cpp \
|
||||
|
@ -42,8 +42,7 @@ SOURCES += \
|
|||
navigator.cpp \
|
||||
drawer_yg.cpp \
|
||||
draw_processor.cpp \
|
||||
render_queue.cpp \
|
||||
render_queue_routine.cpp \
|
||||
tile_renderer.cpp \
|
||||
information_display.cpp \
|
||||
location_state.cpp \
|
||||
benchmark_provider.cpp \
|
||||
|
@ -58,7 +57,8 @@ SOURCES += \
|
|||
coverage_generator.cpp \
|
||||
tiler.cpp \
|
||||
tile_cache.cpp \
|
||||
tile.cpp
|
||||
tile.cpp \
|
||||
screen_coverage.cpp
|
||||
|
||||
!iphone*:!bada*:!android* {
|
||||
HEADERS += qgl_render_context.hpp
|
||||
|
|
|
@ -36,8 +36,6 @@ void RenderPolicyST::DrawFrame(shared_ptr<PaintEvent> const & e,
|
|||
|
||||
e->drawer()->screen()->setInfoLayer(infoLayer);
|
||||
|
||||
e->drawer()->SetVisualScale(GetPlatform().VisualScale());
|
||||
|
||||
e->drawer()->screen()->clear(bgColor());
|
||||
renderFn()(e, s, s.GlobalRect(), scales::GetScaleLevel(glbRect));
|
||||
|
||||
|
|
229
map/screen_coverage.cpp
Normal file
229
map/screen_coverage.cpp
Normal file
|
@ -0,0 +1,229 @@
|
|||
#include "../base/SRC_FIRST.hpp"
|
||||
|
||||
#include "../std/bind.hpp"
|
||||
#include "../std/set.hpp"
|
||||
#include "../std/algorithm.hpp"
|
||||
|
||||
#include "../yg/screen.hpp"
|
||||
#include "../yg/base_texture.hpp"
|
||||
|
||||
#include "screen_coverage.hpp"
|
||||
#include "tile_renderer.hpp"
|
||||
#include "window_handle.hpp"
|
||||
#include "coverage_generator.hpp"
|
||||
|
||||
ScreenCoverage::ScreenCoverage()
|
||||
: m_tiler(0, 0)
|
||||
{}
|
||||
|
||||
ScreenCoverage::ScreenCoverage(TileRenderer * tileRenderer,
|
||||
CoverageGenerator * coverageGenerator,
|
||||
size_t tileSize,
|
||||
size_t scaleEtalonSize)
|
||||
: m_tileRenderer(tileRenderer),
|
||||
m_tiler(tileSize, scaleEtalonSize),
|
||||
m_coverageGenerator(coverageGenerator)
|
||||
{}
|
||||
|
||||
ScreenCoverage::ScreenCoverage(ScreenCoverage const & src)
|
||||
: m_tileRenderer(src.m_tileRenderer),
|
||||
m_tiler(src.m_tiler),
|
||||
m_screen(src.m_screen),
|
||||
m_tiles(src.m_tiles),
|
||||
m_infoLayer(src.m_infoLayer)
|
||||
{
|
||||
TileCache * tileCache = &m_tileRenderer->GetTileCache();
|
||||
tileCache->readLock();
|
||||
|
||||
for (size_t i = 0; i < m_tiles.size(); ++i)
|
||||
tileCache->lockTile(m_tiles[i]->m_rectInfo);
|
||||
|
||||
tileCache->readUnlock();
|
||||
}
|
||||
|
||||
ScreenCoverage const & ScreenCoverage::operator=(ScreenCoverage const & src)
|
||||
{
|
||||
if (&src != this)
|
||||
{
|
||||
m_tileRenderer = src.m_tileRenderer;
|
||||
m_tiler = src.m_tiler;
|
||||
m_screen = src.m_screen;
|
||||
m_tiles = src.m_tiles;
|
||||
|
||||
TileCache * tileCache = &m_tileRenderer->GetTileCache();
|
||||
|
||||
tileCache->readLock();
|
||||
|
||||
for (size_t i = 0; i < m_tiles.size(); ++i)
|
||||
tileCache->lockTile(m_tiles[i]->m_rectInfo);
|
||||
|
||||
tileCache->readUnlock();
|
||||
|
||||
m_infoLayer = src.m_infoLayer;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void ScreenCoverage::Merge(Tiler::RectInfo const & ri)
|
||||
{
|
||||
threads::MutexGuard g(m_mutex);
|
||||
TileCache * tileCache = &m_tileRenderer->GetTileCache();
|
||||
|
||||
Tile const * tile = 0;
|
||||
bool hasTile = false;
|
||||
|
||||
tileCache->readLock();
|
||||
hasTile = tileCache->hasTile(ri);
|
||||
if (hasTile)
|
||||
tile = &tileCache->getTile(ri);
|
||||
tileCache->readUnlock();
|
||||
|
||||
if (hasTile)
|
||||
{
|
||||
m_tiles.push_back(tile);
|
||||
m_infoLayer.merge(*tile->m_infoLayer.get(),
|
||||
tile->m_tileScreen.PtoGMatrix() * m_screen.GtoPMatrix());
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenCoverage::Remove(Tile const * tile)
|
||||
{
|
||||
threads::MutexGuard g(m_mutex);
|
||||
}
|
||||
|
||||
struct lessRectInfo
|
||||
{
|
||||
bool operator()(Tile const * l, Tile const * r)
|
||||
{
|
||||
return l->m_rectInfo.toUInt64Cell() < r->m_rectInfo.toUInt64Cell();
|
||||
}
|
||||
};
|
||||
|
||||
void ScreenCoverage::SetScreen(ScreenBase const & screen, bool mergePathNames)
|
||||
{
|
||||
threads::MutexGuard g(m_mutex);
|
||||
|
||||
m_screen = screen;
|
||||
|
||||
m_tiler.seed(m_screen, m_screen.GlobalRect().Center());
|
||||
|
||||
vector<Tiler::RectInfo> allRects;
|
||||
vector<Tiler::RectInfo> newRects;
|
||||
vector<Tile const *> tiles;
|
||||
|
||||
m_tiler.visibleTiles(allRects);
|
||||
|
||||
TileCache * tileCache = &m_tileRenderer->GetTileCache();
|
||||
|
||||
tileCache->readLock();
|
||||
|
||||
for (unsigned i = 0; i < allRects.size(); ++i)
|
||||
{
|
||||
Tiler::RectInfo ri = allRects[i];
|
||||
if (tileCache->hasTile(ri))
|
||||
{
|
||||
tiles.push_back(&tileCache->getTile(ri));
|
||||
tileCache->touchTile(ri);
|
||||
}
|
||||
else
|
||||
newRects.push_back(ri);
|
||||
}
|
||||
|
||||
/// computing difference between current and previous coverage
|
||||
/// tiles, that aren't in current coverage are unlocked to allow their deletion from TileCache
|
||||
/// tiles, that are new to the current coverage are added into m_tiles and locked in TileCache
|
||||
|
||||
typedef set<Tile const *, lessRectInfo> TileSet;
|
||||
|
||||
LOG(LINFO, ("prevSet size: ", m_tiles.size()));
|
||||
|
||||
TileSet prevSet;
|
||||
copy(m_tiles.begin(), m_tiles.end(), inserter(prevSet, prevSet.end()));
|
||||
|
||||
LOG(LINFO, ("curSet size: ", tiles.size()));
|
||||
|
||||
TileSet curSet;
|
||||
copy(tiles.begin(), tiles.end(), inserter(curSet, curSet.end()));
|
||||
|
||||
TileSet erasedTiles;
|
||||
TileSet addedTiles;
|
||||
|
||||
set_difference(prevSet.begin(), prevSet.end(), curSet.begin(), curSet.end(), inserter(erasedTiles, erasedTiles.end()));
|
||||
set_difference(curSet.begin(), curSet.end(), prevSet.begin(), prevSet.end(), inserter(addedTiles, addedTiles.end()));
|
||||
|
||||
LOG(LINFO, ("erased tiles size: ", erasedTiles.size()));
|
||||
|
||||
for (TileSet::const_iterator it = erasedTiles.begin(); it != erasedTiles.end(); ++it)
|
||||
{
|
||||
Tiler::RectInfo ri = (*it)->m_rectInfo;
|
||||
// LOG(LINFO, ("erasing tile: ", ri.m_tileScale, ri.m_drawScale, ri.m_y, ri.m_x));
|
||||
tileCache->unlockTile((*it)->m_rectInfo);
|
||||
/// here we should "unmerge" erasedTiles[i].m_infoLayer from m_infoLayer
|
||||
}
|
||||
|
||||
LOG(LINFO, ("added tiles size: ", addedTiles.size()));
|
||||
|
||||
for (TileSet::const_iterator it = addedTiles.begin(); it != addedTiles.end(); ++it)
|
||||
{
|
||||
Tiler::RectInfo ri = (*it)->m_rectInfo;
|
||||
// LOG(LINFO, ("adding tile: ", ri.m_tileScale, ri.m_drawScale, ri.m_y, ri.m_x));
|
||||
tileCache->lockTile((*it)->m_rectInfo);
|
||||
// m_infoLayer.merge(*((*it)->m_infoLayer.get()), (*it)->m_tileScreen.PtoGMatrix() * screen.GtoPMatrix());
|
||||
}
|
||||
|
||||
m_infoLayer.clear();
|
||||
for (size_t i = 0; i < tiles.size(); ++i)
|
||||
m_infoLayer.merge(*tiles[i]->m_infoLayer.get(), tiles[i]->m_tileScreen.PtoGMatrix() * screen.GtoPMatrix());
|
||||
|
||||
tileCache->readUnlock();
|
||||
|
||||
m_tiles = tiles;
|
||||
|
||||
/// adding commands for tiles which aren't in cache
|
||||
for (size_t i = 0; i < newRects.size(); ++i)
|
||||
{
|
||||
m_tileRenderer->AddCommand(newRects[i], m_tiler.sequenceID(),
|
||||
bind(&CoverageGenerator::AddMergeTileTask, m_coverageGenerator, newRects[i]));
|
||||
}
|
||||
}
|
||||
|
||||
ScreenCoverage::~ScreenCoverage()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
void ScreenCoverage::Clear()
|
||||
{
|
||||
threads::MutexGuard g(m_mutex);
|
||||
|
||||
TileCache * tileCache = &m_tileRenderer->GetTileCache();
|
||||
|
||||
tileCache->readLock();
|
||||
for (unsigned i = 0; i < m_tiles.size(); ++i)
|
||||
tileCache->unlockTile(m_tiles[i]->m_rectInfo);
|
||||
|
||||
tileCache->readUnlock();
|
||||
|
||||
m_tiles.clear();
|
||||
m_infoLayer.clear();
|
||||
}
|
||||
|
||||
void ScreenCoverage::Draw(yg::gl::Screen * s, ScreenBase const & screen)
|
||||
{
|
||||
threads::MutexGuard g(m_mutex);
|
||||
|
||||
for (size_t i = 0; i < m_tiles.size(); ++i)
|
||||
{
|
||||
Tile const * tile = m_tiles[i];
|
||||
size_t tileWidth = tile->m_renderTarget->width();
|
||||
size_t tileHeight = tile->m_renderTarget->height();
|
||||
|
||||
s->blit(tile->m_renderTarget, tile->m_tileScreen, screen, true,
|
||||
yg::Color(),
|
||||
m2::RectI(0, 0, tileWidth - 2, tileHeight - 2),
|
||||
m2::RectU(1, 1, tileWidth - 1, tileHeight - 1));
|
||||
|
||||
}
|
||||
|
||||
m_infoLayer.draw(s, m_screen.PtoGMatrix() * screen.GtoPMatrix());
|
||||
}
|
59
map/screen_coverage.hpp
Normal file
59
map/screen_coverage.hpp
Normal file
|
@ -0,0 +1,59 @@
|
|||
#pragma once
|
||||
|
||||
#include "../base/mutex.hpp"
|
||||
|
||||
#include "../geometry/screenbase.hpp"
|
||||
|
||||
#include "../yg/info_layer.hpp"
|
||||
|
||||
#include "tile.hpp"
|
||||
#include "tiler.hpp"
|
||||
#include "render_policy.hpp"
|
||||
|
||||
class TileRenderer;
|
||||
|
||||
namespace yg
|
||||
{
|
||||
namespace gl
|
||||
{
|
||||
class Screen;
|
||||
}
|
||||
}
|
||||
|
||||
class CoverageGenerator;
|
||||
|
||||
class ScreenCoverage
|
||||
{
|
||||
private:
|
||||
|
||||
threads::Mutex m_mutex;
|
||||
|
||||
TileRenderer * m_tileRenderer; //< queue to put new rendering tasks in
|
||||
Tiler m_tiler; //< tiler to compute visible and predicted tiles
|
||||
|
||||
ScreenBase m_screen; //< last covered screen
|
||||
vector<Tile const * > m_tiles; //< vector of visible tiles for m_screen
|
||||
yg::InfoLayer m_infoLayer; //< composite infoLayers for visible tiles
|
||||
|
||||
CoverageGenerator * m_coverageGenerator;
|
||||
|
||||
public:
|
||||
|
||||
ScreenCoverage();
|
||||
ScreenCoverage(TileRenderer * tileRenderer, CoverageGenerator * coverageGenerator, size_t tileSize, size_t scaleEtalonSize);
|
||||
~ScreenCoverage();
|
||||
|
||||
ScreenCoverage(ScreenCoverage const & src);
|
||||
ScreenCoverage const & operator=(ScreenCoverage const & src);
|
||||
|
||||
/// add rendered tile to coverage. Tile is locked, so make sure to unlock it in case it's not needed.
|
||||
void Merge(Tiler::RectInfo const & ri);
|
||||
/// remove tile from coverage
|
||||
void Remove(Tile const * tile);
|
||||
/// recalculate screen coverage, using as much info from prev coverage as possible
|
||||
void SetScreen(ScreenBase const & screen, bool mergePathNames = true);
|
||||
/// clear screen coverage and associated info layer
|
||||
void Clear();
|
||||
/// draw screen coverage
|
||||
void Draw(yg::gl::Screen * s, ScreenBase const & currentScreen);
|
||||
};
|
|
@ -39,12 +39,13 @@ private:
|
|||
public:
|
||||
|
||||
TileCache(size_t maxCacheSize);
|
||||
/// lock for multithreaded access
|
||||
/// lock for multithreaded READ access
|
||||
void readLock();
|
||||
/// unlock for multithreaded access
|
||||
/// unlock for multithreaded READ access
|
||||
void readUnlock();
|
||||
|
||||
/// lock for multithreaded WRITE access
|
||||
void writeLock();
|
||||
/// unlock for multithreaded WRITE access
|
||||
void writeUnlock();
|
||||
/// add tile to cache
|
||||
void addTile(Tiler::RectInfo const & key, Entry const & entry);
|
||||
|
|
|
@ -1,128 +1,198 @@
|
|||
#include "../base/SRC_FIRST.hpp"
|
||||
|
||||
#include "render_queue.hpp"
|
||||
#include "tile_renderer.hpp"
|
||||
#include "window_handle.hpp"
|
||||
|
||||
#include "../yg/render_state.hpp"
|
||||
#include "../yg/rendercontext.hpp"
|
||||
#include "../yg/base_texture.hpp"
|
||||
|
||||
#include "../std/bind.hpp"
|
||||
|
||||
#include "../base/logging.hpp"
|
||||
#include "../base/condition.hpp"
|
||||
|
||||
RenderQueue::RenderQueue(
|
||||
TileRenderer::TileRenderer(
|
||||
string const & skinName,
|
||||
unsigned scaleEtalonSize,
|
||||
unsigned maxTilesCount,
|
||||
unsigned tasksCount,
|
||||
yg::Color const & bgColor
|
||||
) : m_sequenceID(0), m_tileCache(maxTilesCount - 1), m_activeCommands(0)
|
||||
unsigned executorsCount,
|
||||
yg::Color const & bgColor,
|
||||
RenderPolicy::TRenderFn const & renderFn
|
||||
) : m_queue(executorsCount),
|
||||
m_tileCache(maxTilesCount - executorsCount - 1),
|
||||
m_renderFn(renderFn),
|
||||
m_skinName(skinName),
|
||||
m_bgColor(bgColor),
|
||||
m_sequenceID(0)
|
||||
{
|
||||
m_tasksCount = tasksCount; //< calculate from the CPU Cores Number
|
||||
LOG(LINFO, ("initializing ", tasksCount, " rendering threads"));
|
||||
m_tasks = new Task[m_tasksCount];
|
||||
for (unsigned i = 0; i < m_tasksCount; ++i)
|
||||
m_tasks[i].m_routine = new RenderQueueRoutine(
|
||||
skinName,
|
||||
scaleEtalonSize,
|
||||
bgColor,
|
||||
i,
|
||||
this);
|
||||
}
|
||||
|
||||
void RenderQueue::Initialize(shared_ptr<yg::gl::RenderContext> const & primaryContext,
|
||||
void TileRenderer::Initialize(shared_ptr<yg::gl::RenderContext> const & primaryContext,
|
||||
shared_ptr<yg::ResourceManager> const & resourceManager,
|
||||
double visualScale)
|
||||
{
|
||||
m_resourceManager = resourceManager;
|
||||
for (unsigned i = 0; i < m_tasksCount; ++i)
|
||||
|
||||
LOG(LINFO, ("initializing ", m_queue.ExecutorsCount(), " rendering threads"));
|
||||
|
||||
int tileWidth = m_resourceManager->tileTextureWidth();
|
||||
int tileHeight = m_resourceManager->tileTextureHeight();
|
||||
|
||||
for (unsigned i = 0; i < m_queue.ExecutorsCount(); ++i)
|
||||
{
|
||||
m_tasks[i].m_routine->InitializeGL(primaryContext->createShared(),
|
||||
m_resourceManager,
|
||||
visualScale);
|
||||
m_tasks[i].m_thread.Create(m_tasks[i].m_routine);
|
||||
DrawerYG::params_t params;
|
||||
|
||||
params.m_resourceManager = m_resourceManager;
|
||||
params.m_frameBuffer = make_shared_ptr(new yg::gl::FrameBuffer());
|
||||
|
||||
shared_ptr<yg::gl::RenderBuffer> depthBuffer(new yg::gl::RenderBuffer(tileWidth, tileHeight, true));
|
||||
params.m_frameBuffer->setDepthBuffer(depthBuffer);
|
||||
|
||||
params.m_glyphCacheID = m_resourceManager->renderThreadGlyphCacheID(i);
|
||||
params.m_useOverlay = true;
|
||||
params.m_threadID = i;
|
||||
params.m_visualScale = visualScale;
|
||||
params.m_skinName = m_skinName;
|
||||
/* params.m_isDebugging = true;
|
||||
params.m_drawPathes = false;
|
||||
params.m_drawAreas = false;
|
||||
params.m_drawTexts = false; */
|
||||
|
||||
m_drawerParams.push_back(params);
|
||||
m_drawers.push_back(0);
|
||||
m_renderContexts.push_back(primaryContext->createShared());
|
||||
}
|
||||
|
||||
m_queue.AddInitCommand(bind(&TileRenderer::InitializeThreadGL, this, _1));
|
||||
m_queue.AddFinCommand(bind(&TileRenderer::FinalizeThreadGL, this, _1));
|
||||
|
||||
m_queue.Start();
|
||||
}
|
||||
|
||||
RenderQueue::~RenderQueue()
|
||||
TileRenderer::~TileRenderer()
|
||||
{
|
||||
m_renderCommands.Cancel();
|
||||
for (unsigned i = 0; i < m_tasksCount; ++i)
|
||||
m_tasks[i].m_thread.Cancel();
|
||||
delete [] m_tasks;
|
||||
m_queue.Cancel();
|
||||
}
|
||||
|
||||
void RenderQueue::AddCommand(
|
||||
RenderQueueRoutine::TRenderFn const & renderFn,
|
||||
Tiler::RectInfo const & rectInfo,
|
||||
size_t sequenceID,
|
||||
RenderQueueRoutine::TCommandFinishedFn const & commandFinishedFn)
|
||||
void TileRenderer::InitializeThreadGL(core::CommandsQueue::Environment const & env)
|
||||
{
|
||||
m_renderContexts[env.GetThreadNum()]->makeCurrent();
|
||||
|
||||
m_drawers[env.GetThreadNum()] = new DrawerYG(m_drawerParams[env.GetThreadNum()]);
|
||||
}
|
||||
|
||||
void TileRenderer::FinalizeThreadGL(core::CommandsQueue::Environment const & env)
|
||||
{
|
||||
m_renderContexts[env.GetThreadNum()]->endThreadDrawing();
|
||||
if (m_drawers[env.GetThreadNum()] != 0)
|
||||
delete m_drawers[env.GetThreadNum()];
|
||||
}
|
||||
|
||||
void TileRenderer::DrawTile(core::CommandsQueue::Environment const & env,
|
||||
Tiler::RectInfo const & rectInfo,
|
||||
int sequenceID)
|
||||
{
|
||||
DrawerYG * drawer = m_drawers[env.GetThreadNum()];
|
||||
|
||||
ScreenBase frameScreen;
|
||||
|
||||
unsigned tileWidth = m_resourceManager->tileTextureWidth();
|
||||
unsigned tileHeight = m_resourceManager->tileTextureHeight();
|
||||
|
||||
m2::RectI renderRect(1, 1, tileWidth - 1, tileHeight - 1);
|
||||
|
||||
frameScreen.OnSize(renderRect);
|
||||
|
||||
/// commands from the previous sequence are ignored
|
||||
if (sequenceID < m_sequenceID)
|
||||
return;
|
||||
|
||||
if (HasTile(rectInfo))
|
||||
return;
|
||||
|
||||
shared_ptr<PaintEvent> paintEvent = make_shared_ptr(new PaintEvent(drawer, &env));
|
||||
|
||||
my::Timer timer;
|
||||
|
||||
shared_ptr<yg::gl::BaseTexture> tileTarget = m_resourceManager->renderTargets().Front(true);
|
||||
|
||||
if (m_resourceManager->renderTargets().IsCancelled())
|
||||
return;
|
||||
|
||||
drawer->screen()->setRenderTarget(tileTarget);
|
||||
|
||||
shared_ptr<yg::InfoLayer> tileInfoLayer(new yg::InfoLayer());
|
||||
|
||||
drawer->screen()->setInfoLayer(tileInfoLayer);
|
||||
|
||||
drawer->beginFrame();
|
||||
drawer->clear(yg::Color(m_bgColor.r, m_bgColor.g, m_bgColor.b, 0));
|
||||
drawer->screen()->setClipRect(renderRect);
|
||||
drawer->clear(m_bgColor);
|
||||
|
||||
frameScreen.SetFromRect(rectInfo.m_rect);
|
||||
|
||||
m2::RectD selectionRect;
|
||||
|
||||
double const inflationSize = 24 * drawer->VisualScale();
|
||||
//frameScreen.PtoG(m2::Inflate(m2::RectD(renderRect), inflationSize, inflationSize), selectionRect);
|
||||
frameScreen.PtoG(m2::RectD(renderRect), selectionRect);
|
||||
|
||||
m_renderFn(
|
||||
paintEvent,
|
||||
frameScreen,
|
||||
selectionRect,
|
||||
rectInfo.m_drawScale
|
||||
);
|
||||
|
||||
drawer->endFrame();
|
||||
drawer->screen()->resetInfoLayer();
|
||||
|
||||
double duration = timer.ElapsedSeconds();
|
||||
|
||||
if (env.IsCancelled())
|
||||
m_resourceManager->renderTargets().PushBack(tileTarget);
|
||||
else
|
||||
AddTile(rectInfo, Tile(tileTarget, tileInfoLayer, frameScreen, rectInfo, duration));
|
||||
}
|
||||
|
||||
void TileRenderer::AddCommand(Tiler::RectInfo const & rectInfo, int sequenceID, core::CommandsQueue::Chain const & afterTileFns)
|
||||
{
|
||||
m_sequenceID = sequenceID;
|
||||
m_renderCommands.PushBack(make_shared_ptr(new RenderQueueRoutine::Command(renderFn, rectInfo, sequenceID, commandFinishedFn)));
|
||||
{
|
||||
threads::ConditionGuard g(m_emptyAndFinished);
|
||||
m_activeCommands++;
|
||||
}
|
||||
|
||||
core::CommandsQueue::Chain chain;
|
||||
chain.addCommand(bind(&TileRenderer::DrawTile, this, _1, rectInfo, sequenceID));
|
||||
chain.addCommand(afterTileFns);
|
||||
|
||||
m_queue.AddCommand(chain);
|
||||
}
|
||||
|
||||
void RenderQueue::AddWindowHandle(shared_ptr<WindowHandle> const & window)
|
||||
{
|
||||
m_windowHandles.push_back(window);
|
||||
}
|
||||
|
||||
void RenderQueue::Invalidate()
|
||||
{
|
||||
for_each(m_windowHandles.begin(),
|
||||
m_windowHandles.end(),
|
||||
bind(&WindowHandle::invalidate, _1));
|
||||
}
|
||||
|
||||
void RenderQueue::MemoryWarning()
|
||||
{
|
||||
for (unsigned i = 0; i < m_tasksCount; ++i)
|
||||
m_tasks[i].m_routine->MemoryWarning();
|
||||
}
|
||||
|
||||
void RenderQueue::EnterBackground()
|
||||
{
|
||||
for (unsigned i = 0; i < m_tasksCount; ++i)
|
||||
m_tasks[i].m_routine->EnterBackground();
|
||||
}
|
||||
|
||||
void RenderQueue::EnterForeground()
|
||||
{
|
||||
for (unsigned i = 0; i < m_tasksCount; ++i)
|
||||
m_tasks[i].m_routine->EnterForeground();
|
||||
}
|
||||
|
||||
TileCache & RenderQueue::GetTileCache()
|
||||
TileCache & TileRenderer::GetTileCache()
|
||||
{
|
||||
return m_tileCache;
|
||||
}
|
||||
|
||||
size_t RenderQueue::CurrentSequence() const
|
||||
void TileRenderer::WaitForEmptyAndFinished()
|
||||
{
|
||||
return m_sequenceID;
|
||||
m_queue.Join();
|
||||
}
|
||||
|
||||
ThreadedList<shared_ptr<RenderQueueRoutine::Command > > & RenderQueue::RenderCommands()
|
||||
bool TileRenderer::HasTile(Tiler::RectInfo const & rectInfo)
|
||||
{
|
||||
return m_renderCommands;
|
||||
m_tileCache.readLock();
|
||||
bool res = m_tileCache.hasTile(rectInfo);
|
||||
m_tileCache.readUnlock();
|
||||
return res;
|
||||
}
|
||||
|
||||
void RenderQueue::WaitForEmptyAndFinished()
|
||||
void TileRenderer::AddTile(Tiler::RectInfo const & rectInfo, Tile const & tile)
|
||||
{
|
||||
threads::ConditionGuard g(m_emptyAndFinished);
|
||||
if (m_activeCommands != 0)
|
||||
m_emptyAndFinished.Wait();
|
||||
m_tileCache.writeLock();
|
||||
if (m_tileCache.hasTile(rectInfo))
|
||||
m_tileCache.touchTile(rectInfo);
|
||||
else
|
||||
m_tileCache.addTile(rectInfo, TileCache::Entry(tile, m_resourceManager));
|
||||
m_tileCache.writeUnlock();
|
||||
}
|
||||
|
||||
void RenderQueue::FinishCommand()
|
||||
{
|
||||
threads::ConditionGuard g(m_emptyAndFinished);
|
||||
--m_activeCommands;
|
||||
if (m_activeCommands == 0)
|
||||
m_emptyAndFinished.Signal();
|
||||
}
|
||||
|
|
|
@ -1,15 +1,18 @@
|
|||
#pragma once
|
||||
|
||||
#include "render_queue_routine.hpp"
|
||||
#include "render_policy.hpp"
|
||||
#include "tiler.hpp"
|
||||
#include "tile_cache.hpp"
|
||||
#include "drawer_yg.hpp"
|
||||
|
||||
#include "../geometry/screenbase.hpp"
|
||||
|
||||
#include "../base/thread.hpp"
|
||||
#include "../base/threaded_list.hpp"
|
||||
#include "../base/commands_queue.hpp"
|
||||
|
||||
#include "../std/shared_ptr.hpp"
|
||||
#include "../std/vector.hpp"
|
||||
|
||||
namespace yg
|
||||
{
|
||||
|
@ -22,77 +25,58 @@ namespace yg
|
|||
}
|
||||
|
||||
class WindowHandle;
|
||||
class DrawerYG;
|
||||
|
||||
class RenderQueue
|
||||
class TileRenderer
|
||||
{
|
||||
private:
|
||||
|
||||
/// Single rendering task. Engine allocates tasks by number of cores
|
||||
struct Task
|
||||
{
|
||||
threads::Thread m_thread;
|
||||
RenderQueueRoutine * m_routine;
|
||||
};
|
||||
|
||||
Task * m_tasks;
|
||||
size_t m_tasksCount;
|
||||
|
||||
size_t m_sequenceID;
|
||||
|
||||
friend class RenderQueueRoutine;
|
||||
core::CommandsQueue m_queue;
|
||||
|
||||
shared_ptr<yg::ResourceManager> m_resourceManager;
|
||||
|
||||
ThreadedList<shared_ptr<RenderQueueRoutine::Command> > m_renderCommands;
|
||||
vector<DrawerYG*> m_drawers;
|
||||
vector<DrawerYG::params_t> m_drawerParams;
|
||||
vector<shared_ptr<yg::gl::RenderContext> > m_renderContexts;
|
||||
|
||||
TileCache m_tileCache;
|
||||
|
||||
/// A list of window handles to notify about ending rendering operations.
|
||||
list<shared_ptr<WindowHandle> > m_windowHandles;
|
||||
RenderPolicy::TRenderFn m_renderFn;
|
||||
string m_skinName;
|
||||
yg::Color m_bgColor;
|
||||
int m_sequenceID;
|
||||
|
||||
threads::Condition m_emptyAndFinished;
|
||||
void InitializeThreadGL(core::CommandsQueue::Environment const & env);
|
||||
void FinalizeThreadGL(core::CommandsQueue::Environment const & env);
|
||||
|
||||
/// number of currently active render commands
|
||||
int m_activeCommands;
|
||||
void DrawTile(core::CommandsQueue::Environment const & env,
|
||||
Tiler::RectInfo const & rectInfo,
|
||||
int sequenceID);
|
||||
|
||||
public:
|
||||
|
||||
/// constructor.
|
||||
RenderQueue(string const & skinName,
|
||||
unsigned scaleEtalonSize,
|
||||
unsigned maxTilesCount,
|
||||
unsigned tasksCount,
|
||||
yg::Color const & bgColor);
|
||||
TileRenderer(string const & skinName,
|
||||
unsigned scaleEtalonSize,
|
||||
unsigned maxTilesCount,
|
||||
unsigned tasksCount,
|
||||
yg::Color const & bgColor,
|
||||
RenderPolicy::TRenderFn const & renderFn);
|
||||
/// destructor.
|
||||
~RenderQueue();
|
||||
~TileRenderer();
|
||||
/// set the primary context. it starts the rendering thread.
|
||||
void Initialize(shared_ptr<yg::gl::RenderContext> const & primaryContext,
|
||||
shared_ptr<yg::ResourceManager> const & resourceManager,
|
||||
double visualScale);
|
||||
/// add command to the commands queue.
|
||||
void AddCommand(RenderQueueRoutine::TRenderFn const & renderFn,
|
||||
Tiler::RectInfo const & rectInfo,
|
||||
size_t sequenceID,
|
||||
RenderQueueRoutine::TCommandFinishedFn const & commandFinishedFn);
|
||||
/// add window handle to notify when rendering operation finishes
|
||||
void AddWindowHandle(shared_ptr<WindowHandle> const & windowHandle);
|
||||
/// free all possible memory caches.
|
||||
void MemoryWarning();
|
||||
/// free all possible memory caches, opengl resources,
|
||||
/// and make sure no opengl call will be made in background
|
||||
void EnterBackground();
|
||||
/// load all necessary memory caches and opengl resources.
|
||||
void EnterForeground();
|
||||
void AddCommand(Tiler::RectInfo const & rectInfo,
|
||||
int sequenceID,
|
||||
core::CommandsQueue::Chain const & afterTileFns = core::CommandsQueue::Chain());
|
||||
/// get tile cache.
|
||||
TileCache & GetTileCache();
|
||||
/// number of the current tiler sequence to skip the old commands upon rendering.
|
||||
size_t CurrentSequence() const;
|
||||
/// common render commands queue for all rendering threads.
|
||||
ThreadedList<shared_ptr<RenderQueueRoutine::Command> > & RenderCommands();
|
||||
/// invalidate all connected windows
|
||||
void Invalidate();
|
||||
/// wait on a condition variable for an empty queue.
|
||||
void WaitForEmptyAndFinished();
|
||||
|
||||
void FinishCommand();
|
||||
bool HasTile(Tiler::RectInfo const & rectInfo);
|
||||
void AddTile(Tiler::RectInfo const & rectInfo, Tile const & tile);
|
||||
};
|
||||
|
|
|
@ -123,23 +123,16 @@ void Tiler::seed(ScreenBase const & screen, m2::PointD const & centerPt)
|
|||
m_coverage.push_back(RectInfo(m_drawScale, m_tileScale, tileX, tileY, screenRect, centerPt));
|
||||
|
||||
/// sorting coverage elements
|
||||
sort(m_coverage.begin(), m_coverage.end(), greater<RectInfo>());
|
||||
sort(m_coverage.begin(), m_coverage.end(), less<RectInfo>());
|
||||
}
|
||||
|
||||
Tiler::Tiler(size_t tileSize, size_t scaleEtalonSize)
|
||||
: m_sequenceID(0), m_tileSize(tileSize), m_scaleEtalonSize(scaleEtalonSize)
|
||||
{}
|
||||
|
||||
bool Tiler::hasTile()
|
||||
void Tiler::visibleTiles(vector<RectInfo> & tiles)
|
||||
{
|
||||
return !m_coverage.empty();
|
||||
}
|
||||
|
||||
Tiler::RectInfo const Tiler::nextTile()
|
||||
{
|
||||
RectInfo r = m_coverage.back();
|
||||
m_coverage.pop_back();
|
||||
return r;
|
||||
tiles = m_coverage;
|
||||
}
|
||||
|
||||
size_t Tiler::sequenceID() const
|
||||
|
|
|
@ -51,10 +51,7 @@ public:
|
|||
/// seed tiler with new screenBase.
|
||||
void seed(ScreenBase const & screenBase, m2::PointD const & centerPt);
|
||||
|
||||
/// check whether the sequence has next tile
|
||||
bool hasTile();
|
||||
/// pop tile from the sequence and return it
|
||||
RectInfo const nextTile();
|
||||
void visibleTiles(vector<RectInfo> & tiles);
|
||||
|
||||
size_t sequenceID() const;
|
||||
};
|
||||
|
|
|
@ -11,31 +11,33 @@
|
|||
#include "events.hpp"
|
||||
#include "tiling_render_policy_mt.hpp"
|
||||
#include "window_handle.hpp"
|
||||
#include "screen_coverage.hpp"
|
||||
|
||||
TilingRenderPolicyMT::TilingRenderPolicyMT(shared_ptr<WindowHandle> const & windowHandle,
|
||||
RenderPolicy::TRenderFn const & renderFn)
|
||||
: RenderPolicy(windowHandle, renderFn),
|
||||
m_renderQueue(GetPlatform().SkinName(),
|
||||
m_tileRenderer(GetPlatform().SkinName(),
|
||||
GetPlatform().ScaleEtalonSize(),
|
||||
GetPlatform().MaxTilesCount(),
|
||||
1/*GetPlatform().CpuCores()*/,
|
||||
bgColor()),
|
||||
m_tiler(GetPlatform().TileSize(),
|
||||
GetPlatform().ScaleEtalonSize())
|
||||
// m_coverageGenerator(GetPlatform().TileSize(),
|
||||
// GetPlatform().ScaleEtalonSize(),
|
||||
// renderFn,
|
||||
// &m_renderQueue)
|
||||
2/*GetPlatform().CpuCores()*/,
|
||||
bgColor(),
|
||||
renderFn),
|
||||
m_coverageGenerator(GetPlatform().TileSize(),
|
||||
GetPlatform().ScaleEtalonSize(),
|
||||
&m_tileRenderer,
|
||||
windowHandle)
|
||||
/* m_screenCoverage(&m_tileRenderer,
|
||||
GetPlatform().TileSize(),
|
||||
GetPlatform().ScaleEtalonSize())*/
|
||||
{
|
||||
m_renderQueue.AddWindowHandle(windowHandle);
|
||||
}
|
||||
|
||||
void TilingRenderPolicyMT::Initialize(shared_ptr<yg::gl::RenderContext> const & primaryContext,
|
||||
shared_ptr<yg::ResourceManager> const & resourceManager)
|
||||
{
|
||||
RenderPolicy::Initialize(primaryContext, resourceManager);
|
||||
m_renderQueue.Initialize(primaryContext, resourceManager, GetPlatform().VisualScale());
|
||||
// m_coverageGenerator.Initialize();
|
||||
m_tileRenderer.Initialize(primaryContext, resourceManager, GetPlatform().VisualScale());
|
||||
m_coverageGenerator.Initialize();
|
||||
}
|
||||
|
||||
void TilingRenderPolicyMT::OnSize(int /*w*/, int /*h*/)
|
||||
|
@ -44,84 +46,24 @@ void TilingRenderPolicyMT::OnSize(int /*w*/, int /*h*/)
|
|||
|
||||
void TilingRenderPolicyMT::DrawFrame(shared_ptr<PaintEvent> const & e, ScreenBase const & currentScreen)
|
||||
{
|
||||
DrawerYG * pDrawer = e->drawer().get();
|
||||
DrawerYG * pDrawer = e->drawer();
|
||||
pDrawer->screen()->clear(bgColor());
|
||||
|
||||
/* m_coverageGenerator.AddCoverageTask(currentScreen);
|
||||
m_coverageGenerator.AddCoverScreenTask(currentScreen);
|
||||
|
||||
ScreenCoverage coverage;
|
||||
|
||||
{
|
||||
/// copying full coverage
|
||||
threads::MutexGuard(m_coverageGenerator.Mutex());
|
||||
coverage = *m_coverageGenerator.CurrentCoverage();
|
||||
}
|
||||
m_coverageGenerator.CopyCurrentCoverage(coverage);
|
||||
|
||||
/// drawing from full coverage
|
||||
for (unsigned i = 0; i < coverage.m_tiles.size(); ++i)
|
||||
{
|
||||
shared_ptr<Tile const> tile = coverage.m_tiles[i];
|
||||
coverage.Draw(pDrawer->screen().get(), currentScreen);
|
||||
|
||||
size_t tileWidth = tile->m_renderTarget->width();
|
||||
size_t tileHeight = tile->m_renderTarget->height();
|
||||
/* m_screenCoverage.SetScreen(currentScreen);
|
||||
|
||||
pDrawer->screen()->blit(tile->m_renderTarget, tile->m_tileScreen, currentScreen, true,
|
||||
yg::Color(),
|
||||
m2::RectI(0, 0, tileWidth - 2, tileHeight - 2),
|
||||
m2::RectU(1, 1, tileWidth - 1, tileHeight - 1));
|
||||
}
|
||||
|
||||
coverage.m_infoLayer.draw(pDrawer->screen().get(),
|
||||
coverage.m_screen.PtoGMatrix() * currentScreen.GtoPMatrix());
|
||||
|
||||
coverage.Clear();*/
|
||||
|
||||
m_infoLayer.clear();
|
||||
|
||||
m_tiler.seed(currentScreen,
|
||||
currentScreen.GlobalRect().Center());
|
||||
|
||||
while (m_tiler.hasTile())
|
||||
{
|
||||
Tiler::RectInfo ri = m_tiler.nextTile();
|
||||
|
||||
TileCache & tileCache = m_renderQueue.GetTileCache();
|
||||
|
||||
tileCache.readLock();
|
||||
|
||||
if (tileCache.hasTile(ri))
|
||||
{
|
||||
tileCache.touchTile(ri);
|
||||
Tile tile = tileCache.getTile(ri);
|
||||
tileCache.readUnlock();
|
||||
|
||||
size_t tileWidth = tile.m_renderTarget->width();
|
||||
size_t tileHeight = tile.m_renderTarget->height();
|
||||
|
||||
pDrawer->screen()->blit(tile.m_renderTarget, tile.m_tileScreen, currentScreen, true,
|
||||
yg::Color(),
|
||||
m2::RectI(0, 0, tileWidth - 2, tileHeight - 2),
|
||||
m2::RectU(1, 1, tileWidth - 1, tileHeight - 1));
|
||||
|
||||
m_infoLayer.merge(*tile.m_infoLayer.get(),
|
||||
tile.m_tileScreen.PtoGMatrix() * currentScreen.GtoPMatrix());
|
||||
}
|
||||
else
|
||||
{
|
||||
tileCache.readUnlock();
|
||||
m_renderQueue.AddCommand(
|
||||
renderFn(),
|
||||
ri,
|
||||
m_tiler.sequenceID(),
|
||||
bind(&WindowHandle::invalidate, windowHandle()));
|
||||
}
|
||||
}
|
||||
|
||||
m_infoLayer.draw(pDrawer->screen().get(),
|
||||
math::Identity<double, 3>());
|
||||
m_screenCoverage.Draw(pDrawer->screen().get(), currentScreen);
|
||||
m_currentScreen = currentScreen;*/
|
||||
}
|
||||
|
||||
RenderQueue & TilingRenderPolicyMT::GetRenderQueue()
|
||||
TileRenderer & TilingRenderPolicyMT::GetTileRenderer()
|
||||
{
|
||||
return m_renderQueue;
|
||||
return m_tileRenderer;
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include "render_policy.hpp"
|
||||
#include "render_queue.hpp"
|
||||
#include "tile_renderer.hpp"
|
||||
#include "coverage_generator.hpp"
|
||||
#include "tiler.hpp"
|
||||
#include "screen_coverage.hpp"
|
||||
|
||||
#include "../yg/info_layer.hpp"
|
||||
|
||||
|
@ -22,16 +23,17 @@ class TilingRenderPolicyMT : public RenderPolicy
|
|||
{
|
||||
private:
|
||||
|
||||
RenderQueue m_renderQueue;
|
||||
// CoverageGenerator m_coverageGenerator;
|
||||
TileRenderer m_tileRenderer;
|
||||
|
||||
yg::InfoLayer m_infoLayer;
|
||||
CoverageGenerator m_coverageGenerator;
|
||||
|
||||
Tiler m_tiler;
|
||||
ScreenBase m_currentScreen;
|
||||
|
||||
// ScreenCoverage m_screenCoverage;
|
||||
|
||||
protected:
|
||||
|
||||
RenderQueue & GetRenderQueue();
|
||||
TileRenderer & GetTileRenderer();
|
||||
|
||||
public:
|
||||
|
||||
|
|
|
@ -42,12 +42,12 @@ void TilingRenderPolicyST::Initialize(shared_ptr<yg::gl::RenderContext> const &
|
|||
params.m_glyphCacheID = resourceManager()->guiThreadGlyphCacheID();
|
||||
params.m_useOverlay = true;
|
||||
params.m_threadID = 0;
|
||||
params.m_skinName = GetPlatform().SkinName();
|
||||
params.m_visualScale = GetPlatform().VisualScale();
|
||||
|
||||
m_tileDrawer = make_shared_ptr(new DrawerYG(GetPlatform().SkinName(), params));
|
||||
m_tileDrawer = make_shared_ptr(new DrawerYG(params));
|
||||
m_tileDrawer->onSize(tileWidth, tileHeight);
|
||||
|
||||
m_tileDrawer->SetVisualScale(GetPlatform().VisualScale());
|
||||
|
||||
m2::RectI renderRect(1, 1, tileWidth - 1, tileWidth - 1);
|
||||
m_tileScreen.OnSize(renderRect);
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ void TilingRenderPolicyST::OnSize(int /*w*/, int /*h*/)
|
|||
|
||||
void TilingRenderPolicyST::DrawFrame(shared_ptr<PaintEvent> const & e, ScreenBase const & currentScreen)
|
||||
{
|
||||
DrawerYG * pDrawer = e->drawer().get();
|
||||
DrawerYG * pDrawer = e->drawer();
|
||||
|
||||
pDrawer->screen()->clear(bgColor());
|
||||
|
||||
|
@ -66,9 +66,12 @@ void TilingRenderPolicyST::DrawFrame(shared_ptr<PaintEvent> const & e, ScreenBas
|
|||
m_tiler.seed(currentScreen,
|
||||
currentScreen.GlobalRect().Center());
|
||||
|
||||
while (m_tiler.hasTile())
|
||||
vector<Tiler::RectInfo> visibleTiles;
|
||||
m_tiler.visibleTiles(visibleTiles);
|
||||
|
||||
for (unsigned i = 0; i < visibleTiles.size(); ++i)
|
||||
{
|
||||
Tiler::RectInfo ri = m_tiler.nextTile();
|
||||
Tiler::RectInfo ri = visibleTiles[i];
|
||||
|
||||
m_tileCache.readLock();
|
||||
|
||||
|
@ -91,7 +94,7 @@ void TilingRenderPolicyST::DrawFrame(shared_ptr<PaintEvent> const & e, ScreenBas
|
|||
else
|
||||
{
|
||||
m_tileCache.readUnlock();
|
||||
shared_ptr<PaintEvent> paintEvent(new PaintEvent(m_tileDrawer));
|
||||
shared_ptr<PaintEvent> paintEvent(new PaintEvent(m_tileDrawer.get()));
|
||||
shared_ptr<yg::gl::BaseTexture> tileTarget = resourceManager()->renderTargets().Front(true);
|
||||
|
||||
shared_ptr<yg::InfoLayer> tileInfoLayer(new yg::InfoLayer());
|
||||
|
|
|
@ -171,7 +171,7 @@ namespace qt
|
|||
|
||||
void DrawWidget::DoDraw(shared_ptr<drawer_t> p)
|
||||
{
|
||||
shared_ptr<PaintEvent> paintEvent(new PaintEvent(p));
|
||||
shared_ptr<PaintEvent> paintEvent(new PaintEvent(p.get()));
|
||||
m_framework->Paint(paintEvent);
|
||||
}
|
||||
|
||||
|
|
|
@ -72,8 +72,10 @@ namespace qt
|
|||
p.m_dynamicPagesCount = 2;
|
||||
p.m_textPagesCount = 2;
|
||||
p.m_glyphCacheID = m_resourceManager->guiThreadGlyphCacheID();
|
||||
p.m_skinName = GetPlatform().SkinName();
|
||||
p.m_visualScale = GetPlatform().VisualScale();
|
||||
|
||||
m_p = shared_ptr<DrawerYG>(new DrawerYG(GetPlatform().SkinName(), p));
|
||||
m_p = shared_ptr<DrawerYG>(new DrawerYG(p));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue