Changeset - r24963:38bc5b49d3a7
[Not reviewed]
master
0 2 0
Michael Lutz - 3 years ago 2021-02-28 09:58:56
michi@icosahedron.de
Fix #8763: [OpenGL] Cursor sprite origin can be negative.
2 files changed with 2 insertions and 2 deletions:
0 comments (0 inline, 0 general)
src/video/opengl.cpp
Show inline comments
 
@@ -1124,193 +1124,193 @@ void OpenGLBackend::ReleaseVideoBuffer(c
 
	_glBindBuffer(GL_PIXEL_UNPACK_BUFFER, this->vid_pbo);
 
	if (!this->persistent_mapping_supported) {
 
		_glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
 
		this->vid_buffer = nullptr;
 
	}
 

	
 
#ifndef NO_GL_BUFFER_SYNC
 
	if (this->persistent_mapping_supported) {
 
		_glDeleteSync(this->sync_vid_mapping);
 
		this->sync_vid_mapping = nullptr;
 
	}
 
#endif
 

	
 
	/* Update changed rect of the video buffer texture. */
 
	if (!IsEmptyRect(update_rect)) {
 
		_glActiveTexture(GL_TEXTURE0);
 
		_glBindTexture(GL_TEXTURE_2D, this->vid_texture);
 
		_glPixelStorei(GL_UNPACK_ROW_LENGTH, _screen.pitch);
 
		switch (BlitterFactory::GetCurrentBlitter()->GetScreenDepth()) {
 
			case 8:
 
				_glTexSubImage2D(GL_TEXTURE_2D, 0, update_rect.left, update_rect.top, update_rect.right - update_rect.left, update_rect.bottom - update_rect.top, GL_RED, GL_UNSIGNED_BYTE, (GLvoid *)(size_t)(update_rect.top * _screen.pitch + update_rect.left));
 
				break;
 

	
 
			default:
 
				_glTexSubImage2D(GL_TEXTURE_2D, 0, update_rect.left, update_rect.top, update_rect.right - update_rect.left, update_rect.bottom - update_rect.top, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, (GLvoid *)(size_t)(update_rect.top * _screen.pitch * 4 + update_rect.left * 4));
 
				break;
 
		}
 

	
 
#ifndef NO_GL_BUFFER_SYNC
 
		if (this->persistent_mapping_supported) this->sync_vid_mapping = _glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
 
#endif
 
	}
 
}
 

	
 
/**
 
 * Update animation buffer texture after the animation buffer was filled.
 
 * @param update_rect Rectangle encompassing the dirty region of the animation buffer.
 
 */
 
void OpenGLBackend::ReleaseAnimBuffer(const Rect &update_rect)
 
{
 
	if (this->anim_pbo == 0) return;
 

	
 
	_glBindBuffer(GL_PIXEL_UNPACK_BUFFER, this->anim_pbo);
 
	if (!this->persistent_mapping_supported) {
 
		_glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
 
		this->anim_buffer = nullptr;
 
	}
 

	
 
#ifndef NO_GL_BUFFER_SYNC
 
	if (this->persistent_mapping_supported) {
 
		_glDeleteSync(this->sync_anim_mapping);
 
		this->sync_anim_mapping = nullptr;
 
	}
 
#endif
 

	
 
	/* Update changed rect of the video buffer texture. */
 
	if (update_rect.left != update_rect.right) {
 
		_glActiveTexture(GL_TEXTURE0);
 
		_glBindTexture(GL_TEXTURE_2D, this->anim_texture);
 
		_glPixelStorei(GL_UNPACK_ROW_LENGTH, _screen.pitch);
 
		_glTexSubImage2D(GL_TEXTURE_2D, 0, update_rect.left, update_rect.top, update_rect.right - update_rect.left, update_rect.bottom - update_rect.top, GL_RED, GL_UNSIGNED_BYTE, (GLvoid *)(size_t)(update_rect.top * _screen.pitch + update_rect.left));
 

	
 
#ifndef NO_GL_BUFFER_SYNC
 
		if (this->persistent_mapping_supported) this->sync_anim_mapping = _glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
 
#endif
 
	}
 
}
 

	
 
/* virtual */ Sprite *OpenGLBackend::Encode(const SpriteLoader::Sprite *sprite, AllocatorProc *allocator)
 
