[Scummvm-git-logs] scummvm master -> b483f9f22b6212981cfe4e362e1bb75c4b579ee8

Helco noreply at scummvm.org
Sun Sep 14 06:43:01 UTC 2025


This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .

Summary:
b483f9f22b ALCACHOFA: Add TinyGLRenderer


Commit: b483f9f22b6212981cfe4e362e1bb75c4b579ee8
    https://github.com/scummvm/scummvm/commit/b483f9f22b6212981cfe4e362e1bb75c4b579ee8
Author: Helco (hermann.noll at hotmail.com)
Date: 2025-09-14T08:42:48+02:00

Commit Message:
ALCACHOFA: Add TinyGLRenderer

Changed paths:
  A engines/alcachofa/graphics-tinygl.cpp
    engines/alcachofa/configure.engine
    engines/alcachofa/graphics-opengl-classic.cpp
    engines/alcachofa/graphics-opengl-shaders.cpp
    engines/alcachofa/graphics-opengl.cpp
    engines/alcachofa/graphics-opengl.h
    engines/alcachofa/module.mk


diff --git a/engines/alcachofa/configure.engine b/engines/alcachofa/configure.engine
index 3904b6543c3..d1f40219a55 100644
--- a/engines/alcachofa/configure.engine
+++ b/engines/alcachofa/configure.engine
@@ -1,3 +1,3 @@
 # This file is included from the main "configure" script
 # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
-add_engine alcachofa "Alcachofa" no "" "" "highres opengl_game_classic mpeg2"
+add_engine alcachofa "Alcachofa" no "" "" "highres mpeg2" "tinygl"
diff --git a/engines/alcachofa/graphics-opengl-classic.cpp b/engines/alcachofa/graphics-opengl-classic.cpp
index 53e21ce3bc1..df10ab5ea13 100644
--- a/engines/alcachofa/graphics-opengl-classic.cpp
+++ b/engines/alcachofa/graphics-opengl-classic.cpp
@@ -40,6 +40,7 @@ public:
 		GL_CALL(glDisableClientState(GL_INDEX_ARRAY));
 		GL_CALL(glDisableClientState(GL_TEXTURE_COORD_ARRAY));
 		resetState();
+		_currentTexture = nullptr;
 	}
 
 	void setTexture(ITexture *texture) override {
diff --git a/engines/alcachofa/graphics-opengl-shaders.cpp b/engines/alcachofa/graphics-opengl-shaders.cpp
index ea12fd0ee16..2f06d294544 100644
--- a/engines/alcachofa/graphics-opengl-shaders.cpp
+++ b/engines/alcachofa/graphics-opengl-shaders.cpp
@@ -65,7 +65,7 @@ public:
 		for (int i = 0; i < 4; i++)
 			_vbos.emplace_back(Shader::createBuffer(GL_ARRAY_BUFFER, 0, nullptr, GL_STREAM_DRAW));
 
-		_vertices.resize(8 * 6);
+		_vertices.resize(8 * 6); // heuristic, we should be lucky if we can batch 8 quads together
 
 		_whiteTexture.reset(new OpenGLTexture(1, 1, false));
 		const byte whiteData[] = { 0xff, 0xff, 0xff, 0xff };
@@ -74,6 +74,7 @@ public:
 
 	void begin() override {
 		resetState();
+		_currentTexture = nullptr;
 		_needsNewBatch = true;
 	}
 
diff --git a/engines/alcachofa/graphics-opengl.cpp b/engines/alcachofa/graphics-opengl.cpp
index 5d5eb1401df..57e7f4866be 100644
--- a/engines/alcachofa/graphics-opengl.cpp
+++ b/engines/alcachofa/graphics-opengl.cpp
@@ -45,24 +45,15 @@ static bool isCompatibleFormat(const PixelFormat &format) {
 		areComponentsInOrder(format, 3, 2, 1, 0);
 }
 
-OpenGLTexture::OpenGLTexture(int32 w, int32 h, bool withMipmaps)
-	: ITexture({ (int16)w, (int16)h })
-	, _withMipmaps(withMipmaps) {
-	glEnable(GL_TEXTURE_2D); // will error on GLES2, but that is okay
-	OpenGL::clearGLError(); // we will just ignore it
-	GL_CALL(glGenTextures(1, &_handle));
-	GL_CALL(glBindTexture(GL_TEXTURE_2D, _handle));
-	GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR));
-	GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
-	setMirrorWrap(false);
-}
+//
+// Base classes, no calls to gl* or tgl* in this area
+//
 
