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:
Eduardo Bart 2012-06-01 16:39:09 -03:00
parent c01b32b032
commit bd2faabe99
43 changed files with 461 additions and 138 deletions

1
TODO
View File

@ -36,6 +36,7 @@ scrolling multiline edit
mouse wheel lineedits
onMapKnown event
save different account/pasword/server for each otclient protocol version
dont store textures upsidedown
== OPTIMIZATIONS AND REWORKS
* game

View File

@ -13,20 +13,60 @@ Panel
!text: tr('Fullscreen')
Label
!text: tr('Frame rate limit')
id: backgroundFrameRateLimitLabel
!text: tr('Background pane framerate limit: %s', 'max')
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 6
HorizontalScrollBar
id: frameRateScrollBar
id: backgroundFrameRateScrollBar
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 3
minimum: 0
maximum: 50
value: 0
minimum: 10
maximum: 201
value: 201
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)

View File

@ -69,9 +69,19 @@ TopPanel
anchors.bottom: parent.bottom
anchors.right: parent.right
FrameCounter
UILabel
size: 68 16
text-align: right
color: white
id: frameCounter
anchors.top: parent.top
anchors.right: prev.left
margin-top: 8
margin-right: 5
@onSetup: |
local updateFunc
updateFunc = function()
self:setText('FPS: ' .. g_app.getBackgroundPaneFps())
scheduleEvent(updateFunc, 250)
end
updateFunc()

View File

@ -6,7 +6,7 @@ local currentHoveredWidget
-- private functions
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()
pos.y = pos.y + 1
@ -49,6 +49,7 @@ function ToolTip.init()
toolTipLabel:setId('toolTip')
toolTipLabel:setBackgroundColor('#111111cc')
toolTipLabel:setTextAlign(AlignCenter)
toolTipLabel:hide()
toolTipLabel.onMouseMove = moveToolTip
end)
end

View File

@ -89,8 +89,8 @@ function UIScrollBar.create()
local scrollbar = UIScrollBar.internalCreate()
scrollbar:setFocusable(false)
scrollbar.value = 0
scrollbar.minimum = 0
scrollbar.maximum = 0
scrollbar.minimum = -999999
scrollbar.maximum = 999999
scrollbar.step = 1
scrollbar.orientation = 'vertical'
scrollbar.pixelsScroll = false
@ -98,6 +98,8 @@ function UIScrollBar.create()
end
function UIScrollBar:onSetup()
self.setupDone = true
signalcall(self.onValueChange, self, self.value)
addEvent(function()
Mouse.bindAutoPress(self:getChildById('decrementButton'), function() self:decrement() end)
Mouse.bindAutoPress(self:getChildById('incrementButton'), function() self:increment() end)
@ -163,7 +165,9 @@ function UIScrollBar:setValue(value)
local delta = value - self.value
self.value = value
updateSlider(self)
if self.setupDone then
signalcall(self.onValueChange, self, value, delta)
end
end
function UIScrollBar:setStep(step)

View File

@ -20,7 +20,3 @@ FlatLabel < UILabel
GameLabel < UILabel
font: verdana-11px-antialised
color: #bbbbbb
FrameCounter < UIFrameCounter
size: 68 16
align: right

View File

@ -173,6 +173,7 @@ SET(framework_SOURCES ${framework_SOURCES}
${CMAKE_CURRENT_LIST_DIR}/util/color.cpp
# framework core
${CMAKE_CURRENT_LIST_DIR}/core/adaptativeframecounter.cpp
${CMAKE_CURRENT_LIST_DIR}/core/logger.cpp
${CMAKE_CURRENT_LIST_DIR}/core/configmanager.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/uianchorlayout.cpp
${CMAKE_CURRENT_LIST_DIR}/ui/uilayout.cpp
${CMAKE_CURRENT_LIST_DIR}/ui/uiframecounter.cpp
${CMAKE_CURRENT_LIST_DIR}/ui/uitranslator.cpp
# framework third party

View File

@ -57,7 +57,7 @@ Application::Application(const std::string& appName)
{
g_app = this;
m_appName = appName;
m_frameSleep = 0;
m_foregroundFrameCounter.setMaxFps(60);
}
Application::~Application()
@ -157,6 +157,8 @@ void Application::terminate()
// terminate script environment
g_lua.terminate();
m_foreground = nullptr;
logInfo("Application ended successfully.");
}
@ -182,19 +184,70 @@ void Application::run()
poll();
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();
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();
// update screen pixels
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 {
// sleeps until next poll to avoid massive cpu usage
g_clock.sleep(POLL_CYCLE_DELAY+1);
}
if(m_frameSleep > 0)
g_clock.sleep(m_frameSleep);
}
m_stopping = false;
@ -227,20 +280,15 @@ void Application::close()
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)
{
m_onInputEvent = true;
g_graphics.resize(size);
g_ui.resize(size);
m_onInputEvent = false;
m_foreground = TexturePtr(new Texture(size.width(), size.height()));
m_mustRepaint = true;
}
void Application::inputEvent(const InputEvent& event)

