#include "frontend_renderer.hpp" #include "message_subclasses.hpp" #include "visual_params.hpp" #include "../base/timer.hpp" #include "../base/assert.hpp" #include "../base/stl_add.hpp" #include "../geometry/any_rect2d.hpp" #include "../std/bind.hpp" #include "../std/cmath.hpp" namespace df { namespace { #ifdef DEBUG const double VSyncInterval = 0.030; //const double InitAvarageTimePerMessage = 0.003; #else const double VSyncInterval = 0.014; //const double InitAvarageTimePerMessage = 0.001; #endif void OrthoMatrix(float * m, float left, float right, float bottom, float top, float nearClip, float farClip) { memset(m, 0, 16 * sizeof(float)); m[0] = 2.0f / (right - left); m[3] = - (right + left) / (right - left); m[5] = 2.0f / (top - bottom); m[7] = - (top + bottom) / (top - bottom); m[10] = -2.0f / (farClip - nearClip); m[11] = - (farClip + nearClip) / (farClip - nearClip); m[15] = 1.0; } } // namespace FrontendRenderer::FrontendRenderer(dp::RefPointer commutator, dp::RefPointer oglcontextfactory, dp::TransferPointer textureController, Viewport viewport) : m_commutator(commutator) , m_contextFactory(oglcontextfactory) , m_textureController(textureController) , m_gpuProgramManager(new dp::GpuProgramManager()) , m_viewport(viewport) { #ifdef DRAW_INFO m_tpf = 0,0; m_fps = 0.0; #endif m_commutator->RegisterThread(ThreadsCommutator::RenderThread, this); RefreshProjection(); RefreshModelView(); StartThread(); } FrontendRenderer::~FrontendRenderer() { StopThread(); } #ifdef DRAW_INFO void FrontendRenderer::BeforeDrawFrame() { m_frameStartTime = m_timer.ElapsedSeconds(); } void FrontendRenderer::AfterDrawFrame() { m_drawedFrames++; double elapsed = m_timer.ElapsedSeconds(); m_tpfs.push_back(elapsed - m_frameStartTime); if (elapsed > 1.0) { m_timer.Reset(); m_fps = m_drawedFrames / elapsed; m_drawedFrames = 0; m_tpf = accumulate(m_tpfs.begin(), m_tpfs.end(), 0.0) / m_tpfs.size(); LOG(LINFO, ("Average Fps : ", m_fps)); LOG(LINFO, ("Average Tpf : ", m_tpf)); } } #endif void FrontendRenderer::AcceptMessage(dp::RefPointer message) { switch (message->GetType()) { case Message::FlushTile: { FlushRenderBucketMessage * msg = df::CastMessage(message); dp::GLState const & state = msg->GetState(); TileKey const & key = msg->GetKey(); dp::MasterPointer bucket(msg->AcceptBuffer()); dp::RefPointer program = m_gpuProgramManager->GetProgram(state.GetProgramIndex()); program->Bind(); bucket->GetBuffer()->Build(program); RenderGroup * group = new RenderGroup(state, key); group->AddBucket(bucket.Move()); m_renderGroups.push_back(group); break; } case Message::Resize: { ResizeMessage * rszMsg = df::CastMessage(message); m_viewport = rszMsg->GetViewport(); m_view.OnSize(m_viewport.GetX0(), m_viewport.GetY0(), m_viewport.GetWidth(), m_viewport.GetHeight()); RefreshProjection(); RefreshModelView(); ResolveTileKeys(); m_commutator->PostMessage(ThreadsCommutator::ResourceUploadThread, dp::MovePointer(new UpdateReadManagerMessage(m_view, m_tiles))); break; } case Message::UpdateModelView: { UpdateModelViewMessage * coverMessage = df::CastMessage(message); m_view = coverMessage->GetScreen(); RefreshModelView(); ResolveTileKeys(); m_commutator->PostMessage(ThreadsCommutator::ResourceUploadThread, dp::MovePointer(new UpdateReadManagerMessage(m_view, m_tiles))); break; } case Message::InvalidateRect: { InvalidateRectMessage * m = df::CastMessage(message); set keyStorage; ResolveTileKeys(keyStorage, m->GetRect()); InvalidateRenderGroups(keyStorage); Message * msgToBackend = new InvalidateReadManagerRectMessage(keyStorage); m_commutator->PostMessage(ThreadsCommutator::ResourceUploadThread, dp::MovePointer(msgToBackend)); break; } default: ASSERT(false, ()); } } void FrontendRenderer::RenderScene() { #ifdef DRAW_INFO BeforeDrawFrame(); #endif RenderBucketComparator comparator(GetTileKeyStorage()); sort(m_renderGroups.begin(), m_renderGroups.end(), bind(&RenderBucketComparator::operator (), &comparator, _1, _2)); m_overlayTree.StartOverlayPlacing(m_view); size_t eraseCount = 0; for (size_t i = 0; i < m_renderGroups.size(); ++i) { RenderGroup * group = m_renderGroups[i]; if (group->IsEmpty()) continue; if (group->IsPendingOnDelete()) { delete group; ++eraseCount; continue; } switch (group->GetState().GetDepthLayer()) { case dp::GLState::OverlayLayer: group->CollectOverlay(dp::MakeStackRefPointer(&m_overlayTree)); break; case dp::GLState::DynamicGeometry: group->Update(m_view); break; default: break; } } m_overlayTree.EndOverlayPlacing(); m_renderGroups.resize(m_renderGroups.size() - eraseCount); m_viewport.Apply(); GLFunctions::glEnable(gl_const::GLDepthTest); GLFunctions::glClearColor(0.93f, 0.93f, 0.86f, 1.f); GLFunctions::glClearDepthValue(1.0); GLFunctions::glDepthFunc(gl_const::GLLessOrEqual); GLFunctions::glDepthMask(true); GLFunctions::glClear(); dp::GLState::DepthLayer prevLayer = dp::GLState::GeometryLayer; for (size_t i = 0; i < m_renderGroups.size(); ++i) { RenderGroup * group = m_renderGroups[i]; dp::GLState const & state = group->GetState(); dp::GLState::DepthLayer layer = state.GetDepthLayer(); if (prevLayer != layer && layer == dp::GLState::OverlayLayer) GLFunctions::glClearDepth(); prevLayer = layer; ASSERT_LESS_OR_EQUAL(prevLayer, layer, ()); dp::RefPointer program = m_gpuProgramManager->GetProgram(state.GetProgramIndex()); program->Bind(); ApplyUniforms(m_generalUniforms, program); ApplyState(state, program, m_textureController.GetRefPointer()); group->Render(m_view); } #ifdef DRAW_INFO AfterDrawFrame(); #endif } void FrontendRenderer::RefreshProjection() { float m[4*4]; OrthoMatrix(m, 0.0f, m_viewport.GetWidth(), m_viewport.GetHeight(), 0.0f, -20000.0f, 20000.0f); m_generalUniforms.SetMatrix4x4Value("projection", m); } void FrontendRenderer::RefreshModelView() { ScreenBase::MatrixT const & m = m_view.GtoPMatrix(); math::Matrix mv; /// preparing ModelView matrix mv(0, 0) = m(0, 0); mv(0, 1) = m(1, 0); mv(0, 2) = 0; mv(0, 3) = m(2, 0); mv(1, 0) = m(0, 1); mv(1, 1) = m(1, 1); mv(1, 2) = 0; mv(1, 3) = m(2, 1); mv(2, 0) = 0; mv(2, 1) = 0; mv(2, 2) = 1; mv(2, 3) = 0; mv(3, 0) = m(0, 2); mv(3, 1) = m(1, 2); mv(3, 2) = 0; mv(3, 3) = m(2, 2); m_generalUniforms.SetMatrix4x4Value("modelView", mv.m_data); } void FrontendRenderer::ResolveTileKeys() { ResolveTileKeys(GetTileKeyStorage(), df::GetTileScaleBase(m_view)); } void FrontendRenderer::ResolveTileKeys(set & keyStorage, m2::RectD const & rect) { ResolveTileKeys(keyStorage, df::GetTileScaleBase(rect)); } void FrontendRenderer::ResolveTileKeys(set & keyStorage, int tileScale) { // equal for x and y double const range = MercatorBounds::maxX - MercatorBounds::minX; double const rectSize = range / (1 << tileScale); m2::RectD const & clipRect = m_view.ClipRect(); int const minTileX = static_cast(floor(clipRect.minX() / rectSize)); int const maxTileX = static_cast(ceil(clipRect.maxX() / rectSize)); int const minTileY = static_cast(floor(clipRect.minY() / rectSize)); int const maxTileY = static_cast(ceil(clipRect.maxY() / rectSize)); keyStorage.clear(); for (int tileY = minTileY; tileY < maxTileY; ++tileY) { for (int tileX = minTileX; tileX < maxTileX; ++tileX) { TileKey key(tileX, tileY, tileScale); if (clipRect.IsIntersect(key.GetGlobalRect())) keyStorage.insert(key); } } } void FrontendRenderer::InvalidateRenderGroups(set & keyStorage) { for (size_t i = 0; i < m_renderGroups.size(); ++i) { RenderGroup * group = m_renderGroups[i]; if (keyStorage.find(group->GetTileKey()) != keyStorage.end()) group->DeleteLater(); } } set & FrontendRenderer::GetTileKeyStorage() { return m_tiles; } void FrontendRenderer::StartThread() { m_selfThread.Create(this); } void FrontendRenderer::StopThread() { IRoutine::Cancel(); CloseQueue(); m_selfThread.Join(); } void FrontendRenderer::ThreadMain() { dp::OGLContext * context = m_contextFactory->getDrawContext(); context->makeCurrent(); my::Timer timer; //double processingTime = InitAvarageTimePerMessage; // By init we think that one message processed by 1ms timer.Reset(); while (!IsCancelled()) { context->setDefaultFramebuffer(); RenderScene(); double availableTime = VSyncInterval - (timer.ElapsedSeconds() /*+ avarageMessageTime*/); if (availableTime < 0.0) availableTime = 0.01; while (availableTime > 0) { ProcessSingleMessage(availableTime * 1000.0); availableTime = VSyncInterval - (timer.ElapsedSeconds() /*+ avarageMessageTime*/); //messageCount++; } //processingTime = (timer.ElapsedSeconds() - processingTime) / messageCount; context->present(); timer.Reset(); } ReleaseResources(); } void FrontendRenderer::ReleaseResources() { DeleteRenderData(); m_gpuProgramManager.Destroy(); m_textureController.Destroy(); } void FrontendRenderer::Do() { ThreadMain(); } void FrontendRenderer::DeleteRenderData() { (void)GetRangeDeletor(m_renderGroups, DeleteFunctor())(); } } // namespace df