Viewport: Use column-aligned block data type for dirty block tracking
Use bit operations to reduce looping over individual bits where possible
This commit is contained in:
@@ -3514,7 +3514,7 @@ DEF_CONSOLE_CMD(ConViewportMarkDirty)
|
||||
uint b = std::min<uint>(t + ((argc > 4) ? strtoul(argv[4], nullptr, 0) : 1), vp->dirty_blocks_per_column);
|
||||
for (uint x = l; x < r; x++) {
|
||||
for (uint y = t; y < b; y++) {
|
||||
vp->dirty_blocks[(x * vp->dirty_blocks_per_column) + y] = true;
|
||||
SetBit(vp->dirty_blocks[(x * vp->dirty_blocks_column_pitch) + (y / VP_BLOCK_BITS)], y % VP_BLOCK_BITS);
|
||||
}
|
||||
}
|
||||
vp->is_dirty = true;
|
||||
|
111
src/gfx.cpp
111
src/gfx.cpp
@@ -1667,52 +1667,93 @@ void DrawDirtyBlocks()
|
||||
}
|
||||
|
||||
const uint grid_w = vp->dirty_blocks_per_row;
|
||||
const uint grid_h = vp->dirty_blocks_per_column;
|
||||
|
||||
uint pos = 0;
|
||||
uint x = 0;
|
||||
do {
|
||||
uint y = 0;
|
||||
do {
|
||||
if (vp->dirty_blocks[pos]) {
|
||||
uint left = x;
|
||||
uint top = y;
|
||||
uint column_pos = 0;
|
||||
for (uint x = 0; x < grid_w; x++, column_pos += vp->dirty_blocks_column_pitch) {
|
||||
for (uint offset = 0; offset < vp->dirty_blocks_column_pitch; offset++) {
|
||||
const uint start = column_pos + offset;
|
||||
while (vp->dirty_blocks[start] != 0) {
|
||||
const ViewPortBlockT first_block = vp->dirty_blocks[start];
|
||||
const uint first_block_start = FindFirstBit(first_block);
|
||||
const uint left = x;
|
||||
uint right = x + 1;
|
||||
uint bottom = y;
|
||||
uint p = pos;
|
||||
const uint top = (offset * VP_BLOCK_BITS) + first_block_start;
|
||||
|
||||
/* First try coalescing downwards */
|
||||
do {
|
||||
vp->dirty_blocks[p] = false;
|
||||
p++;
|
||||
bottom++;
|
||||
} while (bottom != grid_h && vp->dirty_blocks[p]);
|
||||
|
||||
/* First block */
|
||||
const uint first_block_count = std::countr_one(first_block >> first_block_start);
|
||||
vp->dirty_blocks[start] &= ~GetBitMaskSC<ViewPortBlockT>(0, first_block_start + first_block_count);
|
||||
|
||||
const ViewPortBlockT first_block_bits = first_block ^ vp->dirty_blocks[start];
|
||||
|
||||
uint bottom = top + first_block_count;
|
||||
uint non_first_complete_blocks = 0;
|
||||
ViewPortBlockT last_partial_block_bits = 0;
|
||||
|
||||
if (bottom % VP_BLOCK_BITS == 0) {
|
||||
/* First block ended at the block boundary, continue at the next block */
|
||||
for (uint block_y = offset + 1; block_y < vp->dirty_blocks_column_pitch; block_y++) {
|
||||
ViewPortBlockT ¤t_block = vp->dirty_blocks[column_pos + block_y];
|
||||
if (current_block == 0) break;
|
||||
const uint count = std::countr_one(current_block);
|
||||
bottom += count;
|
||||
if (count == VP_BLOCK_BITS) {
|
||||
/* Complete block */
|
||||
current_block = 0;
|
||||
non_first_complete_blocks++;
|
||||
} else {
|
||||
/* Partial block, stop here */
|
||||
last_partial_block_bits = GetBitMaskSC<ViewPortBlockT>(0, count);
|
||||
current_block &= ~last_partial_block_bits;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Try coalescing to the right too. */
|
||||
uint block_h = (bottom - y);
|
||||
p = pos;
|
||||
|
||||
uint next_col_pos = start;
|
||||
while (right != grid_w) {
|
||||
uint p2 = (p += grid_h);
|
||||
uint check_h = block_h;
|
||||
/* Check if a full line of dirty flags is set. */
|
||||
do {
|
||||
if (!vp->dirty_blocks[p2]) goto no_more_coalesc;
|
||||
p2++;
|
||||
} while (--check_h != 0);
|
||||
next_col_pos += vp->dirty_blocks_column_pitch;
|
||||
uint pos = next_col_pos;
|
||||
|
||||
if ((vp->dirty_blocks[pos] & first_block_bits) != first_block_bits) {
|
||||
/* First block doesn't match, give up */
|
||||
break;
|
||||
}
|
||||
pos++;
|
||||
if (non_first_complete_blocks > 0) {
|
||||
auto iter = vp->dirty_blocks.begin() + pos;
|
||||
auto not_all_bits_set = [](const ViewPortBlockT &val) {
|
||||
return (~val) != 0;
|
||||
};
|
||||
if (std::any_of(iter, iter + non_first_complete_blocks, not_all_bits_set)) {
|
||||
/* Complete blocks don't match, give up */
|
||||
break;
|
||||
}
|
||||
pos += non_first_complete_blocks;
|
||||
}
|
||||
if (last_partial_block_bits != 0 && (vp->dirty_blocks[pos] & last_partial_block_bits) != last_partial_block_bits) {
|
||||
/* Last block doesn't match, give up */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Wohoo, can combine it one step to the right!
|
||||
* Do that, and clear the bits. */
|
||||
right++;
|
||||
|
||||
check_h = block_h;
|
||||
p2 = p;
|
||||
do {
|
||||
vp->dirty_blocks[p2] = false;
|
||||
p2++;
|
||||
} while (--check_h != 0);
|
||||
pos = next_col_pos;
|
||||
vp->dirty_blocks[pos] &= ~first_block_bits;
|
||||
pos++;
|
||||
if (non_first_complete_blocks > 0) {
|
||||
auto iter = vp->dirty_blocks.begin() + pos;
|
||||
std::fill(iter, iter + non_first_complete_blocks, 0);
|
||||
pos += non_first_complete_blocks;
|
||||
}
|
||||
if (last_partial_block_bits != 0) {
|
||||
vp->dirty_blocks[pos] &= ~last_partial_block_bits;
|
||||
}
|
||||
}
|
||||
no_more_coalesc:
|
||||
|
||||
assert(_cur_dpi == &bk);
|
||||
int draw_left = std::max<int>(0, ((left == 0) ? 0 : vp->dirty_block_left_margin + (left << vp->GetDirtyBlockWidthShift())) + vp->left);
|
||||
@@ -1723,8 +1764,8 @@ void DrawDirtyBlocks()
|
||||
DrawDirtyViewport(0, draw_left, draw_top, draw_right, draw_bottom);
|
||||
}
|
||||
}
|
||||
} while (pos++, ++y != grid_h);
|
||||
} while (++x != grid_w);
|
||||
}
|
||||
}
|
||||
|
||||
_transparency_opt = to_backup;
|
||||
w->viewport->ClearDirty();
|
||||
|
@@ -4441,8 +4441,8 @@ void UpdateViewportSizeZoom(Viewport *vp)
|
||||
{
|
||||
vp->dirty_blocks_per_column = CeilDiv(vp->height, vp->GetDirtyBlockHeight());
|
||||
vp->dirty_blocks_per_row = CeilDiv(vp->width, vp->GetDirtyBlockWidth());
|
||||
uint size = vp->dirty_blocks_per_row * vp->dirty_blocks_per_column;
|
||||
vp->dirty_blocks.assign(size, false);
|
||||
vp->dirty_blocks_column_pitch = CeilDivT(vp->dirty_blocks_per_column, VP_BLOCK_BITS);
|
||||
vp->dirty_blocks.assign(vp->dirty_blocks_column_pitch * vp->dirty_blocks_per_row, 0);
|
||||
UpdateViewportDirtyBlockLeftMargin(vp);
|
||||
if (vp->zoom >= ZOOM_LVL_DRAW_MAP) {
|
||||
memset(vp->map_draw_vehicles_cache.done_hash_bits, 0, sizeof(vp->map_draw_vehicles_cache.done_hash_bits));
|
||||
@@ -4539,14 +4539,39 @@ void MarkViewportDirty(Viewport * const vp, int left, int top, int right, int bo
|
||||
uint w = (std::max<int>(0, UnScaleByZoom(right, vp->zoom) - 1 - vp->dirty_block_left_margin) >> vp->GetDirtyBlockWidthShift()) + 1 - x;
|
||||
uint h = ((UnScaleByZoom(bottom, vp->zoom) - 1) >> vp->GetDirtyBlockHeightShift()) + 1 - y;
|
||||
|
||||
uint column_skip = vp->dirty_blocks_per_column - h;
|
||||
uint pos = (x * vp->dirty_blocks_per_column) + y;
|
||||
for (uint i = 0; i < w; i++) {
|
||||
for (uint j = 0; j < h; j++) {
|
||||
vp->dirty_blocks[pos] = true;
|
||||
pos++;
|
||||
if (w == 0 || h == 0) return;
|
||||
|
||||
uint col_start = (x * vp->dirty_blocks_column_pitch) + (y / VP_BLOCK_BITS);
|
||||
uint y_end = y + h;
|
||||
if ((y_end - 1) / VP_BLOCK_BITS == y / VP_BLOCK_BITS) {
|
||||
/* Only dirtying a single block row */
|
||||
const ViewPortBlockT mask = GetBitMaskSC<ViewPortBlockT>(y % VP_BLOCK_BITS, h);
|
||||
for (uint i = 0; i < w; i++, col_start += vp->dirty_blocks_column_pitch) {
|
||||
vp->dirty_blocks[col_start] |= mask;
|
||||
}
|
||||
} else {
|
||||
/* Dirtying multiple block rows */
|
||||
const uint h_non_first = y_end - Align(y + 1, VP_BLOCK_BITS); // Height, excluding the first block
|
||||
for (uint i = 0; i < w; i++, col_start += vp->dirty_blocks_column_pitch) {
|
||||
uint pos = col_start;
|
||||
|
||||
/* Set only high bits for first block in column */
|
||||
vp->dirty_blocks[pos] |= (~static_cast<ViewPortBlockT>(0)) << (y % VP_BLOCK_BITS);
|
||||
|
||||
uint left = h_non_first;
|
||||
while (left > 0) {
|
||||
pos++;
|
||||
if (left < VP_BLOCK_BITS) {
|
||||
/* Set only low bits for last block in column */
|
||||
vp->dirty_blocks[pos] |= GetBitMaskSC<ViewPortBlockT>(0, left);
|
||||
break;
|
||||
} else {
|
||||
/* Set all bits for middle blocks in column */
|
||||
vp->dirty_blocks[pos] = ~static_cast<ViewPortBlockT>(0);
|
||||
}
|
||||
left -= VP_BLOCK_BITS;
|
||||
}
|
||||
}
|
||||
pos += column_skip;
|
||||
}
|
||||
vp->is_dirty = true;
|
||||
|
||||
|
@@ -58,7 +58,8 @@ struct Viewport {
|
||||
|
||||
LinkGraphOverlay *overlay;
|
||||
|
||||
std::vector<bool> dirty_blocks;
|
||||
std::vector<ViewPortBlockT> dirty_blocks;
|
||||
uint dirty_blocks_column_pitch;
|
||||
uint dirty_blocks_per_column;
|
||||
uint dirty_blocks_per_row;
|
||||
uint8_t dirty_block_left_margin;
|
||||
|
@@ -3803,10 +3803,10 @@ char *DumpWindowInfo(char *b, const char *last, const Window *w)
|
||||
w->window_class, w->window_number, w->flags, w->left, w->top, w->width, w->height, w->owner);
|
||||
if (w->viewport != nullptr) {
|
||||
const ViewportData *vd = w->viewport;
|
||||
b += seprintf(b, last, ", viewport: (veh: 0x%X, x: (%d, %d), y: (%d, %d), z: %u, l: %d, t: %d, w: %d, h: %d, vl: %d, vt: %d, vw: %d, vh: %d, dbc: %u, dbr: %u, dblm: %u, db: %u)",
|
||||
b += seprintf(b, last, ", viewport: (veh: 0x%X, x: (%d, %d), y: (%d, %d), z: %u, l: %d, t: %d, w: %d, h: %d, vl: %d, vt: %d, vw: %d, vh: %d, dbc: %u, dbr: %u, dblm: %u, dbcp: %u, db: %u)",
|
||||
vd->follow_vehicle, vd->scrollpos_x, vd->dest_scrollpos_x, vd->scrollpos_y, vd->dest_scrollpos_y, vd->zoom, vd->left, vd->top, vd->width, vd->height,
|
||||
vd->virtual_left, vd->virtual_top, vd->virtual_width, vd->virtual_height, vd->dirty_blocks_per_column, vd->dirty_blocks_per_row, vd->dirty_block_left_margin,
|
||||
(uint) vd->dirty_blocks.size());
|
||||
vd->dirty_blocks_column_pitch, (uint) vd->dirty_blocks.size());
|
||||
}
|
||||
if (w->parent != nullptr) {
|
||||
b += seprintf(b, last, ", parent ");
|
||||
|
Reference in New Issue
Block a user