-OpenGLTexture::~OpenGLTexture() {
-	if (_handle != 0)
-		GL_CALL(glDeleteTextures(1, &_handle));
-}
+OpenGLTextureBase::OpenGLTextureBase(int32 w, int32 h, bool withMipmaps)
+	: ITexture({ (int16)w, (int16)h })
+	, _withMipmaps(withMipmaps) { }
 
-void OpenGLTexture::update(const Surface &surface) {
+void OpenGLTextureBase::update(const Surface &surface) {
 	assert(isCompatibleFormat(surface.format));
 	assert(surface.w == size().x && surface.h == size().y);
 
@@ -83,17 +74,10 @@ void OpenGLTexture::update(const Surface &surface) {
 			surface.format);
 		pixels = _tmpSurface.getPixels();
 	} else {
-		glEnable(GL_TEXTURE_2D);
-		OpenGL::clearGLError();
 		pixels = surface.getPixels();
 	}
 
-	GL_CALL(glBindTexture(GL_TEXTURE_2D, _handle));
-	GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, surface.w, surface.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels));
-	if (_withMipmaps)
-		GL_CALL(glGenerateMipmap(GL_TEXTURE_2D));
-	else
-		GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0));
+	updateInner(pixels);
 
 	if (!_tmpSurface.empty()) {
 		if (!_didConvertOnce)
@@ -102,6 +86,80 @@ void OpenGLTexture::update(const Surface &surface) {
 	}
 }
 
+OpenGLRendererBase::OpenGLRendererBase(Point resolution) : _resolution(resolution) {}
+
+bool OpenGLRendererBase::hasOutput() const {
+	return _currentOutput != nullptr;
+}
+
+void OpenGLRendererBase::resetState() {
+	setViewportToScreen();
+	_currentOutput = nullptr;
+	_currentLodBias = -1000.0f;
+	_currentBlendMode = (BlendMode)-1;
+	_isFirstDrawCommand = true;
+}
+
+void OpenGLRendererBase::setViewportToScreen() {
+	int32 screenWidth = g_system->getWidth();
+	int32 screenHeight = g_system->getHeight();
+	Rect viewport(
+		MIN<int32>(screenWidth, screenHeight * (float)_resolution.x / _resolution.y),
+		MIN<int32>(screenHeight, screenWidth * (float)_resolution.y / _resolution.x));
+	viewport.translate(
+		(screenWidth - viewport.width()) / 2,
+		(screenHeight - viewport.height()) / 2);
+
+	setViewportInner(viewport.left, viewport.top, viewport.width(), viewport.height());
+	setMatrices(true);
+}
+
+void OpenGLRendererBase::setViewportToRect(int16 outputWidth, int16 outputHeight) {
+	_outputSize.x = MIN(outputWidth, g_system->getWidth());
+	_outputSize.y = MIN(outputHeight, g_system->getHeight());
+	setViewportInner(0, 0, _outputSize.x, _outputSize.y);
+	setMatrices(false);
+}
+
+void OpenGLRendererBase::getQuadPositions(Vector2d topLeft, Vector2d size, Angle rotation, Vector2d positions[]) const {
+	positions[0] = topLeft + Vector2d(0, 0);
+	positions[1] = topLeft + Vector2d(0, +size.getY());
+	positions[2] = topLeft + Vector2d(+size.getX(), +size.getY());
+	positions[3] = topLeft + Vector2d(+size.getX(), 0);
+	if (abs(rotation.getDegrees()) > epsilon) {
+		const Vector2d zero(0, 0);
+		for (int i = 0; i < 4; i++)
+			positions[i].rotateAround(zero, rotation);
+	}
+}
+
+void OpenGLRendererBase::getQuadTexCoords(Vector2d texMin, Vector2d texMax, Vector2d texCoords[]) const {
+	texCoords[0] = { texMin.getX(), texMin.getY() };
+	texCoords[1] = { texMin.getX(), texMax.getY() };
+	texCoords[2] = { texMax.getX(), texMax.getY() };
+	texCoords[3] = { texMax.getX(), texMin.getY() };
+}
+
+OpenGLTexture::OpenGLTexture(int32 w, int32 h, bool withMipmaps)
+	: OpenGLTextureBase(w, h, withMipmaps) {
+	glEnable(GL_TEXTURE_2D); // will error on GLES2, but that is okay
+	OpenGL::clearGLError(); // we will just ignore it
+	GL_CALL(glGenTextures(1, &_handle));
+	GL_CALL(glBindTexture(GL_TEXTURE_2D, _handle));
+	GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR));
+	GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
+	setMirrorWrap(false);
+}
+
+//
+// OpenGL classes, calls to gl* are allowed here
+//
+
+OpenGLTexture::~OpenGLTexture() {
+	if (_handle != 0)
+		GL_CALL(glDeleteTextures(1, &_handle));
+}
+
 void OpenGLTexture::setMirrorWrap(bool wrap) {
 	if (_mirrorWrap == wrap)
 		return;
@@ -110,14 +168,29 @@ void OpenGLTexture::setMirrorWrap(bool wrap) {
 	if (wrap)
 		wrapMode = OpenGLContext.textureMirrorRepeatSupported ? GL_MIRRORED_REPEAT : GL_REPEAT;
 	else
+#if USE_FORCED_GLES2 // GLES2 does not define GL_CLAMP
+		wrapMode = GL_CLAMP_TO_EDGE;
+#else
 		wrapMode = OpenGLContext.textureEdgeClampSupported ? GL_CLAMP_TO_EDGE : GL_CLAMP;
+#endif
 
 	GL_CALL(glBindTexture(GL_TEXTURE_2D, _handle));
 	GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapMode));
 	GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapMode));
 }
 
