Initial commit
This commit is contained in:
316
backends/graphics/opengl/framebuffer.cpp
Normal file
316
backends/graphics/opengl/framebuffer.cpp
Normal file
@@ -0,0 +1,316 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "backends/graphics/opengl/framebuffer.h"
|
||||
#include "backends/graphics/opengl/pipelines/pipeline.h"
|
||||
#include "graphics/opengl/debug.h"
|
||||
#include "graphics/opengl/texture.h"
|
||||
#include "common/rotationmode.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
Framebuffer::Framebuffer()
|
||||
: _viewport(), _projectionMatrix(), _pipeline(nullptr), _clearColor(),
|
||||
_blendState(kBlendModeDisabled), _scissorTestState(false), _scissorBox() {
|
||||
}
|
||||
|
||||
void Framebuffer::activate(Pipeline *pipeline) {
|
||||
assert(pipeline);
|
||||
_pipeline = pipeline;
|
||||
|
||||
applyViewport();
|
||||
applyProjectionMatrix();
|
||||
applyClearColor();
|
||||
applyBlendState();
|
||||
applyScissorTestState();
|
||||
applyScissorBox();
|
||||
|
||||
activateInternal();
|
||||
}
|
||||
|
||||
void Framebuffer::deactivate() {
|
||||
deactivateInternal();
|
||||
|
||||
_pipeline = nullptr;
|
||||
}
|
||||
|
||||
void Framebuffer::setClearColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) {
|
||||
_clearColor[0] = r;
|
||||
_clearColor[1] = g;
|
||||
_clearColor[2] = b;
|
||||
_clearColor[3] = a;
|
||||
|
||||
// Directly apply changes when we are active.
|
||||
if (isActive()) {
|
||||
applyClearColor();
|
||||
}
|
||||
}
|
||||
|
||||
void Framebuffer::enableBlend(BlendMode mode) {
|
||||
_blendState = mode;
|
||||
|
||||
// Directly apply changes when we are active.
|
||||
if (isActive()) {
|
||||
applyBlendState();
|
||||
}
|
||||
}
|
||||
|
||||
void Framebuffer::enableScissorTest(bool enable) {
|
||||
_scissorTestState = enable;
|
||||
|
||||
// Directly apply changes when we are active.
|
||||
if (isActive()) {
|
||||
applyScissorTestState();
|
||||
}
|
||||
}
|
||||
|
||||
void Framebuffer::setScissorBox(GLint x, GLint y, GLsizei w, GLsizei h) {
|
||||
_scissorBox[0] = x;
|
||||
_scissorBox[1] = y;
|
||||
_scissorBox[2] = w;
|
||||
_scissorBox[3] = h;
|
||||
|
||||
// Directly apply changes when we are active.
|
||||
if (isActive()) {
|
||||
applyScissorBox();
|
||||
}
|
||||
}
|
||||
|
||||
void Framebuffer::applyViewport() {
|
||||
GL_CALL(glViewport(_viewport[0], _viewport[1], _viewport[2], _viewport[3]));
|
||||
}
|
||||
|
||||
void Framebuffer::applyProjectionMatrix() {
|
||||
assert(_pipeline);
|
||||
_pipeline->setProjectionMatrix(_projectionMatrix);
|
||||
}
|
||||
|
||||
void Framebuffer::applyClearColor() {
|
||||
GL_CALL(glClearColor(_clearColor[0], _clearColor[1], _clearColor[2], _clearColor[3]));
|
||||
}
|
||||
|
||||
void Framebuffer::applyBlendState() {
|
||||
switch (_blendState) {
|
||||
case kBlendModeDisabled:
|
||||
GL_CALL(glDisable(GL_BLEND));
|
||||
break;
|
||||
case kBlendModeOpaque:
|
||||
if (!glBlendColor) {
|
||||
// If glBlendColor is not available (old OpenGL) fallback on disabling blending
|
||||
GL_CALL(glDisable(GL_BLEND));
|
||||
break;
|
||||
}
|
||||
GL_CALL(glEnable(GL_BLEND));
|
||||
GL_CALL(glBlendColor(1.f, 1.f, 1.f, 0.f));
|
||||
GL_CALL(glBlendFunc(GL_CONSTANT_COLOR, GL_ONE_MINUS_CONSTANT_COLOR));
|
||||
break;
|
||||
case kBlendModeTraditionalTransparency:
|
||||
GL_CALL(glEnable(GL_BLEND));
|
||||
GL_CALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
|
||||
break;
|
||||
case kBlendModePremultipliedTransparency:
|
||||
GL_CALL(glEnable(GL_BLEND));
|
||||
GL_CALL(glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA));
|
||||
break;
|
||||
case kBlendModeAdditive:
|
||||
GL_CALL(glEnable(GL_BLEND));
|
||||
GL_CALL(glBlendFunc(GL_ONE, GL_ONE));
|
||||
break;
|
||||
case kBlendModeMaskAlphaAndInvertByColor:
|
||||
GL_CALL(glEnable(GL_BLEND));
|
||||
GL_CALL(glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Framebuffer::applyScissorTestState() {
|
||||
if (_scissorTestState) {
|
||||
GL_CALL(glEnable(GL_SCISSOR_TEST));
|
||||
} else {
|
||||
GL_CALL(glDisable(GL_SCISSOR_TEST));
|
||||
}
|
||||
}
|
||||
|
||||
void Framebuffer::applyScissorBox() {
|
||||
GL_CALL(glScissor(_scissorBox[0], _scissorBox[1], _scissorBox[2], _scissorBox[3]));
|
||||
}
|
||||
|
||||
void Framebuffer::copyRenderStateFrom(const Framebuffer &other, uint copyMask) {
|
||||
if (copyMask & kCopyMaskClearColor) {
|
||||
memcpy(_clearColor, other._clearColor, sizeof(_clearColor));
|
||||
}
|
||||
if (copyMask & kCopyMaskBlendState) {
|
||||
_blendState = other._blendState;
|
||||
}
|
||||
if (copyMask & kCopyMaskScissorState) {
|
||||
_scissorTestState = other._scissorTestState;
|
||||
}
|
||||
if (copyMask & kCopyMaskScissorBox) {
|
||||
memcpy(_scissorBox, other._scissorBox, sizeof(_scissorBox));
|
||||
}
|
||||
|
||||
if (isActive()) {
|
||||
applyClearColor();
|
||||
applyBlendState();
|
||||
applyScissorTestState();
|
||||
applyScissorBox();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Backbuffer implementation
|
||||
//
|
||||
|
||||
void Backbuffer::activateInternal() {
|
||||
#if !USE_FORCED_GLES
|
||||
if (OpenGLContext.framebufferObjectSupported) {
|
||||
GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, 0));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Backbuffer::setSize(uint width, uint height) {
|
||||
// Set viewport dimensions.
|
||||
_viewport[0] = 0;
|
||||
_viewport[1] = 0;
|
||||
_viewport[2] = width;
|
||||
_viewport[3] = height;
|
||||
|
||||
// Setup orthogonal projection matrix.
|
||||
_projectionMatrix(0, 0) = 2.0f / width;
|
||||
_projectionMatrix(0, 1) = 0.0f;
|
||||
_projectionMatrix(0, 2) = 0.0f;
|
||||
_projectionMatrix(0, 3) = 0.0f;
|
||||
|
||||
_projectionMatrix(1, 0) = 0.0f;
|
||||
_projectionMatrix(1, 1) = -2.0f / height;
|
||||
_projectionMatrix(1, 2) = 0.0f;
|
||||
_projectionMatrix(1, 3) = 0.0f;
|
||||
|
||||
_projectionMatrix(2, 0) = 0.0f;
|
||||
_projectionMatrix(2, 1) = 0.0f;
|
||||
_projectionMatrix(2, 2) = 0.0f;
|
||||
_projectionMatrix(2, 3) = 0.0f;
|
||||
|
||||
_projectionMatrix(3, 0) = -1.0f;
|
||||
_projectionMatrix(3, 1) = 1.0f;
|
||||
_projectionMatrix(3, 2) = 0.0f;
|
||||
_projectionMatrix(3, 3) = 1.0f;
|
||||
|
||||
// Directly apply changes when we are active.
|
||||
if (isActive()) {
|
||||
applyViewport();
|
||||
applyProjectionMatrix();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// Render to texture target implementation
|
||||
//
|
||||
|
||||
#if !USE_FORCED_GLES
|
||||
TextureTarget::TextureTarget()
|
||||
: _texture(new Texture(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE)), _glFBO(0), _needUpdate(true) {
|
||||
}
|
||||
|
||||
TextureTarget::~TextureTarget() {
|
||||
delete _texture;
|
||||
GL_CALL_SAFE(glDeleteFramebuffers, (1, &_glFBO));
|
||||
}
|
||||
|
||||
void TextureTarget::activateInternal() {
|
||||
// Allocate framebuffer object if necessary.
|
||||
if (!_glFBO) {
|
||||
GL_CALL(glGenFramebuffers(1, &_glFBO));
|
||||
_needUpdate = true;
|
||||
}
|
||||
|
||||
// Attach destination texture to FBO.
|
||||
GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, _glFBO));
|
||||
|
||||
// If required attach texture to FBO.
|
||||
if (_needUpdate) {
|
||||
GL_CALL(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _texture->getGLTexture(), 0));
|
||||
_needUpdate = false;
|
||||
}
|
||||
}
|
||||
|
||||
void TextureTarget::destroy() {
|
||||
GL_CALL(glDeleteFramebuffers(1, &_glFBO));
|
||||
_glFBO = 0;
|
||||
|
||||
_texture->destroy();
|
||||
}
|
||||
|
||||
void TextureTarget::create() {
|
||||
_texture->create();
|
||||
|
||||
_needUpdate = true;
|
||||
}
|
||||
|
||||
bool TextureTarget::setSize(uint width, uint height) {
|
||||
if (!_texture->setSize(width, height)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint texWidth = _texture->getWidth();
|
||||
const uint texHeight = _texture->getHeight();
|
||||
|
||||
// Set viewport dimensions.
|
||||
_viewport[0] = 0;
|
||||
_viewport[1] = 0;
|
||||
_viewport[2] = texWidth;
|
||||
_viewport[3] = texHeight;
|
||||
|
||||
// Setup orthogonal projection matrix.
|
||||
_projectionMatrix(0, 0) = 2.0f / texWidth;
|
||||
_projectionMatrix(0, 1) = 0.0f;
|
||||
_projectionMatrix(0, 2) = 0.0f;
|
||||
_projectionMatrix(0, 3) = 0.0f;
|
||||
|
||||
_projectionMatrix(1, 0) = 0.0f;
|
||||
_projectionMatrix(1, 1) = 2.0f / texHeight;
|
||||
_projectionMatrix(1, 2) = 0.0f;
|
||||
_projectionMatrix(1, 3) = 0.0f;
|
||||
|
||||
_projectionMatrix(2, 0) = 0.0f;
|
||||
_projectionMatrix(2, 1) = 0.0f;
|
||||
_projectionMatrix(2, 2) = 0.0f;
|
||||
_projectionMatrix(2, 3) = 0.0f;
|
||||
|
||||
_projectionMatrix(3, 0) = -1.0f;
|
||||
_projectionMatrix(3, 1) = -1.0f;
|
||||
_projectionMatrix(3, 2) = 0.0f;
|
||||
_projectionMatrix(3, 3) = 1.0f;
|
||||
|
||||
// Directly apply changes when we are active.
|
||||
if (isActive()) {
|
||||
applyViewport();
|
||||
applyProjectionMatrix();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif // !USE_FORCED_GLES
|
||||
|
||||
} // End of namespace OpenGL
|
||||
240
backends/graphics/opengl/framebuffer.h
Normal file
240
backends/graphics/opengl/framebuffer.h
Normal file
@@ -0,0 +1,240 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_GRAPHICS_OPENGL_FRAMEBUFFER_H
|
||||
#define BACKENDS_GRAPHICS_OPENGL_FRAMEBUFFER_H
|
||||
|
||||
#include "graphics/opengl/system_headers.h"
|
||||
|
||||
#include "math/matrix4.h"
|
||||
|
||||
#include "common/rotationmode.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
class Pipeline;
|
||||
|
||||
/**
|
||||
* Object describing a framebuffer OpenGL can render to.
|
||||
*/
|
||||
class Framebuffer {
|
||||
public:
|
||||
Framebuffer();
|
||||
virtual ~Framebuffer() {};
|
||||
|
||||
public:
|
||||
enum BlendMode {
|
||||
/**
|
||||
* Newly drawn pixels overwrite the existing contents of the framebuffer
|
||||
* without mixing with them.
|
||||
*/
|
||||
kBlendModeDisabled,
|
||||
|
||||
/**
|
||||
* Newly drawn pixels overwrite the existing contents of the framebuffer
|
||||
* without mixing with them. Alpha channel is discarded.
|
||||
*/
|
||||
kBlendModeOpaque,
|
||||
|
||||
/**
|
||||
* Newly drawn pixels mix with the framebuffer based on their alpha value
|
||||
* for transparency.
|
||||
*/
|
||||
kBlendModeTraditionalTransparency,
|
||||
|
||||
/**
|
||||
* Newly drawn pixels mix with the framebuffer based on their alpha value
|
||||
* for transparency.
|
||||
*
|
||||
* Requires the image data being drawn to have its color values pre-multiplied
|
||||
* with the alpha value.
|
||||
*/
|
||||
kBlendModePremultipliedTransparency,
|
||||
|
||||
/**
|
||||
* Newly drawn pixels add to the destination value.
|
||||
*/
|
||||
kBlendModeAdditive,
|
||||
|
||||
/**
|
||||
* Newly drawn pixels mask out existing pixels based on the alpha value and
|
||||
* add inversions of the pixels based on the color.
|
||||
*/
|
||||
kBlendModeMaskAlphaAndInvertByColor,
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the clear color of the framebuffer.
|
||||
*/
|
||||
void setClearColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a);
|
||||
|
||||
/**
|
||||
* Enable/disable GL_BLEND.
|
||||
*/
|
||||
void enableBlend(BlendMode mode);
|
||||
|
||||
/**
|
||||
* Enable/disable GL_SCISSOR_TEST.
|
||||
*/
|
||||
void enableScissorTest(bool enable);
|
||||
|
||||
/**
|
||||
* Set scissor box dimensions.
|
||||
*/
|
||||
void setScissorBox(GLint x, GLint y, GLsizei w, GLsizei h);
|
||||
|
||||
/**
|
||||
* Obtain projection matrix of the framebuffer.
|
||||
*/
|
||||
const Math::Matrix4 &getProjectionMatrix() const { return _projectionMatrix; }
|
||||
|
||||
enum CopyMask {
|
||||
kCopyMaskClearColor = (1 << 0),
|
||||
kCopyMaskBlendState = (1 << 1),
|
||||
kCopyMaskScissorState = (1 << 2),
|
||||
kCopyMaskScissorBox = (1 << 4),
|
||||
|
||||
kCopyMaskAll = kCopyMaskClearColor | kCopyMaskBlendState |
|
||||
kCopyMaskScissorState | kCopyMaskScissorBox,
|
||||
};
|
||||
|
||||
/**
|
||||
* Copy rendering state from another framebuffer
|
||||
*/
|
||||
void copyRenderStateFrom(const Framebuffer &other, uint copyMask);
|
||||
|
||||
protected:
|
||||
bool isActive() const { return _pipeline != nullptr; }
|
||||
|
||||
GLint _viewport[4];
|
||||
void applyViewport();
|
||||
|
||||
Math::Matrix4 _projectionMatrix;
|
||||
void applyProjectionMatrix();
|
||||
|
||||
/**
|
||||
* Activate framebuffer.
|
||||
*
|
||||
* This is supposed to set all state associated with the framebuffer.
|
||||
*/
|
||||
virtual void activateInternal() = 0;
|
||||
|
||||
/**
|
||||
* Deactivate framebuffer.
|
||||
*
|
||||
* This is supposed to make any cleanup required when unbinding the
|
||||
* framebuffer.
|
||||
*/
|
||||
virtual void deactivateInternal() {}
|
||||
|
||||
public:
|
||||
/**
|
||||
* Set the size of the target buffer.
|
||||
*/
|
||||
virtual bool setSize(uint width, uint height) = 0;
|
||||
|
||||
/**
|
||||
* Accessor to activate framebuffer for pipeline.
|
||||
*/
|
||||
void activate(Pipeline *pipeline);
|
||||
|
||||
/**
|
||||
* Accessor to deactivate framebuffer from pipeline.
|
||||
*/
|
||||
void deactivate();
|
||||
|
||||
private:
|
||||
Pipeline *_pipeline;
|
||||
|
||||
GLfloat _clearColor[4];
|
||||
void applyClearColor();
|
||||
|
||||
BlendMode _blendState;
|
||||
void applyBlendState();
|
||||
|
||||
bool _scissorTestState;
|
||||
void applyScissorTestState();
|
||||
|
||||
GLint _scissorBox[4];
|
||||
void applyScissorBox();
|
||||
};
|
||||
|
||||
/**
|
||||
* Default back buffer implementation.
|
||||
*/
|
||||
class Backbuffer : public Framebuffer {
|
||||
public:
|
||||
/**
|
||||
* Set the size of the back buffer.
|
||||
*/
|
||||
bool setSize(uint width, uint height) override;
|
||||
|
||||
protected:
|
||||
void activateInternal() override;
|
||||
};
|
||||
|
||||
#if !USE_FORCED_GLES
|
||||
class Texture;
|
||||
|
||||
/**
|
||||
* Render to texture framebuffer implementation.
|
||||
*
|
||||
* This target allows to render to a texture, which can then be used for
|
||||
* further rendering.
|
||||
*/
|
||||
class TextureTarget : public Framebuffer {
|
||||
public:
|
||||
TextureTarget();
|
||||
~TextureTarget() override;
|
||||
|
||||
/**
|
||||
* Notify that the GL context is about to be destroyed.
|
||||
*/
|
||||
void destroy();
|
||||
|
||||
/**
|
||||
* Notify that the GL context has been created.
|
||||
*/
|
||||
void create();
|
||||
|
||||
/**
|
||||
* Set size of the texture target.
|
||||
*/
|
||||
bool setSize(uint width, uint height) override;
|
||||
|
||||
/**
|
||||
* Query pointer to underlying GL texture.
|
||||
*/
|
||||
Texture *getTexture() const { return _texture; }
|
||||
|
||||
protected:
|
||||
void activateInternal() override;
|
||||
|
||||
private:
|
||||
Texture *_texture;
|
||||
GLuint _glFBO;
|
||||
bool _needUpdate;
|
||||
};
|
||||
#endif
|
||||
|
||||
} // End of namespace OpenGL
|
||||
|
||||
#endif
|
||||
2066
backends/graphics/opengl/opengl-graphics.cpp
Normal file
2066
backends/graphics/opengl/opengl-graphics.cpp
Normal file
File diff suppressed because it is too large
Load Diff
538
backends/graphics/opengl/opengl-graphics.h
Normal file
538
backends/graphics/opengl/opengl-graphics.h
Normal file
@@ -0,0 +1,538 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_GRAPHICS_OPENGL_OPENGL_GRAPHICS_H
|
||||
#define BACKENDS_GRAPHICS_OPENGL_OPENGL_GRAPHICS_H
|
||||
|
||||
#include "backends/graphics/opengl/framebuffer.h"
|
||||
#include "backends/graphics/windowed.h"
|
||||
|
||||
#include "base/plugins.h"
|
||||
|
||||
#include "common/frac.h"
|
||||
#include "common/mutex.h"
|
||||
#include "common/ustr.h"
|
||||
|
||||
#include "graphics/surface.h"
|
||||
|
||||
namespace Graphics {
|
||||
class Font;
|
||||
} // End of namespace Graphics
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
// HACK: We use glColor in the OSD code. This might not be working on GL ES but
|
||||
// we still enable it because Tizen already shipped with it. Also, the
|
||||
// SurfaceSDL backend enables it and disabling it can cause issues in sdl.cpp.
|
||||
#define USE_OSD 1
|
||||
|
||||
class Surface;
|
||||
class Pipeline;
|
||||
#if !USE_FORCED_GLES
|
||||
class LibRetroPipeline;
|
||||
#endif
|
||||
#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
|
||||
class Renderer3D;
|
||||
#endif
|
||||
|
||||
enum {
|
||||
GFX_OPENGL = 0
|
||||
};
|
||||
|
||||
class OpenGLGraphicsManager : virtual public WindowedGraphicsManager {
|
||||
public:
|
||||
OpenGLGraphicsManager();
|
||||
virtual ~OpenGLGraphicsManager();
|
||||
|
||||
// GraphicsManager API
|
||||
bool hasFeature(OSystem::Feature f) const override;
|
||||
void setFeatureState(OSystem::Feature f, bool enable) override;
|
||||
bool getFeatureState(OSystem::Feature f) const override;
|
||||
|
||||
const OSystem::GraphicsMode *getSupportedGraphicsModes() const override;
|
||||
int getDefaultGraphicsMode() const override;
|
||||
bool setGraphicsMode(int mode, uint flags = OSystem::kGfxModeNoFlags) override;
|
||||
int getGraphicsMode() const override;
|
||||
|
||||
#ifdef USE_RGB_COLOR
|
||||
Graphics::PixelFormat getScreenFormat() const override;
|
||||
Common::List<Graphics::PixelFormat> getSupportedFormats() const override;
|
||||
#endif
|
||||
|
||||
const OSystem::GraphicsMode *getSupportedStretchModes() const override;
|
||||
int getDefaultStretchMode() const override;
|
||||
bool setStretchMode(int mode) override;
|
||||
int getStretchMode() const override;
|
||||
|
||||
#ifdef USE_SCALERS
|
||||
uint getDefaultScaler() const override;
|
||||
uint getDefaultScaleFactor() const override;
|
||||
bool setScaler(uint mode, int factor) override;
|
||||
uint getScaler() const override;
|
||||
uint getScaleFactor() const override;
|
||||
#endif
|
||||
|
||||
#if !USE_FORCED_GLES
|
||||
bool setShader(const Common::Path &fileNode) override;
|
||||
#endif
|
||||
|
||||
void beginGFXTransaction() override;
|
||||
OSystem::TransactionError endGFXTransaction() override;
|
||||
|
||||
int getScreenChangeID() const override;
|
||||
|
||||
void initSize(uint width, uint height, const Graphics::PixelFormat *format) override;
|
||||
|
||||
int16 getWidth() const override;
|
||||
int16 getHeight() const override;
|
||||
|
||||
void copyRectToScreen(const void *buf, int pitch, int x, int y, int w, int h) override;
|
||||
void fillScreen(uint32 col) override;
|
||||
void fillScreen(const Common::Rect &r, uint32 col) override;
|
||||
|
||||
void updateScreen() override;
|
||||
void presentBuffer() override;
|
||||
|
||||
Graphics::Surface *lockScreen() override;
|
||||
void unlockScreen() override;
|
||||
|
||||
void setFocusRectangle(const Common::Rect& rect) override;
|
||||
void clearFocusRectangle() override;
|
||||
|
||||
int16 getOverlayWidth() const override;
|
||||
int16 getOverlayHeight() const override;
|
||||
void showOverlay(bool inGUI) override;
|
||||
void hideOverlay() override;
|
||||
|
||||
Graphics::PixelFormat getOverlayFormat() const override;
|
||||
|
||||
void copyRectToOverlay(const void *buf, int pitch, int x, int y, int w, int h) override;
|
||||
void clearOverlay() override;
|
||||
void grabOverlay(Graphics::Surface &surface) const override;
|
||||
|
||||
void setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format, const byte *mask) override;
|
||||
void setCursorPalette(const byte *colors, uint start, uint num) override;
|
||||
|
||||
void displayMessageOnOSD(const Common::U32String &msg) override;
|
||||
void displayActivityIconOnOSD(const Graphics::Surface *icon) override;
|
||||
|
||||
// PaletteManager interface
|
||||
void setPalette(const byte *colors, uint start, uint num) override;
|
||||
void grabPalette(byte *colors, uint start, uint num) const override;
|
||||
|
||||
protected:
|
||||
void renderCursor();
|
||||
|
||||
/**
|
||||
* Whether a GLES or GLES2 context is active.
|
||||
*/
|
||||
bool isGLESContext() const { return OpenGLContext.type == kContextGLES || OpenGLContext.type == kContextGLES2; }
|
||||
|
||||
/**
|
||||
* Notify the manager of a OpenGL context change. This should be the first
|
||||
* thing to call after you created an OpenGL (ES) context!
|
||||
*
|
||||
* @param type Type of the OpenGL (ES) contexts created.
|
||||
* @param defaultFormat The new default format for the game screen
|
||||
* (this is used for the CLUT8 game screens).
|
||||
* @param defaultFormatAlpha The new default format with an alpha channel
|
||||
* (this is used for the overlay and cursor).
|
||||
*/
|
||||
void notifyContextCreate(
|
||||
ContextType type,
|
||||
Framebuffer *target,
|
||||
const Graphics::PixelFormat &defaultFormat,
|
||||
const Graphics::PixelFormat &defaultFormatAlpha);
|
||||
|
||||
/**
|
||||
* Notify the manager that the OpenGL context is about to be destroyed.
|
||||
* This will free up/reset internal OpenGL related state and *must* be
|
||||
* called whenever a context might be created again after destroying a
|
||||
* context.
|
||||
*/
|
||||
void notifyContextDestroy();
|
||||
|
||||
/**
|
||||
* Create a surface with the specified pixel format.
|
||||
*
|
||||
* @param format The pixel format the Surface object should accept as
|
||||
* input.
|
||||
* @param wantAlpha For CLUT8 surfaces this marks whether an alpha
|
||||
* channel should be used.
|
||||
* @param wantScaler Whether or not a software scaler should be used.
|
||||
* @return A pointer to the surface or nullptr on failure.
|
||||
*/
|
||||
Surface *createSurface(const Graphics::PixelFormat &format, bool wantAlpha = false, bool wantScaler = false, bool wantMask = false);
|
||||
|
||||
//
|
||||
// Transaction support
|
||||
//
|
||||
struct VideoState {
|
||||
VideoState() : valid(false), gameWidth(0), gameHeight(0),
|
||||
#ifdef USE_RGB_COLOR
|
||||
gameFormat(),
|
||||
#endif
|
||||
aspectRatioCorrection(false), graphicsMode(GFX_OPENGL), flags(0),
|
||||
filtering(true), scalerIndex(0), scaleFactor(1), shader() {
|
||||
}
|
||||
|
||||
bool valid;
|
||||
|
||||
uint gameWidth, gameHeight;
|
||||
#ifdef USE_RGB_COLOR
|
||||
Graphics::PixelFormat gameFormat;
|
||||
#endif
|
||||
bool aspectRatioCorrection;
|
||||
int graphicsMode;
|
||||
uint flags;
|
||||
bool filtering;
|
||||
|
||||
uint scalerIndex;
|
||||
int scaleFactor;
|
||||
|
||||
Common::Path shader;
|
||||
|
||||
bool operator==(const VideoState &right) {
|
||||
return gameWidth == right.gameWidth && gameHeight == right.gameHeight
|
||||
#ifdef USE_RGB_COLOR
|
||||
&& gameFormat == right.gameFormat
|
||||
#endif
|
||||
&& aspectRatioCorrection == right.aspectRatioCorrection
|
||||
&& graphicsMode == right.graphicsMode
|
||||
&& flags == right.flags
|
||||
&& filtering == right.filtering
|
||||
&& shader == right.shader;
|
||||
}
|
||||
|
||||
bool operator!=(const VideoState &right) {
|
||||
return !(*this == right);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The currently set up video state.
|
||||
*/
|
||||
VideoState _currentState;
|
||||
|
||||
/**
|
||||
* The old video state used when doing a transaction rollback.
|
||||
*/
|
||||
VideoState _oldState;
|
||||
|
||||
protected:
|
||||
enum TransactionMode {
|
||||
kTransactionNone = 0,
|
||||
kTransactionActive = 1,
|
||||
kTransactionRollback = 2
|
||||
};
|
||||
|
||||
TransactionMode getTransactionMode() const { return _transactionMode; }
|
||||
|
||||
private:
|
||||
/**
|
||||
* The current transaction mode.
|
||||
*/
|
||||
TransactionMode _transactionMode;
|
||||
|
||||
/**
|
||||
* The current screen change ID.
|
||||
*/
|
||||
int _screenChangeID;
|
||||
|
||||
/**
|
||||
* The current stretch mode.
|
||||
*/
|
||||
int _stretchMode;
|
||||
|
||||
/**
|
||||
* Scaled version of _gameScreenShakeXOffset and _gameScreenShakeYOffset (as a Common::Point)
|
||||
*/
|
||||
Common::Point _shakeOffsetScaled;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Set up the requested video mode. This takes parameters which describe
|
||||
* what resolution the game screen requests (this is possibly aspect ratio
|
||||
* corrected!).
|
||||
*
|
||||
* A sub-class should take these parameters as hints. It might very well
|
||||
* set up a mode which it thinks suites the situation best.
|
||||
*
|
||||
* @parma requestedWidth This is the requested actual game screen width.
|
||||
* @param requestedHeight This is the requested actual game screen height.
|
||||
* @param resizable This indicates that the window should not be resized because we can't handle it.
|
||||
* @param antialiasing This is the requested antialiasing level.
|
||||
* @return true on success, false otherwise
|
||||
*/
|
||||
virtual bool loadVideoMode(uint requestedWidth, uint requestedHeight, bool resizable, int antialiasing) = 0;
|
||||
|
||||
bool loadShader(const Common::Path &fileName);
|
||||
|
||||
/**
|
||||
* Refresh the screen contents.
|
||||
*/
|
||||
virtual void refreshScreen() = 0;
|
||||
|
||||
/**
|
||||
* Saves a screenshot of the entire window, excluding window decorations.
|
||||
*
|
||||
* @param filename The output filename.
|
||||
* @return true on success, false otherwise
|
||||
*/
|
||||
bool saveScreenshot(const Common::Path &filename) const;
|
||||
|
||||
// Do not hide the argument-less saveScreenshot from the base class
|
||||
using WindowedGraphicsManager::saveScreenshot;
|
||||
|
||||
private:
|
||||
//
|
||||
// OpenGL utilities
|
||||
//
|
||||
|
||||
/**
|
||||
* Initialize the active context for use.
|
||||
*/
|
||||
void initializeGLContext();
|
||||
|
||||
/**
|
||||
* OpenGL pipeline used for rendering.
|
||||
*/
|
||||
Pipeline *_pipeline;
|
||||
|
||||
#if !USE_FORCED_GLES
|
||||
/**
|
||||
* OpenGL pipeline used for post-processing.
|
||||
*/
|
||||
LibRetroPipeline *_libretroPipeline;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Try to determine the internal parameters for a given pixel format.
|
||||
*
|
||||
* @return true when the format can be used, false otherwise.
|
||||
*/
|
||||
bool getGLPixelFormat(const Graphics::PixelFormat &pixelFormat, GLenum &glIntFormat, GLenum &glFormat, GLenum &glType) const;
|
||||
|
||||
bool gameNeedsAspectRatioCorrection() const override;
|
||||
int getGameRenderScale() const override;
|
||||
void recalculateDisplayAreas() override;
|
||||
void handleResizeImpl(const int width, const int height) override;
|
||||
|
||||
void updateTextureSettings();
|
||||
|
||||
Pipeline *getPipeline() const { return _pipeline; }
|
||||
|
||||
/**
|
||||
* The default pixel format of the backend.
|
||||
*/
|
||||
Graphics::PixelFormat _defaultFormat;
|
||||
|
||||
/**
|
||||
* The default pixel format with an alpha channel.
|
||||
*/
|
||||
Graphics::PixelFormat _defaultFormatAlpha;
|
||||
|
||||
/**
|
||||
* Render target.
|
||||
*/
|
||||
Framebuffer *_targetBuffer;
|
||||
|
||||
/**
|
||||
* The rendering surface for the virtual game screen.
|
||||
*/
|
||||
Surface *_gameScreen;
|
||||
|
||||
#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
|
||||
/**
|
||||
* The rendering helper for 3D games.
|
||||
*/
|
||||
Renderer3D *_renderer3d;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* The game palette if in CLUT8 mode.
|
||||
*/
|
||||
byte _gamePalette[3 * 256];
|
||||
|
||||
//
|
||||
// Overlay
|
||||
//
|
||||
|
||||
/**
|
||||
* The rendering surface for the overlay.
|
||||
*/
|
||||
Surface *_overlay;
|
||||
|
||||
//
|
||||
// Cursor
|
||||
//
|
||||
|
||||
/**
|
||||
* Set up the correct cursor palette.
|
||||
*/
|
||||
void updateCursorPalette();
|
||||
|
||||
/**
|
||||
* The rendering surface for the mouse cursor.
|
||||
*/
|
||||
Surface *_cursor;
|
||||
|
||||
/**
|
||||
* The rendering surface for the opacity and inversion mask (if any)
|
||||
*/
|
||||
Surface *_cursorMask;
|
||||
|
||||
/**
|
||||
* The X offset for the cursor hotspot in unscaled game coordinates.
|
||||
*/
|
||||
int _cursorHotspotX;
|
||||
|
||||
/**
|
||||
* The Y offset for the cursor hotspot in unscaled game coordinates.
|
||||
*/
|
||||
int _cursorHotspotY;
|
||||
|
||||
/**
|
||||
* Recalculate the cursor scaling. Scaling is always done according to
|
||||
* the game screen.
|
||||
*/
|
||||
void recalculateCursorScaling();
|
||||
|
||||
/**
|
||||
* The X offset for the cursor hotspot in scaled game display area
|
||||
* coordinates.
|
||||
*/
|
||||
int _cursorHotspotXScaled;
|
||||
|
||||
/**
|
||||
* The Y offset for the cursor hotspot in scaled game display area
|
||||
* coordinates.
|
||||
*/
|
||||
int _cursorHotspotYScaled;
|
||||
|
||||
/**
|
||||
* The width of the cursor in scaled game display area coordinates.
|
||||
*/
|
||||
float _cursorWidthScaled;
|
||||
|
||||
/**
|
||||
* The height of the cursor in scaled game display area coordinates.
|
||||
*/
|
||||
float _cursorHeightScaled;
|
||||
|
||||
/**
|
||||
* The key color.
|
||||
*/
|
||||
uint32 _cursorKeyColor;
|
||||
|
||||
/**
|
||||
* If true, use key color.
|
||||
*/
|
||||
bool _cursorUseKey;
|
||||
|
||||
/**
|
||||
* Whether no cursor scaling should be applied.
|
||||
*/
|
||||
bool _cursorDontScale;
|
||||
|
||||
/**
|
||||
* Whether the special cursor palette is enabled.
|
||||
*/
|
||||
bool _cursorPaletteEnabled;
|
||||
|
||||
/**
|
||||
* The special cursor palette in case enabled.
|
||||
*/
|
||||
byte _cursorPalette[3 * 256];
|
||||
|
||||
#ifdef USE_SCALERS
|
||||
/**
|
||||
* The list of scaler plugins
|
||||
*/
|
||||
const PluginList &_scalerPlugins;
|
||||
#endif
|
||||
|
||||
#ifdef USE_OSD
|
||||
//
|
||||
// OSD
|
||||
//
|
||||
protected:
|
||||
/**
|
||||
* Returns the font used for on screen display
|
||||
*/
|
||||
virtual const Graphics::Font *getFontOSD() const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Request for the OSD icon surface to be updated.
|
||||
*/
|
||||
bool _osdMessageChangeRequest;
|
||||
|
||||
/**
|
||||
* The next OSD message.
|
||||
*
|
||||
* If this value is not empty, the OSD message will be set
|
||||
* to it on the next frame.
|
||||
*/
|
||||
Common::U32String _osdMessageNextData;
|
||||
|
||||
/**
|
||||
* Set the OSD message surface with the value of the next OSD message.
|
||||
*/
|
||||
void osdMessageUpdateSurface();
|
||||
|
||||
/**
|
||||
* The OSD message's contents.
|
||||
*/
|
||||
Surface *_osdMessageSurface;
|
||||
|
||||
/**
|
||||
* Current opacity level of the OSD message.
|
||||
*/
|
||||
uint8 _osdMessageAlpha;
|
||||
|
||||
/**
|
||||
* When fading the OSD message has started.
|
||||
*/
|
||||
uint32 _osdMessageFadeStartTime;
|
||||
|
||||
enum {
|
||||
kOSDMessageFadeOutDelay = 2 * 1000,
|
||||
kOSDMessageFadeOutDuration = 500,
|
||||
kOSDMessageInitialAlpha = 80
|
||||
};
|
||||
|
||||
/**
|
||||
* The OSD background activity icon's contents.
|
||||
*/
|
||||
Surface *_osdIconSurface;
|
||||
|
||||
enum {
|
||||
kOSDIconTopMargin = 10,
|
||||
kOSDIconRightMargin = 10
|
||||
};
|
||||
#endif
|
||||
};
|
||||
|
||||
} // End of namespace OpenGL
|
||||
|
||||
#endif
|
||||
48
backends/graphics/opengl/pipelines/clut8.cpp
Normal file
48
backends/graphics/opengl/pipelines/clut8.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "backends/graphics/opengl/pipelines/clut8.h"
|
||||
#include "backends/graphics/opengl/shader.h"
|
||||
#include "backends/graphics/opengl/framebuffer.h"
|
||||
#include "graphics/opengl/debug.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
#if !USE_FORCED_GLES
|
||||
CLUT8LookUpPipeline::CLUT8LookUpPipeline()
|
||||
: ShaderPipeline(ShaderMan.query(ShaderManager::kCLUT8LookUp)), _paletteTexture(nullptr) {
|
||||
}
|
||||
|
||||
void CLUT8LookUpPipeline::drawTextureInternal(const Texture &texture, const GLfloat *coordinates, const GLfloat *texcoords) {
|
||||
assert(isActive());
|
||||
|
||||
// Set the palette texture.
|
||||
GL_CALL(glActiveTexture(GL_TEXTURE1));
|
||||
if (_paletteTexture) {
|
||||
_paletteTexture->bind();
|
||||
}
|
||||
|
||||
GL_CALL(glActiveTexture(GL_TEXTURE0));
|
||||
ShaderPipeline::drawTextureInternal(texture, coordinates, texcoords);
|
||||
}
|
||||
#endif // !USE_FORCED_GLES
|
||||
|
||||
} // End of namespace OpenGL
|
||||
46
backends/graphics/opengl/pipelines/clut8.h
Normal file
46
backends/graphics/opengl/pipelines/clut8.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_GRAPHICS_OPENGL_PIPELINES_CLUT8_H
|
||||
#define BACKENDS_GRAPHICS_OPENGL_PIPELINES_CLUT8_H
|
||||
|
||||
#include "backends/graphics/opengl/pipelines/shader.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
#if !USE_FORCED_GLES
|
||||
class CLUT8LookUpPipeline : public ShaderPipeline {
|
||||
public:
|
||||
CLUT8LookUpPipeline();
|
||||
|
||||
void setPaletteTexture(const Texture *paletteTexture) { _paletteTexture = paletteTexture; }
|
||||
|
||||
protected:
|
||||
void drawTextureInternal(const Texture &texture, const GLfloat *coordinates, const GLfloat *texcoords) override;
|
||||
|
||||
private:
|
||||
const Texture *_paletteTexture;
|
||||
};
|
||||
#endif // !USE_FORCED_GLES
|
||||
|
||||
} // End of namespace OpenGL
|
||||
|
||||
#endif
|
||||
88
backends/graphics/opengl/pipelines/fixed.cpp
Normal file
88
backends/graphics/opengl/pipelines/fixed.cpp
Normal file
@@ -0,0 +1,88 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "backends/graphics/opengl/pipelines/fixed.h"
|
||||
#include "graphics/opengl/debug.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
#if !USE_FORCED_GLES2
|
||||
void FixedPipeline::activateInternal() {
|
||||
Pipeline::activateInternal();
|
||||
|
||||
// Disable 3D properties.
|
||||
GL_CALL(glDisable(GL_CULL_FACE));
|
||||
GL_CALL(glDisable(GL_DEPTH_TEST));
|
||||
GL_CALL(glDisable(GL_DITHER));
|
||||
|
||||
GL_CALL(glDisable(GL_LIGHTING));
|
||||
GL_CALL(glDisable(GL_FOG));
|
||||
GL_CALL(glShadeModel(GL_FLAT));
|
||||
GL_CALL(glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST));
|
||||
|
||||
GL_CALL(glEnableClientState(GL_VERTEX_ARRAY));
|
||||
GL_CALL(glEnableClientState(GL_TEXTURE_COORD_ARRAY));
|
||||
|
||||
#if !USE_FORCED_GLES
|
||||
if (OpenGLContext.multitextureSupported) {
|
||||
GL_CALL(glActiveTexture(GL_TEXTURE0));
|
||||
}
|
||||
#endif
|
||||
GL_CALL(glEnable(GL_TEXTURE_2D));
|
||||
GL_CALL(glColor4f(_r, _g, _b, _a));
|
||||
}
|
||||
|
||||
void FixedPipeline::setColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) {
|
||||
_r = r;
|
||||
_g = g;
|
||||
_b = b;
|
||||
_a = a;
|
||||
|
||||
if (isActive()) {
|
||||
GL_CALL(glColor4f(r, g, b, a));
|
||||
}
|
||||
}
|
||||
|
||||
void FixedPipeline::drawTextureInternal(const Texture &texture, const GLfloat *coordinates, const GLfloat *texcoords) {
|
||||
assert(isActive());
|
||||
|
||||
texture.bind();
|
||||
|
||||
GL_CALL(glTexCoordPointer(2, GL_FLOAT, 0, texcoords));
|
||||
GL_CALL(glVertexPointer(2, GL_FLOAT, 0, coordinates));
|
||||
GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4));
|
||||
}
|
||||
|
||||
void FixedPipeline::setProjectionMatrix(const Math::Matrix4 &projectionMatrix) {
|
||||
assert(isActive());
|
||||
|
||||
GL_CALL(glMatrixMode(GL_PROJECTION));
|
||||
GL_CALL(glLoadMatrixf(projectionMatrix.getData()));
|
||||
|
||||
GL_CALL(glMatrixMode(GL_MODELVIEW));
|
||||
GL_CALL(glLoadIdentity());
|
||||
|
||||
GL_CALL(glMatrixMode(GL_TEXTURE));
|
||||
GL_CALL(glLoadIdentity());
|
||||
}
|
||||
#endif // !USE_FORCED_GLES2
|
||||
|
||||
} // End of namespace OpenGL
|
||||
48
backends/graphics/opengl/pipelines/fixed.h
Normal file
48
backends/graphics/opengl/pipelines/fixed.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_GRAPHICS_OPENGL_PIPELINES_FIXED_H
|
||||
#define BACKENDS_GRAPHICS_OPENGL_PIPELINES_FIXED_H
|
||||
|
||||
#include "backends/graphics/opengl/pipelines/pipeline.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
#if !USE_FORCED_GLES2
|
||||
class FixedPipeline : public Pipeline {
|
||||
public:
|
||||
FixedPipeline() : _r(0.f), _g(0.f), _b(0.f), _a(0.f) {}
|
||||
|
||||
void setColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) override;
|
||||
|
||||
void setProjectionMatrix(const Math::Matrix4 &projectionMatrix) override;
|
||||
|
||||
protected:
|
||||
void activateInternal() override;
|
||||
void drawTextureInternal(const Texture &texture, const GLfloat *coordinates, const GLfloat *texcoords) override;
|
||||
|
||||
GLfloat _r, _g, _b, _a;
|
||||
};
|
||||
#endif // !USE_FORCED_GLES2
|
||||
|
||||
} // End of namespace OpenGL
|
||||
|
||||
#endif
|
||||
1075
backends/graphics/opengl/pipelines/libretro.cpp
Normal file
1075
backends/graphics/opengl/pipelines/libretro.cpp
Normal file
File diff suppressed because it is too large
Load Diff
277
backends/graphics/opengl/pipelines/libretro.h
Normal file
277
backends/graphics/opengl/pipelines/libretro.h
Normal file
@@ -0,0 +1,277 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_GRAPHICS_OPENGL_PIPELINES_LIBRETRO_H
|
||||
#define BACKENDS_GRAPHICS_OPENGL_PIPELINES_LIBRETRO_H
|
||||
|
||||
#include "graphics/opengl/system_headers.h"
|
||||
|
||||
#if !USE_FORCED_GLES
|
||||
#include "backends/graphics/opengl/pipelines/shader.h"
|
||||
|
||||
#include "common/array.h"
|
||||
#include "common/fs.h"
|
||||
|
||||
namespace Graphics {
|
||||
struct Surface;
|
||||
}
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
namespace LibRetro {
|
||||
struct ShaderPreset;
|
||||
struct ShaderPass;
|
||||
} // End of namespace LibRetro
|
||||
|
||||
class TextureTarget;
|
||||
class LibRetroTextureTarget;
|
||||
|
||||
/**
|
||||
* Pipeline implementation using Libretro shader presets.
|
||||
*/
|
||||
class LibRetroPipeline : public Pipeline {
|
||||
public:
|
||||
LibRetroPipeline();
|
||||
~LibRetroPipeline() override;
|
||||
|
||||
void setColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) override;
|
||||
void setProjectionMatrix(const Math::Matrix4 &projectionMatrix) override;
|
||||
|
||||
bool open(const Common::Path &shaderPath, Common::SearchSet &archSet);
|
||||
void close();
|
||||
|
||||
/* Called by OpenGLGraphicsManager */
|
||||
void enableLinearFiltering(bool enabled) { _linearFiltering = enabled; }
|
||||
void setRotation(Common::RotationMode rotation) { if (_rotation != rotation) { _rotation = rotation; setPipelineState(); } }
|
||||
/* Called by OpenGLGraphicsManager to setup the internal objects sizes */
|
||||
void setDisplaySizes(uint inputWidth, uint inputHeight, const Common::Rect &outputRect);
|
||||
/* Called by OpenGLGraphicsManager to indicate that next draws need to be scaled. */
|
||||
void beginScaling();
|
||||
/* Called by OpenGLGraphicsManager to indicate that next draws don't need to be scaled.
|
||||
* This must be called to execute scaling. */
|
||||
void finishScaling();
|
||||
bool isAnimated() const { return _isAnimated; }
|
||||
|
||||
static bool isSupportedByContext() {
|
||||
return OpenGLContext.shadersSupported
|
||||
&& OpenGLContext.multitextureSupported
|
||||
&& OpenGLContext.framebufferObjectSupported;
|
||||
}
|
||||
private:
|
||||
void activateInternal() override;
|
||||
void deactivateInternal() override;
|
||||
void drawTextureInternal(const Texture &texture, const GLfloat *coordinates, const GLfloat *texcoords) override;
|
||||
|
||||
bool loadTextures(Common::SearchSet &archSet);
|
||||
bool loadPasses(Common::SearchSet &archSet);
|
||||
|
||||
void setPipelineState();
|
||||
bool setupFBOs();
|
||||
void setupPassUniforms(const uint id);
|
||||
void setShaderTexUniforms(const Common::String &prefix, Shader *shader, const Texture &texture);
|
||||
|
||||
/* Pipelines used to draw all layers
|
||||
* First before the scaler then after it to draw on screen
|
||||
*/
|
||||
ShaderPipeline _inputPipeline;
|
||||
ShaderPipeline _outputPipeline;
|
||||
bool _needsScaling;
|
||||
|
||||
const LibRetro::ShaderPreset *_shaderPreset;
|
||||
|
||||
uint _inputWidth;
|
||||
uint _inputHeight;
|
||||
Common::Rect _outputRect;
|
||||
|
||||
bool _linearFiltering;
|
||||
Common::RotationMode _rotation;
|
||||
|
||||
/* Determines if preset depends on frameCount or from previous frames */
|
||||
bool _isAnimated;
|
||||
uint _frameCount;
|
||||
|
||||
Common::Array<LibRetroTextureTarget> _inputTargets;
|
||||
uint _currentTarget;
|
||||
|
||||
struct LibRetroTexture {
|
||||
LibRetroTexture() : textureData(nullptr), glTexture(nullptr) {}
|
||||
LibRetroTexture(Graphics::Surface *tD, Texture *glTex) : textureData(tD), glTexture(glTex) {}
|
||||
|
||||
Common::String id;
|
||||
Graphics::Surface *textureData;
|
||||
Texture *glTexture;
|
||||
};
|
||||
LibRetroTexture loadTexture(const Common::Path &fileName, Common::Archive *container, Common::SearchSet &archSet);
|
||||
|
||||
typedef Common::Array<LibRetroTexture> TextureArray;
|
||||
TextureArray _textures;
|
||||
|
||||
struct Pass {
|
||||
Pass()
|
||||
: shaderPass(nullptr), shader(nullptr), target(nullptr), texCoords(), texSamplers(),
|
||||
inputTexture(nullptr), vertexCoord(), hasFrameCount(false), prevCount(0) {}
|
||||
Pass(const LibRetro::ShaderPass *sP, Shader *s, TextureTarget *t)
|
||||
: shaderPass(sP), shader(s), target(t), texCoords(), texSamplers(),
|
||||
inputTexture(nullptr), vertexCoord(), hasFrameCount(false), prevCount(0) {}
|
||||
|
||||
const LibRetro::ShaderPass *shaderPass;
|
||||
Shader *shader;
|
||||
TextureTarget *target;
|
||||
|
||||
/**
|
||||
* Description of texture coordinates bound to attribute.
|
||||
*/
|
||||
struct TexCoordAttribute {
|
||||
/**
|
||||
* Attribute name to bind data to.
|
||||
*/
|
||||
Common::String name;
|
||||
|
||||
enum Type {
|
||||
/**
|
||||
* 'index' denotes the 'index'th shader texture's coordinates.
|
||||
*/
|
||||
kTypeTexture,
|
||||
|
||||
/**
|
||||
* 'index' denotes the texture coordinates given to pass 'index'.
|
||||
*/
|
||||
kTypePass,
|
||||
|
||||
/**
|
||||
* 'index' denotes the texture coordinates of the 'index'th previous frame.
|
||||
*/
|
||||
kTypePrev
|
||||
};
|
||||
|
||||
/**
|
||||
* The type of the attribute.
|
||||
*/
|
||||
Type type;
|
||||
|
||||
/**
|
||||
* Index for the texture coordinates to use.
|
||||
*/
|
||||
uint index;
|
||||
|
||||
TexCoordAttribute() : name(), type(), index() {}
|
||||
TexCoordAttribute(const Common::String &n, Type t, uint i) : name(n), type(t), index(i) {}
|
||||
};
|
||||
|
||||
typedef Common::Array<TexCoordAttribute> TexCoordAttributeArray;
|
||||
TexCoordAttributeArray texCoords;
|
||||
|
||||
/**
|
||||
* Build the 'texCoords' array.
|
||||
*
|
||||
* @param id Identifier of the current pass.
|
||||
*/
|
||||
void buildTexCoords(const uint id, const Common::StringArray &aliases);
|
||||
|
||||
void addTexCoord(const Common::String &prefix, const TexCoordAttribute::Type type, const uint index);
|
||||
|
||||
/**
|
||||
* Description of a texture sampler.
|
||||
*/
|
||||
struct TextureSampler {
|
||||
/**
|
||||
* Texture unit to use.
|
||||
*/
|
||||
uint unit;
|
||||
|
||||
enum Type {
|
||||
/**
|
||||
* 'index' denotes the 'index'th shader texture.
|
||||
*/
|
||||
kTypeTexture,
|
||||
|
||||
/**
|
||||
* 'index' denotes the input to pass 'index'.
|
||||
*/
|
||||
kTypePass,
|
||||
|
||||
/**
|
||||
* 'index' denotes the input of the 'index'th previous frame.
|
||||
*/
|
||||
kTypePrev
|
||||
};
|
||||
|
||||
/**
|
||||
* Source type of the texture to bind.
|
||||
*/
|
||||
Type type;
|
||||
|
||||
/**
|
||||
* Index of the texture.
|
||||
*/
|
||||
uint index;
|
||||
|
||||
TextureSampler() : unit(), type(), index() {}
|
||||
TextureSampler(uint u, Type t, uint i) : unit(u), type(t), index(i) {}
|
||||
};
|
||||
|
||||
typedef Common::Array<TextureSampler> TextureSamplerArray;
|
||||
TextureSamplerArray texSamplers;
|
||||
|
||||
/**
|
||||
* Build the 'texSamplers' array.
|
||||
*
|
||||
* @param id Identifier of the current pass.
|
||||
* @param textures Array of shader textures available.
|
||||
*/
|
||||
void buildTexSamplers(const uint id, const TextureArray &textures, const Common::StringArray &aliases);
|
||||
|
||||
bool addTexSampler(const Common::String &name, uint *unit, const TextureSampler::Type type, const uint index, const bool prefixIsId = false);
|
||||
|
||||
/**
|
||||
* Input texture of the pass.
|
||||
*/
|
||||
const Texture *inputTexture;
|
||||
|
||||
/**
|
||||
* Vertex coordinates used for drawing.
|
||||
*/
|
||||
GLfloat vertexCoord[2*4];
|
||||
|
||||
/**
|
||||
* Whether the shader has a FrameCount uniform or not
|
||||
* Allows to speed up if it is not here
|
||||
*/
|
||||
bool hasFrameCount;
|
||||
|
||||
/**
|
||||
* The number of previous frames this pass needs
|
||||
*/
|
||||
uint prevCount;
|
||||
};
|
||||
|
||||
typedef Common::Array<Pass> PassArray;
|
||||
PassArray _passes;
|
||||
|
||||
void renderPass(const Pass &pass);
|
||||
void renderPassSetupCoordinates(const Pass &pass);
|
||||
void renderPassSetupTextures(const Pass &pass);
|
||||
};
|
||||
|
||||
} // End of namespace OpenGL
|
||||
#endif // !USE_FORCED_GLES
|
||||
|
||||
#endif
|
||||
573
backends/graphics/opengl/pipelines/libretro/parser.cpp
Normal file
573
backends/graphics/opengl/pipelines/libretro/parser.cpp
Normal file
@@ -0,0 +1,573 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "graphics/opengl/system_headers.h"
|
||||
|
||||
#if !USE_FORCED_GLES
|
||||
#include "backends/graphics/opengl/pipelines/libretro/parser.h"
|
||||
|
||||
#include "common/file.h"
|
||||
#include "common/hash-str.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/algorithm.h"
|
||||
#include "common/tokenizer.h"
|
||||
#include "common/ptr.h"
|
||||
#include "common/util.h"
|
||||
|
||||
#include "common/textconsole.h"
|
||||
|
||||
namespace OpenGL {
|
||||
namespace LibRetro {
|
||||
|
||||
class PresetParser {
|
||||
public:
|
||||
ShaderPreset *parseStream(Common::SeekableReadStream &stream);
|
||||
|
||||
const Common::String &getErrorDesc() const { return _errorDesc; }
|
||||
private:
|
||||
bool parsePreset(Common::SeekableReadStream &stream);
|
||||
bool lookUpValue(const Common::String &key, Common::String *value);
|
||||
bool lookUpValue(const Common::String &key, bool *value);
|
||||
bool lookUpValue(const Common::String &key, uint *value);
|
||||
bool lookUpValue(const Common::String &key, float *value);
|
||||
bool lookUpValue(const Common::String &key, FilteringMode *value, const FilteringMode defaultValue);
|
||||
bool lookUpValue(const Common::String &key, ScaleType *value, const ScaleType defaultValue);
|
||||
bool lookUpValueScale(const Common::String &key, float *floatValue, uint *uintValue, const ScaleType scaleType);
|
||||
bool lookUpValue(const Common::String &key, WrapMode *value, const WrapMode defaultValue);
|
||||
|
||||
template<typename T, typename DefaultT>
|
||||
bool lookUpValue(const Common::String &key, T *value, const DefaultT &defaultValue) {
|
||||
if (_entries.contains(key)) {
|
||||
return lookUpValue(key, value);
|
||||
} else {
|
||||
*value = defaultValue;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool parseTextures();
|
||||
bool parseTexture(const Common::String &id);
|
||||
|
||||
bool parsePasses();
|
||||
bool parsePass(const uint id, const bool isLast);
|
||||
bool parsePassScaleType(const uint id, const bool isLast, ShaderPass *pass);
|
||||
bool parsePassScale(const uint id, ShaderPass *pass);
|
||||
bool computeDefaultScale(const Common::String &key, float *floatValue, uint *uintValue, const ScaleType scaleType);
|
||||
|
||||
bool parseParameters();
|
||||
|
||||
typedef Common::HashMap<Common::String, Common::String> StringMap;
|
||||
StringMap _entries;
|
||||
|
||||
Common::String _errorDesc;
|
||||
|
||||
Common::ScopedPtr<ShaderPreset> _shader;
|
||||
};
|
||||
|
||||
ShaderPreset *PresetParser::parseStream(Common::SeekableReadStream &stream) {
|
||||
_errorDesc.clear();
|
||||
_entries.clear();
|
||||
|
||||
if (!parsePreset(stream)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
_shader.reset(new ShaderPreset);
|
||||
if (!parseTextures()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!parsePasses()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!parseParameters()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return _shader.release();
|
||||
}
|
||||
|
||||
bool PresetParser::parsePreset(Common::SeekableReadStream &stream) {
|
||||
while (!stream.eos()) {
|
||||
Common::String line = stream.readLine();
|
||||
if (stream.err()) {
|
||||
_errorDesc = "Read error";
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t sharpPos = line.findFirstOf('#');
|
||||
if (sharpPos != line.npos) {
|
||||
// Remove the end of line
|
||||
line.erase(sharpPos);
|
||||
}
|
||||
|
||||
if (line.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool empty = true;
|
||||
for (uint i = 0; i < line.size(); i++) {
|
||||
if (!Common::isSpace(line[i])) {
|
||||
empty = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty)
|
||||
continue;
|
||||
|
||||
// Split line into key, value pair.
|
||||
Common::String::const_iterator equalIter = Common::find(line.begin(), line.end(), '=');
|
||||
if (equalIter == line.end()) {
|
||||
_errorDesc = "Could not find '=' in line '" + line + '\'';
|
||||
return false;
|
||||
}
|
||||
|
||||
Common::String key(line.begin(), equalIter);
|
||||
Common::String value(equalIter + 1);
|
||||
|
||||
key.trim();
|
||||
value.trim();
|
||||
|
||||
// Check whether the value is put in quotation marks. This might be
|
||||
// useful when a path contains a whitespace. But Libretro's is not
|
||||
// mentioning any exact format, but one example for 'textures'
|
||||
// indicates quotes are supported in this place.
|
||||
if (!value.empty() && value[0] == '"') {
|
||||
if (value.size() < 2 || value.lastChar() != '"') {
|
||||
_errorDesc = "Unmatched '\"' for value in line '" + line + '\'';
|
||||
}
|
||||
|
||||
value.deleteLastChar();
|
||||
value.deleteChar(0);
|
||||
}
|
||||
|
||||
_entries[key] = value;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PresetParser::lookUpValue(const Common::String &key, Common::String *value) {
|
||||
StringMap::const_iterator iter = _entries.find(key);
|
||||
if (iter != _entries.end()) {
|
||||
*value = iter->_value;
|
||||
return true;
|
||||
} else {
|
||||
_errorDesc = "Missing key '" + key + "'";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool PresetParser::lookUpValue(const Common::String &key, bool *value) {
|
||||
StringMap::const_iterator iter = _entries.find(key);
|
||||
if (iter != _entries.end()) {
|
||||
if (Common::parseBool(iter->_value, *value)) {
|
||||
return true;
|
||||
} else {
|
||||
_errorDesc = "Invalid boolean value for key '" + key + "': '" + iter->_value + '\'';
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
_errorDesc = "Missing key '" + key + "'";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool PresetParser::lookUpValue(const Common::String &key, uint *value) {
|
||||
StringMap::const_iterator iter = _entries.find(key);
|
||||
if (iter != _entries.end()) {
|
||||
char *endptr;
|
||||
const unsigned long uintVal = strtoul(iter->_value.c_str(), &endptr, 0);
|
||||
// Original libretro is quite laxist with int values and only checks errno
|
||||
// This means that as long as there is some number at start, parsing won't fail
|
||||
if (endptr == iter->_value.c_str() || uintVal >= UINT_MAX) {
|
||||
_errorDesc = "Invalid unsigned integer value for key '" + key + "': '" + iter->_value + '\'';
|
||||
return false;
|
||||
} else {
|
||||
if (*endptr != '\0') {
|
||||
warning("Possibly invalid unsigned integer value for key '%s': '%s'", key.c_str(), iter->_value.c_str());
|
||||
}
|
||||
*value = uintVal;
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
_errorDesc = "Missing key '" + key + "'";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool PresetParser::lookUpValue(const Common::String &key, float *value) {
|
||||
StringMap::const_iterator iter = _entries.find(key);
|
||||
if (iter != _entries.end()) {
|
||||
char *endptr;
|
||||
const double doubleVal = strtod(iter->_value.c_str(), &endptr);
|
||||
if (*endptr != '\0') {
|
||||
_errorDesc = "Invalid float value for key '" + key + "': '" + iter->_value + '\'';
|
||||
return false;
|
||||
} else {
|
||||
*value = doubleVal;
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
_errorDesc = "Missing key '" + key + "'";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool PresetParser::lookUpValue(const Common::String &key, FilteringMode *value, const FilteringMode defaultValue) {
|
||||
StringMap::const_iterator iter = _entries.find(key);
|
||||
if (iter != _entries.end()) {
|
||||
bool isLinear;
|
||||
if (Common::parseBool(iter->_value, isLinear)) {
|
||||
*value = isLinear ? kFilteringModeLinear : kFilteringModeNearest;
|
||||
return true;
|
||||
} else {
|
||||
_errorDesc = "Invalid filtering mode for key '" + key + "': '" + iter->_value + '\'';
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
*value = defaultValue;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool PresetParser::lookUpValue(const Common::String &key, ScaleType *value, const ScaleType defaultValue) {
|
||||
StringMap::const_iterator iter = _entries.find(key);
|
||||
if (iter != _entries.end()) {
|
||||
if (iter->_value == "source") {
|
||||
*value = kScaleTypeSource;
|
||||
return true;
|
||||
} else if (iter->_value == "viewport") {
|
||||
*value = kScaleTypeViewport;
|
||||
return true;
|
||||
} else if (iter->_value == "absolute") {
|
||||
*value = kScaleTypeAbsolute;
|
||||
return true;
|
||||
} else {
|
||||
_errorDesc = "Invalid scale type for key '" + key + "': '" + iter->_value + '\'';
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
*value = defaultValue;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool PresetParser::lookUpValueScale(const Common::String &key, float *floatValue, uint *uintValue, const ScaleType scaleType) {
|
||||
switch (scaleType) {
|
||||
case kScaleTypeSource:
|
||||
case kScaleTypeViewport:
|
||||
return lookUpValue(key, floatValue, 1.0f);
|
||||
|
||||
case kScaleTypeAbsolute:
|
||||
return lookUpValue(key, uintValue);
|
||||
|
||||
case kScaleTypeFull:
|
||||
return true;
|
||||
|
||||
default:
|
||||
_errorDesc = "Internal Error: Invalid scale type";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool PresetParser::lookUpValue(const Common::String &key, WrapMode *value, const WrapMode defaultValue) {
|
||||
StringMap::const_iterator iter = _entries.find(key);
|
||||
if (iter != _entries.end()) {
|
||||
if (iter->_value == "clamp_to_border") {
|
||||
*value = kWrapModeBorder;
|
||||
return true;
|
||||
} else if (iter->_value == "clamp_to_edge") {
|
||||
*value = kWrapModeEdge;
|
||||
return true;
|
||||
} else if (iter->_value == "repeat") {
|
||||
*value = kWrapModeRepeat;
|
||||
return true;
|
||||
} else if (iter->_value == "mirrored_repeat") {
|
||||
*value = kWrapModeMirroredRepeat;
|
||||
return true;
|
||||
} else {
|
||||
_errorDesc = "Invalid wrap mode for key '" + key + "': '" + iter->_value + '\'';
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
*value = defaultValue;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool PresetParser::parseTextures() {
|
||||
Common::String textures;
|
||||
if (!lookUpValue("textures", &textures)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Parse all texture information from preset.
|
||||
Common::StringTokenizer tokenizer(textures, ";");
|
||||
while (!tokenizer.empty()) {
|
||||
if (!parseTexture(tokenizer.nextToken())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PresetParser::parseTexture(const Common::String &id) {
|
||||
Common::String fileName;
|
||||
if (!lookUpValue(id, &fileName)) {
|
||||
_errorDesc = "No file name specified for texture '" + id + '\'';
|
||||
return false;
|
||||
}
|
||||
|
||||
FilteringMode filteringMode;
|
||||
if (!lookUpValue(id + "_linear", &filteringMode, kFilteringModeLinear)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
WrapMode wrapMode;
|
||||
if (!lookUpValue(id + "_wrap_mode", &wrapMode, kWrapModeBorder)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_shader->textures.push_back(ShaderTexture(id, fileName, filteringMode, wrapMode));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PresetParser::parsePasses() {
|
||||
uint numShaders;
|
||||
if (!lookUpValue("shaders", &numShaders)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint shaderPass = 0; shaderPass < numShaders; ++shaderPass) {
|
||||
if (!parsePass(shaderPass, shaderPass == numShaders - 1)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#define passKey(x) Common::String::format(x "%u", id)
|
||||
bool PresetParser::parsePass(const uint id, const bool isLast) {
|
||||
ShaderPass pass;
|
||||
|
||||
if (!lookUpValue(passKey("shader"), &pass.fileName)) {
|
||||
_errorDesc = Common::String::format("No file name specified for pass '%u'", id);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!lookUpValue(passKey("alias"), &pass.alias)) {
|
||||
_errorDesc.clear();
|
||||
pass.alias.clear();
|
||||
}
|
||||
|
||||
if (!lookUpValue(passKey("filter_linear"), &pass.filteringMode, kFilteringModeUnspecified)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!lookUpValue(passKey("wrap_mode"), &pass.wrapMode, kWrapModeBorder)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!lookUpValue(passKey("mipmap_input"), &pass.mipmapInput, false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!lookUpValue(passKey("float_framebuffer"), &pass.floatFBO, false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!lookUpValue(passKey("srgb_framebuffer"), &pass.srgbFBO, false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!lookUpValue(passKey("frame_count_mod"), &pass.frameCountMod, 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!parsePassScaleType(id, isLast, &pass)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!parsePassScale(id, &pass)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_shader->passes.push_back(pass);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PresetParser::parsePassScaleType(const uint id, const bool isLast, ShaderPass *pass) {
|
||||
// Parse scale type for the pass.
|
||||
//
|
||||
// This is a little more complicated because it is possible to specify the
|
||||
// scale type per axis. However, a generic scale type overrides the axis
|
||||
// scale types.
|
||||
//
|
||||
// Additionally, the default value for the passes vary. The last pass
|
||||
// defaults to use full size, all other default to source scaling.
|
||||
|
||||
const ScaleType defaultScaleType = isLast ? kScaleTypeFull : kScaleTypeSource;
|
||||
|
||||
if (!lookUpValue(passKey("scale_type_x"), &pass->scaleTypeX, defaultScaleType)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!lookUpValue(passKey("scale_type_y"), &pass->scaleTypeY, defaultScaleType)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ScaleType scale_type;
|
||||
// Small trick here: lookUpValue never returns kScaleTypeFull
|
||||
if (!lookUpValue(passKey("scale_type"), &scale_type, kScaleTypeFull)) {
|
||||
return false;
|
||||
} else if (scale_type != kScaleTypeFull) {
|
||||
pass->scaleTypeY = pass->scaleTypeX = scale_type;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PresetParser::parsePassScale(const uint id, ShaderPass *pass) {
|
||||
// Parse actual scale value for the pass.
|
||||
//
|
||||
// Like for the scale type, 'scale' overrides 'scale_x'/'scale_y'.
|
||||
if (_entries.contains(passKey("scale"))) {
|
||||
if (!lookUpValueScale(passKey("scale"), &pass->scaleXFloat, &pass->scaleXUint, pass->scaleTypeX)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!lookUpValueScale(passKey("scale"), &pass->scaleYFloat, &pass->scaleYUint, pass->scaleTypeY)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_entries.contains(passKey("scale_x"))) {
|
||||
if (!lookUpValueScale(passKey("scale_x"), &pass->scaleXFloat, &pass->scaleXUint, pass->scaleTypeX)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!computeDefaultScale(passKey("scale_x"), &pass->scaleXFloat, &pass->scaleXUint, pass->scaleTypeX)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (_entries.contains(passKey("scale_y"))) {
|
||||
if (!lookUpValueScale(passKey("scale_y"), &pass->scaleYFloat, &pass->scaleYUint, pass->scaleTypeY)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!computeDefaultScale(passKey("scale_y"), &pass->scaleYFloat, &pass->scaleYUint, pass->scaleTypeY)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#undef passKey
|
||||
|
||||
bool PresetParser::computeDefaultScale(const Common::String &key, float *floatValue, uint *uintValue, const ScaleType scaleType) {
|
||||
switch (scaleType) {
|
||||
case kScaleTypeSource:
|
||||
case kScaleTypeViewport:
|
||||
*floatValue = 1.0f;
|
||||
return true;
|
||||
|
||||
case kScaleTypeAbsolute:
|
||||
_errorDesc = "No value specified for scale '" + key + '\'';
|
||||
return false;
|
||||
|
||||
case kScaleTypeFull:
|
||||
return true;
|
||||
|
||||
default:
|
||||
_errorDesc = "Internal Error: Invalid scale type";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool PresetParser::parseParameters() {
|
||||
Common::String parameters;
|
||||
if (!lookUpValue("parameters", ¶meters)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Parse all texture information from preset.
|
||||
Common::StringTokenizer tokenizer(parameters, ";");
|
||||
while (!tokenizer.empty()) {
|
||||
Common::String key = tokenizer.nextToken();
|
||||
if (_entries.contains(key)) {
|
||||
float value;
|
||||
if (!lookUpValue(key, &value)) {
|
||||
return false;
|
||||
}
|
||||
_shader->parameters[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ShaderPreset *parsePreset(const Common::Path &shaderPreset, Common::SearchSet &archSet) {
|
||||
Common::SeekableReadStream *stream;
|
||||
Common::Archive *container = nullptr;
|
||||
Common::Path basePath;
|
||||
|
||||
// First try SearchMan, then fallback to filesystem
|
||||
if (archSet.hasFile(shaderPreset)) {
|
||||
Common::ArchiveMemberPtr member = archSet.getMember(shaderPreset, &container);
|
||||
stream = member->createReadStream();
|
||||
basePath = shaderPreset.getParent();
|
||||
} else {
|
||||
Common::FSNode fsnode(shaderPreset);
|
||||
if (!fsnode.exists() || !fsnode.isReadable() || fsnode.isDirectory()
|
||||
|| !(stream = fsnode.createReadStream())) {
|
||||
warning("LibRetro Preset Parsing: Invalid file path '%s'", shaderPreset.toString(Common::Path::kNativeSeparator).c_str());
|
||||
return nullptr;
|
||||
}
|
||||
basePath = fsnode.getParent().getPath();
|
||||
}
|
||||
|
||||
PresetParser parser;
|
||||
ShaderPreset *shader = parser.parseStream(*stream);
|
||||
|
||||
delete stream;
|
||||
|
||||
if (!shader) {
|
||||
warning("LibRetro Preset Parsing: Error while parsing file '%s': %s", shaderPreset.toString().c_str(), parser.getErrorDesc().c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
shader->container = container;
|
||||
shader->basePath = basePath;
|
||||
return shader;
|
||||
}
|
||||
|
||||
} // End of namespace LibRetro
|
||||
} // End of namespace OpenGL
|
||||
#endif // !USE_FORCED_GLES
|
||||
39
backends/graphics/opengl/pipelines/libretro/parser.h
Normal file
39
backends/graphics/opengl/pipelines/libretro/parser.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_GRAPHICS_OPENGL_PIPELINES_LIBRETRO_PARSER_H
|
||||
#define BACKENDS_GRAPHICS_OPENGL_PIPELINES_LIBRETRO_PARSER_H
|
||||
|
||||
#include "graphics/opengl/system_headers.h"
|
||||
|
||||
#if !USE_FORCED_GLES
|
||||
#include "backends/graphics/opengl/pipelines/libretro/types.h"
|
||||
|
||||
namespace OpenGL {
|
||||
namespace LibRetro {
|
||||
|
||||
ShaderPreset *parsePreset(const Common::Path &shaderPreset, Common::SearchSet &archSet);
|
||||
|
||||
} // End of namespace LibRetro
|
||||
} // End of namespace OpenGL
|
||||
#endif // !USE_FORCED_GLES
|
||||
|
||||
#endif
|
||||
132
backends/graphics/opengl/pipelines/libretro/types.h
Normal file
132
backends/graphics/opengl/pipelines/libretro/types.h
Normal file
@@ -0,0 +1,132 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_GRAPHICS_OPENGL_PIPELINES_LIBRETRO_TYPES_H
|
||||
#define BACKENDS_GRAPHICS_OPENGL_PIPELINES_LIBRETRO_TYPES_H
|
||||
|
||||
#include "graphics/opengl/system_headers.h"
|
||||
#include "graphics/opengl/texture.h"
|
||||
|
||||
#if !USE_FORCED_GLES
|
||||
#include "common/str.h"
|
||||
#include "common/array.h"
|
||||
#include "common/fs.h"
|
||||
|
||||
namespace OpenGL {
|
||||
namespace LibRetro {
|
||||
|
||||
typedef Common::HashMap<Common::String, float, Common::CaseSensitiveString_Hash, Common::CaseSensitiveString_EqualTo> UniformsMap;
|
||||
|
||||
enum FilteringMode {
|
||||
kFilteringModeUnspecified,
|
||||
kFilteringModeNearest,
|
||||
kFilteringModeLinear
|
||||
};
|
||||
|
||||
struct ShaderTexture {
|
||||
ShaderTexture() : id(), fileName(), filteringMode(kFilteringModeUnspecified) {}
|
||||
ShaderTexture(const Common::String &i, const Common::String &fN, FilteringMode fM, WrapMode wM)
|
||||
: id(i), fileName(fN), filteringMode(fM), wrapMode(wM) {}
|
||||
|
||||
Common::String id;
|
||||
Common::String fileName;
|
||||
FilteringMode filteringMode;
|
||||
WrapMode wrapMode;
|
||||
};
|
||||
|
||||
enum ScaleType {
|
||||
kScaleTypeSource,
|
||||
kScaleTypeViewport,
|
||||
kScaleTypeAbsolute,
|
||||
kScaleTypeFull
|
||||
};
|
||||
|
||||
inline void applyScale(const ScaleType type,
|
||||
const float source, const float viewport,
|
||||
const float scaleFloat, const uint scaleUint,
|
||||
float *output) {
|
||||
switch (type) {
|
||||
case kScaleTypeSource:
|
||||
*output = source * scaleFloat;
|
||||
break;
|
||||
|
||||
case kScaleTypeViewport:
|
||||
*output = viewport * scaleFloat;
|
||||
break;
|
||||
|
||||
case kScaleTypeAbsolute:
|
||||
*output = scaleUint;
|
||||
break;
|
||||
|
||||
case kScaleTypeFull:
|
||||
*output = viewport;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
struct ShaderPass {
|
||||
Common::String fileName;
|
||||
Common::String alias;
|
||||
|
||||
FilteringMode filteringMode;
|
||||
WrapMode wrapMode;
|
||||
bool mipmapInput;
|
||||
|
||||
bool floatFBO;
|
||||
bool srgbFBO;
|
||||
|
||||
uint frameCountMod;
|
||||
|
||||
ScaleType scaleTypeX;
|
||||
ScaleType scaleTypeY;
|
||||
|
||||
float scaleXFloat;
|
||||
float scaleYFloat;
|
||||
|
||||
uint scaleXUint;
|
||||
uint scaleYUint;
|
||||
|
||||
void applyScale(const float sourceW, const float sourceH,
|
||||
const float viewportW, const float viewportH,
|
||||
float *outputW, float *outputH) const {
|
||||
OpenGL::LibRetro::applyScale(scaleTypeX, sourceW, viewportW, scaleXFloat, scaleXUint, outputW);
|
||||
OpenGL::LibRetro::applyScale(scaleTypeY, sourceH, viewportH, scaleYFloat, scaleYUint, outputH);
|
||||
}
|
||||
};
|
||||
|
||||
struct ShaderPreset {
|
||||
Common::Archive *container;
|
||||
Common::Path basePath;
|
||||
|
||||
typedef Common::Array<ShaderTexture> TextureArray;
|
||||
TextureArray textures;
|
||||
|
||||
typedef Common::Array<ShaderPass> PassArray;
|
||||
PassArray passes;
|
||||
|
||||
UniformsMap parameters;
|
||||
};
|
||||
|
||||
} // End of namespace LibRetro
|
||||
} // End of namespace OpenGL
|
||||
#endif // !USE_FORCED_GLES
|
||||
|
||||
#endif
|
||||
81
backends/graphics/opengl/pipelines/pipeline.cpp
Normal file
81
backends/graphics/opengl/pipelines/pipeline.cpp
Normal file
@@ -0,0 +1,81 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "backends/graphics/opengl/pipelines/pipeline.h"
|
||||
#include "backends/graphics/opengl/framebuffer.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
Pipeline *Pipeline::activePipeline = nullptr;
|
||||
|
||||
Pipeline::Pipeline()
|
||||
: _activeFramebuffer(nullptr) {
|
||||
}
|
||||
|
||||
void Pipeline::activate() {
|
||||
if (activePipeline == this) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (activePipeline) {
|
||||
activePipeline->deactivate();
|
||||
}
|
||||
|
||||
activePipeline = this;
|
||||
|
||||
activateInternal();
|
||||
}
|
||||
|
||||
void Pipeline::activateInternal() {
|
||||
if (_activeFramebuffer) {
|
||||
_activeFramebuffer->activate(this);
|
||||
}
|
||||
}
|
||||
|
||||
void Pipeline::deactivate() {
|
||||
assert(isActive());
|
||||
|
||||
deactivateInternal();
|
||||
|
||||
activePipeline = nullptr;
|
||||
}
|
||||
|
||||
void Pipeline::deactivateInternal() {
|
||||
if (_activeFramebuffer) {
|
||||
_activeFramebuffer->deactivate();
|
||||
}
|
||||
}
|
||||
|
||||
Framebuffer *Pipeline::setFramebuffer(Framebuffer *framebuffer) {
|
||||
Framebuffer *oldFramebuffer = _activeFramebuffer;
|
||||
if (isActive() && oldFramebuffer) {
|
||||
oldFramebuffer->deactivate();
|
||||
}
|
||||
|
||||
_activeFramebuffer = framebuffer;
|
||||
if (isActive() && _activeFramebuffer) {
|
||||
_activeFramebuffer->activate(this);
|
||||
}
|
||||
|
||||
return oldFramebuffer;
|
||||
}
|
||||
|
||||
} // End of namespace OpenGL
|
||||
167
backends/graphics/opengl/pipelines/pipeline.h
Normal file
167
backends/graphics/opengl/pipelines/pipeline.h
Normal file
@@ -0,0 +1,167 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_GRAPHICS_OPENGL_PIPELINES_PIPELINE_H
|
||||
#define BACKENDS_GRAPHICS_OPENGL_PIPELINES_PIPELINE_H
|
||||
|
||||
#include "graphics/opengl/system_headers.h"
|
||||
#include "graphics/opengl/texture.h"
|
||||
|
||||
#include "math/matrix4.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
class Framebuffer;
|
||||
|
||||
/**
|
||||
* Interface for OpenGL pipeline functionality.
|
||||
*
|
||||
* This encapsulates differences in various rendering pipelines used for
|
||||
* OpenGL, OpenGL ES 1, and OpenGL ES 2.
|
||||
*/
|
||||
class Pipeline {
|
||||
public:
|
||||
/**
|
||||
* Deactivate any pipeline.
|
||||
*/
|
||||
static void disable() { if (activePipeline) activePipeline->deactivate(); }
|
||||
|
||||
Pipeline();
|
||||
virtual ~Pipeline() { if (isActive()) deactivate(); }
|
||||
|
||||
/**
|
||||
* Activate the pipeline.
|
||||
*
|
||||
* This sets the OpenGL state to make use of drawing with the given
|
||||
* OpenGL pipeline.
|
||||
*/
|
||||
void activate();
|
||||
|
||||
/**
|
||||
* Deactivate the pipeline.
|
||||
*/
|
||||
void deactivate();
|
||||
|
||||
/**
|
||||
* Set framebuffer to render to.
|
||||
*
|
||||
* Client is responsible for any memory management related to framebuffer.
|
||||
*
|
||||
* @param framebuffer Framebuffer to activate.
|
||||
* @return Formerly active framebuffer.
|
||||
*/
|
||||
Framebuffer *setFramebuffer(Framebuffer *framebuffer);
|
||||
|
||||
/**
|
||||
* Set modulation color.
|
||||
*
|
||||
* @param r Red component in [0,1].
|
||||
* @param g Green component in [0,1].
|
||||
* @param b Blue component in [0,1].
|
||||
* @param a Alpha component in [0,1].
|
||||
*/
|
||||
virtual void setColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) = 0;
|
||||
|
||||
/**
|
||||
* Draw a texture rectangle to the currently active framebuffer.
|
||||
*
|
||||
* @param texture Texture to use for drawing.
|
||||
* @param coordinates x1, y1, x2, y2 coordinates where to draw the texture.
|
||||
*/
|
||||
inline void drawTexture(const Texture &texture, const GLfloat *coordinates, const GLfloat *texcoords) {
|
||||
drawTextureInternal(texture, coordinates, texcoords);
|
||||
}
|
||||
|
||||
inline void drawTexture(const Texture &texture, const GLfloat *coordinates) {
|
||||
drawTextureInternal(texture, coordinates, texture.getTexCoords());
|
||||
}
|
||||
|
||||
inline void drawTexture(const Texture &texture, GLfloat x, GLfloat y, GLfloat w, GLfloat h) {
|
||||
const GLfloat coordinates[4*2] = {
|
||||
x, y,
|
||||
x + w, y,
|
||||
x, y + h,
|
||||
x + w, y + h
|
||||
};
|
||||
drawTextureInternal(texture, coordinates, texture.getTexCoords());
|
||||
}
|
||||
|
||||
inline void drawTexture(const Texture &texture, GLfloat x, GLfloat y, GLfloat w, GLfloat h, const Common::Rect &clip) {
|
||||
const GLfloat coordinates[4*2] = {
|
||||
x, y,
|
||||
x + w, y,
|
||||
x, y + h,
|
||||
x + w, y + h
|
||||
};
|
||||
|
||||
const uint tw = texture.getWidth(),
|
||||
th = texture.getHeight();
|
||||
|
||||
if (tw == 0 || th == 0) {
|
||||
// Nothing to display
|
||||
return;
|
||||
}
|
||||
|
||||
const GLfloat texcoords[4*2] = {
|
||||
(float)clip.left / tw, (float)clip.top / th,
|
||||
(float)clip.right / tw, (float)clip.top / th,
|
||||
(float)clip.left / tw, (float)clip.bottom / th,
|
||||
(float)clip.right / tw, (float)clip.bottom / th
|
||||
};
|
||||
|
||||
drawTextureInternal(texture, coordinates, texcoords);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the projection matrix.
|
||||
*
|
||||
* This is intended to be only ever be used by Framebuffer subclasses.
|
||||
*/
|
||||
virtual void setProjectionMatrix(const Math::Matrix4 &projectionMatrix) = 0;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Activate the pipeline.
|
||||
*
|
||||
* This sets the OpenGL state to make use of drawing with the given
|
||||
* OpenGL pipeline.
|
||||
*/
|
||||
virtual void activateInternal();
|
||||
|
||||
/**
|
||||
* Deactivate the pipeline.
|
||||
*/
|
||||
virtual void deactivateInternal();
|
||||
|
||||
virtual void drawTextureInternal(const Texture &texture, const GLfloat *coordinates, const GLfloat *texcoords) = 0;
|
||||
|
||||
bool isActive() const { return activePipeline == this; }
|
||||
|
||||
Framebuffer *_activeFramebuffer;
|
||||
|
||||
private:
|
||||
/** Currently active rendering pipeline. */
|
||||
static Pipeline *activePipeline;
|
||||
};
|
||||
|
||||
} // End of namespace OpenGL
|
||||
|
||||
#endif
|
||||
110
backends/graphics/opengl/pipelines/shader.cpp
Normal file
110
backends/graphics/opengl/pipelines/shader.cpp
Normal file
@@ -0,0 +1,110 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "backends/graphics/opengl/pipelines/shader.h"
|
||||
#include "backends/graphics/opengl/shader.h"
|
||||
#include "backends/graphics/opengl/framebuffer.h"
|
||||
#include "graphics/opengl/debug.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
// A 4 elements with 2 components vector of floats
|
||||
static const int kCoordinatesSize = 4 * 2 * sizeof(float);
|
||||
|
||||
#if !USE_FORCED_GLES
|
||||
ShaderPipeline::ShaderPipeline(Shader *shader)
|
||||
: _activeShader(shader), _colorAttributes(), _colorDirty(true) {
|
||||
// Use the same VBO for vertices and texcoords as we modify them at the same time
|
||||
_coordsVBO = OpenGL::Shader::createBuffer(GL_ARRAY_BUFFER, kCoordinatesSize, nullptr, GL_STATIC_DRAW);
|
||||
_activeShader->enableVertexAttribute("position", _coordsVBO, 2, GL_FLOAT, GL_FALSE, 0, 0);
|
||||
_texcoordsVBO = OpenGL::Shader::createBuffer(GL_ARRAY_BUFFER, kCoordinatesSize, nullptr, GL_STATIC_DRAW);
|
||||
_activeShader->enableVertexAttribute("texCoordIn", _texcoordsVBO, 2, GL_FLOAT, GL_FALSE, 0, 0);
|
||||
_colorVBO = OpenGL::Shader::createBuffer(GL_ARRAY_BUFFER, sizeof(_colorAttributes), nullptr, GL_DYNAMIC_DRAW);
|
||||
_activeShader->enableVertexAttribute("blendColorIn", _colorVBO, 4, GL_FLOAT, GL_FALSE, 0, 0);
|
||||
}
|
||||
|
||||
ShaderPipeline::~ShaderPipeline() {
|
||||
delete _activeShader;
|
||||
|
||||
OpenGL::Shader::freeBuffer(_coordsVBO);
|
||||
OpenGL::Shader::freeBuffer(_texcoordsVBO);
|
||||
OpenGL::Shader::freeBuffer(_colorVBO);
|
||||
}
|
||||
|
||||
void ShaderPipeline::activateInternal() {
|
||||
Pipeline::activateInternal();
|
||||
|
||||
// Disable 3D properties.
|
||||
GL_CALL(glDisable(GL_CULL_FACE));
|
||||
GL_CALL(glDisable(GL_DEPTH_TEST));
|
||||
GL_CALL(glDisable(GL_DITHER));
|
||||
|
||||
if (OpenGLContext.multitextureSupported) {
|
||||
GL_CALL(glActiveTexture(GL_TEXTURE0));
|
||||
}
|
||||
|
||||
_activeShader->use();
|
||||
}
|
||||
|
||||
void ShaderPipeline::deactivateInternal() {
|
||||
_activeShader->unbind();
|
||||
|
||||
Pipeline::deactivateInternal();
|
||||
}
|
||||
|
||||
void ShaderPipeline::setColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) {
|
||||
GLfloat *dst = _colorAttributes;
|
||||
for (uint i = 0; i < 4; ++i) {
|
||||
*dst++ = r;
|
||||
*dst++ = g;
|
||||
*dst++ = b;
|
||||
*dst++ = a;
|
||||
}
|
||||
_colorDirty = true;
|
||||
}
|
||||
|
||||
void ShaderPipeline::drawTextureInternal(const Texture &texture, const GLfloat *coordinates, const GLfloat *texcoords) {
|
||||
assert(isActive());
|
||||
|
||||
texture.bind();
|
||||
|
||||
if (_colorDirty) {
|
||||
GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, _colorVBO));
|
||||
GL_CALL(glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(_colorAttributes), _colorAttributes));
|
||||
_colorDirty = false;
|
||||
}
|
||||
GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, _coordsVBO));
|
||||
GL_CALL(glBufferData(GL_ARRAY_BUFFER, kCoordinatesSize, coordinates, GL_STATIC_DRAW));
|
||||
GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, _texcoordsVBO));
|
||||
GL_CALL(glBufferData(GL_ARRAY_BUFFER, kCoordinatesSize, texcoords, GL_STATIC_DRAW));
|
||||
GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, 0));
|
||||
|
||||
GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4));
|
||||
}
|
||||
|
||||
void ShaderPipeline::setProjectionMatrix(const Math::Matrix4 &projectionMatrix) {
|
||||
assert(isActive());
|
||||
|
||||
_activeShader->setUniform("projection", projectionMatrix);
|
||||
}
|
||||
#endif // !USE_FORCED_GLES
|
||||
|
||||
} // End of namespace OpenGL
|
||||
59
backends/graphics/opengl/pipelines/shader.h
Normal file
59
backends/graphics/opengl/pipelines/shader.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_GRAPHICS_OPENGL_PIPELINES_SHADER_H
|
||||
#define BACKENDS_GRAPHICS_OPENGL_PIPELINES_SHADER_H
|
||||
|
||||
#include "backends/graphics/opengl/pipelines/pipeline.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
#if !USE_FORCED_GLES
|
||||
class Shader;
|
||||
|
||||
class ShaderPipeline : public Pipeline {
|
||||
public:
|
||||
ShaderPipeline(Shader *shader);
|
||||
~ShaderPipeline() override;
|
||||
|
||||
void setColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) override;
|
||||
|
||||
void setProjectionMatrix(const Math::Matrix4 &projectionMatrix) override;
|
||||
|
||||
protected:
|
||||
void activateInternal() override;
|
||||
void deactivateInternal() override;
|
||||
void drawTextureInternal(const Texture &texture, const GLfloat *coordinates, const GLfloat *texcoords) override;
|
||||
|
||||
GLuint _coordsVBO;
|
||||
GLuint _texcoordsVBO;
|
||||
GLuint _colorVBO;
|
||||
|
||||
GLfloat _colorAttributes[4*4];
|
||||
bool _colorDirty;
|
||||
|
||||
Shader *const _activeShader;
|
||||
};
|
||||
#endif // !USE_FORCED_GLES
|
||||
|
||||
} // End of namespace OpenGL
|
||||
|
||||
#endif
|
||||
484
backends/graphics/opengl/renderer3d.cpp
Normal file
484
backends/graphics/opengl/renderer3d.cpp
Normal file
@@ -0,0 +1,484 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/scummsys.h"
|
||||
|
||||
#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
|
||||
|
||||
#include "backends/graphics/opengl/renderer3d.h"
|
||||
#include "backends/graphics/opengl/pipelines/pipeline.h"
|
||||
|
||||
#include "common/system.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
static void setupRenderbufferStorage(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) {
|
||||
#if !USE_FORCED_GLES2 || defined(USE_GLAD)
|
||||
if (samples > 1) {
|
||||
glRenderbufferStorageMultisample(target, samples, internalformat, width, height);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
glRenderbufferStorage(target, internalformat, width, height);
|
||||
}
|
||||
|
||||
// This constructor must not depend on any existing GL context
|
||||
Renderer3D::Renderer3D() :
|
||||
_texture(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, false),
|
||||
_renderBuffers{0, 0, 0}, _frameBuffers{0, 0}, _renderToFrameBuffer(false),
|
||||
_samples(0), _stackLevel(0), _inOverlay(false),
|
||||
_pendingScreenChangeWidth(-1), _pendingScreenChangeHeight(-1) {
|
||||
_texture.enableLinearFiltering(true);
|
||||
_texture.setFlip(true);
|
||||
}
|
||||
|
||||
void Renderer3D::destroy() {
|
||||
while (_stackLevel) {
|
||||
enter3D();
|
||||
}
|
||||
|
||||
if (_frameBuffers[0]) {
|
||||
// Check that we did allocated some framebuffer before destroying them
|
||||
// This avoids to call glDeleteFramebuffers and glDeleteRenderbuffers
|
||||
// on platforms not supporting it
|
||||
glDeleteFramebuffers(ARRAYSIZE(_frameBuffers), _frameBuffers);
|
||||
glDeleteRenderbuffers(ARRAYSIZE(_renderBuffers), _renderBuffers);
|
||||
memset(_renderBuffers, 0, sizeof(_renderBuffers));
|
||||
memset(_frameBuffers, 0, sizeof(_frameBuffers));
|
||||
}
|
||||
|
||||
_texture.destroy();
|
||||
}
|
||||
|
||||
void Renderer3D::initSize(uint w, uint h, int samples, bool renderToFrameBuffer) {
|
||||
_samples = samples;
|
||||
_renderToFrameBuffer = renderToFrameBuffer;
|
||||
|
||||
if (!renderToFrameBuffer) {
|
||||
destroy();
|
||||
_texture.setSize(0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
_texture.setSize(w, h);
|
||||
recreate();
|
||||
}
|
||||
|
||||
void Renderer3D::resize(uint w, uint h) {
|
||||
assert(!_stackLevel);
|
||||
|
||||
if (!_renderToFrameBuffer) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_inOverlay) {
|
||||
// While the (GUI) overlay is active, the game doesn't renders
|
||||
// So, instead of loosing the contents of the FBO because of a resize,
|
||||
// just delay it to when we close the GUI.
|
||||
_pendingScreenChangeWidth = w;
|
||||
_pendingScreenChangeHeight = h;
|
||||
return;
|
||||
}
|
||||
|
||||
_texture.setSize(w, h);
|
||||
setup();
|
||||
|
||||
// Rebind the framebuffer
|
||||
#if !USE_FORCED_GLES2 || defined(USE_GLAD)
|
||||
if (_frameBuffers[1]) {
|
||||
// We are using multisampling
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, _frameBuffers[1]);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _frameBuffers[0]);
|
||||
} else if (_frameBuffers[0])
|
||||
#endif
|
||||
{
|
||||
// Draw on framebuffer if one was setup
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffers[0]);
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer3D::recreate() {
|
||||
destroy();
|
||||
|
||||
if (!_renderToFrameBuffer) {
|
||||
// No framebuffer was requested
|
||||
return;
|
||||
}
|
||||
|
||||
// A 1x antialiasing is not an antialiasing
|
||||
if (_samples > 1) {
|
||||
#if !USE_FORCED_GLES2 || defined(USE_GLAD)
|
||||
if (!OpenGLContext.framebufferObjectMultisampleSupported) {
|
||||
warning("The current OpenGL context does not support multisample framebuffer objects");
|
||||
_samples = 0;
|
||||
}
|
||||
if (_samples > OpenGLContext.multisampleMaxSamples) {
|
||||
warning("Requested anti-aliasing with '%d' samples, but the current OpenGL context supports '%d' samples at most",
|
||||
_samples, OpenGLContext.multisampleMaxSamples);
|
||||
_samples = OpenGLContext.multisampleMaxSamples;
|
||||
}
|
||||
#else
|
||||
warning("multisample framebuffer objects support is not compiled in");
|
||||
_samples = 0;
|
||||
#endif
|
||||
} else {
|
||||
_samples = 0;
|
||||
}
|
||||
|
||||
setup();
|
||||
|
||||
// Context got destroyed
|
||||
_stackLevel = 0;
|
||||
}
|
||||
|
||||
void Renderer3D::setup() {
|
||||
#if !USE_FORCED_GLES2 || defined(USE_GLAD)
|
||||
const bool multiSample = _samples > 1;
|
||||
#else
|
||||
const bool multiSample = false;
|
||||
#endif
|
||||
const uint w = _texture.getLogicalWidth();
|
||||
const uint h = _texture.getLogicalHeight();
|
||||
|
||||
if (!_texture.getGLTexture()) {
|
||||
_texture.create();
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
if (!_frameBuffers[0]) {
|
||||
glGenFramebuffers(multiSample ? 2 : 1, _frameBuffers);
|
||||
glGenRenderbuffers(multiSample ? 3 : 2, _renderBuffers);
|
||||
}
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffers[multiSample ? 1 : 0]);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _texture.getGLTexture(), 0);
|
||||
|
||||
#if !USE_FORCED_GLES2 || defined(USE_GLAD)
|
||||
if (multiSample) {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffers[0]);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, _renderBuffers[2]);
|
||||
setupRenderbufferStorage(GL_RENDERBUFFER, _samples, GL_RGBA8, w, h);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _renderBuffers[2]);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef EMSCRIPTEN
|
||||
// See https://www.khronos.org/registry/webgl/specs/latest/1.0/#FBO_ATTACHMENTS
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, _renderBuffers[0]);
|
||||
setupRenderbufferStorage(GL_RENDERBUFFER, _samples, GL_DEPTH_STENCIL, w, h);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, _renderBuffers[0]);
|
||||
#else
|
||||
if (OpenGLContext.packedDepthStencilSupported) {
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, _renderBuffers[0]);
|
||||
setupRenderbufferStorage(GL_RENDERBUFFER, _samples, GL_DEPTH24_STENCIL8, w, h);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _renderBuffers[0]);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, _renderBuffers[0]);
|
||||
} else {
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, _renderBuffers[0]);
|
||||
setupRenderbufferStorage(GL_RENDERBUFFER, _samples, OpenGLContext.OESDepth24 ? GL_DEPTH_COMPONENT24 : GL_DEPTH_COMPONENT16, w, h);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _renderBuffers[0]);
|
||||
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, _renderBuffers[1]);
|
||||
setupRenderbufferStorage(GL_RENDERBUFFER, _samples, GL_STENCIL_INDEX8, w, h);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, _renderBuffers[1]);
|
||||
}
|
||||
#endif
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
||||
|
||||
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
if (status != GL_FRAMEBUFFER_COMPLETE) {
|
||||
error("Framebuffer is not complete! status: %d", status);
|
||||
}
|
||||
|
||||
if (multiSample) {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffers[1]);
|
||||
status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
if (status != GL_FRAMEBUFFER_COMPLETE) {
|
||||
error("Target framebuffer is not complete! status: %d", status);
|
||||
}
|
||||
}
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
void Renderer3D::leave3D() {
|
||||
#if !USE_FORCED_GLES2
|
||||
if (OpenGLContext.type == kContextGL) {
|
||||
// Save current state (only available on OpenGL)
|
||||
glPushAttrib(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_ENABLE_BIT |
|
||||
GL_LIGHTING_BIT | GL_PIXEL_MODE_BIT | GL_SCISSOR_BIT |
|
||||
GL_TEXTURE_BIT | GL_TRANSFORM_BIT | GL_VIEWPORT_BIT);
|
||||
glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT | GL_CLIENT_VERTEX_ARRAY_BIT);
|
||||
|
||||
// prepare view
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPushMatrix();
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glPushMatrix();
|
||||
glMatrixMode(GL_TEXTURE);
|
||||
glPushMatrix();
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
// Save context by ourselves
|
||||
#define CTX_STATE(gl_param) _save ## gl_param = glIsEnabled(gl_param)
|
||||
#define CTX_BOOLEAN(gl_param) glGetBooleanv(gl_param, &_save ## gl_param)
|
||||
#define CTX_INTEGER(gl_param, count) glGetIntegerv(gl_param, _save ## gl_param)
|
||||
|
||||
CTX_STATE(GL_BLEND);
|
||||
CTX_STATE(GL_CULL_FACE);
|
||||
CTX_STATE(GL_DEPTH_TEST);
|
||||
CTX_STATE(GL_DITHER);
|
||||
CTX_STATE(GL_POLYGON_OFFSET_FILL);
|
||||
CTX_STATE(GL_SCISSOR_TEST);
|
||||
CTX_STATE(GL_STENCIL_TEST);
|
||||
|
||||
CTX_BOOLEAN(GL_DEPTH_WRITEMASK);
|
||||
|
||||
CTX_INTEGER(GL_BLEND_SRC_RGB, 1);
|
||||
CTX_INTEGER(GL_BLEND_DST_RGB, 1);
|
||||
CTX_INTEGER(GL_BLEND_SRC_ALPHA, 1);
|
||||
CTX_INTEGER(GL_BLEND_DST_ALPHA, 1);
|
||||
|
||||
CTX_INTEGER(GL_SCISSOR_BOX, 4);
|
||||
CTX_INTEGER(GL_VIEWPORT, 4);
|
||||
|
||||
#undef CTX_INTEGER
|
||||
#undef CTX_BOOLEAN
|
||||
#undef CTX_STATE
|
||||
}
|
||||
_stackLevel++;
|
||||
|
||||
#if !USE_FORCED_GLES2 || defined(USE_GLAD)
|
||||
if (_frameBuffers[1]) {
|
||||
// Frambuffer blit is impacted by scissor test, disable it
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
// We are using multisampling
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, _frameBuffers[0]);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _frameBuffers[1]);
|
||||
const uint w = _texture.getLogicalWidth();
|
||||
const uint h = _texture.getLogicalHeight();
|
||||
glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
} else if (_frameBuffers[0])
|
||||
#endif
|
||||
{
|
||||
// Don't mess with the framebuffer if one was setup
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer3D::enter3D() {
|
||||
#if !USE_FORCED_GLES2 || defined(USE_GLAD)
|
||||
if (_frameBuffers[1]) {
|
||||
// We are using multisampling
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, _frameBuffers[1]);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _frameBuffers[0]);
|
||||
} else if (_frameBuffers[0])
|
||||
#endif
|
||||
{
|
||||
// Draw on framebuffer if one was setup
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffers[0]);
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
Pipeline::disable();
|
||||
|
||||
if (_stackLevel) {
|
||||
#if !USE_FORCED_GLES2
|
||||
if (OpenGLContext.type == kContextGL) {
|
||||
glMatrixMode(GL_TEXTURE);
|
||||
glPopMatrix();
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glPopMatrix();
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPopMatrix();
|
||||
glPopClientAttrib();
|
||||
glPopAttrib();
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
#define CTX_STATE(gl_param) _save ## gl_param ? glEnable(gl_param) : glDisable(gl_param)
|
||||
|
||||
CTX_STATE(GL_BLEND);
|
||||
CTX_STATE(GL_CULL_FACE);
|
||||
CTX_STATE(GL_DEPTH_TEST);
|
||||
CTX_STATE(GL_DITHER);
|
||||
CTX_STATE(GL_POLYGON_OFFSET_FILL);
|
||||
CTX_STATE(GL_SCISSOR_TEST);
|
||||
CTX_STATE(GL_STENCIL_TEST);
|
||||
|
||||
glDepthMask(_saveGL_DEPTH_WRITEMASK);
|
||||
|
||||
glBlendFuncSeparate(_saveGL_BLEND_SRC_RGB[0], _saveGL_BLEND_DST_RGB[0],
|
||||
_saveGL_BLEND_SRC_ALPHA[0], _saveGL_BLEND_DST_ALPHA[0]);
|
||||
|
||||
glScissor(_saveGL_SCISSOR_BOX[0], _saveGL_SCISSOR_BOX[1],
|
||||
_saveGL_SCISSOR_BOX[2], _saveGL_SCISSOR_BOX[3]);
|
||||
|
||||
glViewport(_saveGL_VIEWPORT[0], _saveGL_VIEWPORT[1],
|
||||
_saveGL_VIEWPORT[2], _saveGL_VIEWPORT[3]);
|
||||
#undef CTX_STATE
|
||||
}
|
||||
_stackLevel--;
|
||||
} else {
|
||||
// 3D engine just starts, make sure the state is clean
|
||||
glDisable(GL_BLEND);
|
||||
if (OpenGLContext.imagingSupported) {
|
||||
glBlendEquation(GL_FUNC_ADD);
|
||||
glBlendFunc(GL_ONE, GL_ZERO);
|
||||
}
|
||||
|
||||
glDisable(GL_CULL_FACE);
|
||||
glCullFace(GL_BACK);
|
||||
glFrontFace(GL_CCW);
|
||||
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDepthFunc(GL_LESS);
|
||||
|
||||
glEnable(GL_DITHER);
|
||||
|
||||
glDisable(GL_POLYGON_OFFSET_FILL);
|
||||
glPolygonOffset(0.f, 0.f);
|
||||
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
glScissor(0, 0, g_system->getWidth(), g_system->getHeight());
|
||||
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
glStencilFunc(GL_ALWAYS, 0, -1u);
|
||||
glStencilMask(-1u);
|
||||
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
|
||||
|
||||
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
glLineWidth(1.f);
|
||||
|
||||
glViewport(0, 0, g_system->getWidth(), g_system->getHeight());
|
||||
|
||||
#if !USE_FORCED_GLES2
|
||||
if (OpenGLContext.type == kContextGL) {
|
||||
glDisable(GL_ALPHA_TEST);
|
||||
glAlphaFunc(GL_ALWAYS, 0);
|
||||
|
||||
glDisable(GL_COLOR_MATERIAL);
|
||||
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
|
||||
|
||||
glDisable(GL_FOG);
|
||||
glDisable(GL_LIGHTING);
|
||||
glDisable(GL_LINE_SMOOTH);
|
||||
glEnable(GL_MULTISAMPLE);
|
||||
glDisable(GL_NORMALIZE);
|
||||
glDisable(GL_POLYGON_OFFSET_FILL);
|
||||
glDisable(GL_POLYGON_STIPPLE);
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
||||
glDisable(GL_TEXTURE_1D);
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
glDisable(GL_TEXTURE_3D);
|
||||
glDisable(GL_TEXTURE_CUBE_MAP);
|
||||
glDisable(GL_TEXTURE_GEN_Q);
|
||||
glDisable(GL_TEXTURE_GEN_R);
|
||||
glDisable(GL_TEXTURE_GEN_S);
|
||||
glDisable(GL_TEXTURE_GEN_T);
|
||||
|
||||
glDisableClientState(GL_COLOR_ARRAY);
|
||||
glDisableClientState(GL_EDGE_FLAG_ARRAY);
|
||||
glDisableClientState(GL_FOG_COORD_ARRAY);
|
||||
glDisableClientState(GL_INDEX_ARRAY);
|
||||
glDisableClientState(GL_NORMAL_ARRAY);
|
||||
glDisableClientState(GL_SECONDARY_COLOR_ARRAY);
|
||||
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
|
||||
// The others targets are not modified by engines
|
||||
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_DONT_CARE);
|
||||
glLogicOp(GL_COPY);
|
||||
glPointSize(1.f);
|
||||
glShadeModel(GL_SMOOTH);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer3D::presentBuffer() {
|
||||
#if !USE_FORCED_GLES2 || defined(USE_GLAD)
|
||||
if (!_frameBuffers[1]) {
|
||||
// We are not using multisampling: contents are readily available
|
||||
// The engine just has to read from the FBO or the backbuffer
|
||||
return;
|
||||
}
|
||||
|
||||
assert(_stackLevel == 0);
|
||||
bool saveScissorTest = glIsEnabled(GL_SCISSOR_TEST);
|
||||
|
||||
// Frambuffer blit is impacted by scissor test, disable it
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
// Swap the framebuffers and blit
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, _frameBuffers[0]);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _frameBuffers[1]);
|
||||
const uint w = _texture.getLogicalWidth();
|
||||
const uint h = _texture.getLogicalHeight();
|
||||
glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||
|
||||
// Put back things as they were before
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, _frameBuffers[1]);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _frameBuffers[0]);
|
||||
|
||||
saveScissorTest ? glEnable(GL_SCISSOR_TEST) : glDisable(GL_SCISSOR_TEST);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Renderer3D::showOverlay(uint w, uint h) {
|
||||
_inOverlay = true;
|
||||
|
||||
if (_frameBuffers[0]) {
|
||||
// We have a framebuffer: the texture already contains an image
|
||||
return;
|
||||
}
|
||||
|
||||
_texture.create();
|
||||
_texture.setSize(w, h);
|
||||
Graphics::Surface background;
|
||||
background.create(w, h, Texture::getRGBAPixelFormat());
|
||||
glReadPixels(0, 0, background.w, background.h, GL_RGBA, GL_UNSIGNED_BYTE, background.getPixels());
|
||||
_texture.updateArea(Common::Rect(w, h), background);
|
||||
background.free();
|
||||
}
|
||||
|
||||
void Renderer3D::hideOverlay() {
|
||||
_inOverlay = false;
|
||||
|
||||
if (!_frameBuffers[0]) {
|
||||
// We don't have a framebuffer: destroy the texture we used to store the background
|
||||
_texture.destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
// We have a framebuffer: resize the screen if we have a pending change
|
||||
if (_pendingScreenChangeWidth >= 0 && _pendingScreenChangeHeight >= 0) {
|
||||
resize(_pendingScreenChangeWidth, _pendingScreenChangeHeight);
|
||||
_pendingScreenChangeWidth = -1;
|
||||
_pendingScreenChangeHeight = -1;
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace OpenGL
|
||||
|
||||
#endif
|
||||
99
backends/graphics/opengl/renderer3d.h
Normal file
99
backends/graphics/opengl/renderer3d.h
Normal file
@@ -0,0 +1,99 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_GRAPHICS_OPENGL_RENDERER3D_H
|
||||
#define BACKENDS_GRAPHICS_OPENGL_RENDERER3D_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
|
||||
#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
|
||||
|
||||
#include "graphics/opengl/texture.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
class Renderer3D {
|
||||
public:
|
||||
Renderer3D();
|
||||
~Renderer3D() { destroy(); }
|
||||
|
||||
void initSize(uint w, uint h, int samples, bool renderToFrameBuffer);
|
||||
void resize(uint w, uint h);
|
||||
void recreate();
|
||||
void destroy();
|
||||
|
||||
void leave3D();
|
||||
void enter3D();
|
||||
void presentBuffer();
|
||||
|
||||
void showOverlay(uint w, uint h);
|
||||
void hideOverlay();
|
||||
|
||||
const Texture &getGLTexture() const { return _texture; }
|
||||
bool hasTexture() const { return _texture.getGLTexture() != 0; }
|
||||
|
||||
void setRotation(Common::RotationMode rotation) { _texture.setRotation(rotation); }
|
||||
|
||||
protected:
|
||||
void setup();
|
||||
|
||||
int _stackLevel;
|
||||
bool _inOverlay;
|
||||
int _pendingScreenChangeWidth;
|
||||
int _pendingScreenChangeHeight;
|
||||
|
||||
bool _renderToFrameBuffer;
|
||||
int _samples;
|
||||
Texture _texture;
|
||||
GLuint _renderBuffers[3];
|
||||
GLuint _frameBuffers[2];
|
||||
|
||||
#define CTX_STATE(gl_param) GLboolean _save ## gl_param = false
|
||||
#define CTX_BOOLEAN(gl_param) GLboolean _save ## gl_param = false
|
||||
#define CTX_INTEGER(gl_param, count) GLint _save ## gl_param[count] = { 0 }
|
||||
|
||||
CTX_STATE(GL_BLEND);
|
||||
CTX_STATE(GL_CULL_FACE);
|
||||
CTX_STATE(GL_DEPTH_TEST);
|
||||
CTX_STATE(GL_DITHER);
|
||||
CTX_STATE(GL_POLYGON_OFFSET_FILL);
|
||||
CTX_STATE(GL_SCISSOR_TEST);
|
||||
CTX_STATE(GL_STENCIL_TEST);
|
||||
|
||||
CTX_BOOLEAN(GL_DEPTH_WRITEMASK);
|
||||
|
||||
CTX_INTEGER(GL_BLEND_SRC_RGB, 1);
|
||||
CTX_INTEGER(GL_BLEND_DST_RGB, 1);
|
||||
CTX_INTEGER(GL_BLEND_SRC_ALPHA, 1);
|
||||
CTX_INTEGER(GL_BLEND_DST_ALPHA, 1);
|
||||
CTX_INTEGER(GL_SCISSOR_BOX, 4);
|
||||
CTX_INTEGER(GL_VIEWPORT, 4);
|
||||
|
||||
#undef CTX_INTEGER
|
||||
#undef CTX_BOOLEAN
|
||||
#undef CTX_STATE
|
||||
};
|
||||
|
||||
} // End of namespace OpenGL
|
||||
|
||||
#endif // defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
|
||||
|
||||
#endif
|
||||
128
backends/graphics/opengl/shader.cpp
Normal file
128
backends/graphics/opengl/shader.cpp
Normal file
@@ -0,0 +1,128 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "backends/graphics/opengl/shader.h"
|
||||
#include "graphics/opengl/debug.h"
|
||||
|
||||
#if !USE_FORCED_GLES
|
||||
|
||||
namespace Common {
|
||||
DECLARE_SINGLETON(OpenGL::ShaderManager);
|
||||
}
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
namespace {
|
||||
|
||||
#pragma mark - Builtin Shader Sources -
|
||||
|
||||
const char *const g_defaultShaderAttributes[] = {
|
||||
"position", "texCoordIn", "blendColorIn", nullptr
|
||||
};
|
||||
|
||||
const char *const g_defaultVertexShader =
|
||||
"attribute vec4 position;\n"
|
||||
"attribute vec2 texCoordIn;\n"
|
||||
"attribute vec4 blendColorIn;\n"
|
||||
"\n"
|
||||
"uniform mat4 projection;\n"
|
||||
"\n"
|
||||
"varying vec2 texCoord;\n"
|
||||
"varying vec4 blendColor;\n"
|
||||
"\n"
|
||||
"void main(void) {\n"
|
||||
"\ttexCoord = texCoordIn;\n"
|
||||
"\tblendColor = blendColorIn;\n"
|
||||
"\tgl_Position = projection * position;\n"
|
||||
"}\n";
|
||||
|
||||
const char *const g_defaultFragmentShader =
|
||||
"varying vec2 texCoord;\n"
|
||||
"varying vec4 blendColor;\n"
|
||||
"\n"
|
||||
"uniform sampler2D shaderTexture;\n"
|
||||
"\n"
|
||||
"void main(void) {\n"
|
||||
"\tgl_FragColor = blendColor * texture2D(shaderTexture, texCoord);\n"
|
||||
"}\n";
|
||||
|
||||
const char *const g_lookUpFragmentShader =
|
||||
"varying vec2 texCoord;\n"
|
||||
"varying vec4 blendColor;\n"
|
||||
"\n"
|
||||
"uniform sampler2D shaderTexture;\n"
|
||||
"uniform sampler2D palette;\n"
|
||||
"\n"
|
||||
"const float scaleFactor = 255.0 / 256.0;\n"
|
||||
"const float offsetFactor = 1.0 / (2.0 * 256.0);\n"
|
||||
"\n"
|
||||
"void main(void) {\n"
|
||||
"\tvec4 index = texture2D(shaderTexture, texCoord);\n"
|
||||
"\tgl_FragColor = blendColor * texture2D(palette, vec2(index.a * scaleFactor + offsetFactor, 0.0));\n"
|
||||
"}\n";
|
||||
|
||||
} // End of anonymous namespace
|
||||
|
||||
ShaderManager::ShaderManager() {
|
||||
for (int i = 0; i < ARRAYSIZE(_builtIn); ++i) {
|
||||
_builtIn[i] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
ShaderManager::~ShaderManager() {
|
||||
for (int i = 0; i < ARRAYSIZE(_builtIn); ++i) {
|
||||
delete _builtIn[i];
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderManager::notifyDestroy() {
|
||||
for (int i = 0; i < ARRAYSIZE(_builtIn); ++i) {
|
||||
delete _builtIn[i];
|
||||
_builtIn[i] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderManager::notifyCreate() {
|
||||
// Ensure everything is destroyed
|
||||
notifyDestroy();
|
||||
|
||||
_builtIn[kDefault] = Shader::fromStrings("default", g_defaultVertexShader, g_defaultFragmentShader, g_defaultShaderAttributes, 110);
|
||||
_builtIn[kCLUT8LookUp] = Shader::fromStrings("clut8lookup", g_defaultVertexShader, g_lookUpFragmentShader, g_defaultShaderAttributes, 110);
|
||||
_builtIn[kCLUT8LookUp]->setUniform("palette", 1);
|
||||
|
||||
for (uint i = 0; i < kMaxUsages; ++i) {
|
||||
_builtIn[i]->setUniform("shaderTexture", 0);
|
||||
}
|
||||
}
|
||||
|
||||
Shader *ShaderManager::query(ShaderUsage shader) const {
|
||||
if (shader == kMaxUsages) {
|
||||
warning("OpenGL: ShaderManager::query used with kMaxUsages");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
assert(_builtIn[shader]);
|
||||
return _builtIn[shader]->clone();
|
||||
}
|
||||
|
||||
} // End of namespace OpenGL
|
||||
|
||||
#endif // !USE_FORCED_GLES
|
||||
79
backends/graphics/opengl/shader.h
Normal file
79
backends/graphics/opengl/shader.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_GRAPHICS_OPENGL_SHADER_H
|
||||
#define BACKENDS_GRAPHICS_OPENGL_SHADER_H
|
||||
|
||||
#include "graphics/opengl/system_headers.h"
|
||||
|
||||
#if !USE_FORCED_GLES
|
||||
|
||||
#include "common/singleton.h"
|
||||
|
||||
#include "graphics/opengl/shader.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
class ShaderManager : public Common::Singleton<ShaderManager> {
|
||||
public:
|
||||
enum ShaderUsage {
|
||||
/** Default shader implementing the GL fixed-function pipeline. */
|
||||
kDefault = 0,
|
||||
|
||||
/** CLUT8 look up shader. */
|
||||
kCLUT8LookUp,
|
||||
|
||||
/** Number of built-in shaders. Should not be used for query. */
|
||||
kMaxUsages
|
||||
};
|
||||
|
||||
/**
|
||||
* Notify shader manager about context destruction.
|
||||
*/
|
||||
void notifyDestroy();
|
||||
|
||||
/**
|
||||
* Notify shader manager about context creation.
|
||||
*/
|
||||
void notifyCreate();
|
||||
|
||||
/**
|
||||
* Query a built-in shader.
|
||||
* Shader returned must be destroyed by caller.
|
||||
*/
|
||||
Shader *query(ShaderUsage shader) const;
|
||||
|
||||
private:
|
||||
friend class Common::Singleton<SingletonBaseType>;
|
||||
ShaderManager();
|
||||
~ShaderManager();
|
||||
|
||||
Shader *_builtIn[kMaxUsages];
|
||||
};
|
||||
|
||||
} // End of namespace OpenGL
|
||||
|
||||
/** Shortcut for accessing the font manager. */
|
||||
#define ShaderMan (OpenGL::ShaderManager::instance())
|
||||
|
||||
#endif // !USE_FORCED_GLES
|
||||
|
||||
#endif
|
||||
652
backends/graphics/opengl/texture.cpp
Normal file
652
backends/graphics/opengl/texture.cpp
Normal file
@@ -0,0 +1,652 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "backends/graphics/opengl/texture.h"
|
||||
#include "backends/graphics/opengl/shader.h"
|
||||
#include "backends/graphics/opengl/pipelines/pipeline.h"
|
||||
#include "backends/graphics/opengl/pipelines/clut8.h"
|
||||
#include "backends/graphics/opengl/framebuffer.h"
|
||||
#include "graphics/opengl/debug.h"
|
||||
|
||||
#include "common/algorithm.h"
|
||||
#include "common/endian.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
#include "graphics/blit.h"
|
||||
|
||||
#ifdef USE_SCALERS
|
||||
#include "graphics/scalerplugin.h"
|
||||
#endif
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
//
|
||||
// Surface
|
||||
//
|
||||
|
||||
Surface::Surface()
|
||||
: _allDirty(false), _dirtyArea() {
|
||||
}
|
||||
|
||||
void Surface::copyRectToTexture(uint x, uint y, uint w, uint h, const void *srcPtr, uint srcPitch) {
|
||||
Graphics::Surface *dstSurf = getSurface();
|
||||
assert(x + w <= (uint)dstSurf->w);
|
||||
assert(y + h <= (uint)dstSurf->h);
|
||||
|
||||
addDirtyArea(Common::Rect(x, y, x + w, y + h));
|
||||
|
||||
const byte *src = (const byte *)srcPtr;
|
||||
byte *dst = (byte *)dstSurf->getBasePtr(x, y);
|
||||
const uint pitch = dstSurf->pitch;
|
||||
const uint bytesPerPixel = dstSurf->format.bytesPerPixel;
|
||||
|
||||
if (srcPitch == pitch && x == 0 && w == (uint)dstSurf->w) {
|
||||
memcpy(dst, src, h * pitch);
|
||||
} else {
|
||||
while (h-- > 0) {
|
||||
memcpy(dst, src, w * bytesPerPixel);
|
||||
dst += pitch;
|
||||
src += srcPitch;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Surface::fill(uint32 color) {
|
||||
Graphics::Surface *dst = getSurface();
|
||||
dst->fillRect(Common::Rect(dst->w, dst->h), color);
|
||||
|
||||
flagDirty();
|
||||
}
|
||||
|
||||
void Surface::fill(const Common::Rect &r, uint32 color) {
|
||||
Graphics::Surface *dst = getSurface();
|
||||
dst->fillRect(r, color);
|
||||
|
||||
addDirtyArea(r);
|
||||
}
|
||||
|
||||
void Surface::addDirtyArea(const Common::Rect &r) {
|
||||
// *sigh* Common::Rect::extend behaves unexpected whenever one of the two
|
||||
// parameters is an empty rect. Thus, we check whether the current dirty
|
||||
// area is valid. In case it is not we simply use the parameters as new
|
||||
// dirty area. Otherwise, we simply call extend.
|
||||
if (_dirtyArea.isEmpty()) {
|
||||
_dirtyArea = r;
|
||||
} else {
|
||||
_dirtyArea.extend(r);
|
||||
}
|
||||
}
|
||||
|
||||
Common::Rect Surface::getDirtyArea() const {
|
||||
if (_allDirty) {
|
||||
return Common::Rect(getWidth(), getHeight());
|
||||
} else {
|
||||
return _dirtyArea;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Surface implementations
|
||||
//
|
||||
|
||||
TextureSurface::TextureSurface(GLenum glIntFormat, GLenum glFormat, GLenum glType, const Graphics::PixelFormat &format)
|
||||
: Surface(), _format(format), _glTexture(glIntFormat, glFormat, glType),
|
||||
_textureData(), _userPixelData() {
|
||||
}
|
||||
|
||||
TextureSurface::~TextureSurface() {
|
||||
_textureData.free();
|
||||
}
|
||||
|
||||
void TextureSurface::destroy() {
|
||||
_glTexture.destroy();
|
||||
}
|
||||
|
||||
void TextureSurface::recreate() {
|
||||
_glTexture.create();
|
||||
|
||||
// In case image date exists assure it will be completely refreshed next
|
||||
// time.
|
||||
if (_textureData.getPixels()) {
|
||||
flagDirty();
|
||||
}
|
||||
}
|
||||
|
||||
void TextureSurface::enableLinearFiltering(bool enable) {
|
||||
_glTexture.enableLinearFiltering(enable);
|
||||
}
|
||||
|
||||
void TextureSurface::setRotation(Common::RotationMode rotation) {
|
||||
_glTexture.setRotation(rotation);
|
||||
}
|
||||
|
||||
void TextureSurface::allocate(uint width, uint height) {
|
||||
// Assure the texture can contain our user data.
|
||||
_glTexture.setSize(width, height);
|
||||
|
||||
// In case the needed texture dimension changed we will reinitialize the
|
||||
// texture data buffer.
|
||||
if (_glTexture.getWidth() != (uint)_textureData.w || _glTexture.getHeight() != (uint)_textureData.h) {
|
||||
// Create a buffer for the texture data.
|
||||
_textureData.create(_glTexture.getWidth(), _glTexture.getHeight(), _format);
|
||||
}
|
||||
|
||||
// Create a sub-buffer for raw access.
|
||||
_userPixelData = _textureData.getSubArea(Common::Rect(width, height));
|
||||
|
||||
// The whole texture is dirty after we changed the size. This fixes
|
||||
// multiple texture size changes without any actual update in between.
|
||||
// Without this we might try to write a too big texture into the GL
|
||||
// texture.
|
||||
flagDirty();
|
||||
}
|
||||
|
||||
void TextureSurface::updateGLTexture() {
|
||||
if (!isDirty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Common::Rect dirtyArea = getDirtyArea();
|
||||
|
||||
updateGLTexture(dirtyArea);
|
||||
}
|
||||
|
||||
void TextureSurface::updateGLTexture(Common::Rect &dirtyArea) {
|
||||
// In case we use linear filtering we might need to duplicate the last
|
||||
// pixel row/column to avoid glitches with filtering.
|
||||
if (_glTexture.isLinearFilteringEnabled()) {
|
||||
if (dirtyArea.right == _userPixelData.w && _userPixelData.w != _textureData.w) {
|
||||
uint height = dirtyArea.height();
|
||||
|
||||
const byte *src = (const byte *)_textureData.getBasePtr(_userPixelData.w - 1, dirtyArea.top);
|
||||
byte *dst = (byte *)_textureData.getBasePtr(_userPixelData.w, dirtyArea.top);
|
||||
|
||||
while (height-- > 0) {
|
||||
memcpy(dst, src, _textureData.format.bytesPerPixel);
|
||||
dst += _textureData.pitch;
|
||||
src += _textureData.pitch;
|
||||
}
|
||||
|
||||
// Extend the dirty area.
|
||||
++dirtyArea.right;
|
||||
}
|
||||
|
||||
if (dirtyArea.bottom == _userPixelData.h && _userPixelData.h != _textureData.h) {
|
||||
const byte *src = (const byte *)_textureData.getBasePtr(dirtyArea.left, _userPixelData.h - 1);
|
||||
byte *dst = (byte *)_textureData.getBasePtr(dirtyArea.left, _userPixelData.h);
|
||||
memcpy(dst, src, dirtyArea.width() * _textureData.format.bytesPerPixel);
|
||||
|
||||
// Extend the dirty area.
|
||||
++dirtyArea.bottom;
|
||||
}
|
||||
}
|
||||
|
||||
_glTexture.updateArea(dirtyArea, _textureData);
|
||||
|
||||
// We should have handled everything, thus not dirty anymore.
|
||||
clearDirty();
|
||||
}
|
||||
|
||||
FakeTextureSurface::FakeTextureSurface(GLenum glIntFormat, GLenum glFormat, GLenum glType, const Graphics::PixelFormat &format, const Graphics::PixelFormat &fakeFormat)
|
||||
: TextureSurface(glIntFormat, glFormat, glType, format),
|
||||
_fakeFormat(fakeFormat),
|
||||
_rgbData(),
|
||||
_blitFunc(nullptr),
|
||||
_palette(nullptr),
|
||||
_mask(nullptr) {
|
||||
if (_fakeFormat.isCLUT8()) {
|
||||
_palette = new uint32[256]();
|
||||
} else {
|
||||
_blitFunc = Graphics::getFastBlitFunc(format, fakeFormat);
|
||||
}
|
||||
}
|
||||
|
||||
FakeTextureSurface::~FakeTextureSurface() {
|
||||
delete[] _palette;
|
||||
delete[] _mask;
|
||||
_palette = nullptr;
|
||||
_rgbData.free();
|
||||
}
|
||||
|
||||
void FakeTextureSurface::allocate(uint width, uint height) {
|
||||
TextureSurface::allocate(width, height);
|
||||
|
||||
// We only need to reinitialize our surface when the output size
|
||||
// changed.
|
||||
if (width == (uint)_rgbData.w && height == (uint)_rgbData.h) {
|
||||
return;
|
||||
}
|
||||
|
||||
_rgbData.create(width, height, getFormat());
|
||||
}
|
||||
|
||||
void FakeTextureSurface::setMask(const byte *mask) {
|
||||
if (mask) {
|
||||
const uint numPixels = _rgbData.w * _rgbData.h;
|
||||
|
||||
if (!_mask)
|
||||
_mask = new byte[numPixels];
|
||||
|
||||
memcpy(_mask, mask, numPixels);
|
||||
} else {
|
||||
delete[] _mask;
|
||||
_mask = nullptr;
|
||||
}
|
||||
|
||||
flagDirty();
|
||||
}
|
||||
|
||||
void FakeTextureSurface::setColorKey(uint colorKey) {
|
||||
if (!_palette)
|
||||
return;
|
||||
|
||||
// The key color is set to black so the color value is pre-multiplied with the alpha value
|
||||
// to avoid color fringes due to filtering.
|
||||
// Erasing the color data is not a problem as the palette is always fully re-initialized
|
||||
// before setting the key color.
|
||||
uint32 *palette = _palette + colorKey;
|
||||
*palette = 0;
|
||||
|
||||
// A palette changes means we need to refresh the whole surface.
|
||||
flagDirty();
|
||||
}
|
||||
|
||||
void FakeTextureSurface::setPalette(uint start, uint colors, const byte *palData) {
|
||||
if (!_palette)
|
||||
return;
|
||||
|
||||
Graphics::convertPaletteToMap(_palette + start, palData, colors, _format);
|
||||
|
||||
// A palette changes means we need to refresh the whole surface.
|
||||
flagDirty();
|
||||
}
|
||||
|
||||
void FakeTextureSurface::updateGLTexture() {
|
||||
if (!isDirty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert color space.
|
||||
Graphics::Surface *outSurf = TextureSurface::getSurface();
|
||||
|
||||
const Common::Rect dirtyArea = getDirtyArea();
|
||||
|
||||
byte *dst = (byte *)outSurf->getBasePtr(dirtyArea.left, dirtyArea.top);
|
||||
const byte *src = (const byte *)_rgbData.getBasePtr(dirtyArea.left, dirtyArea.top);
|
||||
|
||||
applyPaletteAndMask(dst, src, outSurf->pitch, _rgbData.pitch, _rgbData.w, dirtyArea, outSurf->format, _rgbData.format);
|
||||
|
||||
// Do generic handling of updating the texture.
|
||||
TextureSurface::updateGLTexture();
|
||||
}
|
||||
|
||||
void FakeTextureSurface::applyPaletteAndMask(byte *dst, const byte *src, uint dstPitch, uint srcPitch, uint srcWidth, const Common::Rect &dirtyArea, const Graphics::PixelFormat &dstFormat, const Graphics::PixelFormat &srcFormat) const {
|
||||
if (_palette) {
|
||||
Graphics::crossBlitMap(dst, src, dstPitch, srcPitch, dirtyArea.width(), dirtyArea.height(), dstFormat.bytesPerPixel, _palette);
|
||||
} else if (_blitFunc) {
|
||||
_blitFunc(dst, src, dstPitch, srcPitch, dirtyArea.width(), dirtyArea.height());
|
||||
} else {
|
||||
Graphics::crossBlit(dst, src, dstPitch, srcPitch, dirtyArea.width(), dirtyArea.height(), dstFormat, srcFormat);
|
||||
}
|
||||
|
||||
if (_mask) {
|
||||
uint maskPitch = srcWidth;
|
||||
uint dirtyWidth = dirtyArea.width();
|
||||
byte destBPP = dstFormat.bytesPerPixel;
|
||||
|
||||
const byte *maskRowStart = (_mask + dirtyArea.top * maskPitch + dirtyArea.left);
|
||||
byte *dstRowStart = dst;
|
||||
|
||||
for (uint y = dirtyArea.top; y < static_cast<uint>(dirtyArea.bottom); y++) {
|
||||
if (destBPP == 2) {
|
||||
for (uint x = 0; x < dirtyWidth; x++) {
|
||||
if (!maskRowStart[x])
|
||||
reinterpret_cast<uint16 *>(dstRowStart)[x] = 0;
|
||||
}
|
||||
} else if (destBPP == 4) {
|
||||
for (uint x = 0; x < dirtyWidth; x++) {
|
||||
if (!maskRowStart[x])
|
||||
reinterpret_cast<uint32 *>(dstRowStart)[x] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
dstRowStart += dstPitch;
|
||||
maskRowStart += maskPitch;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextureSurfaceRGB555::TextureSurfaceRGB555()
|
||||
: FakeTextureSurface(GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0), Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0)) {
|
||||
}
|
||||
|
||||
void TextureSurfaceRGB555::updateGLTexture() {
|
||||
if (!isDirty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert color space.
|
||||
Graphics::Surface *outSurf = TextureSurface::getSurface();
|
||||
|
||||
const Common::Rect dirtyArea = getDirtyArea();
|
||||
|
||||
uint16 *dst = (uint16 *)outSurf->getBasePtr(dirtyArea.left, dirtyArea.top);
|
||||
const uint dstAdd = outSurf->pitch - 2 * dirtyArea.width();
|
||||
|
||||
const uint16 *src = (const uint16 *)_rgbData.getBasePtr(dirtyArea.left, dirtyArea.top);
|
||||
const uint srcAdd = _rgbData.pitch - 2 * dirtyArea.width();
|
||||
|
||||
for (int height = dirtyArea.height(); height > 0; --height) {
|
||||
for (int width = dirtyArea.width(); width > 0; --width) {
|
||||
const uint16 color = *src++;
|
||||
|
||||
*dst++ = ((color & 0x7C00) << 1) // R
|
||||
| (((color & 0x03E0) << 1) | ((color & 0x0200) >> 4)) // G
|
||||
| (color & 0x001F); // B
|
||||
}
|
||||
|
||||
src = (const uint16 *)((const byte *)src + srcAdd);
|
||||
dst = (uint16 *)((byte *)dst + dstAdd);
|
||||
}
|
||||
|
||||
// Do generic handling of updating the texture.
|
||||
TextureSurface::updateGLTexture();
|
||||
}
|
||||
|
||||
#ifdef USE_SCALERS
|
||||
|
||||
ScaledTextureSurface::ScaledTextureSurface(GLenum glIntFormat, GLenum glFormat, GLenum glType, const Graphics::PixelFormat &format, const Graphics::PixelFormat &fakeFormat)
|
||||
: FakeTextureSurface(glIntFormat, glFormat, glType, format, fakeFormat), _convData(nullptr), _scaler(nullptr), _scalerIndex(0), _scaleFactor(1), _extraPixels(0) {
|
||||
}
|
||||
|
||||
ScaledTextureSurface::~ScaledTextureSurface() {
|
||||
delete _scaler;
|
||||
|
||||
if (_convData) {
|
||||
_convData->free();
|
||||
delete _convData;
|
||||
}
|
||||
}
|
||||
|
||||
void ScaledTextureSurface::allocate(uint width, uint height) {
|
||||
TextureSurface::allocate(width * _scaleFactor, height * _scaleFactor);
|
||||
|
||||
// We only need to reinitialize our surface when the output size
|
||||
// changed.
|
||||
if (width != (uint)_rgbData.w || height != (uint)_rgbData.h) {
|
||||
_rgbData.create(width, height, _fakeFormat);
|
||||
}
|
||||
|
||||
if (_format != _fakeFormat || _extraPixels != 0) {
|
||||
if (!_convData)
|
||||
_convData = new Graphics::Surface();
|
||||
|
||||
_convData->create(width + (_extraPixels * 2), height + (_extraPixels * 2), _format);
|
||||
} else if (_convData) {
|
||||
_convData->free();
|
||||
delete _convData;
|
||||
_convData = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void ScaledTextureSurface::updateGLTexture() {
|
||||
if (!isDirty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert color space.
|
||||
Graphics::Surface *outSurf = TextureSurface::getSurface();
|
||||
|
||||
Common::Rect dirtyArea = getDirtyArea();
|
||||
|
||||
// Extend the dirty region for scalers
|
||||
// that "smear" the screen, e.g. 2xSAI
|
||||
dirtyArea.grow(_extraPixels);
|
||||
dirtyArea.clip(Common::Rect(0, 0, _rgbData.w, _rgbData.h));
|
||||
|
||||
const byte *src = (const byte *)_rgbData.getBasePtr(dirtyArea.left, dirtyArea.top);
|
||||
uint srcPitch = _rgbData.pitch;
|
||||
byte *dst;
|
||||
uint dstPitch;
|
||||
|
||||
if (_convData) {
|
||||
dst = (byte *)_convData->getBasePtr(dirtyArea.left + _extraPixels, dirtyArea.top + _extraPixels);
|
||||
dstPitch = _convData->pitch;
|
||||
|
||||
applyPaletteAndMask(dst, src, dstPitch, srcPitch, _rgbData.w, dirtyArea, _convData->format, _rgbData.format);
|
||||
|
||||
src = dst;
|
||||
srcPitch = dstPitch;
|
||||
}
|
||||
|
||||
dst = (byte *)outSurf->getBasePtr(dirtyArea.left * _scaleFactor, dirtyArea.top * _scaleFactor);
|
||||
dstPitch = outSurf->pitch;
|
||||
|
||||
if (_scaler && (uint)dirtyArea.height() >= _extraPixels) {
|
||||
_scaler->scale(src, srcPitch, dst, dstPitch, dirtyArea.width(), dirtyArea.height(), dirtyArea.left, dirtyArea.top);
|
||||
} else {
|
||||
Graphics::scaleBlit(dst, src, dstPitch, srcPitch,
|
||||
dirtyArea.width() * _scaleFactor, dirtyArea.height() * _scaleFactor,
|
||||
dirtyArea.width(), dirtyArea.height(), outSurf->format);
|
||||
}
|
||||
|
||||
dirtyArea.left *= _scaleFactor;
|
||||
dirtyArea.right *= _scaleFactor;
|
||||
dirtyArea.top *= _scaleFactor;
|
||||
dirtyArea.bottom *= _scaleFactor;
|
||||
|
||||
// Do generic handling of updating the texture.
|
||||
TextureSurface::updateGLTexture(dirtyArea);
|
||||
}
|
||||
|
||||
void ScaledTextureSurface::setScaler(uint scalerIndex, int scaleFactor) {
|
||||
const PluginList &scalerPlugins = ScalerMan.getPlugins();
|
||||
const ScalerPluginObject &scalerPlugin = scalerPlugins[scalerIndex]->get<ScalerPluginObject>();
|
||||
|
||||
// If the scalerIndex has changed, change scaler plugins
|
||||
if (_scaler && scalerIndex != _scalerIndex) {
|
||||
delete _scaler;
|
||||
_scaler = nullptr;
|
||||
}
|
||||
|
||||
if (!_scaler) {
|
||||
_scaler = scalerPlugin.createInstance(_format);
|
||||
}
|
||||
_scaler->setFactor(scaleFactor);
|
||||
|
||||
_scalerIndex = scalerIndex;
|
||||
_scaleFactor = _scaler->getFactor();
|
||||
_extraPixels = scalerPlugin.extraPixels();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !USE_FORCED_GLES
|
||||
|
||||
// _clut8Texture needs 8 bits internal precision, otherwise graphics glitches
|
||||
// can occur. GL_ALPHA does not have any internal precision requirements.
|
||||
// However, in practice (according to fuzzie) it's 8bit. If we run into
|
||||
// problems, we need to switch to GL_R8 and GL_RED, but that is only supported
|
||||
// for ARB_texture_rg and GLES3+ (EXT_rexture_rg does not support GL_R8).
|
||||
TextureSurfaceCLUT8GPU::TextureSurfaceCLUT8GPU()
|
||||
: _clut8Texture(GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE),
|
||||
_paletteTexture(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE),
|
||||
_target(new TextureTarget()), _clut8Pipeline(new CLUT8LookUpPipeline()),
|
||||
_clut8Vertices(), _clut8Data(), _userPixelData(), _palette(),
|
||||
_paletteDirty(false) {
|
||||
// Allocate space for 256 colors.
|
||||
_paletteTexture.setSize(256, 1);
|
||||
|
||||
// Setup pipeline.
|
||||
_clut8Pipeline->setFramebuffer(_target);
|
||||
_clut8Pipeline->setPaletteTexture(&_paletteTexture);
|
||||
_clut8Pipeline->setColor(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
}
|
||||
|
||||
TextureSurfaceCLUT8GPU::~TextureSurfaceCLUT8GPU() {
|
||||
delete _clut8Pipeline;
|
||||
delete _target;
|
||||
_clut8Data.free();
|
||||
}
|
||||
|
||||
void TextureSurfaceCLUT8GPU::destroy() {
|
||||
_clut8Texture.destroy();
|
||||
_paletteTexture.destroy();
|
||||
_target->destroy();
|
||||
delete _clut8Pipeline;
|
||||
_clut8Pipeline = nullptr;
|
||||
}
|
||||
|
||||
void TextureSurfaceCLUT8GPU::recreate() {
|
||||
_clut8Texture.create();
|
||||
_paletteTexture.create();
|
||||
_target->create();
|
||||
|
||||
// In case image date exists assure it will be completely refreshed next
|
||||
// time.
|
||||
if (_clut8Data.getPixels()) {
|
||||
flagDirty();
|
||||
_paletteDirty = true;
|
||||
}
|
||||
|
||||
if (_clut8Pipeline == nullptr) {
|
||||
_clut8Pipeline = new CLUT8LookUpPipeline();
|
||||
// Setup pipeline.
|
||||
_clut8Pipeline->setFramebuffer(_target);
|
||||
_clut8Pipeline->setPaletteTexture(&_paletteTexture);
|
||||
_clut8Pipeline->setColor(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
void TextureSurfaceCLUT8GPU::enableLinearFiltering(bool enable) {
|
||||
_target->getTexture()->enableLinearFiltering(enable);
|
||||
}
|
||||
|
||||
void TextureSurfaceCLUT8GPU::setRotation(Common::RotationMode rotation) {
|
||||
_target->getTexture()->setRotation(rotation);
|
||||
}
|
||||
|
||||
void TextureSurfaceCLUT8GPU::allocate(uint width, uint height) {
|
||||
// Assure the texture can contain our user data.
|
||||
_clut8Texture.setSize(width, height);
|
||||
_target->setSize(width, height);
|
||||
|
||||
// In case the needed texture dimension changed we will reinitialize the
|
||||
// texture data buffer.
|
||||
if (_clut8Texture.getWidth() != (uint)_clut8Data.w || _clut8Texture.getHeight() != (uint)_clut8Data.h) {
|
||||
// Create a buffer for the texture data.
|
||||
_clut8Data.create(_clut8Texture.getWidth(), _clut8Texture.getHeight(), Graphics::PixelFormat::createFormatCLUT8());
|
||||
}
|
||||
|
||||
// Create a sub-buffer for raw access.
|
||||
_userPixelData = _clut8Data.getSubArea(Common::Rect(width, height));
|
||||
|
||||
// Setup structures for internal rendering to _glTexture.
|
||||
_clut8Vertices[0] = 0;
|
||||
_clut8Vertices[1] = 0;
|
||||
|
||||
_clut8Vertices[2] = width;
|
||||
_clut8Vertices[3] = 0;
|
||||
|
||||
_clut8Vertices[4] = 0;
|
||||
_clut8Vertices[5] = height;
|
||||
|
||||
_clut8Vertices[6] = width;
|
||||
_clut8Vertices[7] = height;
|
||||
|
||||
// The whole texture is dirty after we changed the size. This fixes
|
||||
// multiple texture size changes without any actual update in between.
|
||||
// Without this we might try to write a too big texture into the GL
|
||||
// texture.
|
||||
flagDirty();
|
||||
}
|
||||
|
||||
Graphics::PixelFormat TextureSurfaceCLUT8GPU::getFormat() const {
|
||||
return Graphics::PixelFormat::createFormatCLUT8();
|
||||
}
|
||||
|
||||
void TextureSurfaceCLUT8GPU::setColorKey(uint colorKey) {
|
||||
// The key color is set to black so the color value is pre-multiplied with the alpha value
|
||||
// to avoid color fringes due to filtering.
|
||||
// Erasing the color data is not a problem as the palette is always fully re-initialized
|
||||
// before setting the key color.
|
||||
_palette[colorKey * 4 ] = 0x00;
|
||||
_palette[colorKey * 4 + 1] = 0x00;
|
||||
_palette[colorKey * 4 + 2] = 0x00;
|
||||
_palette[colorKey * 4 + 3] = 0x00;
|
||||
|
||||
_paletteDirty = true;
|
||||
}
|
||||
|
||||
void TextureSurfaceCLUT8GPU::setPalette(uint start, uint colors, const byte *palData) {
|
||||
byte *dst = _palette + start * 4;
|
||||
|
||||
while (colors-- > 0) {
|
||||
memcpy(dst, palData, 3);
|
||||
dst[3] = 0xFF;
|
||||
|
||||
dst += 4;
|
||||
palData += 3;
|
||||
}
|
||||
|
||||
_paletteDirty = true;
|
||||
}
|
||||
|
||||
const Texture &TextureSurfaceCLUT8GPU::getGLTexture() const {
|
||||
return *_target->getTexture();
|
||||
}
|
||||
|
||||
void TextureSurfaceCLUT8GPU::updateGLTexture() {
|
||||
const bool needLookUp = Surface::isDirty() || _paletteDirty;
|
||||
|
||||
// Update CLUT8 texture if necessary.
|
||||
if (Surface::isDirty()) {
|
||||
_clut8Texture.updateArea(getDirtyArea(), _clut8Data);
|
||||
clearDirty();
|
||||
}
|
||||
|
||||
// Update palette if necessary.
|
||||
if (_paletteDirty) {
|
||||
Graphics::Surface palSurface;
|
||||
palSurface.init(256, 1, 256, _palette, OpenGL::Texture::getRGBAPixelFormat());
|
||||
|
||||
_paletteTexture.updateArea(Common::Rect(256, 1), palSurface);
|
||||
_paletteDirty = false;
|
||||
}
|
||||
|
||||
// In case any data changed, do color look up and store result in _target.
|
||||
if (needLookUp) {
|
||||
lookUpColors();
|
||||
}
|
||||
}
|
||||
|
||||
void TextureSurfaceCLUT8GPU::lookUpColors() {
|
||||
// Setup pipeline to do color look up.
|
||||
_clut8Pipeline->activate();
|
||||
|
||||
// Do color look up.
|
||||
_clut8Pipeline->drawTexture(_clut8Texture, _clut8Vertices);
|
||||
|
||||
_clut8Pipeline->deactivate();
|
||||
}
|
||||
#endif // !USE_FORCED_GLES
|
||||
|
||||
} // End of namespace OpenGL
|
||||
341
backends/graphics/opengl/texture.h
Normal file
341
backends/graphics/opengl/texture.h
Normal file
@@ -0,0 +1,341 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_GRAPHICS_OPENGL_TEXTURE_H
|
||||
#define BACKENDS_GRAPHICS_OPENGL_TEXTURE_H
|
||||
|
||||
#include "graphics/opengl/system_headers.h"
|
||||
#include "graphics/opengl/context.h"
|
||||
#include "graphics/opengl/texture.h"
|
||||
|
||||
#include "graphics/pixelformat.h"
|
||||
#include "graphics/surface.h"
|
||||
#include "graphics/blit.h"
|
||||
|
||||
#include "common/rect.h"
|
||||
#include "common/rotationmode.h"
|
||||
|
||||
class Scaler;
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
/**
|
||||
* Interface for OpenGL implementations of a 2D surface.
|
||||
*/
|
||||
class Surface {
|
||||
public:
|
||||
Surface();
|
||||
virtual ~Surface() {}
|
||||
|
||||
/**
|
||||
* Destroy OpenGL description of surface.
|
||||
*/
|
||||
virtual void destroy() = 0;
|
||||
|
||||
/**
|
||||
* Recreate OpenGL description of surface.
|
||||
*/
|
||||
virtual void recreate() = 0;
|
||||
|
||||
/**
|
||||
* Enable or disable linear texture filtering.
|
||||
*
|
||||
* @param enable true to enable and false to disable.
|
||||
*/
|
||||
virtual void enableLinearFiltering(bool enable) = 0;
|
||||
|
||||
/**
|
||||
* Sets the rotate parameter of the texture
|
||||
*
|
||||
* @param rotation How to rotate the texture
|
||||
*/
|
||||
virtual void setRotation(Common::RotationMode rotation) = 0;
|
||||
|
||||
/**
|
||||
* Allocate storage for surface.
|
||||
*
|
||||
* @param width The desired logical width.
|
||||
* @param height The desired logical height.
|
||||
*/
|
||||
virtual void allocate(uint width, uint height) = 0;
|
||||
|
||||
/**
|
||||
* Assign a mask to the surface, where a byte value of 0 is black with 0 alpha and 1 is the normal color.
|
||||
*
|
||||
* @param mask The mask data.
|
||||
*/
|
||||
virtual void setMask(const byte *mask) {}
|
||||
|
||||
/**
|
||||
* Copy image data to the surface.
|
||||
*
|
||||
* The format of the input data needs to match the format returned by
|
||||
* getFormat.
|
||||
*
|
||||
* @param x X coordinate of upper left corner to copy data to.
|
||||
* @param y Y coordinate of upper left corner to copy data to.
|
||||
* @param w Width of the image data to copy.
|
||||
* @param h Height of the image data to copy.
|
||||
* @param src Pointer to image data.
|
||||
* @param srcPitch The number of bytes in a row of the image data.
|
||||
*/
|
||||
void copyRectToTexture(uint x, uint y, uint w, uint h, const void *src, uint srcPitch);
|
||||
|
||||
/**
|
||||
* Fill the surface with a fixed color.
|
||||
*
|
||||
* @param color Color value in format returned by getFormat.
|
||||
*/
|
||||
void fill(uint32 color);
|
||||
void fill(const Common::Rect &r, uint32 color);
|
||||
|
||||
void flagDirty() { _allDirty = true; }
|
||||
virtual bool isDirty() const { return _allDirty || !_dirtyArea.isEmpty(); }
|
||||
|
||||
virtual uint getWidth() const = 0;
|
||||
virtual uint getHeight() const = 0;
|
||||
|
||||
/**
|
||||
* @return The logical format of the texture data.
|
||||
*/
|
||||
virtual Graphics::PixelFormat getFormat() const = 0;
|
||||
|
||||
virtual Graphics::Surface *getSurface() = 0;
|
||||
virtual const Graphics::Surface *getSurface() const = 0;
|
||||
|
||||
/**
|
||||
* @return Whether the surface is having a palette.
|
||||
*/
|
||||
virtual bool hasPalette() const { return false; }
|
||||
|
||||
/**
|
||||
* Set color key for paletted textures.
|
||||
*
|
||||
* This needs to be called after any palette update affecting the color
|
||||
* key. Calling this multiple times will result in multiple color indices
|
||||
* to be treated as color keys.
|
||||
*/
|
||||
virtual void setColorKey(uint colorKey) {}
|
||||
virtual void setPalette(uint start, uint colors, const byte *palData) {}
|
||||
|
||||
virtual void setScaler(uint scalerIndex, int scaleFactor) {}
|
||||
|
||||
/**
|
||||
* Update underlying OpenGL texture to reflect current state.
|
||||
*/
|
||||
virtual void updateGLTexture() = 0;
|
||||
|
||||
/**
|
||||
* Obtain underlying OpenGL texture.
|
||||
*/
|
||||
virtual const Texture &getGLTexture() const = 0;
|
||||
protected:
|
||||
void clearDirty() { _allDirty = false; _dirtyArea = Common::Rect(); }
|
||||
|
||||
void addDirtyArea(const Common::Rect &r);
|
||||
Common::Rect getDirtyArea() const;
|
||||
private:
|
||||
bool _allDirty;
|
||||
Common::Rect _dirtyArea;
|
||||
};
|
||||
|
||||
/**
|
||||
* An OpenGL texture wrapper. It automatically takes care of all OpenGL
|
||||
* texture handling issues and also provides access to the texture data.
|
||||
*/
|
||||
class TextureSurface : public Surface {
|
||||
public:
|
||||
/**
|
||||
* Create a new texture with the specific internal format.
|
||||
*
|
||||
* @param glIntFormat The internal format to use.
|
||||
* @param glFormat The input format.
|
||||
* @param glType The input type.
|
||||
* @param format The format used for the texture input.
|
||||
*/
|
||||
TextureSurface(GLenum glIntFormat, GLenum glFormat, GLenum glType, const Graphics::PixelFormat &format);
|
||||
~TextureSurface() override;
|
||||
|
||||
void destroy() override;
|
||||
|
||||
void recreate() override;
|
||||
|
||||
void enableLinearFiltering(bool enable) override;
|
||||
void setRotation(Common::RotationMode rotation) override;
|
||||
|
||||
void allocate(uint width, uint height) override;
|
||||
|
||||
uint getWidth() const override { return _userPixelData.w; }
|
||||
uint getHeight() const override { return _userPixelData.h; }
|
||||
|
||||
/**
|
||||
* @return The logical format of the texture data.
|
||||
*/
|
||||
Graphics::PixelFormat getFormat() const override { return _format; }
|
||||
|
||||
Graphics::Surface *getSurface() override { return &_userPixelData; }
|
||||
const Graphics::Surface *getSurface() const override { return &_userPixelData; }
|
||||
|
||||
void updateGLTexture() override;
|
||||
const Texture &getGLTexture() const override { return _glTexture; }
|
||||
protected:
|
||||
const Graphics::PixelFormat _format;
|
||||
|
||||
void updateGLTexture(Common::Rect &dirtyArea);
|
||||
|
||||
private:
|
||||
Texture _glTexture;
|
||||
|
||||
Graphics::Surface _textureData;
|
||||
Graphics::Surface _userPixelData;
|
||||
};
|
||||
|
||||
class FakeTextureSurface : public TextureSurface {
|
||||
public:
|
||||
FakeTextureSurface(GLenum glIntFormat, GLenum glFormat, GLenum glType, const Graphics::PixelFormat &format, const Graphics::PixelFormat &fakeFormat);
|
||||
~FakeTextureSurface() override;
|
||||
|
||||
void allocate(uint width, uint height) override;
|
||||
void setMask(const byte *mask) override;
|
||||
|
||||
Graphics::PixelFormat getFormat() const override { return _fakeFormat; }
|
||||
|
||||
bool hasPalette() const override { return (_palette != nullptr); }
|
||||
|
||||
void setColorKey(uint colorKey) override;
|
||||
void setPalette(uint start, uint colors, const byte *palData) override;
|
||||
|
||||
Graphics::Surface *getSurface() override { return &_rgbData; }
|
||||
const Graphics::Surface *getSurface() const override { return &_rgbData; }
|
||||
|
||||
void updateGLTexture() override;
|
||||
protected:
|
||||
void applyPaletteAndMask(byte *dst, const byte *src, uint dstPitch, uint srcPitch, uint srcWidth, const Common::Rect &dirtyArea, const Graphics::PixelFormat &dstFormat, const Graphics::PixelFormat &srcFormat) const;
|
||||
|
||||
Graphics::Surface _rgbData;
|
||||
Graphics::PixelFormat _fakeFormat;
|
||||
Graphics::FastBlitFunc _blitFunc;
|
||||
uint32 *_palette;
|
||||
uint8 *_mask;
|
||||
};
|
||||
|
||||
class TextureSurfaceRGB555 : public FakeTextureSurface {
|
||||
public:
|
||||
TextureSurfaceRGB555();
|
||||
~TextureSurfaceRGB555() override {}
|
||||
|
||||
void updateGLTexture() override;
|
||||
};
|
||||
|
||||
#ifdef USE_SCALERS
|
||||
class ScaledTextureSurface : public FakeTextureSurface {
|
||||
public:
|
||||
ScaledTextureSurface(GLenum glIntFormat, GLenum glFormat, GLenum glType, const Graphics::PixelFormat &format, const Graphics::PixelFormat &fakeFormat);
|
||||
~ScaledTextureSurface() override;
|
||||
|
||||
void allocate(uint width, uint height) override;
|
||||
|
||||
uint getWidth() const override { return _rgbData.w; }
|
||||
uint getHeight() const override { return _rgbData.h; }
|
||||
Graphics::PixelFormat getFormat() const override { return _fakeFormat; }
|
||||
|
||||
bool hasPalette() const override { return (_palette != nullptr); }
|
||||
|
||||
Graphics::Surface *getSurface() override { return &_rgbData; }
|
||||
const Graphics::Surface *getSurface() const override { return &_rgbData; }
|
||||
|
||||
void updateGLTexture() override;
|
||||
|
||||
void setScaler(uint scalerIndex, int scaleFactor) override;
|
||||
protected:
|
||||
Graphics::Surface *_convData;
|
||||
Scaler *_scaler;
|
||||
uint _scalerIndex;
|
||||
uint _extraPixels;
|
||||
uint _scaleFactor;
|
||||
};
|
||||
#endif
|
||||
|
||||
#if !USE_FORCED_GLES
|
||||
class TextureTarget;
|
||||
class CLUT8LookUpPipeline;
|
||||
|
||||
class TextureSurfaceCLUT8GPU : public Surface {
|
||||
public:
|
||||
TextureSurfaceCLUT8GPU();
|
||||
~TextureSurfaceCLUT8GPU() override;
|
||||
|
||||
void destroy() override;
|
||||
|
||||
void recreate() override;
|
||||
|
||||
void enableLinearFiltering(bool enable) override;
|
||||
void setRotation(Common::RotationMode rotation) override;
|
||||
|
||||
void allocate(uint width, uint height) override;
|
||||
|
||||
bool isDirty() const override { return _paletteDirty || Surface::isDirty(); }
|
||||
|
||||
uint getWidth() const override { return _userPixelData.w; }
|
||||
uint getHeight() const override { return _userPixelData.h; }
|
||||
|
||||
Graphics::PixelFormat getFormat() const override;
|
||||
|
||||
bool hasPalette() const override { return true; }
|
||||
|
||||
void setColorKey(uint colorKey) override;
|
||||
void setPalette(uint start, uint colors, const byte *palData) override;
|
||||
|
||||
Graphics::Surface *getSurface() override { return &_userPixelData; }
|
||||
const Graphics::Surface *getSurface() const override { return &_userPixelData; }
|
||||
|
||||
void updateGLTexture() override;
|
||||
const Texture &getGLTexture() const override;
|
||||
|
||||
static bool isSupportedByContext() {
|
||||
return OpenGLContext.shadersSupported
|
||||
&& OpenGLContext.multitextureSupported
|
||||
&& OpenGLContext.framebufferObjectSupported
|
||||
// With 2^-8 precision this is too prone to approximation errors
|
||||
&& OpenGLContext.textureLookupPrecision > 8;
|
||||
}
|
||||
private:
|
||||
void lookUpColors();
|
||||
|
||||
Texture _clut8Texture;
|
||||
Texture _paletteTexture;
|
||||
|
||||
TextureTarget *_target;
|
||||
CLUT8LookUpPipeline *_clut8Pipeline;
|
||||
|
||||
GLfloat _clut8Vertices[4*2];
|
||||
|
||||
Graphics::Surface _clut8Data;
|
||||
Graphics::Surface _userPixelData;
|
||||
|
||||
byte _palette[4 * 256];
|
||||
bool _paletteDirty;
|
||||
};
|
||||
#endif // !USE_FORCED_GLES
|
||||
|
||||
} // End of namespace OpenGL
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user