View File

@ -24,6 +24,8 @@
#define APPLICATION_H
#include <framework/core/inputevent.h>
#include <framework/core/adaptativeframecounter.h>
#include <framework/graphics/declarations.h>
class Application
{
@ -43,33 +45,40 @@ public:
virtual void poll();
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 isStopping() { return m_stopping; }
bool isOnInputEvent() { return m_onInputEvent; }
int getFrameSleep() { return m_frameSleep; }
const std::string& getName() { return m_appName; }
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 getBuildDate() { return BUILD_DATE; }
std::string getBuildRevision() { return BUILD_REVISION; }
std::string getBuildType() { return BUILD_TYPE; }
protected:
virtual void render();
virtual void resize(const Size& size);
virtual void inputEvent(const InputEvent& event);
std::string m_appName;
std::string m_appVersion;
std::string m_appBuildDate;
int m_frameSleep;
Boolean<false> m_initialized;
Boolean<false> m_running;
Boolean<false> m_stopping;
Boolean<false> m_onInputEvent;
Boolean<false> m_mustRepaint;
AdaptativeFrameCounter m_backgroundFrameCounter;
AdaptativeFrameCounter m_foregroundFrameCounter;
TexturePtr m_foreground;
};
extern Application *g_app;

View File

@ -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;
}

View File

@ -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

View File

@ -35,7 +35,7 @@ public:
ticks_t asyncTicks();
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; }
float asyncTime() { return asyncTicks()/1000.0f; }

View File

@ -75,7 +75,7 @@ void FrameBuffer::resize(const Size& size)
logFatal("Unable to setup framebuffer object");
internalRelease();
} 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);
}
void FrameBuffer::draw()
{
Rect rect(0,0, getSize());
g_painter->drawTexturedRect(rect, m_texture, rect);
}
void FrameBuffer::draw(const Rect& dest, const Rect& src)
{
g_painter->drawTexturedRect(dest, m_texture, src);
@ -132,9 +138,7 @@ void FrameBuffer::internalBind()
boundFbo = m_fbo;
} else {
// backup screen color buffer into a texture
m_screenBackup->bind();
Size size = getSize();
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, size.width(), size.height());
m_screenBackup->copyFromScreen(Rect(0, 0, getSize()));
}
}
@ -145,16 +149,14 @@ void FrameBuffer::internalRelease()
glBindFramebuffer(GL_FRAMEBUFFER, m_prevBoundFbo);
boundFbo = m_prevBoundFbo;
} else {
Size size = getSize();
Rect screenRect(0, 0, getSize());
// copy the drawn color buffer into the framebuffer texture
m_texture->bind();
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, size.width(), size.height());
m_texture->copyFromScreen(screenRect);
// restore screen original content
glDisable(GL_BLEND);
g_painter->drawTexturedRect(Rect(0, 0, size), m_screenBackup, Rect(0, 0, size));
glEnable(GL_BLEND);
g_painter->setCompositionMode(Painter::CompositionMode_Replace);
g_painter->drawTexturedRect(screenRect, m_screenBackup, screenRect);
}
}

View File

@ -37,6 +37,7 @@ public:
void bind();
void clear(const Color& color = Color::black, const Rect& rect = Rect());
void release();
void draw();
void draw(const Rect& dest);
void draw(const Rect& dest, const Rect& src);

View File

@ -88,7 +88,6 @@ void Graphics::init()
selectPainterEngine(m_prefferedPainterEngine);
m_emptyTexture = TexturePtr(new Texture);
}
void Graphics::terminate()

View File

