30 #define ERR_DM LOG_STREAM(err, log_draw_man)
31 #define WRN_DM LOG_STREAM(warn, log_draw_man)
32 #define LOG_DM LOG_STREAM(info, log_draw_man)
33 #define DBG_DM LOG_STREAM(debug, log_draw_man)
36 using std::chrono::steady_clock;
37 using namespace std::chrono_literals;
44 std::vector<top_level_drawable*> top_level_drawables_;
45 std::vector<rect> invalidated_regions_;
46 bool drawing_ =
false;
47 bool tlds_need_tidying_ =
false;
48 steady_clock::time_point last_sparkle_;
49 bool extra_pass_requested_ =
false;
64 ERR_DM <<
"Attempted to invalidate region " << region
70 rect progressive_cover = region;
71 int64_t cumulative_area = 0;
72 for (
auto& r : invalidated_regions_) {
73 if (r.contains(region)) {
83 DBG_DM <<
"superseding previous invalidation " << r
84 <<
" with " << region;
91 if (m.
area() <= r.area() + region.
area()) {
94 DBG_DM <<
"merging " << region <<
" with " << r
95 <<
" to invalidate " << m;
102 cumulative_area += r.
area();
103 if (progressive_cover.
area() <= cumulative_area) {
104 DBG_DM <<
"conglomerating invalidations to "
105 << progressive_cover;
108 invalidated_regions_[0] = progressive_cover;
114 DBG_DM <<
"invalidating region " << region;
116 invalidated_regions_.push_back(region);
127 extra_pass_requested_ =
true;
133 ERR_DM <<
"Draw recursion detected";
138 if (tlds_need_tidying_) {
140 tlds_need_tidying_ =
false;
152 invalidated_regions_.clear();
163 while (extra_pass_requested_) {
164 extra_pass_requested_ =
false;
169 if (drew_something) {
176 last_sparkle_ = steady_clock::now();
187 auto vsync_delay = (1000ms / rr) - 1ms;
188 return std::min(vsync_delay, 1000ms);
193 auto now = steady_clock::now();
195 if (now < next_frame) {
197 std::this_thread::sleep_for(std::min<steady_clock::duration>(next_frame - now, 1
s));
203 for (
size_t i = 0;
i < top_level_drawables_.size(); ++
i) {
205 if (tld) { tld->
update(); }
211 for (
size_t i = 0;
i < top_level_drawables_.size(); ++
i) {
213 if (tld) { tld->
layout(); }
219 for (
size_t i = 0;
i < top_level_drawables_.size(); ++
i) {
221 if (tld) { tld->
render(); }
232 while (!invalidated_regions_.empty()) {
233 rect r = invalidated_regions_.back();
234 invalidated_regions_.pop_back();
236 for (
auto& other : invalidated_regions_) {
238 if (other.contains(r)) {
239 DBG_DM <<
"skipping redundant draw " << r;
244 if (m.
area() <= r.
area() + other.area()) {
245 DBG_DM <<
"merging inefficient draws " << r;
251 DBG_DM <<
"drawing " << r;
254 for (
auto tld : top_level_drawables_) {
255 if (!tld) {
continue; }
262 DBG_DM <<
" to " <<
static_cast<void*
>(tld);
265 drawn |= tld->expose(
i);
268 <<
" thrown during expose " <<
static_cast<void*
>(tld);
282 DBG_DM <<
"registering TLD " <<
static_cast<void*
>(tld);
283 auto& vec = top_level_drawables_;
284 if (
std::find(vec.begin(), vec.end(), tld) != vec.end()) {
287 top_level_drawables_.push_back(tld);
293 DBG_DM <<
"deregistering TLD " <<
static_cast<void*
>(tld);
294 auto& vec = top_level_drawables_;
295 auto it =
std::find(vec.begin(), vec.end(), tld);
297 if (it == vec.end()) {
298 WRN_DM <<
"attempted to deregister nonexistent TLD "
299 <<
static_cast<void*
>(tld);
305 tlds_need_tidying_ =
true;
310 DBG_DM <<
"raising TLD " <<
static_cast<void*
>(tld);
311 auto& vec = top_level_drawables_;
312 auto it =
std::find(vec.begin(), vec.end(), tld);
314 if (it == vec.end()) {
315 ERR_DM <<
"attempted to raise nonexistent TLD "
316 <<
static_cast<void*
>(tld);
320 for ( ; it != vec.end(); it =
std::find(it, vec.end(), tld)) {
325 tlds_need_tidying_ =
true;
331 DBG_DM <<
"tidying " << top_level_drawables_.size() <<
" drawables";
333 DBG_DM << top_level_drawables_.size() <<
" after tidying";
A top-level drawable item (TLD), such as a window.
virtual void render()
Perform any internal rendering necessary to prepare the drawable.
virtual void layout()
Finalize the size and position of the drawable and its children, and invalidate any regions requiring...
virtual void update()
Update state and any parameters that may effect layout, or any of the later stages.
Drawing functions, for drawing things on the screen.
static lg::log_domain log_draw_man("draw/manager")
Standard logging facilities (interface).
A global draw management interface.
void invalidate_region(const rect ®ion)
Mark a region of the screen as requiring redraw.
void request_extra_render_pass()
Request an extra render pass.
static void tidy_drawables()
void register_drawable(top_level_drawable *tld)
Register a top-level drawable.
void deregister_drawable(top_level_drawable *tld)
Remove a top-level drawable from the drawing stack.
void invalidate_all()
Mark the entire screen as requiring redraw.
void sparkle()
Ensure that everything which needs to be drawn is drawn.
std::chrono::milliseconds get_frame_length()
Returns the length of one display frame, in milliseconds.
void raise_drawable(top_level_drawable *tld)
Raise a TLD to the top of the drawing stack.
static void wait_for_vsync()
clip_setter override_clip(const SDL_Rect &clip)
Override the clipping area.
std::size_t erase(Container &container, const Value &value)
Convenience wrapper for using std::remove on a container.
std::string get_unknown_exception_type()
Utility function for finding the type of thing caught with catch(...).
auto * find(Container &container, const Value &value)
Convenience wrapper for using find on a container without needing to comare to end()
bool headless()
The game is running headless.
bool testing()
The game is running unit tests.
rect game_canvas()
The game canvas area, in drawing coordinates.
int current_refresh_rate()
The refresh rate of the screen.
Contains the SDL_Rect helper code.
Base class for all the errors encountered by the engine.
An abstract description of a rectangle with integer coordinates.
rect & expand_to_cover(const SDL_Rect &r)
Minimally expand this rect to fully contain another.
rect minimal_cover(const SDL_Rect &r) const
Calculates the minimal rectangle that completely contains both this rectangle and the given rectangle...
bool contains(int x, int y) const
Whether the given point lies within the rectangle.
constexpr int area() const
The area of this rectangle, in square pixels.
rect intersect(const SDL_Rect &r) const
Calculates the intersection of this rectangle and another; that is, the maximal rectangle that is con...
static map_location::direction s