{
 
	/* Allocate and construct sprite data. */
 
	Sprite *dest_sprite = (Sprite *)allocator(sizeof(*dest_sprite) + sizeof(OpenGLSprite));
 

	
 
	OpenGLSprite *gl_sprite = (OpenGLSprite *)dest_sprite->data;
 
	new (gl_sprite) OpenGLSprite(sprite->width, sprite->height, sprite->type == ST_FONT ? 1 : ZOOM_LVL_COUNT, sprite->colours);
 

	
 
	/* Upload texture data. */
 
	for (int i = 0; i < (sprite->type == ST_FONT ? 1 : ZOOM_LVL_COUNT); i++) {
 
		gl_sprite->Update(sprite[i].width, sprite[i].height, i, sprite[i].data);
 
	}
 

	
 
	dest_sprite->height = sprite->height;
 
	dest_sprite->width  = sprite->width;
 
	dest_sprite->x_offs = sprite->x_offs;
 
	dest_sprite->y_offs = sprite->y_offs;
 

	
 
	return dest_sprite;
 
}
 

	
 
/**
 
 * Render a sprite to the back buffer.
 
 * @param gl_sprite Sprite to render.
 
 * @param x X position of the sprite.
 
 * @param y Y position of the sprite.
 
 * @param zoom Zoom level to use.
 
 */
 
void OpenGLBackend::RenderOglSprite(OpenGLSprite *gl_sprite, PaletteID pal, uint x, uint y, ZoomLevel zoom)
 
void OpenGLBackend::RenderOglSprite(OpenGLSprite *gl_sprite, PaletteID pal, int x, int y, ZoomLevel zoom)
 
{
 
	/* Set textures. */
 
	bool rgb = gl_sprite->BindTextures();
 
	_glActiveTexture(GL_TEXTURE0 + 1);
 
	_glBindTexture(GL_TEXTURE_1D, this->pal_texture);
 

	
 
	/* Set palette remap. */
 
	_glActiveTexture(GL_TEXTURE0 + 3);
 
	if (pal != PAL_NONE) {
 
		_glBindTexture(GL_TEXTURE_1D, OpenGLSprite::pal_tex);
 
		if (pal != this->last_sprite_pal) {
 
			/* Different remap palette in use, update texture. */
 
			_glBindBuffer(GL_PIXEL_UNPACK_BUFFER, OpenGLSprite::pal_pbo);
 
			_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
 

	
 
			_glBufferSubData(GL_PIXEL_UNPACK_BUFFER, 0, 256, GetNonSprite(GB(pal, 0, PALETTE_WIDTH), ST_RECOLOUR) + 1);
 
			_glTexSubImage1D(GL_TEXTURE_1D, 0, 0, 256, GL_RED, GL_UNSIGNED_BYTE, 0);
 

	
 
			_glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
 

	
 
			this->last_sprite_pal = pal;
 
		}
 
	} else {
 
		_glBindTexture(GL_TEXTURE_1D, OpenGLSprite::pal_identity);
 
	}
 

	
 
	/* Set up shader program. */
 
	Dimension dim = gl_sprite->GetSize(zoom);
 
	_glUseProgram(this->sprite_program);
 
	_glUniform4f(this->sprite_sprite_loc, (float)x, (float)y, (float)dim.width, (float)dim.height);
 
	_glUniform1f(this->sprite_zoom_loc, (float)(zoom - ZOOM_LVL_BEGIN));
 
	_glUniform2f(this->sprite_screen_loc, (float)_screen.width, (float)_screen.height);
 
	_glUniform1i(this->sprite_rgb_loc, rgb ? 1 : 0);
 
	_glUniform1i(this->sprite_crash_loc, pal == PALETTE_CRASH ? 1 : 0);
 

	
 
	_glBindVertexArray(this->vao_quad);
 
	_glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
 
}
 

	
 

	
 
/* static */ GLuint OpenGLSprite::dummy_tex[] = { 0, 0 };
 
/* static */ GLuint OpenGLSprite::pal_identity = 0;
 
/* static */ GLuint OpenGLSprite::pal_tex = 0;
 
/* static */ GLuint OpenGLSprite::pal_pbo = 0;
 

	
 
/**
 
 * Create all common resources for sprite rendering.
 
 * @return True if no error occurred.
 
 */
 
/* static */ bool OpenGLSprite::Create()
 
