[Add] Occlusion culling.

Prevents rendering of UI windows that are completely hidden by other
windows.

Desktop rendering now checks if a window is fully occluded by any single
window higher in the Z-order. If a window is determined to be hidden,
its entire render function can be skipped.

This is a pragmatic implementation that handles the most common
occlusion scenario (i.e., a maximised window). There would be little
benefit of other edge cases in the particular game I think.
This commit is contained in:
Ritchie Cunningham 2025-10-04 22:27:38 +01:00
parent b1392c2e6b
commit d992cb54bf
5 changed files with 36 additions and 3 deletions

View File

@ -125,6 +125,8 @@ void Terminal::render(const RenderContext& context, int x, int y_screen, int y_g
/* Draw History. */
for(size_t i = _scroll_offset; i < _history.size(); ++i) {
float line_y_pos = y_screen + padding + ((i - _scroll_offset) * line_height);
/* Culling: If line is already below the view, stop. */
if(line_y_pos > y_screen + height) { break; }
context.ui_renderer->render_text(_history[i].c_str(), x+padding, line_y_pos+18, white);
}

View File

@ -214,9 +214,27 @@ void Desktop::render(const RenderContext& context) {
context.ui_renderer->flush_text();
/* Pass 2: Windows Render in order, last window is top-most. */
for(const auto& win : _windows) {
if(!win->is_minimized()) {
win.get()->render(context);
for(size_t i = 0; i < _windows.size(); ++i) {
auto& win_i = _windows[i];
if(win_i->is_minimized()) continue;
bool is_occluded = false;
Rect rect_i = win_i->get_rect();
/* Check against all windows on top of this one. */
for(size_t j = i+1; j < _windows.size(); ++j) {
auto& win_j = _windows[j];
if(win_j->is_minimized()) continue;
Rect rect_j = win_j->get_rect();
if(rect_i.x >= rect_j.x && (rect_i.x + rect_i.w) <= (rect_j.x + rect_j.w) &&
rect_i.y >= rect_j.y && (rect_i.y + rect_i.h) <= (rect_j.y + rect_j.h)) {
is_occluded = true;
break;
}
}
if(!is_occluded) {
win_i->render(context);
}
}

View File

@ -82,6 +82,14 @@ void TextView::render(UIRenderer* ui_renderer, int x, int y, int width, int heig
float current_y = y;
for(size_t i = _scroll_offset; i < _buffer->get_line_count(); ++i) {
/*
* Culling: If the top of the current line is already below the bottom of the
* view, we can stop rendering completely.
*/
if(current_y > y + height) {
break;
}
std::string line = _buffer->get_line(i);
if(show_cursor && (int)i == cursor_pos.row) {

View File

@ -43,6 +43,10 @@ const std::string& UIWindow::get_title(void) const {
return _title;
}
Rect UIWindow::get_rect(void) const {
return { _x, _y, _width, _height };
}
bool UIWindow::is_mouse_over_resize_handle(int mouse_x, int mouse_y) const {
return (mouse_x >= _x + _width - _resize_margin && mouse_x <= _x + _width &&
mouse_y >= _y + _height - _resize_margin &&

View File

@ -43,6 +43,7 @@ public:
IWindowContent* get_content(void);
bool is_mouse_over_resize_handle(int mouse_x, int mouse_y) const;
const std::string& get_title(void) const;
Rect get_rect(void) const;
private:
friend class Taskbar; /* Allow taskbar to access private members. */