@ -53,7 +53,7 @@ void Painter::refreshState()
updateGlTexture();
}
void Painter::saveAndResetState()
void Painter::saveState()
{
assert(m_oldStateIndex<10);
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].texture = m_texture;
m_oldStateIndex++;
}
void Painter::saveAndResetState()
{
saveState();
resetState();
}

View File

@ -62,6 +62,7 @@ public:
void resetState();
virtual void refreshState();
void saveState();
void saveAndResetState();
void restoreSavedState();

View File

@ -50,9 +50,10 @@ void Particle::render()
if(!m_texture)
g_painter->drawFilledRect(m_rect);
else {
g_painter->saveState();
g_painter->setCompositionMode(m_compositionMode);
g_painter->drawTexturedRect(m_rect, m_texture);
g_painter->setCompositionMode(Painter::CompositionMode_Normal);
g_painter->restoreSavedState();
}
}

View File

@ -129,6 +129,12 @@ uint Texture::internalLoadGLTexture(uchar *pixels, int channels, int width, int
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()
{
// must reset painter texture state

View File

@ -30,9 +30,11 @@ class Texture : public std::enable_shared_from_this<Texture>
public:
Texture();
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();
void copyFromScreen(const Rect& screenRect);
void bind();
/// Tries to generate mipmaps via hardware, otherwise fallback to software implementation

View File

@ -390,11 +390,6 @@ void Application::registerLuaFunctions()
g_lua.bindClassMemberFunction<UITextEdit>("isShiftNavigation", &UITextEdit::isShiftNavigation);
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
g_lua.registerClass<Protocol>();
//g_lua.bindClassMemberFunction<Protocol>("connect", &Protocol::connect);
@ -457,10 +452,23 @@ void Application::registerLuaFunctions()
// Application
g_lua.registerStaticClass("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", "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", "getVersion", std::bind(&Application::getVersion, g_app));
g_lua.bindClassStaticFunction("g_app", "getBuildCompiler", std::bind(&Application::getBuildCompiler, g_app));

View File

@ -25,7 +25,6 @@
#include <framework/application.h>
#include <framework/thirdparty/apngloader.h>
#include <framework/core/resourcemanager.h>
#include <framework/util/utf8.h>
#define HSB_BIT_SET(p, n) (p[(n)/8] |= (128 >>((n)%8)))
@ -305,7 +304,7 @@ void WIN32Window::internalChooseGLVisual()
PFD_TYPE_RGBA,
32, // Select Our Color Depth
0, 0, 0, 0, 0, 0, // Color Bits Ignored
0, // No Alpha Buffer
1, // Alpha Buffer Bits
0, // Shift Bit Ignored
0, // No Accumulation Buffer
0, 0, 0, 0, // Accumulation Bits Ignored

View File

@ -371,6 +371,7 @@ void X11Window::internalChooseGLVisual()
#else
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT,
#endif
EGL_ALPHA_SIZE, 1,
EGL_NONE
};
@ -386,6 +387,7 @@ void X11Window::internalChooseGLVisual()
static int attrList[] = {
GLX_RENDER_TYPE, GLX_RGBA_BIT,
GLX_DOUBLEBUFFER, True,
GLX_ALPHA_SIZE, 1,
None
};

View File

@ -28,7 +28,7 @@
class StreamSoundSource : public SoundSource
{
enum {
STREAM_BUFFER_SIZE = 1024 * 500,
STREAM_BUFFER_SIZE = 1024 * 100,
STREAM_FRAGMENTS = 5,
STREAM_FRAGMENT_SIZE = STREAM_BUFFER_SIZE / STREAM_FRAGMENTS
};

View File

@ -31,5 +31,6 @@
#include "math.h"
#include "string.h"
#include "dumper.h"
#include "time.h"
#endif

View File

@ -20,43 +20,22 @@
* THE SOFTWARE.
*/
#include "uiframecounter.h"
#include "uitranslator.h"
#include <framework/graphics/font.h>
#include <framework/otml/otmlnode.h>
#include <framework/core/clock.h>
#include <framework/graphics/graphics.h>
#ifndef STDEXT_TIME_H
#define STDEXT_TIME_H
#include "types.h"
#include <chrono>
#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()
{
UIWidget::drawSelf();
#endif
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()));
}
}

View File

