diff options
Diffstat (limited to 'servers/rendering')
25 files changed, 2581 insertions, 568 deletions
diff --git a/servers/rendering/rasterizer_dummy.h b/servers/rendering/rasterizer_dummy.h new file mode 100644 index 0000000000..bfc7e7684f --- /dev/null +++ b/servers/rendering/rasterizer_dummy.h @@ -0,0 +1,787 @@ +/*************************************************************************/ +/* rasterizer_dummy.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef RASTERIZER_DUMMY_H +#define RASTERIZER_DUMMY_H + +#include "core/math/camera_matrix.h" +#include "core/templates/rid_owner.h" +#include "core/templates/self_list.h" +#include "scene/resources/mesh.h" +#include "servers/rendering/renderer_compositor.h" +#include "servers/rendering_server.h" + +class RasterizerSceneDummy : public RendererSceneRender { +public: + GeometryInstance *geometry_instance_create(RID p_base) override { return nullptr; } + void geometry_instance_set_skeleton(GeometryInstance *p_geometry_instance, RID p_skeleton) override {} + void geometry_instance_set_material_override(GeometryInstance *p_geometry_instance, RID p_override) override {} + void geometry_instance_set_surface_materials(GeometryInstance *p_geometry_instance, const Vector<RID> &p_material) override {} + void geometry_instance_set_mesh_instance(GeometryInstance *p_geometry_instance, RID p_mesh_instance) override {} + void geometry_instance_set_transform(GeometryInstance *p_geometry_instance, const Transform &p_transform, const AABB &p_aabb, const AABB &p_transformed_aabbb) override {} + void geometry_instance_set_layer_mask(GeometryInstance *p_geometry_instance, uint32_t p_layer_mask) override {} + void geometry_instance_set_lod_bias(GeometryInstance *p_geometry_instance, float p_lod_bias) override {} + void geometry_instance_set_use_baked_light(GeometryInstance *p_geometry_instance, bool p_enable) override {} + void geometry_instance_set_use_dynamic_gi(GeometryInstance *p_geometry_instance, bool p_enable) override {} + void geometry_instance_set_use_lightmap(GeometryInstance *p_geometry_instance, RID p_lightmap_instance, const Rect2 &p_lightmap_uv_scale, int p_lightmap_slice_index) override {} + void geometry_instance_set_lightmap_capture(GeometryInstance *p_geometry_instance, const Color *p_sh9) override {} + void geometry_instance_set_instance_shader_parameters_offset(GeometryInstance *p_geometry_instance, int32_t p_offset) override {} + void geometry_instance_set_cast_double_sided_shadows(GeometryInstance *p_geometry_instance, bool p_enable) override {} + + uint32_t geometry_instance_get_pair_mask() override { return 0; } + void geometry_instance_pair_light_instances(GeometryInstance *p_geometry_instance, const RID *p_light_instances, uint32_t p_light_instance_count) override {} + void geometry_instance_pair_reflection_probe_instances(GeometryInstance *p_geometry_instance, const RID *p_reflection_probe_instances, uint32_t p_reflection_probe_instance_count) override {} + void geometry_instance_pair_decal_instances(GeometryInstance *p_geometry_instance, const RID *p_decal_instances, uint32_t p_decal_instance_count) override {} + void geometry_instance_pair_gi_probe_instances(GeometryInstance *p_geometry_instance, const RID *p_gi_probe_instances, uint32_t p_gi_probe_instance_count) override {} + + void geometry_instance_free(GeometryInstance *p_geometry_instance) override {} + + /* SHADOW ATLAS API */ + + RID shadow_atlas_create() override { return RID(); } + void shadow_atlas_set_size(RID p_atlas, int p_size, bool p_16_bits = false) override {} + void shadow_atlas_set_quadrant_subdivision(RID p_atlas, int p_quadrant, int p_subdivision) override {} + bool shadow_atlas_update_light(RID p_atlas, RID p_light_intance, float p_coverage, uint64_t p_light_version) override { return false; } + + void directional_shadow_atlas_set_size(int p_size, bool p_16_bits = false) override {} + int get_directional_light_shadow_size(RID p_light_intance) override { return 0; } + void set_directional_shadow_count(int p_count) override {} + + /* SDFGI UPDATE */ + + void sdfgi_update(RID p_render_buffers, RID p_environment, const Vector3 &p_world_position) override {} + int sdfgi_get_pending_region_count(RID p_render_buffers) const override { return 0; } + AABB sdfgi_get_pending_region_bounds(RID p_render_buffers, int p_region) const override { return AABB(); } + uint32_t sdfgi_get_pending_region_cascade(RID p_render_buffers, int p_region) const override { return 0; } + + /* SKY API */ + + RID sky_allocate() override { return RID(); } + void sky_initialize(RID p_rid) override {} + void sky_set_radiance_size(RID p_sky, int p_radiance_size) override {} + void sky_set_mode(RID p_sky, RS::SkyMode p_samples) override {} + void sky_set_material(RID p_sky, RID p_material) override {} + Ref<Image> sky_bake_panorama(RID p_sky, float p_energy, bool p_bake_irradiance, const Size2i &p_size) override { return Ref<Image>(); } + + /* ENVIRONMENT API */ + + RID environment_allocate() override { return RID(); } + void environment_initialize(RID p_rid) override {} + void environment_set_background(RID p_env, RS::EnvironmentBG p_bg) override {} + void environment_set_sky(RID p_env, RID p_sky) override {} + void environment_set_sky_custom_fov(RID p_env, float p_scale) override {} + void environment_set_sky_orientation(RID p_env, const Basis &p_orientation) override {} + void environment_set_bg_color(RID p_env, const Color &p_color) override {} + void environment_set_bg_energy(RID p_env, float p_energy) override {} + void environment_set_canvas_max_layer(RID p_env, int p_max_layer) override {} + void environment_set_ambient_light(RID p_env, const Color &p_color, RS::EnvironmentAmbientSource p_ambient = RS::ENV_AMBIENT_SOURCE_BG, float p_energy = 1.0, float p_sky_contribution = 0.0, RS::EnvironmentReflectionSource p_reflection_source = RS::ENV_REFLECTION_SOURCE_BG, const Color &p_ao_color = Color()) override {} + + void environment_set_glow(RID p_env, bool p_enable, Vector<float> p_levels, float p_intensity, float p_strength, float p_mix, float p_bloom_threshold, RS::EnvironmentGlowBlendMode p_blend_mode, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, float p_hdr_luminance_cap) override {} + void environment_glow_set_use_bicubic_upscale(bool p_enable) override {} + void environment_glow_set_use_high_quality(bool p_enable) override {} + + void environment_set_ssr(RID p_env, bool p_enable, int p_max_steps, float p_fade_int, float p_fade_out, float p_depth_tolerance) override {} + void environment_set_ssr_roughness_quality(RS::EnvironmentSSRRoughnessQuality p_quality) override {} + void environment_set_ssao(RID p_env, bool p_enable, float p_radius, float p_intensity, float p_power, float p_detail, float p_horizon, float p_sharpness, float p_light_affect, float p_ao_channel_affect) override {} + void environment_set_ssao_quality(RS::EnvironmentSSAOQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) override {} + + void environment_set_sdfgi(RID p_env, bool p_enable, RS::EnvironmentSDFGICascades p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias) override {} + + void environment_set_sdfgi_ray_count(RS::EnvironmentSDFGIRayCount p_ray_count) override {} + void environment_set_sdfgi_frames_to_converge(RS::EnvironmentSDFGIFramesToConverge p_frames) override {} + void environment_set_sdfgi_frames_to_update_light(RS::EnvironmentSDFGIFramesToUpdateLight p_update) override {} + + void environment_set_tonemap(RID p_env, RS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white, bool p_auto_exposure, float p_min_luminance, float p_max_luminance, float p_auto_exp_speed, float p_auto_exp_scale) override {} + + void environment_set_adjustment(RID p_env, bool p_enable, float p_brightness, float p_contrast, float p_saturation, bool p_use_1d_color_correction, RID p_color_correction) override {} + + void environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_aerial_perspective) override {} + void environment_set_volumetric_fog(RID p_env, bool p_enable, float p_density, const Color &p_light, float p_light_energy, float p_length, float p_detail_spread, float p_gi_inject, bool p_temporal_reprojection, float p_temporal_reprojection_amount) override {} + void environment_set_volumetric_fog_volume_size(int p_size, int p_depth) override {} + void environment_set_volumetric_fog_filter_active(bool p_enable) override {} + + Ref<Image> environment_bake_panorama(RID p_env, bool p_bake_irradiance, const Size2i &p_size) override { return Ref<Image>(); } + + bool is_environment(RID p_env) const override { return false; } + RS::EnvironmentBG environment_get_background(RID p_env) const override { return RS::ENV_BG_KEEP; } + int environment_get_canvas_max_layer(RID p_env) const override { return 0; } + + RID camera_effects_allocate() override { return RID(); } + void camera_effects_initialize(RID p_rid) override {} + void camera_effects_set_dof_blur_quality(RS::DOFBlurQuality p_quality, bool p_use_jitter) override {} + void camera_effects_set_dof_blur_bokeh_shape(RS::DOFBokehShape p_shape) override {} + + void camera_effects_set_dof_blur(RID p_camera_effects, bool p_far_enable, float p_far_distance, float p_far_transition, bool p_near_enable, float p_near_distance, float p_near_transition, float p_amount) override {} + void camera_effects_set_custom_exposure(RID p_camera_effects, bool p_enable, float p_exposure) override {} + + void shadows_quality_set(RS::ShadowQuality p_quality) override {} + void directional_shadow_quality_set(RS::ShadowQuality p_quality) override {} + + RID light_instance_create(RID p_light) override { return RID(); } + void light_instance_set_transform(RID p_light_instance, const Transform &p_transform) override {} + void light_instance_set_aabb(RID p_light_instance, const AABB &p_aabb) override {} + void light_instance_set_shadow_transform(RID p_light_instance, const CameraMatrix &p_projection, const Transform &p_transform, float p_far, float p_split, int p_pass, float p_shadow_texel_size, float p_bias_scale = 1.0, float p_range_begin = 0, const Vector2 &p_uv_scale = Vector2()) override {} + void light_instance_mark_visible(RID p_light_instance) override {} + + RID reflection_atlas_create() override { return RID(); } + int reflection_atlas_get_size(RID p_ref_atlas) const override { return 0; } + void reflection_atlas_set_size(RID p_ref_atlas, int p_reflection_size, int p_reflection_count) override {} + + RID reflection_probe_instance_create(RID p_probe) override { return RID(); } + void reflection_probe_instance_set_transform(RID p_instance, const Transform &p_transform) override {} + void reflection_probe_release_atlas_index(RID p_instance) override {} + bool reflection_probe_instance_needs_redraw(RID p_instance) override { return false; } + bool reflection_probe_instance_has_reflection(RID p_instance) override { return false; } + bool reflection_probe_instance_begin_render(RID p_instance, RID p_reflection_atlas) override { return false; } + bool reflection_probe_instance_postprocess_step(RID p_instance) override { return true; } + + RID decal_instance_create(RID p_decal) override { return RID(); } + void decal_instance_set_transform(RID p_decal, const Transform &p_transform) override {} + + RID lightmap_instance_create(RID p_lightmap) override { return RID(); } + void lightmap_instance_set_transform(RID p_lightmap, const Transform &p_transform) override {} + + RID gi_probe_instance_create(RID p_gi_probe) override { return RID(); } + void gi_probe_instance_set_transform_to_data(RID p_probe, const Transform &p_xform) override {} + bool gi_probe_needs_update(RID p_probe) const override { return false; } + void gi_probe_update(RID p_probe, bool p_update_light_instances, const Vector<RID> &p_light_instances, const PagedArray<RendererSceneRender::GeometryInstance *> &p_dynamic_objects) override {} + + void gi_probe_set_quality(RS::GIProbeQuality) override {} + + void render_scene(RID p_render_buffers, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, const PagedArray<GeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_gi_probes, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr) override {} + void render_material(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, const PagedArray<GeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) override {} + void render_particle_collider_heightfield(RID p_collider, const Transform &p_transform, const PagedArray<GeometryInstance *> &p_instances) override {} + + void set_scene_pass(uint64_t p_pass) override {} + void set_time(double p_time, double p_step) override {} + void set_debug_draw_mode(RS::ViewportDebugDraw p_debug_draw) override {} + + RID render_buffers_create() override { return RID(); } + void render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_width, int p_height, RS::ViewportMSAA p_msaa, RS::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_debanding) override {} + void gi_set_use_half_resolution(bool p_enable) override {} + + void screen_space_roughness_limiter_set_active(bool p_enable, float p_amount, float p_curve) override {} + bool screen_space_roughness_limiter_is_active() const override { return false; } + + void sub_surface_scattering_set_quality(RS::SubSurfaceScatteringQuality p_quality) override {} + void sub_surface_scattering_set_scale(float p_scale, float p_depth_scale) override {} + + TypedArray<Image> bake_render_uv2(RID p_base, const Vector<RID> &p_material_overrides, const Size2i &p_image_size) override { return TypedArray<Image>(); } + + bool free(RID p_rid) override { return true; } + void update() override {} + void sdfgi_set_debug_probe_select(const Vector3 &p_position, const Vector3 &p_dir) override {} + + RasterizerSceneDummy() {} + ~RasterizerSceneDummy() {} +}; + +class RasterizerStorageDummy : public RendererStorage { +public: + bool can_create_resources_async() const override { return false; } + + /* TEXTURE API */ + struct DummyTexture { + Ref<Image> image; + }; + mutable RID_PtrOwner<DummyTexture> texture_owner; + + RID texture_allocate() override { + DummyTexture *texture = memnew(DummyTexture); + ERR_FAIL_COND_V(!texture, RID()); + return texture_owner.make_rid(texture); + } + void texture_2d_initialize(RID p_texture, const Ref<Image> &p_image) override { + DummyTexture *t = texture_owner.getornull(p_texture); + ERR_FAIL_COND(!t); + t->image = p_image->duplicate(); + } + + void texture_2d_layered_initialize(RID p_texture, const Vector<Ref<Image>> &p_layers, RS::TextureLayeredType p_layered_type) override {} + void texture_2d_update_immediate(RID p_texture, const Ref<Image> &p_image, int p_layer = 0) override {} + void texture_2d_update(RID p_texture, const Ref<Image> &p_image, int p_layer = 0) override {} + void texture_3d_initialize(RID p_texture, Image::Format, int p_width, int p_height, int p_depth, bool p_mipmaps, const Vector<Ref<Image>> &p_data) override {} + void texture_3d_update(RID p_texture, const Vector<Ref<Image>> &p_data) override {} + void texture_proxy_initialize(RID p_texture, RID p_base) override {} + void texture_proxy_update(RID p_proxy, RID p_base) override {} + + void texture_2d_placeholder_initialize(RID p_texture) override {} + void texture_2d_layered_placeholder_initialize(RID p_texture, RenderingServer::TextureLayeredType p_layered_type) override {} + void texture_3d_placeholder_initialize(RID p_texture) override {} + + Ref<Image> texture_2d_get(RID p_texture) const override { + DummyTexture *t = texture_owner.getornull(p_texture); + ERR_FAIL_COND_V(!t, Ref<Image>()); + return t->image; + } + + Ref<Image> texture_2d_layer_get(RID p_texture, int p_layer) const override { return Ref<Image>(); } + Vector<Ref<Image>> texture_3d_get(RID p_texture) const override { return Vector<Ref<Image>>(); } + + void texture_replace(RID p_texture, RID p_by_texture) override {} + void texture_set_size_override(RID p_texture, int p_width, int p_height) override {} + + void texture_set_path(RID p_texture, const String &p_path) override {} + String texture_get_path(RID p_texture) const override { return String(); } + + void texture_set_detect_3d_callback(RID p_texture, RS::TextureDetectCallback p_callback, void *p_userdata) override {} + void texture_set_detect_normal_callback(RID p_texture, RS::TextureDetectCallback p_callback, void *p_userdata) override {} + void texture_set_detect_roughness_callback(RID p_texture, RS::TextureDetectRoughnessCallback p_callback, void *p_userdata) override {} + + void texture_debug_usage(List<RS::TextureInfo> *r_info) override {} + void texture_set_force_redraw_if_visible(RID p_texture, bool p_enable) override {} + Size2 texture_size_with_proxy(RID p_proxy) override { return Size2(); } + + void texture_add_to_decal_atlas(RID p_texture, bool p_panorama_to_dp = false) override {} + void texture_remove_from_decal_atlas(RID p_texture, bool p_panorama_to_dp = false) override {} + + /* CANVAS TEXTURE API */ + + RID canvas_texture_allocate() override { return RID(); } + void canvas_texture_initialize(RID p_rid) override {} + void canvas_texture_set_channel(RID p_canvas_texture, RS::CanvasTextureChannel p_channel, RID p_texture) override {} + void canvas_texture_set_shading_parameters(RID p_canvas_texture, const Color &p_base_color, float p_shininess) override {} + + void canvas_texture_set_texture_filter(RID p_item, RS::CanvasItemTextureFilter p_filter) override {} + void canvas_texture_set_texture_repeat(RID p_item, RS::CanvasItemTextureRepeat p_repeat) override {} + + /* SHADER API */ + + RID shader_allocate() override { return RID(); } + void shader_initialize(RID p_rid) override {} + void shader_set_code(RID p_shader, const String &p_code) override {} + String shader_get_code(RID p_shader) const override { return ""; } + void shader_get_param_list(RID p_shader, List<PropertyInfo> *p_param_list) const override {} + + void shader_set_default_texture_param(RID p_shader, const StringName &p_name, RID p_texture) override {} + RID shader_get_default_texture_param(RID p_shader, const StringName &p_name) const override { return RID(); } + Variant shader_get_param_default(RID p_material, const StringName &p_param) const override { return Variant(); } + + RS::ShaderNativeSourceCode shader_get_native_source_code(RID p_shader) const override { return RS::ShaderNativeSourceCode(); }; + + /* COMMON MATERIAL API */ + + RID material_allocate() override { return RID(); } + void material_initialize(RID p_rid) override {} + void material_set_render_priority(RID p_material, int priority) override {} + void material_set_shader(RID p_shader_material, RID p_shader) override {} + + void material_set_param(RID p_material, const StringName &p_param, const Variant &p_value) override {} + Variant material_get_param(RID p_material, const StringName &p_param) const override { return Variant(); } + + void material_set_next_pass(RID p_material, RID p_next_material) override {} + + bool material_is_animated(RID p_material) override { return false; } + bool material_casts_shadows(RID p_material) override { return false; } + void material_get_instance_shader_parameters(RID p_material, List<InstanceShaderParam> *r_parameters) override {} + void material_update_dependency(RID p_material, DependencyTracker *p_instance) override {} + + /* MESH API */ + + RID mesh_allocate() override { return RID(); } + void mesh_initialize(RID p_rid) override {} + void mesh_set_blend_shape_count(RID p_mesh, int p_blend_shape_count) override {} + bool mesh_needs_instance(RID p_mesh, bool p_has_skeleton) override { return false; } + RID mesh_instance_create(RID p_base) override { return RID(); } + void mesh_instance_set_skeleton(RID p_mesh_instance, RID p_skeleton) override {} + void mesh_instance_set_blend_shape_weight(RID p_mesh_instance, int p_shape, float p_weight) override {} + void mesh_instance_check_for_update(RID p_mesh_instance) override {} + void update_mesh_instances() override {} + void reflection_probe_set_lod_threshold(RID p_probe, float p_ratio) override {} + float reflection_probe_get_lod_threshold(RID p_probe) const override { return 0.0; } + + void mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface) override {} + + int mesh_get_blend_shape_count(RID p_mesh) const override { return 0; } + + void mesh_set_blend_shape_mode(RID p_mesh, RS::BlendShapeMode p_mode) override {} + RS::BlendShapeMode mesh_get_blend_shape_mode(RID p_mesh) const override { return RS::BLEND_SHAPE_MODE_NORMALIZED; } + + void mesh_surface_update_region(RID p_mesh, int p_surface, int p_offset, const Vector<uint8_t> &p_data) override {} + + void mesh_surface_set_material(RID p_mesh, int p_surface, RID p_material) override {} + RID mesh_surface_get_material(RID p_mesh, int p_surface) const override { return RID(); } + + RS::SurfaceData mesh_get_surface(RID p_mesh, int p_surface) const override { return RS::SurfaceData(); } + int mesh_get_surface_count(RID p_mesh) const override { return 0; } + + void mesh_set_custom_aabb(RID p_mesh, const AABB &p_aabb) override {} + AABB mesh_get_custom_aabb(RID p_mesh) const override { return AABB(); } + + AABB mesh_get_aabb(RID p_mesh, RID p_skeleton = RID()) override { return AABB(); } + void mesh_set_shadow_mesh(RID p_mesh, RID p_shadow_mesh) override {} + void mesh_clear(RID p_mesh) override {} + + /* MULTIMESH API */ + + RID multimesh_allocate() override { return RID(); } + void multimesh_initialize(RID p_rid) override {} + void multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors = false, bool p_use_custom_data = false) override {} + int multimesh_get_instance_count(RID p_multimesh) const override { return 0; } + + void multimesh_set_mesh(RID p_multimesh, RID p_mesh) override {} + void multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform &p_transform) override {} + void multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) override {} + void multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) override {} + void multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) override {} + + RID multimesh_get_mesh(RID p_multimesh) const override { return RID(); } + AABB multimesh_get_aabb(RID p_multimesh) const override { return AABB(); } + + Transform multimesh_instance_get_transform(RID p_multimesh, int p_index) const override { return Transform(); } + Transform2D multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const override { return Transform2D(); } + Color multimesh_instance_get_color(RID p_multimesh, int p_index) const override { return Color(); } + Color multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const override { return Color(); } + void multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) override {} + Vector<float> multimesh_get_buffer(RID p_multimesh) const override { return Vector<float>(); } + + void multimesh_set_visible_instances(RID p_multimesh, int p_visible) override {} + int multimesh_get_visible_instances(RID p_multimesh) const override { return 0; } + + /* IMMEDIATE API */ + + RID immediate_allocate() override { return RID(); } + void immediate_initialize(RID p_rid) override {} + void immediate_begin(RID p_immediate, RS::PrimitiveType p_rimitive, RID p_texture = RID()) override {} + void immediate_vertex(RID p_immediate, const Vector3 &p_vertex) override {} + void immediate_normal(RID p_immediate, const Vector3 &p_normal) override {} + void immediate_tangent(RID p_immediate, const Plane &p_tangent) override {} + void immediate_color(RID p_immediate, const Color &p_color) override {} + void immediate_uv(RID p_immediate, const Vector2 &tex_uv) override {} + void immediate_uv2(RID p_immediate, const Vector2 &tex_uv) override {} + void immediate_end(RID p_immediate) override {} + void immediate_clear(RID p_immediate) override {} + void immediate_set_material(RID p_immediate, RID p_material) override {} + RID immediate_get_material(RID p_immediate) const override { return RID(); } + AABB immediate_get_aabb(RID p_immediate) const override { return AABB(); } + + /* SKELETON API */ + + RID skeleton_allocate() override { return RID(); } + void skeleton_initialize(RID p_rid) override {} + void skeleton_allocate_data(RID p_skeleton, int p_bones, bool p_2d_skeleton = false) override {} + void skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform) override {} + int skeleton_get_bone_count(RID p_skeleton) const override { return 0; } + void skeleton_bone_set_transform(RID p_skeleton, int p_bone, const Transform &p_transform) override {} + Transform skeleton_bone_get_transform(RID p_skeleton, int p_bone) const override { return Transform(); } + void skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform) override {} + Transform2D skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const override { return Transform2D(); } + + /* Light API */ + + RID directional_light_allocate() override { return RID(); } + void directional_light_initialize(RID p_rid) override {} + RID omni_light_allocate() override { return RID(); } + void omni_light_initialize(RID p_rid) override {} + RID spot_light_allocate() override { return RID(); } + void spot_light_initialize(RID p_rid) override {} + RID reflection_probe_allocate() override { return RID(); } + void reflection_probe_initialize(RID p_rid) override {} + + void light_set_color(RID p_light, const Color &p_color) override {} + void light_set_param(RID p_light, RS::LightParam p_param, float p_value) override {} + void light_set_shadow(RID p_light, bool p_enabled) override {} + void light_set_shadow_color(RID p_light, const Color &p_color) override {} + void light_set_projector(RID p_light, RID p_texture) override {} + void light_set_negative(RID p_light, bool p_enable) override {} + void light_set_cull_mask(RID p_light, uint32_t p_mask) override {} + void light_set_reverse_cull_face_mode(RID p_light, bool p_enabled) override {} + void light_set_bake_mode(RID p_light, RS::LightBakeMode p_bake_mode) override {} + void light_set_max_sdfgi_cascade(RID p_light, uint32_t p_cascade) override {} + + void light_omni_set_shadow_mode(RID p_light, RS::LightOmniShadowMode p_mode) override {} + + void light_directional_set_shadow_mode(RID p_light, RS::LightDirectionalShadowMode p_mode) override {} + void light_directional_set_blend_splits(RID p_light, bool p_enable) override {} + bool light_directional_get_blend_splits(RID p_light) const override { return false; } + void light_directional_set_shadow_depth_range_mode(RID p_light, RS::LightDirectionalShadowDepthRangeMode p_range_mode) override {} + void light_directional_set_sky_only(RID p_light, bool p_sky_only) override {} + bool light_directional_is_sky_only(RID p_light) const override { return false; } + RS::LightDirectionalShadowDepthRangeMode light_directional_get_shadow_depth_range_mode(RID p_light) const override { return RS::LIGHT_DIRECTIONAL_SHADOW_DEPTH_RANGE_STABLE; } + + RS::LightDirectionalShadowMode light_directional_get_shadow_mode(RID p_light) override { return RS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL; } + RS::LightOmniShadowMode light_omni_get_shadow_mode(RID p_light) override { return RS::LIGHT_OMNI_SHADOW_DUAL_PARABOLOID; } + + bool light_has_shadow(RID p_light) const override { return false; } + + RS::LightType light_get_type(RID p_light) const override { return RS::LIGHT_OMNI; } + AABB light_get_aabb(RID p_light) const override { return AABB(); } + float light_get_param(RID p_light, RS::LightParam p_param) override { return 0.0; } + Color light_get_color(RID p_light) override { return Color(); } + RS::LightBakeMode light_get_bake_mode(RID p_light) override { return RS::LIGHT_BAKE_DISABLED; } + uint32_t light_get_max_sdfgi_cascade(RID p_light) override { return 0; } + uint64_t light_get_version(RID p_light) const override { return 0; } + + /* PROBE API */ + + void reflection_probe_set_update_mode(RID p_probe, RS::ReflectionProbeUpdateMode p_mode) override {} + void reflection_probe_set_intensity(RID p_probe, float p_intensity) override {} + void reflection_probe_set_ambient_mode(RID p_probe, RS::ReflectionProbeAmbientMode p_mode) override {} + void reflection_probe_set_ambient_color(RID p_probe, const Color &p_color) override {} + void reflection_probe_set_ambient_energy(RID p_probe, float p_energy) override {} + void reflection_probe_set_max_distance(RID p_probe, float p_distance) override {} + void reflection_probe_set_extents(RID p_probe, const Vector3 &p_extents) override {} + void reflection_probe_set_origin_offset(RID p_probe, const Vector3 &p_offset) override {} + void reflection_probe_set_as_interior(RID p_probe, bool p_enable) override {} + void reflection_probe_set_enable_box_projection(RID p_probe, bool p_enable) override {} + void reflection_probe_set_enable_shadows(RID p_probe, bool p_enable) override {} + void reflection_probe_set_cull_mask(RID p_probe, uint32_t p_layers) override {} + void reflection_probe_set_resolution(RID p_probe, int p_resolution) override {} + + AABB reflection_probe_get_aabb(RID p_probe) const override { return AABB(); } + RS::ReflectionProbeUpdateMode reflection_probe_get_update_mode(RID p_probe) const override { return RenderingServer::REFLECTION_PROBE_UPDATE_ONCE; } + uint32_t reflection_probe_get_cull_mask(RID p_probe) const override { return 0; } + Vector3 reflection_probe_get_extents(RID p_probe) const override { return Vector3(); } + Vector3 reflection_probe_get_origin_offset(RID p_probe) const override { return Vector3(); } + float reflection_probe_get_origin_max_distance(RID p_probe) const override { return 0.0; } + bool reflection_probe_renders_shadows(RID p_probe) const override { return false; } + + void base_update_dependency(RID p_base, DependencyTracker *p_instance) override {} + void skeleton_update_dependency(RID p_base, DependencyTracker *p_instance) override {} + + /* DECAL API */ + + RID decal_allocate() override { return RID(); } + void decal_initialize(RID p_rid) override {} + void decal_set_extents(RID p_decal, const Vector3 &p_extents) override {} + void decal_set_texture(RID p_decal, RS::DecalTexture p_type, RID p_texture) override {} + void decal_set_emission_energy(RID p_decal, float p_energy) override {} + void decal_set_albedo_mix(RID p_decal, float p_mix) override {} + void decal_set_modulate(RID p_decal, const Color &p_modulate) override {} + void decal_set_cull_mask(RID p_decal, uint32_t p_layers) override {} + void decal_set_distance_fade(RID p_decal, bool p_enabled, float p_begin, float p_length) override {} + void decal_set_fade(RID p_decal, float p_above, float p_below) override {} + void decal_set_normal_fade(RID p_decal, float p_fade) override {} + + AABB decal_get_aabb(RID p_decal) const override { return AABB(); } + + /* GI PROBE API */ + + RID gi_probe_allocate() override { return RID(); } + void gi_probe_initialize(RID p_rid) override {} + void gi_probe_allocate_data(RID p_gi_probe, const Transform &p_to_cell_xform, const AABB &p_aabb, const Vector3i &p_octree_size, const Vector<uint8_t> &p_octree_cells, const Vector<uint8_t> &p_data_cells, const Vector<uint8_t> &p_distance_field, const Vector<int> &p_level_counts) override {} + + AABB gi_probe_get_bounds(RID p_gi_probe) const override { return AABB(); } + Vector3i gi_probe_get_octree_size(RID p_gi_probe) const override { return Vector3i(); } + Vector<uint8_t> gi_probe_get_octree_cells(RID p_gi_probe) const override { return Vector<uint8_t>(); } + Vector<uint8_t> gi_probe_get_data_cells(RID p_gi_probe) const override { return Vector<uint8_t>(); } + Vector<uint8_t> gi_probe_get_distance_field(RID p_gi_probe) const override { return Vector<uint8_t>(); } + + Vector<int> gi_probe_get_level_counts(RID p_gi_probe) const override { return Vector<int>(); } + Transform gi_probe_get_to_cell_xform(RID p_gi_probe) const override { return Transform(); } + + void gi_probe_set_dynamic_range(RID p_gi_probe, float p_range) override {} + float gi_probe_get_dynamic_range(RID p_gi_probe) const override { return 0; } + + void gi_probe_set_propagation(RID p_gi_probe, float p_range) override {} + float gi_probe_get_propagation(RID p_gi_probe) const override { return 0; } + + void gi_probe_set_energy(RID p_gi_probe, float p_range) override {} + float gi_probe_get_energy(RID p_gi_probe) const override { return 0.0; } + + void gi_probe_set_ao(RID p_gi_probe, float p_ao) override {} + float gi_probe_get_ao(RID p_gi_probe) const override { return 0; } + + void gi_probe_set_ao_size(RID p_gi_probe, float p_strength) override {} + float gi_probe_get_ao_size(RID p_gi_probe) const override { return 0; } + + void gi_probe_set_bias(RID p_gi_probe, float p_range) override {} + float gi_probe_get_bias(RID p_gi_probe) const override { return 0.0; } + + void gi_probe_set_normal_bias(RID p_gi_probe, float p_range) override {} + float gi_probe_get_normal_bias(RID p_gi_probe) const override { return 0.0; } + + void gi_probe_set_interior(RID p_gi_probe, bool p_enable) override {} + bool gi_probe_is_interior(RID p_gi_probe) const override { return false; } + + void gi_probe_set_use_two_bounces(RID p_gi_probe, bool p_enable) override {} + bool gi_probe_is_using_two_bounces(RID p_gi_probe) const override { return false; } + + void gi_probe_set_anisotropy_strength(RID p_gi_probe, float p_strength) override {} + float gi_probe_get_anisotropy_strength(RID p_gi_probe) const override { return 0; } + + uint32_t gi_probe_get_version(RID p_gi_probe) override { return 0; } + + /* LIGHTMAP CAPTURE */ + RID lightmap_allocate() override { return RID(); } + void lightmap_initialize(RID p_rid) override {} + void lightmap_set_textures(RID p_lightmap, RID p_light, bool p_uses_spherical_haromics) override {} + void lightmap_set_probe_bounds(RID p_lightmap, const AABB &p_bounds) override {} + void lightmap_set_probe_interior(RID p_lightmap, bool p_interior) override {} + void lightmap_set_probe_capture_data(RID p_lightmap, const PackedVector3Array &p_points, const PackedColorArray &p_point_sh, const PackedInt32Array &p_tetrahedra, const PackedInt32Array &p_bsp_tree) override {} + PackedVector3Array lightmap_get_probe_capture_points(RID p_lightmap) const override { return PackedVector3Array(); } + PackedColorArray lightmap_get_probe_capture_sh(RID p_lightmap) const override { return PackedColorArray(); } + PackedInt32Array lightmap_get_probe_capture_tetrahedra(RID p_lightmap) const override { return PackedInt32Array(); } + PackedInt32Array lightmap_get_probe_capture_bsp_tree(RID p_lightmap) const override { return PackedInt32Array(); } + AABB lightmap_get_aabb(RID p_lightmap) const override { return AABB(); } + void lightmap_tap_sh_light(RID p_lightmap, const Vector3 &p_point, Color *r_sh) override {} + bool lightmap_is_interior(RID p_lightmap) const override { return false; } + void lightmap_set_probe_capture_update_speed(float p_speed) override {} + float lightmap_get_probe_capture_update_speed() const override { return 0; } + + /* OCCLUDER */ + + void occluder_set_mesh(RID p_occluder, const PackedVector3Array &p_vertices, const PackedInt32Array &p_indices) {} + + /* PARTICLES */ + + RID particles_allocate() override { return RID(); } + void particles_initialize(RID p_rid) override {} + void particles_set_mode(RID p_particles, RS::ParticlesMode p_mode) override {} + void particles_emit(RID p_particles, const Transform &p_transform, const Vector3 &p_velocity, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags) override {} + void particles_set_emitting(RID p_particles, bool p_emitting) override {} + void particles_set_amount(RID p_particles, int p_amount) override {} + void particles_set_lifetime(RID p_particles, float p_lifetime) override {} + void particles_set_one_shot(RID p_particles, bool p_one_shot) override {} + void particles_set_pre_process_time(RID p_particles, float p_time) override {} + void particles_set_explosiveness_ratio(RID p_particles, float p_ratio) override {} + void particles_set_randomness_ratio(RID p_particles, float p_ratio) override {} + void particles_set_custom_aabb(RID p_particles, const AABB &p_aabb) override {} + void particles_set_speed_scale(RID p_particles, float p_scale) override {} + void particles_set_use_local_coordinates(RID p_particles, bool p_enable) override {} + void particles_set_process_material(RID p_particles, RID p_material) override {} + void particles_set_fixed_fps(RID p_particles, int p_fps) override {} + void particles_set_interpolate(RID p_particles, bool p_enable) override {} + void particles_set_fractional_delta(RID p_particles, bool p_enable) override {} + void particles_set_subemitter(RID p_particles, RID p_subemitter_particles) override {} + void particles_set_view_axis(RID p_particles, const Vector3 &p_axis, const Vector3 &p_up_axis) override {} + void particles_set_collision_base_size(RID p_particles, float p_size) override {} + + void particles_set_transform_align(RID p_particles, RS::ParticlesTransformAlign p_transform_align) override {} + + void particles_set_trails(RID p_particles, bool p_enable, float p_length) override {} + void particles_set_trail_bind_poses(RID p_particles, const Vector<Transform> &p_bind_poses) override {} + + void particles_restart(RID p_particles) override {} + + void particles_set_draw_order(RID p_particles, RS::ParticlesDrawOrder p_order) override {} + + void particles_set_draw_passes(RID p_particles, int p_count) override {} + void particles_set_draw_pass_mesh(RID p_particles, int p_pass, RID p_mesh) override {} + + void particles_request_process(RID p_particles) override {} + AABB particles_get_current_aabb(RID p_particles) override { return AABB(); } + AABB particles_get_aabb(RID p_particles) const override { return AABB(); } + + void particles_set_emission_transform(RID p_particles, const Transform &p_transform) override {} + + bool particles_get_emitting(RID p_particles) override { return false; } + int particles_get_draw_passes(RID p_particles) const override { return 0; } + RID particles_get_draw_pass_mesh(RID p_particles, int p_pass) const override { return RID(); } + + void particles_add_collision(RID p_particles, RID p_instance) override {} + void particles_remove_collision(RID p_particles, RID p_instance) override {} + + void particles_set_canvas_sdf_collision(RID p_particles, bool p_enable, const Transform2D &p_xform, const Rect2 &p_to_screen, RID p_texture) override {} + + void update_particles() override {} + + /* PARTICLES COLLISION */ + + RID particles_collision_allocate() override { return RID(); } + void particles_collision_initialize(RID p_rid) override {} + void particles_collision_set_collision_type(RID p_particles_collision, RS::ParticlesCollisionType p_type) override {} + void particles_collision_set_cull_mask(RID p_particles_collision, uint32_t p_cull_mask) override {} + void particles_collision_set_sphere_radius(RID p_particles_collision, float p_radius) override {} + void particles_collision_set_box_extents(RID p_particles_collision, const Vector3 &p_extents) override {} + void particles_collision_set_attractor_strength(RID p_particles_collision, float p_strength) override {} + void particles_collision_set_attractor_directionality(RID p_particles_collision, float p_directionality) override {} + void particles_collision_set_attractor_attenuation(RID p_particles_collision, float p_curve) override {} + void particles_collision_set_field_texture(RID p_particles_collision, RID p_texture) override {} + void particles_collision_height_field_update(RID p_particles_collision) override {} + void particles_collision_set_height_field_resolution(RID p_particles_collision, RS::ParticlesCollisionHeightfieldResolution p_resolution) override {} + AABB particles_collision_get_aabb(RID p_particles_collision) const override { return AABB(); } + bool particles_collision_is_heightfield(RID p_particles_collision) const override { return false; } + RID particles_collision_get_heightfield_framebuffer(RID p_particles_collision) const override { return RID(); } + + RID particles_collision_instance_create(RID p_collision) override { return RID(); }; + void particles_collision_instance_set_transform(RID p_collision_instance, const Transform &p_transform) override{}; + void particles_collision_instance_set_active(RID p_collision_instance, bool p_active) override{}; + + /* GLOBAL VARIABLES */ + + void global_variable_add(const StringName &p_name, RS::GlobalVariableType p_type, const Variant &p_value) override {} + void global_variable_remove(const StringName &p_name) override {} + Vector<StringName> global_variable_get_list() const override { return Vector<StringName>(); } + + void global_variable_set(const StringName &p_name, const Variant &p_value) override {} + void global_variable_set_override(const StringName &p_name, const Variant &p_value) override {} + Variant global_variable_get(const StringName &p_name) const override { return Variant(); } + RS::GlobalVariableType global_variable_get_type(const StringName &p_name) const override { return RS::GLOBAL_VAR_TYPE_MAX; } + + void global_variables_load_settings(bool p_load_textures = true) override {} + void global_variables_clear() override {} + + int32_t global_variables_instance_allocate(RID p_instance) override { return 0; } + void global_variables_instance_free(RID p_instance) override {} + void global_variables_instance_update(RID p_instance, int p_index, const Variant &p_value) override {} + + bool particles_is_inactive(RID p_particles) const override { return false; } + + /* RENDER TARGET */ + + RID render_target_create() override { return RID(); } + void render_target_set_position(RID p_render_target, int p_x, int p_y) override {} + void render_target_set_size(RID p_render_target, int p_width, int p_height) override {} + RID render_target_get_texture(RID p_render_target) override { return RID(); } + void render_target_set_external_texture(RID p_render_target, unsigned int p_texture_id) override {} + void render_target_set_flag(RID p_render_target, RenderTargetFlags p_flag, bool p_value) override {} + bool render_target_was_used(RID p_render_target) override { return false; } + void render_target_set_as_unused(RID p_render_target) override {} + + void render_target_request_clear(RID p_render_target, const Color &p_clear_color) override {} + bool render_target_is_clear_requested(RID p_render_target) override { return false; } + Color render_target_get_clear_request_color(RID p_render_target) override { return Color(); } + void render_target_disable_clear_request(RID p_render_target) override {} + void render_target_do_clear_request(RID p_render_target) override {} + + void render_target_set_sdf_size_and_scale(RID p_render_target, RS::ViewportSDFOversize p_size, RS::ViewportSDFScale p_scale) override {} + Rect2i render_target_get_sdf_rect(RID p_render_target) const override { return Rect2i(); } + void render_target_mark_sdf_enabled(RID p_render_target, bool p_enabled) override {} + + RS::InstanceType get_base_type(RID p_rid) const override { return RS::INSTANCE_NONE; } + bool free(RID p_rid) override { + if (texture_owner.owns(p_rid)) { + // delete the texture + DummyTexture *texture = texture_owner.getornull(p_rid); + texture_owner.free(p_rid); + memdelete(texture); + } + return true; + } + + bool has_os_feature(const String &p_feature) const override { return false; } + + void update_dirty_resources() override {} + + void set_debug_generate_wireframes(bool p_generate) override {} + + void render_info_begin_capture() override {} + void render_info_end_capture() override {} + int get_captured_render_info(RS::RenderInfo p_info) override { return 0; } + + uint64_t get_render_info(RS::RenderInfo p_info) override { return 0; } + String get_video_adapter_name() const override { return String(); } + String get_video_adapter_vendor() const override { return String(); } + + static RendererStorage *base_singleton; + + void capture_timestamps_begin() override {} + void capture_timestamp(const String &p_name) override {} + uint32_t get_captured_timestamps_count() const override { return 0; } + uint64_t get_captured_timestamps_frame() const override { return 0; } + uint64_t get_captured_timestamp_gpu_time(uint32_t p_index) const override { return 0; } + uint64_t get_captured_timestamp_cpu_time(uint32_t p_index) const override { return 0; } + String get_captured_timestamp_name(uint32_t p_index) const override { return String(); } + + RasterizerStorageDummy() {} + ~RasterizerStorageDummy() {} +}; + +class RasterizerCanvasDummy : public RendererCanvasRender { +public: + PolygonID request_polygon(const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), const Vector<int> &p_bones = Vector<int>(), const Vector<float> &p_weights = Vector<float>()) override { return 0; } + void free_polygon(PolygonID p_polygon) override {} + + void canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_list, const Transform2D &p_canvas_transform, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, bool &r_sdf_used) override {} + void canvas_debug_viewport_shadows(Light *p_lights_with_shadow) override {} + + RID light_create() override { return RID(); } + void light_set_texture(RID p_rid, RID p_texture) override {} + void light_set_use_shadow(RID p_rid, bool p_enable) override {} + void light_update_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders) override {} + void light_update_directional_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_cull_distance, const Rect2 &p_clip_rect, LightOccluderInstance *p_occluders) override {} + + void render_sdf(RID p_render_target, LightOccluderInstance *p_occluders) override {} + RID occluder_polygon_create() override { return RID(); } + void occluder_polygon_set_shape(RID p_occluder, const Vector<Vector2> &p_points, bool p_closed) override {} + void occluder_polygon_set_cull_mode(RID p_occluder, RS::CanvasOccluderPolygonCullMode p_mode) override {} + void set_shadow_texture_size(int p_size) override {} + + void draw_window_margins(int *p_margins, RID *p_margin_textures) override {} + + bool free(RID p_rid) override { return true; } + void update() override {} + + RasterizerCanvasDummy() {} + ~RasterizerCanvasDummy() {} +}; + +class RasterizerDummy : public RendererCompositor { +private: + uint64_t frame = 1; + float delta = 0; + +protected: + RasterizerCanvasDummy canvas; + RasterizerStorageDummy storage; + RasterizerSceneDummy scene; + +public: + RendererStorage *get_storage() override { return &storage; } + RendererCanvasRender *get_canvas() override { return &canvas; } + RendererSceneRender *get_scene() override { return &scene; } + + void set_boot_image(const Ref<Image> &p_image, const Color &p_color, bool p_scale, bool p_use_filter = true) override {} + + void initialize() override {} + void begin_frame(double frame_step) override { + frame++; + delta = frame_step; + } + + void prepare_for_blitting_render_targets() override {} + void blit_render_targets_to_screen(int p_screen, const BlitToScreen *p_render_targets, int p_amount) override {} + + void end_frame(bool p_swap_buffers) override { + if (p_swap_buffers) { + DisplayServer::get_singleton()->swap_buffers(); + } + } + + void finalize() override {} + + static RendererCompositor *_create_current() { + return memnew(RasterizerDummy); + } + + static void make_current() { + _create_func = _create_current; + } + + bool is_low_end() const override { return true; } + uint64_t get_frame_number() const override { return frame; } + float get_frame_delta_time() const override { return delta; } + + RasterizerDummy() {} + ~RasterizerDummy() {} +}; + +#endif // RASTERIZER_DUMMY_H diff --git a/servers/rendering/renderer_canvas_render.h b/servers/rendering/renderer_canvas_render.h index 0e9ef616cb..0266e137c0 100644 --- a/servers/rendering/renderer_canvas_render.h +++ b/servers/rendering/renderer_canvas_render.h @@ -420,7 +420,6 @@ public: if (found_xform) { r = xf.xform(r); - found_xform = false; } if (first) { diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp index c96c541461..5a26b5abbb 100644 --- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp @@ -30,6 +30,7 @@ #include "scene_shader_forward_clustered.h" #include "core/config/project_settings.h" +#include "core/math/math_defs.h" #include "render_forward_clustered.h" using namespace RendererSceneRenderImplementation; @@ -608,6 +609,9 @@ void SceneShaderForwardClustered::init(RendererStorageRD *p_storage, const Strin //builtins actions.renames["TIME"] = "scene_data.time"; + actions.renames["PI"] = _MKSTR(Math_PI); + actions.renames["TAU"] = _MKSTR(Math_TAU); + actions.renames["E"] = _MKSTR(Math_E); actions.renames["VIEWPORT_SIZE"] = "scene_data.viewport_size"; actions.renames["FRAGCOORD"] = "gl_FragCoord"; diff --git a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp index b9220cc514..883273c8a5 100644 --- a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp @@ -30,6 +30,7 @@ #include "scene_shader_forward_mobile.h" #include "core/config/project_settings.h" +#include "core/math/math_defs.h" #include "render_forward_mobile.h" using namespace RendererSceneRenderImplementation; @@ -625,6 +626,9 @@ void SceneShaderForwardMobile::init(RendererStorageRD *p_storage, const String p //builtins actions.renames["TIME"] = "scene_data.time"; + actions.renames["PI"] = _MKSTR(Math_PI); + actions.renames["TAU"] = _MKSTR(Math_TAU); + actions.renames["E"] = _MKSTR(Math_E); actions.renames["VIEWPORT_SIZE"] = "scene_data.viewport_size"; actions.renames["FRAGCOORD"] = "gl_FragCoord"; diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp index f448698976..70c1705bff 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp @@ -31,8 +31,10 @@ #include "renderer_canvas_render_rd.h" #include "core/config/project_settings.h" #include "core/math/geometry_2d.h" +#include "core/math/math_defs.h" #include "core/math/math_funcs.h" #include "renderer_compositor_rd.h" +#include "servers/rendering/rendering_server_default.h" void RendererCanvasRenderRD::_update_transform_2d_to_mat4(const Transform2D &p_transform, float *p_mat4) { p_mat4[0] = p_transform.elements[0][0]; @@ -390,7 +392,7 @@ void RendererCanvasRenderRD::_bind_canvas_texture(RD::DrawListID p_draw_list, RI r_last_texture = p_texture; } -void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, const Item *p_item, RD::FramebufferFormatID p_framebuffer_format, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, Light *p_lights, PipelineVariants *p_pipeline_variants) { +void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_render_target, const Item *p_item, RD::FramebufferFormatID p_framebuffer_format, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, Light *p_lights, PipelineVariants *p_pipeline_variants) { //create an empty push constant RS::CanvasItemTextureFilter current_filter = default_filter; @@ -747,9 +749,15 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, const Item } else if (c->type == Item::Command::TYPE_PARTICLES) { const Item::CommandParticles *pt = static_cast<const Item::CommandParticles *>(c); ERR_BREAK(storage->particles_get_mode(pt->particles) != RS::PARTICLES_MODE_2D); + storage->particles_request_process(pt->particles); + if (storage->particles_is_inactive(pt->particles)) { break; } + + RenderingServerDefault::redraw_request(); // active particles means redraw request + + bool local_coords = true; int dpc = storage->particles_get_draw_passes(pt->particles); if (dpc == 0) { break; //nothing to draw @@ -768,6 +776,30 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, const Item mesh = storage->particles_get_draw_pass_mesh(pt->particles, 0); //higher ones are ignored texture = pt->texture; + + if (storage->particles_has_collision(pt->particles) && storage->render_target_is_sdf_enabled(p_render_target)) { + //pass collision information + Transform2D xform; + if (local_coords) { + xform = p_item->final_transform; + } else { + xform = p_canvas_transform_inverse; + } + + RID sdf_texture = storage->render_target_get_sdf_texture(p_render_target); + + Rect2 to_screen; + { + Rect2 sdf_rect = storage->render_target_get_sdf_rect(p_render_target); + + to_screen.size = Vector2(1.0 / sdf_rect.size.width, 1.0 / sdf_rect.size.height); + to_screen.position = -sdf_rect.position * to_screen.size; + } + + storage->particles_set_canvas_sdf_collision(pt->particles, true, xform, to_screen, sdf_texture); + } else { + storage->particles_set_canvas_sdf_collision(pt->particles, false, Transform2D(), Rect2(), RID()); + } } if (mesh.is_null()) { @@ -1052,7 +1084,7 @@ void RendererCanvasRenderRD::_render_items(RID p_to_render_target, int p_item_co } } - _render_item(draw_list, ci, fb_format, canvas_transform_inverse, current_clip, p_lights, pipeline_variants); + _render_item(draw_list, p_to_render_target, ci, fb_format, canvas_transform_inverse, current_clip, p_lights, pipeline_variants); prev_material = material; } @@ -1280,6 +1312,7 @@ void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p Item *canvas_group_owner = nullptr; bool update_skeletons = false; + bool time_used = false; while (ci) { if (ci->copy_back_buffer && canvas_group_owner == nullptr) { @@ -1305,6 +1338,9 @@ void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p if (md->shader_data->uses_sdf) { r_sdf_used = true; } + if (md->shader_data->uses_time) { + time_used = true; + } if (md->last_frame != RendererCompositorRD::singleton->get_frame_number()) { md->last_frame = RendererCompositorRD::singleton->get_frame_number(); if (!RD::get_singleton()->uniform_set_is_valid(md->uniform_set)) { @@ -1401,6 +1437,10 @@ void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p ci = ci->next; } + + if (time_used) { + RenderingServerDefault::redraw_request(); + } } RID RendererCanvasRenderRD::light_create() { @@ -1877,6 +1917,7 @@ void RendererCanvasRenderRD::ShaderData::set_code(const String &p_code) { uniforms.clear(); uses_screen_texture = false; uses_sdf = false; + uses_time = false; if (code == String()) { return; //just invalid, but no error @@ -1901,6 +1942,7 @@ void RendererCanvasRenderRD::ShaderData::set_code(const String &p_code) { actions.usage_flag_pointers["SCREEN_TEXTURE"] = &uses_screen_texture; actions.usage_flag_pointers["texture_sdf"] = &uses_sdf; + actions.usage_flag_pointers["TIME"] = &uses_time; actions.uniforms = &uniforms; @@ -2367,6 +2409,9 @@ RendererCanvasRenderRD::RendererCanvasRenderRD(RendererStorageRD *p_storage) { actions.renames["CANVAS_MATRIX"] = "canvas_data.canvas_transform"; actions.renames["SCREEN_MATRIX"] = "canvas_data.screen_transform"; actions.renames["TIME"] = "canvas_data.time"; + actions.renames["PI"] = _MKSTR(Math_PI); + actions.renames["TAU"] = _MKSTR(Math_TAU); + actions.renames["E"] = _MKSTR(Math_E); actions.renames["AT_LIGHT_PASS"] = "false"; actions.renames["INSTANCE_CUSTOM"] = "instance_custom"; diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h index 8129cc6c9b..890a4e3649 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h @@ -176,6 +176,7 @@ class RendererCanvasRenderRD : public RendererCanvasRender { bool uses_screen_texture = false; bool uses_sdf = false; + bool uses_time = false; virtual void set_code(const String &p_Code); virtual void set_default_texture_param(const StringName &p_name, RID p_texture); @@ -425,7 +426,7 @@ class RendererCanvasRenderRD : public RendererCanvasRender { RID _create_base_uniform_set(RID p_to_render_target, bool p_backbuffer); inline void _bind_canvas_texture(RD::DrawListID p_draw_list, RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID &r_last_texture, PushConstant &push_constant, Size2 &r_texpixel_size); //recursive, so regular inline used instead. - void _render_item(RenderingDevice::DrawListID p_draw_list, const Item *p_item, RenderingDevice::FramebufferFormatID p_framebuffer_format, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, Light *p_lights, PipelineVariants *p_pipeline_variants); + void _render_item(RenderingDevice::DrawListID p_draw_list, RID p_render_target, const Item *p_item, RenderingDevice::FramebufferFormatID p_framebuffer_format, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, Light *p_lights, PipelineVariants *p_pipeline_variants); void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool p_to_backbuffer = false); _FORCE_INLINE_ void _update_transform_2d_to_mat2x4(const Transform2D &p_transform, float *p_mat2x4); diff --git a/servers/rendering/renderer_rd/renderer_compositor_rd.cpp b/servers/rendering/renderer_rd/renderer_compositor_rd.cpp index 0012ba9c27..1337d36762 100644 --- a/servers/rendering/renderer_rd/renderer_compositor_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_compositor_rd.cpp @@ -31,6 +31,7 @@ #include "renderer_compositor_rd.h" #include "core/config/project_settings.h" +#include "core/os/dir_access.h" void RendererCompositorRD::prepare_for_blitting_render_targets() { RD::get_singleton()->prepare_screen_for_drawing(); @@ -155,6 +156,43 @@ void RendererCompositorRD::finalize() { RendererCompositorRD *RendererCompositorRD::singleton = nullptr; RendererCompositorRD::RendererCompositorRD() { + { + String shader_cache_dir = Engine::get_singleton()->get_shader_cache_path(); + if (shader_cache_dir == String()) { + shader_cache_dir = "user://"; + } + DirAccessRef da = DirAccess::open(shader_cache_dir); + if (!da) { + ERR_PRINT("Can't create shader cache folder, no shader caching will happen: " + shader_cache_dir); + } else { + Error err = da->change_dir("shader_cache"); + if (err != OK) { + err = da->make_dir("shader_cache"); + } + if (err != OK) { + ERR_PRINT("Can't create shader cache folder, no shader caching will happen: " + shader_cache_dir); + } else { + shader_cache_dir = shader_cache_dir.plus_file("shader_cache"); + + bool shader_cache_enabled = GLOBAL_GET("rendering/shader_compiler/shader_cache/enabled"); + if (!Engine::get_singleton()->is_editor_hint() && !shader_cache_enabled) { + shader_cache_dir = String(); //disable only if not editor + } + + if (shader_cache_dir != String()) { + bool compress = GLOBAL_GET("rendering/shader_compiler/shader_cache/compress"); + bool use_zstd = GLOBAL_GET("rendering/shader_compiler/shader_cache/use_zstd_compression"); + bool strip_debug = GLOBAL_GET("rendering/shader_compiler/shader_cache/strip_debug"); + + ShaderRD::set_shader_cache_dir(shader_cache_dir); + ShaderRD::set_shader_cache_save_compressed(compress); + ShaderRD::set_shader_cache_save_compressed_zstd(use_zstd); + ShaderRD::set_shader_cache_save_debug(!strip_debug); + } + } + } + } + singleton = this; time = 0; @@ -171,3 +209,7 @@ RendererCompositorRD::RendererCompositorRD() { scene = memnew(RendererSceneRenderImplementation::RenderForwardClustered(storage)); } } + +RendererCompositorRD::~RendererCompositorRD() { + ShaderRD::set_shader_cache_dir(String()); +} diff --git a/servers/rendering/renderer_rd/renderer_compositor_rd.h b/servers/rendering/renderer_rd/renderer_compositor_rd.h index 52552f7ee3..7a78322051 100644 --- a/servers/rendering/renderer_rd/renderer_compositor_rd.h +++ b/servers/rendering/renderer_rd/renderer_compositor_rd.h @@ -118,6 +118,6 @@ public: static RendererCompositorRD *singleton; RendererCompositorRD(); - ~RendererCompositorRD() {} + ~RendererCompositorRD(); }; #endif // RASTERIZER_RD_H diff --git a/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp index 54c6e81110..966a1c6815 100644 --- a/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp @@ -30,6 +30,7 @@ #include "renderer_scene_sky_rd.h" #include "core/config/project_settings.h" +#include "core/math/math_defs.h" #include "renderer_scene_render_rd.h" #include "servers/rendering/rendering_server_default.h" @@ -710,6 +711,9 @@ void RendererSceneSkyRD::init(RendererStorageRD *p_storage) { actions.renames["SKY_COORDS"] = "panorama_coords"; actions.renames["SCREEN_UV"] = "uv"; actions.renames["TIME"] = "params.time"; + actions.renames["PI"] = _MKSTR(Math_PI); + actions.renames["TAU"] = _MKSTR(Math_TAU); + actions.renames["E"] = _MKSTR(Math_E); actions.renames["HALF_RES_COLOR"] = "half_res_color"; actions.renames["QUARTER_RES_COLOR"] = "quarter_res_color"; actions.renames["RADIANCE"] = "radiance"; diff --git a/servers/rendering/renderer_rd/renderer_storage_rd.cpp b/servers/rendering/renderer_rd/renderer_storage_rd.cpp index f419875d58..7a26e40c0a 100644 --- a/servers/rendering/renderer_rd/renderer_storage_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_storage_rd.cpp @@ -33,6 +33,7 @@ #include "core/config/engine.h" #include "core/config/project_settings.h" #include "core/io/resource_loader.h" +#include "core/math/math_defs.h" #include "renderer_compositor_rd.h" #include "servers/rendering/shader_language.h" @@ -4305,6 +4306,15 @@ void RendererStorageRD::particles_remove_collision(RID p_particles, RID p_partic particles->collisions.erase(p_particles_collision_instance); } +void RendererStorageRD::particles_set_canvas_sdf_collision(RID p_particles, bool p_enable, const Transform2D &p_xform, const Rect2 &p_to_screen, RID p_texture) { + Particles *particles = particles_owner.getornull(p_particles); + ERR_FAIL_COND(!particles); + particles->has_sdf_collision = p_enable; + particles->sdf_collision_transform = p_xform; + particles->sdf_collision_to_screen = p_to_screen; + particles->sdf_collision_texture = p_texture; +} + void RendererStorageRD::_particles_process(Particles *p_particles, float p_delta) { if (p_particles->particles_material_uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(p_particles->particles_material_uniform_set)) { Vector<RD::Uniform> uniforms; @@ -4410,6 +4420,50 @@ void RendererStorageRD::_particles_process(Particles *p_particles, float p_delta if (p_particles->use_local_coords) { to_particles = p_particles->emission_transform.affine_inverse(); } + + if (p_particles->has_sdf_collision && RD::get_singleton()->texture_is_valid(p_particles->sdf_collision_texture)) { + //2D collision + + Transform2D xform = p_particles->sdf_collision_transform; //will use dotproduct manually so invert beforehand + Transform2D revert = xform.affine_inverse(); + frame_params.collider_count = 1; + frame_params.colliders[0].transform[0] = xform.elements[0][0]; + frame_params.colliders[0].transform[1] = xform.elements[0][1]; + frame_params.colliders[0].transform[2] = 0; + frame_params.colliders[0].transform[3] = xform.elements[2][0]; + + frame_params.colliders[0].transform[4] = xform.elements[1][0]; + frame_params.colliders[0].transform[5] = xform.elements[1][1]; + frame_params.colliders[0].transform[6] = 0; + frame_params.colliders[0].transform[7] = xform.elements[2][1]; + + frame_params.colliders[0].transform[8] = revert.elements[0][0]; + frame_params.colliders[0].transform[9] = revert.elements[0][1]; + frame_params.colliders[0].transform[10] = 0; + frame_params.colliders[0].transform[11] = revert.elements[2][0]; + + frame_params.colliders[0].transform[12] = revert.elements[1][0]; + frame_params.colliders[0].transform[13] = revert.elements[1][1]; + frame_params.colliders[0].transform[14] = 0; + frame_params.colliders[0].transform[15] = revert.elements[2][1]; + + frame_params.colliders[0].extents[0] = p_particles->sdf_collision_to_screen.size.x; + frame_params.colliders[0].extents[1] = p_particles->sdf_collision_to_screen.size.y; + frame_params.colliders[0].extents[2] = p_particles->sdf_collision_to_screen.position.x; + frame_params.colliders[0].scale = p_particles->sdf_collision_to_screen.position.y; + frame_params.colliders[0].texture_index = 0; + frame_params.colliders[0].type = ParticlesFrameParams::COLLISION_TYPE_2D_SDF; + + collision_heightmap_texture = p_particles->sdf_collision_texture; + + //replace in all other history frames where used because parameters are no longer valid if screen moves + for (uint32_t i = 1; i < p_particles->frame_history.size(); i++) { + if (p_particles->frame_history[i].collider_count > 0 && p_particles->frame_history[i].colliders[0].type == ParticlesFrameParams::COLLISION_TYPE_2D_SDF) { + p_particles->frame_history[i].colliders[0] = frame_params.colliders[0]; + } + } + } + uint32_t collision_3d_textures_used = 0; for (const Set<RID>::Element *E = p_particles->collisions.front(); E; E = E->next()) { ParticlesCollisionInstance *pci = particles_collision_instance_owner.getornull(E->get()); @@ -4657,6 +4711,8 @@ void RendererStorageRD::_particles_process(Particles *p_particles, float p_delta ERR_FAIL_COND(!m); + p_particles->has_collision_cache = m->shader_data->uses_collision; + //todo should maybe compute all particle systems together? RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, m->shader_data->pipeline); @@ -4740,6 +4796,11 @@ void RendererStorageRD::particles_set_view_axis(RID p_particles, const Vector3 & copy_push_constant.trail_total = 1; copy_push_constant.frame_delta = 0.0; } + + copy_push_constant.order_by_lifetime = (particles->draw_order == RS::PARTICLES_DRAW_ORDER_LIFETIME || particles->draw_order == RS::PARTICLES_DRAW_ORDER_REVERSE_LIFETIME); + copy_push_constant.lifetime_split = MIN(particles->amount * particles->phase, particles->amount - 1); + copy_push_constant.lifetime_reverse = particles->draw_order == RS::PARTICLES_DRAW_ORDER_REVERSE_LIFETIME; + copy_push_constant.frame_remainder = particles->interpolate ? particles->frame_remainder : 0.0; copy_push_constant.total_particles = particles->amount; @@ -5019,6 +5080,10 @@ void RendererStorageRD::update_particles() { copy_push_constant.frame_delta = 0.0; } + copy_push_constant.order_by_lifetime = (particles->draw_order == RS::PARTICLES_DRAW_ORDER_LIFETIME || particles->draw_order == RS::PARTICLES_DRAW_ORDER_REVERSE_LIFETIME); + copy_push_constant.lifetime_split = MIN(particles->amount * particles->phase, particles->amount - 1); + copy_push_constant.lifetime_reverse = particles->draw_order == RS::PARTICLES_DRAW_ORDER_REVERSE_LIFETIME; + RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, particles_shader.copy_pipelines[particles->mode == RS::PARTICLES_MODE_2D ? ParticlesShader::COPY_MODE_FILL_INSTANCES_2D : ParticlesShader::COPY_MODE_FILL_INSTANCES]); RD::get_singleton()->compute_list_bind_uniform_set(compute_list, particles->particles_copy_uniform_set, 0); @@ -5049,6 +5114,7 @@ void RendererStorageRD::ParticlesShaderData::set_code(const String &p_code) { valid = false; ubo_size = 0; uniforms.clear(); + uses_collision = false; if (code == String()) { return; //just invalid, but no error @@ -5068,6 +5134,8 @@ void RendererStorageRD::ParticlesShaderData::set_code(const String &p_code) { actions.usage_flag_pointers["TIME"] = &uses_time; */ + actions.usage_flag_pointers["COLLIDED"] = &uses_collision; + actions.uniforms = &uniforms; Error err = base_singleton->particles_shader.compiler.compile(RS::SHADER_PARTICLES, code, &actions, path, gen_code); @@ -7133,6 +7201,20 @@ Rect2i RendererStorageRD::render_target_get_sdf_rect(RID p_render_target) const return _render_target_get_sdf_rect(rt); } +void RendererStorageRD::render_target_mark_sdf_enabled(RID p_render_target, bool p_enabled) { + RenderTarget *rt = render_target_owner.getornull(p_render_target); + ERR_FAIL_COND(!rt); + + rt->sdf_enabled = p_enabled; +} + +bool RendererStorageRD::render_target_is_sdf_enabled(RID p_render_target) const { + const RenderTarget *rt = render_target_owner.getornull(p_render_target); + ERR_FAIL_COND_V(!rt, false); + + return rt->sdf_enabled; +} + RID RendererStorageRD::render_target_get_sdf_texture(RID p_render_target) { RenderTarget *rt = render_target_owner.getornull(p_render_target); ERR_FAIL_COND_V(!rt, RID()); @@ -7200,7 +7282,7 @@ void RendererStorageRD::_render_target_allocate_sdf(RenderTarget *rt) { rt->process_size.x = MAX(rt->process_size.x, 1); rt->process_size.y = MAX(rt->process_size.y, 1); - tformat.format = RD::DATA_FORMAT_R16G16_UINT; + tformat.format = RD::DATA_FORMAT_R16G16_SINT; tformat.width = rt->process_size.width; tformat.height = rt->process_size.height; tformat.usage_bits = RD::TEXTURE_USAGE_STORAGE_BIT; @@ -7208,7 +7290,7 @@ void RendererStorageRD::_render_target_allocate_sdf(RenderTarget *rt) { rt->sdf_buffer_process[0] = RD::get_singleton()->texture_create(tformat, RD::TextureView()); rt->sdf_buffer_process[1] = RD::get_singleton()->texture_create(tformat, RD::TextureView()); - tformat.format = RD::DATA_FORMAT_R16_UNORM; + tformat.format = RD::DATA_FORMAT_R16_SNORM; tformat.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT; rt->sdf_buffer_read = RD::get_singleton()->texture_create(tformat, RD::TextureView()); @@ -9144,6 +9226,9 @@ RendererStorageRD::RendererStorageRD() { actions.renames["CUSTOM"] = "PARTICLE.custom"; actions.renames["TRANSFORM"] = "PARTICLE.xform"; actions.renames["TIME"] = "FRAME.time"; + actions.renames["PI"] = _MKSTR(Math_PI); + actions.renames["TAU"] = _MKSTR(Math_TAU); + actions.renames["E"] = _MKSTR(Math_E); actions.renames["LIFETIME"] = "params.lifetime"; actions.renames["DELTA"] = "local_delta"; actions.renames["NUMBER"] = "particle_number"; diff --git a/servers/rendering/renderer_rd/renderer_storage_rd.h b/servers/rendering/renderer_rd/renderer_storage_rd.h index 49f7f3dba6..67fbeb3008 100644 --- a/servers/rendering/renderer_rd/renderer_storage_rd.h +++ b/servers/rendering/renderer_rd/renderer_storage_rd.h @@ -638,7 +638,9 @@ private: COLLISION_TYPE_SPHERE, COLLISION_TYPE_BOX, COLLISION_TYPE_SDF, - COLLISION_TYPE_HEIGHT_FIELD + COLLISION_TYPE_HEIGHT_FIELD, + COLLISION_TYPE_2D_SDF, + }; struct Collider { @@ -710,6 +712,13 @@ private: bool restart_request = false; AABB custom_aabb = AABB(Vector3(-4, -4, -4), Vector3(8, 8, 8)); bool use_local_coords = true; + bool has_collision_cache = false; + + bool has_sdf_collision = false; + Transform2D sdf_collision_transform; + Rect2 sdf_collision_to_screen; + RID sdf_collision_texture; + RID process_material; uint32_t frame_counter = 0; RS::ParticlesTransformAlign transform_align = RS::PARTICLES_TRANSFORM_ALIGN_DISABLED; @@ -820,6 +829,11 @@ private: float align_up[3]; uint32_t align_mode; + + uint32_t order_by_lifetime; + uint32_t lifetime_split; + uint32_t lifetime_reverse; + uint32_t pad; }; enum { @@ -843,6 +857,7 @@ private: struct ParticlesShaderData : public ShaderData { bool valid; RID version; + bool uses_collision = false; //PipelineCacheRD pipelines[SKY_VERSION_MAX]; Map<StringName, ShaderLanguage::ShaderNode::Uniform> uniforms; @@ -1120,6 +1135,8 @@ private: bool flags[RENDER_TARGET_FLAG_MAX]; + bool sdf_enabled = false; + RID backbuffer; //used for effects RID backbuffer_fb; RID backbuffer_mipmap0; @@ -2175,6 +2192,13 @@ public: return particles->amount * r_trail_divisor; } + _FORCE_INLINE_ bool particles_has_collision(RID p_particles) { + Particles *particles = particles_owner.getornull(p_particles); + ERR_FAIL_COND_V(!particles, 0); + + return particles->has_collision_cache; + } + _FORCE_INLINE_ uint32_t particles_is_using_local_coords(RID p_particles) { Particles *particles = particles_owner.getornull(p_particles); ERR_FAIL_COND_V(!particles, false); @@ -2206,6 +2230,7 @@ public: virtual void particles_add_collision(RID p_particles, RID p_particles_collision_instance); virtual void particles_remove_collision(RID p_particles, RID p_particles_collision_instance); + virtual void particles_set_canvas_sdf_collision(RID p_particles, bool p_enable, const Transform2D &p_xform, const Rect2 &p_to_screen, RID p_texture); /* PARTICLES COLLISION */ @@ -2280,6 +2305,8 @@ public: RID render_target_get_sdf_framebuffer(RID p_render_target); void render_target_sdf_process(RID p_render_target); virtual Rect2i render_target_get_sdf_rect(RID p_render_target) const; + void render_target_mark_sdf_enabled(RID p_render_target, bool p_enabled); + bool render_target_is_sdf_enabled(RID p_render_target) const; Size2 render_target_get_size(RID p_render_target); RID render_target_get_rd_framebuffer(RID p_render_target); diff --git a/servers/rendering/renderer_rd/shader_compiler_rd.cpp b/servers/rendering/renderer_rd/shader_compiler_rd.cpp index 3a000bd06e..7deedb80c3 100644 --- a/servers/rendering/renderer_rd/shader_compiler_rd.cpp +++ b/servers/rendering/renderer_rd/shader_compiler_rd.cpp @@ -369,17 +369,24 @@ void ShaderCompilerRD::_dump_function_deps(const SL::ShaderNode *p_node, const S ERR_FAIL_COND(fidx == -1); + Vector<StringName> uses_functions; + for (Set<StringName>::Element *E = p_node->functions[fidx].uses_function.front(); E; E = E->next()) { - if (added.has(E->get())) { + uses_functions.push_back(E->get()); + } + uses_functions.sort_custom<StringName::AlphCompare>(); //ensure order is deterministic so the same shader is always produced + + for (int k = 0; k < uses_functions.size(); k++) { + if (added.has(uses_functions[k])) { continue; //was added already } - _dump_function_deps(p_node, E->get(), p_func_code, r_to_add, added); + _dump_function_deps(p_node, uses_functions[k], p_func_code, r_to_add, added); SL::FunctionNode *fnode = nullptr; for (int i = 0; i < p_node->functions.size(); i++) { - if (p_node->functions[i].name == E->get()) { + if (p_node->functions[i].name == uses_functions[k]) { fnode = p_node->functions[i].function; break; } @@ -391,10 +398,21 @@ void ShaderCompilerRD::_dump_function_deps(const SL::ShaderNode *p_node, const S String header; if (fnode->return_type == SL::TYPE_STRUCT) { - header = _mkid(fnode->return_struct_name) + " " + _mkid(fnode->name) + "("; + header = _mkid(fnode->return_struct_name); } else { - header = _typestr(fnode->return_type) + " " + _mkid(fnode->name) + "("; + header = _typestr(fnode->return_type); } + + if (fnode->return_array_size > 0) { + header += "["; + header += itos(fnode->return_array_size); + header += "]"; + } + + header += " "; + header += _mkid(fnode->name); + header += "("; + for (int i = 0; i < fnode->arguments.size(); i++) { if (i > 0) { header += ", "; @@ -407,13 +425,18 @@ void ShaderCompilerRD::_dump_function_deps(const SL::ShaderNode *p_node, const S } else { header += _qualstr(fnode->arguments[i].qualifier) + _prestr(fnode->arguments[i].precision) + _typestr(fnode->arguments[i].type) + " " + _mkid(fnode->arguments[i].name); } + if (fnode->arguments[i].array_size > 0) { + header += "["; + header += itos(fnode->arguments[i].array_size); + header += "]"; + } } header += ")\n"; r_to_add += header; - r_to_add += p_func_code[E->get()]; + r_to_add += p_func_code[uses_functions[k]]; - added.insert(E->get()); + added.insert(uses_functions[k]); } } @@ -565,63 +588,74 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge uniform_defines.resize(max_uniforms); bool uses_uniforms = false; + Vector<StringName> uniform_names; + for (Map<StringName, SL::ShaderNode::Uniform>::Element *E = pnode->uniforms.front(); E; E = E->next()) { + uniform_names.push_back(E->key()); + } + + uniform_names.sort_custom<StringName::AlphCompare>(); //ensure order is deterministic so the same shader is always produced + + for (int k = 0; k < uniform_names.size(); k++) { + StringName uniform_name = uniform_names[k]; + const SL::ShaderNode::Uniform &uniform = pnode->uniforms[uniform_name]; + String ucode; - if (E->get().scope == SL::ShaderNode::Uniform::SCOPE_INSTANCE) { + if (uniform.scope == SL::ShaderNode::Uniform::SCOPE_INSTANCE) { //insert, but don't generate any code. - p_actions.uniforms->insert(E->key(), E->get()); + p_actions.uniforms->insert(uniform_name, uniform); continue; //instances are indexed directly, dont need index uniforms } - if (SL::is_sampler_type(E->get().type)) { - ucode = "layout(set = " + itos(actions.texture_layout_set) + ", binding = " + itos(actions.base_texture_binding_index + E->get().texture_order) + ") uniform "; + if (SL::is_sampler_type(uniform.type)) { + ucode = "layout(set = " + itos(actions.texture_layout_set) + ", binding = " + itos(actions.base_texture_binding_index + uniform.texture_order) + ") uniform "; } - bool is_buffer_global = !SL::is_sampler_type(E->get().type) && E->get().scope == SL::ShaderNode::Uniform::SCOPE_GLOBAL; + bool is_buffer_global = !SL::is_sampler_type(uniform.type) && uniform.scope == SL::ShaderNode::Uniform::SCOPE_GLOBAL; if (is_buffer_global) { //this is an integer to index the global table ucode += _typestr(ShaderLanguage::TYPE_UINT); } else { - ucode += _prestr(E->get().precision); - ucode += _typestr(E->get().type); + ucode += _prestr(uniform.precision); + ucode += _typestr(uniform.type); } - ucode += " " + _mkid(E->key()); + ucode += " " + _mkid(uniform_name); ucode += ";\n"; - if (SL::is_sampler_type(E->get().type)) { + if (SL::is_sampler_type(uniform.type)) { for (int j = 0; j < STAGE_MAX; j++) { r_gen_code.stage_globals[j] += ucode; } GeneratedCode::Texture texture; - texture.name = E->key(); - texture.hint = E->get().hint; - texture.type = E->get().type; - texture.filter = E->get().filter; - texture.repeat = E->get().repeat; - texture.global = E->get().scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_GLOBAL; + texture.name = uniform_name; + texture.hint = uniform.hint; + texture.type = uniform.type; + texture.filter = uniform.filter; + texture.repeat = uniform.repeat; + texture.global = uniform.scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_GLOBAL; if (texture.global) { r_gen_code.uses_global_textures = true; } - r_gen_code.texture_uniforms.write[E->get().texture_order] = texture; + r_gen_code.texture_uniforms.write[uniform.texture_order] = texture; } else { if (!uses_uniforms) { uses_uniforms = true; } - uniform_defines.write[E->get().order] = ucode; + uniform_defines.write[uniform.order] = ucode; if (is_buffer_global) { //globals are indices into the global table - uniform_sizes.write[E->get().order] = _get_datatype_size(ShaderLanguage::TYPE_UINT); - uniform_alignments.write[E->get().order] = _get_datatype_alignment(ShaderLanguage::TYPE_UINT); + uniform_sizes.write[uniform.order] = _get_datatype_size(ShaderLanguage::TYPE_UINT); + uniform_alignments.write[uniform.order] = _get_datatype_alignment(ShaderLanguage::TYPE_UINT); } else { - uniform_sizes.write[E->get().order] = _get_datatype_size(E->get().type); - uniform_alignments.write[E->get().order] = _get_datatype_alignment(E->get().type); + uniform_sizes.write[uniform.order] = _get_datatype_size(uniform.type); + uniform_alignments.write[uniform.order] = _get_datatype_alignment(uniform.type); } } - p_actions.uniforms->insert(E->key(), E->get()); + p_actions.uniforms->insert(uniform_name, uniform); } for (int i = 0; i < max_uniforms; i++) { @@ -688,21 +722,32 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge List<Pair<StringName, SL::ShaderNode::Varying>> var_frag_to_light; + Vector<StringName> varying_names; + for (Map<StringName, SL::ShaderNode::Varying>::Element *E = pnode->varyings.front(); E; E = E->next()) { - if (E->get().stage == SL::ShaderNode::Varying::STAGE_FRAGMENT_TO_LIGHT || E->get().stage == SL::ShaderNode::Varying::STAGE_FRAGMENT) { - var_frag_to_light.push_back(Pair<StringName, SL::ShaderNode::Varying>(E->key(), E->get())); - fragment_varyings.insert(E->key()); + varying_names.push_back(E->key()); + } + + varying_names.sort_custom<StringName::AlphCompare>(); //ensure order is deterministic so the same shader is always produced + + for (int k = 0; k < varying_names.size(); k++) { + StringName varying_name = varying_names[k]; + const SL::ShaderNode::Varying &varying = pnode->varyings[varying_name]; + + if (varying.stage == SL::ShaderNode::Varying::STAGE_FRAGMENT_TO_LIGHT || varying.stage == SL::ShaderNode::Varying::STAGE_FRAGMENT) { + var_frag_to_light.push_back(Pair<StringName, SL::ShaderNode::Varying>(varying_name, varying)); + fragment_varyings.insert(varying_name); continue; } String vcode; - String interp_mode = _interpstr(E->get().interpolation); - vcode += _prestr(E->get().precision); - vcode += _typestr(E->get().type); - vcode += " " + _mkid(E->key()); - if (E->get().array_size > 0) { + String interp_mode = _interpstr(varying.interpolation); + vcode += _prestr(varying.precision); + vcode += _typestr(varying.type); + vcode += " " + _mkid(varying_name); + if (varying.array_size > 0) { vcode += "["; - vcode += itos(E->get().array_size); + vcode += itos(varying.array_size); vcode += "]"; } vcode += ";\n"; @@ -959,25 +1004,30 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge declaration += itos(adnode->declarations[i].size); } declaration += "]"; - int sz = adnode->declarations[i].initializer.size(); - if (sz > 0) { + if (adnode->declarations[i].single_expression) { declaration += "="; - if (adnode->datatype == SL::TYPE_STRUCT) { - declaration += _mkid(adnode->struct_name); - } else { - declaration += _typestr(adnode->datatype); - } - declaration += "["; - declaration += itos(sz); - declaration += "]"; - declaration += "("; - for (int j = 0; j < sz; j++) { - declaration += _dump_node_code(adnode->declarations[i].initializer[j], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); - if (j != sz - 1) { - declaration += ", "; + declaration += _dump_node_code(adnode->declarations[i].initializer[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); + } else { + int sz = adnode->declarations[i].initializer.size(); + if (sz > 0) { + declaration += "="; + if (adnode->datatype == SL::TYPE_STRUCT) { + declaration += _mkid(adnode->struct_name); + } else { + declaration += _typestr(adnode->datatype); + } + declaration += "["; + declaration += itos(sz); + declaration += "]"; + declaration += "("; + for (int j = 0; j < sz; j++) { + declaration += _dump_node_code(adnode->declarations[i].initializer[j], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); + if (j != sz - 1) { + declaration += ", "; + } } + declaration += ")"; } - declaration += ")"; } } @@ -988,7 +1038,7 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge bool use_fragment_varying = false; if (!(p_actions.entry_point_stages.has(current_func_name) && p_actions.entry_point_stages[current_func_name] == STAGE_VERTEX)) { - if (anode->assign_expression != nullptr) { + if (anode->assign_expression != nullptr && shader->varyings.has(anode->name)) { use_fragment_varying = true; } else { if (p_assigning) { diff --git a/servers/rendering/renderer_rd/shader_rd.cpp b/servers/rendering/renderer_rd/shader_rd.cpp index f7242a2b17..6f29ff42bc 100644 --- a/servers/rendering/renderer_rd/shader_rd.cpp +++ b/servers/rendering/renderer_rd/shader_rd.cpp @@ -30,8 +30,12 @@ #include "shader_rd.h" +#include "core/io/compression.h" +#include "core/os/dir_access.h" +#include "core/os/file_access.h" #include "renderer_compositor_rd.h" #include "servers/rendering/rendering_device.h" +#include "thirdparty/misc/smolv.h" void ShaderRD::_add_stage(const char *p_code, StageType p_stage_type) { Vector<String> lines = String(p_code).split("\n"); @@ -97,6 +101,7 @@ void ShaderRD::_add_stage(const char *p_code, StageType p_stage_type) { void ShaderRD::setup(const char *p_vertex_code, const char *p_fragment_code, const char *p_compute_code, const char *p_name) { name = p_name; + if (p_compute_code) { _add_stage(p_compute_code, STAGE_TYPE_COMPUTE); is_compute = true; @@ -109,6 +114,18 @@ void ShaderRD::setup(const char *p_vertex_code, const char *p_fragment_code, con _add_stage(p_fragment_code, STAGE_TYPE_FRAGMENT); } } + + StringBuilder tohash; + tohash.append("[VersionKey]"); + tohash.append(RenderingDevice::get_singleton()->shader_get_cache_key()); + tohash.append("[Vertex]"); + tohash.append(p_vertex_code ? p_vertex_code : ""); + tohash.append("[Fragment]"); + tohash.append(p_fragment_code ? p_fragment_code : ""); + tohash.append("[Compute]"); + tohash.append(p_compute_code ? p_compute_code : ""); + + base_sha256 = tohash.as_string().sha256_text(); } RID ShaderRD::version_create() { @@ -131,6 +148,9 @@ void ShaderRD::_clear_version(Version *p_version) { } memdelete_arr(p_version->variants); + if (p_version->variant_stages) { + memdelete_arr(p_version->variant_stages); + } p_version->variants = nullptr; } } @@ -183,7 +203,7 @@ void ShaderRD::_compile_variant(uint32_t p_variant, Version *p_version) { return; //variant is disabled, return } - Vector<RD::ShaderStageData> stages; + Vector<RD::ShaderStageData> &stages = p_version->variant_stages[p_variant]; String error; String current_source; @@ -313,6 +333,197 @@ RS::ShaderNativeSourceCode ShaderRD::version_get_native_source_code(RID p_versio return source_code; } +String ShaderRD::_version_get_sha1(Version *p_version) const { + StringBuilder hash_build; + + hash_build.append("[uniforms]"); + hash_build.append(p_version->uniforms.get_data()); + hash_build.append("[vertex_globals]"); + hash_build.append(p_version->vertex_globals.get_data()); + hash_build.append("[fragment_globals]"); + hash_build.append(p_version->fragment_globals.get_data()); + hash_build.append("[compute_globals]"); + hash_build.append(p_version->compute_globals.get_data()); + + Vector<StringName> code_sections; + for (Map<StringName, CharString>::Element *E = p_version->code_sections.front(); E; E = E->next()) { + code_sections.push_back(E->key()); + } + code_sections.sort_custom<StringName::AlphCompare>(); + + for (int i = 0; i < code_sections.size(); i++) { + hash_build.append(String("[code:") + String(code_sections[i]) + "]"); + hash_build.append(p_version->code_sections[code_sections[i]].get_data()); + } + for (int i = 0; i < p_version->custom_defines.size(); i++) { + hash_build.append("[custom_defines:" + itos(i) + "]"); + hash_build.append(p_version->custom_defines[i].get_data()); + } + + return hash_build.as_string().sha1_text(); +} + +static const char *shader_file_header = "GDSC"; +static const uint32_t cache_file_version = 1; + +bool ShaderRD::_load_from_cache(Version *p_version) { + String sha1 = _version_get_sha1(p_version); + String path = shader_cache_dir.plus_file(name).plus_file(base_sha256).plus_file(sha1) + ".cache"; + + uint64_t time_from = OS::get_singleton()->get_ticks_usec(); + + FileAccessRef f = FileAccess::open(path, FileAccess::READ); + if (!f) { + return false; + } + + char header[5] = { 0, 0, 0, 0, 0 }; + f->get_buffer((uint8_t *)header, 4); + ERR_FAIL_COND_V(header != String(shader_file_header), false); + + uint32_t file_version = f->get_32(); + if (file_version != cache_file_version) { + return false; // wrong version + } + + uint32_t variant_count = f->get_32(); + + ERR_FAIL_COND_V(variant_count != (uint32_t)variant_defines.size(), false); //should not happen but check + + bool success = true; + for (uint32_t i = 0; i < variant_count; i++) { + uint32_t stage_count = f->get_32(); + p_version->variant_stages[i].resize(stage_count); + for (uint32_t j = 0; j < stage_count; j++) { + p_version->variant_stages[i].write[j].shader_stage = RD::ShaderStage(f->get_32()); + + int compression = f->get_32(); + uint32_t length = f->get_32(); + + if (compression == 0) { + Vector<uint8_t> data; + data.resize(length); + + f->get_buffer(data.ptrw(), length); + + p_version->variant_stages[i].write[j].spir_v = data; + } else { + Vector<uint8_t> data; + + if (compression == 2) { + //zstd + int smol_length = f->get_32(); + Vector<uint8_t> zstd_data; + + zstd_data.resize(smol_length); + f->get_buffer(zstd_data.ptrw(), smol_length); + + data.resize(length); + Compression::decompress(data.ptrw(), data.size(), zstd_data.ptr(), zstd_data.size(), Compression::MODE_ZSTD); + + } else { + data.resize(length); + f->get_buffer(data.ptrw(), length); + } + + Vector<uint8_t> spirv; + uint32_t spirv_size = smolv::GetDecodedBufferSize(data.ptr(), data.size()); + spirv.resize(spirv_size); + if (!smolv::Decode(data.ptr(), data.size(), spirv.ptrw(), spirv_size)) { + ERR_PRINT("Malformed smolv input uncompressing shader " + name + ", variant #" + itos(i) + " stage :" + itos(j)); + success = false; + break; + } + p_version->variant_stages[i].write[j].spir_v = spirv; + } + } + } + + if (!success) { + for (uint32_t i = 0; i < variant_count; i++) { + p_version->variant_stages[i].resize(0); + } + return false; + } + + float time_ms = double(OS::get_singleton()->get_ticks_usec() - time_from) / 1000.0; + + print_verbose("Shader cache load success '" + path + "' " + rtos(time_ms) + "ms."); + + for (uint32_t i = 0; i < variant_count; i++) { + RID shader = RD::get_singleton()->shader_create(p_version->variant_stages[i]); + { + MutexLock lock(variant_set_mutex); + p_version->variants[i] = shader; + } + } + + memdelete_arr(p_version->variant_stages); //clear stages + p_version->variant_stages = nullptr; + p_version->valid = true; + return true; +} + +void ShaderRD::_save_to_cache(Version *p_version) { + String sha1 = _version_get_sha1(p_version); + String path = shader_cache_dir.plus_file(name).plus_file(base_sha256).plus_file(sha1) + ".cache"; + + FileAccessRef f = FileAccess::open(path, FileAccess::WRITE); + ERR_FAIL_COND(!f); + f->store_buffer((const uint8_t *)shader_file_header, 4); + f->store_32(cache_file_version); //file version + uint32_t variant_count = variant_defines.size(); + f->store_32(variant_count); //variant count + + for (uint32_t i = 0; i < variant_count; i++) { + f->store_32(p_version->variant_stages[i].size()); //stage count + for (int j = 0; j < p_version->variant_stages[i].size(); j++) { + f->store_32(p_version->variant_stages[i][j].shader_stage); //stage count + Vector<uint8_t> spirv = p_version->variant_stages[i][j].spir_v; + + bool save_uncompressed = true; + if (shader_cache_save_compressed) { + smolv::ByteArray smolv; + bool strip_debug = !shader_cache_save_debug; + if (!smolv::Encode(spirv.ptr(), spirv.size(), smolv, strip_debug ? smolv::kEncodeFlagStripDebugInfo : 0)) { + ERR_PRINT("Error compressing shader " + name + ", variant #" + itos(i) + " stage :" + itos(i)); + } else { + bool compress_zstd = shader_cache_save_compressed_zstd; + + if (compress_zstd) { + Vector<uint8_t> zstd; + zstd.resize(Compression::get_max_compressed_buffer_size(smolv.size(), Compression::MODE_ZSTD)); + int dst_size = Compression::compress(zstd.ptrw(), &smolv[0], smolv.size(), Compression::MODE_ZSTD); + if (dst_size >= 0 && (uint32_t)dst_size < smolv.size()) { + f->store_32(2); //compressed zstd + f->store_32(smolv.size()); //size of smolv buffer + f->store_32(dst_size); //size of smolv buffer + f->store_buffer(zstd.ptr(), dst_size); //smolv buffer + } else { + compress_zstd = false; + } + } + + if (!compress_zstd) { + f->store_32(1); //compressed + f->store_32(smolv.size()); //size of smolv buffer + f->store_buffer(&smolv[0], smolv.size()); //smolv buffer + } + save_uncompressed = false; + } + } + + if (save_uncompressed) { + f->store_32(0); //uncompressed + f->store_32(spirv.size()); //stage count + f->store_buffer(spirv.ptr(), spirv.size()); //stage count + } + } + } + + f->close(); +} + void ShaderRD::_compile_version(Version *p_version) { _clear_version(p_version); @@ -320,6 +531,15 @@ void ShaderRD::_compile_version(Version *p_version) { p_version->dirty = false; p_version->variants = memnew_arr(RID, variant_defines.size()); + typedef Vector<RD::ShaderStageData> ShaderStageArray; + p_version->variant_stages = memnew_arr(ShaderStageArray, variant_defines.size()); + + if (shader_cache_dir_valid) { + if (_load_from_cache(p_version)) { + return; + } + } + #if 1 RendererThreadPool::singleton->thread_work_pool.do_work(variant_defines.size(), this, &ShaderRD::_compile_variant, p_version); @@ -351,10 +571,20 @@ void ShaderRD::_compile_version(Version *p_version) { } } memdelete_arr(p_version->variants); + if (p_version->variant_stages) { + memdelete_arr(p_version->variant_stages); + } p_version->variants = nullptr; + p_version->variant_stages = nullptr; return; + } else if (shader_cache_dir_valid) { + //save shader cache + _save_to_cache(p_version); } + memdelete_arr(p_version->variant_stages); //clear stages + p_version->variant_stages = nullptr; + p_version->valid = true; } @@ -443,6 +673,8 @@ bool ShaderRD::is_variant_enabled(int p_variant) const { return variants_enabled[p_variant]; } +bool ShaderRD::shader_cache_cleanup_on_start = false; + ShaderRD::ShaderRD() { // Do not feel forced to use this, in most cases it makes little to no difference. bool use_32_threads = false; @@ -469,8 +701,64 @@ void ShaderRD::initialize(const Vector<String> &p_variant_defines, const String variant_defines.push_back(p_variant_defines[i].utf8()); variants_enabled.push_back(true); } + + if (shader_cache_dir != String()) { + StringBuilder hash_build; + + hash_build.append("[base_hash]"); + hash_build.append(base_sha256); + hash_build.append("[general_defines]"); + hash_build.append(general_defines.get_data()); + for (int i = 0; i < variant_defines.size(); i++) { + hash_build.append("[variant_defines:" + itos(i) + "]"); + hash_build.append(variant_defines[i].get_data()); + } + + base_sha256 = hash_build.as_string().sha256_text(); + + DirAccessRef d = DirAccess::open(shader_cache_dir); + ERR_FAIL_COND(!d); + if (d->change_dir(name) != OK) { + Error err = d->make_dir(name); + ERR_FAIL_COND(err != OK); + d->change_dir(name); + } + + //erase other versions? + if (shader_cache_cleanup_on_start) { + } + // + if (d->change_dir(base_sha256) != OK) { + Error err = d->make_dir(base_sha256); + ERR_FAIL_COND(err != OK); + } + shader_cache_dir_valid = true; + + print_verbose("Shader '" + name + "' SHA256: " + base_sha256); + } +} + +void ShaderRD::set_shader_cache_dir(const String &p_dir) { + shader_cache_dir = p_dir; +} + +void ShaderRD::set_shader_cache_save_compressed(bool p_enable) { + shader_cache_save_compressed = p_enable; } +void ShaderRD::set_shader_cache_save_compressed_zstd(bool p_enable) { + shader_cache_save_compressed_zstd = p_enable; +} + +void ShaderRD::set_shader_cache_save_debug(bool p_enable) { + shader_cache_save_debug = p_enable; +} + +String ShaderRD::shader_cache_dir; +bool ShaderRD::shader_cache_save_compressed = true; +bool ShaderRD::shader_cache_save_compressed_zstd = true; +bool ShaderRD::shader_cache_save_debug = true; + ShaderRD::~ShaderRD() { List<RID> remaining; version_owner.get_owned_list(&remaining); diff --git a/servers/rendering/renderer_rd/shader_rd.h b/servers/rendering/renderer_rd/shader_rd.h index f20d539621..9a68e02007 100644 --- a/servers/rendering/renderer_rd/shader_rd.h +++ b/servers/rendering/renderer_rd/shader_rd.h @@ -59,7 +59,8 @@ class ShaderRD { Map<StringName, CharString> code_sections; Vector<CharString> custom_defines; - RID *variants; //same size as version defines + Vector<RD::ShaderStageData> *variant_stages = nullptr; + RID *variants = nullptr; //same size as version defines bool valid; bool dirty; @@ -96,10 +97,19 @@ class ShaderRD { bool is_compute = false; - const char *name; + String name; CharString base_compute_defines; + String base_sha256; + + static String shader_cache_dir; + static bool shader_cache_cleanup_on_start; + static bool shader_cache_save_compressed; + static bool shader_cache_save_compressed_zstd; + static bool shader_cache_save_debug; + bool shader_cache_dir_valid = false; + enum StageType { STAGE_TYPE_VERTEX, STAGE_TYPE_FRAGMENT, @@ -113,6 +123,10 @@ class ShaderRD { void _add_stage(const char *p_code, StageType p_stage_type); + String _version_get_sha1(Version *p_version) const; + bool _load_from_cache(Version *p_version); + void _save_to_cache(Version *p_version); + protected: ShaderRD(); void setup(const char *p_vertex_code, const char *p_fragment_code, const char *p_compute_code, const char *p_name); @@ -148,6 +162,11 @@ public: void set_variant_enabled(int p_variant, bool p_enabled); bool is_variant_enabled(int p_variant) const; + static void set_shader_cache_dir(const String &p_dir); + static void set_shader_cache_save_compressed(bool p_enable); + static void set_shader_cache_save_compressed_zstd(bool p_enable); + static void set_shader_cache_save_debug(bool p_enable); + RS::ShaderNativeSourceCode version_get_native_source_code(RID p_version); void initialize(const Vector<String> &p_variant_defines, const String &p_general_defines = ""); diff --git a/servers/rendering/renderer_rd/shaders/canvas.glsl b/servers/rendering/renderer_rd/shaders/canvas.glsl index cf4c77db0d..2186bd174b 100644 --- a/servers/rendering/renderer_rd/shaders/canvas.glsl +++ b/servers/rendering/renderer_rd/shaders/canvas.glsl @@ -101,36 +101,34 @@ void main() { uint offset = trail_size * stride * gl_InstanceIndex; - mat4 matrix; vec4 pcolor; + vec2 new_vertex; { uint boffset = offset + bone_attrib.x * stride; - matrix = mat4(transforms.data[boffset + 0], transforms.data[boffset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0)) * weight_attrib.x; - pcolor = transforms.data[boffset + 3] * weight_attrib.x; + new_vertex = (vec4(vertex, 0.0, 1.0) * mat4(transforms.data[boffset + 0], transforms.data[boffset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy * weight_attrib.x; + pcolor = transforms.data[boffset + 2] * weight_attrib.x; } if (weight_attrib.y > 0.001) { uint boffset = offset + bone_attrib.y * stride; - matrix += mat4(transforms.data[boffset + 0], transforms.data[boffset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0)) * weight_attrib.y; - pcolor += transforms.data[boffset + 3] * weight_attrib.y; + new_vertex += (vec4(vertex, 0.0, 1.0) * mat4(transforms.data[boffset + 0], transforms.data[boffset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy * weight_attrib.y; + pcolor += transforms.data[boffset + 2] * weight_attrib.y; } if (weight_attrib.z > 0.001) { uint boffset = offset + bone_attrib.z * stride; - matrix += mat4(transforms.data[boffset + 0], transforms.data[boffset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0)) * weight_attrib.z; - pcolor += transforms.data[boffset + 3] * weight_attrib.z; + new_vertex += (vec4(vertex, 0.0, 1.0) * mat4(transforms.data[boffset + 0], transforms.data[boffset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy * weight_attrib.z; + pcolor += transforms.data[boffset + 2] * weight_attrib.z; } if (weight_attrib.w > 0.001) { uint boffset = offset + bone_attrib.w * stride; - matrix += mat4(transforms.data[boffset + 0], transforms.data[boffset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0)) * weight_attrib.w; - pcolor += transforms.data[boffset + 3] * weight_attrib.w; + new_vertex += (vec4(vertex, 0.0, 1.0) * mat4(transforms.data[boffset + 0], transforms.data[boffset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy * weight_attrib.w; + pcolor += transforms.data[boffset + 2] * weight_attrib.w; } - instance_custom = transforms.data[offset + 4]; + instance_custom = transforms.data[offset + 3]; + vertex = new_vertex; color *= pcolor; - matrix = transpose(matrix); - world_matrix = world_matrix * matrix; - } else #endif // USE_ATTRIBUTES @@ -283,7 +281,7 @@ vec2 screen_uv_to_sdf(vec2 p_uv) { float texture_sdf(vec2 p_sdf) { vec2 uv = p_sdf * canvas_data.sdf_to_tex.xy + canvas_data.sdf_to_tex.zw; float d = texture(sampler2D(sdf_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uv).r; - d = d * SDF_MAX_LENGTH - 1.0; + d *= SDF_MAX_LENGTH; return d * canvas_data.tex_to_sdf; } diff --git a/servers/rendering/renderer_rd/shaders/canvas_sdf.glsl b/servers/rendering/renderer_rd/shaders/canvas_sdf.glsl index 65a554e839..2bdfbabfcf 100644 --- a/servers/rendering/renderer_rd/shaders/canvas_sdf.glsl +++ b/servers/rendering/renderer_rd/shaders/canvas_sdf.glsl @@ -7,7 +7,7 @@ layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; layout(r8, set = 0, binding = 1) uniform restrict readonly image2D src_pixels; -layout(r16, set = 0, binding = 2) uniform restrict writeonly image2D dst_sdf; +layout(r16_snorm, set = 0, binding = 2) uniform restrict writeonly image2D dst_sdf; layout(rg16i, set = 0, binding = 3) uniform restrict readonly iimage2D src_process; layout(rg16i, set = 0, binding = 4) uniform restrict writeonly iimage2D dst_process; @@ -32,7 +32,7 @@ void main() { #ifdef MODE_LOAD bool solid = imageLoad(src_pixels, pos).r > 0.5; - imageStore(dst_process, pos, solid ? ivec4(pos, 0, 0) : ivec4(ivec2(32767), 0, 0)); + imageStore(dst_process, pos, solid ? ivec4(ivec2(-32767), 0, 0) : ivec4(ivec2(32767), 0, 0)); #endif #ifdef MODE_LOAD_SHRINK @@ -43,6 +43,8 @@ void main() { ivec2 rel = ivec2(32767); float d = 1e20; + int found = 0; + int solid_found = 0; for (int i = 0; i < s; i++) { for (int j = 0; j < s; j++) { ivec2 src_pos = base + ivec2(i, j); @@ -56,10 +58,17 @@ void main() { d = dist; rel = src_pos; } + solid_found++; } + found++; } } + if (solid_found == found) { + //mark solid only if all are solid + rel = ivec2(-32767); + } + imageStore(dst_process, pos, ivec4(rel, 0, 0)); #endif @@ -70,6 +79,12 @@ void main() { ivec2 rel = imageLoad(src_process, pos).xy; + bool solid = rel.x < 0; + + if (solid) { + rel = -rel - ivec2(1); + } + if (center != rel) { //only process if it does not point to itself const int ofs_table_size = 8; @@ -92,6 +107,15 @@ void main() { continue; } ivec2 src_rel = imageLoad(src_process, src_pos).xy; + bool src_solid = src_rel.x < 0; + if (src_solid) { + src_rel = -src_rel - ivec2(1); + } + + if (src_solid != solid) { + src_rel = ivec2(src_pos << params.shift); //point to itself if of different type + } + float src_dist = length(vec2(src_rel - center)); if (src_dist < dist) { dist = src_dist; @@ -100,18 +124,31 @@ void main() { } } + if (solid) { + rel = -rel - ivec2(1); + } + imageStore(dst_process, pos, ivec4(rel, 0, 0)); #endif #ifdef MODE_STORE ivec2 rel = imageLoad(src_process, pos).xy; + + bool solid = rel.x < 0; + + if (solid) { + rel = -rel - ivec2(1); + } + float d = length(vec2(rel - pos)); - if (d > 0.01) { - d += 1.0; //make it signed + + if (solid) { + d = -d; } + d /= SDF_MAX_LENGTH; - d = clamp(d, 0.0, 1.0); + d = clamp(d, -1.0, 1.0); imageStore(dst_sdf, pos, vec4(d)); #endif @@ -122,13 +159,20 @@ void main() { ivec2 center = base + ivec2(params.shift); ivec2 rel = imageLoad(src_process, pos).xy; + + bool solid = rel.x < 0; + + if (solid) { + rel = -rel - ivec2(1); + } + float d = length(vec2(rel - center)); - if (d > 0.01) { - d += 1.0; //make it signed + if (solid) { + d = -d; } d /= SDF_MAX_LENGTH; - d = clamp(d, 0.0, 1.0); + d = clamp(d, -1.0, 1.0); imageStore(dst_sdf, pos, vec4(d)); #endif diff --git a/servers/rendering/renderer_rd/shaders/particles.glsl b/servers/rendering/renderer_rd/shaders/particles.glsl index beaff10793..9f8410fd8a 100644 --- a/servers/rendering/renderer_rd/shaders/particles.glsl +++ b/servers/rendering/renderer_rd/shaders/particles.glsl @@ -19,6 +19,8 @@ layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in; #define SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_REPEAT 10 #define SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_REPEAT 11 +#define SDF_MAX_LENGTH 16384.0 + /* SET 0: GLOBAL DATA */ layout(set = 0, binding = 1) uniform sampler material_samplers[12]; @@ -54,6 +56,7 @@ struct Attractor { #define COLLIDER_TYPE_BOX 1 #define COLLIDER_TYPE_SDF 2 #define COLLIDER_TYPE_HEIGHT_FIELD 3 +#define COLLIDER_TYPE_2D_SDF 4 struct Collider { mat4 transform; @@ -452,128 +455,167 @@ void main() { #endif - for (uint i = 0; i < FRAME.collider_count; i++) { - vec3 normal; - float depth; - bool col = false; + if (FRAME.collider_count == 1 && FRAME.colliders[0].type == COLLIDER_TYPE_2D_SDF) { + //2D collision - vec3 rel_vec = PARTICLE.xform[3].xyz - FRAME.colliders[i].transform[3].xyz; - vec3 local_pos = rel_vec * mat3(FRAME.colliders[i].transform); + vec2 pos = PARTICLE.xform[3].xy; + vec4 to_sdf_x = FRAME.colliders[0].transform[0]; + vec4 to_sdf_y = FRAME.colliders[0].transform[1]; + vec2 sdf_pos = vec2(dot(vec4(pos, 0, 1), to_sdf_x), dot(vec4(pos, 0, 1), to_sdf_y)); - switch (FRAME.colliders[i].type) { - case COLLIDER_TYPE_SPHERE: { - float d = length(rel_vec) - (particle_size + FRAME.colliders[i].extents.x); + vec4 sdf_to_screen = vec4(FRAME.colliders[0].extents, FRAME.colliders[0].scale); - if (d < 0.0) { - col = true; - depth = -d; - normal = normalize(rel_vec); - } + vec2 uv_pos = sdf_pos * sdf_to_screen.xy + sdf_to_screen.zw; - } break; - case COLLIDER_TYPE_BOX: { - vec3 abs_pos = abs(local_pos); - vec3 sgn_pos = sign(local_pos); + if (all(greaterThan(uv_pos, vec2(0.0))) && all(lessThan(uv_pos, vec2(1.0)))) { + vec2 pos2 = pos + vec2(0, particle_size); + vec2 sdf_pos2 = vec2(dot(vec4(pos2, 0, 1), to_sdf_x), dot(vec4(pos2, 0, 1), to_sdf_y)); + float sdf_particle_size = distance(sdf_pos, sdf_pos2); + + float d = texture(sampler2D(height_field_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uv_pos).r * SDF_MAX_LENGTH; + + d -= sdf_particle_size; + + if (d < 0.0) { + const float EPSILON = 0.001; + vec2 n = normalize(vec2( + texture(sampler2D(height_field_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uv_pos + vec2(EPSILON, 0.0)).r - texture(sampler2D(height_field_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uv_pos - vec2(EPSILON, 0.0)).r, + texture(sampler2D(height_field_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uv_pos + vec2(0.0, EPSILON)).r - texture(sampler2D(height_field_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uv_pos - vec2(0.0, EPSILON)).r)); + + collided = true; + sdf_pos2 = sdf_pos + n * d; + pos2 = vec2(dot(vec4(sdf_pos2, 0, 1), FRAME.colliders[0].transform[2]), dot(vec4(sdf_pos2, 0, 1), FRAME.colliders[0].transform[3])); + + n = pos - pos2; + + collision_normal = normalize(vec3(n, 0.0)); + collision_depth = length(n); + } + } + + } else { + for (uint i = 0; i < FRAME.collider_count; i++) { + vec3 normal; + float depth; + bool col = false; - if (any(greaterThan(abs_pos, FRAME.colliders[i].extents))) { - //point outside box + vec3 rel_vec = PARTICLE.xform[3].xyz - FRAME.colliders[i].transform[3].xyz; + vec3 local_pos = rel_vec * mat3(FRAME.colliders[i].transform); - vec3 closest = min(abs_pos, FRAME.colliders[i].extents); - vec3 rel = abs_pos - closest; - depth = length(rel) - particle_size; - if (depth < 0.0) { + switch (FRAME.colliders[i].type) { + case COLLIDER_TYPE_SPHERE: { + float d = length(rel_vec) - (particle_size + FRAME.colliders[i].extents.x); + + if (d < 0.0) { col = true; - normal = mat3(FRAME.colliders[i].transform) * (normalize(rel) * sgn_pos); - depth = -depth; + depth = -d; + normal = normalize(rel_vec); } - } else { - //point inside box - vec3 axis_len = FRAME.colliders[i].extents - abs_pos; - // there has to be a faster way to do this? - if (all(lessThan(axis_len.xx, axis_len.yz))) { - normal = vec3(1, 0, 0); - } else if (all(lessThan(axis_len.yy, axis_len.xz))) { - normal = vec3(0, 1, 0); + + } break; + case COLLIDER_TYPE_BOX: { + vec3 abs_pos = abs(local_pos); + vec3 sgn_pos = sign(local_pos); + + if (any(greaterThan(abs_pos, FRAME.colliders[i].extents))) { + //point outside box + + vec3 closest = min(abs_pos, FRAME.colliders[i].extents); + vec3 rel = abs_pos - closest; + depth = length(rel) - particle_size; + if (depth < 0.0) { + col = true; + normal = mat3(FRAME.colliders[i].transform) * (normalize(rel) * sgn_pos); + depth = -depth; + } } else { - normal = vec3(0, 0, 1); + //point inside box + vec3 axis_len = FRAME.colliders[i].extents - abs_pos; + // there has to be a faster way to do this? + if (all(lessThan(axis_len.xx, axis_len.yz))) { + normal = vec3(1, 0, 0); + } else if (all(lessThan(axis_len.yy, axis_len.xz))) { + normal = vec3(0, 1, 0); + } else { + normal = vec3(0, 0, 1); + } + + col = true; + depth = dot(normal * axis_len, vec3(1)) + particle_size; + normal = mat3(FRAME.colliders[i].transform) * (normal * sgn_pos); } - col = true; - depth = dot(normal * axis_len, vec3(1)) + particle_size; - normal = mat3(FRAME.colliders[i].transform) * (normal * sgn_pos); - } + } break; + case COLLIDER_TYPE_SDF: { + vec3 apos = abs(local_pos); + float extra_dist = 0.0; + if (any(greaterThan(apos, FRAME.colliders[i].extents))) { //outside + vec3 mpos = min(apos, FRAME.colliders[i].extents); + extra_dist = distance(mpos, apos); + } - } break; - case COLLIDER_TYPE_SDF: { - vec3 apos = abs(local_pos); - float extra_dist = 0.0; - if (any(greaterThan(apos, FRAME.colliders[i].extents))) { //outside - vec3 mpos = min(apos, FRAME.colliders[i].extents); - extra_dist = distance(mpos, apos); - } + if (extra_dist > particle_size) { + continue; + } - if (extra_dist > particle_size) { - continue; - } + vec3 uvw_pos = (local_pos / FRAME.colliders[i].extents) * 0.5 + 0.5; + float s = texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos).r; + s *= FRAME.colliders[i].scale; + s += extra_dist; + if (s < particle_size) { + col = true; + depth = particle_size - s; + const float EPSILON = 0.001; + normal = mat3(FRAME.colliders[i].transform) * + normalize( + vec3( + texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos + vec3(EPSILON, 0.0, 0.0)).r - texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos - vec3(EPSILON, 0.0, 0.0)).r, + texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos + vec3(0.0, EPSILON, 0.0)).r - texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos - vec3(0.0, EPSILON, 0.0)).r, + texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos + vec3(0.0, 0.0, EPSILON)).r - texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos - vec3(0.0, 0.0, EPSILON)).r)); + } - vec3 uvw_pos = (local_pos / FRAME.colliders[i].extents) * 0.5 + 0.5; - float s = texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos).r; - s *= FRAME.colliders[i].scale; - s += extra_dist; - if (s < particle_size) { - col = true; - depth = particle_size - s; - const float EPSILON = 0.001; - normal = mat3(FRAME.colliders[i].transform) * - normalize( - vec3( - texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos + vec3(EPSILON, 0.0, 0.0)).r - texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos - vec3(EPSILON, 0.0, 0.0)).r, - texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos + vec3(0.0, EPSILON, 0.0)).r - texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos - vec3(0.0, EPSILON, 0.0)).r, - texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos + vec3(0.0, 0.0, EPSILON)).r - texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos - vec3(0.0, 0.0, EPSILON)).r)); - } + } break; + case COLLIDER_TYPE_HEIGHT_FIELD: { + vec3 local_pos_bottom = local_pos; + local_pos_bottom.y -= particle_size; - } break; - case COLLIDER_TYPE_HEIGHT_FIELD: { - vec3 local_pos_bottom = local_pos; - local_pos_bottom.y -= particle_size; + if (any(greaterThan(abs(local_pos_bottom), FRAME.colliders[i].extents))) { + continue; + } + const float DELTA = 1.0 / 8192.0; - if (any(greaterThan(abs(local_pos_bottom), FRAME.colliders[i].extents))) { - continue; - } + vec3 uvw_pos = vec3(local_pos_bottom / FRAME.colliders[i].extents) * 0.5 + 0.5; - const float DELTA = 1.0 / 8192.0; + float y = 1.0 - texture(sampler2D(height_field_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos.xz).r; - vec3 uvw_pos = vec3(local_pos_bottom / FRAME.colliders[i].extents) * 0.5 + 0.5; + if (y > uvw_pos.y) { + //inside heightfield - float y = 1.0 - texture(sampler2D(height_field_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos.xz).r; + vec3 pos1 = (vec3(uvw_pos.x, y, uvw_pos.z) * 2.0 - 1.0) * FRAME.colliders[i].extents; + vec3 pos2 = (vec3(uvw_pos.x + DELTA, 1.0 - texture(sampler2D(height_field_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos.xz + vec2(DELTA, 0)).r, uvw_pos.z) * 2.0 - 1.0) * FRAME.colliders[i].extents; + vec3 pos3 = (vec3(uvw_pos.x, 1.0 - texture(sampler2D(height_field_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos.xz + vec2(0, DELTA)).r, uvw_pos.z + DELTA) * 2.0 - 1.0) * FRAME.colliders[i].extents; - if (y > uvw_pos.y) { - //inside heightfield + normal = normalize(cross(pos1 - pos2, pos1 - pos3)); + float local_y = (vec3(local_pos / FRAME.colliders[i].extents) * 0.5 + 0.5).y; - vec3 pos1 = (vec3(uvw_pos.x, y, uvw_pos.z) * 2.0 - 1.0) * FRAME.colliders[i].extents; - vec3 pos2 = (vec3(uvw_pos.x + DELTA, 1.0 - texture(sampler2D(height_field_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos.xz + vec2(DELTA, 0)).r, uvw_pos.z) * 2.0 - 1.0) * FRAME.colliders[i].extents; - vec3 pos3 = (vec3(uvw_pos.x, 1.0 - texture(sampler2D(height_field_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos.xz + vec2(0, DELTA)).r, uvw_pos.z + DELTA) * 2.0 - 1.0) * FRAME.colliders[i].extents; + col = true; + depth = dot(normal, pos1) - dot(normal, local_pos_bottom); + } - normal = normalize(cross(pos1 - pos2, pos1 - pos3)); - float local_y = (vec3(local_pos / FRAME.colliders[i].extents) * 0.5 + 0.5).y; + } break; + } - col = true; - depth = dot(normal, pos1) - dot(normal, local_pos_bottom); + if (col) { + if (!collided) { + collided = true; + collision_normal = normal; + collision_depth = depth; + } else { + vec3 c = collision_normal * collision_depth; + c += normal * max(0.0, depth - dot(normal, c)); + collision_normal = normalize(c); + collision_depth = length(c); } - - } break; - } - - if (col) { - if (!collided) { - collided = true; - collision_normal = normal; - collision_depth = depth; - } else { - vec3 c = collision_normal * collision_depth; - c += normal * max(0.0, depth - dot(normal, c)); - collision_normal = normalize(c); - collision_depth = length(c); } } } diff --git a/servers/rendering/renderer_rd/shaders/particles_copy.glsl b/servers/rendering/renderer_rd/shaders/particles_copy.glsl index e2bebadf1a..4dceeea995 100644 --- a/servers/rendering/renderer_rd/shaders/particles_copy.glsl +++ b/servers/rendering/renderer_rd/shaders/particles_copy.glsl @@ -53,6 +53,11 @@ layout(push_constant, binding = 0, std430) uniform Params { vec3 align_up; uint align_mode; + + bool order_by_lifetime; + uint lifetime_split; + bool lifetime_reverse; + uint pad; } params; @@ -80,7 +85,6 @@ void main() { #ifdef MODE_FILL_INSTANCES uint particle = gl_GlobalInvocationID.x; - uint write_offset = gl_GlobalInvocationID.x * (3 + 1 + 1); //xform + color + custom if (particle >= params.total_particles) { return; //discard @@ -93,7 +97,41 @@ void main() { } else { particle = uint(sort_buffer.data[particle].y); //use index from sort buffer } -#endif +#else + if (params.order_by_lifetime) { + if (params.trail_size > 1) { + uint limit = (params.total_particles / params.trail_size) - params.lifetime_split; + + uint base_index = particle / params.trail_size; + uint base_offset = particle % params.trail_size; + + if (params.lifetime_reverse) { + base_index = (params.total_particles / params.trail_size) - base_index - 1; + } + + if (base_index < limit) { + base_index = params.lifetime_split + base_index; + } else { + base_index -= limit; + } + + particle = base_index * params.trail_size + base_offset; + + } else { + uint limit = params.total_particles - params.lifetime_split; + + if (params.lifetime_reverse) { + particle = params.total_particles - particle - 1; + } + + if (particle < limit) { + particle = params.lifetime_split + particle; + } else { + particle -= limit; + } + } + } +#endif // USE_SORT_BUFFER mat4 txform; @@ -165,12 +203,17 @@ void main() { #ifdef MODE_2D + uint write_offset = gl_GlobalInvocationID.x * (2 + 1 + 1); //xform + color + custom + instances.data[write_offset + 0] = txform[0]; instances.data[write_offset + 1] = txform[1]; instances.data[write_offset + 2] = particles.data[particle].color; instances.data[write_offset + 3] = particles.data[particle].custom; #else + + uint write_offset = gl_GlobalInvocationID.x * (3 + 1 + 1); //xform + color + custom + instances.data[write_offset + 0] = txform[0]; instances.data[write_offset + 1] = txform[1]; instances.data[write_offset + 2] = txform[2]; diff --git a/servers/rendering/renderer_storage.h b/servers/rendering/renderer_storage.h index cc4ff5d55e..75975e909d 100644 --- a/servers/rendering/renderer_storage.h +++ b/servers/rendering/renderer_storage.h @@ -534,6 +534,8 @@ public: virtual void particles_add_collision(RID p_particles, RID p_particles_collision_instance) = 0; virtual void particles_remove_collision(RID p_particles, RID p_particles_collision_instance) = 0; + virtual void particles_set_canvas_sdf_collision(RID p_particles, bool p_enable, const Transform2D &p_xform, const Rect2 &p_to_screen, RID p_texture) = 0; + virtual void update_particles() = 0; /* PARTICLES COLLISION */ @@ -603,6 +605,7 @@ public: virtual void render_target_set_sdf_size_and_scale(RID p_render_target, RS::ViewportSDFOversize p_size, RS::ViewportSDFScale p_scale) = 0; virtual Rect2i render_target_get_sdf_rect(RID p_render_target) const = 0; + virtual void render_target_mark_sdf_enabled(RID p_render_target, bool p_enabled) = 0; virtual RS::InstanceType get_base_type(RID p_rid) const = 0; virtual bool free(RID p_rid) = 0; diff --git a/servers/rendering/renderer_viewport.cpp b/servers/rendering/renderer_viewport.cpp index 9ac2c1918f..f97e24947d 100644 --- a/servers/rendering/renderer_viewport.cpp +++ b/servers/rendering/renderer_viewport.cpp @@ -186,8 +186,11 @@ void RendererViewport::_draw_viewport(Viewport *p_viewport, XRInterface::Eyes p_ } RSG::canvas_render->render_sdf(p_viewport->render_target, occluders); + RSG::storage->render_target_mark_sdf_enabled(p_viewport->render_target, true); p_viewport->sdf_active = false; // if used, gets set active again + } else { + RSG::storage->render_target_mark_sdf_enabled(p_viewport->render_target, false); } Rect2 shadow_rect; diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp index e6ad001807..056cec4c1f 100644 --- a/servers/rendering/rendering_device.cpp +++ b/servers/rendering/rendering_device.cpp @@ -40,6 +40,7 @@ RenderingDevice *RenderingDevice::get_singleton() { RenderingDevice::ShaderCompileFunction RenderingDevice::compile_function = nullptr; RenderingDevice::ShaderCacheFunction RenderingDevice::cache_function = nullptr; +RenderingDevice::ShaderGetCacheKeyFunction RenderingDevice::get_cache_key_function = nullptr; void RenderingDevice::shader_set_compile_function(ShaderCompileFunction p_function) { compile_function = p_function; @@ -49,6 +50,10 @@ void RenderingDevice::shader_set_cache_function(ShaderCacheFunction p_function) cache_function = p_function; } +void RenderingDevice::shader_set_get_cache_key_function(ShaderGetCacheKeyFunction p_function) { + get_cache_key_function = p_function; +} + Vector<uint8_t> RenderingDevice::shader_compile_from_source(ShaderStage p_stage, const String &p_source_code, ShaderLanguage p_language, String *r_error, bool p_allow_cache) { if (p_allow_cache && cache_function) { Vector<uint8_t> cache = cache_function(p_stage, p_source_code, p_language); @@ -62,6 +67,13 @@ Vector<uint8_t> RenderingDevice::shader_compile_from_source(ShaderStage p_stage, return compile_function(p_stage, p_source_code, p_language, r_error, &device_capabilities); } +String RenderingDevice::shader_get_cache_key() const { + if (get_cache_key_function) { + return get_cache_key_function(&device_capabilities); + } + return String(); +} + RID RenderingDevice::_texture_create(const Ref<RDTextureFormat> &p_format, const Ref<RDTextureView> &p_view, const TypedArray<PackedByteArray> &p_data) { ERR_FAIL_COND_V(p_format.is_null(), RID()); ERR_FAIL_COND_V(p_view.is_null(), RID()); diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h index 27bded9810..4dcb9b963e 100644 --- a/servers/rendering/rendering_device.h +++ b/servers/rendering/rendering_device.h @@ -103,12 +103,14 @@ public: bool supports_multiview = false; // If true this device supports multiview options }; + typedef String (*ShaderGetCacheKeyFunction)(const Capabilities *p_capabilities); typedef Vector<uint8_t> (*ShaderCompileFunction)(ShaderStage p_stage, const String &p_source_code, ShaderLanguage p_language, String *r_error, const Capabilities *p_capabilities); typedef Vector<uint8_t> (*ShaderCacheFunction)(ShaderStage p_stage, const String &p_source_code, ShaderLanguage p_language); private: static ShaderCompileFunction compile_function; static ShaderCacheFunction cache_function; + static ShaderGetCacheKeyFunction get_cache_key_function; static RenderingDevice *singleton; @@ -635,9 +637,11 @@ public: const Capabilities *get_device_capabilities() const { return &device_capabilities; }; virtual Vector<uint8_t> shader_compile_from_source(ShaderStage p_stage, const String &p_source_code, ShaderLanguage p_language = SHADER_LANGUAGE_GLSL, String *r_error = nullptr, bool p_allow_cache = true); + virtual String shader_get_cache_key() const; static void shader_set_compile_function(ShaderCompileFunction p_function); static void shader_set_cache_function(ShaderCacheFunction p_function); + static void shader_set_get_cache_key_function(ShaderGetCacheKeyFunction p_function); struct ShaderStageData { ShaderStage shader_stage; diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp index 2ce7707257..14b21e1f42 100644 --- a/servers/rendering/shader_language.cpp +++ b/servers/rendering/shader_language.cpp @@ -926,6 +926,8 @@ void ShaderLanguage::clear() { char_idx = 0; error_set = false; error_str = ""; + last_const = false; + pass_array = false; while (nodes) { Node *n = nodes; nodes = nodes->next; @@ -973,7 +975,6 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea if (r_type) { *r_type = IDENTIFIER_BUILTIN_VAR; } - return true; } @@ -987,7 +988,6 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea if (r_type) { *r_type = IDENTIFIER_FUNCTION; } - return true; } @@ -1004,16 +1004,15 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea if (r_array_size) { *r_array_size = p_block->variables[p_identifier].array_size; } - if (r_type) { - *r_type = IDENTIFIER_LOCAL_VAR; - } if (r_struct_name) { *r_struct_name = p_block->variables[p_identifier].struct_name; } if (r_constant_value) { *r_constant_value = p_block->variables[p_identifier].value; } - + if (r_type) { + *r_type = IDENTIFIER_LOCAL_VAR; + } return true; } @@ -1035,15 +1034,18 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea if (r_data_type) { *r_data_type = function->arguments[i].type; } - if (r_type) { - *r_type = IDENTIFIER_FUNCTION_ARGUMENT; - } if (r_struct_name) { *r_struct_name = function->arguments[i].type_str; } + if (r_array_size) { + *r_array_size = function->arguments[i].array_size; + } if (r_is_const) { *r_is_const = function->arguments[i].is_const; } + if (r_type) { + *r_type = IDENTIFIER_FUNCTION_ARGUMENT; + } return true; } } @@ -1082,9 +1084,6 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea if (r_array_size) { *r_array_size = shader->constants[p_identifier].array_size; } - if (r_type) { - *r_type = IDENTIFIER_CONSTANT; - } if (r_struct_name) { *r_struct_name = shader->constants[p_identifier].type_str; } @@ -1093,6 +1092,9 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea *r_constant_value = shader->constants[p_identifier].initializer->values[0]; } } + if (r_type) { + *r_type = IDENTIFIER_CONSTANT; + } return true; } @@ -1105,6 +1107,9 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea if (r_data_type) { *r_data_type = shader->functions[i].function->return_type; } + if (r_array_size) { + *r_array_size = shader->functions[i].function->return_array_size; + } if (r_type) { *r_type = IDENTIFIER_FUNCTION; } @@ -1115,13 +1120,18 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea return false; } -bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type) { +bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type, int *r_ret_size) { bool valid = false; DataType ret_type = TYPE_VOID; + int ret_size = 0; switch (p_op->op) { case OP_EQUAL: case OP_NOT_EQUAL: { + if ((!p_op->arguments[0]->is_indexed() && p_op->arguments[0]->get_array_size() > 0) || (!p_op->arguments[1]->is_indexed() && p_op->arguments[1]->get_array_size() > 0)) { + break; // don't accept arrays + } + DataType na = p_op->arguments[0]->get_datatype(); DataType nb = p_op->arguments[1]->get_datatype(); valid = na == nb; @@ -1131,6 +1141,10 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type case OP_LESS_EQUAL: case OP_GREATER: case OP_GREATER_EQUAL: { + if ((!p_op->arguments[0]->is_indexed() && p_op->arguments[0]->get_array_size() > 0) || (!p_op->arguments[1]->is_indexed() && p_op->arguments[1]->get_array_size() > 0)) { + break; // don't accept arrays + } + DataType na = p_op->arguments[0]->get_datatype(); DataType nb = p_op->arguments[1]->get_datatype(); @@ -1140,6 +1154,10 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type } break; case OP_AND: case OP_OR: { + if ((!p_op->arguments[0]->is_indexed() && p_op->arguments[0]->get_array_size() > 0) || (!p_op->arguments[1]->is_indexed() && p_op->arguments[1]->get_array_size() > 0)) { + break; // don't accept arrays + } + DataType na = p_op->arguments[0]->get_datatype(); DataType nb = p_op->arguments[1]->get_datatype(); @@ -1148,6 +1166,10 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type } break; case OP_NOT: { + if (!p_op->arguments[0]->is_indexed() && p_op->arguments[0]->get_array_size() > 0) { + break; // don't accept arrays + } + DataType na = p_op->arguments[0]->get_datatype(); valid = na == TYPE_BOOL; ret_type = TYPE_BOOL; @@ -1158,6 +1180,10 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type case OP_POST_INCREMENT: case OP_POST_DECREMENT: case OP_NEGATE: { + if (!p_op->arguments[0]->is_indexed() && p_op->arguments[0]->get_array_size() > 0) { + break; // don't accept arrays + } + DataType na = p_op->arguments[0]->get_datatype(); valid = na > TYPE_BOOL && na < TYPE_MAT2; ret_type = na; @@ -1166,6 +1192,10 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type case OP_SUB: case OP_MUL: case OP_DIV: { + if ((!p_op->arguments[0]->is_indexed() && p_op->arguments[0]->get_array_size() > 0) || (!p_op->arguments[1]->is_indexed() && p_op->arguments[1]->get_array_size() > 0)) { + break; // don't accept arrays + } + DataType na = p_op->arguments[0]->get_datatype(); DataType nb = p_op->arguments[1]->get_datatype(); @@ -1234,6 +1264,10 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type * component-wise. */ + if ((!p_op->arguments[0]->is_indexed() && p_op->arguments[0]->get_array_size() > 0) || (!p_op->arguments[1]->is_indexed() && p_op->arguments[1]->get_array_size() > 0)) { + break; // don't accept arrays + } + DataType na = p_op->arguments[0]->get_datatype(); DataType nb = p_op->arguments[1]->get_datatype(); @@ -1286,6 +1320,10 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type case OP_ASSIGN_SHIFT_RIGHT: case OP_SHIFT_LEFT: case OP_SHIFT_RIGHT: { + if ((!p_op->arguments[0]->is_indexed() && p_op->arguments[0]->get_array_size() > 0) || (!p_op->arguments[1]->is_indexed() && p_op->arguments[1]->get_array_size() > 0)) { + break; // don't accept arrays + } + DataType na = p_op->arguments[0]->get_datatype(); DataType nb = p_op->arguments[1]->get_datatype(); @@ -1334,6 +1372,18 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type } } break; case OP_ASSIGN: { + int sa = 0; + int sb = 0; + if (!p_op->arguments[0]->is_indexed()) { + sa = p_op->arguments[0]->get_array_size(); + } + if (!p_op->arguments[1]->is_indexed()) { + sb = p_op->arguments[1]->get_array_size(); + } + if (sa != sb) { + break; // don't accept arrays if their sizes are not equal + } + DataType na = p_op->arguments[0]->get_datatype(); DataType nb = p_op->arguments[1]->get_datatype(); if (na == TYPE_STRUCT || nb == TYPE_STRUCT) { @@ -1342,11 +1392,24 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type valid = na == nb; } ret_type = na; + ret_size = sa; } break; case OP_ASSIGN_ADD: case OP_ASSIGN_SUB: case OP_ASSIGN_MUL: case OP_ASSIGN_DIV: { + int sa = 0; + int sb = 0; + if (!p_op->arguments[0]->is_indexed()) { + sa = p_op->arguments[0]->get_array_size(); + } + if (!p_op->arguments[1]->is_indexed()) { + sb = p_op->arguments[1]->get_array_size(); + } + if (sa > 0 || sb > 0) { + break; // don't accept arrays + } + DataType na = p_op->arguments[0]->get_datatype(); DataType nb = p_op->arguments[1]->get_datatype(); @@ -1414,6 +1477,18 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type * must match. */ + int sa = 0; + int sb = 0; + if (!p_op->arguments[0]->is_indexed()) { + sa = p_op->arguments[0]->get_array_size(); + } + if (!p_op->arguments[1]->is_indexed()) { + sb = p_op->arguments[1]->get_array_size(); + } + if (sa > 0 || sb > 0) { + break; // don't accept arrays + } + DataType na = p_op->arguments[0]->get_datatype(); DataType nb = p_op->arguments[1]->get_datatype(); @@ -1468,17 +1543,34 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type } } break; case OP_BIT_INVERT: { //unaries + if (!p_op->arguments[0]->is_indexed() && p_op->arguments[0]->get_array_size() > 0) { + break; // don't accept arrays + } + DataType na = p_op->arguments[0]->get_datatype(); valid = na >= TYPE_INT && na < TYPE_FLOAT; ret_type = na; } break; case OP_SELECT_IF: { + int sa = 0; + int sb = 0; + if (!p_op->arguments[1]->is_indexed()) { + sa = p_op->arguments[1]->get_array_size(); + } + if (!p_op->arguments[2]->is_indexed()) { + sb = p_op->arguments[2]->get_array_size(); + } + if (sa != sb) { + break; // don't accept arrays if their sizes are not equal + } + DataType na = p_op->arguments[0]->get_datatype(); DataType nb = p_op->arguments[1]->get_datatype(); DataType nc = p_op->arguments[2]->get_datatype(); valid = na == TYPE_BOOL && (nb == nc); ret_type = nb; + ret_size = sa; } break; default: { ERR_FAIL_V(false); @@ -1488,6 +1580,9 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type if (r_ret_type) { *r_ret_type = ret_type; } + if (r_ret_size) { + *r_ret_size = ret_size; + } return valid; } @@ -2223,6 +2318,7 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI Vector<DataType> args; Vector<StringName> args2; + Vector<int> args3; ERR_FAIL_COND_V(p_func->arguments[0]->type != Node::TYPE_VARIABLE, false); @@ -2231,6 +2327,7 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI for (int i = 1; i < p_func->arguments.size(); i++) { args.push_back(p_func->arguments[i]->get_datatype()); args2.push_back(p_func->arguments[i]->get_datatype_name()); + args3.push_back(p_func->arguments[i]->get_array_size()); } int argcount = args.size(); @@ -2498,6 +2595,11 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI } else { func_arg_name = get_datatype_name(pfunc->arguments[j].type); } + if (pfunc->arguments[j].array_size > 0) { + func_arg_name += "["; + func_arg_name += itos(pfunc->arguments[j].array_size); + func_arg_name += "]"; + } arg_list += func_arg_name; } } @@ -2510,21 +2612,32 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI bool fail = false; for (int j = 0; j < args.size(); j++) { - if (get_scalar_type(args[j]) == args[j] && p_func->arguments[j + 1]->type == Node::TYPE_CONSTANT && convert_constant(static_cast<ConstantNode *>(p_func->arguments[j + 1]), pfunc->arguments[j].type)) { + if (get_scalar_type(args[j]) == args[j] && p_func->arguments[j + 1]->type == Node::TYPE_CONSTANT && args3[j] == 0 && convert_constant(static_cast<ConstantNode *>(p_func->arguments[j + 1]), pfunc->arguments[j].type)) { //all good, but it needs implicit conversion later - } else if (args[j] != pfunc->arguments[j].type || (args[j] == TYPE_STRUCT && args2[j] != pfunc->arguments[j].type_str)) { + } else if (args[j] != pfunc->arguments[j].type || (args[j] == TYPE_STRUCT && args2[j] != pfunc->arguments[j].type_str) || args3[j] != pfunc->arguments[j].array_size) { String func_arg_name; if (pfunc->arguments[j].type == TYPE_STRUCT) { func_arg_name = pfunc->arguments[j].type_str; } else { func_arg_name = get_datatype_name(pfunc->arguments[j].type); } + if (pfunc->arguments[j].array_size > 0) { + func_arg_name += "["; + func_arg_name += itos(pfunc->arguments[j].array_size); + func_arg_name += "]"; + } String arg_name; if (args[j] == TYPE_STRUCT) { arg_name = args2[j]; } else { arg_name = get_datatype_name(args[j]); } + if (args3[j] > 0) { + arg_name += "["; + arg_name += itos(args3[j]); + arg_name += "]"; + } + _set_error(vformat("Invalid argument for \"%s(%s)\" function: argument %s should be %s but is %s.", String(name), arg_list, j + 1, func_arg_name, arg_name)); fail = true; break; @@ -2570,16 +2683,45 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI return false; } -bool ShaderLanguage::_compare_datatypes_in_nodes(Node *a, Node *b) const { - if (a->get_datatype() != b->get_datatype()) { - return false; +bool ShaderLanguage::_compare_datatypes(DataType p_datatype_a, String p_datatype_name_a, int p_array_size_a, DataType p_datatype_b, String p_datatype_name_b, int p_array_size_b) { + bool result = true; + + if (p_datatype_a == TYPE_STRUCT || p_datatype_b == TYPE_STRUCT) { + if (p_datatype_name_a != p_datatype_name_b) { + result = false; + } + } else { + if (p_datatype_a != p_datatype_b) { + result = false; + } } - if (a->get_datatype() == TYPE_STRUCT || b->get_datatype() == TYPE_STRUCT) { - if (a->get_datatype_name() != b->get_datatype_name()) { - return false; + + if (p_array_size_a != p_array_size_b) { + result = false; + } + + if (!result) { + String type_name = p_datatype_a == TYPE_STRUCT ? p_datatype_name_a : get_datatype_name(p_datatype_a); + if (p_array_size_a > 0) { + type_name += "["; + type_name += itos(p_array_size_a); + type_name += "]"; } + + String type_name2 = p_datatype_b == TYPE_STRUCT ? p_datatype_name_b : get_datatype_name(p_datatype_b); + if (p_array_size_b > 0) { + type_name2 += "["; + type_name2 += itos(p_array_size_b); + type_name2 += "]"; + } + + _set_error("Invalid assignment of '" + type_name2 + "' to '" + type_name + "'"); } - return true; + return result; +} + +bool ShaderLanguage::_compare_datatypes_in_nodes(Node *a, Node *b) { + return _compare_datatypes(a->get_datatype(), a->get_datatype_name(), a->get_array_size(), b->get_datatype(), b->get_datatype_name(), b->get_array_size()); } bool ShaderLanguage::_parse_function_arguments(BlockNode *p_block, const FunctionInfo &p_function_info, OperatorNode *p_func, int *r_complete_arg) { @@ -3390,11 +3532,12 @@ bool ShaderLanguage::_propagate_function_call_sampler_builtin_reference(StringNa ERR_FAIL_V(false); //bug? function not found } -ShaderLanguage::Node *ShaderLanguage::_parse_array_constructor(BlockNode *p_block, const FunctionInfo &p_function_info, DataType p_type, const StringName &p_struct_name, int p_array_size) { +ShaderLanguage::Node *ShaderLanguage::_parse_array_constructor(BlockNode *p_block, const FunctionInfo &p_function_info) { DataType type = TYPE_VOID; String struct_name = ""; int array_size = 0; bool auto_size = false; + bool undefined_size = false; Token tk = _get_token(); if (tk.type == TK_CURLY_BRACKET_OPEN) { @@ -3415,6 +3558,137 @@ ShaderLanguage::Node *ShaderLanguage::_parse_array_constructor(BlockNode *p_bloc TkPos pos = _get_tkpos(); tk = _get_token(); if (tk.type == TK_BRACKET_CLOSE) { + undefined_size = true; + tk = _get_token(); + } else { + _set_tkpos(pos); + + Node *n = _parse_and_reduce_expression(p_block, p_function_info); + if (!n || n->type != Node::TYPE_CONSTANT || n->get_datatype() != TYPE_INT) { + _set_error("Expected single integer constant > 0"); + return nullptr; + } + + ConstantNode *cnode = (ConstantNode *)n; + if (cnode->values.size() == 1) { + array_size = cnode->values[0].sint; + if (array_size <= 0) { + _set_error("Expected single integer constant > 0"); + return nullptr; + } + } else { + _set_error("Expected single integer constant > 0"); + return nullptr; + } + + tk = _get_token(); + if (tk.type != TK_BRACKET_CLOSE) { + _set_error("Expected ']'"); + return nullptr; + } else { + tk = _get_token(); + } + } + } else { + _set_error("Expected '['"); + return nullptr; + } + } + + ArrayConstructNode *an = alloc_node<ArrayConstructNode>(); + + if (tk.type == TK_PARENTHESIS_OPEN || auto_size) { // initialization + int idx = 0; + while (true) { + Node *n = _parse_and_reduce_expression(p_block, p_function_info); + if (!n) { + return nullptr; + } + + // define type by using the first member + if (auto_size && idx == 0) { + type = n->get_datatype(); + if (type == TYPE_STRUCT) { + struct_name = n->get_datatype_name(); + } + } else { + if (!_compare_datatypes(type, struct_name, 0, n->get_datatype(), n->get_datatype_name(), 0)) { + return nullptr; + } + } + + tk = _get_token(); + if (tk.type == TK_COMMA) { + an->initializer.push_back(n); + } else if (!auto_size && tk.type == TK_PARENTHESIS_CLOSE) { + an->initializer.push_back(n); + break; + } else if (auto_size && tk.type == TK_CURLY_BRACKET_CLOSE) { + an->initializer.push_back(n); + break; + } else { + if (auto_size) { + _set_error("Expected '}' or ','"); + } else { + _set_error("Expected ')' or ','"); + } + return nullptr; + } + idx++; + } + if (!auto_size && !undefined_size && an->initializer.size() != array_size) { + _set_error("Array size mismatch"); + return nullptr; + } + } else { + _set_error("Expected array initialization!"); + return nullptr; + } + + an->datatype = type; + an->struct_name = struct_name; + return an; +} + +ShaderLanguage::Node *ShaderLanguage::_parse_array_constructor(BlockNode *p_block, const FunctionInfo &p_function_info, DataType p_type, const StringName &p_struct_name, int p_array_size) { + DataType type = TYPE_VOID; + String struct_name = ""; + int array_size = 0; + bool auto_size = false; + TkPos prev_pos = _get_tkpos(); + Token tk = _get_token(); + + if (tk.type == TK_CURLY_BRACKET_OPEN) { + auto_size = true; + } else { + if (shader->structs.has(tk.text)) { + type = TYPE_STRUCT; + struct_name = tk.text; + } else { + if (!is_token_variable_datatype(tk.type)) { + _set_tkpos(prev_pos); + + pass_array = true; + Node *n = _parse_and_reduce_expression(p_block, p_function_info); + pass_array = false; + + if (!n) { + _set_error("Invalid data type for array"); + return nullptr; + } + + if (!_compare_datatypes(p_type, p_struct_name, p_array_size, n->get_datatype(), n->get_datatype_name(), n->get_array_size())) { + return nullptr; + } + return n; + } + type = get_token_datatype(tk.type); + } + tk = _get_token(); + if (tk.type == TK_BRACKET_OPEN) { + TkPos pos = _get_tkpos(); + tk = _get_token(); + if (tk.type == TK_BRACKET_CLOSE) { array_size = p_array_size; tk = _get_token(); } else { @@ -3486,8 +3760,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_array_constructor(BlockNode *p_bloc return nullptr; } - if (p_type != n->get_datatype() || p_struct_name != n->get_datatype_name()) { - _set_error("Invalid assignment of '" + (n->get_datatype() == TYPE_STRUCT ? n->get_datatype_name() : get_datatype_name(n->get_datatype())) + "' to '" + (type == TYPE_STRUCT ? struct_name : get_datatype_name(type)) + "'"); + if (!_compare_datatypes(p_type, p_struct_name, 0, n->get_datatype(), n->get_datatype_name(), 0)) { return nullptr; } @@ -3587,50 +3860,73 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons //make sure void is not used in expression _set_error("Void value not allowed in Expression"); return nullptr; - } else if (is_token_nonvoid_datatype(tk.type)) { - //basic type constructor + } else if (is_token_nonvoid_datatype(tk.type) || tk.type == TK_CURLY_BRACKET_OPEN) { + if (tk.type == TK_CURLY_BRACKET_OPEN) { + //array constructor - OperatorNode *func = alloc_node<OperatorNode>(); - func->op = OP_CONSTRUCT; + _set_tkpos(prepos); + expr = _parse_array_constructor(p_block, p_function_info); + } else { + DataType datatype; + DataPrecision precision; + bool precision_defined = false; - if (is_token_precision(tk.type)) { - func->return_precision_cache = get_token_precision(tk.type); + if (is_token_precision(tk.type)) { + precision = get_token_precision(tk.type); + precision_defined = true; + tk = _get_token(); + } + + datatype = get_token_datatype(tk.type); tk = _get_token(); - } - VariableNode *funcname = alloc_node<VariableNode>(); - funcname->name = get_datatype_name(get_token_datatype(tk.type)); - func->arguments.push_back(funcname); + if (tk.type == TK_BRACKET_OPEN) { + //array constructor - tk = _get_token(); - if (tk.type != TK_PARENTHESIS_OPEN) { - _set_error("Expected '(' after type name"); - return nullptr; - } + _set_tkpos(prepos); + expr = _parse_array_constructor(p_block, p_function_info); + } else { + if (tk.type != TK_PARENTHESIS_OPEN) { + _set_error("Expected '(' after type name"); + return nullptr; + } + //basic type constructor - int carg = -1; + OperatorNode *func = alloc_node<OperatorNode>(); + func->op = OP_CONSTRUCT; - bool ok = _parse_function_arguments(p_block, p_function_info, func, &carg); + if (precision_defined) { + func->return_precision_cache = precision; + } - if (carg >= 0) { - completion_type = COMPLETION_CALL_ARGUMENTS; - completion_line = tk_line; - completion_block = p_block; - completion_function = funcname->name; - completion_argument = carg; - } + VariableNode *funcname = alloc_node<VariableNode>(); + funcname->name = get_datatype_name(datatype); + func->arguments.push_back(funcname); - if (!ok) { - return nullptr; - } + int carg = -1; - if (!_validate_function_call(p_block, p_function_info, func, &func->return_cache, &func->struct_name)) { - _set_error("No matching constructor found for: '" + String(funcname->name) + "'"); - return nullptr; - } + bool ok = _parse_function_arguments(p_block, p_function_info, func, &carg); + + if (carg >= 0) { + completion_type = COMPLETION_CALL_ARGUMENTS; + completion_line = tk_line; + completion_block = p_block; + completion_function = funcname->name; + completion_argument = carg; + } - expr = _reduce_expression(p_block, func); + if (!ok) { + return nullptr; + } + + if (!_validate_function_call(p_block, p_function_info, func, &func->return_cache, &func->struct_name)) { + _set_error("No matching constructor found for: '" + String(funcname->name) + "'"); + return nullptr; + } + expr = _reduce_expression(p_block, func); + } + } } else if (tk.type == TK_IDENTIFIER) { _set_tkpos(prepos); @@ -3678,11 +3974,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons if (!nexpr) { return nullptr; } - Node *node = pstruct->members[i]; if (!_compare_datatypes_in_nodes(pstruct->members[i], nexpr)) { - String type_name = nexpr->get_datatype() == TYPE_STRUCT ? nexpr->get_datatype_name() : get_datatype_name(nexpr->get_datatype()); - String type_name2 = node->get_datatype() == TYPE_STRUCT ? node->get_datatype_name() : get_datatype_name(node->get_datatype()); - _set_error("Invalid assignment of '" + type_name + "' to '" + type_name2 + "'"); return nullptr; } } @@ -3704,7 +3996,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons expr = func; - } else { //a function + } else { //a function call const StringName &name = identifier; @@ -3716,7 +4008,9 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons int carg = -1; + pass_array = true; bool ok = _parse_function_arguments(p_block, p_function_info, func, &carg); + pass_array = false; // Check if block has a variable with the same name as function to prevent shader crash. ShaderLanguage::BlockNode *bnode = p_block; @@ -3769,6 +4063,10 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons FunctionNode *call_function = shader->functions[function_index].function; if (call_function) { + func->return_cache = call_function->get_datatype(); + func->struct_name = call_function->get_datatype_name(); + func->return_array_size = call_function->get_array_size(); + //get current base function FunctionNode *base_function = nullptr; { @@ -3943,60 +4241,61 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons Node *assign_expression = nullptr; if (array_size > 0) { - tk = _get_token(); - - if (tk.type != TK_BRACKET_OPEN && tk.type != TK_PERIOD && tk.type != TK_OP_ASSIGN) { - _set_error("Expected '[','.' or '='"); - return nullptr; - } + if (!pass_array) { + tk = _get_token(); - if (tk.type == TK_OP_ASSIGN) { - if (is_const) { - _set_error("Constants cannot be modified."); + if (tk.type != TK_BRACKET_OPEN && tk.type != TK_PERIOD && tk.type != TK_OP_ASSIGN) { + _set_error("Expected '[','.' or '='"); return nullptr; } - assign_expression = _parse_array_constructor(p_block, p_function_info, data_type, struct_name, array_size); - if (!assign_expression) { - return nullptr; - } - } else if (tk.type == TK_PERIOD) { - completion_class = TAG_ARRAY; - p_block->block_tag = SubClassTag::TAG_ARRAY; - call_expression = _parse_and_reduce_expression(p_block, p_function_info); - p_block->block_tag = SubClassTag::TAG_GLOBAL; - if (!call_expression) { - return nullptr; - } - data_type = call_expression->get_datatype(); - } else { // indexing - index_expression = _parse_and_reduce_expression(p_block, p_function_info); - if (!index_expression) { - return nullptr; - } + if (tk.type == TK_OP_ASSIGN) { + if (is_const) { + _set_error("Constants cannot be modified."); + return nullptr; + } + assign_expression = _parse_array_constructor(p_block, p_function_info, data_type, struct_name, array_size); + if (!assign_expression) { + return nullptr; + } + } else if (tk.type == TK_PERIOD) { + completion_class = TAG_ARRAY; + p_block->block_tag = SubClassTag::TAG_ARRAY; + call_expression = _parse_and_reduce_expression(p_block, p_function_info); + p_block->block_tag = SubClassTag::TAG_GLOBAL; + if (!call_expression) { + return nullptr; + } + data_type = call_expression->get_datatype(); + } else { // indexing + index_expression = _parse_and_reduce_expression(p_block, p_function_info); + if (!index_expression) { + return nullptr; + } - if (index_expression->get_datatype() != TYPE_INT && index_expression->get_datatype() != TYPE_UINT) { - _set_error("Only integer expressions are allowed for indexing"); - return nullptr; - } + if (index_expression->get_datatype() != TYPE_INT && index_expression->get_datatype() != TYPE_UINT) { + _set_error("Only integer expressions are allowed for indexing"); + return nullptr; + } - if (index_expression->type == Node::TYPE_CONSTANT) { - ConstantNode *cnode = (ConstantNode *)index_expression; - if (cnode) { - if (!cnode->values.is_empty()) { - int value = cnode->values[0].sint; - if (value < 0 || value >= array_size) { - _set_error(vformat("Index [%s] out of range [%s..%s]", value, 0, array_size - 1)); - return nullptr; + if (index_expression->type == Node::TYPE_CONSTANT) { + ConstantNode *cnode = (ConstantNode *)index_expression; + if (cnode) { + if (!cnode->values.is_empty()) { + int value = cnode->values[0].sint; + if (value < 0 || value >= array_size) { + _set_error(vformat("Index [%s] out of range [%s..%s]", value, 0, array_size - 1)); + return nullptr; + } } } } - } - tk = _get_token(); - if (tk.type != TK_BRACKET_CLOSE) { - _set_error("Expected ']'"); - return nullptr; + tk = _get_token(); + if (tk.type != TK_BRACKET_CLOSE) { + _set_error("Expected ']'"); + return nullptr; + } } } @@ -4008,6 +4307,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons arrname->call_expression = call_expression; arrname->assign_expression = assign_expression; arrname->is_const = is_const; + arrname->array_size = array_size; expr = arrname; } else { VariableNode *varname = alloc_node<VariableNode>(); @@ -4072,6 +4372,18 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons DataType dt = expr->get_datatype(); String st = expr->get_datatype_name(); + if (!expr->is_indexed() && expr->get_array_size() > 0) { + completion_class = TAG_ARRAY; + p_block->block_tag = SubClassTag::TAG_ARRAY; + Node *call_expression = _parse_and_reduce_expression(p_block, p_function_info); + p_block->block_tag = SubClassTag::TAG_GLOBAL; + if (!call_expression) { + return nullptr; + } + expr = call_expression; + break; + } + StringName identifier; if (_get_completable_identifier(p_block, dt == TYPE_STRUCT ? COMPLETION_STRUCT : COMPLETION_INDEX, identifier)) { if (dt == TYPE_STRUCT) { @@ -4348,6 +4660,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons mn->has_swizzling_duplicates = repeated; if (array_size > 0) { + TkPos prev_pos = _get_tkpos(); tk = _get_token(); if (tk.type == TK_OP_ASSIGN) { if (last_type == IDENTIFIER_CONSTANT) { @@ -4399,13 +4712,14 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons return nullptr; } mn->index_expression = index_expression; - } else { - _set_error("Expected '[','.' or '='"); - return nullptr; + if (!pass_array) { + _set_error("Expected '[','.' or '='"); + return nullptr; + } + _set_tkpos(prev_pos); } } - expr = mn; //todo @@ -4430,117 +4744,130 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons } DataType member_type = TYPE_VOID; + String member_struct_name; - switch (expr->get_datatype()) { - case TYPE_BVEC2: - case TYPE_VEC2: - case TYPE_IVEC2: - case TYPE_UVEC2: - case TYPE_MAT2: - if (index->type == Node::TYPE_CONSTANT) { - uint32_t index_constant = static_cast<ConstantNode *>(index)->values[0].uint; - if (index_constant >= 2) { - _set_error("Index out of range (0-1)"); - return nullptr; + if (expr->get_array_size() > 0) { + uint32_t index_constant = static_cast<ConstantNode *>(index)->values[0].uint; + if (index_constant >= (uint32_t)expr->get_array_size()) { + _set_error(vformat("Index [%s] out of range [%s..%s]", index_constant, 0, expr->get_array_size() - 1)); + return nullptr; + } + member_type = expr->get_datatype(); + if (member_type == TYPE_STRUCT) { + member_struct_name = expr->get_datatype_name(); + } + } else { + switch (expr->get_datatype()) { + case TYPE_BVEC2: + case TYPE_VEC2: + case TYPE_IVEC2: + case TYPE_UVEC2: + case TYPE_MAT2: + if (index->type == Node::TYPE_CONSTANT) { + uint32_t index_constant = static_cast<ConstantNode *>(index)->values[0].uint; + if (index_constant >= 2) { + _set_error("Index out of range (0-1)"); + return nullptr; + } } - } - switch (expr->get_datatype()) { - case TYPE_BVEC2: - member_type = TYPE_BOOL; - break; - case TYPE_VEC2: - member_type = TYPE_FLOAT; - break; - case TYPE_IVEC2: - member_type = TYPE_INT; - break; - case TYPE_UVEC2: - member_type = TYPE_UINT; - break; - case TYPE_MAT2: - member_type = TYPE_VEC2; - break; - default: - break; - } + switch (expr->get_datatype()) { + case TYPE_BVEC2: + member_type = TYPE_BOOL; + break; + case TYPE_VEC2: + member_type = TYPE_FLOAT; + break; + case TYPE_IVEC2: + member_type = TYPE_INT; + break; + case TYPE_UVEC2: + member_type = TYPE_UINT; + break; + case TYPE_MAT2: + member_type = TYPE_VEC2; + break; + default: + break; + } - break; - case TYPE_BVEC3: - case TYPE_VEC3: - case TYPE_IVEC3: - case TYPE_UVEC3: - case TYPE_MAT3: - if (index->type == Node::TYPE_CONSTANT) { - uint32_t index_constant = static_cast<ConstantNode *>(index)->values[0].uint; - if (index_constant >= 3) { - _set_error("Index out of range (0-2)"); - return nullptr; + break; + case TYPE_BVEC3: + case TYPE_VEC3: + case TYPE_IVEC3: + case TYPE_UVEC3: + case TYPE_MAT3: + if (index->type == Node::TYPE_CONSTANT) { + uint32_t index_constant = static_cast<ConstantNode *>(index)->values[0].uint; + if (index_constant >= 3) { + _set_error("Index out of range (0-2)"); + return nullptr; + } } - } - switch (expr->get_datatype()) { - case TYPE_BVEC3: - member_type = TYPE_BOOL; - break; - case TYPE_VEC3: - member_type = TYPE_FLOAT; - break; - case TYPE_IVEC3: - member_type = TYPE_INT; - break; - case TYPE_UVEC3: - member_type = TYPE_UINT; - break; - case TYPE_MAT3: - member_type = TYPE_VEC3; - break; - default: - break; - } - break; - case TYPE_BVEC4: - case TYPE_VEC4: - case TYPE_IVEC4: - case TYPE_UVEC4: - case TYPE_MAT4: - if (index->type == Node::TYPE_CONSTANT) { - uint32_t index_constant = static_cast<ConstantNode *>(index)->values[0].uint; - if (index_constant >= 4) { - _set_error("Index out of range (0-3)"); - return nullptr; + switch (expr->get_datatype()) { + case TYPE_BVEC3: + member_type = TYPE_BOOL; + break; + case TYPE_VEC3: + member_type = TYPE_FLOAT; + break; + case TYPE_IVEC3: + member_type = TYPE_INT; + break; + case TYPE_UVEC3: + member_type = TYPE_UINT; + break; + case TYPE_MAT3: + member_type = TYPE_VEC3; + break; + default: + break; + } + break; + case TYPE_BVEC4: + case TYPE_VEC4: + case TYPE_IVEC4: + case TYPE_UVEC4: + case TYPE_MAT4: + if (index->type == Node::TYPE_CONSTANT) { + uint32_t index_constant = static_cast<ConstantNode *>(index)->values[0].uint; + if (index_constant >= 4) { + _set_error("Index out of range (0-3)"); + return nullptr; + } } - } - switch (expr->get_datatype()) { - case TYPE_BVEC4: - member_type = TYPE_BOOL; - break; - case TYPE_VEC4: - member_type = TYPE_FLOAT; - break; - case TYPE_IVEC4: - member_type = TYPE_INT; - break; - case TYPE_UVEC4: - member_type = TYPE_UINT; - break; - case TYPE_MAT4: - member_type = TYPE_VEC4; - break; - default: - break; + switch (expr->get_datatype()) { + case TYPE_BVEC4: + member_type = TYPE_BOOL; + break; + case TYPE_VEC4: + member_type = TYPE_FLOAT; + break; + case TYPE_IVEC4: + member_type = TYPE_INT; + break; + case TYPE_UVEC4: + member_type = TYPE_UINT; + break; + case TYPE_MAT4: + member_type = TYPE_VEC4; + break; + default: + break; + } + break; + default: { + _set_error("Object of type '" + (expr->get_datatype() == TYPE_STRUCT ? expr->get_datatype_name() : get_datatype_name(expr->get_datatype())) + "' can't be indexed"); + return nullptr; } - break; - default: { - _set_error("Object of type '" + (expr->get_datatype() == TYPE_STRUCT ? expr->get_datatype_name() : get_datatype_name(expr->get_datatype())) + "' can't be indexed"); - return nullptr; } } - OperatorNode *op = alloc_node<OperatorNode>(); op->op = OP_INDEX; op->return_cache = member_type; + op->struct_name = member_struct_name; op->arguments.push_back(expr); op->arguments.push_back(index); expr = op; @@ -4556,7 +4883,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons op->op = tk.type == TK_OP_DECREMENT ? OP_POST_DECREMENT : OP_POST_INCREMENT; op->arguments.push_back(expr); - if (!_validate_operator(op, &op->return_cache)) { + if (!_validate_operator(op, &op->return_cache, &op->return_array_size)) { _set_error("Invalid base type for increment/decrement operator"); return nullptr; } @@ -4874,13 +5201,18 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons expression.write[i].is_op = false; expression.write[i].node = op; - if (!_validate_operator(op, &op->return_cache)) { + if (!_validate_operator(op, &op->return_cache, &op->return_array_size)) { String at; for (int j = 0; j < op->arguments.size(); j++) { if (j > 0) { at += " and "; } at += get_datatype_name(op->arguments[j]->get_datatype()); + if (!op->arguments[j]->is_indexed() && op->arguments[j]->get_array_size() > 0) { + at += "["; + at += itos(op->arguments[j]->get_array_size()); + at += "]"; + } } _set_error("Invalid arguments to unary operator '" + get_operator_text(op->op) + "' :" + at); return nullptr; @@ -4907,13 +5239,18 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons expression.write[next_op - 1].is_op = false; expression.write[next_op - 1].node = op; - if (!_validate_operator(op, &op->return_cache)) { + if (!_validate_operator(op, &op->return_cache, &op->return_array_size)) { String at; for (int i = 0; i < op->arguments.size(); i++) { if (i > 0) { at += " and "; } at += get_datatype_name(op->arguments[i]->get_datatype()); + if (!op->arguments[i]->is_indexed() && op->arguments[i]->get_array_size() > 0) { + at += "["; + at += itos(op->arguments[i]->get_array_size()); + at += "]"; + } } _set_error("Invalid argument to ternary ?: operator: " + at); return nullptr; @@ -4960,7 +5297,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons //replace all 3 nodes by this operator and make it an expression - if (!_validate_operator(op, &op->return_cache)) { + if (!_validate_operator(op, &op->return_cache, &op->return_array_size)) { String at; for (int i = 0; i < op->arguments.size(); i++) { if (i > 0) { @@ -4971,6 +5308,11 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons } else { at += get_datatype_name(op->arguments[i]->get_datatype()); } + if (!op->arguments[i]->is_indexed() && op->arguments[i]->get_array_size() > 0) { + at += "["; + at += itos(op->arguments[i]->get_array_size()); + at += "]"; + } } _set_error("Invalid arguments to operator '" + get_operator_text(op->op) + "' :" + at); return nullptr; @@ -5227,6 +5569,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun ArrayDeclarationNode::Declaration decl; decl.name = name; decl.size = 0U; + decl.single_expression = false; pos = _get_tkpos(); tk = _get_token(); @@ -5293,179 +5636,205 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun return ERR_PARSE_ERROR; } + TkPos prev_pos = _get_tkpos(); tk = _get_token(); - if (tk.type != TK_CURLY_BRACKET_OPEN) { - if (unknown_size) { - _set_error("Expected '{'"); - return ERR_PARSE_ERROR; - } - - full_def = true; + if (tk.type == TK_IDENTIFIER) { // a function call array initialization + _set_tkpos(prev_pos); + pass_array = true; + Node *n = _parse_and_reduce_expression(p_block, p_function_info); + pass_array = false; - DataPrecision precision2 = PRECISION_DEFAULT; - if (is_token_precision(tk.type)) { - precision2 = get_token_precision(tk.type); - tk = _get_token(); - if (shader->structs.has(tk.text)) { - _set_error("Precision modifier cannot be used on structs."); - return ERR_PARSE_ERROR; + if (!n) { + _set_error("Expected correct array initializer!"); + return ERR_PARSE_ERROR; + } else { + if (unknown_size) { + decl.size = n->get_array_size(); + var.array_size = n->get_array_size(); } - if (!is_token_nonvoid_datatype(tk.type)) { - _set_error("Expected datatype after precision"); + + if (!_compare_datatypes(var.type, var.struct_name, var.array_size, n->get_datatype(), n->get_datatype_name(), n->get_array_size())) { return ERR_PARSE_ERROR; } - } - DataType type2; - StringName struct_name2 = ""; + decl.single_expression = true; + decl.initializer.push_back(n); + } - if (shader->structs.has(tk.text)) { - type2 = TYPE_STRUCT; - struct_name2 = tk.text; - } else { - if (!is_token_variable_datatype(tk.type)) { - _set_error("Invalid data type for array"); + tk = _get_token(); + } else { + if (tk.type != TK_CURLY_BRACKET_OPEN) { + if (unknown_size) { + _set_error("Expected '{'"); return ERR_PARSE_ERROR; } - type2 = get_token_datatype(tk.type); - } - int array_size2 = 0; + full_def = true; - tk = _get_token(); - if (tk.type == TK_BRACKET_OPEN) { - TkPos pos2 = _get_tkpos(); - tk = _get_token(); - if (tk.type == TK_BRACKET_CLOSE) { - array_size2 = var.array_size; + DataPrecision precision2 = PRECISION_DEFAULT; + if (is_token_precision(tk.type)) { + precision2 = get_token_precision(tk.type); tk = _get_token(); - } else { - _set_tkpos(pos2); - - Node *n = _parse_and_reduce_expression(p_block, p_function_info); - if (!n || n->type != Node::TYPE_CONSTANT || n->get_datatype() != TYPE_INT) { - _set_error("Expected single integer constant > 0"); + if (shader->structs.has(tk.text)) { + _set_error("Precision modifier cannot be used on structs."); + return ERR_PARSE_ERROR; + } + if (!is_token_nonvoid_datatype(tk.type)) { + _set_error("Expected datatype after precision"); return ERR_PARSE_ERROR; } + } - ConstantNode *cnode = (ConstantNode *)n; - if (cnode->values.size() == 1) { - array_size2 = cnode->values[0].sint; - if (array_size2 <= 0) { - _set_error("Expected single integer constant > 0"); - return ERR_PARSE_ERROR; - } - } else { - _set_error("Expected single integer constant > 0"); + DataType type2; + StringName struct_name2 = ""; + + if (shader->structs.has(tk.text)) { + type2 = TYPE_STRUCT; + struct_name2 = tk.text; + } else { + if (!is_token_variable_datatype(tk.type)) { + _set_error("Invalid data type for array"); return ERR_PARSE_ERROR; } + type2 = get_token_datatype(tk.type); + } + int array_size2 = 0; + + tk = _get_token(); + if (tk.type == TK_BRACKET_OPEN) { + TkPos pos2 = _get_tkpos(); tk = _get_token(); - if (tk.type != TK_BRACKET_CLOSE) { - _set_error("Expected ']'"); - return ERR_PARSE_ERROR; + if (tk.type == TK_BRACKET_CLOSE) { + array_size2 = var.array_size; + tk = _get_token(); } else { + _set_tkpos(pos2); + + Node *n = _parse_and_reduce_expression(p_block, p_function_info); + if (!n || n->type != Node::TYPE_CONSTANT || n->get_datatype() != TYPE_INT) { + _set_error("Expected single integer constant > 0"); + return ERR_PARSE_ERROR; + } + + ConstantNode *cnode = (ConstantNode *)n; + if (cnode->values.size() == 1) { + array_size2 = cnode->values[0].sint; + if (array_size2 <= 0) { + _set_error("Expected single integer constant > 0"); + return ERR_PARSE_ERROR; + } + } else { + _set_error("Expected single integer constant > 0"); + return ERR_PARSE_ERROR; + } + tk = _get_token(); + if (tk.type != TK_BRACKET_CLOSE) { + _set_error("Expected ']'"); + return ERR_PARSE_ERROR; + } else { + tk = _get_token(); + } } - } - } else { - _set_error("Expected '['"); - return ERR_PARSE_ERROR; - } - - if (precision != precision2 || type != type2 || struct_name != struct_name2 || var.array_size != array_size2) { - String error_str = "Cannot convert from '"; - if (precision2 != PRECISION_DEFAULT) { - error_str += get_precision_name(precision2); - error_str += " "; - } - if (type2 == TYPE_STRUCT) { - error_str += struct_name2; } else { - error_str += get_datatype_name(type2); - } - error_str += "["; - error_str += itos(array_size2); - error_str += "]'"; - error_str += " to '"; - if (precision != PRECISION_DEFAULT) { - error_str += get_precision_name(precision); - error_str += " "; + _set_error("Expected '['"); + return ERR_PARSE_ERROR; } - if (type == TYPE_STRUCT) { - error_str += struct_name; - } else { - error_str += get_datatype_name(type); + + if (precision != precision2 || type != type2 || struct_name != struct_name2 || var.array_size != array_size2) { + String error_str = "Cannot convert from '"; + if (precision2 != PRECISION_DEFAULT) { + error_str += get_precision_name(precision2); + error_str += " "; + } + if (type2 == TYPE_STRUCT) { + error_str += struct_name2; + } else { + error_str += get_datatype_name(type2); + } + error_str += "["; + error_str += itos(array_size2); + error_str += "]'"; + error_str += " to '"; + if (precision != PRECISION_DEFAULT) { + error_str += get_precision_name(precision); + error_str += " "; + } + if (type == TYPE_STRUCT) { + error_str += struct_name; + } else { + error_str += get_datatype_name(type); + } + error_str += "["; + error_str += itos(var.array_size); + error_str += "]'"; + _set_error(error_str); + return ERR_PARSE_ERROR; } - error_str += "["; - error_str += itos(var.array_size); - error_str += "]'"; - _set_error(error_str); - return ERR_PARSE_ERROR; } - } - bool curly = tk.type == TK_CURLY_BRACKET_OPEN; + bool curly = tk.type == TK_CURLY_BRACKET_OPEN; - if (unknown_size) { - if (!curly) { - _set_error("Expected '{'"); - return ERR_PARSE_ERROR; - } - } else { - if (full_def) { - if (curly) { - _set_error("Expected '('"); + if (unknown_size) { + if (!curly) { + _set_error("Expected '{'"); return ERR_PARSE_ERROR; } + } else { + if (full_def) { + if (curly) { + _set_error("Expected '('"); + return ERR_PARSE_ERROR; + } + } } - } - if (tk.type == TK_PARENTHESIS_OPEN || curly) { // initialization - while (true) { - Node *n = _parse_and_reduce_expression(p_block, p_function_info); - if (!n) { - return ERR_PARSE_ERROR; - } + if (tk.type == TK_PARENTHESIS_OPEN || curly) { // initialization + while (true) { + Node *n = _parse_and_reduce_expression(p_block, p_function_info); + if (!n) { + return ERR_PARSE_ERROR; + } - if (node->is_const && n->type == Node::TYPE_OPERATOR && ((OperatorNode *)n)->op == OP_CALL) { - _set_error("Expected constant expression"); - return ERR_PARSE_ERROR; - } + if (node->is_const && n->type == Node::TYPE_OPERATOR && ((OperatorNode *)n)->op == OP_CALL) { + _set_error("Expected constant expression"); + return ERR_PARSE_ERROR; + } - if (var.type != n->get_datatype() || struct_name != n->get_datatype_name()) { - _set_error("Invalid assignment of '" + (n->get_datatype() == TYPE_STRUCT ? n->get_datatype_name() : get_datatype_name(n->get_datatype())) + "' to '" + (var.type == TYPE_STRUCT ? struct_name : get_datatype_name(var.type)) + "'"); - return ERR_PARSE_ERROR; - } + if (!_compare_datatypes(var.type, struct_name, 0, n->get_datatype(), n->get_datatype_name(), 0)) { + return ERR_PARSE_ERROR; + } - tk = _get_token(); - if (tk.type == TK_COMMA) { - decl.initializer.push_back(n); - continue; - } else if (!curly && tk.type == TK_PARENTHESIS_CLOSE) { - decl.initializer.push_back(n); - break; - } else if (curly && tk.type == TK_CURLY_BRACKET_CLOSE) { - decl.initializer.push_back(n); - break; - } else { - if (curly) { - _set_error("Expected '}' or ','"); + tk = _get_token(); + if (tk.type == TK_COMMA) { + decl.initializer.push_back(n); + continue; + } else if (!curly && tk.type == TK_PARENTHESIS_CLOSE) { + decl.initializer.push_back(n); + break; + } else if (curly && tk.type == TK_CURLY_BRACKET_CLOSE) { + decl.initializer.push_back(n); + break; } else { - _set_error("Expected ')' or ','"); + if (curly) { + _set_error("Expected '}' or ','"); + } else { + _set_error("Expected ')' or ','"); + } + return ERR_PARSE_ERROR; } + } + if (unknown_size) { + decl.size = decl.initializer.size(); + var.array_size = decl.initializer.size(); + } else if (decl.initializer.size() != var.array_size) { + _set_error("Array size mismatch"); return ERR_PARSE_ERROR; } + tk = _get_token(); } - if (unknown_size) { - decl.size = decl.initializer.size(); - var.array_size = decl.initializer.size(); - } else if (decl.initializer.size() != var.array_size) { - _set_error("Array size mismatch"); - return ERR_PARSE_ERROR; - } - tk = _get_token(); } } else { if (unknown_size) { @@ -5518,8 +5887,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun } } - if (var.type == TYPE_STRUCT ? (var.struct_name != n->get_datatype_name()) : (var.type != n->get_datatype())) { - _set_error("Invalid assignment of '" + (n->get_datatype() == TYPE_STRUCT ? n->get_datatype_name() : get_datatype_name(n->get_datatype())) + "' to '" + (var.type == TYPE_STRUCT ? String(var.struct_name) : get_datatype_name(var.type)) + "'"); + if (!_compare_datatypes(var.type, var.struct_name, var.array_size, n->get_datatype(), n->get_datatype_name(), n->get_array_size())) { return ERR_PARSE_ERROR; } tk = _get_token(); @@ -5980,6 +6348,11 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun } String return_struct_name = String(b->parent_function->return_struct_name); + String array_size_string; + + if (b->parent_function->return_array_size > 0) { + array_size_string = "[" + itos(b->parent_function->return_array_size) + "]"; + } ControlFlowNode *flow = alloc_node<ControlFlowNode>(); flow->flow_op = FLOW_OP_RETURN; @@ -5989,18 +6362,27 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun if (tk.type == TK_SEMICOLON) { //all is good if (b->parent_function->return_type != TYPE_VOID) { - _set_error("Expected return with an expression of type '" + (return_struct_name != "" ? return_struct_name : get_datatype_name(b->parent_function->return_type)) + "'"); + _set_error("Expected return with an expression of type '" + (return_struct_name != "" ? return_struct_name : get_datatype_name(b->parent_function->return_type)) + array_size_string + "'"); return ERR_PARSE_ERROR; } } else { _set_tkpos(pos); //rollback, wants expression + + pass_array = true; Node *expr = _parse_and_reduce_expression(p_block, p_function_info); if (!expr) { return ERR_PARSE_ERROR; } + pass_array = false; + + bool array_size_incorrect = false; + + if (b->parent_function->return_array_size > 0 && b->parent_function->return_array_size != expr->get_array_size()) { + array_size_incorrect = true; + } - if (b->parent_function->return_type != expr->get_datatype() || return_struct_name != expr->get_datatype_name()) { - _set_error("Expected return with an expression of type '" + (return_struct_name != "" ? return_struct_name : get_datatype_name(b->parent_function->return_type)) + "'"); + if (b->parent_function->return_type != expr->get_datatype() || array_size_incorrect || return_struct_name != expr->get_datatype_name()) { + _set_error("Expected return with an expression of type '" + (return_struct_name != "" ? return_struct_name : get_datatype_name(b->parent_function->return_type)) + array_size_string + "'"); return ERR_PARSE_ERROR; } @@ -6795,6 +7177,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct DataPrecision precision = PRECISION_DEFAULT; DataType type; StringName name; + int return_array_size = 0; if (tk.type == TK_CONST) { is_constant = true; @@ -6832,10 +7215,33 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct } TkPos prev_pos = _get_tkpos(); tk = _get_token(); + if (tk.type == TK_BRACKET_OPEN) { - _set_error("Cannot use arrays as return types"); - return ERR_PARSE_ERROR; + bool error = false; + tk = _get_token(); + + if (tk.type == TK_INT_CONSTANT) { + return_array_size = (int)tk.constant; + if (return_array_size > 0) { + tk = _get_token(); + if (tk.type != TK_BRACKET_CLOSE) { + _set_error("Expected ']'"); + return ERR_PARSE_ERROR; + } + } else { + error = true; + } + } else { + error = true; + } + if (error) { + _set_error("Expected integer constant > 0"); + return ERR_PARSE_ERROR; + } + + prev_pos = _get_tkpos(); } + _set_tkpos(prev_pos); _get_completable_identifier(nullptr, COMPLETION_MAIN_FUNCTION, name); @@ -7049,8 +7455,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct return ERR_PARSE_ERROR; } - if (constant.type != n->get_datatype() || n->get_datatype_name() != struct_name) { - _set_error("Invalid assignment of '" + (n->get_datatype() == TYPE_STRUCT ? n->get_datatype_name() : get_datatype_name(n->get_datatype())) + "' to '" + (is_struct ? String(struct_name) : get_datatype_name(constant.type)) + "'"); + if (!_compare_datatypes(constant.type, struct_name, 0, n->get_datatype(), n->get_datatype_name(), 0)) { return ERR_PARSE_ERROR; } @@ -7111,8 +7516,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct constant.initializer = static_cast<ConstantNode *>(expr); - if (type != expr->get_datatype() || expr->get_datatype_name() != struct_name) { - _set_error("Invalid assignment of '" + (expr->get_datatype() == TYPE_STRUCT ? expr->get_datatype_name() : get_datatype_name(expr->get_datatype())) + "' to '" + (is_struct ? String(struct_name) : get_datatype_name(type)) + "'"); + if (!_compare_datatypes(type, struct_name, 0, expr->get_datatype(), expr->get_datatype_name(), 0)) { return ERR_PARSE_ERROR; } } @@ -7192,6 +7596,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct func_node->return_type = type; func_node->return_struct_name = struct_name; func_node->return_precision = precision; + func_node->return_array_size = return_array_size; if (p_functions.has(name)) { func_node->can_discard = p_functions[name].can_discard; @@ -7245,6 +7650,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct StringName param_struct_name; DataPrecision pprecision = PRECISION_DEFAULT; bool use_precision = false; + int array_size = 0; if (is_token_precision(tk.type)) { pprecision = get_token_precision(tk.type); @@ -7291,8 +7697,29 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct tk = _get_token(); if (tk.type == TK_BRACKET_OPEN) { - _set_error("Arrays as parameters are not implemented yet"); - return ERR_PARSE_ERROR; + bool error = false; + tk = _get_token(); + + if (tk.type == TK_INT_CONSTANT) { + array_size = (int)tk.constant; + + if (array_size > 0) { + tk = _get_token(); + if (tk.type != TK_BRACKET_CLOSE) { + _set_error("Expected ']'"); + return ERR_PARSE_ERROR; + } + } else { + error = true; + } + } else { + error = true; + } + if (error) { + _set_error("Expected integer constant > 0"); + return ERR_PARSE_ERROR; + } + tk = _get_token(); } if (tk.type != TK_IDENTIFIER) { _set_error("Expected identifier for argument name"); @@ -7326,14 +7753,41 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct arg.tex_argument_repeat = REPEAT_DEFAULT; arg.is_const = is_const; - func_node->arguments.push_back(arg); - tk = _get_token(); if (tk.type == TK_BRACKET_OPEN) { - _set_error("Arrays as parameters are not implemented yet"); - return ERR_PARSE_ERROR; + if (array_size > 0) { + _set_error("Array size is already defined!"); + return ERR_PARSE_ERROR; + } + bool error = false; + tk = _get_token(); + + if (tk.type == TK_INT_CONSTANT) { + array_size = (int)tk.constant; + + if (array_size > 0) { + tk = _get_token(); + if (tk.type != TK_BRACKET_CLOSE) { + _set_error("Expected ']'"); + return ERR_PARSE_ERROR; + } + } else { + error = true; + } + } else { + error = true; + } + + if (error) { + _set_error("Expected integer constant > 0"); + return ERR_PARSE_ERROR; + } + tk = _get_token(); } + arg.array_size = array_size; + func_node->arguments.push_back(arg); + if (tk.type == TK_COMMA) { tk = _get_token(); //do none and go on @@ -7756,6 +8210,13 @@ Error ShaderLanguage::complete(const String &p_code, const Map<StringName, Funct String calltip; calltip += get_datatype_name(shader->functions[i].function->return_type); + + if (shader->functions[i].function->return_array_size > 0) { + calltip += "["; + calltip += itos(shader->functions[i].function->return_array_size); + calltip += "]"; + } + calltip += " "; calltip += shader->functions[i].name; calltip += "("; @@ -7787,6 +8248,12 @@ Error ShaderLanguage::complete(const String &p_code, const Map<StringName, Funct calltip += " "; calltip += shader->functions[i].function->arguments[j].name; + if (shader->functions[i].function->arguments[j].array_size > 0) { + calltip += "["; + calltip += itos(shader->functions[i].function->arguments[j].array_size); + calltip += "]"; + } + if (j == completion_argument) { calltip += char32_t(0xFFFF); } diff --git a/servers/rendering/shader_language.h b/servers/rendering/shader_language.h index c8c5bb1fa7..af66e32e06 100644 --- a/servers/rendering/shader_language.h +++ b/servers/rendering/shader_language.h @@ -369,6 +369,8 @@ public: virtual DataType get_datatype() const { return TYPE_VOID; } virtual String get_datatype_name() const { return ""; } + virtual int get_array_size() const { return 0; } + virtual bool is_indexed() const { return false; } Node(Type t) : type(t) {} @@ -388,11 +390,15 @@ public: struct OperatorNode : public Node { DataType return_cache = TYPE_VOID; DataPrecision return_precision_cache = PRECISION_DEFAULT; + int return_array_size = 0; Operator op = OP_EQUAL; StringName struct_name; Vector<Node *> arguments; - virtual DataType get_datatype() const { return return_cache; } - virtual String get_datatype_name() const { return String(struct_name); } + + virtual DataType get_datatype() const override { return return_cache; } + virtual String get_datatype_name() const override { return String(struct_name); } + virtual int get_array_size() const override { return return_array_size; } + virtual bool is_indexed() const override { return op == OP_INDEX; } OperatorNode() : Node(TYPE_OPERATOR) {} @@ -402,10 +408,11 @@ public: DataType datatype_cache = TYPE_VOID; StringName name; StringName struct_name; - virtual DataType get_datatype() const { return datatype_cache; } - virtual String get_datatype_name() const { return String(struct_name); } bool is_const = false; + virtual DataType get_datatype() const override { return datatype_cache; } + virtual String get_datatype_name() const override { return String(struct_name); } + VariableNode() : Node(TYPE_VARIABLE) {} }; @@ -420,9 +427,9 @@ public: StringName name; Node *initializer; }; - Vector<Declaration> declarations; - virtual DataType get_datatype() const { return datatype; } + + virtual DataType get_datatype() const override { return datatype; } VariableDeclarationNode() : Node(TYPE_VARIABLE_DECLARATION) {} @@ -436,9 +443,12 @@ public: Node *call_expression = nullptr; Node *assign_expression = nullptr; bool is_const = false; + int array_size = 0; - virtual DataType get_datatype() const { return datatype_cache; } - virtual String get_datatype_name() const { return String(struct_name); } + virtual DataType get_datatype() const override { return datatype_cache; } + virtual String get_datatype_name() const override { return String(struct_name); } + virtual int get_array_size() const override { return array_size; } + virtual bool is_indexed() const override { return index_expression != nullptr; } ArrayNode() : Node(TYPE_ARRAY) {} @@ -449,6 +459,10 @@ public: String struct_name; Vector<Node *> initializer; + virtual DataType get_datatype() const override { return datatype; } + virtual String get_datatype_name() const override { return struct_name; } + virtual int get_array_size() const override { return initializer.size(); } + ArrayConstructNode() : Node(TYPE_ARRAY_CONSTRUCT) {} }; @@ -464,10 +478,11 @@ public: StringName name; uint32_t size; Vector<Node *> initializer; + bool single_expression; }; - Vector<Declaration> declarations; - virtual DataType get_datatype() const { return datatype; } + + virtual DataType get_datatype() const override { return datatype; } ArrayDeclarationNode() : Node(TYPE_ARRAY_DECLARATION) {} @@ -487,8 +502,10 @@ public: Vector<Value> values; Vector<ArrayDeclarationNode::Declaration> array_declarations; - virtual DataType get_datatype() const { return datatype; } - virtual String get_datatype_name() const { return struct_name; } + + virtual DataType get_datatype() const override { return datatype; } + virtual String get_datatype_name() const override { return struct_name; } + virtual int get_array_size() const override { return array_size; } ConstantNode() : Node(TYPE_CONSTANT) {} @@ -553,8 +570,10 @@ public: Node *call_expression = nullptr; bool has_swizzling_duplicates = false; - virtual DataType get_datatype() const { return datatype; } - virtual String get_datatype_name() const { return String(struct_name); } + virtual DataType get_datatype() const override { return datatype; } + virtual String get_datatype_name() const override { return String(struct_name); } + virtual int get_array_size() const override { return array_size; } + virtual bool is_indexed() const override { return index_expression != nullptr || call_expression != nullptr; } MemberNode() : Node(TYPE_MEMBER) {} @@ -580,6 +599,7 @@ public: bool tex_builtin_check; StringName tex_builtin; bool is_const; + int array_size; Map<StringName, Set<int>> tex_argument_connect; }; @@ -588,10 +608,15 @@ public: DataType return_type = TYPE_VOID; StringName return_struct_name; DataPrecision return_precision = PRECISION_DEFAULT; + int return_array_size = 0; Vector<Argument> arguments; BlockNode *body = nullptr; bool can_discard = false; + virtual DataType get_datatype() const override { return return_type; } + virtual String get_datatype_name() const override { return String(return_struct_name); } + virtual int get_array_size() const override { return return_array_size; } + FunctionNode() : Node(TYPE_FUNCTION) {} }; @@ -842,6 +867,8 @@ private: int tk_line; StringName current_function; + bool last_const = false; + bool pass_array = false; StringName last_name; VaryingFunctionNames varying_function_names; @@ -894,7 +921,7 @@ private: #endif // DEBUG_ENABLED bool _is_operator_assign(Operator p_op) const; bool _validate_assign(Node *p_node, const FunctionInfo &p_function_info, String *r_message = nullptr); - bool _validate_operator(OperatorNode *p_op, DataType *r_ret_type = nullptr); + bool _validate_operator(OperatorNode *p_op, DataType *r_ret_type = nullptr, int *r_ret_size = nullptr); struct BuiltinFuncDef { enum { MAX_ARGS = 5 }; @@ -925,7 +952,8 @@ private: static const BuiltinFuncOutArgs builtin_func_out_args[]; Error _validate_datatype(DataType p_type); - bool _compare_datatypes_in_nodes(Node *a, Node *b) const; + bool _compare_datatypes(DataType p_datatype_a, String p_datatype_name_a, int p_array_size_a, DataType p_datatype_b, String p_datatype_name_b, int p_array_size_b); + bool _compare_datatypes_in_nodes(Node *a, Node *b); bool _validate_function_call(BlockNode *p_block, const FunctionInfo &p_function_info, OperatorNode *p_func, DataType *r_ret_type, StringName *r_ret_type_str); bool _parse_function_arguments(BlockNode *p_block, const FunctionInfo &p_function_info, OperatorNode *p_func, int *r_complete_arg = nullptr); @@ -936,6 +964,7 @@ private: bool _check_node_constness(const Node *p_node) const; Node *_parse_expression(BlockNode *p_block, const FunctionInfo &p_function_info); + Node *_parse_array_constructor(BlockNode *p_block, const FunctionInfo &p_function_info); Node *_parse_array_constructor(BlockNode *p_block, const FunctionInfo &p_function_info, DataType p_type, const StringName &p_struct_name, int p_array_size); ShaderLanguage::Node *_reduce_expression(BlockNode *p_block, ShaderLanguage::Node *p_node); diff --git a/servers/rendering/shader_types.cpp b/servers/rendering/shader_types.cpp index 0bf68b9e0f..a1e892498b 100644 --- a/servers/rendering/shader_types.cpp +++ b/servers/rendering/shader_types.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "shader_types.h" +#include "core/math/math_defs.h" const Map<StringName, ShaderLanguage::FunctionInfo> &ShaderTypes::get_functions(RS::ShaderMode p_mode) { return shader_modes[p_mode].functions; @@ -54,6 +55,9 @@ ShaderTypes::ShaderTypes() { /*************** SPATIAL ***********************/ shader_modes[RS::SHADER_SPATIAL].functions["global"].built_ins["TIME"] = constt(ShaderLanguage::TYPE_FLOAT); + shader_modes[RS::SHADER_SPATIAL].functions["global"].built_ins["PI"] = constt(ShaderLanguage::TYPE_FLOAT); + shader_modes[RS::SHADER_SPATIAL].functions["global"].built_ins["TAU"] = constt(ShaderLanguage::TYPE_FLOAT); + shader_modes[RS::SHADER_SPATIAL].functions["global"].built_ins["E"] = constt(ShaderLanguage::TYPE_FLOAT); shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["VERTEX"] = ShaderLanguage::TYPE_VEC3; shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["NORMAL"] = ShaderLanguage::TYPE_VEC3; @@ -120,11 +124,11 @@ ShaderTypes::ShaderTypes() { shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["AO"] = ShaderLanguage::TYPE_FLOAT; shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["AO_LIGHT_AFFECT"] = ShaderLanguage::TYPE_FLOAT; shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["EMISSION"] = ShaderLanguage::TYPE_VEC3; - shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["SCREEN_TEXTURE"] = ShaderLanguage::TYPE_SAMPLER2D; - shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["NORMAL_ROUGHNESS_TEXTURE"] = ShaderLanguage::TYPE_SAMPLER2D; - shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["DEPTH_TEXTURE"] = ShaderLanguage::TYPE_SAMPLER2D; + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["SCREEN_TEXTURE"] = constt(ShaderLanguage::TYPE_SAMPLER2D); + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["NORMAL_ROUGHNESS_TEXTURE"] = constt(ShaderLanguage::TYPE_SAMPLER2D); + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["DEPTH_TEXTURE"] = constt(ShaderLanguage::TYPE_SAMPLER2D); shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["DEPTH"] = ShaderLanguage::TYPE_FLOAT; - shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["SCREEN_UV"] = ShaderLanguage::TYPE_VEC2; + shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["SCREEN_UV"] = constt(ShaderLanguage::TYPE_VEC2); shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["POINT_COORD"] = constt(ShaderLanguage::TYPE_VEC2); shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["OUTPUT_IS_SRGB"] = constt(ShaderLanguage::TYPE_BOOL); @@ -227,6 +231,9 @@ ShaderTypes::ShaderTypes() { /************ CANVAS ITEM **************************/ shader_modes[RS::SHADER_CANVAS_ITEM].functions["global"].built_ins["TIME"] = constt(ShaderLanguage::TYPE_FLOAT); + shader_modes[RS::SHADER_CANVAS_ITEM].functions["global"].built_ins["PI"] = constt(ShaderLanguage::TYPE_FLOAT); + shader_modes[RS::SHADER_CANVAS_ITEM].functions["global"].built_ins["TAU"] = constt(ShaderLanguage::TYPE_FLOAT); + shader_modes[RS::SHADER_CANVAS_ITEM].functions["global"].built_ins["E"] = constt(ShaderLanguage::TYPE_FLOAT); shader_modes[RS::SHADER_CANVAS_ITEM].functions["vertex"].built_ins["VERTEX"] = ShaderLanguage::TYPE_VEC2; shader_modes[RS::SHADER_CANVAS_ITEM].functions["vertex"].built_ins["UV"] = ShaderLanguage::TYPE_VEC2; @@ -317,6 +324,9 @@ ShaderTypes::ShaderTypes() { /************ PARTICLES **************************/ shader_modes[RS::SHADER_PARTICLES].functions["global"].built_ins["TIME"] = constt(ShaderLanguage::TYPE_FLOAT); + shader_modes[RS::SHADER_PARTICLES].functions["global"].built_ins["PI"] = constt(ShaderLanguage::TYPE_FLOAT); + shader_modes[RS::SHADER_PARTICLES].functions["global"].built_ins["TAU"] = constt(ShaderLanguage::TYPE_FLOAT); + shader_modes[RS::SHADER_PARTICLES].functions["global"].built_ins["E"] = constt(ShaderLanguage::TYPE_FLOAT); shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["COLOR"] = ShaderLanguage::TYPE_VEC4; shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["VELOCITY"] = ShaderLanguage::TYPE_VEC3; @@ -381,6 +391,9 @@ ShaderTypes::ShaderTypes() { /************ SKY **************************/ shader_modes[RS::SHADER_SKY].functions["global"].built_ins["TIME"] = constt(ShaderLanguage::TYPE_FLOAT); + shader_modes[RS::SHADER_SKY].functions["global"].built_ins["PI"] = constt(ShaderLanguage::TYPE_FLOAT); + shader_modes[RS::SHADER_SKY].functions["global"].built_ins["TAU"] = constt(ShaderLanguage::TYPE_FLOAT); + shader_modes[RS::SHADER_SKY].functions["global"].built_ins["E"] = constt(ShaderLanguage::TYPE_FLOAT); shader_modes[RS::SHADER_SKY].functions["global"].built_ins["POSITION"] = constt(ShaderLanguage::TYPE_VEC3); shader_modes[RS::SHADER_SKY].functions["global"].built_ins["RADIANCE"] = constt(ShaderLanguage::TYPE_SAMPLERCUBE); shader_modes[RS::SHADER_SKY].functions["global"].built_ins["AT_HALF_RES_PASS"] = constt(ShaderLanguage::TYPE_BOOL); |