1 | /**************************************************************************/ |
2 | /* renderer_viewport.cpp */ |
3 | /**************************************************************************/ |
4 | /* This file is part of: */ |
5 | /* GODOT ENGINE */ |
6 | /* https://godotengine.org */ |
7 | /**************************************************************************/ |
8 | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ |
9 | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ |
10 | /* */ |
11 | /* Permission is hereby granted, free of charge, to any person obtaining */ |
12 | /* a copy of this software and associated documentation files (the */ |
13 | /* "Software"), to deal in the Software without restriction, including */ |
14 | /* without limitation the rights to use, copy, modify, merge, publish, */ |
15 | /* distribute, sublicense, and/or sell copies of the Software, and to */ |
16 | /* permit persons to whom the Software is furnished to do so, subject to */ |
17 | /* the following conditions: */ |
18 | /* */ |
19 | /* The above copyright notice and this permission notice shall be */ |
20 | /* included in all copies or substantial portions of the Software. */ |
21 | /* */ |
22 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ |
23 | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ |
24 | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ |
25 | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ |
26 | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ |
27 | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ |
28 | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ |
29 | /**************************************************************************/ |
30 | |
31 | #include "renderer_viewport.h" |
32 | |
33 | #include "core/config/project_settings.h" |
34 | #include "core/object/worker_thread_pool.h" |
35 | #include "renderer_canvas_cull.h" |
36 | #include "renderer_scene_cull.h" |
37 | #include "rendering_server_globals.h" |
38 | #include "storage/texture_storage.h" |
39 | |
40 | static Transform2D _canvas_get_transform(RendererViewport::Viewport *p_viewport, RendererCanvasCull::Canvas *p_canvas, RendererViewport::Viewport::CanvasData *p_canvas_data, const Vector2 &p_vp_size) { |
41 | Transform2D xf = p_viewport->global_transform; |
42 | |
43 | float scale = 1.0; |
44 | if (p_viewport->canvas_map.has(p_canvas->parent)) { |
45 | Transform2D c_xform = p_viewport->canvas_map[p_canvas->parent].transform; |
46 | if (p_viewport->snap_2d_transforms_to_pixel) { |
47 | c_xform.columns[2] = c_xform.columns[2].floor(); |
48 | } |
49 | xf = xf * c_xform; |
50 | scale = p_canvas->parent_scale; |
51 | } |
52 | |
53 | Transform2D c_xform = p_canvas_data->transform; |
54 | |
55 | if (p_viewport->snap_2d_transforms_to_pixel) { |
56 | c_xform.columns[2] = c_xform.columns[2].floor(); |
57 | } |
58 | |
59 | xf = xf * c_xform; |
60 | |
61 | if (scale != 1.0 && !RSG::canvas->disable_scale) { |
62 | Vector2 pivot = p_vp_size * 0.5; |
63 | Transform2D xfpivot; |
64 | xfpivot.set_origin(pivot); |
65 | Transform2D xfscale; |
66 | xfscale.scale(Vector2(scale, scale)); |
67 | |
68 | xf = xfpivot.affine_inverse() * xf; |
69 | xf = xfscale * xf; |
70 | xf = xfpivot * xf; |
71 | } |
72 | |
73 | return xf; |
74 | } |
75 | |
76 | Vector<RendererViewport::Viewport *> RendererViewport::_sort_active_viewports() { |
77 | // We need to sort the viewports in a "topological order", children first and |
78 | // parents last. We also need to keep sibling viewports in the original order |
79 | // from top to bottom. |
80 | |
81 | Vector<Viewport *> result; |
82 | List<Viewport *> nodes; |
83 | |
84 | for (int i = active_viewports.size() - 1; i >= 0; --i) { |
85 | Viewport *viewport = active_viewports[i]; |
86 | if (viewport->parent.is_valid()) { |
87 | continue; |
88 | } |
89 | |
90 | nodes.push_back(viewport); |
91 | result.insert(0, viewport); |
92 | } |
93 | |
94 | while (!nodes.is_empty()) { |
95 | const Viewport *node = nodes[0]; |
96 | nodes.pop_front(); |
97 | |
98 | for (int i = active_viewports.size() - 1; i >= 0; --i) { |
99 | Viewport *child = active_viewports[i]; |
100 | if (child->parent != node->self) { |
101 | continue; |
102 | } |
103 | |
104 | if (!nodes.find(child)) { |
105 | nodes.push_back(child); |
106 | result.insert(0, child); |
107 | } |
108 | } |
109 | } |
110 | |
111 | return result; |
112 | } |
113 | |
114 | void RendererViewport::_configure_3d_render_buffers(Viewport *p_viewport) { |
115 | if (p_viewport->render_buffers.is_valid()) { |
116 | if (p_viewport->size.width == 0 || p_viewport->size.height == 0) { |
117 | p_viewport->render_buffers.unref(); |
118 | } else { |
119 | float scaling_3d_scale = p_viewport->scaling_3d_scale; |
120 | RS::ViewportScaling3DMode scaling_3d_mode = p_viewport->scaling_3d_mode; |
121 | |
122 | if ((scaling_3d_mode == RS::VIEWPORT_SCALING_3D_MODE_FSR) && (scaling_3d_scale > 1.0)) { |
123 | // FSR is not designed for downsampling. |
124 | // Fall back to bilinear scaling. |
125 | scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_BILINEAR; |
126 | } |
127 | |
128 | if ((scaling_3d_mode == RS::VIEWPORT_SCALING_3D_MODE_FSR) && !p_viewport->fsr_enabled) { |
129 | // FSR is not actually available. |
130 | // Fall back to bilinear scaling. |
131 | WARN_PRINT_ONCE("FSR 1.0 3D resolution scaling is not available. Falling back to bilinear 3D resolution scaling." ); |
132 | scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_BILINEAR; |
133 | } |
134 | |
135 | if (scaling_3d_scale == 1.0) { |
136 | scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_OFF; |
137 | } |
138 | |
139 | int width; |
140 | int height; |
141 | int render_width; |
142 | int render_height; |
143 | |
144 | switch (scaling_3d_mode) { |
145 | case RS::VIEWPORT_SCALING_3D_MODE_BILINEAR: |
146 | // Clamp 3D rendering resolution to reasonable values supported on most hardware. |
147 | // This prevents freezing the engine or outright crashing on lower-end GPUs. |
148 | width = CLAMP(p_viewport->size.width * scaling_3d_scale, 1, 16384); |
149 | height = CLAMP(p_viewport->size.height * scaling_3d_scale, 1, 16384); |
150 | render_width = width; |
151 | render_height = height; |
152 | break; |
153 | case RS::VIEWPORT_SCALING_3D_MODE_FSR: |
154 | width = p_viewport->size.width; |
155 | height = p_viewport->size.height; |
156 | render_width = MAX(width * scaling_3d_scale, 1.0); // width / (width * scaling) |
157 | render_height = MAX(height * scaling_3d_scale, 1.0); |
158 | break; |
159 | case RS::VIEWPORT_SCALING_3D_MODE_OFF: |
160 | width = p_viewport->size.width; |
161 | height = p_viewport->size.height; |
162 | render_width = width; |
163 | render_height = height; |
164 | break; |
165 | default: |
166 | // This is an unknown mode. |
167 | WARN_PRINT_ONCE(vformat("Unknown scaling mode: %d. Disabling 3D resolution scaling." , scaling_3d_mode)); |
168 | scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_OFF; |
169 | scaling_3d_scale = 1.0; |
170 | width = p_viewport->size.width; |
171 | height = p_viewport->size.height; |
172 | render_width = width; |
173 | render_height = height; |
174 | break; |
175 | } |
176 | |
177 | p_viewport->internal_size = Size2(render_width, render_height); |
178 | |
179 | // At resolution scales lower than 1.0, use negative texture mipmap bias |
180 | // to compensate for the loss of sharpness. |
181 | const float texture_mipmap_bias = log2f(MIN(scaling_3d_scale, 1.0)) + p_viewport->texture_mipmap_bias; |
182 | |
183 | RenderSceneBuffersConfiguration rb_config; |
184 | rb_config.set_render_target(p_viewport->render_target); |
185 | rb_config.set_internal_size(Size2i(render_width, render_height)); |
186 | rb_config.set_target_size(Size2(width, height)); |
187 | rb_config.set_view_count(p_viewport->view_count); |
188 | rb_config.set_scaling_3d_mode(scaling_3d_mode); |
189 | rb_config.set_msaa_3d(p_viewport->msaa_3d); |
190 | rb_config.set_screen_space_aa(p_viewport->screen_space_aa); |
191 | rb_config.set_fsr_sharpness(p_viewport->fsr_sharpness); |
192 | rb_config.set_texture_mipmap_bias(texture_mipmap_bias); |
193 | rb_config.set_use_taa(p_viewport->use_taa); |
194 | |
195 | p_viewport->render_buffers->configure(&rb_config); |
196 | } |
197 | } |
198 | } |
199 | |
200 | void RendererViewport::_draw_3d(Viewport *p_viewport) { |
201 | RENDER_TIMESTAMP("> Render 3D Scene" ); |
202 | |
203 | Ref<XRInterface> xr_interface; |
204 | if (p_viewport->use_xr && XRServer::get_singleton() != nullptr) { |
205 | xr_interface = XRServer::get_singleton()->get_primary_interface(); |
206 | } |
207 | |
208 | if (p_viewport->use_occlusion_culling) { |
209 | if (p_viewport->occlusion_buffer_dirty) { |
210 | float aspect = p_viewport->size.aspect(); |
211 | int max_size = occlusion_rays_per_thread * WorkerThreadPool::get_singleton()->get_thread_count(); |
212 | |
213 | int viewport_size = p_viewport->size.width * p_viewport->size.height; |
214 | max_size = CLAMP(max_size, viewport_size / (32 * 32), viewport_size / (2 * 2)); // At least one depth pixel for every 16x16 region. At most one depth pixel for every 2x2 region. |
215 | |
216 | float height = Math::sqrt(max_size / aspect); |
217 | Size2i new_size = Size2i(height * aspect, height); |
218 | RendererSceneOcclusionCull::get_singleton()->buffer_set_size(p_viewport->self, new_size); |
219 | p_viewport->occlusion_buffer_dirty = false; |
220 | } |
221 | } |
222 | |
223 | float screen_mesh_lod_threshold = p_viewport->mesh_lod_threshold / float(p_viewport->size.width); |
224 | RSG::scene->render_camera(p_viewport->render_buffers, p_viewport->camera, p_viewport->scenario, p_viewport->self, p_viewport->internal_size, p_viewport->use_taa, screen_mesh_lod_threshold, p_viewport->shadow_atlas, xr_interface, &p_viewport->render_info); |
225 | |
226 | RENDER_TIMESTAMP("< Render 3D Scene" ); |
227 | } |
228 | |
229 | void RendererViewport::_draw_viewport(Viewport *p_viewport) { |
230 | if (p_viewport->measure_render_time) { |
231 | String rt_id = "vp_begin_" + itos(p_viewport->self.get_id()); |
232 | RSG::utilities->capture_timestamp(rt_id); |
233 | timestamp_vp_map[rt_id] = p_viewport->self; |
234 | } |
235 | |
236 | if (OS::get_singleton()->get_current_rendering_method() == "gl_compatibility" ) { |
237 | // This is currently needed for GLES to keep the current window being rendered to up to date |
238 | DisplayServer::get_singleton()->gl_window_make_current(p_viewport->viewport_to_screen); |
239 | } |
240 | |
241 | /* Camera should always be BEFORE any other 3D */ |
242 | |
243 | bool scenario_draw_canvas_bg = false; //draw canvas, or some layer of it, as BG for 3D instead of in front |
244 | int scenario_canvas_max_layer = 0; |
245 | bool force_clear_render_target = false; |
246 | |
247 | for (int i = 0; i < RS::VIEWPORT_RENDER_INFO_TYPE_MAX; i++) { |
248 | for (int j = 0; j < RS::VIEWPORT_RENDER_INFO_MAX; j++) { |
249 | p_viewport->render_info.info[i][j] = 0; |
250 | } |
251 | } |
252 | |
253 | if (RSG::scene->is_scenario(p_viewport->scenario)) { |
254 | RID environment = RSG::scene->scenario_get_environment(p_viewport->scenario); |
255 | if (RSG::scene->is_environment(environment)) { |
256 | if (!p_viewport->disable_2d && !viewport_is_environment_disabled(p_viewport)) { |
257 | scenario_draw_canvas_bg = RSG::scene->environment_get_background(environment) == RS::ENV_BG_CANVAS; |
258 | scenario_canvas_max_layer = RSG::scene->environment_get_canvas_max_layer(environment); |
259 | } else if (RSG::scene->environment_get_background(environment) == RS::ENV_BG_CANVAS) { |
260 | // The scene renderer will still copy over the last frame, so we need to clear the render target. |
261 | force_clear_render_target = true; |
262 | } |
263 | } |
264 | } |
265 | |
266 | bool can_draw_3d = RSG::scene->is_camera(p_viewport->camera) && !p_viewport->disable_3d; |
267 | |
268 | if ((scenario_draw_canvas_bg || can_draw_3d) && !p_viewport->render_buffers.is_valid()) { |
269 | //wants to draw 3D but there is no render buffer, create |
270 | p_viewport->render_buffers = RSG::scene->render_buffers_create(); |
271 | |
272 | _configure_3d_render_buffers(p_viewport); |
273 | } |
274 | |
275 | Color bgcolor = p_viewport->transparent_bg ? Color(0, 0, 0, 0) : RSG::texture_storage->get_default_clear_color(); |
276 | |
277 | if (p_viewport->clear_mode != RS::VIEWPORT_CLEAR_NEVER) { |
278 | RSG::texture_storage->render_target_request_clear(p_viewport->render_target, bgcolor); |
279 | if (p_viewport->clear_mode == RS::VIEWPORT_CLEAR_ONLY_NEXT_FRAME) { |
280 | p_viewport->clear_mode = RS::VIEWPORT_CLEAR_NEVER; |
281 | } |
282 | } |
283 | |
284 | if (!scenario_draw_canvas_bg && can_draw_3d) { |
285 | if (force_clear_render_target) { |
286 | RSG::texture_storage->render_target_do_clear_request(p_viewport->render_target); |
287 | } |
288 | _draw_3d(p_viewport); |
289 | } |
290 | |
291 | if (!p_viewport->disable_2d) { |
292 | RBMap<Viewport::CanvasKey, Viewport::CanvasData *> canvas_map; |
293 | |
294 | Rect2 clip_rect(0, 0, p_viewport->size.x, p_viewport->size.y); |
295 | RendererCanvasRender::Light *lights = nullptr; |
296 | RendererCanvasRender::Light *lights_with_shadow = nullptr; |
297 | |
298 | RendererCanvasRender::Light *directional_lights = nullptr; |
299 | RendererCanvasRender::Light *directional_lights_with_shadow = nullptr; |
300 | |
301 | if (p_viewport->sdf_active) { |
302 | // Process SDF. |
303 | |
304 | Rect2 sdf_rect = RSG::texture_storage->render_target_get_sdf_rect(p_viewport->render_target); |
305 | |
306 | RendererCanvasRender::LightOccluderInstance *occluders = nullptr; |
307 | |
308 | // Make list of occluders. |
309 | for (KeyValue<RID, Viewport::CanvasData> &E : p_viewport->canvas_map) { |
310 | RendererCanvasCull::Canvas *canvas = static_cast<RendererCanvasCull::Canvas *>(E.value.canvas); |
311 | Transform2D xf = _canvas_get_transform(p_viewport, canvas, &E.value, clip_rect.size); |
312 | |
313 | for (RendererCanvasRender::LightOccluderInstance *F : canvas->occluders) { |
314 | if (!F->enabled) { |
315 | continue; |
316 | } |
317 | F->xform_cache = xf * F->xform; |
318 | |
319 | if (sdf_rect.intersects_transformed(F->xform_cache, F->aabb_cache)) { |
320 | F->next = occluders; |
321 | occluders = F; |
322 | } |
323 | } |
324 | } |
325 | |
326 | RSG::canvas_render->render_sdf(p_viewport->render_target, occluders); |
327 | RSG::texture_storage->render_target_mark_sdf_enabled(p_viewport->render_target, true); |
328 | |
329 | p_viewport->sdf_active = false; // If used, gets set active again. |
330 | } else { |
331 | RSG::texture_storage->render_target_mark_sdf_enabled(p_viewport->render_target, false); |
332 | } |
333 | |
334 | Rect2 shadow_rect; |
335 | |
336 | int shadow_count = 0; |
337 | int directional_light_count = 0; |
338 | |
339 | RENDER_TIMESTAMP("Cull 2D Lights" ); |
340 | for (KeyValue<RID, Viewport::CanvasData> &E : p_viewport->canvas_map) { |
341 | RendererCanvasCull::Canvas *canvas = static_cast<RendererCanvasCull::Canvas *>(E.value.canvas); |
342 | |
343 | Transform2D xf = _canvas_get_transform(p_viewport, canvas, &E.value, clip_rect.size); |
344 | |
345 | // Find lights in canvas. |
346 | |
347 | for (RendererCanvasRender::Light *F : canvas->lights) { |
348 | RendererCanvasRender::Light *cl = F; |
349 | if (cl->enabled && cl->texture.is_valid()) { |
350 | //not super efficient.. |
351 | Size2 tsize = RSG::texture_storage->texture_size_with_proxy(cl->texture); |
352 | tsize *= cl->scale; |
353 | |
354 | Vector2 offset = tsize / 2.0; |
355 | cl->rect_cache = Rect2(-offset + cl->texture_offset, tsize); |
356 | cl->xform_cache = xf * cl->xform; |
357 | |
358 | if (clip_rect.intersects_transformed(cl->xform_cache, cl->rect_cache)) { |
359 | cl->filter_next_ptr = lights; |
360 | lights = cl; |
361 | Transform2D scale; |
362 | scale.scale(cl->rect_cache.size); |
363 | scale.columns[2] = cl->rect_cache.position; |
364 | cl->light_shader_xform = xf * cl->xform * scale; |
365 | if (cl->use_shadow) { |
366 | cl->shadows_next_ptr = lights_with_shadow; |
367 | if (lights_with_shadow == nullptr) { |
368 | shadow_rect = cl->xform_cache.xform(cl->rect_cache); |
369 | } else { |
370 | shadow_rect = shadow_rect.merge(cl->xform_cache.xform(cl->rect_cache)); |
371 | } |
372 | lights_with_shadow = cl; |
373 | cl->radius_cache = cl->rect_cache.size.length(); |
374 | } |
375 | } |
376 | } |
377 | } |
378 | |
379 | for (RendererCanvasRender::Light *F : canvas->directional_lights) { |
380 | RendererCanvasRender::Light *cl = F; |
381 | if (cl->enabled) { |
382 | cl->filter_next_ptr = directional_lights; |
383 | directional_lights = cl; |
384 | cl->xform_cache = xf * cl->xform; |
385 | cl->xform_cache.columns[2] = Vector2(); //translation is pointless |
386 | if (cl->use_shadow) { |
387 | cl->shadows_next_ptr = directional_lights_with_shadow; |
388 | directional_lights_with_shadow = cl; |
389 | } |
390 | |
391 | directional_light_count++; |
392 | |
393 | if (directional_light_count == RS::MAX_2D_DIRECTIONAL_LIGHTS) { |
394 | break; |
395 | } |
396 | } |
397 | } |
398 | |
399 | canvas_map[Viewport::CanvasKey(E.key, E.value.layer, E.value.sublayer)] = &E.value; |
400 | } |
401 | |
402 | if (lights_with_shadow) { |
403 | //update shadows if any |
404 | |
405 | RendererCanvasRender::LightOccluderInstance *occluders = nullptr; |
406 | |
407 | RENDER_TIMESTAMP("> Render PointLight2D Shadows" ); |
408 | RENDER_TIMESTAMP("Cull LightOccluder2Ds" ); |
409 | |
410 | //make list of occluders |
411 | for (KeyValue<RID, Viewport::CanvasData> &E : p_viewport->canvas_map) { |
412 | RendererCanvasCull::Canvas *canvas = static_cast<RendererCanvasCull::Canvas *>(E.value.canvas); |
413 | Transform2D xf = _canvas_get_transform(p_viewport, canvas, &E.value, clip_rect.size); |
414 | |
415 | for (RendererCanvasRender::LightOccluderInstance *F : canvas->occluders) { |
416 | if (!F->enabled) { |
417 | continue; |
418 | } |
419 | F->xform_cache = xf * F->xform; |
420 | if (shadow_rect.intersects_transformed(F->xform_cache, F->aabb_cache)) { |
421 | F->next = occluders; |
422 | occluders = F; |
423 | } |
424 | } |
425 | } |
426 | //update the light shadowmaps with them |
427 | |
428 | RendererCanvasRender::Light *light = lights_with_shadow; |
429 | while (light) { |
430 | RENDER_TIMESTAMP("Render PointLight2D Shadow" ); |
431 | |
432 | RSG::canvas_render->light_update_shadow(light->light_internal, shadow_count++, light->xform_cache.affine_inverse(), light->item_shadow_mask, light->radius_cache / 1000.0, light->radius_cache * 1.1, occluders); |
433 | light = light->shadows_next_ptr; |
434 | } |
435 | |
436 | RENDER_TIMESTAMP("< Render PointLight2D Shadows" ); |
437 | } |
438 | |
439 | if (directional_lights_with_shadow) { |
440 | //update shadows if any |
441 | RendererCanvasRender::Light *light = directional_lights_with_shadow; |
442 | while (light) { |
443 | Vector2 light_dir = -light->xform_cache.columns[1].normalized(); // Y is light direction |
444 | float cull_distance = light->directional_distance; |
445 | |
446 | Vector2 light_dir_sign; |
447 | light_dir_sign.x = (ABS(light_dir.x) < CMP_EPSILON) ? 0.0 : ((light_dir.x > 0.0) ? 1.0 : -1.0); |
448 | light_dir_sign.y = (ABS(light_dir.y) < CMP_EPSILON) ? 0.0 : ((light_dir.y > 0.0) ? 1.0 : -1.0); |
449 | |
450 | Vector2 points[6]; |
451 | int point_count = 0; |
452 | |
453 | for (int j = 0; j < 4; j++) { |
454 | static const Vector2 signs[4] = { Vector2(1, 1), Vector2(1, 0), Vector2(0, 0), Vector2(0, 1) }; |
455 | Vector2 sign_cmp = signs[j] * 2.0 - Vector2(1.0, 1.0); |
456 | Vector2 point = clip_rect.position + clip_rect.size * signs[j]; |
457 | |
458 | if (sign_cmp == light_dir_sign) { |
459 | //both point in same direction, plot offsetted |
460 | points[point_count++] = point + light_dir * cull_distance; |
461 | } else if (sign_cmp.x == light_dir_sign.x || sign_cmp.y == light_dir_sign.y) { |
462 | int next_j = (j + 1) % 4; |
463 | Vector2 next_sign_cmp = signs[next_j] * 2.0 - Vector2(1.0, 1.0); |
464 | |
465 | //one point in the same direction, plot segment |
466 | |
467 | if (next_sign_cmp.x == light_dir_sign.x || next_sign_cmp.y == light_dir_sign.y) { |
468 | if (light_dir_sign.x != 0.0 || light_dir_sign.y != 0.0) { |
469 | points[point_count++] = point; |
470 | } |
471 | points[point_count++] = point + light_dir * cull_distance; |
472 | } else { |
473 | points[point_count++] = point + light_dir * cull_distance; |
474 | if (light_dir_sign.x != 0.0 || light_dir_sign.y != 0.0) { |
475 | points[point_count++] = point; |
476 | } |
477 | } |
478 | } else { |
479 | //plot normally |
480 | points[point_count++] = point; |
481 | } |
482 | } |
483 | |
484 | Vector2 xf_points[6]; |
485 | |
486 | RendererCanvasRender::LightOccluderInstance *occluders = nullptr; |
487 | |
488 | RENDER_TIMESTAMP("> Render DirectionalLight2D Shadows" ); |
489 | |
490 | // Make list of occluders. |
491 | for (KeyValue<RID, Viewport::CanvasData> &E : p_viewport->canvas_map) { |
492 | RendererCanvasCull::Canvas *canvas = static_cast<RendererCanvasCull::Canvas *>(E.value.canvas); |
493 | Transform2D xf = _canvas_get_transform(p_viewport, canvas, &E.value, clip_rect.size); |
494 | |
495 | for (RendererCanvasRender::LightOccluderInstance *F : canvas->occluders) { |
496 | if (!F->enabled) { |
497 | continue; |
498 | } |
499 | F->xform_cache = xf * F->xform; |
500 | Transform2D localizer = F->xform_cache.affine_inverse(); |
501 | |
502 | for (int j = 0; j < point_count; j++) { |
503 | xf_points[j] = localizer.xform(points[j]); |
504 | } |
505 | if (F->aabb_cache.intersects_filled_polygon(xf_points, point_count)) { |
506 | F->next = occluders; |
507 | occluders = F; |
508 | } |
509 | } |
510 | } |
511 | |
512 | RSG::canvas_render->light_update_directional_shadow(light->light_internal, shadow_count++, light->xform_cache, light->item_shadow_mask, cull_distance, clip_rect, occluders); |
513 | |
514 | light = light->shadows_next_ptr; |
515 | } |
516 | |
517 | RENDER_TIMESTAMP("< Render DirectionalLight2D Shadows" ); |
518 | } |
519 | |
520 | if (scenario_draw_canvas_bg && canvas_map.begin() && canvas_map.begin()->key.get_layer() > scenario_canvas_max_layer) { |
521 | // There may be an outstanding clear request if a clear was requested, but no 2D elements were drawn. |
522 | // Clear now otherwise we copy over garbage from the render target. |
523 | RSG::texture_storage->render_target_do_clear_request(p_viewport->render_target); |
524 | if (!can_draw_3d) { |
525 | RSG::scene->render_empty_scene(p_viewport->render_buffers, p_viewport->scenario, p_viewport->shadow_atlas); |
526 | } else { |
527 | _draw_3d(p_viewport); |
528 | } |
529 | scenario_draw_canvas_bg = false; |
530 | } |
531 | |
532 | for (const KeyValue<Viewport::CanvasKey, Viewport::CanvasData *> &E : canvas_map) { |
533 | RendererCanvasCull::Canvas *canvas = static_cast<RendererCanvasCull::Canvas *>(E.value->canvas); |
534 | |
535 | Transform2D xform = _canvas_get_transform(p_viewport, canvas, E.value, clip_rect.size); |
536 | |
537 | RendererCanvasRender::Light *canvas_lights = nullptr; |
538 | RendererCanvasRender::Light *canvas_directional_lights = nullptr; |
539 | |
540 | RendererCanvasRender::Light *ptr = lights; |
541 | while (ptr) { |
542 | if (E.value->layer >= ptr->layer_min && E.value->layer <= ptr->layer_max) { |
543 | ptr->next_ptr = canvas_lights; |
544 | canvas_lights = ptr; |
545 | } |
546 | ptr = ptr->filter_next_ptr; |
547 | } |
548 | |
549 | ptr = directional_lights; |
550 | while (ptr) { |
551 | if (E.value->layer >= ptr->layer_min && E.value->layer <= ptr->layer_max) { |
552 | ptr->next_ptr = canvas_directional_lights; |
553 | canvas_directional_lights = ptr; |
554 | } |
555 | ptr = ptr->filter_next_ptr; |
556 | } |
557 | |
558 | RSG::canvas->render_canvas(p_viewport->render_target, canvas, xform, canvas_lights, canvas_directional_lights, clip_rect, p_viewport->texture_filter, p_viewport->texture_repeat, p_viewport->snap_2d_transforms_to_pixel, p_viewport->snap_2d_vertices_to_pixel, p_viewport->canvas_cull_mask); |
559 | if (RSG::canvas->was_sdf_used()) { |
560 | p_viewport->sdf_active = true; |
561 | } |
562 | |
563 | if (scenario_draw_canvas_bg && E.key.get_layer() >= scenario_canvas_max_layer) { |
564 | // There may be an outstanding clear request if a clear was requested, but no 2D elements were drawn. |
565 | // Clear now otherwise we copy over garbage from the render target. |
566 | RSG::texture_storage->render_target_do_clear_request(p_viewport->render_target); |
567 | if (!can_draw_3d) { |
568 | RSG::scene->render_empty_scene(p_viewport->render_buffers, p_viewport->scenario, p_viewport->shadow_atlas); |
569 | } else { |
570 | _draw_3d(p_viewport); |
571 | } |
572 | |
573 | scenario_draw_canvas_bg = false; |
574 | } |
575 | } |
576 | |
577 | if (scenario_draw_canvas_bg) { |
578 | // There may be an outstanding clear request if a clear was requested, but no 2D elements were drawn. |
579 | // Clear now otherwise we copy over garbage from the render target. |
580 | RSG::texture_storage->render_target_do_clear_request(p_viewport->render_target); |
581 | if (!can_draw_3d) { |
582 | RSG::scene->render_empty_scene(p_viewport->render_buffers, p_viewport->scenario, p_viewport->shadow_atlas); |
583 | } else { |
584 | _draw_3d(p_viewport); |
585 | } |
586 | } |
587 | } |
588 | |
589 | if (RSG::texture_storage->render_target_is_clear_requested(p_viewport->render_target)) { |
590 | //was never cleared in the end, force clear it |
591 | RSG::texture_storage->render_target_do_clear_request(p_viewport->render_target); |
592 | } |
593 | |
594 | if (p_viewport->measure_render_time) { |
595 | String rt_id = "vp_end_" + itos(p_viewport->self.get_id()); |
596 | RSG::utilities->capture_timestamp(rt_id); |
597 | timestamp_vp_map[rt_id] = p_viewport->self; |
598 | } |
599 | } |
600 | |
601 | void RendererViewport::draw_viewports() { |
602 | timestamp_vp_map.clear(); |
603 | |
604 | // get our xr interface in case we need it |
605 | Ref<XRInterface> xr_interface; |
606 | XRServer *xr_server = XRServer::get_singleton(); |
607 | if (xr_server != nullptr) { |
608 | // let our XR server know we're about to render our frames so we can get our frame timing |
609 | xr_server->pre_render(); |
610 | |
611 | // retrieve the interface responsible for rendering |
612 | xr_interface = xr_server->get_primary_interface(); |
613 | } |
614 | |
615 | if (Engine::get_singleton()->is_editor_hint()) { |
616 | set_default_clear_color(GLOBAL_GET("rendering/environment/defaults/default_clear_color" )); |
617 | } |
618 | |
619 | if (sorted_active_viewports_dirty) { |
620 | sorted_active_viewports = _sort_active_viewports(); |
621 | sorted_active_viewports_dirty = false; |
622 | } |
623 | |
624 | HashMap<DisplayServer::WindowID, Vector<BlitToScreen>> blit_to_screen_list; |
625 | //draw viewports |
626 | RENDER_TIMESTAMP("> Render Viewports" ); |
627 | |
628 | //determine what is visible |
629 | draw_viewports_pass++; |
630 | |
631 | for (int i = sorted_active_viewports.size() - 1; i >= 0; i--) { //to compute parent dependency, must go in reverse draw order |
632 | |
633 | Viewport *vp = sorted_active_viewports[i]; |
634 | |
635 | if (vp->update_mode == RS::VIEWPORT_UPDATE_DISABLED) { |
636 | continue; |
637 | } |
638 | |
639 | if (!vp->render_target.is_valid()) { |
640 | continue; |
641 | } |
642 | //ERR_CONTINUE(!vp->render_target.is_valid()); |
643 | |
644 | bool visible = vp->viewport_to_screen_rect != Rect2(); |
645 | |
646 | if (vp->use_xr) { |
647 | if (xr_interface.is_valid()) { |
648 | // Ignore update mode we have to commit frames to our XR interface |
649 | visible = true; |
650 | |
651 | // Override our size, make sure it matches our required size and is created as a stereo target |
652 | Size2 xr_size = xr_interface->get_render_target_size(); |
653 | _viewport_set_size(vp, xr_size.width, xr_size.height, xr_interface->get_view_count()); |
654 | } else { |
655 | // don't render anything |
656 | visible = false; |
657 | vp->size = Size2(); |
658 | } |
659 | } else { |
660 | if (vp->update_mode == RS::VIEWPORT_UPDATE_ALWAYS || vp->update_mode == RS::VIEWPORT_UPDATE_ONCE) { |
661 | visible = true; |
662 | } |
663 | |
664 | if (vp->update_mode == RS::VIEWPORT_UPDATE_WHEN_VISIBLE && RSG::texture_storage->render_target_was_used(vp->render_target)) { |
665 | visible = true; |
666 | } |
667 | |
668 | if (vp->update_mode == RS::VIEWPORT_UPDATE_WHEN_PARENT_VISIBLE) { |
669 | Viewport *parent = viewport_owner.get_or_null(vp->parent); |
670 | if (parent && parent->last_pass == draw_viewports_pass) { |
671 | visible = true; |
672 | } |
673 | } |
674 | } |
675 | |
676 | visible = visible && vp->size.x > 1 && vp->size.y > 1; |
677 | |
678 | if (visible) { |
679 | vp->last_pass = draw_viewports_pass; |
680 | } |
681 | } |
682 | |
683 | int vertices_drawn = 0; |
684 | int objects_drawn = 0; |
685 | int draw_calls_used = 0; |
686 | |
687 | for (int i = 0; i < sorted_active_viewports.size(); i++) { |
688 | Viewport *vp = sorted_active_viewports[i]; |
689 | |
690 | if (vp->last_pass != draw_viewports_pass) { |
691 | continue; //should not draw |
692 | } |
693 | |
694 | RENDER_TIMESTAMP("> Render Viewport " + itos(i)); |
695 | |
696 | RSG::texture_storage->render_target_set_as_unused(vp->render_target); |
697 | if (vp->use_xr && xr_interface.is_valid()) { |
698 | // Inform XR interface we're about to render its viewport, |
699 | // if this returns false we don't render. |
700 | // This usually is a result of the player taking off their headset and OpenXR telling us to skip |
701 | // rendering frames. |
702 | if (xr_interface->pre_draw_viewport(vp->render_target)) { |
703 | RSG::texture_storage->render_target_set_override(vp->render_target, |
704 | xr_interface->get_color_texture(), |
705 | xr_interface->get_depth_texture(), |
706 | xr_interface->get_velocity_texture()); |
707 | |
708 | // render... |
709 | RSG::scene->set_debug_draw_mode(vp->debug_draw); |
710 | |
711 | // and draw viewport |
712 | _draw_viewport(vp); |
713 | |
714 | // commit our eyes |
715 | Vector<BlitToScreen> blits = xr_interface->post_draw_viewport(vp->render_target, vp->viewport_to_screen_rect); |
716 | if (vp->viewport_to_screen != DisplayServer::INVALID_WINDOW_ID) { |
717 | if (OS::get_singleton()->get_current_rendering_driver_name() == "opengl3" ) { |
718 | if (blits.size() > 0) { |
719 | RSG::rasterizer->blit_render_targets_to_screen(vp->viewport_to_screen, blits.ptr(), blits.size()); |
720 | } |
721 | RSG::rasterizer->end_frame(true); |
722 | } else if (blits.size() > 0) { |
723 | if (!blit_to_screen_list.has(vp->viewport_to_screen)) { |
724 | blit_to_screen_list[vp->viewport_to_screen] = Vector<BlitToScreen>(); |
725 | } |
726 | |
727 | for (int b = 0; b < blits.size(); b++) { |
728 | blit_to_screen_list[vp->viewport_to_screen].push_back(blits[b]); |
729 | } |
730 | } |
731 | } |
732 | } |
733 | } else { |
734 | RSG::texture_storage->render_target_set_override(vp->render_target, RID(), RID(), RID()); |
735 | |
736 | RSG::scene->set_debug_draw_mode(vp->debug_draw); |
737 | |
738 | // render standard mono camera |
739 | _draw_viewport(vp); |
740 | |
741 | if (vp->viewport_to_screen != DisplayServer::INVALID_WINDOW_ID && (!vp->viewport_render_direct_to_screen || !RSG::rasterizer->is_low_end())) { |
742 | //copy to screen if set as such |
743 | BlitToScreen blit; |
744 | blit.render_target = vp->render_target; |
745 | if (vp->viewport_to_screen_rect != Rect2()) { |
746 | blit.dst_rect = vp->viewport_to_screen_rect; |
747 | } else { |
748 | blit.dst_rect.position = Vector2(); |
749 | blit.dst_rect.size = vp->size; |
750 | } |
751 | |
752 | if (!blit_to_screen_list.has(vp->viewport_to_screen)) { |
753 | blit_to_screen_list[vp->viewport_to_screen] = Vector<BlitToScreen>(); |
754 | } |
755 | |
756 | if (OS::get_singleton()->get_current_rendering_driver_name() == "opengl3" ) { |
757 | Vector<BlitToScreen> blit_to_screen_vec; |
758 | blit_to_screen_vec.push_back(blit); |
759 | RSG::rasterizer->blit_render_targets_to_screen(vp->viewport_to_screen, blit_to_screen_vec.ptr(), 1); |
760 | RSG::rasterizer->end_frame(true); |
761 | } else { |
762 | blit_to_screen_list[vp->viewport_to_screen].push_back(blit); |
763 | } |
764 | } |
765 | } |
766 | |
767 | if (vp->update_mode == RS::VIEWPORT_UPDATE_ONCE) { |
768 | vp->update_mode = RS::VIEWPORT_UPDATE_DISABLED; |
769 | } |
770 | |
771 | RENDER_TIMESTAMP("< Render Viewport " + itos(i)); |
772 | |
773 | objects_drawn += vp->render_info.info[RS::VIEWPORT_RENDER_INFO_TYPE_VISIBLE][RS::VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME] + vp->render_info.info[RS::VIEWPORT_RENDER_INFO_TYPE_SHADOW][RS::VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME]; |
774 | vertices_drawn += vp->render_info.info[RS::VIEWPORT_RENDER_INFO_TYPE_VISIBLE][RS::VIEWPORT_RENDER_INFO_PRIMITIVES_IN_FRAME] + vp->render_info.info[RS::VIEWPORT_RENDER_INFO_TYPE_SHADOW][RS::VIEWPORT_RENDER_INFO_PRIMITIVES_IN_FRAME]; |
775 | draw_calls_used += vp->render_info.info[RS::VIEWPORT_RENDER_INFO_TYPE_VISIBLE][RS::VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME] + vp->render_info.info[RS::VIEWPORT_RENDER_INFO_TYPE_SHADOW][RS::VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME]; |
776 | } |
777 | RSG::scene->set_debug_draw_mode(RS::VIEWPORT_DEBUG_DRAW_DISABLED); |
778 | |
779 | total_objects_drawn = objects_drawn; |
780 | total_vertices_drawn = vertices_drawn; |
781 | total_draw_calls_used = draw_calls_used; |
782 | |
783 | RENDER_TIMESTAMP("< Render Viewports" ); |
784 | //this needs to be called to make screen swapping more efficient |
785 | RSG::rasterizer->prepare_for_blitting_render_targets(); |
786 | |
787 | for (const KeyValue<int, Vector<BlitToScreen>> &E : blit_to_screen_list) { |
788 | RSG::rasterizer->blit_render_targets_to_screen(E.key, E.value.ptr(), E.value.size()); |
789 | } |
790 | } |
791 | |
792 | RID RendererViewport::viewport_allocate() { |
793 | return viewport_owner.allocate_rid(); |
794 | } |
795 | |
796 | void RendererViewport::viewport_initialize(RID p_rid) { |
797 | viewport_owner.initialize_rid(p_rid); |
798 | Viewport *viewport = viewport_owner.get_or_null(p_rid); |
799 | viewport->self = p_rid; |
800 | viewport->render_target = RSG::texture_storage->render_target_create(); |
801 | viewport->shadow_atlas = RSG::light_storage->shadow_atlas_create(); |
802 | viewport->viewport_render_direct_to_screen = false; |
803 | |
804 | viewport->fsr_enabled = !RSG::rasterizer->is_low_end() && !viewport->disable_3d; |
805 | } |
806 | |
807 | void RendererViewport::viewport_set_use_xr(RID p_viewport, bool p_use_xr) { |
808 | Viewport *viewport = viewport_owner.get_or_null(p_viewport); |
809 | ERR_FAIL_COND(!viewport); |
810 | |
811 | if (viewport->use_xr == p_use_xr) { |
812 | return; |
813 | } |
814 | |
815 | viewport->use_xr = p_use_xr; |
816 | |
817 | // Re-configure the 3D render buffers when disabling XR. They'll get |
818 | // re-configured when enabling XR in draw_viewports(). |
819 | if (!p_use_xr) { |
820 | viewport->view_count = 1; |
821 | _configure_3d_render_buffers(viewport); |
822 | } |
823 | } |
824 | |
825 | void RendererViewport::viewport_set_scaling_3d_mode(RID p_viewport, RS::ViewportScaling3DMode p_mode) { |
826 | Viewport *viewport = viewport_owner.get_or_null(p_viewport); |
827 | ERR_FAIL_COND(!viewport); |
828 | |
829 | viewport->scaling_3d_mode = p_mode; |
830 | _configure_3d_render_buffers(viewport); |
831 | } |
832 | |
833 | void RendererViewport::viewport_set_fsr_sharpness(RID p_viewport, float p_sharpness) { |
834 | Viewport *viewport = viewport_owner.get_or_null(p_viewport); |
835 | ERR_FAIL_COND(!viewport); |
836 | |
837 | viewport->fsr_sharpness = p_sharpness; |
838 | if (viewport->render_buffers.is_valid()) { |
839 | viewport->render_buffers->set_fsr_sharpness(p_sharpness); |
840 | } |
841 | } |
842 | |
843 | void RendererViewport::viewport_set_texture_mipmap_bias(RID p_viewport, float p_mipmap_bias) { |
844 | Viewport *viewport = viewport_owner.get_or_null(p_viewport); |
845 | ERR_FAIL_COND(!viewport); |
846 | |
847 | viewport->texture_mipmap_bias = p_mipmap_bias; |
848 | if (viewport->render_buffers.is_valid()) { |
849 | viewport->render_buffers->set_texture_mipmap_bias(p_mipmap_bias); |
850 | } |
851 | } |
852 | |
853 | void RendererViewport::viewport_set_scaling_3d_scale(RID p_viewport, float p_scaling_3d_scale) { |
854 | Viewport *viewport = viewport_owner.get_or_null(p_viewport); |
855 | ERR_FAIL_COND(!viewport); |
856 | |
857 | // Clamp to reasonable values that are actually useful. |
858 | // Values above 2.0 don't serve a practical purpose since the viewport |
859 | // isn't displayed with mipmaps. |
860 | if (viewport->scaling_3d_scale == CLAMP(p_scaling_3d_scale, 0.1, 2.0)) { |
861 | return; |
862 | } |
863 | |
864 | viewport->scaling_3d_scale = CLAMP(p_scaling_3d_scale, 0.1, 2.0); |
865 | _configure_3d_render_buffers(viewport); |
866 | } |
867 | |
868 | void RendererViewport::viewport_set_size(RID p_viewport, int p_width, int p_height) { |
869 | ERR_FAIL_COND(p_width < 0 || p_height < 0); |
870 | |
871 | Viewport *viewport = viewport_owner.get_or_null(p_viewport); |
872 | ERR_FAIL_COND(!viewport); |
873 | ERR_FAIL_COND_MSG(viewport->use_xr, "Cannot set viewport size when using XR" ); |
874 | |
875 | _viewport_set_size(viewport, p_width, p_height, 1); |
876 | } |
877 | |
878 | void RendererViewport::_viewport_set_size(Viewport *p_viewport, int p_width, int p_height, uint32_t p_view_count) { |
879 | Size2i new_size(p_width, p_height); |
880 | if (p_viewport->size != new_size || p_viewport->view_count != p_view_count) { |
881 | p_viewport->size = new_size; |
882 | p_viewport->view_count = p_view_count; |
883 | |
884 | RSG::texture_storage->render_target_set_size(p_viewport->render_target, p_width, p_height, p_view_count); |
885 | _configure_3d_render_buffers(p_viewport); |
886 | |
887 | p_viewport->occlusion_buffer_dirty = true; |
888 | } |
889 | } |
890 | |
891 | void RendererViewport::viewport_set_active(RID p_viewport, bool p_active) { |
892 | Viewport *viewport = viewport_owner.get_or_null(p_viewport); |
893 | ERR_FAIL_COND(!viewport); |
894 | |
895 | if (p_active) { |
896 | ERR_FAIL_COND_MSG(active_viewports.has(viewport), "Can't make active a Viewport that is already active." ); |
897 | viewport->occlusion_buffer_dirty = true; |
898 | active_viewports.push_back(viewport); |
899 | } else { |
900 | active_viewports.erase(viewport); |
901 | } |
902 | |
903 | sorted_active_viewports_dirty = true; |
904 | } |
905 | |
906 | void RendererViewport::viewport_set_parent_viewport(RID p_viewport, RID p_parent_viewport) { |
907 | Viewport *viewport = viewport_owner.get_or_null(p_viewport); |
908 | ERR_FAIL_COND(!viewport); |
909 | |
910 | viewport->parent = p_parent_viewport; |
911 | } |
912 | |
913 | void RendererViewport::viewport_set_clear_mode(RID p_viewport, RS::ViewportClearMode p_clear_mode) { |
914 | Viewport *viewport = viewport_owner.get_or_null(p_viewport); |
915 | ERR_FAIL_COND(!viewport); |
916 | |
917 | viewport->clear_mode = p_clear_mode; |
918 | } |
919 | |
920 | void RendererViewport::viewport_attach_to_screen(RID p_viewport, const Rect2 &p_rect, DisplayServer::WindowID p_screen) { |
921 | Viewport *viewport = viewport_owner.get_or_null(p_viewport); |
922 | ERR_FAIL_COND(!viewport); |
923 | |
924 | if (p_screen != DisplayServer::INVALID_WINDOW_ID) { |
925 | // If using OpenGL we can optimize this operation by rendering directly to system_fbo |
926 | // instead of rendering to fbo and copying to system_fbo after |
927 | if (RSG::rasterizer->is_low_end() && viewport->viewport_render_direct_to_screen) { |
928 | RSG::texture_storage->render_target_set_size(viewport->render_target, p_rect.size.x, p_rect.size.y, viewport->view_count); |
929 | RSG::texture_storage->render_target_set_position(viewport->render_target, p_rect.position.x, p_rect.position.y); |
930 | } |
931 | |
932 | viewport->viewport_to_screen_rect = p_rect; |
933 | viewport->viewport_to_screen = p_screen; |
934 | } else { |
935 | // if render_direct_to_screen was used, reset size and position |
936 | if (RSG::rasterizer->is_low_end() && viewport->viewport_render_direct_to_screen) { |
937 | RSG::texture_storage->render_target_set_position(viewport->render_target, 0, 0); |
938 | RSG::texture_storage->render_target_set_size(viewport->render_target, viewport->size.x, viewport->size.y, viewport->view_count); |
939 | } |
940 | |
941 | viewport->viewport_to_screen_rect = Rect2(); |
942 | viewport->viewport_to_screen = DisplayServer::INVALID_WINDOW_ID; |
943 | } |
944 | } |
945 | |
946 | void RendererViewport::viewport_set_render_direct_to_screen(RID p_viewport, bool p_enable) { |
947 | Viewport *viewport = viewport_owner.get_or_null(p_viewport); |
948 | ERR_FAIL_COND(!viewport); |
949 | |
950 | if (p_enable == viewport->viewport_render_direct_to_screen) { |
951 | return; |
952 | } |
953 | |
954 | // if disabled, reset render_target size and position |
955 | if (!p_enable) { |
956 | RSG::texture_storage->render_target_set_position(viewport->render_target, 0, 0); |
957 | RSG::texture_storage->render_target_set_size(viewport->render_target, viewport->size.x, viewport->size.y, viewport->view_count); |
958 | } |
959 | |
960 | RSG::texture_storage->render_target_set_direct_to_screen(viewport->render_target, p_enable); |
961 | viewport->viewport_render_direct_to_screen = p_enable; |
962 | |
963 | // if attached to screen already, setup screen size and position, this needs to happen after setting flag to avoid an unnecessary buffer allocation |
964 | if (RSG::rasterizer->is_low_end() && viewport->viewport_to_screen_rect != Rect2() && p_enable) { |
965 | RSG::texture_storage->render_target_set_size(viewport->render_target, viewport->viewport_to_screen_rect.size.x, viewport->viewport_to_screen_rect.size.y, viewport->view_count); |
966 | RSG::texture_storage->render_target_set_position(viewport->render_target, viewport->viewport_to_screen_rect.position.x, viewport->viewport_to_screen_rect.position.y); |
967 | } |
968 | } |
969 | |
970 | void RendererViewport::viewport_set_update_mode(RID p_viewport, RS::ViewportUpdateMode p_mode) { |
971 | Viewport *viewport = viewport_owner.get_or_null(p_viewport); |
972 | ERR_FAIL_COND(!viewport); |
973 | |
974 | viewport->update_mode = p_mode; |
975 | } |
976 | |
977 | RID RendererViewport::viewport_get_render_target(RID p_viewport) const { |
978 | const Viewport *viewport = viewport_owner.get_or_null(p_viewport); |
979 | ERR_FAIL_COND_V(!viewport, RID()); |
980 | |
981 | return viewport->render_target; |
982 | } |
983 | |
984 | RID RendererViewport::viewport_get_texture(RID p_viewport) const { |
985 | const Viewport *viewport = viewport_owner.get_or_null(p_viewport); |
986 | ERR_FAIL_COND_V(!viewport, RID()); |
987 | |
988 | return RSG::texture_storage->render_target_get_texture(viewport->render_target); |
989 | } |
990 | |
991 | RID RendererViewport::viewport_get_occluder_debug_texture(RID p_viewport) const { |
992 | const Viewport *viewport = viewport_owner.get_or_null(p_viewport); |
993 | ERR_FAIL_COND_V(!viewport, RID()); |
994 | |
995 | if (viewport->use_occlusion_culling && viewport->debug_draw == RenderingServer::VIEWPORT_DEBUG_DRAW_OCCLUDERS) { |
996 | return RendererSceneOcclusionCull::get_singleton()->buffer_get_debug_texture(p_viewport); |
997 | } |
998 | return RID(); |
999 | } |
1000 | |
1001 | void RendererViewport::viewport_set_prev_camera_data(RID p_viewport, const RendererSceneRender::CameraData *p_camera_data) { |
1002 | Viewport *viewport = viewport_owner.get_or_null(p_viewport); |
1003 | ERR_FAIL_COND(!viewport); |
1004 | uint64_t frame = RSG::rasterizer->get_frame_number(); |
1005 | if (viewport->prev_camera_data_frame != frame) { |
1006 | viewport->prev_camera_data = *p_camera_data; |
1007 | viewport->prev_camera_data_frame = frame; |
1008 | } |
1009 | } |
1010 | |
1011 | const RendererSceneRender::CameraData *RendererViewport::viewport_get_prev_camera_data(RID p_viewport) { |
1012 | const Viewport *viewport = viewport_owner.get_or_null(p_viewport); |
1013 | ERR_FAIL_COND_V(!viewport, nullptr); |
1014 | return &viewport->prev_camera_data; |
1015 | } |
1016 | |
1017 | void RendererViewport::viewport_set_disable_2d(RID p_viewport, bool p_disable) { |
1018 | Viewport *viewport = viewport_owner.get_or_null(p_viewport); |
1019 | ERR_FAIL_COND(!viewport); |
1020 | |
1021 | viewport->disable_2d = p_disable; |
1022 | } |
1023 | |
1024 | void RendererViewport::viewport_set_environment_mode(RID p_viewport, RS::ViewportEnvironmentMode p_mode) { |
1025 | Viewport *viewport = viewport_owner.get_or_null(p_viewport); |
1026 | ERR_FAIL_COND(!viewport); |
1027 | |
1028 | viewport->disable_environment = p_mode; |
1029 | } |
1030 | |
1031 | bool RendererViewport::viewport_is_environment_disabled(Viewport *viewport) { |
1032 | ERR_FAIL_COND_V(!viewport, false); |
1033 | |
1034 | if (viewport->parent.is_valid() && viewport->disable_environment == RS::VIEWPORT_ENVIRONMENT_INHERIT) { |
1035 | Viewport *parent = viewport_owner.get_or_null(viewport->parent); |
1036 | return viewport_is_environment_disabled(parent); |
1037 | } |
1038 | return viewport->disable_environment == RS::VIEWPORT_ENVIRONMENT_DISABLED; |
1039 | } |
1040 | |
1041 | void RendererViewport::viewport_set_disable_3d(RID p_viewport, bool p_disable) { |
1042 | Viewport *viewport = viewport_owner.get_or_null(p_viewport); |
1043 | ERR_FAIL_COND(!viewport); |
1044 | |
1045 | viewport->disable_3d = p_disable; |
1046 | } |
1047 | |
1048 | void RendererViewport::viewport_attach_camera(RID p_viewport, RID p_camera) { |
1049 | Viewport *viewport = viewport_owner.get_or_null(p_viewport); |
1050 | ERR_FAIL_COND(!viewport); |
1051 | |
1052 | viewport->camera = p_camera; |
1053 | } |
1054 | |
1055 | void RendererViewport::viewport_set_scenario(RID p_viewport, RID p_scenario) { |
1056 | Viewport *viewport = viewport_owner.get_or_null(p_viewport); |
1057 | ERR_FAIL_COND(!viewport); |
1058 | |
1059 | if (viewport->scenario.is_valid()) { |
1060 | RSG::scene->scenario_remove_viewport_visibility_mask(viewport->scenario, p_viewport); |
1061 | } |
1062 | |
1063 | viewport->scenario = p_scenario; |
1064 | if (viewport->use_occlusion_culling) { |
1065 | RendererSceneOcclusionCull::get_singleton()->buffer_set_scenario(p_viewport, p_scenario); |
1066 | } |
1067 | } |
1068 | |
1069 | void RendererViewport::viewport_attach_canvas(RID p_viewport, RID p_canvas) { |
1070 | Viewport *viewport = viewport_owner.get_or_null(p_viewport); |
1071 | ERR_FAIL_COND(!viewport); |
1072 | |
1073 | ERR_FAIL_COND(viewport->canvas_map.has(p_canvas)); |
1074 | RendererCanvasCull::Canvas *canvas = RSG::canvas->canvas_owner.get_or_null(p_canvas); |
1075 | ERR_FAIL_COND(!canvas); |
1076 | |
1077 | canvas->viewports.insert(p_viewport); |
1078 | viewport->canvas_map[p_canvas] = Viewport::CanvasData(); |
1079 | viewport->canvas_map[p_canvas].layer = 0; |
1080 | viewport->canvas_map[p_canvas].sublayer = 0; |
1081 | viewport->canvas_map[p_canvas].canvas = canvas; |
1082 | } |
1083 | |
1084 | void RendererViewport::viewport_remove_canvas(RID p_viewport, RID p_canvas) { |
1085 | Viewport *viewport = viewport_owner.get_or_null(p_viewport); |
1086 | ERR_FAIL_COND(!viewport); |
1087 | |
1088 | RendererCanvasCull::Canvas *canvas = RSG::canvas->canvas_owner.get_or_null(p_canvas); |
1089 | ERR_FAIL_COND(!canvas); |
1090 | |
1091 | viewport->canvas_map.erase(p_canvas); |
1092 | canvas->viewports.erase(p_viewport); |
1093 | } |
1094 | |
1095 | void RendererViewport::viewport_set_canvas_transform(RID p_viewport, RID p_canvas, const Transform2D &p_offset) { |
1096 | Viewport *viewport = viewport_owner.get_or_null(p_viewport); |
1097 | ERR_FAIL_COND(!viewport); |
1098 | |
1099 | ERR_FAIL_COND(!viewport->canvas_map.has(p_canvas)); |
1100 | viewport->canvas_map[p_canvas].transform = p_offset; |
1101 | } |
1102 | |
1103 | void RendererViewport::viewport_set_transparent_background(RID p_viewport, bool p_enabled) { |
1104 | Viewport *viewport = viewport_owner.get_or_null(p_viewport); |
1105 | ERR_FAIL_COND(!viewport); |
1106 | |
1107 | RSG::texture_storage->render_target_set_transparent(viewport->render_target, p_enabled); |
1108 | viewport->transparent_bg = p_enabled; |
1109 | } |
1110 | |
1111 | void RendererViewport::viewport_set_global_canvas_transform(RID p_viewport, const Transform2D &p_transform) { |
1112 | Viewport *viewport = viewport_owner.get_or_null(p_viewport); |
1113 | ERR_FAIL_COND(!viewport); |
1114 | |
1115 | viewport->global_transform = p_transform; |
1116 | } |
1117 | |
1118 | void RendererViewport::viewport_set_canvas_stacking(RID p_viewport, RID p_canvas, int p_layer, int p_sublayer) { |
1119 | Viewport *viewport = viewport_owner.get_or_null(p_viewport); |
1120 | ERR_FAIL_COND(!viewport); |
1121 | |
1122 | ERR_FAIL_COND(!viewport->canvas_map.has(p_canvas)); |
1123 | viewport->canvas_map[p_canvas].layer = p_layer; |
1124 | viewport->canvas_map[p_canvas].sublayer = p_sublayer; |
1125 | } |
1126 | |
1127 | void RendererViewport::viewport_set_positional_shadow_atlas_size(RID p_viewport, int p_size, bool p_16_bits) { |
1128 | Viewport *viewport = viewport_owner.get_or_null(p_viewport); |
1129 | ERR_FAIL_COND(!viewport); |
1130 | |
1131 | viewport->shadow_atlas_size = p_size; |
1132 | viewport->shadow_atlas_16_bits = p_16_bits; |
1133 | |
1134 | RSG::light_storage->shadow_atlas_set_size(viewport->shadow_atlas, viewport->shadow_atlas_size, viewport->shadow_atlas_16_bits); |
1135 | } |
1136 | |
1137 | void RendererViewport::viewport_set_positional_shadow_atlas_quadrant_subdivision(RID p_viewport, int p_quadrant, int p_subdiv) { |
1138 | Viewport *viewport = viewport_owner.get_or_null(p_viewport); |
1139 | ERR_FAIL_COND(!viewport); |
1140 | |
1141 | RSG::light_storage->shadow_atlas_set_quadrant_subdivision(viewport->shadow_atlas, p_quadrant, p_subdiv); |
1142 | } |
1143 | |
1144 | void RendererViewport::viewport_set_msaa_2d(RID p_viewport, RS::ViewportMSAA p_msaa) { |
1145 | Viewport *viewport = viewport_owner.get_or_null(p_viewport); |
1146 | ERR_FAIL_COND(!viewport); |
1147 | |
1148 | if (viewport->msaa_2d == p_msaa) { |
1149 | return; |
1150 | } |
1151 | viewport->msaa_2d = p_msaa; |
1152 | RSG::texture_storage->render_target_set_msaa(viewport->render_target, p_msaa); |
1153 | } |
1154 | |
1155 | void RendererViewport::viewport_set_msaa_3d(RID p_viewport, RS::ViewportMSAA p_msaa) { |
1156 | Viewport *viewport = viewport_owner.get_or_null(p_viewport); |
1157 | ERR_FAIL_COND(!viewport); |
1158 | |
1159 | if (viewport->msaa_3d == p_msaa) { |
1160 | return; |
1161 | } |
1162 | viewport->msaa_3d = p_msaa; |
1163 | _configure_3d_render_buffers(viewport); |
1164 | } |
1165 | |
1166 | void RendererViewport::viewport_set_use_hdr_2d(RID p_viewport, bool p_use_hdr_2d) { |
1167 | Viewport *viewport = viewport_owner.get_or_null(p_viewport); |
1168 | ERR_FAIL_COND(!viewport); |
1169 | |
1170 | if (viewport->use_hdr_2d == p_use_hdr_2d) { |
1171 | return; |
1172 | } |
1173 | viewport->use_hdr_2d = p_use_hdr_2d; |
1174 | RSG::texture_storage->render_target_set_use_hdr(viewport->render_target, p_use_hdr_2d); |
1175 | } |
1176 | |
1177 | void RendererViewport::viewport_set_screen_space_aa(RID p_viewport, RS::ViewportScreenSpaceAA p_mode) { |
1178 | Viewport *viewport = viewport_owner.get_or_null(p_viewport); |
1179 | ERR_FAIL_COND(!viewport); |
1180 | |
1181 | if (viewport->screen_space_aa == p_mode) { |
1182 | return; |
1183 | } |
1184 | viewport->screen_space_aa = p_mode; |
1185 | _configure_3d_render_buffers(viewport); |
1186 | } |
1187 | |
1188 | void RendererViewport::viewport_set_use_taa(RID p_viewport, bool p_use_taa) { |
1189 | Viewport *viewport = viewport_owner.get_or_null(p_viewport); |
1190 | ERR_FAIL_COND(!viewport); |
1191 | ERR_FAIL_COND_EDMSG(OS::get_singleton()->get_current_rendering_method() != "forward_plus" , "TAA is only available when using the Forward+ renderer." ); |
1192 | |
1193 | if (viewport->use_taa == p_use_taa) { |
1194 | return; |
1195 | } |
1196 | viewport->use_taa = p_use_taa; |
1197 | num_viewports_with_motion_vectors += p_use_taa ? 1 : -1; |
1198 | _configure_3d_render_buffers(viewport); |
1199 | } |
1200 | |
1201 | void RendererViewport::viewport_set_use_debanding(RID p_viewport, bool p_use_debanding) { |
1202 | Viewport *viewport = viewport_owner.get_or_null(p_viewport); |
1203 | ERR_FAIL_COND(!viewport); |
1204 | |
1205 | if (viewport->use_debanding == p_use_debanding) { |
1206 | return; |
1207 | } |
1208 | viewport->use_debanding = p_use_debanding; |
1209 | if (viewport->render_buffers.is_valid()) { |
1210 | viewport->render_buffers->set_use_debanding(p_use_debanding); |
1211 | } |
1212 | } |
1213 | |
1214 | void RendererViewport::viewport_set_use_occlusion_culling(RID p_viewport, bool p_use_occlusion_culling) { |
1215 | Viewport *viewport = viewport_owner.get_or_null(p_viewport); |
1216 | ERR_FAIL_COND(!viewport); |
1217 | |
1218 | if (viewport->use_occlusion_culling == p_use_occlusion_culling) { |
1219 | return; |
1220 | } |
1221 | viewport->use_occlusion_culling = p_use_occlusion_culling; |
1222 | |
1223 | if (viewport->use_occlusion_culling) { |
1224 | RendererSceneOcclusionCull::get_singleton()->add_buffer(p_viewport); |
1225 | RendererSceneOcclusionCull::get_singleton()->buffer_set_scenario(p_viewport, viewport->scenario); |
1226 | } else { |
1227 | RendererSceneOcclusionCull::get_singleton()->remove_buffer(p_viewport); |
1228 | } |
1229 | |
1230 | viewport->occlusion_buffer_dirty = true; |
1231 | } |
1232 | |
1233 | void RendererViewport::viewport_set_occlusion_rays_per_thread(int p_rays_per_thread) { |
1234 | if (occlusion_rays_per_thread == p_rays_per_thread) { |
1235 | return; |
1236 | } |
1237 | |
1238 | occlusion_rays_per_thread = p_rays_per_thread; |
1239 | |
1240 | for (int i = 0; i < active_viewports.size(); i++) { |
1241 | active_viewports[i]->occlusion_buffer_dirty = true; |
1242 | } |
1243 | } |
1244 | |
1245 | void RendererViewport::viewport_set_occlusion_culling_build_quality(RS::ViewportOcclusionCullingBuildQuality p_quality) { |
1246 | RendererSceneOcclusionCull::get_singleton()->set_build_quality(p_quality); |
1247 | } |
1248 | |
1249 | void RendererViewport::viewport_set_mesh_lod_threshold(RID p_viewport, float p_pixels) { |
1250 | Viewport *viewport = viewport_owner.get_or_null(p_viewport); |
1251 | ERR_FAIL_COND(!viewport); |
1252 | |
1253 | viewport->mesh_lod_threshold = p_pixels; |
1254 | } |
1255 | |
1256 | int RendererViewport::viewport_get_render_info(RID p_viewport, RS::ViewportRenderInfoType p_type, RS::ViewportRenderInfo p_info) { |
1257 | ERR_FAIL_INDEX_V(p_type, RS::VIEWPORT_RENDER_INFO_TYPE_MAX, -1); |
1258 | ERR_FAIL_INDEX_V(p_info, RS::VIEWPORT_RENDER_INFO_MAX, -1); |
1259 | |
1260 | Viewport *viewport = viewport_owner.get_or_null(p_viewport); |
1261 | if (!viewport) { |
1262 | return 0; //there should be a lock here.. |
1263 | } |
1264 | |
1265 | return viewport->render_info.info[p_type][p_info]; |
1266 | } |
1267 | |
1268 | void RendererViewport::viewport_set_debug_draw(RID p_viewport, RS::ViewportDebugDraw p_draw) { |
1269 | Viewport *viewport = viewport_owner.get_or_null(p_viewport); |
1270 | ERR_FAIL_COND(!viewport); |
1271 | |
1272 | viewport->debug_draw = p_draw; |
1273 | } |
1274 | |
1275 | void RendererViewport::viewport_set_measure_render_time(RID p_viewport, bool p_enable) { |
1276 | Viewport *viewport = viewport_owner.get_or_null(p_viewport); |
1277 | ERR_FAIL_COND(!viewport); |
1278 | |
1279 | viewport->measure_render_time = p_enable; |
1280 | } |
1281 | |
1282 | float RendererViewport::viewport_get_measured_render_time_cpu(RID p_viewport) const { |
1283 | Viewport *viewport = viewport_owner.get_or_null(p_viewport); |
1284 | ERR_FAIL_COND_V(!viewport, 0); |
1285 | |
1286 | return double(viewport->time_cpu_end - viewport->time_cpu_begin) / 1000.0; |
1287 | } |
1288 | |
1289 | float RendererViewport::viewport_get_measured_render_time_gpu(RID p_viewport) const { |
1290 | Viewport *viewport = viewport_owner.get_or_null(p_viewport); |
1291 | ERR_FAIL_COND_V(!viewport, 0); |
1292 | |
1293 | return double((viewport->time_gpu_end - viewport->time_gpu_begin) / 1000) / 1000.0; |
1294 | } |
1295 | |
1296 | void RendererViewport::viewport_set_snap_2d_transforms_to_pixel(RID p_viewport, bool p_enabled) { |
1297 | Viewport *viewport = viewport_owner.get_or_null(p_viewport); |
1298 | ERR_FAIL_COND(!viewport); |
1299 | viewport->snap_2d_transforms_to_pixel = p_enabled; |
1300 | } |
1301 | |
1302 | void RendererViewport::viewport_set_snap_2d_vertices_to_pixel(RID p_viewport, bool p_enabled) { |
1303 | Viewport *viewport = viewport_owner.get_or_null(p_viewport); |
1304 | ERR_FAIL_COND(!viewport); |
1305 | viewport->snap_2d_vertices_to_pixel = p_enabled; |
1306 | } |
1307 | |
1308 | void RendererViewport::viewport_set_default_canvas_item_texture_filter(RID p_viewport, RS::CanvasItemTextureFilter p_filter) { |
1309 | ERR_FAIL_COND_MSG(p_filter == RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, "Viewport does not accept DEFAULT as texture filter (it's the topmost choice already).)" ); |
1310 | Viewport *viewport = viewport_owner.get_or_null(p_viewport); |
1311 | ERR_FAIL_COND(!viewport); |
1312 | |
1313 | viewport->texture_filter = p_filter; |
1314 | } |
1315 | void RendererViewport::viewport_set_default_canvas_item_texture_repeat(RID p_viewport, RS::CanvasItemTextureRepeat p_repeat) { |
1316 | ERR_FAIL_COND_MSG(p_repeat == RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT, "Viewport does not accept DEFAULT as texture repeat (it's the topmost choice already).)" ); |
1317 | Viewport *viewport = viewport_owner.get_or_null(p_viewport); |
1318 | ERR_FAIL_COND(!viewport); |
1319 | |
1320 | viewport->texture_repeat = p_repeat; |
1321 | } |
1322 | |
1323 | void RendererViewport::viewport_set_sdf_oversize_and_scale(RID p_viewport, RS::ViewportSDFOversize p_size, RS::ViewportSDFScale p_scale) { |
1324 | Viewport *viewport = viewport_owner.get_or_null(p_viewport); |
1325 | ERR_FAIL_COND(!viewport); |
1326 | |
1327 | RSG::texture_storage->render_target_set_sdf_size_and_scale(viewport->render_target, p_size, p_scale); |
1328 | } |
1329 | |
1330 | RID RendererViewport::viewport_find_from_screen_attachment(DisplayServer::WindowID p_id) const { |
1331 | RID *rids = nullptr; |
1332 | uint32_t rid_count = viewport_owner.get_rid_count(); |
1333 | rids = (RID *)alloca(sizeof(RID *) * rid_count); |
1334 | viewport_owner.fill_owned_buffer(rids); |
1335 | for (uint32_t i = 0; i < rid_count; i++) { |
1336 | Viewport *viewport = viewport_owner.get_or_null(rids[i]); |
1337 | if (viewport->viewport_to_screen == p_id) { |
1338 | return rids[i]; |
1339 | } |
1340 | } |
1341 | return RID(); |
1342 | } |
1343 | |
1344 | void RendererViewport::viewport_set_vrs_mode(RID p_viewport, RS::ViewportVRSMode p_mode) { |
1345 | Viewport *viewport = viewport_owner.get_or_null(p_viewport); |
1346 | ERR_FAIL_COND(!viewport); |
1347 | |
1348 | RSG::texture_storage->render_target_set_vrs_mode(viewport->render_target, p_mode); |
1349 | _configure_3d_render_buffers(viewport); |
1350 | } |
1351 | |
1352 | void RendererViewport::viewport_set_vrs_texture(RID p_viewport, RID p_texture) { |
1353 | Viewport *viewport = viewport_owner.get_or_null(p_viewport); |
1354 | ERR_FAIL_COND(!viewport); |
1355 | |
1356 | RSG::texture_storage->render_target_set_vrs_texture(viewport->render_target, p_texture); |
1357 | _configure_3d_render_buffers(viewport); |
1358 | } |
1359 | |
1360 | bool RendererViewport::free(RID p_rid) { |
1361 | if (viewport_owner.owns(p_rid)) { |
1362 | Viewport *viewport = viewport_owner.get_or_null(p_rid); |
1363 | |
1364 | RSG::texture_storage->render_target_free(viewport->render_target); |
1365 | RSG::light_storage->shadow_atlas_free(viewport->shadow_atlas); |
1366 | if (viewport->render_buffers.is_valid()) { |
1367 | viewport->render_buffers.unref(); |
1368 | } |
1369 | |
1370 | while (viewport->canvas_map.begin()) { |
1371 | viewport_remove_canvas(p_rid, viewport->canvas_map.begin()->key); |
1372 | } |
1373 | |
1374 | viewport_set_scenario(p_rid, RID()); |
1375 | active_viewports.erase(viewport); |
1376 | sorted_active_viewports_dirty = true; |
1377 | |
1378 | if (viewport->use_occlusion_culling) { |
1379 | RendererSceneOcclusionCull::get_singleton()->remove_buffer(p_rid); |
1380 | } |
1381 | |
1382 | if (viewport->use_taa) { |
1383 | num_viewports_with_motion_vectors--; |
1384 | } |
1385 | |
1386 | viewport_owner.free(p_rid); |
1387 | |
1388 | return true; |
1389 | } |
1390 | |
1391 | return false; |
1392 | } |
1393 | |
1394 | void RendererViewport::handle_timestamp(String p_timestamp, uint64_t p_cpu_time, uint64_t p_gpu_time) { |
1395 | RID *vp = timestamp_vp_map.getptr(p_timestamp); |
1396 | if (!vp) { |
1397 | return; |
1398 | } |
1399 | |
1400 | Viewport *viewport = viewport_owner.get_or_null(*vp); |
1401 | if (!viewport) { |
1402 | return; |
1403 | } |
1404 | |
1405 | if (p_timestamp.begins_with("vp_begin" )) { |
1406 | viewport->time_cpu_begin = p_cpu_time; |
1407 | viewport->time_gpu_begin = p_gpu_time; |
1408 | } |
1409 | |
1410 | if (p_timestamp.begins_with("vp_end" )) { |
1411 | viewport->time_cpu_end = p_cpu_time; |
1412 | viewport->time_gpu_end = p_gpu_time; |
1413 | } |
1414 | } |
1415 | |
1416 | void RendererViewport::set_default_clear_color(const Color &p_color) { |
1417 | RSG::texture_storage->set_default_clear_color(p_color); |
1418 | } |
1419 | |
1420 | void RendererViewport::viewport_set_canvas_cull_mask(RID p_viewport, uint32_t p_canvas_cull_mask) { |
1421 | Viewport *viewport = viewport_owner.get_or_null(p_viewport); |
1422 | ERR_FAIL_COND(!viewport); |
1423 | viewport->canvas_cull_mask = p_canvas_cull_mask; |
1424 | } |
1425 | |
1426 | // Workaround for setting this on thread. |
1427 | void RendererViewport::call_set_vsync_mode(DisplayServer::VSyncMode p_mode, DisplayServer::WindowID p_window) { |
1428 | DisplayServer::get_singleton()->window_set_vsync_mode(p_mode, p_window); |
1429 | } |
1430 | |
1431 | int RendererViewport::get_total_objects_drawn() const { |
1432 | return total_objects_drawn; |
1433 | } |
1434 | int RendererViewport::get_total_primitives_drawn() const { |
1435 | return total_vertices_drawn; |
1436 | } |
1437 | int RendererViewport::get_total_draw_calls_used() const { |
1438 | return total_draw_calls_used; |
1439 | } |
1440 | |
1441 | int RendererViewport::get_num_viewports_with_motion_vectors() const { |
1442 | return num_viewports_with_motion_vectors; |
1443 | } |
1444 | |
1445 | RendererViewport::RendererViewport() { |
1446 | occlusion_rays_per_thread = GLOBAL_GET("rendering/occlusion_culling/occlusion_rays_per_thread" ); |
1447 | } |
1448 | |