@ -39,8 +39,6 @@ typedef int32_t int32;
typedef int16_t int16;
typedef int8_t int8;
// note that on 32 bit platforms the max ticks will overflow for values above 2,147,483,647
// thus this means that the app may cause unknown behavior after running 24 days without restarting
typedef long ticks_t;
typedef int64_t ticks_t;
#endif

View File

@ -28,7 +28,6 @@
class UIManager;
class UIWidget;
class UITextEdit;
class UIFrameCounter;
class UILayout;
class UIBoxLayout;
class UIHorizontalLayout;
@ -40,7 +39,6 @@ typedef std::shared_ptr<UIWidget> UIWidgetPtr;
typedef std::weak_ptr<UIWidget> UIWidgetWeakPtr;
typedef std::shared_ptr<UITextEdit> UITextEditPtr;
typedef std::shared_ptr<UIFrameCounter> UIFrameCounterPtr;
typedef std::shared_ptr<UILayout> UILayoutPtr;
typedef std::shared_ptr<UIBoxLayout> UIBoxLayoutPtr;
typedef std::shared_ptr<UIHorizontalLayout> UIHorizontalLayoutPtr;

View File

@ -26,7 +26,6 @@
#include "uimanager.h"
#include "uiwidget.h"
#include "uitextedit.h"
#include "uiframecounter.h"
#include "uilayout.h"
#include "uihorizontallayout.h"
#include "uiverticallayout.h"

View File

@ -27,6 +27,7 @@
#include <framework/graphics/graphics.h>
#include <framework/platform/platformwindow.h>
#include <framework/core/eventdispatcher.h>
#include <framework/application.h>
UIManager g_ui;
@ -51,9 +52,9 @@ void UIManager::terminate()
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)

View File

@ -33,7 +33,7 @@ public:
void init();
void terminate();
void render();
void render(bool foregroundPane);
void resize(const Size& size);
void inputEvent(const InputEvent& event);

View File

