godot/drivers/gles3/storage/material_storage.cpp
Clay 7c812cd7e9 Fix use of discard in shaders
discard was being included in all shaders set to depth pass opaque, which is the majority of shaders

Instead it should only be used with alpha prepass materials
2023-07-24 23:21:04 +02:00

3183 lines
115 KiB
C++

/**************************************************************************/
/* material_storage.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
#ifdef GLES3_ENABLED
#include "core/config/project_settings.h"
#include "config.h"
#include "material_storage.h"
#include "particles_storage.h"
#include "texture_storage.h"
#include "drivers/gles3/rasterizer_canvas_gles3.h"
#include "drivers/gles3/rasterizer_gles3.h"
#include "servers/rendering/storage/variant_converters.h"
using namespace GLES3;
///////////////////////////////////////////////////////////////////////////
// UBI helper functions
static void _fill_std140_variant_ubo_value(ShaderLanguage::DataType type, int p_array_size, const Variant &value, uint8_t *data) {
switch (type) {
case ShaderLanguage::TYPE_BOOL: {
uint32_t *gui = (uint32_t *)data;
if (p_array_size > 0) {
PackedInt32Array ba = value;
for (int i = 0; i < ba.size(); i++) {
ba.set(i, ba[i] ? 1 : 0);
}
write_array_std140<int32_t>(ba, gui, p_array_size, 4);
} else {
bool v = value;
gui[0] = v ? 1 : 0;
}
} break;
case ShaderLanguage::TYPE_BVEC2: {
uint32_t *gui = (uint32_t *)data;
if (p_array_size > 0) {
PackedInt32Array ba = convert_array_std140<Vector2i, int32_t>(value);
for (int i = 0; i < ba.size(); i++) {
ba.set(i, ba[i] ? 1 : 0);
}
write_array_std140<Vector2i>(ba, gui, p_array_size, 4);
} else {
uint32_t v = value;
gui[0] = v & 1 ? 1 : 0;
gui[1] = v & 2 ? 1 : 0;
}
} break;
case ShaderLanguage::TYPE_BVEC3: {
uint32_t *gui = (uint32_t *)data;
if (p_array_size > 0) {
PackedInt32Array ba = convert_array_std140<Vector3i, int32_t>(value);
for (int i = 0; i < ba.size(); i++) {
ba.set(i, ba[i] ? 1 : 0);
}
write_array_std140<Vector3i>(ba, gui, p_array_size, 4);
} else {
uint32_t v = value;
gui[0] = (v & 1) ? 1 : 0;
gui[1] = (v & 2) ? 1 : 0;
gui[2] = (v & 4) ? 1 : 0;
}
} break;
case ShaderLanguage::TYPE_BVEC4: {
uint32_t *gui = (uint32_t *)data;
if (p_array_size > 0) {
PackedInt32Array ba = convert_array_std140<Vector4i, int32_t>(value);
for (int i = 0; i < ba.size(); i++) {
ba.set(i, ba[i] ? 1 : 0);
}
write_array_std140<Vector4i>(ba, gui, p_array_size, 4);
} else {
uint32_t v = value;
gui[0] = (v & 1) ? 1 : 0;
gui[1] = (v & 2) ? 1 : 0;
gui[2] = (v & 4) ? 1 : 0;
gui[3] = (v & 8) ? 1 : 0;
}
} break;
case ShaderLanguage::TYPE_INT: {
int32_t *gui = (int32_t *)data;
if (p_array_size > 0) {
const PackedInt32Array &iv = value;
write_array_std140<int32_t>(iv, gui, p_array_size, 4);
} else {
int v = value;
gui[0] = v;
}
} break;
case ShaderLanguage::TYPE_IVEC2: {
int32_t *gui = (int32_t *)data;
if (p_array_size > 0) {
const PackedInt32Array &iv = convert_array_std140<Vector2i, int32_t>(value);
write_array_std140<Vector2i>(iv, gui, p_array_size, 4);
} else {
Vector2i v = convert_to_vector<Vector2i>(value);
gui[0] = v.x;
gui[1] = v.y;
}
} break;
case ShaderLanguage::TYPE_IVEC3: {
int32_t *gui = (int32_t *)data;
if (p_array_size > 0) {
const PackedInt32Array &iv = convert_array_std140<Vector3i, int32_t>(value);
write_array_std140<Vector3i>(iv, gui, p_array_size, 4);
} else {
Vector3i v = convert_to_vector<Vector3i>(value);
gui[0] = v.x;
gui[1] = v.y;
gui[2] = v.z;
}
} break;
case ShaderLanguage::TYPE_IVEC4: {
int32_t *gui = (int32_t *)data;
if (p_array_size > 0) {
const PackedInt32Array &iv = convert_array_std140<Vector4i, int32_t>(value);
write_array_std140<Vector4i>(iv, gui, p_array_size, 4);
} else {
Vector4i v = convert_to_vector<Vector4i>(value);
gui[0] = v.x;
gui[1] = v.y;
gui[2] = v.z;
gui[3] = v.w;
}
} break;
case ShaderLanguage::TYPE_UINT: {
uint32_t *gui = (uint32_t *)data;
if (p_array_size > 0) {
const PackedInt32Array &iv = value;
write_array_std140<uint32_t>(iv, gui, p_array_size, 4);
} else {
int v = value;
gui[0] = v;
}
} break;
case ShaderLanguage::TYPE_UVEC2: {
uint32_t *gui = (uint32_t *)data;
if (p_array_size > 0) {
const PackedInt32Array &iv = convert_array_std140<Vector2i, int32_t>(value);
write_array_std140<Vector2i>(iv, gui, p_array_size, 4);
} else {
Vector2i v = convert_to_vector<Vector2i>(value);
gui[0] = v.x;
gui[1] = v.y;
}
} break;
case ShaderLanguage::TYPE_UVEC3: {
uint32_t *gui = (uint32_t *)data;
if (p_array_size > 0) {
const PackedInt32Array &iv = convert_array_std140<Vector3i, int32_t>(value);
write_array_std140<Vector3i>(iv, gui, p_array_size, 4);
} else {
Vector3i v = convert_to_vector<Vector3i>(value);
gui[0] = v.x;
gui[1] = v.y;
gui[2] = v.z;
}
} break;
case ShaderLanguage::TYPE_UVEC4: {
uint32_t *gui = (uint32_t *)data;
if (p_array_size > 0) {
const PackedInt32Array &iv = convert_array_std140<Vector4i, int32_t>(value);
write_array_std140<Vector4i>(iv, gui, p_array_size, 4);
} else {
Vector4i v = convert_to_vector<Vector4i>(value);
gui[0] = v.x;
gui[1] = v.y;
gui[2] = v.z;
gui[3] = v.w;
}
} break;
case ShaderLanguage::TYPE_FLOAT: {
float *gui = (float *)data;
if (p_array_size > 0) {
const PackedFloat32Array &a = value;
write_array_std140<float>(a, gui, p_array_size, 4);
} else {
float v = value;
gui[0] = v;
}
} break;
case ShaderLanguage::TYPE_VEC2: {
float *gui = (float *)data;
if (p_array_size > 0) {
const PackedFloat32Array &a = convert_array_std140<Vector2, float>(value);
write_array_std140<Vector2>(a, gui, p_array_size, 4);
} else {
Vector2 v = convert_to_vector<Vector2>(value);
gui[0] = v.x;
gui[1] = v.y;
}
} break;
case ShaderLanguage::TYPE_VEC3: {
float *gui = (float *)data;
if (p_array_size > 0) {
const PackedFloat32Array &a = convert_array_std140<Vector3, float>(value);
write_array_std140<Vector3>(a, gui, p_array_size, 4);
} else {
Vector3 v = convert_to_vector<Vector3>(value);
gui[0] = v.x;
gui[1] = v.y;
gui[2] = v.z;
}
} break;
case ShaderLanguage::TYPE_VEC4: {
float *gui = (float *)data;
if (p_array_size > 0) {
const PackedFloat32Array &a = convert_array_std140<Vector4, float>(value);
write_array_std140<Vector4>(a, gui, p_array_size, 4);
} else {
Vector4 v = convert_to_vector<Vector4>(value);
gui[0] = v.x;
gui[1] = v.y;
gui[2] = v.z;
gui[3] = v.w;
}
} break;
case ShaderLanguage::TYPE_MAT2: {
float *gui = (float *)data;
if (p_array_size > 0) {
const PackedFloat32Array &a = value;
int s = a.size();
for (int i = 0, j = 0; i < p_array_size * 4; i += 4, j += 8) {
if (i + 3 < s) {
gui[j] = a[i];
gui[j + 1] = a[i + 1];
gui[j + 4] = a[i + 2];
gui[j + 5] = a[i + 3];
} else {
gui[j] = 1;
gui[j + 1] = 0;
gui[j + 4] = 0;
gui[j + 5] = 1;
}
gui[j + 2] = 0; // ignored
gui[j + 3] = 0; // ignored
gui[j + 6] = 0; // ignored
gui[j + 7] = 0; // ignored
}
} else {
Transform2D v = value;
//in std140 members of mat2 are treated as vec4s
gui[0] = v.columns[0][0];
gui[1] = v.columns[0][1];
gui[2] = 0; // ignored
gui[3] = 0; // ignored
gui[4] = v.columns[1][0];
gui[5] = v.columns[1][1];
gui[6] = 0; // ignored
gui[7] = 0; // ignored
}
} break;
case ShaderLanguage::TYPE_MAT3: {
float *gui = (float *)data;
if (p_array_size > 0) {
const PackedFloat32Array &a = convert_array_std140<Basis, float>(value);
const Basis default_basis;
const int s = a.size();
for (int i = 0, j = 0; i < p_array_size * 9; i += 9, j += 12) {
if (i + 8 < s) {
gui[j] = a[i];
gui[j + 1] = a[i + 1];
gui[j + 2] = a[i + 2];
gui[j + 3] = 0; // Ignored.
gui[j + 4] = a[i + 3];
gui[j + 5] = a[i + 4];
gui[j + 6] = a[i + 5];
gui[j + 7] = 0; // Ignored.
gui[j + 8] = a[i + 6];
gui[j + 9] = a[i + 7];
gui[j + 10] = a[i + 8];
gui[j + 11] = 0; // Ignored.
} else {
convert_item_std140(default_basis, gui + j);
}
}
} else {
convert_item_std140<Basis>(value, gui);
}
} break;
case ShaderLanguage::TYPE_MAT4: {
float *gui = (float *)data;
if (p_array_size > 0) {
const PackedFloat32Array &a = convert_array_std140<Projection, float>(value);
write_array_std140<Projection>(a, gui, p_array_size, 16);
} else {
convert_item_std140<Projection>(value, gui);
}
} 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[0] = value[0].boolean ? 1 : 0;
} break;
case ShaderLanguage::TYPE_BVEC2: {
uint32_t *gui = (uint32_t *)data;
gui[0] = value[0].boolean ? 1 : 0;
gui[1] = value[1].boolean ? 1 : 0;
} break;
case ShaderLanguage::TYPE_BVEC3: {
uint32_t *gui = (uint32_t *)data;
gui[0] = value[0].boolean ? 1 : 0;
gui[1] = value[1].boolean ? 1 : 0;
gui[2] = value[2].boolean ? 1 : 0;
} break;
case ShaderLanguage::TYPE_BVEC4: {
uint32_t *gui = (uint32_t *)data;
gui[0] = value[0].boolean ? 1 : 0;
gui[1] = value[1].boolean ? 1 : 0;
gui[2] = value[2].boolean ? 1 : 0;
gui[3] = value[3].boolean ? 1 : 0;
} break;
case ShaderLanguage::TYPE_INT: {
int32_t *gui = (int32_t *)data;
gui[0] = value[0].sint;
} break;
case ShaderLanguage::TYPE_IVEC2: {
int32_t *gui = (int32_t *)data;
for (int i = 0; i < 2; i++) {
gui[i] = value[i].sint;
}
} break;
case ShaderLanguage::TYPE_IVEC3: {
int32_t *gui = (int32_t *)data;
for (int i = 0; i < 3; i++) {
gui[i] = value[i].sint;
}
} break;
case ShaderLanguage::TYPE_IVEC4: {
int32_t *gui = (int32_t *)data;
for (int i = 0; i < 4; i++) {
gui[i] = value[i].sint;
}
} break;
case ShaderLanguage::TYPE_UINT: {
uint32_t *gui = (uint32_t *)data;
gui[0] = value[0].uint;
} break;
case ShaderLanguage::TYPE_UVEC2: {
int32_t *gui = (int32_t *)data;
for (int i = 0; i < 2; i++) {
gui[i] = value[i].uint;
}
} break;
case ShaderLanguage::TYPE_UVEC3: {
int32_t *gui = (int32_t *)data;
for (int i = 0; i < 3; i++) {
gui[i] = value[i].uint;
}
} break;
case ShaderLanguage::TYPE_UVEC4: {
int32_t *gui = (int32_t *)data;
for (int i = 0; i < 4; i++) {
gui[i] = value[i].uint;
}
} break;
case ShaderLanguage::TYPE_FLOAT: {
float *gui = (float *)data;
gui[0] = value[0].real;
} break;
case ShaderLanguage::TYPE_VEC2: {
float *gui = (float *)data;
for (int i = 0; i < 2; i++) {
gui[i] = value[i].real;
}
} break;
case ShaderLanguage::TYPE_VEC3: {
float *gui = (float *)data;
for (int i = 0; i < 3; i++) {
gui[i] = value[i].real;
}
} break;
case ShaderLanguage::TYPE_VEC4: {
float *gui = (float *)data;
for (int i = 0; i < 4; i++) {
gui[i] = value[i].real;
}
} break;
case ShaderLanguage::TYPE_MAT2: {
float *gui = (float *)data;
//in std140 members of mat2 are treated as vec4s
gui[0] = value[0].real;
gui[1] = value[1].real;
gui[2] = 0;
gui[3] = 0;
gui[4] = value[2].real;
gui[5] = value[3].real;
gui[6] = 0;
gui[7] = 0;
} break;
case ShaderLanguage::TYPE_MAT3: {
float *gui = (float *)data;
gui[0] = value[0].real;
gui[1] = value[1].real;
gui[2] = value[2].real;
gui[3] = 0;
gui[4] = value[3].real;
gui[5] = value[4].real;
gui[6] = value[5].real;
gui[7] = 0;
gui[8] = value[6].real;
gui[9] = value[7].real;
gui[10] = value[8].real;
gui[11] = 0;
} break;
case ShaderLanguage::TYPE_MAT4: {
float *gui = (float *)data;
for (int i = 0; i < 16; i++) {
gui[i] = value[i].real;
}
} break;
default: {
}
}
}
_FORCE_INLINE_ static void _fill_std140_ubo_empty(ShaderLanguage::DataType type, int p_array_size, uint8_t *data) {
if (p_array_size <= 0) {
p_array_size = 1;
}
switch (type) {
case ShaderLanguage::TYPE_BOOL:
case ShaderLanguage::TYPE_INT:
case ShaderLanguage::TYPE_UINT:
case ShaderLanguage::TYPE_FLOAT: {
memset(data, 0, 4 * p_array_size);
} break;
case ShaderLanguage::TYPE_BVEC2:
case ShaderLanguage::TYPE_IVEC2:
case ShaderLanguage::TYPE_UVEC2:
case ShaderLanguage::TYPE_VEC2: {
memset(data, 0, 8 * p_array_size);
} break;
case ShaderLanguage::TYPE_BVEC3:
case ShaderLanguage::TYPE_IVEC3:
case ShaderLanguage::TYPE_UVEC3:
case ShaderLanguage::TYPE_VEC3: {
memset(data, 0, 12 * p_array_size);
} break;
case ShaderLanguage::TYPE_BVEC4:
case ShaderLanguage::TYPE_IVEC4:
case ShaderLanguage::TYPE_UVEC4:
case ShaderLanguage::TYPE_VEC4: {
memset(data, 0, 16 * p_array_size);
} break;
case ShaderLanguage::TYPE_MAT2: {
memset(data, 0, 32 * p_array_size);
} break;
case ShaderLanguage::TYPE_MAT3: {
memset(data, 0, 48 * p_array_size);
} break;
case ShaderLanguage::TYPE_MAT4: {
memset(data, 0, 64 * p_array_size);
} break;
default: {
}
}
}
///////////////////////////////////////////////////////////////////////////
// ShaderData
void ShaderData::set_path_hint(const String &p_hint) {
path = p_hint;
}
void ShaderData::set_default_texture_parameter(const StringName &p_name, RID p_texture, int p_index) {
if (!p_texture.is_valid()) {
if (default_texture_params.has(p_name) && default_texture_params[p_name].has(p_index)) {
default_texture_params[p_name].erase(p_index);
if (default_texture_params[p_name].is_empty()) {
default_texture_params.erase(p_name);
}
}
} else {
if (!default_texture_params.has(p_name)) {
default_texture_params[p_name] = HashMap<int, RID>();
}
default_texture_params[p_name][p_index] = p_texture;
}
}
Variant ShaderData::get_default_parameter(const StringName &p_parameter) const {
if (uniforms.has(p_parameter)) {
ShaderLanguage::ShaderNode::Uniform uniform = uniforms[p_parameter];
Vector<ShaderLanguage::ConstantNode::Value> default_value = uniform.default_value;
return ShaderLanguage::constant_value_to_variant(default_value, uniform.type, uniform.array_size, uniform.hint);
}
return Variant();
}
void ShaderData::get_shader_uniform_list(List<PropertyInfo> *p_param_list) const {
SortArray<Pair<StringName, int>, ShaderLanguage::UniformOrderComparator> sorter;
LocalVector<Pair<StringName, int>> filtered_uniforms;
for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : uniforms) {
if (E.value.scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_LOCAL) {
continue;
}
if (E.value.texture_order >= 0) {
filtered_uniforms.push_back(Pair<StringName, int>(E.key, E.value.texture_order + 100000));
} else {
filtered_uniforms.push_back(Pair<StringName, int>(E.key, E.value.order));
}
}
int uniform_count = filtered_uniforms.size();
sorter.sort(filtered_uniforms.ptr(), uniform_count);
String last_group;
for (int i = 0; i < uniform_count; i++) {
const StringName &uniform_name = filtered_uniforms[i].first;
const ShaderLanguage::ShaderNode::Uniform &uniform = uniforms[uniform_name];
String group = uniform.group;
if (!uniform.subgroup.is_empty()) {
group += "::" + uniform.subgroup;
}
if (group != last_group) {
PropertyInfo pi;
pi.usage = PROPERTY_USAGE_GROUP;
pi.name = group;
p_param_list->push_back(pi);
last_group = group;
}
PropertyInfo pi = ShaderLanguage::uniform_to_property_info(uniform);
pi.name = uniform_name;
p_param_list->push_back(pi);
}
}
void ShaderData::get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const {
for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : uniforms) {
if (E.value.scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) {
continue;
}
RendererMaterialStorage::InstanceShaderParam p;
p.info = ShaderLanguage::uniform_to_property_info(E.value);
p.info.name = E.key; //supply name
p.index = E.value.instance_index;
p.default_value = ShaderLanguage::constant_value_to_variant(E.value.default_value, E.value.type, E.value.array_size, E.value.hint);
p_param_list->push_back(p);
}
}
bool ShaderData::is_parameter_texture(const StringName &p_param) const {
if (!uniforms.has(p_param)) {
return false;
}
return uniforms[p_param].texture_order >= 0;
}
///////////////////////////////////////////////////////////////////////////
// MaterialData
// Look up table to translate ShaderLanguage::DataType to GL_TEXTURE_*
static const GLenum target_from_type[ShaderLanguage::TYPE_MAX] = {
GL_TEXTURE_2D, // TYPE_VOID,
GL_TEXTURE_2D, // TYPE_BOOL,
GL_TEXTURE_2D, // TYPE_BVEC2,
GL_TEXTURE_2D, // TYPE_BVEC3,
GL_TEXTURE_2D, // TYPE_BVEC4,
GL_TEXTURE_2D, // TYPE_INT,
GL_TEXTURE_2D, // TYPE_IVEC2,
GL_TEXTURE_2D, // TYPE_IVEC3,
GL_TEXTURE_2D, // TYPE_IVEC4,
GL_TEXTURE_2D, // TYPE_UINT,
GL_TEXTURE_2D, // TYPE_UVEC2,
GL_TEXTURE_2D, // TYPE_UVEC3,
GL_TEXTURE_2D, // TYPE_UVEC4,
GL_TEXTURE_2D, // TYPE_FLOAT,
GL_TEXTURE_2D, // TYPE_VEC2,
GL_TEXTURE_2D, // TYPE_VEC3,
GL_TEXTURE_2D, // TYPE_VEC4,
GL_TEXTURE_2D, // TYPE_MAT2,
GL_TEXTURE_2D, // TYPE_MAT3,
GL_TEXTURE_2D, // TYPE_MAT4,
GL_TEXTURE_2D, // TYPE_SAMPLER2D,
GL_TEXTURE_2D, // TYPE_ISAMPLER2D,
GL_TEXTURE_2D, // TYPE_USAMPLER2D,
GL_TEXTURE_2D_ARRAY, // TYPE_SAMPLER2DARRAY,
GL_TEXTURE_2D_ARRAY, // TYPE_ISAMPLER2DARRAY,
GL_TEXTURE_2D_ARRAY, // TYPE_USAMPLER2DARRAY,
GL_TEXTURE_3D, // TYPE_SAMPLER3D,
GL_TEXTURE_3D, // TYPE_ISAMPLER3D,
GL_TEXTURE_3D, // TYPE_USAMPLER3D,
GL_TEXTURE_CUBE_MAP, // TYPE_SAMPLERCUBE,
GL_TEXTURE_CUBE_MAP, // TYPE_SAMPLERCUBEARRAY,
GL_TEXTURE_2D, // TYPE_STRUCT
};
static const RS::CanvasItemTextureRepeat repeat_from_uniform[ShaderLanguage::REPEAT_DEFAULT + 1] = {
RS::CanvasItemTextureRepeat::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED, // ShaderLanguage::TextureRepeat::REPEAT_DISABLE,
RS::CanvasItemTextureRepeat::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED, // ShaderLanguage::TextureRepeat::REPEAT_ENABLE,
RS::CanvasItemTextureRepeat::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED, // ShaderLanguage::TextureRepeat::REPEAT_DEFAULT,
};
static const RS::CanvasItemTextureRepeat repeat_from_uniform_canvas[ShaderLanguage::REPEAT_DEFAULT + 1] = {
RS::CanvasItemTextureRepeat::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED, // ShaderLanguage::TextureRepeat::REPEAT_DISABLE,
RS::CanvasItemTextureRepeat::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED, // ShaderLanguage::TextureRepeat::REPEAT_ENABLE,
RS::CanvasItemTextureRepeat::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED, // ShaderLanguage::TextureRepeat::REPEAT_DEFAULT,
};
static const RS::CanvasItemTextureFilter filter_from_uniform[ShaderLanguage::FILTER_DEFAULT + 1] = {
RS::CanvasItemTextureFilter::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, // ShaderLanguage::TextureFilter::FILTER_NEAREST,
RS::CanvasItemTextureFilter::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, // ShaderLanguage::TextureFilter::FILTER_LINEAR,
RS::CanvasItemTextureFilter::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, // ShaderLanguage::TextureFilter::FILTER_NEAREST_MIPMAP,
RS::CanvasItemTextureFilter::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, // ShaderLanguage::TextureFilter::FILTER_LINEAR_MIPMAP,
RS::CanvasItemTextureFilter::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, // ShaderLanguage::TextureFilter::FILTER_NEAREST_MIPMAP_ANISOTROPIC,
RS::CanvasItemTextureFilter::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, // ShaderLanguage::TextureFilter::FILTER_LINEAR_MIPMAP_ANISOTROPIC,
RS::CanvasItemTextureFilter::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, // ShaderLanguage::TextureFilter::FILTER_DEFAULT,
};
static const RS::CanvasItemTextureFilter filter_from_uniform_canvas[ShaderLanguage::FILTER_DEFAULT + 1] = {
RS::CanvasItemTextureFilter::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, // ShaderLanguage::TextureFilter::FILTER_NEAREST,
RS::CanvasItemTextureFilter::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, // ShaderLanguage::TextureFilter::FILTER_LINEAR,
RS::CanvasItemTextureFilter::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS, // ShaderLanguage::TextureFilter::FILTER_NEAREST_MIPMAP,
RS::CanvasItemTextureFilter::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, // ShaderLanguage::TextureFilter::FILTER_LINEAR_MIPMAP,
RS::CanvasItemTextureFilter::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC, // ShaderLanguage::TextureFilter::FILTER_NEAREST_MIPMAP_ANISOTROPIC,
RS::CanvasItemTextureFilter::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC, // ShaderLanguage::TextureFilter::FILTER_LINEAR_MIPMAP_ANISOTROPIC,
RS::CanvasItemTextureFilter::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, // ShaderLanguage::TextureFilter::FILTER_DEFAULT,
};
void MaterialData::update_uniform_buffer(const HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> &p_uniforms, const uint32_t *p_uniform_offsets, const HashMap<StringName, Variant> &p_parameters, uint8_t *p_buffer, uint32_t p_buffer_size) {
MaterialStorage *material_storage = MaterialStorage::get_singleton();
bool uses_global_buffer = false;
for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : p_uniforms) {
if (E.value.order < 0) {
continue; // texture, does not go here
}
if (E.value.scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) {
continue; //instance uniforms don't appear in the buffer
}
if (E.value.scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_GLOBAL) {
//this is a global variable, get the index to it
GlobalShaderUniforms::Variable *gv = material_storage->global_shader_uniforms.variables.getptr(E.key);
uint32_t index = 0;
if (gv) {
index = gv->buffer_index;
} else {
WARN_PRINT("Shader uses global parameter '" + E.key + "', but it was removed at some point. Material will not display correctly.");
}
uint32_t offset = p_uniform_offsets[E.value.order];
uint32_t *intptr = (uint32_t *)&p_buffer[offset];
*intptr = index;
uses_global_buffer = true;
continue;
}
//regular uniform
uint32_t offset = p_uniform_offsets[E.value.order];
#ifdef DEBUG_ENABLED
uint32_t size = 0U;
// The following code enforces a 16-byte alignment of uniform arrays.
if (E.value.array_size > 0) {
size = ShaderLanguage::get_datatype_size(E.value.type) * E.value.array_size;
int m = (16 * E.value.array_size);
if ((size % m) != 0U) {
size += m - (size % m);
}
} else {
size = ShaderLanguage::get_datatype_size(E.value.type);
}
ERR_CONTINUE(offset + size > p_buffer_size);
#endif
uint8_t *data = &p_buffer[offset];
HashMap<StringName, Variant>::ConstIterator V = p_parameters.find(E.key);
if (V) {
//user provided
_fill_std140_variant_ubo_value(E.value.type, E.value.array_size, V->value, data);
} else if (E.value.default_value.size()) {
//default value
_fill_std140_ubo_value(E.value.type, E.value.default_value, data);
//value=E.value.default_value;
} else {
//zero because it was not provided
if ((E.value.type == ShaderLanguage::TYPE_VEC3 || E.value.type == ShaderLanguage::TYPE_VEC4) && E.value.hint == ShaderLanguage::ShaderNode::Uniform::HINT_SOURCE_COLOR) {
//colors must be set as black, with alpha as 1.0
_fill_std140_variant_ubo_value(E.value.type, E.value.array_size, Color(0, 0, 0, 1), data);
} else {
//else just zero it out
_fill_std140_ubo_empty(E.value.type, E.value.array_size, data);
}
}
}
if (uses_global_buffer != (global_buffer_E != nullptr)) {
if (uses_global_buffer) {
global_buffer_E = material_storage->global_shader_uniforms.materials_using_buffer.push_back(self);
} else {
material_storage->global_shader_uniforms.materials_using_buffer.erase(global_buffer_E);
global_buffer_E = nullptr;
}
}
}
MaterialData::~MaterialData() {
MaterialStorage *material_storage = MaterialStorage::get_singleton();
if (global_buffer_E) {
//unregister global buffers
material_storage->global_shader_uniforms.materials_using_buffer.erase(global_buffer_E);
}
if (global_texture_E) {
//unregister global textures
for (const KeyValue<StringName, uint64_t> &E : used_global_textures) {
GlobalShaderUniforms::Variable *v = material_storage->global_shader_uniforms.variables.getptr(E.key);
if (v) {
v->texture_materials.erase(self);
}
}
//unregister material from those using global textures
material_storage->global_shader_uniforms.materials_using_texture.erase(global_texture_E);
}
if (uniform_buffer) {
glDeleteBuffers(1, &uniform_buffer);
uniform_buffer = 0;
}
}
void MaterialData::update_textures(const HashMap<StringName, Variant> &p_parameters, const HashMap<StringName, HashMap<int, RID>> &p_default_textures, const Vector<ShaderCompiler::GeneratedCode::Texture> &p_texture_uniforms, RID *p_textures, bool p_is_3d_shader_type) {
TextureStorage *texture_storage = TextureStorage::get_singleton();
MaterialStorage *material_storage = MaterialStorage::get_singleton();
#ifdef TOOLS_ENABLED
Texture *roughness_detect_texture = nullptr;
RS::TextureDetectRoughnessChannel roughness_channel = RS::TEXTURE_DETECT_ROUGHNESS_R;
Texture *normal_detect_texture = nullptr;
#endif
bool uses_global_textures = false;
global_textures_pass++;
for (int i = 0, k = 0; i < p_texture_uniforms.size(); i++) {
const StringName &uniform_name = p_texture_uniforms[i].name;
int uniform_array_size = p_texture_uniforms[i].array_size;
Vector<RID> textures;
if (p_texture_uniforms[i].hint == ShaderLanguage::ShaderNode::Uniform::HINT_SCREEN_TEXTURE ||
p_texture_uniforms[i].hint == ShaderLanguage::ShaderNode::Uniform::HINT_NORMAL_ROUGHNESS_TEXTURE ||
p_texture_uniforms[i].hint == ShaderLanguage::ShaderNode::Uniform::HINT_DEPTH_TEXTURE) {
continue;
}
if (p_texture_uniforms[i].global) {
uses_global_textures = true;
GlobalShaderUniforms::Variable *v = material_storage->global_shader_uniforms.variables.getptr(uniform_name);
if (v) {
if (v->buffer_index >= 0) {
WARN_PRINT("Shader uses global parameter texture '" + String(uniform_name) + "', but it changed type and is no longer a texture!.");
} else {
HashMap<StringName, uint64_t>::Iterator 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->value = global_textures_pass;
}
textures.push_back(v->override.get_type() != Variant::NIL ? v->override : v->value);
}
} else {
WARN_PRINT("Shader uses global parameter texture '" + String(uniform_name) + "', but it was removed at some point. Material will not display correctly.");
}
} else {
HashMap<StringName, Variant>::ConstIterator V = p_parameters.find(uniform_name);
if (V) {
if (V->value.is_array()) {
Array array = (Array)V->value;
if (uniform_array_size > 0) {
for (int j = 0; j < array.size(); j++) {
textures.push_back(array[j]);
}
} else {
if (array.size() > 0) {
textures.push_back(array[0]);
}
}
} else {
textures.push_back(V->value);
}
}
if (uniform_array_size > 0) {
if (textures.size() < uniform_array_size) {
HashMap<StringName, HashMap<int, RID>>::ConstIterator W = p_default_textures.find(uniform_name);
for (int j = textures.size(); j < uniform_array_size; j++) {
if (W && W->value.has(j)) {
textures.push_back(W->value[j]);
} else {
textures.push_back(RID());
}
}
}
} else if (textures.is_empty()) {
HashMap<StringName, HashMap<int, RID>>::ConstIterator W = p_default_textures.find(uniform_name);
if (W && W->value.has(0)) {
textures.push_back(W->value[0]);
}
}
}
RID gl_texture = texture_storage->texture_gl_get_default(DEFAULT_GL_TEXTURE_WHITE);
if (textures.is_empty()) {
//check default usage
switch (p_texture_uniforms[i].type) {
case ShaderLanguage::TYPE_ISAMPLER2D:
case ShaderLanguage::TYPE_USAMPLER2D:
case ShaderLanguage::TYPE_SAMPLER2D: {
switch (p_texture_uniforms[i].hint) {
case ShaderLanguage::ShaderNode::Uniform::HINT_DEFAULT_BLACK: {
gl_texture = texture_storage->texture_gl_get_default(DEFAULT_GL_TEXTURE_BLACK);
} break;
case ShaderLanguage::ShaderNode::Uniform::HINT_DEFAULT_TRANSPARENT: {
gl_texture = texture_storage->texture_gl_get_default(DEFAULT_GL_TEXTURE_TRANSPARENT);
} break;
case ShaderLanguage::ShaderNode::Uniform::HINT_ANISOTROPY: {
gl_texture = texture_storage->texture_gl_get_default(DEFAULT_GL_TEXTURE_ANISO);
} break;
case ShaderLanguage::ShaderNode::Uniform::HINT_NORMAL: {
gl_texture = texture_storage->texture_gl_get_default(DEFAULT_GL_TEXTURE_NORMAL);
} break;
case ShaderLanguage::ShaderNode::Uniform::HINT_ROUGHNESS_NORMAL: {
gl_texture = texture_storage->texture_gl_get_default(DEFAULT_GL_TEXTURE_NORMAL);
} break;
default: {
gl_texture = texture_storage->texture_gl_get_default(DEFAULT_GL_TEXTURE_WHITE);
} break;
}
} break;
case ShaderLanguage::TYPE_SAMPLERCUBE: {
switch (p_texture_uniforms[i].hint) {
case ShaderLanguage::ShaderNode::Uniform::HINT_DEFAULT_BLACK: {
gl_texture = texture_storage->texture_gl_get_default(DEFAULT_GL_TEXTURE_CUBEMAP_BLACK);
} break;
default: {
gl_texture = texture_storage->texture_gl_get_default(DEFAULT_GL_TEXTURE_CUBEMAP_WHITE);
} break;
}
} break;
case ShaderLanguage::TYPE_SAMPLERCUBEARRAY: {
ERR_PRINT_ONCE("Type: SamplerCubeArray not supported in GL Compatibility rendering backend, please use another type.");
} break;
case ShaderLanguage::TYPE_ISAMPLER3D:
case ShaderLanguage::TYPE_USAMPLER3D:
case ShaderLanguage::TYPE_SAMPLER3D: {
switch (p_texture_uniforms[i].hint) {
case ShaderLanguage::ShaderNode::Uniform::HINT_DEFAULT_BLACK: {
gl_texture = texture_storage->texture_gl_get_default(DEFAULT_GL_TEXTURE_3D_BLACK);
} break;
default: {
gl_texture = texture_storage->texture_gl_get_default(DEFAULT_GL_TEXTURE_3D_WHITE);
} break;
}
} break;
case ShaderLanguage::TYPE_ISAMPLER2DARRAY:
case ShaderLanguage::TYPE_USAMPLER2DARRAY:
case ShaderLanguage::TYPE_SAMPLER2DARRAY: {
gl_texture = texture_storage->texture_gl_get_default(DEFAULT_GL_TEXTURE_2D_ARRAY_WHITE);
} break;
default: {
}
}
#ifdef TOOLS_ENABLED
if (roughness_detect_texture && normal_detect_texture && !normal_detect_texture->path.is_empty()) {
roughness_detect_texture->detect_roughness_callback(roughness_detect_texture->detect_roughness_callback_ud, normal_detect_texture->path, roughness_channel);
}
#endif
if (uniform_array_size > 0) {
for (int j = 0; j < uniform_array_size; j++) {
p_textures[k++] = gl_texture;
}
} else {
p_textures[k++] = gl_texture;
}
} else {
for (int j = 0; j < textures.size(); j++) {
Texture *tex = TextureStorage::get_singleton()->get_texture(textures[j]);
if (tex) {
gl_texture = textures[j];
#ifdef TOOLS_ENABLED
if (tex->detect_3d_callback && p_is_3d_shader_type) {
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
}
#ifdef TOOLS_ENABLED
if (roughness_detect_texture && normal_detect_texture && !normal_detect_texture->path.is_empty()) {
roughness_detect_texture->detect_roughness_callback(roughness_detect_texture->detect_roughness_callback_ud, normal_detect_texture->path, roughness_channel);
}
#endif
p_textures[k++] = gl_texture;
}
}
}
{
//for textures no longer used, unregister them
List<StringName> to_delete;
for (KeyValue<StringName, uint64_t> &E : used_global_textures) {
if (E.value != global_textures_pass) {
to_delete.push_back(E.key);
GlobalShaderUniforms::Variable *v = material_storage->global_shader_uniforms.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 = material_storage->global_shader_uniforms.materials_using_texture.push_back(self);
} else {
material_storage->global_shader_uniforms.materials_using_texture.erase(global_texture_E);
global_texture_E = nullptr;
}
}
}
}
void MaterialData::update_parameters_internal(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty, const HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> &p_uniforms, const uint32_t *p_uniform_offsets, const Vector<ShaderCompiler::GeneratedCode::Texture> &p_texture_uniforms, const HashMap<StringName, HashMap<int, RID>> &p_default_texture_params, uint32_t p_ubo_size, bool p_is_3d_shader_type) {
if ((uint32_t)ubo_data.size() != p_ubo_size) {
p_uniform_dirty = true;
if (!uniform_buffer) {
glGenBuffers(1, &uniform_buffer);
}
ubo_data.resize(p_ubo_size);
if (ubo_data.size()) {
memset(ubo_data.ptrw(), 0, ubo_data.size()); //clear
}
}
//check whether buffer changed
if (p_uniform_dirty && ubo_data.size()) {
update_uniform_buffer(p_uniforms, p_uniform_offsets, p_parameters, ubo_data.ptrw(), ubo_data.size());
glBindBuffer(GL_UNIFORM_BUFFER, uniform_buffer);
glBufferData(GL_UNIFORM_BUFFER, ubo_data.size(), ubo_data.ptrw(), GL_DYNAMIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
}
uint32_t tex_uniform_count = 0U;
for (int i = 0; i < p_texture_uniforms.size(); i++) {
tex_uniform_count += uint32_t(p_texture_uniforms[i].array_size > 0 ? p_texture_uniforms[i].array_size : 1);
}
if ((uint32_t)texture_cache.size() != tex_uniform_count || p_textures_dirty) {
texture_cache.resize(tex_uniform_count);
p_textures_dirty = true;
}
if (p_textures_dirty && tex_uniform_count) {
update_textures(p_parameters, p_default_texture_params, p_texture_uniforms, texture_cache.ptrw(), p_is_3d_shader_type);
}
}
///////////////////////////////////////////////////////////////////////////
// Material Storage
MaterialStorage *MaterialStorage::singleton = nullptr;
MaterialStorage *MaterialStorage::get_singleton() {
return singleton;
}
MaterialStorage::MaterialStorage() {
singleton = this;
shader_data_request_func[RS::SHADER_SPATIAL] = _create_scene_shader_func;
shader_data_request_func[RS::SHADER_CANVAS_ITEM] = _create_canvas_shader_func;
shader_data_request_func[RS::SHADER_PARTICLES] = _create_particles_shader_func;
shader_data_request_func[RS::SHADER_SKY] = _create_sky_shader_func;
shader_data_request_func[RS::SHADER_FOG] = nullptr;
material_data_request_func[RS::SHADER_SPATIAL] = _create_scene_material_func;
material_data_request_func[RS::SHADER_CANVAS_ITEM] = _create_canvas_material_func;
material_data_request_func[RS::SHADER_PARTICLES] = _create_particles_material_func;
material_data_request_func[RS::SHADER_SKY] = _create_sky_material_func;
material_data_request_func[RS::SHADER_FOG] = nullptr;
static_assert(sizeof(GlobalShaderUniforms::Value) == 16);
global_shader_uniforms.buffer_size = MAX(4096, (int)GLOBAL_GET("rendering/limits/global_shader_variables/buffer_size"));
if (global_shader_uniforms.buffer_size > uint32_t(Config::get_singleton()->max_uniform_buffer_size)) {
global_shader_uniforms.buffer_size = uint32_t(Config::get_singleton()->max_uniform_buffer_size);
WARN_PRINT("Project setting \"rendering/limits/global_shader_variables/buffer_size\" exceeds maximum uniform buffer size of: " + itos(Config::get_singleton()->max_uniform_buffer_size));
}
global_shader_uniforms.buffer_values = memnew_arr(GlobalShaderUniforms::Value, global_shader_uniforms.buffer_size);
memset(global_shader_uniforms.buffer_values, 0, sizeof(GlobalShaderUniforms::Value) * global_shader_uniforms.buffer_size);
global_shader_uniforms.buffer_usage = memnew_arr(GlobalShaderUniforms::ValueUsage, global_shader_uniforms.buffer_size);
global_shader_uniforms.buffer_dirty_regions = memnew_arr(bool, global_shader_uniforms.buffer_size / GlobalShaderUniforms::BUFFER_DIRTY_REGION_SIZE);
memset(global_shader_uniforms.buffer_dirty_regions, 0, sizeof(bool) * global_shader_uniforms.buffer_size / GlobalShaderUniforms::BUFFER_DIRTY_REGION_SIZE);
glGenBuffers(1, &global_shader_uniforms.buffer);
glBindBuffer(GL_UNIFORM_BUFFER, global_shader_uniforms.buffer);
glBufferData(GL_UNIFORM_BUFFER, sizeof(GlobalShaderUniforms::Value) * global_shader_uniforms.buffer_size, nullptr, GL_DYNAMIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
{
// Setup CanvasItem compiler
ShaderCompiler::DefaultIdentifierActions actions;
actions.renames["VERTEX"] = "vertex";
actions.renames["LIGHT_VERTEX"] = "light_vertex";
actions.renames["SHADOW_VERTEX"] = "shadow_vertex";
actions.renames["UV"] = "uv";
actions.renames["POINT_SIZE"] = "point_size";
actions.renames["MODEL_MATRIX"] = "model_matrix";
actions.renames["CANVAS_MATRIX"] = "canvas_transform";
actions.renames["SCREEN_MATRIX"] = "screen_transform";
actions.renames["TIME"] = "time";
actions.renames["PI"] = _MKSTR(Math_PI);
actions.renames["TAU"] = _MKSTR(Math_TAU);
actions.renames["E"] = _MKSTR(Math_E);
actions.renames["AT_LIGHT_PASS"] = "false";
actions.renames["INSTANCE_CUSTOM"] = "instance_custom";
actions.renames["COLOR"] = "color";
actions.renames["NORMAL"] = "normal";
actions.renames["NORMAL_MAP"] = "normal_map";
actions.renames["NORMAL_MAP_DEPTH"] = "normal_map_depth";
actions.renames["TEXTURE"] = "color_texture";
actions.renames["TEXTURE_PIXEL_SIZE"] = "color_texture_pixel_size";
actions.renames["NORMAL_TEXTURE"] = "normal_texture";
actions.renames["SPECULAR_SHININESS_TEXTURE"] = "specular_texture";
actions.renames["SPECULAR_SHININESS"] = "specular_shininess";
actions.renames["SCREEN_UV"] = "screen_uv";
actions.renames["SCREEN_PIXEL_SIZE"] = "screen_pixel_size";
actions.renames["FRAGCOORD"] = "gl_FragCoord";
actions.renames["POINT_COORD"] = "gl_PointCoord";
actions.renames["INSTANCE_ID"] = "gl_InstanceIndex";
actions.renames["VERTEX_ID"] = "gl_VertexIndex";
actions.renames["LIGHT_POSITION"] = "light_position";
actions.renames["LIGHT_DIRECTION"] = "light_direction";
actions.renames["LIGHT_IS_DIRECTIONAL"] = "is_directional";
actions.renames["LIGHT_COLOR"] = "light_color";
actions.renames["LIGHT_ENERGY"] = "light_energy";
actions.renames["LIGHT"] = "light";
actions.renames["SHADOW_MODULATE"] = "shadow_modulate";
actions.renames["texture_sdf"] = "texture_sdf";
actions.renames["texture_sdf_normal"] = "texture_sdf_normal";
actions.renames["sdf_to_screen_uv"] = "sdf_to_screen_uv";
actions.renames["screen_uv_to_sdf"] = "screen_uv_to_sdf";
actions.usage_defines["COLOR"] = "#define COLOR_USED\n";
actions.usage_defines["SCREEN_UV"] = "#define SCREEN_UV_USED\n";
actions.usage_defines["SCREEN_PIXEL_SIZE"] = "@SCREEN_UV";
actions.usage_defines["NORMAL"] = "#define NORMAL_USED\n";
actions.usage_defines["NORMAL_MAP"] = "#define NORMAL_MAP_USED\n";
actions.usage_defines["LIGHT"] = "#define LIGHT_SHADER_CODE_USED\n";
actions.usage_defines["SPECULAR_SHININESS"] = "#define SPECULAR_SHININESS_USED\n";
actions.render_mode_defines["skip_vertex_transform"] = "#define SKIP_TRANSFORM_USED\n";
actions.render_mode_defines["unshaded"] = "#define MODE_UNSHADED\n";
actions.render_mode_defines["light_only"] = "#define MODE_LIGHT_ONLY\n";
actions.global_buffer_array_variable = "global_shader_uniforms";
shaders.compiler_canvas.initialize(actions);
}
{
// Setup Scene compiler
//shader compiler
ShaderCompiler::DefaultIdentifierActions actions;
actions.renames["MODEL_MATRIX"] = "model_matrix";
actions.renames["MODEL_NORMAL_MATRIX"] = "model_normal_matrix";
actions.renames["VIEW_MATRIX"] = "scene_data.view_matrix";
actions.renames["INV_VIEW_MATRIX"] = "scene_data.inv_view_matrix";
actions.renames["PROJECTION_MATRIX"] = "projection_matrix";
actions.renames["INV_PROJECTION_MATRIX"] = "inv_projection_matrix";
actions.renames["MODELVIEW_MATRIX"] = "modelview";
actions.renames["MODELVIEW_NORMAL_MATRIX"] = "modelview_normal";
actions.renames["VERTEX"] = "vertex";
actions.renames["NORMAL"] = "normal";
actions.renames["TANGENT"] = "tangent";
actions.renames["BINORMAL"] = "binormal";
actions.renames["POSITION"] = "position";
actions.renames["UV"] = "uv_interp";
actions.renames["UV2"] = "uv2_interp";
actions.renames["COLOR"] = "color_interp";
actions.renames["POINT_SIZE"] = "point_size";
actions.renames["INSTANCE_ID"] = "gl_InstanceID";
actions.renames["VERTEX_ID"] = "gl_VertexID";
actions.renames["ALPHA_SCISSOR_THRESHOLD"] = "alpha_scissor_threshold";
actions.renames["ALPHA_HASH_SCALE"] = "alpha_hash_scale";
actions.renames["ALPHA_ANTIALIASING_EDGE"] = "alpha_antialiasing_edge";
actions.renames["ALPHA_TEXTURE_COORDINATE"] = "alpha_texture_coordinate";
//builtins
actions.renames["TIME"] = "scene_data.time";
actions.renames["EXPOSURE"] = "(1.0 / scene_data.emissive_exposure_normalization)";
actions.renames["PI"] = _MKSTR(Math_PI);
actions.renames["TAU"] = _MKSTR(Math_TAU);
actions.renames["E"] = _MKSTR(Math_E);
actions.renames["VIEWPORT_SIZE"] = "scene_data.viewport_size";
actions.renames["FRAGCOORD"] = "gl_FragCoord";
actions.renames["FRONT_FACING"] = "gl_FrontFacing";
actions.renames["NORMAL_MAP"] = "normal_map";
actions.renames["NORMAL_MAP_DEPTH"] = "normal_map_depth";
actions.renames["ALBEDO"] = "albedo";
actions.renames["ALPHA"] = "alpha";
actions.renames["METALLIC"] = "metallic";
actions.renames["SPECULAR"] = "specular";
actions.renames["ROUGHNESS"] = "roughness";
actions.renames["RIM"] = "rim";
actions.renames["RIM_TINT"] = "rim_tint";
actions.renames["CLEARCOAT"] = "clearcoat";
actions.renames["CLEARCOAT_ROUGHNESS"] = "clearcoat_roughness";
actions.renames["ANISOTROPY"] = "anisotropy";
actions.renames["ANISOTROPY_FLOW"] = "anisotropy_flow";
actions.renames["SSS_STRENGTH"] = "sss_strength";
actions.renames["SSS_TRANSMITTANCE_COLOR"] = "transmittance_color";
actions.renames["SSS_TRANSMITTANCE_DEPTH"] = "transmittance_depth";
actions.renames["SSS_TRANSMITTANCE_BOOST"] = "transmittance_boost";
actions.renames["BACKLIGHT"] = "backlight";
actions.renames["AO"] = "ao";
actions.renames["AO_LIGHT_AFFECT"] = "ao_light_affect";
actions.renames["EMISSION"] = "emission";
actions.renames["POINT_COORD"] = "gl_PointCoord";
actions.renames["INSTANCE_CUSTOM"] = "instance_custom";
actions.renames["SCREEN_UV"] = "screen_uv";
actions.renames["DEPTH"] = "gl_FragDepth";
actions.renames["FOG"] = "fog";
actions.renames["RADIANCE"] = "custom_radiance";
actions.renames["IRRADIANCE"] = "custom_irradiance";
actions.renames["BONE_INDICES"] = "bone_attrib";
actions.renames["BONE_WEIGHTS"] = "weight_attrib";
actions.renames["CUSTOM0"] = "custom0_attrib";
actions.renames["CUSTOM1"] = "custom1_attrib";
actions.renames["CUSTOM2"] = "custom2_attrib";
actions.renames["CUSTOM3"] = "custom3_attrib";
actions.renames["OUTPUT_IS_SRGB"] = "SHADER_IS_SRGB";
actions.renames["NODE_POSITION_WORLD"] = "model_matrix[3].xyz";
actions.renames["CAMERA_POSITION_WORLD"] = "scene_data.inv_view_matrix[3].xyz";
actions.renames["CAMERA_DIRECTION_WORLD"] = "scene_data.view_matrix[3].xyz";
actions.renames["CAMERA_VISIBLE_LAYERS"] = "scene_data.camera_visible_layers";
actions.renames["NODE_POSITION_VIEW"] = "(scene_data.view_matrix * model_matrix)[3].xyz";
actions.renames["VIEW_INDEX"] = "ViewIndex";
actions.renames["VIEW_MONO_LEFT"] = "uint(0)";
actions.renames["VIEW_RIGHT"] = "uint(1)";
actions.renames["EYE_OFFSET"] = "eye_offset";
//for light
actions.renames["VIEW"] = "view";
actions.renames["SPECULAR_AMOUNT"] = "specular_amount";
actions.renames["LIGHT_COLOR"] = "light_color";
actions.renames["LIGHT_IS_DIRECTIONAL"] = "is_directional";
actions.renames["LIGHT"] = "light";
actions.renames["ATTENUATION"] = "attenuation";
actions.renames["DIFFUSE_LIGHT"] = "diffuse_light";
actions.renames["SPECULAR_LIGHT"] = "specular_light";
actions.usage_defines["NORMAL"] = "#define NORMAL_USED\n";
actions.usage_defines["TANGENT"] = "#define TANGENT_USED\n";
actions.usage_defines["BINORMAL"] = "@TANGENT";
actions.usage_defines["RIM"] = "#define LIGHT_RIM_USED\n";
actions.usage_defines["RIM_TINT"] = "@RIM";
actions.usage_defines["CLEARCOAT"] = "#define LIGHT_CLEARCOAT_USED\n";
actions.usage_defines["CLEARCOAT_ROUGHNESS"] = "@CLEARCOAT";
actions.usage_defines["ANISOTROPY"] = "#define LIGHT_ANISOTROPY_USED\n";
actions.usage_defines["ANISOTROPY_FLOW"] = "@ANISOTROPY";
actions.usage_defines["AO"] = "#define AO_USED\n";
actions.usage_defines["AO_LIGHT_AFFECT"] = "#define AO_USED\n";
actions.usage_defines["UV"] = "#define UV_USED\n";
actions.usage_defines["UV2"] = "#define UV2_USED\n";
actions.usage_defines["BONE_INDICES"] = "#define BONES_USED\n";
actions.usage_defines["BONE_WEIGHTS"] = "#define WEIGHTS_USED\n";
actions.usage_defines["CUSTOM0"] = "#define CUSTOM0_USED\n";
actions.usage_defines["CUSTOM1"] = "#define CUSTOM1_USED\n";
actions.usage_defines["CUSTOM2"] = "#define CUSTOM2_USED\n";
actions.usage_defines["CUSTOM3"] = "#define CUSTOM3_USED\n";
actions.usage_defines["NORMAL_MAP"] = "#define NORMAL_MAP_USED\n";
actions.usage_defines["NORMAL_MAP_DEPTH"] = "@NORMAL_MAP";
actions.usage_defines["COLOR"] = "#define COLOR_USED\n";
actions.usage_defines["INSTANCE_CUSTOM"] = "#define ENABLE_INSTANCE_CUSTOM\n";
actions.usage_defines["POSITION"] = "#define OVERRIDE_POSITION\n";
actions.usage_defines["ALPHA_SCISSOR_THRESHOLD"] = "#define ALPHA_SCISSOR_USED\n";
actions.usage_defines["ALPHA_HASH_SCALE"] = "#define ALPHA_HASH_USED\n";
actions.usage_defines["ALPHA_ANTIALIASING_EDGE"] = "#define ALPHA_ANTIALIASING_EDGE_USED\n";
actions.usage_defines["ALPHA_TEXTURE_COORDINATE"] = "@ALPHA_ANTIALIASING_EDGE";
actions.usage_defines["SSS_STRENGTH"] = "#define ENABLE_SSS\n";
actions.usage_defines["SSS_TRANSMITTANCE_DEPTH"] = "#define ENABLE_TRANSMITTANCE\n";
actions.usage_defines["BACKLIGHT"] = "#define LIGHT_BACKLIGHT_USED\n";
actions.usage_defines["SCREEN_UV"] = "#define SCREEN_UV_USED\n";
actions.usage_defines["DIFFUSE_LIGHT"] = "#define USE_LIGHT_SHADER_CODE\n";
actions.usage_defines["SPECULAR_LIGHT"] = "#define USE_LIGHT_SHADER_CODE\n";
actions.usage_defines["FOG"] = "#define CUSTOM_FOG_USED\n";
actions.usage_defines["RADIANCE"] = "#define CUSTOM_RADIANCE_USED\n";
actions.usage_defines["IRRADIANCE"] = "#define CUSTOM_IRRADIANCE_USED\n";
actions.render_mode_defines["skip_vertex_transform"] = "#define SKIP_TRANSFORM_USED\n";
actions.render_mode_defines["world_vertex_coords"] = "#define VERTEX_WORLD_COORDS_USED\n";
actions.render_mode_defines["ensure_correct_normals"] = "#define ENSURE_CORRECT_NORMALS\n";
actions.render_mode_defines["cull_front"] = "#define DO_SIDE_CHECK\n";
actions.render_mode_defines["cull_disabled"] = "#define DO_SIDE_CHECK\n";
actions.render_mode_defines["particle_trails"] = "#define USE_PARTICLE_TRAILS\n";
actions.render_mode_defines["depth_prepass_alpha"] = "#define USE_OPAQUE_PREPASS\n";
bool force_lambert = GLOBAL_GET("rendering/shading/overrides/force_lambert_over_burley");
if (!force_lambert) {
actions.render_mode_defines["diffuse_burley"] = "#define DIFFUSE_BURLEY\n";
}
actions.render_mode_defines["diffuse_lambert_wrap"] = "#define DIFFUSE_LAMBERT_WRAP\n";
actions.render_mode_defines["diffuse_toon"] = "#define DIFFUSE_TOON\n";
actions.render_mode_defines["sss_mode_skin"] = "#define SSS_MODE_SKIN\n";
actions.render_mode_defines["specular_schlick_ggx"] = "#define SPECULAR_SCHLICK_GGX\n";
actions.render_mode_defines["specular_toon"] = "#define SPECULAR_TOON\n";
actions.render_mode_defines["specular_disabled"] = "#define SPECULAR_DISABLED\n";
actions.render_mode_defines["shadows_disabled"] = "#define SHADOWS_DISABLED\n";
actions.render_mode_defines["ambient_light_disabled"] = "#define AMBIENT_LIGHT_DISABLED\n";
actions.render_mode_defines["shadow_to_opacity"] = "#define USE_SHADOW_TO_OPACITY\n";
actions.render_mode_defines["unshaded"] = "#define MODE_UNSHADED\n";
actions.default_filter = ShaderLanguage::FILTER_LINEAR_MIPMAP;
actions.default_repeat = ShaderLanguage::REPEAT_ENABLE;
actions.check_multiview_samplers = RasterizerGLES3::get_singleton()->is_xr_enabled();
actions.global_buffer_array_variable = "global_shader_uniforms";
shaders.compiler_scene.initialize(actions);
}
{
// Setup Particles compiler
ShaderCompiler::DefaultIdentifierActions actions;
actions.renames["COLOR"] = "out_color";
actions.renames["VELOCITY"] = "out_velocity_flags.xyz";
//actions.renames["MASS"] = "mass"; ?
actions.renames["ACTIVE"] = "particle_active";
actions.renames["RESTART"] = "restart";
actions.renames["CUSTOM"] = "out_custom";
for (int i = 0; i < PARTICLES_MAX_USERDATAS; i++) {
String udname = "USERDATA" + itos(i + 1);
actions.renames[udname] = "out_userdata" + itos(i + 1);
actions.usage_defines[udname] = "#define USERDATA" + itos(i + 1) + "_USED\n";
}
actions.renames["TRANSFORM"] = "xform";
actions.renames["TIME"] = "time";
actions.renames["PI"] = _MKSTR(Math_PI);
actions.renames["TAU"] = _MKSTR(Math_TAU);
actions.renames["E"] = _MKSTR(Math_E);
actions.renames["LIFETIME"] = "lifetime";
actions.renames["DELTA"] = "local_delta";
actions.renames["NUMBER"] = "particle_number";
actions.renames["INDEX"] = "index";
//actions.renames["GRAVITY"] = "current_gravity";
actions.renames["EMISSION_TRANSFORM"] = "emission_transform";
actions.renames["RANDOM_SEED"] = "random_seed";
actions.renames["RESTART_POSITION"] = "restart_position";
actions.renames["RESTART_ROT_SCALE"] = "restart_rotation_scale";
actions.renames["RESTART_VELOCITY"] = "restart_velocity";
actions.renames["RESTART_COLOR"] = "restart_color";
actions.renames["RESTART_CUSTOM"] = "restart_custom";
actions.renames["COLLIDED"] = "collided";
actions.renames["COLLISION_NORMAL"] = "collision_normal";
actions.renames["COLLISION_DEPTH"] = "collision_depth";
actions.renames["ATTRACTOR_FORCE"] = "attractor_force";
// These are unsupported, but may be used by users. To avoid compile time overhead, we add the stub only when used.
actions.renames["FLAG_EMIT_POSITION"] = "uint(1)";
actions.renames["FLAG_EMIT_ROT_SCALE"] = "uint(2)";
actions.renames["FLAG_EMIT_VELOCITY"] = "uint(4)";
actions.renames["FLAG_EMIT_COLOR"] = "uint(8)";
actions.renames["FLAG_EMIT_CUSTOM"] = "uint(16)";
actions.renames["emit_subparticle"] = "emit_subparticle";
actions.usage_defines["emit_subparticle"] = "\nbool emit_subparticle(mat4 p_xform, vec3 p_velocity, vec4 p_color, vec4 p_custom, uint p_flags) {\n\treturn false;\n}\n";
actions.render_mode_defines["disable_force"] = "#define DISABLE_FORCE\n";
actions.render_mode_defines["disable_velocity"] = "#define DISABLE_VELOCITY\n";
actions.render_mode_defines["keep_data"] = "#define ENABLE_KEEP_DATA\n";
actions.render_mode_defines["collision_use_scale"] = "#define USE_COLLISION_SCALE\n";
actions.default_filter = ShaderLanguage::FILTER_LINEAR_MIPMAP;
actions.default_repeat = ShaderLanguage::REPEAT_ENABLE;
actions.global_buffer_array_variable = "global_shader_uniforms";
shaders.compiler_particles.initialize(actions);
}
{
// Setup Sky compiler
ShaderCompiler::DefaultIdentifierActions actions;
actions.renames["COLOR"] = "color";
actions.renames["ALPHA"] = "alpha";
actions.renames["EYEDIR"] = "cube_normal";
actions.renames["POSITION"] = "position";
actions.renames["SKY_COORDS"] = "panorama_coords";
actions.renames["SCREEN_UV"] = "uv";
actions.renames["TIME"] = "time";
actions.renames["FRAGCOORD"] = "gl_FragCoord";
actions.renames["PI"] = _MKSTR(Math_PI);
actions.renames["TAU"] = _MKSTR(Math_TAU);
actions.renames["E"] = _MKSTR(Math_E);
actions.renames["HALF_RES_COLOR"] = "half_res_color";
actions.renames["QUARTER_RES_COLOR"] = "quarter_res_color";
actions.renames["RADIANCE"] = "radiance";
actions.renames["FOG"] = "custom_fog";
actions.renames["LIGHT0_ENABLED"] = "directional_lights.data[0].enabled";
actions.renames["LIGHT0_DIRECTION"] = "directional_lights.data[0].direction_energy.xyz";
actions.renames["LIGHT0_ENERGY"] = "directional_lights.data[0].direction_energy.w";
actions.renames["LIGHT0_COLOR"] = "directional_lights.data[0].color_size.xyz";
actions.renames["LIGHT0_SIZE"] = "directional_lights.data[0].color_size.w";
actions.renames["LIGHT1_ENABLED"] = "directional_lights.data[1].enabled";
actions.renames["LIGHT1_DIRECTION"] = "directional_lights.data[1].direction_energy.xyz";
actions.renames["LIGHT1_ENERGY"] = "directional_lights.data[1].direction_energy.w";
actions.renames["LIGHT1_COLOR"] = "directional_lights.data[1].color_size.xyz";
actions.renames["LIGHT1_SIZE"] = "directional_lights.data[1].color_size.w";
actions.renames["LIGHT2_ENABLED"] = "directional_lights.data[2].enabled";
actions.renames["LIGHT2_DIRECTION"] = "directional_lights.data[2].direction_energy.xyz";
actions.renames["LIGHT2_ENERGY"] = "directional_lights.data[2].direction_energy.w";
actions.renames["LIGHT2_COLOR"] = "directional_lights.data[2].color_size.xyz";
actions.renames["LIGHT2_SIZE"] = "directional_lights.data[2].color_size.w";
actions.renames["LIGHT3_ENABLED"] = "directional_lights.data[3].enabled";
actions.renames["LIGHT3_DIRECTION"] = "directional_lights.data[3].direction_energy.xyz";
actions.renames["LIGHT3_ENERGY"] = "directional_lights.data[3].direction_energy.w";
actions.renames["LIGHT3_COLOR"] = "directional_lights.data[3].color_size.xyz";
actions.renames["LIGHT3_SIZE"] = "directional_lights.data[3].color_size.w";
actions.renames["AT_CUBEMAP_PASS"] = "AT_CUBEMAP_PASS";
actions.renames["AT_HALF_RES_PASS"] = "AT_HALF_RES_PASS";
actions.renames["AT_QUARTER_RES_PASS"] = "AT_QUARTER_RES_PASS";
actions.usage_defines["HALF_RES_COLOR"] = "\n#define USES_HALF_RES_COLOR\n";
actions.usage_defines["QUARTER_RES_COLOR"] = "\n#define USES_QUARTER_RES_COLOR\n";
actions.render_mode_defines["disable_fog"] = "#define DISABLE_FOG\n";
actions.render_mode_defines["use_debanding"] = "#define USE_DEBANDING\n";
actions.default_filter = ShaderLanguage::FILTER_LINEAR_MIPMAP;
actions.default_repeat = ShaderLanguage::REPEAT_ENABLE;
actions.global_buffer_array_variable = "global_shader_uniforms";
shaders.compiler_sky.initialize(actions);
}
}
MaterialStorage::~MaterialStorage() {
//shaders.copy.version_free(shaders.copy_version);
memdelete_arr(global_shader_uniforms.buffer_values);
memdelete_arr(global_shader_uniforms.buffer_usage);
memdelete_arr(global_shader_uniforms.buffer_dirty_regions);
glDeleteBuffers(1, &global_shader_uniforms.buffer);
singleton = nullptr;
}
/* GLOBAL SHADER UNIFORM API */
int32_t MaterialStorage::_global_shader_uniform_allocate(uint32_t p_elements) {
int32_t idx = 0;
while (idx + p_elements <= global_shader_uniforms.buffer_size) {
if (global_shader_uniforms.buffer_usage[idx].elements == 0) {
bool valid = true;
for (uint32_t i = 1; i < p_elements; i++) {
if (global_shader_uniforms.buffer_usage[idx + i].elements > 0) {
valid = false;
idx += i + global_shader_uniforms.buffer_usage[idx + i].elements;
break;
}
}
if (!valid) {
continue; //if not valid, idx is in new position
}
return idx;
} else {
idx += global_shader_uniforms.buffer_usage[idx].elements;
}
}
return -1;
}
void MaterialStorage::_global_shader_uniform_store_in_buffer(int32_t p_index, RS::GlobalShaderParameterType p_type, const Variant &p_value) {
switch (p_type) {
case RS::GLOBAL_VAR_TYPE_BOOL: {
GlobalShaderUniforms::Value &bv = global_shader_uniforms.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: {
GlobalShaderUniforms::Value &bv = global_shader_uniforms.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: {
GlobalShaderUniforms::Value &bv = global_shader_uniforms.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: {
GlobalShaderUniforms::Value &bv = global_shader_uniforms.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: {
GlobalShaderUniforms::ValueInt &bv = *(GlobalShaderUniforms::ValueInt *)&global_shader_uniforms.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: {
GlobalShaderUniforms::ValueInt &bv = *(GlobalShaderUniforms::ValueInt *)&global_shader_uniforms.buffer_values[p_index];
Vector2i v = convert_to_vector<Vector2i>(p_value);
bv.x = v.x;
bv.y = v.y;
bv.z = 0;
bv.w = 0;
} break;
case RS::GLOBAL_VAR_TYPE_IVEC3: {
GlobalShaderUniforms::ValueInt &bv = *(GlobalShaderUniforms::ValueInt *)&global_shader_uniforms.buffer_values[p_index];
Vector3i v = convert_to_vector<Vector3i>(p_value);
bv.x = v.x;
bv.y = v.y;
bv.z = v.z;
bv.w = 0;
} break;
case RS::GLOBAL_VAR_TYPE_IVEC4: {
GlobalShaderUniforms::ValueInt &bv = *(GlobalShaderUniforms::ValueInt *)&global_shader_uniforms.buffer_values[p_index];
Vector4i v = convert_to_vector<Vector4i>(p_value);
bv.x = v.x;
bv.y = v.y;
bv.z = v.z;
bv.w = v.w;
} break;
case RS::GLOBAL_VAR_TYPE_RECT2I: {
GlobalShaderUniforms::ValueInt &bv = *(GlobalShaderUniforms::ValueInt *)&global_shader_uniforms.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: {
GlobalShaderUniforms::ValueUInt &bv = *(GlobalShaderUniforms::ValueUInt *)&global_shader_uniforms.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: {
GlobalShaderUniforms::ValueUInt &bv = *(GlobalShaderUniforms::ValueUInt *)&global_shader_uniforms.buffer_values[p_index];
Vector2i v = convert_to_vector<Vector2i>(p_value);
bv.x = v.x;
bv.y = v.y;
bv.z = 0;
bv.w = 0;
} break;
case RS::GLOBAL_VAR_TYPE_UVEC3: {
GlobalShaderUniforms::ValueUInt &bv = *(GlobalShaderUniforms::ValueUInt *)&global_shader_uniforms.buffer_values[p_index];
Vector3i v = convert_to_vector<Vector3i>(p_value);
bv.x = v.x;
bv.y = v.y;
bv.z = v.z;
bv.w = 0;
} break;
case RS::GLOBAL_VAR_TYPE_UVEC4: {
GlobalShaderUniforms::ValueUInt &bv = *(GlobalShaderUniforms::ValueUInt *)&global_shader_uniforms.buffer_values[p_index];
Vector4i v = convert_to_vector<Vector4i>(p_value);
bv.x = v.x;
bv.y = v.y;
bv.z = v.z;
bv.w = v.w;
} break;
case RS::GLOBAL_VAR_TYPE_FLOAT: {
GlobalShaderUniforms::Value &bv = global_shader_uniforms.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: {
GlobalShaderUniforms::Value &bv = global_shader_uniforms.buffer_values[p_index];
Vector2 v = convert_to_vector<Vector2>(p_value);
bv.x = v.x;
bv.y = v.y;
bv.z = 0;
bv.w = 0;
} break;
case RS::GLOBAL_VAR_TYPE_VEC3: {
GlobalShaderUniforms::Value &bv = global_shader_uniforms.buffer_values[p_index];
Vector3 v = convert_to_vector<Vector3>(p_value);
bv.x = v.x;
bv.y = v.y;
bv.z = v.z;
bv.w = 0;
} break;
case RS::GLOBAL_VAR_TYPE_VEC4: {
GlobalShaderUniforms::Value &bv = global_shader_uniforms.buffer_values[p_index];
Vector4 v = convert_to_vector<Vector4>(p_value);
bv.x = v.x;
bv.y = v.y;
bv.z = v.z;
bv.w = v.w;
} break;
case RS::GLOBAL_VAR_TYPE_COLOR: {
GlobalShaderUniforms::Value &bv = global_shader_uniforms.buffer_values[p_index];
Color v = p_value;
bv.x = v.r;
bv.y = v.g;
bv.z = v.b;
bv.w = v.a;
GlobalShaderUniforms::Value &bv_linear = global_shader_uniforms.buffer_values[p_index + 1];
//v = v.srgb_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: {
GlobalShaderUniforms::Value &bv = global_shader_uniforms.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: {
GlobalShaderUniforms::Value *bv = &global_shader_uniforms.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: {
GlobalShaderUniforms::Value *bv = &global_shader_uniforms.buffer_values[p_index];
Basis v = p_value;
convert_item_std140<Basis>(v, &bv->x);
} break;
case RS::GLOBAL_VAR_TYPE_MAT4: {
GlobalShaderUniforms::Value *bv = &global_shader_uniforms.buffer_values[p_index];
Projection m = p_value;
convert_item_std140<Projection>(m, &bv->x);
} break;
case RS::GLOBAL_VAR_TYPE_TRANSFORM_2D: {
GlobalShaderUniforms::Value *bv = &global_shader_uniforms.buffer_values[p_index];
Transform2D v = p_value;
convert_item_std140<Transform2D>(v, &bv->x);
} break;
case RS::GLOBAL_VAR_TYPE_TRANSFORM: {
GlobalShaderUniforms::Value *bv = &global_shader_uniforms.buffer_values[p_index];
Transform3D v = p_value;
convert_item_std140<Transform3D>(v, &bv->x);
} break;
default: {
ERR_FAIL();
}
}
}
void MaterialStorage::_global_shader_uniform_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) / GlobalShaderUniforms::BUFFER_DIRTY_REGION_SIZE;
if (chunk != prev_chunk) {
if (!global_shader_uniforms.buffer_dirty_regions[chunk]) {
global_shader_uniforms.buffer_dirty_regions[chunk] = true;
global_shader_uniforms.buffer_dirty_region_count++;
}
}
prev_chunk = chunk;
}
}
void MaterialStorage::global_shader_parameter_add(const StringName &p_name, RS::GlobalShaderParameterType p_type, const Variant &p_value) {
ERR_FAIL_COND(global_shader_uniforms.variables.has(p_name));
GlobalShaderUniforms::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_shader_uniforms.must_update_texture_materials = true; //normally there are none
} else {
gv.buffer_elements = 1;
if (p_type == RS::GLOBAL_VAR_TYPE_COLOR || p_type == RS::GLOBAL_VAR_TYPE_MAT2) {
//color needs to elements to store srgb and linear
gv.buffer_elements = 2;
}
if (p_type == RS::GLOBAL_VAR_TYPE_MAT3 || p_type == RS::GLOBAL_VAR_TYPE_TRANSFORM_2D) {
//color needs to elements to store srgb and linear
gv.buffer_elements = 3;
}
if (p_type == RS::GLOBAL_VAR_TYPE_MAT4 || p_type == RS::GLOBAL_VAR_TYPE_TRANSFORM) {
//color needs to elements to store srgb and linear
gv.buffer_elements = 4;
}
//is vector, allocate in buffer and update index
gv.buffer_index = _global_shader_uniform_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_shader_uniforms.buffer_usage[gv.buffer_index].elements = gv.buffer_elements;
_global_shader_uniform_store_in_buffer(gv.buffer_index, gv.type, gv.value);
_global_shader_uniform_mark_buffer_dirty(gv.buffer_index, gv.buffer_elements);
global_shader_uniforms.must_update_buffer_materials = true; //normally there are none
}
global_shader_uniforms.variables[p_name] = gv;
}
void MaterialStorage::global_shader_parameter_remove(const StringName &p_name) {
if (!global_shader_uniforms.variables.has(p_name)) {
return;
}
GlobalShaderUniforms::Variable &gv = global_shader_uniforms.variables[p_name];
if (gv.buffer_index >= 0) {
global_shader_uniforms.buffer_usage[gv.buffer_index].elements = 0;
global_shader_uniforms.must_update_buffer_materials = true;
} else {
global_shader_uniforms.must_update_texture_materials = true;
}
global_shader_uniforms.variables.erase(p_name);
}
Vector<StringName> MaterialStorage::global_shader_parameter_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.");
}
Vector<StringName> names;
for (const KeyValue<StringName, GlobalShaderUniforms::Variable> &E : global_shader_uniforms.variables) {
names.push_back(E.key);
}
names.sort_custom<StringName::AlphCompare>();
return names;
}
void MaterialStorage::global_shader_parameter_set(const StringName &p_name, const Variant &p_value) {
ERR_FAIL_COND(!global_shader_uniforms.variables.has(p_name));
GlobalShaderUniforms::Variable &gv = global_shader_uniforms.variables[p_name];
gv.value = p_value;
if (gv.override.get_type() == Variant::NIL) {
if (gv.buffer_index >= 0) {
//buffer
_global_shader_uniform_store_in_buffer(gv.buffer_index, gv.type, gv.value);
_global_shader_uniform_mark_buffer_dirty(gv.buffer_index, gv.buffer_elements);
} else {
//texture
MaterialStorage *material_storage = MaterialStorage::get_singleton();
for (const RID &E : gv.texture_materials) {
Material *material = material_storage->get_material(E);
ERR_CONTINUE(!material);
material_storage->_material_queue_update(material, false, true);
}
}
}
}
void MaterialStorage::global_shader_parameter_set_override(const StringName &p_name, const Variant &p_value) {
if (!global_shader_uniforms.variables.has(p_name)) {
return; //variable may not exist
}
ERR_FAIL_COND(p_value.get_type() == Variant::OBJECT);
GlobalShaderUniforms::Variable &gv = global_shader_uniforms.variables[p_name];
gv.override = p_value;
if (gv.buffer_index >= 0) {
//buffer
if (gv.override.get_type() == Variant::NIL) {
_global_shader_uniform_store_in_buffer(gv.buffer_index, gv.type, gv.value);
} else {
_global_shader_uniform_store_in_buffer(gv.buffer_index, gv.type, gv.override);
}
_global_shader_uniform_mark_buffer_dirty(gv.buffer_index, gv.buffer_elements);
} else {
//texture
MaterialStorage *material_storage = MaterialStorage::get_singleton();
for (const RID &E : gv.texture_materials) {
Material *material = material_storage->get_material(E);
ERR_CONTINUE(!material);
material_storage->_material_queue_update(material, false, true);
}
}
}
Variant MaterialStorage::global_shader_parameter_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_shader_uniforms.variables.has(p_name)) {
return Variant();
}
return global_shader_uniforms.variables[p_name].value;
}
RS::GlobalShaderParameterType MaterialStorage::global_shader_parameter_get_type_internal(const StringName &p_name) const {
if (!global_shader_uniforms.variables.has(p_name)) {
return RS::GLOBAL_VAR_TYPE_MAX;
}
return global_shader_uniforms.variables[p_name].type;
}
RS::GlobalShaderParameterType MaterialStorage::global_shader_parameter_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_shader_parameter_get_type_internal(p_name);
}
void MaterialStorage::global_shader_parameters_load_settings(bool p_load_textures) {
List<PropertyInfo> settings;
ProjectSettings::get_singleton()->get_property_list(&settings);
for (const PropertyInfo &E : settings) {
if (E.name.begins_with("shader_globals/")) {
StringName name = E.name.get_slice("/", 1);
Dictionary d = GLOBAL_GET(E.name);
ERR_CONTINUE(!d.has("type"));
ERR_CONTINUE(!d.has("value"));
String type = d["type"];
static const char *global_var_type_names[RS::GLOBAL_VAR_TYPE_MAX] = {
"bool",
"bvec2",
"bvec3",
"bvec4",
"int",
"ivec2",
"ivec3",
"ivec4",
"rect2i",
"uint",
"uvec2",
"uvec3",
"uvec4",
"float",
"vec2",
"vec3",
"vec4",
"color",
"rect2",
"mat2",
"mat3",
"mat4",
"transform_2d",
"transform",
"sampler2D",
"sampler2DArray",
"sampler3D",
"samplerCube",
};
RS::GlobalShaderParameterType 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::GlobalShaderParameterType(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;
Ref<Resource> resource = ResourceLoader::load(path);
ERR_CONTINUE(resource.is_null());
value = resource;
}
if (global_shader_uniforms.variables.has(name)) {
//has it, update it
global_shader_parameter_set(name, value);
} else {
global_shader_parameter_add(name, gvtype, value);
}
}
}
}
void MaterialStorage::global_shader_parameters_clear() {
global_shader_uniforms.variables.clear();
}
GLuint MaterialStorage::global_shader_parameters_get_uniform_buffer() const {
return global_shader_uniforms.buffer;
}
int32_t MaterialStorage::global_shader_parameters_instance_allocate(RID p_instance) {
ERR_FAIL_COND_V(global_shader_uniforms.instance_buffer_pos.has(p_instance), -1);
int32_t pos = _global_shader_uniform_allocate(ShaderLanguage::MAX_INSTANCE_UNIFORM_INDICES);
global_shader_uniforms.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_shader_uniforms.buffer_usage[pos].elements = ShaderLanguage::MAX_INSTANCE_UNIFORM_INDICES;
return pos;
}
void MaterialStorage::global_shader_parameters_instance_free(RID p_instance) {
ERR_FAIL_COND(!global_shader_uniforms.instance_buffer_pos.has(p_instance));
int32_t pos = global_shader_uniforms.instance_buffer_pos[p_instance];
if (pos >= 0) {
global_shader_uniforms.buffer_usage[pos].elements = 0;
}
global_shader_uniforms.instance_buffer_pos.erase(p_instance);
}
void MaterialStorage::global_shader_parameters_instance_update(RID p_instance, int p_index, const Variant &p_value, int p_flags_count) {
if (!global_shader_uniforms.instance_buffer_pos.has(p_instance)) {
return; //just not allocated, ignore
}
int32_t pos = global_shader_uniforms.instance_buffer_pos[p_instance];
if (pos < 0) {
return; //again, not allocated, ignore
}
ERR_FAIL_INDEX(p_index, ShaderLanguage::MAX_INSTANCE_UNIFORM_INDICES);
Variant::Type value_type = p_value.get_type();
ERR_FAIL_COND_MSG(p_value.get_type() > Variant::COLOR, "Unsupported variant type for instance parameter: " + Variant::get_type_name(value_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, //vec4
ShaderLanguage::TYPE_IVEC4, //vec4i
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_MAX, //projection not supported here
ShaderLanguage::TYPE_VEC4 //color
};
ShaderLanguage::DataType datatype = ShaderLanguage::TYPE_MAX;
if (value_type == Variant::INT && p_flags_count > 0) {
switch (p_flags_count) {
case 1:
datatype = ShaderLanguage::TYPE_BVEC2;
break;
case 2:
datatype = ShaderLanguage::TYPE_BVEC3;
break;
case 3:
datatype = ShaderLanguage::TYPE_BVEC4;
break;
}
} else {
datatype = datatype_from_value[value_type];
}
ERR_FAIL_COND_MSG(datatype == ShaderLanguage::TYPE_MAX, "Unsupported variant type for instance parameter: " + Variant::get_type_name(value_type)); //anything greater not supported
pos += p_index;
_fill_std140_variant_ubo_value(datatype, 0, p_value, (uint8_t *)&global_shader_uniforms.buffer_values[pos]);
_global_shader_uniform_mark_buffer_dirty(pos, 1);
}
void MaterialStorage::_update_global_shader_uniforms() {
MaterialStorage *material_storage = MaterialStorage::get_singleton();
if (global_shader_uniforms.buffer_dirty_region_count > 0) {
uint32_t total_regions = global_shader_uniforms.buffer_size / GlobalShaderUniforms::BUFFER_DIRTY_REGION_SIZE;
if (total_regions / global_shader_uniforms.buffer_dirty_region_count <= 4) {
// 25% of regions dirty, just update all buffer
glBindBuffer(GL_UNIFORM_BUFFER, global_shader_uniforms.buffer);
glBufferData(GL_UNIFORM_BUFFER, sizeof(GlobalShaderUniforms::Value) * global_shader_uniforms.buffer_size, global_shader_uniforms.buffer_values, GL_DYNAMIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
memset(global_shader_uniforms.buffer_dirty_regions, 0, sizeof(bool) * total_regions);
} else {
uint32_t region_byte_size = sizeof(GlobalShaderUniforms::Value) * GlobalShaderUniforms::BUFFER_DIRTY_REGION_SIZE;
glBindBuffer(GL_UNIFORM_BUFFER, global_shader_uniforms.buffer);
for (uint32_t i = 0; i < total_regions; i++) {
if (global_shader_uniforms.buffer_dirty_regions[i]) {
glBufferSubData(GL_UNIFORM_BUFFER, i * region_byte_size, region_byte_size, &global_shader_uniforms.buffer_values[i * GlobalShaderUniforms::BUFFER_DIRTY_REGION_SIZE]);
global_shader_uniforms.buffer_dirty_regions[i] = false;
}
}
glBindBuffer(GL_UNIFORM_BUFFER, 0);
}
global_shader_uniforms.buffer_dirty_region_count = 0;
}
if (global_shader_uniforms.must_update_buffer_materials) {
// only happens in the case of a buffer variable added or removed,
// so not often.
for (const RID &E : global_shader_uniforms.materials_using_buffer) {
Material *material = material_storage->get_material(E);
ERR_CONTINUE(!material); //wtf
material_storage->_material_queue_update(material, true, false);
}
global_shader_uniforms.must_update_buffer_materials = false;
}
if (global_shader_uniforms.must_update_texture_materials) {
// only happens in the case of a buffer variable added or removed,
// so not often.
for (const RID &E : global_shader_uniforms.materials_using_texture) {
Material *material = material_storage->get_material(E);
ERR_CONTINUE(!material); //wtf
material_storage->_material_queue_update(material, false, true);
}
global_shader_uniforms.must_update_texture_materials = false;
}
}
/* SHADER API */
RID MaterialStorage::shader_allocate() {
return shader_owner.allocate_rid();
}
void MaterialStorage::shader_initialize(RID p_rid) {
Shader shader;
shader.data = nullptr;
shader.mode = RS::SHADER_MAX;
shader_owner.initialize_rid(p_rid, shader);
}
void MaterialStorage::shader_free(RID p_rid) {
GLES3::Shader *shader = shader_owner.get_or_null(p_rid);
ERR_FAIL_COND(!shader);
//make material unreference this
while (shader->owners.size()) {
material_set_shader((*shader->owners.begin())->self, RID());
}
//clear data if exists
if (shader->data) {
memdelete(shader->data);
}
shader_owner.free(p_rid);
}
void MaterialStorage::shader_set_code(RID p_shader, const String &p_code) {
GLES3::Shader *shader = shader_owner.get_or_null(p_shader);
ERR_FAIL_COND(!shader);
shader->code = p_code;
String mode_string = ShaderLanguage::get_shader_type(p_code);
RS::ShaderMode new_mode;
if (mode_string == "canvas_item") {
new_mode = RS::SHADER_CANVAS_ITEM;
} else if (mode_string == "particles") {
new_mode = RS::SHADER_PARTICLES;
} else if (mode_string == "spatial") {
new_mode = RS::SHADER_SPATIAL;
} else if (mode_string == "sky") {
new_mode = RS::SHADER_SKY;
//} else if (mode_string == "fog") {
// new_mode = RS::SHADER_FOG;
} else {
new_mode = RS::SHADER_MAX;
ERR_PRINT("shader type " + mode_string + " not supported in OpenGL renderer");
}
if (new_mode != shader->mode) {
if (shader->data) {
memdelete(shader->data);
shader->data = nullptr;
}
for (Material *E : shader->owners) {
Material *material = E;
material->shader_mode = new_mode;
if (material->data) {
memdelete(material->data);
material->data = nullptr;
}
}
shader->mode = new_mode;
if (new_mode < RS::SHADER_MAX && shader_data_request_func[new_mode]) {
shader->data = shader_data_request_func[new_mode]();
} else {
shader->mode = RS::SHADER_MAX; //invalid
}
for (Material *E : shader->owners) {
Material *material = E;
if (shader->data) {
material->data = material_data_request_func[new_mode](shader->data);
material->data->self = material->self;
material->data->set_next_pass(material->next_pass);
material->data->set_render_priority(material->priority);
}
material->shader_mode = new_mode;
}
if (shader->data) {
for (const KeyValue<StringName, HashMap<int, RID>> &E : shader->default_texture_parameter) {
for (const KeyValue<int, RID> &E2 : E.value) {
shader->data->set_default_texture_parameter(E.key, E2.value, E2.key);
}
}
}
}
if (shader->data) {
shader->data->set_code(p_code);
}
for (Material *E : shader->owners) {
Material *material = E;
material->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MATERIAL);
_material_queue_update(material, true, true);
}
}
void MaterialStorage::shader_set_path_hint(RID p_shader, const String &p_path) {
GLES3::Shader *shader = shader_owner.get_or_null(p_shader);
ERR_FAIL_COND(!shader);
shader->path_hint = p_path;
if (shader->data) {
shader->data->set_path_hint(p_path);
}
}
String MaterialStorage::shader_get_code(RID p_shader) const {
const GLES3::Shader *shader = shader_owner.get_or_null(p_shader);
ERR_FAIL_COND_V(!shader, String());
return shader->code;
}
void MaterialStorage::get_shader_parameter_list(RID p_shader, List<PropertyInfo> *p_param_list) const {
GLES3::Shader *shader = shader_owner.get_or_null(p_shader);
ERR_FAIL_COND(!shader);
if (shader->data) {
return shader->data->get_shader_uniform_list(p_param_list);
}
}
void MaterialStorage::shader_set_default_texture_parameter(RID p_shader, const StringName &p_name, RID p_texture, int p_index) {
GLES3::Shader *shader = shader_owner.get_or_null(p_shader);
ERR_FAIL_COND(!shader);
if (p_texture.is_valid() && TextureStorage::get_singleton()->owns_texture(p_texture)) {
if (!shader->default_texture_parameter.has(p_name)) {
shader->default_texture_parameter[p_name] = HashMap<int, RID>();
}
shader->default_texture_parameter[p_name][p_index] = p_texture;
} else {
if (shader->default_texture_parameter.has(p_name) && shader->default_texture_parameter[p_name].has(p_index)) {
shader->default_texture_parameter[p_name].erase(p_index);
if (shader->default_texture_parameter[p_name].is_empty()) {
shader->default_texture_parameter.erase(p_name);
}
}
}
if (shader->data) {
shader->data->set_default_texture_parameter(p_name, p_texture, p_index);
}
for (Material *E : shader->owners) {
Material *material = E;
_material_queue_update(material, false, true);
}
}
RID MaterialStorage::shader_get_default_texture_parameter(RID p_shader, const StringName &p_name, int p_index) const {
const GLES3::Shader *shader = shader_owner.get_or_null(p_shader);
ERR_FAIL_COND_V(!shader, RID());
if (shader->default_texture_parameter.has(p_name) && shader->default_texture_parameter[p_name].has(p_index)) {
return shader->default_texture_parameter[p_name][p_index];
}
return RID();
}
Variant MaterialStorage::shader_get_parameter_default(RID p_shader, const StringName &p_param) const {
Shader *shader = shader_owner.get_or_null(p_shader);
ERR_FAIL_COND_V(!shader, Variant());
if (shader->data) {
return shader->data->get_default_parameter(p_param);
}
return Variant();
}
RS::ShaderNativeSourceCode MaterialStorage::shader_get_native_source_code(RID p_shader) const {
Shader *shader = shader_owner.get_or_null(p_shader);
ERR_FAIL_COND_V(!shader, RS::ShaderNativeSourceCode());
if (shader->data) {
return shader->data->get_native_source_code();
}
return RS::ShaderNativeSourceCode();
}
/* MATERIAL API */
void MaterialStorage::_material_queue_update(GLES3::Material *material, bool p_uniform, bool p_texture) {
material->uniform_dirty = material->uniform_dirty || p_uniform;
material->texture_dirty = material->texture_dirty || p_texture;
if (material->update_element.in_list()) {
return;
}
material_update_list.add(&material->update_element);
}
void MaterialStorage::_update_queued_materials() {
while (material_update_list.first()) {
Material *material = material_update_list.first()->self();
if (material->data) {
material->data->update_parameters(material->params, material->uniform_dirty, material->texture_dirty);
}
material->texture_dirty = false;
material->uniform_dirty = false;
material_update_list.remove(&material->update_element);
}
}
RID MaterialStorage::material_allocate() {
return material_owner.allocate_rid();
}
void MaterialStorage::material_initialize(RID p_rid) {
material_owner.initialize_rid(p_rid);
Material *material = material_owner.get_or_null(p_rid);
material->self = p_rid;
}
void MaterialStorage::material_free(RID p_rid) {
Material *material = material_owner.get_or_null(p_rid);
ERR_FAIL_COND(!material);
// Need to clear texture arrays to prevent spin locking of their RID's.
// This happens when the app is being closed.
for (KeyValue<StringName, Variant> &E : material->params) {
if (E.value.get_type() == Variant::ARRAY) {
Array(E.value).clear();
}
}
material_set_shader(p_rid, RID()); //clean up shader
material->dependency.deleted_notify(p_rid);
material_owner.free(p_rid);
}
void MaterialStorage::material_set_shader(RID p_material, RID p_shader) {
GLES3::Material *material = material_owner.get_or_null(p_material);
ERR_FAIL_COND(!material);
if (material->data) {
memdelete(material->data);
material->data = nullptr;
}
if (material->shader) {
material->shader->owners.erase(material);
material->shader = nullptr;
material->shader_mode = RS::SHADER_MAX;
}
if (p_shader.is_null()) {
material->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MATERIAL);
material->shader_id = 0;
return;
}
Shader *shader = get_shader(p_shader);
ERR_FAIL_COND(!shader);
material->shader = shader;
material->shader_mode = shader->mode;
material->shader_id = p_shader.get_local_index();
shader->owners.insert(material);
if (shader->mode == RS::SHADER_MAX) {
return;
}
ERR_FAIL_COND(shader->data == nullptr);
material->data = material_data_request_func[shader->mode](shader->data);
material->data->self = p_material;
material->data->set_next_pass(material->next_pass);
material->data->set_render_priority(material->priority);
//updating happens later
material->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MATERIAL);
_material_queue_update(material, true, true);
}
void MaterialStorage::material_set_param(RID p_material, const StringName &p_param, const Variant &p_value) {
GLES3::Material *material = material_owner.get_or_null(p_material);
ERR_FAIL_COND(!material);
if (p_value.get_type() == Variant::NIL) {
material->params.erase(p_param);
} else {
ERR_FAIL_COND(p_value.get_type() == Variant::OBJECT); //object not allowed
material->params[p_param] = p_value;
}
if (material->shader && material->shader->data) { //shader is valid
bool is_texture = material->shader->data->is_parameter_texture(p_param);
_material_queue_update(material, !is_texture, is_texture);
} else {
_material_queue_update(material, true, true);
}
}
Variant MaterialStorage::material_get_param(RID p_material, const StringName &p_param) const {
const GLES3::Material *material = material_owner.get_or_null(p_material);
ERR_FAIL_COND_V(!material, Variant());
if (material->params.has(p_param)) {
return material->params[p_param];
} else {
return Variant();
}
}
void MaterialStorage::material_set_next_pass(RID p_material, RID p_next_material) {
GLES3::Material *material = material_owner.get_or_null(p_material);
ERR_FAIL_COND(!material);
if (material->next_pass == p_next_material) {
return;
}
material->next_pass = p_next_material;
if (material->data) {
material->data->set_next_pass(p_next_material);
}
material->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MATERIAL);
}
void MaterialStorage::material_set_render_priority(RID p_material, int priority) {
ERR_FAIL_COND(priority < RS::MATERIAL_RENDER_PRIORITY_MIN);
ERR_FAIL_COND(priority > RS::MATERIAL_RENDER_PRIORITY_MAX);
GLES3::Material *material = material_owner.get_or_null(p_material);
ERR_FAIL_COND(!material);
material->priority = priority;
if (material->data) {
material->data->set_render_priority(priority);
}
material->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MATERIAL);
}
bool MaterialStorage::material_is_animated(RID p_material) {
GLES3::Material *material = material_owner.get_or_null(p_material);
ERR_FAIL_COND_V(!material, false);
if (material->shader && material->shader->data) {
if (material->shader->data->is_animated()) {
return true;
} else if (material->next_pass.is_valid()) {
return material_is_animated(material->next_pass);
}
}
return false; //by default nothing is animated
}
bool MaterialStorage::material_casts_shadows(RID p_material) {
GLES3::Material *material = material_owner.get_or_null(p_material);
ERR_FAIL_COND_V(!material, true);
if (material->shader && material->shader->data) {
if (material->shader->data->casts_shadows()) {
return true;
} else if (material->next_pass.is_valid()) {
return material_casts_shadows(material->next_pass);
}
}
return true; //by default everything casts shadows
}
void MaterialStorage::material_get_instance_shader_parameters(RID p_material, List<InstanceShaderParam> *r_parameters) {
GLES3::Material *material = material_owner.get_or_null(p_material);
ERR_FAIL_COND(!material);
if (material->shader && material->shader->data) {
material->shader->data->get_instance_param_list(r_parameters);
if (material->next_pass.is_valid()) {
material_get_instance_shader_parameters(material->next_pass, r_parameters);
}
}
}
void MaterialStorage::material_update_dependency(RID p_material, DependencyTracker *p_instance) {
Material *material = material_owner.get_or_null(p_material);
ERR_FAIL_COND(!material);
p_instance->update_dependency(&material->dependency);
if (material->next_pass.is_valid()) {
material_update_dependency(material->next_pass, p_instance);
}
}
LocalVector<ShaderGLES3::TextureUniformData> get_texture_uniform_data(const Vector<ShaderCompiler::GeneratedCode::Texture> &texture_uniforms) {
LocalVector<ShaderGLES3::TextureUniformData> texture_uniform_data;
for (int i = 0; i < texture_uniforms.size(); i++) {
int num_textures = texture_uniforms[i].array_size;
if (num_textures == 0) {
num_textures = 1;
}
texture_uniform_data.push_back({ texture_uniforms[i].name, num_textures });
}
return texture_uniform_data;
}
/* Canvas Shader Data */
void CanvasShaderData::set_code(const String &p_code) {
// compile the shader
code = p_code;
valid = false;
ubo_size = 0;
uniforms.clear();
uses_screen_texture = false;
uses_screen_texture_mipmaps = false;
uses_sdf = false;
uses_time = false;
if (code.is_empty()) {
return; //just invalid, but no error
}
ShaderCompiler::GeneratedCode gen_code;
int blend_modei = BLEND_MODE_MIX;
ShaderCompiler::IdentifierActions actions;
actions.entry_point_stages["vertex"] = ShaderCompiler::STAGE_VERTEX;
actions.entry_point_stages["fragment"] = ShaderCompiler::STAGE_FRAGMENT;
actions.entry_point_stages["light"] = ShaderCompiler::STAGE_FRAGMENT;
actions.render_mode_values["blend_add"] = Pair<int *, int>(&blend_modei, BLEND_MODE_ADD);
actions.render_mode_values["blend_mix"] = Pair<int *, int>(&blend_modei, BLEND_MODE_MIX);
actions.render_mode_values["blend_sub"] = Pair<int *, int>(&blend_modei, BLEND_MODE_SUB);
actions.render_mode_values["blend_mul"] = Pair<int *, int>(&blend_modei, BLEND_MODE_MUL);
actions.render_mode_values["blend_premul_alpha"] = Pair<int *, int>(&blend_modei, BLEND_MODE_PMALPHA);
actions.render_mode_values["blend_disabled"] = Pair<int *, int>(&blend_modei, BLEND_MODE_DISABLED);
actions.usage_flag_pointers["texture_sdf"] = &uses_sdf;
actions.usage_flag_pointers["TIME"] = &uses_time;
actions.uniforms = &uniforms;
Error err = MaterialStorage::get_singleton()->shaders.compiler_canvas.compile(RS::SHADER_CANVAS_ITEM, code, &actions, path, gen_code);
ERR_FAIL_COND_MSG(err != OK, "Shader compilation failed.");
if (version.is_null()) {
version = MaterialStorage::get_singleton()->shaders.canvas_shader.version_create();
}
blend_mode = BlendMode(blend_modei);
uses_screen_texture_mipmaps = gen_code.uses_screen_texture_mipmaps;
uses_screen_texture = gen_code.uses_screen_texture;
#if 0
print_line("**compiling shader:");
print_line("**defines:\n");
for (int i = 0; i < gen_code.defines.size(); i++) {
print_line(gen_code.defines[i]);
}
HashMap<String, String>::Iterator el = gen_code.code.begin();
while (el) {
print_line("\n**code " + el->key + ":\n" + el->value);
++el;
}
print_line("\n**uniforms:\n" + gen_code.uniforms);
print_line("\n**vertex_globals:\n" + gen_code.stage_globals[ShaderCompiler::STAGE_VERTEX]);
print_line("\n**fragment_globals:\n" + gen_code.stage_globals[ShaderCompiler::STAGE_FRAGMENT]);
#endif
LocalVector<ShaderGLES3::TextureUniformData> texture_uniform_data = get_texture_uniform_data(gen_code.texture_uniforms);
MaterialStorage::get_singleton()->shaders.canvas_shader.version_set_code(version, gen_code.code, gen_code.uniforms, gen_code.stage_globals[ShaderCompiler::STAGE_VERTEX], gen_code.stage_globals[ShaderCompiler::STAGE_FRAGMENT], gen_code.defines, texture_uniform_data);
ERR_FAIL_COND(!MaterialStorage::get_singleton()->shaders.canvas_shader.version_is_valid(version));
ubo_size = gen_code.uniform_total_size;
ubo_offsets = gen_code.uniform_offsets;
texture_uniforms = gen_code.texture_uniforms;
valid = true;
}
bool CanvasShaderData::is_animated() const {
return false;
}
bool CanvasShaderData::casts_shadows() const {
return false;
}
RS::ShaderNativeSourceCode CanvasShaderData::get_native_source_code() const {
return MaterialStorage::get_singleton()->shaders.canvas_shader.version_get_native_source_code(version);
}
CanvasShaderData::CanvasShaderData() {
valid = false;
uses_screen_texture = false;
uses_sdf = false;
}
CanvasShaderData::~CanvasShaderData() {
if (version.is_valid()) {
MaterialStorage::get_singleton()->shaders.canvas_shader.version_free(version);
}
}
GLES3::ShaderData *GLES3::_create_canvas_shader_func() {
CanvasShaderData *shader_data = memnew(CanvasShaderData);
return shader_data;
}
void CanvasMaterialData::update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) {
update_parameters_internal(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, false);
}
static void bind_uniforms_generic(const Vector<RID> &p_textures, const Vector<ShaderCompiler::GeneratedCode::Texture> &p_texture_uniforms, int texture_offset = 0, const RS::CanvasItemTextureFilter *filter_mapping = filter_from_uniform, const RS::CanvasItemTextureRepeat *repeat_mapping = repeat_from_uniform) {
const RID *textures = p_textures.ptr();
const ShaderCompiler::GeneratedCode::Texture *texture_uniforms = p_texture_uniforms.ptr();
int texture_uniform_index = 0;
int texture_uniform_count = 0;
for (int ti = 0; ti < p_textures.size(); ti++) {
ERR_FAIL_COND_MSG(texture_uniform_index >= p_texture_uniforms.size(), "texture_uniform_index out of bounds");
GLES3::Texture *texture = TextureStorage::get_singleton()->get_texture(textures[ti]);
const ShaderCompiler::GeneratedCode::Texture &texture_uniform = texture_uniforms[texture_uniform_index];
if (texture) {
glActiveTexture(GL_TEXTURE0 + texture_offset + ti);
glBindTexture(target_from_type[texture_uniform.type], texture->tex_id);
if (texture->render_target) {
texture->render_target->used_in_frame = true;
}
texture->gl_set_filter(filter_mapping[int(texture_uniform.filter)]);
texture->gl_set_repeat(repeat_mapping[int(texture_uniform.repeat)]);
}
texture_uniform_count++;
if (texture_uniform_count >= texture_uniform.array_size) {
texture_uniform_index++;
texture_uniform_count = 0;
}
}
}
void CanvasMaterialData::bind_uniforms() {
// Bind Material Uniforms
glBindBufferBase(GL_UNIFORM_BUFFER, RasterizerCanvasGLES3::MATERIAL_UNIFORM_LOCATION, uniform_buffer);
bind_uniforms_generic(texture_cache, shader_data->texture_uniforms, 1, filter_from_uniform_canvas, repeat_from_uniform_canvas); // Start at GL_TEXTURE1 because texture slot 0 is used by the base texture
}
CanvasMaterialData::~CanvasMaterialData() {
}
GLES3::MaterialData *GLES3::_create_canvas_material_func(ShaderData *p_shader) {
CanvasMaterialData *material_data = memnew(CanvasMaterialData);
material_data->shader_data = static_cast<CanvasShaderData *>(p_shader);
//update will happen later anyway so do nothing.
return material_data;
}
////////////////////////////////////////////////////////////////////////////////
// SKY SHADER
void SkyShaderData::set_code(const String &p_code) {
//compile
code = p_code;
valid = false;
ubo_size = 0;
uniforms.clear();
if (code.is_empty()) {
return; //just invalid, but no error
}
ShaderCompiler::GeneratedCode gen_code;
ShaderCompiler::IdentifierActions actions;
actions.entry_point_stages["sky"] = ShaderCompiler::STAGE_FRAGMENT;
uses_time = false;
uses_half_res = false;
uses_quarter_res = false;
uses_position = false;
uses_light = false;
actions.render_mode_flags["use_half_res_pass"] = &uses_half_res;
actions.render_mode_flags["use_quarter_res_pass"] = &uses_quarter_res;
actions.usage_flag_pointers["TIME"] = &uses_time;
actions.usage_flag_pointers["POSITION"] = &uses_position;
actions.usage_flag_pointers["LIGHT0_ENABLED"] = &uses_light;
actions.usage_flag_pointers["LIGHT0_ENERGY"] = &uses_light;
actions.usage_flag_pointers["LIGHT0_DIRECTION"] = &uses_light;
actions.usage_flag_pointers["LIGHT0_COLOR"] = &uses_light;
actions.usage_flag_pointers["LIGHT0_SIZE"] = &uses_light;
actions.usage_flag_pointers["LIGHT1_ENABLED"] = &uses_light;
actions.usage_flag_pointers["LIGHT1_ENERGY"] = &uses_light;
actions.usage_flag_pointers["LIGHT1_DIRECTION"] = &uses_light;
actions.usage_flag_pointers["LIGHT1_COLOR"] = &uses_light;
actions.usage_flag_pointers["LIGHT1_SIZE"] = &uses_light;
actions.usage_flag_pointers["LIGHT2_ENABLED"] = &uses_light;
actions.usage_flag_pointers["LIGHT2_ENERGY"] = &uses_light;
actions.usage_flag_pointers["LIGHT2_DIRECTION"] = &uses_light;
actions.usage_flag_pointers["LIGHT2_COLOR"] = &uses_light;
actions.usage_flag_pointers["LIGHT2_SIZE"] = &uses_light;
actions.usage_flag_pointers["LIGHT3_ENABLED"] = &uses_light;
actions.usage_flag_pointers["LIGHT3_ENERGY"] = &uses_light;
actions.usage_flag_pointers["LIGHT3_DIRECTION"] = &uses_light;
actions.usage_flag_pointers["LIGHT3_COLOR"] = &uses_light;
actions.usage_flag_pointers["LIGHT3_SIZE"] = &uses_light;
actions.uniforms = &uniforms;
Error err = MaterialStorage::get_singleton()->shaders.compiler_sky.compile(RS::SHADER_SKY, code, &actions, path, gen_code);
ERR_FAIL_COND_MSG(err != OK, "Shader compilation failed.");
if (version.is_null()) {
version = MaterialStorage::get_singleton()->shaders.sky_shader.version_create();
}
#if 0
print_line("**compiling shader:");
print_line("**defines:\n");
for (int i = 0; i < gen_code.defines.size(); i++) {
print_line(gen_code.defines[i]);
}
HashMap<String, String>::Iterator el = gen_code.code.begin();
while (el) {
print_line("\n**code " + el->key + ":\n" + el->value);
++el;
}
print_line("\n**uniforms:\n" + gen_code.uniforms);
print_line("\n**vertex_globals:\n" + gen_code.stage_globals[ShaderCompiler::STAGE_VERTEX]);
print_line("\n**fragment_globals:\n" + gen_code.stage_globals[ShaderCompiler::STAGE_FRAGMENT]);
#endif
LocalVector<ShaderGLES3::TextureUniformData> texture_uniform_data = get_texture_uniform_data(gen_code.texture_uniforms);
MaterialStorage::get_singleton()->shaders.sky_shader.version_set_code(version, gen_code.code, gen_code.uniforms, gen_code.stage_globals[ShaderCompiler::STAGE_VERTEX], gen_code.stage_globals[ShaderCompiler::STAGE_FRAGMENT], gen_code.defines, texture_uniform_data);
ERR_FAIL_COND(!MaterialStorage::get_singleton()->shaders.sky_shader.version_is_valid(version));
ubo_size = gen_code.uniform_total_size;
ubo_offsets = gen_code.uniform_offsets;
texture_uniforms = gen_code.texture_uniforms;
valid = true;
}
bool SkyShaderData::is_animated() const {
return false;
}
bool SkyShaderData::casts_shadows() const {
return false;
}
RS::ShaderNativeSourceCode SkyShaderData::get_native_source_code() const {
return MaterialStorage::get_singleton()->shaders.sky_shader.version_get_native_source_code(version);
}
SkyShaderData::SkyShaderData() {
valid = false;
}
SkyShaderData::~SkyShaderData() {
if (version.is_valid()) {
MaterialStorage::get_singleton()->shaders.sky_shader.version_free(version);
}
}
GLES3::ShaderData *GLES3::_create_sky_shader_func() {
SkyShaderData *shader_data = memnew(SkyShaderData);
return shader_data;
}
////////////////////////////////////////////////////////////////////////////////
// Sky material
void SkyMaterialData::update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) {
uniform_set_updated = true;
update_parameters_internal(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, true);
}
SkyMaterialData::~SkyMaterialData() {
}
GLES3::MaterialData *GLES3::_create_sky_material_func(ShaderData *p_shader) {
SkyMaterialData *material_data = memnew(SkyMaterialData);
material_data->shader_data = static_cast<SkyShaderData *>(p_shader);
//update will happen later anyway so do nothing.
return material_data;
}
void SkyMaterialData::bind_uniforms() {
// Bind Material Uniforms
glBindBufferBase(GL_UNIFORM_BUFFER, SKY_MATERIAL_UNIFORM_LOCATION, uniform_buffer);
bind_uniforms_generic(texture_cache, shader_data->texture_uniforms);
}
////////////////////////////////////////////////////////////////////////////////
// Scene SHADER
void SceneShaderData::set_code(const String &p_code) {
//compile
code = p_code;
valid = false;
ubo_size = 0;
uniforms.clear();
if (code.is_empty()) {
return; //just invalid, but no error
}
ShaderCompiler::GeneratedCode gen_code;
int blend_modei = BLEND_MODE_MIX;
int depth_testi = DEPTH_TEST_ENABLED;
int alpha_antialiasing_modei = ALPHA_ANTIALIASING_OFF;
int cull_modei = CULL_BACK;
int depth_drawi = DEPTH_DRAW_OPAQUE;
uses_point_size = false;
uses_alpha = false;
uses_alpha_clip = false;
uses_blend_alpha = false;
uses_depth_prepass_alpha = false;
uses_discard = false;
uses_roughness = false;
uses_normal = false;
wireframe = false;
unshaded = false;
uses_vertex = false;
uses_position = false;
uses_sss = false;
uses_transmittance = false;
uses_screen_texture = false;
uses_depth_texture = false;
uses_normal_texture = false;
uses_time = false;
writes_modelview_or_projection = false;
uses_world_coordinates = false;
uses_particle_trails = false;
ShaderCompiler::IdentifierActions actions;
actions.entry_point_stages["vertex"] = ShaderCompiler::STAGE_VERTEX;
actions.entry_point_stages["fragment"] = ShaderCompiler::STAGE_FRAGMENT;
actions.entry_point_stages["light"] = ShaderCompiler::STAGE_FRAGMENT;
actions.render_mode_values["blend_add"] = Pair<int *, int>(&blend_modei, BLEND_MODE_ADD);
actions.render_mode_values["blend_mix"] = Pair<int *, int>(&blend_modei, BLEND_MODE_MIX);
actions.render_mode_values["blend_sub"] = Pair<int *, int>(&blend_modei, BLEND_MODE_SUB);
actions.render_mode_values["blend_mul"] = Pair<int *, int>(&blend_modei, BLEND_MODE_MUL);
actions.render_mode_values["alpha_to_coverage"] = Pair<int *, int>(&alpha_antialiasing_modei, ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE);
actions.render_mode_values["alpha_to_coverage_and_one"] = Pair<int *, int>(&alpha_antialiasing_modei, ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE_AND_TO_ONE);
actions.render_mode_values["depth_draw_never"] = Pair<int *, int>(&depth_drawi, DEPTH_DRAW_DISABLED);
actions.render_mode_values["depth_draw_opaque"] = Pair<int *, int>(&depth_drawi, DEPTH_DRAW_OPAQUE);
actions.render_mode_values["depth_draw_always"] = Pair<int *, int>(&depth_drawi, DEPTH_DRAW_ALWAYS);
actions.render_mode_values["depth_test_disabled"] = Pair<int *, int>(&depth_testi, DEPTH_TEST_DISABLED);
actions.render_mode_values["cull_disabled"] = Pair<int *, int>(&cull_modei, CULL_DISABLED);
actions.render_mode_values["cull_front"] = Pair<int *, int>(&cull_modei, CULL_FRONT);
actions.render_mode_values["cull_back"] = Pair<int *, int>(&cull_modei, CULL_BACK);
actions.render_mode_flags["unshaded"] = &unshaded;
actions.render_mode_flags["wireframe"] = &wireframe;
actions.render_mode_flags["particle_trails"] = &uses_particle_trails;
actions.usage_flag_pointers["ALPHA"] = &uses_alpha;
actions.usage_flag_pointers["ALPHA_SCISSOR_THRESHOLD"] = &uses_alpha_clip;
// Use alpha clip pipeline for alpha hash/dither.
// This prevents sorting issues inherent to alpha blending and allows such materials to cast shadows.
actions.usage_flag_pointers["ALPHA_HASH_SCALE"] = &uses_alpha_clip;
actions.render_mode_flags["depth_prepass_alpha"] = &uses_depth_prepass_alpha;
actions.usage_flag_pointers["SSS_STRENGTH"] = &uses_sss;
actions.usage_flag_pointers["SSS_TRANSMITTANCE_DEPTH"] = &uses_transmittance;
actions.usage_flag_pointers["DISCARD"] = &uses_discard;
actions.usage_flag_pointers["TIME"] = &uses_time;
actions.usage_flag_pointers["ROUGHNESS"] = &uses_roughness;
actions.usage_flag_pointers["NORMAL"] = &uses_normal;
actions.usage_flag_pointers["NORMAL_MAP"] = &uses_normal;
actions.usage_flag_pointers["POINT_SIZE"] = &uses_point_size;
actions.usage_flag_pointers["POINT_COORD"] = &uses_point_size;
actions.write_flag_pointers["MODELVIEW_MATRIX"] = &writes_modelview_or_projection;
actions.write_flag_pointers["PROJECTION_MATRIX"] = &writes_modelview_or_projection;
actions.write_flag_pointers["VERTEX"] = &uses_vertex;
actions.write_flag_pointers["POSITION"] = &uses_position;
actions.usage_flag_pointers["TANGENT"] = &uses_tangent;
actions.usage_flag_pointers["BINORMAL"] = &uses_tangent;
actions.usage_flag_pointers["COLOR"] = &uses_color;
actions.usage_flag_pointers["UV"] = &uses_uv;
actions.usage_flag_pointers["UV2"] = &uses_uv2;
actions.usage_flag_pointers["CUSTOM0"] = &uses_custom0;
actions.usage_flag_pointers["CUSTOM1"] = &uses_custom1;
actions.usage_flag_pointers["CUSTOM2"] = &uses_custom2;
actions.usage_flag_pointers["CUSTOM3"] = &uses_custom3;
actions.usage_flag_pointers["BONE_INDICES"] = &uses_bones;
actions.usage_flag_pointers["BONE_WEIGHTS"] = &uses_weights;
actions.uniforms = &uniforms;
Error err = MaterialStorage::get_singleton()->shaders.compiler_scene.compile(RS::SHADER_SPATIAL, code, &actions, path, gen_code);
ERR_FAIL_COND_MSG(err != OK, "Shader compilation failed.");
if (version.is_null()) {
version = MaterialStorage::get_singleton()->shaders.scene_shader.version_create();
}
depth_draw = DepthDraw(depth_drawi);
depth_test = DepthTest(depth_testi);
cull_mode = Cull(cull_modei);
blend_mode = BlendMode(blend_modei);
alpha_antialiasing_mode = AlphaAntiAliasing(alpha_antialiasing_modei);
vertex_input_mask = uint32_t(uses_normal);
vertex_input_mask |= uses_tangent << 1;
vertex_input_mask |= uses_color << 2;
vertex_input_mask |= uses_uv << 3;
vertex_input_mask |= uses_uv2 << 4;
vertex_input_mask |= uses_custom0 << 5;
vertex_input_mask |= uses_custom1 << 6;
vertex_input_mask |= uses_custom2 << 7;
vertex_input_mask |= uses_custom3 << 8;
vertex_input_mask |= uses_bones << 9;
vertex_input_mask |= uses_weights << 10;
uses_screen_texture_mipmaps = gen_code.uses_screen_texture_mipmaps;
uses_screen_texture = gen_code.uses_screen_texture;
uses_depth_texture = gen_code.uses_depth_texture;
uses_normal_texture = gen_code.uses_normal_roughness_texture;
uses_vertex_time = gen_code.uses_vertex_time;
uses_fragment_time = gen_code.uses_fragment_time;
#ifdef DEBUG_ENABLED
if (uses_particle_trails) {
WARN_PRINT_ONCE_ED("Particle trails are only available when using the Forward+ or Mobile rendering backends.");
}
if (uses_sss) {
WARN_PRINT_ONCE_ED("Sub-surface scattering is only available when using the Forward+ rendering backend.");
}
if (uses_transmittance) {
WARN_PRINT_ONCE_ED("Transmittance is only available when using the Forward+ rendering backend.");
}
if (uses_depth_texture) {
WARN_PRINT_ONCE_ED("Reading from the depth texture is not supported when using the GL Compatibility backend yet. Support will be added in a future release.");
}
if (uses_normal_texture) {
WARN_PRINT_ONCE_ED("Reading from the normal-roughness texture is only available when using the Forward+ or Mobile rendering backends.");
}
#endif
#if 0
print_line("**compiling shader:");
print_line("**defines:\n");
for (int i = 0; i < gen_code.defines.size(); i++) {
print_line(gen_code.defines[i]);
}
HashMap<String, String>::Iterator el = gen_code.code.begin();
while (el) {
print_line("\n**code " + el->key + ":\n" + el->value);
++el;
}
print_line("\n**uniforms:\n" + gen_code.uniforms);
print_line("\n**vertex_globals:\n" + gen_code.stage_globals[ShaderCompiler::STAGE_VERTEX]);
print_line("\n**fragment_globals:\n" + gen_code.stage_globals[ShaderCompiler::STAGE_FRAGMENT]);
#endif
LocalVector<ShaderGLES3::TextureUniformData> texture_uniform_data = get_texture_uniform_data(gen_code.texture_uniforms);
MaterialStorage::get_singleton()->shaders.scene_shader.version_set_code(version, gen_code.code, gen_code.uniforms, gen_code.stage_globals[ShaderCompiler::STAGE_VERTEX], gen_code.stage_globals[ShaderCompiler::STAGE_FRAGMENT], gen_code.defines, texture_uniform_data);
ERR_FAIL_COND(!MaterialStorage::get_singleton()->shaders.scene_shader.version_is_valid(version));
ubo_size = gen_code.uniform_total_size;
ubo_offsets = gen_code.uniform_offsets;
texture_uniforms = gen_code.texture_uniforms;
// if any form of Alpha Antialiasing is enabled, set the blend mode to alpha to coverage
if (alpha_antialiasing_mode != ALPHA_ANTIALIASING_OFF) {
blend_mode = BLEND_MODE_ALPHA_TO_COVERAGE;
}
if (blend_mode == BLEND_MODE_ADD || blend_mode == BLEND_MODE_SUB || blend_mode == BLEND_MODE_MUL) {
uses_blend_alpha = true; // Force alpha used because of blend.
}
valid = true;
}
bool SceneShaderData::is_animated() const {
return (uses_fragment_time && uses_discard) || (uses_vertex_time && uses_vertex);
}
bool SceneShaderData::casts_shadows() const {
bool has_read_screen_alpha = uses_screen_texture || uses_depth_texture || uses_normal_texture;
bool has_base_alpha = (uses_alpha && !uses_alpha_clip) || has_read_screen_alpha;
bool has_alpha = has_base_alpha || uses_blend_alpha;
return !has_alpha || (uses_depth_prepass_alpha && !(depth_draw == DEPTH_DRAW_DISABLED || depth_test == DEPTH_TEST_DISABLED));
}
RS::ShaderNativeSourceCode SceneShaderData::get_native_source_code() const {
return MaterialStorage::get_singleton()->shaders.scene_shader.version_get_native_source_code(version);
}
SceneShaderData::SceneShaderData() {
valid = false;
uses_screen_texture = false;
}
SceneShaderData::~SceneShaderData() {
if (version.is_valid()) {
MaterialStorage::get_singleton()->shaders.scene_shader.version_free(version);
}
}
GLES3::ShaderData *GLES3::_create_scene_shader_func() {
SceneShaderData *shader_data = memnew(SceneShaderData);
return shader_data;
}
void SceneMaterialData::set_render_priority(int p_priority) {
priority = p_priority - RS::MATERIAL_RENDER_PRIORITY_MIN; //8 bits
}
void SceneMaterialData::set_next_pass(RID p_pass) {
next_pass = p_pass;
}
void SceneMaterialData::update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) {
update_parameters_internal(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, true);
}
SceneMaterialData::~SceneMaterialData() {
}
GLES3::MaterialData *GLES3::_create_scene_material_func(ShaderData *p_shader) {
SceneMaterialData *material_data = memnew(SceneMaterialData);
material_data->shader_data = static_cast<SceneShaderData *>(p_shader);
//update will happen later anyway so do nothing.
return material_data;
}
void SceneMaterialData::bind_uniforms() {
// Bind Material Uniforms
glBindBufferBase(GL_UNIFORM_BUFFER, SCENE_MATERIAL_UNIFORM_LOCATION, uniform_buffer);
bind_uniforms_generic(texture_cache, shader_data->texture_uniforms);
}
/* Particles SHADER */
void ParticlesShaderData::set_code(const String &p_code) {
//compile
code = p_code;
valid = false;
ubo_size = 0;
uniforms.clear();
uses_collision = false;
if (code.is_empty()) {
return; //just invalid, but no error
}
ShaderCompiler::GeneratedCode gen_code;
ShaderCompiler::IdentifierActions actions;
actions.entry_point_stages["start"] = ShaderCompiler::STAGE_VERTEX;
actions.entry_point_stages["process"] = ShaderCompiler::STAGE_VERTEX;
actions.usage_flag_pointers["COLLIDED"] = &uses_collision;
userdata_count = 0;
for (uint32_t i = 0; i < PARTICLES_MAX_USERDATAS; i++) {
userdatas_used[i] = false;
actions.usage_flag_pointers["USERDATA" + itos(i + 1)] = &userdatas_used[i];
}
actions.uniforms = &uniforms;
Error err = MaterialStorage::get_singleton()->shaders.compiler_particles.compile(RS::SHADER_PARTICLES, code, &actions, path, gen_code);
ERR_FAIL_COND_MSG(err != OK, "Shader compilation failed.");
if (version.is_null()) {
version = MaterialStorage::get_singleton()->shaders.particles_process_shader.version_create();
}
for (uint32_t i = 0; i < PARTICLES_MAX_USERDATAS; i++) {
if (userdatas_used[i]) {
userdata_count++;
}
}
LocalVector<ShaderGLES3::TextureUniformData> texture_uniform_data = get_texture_uniform_data(gen_code.texture_uniforms);
MaterialStorage::get_singleton()->shaders.particles_process_shader.version_set_code(version, gen_code.code, gen_code.uniforms, gen_code.stage_globals[ShaderCompiler::STAGE_VERTEX], gen_code.stage_globals[ShaderCompiler::STAGE_FRAGMENT], gen_code.defines, texture_uniform_data);
ERR_FAIL_COND(!MaterialStorage::get_singleton()->shaders.particles_process_shader.version_is_valid(version));
ubo_size = gen_code.uniform_total_size;
ubo_offsets = gen_code.uniform_offsets;
texture_uniforms = gen_code.texture_uniforms;
valid = true;
}
bool ParticlesShaderData::is_animated() const {
return false;
}
bool ParticlesShaderData::casts_shadows() const {
return false;
}
RS::ShaderNativeSourceCode ParticlesShaderData::get_native_source_code() const {
return MaterialStorage::get_singleton()->shaders.particles_process_shader.version_get_native_source_code(version);
}
ParticlesShaderData::~ParticlesShaderData() {
if (version.is_valid()) {
MaterialStorage::get_singleton()->shaders.particles_process_shader.version_free(version);
}
}
GLES3::ShaderData *GLES3::_create_particles_shader_func() {
ParticlesShaderData *shader_data = memnew(ParticlesShaderData);
return shader_data;
}
void ParticleProcessMaterialData::update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) {
update_parameters_internal(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, true);
}
ParticleProcessMaterialData::~ParticleProcessMaterialData() {
}
GLES3::MaterialData *GLES3::_create_particles_material_func(ShaderData *p_shader) {
ParticleProcessMaterialData *material_data = memnew(ParticleProcessMaterialData);
material_data->shader_data = static_cast<ParticlesShaderData *>(p_shader);
//update will happen later anyway so do nothing.
return material_data;
}
void ParticleProcessMaterialData::bind_uniforms() {
// Bind Material Uniforms
glBindBufferBase(GL_UNIFORM_BUFFER, GLES3::PARTICLES_MATERIAL_UNIFORM_LOCATION, uniform_buffer);
bind_uniforms_generic(texture_cache, shader_data->texture_uniforms, 1); // Start at GL_TEXTURE1 because texture slot 0 is reserved for the heightmap texture.
}
#endif // !GLES3_ENABLED