graphics optimization feature inspirated by diablo3 engine
* the rendering now consits of two panes - the background pane (for animated stuff like the map) - the foreground pane (for steady stuff, like UI) each pane has it own max FPS and works idependently this may increase graphics performance on many platforms
This commit is contained in:
parent
c01b32b032
commit
bd2faabe99
1
TODO
1
TODO
|
@ -36,6 +36,7 @@ scrolling multiline edit
|
||||||
mouse wheel lineedits
|
mouse wheel lineedits
|
||||||
onMapKnown event
|
onMapKnown event
|
||||||
save different account/pasword/server for each otclient protocol version
|
save different account/pasword/server for each otclient protocol version
|
||||||
|
dont store textures upsidedown
|
||||||
|
|
||||||
== OPTIMIZATIONS AND REWORKS
|
== OPTIMIZATIONS AND REWORKS
|
||||||
* game
|
* game
|
||||||
|
|
|
@ -13,20 +13,60 @@ Panel
|
||||||
!text: tr('Fullscreen')
|
!text: tr('Fullscreen')
|
||||||
|
|
||||||
Label
|
Label
|
||||||
!text: tr('Frame rate limit')
|
id: backgroundFrameRateLimitLabel
|
||||||
|
!text: tr('Background pane framerate limit: %s', 'max')
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.top: prev.bottom
|
anchors.top: prev.bottom
|
||||||
margin-top: 6
|
margin-top: 6
|
||||||
|
|
||||||
HorizontalScrollBar
|
HorizontalScrollBar
|
||||||
id: frameRateScrollBar
|
id: backgroundFrameRateScrollBar
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.top: prev.bottom
|
anchors.top: prev.bottom
|
||||||
margin-top: 3
|
margin-top: 3
|
||||||
minimum: 0
|
minimum: 10
|
||||||
maximum: 50
|
maximum: 201
|
||||||
value: 0
|
value: 201
|
||||||
step: 1
|
step: 1
|
||||||
@onValueChange: g_app.setFrameSleep(self:getValue())
|
@onValueChange: |
|
||||||
|
local value = self:getValue()
|
||||||
|
local text = value
|
||||||
|
if value == self:getMaximum() then
|
||||||
|
text = 'max'
|
||||||
|
value = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
self:getParent():getChildById('backgroundFrameRateLimitLabel'):setText(tr('Background pane framerate limit: %s', text))
|
||||||
|
g_app.setBackgroundPaneMaxFps(value)
|
||||||
|
|
||||||
|
Label
|
||||||
|
id: foregroundFrameRateLimitLabel
|
||||||
|
!text: tr('Foreground pane framerate limit: %s', '8')
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.top: prev.bottom
|
||||||
|
margin-top: 6
|
||||||
|
|
||||||
|
HorizontalScrollBar
|
||||||
|
id: foregroundFrameRateScrollBar
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.top: prev.bottom
|
||||||
|
margin-top: 3
|
||||||
|
minimum: 1
|
||||||
|
maximum: 61
|
||||||
|
value: 8
|
||||||
|
step: 1
|
||||||
|
@onValueChange: |
|
||||||
|
local value = self:getValue()
|
||||||
|
local text = value
|
||||||
|
if value == self:getMaximum() then
|
||||||
|
text = 'max'
|
||||||
|
value = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
self:getParent():getChildById('foregroundFrameRateLimitLabel'):setText(tr('Foreground pane framerate limit: %s', text))
|
||||||
|
g_app.setForegroundPaneMaxFps(value)
|
||||||
|
|
||||||
|
|
|
@ -69,9 +69,19 @@ TopPanel
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
|
|
||||||
FrameCounter
|
UILabel
|
||||||
|
size: 68 16
|
||||||
|
text-align: right
|
||||||
|
color: white
|
||||||
id: frameCounter
|
id: frameCounter
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.right: prev.left
|
anchors.right: prev.left
|
||||||
margin-top: 8
|
margin-top: 8
|
||||||
margin-right: 5
|
margin-right: 5
|
||||||
|
@onSetup: |
|
||||||
|
local updateFunc
|
||||||
|
updateFunc = function()
|
||||||
|
self:setText('FPS: ' .. g_app.getBackgroundPaneFps())
|
||||||
|
scheduleEvent(updateFunc, 250)
|
||||||
|
end
|
||||||
|
updateFunc()
|
||||||
|
|
|
@ -6,7 +6,7 @@ local currentHoveredWidget
|
||||||
|
|
||||||
-- private functions
|
-- private functions
|
||||||
local function moveToolTip(tooltip)
|
local function moveToolTip(tooltip)
|
||||||
if not tooltip:isVisible() then return end
|
if not tooltip:isVisible() or tooltip:getOpacity() < 0.1 then return end
|
||||||
|
|
||||||
local pos = g_window.getMousePosition()
|
local pos = g_window.getMousePosition()
|
||||||
pos.y = pos.y + 1
|
pos.y = pos.y + 1
|
||||||
|
@ -49,6 +49,7 @@ function ToolTip.init()
|
||||||
toolTipLabel:setId('toolTip')
|
toolTipLabel:setId('toolTip')
|
||||||
toolTipLabel:setBackgroundColor('#111111cc')
|
toolTipLabel:setBackgroundColor('#111111cc')
|
||||||
toolTipLabel:setTextAlign(AlignCenter)
|
toolTipLabel:setTextAlign(AlignCenter)
|
||||||
|
toolTipLabel:hide()
|
||||||
toolTipLabel.onMouseMove = moveToolTip
|
toolTipLabel.onMouseMove = moveToolTip
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
|
@ -89,8 +89,8 @@ function UIScrollBar.create()
|
||||||
local scrollbar = UIScrollBar.internalCreate()
|
local scrollbar = UIScrollBar.internalCreate()
|
||||||
scrollbar:setFocusable(false)
|
scrollbar:setFocusable(false)
|
||||||
scrollbar.value = 0
|
scrollbar.value = 0
|
||||||
scrollbar.minimum = 0
|
scrollbar.minimum = -999999
|
||||||
scrollbar.maximum = 0
|
scrollbar.maximum = 999999
|
||||||
scrollbar.step = 1
|
scrollbar.step = 1
|
||||||
scrollbar.orientation = 'vertical'
|
scrollbar.orientation = 'vertical'
|
||||||
scrollbar.pixelsScroll = false
|
scrollbar.pixelsScroll = false
|
||||||
|
@ -98,6 +98,8 @@ function UIScrollBar.create()
|
||||||
end
|
end
|
||||||
|
|
||||||
function UIScrollBar:onSetup()
|
function UIScrollBar:onSetup()
|
||||||
|
self.setupDone = true
|
||||||
|
signalcall(self.onValueChange, self, self.value)
|
||||||
addEvent(function()
|
addEvent(function()
|
||||||
Mouse.bindAutoPress(self:getChildById('decrementButton'), function() self:decrement() end)
|
Mouse.bindAutoPress(self:getChildById('decrementButton'), function() self:decrement() end)
|
||||||
Mouse.bindAutoPress(self:getChildById('incrementButton'), function() self:increment() end)
|
Mouse.bindAutoPress(self:getChildById('incrementButton'), function() self:increment() end)
|
||||||
|
@ -163,8 +165,10 @@ function UIScrollBar:setValue(value)
|
||||||
local delta = value - self.value
|
local delta = value - self.value
|
||||||
self.value = value
|
self.value = value
|
||||||
updateSlider(self)
|
updateSlider(self)
|
||||||
|
if self.setupDone then
|
||||||
signalcall(self.onValueChange, self, value, delta)
|
signalcall(self.onValueChange, self, value, delta)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
function UIScrollBar:setStep(step)
|
function UIScrollBar:setStep(step)
|
||||||
self.step = step
|
self.step = step
|
||||||
|
|
|
@ -20,7 +20,3 @@ FlatLabel < UILabel
|
||||||
GameLabel < UILabel
|
GameLabel < UILabel
|
||||||
font: verdana-11px-antialised
|
font: verdana-11px-antialised
|
||||||
color: #bbbbbb
|
color: #bbbbbb
|
||||||
|
|
||||||
FrameCounter < UIFrameCounter
|
|
||||||
size: 68 16
|
|
||||||
align: right
|
|
||||||
|
|
|
@ -173,6 +173,7 @@ SET(framework_SOURCES ${framework_SOURCES}
|
||||||
${CMAKE_CURRENT_LIST_DIR}/util/color.cpp
|
${CMAKE_CURRENT_LIST_DIR}/util/color.cpp
|
||||||
|
|
||||||
# framework core
|
# framework core
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/core/adaptativeframecounter.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/core/logger.cpp
|
${CMAKE_CURRENT_LIST_DIR}/core/logger.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/core/configmanager.cpp
|
${CMAKE_CURRENT_LIST_DIR}/core/configmanager.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/core/resourcemanager.cpp
|
${CMAKE_CURRENT_LIST_DIR}/core/resourcemanager.cpp
|
||||||
|
@ -250,7 +251,6 @@ SET(framework_SOURCES ${framework_SOURCES}
|
||||||
${CMAKE_CURRENT_LIST_DIR}/ui/uigridlayout.cpp
|
${CMAKE_CURRENT_LIST_DIR}/ui/uigridlayout.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/ui/uianchorlayout.cpp
|
${CMAKE_CURRENT_LIST_DIR}/ui/uianchorlayout.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/ui/uilayout.cpp
|
${CMAKE_CURRENT_LIST_DIR}/ui/uilayout.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/ui/uiframecounter.cpp
|
|
||||||
${CMAKE_CURRENT_LIST_DIR}/ui/uitranslator.cpp
|
${CMAKE_CURRENT_LIST_DIR}/ui/uitranslator.cpp
|
||||||
|
|
||||||
# framework third party
|
# framework third party
|
||||||
|
|
|
@ -57,7 +57,7 @@ Application::Application(const std::string& appName)
|
||||||
{
|
{
|
||||||
g_app = this;
|
g_app = this;
|
||||||
m_appName = appName;
|
m_appName = appName;
|
||||||
m_frameSleep = 0;
|
m_foregroundFrameCounter.setMaxFps(60);
|
||||||
}
|
}
|
||||||
|
|
||||||
Application::~Application()
|
Application::~Application()
|
||||||
|
@ -157,6 +157,8 @@ void Application::terminate()
|
||||||
// terminate script environment
|
// terminate script environment
|
||||||
g_lua.terminate();
|
g_lua.terminate();
|
||||||
|
|
||||||
|
m_foreground = nullptr;
|
||||||
|
|
||||||
logInfo("Application ended successfully.");
|
logInfo("Application ended successfully.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,19 +184,70 @@ void Application::run()
|
||||||
poll();
|
poll();
|
||||||
|
|
||||||
if(g_window.isVisible()) {
|
if(g_window.isVisible()) {
|
||||||
|
// the otclient's screen consists of two panes
|
||||||
|
// background pane - high updated and animated pane (where the game are stuff happens)
|
||||||
|
// foreground pane - steady pane with few animated stuff (UI)
|
||||||
|
bool redraw = false;
|
||||||
|
bool updateForeground = false;
|
||||||
|
|
||||||
|
if(m_backgroundFrameCounter.shouldProcessNextFrame()) {
|
||||||
|
redraw = true;
|
||||||
|
|
||||||
|
if(m_mustRepaint || m_foregroundFrameCounter.shouldProcessNextFrame()) {
|
||||||
|
m_mustRepaint = false;
|
||||||
|
updateForeground = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(redraw) {
|
||||||
g_graphics.beginRender();
|
g_graphics.beginRender();
|
||||||
render();
|
|
||||||
|
Rect viewportRect(0, 0, g_graphics.getViewportSize());
|
||||||
|
|
||||||
|
// draw the foreground into a texture
|
||||||
|
if(updateForeground) {
|
||||||
|
m_foregroundFrameCounter.processNextFrame();
|
||||||
|
|
||||||
|
// draw foreground
|
||||||
|
g_ui.render(true);
|
||||||
|
|
||||||
|
// copy the foreground to a texture
|
||||||
|
m_foreground->copyFromScreen(viewportRect);
|
||||||
|
}
|
||||||
|
|
||||||
|
// draw background (animated stuff)
|
||||||
|
m_backgroundFrameCounter.processNextFrame();
|
||||||
|
g_ui.render(false);
|
||||||
|
|
||||||
|
// transform projection matrix to render upside down
|
||||||
|
Matrix3 projectionMatrix = g_painter->getProjectionMatrix();
|
||||||
|
projectionMatrix(2,2) *= -1.0f;
|
||||||
|
projectionMatrix(3,2) *= -1.0f;
|
||||||
|
|
||||||
|
// draw the foreground (steady stuff)
|
||||||
|
g_painter->saveAndResetState();
|
||||||
|
g_painter->setProjectionMatrix(projectionMatrix);
|
||||||
|
g_painter->drawTexturedRect(viewportRect, m_foreground, viewportRect);
|
||||||
|
g_painter->restoreSavedState();
|
||||||
|
|
||||||
g_graphics.endRender();
|
g_graphics.endRender();
|
||||||
|
|
||||||
// update screen pixels
|
// update screen pixels
|
||||||
g_window.swapBuffers();
|
g_window.swapBuffers();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_backgroundFrameCounter.update();
|
||||||
|
m_foregroundFrameCounter.update();
|
||||||
|
|
||||||
|
int sleepMicros = std::min(m_backgroundFrameCounter.getMaximumSleepMicros(), m_foregroundFrameCounter.getMaximumSleepMicros());
|
||||||
|
if(sleepMicros >= AdaptativeFrameCounter::MINIMUM_MICROS_SLEEP) {
|
||||||
|
stdext::microsleep(AdaptativeFrameCounter::MINIMUM_MICROS_SLEEP);
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// sleeps until next poll to avoid massive cpu usage
|
// sleeps until next poll to avoid massive cpu usage
|
||||||
g_clock.sleep(POLL_CYCLE_DELAY+1);
|
g_clock.sleep(POLL_CYCLE_DELAY+1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(m_frameSleep > 0)
|
|
||||||
g_clock.sleep(m_frameSleep);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_stopping = false;
|
m_stopping = false;
|
||||||
|
@ -227,20 +280,15 @@ void Application::close()
|
||||||
m_onInputEvent = false;
|
m_onInputEvent = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::render()
|
|
||||||
{
|
|
||||||
// everything is rendered by UI components, even the game
|
|
||||||
g_ui.render();
|
|
||||||
|
|
||||||
//g_particleManager.render();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::resize(const Size& size)
|
void Application::resize(const Size& size)
|
||||||
{
|
{
|
||||||
m_onInputEvent = true;
|
m_onInputEvent = true;
|
||||||
g_graphics.resize(size);
|
g_graphics.resize(size);
|
||||||
g_ui.resize(size);
|
g_ui.resize(size);
|
||||||
m_onInputEvent = false;
|
m_onInputEvent = false;
|
||||||
|
|
||||||
|
m_foreground = TexturePtr(new Texture(size.width(), size.height()));
|
||||||
|
m_mustRepaint = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::inputEvent(const InputEvent& event)
|
void Application::inputEvent(const InputEvent& event)
|
||||||
|
|
|
@ -24,6 +24,8 @@
|
||||||
#define APPLICATION_H
|
#define APPLICATION_H
|
||||||
|
|
||||||
#include <framework/core/inputevent.h>
|
#include <framework/core/inputevent.h>
|
||||||
|
#include <framework/core/adaptativeframecounter.h>
|
||||||
|
#include <framework/graphics/declarations.h>
|
||||||
|
|
||||||
class Application
|
class Application
|
||||||
{
|
{
|
||||||
|
@ -43,33 +45,40 @@ public:
|
||||||
virtual void poll();
|
virtual void poll();
|
||||||
virtual void close();
|
virtual void close();
|
||||||
|
|
||||||
void setFrameSleep(int delay) { m_frameSleep = delay; }
|
bool willRepaint() { return m_mustRepaint; }
|
||||||
|
void repaint() { m_mustRepaint = true; }
|
||||||
|
|
||||||
|
void setForegroundPaneMaxFps(int maxFps) { m_foregroundFrameCounter.setMaxFps(maxFps); }
|
||||||
|
void setBackgroundPaneMaxFps(int maxFps) { m_backgroundFrameCounter.setMaxFps(maxFps); }
|
||||||
|
|
||||||
bool isRunning() { return m_running; }
|
bool isRunning() { return m_running; }
|
||||||
bool isStopping() { return m_stopping; }
|
bool isStopping() { return m_stopping; }
|
||||||
bool isOnInputEvent() { return m_onInputEvent; }
|
bool isOnInputEvent() { return m_onInputEvent; }
|
||||||
int getFrameSleep() { return m_frameSleep; }
|
|
||||||
const std::string& getName() { return m_appName; }
|
const std::string& getName() { return m_appName; }
|
||||||
const std::string& getVersion() { return m_appVersion; }
|
const std::string& getVersion() { return m_appVersion; }
|
||||||
|
|
||||||
|
int getForegroundPaneFps() { return m_foregroundFrameCounter.getLastFps(); }
|
||||||
|
int getBackgroundPaneFps() { return m_backgroundFrameCounter.getLastFps(); }
|
||||||
std::string getBuildCompiler() { return BUILD_COMPILER; }
|
std::string getBuildCompiler() { return BUILD_COMPILER; }
|
||||||
std::string getBuildDate() { return BUILD_DATE; }
|
std::string getBuildDate() { return BUILD_DATE; }
|
||||||
std::string getBuildRevision() { return BUILD_REVISION; }
|
std::string getBuildRevision() { return BUILD_REVISION; }
|
||||||
std::string getBuildType() { return BUILD_TYPE; }
|
std::string getBuildType() { return BUILD_TYPE; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void render();
|
|
||||||
virtual void resize(const Size& size);
|
virtual void resize(const Size& size);
|
||||||
virtual void inputEvent(const InputEvent& event);
|
virtual void inputEvent(const InputEvent& event);
|
||||||
|
|
||||||
std::string m_appName;
|
std::string m_appName;
|
||||||
std::string m_appVersion;
|
std::string m_appVersion;
|
||||||
std::string m_appBuildDate;
|
std::string m_appBuildDate;
|
||||||
int m_frameSleep;
|
|
||||||
Boolean<false> m_initialized;
|
Boolean<false> m_initialized;
|
||||||
Boolean<false> m_running;
|
Boolean<false> m_running;
|
||||||
Boolean<false> m_stopping;
|
Boolean<false> m_stopping;
|
||||||
Boolean<false> m_onInputEvent;
|
Boolean<false> m_onInputEvent;
|
||||||
|
Boolean<false> m_mustRepaint;
|
||||||
|
AdaptativeFrameCounter m_backgroundFrameCounter;
|
||||||
|
AdaptativeFrameCounter m_foregroundFrameCounter;
|
||||||
|
TexturePtr m_foreground;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Application *g_app;
|
extern Application *g_app;
|
||||||
|
|
|
@ -0,0 +1,113 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2010-2012 OTClient <https://github.com/edubart/otclient>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "adaptativeframecounter.h"
|
||||||
|
|
||||||
|
AdaptativeFrameCounter::AdaptativeFrameCounter()
|
||||||
|
{
|
||||||
|
m_frames = 0;
|
||||||
|
m_partialFrames = 0;
|
||||||
|
m_partialFps = 0;
|
||||||
|
m_maxPartialFps = 0;
|
||||||
|
m_frameDelaySum = 0;
|
||||||
|
m_mediumFrameDelay = 0;
|
||||||
|
m_lastFps = 0;
|
||||||
|
m_lastFrame = 0;
|
||||||
|
m_maxFps = 0;
|
||||||
|
m_sleepMicros = 0;
|
||||||
|
m_mediumFrameDelay = 0;
|
||||||
|
m_lastFpsUpdate = stdext::micros();
|
||||||
|
m_lastPartialFpsUpdate = stdext::micros();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AdaptativeFrameCounter::shouldProcessNextFrame()
|
||||||
|
{
|
||||||
|
if(m_maxFps == 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
ticks_t now = stdext::micros();
|
||||||
|
if(now - m_lastFrame < m_bestFrameDelay)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdaptativeFrameCounter::processNextFrame()
|
||||||
|
{
|
||||||
|
ticks_t now = stdext::micros();
|
||||||
|
m_frames++;
|
||||||
|
m_partialFrames++;
|
||||||
|
m_frameDelaySum += now - m_lastFrame;
|
||||||
|
m_lastFrame = now ;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdaptativeFrameCounter::update()
|
||||||
|
{
|
||||||
|
ticks_t now = stdext::micros();
|
||||||
|
ticks_t delta = now - m_lastPartialFpsUpdate;
|
||||||
|
if(delta > 41000 && m_partialFrames > 0) {
|
||||||
|
m_partialFps = m_partialFrames / (delta / 1000000.0f);
|
||||||
|
m_lastPartialFpsUpdate = now;
|
||||||
|
m_partialFrames = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
delta = now - m_lastFpsUpdate;
|
||||||
|
if(delta >= 1000000) {
|
||||||
|
m_lastFps = m_frames;
|
||||||
|
if(m_frames > 0)
|
||||||
|
m_mediumFrameDelay = m_frameDelaySum / m_frames;
|
||||||
|
else
|
||||||
|
m_mediumFrameDelay = 0;
|
||||||
|
m_lastFpsUpdate = now;
|
||||||
|
m_frames = 0;
|
||||||
|
m_frameDelaySum = 0;
|
||||||
|
|
||||||
|
//dump << stdext::format("FPS => %d Partial FPS => %d Frame Delay Hit => %.2f%%", m_lastFps, (int)m_partialFps, getFrameDelayHit());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdaptativeFrameCounter::setMaxFps(int maxFps)
|
||||||
|
{
|
||||||
|
maxFps = std::max(std::min(maxFps, 1000), 0);
|
||||||
|
|
||||||
|
if(maxFps != 0) {
|
||||||
|
m_bestFrameDelay = 1000000 / maxFps;
|
||||||
|
} else {
|
||||||
|
m_maxPartialFps = 0;
|
||||||
|
m_bestFrameDelay = 0;
|
||||||
|
}
|
||||||
|
m_maxFps = maxFps;
|
||||||
|
}
|
||||||
|
|
||||||
|
int AdaptativeFrameCounter::getMaximumSleepMicros()
|
||||||
|
{
|
||||||
|
if(m_maxFps == 0)
|
||||||
|
return 0;
|
||||||
|
return m_lastFrame + m_bestFrameDelay - stdext::micros();
|
||||||
|
}
|
||||||
|
|
||||||
|
float AdaptativeFrameCounter::getFrameDelayHit()
|
||||||
|
{
|
||||||
|
if(m_bestFrameDelay > 0)
|
||||||
|
return ((m_bestFrameDelay - std::abs(m_bestFrameDelay - m_mediumFrameDelay)) * 100.0f) / (float)m_bestFrameDelay;
|
||||||
|
else
|
||||||
|
return 100.0f;
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2010-2012 OTClient <https://github.com/edubart/otclient>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ADAPTATIVEFRAMECOUNTER_H
|
||||||
|
#define ADAPTATIVEFRAMECOUNTER_H
|
||||||
|
|
||||||
|
#include <framework/global.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class that help counting and limiting frames per second in a application,
|
||||||
|
* to make fps limit work as desired, this class requires 2 platforms prerequisites:
|
||||||
|
* - the platform must have timer with a precision of at least 1ms
|
||||||
|
* - the platform sleep must have a precision of at least 4ms
|
||||||
|
*/
|
||||||
|
class AdaptativeFrameCounter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum {
|
||||||
|
// 4ms because most platforms has kernel timer of 250Hz
|
||||||
|
MINIMUM_MICROS_SLEEP = 4000
|
||||||
|
};
|
||||||
|
|
||||||
|
AdaptativeFrameCounter();
|
||||||
|
|
||||||
|
bool shouldProcessNextFrame();
|
||||||
|
void processNextFrame();
|
||||||
|
void update();
|
||||||
|
void setMaxFps(int maxFps);
|
||||||
|
bool isFpsLimitActive() { return m_maxFps != 0; }
|
||||||
|
|
||||||
|
int getMaximumSleepMicros();
|
||||||
|
float getFrameDelayHit();
|
||||||
|
int getLastFps() { return m_lastFps; }
|
||||||
|
int getPartialFps() { return (int)m_partialFps; }
|
||||||
|
int getMaxFps() { return m_maxFps; }
|
||||||
|
float getMediumFrameDelay() { return m_mediumFrameDelay; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
int m_frames;
|
||||||
|
int m_partialFrames;
|
||||||
|
ticks_t m_partialFpsDelta;
|
||||||
|
float m_partialFps;
|
||||||
|
float m_maxPartialFps;
|
||||||
|
ticks_t m_frameDelaySum;
|
||||||
|
ticks_t m_mediumFrameDelay;
|
||||||
|
ticks_t m_lastFrame;
|
||||||
|
int m_lastFps;
|
||||||
|
int m_maxFps;
|
||||||
|
ticks_t m_bestFrameDelay;
|
||||||
|
ticks_t m_lastFpsUpdate;
|
||||||
|
ticks_t m_lastPartialFpsUpdate;
|
||||||
|
float m_sleepMicros;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -35,7 +35,7 @@ public:
|
||||||
|
|
||||||
ticks_t asyncTicks();
|
ticks_t asyncTicks();
|
||||||
ticks_t ticks() { return m_currentTicks; }
|
ticks_t ticks() { return m_currentTicks; }
|
||||||
ticks_t ticksElapsed(long prevTicks) { return m_currentTicks - prevTicks; }
|
ticks_t ticksElapsed(ticks_t prevTicks) { return m_currentTicks - prevTicks; }
|
||||||
ticks_t ticksFor(int delay) { return m_currentTicks + delay; }
|
ticks_t ticksFor(int delay) { return m_currentTicks + delay; }
|
||||||
|
|
||||||
float asyncTime() { return asyncTicks()/1000.0f; }
|
float asyncTime() { return asyncTicks()/1000.0f; }
|
||||||
|
|
|
@ -75,7 +75,7 @@ void FrameBuffer::resize(const Size& size)
|
||||||
logFatal("Unable to setup framebuffer object");
|
logFatal("Unable to setup framebuffer object");
|
||||||
internalRelease();
|
internalRelease();
|
||||||
} else {
|
} else {
|
||||||
m_screenBackup = TexturePtr(new Texture(size.width(), size.height(), 4));
|
m_screenBackup = TexturePtr(new Texture(size.width(), size.height()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,6 +113,12 @@ void FrameBuffer::release()
|
||||||
g_graphics.setViewportSize(m_oldViewportSize);
|
g_graphics.setViewportSize(m_oldViewportSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FrameBuffer::draw()
|
||||||
|
{
|
||||||
|
Rect rect(0,0, getSize());
|
||||||
|
g_painter->drawTexturedRect(rect, m_texture, rect);
|
||||||
|
}
|
||||||
|
|
||||||
void FrameBuffer::draw(const Rect& dest, const Rect& src)
|
void FrameBuffer::draw(const Rect& dest, const Rect& src)
|
||||||
{
|
{
|
||||||
g_painter->drawTexturedRect(dest, m_texture, src);
|
g_painter->drawTexturedRect(dest, m_texture, src);
|
||||||
|
@ -132,9 +138,7 @@ void FrameBuffer::internalBind()
|
||||||
boundFbo = m_fbo;
|
boundFbo = m_fbo;
|
||||||
} else {
|
} else {
|
||||||
// backup screen color buffer into a texture
|
// backup screen color buffer into a texture
|
||||||
m_screenBackup->bind();
|
m_screenBackup->copyFromScreen(Rect(0, 0, getSize()));
|
||||||
Size size = getSize();
|
|
||||||
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, size.width(), size.height());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,16 +149,14 @@ void FrameBuffer::internalRelease()
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, m_prevBoundFbo);
|
glBindFramebuffer(GL_FRAMEBUFFER, m_prevBoundFbo);
|
||||||
boundFbo = m_prevBoundFbo;
|
boundFbo = m_prevBoundFbo;
|
||||||
} else {
|
} else {
|
||||||
Size size = getSize();
|
Rect screenRect(0, 0, getSize());
|
||||||
|
|
||||||
// copy the drawn color buffer into the framebuffer texture
|
// copy the drawn color buffer into the framebuffer texture
|
||||||
m_texture->bind();
|
m_texture->copyFromScreen(screenRect);
|
||||||
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, size.width(), size.height());
|
|
||||||
|
|
||||||
// restore screen original content
|
// restore screen original content
|
||||||
glDisable(GL_BLEND);
|
g_painter->setCompositionMode(Painter::CompositionMode_Replace);
|
||||||
g_painter->drawTexturedRect(Rect(0, 0, size), m_screenBackup, Rect(0, 0, size));
|
g_painter->drawTexturedRect(screenRect, m_screenBackup, screenRect);
|
||||||
glEnable(GL_BLEND);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,7 @@ public:
|
||||||
void bind();
|
void bind();
|
||||||
void clear(const Color& color = Color::black, const Rect& rect = Rect());
|
void clear(const Color& color = Color::black, const Rect& rect = Rect());
|
||||||
void release();
|
void release();
|
||||||
|
void draw();
|
||||||
void draw(const Rect& dest);
|
void draw(const Rect& dest);
|
||||||
void draw(const Rect& dest, const Rect& src);
|
void draw(const Rect& dest, const Rect& src);
|
||||||
|
|
||||||
|
|
|
@ -88,7 +88,6 @@ void Graphics::init()
|
||||||
|
|
||||||
selectPainterEngine(m_prefferedPainterEngine);
|
selectPainterEngine(m_prefferedPainterEngine);
|
||||||
m_emptyTexture = TexturePtr(new Texture);
|
m_emptyTexture = TexturePtr(new Texture);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Graphics::terminate()
|
void Graphics::terminate()
|
||||||
|
|
|
@ -53,7 +53,7 @@ void Painter::refreshState()
|
||||||
updateGlTexture();
|
updateGlTexture();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Painter::saveAndResetState()
|
void Painter::saveState()
|
||||||
{
|
{
|
||||||
assert(m_oldStateIndex<10);
|
assert(m_oldStateIndex<10);
|
||||||
m_olderStates[m_oldStateIndex].projectionMatrix = m_projectionMatrix;
|
m_olderStates[m_oldStateIndex].projectionMatrix = m_projectionMatrix;
|
||||||
|
@ -65,6 +65,11 @@ void Painter::saveAndResetState()
|
||||||
m_olderStates[m_oldStateIndex].shaderProgram = m_shaderProgram;
|
m_olderStates[m_oldStateIndex].shaderProgram = m_shaderProgram;
|
||||||
m_olderStates[m_oldStateIndex].texture = m_texture;
|
m_olderStates[m_oldStateIndex].texture = m_texture;
|
||||||
m_oldStateIndex++;
|
m_oldStateIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Painter::saveAndResetState()
|
||||||
|
{
|
||||||
|
saveState();
|
||||||
resetState();
|
resetState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -62,6 +62,7 @@ public:
|
||||||
|
|
||||||
void resetState();
|
void resetState();
|
||||||
virtual void refreshState();
|
virtual void refreshState();
|
||||||
|
void saveState();
|
||||||
void saveAndResetState();
|
void saveAndResetState();
|
||||||
void restoreSavedState();
|
void restoreSavedState();
|
||||||
|
|
||||||
|
|
|
@ -50,9 +50,10 @@ void Particle::render()
|
||||||
if(!m_texture)
|
if(!m_texture)
|
||||||
g_painter->drawFilledRect(m_rect);
|
g_painter->drawFilledRect(m_rect);
|
||||||
else {
|
else {
|
||||||
|
g_painter->saveState();
|
||||||
g_painter->setCompositionMode(m_compositionMode);
|
g_painter->setCompositionMode(m_compositionMode);
|
||||||
g_painter->drawTexturedRect(m_rect, m_texture);
|
g_painter->drawTexturedRect(m_rect, m_texture);
|
||||||
g_painter->setCompositionMode(Painter::CompositionMode_Normal);
|
g_painter->restoreSavedState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -129,6 +129,12 @@ uint Texture::internalLoadGLTexture(uchar *pixels, int channels, int width, int
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Texture::copyFromScreen(const Rect& screenRect)
|
||||||
|
{
|
||||||
|
bind();
|
||||||
|
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, screenRect.x(), screenRect.y(), screenRect.width(), screenRect.height());
|
||||||
|
}
|
||||||
|
|
||||||
void Texture::bind()
|
void Texture::bind()
|
||||||
{
|
{
|
||||||
// must reset painter texture state
|
// must reset painter texture state
|
||||||
|
|
|
@ -30,9 +30,11 @@ class Texture : public std::enable_shared_from_this<Texture>
|
||||||
public:
|
public:
|
||||||
Texture();
|
Texture();
|
||||||
Texture(const ImagePtr& image);
|
Texture(const ImagePtr& image);
|
||||||
Texture(int width, int height, int channels, uchar* pixels = NULL);
|
Texture(int width, int height, int channels = 4, uchar* pixels = NULL);
|
||||||
virtual ~Texture();
|
virtual ~Texture();
|
||||||
|
|
||||||
|
void copyFromScreen(const Rect& screenRect);
|
||||||
|
|
||||||
void bind();
|
void bind();
|
||||||
|
|
||||||
/// Tries to generate mipmaps via hardware, otherwise fallback to software implementation
|
/// Tries to generate mipmaps via hardware, otherwise fallback to software implementation
|
||||||
|
|
|
@ -390,11 +390,6 @@ void Application::registerLuaFunctions()
|
||||||
g_lua.bindClassMemberFunction<UITextEdit>("isShiftNavigation", &UITextEdit::isShiftNavigation);
|
g_lua.bindClassMemberFunction<UITextEdit>("isShiftNavigation", &UITextEdit::isShiftNavigation);
|
||||||
g_lua.bindClassMemberFunction<UITextEdit>("isMultiline", &UITextEdit::isMultiline);
|
g_lua.bindClassMemberFunction<UITextEdit>("isMultiline", &UITextEdit::isMultiline);
|
||||||
|
|
||||||
// UIFrameCounter
|
|
||||||
g_lua.registerClass<UIFrameCounter, UIWidget>();
|
|
||||||
g_lua.bindClassStaticFunction<UIFrameCounter>("create", []{ return UIFrameCounterPtr(new UIFrameCounter); } );
|
|
||||||
g_lua.bindClassMemberFunction<UIFrameCounter>("getFrameCount", &UIFrameCounter::getFrameCount);
|
|
||||||
|
|
||||||
// Protocol
|
// Protocol
|
||||||
g_lua.registerClass<Protocol>();
|
g_lua.registerClass<Protocol>();
|
||||||
//g_lua.bindClassMemberFunction<Protocol>("connect", &Protocol::connect);
|
//g_lua.bindClassMemberFunction<Protocol>("connect", &Protocol::connect);
|
||||||
|
@ -457,10 +452,23 @@ void Application::registerLuaFunctions()
|
||||||
// Application
|
// Application
|
||||||
g_lua.registerStaticClass("g_app");
|
g_lua.registerStaticClass("g_app");
|
||||||
g_lua.bindClassStaticFunction("g_app", "exit", std::bind(&Application::exit, g_app));
|
g_lua.bindClassStaticFunction("g_app", "exit", std::bind(&Application::exit, g_app));
|
||||||
g_lua.bindClassStaticFunction("g_app", "setFrameSleep", std::bind(&Application::setFrameSleep, g_app, std::placeholders::_1));
|
g_lua.bindClassStaticFunction("g_app", "setForegroundPaneMaxFps", std::bind(&Application::setForegroundPaneMaxFps, g_app, std::placeholders::_1));
|
||||||
|
g_lua.bindClassStaticFunction("g_app", "setBackgroundPaneMaxFps", std::bind(&Application::setBackgroundPaneMaxFps, g_app, std::placeholders::_1));
|
||||||
|
g_lua.bindClassStaticFunction("g_app", "isRunning", std::bind(&Application::isRunning, g_app));
|
||||||
|
g_lua.bindClassStaticFunction("g_app", "isStopping", std::bind(&Application::isStopping, g_app));
|
||||||
|
g_lua.bindClassStaticFunction("g_app", "isOnInputEvent", std::bind(&Application::isOnInputEvent, g_app));
|
||||||
|
g_lua.bindClassStaticFunction("g_app", "getName", std::bind(&Application::getName, g_app));
|
||||||
|
g_lua.bindClassStaticFunction("g_app", "getVersion", std::bind(&Application::getVersion, g_app));
|
||||||
|
g_lua.bindClassStaticFunction("g_app", "getForegroundPaneFps", std::bind(&Application::getForegroundPaneFps, g_app));
|
||||||
|
g_lua.bindClassStaticFunction("g_app", "getBackgroundPaneFps", std::bind(&Application::getBackgroundPaneFps, g_app));
|
||||||
|
g_lua.bindClassStaticFunction("g_app", "getBuildCompiler", std::bind(&Application::getBuildCompiler, g_app));
|
||||||
|
g_lua.bindClassStaticFunction("g_app", "getBuildDate", std::bind(&Application::getBuildDate, g_app));
|
||||||
|
g_lua.bindClassStaticFunction("g_app", "getBuildRevision", std::bind(&Application::getBuildRevision, g_app));
|
||||||
|
g_lua.bindClassStaticFunction("g_app", "getBuildType", std::bind(&Application::getBuildType, g_app));
|
||||||
|
|
||||||
|
g_lua.bindClassStaticFunction("g_app", "exit", std::bind(&Application::exit, g_app));
|
||||||
g_lua.bindClassStaticFunction("g_app", "isRunning", std::bind(&Application::isRunning, g_app));
|
g_lua.bindClassStaticFunction("g_app", "isRunning", std::bind(&Application::isRunning, g_app));
|
||||||
g_lua.bindClassStaticFunction("g_app", "isStopping", std::bind(&Application::isStopping, g_app));
|
g_lua.bindClassStaticFunction("g_app", "isStopping", std::bind(&Application::isStopping, g_app));
|
||||||
g_lua.bindClassStaticFunction("g_app", "getFrameSleep", std::bind(&Application::getFrameSleep, g_app));
|
|
||||||
g_lua.bindClassStaticFunction("g_app", "getName", std::bind(&Application::getName, g_app));
|
g_lua.bindClassStaticFunction("g_app", "getName", std::bind(&Application::getName, g_app));
|
||||||
g_lua.bindClassStaticFunction("g_app", "getVersion", std::bind(&Application::getVersion, g_app));
|
g_lua.bindClassStaticFunction("g_app", "getVersion", std::bind(&Application::getVersion, g_app));
|
||||||
g_lua.bindClassStaticFunction("g_app", "getBuildCompiler", std::bind(&Application::getBuildCompiler, g_app));
|
g_lua.bindClassStaticFunction("g_app", "getBuildCompiler", std::bind(&Application::getBuildCompiler, g_app));
|
||||||
|
|
|
@ -25,7 +25,6 @@
|
||||||
#include <framework/application.h>
|
#include <framework/application.h>
|
||||||
#include <framework/thirdparty/apngloader.h>
|
#include <framework/thirdparty/apngloader.h>
|
||||||
#include <framework/core/resourcemanager.h>
|
#include <framework/core/resourcemanager.h>
|
||||||
#include <framework/util/utf8.h>
|
|
||||||
|
|
||||||
#define HSB_BIT_SET(p, n) (p[(n)/8] |= (128 >>((n)%8)))
|
#define HSB_BIT_SET(p, n) (p[(n)/8] |= (128 >>((n)%8)))
|
||||||
|
|
||||||
|
@ -305,7 +304,7 @@ void WIN32Window::internalChooseGLVisual()
|
||||||
PFD_TYPE_RGBA,
|
PFD_TYPE_RGBA,
|
||||||
32, // Select Our Color Depth
|
32, // Select Our Color Depth
|
||||||
0, 0, 0, 0, 0, 0, // Color Bits Ignored
|
0, 0, 0, 0, 0, 0, // Color Bits Ignored
|
||||||
0, // No Alpha Buffer
|
1, // Alpha Buffer Bits
|
||||||
0, // Shift Bit Ignored
|
0, // Shift Bit Ignored
|
||||||
0, // No Accumulation Buffer
|
0, // No Accumulation Buffer
|
||||||
0, 0, 0, 0, // Accumulation Bits Ignored
|
0, 0, 0, 0, // Accumulation Bits Ignored
|
||||||
|
|
|
@ -371,6 +371,7 @@ void X11Window::internalChooseGLVisual()
|
||||||
#else
|
#else
|
||||||
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT,
|
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT,
|
||||||
#endif
|
#endif
|
||||||
|
EGL_ALPHA_SIZE, 1,
|
||||||
EGL_NONE
|
EGL_NONE
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -386,6 +387,7 @@ void X11Window::internalChooseGLVisual()
|
||||||
static int attrList[] = {
|
static int attrList[] = {
|
||||||
GLX_RENDER_TYPE, GLX_RGBA_BIT,
|
GLX_RENDER_TYPE, GLX_RGBA_BIT,
|
||||||
GLX_DOUBLEBUFFER, True,
|
GLX_DOUBLEBUFFER, True,
|
||||||
|
GLX_ALPHA_SIZE, 1,
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
class StreamSoundSource : public SoundSource
|
class StreamSoundSource : public SoundSource
|
||||||
{
|
{
|
||||||
enum {
|
enum {
|
||||||
STREAM_BUFFER_SIZE = 1024 * 500,
|
STREAM_BUFFER_SIZE = 1024 * 100,
|
||||||
STREAM_FRAGMENTS = 5,
|
STREAM_FRAGMENTS = 5,
|
||||||
STREAM_FRAGMENT_SIZE = STREAM_BUFFER_SIZE / STREAM_FRAGMENTS
|
STREAM_FRAGMENT_SIZE = STREAM_BUFFER_SIZE / STREAM_FRAGMENTS
|
||||||
};
|
};
|
||||||
|
|
|
@ -31,5 +31,6 @@
|
||||||
#include "math.h"
|
#include "math.h"
|
||||||
#include "string.h"
|
#include "string.h"
|
||||||
#include "dumper.h"
|
#include "dumper.h"
|
||||||
|
#include "time.h"
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -20,43 +20,22 @@
|
||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "uiframecounter.h"
|
#ifndef STDEXT_TIME_H
|
||||||
#include "uitranslator.h"
|
#define STDEXT_TIME_H
|
||||||
#include <framework/graphics/font.h>
|
|
||||||
#include <framework/otml/otmlnode.h>
|
#include "types.h"
|
||||||
#include <framework/core/clock.h>
|
#include <chrono>
|
||||||
#include <framework/graphics/graphics.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
namespace stdext {
|
||||||
|
|
||||||
|
const static std::chrono::system_clock::time_point startup_time = std::chrono::high_resolution_clock::now();
|
||||||
|
inline ticks_t millis() { return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - startup_time).count(); }
|
||||||
|
inline ticks_t micros() { return std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now() - startup_time).count(); }
|
||||||
|
inline void millisleep(uint32 ms) { usleep(ms * 1000); };
|
||||||
|
inline void microsleep(uint32 us) { usleep(us); };
|
||||||
|
|
||||||
UIFrameCounter::UIFrameCounter()
|
|
||||||
{
|
|
||||||
m_focusable = false;
|
|
||||||
m_phantom = true;
|
|
||||||
m_align = Fw::AlignLeft;
|
|
||||||
m_lastFrameTicks = g_clock.ticks();
|
|
||||||
m_frameCount = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void UIFrameCounter::drawSelf()
|
#endif
|
||||||
{
|
|
||||||
UIWidget::drawSelf();
|
|
||||||
|
|
||||||
if(g_clock.ticksElapsed(m_lastFrameTicks) >= 1000) {
|
|
||||||
m_fpsText = stdext::format("FPS: %d", m_frameCount);
|
|
||||||
m_lastFrameTicks = g_clock.ticks();
|
|
||||||
m_frameCount = 0;
|
|
||||||
}
|
|
||||||
m_frameCount++;
|
|
||||||
|
|
||||||
g_painter->setColor(Color::white);
|
|
||||||
m_font->drawText(m_fpsText, m_rect, m_align);
|
|
||||||
}
|
|
||||||
|
|
||||||
void UIFrameCounter::onStyleApply(const std::string& styleName, const OTMLNodePtr& styleNode)
|
|
||||||
{
|
|
||||||
UIWidget::onStyleApply(styleName, styleNode);
|
|
||||||
|
|
||||||
for(const OTMLNodePtr& node : styleNode->children()) {
|
|
||||||
if(node->tag() == "align")
|
|
||||||
setAlign(Fw::translateAlignment(node->value()));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -39,8 +39,6 @@ typedef int32_t int32;
|
||||||
typedef int16_t int16;
|
typedef int16_t int16;
|
||||||
typedef int8_t int8;
|
typedef int8_t int8;
|
||||||
|
|
||||||
// note that on 32 bit platforms the max ticks will overflow for values above 2,147,483,647
|
typedef int64_t ticks_t;
|
||||||
// thus this means that the app may cause unknown behavior after running 24 days without restarting
|
|
||||||
typedef long ticks_t;
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -28,7 +28,6 @@
|
||||||
class UIManager;
|
class UIManager;
|
||||||
class UIWidget;
|
class UIWidget;
|
||||||
class UITextEdit;
|
class UITextEdit;
|
||||||
class UIFrameCounter;
|
|
||||||
class UILayout;
|
class UILayout;
|
||||||
class UIBoxLayout;
|
class UIBoxLayout;
|
||||||
class UIHorizontalLayout;
|
class UIHorizontalLayout;
|
||||||
|
@ -40,7 +39,6 @@ typedef std::shared_ptr<UIWidget> UIWidgetPtr;
|
||||||
typedef std::weak_ptr<UIWidget> UIWidgetWeakPtr;
|
typedef std::weak_ptr<UIWidget> UIWidgetWeakPtr;
|
||||||
|
|
||||||
typedef std::shared_ptr<UITextEdit> UITextEditPtr;
|
typedef std::shared_ptr<UITextEdit> UITextEditPtr;
|
||||||
typedef std::shared_ptr<UIFrameCounter> UIFrameCounterPtr;
|
|
||||||
typedef std::shared_ptr<UILayout> UILayoutPtr;
|
typedef std::shared_ptr<UILayout> UILayoutPtr;
|
||||||
typedef std::shared_ptr<UIBoxLayout> UIBoxLayoutPtr;
|
typedef std::shared_ptr<UIBoxLayout> UIBoxLayoutPtr;
|
||||||
typedef std::shared_ptr<UIHorizontalLayout> UIHorizontalLayoutPtr;
|
typedef std::shared_ptr<UIHorizontalLayout> UIHorizontalLayoutPtr;
|
||||||
|
|
|
@ -26,7 +26,6 @@
|
||||||
#include "uimanager.h"
|
#include "uimanager.h"
|
||||||
#include "uiwidget.h"
|
#include "uiwidget.h"
|
||||||
#include "uitextedit.h"
|
#include "uitextedit.h"
|
||||||
#include "uiframecounter.h"
|
|
||||||
#include "uilayout.h"
|
#include "uilayout.h"
|
||||||
#include "uihorizontallayout.h"
|
#include "uihorizontallayout.h"
|
||||||
#include "uiverticallayout.h"
|
#include "uiverticallayout.h"
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include <framework/graphics/graphics.h>
|
#include <framework/graphics/graphics.h>
|
||||||
#include <framework/platform/platformwindow.h>
|
#include <framework/platform/platformwindow.h>
|
||||||
#include <framework/core/eventdispatcher.h>
|
#include <framework/core/eventdispatcher.h>
|
||||||
|
#include <framework/application.h>
|
||||||
|
|
||||||
UIManager g_ui;
|
UIManager g_ui;
|
||||||
|
|
||||||
|
@ -51,9 +52,9 @@ void UIManager::terminate()
|
||||||
m_pressedWidget = nullptr;
|
m_pressedWidget = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void UIManager::render()
|
void UIManager::render(bool foregroundPane)
|
||||||
{
|
{
|
||||||
m_rootWidget->draw(m_rootWidget->getRect());
|
m_rootWidget->draw(m_rootWidget->getRect(), foregroundPane);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UIManager::resize(const Size& size)
|
void UIManager::resize(const Size& size)
|
||||||
|
|
|
@ -33,7 +33,7 @@ public:
|
||||||
void init();
|
void init();
|
||||||
void terminate();
|
void terminate();
|
||||||
|
|
||||||
void render();
|
void render(bool foregroundPane);
|
||||||
void resize(const Size& size);
|
void resize(const Size& size);
|
||||||
void inputEvent(const InputEvent& event);
|
void inputEvent(const InputEvent& event);
|
||||||
|
|
||||||
|
|
|
@ -40,8 +40,11 @@ UITextEdit::UITextEdit()
|
||||||
blinkCursor();
|
blinkCursor();
|
||||||
}
|
}
|
||||||
|
|
||||||
void UITextEdit::drawSelf()
|
void UITextEdit::drawSelf(bool foregroundPane)
|
||||||
{
|
{
|
||||||
|
if(!foregroundPane)
|
||||||
|
return;
|
||||||
|
|
||||||
drawBackground(m_rect);
|
drawBackground(m_rect);
|
||||||
drawBorder(m_rect);
|
drawBorder(m_rect);
|
||||||
drawImage(m_rect);
|
drawImage(m_rect);
|
||||||
|
|
|
@ -30,7 +30,7 @@ class UITextEdit : public UIWidget
|
||||||
public:
|
public:
|
||||||
UITextEdit();
|
UITextEdit();
|
||||||
|
|
||||||
virtual void drawSelf();
|
void drawSelf(bool foregroundPane);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void update();
|
void update();
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
#include <framework/graphics/graphics.h>
|
#include <framework/graphics/graphics.h>
|
||||||
#include <framework/platform/platformwindow.h>
|
#include <framework/platform/platformwindow.h>
|
||||||
#include <framework/graphics/texturemanager.h>
|
#include <framework/graphics/texturemanager.h>
|
||||||
|
#include <framework/application.h>
|
||||||
|
|
||||||
UIWidget::UIWidget()
|
UIWidget::UIWidget()
|
||||||
{
|
{
|
||||||
|
@ -51,26 +52,29 @@ UIWidget::~UIWidget()
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void UIWidget::draw(const Rect& visibleRect)
|
void UIWidget::draw(const Rect& visibleRect, bool foregroundPane)
|
||||||
{
|
{
|
||||||
if(m_clipping)
|
if(m_clipping)
|
||||||
g_painter->setClipRect(visibleRect);
|
g_painter->setClipRect(visibleRect);
|
||||||
|
|
||||||
drawSelf();
|
drawSelf(foregroundPane);
|
||||||
|
|
||||||
if(m_children.size() > 0) {
|
if(m_children.size() > 0) {
|
||||||
if(m_clipping)
|
if(m_clipping)
|
||||||
g_painter->setClipRect(visibleRect.intersection(getClippingRect()));
|
g_painter->setClipRect(visibleRect.intersection(getClippingRect()));
|
||||||
|
|
||||||
drawChildren(visibleRect);
|
drawChildren(visibleRect, foregroundPane);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(m_clipping)
|
if(m_clipping)
|
||||||
g_painter->resetClipRect();
|
g_painter->resetClipRect();
|
||||||
}
|
}
|
||||||
|
|
||||||
void UIWidget::drawSelf()
|
void UIWidget::drawSelf(bool foregroundPane)
|
||||||
{
|
{
|
||||||
|
if(!foregroundPane)
|
||||||
|
return;
|
||||||
|
|
||||||
// draw style components in order
|
// draw style components in order
|
||||||
if(m_backgroundColor.aF() > Fw::MIN_ALPHA) {
|
if(m_backgroundColor.aF() > Fw::MIN_ALPHA) {
|
||||||
Rect backgroundDestRect = m_rect;
|
Rect backgroundDestRect = m_rect;
|
||||||
|
@ -84,7 +88,7 @@ void UIWidget::drawSelf()
|
||||||
drawText(m_rect);
|
drawText(m_rect);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UIWidget::drawChildren(const Rect& visibleRect)
|
void UIWidget::drawChildren(const Rect& visibleRect, bool foregroundPane)
|
||||||
{
|
{
|
||||||
// draw children
|
// draw children
|
||||||
for(const UIWidgetPtr& child : m_children) {
|
for(const UIWidgetPtr& child : m_children) {
|
||||||
|
@ -103,10 +107,10 @@ void UIWidget::drawChildren(const Rect& visibleRect)
|
||||||
if(child->getOpacity() < oldOpacity)
|
if(child->getOpacity() < oldOpacity)
|
||||||
g_painter->setOpacity(child->getOpacity());
|
g_painter->setOpacity(child->getOpacity());
|
||||||
|
|
||||||
child->draw(childVisibleRect);
|
child->draw(childVisibleRect, foregroundPane);
|
||||||
|
|
||||||
// debug draw box
|
// debug draw box
|
||||||
if(g_ui.isDrawingDebugBoxes()) {
|
if(foregroundPane && g_ui.isDrawingDebugBoxes()) {
|
||||||
g_painter->setColor(Color::green);
|
g_painter->setColor(Color::green);
|
||||||
g_painter->drawBoundingRect(child->getRect());
|
g_painter->drawBoundingRect(child->getRect());
|
||||||
}
|
}
|
||||||
|
@ -807,6 +811,7 @@ bool UIWidget::setRect(const Rect& rect)
|
||||||
if(rect == oldRect)
|
if(rect == oldRect)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
g_app->repaint();
|
||||||
m_rect = rect;
|
m_rect = rect;
|
||||||
|
|
||||||
// updates own layout
|
// updates own layout
|
||||||
|
|
|
@ -52,9 +52,9 @@ public:
|
||||||
virtual ~UIWidget();
|
virtual ~UIWidget();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void draw(const Rect& visibleRect);
|
virtual void draw(const Rect& visibleRect, bool foregroundPane);
|
||||||
virtual void drawSelf();
|
virtual void drawSelf(bool foregroundPane);
|
||||||
virtual void drawChildren(const Rect& visibleRect);
|
virtual void drawChildren(const Rect& visibleRect, bool foregroundPane);
|
||||||
|
|
||||||
friend class UIManager;
|
friend class UIManager;
|
||||||
|
|
||||||
|
|
|
@ -148,6 +148,7 @@ void Creature::internalDrawOutfit(const Point& dest, float scaleFactor, bool ani
|
||||||
Point p = dest + (-Point(w,h)*Otc::TILE_PIXELS)*scaleFactor;
|
Point p = dest + (-Point(w,h)*Otc::TILE_PIXELS)*scaleFactor;
|
||||||
|
|
||||||
if(getLayers() > 1) {
|
if(getLayers() > 1) {
|
||||||
|
g_painter->saveState();
|
||||||
g_painter->setCompositionMode(Painter::CompositionMode_Multiply);
|
g_painter->setCompositionMode(Painter::CompositionMode_Multiply);
|
||||||
|
|
||||||
g_painter->setColor(m_outfit.getHeadColor());
|
g_painter->setColor(m_outfit.getHeadColor());
|
||||||
|
@ -159,8 +160,7 @@ void Creature::internalDrawOutfit(const Point& dest, float scaleFactor, bool ani
|
||||||
g_painter->setColor(m_outfit.getFeetColor());
|
g_painter->setColor(m_outfit.getFeetColor());
|
||||||
m_type->drawMask(p, scaleFactor, w, h, xPattern, yPattern, zPattern, 1, animationPhase, ThingType::BlueMask);
|
m_type->drawMask(p, scaleFactor, w, h, xPattern, yPattern, zPattern, 1, animationPhase, ThingType::BlueMask);
|
||||||
|
|
||||||
g_painter->resetColor();
|
g_painter->restoreSavedState();
|
||||||
g_painter->resetCompositionMode();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,11 +77,13 @@ void MapView::draw(const Rect& rect)
|
||||||
Rect clearRect = Rect(0, 0, m_drawDimension * m_tileSize);
|
Rect clearRect = Rect(0, 0, m_drawDimension * m_tileSize);
|
||||||
|
|
||||||
// drawing a black rect is actually faster than FrameBuffer::clear()
|
// drawing a black rect is actually faster than FrameBuffer::clear()
|
||||||
|
/*
|
||||||
g_painter->setColor(Color::black);
|
g_painter->setColor(Color::black);
|
||||||
g_painter->drawFilledRect(clearRect);
|
g_painter->drawFilledRect(clearRect);
|
||||||
g_painter->setColor(Color::white);
|
g_painter->setColor(Color::white);
|
||||||
|
*/
|
||||||
|
|
||||||
// m_framebuffer->clear(Color::black);
|
m_framebuffer->clear(Color::black);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto it = m_cachedVisibleTiles.begin();
|
auto it = m_cachedVisibleTiles.begin();
|
||||||
|
@ -131,6 +133,7 @@ void MapView::draw(const Rect& rect)
|
||||||
drawOffset.y += (srcVisible.height() - srcSize.height()) / 2;
|
drawOffset.y += (srcVisible.height() - srcSize.height()) / 2;
|
||||||
Rect srcRect = Rect(drawOffset, srcSize);
|
Rect srcRect = Rect(drawOffset, srcSize);
|
||||||
|
|
||||||
|
g_painter->saveState();
|
||||||
g_painter->setColor(Color::white);
|
g_painter->setColor(Color::white);
|
||||||
g_painter->setCompositionMode(Painter::CompositionMode_Replace);
|
g_painter->setCompositionMode(Painter::CompositionMode_Replace);
|
||||||
#if 0
|
#if 0
|
||||||
|
@ -146,8 +149,8 @@ void MapView::draw(const Rect& rect)
|
||||||
m_framebuffer->draw(rect, srcRect);
|
m_framebuffer->draw(rect, srcRect);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
g_painter->resetCompositionMode();
|
g_painter->restoreSavedState();
|
||||||
g_painter->resetShaderProgram();
|
//g_painter->resetShaderProgram();
|
||||||
|
|
||||||
// this could happen if the player position is not known yet
|
// this could happen if the player position is not known yet
|
||||||
if(!cameraPosition.isValid())
|
if(!cameraPosition.isValid())
|
||||||
|
|
|
@ -24,9 +24,12 @@
|
||||||
#include <framework/otml/otml.h>
|
#include <framework/otml/otml.h>
|
||||||
#include <framework/graphics/graphics.h>
|
#include <framework/graphics/graphics.h>
|
||||||
|
|
||||||
void UICreature::drawSelf()
|
void UICreature::drawSelf(bool foregroundPane)
|
||||||
{
|
{
|
||||||
UIWidget::drawSelf();
|
if(!foregroundPane)
|
||||||
|
return;
|
||||||
|
|
||||||
|
UIWidget::drawSelf(foregroundPane);
|
||||||
|
|
||||||
if(m_creature) {
|
if(m_creature) {
|
||||||
g_painter->setColor(Color::white);
|
g_painter->setColor(Color::white);
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
class UICreature : public UIWidget
|
class UICreature : public UIWidget
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void drawSelf();
|
void drawSelf(bool foregroundPane);
|
||||||
|
|
||||||
void setCreature(const CreaturePtr& creature) { m_creature = creature; }
|
void setCreature(const CreaturePtr& creature) { m_creature = creature; }
|
||||||
void setFixedCreatureSize(bool fixed) { m_fixedCreatureSize = fixed; }
|
void setFixedCreatureSize(bool fixed) { m_fixedCreatureSize = fixed; }
|
||||||
|
|
|
@ -30,9 +30,12 @@ UIItem::UIItem()
|
||||||
m_dragable = true;
|
m_dragable = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void UIItem::drawSelf()
|
void UIItem::drawSelf(bool foregroundPane)
|
||||||
{
|
{
|
||||||
UIWidget::drawSelf();
|
if(!foregroundPane)
|
||||||
|
return;
|
||||||
|
|
||||||
|
UIWidget::drawSelf(foregroundPane);
|
||||||
|
|
||||||
if(m_item) {
|
if(m_item) {
|
||||||
Rect drawRect = getClippingRect();
|
Rect drawRect = getClippingRect();
|
||||||
|
|
|
@ -31,7 +31,7 @@ class UIItem : public UIWidget
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
UIItem();
|
UIItem();
|
||||||
void drawSelf();
|
void drawSelf(bool foregroundPane);
|
||||||
|
|
||||||
void setItemId(int id);
|
void setItemId(int id);
|
||||||
void setItemCount(int count) { if(m_item) m_item->setCount(count); }
|
void setItemCount(int count) { if(m_item) m_item->setCount(count); }
|
||||||
|
|
|
@ -45,17 +45,25 @@ UIMap::~UIMap()
|
||||||
g_map.removeMapView(m_mapView);
|
g_map.removeMapView(m_mapView);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UIMap::drawSelf()
|
void UIMap::drawSelf(bool foregroundPane)
|
||||||
{
|
{
|
||||||
UIWidget::drawSelf();
|
if(foregroundPane) {
|
||||||
|
UIWidget::drawSelf(foregroundPane);
|
||||||
|
|
||||||
// draw map border
|
// draw map border
|
||||||
g_painter->setColor(Color::black);
|
g_painter->setColor(Color::black);
|
||||||
g_painter->drawBoundingRect(m_mapRect.expanded(1));
|
g_painter->drawBoundingRect(m_mapRect.expanded(1));
|
||||||
|
|
||||||
|
g_painter->saveState();
|
||||||
|
g_painter->setCompositionMode(Painter::CompositionMode_Replace);
|
||||||
|
g_painter->setColor(Color::alpha);
|
||||||
|
g_painter->drawFilledRect(m_mapRect);
|
||||||
|
g_painter->restoreSavedState();
|
||||||
|
} else {
|
||||||
g_painter->setColor(Color::white);
|
g_painter->setColor(Color::white);
|
||||||
m_mapView->draw(m_mapRect);
|
m_mapView->draw(m_mapRect);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool UIMap::setZoom(int zoom)
|
bool UIMap::setZoom(int zoom)
|
||||||
{
|
{
|
||||||
|
|
|
@ -35,7 +35,7 @@ public:
|
||||||
UIMap();
|
UIMap();
|
||||||
~UIMap();
|
~UIMap();
|
||||||
|
|
||||||
void drawSelf();
|
void drawSelf(bool foregroundPane);
|
||||||
|
|
||||||
bool setZoom(int zoom);
|
bool setZoom(int zoom);
|
||||||
bool zoomIn();
|
bool zoomIn();
|
||||||
|
|
Loading…
Reference in New Issue