Files @ r24874:b9bdc5d49a71
Branch filter:

Location: cpp/openttd-patchpack/source/src/sprite.h

Patric Stout
Fix #6319: [Win32] don't use clipping; draw whole screen every frame (#8726)

When we clip the region that is only been redrawn, something
weird happens on Windows. When pushing 60 frames per second on a
60Hz monitor, it appears that the clipped region is often shown
of another frame, instead of the current.

Examples of this are:
- pause the game, move your mouse to the left, and at the right
speed it totally disappears.
- fast aircrafts seem to be in several places at once, weirdly
lagging behind.
- in title screen, moving your mouse gives you the idea it is
jumping places, instead of smooth movements.

In the end, if you do nothing, everything is correct, so it is
eventually consistent. Just when we are firing many BitBlt in
a clipped region, the in-between is not.

What goes wrong exactly, I honestly do not know. On every frame
that we push to the DC is a mouse painted, but visually it
sometimes appears like it is not. Recording with external software
shows it really is there.
It is also not our eyes playing tricks on us, as the first example
makes it really clear the mouse pointer really is not painted.

And to be clear, with the mouse this is easiest reproduceable,
as high-speed objects are influences by this most. But this happens
for all movement that redraws small regions.

Either way, not using clipped regions resolves the issue completely,
and there appears to be little to no penalty (I failed to measure
any impact of drawing the full screen). So better have a good game
than fast code, I guess?
/*
 * 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 sprite.h Base for drawing complex sprites. */

#ifndef SPRITE_H
#define SPRITE_H

#include "transparency.h"

#include "table/sprites.h"

#define GENERAL_SPRITE_COLOUR(colour) ((colour) + PALETTE_RECOLOUR_START)
#define COMPANY_SPRITE_COLOUR(owner) (GENERAL_SPRITE_COLOUR(_company_colours[owner]))

/* The following describes bunch of sprites to be drawn together in a single 3D
 * bounding box. Used especially for various multi-sprite buildings (like
 * depots or stations): */

/** A tile child sprite and palette to draw for stations etc, with 3D bounding box */
struct DrawTileSeqStruct {
	int8 delta_x; ///< \c 0x80 is sequence terminator
	int8 delta_y;
	int8 delta_z; ///< \c 0x80 identifies child sprites
	byte size_x;
	byte size_y;
	byte size_z;
	PalSpriteID image;

	/** Make this struct a sequence terminator. */
	void MakeTerminator()
	{
		this->delta_x = (int8)0x80;
	}

	/** Check whether this is a sequence terminator. */
	bool IsTerminator() const
	{
		return (byte)this->delta_x == 0x80;
	}

	/** Check whether this is a parent sprite with a boundingbox. */
	bool IsParentSprite() const
	{
		return (byte)this->delta_z != 0x80;
	}
};

/**
 * Ground palette sprite of a tile, together with its sprite layout.
 * This struct is used for static sprite layouts in the code.
 * For allocated ones from NewGRF see #NewGRFSpriteLayout.
 */
struct DrawTileSprites {
	PalSpriteID ground;           ///< Palette and sprite for the ground
	const DrawTileSeqStruct *seq; ///< Array of child sprites. Terminated with a terminator entry
};

/**
 * This structure is the same for both Industries and Houses.
 * Buildings here reference a general type of construction
 */
struct DrawBuildingsTileStruct {
	PalSpriteID ground;
	PalSpriteID building;
	byte subtile_x;
	byte subtile_y;
	byte width;
	byte height;
	byte dz;
	byte draw_proc;  // this allows to specify a special drawing procedure.
};

/** Iterate through all DrawTileSeqStructs in DrawTileSprites. */
#define foreach_draw_tile_seq(idx, list) for (idx = list; !idx->IsTerminator(); idx++)

void DrawCommonTileSeq(const struct TileInfo *ti, const DrawTileSprites *dts, TransparencyOption to, int32 orig_offset, uint32 newgrf_offset, PaletteID default_palette, bool child_offset_is_unsigned);
void DrawCommonTileSeqInGUI(int x, int y, const DrawTileSprites *dts, int32 orig_offset, uint32 newgrf_offset, PaletteID default_palette, bool child_offset_is_unsigned);

