diff --git a/init.lua b/init.lua index 5f57a19ac..e398b2bc0 100644 --- a/init.lua +++ b/init.lua @@ -8,7 +8,7 @@ Services = { g_app.setName("OTClient - Redemption"); g_app.setCompactName("otclient"); -g_app.setOrganizationName("otbr"); +g_app.setOrganizationName("otcr"); g_app.hasUpdater = function() return (Services.updater and Services.updater ~= "" and g_modules.getModule("updater")) diff --git a/modules/game_battle/battle.lua b/modules/game_battle/battle.lua index 834c91f6c..eea017d72 100644 --- a/modules/game_battle/battle.lua +++ b/modules/game_battle/battle.lua @@ -1,5 +1,5 @@ -- Global Tables -local binaryTree = {} -- BST +local binaryTree = {} -- BST local battleButtons = {} -- map of creature id -- Global variables that will inherit from init @@ -64,7 +64,7 @@ end function init() -- Initiating the module (load) g_ui.importStyle('battlebutton') battleButton = modules.client_topmenu.addRightGameToggleButton('battleButton', tr('Battle') .. ' (Ctrl+B)', - '/images/topbuttons/battle', toggle) + '/images/topbuttons/battle', toggle) battleButton:setOn(true) battleWindow = g_ui.loadUI('battle') @@ -88,14 +88,14 @@ function init() -- Initiating the module (load) end -- Adding Filter options - local options = {'hidePlayers', 'hideNPCs', 'hideMonsters', 'hideSkulls', 'hideParty'} + local options = { 'hidePlayers', 'hideNPCs', 'hideMonsters', 'hideSkulls', 'hideParty' } for i, v in ipairs(options) do hideButtons[v] = battleWindow:recursiveGetChildById(v) end -- Adding SortType and SortOrder options - local sortTypeOptions = {'Name', 'Distance', 'Age', 'Health'} - local sortOrderOptions = {'Asc.', 'Desc.'} + local sortTypeOptions = { 'Name', 'Distance', 'Age', 'Health' } + local sortOrderOptions = { 'Asc.', 'Desc.' } local sortTypeBox = battleWindow:recursiveGetChildById('sortTypeBox') for i, v in ipairs(sortTypeOptions) do @@ -134,7 +134,6 @@ end -- Binary Search, Insertion and Resort functions local function debugTables(sortType) -- Print both battlebutton and binarytree tables - local function getInfo(v, sortType) local returnedInfo = v.id if sortType then @@ -464,10 +463,14 @@ end local function canBeSeen(creature) return creature and creature:canBeSeen() and creature:getPosition() and - modules.game_interface.getMapPanel():isInRange(creature:getPosition()) + modules.game_interface.getMapPanel():isInRange(creature:getPosition()) end local function getDistanceBetween(p1, p2) -- Calculate distance + if p2 == nil then + p2 = { x = 0, y = 0 } + end + local xd = math.abs(p1.x - p2.x); local yd = math.abs(p1.y - p2.y); @@ -622,8 +625,8 @@ function removeCreature(creature, all) -- Remove a single creature or all msg = msg .. p end assert(index ~= nil, - 'Not able to remove creature: id ' .. creatureId .. ' not found in binary search using ' .. sortType .. - ' to find value ' .. msg .. '.') + 'Not able to remove creature: id ' .. creatureId .. ' not found in binary search using ' .. sortType .. + ' to find value ' .. msg .. '.') end end return false @@ -688,10 +691,8 @@ function attackNext(previous) if battleButton.isTarget then foundTarget = true - elseif foundTarget and not nextElement then nextElement = battleButton - elseif not foundTarget then prevElement = battleButton end @@ -712,7 +713,6 @@ function attackNext(previous) g_game.attack(firstElement.creature) end end - elseif firstElement then g_game.attack(firstElement.creature) else @@ -896,9 +896,9 @@ function onCreaturePositionChange(creature, newPos, oldPos) -- Update battleButt correctBattleButtons() else assert(index ~= nil, - 'Not able to update Position Change. Creature: ' .. creature:getName() .. ' id ' .. - creatureId .. ' not found in binary search using ' .. sortType .. - ' to find value ' .. oldDistance .. '.\n') + 'Not able to update Position Change. Creature: ' .. creature:getName() .. ' id ' .. + creatureId .. ' not found in binary search using ' .. sortType .. + ' to find value ' .. oldDistance .. '.\n') end end end @@ -952,11 +952,10 @@ function onCreatureHealthPercentChange(creature, healthPercent, oldHealthPercent correctBattleButtons() else assert(index ~= nil, - 'Not able to update HealthPercent Change. Creature: id ' .. creatureId .. - ' not found in binary search using ' .. sortType .. ' to find value ' .. oldHealthPercent .. - '.') + 'Not able to update HealthPercent Change. Creature: id ' .. creatureId .. + ' not found in binary search using ' .. sortType .. ' to find value ' .. oldHealthPercent .. + '.') end - end battleButton:setLifeBarPercent(healthPercent) end @@ -986,7 +985,7 @@ function onBattleButtonMouseRelease(self, mousePosition, mouseButton) -- Interac end if ((g_mouse.isPressed(MouseLeftButton) and mouseButton == MouseRightButton) or - (g_mouse.isPressed(MouseRightButton) and mouseButton == MouseLeftButton)) then + (g_mouse.isPressed(MouseRightButton) and mouseButton == MouseLeftButton)) then mouseWidget.cancelNextRelease = true g_game.look(self.creature, true) return true @@ -1080,5 +1079,4 @@ function terminate() -- Terminating the Module (unload) onGameStart = onGameStart }) disconnecting() - end diff --git a/modules/game_features/features.lua b/modules/game_features/features.lua index 49fce1398..a7e0236c8 100644 --- a/modules/game_features/features.lua +++ b/modules/game_features/features.lua @@ -1,7 +1,8 @@ controller = Controller:new() controller:registerEvents(g_game, { onClientVersionChange = function(version) - g_game.enableFeature(GameFormatCreatureName); + -- g_game.enableFeature(GameKeepUnawareTiles) + g_game.enableFeature(GameFormatCreatureName) if version >= 750 then g_game.enableFeature(GameSoul); diff --git a/src/client/lightview.cpp b/src/client/lightview.cpp index e8d135c9f..63a11f6ec 100644 --- a/src/client/lightview.cpp +++ b/src/client/lightview.cpp @@ -34,26 +34,13 @@ LightView::LightView(const Size& size, const uint16_t tileSize) : m_pool(g_drawP m_texture = std::make_shared(size); m_texture->setSmooth(true); }); - - g_drawPool.use(DrawPoolType::LIGHT); - g_drawPool.addAction([this] { - { - std::scoped_lock l(m_pool->getMutex()); - m_texture->updatePixels(m_pixels.data()); - } - g_painter->resetColor(); - g_painter->resetTransformMatrix(); - g_painter->setTexture(m_texture.get()); - g_painter->setCompositionMode(CompositionMode::MULTIPLY); - g_painter->drawCoords(m_coords); - }); } void LightView::resize(const Size& size, const uint16_t tileSize) { if (!m_texture || m_mapSize == size && m_tileSize == tileSize) return; - std::scoped_lock l(m_pool->getMutex()); + std::scoped_lock l(m_pool->getMutex(), m_pool->getMutexPreDraw()); m_mapSize = size; m_tileSize = tileSize; @@ -71,10 +58,10 @@ void LightView::resize(const Size& size, const uint16_t tileSize) { void LightView::addLightSource(const Point& pos, const Light& light, float brightness) { - if (light.intensity == 0) + if (!isDark() || light.intensity == 0) return; - auto& lightData = m_lightData[m_currentLightData]; + auto& lightData = m_lightData[0]; if (!lightData.lights.empty()) { auto& prevLight = lightData.lights.back(); @@ -95,7 +82,7 @@ void LightView::addLightSource(const Point& pos, const Light& light, float brigh void LightView::resetShade(const Point& pos) { - auto& lightData = m_lightData[m_currentLightData]; + auto& lightData = m_lightData[0]; size_t index = (pos.y / m_tileSize) * m_mapSize.width() + (pos.x / m_tileSize); if (index >= lightData.tiles.size()) return; @@ -104,20 +91,34 @@ void LightView::resetShade(const Point& pos) void LightView::draw(const Rect& dest, const Rect& src) { - updateCoords(dest, src); - if (m_updatedHash != m_hash) { m_hash = m_updatedHash; m_updatedHash = 0; - std::scoped_lock l(m_pool->getMutex()); - if (++m_currentLightData > 1) m_currentLightData = 0; + std::scoped_lock l(m_pool->getMutexPreDraw()); + std::swap(m_lightData[0], m_lightData[1]); g_asyncDispatcher.dispatch([this] { updatePixels(); }); } - auto& lightData = m_lightData[m_currentLightData]; + g_drawPool.preDraw(DrawPoolType::LIGHT, [this, &dest, &src] { + g_drawPool.addAction([=, this] { + { + std::scoped_lock l(m_pool->getMutexPreDraw()); + m_texture->updatePixels(m_pixels.data()); + } + + updateCoords(dest, src); + g_painter->setCompositionMode(CompositionMode::MULTIPLY); + g_painter->resetTransformMatrix(); + g_painter->resetColor(); + g_painter->setTexture(m_texture.get()); + g_painter->drawCoords(m_coords); + }); + }); + + auto& lightData = m_lightData[0]; lightData.lights.clear(); lightData.tiles.assign(m_mapSize.area(), {}); } @@ -139,9 +140,9 @@ void LightView::updateCoords(const Rect& dest, const Rect& src) { } void LightView::updatePixels() { - std::scoped_lock l(m_pool->getMutex()); + std::scoped_lock l(m_pool->getMutexPreDraw()); - const auto& lightData = m_lightData[m_currentLightData ? 0 : 1]; + const auto& lightData = m_lightData[1]; const size_t lightSize = lightData.lights.size(); diff --git a/src/client/lightview.h b/src/client/lightview.h index fb7ae444c..3fe527e34 100644 --- a/src/client/lightview.h +++ b/src/client/lightview.h @@ -82,6 +82,5 @@ private: CoordsBuffer m_coords; TexturePtr m_texture; LightData m_lightData[2]; - std::atomic_uint8_t m_currentLightData{ 0 }; std::vector m_pixels; }; diff --git a/src/client/map.cpp b/src/client/map.cpp index a6202b8c4..3279fa4b1 100644 --- a/src/client/map.cpp +++ b/src/client/map.cpp @@ -152,51 +152,55 @@ void Map::addStaticText(const StaticTextPtr& txt, const Position& pos) { if (!g_app.isDrawingTexts()) return; - for (const auto& other : m_staticTexts) { - // try to combine messages - if (other->getPosition() == pos && other->addMessage(txt->getName(), txt->getMessageMode(), txt->getFirstMessage())) { - return; + g_textDispatcher.addEvent([=, this] { + for (const auto& other : m_staticTexts) { + // try to combine messages + if (other->getPosition() == pos && other->addMessage(txt->getName(), txt->getMessageMode(), txt->getFirstMessage())) { + return; + } } - } - txt->setPosition(pos); - m_staticTexts.emplace_back(txt); + txt->setPosition(pos); + m_staticTexts.emplace_back(txt); + }); } void Map::addAnimatedText(const AnimatedTextPtr& txt, const Position& pos) { if (!g_app.isDrawingTexts()) return; - // this code will stack animated texts of the same color - AnimatedTextPtr prevAnimatedText; + g_textDispatcher.addEvent([=, this] { + // this code will stack animated texts of the same color + AnimatedTextPtr prevAnimatedText; - bool merged = false; - for (const auto& other : m_animatedTexts) { - if (other->getPosition() == pos) { - prevAnimatedText = other; - if (other->merge(txt)) { - merged = true; - break; + bool merged = false; + for (const auto& other : m_animatedTexts) { + if (other->getPosition() == pos) { + prevAnimatedText = other; + if (other->merge(txt)) { + merged = true; + break; + } } } - } - if (!merged) { - if (prevAnimatedText) { - Point offset = prevAnimatedText->getOffset(); - if (const float t = prevAnimatedText->getTimer().ticksElapsed(); - t < g_gameConfig.getAnimatedTextDuration() / 4.0) { // didnt move 12 pixels - const int32_t y = 12 - 48 * t / static_cast(g_gameConfig.getAnimatedTextDuration()); - offset += Point(0, y); + if (!merged) { + if (prevAnimatedText) { + Point offset = prevAnimatedText->getOffset(); + if (const float t = prevAnimatedText->getTimer().ticksElapsed(); + t < g_gameConfig.getAnimatedTextDuration() / 4.0) { // didnt move 12 pixels + const int32_t y = 12 - 48 * t / static_cast(g_gameConfig.getAnimatedTextDuration()); + offset += Point(0, y); + } + offset.y = std::min(offset.y, 12); + txt->setOffset(offset); } - offset.y = std::min(offset.y, 12); - txt->setOffset(offset); + m_animatedTexts.emplace_back(txt); } - m_animatedTexts.emplace_back(txt); - } - txt->setPosition(pos); - txt->onAppear(); + txt->setPosition(pos); + txt->onAppear(); + }); } ThingPtr Map::getThing(const Position& pos, int16_t stackPos) diff --git a/src/client/mapview.cpp b/src/client/mapview.cpp index 4c7e610de..500dccfb1 100644 --- a/src/client/mapview.cpp +++ b/src/client/mapview.cpp @@ -46,47 +46,6 @@ MapView::MapView() : m_pool(g_drawPool.get(DrawPoolType::MAP)), m_lightView(std: { m_floors.resize(g_gameConfig.getMapMaxZ() + 1); - m_pool->onBeforeDraw([this] { - float fadeOpacity = 1.f; - if (!m_shaderSwitchDone && m_fadeOutTime > 0) { - fadeOpacity = 1.f - (m_fadeTimer.timeElapsed() / m_fadeOutTime); - if (fadeOpacity < 0.f) { - m_shader = m_nextShader; - m_nextShader = nullptr; - m_shaderSwitchDone = true; - m_fadeTimer.restart(); - } - } - - if (m_shaderSwitchDone && m_shader && m_fadeInTime > 0) - fadeOpacity = std::min(m_fadeTimer.timeElapsed() / m_fadeInTime, 1.f); - - if (m_shader) { - const auto& center = m_posInfo.srcRect.center(); - const auto& globalCoord = Point(m_posInfo.camera.x - m_drawDimension.width() / 2, -(m_posInfo.camera.y - m_drawDimension.height() / 2)) * m_tileSize; - - m_shader->bind(); - m_shader->setUniformValue(ShaderManager::MAP_CENTER_COORD, center.x / static_cast(m_rectDimension.width()), 1.f - center.y / static_cast(m_rectDimension.height())); - m_shader->setUniformValue(ShaderManager::MAP_GLOBAL_COORD, globalCoord.x / static_cast(m_rectDimension.height()), globalCoord.y / static_cast(m_rectDimension.height())); - m_shader->setUniformValue(ShaderManager::MAP_ZOOM, m_pool->getScaleFactor()); - - Point last = transformPositionTo2D(m_posInfo.camera, m_shaderPosition); - //Reverse vertical axis. - last.y = -last.y; - - m_shader->setUniformValue(ShaderManager::MAP_WALKOFFSET, last.x / static_cast(m_rectDimension.width()), last.y / static_cast(m_rectDimension.height())); - - g_painter->setShaderProgram(m_shader); - } - - g_painter->setOpacity(fadeOpacity); - }); - - m_pool->onAfterDraw([] { - g_painter->resetShaderProgram(); - g_painter->resetOpacity(); - }); - setVisibleDimension(Size(15, 11)); } @@ -98,7 +57,52 @@ MapView::~MapView() m_lightView = nullptr; } -void MapView::draw() +void MapView::registerEvents() { + g_drawPool.addAction([this, camera = m_posInfo.camera, srcRect = m_posInfo.srcRect] { + m_pool->onBeforeDraw([=, this] { + float fadeOpacity = 1.f; + if (!m_shaderSwitchDone && m_fadeOutTime > 0) { + fadeOpacity = 1.f - (m_fadeTimer.timeElapsed() / m_fadeOutTime); + if (fadeOpacity < 0.f) { + m_shader = m_nextShader; + m_nextShader = nullptr; + m_shaderSwitchDone = true; + m_fadeTimer.restart(); + } + } + + if (m_shaderSwitchDone && m_shader && m_fadeInTime > 0) + fadeOpacity = std::min(m_fadeTimer.timeElapsed() / m_fadeInTime, 1.f); + + if (m_shader) { + const auto& center = srcRect.center(); + const auto& globalCoord = Point(camera.x - m_drawDimension.width() / 2, -(camera.y - m_drawDimension.height() / 2)) * m_tileSize; + + m_shader->bind(); + m_shader->setUniformValue(ShaderManager::MAP_CENTER_COORD, center.x / static_cast(m_rectDimension.width()), 1.f - center.y / static_cast(m_rectDimension.height())); + m_shader->setUniformValue(ShaderManager::MAP_GLOBAL_COORD, globalCoord.x / static_cast(m_rectDimension.height()), globalCoord.y / static_cast(m_rectDimension.height())); + m_shader->setUniformValue(ShaderManager::MAP_ZOOM, m_pool->getScaleFactor()); + + Point last = transformPositionTo2D(camera, m_shaderPosition); + //Reverse vertical axis. + last.y = -last.y; + + m_shader->setUniformValue(ShaderManager::MAP_WALKOFFSET, last.x / static_cast(m_rectDimension.width()), last.y / static_cast(m_rectDimension.height())); + + g_painter->setShaderProgram(m_shader); + } + + g_painter->setOpacity(fadeOpacity); + }); + + m_pool->onAfterDraw([] { + g_painter->resetShaderProgram(); + g_painter->resetOpacity(); + }); + }); +} + +void MapView::draw(const Rect& rect) { // update visible tiles cache when needed if (m_updateVisibleTiles) @@ -125,91 +129,92 @@ void MapView::draw() void MapView::drawFloor() { - g_drawPool.use(DrawPoolType::MAP, m_posInfo.rect, m_posInfo.srcRect, Color::black); - { - const auto& cameraPosition = m_posInfo.camera; - const auto& lightView = isDrawingLights() ? m_lightView.get() : nullptr; + const auto& cameraPosition = m_posInfo.camera; + const auto& lightView = isDrawingLights() ? m_lightView.get() : nullptr; - uint32_t flags = Otc::DrawThings; - if (lightView) flags |= Otc::DrawLights; - if (m_drawNames) { flags |= Otc::DrawNames; } - if (m_drawHealthBars) { flags |= Otc::DrawBars; } - if (m_drawManaBar) { flags |= Otc::DrawManaBar; } + uint32_t flags = Otc::DrawThings; + if (lightView) flags |= Otc::DrawLights; + if (m_drawNames) { flags |= Otc::DrawNames; } + if (m_drawHealthBars) { flags |= Otc::DrawBars; } + if (m_drawManaBar) { flags |= Otc::DrawManaBar; } - for (int_fast8_t z = m_floorMax; z >= m_floorMin; --z) { - const float fadeLevel = getFadeLevel(z); - if (fadeLevel == 0.f) break; - if (fadeLevel < .99f) - g_drawPool.setOpacity(fadeLevel); + for (int_fast8_t z = m_floorMax; z >= m_floorMin; --z) { + const float fadeLevel = getFadeLevel(z); + if (fadeLevel == 0.f) break; + if (fadeLevel < .99f) + g_drawPool.setOpacity(fadeLevel); - Position _camera = cameraPosition; - const bool alwaysTransparent = m_floorViewMode == ALWAYS_WITH_TRANSPARENCY && z < m_cachedFirstVisibleFloor && _camera.coveredUp(cameraPosition.z - z); + Position _camera = cameraPosition; + const bool alwaysTransparent = m_floorViewMode == ALWAYS_WITH_TRANSPARENCY && z < m_cachedFirstVisibleFloor && _camera.coveredUp(cameraPosition.z - z); - const auto& map = m_floors[z].cachedVisibleTiles; + const auto& map = m_floors[z].cachedVisibleTiles; - if (m_fadeType != FadeType::OUT$ || fadeLevel == 1.f) { - for (const auto& tile : map.shades) { - if (alwaysTransparent && tile->getPosition().isInRange(_camera, g_gameConfig.getTileTransparentFloorViewRange(), g_gameConfig.getTileTransparentFloorViewRange(), true)) - continue; - - m_lightView->resetShade(transformPositionTo2D(tile->getPosition(), cameraPosition)); - } - } - - for (const auto& tile : map.tiles) { - uint32_t tileFlags = flags; - - if (!m_drawViewportEdge && !tile->canRender(tileFlags, cameraPosition, m_viewport)) + if (m_fadeType != FadeType::OUT$ || fadeLevel == 1.f) { + for (const auto& tile : map.shades) { + if (alwaysTransparent && tile->getPosition().isInRange(_camera, g_gameConfig.getTileTransparentFloorViewRange(), g_gameConfig.getTileTransparentFloorViewRange(), true)) continue; - bool isCovered = false; - if (tile->hasCreature()) { - isCovered = tile->isCovered(m_cachedFirstVisibleFloor); - } + m_lightView->resetShade(transformPositionTo2D(tile->getPosition(), cameraPosition)); + } + } - if (alwaysTransparent) { - const bool inRange = tile->getPosition().isInRange(_camera, g_gameConfig.getTileTransparentFloorViewRange(), g_gameConfig.getTileTransparentFloorViewRange(), true); - isCovered = isCovered && !inRange; + for (const auto& tile : map.tiles) { + uint32_t tileFlags = flags; - g_drawPool.setOpacity(inRange ? .16 : .7); - } + if (!m_drawViewportEdge && !tile->canRender(tileFlags, cameraPosition, m_viewport)) + continue; - tile->draw(transformPositionTo2D(tile->getPosition(), cameraPosition), m_posInfo, tileFlags, isCovered, lightView); - - if (alwaysTransparent) - g_drawPool.resetOpacity(); + bool isCovered = false; + if (tile->hasCreature()) { + isCovered = tile->isCovered(m_cachedFirstVisibleFloor); } - for (const auto& missile : g_map.getFloorMissiles(z)) - missile->draw(transformPositionTo2D(missile->getPosition(), cameraPosition), true, lightView); + if (alwaysTransparent) { + const bool inRange = tile->getPosition().isInRange(_camera, g_gameConfig.getTileTransparentFloorViewRange(), g_gameConfig.getTileTransparentFloorViewRange(), true); + isCovered = isCovered && !inRange; - if (m_shadowFloorIntensity > 0 && z == cameraPosition.z + 1) { - g_drawPool.setOpacity(m_shadowFloorIntensity, true); - g_drawPool.addFilledRect(m_rectDimension, Color::black, m_shadowConductor); + g_drawPool.setOpacity(inRange ? .16 : .7); } - if (canFloorFade()) + tile->draw(transformPositionTo2D(tile->getPosition(), cameraPosition), m_posInfo, tileFlags, isCovered, lightView); + + if (alwaysTransparent) g_drawPool.resetOpacity(); - - g_drawPool.flush(); } - if (m_posInfo.rect.contains(g_window.getMousePosition())) { - if (m_crosshairTexture && m_mousePosition.isValid()) { - const auto& point = transformPositionTo2D(m_mousePosition, cameraPosition); - const auto& crosshairRect = Rect(point, m_tileSize, m_tileSize); - g_drawPool.addTexturedRect(crosshairRect, m_crosshairTexture); - } - } else if (m_lastHighlightTile) { - m_mousePosition = {}; // Invalidate mousePosition - destroyHighlightTile(); + for (const auto& missile : g_map.getFloorMissiles(z)) + missile->draw(transformPositionTo2D(missile->getPosition(), cameraPosition), true, lightView); + + if (m_shadowFloorIntensity > 0 && z == cameraPosition.z + 1) { + g_drawPool.setOpacity(m_shadowFloorIntensity, true); + g_drawPool.addFilledRect(m_rectDimension, Color::black, m_shadowConductor); } + + if (canFloorFade()) + g_drawPool.resetOpacity(); + + g_drawPool.flush(); + } + + if (m_posInfo.rect.contains(g_window.getMousePosition())) { + if (m_crosshairTexture && m_mousePosition.isValid()) { + const auto& point = transformPositionTo2D(m_mousePosition, cameraPosition); + const auto& crosshairRect = Rect(point, m_tileSize, m_tileSize); + g_drawPool.addTexturedRect(crosshairRect, m_crosshairTexture); + } + } else if (m_lastHighlightTile) { + m_mousePosition = {}; // Invalidate mousePosition + destroyHighlightTile(); } } -void MapView::drawText() +void MapView::drawForeground(const Rect& rect) { - g_drawPool.use(DrawPoolType::TEXT); + const auto& camera = getCameraPosition(); + const auto& srcRect = calcFramebufferSource(rect.size()); + const auto& drawOffset = srcRect.topLeft(); + const auto& horizontalStretchFactor = rect.width() / static_cast(srcRect.width()); + const auto& verticalStretchFactor = rect.height() / static_cast(srcRect.height()); g_drawPool.scale(g_app.getStaticTextScale()); for (const auto& staticText : g_map.getStaticTexts()) { @@ -217,28 +222,43 @@ void MapView::drawText() continue; const auto& pos = staticText->getPosition(); - if (pos.z != m_posInfo.camera.z && staticText->getMessageMode() == Otc::MessageNone) + if (pos.z != camera.z && staticText->getMessageMode() == Otc::MessageNone) continue; - Point p = transformPositionTo2D(pos, m_posInfo.camera) - m_posInfo.drawOffset; - p.x *= m_posInfo.horizontalStretchFactor; - p.y *= m_posInfo.verticalStretchFactor; - p += m_posInfo.rect.topLeft(); - staticText->drawText(p.scale(g_app.getStaticTextScale()), m_posInfo.rect); + Point p = transformPositionTo2D(pos, camera) - drawOffset; + p.x *= horizontalStretchFactor; + p.y *= verticalStretchFactor; + p += rect.topLeft(); + staticText->drawText(p.scale(g_app.getStaticTextScale()), rect); } g_drawPool.scale(g_app.getAnimatedTextScale()); for (const auto& animatedText : g_map.getAnimatedTexts()) { const auto& pos = animatedText->getPosition(); - if (pos.z != m_posInfo.camera.z) + if (pos.z != camera.z) continue; - auto p = transformPositionTo2D(pos, m_posInfo.camera) - m_posInfo.drawOffset; - p.x *= m_posInfo.horizontalStretchFactor; - p.y *= m_posInfo.verticalStretchFactor; - p += m_posInfo.rect.topLeft(); - animatedText->drawText(p, m_posInfo.rect); + auto p = transformPositionTo2D(pos, camera) - drawOffset; + p.x *= horizontalStretchFactor; + p.y *= verticalStretchFactor; + p += rect.topLeft(); + animatedText->drawText(p, rect); + } + + g_drawPool.scale(1.f); + for (const auto& tile : m_foregroundTiles) { + const auto& dest = transformPositionTo2D(tile->getPosition(), camera); +#ifndef BOT_PROTECTION + Point p = dest - drawOffset; + p.x *= horizontalStretchFactor; + p.y *= verticalStretchFactor; + p += rect.topLeft(); + p.y += 5; + + tile->drawTexts(p); +#endif + tile->drawWidget(dest, rect); } } @@ -410,6 +430,7 @@ void MapView::updateGeometry(const Size& visibleDimension) m_lightView->resize(lightSize, tileSize); } + g_mainDispatcher.addEvent([this, bufferSize]() { m_pool->getFrameBuffer()->resize(bufferSize); }); @@ -456,15 +477,15 @@ void MapView::updateLight() void MapView::onTileUpdate(const Position& pos, const ThingPtr& thing, const Otc::Operation op) { - if (m_lastHighlightTile && m_lastHighlightTile->getPosition() == pos && op == Otc::OPERATION_CLEAN) - m_lastHighlightTile = nullptr; + if (thing && thing->isOpaque() && op == Otc::OPERATION_REMOVE) + m_resetCoveredCache = true; - if (thing) { - if (thing->isOpaque() && op == Otc::OPERATION_REMOVE) - m_resetCoveredCache = true; + if (op == Otc::OPERATION_CLEAN) { + if (m_lastHighlightTile && m_lastHighlightTile->getPosition() == pos) + m_lastHighlightTile = nullptr; + + requestUpdateVisibleTiles(); } - - requestUpdateVisibleTiles(); } void MapView::onFadeInFinished() @@ -548,8 +569,7 @@ void MapView::setAntiAliasingMode(const AntialiasingMode mode) m_antiAliasingMode = mode; g_mainDispatcher.addEvent([=, this]() { - g_drawPool.get(DrawPoolType::MAP)->getFrameBuffer() - ->setSmooth(mode != ANTIALIASING_DISABLED); + m_pool->getFrameBuffer()->setSmooth(mode != ANTIALIASING_DISABLED); }); updateGeometry(m_visibleDimension); @@ -864,4 +884,19 @@ void MapView::destroyHighlightTile() { m_lastHighlightTile->unselect(); m_lastHighlightTile = nullptr; } +} + +void MapView::addForegroundTile(const TilePtr& tile) { + std::scoped_lock l(g_drawPool.get(DrawPoolType::FOREGROUND_MAP)->getMutex()); + + if (std::find(m_foregroundTiles.begin(), m_foregroundTiles.end(), tile) == m_foregroundTiles.end()) + m_foregroundTiles.emplace_back(tile); +} +void MapView::removeForegroundTile(const TilePtr& tile) { + std::scoped_lock l(g_drawPool.get(DrawPoolType::FOREGROUND_MAP)->getMutex()); + const auto it = std::find(m_foregroundTiles.begin(), m_foregroundTiles.end(), tile); + if (it == m_foregroundTiles.end()) + return; + + m_foregroundTiles.erase(it); } \ No newline at end of file diff --git a/src/client/mapview.h b/src/client/mapview.h index 02a29c6f7..c02c1680c 100644 --- a/src/client/mapview.h +++ b/src/client/mapview.h @@ -88,8 +88,8 @@ public: MapView(); ~MapView() override; - void draw(); - void drawText(); + void draw(const Rect& rect); + void drawForeground(const Rect& rect); // floor visibility related uint8_t getLockedFirstVisibleFloor() const { return m_lockedFirstVisibleFloor; } @@ -181,6 +181,9 @@ public: PainterShaderProgramPtr getNextShader() { return m_nextShader; } bool isSwitchingShader() { return !m_shaderSwitchDone; } + void addForegroundTile(const TilePtr& tile); + void removeForegroundTile(const TilePtr& tile); + protected: void onGlobalLightChange(const Light& light); void onFloorChange(uint8_t floor, uint8_t previousFloor); @@ -220,24 +223,25 @@ private: TexturePtr texture; }; + void updateHighlightTile(const Position& mousePos); + void destroyHighlightTile(); + + void updateLight(); + void updateViewportDirectionCache(); void updateGeometry(const Size& visibleDimension); void updateVisibleTiles(); void updateRect(const Rect& rect); + void updateViewport(const Otc::Direction dir = Otc::InvalidDirection) { m_viewport = m_viewPortDirection[dir]; } void requestUpdateVisibleTiles() { m_updateVisibleTiles = true; } void requestUpdateMapPosInfo() { m_updateMapPosInfo = true; } + void registerEvents(); + uint8_t calcFirstVisibleFloor(bool checkLimitsFloorsView) const; uint8_t calcLastVisibleFloor() const; - void updateLight(); - void updateViewportDirectionCache(); void drawFloor(); - void updateHighlightTile(const Position& mousePos); - void destroyHighlightTile(); - - void updateViewport(const Otc::Direction dir = Otc::InvalidDirection) { m_viewport = m_viewPortDirection[dir]; } - bool canFloorFade() const { return m_floorViewMode == FADE && m_floorFading; } float getFadeLevel(uint8_t z) const @@ -314,6 +318,7 @@ private: AntialiasingMode m_antiAliasingMode{ AntialiasingMode::ANTIALIASING_DISABLED }; std::vector m_floors; + std::vector m_foregroundTiles; PainterShaderProgramPtr m_shader; PainterShaderProgramPtr m_nextShader; diff --git a/src/client/tile.cpp b/src/client/tile.cpp index 37d8bf476..6df583fb3 100644 --- a/src/client/tile.cpp +++ b/src/client/tile.cpp @@ -146,12 +146,12 @@ void Tile::updateWidget(const Point& dest, const MapPosInfo& mapRect) m_widget->setRect(rect); } -void Tile::drawWidget(const Point& dest, const MapPosInfo& mapRect) +void Tile::drawWidget(const Point& dest, const Rect& rect) { if (!m_widget) return; - m_widget->draw(mapRect.rect, DrawPoolType::FOREGROUND); + m_widget->draw(rect, DrawPoolType::FOREGROUND); } void Tile::setWidget(const UIWidgetPtr& widget) { @@ -160,7 +160,7 @@ void Tile::setWidget(const UIWidgetPtr& widget) { m_widget = widget; m_widget->setClipping(true); g_dispatcher.scheduleEvent([tile = static_self_cast()] { - g_ui.getMapWidget()->addTile(tile); + g_ui.getMapWidget()->getMapView()->addForegroundTile(tile); }, g_game.getServerBeat()); } @@ -181,7 +181,7 @@ void Tile::clean() #endif )) { g_dispatcher.scheduleEvent([tile = static_self_cast()] { - g_ui.getMapWidget()->removeTile(tile); + g_ui.getMapWidget()->getMapView()->removeForegroundTile(tile); }, g_game.getServerBeat()); } @@ -849,24 +849,18 @@ bool Tile::canRender(uint32_t& flags, const Position& cameraPosition, const Awar } #ifndef BOT_PROTECTION -void Tile::drawTexts(const Point& dest, const MapPosInfo& mapRect) +void Tile::drawTexts(Point dest) { - Point p = dest - mapRect.drawOffset; - p.x *= mapRect.horizontalStretchFactor; - p.y *= mapRect.verticalStretchFactor; - p += mapRect.rect.topLeft(); - p.y += 5; - if (m_timerText && g_clock.millis() < m_timer) { if (m_text && m_text->hasText()) - p.y -= 8; + dest.y -= 8; m_timerText->setText(stdext::format("%.01f", (m_timer - g_clock.millis()) / 1000.)); - m_timerText->drawText(p, Rect(p.x - 64, p.y - 64, 128, 128)); - p.y += 16; + m_timerText->drawText(dest, Rect(dest.x - 64, dest.y - 64, 128, 128)); + dest.y += 16; } if (m_text && m_text->hasText()) { - m_text->drawText(p, Rect(p.x - 64, p.y - 64, 128, 128)); + m_text->drawText(dest, Rect(dest.x - 64, dest.y - 64, 128, 128)); } } @@ -875,7 +869,7 @@ void Tile::setText(const std::string& text, Color color) if (!m_text) { m_text = std::make_shared(); g_dispatcher.scheduleEvent([tile = static_self_cast()] { - g_ui.getMapWidget()->addTile(tile); + g_ui.getMapWidget()->getMapView()->addForegroundTile(tile); }, g_game.getServerBeat()); } @@ -898,7 +892,7 @@ void Tile::setTimer(int time, Color color) if (!m_timerText) { m_timerText = std::make_shared(); g_dispatcher.scheduleEvent([tile = static_self_cast()] { - g_ui.getMapWidget()->addTile(tile); + g_ui.getMapWidget()->getMapView()->addForegroundTile(tile); }, g_game.getServerBeat()); } diff --git a/src/client/tile.h b/src/client/tile.h index 72dd2a548..0a24310d8 100644 --- a/src/client/tile.h +++ b/src/client/tile.h @@ -119,7 +119,7 @@ public: ThingPtr getTopMultiUseThing(); bool hasWidget() const { return m_widget != nullptr; } - void drawWidget(const Point& dest, const MapPosInfo& mapRect); + void drawWidget(const Point& dest, const Rect& rect); void setWidget(const UIWidgetPtr& widget); UIWidgetPtr getWidget() { return m_widget; } void removeWidget(); @@ -212,7 +212,7 @@ public: bool checkForDetachableThing(); #ifndef BOT_PROTECTION - void drawTexts(const Point& dest, const MapPosInfo& mapRect); + void drawTexts(Point dest); void setText(const std::string& text, Color color); std::string getText(); void setTimer(int time, Color color); diff --git a/src/client/uimap.cpp b/src/client/uimap.cpp index e1ce9cc72..06dca2cb8 100644 --- a/src/client/uimap.cpp +++ b/src/client/uimap.cpp @@ -21,6 +21,7 @@ */ #include "uimap.h" +#include #include #include #include @@ -60,25 +61,19 @@ void UIMap::drawSelf(DrawPoolType drawPane) return; } - if (m_mapView && drawPane == DrawPoolType::FOREGROUND_TILE) { - g_drawPool.use(DrawPoolType::FOREGROUND_TILE); - for (const auto& tile : m_tiles) { - const auto& dest = m_mapView->transformPositionTo2D(tile->getPosition(), m_mapView->getCameraPosition()); -#ifndef BOT_PROTECTION - tile->drawTexts(dest, m_mapView->m_posInfo); -#endif - tile->drawWidget(dest, m_mapView->m_posInfo); - } - return; - } - - const auto& mapSize = g_app.isScaled() ? Rect(0, 0, g_graphics.getViewportSize()) : m_mapRect; - m_mapView->updateRect(mapSize); + const auto& mapRect = g_app.isScaled() ? Rect(0, 0, g_graphics.getViewportSize()) : m_mapRect; if (drawPane == DrawPoolType::MAP) { - m_mapView->draw(); - } else if (drawPane == DrawPoolType::TEXT) { - m_mapView->drawText(); + m_mapView->updateRect(mapRect); + g_drawPool.preDraw(drawPane, [this, &mapRect] { + m_mapView->registerEvents(); + m_mapView->draw(mapRect); + }, m_mapView->m_posInfo.rect, m_mapView->m_posInfo.srcRect, Color::black); + } else if (drawPane == DrawPoolType::FOREGROUND_MAP) { + g_textDispatcher.poll(); + g_drawPool.preDraw(drawPane, [this, &mapRect] { + m_mapView->drawForeground(mapRect); + }); } } @@ -212,19 +207,4 @@ void UIMap::updateMapSize() updateVisibleDimension(); } -void UIMap::addTile(const TilePtr& tile) { - std::scoped_lock l(g_drawPool.get(DrawPoolType::FOREGROUND)->getMutex()); - - if (std::ranges::find(m_tiles, tile) == m_tiles.end()) - m_tiles.emplace_back(tile); -} -void UIMap::removeTile(const TilePtr& tile) { - std::scoped_lock l(g_drawPool.get(DrawPoolType::FOREGROUND)->getMutex()); - const auto it = std::find(m_tiles.begin(), m_tiles.end(), tile); - if (it == m_tiles.end()) - return; - - m_tiles.erase(it); -} - /* vim: set ts=4 sw=4 et: */ \ No newline at end of file diff --git a/src/client/uimap.h b/src/client/uimap.h index 4f7e9a9ce..8da667214 100644 --- a/src/client/uimap.h +++ b/src/client/uimap.h @@ -93,11 +93,9 @@ public: void setDrawHighlightTarget(const bool enable) { m_mapView->setDrawHighlightTarget(enable); } void setAntiAliasingMode(const MapView::AntialiasingMode mode) { m_mapView->setAntiAliasingMode(mode); } void setFloorFading(const uint16_t v) { m_mapView->setFloorFading(v); } - - void addTile(const TilePtr& tile); - void removeTile(const TilePtr& tile); + MapViewPtr getMapView() const { return m_mapView; } void clearTiles() { - m_tiles.clear(); + m_mapView->m_foregroundTiles.clear(); } protected: @@ -109,7 +107,6 @@ private: void updateVisibleDimension(); void updateMapSize(); - std::vector m_tiles; MapViewPtr m_mapView; Rect m_mapRect; diff --git a/src/framework/core/adaptativeframecounter.cpp b/src/framework/core/adaptativeframecounter.cpp index 1e761aa8c..f4c09f357 100644 --- a/src/framework/core/adaptativeframecounter.cpp +++ b/src/framework/core/adaptativeframecounter.cpp @@ -24,9 +24,9 @@ #include #include -void AdaptativeFrameCounter::update() +bool AdaptativeFrameCounter::update() { - const uint8_t maxFps = m_targetFps == 0 ? m_maxFps : std::clamp(m_targetFps, 1, std::max(m_maxFps, m_targetFps)); + const auto maxFps = m_targetFps == 0 ? m_maxFps : std::clamp(m_targetFps, 1, std::max(m_maxFps, m_targetFps)); if (maxFps > 0) { const int32_t sleepPeriod = (getMaxPeriod(maxFps) - 1000) - m_timer.elapsed_micros(); if (sleepPeriod > 0) stdext::microsleep(sleepPeriod); @@ -37,15 +37,15 @@ void AdaptativeFrameCounter::update() ++m_fpsCount; if (m_fps == m_fpsCount) - return; + return false; const uint32_t tickCount = stdext::millis(); if (tickCount - m_interval <= 1000) - return; + return false; m_fps = m_fpsCount; m_fpsCount = 0; m_interval = tickCount; - g_dispatcher.addEvent([this] { g_lua.callGlobalField("g_app", "onFps", getFps()); }); + return true; } \ No newline at end of file diff --git a/src/framework/core/adaptativeframecounter.h b/src/framework/core/adaptativeframecounter.h index e4fa40876..82ba747c2 100644 --- a/src/framework/core/adaptativeframecounter.h +++ b/src/framework/core/adaptativeframecounter.h @@ -33,22 +33,31 @@ public: AdaptativeFrameCounter() : m_interval(stdext::millis()) { } void init() { m_timer.restart(); } - void update(); + bool update(); uint16_t getFps() const { return m_fps; } - uint8_t getMaxFps() const { return m_maxFps; } - uint8_t getTargetFps() const { return m_targetFps; } + uint16_t getMaxFps() const { return m_maxFps; } + uint16_t getTargetFps() const { return m_targetFps; } void setMaxFps(const uint16_t max) { m_maxFps = max; } - void setTargetFps(const uint16_t target) { m_targetFps = target; } + void setTargetFps(const uint16_t target) { if (m_targetFps != target) m_targetFps = target; } void resetTargetFps() { m_targetFps = 0; } + float getPercent() const { + const float maxFps = std::clamp(m_targetFps, 1, std::max(m_maxFps, m_targetFps)); + return ((maxFps - m_fps) / maxFps) * 100.f; + } + + float getFpsPercent(float percent) const { + return getFps() * (percent / 100); + } + private: uint32_t getMaxPeriod(uint16_t fps) const { return 1000000u / fps; } - uint8_t m_maxFps{}; - uint8_t m_targetFps{ 60u }; + uint16_t m_maxFps{}; + uint16_t m_targetFps{ 60u }; uint16_t m_fps{}; uint16_t m_fpsCount{}; diff --git a/src/framework/core/application.h b/src/framework/core/application.h index f8c280f45..88d4eba1e 100644 --- a/src/framework/core/application.h +++ b/src/framework/core/application.h @@ -67,7 +67,7 @@ protected: std::string m_charset{ "cp1252" }; std::string m_organizationName{ "otbr" }; std::string m_appName{ "OTClient - Redemption" }; - std::string m_appCompactName{ "otclient" }; + std::string m_appCompactName{ "otcr" }; std::string m_startupOptions; std::vector m_startupArgs; diff --git a/src/framework/core/asyncdispatcher.cpp b/src/framework/core/asyncdispatcher.cpp index e662a098a..87c259f55 100644 --- a/src/framework/core/asyncdispatcher.cpp +++ b/src/framework/core/asyncdispatcher.cpp @@ -26,11 +26,20 @@ AsyncDispatcher g_asyncDispatcher; void AsyncDispatcher::init(uint8_t maxThreads) { + /* + * -1 = Graphic + * 1 = Map and (Connection, Particle and Sound) Pool + * 2 = Foreground UI + * 3 = Foreground MAP + * 4 = Extra, ex: pathfinder and lighting system + */ + const uint8_t minThreads = 4; + if (maxThreads == 0) maxThreads = 6; - // -2 = Main Thread and Map Thread - int_fast8_t threads = std::clamp(std::thread::hardware_concurrency() - 2, 1, maxThreads); + // 2 = Min Threads + int_fast8_t threads = std::clamp(std::thread::hardware_concurrency() - 1, minThreads, maxThreads); while (--threads >= 0) m_threads.emplace_back([this] { m_ioService.run(); }); } @@ -45,9 +54,8 @@ void AsyncDispatcher::stop() m_ioService.stop(); - for (std::size_t i = 0; i < m_threads.size(); i++) { - if (m_threads[i].joinable()) { - m_threads[i].join(); - } + for (auto& thread : m_threads) { + if (thread.joinable()) + thread.join(); } }; \ No newline at end of file diff --git a/src/framework/core/eventdispatcher.cpp b/src/framework/core/eventdispatcher.cpp index 698fa24bd..3b2928efa 100644 --- a/src/framework/core/eventdispatcher.cpp +++ b/src/framework/core/eventdispatcher.cpp @@ -51,8 +51,8 @@ void EventDispatcher::shutdown() void EventDispatcher::poll() { - executeScheduledEvents(); executeEvents(); + executeScheduledEvents(); mergeEvents(); } @@ -63,12 +63,9 @@ ScheduledEventPtr EventDispatcher::scheduleEvent(const std::function& ca assert(delay >= 0); - const auto& scheduledEvent = std::make_shared(callback, delay, 1); const auto& thread = getThreadTask(); std::scoped_lock lock(thread->mutex); - - thread->scheduledEventList.emplace_back(scheduledEvent); - return scheduledEvent; + return thread->scheduledEventList.emplace_back(std::make_shared(callback, delay, 1)); } ScheduledEventPtr EventDispatcher::cycleEvent(const std::function& callback, int delay) @@ -78,12 +75,9 @@ ScheduledEventPtr EventDispatcher::cycleEvent(const std::function& callb assert(delay > 0); - const auto& scheduledEvent = std::make_shared(callback, delay, 0); const auto& thread = getThreadTask(); std::scoped_lock lock(thread->mutex); - - thread->scheduledEventList.emplace_back(scheduledEvent); - return scheduledEvent; + return thread->scheduledEventList.emplace_back(std::make_shared(callback, delay, 0)); } EventPtr EventDispatcher::addEvent(const std::function& callback) @@ -96,13 +90,9 @@ EventPtr EventDispatcher::addEvent(const std::function& callback) return std::make_shared(nullptr); } - const auto& event = std::make_shared(callback); - const auto& thread = getThreadTask(); std::scoped_lock lock(thread->mutex); - thread->events.emplace_back(event); - - return event; + return thread->events.emplace_back(std::make_shared(callback)); } void EventDispatcher::executeEvents() { @@ -113,7 +103,6 @@ void EventDispatcher::executeEvents() { for (const auto& event : m_eventList) { event->execute(); } - m_eventList.clear(); } diff --git a/src/framework/core/graphicalapplication.cpp b/src/framework/core/graphicalapplication.cpp index fbae7ee5c..46454031c 100644 --- a/src/framework/core/graphicalapplication.cpp +++ b/src/framework/core/graphicalapplication.cpp @@ -82,7 +82,8 @@ void GraphicalApplication::init(std::vector& args, uint8_t asyncDis g_sounds.init(); #endif - m_frameCounter.init(); + m_mapProcessFrameCounter.init(); + m_graphicFrameCounter.init(); } void GraphicalApplication::deinit() @@ -134,13 +135,51 @@ void GraphicalApplication::run() g_lua.callGlobalField("g_app", "onRun"); - const auto& foreground = g_drawPool.get(DrawPoolType::FOREGROUND); - const auto& foreground_tile = g_drawPool.get(DrawPoolType::FOREGROUND_TILE); - const auto& txt = g_drawPool.get(DrawPoolType::TEXT); - const auto& map = g_drawPool.get(DrawPoolType::MAP); + std::condition_variable foregroundUICondition, foregroundMapCondition; - // clang c++20 dont support jthread - std::thread t1([&]() { + const auto& realFPS = [&] { + m_mapProcessFrameCounter.setTargetFps(g_window.vsyncEnabled() || getMaxFps() || getTargetFps() ? 500u : 999u); + return std::min(m_graphicFrameCounter.getFps(), m_mapProcessFrameCounter.getFps()); + }; + + const auto& drawForeground = [&] { + const auto& foregroundUI = g_drawPool.get(DrawPoolType::FOREGROUND); + const auto& foregroundMap = g_drawPool.get(DrawPoolType::FOREGROUND_MAP); + + if (foregroundUI->canRepaint()) { + if (g_game.isOnline()) + foregroundUICondition.notify_one(); + else + g_ui.render(DrawPoolType::FOREGROUND); + } + + if (g_game.isOnline() && foregroundMap->canRepaint()) + foregroundMapCondition.notify_one(); + }; + + // THREAD - FOREGROUND UI + g_asyncDispatcher.dispatch([this, &condition = foregroundUICondition] { + const auto& pool = g_drawPool.get(DrawPoolType::FOREGROUND); + std::unique_lock lock(pool->getMutexPreDraw()); + condition.wait(lock, [this]() -> bool { + g_ui.render(DrawPoolType::FOREGROUND); + return m_stopping; + }); + }); + + // THREAD - FOREGROUND MAP + g_asyncDispatcher.dispatch([this, &condition = foregroundMapCondition] { + const auto& pool = g_drawPool.get(DrawPoolType::FOREGROUND_MAP); + std::unique_lock lock(pool->getMutexPreDraw()); + condition.wait(lock, [this]() -> bool { + if (g_ui.m_mapWidget) + g_ui.m_mapWidget->drawSelf(DrawPoolType::FOREGROUND_MAP); + return m_stopping; + }); + }); + + // THREAD - POOL & MAP + g_asyncDispatcher.dispatch([&] { g_eventThreadId = std::this_thread::get_id(); while (!m_stopping) { poll(); @@ -150,42 +189,20 @@ void GraphicalApplication::run() continue; } - /*if (g_drawPool.isDrawing()) { - stdext::millisleep(1); - continue; - }*/ - - if (foreground->canRepaint()) { - g_asyncDispatcher.dispatch([this, &foreground] { - std::scoped_lock l(foreground->getMutex()); - g_ui.render(DrawPoolType::FOREGROUND); - }); - } + drawForeground(); if (g_game.isOnline()) { if (!g_ui.m_mapWidget) g_ui.m_mapWidget = g_ui.getRootWidget()->recursiveGetChildById("gameMapPanel")->static_self_cast(); - if (txt->canRepaint() || foreground_tile->canRepaint()) { - g_asyncDispatcher.dispatch([this, &txt] { - std::scoped_lock l(txt->getMutex()); - g_textDispatcher.poll(); - - if (g_ui.m_mapWidget) { - g_ui.m_mapWidget->drawSelf(DrawPoolType::TEXT); - g_ui.m_mapWidget->drawSelf(DrawPoolType::FOREGROUND_TILE); - } - }); - } - - { - std::scoped_lock l(map->getMutex()); - g_ui.m_mapWidget->drawSelf(DrawPoolType::MAP); - } + g_ui.m_mapWidget->drawSelf(DrawPoolType::MAP); } else g_ui.m_mapWidget = nullptr; - stdext::millisleep(1); + m_mapProcessFrameCounter.update(); } + + foregroundUICondition.notify_one(); + foregroundMapCondition.notify_one(); }); m_running = true; @@ -201,10 +218,13 @@ void GraphicalApplication::run() // update screen pixels g_window.swapBuffers(); - m_frameCounter.update(); - } - t1.join(); + if (m_graphicFrameCounter.update()) { + g_dispatcher.addEvent([this, fps = realFPS()] { + g_lua.callGlobalField("g_app", "onFps", fps); + }); + } + } m_stopping = false; m_running = false; @@ -255,7 +275,7 @@ void GraphicalApplication::resize(const Size& size) if (USE_FRAMEBUFFER) { g_drawPool.get(DrawPoolType::CREATURE_INFORMATION)->setFramebuffer(size); - g_drawPool.get(DrawPoolType::TEXT)->setFramebuffer(size); + g_drawPool.get(DrawPoolType::FOREGROUND_MAP)->setFramebuffer(size); } }); } diff --git a/src/framework/core/graphicalapplication.h b/src/framework/core/graphicalapplication.h index 5cce89c3b..6ca438527 100644 --- a/src/framework/core/graphicalapplication.h +++ b/src/framework/core/graphicalapplication.h @@ -41,14 +41,14 @@ public: void mainPoll(); void close() override; - void setMaxFps(uint16_t maxFps) { m_frameCounter.setMaxFps(maxFps); } - void setTargetFps(uint16_t targetFps) { m_frameCounter.setTargetFps(targetFps); } + void setMaxFps(uint16_t maxFps) { m_graphicFrameCounter.setMaxFps(maxFps); } + void setTargetFps(uint16_t targetFps) { m_graphicFrameCounter.setTargetFps(targetFps); } - uint16_t getFps() { return m_frameCounter.getFps(); } - uint8_t getMaxFps() { return m_frameCounter.getMaxFps(); } - uint8_t getTargetFps() { return m_frameCounter.getTargetFps(); } + uint16_t getFps() { return m_graphicFrameCounter.getFps(); } + uint8_t getMaxFps() { return m_graphicFrameCounter.getMaxFps(); } + uint8_t getTargetFps() { return m_graphicFrameCounter.getTargetFps(); } - void resetTargetFps() { m_frameCounter.resetTargetFps(); } + void resetTargetFps() { m_graphicFrameCounter.resetTargetFps(); } bool isOnInputEvent() { return m_onInputEvent; } bool mustOptimize() { @@ -112,7 +112,8 @@ private: float m_animatedTextScale{ PlatformWindow::DEFAULT_DISPLAY_DENSITY }; float m_staticTextScale{ PlatformWindow::DEFAULT_DISPLAY_DENSITY }; - AdaptativeFrameCounter m_frameCounter; + AdaptativeFrameCounter m_mapProcessFrameCounter; + AdaptativeFrameCounter m_graphicFrameCounter; }; extern GraphicalApplication g_app; diff --git a/src/framework/graphics/drawpool.cpp b/src/framework/graphics/drawpool.cpp index ca63e3f2c..84dc1bf17 100644 --- a/src/framework/graphics/drawpool.cpp +++ b/src/framework/graphics/drawpool.cpp @@ -40,7 +40,7 @@ DrawPool* DrawPool::create(const DrawPoolType type) } else { pool->m_alwaysGroupDrawings = true; // CREATURE_INFORMATION & TEXT - if (type == DrawPoolType::TEXT || type == DrawPoolType::FOREGROUND_TILE) { + if (type == DrawPoolType::FOREGROUND_MAP) { pool->setFPS(FPS60); } } @@ -49,7 +49,7 @@ DrawPool* DrawPool::create(const DrawPoolType type) return pool; } -void DrawPool::add(const Color& color, const TexturePtr& texture, DrawPool::DrawMethod& method, +void DrawPool::add(const Color& color, const TexturePtr& texture, DrawPool::DrawMethod&& method, DrawMode drawMode, const DrawConductor& conductor, const CoordsBufferPtr& coordsBuffer) { updateHash(method, texture, color); @@ -63,8 +63,8 @@ void DrawPool::add(const Color& color, const TexturePtr& texture, DrawPool::Draw if (m_alwaysGroupDrawings || conductor.agroup) { auto& coords = m_coords.try_emplace(m_state.hash, nullptr).first->second; if (!coords) { - auto state = getState(method, texture, color); - coords = m_objects[m_depthLevel][order].emplace_back(state).coords.get(); + auto state = getState(texture, color); + coords = m_objects[order].emplace_back(std::move(state)).coords.get(); } if (coordsBuffer) @@ -74,12 +74,12 @@ void DrawPool::add(const Color& color, const TexturePtr& texture, DrawPool::Draw } else { bool addNewObj = true; - auto& list = m_objects[m_depthLevel][order]; + auto& list = m_objects[order]; if (!list.empty()) { auto& prevObj = list.back(); - if (prevObj.state.hash == m_state.hash) { + if (prevObj.state == m_state) { if (!prevObj.coords) - prevObj.addMethod(method); + prevObj.addMethod(std::move(method)); else if (coordsBuffer) prevObj.coords->append(coordsBuffer.get()); else @@ -90,11 +90,11 @@ void DrawPool::add(const Color& color, const TexturePtr& texture, DrawPool::Draw } if (addNewObj) { - auto state = getState(method, texture, color); + auto state = getState(texture, color); if (coordsBuffer) { - list.emplace_back(state).coords->append(coordsBuffer.get()); + list.emplace_back(std::move(state)).coords->append(coordsBuffer.get()); } else - list.emplace_back(drawMode, state, method); + list.emplace_back(drawMode, std::move(state), std::move(method)); } } @@ -143,38 +143,38 @@ void DrawPool::updateHash(const DrawPool::DrawMethod& method, const TexturePtr& { // State Hash m_state.hash = 0; + if (m_bindedFramebuffers) + stdext::hash_combine(m_state.hash, m_lastFramebufferId); + if (m_state.blendEquation != BlendEquation::ADD) stdext::hash_combine(m_state.hash, m_state.blendEquation); - if (m_state.clipRect.isValid()) - stdext::hash_union(m_state.hash, m_state.clipRect.hash()); - if (m_state.compositionMode != CompositionMode::NORMAL) stdext::hash_combine(m_state.hash, m_state.compositionMode); if (m_state.opacity < 1.f) stdext::hash_combine(m_state.hash, m_state.opacity); + if (m_state.clipRect.isValid()) + stdext::hash_union(m_state.hash, m_state.clipRect.hash()); + if (m_state.shaderProgram) - stdext::hash_combine(m_state.hash, m_state.shaderProgram->getProgramId()); + stdext::hash_union(m_state.hash, m_state.shaderProgram->hash()); if (m_state.transformMatrix != DEFAULT_MATRIX3) stdext::hash_union(m_state.hash, m_state.transformMatrix.hash()); - if (m_bindedFramebuffers) - stdext::hash_combine(m_state.hash, m_lastFramebufferId); - if (color != Color::white) stdext::hash_union(m_state.hash, color.hash()); if (texture) stdext::hash_union(m_state.hash, texture->hash()); - - if (hasFrameBuffer()) - stdext::hash_union(m_status.second, m_state.hash); } - if (hasFrameBuffer()) { // Method Hash + if (hasFrameBuffer()) { // Pool Hash + if (m_state.hash) + stdext::hash_union(m_status.second, m_state.hash); + if (method.type == DrawPool::DrawMethodType::TRIANGLE) { if (!method.a.isNull()) stdext::hash_union(m_status.second, method.a.hash()); if (!method.b.isNull()) stdext::hash_union(m_status.second, method.b.hash()); @@ -188,7 +188,7 @@ void DrawPool::updateHash(const DrawPool::DrawMethod& method, const TexturePtr& } } -DrawPool::PoolState DrawPool::getState(const DrawPool::DrawMethod& method, const TexturePtr& texture, const Color& color) +DrawPool::PoolState DrawPool::getState(const TexturePtr& texture, const Color& color) { return PoolState{ std::move(m_state.transformMatrix), m_state.opacity, @@ -243,14 +243,12 @@ void DrawPool::setShaderProgram(const PainterShaderProgramPtr& shaderProgram, bo void DrawPool::resetState() { - for (auto& objs : m_objects) { - for (auto& order : objs) - order.clear(); - } - + for (auto& objs : m_objects) + objs.clear(); + m_objectsFlushed.clear(); m_coords.clear(); + m_state = {}; - m_depthLevel = 0; m_status.second = 0; m_lastFramebufferId = 0; m_shaderRefreshDelay = 0; @@ -259,6 +257,9 @@ void DrawPool::resetState() bool DrawPool::canRepaint(const bool autoUpdateStatus) { + if (!hasFrameBuffer()) + return true; + uint16_t refreshDelay = m_refreshDelay; if (m_shaderRefreshDelay > 0 && (m_refreshDelay == 0 || m_shaderRefreshDelay < m_refreshDelay)) refreshDelay = m_shaderRefreshDelay; @@ -364,7 +365,7 @@ void DrawPool::removeFramebuffer() { void DrawPool::addAction(const std::function& action) { const uint8_t order = m_type == DrawPoolType::MAP ? DrawOrder::THIRD : DrawOrder::FIRST; - m_objects[m_depthLevel][order].emplace_back(action); + m_objects[order].emplace_back(action); } void DrawPool::bindFrameBuffer(const Size& size) diff --git a/src/framework/graphics/drawpool.h b/src/framework/graphics/drawpool.h index 0f3371bb7..4e0f785a7 100644 --- a/src/framework/graphics/drawpool.h +++ b/src/framework/graphics/drawpool.h @@ -29,20 +29,18 @@ #include "texture.h" #include "framework/core/timer.h" #include +#include #include "../stdext/storage.h" -#define MAX_DRAW_DEPTH 15 - enum class DrawPoolType : uint8_t { MAP, CREATURE_INFORMATION, LIGHT, - TEXT, - FOREGROUND_TILE, + FOREGROUND_MAP, FOREGROUND, - UNKNOW + LAST }; enum DrawOrder : uint8_t @@ -99,7 +97,8 @@ public: void onBeforeDraw(std::function f) { m_beforeDraw = std::move(f); } void onAfterDraw(std::function f) { m_afterDraw = std::move(f); } - std::mutex& getMutex() { return m_mutex; } + std::mutex& getMutex() { return m_mutexDraw; } + std::mutex& getMutexPreDraw() { return m_mutexPreDraw; } protected: @@ -140,11 +139,11 @@ protected: struct DrawObject { DrawObject(std::function action) : action(std::move(action)) {} - DrawObject(PoolState& state) : state(std::move(state)), coords(std::make_unique()) {} - DrawObject(const DrawMode drawMode, PoolState& state, DrawMethod& method) : + DrawObject(PoolState&& state) : state(std::move(state)), coords(std::make_unique()) {} + DrawObject(const DrawMode drawMode, PoolState&& state, DrawMethod&& method) : drawMode(drawMode), state(std::move(state)) { methods.emplace_back(std::move(method)); } - void addMethod(DrawMethod& method) + void addMethod(DrawMethod&& method) { drawMode = DrawMode::TRIANGLES; methods.emplace_back(std::move(method)); @@ -180,7 +179,7 @@ private: STATE_BLEND_EQUATION = 1 << 4, }; - void add(const Color& color, const TexturePtr& texture, DrawPool::DrawMethod& method, + void add(const Color& color, const TexturePtr& texture, DrawPool::DrawMethod&& method, DrawMode drawMode = DrawMode::TRIANGLES, const DrawConductor& conductor = DEFAULT_DRAW_CONDUCTOR, const CoordsBufferPtr& coordsBuffer = nullptr); @@ -191,7 +190,7 @@ private: inline void setFPS(uint16_t fps) { m_refreshDelay = fps; } void updateHash(const DrawPool::DrawMethod& method, const TexturePtr& texture, const Color& color); - PoolState getState(const DrawPool::DrawMethod& method, const TexturePtr& texture, const Color& color); + PoolState getState(const TexturePtr& texture, const Color& color); float getOpacity() const { return m_state.opacity; } Rect getClipRect() { return m_state.clipRect; } @@ -220,8 +219,25 @@ private: void flush() { m_coords.clear(); - if (m_depthLevel < MAX_DRAW_DEPTH) - ++m_depthLevel; + for (auto& objs : m_objects) { + m_objectsFlushed.insert(m_objectsFlushed.end(), make_move_iterator(objs.begin()), make_move_iterator(objs.end())); + objs.clear(); + } + } + + void release(bool draw = true) { + m_objectsDraw.clear(); + if (draw) { + if (!m_objectsFlushed.empty()) + m_objectsDraw.insert(m_objectsDraw.end(), make_move_iterator(m_objectsFlushed.begin()), make_move_iterator(m_objectsFlushed.end())); + + for (auto& objs : m_objects) { + m_objectsDraw.insert(m_objectsDraw.end(), make_move_iterator(objs.begin()), make_move_iterator(objs.end())); + objs.clear(); + } + } + + m_objectsFlushed.clear(); } bool canRepaint(bool autoUpdateStatus); @@ -232,7 +248,6 @@ private: bool m_alwaysGroupDrawings{ false }; int_fast8_t m_bindedFramebuffers{ -1 }; - uint8_t m_depthLevel{ 0 }; uint16_t m_refreshDelay{ 0 }, m_shaderRefreshDelay{ 0 }; uint32_t m_onlyOnceStateFlag{ 0 }; @@ -240,7 +255,7 @@ private: PoolState m_state, m_oldState; - DrawPoolType m_type{ DrawPoolType::UNKNOW }; + DrawPoolType m_type{ DrawPoolType::LAST }; Timer m_refreshTimer; @@ -248,7 +263,10 @@ private: std::vector m_transformMatrixStack; std::vector m_temporaryFramebuffers; - std::vector m_objects[MAX_DRAW_DEPTH + 1][static_cast(DrawOrder::LAST)]; + + std::vector m_objects[static_cast(DrawOrder::LAST)]; + std::vector m_objectsFlushed; + std::vector m_objectsDraw; stdext::map m_coords; @@ -260,7 +278,9 @@ private: std::function m_beforeDraw; std::function m_afterDraw; - std::mutex m_mutex; + std::atomic_bool m_repaint{ false }; + std::mutex m_mutexDraw; + std::mutex m_mutexPreDraw; friend class DrawPoolManager; }; diff --git a/src/framework/graphics/drawpoolmanager.cpp b/src/framework/graphics/drawpoolmanager.cpp index e750eebb4..ff097f814 100644 --- a/src/framework/graphics/drawpoolmanager.cpp +++ b/src/framework/graphics/drawpoolmanager.cpp @@ -33,7 +33,7 @@ void DrawPoolManager::init(uint16_t spriteSize) m_spriteSize = spriteSize; // Create Pools - for (int8_t i = -1; ++i <= static_cast(DrawPoolType::UNKNOW);) { + for (int8_t i = -1; ++i < static_cast(DrawPoolType::LAST);) { m_pools[i] = DrawPool::create(static_cast(i)); } } @@ -41,7 +41,7 @@ void DrawPoolManager::init(uint16_t spriteSize) void DrawPoolManager::terminate() const { // Destroy Pools - for (int_fast8_t i = -1; ++i <= static_cast(DrawPoolType::UNKNOW);) { + for (int_fast8_t i = -1; ++i < static_cast(DrawPoolType::LAST);) { delete m_pools[i]; } } @@ -57,62 +57,16 @@ void DrawPoolManager::draw() g_painter->setResolution(m_size, m_transformMatrix); } - // m_drawing = true; + for (auto pool : m_pools) { + if (pool->getType() == DrawPoolType::CREATURE_INFORMATION) + continue; - const auto& map = get(DrawPoolType::MAP); { - std::scoped_lock l(map->m_mutex); - if (drawPool(map)) { + std::scoped_lock l(pool->m_mutexDraw); + drawPool(pool); + + if (pool->getType() == DrawPoolType::MAP) drawPool(get(DrawPoolType::CREATURE_INFORMATION)); - drawPool(get(DrawPoolType::LIGHT)); - } } - - const auto& text = get(DrawPoolType::TEXT); { - std::scoped_lock l(text->m_mutex); - drawPool(text); - drawPool(get(DrawPoolType::FOREGROUND_TILE)); - } - - const auto& foreground = get(DrawPoolType::FOREGROUND); { - std::scoped_lock l(foreground->m_mutex); - drawPool(foreground); - } - - // m_drawing = false; -} - -bool DrawPoolManager::drawPool(DrawPool* pool) { - if (!pool->isEnabled()) - return false; - - if (!pool->hasFrameBuffer()) { - for (const auto& obj : pool->m_objects[0][DrawOrder::FIRST]) { - drawObject(obj); - } - return true; - } - - if (!pool->m_framebuffer->canDraw()) - return false; - - if (pool->canRepaint(true)) { - pool->m_framebuffer->bind(); - for (int_fast8_t i = -1; ++i <= pool->m_depthLevel;) { - for (const auto& order : pool->m_objects[i]) - for (const auto& obj : order) - drawObject(obj); - } - - pool->m_framebuffer->release(); - } - - g_painter->resetState(); - - if (pool->m_beforeDraw) pool->m_beforeDraw(); - pool->m_framebuffer->draw(); - if (pool->m_afterDraw) pool->m_afterDraw(); - - return true; } void DrawPoolManager::drawObject(const DrawPool::DrawObject& obj) @@ -135,8 +89,7 @@ void DrawPoolManager::drawObject(const DrawPool::DrawObject& obj) void DrawPoolManager::addTexturedCoordsBuffer(const TexturePtr& texture, const CoordsBufferPtr& coords, const Color& color) const { - DrawPool::DrawMethod method; - getCurrentPool()->add(color, texture, method, DrawMode::TRIANGLE_STRIP, DEFAULT_DRAW_CONDUCTOR, coords); + getCurrentPool()->add(color, texture, DrawPool::DrawMethod{}, DrawMode::TRIANGLE_STRIP, DEFAULT_DRAW_CONDUCTOR, coords); } void DrawPoolManager::addTexturedRect(const Rect& dest, const TexturePtr& texture, const Rect& src, const Color& color, const DrawConductor& condutor) const @@ -144,12 +97,10 @@ void DrawPoolManager::addTexturedRect(const Rect& dest, const TexturePtr& textur if (dest.isEmpty() || src.isEmpty()) return; - DrawPool::DrawMethod method{ + getCurrentPool()->add(color, texture, DrawPool::DrawMethod{ .type = DrawPool::DrawMethodType::RECT, .dest = dest, .src = src - }; - - getCurrentPool()->add(color, texture, method, DrawMode::TRIANGLE_STRIP, condutor); + }, DrawMode::TRIANGLE_STRIP, condutor); } void DrawPoolManager::addUpsideDownTexturedRect(const Rect& dest, const TexturePtr& texture, const Rect& src, const Color& color) const @@ -157,9 +108,7 @@ void DrawPoolManager::addUpsideDownTexturedRect(const Rect& dest, const TextureP if (dest.isEmpty() || src.isEmpty()) return; - DrawPool::DrawMethod method{ DrawPool::DrawMethodType::UPSIDEDOWN_RECT, dest, src }; - - getCurrentPool()->add(color, texture, method, DrawMode::TRIANGLE_STRIP); + getCurrentPool()->add(color, texture, DrawPool::DrawMethod{ DrawPool::DrawMethodType::UPSIDEDOWN_RECT, dest, src }, DrawMode::TRIANGLE_STRIP); } void DrawPoolManager::addTexturedRepeatedRect(const Rect& dest, const TexturePtr& texture, const Rect& src, const Color& color) const @@ -167,9 +116,7 @@ void DrawPoolManager::addTexturedRepeatedRect(const Rect& dest, const TexturePtr if (dest.isEmpty() || src.isEmpty()) return; - DrawPool::DrawMethod method{ DrawPool::DrawMethodType::REPEATED_RECT, dest, src }; - - getCurrentPool()->add(color, texture, method); + getCurrentPool()->add(color, texture, DrawPool::DrawMethod{ DrawPool::DrawMethodType::REPEATED_RECT, dest, src }); } void DrawPoolManager::addFilledRect(const Rect& dest, const Color& color, const DrawConductor& condutor) const @@ -177,9 +124,7 @@ void DrawPoolManager::addFilledRect(const Rect& dest, const Color& color, const if (dest.isEmpty()) return; - DrawPool::DrawMethod method{ DrawPool::DrawMethodType::RECT, dest }; - - getCurrentPool()->add(color, nullptr, method, DrawMode::TRIANGLES, condutor); + getCurrentPool()->add(color, nullptr, DrawPool::DrawMethod{ DrawPool::DrawMethodType::RECT, dest }, DrawMode::TRIANGLES, condutor); } void DrawPoolManager::addFilledTriangle(const Point& a, const Point& b, const Point& c, const Color& color) const @@ -187,9 +132,12 @@ void DrawPoolManager::addFilledTriangle(const Point& a, const Point& b, const Po if (a == b || a == c || b == c) return; - DrawPool::DrawMethod method{ .type = DrawPool::DrawMethodType::TRIANGLE, .a = a, .b = b, .c = c }; - - getCurrentPool()->add(color, nullptr, method); + getCurrentPool()->add(color, nullptr, DrawPool::DrawMethod{ + .type = DrawPool::DrawMethodType::TRIANGLE, + .a = a, + .b = b, + .c = c + }); } void DrawPoolManager::addBoundingRect(const Rect& dest, const Color& color, uint16_t innerLineWidth) const @@ -197,30 +145,69 @@ void DrawPoolManager::addBoundingRect(const Rect& dest, const Color& color, uint if (dest.isEmpty() || innerLineWidth == 0) return; - DrawPool::DrawMethod method{ + getCurrentPool()->add(color, nullptr, DrawPool::DrawMethod{ .type = DrawPool::DrawMethodType::BOUNDING_RECT, .dest = dest, .intValue = innerLineWidth - }; - - getCurrentPool()->add(color, nullptr, method); + }); } -void DrawPoolManager::use(const DrawPoolType type, const Rect& dest, const Rect& src, const Color& colorClear) +void DrawPoolManager::preDraw(const DrawPoolType type, const std::function& f, const Rect& dest, const Rect& src, const Color& colorClear) { select(type); + const auto pool = getCurrentPool(); - auto* currentPoll = getCurrentPool(); + if (pool->hasFrameBuffer() && pool->m_repaint.load()) + return; - currentPoll->setEnable(true); - currentPoll->resetState(); + pool->resetState(); - if (currentPoll->hasFrameBuffer()) { - currentPoll->m_framebuffer->prepare(dest, src, colorClear); - - // when the selected pool is MAP, reset the creature information state. - if (type == DrawPoolType::MAP) { - get(DrawPoolType::CREATURE_INFORMATION)->resetState(); - } + // when the selected pool is MAP, reset the creature information state. + if (type == DrawPoolType::MAP) { + get(DrawPoolType::CREATURE_INFORMATION)->resetState(); } + + if (f) f(); + + std::scoped_lock l(pool->m_mutexDraw); + + pool->setEnable(true); + if (pool->hasFrameBuffer()) + pool->m_framebuffer->prepare(dest, src, colorClear); + + pool->release(pool->m_repaint = pool->canRepaint(true)); + + if (type == DrawPoolType::MAP) { + get(DrawPoolType::CREATURE_INFORMATION)->release(); + } +} + +void DrawPoolManager::drawPool(DrawPool* pool) { + if (!pool->isEnabled()) + return; + + if (!pool->hasFrameBuffer()) { + for (const auto& obj : pool->m_objectsDraw) { + drawObject(obj); + } + return; + } + + if (!pool->m_framebuffer->canDraw()) + return; + + if (pool->m_repaint) { + pool->m_repaint.store(false); + + pool->m_framebuffer->bind(); + for (const auto& obj : pool->m_objectsDraw) + drawObject(obj); + pool->m_framebuffer->release(); + } + + g_painter->resetState(); + + if (pool->m_beforeDraw) pool->m_beforeDraw(); + pool->m_framebuffer->draw(); + if (pool->m_afterDraw) pool->m_afterDraw(); } \ No newline at end of file diff --git a/src/framework/graphics/drawpoolmanager.h b/src/framework/graphics/drawpoolmanager.h index 03a99517e..d305d76d4 100644 --- a/src/framework/graphics/drawpoolmanager.h +++ b/src/framework/graphics/drawpoolmanager.h @@ -33,8 +33,8 @@ public: DrawPool* get(const DrawPoolType type) const { return m_pools[static_cast(type)]; } void select(DrawPoolType type); - void use(const DrawPoolType type) { use(type, {}, {}); } - void use(DrawPoolType type, const Rect& dest, const Rect& src, const Color& colorClear = Color::alpha); + void preDraw(const DrawPoolType type, const std::function& f) { return preDraw(type, f, {}, {}); } + void preDraw(DrawPoolType type, const std::function& f, const Rect& dest, const Rect& src, const Color& colorClear = Color::alpha); void addTexturedPoint(const TexturePtr& texture, const Point& point, const Color& color = Color::white) const { addTexturedRect(Rect(point, texture->getSize()), texture, color); } @@ -93,8 +93,6 @@ public: DrawPoolType getCurrentType() const { return getCurrentPool()->m_type; } - bool isDrawing() const { return m_drawing; } - private: DrawPool* getCurrentPool() const; @@ -103,12 +101,10 @@ private: void terminate() const; void drawObject(const DrawPool::DrawObject& obj); - bool drawPool(DrawPool* pool); - - std::atomic_bool m_drawing{ false }; + void drawPool(DrawPool* pool); CoordsBuffer m_coordsBuffer; - std::array(DrawPoolType::UNKNOW) + 1> m_pools{}; + std::array(DrawPoolType::LAST)> m_pools{}; Size m_size; Matrix3 m_transformMatrix; diff --git a/src/framework/graphics/framebuffer.h b/src/framework/graphics/framebuffer.h index 85782bba9..31026796b 100644 --- a/src/framework/graphics/framebuffer.h +++ b/src/framework/graphics/framebuffer.h @@ -41,7 +41,9 @@ public: bool resize(const Size& size); bool isValid() const { return m_texture != nullptr; } - bool canDraw() const { return m_coordsBuffer.getVertexCount() > 0; } + bool canDraw() const { + return m_texture && m_coordsBuffer.getVertexCount() > 0; + } TexturePtr getTexture() const { return m_texture; } Size getSize() const { return m_texture->getSize(); } diff --git a/src/framework/graphics/graphics.cpp b/src/framework/graphics/graphics.cpp index 2417e581c..d4cd7c58b 100644 --- a/src/framework/graphics/graphics.cpp +++ b/src/framework/graphics/graphics.cpp @@ -77,7 +77,7 @@ void Graphics::init() m_ok = true; - g_painter = new Painter; + g_painter = std::make_unique(); g_textures.init(); } @@ -87,9 +87,6 @@ void Graphics::terminate() g_fonts.terminate(); g_textures.terminate(); - delete g_painter; - g_painter = nullptr; - m_ok = false; } diff --git a/src/framework/graphics/painter.cpp b/src/framework/graphics/painter.cpp index 72db5beb5..3c22d6ba8 100644 --- a/src/framework/graphics/painter.cpp +++ b/src/framework/graphics/painter.cpp @@ -27,7 +27,7 @@ #include "shader/shadersources.h" -Painter* g_painter = nullptr; +std::unique_ptr g_painter = nullptr; /** * Painter using OpenGL 2.0 programmable rendering pipeline, diff --git a/src/framework/graphics/painter.h b/src/framework/graphics/painter.h index 120966e9d..3917fcfcc 100644 --- a/src/framework/graphics/painter.h +++ b/src/framework/graphics/painter.h @@ -142,4 +142,4 @@ protected: PainterShaderProgramPtr m_drawReplaceColorProgram; }; -extern Painter* g_painter; +extern std::unique_ptr g_painter; diff --git a/src/framework/graphics/shaderprogram.cpp b/src/framework/graphics/shaderprogram.cpp index 863fe6333..248c19d18 100644 --- a/src/framework/graphics/shaderprogram.cpp +++ b/src/framework/graphics/shaderprogram.cpp @@ -24,6 +24,7 @@ #include "graphics.h" #include +#include uint32_t ShaderProgram::m_currentProgram = 0; @@ -48,6 +49,7 @@ bool ShaderProgram::addShader(const ShaderPtr& shader) glAttachShader(m_programId, shader->getShaderId()); m_linked = false; m_shaders.emplace_back(shader); + m_hash = stdext::hash_int(m_programId); return true; } @@ -80,6 +82,7 @@ void ShaderProgram::removeShader(const ShaderPtr& shader) glDetachShader(m_programId, shader->getShaderId()); m_shaders.erase(it); m_linked = false; + m_hash = 0; } void ShaderProgram::removeAllShaders() diff --git a/src/framework/graphics/shaderprogram.h b/src/framework/graphics/shaderprogram.h index 75442ad2f..d64cc8c18 100644 --- a/src/framework/graphics/shaderprogram.h +++ b/src/framework/graphics/shaderprogram.h @@ -109,11 +109,13 @@ public: bool isLinked() const { return m_linked; } uint32_t getProgramId() const { return m_programId; } + size_t hash() const { return m_hash; } ShaderList getShaders() { return m_shaders; } private: bool m_linked{ false }; uint32_t m_programId; + size_t m_hash{ 0 }; static uint32_t m_currentProgram; ShaderList m_shaders; std::array m_uniformLocations{ }; diff --git a/src/framework/platform/win32window.cpp b/src/framework/platform/win32window.cpp index f7b6a29a9..5402edd18 100644 --- a/src/framework/platform/win32window.cpp +++ b/src/framework/platform/win32window.cpp @@ -25,6 +25,7 @@ #include "win32window.h" #include #include +#include #include #include #include @@ -936,8 +937,9 @@ void WIN32Window::setFullscreen(bool fullscreen) void WIN32Window::setVerticalSync(bool enable) { - g_mainDispatcher.addEvent([&, enable] { - m_vsync = enable; + m_vsync = enable; + + g_mainDispatcher.addEvent([this, enable] { #ifdef OPENGL_ES eglSwapInterval(m_eglDisplay, enable); #else diff --git a/src/framework/ui/uimanager.cpp b/src/framework/ui/uimanager.cpp index f515e1796..abde91c63 100644 --- a/src/framework/ui/uimanager.cpp +++ b/src/framework/ui/uimanager.cpp @@ -59,8 +59,12 @@ void UIManager::terminate() void UIManager::render(DrawPoolType drawPane) const { - if (drawPane == DrawPoolType::FOREGROUND) - g_drawPool.use(DrawPoolType::FOREGROUND, { 0,0, g_graphics.getViewportSize() }, {}); + if (drawPane == DrawPoolType::FOREGROUND) { + g_drawPool.preDraw(DrawPoolType::FOREGROUND, [this, drawPane] { + m_rootWidget->draw(m_rootWidget->getRect(), drawPane); + }, { 0,0, g_graphics.getViewportSize() }, {}); + return; + } m_rootWidget->draw(m_rootWidget->getRect(), drawPane); } diff --git a/src/framework/ui/uiwidget.cpp b/src/framework/ui/uiwidget.cpp index 450d4f4cb..50d6e814e 100644 --- a/src/framework/ui/uiwidget.cpp +++ b/src/framework/ui/uiwidget.cpp @@ -1918,7 +1918,7 @@ void UIWidget::move(int x, int y) { self->m_rect = {}; // force update self->setRect(rect); self->setProp(PropUpdatingMove, false); - }, 13); + }, 8); } m_rect = { x, y, getSize() }; @@ -1935,4 +1935,4 @@ void UIWidget::setShader(const std::string_view name) { }); } -void UIWidget::repaint() { g_app.repaint(); } +void UIWidget::repaint() { g_app.repaint(); } \ No newline at end of file diff --git a/src/gitinfo.h b/src/gitinfo.h index e03e28d0a..db0f2f9f0 100644 --- a/src/gitinfo.h +++ b/src/gitinfo.h @@ -1,3 +1,3 @@ #define GIT_BRANCH desenv -#define GIT_VERSION 1.0 +#define GIT_VERSION 3.x #define GIT_COMMITS 0 diff --git a/vc17/otclient.vcxproj b/vc17/otclient.vcxproj index f79396697..149b23df8 100644 --- a/vc17/otclient.vcxproj +++ b/vc17/otclient.vcxproj @@ -29,13 +29,13 @@ Application true v143 - false + true Application true v143 - false + true Application @@ -142,6 +142,7 @@ cmd /c "start ../vcpkg_installed\$(VcpkgTriplet)\$(VcpkgTriplet)\tools\protobuf\ $(VcpkgRoot)\installed\$(VcpkgTriplet)\include\luajit;%(AdditionalIncludeDirectories) stdcpp20 4244;4251;4996;%(DisableSpecificWarnings) + true true