-OpenGLRenderer::OpenGLRenderer(Point resolution) : _resolution(resolution) {
+void OpenGLTexture::updateInner(const void *pixels) {
+	glEnable(GL_TEXTURE_2D);
+	OpenGL::clearGLError();
+	GL_CALL(glBindTexture(GL_TEXTURE_2D, _handle));
+	GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size().x, size().y, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels));
+	if (_withMipmaps)
+		GL_CALL(glGenerateMipmap(GL_TEXTURE_2D));
+	else
+		GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0));
+}
+
+OpenGLRenderer::OpenGLRenderer(Point resolution) : OpenGLRendererBase(resolution) {
 	initGraphics3d(resolution.x, resolution.y);
 	GL_CALL(glDisable(GL_DEPTH_TEST));
 	GL_CALL(glDisable(GL_SCISSOR_TEST));
@@ -136,15 +209,6 @@ ScopedPtr<ITexture> OpenGLRenderer::createTexture(int32 w, int32 h, bool withMip
 	return ScopedPtr<ITexture>(new OpenGLTexture(w, h, withMipmaps));
 }
 
-void OpenGLRenderer::resetState() {
-	setViewportToScreen();
-	_currentOutput = nullptr;
-	_currentLodBias = -1000.0f;
-	_currentTexture = nullptr;
-	_currentBlendMode = (BlendMode)-1;
-	_isFirstDrawCommand = true;
-}
-
 void OpenGLRenderer::end() {
 	GL_CALL(glFlush());
 
@@ -214,34 +278,13 @@ void OpenGLRenderer::setOutput(Surface &output) {
 	}
 }
 
-bool OpenGLRenderer::hasOutput() const {
-	return _currentOutput != nullptr;
-}
-
-void OpenGLRenderer::setViewportToScreen() {
-	int32 screenWidth = g_system->getWidth();
-	int32 screenHeight = g_system->getHeight();
-	Rect viewport(
-		MIN<int32>(screenWidth, screenHeight * (float)_resolution.x / _resolution.y),
-		MIN<int32>(screenHeight, screenWidth * (float)_resolution.y / _resolution.x));
-	viewport.translate(
-		(screenWidth - viewport.width()) / 2,
-		(screenHeight - viewport.height()) / 2);
-
-	GL_CALL(glViewport(viewport.left, viewport.top, viewport.width(), viewport.height()));
-	setMatrices(true);
-}
-
-void OpenGLRenderer::setViewportToRect(int16 outputWidth, int16 outputHeight) {
-	_outputSize.x = MIN(outputWidth, g_system->getWidth());
-	_outputSize.y = MIN(outputHeight, g_system->getHeight());
-	GL_CALL(glViewport(0, 0, _outputSize.x, _outputSize.y));
-	setMatrices(false);
+void OpenGLRenderer::setViewportInner(int x, int y, int width, int height) {
+	GL_CALL(glViewport(x, y, width, height));
 }
 
 void OpenGLRenderer::checkFirstDrawCommand() {
 	// We delay clearing the screen. It is much easier for the game
-		// to switch to a framebuffer before
+	// to switch to a framebuffer before
 	if (!_isFirstDrawCommand)
 		return;
 	_isFirstDrawCommand = false;
@@ -249,27 +292,8 @@ void OpenGLRenderer::checkFirstDrawCommand() {
 	GL_CALL(glClear(GL_COLOR_BUFFER_BIT));
 }
 
-void OpenGLRenderer::getQuadPositions(Vector2d topLeft, Vector2d size, Angle rotation, Vector2d positions[]) const {
-	positions[0] = topLeft + Vector2d(0, 0);
-	positions[1] = topLeft + Vector2d(0, +size.getY());
-	positions[2] = topLeft + Vector2d(+size.getX(), +size.getY());
-	positions[3] = topLeft + Vector2d(+size.getX(), 0);
-	if (abs(rotation.getDegrees()) > epsilon) {
-		const Vector2d zero(0, 0);
-		for (int i = 0; i < 4; i++)
-			positions[i].rotateAround(zero, rotation);
-	}
-}
-
-void OpenGLRenderer::getQuadTexCoords(Vector2d texMin, Vector2d texMax, Vector2d texCoords[]) const {
-	texCoords[0] = { texMin.getX(), texMin.getY() };
-	texCoords[1] = { texMin.getX(), texMax.getY() };
-	texCoords[2] = { texMax.getX(), texMax.getY() };
-	texCoords[3] = { texMax.getX(), texMin.getY() };
-}
-
 IRenderer *IRenderer::createOpenGLRenderer(Point resolution) {
-	const auto available = Renderer::getAvailableTypes() & ~kRendererTypeTinyGL;
+	const auto available = Renderer::getAvailableTypes();
 	const auto &rendererCode = ConfMan.get("renderer");
 	RendererType rendererType = Renderer::parseTypeCode(rendererCode);
 	rendererType = (RendererType)(rendererType & available);
@@ -282,11 +306,16 @@ IRenderer *IRenderer::createOpenGLRenderer(Point resolution) {
 	case kRendererTypeOpenGL:
 		renderer = createOpenGLRendererClassic(resolution);
 		break;
+	case kRendererTypeTinyGL:
+		renderer = createTinyGLRenderer(resolution);
+		break;
 	default:
 		if (available & kRendererTypeOpenGLShaders)
 			renderer = createOpenGLRendererShaders(resolution);
 		else if (available & kRendererTypeOpenGL)
 			renderer = createOpenGLRendererClassic(resolution);
+		else if (available & kRendererTypeTinyGL)
+			renderer = createTinyGLRenderer(resolution);
 		break;
 	}
 
@@ -309,4 +338,11 @@ IRenderer *createOpenGLRendererClassic(Point _) {
 }
 #endif
 
+#ifndef USE_TINYGL
+IRenderer *createTinyGLRenderer(Point _) {
+	(void)_;
+	return nullptr;
+}
+#endif
+
 }
diff --git a/engines/alcachofa/graphics-opengl.h b/engines/alcachofa/graphics-opengl.h
index 92c842a4826..6badc386fb3 100644
--- a/engines/alcachofa/graphics-opengl.h
+++ b/engines/alcachofa/graphics-opengl.h
@@ -30,52 +30,78 @@
 
 namespace Alcachofa {
 
-class OpenGLTexture : public ITexture {
+/** This base class does not call any gl* functions so we can use it for TinyGL as well */
+class OpenGLTextureBase : public ITexture {
 public:
-	OpenGLTexture(int32 w, int32 h, bool withMipmaps);
-	~OpenGLTexture() override;
+	OpenGLTextureBase(int32 w, int32 h, bool withMipmaps);
+	~OpenGLTextureBase() override = default;
 	void update(const Graphics::Surface &surface) override;
-	void setMirrorWrap(bool wrap);
 
 	inline GLuint handle() const { return _handle; }
 
-private:
-	GLuint _handle;
+protected:
+	virtual void updateInner(const void *pixels) = 0; ///< expects pixels to be RGBA32
+
+	GLuint _handle = 0;
 	bool _withMipmaps;
 	bool _mirrorWrap = true;
 	bool _didConvertOnce = false;
 	Graphics::ManagedSurface _tmpSurface;
 };
 
-class OpenGLRenderer : public virtual IRenderer {
+class OpenGLTexture : public OpenGLTextureBase {
 public:
-	OpenGLRenderer(Common::Point resolution);
+	OpenGLTexture(int32 w, int32 h, bool withMipmaps);
+	~OpenGLTexture() override;
+
+	void setMirrorWrap(bool wrap);
+
+protected:
+	void updateInner(const void *pixels) override;
+};
+
+class OpenGLRendererBase : public virtual IRenderer {
+public:
+	OpenGLRendererBase(Common::Point resolution);
 
-	Common::ScopedPtr<ITexture> createTexture(int32 w, int32 h, bool withMipmaps) override;
-	void end() override;
-	void setOutput(Graphics::Surface &output) override;
 	bool hasOutput() const override;
 
 protected:
+	virtual void setViewportInner(int x, int y, int width, int height) = 0;
+	virtual void setMatrices(bool flipped) = 0;
+
 	void resetState();
-	void setBlendFunc(BlendMode blendMode); ///< just the blend-func, not texenv/shader uniform
 	void setViewportToScreen();
 	void setViewportToRect(int16 outputWidth, int16 outputHeight);
-	virtual void setMatrices(bool flipped) = 0;
-	void checkFirstDrawCommand();
 	void getQuadPositions(Math::Vector2d topLeft, Math::Vector2d size, Math::Angle rotation, Math::Vector2d positions[]) const;
 	void getQuadTexCoords(Math::Vector2d texMin, Math::Vector2d texMax, Math::Vector2d texCoords[]) const;
 
 	Common::Point _resolution, _outputSize;
 	Graphics::Surface *_currentOutput = nullptr;
-	OpenGLTexture *_currentTexture = nullptr;
 	BlendMode _currentBlendMode = (BlendMode)-1;
 	float _currentLodBias = 0.0f;
 	bool _isFirstDrawCommand = false;
 };
 
+class OpenGLRenderer : public OpenGLRendererBase {
+public:
+	OpenGLRenderer(Common::Point resolution);
+
+	Common::ScopedPtr<ITexture> createTexture(int32 w, int32 h, bool withMipmaps) override;
+	void end() override;
+	void setOutput(Graphics::Surface &output) override;
+
+protected:
+	void setViewportInner(int x, int y, int width, int height) override;
+	void setBlendFunc(BlendMode blendMode); ///< just the blend-func, not texenv/shader uniform
+	void checkFirstDrawCommand();
+
+	OpenGLTexture *_currentTexture = nullptr;
+};
+
 IRenderer *createOpenGLRendererClassic(Common::Point resolution);
 IRenderer *createOpenGLRendererShaders(Common::Point resolution);
+IRenderer *createTinyGLRenderer(Common::Point resolution);
 
 }
 
diff --git a/engines/alcachofa/graphics-tinygl.cpp b/engines/alcachofa/graphics-tinygl.cpp
new file mode 100644
index 00000000000..3c28184d44c
--- /dev/null
+++ b/engines/alcachofa/graphics-tinygl.cpp
@@ -0,0 +1,290 @@
+/* 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 "alcachofa/graphics.h"
+#include "alcachofa/detection.h"
+#include "alcachofa/graphics-opengl.h"
+
+#include "common/system.h"
+#include "common/config-manager.h"
+#include "engines/util.h"
+#include "graphics/tinygl/tinygl.h"
+
+using namespace Common;
+using namespace Math;
+using namespace Graphics;
+
+namespace Alcachofa {
+
+class TinyGLTexture : public OpenGLTextureBase {
+public:
+	TinyGLTexture(int32 w, int32 h, bool withMipmaps)
+		: OpenGLTextureBase(w, h, withMipmaps) {
+		tglEnable(TGL_TEXTURE_2D);
+		tglGenTextures(1, &_handle);
+		tglBindTexture(TGL_TEXTURE_2D, _handle);
+		tglTexParameteri(TGL_TEXTURE_2D, TGL_TEXTURE_MIN_FILTER, TGL_LINEAR_MIPMAP_LINEAR);
+		tglTexParameteri(TGL_TEXTURE_2D, TGL_TEXTURE_MAG_FILTER, TGL_LINEAR);
+		tglTexParameteri(TGL_TEXTURE_2D, TGL_TEXTURE_WRAP_S, TGL_MIRRORED_REPEAT);
+		tglTexParameteri(TGL_TEXTURE_2D, TGL_TEXTURE_WRAP_T, TGL_MIRRORED_REPEAT);
+	}
+
+	~TinyGLTexture() override {
+		if (_handle != 0)
+			tglDeleteTextures(1, &_handle);
+	}
+
+protected:
+	void updateInner(const void *pixels) override {
+		tglEnable(TGL_TEXTURE_2D);
+		tglBindTexture(TGL_TEXTURE_2D, _handle);
+		tglTexImage2D(TGL_TEXTURE_2D, 0, TGL_RGBA, size().x, size().y, 0, TGL_RGBA, TGL_UNSIGNED_BYTE, pixels);
+		if (_withMipmaps && false)
+			warning("NO TINYGL MIPMAPS IMPLEMENTED YET");
+		else
+			tglTexParameteri(TGL_TEXTURE_2D, TGL_TEXTURE_MAX_LEVEL, 0);
+	}
+};
+
+class TinyGLRenderer : public OpenGLRendererBase {
+public:
+	TinyGLRenderer(Point resolution) : OpenGLRendererBase(resolution) {
+		initGraphics(resolution.x, resolution.y, nullptr);
+		debug("Using framebuffer format: %s", g_system->getScreenFormat().toString().c_str());
+		if (g_system->getScreenFormat().bytesPerPixel < 2)
+			error("Alcachofa needs at least 16bit colors"); 
+		_context = TinyGL::createContext(
+			resolution.x, resolution.y,
+			g_system->getScreenFormat(),
+			1024, // some background images are even larger than this
+			false, false);
+		TinyGL::setContext(_context);
+
+		tglDisable(TGL_DEPTH_TEST);
+		tglDisable(TGL_SCISSOR_TEST);
+		tglDisable(TGL_STENCIL_TEST);
+		tglDisable(TGL_CULL_FACE);
+		tglEnable(TGL_BLEND);
+		tglDepthMask(TGL_FALSE);
+	}
+
+	~TinyGLRenderer() override {
+		if (_context != nullptr)
+			TinyGL::destroyContext(_context);
+	}
+
+	ScopedPtr<ITexture> createTexture(int32 w, int32 h, bool withMipmaps) override {
+		assert(w >= 0 && h >= 0);
+		return ScopedPtr<ITexture>(new TinyGLTexture(w, h, withMipmaps));
+	}
+
+	void begin() override {
+		resetState();
+		_currentTexture = nullptr;
+	}
+
+	void setTexture(ITexture *texture) override {
+		if (texture == _currentTexture)
+			return;
+		else if (texture == nullptr) {
+			tglDisable(TGL_TEXTURE_2D);
+			_currentTexture = nullptr;
+		} else {
+			if (_currentTexture == nullptr) {
+				tglEnable(TGL_TEXTURE_2D);
+			}
+			auto glTexture = dynamic_cast<TinyGLTexture *>(texture);
+			assert(glTexture != nullptr);
+			tglBindTexture(TGL_TEXTURE_2D, glTexture->handle());
+			_currentTexture = glTexture;
+		}
+	}
+
+	void setBlendMode(BlendMode blendMode) override {
+		if (blendMode == _currentBlendMode)
+			return;
+		switch (blendMode) {
+		case BlendMode::AdditiveAlpha:
+			tglBlendFunc(TGL_ONE, TGL_ONE_MINUS_SRC_ALPHA);
+			break;
+		case BlendMode::Additive:
+			tglBlendFunc(TGL_ONE, TGL_ONE);
+			break;
+		case BlendMode::Multiply:
+			tglBlendFunc(TGL_DST_COLOR, TGL_ONE);
+			break;
+		case BlendMode::Alpha:
+			tglBlendFunc(TGL_SRC_ALPHA, TGL_ONE_MINUS_SRC_ALPHA);
+			break;
+		case BlendMode::Tinted:
+			tglBlendFunc(TGL_ONE, TGL_ONE_MINUS_SRC_ALPHA);
+			break;
+		default: assert(false && "Invalid blend mode"); break;
+		}
+
+		tglTexEnvi(TGL_TEXTURE_ENV, TGL_TEXTURE_ENV_MODE, TGL_COMBINE);
+		switch (blendMode) {
+		case BlendMode::AdditiveAlpha:
+		case BlendMode::Additive:
+		case BlendMode::Multiply:
+			// TintAlpha * TexColor, TexAlpha
+			tglTexEnvi(TGL_TEXTURE_ENV, TGL_COMBINE_RGB, TGL_MODULATE);
+			tglTexEnvi(TGL_TEXTURE_ENV, TGL_COMBINE_ALPHA, TGL_REPLACE);
+
+			tglTexEnvi(TGL_TEXTURE_ENV, TGL_SOURCE0_RGB, TGL_TEXTURE);
+			tglTexEnvi(TGL_TEXTURE_ENV, TGL_OPERAND0_RGB, TGL_SRC_COLOR);
+			tglTexEnvi(TGL_TEXTURE_ENV, TGL_SOURCE0_ALPHA, TGL_TEXTURE);
+			tglTexEnvi(TGL_TEXTURE_ENV, TGL_OPERAND0_ALPHA, TGL_SRC_ALPHA);
+
+			tglTexEnvi(TGL_TEXTURE_ENV, TGL_SOURCE1_RGB, TGL_PRIMARY_COLOR);
+			tglTexEnvi(TGL_TEXTURE_ENV, TGL_OPERAND1_RGB, TGL_SRC_ALPHA); // alpha replaces color
+			break;
+		case BlendMode::Alpha:
+			// TexColor, TintAlpha
+			tglTexEnvi(TGL_TEXTURE_ENV, TGL_COMBINE_RGB, TGL_REPLACE);
+			tglTexEnvi(TGL_TEXTURE_ENV, TGL_COMBINE_ALPHA, TGL_REPLACE);
+
+			tglTexEnvi(TGL_TEXTURE_ENV, TGL_SOURCE0_RGB, TGL_TEXTURE);
+			tglTexEnvi(TGL_TEXTURE_ENV, TGL_OPERAND0_RGB, TGL_SRC_COLOR);
+			tglTexEnvi(TGL_TEXTURE_ENV, TGL_SOURCE0_ALPHA, TGL_PRIMARY_COLOR);
+			tglTexEnvi(TGL_TEXTURE_ENV, TGL_OPERAND0_ALPHA, TGL_SRC_ALPHA);
+			break;
+		case BlendMode::Tinted:
+			// (TintColor * TintAlpha) * TexColor, TexAlpha
+			tglTexEnvi(TGL_TEXTURE_ENV, TGL_COMBINE_RGB, TGL_MODULATE);
+			tglTexEnvi(TGL_TEXTURE_ENV, TGL_COMBINE_ALPHA, TGL_REPLACE);
+
+			tglTexEnvi(TGL_TEXTURE_ENV, TGL_SOURCE0_RGB, TGL_TEXTURE);
+			tglTexEnvi(TGL_TEXTURE_ENV, TGL_OPERAND0_RGB, TGL_SRC_COLOR);
+			tglTexEnvi(TGL_TEXTURE_ENV, TGL_SOURCE0_ALPHA, TGL_TEXTURE);
+			tglTexEnvi(TGL_TEXTURE_ENV, TGL_OPERAND0_ALPHA, TGL_SRC_ALPHA);
+
+			tglTexEnvi(TGL_TEXTURE_ENV, TGL_SOURCE1_RGB, TGL_PRIMARY_COLOR);
+			tglTexEnvi(TGL_TEXTURE_ENV, TGL_OPERAND1_RGB, TGL_SRC_COLOR); // we have to pre-multiply
+			break;
+		default:
+			assert(false && "Invalid blend mode");
+			break;
+		}
+		_currentBlendMode = blendMode;
+	}
+
+	void setLodBias(float lodBias) override {
+		_currentLodBias = lodBias;
+		// TinyGL does not support lod bias
+	}
+
+	void setOutput(Surface &output) override {
+		assert(_isFirstDrawCommand);
+		setViewportToRect(output.w, output.h);
+		_currentOutput = &output;
+
+		if (output.w > _resolution.x || output.h > _resolution.y)
+			debugC(0, kDebugGraphics, "Output is larger than screen, output will be cropped (%d, %d) > (%d, %d)",
+				output.w, output.h, _resolution.x, _resolution.y);
+
+		// no need to check format for TinyGL, we need to be prepared for conversion anyways
+	}
+
+	void end() override {
+		tglFlush();
+		TinyGL::presentBuffer();
+
+		Surface framebuffer;
+		TinyGL::getSurfaceRef(framebuffer);
+		if (_currentOutput == nullptr) {
+			g_system->copyRectToScreen(framebuffer.getPixels(), framebuffer.pitch, 0, 0, framebuffer.w, framebuffer.h);
+			g_system->updateScreen();
+		} else {
+			framebuffer = framebuffer.getSubArea(Rect(0, 0, _currentOutput->w, _currentOutput->h));
+			crossBlit(
+				(byte *)_currentOutput->getPixels(),
+				(const byte *)framebuffer.getPixels(),
+				_currentOutput->pitch,
+				framebuffer.pitch,
+				framebuffer.w,
+				framebuffer.h,
+				_currentOutput->format,
+				framebuffer.format);
+		}
+	}
+
+	void quad(
+		Vector2d topLeft,
+		Vector2d size,
+		Color color,
+		Angle rotation,
+		Vector2d texMin,
+		Vector2d texMax) override {
+		Vector2d positions[4], texCoords[4];
+		getQuadPositions(topLeft, size, rotation, positions);
+		getQuadTexCoords(texMin, texMax, texCoords);
+
+		float colors[] = { color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, color.a / 255.0f };
+		if (_currentBlendMode == BlendMode::Tinted) {
+			colors[0] *= colors[3];
+			colors[1] *= colors[3];
+			colors[2] *= colors[3];
+		}
+
+		if (_isFirstDrawCommand) {
+			_isFirstDrawCommand = false;
+			tglClearColor(0, 0, 0, 0);
+			tglClear(TGL_COLOR_BUFFER_BIT);
+		}
+
+		tglColor4fv(colors);
+		tglBegin(TGL_QUADS);
+		for (int i = 0; i < 4; i++) {
+			tglTexCoord2f(texCoords[i].getX(), texCoords[i].getY());
+			tglVertex2f(positions[i].getX(), positions[i].getY());
+		}
+		tglEnd();
+		TinyGL::presentBuffer();
+	}
+
+protected:
+	void setViewportInner(int x, int y, int width, int height) override {
+		tglViewport(x, y, width, height);
+	}
+
+	void setMatrices(bool flipped) override {
+		float bottom = flipped ? _resolution.y : 0.0f;
+		float top = flipped ? 0.0f : _resolution.y;
+
+		tglMatrixMode(TGL_PROJECTION);
+		tglLoadIdentity();
+		tglOrtho(0.0f, _resolution.x, bottom, top, -1.0f, 1.0f);
+		tglMatrixMode(TGL_MODELVIEW);
+		tglLoadIdentity();
+	}
+
+private:
+	TinyGL::ContextHandle *_context = nullptr;
+	TinyGLTexture *_currentTexture = nullptr;
+};
+
+IRenderer *createTinyGLRenderer(Point resolution) {
+	debug("Use TinyGL renderer");
+	return new TinyGLRenderer(resolution);
+}
+
+}
diff --git a/engines/alcachofa/module.mk b/engines/alcachofa/module.mk
index 575ad2fa363..d4e01639654 100644
--- a/engines/alcachofa/module.mk
+++ b/engines/alcachofa/module.mk
@@ -33,6 +33,11 @@ MODULE_OBJS += \
 	graphics-opengl-shaders.o
 endif
 
+ifdef USE_TINYGL
+MODULE_OBJS += \
+	graphics-tinygl.o
+endif
+
 # This module can be built as a plugin
 ifeq ($(ENABLE_ALCACHOFA), DYNAMIC_PLUGIN)
 PLUGIN := 1




More information about the Scummvm-git-logs mailing list