{
 
	_glGenTextures(NUM_TEX, OpenGLSprite::dummy_tex);
 

	
 
	for (int t = TEX_RGBA; t < NUM_TEX; t++) {
 
		_glBindTexture(GL_TEXTURE_2D, OpenGLSprite::dummy_tex[t]);
 

	
 
		_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
 
		_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
 
		_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
 
		_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
 
		_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 
	}
 

	
 
	_glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
 
	_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
 

	
 
	/* Load dummy RGBA texture. */
 
	const Colour rgb_pixel(0, 0, 0);
 
	_glBindTexture(GL_TEXTURE_2D, OpenGLSprite::dummy_tex[TEX_RGBA]);
 
	_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, &rgb_pixel);
 

	
 
	/* Load dummy remap texture. */
 
	const uint pal = 0;
 
	_glBindTexture(GL_TEXTURE_2D, OpenGLSprite::dummy_tex[TEX_REMAP]);
 
	_glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, 1, 1, 0, GL_RED, GL_UNSIGNED_BYTE, &pal);
 

	
 
	/* Create palette remap textures. */
 
	std::array<uint8, 256> identity_pal;
 
	std::iota(std::begin(identity_pal), std::end(identity_pal), 0);
 

	
 
	/* Permanent texture for identity remap. */
 
	_glGenTextures(1, &OpenGLSprite::pal_identity);
 
	_glBindTexture(GL_TEXTURE_1D, OpenGLSprite::pal_identity);
 
	_glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
 
	_glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
 
	_glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAX_LEVEL, 0);
 
	_glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
 
	_glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 
	_glTexImage1D(GL_TEXTURE_1D, 0, GL_R8, 256, 0, GL_RED, GL_UNSIGNED_BYTE, identity_pal.data());
 

	
 
	/* Dynamically updated texture for remaps. */
 
	_glGenTextures(1, &OpenGLSprite::pal_tex);
 
	_glBindTexture(GL_TEXTURE_1D, OpenGLSprite::pal_tex);
 
	_glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
 
	_glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
 
	_glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAX_LEVEL, 0);
src/video/opengl.h
Show inline comments
 
/* $Id$ */
 

	
 
/*
 
 * This file is part of OpenTTD.
 
 * OpenTTD 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, version 2.
 
 * OpenTTD 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 OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file opengl.h OpenGL video driver support. */
 

	
 
#ifndef VIDEO_OPENGL_H
 
#define VIDEO_OPENGL_H
 

	
 
#include "../core/alloc_type.hpp"
 
#include "../core/geometry_type.hpp"
 
#include "../gfx_type.h"
 
#include "../spriteloader/spriteloader.hpp"
 
#include "../misc/lrucache.hpp"
 

	
 
typedef void (*OGLProc)();
 
typedef OGLProc (*GetOGLProcAddressProc)(const char *proc);
 

	
 
bool IsOpenGLVersionAtLeast(byte major, byte minor);
 
const char *FindStringInExtensionList(const char *string, const char *substring);
 

	
 
class OpenGLSprite;
 

	
 
/** Platform-independent back-end class for OpenGL video drivers. */
 
class OpenGLBackend : public ZeroedMemoryAllocator, SpriteEncoder {
 
private:
 
	static OpenGLBackend *instance; ///< Singleton instance pointer.
 

	
 
	bool persistent_mapping_supported; ///< Persistent pixel buffer mapping supported.
 
	GLsync sync_vid_mapping;           ///< Sync object for the persistently mapped video buffer.
 
	GLsync sync_anim_mapping;          ///< Sync object for the persistently mapped animation buffer.
 

	
 
	void *vid_buffer;   ///< Pointer to the mapped video buffer.
 
	GLuint vid_pbo;     ///< Pixel buffer object storing the memory used for the video driver to draw to.
 
	GLuint vid_texture; ///< Texture handle for the video buffer texture.
 
	GLuint vid_program; ///< Shader program for rendering a RGBA video buffer.
 
	GLuint pal_program; ///< Shader program for rendering a paletted video buffer.
 
	GLuint vao_quad;    ///< Vertex array object storing the rendering state for the fullscreen quad.
 
	GLuint vbo_quad;    ///< Vertex buffer with a fullscreen quad.
 
	GLuint pal_texture; ///< Palette lookup texture.
 

	
 
	void *anim_buffer;   ///< Pointer to the mapped animation buffer.
 
	GLuint anim_pbo;     ///< Pixel buffer object storing the memory used for the animation buffer.
 
