From db924a4681f019a6372f5192693af5aede36d080 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Tue, 16 Jan 2018 23:23:52 +0000 Subject: Codechange: [Blitter] Change DrawLine to be templated This is remove per-pixel overheads due to use of the SetPixel virtual method. These overheads included: * expensive virtual method call which prevents inlining * palette lookup for every pixel * branch on whether palette animation is enabled on every pixel Regenerate project files. --- src/blitter/32bpp_anim.cpp | 19 ++++++ src/blitter/32bpp_anim.hpp | 1 + src/blitter/32bpp_base.cpp | 9 +++ src/blitter/32bpp_base.hpp | 1 + src/blitter/8bpp_base.cpp | 8 +++ src/blitter/8bpp_base.hpp | 1 + src/blitter/base.cpp | 138 -------------------------------------------- src/blitter/base.hpp | 4 +- src/blitter/common.hpp | 141 +++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 183 insertions(+), 139 deletions(-) delete mode 100644 src/blitter/base.cpp create mode 100644 src/blitter/common.hpp (limited to 'src') diff --git a/src/blitter/32bpp_anim.cpp b/src/blitter/32bpp_anim.cpp index 98ae22b00..27b1fbd5b 100644 --- a/src/blitter/32bpp_anim.cpp +++ b/src/blitter/32bpp_anim.cpp @@ -12,6 +12,7 @@ #include "../stdafx.h" #include "../video/video_driver.hpp" #include "32bpp_anim.hpp" +#include "common.hpp" #include "../table/sprites.h" @@ -321,6 +322,24 @@ void Blitter_32bppAnim::SetPixel(void *video, int x, int y, uint8 colour) this->anim_buf[this->ScreenToAnimOffset((uint32 *)video) + x + y * this->anim_buf_pitch] = colour | (DEFAULT_BRIGHTNESS << 8); } +void Blitter_32bppAnim::DrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, uint8 colour, int width, int dash) +{ + const Colour c = LookupColourInPalette(colour); + + if (_screen_disable_anim) { + this->DrawLineGeneric(x, y, x2, y2, screen_width, screen_height, width, dash, [&](int x, int y) { + *((Colour *)video + x + y * _screen.pitch) = c; + }); + } else { + uint16 * const offset_anim_buf = this->anim_buf + this->ScreenToAnimOffset((uint32 *)video); + const uint16 anim_colour = colour | (DEFAULT_BRIGHTNESS << 8); + this->DrawLineGeneric(x, y, x2, y2, screen_width, screen_height, width, dash, [&](int x, int y) { + *((Colour *)video + x + y * _screen.pitch) = c; + offset_anim_buf[x + y * this->anim_buf_pitch] = anim_colour; + }); + } +} + void Blitter_32bppAnim::DrawRect(void *video, int width, int height, uint8 colour) { if (_screen_disable_anim) { diff --git a/src/blitter/32bpp_anim.hpp b/src/blitter/32bpp_anim.hpp index da33ec95b..ecf6dcfca 100644 --- a/src/blitter/32bpp_anim.hpp +++ b/src/blitter/32bpp_anim.hpp @@ -40,6 +40,7 @@ public: /* virtual */ void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom); /* virtual */ void DrawColourMappingRect(void *dst, int width, int height, PaletteID pal); /* virtual */ void SetPixel(void *video, int x, int y, uint8 colour); + /* virtual */ void DrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, uint8 colour, int width, int dash); /* virtual */ void DrawRect(void *video, int width, int height, uint8 colour); /* virtual */ void CopyFromBuffer(void *video, const void *src, int width, int height); /* virtual */ void CopyToBuffer(const void *video, void *dst, int width, int height); diff --git a/src/blitter/32bpp_base.cpp b/src/blitter/32bpp_base.cpp index c396e4541..b2e66b0be 100644 --- a/src/blitter/32bpp_base.cpp +++ b/src/blitter/32bpp_base.cpp @@ -11,6 +11,7 @@ #include "../stdafx.h" #include "32bpp_base.hpp" +#include "common.hpp" #include "../safeguards.h" @@ -24,6 +25,14 @@ void Blitter_32bppBase::SetPixel(void *video, int x, int y, uint8 colour) *((Colour *)video + x + y * _screen.pitch) = LookupColourInPalette(colour); } +void Blitter_32bppBase::DrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, uint8 colour, int width, int dash) +{ + const Colour c = LookupColourInPalette(colour); + this->DrawLineGeneric(x, y, x2, y2, screen_width, screen_height, width, dash, [=](int x, int y) { + *((Colour *)video + x + y * _screen.pitch) = c; + }); +} + void Blitter_32bppBase::DrawRect(void *video, int width, int height, uint8 colour) { Colour colour32 = LookupColourInPalette(colour); diff --git a/src/blitter/32bpp_base.hpp b/src/blitter/32bpp_base.hpp index 9b7627170..697593da6 100644 --- a/src/blitter/32bpp_base.hpp +++ b/src/blitter/32bpp_base.hpp @@ -23,6 +23,7 @@ public: /* virtual */ uint8 GetScreenDepth() { return 32; } /* virtual */ void *MoveTo(void *video, int x, int y); /* virtual */ void SetPixel(void *video, int x, int y, uint8 colour); + /* virtual */ void DrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, uint8 colour, int width, int dash); /* virtual */ void DrawRect(void *video, int width, int height, uint8 colour); /* virtual */ void CopyFromBuffer(void *video, const void *src, int width, int height); /* virtual */ void CopyToBuffer(const void *video, void *dst, int width, int height); diff --git a/src/blitter/8bpp_base.cpp b/src/blitter/8bpp_base.cpp index eab6eaa0d..dccfda3d7 100644 --- a/src/blitter/8bpp_base.cpp +++ b/src/blitter/8bpp_base.cpp @@ -12,6 +12,7 @@ #include "../stdafx.h" #include "../gfx_func.h" #include "8bpp_base.hpp" +#include "common.hpp" #include "../safeguards.h" @@ -35,6 +36,13 @@ void Blitter_8bppBase::SetPixel(void *video, int x, int y, uint8 colour) *((uint8 *)video + x + y * _screen.pitch) = colour; } +void Blitter_8bppBase::DrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, uint8 colour, int width, int dash) +{ + this->DrawLineGeneric(x, y, x2, y2, screen_width, screen_height, width, dash, [=](int x, int y) { + *((uint8 *)video + x + y * _screen.pitch) = colour; + }); +} + void Blitter_8bppBase::DrawRect(void *video, int width, int height, uint8 colour) { do { diff --git a/src/blitter/8bpp_base.hpp b/src/blitter/8bpp_base.hpp index 2dff78499..8f75dda5d 100644 --- a/src/blitter/8bpp_base.hpp +++ b/src/blitter/8bpp_base.hpp @@ -21,6 +21,7 @@ public: /* virtual */ void DrawColourMappingRect(void *dst, int width, int height, PaletteID pal); /* virtual */ void *MoveTo(void *video, int x, int y); /* virtual */ void SetPixel(void *video, int x, int y, uint8 colour); + /* virtual */ void DrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, uint8 colour, int width, int dash); /* virtual */ void DrawRect(void *video, int width, int height, uint8 colour); /* virtual */ void CopyFromBuffer(void *video, const void *src, int width, int height); /* virtual */ void CopyToBuffer(const void *video, void *dst, int width, int height); diff --git a/src/blitter/base.cpp b/src/blitter/base.cpp deleted file mode 100644 index e83df2e71..000000000 --- a/src/blitter/base.cpp +++ /dev/null @@ -1,138 +0,0 @@ -/* $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 . - */ - -/** @file base.cpp Implementation of the base for all blitters. */ - -#include "../stdafx.h" -#include "base.hpp" -#include "../core/math_func.hpp" - -#include "../safeguards.h" - -void Blitter::DrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, uint8 colour, int width, int dash) -{ - int dy; - int dx; - int stepx; - int stepy; - - dy = (y2 - y) * 2; - if (dy < 0) { - dy = -dy; - stepy = -1; - } else { - stepy = 1; - } - - dx = (x2 - x) * 2; - if (dx < 0) { - dx = -dx; - stepx = -1; - } else { - stepx = 1; - } - - if (dx == 0 && dy == 0) { - /* The algorithm below cannot handle this special case; make it work at least for line width 1 */ - if (x >= 0 && x < screen_width && y >= 0 && y < screen_height) this->SetPixel(video, x, y, colour); - return; - } - - int frac_diff = width * max(dx, dy); - if (width > 1) { - /* compute frac_diff = width * sqrt(dx*dx + dy*dy) - * Start interval: - * max(dx, dy) <= sqrt(dx*dx + dy*dy) <= sqrt(2) * max(dx, dy) <= 3/2 * max(dx, dy) */ - int frac_sq = width * width * (dx * dx + dy * dy); - int frac_max = 3 * frac_diff / 2; - while (frac_diff < frac_max) { - int frac_test = (frac_diff + frac_max) / 2; - if (frac_test * frac_test < frac_sq) { - frac_diff = frac_test + 1; - } else { - frac_max = frac_test - 1; - } - } - } - - int gap = dash; - if (dash == 0) dash = 1; - int dash_count = 0; - if (dx > dy) { - int y_low = y; - int y_high = y; - int frac_low = dy - frac_diff / 2; - int frac_high = dy + frac_diff / 2; - - while (frac_low + dx / 2 < 0) { - frac_low += dx; - y_low -= stepy; - } - while (frac_high - dx / 2 >= 0) { - frac_high -= dx; - y_high += stepy; - } - x2 += stepx; - - while (x != x2) { - if (dash_count < dash && x >= 0 && x < screen_width) { - for (int y = y_low; y != y_high; y += stepy) { - if (y >= 0 && y < screen_height) this->SetPixel(video, x, y, colour); - } - } - if (frac_low >= 0) { - y_low += stepy; - frac_low -= dx; - } - if (frac_high >= 0) { - y_high += stepy; - frac_high -= dx; - } - x += stepx; - frac_low += dy; - frac_high += dy; - if (++dash_count >= dash + gap) dash_count = 0; - } - } else { - int x_low = x; - int x_high = x; - int frac_low = dx - frac_diff / 2; - int frac_high = dx + frac_diff / 2; - - while (frac_low + dy / 2 < 0) { - frac_low += dy; - x_low -= stepx; - } - while (frac_high - dy / 2 >= 0) { - frac_high -= dy; - x_high += stepx; - } - y2 += stepy; - - while (y != y2) { - if (dash_count < dash && y >= 0 && y < screen_height) { - for (int x = x_low; x != x_high; x += stepx) { - if (x >= 0 && x < screen_width) this->SetPixel(video, x, y, colour); - } - } - if (frac_low >= 0) { - x_low += stepx; - frac_low -= dy; - } - if (frac_high >= 0) { - x_high += stepx; - frac_high -= dy; - } - y += stepy; - frac_low += dx; - frac_high += dx; - if (++dash_count >= dash + gap) dash_count = 0; - } - } -} diff --git a/src/blitter/base.hpp b/src/blitter/base.hpp index a9403b339..388359441 100644 --- a/src/blitter/base.hpp +++ b/src/blitter/base.hpp @@ -122,7 +122,7 @@ public: * @param width Line width. * @param dash Length of dashes for dashed lines. 0 means solid line. */ - virtual void DrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, uint8 colour, int width, int dash = 0); + virtual void DrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, uint8 colour, int width, int dash = 0) = 0; /** * Copy from a buffer to the screen. @@ -203,6 +203,8 @@ public: virtual void PostResize() { }; virtual ~Blitter() { } + + template void DrawLineGeneric(int x, int y, int x2, int y2, int screen_width, int screen_height, int width, int dash, SetPixelT set_pixel); }; #endif /* BLITTER_BASE_HPP */ diff --git a/src/blitter/common.hpp b/src/blitter/common.hpp new file mode 100644 index 000000000..0e255ca9a --- /dev/null +++ b/src/blitter/common.hpp @@ -0,0 +1,141 @@ +/* $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 . + */ + +/** @file common.hpp Common functionality for all blitter implementations. */ + +#ifndef BLITTER_COMMON_HPP +#define BLITTER_COMMON_HPP + +#include "base.hpp" +#include "../core/math_func.hpp" + +template +void Blitter::DrawLineGeneric(int x, int y, int x2, int y2, int screen_width, int screen_height, int width, int dash, SetPixelT set_pixel) +{ + int dy; + int dx; + int stepx; + int stepy; + + dy = (y2 - y) * 2; + if (dy < 0) { + dy = -dy; + stepy = -1; + } else { + stepy = 1; + } + + dx = (x2 - x) * 2; + if (dx < 0) { + dx = -dx; + stepx = -1; + } else { + stepx = 1; + } + + if (dx == 0 && dy == 0) { + /* The algorithm below cannot handle this special case; make it work at least for line width 1 */ + if (x >= 0 && x < screen_width && y >= 0 && y < screen_height) set_pixel(x, y); + return; + } + + int frac_diff = width * max(dx, dy); + if (width > 1) { + /* compute frac_diff = width * sqrt(dx*dx + dy*dy) + * Start interval: + * max(dx, dy) <= sqrt(dx*dx + dy*dy) <= sqrt(2) * max(dx, dy) <= 3/2 * max(dx, dy) */ + int frac_sq = width * width * (dx * dx + dy * dy); + int frac_max = 3 * frac_diff / 2; + while (frac_diff < frac_max) { + int frac_test = (frac_diff + frac_max) / 2; + if (frac_test * frac_test < frac_sq) { + frac_diff = frac_test + 1; + } else { + frac_max = frac_test - 1; + } + } + } + + int gap = dash; + if (dash == 0) dash = 1; + int dash_count = 0; + if (dx > dy) { + int y_low = y; + int y_high = y; + int frac_low = dy - frac_diff / 2; + int frac_high = dy + frac_diff / 2; + + while (frac_low + dx / 2 < 0) { + frac_low += dx; + y_low -= stepy; + } + while (frac_high - dx / 2 >= 0) { + frac_high -= dx; + y_high += stepy; + } + x2 += stepx; + + while (x != x2) { + if (dash_count < dash && x >= 0 && x < screen_width) { + for (int y = y_low; y != y_high; y += stepy) { + if (y >= 0 && y < screen_height) set_pixel(x, y); + } + } + if (frac_low >= 0) { + y_low += stepy; + frac_low -= dx; + } + if (frac_high >= 0) { + y_high += stepy; + frac_high -= dx; + } + x += stepx; + frac_low += dy; + frac_high += dy; + if (++dash_count >= dash + gap) dash_count = 0; + } + } else { + int x_low = x; + int x_high = x; + int frac_low = dx - frac_diff / 2; + int frac_high = dx + frac_diff / 2; + + while (frac_low + dy / 2 < 0) { + frac_low += dy; + x_low -= stepx; + } + while (frac_high - dy / 2 >= 0) { + frac_high -= dy; + x_high += stepx; + } + y2 += stepy; + + while (y != y2) { + if (dash_count < dash && y >= 0 && y < screen_height) { + for (int x = x_low; x != x_high; x += stepx) { + if (x >= 0 && x < screen_width) set_pixel(x, y); + } + } + if (frac_low >= 0) { + x_low += stepx; + frac_low -= dy; + } + if (frac_high >= 0) { + x_high += stepx; + frac_high -= dy; + } + y += stepy; + frac_low += dx; + frac_high += dx; + if (++dash_count >= dash + gap) dash_count = 0; + } + } +} + +#endif /* BLITTER_COMMON_HPP */ -- cgit v1.2.3-54-g00ecf