3a11baaeac
Since it only accepts power-of-two values, exposing it as an enum makes more sense. This also allows for adding property hints to indicate the performance cost of each value. This also improves property hints for MSAA and FXAA.
6027 lines
187 KiB
C++
6027 lines
187 KiB
C++
/*************************************************************************/
|
|
/* rasterizer_storage_rd.cpp */
|
|
/*************************************************************************/
|
|
/* This file is part of: */
|
|
/* GODOT ENGINE */
|
|
/* https://godotengine.org */
|
|
/*************************************************************************/
|
|
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
|
/* Copyright (c) 2014-2020 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 "rasterizer_storage_rd.h"
|
|
|
|
#include "core/engine.h"
|
|
#include "core/io/resource_loader.h"
|
|
#include "core/project_settings.h"
|
|
#include "servers/rendering/shader_language.h"
|
|
|
|
Ref<Image> RasterizerStorageRD::_validate_texture_format(const Ref<Image> &p_image, TextureToRDFormat &r_format) {
|
|
|
|
Ref<Image> 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_PVRTC2: {
|
|
//this is not properly supported by MoltekVK it seems, so best to use ETC2
|
|
if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) {
|
|
r_format.format = RD::DATA_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG;
|
|
r_format.format_srgb = RD::DATA_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG;
|
|
} 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; //pvrtc
|
|
case Image::FORMAT_PVRTC2A: {
|
|
//this is not properly supported by MoltekVK it seems, so best to use ETC2
|
|
if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) {
|
|
r_format.format = RD::DATA_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG;
|
|
r_format.format_srgb = RD::DATA_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG;
|
|
} 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_PVRTC4: {
|
|
//this is not properly supported by MoltekVK it seems, so best to use ETC2
|
|
if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) {
|
|
r_format.format = RD::DATA_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG;
|
|
r_format.format_srgb = RD::DATA_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG;
|
|
} 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_PVRTC4A: {
|
|
//this is not properly supported by MoltekVK it seems, so best to use ETC2
|
|
if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) {
|
|
r_format.format = RD::DATA_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG;
|
|
r_format.format_srgb = RD::DATA_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG;
|
|
} 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_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 RasterizerStorageRD::texture_2d_create(const Ref<Image> &p_image) {
|
|
ERR_FAIL_COND_V(p_image.is_null(), RID());
|
|
ERR_FAIL_COND_V(p_image->empty(), RID());
|
|
|
|
TextureToRDFormat ret_format;
|
|
Ref<Image> 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.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<uint8_t> data = image->get_data(); //use image data
|
|
Vector<Vector<uint8_t>> data_slices;
|
|
data_slices.push_back(data);
|
|
texture.rd_texture = RD::get_singleton()->texture_create(rd_format, rd_view, data_slices);
|
|
ERR_FAIL_COND_V(texture.rd_texture.is_null(), RID());
|
|
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_V(texture.rd_texture_srgb.is_null(), RID());
|
|
}
|
|
}
|
|
|
|
//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;
|
|
|
|
return texture_owner.make_rid(texture);
|
|
}
|
|
|
|
RID RasterizerStorageRD::texture_2d_layered_create(const Vector<Ref<Image>> &p_layers, RS::TextureLayeredType p_layered_type) {
|
|
|
|
return RID();
|
|
}
|
|
RID RasterizerStorageRD::texture_3d_create(const Vector<Ref<Image>> &p_slices) {
|
|
|
|
return RID();
|
|
}
|
|
|
|
RID RasterizerStorageRD::texture_proxy_create(RID p_base) {
|
|
Texture *tex = texture_owner.getornull(p_base);
|
|
ERR_FAIL_COND_V(!tex, RID());
|
|
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();
|
|
|
|
RID rid = texture_owner.make_rid(proxy_tex);
|
|
|
|
tex->proxies.push_back(rid);
|
|
|
|
return rid;
|
|
}
|
|
|
|
void RasterizerStorageRD::_texture_2d_update(RID p_texture, const Ref<Image> &p_image, int p_layer, bool p_immediate) {
|
|
|
|
ERR_FAIL_COND(p_image.is_null() || p_image->empty());
|
|
|
|
Texture *tex = texture_owner.getornull(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<Image> validated = _validate_texture_format(p_image, f);
|
|
|
|
RD::get_singleton()->texture_update(tex->rd_texture, p_layer, validated->get_data(), !p_immediate);
|
|
}
|
|
|
|
void RasterizerStorageRD::texture_2d_update_immediate(RID p_texture, const Ref<Image> &p_image, int p_layer) {
|
|
_texture_2d_update(p_texture, p_image, p_layer, true);
|
|
}
|
|
void RasterizerStorageRD::texture_2d_update(RID p_texture, const Ref<Image> &p_image, int p_layer) {
|
|
_texture_2d_update(p_texture, p_image, p_layer, false);
|
|
}
|
|
void RasterizerStorageRD::texture_3d_update(RID p_texture, const Ref<Image> &p_image, int p_depth, int p_mipmap) {
|
|
}
|
|
|
|
void RasterizerStorageRD::texture_proxy_update(RID p_texture, RID p_proxy_to) {
|
|
|
|
Texture *tex = texture_owner.getornull(p_texture);
|
|
ERR_FAIL_COND(!tex);
|
|
ERR_FAIL_COND(!tex->is_proxy);
|
|
Texture *proxy_to = texture_owner.getornull(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.getornull(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.
|
|
RID RasterizerStorageRD::texture_2d_placeholder_create() {
|
|
|
|
//this could be better optimized to reuse an existing image , done this way
|
|
//for now to get it working
|
|
Ref<Image> image;
|
|
image.instance();
|
|
image->create(4, 4, false, Image::FORMAT_RGBA8);
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
for (int j = 0; j < 4; j++) {
|
|
image->set_pixel(i, j, Color(1, 0, 1, 1));
|
|
}
|
|
}
|
|
|
|
return texture_2d_create(image);
|
|
}
|
|
RID RasterizerStorageRD::texture_2d_layered_placeholder_create() {
|
|
|
|
return RID();
|
|
}
|
|
RID RasterizerStorageRD::texture_3d_placeholder_create() {
|
|
|
|
return RID();
|
|
}
|
|
|
|
Ref<Image> RasterizerStorageRD::texture_2d_get(RID p_texture) const {
|
|
|
|
Texture *tex = texture_owner.getornull(p_texture);
|
|
ERR_FAIL_COND_V(!tex, Ref<Image>());
|
|
|
|
#ifdef TOOLS_ENABLED
|
|
if (tex->image_cache_2d.is_valid()) {
|
|
return tex->image_cache_2d;
|
|
}
|
|
#endif
|
|
Vector<uint8_t> data = RD::get_singleton()->texture_get_data(tex->rd_texture, 0);
|
|
ERR_FAIL_COND_V(data.size() == 0, Ref<Image>());
|
|
Ref<Image> image;
|
|
image.instance();
|
|
image->create(tex->width, tex->height, tex->mipmaps > 1, tex->validated_format, data);
|
|
ERR_FAIL_COND_V(image->empty(), Ref<Image>());
|
|
if (tex->format != tex->validated_format) {
|
|
image->convert(tex->format);
|
|
}
|
|
|
|
#ifdef TOOLS_ENABLED
|
|
if (Engine::get_singleton()->is_editor_hint()) {
|
|
tex->image_cache_2d = image;
|
|
}
|
|
#endif
|
|
|
|
return image;
|
|
}
|
|
Ref<Image> RasterizerStorageRD::texture_2d_layer_get(RID p_texture, int p_layer) const {
|
|
|
|
return Ref<Image>();
|
|
}
|
|
Ref<Image> RasterizerStorageRD::texture_3d_slice_get(RID p_texture, int p_depth, int p_mipmap) const {
|
|
|
|
return Ref<Image>();
|
|
}
|
|
|
|
void RasterizerStorageRD::texture_replace(RID p_texture, RID p_by_texture) {
|
|
|
|
Texture *tex = texture_owner.getornull(p_texture);
|
|
ERR_FAIL_COND(!tex);
|
|
ERR_FAIL_COND(tex->proxy_to.is_valid()); //cant replace proxy
|
|
Texture *by_tex = texture_owner.getornull(p_by_texture);
|
|
ERR_FAIL_COND(!by_tex);
|
|
ERR_FAIL_COND(by_tex->proxy_to.is_valid()); //cant 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);
|
|
|
|
Vector<RID> proxies_to_update = tex->proxies;
|
|
Vector<RID> proxies_to_redirect = by_tex->proxies;
|
|
|
|
*tex = *by_tex;
|
|
|
|
tex->proxies = proxies_to_update; //restore proxies, so they can be updated
|
|
|
|
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 RasterizerStorageRD::texture_set_size_override(RID p_texture, int p_width, int p_height) {
|
|
Texture *tex = texture_owner.getornull(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 RasterizerStorageRD::texture_set_path(RID p_texture, const String &p_path) {
|
|
Texture *tex = texture_owner.getornull(p_texture);
|
|
ERR_FAIL_COND(!tex);
|
|
tex->path = p_path;
|
|
}
|
|
String RasterizerStorageRD::texture_get_path(RID p_texture) const {
|
|
return String();
|
|
}
|
|
|
|
void RasterizerStorageRD::texture_set_detect_3d_callback(RID p_texture, RS::TextureDetectCallback p_callback, void *p_userdata) {
|
|
Texture *tex = texture_owner.getornull(p_texture);
|
|
ERR_FAIL_COND(!tex);
|
|
tex->detect_3d_callback_ud = p_userdata;
|
|
tex->detect_3d_callback = p_callback;
|
|
}
|
|
void RasterizerStorageRD::texture_set_detect_normal_callback(RID p_texture, RS::TextureDetectCallback p_callback, void *p_userdata) {
|
|
Texture *tex = texture_owner.getornull(p_texture);
|
|
ERR_FAIL_COND(!tex);
|
|
tex->detect_normal_callback_ud = p_userdata;
|
|
tex->detect_normal_callback = p_callback;
|
|
}
|
|
void RasterizerStorageRD::texture_set_detect_roughness_callback(RID p_texture, RS::TextureDetectRoughnessCallback p_callback, void *p_userdata) {
|
|
Texture *tex = texture_owner.getornull(p_texture);
|
|
ERR_FAIL_COND(!tex);
|
|
tex->detect_roughness_callback_ud = p_userdata;
|
|
tex->detect_roughness_callback = p_callback;
|
|
}
|
|
void RasterizerStorageRD::texture_debug_usage(List<RS::TextureInfo> *r_info) {
|
|
}
|
|
|
|
void RasterizerStorageRD::texture_set_proxy(RID p_proxy, RID p_base) {
|
|
}
|
|
void RasterizerStorageRD::texture_set_force_redraw_if_visible(RID p_texture, bool p_enable) {
|
|
}
|
|
|
|
Size2 RasterizerStorageRD::texture_size_with_proxy(RID p_proxy) {
|
|
return texture_2d_get_size(p_proxy);
|
|
}
|
|
|
|
/* SHADER API */
|
|
|
|
RID RasterizerStorageRD::shader_create() {
|
|
|
|
Shader shader;
|
|
shader.data = nullptr;
|
|
shader.type = SHADER_TYPE_MAX;
|
|
|
|
return shader_owner.make_rid(shader);
|
|
}
|
|
|
|
void RasterizerStorageRD::shader_set_code(RID p_shader, const String &p_code) {
|
|
Shader *shader = shader_owner.getornull(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
|
|
new_type = SHADER_TYPE_MAX;
|
|
|
|
if (new_type != shader->type) {
|
|
if (shader->data) {
|
|
memdelete(shader->data);
|
|
shader->data = nullptr;
|
|
}
|
|
|
|
for (Set<Material *>::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<Material *>::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) {
|
|
shader->data->set_code(p_code);
|
|
}
|
|
|
|
for (Set<Material *>::Element *E = shader->owners.front(); E; E = E->next()) {
|
|
Material *material = E->get();
|
|
material->instance_dependency.instance_notify_changed(false, true);
|
|
_material_queue_update(material, true, true);
|
|
}
|
|
}
|
|
|
|
String RasterizerStorageRD::shader_get_code(RID p_shader) const {
|
|
Shader *shader = shader_owner.getornull(p_shader);
|
|
ERR_FAIL_COND_V(!shader, String());
|
|
return shader->code;
|
|
}
|
|
void RasterizerStorageRD::shader_get_param_list(RID p_shader, List<PropertyInfo> *p_param_list) const {
|
|
|
|
Shader *shader = shader_owner.getornull(p_shader);
|
|
ERR_FAIL_COND(!shader);
|
|
if (shader->data) {
|
|
return shader->data->get_param_list(p_param_list);
|
|
}
|
|
}
|
|
|
|
void RasterizerStorageRD::shader_set_default_texture_param(RID p_shader, const StringName &p_name, RID p_texture) {
|
|
|
|
Shader *shader = shader_owner.getornull(p_shader);
|
|
ERR_FAIL_COND(!shader);
|
|
|
|
if (p_texture.is_valid() && texture_owner.owns(p_texture)) {
|
|
shader->default_texture_parameter[p_name] = p_texture;
|
|
} else {
|
|
shader->default_texture_parameter.erase(p_name);
|
|
}
|
|
|
|
for (Set<Material *>::Element *E = shader->owners.front(); E; E = E->next()) {
|
|
Material *material = E->get();
|
|
_material_queue_update(material, false, true);
|
|
}
|
|
}
|
|
|
|
RID RasterizerStorageRD::shader_get_default_texture_param(RID p_shader, const StringName &p_name) const {
|
|
Shader *shader = shader_owner.getornull(p_shader);
|
|
ERR_FAIL_COND_V(!shader, RID());
|
|
if (shader->default_texture_parameter.has(p_name)) {
|
|
return shader->default_texture_parameter[p_name];
|
|
}
|
|
|
|
return RID();
|
|
}
|
|
Variant RasterizerStorageRD::shader_get_param_default(RID p_shader, const StringName &p_param) const {
|
|
Shader *shader = shader_owner.getornull(p_shader);
|
|
ERR_FAIL_COND_V(!shader, Variant());
|
|
if (shader->data) {
|
|
return shader->data->get_default_parameter(p_param);
|
|
}
|
|
return Variant();
|
|
}
|
|
void RasterizerStorageRD::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;
|
|
}
|
|
|
|
/* COMMON MATERIAL API */
|
|
|
|
RID RasterizerStorageRD::material_create() {
|
|
|
|
Material material;
|
|
material.data = nullptr;
|
|
material.shader = nullptr;
|
|
material.shader_type = SHADER_TYPE_MAX;
|
|
material.update_next = nullptr;
|
|
material.update_requested = false;
|
|
material.uniform_dirty = false;
|
|
material.texture_dirty = false;
|
|
material.priority = 0;
|
|
RID id = material_owner.make_rid(material);
|
|
{
|
|
Material *material_ptr = material_owner.getornull(id);
|
|
material_ptr->self = id;
|
|
}
|
|
return id;
|
|
}
|
|
|
|
void RasterizerStorageRD::_material_queue_update(Material *material, bool p_uniform, bool p_texture) {
|
|
if (material->update_requested) {
|
|
return;
|
|
}
|
|
|
|
material->update_next = material_update_list;
|
|
material_update_list = material;
|
|
material->update_requested = true;
|
|
material->uniform_dirty = material->uniform_dirty || p_uniform;
|
|
material->texture_dirty = material->texture_dirty || p_texture;
|
|
}
|
|
|
|
void RasterizerStorageRD::material_set_shader(RID p_material, RID p_shader) {
|
|
|
|
Material *material = material_owner.getornull(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->instance_dependency.instance_notify_changed(false, true);
|
|
return;
|
|
}
|
|
|
|
Shader *shader = shader_owner.getornull(p_shader);
|
|
ERR_FAIL_COND(!shader);
|
|
material->shader = shader;
|
|
material->shader_type = shader->type;
|
|
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->instance_dependency.instance_notify_changed(false, true);
|
|
_material_queue_update(material, true, true);
|
|
}
|
|
|
|
void RasterizerStorageRD::material_set_param(RID p_material, const StringName &p_param, const Variant &p_value) {
|
|
|
|
Material *material = material_owner.getornull(p_material);
|
|
ERR_FAIL_COND(!material);
|
|
|
|
if (p_value.get_type() == Variant::NIL) {
|
|
material->params.erase(p_param);
|
|
} else {
|
|
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 RasterizerStorageRD::material_get_param(RID p_material, const StringName &p_param) const {
|
|
Material *material = material_owner.getornull(p_material);
|
|
ERR_FAIL_COND_V(!material, Variant());
|
|
if (material->params.has(p_param)) {
|
|
return material->params[p_param];
|
|
} else {
|
|
return Variant();
|
|
}
|
|
}
|
|
|
|
void RasterizerStorageRD::material_set_next_pass(RID p_material, RID p_next_material) {
|
|
Material *material = material_owner.getornull(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->instance_dependency.instance_notify_changed(false, true);
|
|
}
|
|
void RasterizerStorageRD::material_set_render_priority(RID p_material, int priority) {
|
|
Material *material = material_owner.getornull(p_material);
|
|
ERR_FAIL_COND(!material);
|
|
material->priority = priority;
|
|
if (material->data) {
|
|
material->data->set_render_priority(priority);
|
|
}
|
|
}
|
|
|
|
bool RasterizerStorageRD::material_is_animated(RID p_material) {
|
|
Material *material = material_owner.getornull(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 RasterizerStorageRD::material_casts_shadows(RID p_material) {
|
|
Material *material = material_owner.getornull(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 RasterizerStorageRD::material_get_instance_shader_parameters(RID p_material, List<InstanceShaderParam> *r_parameters) {
|
|
|
|
Material *material = material_owner.getornull(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 RasterizerStorageRD::material_update_dependency(RID p_material, RasterizerScene::InstanceBase *p_instance) {
|
|
Material *material = material_owner.getornull(p_material);
|
|
ERR_FAIL_COND(!material);
|
|
p_instance->update_dependency(&material->instance_dependency);
|
|
if (material->next_pass.is_valid()) {
|
|
material_update_dependency(material->next_pass, p_instance);
|
|
}
|
|
}
|
|
|
|
void RasterizerStorageRD::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, const Variant &value, uint8_t *data, bool p_linear_color) {
|
|
switch (type) {
|
|
case ShaderLanguage::TYPE_BOOL: {
|
|
|
|
bool v = value;
|
|
|
|
uint32_t *gui = (uint32_t *)data;
|
|
*gui = v ? 1 : 0;
|
|
} break;
|
|
case ShaderLanguage::TYPE_BVEC2: {
|
|
|
|
int v = value;
|
|
uint32_t *gui = (uint32_t *)data;
|
|
gui[0] = v & 1 ? 1 : 0;
|
|
gui[1] = v & 2 ? 1 : 0;
|
|
|
|
} break;
|
|
case ShaderLanguage::TYPE_BVEC3: {
|
|
|
|
int v = value;
|
|
uint32_t *gui = (uint32_t *)data;
|
|
gui[0] = (v & 1) ? 1 : 0;
|
|
gui[1] = (v & 2) ? 1 : 0;
|
|
gui[2] = (v & 4) ? 1 : 0;
|
|
|
|
} break;
|
|
case ShaderLanguage::TYPE_BVEC4: {
|
|
|
|
int v = value;
|
|
uint32_t *gui = (uint32_t *)data;
|
|
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: {
|
|
|
|
int v = value;
|
|
int32_t *gui = (int32_t *)data;
|
|
gui[0] = v;
|
|
|
|
} break;
|
|
case ShaderLanguage::TYPE_IVEC2: {
|
|
|
|
Vector<int> iv = value;
|
|
int s = iv.size();
|
|
int32_t *gui = (int32_t *)data;
|
|
|
|
const int *r = iv.ptr();
|
|
|
|
for (int i = 0; i < 2; i++) {
|
|
if (i < s)
|
|
gui[i] = r[i];
|
|
else
|
|
gui[i] = 0;
|
|
}
|
|
|
|
} break;
|
|
case ShaderLanguage::TYPE_IVEC3: {
|
|
|
|
Vector<int> iv = value;
|
|
int s = iv.size();
|
|
int32_t *gui = (int32_t *)data;
|
|
|
|
const int *r = iv.ptr();
|
|
|
|
for (int i = 0; i < 3; i++) {
|
|
if (i < s)
|
|
gui[i] = r[i];
|
|
else
|
|
gui[i] = 0;
|
|
}
|
|
} break;
|
|
case ShaderLanguage::TYPE_IVEC4: {
|
|
|
|
Vector<int> iv = value;
|
|
int s = iv.size();
|
|
int32_t *gui = (int32_t *)data;
|
|
|
|
const int *r = iv.ptr();
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
if (i < s)
|
|
gui[i] = r[i];
|
|
else
|
|
gui[i] = 0;
|
|
}
|
|
} break;
|
|
case ShaderLanguage::TYPE_UINT: {
|
|
|
|
int v = value;
|
|
uint32_t *gui = (uint32_t *)data;
|
|
gui[0] = v;
|
|
|
|
} break;
|
|
case ShaderLanguage::TYPE_UVEC2: {
|
|
|
|
Vector<int> iv = value;
|
|
int s = iv.size();
|
|
uint32_t *gui = (uint32_t *)data;
|
|
|
|
const int *r = iv.ptr();
|
|
|
|
for (int i = 0; i < 2; i++) {
|
|
if (i < s)
|
|
gui[i] = r[i];
|
|
else
|
|
gui[i] = 0;
|
|
}
|
|
} break;
|
|
case ShaderLanguage::TYPE_UVEC3: {
|
|
Vector<int> iv = value;
|
|
int s = iv.size();
|
|
uint32_t *gui = (uint32_t *)data;
|
|
|
|
const int *r = iv.ptr();
|
|
|
|
for (int i = 0; i < 3; i++) {
|
|
if (i < s)
|
|
gui[i] = r[i];
|
|
else
|
|
gui[i] = 0;
|
|
}
|
|
|
|
} break;
|
|
case ShaderLanguage::TYPE_UVEC4: {
|
|
Vector<int> iv = value;
|
|
int s = iv.size();
|
|
uint32_t *gui = (uint32_t *)data;
|
|
|
|
const int *r = iv.ptr();
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
if (i < s)
|
|
gui[i] = r[i];
|
|
else
|
|
gui[i] = 0;
|
|
}
|
|
} break;
|
|
case ShaderLanguage::TYPE_FLOAT: {
|
|
float v = value;
|
|
float *gui = (float *)data;
|
|
gui[0] = v;
|
|
|
|
} break;
|
|
case ShaderLanguage::TYPE_VEC2: {
|
|
Vector2 v = value;
|
|
float *gui = (float *)data;
|
|
gui[0] = v.x;
|
|
gui[1] = v.y;
|
|
|
|
} break;
|
|
case ShaderLanguage::TYPE_VEC3: {
|
|
Vector3 v = value;
|
|
float *gui = (float *)data;
|
|
gui[0] = v.x;
|
|
gui[1] = v.y;
|
|
gui[2] = v.z;
|
|
|
|
} break;
|
|
case ShaderLanguage::TYPE_VEC4: {
|
|
|
|
float *gui = (float *)data;
|
|
|
|
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::QUAT) {
|
|
Quat 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: {
|
|
Transform2D v = value;
|
|
float *gui = (float *)data;
|
|
|
|
//in std140 members of mat2 are treated as vec4s
|
|
gui[0] = v.elements[0][0];
|
|
gui[1] = v.elements[0][1];
|
|
gui[2] = 0;
|
|
gui[3] = 0;
|
|
gui[4] = v.elements[1][0];
|
|
gui[5] = v.elements[1][1];
|
|
gui[6] = 0;
|
|
gui[7] = 0;
|
|
} break;
|
|
case ShaderLanguage::TYPE_MAT3: {
|
|
|
|
Basis v = value;
|
|
float *gui = (float *)data;
|
|
|
|
gui[0] = v.elements[0][0];
|
|
gui[1] = v.elements[1][0];
|
|
gui[2] = v.elements[2][0];
|
|
gui[3] = 0;
|
|
gui[4] = v.elements[0][1];
|
|
gui[5] = v.elements[1][1];
|
|
gui[6] = v.elements[2][1];
|
|
gui[7] = 0;
|
|
gui[8] = v.elements[0][2];
|
|
gui[9] = v.elements[1][2];
|
|
gui[10] = v.elements[2][2];
|
|
gui[11] = 0;
|
|
} break;
|
|
case ShaderLanguage::TYPE_MAT4: {
|
|
|
|
Transform v = value;
|
|
float *gui = (float *)data;
|
|
|
|
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<ShaderLanguage::ConstantNode::Value> &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, uint8_t *data) {
|
|
|
|
switch (type) {
|
|
|
|
case ShaderLanguage::TYPE_BOOL:
|
|
case ShaderLanguage::TYPE_INT:
|
|
case ShaderLanguage::TYPE_UINT:
|
|
case ShaderLanguage::TYPE_FLOAT: {
|
|
zeromem(data, 4);
|
|
} break;
|
|
case ShaderLanguage::TYPE_BVEC2:
|
|
case ShaderLanguage::TYPE_IVEC2:
|
|
case ShaderLanguage::TYPE_UVEC2:
|
|
case ShaderLanguage::TYPE_VEC2: {
|
|
zeromem(data, 8);
|
|
} 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: {
|
|
|
|
zeromem(data, 16);
|
|
} break;
|
|
case ShaderLanguage::TYPE_MAT2: {
|
|
|
|
zeromem(data, 32);
|
|
} break;
|
|
case ShaderLanguage::TYPE_MAT3: {
|
|
|
|
zeromem(data, 48);
|
|
} break;
|
|
case ShaderLanguage::TYPE_MAT4: {
|
|
zeromem(data, 64);
|
|
} break;
|
|
|
|
default: {
|
|
}
|
|
}
|
|
}
|
|
|
|
void RasterizerStorageRD::MaterialData::update_uniform_buffer(const Map<StringName, ShaderLanguage::ShaderNode::Uniform> &p_uniforms, const uint32_t *p_uniform_offsets, const Map<StringName, Variant> &p_parameters, uint8_t *p_buffer, uint32_t p_buffer_size, bool p_use_linear_color) {
|
|
|
|
bool uses_global_buffer = false;
|
|
|
|
for (Map<StringName, ShaderLanguage::ShaderNode::Uniform>::Element *E = p_uniforms.front(); E; E = E->next()) {
|
|
|
|
if (E->get().order < 0)
|
|
continue; // texture, does not go here
|
|
|
|
if (E->get().scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) {
|
|
continue; //instance uniforms don't appear in the bufferr
|
|
}
|
|
|
|
if (E->get().scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_GLOBAL) {
|
|
//this is a global variable, get the index to it
|
|
RasterizerStorageRD *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->get().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->get().order];
|
|
#ifdef DEBUG_ENABLED
|
|
uint32_t size = ShaderLanguage::get_type_size(E->get().type);
|
|
ERR_CONTINUE(offset + size > p_buffer_size);
|
|
#endif
|
|
uint8_t *data = &p_buffer[offset];
|
|
const Map<StringName, Variant>::Element *V = p_parameters.find(E->key());
|
|
|
|
if (V) {
|
|
//user provided
|
|
_fill_std140_variant_ubo_value(E->get().type, V->get(), data, p_use_linear_color);
|
|
|
|
} else if (E->get().default_value.size()) {
|
|
//default value
|
|
_fill_std140_ubo_value(E->get().type, E->get().default_value, data);
|
|
//value=E->get().default_value;
|
|
} else {
|
|
//zero because it was not provided
|
|
if (E->get().type == ShaderLanguage::TYPE_VEC4 && E->get().hint == ShaderLanguage::ShaderNode::Uniform::HINT_COLOR) {
|
|
//colors must be set as black, with alpha as 1.0
|
|
_fill_std140_variant_ubo_value(E->get().type, Color(0, 0, 0, 1), data, p_use_linear_color);
|
|
} else {
|
|
//else just zero it out
|
|
_fill_std140_ubo_empty(E->get().type, data);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (uses_global_buffer != (global_buffer_E != nullptr)) {
|
|
RasterizerStorageRD *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;
|
|
}
|
|
}
|
|
}
|
|
|
|
RasterizerStorageRD::MaterialData::~MaterialData() {
|
|
if (global_buffer_E) {
|
|
//unregister global buffers
|
|
RasterizerStorageRD *rs = base_singleton;
|
|
rs->global_variables.materials_using_buffer.erase(global_buffer_E);
|
|
}
|
|
|
|
if (global_texture_E) {
|
|
//unregister global textures
|
|
RasterizerStorageRD *rs = base_singleton;
|
|
|
|
for (Map<StringName, uint64_t>::Element *E = used_global_textures.front(); E; E = E->next()) {
|
|
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);
|
|
}
|
|
}
|
|
|
|
void RasterizerStorageRD::MaterialData::update_textures(const Map<StringName, Variant> &p_parameters, const Map<StringName, RID> &p_default_textures, const Vector<ShaderCompilerRD::GeneratedCode::Texture> &p_texture_uniforms, RID *p_textures, bool p_use_linear_color) {
|
|
|
|
RasterizerStorageRD *singleton = (RasterizerStorageRD *)RasterizerStorage::base_singleton;
|
|
#ifdef TOOLS_ENABLED
|
|
Texture *roughness_detect_texture = nullptr;
|
|
RS::TextureDetectRoughnessChannel roughness_channel = RS::TEXTURE_DETECT_ROUGNHESS_R;
|
|
Texture *normal_detect_texture = nullptr;
|
|
#endif
|
|
|
|
bool uses_global_textures = false;
|
|
global_textures_pass++;
|
|
|
|
for (int i = 0; i < p_texture_uniforms.size(); i++) {
|
|
|
|
const StringName &uniform_name = p_texture_uniforms[i].name;
|
|
|
|
RID texture;
|
|
|
|
if (p_texture_uniforms[i].global) {
|
|
|
|
RasterizerStorageRD *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<StringName, uint64_t>::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;
|
|
}
|
|
|
|
texture = 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 {
|
|
if (!texture.is_valid()) {
|
|
|
|
const Map<StringName, Variant>::Element *V = p_parameters.find(uniform_name);
|
|
if (V) {
|
|
texture = V->get();
|
|
}
|
|
}
|
|
|
|
if (!texture.is_valid()) {
|
|
const Map<StringName, RID>::Element *W = p_default_textures.find(uniform_name);
|
|
if (W) {
|
|
|
|
texture = W->get();
|
|
}
|
|
}
|
|
}
|
|
|
|
RID rd_texture;
|
|
|
|
if (texture.is_null()) {
|
|
//check default usage
|
|
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_NONE: {
|
|
rd_texture = singleton->texture_rd_get_default(DEFAULT_RD_TEXTURE_NORMAL);
|
|
} break;
|
|
case ShaderLanguage::ShaderNode::Uniform::HINT_ANISO: {
|
|
rd_texture = singleton->texture_rd_get_default(DEFAULT_RD_TEXTURE_ANISO);
|
|
} break;
|
|
default: {
|
|
rd_texture = singleton->texture_rd_get_default(DEFAULT_RD_TEXTURE_WHITE);
|
|
} break;
|
|
}
|
|
} 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);
|
|
|
|
Texture *tex = singleton->texture_owner.getornull(texture);
|
|
|
|
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()) {
|
|
//wtf
|
|
rd_texture = singleton->texture_rd_get_default(DEFAULT_RD_TEXTURE_WHITE);
|
|
}
|
|
}
|
|
|
|
p_textures[i] = rd_texture;
|
|
}
|
|
#ifdef TOOLS_ENABLED
|
|
if (roughness_detect_texture && normal_detect_texture && normal_detect_texture->path != String()) {
|
|
roughness_detect_texture->detect_roughness_callback(roughness_detect_texture->detect_roughness_callback_ud, normal_detect_texture->path, roughness_channel);
|
|
}
|
|
#endif
|
|
{
|
|
//for textures no longer used, unregister them
|
|
List<Map<StringName, uint64_t>::Element *> to_delete;
|
|
RasterizerStorageRD *rs = base_singleton;
|
|
|
|
for (Map<StringName, uint64_t>::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 RasterizerStorageRD::material_force_update_textures(RID p_material, ShaderType p_shader_type) {
|
|
Material *material = material_owner.getornull(p_material);
|
|
if (material->shader_type != p_shader_type) {
|
|
return;
|
|
}
|
|
if (material->data) {
|
|
material->data->update_parameters(material->params, false, true);
|
|
}
|
|
}
|
|
|
|
void RasterizerStorageRD::_update_queued_materials() {
|
|
Material *material = material_update_list;
|
|
while (material) {
|
|
Material *next = material->update_next;
|
|
|
|
if (material->data) {
|
|
material->data->update_parameters(material->params, material->uniform_dirty, material->texture_dirty);
|
|
}
|
|
material->update_requested = false;
|
|
material->texture_dirty = false;
|
|
material->uniform_dirty = false;
|
|
material->update_next = nullptr;
|
|
material = next;
|
|
}
|
|
material_update_list = nullptr;
|
|
}
|
|
/* MESH API */
|
|
|
|
RID RasterizerStorageRD::mesh_create() {
|
|
|
|
return mesh_owner.make_rid(Mesh());
|
|
}
|
|
|
|
/// Returns stride
|
|
void RasterizerStorageRD::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface) {
|
|
|
|
Mesh *mesh = mesh_owner.getornull(p_mesh);
|
|
ERR_FAIL_COND(!mesh);
|
|
|
|
//ensure blend shape consistency
|
|
ERR_FAIL_COND(mesh->blend_shape_count && p_surface.blend_shapes.size() != (int)mesh->blend_shape_count);
|
|
ERR_FAIL_COND(mesh->blend_shape_count && p_surface.bone_aabbs.size() != mesh->bone_aabbs.size());
|
|
|
|
#ifdef DEBUG_ENABLED
|
|
//do a validation, to catch errors first
|
|
{
|
|
|
|
uint32_t 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: {
|
|
|
|
if (p_surface.format & RS::ARRAY_COMPRESS_NORMAL) {
|
|
stride += sizeof(int8_t) * 4;
|
|
} else {
|
|
stride += sizeof(float) * 4;
|
|
}
|
|
|
|
} break;
|
|
case RS::ARRAY_TANGENT: {
|
|
|
|
if (p_surface.format & RS::ARRAY_COMPRESS_TANGENT) {
|
|
stride += sizeof(int8_t) * 4;
|
|
} else {
|
|
stride += sizeof(float) * 4;
|
|
}
|
|
|
|
} break;
|
|
case RS::ARRAY_COLOR: {
|
|
|
|
if (p_surface.format & RS::ARRAY_COMPRESS_COLOR) {
|
|
stride += sizeof(int8_t) * 4;
|
|
} else {
|
|
stride += sizeof(float) * 4;
|
|
}
|
|
|
|
} break;
|
|
case RS::ARRAY_TEX_UV: {
|
|
|
|
if (p_surface.format & RS::ARRAY_COMPRESS_TEX_UV) {
|
|
stride += sizeof(int16_t) * 2;
|
|
} else {
|
|
stride += sizeof(float) * 2;
|
|
}
|
|
|
|
} break;
|
|
case RS::ARRAY_TEX_UV2: {
|
|
|
|
if (p_surface.format & RS::ARRAY_COMPRESS_TEX_UV2) {
|
|
stride += sizeof(int16_t) * 2;
|
|
} else {
|
|
stride += sizeof(float) * 2;
|
|
}
|
|
|
|
} break;
|
|
case RS::ARRAY_BONES: {
|
|
//assumed weights too
|
|
|
|
//unique format, internally 16 bits, exposed as single array for 32
|
|
|
|
stride += sizeof(int32_t) * 4;
|
|
|
|
} break;
|
|
}
|
|
}
|
|
}
|
|
|
|
int expected_size = stride * p_surface.vertex_count;
|
|
ERR_FAIL_COND_MSG(expected_size != p_surface.vertex_data.size(), "Size of data provided (" + itos(p_surface.vertex_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;
|
|
|
|
s->vertex_buffer = RD::get_singleton()->vertex_buffer_create(p_surface.vertex_data.size(), p_surface.vertex_data);
|
|
s->vertex_count = p_surface.vertex_count;
|
|
|
|
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->aabb = p_surface.aabb;
|
|
s->bone_aabbs = p_surface.bone_aabbs; //only really useful for returning them.
|
|
|
|
for (int i = 0; i < p_surface.blend_shapes.size(); i++) {
|
|
|
|
if (p_surface.blend_shapes[i].size() != p_surface.vertex_data.size()) {
|
|
memdelete(s);
|
|
ERR_FAIL_COND(p_surface.blend_shapes[i].size() != p_surface.vertex_data.size());
|
|
}
|
|
RID vertex_buffer = RD::get_singleton()->vertex_buffer_create(p_surface.blend_shapes[i].size(), p_surface.blend_shapes[i]);
|
|
s->blend_shapes.push_back(vertex_buffer);
|
|
}
|
|
|
|
mesh->blend_shape_count = p_surface.blend_shapes.size();
|
|
|
|
if (mesh->surface_count == 0) {
|
|
mesh->bone_aabbs = p_surface.bone_aabbs;
|
|
mesh->aabb = p_surface.aabb;
|
|
} else {
|
|
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++;
|
|
|
|
mesh->instance_dependency.instance_notify_changed(true, true);
|
|
|
|
mesh->material_cache.clear();
|
|
}
|
|
|
|
int RasterizerStorageRD::mesh_get_blend_shape_count(RID p_mesh) const {
|
|
const Mesh *mesh = mesh_owner.getornull(p_mesh);
|
|
ERR_FAIL_COND_V(!mesh, -1);
|
|
return mesh->blend_shape_count;
|
|
}
|
|
|
|
void RasterizerStorageRD::mesh_set_blend_shape_mode(RID p_mesh, RS::BlendShapeMode p_mode) {
|
|
Mesh *mesh = mesh_owner.getornull(p_mesh);
|
|
ERR_FAIL_COND(!mesh);
|
|
ERR_FAIL_INDEX((int)p_mode, 2);
|
|
|
|
mesh->blend_shape_mode = p_mode;
|
|
}
|
|
RS::BlendShapeMode RasterizerStorageRD::mesh_get_blend_shape_mode(RID p_mesh) const {
|
|
Mesh *mesh = mesh_owner.getornull(p_mesh);
|
|
ERR_FAIL_COND_V(!mesh, RS::BLEND_SHAPE_MODE_NORMALIZED);
|
|
return mesh->blend_shape_mode;
|
|
}
|
|
|
|
void RasterizerStorageRD::mesh_surface_update_region(RID p_mesh, int p_surface, int p_offset, const Vector<uint8_t> &p_data) {
|
|
Mesh *mesh = mesh_owner.getornull(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 RasterizerStorageRD::mesh_surface_set_material(RID p_mesh, int p_surface, RID p_material) {
|
|
Mesh *mesh = mesh_owner.getornull(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->instance_dependency.instance_notify_changed(false, true);
|
|
mesh->material_cache.clear();
|
|
}
|
|
RID RasterizerStorageRD::mesh_surface_get_material(RID p_mesh, int p_surface) const {
|
|
Mesh *mesh = mesh_owner.getornull(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 RasterizerStorageRD::mesh_get_surface(RID p_mesh, int p_surface) const {
|
|
|
|
Mesh *mesh = mesh_owner.getornull(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);
|
|
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;
|
|
|
|
for (int i = 0; i < s.blend_shapes.size(); i++) {
|
|
Vector<uint8_t> bs = RD::get_singleton()->buffer_get_data(s.blend_shapes[i]);
|
|
sd.blend_shapes.push_back(bs);
|
|
}
|
|
|
|
return sd;
|
|
}
|
|
|
|
int RasterizerStorageRD::mesh_get_surface_count(RID p_mesh) const {
|
|
Mesh *mesh = mesh_owner.getornull(p_mesh);
|
|
ERR_FAIL_COND_V(!mesh, 0);
|
|
return mesh->surface_count;
|
|
}
|
|
|
|
void RasterizerStorageRD::mesh_set_custom_aabb(RID p_mesh, const AABB &p_aabb) {
|
|
Mesh *mesh = mesh_owner.getornull(p_mesh);
|
|
ERR_FAIL_COND(!mesh);
|
|
mesh->custom_aabb = p_aabb;
|
|
}
|
|
AABB RasterizerStorageRD::mesh_get_custom_aabb(RID p_mesh) const {
|
|
Mesh *mesh = mesh_owner.getornull(p_mesh);
|
|
ERR_FAIL_COND_V(!mesh, AABB());
|
|
return mesh->custom_aabb;
|
|
}
|
|
|
|
AABB RasterizerStorageRD::mesh_get_aabb(RID p_mesh, RID p_skeleton) {
|
|
Mesh *mesh = mesh_owner.getornull(p_mesh);
|
|
ERR_FAIL_COND_V(!mesh, AABB());
|
|
|
|
if (mesh->custom_aabb != AABB()) {
|
|
return mesh->custom_aabb;
|
|
}
|
|
|
|
Skeleton *skeleton = skeleton_owner.getornull(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;
|
|
|
|
Transform 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;
|
|
|
|
Transform 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 RasterizerStorageRD::mesh_clear(RID p_mesh) {
|
|
|
|
Mesh *mesh = mesh_owner.getornull(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.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);
|
|
}
|
|
|
|
for (int32_t j = 0; j < s.blend_shapes.size(); j++) {
|
|
RD::get_singleton()->free(s.blend_shapes[j]);
|
|
}
|
|
|
|
if (s.blend_shape_base_buffer.is_valid()) {
|
|
RD::get_singleton()->free(s.blend_shape_base_buffer);
|
|
}
|
|
|
|
memdelete(mesh->surfaces[i]);
|
|
}
|
|
if (mesh->surfaces) {
|
|
memfree(mesh->surfaces);
|
|
}
|
|
|
|
mesh->surfaces = nullptr;
|
|
mesh->surface_count = 0;
|
|
mesh->material_cache.clear();
|
|
mesh->instance_dependency.instance_notify_changed(true, true);
|
|
}
|
|
|
|
void RasterizerStorageRD::_mesh_surface_generate_version_for_input_mask(Mesh::Surface *s, uint32_t p_input_mask) {
|
|
uint32_t version = s->version_count;
|
|
s->version_count++;
|
|
s->versions = (Mesh::Surface::Version *)memrealloc(s->versions, sizeof(Mesh::Surface::Version) * s->version_count);
|
|
|
|
Mesh::Surface::Version &v = s->versions[version];
|
|
|
|
Vector<RD::VertexAttribute> attributes;
|
|
Vector<RID> buffers;
|
|
|
|
uint32_t stride = 0;
|
|
|
|
for (int i = 0; i < RS::ARRAY_WEIGHTS; 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];
|
|
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_BONES: {
|
|
|
|
//assumed weights too
|
|
vd.format = RD::DATA_FORMAT_R32G32B32A32_UINT;
|
|
} break;
|
|
}
|
|
} else {
|
|
//Supplied, use it
|
|
|
|
vd.offset = stride;
|
|
vd.stride = 1; //mark that it needs a stride set
|
|
buffer = s->vertex_buffer;
|
|
|
|
switch (i) {
|
|
|
|
case RS::ARRAY_VERTEX: {
|
|
|
|
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;
|
|
}
|
|
|
|
} break;
|
|
case RS::ARRAY_NORMAL: {
|
|
|
|
if (s->format & RS::ARRAY_COMPRESS_NORMAL) {
|
|
vd.format = RD::DATA_FORMAT_R8G8B8A8_SNORM;
|
|
stride += sizeof(int8_t) * 4;
|
|
} else {
|
|
vd.format = RD::DATA_FORMAT_R32G32B32A32_SFLOAT;
|
|
stride += sizeof(float) * 4;
|
|
}
|
|
|
|
} break;
|
|
case RS::ARRAY_TANGENT: {
|
|
|
|
if (s->format & RS::ARRAY_COMPRESS_TANGENT) {
|
|
vd.format = RD::DATA_FORMAT_R8G8B8A8_SNORM;
|
|
stride += sizeof(int8_t) * 4;
|
|
} else {
|
|
vd.format = RD::DATA_FORMAT_R32G32B32A32_SFLOAT;
|
|
stride += sizeof(float) * 4;
|
|
}
|
|
|
|
} break;
|
|
case RS::ARRAY_COLOR: {
|
|
|
|
if (s->format & RS::ARRAY_COMPRESS_COLOR) {
|
|
vd.format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
|
|
stride += sizeof(int8_t) * 4;
|
|
} else {
|
|
vd.format = RD::DATA_FORMAT_R32G32B32A32_SFLOAT;
|
|
stride += sizeof(float) * 4;
|
|
}
|
|
|
|
} break;
|
|
case RS::ARRAY_TEX_UV: {
|
|
|
|
if (s->format & RS::ARRAY_COMPRESS_TEX_UV) {
|
|
vd.format = RD::DATA_FORMAT_R16G16_SFLOAT;
|
|
stride += sizeof(int16_t) * 2;
|
|
} else {
|
|
vd.format = RD::DATA_FORMAT_R32G32_SFLOAT;
|
|
stride += sizeof(float) * 2;
|
|
}
|
|
|
|
} break;
|
|
case RS::ARRAY_TEX_UV2: {
|
|
|
|
if (s->format & RS::ARRAY_COMPRESS_TEX_UV2) {
|
|
vd.format = RD::DATA_FORMAT_R16G16_SFLOAT;
|
|
stride += sizeof(int16_t) * 2;
|
|
} else {
|
|
vd.format = RD::DATA_FORMAT_R32G32_SFLOAT;
|
|
stride += sizeof(float) * 2;
|
|
}
|
|
|
|
} break;
|
|
case RS::ARRAY_BONES: {
|
|
//assumed weights too
|
|
|
|
//unique format, internally 16 bits, exposed as single array for 32
|
|
|
|
vd.format = RD::DATA_FORMAT_R32G32B32A32_UINT;
|
|
stride += sizeof(int32_t) * 4;
|
|
|
|
} break;
|
|
}
|
|
}
|
|
|
|
if (!(p_input_mask & (1 << i))) {
|
|
continue; // Shader does not need this, skip it
|
|
}
|
|
|
|
attributes.push_back(vd);
|
|
buffers.push_back(buffer);
|
|
}
|
|
|
|
//update final stride
|
|
for (int i = 0; i < attributes.size(); i++) {
|
|
if (attributes[i].stride == 1) {
|
|
attributes.write[i].stride = 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 RasterizerStorageRD::multimesh_create() {
|
|
|
|
return multimesh_owner.make_rid(MultiMesh());
|
|
}
|
|
|
|
void RasterizerStorageRD::multimesh_allocate(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors, bool p_use_custom_data) {
|
|
|
|
MultiMesh *multimesh = multimesh_owner.getornull(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_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<float>();
|
|
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);
|
|
}
|
|
}
|
|
|
|
int RasterizerStorageRD::multimesh_get_instance_count(RID p_multimesh) const {
|
|
MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh);
|
|
ERR_FAIL_COND_V(!multimesh, 0);
|
|
return multimesh->instances;
|
|
}
|
|
|
|
void RasterizerStorageRD::multimesh_set_mesh(RID p_multimesh, RID p_mesh) {
|
|
MultiMesh *multimesh = multimesh_owner.getornull(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<uint8_t> 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->instance_dependency.instance_notify_changed(true, true);
|
|
}
|
|
|
|
#define MULTIMESH_DIRTY_REGION_SIZE 512
|
|
|
|
void RasterizerStorageRD::_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<uint8_t> buffer = RD::get_singleton()->buffer_get_data(multimesh->buffer);
|
|
{
|
|
|
|
const uint8_t *r = buffer.ptr();
|
|
copymem(w, r, buffer.size());
|
|
}
|
|
} else {
|
|
zeromem(w, 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] = 0;
|
|
}
|
|
multimesh->data_cache_used_dirty_regions = 0;
|
|
}
|
|
|
|
void RasterizerStorageRD::_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 RasterizerStorageRD::_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 RasterizerStorageRD::_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;
|
|
Transform 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 RasterizerStorageRD::multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform &p_transform) {
|
|
|
|
MultiMesh *multimesh = multimesh_owner.getornull(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 RasterizerStorageRD::multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) {
|
|
|
|
MultiMesh *multimesh = multimesh_owner.getornull(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 RasterizerStorageRD::multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) {
|
|
|
|
MultiMesh *multimesh = multimesh_owner.getornull(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 RasterizerStorageRD::multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) {
|
|
MultiMesh *multimesh = multimesh_owner.getornull(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 RasterizerStorageRD::multimesh_get_mesh(RID p_multimesh) const {
|
|
|
|
MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh);
|
|
ERR_FAIL_COND_V(!multimesh, RID());
|
|
|
|
return multimesh->mesh;
|
|
}
|
|
|
|
Transform RasterizerStorageRD::multimesh_instance_get_transform(RID p_multimesh, int p_index) const {
|
|
|
|
MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh);
|
|
ERR_FAIL_COND_V(!multimesh, Transform());
|
|
ERR_FAIL_INDEX_V(p_index, multimesh->instances, Transform());
|
|
ERR_FAIL_COND_V(multimesh->xform_format != RS::MULTIMESH_TRANSFORM_3D, Transform());
|
|
|
|
_multimesh_make_local(multimesh);
|
|
|
|
Transform 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 RasterizerStorageRD::multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const {
|
|
|
|
MultiMesh *multimesh = multimesh_owner.getornull(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 RasterizerStorageRD::multimesh_instance_get_color(RID p_multimesh, int p_index) const {
|
|
|
|
MultiMesh *multimesh = multimesh_owner.getornull(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 RasterizerStorageRD::multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const {
|
|
|
|
MultiMesh *multimesh = multimesh_owner.getornull(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 RasterizerStorageRD::multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) {
|
|
MultiMesh *multimesh = multimesh_owner.getornull(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, false);
|
|
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->instance_dependency.instance_notify_changed(true, false);
|
|
}
|
|
}
|
|
|
|
Vector<float> RasterizerStorageRD::multimesh_get_buffer(RID p_multimesh) const {
|
|
MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh);
|
|
ERR_FAIL_COND_V(!multimesh, Vector<float>());
|
|
if (multimesh->buffer.is_null()) {
|
|
return Vector<float>();
|
|
} else if (multimesh->data_cache.size()) {
|
|
return multimesh->data_cache;
|
|
} else {
|
|
//get from memory
|
|
|
|
Vector<uint8_t> buffer = RD::get_singleton()->buffer_get_data(multimesh->buffer);
|
|
Vector<float> ret;
|
|
ret.resize(multimesh->instances);
|
|
{
|
|
float *w = multimesh->data_cache.ptrw();
|
|
const uint8_t *r = buffer.ptr();
|
|
copymem(w, r, buffer.size());
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
void RasterizerStorageRD::multimesh_set_visible_instances(RID p_multimesh, int p_visible) {
|
|
|
|
MultiMesh *multimesh = multimesh_owner.getornull(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;
|
|
}
|
|
int RasterizerStorageRD::multimesh_get_visible_instances(RID p_multimesh) const {
|
|
MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh);
|
|
ERR_FAIL_COND_V(!multimesh, 0);
|
|
return multimesh->visible_instances;
|
|
}
|
|
|
|
AABB RasterizerStorageRD::multimesh_get_aabb(RID p_multimesh) const {
|
|
MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh);
|
|
ERR_FAIL_COND_V(!multimesh, AABB());
|
|
if (multimesh->aabb_dirty) {
|
|
const_cast<RasterizerStorageRD *>(this)->_update_dirty_multimeshes();
|
|
}
|
|
return multimesh->aabb;
|
|
}
|
|
|
|
void RasterizerStorageRD::_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 - 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 * multimesh->stride_cache * sizeof(float)), data, false);
|
|
} 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]) {
|
|
uint64_t offset = i * region_size;
|
|
uint64_t size = multimesh->stride_cache * multimesh->instances * sizeof(float);
|
|
RD::get_singleton()->buffer_update(multimesh->buffer, offset, MIN(region_size, size - offset), &data[i * region_size], false);
|
|
}
|
|
}
|
|
}
|
|
|
|
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->instance_dependency.instance_notify_changed(true, false);
|
|
}
|
|
}
|
|
|
|
multimesh_dirty_list = multimesh->dirty_list;
|
|
|
|
multimesh->dirty_list = nullptr;
|
|
multimesh->dirty = false;
|
|
}
|
|
|
|
multimesh_dirty_list = nullptr;
|
|
}
|
|
|
|
/* SKELETON */
|
|
|
|
/* SKELETON API */
|
|
|
|
RID RasterizerStorageRD::skeleton_create() {
|
|
|
|
return skeleton_owner.make_rid(Skeleton());
|
|
}
|
|
|
|
void RasterizerStorageRD::_skeleton_make_dirty(Skeleton *skeleton) {
|
|
|
|
if (!skeleton->dirty) {
|
|
skeleton->dirty = true;
|
|
skeleton->dirty_list = skeleton_dirty_list;
|
|
skeleton_dirty_list = skeleton;
|
|
}
|
|
}
|
|
|
|
void RasterizerStorageRD::skeleton_allocate(RID p_skeleton, int p_bones, bool p_2d_skeleton) {
|
|
|
|
Skeleton *skeleton = skeleton_owner.getornull(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.resize(0);
|
|
}
|
|
|
|
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));
|
|
zeromem(skeleton->data.ptrw(), skeleton->data.size() * sizeof(float));
|
|
|
|
_skeleton_make_dirty(skeleton);
|
|
}
|
|
}
|
|
int RasterizerStorageRD::skeleton_get_bone_count(RID p_skeleton) const {
|
|
|
|
Skeleton *skeleton = skeleton_owner.getornull(p_skeleton);
|
|
ERR_FAIL_COND_V(!skeleton, 0);
|
|
|
|
return skeleton->size;
|
|
}
|
|
|
|
void RasterizerStorageRD::skeleton_bone_set_transform(RID p_skeleton, int p_bone, const Transform &p_transform) {
|
|
|
|
Skeleton *skeleton = skeleton_owner.getornull(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);
|
|
}
|
|
|
|
Transform RasterizerStorageRD::skeleton_bone_get_transform(RID p_skeleton, int p_bone) const {
|
|
|
|
Skeleton *skeleton = skeleton_owner.getornull(p_skeleton);
|
|
|
|
ERR_FAIL_COND_V(!skeleton, Transform());
|
|
ERR_FAIL_INDEX_V(p_bone, skeleton->size, Transform());
|
|
ERR_FAIL_COND_V(skeleton->use_2d, Transform());
|
|
|
|
const float *dataptr = skeleton->data.ptr() + p_bone * 12;
|
|
|
|
Transform 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 RasterizerStorageRD::skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform) {
|
|
|
|
Skeleton *skeleton = skeleton_owner.getornull(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 RasterizerStorageRD::skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const {
|
|
|
|
Skeleton *skeleton = skeleton_owner.getornull(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 RasterizerStorageRD::skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform) {
|
|
|
|
Skeleton *skeleton = skeleton_owner.getornull(p_skeleton);
|
|
|
|
ERR_FAIL_COND(!skeleton->use_2d);
|
|
|
|
skeleton->base_transform_2d = p_base_transform;
|
|
}
|
|
|
|
void RasterizerStorageRD::_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(), false);
|
|
}
|
|
|
|
skeleton_dirty_list = skeleton->dirty_list;
|
|
|
|
skeleton->instance_dependency.instance_notify_changed(true, false);
|
|
|
|
skeleton->dirty = false;
|
|
skeleton->dirty_list = nullptr;
|
|
}
|
|
|
|
skeleton_dirty_list = nullptr;
|
|
}
|
|
|
|
/* LIGHT */
|
|
|
|
RID RasterizerStorageRD::light_create(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_SPOT_ANGLE] = 45;
|
|
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_BIAS] = 0.02;
|
|
light.param[RS::LIGHT_PARAM_SHADOW_NORMAL_BIAS] = 1.0;
|
|
light.param[RS::LIGHT_PARAM_SHADOW_PANCAKE_SIZE] = 20.0;
|
|
light.param[RS::LIGHT_PARAM_TRANSMITTANCE_BIAS] = 0.05;
|
|
|
|
return light_owner.make_rid(light);
|
|
}
|
|
|
|
void RasterizerStorageRD::light_set_color(RID p_light, const Color &p_color) {
|
|
|
|
Light *light = light_owner.getornull(p_light);
|
|
ERR_FAIL_COND(!light);
|
|
|
|
light->color = p_color;
|
|
}
|
|
void RasterizerStorageRD::light_set_param(RID p_light, RS::LightParam p_param, float p_value) {
|
|
|
|
Light *light = light_owner.getornull(p_light);
|
|
ERR_FAIL_COND(!light);
|
|
ERR_FAIL_INDEX(p_param, RS::LIGHT_PARAM_MAX);
|
|
|
|
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->instance_dependency.instance_notify_changed(true, false);
|
|
} break;
|
|
default: {
|
|
}
|
|
}
|
|
|
|
light->param[p_param] = p_value;
|
|
}
|
|
void RasterizerStorageRD::light_set_shadow(RID p_light, bool p_enabled) {
|
|
|
|
Light *light = light_owner.getornull(p_light);
|
|
ERR_FAIL_COND(!light);
|
|
light->shadow = p_enabled;
|
|
|
|
light->version++;
|
|
light->instance_dependency.instance_notify_changed(true, false);
|
|
}
|
|
|
|
void RasterizerStorageRD::light_set_shadow_color(RID p_light, const Color &p_color) {
|
|
|
|
Light *light = light_owner.getornull(p_light);
|
|
ERR_FAIL_COND(!light);
|
|
light->shadow_color = p_color;
|
|
}
|
|
|
|
void RasterizerStorageRD::light_set_projector(RID p_light, RID p_texture) {
|
|
|
|
Light *light = light_owner.getornull(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 && light->projector.is_valid()) {
|
|
texture_add_to_decal_atlas(light->projector, light->type == RS::LIGHT_OMNI);
|
|
}
|
|
}
|
|
|
|
void RasterizerStorageRD::light_set_negative(RID p_light, bool p_enable) {
|
|
|
|
Light *light = light_owner.getornull(p_light);
|
|
ERR_FAIL_COND(!light);
|
|
|
|
light->negative = p_enable;
|
|
}
|
|
void RasterizerStorageRD::light_set_cull_mask(RID p_light, uint32_t p_mask) {
|
|
|
|
Light *light = light_owner.getornull(p_light);
|
|
ERR_FAIL_COND(!light);
|
|
|
|
light->cull_mask = p_mask;
|
|
|
|
light->version++;
|
|
light->instance_dependency.instance_notify_changed(true, false);
|
|
}
|
|
|
|
void RasterizerStorageRD::light_set_reverse_cull_face_mode(RID p_light, bool p_enabled) {
|
|
|
|
Light *light = light_owner.getornull(p_light);
|
|
ERR_FAIL_COND(!light);
|
|
|
|
light->reverse_cull = p_enabled;
|
|
|
|
light->version++;
|
|
light->instance_dependency.instance_notify_changed(true, false);
|
|
}
|
|
|
|
void RasterizerStorageRD::light_set_use_gi(RID p_light, bool p_enabled) {
|
|
Light *light = light_owner.getornull(p_light);
|
|
ERR_FAIL_COND(!light);
|
|
|
|
light->use_gi = p_enabled;
|
|
|
|
light->version++;
|
|
light->instance_dependency.instance_notify_changed(true, false);
|
|
}
|
|
void RasterizerStorageRD::light_omni_set_shadow_mode(RID p_light, RS::LightOmniShadowMode p_mode) {
|
|
|
|
Light *light = light_owner.getornull(p_light);
|
|
ERR_FAIL_COND(!light);
|
|
|
|
light->omni_shadow_mode = p_mode;
|
|
|
|
light->version++;
|
|
light->instance_dependency.instance_notify_changed(true, false);
|
|
}
|
|
|
|
RS::LightOmniShadowMode RasterizerStorageRD::light_omni_get_shadow_mode(RID p_light) {
|
|
|
|
const Light *light = light_owner.getornull(p_light);
|
|
ERR_FAIL_COND_V(!light, RS::LIGHT_OMNI_SHADOW_CUBE);
|
|
|
|
return light->omni_shadow_mode;
|
|
}
|
|
|
|
void RasterizerStorageRD::light_directional_set_shadow_mode(RID p_light, RS::LightDirectionalShadowMode p_mode) {
|
|
|
|
Light *light = light_owner.getornull(p_light);
|
|
ERR_FAIL_COND(!light);
|
|
|
|
light->directional_shadow_mode = p_mode;
|
|
light->version++;
|
|
light->instance_dependency.instance_notify_changed(true, false);
|
|
}
|
|
|
|
void RasterizerStorageRD::light_directional_set_blend_splits(RID p_light, bool p_enable) {
|
|
|
|
Light *light = light_owner.getornull(p_light);
|
|
ERR_FAIL_COND(!light);
|
|
|
|
light->directional_blend_splits = p_enable;
|
|
light->version++;
|
|
light->instance_dependency.instance_notify_changed(true, false);
|
|
}
|
|
|
|
bool RasterizerStorageRD::light_directional_get_blend_splits(RID p_light) const {
|
|
|
|
const Light *light = light_owner.getornull(p_light);
|
|
ERR_FAIL_COND_V(!light, false);
|
|
|
|
return light->directional_blend_splits;
|
|
}
|
|
|
|
RS::LightDirectionalShadowMode RasterizerStorageRD::light_directional_get_shadow_mode(RID p_light) {
|
|
|
|
const Light *light = light_owner.getornull(p_light);
|
|
ERR_FAIL_COND_V(!light, RS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL);
|
|
|
|
return light->directional_shadow_mode;
|
|
}
|
|
|
|
void RasterizerStorageRD::light_directional_set_shadow_depth_range_mode(RID p_light, RS::LightDirectionalShadowDepthRangeMode p_range_mode) {
|
|
|
|
Light *light = light_owner.getornull(p_light);
|
|
ERR_FAIL_COND(!light);
|
|
|
|
light->directional_range_mode = p_range_mode;
|
|
}
|
|
|
|
RS::LightDirectionalShadowDepthRangeMode RasterizerStorageRD::light_directional_get_shadow_depth_range_mode(RID p_light) const {
|
|
|
|
const Light *light = light_owner.getornull(p_light);
|
|
ERR_FAIL_COND_V(!light, RS::LIGHT_DIRECTIONAL_SHADOW_DEPTH_RANGE_STABLE);
|
|
|
|
return light->directional_range_mode;
|
|
}
|
|
|
|
bool RasterizerStorageRD::light_get_use_gi(RID p_light) {
|
|
Light *light = light_owner.getornull(p_light);
|
|
ERR_FAIL_COND_V(!light, false);
|
|
|
|
return light->use_gi;
|
|
}
|
|
|
|
uint64_t RasterizerStorageRD::light_get_version(RID p_light) const {
|
|
|
|
const Light *light = light_owner.getornull(p_light);
|
|
ERR_FAIL_COND_V(!light, 0);
|
|
|
|
return light->version;
|
|
}
|
|
|
|
AABB RasterizerStorageRD::light_get_aabb(RID p_light) const {
|
|
|
|
const Light *light = light_owner.getornull(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 RasterizerStorageRD::reflection_probe_create() {
|
|
|
|
return reflection_probe_owner.make_rid(ReflectionProbe());
|
|
}
|
|
|
|
void RasterizerStorageRD::reflection_probe_set_update_mode(RID p_probe, RS::ReflectionProbeUpdateMode p_mode) {
|
|
|
|
ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
|
|
ERR_FAIL_COND(!reflection_probe);
|
|
|
|
reflection_probe->update_mode = p_mode;
|
|
reflection_probe->instance_dependency.instance_notify_changed(true, false);
|
|
}
|
|
|
|
void RasterizerStorageRD::reflection_probe_set_intensity(RID p_probe, float p_intensity) {
|
|
|
|
ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
|
|
ERR_FAIL_COND(!reflection_probe);
|
|
|
|
reflection_probe->intensity = p_intensity;
|
|
}
|
|
|
|
void RasterizerStorageRD::reflection_probe_set_interior_ambient(RID p_probe, const Color &p_ambient) {
|
|
|
|
ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
|
|
ERR_FAIL_COND(!reflection_probe);
|
|
|
|
reflection_probe->interior_ambient = p_ambient;
|
|
}
|
|
|
|
void RasterizerStorageRD::reflection_probe_set_interior_ambient_energy(RID p_probe, float p_energy) {
|
|
|
|
ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
|
|
ERR_FAIL_COND(!reflection_probe);
|
|
|
|
reflection_probe->interior_ambient_energy = p_energy;
|
|
}
|
|
|
|
void RasterizerStorageRD::reflection_probe_set_interior_ambient_probe_contribution(RID p_probe, float p_contrib) {
|
|
|
|
ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
|
|
ERR_FAIL_COND(!reflection_probe);
|
|
|
|
reflection_probe->interior_ambient_probe_contrib = p_contrib;
|
|
}
|
|
|
|
void RasterizerStorageRD::reflection_probe_set_max_distance(RID p_probe, float p_distance) {
|
|
|
|
ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
|
|
ERR_FAIL_COND(!reflection_probe);
|
|
|
|
reflection_probe->max_distance = p_distance;
|
|
|
|
reflection_probe->instance_dependency.instance_notify_changed(true, false);
|
|
}
|
|
void RasterizerStorageRD::reflection_probe_set_extents(RID p_probe, const Vector3 &p_extents) {
|
|
|
|
ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
|
|
ERR_FAIL_COND(!reflection_probe);
|
|
|
|
reflection_probe->extents = p_extents;
|
|
reflection_probe->instance_dependency.instance_notify_changed(true, false);
|
|
}
|
|
void RasterizerStorageRD::reflection_probe_set_origin_offset(RID p_probe, const Vector3 &p_offset) {
|
|
|
|
ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
|
|
ERR_FAIL_COND(!reflection_probe);
|
|
|
|
reflection_probe->origin_offset = p_offset;
|
|
reflection_probe->instance_dependency.instance_notify_changed(true, false);
|
|
}
|
|
|
|
void RasterizerStorageRD::reflection_probe_set_as_interior(RID p_probe, bool p_enable) {
|
|
|
|
ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
|
|
ERR_FAIL_COND(!reflection_probe);
|
|
|
|
reflection_probe->interior = p_enable;
|
|
reflection_probe->instance_dependency.instance_notify_changed(true, false);
|
|
}
|
|
void RasterizerStorageRD::reflection_probe_set_enable_box_projection(RID p_probe, bool p_enable) {
|
|
|
|
ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
|
|
ERR_FAIL_COND(!reflection_probe);
|
|
|
|
reflection_probe->box_projection = p_enable;
|
|
}
|
|
|
|
void RasterizerStorageRD::reflection_probe_set_enable_shadows(RID p_probe, bool p_enable) {
|
|
|
|
ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
|
|
ERR_FAIL_COND(!reflection_probe);
|
|
|
|
reflection_probe->enable_shadows = p_enable;
|
|
reflection_probe->instance_dependency.instance_notify_changed(true, false);
|
|
}
|
|
void RasterizerStorageRD::reflection_probe_set_cull_mask(RID p_probe, uint32_t p_layers) {
|
|
|
|
ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
|
|
ERR_FAIL_COND(!reflection_probe);
|
|
|
|
reflection_probe->cull_mask = p_layers;
|
|
reflection_probe->instance_dependency.instance_notify_changed(true, false);
|
|
}
|
|
|
|
void RasterizerStorageRD::reflection_probe_set_resolution(RID p_probe, int p_resolution) {
|
|
|
|
ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
|
|
ERR_FAIL_COND(!reflection_probe);
|
|
ERR_FAIL_COND(p_resolution < 32);
|
|
|
|
reflection_probe->resolution = p_resolution;
|
|
}
|
|
|
|
AABB RasterizerStorageRD::reflection_probe_get_aabb(RID p_probe) const {
|
|
const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(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 RasterizerStorageRD::reflection_probe_get_update_mode(RID p_probe) const {
|
|
|
|
const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
|
|
ERR_FAIL_COND_V(!reflection_probe, RS::REFLECTION_PROBE_UPDATE_ALWAYS);
|
|
|
|
return reflection_probe->update_mode;
|
|
}
|
|
|
|
uint32_t RasterizerStorageRD::reflection_probe_get_cull_mask(RID p_probe) const {
|
|
|
|
const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
|
|
ERR_FAIL_COND_V(!reflection_probe, 0);
|
|
|
|
return reflection_probe->cull_mask;
|
|
}
|
|
|
|
Vector3 RasterizerStorageRD::reflection_probe_get_extents(RID p_probe) const {
|
|
|
|
const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
|
|
ERR_FAIL_COND_V(!reflection_probe, Vector3());
|
|
|
|
return reflection_probe->extents;
|
|
}
|
|
Vector3 RasterizerStorageRD::reflection_probe_get_origin_offset(RID p_probe) const {
|
|
|
|
const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
|
|
ERR_FAIL_COND_V(!reflection_probe, Vector3());
|
|
|
|
return reflection_probe->origin_offset;
|
|
}
|
|
|
|
bool RasterizerStorageRD::reflection_probe_renders_shadows(RID p_probe) const {
|
|
|
|
const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
|
|
ERR_FAIL_COND_V(!reflection_probe, false);
|
|
|
|
return reflection_probe->enable_shadows;
|
|
}
|
|
|
|
float RasterizerStorageRD::reflection_probe_get_origin_max_distance(RID p_probe) const {
|
|
|
|
const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
|
|
ERR_FAIL_COND_V(!reflection_probe, 0);
|
|
|
|
return reflection_probe->max_distance;
|
|
}
|
|
|
|
int RasterizerStorageRD::reflection_probe_get_resolution(RID p_probe) const {
|
|
|
|
const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
|
|
ERR_FAIL_COND_V(!reflection_probe, 0);
|
|
|
|
return reflection_probe->resolution;
|
|
}
|
|
|
|
float RasterizerStorageRD::reflection_probe_get_intensity(RID p_probe) const {
|
|
|
|
const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
|
|
ERR_FAIL_COND_V(!reflection_probe, 0);
|
|
|
|
return reflection_probe->intensity;
|
|
}
|
|
bool RasterizerStorageRD::reflection_probe_is_interior(RID p_probe) const {
|
|
|
|
const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
|
|
ERR_FAIL_COND_V(!reflection_probe, false);
|
|
|
|
return reflection_probe->interior;
|
|
}
|
|
bool RasterizerStorageRD::reflection_probe_is_box_projection(RID p_probe) const {
|
|
|
|
const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
|
|
ERR_FAIL_COND_V(!reflection_probe, false);
|
|
|
|
return reflection_probe->box_projection;
|
|
}
|
|
|
|
Color RasterizerStorageRD::reflection_probe_get_interior_ambient(RID p_probe) const {
|
|
|
|
const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
|
|
ERR_FAIL_COND_V(!reflection_probe, Color());
|
|
|
|
return reflection_probe->interior_ambient;
|
|
}
|
|
float RasterizerStorageRD::reflection_probe_get_interior_ambient_energy(RID p_probe) const {
|
|
|
|
const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
|
|
ERR_FAIL_COND_V(!reflection_probe, 0);
|
|
|
|
return reflection_probe->interior_ambient_energy;
|
|
}
|
|
float RasterizerStorageRD::reflection_probe_get_interior_ambient_probe_contribution(RID p_probe) const {
|
|
|
|
const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
|
|
ERR_FAIL_COND_V(!reflection_probe, 0);
|
|
|
|
return reflection_probe->interior_ambient_probe_contrib;
|
|
}
|
|
|
|
RID RasterizerStorageRD::decal_create() {
|
|
return decal_owner.make_rid(Decal());
|
|
}
|
|
|
|
void RasterizerStorageRD::decal_set_extents(RID p_decal, const Vector3 &p_extents) {
|
|
Decal *decal = decal_owner.getornull(p_decal);
|
|
ERR_FAIL_COND(!decal);
|
|
decal->extents = p_extents;
|
|
decal->instance_dependency.instance_notify_changed(true, false);
|
|
}
|
|
void RasterizerStorageRD::decal_set_texture(RID p_decal, RS::DecalTexture p_type, RID p_texture) {
|
|
Decal *decal = decal_owner.getornull(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->instance_dependency.instance_notify_changed(false, true);
|
|
}
|
|
void RasterizerStorageRD::decal_set_emission_energy(RID p_decal, float p_energy) {
|
|
Decal *decal = decal_owner.getornull(p_decal);
|
|
ERR_FAIL_COND(!decal);
|
|
decal->emission_energy = p_energy;
|
|
}
|
|
|
|
void RasterizerStorageRD::decal_set_albedo_mix(RID p_decal, float p_mix) {
|
|
Decal *decal = decal_owner.getornull(p_decal);
|
|
ERR_FAIL_COND(!decal);
|
|
decal->albedo_mix = p_mix;
|
|
}
|
|
|
|
void RasterizerStorageRD::decal_set_modulate(RID p_decal, const Color &p_modulate) {
|
|
Decal *decal = decal_owner.getornull(p_decal);
|
|
ERR_FAIL_COND(!decal);
|
|
decal->modulate = p_modulate;
|
|
}
|
|
void RasterizerStorageRD::decal_set_cull_mask(RID p_decal, uint32_t p_layers) {
|
|
Decal *decal = decal_owner.getornull(p_decal);
|
|
ERR_FAIL_COND(!decal);
|
|
decal->cull_mask = p_layers;
|
|
decal->instance_dependency.instance_notify_changed(true, false);
|
|
}
|
|
|
|
void RasterizerStorageRD::decal_set_distance_fade(RID p_decal, bool p_enabled, float p_begin, float p_length) {
|
|
|
|
Decal *decal = decal_owner.getornull(p_decal);
|
|
ERR_FAIL_COND(!decal);
|
|
decal->distance_fade = p_enabled;
|
|
decal->distance_fade_begin = p_begin;
|
|
decal->distance_fade_length = p_length;
|
|
}
|
|
|
|
void RasterizerStorageRD::decal_set_fade(RID p_decal, float p_above, float p_below) {
|
|
|
|
Decal *decal = decal_owner.getornull(p_decal);
|
|
ERR_FAIL_COND(!decal);
|
|
decal->upper_fade = p_above;
|
|
decal->lower_fade = p_below;
|
|
}
|
|
|
|
void RasterizerStorageRD::decal_set_normal_fade(RID p_decal, float p_fade) {
|
|
|
|
Decal *decal = decal_owner.getornull(p_decal);
|
|
ERR_FAIL_COND(!decal);
|
|
decal->normal_fade = p_fade;
|
|
}
|
|
|
|
AABB RasterizerStorageRD::decal_get_aabb(RID p_decal) const {
|
|
Decal *decal = decal_owner.getornull(p_decal);
|
|
ERR_FAIL_COND_V(!decal, AABB());
|
|
|
|
return AABB(-decal->extents, decal->extents * 2.0);
|
|
}
|
|
|
|
RID RasterizerStorageRD::gi_probe_create() {
|
|
|
|
return gi_probe_owner.make_rid(GIProbe());
|
|
}
|
|
|
|
void RasterizerStorageRD::gi_probe_allocate(RID p_gi_probe, const Transform &p_to_cell_xform, const AABB &p_aabb, const Vector3i &p_octree_size, const Vector<uint8_t> &p_octree_cells, const Vector<uint8_t> &p_data_cells, const Vector<uint8_t> &p_distance_field, const Vector<int> &p_level_counts) {
|
|
GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe);
|
|
ERR_FAIL_COND(!gi_probe);
|
|
|
|
if (gi_probe->octree_buffer.is_valid()) {
|
|
RD::get_singleton()->free(gi_probe->octree_buffer);
|
|
RD::get_singleton()->free(gi_probe->data_buffer);
|
|
if (gi_probe->sdf_texture.is_valid()) {
|
|
RD::get_singleton()->free(gi_probe->sdf_texture);
|
|
}
|
|
|
|
gi_probe->sdf_texture = RID();
|
|
gi_probe->octree_buffer = RID();
|
|
gi_probe->data_buffer = RID();
|
|
gi_probe->octree_buffer_size = 0;
|
|
gi_probe->data_buffer_size = 0;
|
|
gi_probe->cell_count = 0;
|
|
}
|
|
|
|
gi_probe->to_cell_xform = p_to_cell_xform;
|
|
gi_probe->bounds = p_aabb;
|
|
gi_probe->octree_size = p_octree_size;
|
|
gi_probe->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
|
|
|
|
gi_probe->cell_count = cell_count;
|
|
gi_probe->octree_buffer = RD::get_singleton()->storage_buffer_create(p_octree_cells.size(), p_octree_cells);
|
|
gi_probe->octree_buffer_size = p_octree_cells.size();
|
|
gi_probe->data_buffer = RD::get_singleton()->storage_buffer_create(p_data_cells.size(), p_data_cells);
|
|
gi_probe->data_buffer_size = p_data_cells.size();
|
|
|
|
if (p_distance_field.size()) {
|
|
RD::TextureFormat tf;
|
|
tf.format = RD::DATA_FORMAT_R8_UNORM;
|
|
tf.width = gi_probe->octree_size.x;
|
|
tf.height = gi_probe->octree_size.y;
|
|
tf.depth = gi_probe->octree_size.z;
|
|
tf.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<Vector<uint8_t>> s;
|
|
s.push_back(p_distance_field);
|
|
gi_probe->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 = gi_probe->octree_size.x;
|
|
tf.height = gi_probe->octree_size.y;
|
|
tf.depth = gi_probe->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);
|
|
gi_probe->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, gi_probe->sdf_texture);
|
|
}
|
|
//update SDF texture
|
|
Vector<RD::Uniform> uniforms;
|
|
{
|
|
RD::Uniform u;
|
|
u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
|
|
u.binding = 1;
|
|
u.ids.push_back(gi_probe->octree_buffer);
|
|
uniforms.push_back(u);
|
|
}
|
|
{
|
|
RD::Uniform u;
|
|
u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
|
|
u.binding = 2;
|
|
u.ids.push_back(gi_probe->data_buffer);
|
|
uniforms.push_back(u);
|
|
}
|
|
{
|
|
RD::Uniform u;
|
|
u.type = RD::UNIFORM_TYPE_IMAGE;
|
|
u.binding = 3;
|
|
u.ids.push_back(shared_tex);
|
|
uniforms.push_back(u);
|
|
}
|
|
|
|
RID uniform_set = RD::get_singleton()->uniform_set_create(uniforms, giprobe_sdf_shader_version_shader, 0);
|
|
|
|
{
|
|
uint32_t push_constant[4] = { 0, 0, 0, 0 };
|
|
|
|
for (int i = 0; i < gi_probe->level_counts.size() - 1; i++) {
|
|
push_constant[0] += gi_probe->level_counts[i];
|
|
}
|
|
push_constant[1] = push_constant[0] + gi_probe->level_counts[gi_probe->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, giprobe_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, gi_probe->octree_size.x / 4, gi_probe->octree_size.y / 4, gi_probe->octree_size.z / 4);
|
|
RD::get_singleton()->compute_list_end();
|
|
}
|
|
|
|
RD::get_singleton()->free(uniform_set);
|
|
RD::get_singleton()->free(shared_tex);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
gi_probe->version++;
|
|
gi_probe->data_version++;
|
|
|
|
gi_probe->instance_dependency.instance_notify_changed(true, false);
|
|
}
|
|
|
|
AABB RasterizerStorageRD::gi_probe_get_bounds(RID p_gi_probe) const {
|
|
GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe);
|
|
ERR_FAIL_COND_V(!gi_probe, AABB());
|
|
|
|
return gi_probe->bounds;
|
|
}
|
|
|
|
Vector3i RasterizerStorageRD::gi_probe_get_octree_size(RID p_gi_probe) const {
|
|
GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe);
|
|
ERR_FAIL_COND_V(!gi_probe, Vector3i());
|
|
return gi_probe->octree_size;
|
|
}
|
|
Vector<uint8_t> RasterizerStorageRD::gi_probe_get_octree_cells(RID p_gi_probe) const {
|
|
GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe);
|
|
ERR_FAIL_COND_V(!gi_probe, Vector<uint8_t>());
|
|
|
|
if (gi_probe->octree_buffer.is_valid()) {
|
|
return RD::get_singleton()->buffer_get_data(gi_probe->octree_buffer);
|
|
}
|
|
return Vector<uint8_t>();
|
|
}
|
|
Vector<uint8_t> RasterizerStorageRD::gi_probe_get_data_cells(RID p_gi_probe) const {
|
|
GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe);
|
|
ERR_FAIL_COND_V(!gi_probe, Vector<uint8_t>());
|
|
|
|
if (gi_probe->data_buffer.is_valid()) {
|
|
return RD::get_singleton()->buffer_get_data(gi_probe->data_buffer);
|
|
}
|
|
return Vector<uint8_t>();
|
|
}
|
|
Vector<uint8_t> RasterizerStorageRD::gi_probe_get_distance_field(RID p_gi_probe) const {
|
|
GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe);
|
|
ERR_FAIL_COND_V(!gi_probe, Vector<uint8_t>());
|
|
|
|
if (gi_probe->data_buffer.is_valid()) {
|
|
return RD::get_singleton()->texture_get_data(gi_probe->sdf_texture, 0);
|
|
}
|
|
return Vector<uint8_t>();
|
|
}
|
|
Vector<int> RasterizerStorageRD::gi_probe_get_level_counts(RID p_gi_probe) const {
|
|
GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe);
|
|
ERR_FAIL_COND_V(!gi_probe, Vector<int>());
|
|
|
|
return gi_probe->level_counts;
|
|
}
|
|
Transform RasterizerStorageRD::gi_probe_get_to_cell_xform(RID p_gi_probe) const {
|
|
GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe);
|
|
ERR_FAIL_COND_V(!gi_probe, Transform());
|
|
|
|
return gi_probe->to_cell_xform;
|
|
}
|
|
|
|
void RasterizerStorageRD::gi_probe_set_dynamic_range(RID p_gi_probe, float p_range) {
|
|
GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe);
|
|
ERR_FAIL_COND(!gi_probe);
|
|
|
|
gi_probe->dynamic_range = p_range;
|
|
gi_probe->version++;
|
|
}
|
|
float RasterizerStorageRD::gi_probe_get_dynamic_range(RID p_gi_probe) const {
|
|
GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe);
|
|
ERR_FAIL_COND_V(!gi_probe, 0);
|
|
|
|
return gi_probe->dynamic_range;
|
|
}
|
|
|
|
void RasterizerStorageRD::gi_probe_set_propagation(RID p_gi_probe, float p_range) {
|
|
GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe);
|
|
ERR_FAIL_COND(!gi_probe);
|
|
|
|
gi_probe->propagation = p_range;
|
|
gi_probe->version++;
|
|
}
|
|
float RasterizerStorageRD::gi_probe_get_propagation(RID p_gi_probe) const {
|
|
GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe);
|
|
ERR_FAIL_COND_V(!gi_probe, 0);
|
|
return gi_probe->propagation;
|
|
}
|
|
|
|
void RasterizerStorageRD::gi_probe_set_energy(RID p_gi_probe, float p_energy) {
|
|
GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe);
|
|
ERR_FAIL_COND(!gi_probe);
|
|
|
|
gi_probe->energy = p_energy;
|
|
}
|
|
float RasterizerStorageRD::gi_probe_get_energy(RID p_gi_probe) const {
|
|
GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe);
|
|
ERR_FAIL_COND_V(!gi_probe, 0);
|
|
return gi_probe->energy;
|
|
}
|
|
|
|
void RasterizerStorageRD::gi_probe_set_ao(RID p_gi_probe, float p_ao) {
|
|
GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe);
|
|
ERR_FAIL_COND(!gi_probe);
|
|
|
|
gi_probe->ao = p_ao;
|
|
}
|
|
float RasterizerStorageRD::gi_probe_get_ao(RID p_gi_probe) const {
|
|
GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe);
|
|
ERR_FAIL_COND_V(!gi_probe, 0);
|
|
return gi_probe->ao;
|
|
}
|
|
|
|
void RasterizerStorageRD::gi_probe_set_ao_size(RID p_gi_probe, float p_strength) {
|
|
|
|
GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe);
|
|
ERR_FAIL_COND(!gi_probe);
|
|
|
|
gi_probe->ao_size = p_strength;
|
|
}
|
|
|
|
float RasterizerStorageRD::gi_probe_get_ao_size(RID p_gi_probe) const {
|
|
GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe);
|
|
ERR_FAIL_COND_V(!gi_probe, 0);
|
|
return gi_probe->ao_size;
|
|
}
|
|
|
|
void RasterizerStorageRD::gi_probe_set_bias(RID p_gi_probe, float p_bias) {
|
|
GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe);
|
|
ERR_FAIL_COND(!gi_probe);
|
|
|
|
gi_probe->bias = p_bias;
|
|
}
|
|
float RasterizerStorageRD::gi_probe_get_bias(RID p_gi_probe) const {
|
|
GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe);
|
|
ERR_FAIL_COND_V(!gi_probe, 0);
|
|
return gi_probe->bias;
|
|
}
|
|
|
|
void RasterizerStorageRD::gi_probe_set_normal_bias(RID p_gi_probe, float p_normal_bias) {
|
|
GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe);
|
|
ERR_FAIL_COND(!gi_probe);
|
|
|
|
gi_probe->normal_bias = p_normal_bias;
|
|
}
|
|
float RasterizerStorageRD::gi_probe_get_normal_bias(RID p_gi_probe) const {
|
|
GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe);
|
|
ERR_FAIL_COND_V(!gi_probe, 0);
|
|
return gi_probe->normal_bias;
|
|
}
|
|
|
|
void RasterizerStorageRD::gi_probe_set_anisotropy_strength(RID p_gi_probe, float p_strength) {
|
|
|
|
GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe);
|
|
ERR_FAIL_COND(!gi_probe);
|
|
|
|
gi_probe->anisotropy_strength = p_strength;
|
|
}
|
|
|
|
float RasterizerStorageRD::gi_probe_get_anisotropy_strength(RID p_gi_probe) const {
|
|
GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe);
|
|
ERR_FAIL_COND_V(!gi_probe, 0);
|
|
return gi_probe->anisotropy_strength;
|
|
}
|
|
|
|
void RasterizerStorageRD::gi_probe_set_interior(RID p_gi_probe, bool p_enable) {
|
|
GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe);
|
|
ERR_FAIL_COND(!gi_probe);
|
|
|
|
gi_probe->interior = p_enable;
|
|
}
|
|
|
|
void RasterizerStorageRD::gi_probe_set_use_two_bounces(RID p_gi_probe, bool p_enable) {
|
|
GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe);
|
|
ERR_FAIL_COND(!gi_probe);
|
|
|
|
gi_probe->use_two_bounces = p_enable;
|
|
gi_probe->version++;
|
|
}
|
|
|
|
bool RasterizerStorageRD::gi_probe_is_using_two_bounces(RID p_gi_probe) const {
|
|
GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe);
|
|
ERR_FAIL_COND_V(!gi_probe, false);
|
|
return gi_probe->use_two_bounces;
|
|
}
|
|
|
|
bool RasterizerStorageRD::gi_probe_is_interior(RID p_gi_probe) const {
|
|
GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe);
|
|
ERR_FAIL_COND_V(!gi_probe, 0);
|
|
return gi_probe->interior;
|
|
}
|
|
|
|
uint32_t RasterizerStorageRD::gi_probe_get_version(RID p_gi_probe) {
|
|
GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe);
|
|
ERR_FAIL_COND_V(!gi_probe, 0);
|
|
return gi_probe->version;
|
|
}
|
|
|
|
uint32_t RasterizerStorageRD::gi_probe_get_data_version(RID p_gi_probe) {
|
|
GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe);
|
|
ERR_FAIL_COND_V(!gi_probe, 0);
|
|
return gi_probe->data_version;
|
|
}
|
|
|
|
RID RasterizerStorageRD::gi_probe_get_octree_buffer(RID p_gi_probe) const {
|
|
|
|
GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe);
|
|
ERR_FAIL_COND_V(!gi_probe, RID());
|
|
return gi_probe->octree_buffer;
|
|
}
|
|
RID RasterizerStorageRD::gi_probe_get_data_buffer(RID p_gi_probe) const {
|
|
|
|
GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe);
|
|
ERR_FAIL_COND_V(!gi_probe, RID());
|
|
return gi_probe->data_buffer;
|
|
}
|
|
|
|
RID RasterizerStorageRD::gi_probe_get_sdf_texture(RID p_gi_probe) {
|
|
GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe);
|
|
ERR_FAIL_COND_V(!gi_probe, RID());
|
|
|
|
return gi_probe->sdf_texture;
|
|
}
|
|
|
|
/* RENDER TARGET API */
|
|
|
|
void RasterizerStorageRD::_clear_render_target(RenderTarget *rt) {
|
|
|
|
//free in reverse dependency order
|
|
if (rt->framebuffer.is_valid()) {
|
|
RD::get_singleton()->free(rt->framebuffer);
|
|
}
|
|
|
|
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();
|
|
for (int i = 0; i < rt->backbuffer_mipmaps.size(); i++) {
|
|
//just erase copies, since the rest are erased by dependency
|
|
RD::get_singleton()->free(rt->backbuffer_mipmaps[i].mipmap_copy);
|
|
}
|
|
rt->backbuffer_mipmaps.clear();
|
|
if (rt->backbuffer_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(rt->backbuffer_uniform_set)) {
|
|
RD::get_singleton()->free(rt->backbuffer_uniform_set);
|
|
}
|
|
rt->backbuffer_uniform_set = RID();
|
|
}
|
|
|
|
rt->framebuffer = RID();
|
|
rt->color = RID();
|
|
}
|
|
|
|
void RasterizerStorageRD::_update_render_target(RenderTarget *rt) {
|
|
|
|
if (rt->texture.is_null()) {
|
|
//create a placeholder until updated
|
|
rt->texture = texture_2d_placeholder_create();
|
|
Texture *tex = texture_owner.getornull(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 = 1;
|
|
rd_format.mipmaps = 1;
|
|
rd_format.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<RID> fb_textures;
|
|
fb_textures.push_back(rt->color);
|
|
rt->framebuffer = RD::get_singleton()->framebuffer_create(fb_textures);
|
|
if (rt->framebuffer.is_null()) {
|
|
_clear_render_target(rt);
|
|
ERR_FAIL_COND(rt->framebuffer.is_null());
|
|
}
|
|
|
|
{ //update texture
|
|
|
|
Texture *tex = texture_owner.getornull(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<RID> 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 RasterizerStorageRD::_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.type = RD::TEXTURE_TYPE_2D;
|
|
tf.usage_bits = 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());
|
|
rt->backbuffer_mipmap0 = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), rt->backbuffer, 0, 0);
|
|
|
|
//create mipmaps
|
|
for (uint32_t i = 1; i < mipmaps_required; i++) {
|
|
|
|
RenderTarget::BackbufferMipmap mm;
|
|
{
|
|
mm.mipmap = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), rt->backbuffer, 0, i);
|
|
}
|
|
|
|
{
|
|
Size2 mm_size = Image::get_image_mipmap_size(tf.width, tf.height, Image::FORMAT_RGBA8, i);
|
|
|
|
RD::TextureFormat mmtf = tf;
|
|
mmtf.width = mm_size.width;
|
|
mmtf.height = mm_size.height;
|
|
mmtf.mipmaps = 1;
|
|
|
|
mm.mipmap_copy = RD::get_singleton()->texture_create(mmtf, RD::TextureView());
|
|
}
|
|
|
|
rt->backbuffer_mipmaps.push_back(mm);
|
|
}
|
|
}
|
|
|
|
RID RasterizerStorageRD::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 RasterizerStorageRD::render_target_set_position(RID p_render_target, int p_x, int p_y) {
|
|
//unused for this render target
|
|
}
|
|
|
|
void RasterizerStorageRD::render_target_set_size(RID p_render_target, int p_width, int p_height) {
|
|
RenderTarget *rt = render_target_owner.getornull(p_render_target);
|
|
ERR_FAIL_COND(!rt);
|
|
rt->size.x = p_width;
|
|
rt->size.y = p_height;
|
|
_update_render_target(rt);
|
|
}
|
|
|
|
RID RasterizerStorageRD::render_target_get_texture(RID p_render_target) {
|
|
RenderTarget *rt = render_target_owner.getornull(p_render_target);
|
|
ERR_FAIL_COND_V(!rt, RID());
|
|
|
|
return rt->texture;
|
|
}
|
|
|
|
void RasterizerStorageRD::render_target_set_external_texture(RID p_render_target, unsigned int p_texture_id) {
|
|
}
|
|
|
|
void RasterizerStorageRD::render_target_set_flag(RID p_render_target, RenderTargetFlags p_flag, bool p_value) {
|
|
RenderTarget *rt = render_target_owner.getornull(p_render_target);
|
|
ERR_FAIL_COND(!rt);
|
|
rt->flags[p_flag] = p_value;
|
|
_update_render_target(rt);
|
|
}
|
|
|
|
bool RasterizerStorageRD::render_target_was_used(RID p_render_target) {
|
|
|
|
RenderTarget *rt = render_target_owner.getornull(p_render_target);
|
|
ERR_FAIL_COND_V(!rt, false);
|
|
return rt->was_used;
|
|
}
|
|
|
|
void RasterizerStorageRD::render_target_set_as_unused(RID p_render_target) {
|
|
|
|
RenderTarget *rt = render_target_owner.getornull(p_render_target);
|
|
ERR_FAIL_COND(!rt);
|
|
rt->was_used = false;
|
|
}
|
|
|
|
Size2 RasterizerStorageRD::render_target_get_size(RID p_render_target) {
|
|
RenderTarget *rt = render_target_owner.getornull(p_render_target);
|
|
ERR_FAIL_COND_V(!rt, Size2());
|
|
|
|
return rt->size;
|
|
}
|
|
|
|
RID RasterizerStorageRD::render_target_get_rd_framebuffer(RID p_render_target) {
|
|
RenderTarget *rt = render_target_owner.getornull(p_render_target);
|
|
ERR_FAIL_COND_V(!rt, RID());
|
|
|
|
return rt->framebuffer;
|
|
}
|
|
RID RasterizerStorageRD::render_target_get_rd_texture(RID p_render_target) {
|
|
RenderTarget *rt = render_target_owner.getornull(p_render_target);
|
|
ERR_FAIL_COND_V(!rt, RID());
|
|
|
|
return rt->color;
|
|
}
|
|
void RasterizerStorageRD::render_target_request_clear(RID p_render_target, const Color &p_clear_color) {
|
|
RenderTarget *rt = render_target_owner.getornull(p_render_target);
|
|
ERR_FAIL_COND(!rt);
|
|
rt->clear_requested = true;
|
|
rt->clear_color = p_clear_color;
|
|
}
|
|
|
|
bool RasterizerStorageRD::render_target_is_clear_requested(RID p_render_target) {
|
|
RenderTarget *rt = render_target_owner.getornull(p_render_target);
|
|
ERR_FAIL_COND_V(!rt, false);
|
|
return rt->clear_requested;
|
|
}
|
|
|
|
Color RasterizerStorageRD::render_target_get_clear_request_color(RID p_render_target) {
|
|
|
|
RenderTarget *rt = render_target_owner.getornull(p_render_target);
|
|
ERR_FAIL_COND_V(!rt, Color());
|
|
return rt->clear_color;
|
|
}
|
|
|
|
void RasterizerStorageRD::render_target_disable_clear_request(RID p_render_target) {
|
|
|
|
RenderTarget *rt = render_target_owner.getornull(p_render_target);
|
|
ERR_FAIL_COND(!rt);
|
|
rt->clear_requested = false;
|
|
}
|
|
|
|
void RasterizerStorageRD::render_target_do_clear_request(RID p_render_target) {
|
|
|
|
RenderTarget *rt = render_target_owner.getornull(p_render_target);
|
|
ERR_FAIL_COND(!rt);
|
|
if (!rt->clear_requested) {
|
|
return;
|
|
}
|
|
Vector<Color> 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 RasterizerStorageRD::render_target_copy_to_back_buffer(RID p_render_target, const Rect2i &p_region) {
|
|
RenderTarget *rt = render_target_owner.getornull(p_render_target);
|
|
ERR_FAIL_COND(!rt);
|
|
if (!rt->backbuffer.is_valid()) {
|
|
_create_render_target_backbuffer(rt);
|
|
}
|
|
|
|
Rect2i region = p_region;
|
|
if (region == Rect2i()) {
|
|
region.size = rt->size;
|
|
}
|
|
|
|
//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(rt->color, rt->backbuffer_fb, blur_region);
|
|
|
|
//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);
|
|
|
|
const RenderTarget::BackbufferMipmap &mm = rt->backbuffer_mipmaps[i];
|
|
effects.gaussian_blur(prev_texture, mm.mipmap, mm.mipmap_copy, region, true);
|
|
prev_texture = mm.mipmap;
|
|
}
|
|
}
|
|
|
|
RID RasterizerStorageRD::render_target_get_back_buffer_uniform_set(RID p_render_target, RID p_base_shader) {
|
|
RenderTarget *rt = render_target_owner.getornull(p_render_target);
|
|
ERR_FAIL_COND_V(!rt, RID());
|
|
|
|
if (!rt->backbuffer.is_valid()) {
|
|
_create_render_target_backbuffer(rt);
|
|
}
|
|
|
|
if (rt->backbuffer_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(rt->backbuffer_uniform_set)) {
|
|
return rt->backbuffer_uniform_set; //if still valid, return/reuse it.
|
|
}
|
|
|
|
//create otherwise
|
|
Vector<RD::Uniform> uniforms;
|
|
RD::Uniform u;
|
|
u.type = RD::UNIFORM_TYPE_TEXTURE;
|
|
u.binding = 0;
|
|
u.ids.push_back(rt->backbuffer);
|
|
uniforms.push_back(u);
|
|
|
|
rt->backbuffer_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, p_base_shader, 3);
|
|
ERR_FAIL_COND_V(!rt->backbuffer_uniform_set.is_valid(), RID());
|
|
|
|
return rt->backbuffer_uniform_set;
|
|
}
|
|
|
|
void RasterizerStorageRD::base_update_dependency(RID p_base, RasterizerScene::InstanceBase *p_instance) {
|
|
if (mesh_owner.owns(p_base)) {
|
|
Mesh *mesh = mesh_owner.getornull(p_base);
|
|
p_instance->update_dependency(&mesh->instance_dependency);
|
|
} else if (multimesh_owner.owns(p_base)) {
|
|
|
|
MultiMesh *multimesh = multimesh_owner.getornull(p_base);
|
|
p_instance->update_dependency(&multimesh->instance_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.getornull(p_base);
|
|
p_instance->update_dependency(&rp->instance_dependency);
|
|
} else if (decal_owner.owns(p_base)) {
|
|
Decal *decal = decal_owner.getornull(p_base);
|
|
p_instance->update_dependency(&decal->instance_dependency);
|
|
} else if (gi_probe_owner.owns(p_base)) {
|
|
GIProbe *gip = gi_probe_owner.getornull(p_base);
|
|
p_instance->update_dependency(&gip->instance_dependency);
|
|
} else if (light_owner.owns(p_base)) {
|
|
Light *l = light_owner.getornull(p_base);
|
|
p_instance->update_dependency(&l->instance_dependency);
|
|
}
|
|
}
|
|
|
|
void RasterizerStorageRD::skeleton_update_dependency(RID p_skeleton, RasterizerScene::InstanceBase *p_instance) {
|
|
|
|
Skeleton *skeleton = skeleton_owner.getornull(p_skeleton);
|
|
ERR_FAIL_COND(!skeleton);
|
|
|
|
p_instance->update_dependency(&skeleton->instance_dependency);
|
|
}
|
|
|
|
RS::InstanceType RasterizerStorageRD::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 (gi_probe_owner.owns(p_rid)) {
|
|
return RS::INSTANCE_GI_PROBE;
|
|
}
|
|
if (light_owner.owns(p_rid)) {
|
|
return RS::INSTANCE_LIGHT;
|
|
}
|
|
|
|
return RS::INSTANCE_NONE;
|
|
}
|
|
|
|
void RasterizerStorageRD::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 RasterizerStorageRD::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 RasterizerStorageRD::decal_atlas_get_texture() const {
|
|
return decal_atlas.texture;
|
|
}
|
|
|
|
RID RasterizerStorageRD::decal_atlas_get_texture_srgb() const {
|
|
return decal_atlas.texture;
|
|
}
|
|
|
|
void RasterizerStorageRD::_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<DecalAtlas::SortItem> itemsv;
|
|
itemsv.resize(decal_atlas.textures.size());
|
|
int base_size = 8;
|
|
const RID *K = NULL;
|
|
|
|
int idx = 0;
|
|
while ((K = decal_atlas.textures.next(K))) {
|
|
DecalAtlas::SortItem &si = itemsv.write[idx];
|
|
|
|
Texture *src_tex = texture_owner.getornull(*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<int> v_offsetsv;
|
|
v_offsetsv.resize(base_size);
|
|
|
|
int *v_offsets = v_offsetsv.ptrw();
|
|
zeromem(v_offsets, 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;
|
|
//print_line("blitrect: " + t->uv_rect);
|
|
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.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());
|
|
|
|
{
|
|
//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<RID> 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<Color> 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 = NULL;
|
|
while ((K = decal_atlas.textures.next(K))) {
|
|
DecalAtlas::Texture *t = decal_atlas.textures.getptr(*K);
|
|
Texture *src_tex = texture_owner.getornull(*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, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
int32_t RasterizerStorageRD::_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 RasterizerStorageRD::_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<int32_t> 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<int32_t> 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<float> 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<float> 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];
|
|
Transform 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 RasterizerStorageRD::_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 RasterizerStorageRD::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 ther are no
|
|
} 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 ther are no
|
|
}
|
|
|
|
global_variables.variables[p_name] = gv;
|
|
}
|
|
|
|
void RasterizerStorageRD::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<StringName> RasterizerStorageRD::global_variable_get_list() const {
|
|
|
|
if (!Engine::get_singleton()->is_editor_hint()) {
|
|
ERR_FAIL_V_MSG(Vector<StringName>(), "This function should never be used outside the editor, it can severely damage performance.");
|
|
}
|
|
|
|
const StringName *K = NULL;
|
|
Vector<StringName> names;
|
|
while ((K = global_variables.variables.next(K))) {
|
|
names.push_back(*K);
|
|
}
|
|
names.sort_custom<StringName::AlphCompare>();
|
|
return names;
|
|
}
|
|
|
|
void RasterizerStorageRD::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<RID>::Element *E = gv.texture_materials.front(); E; E = E->next()) {
|
|
Material *material = material_owner.getornull(E->get());
|
|
ERR_CONTINUE(!material);
|
|
_material_queue_update(material, false, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
void RasterizerStorageRD::global_variable_set_override(const StringName &p_name, const Variant &p_value) {
|
|
if (!global_variables.variables.has(p_name)) {
|
|
return; //variable may not exist
|
|
}
|
|
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
|
|
//texture
|
|
for (Set<RID>::Element *E = gv.texture_materials.front(); E; E = E->next()) {
|
|
Material *material = material_owner.getornull(E->get());
|
|
ERR_CONTINUE(!material);
|
|
_material_queue_update(material, false, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
Variant RasterizerStorageRD::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 RasterizerStorageRD::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 RasterizerStorageRD::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 RasterizerStorageRD::global_variables_load_settings(bool p_load_textures) {
|
|
|
|
List<PropertyInfo> settings;
|
|
ProjectSettings::get_singleton()->get_property_list(&settings);
|
|
|
|
for (List<PropertyInfo>::Element *E = settings.front(); E; E = E->next()) {
|
|
if (E->get().name.begins_with("shader_globals/")) {
|
|
StringName name = E->get().name.get_slice("/", 1);
|
|
Dictionary d = ProjectSettings::get_singleton()->get(E->get().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 RasterizerStorageRD::global_variables_clear() {
|
|
global_variables.variables.clear(); //not right but for now enough
|
|
}
|
|
|
|
RID RasterizerStorageRD::global_variables_get_storage_buffer() const {
|
|
return global_variables.buffer;
|
|
}
|
|
|
|
int32_t RasterizerStorageRD::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 RasterizerStorageRD::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 RasterizerStorageRD::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, 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 RasterizerStorageRD::_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);
|
|
zeromem(global_variables.buffer_dirty_regions, 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);
|
|
|
|
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 (List<RID>::Element *E = global_variables.materials_using_buffer.front(); E; E = E->next()) {
|
|
Material *material = material_owner.getornull(E->get());
|
|
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 (List<RID>::Element *E = global_variables.materials_using_texture.front(); E; E = E->next()) {
|
|
Material *material = material_owner.getornull(E->get());
|
|
ERR_CONTINUE(!material); //wtf
|
|
|
|
_material_queue_update(material, false, true);
|
|
print_line("update material texture?");
|
|
}
|
|
|
|
global_variables.must_update_texture_materials = false;
|
|
}
|
|
}
|
|
|
|
void RasterizerStorageRD::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 RasterizerStorageRD::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;
|
|
}
|
|
|
|
if (p_feature == "pvrtc" && RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG, RD::TEXTURE_USAGE_SAMPLING_BIT)) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
bool RasterizerStorageRD::free(RID p_rid) {
|
|
|
|
if (texture_owner.owns(p_rid)) {
|
|
Texture *t = texture_owner.getornull(p_rid);
|
|
|
|
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.getornull(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.getornull(t->proxies[i]);
|
|
ERR_CONTINUE(!p);
|
|
p->proxy_to = RID();
|
|
p->rd_texture = RID();
|
|
p->rd_texture_srgb = RID();
|
|
}
|
|
texture_owner.free(p_rid);
|
|
|
|
} else if (shader_owner.owns(p_rid)) {
|
|
Shader *shader = shader_owner.getornull(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.getornull(p_rid);
|
|
if (material->update_requested) {
|
|
_update_queued_materials();
|
|
}
|
|
material_set_shader(p_rid, RID()); //clean up shader
|
|
material->instance_dependency.instance_notify_deleted(p_rid);
|
|
material_owner.free(p_rid);
|
|
} else if (mesh_owner.owns(p_rid)) {
|
|
mesh_clear(p_rid);
|
|
Mesh *mesh = mesh_owner.getornull(p_rid);
|
|
mesh->instance_dependency.instance_notify_deleted(p_rid);
|
|
mesh_owner.free(p_rid);
|
|
} else if (multimesh_owner.owns(p_rid)) {
|
|
_update_dirty_multimeshes();
|
|
multimesh_allocate(p_rid, 0, RS::MULTIMESH_TRANSFORM_2D);
|
|
MultiMesh *multimesh = multimesh_owner.getornull(p_rid);
|
|
multimesh->instance_dependency.instance_notify_deleted(p_rid);
|
|
multimesh_owner.free(p_rid);
|
|
} else if (skeleton_owner.owns(p_rid)) {
|
|
_update_dirty_skeletons();
|
|
skeleton_allocate(p_rid, 0);
|
|
Skeleton *skeleton = skeleton_owner.getornull(p_rid);
|
|
skeleton->instance_dependency.instance_notify_deleted(p_rid);
|
|
skeleton_owner.free(p_rid);
|
|
} else if (reflection_probe_owner.owns(p_rid)) {
|
|
ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_rid);
|
|
reflection_probe->instance_dependency.instance_notify_deleted(p_rid);
|
|
reflection_probe_owner.free(p_rid);
|
|
} else if (decal_owner.owns(p_rid)) {
|
|
Decal *decal = decal_owner.getornull(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->instance_dependency.instance_notify_deleted(p_rid);
|
|
decal_owner.free(p_rid);
|
|
} else if (gi_probe_owner.owns(p_rid)) {
|
|
gi_probe_allocate(p_rid, Transform(), AABB(), Vector3i(), Vector<uint8_t>(), Vector<uint8_t>(), Vector<uint8_t>(), Vector<int>()); //deallocate
|
|
GIProbe *gi_probe = gi_probe_owner.getornull(p_rid);
|
|
gi_probe->instance_dependency.instance_notify_deleted(p_rid);
|
|
gi_probe_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.getornull(p_rid);
|
|
light->instance_dependency.instance_notify_deleted(p_rid);
|
|
light_owner.free(p_rid);
|
|
|
|
} else if (render_target_owner.owns(p_rid)) {
|
|
RenderTarget *rt = render_target_owner.getornull(p_rid);
|
|
|
|
_clear_render_target(rt);
|
|
|
|
if (rt->texture.is_valid()) {
|
|
Texture *tex = texture_owner.getornull(rt->texture);
|
|
tex->is_render_target = false;
|
|
free(rt->texture);
|
|
}
|
|
|
|
render_target_owner.free(p_rid);
|
|
} else {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
RasterizerEffectsRD *RasterizerStorageRD::get_effects() {
|
|
return &effects;
|
|
}
|
|
|
|
void RasterizerStorageRD::capture_timestamps_begin() {
|
|
RD::get_singleton()->capture_timestamp("Frame Begin", false);
|
|
}
|
|
|
|
void RasterizerStorageRD::capture_timestamp(const String &p_name) {
|
|
RD::get_singleton()->capture_timestamp(p_name, true);
|
|
}
|
|
|
|
uint32_t RasterizerStorageRD::get_captured_timestamps_count() const {
|
|
return RD::get_singleton()->get_captured_timestamps_count();
|
|
}
|
|
uint64_t RasterizerStorageRD::get_captured_timestamps_frame() const {
|
|
return RD::get_singleton()->get_captured_timestamps_frame();
|
|
}
|
|
|
|
uint64_t RasterizerStorageRD::get_captured_timestamp_gpu_time(uint32_t p_index) const {
|
|
return RD::get_singleton()->get_captured_timestamp_gpu_time(p_index);
|
|
}
|
|
uint64_t RasterizerStorageRD::get_captured_timestamp_cpu_time(uint32_t p_index) const {
|
|
return RD::get_singleton()->get_captured_timestamp_cpu_time(p_index);
|
|
}
|
|
String RasterizerStorageRD::get_captured_timestamp_name(uint32_t p_index) const {
|
|
return RD::get_singleton()->get_captured_timestamp_name(p_index);
|
|
}
|
|
|
|
RasterizerStorageRD *RasterizerStorageRD::base_singleton = nullptr;
|
|
|
|
RasterizerStorageRD::RasterizerStorageRD() {
|
|
|
|
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 = GLOBAL_GET("rendering/high_end/global_shader_variables_buffer_size");
|
|
global_variables.buffer_size = MAX(4096, global_variables.buffer_size);
|
|
global_variables.buffer_values = memnew_arr(GlobalVariables::Value, global_variables.buffer_size);
|
|
zeromem(global_variables.buffer_values, 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);
|
|
zeromem(global_variables.buffer_dirty_regions, 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);
|
|
|
|
material_update_list = nullptr;
|
|
{ //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.type = RD::TEXTURE_TYPE_2D;
|
|
|
|
Vector<uint8_t> 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<Vector<uint8_t>> 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<Vector<uint8_t>> 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<Vector<uint8_t>> 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<Vector<uint8_t>> 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);
|
|
}
|
|
|
|
{ //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.type = RD::TEXTURE_TYPE_CUBE_ARRAY;
|
|
|
|
Vector<uint8_t> 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<Vector<uint8_t>> 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.type = RD::TEXTURE_TYPE_CUBE;
|
|
|
|
Vector<uint8_t> 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<Vector<uint8_t>> 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 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.type = RD::TEXTURE_TYPE_3D;
|
|
|
|
Vector<uint8_t> 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<Vector<uint8_t>> vpv;
|
|
vpv.push_back(pv);
|
|
default_rd_textures[DEFAULT_RD_TEXTURE_3D_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_LINEAR;
|
|
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;
|
|
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_LINEAR;
|
|
sampler_state.mip_filter = RD::SAMPLER_FILTER_LINEAR;
|
|
sampler_state.use_anisotropy = true;
|
|
sampler_state.anisotropy_max = 1 << int(GLOBAL_GET("rendering/quality/texture_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;
|
|
sampler_state.mip_filter = RD::SAMPLER_FILTER_LINEAR;
|
|
sampler_state.use_anisotropy = true;
|
|
sampler_state.anisotropy_max = 1 << int(GLOBAL_GET("rendering/quality/texture_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;
|
|
|
|
} 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;
|
|
} 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;
|
|
} break;
|
|
default: {
|
|
}
|
|
}
|
|
|
|
default_rd_samplers[i][j] = RD::get_singleton()->sampler_create(sampler_state);
|
|
}
|
|
}
|
|
|
|
//default rd buffers
|
|
{
|
|
|
|
//vertex
|
|
{
|
|
|
|
Vector<uint8_t> 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
|
|
Vector<uint8_t> buffer;
|
|
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
|
|
Vector<uint8_t> buffer;
|
|
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
|
|
Vector<uint8_t> buffer;
|
|
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
|
|
Vector<uint8_t> buffer;
|
|
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
|
|
Vector<uint8_t> buffer;
|
|
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);
|
|
}
|
|
|
|
{ //bones
|
|
Vector<uint8_t> buffer;
|
|
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
|
|
Vector<uint8_t> buffer;
|
|
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);
|
|
}
|
|
}
|
|
|
|
{
|
|
Vector<String> sdf_versions;
|
|
sdf_versions.push_back(""); //one only
|
|
giprobe_sdf_shader.initialize(sdf_versions);
|
|
giprobe_sdf_shader_version = giprobe_sdf_shader.version_create();
|
|
giprobe_sdf_shader.version_set_compute_code(giprobe_sdf_shader_version, "", "", "", Vector<String>());
|
|
giprobe_sdf_shader_version_shader = giprobe_sdf_shader.version_get_shader(giprobe_sdf_shader_version, 0);
|
|
giprobe_sdf_shader_pipeline = RD::get_singleton()->compute_pipeline_create(giprobe_sdf_shader_version_shader);
|
|
}
|
|
}
|
|
|
|
RasterizerStorageRD::~RasterizerStorageRD() {
|
|
|
|
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]);
|
|
}
|
|
}
|
|
|
|
//def buffers
|
|
for (int i = 0; i < DEFAULT_RD_BUFFER_MAX; i++) {
|
|
RD::get_singleton()->free(mesh_default_rd_buffers[i]);
|
|
}
|
|
giprobe_sdf_shader.version_free(giprobe_sdf_shader_version);
|
|
|
|
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);
|
|
}
|
|
}
|