/*************************************************************************/ /* renderer_storage_rd.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ /* Copyright (c) 2014-2022 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. */ /*************************************************************************/ #include "renderer_storage_rd.h" #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/rendering_server_globals.h" #include "servers/rendering/shader_language.h" bool RendererStorageRD::can_create_resources_async() const { return true; } Ref RendererStorageRD::_validate_texture_format(const Ref &p_image, TextureToRDFormat &r_format) { Ref image = p_image->duplicate(); switch (p_image->get_format()) { case Image::FORMAT_L8: { r_format.format = RD::DATA_FORMAT_R8_UNORM; r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; r_format.swizzle_g = RD::TEXTURE_SWIZZLE_R; r_format.swizzle_b = RD::TEXTURE_SWIZZLE_R; r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; } break; //luminance case Image::FORMAT_LA8: { r_format.format = RD::DATA_FORMAT_R8G8_UNORM; r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; r_format.swizzle_g = RD::TEXTURE_SWIZZLE_R; r_format.swizzle_b = RD::TEXTURE_SWIZZLE_R; r_format.swizzle_a = RD::TEXTURE_SWIZZLE_G; } break; //luminance-alpha case Image::FORMAT_R8: { r_format.format = RD::DATA_FORMAT_R8_UNORM; r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; r_format.swizzle_g = RD::TEXTURE_SWIZZLE_ZERO; r_format.swizzle_b = RD::TEXTURE_SWIZZLE_ZERO; r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; } break; case Image::FORMAT_RG8: { r_format.format = RD::DATA_FORMAT_R8G8_UNORM; r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; r_format.swizzle_b = RD::TEXTURE_SWIZZLE_ZERO; r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; } break; case Image::FORMAT_RGB8: { //this format is not mandatory for specification, check if supported first if (false && RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_R8G8B8_UNORM, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT) && RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_R8G8B8_SRGB, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) { r_format.format = RD::DATA_FORMAT_R8G8B8_UNORM; r_format.format_srgb = RD::DATA_FORMAT_R8G8B8_SRGB; } else { //not supported, reconvert r_format.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; r_format.format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB; image->convert(Image::FORMAT_RGBA8); } r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B; r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; } break; case Image::FORMAT_RGBA8: { r_format.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; r_format.format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB; r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B; r_format.swizzle_a = RD::TEXTURE_SWIZZLE_A; } break; case Image::FORMAT_RGBA4444: { r_format.format = RD::DATA_FORMAT_B4G4R4A4_UNORM_PACK16; r_format.swizzle_r = RD::TEXTURE_SWIZZLE_B; //needs swizzle r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; r_format.swizzle_b = RD::TEXTURE_SWIZZLE_R; r_format.swizzle_a = RD::TEXTURE_SWIZZLE_A; } break; case Image::FORMAT_RGB565: { r_format.format = RD::DATA_FORMAT_B5G6R5_UNORM_PACK16; r_format.swizzle_r = RD::TEXTURE_SWIZZLE_B; r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; r_format.swizzle_b = RD::TEXTURE_SWIZZLE_R; r_format.swizzle_a = RD::TEXTURE_SWIZZLE_A; } break; case Image::FORMAT_RF: { r_format.format = RD::DATA_FORMAT_R32_SFLOAT; r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; r_format.swizzle_g = RD::TEXTURE_SWIZZLE_ZERO; r_format.swizzle_b = RD::TEXTURE_SWIZZLE_ZERO; r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; } break; //float case Image::FORMAT_RGF: { r_format.format = RD::DATA_FORMAT_R32G32_SFLOAT; r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; r_format.swizzle_b = RD::TEXTURE_SWIZZLE_ZERO; r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; } break; case Image::FORMAT_RGBF: { //this format is not mandatory for specification, check if supported first if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_R32G32B32_SFLOAT, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) { r_format.format = RD::DATA_FORMAT_R32G32B32_SFLOAT; } else { //not supported, reconvert r_format.format = RD::DATA_FORMAT_R32G32B32A32_SFLOAT; image->convert(Image::FORMAT_RGBAF); } r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B; r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; } break; case Image::FORMAT_RGBAF: { r_format.format = RD::DATA_FORMAT_R32G32B32A32_SFLOAT; r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B; r_format.swizzle_a = RD::TEXTURE_SWIZZLE_A; } break; case Image::FORMAT_RH: { r_format.format = RD::DATA_FORMAT_R16_SFLOAT; r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; r_format.swizzle_g = RD::TEXTURE_SWIZZLE_ZERO; r_format.swizzle_b = RD::TEXTURE_SWIZZLE_ZERO; r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; } break; //half float case Image::FORMAT_RGH: { r_format.format = RD::DATA_FORMAT_R16G16_SFLOAT; r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; r_format.swizzle_b = RD::TEXTURE_SWIZZLE_ZERO; r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; } break; case Image::FORMAT_RGBH: { //this format is not mandatory for specification, check if supported first if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_R16G16B16_SFLOAT, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) { r_format.format = RD::DATA_FORMAT_R16G16B16_SFLOAT; } else { //not supported, reconvert r_format.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; image->convert(Image::FORMAT_RGBAH); } r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B; r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; } break; case Image::FORMAT_RGBAH: { r_format.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B; r_format.swizzle_a = RD::TEXTURE_SWIZZLE_A; } break; case Image::FORMAT_RGBE9995: { r_format.format = RD::DATA_FORMAT_E5B9G9R9_UFLOAT_PACK32; #ifndef _MSC_VER #warning TODO need to make a function in Image to swap bits for this #endif r_format.swizzle_r = RD::TEXTURE_SWIZZLE_IDENTITY; r_format.swizzle_g = RD::TEXTURE_SWIZZLE_IDENTITY; r_format.swizzle_b = RD::TEXTURE_SWIZZLE_IDENTITY; r_format.swizzle_a = RD::TEXTURE_SWIZZLE_IDENTITY; } break; case Image::FORMAT_DXT1: { if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_BC1_RGB_UNORM_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) { r_format.format = RD::DATA_FORMAT_BC1_RGB_UNORM_BLOCK; r_format.format_srgb = RD::DATA_FORMAT_BC1_RGB_SRGB_BLOCK; } else { //not supported, reconvert r_format.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; r_format.format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB; image->decompress(); image->convert(Image::FORMAT_RGBA8); } r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B; r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; } break; //s3tc bc1 case Image::FORMAT_DXT3: { if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_BC2_UNORM_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) { r_format.format = RD::DATA_FORMAT_BC2_UNORM_BLOCK; r_format.format_srgb = RD::DATA_FORMAT_BC2_SRGB_BLOCK; } else { //not supported, reconvert r_format.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; r_format.format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB; image->decompress(); image->convert(Image::FORMAT_RGBA8); } r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B; r_format.swizzle_a = RD::TEXTURE_SWIZZLE_A; } break; //bc2 case Image::FORMAT_DXT5: { if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_BC3_UNORM_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) { r_format.format = RD::DATA_FORMAT_BC3_UNORM_BLOCK; r_format.format_srgb = RD::DATA_FORMAT_BC3_SRGB_BLOCK; } else { //not supported, reconvert r_format.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; r_format.format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB; image->decompress(); image->convert(Image::FORMAT_RGBA8); } r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B; r_format.swizzle_a = RD::TEXTURE_SWIZZLE_A; } break; //bc3 case Image::FORMAT_RGTC_R: { if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_BC4_UNORM_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) { r_format.format = RD::DATA_FORMAT_BC4_UNORM_BLOCK; } else { //not supported, reconvert r_format.format = RD::DATA_FORMAT_R8_UNORM; image->decompress(); image->convert(Image::FORMAT_R8); } r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; r_format.swizzle_g = RD::TEXTURE_SWIZZLE_ZERO; r_format.swizzle_b = RD::TEXTURE_SWIZZLE_ZERO; r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; } break; case Image::FORMAT_RGTC_RG: { if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_BC5_UNORM_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) { r_format.format = RD::DATA_FORMAT_BC5_UNORM_BLOCK; } else { //not supported, reconvert r_format.format = RD::DATA_FORMAT_R8G8_UNORM; image->decompress(); image->convert(Image::FORMAT_RG8); } r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; r_format.swizzle_b = RD::TEXTURE_SWIZZLE_ZERO; r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; } break; case Image::FORMAT_BPTC_RGBA: { if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_BC7_UNORM_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) { r_format.format = RD::DATA_FORMAT_BC7_UNORM_BLOCK; r_format.format_srgb = RD::DATA_FORMAT_BC7_SRGB_BLOCK; } else { //not supported, reconvert r_format.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; r_format.format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB; image->decompress(); image->convert(Image::FORMAT_RGBA8); } r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B; r_format.swizzle_a = RD::TEXTURE_SWIZZLE_A; } break; //btpc bc7 case Image::FORMAT_BPTC_RGBF: { if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_BC6H_SFLOAT_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) { r_format.format = RD::DATA_FORMAT_BC6H_SFLOAT_BLOCK; } else { //not supported, reconvert r_format.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; image->decompress(); image->convert(Image::FORMAT_RGBAH); } r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B; r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; } break; //float bc6h case Image::FORMAT_BPTC_RGBFU: { if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_BC6H_UFLOAT_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) { r_format.format = RD::DATA_FORMAT_BC6H_UFLOAT_BLOCK; } else { //not supported, reconvert r_format.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; image->decompress(); image->convert(Image::FORMAT_RGBAH); } r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B; r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; } break; //unsigned float bc6hu case Image::FORMAT_ETC2_R11: { if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_EAC_R11_UNORM_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) { r_format.format = RD::DATA_FORMAT_EAC_R11_UNORM_BLOCK; } else { //not supported, reconvert r_format.format = RD::DATA_FORMAT_R8_UNORM; image->decompress(); image->convert(Image::FORMAT_R8); } r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; r_format.swizzle_g = RD::TEXTURE_SWIZZLE_ZERO; r_format.swizzle_b = RD::TEXTURE_SWIZZLE_ZERO; r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; } break; //etc2 case Image::FORMAT_ETC2_R11S: { if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_EAC_R11_SNORM_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) { r_format.format = RD::DATA_FORMAT_EAC_R11_SNORM_BLOCK; } else { //not supported, reconvert r_format.format = RD::DATA_FORMAT_R8_SNORM; image->decompress(); image->convert(Image::FORMAT_R8); } r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; r_format.swizzle_g = RD::TEXTURE_SWIZZLE_ZERO; r_format.swizzle_b = RD::TEXTURE_SWIZZLE_ZERO; r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; } break; //signed: {} break; NOT srgb. case Image::FORMAT_ETC2_RG11: { if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_EAC_R11G11_UNORM_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) { r_format.format = RD::DATA_FORMAT_EAC_R11G11_UNORM_BLOCK; } else { //not supported, reconvert r_format.format = RD::DATA_FORMAT_R8G8_UNORM; image->decompress(); image->convert(Image::FORMAT_RG8); } r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; r_format.swizzle_b = RD::TEXTURE_SWIZZLE_ZERO; r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; } break; case Image::FORMAT_ETC2_RG11S: { if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_EAC_R11G11_SNORM_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) { r_format.format = RD::DATA_FORMAT_EAC_R11G11_SNORM_BLOCK; } else { //not supported, reconvert r_format.format = RD::DATA_FORMAT_R8G8_SNORM; image->decompress(); image->convert(Image::FORMAT_RG8); } r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; r_format.swizzle_b = RD::TEXTURE_SWIZZLE_ZERO; r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; } break; case Image::FORMAT_ETC: case Image::FORMAT_ETC2_RGB8: { //ETC2 is backwards compatible with ETC1, and all modern platforms support it if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_ETC2_R8G8B8_UNORM_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) { r_format.format = RD::DATA_FORMAT_ETC2_R8G8B8_UNORM_BLOCK; r_format.format_srgb = RD::DATA_FORMAT_ETC2_R8G8B8_SRGB_BLOCK; } else { //not supported, reconvert r_format.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; r_format.format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB; image->decompress(); image->convert(Image::FORMAT_RGBA8); } r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B; r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; } break; case Image::FORMAT_ETC2_RGBA8: { if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) { r_format.format = RD::DATA_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK; r_format.format_srgb = RD::DATA_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK; } else { //not supported, reconvert r_format.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; r_format.format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB; image->decompress(); image->convert(Image::FORMAT_RGBA8); } r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B; r_format.swizzle_a = RD::TEXTURE_SWIZZLE_A; } break; case Image::FORMAT_ETC2_RGB8A1: { if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) { r_format.format = RD::DATA_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK; r_format.format_srgb = RD::DATA_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK; } else { //not supported, reconvert r_format.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; r_format.format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB; image->decompress(); image->convert(Image::FORMAT_RGBA8); } r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G; r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B; r_format.swizzle_a = RD::TEXTURE_SWIZZLE_A; } break; case Image::FORMAT_ETC2_RA_AS_RG: { if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) { r_format.format = RD::DATA_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK; r_format.format_srgb = RD::DATA_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK; } else { //not supported, reconvert r_format.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; r_format.format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB; image->decompress(); image->convert(Image::FORMAT_RGBA8); } r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; r_format.swizzle_g = RD::TEXTURE_SWIZZLE_A; r_format.swizzle_b = RD::TEXTURE_SWIZZLE_ZERO; r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; } break; case Image::FORMAT_DXT5_RA_AS_RG: { if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_BC3_UNORM_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) { r_format.format = RD::DATA_FORMAT_BC3_UNORM_BLOCK; r_format.format_srgb = RD::DATA_FORMAT_BC3_SRGB_BLOCK; } else { //not supported, reconvert r_format.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; r_format.format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB; image->decompress(); image->convert(Image::FORMAT_RGBA8); } r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R; r_format.swizzle_g = RD::TEXTURE_SWIZZLE_A; r_format.swizzle_b = RD::TEXTURE_SWIZZLE_ZERO; r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; } break; default: { } } return image; } RID RendererStorageRD::texture_allocate() { return texture_owner.allocate_rid(); } void RendererStorageRD::texture_2d_initialize(RID p_texture, const Ref &p_image) { ERR_FAIL_COND(p_image.is_null()); ERR_FAIL_COND(p_image->is_empty()); TextureToRDFormat ret_format; Ref image = _validate_texture_format(p_image, ret_format); Texture texture; texture.type = Texture::TYPE_2D; texture.width = p_image->get_width(); texture.height = p_image->get_height(); texture.layers = 1; texture.mipmaps = p_image->get_mipmap_count() + 1; texture.depth = 1; texture.format = p_image->get_format(); texture.validated_format = image->get_format(); texture.rd_type = RD::TEXTURE_TYPE_2D; texture.rd_format = ret_format.format; texture.rd_format_srgb = ret_format.format_srgb; RD::TextureFormat rd_format; RD::TextureView rd_view; { //attempt register rd_format.format = texture.rd_format; rd_format.width = texture.width; rd_format.height = texture.height; rd_format.depth = 1; rd_format.array_layers = 1; rd_format.mipmaps = texture.mipmaps; rd_format.texture_type = texture.rd_type; rd_format.samples = RD::TEXTURE_SAMPLES_1; rd_format.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT; if (texture.rd_format_srgb != RD::DATA_FORMAT_MAX) { rd_format.shareable_formats.push_back(texture.rd_format); rd_format.shareable_formats.push_back(texture.rd_format_srgb); } } { rd_view.swizzle_r = ret_format.swizzle_r; rd_view.swizzle_g = ret_format.swizzle_g; rd_view.swizzle_b = ret_format.swizzle_b; rd_view.swizzle_a = ret_format.swizzle_a; } Vector data = image->get_data(); //use image data Vector> data_slices; data_slices.push_back(data); texture.rd_texture = RD::get_singleton()->texture_create(rd_format, rd_view, data_slices); ERR_FAIL_COND(texture.rd_texture.is_null()); if (texture.rd_format_srgb != RD::DATA_FORMAT_MAX) { rd_view.format_override = texture.rd_format_srgb; texture.rd_texture_srgb = RD::get_singleton()->texture_create_shared(rd_view, texture.rd_texture); if (texture.rd_texture_srgb.is_null()) { RD::get_singleton()->free(texture.rd_texture); ERR_FAIL_COND(texture.rd_texture_srgb.is_null()); } } //used for 2D, overridable texture.width_2d = texture.width; texture.height_2d = texture.height; texture.is_render_target = false; texture.rd_view = rd_view; texture.is_proxy = false; texture_owner.initialize_rid(p_texture, texture); } void RendererStorageRD::texture_2d_layered_initialize(RID p_texture, const Vector> &p_layers, RS::TextureLayeredType p_layered_type) { ERR_FAIL_COND(p_layers.size() == 0); ERR_FAIL_COND(p_layered_type == RS::TEXTURE_LAYERED_CUBEMAP && p_layers.size() != 6); ERR_FAIL_COND(p_layered_type == RS::TEXTURE_LAYERED_CUBEMAP_ARRAY && (p_layers.size() < 6 || (p_layers.size() % 6) != 0)); TextureToRDFormat ret_format; Vector> images; { int valid_width = 0; int valid_height = 0; bool valid_mipmaps = false; Image::Format valid_format = Image::FORMAT_MAX; for (int i = 0; i < p_layers.size(); i++) { ERR_FAIL_COND(p_layers[i]->is_empty()); if (i == 0) { valid_width = p_layers[i]->get_width(); valid_height = p_layers[i]->get_height(); valid_format = p_layers[i]->get_format(); valid_mipmaps = p_layers[i]->has_mipmaps(); } else { ERR_FAIL_COND(p_layers[i]->get_width() != valid_width); ERR_FAIL_COND(p_layers[i]->get_height() != valid_height); ERR_FAIL_COND(p_layers[i]->get_format() != valid_format); ERR_FAIL_COND(p_layers[i]->has_mipmaps() != valid_mipmaps); } images.push_back(_validate_texture_format(p_layers[i], ret_format)); } } Texture texture; texture.type = Texture::TYPE_LAYERED; texture.layered_type = p_layered_type; texture.width = p_layers[0]->get_width(); texture.height = p_layers[0]->get_height(); texture.layers = p_layers.size(); texture.mipmaps = p_layers[0]->get_mipmap_count() + 1; texture.depth = 1; texture.format = p_layers[0]->get_format(); texture.validated_format = images[0]->get_format(); switch (p_layered_type) { case RS::TEXTURE_LAYERED_2D_ARRAY: { texture.rd_type = RD::TEXTURE_TYPE_2D_ARRAY; } break; case RS::TEXTURE_LAYERED_CUBEMAP: { texture.rd_type = RD::TEXTURE_TYPE_CUBE; } break; case RS::TEXTURE_LAYERED_CUBEMAP_ARRAY: { texture.rd_type = RD::TEXTURE_TYPE_CUBE_ARRAY; } break; } texture.rd_format = ret_format.format; texture.rd_format_srgb = ret_format.format_srgb; RD::TextureFormat rd_format; RD::TextureView rd_view; { //attempt register rd_format.format = texture.rd_format; rd_format.width = texture.width; rd_format.height = texture.height; rd_format.depth = 1; rd_format.array_layers = texture.layers; rd_format.mipmaps = texture.mipmaps; rd_format.texture_type = texture.rd_type; rd_format.samples = RD::TEXTURE_SAMPLES_1; rd_format.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT; if (texture.rd_format_srgb != RD::DATA_FORMAT_MAX) { rd_format.shareable_formats.push_back(texture.rd_format); rd_format.shareable_formats.push_back(texture.rd_format_srgb); } } { rd_view.swizzle_r = ret_format.swizzle_r; rd_view.swizzle_g = ret_format.swizzle_g; rd_view.swizzle_b = ret_format.swizzle_b; rd_view.swizzle_a = ret_format.swizzle_a; } Vector> data_slices; for (int i = 0; i < images.size(); i++) { Vector data = images[i]->get_data(); //use image data data_slices.push_back(data); } texture.rd_texture = RD::get_singleton()->texture_create(rd_format, rd_view, data_slices); ERR_FAIL_COND(texture.rd_texture.is_null()); if (texture.rd_format_srgb != RD::DATA_FORMAT_MAX) { rd_view.format_override = texture.rd_format_srgb; texture.rd_texture_srgb = RD::get_singleton()->texture_create_shared(rd_view, texture.rd_texture); if (texture.rd_texture_srgb.is_null()) { RD::get_singleton()->free(texture.rd_texture); ERR_FAIL_COND(texture.rd_texture_srgb.is_null()); } } //used for 2D, overridable texture.width_2d = texture.width; texture.height_2d = texture.height; texture.is_render_target = false; texture.rd_view = rd_view; texture.is_proxy = false; texture_owner.initialize_rid(p_texture, texture); } void RendererStorageRD::texture_3d_initialize(RID p_texture, Image::Format p_format, int p_width, int p_height, int p_depth, bool p_mipmaps, const Vector> &p_data) { ERR_FAIL_COND(p_data.size() == 0); Image::Image3DValidateError verr = Image::validate_3d_image(p_format, p_width, p_height, p_depth, p_mipmaps, p_data); if (verr != Image::VALIDATE_3D_OK) { ERR_FAIL_MSG(Image::get_3d_image_validation_error_text(verr)); } TextureToRDFormat ret_format; Image::Format validated_format = Image::FORMAT_MAX; Vector all_data; uint32_t mipmap_count = 0; Vector slices; { Vector> images; uint32_t all_data_size = 0; images.resize(p_data.size()); for (int i = 0; i < p_data.size(); i++) { TextureToRDFormat f; images.write[i] = _validate_texture_format(p_data[i], f); if (i == 0) { ret_format = f; validated_format = images[0]->get_format(); } all_data_size += images[i]->get_data().size(); } all_data.resize(all_data_size); //consolidate all data here uint32_t offset = 0; Size2i prev_size; for (int i = 0; i < p_data.size(); i++) { uint32_t s = images[i]->get_data().size(); memcpy(&all_data.write[offset], images[i]->get_data().ptr(), s); { Texture::BufferSlice3D slice; slice.size.width = images[i]->get_width(); slice.size.height = images[i]->get_height(); slice.offset = offset; slice.buffer_size = s; slices.push_back(slice); } offset += s; Size2i img_size(images[i]->get_width(), images[i]->get_height()); if (img_size != prev_size) { mipmap_count++; } prev_size = img_size; } } Texture texture; texture.type = Texture::TYPE_3D; texture.width = p_width; texture.height = p_height; texture.depth = p_depth; texture.mipmaps = mipmap_count; texture.format = p_data[0]->get_format(); texture.validated_format = validated_format; texture.buffer_size_3d = all_data.size(); texture.buffer_slices_3d = slices; texture.rd_type = RD::TEXTURE_TYPE_3D; texture.rd_format = ret_format.format; texture.rd_format_srgb = ret_format.format_srgb; RD::TextureFormat rd_format; RD::TextureView rd_view; { //attempt register rd_format.format = texture.rd_format; rd_format.width = texture.width; rd_format.height = texture.height; rd_format.depth = texture.depth; rd_format.array_layers = 1; rd_format.mipmaps = texture.mipmaps; rd_format.texture_type = texture.rd_type; rd_format.samples = RD::TEXTURE_SAMPLES_1; rd_format.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT; if (texture.rd_format_srgb != RD::DATA_FORMAT_MAX) { rd_format.shareable_formats.push_back(texture.rd_format); rd_format.shareable_formats.push_back(texture.rd_format_srgb); } } { rd_view.swizzle_r = ret_format.swizzle_r; rd_view.swizzle_g = ret_format.swizzle_g; rd_view.swizzle_b = ret_format.swizzle_b; rd_view.swizzle_a = ret_format.swizzle_a; } Vector> data_slices; data_slices.push_back(all_data); //one slice texture.rd_texture = RD::get_singleton()->texture_create(rd_format, rd_view, data_slices); ERR_FAIL_COND(texture.rd_texture.is_null()); if (texture.rd_format_srgb != RD::DATA_FORMAT_MAX) { rd_view.format_override = texture.rd_format_srgb; texture.rd_texture_srgb = RD::get_singleton()->texture_create_shared(rd_view, texture.rd_texture); if (texture.rd_texture_srgb.is_null()) { RD::get_singleton()->free(texture.rd_texture); ERR_FAIL_COND(texture.rd_texture_srgb.is_null()); } } //used for 2D, overridable texture.width_2d = texture.width; texture.height_2d = texture.height; texture.is_render_target = false; texture.rd_view = rd_view; texture.is_proxy = false; texture_owner.initialize_rid(p_texture, texture); } void RendererStorageRD::texture_proxy_initialize(RID p_texture, RID p_base) { Texture *tex = texture_owner.get_or_null(p_base); ERR_FAIL_COND(!tex); Texture proxy_tex = *tex; proxy_tex.rd_view.format_override = tex->rd_format; proxy_tex.rd_texture = RD::get_singleton()->texture_create_shared(proxy_tex.rd_view, tex->rd_texture); if (proxy_tex.rd_texture_srgb.is_valid()) { proxy_tex.rd_view.format_override = tex->rd_format_srgb; proxy_tex.rd_texture_srgb = RD::get_singleton()->texture_create_shared(proxy_tex.rd_view, tex->rd_texture); } proxy_tex.proxy_to = p_base; proxy_tex.is_render_target = false; proxy_tex.is_proxy = true; proxy_tex.proxies.clear(); texture_owner.initialize_rid(p_texture, proxy_tex); tex->proxies.push_back(p_texture); } void RendererStorageRD::_texture_2d_update(RID p_texture, const Ref &p_image, int p_layer, bool p_immediate) { ERR_FAIL_COND(p_image.is_null() || p_image->is_empty()); Texture *tex = texture_owner.get_or_null(p_texture); ERR_FAIL_COND(!tex); ERR_FAIL_COND(tex->is_render_target); ERR_FAIL_COND(p_image->get_width() != tex->width || p_image->get_height() != tex->height); ERR_FAIL_COND(p_image->get_format() != tex->format); if (tex->type == Texture::TYPE_LAYERED) { ERR_FAIL_INDEX(p_layer, tex->layers); } #ifdef TOOLS_ENABLED tex->image_cache_2d.unref(); #endif TextureToRDFormat f; Ref validated = _validate_texture_format(p_image, f); RD::get_singleton()->texture_update(tex->rd_texture, p_layer, validated->get_data()); } void RendererStorageRD::texture_2d_update(RID p_texture, const Ref &p_image, int p_layer) { _texture_2d_update(p_texture, p_image, p_layer, false); } void RendererStorageRD::texture_3d_update(RID p_texture, const Vector> &p_data) { Texture *tex = texture_owner.get_or_null(p_texture); ERR_FAIL_COND(!tex); ERR_FAIL_COND(tex->type != Texture::TYPE_3D); Image::Image3DValidateError verr = Image::validate_3d_image(tex->format, tex->width, tex->height, tex->depth, tex->mipmaps > 1, p_data); if (verr != Image::VALIDATE_3D_OK) { ERR_FAIL_MSG(Image::get_3d_image_validation_error_text(verr)); } Vector all_data; { Vector> images; uint32_t all_data_size = 0; images.resize(p_data.size()); for (int i = 0; i < p_data.size(); i++) { Ref image = p_data[i]; if (image->get_format() != tex->validated_format) { image = image->duplicate(); image->convert(tex->validated_format); } all_data_size += images[i]->get_data().size(); images.push_back(image); } all_data.resize(all_data_size); //consolidate all data here uint32_t offset = 0; for (int i = 0; i < p_data.size(); i++) { uint32_t s = images[i]->get_data().size(); memcpy(&all_data.write[offset], images[i]->get_data().ptr(), s); offset += s; } } RD::get_singleton()->texture_update(tex->rd_texture, 0, all_data); } void RendererStorageRD::texture_proxy_update(RID p_texture, RID p_proxy_to) { Texture *tex = texture_owner.get_or_null(p_texture); ERR_FAIL_COND(!tex); ERR_FAIL_COND(!tex->is_proxy); Texture *proxy_to = texture_owner.get_or_null(p_proxy_to); ERR_FAIL_COND(!proxy_to); ERR_FAIL_COND(proxy_to->is_proxy); if (tex->proxy_to.is_valid()) { //unlink proxy if (RD::get_singleton()->texture_is_valid(tex->rd_texture)) { RD::get_singleton()->free(tex->rd_texture); tex->rd_texture = RID(); } if (RD::get_singleton()->texture_is_valid(tex->rd_texture_srgb)) { RD::get_singleton()->free(tex->rd_texture_srgb); tex->rd_texture_srgb = RID(); } Texture *prev_tex = texture_owner.get_or_null(tex->proxy_to); ERR_FAIL_COND(!prev_tex); prev_tex->proxies.erase(p_texture); } *tex = *proxy_to; tex->proxy_to = p_proxy_to; tex->is_render_target = false; tex->is_proxy = true; tex->proxies.clear(); proxy_to->proxies.push_back(p_texture); tex->rd_view.format_override = tex->rd_format; tex->rd_texture = RD::get_singleton()->texture_create_shared(tex->rd_view, proxy_to->rd_texture); if (tex->rd_texture_srgb.is_valid()) { tex->rd_view.format_override = tex->rd_format_srgb; tex->rd_texture_srgb = RD::get_singleton()->texture_create_shared(tex->rd_view, proxy_to->rd_texture); } } //these two APIs can be used together or in combination with the others. void RendererStorageRD::texture_2d_placeholder_initialize(RID p_texture) { //this could be better optimized to reuse an existing image , done this way //for now to get it working Ref image; image.instantiate(); image->create(4, 4, false, Image::FORMAT_RGBA8); image->fill(Color(1, 0, 1, 1)); texture_2d_initialize(p_texture, image); } void RendererStorageRD::texture_2d_layered_placeholder_initialize(RID p_texture, RS::TextureLayeredType p_layered_type) { //this could be better optimized to reuse an existing image , done this way //for now to get it working Ref image; image.instantiate(); image->create(4, 4, false, Image::FORMAT_RGBA8); image->fill(Color(1, 0, 1, 1)); Vector> images; if (p_layered_type == RS::TEXTURE_LAYERED_2D_ARRAY) { images.push_back(image); } else { //cube for (int i = 0; i < 6; i++) { images.push_back(image); } } texture_2d_layered_initialize(p_texture, images, p_layered_type); } void RendererStorageRD::texture_3d_placeholder_initialize(RID p_texture) { //this could be better optimized to reuse an existing image , done this way //for now to get it working Ref image; image.instantiate(); image->create(4, 4, false, Image::FORMAT_RGBA8); image->fill(Color(1, 0, 1, 1)); Vector> images; //cube for (int i = 0; i < 4; i++) { images.push_back(image); } texture_3d_initialize(p_texture, Image::FORMAT_RGBA8, 4, 4, 4, false, images); } Ref RendererStorageRD::texture_2d_get(RID p_texture) const { Texture *tex = texture_owner.get_or_null(p_texture); ERR_FAIL_COND_V(!tex, Ref()); #ifdef TOOLS_ENABLED if (tex->image_cache_2d.is_valid() && !tex->is_render_target) { return tex->image_cache_2d; } #endif Vector data = RD::get_singleton()->texture_get_data(tex->rd_texture, 0); ERR_FAIL_COND_V(data.size() == 0, Ref()); Ref image; image.instantiate(); image->create(tex->width, tex->height, tex->mipmaps > 1, tex->validated_format, data); ERR_FAIL_COND_V(image->is_empty(), Ref()); if (tex->format != tex->validated_format) { image->convert(tex->format); } #ifdef TOOLS_ENABLED if (Engine::get_singleton()->is_editor_hint() && !tex->is_render_target) { tex->image_cache_2d = image; } #endif return image; } Ref RendererStorageRD::texture_2d_layer_get(RID p_texture, int p_layer) const { Texture *tex = texture_owner.get_or_null(p_texture); ERR_FAIL_COND_V(!tex, Ref()); Vector data = RD::get_singleton()->texture_get_data(tex->rd_texture, p_layer); ERR_FAIL_COND_V(data.size() == 0, Ref()); Ref image; image.instantiate(); image->create(tex->width, tex->height, tex->mipmaps > 1, tex->validated_format, data); ERR_FAIL_COND_V(image->is_empty(), Ref()); if (tex->format != tex->validated_format) { image->convert(tex->format); } return image; } Vector> RendererStorageRD::texture_3d_get(RID p_texture) const { Texture *tex = texture_owner.get_or_null(p_texture); ERR_FAIL_COND_V(!tex, Vector>()); ERR_FAIL_COND_V(tex->type != Texture::TYPE_3D, Vector>()); Vector all_data = RD::get_singleton()->texture_get_data(tex->rd_texture, 0); ERR_FAIL_COND_V(all_data.size() != (int)tex->buffer_size_3d, Vector>()); Vector> ret; for (int i = 0; i < tex->buffer_slices_3d.size(); i++) { const Texture::BufferSlice3D &bs = tex->buffer_slices_3d[i]; ERR_FAIL_COND_V(bs.offset >= (uint32_t)all_data.size(), Vector>()); ERR_FAIL_COND_V(bs.offset + bs.buffer_size > (uint32_t)all_data.size(), Vector>()); Vector sub_region = all_data.slice(bs.offset, bs.offset + bs.buffer_size); Ref img; img.instantiate(); img->create(bs.size.width, bs.size.height, false, tex->validated_format, sub_region); ERR_FAIL_COND_V(img->is_empty(), Vector>()); if (tex->format != tex->validated_format) { img->convert(tex->format); } ret.push_back(img); } return ret; } void RendererStorageRD::texture_replace(RID p_texture, RID p_by_texture) { Texture *tex = texture_owner.get_or_null(p_texture); ERR_FAIL_COND(!tex); ERR_FAIL_COND(tex->proxy_to.is_valid()); //can't replace proxy Texture *by_tex = texture_owner.get_or_null(p_by_texture); ERR_FAIL_COND(!by_tex); ERR_FAIL_COND(by_tex->proxy_to.is_valid()); //can't replace proxy if (tex == by_tex) { return; } if (tex->rd_texture_srgb.is_valid()) { RD::get_singleton()->free(tex->rd_texture_srgb); } RD::get_singleton()->free(tex->rd_texture); if (tex->canvas_texture) { memdelete(tex->canvas_texture); tex->canvas_texture = nullptr; } Vector proxies_to_update = tex->proxies; Vector proxies_to_redirect = by_tex->proxies; *tex = *by_tex; tex->proxies = proxies_to_update; //restore proxies, so they can be updated if (tex->canvas_texture) { tex->canvas_texture->diffuse = p_texture; //update } for (int i = 0; i < proxies_to_update.size(); i++) { texture_proxy_update(proxies_to_update[i], p_texture); } for (int i = 0; i < proxies_to_redirect.size(); i++) { texture_proxy_update(proxies_to_redirect[i], p_texture); } //delete last, so proxies can be updated texture_owner.free(p_by_texture); if (decal_atlas.textures.has(p_texture)) { //belongs to decal atlas.. decal_atlas.dirty = true; //mark it dirty since it was most likely modified } } void RendererStorageRD::texture_set_size_override(RID p_texture, int p_width, int p_height) { Texture *tex = texture_owner.get_or_null(p_texture); ERR_FAIL_COND(!tex); ERR_FAIL_COND(tex->type != Texture::TYPE_2D); tex->width_2d = p_width; tex->height_2d = p_height; } void RendererStorageRD::texture_set_path(RID p_texture, const String &p_path) { Texture *tex = texture_owner.get_or_null(p_texture); ERR_FAIL_COND(!tex); tex->path = p_path; } String RendererStorageRD::texture_get_path(RID p_texture) const { return String(); } void RendererStorageRD::texture_set_detect_3d_callback(RID p_texture, RS::TextureDetectCallback p_callback, void *p_userdata) { Texture *tex = texture_owner.get_or_null(p_texture); ERR_FAIL_COND(!tex); tex->detect_3d_callback_ud = p_userdata; tex->detect_3d_callback = p_callback; } void RendererStorageRD::texture_set_detect_normal_callback(RID p_texture, RS::TextureDetectCallback p_callback, void *p_userdata) { Texture *tex = texture_owner.get_or_null(p_texture); ERR_FAIL_COND(!tex); tex->detect_normal_callback_ud = p_userdata; tex->detect_normal_callback = p_callback; } void RendererStorageRD::texture_set_detect_roughness_callback(RID p_texture, RS::TextureDetectRoughnessCallback p_callback, void *p_userdata) { Texture *tex = texture_owner.get_or_null(p_texture); ERR_FAIL_COND(!tex); tex->detect_roughness_callback_ud = p_userdata; tex->detect_roughness_callback = p_callback; } void RendererStorageRD::texture_debug_usage(List *r_info) { } void RendererStorageRD::texture_set_proxy(RID p_proxy, RID p_base) { } void RendererStorageRD::texture_set_force_redraw_if_visible(RID p_texture, bool p_enable) { } Size2 RendererStorageRD::texture_size_with_proxy(RID p_proxy) { return texture_2d_get_size(p_proxy); } /* CANVAS TEXTURE */ void RendererStorageRD::CanvasTexture::clear_sets() { if (cleared_cache) { return; } for (int i = 1; i < RS::CANVAS_ITEM_TEXTURE_FILTER_MAX; i++) { for (int j = 1; j < RS::CANVAS_ITEM_TEXTURE_REPEAT_MAX; j++) { if (RD::get_singleton()->uniform_set_is_valid(uniform_sets[i][j])) { RD::get_singleton()->free(uniform_sets[i][j]); uniform_sets[i][j] = RID(); } } } cleared_cache = true; } RendererStorageRD::CanvasTexture::~CanvasTexture() { clear_sets(); } void RendererStorageRD::sampler_rd_configure_custom(float p_mipmap_bias) { for (int i = 1; i < RS::CANVAS_ITEM_TEXTURE_FILTER_MAX; i++) { for (int j = 1; j < RS::CANVAS_ITEM_TEXTURE_REPEAT_MAX; j++) { RD::SamplerState sampler_state; switch (i) { case RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST: { sampler_state.mag_filter = RD::SAMPLER_FILTER_NEAREST; sampler_state.min_filter = RD::SAMPLER_FILTER_NEAREST; sampler_state.max_lod = 0; } break; case RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR: { sampler_state.mag_filter = RD::SAMPLER_FILTER_LINEAR; sampler_state.min_filter = RD::SAMPLER_FILTER_LINEAR; sampler_state.max_lod = 0; } break; case RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS: { sampler_state.mag_filter = RD::SAMPLER_FILTER_NEAREST; sampler_state.min_filter = RD::SAMPLER_FILTER_NEAREST; if (GLOBAL_GET("rendering/textures/default_filters/use_nearest_mipmap_filter")) { sampler_state.mip_filter = RD::SAMPLER_FILTER_NEAREST; } else { sampler_state.mip_filter = RD::SAMPLER_FILTER_LINEAR; } sampler_state.lod_bias = p_mipmap_bias; } break; case RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS: { sampler_state.mag_filter = RD::SAMPLER_FILTER_LINEAR; sampler_state.min_filter = RD::SAMPLER_FILTER_LINEAR; if (GLOBAL_GET("rendering/textures/default_filters/use_nearest_mipmap_filter")) { sampler_state.mip_filter = RD::SAMPLER_FILTER_NEAREST; } else { sampler_state.mip_filter = RD::SAMPLER_FILTER_LINEAR; } sampler_state.lod_bias = p_mipmap_bias; } break; case RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC: { sampler_state.mag_filter = RD::SAMPLER_FILTER_NEAREST; sampler_state.min_filter = RD::SAMPLER_FILTER_NEAREST; if (GLOBAL_GET("rendering/textures/default_filters/use_nearest_mipmap_filter")) { sampler_state.mip_filter = RD::SAMPLER_FILTER_NEAREST; } else { sampler_state.mip_filter = RD::SAMPLER_FILTER_LINEAR; } sampler_state.lod_bias = p_mipmap_bias; sampler_state.use_anisotropy = true; sampler_state.anisotropy_max = 1 << int(GLOBAL_GET("rendering/textures/default_filters/anisotropic_filtering_level")); } break; case RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC: { sampler_state.mag_filter = RD::SAMPLER_FILTER_LINEAR; sampler_state.min_filter = RD::SAMPLER_FILTER_LINEAR; if (GLOBAL_GET("rendering/textures/default_filters/use_nearest_mipmap_filter")) { sampler_state.mip_filter = RD::SAMPLER_FILTER_NEAREST; } else { sampler_state.mip_filter = RD::SAMPLER_FILTER_LINEAR; } sampler_state.lod_bias = p_mipmap_bias; sampler_state.use_anisotropy = true; sampler_state.anisotropy_max = 1 << int(GLOBAL_GET("rendering/textures/default_filters/anisotropic_filtering_level")); } break; default: { } } switch (j) { case RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED: { sampler_state.repeat_u = RD::SAMPLER_REPEAT_MODE_CLAMP_TO_EDGE; sampler_state.repeat_v = RD::SAMPLER_REPEAT_MODE_CLAMP_TO_EDGE; sampler_state.repeat_w = RD::SAMPLER_REPEAT_MODE_CLAMP_TO_EDGE; } break; case RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED: { sampler_state.repeat_u = RD::SAMPLER_REPEAT_MODE_REPEAT; sampler_state.repeat_v = RD::SAMPLER_REPEAT_MODE_REPEAT; sampler_state.repeat_w = RD::SAMPLER_REPEAT_MODE_REPEAT; } break; case RS::CANVAS_ITEM_TEXTURE_REPEAT_MIRROR: { sampler_state.repeat_u = RD::SAMPLER_REPEAT_MODE_MIRRORED_REPEAT; sampler_state.repeat_v = RD::SAMPLER_REPEAT_MODE_MIRRORED_REPEAT; sampler_state.repeat_w = RD::SAMPLER_REPEAT_MODE_MIRRORED_REPEAT; } break; default: { } } if (custom_rd_samplers[i][j].is_valid()) { RD::get_singleton()->free(custom_rd_samplers[i][j]); } custom_rd_samplers[i][j] = RD::get_singleton()->sampler_create(sampler_state); } } } RID RendererStorageRD::canvas_texture_allocate() { return canvas_texture_owner.allocate_rid(); } void RendererStorageRD::canvas_texture_initialize(RID p_rid) { canvas_texture_owner.initialize_rid(p_rid); } void RendererStorageRD::canvas_texture_set_channel(RID p_canvas_texture, RS::CanvasTextureChannel p_channel, RID p_texture) { CanvasTexture *ct = canvas_texture_owner.get_or_null(p_canvas_texture); switch (p_channel) { case RS::CANVAS_TEXTURE_CHANNEL_DIFFUSE: { ct->diffuse = p_texture; } break; case RS::CANVAS_TEXTURE_CHANNEL_NORMAL: { ct->normal_map = p_texture; } break; case RS::CANVAS_TEXTURE_CHANNEL_SPECULAR: { ct->specular = p_texture; } break; } ct->clear_sets(); } void RendererStorageRD::canvas_texture_set_shading_parameters(RID p_canvas_texture, const Color &p_specular_color, float p_shininess) { CanvasTexture *ct = canvas_texture_owner.get_or_null(p_canvas_texture); ct->specular_color.r = p_specular_color.r; ct->specular_color.g = p_specular_color.g; ct->specular_color.b = p_specular_color.b; ct->specular_color.a = p_shininess; ct->clear_sets(); } void RendererStorageRD::canvas_texture_set_texture_filter(RID p_canvas_texture, RS::CanvasItemTextureFilter p_filter) { CanvasTexture *ct = canvas_texture_owner.get_or_null(p_canvas_texture); ct->texture_filter = p_filter; ct->clear_sets(); } void RendererStorageRD::canvas_texture_set_texture_repeat(RID p_canvas_texture, RS::CanvasItemTextureRepeat p_repeat) { CanvasTexture *ct = canvas_texture_owner.get_or_null(p_canvas_texture); ct->texture_repeat = p_repeat; ct->clear_sets(); } bool RendererStorageRD::canvas_texture_get_uniform_set(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID p_base_shader, int p_base_set, RID &r_uniform_set, Size2i &r_size, Color &r_specular_shininess, bool &r_use_normal, bool &r_use_specular) { CanvasTexture *ct = nullptr; Texture *t = texture_owner.get_or_null(p_texture); if (t) { //regular texture if (!t->canvas_texture) { t->canvas_texture = memnew(CanvasTexture); t->canvas_texture->diffuse = p_texture; } ct = t->canvas_texture; } else { ct = canvas_texture_owner.get_or_null(p_texture); } if (!ct) { return false; //invalid texture RID } RS::CanvasItemTextureFilter filter = ct->texture_filter != RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT ? ct->texture_filter : p_base_filter; ERR_FAIL_COND_V(filter == RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, false); RS::CanvasItemTextureRepeat repeat = ct->texture_repeat != RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT ? ct->texture_repeat : p_base_repeat; ERR_FAIL_COND_V(repeat == RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT, false); RID uniform_set = ct->uniform_sets[filter][repeat]; if (!RD::get_singleton()->uniform_set_is_valid(uniform_set)) { //create and update Vector uniforms; { //diffuse RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; u.binding = 0; t = texture_owner.get_or_null(ct->diffuse); if (!t) { u.append_id(texture_rd_get_default(DEFAULT_RD_TEXTURE_WHITE)); ct->size_cache = Size2i(1, 1); } else { u.append_id(t->rd_texture); ct->size_cache = Size2i(t->width_2d, t->height_2d); } uniforms.push_back(u); } { //normal RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; u.binding = 1; t = texture_owner.get_or_null(ct->normal_map); if (!t) { u.append_id(texture_rd_get_default(DEFAULT_RD_TEXTURE_NORMAL)); ct->use_normal_cache = false; } else { u.append_id(t->rd_texture); ct->use_normal_cache = true; } uniforms.push_back(u); } { //specular RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; u.binding = 2; t = texture_owner.get_or_null(ct->specular); if (!t) { u.append_id(texture_rd_get_default(DEFAULT_RD_TEXTURE_WHITE)); ct->use_specular_cache = false; } else { u.append_id(t->rd_texture); ct->use_specular_cache = true; } uniforms.push_back(u); } { //sampler RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_SAMPLER; u.binding = 3; u.append_id(sampler_rd_get_default(filter, repeat)); uniforms.push_back(u); } uniform_set = RD::get_singleton()->uniform_set_create(uniforms, p_base_shader, p_base_set); ct->uniform_sets[filter][repeat] = uniform_set; ct->cleared_cache = false; } r_uniform_set = uniform_set; r_size = ct->size_cache; r_specular_shininess = ct->specular_color; r_use_normal = ct->use_normal_cache; r_use_specular = ct->use_specular_cache; return true; } /* SHADER API */ RID RendererStorageRD::shader_allocate() { return shader_owner.allocate_rid(); } void RendererStorageRD::shader_initialize(RID p_rid) { Shader shader; shader.data = nullptr; shader.type = SHADER_TYPE_MAX; shader_owner.initialize_rid(p_rid, shader); } void RendererStorageRD::shader_set_code(RID p_shader, const String &p_code) { Shader *shader = shader_owner.get_or_null(p_shader); ERR_FAIL_COND(!shader); shader->code = p_code; String mode_string = ShaderLanguage::get_shader_type(p_code); ShaderType new_type; if (mode_string == "canvas_item") { new_type = SHADER_TYPE_2D; } else if (mode_string == "particles") { new_type = SHADER_TYPE_PARTICLES; } else if (mode_string == "spatial") { new_type = SHADER_TYPE_3D; } else if (mode_string == "sky") { new_type = SHADER_TYPE_SKY; } else if (mode_string == "fog") { new_type = SHADER_TYPE_FOG; } else { new_type = SHADER_TYPE_MAX; } if (new_type != shader->type) { if (shader->data) { memdelete(shader->data); shader->data = nullptr; } for (Set::Element *E = shader->owners.front(); E; E = E->next()) { Material *material = E->get(); material->shader_type = new_type; if (material->data) { memdelete(material->data); material->data = nullptr; } } shader->type = new_type; if (new_type < SHADER_TYPE_MAX && shader_data_request_func[new_type]) { shader->data = shader_data_request_func[new_type](); } else { shader->type = SHADER_TYPE_MAX; //invalid } for (Set::Element *E = shader->owners.front(); E; E = E->next()) { Material *material = E->get(); if (shader->data) { material->data = material_data_request_func[new_type](shader->data); material->data->self = material->self; material->data->set_next_pass(material->next_pass); material->data->set_render_priority(material->priority); } material->shader_type = new_type; } if (shader->data) { for (const KeyValue> &E : shader->default_texture_parameter) { for (const KeyValue &E2 : E.value) { shader->data->set_default_texture_param(E.key, E2.value, E2.key); } } } } if (shader->data) { shader->data->set_code(p_code); } for (Set::Element *E = shader->owners.front(); E; E = E->next()) { Material *material = E->get(); material->dependency.changed_notify(DEPENDENCY_CHANGED_MATERIAL); _material_queue_update(material, true, true); } } String RendererStorageRD::shader_get_code(RID p_shader) const { Shader *shader = shader_owner.get_or_null(p_shader); ERR_FAIL_COND_V(!shader, String()); return shader->code; } void RendererStorageRD::shader_get_param_list(RID p_shader, List *p_param_list) const { Shader *shader = shader_owner.get_or_null(p_shader); ERR_FAIL_COND(!shader); if (shader->data) { return shader->data->get_param_list(p_param_list); } } void RendererStorageRD::shader_set_default_texture_param(RID p_shader, const StringName &p_name, RID p_texture, int p_index) { Shader *shader = shader_owner.get_or_null(p_shader); ERR_FAIL_COND(!shader); if (p_texture.is_valid() && texture_owner.owns(p_texture)) { if (!shader->default_texture_parameter.has(p_name)) { shader->default_texture_parameter[p_name] = Map(); } shader->default_texture_parameter[p_name][p_index] = p_texture; } else { if (shader->default_texture_parameter.has(p_name) && shader->default_texture_parameter[p_name].has(p_index)) { shader->default_texture_parameter[p_name].erase(p_index); if (shader->default_texture_parameter[p_name].is_empty()) { shader->default_texture_parameter.erase(p_name); } } } if (shader->data) { shader->data->set_default_texture_param(p_name, p_texture, p_index); } for (Set::Element *E = shader->owners.front(); E; E = E->next()) { Material *material = E->get(); _material_queue_update(material, false, true); } } RID RendererStorageRD::shader_get_default_texture_param(RID p_shader, const StringName &p_name, int p_index) const { Shader *shader = shader_owner.get_or_null(p_shader); ERR_FAIL_COND_V(!shader, RID()); if (shader->default_texture_parameter.has(p_name) && shader->default_texture_parameter[p_name].has(p_index)) { return shader->default_texture_parameter[p_name][p_index]; } return RID(); } Variant RendererStorageRD::shader_get_param_default(RID p_shader, const StringName &p_param) const { Shader *shader = shader_owner.get_or_null(p_shader); ERR_FAIL_COND_V(!shader, Variant()); if (shader->data) { return shader->data->get_default_parameter(p_param); } return Variant(); } void RendererStorageRD::shader_set_data_request_function(ShaderType p_shader_type, ShaderDataRequestFunction p_function) { ERR_FAIL_INDEX(p_shader_type, SHADER_TYPE_MAX); shader_data_request_func[p_shader_type] = p_function; } RS::ShaderNativeSourceCode RendererStorageRD::shader_get_native_source_code(RID p_shader) const { Shader *shader = shader_owner.get_or_null(p_shader); ERR_FAIL_COND_V(!shader, RS::ShaderNativeSourceCode()); if (shader->data) { return shader->data->get_native_source_code(); } return RS::ShaderNativeSourceCode(); } /* COMMON MATERIAL API */ RID RendererStorageRD::material_allocate() { return material_owner.allocate_rid(); } void RendererStorageRD::material_initialize(RID p_rid) { material_owner.initialize_rid(p_rid); Material *material = material_owner.get_or_null(p_rid); material->self = p_rid; } void RendererStorageRD::_material_queue_update(Material *material, bool p_uniform, bool p_texture) { material->uniform_dirty = material->uniform_dirty || p_uniform; material->texture_dirty = material->texture_dirty || p_texture; if (material->update_element.in_list()) { return; } material_update_list.add(&material->update_element); } void RendererStorageRD::material_set_shader(RID p_material, RID p_shader) { Material *material = material_owner.get_or_null(p_material); ERR_FAIL_COND(!material); if (material->data) { memdelete(material->data); material->data = nullptr; } if (material->shader) { material->shader->owners.erase(material); material->shader = nullptr; material->shader_type = SHADER_TYPE_MAX; } if (p_shader.is_null()) { material->dependency.changed_notify(DEPENDENCY_CHANGED_MATERIAL); material->shader_id = 0; return; } Shader *shader = shader_owner.get_or_null(p_shader); ERR_FAIL_COND(!shader); material->shader = shader; material->shader_type = shader->type; material->shader_id = p_shader.get_local_index(); shader->owners.insert(material); if (shader->type == SHADER_TYPE_MAX) { return; } ERR_FAIL_COND(shader->data == nullptr); material->data = material_data_request_func[shader->type](shader->data); material->data->self = p_material; material->data->set_next_pass(material->next_pass); material->data->set_render_priority(material->priority); //updating happens later material->dependency.changed_notify(DEPENDENCY_CHANGED_MATERIAL); _material_queue_update(material, true, true); } void RendererStorageRD::material_set_param(RID p_material, const StringName &p_param, const Variant &p_value) { Material *material = material_owner.get_or_null(p_material); ERR_FAIL_COND(!material); if (p_value.get_type() == Variant::NIL) { material->params.erase(p_param); } else { ERR_FAIL_COND(p_value.get_type() == Variant::OBJECT); //object not allowed material->params[p_param] = p_value; } if (material->shader && material->shader->data) { //shader is valid bool is_texture = material->shader->data->is_param_texture(p_param); _material_queue_update(material, !is_texture, is_texture); } else { _material_queue_update(material, true, true); } } Variant RendererStorageRD::material_get_param(RID p_material, const StringName &p_param) const { Material *material = material_owner.get_or_null(p_material); ERR_FAIL_COND_V(!material, Variant()); if (material->params.has(p_param)) { return material->params[p_param]; } else { return Variant(); } } void RendererStorageRD::material_set_next_pass(RID p_material, RID p_next_material) { Material *material = material_owner.get_or_null(p_material); ERR_FAIL_COND(!material); if (material->next_pass == p_next_material) { return; } material->next_pass = p_next_material; if (material->data) { material->data->set_next_pass(p_next_material); } material->dependency.changed_notify(DEPENDENCY_CHANGED_MATERIAL); } void RendererStorageRD::material_set_render_priority(RID p_material, int priority) { Material *material = material_owner.get_or_null(p_material); ERR_FAIL_COND(!material); material->priority = priority; if (material->data) { material->data->set_render_priority(priority); } } bool RendererStorageRD::material_is_animated(RID p_material) { Material *material = material_owner.get_or_null(p_material); ERR_FAIL_COND_V(!material, false); if (material->shader && material->shader->data) { if (material->shader->data->is_animated()) { return true; } else if (material->next_pass.is_valid()) { return material_is_animated(material->next_pass); } } return false; //by default nothing is animated } bool RendererStorageRD::material_casts_shadows(RID p_material) { Material *material = material_owner.get_or_null(p_material); ERR_FAIL_COND_V(!material, true); if (material->shader && material->shader->data) { if (material->shader->data->casts_shadows()) { return true; } else if (material->next_pass.is_valid()) { return material_casts_shadows(material->next_pass); } } return true; //by default everything casts shadows } void RendererStorageRD::material_get_instance_shader_parameters(RID p_material, List *r_parameters) { Material *material = material_owner.get_or_null(p_material); ERR_FAIL_COND(!material); if (material->shader && material->shader->data) { material->shader->data->get_instance_param_list(r_parameters); if (material->next_pass.is_valid()) { material_get_instance_shader_parameters(material->next_pass, r_parameters); } } } void RendererStorageRD::material_update_dependency(RID p_material, DependencyTracker *p_instance) { Material *material = material_owner.get_or_null(p_material); ERR_FAIL_COND(!material); p_instance->update_dependency(&material->dependency); if (material->next_pass.is_valid()) { material_update_dependency(material->next_pass, p_instance); } } void RendererStorageRD::material_set_data_request_function(ShaderType p_shader_type, MaterialDataRequestFunction p_function) { ERR_FAIL_INDEX(p_shader_type, SHADER_TYPE_MAX); material_data_request_func[p_shader_type] = p_function; } _FORCE_INLINE_ static void _fill_std140_variant_ubo_value(ShaderLanguage::DataType type, int p_array_size, const Variant &value, uint8_t *data, bool p_linear_color) { switch (type) { case ShaderLanguage::TYPE_BOOL: { uint32_t *gui = (uint32_t *)data; if (p_array_size > 0) { const PackedInt32Array &ba = value; int s = ba.size(); const int *r = ba.ptr(); for (int i = 0, j = 0; i < p_array_size; i++, j += 4) { if (i < s) { gui[j] = (r[i] != 0) ? 1 : 0; } else { gui[j] = 0; } gui[j + 1] = 0; // ignored gui[j + 2] = 0; // ignored gui[j + 3] = 0; // ignored } } else { bool v = value; gui[0] = v ? 1 : 0; } } break; case ShaderLanguage::TYPE_BVEC2: { uint32_t *gui = (uint32_t *)data; if (p_array_size > 0) { const PackedInt32Array &ba = value; int s = ba.size(); const int *r = ba.ptr(); int count = 2 * p_array_size; for (int i = 0, j = 0; i < count; i += 2, j += 4) { if (i < s) { gui[j] = r[i] ? 1 : 0; gui[j + 1] = r[i + 1] ? 1 : 0; } else { gui[j] = 0; gui[j + 1] = 0; } gui[j + 2] = 0; // ignored gui[j + 3] = 0; // ignored } } else { int v = value; gui[0] = v & 1 ? 1 : 0; gui[1] = v & 2 ? 1 : 0; } } break; case ShaderLanguage::TYPE_BVEC3: { uint32_t *gui = (uint32_t *)data; if (p_array_size > 0) { const PackedInt32Array &ba = value; int s = ba.size(); const int *r = ba.ptr(); int count = 3 * p_array_size; for (int i = 0, j = 0; i < count; i += 3, j += 4) { if (i < s) { gui[j] = r[i] ? 1 : 0; gui[j + 1] = r[i + 1] ? 1 : 0; gui[j + 2] = r[i + 2] ? 1 : 0; } else { gui[j] = 0; gui[j + 1] = 0; gui[j + 2] = 0; } gui[j + 3] = 0; // ignored } } else { int v = value; gui[0] = (v & 1) ? 1 : 0; gui[1] = (v & 2) ? 1 : 0; gui[2] = (v & 4) ? 1 : 0; } } break; case ShaderLanguage::TYPE_BVEC4: { uint32_t *gui = (uint32_t *)data; if (p_array_size > 0) { const PackedInt32Array &ba = value; int s = ba.size(); const int *r = ba.ptr(); int count = 4 * p_array_size; for (int i = 0; i < count; i += 4) { if (i < s) { gui[i] = r[i] ? 1 : 0; gui[i + 1] = r[i + 1] ? 1 : 0; gui[i + 2] = r[i + 2] ? 1 : 0; gui[i + 3] = r[i + 3] ? 1 : 0; } else { gui[i] = 0; gui[i + 1] = 0; gui[i + 2] = 0; gui[i + 3] = 0; } } } else { int v = value; gui[0] = (v & 1) ? 1 : 0; gui[1] = (v & 2) ? 1 : 0; gui[2] = (v & 4) ? 1 : 0; gui[3] = (v & 8) ? 1 : 0; } } break; case ShaderLanguage::TYPE_INT: { int32_t *gui = (int32_t *)data; if (p_array_size > 0) { Vector iv = value; int s = iv.size(); const int *r = iv.ptr(); for (int i = 0, j = 0; i < p_array_size; i++, j += 4) { if (i < s) { gui[j] = r[i]; } else { gui[j] = 0; } gui[j + 1] = 0; // ignored gui[j + 2] = 0; // ignored gui[j + 3] = 0; // ignored } } else { int v = value; gui[0] = v; } } break; case ShaderLanguage::TYPE_IVEC2: { Vector iv = value; int s = iv.size(); int32_t *gui = (int32_t *)data; if (p_array_size <= 0) { p_array_size = 1; } int count = 2 * p_array_size; const int *r = iv.ptr(); for (int i = 0, j = 0; i < count; i += 2, j += 4) { if (i < s) { gui[j] = r[i]; gui[j + 1] = r[i + 1]; } else { gui[j] = 0; gui[j + 1] = 0; } gui[j + 2] = 0; // ignored gui[j + 3] = 0; // ignored } } break; case ShaderLanguage::TYPE_IVEC3: { Vector iv = value; int s = iv.size(); int32_t *gui = (int32_t *)data; if (p_array_size <= 0) { p_array_size = 1; } int count = 3 * p_array_size; const int *r = iv.ptr(); for (int i = 0, j = 0; i < count; i += 3, j += 4) { if (i < s) { gui[j] = r[i]; gui[j + 1] = r[i + 1]; gui[j + 2] = r[i + 2]; } else { gui[j] = 0; gui[j + 1] = 0; gui[j + 2] = 0; } gui[j + 3] = 0; // ignored } } break; case ShaderLanguage::TYPE_IVEC4: { Vector iv = value; int s = iv.size(); int32_t *gui = (int32_t *)data; if (p_array_size <= 0) { p_array_size = 1; } int count = 4 * p_array_size; const int *r = iv.ptr(); for (int i = 0; i < count; i += 4) { if (i < s) { gui[i] = r[i]; gui[i + 1] = r[i + 1]; gui[i + 2] = r[i + 2]; gui[i + 3] = r[i + 3]; } else { gui[i] = 0; gui[i + 1] = 0; gui[i + 2] = 0; gui[i + 3] = 0; } } } break; case ShaderLanguage::TYPE_UINT: { uint32_t *gui = (uint32_t *)data; if (p_array_size > 0) { Vector iv = value; int s = iv.size(); const int *r = iv.ptr(); for (int i = 0, j = 0; i < p_array_size; i++, j += 4) { if (i < s) { gui[j] = r[i]; } else { gui[j] = 0; } gui[j + 1] = 0; // ignored gui[j + 2] = 0; // ignored gui[j + 3] = 0; // ignored } } else { int v = value; gui[0] = v; } } break; case ShaderLanguage::TYPE_UVEC2: { Vector iv = value; int s = iv.size(); uint32_t *gui = (uint32_t *)data; if (p_array_size <= 0) { p_array_size = 1; } int count = 2 * p_array_size; const int *r = iv.ptr(); for (int i = 0, j = 0; i < count; i += 2, j += 4) { if (i < s) { gui[j] = r[i]; gui[j + 1] = r[i + 1]; } else { gui[j] = 0; gui[j + 1] = 0; } gui[j + 2] = 0; // ignored gui[j + 3] = 0; // ignored } } break; case ShaderLanguage::TYPE_UVEC3: { Vector iv = value; int s = iv.size(); uint32_t *gui = (uint32_t *)data; if (p_array_size <= 0) { p_array_size = 1; } int count = 3 * p_array_size; const int *r = iv.ptr(); for (int i = 0, j = 0; i < count; i += 3, j += 4) { if (i < s) { gui[j] = r[i]; gui[j + 1] = r[i + 1]; gui[j + 2] = r[i + 2]; } else { gui[j] = 0; gui[j + 1] = 0; gui[j + 2] = 0; } gui[j + 3] = 0; // ignored } } break; case ShaderLanguage::TYPE_UVEC4: { Vector iv = value; int s = iv.size(); uint32_t *gui = (uint32_t *)data; if (p_array_size <= 0) { p_array_size = 1; } int count = 4 * p_array_size; const int *r = iv.ptr(); for (int i = 0; i < count; i++) { if (i < s) { gui[i] = r[i]; gui[i + 1] = r[i + 1]; gui[i + 2] = r[i + 2]; gui[i + 3] = r[i + 3]; } else { gui[i] = 0; gui[i + 1] = 0; gui[i + 2] = 0; gui[i + 3] = 0; } } } break; case ShaderLanguage::TYPE_FLOAT: { float *gui = (float *)data; if (p_array_size > 0) { const PackedFloat32Array &a = value; int s = a.size(); for (int i = 0, j = 0; i < p_array_size; i++, j += 4) { if (i < s) { gui[j] = a[i]; } else { gui[j] = 0; } gui[j + 1] = 0; // ignored gui[j + 2] = 0; // ignored gui[j + 3] = 0; // ignored } } else { float v = value; gui[0] = v; } } break; case ShaderLanguage::TYPE_VEC2: { float *gui = (float *)data; if (p_array_size > 0) { const PackedVector2Array &a = value; int s = a.size(); for (int i = 0, j = 0; i < p_array_size; i++, j += 4) { if (i < s) { gui[j] = a[i].x; gui[j + 1] = a[i].y; } else { gui[j] = 0; gui[j + 1] = 0; } gui[j + 2] = 0; // ignored gui[j + 3] = 0; // ignored } } else { Vector2 v = value; gui[0] = v.x; gui[1] = v.y; } } break; case ShaderLanguage::TYPE_VEC3: { float *gui = (float *)data; if (p_array_size > 0) { const PackedVector3Array &a = value; int s = a.size(); for (int i = 0, j = 0; i < p_array_size; i++, j += 4) { if (i < s) { gui[j] = a[i].x; gui[j + 1] = a[i].y; gui[j + 2] = a[i].z; } else { gui[j] = 0; gui[j + 1] = 0; gui[j + 2] = 0; } gui[j + 3] = 0; // ignored } } else { Vector3 v = value; gui[0] = v.x; gui[1] = v.y; gui[2] = v.z; } } break; case ShaderLanguage::TYPE_VEC4: { float *gui = (float *)data; if (p_array_size > 0) { if (value.get_type() == Variant::PACKED_COLOR_ARRAY) { const PackedColorArray &a = value; int s = a.size(); for (int i = 0, j = 0; i < p_array_size; i++, j += 4) { if (i < s) { Color color = a[i]; if (p_linear_color) { color = color.to_linear(); } gui[j] = color.r; gui[j + 1] = color.g; gui[j + 2] = color.b; gui[j + 3] = color.a; } else { gui[j] = 0; gui[j + 1] = 0; gui[j + 2] = 0; gui[j + 3] = 0; } } } else { const PackedFloat32Array &a = value; int s = a.size(); int count = 4 * p_array_size; for (int i = 0; i < count; i += 4) { if (i + 3 < s) { gui[i] = a[i]; gui[i + 1] = a[i + 1]; gui[i + 2] = a[i + 2]; gui[i + 3] = a[i + 3]; } else { gui[i] = 0; gui[i + 1] = 0; gui[i + 2] = 0; gui[i + 3] = 0; } } } } else { if (value.get_type() == Variant::COLOR) { Color v = value; if (p_linear_color) { v = v.to_linear(); } gui[0] = v.r; gui[1] = v.g; gui[2] = v.b; gui[3] = v.a; } else if (value.get_type() == Variant::RECT2) { Rect2 v = value; gui[0] = v.position.x; gui[1] = v.position.y; gui[2] = v.size.x; gui[3] = v.size.y; } else if (value.get_type() == Variant::QUATERNION) { Quaternion v = value; gui[0] = v.x; gui[1] = v.y; gui[2] = v.z; gui[3] = v.w; } else { Plane v = value; gui[0] = v.normal.x; gui[1] = v.normal.y; gui[2] = v.normal.z; gui[3] = v.d; } } } break; case ShaderLanguage::TYPE_MAT2: { float *gui = (float *)data; if (p_array_size > 0) { const PackedFloat32Array &a = value; int s = a.size(); for (int i = 0, j = 0; i < p_array_size * 4; i += 4, j += 8) { if (i + 3 < s) { gui[j] = a[i]; gui[j + 1] = a[i + 1]; gui[j + 4] = a[i + 2]; gui[j + 5] = a[i + 3]; } else { gui[j] = 1; gui[j + 1] = 0; gui[j + 4] = 0; gui[j + 5] = 1; } gui[j + 2] = 0; // ignored gui[j + 3] = 0; // ignored gui[j + 6] = 0; // ignored gui[j + 7] = 0; // ignored } } else { Transform2D v = value; //in std140 members of mat2 are treated as vec4s gui[0] = v.elements[0][0]; gui[1] = v.elements[0][1]; gui[2] = 0; // ignored gui[3] = 0; // ignored gui[4] = v.elements[1][0]; gui[5] = v.elements[1][1]; gui[6] = 0; // ignored gui[7] = 0; // ignored } } break; case ShaderLanguage::TYPE_MAT3: { float *gui = (float *)data; if (p_array_size > 0) { const PackedFloat32Array &a = value; int s = a.size(); for (int i = 0, j = 0; i < p_array_size * 9; i += 9, j += 12) { if (i + 8 < s) { gui[j] = a[i]; gui[j + 1] = a[i + 1]; gui[j + 2] = a[i + 2]; gui[j + 4] = a[i + 3]; gui[j + 5] = a[i + 4]; gui[j + 6] = a[i + 5]; gui[j + 8] = a[i + 6]; gui[j + 9] = a[i + 7]; gui[j + 10] = a[i + 8]; } else { gui[j] = 1; gui[j + 1] = 0; gui[j + 2] = 0; gui[j + 4] = 0; gui[j + 5] = 1; gui[j + 6] = 0; gui[j + 8] = 0; gui[j + 9] = 0; gui[j + 10] = 1; } gui[j + 3] = 0; // ignored gui[j + 7] = 0; // ignored gui[j + 11] = 0; // ignored } } else { Basis v = value; gui[0] = v.elements[0][0]; gui[1] = v.elements[1][0]; gui[2] = v.elements[2][0]; gui[3] = 0; // ignored gui[4] = v.elements[0][1]; gui[5] = v.elements[1][1]; gui[6] = v.elements[2][1]; gui[7] = 0; // ignored gui[8] = v.elements[0][2]; gui[9] = v.elements[1][2]; gui[10] = v.elements[2][2]; gui[11] = 0; // ignored } } break; case ShaderLanguage::TYPE_MAT4: { float *gui = (float *)data; if (p_array_size > 0) { const PackedFloat32Array &a = value; int s = a.size(); for (int i = 0; i < p_array_size * 16; i += 16) { if (i + 15 < s) { gui[i] = a[i]; gui[i + 1] = a[i + 1]; gui[i + 2] = a[i + 2]; gui[i + 3] = a[i + 3]; gui[i + 4] = a[i + 4]; gui[i + 5] = a[i + 5]; gui[i + 6] = a[i + 6]; gui[i + 7] = a[i + 7]; gui[i + 8] = a[i + 8]; gui[i + 9] = a[i + 9]; gui[i + 10] = a[i + 10]; gui[i + 11] = a[i + 11]; gui[i + 12] = a[i + 12]; gui[i + 13] = a[i + 13]; gui[i + 14] = a[i + 14]; gui[i + 15] = a[i + 15]; } else { gui[i] = 1; gui[i + 1] = 0; gui[i + 2] = 0; gui[i + 3] = 0; gui[i + 4] = 0; gui[i + 5] = 1; gui[i + 6] = 0; gui[i + 7] = 0; gui[i + 8] = 0; gui[i + 9] = 0; gui[i + 10] = 1; gui[i + 11] = 0; gui[i + 12] = 0; gui[i + 13] = 0; gui[i + 14] = 0; gui[i + 15] = 1; } } } else { Transform3D v = value; gui[0] = v.basis.elements[0][0]; gui[1] = v.basis.elements[1][0]; gui[2] = v.basis.elements[2][0]; gui[3] = 0; gui[4] = v.basis.elements[0][1]; gui[5] = v.basis.elements[1][1]; gui[6] = v.basis.elements[2][1]; gui[7] = 0; gui[8] = v.basis.elements[0][2]; gui[9] = v.basis.elements[1][2]; gui[10] = v.basis.elements[2][2]; gui[11] = 0; gui[12] = v.origin.x; gui[13] = v.origin.y; gui[14] = v.origin.z; gui[15] = 1; } } break; default: { } } } _FORCE_INLINE_ static void _fill_std140_ubo_value(ShaderLanguage::DataType type, const Vector &value, uint8_t *data) { switch (type) { case ShaderLanguage::TYPE_BOOL: { uint32_t *gui = (uint32_t *)data; *gui = value[0].boolean ? 1 : 0; } break; case ShaderLanguage::TYPE_BVEC2: { uint32_t *gui = (uint32_t *)data; gui[0] = value[0].boolean ? 1 : 0; gui[1] = value[1].boolean ? 1 : 0; } break; case ShaderLanguage::TYPE_BVEC3: { uint32_t *gui = (uint32_t *)data; gui[0] = value[0].boolean ? 1 : 0; gui[1] = value[1].boolean ? 1 : 0; gui[2] = value[2].boolean ? 1 : 0; } break; case ShaderLanguage::TYPE_BVEC4: { uint32_t *gui = (uint32_t *)data; gui[0] = value[0].boolean ? 1 : 0; gui[1] = value[1].boolean ? 1 : 0; gui[2] = value[2].boolean ? 1 : 0; gui[3] = value[3].boolean ? 1 : 0; } break; case ShaderLanguage::TYPE_INT: { int32_t *gui = (int32_t *)data; gui[0] = value[0].sint; } break; case ShaderLanguage::TYPE_IVEC2: { int32_t *gui = (int32_t *)data; for (int i = 0; i < 2; i++) { gui[i] = value[i].sint; } } break; case ShaderLanguage::TYPE_IVEC3: { int32_t *gui = (int32_t *)data; for (int i = 0; i < 3; i++) { gui[i] = value[i].sint; } } break; case ShaderLanguage::TYPE_IVEC4: { int32_t *gui = (int32_t *)data; for (int i = 0; i < 4; i++) { gui[i] = value[i].sint; } } break; case ShaderLanguage::TYPE_UINT: { uint32_t *gui = (uint32_t *)data; gui[0] = value[0].uint; } break; case ShaderLanguage::TYPE_UVEC2: { int32_t *gui = (int32_t *)data; for (int i = 0; i < 2; i++) { gui[i] = value[i].uint; } } break; case ShaderLanguage::TYPE_UVEC3: { int32_t *gui = (int32_t *)data; for (int i = 0; i < 3; i++) { gui[i] = value[i].uint; } } break; case ShaderLanguage::TYPE_UVEC4: { int32_t *gui = (int32_t *)data; for (int i = 0; i < 4; i++) { gui[i] = value[i].uint; } } break; case ShaderLanguage::TYPE_FLOAT: { float *gui = (float *)data; gui[0] = value[0].real; } break; case ShaderLanguage::TYPE_VEC2: { float *gui = (float *)data; for (int i = 0; i < 2; i++) { gui[i] = value[i].real; } } break; case ShaderLanguage::TYPE_VEC3: { float *gui = (float *)data; for (int i = 0; i < 3; i++) { gui[i] = value[i].real; } } break; case ShaderLanguage::TYPE_VEC4: { float *gui = (float *)data; for (int i = 0; i < 4; i++) { gui[i] = value[i].real; } } break; case ShaderLanguage::TYPE_MAT2: { float *gui = (float *)data; //in std140 members of mat2 are treated as vec4s gui[0] = value[0].real; gui[1] = value[1].real; gui[2] = 0; gui[3] = 0; gui[4] = value[2].real; gui[5] = value[3].real; gui[6] = 0; gui[7] = 0; } break; case ShaderLanguage::TYPE_MAT3: { float *gui = (float *)data; gui[0] = value[0].real; gui[1] = value[1].real; gui[2] = value[2].real; gui[3] = 0; gui[4] = value[3].real; gui[5] = value[4].real; gui[6] = value[5].real; gui[7] = 0; gui[8] = value[6].real; gui[9] = value[7].real; gui[10] = value[8].real; gui[11] = 0; } break; case ShaderLanguage::TYPE_MAT4: { float *gui = (float *)data; for (int i = 0; i < 16; i++) { gui[i] = value[i].real; } } break; default: { } } } _FORCE_INLINE_ static void _fill_std140_ubo_empty(ShaderLanguage::DataType type, int p_array_size, uint8_t *data) { if (p_array_size <= 0) { p_array_size = 1; } switch (type) { case ShaderLanguage::TYPE_BOOL: case ShaderLanguage::TYPE_INT: case ShaderLanguage::TYPE_UINT: case ShaderLanguage::TYPE_FLOAT: { memset(data, 0, 4 * p_array_size); } break; case ShaderLanguage::TYPE_BVEC2: case ShaderLanguage::TYPE_IVEC2: case ShaderLanguage::TYPE_UVEC2: case ShaderLanguage::TYPE_VEC2: { memset(data, 0, 8 * p_array_size); } break; case ShaderLanguage::TYPE_BVEC3: case ShaderLanguage::TYPE_IVEC3: case ShaderLanguage::TYPE_UVEC3: case ShaderLanguage::TYPE_VEC3: case ShaderLanguage::TYPE_BVEC4: case ShaderLanguage::TYPE_IVEC4: case ShaderLanguage::TYPE_UVEC4: case ShaderLanguage::TYPE_VEC4: { memset(data, 0, 16 * p_array_size); } break; case ShaderLanguage::TYPE_MAT2: { memset(data, 0, 32 * p_array_size); } break; case ShaderLanguage::TYPE_MAT3: { memset(data, 0, 48 * p_array_size); } break; case ShaderLanguage::TYPE_MAT4: { memset(data, 0, 64 * p_array_size); } break; default: { } } } void RendererStorageRD::MaterialData::update_uniform_buffer(const Map &p_uniforms, const uint32_t *p_uniform_offsets, const Map &p_parameters, uint8_t *p_buffer, uint32_t p_buffer_size, bool p_use_linear_color) { bool uses_global_buffer = false; for (const KeyValue &E : p_uniforms) { if (E.value.order < 0) { continue; // texture, does not go here } if (E.value.scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) { continue; //instance uniforms don't appear in the buffer } if (E.value.scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_GLOBAL) { //this is a global variable, get the index to it RendererStorageRD *rs = base_singleton; GlobalVariables::Variable *gv = rs->global_variables.variables.getptr(E.key); uint32_t index = 0; if (gv) { index = gv->buffer_index; } else { WARN_PRINT("Shader uses global uniform '" + E.key + "', but it was removed at some point. Material will not display correctly."); } uint32_t offset = p_uniform_offsets[E.value.order]; uint32_t *intptr = (uint32_t *)&p_buffer[offset]; *intptr = index; uses_global_buffer = true; continue; } //regular uniform uint32_t offset = p_uniform_offsets[E.value.order]; #ifdef DEBUG_ENABLED uint32_t size = 0U; // The following code enforces a 16-byte alignment of uniform arrays. if (E.value.array_size > 0) { size = ShaderLanguage::get_datatype_size(E.value.type) * E.value.array_size; int m = (16 * E.value.array_size); if ((size % m) != 0U) { size += m - (size % m); } } else { size = ShaderLanguage::get_datatype_size(E.value.type); } ERR_CONTINUE(offset + size > p_buffer_size); #endif uint8_t *data = &p_buffer[offset]; const Map::Element *V = p_parameters.find(E.key); if (V) { //user provided _fill_std140_variant_ubo_value(E.value.type, E.value.array_size, V->get(), data, p_use_linear_color); } else if (E.value.default_value.size()) { //default value _fill_std140_ubo_value(E.value.type, E.value.default_value, data); //value=E.value.default_value; } else { //zero because it was not provided if (E.value.type == ShaderLanguage::TYPE_VEC4 && E.value.hint == ShaderLanguage::ShaderNode::Uniform::HINT_COLOR) { //colors must be set as black, with alpha as 1.0 _fill_std140_variant_ubo_value(E.value.type, E.value.array_size, Color(0, 0, 0, 1), data, p_use_linear_color); } else { //else just zero it out _fill_std140_ubo_empty(E.value.type, E.value.array_size, data); } } } if (uses_global_buffer != (global_buffer_E != nullptr)) { RendererStorageRD *rs = base_singleton; if (uses_global_buffer) { global_buffer_E = rs->global_variables.materials_using_buffer.push_back(self); } else { rs->global_variables.materials_using_buffer.erase(global_buffer_E); global_buffer_E = nullptr; } } } RendererStorageRD::MaterialData::~MaterialData() { if (global_buffer_E) { //unregister global buffers RendererStorageRD *rs = base_singleton; rs->global_variables.materials_using_buffer.erase(global_buffer_E); } if (global_texture_E) { //unregister global textures RendererStorageRD *rs = base_singleton; for (const KeyValue &E : used_global_textures) { GlobalVariables::Variable *v = rs->global_variables.variables.getptr(E.key); if (v) { v->texture_materials.erase(self); } } //unregister material from those using global textures rs->global_variables.materials_using_texture.erase(global_texture_E); } if (uniform_buffer.is_valid()) { RD::get_singleton()->free(uniform_buffer); } } void RendererStorageRD::MaterialData::update_textures(const Map &p_parameters, const Map> &p_default_textures, const Vector &p_texture_uniforms, RID *p_textures, bool p_use_linear_color) { RendererStorageRD *singleton = (RendererStorageRD *)RendererStorage::base_singleton; #ifdef TOOLS_ENABLED Texture *roughness_detect_texture = nullptr; RS::TextureDetectRoughnessChannel roughness_channel = RS::TEXTURE_DETECT_ROUGHNESS_R; Texture *normal_detect_texture = nullptr; #endif bool uses_global_textures = false; global_textures_pass++; for (int i = 0, k = 0; i < p_texture_uniforms.size(); i++) { const StringName &uniform_name = p_texture_uniforms[i].name; int uniform_array_size = p_texture_uniforms[i].array_size; Vector textures; if (p_texture_uniforms[i].global) { RendererStorageRD *rs = base_singleton; uses_global_textures = true; GlobalVariables::Variable *v = rs->global_variables.variables.getptr(uniform_name); if (v) { if (v->buffer_index >= 0) { WARN_PRINT("Shader uses global uniform texture '" + String(uniform_name) + "', but it changed type and is no longer a texture!."); } else { Map::Element *E = used_global_textures.find(uniform_name); if (!E) { E = used_global_textures.insert(uniform_name, global_textures_pass); v->texture_materials.insert(self); } else { E->get() = global_textures_pass; } textures.push_back(v->override.get_type() != Variant::NIL ? v->override : v->value); } } else { WARN_PRINT("Shader uses global uniform texture '" + String(uniform_name) + "', but it was removed at some point. Material will not display correctly."); } } else { const Map::Element *V = p_parameters.find(uniform_name); if (V) { if (V->get().is_array()) { Array array = (Array)V->get(); if (uniform_array_size > 0) { for (int j = 0; j < array.size(); j++) { textures.push_back(array[j]); } } else { if (array.size() > 0) { textures.push_back(array[0]); } } } else { textures.push_back(V->get()); } } if (uniform_array_size > 0) { if (textures.size() < uniform_array_size) { const Map>::Element *W = p_default_textures.find(uniform_name); for (int j = textures.size(); j < uniform_array_size; j++) { if (W && W->get().has(j)) { textures.push_back(W->get()[j]); } else { textures.push_back(RID()); } } } } else if (textures.is_empty()) { const Map>::Element *W = p_default_textures.find(uniform_name); if (W && W->get().has(0)) { textures.push_back(W->get()[0]); } } } RID rd_texture; if (textures.is_empty()) { //check default usage switch (p_texture_uniforms[i].type) { case ShaderLanguage::TYPE_ISAMPLER2D: case ShaderLanguage::TYPE_USAMPLER2D: case ShaderLanguage::TYPE_SAMPLER2D: { switch (p_texture_uniforms[i].hint) { case ShaderLanguage::ShaderNode::Uniform::HINT_BLACK: case ShaderLanguage::ShaderNode::Uniform::HINT_BLACK_ALBEDO: { rd_texture = singleton->texture_rd_get_default(DEFAULT_RD_TEXTURE_BLACK); } break; case ShaderLanguage::ShaderNode::Uniform::HINT_ANISOTROPY: { rd_texture = singleton->texture_rd_get_default(DEFAULT_RD_TEXTURE_ANISO); } break; case ShaderLanguage::ShaderNode::Uniform::HINT_NORMAL: { rd_texture = singleton->texture_rd_get_default(DEFAULT_RD_TEXTURE_NORMAL); } break; case ShaderLanguage::ShaderNode::Uniform::HINT_ROUGHNESS_NORMAL: { rd_texture = singleton->texture_rd_get_default(DEFAULT_RD_TEXTURE_NORMAL); } break; default: { rd_texture = singleton->texture_rd_get_default(DEFAULT_RD_TEXTURE_WHITE); } break; } } break; case ShaderLanguage::TYPE_SAMPLERCUBE: { switch (p_texture_uniforms[i].hint) { case ShaderLanguage::ShaderNode::Uniform::HINT_BLACK: case ShaderLanguage::ShaderNode::Uniform::HINT_BLACK_ALBEDO: { rd_texture = singleton->texture_rd_get_default(DEFAULT_RD_TEXTURE_CUBEMAP_BLACK); } break; default: { rd_texture = singleton->texture_rd_get_default(DEFAULT_RD_TEXTURE_CUBEMAP_WHITE); } break; } } break; case ShaderLanguage::TYPE_SAMPLERCUBEARRAY: { rd_texture = singleton->texture_rd_get_default(DEFAULT_RD_TEXTURE_CUBEMAP_ARRAY_BLACK); } break; case ShaderLanguage::TYPE_ISAMPLER3D: case ShaderLanguage::TYPE_USAMPLER3D: case ShaderLanguage::TYPE_SAMPLER3D: { rd_texture = singleton->texture_rd_get_default(DEFAULT_RD_TEXTURE_3D_WHITE); } break; case ShaderLanguage::TYPE_ISAMPLER2DARRAY: case ShaderLanguage::TYPE_USAMPLER2DARRAY: case ShaderLanguage::TYPE_SAMPLER2DARRAY: { rd_texture = singleton->texture_rd_get_default(DEFAULT_RD_TEXTURE_2D_ARRAY_WHITE); } break; default: { } } #ifdef TOOLS_ENABLED if (roughness_detect_texture && normal_detect_texture && !normal_detect_texture->path.is_empty()) { roughness_detect_texture->detect_roughness_callback(roughness_detect_texture->detect_roughness_callback_ud, normal_detect_texture->path, roughness_channel); } #endif if (uniform_array_size > 0) { for (int j = 0; j < uniform_array_size; j++) { p_textures[k++] = rd_texture; } } else { p_textures[k++] = rd_texture; } } else { bool srgb = p_use_linear_color && (p_texture_uniforms[i].hint == ShaderLanguage::ShaderNode::Uniform::HINT_ALBEDO || p_texture_uniforms[i].hint == ShaderLanguage::ShaderNode::Uniform::HINT_BLACK_ALBEDO); for (int j = 0; j < textures.size(); j++) { Texture *tex = singleton->texture_owner.get_or_null(textures[j]); if (tex) { rd_texture = (srgb && tex->rd_texture_srgb.is_valid()) ? tex->rd_texture_srgb : tex->rd_texture; #ifdef TOOLS_ENABLED if (tex->detect_3d_callback && p_use_linear_color) { tex->detect_3d_callback(tex->detect_3d_callback_ud); } if (tex->detect_normal_callback && (p_texture_uniforms[i].hint == ShaderLanguage::ShaderNode::Uniform::HINT_NORMAL || p_texture_uniforms[i].hint == ShaderLanguage::ShaderNode::Uniform::HINT_ROUGHNESS_NORMAL)) { if (p_texture_uniforms[i].hint == ShaderLanguage::ShaderNode::Uniform::HINT_ROUGHNESS_NORMAL) { normal_detect_texture = tex; } tex->detect_normal_callback(tex->detect_normal_callback_ud); } if (tex->detect_roughness_callback && (p_texture_uniforms[i].hint >= ShaderLanguage::ShaderNode::Uniform::HINT_ROUGHNESS_R || p_texture_uniforms[i].hint <= ShaderLanguage::ShaderNode::Uniform::HINT_ROUGHNESS_GRAY)) { //find the normal texture roughness_detect_texture = tex; roughness_channel = RS::TextureDetectRoughnessChannel(p_texture_uniforms[i].hint - ShaderLanguage::ShaderNode::Uniform::HINT_ROUGHNESS_R); } #endif } if (rd_texture.is_null()) { rd_texture = singleton->texture_rd_get_default(DEFAULT_RD_TEXTURE_WHITE); } #ifdef TOOLS_ENABLED if (roughness_detect_texture && normal_detect_texture && !normal_detect_texture->path.is_empty()) { roughness_detect_texture->detect_roughness_callback(roughness_detect_texture->detect_roughness_callback_ud, normal_detect_texture->path, roughness_channel); } #endif p_textures[k++] = rd_texture; } } } { //for textures no longer used, unregister them List::Element *> to_delete; RendererStorageRD *rs = base_singleton; for (Map::Element *E = used_global_textures.front(); E; E = E->next()) { if (E->get() != global_textures_pass) { to_delete.push_back(E); GlobalVariables::Variable *v = rs->global_variables.variables.getptr(E->key()); if (v) { v->texture_materials.erase(self); } } } while (to_delete.front()) { used_global_textures.erase(to_delete.front()->get()); to_delete.pop_front(); } //handle registering/unregistering global textures if (uses_global_textures != (global_texture_E != nullptr)) { if (uses_global_textures) { global_texture_E = rs->global_variables.materials_using_texture.push_back(self); } else { rs->global_variables.materials_using_texture.erase(global_texture_E); global_texture_E = nullptr; } } } } void RendererStorageRD::MaterialData::free_parameters_uniform_set(RID p_uniform_set) { if (p_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(p_uniform_set)) { RD::get_singleton()->uniform_set_set_invalidation_callback(p_uniform_set, nullptr, nullptr); RD::get_singleton()->free(p_uniform_set); } } bool RendererStorageRD::MaterialData::update_parameters_uniform_set(const Map &p_parameters, bool p_uniform_dirty, bool p_textures_dirty, const Map &p_uniforms, const uint32_t *p_uniform_offsets, const Vector &p_texture_uniforms, const Map> &p_default_texture_params, uint32_t p_ubo_size, RID &uniform_set, RID p_shader, uint32_t p_shader_uniform_set, uint32_t p_barrier) { if ((uint32_t)ubo_data.size() != p_ubo_size) { p_uniform_dirty = true; if (uniform_buffer.is_valid()) { RD::get_singleton()->free(uniform_buffer); uniform_buffer = RID(); } ubo_data.resize(p_ubo_size); if (ubo_data.size()) { uniform_buffer = RD::get_singleton()->uniform_buffer_create(ubo_data.size()); memset(ubo_data.ptrw(), 0, ubo_data.size()); //clear } //clear previous uniform set if (uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(uniform_set)) { RD::get_singleton()->uniform_set_set_invalidation_callback(uniform_set, nullptr, nullptr); RD::get_singleton()->free(uniform_set); uniform_set = RID(); } } //check whether buffer changed if (p_uniform_dirty && ubo_data.size()) { update_uniform_buffer(p_uniforms, p_uniform_offsets, p_parameters, ubo_data.ptrw(), ubo_data.size(), true); RD::get_singleton()->buffer_update(uniform_buffer, 0, ubo_data.size(), ubo_data.ptrw(), p_barrier); } uint32_t tex_uniform_count = 0U; for (int i = 0; i < p_texture_uniforms.size(); i++) { tex_uniform_count += uint32_t(p_texture_uniforms[i].array_size > 0 ? p_texture_uniforms[i].array_size : 1); } if ((uint32_t)texture_cache.size() != tex_uniform_count || p_textures_dirty) { texture_cache.resize(tex_uniform_count); p_textures_dirty = true; //clear previous uniform set if (uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(uniform_set)) { RD::get_singleton()->uniform_set_set_invalidation_callback(uniform_set, nullptr, nullptr); RD::get_singleton()->free(uniform_set); uniform_set = RID(); } } if (p_textures_dirty && tex_uniform_count) { update_textures(p_parameters, p_default_texture_params, p_texture_uniforms, texture_cache.ptrw(), true); } if (p_ubo_size == 0 && p_texture_uniforms.size() == 0) { // This material does not require an uniform set, so don't create it. return false; } if (!p_textures_dirty && uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(uniform_set)) { //no reason to update uniform set, only UBO (or nothing) was needed to update return false; } Vector uniforms; { if (p_ubo_size) { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; u.binding = 0; u.append_id(uniform_buffer); uniforms.push_back(u); } const RID *textures = texture_cache.ptrw(); for (int i = 0, k = 0; i < p_texture_uniforms.size(); i++) { const int array_size = p_texture_uniforms[i].array_size; RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; u.binding = 1 + k; if (array_size > 0) { for (int j = 0; j < array_size; j++) { u.append_id(textures[k++]); } } else { u.append_id(textures[k++]); } uniforms.push_back(u); } } uniform_set = RD::get_singleton()->uniform_set_create(uniforms, p_shader, p_shader_uniform_set); RD::get_singleton()->uniform_set_set_invalidation_callback(uniform_set, _material_uniform_set_erased, &self); return true; } void RendererStorageRD::_material_uniform_set_erased(void *p_material) { RID rid = *(RID *)p_material; Material *material = base_singleton->material_owner.get_or_null(rid); if (material) { if (material->data) { // Uniform set may be gone because a dependency was erased. This happens // if a texture is deleted, so re-create it. base_singleton->_material_queue_update(material, false, true); } material->dependency.changed_notify(DEPENDENCY_CHANGED_MATERIAL); } } void RendererStorageRD::_update_queued_materials() { while (material_update_list.first()) { Material *material = material_update_list.first()->self(); bool uniforms_changed = false; if (material->data) { uniforms_changed = material->data->update_parameters(material->params, material->uniform_dirty, material->texture_dirty); } material->texture_dirty = false; material->uniform_dirty = false; material_update_list.remove(&material->update_element); if (uniforms_changed) { //some implementations such as 3D renderer cache the matreial uniform set, so update is required material->dependency.changed_notify(DEPENDENCY_CHANGED_MATERIAL); } } } /* MESH API */ RID RendererStorageRD::mesh_allocate() { return mesh_owner.allocate_rid(); } void RendererStorageRD::mesh_initialize(RID p_rid) { mesh_owner.initialize_rid(p_rid, Mesh()); } void RendererStorageRD::mesh_set_blend_shape_count(RID p_mesh, int p_blend_shape_count) { ERR_FAIL_COND(p_blend_shape_count < 0); Mesh *mesh = mesh_owner.get_or_null(p_mesh); ERR_FAIL_COND(!mesh); ERR_FAIL_COND(mesh->surface_count > 0); //surfaces already exist mesh->blend_shape_count = p_blend_shape_count; } /// Returns stride void RendererStorageRD::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface) { Mesh *mesh = mesh_owner.get_or_null(p_mesh); ERR_FAIL_COND(!mesh); ERR_FAIL_COND(mesh->surface_count == RS::MAX_MESH_SURFACES); #ifdef DEBUG_ENABLED //do a validation, to catch errors first { uint32_t stride = 0; uint32_t attrib_stride = 0; uint32_t skin_stride = 0; for (int i = 0; i < RS::ARRAY_WEIGHTS; i++) { if ((p_surface.format & (1 << i))) { switch (i) { case RS::ARRAY_VERTEX: { if (p_surface.format & RS::ARRAY_FLAG_USE_2D_VERTICES) { stride += sizeof(float) * 2; } else { stride += sizeof(float) * 3; } } break; case RS::ARRAY_NORMAL: { stride += sizeof(int32_t); } break; case RS::ARRAY_TANGENT: { stride += sizeof(int32_t); } break; case RS::ARRAY_COLOR: { attrib_stride += sizeof(uint32_t); } break; case RS::ARRAY_TEX_UV: { attrib_stride += sizeof(float) * 2; } break; case RS::ARRAY_TEX_UV2: { attrib_stride += sizeof(float) * 2; } break; case RS::ARRAY_CUSTOM0: case RS::ARRAY_CUSTOM1: case RS::ARRAY_CUSTOM2: case RS::ARRAY_CUSTOM3: { int idx = i - RS::ARRAY_CUSTOM0; uint32_t fmt_shift[RS::ARRAY_CUSTOM_COUNT] = { RS::ARRAY_FORMAT_CUSTOM0_SHIFT, RS::ARRAY_FORMAT_CUSTOM1_SHIFT, RS::ARRAY_FORMAT_CUSTOM2_SHIFT, RS::ARRAY_FORMAT_CUSTOM3_SHIFT }; uint32_t fmt = (p_surface.format >> fmt_shift[idx]) & RS::ARRAY_FORMAT_CUSTOM_MASK; uint32_t fmtsize[RS::ARRAY_CUSTOM_MAX] = { 4, 4, 4, 8, 4, 8, 12, 16 }; attrib_stride += fmtsize[fmt]; } break; case RS::ARRAY_WEIGHTS: case RS::ARRAY_BONES: { //uses a separate array bool use_8 = p_surface.format & RS::ARRAY_FLAG_USE_8_BONE_WEIGHTS; skin_stride += sizeof(int16_t) * (use_8 ? 16 : 8); } break; } } } int expected_size = stride * p_surface.vertex_count; ERR_FAIL_COND_MSG(expected_size != p_surface.vertex_data.size(), "Size of vertex data provided (" + itos(p_surface.vertex_data.size()) + ") does not match expected (" + itos(expected_size) + ")"); int bs_expected_size = expected_size * mesh->blend_shape_count; ERR_FAIL_COND_MSG(bs_expected_size != p_surface.blend_shape_data.size(), "Size of blend shape data provided (" + itos(p_surface.blend_shape_data.size()) + ") does not match expected (" + itos(bs_expected_size) + ")"); int expected_attrib_size = attrib_stride * p_surface.vertex_count; ERR_FAIL_COND_MSG(expected_attrib_size != p_surface.attribute_data.size(), "Size of attribute data provided (" + itos(p_surface.attribute_data.size()) + ") does not match expected (" + itos(expected_attrib_size) + ")"); if ((p_surface.format & RS::ARRAY_FORMAT_WEIGHTS) && (p_surface.format & RS::ARRAY_FORMAT_BONES)) { expected_size = skin_stride * p_surface.vertex_count; ERR_FAIL_COND_MSG(expected_size != p_surface.skin_data.size(), "Size of skin data provided (" + itos(p_surface.skin_data.size()) + ") does not match expected (" + itos(expected_size) + ")"); } } #endif Mesh::Surface *s = memnew(Mesh::Surface); s->format = p_surface.format; s->primitive = p_surface.primitive; bool use_as_storage = (p_surface.skin_data.size() || mesh->blend_shape_count > 0); s->vertex_buffer = RD::get_singleton()->vertex_buffer_create(p_surface.vertex_data.size(), p_surface.vertex_data, use_as_storage); s->vertex_buffer_size = p_surface.vertex_data.size(); if (p_surface.attribute_data.size()) { s->attribute_buffer = RD::get_singleton()->vertex_buffer_create(p_surface.attribute_data.size(), p_surface.attribute_data); } if (p_surface.skin_data.size()) { s->skin_buffer = RD::get_singleton()->vertex_buffer_create(p_surface.skin_data.size(), p_surface.skin_data, use_as_storage); s->skin_buffer_size = p_surface.skin_data.size(); } s->vertex_count = p_surface.vertex_count; if (p_surface.format & RS::ARRAY_FORMAT_BONES) { mesh->has_bone_weights = true; } if (p_surface.index_count) { bool is_index_16 = p_surface.vertex_count <= 65536; s->index_buffer = RD::get_singleton()->index_buffer_create(p_surface.index_count, is_index_16 ? RD::INDEX_BUFFER_FORMAT_UINT16 : RD::INDEX_BUFFER_FORMAT_UINT32, p_surface.index_data, false); s->index_count = p_surface.index_count; s->index_array = RD::get_singleton()->index_array_create(s->index_buffer, 0, s->index_count); if (p_surface.lods.size()) { s->lods = memnew_arr(Mesh::Surface::LOD, p_surface.lods.size()); s->lod_count = p_surface.lods.size(); for (int i = 0; i < p_surface.lods.size(); i++) { uint32_t indices = p_surface.lods[i].index_data.size() / (is_index_16 ? 2 : 4); s->lods[i].index_buffer = RD::get_singleton()->index_buffer_create(indices, is_index_16 ? RD::INDEX_BUFFER_FORMAT_UINT16 : RD::INDEX_BUFFER_FORMAT_UINT32, p_surface.lods[i].index_data); s->lods[i].index_array = RD::get_singleton()->index_array_create(s->lods[i].index_buffer, 0, indices); s->lods[i].edge_length = p_surface.lods[i].edge_length; s->lods[i].index_count = indices; } } } s->aabb = p_surface.aabb; s->bone_aabbs = p_surface.bone_aabbs; //only really useful for returning them. if (mesh->blend_shape_count > 0) { s->blend_shape_buffer = RD::get_singleton()->storage_buffer_create(p_surface.blend_shape_data.size(), p_surface.blend_shape_data); } if (use_as_storage) { Vector uniforms; { RD::Uniform u; u.binding = 0; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.append_id(s->vertex_buffer); uniforms.push_back(u); } { RD::Uniform u; u.binding = 1; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; if (s->skin_buffer.is_valid()) { u.append_id(s->skin_buffer); } else { u.append_id(default_rd_storage_buffer); } uniforms.push_back(u); } { RD::Uniform u; u.binding = 2; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; if (s->blend_shape_buffer.is_valid()) { u.append_id(s->blend_shape_buffer); } else { u.append_id(default_rd_storage_buffer); } uniforms.push_back(u); } s->uniform_set = RD::get_singleton()->uniform_set_create(uniforms, skeleton_shader.version_shader[0], SkeletonShader::UNIFORM_SET_SURFACE); } if (mesh->surface_count == 0) { mesh->bone_aabbs = p_surface.bone_aabbs; mesh->aabb = p_surface.aabb; } else { if (mesh->bone_aabbs.size() < p_surface.bone_aabbs.size()) { // ArrayMesh::_surface_set_data only allocates bone_aabbs up to max_bone // Each surface may affect different numbers of bones. mesh->bone_aabbs.resize(p_surface.bone_aabbs.size()); } for (int i = 0; i < p_surface.bone_aabbs.size(); i++) { mesh->bone_aabbs.write[i].merge_with(p_surface.bone_aabbs[i]); } mesh->aabb.merge_with(p_surface.aabb); } s->material = p_surface.material; mesh->surfaces = (Mesh::Surface **)memrealloc(mesh->surfaces, sizeof(Mesh::Surface *) * (mesh->surface_count + 1)); mesh->surfaces[mesh->surface_count] = s; mesh->surface_count++; for (MeshInstance *mi : mesh->instances) { _mesh_instance_add_surface(mi, mesh, mesh->surface_count - 1); } mesh->dependency.changed_notify(DEPENDENCY_CHANGED_MESH); for (Set::Element *E = mesh->shadow_owners.front(); E; E = E->next()) { Mesh *shadow_owner = E->get(); shadow_owner->shadow_mesh = RID(); shadow_owner->dependency.changed_notify(DEPENDENCY_CHANGED_MESH); } mesh->material_cache.clear(); } int RendererStorageRD::mesh_get_blend_shape_count(RID p_mesh) const { const Mesh *mesh = mesh_owner.get_or_null(p_mesh); ERR_FAIL_COND_V(!mesh, -1); return mesh->blend_shape_count; } void RendererStorageRD::mesh_set_blend_shape_mode(RID p_mesh, RS::BlendShapeMode p_mode) { Mesh *mesh = mesh_owner.get_or_null(p_mesh); ERR_FAIL_COND(!mesh); ERR_FAIL_INDEX((int)p_mode, 2); mesh->blend_shape_mode = p_mode; } RS::BlendShapeMode RendererStorageRD::mesh_get_blend_shape_mode(RID p_mesh) const { Mesh *mesh = mesh_owner.get_or_null(p_mesh); ERR_FAIL_COND_V(!mesh, RS::BLEND_SHAPE_MODE_NORMALIZED); return mesh->blend_shape_mode; } void RendererStorageRD::mesh_surface_update_vertex_region(RID p_mesh, int p_surface, int p_offset, const Vector &p_data) { Mesh *mesh = mesh_owner.get_or_null(p_mesh); ERR_FAIL_COND(!mesh); ERR_FAIL_UNSIGNED_INDEX((uint32_t)p_surface, mesh->surface_count); ERR_FAIL_COND(p_data.size() == 0); uint64_t data_size = p_data.size(); const uint8_t *r = p_data.ptr(); RD::get_singleton()->buffer_update(mesh->surfaces[p_surface]->vertex_buffer, p_offset, data_size, r); } void RendererStorageRD::mesh_surface_update_attribute_region(RID p_mesh, int p_surface, int p_offset, const Vector &p_data) { Mesh *mesh = mesh_owner.get_or_null(p_mesh); ERR_FAIL_COND(!mesh); ERR_FAIL_UNSIGNED_INDEX((uint32_t)p_surface, mesh->surface_count); ERR_FAIL_COND(p_data.size() == 0); ERR_FAIL_COND(mesh->surfaces[p_surface]->attribute_buffer.is_null()); uint64_t data_size = p_data.size(); const uint8_t *r = p_data.ptr(); RD::get_singleton()->buffer_update(mesh->surfaces[p_surface]->attribute_buffer, p_offset, data_size, r); } void RendererStorageRD::mesh_surface_update_skin_region(RID p_mesh, int p_surface, int p_offset, const Vector &p_data) { Mesh *mesh = mesh_owner.get_or_null(p_mesh); ERR_FAIL_COND(!mesh); ERR_FAIL_UNSIGNED_INDEX((uint32_t)p_surface, mesh->surface_count); ERR_FAIL_COND(p_data.size() == 0); ERR_FAIL_COND(mesh->surfaces[p_surface]->skin_buffer.is_null()); uint64_t data_size = p_data.size(); const uint8_t *r = p_data.ptr(); RD::get_singleton()->buffer_update(mesh->surfaces[p_surface]->skin_buffer, p_offset, data_size, r); } void RendererStorageRD::mesh_surface_set_material(RID p_mesh, int p_surface, RID p_material) { Mesh *mesh = mesh_owner.get_or_null(p_mesh); ERR_FAIL_COND(!mesh); ERR_FAIL_UNSIGNED_INDEX((uint32_t)p_surface, mesh->surface_count); mesh->surfaces[p_surface]->material = p_material; mesh->dependency.changed_notify(DEPENDENCY_CHANGED_MATERIAL); mesh->material_cache.clear(); } RID RendererStorageRD::mesh_surface_get_material(RID p_mesh, int p_surface) const { Mesh *mesh = mesh_owner.get_or_null(p_mesh); ERR_FAIL_COND_V(!mesh, RID()); ERR_FAIL_UNSIGNED_INDEX_V((uint32_t)p_surface, mesh->surface_count, RID()); return mesh->surfaces[p_surface]->material; } RS::SurfaceData RendererStorageRD::mesh_get_surface(RID p_mesh, int p_surface) const { Mesh *mesh = mesh_owner.get_or_null(p_mesh); ERR_FAIL_COND_V(!mesh, RS::SurfaceData()); ERR_FAIL_UNSIGNED_INDEX_V((uint32_t)p_surface, mesh->surface_count, RS::SurfaceData()); Mesh::Surface &s = *mesh->surfaces[p_surface]; RS::SurfaceData sd; sd.format = s.format; sd.vertex_data = RD::get_singleton()->buffer_get_data(s.vertex_buffer); if (s.attribute_buffer.is_valid()) { sd.attribute_data = RD::get_singleton()->buffer_get_data(s.attribute_buffer); } if (s.skin_buffer.is_valid()) { sd.skin_data = RD::get_singleton()->buffer_get_data(s.skin_buffer); } sd.vertex_count = s.vertex_count; sd.index_count = s.index_count; sd.primitive = s.primitive; if (sd.index_count) { sd.index_data = RD::get_singleton()->buffer_get_data(s.index_buffer); } sd.aabb = s.aabb; for (uint32_t i = 0; i < s.lod_count; i++) { RS::SurfaceData::LOD lod; lod.edge_length = s.lods[i].edge_length; lod.index_data = RD::get_singleton()->buffer_get_data(s.lods[i].index_buffer); sd.lods.push_back(lod); } sd.bone_aabbs = s.bone_aabbs; if (s.blend_shape_buffer.is_valid()) { sd.blend_shape_data = RD::get_singleton()->buffer_get_data(s.blend_shape_buffer); } return sd; } int RendererStorageRD::mesh_get_surface_count(RID p_mesh) const { Mesh *mesh = mesh_owner.get_or_null(p_mesh); ERR_FAIL_COND_V(!mesh, 0); return mesh->surface_count; } void RendererStorageRD::mesh_set_custom_aabb(RID p_mesh, const AABB &p_aabb) { Mesh *mesh = mesh_owner.get_or_null(p_mesh); ERR_FAIL_COND(!mesh); mesh->custom_aabb = p_aabb; } AABB RendererStorageRD::mesh_get_custom_aabb(RID p_mesh) const { Mesh *mesh = mesh_owner.get_or_null(p_mesh); ERR_FAIL_COND_V(!mesh, AABB()); return mesh->custom_aabb; } AABB RendererStorageRD::mesh_get_aabb(RID p_mesh, RID p_skeleton) { Mesh *mesh = mesh_owner.get_or_null(p_mesh); ERR_FAIL_COND_V(!mesh, AABB()); if (mesh->custom_aabb != AABB()) { return mesh->custom_aabb; } Skeleton *skeleton = skeleton_owner.get_or_null(p_skeleton); if (!skeleton || skeleton->size == 0) { return mesh->aabb; } AABB aabb; for (uint32_t i = 0; i < mesh->surface_count; i++) { AABB laabb; if ((mesh->surfaces[i]->format & RS::ARRAY_FORMAT_BONES) && mesh->surfaces[i]->bone_aabbs.size()) { int bs = mesh->surfaces[i]->bone_aabbs.size(); const AABB *skbones = mesh->surfaces[i]->bone_aabbs.ptr(); int sbs = skeleton->size; ERR_CONTINUE(bs > sbs); const float *baseptr = skeleton->data.ptr(); bool first = true; if (skeleton->use_2d) { for (int j = 0; j < bs; j++) { if (skbones[0].size == Vector3()) { continue; //bone is unused } const float *dataptr = baseptr + j * 8; Transform3D mtx; mtx.basis.elements[0].x = dataptr[0]; mtx.basis.elements[1].x = dataptr[1]; mtx.origin.x = dataptr[3]; mtx.basis.elements[0].y = dataptr[4]; mtx.basis.elements[1].y = dataptr[5]; mtx.origin.y = dataptr[7]; AABB baabb = mtx.xform(skbones[j]); if (first) { laabb = baabb; first = false; } else { laabb.merge_with(baabb); } } } else { for (int j = 0; j < bs; j++) { if (skbones[0].size == Vector3()) { continue; //bone is unused } const float *dataptr = baseptr + j * 12; Transform3D mtx; mtx.basis.elements[0][0] = dataptr[0]; mtx.basis.elements[0][1] = dataptr[1]; mtx.basis.elements[0][2] = dataptr[2]; mtx.origin.x = dataptr[3]; mtx.basis.elements[1][0] = dataptr[4]; mtx.basis.elements[1][1] = dataptr[5]; mtx.basis.elements[1][2] = dataptr[6]; mtx.origin.y = dataptr[7]; mtx.basis.elements[2][0] = dataptr[8]; mtx.basis.elements[2][1] = dataptr[9]; mtx.basis.elements[2][2] = dataptr[10]; mtx.origin.z = dataptr[11]; AABB baabb = mtx.xform(skbones[j]); if (first) { laabb = baabb; first = false; } else { laabb.merge_with(baabb); } } } if (laabb.size == Vector3()) { laabb = mesh->surfaces[i]->aabb; } } else { laabb = mesh->surfaces[i]->aabb; } if (i == 0) { aabb = laabb; } else { aabb.merge_with(laabb); } } return aabb; } void RendererStorageRD::mesh_set_shadow_mesh(RID p_mesh, RID p_shadow_mesh) { Mesh *mesh = mesh_owner.get_or_null(p_mesh); ERR_FAIL_COND(!mesh); Mesh *shadow_mesh = mesh_owner.get_or_null(mesh->shadow_mesh); if (shadow_mesh) { shadow_mesh->shadow_owners.erase(mesh); } mesh->shadow_mesh = p_shadow_mesh; shadow_mesh = mesh_owner.get_or_null(mesh->shadow_mesh); if (shadow_mesh) { shadow_mesh->shadow_owners.insert(mesh); } mesh->dependency.changed_notify(DEPENDENCY_CHANGED_MESH); } void RendererStorageRD::mesh_clear(RID p_mesh) { Mesh *mesh = mesh_owner.get_or_null(p_mesh); ERR_FAIL_COND(!mesh); for (uint32_t i = 0; i < mesh->surface_count; i++) { Mesh::Surface &s = *mesh->surfaces[i]; RD::get_singleton()->free(s.vertex_buffer); //clears arrays as dependency automatically, including all versions if (s.attribute_buffer.is_valid()) { RD::get_singleton()->free(s.attribute_buffer); } if (s.skin_buffer.is_valid()) { RD::get_singleton()->free(s.skin_buffer); } if (s.versions) { memfree(s.versions); //reallocs, so free with memfree. } if (s.index_buffer.is_valid()) { RD::get_singleton()->free(s.index_buffer); } if (s.lod_count) { for (uint32_t j = 0; j < s.lod_count; j++) { RD::get_singleton()->free(s.lods[j].index_buffer); } memdelete_arr(s.lods); } if (s.blend_shape_buffer.is_valid()) { RD::get_singleton()->free(s.blend_shape_buffer); } memdelete(mesh->surfaces[i]); } if (mesh->surfaces) { memfree(mesh->surfaces); } mesh->surfaces = nullptr; mesh->surface_count = 0; mesh->material_cache.clear(); //clear instance data for (MeshInstance *mi : mesh->instances) { _mesh_instance_clear(mi); } mesh->has_bone_weights = false; mesh->dependency.changed_notify(DEPENDENCY_CHANGED_MESH); for (Set::Element *E = mesh->shadow_owners.front(); E; E = E->next()) { Mesh *shadow_owner = E->get(); shadow_owner->shadow_mesh = RID(); shadow_owner->dependency.changed_notify(DEPENDENCY_CHANGED_MESH); } } bool RendererStorageRD::mesh_needs_instance(RID p_mesh, bool p_has_skeleton) { Mesh *mesh = mesh_owner.get_or_null(p_mesh); ERR_FAIL_COND_V(!mesh, false); return mesh->blend_shape_count > 0 || (mesh->has_bone_weights && p_has_skeleton); } /* MESH INSTANCE */ RID RendererStorageRD::mesh_instance_create(RID p_base) { Mesh *mesh = mesh_owner.get_or_null(p_base); ERR_FAIL_COND_V(!mesh, RID()); RID rid = mesh_instance_owner.make_rid(); MeshInstance *mi = mesh_instance_owner.get_or_null(rid); mi->mesh = mesh; for (uint32_t i = 0; i < mesh->surface_count; i++) { _mesh_instance_add_surface(mi, mesh, i); } mi->I = mesh->instances.push_back(mi); mi->dirty = true; return rid; } void RendererStorageRD::mesh_instance_set_skeleton(RID p_mesh_instance, RID p_skeleton) { MeshInstance *mi = mesh_instance_owner.get_or_null(p_mesh_instance); if (mi->skeleton == p_skeleton) { return; } mi->skeleton = p_skeleton; mi->skeleton_version = 0; mi->dirty = true; } void RendererStorageRD::mesh_instance_set_blend_shape_weight(RID p_mesh_instance, int p_shape, float p_weight) { MeshInstance *mi = mesh_instance_owner.get_or_null(p_mesh_instance); ERR_FAIL_COND(!mi); ERR_FAIL_INDEX(p_shape, (int)mi->blend_weights.size()); mi->blend_weights[p_shape] = p_weight; mi->weights_dirty = true; //will be eventually updated } void RendererStorageRD::_mesh_instance_clear(MeshInstance *mi) { for (uint32_t i = 0; i < mi->surfaces.size(); i++) { if (mi->surfaces[i].versions) { for (uint32_t j = 0; j < mi->surfaces[i].version_count; j++) { RD::get_singleton()->free(mi->surfaces[i].versions[j].vertex_array); } memfree(mi->surfaces[i].versions); } if (mi->surfaces[i].vertex_buffer.is_valid()) { RD::get_singleton()->free(mi->surfaces[i].vertex_buffer); } } mi->surfaces.clear(); if (mi->blend_weights_buffer.is_valid()) { RD::get_singleton()->free(mi->blend_weights_buffer); } mi->blend_weights.clear(); mi->weights_dirty = false; mi->skeleton_version = 0; } void RendererStorageRD::_mesh_instance_add_surface(MeshInstance *mi, Mesh *mesh, uint32_t p_surface) { if (mesh->blend_shape_count > 0 && mi->blend_weights_buffer.is_null()) { mi->blend_weights.resize(mesh->blend_shape_count); for (uint32_t i = 0; i < mi->blend_weights.size(); i++) { mi->blend_weights[i] = 0; } mi->blend_weights_buffer = RD::get_singleton()->storage_buffer_create(sizeof(float) * mi->blend_weights.size(), mi->blend_weights.to_byte_array()); mi->weights_dirty = true; } MeshInstance::Surface s; if (mesh->blend_shape_count > 0 || (mesh->surfaces[p_surface]->format & RS::ARRAY_FORMAT_BONES)) { //surface warrants transform s.vertex_buffer = RD::get_singleton()->vertex_buffer_create(mesh->surfaces[p_surface]->vertex_buffer_size, Vector(), true); Vector uniforms; { RD::Uniform u; u.binding = 1; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.append_id(s.vertex_buffer); uniforms.push_back(u); } { RD::Uniform u; u.binding = 2; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; if (mi->blend_weights_buffer.is_valid()) { u.append_id(mi->blend_weights_buffer); } else { u.append_id(default_rd_storage_buffer); } uniforms.push_back(u); } s.uniform_set = RD::get_singleton()->uniform_set_create(uniforms, skeleton_shader.version_shader[0], SkeletonShader::UNIFORM_SET_INSTANCE); } mi->surfaces.push_back(s); mi->dirty = true; } void RendererStorageRD::mesh_instance_check_for_update(RID p_mesh_instance) { MeshInstance *mi = mesh_instance_owner.get_or_null(p_mesh_instance); bool needs_update = mi->dirty; if (mi->weights_dirty && !mi->weight_update_list.in_list()) { dirty_mesh_instance_weights.add(&mi->weight_update_list); needs_update = true; } if (mi->array_update_list.in_list()) { return; } if (!needs_update && mi->skeleton.is_valid()) { Skeleton *sk = skeleton_owner.get_or_null(mi->skeleton); if (sk && sk->version != mi->skeleton_version) { needs_update = true; } } if (needs_update) { dirty_mesh_instance_arrays.add(&mi->array_update_list); } } void RendererStorageRD::update_mesh_instances() { while (dirty_mesh_instance_weights.first()) { MeshInstance *mi = dirty_mesh_instance_weights.first()->self(); if (mi->blend_weights_buffer.is_valid()) { RD::get_singleton()->buffer_update(mi->blend_weights_buffer, 0, mi->blend_weights.size() * sizeof(float), mi->blend_weights.ptr()); } dirty_mesh_instance_weights.remove(&mi->weight_update_list); mi->weights_dirty = false; } if (dirty_mesh_instance_arrays.first() == nullptr) { return; //nothing to do } //process skeletons and blend shapes RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); while (dirty_mesh_instance_arrays.first()) { MeshInstance *mi = dirty_mesh_instance_arrays.first()->self(); Skeleton *sk = skeleton_owner.get_or_null(mi->skeleton); for (uint32_t i = 0; i < mi->surfaces.size(); i++) { if (mi->surfaces[i].uniform_set == RID() || mi->mesh->surfaces[i]->uniform_set == RID()) { continue; } bool array_is_2d = mi->mesh->surfaces[i]->format & RS::ARRAY_FLAG_USE_2D_VERTICES; RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, skeleton_shader.pipeline[array_is_2d ? SkeletonShader::SHADER_MODE_2D : SkeletonShader::SHADER_MODE_3D]); RD::get_singleton()->compute_list_bind_uniform_set(compute_list, mi->surfaces[i].uniform_set, SkeletonShader::UNIFORM_SET_INSTANCE); RD::get_singleton()->compute_list_bind_uniform_set(compute_list, mi->mesh->surfaces[i]->uniform_set, SkeletonShader::UNIFORM_SET_SURFACE); if (sk && sk->uniform_set_mi.is_valid()) { RD::get_singleton()->compute_list_bind_uniform_set(compute_list, sk->uniform_set_mi, SkeletonShader::UNIFORM_SET_SKELETON); } else { RD::get_singleton()->compute_list_bind_uniform_set(compute_list, skeleton_shader.default_skeleton_uniform_set, SkeletonShader::UNIFORM_SET_SKELETON); } SkeletonShader::PushConstant push_constant; push_constant.has_normal = mi->mesh->surfaces[i]->format & RS::ARRAY_FORMAT_NORMAL; push_constant.has_tangent = mi->mesh->surfaces[i]->format & RS::ARRAY_FORMAT_TANGENT; push_constant.has_skeleton = sk != nullptr && sk->use_2d == array_is_2d && (mi->mesh->surfaces[i]->format & RS::ARRAY_FORMAT_BONES); push_constant.has_blend_shape = mi->mesh->blend_shape_count > 0; push_constant.vertex_count = mi->mesh->surfaces[i]->vertex_count; push_constant.vertex_stride = (mi->mesh->surfaces[i]->vertex_buffer_size / mi->mesh->surfaces[i]->vertex_count) / 4; push_constant.skin_stride = (mi->mesh->surfaces[i]->skin_buffer_size / mi->mesh->surfaces[i]->vertex_count) / 4; push_constant.skin_weight_offset = (mi->mesh->surfaces[i]->format & RS::ARRAY_FLAG_USE_8_BONE_WEIGHTS) ? 4 : 2; push_constant.blend_shape_count = mi->mesh->blend_shape_count; push_constant.normalized_blend_shapes = mi->mesh->blend_shape_mode == RS::BLEND_SHAPE_MODE_NORMALIZED; push_constant.pad0 = 0; push_constant.pad1 = 0; RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SkeletonShader::PushConstant)); //dispatch without barrier, so all is done at the same time RD::get_singleton()->compute_list_dispatch_threads(compute_list, push_constant.vertex_count, 1, 1); } mi->dirty = false; if (sk) { mi->skeleton_version = sk->version; } dirty_mesh_instance_arrays.remove(&mi->array_update_list); } RD::get_singleton()->compute_list_end(); } void RendererStorageRD::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::Version &v, Mesh::Surface *s, uint32_t p_input_mask, MeshInstance::Surface *mis) { Vector attributes; Vector buffers; uint32_t stride = 0; uint32_t attribute_stride = 0; uint32_t skin_stride = 0; for (int i = 0; i < RS::ARRAY_INDEX; i++) { RD::VertexAttribute vd; RID buffer; vd.location = i; if (!(s->format & (1 << i))) { // Not supplied by surface, use default value buffer = mesh_default_rd_buffers[i]; vd.stride = 0; switch (i) { case RS::ARRAY_VERTEX: { vd.format = RD::DATA_FORMAT_R32G32B32_SFLOAT; } break; case RS::ARRAY_NORMAL: { vd.format = RD::DATA_FORMAT_R32G32B32_SFLOAT; } break; case RS::ARRAY_TANGENT: { vd.format = RD::DATA_FORMAT_R32G32B32A32_SFLOAT; } break; case RS::ARRAY_COLOR: { vd.format = RD::DATA_FORMAT_R32G32B32A32_SFLOAT; } break; case RS::ARRAY_TEX_UV: { vd.format = RD::DATA_FORMAT_R32G32_SFLOAT; } break; case RS::ARRAY_TEX_UV2: { vd.format = RD::DATA_FORMAT_R32G32_SFLOAT; } break; case RS::ARRAY_CUSTOM0: case RS::ARRAY_CUSTOM1: case RS::ARRAY_CUSTOM2: case RS::ARRAY_CUSTOM3: { //assumed weights too vd.format = RD::DATA_FORMAT_R32G32B32A32_SFLOAT; } break; case RS::ARRAY_BONES: { //assumed weights too vd.format = RD::DATA_FORMAT_R32G32B32A32_UINT; } break; case RS::ARRAY_WEIGHTS: { //assumed weights too vd.format = RD::DATA_FORMAT_R32G32B32A32_SFLOAT; } break; } } else { //Supplied, use it vd.stride = 1; //mark that it needs a stride set (default uses 0) switch (i) { case RS::ARRAY_VERTEX: { vd.offset = stride; if (s->format & RS::ARRAY_FLAG_USE_2D_VERTICES) { vd.format = RD::DATA_FORMAT_R32G32_SFLOAT; stride += sizeof(float) * 2; } else { vd.format = RD::DATA_FORMAT_R32G32B32_SFLOAT; stride += sizeof(float) * 3; } if (mis) { buffer = mis->vertex_buffer; } else { buffer = s->vertex_buffer; } } break; case RS::ARRAY_NORMAL: { vd.offset = stride; vd.format = RD::DATA_FORMAT_A2B10G10R10_UNORM_PACK32; stride += sizeof(uint32_t); if (mis) { buffer = mis->vertex_buffer; } else { buffer = s->vertex_buffer; } } break; case RS::ARRAY_TANGENT: { vd.offset = stride; vd.format = RD::DATA_FORMAT_A2B10G10R10_UNORM_PACK32; stride += sizeof(uint32_t); if (mis) { buffer = mis->vertex_buffer; } else { buffer = s->vertex_buffer; } } break; case RS::ARRAY_COLOR: { vd.offset = attribute_stride; vd.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; attribute_stride += sizeof(int8_t) * 4; buffer = s->attribute_buffer; } break; case RS::ARRAY_TEX_UV: { vd.offset = attribute_stride; vd.format = RD::DATA_FORMAT_R32G32_SFLOAT; attribute_stride += sizeof(float) * 2; buffer = s->attribute_buffer; } break; case RS::ARRAY_TEX_UV2: { vd.offset = attribute_stride; vd.format = RD::DATA_FORMAT_R32G32_SFLOAT; attribute_stride += sizeof(float) * 2; buffer = s->attribute_buffer; } break; case RS::ARRAY_CUSTOM0: case RS::ARRAY_CUSTOM1: case RS::ARRAY_CUSTOM2: case RS::ARRAY_CUSTOM3: { vd.offset = attribute_stride; int idx = i - RS::ARRAY_CUSTOM0; uint32_t fmt_shift[RS::ARRAY_CUSTOM_COUNT] = { RS::ARRAY_FORMAT_CUSTOM0_SHIFT, RS::ARRAY_FORMAT_CUSTOM1_SHIFT, RS::ARRAY_FORMAT_CUSTOM2_SHIFT, RS::ARRAY_FORMAT_CUSTOM3_SHIFT }; uint32_t fmt = (s->format >> fmt_shift[idx]) & RS::ARRAY_FORMAT_CUSTOM_MASK; uint32_t fmtsize[RS::ARRAY_CUSTOM_MAX] = { 4, 4, 4, 8, 4, 8, 12, 16 }; RD::DataFormat fmtrd[RS::ARRAY_CUSTOM_MAX] = { RD::DATA_FORMAT_R8G8B8A8_UNORM, RD::DATA_FORMAT_R8G8B8A8_SNORM, RD::DATA_FORMAT_R16G16_SFLOAT, RD::DATA_FORMAT_R16G16B16A16_SFLOAT, RD::DATA_FORMAT_R32_SFLOAT, RD::DATA_FORMAT_R32G32_SFLOAT, RD::DATA_FORMAT_R32G32B32_SFLOAT, RD::DATA_FORMAT_R32G32B32A32_SFLOAT }; vd.format = fmtrd[fmt]; attribute_stride += fmtsize[fmt]; buffer = s->attribute_buffer; } break; case RS::ARRAY_BONES: { vd.offset = skin_stride; vd.format = RD::DATA_FORMAT_R16G16B16A16_UINT; skin_stride += sizeof(int16_t) * 4; buffer = s->skin_buffer; } break; case RS::ARRAY_WEIGHTS: { vd.offset = skin_stride; vd.format = RD::DATA_FORMAT_R16G16B16A16_UNORM; skin_stride += sizeof(int16_t) * 4; buffer = s->skin_buffer; } break; } } if (!(p_input_mask & (1 << i))) { continue; // Shader does not need this, skip it (but computing stride was important anyway) } attributes.push_back(vd); buffers.push_back(buffer); } //update final stride for (int i = 0; i < attributes.size(); i++) { if (attributes[i].stride == 0) { continue; //default location } int loc = attributes[i].location; if (loc < RS::ARRAY_COLOR) { attributes.write[i].stride = stride; } else if (loc < RS::ARRAY_BONES) { attributes.write[i].stride = attribute_stride; } else { attributes.write[i].stride = skin_stride; } } v.input_mask = p_input_mask; v.vertex_format = RD::get_singleton()->vertex_format_create(attributes); v.vertex_array = RD::get_singleton()->vertex_array_create(s->vertex_count, v.vertex_format, buffers); } ////////////////// MULTIMESH RID RendererStorageRD::multimesh_allocate() { return multimesh_owner.allocate_rid(); } void RendererStorageRD::multimesh_initialize(RID p_rid) { multimesh_owner.initialize_rid(p_rid, MultiMesh()); } void RendererStorageRD::multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors, bool p_use_custom_data) { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_COND(!multimesh); if (multimesh->instances == p_instances && multimesh->xform_format == p_transform_format && multimesh->uses_colors == p_use_colors && multimesh->uses_custom_data == p_use_custom_data) { return; } if (multimesh->buffer.is_valid()) { RD::get_singleton()->free(multimesh->buffer); multimesh->buffer = RID(); multimesh->uniform_set_2d = RID(); //cleared by dependency multimesh->uniform_set_3d = RID(); //cleared by dependency } if (multimesh->data_cache_dirty_regions) { memdelete_arr(multimesh->data_cache_dirty_regions); multimesh->data_cache_dirty_regions = nullptr; multimesh->data_cache_used_dirty_regions = 0; } multimesh->instances = p_instances; multimesh->xform_format = p_transform_format; multimesh->uses_colors = p_use_colors; multimesh->color_offset_cache = p_transform_format == RS::MULTIMESH_TRANSFORM_2D ? 8 : 12; multimesh->uses_custom_data = p_use_custom_data; multimesh->custom_data_offset_cache = multimesh->color_offset_cache + (p_use_colors ? 4 : 0); multimesh->stride_cache = multimesh->custom_data_offset_cache + (p_use_custom_data ? 4 : 0); multimesh->buffer_set = false; //print_line("allocate, elements: " + itos(p_instances) + " 2D: " + itos(p_transform_format == RS::MULTIMESH_TRANSFORM_2D) + " colors " + itos(multimesh->uses_colors) + " data " + itos(multimesh->uses_custom_data) + " stride " + itos(multimesh->stride_cache) + " total size " + itos(multimesh->stride_cache * multimesh->instances)); multimesh->data_cache = Vector(); multimesh->aabb = AABB(); multimesh->aabb_dirty = false; multimesh->visible_instances = MIN(multimesh->visible_instances, multimesh->instances); if (multimesh->instances) { multimesh->buffer = RD::get_singleton()->storage_buffer_create(multimesh->instances * multimesh->stride_cache * 4); } multimesh->dependency.changed_notify(DEPENDENCY_CHANGED_MULTIMESH); } int RendererStorageRD::multimesh_get_instance_count(RID p_multimesh) const { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_COND_V(!multimesh, 0); return multimesh->instances; } void RendererStorageRD::multimesh_set_mesh(RID p_multimesh, RID p_mesh) { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_COND(!multimesh); if (multimesh->mesh == p_mesh) { return; } multimesh->mesh = p_mesh; if (multimesh->instances == 0) { return; } if (multimesh->data_cache.size()) { //we have a data cache, just mark it dirt _multimesh_mark_all_dirty(multimesh, false, true); } else if (multimesh->instances) { //need to re-create AABB unfortunately, calling this has a penalty if (multimesh->buffer_set) { Vector buffer = RD::get_singleton()->buffer_get_data(multimesh->buffer); const uint8_t *r = buffer.ptr(); const float *data = (const float *)r; _multimesh_re_create_aabb(multimesh, data, multimesh->instances); } } multimesh->dependency.changed_notify(DEPENDENCY_CHANGED_MESH); } #define MULTIMESH_DIRTY_REGION_SIZE 512 void RendererStorageRD::_multimesh_make_local(MultiMesh *multimesh) const { if (multimesh->data_cache.size() > 0) { return; //already local } ERR_FAIL_COND(multimesh->data_cache.size() > 0); // this means that the user wants to load/save individual elements, // for this, the data must reside on CPU, so just copy it there. multimesh->data_cache.resize(multimesh->instances * multimesh->stride_cache); { float *w = multimesh->data_cache.ptrw(); if (multimesh->buffer_set) { Vector buffer = RD::get_singleton()->buffer_get_data(multimesh->buffer); { const uint8_t *r = buffer.ptr(); memcpy(w, r, buffer.size()); } } else { memset(w, 0, (size_t)multimesh->instances * multimesh->stride_cache * sizeof(float)); } } uint32_t data_cache_dirty_region_count = (multimesh->instances - 1) / MULTIMESH_DIRTY_REGION_SIZE + 1; multimesh->data_cache_dirty_regions = memnew_arr(bool, data_cache_dirty_region_count); for (uint32_t i = 0; i < data_cache_dirty_region_count; i++) { multimesh->data_cache_dirty_regions[i] = false; } multimesh->data_cache_used_dirty_regions = 0; } void RendererStorageRD::_multimesh_mark_dirty(MultiMesh *multimesh, int p_index, bool p_aabb) { uint32_t region_index = p_index / MULTIMESH_DIRTY_REGION_SIZE; #ifdef DEBUG_ENABLED uint32_t data_cache_dirty_region_count = (multimesh->instances - 1) / MULTIMESH_DIRTY_REGION_SIZE + 1; ERR_FAIL_UNSIGNED_INDEX(region_index, data_cache_dirty_region_count); //bug #endif if (!multimesh->data_cache_dirty_regions[region_index]) { multimesh->data_cache_dirty_regions[region_index] = true; multimesh->data_cache_used_dirty_regions++; } if (p_aabb) { multimesh->aabb_dirty = true; } if (!multimesh->dirty) { multimesh->dirty_list = multimesh_dirty_list; multimesh_dirty_list = multimesh; multimesh->dirty = true; } } void RendererStorageRD::_multimesh_mark_all_dirty(MultiMesh *multimesh, bool p_data, bool p_aabb) { if (p_data) { uint32_t data_cache_dirty_region_count = (multimesh->instances - 1) / MULTIMESH_DIRTY_REGION_SIZE + 1; for (uint32_t i = 0; i < data_cache_dirty_region_count; i++) { if (!multimesh->data_cache_dirty_regions[i]) { multimesh->data_cache_dirty_regions[i] = true; multimesh->data_cache_used_dirty_regions++; } } } if (p_aabb) { multimesh->aabb_dirty = true; } if (!multimesh->dirty) { multimesh->dirty_list = multimesh_dirty_list; multimesh_dirty_list = multimesh; multimesh->dirty = true; } } void RendererStorageRD::_multimesh_re_create_aabb(MultiMesh *multimesh, const float *p_data, int p_instances) { ERR_FAIL_COND(multimesh->mesh.is_null()); AABB aabb; AABB mesh_aabb = mesh_get_aabb(multimesh->mesh); for (int i = 0; i < p_instances; i++) { const float *data = p_data + multimesh->stride_cache * i; Transform3D t; if (multimesh->xform_format == RS::MULTIMESH_TRANSFORM_3D) { t.basis.elements[0][0] = data[0]; t.basis.elements[0][1] = data[1]; t.basis.elements[0][2] = data[2]; t.origin.x = data[3]; t.basis.elements[1][0] = data[4]; t.basis.elements[1][1] = data[5]; t.basis.elements[1][2] = data[6]; t.origin.y = data[7]; t.basis.elements[2][0] = data[8]; t.basis.elements[2][1] = data[9]; t.basis.elements[2][2] = data[10]; t.origin.z = data[11]; } else { t.basis.elements[0].x = data[0]; t.basis.elements[1].x = data[1]; t.origin.x = data[3]; t.basis.elements[0].y = data[4]; t.basis.elements[1].y = data[5]; t.origin.y = data[7]; } if (i == 0) { aabb = t.xform(mesh_aabb); } else { aabb.merge_with(t.xform(mesh_aabb)); } } multimesh->aabb = aabb; } void RendererStorageRD::multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform) { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_COND(!multimesh); ERR_FAIL_INDEX(p_index, multimesh->instances); ERR_FAIL_COND(multimesh->xform_format != RS::MULTIMESH_TRANSFORM_3D); _multimesh_make_local(multimesh); { float *w = multimesh->data_cache.ptrw(); float *dataptr = w + p_index * multimesh->stride_cache; dataptr[0] = p_transform.basis.elements[0][0]; dataptr[1] = p_transform.basis.elements[0][1]; dataptr[2] = p_transform.basis.elements[0][2]; dataptr[3] = p_transform.origin.x; dataptr[4] = p_transform.basis.elements[1][0]; dataptr[5] = p_transform.basis.elements[1][1]; dataptr[6] = p_transform.basis.elements[1][2]; dataptr[7] = p_transform.origin.y; dataptr[8] = p_transform.basis.elements[2][0]; dataptr[9] = p_transform.basis.elements[2][1]; dataptr[10] = p_transform.basis.elements[2][2]; dataptr[11] = p_transform.origin.z; } _multimesh_mark_dirty(multimesh, p_index, true); } void RendererStorageRD::multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_COND(!multimesh); ERR_FAIL_INDEX(p_index, multimesh->instances); ERR_FAIL_COND(multimesh->xform_format != RS::MULTIMESH_TRANSFORM_2D); _multimesh_make_local(multimesh); { float *w = multimesh->data_cache.ptrw(); float *dataptr = w + p_index * multimesh->stride_cache; dataptr[0] = p_transform.elements[0][0]; dataptr[1] = p_transform.elements[1][0]; dataptr[2] = 0; dataptr[3] = p_transform.elements[2][0]; dataptr[4] = p_transform.elements[0][1]; dataptr[5] = p_transform.elements[1][1]; dataptr[6] = 0; dataptr[7] = p_transform.elements[2][1]; } _multimesh_mark_dirty(multimesh, p_index, true); } void RendererStorageRD::multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_COND(!multimesh); ERR_FAIL_INDEX(p_index, multimesh->instances); ERR_FAIL_COND(!multimesh->uses_colors); _multimesh_make_local(multimesh); { float *w = multimesh->data_cache.ptrw(); float *dataptr = w + p_index * multimesh->stride_cache + multimesh->color_offset_cache; dataptr[0] = p_color.r; dataptr[1] = p_color.g; dataptr[2] = p_color.b; dataptr[3] = p_color.a; } _multimesh_mark_dirty(multimesh, p_index, false); } void RendererStorageRD::multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_COND(!multimesh); ERR_FAIL_INDEX(p_index, multimesh->instances); ERR_FAIL_COND(!multimesh->uses_custom_data); _multimesh_make_local(multimesh); { float *w = multimesh->data_cache.ptrw(); float *dataptr = w + p_index * multimesh->stride_cache + multimesh->custom_data_offset_cache; dataptr[0] = p_color.r; dataptr[1] = p_color.g; dataptr[2] = p_color.b; dataptr[3] = p_color.a; } _multimesh_mark_dirty(multimesh, p_index, false); } RID RendererStorageRD::multimesh_get_mesh(RID p_multimesh) const { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_COND_V(!multimesh, RID()); return multimesh->mesh; } Transform3D RendererStorageRD::multimesh_instance_get_transform(RID p_multimesh, int p_index) const { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_COND_V(!multimesh, Transform3D()); ERR_FAIL_INDEX_V(p_index, multimesh->instances, Transform3D()); ERR_FAIL_COND_V(multimesh->xform_format != RS::MULTIMESH_TRANSFORM_3D, Transform3D()); _multimesh_make_local(multimesh); Transform3D t; { const float *r = multimesh->data_cache.ptr(); const float *dataptr = r + p_index * multimesh->stride_cache; t.basis.elements[0][0] = dataptr[0]; t.basis.elements[0][1] = dataptr[1]; t.basis.elements[0][2] = dataptr[2]; t.origin.x = dataptr[3]; t.basis.elements[1][0] = dataptr[4]; t.basis.elements[1][1] = dataptr[5]; t.basis.elements[1][2] = dataptr[6]; t.origin.y = dataptr[7]; t.basis.elements[2][0] = dataptr[8]; t.basis.elements[2][1] = dataptr[9]; t.basis.elements[2][2] = dataptr[10]; t.origin.z = dataptr[11]; } return t; } Transform2D RendererStorageRD::multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_COND_V(!multimesh, Transform2D()); ERR_FAIL_INDEX_V(p_index, multimesh->instances, Transform2D()); ERR_FAIL_COND_V(multimesh->xform_format != RS::MULTIMESH_TRANSFORM_2D, Transform2D()); _multimesh_make_local(multimesh); Transform2D t; { const float *r = multimesh->data_cache.ptr(); const float *dataptr = r + p_index * multimesh->stride_cache; t.elements[0][0] = dataptr[0]; t.elements[1][0] = dataptr[1]; t.elements[2][0] = dataptr[3]; t.elements[0][1] = dataptr[4]; t.elements[1][1] = dataptr[5]; t.elements[2][1] = dataptr[7]; } return t; } Color RendererStorageRD::multimesh_instance_get_color(RID p_multimesh, int p_index) const { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_COND_V(!multimesh, Color()); ERR_FAIL_INDEX_V(p_index, multimesh->instances, Color()); ERR_FAIL_COND_V(!multimesh->uses_colors, Color()); _multimesh_make_local(multimesh); Color c; { const float *r = multimesh->data_cache.ptr(); const float *dataptr = r + p_index * multimesh->stride_cache + multimesh->color_offset_cache; c.r = dataptr[0]; c.g = dataptr[1]; c.b = dataptr[2]; c.a = dataptr[3]; } return c; } Color RendererStorageRD::multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_COND_V(!multimesh, Color()); ERR_FAIL_INDEX_V(p_index, multimesh->instances, Color()); ERR_FAIL_COND_V(!multimesh->uses_custom_data, Color()); _multimesh_make_local(multimesh); Color c; { const float *r = multimesh->data_cache.ptr(); const float *dataptr = r + p_index * multimesh->stride_cache + multimesh->custom_data_offset_cache; c.r = dataptr[0]; c.g = dataptr[1]; c.b = dataptr[2]; c.a = dataptr[3]; } return c; } void RendererStorageRD::multimesh_set_buffer(RID p_multimesh, const Vector &p_buffer) { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_COND(!multimesh); ERR_FAIL_COND(p_buffer.size() != (multimesh->instances * (int)multimesh->stride_cache)); { const float *r = p_buffer.ptr(); RD::get_singleton()->buffer_update(multimesh->buffer, 0, p_buffer.size() * sizeof(float), r); multimesh->buffer_set = true; } if (multimesh->data_cache.size()) { //if we have a data cache, just update it multimesh->data_cache = p_buffer; { //clear dirty since nothing will be dirty anymore uint32_t data_cache_dirty_region_count = (multimesh->instances - 1) / MULTIMESH_DIRTY_REGION_SIZE + 1; for (uint32_t i = 0; i < data_cache_dirty_region_count; i++) { multimesh->data_cache_dirty_regions[i] = false; } multimesh->data_cache_used_dirty_regions = 0; } _multimesh_mark_all_dirty(multimesh, false, true); //update AABB } else if (multimesh->mesh.is_valid()) { //if we have a mesh set, we need to re-generate the AABB from the new data const float *data = p_buffer.ptr(); _multimesh_re_create_aabb(multimesh, data, multimesh->instances); multimesh->dependency.changed_notify(DEPENDENCY_CHANGED_AABB); } } Vector RendererStorageRD::multimesh_get_buffer(RID p_multimesh) const { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_COND_V(!multimesh, Vector()); if (multimesh->buffer.is_null()) { return Vector(); } else if (multimesh->data_cache.size()) { return multimesh->data_cache; } else { //get from memory Vector buffer = RD::get_singleton()->buffer_get_data(multimesh->buffer); Vector ret; ret.resize(multimesh->instances * multimesh->stride_cache); { float *w = ret.ptrw(); const uint8_t *r = buffer.ptr(); memcpy(w, r, buffer.size()); } return ret; } } void RendererStorageRD::multimesh_set_visible_instances(RID p_multimesh, int p_visible) { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_COND(!multimesh); ERR_FAIL_COND(p_visible < -1 || p_visible > multimesh->instances); if (multimesh->visible_instances == p_visible) { return; } if (multimesh->data_cache.size()) { //there is a data cache.. _multimesh_mark_all_dirty(multimesh, false, true); } multimesh->visible_instances = p_visible; multimesh->dependency.changed_notify(DEPENDENCY_CHANGED_MULTIMESH_VISIBLE_INSTANCES); } int RendererStorageRD::multimesh_get_visible_instances(RID p_multimesh) const { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_COND_V(!multimesh, 0); return multimesh->visible_instances; } AABB RendererStorageRD::multimesh_get_aabb(RID p_multimesh) const { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_COND_V(!multimesh, AABB()); if (multimesh->aabb_dirty) { const_cast(this)->_update_dirty_multimeshes(); } return multimesh->aabb; } void RendererStorageRD::_update_dirty_multimeshes() { while (multimesh_dirty_list) { MultiMesh *multimesh = multimesh_dirty_list; if (multimesh->data_cache.size()) { //may have been cleared, so only process if it exists const float *data = multimesh->data_cache.ptr(); uint32_t visible_instances = multimesh->visible_instances >= 0 ? multimesh->visible_instances : multimesh->instances; if (multimesh->data_cache_used_dirty_regions) { uint32_t data_cache_dirty_region_count = (multimesh->instances - 1) / MULTIMESH_DIRTY_REGION_SIZE + 1; uint32_t visible_region_count = visible_instances == 0 ? 0 : (visible_instances - 1) / MULTIMESH_DIRTY_REGION_SIZE + 1; uint32_t region_size = multimesh->stride_cache * MULTIMESH_DIRTY_REGION_SIZE * sizeof(float); if (multimesh->data_cache_used_dirty_regions > 32 || multimesh->data_cache_used_dirty_regions > visible_region_count / 2) { //if there too many dirty regions, or represent the majority of regions, just copy all, else transfer cost piles up too much RD::get_singleton()->buffer_update(multimesh->buffer, 0, MIN(visible_region_count * region_size, multimesh->instances * (uint32_t)multimesh->stride_cache * (uint32_t)sizeof(float)), data); } else { //not that many regions? update them all for (uint32_t i = 0; i < visible_region_count; i++) { if (multimesh->data_cache_dirty_regions[i]) { uint32_t offset = i * region_size; uint32_t size = multimesh->stride_cache * (uint32_t)multimesh->instances * (uint32_t)sizeof(float); uint32_t region_start_index = multimesh->stride_cache * MULTIMESH_DIRTY_REGION_SIZE * i; RD::get_singleton()->buffer_update(multimesh->buffer, offset, MIN(region_size, size - offset), &data[region_start_index]); } } } for (uint32_t i = 0; i < data_cache_dirty_region_count; i++) { multimesh->data_cache_dirty_regions[i] = false; } multimesh->data_cache_used_dirty_regions = 0; } if (multimesh->aabb_dirty) { //aabb is dirty.. _multimesh_re_create_aabb(multimesh, data, visible_instances); multimesh->aabb_dirty = false; multimesh->dependency.changed_notify(DEPENDENCY_CHANGED_AABB); } } multimesh_dirty_list = multimesh->dirty_list; multimesh->dirty_list = nullptr; multimesh->dirty = false; } multimesh_dirty_list = nullptr; } /* PARTICLES */ RID RendererStorageRD::particles_allocate() { return particles_owner.allocate_rid(); } void RendererStorageRD::particles_initialize(RID p_rid) { particles_owner.initialize_rid(p_rid, Particles()); } void RendererStorageRD::particles_set_mode(RID p_particles, RS::ParticlesMode p_mode) { Particles *particles = particles_owner.get_or_null(p_particles); ERR_FAIL_COND(!particles); if (particles->mode == p_mode) { return; } _particles_free_data(particles); particles->mode = p_mode; } void RendererStorageRD::particles_set_emitting(RID p_particles, bool p_emitting) { Particles *particles = particles_owner.get_or_null(p_particles); ERR_FAIL_COND(!particles); particles->emitting = p_emitting; } bool RendererStorageRD::particles_get_emitting(RID p_particles) { ERR_FAIL_COND_V_MSG(RSG::threaded, false, "This function should never be used with threaded rendering, as it stalls the renderer."); Particles *particles = particles_owner.get_or_null(p_particles); ERR_FAIL_COND_V(!particles, false); return particles->emitting; } void RendererStorageRD::_particles_free_data(Particles *particles) { if (particles->particle_buffer.is_valid()) { RD::get_singleton()->free(particles->particle_buffer); particles->particle_buffer = RID(); RD::get_singleton()->free(particles->particle_instance_buffer); particles->particle_instance_buffer = RID(); } particles->userdata_count = 0; if (particles->frame_params_buffer.is_valid()) { RD::get_singleton()->free(particles->frame_params_buffer); particles->frame_params_buffer = RID(); } particles->particles_transforms_buffer_uniform_set = RID(); if (RD::get_singleton()->uniform_set_is_valid(particles->trail_bind_pose_uniform_set)) { RD::get_singleton()->free(particles->trail_bind_pose_uniform_set); } particles->trail_bind_pose_uniform_set = RID(); if (particles->trail_bind_pose_buffer.is_valid()) { RD::get_singleton()->free(particles->trail_bind_pose_buffer); particles->trail_bind_pose_buffer = RID(); } if (RD::get_singleton()->uniform_set_is_valid(particles->collision_textures_uniform_set)) { RD::get_singleton()->free(particles->collision_textures_uniform_set); } particles->collision_textures_uniform_set = RID(); if (particles->particles_sort_buffer.is_valid()) { RD::get_singleton()->free(particles->particles_sort_buffer); particles->particles_sort_buffer = RID(); particles->particles_sort_uniform_set = RID(); } if (particles->emission_buffer != nullptr) { particles->emission_buffer = nullptr; particles->emission_buffer_data.clear(); RD::get_singleton()->free(particles->emission_storage_buffer); particles->emission_storage_buffer = RID(); } if (RD::get_singleton()->uniform_set_is_valid(particles->particles_material_uniform_set)) { //will need to be re-created RD::get_singleton()->free(particles->particles_material_uniform_set); } particles->particles_material_uniform_set = RID(); } void RendererStorageRD::particles_set_amount(RID p_particles, int p_amount) { Particles *particles = particles_owner.get_or_null(p_particles); ERR_FAIL_COND(!particles); if (particles->amount == p_amount) { return; } _particles_free_data(particles); particles->amount = p_amount; particles->prev_ticks = 0; particles->phase = 0; particles->prev_phase = 0; particles->clear = true; particles->dependency.changed_notify(DEPENDENCY_CHANGED_PARTICLES); } void RendererStorageRD::particles_set_lifetime(RID p_particles, double p_lifetime) { Particles *particles = particles_owner.get_or_null(p_particles); ERR_FAIL_COND(!particles); particles->lifetime = p_lifetime; } void RendererStorageRD::particles_set_one_shot(RID p_particles, bool p_one_shot) { Particles *particles = particles_owner.get_or_null(p_particles); ERR_FAIL_COND(!particles); particles->one_shot = p_one_shot; } void RendererStorageRD::particles_set_pre_process_time(RID p_particles, double p_time) { Particles *particles = particles_owner.get_or_null(p_particles); ERR_FAIL_COND(!particles); particles->pre_process_time = p_time; } void RendererStorageRD::particles_set_explosiveness_ratio(RID p_particles, real_t p_ratio) { Particles *particles = particles_owner.get_or_null(p_particles); ERR_FAIL_COND(!particles); particles->explosiveness = p_ratio; } void RendererStorageRD::particles_set_randomness_ratio(RID p_particles, real_t p_ratio) { Particles *particles = particles_owner.get_or_null(p_particles); ERR_FAIL_COND(!particles); particles->randomness = p_ratio; } void RendererStorageRD::particles_set_custom_aabb(RID p_particles, const AABB &p_aabb) { Particles *particles = particles_owner.get_or_null(p_particles); ERR_FAIL_COND(!particles); particles->custom_aabb = p_aabb; particles->dependency.changed_notify(DEPENDENCY_CHANGED_AABB); } void RendererStorageRD::particles_set_speed_scale(RID p_particles, double p_scale) { Particles *particles = particles_owner.get_or_null(p_particles); ERR_FAIL_COND(!particles); particles->speed_scale = p_scale; } void RendererStorageRD::particles_set_use_local_coordinates(RID p_particles, bool p_enable) { Particles *particles = particles_owner.get_or_null(p_particles); ERR_FAIL_COND(!particles); particles->use_local_coords = p_enable; particles->dependency.changed_notify(DEPENDENCY_CHANGED_PARTICLES); } void RendererStorageRD::particles_set_fixed_fps(RID p_particles, int p_fps) { Particles *particles = particles_owner.get_or_null(p_particles); ERR_FAIL_COND(!particles); particles->fixed_fps = p_fps; _particles_free_data(particles); particles->prev_ticks = 0; particles->phase = 0; particles->prev_phase = 0; particles->clear = true; particles->dependency.changed_notify(DEPENDENCY_CHANGED_PARTICLES); } void RendererStorageRD::particles_set_interpolate(RID p_particles, bool p_enable) { Particles *particles = particles_owner.get_or_null(p_particles); ERR_FAIL_COND(!particles); particles->interpolate = p_enable; } void RendererStorageRD::particles_set_fractional_delta(RID p_particles, bool p_enable) { Particles *particles = particles_owner.get_or_null(p_particles); ERR_FAIL_COND(!particles); particles->fractional_delta = p_enable; } void RendererStorageRD::particles_set_trails(RID p_particles, bool p_enable, double p_length) { Particles *particles = particles_owner.get_or_null(p_particles); ERR_FAIL_COND(!particles); ERR_FAIL_COND(p_length < 0.1); p_length = MIN(10.0, p_length); particles->trails_enabled = p_enable; particles->trail_length = p_length; _particles_free_data(particles); particles->prev_ticks = 0; particles->phase = 0; particles->prev_phase = 0; particles->clear = true; particles->dependency.changed_notify(DEPENDENCY_CHANGED_PARTICLES); } void RendererStorageRD::particles_set_trail_bind_poses(RID p_particles, const Vector &p_bind_poses) { Particles *particles = particles_owner.get_or_null(p_particles); ERR_FAIL_COND(!particles); if (particles->trail_bind_pose_buffer.is_valid() && particles->trail_bind_poses.size() != p_bind_poses.size()) { _particles_free_data(particles); particles->prev_ticks = 0; particles->phase = 0; particles->prev_phase = 0; particles->clear = true; } particles->trail_bind_poses = p_bind_poses; particles->trail_bind_poses_dirty = true; particles->dependency.changed_notify(DEPENDENCY_CHANGED_PARTICLES); } void RendererStorageRD::particles_set_collision_base_size(RID p_particles, real_t p_size) { Particles *particles = particles_owner.get_or_null(p_particles); ERR_FAIL_COND(!particles); particles->collision_base_size = p_size; } void RendererStorageRD::particles_set_transform_align(RID p_particles, RS::ParticlesTransformAlign p_transform_align) { Particles *particles = particles_owner.get_or_null(p_particles); ERR_FAIL_COND(!particles); particles->transform_align = p_transform_align; } void RendererStorageRD::particles_set_process_material(RID p_particles, RID p_material) { Particles *particles = particles_owner.get_or_null(p_particles); ERR_FAIL_COND(!particles); particles->process_material = p_material; particles->dependency.changed_notify(DEPENDENCY_CHANGED_PARTICLES); //the instance buffer may have changed } RID RendererStorageRD::particles_get_process_material(RID p_particles) const { Particles *particles = particles_owner.get_or_null(p_particles); ERR_FAIL_COND_V(!particles, RID()); return particles->process_material; } void RendererStorageRD::particles_set_draw_order(RID p_particles, RS::ParticlesDrawOrder p_order) { Particles *particles = particles_owner.get_or_null(p_particles); ERR_FAIL_COND(!particles); particles->draw_order = p_order; } void RendererStorageRD::particles_set_draw_passes(RID p_particles, int p_passes) { Particles *particles = particles_owner.get_or_null(p_particles); ERR_FAIL_COND(!particles); particles->draw_passes.resize(p_passes); } void RendererStorageRD::particles_set_draw_pass_mesh(RID p_particles, int p_pass, RID p_mesh) { Particles *particles = particles_owner.get_or_null(p_particles); ERR_FAIL_COND(!particles); ERR_FAIL_INDEX(p_pass, particles->draw_passes.size()); particles->draw_passes.write[p_pass] = p_mesh; } void RendererStorageRD::particles_restart(RID p_particles) { Particles *particles = particles_owner.get_or_null(p_particles); ERR_FAIL_COND(!particles); particles->restart_request = true; } void RendererStorageRD::_particles_allocate_emission_buffer(Particles *particles) { ERR_FAIL_COND(particles->emission_buffer != nullptr); particles->emission_buffer_data.resize(sizeof(ParticleEmissionBuffer::Data) * particles->amount + sizeof(uint32_t) * 4); memset(particles->emission_buffer_data.ptrw(), 0, particles->emission_buffer_data.size()); particles->emission_buffer = (ParticleEmissionBuffer *)particles->emission_buffer_data.ptrw(); particles->emission_buffer->particle_max = particles->amount; particles->emission_storage_buffer = RD::get_singleton()->storage_buffer_create(particles->emission_buffer_data.size(), particles->emission_buffer_data); if (RD::get_singleton()->uniform_set_is_valid(particles->particles_material_uniform_set)) { //will need to be re-created RD::get_singleton()->free(particles->particles_material_uniform_set); particles->particles_material_uniform_set = RID(); } } void RendererStorageRD::particles_set_subemitter(RID p_particles, RID p_subemitter_particles) { Particles *particles = particles_owner.get_or_null(p_particles); ERR_FAIL_COND(!particles); ERR_FAIL_COND(p_particles == p_subemitter_particles); particles->sub_emitter = p_subemitter_particles; if (RD::get_singleton()->uniform_set_is_valid(particles->particles_material_uniform_set)) { RD::get_singleton()->free(particles->particles_material_uniform_set); particles->particles_material_uniform_set = RID(); //clear and force to re create sub emitting } } void RendererStorageRD::particles_emit(RID p_particles, const Transform3D &p_transform, const Vector3 &p_velocity, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags) { Particles *particles = particles_owner.get_or_null(p_particles); ERR_FAIL_COND(!particles); ERR_FAIL_COND(particles->amount == 0); if (particles->emitting) { particles->clear = true; particles->emitting = false; } if (particles->emission_buffer == nullptr) { _particles_allocate_emission_buffer(particles); } if (particles->inactive) { //in case it was inactive, make active again particles->inactive = false; particles->inactive_time = 0; } int32_t idx = particles->emission_buffer->particle_count; if (idx < particles->emission_buffer->particle_max) { store_transform(p_transform, particles->emission_buffer->data[idx].xform); particles->emission_buffer->data[idx].velocity[0] = p_velocity.x; particles->emission_buffer->data[idx].velocity[1] = p_velocity.y; particles->emission_buffer->data[idx].velocity[2] = p_velocity.z; particles->emission_buffer->data[idx].custom[0] = p_custom.r; particles->emission_buffer->data[idx].custom[1] = p_custom.g; particles->emission_buffer->data[idx].custom[2] = p_custom.b; particles->emission_buffer->data[idx].custom[3] = p_custom.a; particles->emission_buffer->data[idx].color[0] = p_color.r; particles->emission_buffer->data[idx].color[1] = p_color.g; particles->emission_buffer->data[idx].color[2] = p_color.b; particles->emission_buffer->data[idx].color[3] = p_color.a; particles->emission_buffer->data[idx].flags = p_emit_flags; particles->emission_buffer->particle_count++; } } void RendererStorageRD::particles_request_process(RID p_particles) { Particles *particles = particles_owner.get_or_null(p_particles); ERR_FAIL_COND(!particles); if (!particles->dirty) { particles->dirty = true; particles->update_list = particle_update_list; particle_update_list = particles; } } AABB RendererStorageRD::particles_get_current_aabb(RID p_particles) { if (RSG::threaded) { WARN_PRINT_ONCE("Calling this function with threaded rendering enabled stalls the renderer, use with care."); } const Particles *particles = particles_owner.get_or_null(p_particles); ERR_FAIL_COND_V(!particles, AABB()); int total_amount = particles->amount; if (particles->trails_enabled && particles->trail_bind_poses.size() > 1) { total_amount *= particles->trail_bind_poses.size(); } Vector buffer = RD::get_singleton()->buffer_get_data(particles->particle_buffer); ERR_FAIL_COND_V(buffer.size() != (int)(total_amount * sizeof(ParticleData)), AABB()); Transform3D inv = particles->emission_transform.affine_inverse(); AABB aabb; if (buffer.size()) { bool first = true; const uint8_t *data_ptr = (const uint8_t *)buffer.ptr(); uint32_t particle_data_size = sizeof(ParticleData) + sizeof(float) * particles->userdata_count; for (int i = 0; i < total_amount; i++) { const ParticleData &particle_data = *(const ParticleData *)&data_ptr[particle_data_size * i]; if (particle_data.active) { Vector3 pos = Vector3(particle_data.xform[12], particle_data.xform[13], particle_data.xform[14]); if (!particles->use_local_coords) { pos = inv.xform(pos); } if (first) { aabb.position = pos; first = false; } else { aabb.expand_to(pos); } } } } float longest_axis_size = 0; for (int i = 0; i < particles->draw_passes.size(); i++) { if (particles->draw_passes[i].is_valid()) { AABB maabb = mesh_get_aabb(particles->draw_passes[i], RID()); longest_axis_size = MAX(maabb.get_longest_axis_size(), longest_axis_size); } } aabb.grow_by(longest_axis_size); return aabb; } AABB RendererStorageRD::particles_get_aabb(RID p_particles) const { const Particles *particles = particles_owner.get_or_null(p_particles); ERR_FAIL_COND_V(!particles, AABB()); return particles->custom_aabb; } void RendererStorageRD::particles_set_emission_transform(RID p_particles, const Transform3D &p_transform) { Particles *particles = particles_owner.get_or_null(p_particles); ERR_FAIL_COND(!particles); particles->emission_transform = p_transform; } int RendererStorageRD::particles_get_draw_passes(RID p_particles) const { const Particles *particles = particles_owner.get_or_null(p_particles); ERR_FAIL_COND_V(!particles, 0); return particles->draw_passes.size(); } RID RendererStorageRD::particles_get_draw_pass_mesh(RID p_particles, int p_pass) const { const Particles *particles = particles_owner.get_or_null(p_particles); ERR_FAIL_COND_V(!particles, RID()); ERR_FAIL_INDEX_V(p_pass, particles->draw_passes.size(), RID()); return particles->draw_passes[p_pass]; } void RendererStorageRD::particles_add_collision(RID p_particles, RID p_particles_collision_instance) { Particles *particles = particles_owner.get_or_null(p_particles); ERR_FAIL_COND(!particles); particles->collisions.insert(p_particles_collision_instance); } void RendererStorageRD::particles_remove_collision(RID p_particles, RID p_particles_collision_instance) { Particles *particles = particles_owner.get_or_null(p_particles); ERR_FAIL_COND(!particles); 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.get_or_null(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, double 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 uniforms; { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.binding = 0; u.append_id(p_particles->frame_params_buffer); uniforms.push_back(u); } { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.binding = 1; u.append_id(p_particles->particle_buffer); uniforms.push_back(u); } { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.binding = 2; if (p_particles->emission_storage_buffer.is_valid()) { u.append_id(p_particles->emission_storage_buffer); } else { u.append_id(default_rd_storage_buffer); } uniforms.push_back(u); } { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.binding = 3; Particles *sub_emitter = particles_owner.get_or_null(p_particles->sub_emitter); if (sub_emitter) { if (sub_emitter->emission_buffer == nullptr) { //no emission buffer, allocate emission buffer _particles_allocate_emission_buffer(sub_emitter); } u.append_id(sub_emitter->emission_storage_buffer); } else { u.append_id(default_rd_storage_buffer); } uniforms.push_back(u); } p_particles->particles_material_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, particles_shader.default_shader_rd, 1); } double new_phase = Math::fmod((double)p_particles->phase + (p_delta / p_particles->lifetime) * p_particles->speed_scale, 1.0); //move back history (if there is any) for (uint32_t i = p_particles->frame_history.size() - 1; i > 0; i--) { p_particles->frame_history[i] = p_particles->frame_history[i - 1]; } //update current frame ParticlesFrameParams &frame_params = p_particles->frame_history[0]; if (p_particles->clear) { p_particles->cycle_number = 0; p_particles->random_seed = Math::rand(); } else if (new_phase < p_particles->phase) { if (p_particles->one_shot) { p_particles->emitting = false; } p_particles->cycle_number++; } frame_params.emitting = p_particles->emitting; frame_params.system_phase = new_phase; frame_params.prev_system_phase = p_particles->phase; p_particles->phase = new_phase; frame_params.time = RendererCompositorRD::singleton->get_total_time(); frame_params.delta = p_delta * p_particles->speed_scale; frame_params.random_seed = p_particles->random_seed; frame_params.explosiveness = p_particles->explosiveness; frame_params.randomness = p_particles->randomness; if (p_particles->use_local_coords) { store_transform(Transform3D(), frame_params.emission_transform); } else { store_transform(p_particles->emission_transform, frame_params.emission_transform); } frame_params.cycle = p_particles->cycle_number; frame_params.frame = p_particles->frame_counter++; frame_params.pad0 = 0; frame_params.pad1 = 0; frame_params.pad2 = 0; { //collision and attractors frame_params.collider_count = 0; frame_params.attractor_count = 0; frame_params.particle_size = p_particles->collision_base_size; RID collision_3d_textures[ParticlesFrameParams::MAX_3D_TEXTURES]; RID collision_heightmap_texture; Transform3D to_particles; 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::Element *E = p_particles->collisions.front(); E; E = E->next()) { ParticlesCollisionInstance *pci = particles_collision_instance_owner.get_or_null(E->get()); if (!pci || !pci->active) { continue; } ParticlesCollision *pc = particles_collision_owner.get_or_null(pci->collision); ERR_CONTINUE(!pc); Transform3D to_collider = pci->transform; if (p_particles->use_local_coords) { to_collider = to_particles * to_collider; } Vector3 scale = to_collider.basis.get_scale(); to_collider.basis.orthonormalize(); if (pc->type <= RS::PARTICLES_COLLISION_TYPE_VECTOR_FIELD_ATTRACT) { //attractor if (frame_params.attractor_count >= ParticlesFrameParams::MAX_ATTRACTORS) { continue; } ParticlesFrameParams::Attractor &attr = frame_params.attractors[frame_params.attractor_count]; store_transform(to_collider, attr.transform); attr.strength = pc->attractor_strength; attr.attenuation = pc->attractor_attenuation; attr.directionality = pc->attractor_directionality; switch (pc->type) { case RS::PARTICLES_COLLISION_TYPE_SPHERE_ATTRACT: { attr.type = ParticlesFrameParams::ATTRACTOR_TYPE_SPHERE; float radius = pc->radius; radius *= (scale.x + scale.y + scale.z) / 3.0; attr.extents[0] = radius; attr.extents[1] = radius; attr.extents[2] = radius; } break; case RS::PARTICLES_COLLISION_TYPE_BOX_ATTRACT: { attr.type = ParticlesFrameParams::ATTRACTOR_TYPE_BOX; Vector3 extents = pc->extents * scale; attr.extents[0] = extents.x; attr.extents[1] = extents.y; attr.extents[2] = extents.z; } break; case RS::PARTICLES_COLLISION_TYPE_VECTOR_FIELD_ATTRACT: { if (collision_3d_textures_used >= ParticlesFrameParams::MAX_3D_TEXTURES) { continue; } attr.type = ParticlesFrameParams::ATTRACTOR_TYPE_VECTOR_FIELD; Vector3 extents = pc->extents * scale; attr.extents[0] = extents.x; attr.extents[1] = extents.y; attr.extents[2] = extents.z; attr.texture_index = collision_3d_textures_used; collision_3d_textures[collision_3d_textures_used] = pc->field_texture; collision_3d_textures_used++; } break; default: { } } frame_params.attractor_count++; } else { //collider if (frame_params.collider_count >= ParticlesFrameParams::MAX_COLLIDERS) { continue; } ParticlesFrameParams::Collider &col = frame_params.colliders[frame_params.collider_count]; store_transform(to_collider, col.transform); switch (pc->type) { case RS::PARTICLES_COLLISION_TYPE_SPHERE_COLLIDE: { col.type = ParticlesFrameParams::COLLISION_TYPE_SPHERE; float radius = pc->radius; radius *= (scale.x + scale.y + scale.z) / 3.0; col.extents[0] = radius; col.extents[1] = radius; col.extents[2] = radius; } break; case RS::PARTICLES_COLLISION_TYPE_BOX_COLLIDE: { col.type = ParticlesFrameParams::COLLISION_TYPE_BOX; Vector3 extents = pc->extents * scale; col.extents[0] = extents.x; col.extents[1] = extents.y; col.extents[2] = extents.z; } break; case RS::PARTICLES_COLLISION_TYPE_SDF_COLLIDE: { if (collision_3d_textures_used >= ParticlesFrameParams::MAX_3D_TEXTURES) { continue; } col.type = ParticlesFrameParams::COLLISION_TYPE_SDF; Vector3 extents = pc->extents * scale; col.extents[0] = extents.x; col.extents[1] = extents.y; col.extents[2] = extents.z; col.texture_index = collision_3d_textures_used; col.scale = (scale.x + scale.y + scale.z) * 0.333333333333; //non uniform scale non supported collision_3d_textures[collision_3d_textures_used] = pc->field_texture; collision_3d_textures_used++; } break; case RS::PARTICLES_COLLISION_TYPE_HEIGHTFIELD_COLLIDE: { if (collision_heightmap_texture != RID()) { //already taken continue; } col.type = ParticlesFrameParams::COLLISION_TYPE_HEIGHT_FIELD; Vector3 extents = pc->extents * scale; col.extents[0] = extents.x; col.extents[1] = extents.y; col.extents[2] = extents.z; collision_heightmap_texture = pc->heightfield_texture; } break; default: { } } frame_params.collider_count++; } } bool different = false; if (collision_3d_textures_used == p_particles->collision_3d_textures_used) { for (int i = 0; i < ParticlesFrameParams::MAX_3D_TEXTURES; i++) { if (p_particles->collision_3d_textures[i] != collision_3d_textures[i]) { different = true; break; } } } if (collision_heightmap_texture != p_particles->collision_heightmap_texture) { different = true; } bool uniform_set_valid = RD::get_singleton()->uniform_set_is_valid(p_particles->collision_textures_uniform_set); if (different || !uniform_set_valid) { if (uniform_set_valid) { RD::get_singleton()->free(p_particles->collision_textures_uniform_set); } Vector uniforms; { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; u.binding = 0; for (uint32_t i = 0; i < ParticlesFrameParams::MAX_3D_TEXTURES; i++) { RID rd_tex; if (i < collision_3d_textures_used) { Texture *t = texture_owner.get_or_null(collision_3d_textures[i]); if (t && t->type == Texture::TYPE_3D) { rd_tex = t->rd_texture; } } if (rd_tex == RID()) { rd_tex = default_rd_textures[DEFAULT_RD_TEXTURE_3D_WHITE]; } u.append_id(rd_tex); } uniforms.push_back(u); } { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; u.binding = 1; if (collision_heightmap_texture.is_valid()) { u.append_id(collision_heightmap_texture); } else { u.append_id(default_rd_textures[DEFAULT_RD_TEXTURE_BLACK]); } uniforms.push_back(u); } p_particles->collision_textures_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, particles_shader.default_shader_rd, 2); } } ParticlesShader::PushConstant push_constant; int process_amount = p_particles->amount; if (p_particles->trails_enabled && p_particles->trail_bind_poses.size() > 1) { process_amount *= p_particles->trail_bind_poses.size(); } push_constant.clear = p_particles->clear; push_constant.total_particles = p_particles->amount; push_constant.lifetime = p_particles->lifetime; push_constant.trail_size = p_particles->trail_params.size(); push_constant.use_fractional_delta = p_particles->fractional_delta; push_constant.sub_emitter_mode = !p_particles->emitting && p_particles->emission_buffer && (p_particles->emission_buffer->particle_count > 0 || p_particles->force_sub_emit); push_constant.trail_pass = false; p_particles->force_sub_emit = false; //reset Particles *sub_emitter = particles_owner.get_or_null(p_particles->sub_emitter); if (sub_emitter && sub_emitter->emission_storage_buffer.is_valid()) { // print_line("updating subemitter buffer"); int32_t zero[4] = { 0, sub_emitter->amount, 0, 0 }; RD::get_singleton()->buffer_update(sub_emitter->emission_storage_buffer, 0, sizeof(uint32_t) * 4, zero); push_constant.can_emit = true; if (sub_emitter->emitting) { sub_emitter->emitting = false; sub_emitter->clear = true; //will need to clear if it was emitting, sorry } //make sure the sub emitter processes particles too sub_emitter->inactive = false; sub_emitter->inactive_time = 0; sub_emitter->force_sub_emit = true; } else { push_constant.can_emit = false; } if (p_particles->emission_buffer && p_particles->emission_buffer->particle_count) { RD::get_singleton()->buffer_update(p_particles->emission_storage_buffer, 0, sizeof(uint32_t) * 4 + sizeof(ParticleEmissionBuffer::Data) * p_particles->emission_buffer->particle_count, p_particles->emission_buffer); p_particles->emission_buffer->particle_count = 0; } p_particles->clear = false; if (p_particles->trail_params.size() > 1) { //fill the trail params for (uint32_t i = 0; i < p_particles->trail_params.size(); i++) { uint32_t src_idx = i * p_particles->frame_history.size() / p_particles->trail_params.size(); p_particles->trail_params[i] = p_particles->frame_history[src_idx]; } } else { p_particles->trail_params[0] = p_particles->frame_history[0]; } RD::get_singleton()->buffer_update(p_particles->frame_params_buffer, 0, sizeof(ParticlesFrameParams) * p_particles->trail_params.size(), p_particles->trail_params.ptr()); ParticlesMaterialData *m = (ParticlesMaterialData *)material_get_data(p_particles->process_material, SHADER_TYPE_PARTICLES); if (!m) { m = (ParticlesMaterialData *)material_get_data(particles_shader.default_material, SHADER_TYPE_PARTICLES); } 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); RD::get_singleton()->compute_list_bind_uniform_set(compute_list, particles_shader.base_uniform_set, 0); RD::get_singleton()->compute_list_bind_uniform_set(compute_list, p_particles->particles_material_uniform_set, 1); RD::get_singleton()->compute_list_bind_uniform_set(compute_list, p_particles->collision_textures_uniform_set, 2); if (m->uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(m->uniform_set)) { RD::get_singleton()->compute_list_bind_uniform_set(compute_list, m->uniform_set, 3); } RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(ParticlesShader::PushConstant)); if (p_particles->trails_enabled && p_particles->trail_bind_poses.size() > 1) { //trails requires two passes in order to catch particle starts RD::get_singleton()->compute_list_dispatch_threads(compute_list, process_amount / p_particles->trail_bind_poses.size(), 1, 1); RD::get_singleton()->compute_list_add_barrier(compute_list); push_constant.trail_pass = true; RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(ParticlesShader::PushConstant)); RD::get_singleton()->compute_list_dispatch_threads(compute_list, process_amount - p_particles->amount, 1, 1); } else { RD::get_singleton()->compute_list_dispatch_threads(compute_list, process_amount, 1, 1); } RD::get_singleton()->compute_list_end(); } void RendererStorageRD::particles_set_view_axis(RID p_particles, const Vector3 &p_axis, const Vector3 &p_up_axis) { Particles *particles = particles_owner.get_or_null(p_particles); ERR_FAIL_COND(!particles); if (particles->draw_order != RS::PARTICLES_DRAW_ORDER_VIEW_DEPTH && particles->transform_align != RS::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD && particles->transform_align != RS::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY) { return; } if (particles->particle_buffer.is_null()) { return; //particles have not processed yet } bool do_sort = particles->draw_order == RS::PARTICLES_DRAW_ORDER_VIEW_DEPTH; //copy to sort buffer if (do_sort && particles->particles_sort_buffer == RID()) { uint32_t size = particles->amount; if (size & 1) { size++; //make multiple of 16 } size *= sizeof(float) * 2; particles->particles_sort_buffer = RD::get_singleton()->storage_buffer_create(size); { Vector uniforms; { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.binding = 0; u.append_id(particles->particles_sort_buffer); uniforms.push_back(u); } particles->particles_sort_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, particles_shader.copy_shader.version_get_shader(particles_shader.copy_shader_version, ParticlesShader::COPY_MODE_FILL_SORT_BUFFER), 1); } } ParticlesShader::CopyPushConstant copy_push_constant; if (particles->trails_enabled && particles->trail_bind_poses.size() > 1) { int fixed_fps = 60.0; if (particles->fixed_fps > 0) { fixed_fps = particles->fixed_fps; } copy_push_constant.trail_size = particles->trail_bind_poses.size(); copy_push_constant.trail_total = particles->frame_history.size(); copy_push_constant.frame_delta = 1.0 / fixed_fps; } else { copy_push_constant.trail_size = 1; 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; copy_push_constant.copy_mode_2d = false; Vector3 axis = -p_axis; // cameras look to z negative if (particles->use_local_coords) { axis = particles->emission_transform.basis.xform_inv(axis).normalized(); } copy_push_constant.sort_direction[0] = axis.x; copy_push_constant.sort_direction[1] = axis.y; copy_push_constant.sort_direction[2] = axis.z; copy_push_constant.align_up[0] = p_up_axis.x; copy_push_constant.align_up[1] = p_up_axis.y; copy_push_constant.align_up[2] = p_up_axis.z; copy_push_constant.align_mode = particles->transform_align; if (do_sort) { RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, particles_shader.copy_pipelines[ParticlesShader::COPY_MODE_FILL_SORT_BUFFER + particles->userdata_count * ParticlesShader::COPY_MODE_MAX]); RD::get_singleton()->compute_list_bind_uniform_set(compute_list, particles->particles_copy_uniform_set, 0); RD::get_singleton()->compute_list_bind_uniform_set(compute_list, particles->particles_sort_uniform_set, 1); RD::get_singleton()->compute_list_bind_uniform_set(compute_list, particles->trail_bind_pose_uniform_set, 2); RD::get_singleton()->compute_list_set_push_constant(compute_list, ©_push_constant, sizeof(ParticlesShader::CopyPushConstant)); RD::get_singleton()->compute_list_dispatch_threads(compute_list, particles->amount, 1, 1); RD::get_singleton()->compute_list_end(); effects->sort_buffer(particles->particles_sort_uniform_set, particles->amount); } copy_push_constant.total_particles *= copy_push_constant.total_particles; RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); uint32_t copy_pipeline = do_sort ? ParticlesShader::COPY_MODE_FILL_INSTANCES_WITH_SORT_BUFFER : ParticlesShader::COPY_MODE_FILL_INSTANCES; copy_pipeline += particles->userdata_count * ParticlesShader::COPY_MODE_MAX; copy_push_constant.copy_mode_2d = particles->mode == RS::PARTICLES_MODE_2D ? 1 : 0; RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, particles_shader.copy_pipelines[copy_pipeline]); RD::get_singleton()->compute_list_bind_uniform_set(compute_list, particles->particles_copy_uniform_set, 0); if (do_sort) { RD::get_singleton()->compute_list_bind_uniform_set(compute_list, particles->particles_sort_uniform_set, 1); } RD::get_singleton()->compute_list_bind_uniform_set(compute_list, particles->trail_bind_pose_uniform_set, 2); RD::get_singleton()->compute_list_set_push_constant(compute_list, ©_push_constant, sizeof(ParticlesShader::CopyPushConstant)); RD::get_singleton()->compute_list_dispatch_threads(compute_list, copy_push_constant.total_particles, 1, 1); RD::get_singleton()->compute_list_end(); } void RendererStorageRD::_particles_update_buffers(Particles *particles) { uint32_t userdata_count = 0; const Material *material = material_owner.get_or_null(particles->process_material); if (material && material->shader && material->shader->data) { const ParticlesShaderData *shader_data = static_cast(material->shader->data); userdata_count = shader_data->userdata_count; } if (userdata_count != particles->userdata_count) { // Mismatch userdata, re-create buffers. _particles_free_data(particles); } if (particles->amount > 0 && particles->particle_buffer.is_null()) { int total_amount = particles->amount; if (particles->trails_enabled && particles->trail_bind_poses.size() > 1) { total_amount *= particles->trail_bind_poses.size(); } uint32_t xform_size = particles->mode == RS::PARTICLES_MODE_2D ? 2 : 3; particles->particle_buffer = RD::get_singleton()->storage_buffer_create((sizeof(ParticleData) + userdata_count * sizeof(float) * 4) * total_amount); particles->userdata_count = userdata_count; particles->particle_instance_buffer = RD::get_singleton()->storage_buffer_create(sizeof(float) * 4 * (xform_size + 1 + 1) * total_amount); //needs to clear it { Vector uniforms; { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.binding = 1; u.append_id(particles->particle_buffer); uniforms.push_back(u); } { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.binding = 2; u.append_id(particles->particle_instance_buffer); uniforms.push_back(u); } particles->particles_copy_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, particles_shader.copy_shader.version_get_shader(particles_shader.copy_shader_version, 0), 0); } } } void RendererStorageRD::update_particles() { while (particle_update_list) { //use transform feedback to process particles Particles *particles = particle_update_list; //take and remove particle_update_list = particles->update_list; particles->update_list = nullptr; particles->dirty = false; _particles_update_buffers(particles); if (particles->restart_request) { particles->prev_ticks = 0; particles->phase = 0; particles->prev_phase = 0; particles->clear = true; particles->restart_request = false; } if (particles->inactive && !particles->emitting) { //go next continue; } if (particles->emitting) { if (particles->inactive) { //restart system from scratch particles->prev_ticks = 0; particles->phase = 0; particles->prev_phase = 0; particles->clear = true; } particles->inactive = false; particles->inactive_time = 0; } else { particles->inactive_time += particles->speed_scale * RendererCompositorRD::singleton->get_frame_delta_time(); if (particles->inactive_time > particles->lifetime * 1.2) { particles->inactive = true; continue; } } #ifndef _MSC_VER #warning Should use display refresh rate for all this #endif float screen_hz = 60; int fixed_fps = 0; if (particles->fixed_fps > 0) { fixed_fps = particles->fixed_fps; } else if (particles->trails_enabled && particles->trail_bind_poses.size() > 1) { fixed_fps = screen_hz; } { //update trails int history_size = 1; int trail_steps = 1; if (particles->trails_enabled && particles->trail_bind_poses.size() > 1) { history_size = MAX(1, int(particles->trail_length * fixed_fps)); trail_steps = particles->trail_bind_poses.size(); } if (uint32_t(history_size) != particles->frame_history.size()) { particles->frame_history.resize(history_size); memset(particles->frame_history.ptr(), 0, sizeof(ParticlesFrameParams) * history_size); } if (uint32_t(trail_steps) != particles->trail_params.size() || particles->frame_params_buffer.is_null()) { particles->trail_params.resize(trail_steps); if (particles->frame_params_buffer.is_valid()) { RD::get_singleton()->free(particles->frame_params_buffer); } particles->frame_params_buffer = RD::get_singleton()->storage_buffer_create(sizeof(ParticlesFrameParams) * trail_steps); } if (particles->trail_bind_poses.size() > 1 && particles->trail_bind_pose_buffer.is_null()) { particles->trail_bind_pose_buffer = RD::get_singleton()->storage_buffer_create(sizeof(float) * 16 * particles->trail_bind_poses.size()); particles->trail_bind_poses_dirty = true; } if (particles->trail_bind_pose_uniform_set.is_null()) { Vector uniforms; { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.binding = 0; if (particles->trail_bind_pose_buffer.is_valid()) { u.append_id(particles->trail_bind_pose_buffer); } else { u.append_id(default_rd_storage_buffer); } uniforms.push_back(u); } particles->trail_bind_pose_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, particles_shader.copy_shader.version_get_shader(particles_shader.copy_shader_version, 0), 2); } if (particles->trail_bind_pose_buffer.is_valid() && particles->trail_bind_poses_dirty) { if (particles_shader.pose_update_buffer.size() < uint32_t(particles->trail_bind_poses.size()) * 16) { particles_shader.pose_update_buffer.resize(particles->trail_bind_poses.size() * 16); } for (int i = 0; i < particles->trail_bind_poses.size(); i++) { store_transform(particles->trail_bind_poses[i], &particles_shader.pose_update_buffer[i * 16]); } RD::get_singleton()->buffer_update(particles->trail_bind_pose_buffer, 0, particles->trail_bind_poses.size() * 16 * sizeof(float), particles_shader.pose_update_buffer.ptr()); } } bool zero_time_scale = Engine::get_singleton()->get_time_scale() <= 0.0; if (particles->clear && particles->pre_process_time > 0.0) { double frame_time; if (fixed_fps > 0) { frame_time = 1.0 / fixed_fps; } else { frame_time = 1.0 / 30.0; } double todo = particles->pre_process_time; while (todo >= 0) { _particles_process(particles, frame_time); todo -= frame_time; } } if (fixed_fps > 0) { double frame_time; double decr; if (zero_time_scale) { frame_time = 0.0; decr = 1.0 / fixed_fps; } else { frame_time = 1.0 / fixed_fps; decr = frame_time; } double delta = RendererCompositorRD::singleton->get_frame_delta_time(); if (delta > 0.1) { //avoid recursive stalls if fps goes below 10 delta = 0.1; } else if (delta <= 0.0) { //unlikely but.. delta = 0.001; } double todo = particles->frame_remainder + delta; while (todo >= frame_time) { _particles_process(particles, frame_time); todo -= decr; } particles->frame_remainder = todo; } else { if (zero_time_scale) { _particles_process(particles, 0.0); } else { _particles_process(particles, RendererCompositorRD::singleton->get_frame_delta_time()); } } //copy particles to instance buffer if (particles->draw_order != RS::PARTICLES_DRAW_ORDER_VIEW_DEPTH && particles->transform_align != RS::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD && particles->transform_align != RS::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY) { //does not need view dependent operation, do copy here ParticlesShader::CopyPushConstant copy_push_constant; int total_amount = particles->amount; if (particles->trails_enabled && particles->trail_bind_poses.size() > 1) { total_amount *= particles->trail_bind_poses.size(); } // Affect 2D only. if (particles->use_local_coords) { // In local mode, particle positions are calculated locally (relative to the node position) // and they're also drawn locally. // It works as expected, so we just pass an identity transform. store_transform(Transform3D(), copy_push_constant.inv_emission_transform); } else { // In global mode, particle positions are calculated globally (relative to the canvas origin) // but they're drawn locally. // So, we need to pass the inverse of the emission transform to bring the // particles to local coordinates before drawing. Transform3D inv = particles->emission_transform.affine_inverse(); store_transform(inv, copy_push_constant.inv_emission_transform); } copy_push_constant.total_particles = total_amount; copy_push_constant.frame_remainder = particles->interpolate ? particles->frame_remainder : 0.0; copy_push_constant.align_mode = particles->transform_align; copy_push_constant.align_up[0] = 0; copy_push_constant.align_up[1] = 0; copy_push_constant.align_up[2] = 0; if (particles->trails_enabled && particles->trail_bind_poses.size() > 1) { copy_push_constant.trail_size = particles->trail_bind_poses.size(); copy_push_constant.trail_total = particles->frame_history.size(); copy_push_constant.frame_delta = 1.0 / fixed_fps; } else { copy_push_constant.trail_size = 1; 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; RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); copy_push_constant.copy_mode_2d = particles->mode == RS::PARTICLES_MODE_2D ? 1 : 0; RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, particles_shader.copy_pipelines[ParticlesShader::COPY_MODE_FILL_INSTANCES + particles->userdata_count * ParticlesShader::COPY_MODE_MAX]); RD::get_singleton()->compute_list_bind_uniform_set(compute_list, particles->particles_copy_uniform_set, 0); RD::get_singleton()->compute_list_bind_uniform_set(compute_list, particles->trail_bind_pose_uniform_set, 2); RD::get_singleton()->compute_list_set_push_constant(compute_list, ©_push_constant, sizeof(ParticlesShader::CopyPushConstant)); RD::get_singleton()->compute_list_dispatch_threads(compute_list, total_amount, 1, 1); RD::get_singleton()->compute_list_end(); } particles->dependency.changed_notify(DEPENDENCY_CHANGED_AABB); } } bool RendererStorageRD::particles_is_inactive(RID p_particles) const { ERR_FAIL_COND_V_MSG(RSG::threaded, false, "This function should never be used with threaded rendering, as it stalls the renderer."); const Particles *particles = particles_owner.get_or_null(p_particles); ERR_FAIL_COND_V(!particles, false); return !particles->emitting && particles->inactive; } /* SKY SHADER */ void RendererStorageRD::ParticlesShaderData::set_code(const String &p_code) { //compile code = p_code; valid = false; ubo_size = 0; uniforms.clear(); uses_collision = false; if (code.is_empty()) { return; //just invalid, but no error } ShaderCompiler::GeneratedCode gen_code; ShaderCompiler::IdentifierActions actions; actions.entry_point_stages["start"] = ShaderCompiler::STAGE_COMPUTE; actions.entry_point_stages["process"] = ShaderCompiler::STAGE_COMPUTE; /* uses_time = false; actions.render_mode_flags["use_half_res_pass"] = &uses_half_res; actions.render_mode_flags["use_quarter_res_pass"] = &uses_quarter_res; actions.usage_flag_pointers["TIME"] = &uses_time; */ actions.usage_flag_pointers["COLLIDED"] = &uses_collision; userdata_count = 0; for (uint32_t i = 0; i < ParticlesShader::MAX_USERDATAS; i++) { userdatas_used[i] = false; actions.usage_flag_pointers["USERDATA" + itos(i + 1)] = &userdatas_used[i]; } actions.uniforms = &uniforms; Error err = base_singleton->particles_shader.compiler.compile(RS::SHADER_PARTICLES, code, &actions, path, gen_code); ERR_FAIL_COND_MSG(err != OK, "Shader compilation failed."); if (version.is_null()) { version = base_singleton->particles_shader.shader.version_create(); } for (uint32_t i = 0; i < ParticlesShader::MAX_USERDATAS; i++) { if (userdatas_used[i]) { userdata_count++; } } base_singleton->particles_shader.shader.version_set_compute_code(version, gen_code.code, gen_code.uniforms, gen_code.stage_globals[ShaderCompiler::STAGE_COMPUTE], gen_code.defines); ERR_FAIL_COND(!base_singleton->particles_shader.shader.version_is_valid(version)); ubo_size = gen_code.uniform_total_size; ubo_offsets = gen_code.uniform_offsets; texture_uniforms = gen_code.texture_uniforms; //update pipelines pipeline = RD::get_singleton()->compute_pipeline_create(base_singleton->particles_shader.shader.version_get_shader(version, 0)); valid = true; } void RendererStorageRD::ParticlesShaderData::set_default_texture_param(const StringName &p_name, RID p_texture, int p_index) { if (!p_texture.is_valid()) { if (default_texture_params.has(p_name) && default_texture_params[p_name].has(p_index)) { default_texture_params[p_name].erase(p_index); if (default_texture_params[p_name].is_empty()) { default_texture_params.erase(p_name); } } } else { if (!default_texture_params.has(p_name)) { default_texture_params[p_name] = Map(); } default_texture_params[p_name][p_index] = p_texture; } } void RendererStorageRD::ParticlesShaderData::get_param_list(List *p_param_list) const { Map order; for (const KeyValue &E : uniforms) { if (E.value.scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_GLOBAL || E.value.scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) { continue; } if (E.value.texture_order >= 0) { order[E.value.texture_order + 100000] = E.key; } else { order[E.value.order] = E.key; } } for (const KeyValue &E : order) { PropertyInfo pi = ShaderLanguage::uniform_to_property_info(uniforms[E.value]); pi.name = E.value; p_param_list->push_back(pi); } } void RendererStorageRD::ParticlesShaderData::get_instance_param_list(List *p_param_list) const { for (const KeyValue &E : uniforms) { if (E.value.scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) { continue; } RendererStorage::InstanceShaderParam p; p.info = ShaderLanguage::uniform_to_property_info(E.value); p.info.name = E.key; //supply name p.index = E.value.instance_index; p.default_value = ShaderLanguage::constant_value_to_variant(E.value.default_value, E.value.type, E.value.array_size, E.value.hint); p_param_list->push_back(p); } } bool RendererStorageRD::ParticlesShaderData::is_param_texture(const StringName &p_param) const { if (!uniforms.has(p_param)) { return false; } return uniforms[p_param].texture_order >= 0; } bool RendererStorageRD::ParticlesShaderData::is_animated() const { return false; } bool RendererStorageRD::ParticlesShaderData::casts_shadows() const { return false; } Variant RendererStorageRD::ParticlesShaderData::get_default_parameter(const StringName &p_parameter) const { if (uniforms.has(p_parameter)) { ShaderLanguage::ShaderNode::Uniform uniform = uniforms[p_parameter]; Vector default_value = uniform.default_value; return ShaderLanguage::constant_value_to_variant(default_value, uniform.type, uniform.array_size, uniform.hint); } return Variant(); } RS::ShaderNativeSourceCode RendererStorageRD::ParticlesShaderData::get_native_source_code() const { return base_singleton->particles_shader.shader.version_get_native_source_code(version); } RendererStorageRD::ParticlesShaderData::ParticlesShaderData() { valid = false; } RendererStorageRD::ParticlesShaderData::~ParticlesShaderData() { //pipeline variants will clear themselves if shader is gone if (version.is_valid()) { base_singleton->particles_shader.shader.version_free(version); } } RendererStorageRD::ShaderData *RendererStorageRD::_create_particles_shader_func() { ParticlesShaderData *shader_data = memnew(ParticlesShaderData); return shader_data; } bool RendererStorageRD::ParticlesMaterialData::update_parameters(const Map &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) { return update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, base_singleton->particles_shader.shader.version_get_shader(shader_data->version, 0), 3); } RendererStorageRD::ParticlesMaterialData::~ParticlesMaterialData() { free_parameters_uniform_set(uniform_set); } RendererStorageRD::MaterialData *RendererStorageRD::_create_particles_material_func(ParticlesShaderData *p_shader) { ParticlesMaterialData *material_data = memnew(ParticlesMaterialData); material_data->shader_data = p_shader; //update will happen later anyway so do nothing. return material_data; } //////// /* PARTICLES COLLISION API */ RID RendererStorageRD::particles_collision_allocate() { return particles_collision_owner.allocate_rid(); } void RendererStorageRD::particles_collision_initialize(RID p_rid) { particles_collision_owner.initialize_rid(p_rid, ParticlesCollision()); } RID RendererStorageRD::particles_collision_get_heightfield_framebuffer(RID p_particles_collision) const { ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); ERR_FAIL_COND_V(!particles_collision, RID()); ERR_FAIL_COND_V(particles_collision->type != RS::PARTICLES_COLLISION_TYPE_HEIGHTFIELD_COLLIDE, RID()); if (particles_collision->heightfield_texture == RID()) { //create int resolutions[RS::PARTICLES_COLLISION_HEIGHTFIELD_RESOLUTION_MAX] = { 256, 512, 1024, 2048, 4096, 8192 }; Size2i size; if (particles_collision->extents.x > particles_collision->extents.z) { size.x = resolutions[particles_collision->heightfield_resolution]; size.y = int32_t(particles_collision->extents.z / particles_collision->extents.x * size.x); } else { size.y = resolutions[particles_collision->heightfield_resolution]; size.x = int32_t(particles_collision->extents.x / particles_collision->extents.z * size.y); } RD::TextureFormat tf; tf.format = RD::DATA_FORMAT_D32_SFLOAT; tf.width = size.x; tf.height = size.y; tf.texture_type = RD::TEXTURE_TYPE_2D; tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; particles_collision->heightfield_texture = RD::get_singleton()->texture_create(tf, RD::TextureView()); Vector fb_tex; fb_tex.push_back(particles_collision->heightfield_texture); particles_collision->heightfield_fb = RD::get_singleton()->framebuffer_create(fb_tex); particles_collision->heightfield_fb_size = size; } return particles_collision->heightfield_fb; } void RendererStorageRD::particles_collision_set_collision_type(RID p_particles_collision, RS::ParticlesCollisionType p_type) { ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); ERR_FAIL_COND(!particles_collision); if (p_type == particles_collision->type) { return; } if (particles_collision->heightfield_texture.is_valid()) { RD::get_singleton()->free(particles_collision->heightfield_texture); particles_collision->heightfield_texture = RID(); } particles_collision->type = p_type; particles_collision->dependency.changed_notify(DEPENDENCY_CHANGED_AABB); } void RendererStorageRD::particles_collision_set_cull_mask(RID p_particles_collision, uint32_t p_cull_mask) { ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); ERR_FAIL_COND(!particles_collision); particles_collision->cull_mask = p_cull_mask; } void RendererStorageRD::particles_collision_set_sphere_radius(RID p_particles_collision, real_t p_radius) { ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); ERR_FAIL_COND(!particles_collision); particles_collision->radius = p_radius; particles_collision->dependency.changed_notify(DEPENDENCY_CHANGED_AABB); } void RendererStorageRD::particles_collision_set_box_extents(RID p_particles_collision, const Vector3 &p_extents) { ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); ERR_FAIL_COND(!particles_collision); particles_collision->extents = p_extents; particles_collision->dependency.changed_notify(DEPENDENCY_CHANGED_AABB); } void RendererStorageRD::particles_collision_set_attractor_strength(RID p_particles_collision, real_t p_strength) { ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); ERR_FAIL_COND(!particles_collision); particles_collision->attractor_strength = p_strength; } void RendererStorageRD::particles_collision_set_attractor_directionality(RID p_particles_collision, real_t p_directionality) { ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); ERR_FAIL_COND(!particles_collision); particles_collision->attractor_directionality = p_directionality; } void RendererStorageRD::particles_collision_set_attractor_attenuation(RID p_particles_collision, real_t p_curve) { ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); ERR_FAIL_COND(!particles_collision); particles_collision->attractor_attenuation = p_curve; } void RendererStorageRD::particles_collision_set_field_texture(RID p_particles_collision, RID p_texture) { ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); ERR_FAIL_COND(!particles_collision); particles_collision->field_texture = p_texture; } void RendererStorageRD::particles_collision_height_field_update(RID p_particles_collision) { ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); ERR_FAIL_COND(!particles_collision); particles_collision->dependency.changed_notify(DEPENDENCY_CHANGED_AABB); } void RendererStorageRD::particles_collision_set_height_field_resolution(RID p_particles_collision, RS::ParticlesCollisionHeightfieldResolution p_resolution) { ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); ERR_FAIL_COND(!particles_collision); ERR_FAIL_INDEX(p_resolution, RS::PARTICLES_COLLISION_HEIGHTFIELD_RESOLUTION_MAX); if (particles_collision->heightfield_resolution == p_resolution) { return; } particles_collision->heightfield_resolution = p_resolution; if (particles_collision->heightfield_texture.is_valid()) { RD::get_singleton()->free(particles_collision->heightfield_texture); particles_collision->heightfield_texture = RID(); } } AABB RendererStorageRD::particles_collision_get_aabb(RID p_particles_collision) const { ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); ERR_FAIL_COND_V(!particles_collision, AABB()); switch (particles_collision->type) { case RS::PARTICLES_COLLISION_TYPE_SPHERE_ATTRACT: case RS::PARTICLES_COLLISION_TYPE_SPHERE_COLLIDE: { AABB aabb; aabb.position = -Vector3(1, 1, 1) * particles_collision->radius; aabb.size = Vector3(2, 2, 2) * particles_collision->radius; return aabb; } default: { AABB aabb; aabb.position = -particles_collision->extents; aabb.size = particles_collision->extents * 2; return aabb; } } return AABB(); } Vector3 RendererStorageRD::particles_collision_get_extents(RID p_particles_collision) const { const ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); ERR_FAIL_COND_V(!particles_collision, Vector3()); return particles_collision->extents; } bool RendererStorageRD::particles_collision_is_heightfield(RID p_particles_collision) const { const ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); ERR_FAIL_COND_V(!particles_collision, false); return particles_collision->type == RS::PARTICLES_COLLISION_TYPE_HEIGHTFIELD_COLLIDE; } RID RendererStorageRD::particles_collision_instance_create(RID p_collision) { ParticlesCollisionInstance pci; pci.collision = p_collision; return particles_collision_instance_owner.make_rid(pci); } void RendererStorageRD::particles_collision_instance_set_transform(RID p_collision_instance, const Transform3D &p_transform) { ParticlesCollisionInstance *pci = particles_collision_instance_owner.get_or_null(p_collision_instance); ERR_FAIL_COND(!pci); pci->transform = p_transform; } void RendererStorageRD::particles_collision_instance_set_active(RID p_collision_instance, bool p_active) { ParticlesCollisionInstance *pci = particles_collision_instance_owner.get_or_null(p_collision_instance); ERR_FAIL_COND(!pci); pci->active = p_active; } /* FOG VOLUMES */ RID RendererStorageRD::fog_volume_allocate() { return fog_volume_owner.allocate_rid(); } void RendererStorageRD::fog_volume_initialize(RID p_rid) { fog_volume_owner.initialize_rid(p_rid, FogVolume()); } void RendererStorageRD::fog_volume_set_shape(RID p_fog_volume, RS::FogVolumeShape p_shape) { FogVolume *fog_volume = fog_volume_owner.get_or_null(p_fog_volume); ERR_FAIL_COND(!fog_volume); if (p_shape == fog_volume->shape) { return; } fog_volume->shape = p_shape; fog_volume->dependency.changed_notify(DEPENDENCY_CHANGED_AABB); } void RendererStorageRD::fog_volume_set_extents(RID p_fog_volume, const Vector3 &p_extents) { FogVolume *fog_volume = fog_volume_owner.get_or_null(p_fog_volume); ERR_FAIL_COND(!fog_volume); fog_volume->extents = p_extents; fog_volume->dependency.changed_notify(DEPENDENCY_CHANGED_AABB); } void RendererStorageRD::fog_volume_set_material(RID p_fog_volume, RID p_material) { FogVolume *fog_volume = fog_volume_owner.get_or_null(p_fog_volume); ERR_FAIL_COND(!fog_volume); fog_volume->material = p_material; } RID RendererStorageRD::fog_volume_get_material(RID p_fog_volume) const { FogVolume *fog_volume = fog_volume_owner.get_or_null(p_fog_volume); ERR_FAIL_COND_V(!fog_volume, RID()); return fog_volume->material; } RS::FogVolumeShape RendererStorageRD::fog_volume_get_shape(RID p_fog_volume) const { FogVolume *fog_volume = fog_volume_owner.get_or_null(p_fog_volume); ERR_FAIL_COND_V(!fog_volume, RS::FOG_VOLUME_SHAPE_BOX); return fog_volume->shape; } AABB RendererStorageRD::fog_volume_get_aabb(RID p_fog_volume) const { FogVolume *fog_volume = fog_volume_owner.get_or_null(p_fog_volume); ERR_FAIL_COND_V(!fog_volume, AABB()); switch (fog_volume->shape) { case RS::FOG_VOLUME_SHAPE_ELLIPSOID: case RS::FOG_VOLUME_SHAPE_BOX: { AABB aabb; aabb.position = -fog_volume->extents; aabb.size = fog_volume->extents * 2; return aabb; } default: { // Need some size otherwise will get culled return AABB(Vector3(-1, -1, -1), Vector3(2, 2, 2)); } } return AABB(); } Vector3 RendererStorageRD::fog_volume_get_extents(RID p_fog_volume) const { const FogVolume *fog_volume = fog_volume_owner.get_or_null(p_fog_volume); ERR_FAIL_COND_V(!fog_volume, Vector3()); return fog_volume->extents; } /* VISIBILITY NOTIFIER */ RID RendererStorageRD::visibility_notifier_allocate() { return visibility_notifier_owner.allocate_rid(); } void RendererStorageRD::visibility_notifier_initialize(RID p_notifier) { visibility_notifier_owner.initialize_rid(p_notifier, VisibilityNotifier()); } void RendererStorageRD::visibility_notifier_set_aabb(RID p_notifier, const AABB &p_aabb) { VisibilityNotifier *vn = visibility_notifier_owner.get_or_null(p_notifier); ERR_FAIL_COND(!vn); vn->aabb = p_aabb; vn->dependency.changed_notify(DEPENDENCY_CHANGED_AABB); } void RendererStorageRD::visibility_notifier_set_callbacks(RID p_notifier, const Callable &p_enter_callbable, const Callable &p_exit_callable) { VisibilityNotifier *vn = visibility_notifier_owner.get_or_null(p_notifier); ERR_FAIL_COND(!vn); vn->enter_callback = p_enter_callbable; vn->exit_callback = p_exit_callable; } AABB RendererStorageRD::visibility_notifier_get_aabb(RID p_notifier) const { const VisibilityNotifier *vn = visibility_notifier_owner.get_or_null(p_notifier); ERR_FAIL_COND_V(!vn, AABB()); return vn->aabb; } void RendererStorageRD::visibility_notifier_call(RID p_notifier, bool p_enter, bool p_deferred) { VisibilityNotifier *vn = visibility_notifier_owner.get_or_null(p_notifier); ERR_FAIL_COND(!vn); if (p_enter) { if (!vn->enter_callback.is_null()) { if (p_deferred) { vn->enter_callback.call_deferred(nullptr, 0); } else { Variant r; Callable::CallError ce; vn->enter_callback.call(nullptr, 0, r, ce); } } } else { if (!vn->exit_callback.is_null()) { if (p_deferred) { vn->exit_callback.call_deferred(nullptr, 0); } else { Variant r; Callable::CallError ce; vn->exit_callback.call(nullptr, 0, r, ce); } } } } /* SKELETON API */ RID RendererStorageRD::skeleton_allocate() { return skeleton_owner.allocate_rid(); } void RendererStorageRD::skeleton_initialize(RID p_rid) { skeleton_owner.initialize_rid(p_rid, Skeleton()); } void RendererStorageRD::_skeleton_make_dirty(Skeleton *skeleton) { if (!skeleton->dirty) { skeleton->dirty = true; skeleton->dirty_list = skeleton_dirty_list; skeleton_dirty_list = skeleton; } } void RendererStorageRD::skeleton_allocate_data(RID p_skeleton, int p_bones, bool p_2d_skeleton) { Skeleton *skeleton = skeleton_owner.get_or_null(p_skeleton); ERR_FAIL_COND(!skeleton); ERR_FAIL_COND(p_bones < 0); if (skeleton->size == p_bones && skeleton->use_2d == p_2d_skeleton) { return; } skeleton->size = p_bones; skeleton->use_2d = p_2d_skeleton; skeleton->uniform_set_3d = RID(); if (skeleton->buffer.is_valid()) { RD::get_singleton()->free(skeleton->buffer); skeleton->buffer = RID(); skeleton->data.clear(); skeleton->uniform_set_mi = RID(); } if (skeleton->size) { skeleton->data.resize(skeleton->size * (skeleton->use_2d ? 8 : 12)); skeleton->buffer = RD::get_singleton()->storage_buffer_create(skeleton->data.size() * sizeof(float)); memset(skeleton->data.ptrw(), 0, skeleton->data.size() * sizeof(float)); _skeleton_make_dirty(skeleton); { Vector uniforms; { RD::Uniform u; u.binding = 0; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.append_id(skeleton->buffer); uniforms.push_back(u); } skeleton->uniform_set_mi = RD::get_singleton()->uniform_set_create(uniforms, skeleton_shader.version_shader[0], SkeletonShader::UNIFORM_SET_SKELETON); } } skeleton->dependency.changed_notify(DEPENDENCY_CHANGED_SKELETON_DATA); } int RendererStorageRD::skeleton_get_bone_count(RID p_skeleton) const { Skeleton *skeleton = skeleton_owner.get_or_null(p_skeleton); ERR_FAIL_COND_V(!skeleton, 0); return skeleton->size; } void RendererStorageRD::skeleton_bone_set_transform(RID p_skeleton, int p_bone, const Transform3D &p_transform) { Skeleton *skeleton = skeleton_owner.get_or_null(p_skeleton); ERR_FAIL_COND(!skeleton); ERR_FAIL_INDEX(p_bone, skeleton->size); ERR_FAIL_COND(skeleton->use_2d); float *dataptr = skeleton->data.ptrw() + p_bone * 12; dataptr[0] = p_transform.basis.elements[0][0]; dataptr[1] = p_transform.basis.elements[0][1]; dataptr[2] = p_transform.basis.elements[0][2]; dataptr[3] = p_transform.origin.x; dataptr[4] = p_transform.basis.elements[1][0]; dataptr[5] = p_transform.basis.elements[1][1]; dataptr[6] = p_transform.basis.elements[1][2]; dataptr[7] = p_transform.origin.y; dataptr[8] = p_transform.basis.elements[2][0]; dataptr[9] = p_transform.basis.elements[2][1]; dataptr[10] = p_transform.basis.elements[2][2]; dataptr[11] = p_transform.origin.z; _skeleton_make_dirty(skeleton); } Transform3D RendererStorageRD::skeleton_bone_get_transform(RID p_skeleton, int p_bone) const { Skeleton *skeleton = skeleton_owner.get_or_null(p_skeleton); ERR_FAIL_COND_V(!skeleton, Transform3D()); ERR_FAIL_INDEX_V(p_bone, skeleton->size, Transform3D()); ERR_FAIL_COND_V(skeleton->use_2d, Transform3D()); const float *dataptr = skeleton->data.ptr() + p_bone * 12; Transform3D t; t.basis.elements[0][0] = dataptr[0]; t.basis.elements[0][1] = dataptr[1]; t.basis.elements[0][2] = dataptr[2]; t.origin.x = dataptr[3]; t.basis.elements[1][0] = dataptr[4]; t.basis.elements[1][1] = dataptr[5]; t.basis.elements[1][2] = dataptr[6]; t.origin.y = dataptr[7]; t.basis.elements[2][0] = dataptr[8]; t.basis.elements[2][1] = dataptr[9]; t.basis.elements[2][2] = dataptr[10]; t.origin.z = dataptr[11]; return t; } void RendererStorageRD::skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform) { Skeleton *skeleton = skeleton_owner.get_or_null(p_skeleton); ERR_FAIL_COND(!skeleton); ERR_FAIL_INDEX(p_bone, skeleton->size); ERR_FAIL_COND(!skeleton->use_2d); float *dataptr = skeleton->data.ptrw() + p_bone * 8; dataptr[0] = p_transform.elements[0][0]; dataptr[1] = p_transform.elements[1][0]; dataptr[2] = 0; dataptr[3] = p_transform.elements[2][0]; dataptr[4] = p_transform.elements[0][1]; dataptr[5] = p_transform.elements[1][1]; dataptr[6] = 0; dataptr[7] = p_transform.elements[2][1]; _skeleton_make_dirty(skeleton); } Transform2D RendererStorageRD::skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const { Skeleton *skeleton = skeleton_owner.get_or_null(p_skeleton); ERR_FAIL_COND_V(!skeleton, Transform2D()); ERR_FAIL_INDEX_V(p_bone, skeleton->size, Transform2D()); ERR_FAIL_COND_V(!skeleton->use_2d, Transform2D()); const float *dataptr = skeleton->data.ptr() + p_bone * 8; Transform2D t; t.elements[0][0] = dataptr[0]; t.elements[1][0] = dataptr[1]; t.elements[2][0] = dataptr[3]; t.elements[0][1] = dataptr[4]; t.elements[1][1] = dataptr[5]; t.elements[2][1] = dataptr[7]; return t; } void RendererStorageRD::skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform) { Skeleton *skeleton = skeleton_owner.get_or_null(p_skeleton); ERR_FAIL_COND(!skeleton->use_2d); skeleton->base_transform_2d = p_base_transform; } void RendererStorageRD::_update_dirty_skeletons() { while (skeleton_dirty_list) { Skeleton *skeleton = skeleton_dirty_list; if (skeleton->size) { RD::get_singleton()->buffer_update(skeleton->buffer, 0, skeleton->data.size() * sizeof(float), skeleton->data.ptr()); } skeleton_dirty_list = skeleton->dirty_list; skeleton->dependency.changed_notify(DEPENDENCY_CHANGED_SKELETON_BONES); skeleton->version++; skeleton->dirty = false; skeleton->dirty_list = nullptr; } skeleton_dirty_list = nullptr; } /* LIGHT */ void RendererStorageRD::_light_initialize(RID p_light, RS::LightType p_type) { Light light; light.type = p_type; light.param[RS::LIGHT_PARAM_ENERGY] = 1.0; light.param[RS::LIGHT_PARAM_INDIRECT_ENERGY] = 1.0; light.param[RS::LIGHT_PARAM_SPECULAR] = 0.5; light.param[RS::LIGHT_PARAM_RANGE] = 1.0; light.param[RS::LIGHT_PARAM_SIZE] = 0.0; light.param[RS::LIGHT_PARAM_ATTENUATION] = 1.0; light.param[RS::LIGHT_PARAM_SPOT_ANGLE] = 45; light.param[RS::LIGHT_PARAM_SPOT_ATTENUATION] = 1.0; light.param[RS::LIGHT_PARAM_SHADOW_MAX_DISTANCE] = 0; light.param[RS::LIGHT_PARAM_SHADOW_SPLIT_1_OFFSET] = 0.1; light.param[RS::LIGHT_PARAM_SHADOW_SPLIT_2_OFFSET] = 0.3; light.param[RS::LIGHT_PARAM_SHADOW_SPLIT_3_OFFSET] = 0.6; light.param[RS::LIGHT_PARAM_SHADOW_FADE_START] = 0.8; light.param[RS::LIGHT_PARAM_SHADOW_NORMAL_BIAS] = 1.0; light.param[RS::LIGHT_PARAM_SHADOW_BIAS] = 0.02; light.param[RS::LIGHT_PARAM_SHADOW_BLUR] = 0; light.param[RS::LIGHT_PARAM_SHADOW_PANCAKE_SIZE] = 20.0; light.param[RS::LIGHT_PARAM_SHADOW_VOLUMETRIC_FOG_FADE] = 0.1; light.param[RS::LIGHT_PARAM_TRANSMITTANCE_BIAS] = 0.05; light_owner.initialize_rid(p_light, light); } RID RendererStorageRD::directional_light_allocate() { return light_owner.allocate_rid(); } void RendererStorageRD::directional_light_initialize(RID p_light) { _light_initialize(p_light, RS::LIGHT_DIRECTIONAL); } RID RendererStorageRD::omni_light_allocate() { return light_owner.allocate_rid(); } void RendererStorageRD::omni_light_initialize(RID p_light) { _light_initialize(p_light, RS::LIGHT_OMNI); } RID RendererStorageRD::spot_light_allocate() { return light_owner.allocate_rid(); } void RendererStorageRD::spot_light_initialize(RID p_light) { _light_initialize(p_light, RS::LIGHT_SPOT); } void RendererStorageRD::light_set_color(RID p_light, const Color &p_color) { Light *light = light_owner.get_or_null(p_light); ERR_FAIL_COND(!light); light->color = p_color; } void RendererStorageRD::light_set_param(RID p_light, RS::LightParam p_param, float p_value) { Light *light = light_owner.get_or_null(p_light); ERR_FAIL_COND(!light); ERR_FAIL_INDEX(p_param, RS::LIGHT_PARAM_MAX); if (light->param[p_param] == p_value) { return; } switch (p_param) { case RS::LIGHT_PARAM_RANGE: case RS::LIGHT_PARAM_SPOT_ANGLE: case RS::LIGHT_PARAM_SHADOW_MAX_DISTANCE: case RS::LIGHT_PARAM_SHADOW_SPLIT_1_OFFSET: case RS::LIGHT_PARAM_SHADOW_SPLIT_2_OFFSET: case RS::LIGHT_PARAM_SHADOW_SPLIT_3_OFFSET: case RS::LIGHT_PARAM_SHADOW_NORMAL_BIAS: case RS::LIGHT_PARAM_SHADOW_PANCAKE_SIZE: case RS::LIGHT_PARAM_SHADOW_BIAS: { light->version++; light->dependency.changed_notify(DEPENDENCY_CHANGED_LIGHT); } break; case RS::LIGHT_PARAM_SIZE: { if ((light->param[p_param] > CMP_EPSILON) != (p_value > CMP_EPSILON)) { //changing from no size to size and the opposite light->dependency.changed_notify(DEPENDENCY_CHANGED_LIGHT_SOFT_SHADOW_AND_PROJECTOR); } } break; default: { } } light->param[p_param] = p_value; } void RendererStorageRD::light_set_shadow(RID p_light, bool p_enabled) { Light *light = light_owner.get_or_null(p_light); ERR_FAIL_COND(!light); light->shadow = p_enabled; light->version++; light->dependency.changed_notify(DEPENDENCY_CHANGED_LIGHT); } void RendererStorageRD::light_set_projector(RID p_light, RID p_texture) { Light *light = light_owner.get_or_null(p_light); ERR_FAIL_COND(!light); if (light->projector == p_texture) { return; } if (light->type != RS::LIGHT_DIRECTIONAL && light->projector.is_valid()) { texture_remove_from_decal_atlas(light->projector, light->type == RS::LIGHT_OMNI); } light->projector = p_texture; if (light->type != RS::LIGHT_DIRECTIONAL) { if (light->projector.is_valid()) { texture_add_to_decal_atlas(light->projector, light->type == RS::LIGHT_OMNI); } light->dependency.changed_notify(DEPENDENCY_CHANGED_LIGHT_SOFT_SHADOW_AND_PROJECTOR); } } void RendererStorageRD::light_set_negative(RID p_light, bool p_enable) { Light *light = light_owner.get_or_null(p_light); ERR_FAIL_COND(!light); light->negative = p_enable; } void RendererStorageRD::light_set_cull_mask(RID p_light, uint32_t p_mask) { Light *light = light_owner.get_or_null(p_light); ERR_FAIL_COND(!light); light->cull_mask = p_mask; light->version++; light->dependency.changed_notify(DEPENDENCY_CHANGED_LIGHT); } void RendererStorageRD::light_set_distance_fade(RID p_light, bool p_enabled, float p_begin, float p_shadow, float p_length) { Light *light = light_owner.get_or_null(p_light); ERR_FAIL_COND(!light); light->distance_fade = p_enabled; light->distance_fade_begin = p_begin; light->distance_fade_shadow = p_shadow; light->distance_fade_length = p_length; } void RendererStorageRD::light_set_reverse_cull_face_mode(RID p_light, bool p_enabled) { Light *light = light_owner.get_or_null(p_light); ERR_FAIL_COND(!light); light->reverse_cull = p_enabled; light->version++; light->dependency.changed_notify(DEPENDENCY_CHANGED_LIGHT); } void RendererStorageRD::light_set_bake_mode(RID p_light, RS::LightBakeMode p_bake_mode) { Light *light = light_owner.get_or_null(p_light); ERR_FAIL_COND(!light); light->bake_mode = p_bake_mode; light->version++; light->dependency.changed_notify(DEPENDENCY_CHANGED_LIGHT); } void RendererStorageRD::light_set_max_sdfgi_cascade(RID p_light, uint32_t p_cascade) { Light *light = light_owner.get_or_null(p_light); ERR_FAIL_COND(!light); light->max_sdfgi_cascade = p_cascade; light->version++; light->dependency.changed_notify(DEPENDENCY_CHANGED_LIGHT); } void RendererStorageRD::light_omni_set_shadow_mode(RID p_light, RS::LightOmniShadowMode p_mode) { Light *light = light_owner.get_or_null(p_light); ERR_FAIL_COND(!light); light->omni_shadow_mode = p_mode; light->version++; light->dependency.changed_notify(DEPENDENCY_CHANGED_LIGHT); } RS::LightOmniShadowMode RendererStorageRD::light_omni_get_shadow_mode(RID p_light) { const Light *light = light_owner.get_or_null(p_light); ERR_FAIL_COND_V(!light, RS::LIGHT_OMNI_SHADOW_CUBE); return light->omni_shadow_mode; } void RendererStorageRD::light_directional_set_shadow_mode(RID p_light, RS::LightDirectionalShadowMode p_mode) { Light *light = light_owner.get_or_null(p_light); ERR_FAIL_COND(!light); light->directional_shadow_mode = p_mode; light->version++; light->dependency.changed_notify(DEPENDENCY_CHANGED_LIGHT); } void RendererStorageRD::light_directional_set_blend_splits(RID p_light, bool p_enable) { Light *light = light_owner.get_or_null(p_light); ERR_FAIL_COND(!light); light->directional_blend_splits = p_enable; light->version++; light->dependency.changed_notify(DEPENDENCY_CHANGED_LIGHT); } bool RendererStorageRD::light_directional_get_blend_splits(RID p_light) const { const Light *light = light_owner.get_or_null(p_light); ERR_FAIL_COND_V(!light, false); return light->directional_blend_splits; } void RendererStorageRD::light_directional_set_sky_only(RID p_light, bool p_sky_only) { Light *light = light_owner.get_or_null(p_light); ERR_FAIL_COND(!light); light->directional_sky_only = p_sky_only; } bool RendererStorageRD::light_directional_is_sky_only(RID p_light) const { const Light *light = light_owner.get_or_null(p_light); ERR_FAIL_COND_V(!light, false); return light->directional_sky_only; } RS::LightDirectionalShadowMode RendererStorageRD::light_directional_get_shadow_mode(RID p_light) { const Light *light = light_owner.get_or_null(p_light); ERR_FAIL_COND_V(!light, RS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL); return light->directional_shadow_mode; } uint32_t RendererStorageRD::light_get_max_sdfgi_cascade(RID p_light) { const Light *light = light_owner.get_or_null(p_light); ERR_FAIL_COND_V(!light, 0); return light->max_sdfgi_cascade; } RS::LightBakeMode RendererStorageRD::light_get_bake_mode(RID p_light) { const Light *light = light_owner.get_or_null(p_light); ERR_FAIL_COND_V(!light, RS::LIGHT_BAKE_DISABLED); return light->bake_mode; } uint64_t RendererStorageRD::light_get_version(RID p_light) const { const Light *light = light_owner.get_or_null(p_light); ERR_FAIL_COND_V(!light, 0); return light->version; } AABB RendererStorageRD::light_get_aabb(RID p_light) const { const Light *light = light_owner.get_or_null(p_light); ERR_FAIL_COND_V(!light, AABB()); switch (light->type) { case RS::LIGHT_SPOT: { float len = light->param[RS::LIGHT_PARAM_RANGE]; float size = Math::tan(Math::deg2rad(light->param[RS::LIGHT_PARAM_SPOT_ANGLE])) * len; return AABB(Vector3(-size, -size, -len), Vector3(size * 2, size * 2, len)); }; case RS::LIGHT_OMNI: { float r = light->param[RS::LIGHT_PARAM_RANGE]; return AABB(-Vector3(r, r, r), Vector3(r, r, r) * 2); }; case RS::LIGHT_DIRECTIONAL: { return AABB(); }; } ERR_FAIL_V(AABB()); } /* REFLECTION PROBE */ RID RendererStorageRD::reflection_probe_allocate() { return reflection_probe_owner.allocate_rid(); } void RendererStorageRD::reflection_probe_initialize(RID p_reflection_probe) { reflection_probe_owner.initialize_rid(p_reflection_probe, ReflectionProbe()); } void RendererStorageRD::reflection_probe_set_update_mode(RID p_probe, RS::ReflectionProbeUpdateMode p_mode) { ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe); ERR_FAIL_COND(!reflection_probe); reflection_probe->update_mode = p_mode; reflection_probe->dependency.changed_notify(DEPENDENCY_CHANGED_REFLECTION_PROBE); } void RendererStorageRD::reflection_probe_set_intensity(RID p_probe, float p_intensity) { ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe); ERR_FAIL_COND(!reflection_probe); reflection_probe->intensity = p_intensity; } void RendererStorageRD::reflection_probe_set_ambient_mode(RID p_probe, RS::ReflectionProbeAmbientMode p_mode) { ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe); ERR_FAIL_COND(!reflection_probe); reflection_probe->ambient_mode = p_mode; } void RendererStorageRD::reflection_probe_set_ambient_color(RID p_probe, const Color &p_color) { ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe); ERR_FAIL_COND(!reflection_probe); reflection_probe->ambient_color = p_color; } void RendererStorageRD::reflection_probe_set_ambient_energy(RID p_probe, float p_energy) { ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe); ERR_FAIL_COND(!reflection_probe); reflection_probe->ambient_color_energy = p_energy; } void RendererStorageRD::reflection_probe_set_max_distance(RID p_probe, float p_distance) { ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe); ERR_FAIL_COND(!reflection_probe); reflection_probe->max_distance = p_distance; reflection_probe->dependency.changed_notify(DEPENDENCY_CHANGED_REFLECTION_PROBE); } void RendererStorageRD::reflection_probe_set_extents(RID p_probe, const Vector3 &p_extents) { ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe); ERR_FAIL_COND(!reflection_probe); if (reflection_probe->extents == p_extents) { return; } reflection_probe->extents = p_extents; reflection_probe->dependency.changed_notify(DEPENDENCY_CHANGED_REFLECTION_PROBE); } void RendererStorageRD::reflection_probe_set_origin_offset(RID p_probe, const Vector3 &p_offset) { ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe); ERR_FAIL_COND(!reflection_probe); reflection_probe->origin_offset = p_offset; reflection_probe->dependency.changed_notify(DEPENDENCY_CHANGED_REFLECTION_PROBE); } void RendererStorageRD::reflection_probe_set_as_interior(RID p_probe, bool p_enable) { ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe); ERR_FAIL_COND(!reflection_probe); reflection_probe->interior = p_enable; reflection_probe->dependency.changed_notify(DEPENDENCY_CHANGED_REFLECTION_PROBE); } void RendererStorageRD::reflection_probe_set_enable_box_projection(RID p_probe, bool p_enable) { ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe); ERR_FAIL_COND(!reflection_probe); reflection_probe->box_projection = p_enable; } void RendererStorageRD::reflection_probe_set_enable_shadows(RID p_probe, bool p_enable) { ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe); ERR_FAIL_COND(!reflection_probe); reflection_probe->enable_shadows = p_enable; reflection_probe->dependency.changed_notify(DEPENDENCY_CHANGED_REFLECTION_PROBE); } void RendererStorageRD::reflection_probe_set_cull_mask(RID p_probe, uint32_t p_layers) { ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe); ERR_FAIL_COND(!reflection_probe); reflection_probe->cull_mask = p_layers; reflection_probe->dependency.changed_notify(DEPENDENCY_CHANGED_REFLECTION_PROBE); } void RendererStorageRD::reflection_probe_set_resolution(RID p_probe, int p_resolution) { ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe); ERR_FAIL_COND(!reflection_probe); ERR_FAIL_COND(p_resolution < 32); reflection_probe->resolution = p_resolution; } void RendererStorageRD::reflection_probe_set_mesh_lod_threshold(RID p_probe, float p_ratio) { ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe); ERR_FAIL_COND(!reflection_probe); reflection_probe->mesh_lod_threshold = p_ratio; reflection_probe->dependency.changed_notify(DEPENDENCY_CHANGED_REFLECTION_PROBE); } AABB RendererStorageRD::reflection_probe_get_aabb(RID p_probe) const { const ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe); ERR_FAIL_COND_V(!reflection_probe, AABB()); AABB aabb; aabb.position = -reflection_probe->extents; aabb.size = reflection_probe->extents * 2.0; return aabb; } RS::ReflectionProbeUpdateMode RendererStorageRD::reflection_probe_get_update_mode(RID p_probe) const { const ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe); ERR_FAIL_COND_V(!reflection_probe, RS::REFLECTION_PROBE_UPDATE_ALWAYS); return reflection_probe->update_mode; } uint32_t RendererStorageRD::reflection_probe_get_cull_mask(RID p_probe) const { const ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe); ERR_FAIL_COND_V(!reflection_probe, 0); return reflection_probe->cull_mask; } Vector3 RendererStorageRD::reflection_probe_get_extents(RID p_probe) const { const ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe); ERR_FAIL_COND_V(!reflection_probe, Vector3()); return reflection_probe->extents; } Vector3 RendererStorageRD::reflection_probe_get_origin_offset(RID p_probe) const { const ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe); ERR_FAIL_COND_V(!reflection_probe, Vector3()); return reflection_probe->origin_offset; } bool RendererStorageRD::reflection_probe_renders_shadows(RID p_probe) const { const ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe); ERR_FAIL_COND_V(!reflection_probe, false); return reflection_probe->enable_shadows; } float RendererStorageRD::reflection_probe_get_origin_max_distance(RID p_probe) const { const ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe); ERR_FAIL_COND_V(!reflection_probe, 0); return reflection_probe->max_distance; } float RendererStorageRD::reflection_probe_get_mesh_lod_threshold(RID p_probe) const { const ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe); ERR_FAIL_COND_V(!reflection_probe, 0); return reflection_probe->mesh_lod_threshold; } int RendererStorageRD::reflection_probe_get_resolution(RID p_probe) const { const ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe); ERR_FAIL_COND_V(!reflection_probe, 0); return reflection_probe->resolution; } float RendererStorageRD::reflection_probe_get_intensity(RID p_probe) const { const ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe); ERR_FAIL_COND_V(!reflection_probe, 0); return reflection_probe->intensity; } bool RendererStorageRD::reflection_probe_is_interior(RID p_probe) const { const ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe); ERR_FAIL_COND_V(!reflection_probe, false); return reflection_probe->interior; } bool RendererStorageRD::reflection_probe_is_box_projection(RID p_probe) const { const ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe); ERR_FAIL_COND_V(!reflection_probe, false); return reflection_probe->box_projection; } RS::ReflectionProbeAmbientMode RendererStorageRD::reflection_probe_get_ambient_mode(RID p_probe) const { const ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe); ERR_FAIL_COND_V(!reflection_probe, RS::REFLECTION_PROBE_AMBIENT_DISABLED); return reflection_probe->ambient_mode; } Color RendererStorageRD::reflection_probe_get_ambient_color(RID p_probe) const { const ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe); ERR_FAIL_COND_V(!reflection_probe, Color()); return reflection_probe->ambient_color; } float RendererStorageRD::reflection_probe_get_ambient_color_energy(RID p_probe) const { const ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe); ERR_FAIL_COND_V(!reflection_probe, 0); return reflection_probe->ambient_color_energy; } RID RendererStorageRD::decal_allocate() { return decal_owner.allocate_rid(); } void RendererStorageRD::decal_initialize(RID p_decal) { decal_owner.initialize_rid(p_decal, Decal()); } void RendererStorageRD::decal_set_extents(RID p_decal, const Vector3 &p_extents) { Decal *decal = decal_owner.get_or_null(p_decal); ERR_FAIL_COND(!decal); decal->extents = p_extents; decal->dependency.changed_notify(DEPENDENCY_CHANGED_AABB); } void RendererStorageRD::decal_set_texture(RID p_decal, RS::DecalTexture p_type, RID p_texture) { Decal *decal = decal_owner.get_or_null(p_decal); ERR_FAIL_COND(!decal); ERR_FAIL_INDEX(p_type, RS::DECAL_TEXTURE_MAX); if (decal->textures[p_type] == p_texture) { return; } ERR_FAIL_COND(p_texture.is_valid() && !texture_owner.owns(p_texture)); if (decal->textures[p_type].is_valid() && texture_owner.owns(decal->textures[p_type])) { texture_remove_from_decal_atlas(decal->textures[p_type]); } decal->textures[p_type] = p_texture; if (decal->textures[p_type].is_valid()) { texture_add_to_decal_atlas(decal->textures[p_type]); } decal->dependency.changed_notify(DEPENDENCY_CHANGED_DECAL); } void RendererStorageRD::decal_set_emission_energy(RID p_decal, float p_energy) { Decal *decal = decal_owner.get_or_null(p_decal); ERR_FAIL_COND(!decal); decal->emission_energy = p_energy; } void RendererStorageRD::decal_set_albedo_mix(RID p_decal, float p_mix) { Decal *decal = decal_owner.get_or_null(p_decal); ERR_FAIL_COND(!decal); decal->albedo_mix = p_mix; } void RendererStorageRD::decal_set_modulate(RID p_decal, const Color &p_modulate) { Decal *decal = decal_owner.get_or_null(p_decal); ERR_FAIL_COND(!decal); decal->modulate = p_modulate; } void RendererStorageRD::decal_set_cull_mask(RID p_decal, uint32_t p_layers) { Decal *decal = decal_owner.get_or_null(p_decal); ERR_FAIL_COND(!decal); decal->cull_mask = p_layers; decal->dependency.changed_notify(DEPENDENCY_CHANGED_AABB); } void RendererStorageRD::decal_set_distance_fade(RID p_decal, bool p_enabled, float p_begin, float p_length) { Decal *decal = decal_owner.get_or_null(p_decal); ERR_FAIL_COND(!decal); decal->distance_fade = p_enabled; decal->distance_fade_begin = p_begin; decal->distance_fade_length = p_length; } void RendererStorageRD::decal_set_fade(RID p_decal, float p_above, float p_below) { Decal *decal = decal_owner.get_or_null(p_decal); ERR_FAIL_COND(!decal); decal->upper_fade = p_above; decal->lower_fade = p_below; } void RendererStorageRD::decal_set_normal_fade(RID p_decal, float p_fade) { Decal *decal = decal_owner.get_or_null(p_decal); ERR_FAIL_COND(!decal); decal->normal_fade = p_fade; } AABB RendererStorageRD::decal_get_aabb(RID p_decal) const { Decal *decal = decal_owner.get_or_null(p_decal); ERR_FAIL_COND_V(!decal, AABB()); return AABB(-decal->extents, decal->extents * 2.0); } RID RendererStorageRD::voxel_gi_allocate() { return voxel_gi_owner.allocate_rid(); } void RendererStorageRD::voxel_gi_initialize(RID p_voxel_gi) { voxel_gi_owner.initialize_rid(p_voxel_gi, VoxelGI()); } void RendererStorageRD::voxel_gi_allocate_data(RID p_voxel_gi, const Transform3D &p_to_cell_xform, const AABB &p_aabb, const Vector3i &p_octree_size, const Vector &p_octree_cells, const Vector &p_data_cells, const Vector &p_distance_field, const Vector &p_level_counts) { VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi); ERR_FAIL_COND(!voxel_gi); if (voxel_gi->octree_buffer.is_valid()) { RD::get_singleton()->free(voxel_gi->octree_buffer); RD::get_singleton()->free(voxel_gi->data_buffer); if (voxel_gi->sdf_texture.is_valid()) { RD::get_singleton()->free(voxel_gi->sdf_texture); } voxel_gi->sdf_texture = RID(); voxel_gi->octree_buffer = RID(); voxel_gi->data_buffer = RID(); voxel_gi->octree_buffer_size = 0; voxel_gi->data_buffer_size = 0; voxel_gi->cell_count = 0; } voxel_gi->to_cell_xform = p_to_cell_xform; voxel_gi->bounds = p_aabb; voxel_gi->octree_size = p_octree_size; voxel_gi->level_counts = p_level_counts; if (p_octree_cells.size()) { ERR_FAIL_COND(p_octree_cells.size() % 32 != 0); //cells size must be a multiple of 32 uint32_t cell_count = p_octree_cells.size() / 32; ERR_FAIL_COND(p_data_cells.size() != (int)cell_count * 16); //see that data size matches voxel_gi->cell_count = cell_count; voxel_gi->octree_buffer = RD::get_singleton()->storage_buffer_create(p_octree_cells.size(), p_octree_cells); voxel_gi->octree_buffer_size = p_octree_cells.size(); voxel_gi->data_buffer = RD::get_singleton()->storage_buffer_create(p_data_cells.size(), p_data_cells); voxel_gi->data_buffer_size = p_data_cells.size(); if (p_distance_field.size()) { RD::TextureFormat tf; tf.format = RD::DATA_FORMAT_R8_UNORM; tf.width = voxel_gi->octree_size.x; tf.height = voxel_gi->octree_size.y; tf.depth = voxel_gi->octree_size.z; tf.texture_type = RD::TEXTURE_TYPE_3D; tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT; Vector> s; s.push_back(p_distance_field); voxel_gi->sdf_texture = RD::get_singleton()->texture_create(tf, RD::TextureView(), s); } #if 0 { RD::TextureFormat tf; tf.format = RD::DATA_FORMAT_R8_UNORM; tf.width = voxel_gi->octree_size.x; tf.height = voxel_gi->octree_size.y; tf.depth = voxel_gi->octree_size.z; tf.type = RD::TEXTURE_TYPE_3D; tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT; tf.shareable_formats.push_back(RD::DATA_FORMAT_R8_UNORM); tf.shareable_formats.push_back(RD::DATA_FORMAT_R8_UINT); voxel_gi->sdf_texture = RD::get_singleton()->texture_create(tf, RD::TextureView()); } RID shared_tex; { RD::TextureView tv; tv.format_override = RD::DATA_FORMAT_R8_UINT; shared_tex = RD::get_singleton()->texture_create_shared(tv, voxel_gi->sdf_texture); } //update SDF texture Vector uniforms; { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.binding = 1; u.append_id(voxel_gi->octree_buffer); uniforms.push_back(u); } { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.binding = 2; u.append_id(voxel_gi->data_buffer); uniforms.push_back(u); } { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_IMAGE; u.binding = 3; u.append_id(shared_tex); uniforms.push_back(u); } RID uniform_set = RD::get_singleton()->uniform_set_create(uniforms, voxel_gi_sdf_shader_version_shader, 0); { uint32_t push_constant[4] = { 0, 0, 0, 0 }; for (int i = 0; i < voxel_gi->level_counts.size() - 1; i++) { push_constant[0] += voxel_gi->level_counts[i]; } push_constant[1] = push_constant[0] + voxel_gi->level_counts[voxel_gi->level_counts.size() - 1]; print_line("offset: " + itos(push_constant[0])); print_line("size: " + itos(push_constant[1])); //create SDF RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, voxel_gi_sdf_shader_pipeline); RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set, 0); RD::get_singleton()->compute_list_set_push_constant(compute_list, push_constant, sizeof(uint32_t) * 4); RD::get_singleton()->compute_list_dispatch(compute_list, voxel_gi->octree_size.x / 4, voxel_gi->octree_size.y / 4, voxel_gi->octree_size.z / 4); RD::get_singleton()->compute_list_end(); } RD::get_singleton()->free(uniform_set); RD::get_singleton()->free(shared_tex); } #endif } voxel_gi->version++; voxel_gi->data_version++; voxel_gi->dependency.changed_notify(DEPENDENCY_CHANGED_AABB); } AABB RendererStorageRD::voxel_gi_get_bounds(RID p_voxel_gi) const { VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi); ERR_FAIL_COND_V(!voxel_gi, AABB()); return voxel_gi->bounds; } Vector3i RendererStorageRD::voxel_gi_get_octree_size(RID p_voxel_gi) const { VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi); ERR_FAIL_COND_V(!voxel_gi, Vector3i()); return voxel_gi->octree_size; } Vector RendererStorageRD::voxel_gi_get_octree_cells(RID p_voxel_gi) const { VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi); ERR_FAIL_COND_V(!voxel_gi, Vector()); if (voxel_gi->octree_buffer.is_valid()) { return RD::get_singleton()->buffer_get_data(voxel_gi->octree_buffer); } return Vector(); } Vector RendererStorageRD::voxel_gi_get_data_cells(RID p_voxel_gi) const { VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi); ERR_FAIL_COND_V(!voxel_gi, Vector()); if (voxel_gi->data_buffer.is_valid()) { return RD::get_singleton()->buffer_get_data(voxel_gi->data_buffer); } return Vector(); } Vector RendererStorageRD::voxel_gi_get_distance_field(RID p_voxel_gi) const { VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi); ERR_FAIL_COND_V(!voxel_gi, Vector()); if (voxel_gi->data_buffer.is_valid()) { return RD::get_singleton()->texture_get_data(voxel_gi->sdf_texture, 0); } return Vector(); } Vector RendererStorageRD::voxel_gi_get_level_counts(RID p_voxel_gi) const { VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi); ERR_FAIL_COND_V(!voxel_gi, Vector()); return voxel_gi->level_counts; } Transform3D RendererStorageRD::voxel_gi_get_to_cell_xform(RID p_voxel_gi) const { VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi); ERR_FAIL_COND_V(!voxel_gi, Transform3D()); return voxel_gi->to_cell_xform; } void RendererStorageRD::voxel_gi_set_dynamic_range(RID p_voxel_gi, float p_range) { VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi); ERR_FAIL_COND(!voxel_gi); voxel_gi->dynamic_range = p_range; voxel_gi->version++; } float RendererStorageRD::voxel_gi_get_dynamic_range(RID p_voxel_gi) const { VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi); ERR_FAIL_COND_V(!voxel_gi, 0); return voxel_gi->dynamic_range; } void RendererStorageRD::voxel_gi_set_propagation(RID p_voxel_gi, float p_range) { VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi); ERR_FAIL_COND(!voxel_gi); voxel_gi->propagation = p_range; voxel_gi->version++; } float RendererStorageRD::voxel_gi_get_propagation(RID p_voxel_gi) const { VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi); ERR_FAIL_COND_V(!voxel_gi, 0); return voxel_gi->propagation; } void RendererStorageRD::voxel_gi_set_energy(RID p_voxel_gi, float p_energy) { VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi); ERR_FAIL_COND(!voxel_gi); voxel_gi->energy = p_energy; } float RendererStorageRD::voxel_gi_get_energy(RID p_voxel_gi) const { VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi); ERR_FAIL_COND_V(!voxel_gi, 0); return voxel_gi->energy; } void RendererStorageRD::voxel_gi_set_bias(RID p_voxel_gi, float p_bias) { VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi); ERR_FAIL_COND(!voxel_gi); voxel_gi->bias = p_bias; } float RendererStorageRD::voxel_gi_get_bias(RID p_voxel_gi) const { VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi); ERR_FAIL_COND_V(!voxel_gi, 0); return voxel_gi->bias; } void RendererStorageRD::voxel_gi_set_normal_bias(RID p_voxel_gi, float p_normal_bias) { VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi); ERR_FAIL_COND(!voxel_gi); voxel_gi->normal_bias = p_normal_bias; } float RendererStorageRD::voxel_gi_get_normal_bias(RID p_voxel_gi) const { VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi); ERR_FAIL_COND_V(!voxel_gi, 0); return voxel_gi->normal_bias; } void RendererStorageRD::voxel_gi_set_anisotropy_strength(RID p_voxel_gi, float p_strength) { VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi); ERR_FAIL_COND(!voxel_gi); voxel_gi->anisotropy_strength = p_strength; } float RendererStorageRD::voxel_gi_get_anisotropy_strength(RID p_voxel_gi) const { VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi); ERR_FAIL_COND_V(!voxel_gi, 0); return voxel_gi->anisotropy_strength; } void RendererStorageRD::voxel_gi_set_interior(RID p_voxel_gi, bool p_enable) { VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi); ERR_FAIL_COND(!voxel_gi); voxel_gi->interior = p_enable; } void RendererStorageRD::voxel_gi_set_use_two_bounces(RID p_voxel_gi, bool p_enable) { VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi); ERR_FAIL_COND(!voxel_gi); voxel_gi->use_two_bounces = p_enable; voxel_gi->version++; } bool RendererStorageRD::voxel_gi_is_using_two_bounces(RID p_voxel_gi) const { VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi); ERR_FAIL_COND_V(!voxel_gi, false); return voxel_gi->use_two_bounces; } bool RendererStorageRD::voxel_gi_is_interior(RID p_voxel_gi) const { VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi); ERR_FAIL_COND_V(!voxel_gi, 0); return voxel_gi->interior; } uint32_t RendererStorageRD::voxel_gi_get_version(RID p_voxel_gi) { VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi); ERR_FAIL_COND_V(!voxel_gi, 0); return voxel_gi->version; } uint32_t RendererStorageRD::voxel_gi_get_data_version(RID p_voxel_gi) { VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi); ERR_FAIL_COND_V(!voxel_gi, 0); return voxel_gi->data_version; } RID RendererStorageRD::voxel_gi_get_octree_buffer(RID p_voxel_gi) const { VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi); ERR_FAIL_COND_V(!voxel_gi, RID()); return voxel_gi->octree_buffer; } RID RendererStorageRD::voxel_gi_get_data_buffer(RID p_voxel_gi) const { VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi); ERR_FAIL_COND_V(!voxel_gi, RID()); return voxel_gi->data_buffer; } RID RendererStorageRD::voxel_gi_get_sdf_texture(RID p_voxel_gi) { VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi); ERR_FAIL_COND_V(!voxel_gi, RID()); return voxel_gi->sdf_texture; } /* LIGHTMAP API */ RID RendererStorageRD::lightmap_allocate() { return lightmap_owner.allocate_rid(); } void RendererStorageRD::lightmap_initialize(RID p_lightmap) { lightmap_owner.initialize_rid(p_lightmap, Lightmap()); } void RendererStorageRD::lightmap_set_textures(RID p_lightmap, RID p_light, bool p_uses_spherical_haromics) { Lightmap *lm = lightmap_owner.get_or_null(p_lightmap); ERR_FAIL_COND(!lm); lightmap_array_version++; //erase lightmap users if (lm->light_texture.is_valid()) { Texture *t = texture_owner.get_or_null(lm->light_texture); if (t) { t->lightmap_users.erase(p_lightmap); } } Texture *t = texture_owner.get_or_null(p_light); lm->light_texture = p_light; lm->uses_spherical_harmonics = p_uses_spherical_haromics; RID default_2d_array = default_rd_textures[DEFAULT_RD_TEXTURE_2D_ARRAY_WHITE]; if (!t) { if (using_lightmap_array) { if (lm->array_index >= 0) { lightmap_textures.write[lm->array_index] = default_2d_array; lm->array_index = -1; } } return; } t->lightmap_users.insert(p_lightmap); if (using_lightmap_array) { if (lm->array_index < 0) { //not in array, try to put in array for (int i = 0; i < lightmap_textures.size(); i++) { if (lightmap_textures[i] == default_2d_array) { lm->array_index = i; break; } } } ERR_FAIL_COND_MSG(lm->array_index < 0, "Maximum amount of lightmaps in use (" + itos(lightmap_textures.size()) + ") has been exceeded, lightmap will nod display properly."); lightmap_textures.write[lm->array_index] = t->rd_texture; } } void RendererStorageRD::lightmap_set_probe_bounds(RID p_lightmap, const AABB &p_bounds) { Lightmap *lm = lightmap_owner.get_or_null(p_lightmap); ERR_FAIL_COND(!lm); lm->bounds = p_bounds; } void RendererStorageRD::lightmap_set_probe_interior(RID p_lightmap, bool p_interior) { Lightmap *lm = lightmap_owner.get_or_null(p_lightmap); ERR_FAIL_COND(!lm); lm->interior = p_interior; } void RendererStorageRD::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) { Lightmap *lm = lightmap_owner.get_or_null(p_lightmap); ERR_FAIL_COND(!lm); if (p_points.size()) { ERR_FAIL_COND(p_points.size() * 9 != p_point_sh.size()); ERR_FAIL_COND((p_tetrahedra.size() % 4) != 0); ERR_FAIL_COND((p_bsp_tree.size() % 6) != 0); } lm->points = p_points; lm->bsp_tree = p_bsp_tree; lm->point_sh = p_point_sh; lm->tetrahedra = p_tetrahedra; } PackedVector3Array RendererStorageRD::lightmap_get_probe_capture_points(RID p_lightmap) const { Lightmap *lm = lightmap_owner.get_or_null(p_lightmap); ERR_FAIL_COND_V(!lm, PackedVector3Array()); return lm->points; } PackedColorArray RendererStorageRD::lightmap_get_probe_capture_sh(RID p_lightmap) const { Lightmap *lm = lightmap_owner.get_or_null(p_lightmap); ERR_FAIL_COND_V(!lm, PackedColorArray()); return lm->point_sh; } PackedInt32Array RendererStorageRD::lightmap_get_probe_capture_tetrahedra(RID p_lightmap) const { Lightmap *lm = lightmap_owner.get_or_null(p_lightmap); ERR_FAIL_COND_V(!lm, PackedInt32Array()); return lm->tetrahedra; } PackedInt32Array RendererStorageRD::lightmap_get_probe_capture_bsp_tree(RID p_lightmap) const { Lightmap *lm = lightmap_owner.get_or_null(p_lightmap); ERR_FAIL_COND_V(!lm, PackedInt32Array()); return lm->bsp_tree; } void RendererStorageRD::lightmap_set_probe_capture_update_speed(float p_speed) { lightmap_probe_capture_update_speed = p_speed; } void RendererStorageRD::lightmap_tap_sh_light(RID p_lightmap, const Vector3 &p_point, Color *r_sh) { Lightmap *lm = lightmap_owner.get_or_null(p_lightmap); ERR_FAIL_COND(!lm); for (int i = 0; i < 9; i++) { r_sh[i] = Color(0, 0, 0, 0); } if (!lm->points.size() || !lm->bsp_tree.size() || !lm->tetrahedra.size()) { return; } static_assert(sizeof(Lightmap::BSP) == 24); const Lightmap::BSP *bsp = (const Lightmap::BSP *)lm->bsp_tree.ptr(); int32_t node = 0; while (node >= 0) { if (Plane(bsp[node].plane[0], bsp[node].plane[1], bsp[node].plane[2], bsp[node].plane[3]).is_point_over(p_point)) { #ifdef DEBUG_ENABLED ERR_FAIL_COND(bsp[node].over >= 0 && bsp[node].over < node); #endif node = bsp[node].over; } else { #ifdef DEBUG_ENABLED ERR_FAIL_COND(bsp[node].under >= 0 && bsp[node].under < node); #endif node = bsp[node].under; } } if (node == Lightmap::BSP::EMPTY_LEAF) { return; //nothing could be done } node = ABS(node) - 1; uint32_t *tetrahedron = (uint32_t *)&lm->tetrahedra[node * 4]; Vector3 points[4] = { lm->points[tetrahedron[0]], lm->points[tetrahedron[1]], lm->points[tetrahedron[2]], lm->points[tetrahedron[3]] }; const Color *sh_colors[4]{ &lm->point_sh[tetrahedron[0] * 9], &lm->point_sh[tetrahedron[1] * 9], &lm->point_sh[tetrahedron[2] * 9], &lm->point_sh[tetrahedron[3] * 9] }; Color barycentric = Geometry3D::tetrahedron_get_barycentric_coords(points[0], points[1], points[2], points[3], p_point); for (int i = 0; i < 4; i++) { float c = CLAMP(barycentric[i], 0.0, 1.0); for (int j = 0; j < 9; j++) { r_sh[j] += sh_colors[i][j] * c; } } } bool RendererStorageRD::lightmap_is_interior(RID p_lightmap) const { const Lightmap *lm = lightmap_owner.get_or_null(p_lightmap); ERR_FAIL_COND_V(!lm, false); return lm->interior; } AABB RendererStorageRD::lightmap_get_aabb(RID p_lightmap) const { const Lightmap *lm = lightmap_owner.get_or_null(p_lightmap); ERR_FAIL_COND_V(!lm, AABB()); return lm->bounds; } /* RENDER TARGET API */ void RendererStorageRD::_clear_render_target(RenderTarget *rt) { //free in reverse dependency order if (rt->framebuffer.is_valid()) { RD::get_singleton()->free(rt->framebuffer); rt->framebuffer_uniform_set = RID(); //chain deleted } if (rt->color.is_valid()) { RD::get_singleton()->free(rt->color); } if (rt->backbuffer.is_valid()) { RD::get_singleton()->free(rt->backbuffer); rt->backbuffer = RID(); rt->backbuffer_mipmaps.clear(); rt->backbuffer_uniform_set = RID(); //chain deleted } _render_target_clear_sdf(rt); rt->framebuffer = RID(); rt->color = RID(); } void RendererStorageRD::_update_render_target(RenderTarget *rt) { if (rt->texture.is_null()) { //create a placeholder until updated rt->texture = texture_allocate(); texture_2d_placeholder_initialize(rt->texture); Texture *tex = texture_owner.get_or_null(rt->texture); tex->is_render_target = true; } _clear_render_target(rt); if (rt->size.width == 0 || rt->size.height == 0) { return; } //until we implement support for HDR monitors (and render target is attached to screen), this is enough. rt->color_format = RD::DATA_FORMAT_R8G8B8A8_UNORM; rt->color_format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB; rt->image_format = rt->flags[RENDER_TARGET_TRANSPARENT] ? Image::FORMAT_RGBA8 : Image::FORMAT_RGB8; RD::TextureFormat rd_format; RD::TextureView rd_view; { //attempt register rd_format.format = rt->color_format; rd_format.width = rt->size.width; rd_format.height = rt->size.height; rd_format.depth = 1; rd_format.array_layers = rt->view_count; // for stereo we create two (or more) layers, need to see if we can make fallback work like this too if we don't have multiview rd_format.mipmaps = 1; if (rd_format.array_layers > 1) { // why are we not using rt->texture_type ?? rd_format.texture_type = RD::TEXTURE_TYPE_2D_ARRAY; } else { rd_format.texture_type = RD::TEXTURE_TYPE_2D; } rd_format.samples = RD::TEXTURE_SAMPLES_1; rd_format.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT; rd_format.shareable_formats.push_back(rt->color_format); rd_format.shareable_formats.push_back(rt->color_format_srgb); } rt->color = RD::get_singleton()->texture_create(rd_format, rd_view); ERR_FAIL_COND(rt->color.is_null()); Vector fb_textures; fb_textures.push_back(rt->color); rt->framebuffer = RD::get_singleton()->framebuffer_create(fb_textures, RenderingDevice::INVALID_ID, rt->view_count); if (rt->framebuffer.is_null()) { _clear_render_target(rt); ERR_FAIL_COND(rt->framebuffer.is_null()); } { //update texture Texture *tex = texture_owner.get_or_null(rt->texture); //free existing textures if (RD::get_singleton()->texture_is_valid(tex->rd_texture)) { RD::get_singleton()->free(tex->rd_texture); } if (RD::get_singleton()->texture_is_valid(tex->rd_texture_srgb)) { RD::get_singleton()->free(tex->rd_texture_srgb); } tex->rd_texture = RID(); tex->rd_texture_srgb = RID(); //create shared textures to the color buffer, //so transparent can be supported RD::TextureView view; view.format_override = rt->color_format; if (!rt->flags[RENDER_TARGET_TRANSPARENT]) { view.swizzle_a = RD::TEXTURE_SWIZZLE_ONE; } tex->rd_texture = RD::get_singleton()->texture_create_shared(view, rt->color); if (rt->color_format_srgb != RD::DATA_FORMAT_MAX) { view.format_override = rt->color_format_srgb; tex->rd_texture_srgb = RD::get_singleton()->texture_create_shared(view, rt->color); } tex->rd_view = view; tex->width = rt->size.width; tex->height = rt->size.height; tex->width_2d = rt->size.width; tex->height_2d = rt->size.height; tex->rd_format = rt->color_format; tex->rd_format_srgb = rt->color_format_srgb; tex->format = rt->image_format; Vector proxies = tex->proxies; //make a copy, since update may change it for (int i = 0; i < proxies.size(); i++) { texture_proxy_update(proxies[i], rt->texture); } } } void RendererStorageRD::_create_render_target_backbuffer(RenderTarget *rt) { ERR_FAIL_COND(rt->backbuffer.is_valid()); uint32_t mipmaps_required = Image::get_image_required_mipmaps(rt->size.width, rt->size.height, Image::FORMAT_RGBA8); RD::TextureFormat tf; tf.format = rt->color_format; tf.width = rt->size.width; tf.height = rt->size.height; tf.texture_type = RD::TEXTURE_TYPE_2D; tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT; tf.mipmaps = mipmaps_required; rt->backbuffer = RD::get_singleton()->texture_create(tf, RD::TextureView()); RD::get_singleton()->set_resource_name(rt->backbuffer, "Render Target Back Buffer"); rt->backbuffer_mipmap0 = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), rt->backbuffer, 0, 0); RD::get_singleton()->set_resource_name(rt->backbuffer_mipmap0, "Back Buffer slice mipmap 0"); { Vector fb_tex; fb_tex.push_back(rt->backbuffer_mipmap0); rt->backbuffer_fb = RD::get_singleton()->framebuffer_create(fb_tex); } if (rt->framebuffer_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(rt->framebuffer_uniform_set)) { //the new one will require the backbuffer. RD::get_singleton()->free(rt->framebuffer_uniform_set); rt->framebuffer_uniform_set = RID(); } //create mipmaps for (uint32_t i = 1; i < mipmaps_required; i++) { RID mipmap = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), rt->backbuffer, 0, i); RD::get_singleton()->set_resource_name(mipmap, "Back Buffer slice mip: " + itos(i)); rt->backbuffer_mipmaps.push_back(mipmap); } } RID RendererStorageRD::render_target_create() { RenderTarget render_target; render_target.was_used = false; render_target.clear_requested = false; for (int i = 0; i < RENDER_TARGET_FLAG_MAX; i++) { render_target.flags[i] = false; } _update_render_target(&render_target); return render_target_owner.make_rid(render_target); } void RendererStorageRD::render_target_set_position(RID p_render_target, int p_x, int p_y) { //unused for this render target } void RendererStorageRD::render_target_set_size(RID p_render_target, int p_width, int p_height, uint32_t p_view_count) { RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_COND(!rt); if (rt->size.x != p_width || rt->size.y != p_height || rt->view_count != p_view_count) { rt->size.x = p_width; rt->size.y = p_height; rt->view_count = p_view_count; _update_render_target(rt); } } RID RendererStorageRD::render_target_get_texture(RID p_render_target) { RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_COND_V(!rt, RID()); return rt->texture; } void RendererStorageRD::render_target_set_external_texture(RID p_render_target, unsigned int p_texture_id) { } void RendererStorageRD::render_target_set_flag(RID p_render_target, RenderTargetFlags p_flag, bool p_value) { RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_COND(!rt); rt->flags[p_flag] = p_value; _update_render_target(rt); } bool RendererStorageRD::render_target_was_used(RID p_render_target) { RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_COND_V(!rt, false); return rt->was_used; } void RendererStorageRD::render_target_set_as_unused(RID p_render_target) { RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_COND(!rt); rt->was_used = false; } Size2 RendererStorageRD::render_target_get_size(RID p_render_target) { RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_COND_V(!rt, Size2()); return rt->size; } RID RendererStorageRD::render_target_get_rd_framebuffer(RID p_render_target) { RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_COND_V(!rt, RID()); return rt->framebuffer; } RID RendererStorageRD::render_target_get_rd_texture(RID p_render_target) { RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_COND_V(!rt, RID()); return rt->color; } RID RendererStorageRD::render_target_get_rd_backbuffer(RID p_render_target) { RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_COND_V(!rt, RID()); return rt->backbuffer; } RID RendererStorageRD::render_target_get_rd_backbuffer_framebuffer(RID p_render_target) { RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_COND_V(!rt, RID()); if (!rt->backbuffer.is_valid()) { _create_render_target_backbuffer(rt); } return rt->backbuffer_fb; } void RendererStorageRD::render_target_request_clear(RID p_render_target, const Color &p_clear_color) { RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_COND(!rt); rt->clear_requested = true; rt->clear_color = p_clear_color; } bool RendererStorageRD::render_target_is_clear_requested(RID p_render_target) { RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_COND_V(!rt, false); return rt->clear_requested; } Color RendererStorageRD::render_target_get_clear_request_color(RID p_render_target) { RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_COND_V(!rt, Color()); return rt->clear_color; } void RendererStorageRD::render_target_disable_clear_request(RID p_render_target) { RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_COND(!rt); rt->clear_requested = false; } void RendererStorageRD::render_target_do_clear_request(RID p_render_target) { RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_COND(!rt); if (!rt->clear_requested) { return; } Vector clear_colors; clear_colors.push_back(rt->clear_color); RD::get_singleton()->draw_list_begin(rt->framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD, clear_colors); RD::get_singleton()->draw_list_end(); rt->clear_requested = false; } void RendererStorageRD::render_target_set_sdf_size_and_scale(RID p_render_target, RS::ViewportSDFOversize p_size, RS::ViewportSDFScale p_scale) { RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_COND(!rt); if (rt->sdf_oversize == p_size && rt->sdf_scale == p_scale) { return; } rt->sdf_oversize = p_size; rt->sdf_scale = p_scale; _render_target_clear_sdf(rt); } Rect2i RendererStorageRD::_render_target_get_sdf_rect(const RenderTarget *rt) const { Size2i margin; int scale; switch (rt->sdf_oversize) { case RS::VIEWPORT_SDF_OVERSIZE_100_PERCENT: { scale = 100; } break; case RS::VIEWPORT_SDF_OVERSIZE_120_PERCENT: { scale = 120; } break; case RS::VIEWPORT_SDF_OVERSIZE_150_PERCENT: { scale = 150; } break; case RS::VIEWPORT_SDF_OVERSIZE_200_PERCENT: { scale = 200; } break; default: { } } margin = (rt->size * scale / 100) - rt->size; Rect2i r(Vector2i(), rt->size); r.position -= margin; r.size += margin * 2; return r; } Rect2i RendererStorageRD::render_target_get_sdf_rect(RID p_render_target) const { const RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_COND_V(!rt, Rect2i()); 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.get_or_null(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.get_or_null(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.get_or_null(p_render_target); ERR_FAIL_COND_V(!rt, RID()); if (rt->sdf_buffer_read.is_null()) { // no texture, create a dummy one for the 2D uniform set RD::TextureFormat tformat; tformat.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; tformat.width = 4; tformat.height = 4; tformat.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT; tformat.texture_type = RD::TEXTURE_TYPE_2D; Vector pv; pv.resize(16 * 4); memset(pv.ptrw(), 0, 16 * 4); Vector> vpv; rt->sdf_buffer_read = RD::get_singleton()->texture_create(tformat, RD::TextureView(), vpv); } return rt->sdf_buffer_read; } void RendererStorageRD::_render_target_allocate_sdf(RenderTarget *rt) { ERR_FAIL_COND(rt->sdf_buffer_write_fb.is_valid()); if (rt->sdf_buffer_read.is_valid()) { RD::get_singleton()->free(rt->sdf_buffer_read); rt->sdf_buffer_read = RID(); } Size2i size = _render_target_get_sdf_rect(rt).size; RD::TextureFormat tformat; tformat.format = RD::DATA_FORMAT_R8_UNORM; tformat.width = size.width; tformat.height = size.height; tformat.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; tformat.texture_type = RD::TEXTURE_TYPE_2D; rt->sdf_buffer_write = RD::get_singleton()->texture_create(tformat, RD::TextureView()); { Vector write_fb; write_fb.push_back(rt->sdf_buffer_write); rt->sdf_buffer_write_fb = RD::get_singleton()->framebuffer_create(write_fb); } int scale; switch (rt->sdf_scale) { case RS::VIEWPORT_SDF_SCALE_100_PERCENT: { scale = 100; } break; case RS::VIEWPORT_SDF_SCALE_50_PERCENT: { scale = 50; } break; case RS::VIEWPORT_SDF_SCALE_25_PERCENT: { scale = 25; } break; default: { scale = 100; } break; } rt->process_size = size * scale / 100; 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_SINT; tformat.width = rt->process_size.width; tformat.height = rt->process_size.height; tformat.usage_bits = RD::TEXTURE_USAGE_STORAGE_BIT; 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_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()); { Vector uniforms; { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_IMAGE; u.binding = 1; u.append_id(rt->sdf_buffer_write); uniforms.push_back(u); } { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_IMAGE; u.binding = 2; u.append_id(rt->sdf_buffer_read); uniforms.push_back(u); } { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_IMAGE; u.binding = 3; u.append_id(rt->sdf_buffer_process[0]); uniforms.push_back(u); } { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_IMAGE; u.binding = 4; u.append_id(rt->sdf_buffer_process[1]); uniforms.push_back(u); } rt->sdf_buffer_process_uniform_sets[0] = RD::get_singleton()->uniform_set_create(uniforms, rt_sdf.shader.version_get_shader(rt_sdf.shader_version, 0), 0); RID aux2 = uniforms.write[2].get_id(0); RID aux3 = uniforms.write[3].get_id(0); uniforms.write[2].set_id(0, aux3); uniforms.write[3].set_id(0, aux2); rt->sdf_buffer_process_uniform_sets[1] = RD::get_singleton()->uniform_set_create(uniforms, rt_sdf.shader.version_get_shader(rt_sdf.shader_version, 0), 0); } } void RendererStorageRD::_render_target_clear_sdf(RenderTarget *rt) { if (rt->sdf_buffer_read.is_valid()) { RD::get_singleton()->free(rt->sdf_buffer_read); rt->sdf_buffer_read = RID(); } if (rt->sdf_buffer_write_fb.is_valid()) { RD::get_singleton()->free(rt->sdf_buffer_write); RD::get_singleton()->free(rt->sdf_buffer_process[0]); RD::get_singleton()->free(rt->sdf_buffer_process[1]); rt->sdf_buffer_write = RID(); rt->sdf_buffer_write_fb = RID(); rt->sdf_buffer_process[0] = RID(); rt->sdf_buffer_process[1] = RID(); rt->sdf_buffer_process_uniform_sets[0] = RID(); rt->sdf_buffer_process_uniform_sets[1] = RID(); } } RID RendererStorageRD::render_target_get_sdf_framebuffer(RID p_render_target) { RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_COND_V(!rt, RID()); if (rt->sdf_buffer_write_fb.is_null()) { _render_target_allocate_sdf(rt); } return rt->sdf_buffer_write_fb; } void RendererStorageRD::render_target_sdf_process(RID p_render_target) { RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_COND(!rt); ERR_FAIL_COND(rt->sdf_buffer_write_fb.is_null()); RenderTargetSDF::PushConstant push_constant; Rect2i r = _render_target_get_sdf_rect(rt); push_constant.size[0] = r.size.width; push_constant.size[1] = r.size.height; push_constant.stride = 0; push_constant.shift = 0; push_constant.base_size[0] = r.size.width; push_constant.base_size[1] = r.size.height; bool shrink = false; switch (rt->sdf_scale) { case RS::VIEWPORT_SDF_SCALE_50_PERCENT: { push_constant.size[0] >>= 1; push_constant.size[1] >>= 1; push_constant.shift = 1; shrink = true; } break; case RS::VIEWPORT_SDF_SCALE_25_PERCENT: { push_constant.size[0] >>= 2; push_constant.size[1] >>= 2; push_constant.shift = 2; shrink = true; } break; default: { }; } RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); /* Load */ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, rt_sdf.pipelines[shrink ? RenderTargetSDF::SHADER_LOAD_SHRINK : RenderTargetSDF::SHADER_LOAD]); RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rt->sdf_buffer_process_uniform_sets[1], 0); //fill [0] RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(RenderTargetSDF::PushConstant)); RD::get_singleton()->compute_list_dispatch_threads(compute_list, push_constant.size[0], push_constant.size[1], 1); /* Process */ int stride = nearest_power_of_2_templated(MAX(push_constant.size[0], push_constant.size[1]) / 2); RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, rt_sdf.pipelines[RenderTargetSDF::SHADER_PROCESS]); RD::get_singleton()->compute_list_add_barrier(compute_list); bool swap = false; //jumpflood while (stride > 0) { RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rt->sdf_buffer_process_uniform_sets[swap ? 1 : 0], 0); push_constant.stride = stride; RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(RenderTargetSDF::PushConstant)); RD::get_singleton()->compute_list_dispatch_threads(compute_list, push_constant.size[0], push_constant.size[1], 1); stride /= 2; swap = !swap; RD::get_singleton()->compute_list_add_barrier(compute_list); } /* Store */ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, rt_sdf.pipelines[shrink ? RenderTargetSDF::SHADER_STORE_SHRINK : RenderTargetSDF::SHADER_STORE]); RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rt->sdf_buffer_process_uniform_sets[swap ? 1 : 0], 0); RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(RenderTargetSDF::PushConstant)); RD::get_singleton()->compute_list_dispatch_threads(compute_list, push_constant.size[0], push_constant.size[1], 1); RD::get_singleton()->compute_list_end(); } void RendererStorageRD::render_target_copy_to_back_buffer(RID p_render_target, const Rect2i &p_region, bool p_gen_mipmaps) { RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_COND(!rt); if (!rt->backbuffer.is_valid()) { _create_render_target_backbuffer(rt); } Rect2i region; if (p_region == Rect2i()) { region.size = rt->size; } else { region = Rect2i(Size2i(), rt->size).intersection(p_region); if (region.size == Size2i()) { return; //nothing to do } } //single texture copy for backbuffer //RD::get_singleton()->texture_copy(rt->color, rt->backbuffer_mipmap0, Vector3(region.position.x, region.position.y, 0), Vector3(region.position.x, region.position.y, 0), Vector3(region.size.x, region.size.y, 1), 0, 0, 0, 0, true); effects->copy_to_rect(rt->color, rt->backbuffer_mipmap0, region, false, false, false, true, true); if (!p_gen_mipmaps) { return; } RD::get_singleton()->draw_command_begin_label("Gaussian Blur Mipmaps"); //then mipmap blur RID prev_texture = rt->color; //use color, not backbuffer, as bb has mipmaps. for (int i = 0; i < rt->backbuffer_mipmaps.size(); i++) { region.position.x >>= 1; region.position.y >>= 1; region.size.x = MAX(1, region.size.x >> 1); region.size.y = MAX(1, region.size.y >> 1); RID mipmap = rt->backbuffer_mipmaps[i]; effects->gaussian_blur(prev_texture, mipmap, region, true); prev_texture = mipmap; } RD::get_singleton()->draw_command_end_label(); } void RendererStorageRD::render_target_clear_back_buffer(RID p_render_target, const Rect2i &p_region, const Color &p_color) { RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_COND(!rt); if (!rt->backbuffer.is_valid()) { _create_render_target_backbuffer(rt); } Rect2i region; if (p_region == Rect2i()) { region.size = rt->size; } else { region = Rect2i(Size2i(), rt->size).intersection(p_region); if (region.size == Size2i()) { return; //nothing to do } } //single texture copy for backbuffer effects->set_color(rt->backbuffer_mipmap0, p_color, region, true); } void RendererStorageRD::render_target_gen_back_buffer_mipmaps(RID p_render_target, const Rect2i &p_region) { RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_COND(!rt); if (!rt->backbuffer.is_valid()) { _create_render_target_backbuffer(rt); } Rect2i region; if (p_region == Rect2i()) { region.size = rt->size; } else { region = Rect2i(Size2i(), rt->size).intersection(p_region); if (region.size == Size2i()) { return; //nothing to do } } RD::get_singleton()->draw_command_begin_label("Gaussian Blur Mipmaps2"); //then mipmap blur RID prev_texture = rt->backbuffer_mipmap0; for (int i = 0; i < rt->backbuffer_mipmaps.size(); i++) { region.position.x >>= 1; region.position.y >>= 1; region.size.x = MAX(1, region.size.x >> 1); region.size.y = MAX(1, region.size.y >> 1); RID mipmap = rt->backbuffer_mipmaps[i]; effects->gaussian_blur(prev_texture, mipmap, region, true); prev_texture = mipmap; } RD::get_singleton()->draw_command_end_label(); } RID RendererStorageRD::render_target_get_framebuffer_uniform_set(RID p_render_target) { RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_COND_V(!rt, RID()); return rt->framebuffer_uniform_set; } RID RendererStorageRD::render_target_get_backbuffer_uniform_set(RID p_render_target) { RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_COND_V(!rt, RID()); return rt->backbuffer_uniform_set; } void RendererStorageRD::render_target_set_framebuffer_uniform_set(RID p_render_target, RID p_uniform_set) { RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_COND(!rt); rt->framebuffer_uniform_set = p_uniform_set; } void RendererStorageRD::render_target_set_backbuffer_uniform_set(RID p_render_target, RID p_uniform_set) { RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_COND(!rt); rt->backbuffer_uniform_set = p_uniform_set; } void RendererStorageRD::base_update_dependency(RID p_base, DependencyTracker *p_instance) { if (mesh_owner.owns(p_base)) { Mesh *mesh = mesh_owner.get_or_null(p_base); p_instance->update_dependency(&mesh->dependency); } else if (multimesh_owner.owns(p_base)) { MultiMesh *multimesh = multimesh_owner.get_or_null(p_base); p_instance->update_dependency(&multimesh->dependency); if (multimesh->mesh.is_valid()) { base_update_dependency(multimesh->mesh, p_instance); } } else if (reflection_probe_owner.owns(p_base)) { ReflectionProbe *rp = reflection_probe_owner.get_or_null(p_base); p_instance->update_dependency(&rp->dependency); } else if (decal_owner.owns(p_base)) { Decal *decal = decal_owner.get_or_null(p_base); p_instance->update_dependency(&decal->dependency); } else if (voxel_gi_owner.owns(p_base)) { VoxelGI *gip = voxel_gi_owner.get_or_null(p_base); p_instance->update_dependency(&gip->dependency); } else if (lightmap_owner.owns(p_base)) { Lightmap *lm = lightmap_owner.get_or_null(p_base); p_instance->update_dependency(&lm->dependency); } else if (light_owner.owns(p_base)) { Light *l = light_owner.get_or_null(p_base); p_instance->update_dependency(&l->dependency); } else if (particles_owner.owns(p_base)) { Particles *p = particles_owner.get_or_null(p_base); p_instance->update_dependency(&p->dependency); } else if (particles_collision_owner.owns(p_base)) { ParticlesCollision *pc = particles_collision_owner.get_or_null(p_base); p_instance->update_dependency(&pc->dependency); } else if (fog_volume_owner.owns(p_base)) { FogVolume *fv = fog_volume_owner.get_or_null(p_base); p_instance->update_dependency(&fv->dependency); } else if (visibility_notifier_owner.owns(p_base)) { VisibilityNotifier *vn = visibility_notifier_owner.get_or_null(p_base); p_instance->update_dependency(&vn->dependency); } } void RendererStorageRD::skeleton_update_dependency(RID p_skeleton, DependencyTracker *p_instance) { Skeleton *skeleton = skeleton_owner.get_or_null(p_skeleton); ERR_FAIL_COND(!skeleton); p_instance->update_dependency(&skeleton->dependency); } RS::InstanceType RendererStorageRD::get_base_type(RID p_rid) const { if (mesh_owner.owns(p_rid)) { return RS::INSTANCE_MESH; } if (multimesh_owner.owns(p_rid)) { return RS::INSTANCE_MULTIMESH; } if (reflection_probe_owner.owns(p_rid)) { return RS::INSTANCE_REFLECTION_PROBE; } if (decal_owner.owns(p_rid)) { return RS::INSTANCE_DECAL; } if (voxel_gi_owner.owns(p_rid)) { return RS::INSTANCE_VOXEL_GI; } if (light_owner.owns(p_rid)) { return RS::INSTANCE_LIGHT; } if (lightmap_owner.owns(p_rid)) { return RS::INSTANCE_LIGHTMAP; } if (particles_owner.owns(p_rid)) { return RS::INSTANCE_PARTICLES; } if (particles_collision_owner.owns(p_rid)) { return RS::INSTANCE_PARTICLES_COLLISION; } if (fog_volume_owner.owns(p_rid)) { return RS::INSTANCE_FOG_VOLUME; } if (visibility_notifier_owner.owns(p_rid)) { return RS::INSTANCE_VISIBLITY_NOTIFIER; } return RS::INSTANCE_NONE; } void RendererStorageRD::texture_add_to_decal_atlas(RID p_texture, bool p_panorama_to_dp) { if (!decal_atlas.textures.has(p_texture)) { DecalAtlas::Texture t; t.users = 1; t.panorama_to_dp_users = p_panorama_to_dp ? 1 : 0; decal_atlas.textures[p_texture] = t; decal_atlas.dirty = true; } else { DecalAtlas::Texture *t = decal_atlas.textures.getptr(p_texture); t->users++; if (p_panorama_to_dp) { t->panorama_to_dp_users++; } } } void RendererStorageRD::texture_remove_from_decal_atlas(RID p_texture, bool p_panorama_to_dp) { DecalAtlas::Texture *t = decal_atlas.textures.getptr(p_texture); ERR_FAIL_COND(!t); t->users--; if (p_panorama_to_dp) { ERR_FAIL_COND(t->panorama_to_dp_users == 0); t->panorama_to_dp_users--; } if (t->users == 0) { decal_atlas.textures.erase(p_texture); //do not mark it dirty, there is no need to since it remains working } } RID RendererStorageRD::decal_atlas_get_texture() const { return decal_atlas.texture; } RID RendererStorageRD::decal_atlas_get_texture_srgb() const { return decal_atlas.texture_srgb; } void RendererStorageRD::_update_decal_atlas() { if (!decal_atlas.dirty) { return; //nothing to do } decal_atlas.dirty = false; if (decal_atlas.texture.is_valid()) { RD::get_singleton()->free(decal_atlas.texture); decal_atlas.texture = RID(); decal_atlas.texture_srgb = RID(); decal_atlas.texture_mipmaps.clear(); } int border = 1 << decal_atlas.mipmaps; if (decal_atlas.textures.size()) { //generate atlas Vector itemsv; itemsv.resize(decal_atlas.textures.size()); int base_size = 8; const RID *K = nullptr; int idx = 0; while ((K = decal_atlas.textures.next(K))) { DecalAtlas::SortItem &si = itemsv.write[idx]; Texture *src_tex = texture_owner.get_or_null(*K); si.size.width = (src_tex->width / border) + 1; si.size.height = (src_tex->height / border) + 1; si.pixel_size = Size2i(src_tex->width, src_tex->height); if (base_size < si.size.width) { base_size = nearest_power_of_2_templated(si.size.width); } si.texture = *K; idx++; } //sort items by size itemsv.sort(); //attempt to create atlas int item_count = itemsv.size(); DecalAtlas::SortItem *items = itemsv.ptrw(); int atlas_height = 0; while (true) { Vector v_offsetsv; v_offsetsv.resize(base_size); int *v_offsets = v_offsetsv.ptrw(); memset(v_offsets, 0, sizeof(int) * base_size); int max_height = 0; for (int i = 0; i < item_count; i++) { //best fit DecalAtlas::SortItem &si = items[i]; int best_idx = -1; int best_height = 0x7FFFFFFF; for (int j = 0; j <= base_size - si.size.width; j++) { int height = 0; for (int k = 0; k < si.size.width; k++) { int h = v_offsets[k + j]; if (h > height) { height = h; if (height > best_height) { break; //already bad } } } if (height < best_height) { best_height = height; best_idx = j; } } //update for (int k = 0; k < si.size.width; k++) { v_offsets[k + best_idx] = best_height + si.size.height; } si.pos.x = best_idx; si.pos.y = best_height; if (si.pos.y + si.size.height > max_height) { max_height = si.pos.y + si.size.height; } } if (max_height <= base_size * 2) { atlas_height = max_height; break; //good ratio, break; } base_size *= 2; } decal_atlas.size.width = base_size * border; decal_atlas.size.height = nearest_power_of_2_templated(atlas_height * border); for (int i = 0; i < item_count; i++) { DecalAtlas::Texture *t = decal_atlas.textures.getptr(items[i].texture); t->uv_rect.position = items[i].pos * border + Vector2i(border / 2, border / 2); t->uv_rect.size = items[i].pixel_size; t->uv_rect.position /= Size2(decal_atlas.size); t->uv_rect.size /= Size2(decal_atlas.size); } } else { //use border as size, so it at least has enough mipmaps decal_atlas.size.width = border; decal_atlas.size.height = border; } //blit textures RD::TextureFormat tformat; tformat.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; tformat.width = decal_atlas.size.width; tformat.height = decal_atlas.size.height; tformat.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT; tformat.texture_type = RD::TEXTURE_TYPE_2D; tformat.mipmaps = decal_atlas.mipmaps; tformat.shareable_formats.push_back(RD::DATA_FORMAT_R8G8B8A8_UNORM); tformat.shareable_formats.push_back(RD::DATA_FORMAT_R8G8B8A8_SRGB); decal_atlas.texture = RD::get_singleton()->texture_create(tformat, RD::TextureView()); RD::get_singleton()->texture_clear(decal_atlas.texture, Color(0, 0, 0, 0), 0, decal_atlas.mipmaps, 0, 1); { //create the framebuffer Size2i s = decal_atlas.size; for (int i = 0; i < decal_atlas.mipmaps; i++) { DecalAtlas::MipMap mm; mm.texture = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), decal_atlas.texture, 0, i); Vector fb; fb.push_back(mm.texture); mm.fb = RD::get_singleton()->framebuffer_create(fb); mm.size = s; decal_atlas.texture_mipmaps.push_back(mm); s.width = MAX(1, s.width >> 1); s.height = MAX(1, s.height >> 1); } { //create the SRGB variant RD::TextureView rd_view; rd_view.format_override = RD::DATA_FORMAT_R8G8B8A8_SRGB; decal_atlas.texture_srgb = RD::get_singleton()->texture_create_shared(rd_view, decal_atlas.texture); } } RID prev_texture; for (int i = 0; i < decal_atlas.texture_mipmaps.size(); i++) { const DecalAtlas::MipMap &mm = decal_atlas.texture_mipmaps[i]; Color clear_color(0, 0, 0, 0); if (decal_atlas.textures.size()) { if (i == 0) { Vector cc; cc.push_back(clear_color); RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(mm.fb, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_DROP, RD::FINAL_ACTION_DISCARD, cc); const RID *K = nullptr; while ((K = decal_atlas.textures.next(K))) { DecalAtlas::Texture *t = decal_atlas.textures.getptr(*K); Texture *src_tex = texture_owner.get_or_null(*K); effects->copy_to_atlas_fb(src_tex->rd_texture, mm.fb, t->uv_rect, draw_list, false, t->panorama_to_dp_users > 0); } RD::get_singleton()->draw_list_end(); prev_texture = mm.texture; } else { effects->copy_to_fb_rect(prev_texture, mm.fb, Rect2i(Point2i(), mm.size)); prev_texture = mm.texture; } } else { RD::get_singleton()->texture_clear(mm.texture, clear_color, 0, 1, 0, 1); } } } int32_t RendererStorageRD::_global_variable_allocate(uint32_t p_elements) { int32_t idx = 0; while (idx + p_elements <= global_variables.buffer_size) { if (global_variables.buffer_usage[idx].elements == 0) { bool valid = true; for (uint32_t i = 1; i < p_elements; i++) { if (global_variables.buffer_usage[idx + i].elements > 0) { valid = false; idx += i + global_variables.buffer_usage[idx + i].elements; break; } } if (!valid) { continue; //if not valid, idx is in new position } return idx; } else { idx += global_variables.buffer_usage[idx].elements; } } return -1; } void RendererStorageRD::_global_variable_store_in_buffer(int32_t p_index, RS::GlobalVariableType p_type, const Variant &p_value) { switch (p_type) { case RS::GLOBAL_VAR_TYPE_BOOL: { GlobalVariables::Value &bv = global_variables.buffer_values[p_index]; bool b = p_value; bv.x = b ? 1.0 : 0.0; bv.y = 0.0; bv.z = 0.0; bv.w = 0.0; } break; case RS::GLOBAL_VAR_TYPE_BVEC2: { GlobalVariables::Value &bv = global_variables.buffer_values[p_index]; uint32_t bvec = p_value; bv.x = (bvec & 1) ? 1.0 : 0.0; bv.y = (bvec & 2) ? 1.0 : 0.0; bv.z = 0.0; bv.w = 0.0; } break; case RS::GLOBAL_VAR_TYPE_BVEC3: { GlobalVariables::Value &bv = global_variables.buffer_values[p_index]; uint32_t bvec = p_value; bv.x = (bvec & 1) ? 1.0 : 0.0; bv.y = (bvec & 2) ? 1.0 : 0.0; bv.z = (bvec & 4) ? 1.0 : 0.0; bv.w = 0.0; } break; case RS::GLOBAL_VAR_TYPE_BVEC4: { GlobalVariables::Value &bv = global_variables.buffer_values[p_index]; uint32_t bvec = p_value; bv.x = (bvec & 1) ? 1.0 : 0.0; bv.y = (bvec & 2) ? 1.0 : 0.0; bv.z = (bvec & 4) ? 1.0 : 0.0; bv.w = (bvec & 8) ? 1.0 : 0.0; } break; case RS::GLOBAL_VAR_TYPE_INT: { GlobalVariables::ValueInt &bv = *(GlobalVariables::ValueInt *)&global_variables.buffer_values[p_index]; int32_t v = p_value; bv.x = v; bv.y = 0; bv.z = 0; bv.w = 0; } break; case RS::GLOBAL_VAR_TYPE_IVEC2: { GlobalVariables::ValueInt &bv = *(GlobalVariables::ValueInt *)&global_variables.buffer_values[p_index]; Vector2i v = p_value; bv.x = v.x; bv.y = v.y; bv.z = 0; bv.w = 0; } break; case RS::GLOBAL_VAR_TYPE_IVEC3: { GlobalVariables::ValueInt &bv = *(GlobalVariables::ValueInt *)&global_variables.buffer_values[p_index]; Vector3i v = p_value; bv.x = v.x; bv.y = v.y; bv.z = v.z; bv.w = 0; } break; case RS::GLOBAL_VAR_TYPE_IVEC4: { GlobalVariables::ValueInt &bv = *(GlobalVariables::ValueInt *)&global_variables.buffer_values[p_index]; Vector v = p_value; bv.x = v.size() >= 1 ? v[0] : 0; bv.y = v.size() >= 2 ? v[1] : 0; bv.z = v.size() >= 3 ? v[2] : 0; bv.w = v.size() >= 4 ? v[3] : 0; } break; case RS::GLOBAL_VAR_TYPE_RECT2I: { GlobalVariables::ValueInt &bv = *(GlobalVariables::ValueInt *)&global_variables.buffer_values[p_index]; Rect2i v = p_value; bv.x = v.position.x; bv.y = v.position.y; bv.z = v.size.x; bv.w = v.size.y; } break; case RS::GLOBAL_VAR_TYPE_UINT: { GlobalVariables::ValueUInt &bv = *(GlobalVariables::ValueUInt *)&global_variables.buffer_values[p_index]; uint32_t v = p_value; bv.x = v; bv.y = 0; bv.z = 0; bv.w = 0; } break; case RS::GLOBAL_VAR_TYPE_UVEC2: { GlobalVariables::ValueUInt &bv = *(GlobalVariables::ValueUInt *)&global_variables.buffer_values[p_index]; Vector2i v = p_value; bv.x = v.x; bv.y = v.y; bv.z = 0; bv.w = 0; } break; case RS::GLOBAL_VAR_TYPE_UVEC3: { GlobalVariables::ValueUInt &bv = *(GlobalVariables::ValueUInt *)&global_variables.buffer_values[p_index]; Vector3i v = p_value; bv.x = v.x; bv.y = v.y; bv.z = v.z; bv.w = 0; } break; case RS::GLOBAL_VAR_TYPE_UVEC4: { GlobalVariables::ValueUInt &bv = *(GlobalVariables::ValueUInt *)&global_variables.buffer_values[p_index]; Vector v = p_value; bv.x = v.size() >= 1 ? v[0] : 0; bv.y = v.size() >= 2 ? v[1] : 0; bv.z = v.size() >= 3 ? v[2] : 0; bv.w = v.size() >= 4 ? v[3] : 0; } break; case RS::GLOBAL_VAR_TYPE_FLOAT: { GlobalVariables::Value &bv = global_variables.buffer_values[p_index]; float v = p_value; bv.x = v; bv.y = 0; bv.z = 0; bv.w = 0; } break; case RS::GLOBAL_VAR_TYPE_VEC2: { GlobalVariables::Value &bv = global_variables.buffer_values[p_index]; Vector2 v = p_value; bv.x = v.x; bv.y = v.y; bv.z = 0; bv.w = 0; } break; case RS::GLOBAL_VAR_TYPE_VEC3: { GlobalVariables::Value &bv = global_variables.buffer_values[p_index]; Vector3 v = p_value; bv.x = v.x; bv.y = v.y; bv.z = v.z; bv.w = 0; } break; case RS::GLOBAL_VAR_TYPE_VEC4: { GlobalVariables::Value &bv = global_variables.buffer_values[p_index]; Plane v = p_value; bv.x = v.normal.x; bv.y = v.normal.y; bv.z = v.normal.z; bv.w = v.d; } break; case RS::GLOBAL_VAR_TYPE_COLOR: { GlobalVariables::Value &bv = global_variables.buffer_values[p_index]; Color v = p_value; bv.x = v.r; bv.y = v.g; bv.z = v.b; bv.w = v.a; GlobalVariables::Value &bv_linear = global_variables.buffer_values[p_index + 1]; v = v.to_linear(); bv_linear.x = v.r; bv_linear.y = v.g; bv_linear.z = v.b; bv_linear.w = v.a; } break; case RS::GLOBAL_VAR_TYPE_RECT2: { GlobalVariables::Value &bv = global_variables.buffer_values[p_index]; Rect2 v = p_value; bv.x = v.position.x; bv.y = v.position.y; bv.z = v.size.x; bv.w = v.size.y; } break; case RS::GLOBAL_VAR_TYPE_MAT2: { GlobalVariables::Value *bv = &global_variables.buffer_values[p_index]; Vector m2 = p_value; if (m2.size() < 4) { m2.resize(4); } bv[0].x = m2[0]; bv[0].y = m2[1]; bv[0].z = 0; bv[0].w = 0; bv[1].x = m2[2]; bv[1].y = m2[3]; bv[1].z = 0; bv[1].w = 0; } break; case RS::GLOBAL_VAR_TYPE_MAT3: { GlobalVariables::Value *bv = &global_variables.buffer_values[p_index]; Basis v = p_value; bv[0].x = v.elements[0][0]; bv[0].y = v.elements[1][0]; bv[0].z = v.elements[2][0]; bv[0].w = 0; bv[1].x = v.elements[0][1]; bv[1].y = v.elements[1][1]; bv[1].z = v.elements[2][1]; bv[1].w = 0; bv[2].x = v.elements[0][2]; bv[2].y = v.elements[1][2]; bv[2].z = v.elements[2][2]; bv[2].w = 0; } break; case RS::GLOBAL_VAR_TYPE_MAT4: { GlobalVariables::Value *bv = &global_variables.buffer_values[p_index]; Vector m2 = p_value; if (m2.size() < 16) { m2.resize(16); } bv[0].x = m2[0]; bv[0].y = m2[1]; bv[0].z = m2[2]; bv[0].w = m2[3]; bv[1].x = m2[4]; bv[1].y = m2[5]; bv[1].z = m2[6]; bv[1].w = m2[7]; bv[2].x = m2[8]; bv[2].y = m2[9]; bv[2].z = m2[10]; bv[2].w = m2[11]; bv[3].x = m2[12]; bv[3].y = m2[13]; bv[3].z = m2[14]; bv[3].w = m2[15]; } break; case RS::GLOBAL_VAR_TYPE_TRANSFORM_2D: { GlobalVariables::Value *bv = &global_variables.buffer_values[p_index]; Transform2D v = p_value; bv[0].x = v.elements[0][0]; bv[0].y = v.elements[0][1]; bv[0].z = 0; bv[0].w = 0; bv[1].x = v.elements[1][0]; bv[1].y = v.elements[1][1]; bv[1].z = 0; bv[1].w = 0; bv[2].x = v.elements[2][0]; bv[2].y = v.elements[2][1]; bv[2].z = 1; bv[2].w = 0; } break; case RS::GLOBAL_VAR_TYPE_TRANSFORM: { GlobalVariables::Value *bv = &global_variables.buffer_values[p_index]; Transform3D v = p_value; bv[0].x = v.basis.elements[0][0]; bv[0].y = v.basis.elements[1][0]; bv[0].z = v.basis.elements[2][0]; bv[0].w = 0; bv[1].x = v.basis.elements[0][1]; bv[1].y = v.basis.elements[1][1]; bv[1].z = v.basis.elements[2][1]; bv[1].w = 0; bv[2].x = v.basis.elements[0][2]; bv[2].y = v.basis.elements[1][2]; bv[2].z = v.basis.elements[2][2]; bv[2].w = 0; bv[3].x = v.origin.x; bv[3].y = v.origin.y; bv[3].z = v.origin.z; bv[3].w = 1; } break; default: { ERR_FAIL(); } } } void RendererStorageRD::_global_variable_mark_buffer_dirty(int32_t p_index, int32_t p_elements) { int32_t prev_chunk = -1; for (int32_t i = 0; i < p_elements; i++) { int32_t chunk = (p_index + i) / GlobalVariables::BUFFER_DIRTY_REGION_SIZE; if (chunk != prev_chunk) { if (!global_variables.buffer_dirty_regions[chunk]) { global_variables.buffer_dirty_regions[chunk] = true; global_variables.buffer_dirty_region_count++; } } prev_chunk = chunk; } } void RendererStorageRD::global_variable_add(const StringName &p_name, RS::GlobalVariableType p_type, const Variant &p_value) { ERR_FAIL_COND(global_variables.variables.has(p_name)); GlobalVariables::Variable gv; gv.type = p_type; gv.value = p_value; gv.buffer_index = -1; if (p_type >= RS::GLOBAL_VAR_TYPE_SAMPLER2D) { //is texture global_variables.must_update_texture_materials = true; //normally there are none } else { gv.buffer_elements = 1; if (p_type == RS::GLOBAL_VAR_TYPE_COLOR || p_type == RS::GLOBAL_VAR_TYPE_MAT2) { //color needs to elements to store srgb and linear gv.buffer_elements = 2; } if (p_type == RS::GLOBAL_VAR_TYPE_MAT3 || p_type == RS::GLOBAL_VAR_TYPE_TRANSFORM_2D) { //color needs to elements to store srgb and linear gv.buffer_elements = 3; } if (p_type == RS::GLOBAL_VAR_TYPE_MAT4 || p_type == RS::GLOBAL_VAR_TYPE_TRANSFORM) { //color needs to elements to store srgb and linear gv.buffer_elements = 4; } //is vector, allocate in buffer and update index gv.buffer_index = _global_variable_allocate(gv.buffer_elements); ERR_FAIL_COND_MSG(gv.buffer_index < 0, vformat("Failed allocating global variable '%s' out of buffer memory. Consider increasing it in the Project Settings.", String(p_name))); global_variables.buffer_usage[gv.buffer_index].elements = gv.buffer_elements; _global_variable_store_in_buffer(gv.buffer_index, gv.type, gv.value); _global_variable_mark_buffer_dirty(gv.buffer_index, gv.buffer_elements); global_variables.must_update_buffer_materials = true; //normally there are none } global_variables.variables[p_name] = gv; } void RendererStorageRD::global_variable_remove(const StringName &p_name) { if (!global_variables.variables.has(p_name)) { return; } GlobalVariables::Variable &gv = global_variables.variables[p_name]; if (gv.buffer_index >= 0) { global_variables.buffer_usage[gv.buffer_index].elements = 0; global_variables.must_update_buffer_materials = true; } else { global_variables.must_update_texture_materials = true; } global_variables.variables.erase(p_name); } Vector RendererStorageRD::global_variable_get_list() const { if (!Engine::get_singleton()->is_editor_hint()) { ERR_FAIL_V_MSG(Vector(), "This function should never be used outside the editor, it can severely damage performance."); } const StringName *K = nullptr; Vector names; while ((K = global_variables.variables.next(K))) { names.push_back(*K); } names.sort_custom(); return names; } void RendererStorageRD::global_variable_set(const StringName &p_name, const Variant &p_value) { ERR_FAIL_COND(!global_variables.variables.has(p_name)); GlobalVariables::Variable &gv = global_variables.variables[p_name]; gv.value = p_value; if (gv.override.get_type() == Variant::NIL) { if (gv.buffer_index >= 0) { //buffer _global_variable_store_in_buffer(gv.buffer_index, gv.type, gv.value); _global_variable_mark_buffer_dirty(gv.buffer_index, gv.buffer_elements); } else { //texture for (Set::Element *E = gv.texture_materials.front(); E; E = E->next()) { Material *material = material_owner.get_or_null(E->get()); ERR_CONTINUE(!material); _material_queue_update(material, false, true); } } } } void RendererStorageRD::global_variable_set_override(const StringName &p_name, const Variant &p_value) { if (!global_variables.variables.has(p_name)) { return; //variable may not exist } ERR_FAIL_COND(p_value.get_type() == Variant::OBJECT); GlobalVariables::Variable &gv = global_variables.variables[p_name]; gv.override = p_value; if (gv.buffer_index >= 0) { //buffer if (gv.override.get_type() == Variant::NIL) { _global_variable_store_in_buffer(gv.buffer_index, gv.type, gv.value); } else { _global_variable_store_in_buffer(gv.buffer_index, gv.type, gv.override); } _global_variable_mark_buffer_dirty(gv.buffer_index, gv.buffer_elements); } else { //texture for (Set::Element *E = gv.texture_materials.front(); E; E = E->next()) { Material *material = material_owner.get_or_null(E->get()); ERR_CONTINUE(!material); _material_queue_update(material, false, true); } } } Variant RendererStorageRD::global_variable_get(const StringName &p_name) const { if (!Engine::get_singleton()->is_editor_hint()) { ERR_FAIL_V_MSG(Variant(), "This function should never be used outside the editor, it can severely damage performance."); } if (!global_variables.variables.has(p_name)) { return Variant(); } return global_variables.variables[p_name].value; } RS::GlobalVariableType RendererStorageRD::global_variable_get_type_internal(const StringName &p_name) const { if (!global_variables.variables.has(p_name)) { return RS::GLOBAL_VAR_TYPE_MAX; } return global_variables.variables[p_name].type; } RS::GlobalVariableType RendererStorageRD::global_variable_get_type(const StringName &p_name) const { if (!Engine::get_singleton()->is_editor_hint()) { ERR_FAIL_V_MSG(RS::GLOBAL_VAR_TYPE_MAX, "This function should never be used outside the editor, it can severely damage performance."); } return global_variable_get_type_internal(p_name); } void RendererStorageRD::global_variables_load_settings(bool p_load_textures) { List settings; ProjectSettings::get_singleton()->get_property_list(&settings); for (const PropertyInfo &E : settings) { if (E.name.begins_with("shader_globals/")) { StringName name = E.name.get_slice("/", 1); Dictionary d = ProjectSettings::get_singleton()->get(E.name); ERR_CONTINUE(!d.has("type")); ERR_CONTINUE(!d.has("value")); String type = d["type"]; static const char *global_var_type_names[RS::GLOBAL_VAR_TYPE_MAX] = { "bool", "bvec2", "bvec3", "bvec4", "int", "ivec2", "ivec3", "ivec4", "rect2i", "uint", "uvec2", "uvec3", "uvec4", "float", "vec2", "vec3", "vec4", "color", "rect2", "mat2", "mat3", "mat4", "transform_2d", "transform", "sampler2D", "sampler2DArray", "sampler3D", "samplerCube", }; RS::GlobalVariableType gvtype = RS::GLOBAL_VAR_TYPE_MAX; for (int i = 0; i < RS::GLOBAL_VAR_TYPE_MAX; i++) { if (global_var_type_names[i] == type) { gvtype = RS::GlobalVariableType(i); break; } } ERR_CONTINUE(gvtype == RS::GLOBAL_VAR_TYPE_MAX); //type invalid Variant value = d["value"]; if (gvtype >= RS::GLOBAL_VAR_TYPE_SAMPLER2D) { //textire if (!p_load_textures) { value = RID(); continue; } String path = value; RES resource = ResourceLoader::load(path); ERR_CONTINUE(resource.is_null()); value = resource; } if (global_variables.variables.has(name)) { //has it, update it global_variable_set(name, value); } else { global_variable_add(name, gvtype, value); } } } } void RendererStorageRD::global_variables_clear() { global_variables.variables.clear(); //not right but for now enough } RID RendererStorageRD::global_variables_get_storage_buffer() const { return global_variables.buffer; } int32_t RendererStorageRD::global_variables_instance_allocate(RID p_instance) { ERR_FAIL_COND_V(global_variables.instance_buffer_pos.has(p_instance), -1); int32_t pos = _global_variable_allocate(ShaderLanguage::MAX_INSTANCE_UNIFORM_INDICES); global_variables.instance_buffer_pos[p_instance] = pos; //save anyway ERR_FAIL_COND_V_MSG(pos < 0, -1, "Too many instances using shader instance variables. Increase buffer size in Project Settings."); global_variables.buffer_usage[pos].elements = ShaderLanguage::MAX_INSTANCE_UNIFORM_INDICES; return pos; } void RendererStorageRD::global_variables_instance_free(RID p_instance) { ERR_FAIL_COND(!global_variables.instance_buffer_pos.has(p_instance)); int32_t pos = global_variables.instance_buffer_pos[p_instance]; if (pos >= 0) { global_variables.buffer_usage[pos].elements = 0; } global_variables.instance_buffer_pos.erase(p_instance); } void RendererStorageRD::global_variables_instance_update(RID p_instance, int p_index, const Variant &p_value) { if (!global_variables.instance_buffer_pos.has(p_instance)) { return; //just not allocated, ignore } int32_t pos = global_variables.instance_buffer_pos[p_instance]; if (pos < 0) { return; //again, not allocated, ignore } ERR_FAIL_INDEX(p_index, ShaderLanguage::MAX_INSTANCE_UNIFORM_INDICES); ERR_FAIL_COND_MSG(p_value.get_type() > Variant::COLOR, "Unsupported variant type for instance parameter: " + Variant::get_type_name(p_value.get_type())); //anything greater not supported ShaderLanguage::DataType datatype_from_value[Variant::COLOR + 1] = { ShaderLanguage::TYPE_MAX, //nil ShaderLanguage::TYPE_BOOL, //bool ShaderLanguage::TYPE_INT, //int ShaderLanguage::TYPE_FLOAT, //float ShaderLanguage::TYPE_MAX, //string ShaderLanguage::TYPE_VEC2, //vec2 ShaderLanguage::TYPE_IVEC2, //vec2i ShaderLanguage::TYPE_VEC4, //rect2 ShaderLanguage::TYPE_IVEC4, //rect2i ShaderLanguage::TYPE_VEC3, // vec3 ShaderLanguage::TYPE_IVEC3, //vec3i ShaderLanguage::TYPE_MAX, //xform2d not supported here ShaderLanguage::TYPE_VEC4, //plane ShaderLanguage::TYPE_VEC4, //quat ShaderLanguage::TYPE_MAX, //aabb not supported here ShaderLanguage::TYPE_MAX, //basis not supported here ShaderLanguage::TYPE_MAX, //xform not supported here ShaderLanguage::TYPE_VEC4 //color }; ShaderLanguage::DataType datatype = datatype_from_value[p_value.get_type()]; ERR_FAIL_COND_MSG(datatype == ShaderLanguage::TYPE_MAX, "Unsupported variant type for instance parameter: " + Variant::get_type_name(p_value.get_type())); //anything greater not supported pos += p_index; _fill_std140_variant_ubo_value(datatype, 0, p_value, (uint8_t *)&global_variables.buffer_values[pos], true); //instances always use linear color in this renderer _global_variable_mark_buffer_dirty(pos, 1); } void RendererStorageRD::_update_global_variables() { if (global_variables.buffer_dirty_region_count > 0) { uint32_t total_regions = global_variables.buffer_size / GlobalVariables::BUFFER_DIRTY_REGION_SIZE; if (total_regions / global_variables.buffer_dirty_region_count <= 4) { // 25% of regions dirty, just update all buffer RD::get_singleton()->buffer_update(global_variables.buffer, 0, sizeof(GlobalVariables::Value) * global_variables.buffer_size, global_variables.buffer_values); memset(global_variables.buffer_dirty_regions, 0, sizeof(bool) * total_regions); } else { uint32_t region_byte_size = sizeof(GlobalVariables::Value) * GlobalVariables::BUFFER_DIRTY_REGION_SIZE; for (uint32_t i = 0; i < total_regions; i++) { if (global_variables.buffer_dirty_regions[i]) { RD::get_singleton()->buffer_update(global_variables.buffer, i * region_byte_size, region_byte_size, &global_variables.buffer_values[i * GlobalVariables::BUFFER_DIRTY_REGION_SIZE]); global_variables.buffer_dirty_regions[i] = false; } } } global_variables.buffer_dirty_region_count = 0; } if (global_variables.must_update_buffer_materials) { // only happens in the case of a buffer variable added or removed, // so not often. for (const RID &E : global_variables.materials_using_buffer) { Material *material = material_owner.get_or_null(E); ERR_CONTINUE(!material); //wtf _material_queue_update(material, true, false); } global_variables.must_update_buffer_materials = false; } if (global_variables.must_update_texture_materials) { // only happens in the case of a buffer variable added or removed, // so not often. for (const RID &E : global_variables.materials_using_texture) { Material *material = material_owner.get_or_null(E); ERR_CONTINUE(!material); //wtf _material_queue_update(material, false, true); } global_variables.must_update_texture_materials = false; } } void RendererStorageRD::update_dirty_resources() { _update_global_variables(); //must do before materials, so it can queue them for update _update_queued_materials(); _update_dirty_multimeshes(); _update_dirty_skeletons(); _update_decal_atlas(); } bool RendererStorageRD::has_os_feature(const String &p_feature) const { if (p_feature == "rgtc" && RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_BC5_UNORM_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT)) { return true; } if (p_feature == "s3tc" && RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_BC1_RGB_UNORM_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT)) { return true; } if (p_feature == "bptc" && RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_BC7_UNORM_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT)) { return true; } if ((p_feature == "etc" || p_feature == "etc2") && RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_ETC2_R8G8B8_UNORM_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT)) { return true; } return false; } bool RendererStorageRD::free(RID p_rid) { if (texture_owner.owns(p_rid)) { Texture *t = texture_owner.get_or_null(p_rid); ERR_FAIL_COND_V(!t, false); ERR_FAIL_COND_V(t->is_render_target, false); if (RD::get_singleton()->texture_is_valid(t->rd_texture_srgb)) { //erase this first, as it's a dependency of the one below RD::get_singleton()->free(t->rd_texture_srgb); } if (RD::get_singleton()->texture_is_valid(t->rd_texture)) { RD::get_singleton()->free(t->rd_texture); } if (t->is_proxy && t->proxy_to.is_valid()) { Texture *proxy_to = texture_owner.get_or_null(t->proxy_to); if (proxy_to) { proxy_to->proxies.erase(p_rid); } } if (decal_atlas.textures.has(p_rid)) { decal_atlas.textures.erase(p_rid); //there is not much a point of making it dirty, just let it be. } for (int i = 0; i < t->proxies.size(); i++) { Texture *p = texture_owner.get_or_null(t->proxies[i]); ERR_CONTINUE(!p); p->proxy_to = RID(); p->rd_texture = RID(); p->rd_texture_srgb = RID(); } if (t->canvas_texture) { memdelete(t->canvas_texture); } texture_owner.free(p_rid); } else if (canvas_texture_owner.owns(p_rid)) { canvas_texture_owner.free(p_rid); } else if (shader_owner.owns(p_rid)) { Shader *shader = shader_owner.get_or_null(p_rid); //make material unreference this while (shader->owners.size()) { material_set_shader(shader->owners.front()->get()->self, RID()); } //clear data if exists if (shader->data) { memdelete(shader->data); } shader_owner.free(p_rid); } else if (material_owner.owns(p_rid)) { Material *material = material_owner.get_or_null(p_rid); material_set_shader(p_rid, RID()); //clean up shader material->dependency.deleted_notify(p_rid); material_owner.free(p_rid); } else if (mesh_owner.owns(p_rid)) { mesh_clear(p_rid); mesh_set_shadow_mesh(p_rid, RID()); Mesh *mesh = mesh_owner.get_or_null(p_rid); mesh->dependency.deleted_notify(p_rid); if (mesh->instances.size()) { ERR_PRINT("deleting mesh with active instances"); } if (mesh->shadow_owners.size()) { for (Set::Element *E = mesh->shadow_owners.front(); E; E = E->next()) { Mesh *shadow_owner = E->get(); shadow_owner->shadow_mesh = RID(); shadow_owner->dependency.changed_notify(DEPENDENCY_CHANGED_MESH); } } mesh_owner.free(p_rid); } else if (mesh_instance_owner.owns(p_rid)) { MeshInstance *mi = mesh_instance_owner.get_or_null(p_rid); _mesh_instance_clear(mi); mi->mesh->instances.erase(mi->I); mi->I = nullptr; mesh_instance_owner.free(p_rid); } else if (multimesh_owner.owns(p_rid)) { _update_dirty_multimeshes(); multimesh_allocate_data(p_rid, 0, RS::MULTIMESH_TRANSFORM_2D); MultiMesh *multimesh = multimesh_owner.get_or_null(p_rid); multimesh->dependency.deleted_notify(p_rid); multimesh_owner.free(p_rid); } else if (skeleton_owner.owns(p_rid)) { _update_dirty_skeletons(); skeleton_allocate_data(p_rid, 0); Skeleton *skeleton = skeleton_owner.get_or_null(p_rid); skeleton->dependency.deleted_notify(p_rid); skeleton_owner.free(p_rid); } else if (reflection_probe_owner.owns(p_rid)) { ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_rid); reflection_probe->dependency.deleted_notify(p_rid); reflection_probe_owner.free(p_rid); } else if (decal_owner.owns(p_rid)) { Decal *decal = decal_owner.get_or_null(p_rid); for (int i = 0; i < RS::DECAL_TEXTURE_MAX; i++) { if (decal->textures[i].is_valid() && texture_owner.owns(decal->textures[i])) { texture_remove_from_decal_atlas(decal->textures[i]); } } decal->dependency.deleted_notify(p_rid); decal_owner.free(p_rid); } else if (voxel_gi_owner.owns(p_rid)) { voxel_gi_allocate_data(p_rid, Transform3D(), AABB(), Vector3i(), Vector(), Vector(), Vector(), Vector()); //deallocate VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_rid); voxel_gi->dependency.deleted_notify(p_rid); voxel_gi_owner.free(p_rid); } else if (lightmap_owner.owns(p_rid)) { lightmap_set_textures(p_rid, RID(), false); Lightmap *lightmap = lightmap_owner.get_or_null(p_rid); lightmap->dependency.deleted_notify(p_rid); lightmap_owner.free(p_rid); } else if (light_owner.owns(p_rid)) { light_set_projector(p_rid, RID()); //clear projector // delete the texture Light *light = light_owner.get_or_null(p_rid); light->dependency.deleted_notify(p_rid); light_owner.free(p_rid); } else if (particles_owner.owns(p_rid)) { update_particles(); Particles *particles = particles_owner.get_or_null(p_rid); particles->dependency.deleted_notify(p_rid); _particles_free_data(particles); particles_owner.free(p_rid); } else if (particles_collision_owner.owns(p_rid)) { ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_rid); if (particles_collision->heightfield_texture.is_valid()) { RD::get_singleton()->free(particles_collision->heightfield_texture); } particles_collision->dependency.deleted_notify(p_rid); particles_collision_owner.free(p_rid); } else if (visibility_notifier_owner.owns(p_rid)) { VisibilityNotifier *vn = visibility_notifier_owner.get_or_null(p_rid); vn->dependency.deleted_notify(p_rid); visibility_notifier_owner.free(p_rid); } else if (particles_collision_instance_owner.owns(p_rid)) { particles_collision_instance_owner.free(p_rid); } else if (fog_volume_owner.owns(p_rid)) { FogVolume *fog_volume = fog_volume_owner.get_or_null(p_rid); fog_volume->dependency.deleted_notify(p_rid); fog_volume_owner.free(p_rid); } else if (render_target_owner.owns(p_rid)) { RenderTarget *rt = render_target_owner.get_or_null(p_rid); _clear_render_target(rt); if (rt->texture.is_valid()) { Texture *tex = texture_owner.get_or_null(rt->texture); tex->is_render_target = false; free(rt->texture); } render_target_owner.free(p_rid); } else { return false; } return true; } void RendererStorageRD::init_effects(bool p_prefer_raster_effects) { effects = memnew(EffectsRD(p_prefer_raster_effects)); } EffectsRD *RendererStorageRD::get_effects() { ERR_FAIL_NULL_V_MSG(effects, nullptr, "Effects haven't been initialised yet."); return effects; } void RendererStorageRD::capture_timestamps_begin() { RD::get_singleton()->capture_timestamp("Frame Begin"); } void RendererStorageRD::capture_timestamp(const String &p_name) { RD::get_singleton()->capture_timestamp(p_name); } uint32_t RendererStorageRD::get_captured_timestamps_count() const { return RD::get_singleton()->get_captured_timestamps_count(); } uint64_t RendererStorageRD::get_captured_timestamps_frame() const { return RD::get_singleton()->get_captured_timestamps_frame(); } uint64_t RendererStorageRD::get_captured_timestamp_gpu_time(uint32_t p_index) const { return RD::get_singleton()->get_captured_timestamp_gpu_time(p_index); } uint64_t RendererStorageRD::get_captured_timestamp_cpu_time(uint32_t p_index) const { return RD::get_singleton()->get_captured_timestamp_cpu_time(p_index); } String RendererStorageRD::get_captured_timestamp_name(uint32_t p_index) const { return RD::get_singleton()->get_captured_timestamp_name(p_index); } void RendererStorageRD::update_memory_info() { texture_mem_cache = RenderingDevice::get_singleton()->get_memory_usage(RenderingDevice::MEMORY_TEXTURES); buffer_mem_cache = RenderingDevice::get_singleton()->get_memory_usage(RenderingDevice::MEMORY_BUFFERS); total_mem_cache = RenderingDevice::get_singleton()->get_memory_usage(RenderingDevice::MEMORY_TOTAL); } uint64_t RendererStorageRD::get_rendering_info(RS::RenderingInfo p_info) { if (p_info == RS::RENDERING_INFO_TEXTURE_MEM_USED) { return texture_mem_cache; } else if (p_info == RS::RENDERING_INFO_BUFFER_MEM_USED) { return buffer_mem_cache; } else if (p_info == RS::RENDERING_INFO_VIDEO_MEM_USED) { return total_mem_cache; } return 0; } String RendererStorageRD::get_video_adapter_name() const { return RenderingDevice::get_singleton()->get_device_name(); } String RendererStorageRD::get_video_adapter_vendor() const { return RenderingDevice::get_singleton()->get_device_vendor_name(); } RenderingDevice::DeviceType RendererStorageRD::get_video_adapter_type() const { return RenderingDevice::get_singleton()->get_device_type(); } RendererStorageRD *RendererStorageRD::base_singleton = nullptr; RendererStorageRD::RendererStorageRD() { base_singleton = this; for (int i = 0; i < SHADER_TYPE_MAX; i++) { shader_data_request_func[i] = nullptr; } static_assert(sizeof(GlobalVariables::Value) == 16); global_variables.buffer_size = MAX(4096, (int)GLOBAL_GET("rendering/limits/global_shader_variables/buffer_size")); global_variables.buffer_values = memnew_arr(GlobalVariables::Value, global_variables.buffer_size); memset(global_variables.buffer_values, 0, sizeof(GlobalVariables::Value) * global_variables.buffer_size); global_variables.buffer_usage = memnew_arr(GlobalVariables::ValueUsage, global_variables.buffer_size); global_variables.buffer_dirty_regions = memnew_arr(bool, global_variables.buffer_size / GlobalVariables::BUFFER_DIRTY_REGION_SIZE); memset(global_variables.buffer_dirty_regions, 0, sizeof(bool) * global_variables.buffer_size / GlobalVariables::BUFFER_DIRTY_REGION_SIZE); global_variables.buffer = RD::get_singleton()->storage_buffer_create(sizeof(GlobalVariables::Value) * global_variables.buffer_size); { //create default textures RD::TextureFormat tformat; tformat.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; tformat.width = 4; tformat.height = 4; tformat.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT; tformat.texture_type = RD::TEXTURE_TYPE_2D; Vector pv; pv.resize(16 * 4); for (int i = 0; i < 16; i++) { pv.set(i * 4 + 0, 255); pv.set(i * 4 + 1, 255); pv.set(i * 4 + 2, 255); pv.set(i * 4 + 3, 255); } { Vector> vpv; vpv.push_back(pv); default_rd_textures[DEFAULT_RD_TEXTURE_WHITE] = RD::get_singleton()->texture_create(tformat, RD::TextureView(), vpv); } for (int i = 0; i < 16; i++) { pv.set(i * 4 + 0, 0); pv.set(i * 4 + 1, 0); pv.set(i * 4 + 2, 0); pv.set(i * 4 + 3, 255); } { Vector> vpv; vpv.push_back(pv); default_rd_textures[DEFAULT_RD_TEXTURE_BLACK] = RD::get_singleton()->texture_create(tformat, RD::TextureView(), vpv); //take the chance and initialize decal atlas to something decal_atlas.texture = RD::get_singleton()->texture_create(tformat, RD::TextureView(), vpv); decal_atlas.texture_srgb = decal_atlas.texture; } for (int i = 0; i < 16; i++) { pv.set(i * 4 + 0, 128); pv.set(i * 4 + 1, 128); pv.set(i * 4 + 2, 255); pv.set(i * 4 + 3, 255); } { Vector> vpv; vpv.push_back(pv); default_rd_textures[DEFAULT_RD_TEXTURE_NORMAL] = RD::get_singleton()->texture_create(tformat, RD::TextureView(), vpv); } for (int i = 0; i < 16; i++) { pv.set(i * 4 + 0, 255); pv.set(i * 4 + 1, 128); pv.set(i * 4 + 2, 255); pv.set(i * 4 + 3, 255); } { Vector> vpv; vpv.push_back(pv); default_rd_textures[DEFAULT_RD_TEXTURE_ANISO] = RD::get_singleton()->texture_create(tformat, RD::TextureView(), vpv); } for (int i = 0; i < 16; i++) { pv.set(i * 4 + 0, 0); pv.set(i * 4 + 1, 0); pv.set(i * 4 + 2, 0); pv.set(i * 4 + 3, 0); } default_rd_textures[DEFAULT_RD_TEXTURE_MULTIMESH_BUFFER] = RD::get_singleton()->texture_buffer_create(16, RD::DATA_FORMAT_R8G8B8A8_UNORM, pv); for (int i = 0; i < 16; i++) { pv.set(i * 4 + 0, 0); pv.set(i * 4 + 1, 0); pv.set(i * 4 + 2, 0); pv.set(i * 4 + 3, 0); } { tformat.format = RD::DATA_FORMAT_R8G8B8A8_UINT; Vector> vpv; vpv.push_back(pv); default_rd_textures[DEFAULT_RD_TEXTURE_2D_UINT] = RD::get_singleton()->texture_create(tformat, RD::TextureView(), vpv); } } { //create default cubemap RD::TextureFormat tformat; tformat.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; tformat.width = 4; tformat.height = 4; tformat.array_layers = 6; tformat.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT; tformat.texture_type = RD::TEXTURE_TYPE_CUBE_ARRAY; Vector pv; pv.resize(16 * 4); for (int i = 0; i < 16; i++) { pv.set(i * 4 + 0, 0); pv.set(i * 4 + 1, 0); pv.set(i * 4 + 2, 0); pv.set(i * 4 + 3, 0); } { Vector> vpv; for (int i = 0; i < 6; i++) { vpv.push_back(pv); } default_rd_textures[DEFAULT_RD_TEXTURE_CUBEMAP_ARRAY_BLACK] = RD::get_singleton()->texture_create(tformat, RD::TextureView(), vpv); } } { //create default cubemap array RD::TextureFormat tformat; tformat.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; tformat.width = 4; tformat.height = 4; tformat.array_layers = 6; tformat.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT; tformat.texture_type = RD::TEXTURE_TYPE_CUBE; Vector pv; pv.resize(16 * 4); for (int i = 0; i < 16; i++) { pv.set(i * 4 + 0, 0); pv.set(i * 4 + 1, 0); pv.set(i * 4 + 2, 0); pv.set(i * 4 + 3, 0); } { Vector> vpv; for (int i = 0; i < 6; i++) { vpv.push_back(pv); } default_rd_textures[DEFAULT_RD_TEXTURE_CUBEMAP_BLACK] = RD::get_singleton()->texture_create(tformat, RD::TextureView(), vpv); } } { //create default cubemap white array RD::TextureFormat tformat; tformat.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; tformat.width = 4; tformat.height = 4; tformat.array_layers = 6; tformat.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT; tformat.texture_type = RD::TEXTURE_TYPE_CUBE; Vector pv; pv.resize(16 * 4); for (int i = 0; i < 16; i++) { pv.set(i * 4 + 0, 255); pv.set(i * 4 + 1, 255); pv.set(i * 4 + 2, 255); pv.set(i * 4 + 3, 255); } { Vector> vpv; for (int i = 0; i < 6; i++) { vpv.push_back(pv); } default_rd_textures[DEFAULT_RD_TEXTURE_CUBEMAP_WHITE] = RD::get_singleton()->texture_create(tformat, RD::TextureView(), vpv); } } { //create default 3D RD::TextureFormat tformat; tformat.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; tformat.width = 4; tformat.height = 4; tformat.depth = 4; tformat.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT; tformat.texture_type = RD::TEXTURE_TYPE_3D; Vector pv; pv.resize(64 * 4); for (int i = 0; i < 64; i++) { pv.set(i * 4 + 0, 0); pv.set(i * 4 + 1, 0); pv.set(i * 4 + 2, 0); pv.set(i * 4 + 3, 0); } { Vector> vpv; vpv.push_back(pv); default_rd_textures[DEFAULT_RD_TEXTURE_3D_BLACK] = RD::get_singleton()->texture_create(tformat, RD::TextureView(), vpv); } for (int i = 0; i < 64; i++) { pv.set(i * 4 + 0, 255); pv.set(i * 4 + 1, 255); pv.set(i * 4 + 2, 255); pv.set(i * 4 + 3, 255); } { Vector> vpv; vpv.push_back(pv); default_rd_textures[DEFAULT_RD_TEXTURE_3D_WHITE] = RD::get_singleton()->texture_create(tformat, RD::TextureView(), vpv); } } { //create default array RD::TextureFormat tformat; tformat.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; tformat.width = 4; tformat.height = 4; tformat.array_layers = 1; tformat.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT; tformat.texture_type = RD::TEXTURE_TYPE_2D_ARRAY; Vector pv; pv.resize(16 * 4); for (int i = 0; i < 16; i++) { pv.set(i * 4 + 0, 255); pv.set(i * 4 + 1, 255); pv.set(i * 4 + 2, 255); pv.set(i * 4 + 3, 255); } { Vector> vpv; vpv.push_back(pv); default_rd_textures[DEFAULT_RD_TEXTURE_2D_ARRAY_WHITE] = RD::get_singleton()->texture_create(tformat, RD::TextureView(), vpv); } } //default samplers for (int i = 1; i < RS::CANVAS_ITEM_TEXTURE_FILTER_MAX; i++) { for (int j = 1; j < RS::CANVAS_ITEM_TEXTURE_REPEAT_MAX; j++) { RD::SamplerState sampler_state; switch (i) { case RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST: { sampler_state.mag_filter = RD::SAMPLER_FILTER_NEAREST; sampler_state.min_filter = RD::SAMPLER_FILTER_NEAREST; sampler_state.max_lod = 0; } break; case RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR: { sampler_state.mag_filter = RD::SAMPLER_FILTER_LINEAR; sampler_state.min_filter = RD::SAMPLER_FILTER_LINEAR; sampler_state.max_lod = 0; } break; case RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS: { sampler_state.mag_filter = RD::SAMPLER_FILTER_NEAREST; sampler_state.min_filter = RD::SAMPLER_FILTER_NEAREST; if (GLOBAL_GET("rendering/textures/default_filters/use_nearest_mipmap_filter")) { sampler_state.mip_filter = RD::SAMPLER_FILTER_NEAREST; } else { sampler_state.mip_filter = RD::SAMPLER_FILTER_LINEAR; } } break; case RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS: { sampler_state.mag_filter = RD::SAMPLER_FILTER_LINEAR; sampler_state.min_filter = RD::SAMPLER_FILTER_LINEAR; if (GLOBAL_GET("rendering/textures/default_filters/use_nearest_mipmap_filter")) { sampler_state.mip_filter = RD::SAMPLER_FILTER_NEAREST; } else { sampler_state.mip_filter = RD::SAMPLER_FILTER_LINEAR; } } break; case RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC: { sampler_state.mag_filter = RD::SAMPLER_FILTER_NEAREST; sampler_state.min_filter = RD::SAMPLER_FILTER_NEAREST; if (GLOBAL_GET("rendering/textures/default_filters/use_nearest_mipmap_filter")) { sampler_state.mip_filter = RD::SAMPLER_FILTER_NEAREST; } else { sampler_state.mip_filter = RD::SAMPLER_FILTER_LINEAR; } sampler_state.use_anisotropy = true; sampler_state.anisotropy_max = 1 << int(GLOBAL_GET("rendering/textures/default_filters/anisotropic_filtering_level")); } break; case RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC: { sampler_state.mag_filter = RD::SAMPLER_FILTER_LINEAR; sampler_state.min_filter = RD::SAMPLER_FILTER_LINEAR; if (GLOBAL_GET("rendering/textures/default_filters/use_nearest_mipmap_filter")) { sampler_state.mip_filter = RD::SAMPLER_FILTER_NEAREST; } else { sampler_state.mip_filter = RD::SAMPLER_FILTER_LINEAR; } sampler_state.use_anisotropy = true; sampler_state.anisotropy_max = 1 << int(GLOBAL_GET("rendering/textures/default_filters/anisotropic_filtering_level")); } break; default: { } } switch (j) { case RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED: { sampler_state.repeat_u = RD::SAMPLER_REPEAT_MODE_CLAMP_TO_EDGE; sampler_state.repeat_v = RD::SAMPLER_REPEAT_MODE_CLAMP_TO_EDGE; sampler_state.repeat_w = RD::SAMPLER_REPEAT_MODE_CLAMP_TO_EDGE; } break; case RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED: { sampler_state.repeat_u = RD::SAMPLER_REPEAT_MODE_REPEAT; sampler_state.repeat_v = RD::SAMPLER_REPEAT_MODE_REPEAT; sampler_state.repeat_w = RD::SAMPLER_REPEAT_MODE_REPEAT; } break; case RS::CANVAS_ITEM_TEXTURE_REPEAT_MIRROR: { sampler_state.repeat_u = RD::SAMPLER_REPEAT_MODE_MIRRORED_REPEAT; sampler_state.repeat_v = RD::SAMPLER_REPEAT_MODE_MIRRORED_REPEAT; sampler_state.repeat_w = RD::SAMPLER_REPEAT_MODE_MIRRORED_REPEAT; } break; default: { } } default_rd_samplers[i][j] = RD::get_singleton()->sampler_create(sampler_state); } } //custom sampler sampler_rd_configure_custom(0.0f); //default rd buffers { Vector buffer; { buffer.resize(sizeof(float) * 3); { uint8_t *w = buffer.ptrw(); float *fptr = (float *)w; fptr[0] = 0.0; fptr[1] = 0.0; fptr[2] = 0.0; } mesh_default_rd_buffers[DEFAULT_RD_BUFFER_VERTEX] = RD::get_singleton()->vertex_buffer_create(buffer.size(), buffer); } { //normal buffer.resize(sizeof(float) * 3); { uint8_t *w = buffer.ptrw(); float *fptr = (float *)w; fptr[0] = 1.0; fptr[1] = 0.0; fptr[2] = 0.0; } mesh_default_rd_buffers[DEFAULT_RD_BUFFER_NORMAL] = RD::get_singleton()->vertex_buffer_create(buffer.size(), buffer); } { //tangent buffer.resize(sizeof(float) * 4); { uint8_t *w = buffer.ptrw(); float *fptr = (float *)w; fptr[0] = 1.0; fptr[1] = 0.0; fptr[2] = 0.0; fptr[3] = 0.0; } mesh_default_rd_buffers[DEFAULT_RD_BUFFER_TANGENT] = RD::get_singleton()->vertex_buffer_create(buffer.size(), buffer); } { //color buffer.resize(sizeof(float) * 4); { uint8_t *w = buffer.ptrw(); float *fptr = (float *)w; fptr[0] = 1.0; fptr[1] = 1.0; fptr[2] = 1.0; fptr[3] = 1.0; } mesh_default_rd_buffers[DEFAULT_RD_BUFFER_COLOR] = RD::get_singleton()->vertex_buffer_create(buffer.size(), buffer); } { //tex uv 1 buffer.resize(sizeof(float) * 2); { uint8_t *w = buffer.ptrw(); float *fptr = (float *)w; fptr[0] = 0.0; fptr[1] = 0.0; } mesh_default_rd_buffers[DEFAULT_RD_BUFFER_TEX_UV] = RD::get_singleton()->vertex_buffer_create(buffer.size(), buffer); } { //tex uv 2 buffer.resize(sizeof(float) * 2); { uint8_t *w = buffer.ptrw(); float *fptr = (float *)w; fptr[0] = 0.0; fptr[1] = 0.0; } mesh_default_rd_buffers[DEFAULT_RD_BUFFER_TEX_UV2] = RD::get_singleton()->vertex_buffer_create(buffer.size(), buffer); } for (int i = 0; i < RS::ARRAY_CUSTOM_COUNT; i++) { buffer.resize(sizeof(float) * 4); { uint8_t *w = buffer.ptrw(); float *fptr = (float *)w; fptr[0] = 0.0; fptr[1] = 0.0; fptr[2] = 0.0; fptr[3] = 0.0; } mesh_default_rd_buffers[DEFAULT_RD_BUFFER_CUSTOM0 + i] = RD::get_singleton()->vertex_buffer_create(buffer.size(), buffer); } { //bones buffer.resize(sizeof(uint32_t) * 4); { uint8_t *w = buffer.ptrw(); uint32_t *fptr = (uint32_t *)w; fptr[0] = 0; fptr[1] = 0; fptr[2] = 0; fptr[3] = 0; } mesh_default_rd_buffers[DEFAULT_RD_BUFFER_BONES] = RD::get_singleton()->vertex_buffer_create(buffer.size(), buffer); } { //weights buffer.resize(sizeof(float) * 4); { uint8_t *w = buffer.ptrw(); float *fptr = (float *)w; fptr[0] = 0.0; fptr[1] = 0.0; fptr[2] = 0.0; fptr[3] = 0.0; } mesh_default_rd_buffers[DEFAULT_RD_BUFFER_WEIGHTS] = RD::get_singleton()->vertex_buffer_create(buffer.size(), buffer); } } using_lightmap_array = true; // high end if (using_lightmap_array) { uint32_t textures_per_stage = RD::get_singleton()->limit_get(RD::LIMIT_MAX_TEXTURES_PER_SHADER_STAGE); if (textures_per_stage <= 256) { lightmap_textures.resize(32); } else { lightmap_textures.resize(1024); } for (int i = 0; i < lightmap_textures.size(); i++) { lightmap_textures.write[i] = default_rd_textures[DEFAULT_RD_TEXTURE_2D_ARRAY_WHITE]; } } lightmap_probe_capture_update_speed = GLOBAL_GET("rendering/lightmapping/probe_capture/update_speed"); /* Particles */ { // Initialize particles Vector particles_modes; particles_modes.push_back(""); particles_shader.shader.initialize(particles_modes, String()); } shader_set_data_request_function(RendererStorageRD::SHADER_TYPE_PARTICLES, _create_particles_shader_funcs); material_set_data_request_function(RendererStorageRD::SHADER_TYPE_PARTICLES, _create_particles_material_funcs); { ShaderCompiler::DefaultIdentifierActions actions; actions.renames["COLOR"] = "PARTICLE.color"; actions.renames["VELOCITY"] = "PARTICLE.velocity"; //actions.renames["MASS"] = "mass"; ? actions.renames["ACTIVE"] = "particle_active"; actions.renames["RESTART"] = "restart"; actions.renames["CUSTOM"] = "PARTICLE.custom"; for (int i = 0; i < ParticlesShader::MAX_USERDATAS; i++) { String udname = "USERDATA" + itos(i + 1); actions.renames[udname] = "PARTICLE.userdata" + itos(i + 1); actions.usage_defines[udname] = "#define USERDATA" + itos(i + 1) + "_USED\n"; } actions.renames["TRANSFORM"] = "PARTICLE.xform"; actions.renames["TIME"] = "frame_history.data[0].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"; actions.renames["INDEX"] = "index"; //actions.renames["GRAVITY"] = "current_gravity"; actions.renames["EMISSION_TRANSFORM"] = "FRAME.emission_transform"; actions.renames["RANDOM_SEED"] = "FRAME.random_seed"; actions.renames["FLAG_EMIT_POSITION"] = "EMISSION_FLAG_HAS_POSITION"; actions.renames["FLAG_EMIT_ROT_SCALE"] = "EMISSION_FLAG_HAS_ROTATION_SCALE"; actions.renames["FLAG_EMIT_VELOCITY"] = "EMISSION_FLAG_HAS_VELOCITY"; actions.renames["FLAG_EMIT_COLOR"] = "EMISSION_FLAG_HAS_COLOR"; actions.renames["FLAG_EMIT_CUSTOM"] = "EMISSION_FLAG_HAS_CUSTOM"; actions.renames["RESTART_POSITION"] = "restart_position"; actions.renames["RESTART_ROT_SCALE"] = "restart_rotation_scale"; actions.renames["RESTART_VELOCITY"] = "restart_velocity"; actions.renames["RESTART_COLOR"] = "restart_color"; actions.renames["RESTART_CUSTOM"] = "restart_custom"; actions.renames["emit_subparticle"] = "emit_subparticle"; actions.renames["COLLIDED"] = "collided"; actions.renames["COLLISION_NORMAL"] = "collision_normal"; actions.renames["COLLISION_DEPTH"] = "collision_depth"; actions.renames["ATTRACTOR_FORCE"] = "attractor_force"; actions.render_mode_defines["disable_force"] = "#define DISABLE_FORCE\n"; actions.render_mode_defines["disable_velocity"] = "#define DISABLE_VELOCITY\n"; actions.render_mode_defines["keep_data"] = "#define ENABLE_KEEP_DATA\n"; actions.render_mode_defines["collision_use_scale"] = "#define USE_COLLISON_SCALE\n"; actions.sampler_array_name = "material_samplers"; actions.base_texture_binding_index = 1; actions.texture_layout_set = 3; actions.base_uniform_string = "material."; actions.base_varying_index = 10; actions.default_filter = ShaderLanguage::FILTER_LINEAR_MIPMAP; actions.default_repeat = ShaderLanguage::REPEAT_ENABLE; actions.global_buffer_array_variable = "global_variables.data"; particles_shader.compiler.initialize(actions); } { // default material and shader for particles shader particles_shader.default_shader = shader_allocate(); shader_initialize(particles_shader.default_shader); shader_set_code(particles_shader.default_shader, R"( // Default particles shader. shader_type particles; void process() { COLOR = vec4(1.0); } )"); particles_shader.default_material = material_allocate(); material_initialize(particles_shader.default_material); material_set_shader(particles_shader.default_material, particles_shader.default_shader); ParticlesMaterialData *md = (ParticlesMaterialData *)material_get_data(particles_shader.default_material, RendererStorageRD::SHADER_TYPE_PARTICLES); particles_shader.default_shader_rd = particles_shader.shader.version_get_shader(md->shader_data->version, 0); Vector uniforms; { Vector ids; ids.resize(12); RID *ids_ptr = ids.ptrw(); ids_ptr[0] = sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); ids_ptr[1] = sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); ids_ptr[2] = sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); ids_ptr[3] = sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); ids_ptr[4] = sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); ids_ptr[5] = sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); ids_ptr[6] = sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED); ids_ptr[7] = sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED); ids_ptr[8] = sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED); ids_ptr[9] = sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED); ids_ptr[10] = sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED); ids_ptr[11] = sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED); RD::Uniform u(RD::UNIFORM_TYPE_SAMPLER, 1, ids); uniforms.push_back(u); } { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.binding = 2; u.append_id(global_variables_get_storage_buffer()); uniforms.push_back(u); } particles_shader.base_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, particles_shader.default_shader_rd, 0); } default_rd_storage_buffer = RD::get_singleton()->storage_buffer_create(sizeof(uint32_t) * 4); { Vector copy_modes; for (int i = 0; i <= ParticlesShader::MAX_USERDATAS; i++) { if (i == 0) { copy_modes.push_back("\n#define MODE_FILL_INSTANCES\n"); copy_modes.push_back("\n#define MODE_FILL_SORT_BUFFER\n#define USE_SORT_BUFFER\n"); copy_modes.push_back("\n#define MODE_FILL_INSTANCES\n#define USE_SORT_BUFFER\n"); } else { copy_modes.push_back("\n#define MODE_FILL_INSTANCES\n#define USERDATA_COUNT " + itos(i) + "\n"); copy_modes.push_back("\n#define MODE_FILL_SORT_BUFFER\n#define USE_SORT_BUFFER\n#define USERDATA_COUNT " + itos(i) + "\n"); copy_modes.push_back("\n#define MODE_FILL_INSTANCES\n#define USE_SORT_BUFFER\n#define USERDATA_COUNT " + itos(i) + "\n"); } } particles_shader.copy_shader.initialize(copy_modes); particles_shader.copy_shader_version = particles_shader.copy_shader.version_create(); for (int i = 0; i <= ParticlesShader::MAX_USERDATAS; i++) { for (int j = 0; j < ParticlesShader::COPY_MODE_MAX; j++) { particles_shader.copy_pipelines[i * ParticlesShader::COPY_MODE_MAX + j] = RD::get_singleton()->compute_pipeline_create(particles_shader.copy_shader.version_get_shader(particles_shader.copy_shader_version, i * ParticlesShader::COPY_MODE_MAX + j)); } } } { Vector sdf_modes; sdf_modes.push_back("\n#define MODE_LOAD\n"); sdf_modes.push_back("\n#define MODE_LOAD_SHRINK\n"); sdf_modes.push_back("\n#define MODE_PROCESS\n"); sdf_modes.push_back("\n#define MODE_PROCESS_OPTIMIZED\n"); sdf_modes.push_back("\n#define MODE_STORE\n"); sdf_modes.push_back("\n#define MODE_STORE_SHRINK\n"); rt_sdf.shader.initialize(sdf_modes); rt_sdf.shader_version = rt_sdf.shader.version_create(); for (int i = 0; i < RenderTargetSDF::SHADER_MAX; i++) { rt_sdf.pipelines[i] = RD::get_singleton()->compute_pipeline_create(rt_sdf.shader.version_get_shader(rt_sdf.shader_version, i)); } } { Vector skeleton_modes; skeleton_modes.push_back("\n#define MODE_2D\n"); skeleton_modes.push_back(""); skeleton_shader.shader.initialize(skeleton_modes); skeleton_shader.version = skeleton_shader.shader.version_create(); for (int i = 0; i < SkeletonShader::SHADER_MODE_MAX; i++) { skeleton_shader.version_shader[i] = skeleton_shader.shader.version_get_shader(skeleton_shader.version, i); skeleton_shader.pipeline[i] = RD::get_singleton()->compute_pipeline_create(skeleton_shader.version_shader[i]); } { Vector uniforms; { RD::Uniform u; u.binding = 0; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.append_id(default_rd_storage_buffer); uniforms.push_back(u); } skeleton_shader.default_skeleton_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, skeleton_shader.version_shader[0], SkeletonShader::UNIFORM_SET_SKELETON); } } } RendererStorageRD::~RendererStorageRD() { memdelete_arr(global_variables.buffer_values); memdelete_arr(global_variables.buffer_usage); memdelete_arr(global_variables.buffer_dirty_regions); RD::get_singleton()->free(global_variables.buffer); //def textures for (int i = 0; i < DEFAULT_RD_TEXTURE_MAX; i++) { RD::get_singleton()->free(default_rd_textures[i]); } //def samplers for (int i = 1; i < RS::CANVAS_ITEM_TEXTURE_FILTER_MAX; i++) { for (int j = 1; j < RS::CANVAS_ITEM_TEXTURE_REPEAT_MAX; j++) { RD::get_singleton()->free(default_rd_samplers[i][j]); } } //custom samplers for (int i = 1; i < RS::CANVAS_ITEM_TEXTURE_FILTER_MAX; i++) { for (int j = 0; j < RS::CANVAS_ITEM_TEXTURE_REPEAT_MAX; j++) { if (custom_rd_samplers[i][j].is_valid()) { RD::get_singleton()->free(custom_rd_samplers[i][j]); } } } //def buffers for (int i = 0; i < DEFAULT_RD_BUFFER_MAX; i++) { RD::get_singleton()->free(mesh_default_rd_buffers[i]); } particles_shader.copy_shader.version_free(particles_shader.copy_shader_version); rt_sdf.shader.version_free(rt_sdf.shader_version); skeleton_shader.shader.version_free(skeleton_shader.version); RenderingServer::get_singleton()->free(particles_shader.default_material); RenderingServer::get_singleton()->free(particles_shader.default_shader); RD::get_singleton()->free(default_rd_storage_buffer); if (decal_atlas.textures.size()) { ERR_PRINT("Decal Atlas: " + itos(decal_atlas.textures.size()) + " textures were not removed from the atlas."); } if (decal_atlas.texture.is_valid()) { RD::get_singleton()->free(decal_atlas.texture); } if (effects) { memdelete(effects); effects = nullptr; } }