Path: blob/master/servers/rendering/renderer_viewport.cpp
21097 views
/**************************************************************************/1/* renderer_viewport.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "renderer_viewport.h"3132#include "core/config/project_settings.h"33#include "core/math/transform_interpolator.h"34#include "core/object/worker_thread_pool.h"35#include "core/profiling/profiling.h"36#include "renderer_canvas_cull.h"37#include "renderer_scene_cull.h"38#include "rendering_server_globals.h"39#include "storage/texture_storage.h"4041#ifndef XR_DISABLED42#include "servers/xr/xr_interface.h"43#endif // XR_DISABLED4445static Transform2D _canvas_get_transform(RendererViewport::Viewport *p_viewport, RendererCanvasCull::Canvas *p_canvas, RendererViewport::Viewport::CanvasData *p_canvas_data, const Vector2 &p_vp_size) {46Transform2D xf = p_viewport->global_transform;4748Vector2 pixel_snap_offset;49if (p_viewport->snap_2d_transforms_to_pixel) {50// We use `floor(p + 0.5)` to snap canvas items, but `ceil(p - 0.5)`51// to snap viewport transform because the viewport transform is inverse52// to the camera transform. Also, if the viewport size is not divisible53// by 2, the center point is offset by 0.5 px and we need to add 0.554// before rounding to cancel it out.55pixel_snap_offset.x = (p_viewport->size.width % 2) ? 0.0 : -0.5;56pixel_snap_offset.y = (p_viewport->size.height % 2) ? 0.0 : -0.5;57}5859float scale = 1.0;60if (p_viewport->canvas_map.has(p_canvas->parent)) {61Transform2D c_xform = p_viewport->canvas_map[p_canvas->parent].transform;62if (p_viewport->snap_2d_transforms_to_pixel) {63c_xform.columns[2] = (c_xform.columns[2] * p_canvas->parent_scale + pixel_snap_offset).ceil() / p_canvas->parent_scale;64}65xf = xf * c_xform;66scale = p_canvas->parent_scale;67}6869Transform2D c_xform = p_canvas_data->transform;70if (p_viewport->snap_2d_transforms_to_pixel) {71c_xform.columns[2] = (c_xform.columns[2] + pixel_snap_offset).ceil();72}73xf = xf * c_xform;7475if (scale != 1.0 && !RSG::canvas->disable_scale) {76Vector2 pivot = p_vp_size * 0.5;77Transform2D xfpivot;78xfpivot.set_origin(pivot);79Transform2D xfscale;80xfscale.scale(Vector2(scale, scale));8182xf = xfpivot.affine_inverse() * xf;83xf = xfscale * xf;84xf = xfpivot * xf;85}8687return xf;88}8990Vector<RendererViewport::Viewport *> RendererViewport::_sort_active_viewports() {91// We need to sort the viewports in a "topological order", children first and92// parents last. We also need to keep sibling viewports in the original order93// from top to bottom.9495Vector<Viewport *> result;96List<Viewport *> nodes;9798for (int i = active_viewports.size() - 1; i >= 0; --i) {99Viewport *viewport = active_viewports[i];100if (viewport->parent.is_valid()) {101continue;102}103104nodes.push_back(viewport);105result.insert(0, viewport);106}107108while (!nodes.is_empty()) {109const Viewport *node = nodes.front()->get();110nodes.pop_front();111112for (int i = active_viewports.size() - 1; i >= 0; --i) {113Viewport *child = active_viewports[i];114if (child->parent != node->self) {115continue;116}117118if (!nodes.find(child)) {119nodes.push_back(child);120result.insert(0, child);121}122}123}124125return result;126}127128void RendererViewport::_configure_3d_render_buffers(Viewport *p_viewport) {129if (p_viewport->render_buffers.is_valid()) {130if (p_viewport->size.width == 0 || p_viewport->size.height == 0) {131p_viewport->render_buffers.unref();132} else {133const float EPSILON = 0.0001;134float scaling_3d_scale = p_viewport->scaling_3d_scale;135RS::ViewportScaling3DMode scaling_3d_mode = p_viewport->scaling_3d_mode;136bool upscaler_available = p_viewport->fsr_enabled;137RS::ViewportScaling3DType scaling_type = RS::scaling_3d_mode_type(scaling_3d_mode);138139if ((!upscaler_available || (scaling_type == RS::VIEWPORT_SCALING_3D_TYPE_SPATIAL)) && scaling_3d_scale >= (1.0 - EPSILON) && scaling_3d_scale <= (1.0 + EPSILON)) {140// No 3D scaling for spatial modes? Ignore scaling mode, this just introduces overhead.141// - Mobile can't perform optimal path142// - FSR does an extra pass (or 2 extra passes if 2D-MSAA is enabled)143// Scaling = 1.0 on FSR2 and MetalFX temporal has benefits144scaling_3d_scale = 1.0;145scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_OFF;146}147148if (scaling_3d_mode != RS::VIEWPORT_SCALING_3D_MODE_OFF && scaling_3d_mode != RS::VIEWPORT_SCALING_3D_MODE_BILINEAR && OS::get_singleton()->get_current_rendering_method() == "gl_compatibility") {149scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_BILINEAR;150scaling_type = RS::scaling_3d_mode_type(scaling_3d_mode);151WARN_PRINT_ONCE("MetalFX and FSR upscaling are not supported in the Compatibility renderer. Falling back to bilinear scaling.");152}153154if (scaling_3d_mode == RS::VIEWPORT_SCALING_3D_MODE_METALFX_TEMPORAL && !RD::get_singleton()->has_feature(RD::SUPPORTS_METALFX_TEMPORAL)) {155if (RD::get_singleton()->has_feature(RD::SUPPORTS_METALFX_SPATIAL)) {156// Prefer MetalFX spatial if it is supported, which will be much more efficient than FSR2,157// as the hardware already will struggle with FSR2.158scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_METALFX_SPATIAL;159WARN_PRINT_ONCE("MetalFX temporal upscaling is not supported by the current renderer or hardware. Falling back to MetalFX Spatial scaling.");160} else {161scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_FSR2;162WARN_PRINT_ONCE("MetalFX upscaling is not supported by the current renderer or hardware. Falling back to FSR 2 scaling.");163}164scaling_type = RS::scaling_3d_mode_type(scaling_3d_mode);165}166167if (scaling_3d_mode == RS::VIEWPORT_SCALING_3D_MODE_METALFX_SPATIAL && !RD::get_singleton()->has_feature(RD::SUPPORTS_METALFX_SPATIAL)) {168scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_FSR;169WARN_PRINT_ONCE("MetalFX spatial upscaling is not supported by the current renderer or hardware. Falling back to FSR scaling.");170}171172RS::ViewportMSAA msaa_3d = p_viewport->msaa_3d;173174// If MetalFX Temporal upscaling is supported, verify limits.175if (scaling_3d_mode == RS::VIEWPORT_SCALING_3D_MODE_METALFX_TEMPORAL) {176double min_scale = (double)RD::get_singleton()->limit_get(RD::LIMIT_METALFX_TEMPORAL_SCALER_MIN_SCALE) / 1000'000.0;177double max_scale = (double)RD::get_singleton()->limit_get(RD::LIMIT_METALFX_TEMPORAL_SCALER_MAX_SCALE) / 1000'000.0;178if ((double)scaling_3d_scale < min_scale || (double)scaling_3d_scale > max_scale) {179scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_FSR2;180WARN_PRINT_ONCE(vformat("MetalFX temporal upscaling scale is outside limits; scale must be between %f and %f. Falling back to FSR 2 3D resolution scaling.", min_scale, max_scale));181} else if (msaa_3d != RS::VIEWPORT_MSAA_DISABLED) {182WARN_PRINT_ONCE("MetalFX temporal upscaling does not support 3D MSAA. Disabling 3D MSAA internally.");183msaa_3d = RS::VIEWPORT_MSAA_DISABLED;184}185}186187bool scaling_3d_is_not_bilinear = scaling_3d_mode != RS::VIEWPORT_SCALING_3D_MODE_OFF && scaling_3d_mode != RS::VIEWPORT_SCALING_3D_MODE_BILINEAR;188bool use_taa = p_viewport->use_taa;189190if (scaling_3d_is_not_bilinear && (scaling_3d_scale >= (1.0 + EPSILON))) {191// FSR and MetalFX is not designed for downsampling.192// Fall back to bilinear scaling.193WARN_PRINT_ONCE("FSR 3D resolution scaling is not designed for downsampling. Falling back to bilinear 3D resolution scaling.");194scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_BILINEAR;195scaling_type = RS::scaling_3d_mode_type(scaling_3d_mode);196}197198if (scaling_3d_is_not_bilinear && !upscaler_available) {199// FSR is not actually available.200// Fall back to bilinear scaling.201WARN_PRINT_ONCE("FSR 3D resolution scaling is not available. Falling back to bilinear 3D resolution scaling.");202scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_BILINEAR;203scaling_type = RS::scaling_3d_mode_type(scaling_3d_mode);204}205206if (use_taa && (scaling_type == RS::VIEWPORT_SCALING_3D_TYPE_TEMPORAL)) {207// Temporal upscalers can't be used with TAA.208// Turn it off and prefer using the temporal upscaler.209WARN_PRINT_ONCE("FSR 2 or MetalFX Temporal is not compatible with TAA. Disabling TAA internally.");210use_taa = false;211}212213int target_width;214int target_height;215int render_width;216int render_height;217218switch (scaling_3d_mode) {219case RS::VIEWPORT_SCALING_3D_MODE_BILINEAR:220// Clamp 3D rendering resolution to reasonable values supported on most hardware.221// This prevents freezing the engine or outright crashing on lower-end GPUs.222target_width = p_viewport->size.width;223target_height = p_viewport->size.height;224render_width = CLAMP(target_width * scaling_3d_scale, 1, 16384);225render_height = CLAMP(target_height * scaling_3d_scale, 1, 16384);226break;227case RS::VIEWPORT_SCALING_3D_MODE_METALFX_SPATIAL:228case RS::VIEWPORT_SCALING_3D_MODE_METALFX_TEMPORAL:229case RS::VIEWPORT_SCALING_3D_MODE_FSR:230case RS::VIEWPORT_SCALING_3D_MODE_FSR2:231target_width = p_viewport->size.width;232target_height = p_viewport->size.height;233render_width = MAX(target_width * scaling_3d_scale, 1.0); // target_width / (target_width * scaling)234render_height = MAX(target_height * scaling_3d_scale, 1.0);235break;236case RS::VIEWPORT_SCALING_3D_MODE_OFF:237target_width = p_viewport->size.width;238target_height = p_viewport->size.height;239render_width = target_width;240render_height = target_height;241break;242default:243// This is an unknown mode.244WARN_PRINT_ONCE(vformat("Unknown scaling mode: %d. Disabling 3D resolution scaling.", scaling_3d_mode));245scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_OFF;246scaling_3d_scale = 1.0;247target_width = p_viewport->size.width;248target_height = p_viewport->size.height;249render_width = target_width;250render_height = target_height;251break;252}253254uint32_t jitter_phase_count = 0;255if (scaling_type == RS::VIEWPORT_SCALING_3D_TYPE_TEMPORAL) {256// Implementation has been copied from ffxFsr2GetJitterPhaseCount.257// Also used for MetalFX Temporal scaling.258jitter_phase_count = uint32_t(8.0f * std::pow(float(target_width) / render_width, 2.0f));259} else if (use_taa) {260// Default jitter count for TAA.261jitter_phase_count = 16;262}263264p_viewport->internal_size = Size2(render_width, render_height);265p_viewport->jitter_phase_count = jitter_phase_count;266267// At resolution scales lower than 1.0, use negative texture mipmap bias268// to compensate for the loss of sharpness.269const float texture_mipmap_bias = std::log2(MIN(scaling_3d_scale, 1.0)) + p_viewport->texture_mipmap_bias;270271RenderSceneBuffersConfiguration rb_config;272rb_config.set_render_target(p_viewport->render_target);273rb_config.set_internal_size(Size2i(render_width, render_height));274rb_config.set_target_size(Size2(target_width, target_height));275rb_config.set_view_count(p_viewport->view_count);276rb_config.set_scaling_3d_mode(scaling_3d_mode);277rb_config.set_msaa_3d(msaa_3d);278rb_config.set_screen_space_aa(p_viewport->screen_space_aa);279rb_config.set_fsr_sharpness(p_viewport->fsr_sharpness);280rb_config.set_texture_mipmap_bias(texture_mipmap_bias);281rb_config.set_anisotropic_filtering_level(p_viewport->anisotropic_filtering_level);282rb_config.set_use_taa(use_taa);283rb_config.set_use_debanding(p_viewport->use_debanding);284285p_viewport->render_buffers->configure(&rb_config);286}287}288}289290void RendererViewport::_draw_3d(Viewport *p_viewport) {291#ifndef _3D_DISABLED292RENDER_TIMESTAMP("> Render 3D Scene");293294Ref<XRInterface> xr_interface;295#ifndef XR_DISABLED296if (p_viewport->use_xr && XRServer::get_singleton() != nullptr) {297xr_interface = XRServer::get_singleton()->get_primary_interface();298}299#endif // XR_DISABLED300301if (p_viewport->use_occlusion_culling) {302if (p_viewport->occlusion_buffer_dirty) {303float aspect = p_viewport->size.aspect();304int max_size = occlusion_rays_per_thread * WorkerThreadPool::get_singleton()->get_thread_count();305306int viewport_size = p_viewport->size.width * p_viewport->size.height;307max_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.308309float height = Math::sqrt(max_size / aspect);310Size2i new_size = Size2i(height * aspect, height);311RendererSceneOcclusionCull::get_singleton()->buffer_set_size(p_viewport->self, new_size);312p_viewport->occlusion_buffer_dirty = false;313}314}315316float screen_mesh_lod_threshold = p_viewport->mesh_lod_threshold / float(p_viewport->size.width);317RSG::scene->render_camera(p_viewport->render_buffers, p_viewport->camera, p_viewport->scenario, p_viewport->self, p_viewport->internal_size, p_viewport->jitter_phase_count, screen_mesh_lod_threshold, p_viewport->shadow_atlas, xr_interface, &p_viewport->render_info);318319RENDER_TIMESTAMP("< Render 3D Scene");320#endif // _3D_DISABLED321}322323void RendererViewport::_draw_viewport(Viewport *p_viewport) {324if (p_viewport->measure_render_time) {325String rt_id = "vp_begin_" + itos(p_viewport->self.get_id());326RSG::utilities->capture_timestamp(rt_id);327timestamp_vp_map[rt_id] = p_viewport->self;328}329330if (OS::get_singleton()->get_current_rendering_method() == "gl_compatibility") {331// This is currently needed for GLES to keep the current window being rendered to up to date332DisplayServer::get_singleton()->gl_window_make_current(p_viewport->viewport_to_screen);333}334335/* Camera should always be BEFORE any other 3D */336337bool can_draw_2d = !p_viewport->disable_2d && p_viewport->view_count == 1; // Stereo rendering does not support 2D, no depth data338bool scenario_draw_canvas_bg = false; //draw canvas, or some layer of it, as BG for 3D instead of in front339int scenario_canvas_max_layer = 0;340bool force_clear_render_target = false;341342for (int i = 0; i < RS::VIEWPORT_RENDER_INFO_TYPE_MAX; i++) {343for (int j = 0; j < RS::VIEWPORT_RENDER_INFO_MAX; j++) {344p_viewport->render_info.info[i][j] = 0;345}346}347348if (RSG::scene->is_scenario(p_viewport->scenario)) {349RID environment = RSG::scene->scenario_get_environment(p_viewport->scenario);350if (RSG::scene->is_environment(environment)) {351if (can_draw_2d && !viewport_is_environment_disabled(p_viewport)) {352scenario_draw_canvas_bg = RSG::scene->environment_get_background(environment) == RS::ENV_BG_CANVAS;353scenario_canvas_max_layer = RSG::scene->environment_get_canvas_max_layer(environment);354} else if (RSG::scene->environment_get_background(environment) == RS::ENV_BG_CANVAS) {355// The scene renderer will still copy over the last frame, so we need to clear the render target.356force_clear_render_target = true;357}358}359}360361bool can_draw_3d = RSG::scene->is_camera(p_viewport->camera) && !p_viewport->disable_3d;362363if ((scenario_draw_canvas_bg || can_draw_3d) && !p_viewport->render_buffers.is_valid()) {364//wants to draw 3D but there is no render buffer, create365p_viewport->render_buffers = RSG::scene->render_buffers_create();366367_configure_3d_render_buffers(p_viewport);368}369370Color bgcolor = p_viewport->transparent_bg ? Color(0, 0, 0, 0) : RSG::texture_storage->get_default_clear_color();371372if (p_viewport->clear_mode != RS::VIEWPORT_CLEAR_NEVER) {373RSG::texture_storage->render_target_request_clear(p_viewport->render_target, bgcolor);374if (p_viewport->clear_mode == RS::VIEWPORT_CLEAR_ONLY_NEXT_FRAME) {375p_viewport->clear_mode = RS::VIEWPORT_CLEAR_NEVER;376}377}378379if (!scenario_draw_canvas_bg && can_draw_3d) {380if (force_clear_render_target) {381RSG::texture_storage->render_target_do_clear_request(p_viewport->render_target);382}383_draw_3d(p_viewport);384}385386if (can_draw_2d) {387RBMap<Viewport::CanvasKey, Viewport::CanvasData *> canvas_map;388389Rect2 clip_rect(0, 0, p_viewport->size.x, p_viewport->size.y);390RendererCanvasRender::Light *lights = nullptr;391RendererCanvasRender::Light *lights_with_shadow = nullptr;392393RendererCanvasRender::Light *directional_lights = nullptr;394RendererCanvasRender::Light *directional_lights_with_shadow = nullptr;395396if (p_viewport->sdf_active) {397// Process SDF.398399Rect2 sdf_rect = RSG::texture_storage->render_target_get_sdf_rect(p_viewport->render_target);400401RendererCanvasRender::LightOccluderInstance *occluders = nullptr;402403// Make list of occluders.404for (KeyValue<RID, Viewport::CanvasData> &E : p_viewport->canvas_map) {405RendererCanvasCull::Canvas *canvas = static_cast<RendererCanvasCull::Canvas *>(E.value.canvas);406Transform2D xf = _canvas_get_transform(p_viewport, canvas, &E.value, clip_rect.size);407408for (RendererCanvasRender::LightOccluderInstance *F : canvas->occluders) {409if (!F->enabled) {410continue;411}412413if (!RSG::canvas->_interpolation_data.interpolation_enabled || !F->interpolated) {414F->xform_cache = xf * F->xform_curr;415} else {416real_t f = Engine::get_singleton()->get_physics_interpolation_fraction();417TransformInterpolator::interpolate_transform_2d(F->xform_prev, F->xform_curr, F->xform_cache, f);418F->xform_cache = xf * F->xform_cache;419}420421if (sdf_rect.intersects_transformed(F->xform_cache, F->aabb_cache)) {422F->next = occluders;423occluders = F;424}425}426}427428RSG::canvas_render->render_sdf(p_viewport->render_target, occluders);429RSG::texture_storage->render_target_mark_sdf_enabled(p_viewport->render_target, true);430431p_viewport->sdf_active = false; // If used, gets set active again.432} else {433RSG::texture_storage->render_target_mark_sdf_enabled(p_viewport->render_target, false);434}435436Rect2 shadow_rect;437438int shadow_count = 0;439int directional_light_count = 0;440441RENDER_TIMESTAMP("Cull 2D Lights");442for (KeyValue<RID, Viewport::CanvasData> &E : p_viewport->canvas_map) {443RendererCanvasCull::Canvas *canvas = static_cast<RendererCanvasCull::Canvas *>(E.value.canvas);444445Transform2D xf = _canvas_get_transform(p_viewport, canvas, &E.value, clip_rect.size);446447// Find lights in canvas.448449for (RendererCanvasRender::Light *F : canvas->lights) {450RendererCanvasRender::Light *cl = F;451if (cl->enabled && cl->texture.is_valid()) {452//not super efficient..453Size2 tsize = RSG::texture_storage->texture_size_with_proxy(cl->texture);454tsize *= cl->scale;455456Vector2 offset = tsize / 2.0;457Rect2 local_rect = Rect2(-offset + cl->texture_offset, tsize);458459if (!RSG::canvas->_interpolation_data.interpolation_enabled || !cl->interpolated) {460cl->xform_cache = xf * cl->xform_curr;461} else {462real_t f = Engine::get_singleton()->get_physics_interpolation_fraction();463TransformInterpolator::interpolate_transform_2d(cl->xform_prev, cl->xform_curr, cl->xform_cache, f);464cl->xform_cache = xf * cl->xform_cache;465}466467cl->rect_cache = cl->xform_cache.xform(local_rect);468469if (clip_rect.intersects(cl->rect_cache)) {470cl->filter_next_ptr = lights;471lights = cl;472Transform2D scale;473scale.scale(local_rect.size);474scale.columns[2] = local_rect.position;475cl->light_shader_xform = cl->xform_cache * scale;476if (cl->use_shadow) {477cl->shadows_next_ptr = lights_with_shadow;478if (lights_with_shadow == nullptr) {479shadow_rect = cl->rect_cache;480} else {481shadow_rect = shadow_rect.merge(cl->rect_cache);482}483lights_with_shadow = cl;484cl->radius_cache = local_rect.size.length();485}486}487}488}489490for (RendererCanvasRender::Light *F : canvas->directional_lights) {491RendererCanvasRender::Light *cl = F;492if (cl->enabled) {493cl->filter_next_ptr = directional_lights;494directional_lights = cl;495if (!RSG::canvas->_interpolation_data.interpolation_enabled || !cl->interpolated) {496cl->xform_cache = xf * cl->xform_curr;497} else {498real_t f = Engine::get_singleton()->get_physics_interpolation_fraction();499TransformInterpolator::interpolate_transform_2d(cl->xform_prev, cl->xform_curr, cl->xform_cache, f);500cl->xform_cache = xf * cl->xform_cache;501}502cl->xform_cache.columns[2] = Vector2(); //translation is pointless503if (cl->use_shadow) {504cl->shadows_next_ptr = directional_lights_with_shadow;505directional_lights_with_shadow = cl;506}507508directional_light_count++;509510if (directional_light_count == RS::MAX_2D_DIRECTIONAL_LIGHTS) {511break;512}513}514}515516canvas_map[Viewport::CanvasKey(E.key, E.value.layer, E.value.sublayer)] = &E.value;517}518519if (lights_with_shadow) {520//update shadows if any521522RendererCanvasRender::LightOccluderInstance *occluders = nullptr;523524RENDER_TIMESTAMP("> Render PointLight2D Shadows");525RENDER_TIMESTAMP("Cull LightOccluder2Ds");526527//make list of occluders528for (KeyValue<RID, Viewport::CanvasData> &E : p_viewport->canvas_map) {529RendererCanvasCull::Canvas *canvas = static_cast<RendererCanvasCull::Canvas *>(E.value.canvas);530Transform2D xf = _canvas_get_transform(p_viewport, canvas, &E.value, clip_rect.size);531532for (RendererCanvasRender::LightOccluderInstance *F : canvas->occluders) {533if (!F->enabled) {534continue;535}536if (!RSG::canvas->_interpolation_data.interpolation_enabled || !F->interpolated) {537F->xform_cache = xf * F->xform_curr;538} else {539real_t f = Engine::get_singleton()->get_physics_interpolation_fraction();540TransformInterpolator::interpolate_transform_2d(F->xform_prev, F->xform_curr, F->xform_cache, f);541F->xform_cache = xf * F->xform_cache;542}543if (shadow_rect.intersects_transformed(F->xform_cache, F->aabb_cache)) {544F->next = occluders;545occluders = F;546}547}548}549//update the light shadowmaps with them550551RendererCanvasRender::Light *light = lights_with_shadow;552while (light) {553RENDER_TIMESTAMP("Render PointLight2D Shadow");554555RSG::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, light->rect_cache);556light = light->shadows_next_ptr;557}558559RENDER_TIMESTAMP("< Render PointLight2D Shadows");560}561562if (directional_lights_with_shadow) {563//update shadows if any564RendererCanvasRender::Light *light = directional_lights_with_shadow;565while (light) {566Vector2 light_dir = -light->xform_cache.columns[1].normalized(); // Y is light direction567float cull_distance = light->directional_distance;568569Vector2 light_dir_sign;570light_dir_sign.x = (Math::abs(light_dir.x) < CMP_EPSILON) ? 0.0 : ((light_dir.x > 0.0) ? 1.0 : -1.0);571light_dir_sign.y = (Math::abs(light_dir.y) < CMP_EPSILON) ? 0.0 : ((light_dir.y > 0.0) ? 1.0 : -1.0);572573Vector2 points[6];574int point_count = 0;575576for (int j = 0; j < 4; j++) {577static const Vector2 signs[4] = { Vector2(1, 1), Vector2(1, 0), Vector2(0, 0), Vector2(0, 1) };578Vector2 sign_cmp = signs[j] * 2.0 - Vector2(1.0, 1.0);579Vector2 point = clip_rect.position + clip_rect.size * signs[j];580581if (sign_cmp == light_dir_sign) {582//both point in same direction, plot offsetted583points[point_count++] = point + light_dir * cull_distance;584} else if (sign_cmp.x == light_dir_sign.x || sign_cmp.y == light_dir_sign.y) {585int next_j = (j + 1) % 4;586Vector2 next_sign_cmp = signs[next_j] * 2.0 - Vector2(1.0, 1.0);587588//one point in the same direction, plot segment589590if (next_sign_cmp.x == light_dir_sign.x || next_sign_cmp.y == light_dir_sign.y) {591if (light_dir_sign.x != 0.0 || light_dir_sign.y != 0.0) {592points[point_count++] = point;593}594points[point_count++] = point + light_dir * cull_distance;595} else {596points[point_count++] = point + light_dir * cull_distance;597if (light_dir_sign.x != 0.0 || light_dir_sign.y != 0.0) {598points[point_count++] = point;599}600}601} else {602//plot normally603points[point_count++] = point;604}605}606607Vector2 xf_points[6];608609RendererCanvasRender::LightOccluderInstance *occluders = nullptr;610611RENDER_TIMESTAMP("> Render DirectionalLight2D Shadows");612613// Make list of occluders.614for (KeyValue<RID, Viewport::CanvasData> &E : p_viewport->canvas_map) {615RendererCanvasCull::Canvas *canvas = static_cast<RendererCanvasCull::Canvas *>(E.value.canvas);616Transform2D xf = _canvas_get_transform(p_viewport, canvas, &E.value, clip_rect.size);617618for (RendererCanvasRender::LightOccluderInstance *F : canvas->occluders) {619if (!F->enabled) {620continue;621}622if (!RSG::canvas->_interpolation_data.interpolation_enabled || !F->interpolated) {623F->xform_cache = xf * F->xform_curr;624} else {625real_t f = Engine::get_singleton()->get_physics_interpolation_fraction();626TransformInterpolator::interpolate_transform_2d(F->xform_prev, F->xform_curr, F->xform_cache, f);627F->xform_cache = xf * F->xform_cache;628}629Transform2D localizer = F->xform_cache.affine_inverse();630631for (int j = 0; j < point_count; j++) {632xf_points[j] = localizer.xform(points[j]);633}634if (F->aabb_cache.intersects_filled_polygon(xf_points, point_count)) {635F->next = occluders;636occluders = F;637}638}639}640641RSG::canvas_render->light_update_directional_shadow(light->light_internal, shadow_count++, light->xform_cache, light->item_shadow_mask, cull_distance, clip_rect, occluders);642643light = light->shadows_next_ptr;644}645646RENDER_TIMESTAMP("< Render DirectionalLight2D Shadows");647}648649if (scenario_draw_canvas_bg && canvas_map.begin() && canvas_map.begin()->key.get_layer() > scenario_canvas_max_layer) {650// There may be an outstanding clear request if a clear was requested, but no 2D elements were drawn.651// Clear now otherwise we copy over garbage from the render target.652RSG::texture_storage->render_target_do_clear_request(p_viewport->render_target);653if (!can_draw_3d) {654RSG::scene->render_empty_scene(p_viewport->render_buffers, p_viewport->scenario, p_viewport->shadow_atlas);655} else {656_draw_3d(p_viewport);657}658scenario_draw_canvas_bg = false;659}660661for (const KeyValue<Viewport::CanvasKey, Viewport::CanvasData *> &E : canvas_map) {662RendererCanvasCull::Canvas *canvas = static_cast<RendererCanvasCull::Canvas *>(E.value->canvas);663664Transform2D xform = _canvas_get_transform(p_viewport, canvas, E.value, clip_rect.size);665666RendererCanvasRender::Light *canvas_lights = nullptr;667RendererCanvasRender::Light *canvas_directional_lights = nullptr;668669RendererCanvasRender::Light *ptr = lights;670while (ptr) {671if (E.value->layer >= ptr->layer_min && E.value->layer <= ptr->layer_max) {672ptr->next_ptr = canvas_lights;673canvas_lights = ptr;674}675ptr = ptr->filter_next_ptr;676}677678ptr = directional_lights;679while (ptr) {680if (E.value->layer >= ptr->layer_min && E.value->layer <= ptr->layer_max) {681ptr->next_ptr = canvas_directional_lights;682canvas_directional_lights = ptr;683}684ptr = ptr->filter_next_ptr;685}686687RSG::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, &p_viewport->render_info);688if (RSG::canvas->was_sdf_used()) {689p_viewport->sdf_active = true;690}691692if (scenario_draw_canvas_bg && E.key.get_layer() >= scenario_canvas_max_layer) {693// There may be an outstanding clear request if a clear was requested, but no 2D elements were drawn.694// Clear now otherwise we copy over garbage from the render target.695RSG::texture_storage->render_target_do_clear_request(p_viewport->render_target);696if (!can_draw_3d) {697RSG::scene->render_empty_scene(p_viewport->render_buffers, p_viewport->scenario, p_viewport->shadow_atlas);698} else {699_draw_3d(p_viewport);700}701702scenario_draw_canvas_bg = false;703}704}705706if (scenario_draw_canvas_bg) {707// There may be an outstanding clear request if a clear was requested, but no 2D elements were drawn.708// Clear now otherwise we copy over garbage from the render target.709RSG::texture_storage->render_target_do_clear_request(p_viewport->render_target);710if (!can_draw_3d) {711RSG::scene->render_empty_scene(p_viewport->render_buffers, p_viewport->scenario, p_viewport->shadow_atlas);712} else {713_draw_3d(p_viewport);714}715}716}717718if (RSG::texture_storage->render_target_is_clear_requested(p_viewport->render_target)) {719//was never cleared in the end, force clear it720RSG::texture_storage->render_target_do_clear_request(p_viewport->render_target);721}722723if (RSG::texture_storage->render_target_get_msaa_needs_resolve(p_viewport->render_target)) {724WARN_PRINT_ONCE("2D MSAA is enabled while there is no 2D content. Disable 2D MSAA for better performance.");725RSG::texture_storage->render_target_do_msaa_resolve(p_viewport->render_target);726}727728if (p_viewport->measure_render_time) {729String rt_id = "vp_end_" + itos(p_viewport->self.get_id());730RSG::utilities->capture_timestamp(rt_id);731timestamp_vp_map[rt_id] = p_viewport->self;732}733}734735void RendererViewport::draw_viewports(bool p_swap_buffers) {736GodotProfileZoneGroupedFirst(_profile_zone, "prepare viewports");737timestamp_vp_map.clear();738739#ifndef XR_DISABLED740// get our xr interface in case we need it741Ref<XRInterface> xr_interface;742XRServer *xr_server = XRServer::get_singleton();743if (xr_server != nullptr) {744// retrieve the interface responsible for rendering745xr_interface = xr_server->get_primary_interface();746}747#endif // XR_DISABLED748749if (Engine::get_singleton()->is_editor_hint()) {750RSG::texture_storage->set_default_clear_color(GLOBAL_GET_CACHED(Color, "rendering/environment/defaults/default_clear_color"));751}752753if (sorted_active_viewports_dirty) {754GodotProfileZoneGrouped(_profile_zone, "_sort_active_viewports");755sorted_active_viewports = _sort_active_viewports();756sorted_active_viewports_dirty = false;757}758759HashMap<DisplayServer::WindowID, Vector<BlitToScreen>> blit_to_screen_list;760//draw viewports761RENDER_TIMESTAMP("> Render Viewports");762763GodotProfileZoneGrouped(_profile_zone, "render viewports");764765//determine what is visible766draw_viewports_pass++;767768for (int i = sorted_active_viewports.size() - 1; i >= 0; i--) { //to compute parent dependency, must go in reverse draw order769Viewport *vp = sorted_active_viewports[i];770771if (vp->update_mode == RS::VIEWPORT_UPDATE_DISABLED) {772continue;773}774775if (!vp->render_target.is_valid()) {776continue;777}778//ERR_CONTINUE(!vp->render_target.is_valid());779780bool visible = vp->viewport_to_screen_rect != Rect2();781782#ifndef XR_DISABLED783if (vp->use_xr) {784if (xr_interface.is_valid()) {785// Ignore update mode we have to commit frames to our XR interface786visible = true;787788// Override our size, make sure it matches our required size and is created as a stereo target789Size2 xr_size = xr_interface->get_render_target_size();790_viewport_set_size(vp, xr_size.width, xr_size.height, xr_interface->get_view_count());791} else {792// don't render anything793visible = false;794vp->size = Size2();795}796} else797#endif // XR_DISABLED798{799if (vp->update_mode == RS::VIEWPORT_UPDATE_ALWAYS || vp->update_mode == RS::VIEWPORT_UPDATE_ONCE) {800visible = true;801}802803if (vp->update_mode == RS::VIEWPORT_UPDATE_WHEN_VISIBLE && RSG::texture_storage->render_target_was_used(vp->render_target)) {804visible = true;805}806807if (vp->update_mode == RS::VIEWPORT_UPDATE_WHEN_PARENT_VISIBLE) {808Viewport *parent = viewport_owner.get_or_null(vp->parent);809if (parent && parent->last_pass == draw_viewports_pass) {810visible = true;811}812}813}814815visible = visible && vp->size.x > 1 && vp->size.y > 1;816817if (visible) {818vp->last_pass = draw_viewports_pass;819}820}821822int vertices_drawn = 0;823int objects_drawn = 0;824int draw_calls_used = 0;825826for (int i = 0; i < sorted_active_viewports.size(); i++) {827// TODO Somehow print the index828GodotProfileZone("render viewport");829830Viewport *vp = sorted_active_viewports[i];831832if (vp->last_pass != draw_viewports_pass) {833continue; //should not draw834}835836RENDER_TIMESTAMP("> Render Viewport " + itos(i));837838RSG::texture_storage->render_target_set_as_unused(vp->render_target);839#ifndef XR_DISABLED840if (vp->use_xr && xr_interface.is_valid()) {841// Inform XR interface we're about to render its viewport,842// if this returns false we don't render.843// This usually is a result of the player taking off their headset and OpenXR telling us to skip844// rendering frames.845if (xr_interface->pre_draw_viewport(vp->render_target)) {846RSG::texture_storage->render_target_set_override(vp->render_target,847xr_interface->get_color_texture(),848xr_interface->get_depth_texture(),849xr_interface->get_velocity_texture(),850xr_interface->get_velocity_depth_texture());851852RSG::texture_storage->render_target_set_velocity_target_size(vp->render_target, xr_interface->get_velocity_target_size());853854if (xr_interface->get_velocity_texture().is_valid()) {855_viewport_set_force_motion_vectors(vp, true);856} else {857_viewport_set_force_motion_vectors(vp, false);858}859860RSG::texture_storage->render_target_set_render_region(vp->render_target, xr_interface->get_render_region());861862// render...863RSG::scene->set_debug_draw_mode(vp->debug_draw);864865// and draw viewport866_draw_viewport(vp);867868// commit our eyes869Vector<BlitToScreen> blits = xr_interface->post_draw_viewport(vp->render_target, vp->viewport_to_screen_rect);870if (vp->viewport_to_screen != DisplayServer::INVALID_WINDOW_ID) {871if (RSG::rasterizer->is_opengl()) {872if (blits.size() > 0) {873RSG::rasterizer->blit_render_targets_to_screen(vp->viewport_to_screen, blits.ptr(), blits.size());874RSG::rasterizer->gl_end_frame(p_swap_buffers);875}876} else if (blits.size() > 0) {877if (!blit_to_screen_list.has(vp->viewport_to_screen)) {878blit_to_screen_list[vp->viewport_to_screen] = Vector<BlitToScreen>();879}880881for (int b = 0; b < blits.size(); b++) {882blit_to_screen_list[vp->viewport_to_screen].push_back(blits[b]);883}884}885}886}887} else888#endif // XR_DISABLED889{890RSG::scene->set_debug_draw_mode(vp->debug_draw);891892// render standard mono camera893_draw_viewport(vp);894895if (vp->viewport_to_screen != DisplayServer::INVALID_WINDOW_ID && (!vp->viewport_render_direct_to_screen || !RSG::rasterizer->is_low_end())) {896//copy to screen if set as such897BlitToScreen blit;898blit.render_target = vp->render_target;899if (vp->viewport_to_screen_rect != Rect2()) {900blit.dst_rect = vp->viewport_to_screen_rect;901} else {902blit.dst_rect.position = Vector2();903blit.dst_rect.size = vp->size;904}905906if (RSG::rasterizer->is_opengl()) {907RSG::rasterizer->blit_render_targets_to_screen(vp->viewport_to_screen, &blit, 1);908RSG::rasterizer->gl_end_frame(p_swap_buffers);909} else {910Vector<BlitToScreen> *blits = blit_to_screen_list.getptr(vp->viewport_to_screen);911if (blits == nullptr) {912blits = &blit_to_screen_list.insert(vp->viewport_to_screen, Vector<BlitToScreen>())->value;913}914blits->push_back(blit);915}916}917}918919if (vp->update_mode == RS::VIEWPORT_UPDATE_ONCE) {920vp->update_mode = RS::VIEWPORT_UPDATE_DISABLED;921}922923RENDER_TIMESTAMP("< Render Viewport " + itos(i));924925// 3D render info.926objects_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];927vertices_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];928draw_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];929// 2D render info.930objects_drawn += vp->render_info.info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME];931vertices_drawn += vp->render_info.info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_PRIMITIVES_IN_FRAME];932draw_calls_used += vp->render_info.info[RS::VIEWPORT_RENDER_INFO_TYPE_CANVAS][RS::VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME];933}934935RSG::scene->set_debug_draw_mode(RS::VIEWPORT_DEBUG_DRAW_DISABLED);936937total_objects_drawn = objects_drawn;938total_vertices_drawn = vertices_drawn;939total_draw_calls_used = draw_calls_used;940941RENDER_TIMESTAMP("< Render Viewports");942943GodotProfileZoneGrouped(_profile_zone, "rasterizer->blit_render_targets_to_screen");944if (p_swap_buffers && !blit_to_screen_list.is_empty()) {945for (const KeyValue<int, Vector<BlitToScreen>> &E : blit_to_screen_list) {946RSG::rasterizer->blit_render_targets_to_screen(E.key, E.value.ptr(), E.value.size());947}948}949}950951RID RendererViewport::viewport_allocate() {952return viewport_owner.allocate_rid();953}954955void RendererViewport::viewport_initialize(RID p_rid) {956viewport_owner.initialize_rid(p_rid);957Viewport *viewport = viewport_owner.get_or_null(p_rid);958viewport->self = p_rid;959viewport->render_target = RSG::texture_storage->render_target_create();960viewport->shadow_atlas = RSG::light_storage->shadow_atlas_create();961viewport->viewport_render_direct_to_screen = false;962963viewport->fsr_enabled = !RSG::rasterizer->is_low_end() && !viewport->disable_3d;964}965966#ifndef XR_DISABLED967void RendererViewport::viewport_set_use_xr(RID p_viewport, bool p_use_xr) {968Viewport *viewport = viewport_owner.get_or_null(p_viewport);969ERR_FAIL_NULL(viewport);970971if (viewport->use_xr == p_use_xr) {972return;973}974975viewport->use_xr = p_use_xr;976977// Re-configure the 3D render buffers when disabling XR. They'll get978// re-configured when enabling XR in draw_viewports().979if (!p_use_xr) {980viewport->view_count = 1;981_configure_3d_render_buffers(viewport);982}983}984#endif // !XR_DISABLED985986void RendererViewport::viewport_set_scaling_3d_mode(RID p_viewport, RS::ViewportScaling3DMode p_mode) {987Viewport *viewport = viewport_owner.get_or_null(p_viewport);988ERR_FAIL_NULL(viewport);989#ifdef DEBUG_ENABLED990const String rendering_method = OS::get_singleton()->get_current_rendering_method();991if (rendering_method != "forward_plus") {992if (p_mode == RS::VIEWPORT_SCALING_3D_MODE_FSR) {993WARN_PRINT_ONCE_ED("FSR1 3D scaling is only available when using the Forward+ renderer.");994}995if (p_mode == RS::VIEWPORT_SCALING_3D_MODE_FSR2) {996WARN_PRINT_ONCE_ED("FSR2 3D scaling is only available when using the Forward+ renderer.");997}998if (p_mode == RS::VIEWPORT_SCALING_3D_MODE_METALFX_TEMPORAL) {999WARN_PRINT_ONCE_ED("MetalFX Temporal 3D scaling is only available when using the Forward+ renderer.");1000}1001}1002if (rendering_method == "gl_compatibility" && p_mode == RS::VIEWPORT_SCALING_3D_MODE_METALFX_SPATIAL) {1003WARN_PRINT_ONCE_ED("MetalFX Spatial 3D scaling is only available when using the Forward+ or Mobile renderer.");1004}1005#endif10061007if (viewport->scaling_3d_mode == p_mode) {1008return;1009}10101011bool motion_vectors_before = _viewport_requires_motion_vectors(viewport);1012viewport->scaling_3d_mode = p_mode;10131014bool motion_vectors_after = _viewport_requires_motion_vectors(viewport);1015if (motion_vectors_before != motion_vectors_after) {1016num_viewports_with_motion_vectors += motion_vectors_after ? 1 : -1;1017}10181019_configure_3d_render_buffers(viewport);1020}10211022void RendererViewport::viewport_set_fsr_sharpness(RID p_viewport, float p_sharpness) {1023Viewport *viewport = viewport_owner.get_or_null(p_viewport);1024ERR_FAIL_NULL(viewport);10251026viewport->fsr_sharpness = p_sharpness;1027_configure_3d_render_buffers(viewport);1028}10291030void RendererViewport::viewport_set_texture_mipmap_bias(RID p_viewport, float p_mipmap_bias) {1031Viewport *viewport = viewport_owner.get_or_null(p_viewport);1032ERR_FAIL_NULL(viewport);10331034viewport->texture_mipmap_bias = p_mipmap_bias;1035_configure_3d_render_buffers(viewport);1036}10371038void RendererViewport::viewport_set_anisotropic_filtering_level(RID p_viewport, RS::ViewportAnisotropicFiltering p_anisotropic_filtering_level) {1039Viewport *viewport = viewport_owner.get_or_null(p_viewport);1040ERR_FAIL_NULL(viewport);10411042viewport->anisotropic_filtering_level = p_anisotropic_filtering_level;1043_configure_3d_render_buffers(viewport);1044}10451046void RendererViewport::viewport_set_scaling_3d_scale(RID p_viewport, float p_scaling_3d_scale) {1047Viewport *viewport = viewport_owner.get_or_null(p_viewport);1048ERR_FAIL_NULL(viewport);10491050// Clamp to reasonable values that are actually useful.1051// Values above 2.0 don't serve a practical purpose since the viewport1052// isn't displayed with mipmaps.1053if (viewport->scaling_3d_scale == CLAMP(p_scaling_3d_scale, 0.1, 2.0)) {1054return;1055}10561057viewport->scaling_3d_scale = CLAMP(p_scaling_3d_scale, 0.1, 2.0);1058_configure_3d_render_buffers(viewport);1059}10601061void RendererViewport::viewport_set_size(RID p_viewport, int p_width, int p_height) {1062ERR_FAIL_COND(p_width < 0 || p_height < 0);10631064Viewport *viewport = viewport_owner.get_or_null(p_viewport);1065ERR_FAIL_NULL(viewport);1066ERR_FAIL_COND_MSG(viewport->use_xr, "Cannot set viewport size when using XR");10671068_viewport_set_size(viewport, p_width, p_height, 1);1069}10701071void RendererViewport::_viewport_set_size(Viewport *p_viewport, int p_width, int p_height, uint32_t p_view_count) {1072Size2i new_size(p_width, p_height);1073if (p_viewport->size != new_size || p_viewport->view_count != p_view_count) {1074p_viewport->size = new_size;1075p_viewport->view_count = p_view_count;10761077RSG::texture_storage->render_target_set_size(p_viewport->render_target, p_width, p_height, p_view_count);1078_configure_3d_render_buffers(p_viewport);10791080p_viewport->occlusion_buffer_dirty = true;1081}1082}10831084bool RendererViewport::_viewport_requires_motion_vectors(Viewport *p_viewport) {1085return p_viewport->use_taa ||1086RS::scaling_3d_mode_type(p_viewport->scaling_3d_mode) == RS::VIEWPORT_SCALING_3D_TYPE_TEMPORAL ||1087p_viewport->debug_draw == RenderingServer::VIEWPORT_DEBUG_DRAW_MOTION_VECTORS || p_viewport->force_motion_vectors;1088}10891090void RendererViewport::viewport_set_active(RID p_viewport, bool p_active) {1091Viewport *viewport = viewport_owner.get_or_null(p_viewport);1092ERR_FAIL_NULL(viewport);10931094if (p_active) {1095ERR_FAIL_COND_MSG(active_viewports.has(viewport), "Can't make active a Viewport that is already active.");1096viewport->occlusion_buffer_dirty = true;1097active_viewports.push_back(viewport);1098} else {1099active_viewports.erase(viewport);1100}11011102sorted_active_viewports_dirty = true;1103}11041105void RendererViewport::viewport_set_parent_viewport(RID p_viewport, RID p_parent_viewport) {1106Viewport *viewport = viewport_owner.get_or_null(p_viewport);1107ERR_FAIL_NULL(viewport);11081109viewport->parent = p_parent_viewport;1110}11111112void RendererViewport::viewport_set_clear_mode(RID p_viewport, RS::ViewportClearMode p_clear_mode) {1113Viewport *viewport = viewport_owner.get_or_null(p_viewport);1114ERR_FAIL_NULL(viewport);11151116viewport->clear_mode = p_clear_mode;1117}11181119void RendererViewport::viewport_attach_to_screen(RID p_viewport, const Rect2 &p_rect, DisplayServer::WindowID p_screen) {1120Viewport *viewport = viewport_owner.get_or_null(p_viewport);1121ERR_FAIL_NULL(viewport);11221123if (p_screen != DisplayServer::INVALID_WINDOW_ID) {1124// If using OpenGL we can optimize this operation by rendering directly to system_fbo1125// instead of rendering to fbo and copying to system_fbo after1126if (RSG::rasterizer->is_low_end() && viewport->viewport_render_direct_to_screen) {1127RSG::texture_storage->render_target_set_size(viewport->render_target, p_rect.size.x, p_rect.size.y, viewport->view_count);1128RSG::texture_storage->render_target_set_position(viewport->render_target, p_rect.position.x, p_rect.position.y);1129}11301131viewport->viewport_to_screen_rect = p_rect;1132viewport->viewport_to_screen = p_screen;1133} else {1134// if render_direct_to_screen was used, reset size and position1135if (RSG::rasterizer->is_low_end() && viewport->viewport_render_direct_to_screen) {1136RSG::texture_storage->render_target_set_position(viewport->render_target, 0, 0);1137RSG::texture_storage->render_target_set_size(viewport->render_target, viewport->size.x, viewport->size.y, viewport->view_count);1138}11391140viewport->viewport_to_screen_rect = Rect2();1141viewport->viewport_to_screen = DisplayServer::INVALID_WINDOW_ID;1142}1143}11441145void RendererViewport::viewport_set_render_direct_to_screen(RID p_viewport, bool p_enable) {1146Viewport *viewport = viewport_owner.get_or_null(p_viewport);1147ERR_FAIL_NULL(viewport);11481149if (p_enable == viewport->viewport_render_direct_to_screen) {1150return;1151}11521153// if disabled, reset render_target size and position1154if (!p_enable) {1155RSG::texture_storage->render_target_set_position(viewport->render_target, 0, 0);1156RSG::texture_storage->render_target_set_size(viewport->render_target, viewport->size.x, viewport->size.y, viewport->view_count);1157}11581159RSG::texture_storage->render_target_set_direct_to_screen(viewport->render_target, p_enable);1160viewport->viewport_render_direct_to_screen = p_enable;11611162// if attached to screen already, setup screen size and position, this needs to happen after setting flag to avoid an unnecessary buffer allocation1163if (RSG::rasterizer->is_low_end() && viewport->viewport_to_screen_rect != Rect2() && p_enable) {1164RSG::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);1165RSG::texture_storage->render_target_set_position(viewport->render_target, viewport->viewport_to_screen_rect.position.x, viewport->viewport_to_screen_rect.position.y);1166}1167}11681169void RendererViewport::viewport_set_update_mode(RID p_viewport, RS::ViewportUpdateMode p_mode) {1170Viewport *viewport = viewport_owner.get_or_null(p_viewport);1171ERR_FAIL_NULL(viewport);11721173viewport->update_mode = p_mode;1174}11751176RS::ViewportUpdateMode RendererViewport::viewport_get_update_mode(RID p_viewport) const {1177Viewport *viewport = viewport_owner.get_or_null(p_viewport);1178ERR_FAIL_NULL_V(viewport, RS::VIEWPORT_UPDATE_DISABLED);11791180return viewport->update_mode;1181}11821183RID RendererViewport::viewport_get_render_target(RID p_viewport) const {1184const Viewport *viewport = viewport_owner.get_or_null(p_viewport);1185ERR_FAIL_NULL_V(viewport, RID());11861187return viewport->render_target;1188}11891190RID RendererViewport::viewport_get_texture(RID p_viewport) const {1191const Viewport *viewport = viewport_owner.get_or_null(p_viewport);1192ERR_FAIL_NULL_V(viewport, RID());11931194return RSG::texture_storage->render_target_get_texture(viewport->render_target);1195}11961197RID RendererViewport::viewport_get_occluder_debug_texture(RID p_viewport) const {1198const Viewport *viewport = viewport_owner.get_or_null(p_viewport);1199ERR_FAIL_NULL_V(viewport, RID());12001201if (viewport->use_occlusion_culling && viewport->debug_draw == RenderingServer::VIEWPORT_DEBUG_DRAW_OCCLUDERS) {1202return RendererSceneOcclusionCull::get_singleton()->buffer_get_debug_texture(p_viewport);1203}1204return RID();1205}12061207void RendererViewport::viewport_set_prev_camera_data(RID p_viewport, const RendererSceneRender::CameraData *p_camera_data) {1208Viewport *viewport = viewport_owner.get_or_null(p_viewport);1209ERR_FAIL_NULL(viewport);1210uint64_t frame = RSG::rasterizer->get_frame_number();1211if (viewport->prev_camera_data_frame != frame) {1212viewport->prev_camera_data = *p_camera_data;1213viewport->prev_camera_data_frame = frame;1214}1215}12161217const RendererSceneRender::CameraData *RendererViewport::viewport_get_prev_camera_data(RID p_viewport) {1218const Viewport *viewport = viewport_owner.get_or_null(p_viewport);1219ERR_FAIL_NULL_V(viewport, nullptr);1220return &viewport->prev_camera_data;1221}12221223void RendererViewport::viewport_set_disable_2d(RID p_viewport, bool p_disable) {1224Viewport *viewport = viewport_owner.get_or_null(p_viewport);1225ERR_FAIL_NULL(viewport);12261227viewport->disable_2d = p_disable;1228}12291230void RendererViewport::viewport_set_environment_mode(RID p_viewport, RS::ViewportEnvironmentMode p_mode) {1231Viewport *viewport = viewport_owner.get_or_null(p_viewport);1232ERR_FAIL_NULL(viewport);12331234viewport->disable_environment = p_mode;1235}12361237bool RendererViewport::viewport_is_environment_disabled(Viewport *viewport) {1238ERR_FAIL_NULL_V(viewport, false);12391240if (viewport->parent.is_valid() && viewport->disable_environment == RS::VIEWPORT_ENVIRONMENT_INHERIT) {1241Viewport *parent = viewport_owner.get_or_null(viewport->parent);1242return viewport_is_environment_disabled(parent);1243}1244return viewport->disable_environment == RS::VIEWPORT_ENVIRONMENT_DISABLED;1245}12461247void RendererViewport::viewport_set_disable_3d(RID p_viewport, bool p_disable) {1248Viewport *viewport = viewport_owner.get_or_null(p_viewport);1249ERR_FAIL_NULL(viewport);12501251viewport->disable_3d = p_disable;1252}12531254void RendererViewport::viewport_attach_camera(RID p_viewport, RID p_camera) {1255Viewport *viewport = viewport_owner.get_or_null(p_viewport);1256ERR_FAIL_NULL(viewport);12571258viewport->camera = p_camera;1259}12601261void RendererViewport::viewport_set_scenario(RID p_viewport, RID p_scenario) {1262Viewport *viewport = viewport_owner.get_or_null(p_viewport);1263ERR_FAIL_NULL(viewport);12641265if (viewport->scenario.is_valid()) {1266RSG::scene->scenario_remove_viewport_visibility_mask(viewport->scenario, p_viewport);1267}12681269viewport->scenario = p_scenario;1270if (viewport->use_occlusion_culling) {1271RendererSceneOcclusionCull::get_singleton()->buffer_set_scenario(p_viewport, p_scenario);1272}1273}12741275void RendererViewport::viewport_attach_canvas(RID p_viewport, RID p_canvas) {1276Viewport *viewport = viewport_owner.get_or_null(p_viewport);1277ERR_FAIL_NULL(viewport);12781279ERR_FAIL_COND(viewport->canvas_map.has(p_canvas));1280RendererCanvasCull::Canvas *canvas = RSG::canvas->canvas_owner.get_or_null(p_canvas);1281ERR_FAIL_NULL(canvas);12821283canvas->viewports.insert(p_viewport);1284viewport->canvas_map[p_canvas] = Viewport::CanvasData();1285viewport->canvas_map[p_canvas].layer = 0;1286viewport->canvas_map[p_canvas].sublayer = 0;1287viewport->canvas_map[p_canvas].canvas = canvas;1288}12891290void RendererViewport::viewport_remove_canvas(RID p_viewport, RID p_canvas) {1291Viewport *viewport = viewport_owner.get_or_null(p_viewport);1292ERR_FAIL_NULL(viewport);12931294RendererCanvasCull::Canvas *canvas = RSG::canvas->canvas_owner.get_or_null(p_canvas);1295ERR_FAIL_NULL(canvas);12961297viewport->canvas_map.erase(p_canvas);1298canvas->viewports.erase(p_viewport);1299}13001301void RendererViewport::viewport_set_canvas_transform(RID p_viewport, RID p_canvas, const Transform2D &p_offset) {1302Viewport *viewport = viewport_owner.get_or_null(p_viewport);1303ERR_FAIL_NULL(viewport);13041305ERR_FAIL_COND(!viewport->canvas_map.has(p_canvas));1306viewport->canvas_map[p_canvas].transform = p_offset;1307}13081309void RendererViewport::viewport_set_transparent_background(RID p_viewport, bool p_enabled) {1310Viewport *viewport = viewport_owner.get_or_null(p_viewport);1311ERR_FAIL_NULL(viewport);1312if (viewport->transparent_bg == p_enabled) {1313return;1314}13151316RSG::texture_storage->render_target_set_transparent(viewport->render_target, p_enabled);1317viewport->transparent_bg = p_enabled;1318}13191320void RendererViewport::viewport_set_global_canvas_transform(RID p_viewport, const Transform2D &p_transform) {1321Viewport *viewport = viewport_owner.get_or_null(p_viewport);1322ERR_FAIL_NULL(viewport);13231324viewport->global_transform = p_transform;1325}13261327void RendererViewport::viewport_set_canvas_stacking(RID p_viewport, RID p_canvas, int p_layer, int p_sublayer) {1328Viewport *viewport = viewport_owner.get_or_null(p_viewport);1329ERR_FAIL_NULL(viewport);13301331ERR_FAIL_COND(!viewport->canvas_map.has(p_canvas));1332viewport->canvas_map[p_canvas].layer = p_layer;1333viewport->canvas_map[p_canvas].sublayer = p_sublayer;1334}13351336void RendererViewport::viewport_set_positional_shadow_atlas_size(RID p_viewport, int p_size, bool p_16_bits) {1337Viewport *viewport = viewport_owner.get_or_null(p_viewport);1338ERR_FAIL_NULL(viewport);13391340viewport->shadow_atlas_size = p_size;1341viewport->shadow_atlas_16_bits = p_16_bits;13421343RSG::light_storage->shadow_atlas_set_size(viewport->shadow_atlas, viewport->shadow_atlas_size, viewport->shadow_atlas_16_bits);1344}13451346void RendererViewport::viewport_set_positional_shadow_atlas_quadrant_subdivision(RID p_viewport, int p_quadrant, int p_subdiv) {1347Viewport *viewport = viewport_owner.get_or_null(p_viewport);1348ERR_FAIL_NULL(viewport);13491350RSG::light_storage->shadow_atlas_set_quadrant_subdivision(viewport->shadow_atlas, p_quadrant, p_subdiv);1351}13521353void RendererViewport::viewport_set_msaa_2d(RID p_viewport, RS::ViewportMSAA p_msaa) {1354Viewport *viewport = viewport_owner.get_or_null(p_viewport);1355ERR_FAIL_NULL(viewport);13561357if (viewport->msaa_2d == p_msaa) {1358return;1359}1360viewport->msaa_2d = p_msaa;1361RSG::texture_storage->render_target_set_msaa(viewport->render_target, p_msaa);1362}13631364void RendererViewport::viewport_set_msaa_3d(RID p_viewport, RS::ViewportMSAA p_msaa) {1365Viewport *viewport = viewport_owner.get_or_null(p_viewport);1366ERR_FAIL_NULL(viewport);13671368if (viewport->msaa_3d == p_msaa) {1369return;1370}1371viewport->msaa_3d = p_msaa;1372_configure_3d_render_buffers(viewport);1373}13741375void RendererViewport::viewport_set_use_hdr_2d(RID p_viewport, bool p_use_hdr_2d) {1376Viewport *viewport = viewport_owner.get_or_null(p_viewport);1377ERR_FAIL_NULL(viewport);13781379if (viewport->use_hdr_2d == p_use_hdr_2d) {1380return;1381}1382viewport->use_hdr_2d = p_use_hdr_2d;1383RSG::texture_storage->render_target_set_use_hdr(viewport->render_target, p_use_hdr_2d);1384_configure_3d_render_buffers(viewport);1385}13861387bool RendererViewport::viewport_is_using_hdr_2d(RID p_viewport) const {1388Viewport *viewport = viewport_owner.get_or_null(p_viewport);1389ERR_FAIL_NULL_V(viewport, false);13901391return viewport->use_hdr_2d;1392}13931394void RendererViewport::viewport_set_screen_space_aa(RID p_viewport, RS::ViewportScreenSpaceAA p_mode) {1395Viewport *viewport = viewport_owner.get_or_null(p_viewport);1396ERR_FAIL_NULL(viewport);1397#ifdef DEBUG_ENABLED1398if (OS::get_singleton()->get_current_rendering_method() == "gl_compatibility" && p_mode != RS::VIEWPORT_SCREEN_SPACE_AA_DISABLED) {1399WARN_PRINT_ONCE_ED("Screen-space AA is only available when using the Forward+ or Mobile renderer.");1400}1401#endif14021403if (viewport->screen_space_aa == p_mode) {1404return;1405}1406viewport->screen_space_aa = p_mode;1407_configure_3d_render_buffers(viewport);1408}14091410void RendererViewport::viewport_set_use_taa(RID p_viewport, bool p_use_taa) {1411Viewport *viewport = viewport_owner.get_or_null(p_viewport);1412ERR_FAIL_NULL(viewport);1413#ifdef DEBUG_ENABLED1414if (OS::get_singleton()->get_current_rendering_method() != "forward_plus") {1415WARN_PRINT_ONCE_ED("TAA is only available when using the Forward+ renderer.");1416}1417#endif14181419if (viewport->use_taa == p_use_taa) {1420return;1421}14221423bool motion_vectors_before = _viewport_requires_motion_vectors(viewport);1424viewport->use_taa = p_use_taa;14251426bool motion_vectors_after = _viewport_requires_motion_vectors(viewport);1427if (motion_vectors_before != motion_vectors_after) {1428num_viewports_with_motion_vectors += motion_vectors_after ? 1 : -1;1429}14301431_configure_3d_render_buffers(viewport);1432}14331434void RendererViewport::viewport_set_use_debanding(RID p_viewport, bool p_use_debanding) {1435Viewport *viewport = viewport_owner.get_or_null(p_viewport);1436ERR_FAIL_NULL(viewport);14371438if (viewport->use_debanding == p_use_debanding) {1439return;1440}1441viewport->use_debanding = p_use_debanding;1442RSG::texture_storage->render_target_set_use_debanding(viewport->render_target, p_use_debanding);1443_configure_3d_render_buffers(viewport);1444}14451446void RendererViewport::viewport_set_force_motion_vectors(RID p_viewport, bool p_force_motion_vectors) {1447Viewport *viewport = viewport_owner.get_or_null(p_viewport);1448ERR_FAIL_NULL(viewport);14491450_viewport_set_force_motion_vectors(viewport, p_force_motion_vectors);1451}14521453void RendererViewport::_viewport_set_force_motion_vectors(RendererViewport::Viewport *p_viewport, bool p_force_motion_vectors) {1454if (p_viewport->force_motion_vectors == p_force_motion_vectors) {1455return;1456}14571458bool motion_vectors_before = _viewport_requires_motion_vectors(p_viewport);1459p_viewport->force_motion_vectors = p_force_motion_vectors;14601461bool motion_vectors_after = _viewport_requires_motion_vectors(p_viewport);1462if (motion_vectors_before != motion_vectors_after) {1463num_viewports_with_motion_vectors += motion_vectors_after ? 1 : -1;1464}14651466_configure_3d_render_buffers(p_viewport);1467}14681469void RendererViewport::viewport_set_use_occlusion_culling(RID p_viewport, bool p_use_occlusion_culling) {1470Viewport *viewport = viewport_owner.get_or_null(p_viewport);1471ERR_FAIL_NULL(viewport);14721473if (viewport->use_occlusion_culling == p_use_occlusion_culling) {1474return;1475}1476viewport->use_occlusion_culling = p_use_occlusion_culling;14771478if (viewport->use_occlusion_culling) {1479RendererSceneOcclusionCull::get_singleton()->add_buffer(p_viewport);1480RendererSceneOcclusionCull::get_singleton()->buffer_set_scenario(p_viewport, viewport->scenario);1481} else {1482RendererSceneOcclusionCull::get_singleton()->remove_buffer(p_viewport);1483}14841485viewport->occlusion_buffer_dirty = true;1486}14871488void RendererViewport::viewport_set_occlusion_rays_per_thread(int p_rays_per_thread) {1489if (occlusion_rays_per_thread == p_rays_per_thread) {1490return;1491}14921493occlusion_rays_per_thread = p_rays_per_thread;14941495for (int i = 0; i < active_viewports.size(); i++) {1496active_viewports[i]->occlusion_buffer_dirty = true;1497}1498}14991500void RendererViewport::viewport_set_occlusion_culling_build_quality(RS::ViewportOcclusionCullingBuildQuality p_quality) {1501RendererSceneOcclusionCull::get_singleton()->set_build_quality(p_quality);1502}15031504void RendererViewport::viewport_set_mesh_lod_threshold(RID p_viewport, float p_pixels) {1505Viewport *viewport = viewport_owner.get_or_null(p_viewport);1506ERR_FAIL_NULL(viewport);15071508viewport->mesh_lod_threshold = p_pixels;1509}15101511int RendererViewport::viewport_get_render_info(RID p_viewport, RS::ViewportRenderInfoType p_type, RS::ViewportRenderInfo p_info) {1512ERR_FAIL_INDEX_V(p_type, RS::VIEWPORT_RENDER_INFO_TYPE_MAX, -1);1513ERR_FAIL_INDEX_V(p_info, RS::VIEWPORT_RENDER_INFO_MAX, -1);15141515Viewport *viewport = viewport_owner.get_or_null(p_viewport);1516if (!viewport) {1517return 0; //there should be a lock here..1518}15191520return viewport->render_info.info[p_type][p_info];1521}15221523void RendererViewport::viewport_set_debug_draw(RID p_viewport, RS::ViewportDebugDraw p_draw) {1524Viewport *viewport = viewport_owner.get_or_null(p_viewport);1525ERR_FAIL_NULL(viewport);15261527bool motion_vectors_before = _viewport_requires_motion_vectors(viewport);1528viewport->debug_draw = p_draw;15291530bool motion_vectors_after = _viewport_requires_motion_vectors(viewport);1531if (motion_vectors_before != motion_vectors_after) {1532num_viewports_with_motion_vectors += motion_vectors_after ? 1 : -1;1533}1534}15351536void RendererViewport::viewport_set_measure_render_time(RID p_viewport, bool p_enable) {1537Viewport *viewport = viewport_owner.get_or_null(p_viewport);1538ERR_FAIL_NULL(viewport);15391540viewport->measure_render_time = p_enable;1541}15421543float RendererViewport::viewport_get_measured_render_time_cpu(RID p_viewport) const {1544Viewport *viewport = viewport_owner.get_or_null(p_viewport);1545ERR_FAIL_NULL_V(viewport, 0);15461547return double(viewport->time_cpu_end - viewport->time_cpu_begin) / 1000.0;1548}15491550float RendererViewport::viewport_get_measured_render_time_gpu(RID p_viewport) const {1551Viewport *viewport = viewport_owner.get_or_null(p_viewport);1552ERR_FAIL_NULL_V(viewport, 0);15531554return double((viewport->time_gpu_end - viewport->time_gpu_begin) / 1000) / 1000.0;1555}15561557void RendererViewport::viewport_set_snap_2d_transforms_to_pixel(RID p_viewport, bool p_enabled) {1558Viewport *viewport = viewport_owner.get_or_null(p_viewport);1559ERR_FAIL_NULL(viewport);1560viewport->snap_2d_transforms_to_pixel = p_enabled;1561}15621563void RendererViewport::viewport_set_snap_2d_vertices_to_pixel(RID p_viewport, bool p_enabled) {1564Viewport *viewport = viewport_owner.get_or_null(p_viewport);1565ERR_FAIL_NULL(viewport);1566viewport->snap_2d_vertices_to_pixel = p_enabled;1567}15681569void RendererViewport::viewport_set_default_canvas_item_texture_filter(RID p_viewport, RS::CanvasItemTextureFilter p_filter) {1570ERR_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).)");1571Viewport *viewport = viewport_owner.get_or_null(p_viewport);1572ERR_FAIL_NULL(viewport);15731574viewport->texture_filter = p_filter;1575}1576void RendererViewport::viewport_set_default_canvas_item_texture_repeat(RID p_viewport, RS::CanvasItemTextureRepeat p_repeat) {1577ERR_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).)");1578Viewport *viewport = viewport_owner.get_or_null(p_viewport);1579ERR_FAIL_NULL(viewport);15801581viewport->texture_repeat = p_repeat;1582}15831584void RendererViewport::viewport_set_sdf_oversize_and_scale(RID p_viewport, RS::ViewportSDFOversize p_size, RS::ViewportSDFScale p_scale) {1585Viewport *viewport = viewport_owner.get_or_null(p_viewport);1586ERR_FAIL_NULL(viewport);15871588RSG::texture_storage->render_target_set_sdf_size_and_scale(viewport->render_target, p_size, p_scale);1589}15901591RID RendererViewport::viewport_find_from_screen_attachment(DisplayServer::WindowID p_id) const {1592RID *rids = nullptr;1593uint32_t rid_count = viewport_owner.get_rid_count();1594rids = (RID *)alloca(sizeof(RID) * rid_count);1595viewport_owner.fill_owned_buffer(rids);1596for (uint32_t i = 0; i < rid_count; i++) {1597Viewport *viewport = viewport_owner.get_or_null(rids[i]);1598if (viewport->viewport_to_screen == p_id) {1599return rids[i];1600}1601}1602return RID();1603}16041605void RendererViewport::viewport_set_vrs_mode(RID p_viewport, RS::ViewportVRSMode p_mode) {1606Viewport *viewport = viewport_owner.get_or_null(p_viewport);1607ERR_FAIL_NULL(viewport);16081609RSG::texture_storage->render_target_set_vrs_mode(viewport->render_target, p_mode);1610_configure_3d_render_buffers(viewport);1611}16121613void RendererViewport::viewport_set_vrs_update_mode(RID p_viewport, RS::ViewportVRSUpdateMode p_mode) {1614Viewport *viewport = viewport_owner.get_or_null(p_viewport);1615ERR_FAIL_NULL(viewport);16161617RSG::texture_storage->render_target_set_vrs_update_mode(viewport->render_target, p_mode);1618}16191620void RendererViewport::viewport_set_vrs_texture(RID p_viewport, RID p_texture) {1621Viewport *viewport = viewport_owner.get_or_null(p_viewport);1622ERR_FAIL_NULL(viewport);16231624RSG::texture_storage->render_target_set_vrs_texture(viewport->render_target, p_texture);1625_configure_3d_render_buffers(viewport);1626}16271628bool RendererViewport::free(RID p_rid) {1629if (viewport_owner.owns(p_rid)) {1630Viewport *viewport = viewport_owner.get_or_null(p_rid);16311632RSG::texture_storage->render_target_free(viewport->render_target);1633RSG::light_storage->shadow_atlas_free(viewport->shadow_atlas);1634if (viewport->render_buffers.is_valid()) {1635viewport->render_buffers.unref();1636}16371638while (viewport->canvas_map.begin()) {1639viewport_remove_canvas(p_rid, viewport->canvas_map.begin()->key);1640}16411642viewport_set_scenario(p_rid, RID());1643active_viewports.erase(viewport);1644sorted_active_viewports_dirty = true;16451646if (viewport->use_occlusion_culling) {1647RendererSceneOcclusionCull::get_singleton()->remove_buffer(p_rid);1648}16491650if (_viewport_requires_motion_vectors(viewport)) {1651num_viewports_with_motion_vectors--;1652}16531654viewport_owner.free(p_rid);16551656return true;1657}16581659return false;1660}16611662void RendererViewport::handle_timestamp(String p_timestamp, uint64_t p_cpu_time, uint64_t p_gpu_time) {1663RID *vp = timestamp_vp_map.getptr(p_timestamp);1664if (!vp) {1665return;1666}16671668Viewport *viewport = viewport_owner.get_or_null(*vp);1669if (!viewport) {1670return;1671}16721673if (p_timestamp.begins_with("vp_begin")) {1674viewport->time_cpu_begin = p_cpu_time;1675viewport->time_gpu_begin = p_gpu_time;1676}16771678if (p_timestamp.begins_with("vp_end")) {1679viewport->time_cpu_end = p_cpu_time;1680viewport->time_gpu_end = p_gpu_time;1681}1682}16831684void RendererViewport::viewport_set_canvas_cull_mask(RID p_viewport, uint32_t p_canvas_cull_mask) {1685Viewport *viewport = viewport_owner.get_or_null(p_viewport);1686ERR_FAIL_NULL(viewport);1687viewport->canvas_cull_mask = p_canvas_cull_mask;1688}16891690// Workaround for setting this on thread.1691void RendererViewport::call_set_vsync_mode(DisplayServer::VSyncMode p_mode, DisplayServer::WindowID p_window) {1692DisplayServer::get_singleton()->window_set_vsync_mode(p_mode, p_window);1693}16941695int RendererViewport::get_total_objects_drawn() const {1696return total_objects_drawn;1697}1698int RendererViewport::get_total_primitives_drawn() const {1699return total_vertices_drawn;1700}1701int RendererViewport::get_total_draw_calls_used() const {1702return total_draw_calls_used;1703}17041705int RendererViewport::get_num_viewports_with_motion_vectors() const {1706return num_viewports_with_motion_vectors;1707}17081709RendererViewport::RendererViewport() {1710occlusion_rays_per_thread = GLOBAL_GET("rendering/occlusion_culling/occlusion_rays_per_thread");1711}171217131714