@ -40,8 +40,11 @@ UITextEdit::UITextEdit()
blinkCursor();
}
void UITextEdit::drawSelf()
void UITextEdit::drawSelf(bool foregroundPane)
{
if(!foregroundPane)
return;
drawBackground(m_rect);
drawBorder(m_rect);
drawImage(m_rect);

View File

@ -30,7 +30,7 @@ class UITextEdit : public UIWidget
public:
UITextEdit();
virtual void drawSelf();
void drawSelf(bool foregroundPane);
private:
void update();

View File

@ -30,6 +30,7 @@
#include <framework/graphics/graphics.h>
#include <framework/platform/platformwindow.h>
#include <framework/graphics/texturemanager.h>
#include <framework/application.h>
UIWidget::UIWidget()
{
@ -51,26 +52,29 @@ UIWidget::~UIWidget()
#endif
}
void UIWidget::draw(const Rect& visibleRect)
void UIWidget::draw(const Rect& visibleRect, bool foregroundPane)
{
if(m_clipping)
g_painter->setClipRect(visibleRect);
drawSelf();
drawSelf(foregroundPane);
if(m_children.size() > 0) {
if(m_clipping)
g_painter->setClipRect(visibleRect.intersection(getClippingRect()));
drawChildren(visibleRect);
drawChildren(visibleRect, foregroundPane);
}
if(m_clipping)
g_painter->resetClipRect();
}
void UIWidget::drawSelf()
void UIWidget::drawSelf(bool foregroundPane)
{
if(!foregroundPane)
return;
// draw style components in order
if(m_backgroundColor.aF() > Fw::MIN_ALPHA) {
Rect backgroundDestRect = m_rect;
@ -84,7 +88,7 @@ void UIWidget::drawSelf()
drawText(m_rect);
}
void UIWidget::drawChildren(const Rect& visibleRect)
void UIWidget::drawChildren(const Rect& visibleRect, bool foregroundPane)
{
// draw children
for(const UIWidgetPtr& child : m_children) {
@ -103,10 +107,10 @@ void UIWidget::drawChildren(const Rect& visibleRect)
if(child->getOpacity() < oldOpacity)
g_painter->setOpacity(child->getOpacity());
child->draw(childVisibleRect);
child->draw(childVisibleRect, foregroundPane);
// debug draw box
if(g_ui.isDrawingDebugBoxes()) {
if(foregroundPane && g_ui.isDrawingDebugBoxes()) {
g_painter->setColor(Color::green);
g_painter->drawBoundingRect(child->getRect());
}
@ -807,6 +811,7 @@ bool UIWidget::setRect(const Rect& rect)
if(rect == oldRect)
return false;
g_app->repaint();
m_rect = rect;
// updates own layout

View File

@ -52,9 +52,9 @@ public:
virtual ~UIWidget();
protected:
virtual void draw(const Rect& visibleRect);
virtual void drawSelf();
virtual void drawChildren(const Rect& visibleRect);
virtual void draw(const Rect& visibleRect, bool foregroundPane);
virtual void drawSelf(bool foregroundPane);
virtual void drawChildren(const Rect& visibleRect, bool foregroundPane);
friend class UIManager;

View File

@ -148,6 +148,7 @@ void Creature::internalDrawOutfit(const Point& dest, float scaleFactor, bool ani
Point p = dest + (-Point(w,h)*Otc::TILE_PIXELS)*scaleFactor;
if(getLayers() > 1) {
g_painter->saveState();
g_painter->setCompositionMode(Painter::CompositionMode_Multiply);
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());
m_type->drawMask(p, scaleFactor, w, h, xPattern, yPattern, zPattern, 1, animationPhase, ThingType::BlueMask);
g_painter->resetColor();
g_painter->resetCompositionMode();
g_painter->restoreSavedState();
}
}
}

View File

@ -77,11 +77,13 @@ void MapView::draw(const Rect& rect)
Rect clearRect = Rect(0, 0, m_drawDimension * m_tileSize);
// drawing a black rect is actually faster than FrameBuffer::clear()
/*
g_painter->setColor(Color::black);
g_painter->drawFilledRect(clearRect);
g_painter->setColor(Color::white);
*/
// m_framebuffer->clear(Color::black);
m_framebuffer->clear(Color::black);
}
auto it = m_cachedVisibleTiles.begin();
@ -131,6 +133,7 @@ void MapView::draw(const Rect& rect)
drawOffset.y += (srcVisible.height() - srcSize.height()) / 2;
Rect srcRect = Rect(drawOffset, srcSize);
g_painter->saveState();
g_painter->setColor(Color::white);
g_painter->setCompositionMode(Painter::CompositionMode_Replace);
#if 0
@ -146,8 +149,8 @@ void MapView::draw(const Rect& rect)
m_framebuffer->draw(rect, srcRect);
#endif
g_painter->resetCompositionMode();
g_painter->resetShaderProgram();
g_painter->restoreSavedState();
//g_painter->resetShaderProgram();
// this could happen if the player position is not known yet
if(!cameraPosition.isValid())

View File

@ -24,9 +24,12 @@
#include <framework/otml/otml.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) {
g_painter->setColor(Color::white);

View File

@ -30,7 +30,7 @@
class UICreature : public UIWidget
{
public:
void drawSelf();
void drawSelf(bool foregroundPane);
void setCreature(const CreaturePtr& creature) { m_creature = creature; }
void setFixedCreatureSize(bool fixed) { m_fixedCreatureSize = fixed; }

View File

@ -30,9 +30,12 @@ UIItem::UIItem()
m_dragable = true;
}
void UIItem::drawSelf()
void UIItem::drawSelf(bool foregroundPane)
{
UIWidget::drawSelf();
if(!foregroundPane)
return;
UIWidget::drawSelf(foregroundPane);
if(m_item) {
Rect drawRect = getClippingRect();

View File

@ -31,7 +31,7 @@ class UIItem : public UIWidget
{
public:
UIItem();
void drawSelf();
void drawSelf(bool foregroundPane);
void setItemId(int id);
void setItemCount(int count) { if(m_item) m_item->setCount(count); }

View File

@ -45,16 +45,24 @@ UIMap::~UIMap()
g_map.removeMapView(m_mapView);
}
void UIMap::drawSelf()
void UIMap::drawSelf(bool foregroundPane)
{
UIWidget::drawSelf();
if(foregroundPane) {
UIWidget::drawSelf(foregroundPane);
// draw map border
g_painter->setColor(Color::black);
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);
m_mapView->draw(m_mapRect);
}
}
bool UIMap::setZoom(int zoom)

View File

@ -35,7 +35,7 @@ public:
UIMap();
~UIMap();
void drawSelf();
void drawSelf(bool foregroundPane);
bool setZoom(int zoom);
bool zoomIn();