MYST3: Improve the node transition performance

On 4K displays, taking a screenshot and processing the pixels on the CPU
was taking a noticeable amount of time. Now the screenshot is copied
directly to a texture without going through the CPU.
This commit is contained in:
Bastien Bouclet 2019-11-08 14:50:20 +01:00
parent 93929be2c1
commit 056912f4b2
11 changed files with 99 additions and 27 deletions

View file

@ -95,6 +95,17 @@ void Renderer::freeFont() {
} }
} }
Texture *Renderer::copyScreenshotToTexture() {
Graphics::Surface *surface = getScreenshot();
Texture *texture = createTexture(surface);
surface->free();
delete surface;
return texture;
}
Common::Rect Renderer::getFontCharacterRect(uint8 character) { Common::Rect Renderer::getFontCharacterRect(uint8 character) {
uint index = 0; uint index = 0;

View file

@ -137,6 +137,7 @@ public:
virtual void draw2DText(const Common::String &text, const Common::Point &position) = 0; virtual void draw2DText(const Common::String &text, const Common::Point &position) = 0;
virtual Graphics::Surface *getScreenshot() = 0; virtual Graphics::Surface *getScreenshot() = 0;
virtual Texture *copyScreenshotToTexture();
/** Render a Drawable in the specified window */ /** Render a Drawable in the specified window */
void renderDrawable(Drawable *drawable, Window *window); void renderDrawable(Drawable *drawable, Window *window);

View file

@ -168,10 +168,14 @@ void OpenGLRenderer::drawTexturedRect2D(const Common::Rect &screenRect, const Co
const float tTop = textureRect.top / (float)glTexture->internalHeight; const float tTop = textureRect.top / (float)glTexture->internalHeight;
const float tHeight = textureRect.height() / (float)glTexture->internalHeight; const float tHeight = textureRect.height() / (float)glTexture->internalHeight;
const float sLeft = screenRect.left; float sLeft = screenRect.left;
const float sTop = screenRect.top; float sTop = screenRect.top;
const float sWidth = screenRect.width(); float sRight = sLeft + screenRect.width();
const float sHeight = screenRect.height(); float sBottom = sTop + screenRect.height();
if (glTexture->upsideDown) {
SWAP(sTop, sBottom);
}
if (transparency >= 0.0) { if (transparency >= 0.0) {
if (additiveBlending) { if (additiveBlending) {
@ -192,16 +196,16 @@ void OpenGLRenderer::drawTexturedRect2D(const Common::Rect &screenRect, const Co
glBindTexture(GL_TEXTURE_2D, glTexture->id); glBindTexture(GL_TEXTURE_2D, glTexture->id);
glBegin(GL_TRIANGLE_STRIP); glBegin(GL_TRIANGLE_STRIP);
glTexCoord2f(tLeft, tTop + tHeight); glTexCoord2f(tLeft, tTop + tHeight);
glVertex3f(sLeft + 0, sTop + sHeight, 1.0f); glVertex3f(sLeft + 0, sBottom, 1.0f);
glTexCoord2f(tLeft + tWidth, tTop + tHeight); glTexCoord2f(tLeft + tWidth, tTop + tHeight);
glVertex3f(sLeft + sWidth, sTop + sHeight, 1.0f); glVertex3f(sRight, sBottom, 1.0f);
glTexCoord2f(tLeft, tTop); glTexCoord2f(tLeft, tTop);
glVertex3f(sLeft + 0, sTop + 0, 1.0f); glVertex3f(sLeft + 0, sTop + 0, 1.0f);
glTexCoord2f(tLeft + tWidth, tTop); glTexCoord2f(tLeft + tWidth, tTop);
glVertex3f(sLeft + sWidth, sTop + 0, 1.0f); glVertex3f(sRight, sTop + 0, 1.0f);
glEnd(); glEnd();
glDisable(GL_BLEND); glDisable(GL_BLEND);
@ -328,6 +332,15 @@ Graphics::Surface *OpenGLRenderer::getScreenshot() {
return s; return s;
} }
Texture *OpenGLRenderer::copyScreenshotToTexture() {
OpenGLTexture *texture = new OpenGLTexture();
Common::Rect screen = viewport();
texture->copyFromFramebuffer(screen);
return texture;
}
} // End of namespace Myst3 } // End of namespace Myst3
#endif #endif

View file

@ -55,6 +55,8 @@ public:
virtual void draw2DText(const Common::String &text, const Common::Point &position) override; virtual void draw2DText(const Common::String &text, const Common::Point &position) override;
virtual Graphics::Surface *getScreenshot() override; virtual Graphics::Surface *getScreenshot() override;
Texture *copyScreenshotToTexture() override;
private: private:
void drawFace(uint face, Texture *texture); void drawFace(uint face, Texture *texture);
}; };

View file

@ -249,6 +249,7 @@ void ShaderRenderer::drawTexturedRect2D(const Common::Rect &screenRect, const Co
_boxShader->setUniform("verSizeWH", scaled(sWidth, sHeight)); _boxShader->setUniform("verSizeWH", scaled(sWidth, sHeight));
_boxShader->setUniform("texOffsetXY", Math::Vector2d(tLeft, tTop)); _boxShader->setUniform("texOffsetXY", Math::Vector2d(tLeft, tTop));
_boxShader->setUniform("texSizeWH", Math::Vector2d(tWidth, tHeight)); _boxShader->setUniform("texSizeWH", Math::Vector2d(tWidth, tHeight));
_boxShader->setUniform("flipY", glTexture->upsideDown);
glDepthMask(GL_FALSE); glDepthMask(GL_FALSE);
@ -398,6 +399,15 @@ Graphics::Surface *ShaderRenderer::getScreenshot() {
return s; return s;
} }
Texture *ShaderRenderer::copyScreenshotToTexture() {
OpenGLTexture *texture = new OpenGLTexture();
Common::Rect screen = viewport();
texture->copyFromFramebuffer(screen);
return texture;
}
} // End of namespace Myst3 } // End of namespace Myst3
#endif #endif

View file

@ -56,6 +56,7 @@ public:
virtual void draw2DText(const Common::String &text, const Common::Point &position) override; virtual void draw2DText(const Common::String &text, const Common::Point &position) override;
virtual Graphics::Surface *getScreenshot() override; virtual Graphics::Surface *getScreenshot() override;
Texture *copyScreenshotToTexture() override;
private: private:
void setupQuadEBO(); void setupQuadEBO();

View file

@ -42,10 +42,20 @@ static uint32 upperPowerOfTwo(uint32 v) {
return v; return v;
} }
OpenGLTexture::OpenGLTexture() :
internalFormat(0),
sourceFormat(0),
internalWidth(0),
internalHeight(0),
upsideDown(false) {
glGenTextures(1, &id);
}
OpenGLTexture::OpenGLTexture(const Graphics::Surface *surface) { OpenGLTexture::OpenGLTexture(const Graphics::Surface *surface) {
width = surface->w; width = surface->w;
height = surface->h; height = surface->h;
format = surface->format; format = surface->format;
upsideDown = false;
// Pad the textures if non power of two support is unavailable // Pad the textures if non power of two support is unavailable
if (OpenGLContext.NPOTSupported) { if (OpenGLContext.NPOTSupported) {
@ -90,7 +100,7 @@ void OpenGLTexture::update(const Graphics::Surface *surface) {
updatePartial(surface, Common::Rect(surface->w, surface->h)); updatePartial(surface, Common::Rect(surface->w, surface->h));
} }
void OpenGLTexture::updateTexture(const Graphics::Surface* surface, const Common::Rect& rect) { void OpenGLTexture::updateTexture(const Graphics::Surface *surface, const Common::Rect &rect) {
assert(surface->format == format); assert(surface->format == format);
glBindTexture(GL_TEXTURE_2D, id); glBindTexture(GL_TEXTURE_2D, id);
@ -111,6 +121,28 @@ void OpenGLTexture::updatePartial(const Graphics::Surface *surface, const Common
updateTexture(surface, rect); updateTexture(surface, rect);
} }
void OpenGLTexture::copyFromFramebuffer(const Common::Rect &screen) {
internalFormat = GL_RGB;
width = screen.width();
height = screen.height();
upsideDown = true;
// Pad the textures if non power of two support is unavailable
if (OpenGLContext.NPOTSupported) {
internalHeight = height;
internalWidth = width;
} else {
internalHeight = upperPowerOfTwo(height);
internalWidth = upperPowerOfTwo(width);
}
glBindTexture(GL_TEXTURE_2D, id);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glCopyTexImage2D(GL_TEXTURE_2D, 0, internalFormat, screen.left, screen.top, internalWidth, internalHeight, 0);
}
} // End of namespace Myst3 } // End of namespace Myst3
#endif #endif

View file

@ -34,16 +34,20 @@ namespace Myst3 {
class OpenGLTexture : public Texture { class OpenGLTexture : public Texture {
public: public:
OpenGLTexture(const Graphics::Surface *surface); OpenGLTexture(const Graphics::Surface *surface);
OpenGLTexture();
virtual ~OpenGLTexture(); virtual ~OpenGLTexture();
void update(const Graphics::Surface *surface) override; void update(const Graphics::Surface *surface) override;
void updatePartial(const Graphics::Surface *surface, const Common::Rect &rect) override; void updatePartial(const Graphics::Surface *surface, const Common::Rect &rect) override;
void copyFromFramebuffer(const Common::Rect &screen);
GLuint id; GLuint id;
GLuint internalFormat; GLuint internalFormat;
GLuint sourceFormat; GLuint sourceFormat;
uint32 internalWidth; uint32 internalWidth;
uint32 internalHeight; uint32 internalHeight;
bool upsideDown;
private: private:
void updateTexture(const Graphics::Surface *surface, const Common::Rect &rect); void updateTexture(const Graphics::Surface *surface, const Common::Rect &rect);

View file

@ -5,6 +5,7 @@ uniform vec2 texOffsetXY;
uniform vec2 texSizeWH; uniform vec2 texSizeWH;
uniform vec2 verOffsetXY; uniform vec2 verOffsetXY;
uniform vec2 verSizeWH; uniform vec2 verSizeWH;
uniform bool flipY;
out vec2 Texcoord; out vec2 Texcoord;
@ -17,5 +18,9 @@ void main()
pos.x = pos.x * 2.0 - 1.0; pos.x = pos.x * 2.0 - 1.0;
pos.y = -1.0 * (pos.y * 2.0 - 1.0); pos.y = -1.0 * (pos.y * 2.0 - 1.0);
if (flipY) {
pos.y *= -1.0;
}
gl_Position = vec4(pos, 0.0, 1.0); gl_Position = vec4(pos, 0.0, 1.0);
} }

View file

@ -38,19 +38,15 @@ Transition::Transition(Myst3Engine *vm) :
_sourceScreenshot(nullptr), _sourceScreenshot(nullptr),
_frameLimiter(new FrameLimiter(g_system, ConfMan.getInt("engine_speed"))) { _frameLimiter(new FrameLimiter(g_system, ConfMan.getInt("engine_speed"))) {
int transitionSpeed = ConfMan.getInt("transition_speed");
// Capture a screenshot of the source node // Capture a screenshot of the source node
if (transitionSpeed != 100) { int durationTicks = computeDuration();
_sourceScreenshot = _vm->_gfx->getScreenshot(); if (durationTicks) {
_sourceScreenshot = _vm->_gfx->copyScreenshotToTexture();
} }
} }
Transition::~Transition() { Transition::~Transition() {
if (_sourceScreenshot) { _vm->_gfx->freeTexture(_sourceScreenshot);
_sourceScreenshot->free();
delete _sourceScreenshot;
}
delete _frameLimiter; delete _frameLimiter;
} }
@ -87,13 +83,7 @@ void Transition::draw(TransitionType type) {
// Capture a screenshot of the destination node // Capture a screenshot of the destination node
_vm->drawFrame(true); _vm->drawFrame(true);
Graphics::Surface *target = _vm->_gfx->getScreenshot(); Texture *targetScreenshot = _vm->_gfx->copyScreenshotToTexture();
Texture *sourceTexture = _vm->_gfx->createTexture(_sourceScreenshot);
Texture *targetTexture = _vm->_gfx->createTexture(target);
target->free();
delete target;
// Compute the start and end frames for the animation // Compute the start and end frames for the animation
int startTick = _vm->_state->getTickCount(); int startTick = _vm->_state->getTickCount();
@ -109,7 +99,9 @@ void Transition::draw(TransitionType type) {
completion = CLIP<int>(100 * (_vm->_state->getTickCount() - startTick) / durationTicks, 0, 100); completion = CLIP<int>(100 * (_vm->_state->getTickCount() - startTick) / durationTicks, 0, 100);
drawStep(targetTexture, sourceTexture, completion); _vm->_gfx->clear();
drawStep(targetScreenshot, _sourceScreenshot, completion);
_vm->_gfx->flipBuffer(); _vm->_gfx->flipBuffer();
_frameLimiter->delayBeforeSwap(); _frameLimiter->delayBeforeSwap();
@ -132,8 +124,9 @@ void Transition::draw(TransitionType type) {
} }
} }
_vm->_gfx->freeTexture(sourceTexture); _vm->_gfx->freeTexture(targetScreenshot);
_vm->_gfx->freeTexture(targetTexture); _vm->_gfx->freeTexture(_sourceScreenshot);
_sourceScreenshot = nullptr;
} }
void Transition::drawStep(Texture *targetTexture, Texture *sourceTexture, uint completion) { void Transition::drawStep(Texture *targetTexture, Texture *sourceTexture, uint completion) {

View file

@ -47,7 +47,7 @@ private:
FrameLimiter *_frameLimiter; FrameLimiter *_frameLimiter;
TransitionType _type; TransitionType _type;
Graphics::Surface *_sourceScreenshot; Texture *_sourceScreenshot;
}; };