25 #include <SDL2/SDL_rect.h> 26 #include <SDL2/SDL_timer.h> 33 #define ERR_DM LOG_STREAM(err, log_draw_man) 34 #define WRN_DM LOG_STREAM(warn, log_draw_man) 35 #define LOG_DM LOG_STREAM(info, log_draw_man) 36 #define DBG_DM LOG_STREAM(debug, log_draw_man) 45 std::vector<top_level_drawable*> top_level_drawables_;
46 std::vector<rect> invalidated_regions_;
47 bool drawing_ =
false;
48 bool tlds_need_tidying_ =
false;
49 uint32_t last_sparkle_ = 0;
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 <<
"superceding 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);
128 ERR_DM <<
"Draw recursion detected";
133 if (tlds_need_tidying_) {
135 tlds_need_tidying_ =
false;
147 invalidated_regions_.clear();
162 last_sparkle_ = SDL_GetTicks();
173 int vsync_delay = (1000 / rr) - 1;
181 if (time_to_wait > 0) {
183 SDL_Delay(std::min(time_to_wait, 1000));
189 for (
size_t i = 0;
i < top_level_drawables_.size(); ++
i) {
191 if (tld) { tld->
update(); }
197 for (
size_t i = 0;
i < top_level_drawables_.size(); ++
i) {
199 if (tld) { tld->
layout(); }
205 for (
size_t i = 0;
i < top_level_drawables_.size(); ++
i) {
207 if (tld) { tld->
render(); }
218 while (!invalidated_regions_.empty()) {
219 rect r = invalidated_regions_.back();
220 invalidated_regions_.pop_back();
222 for (
auto& other : invalidated_regions_) {
224 if (other.contains(r)) {
225 DBG_DM <<
"skipping redundant draw " << r;
230 if (m.
area() <= r.
area() + other.area()) {
231 DBG_DM <<
"merging inefficient draws " << r;
237 DBG_DM <<
"drawing " << r;
240 for (
auto tld : top_level_drawables_) {
241 if (!tld) {
continue; }
242 rect i = r.intersect(tld->screen_location());
248 DBG_DM <<
" to " <<
static_cast<void*
>(tld);
251 drawn |= tld->expose(i);
253 WRN_DM <<
"exception thrown during expose " 254 <<
static_cast<void*
>(tld);
268 DBG_DM <<
"registering TLD " <<
static_cast<void*
>(tld);
269 auto& vec = top_level_drawables_;
270 if (std::find(vec.begin(), vec.end(), tld) != vec.end()) {
273 top_level_drawables_.push_back(tld);
279 DBG_DM <<
"deregistering TLD " <<
static_cast<void*
>(tld);
280 auto& vec = top_level_drawables_;
281 auto it = std::find(vec.begin(), vec.end(), tld);
283 if (it == vec.end()) {
284 WRN_DM <<
"attempted to deregister nonexistant TLD " 285 <<
static_cast<void*
>(tld);
291 tlds_need_tidying_ =
true;
296 DBG_DM <<
"raising TLD " <<
static_cast<void*
>(tld);
297 auto& vec = top_level_drawables_;
298 auto it = std::find(vec.begin(), vec.end(), tld);
300 if (it == vec.end()) {
301 ERR_DM <<
"attempted to raise nonexistant TLD " 302 <<
static_cast<void*
>(tld);
306 for ( ; it != vec.end(); it = std::find(it, vec.end(), tld)) {
311 tlds_need_tidying_ =
true;
317 DBG_DM <<
"tidying " << top_level_drawables_.size() <<
" drawables";
318 auto& vec = top_level_drawables_;
319 vec.erase(
std::remove(vec.begin(), vec.end(),
nullptr), vec.end());
320 DBG_DM << top_level_drawables_.size() <<
" after tidying";
Drawing functions, for drawing things on the screen.
void remove()
Removes a tip.
int current_refresh_rate()
The refresh rate of the screen.
bool testing()
The game is running unit tests.
void deregister_drawable(top_level_drawable *tld)
Remove a top-level drawable from the drawing stack.
bool contains(int x, int y) const
Whether the given point lies within the rectangle.
static lg::log_domain log_draw_man("draw/manager")
void invalidate_all()
Mark the entire screen as requiring redraw.
clip_setter override_clip(const SDL_Rect &clip)
Override the clipping area.
rect game_canvas()
The game canvas area, in drawing 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 headless()
The game is running headless.
void sparkle()
Ensure that everything which needs to be drawn is drawn.
int get_frame_length()
Returns the length of one display frame, in milliseconds.
A top-level drawable item (TLD), such as a window.
static void wait_for_vsync()
bool empty() const
False if both w and h are > 0, true otherwise.
virtual void update()
Update state and any parameters that may effect layout, or any of the later stages.
A global draw management interface.
static void tidy_drawables()
An abstract description of a rectangle with integer coordinates.
void register_drawable(top_level_drawable *tld)
Register a top-level drawable.
Contains the SDL_Rect helper code.
Base class for all the errors encountered by the engine.
Standard logging facilities (interface).
int area() const
The area of this rectangle, in square pixels.
virtual void layout()
Finalize the size and position of the drawable and its children, and invalidate any regions requiring...
virtual void render()
Perform any internal rendering necessary to prepare the drawable.
void invalidate_region(const rect ®ion)
Mark a region of the screen as requiring redraw.
void raise_drawable(top_level_drawable *tld)
Raise a TLD to the top of the drawing stack.