/**
 * Draw tile sprite sequence on tile with railroad specifics.
 * @param total_offset Spriteoffset from normal rail to current railtype.
 * @param newgrf_offset Startsprite of the Action1 to use.
 */
static inline void DrawRailTileSeq(const struct TileInfo *ti, const DrawTileSprites *dts, TransparencyOption to, int32 total_offset, uint32 newgrf_offset, PaletteID default_palette)
{
	DrawCommonTileSeq(ti, dts, to, total_offset, newgrf_offset, default_palette, false);
}

/**
 * Draw tile sprite sequence in GUI with railroad specifics.
 * @param total_offset Spriteoffset from normal rail to current railtype.
 * @param newgrf_offset Startsprite of the Action1 to use.
 */
static inline void DrawRailTileSeqInGUI(int x, int y, const DrawTileSprites *dts, int32 total_offset, uint32 newgrf_offset, PaletteID default_palette)
{
	DrawCommonTileSeqInGUI(x, y, dts, total_offset, newgrf_offset, default_palette, false);
}

/**
 * Draw TTD sprite sequence on tile.
 */
static inline void DrawOrigTileSeq(const struct TileInfo *ti, const DrawTileSprites *dts, TransparencyOption to, PaletteID default_palette)
{
	DrawCommonTileSeq(ti, dts, to, 0, 0, default_palette, false);
}

/**
 * Draw TTD sprite sequence in GUI.
 */
static inline void DrawOrigTileSeqInGUI(int x, int y, const DrawTileSprites *dts, PaletteID default_palette)
{
	DrawCommonTileSeqInGUI(x, y, dts, 0, 0, default_palette, false);
}

/**
 * Draw NewGRF industrytile or house sprite layout
 * @param stage Sprite inside the Action1 spritesets to use, i.e. construction stage.
 */
static inline void DrawNewGRFTileSeq(const struct TileInfo *ti, const DrawTileSprites *dts, TransparencyOption to, uint32 stage, PaletteID default_palette)
{
	DrawCommonTileSeq(ti, dts, to, 0, stage, default_palette, true);
}

/**
 * Draw NewGRF object in GUI
 * @param stage Sprite inside the Action1 spritesets to use, i.e. construction stage.
 */
static inline void DrawNewGRFTileSeqInGUI(int x, int y, const DrawTileSprites *dts, uint32 stage, PaletteID default_palette)
{
	DrawCommonTileSeqInGUI(x, y, dts, 0, stage, default_palette, true);
}

/**
 * Applies PALETTE_MODIFIER_TRANSPARENT and PALETTE_MODIFIER_COLOUR to a palette entry of a sprite layout entry
 * @note for ground sprites use #GroundSpritePaletteTransform
 * @note Not useable for OTTD internal spritelayouts from table/xxx_land.h as PALETTE_MODIFIER_TRANSPARENT is only set
 *       when to use the default palette.
 *
 * @param image The sprite to draw
 * @param pal The palette from the sprite layout
 * @param default_pal The default recolour sprite to use (typically company colour resp. random industry/house colour)
 * @return The palette to use
 */
static inline PaletteID SpriteLayoutPaletteTransform(SpriteID image, PaletteID pal, PaletteID default_pal)
{
	if (HasBit(image, PALETTE_MODIFIER_TRANSPARENT) || HasBit(image, PALETTE_MODIFIER_COLOUR)) {
		return (pal != 0 ? pal : default_pal);
	} else {
		return PAL_NONE;
	}
}

/**
 * Applies PALETTE_MODIFIER_COLOUR to a palette entry of a ground sprite
 * @note Not useable for OTTD internal spritelayouts from table/xxx_land.h as PALETTE_MODIFIER_TRANSPARENT is only set
 *       when to use the default palette.
 *
 * @param image The sprite to draw
 * @param pal The palette from the sprite layout
 * @param default_pal The default recolour sprite to use (typically company colour resp. random industry/house colour)
 * @return The palette to use
 */
static inline PaletteID GroundSpritePaletteTransform(SpriteID image, PaletteID pal, PaletteID default_pal)
{
	if (HasBit(image, PALETTE_MODIFIER_COLOUR)) {
		return (pal != 0 ? pal : default_pal);
	} else {
		return PAL_NONE;
	}
}

#endif /* SPRITE_H */