BACKENDS: OPENGL: Add 3D rendering support

This commit is contained in:
Le Philousophe
2025-06-01 08:24:34 +02:00
parent a04fbf4bdb
commit 45afd86a42
6 changed files with 733 additions and 30 deletions
+189 -30
View File
@@ -66,6 +66,10 @@
#include "backends/graphics/opengl/pipelines/libretro/parser.h"
#endif
#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
#include "backends/graphics/opengl/renderer3d.h"
#endif
namespace OpenGL {
OpenGLGraphicsManager::OpenGLGraphicsManager()
@@ -80,6 +84,9 @@ OpenGLGraphicsManager::OpenGLGraphicsManager()
#if !USE_FORCED_GLES
, _libretroPipeline(nullptr)
#endif
#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
, _renderer3d(nullptr)
#endif
#ifdef USE_OSD
, _osdMessageChangeRequest(false), _osdMessageAlpha(0), _osdMessageFadeStartTime(0), _osdMessageSurface(nullptr),
_osdIconSurface(nullptr)
@@ -94,6 +101,9 @@ OpenGLGraphicsManager::OpenGLGraphicsManager()
OpenGLGraphicsManager::~OpenGLGraphicsManager() {
delete _gameScreen;
#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
delete _renderer3d;
#endif
delete _overlay;
delete _cursor;
delete _cursorMask;
@@ -121,6 +131,15 @@ bool OpenGLGraphicsManager::hasFeature(OSystem::Feature f) const {
case OSystem::kFeatureScalers:
#endif
return true;
#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
case OSystem::kFeatureOpenGLForGame:
// No 3D for GLES
return OpenGLContext.type != kContextGLES;
#endif
#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
case OSystem::kFeatureShadersForGame:
return OpenGLContext.enginesShadersSupported;
#endif
#if !USE_FORCED_GLES
case OSystem::kFeatureShaders:
@@ -193,9 +212,17 @@ int OpenGLGraphicsManager::getDefaultGraphicsMode() const {
bool OpenGLGraphicsManager::setGraphicsMode(int mode, uint flags) {
assert(_transactionMode != kTransactionNone);
if (flags & OSystem::kGfxModeRender3d) {
// In 3D, mode is not used
_currentState.graphicsMode = GFX_OPENGL;
_currentState.flags = flags;
return true;
}
switch (mode) {
case GFX_OPENGL:
_currentState.graphicsMode = mode;
_currentState.flags = flags;
return true;
default:
@@ -390,7 +417,8 @@ OSystem::TransactionError OpenGLGraphicsManager::endGFXTransaction() {
bool setupNewGameScreen = false;
if ( _oldState.gameWidth != _currentState.gameWidth
|| _oldState.gameHeight != _currentState.gameHeight) {
|| _oldState.gameHeight != _currentState.gameHeight
|| _oldState.flags != _currentState.flags) {
setupNewGameScreen = true;
}
@@ -415,6 +443,36 @@ OSystem::TransactionError OpenGLGraphicsManager::endGFXTransaction() {
}
#endif
const bool render3d = _currentState.flags & OSystem::kGfxModeRender3d;
assert(!render3d || OpenGLContext.type != kContextGLES);
const int antialiasing = render3d ? ConfMan.getInt("antialiasing") : 0;
// 2D engines always render on a texture that can be stretched
const bool engineSupportsArbitraryResolutions = !g_engine || !render3d || g_engine->hasFeature(Engine::kSupportsArbitraryResolutions);
// Prefer to render on framebuffer if antialiasing is requested: this avoids recreating the window
// This is only needed for 3D rendering
const bool renderToFrameBuffer = render3d && (!engineSupportsArbitraryResolutions
#if !USE_FORCED_GLES
|| _libretroPipeline
#endif
|| (antialiasing && OpenGLContext.framebufferObjectMultisampleSupported)) &&
OpenGLContext.framebufferObjectSupported;
#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
// Create the 3D renderer first to allow the platform specific code know we are in 3D
// The creation doesn't depend on any context existing
if (render3d && !_renderer3d) {
_renderer3d = new Renderer3D();
} else if (!render3d) {
delete _renderer3d;
_renderer3d = nullptr;
}
#else
if (render3d) {
error("3D rendering is not available");
}
#endif
do {
const uint desiredAspect = getDesiredGameAspectRatio();
const uint requestedWidth = _currentState.gameWidth;
@@ -430,7 +488,8 @@ OSystem::TransactionError OpenGLGraphicsManager::endGFXTransaction() {
#else
Graphics::PixelFormat::createFormatCLUT8(),
#endif
true, 0)
renderToFrameBuffer || engineSupportsArbitraryResolutions,
renderToFrameBuffer ? 0 : antialiasing)
|| !(shaderOK = loadShader(_currentState.shader))
// HACK: This is really nasty but we don't have any guarantees of
// a context existing before, which means we don't know the maximum
@@ -507,35 +566,42 @@ OSystem::TransactionError OpenGLGraphicsManager::endGFXTransaction() {
delete _gameScreen;
_gameScreen = nullptr;
bool wantScaler = _currentState.scaleFactor > 1;
if (render3d) {
#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
_renderer3d->initSize(_currentState.gameWidth, _currentState.gameHeight, antialiasing, renderToFrameBuffer);
_renderer3d->enter3D();
#endif
} else {
const bool wantScaler = _currentState.scaleFactor > 1;
#ifdef USE_RGB_COLOR
_gameScreen = createSurface(_currentState.gameFormat, false, wantScaler);
_gameScreen = createSurface(_currentState.gameFormat, false, wantScaler);
#else
_gameScreen = createSurface(Graphics::PixelFormat::createFormatCLUT8(), false, wantScaler);
_gameScreen = createSurface(Graphics::PixelFormat::createFormatCLUT8(), false, wantScaler);
#endif
assert(_gameScreen);
if (_gameScreen->hasPalette()) {
_gameScreen->setPalette(0, 256, _gamePalette);
}
assert(_gameScreen);
if (_gameScreen->hasPalette()) {
_gameScreen->setPalette(0, 256, _gamePalette);
}
#ifdef USE_SCALERS
if (wantScaler) {
_gameScreen->setScaler(_currentState.scalerIndex, _currentState.scaleFactor);
}
if (wantScaler) {
_gameScreen->setScaler(_currentState.scalerIndex, _currentState.scaleFactor);
}
#endif
_gameScreen->allocate(_currentState.gameWidth, _currentState.gameHeight);
// We fill the screen to all black or index 0 for CLUT8.
_gameScreen->allocate(_currentState.gameWidth, _currentState.gameHeight);
// We fill the screen to all black or index 0 for CLUT8.
#ifdef USE_RGB_COLOR
if (_currentState.gameFormat.bytesPerPixel == 1) {
_gameScreen->fill(0);
} else {
_gameScreen->fill(_gameScreen->getSurface()->format.RGBToColor(0, 0, 0));
}
if (_currentState.gameFormat.bytesPerPixel == 1) {
_gameScreen->fill(0);
} else {
_gameScreen->fill(_gameScreen->getSurface()->format.RGBToColor(0, 0, 0));
}
#else
_gameScreen->fill(0);
_gameScreen->fill(0);
#endif
}
}
// Update our display area and cursor scaling. This makes sure we pick up
@@ -582,14 +648,17 @@ int16 OpenGLGraphicsManager::getHeight() const {
}
void OpenGLGraphicsManager::copyRectToScreen(const void *buf, int pitch, int x, int y, int w, int h) {
assert(_gameScreen);
_gameScreen->copyRectToTexture(x, y, w, h, buf, pitch);
}
void OpenGLGraphicsManager::fillScreen(uint32 col) {
assert(_gameScreen);
_gameScreen->fill(col);
}
void OpenGLGraphicsManager::fillScreen(const Common::Rect &r, uint32 col) {
assert(_gameScreen);
_gameScreen->fill(r, col);
}
@@ -638,7 +707,11 @@ void OpenGLGraphicsManager::updateScreen() {
rotatedHeight = _windowWidth;
}
if (!_gameScreen || !_pipeline) {
if ((!_gameScreen
#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
&& !_renderer3d
#endif
) || !_pipeline) {
return;
}
@@ -659,8 +732,11 @@ void OpenGLGraphicsManager::updateScreen() {
// We only update the screen when there actually have been any changes.
if ( !_forceRedraw
&& !_cursorNeedsRedraw
&& !_gameScreen->isDirty()
&& !_cursorNeedsRedraw
#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
&& !_renderer3d
#endif
&& !_gameScreen->isDirty() // if _renderer3d is nullptr, _gameScreen is not null
#if !USE_FORCED_GLES
&& !(_libretroPipeline && _libretroPipeline->isAnimated())
#endif
@@ -673,8 +749,17 @@ void OpenGLGraphicsManager::updateScreen() {
return;
}
#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
if (_renderer3d) {
_renderer3d->leave3D();
}
#endif
// Update changes to textures.
_gameScreen->updateGLTexture();
if (_gameScreen) {
_gameScreen->updateGLTexture();
}
if (_cursorVisible && _cursor) {
_cursor->updateGLTexture();
}
@@ -691,8 +776,13 @@ void OpenGLGraphicsManager::updateScreen() {
_pipeline->activate();
// Clear the screen buffer.
GL_CALL(glClear(GL_COLOR_BUFFER_BIT));
// Clear the screen buffer (when the engine has not drawn on it directly).
#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
if (!_renderer3d || _renderer3d->hasTexture())
#endif
{
GL_CALL(glClear(GL_COLOR_BUFFER_BIT));
}
if (!_overlayVisible) {
// The scissor test is enabled to:
@@ -708,7 +798,14 @@ void OpenGLGraphicsManager::updateScreen() {
_targetBuffer->enableBlend(Framebuffer::kBlendModeOpaque);
// First step: Draw the (virtual) game screen.
_pipeline->drawTexture(_gameScreen->getGLTexture(), _gameDrawRect.left, _gameDrawRect.top, _gameDrawRect.width(), _gameDrawRect.height());
if (_gameScreen) {
_pipeline->drawTexture(_gameScreen->getGLTexture(), _gameDrawRect.left, _gameDrawRect.top, _gameDrawRect.width(), _gameDrawRect.height());
}
#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
else if (_renderer3d && _renderer3d->hasTexture()) {
_pipeline->drawTexture(_renderer3d->getGLTexture(), _gameDrawRect.left, _gameDrawRect.top, _gameDrawRect.width(), _gameDrawRect.height());
}
#endif
// Second step: Draw the cursor if necessary and we are not in GUI and it
#if !USE_FORCED_GLES
@@ -800,13 +897,21 @@ void OpenGLGraphicsManager::updateScreen() {
_cursorNeedsRedraw = false;
_forceRedraw = false;
refreshScreen();
#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
if (_renderer3d) {
_renderer3d->enter3D();
}
#endif
}
Graphics::Surface *OpenGLGraphicsManager::lockScreen() {
assert(_gameScreen);
return _gameScreen->getSurface();
}
void OpenGLGraphicsManager::unlockScreen() {
assert(_gameScreen);
_gameScreen->flagDirty();
}
@@ -832,6 +937,24 @@ int16 OpenGLGraphicsManager::getOverlayHeight() const {
}
}
void OpenGLGraphicsManager::showOverlay(bool inGUI) {
#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
if (!_overlayVisible && _renderer3d) {
_renderer3d->showOverlay(getOverlayWidth(), getOverlayHeight());
}
#endif
WindowedGraphicsManager::showOverlay(inGUI);
}
void OpenGLGraphicsManager::hideOverlay() {
#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
if (_overlayVisible && _renderer3d) {
_renderer3d->hideOverlay();
}
#endif
WindowedGraphicsManager::hideOverlay();
}
Graphics::PixelFormat OpenGLGraphicsManager::getOverlayFormat() const {
return _overlay->getFormat();
}
@@ -1251,6 +1374,7 @@ void OpenGLGraphicsManager::displayActivityIconOnOSD(const Graphics::Surface *ic
}
void OpenGLGraphicsManager::setPalette(const byte *colors, uint start, uint num) {
assert(_gameScreen);
assert(_gameScreen->hasPalette());
memcpy(_gamePalette + start * 3, colors, num * 3);
@@ -1261,6 +1385,7 @@ void OpenGLGraphicsManager::setPalette(const byte *colors, uint start, uint num)
}
void OpenGLGraphicsManager::grabPalette(byte *colors, uint start, uint num) const {
assert(_gameScreen);
assert(_gameScreen->hasPalette());
memcpy(colors, _gamePalette + start * 3, num * 3);
@@ -1403,6 +1528,12 @@ void OpenGLGraphicsManager::notifyContextCreate(ContextType type,
_gameScreen->recreate();
}
#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
if (_renderer3d) {
_renderer3d->recreate();
}
#endif
if (_overlay) {
_overlay->recreate();
}
@@ -1431,6 +1562,12 @@ void OpenGLGraphicsManager::notifyContextDestroy() {
_gameScreen->destroy();
}
#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
if (_renderer3d) {
_renderer3d->destroy();
}
#endif
if (_overlay) {
_overlay->destroy();
}
@@ -1632,15 +1769,32 @@ int OpenGLGraphicsManager::getGameRenderScale() const {
}
void OpenGLGraphicsManager::recalculateDisplayAreas() {
if (!_gameScreen) {
if (!_gameScreen
#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
&& !_renderer3d
#endif
) {
return;
}
#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
const bool engineSupportsArbitraryResolutions = !g_engine || g_engine->hasFeature(Engine::kSupportsArbitraryResolutions);
if (_renderer3d && engineSupportsArbitraryResolutions) {
_currentState.gameWidth = getOverlayWidth();
_currentState.gameHeight = getOverlayHeight();
_renderer3d->resize(_currentState.gameWidth, _currentState.gameHeight);
}
#endif
WindowedGraphicsManager::recalculateDisplayAreas();
#if !USE_FORCED_GLES
if (_libretroPipeline) {
#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
const Texture &gameScreenTexture = _gameScreen ? _gameScreen->getGLTexture() : _renderer3d->getGLTexture();
#else
const Texture &gameScreenTexture = _gameScreen->getGLTexture();
#endif
_libretroPipeline->setDisplaySizes(gameScreenTexture.getLogicalWidth(), gameScreenTexture.getLogicalHeight(),
_gameDrawRect);
}
@@ -1691,7 +1845,11 @@ void OpenGLGraphicsManager::updateCursorPalette() {
}
void OpenGLGraphicsManager::recalculateCursorScaling() {
if (!_cursor || !_gameScreen) {
if (!_cursor || (!_gameScreen
#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
&& !_renderer3d
#endif
)) {
return;
}
@@ -1706,7 +1864,8 @@ void OpenGLGraphicsManager::recalculateCursorScaling() {
// In case scaling is actually enabled we will scale the cursor according
// to the game screen.
if (!_cursorDontScale) {
// In 3D mode, there is no scaling
if (!_cursorDontScale && _gameScreen) {
const frac_t screenScaleFactorX = intToFrac(_gameDrawRect.width()) / _gameScreen->getWidth();
const frac_t screenScaleFactorY = intToFrac(_gameDrawRect.height()) / _gameScreen->getHeight();
@@ -49,6 +49,9 @@ 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
@@ -115,6 +118,8 @@ public:
int16 getOverlayWidth() const override;
int16 getOverlayHeight() const override;
void showOverlay(bool inGUI) override;
void hideOverlay() override;
Graphics::PixelFormat getOverlayFormat() const override;
@@ -196,6 +201,7 @@ protected:
#endif
bool aspectRatioCorrection;
int graphicsMode;
uint flags;
bool filtering;
uint scalerIndex;
@@ -354,6 +360,13 @@ protected:
*/
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.
*/
+430
View File
@@ -0,0 +1,430 @@
/* 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);
}
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, true);
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, true);
setup();
// Rebind the framebuffer
if (_frameBuffers[1]) {
// We are using multisampling
glBindFramebuffer(GL_READ_FRAMEBUFFER, _frameBuffers[1]);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _frameBuffers[0]);
} else if (_frameBuffers[0]) {
// 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() {
const bool multiSample = _samples > 1;
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 (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]);
}
#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 (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 {
// 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 (_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]) {
// Don't mess with the framebuffer if one was setup
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
}
void Renderer3D::enter3D() {
if (_frameBuffers[1]) {
// We are using multisampling
glBindFramebuffer(GL_READ_FRAMEBUFFER, _frameBuffers[1]);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _frameBuffers[0]);
} else if (_frameBuffers[0]) {
// Draw on framebuffer if one was setup
glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffers[0]);
}
glBindTexture(GL_TEXTURE_2D, 0);
Pipeline::disable();
if (_stackLevel) {
if (OpenGLContext.type == kContextGL) {
glMatrixMode(GL_TEXTURE);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glPopClientAttrib();
glPopAttrib();
} else {
#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);
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 (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);
}
}
}
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, true);
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
+96
View File
@@ -0,0 +1,96 @@
/* 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 showOverlay(uint w, uint h);
void hideOverlay();
const Texture &getGLTexture() const { return _texture; }
bool hasTexture() const { return _texture.getGLTexture() != 0; }
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
#define CTX_BOOLEAN(gl_param) GLboolean _save ## gl_param
#define CTX_INTEGER(gl_param, count) GLint _save ## gl_param[count]
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
@@ -789,7 +789,11 @@ bool OpenGLSdlGraphicsManager::setupMode(uint width, uint height) {
height = _desiredFullscreenHeight;
flags |= SDL_FULLSCREEN;
}
#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
if (_resizable && !_renderer3d) {
#else
if (_resizable) {
#endif
// In SDL1.2, resizing the window may invalidate the context
// This would kill all the engine objects
flags |= SDL_RESIZABLE;
+1
View File
@@ -176,6 +176,7 @@ ifdef USE_OPENGL
MODULE_OBJS += \
graphics/opengl/framebuffer.o \
graphics/opengl/opengl-graphics.o \
graphics/opengl/renderer3d.o \
graphics/opengl/shader.o \
graphics/opengl/texture.o \
graphics/opengl/pipelines/clut8.o \