Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

View 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

View 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

File diff suppressed because it is too large Load Diff

View 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

View 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

View 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

View 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

View 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

File diff suppressed because it is too large Load Diff

View 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

View 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", &parameters)) {
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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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