	GLuint anim_texture; ///< Texture handle for the animation buffer texture.
 

	
 
	GLuint remap_program;    ///< Shader program for blending and rendering a RGBA + remap texture.
 
	GLint  remap_sprite_loc; ///< Uniform location for sprite parameters.
 
	GLint  remap_screen_loc; ///< Uniform location for screen size;
 
	GLint  remap_zoom_loc;   ///< Uniform location for sprite zoom;
 
	GLint  remap_rgb_loc;    ///< Uniform location for RGB mode flag;
 

	
 
	GLuint sprite_program;    ///< Shader program for blending and rendering a sprite to the video buffer.
 
	GLint  sprite_sprite_loc; ///< Uniform location for sprite parameters.
 
	GLint  sprite_screen_loc; ///< Uniform location for screen size;
 
	GLint  sprite_zoom_loc;   ///< Uniform location for sprite zoom;
 
	GLint  sprite_rgb_loc;    ///< Uniform location for RGB mode flag;
 
	GLint  sprite_crash_loc;  ///< Uniform location for crash remap mode flag;
 

	
 
	LRUCache<SpriteID, Sprite> cursor_cache;   ///< Cache of encoded cursor sprites.
 
	PaletteID last_sprite_pal = (PaletteID)-1; ///< Last uploaded remap palette.
 

	
 
	OpenGLBackend();
 
	~OpenGLBackend();
 

	
 
	const char *Init();
 
	bool InitShaders();
 

	
 
	void RenderOglSprite(OpenGLSprite *gl_sprite, PaletteID pal, uint x, uint y, ZoomLevel zoom);
 
	void RenderOglSprite(OpenGLSprite *gl_sprite, PaletteID pal, int x, int y, ZoomLevel zoom);
 

	
 
public:
 
	/** Get singleton instance of this class. */
 
	static inline OpenGLBackend *Get()
 
	{
 
		return OpenGLBackend::instance;
 
	}
 
	static const char *Create(GetOGLProcAddressProc get_proc);
 
	static void Destroy();
 

	
 
	void PrepareContext();
 

	
 
	void UpdatePalette(const Colour *pal, uint first, uint length);
 
	bool Resize(int w, int h, bool force = false);
 
	void Paint();
 

	
 
	void DrawMouseCursor();
 
	void ClearCursorCache();
 

	
 
	void *GetVideoBuffer();
 
	uint8 *GetAnimBuffer();
 
	void ReleaseVideoBuffer(const Rect &update_rect);
 
	void ReleaseAnimBuffer(const Rect &update_rect);
 

	
 
	/* SpriteEncoder */
 

	
 
	bool Is32BppSupported() override { return true; }
 
	uint GetSpriteAlignment() override { return 1u << (ZOOM_LVL_COUNT - 1); }
 
	Sprite *Encode(const SpriteLoader::Sprite *sprite, AllocatorProc *allocator) override;
 
};
 

	
 

	
 
/** Class that encapsulates a RGBA texture together with a paletted remap texture. */
 
class OpenGLSprite {
 
private:
 
	/** Enum of all used OpenGL texture objects. */
 
	enum Texture {
 
		TEX_RGBA,    ///< RGBA texture part.
 
		TEX_REMAP,   ///< Remap texture part.
 
		NUM_TEX
 
	};
 

	
 
	Dimension dim;
 
	GLuint tex[NUM_TEX]; ///< The texture objects.
 

	
 
	static GLuint dummy_tex[NUM_TEX]; ///< 1x1 dummy textures to substitute for unused sprite components.
 

	
 
	static GLuint pal_identity; ///< Identity texture mapping.
 
	static GLuint pal_tex;      ///< Texture for palette remap.
 
	static GLuint pal_pbo;      ///< Pixel buffer object for remap upload.
 

	
 
	static bool Create();
 
	static void Destroy();
 

	
 
	bool BindTextures();
 

	
 
public:
 
	OpenGLSprite(uint width, uint height, uint levels, SpriteColourComponent components);
 
	~OpenGLSprite();
 

	
 
	void Update(uint width, uint height, uint level, const SpriteLoader::CommonPixel *data);
 
	Dimension GetSize(ZoomLevel level) const;
 

	
 
	friend class OpenGLBackend;
 
};
 

	
 
#endif /* VIDEO_OPENGL_H */
0 comments (0 inline, 0 general)