lxgui
gui_renderer.cpp
1 #include "lxgui/gui_renderer.hpp"
2 
3 #include "lxgui/gui_atlas.hpp"
4 #include "lxgui/gui_out.hpp"
5 #include "lxgui/gui_quad.hpp"
6 #include "lxgui/gui_render_target.hpp"
7 #include "lxgui/utils_string.hpp"
8 
9 namespace lxgui::gui {
10 
11 void renderer::begin(std::shared_ptr<render_target> target) {
13  current_material_ = nullptr;
14 
16  try {
17  if (quad_cache_[0].cache == nullptr) {
18  for (std::size_t index = 0u; index < batching_cache_cycle_size; ++index) {
19  quad_cache_[index].cache = create_vertex_cache(vertex_cache::type::quads);
20  }
21  }
22  } catch (const std::exception& e) {
23  gui::out << gui::warning << e.what() << std::endl;
25  << "gui::renderer: Failed to create caches for quad batching. Vertex "
26  "caches will be disabled."
27  << std::endl;
28 
29  vertex_cache_enabled_ = false;
30  }
31  }
32  }
33 
34  begin_(std::move(target));
35 }
36 
37 void renderer::end() {
40  }
41 
42  end_();
43 }
44 
46  last_frame_batch_count_ = batch_count_;
47  last_frame_vertex_count_ = vertex_count_;
48  batch_count_ = 0;
49  vertex_count_ = 0;
50 }
51 
52 std::size_t renderer::get_batch_count() const {
53  return last_frame_batch_count_;
54 }
55 
56 std::size_t renderer::get_vertex_count() const {
57  return last_frame_vertex_count_;
58 }
59 
60 void renderer::set_view(const matrix4f& view_matrix) {
63  }
64 
65  set_view_(view_matrix);
66 }
67 
68 void renderer::render_quad(const quad& q) {
69  render_quads(q.mat.get(), {q.v});
70 }
71 
72 bool renderer::uses_same_texture_(const material* mat1, const material* mat2) const {
73  if (mat1 == mat2)
74  return true;
75 
76  if (mat1 && mat2 && mat1->uses_same_texture(*mat2))
77  return true;
78 
80  if (mat1 && mat1->is_in_atlas() && !mat2)
81  return true;
82  if (mat2 && mat2->is_in_atlas() && !mat1)
83  return true;
84  }
85 
86  return false;
87 }
88 
90  const material* mat, const std::vector<std::array<vertex, 4>>& quad_list) {
91  if (quad_list.empty())
92  return;
93 
94  if (!is_quad_batching_enabled()) {
95  // Render immediately
96  vertex_count_ += quad_list.size() * 6;
97  render_quads_(mat, quad_list);
98  ++batch_count_;
99  return;
100  }
101 
102  if (!uses_same_texture_(mat, current_material_)) {
103  // Render current batch and start a new one
105  current_material_ = mat;
106  }
107 
108  if (quad_cache_[current_quad_cache_].data.empty()) {
109  // Start a new batch
110  current_material_ = mat;
111  }
112 
113  if (!current_material_) {
114  // Previous quads had no material, override with the new one
115  current_material_ = mat;
116  }
117 
118  // Add to the cache
119  auto& cache = quad_cache_[current_quad_cache_];
120 
122  // To allow quads with no texture to enter the batch
123  // with atlas textures, we just change their UV coordinates
124  // to map to the first top-left pixel of the atlas, which is always white.
125  cache.data.reserve(cache.data.size() + quad_list.size());
126  for (const auto& orig_quad : quad_list) {
127  cache.data.push_back(orig_quad);
128  auto& quad = cache.data.back();
129  quad[0].uvs = quad[1].uvs = quad[2].uvs = quad[3].uvs = vector2f(0.0f, 0.0f);
130  }
131  } else {
132  cache.data.insert(cache.data.end(), quad_list.begin(), quad_list.end());
133  }
134 }
135 
137  auto& cache = quad_cache_[current_quad_cache_];
138  if (cache.data.empty())
139  return;
140 
141  vertex_count_ += cache.data.size() * 6;
142 
143  if (cache.cache) {
144  cache.cache->update(cache.data[0].data(), cache.data.size() * 4);
145  render_cache_(current_material_, *cache.cache, matrix4f::identity);
146  } else {
147  render_quads_(current_material_, cache.data);
148  }
149 
150  cache.data.clear();
151  current_material_ = nullptr;
152 
153  ++current_quad_cache_;
154  if (current_quad_cache_ == batching_cache_cycle_size)
155  current_quad_cache_ = 0u;
156 
157  ++batch_count_;
158 }
159 
161  const material* mat, const vertex_cache& cache, const matrix4f& model_transform) {
162  if (is_quad_batching_enabled()) {
164  }
165 
166  vertex_count_ += cache.get_vertex_count();
167 
168  render_cache_(mat, cache, model_transform);
169 
170  ++batch_count_;
171 }
172 
174  return quad_batching_enabled_;
175 }
176 
178  quad_batching_enabled_ = enabled;
179 }
180 
181 std::shared_ptr<gui::material>
182 renderer::create_material(const std::string& file_name, material::filter filt) {
183  std::string backed_name = utils::to_string(static_cast<std::size_t>(filt)) + '|' + file_name;
184  auto iter = texture_list_.find(backed_name);
185  if (iter != texture_list_.end()) {
186  if (std::shared_ptr<gui::material> lock = iter->second.lock())
187  return lock;
188  else
189  texture_list_.erase(iter);
190  }
191 
192  try {
193  std::shared_ptr<gui::material> tex = create_material_(file_name, filt);
194  texture_list_[file_name] = tex;
195  return tex;
196  } catch (const std::exception& e) {
197  gui::out << gui::warning << e.what() << std::endl;
198  return nullptr;
199  }
200 }
201 
202 namespace {
203 std::string hash_font_parameters(
204  const std::string& font_file,
205  std::size_t size,
206  std::size_t outline,
207  const std::vector<code_point_range>& code_points,
208  char32_t default_code_point) {
209  std::string font_name = font_file + "|s" + utils::to_string(size);
210  if (outline > 0u)
211  font_name += "|o" + utils::to_string(outline);
212 
213  for (const code_point_range& range : code_points)
214  font_name += "|c" + utils::to_string(range.first) + "-" + utils::to_string(range.last);
215 
216  font_name += "|d" + utils::to_string(default_code_point);
217 
218  return font_name;
219 }
220 } // namespace
221 
222 std::shared_ptr<gui::font> renderer::create_font(
223  const std::string& font_file,
224  std::size_t size,
225  std::size_t outline,
226  const std::vector<code_point_range>& code_points,
227  char32_t default_code_point) {
228  const std::string font_name =
229  hash_font_parameters(font_file, size, outline, code_points, default_code_point);
230 
231  auto iter = font_list_.find(font_name);
232  if (iter != font_list_.end()) {
233  if (std::shared_ptr<gui::font> lock = iter->second.lock())
234  return lock;
235  else
236  font_list_.erase(iter);
237  }
238 
239  std::shared_ptr<gui::font> fnt =
240  create_font_(font_file, size, outline, code_points, default_code_point);
241 
242  font_list_[font_name] = fnt;
243  return fnt;
244 }
245 
247  return texture_atlas_enabled_ && is_texture_atlas_supported();
248 }
249 
251  texture_atlas_enabled_ = enabled;
252 }
253 
255  if (texture_atlas_page_size_ == 0u)
256  return std::min<std::size_t>(4096u, get_texture_max_size());
257  else
258  return texture_atlas_page_size_;
259 }
260 
261 void renderer::set_texture_atlas_page_size(std::size_t page_size) {
262  texture_atlas_page_size_ = page_size;
263 }
264 
266  std::size_t count = 0;
267 
268  for (const auto& page : atlas_list_) {
269  count += page.second->get_page_count();
270  }
271 
272  return count;
273 }
274 
276  return vertex_cache_enabled_ && is_vertex_cache_supported();
277 }
278 
280  vertex_cache_enabled_ = enabled;
281 }
282 
284  vertex_cache_enabled_ = true;
285  texture_atlas_enabled_ = true;
286  quad_batching_enabled_ = true;
287 }
288 
289 atlas& renderer::get_atlas_(const std::string& atlas_category, material::filter filt) {
290  std::shared_ptr<gui::atlas> atlas;
291 
292  std::string baked_atlas_name =
293  utils::to_string(static_cast<std::size_t>(filt)) + '|' + atlas_category;
294  auto iter = atlas_list_.find(baked_atlas_name);
295  if (iter != atlas_list_.end()) {
296  atlas = iter->second;
297  }
298 
299  if (!atlas) {
300  atlas = create_atlas_(filt);
301  atlas_list_[baked_atlas_name] = atlas;
302  }
303 
304  return *atlas;
305 }
306 
307 std::shared_ptr<material> renderer::create_atlas_material(
308  const std::string& atlas_category, const std::string& file_name, material::filter filt) {
310  return create_material(file_name, filt);
311 
312  auto& atlas = get_atlas_(atlas_category, filt);
313 
314  auto tex = atlas.fetch_material(file_name);
315  if (tex)
316  return tex;
317 
318  tex = create_material(file_name, filt);
319  if (!tex)
320  return nullptr;
321 
322  auto added_tex = atlas.add_material(file_name, *tex);
323  if (added_tex)
324  return added_tex;
325  else
326  return tex;
327 }
328 
329 std::shared_ptr<font> renderer::create_atlas_font(
330  const std::string& atlas_category,
331  const std::string& font_file,
332  std::size_t size,
333  std::size_t outline,
334  const std::vector<code_point_range>& code_points,
335  char32_t default_code_point) {
337  return create_font(font_file, size, outline, code_points, default_code_point);
338 
339  auto& atlas = get_atlas_(atlas_category, material::filter::none);
340 
341  const std::string font_name =
342  hash_font_parameters(font_file, size, outline, code_points, default_code_point);
343 
344  auto fnt = atlas.fetch_font(font_name);
345  if (fnt)
346  return fnt;
347 
348  fnt = create_font(font_file, size, outline, code_points, default_code_point);
349  if (!fnt)
350  return nullptr;
351 
352  if (atlas.add_font(font_name, fnt))
353  return fnt;
354 
355  font_list_[font_name] = fnt;
356  return fnt;
357 }
358 
359 std::shared_ptr<material> renderer::create_material(std::shared_ptr<render_target> target) {
360  const auto& rect = target->get_rect();
361  return create_material(std::move(target), rect);
362 }
363 
365 
366 } // namespace lxgui::gui
A class that holds multiple materials for efficient rendering.
Definition: gui_atlas.hpp:126
std::shared_ptr< material > add_material(const std::string &file_name, const material &mat)
Add a new material to the atlas.
Definition: gui_atlas.cpp:172
bool add_font(const std::string &font_name, std::shared_ptr< gui::font > fnt)
Add a new font to the atlas.
Definition: gui_atlas.cpp:208
std::shared_ptr< material > fetch_material(const std::string &file_name) const
Find a material in this atlas (nullptr if not found).
Definition: gui_atlas.cpp:161
std::shared_ptr< font > fetch_font(const std::string &font_name) const
Find a font in this atlas (nullptr if not found).
Definition: gui_atlas.cpp:198
A class that holds rendering data.
virtual bool uses_same_texture(const material &other) const =0
Checks if another material is based on the same texture as the current material.
bool is_in_atlas() const
Checks if the material is embedded in an atlas.
bool is_quad_batching_enabled() const
Checks if the renderer has quad render batching enabled.
std::shared_ptr< material > create_atlas_material(const std::string &atlas_category, const std::string &file_name, material::filter filt=material::filter::none)
Creates a new material from a texture file.
std::size_t get_batch_count() const
Returns the number of batches of vertices sent to the GPU since the last call to reset_counters.
std::shared_ptr< font > create_atlas_font(const std::string &atlas_category, const std::string &font_file, std::size_t size, std::size_t outline, const std::vector< code_point_range > &code_points, char32_t default_code_point)
Creates a new font.
std::size_t get_texture_atlas_page_size() const
Returns the width/height of a texture atlas page (in pixels).
virtual std::shared_ptr< vertex_cache > create_vertex_cache(gui::vertex_cache::type type)=0
Creates a new empty vertex cache.
bool is_texture_atlas_enabled() const
Checks if the renderer has texture atlases enabled.
void render_quad(const quad &q)
Renders a quad.
void set_view(const matrix4f &view_matrix)
Sets the view matrix to use when rendering (viewport).
virtual std::shared_ptr< font > create_font_(const std::string &font_file, std::size_t size, std::size_t outline, const std::vector< code_point_range > &code_points, char32_t default_code_point)=0
Creates a new font.
void end()
Ends rendering.
virtual bool is_texture_atlas_supported() const =0
Checks if the renderer supports texture atlases natively.
void render_cache(const material *mat, const vertex_cache &cache, const matrix4f &model_transform=matrix4f::identity)
Renders a vertex cache.
virtual void notify_window_resized(const vector2ui &dimensions)
Notifies the renderer that the render window has been resized.
std::size_t get_vertex_count() const
Returns the number of vertices sent to the GPU since the last call to reset_counters.
std::unordered_map< std::string, std::weak_ptr< gui::font > > font_list_
void auto_detect_settings()
Automatically determines the best rendering settings for the current platform.
std::size_t get_texture_atlas_page_count() const
Count the total number of texture atlas pages currently in use.
void set_texture_atlas_enabled(bool enabled)
Enables/disables texture atlases.
atlas & get_atlas_(const std::string &atlas_category, material::filter filt)
virtual bool is_vertex_cache_supported() const =0
Checks if the renderer supports vertex caches.
virtual std::size_t get_texture_max_size() const =0
Returns the maximum texture width/height (in pixels).
virtual void set_view_(const matrix4f &view_matrix)=0
Sets the view matrix to use when rendering (viewport).
virtual std::shared_ptr< atlas > create_atlas_(material::filter filt)=0
Creates a new atlas with a given texture filter mode.
void set_quad_batching_enabled(bool enabled)
Enables/disables quad batching.
void begin(std::shared_ptr< render_target > target=nullptr)
Begins rendering on a particular render target.
void set_texture_atlas_page_size(std::size_t page_size)
Set the width/height of a texture atlas page (in pixels).
void render_quads(const material *mat, const std::vector< std::array< vertex, 4 >> &quad_list)
Renders a set of quads.
virtual void end_()=0
Ends rendering.
virtual void render_quads_(const material *mat, const std::vector< std::array< vertex, 4 >> &quad_list)=0
Renders a set of quads.
std::unordered_map< std::string, std::weak_ptr< gui::material > > texture_list_
std::unordered_map< std::string, std::shared_ptr< gui::atlas > > atlas_list_
void reset_counters()
Resets the number of batches to zero (for analytics only).
void flush_quad_batch()
Flushes any pending quad batch render operation.
virtual bool is_texture_vertex_color_supported() const =0
Checks if the renderer supports setting colors for each vertex of a textured quad.
virtual std::shared_ptr< material > create_material_(const std::string &file_name, material::filter filt)=0
Creates a new material from a texture file.
std::shared_ptr< font > create_font(const std::string &font_file, std::size_t size, std::size_t outline, const std::vector< code_point_range > &code_points, char32_t default_code_point)
Creates a new font.
bool is_vertex_cache_enabled() const
Checks if the renderer has enabled support for vertex caches.
void set_vertex_cache_enabled(bool enabled)
Enables/disables vertex caches.
virtual void render_cache_(const material *mat, const vertex_cache &cache, const matrix4f &model_transform)=0
Renders a vertex cache.
virtual void begin_(std::shared_ptr< render_target > target)=0
Begins rendering on a particular render target.
std::shared_ptr< material > create_material(const std::string &file_name, material::filter filt=material::filter::none)
Creates a new material from a texture file.
An object representing cached vertex data on the GPU.
@ quads
3 vertices per element
std::size_t get_vertex_count() const
Returns the number of vertices stored in this cache.
vector2< float > vector2f
Holds 2D coordinates (as floats)
std::ostream out
const std::string warning
Definition: gui_out.cpp:6
Represents a contiguous range of unicode code points.
A 4x4 matrix, used for coordinate transformations.
Definition: gui_matrix4.hpp:13
static const matrix4f identity
Definition: gui_matrix4.hpp:51
Simple structure holding four vertices and a material.
Definition: gui_quad.hpp:18
std::shared_ptr< material > mat
Definition: gui_quad.hpp:20