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 (std::size_t
i = 0;
i < top_level_drawables_.size(); ++
i) {
205 if (tld) { tld->
update(); }
211 for (std::size_t
i = 0;
i < top_level_drawables_.size(); ++
i) {
213 if (tld) { tld->
layout(); }
219 for (std::size_t
i = 0;
i < top_level_drawables_.size(); ++
i) {
221 if (tld) { tld->
render(); }
241 while (!invalidated_regions_.empty()) {
242 rect r = invalidated_regions_.back();
243 invalidated_regions_.pop_back();
245 for (
auto& other : invalidated_regions_) {
247 if (other.contains(r)) {
248 DBG_DM <<
"skipping redundant draw " << r;
253 if (m.
area() <= r.
area() + other.area()) {
254 DBG_DM <<
"merging inefficient draws " << r;
260 DBG_DM <<
"drawing " << r;
263 for (
auto tld : top_level_drawables_) {
264 if (!tld) {
continue; }
271 DBG_DM <<
" to " <<
static_cast<void*
>(tld);
274 drawn |= tld->expose(
i);
277 <<
" thrown during expose " <<
static_cast<void*
>(tld);
291 DBG_DM <<
"registering TLD " <<
static_cast<void*
>(tld);
292 auto& vec = top_level_drawables_;
293 if (
std::find(vec.begin(), vec.end(), tld) != vec.end()) {
296 top_level_drawables_.push_back(tld);
302 DBG_DM <<
"deregistering TLD " <<
static_cast<void*
>(tld);
303 auto& vec = top_level_drawables_;
304 auto it =
std::find(vec.begin(), vec.end(), tld);
306 if (it == vec.end()) {
307 WRN_DM <<
"attempted to deregister nonexistent TLD "
308 <<
static_cast<void*
>(tld);
314 tlds_need_tidying_ =
true;
319 DBG_DM <<
"raising TLD " <<
static_cast<void*
>(tld);
320 auto& vec = top_level_drawables_;
321 auto it =
std::find(vec.begin(), vec.end(), tld);
323 if (it == vec.end()) {
324 ERR_DM <<
"attempted to raise nonexistent TLD "
325 <<
static_cast<void*
>(tld);
329 for ( ; it != vec.end(); it =
std::find(it, vec.end(), tld)) {
334 tlds_need_tidying_ =
true;
340 DBG_DM <<
"tidying " << top_level_drawables_.size() <<
" drawables";
342 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 ::rect &clip)
Override the clipping area.
viewport_setter set_viewport(const ::rect &viewport)
Set the viewport.
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.
rect output_area()
{0, 0, output_size().x, output_size().y}
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 minimal_cover(const 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 rect &r) const
Calculates the intersection of this rectangle and another; that is, the maximal rectangle that is con...
rect & expand_to_cover(const rect &r)
Minimally expand this rect to fully contain another.
static map_location::direction s