initial commit, 4.5 stable
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled

This commit is contained in:
2025-09-16 20:46:46 -04:00
commit 9d30169a8d
13378 changed files with 7050105 additions and 0 deletions

11
drivers/gles3/SCsub Normal file
View File

@@ -0,0 +1,11 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")
env.add_source_files(env.drivers_sources, "*.cpp")
SConscript("shaders/SCsub")
SConscript("storage/SCsub")
SConscript("effects/SCsub")
SConscript("environment/SCsub")

View File

@@ -0,0 +1,6 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")
env.add_source_files(env.drivers_sources, "*.cpp")

View File

@@ -0,0 +1,335 @@
/**************************************************************************/
/* copy_effects.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 "copy_effects.h"
#include "../storage/texture_storage.h"
using namespace GLES3;
CopyEffects *CopyEffects::singleton = nullptr;
CopyEffects *CopyEffects::get_singleton() {
return singleton;
}
CopyEffects::CopyEffects() {
singleton = this;
copy.shader.initialize();
copy.shader_version = copy.shader.version_create();
copy.shader.version_bind_shader(copy.shader_version, CopyShaderGLES3::MODE_DEFAULT);
{ // Screen Triangle.
glGenBuffers(1, &screen_triangle);
glBindBuffer(GL_ARRAY_BUFFER, screen_triangle);
const float qv[6] = {
-1.0f,
-1.0f,
3.0f,
-1.0f,
-1.0f,
3.0f,
};
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6, qv, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
glGenVertexArrays(1, &screen_triangle_array);
glBindVertexArray(screen_triangle_array);
glBindBuffer(GL_ARRAY_BUFFER, screen_triangle);
glVertexAttribPointer(RS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, nullptr);
glEnableVertexAttribArray(RS::ARRAY_VERTEX);
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
}
{ // Screen Quad
glGenBuffers(1, &quad);
glBindBuffer(GL_ARRAY_BUFFER, quad);
const float qv[12] = {
-1.0f,
-1.0f,
1.0f,
-1.0f,
1.0f,
1.0f,
-1.0f,
-1.0f,
1.0f,
1.0f,
-1.0f,
1.0f,
};
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 12, qv, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
glGenVertexArrays(1, &quad_array);
glBindVertexArray(quad_array);
glBindBuffer(GL_ARRAY_BUFFER, quad);
glVertexAttribPointer(RS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, nullptr);
glEnableVertexAttribArray(RS::ARRAY_VERTEX);
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
}
}
CopyEffects::~CopyEffects() {
singleton = nullptr;
glDeleteBuffers(1, &screen_triangle);
glDeleteVertexArrays(1, &screen_triangle_array);
glDeleteBuffers(1, &quad);
glDeleteVertexArrays(1, &quad_array);
copy.shader.version_free(copy.shader_version);
}
void CopyEffects::copy_to_rect(const Rect2 &p_rect, bool p_linear_to_srgb) {
uint64_t specializations = p_linear_to_srgb ? CopyShaderGLES3::CONVERT_LINEAR_TO_SRGB : 0;
bool success = copy.shader.version_bind_shader(copy.shader_version, CopyShaderGLES3::MODE_COPY_SECTION, specializations);
if (!success) {
return;
}
copy.shader.version_set_uniform(CopyShaderGLES3::COPY_SECTION, p_rect.position.x, p_rect.position.y, p_rect.size.x, p_rect.size.y, copy.shader_version, CopyShaderGLES3::MODE_COPY_SECTION, specializations);
draw_screen_quad();
}
void CopyEffects::copy_to_rect_3d(const Rect2 &p_rect, float p_layer, int p_type, float p_lod, bool p_linear_to_srgb) {
ERR_FAIL_COND(p_type != Texture::TYPE_LAYERED && p_type != Texture::TYPE_3D);
CopyShaderGLES3::ShaderVariant variant = p_type == Texture::TYPE_LAYERED
? CopyShaderGLES3::MODE_COPY_SECTION_2D_ARRAY
: CopyShaderGLES3::MODE_COPY_SECTION_3D;
uint64_t specializations = p_linear_to_srgb ? CopyShaderGLES3::CONVERT_LINEAR_TO_SRGB : 0;
bool success = copy.shader.version_bind_shader(copy.shader_version, variant, specializations);
if (!success) {
return;
}
copy.shader.version_set_uniform(CopyShaderGLES3::COPY_SECTION, p_rect.position.x, p_rect.position.y, p_rect.size.x, p_rect.size.y, copy.shader_version, variant, specializations);
copy.shader.version_set_uniform(CopyShaderGLES3::LAYER, p_layer, copy.shader_version, variant, specializations);
copy.shader.version_set_uniform(CopyShaderGLES3::LOD, p_lod, copy.shader_version, variant, specializations);
draw_screen_quad();
}
void CopyEffects::copy_with_lens_distortion(const Rect2 &p_rect, float p_layer, const Vector2 &p_eye_center, float p_k1, float p_k2, float p_upscale, float p_aspect_ration, bool p_linear_to_srgb) {
CopyShaderGLES3::ShaderVariant variant = CopyShaderGLES3::MODE_LENS_DISTORTION;
uint64_t specializations = p_linear_to_srgb ? CopyShaderGLES3::CONVERT_LINEAR_TO_SRGB : 0;
bool success = copy.shader.version_bind_shader(copy.shader_version, variant, specializations);
if (!success) {
return;
}
copy.shader.version_set_uniform(CopyShaderGLES3::COPY_SECTION, p_rect.position.x, p_rect.position.y, p_rect.size.x, p_rect.size.y, copy.shader_version, variant, specializations);
copy.shader.version_set_uniform(CopyShaderGLES3::LAYER, p_layer, copy.shader_version, variant, specializations);
copy.shader.version_set_uniform(CopyShaderGLES3::LOD, 0.0, copy.shader_version, variant, specializations);
copy.shader.version_set_uniform(CopyShaderGLES3::EYE_CENTER, p_eye_center.x, p_eye_center.y, copy.shader_version, variant, specializations);
copy.shader.version_set_uniform(CopyShaderGLES3::K1, p_k1, copy.shader_version, variant, specializations);
copy.shader.version_set_uniform(CopyShaderGLES3::K2, p_k1, copy.shader_version, variant, specializations);
copy.shader.version_set_uniform(CopyShaderGLES3::UPSCALE, p_upscale, copy.shader_version, variant, specializations);
copy.shader.version_set_uniform(CopyShaderGLES3::ASPECT_RATIO, p_aspect_ration, copy.shader_version, variant, specializations);
draw_screen_quad();
}
void CopyEffects::copy_to_and_from_rect(const Rect2 &p_rect) {
bool success = copy.shader.version_bind_shader(copy.shader_version, CopyShaderGLES3::MODE_COPY_SECTION_SOURCE);
if (!success) {
return;
}
copy.shader.version_set_uniform(CopyShaderGLES3::COPY_SECTION, p_rect.position.x, p_rect.position.y, p_rect.size.x, p_rect.size.y, copy.shader_version, CopyShaderGLES3::MODE_COPY_SECTION_SOURCE);
copy.shader.version_set_uniform(CopyShaderGLES3::SOURCE_SECTION, p_rect.position.x, p_rect.position.y, p_rect.size.x, p_rect.size.y, copy.shader_version, CopyShaderGLES3::MODE_COPY_SECTION_SOURCE);
draw_screen_quad();
}
void CopyEffects::copy_screen(float p_multiply) {
bool success = copy.shader.version_bind_shader(copy.shader_version, CopyShaderGLES3::MODE_SCREEN);
if (!success) {
return;
}
copy.shader.version_set_uniform(CopyShaderGLES3::MULTIPLY, p_multiply, copy.shader_version, CopyShaderGLES3::MODE_SCREEN);
draw_screen_triangle();
}
void CopyEffects::copy_cube_to_rect(const Rect2 &p_rect) {
bool success = copy.shader.version_bind_shader(copy.shader_version, CopyShaderGLES3::MODE_CUBE_TO_OCTAHEDRAL);
if (!success) {
return;
}
copy.shader.version_set_uniform(CopyShaderGLES3::COPY_SECTION, p_rect.position.x, p_rect.position.y, p_rect.size.x, p_rect.size.y, copy.shader_version, CopyShaderGLES3::MODE_CUBE_TO_OCTAHEDRAL);
draw_screen_quad();
}
void CopyEffects::copy_cube_to_panorama(float p_mip_level) {
bool success = copy.shader.version_bind_shader(copy.shader_version, CopyShaderGLES3::MODE_CUBE_TO_PANORAMA);
if (!success) {
return;
}
copy.shader.version_set_uniform(CopyShaderGLES3::MIP_LEVEL, p_mip_level, copy.shader_version, CopyShaderGLES3::MODE_CUBE_TO_PANORAMA);
draw_screen_quad();
}
// Intended for efficiently mipmapping textures.
void CopyEffects::bilinear_blur(GLuint p_source_texture, int p_mipmap_count, const Rect2i &p_region) {
GLuint framebuffers[2];
glGenFramebuffers(2, framebuffers);
glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffers[0]);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, p_source_texture, 0);
Rect2i source_region = p_region;
Rect2i dest_region = p_region;
for (int i = 1; i < p_mipmap_count; i++) {
dest_region.position.x >>= 1;
dest_region.position.y >>= 1;
dest_region.size = Size2i(dest_region.size.x >> 1, dest_region.size.y >> 1).maxi(1);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffers[i % 2]);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, p_source_texture, i);
glBlitFramebuffer(source_region.position.x, source_region.position.y, source_region.position.x + source_region.size.x, source_region.position.y + source_region.size.y,
dest_region.position.x, dest_region.position.y, dest_region.position.x + dest_region.size.x, dest_region.position.y + dest_region.size.y, GL_COLOR_BUFFER_BIT, GL_LINEAR);
glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffers[i % 2]);
source_region = dest_region;
}
glBindFramebuffer(GL_READ_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
glDeleteFramebuffers(2, framebuffers);
}
// Intended for approximating a gaussian blur. Used for 2D backbuffer mipmaps. Slightly less efficient than bilinear_blur().
void CopyEffects::gaussian_blur(GLuint p_source_texture, int p_mipmap_count, const Rect2i &p_region, const Size2i &p_size) {
GLuint framebuffer;
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, p_source_texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
Size2i base_size = p_size;
Rect2i source_region = p_region;
Rect2i dest_region = p_region;
Size2 float_size = Size2(p_size);
Rect2 normalized_source_region = Rect2(p_region);
normalized_source_region.position = normalized_source_region.position / float_size;
normalized_source_region.size = normalized_source_region.size / float_size;
Rect2 normalized_dest_region = Rect2(p_region);
for (int i = 1; i < p_mipmap_count; i++) {
dest_region.position.x >>= 1;
dest_region.position.y >>= 1;
dest_region.size = Size2i(dest_region.size.x >> 1, dest_region.size.y >> 1).maxi(1);
base_size.x >>= 1;
base_size.y >>= 1;
glBindTexture(GL_TEXTURE_2D, p_source_texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, i - 1);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, i);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, p_source_texture, i);
#ifdef DEV_ENABLED
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
WARN_PRINT("Could not bind Gaussian blur framebuffer, status: " + GLES3::TextureStorage::get_singleton()->get_framebuffer_error(status));
}
#endif
glViewport(0, 0, base_size.x, base_size.y);
bool success = copy.shader.version_bind_shader(copy.shader_version, CopyShaderGLES3::MODE_GAUSSIAN_BLUR);
if (!success) {
return;
}
float_size = Size2(base_size);
normalized_dest_region.position = Size2(dest_region.position) / float_size;
normalized_dest_region.size = Size2(dest_region.size) / float_size;
copy.shader.version_set_uniform(CopyShaderGLES3::COPY_SECTION, normalized_dest_region.position.x, normalized_dest_region.position.y, normalized_dest_region.size.x, normalized_dest_region.size.y, copy.shader_version, CopyShaderGLES3::MODE_GAUSSIAN_BLUR);
copy.shader.version_set_uniform(CopyShaderGLES3::SOURCE_SECTION, normalized_source_region.position.x, normalized_source_region.position.y, normalized_source_region.size.x, normalized_source_region.size.y, copy.shader_version, CopyShaderGLES3::MODE_GAUSSIAN_BLUR);
copy.shader.version_set_uniform(CopyShaderGLES3::PIXEL_SIZE, 1.0 / float_size.x, 1.0 / float_size.y, copy.shader_version, CopyShaderGLES3::MODE_GAUSSIAN_BLUR);
draw_screen_quad();
source_region = dest_region;
normalized_source_region = normalized_dest_region;
}
glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
glDeleteFramebuffers(1, &framebuffer);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, p_mipmap_count - 1);
glBindTexture(GL_TEXTURE_2D, 0);
glViewport(0, 0, p_size.x, p_size.y);
}
void CopyEffects::set_color(const Color &p_color, const Rect2i &p_region) {
bool success = copy.shader.version_bind_shader(copy.shader_version, CopyShaderGLES3::MODE_SIMPLE_COLOR);
if (!success) {
return;
}
copy.shader.version_set_uniform(CopyShaderGLES3::COPY_SECTION, p_region.position.x, p_region.position.y, p_region.size.x, p_region.size.y, copy.shader_version, CopyShaderGLES3::MODE_SIMPLE_COLOR);
copy.shader.version_set_uniform(CopyShaderGLES3::COLOR_IN, p_color, copy.shader_version, CopyShaderGLES3::MODE_SIMPLE_COLOR);
draw_screen_quad();
}
void CopyEffects::draw_screen_triangle() {
glBindVertexArray(screen_triangle_array);
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindVertexArray(0);
}
void CopyEffects::draw_screen_quad() {
glBindVertexArray(quad_array);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindVertexArray(0);
}
#endif // GLES3_ENABLED

View File

@@ -0,0 +1,79 @@
/**************************************************************************/
/* copy_effects.h */
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#ifdef GLES3_ENABLED
#include "drivers/gles3/shaders/effects/copy.glsl.gen.h"
namespace GLES3 {
class CopyEffects {
private:
struct Copy {
CopyShaderGLES3 shader;
RID shader_version;
} copy;
static CopyEffects *singleton;
// Use for full-screen effects. Slightly more efficient than screen_quad as this eliminates pixel overdraw along the diagonal.
GLuint screen_triangle = 0;
GLuint screen_triangle_array = 0;
// Use for rect-based effects.
GLuint quad = 0;
GLuint quad_array = 0;
public:
static CopyEffects *get_singleton();
CopyEffects();
~CopyEffects();
// These functions assume that a framebuffer and texture are bound already. They only manage the shader, uniforms, and vertex array.
void copy_to_rect(const Rect2 &p_rect, bool p_linear_to_srgb = false);
void copy_to_rect_3d(const Rect2 &p_rect, float p_layer, int p_type, float p_lod = 0.0f, bool p_linear_to_srgb = false);
void copy_with_lens_distortion(const Rect2 &p_rect, float p_layer, const Vector2 &p_eye_center, float p_k1, float p_k2, float p_upscale, float p_aspect_ration, bool p_linear_to_srgb = false);
void copy_to_and_from_rect(const Rect2 &p_rect);
void copy_screen(float p_multiply = 1.0);
void copy_cube_to_rect(const Rect2 &p_rect);
void copy_cube_to_panorama(float p_mip_level);
void bilinear_blur(GLuint p_source_texture, int p_mipmap_count, const Rect2i &p_region);
void gaussian_blur(GLuint p_source_texture, int p_mipmap_count, const Rect2i &p_region, const Size2i &p_size);
void set_color(const Color &p_color, const Rect2i &p_region);
void draw_screen_triangle();
void draw_screen_quad();
};
} //namespace GLES3
#endif // GLES3_ENABLED

View File

@@ -0,0 +1,213 @@
/**************************************************************************/
/* cubemap_filter.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 "cubemap_filter.h"
#include "../storage/texture_storage.h"
#include "core/config/project_settings.h"
using namespace GLES3;
CubemapFilter *CubemapFilter::singleton = nullptr;
CubemapFilter::CubemapFilter() {
singleton = this;
// Use a factor 4 larger for the compatibility renderer to make up for the fact
// That we don't use an array texture. We will reduce samples on low roughness
// to compensate.
ggx_samples = 4 * uint32_t(GLOBAL_GET("rendering/reflections/sky_reflections/ggx_samples"));
{
String defines;
defines += "\n#define MAX_SAMPLE_COUNT " + itos(ggx_samples) + "\n";
cubemap_filter.shader.initialize(defines);
cubemap_filter.shader_version = cubemap_filter.shader.version_create();
}
{ // Screen Triangle.
glGenBuffers(1, &screen_triangle);
glBindBuffer(GL_ARRAY_BUFFER, screen_triangle);
const float qv[6] = {
-1.0f,
-1.0f,
-1.0f,
3.0f,
3.0f,
-1.0f,
};
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6, qv, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
glGenVertexArrays(1, &screen_triangle_array);
glBindVertexArray(screen_triangle_array);
glBindBuffer(GL_ARRAY_BUFFER, screen_triangle);
glVertexAttribPointer(RS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, nullptr);
glEnableVertexAttribArray(RS::ARRAY_VERTEX);
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
}
}
CubemapFilter::~CubemapFilter() {
glDeleteBuffers(1, &screen_triangle);
glDeleteVertexArrays(1, &screen_triangle_array);
cubemap_filter.shader.version_free(cubemap_filter.shader_version);
singleton = nullptr;
}
// Helper functions for IBL filtering
Vector3 importance_sample_GGX(Vector2 xi, float roughness4) {
// Compute distribution direction
float phi = 2.0 * Math::PI * xi.x;
float cos_theta = std::sqrt((1.0 - xi.y) / (1.0 + (roughness4 - 1.0) * xi.y));
float sin_theta = std::sqrt(1.0 - cos_theta * cos_theta);
// Convert to spherical direction
Vector3 half_vector;
half_vector.x = sin_theta * std::cos(phi);
half_vector.y = sin_theta * std::sin(phi);
half_vector.z = cos_theta;
return half_vector;
}
float distribution_GGX(float NdotH, float roughness4) {
float NdotH2 = NdotH * NdotH;
float denom = (NdotH2 * (roughness4 - 1.0) + 1.0);
denom = Math::PI * denom * denom;
return roughness4 / denom;
}
float radical_inverse_vdC(uint32_t bits) {
bits = (bits << 16) | (bits >> 16);
bits = ((bits & 0x55555555) << 1) | ((bits & 0xAAAAAAAA) >> 1);
bits = ((bits & 0x33333333) << 2) | ((bits & 0xCCCCCCCC) >> 2);
bits = ((bits & 0x0F0F0F0F) << 4) | ((bits & 0xF0F0F0F0) >> 4);
bits = ((bits & 0x00FF00FF) << 8) | ((bits & 0xFF00FF00) >> 8);
return float(bits) * 2.3283064365386963e-10;
}
Vector2 hammersley(uint32_t i, uint32_t N) {
return Vector2(float(i) / float(N), radical_inverse_vdC(i));
}
void CubemapFilter::filter_radiance(GLuint p_source_cubemap, GLuint p_dest_cubemap, GLuint p_dest_framebuffer, int p_source_size, int p_mipmap_count, int p_layer) {
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, p_source_cubemap);
glBindFramebuffer(GL_FRAMEBUFFER, p_dest_framebuffer);
CubemapFilterShaderGLES3::ShaderVariant mode = CubemapFilterShaderGLES3::MODE_DEFAULT;
if (p_layer == 0) {
glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
// Copy over base layer without filtering.
mode = CubemapFilterShaderGLES3::MODE_COPY;
}
int size = p_source_size >> p_layer;
glViewport(0, 0, size, size);
glBindVertexArray(screen_triangle_array);
bool success = cubemap_filter.shader.version_bind_shader(cubemap_filter.shader_version, mode);
if (!success) {
return;
}
if (p_layer > 0) {
const uint32_t sample_counts[5] = { 1, ggx_samples / 16, ggx_samples / 8, ggx_samples / 4, ggx_samples };
uint32_t sample_count = sample_counts[MIN(4, p_layer)];
float roughness = float(p_layer) / (p_mipmap_count - 1);
roughness *= roughness; // Convert to non-perceptual roughness.
float roughness4 = roughness * roughness;
roughness4 *= roughness4;
float solid_angle_texel = 4.0 * Math::PI / float(6 * size * size);
LocalVector<float> sample_directions;
sample_directions.resize(4 * sample_count);
uint32_t index = 0;
float weight = 0.0;
for (uint32_t i = 0; i < sample_count; i++) {
Vector2 xi = hammersley(i, sample_count);
Vector3 dir = importance_sample_GGX(xi, roughness4);
Vector3 light_vec = (2.0 * dir.z * dir - Vector3(0.0, 0.0, 1.0));
if (light_vec.z <= 0.0) {
continue;
}
sample_directions[index * 4] = light_vec.x;
sample_directions[index * 4 + 1] = light_vec.y;
sample_directions[index * 4 + 2] = light_vec.z;
float D = distribution_GGX(dir.z, roughness4);
float pdf = D * dir.z / (4.0 * dir.z) + 0.0001;
float solid_angle_sample = 1.0 / (float(sample_count) * pdf + 0.0001);
float mip_level = MAX(0.5 * std::log2(solid_angle_sample / solid_angle_texel) + float(MAX(1, p_layer - 3)), 1.0);
sample_directions[index * 4 + 3] = mip_level;
weight += light_vec.z;
index++;
}
glUniform4fv(cubemap_filter.shader.version_get_uniform(CubemapFilterShaderGLES3::SAMPLE_DIRECTIONS_MIP, cubemap_filter.shader_version, mode), sample_count, sample_directions.ptr());
cubemap_filter.shader.version_set_uniform(CubemapFilterShaderGLES3::WEIGHT, weight, cubemap_filter.shader_version, mode);
cubemap_filter.shader.version_set_uniform(CubemapFilterShaderGLES3::SAMPLE_COUNT, index, cubemap_filter.shader_version, mode);
}
for (int i = 0; i < 6; i++) {
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, p_dest_cubemap, p_layer);
#ifdef DEBUG_ENABLED
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
WARN_PRINT("Could not bind sky radiance face: " + itos(i) + ", status: " + GLES3::TextureStorage::get_singleton()->get_framebuffer_error(status));
}
#endif
cubemap_filter.shader.version_set_uniform(CubemapFilterShaderGLES3::FACE_ID, i, cubemap_filter.shader_version, mode);
glDrawArrays(GL_TRIANGLES, 0, 3);
}
glBindVertexArray(0);
glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
}
#endif // GLES3_ENABLED

View File

@@ -0,0 +1,67 @@
/**************************************************************************/
/* cubemap_filter.h */
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#ifdef GLES3_ENABLED
#include "drivers/gles3/shaders/effects/cubemap_filter.glsl.gen.h"
namespace GLES3 {
class CubemapFilter {
private:
struct CMF {
CubemapFilterShaderGLES3 shader;
RID shader_version;
} cubemap_filter;
static CubemapFilter *singleton;
// Use for full-screen effects. Slightly more efficient than screen_quad as this eliminates pixel overdraw along the diagonal.
GLuint screen_triangle = 0;
GLuint screen_triangle_array = 0;
uint32_t ggx_samples = 128;
public:
static CubemapFilter *get_singleton() {
return singleton;
}
CubemapFilter();
~CubemapFilter();
void filter_radiance(GLuint p_source_cubemap, GLuint p_dest_cubemap, GLuint p_dest_framebuffer, int p_source_size, int p_mipmap_count, int p_layer);
};
} //namespace GLES3
#endif // GLES3_ENABLED

View File

@@ -0,0 +1,128 @@
/**************************************************************************/
/* feed_effects.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 "feed_effects.h"
#ifdef ANDROID_ENABLED
#include <GLES3/gl3ext.h>
#endif
#define GL_PROGRAM_POINT_SIZE 0x8642
using namespace GLES3;
FeedEffects *FeedEffects::singleton = nullptr;
FeedEffects *FeedEffects::get_singleton() {
return singleton;
}
FeedEffects::FeedEffects() {
singleton = this;
feed.shader.initialize();
feed.shader_version = feed.shader.version_create();
feed.shader.version_bind_shader(feed.shader_version, FeedShaderGLES3::MODE_DEFAULT);
{ // Screen Triangle.
glGenBuffers(1, &screen_triangle);
glBindBuffer(GL_ARRAY_BUFFER, screen_triangle);
const float qv[6] = {
-1.0f,
-1.0f,
3.0f,
-1.0f,
-1.0f,
3.0f,
};
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6, qv, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
glGenVertexArrays(1, &screen_triangle_array);
glBindVertexArray(screen_triangle_array);
glBindBuffer(GL_ARRAY_BUFFER, screen_triangle);
glVertexAttribPointer(RS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, nullptr);
glEnableVertexAttribArray(RS::ARRAY_VERTEX);
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
}
}
FeedEffects::~FeedEffects() {
singleton = nullptr;
glDeleteBuffers(1, &screen_triangle);
glDeleteVertexArrays(1, &screen_triangle_array);
feed.shader.version_free(feed.shader_version);
}
Transform3D transform3D_from_mat4(const float *p_mat4) {
Transform3D res;
res.basis.rows[0][0] = p_mat4[0];
res.basis.rows[1][0] = p_mat4[1];
res.basis.rows[2][0] = p_mat4[2];
// p_mat4[3] = 0;
res.basis.rows[0][1] = p_mat4[4];
res.basis.rows[1][1] = p_mat4[5];
res.basis.rows[2][1] = p_mat4[6];
// p_mat4[7] = 0;
res.basis.rows[0][2] = p_mat4[8];
res.basis.rows[1][2] = p_mat4[9];
res.basis.rows[2][2] = p_mat4[10];
// p_mat4[11] = 0;
res.origin.x = p_mat4[12];
res.origin.y = p_mat4[13];
res.origin.z = p_mat4[14];
// p_mat4[15] = 1;
return res;
}
void FeedEffects::draw() {
bool success = feed.shader.version_bind_shader(feed.shader_version, FeedShaderGLES3::MODE_DEFAULT, FeedShaderGLES3::USE_EXTERNAL_SAMPLER);
if (!success) {
OS::get_singleton()->print("Godot : FeedShaderGLES3 Could not bind version_bind_shader USE_EXTERNAL_SAMPLER");
return;
}
draw_screen_triangle();
}
void FeedEffects::draw_screen_triangle() {
glBindVertexArray(screen_triangle_array);
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindVertexArray(0);
}
#endif // GLES3_ENABLED

View File

@@ -0,0 +1,66 @@
/**************************************************************************/
/* feed_effects.h */
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#ifdef GLES3_ENABLED
#include "drivers/gles3/shader_gles3.h"
#include "drivers/gles3/shaders/feed.glsl.gen.h"
namespace GLES3 {
class FeedEffects {
private:
struct Feed {
FeedShaderGLES3 shader;
RID shader_version;
} feed;
static FeedEffects *singleton;
GLuint screen_triangle = 0;
GLuint screen_triangle_array = 0;
public:
static FeedEffects *get_singleton();
FeedEffects();
~FeedEffects();
void draw();
private:
void draw_screen_triangle();
};
} // namespace GLES3
#endif // GLES3_ENABLED

View File

@@ -0,0 +1,173 @@
/**************************************************************************/
/* glow.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 "glow.h"
#include "../storage/texture_storage.h"
using namespace GLES3;
Glow *Glow::singleton = nullptr;
Glow *Glow::get_singleton() {
return singleton;
}
Glow::Glow() {
singleton = this;
glow.shader.initialize();
glow.shader_version = glow.shader.version_create();
{ // Screen Triangle.
glGenBuffers(1, &screen_triangle);
glBindBuffer(GL_ARRAY_BUFFER, screen_triangle);
const float qv[6] = {
-1.0f,
-1.0f,
3.0f,
-1.0f,
-1.0f,
3.0f,
};
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6, qv, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
glGenVertexArrays(1, &screen_triangle_array);
glBindVertexArray(screen_triangle_array);
glBindBuffer(GL_ARRAY_BUFFER, screen_triangle);
glVertexAttribPointer(RS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, nullptr);
glEnableVertexAttribArray(RS::ARRAY_VERTEX);
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
}
}
Glow::~Glow() {
glDeleteBuffers(1, &screen_triangle);
glDeleteVertexArrays(1, &screen_triangle_array);
glow.shader.version_free(glow.shader_version);
singleton = nullptr;
}
void Glow::_draw_screen_triangle() {
glBindVertexArray(screen_triangle_array);
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindVertexArray(0);
}
void Glow::process_glow(GLuint p_source_color, Size2i p_size, const Glow::GLOWLEVEL *p_glow_buffers, uint32_t p_view, bool p_use_multiview) {
ERR_FAIL_COND(p_source_color == 0);
ERR_FAIL_COND(p_glow_buffers[3].color == 0);
// Reset some OpenGL state...
glDisable(GL_BLEND);
glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
// Start with our filter pass
{
glBindFramebuffer(GL_FRAMEBUFFER, p_glow_buffers[0].fbo);
glViewport(0, 0, p_glow_buffers[0].size.x, p_glow_buffers[0].size.y);
glActiveTexture(GL_TEXTURE0);
glBindTexture(p_use_multiview ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D, p_source_color);
uint64_t specialization = p_use_multiview ? GlowShaderGLES3::USE_MULTIVIEW : 0;
bool success = glow.shader.version_bind_shader(glow.shader_version, GlowShaderGLES3::MODE_FILTER, specialization);
if (!success) {
return;
}
glow.shader.version_set_uniform(GlowShaderGLES3::PIXEL_SIZE, 1.0 / p_glow_buffers[0].size.x, 1.0 / p_glow_buffers[0].size.y, glow.shader_version, GlowShaderGLES3::MODE_FILTER, specialization);
glow.shader.version_set_uniform(GlowShaderGLES3::VIEW, float(p_view), glow.shader_version, GlowShaderGLES3::MODE_FILTER, specialization);
glow.shader.version_set_uniform(GlowShaderGLES3::LUMINANCE_MULTIPLIER, luminance_multiplier, glow.shader_version, GlowShaderGLES3::MODE_FILTER, specialization);
glow.shader.version_set_uniform(GlowShaderGLES3::GLOW_BLOOM, glow_bloom, glow.shader_version, GlowShaderGLES3::MODE_FILTER, specialization);
glow.shader.version_set_uniform(GlowShaderGLES3::GLOW_HDR_THRESHOLD, glow_hdr_bleed_threshold, glow.shader_version, GlowShaderGLES3::MODE_FILTER, specialization);
glow.shader.version_set_uniform(GlowShaderGLES3::GLOW_HDR_SCALE, glow_hdr_bleed_scale, glow.shader_version, GlowShaderGLES3::MODE_FILTER, specialization);
glow.shader.version_set_uniform(GlowShaderGLES3::GLOW_LUMINANCE_CAP, glow_hdr_luminance_cap, glow.shader_version, GlowShaderGLES3::MODE_FILTER, specialization);
_draw_screen_triangle();
}
// Continue with downsampling
{
bool success = glow.shader.version_bind_shader(glow.shader_version, GlowShaderGLES3::MODE_DOWNSAMPLE, 0);
if (!success) {
return;
}
for (int i = 1; i < 4; i++) {
glBindFramebuffer(GL_FRAMEBUFFER, p_glow_buffers[i].fbo);
glViewport(0, 0, p_glow_buffers[i].size.x, p_glow_buffers[i].size.y);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, p_glow_buffers[i - 1].color);
glow.shader.version_set_uniform(GlowShaderGLES3::PIXEL_SIZE, 1.0 / p_glow_buffers[i].size.x, 1.0 / p_glow_buffers[i].size.y, glow.shader_version, GlowShaderGLES3::MODE_DOWNSAMPLE);
_draw_screen_triangle();
}
}
// Now upsample
{
bool success = glow.shader.version_bind_shader(glow.shader_version, GlowShaderGLES3::MODE_UPSAMPLE, 0);
if (!success) {
return;
}
for (int i = 2; i >= 0; i--) {
glBindFramebuffer(GL_FRAMEBUFFER, p_glow_buffers[i].fbo);
glViewport(0, 0, p_glow_buffers[i].size.x, p_glow_buffers[i].size.y);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, p_glow_buffers[i + 1].color);
glow.shader.version_set_uniform(GlowShaderGLES3::PIXEL_SIZE, 1.0 / p_glow_buffers[i].size.x, 1.0 / p_glow_buffers[i].size.y, glow.shader_version, GlowShaderGLES3::MODE_UPSAMPLE);
_draw_screen_triangle();
}
}
glDisable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
glUseProgram(0);
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
}
#endif // GLES3_ENABLED

View File

@@ -0,0 +1,86 @@
/**************************************************************************/
/* glow.h */
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#ifdef GLES3_ENABLED
#include "drivers/gles3/shaders/effects/glow.glsl.gen.h"
namespace GLES3 {
class Glow {
private:
static Glow *singleton;
struct GLOW {
GlowShaderGLES3 shader;
RID shader_version;
} glow;
float luminance_multiplier = 1.0;
float glow_intensity = 1.0;
float glow_bloom = 0.0;
float glow_hdr_bleed_threshold = 1.0;
float glow_hdr_bleed_scale = 2.0;
float glow_hdr_luminance_cap = 12.0;
// Use for full-screen effects. Slightly more efficient than screen_quad as this eliminates pixel overdraw along the diagonal.
GLuint screen_triangle = 0;
GLuint screen_triangle_array = 0;
void _draw_screen_triangle();
public:
struct GLOWLEVEL {
Size2i size;
GLuint color = 0;
GLuint fbo = 0;
};
static Glow *get_singleton();
Glow();
~Glow();
void set_intensity(float p_value) { glow_intensity = p_value; }
void set_luminance_multiplier(float p_luminance_multiplier) { luminance_multiplier = p_luminance_multiplier; }
void set_glow_bloom(float p_bloom) { glow_bloom = p_bloom; }
void set_glow_hdr_bleed_threshold(float p_threshold) { glow_hdr_bleed_threshold = p_threshold; }
void set_glow_hdr_bleed_scale(float p_scale) { glow_hdr_bleed_scale = p_scale; }
void set_glow_hdr_luminance_cap(float p_cap) { glow_hdr_luminance_cap = p_cap; }
void process_glow(GLuint p_source_color, Size2i p_size, const GLOWLEVEL *p_glow_buffers, uint32_t p_view = 0, bool p_use_multiview = false);
};
} //namespace GLES3
#endif // GLES3_ENABLED

View File

@@ -0,0 +1,153 @@
/**************************************************************************/
/* post_effects.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 "post_effects.h"
#include "../storage/texture_storage.h"
using namespace GLES3;
PostEffects *PostEffects::singleton = nullptr;
PostEffects *PostEffects::get_singleton() {
return singleton;
}
PostEffects::PostEffects() {
singleton = this;
post.shader.initialize();
post.shader_version = post.shader.version_create();
post.shader.version_bind_shader(post.shader_version, PostShaderGLES3::MODE_DEFAULT);
{ // Screen Triangle.
glGenBuffers(1, &screen_triangle);
glBindBuffer(GL_ARRAY_BUFFER, screen_triangle);
const float qv[6] = {
-1.0f,
-1.0f,
3.0f,
-1.0f,
-1.0f,
3.0f,
};
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6, qv, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
glGenVertexArrays(1, &screen_triangle_array);
glBindVertexArray(screen_triangle_array);
glBindBuffer(GL_ARRAY_BUFFER, screen_triangle);
glVertexAttribPointer(RS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, nullptr);
glEnableVertexAttribArray(RS::ARRAY_VERTEX);
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
}
}
PostEffects::~PostEffects() {
singleton = nullptr;
glDeleteBuffers(1, &screen_triangle);
glDeleteVertexArrays(1, &screen_triangle_array);
post.shader.version_free(post.shader_version);
}
void PostEffects::_draw_screen_triangle() {
glBindVertexArray(screen_triangle_array);
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindVertexArray(0);
}
void PostEffects::post_copy(GLuint p_dest_framebuffer, Size2i p_dest_size, GLuint p_source_color, Size2i p_source_size, float p_luminance_multiplier, const Glow::GLOWLEVEL *p_glow_buffers, float p_glow_intensity, uint32_t p_view, bool p_use_multiview, uint64_t p_spec_constants) {
glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
glDisable(GL_BLEND);
glBindFramebuffer(GL_FRAMEBUFFER, p_dest_framebuffer);
glViewport(0, 0, p_dest_size.x, p_dest_size.y);
PostShaderGLES3::ShaderVariant mode = PostShaderGLES3::MODE_DEFAULT;
uint64_t flags = p_spec_constants;
if (p_use_multiview) {
flags |= PostShaderGLES3::USE_MULTIVIEW;
}
if (p_glow_buffers != nullptr) {
flags |= PostShaderGLES3::USE_GLOW;
}
if (p_luminance_multiplier != 1.0) {
flags |= PostShaderGLES3::USE_LUMINANCE_MULTIPLIER;
}
bool success = post.shader.version_bind_shader(post.shader_version, mode, flags);
if (!success) {
return;
}
GLenum texture_target = p_use_multiview ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D;
glActiveTexture(GL_TEXTURE0);
glBindTexture(texture_target, p_source_color);
glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
if (p_glow_buffers != nullptr) {
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, p_glow_buffers[0].color);
post.shader.version_set_uniform(PostShaderGLES3::PIXEL_SIZE, 1.0 / p_source_size.x, 1.0 / p_source_size.y, post.shader_version, mode, flags);
post.shader.version_set_uniform(PostShaderGLES3::GLOW_INTENSITY, p_glow_intensity, post.shader_version, mode, flags);
}
post.shader.version_set_uniform(PostShaderGLES3::VIEW, float(p_view), post.shader_version, mode, flags);
post.shader.version_set_uniform(PostShaderGLES3::LUMINANCE_MULTIPLIER, p_luminance_multiplier, post.shader_version, mode, flags);
_draw_screen_triangle();
// Reset state
if (p_glow_buffers != nullptr) {
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, 0);
}
// Return back to nearest
glActiveTexture(GL_TEXTURE0);
glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glBindTexture(texture_target, 0);
glDisable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
glUseProgram(0);
glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
}
#endif // GLES3_ENABLED

View File

@@ -0,0 +1,66 @@
/**************************************************************************/
/* post_effects.h */
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#ifdef GLES3_ENABLED
#include "drivers/gles3/shaders/effects/post.glsl.gen.h"
#include "glow.h"
namespace GLES3 {
class PostEffects {
private:
struct Post {
PostShaderGLES3 shader;
RID shader_version;
} post;
static PostEffects *singleton;
// Use for full-screen effects. Slightly more efficient than screen_quad as this eliminates pixel overdraw along the diagonal.
GLuint screen_triangle = 0;
GLuint screen_triangle_array = 0;
void _draw_screen_triangle();
public:
static PostEffects *get_singleton();
PostEffects();
~PostEffects();
void post_copy(GLuint p_dest_framebuffer, Size2i p_dest_size, GLuint p_source_color, Size2i p_source_size, float p_luminance_multiplier, const Glow::GLOWLEVEL *p_glow_buffers, float p_glow_intensity, uint32_t p_view = 0, bool p_use_multiview = false, uint64_t p_spec_constants = 0);
};
} //namespace GLES3
#endif // GLES3_ENABLED

View File

@@ -0,0 +1,6 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")
env.add_source_files(env.drivers_sources, "*.cpp")

View File

@@ -0,0 +1,66 @@
/**************************************************************************/
/* fog.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 "fog.h"
using namespace GLES3;
/* FOG */
RID Fog::fog_volume_allocate() {
return RID();
}
void Fog::fog_volume_initialize(RID p_rid) {
}
void Fog::fog_volume_free(RID p_rid) {
}
void Fog::fog_volume_set_shape(RID p_fog_volume, RS::FogVolumeShape p_shape) {
}
void Fog::fog_volume_set_size(RID p_fog_volume, const Vector3 &p_size) {
}
void Fog::fog_volume_set_material(RID p_fog_volume, RID p_material) {
}
AABB Fog::fog_volume_get_aabb(RID p_fog_volume) const {
return AABB();
}
RS::FogVolumeShape Fog::fog_volume_get_shape(RID p_fog_volume) const {
return RS::FOG_VOLUME_SHAPE_BOX;
}
#endif // GLES3_ENABLED

View File

@@ -0,0 +1,56 @@
/**************************************************************************/
/* fog.h */
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#ifdef GLES3_ENABLED
#include "servers/rendering/environment/renderer_fog.h"
namespace GLES3 {
class Fog : public RendererFog {
public:
/* FOG VOLUMES */
virtual RID fog_volume_allocate() override;
virtual void fog_volume_initialize(RID p_rid) override;
virtual void fog_volume_free(RID p_rid) override;
virtual void fog_volume_set_shape(RID p_fog_volume, RS::FogVolumeShape p_shape) override;
virtual void fog_volume_set_size(RID p_fog_volume, const Vector3 &p_size) override;
virtual void fog_volume_set_material(RID p_fog_volume, RID p_material) override;
virtual AABB fog_volume_get_aabb(RID p_fog_volume) const override;
virtual RS::FogVolumeShape fog_volume_get_shape(RID p_fog_volume) const override;
};
} // namespace GLES3
#endif // GLES3_ENABLED

View File

@@ -0,0 +1,143 @@
/**************************************************************************/
/* gi.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 "gi.h"
using namespace GLES3;
/* VOXEL GI API */
RID GI::voxel_gi_allocate() {
return RID();
}
void GI::voxel_gi_free(RID p_rid) {
}
void GI::voxel_gi_initialize(RID p_rid) {
}
void GI::voxel_gi_allocate_data(RID p_voxel_gi, const Transform3D &p_to_cell_xform, const AABB &p_aabb, const Vector3i &p_octree_size, const Vector<uint8_t> &p_octree_cells, const Vector<uint8_t> &p_data_cells, const Vector<uint8_t> &p_distance_field, const Vector<int> &p_level_counts) {
}
AABB GI::voxel_gi_get_bounds(RID p_voxel_gi) const {
return AABB();
}
Vector3i GI::voxel_gi_get_octree_size(RID p_voxel_gi) const {
return Vector3i();
}
Vector<uint8_t> GI::voxel_gi_get_octree_cells(RID p_voxel_gi) const {
return Vector<uint8_t>();
}
Vector<uint8_t> GI::voxel_gi_get_data_cells(RID p_voxel_gi) const {
return Vector<uint8_t>();
}
Vector<uint8_t> GI::voxel_gi_get_distance_field(RID p_voxel_gi) const {
return Vector<uint8_t>();
}
Vector<int> GI::voxel_gi_get_level_counts(RID p_voxel_gi) const {
return Vector<int>();
}
Transform3D GI::voxel_gi_get_to_cell_xform(RID p_voxel_gi) const {
return Transform3D();
}
void GI::voxel_gi_set_dynamic_range(RID p_voxel_gi, float p_range) {
}
float GI::voxel_gi_get_dynamic_range(RID p_voxel_gi) const {
return 0;
}
void GI::voxel_gi_set_propagation(RID p_voxel_gi, float p_range) {
}
float GI::voxel_gi_get_propagation(RID p_voxel_gi) const {
return 0;
}
void GI::voxel_gi_set_energy(RID p_voxel_gi, float p_range) {
}
float GI::voxel_gi_get_energy(RID p_voxel_gi) const {
return 0.0;
}
void GI::voxel_gi_set_baked_exposure_normalization(RID p_voxel_gi, float p_baked_exposure) {
}
float GI::voxel_gi_get_baked_exposure_normalization(RID p_voxel_gi) const {
return 1.0;
}
void GI::voxel_gi_set_bias(RID p_voxel_gi, float p_range) {
}
float GI::voxel_gi_get_bias(RID p_voxel_gi) const {
return 0.0;
}
void GI::voxel_gi_set_normal_bias(RID p_voxel_gi, float p_range) {
}
float GI::voxel_gi_get_normal_bias(RID p_voxel_gi) const {
return 0.0;
}
void GI::voxel_gi_set_interior(RID p_voxel_gi, bool p_enable) {
}
bool GI::voxel_gi_is_interior(RID p_voxel_gi) const {
return false;
}
void GI::voxel_gi_set_use_two_bounces(RID p_voxel_gi, bool p_enable) {
}
bool GI::voxel_gi_is_using_two_bounces(RID p_voxel_gi) const {
return false;
}
uint32_t GI::voxel_gi_get_version(RID p_voxel_gi) const {
return 0;
}
void GI::sdfgi_reset() {
}
#endif // GLES3_ENABLED

View File

@@ -0,0 +1,88 @@
/**************************************************************************/
/* gi.h */
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#ifdef GLES3_ENABLED
#include "servers/rendering/environment/renderer_gi.h"
namespace GLES3 {
class GI : public RendererGI {
public:
/* VOXEL GI API */
virtual RID voxel_gi_allocate() override;
virtual void voxel_gi_free(RID p_rid) override;
virtual void voxel_gi_initialize(RID p_rid) override;
virtual void voxel_gi_allocate_data(RID p_voxel_gi, const Transform3D &p_to_cell_xform, const AABB &p_aabb, const Vector3i &p_octree_size, const Vector<uint8_t> &p_octree_cells, const Vector<uint8_t> &p_data_cells, const Vector<uint8_t> &p_distance_field, const Vector<int> &p_level_counts) override;
virtual AABB voxel_gi_get_bounds(RID p_voxel_gi) const override;
virtual Vector3i voxel_gi_get_octree_size(RID p_voxel_gi) const override;
virtual Vector<uint8_t> voxel_gi_get_octree_cells(RID p_voxel_gi) const override;
virtual Vector<uint8_t> voxel_gi_get_data_cells(RID p_voxel_gi) const override;
virtual Vector<uint8_t> voxel_gi_get_distance_field(RID p_voxel_gi) const override;
virtual Vector<int> voxel_gi_get_level_counts(RID p_voxel_gi) const override;
virtual Transform3D voxel_gi_get_to_cell_xform(RID p_voxel_gi) const override;
virtual void voxel_gi_set_dynamic_range(RID p_voxel_gi, float p_range) override;
virtual float voxel_gi_get_dynamic_range(RID p_voxel_gi) const override;
virtual void voxel_gi_set_propagation(RID p_voxel_gi, float p_range) override;
virtual float voxel_gi_get_propagation(RID p_voxel_gi) const override;
virtual void voxel_gi_set_energy(RID p_voxel_gi, float p_range) override;
virtual float voxel_gi_get_energy(RID p_voxel_gi) const override;
virtual void voxel_gi_set_baked_exposure_normalization(RID p_voxel_gi, float p_baked_exposure) override;
virtual float voxel_gi_get_baked_exposure_normalization(RID p_voxel_gi) const override;
virtual void voxel_gi_set_bias(RID p_voxel_gi, float p_range) override;
virtual float voxel_gi_get_bias(RID p_voxel_gi) const override;
virtual void voxel_gi_set_normal_bias(RID p_voxel_gi, float p_range) override;
virtual float voxel_gi_get_normal_bias(RID p_voxel_gi) const override;
virtual void voxel_gi_set_interior(RID p_voxel_gi, bool p_enable) override;
virtual bool voxel_gi_is_interior(RID p_voxel_gi) const override;
virtual void voxel_gi_set_use_two_bounces(RID p_voxel_gi, bool p_enable) override;
virtual bool voxel_gi_is_using_two_bounces(RID p_voxel_gi) const override;
virtual uint32_t voxel_gi_get_version(RID p_voxel_gi) const override;
virtual void sdfgi_reset() override;
};
}; // namespace GLES3
#endif // GLES3_ENABLED

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,389 @@
/**************************************************************************/
/* rasterizer_canvas_gles3.h */
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#ifdef GLES3_ENABLED
#include "rasterizer_scene_gles3.h"
#include "servers/rendering/renderer_canvas_render.h"
#include "servers/rendering/renderer_compositor.h"
#include "storage/material_storage.h"
#include "storage/texture_storage.h"
#include "drivers/gles3/shaders/canvas.glsl.gen.h"
#include "drivers/gles3/shaders/canvas_occlusion.glsl.gen.h"
class RasterizerSceneGLES3;
class RasterizerCanvasGLES3 : public RendererCanvasRender {
static RasterizerCanvasGLES3 *singleton;
_FORCE_INLINE_ void _update_transform_2d_to_mat2x4(const Transform2D &p_transform, float *p_mat2x4);
_FORCE_INLINE_ void _update_transform_2d_to_mat2x3(const Transform2D &p_transform, float *p_mat2x3);
_FORCE_INLINE_ void _update_transform_2d_to_mat4(const Transform2D &p_transform, float *p_mat4);
_FORCE_INLINE_ void _update_transform_to_mat4(const Transform3D &p_transform, float *p_mat4);
enum {
INSTANCE_FLAGS_LIGHT_COUNT_SHIFT = 0, // 4 bits for light count.
INSTANCE_FLAGS_CLIP_RECT_UV = (1 << 4),
INSTANCE_FLAGS_TRANSPOSE_RECT = (1 << 5),
INSTANCE_FLAGS_USE_MSDF = (1 << 6),
INSTANCE_FLAGS_USE_LCD = (1 << 7),
INSTANCE_FLAGS_NINEPACH_DRAW_CENTER = (1 << 8),
INSTANCE_FLAGS_NINEPATCH_H_MODE_SHIFT = 9,
INSTANCE_FLAGS_NINEPATCH_V_MODE_SHIFT = 11,
INSTANCE_FLAGS_SHADOW_MASKED_SHIFT = 13, // 16 bits.
};
enum {
BATCH_FLAGS_INSTANCING_MASK = 0x7F,
BATCH_FLAGS_INSTANCING_HAS_COLORS = (1 << 7),
BATCH_FLAGS_INSTANCING_HAS_CUSTOM_DATA = (1 << 8),
BATCH_FLAGS_DEFAULT_NORMAL_MAP_USED = (1 << 9),
BATCH_FLAGS_DEFAULT_SPECULAR_MAP_USED = (1 << 10),
};
enum {
LIGHT_FLAGS_TEXTURE_MASK = 0xFFFF,
LIGHT_FLAGS_BLEND_SHIFT = 16,
LIGHT_FLAGS_BLEND_MASK = (3 << 16),
LIGHT_FLAGS_BLEND_MODE_ADD = (0 << 16),
LIGHT_FLAGS_BLEND_MODE_SUB = (1 << 16),
LIGHT_FLAGS_BLEND_MODE_MIX = (2 << 16),
LIGHT_FLAGS_BLEND_MODE_MASK = (3 << 16),
LIGHT_FLAGS_HAS_SHADOW = (1 << 20),
LIGHT_FLAGS_FILTER_SHIFT = 22
};
enum {
MAX_RENDER_ITEMS = 256 * 1024,
MAX_LIGHT_TEXTURES = 1024,
MAX_LIGHTS_PER_ITEM = 16,
DEFAULT_MAX_LIGHTS_PER_RENDER = 256,
};
/******************/
/**** LIGHTING ****/
/******************/
struct CanvasLight {
RID texture;
struct {
bool enabled = false;
float z_far;
float y_offset;
Transform2D directional_xform;
} shadow;
};
RID_Owner<CanvasLight> canvas_light_owner;
struct OccluderPolygon {
RS::CanvasOccluderPolygonCullMode cull_mode = RS::CANVAS_OCCLUDER_POLYGON_CULL_DISABLED;
int line_point_count = 0;
GLuint vertex_buffer = 0;
GLuint vertex_array = 0;
GLuint index_buffer = 0;
int sdf_point_count = 0;
int sdf_index_count = 0;
GLuint sdf_vertex_buffer = 0;
GLuint sdf_vertex_array = 0;
GLuint sdf_index_buffer = 0;
bool sdf_is_lines = false;
};
RID_Owner<OccluderPolygon> occluder_polygon_owner;
void _update_shadow_atlas();
struct {
CanvasOcclusionShaderGLES3 shader;
RID shader_version;
} shadow_render;
struct LightUniform {
float matrix[8]; //light to texture coordinate matrix
float shadow_matrix[8]; //light to shadow coordinate matrix
float color[4];
uint8_t shadow_color[4];
uint32_t flags; //index to light texture
float shadow_pixel_size;
float height;
float position[2];
float shadow_z_far_inv;
float shadow_y_ofs;
float atlas_rect[4];
};
static_assert(sizeof(LightUniform) % 16 == 0, "2D light UBO size must be a multiple of 16 bytes");
public:
enum {
BASE_UNIFORM_LOCATION = 0,
GLOBAL_UNIFORM_LOCATION = 1,
LIGHT_UNIFORM_LOCATION = 2,
INSTANCE_UNIFORM_LOCATION = 3,
MATERIAL_UNIFORM_LOCATION = 4,
};
struct StateBuffer {
float canvas_transform[16];
float screen_transform[16];
float canvas_normal_transform[16];
float canvas_modulate[4];
float screen_pixel_size[2];
float time;
uint32_t use_pixel_snap;
float sdf_to_tex[4];
float sdf_to_screen[2];
float screen_to_sdf[2];
uint32_t directional_light_count;
float tex_to_sdf;
uint32_t pad1;
uint32_t pad2;
};
static_assert(sizeof(StateBuffer) % 16 == 0, "2D state UBO size must be a multiple of 16 bytes");
struct PolygonBuffers {
GLuint vertex_buffer = 0;
GLuint vertex_array = 0;
GLuint index_buffer = 0;
int count = 0;
bool color_disabled = false;
Color color = Color(1.0, 1.0, 1.0, 1.0);
};
struct {
HashMap<PolygonID, PolygonBuffers> polygons;
PolygonID last_id = 0;
} polygon_buffers;
RendererCanvasRender::PolygonID request_polygon(const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), const Vector<int> &p_bones = Vector<int>(), const Vector<float> &p_weights = Vector<float>(), int p_count = -1) override;
void free_polygon(PolygonID p_polygon) override;
struct InstanceData {
float world[6];
float color_texture_pixel_size[2];
union {
//rect
struct {
float modulation[4];
union {
float msdf[4];
float ninepatch_margins[4];
};
float dst_rect[4];
float src_rect[4];
float pad[2];
};
//primitive
struct {
float points[6]; // vec2 points[3]
float uvs[6]; // vec2 points[3]
uint32_t colors[6]; // colors encoded as half
};
};
uint32_t flags;
uint32_t instance_uniforms_ofs;
uint32_t lights[4];
};
static_assert(sizeof(InstanceData) == 128, "2D instance data struct size must be 128 bytes");
struct Data {
GLuint canvas_quad_vertices;
GLuint canvas_quad_array;
GLuint indexed_quad_buffer;
GLuint indexed_quad_array;
GLuint particle_quad_vertices;
GLuint particle_quad_array;
GLuint ninepatch_vertices;
GLuint ninepatch_elements;
RID canvas_shader_default_version;
uint32_t max_lights_per_render = 256;
uint32_t max_lights_per_item = 16;
uint32_t max_instances_per_buffer = 16384;
uint32_t max_instance_buffer_size = 16384 * 128;
} data;
struct Batch {
// Position in the UBO measured in bytes
uint32_t start = 0;
uint32_t instance_count = 0;
uint32_t instance_buffer_index = 0;
RID tex;
RS::CanvasItemTextureFilter filter = RS::CANVAS_ITEM_TEXTURE_FILTER_MAX;
RS::CanvasItemTextureRepeat repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_MAX;
GLES3::CanvasShaderData::BlendMode blend_mode = GLES3::CanvasShaderData::BLEND_MODE_MIX;
Color blend_color = Color(1.0, 1.0, 1.0, 1.0);
Item *clip = nullptr;
RID material;
GLES3::CanvasMaterialData *material_data = nullptr;
uint64_t vertex_input_mask = RS::ARRAY_FORMAT_VERTEX | RS::ARRAY_FORMAT_COLOR | RS::ARRAY_FORMAT_TEX_UV;
uint64_t specialization = 0;
const Item::Command *command = nullptr;
Item::Command::Type command_type = Item::Command::TYPE_ANIMATION_SLICE; // Can default to any type that doesn't form a batch.
uint32_t primitive_points = 0;
uint32_t flags = 0;
uint32_t specular_shininess = 0.0;
bool lights_disabled = false;
};
// DataBuffer contains our per-frame data. I.e. the resources that are updated each frame.
// We track them and ensure that they don't get reused until at least 2 frames have passed
// to avoid the GPU stalling to wait for a resource to become available.
struct DataBuffer {
Vector<GLuint> instance_buffers;
GLuint light_ubo = 0;
GLuint state_ubo = 0;
uint64_t last_frame_used = -3;
GLsync fence = GLsync();
};
struct State {
LocalVector<DataBuffer> canvas_instance_data_buffers;
LocalVector<Batch> canvas_instance_batches;
uint32_t current_data_buffer_index = 0;
uint32_t current_instance_buffer_index = 0;
uint32_t current_batch_index = 0;
uint32_t last_item_index = 0;
InstanceData *instance_data_array = nullptr;
LightUniform *light_uniforms = nullptr;
GLuint shadow_texture = 0;
GLuint shadow_depth_buffer = 0;
GLuint shadow_fb = 0;
int shadow_texture_size = 2048;
bool using_directional_lights = false;
RID current_tex;
RS::CanvasItemTextureFilter current_filter_mode = RS::CANVAS_ITEM_TEXTURE_FILTER_MAX;
RS::CanvasItemTextureRepeat current_repeat_mode = RS::CANVAS_ITEM_TEXTURE_REPEAT_MAX;
bool transparent_render_target = false;
double time = 0.0;
RS::CanvasItemTextureFilter default_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT;
RS::CanvasItemTextureRepeat default_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT;
} state;
Item *items[MAX_RENDER_ITEMS];
RID default_canvas_texture;
RID default_canvas_group_material;
RID default_canvas_group_shader;
RID default_clip_children_material;
RID default_clip_children_shader;
typedef void Texture;
void canvas_begin(RID p_to_render_target, bool p_to_backbuffer, bool p_backbuffer_has_mipmaps);
//virtual void draw_window_margins(int *black_margin, RID *black_image) override;
void draw_lens_distortion_rect(const Rect2 &p_rect, float p_k1, float p_k2, const Vector2 &p_eye_center, float p_oversample);
void reset_canvas();
RID light_create() override;
void light_set_texture(RID p_rid, RID p_texture) override;
void light_set_use_shadow(RID p_rid, bool p_enable) override;
void light_update_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders, const Rect2 &p_light_rect) override;
void light_update_directional_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_cull_distance, const Rect2 &p_clip_rect, LightOccluderInstance *p_occluders) override;
void render_sdf(RID p_render_target, LightOccluderInstance *p_occluders) override;
RID occluder_polygon_create() override;
void occluder_polygon_set_shape(RID p_occluder, const Vector<Vector2> &p_points, bool p_closed) override;
void occluder_polygon_set_cull_mode(RID p_occluder, RS::CanvasOccluderPolygonCullMode p_mode) override;
void set_shadow_texture_size(int p_size) override;
bool free(RID p_rid) override;
void update() override;
void _bind_canvas_texture(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat);
void _prepare_canvas_texture(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, uint32_t &r_index, Size2 &r_texpixel_size);
void canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_list, const Transform2D &p_canvas_transform, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, bool &r_sdf_used, RenderingMethod::RenderInfo *r_render_info = nullptr) override;
void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool &r_sdf_used, bool p_to_backbuffer = false, RenderingMethod::RenderInfo *r_render_info = nullptr, bool p_backbuffer_has_mipmaps = false);
void _record_item_commands(const Item *p_item, RID p_render_target, const Transform2D &p_canvas_transform_inverse, Item *&current_clip, GLES3::CanvasShaderData::BlendMode p_blend_mode, Light *p_lights, uint32_t &r_index, bool &r_break_batch, bool &r_sdf_used, const Point2 &p_repeat_offset);
void _render_batch(Light *p_lights, uint32_t p_index, RenderingMethod::RenderInfo *r_render_info = nullptr);
bool _bind_material(GLES3::CanvasMaterialData *p_material_data, CanvasShaderGLES3::ShaderVariant p_variant, uint64_t p_specialization);
void _new_batch(bool &r_batch_broken);
void _add_to_batch(uint32_t &r_index, bool &r_batch_broken);
void _allocate_instance_data_buffer();
void _allocate_instance_buffer();
void _enable_attributes(uint32_t p_start, bool p_primitive, uint32_t p_rate = 1);
void set_time(double p_time);
virtual void set_debug_redraw(bool p_enabled, double p_time, const Color &p_color) override {
if (p_enabled) {
WARN_PRINT_ONCE("Debug CanvasItem Redraw is not available yet when using the Compatibility renderer.");
}
}
virtual uint32_t get_pipeline_compilations(RS::PipelineSource p_source) override { return 0; }
static RasterizerCanvasGLES3 *get_singleton();
RasterizerCanvasGLES3();
~RasterizerCanvasGLES3();
};
#endif // GLES3_ENABLED

View File

@@ -0,0 +1,529 @@
/**************************************************************************/
/* rasterizer_gles3.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. */
/**************************************************************************/
#include "rasterizer_gles3.h"
#include "storage/utilities.h"
#ifdef GLES3_ENABLED
#include "core/config/project_settings.h"
#include "core/io/dir_access.h"
#include "core/io/image.h"
#include "core/os/os.h"
#include "storage/texture_storage.h"
#define _EXT_DEBUG_OUTPUT_SYNCHRONOUS_ARB 0x8242
#define _EXT_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_ARB 0x8243
#define _EXT_DEBUG_CALLBACK_FUNCTION_ARB 0x8244
#define _EXT_DEBUG_CALLBACK_USER_PARAM_ARB 0x8245
#define _EXT_DEBUG_SOURCE_API_ARB 0x8246
#define _EXT_DEBUG_SOURCE_WINDOW_SYSTEM_ARB 0x8247
#define _EXT_DEBUG_SOURCE_SHADER_COMPILER_ARB 0x8248
#define _EXT_DEBUG_SOURCE_THIRD_PARTY_ARB 0x8249
#define _EXT_DEBUG_SOURCE_APPLICATION_ARB 0x824A
#define _EXT_DEBUG_SOURCE_OTHER_ARB 0x824B
#define _EXT_DEBUG_TYPE_ERROR_ARB 0x824C
#define _EXT_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB 0x824D
#define _EXT_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB 0x824E
#define _EXT_DEBUG_TYPE_PORTABILITY_ARB 0x824F
#define _EXT_DEBUG_TYPE_PERFORMANCE_ARB 0x8250
#define _EXT_DEBUG_TYPE_OTHER_ARB 0x8251
#define _EXT_DEBUG_TYPE_MARKER_ARB 0x8268
#define _EXT_MAX_DEBUG_MESSAGE_LENGTH_ARB 0x9143
#define _EXT_MAX_DEBUG_LOGGED_MESSAGES_ARB 0x9144
#define _EXT_DEBUG_LOGGED_MESSAGES_ARB 0x9145
#define _EXT_DEBUG_SEVERITY_HIGH_ARB 0x9146
#define _EXT_DEBUG_SEVERITY_MEDIUM_ARB 0x9147
#define _EXT_DEBUG_SEVERITY_LOW_ARB 0x9148
#define _EXT_DEBUG_OUTPUT 0x92E0
#ifndef GL_FRAMEBUFFER_SRGB
#define GL_FRAMEBUFFER_SRGB 0x8DB9
#endif
#ifndef GLAPIENTRY
#if defined(WINDOWS_ENABLED)
#define GLAPIENTRY APIENTRY
#else
#define GLAPIENTRY
#endif
#endif
#if !defined(IOS_ENABLED) && !defined(WEB_ENABLED)
// We include EGL below to get debug callback on GLES2 platforms,
// but EGL is not available on iOS or the web.
#define CAN_DEBUG
#endif
#include "platform_gl.h"
#if defined(MINGW_ENABLED) || defined(_MSC_VER)
#define strcpy strcpy_s
#endif
#ifdef WINDOWS_ENABLED
bool RasterizerGLES3::screen_flipped_y = false;
#endif
bool RasterizerGLES3::gles_over_gl = true;
void RasterizerGLES3::begin_frame(double frame_step) {
frame++;
delta = frame_step;
time_total += frame_step;
double time_roll_over = GLOBAL_GET_CACHED(double, "rendering/limits/time/time_rollover_secs");
time_total = Math::fmod(time_total, time_roll_over);
canvas->set_time(time_total);
scene->set_time(time_total, frame_step);
GLES3::Utilities *utils = GLES3::Utilities::get_singleton();
utils->_capture_timestamps_begin();
//scene->iteration();
}
void RasterizerGLES3::end_frame(bool p_swap_buffers) {
GLES3::Utilities *utils = GLES3::Utilities::get_singleton();
utils->capture_timestamps_end();
}
void RasterizerGLES3::gl_end_frame(bool p_swap_buffers) {
if (p_swap_buffers) {
DisplayServer::get_singleton()->swap_buffers();
} else {
glFinish();
}
}
void RasterizerGLES3::clear_depth(float p_depth) {
#ifdef GL_API_ENABLED
if (is_gles_over_gl()) {
glClearDepth(p_depth);
}
#endif // GL_API_ENABLED
#ifdef GLES_API_ENABLED
if (!is_gles_over_gl()) {
glClearDepthf(p_depth);
}
#endif // GLES_API_ENABLED
}
void RasterizerGLES3::clear_stencil(int32_t p_stencil) {
glClearStencil(p_stencil);
}
#ifdef CAN_DEBUG
static void GLAPIENTRY _gl_debug_print(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const GLvoid *userParam) {
// These are ultimately annoying, so removing for now.
if (type == _EXT_DEBUG_TYPE_OTHER_ARB || type == _EXT_DEBUG_TYPE_PERFORMANCE_ARB || type == _EXT_DEBUG_TYPE_MARKER_ARB) {
return;
}
char debSource[256], debType[256], debSev[256];
if (source == _EXT_DEBUG_SOURCE_API_ARB) {
strcpy(debSource, "OpenGL");
} else if (source == _EXT_DEBUG_SOURCE_WINDOW_SYSTEM_ARB) {
strcpy(debSource, "Windows");
} else if (source == _EXT_DEBUG_SOURCE_SHADER_COMPILER_ARB) {
strcpy(debSource, "Shader Compiler");
} else if (source == _EXT_DEBUG_SOURCE_THIRD_PARTY_ARB) {
strcpy(debSource, "Third Party");
} else if (source == _EXT_DEBUG_SOURCE_APPLICATION_ARB) {
strcpy(debSource, "Application");
} else if (source == _EXT_DEBUG_SOURCE_OTHER_ARB) {
strcpy(debSource, "Other");
} else {
ERR_FAIL_MSG(vformat("GL ERROR: Invalid or unhandled source '%d' in debug callback.", source));
}
if (type == _EXT_DEBUG_TYPE_ERROR_ARB) {
strcpy(debType, "Error");
} else if (type == _EXT_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB) {
strcpy(debType, "Deprecated behavior");
} else if (type == _EXT_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB) {
strcpy(debType, "Undefined behavior");
} else if (type == _EXT_DEBUG_TYPE_PORTABILITY_ARB) {
strcpy(debType, "Portability");
} else {
ERR_FAIL_MSG(vformat("GL ERROR: Invalid or unhandled type '%d' in debug callback.", type));
}
if (severity == _EXT_DEBUG_SEVERITY_HIGH_ARB) {
strcpy(debSev, "High");
} else if (severity == _EXT_DEBUG_SEVERITY_MEDIUM_ARB) {
strcpy(debSev, "Medium");
} else if (severity == _EXT_DEBUG_SEVERITY_LOW_ARB) {
strcpy(debSev, "Low");
} else {
ERR_FAIL_MSG(vformat("GL ERROR: Invalid or unhandled severity '%d' in debug callback.", severity));
}
String output = String() + "GL ERROR: Source: " + debSource + "\tType: " + debType + "\tID: " + itos(id) + "\tSeverity: " + debSev + "\tMessage: " + message;
ERR_PRINT(output);
}
#endif
typedef void(GLAPIENTRY *DEBUGPROCARB)(GLenum source,
GLenum type,
GLuint id,
GLenum severity,
GLsizei length,
const char *message,
const void *userParam);
typedef void(GLAPIENTRY *DebugMessageCallbackARB)(DEBUGPROCARB callback, const void *userParam);
void RasterizerGLES3::initialize() {
Engine::get_singleton()->print_header(vformat("OpenGL API %s - Compatibility - Using Device: %s - %s", RS::get_singleton()->get_video_adapter_api_version(), RS::get_singleton()->get_video_adapter_vendor(), RS::get_singleton()->get_video_adapter_name()));
}
void RasterizerGLES3::finalize() {
memdelete(scene);
memdelete(canvas);
memdelete(gi);
memdelete(fog);
memdelete(post_effects);
memdelete(glow);
memdelete(cubemap_filter);
memdelete(copy_effects);
memdelete(feed_effects);
memdelete(light_storage);
memdelete(particles_storage);
memdelete(mesh_storage);
memdelete(material_storage);
memdelete(texture_storage);
memdelete(utilities);
memdelete(config);
}
RasterizerGLES3 *RasterizerGLES3::singleton = nullptr;
#ifdef EGL_ENABLED
void *_egl_load_function_wrapper(const char *p_name) {
return (void *)eglGetProcAddress(p_name);
}
#endif
RasterizerGLES3::RasterizerGLES3() {
singleton = this;
#ifdef GLAD_ENABLED
bool glad_loaded = false;
#ifdef EGL_ENABLED
// There should be a more flexible system for getting the GL pointer, as
// different DisplayServers can have different ways. We can just use the GLAD
// version global to see if it loaded for now though, otherwise we fall back to
// the generic loader below.
#if defined(EGL_STATIC)
bool has_egl = true;
#else
bool has_egl = (eglGetProcAddress != nullptr);
#endif
if (gles_over_gl) {
if (has_egl && !glad_loaded && gladLoadGL((GLADloadfunc)&_egl_load_function_wrapper)) {
glad_loaded = true;
}
} else {
if (has_egl && !glad_loaded && gladLoadGLES2((GLADloadfunc)&_egl_load_function_wrapper)) {
glad_loaded = true;
}
}
#endif // EGL_ENABLED
if (gles_over_gl) {
if (!glad_loaded && gladLoaderLoadGL()) {
glad_loaded = true;
}
} else {
if (!glad_loaded && gladLoaderLoadGLES2()) {
glad_loaded = true;
}
}
// FIXME this is an early return from a constructor. Any other code using this instance will crash or the finalizer will crash, because none of
// the members of this instance are initialized, so this just makes debugging harder. It should either crash here intentionally,
// or we need to actually test for this situation before constructing this.
ERR_FAIL_COND_MSG(!glad_loaded, "Error initializing GLAD.");
if (gles_over_gl) {
if (OS::get_singleton()->is_stdout_verbose()) {
if (GLAD_GL_ARB_debug_output) {
glEnable(_EXT_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
glDebugMessageCallbackARB((GLDEBUGPROCARB)_gl_debug_print, nullptr);
glEnable(_EXT_DEBUG_OUTPUT);
} else {
print_line("OpenGL debugging not supported!");
}
}
}
#endif // GLAD_ENABLED
// For debugging
#ifdef CAN_DEBUG
#ifdef GL_API_ENABLED
if (gles_over_gl) {
if (OS::get_singleton()->is_stdout_verbose() && GLAD_GL_ARB_debug_output) {
glDebugMessageControlARB(_EXT_DEBUG_SOURCE_API_ARB, _EXT_DEBUG_TYPE_ERROR_ARB, _EXT_DEBUG_SEVERITY_HIGH_ARB, 0, nullptr, GL_TRUE);
glDebugMessageControlARB(_EXT_DEBUG_SOURCE_API_ARB, _EXT_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB, _EXT_DEBUG_SEVERITY_HIGH_ARB, 0, nullptr, GL_TRUE);
glDebugMessageControlARB(_EXT_DEBUG_SOURCE_API_ARB, _EXT_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB, _EXT_DEBUG_SEVERITY_HIGH_ARB, 0, nullptr, GL_TRUE);
glDebugMessageControlARB(_EXT_DEBUG_SOURCE_API_ARB, _EXT_DEBUG_TYPE_PORTABILITY_ARB, _EXT_DEBUG_SEVERITY_HIGH_ARB, 0, nullptr, GL_TRUE);
glDebugMessageControlARB(_EXT_DEBUG_SOURCE_API_ARB, _EXT_DEBUG_TYPE_PERFORMANCE_ARB, _EXT_DEBUG_SEVERITY_HIGH_ARB, 0, nullptr, GL_TRUE);
glDebugMessageControlARB(_EXT_DEBUG_SOURCE_API_ARB, _EXT_DEBUG_TYPE_OTHER_ARB, _EXT_DEBUG_SEVERITY_HIGH_ARB, 0, nullptr, GL_TRUE);
}
}
#endif // GL_API_ENABLED
#ifdef GLES_API_ENABLED
if (!gles_over_gl) {
if (OS::get_singleton()->is_stdout_verbose()) {
DebugMessageCallbackARB callback = (DebugMessageCallbackARB)eglGetProcAddress("glDebugMessageCallback");
if (!callback) {
callback = (DebugMessageCallbackARB)eglGetProcAddress("glDebugMessageCallbackKHR");
}
if (callback) {
print_line("godot: ENABLING GL DEBUG");
glEnable(_EXT_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
callback((DEBUGPROCARB)_gl_debug_print, nullptr);
glEnable(_EXT_DEBUG_OUTPUT);
}
}
}
#endif // GLES_API_ENABLED
#endif // CAN_DEBUG
{
String shader_cache_dir = Engine::get_singleton()->get_shader_cache_path();
if (shader_cache_dir.is_empty()) {
shader_cache_dir = "user://";
}
Ref<DirAccess> da = DirAccess::open(shader_cache_dir);
if (da.is_null()) {
ERR_PRINT("Can't create shader cache folder, no shader caching will happen: " + shader_cache_dir);
} else {
Error err = da->change_dir("shader_cache");
if (err != OK) {
err = da->make_dir("shader_cache");
}
if (err != OK) {
ERR_PRINT("Can't create shader cache folder, no shader caching will happen: " + shader_cache_dir);
} else {
shader_cache_dir = shader_cache_dir.path_join("shader_cache");
bool shader_cache_enabled = GLOBAL_GET("rendering/shader_compiler/shader_cache/enabled");
if (!Engine::get_singleton()->is_editor_hint() && !shader_cache_enabled) {
shader_cache_dir = String(); //disable only if not editor
}
if (!shader_cache_dir.is_empty()) {
ShaderGLES3::set_shader_cache_dir(shader_cache_dir);
}
}
}
}
// OpenGL needs to be initialized before initializing the Rasterizers
config = memnew(GLES3::Config);
utilities = memnew(GLES3::Utilities);
texture_storage = memnew(GLES3::TextureStorage);
material_storage = memnew(GLES3::MaterialStorage);
mesh_storage = memnew(GLES3::MeshStorage);
particles_storage = memnew(GLES3::ParticlesStorage);
light_storage = memnew(GLES3::LightStorage);
copy_effects = memnew(GLES3::CopyEffects);
cubemap_filter = memnew(GLES3::CubemapFilter);
glow = memnew(GLES3::Glow);
post_effects = memnew(GLES3::PostEffects);
feed_effects = memnew(GLES3::FeedEffects);
gi = memnew(GLES3::GI);
fog = memnew(GLES3::Fog);
canvas = memnew(RasterizerCanvasGLES3());
scene = memnew(RasterizerSceneGLES3());
// Disable OpenGL linear to sRGB conversion, because Godot will always do this conversion itself.
if (config->srgb_framebuffer_supported) {
glDisable(GL_FRAMEBUFFER_SRGB);
}
}
RasterizerGLES3::~RasterizerGLES3() {
}
void RasterizerGLES3::_blit_render_target_to_screen(DisplayServer::WindowID p_screen, const BlitToScreen &p_blit, bool p_first) {
GLES3::RenderTarget *rt = GLES3::TextureStorage::get_singleton()->get_render_target(p_blit.render_target);
ERR_FAIL_NULL(rt);
// We normally render to the render target upside down, so flip Y when blitting to the screen.
bool flip_y = true;
bool linear_to_srgb = false;
if (rt->overridden.color.is_valid()) {
// If we've overridden the render target's color texture, that means we
// didn't render upside down, so we don't need to flip it.
// We're probably rendering directly to an XR device.
flip_y = false;
// It is 99% likely our texture uses the GL_SRGB8_ALPHA8 texture format in
// which case we have a GPU sRGB to Linear conversion on texture read.
// We need to counter this.
// Unfortunately we do not have an API to check this as Godot does not
// track this.
linear_to_srgb = true;
}
#ifdef WINDOWS_ENABLED
if (screen_flipped_y) {
flip_y = !flip_y;
}
#endif
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
if (p_first) {
if (p_blit.dst_rect.position != Vector2() || p_blit.dst_rect.size != rt->size) {
// Viewport doesn't cover entire window so clear window to black before blitting.
// Querying the actual window size from the DisplayServer would deadlock in separate render thread mode,
// so let's set the biggest viewport the implementation supports, to be sure the window is fully covered.
Size2i max_vp = GLES3::Utilities::get_singleton()->get_maximum_viewport_size();
glViewport(0, 0, max_vp[0], max_vp[1]);
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
}
}
Vector2 screen_rect_end = p_blit.dst_rect.get_end();
Vector2 p1 = Vector2(p_blit.dst_rect.position.x, flip_y ? screen_rect_end.y : p_blit.dst_rect.position.y);
Vector2 p2 = Vector2(screen_rect_end.x, flip_y ? p_blit.dst_rect.position.y : screen_rect_end.y);
Vector2 size = p2 - p1;
Rect2 screenrect = Rect2(Vector2(0.0, flip_y ? 1.0 : 0.0), Vector2(1.0, flip_y ? -1.0 : 1.0));
glViewport(int(MIN(p1.x, p2.x)), int(MIN(p1.y, p2.y)), Math::abs(size.x), Math::abs(size.y));
glActiveTexture(GL_TEXTURE0);
GLenum target = rt->view_count > 1 ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D;
glBindTexture(target, rt->color);
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glDisable(GL_CULL_FACE);
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ZERO);
if (p_blit.lens_distortion.apply && (p_blit.lens_distortion.k1 != 0.0 || p_blit.lens_distortion.k2)) {
copy_effects->copy_with_lens_distortion(screenrect, p_blit.multi_view.use_layer ? p_blit.multi_view.layer : 0, p_blit.lens_distortion.eye_center, p_blit.lens_distortion.k1, p_blit.lens_distortion.k2, p_blit.lens_distortion.upscale, p_blit.lens_distortion.aspect_ratio, linear_to_srgb);
} else if (rt->view_count > 1) {
copy_effects->copy_to_rect_3d(screenrect, p_blit.multi_view.use_layer ? p_blit.multi_view.layer : 0, GLES3::Texture::TYPE_LAYERED, 0.0, linear_to_srgb);
} else {
copy_effects->copy_to_rect(screenrect, linear_to_srgb);
}
glBindTexture(GL_TEXTURE_2D, 0);
}
// is this p_screen useless in a multi window environment?
void RasterizerGLES3::blit_render_targets_to_screen(DisplayServer::WindowID p_screen, const BlitToScreen *p_render_targets, int p_amount) {
for (int i = 0; i < p_amount; i++) {
_blit_render_target_to_screen(p_screen, p_render_targets[i], i == 0);
}
}
void RasterizerGLES3::set_boot_image(const Ref<Image> &p_image, const Color &p_color, bool p_scale, bool p_use_filter) {
if (p_image.is_null() || p_image->is_empty()) {
return;
}
Size2i win_size = DisplayServer::get_singleton()->window_get_size();
glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
glViewport(0, 0, win_size.width, win_size.height);
glEnable(GL_BLEND);
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE);
glDepthMask(GL_FALSE);
glClearColor(p_color.r, p_color.g, p_color.b, OS::get_singleton()->is_layered_allowed() ? p_color.a : 1.0);
glClear(GL_COLOR_BUFFER_BIT);
RID texture = texture_storage->texture_allocate();
texture_storage->texture_2d_initialize(texture, p_image);
Rect2 imgrect(0, 0, p_image->get_width(), p_image->get_height());
Rect2 screenrect;
if (p_scale) {
if (win_size.width > win_size.height) {
//scale horizontally
screenrect.size.y = win_size.height;
screenrect.size.x = imgrect.size.x * win_size.height / imgrect.size.y;
screenrect.position.x = (win_size.width - screenrect.size.x) / 2;
} else {
//scale vertically
screenrect.size.x = win_size.width;
screenrect.size.y = imgrect.size.y * win_size.width / imgrect.size.x;
screenrect.position.y = (win_size.height - screenrect.size.y) / 2;
}
} else {
screenrect = imgrect;
screenrect.position += ((Size2(win_size.width, win_size.height) - screenrect.size) / 2.0).floor();
}
#ifdef WINDOWS_ENABLED
if (!screen_flipped_y)
#endif
{
// Flip Y.
screenrect.position.y = win_size.y - screenrect.position.y;
screenrect.size.y = -screenrect.size.y;
}
// Normalize texture coordinates to window size.
screenrect.position /= win_size;
screenrect.size /= win_size;
GLES3::Texture *t = texture_storage->get_texture(texture);
t->gl_set_filter(p_use_filter ? RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR : RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, t->tex_id);
copy_effects->copy_to_rect(screenrect);
glBindTexture(GL_TEXTURE_2D, 0);
gl_end_frame(true);
texture_storage->texture_free(texture);
}
#endif // GLES3_ENABLED

View File

@@ -0,0 +1,143 @@
/**************************************************************************/
/* rasterizer_gles3.h */
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#ifdef GLES3_ENABLED
#include "effects/copy_effects.h"
#include "effects/cubemap_filter.h"
#include "effects/feed_effects.h"
#include "effects/glow.h"
#include "effects/post_effects.h"
#include "environment/fog.h"
#include "environment/gi.h"
#include "rasterizer_canvas_gles3.h"
#include "rasterizer_scene_gles3.h"
#include "servers/rendering/renderer_compositor.h"
#include "storage/config.h"
#include "storage/light_storage.h"
#include "storage/material_storage.h"
#include "storage/mesh_storage.h"
#include "storage/particles_storage.h"
#include "storage/texture_storage.h"
#include "storage/utilities.h"
class RasterizerGLES3 : public RendererCompositor {
private:
uint64_t frame = 1;
float delta = 0;
double time_total = 0.0;
#ifdef WINDOWS_ENABLED
static bool screen_flipped_y;
#endif
static bool gles_over_gl;
protected:
GLES3::Config *config = nullptr;
GLES3::Utilities *utilities = nullptr;
GLES3::TextureStorage *texture_storage = nullptr;
GLES3::MaterialStorage *material_storage = nullptr;
GLES3::MeshStorage *mesh_storage = nullptr;
GLES3::ParticlesStorage *particles_storage = nullptr;
GLES3::LightStorage *light_storage = nullptr;
GLES3::GI *gi = nullptr;
GLES3::Fog *fog = nullptr;
GLES3::CopyEffects *copy_effects = nullptr;
GLES3::CubemapFilter *cubemap_filter = nullptr;
GLES3::Glow *glow = nullptr;
GLES3::PostEffects *post_effects = nullptr;
GLES3::FeedEffects *feed_effects = nullptr;
RasterizerCanvasGLES3 *canvas = nullptr;
RasterizerSceneGLES3 *scene = nullptr;
static RasterizerGLES3 *singleton;
void _blit_render_target_to_screen(DisplayServer::WindowID p_screen, const BlitToScreen &p_blit, bool p_first = true);
public:
RendererUtilities *get_utilities() { return utilities; }
RendererLightStorage *get_light_storage() { return light_storage; }
RendererMaterialStorage *get_material_storage() { return material_storage; }
RendererMeshStorage *get_mesh_storage() { return mesh_storage; }
RendererParticlesStorage *get_particles_storage() { return particles_storage; }
RendererTextureStorage *get_texture_storage() { return texture_storage; }
RendererGI *get_gi() { return gi; }
RendererFog *get_fog() { return fog; }
RendererCanvasRender *get_canvas() { return canvas; }
RendererSceneRender *get_scene() { return scene; }
void set_boot_image(const Ref<Image> &p_image, const Color &p_color, bool p_scale, bool p_use_filter = true);
void initialize();
void begin_frame(double frame_step);
void blit_render_targets_to_screen(DisplayServer::WindowID p_screen, const BlitToScreen *p_render_targets, int p_amount);
bool is_opengl() { return true; }
void gl_end_frame(bool p_swap_buffers);
void end_frame(bool p_swap_buffers);
void finalize();
static RendererCompositor *_create_current() {
return memnew(RasterizerGLES3);
}
static bool is_gles_over_gl() { return gles_over_gl; }
static void clear_depth(float p_depth);
static void clear_stencil(int32_t p_stencil);
static void make_current(bool p_gles_over_gl) {
gles_over_gl = p_gles_over_gl;
OS::get_singleton()->set_gles_over_gl(gles_over_gl);
_create_func = _create_current;
low_end = true;
}
#ifdef WINDOWS_ENABLED
static void set_screen_flipped_y(bool p_flipped) {
screen_flipped_y = p_flipped;
}
#endif
_ALWAYS_INLINE_ uint64_t get_frame_number() const { return frame; }
_ALWAYS_INLINE_ double get_frame_delta_time() const { return delta; }
_ALWAYS_INLINE_ double get_total_time() const { return time_total; }
_ALWAYS_INLINE_ bool can_create_resources_async() const { return false; }
static RasterizerGLES3 *get_singleton() { return singleton; }
RasterizerGLES3();
~RasterizerGLES3();
};
#endif // GLES3_ENABLED

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,948 @@
/**************************************************************************/
/* rasterizer_scene_gles3.h */
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#include "platform_gl.h"
#ifdef GLES3_ENABLED
#include "core/math/projection.h"
#include "core/templates/paged_allocator.h"
#include "core/templates/rid_owner.h"
#include "core/templates/self_list.h"
#include "drivers/gles3/shaders/effects/cubemap_filter.glsl.gen.h"
#include "drivers/gles3/shaders/sky.glsl.gen.h"
#include "scene/resources/mesh.h"
#include "servers/rendering/renderer_compositor.h"
#include "servers/rendering/renderer_scene_render.h"
#include "servers/rendering_server.h"
#include "shader_gles3.h"
#include "storage/light_storage.h"
#include "storage/material_storage.h"
#include "storage/render_scene_buffers_gles3.h"
#include "storage/utilities.h"
enum RenderListType {
RENDER_LIST_OPAQUE, //used for opaque objects
RENDER_LIST_ALPHA, //used for transparent objects
RENDER_LIST_SECONDARY, //used for shadows and other objects
RENDER_LIST_MAX
};
enum PassMode {
PASS_MODE_COLOR,
PASS_MODE_COLOR_TRANSPARENT,
PASS_MODE_SHADOW,
PASS_MODE_DEPTH,
PASS_MODE_MATERIAL,
};
// These should share as much as possible with SkyUniform Location
enum SceneUniformLocation {
SCENE_TONEMAP_UNIFORM_LOCATION,
SCENE_GLOBALS_UNIFORM_LOCATION,
SCENE_DATA_UNIFORM_LOCATION,
SCENE_MATERIAL_UNIFORM_LOCATION,
SCENE_EMPTY1, // Unused, put here to avoid conflicts with SKY_DIRECTIONAL_LIGHT_UNIFORM_LOCATION.
SCENE_OMNILIGHT_UNIFORM_LOCATION,
SCENE_SPOTLIGHT_UNIFORM_LOCATION,
SCENE_DIRECTIONAL_LIGHT_UNIFORM_LOCATION,
SCENE_MULTIVIEW_UNIFORM_LOCATION,
SCENE_POSITIONAL_SHADOW_UNIFORM_LOCATION,
SCENE_DIRECTIONAL_SHADOW_UNIFORM_LOCATION,
SCENE_EMPTY2, // Unused, put here to avoid conflicts with SKY_MULTIVIEW_UNIFORM_LOCATION.
};
enum SkyUniformLocation {
SKY_TONEMAP_UNIFORM_LOCATION,
SKY_GLOBALS_UNIFORM_LOCATION,
SKY_EMPTY1, // Unused, put here to avoid conflicts with SCENE_DATA_UNIFORM_LOCATION.
SKY_MATERIAL_UNIFORM_LOCATION,
SKY_DIRECTIONAL_LIGHT_UNIFORM_LOCATION,
SKY_EMPTY2, // Unused, put here to avoid conflicts with SCENE_OMNILIGHT_UNIFORM_LOCATION.
SKY_EMPTY3, // Unused, put here to avoid conflicts with SCENE_SPOTLIGHT_UNIFORM_LOCATION.
SKY_EMPTY4, // Unused, put here to avoid conflicts with SCENE_DIRECTIONAL_LIGHT_UNIFORM_LOCATION.
SKY_EMPTY5, // Unused, put here to avoid conflicts with SCENE_MULTIVIEW_UNIFORM_LOCATION.
SKY_EMPTY6, // Unused, put here to avoid conflicts with SCENE_POSITIONAL_SHADOW_UNIFORM_LOCATION.
SKY_EMPTY7, // Unused, put here to avoid conflicts with SCENE_DIRECTIONAL_SHADOW_UNIFORM_LOCATION.
SKY_MULTIVIEW_UNIFORM_LOCATION,
};
struct RenderDataGLES3 {
Ref<RenderSceneBuffersGLES3> render_buffers;
bool transparent_bg = false;
Rect2i render_region;
Transform3D cam_transform;
Transform3D inv_cam_transform;
Projection cam_projection;
bool cam_orthogonal = false;
bool cam_frustum = false;
uint32_t camera_visible_layers = 0xFFFFFFFF;
// For billboards to cast correct shadows.
Transform3D main_cam_transform;
// For stereo rendering
uint32_t view_count = 1;
Vector3 view_eye_offset[RendererSceneRender::MAX_RENDER_VIEWS];
Projection view_projection[RendererSceneRender::MAX_RENDER_VIEWS];
float z_near = 0.0;
float z_far = 0.0;
const PagedArray<RenderGeometryInstance *> *instances = nullptr;
const PagedArray<RID> *lights = nullptr;
const PagedArray<RID> *reflection_probes = nullptr;
RID environment;
RID camera_attributes;
RID shadow_atlas;
RID reflection_probe;
int reflection_probe_pass = 0;
float lod_distance_multiplier = 0.0;
float screen_mesh_lod_threshold = 0.0;
uint32_t directional_light_count = 0;
uint32_t directional_shadow_count = 0;
uint32_t spot_light_count = 0;
uint32_t omni_light_count = 0;
float luminance_multiplier = 1.0;
RenderingMethod::RenderInfo *render_info = nullptr;
/* Shadow data */
const RendererSceneRender::RenderShadowData *render_shadows = nullptr;
int render_shadow_count = 0;
};
class RasterizerCanvasGLES3;
class RasterizerSceneGLES3 : public RendererSceneRender {
private:
static RasterizerSceneGLES3 *singleton;
RS::ViewportDebugDraw debug_draw = RS::VIEWPORT_DEBUG_DRAW_DISABLED;
uint64_t scene_pass = 0;
template <typename T>
struct InstanceSort {
float depth;
T *instance = nullptr;
bool operator<(const InstanceSort &p_sort) const {
return depth < p_sort.depth;
}
};
struct SceneGlobals {
RID shader_default_version;
RID default_material;
RID default_shader;
RID overdraw_material;
RID overdraw_shader;
} scene_globals;
GLES3::SceneMaterialData *default_material_data_ptr = nullptr;
GLES3::SceneMaterialData *overdraw_material_data_ptr = nullptr;
/* LIGHT INSTANCE */
struct LightData {
float position[3];
float inv_radius;
float direction[3]; // Only used by SpotLight
float size;
float color[3];
float attenuation;
float inv_spot_attenuation;
float cos_spot_angle;
float specular_amount;
float shadow_opacity;
float pad[3];
uint32_t bake_mode;
};
static_assert(sizeof(LightData) % 16 == 0, "LightData size must be a multiple of 16 bytes");
struct DirectionalLightData {
float direction[3];
float energy;
float color[3];
float size;
uint32_t enabled; // For use by SkyShaders
uint32_t bake_mode;
float shadow_opacity;
float specular;
};
static_assert(sizeof(DirectionalLightData) % 16 == 0, "DirectionalLightData size must be a multiple of 16 bytes");
struct ShadowData {
float shadow_matrix[16];
float light_position[3];
float shadow_normal_bias;
float pad[3];
float shadow_atlas_pixel_size;
};
static_assert(sizeof(ShadowData) % 16 == 0, "ShadowData size must be a multiple of 16 bytes");
struct DirectionalShadowData {
float direction[3];
float shadow_atlas_pixel_size;
float shadow_normal_bias[4];
float shadow_split_offsets[4];
float shadow_matrices[4][16];
float fade_from;
float fade_to;
uint32_t blend_splits; // Not exposed to the shader.
uint32_t pad;
};
static_assert(sizeof(DirectionalShadowData) % 16 == 0, "DirectionalShadowData size must be a multiple of 16 bytes");
class GeometryInstanceGLES3;
// Cached data for drawing surfaces
struct GeometryInstanceSurface {
enum {
FLAG_PASS_DEPTH = 1,
FLAG_PASS_OPAQUE = 2,
FLAG_PASS_ALPHA = 4,
FLAG_PASS_SHADOW = 8,
FLAG_USES_SHARED_SHADOW_MATERIAL = 128,
FLAG_USES_SCREEN_TEXTURE = 2048,
FLAG_USES_DEPTH_TEXTURE = 4096,
FLAG_USES_NORMAL_TEXTURE = 8192,
FLAG_USES_DOUBLE_SIDED_SHADOWS = 16384,
FLAG_USES_STENCIL = 32768,
};
union {
struct {
uint64_t sort_key1;
uint64_t sort_key2;
};
struct {
uint64_t lod_index : 8;
uint64_t surface_index : 8;
uint64_t geometry_id : 32;
uint64_t material_id_low : 16;
uint64_t material_id_hi : 16;
uint64_t shader_id : 32;
uint64_t uses_softshadow : 1;
uint64_t uses_projector : 1;
uint64_t uses_forward_gi : 1;
uint64_t uses_lightmap : 1;
uint64_t depth_layer : 4;
uint64_t priority : 8;
};
} sort;
RS::PrimitiveType primitive = RS::PRIMITIVE_MAX;
uint32_t flags = 0;
uint32_t surface_index = 0;
uint32_t lod_index = 0;
uint32_t index_count = 0;
int32_t light_pass_index = -1;
bool finished_base_pass = false;
void *surface = nullptr;
GLES3::SceneShaderData *shader = nullptr;
GLES3::SceneMaterialData *material = nullptr;
void *surface_shadow = nullptr;
GLES3::SceneShaderData *shader_shadow = nullptr;
GLES3::SceneMaterialData *material_shadow = nullptr;
GeometryInstanceSurface *next = nullptr;
GeometryInstanceGLES3 *owner = nullptr;
};
struct GeometryInstanceLightmapSH {
Color sh[9];
};
class GeometryInstanceGLES3 : public RenderGeometryInstanceBase {
public:
//used during rendering
bool store_transform_cache = true;
int32_t instance_count = 0;
bool can_sdfgi = false;
bool using_projectors = false;
bool using_softshadows = false;
struct LightPass {
int32_t light_id = -1; // Position in the light uniform buffer.
int32_t shadow_id = -1; // Position in the shadow uniform buffer.
RID light_instance_rid;
bool is_omni = false;
};
LocalVector<LightPass> light_passes;
uint32_t paired_omni_light_count = 0;
uint32_t paired_spot_light_count = 0;
LocalVector<RID> paired_omni_lights;
LocalVector<RID> paired_spot_lights;
LocalVector<uint32_t> omni_light_gl_cache;
LocalVector<uint32_t> spot_light_gl_cache;
LocalVector<RID> paired_reflection_probes;
LocalVector<RID> reflection_probe_rid_cache;
LocalVector<Transform3D> reflection_probes_local_transform_cache;
RID lightmap_instance;
Rect2 lightmap_uv_scale;
uint32_t lightmap_slice_index;
GeometryInstanceLightmapSH *lightmap_sh = nullptr;
// Used during setup.
GeometryInstanceSurface *surface_caches = nullptr;
SelfList<GeometryInstanceGLES3> dirty_list_element;
GeometryInstanceGLES3() :
dirty_list_element(this) {}
virtual void _mark_dirty() override;
virtual void set_use_lightmap(RID p_lightmap_instance, const Rect2 &p_lightmap_uv_scale, int p_lightmap_slice_index) override;
virtual void set_lightmap_capture(const Color *p_sh9) override;
virtual void pair_light_instances(const RID *p_light_instances, uint32_t p_light_instance_count) override;
virtual void pair_reflection_probe_instances(const RID *p_reflection_probe_instances, uint32_t p_reflection_probe_instance_count) override;
virtual void pair_decal_instances(const RID *p_decal_instances, uint32_t p_decal_instance_count) override {}
virtual void pair_voxel_gi_instances(const RID *p_voxel_gi_instances, uint32_t p_voxel_gi_instance_count) override {}
virtual void set_softshadow_projector_pairing(bool p_softshadow, bool p_projector) override {}
};
enum {
INSTANCE_DATA_FLAGS_DYNAMIC = 1 << 3,
INSTANCE_DATA_FLAGS_NON_UNIFORM_SCALE = 1 << 4,
INSTANCE_DATA_FLAG_USE_GI_BUFFERS = 1 << 5,
INSTANCE_DATA_FLAG_USE_LIGHTMAP_CAPTURE = 1 << 7,
INSTANCE_DATA_FLAG_USE_LIGHTMAP = 1 << 8,
INSTANCE_DATA_FLAG_USE_SH_LIGHTMAP = 1 << 9,
INSTANCE_DATA_FLAG_USE_VOXEL_GI = 1 << 10,
INSTANCE_DATA_FLAG_PARTICLES = 1 << 11,
INSTANCE_DATA_FLAG_MULTIMESH = 1 << 12,
INSTANCE_DATA_FLAG_MULTIMESH_FORMAT_2D = 1 << 13,
INSTANCE_DATA_FLAG_MULTIMESH_HAS_COLOR = 1 << 14,
INSTANCE_DATA_FLAG_MULTIMESH_HAS_CUSTOM_DATA = 1 << 15,
};
static void _geometry_instance_dependency_changed(Dependency::DependencyChangedNotification p_notification, DependencyTracker *p_tracker);
static void _geometry_instance_dependency_deleted(const RID &p_dependency, DependencyTracker *p_tracker);
SelfList<GeometryInstanceGLES3>::List geometry_instance_dirty_list;
// Use PagedAllocator instead of RID to maximize performance
PagedAllocator<GeometryInstanceGLES3> geometry_instance_alloc;
PagedAllocator<GeometryInstanceSurface> geometry_instance_surface_alloc;
void _geometry_instance_add_surface_with_material(GeometryInstanceGLES3 *ginstance, uint32_t p_surface, GLES3::SceneMaterialData *p_material, uint32_t p_material_id, uint32_t p_shader_id, RID p_mesh);
void _geometry_instance_add_surface_with_material_chain(GeometryInstanceGLES3 *ginstance, uint32_t p_surface, GLES3::SceneMaterialData *p_material, RID p_mat_src, RID p_mesh);
void _geometry_instance_add_surface(GeometryInstanceGLES3 *ginstance, uint32_t p_surface, RID p_material, RID p_mesh);
void _geometry_instance_update(RenderGeometryInstance *p_geometry_instance);
void _update_dirty_geometry_instances();
struct SceneState {
struct UBO {
float projection_matrix[16];
float inv_projection_matrix[16];
float inv_view_matrix[16];
float view_matrix[16];
float main_cam_inv_view_matrix[16];
float viewport_size[2];
float screen_pixel_size[2];
float ambient_light_color_energy[4];
float ambient_color_sky_mix;
uint32_t pad2;
float emissive_exposure_normalization;
uint32_t use_ambient_light = 0;
uint32_t use_ambient_cubemap = 0;
uint32_t use_reflection_cubemap = 0;
float fog_aerial_perspective;
float time;
float radiance_inverse_xform[12];
uint32_t directional_light_count;
float z_far;
float z_near;
float IBL_exposure_normalization;
uint32_t fog_enabled;
uint32_t fog_mode;
float fog_density;
float fog_height;
float fog_height_density;
float fog_depth_curve;
float fog_sun_scatter;
float fog_depth_begin;
float fog_light_color[3];
float fog_depth_end;
float shadow_bias;
float luminance_multiplier;
uint32_t camera_visible_layers;
bool pancake_shadows;
};
static_assert(sizeof(UBO) % 16 == 0, "Scene UBO size must be a multiple of 16 bytes");
static_assert(sizeof(UBO) < 16384, "Scene UBO size must be 16384 bytes or smaller");
struct MultiviewUBO {
float projection_matrix_view[RendererSceneRender::MAX_RENDER_VIEWS][16];
float inv_projection_matrix_view[RendererSceneRender::MAX_RENDER_VIEWS][16];
float eye_offset[RendererSceneRender::MAX_RENDER_VIEWS][4];
};
static_assert(sizeof(MultiviewUBO) % 16 == 0, "Multiview UBO size must be a multiple of 16 bytes");
static_assert(sizeof(MultiviewUBO) < 16384, "MultiviewUBO size must be 16384 bytes or smaller");
struct TonemapUBO {
float exposure = 1.0;
float white = 1.0;
int32_t tonemapper = 0;
int32_t pad = 0;
int32_t pad2 = 0;
float brightness = 1.0;
float contrast = 1.0;
float saturation = 1.0;
};
static_assert(sizeof(TonemapUBO) % 16 == 0, "Tonemap UBO size must be a multiple of 16 bytes");
UBO ubo;
GLuint ubo_buffer = 0;
MultiviewUBO multiview_ubo;
GLuint multiview_buffer = 0;
GLuint tonemap_buffer = 0;
bool used_depth_prepass = false;
GLES3::SceneShaderData::BlendMode current_blend_mode = GLES3::SceneShaderData::BLEND_MODE_MIX;
RS::CullMode cull_mode = RS::CULL_MODE_BACK;
GLenum current_depth_function = GL_GEQUAL;
bool current_blend_enabled = false;
bool current_depth_draw_enabled = false;
bool current_depth_test_enabled = false;
bool current_scissor_test_enabled = false;
void reset_gl_state() {
glDisable(GL_BLEND);
current_blend_enabled = false;
glDisable(GL_SCISSOR_TEST);
current_scissor_test_enabled = false;
glCullFace(GL_BACK);
glEnable(GL_CULL_FACE);
cull_mode = RS::CULL_MODE_BACK;
glDepthMask(GL_FALSE);
current_depth_draw_enabled = false;
glDisable(GL_DEPTH_TEST);
current_depth_test_enabled = false;
glDepthFunc(GL_GEQUAL);
current_depth_function = GL_GEQUAL;
glDisable(GL_STENCIL_TEST);
current_stencil_test_enabled = false;
glStencilMask(255);
current_stencil_write_mask = 255;
glStencilFunc(GL_ALWAYS, 0, 255);
current_stencil_compare = GL_ALWAYS;
current_stencil_reference = 0;
current_stencil_compare_mask = 255;
}
void set_gl_cull_mode(RS::CullMode p_mode) {
if (cull_mode != p_mode) {
if (p_mode == RS::CULL_MODE_DISABLED) {
glDisable(GL_CULL_FACE);
} else {
if (cull_mode == RS::CULL_MODE_DISABLED) {
// Last time was disabled, so enable and set proper face.
glEnable(GL_CULL_FACE);
}
glCullFace(p_mode == RS::CULL_MODE_FRONT ? GL_FRONT : GL_BACK);
}
cull_mode = p_mode;
}
}
void enable_gl_blend(bool p_enabled) {
if (current_blend_enabled != p_enabled) {
if (p_enabled) {
glEnable(GL_BLEND);
} else {
glDisable(GL_BLEND);
}
current_blend_enabled = p_enabled;
}
}
void enable_gl_scissor_test(bool p_enabled) {
if (current_scissor_test_enabled != p_enabled) {
if (p_enabled) {
glEnable(GL_SCISSOR_TEST);
} else {
glDisable(GL_SCISSOR_TEST);
}
current_scissor_test_enabled = p_enabled;
}
}
void enable_gl_depth_draw(bool p_enabled) {
if (current_depth_draw_enabled != p_enabled) {
glDepthMask(p_enabled ? GL_TRUE : GL_FALSE);
current_depth_draw_enabled = p_enabled;
}
}
void enable_gl_depth_test(bool p_enabled) {
if (current_depth_test_enabled != p_enabled) {
if (p_enabled) {
glEnable(GL_DEPTH_TEST);
} else {
glDisable(GL_DEPTH_TEST);
}
current_depth_test_enabled = p_enabled;
}
}
void set_gl_depth_func(GLenum p_depth_func) {
if (current_depth_function != p_depth_func) {
glDepthFunc(p_depth_func);
current_depth_function = p_depth_func;
}
}
void enable_gl_stencil_test(bool p_enabled) {
if (current_stencil_test_enabled != p_enabled) {
if (p_enabled) {
glEnable(GL_STENCIL_TEST);
} else {
glDisable(GL_STENCIL_TEST);
}
current_stencil_test_enabled = p_enabled;
}
}
void set_gl_stencil_func(GLenum p_compare, GLint p_reference, GLenum p_compare_mask) {
if (current_stencil_compare != p_compare || current_stencil_reference != p_reference || current_stencil_compare_mask != p_compare_mask) {
glStencilFunc(p_compare, p_reference, p_compare_mask);
current_stencil_compare = p_compare;
current_stencil_reference = p_reference;
current_stencil_compare_mask = p_compare_mask;
}
}
void set_gl_stencil_write_mask(GLuint p_mask) {
if (current_stencil_write_mask != p_mask) {
glStencilMask(p_mask);
current_stencil_write_mask = p_mask;
}
}
void set_gl_stencil_op(GLenum p_op_fail, GLenum p_op_dpfail, GLenum p_op_dppass) {
if (current_stencil_op_fail != p_op_fail || current_stencil_op_dpfail != p_op_dpfail || current_stencil_op_dppass != p_op_dppass) {
glStencilOp(p_op_fail, p_op_dpfail, p_op_dppass);
current_stencil_op_fail = p_op_fail;
current_stencil_op_dpfail = p_op_dpfail;
current_stencil_op_dppass = p_op_dppass;
}
}
GLenum current_stencil_compare = GL_ALWAYS;
GLuint current_stencil_compare_mask = 255;
GLuint current_stencil_write_mask = 255;
GLint current_stencil_reference = 0;
GLenum current_stencil_op_fail = GL_KEEP;
GLenum current_stencil_op_dpfail = GL_KEEP;
GLenum current_stencil_op_dppass = GL_KEEP;
bool current_stencil_test_enabled = false;
bool texscreen_copied = false;
bool used_screen_texture = false;
bool used_normal_texture = false;
bool used_depth_texture = false;
bool used_opaque_stencil = false;
LightData *omni_lights = nullptr;
LightData *spot_lights = nullptr;
ShadowData *positional_shadows = nullptr;
InstanceSort<GLES3::LightInstance> *omni_light_sort;
InstanceSort<GLES3::LightInstance> *spot_light_sort;
GLuint omni_light_buffer = 0;
GLuint spot_light_buffer = 0;
GLuint positional_shadow_buffer = 0;
uint32_t omni_light_count = 0;
uint32_t spot_light_count = 0;
RS::ShadowQuality positional_shadow_quality = RS::ShadowQuality::SHADOW_QUALITY_SOFT_LOW;
DirectionalLightData *directional_lights = nullptr;
GLuint directional_light_buffer = 0;
DirectionalShadowData *directional_shadows = nullptr;
GLuint directional_shadow_buffer = 0;
RS::ShadowQuality directional_shadow_quality = RS::ShadowQuality::SHADOW_QUALITY_SOFT_LOW;
} scene_state;
struct RenderListParameters {
GeometryInstanceSurface **elements = nullptr;
int element_count = 0;
bool reverse_cull = false;
uint64_t spec_constant_base_flags = 0;
bool force_wireframe = false;
Vector2 uv_offset = Vector2(0, 0);
RenderListParameters(GeometryInstanceSurface **p_elements, int p_element_count, bool p_reverse_cull, uint64_t p_spec_constant_base_flags, bool p_force_wireframe = false, Vector2 p_uv_offset = Vector2()) {
elements = p_elements;
element_count = p_element_count;
reverse_cull = p_reverse_cull;
spec_constant_base_flags = p_spec_constant_base_flags;
force_wireframe = p_force_wireframe;
uv_offset = p_uv_offset;
}
};
struct RenderList {
LocalVector<GeometryInstanceSurface *> elements;
void clear() {
elements.clear();
}
//should eventually be replaced by radix
struct SortByKey {
_FORCE_INLINE_ bool operator()(const GeometryInstanceSurface *A, const GeometryInstanceSurface *B) const {
return (A->sort.sort_key2 == B->sort.sort_key2) ? (A->sort.sort_key1 < B->sort.sort_key1) : (A->sort.sort_key2 < B->sort.sort_key2);
}
};
void sort_by_key() {
SortArray<GeometryInstanceSurface *, SortByKey> sorter;
sorter.sort(elements.ptr(), elements.size());
}
void sort_by_key_range(uint32_t p_from, uint32_t p_size) {
SortArray<GeometryInstanceSurface *, SortByKey> sorter;
sorter.sort(elements.ptr() + p_from, p_size);
}
struct SortByDepth {
_FORCE_INLINE_ bool operator()(const GeometryInstanceSurface *A, const GeometryInstanceSurface *B) const {
return (A->owner->depth < B->owner->depth);
}
};
void sort_by_depth() { //used for shadows
SortArray<GeometryInstanceSurface *, SortByDepth> sorter;
sorter.sort(elements.ptr(), elements.size());
}
struct SortByReverseDepthAndPriority {
_FORCE_INLINE_ bool operator()(const GeometryInstanceSurface *A, const GeometryInstanceSurface *B) const {
return (A->sort.priority == B->sort.priority) ? (A->owner->depth > B->owner->depth) : (A->sort.priority < B->sort.priority);
}
};
void sort_by_reverse_depth_and_priority() { //used for alpha
SortArray<GeometryInstanceSurface *, SortByReverseDepthAndPriority> sorter;
sorter.sort(elements.ptr(), elements.size());
}
_FORCE_INLINE_ void add_element(GeometryInstanceSurface *p_element) {
elements.push_back(p_element);
}
};
RenderList render_list[RENDER_LIST_MAX];
void _setup_lights(const RenderDataGLES3 *p_render_data, bool p_using_shadows, uint32_t &r_directional_light_count, uint32_t &r_omni_light_count, uint32_t &r_spot_light_count, uint32_t &r_directional_shadow_count);
void _setup_environment(const RenderDataGLES3 *p_render_data, bool p_no_fog, const Size2i &p_screen_size, bool p_flip_y, const Color &p_default_bg_color, bool p_pancake_shadows, float p_shadow_bias = 0.0);
void _fill_render_list(RenderListType p_render_list, const RenderDataGLES3 *p_render_data, PassMode p_pass_mode, bool p_append = false);
void _render_shadows(const RenderDataGLES3 *p_render_data, const Size2i &p_viewport_size = Size2i(1, 1));
void _render_shadow_pass(RID p_light, RID p_shadow_atlas, int p_pass, const PagedArray<RenderGeometryInstance *> &p_instances, float p_lod_distance_multiplier = 0, float p_screen_mesh_lod_threshold = 0.0, RenderingMethod::RenderInfo *p_render_info = nullptr, const Size2i &p_viewport_size = Size2i(1, 1), const Transform3D &p_main_cam_transform = Transform3D());
void _render_post_processing(const RenderDataGLES3 *p_render_data);
template <PassMode p_pass_mode>
_FORCE_INLINE_ void _render_list_template(RenderListParameters *p_params, const RenderDataGLES3 *p_render_data, uint32_t p_from_element, uint32_t p_to_element, bool p_alpha_pass = false);
protected:
double time;
double time_step = 0;
bool screen_space_roughness_limiter = false;
float screen_space_roughness_limiter_amount = 0.25;
float screen_space_roughness_limiter_limit = 0.18;
void _render_buffers_debug_draw(Ref<RenderSceneBuffersGLES3> p_render_buffers, RID p_shadow_atlas, GLuint p_fbo);
/* Camera Attributes */
struct CameraAttributes {
float exposure_multiplier = 1.0;
float exposure_normalization = 1.0;
};
bool use_physical_light_units = false;
mutable RID_Owner<CameraAttributes, true> camera_attributes_owner;
/* Environment */
RS::EnvironmentSSAOQuality ssao_quality = RS::ENV_SSAO_QUALITY_MEDIUM;
bool ssao_half_size = false;
float ssao_adaptive_target = 0.5;
int ssao_blur_passes = 2;
float ssao_fadeout_from = 50.0;
float ssao_fadeout_to = 300.0;
bool glow_bicubic_upscale = false;
RS::EnvironmentSSRRoughnessQuality ssr_roughness_quality = RS::ENV_SSR_ROUGHNESS_QUALITY_LOW;
bool lightmap_bicubic_upscale = false;
/* Sky */
struct SkyGlobals {
float fog_aerial_perspective = 0.0;
Color fog_light_color;
float fog_sun_scatter = 0.0;
bool fog_enabled = false;
float fog_density = 0.0;
float z_far = 0.0;
uint32_t directional_light_count = 0;
DirectionalLightData *directional_lights = nullptr;
DirectionalLightData *last_frame_directional_lights = nullptr;
uint32_t last_frame_directional_light_count = 0;
GLuint directional_light_buffer = 0;
RID shader_default_version;
RID default_material;
RID default_shader;
RID fog_material;
RID fog_shader;
GLuint screen_triangle = 0;
GLuint screen_triangle_array = 0;
uint32_t max_directional_lights = 4;
uint32_t roughness_layers = 8;
} sky_globals;
struct Sky {
// Screen Buffers
GLuint half_res_pass = 0;
GLuint half_res_framebuffer = 0;
GLuint quarter_res_pass = 0;
GLuint quarter_res_framebuffer = 0;
Size2i screen_size = Size2i(0, 0);
// Radiance Cubemap
GLuint radiance = 0;
GLuint radiance_framebuffer = 0;
GLuint raw_radiance = 0;
RID material;
GLuint uniform_buffer;
int radiance_size = 256;
int mipmap_count = 1;
RS::SkyMode mode = RS::SKY_MODE_AUTOMATIC;
//ReflectionData reflection;
bool reflection_dirty = false;
bool dirty = false;
int processing_layer = 0;
Sky *dirty_list = nullptr;
float baked_exposure = 1.0;
//State to track when radiance cubemap needs updating
GLES3::SkyMaterialData *prev_material = nullptr;
Vector3 prev_position = Vector3(0.0, 0.0, 0.0);
float prev_time = 0.0f;
};
Sky *dirty_sky_list = nullptr;
mutable RID_Owner<Sky, true> sky_owner;
void _setup_sky(const RenderDataGLES3 *p_render_data, const PagedArray<RID> &p_lights, const Projection &p_projection, const Transform3D &p_transform, const Size2i p_screen_size);
void _invalidate_sky(Sky *p_sky);
void _update_dirty_skys();
void _update_sky_radiance(RID p_env, const Projection &p_projection, const Transform3D &p_transform, float p_sky_energy_multiplier);
void _draw_sky(RID p_env, const Projection &p_projection, const Transform3D &p_transform, float p_sky_energy_multiplier, float p_luminance_multiplier, bool p_use_multiview, bool p_flip_y, bool p_apply_color_adjustments_in_post);
void _free_sky_data(Sky *p_sky);
// Needed for a single argument calls (material and uv2).
PagedArrayPool<RenderGeometryInstance *> cull_argument_pool;
PagedArray<RenderGeometryInstance *> cull_argument;
public:
static RasterizerSceneGLES3 *get_singleton() { return singleton; }
RasterizerCanvasGLES3 *canvas = nullptr;
RenderGeometryInstance *geometry_instance_create(RID p_base) override;
void geometry_instance_free(RenderGeometryInstance *p_geometry_instance) override;
uint32_t geometry_instance_get_pair_mask() override;
/* PIPELINES */
virtual void mesh_generate_pipelines(RID p_mesh, bool p_background_compilation) override {}
virtual uint32_t get_pipeline_compilations(RS::PipelineSource p_source) override { return 0; }
/* SDFGI UPDATE */
void sdfgi_update(const Ref<RenderSceneBuffers> &p_render_buffers, RID p_environment, const Vector3 &p_world_position) override {}
int sdfgi_get_pending_region_count(const Ref<RenderSceneBuffers> &p_render_buffers) const override {
return 0;
}
AABB sdfgi_get_pending_region_bounds(const Ref<RenderSceneBuffers> &p_render_buffers, int p_region) const override {
return AABB();
}
uint32_t sdfgi_get_pending_region_cascade(const Ref<RenderSceneBuffers> &p_render_buffers, int p_region) const override {
return 0;
}
/* SKY API */
RID sky_allocate() override;
void sky_initialize(RID p_rid) override;
void sky_set_radiance_size(RID p_sky, int p_radiance_size) override;
void sky_set_mode(RID p_sky, RS::SkyMode p_mode) override;
void sky_set_material(RID p_sky, RID p_material) override;
Ref<Image> sky_bake_panorama(RID p_sky, float p_energy, bool p_bake_irradiance, const Size2i &p_size) override;
float sky_get_baked_exposure(RID p_sky) const;
/* ENVIRONMENT API */
void environment_glow_set_use_bicubic_upscale(bool p_enable) override;
void environment_set_ssr_roughness_quality(RS::EnvironmentSSRRoughnessQuality p_quality) override;
void environment_set_ssao_quality(RS::EnvironmentSSAOQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) override;
void environment_set_ssil_quality(RS::EnvironmentSSILQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) override;
void environment_set_sdfgi_ray_count(RS::EnvironmentSDFGIRayCount p_ray_count) override;
void environment_set_sdfgi_frames_to_converge(RS::EnvironmentSDFGIFramesToConverge p_frames) override;
void environment_set_sdfgi_frames_to_update_light(RS::EnvironmentSDFGIFramesToUpdateLight p_update) override;
void environment_set_volumetric_fog_volume_size(int p_size, int p_depth) override;
void environment_set_volumetric_fog_filter_active(bool p_enable) override;
Ref<Image> environment_bake_panorama(RID p_env, bool p_bake_irradiance, const Size2i &p_size) override;
_FORCE_INLINE_ bool is_using_physical_light_units() {
return use_physical_light_units;
}
void positional_soft_shadow_filter_set_quality(RS::ShadowQuality p_quality) override;
void directional_soft_shadow_filter_set_quality(RS::ShadowQuality p_quality) override;
RID fog_volume_instance_create(RID p_fog_volume) override;
void fog_volume_instance_set_transform(RID p_fog_volume_instance, const Transform3D &p_transform) override;
void fog_volume_instance_set_active(RID p_fog_volume_instance, bool p_active) override;
RID fog_volume_instance_get_volume(RID p_fog_volume_instance) const override;
Vector3 fog_volume_instance_get_position(RID p_fog_volume_instance) const override;
RID voxel_gi_instance_create(RID p_voxel_gi) override;
void voxel_gi_instance_set_transform_to_data(RID p_probe, const Transform3D &p_xform) override;
bool voxel_gi_needs_update(RID p_probe) const override;
void voxel_gi_update(RID p_probe, bool p_update_light_instances, const Vector<RID> &p_light_instances, const PagedArray<RenderGeometryInstance *> &p_dynamic_objects) override;
void voxel_gi_set_quality(RS::VoxelGIQuality) override;
void render_scene(const Ref<RenderSceneBuffers> &p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray<RenderGeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, const PagedArray<RID> &p_fog_volumes, RID p_environment, RID p_camera_attributes, RID p_compositor, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr, RenderingMethod::RenderInfo *r_render_info = nullptr) override;
void render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) override;
void render_particle_collider_heightfield(RID p_collider, const Transform3D &p_transform, const PagedArray<RenderGeometryInstance *> &p_instances) override;
void set_scene_pass(uint64_t p_pass) override {
scene_pass = p_pass;
}
_FORCE_INLINE_ uint64_t get_scene_pass() {
return scene_pass;
}
void set_time(double p_time, double p_step) override;
void set_debug_draw_mode(RS::ViewportDebugDraw p_debug_draw) override;
_FORCE_INLINE_ RS::ViewportDebugDraw get_debug_draw_mode() const {
return debug_draw;
}
Ref<RenderSceneBuffers> render_buffers_create() override;
void gi_set_use_half_resolution(bool p_enable) override;
void screen_space_roughness_limiter_set_active(bool p_enable, float p_amount, float p_curve) override;
bool screen_space_roughness_limiter_is_active() const override;
void sub_surface_scattering_set_quality(RS::SubSurfaceScatteringQuality p_quality) override;
void sub_surface_scattering_set_scale(float p_scale, float p_depth_scale) override;
TypedArray<Image> bake_render_uv2(RID p_base, const TypedArray<RID> &p_material_overrides, const Size2i &p_image_size) override;
void _render_uv2(const PagedArray<RenderGeometryInstance *> &p_instances, GLuint p_framebuffer, const Rect2i &p_region);
bool free(RID p_rid) override;
void update() override;
void sdfgi_set_debug_probe_select(const Vector3 &p_position, const Vector3 &p_dir) override;
void decals_set_filter(RS::DecalFilter p_filter) override;
void light_projectors_set_filter(RS::LightProjectorFilter p_filter) override;
virtual void lightmaps_set_bicubic_filter(bool p_enable) override;
RasterizerSceneGLES3();
~RasterizerSceneGLES3();
};
#endif // GLES3_ENABLED

View File

@@ -0,0 +1,842 @@
/**************************************************************************/
/* shader_gles3.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. */
/**************************************************************************/
#include "shader_gles3.h"
#ifdef GLES3_ENABLED
#include "core/io/dir_access.h"
#include "core/io/file_access.h"
#include "drivers/gles3/rasterizer_gles3.h"
#include "drivers/gles3/storage/config.h"
static String _mkid(const String &p_id) {
String id = "m_" + p_id.replace("__", "_dus_");
return id.replace("__", "_dus_"); //doubleunderscore is reserved in glsl
}
void ShaderGLES3::_add_stage(const char *p_code, StageType p_stage_type) {
Vector<String> lines = String(p_code).split("\n");
String text;
for (int i = 0; i < lines.size(); i++) {
const String &l = lines[i];
bool push_chunk = false;
StageTemplate::Chunk chunk;
if (l.begins_with("#GLOBALS")) {
switch (p_stage_type) {
case STAGE_TYPE_VERTEX:
chunk.type = StageTemplate::Chunk::TYPE_VERTEX_GLOBALS;
break;
case STAGE_TYPE_FRAGMENT:
chunk.type = StageTemplate::Chunk::TYPE_FRAGMENT_GLOBALS;
break;
default: {
}
}
push_chunk = true;
} else if (l.begins_with("#MATERIAL_UNIFORMS")) {
chunk.type = StageTemplate::Chunk::TYPE_MATERIAL_UNIFORMS;
push_chunk = true;
} else if (l.begins_with("#CODE")) {
chunk.type = StageTemplate::Chunk::TYPE_CODE;
push_chunk = true;
chunk.code = l.replace_first("#CODE", String()).remove_char(':').strip_edges().to_upper();
} else {
text += l + "\n";
}
if (push_chunk) {
if (text != String()) {
StageTemplate::Chunk text_chunk;
text_chunk.type = StageTemplate::Chunk::TYPE_TEXT;
text_chunk.text = text.utf8();
stage_templates[p_stage_type].chunks.push_back(text_chunk);
text = String();
}
stage_templates[p_stage_type].chunks.push_back(chunk);
}
if (text != String()) {
StageTemplate::Chunk text_chunk;
text_chunk.type = StageTemplate::Chunk::TYPE_TEXT;
text_chunk.text = text.utf8();
stage_templates[p_stage_type].chunks.push_back(text_chunk);
text = String();
}
}
}
void ShaderGLES3::_setup(const char *p_vertex_code, const char *p_fragment_code, const char *p_name, int p_uniform_count, const char **p_uniform_names, int p_ubo_count, const UBOPair *p_ubos, int p_feedback_count, const Feedback *p_feedback, int p_texture_count, const TexUnitPair *p_tex_units, int p_specialization_count, const Specialization *p_specializations, int p_variant_count, const char **p_variants) {
name = p_name;
if (p_vertex_code) {
_add_stage(p_vertex_code, STAGE_TYPE_VERTEX);
}
if (p_fragment_code) {
_add_stage(p_fragment_code, STAGE_TYPE_FRAGMENT);
}
uniform_names = p_uniform_names;
uniform_count = p_uniform_count;
ubo_pairs = p_ubos;
ubo_count = p_ubo_count;
texunit_pairs = p_tex_units;
texunit_pair_count = p_texture_count;
specializations = p_specializations;
specialization_count = p_specialization_count;
specialization_default_mask = 0;
for (int i = 0; i < specialization_count; i++) {
if (specializations[i].default_value) {
specialization_default_mask |= (uint64_t(1) << uint64_t(i));
}
}
variant_defines = p_variants;
variant_count = p_variant_count;
feedbacks = p_feedback;
feedback_count = p_feedback_count;
StringBuilder tohash;
tohash.append("[Vertex]");
tohash.append(p_vertex_code ? p_vertex_code : "");
tohash.append("[Fragment]");
tohash.append(p_fragment_code ? p_fragment_code : "");
tohash.append("[gl_implementation]");
const String &vendor = String::utf8((const char *)glGetString(GL_VENDOR));
tohash.append(vendor.is_empty() ? "unknown" : vendor);
const String &renderer = String::utf8((const char *)glGetString(GL_RENDERER));
tohash.append(renderer.is_empty() ? "unknown" : renderer);
const String &version = String::utf8((const char *)glGetString(GL_VERSION));
tohash.append(version.is_empty() ? "unknown" : version);
base_sha256 = tohash.as_string().sha256_text();
}
RID ShaderGLES3::version_create() {
//initialize() was never called
ERR_FAIL_COND_V(variant_count == 0, RID());
Version version;
return version_owner.make_rid(version);
}
void ShaderGLES3::_build_variant_code(StringBuilder &builder, uint32_t p_variant, const Version *p_version, StageType p_stage_type, uint64_t p_specialization) {
if (RasterizerGLES3::is_gles_over_gl()) {
builder.append("#version 330\n");
builder.append("#define USE_GLES_OVER_GL\n");
} else {
builder.append("#version 300 es\n");
}
if (GLES3::Config::get_singleton()->polyfill_half2float) {
builder.append("#define USE_HALF2FLOAT\n");
}
for (int i = 0; i < specialization_count; i++) {
if (p_specialization & (uint64_t(1) << uint64_t(i))) {
builder.append("#define " + String(specializations[i].name) + "\n");
}
}
if (p_version->uniforms.size()) {
builder.append("#define MATERIAL_UNIFORMS_USED\n");
}
for (const KeyValue<StringName, CharString> &E : p_version->code_sections) {
builder.append(String("#define ") + String(E.key) + "_CODE_USED\n");
}
builder.append("\n"); //make sure defines begin at newline
builder.append(general_defines.get_data());
builder.append(variant_defines[p_variant]);
builder.append("\n");
for (int j = 0; j < p_version->custom_defines.size(); j++) {
builder.append(p_version->custom_defines[j].get_data());
}
builder.append("\n"); //make sure defines begin at newline
// Optional support for external textures.
if (GLES3::Config::get_singleton()->external_texture_supported) {
builder.append("#extension GL_OES_EGL_image_external : enable\n");
builder.append("#extension GL_OES_EGL_image_external_essl3 : enable\n");
} else {
builder.append("#define samplerExternalOES sampler2D\n");
}
// Insert multiview extension loading, because it needs to appear before
// any non-preprocessor code (like the "precision highp..." lines below).
builder.append("#ifdef USE_MULTIVIEW\n");
builder.append("#if defined(GL_OVR_multiview2)\n");
builder.append("#extension GL_OVR_multiview2 : require\n");
builder.append("#elif defined(GL_OVR_multiview)\n");
builder.append("#extension GL_OVR_multiview : require\n");
builder.append("#endif\n");
if (p_stage_type == StageType::STAGE_TYPE_VERTEX) {
builder.append("layout(num_views=2) in;\n");
}
builder.append("#define ViewIndex gl_ViewID_OVR\n");
builder.append("#define MAX_VIEWS 2\n");
builder.append("#else\n");
builder.append("#define ViewIndex uint(0)\n");
builder.append("#define MAX_VIEWS 1\n");
builder.append("#endif\n");
// Default to highp precision unless specified otherwise.
builder.append("precision highp float;\n");
builder.append("precision highp int;\n");
if (!RasterizerGLES3::is_gles_over_gl()) {
builder.append("precision highp sampler2D;\n");
builder.append("precision highp samplerCube;\n");
builder.append("precision highp sampler2DArray;\n");
builder.append("precision highp sampler3D;\n");
}
const StageTemplate &stage_template = stage_templates[p_stage_type];
for (uint32_t i = 0; i < stage_template.chunks.size(); i++) {
const StageTemplate::Chunk &chunk = stage_template.chunks[i];
switch (chunk.type) {
case StageTemplate::Chunk::TYPE_MATERIAL_UNIFORMS: {
builder.append(p_version->uniforms.get_data()); //uniforms (same for vertex and fragment)
} break;
case StageTemplate::Chunk::TYPE_VERTEX_GLOBALS: {
builder.append(p_version->vertex_globals.get_data()); // vertex globals
} break;
case StageTemplate::Chunk::TYPE_FRAGMENT_GLOBALS: {
builder.append(p_version->fragment_globals.get_data()); // fragment globals
} break;
case StageTemplate::Chunk::TYPE_CODE: {
if (p_version->code_sections.has(chunk.code)) {
builder.append(p_version->code_sections[chunk.code].get_data());
}
} break;
case StageTemplate::Chunk::TYPE_TEXT: {
builder.append(chunk.text.get_data());
} break;
}
}
}
static void _display_error_with_code(const String &p_error, const String &p_code) {
int line = 1;
Vector<String> lines = p_code.split("\n");
for (int j = 0; j < lines.size(); j++) {
print_line(itos(line) + ": " + lines[j]);
line++;
}
ERR_PRINT(p_error);
}
void ShaderGLES3::_get_uniform_locations(Version::Specialization &spec, Version *p_version) {
glUseProgram(spec.id);
spec.uniform_location.resize(uniform_count);
for (int i = 0; i < uniform_count; i++) {
spec.uniform_location[i] = glGetUniformLocation(spec.id, uniform_names[i]);
}
for (int i = 0; i < texunit_pair_count; i++) {
GLint loc = glGetUniformLocation(spec.id, texunit_pairs[i].name);
if (loc >= 0) {
if (texunit_pairs[i].index < 0) {
glUniform1i(loc, max_image_units + texunit_pairs[i].index);
} else {
glUniform1i(loc, texunit_pairs[i].index);
}
}
}
for (int i = 0; i < ubo_count; i++) {
GLint loc = glGetUniformBlockIndex(spec.id, ubo_pairs[i].name);
if (loc >= 0) {
glUniformBlockBinding(spec.id, loc, ubo_pairs[i].index);
}
}
// textures
int texture_index = 0;
for (uint32_t i = 0; i < p_version->texture_uniforms.size(); i++) {
String native_uniform_name = _mkid(p_version->texture_uniforms[i].name);
GLint location = glGetUniformLocation(spec.id, (native_uniform_name).ascii().get_data());
Vector<int32_t> texture_uniform_bindings;
int texture_count = p_version->texture_uniforms[i].array_size;
for (int j = 0; j < texture_count; j++) {
texture_uniform_bindings.append(texture_index + base_texture_index);
texture_index++;
}
glUniform1iv(location, texture_uniform_bindings.size(), texture_uniform_bindings.ptr());
}
glUseProgram(0);
}
void ShaderGLES3::_compile_specialization(Version::Specialization &spec, uint32_t p_variant, Version *p_version, uint64_t p_specialization) {
spec.id = glCreateProgram();
spec.ok = false;
GLint status;
//vertex stage
{
StringBuilder builder;
_build_variant_code(builder, p_variant, p_version, STAGE_TYPE_VERTEX, p_specialization);
spec.vert_id = glCreateShader(GL_VERTEX_SHADER);
String builder_string = builder.as_string();
CharString cs = builder_string.utf8();
const char *cstr = cs.ptr();
GLint cstr_len = cs.length();
glShaderSource(spec.vert_id, 1, &cstr, &cstr_len);
glCompileShader(spec.vert_id);
glGetShaderiv(spec.vert_id, GL_COMPILE_STATUS, &status);
if (status == GL_FALSE) {
GLsizei iloglen;
glGetShaderiv(spec.vert_id, GL_INFO_LOG_LENGTH, &iloglen);
if (iloglen < 0) {
glDeleteShader(spec.vert_id);
glDeleteProgram(spec.id);
spec.id = 0;
ERR_PRINT("No OpenGL vertex shader compiler log.");
} else {
if (iloglen == 0) {
iloglen = 4096; // buggy driver (Adreno 220+)
}
char *ilogmem = (char *)Memory::alloc_static_zeroed(iloglen + 1);
glGetShaderInfoLog(spec.vert_id, iloglen, &iloglen, ilogmem);
String err_string = name + ": Vertex shader compilation failed:\n";
err_string += ilogmem;
_display_error_with_code(err_string, builder_string);
Memory::free_static(ilogmem);
glDeleteShader(spec.vert_id);
glDeleteProgram(spec.id);
spec.id = 0;
}
ERR_FAIL();
}
}
//fragment stage
{
StringBuilder builder;
_build_variant_code(builder, p_variant, p_version, STAGE_TYPE_FRAGMENT, p_specialization);
spec.frag_id = glCreateShader(GL_FRAGMENT_SHADER);
String builder_string = builder.as_string();
CharString cs = builder_string.utf8();
const char *cstr = cs.ptr();
GLint cstr_len = cs.length();
glShaderSource(spec.frag_id, 1, &cstr, &cstr_len);
glCompileShader(spec.frag_id);
glGetShaderiv(spec.frag_id, GL_COMPILE_STATUS, &status);
if (status == GL_FALSE) {
GLsizei iloglen;
glGetShaderiv(spec.frag_id, GL_INFO_LOG_LENGTH, &iloglen);
if (iloglen < 0) {
glDeleteShader(spec.frag_id);
glDeleteProgram(spec.id);
spec.id = 0;
ERR_PRINT("No OpenGL fragment shader compiler log.");
} else {
if (iloglen == 0) {
iloglen = 4096; // buggy driver (Adreno 220+)
}
char *ilogmem = (char *)Memory::alloc_static_zeroed(iloglen + 1);
glGetShaderInfoLog(spec.frag_id, iloglen, &iloglen, ilogmem);
String err_string = name + ": Fragment shader compilation failed:\n";
err_string += ilogmem;
_display_error_with_code(err_string, builder_string);
Memory::free_static(ilogmem);
glDeleteShader(spec.frag_id);
glDeleteProgram(spec.id);
spec.id = 0;
}
ERR_FAIL();
}
}
glAttachShader(spec.id, spec.frag_id);
glAttachShader(spec.id, spec.vert_id);
// If feedback exists, set it up.
if (feedback_count) {
Vector<const char *> feedback;
for (int i = 0; i < feedback_count; i++) {
if (feedbacks[i].specialization == 0 || (feedbacks[i].specialization & p_specialization)) {
// Specialization for this feedback is enabled
feedback.push_back(feedbacks[i].name);
}
}
if (feedback.size()) {
glTransformFeedbackVaryings(spec.id, feedback.size(), feedback.ptr(), GL_INTERLEAVED_ATTRIBS);
}
}
glLinkProgram(spec.id);
glGetProgramiv(spec.id, GL_LINK_STATUS, &status);
if (status == GL_FALSE) {
GLsizei iloglen;
glGetProgramiv(spec.id, GL_INFO_LOG_LENGTH, &iloglen);
if (iloglen < 0) {
glDeleteShader(spec.frag_id);
glDeleteShader(spec.vert_id);
glDeleteProgram(spec.id);
spec.id = 0;
ERR_PRINT("No OpenGL program link log. Something is wrong.");
ERR_FAIL();
}
if (iloglen == 0) {
iloglen = 4096; // buggy driver (Adreno 220+)
}
char *ilogmem = (char *)Memory::alloc_static(iloglen + 1);
ilogmem[iloglen] = '\0';
glGetProgramInfoLog(spec.id, iloglen, &iloglen, ilogmem);
String err_string = name + ": Program linking failed:\n";
err_string += ilogmem;
_display_error_with_code(err_string, String());
Memory::free_static(ilogmem);
glDeleteShader(spec.frag_id);
glDeleteShader(spec.vert_id);
glDeleteProgram(spec.id);
spec.id = 0;
ERR_FAIL();
}
_get_uniform_locations(spec, p_version);
spec.ok = true;
}
RS::ShaderNativeSourceCode ShaderGLES3::version_get_native_source_code(RID p_version) {
Version *version = version_owner.get_or_null(p_version);
RS::ShaderNativeSourceCode source_code;
ERR_FAIL_NULL_V(version, source_code);
source_code.versions.resize(variant_count);
for (int i = 0; i < source_code.versions.size(); i++) {
//vertex stage
{
StringBuilder builder;
_build_variant_code(builder, i, version, STAGE_TYPE_VERTEX, specialization_default_mask);
RS::ShaderNativeSourceCode::Version::Stage stage;
stage.name = "vertex";
stage.code = builder.as_string();
source_code.versions.write[i].stages.push_back(stage);
}
//fragment stage
{
StringBuilder builder;
_build_variant_code(builder, i, version, STAGE_TYPE_FRAGMENT, specialization_default_mask);
RS::ShaderNativeSourceCode::Version::Stage stage;
stage.name = "fragment";
stage.code = builder.as_string();
source_code.versions.write[i].stages.push_back(stage);
}
}
return source_code;
}
String ShaderGLES3::_version_get_sha1(Version *p_version) const {
StringBuilder hash_build;
hash_build.append("[uniforms]");
hash_build.append(p_version->uniforms.get_data());
hash_build.append("[vertex_globals]");
hash_build.append(p_version->vertex_globals.get_data());
hash_build.append("[fragment_globals]");
hash_build.append(p_version->fragment_globals.get_data());
Vector<StringName> code_sections;
for (const KeyValue<StringName, CharString> &E : p_version->code_sections) {
code_sections.push_back(E.key);
}
code_sections.sort_custom<StringName::AlphCompare>();
for (int i = 0; i < code_sections.size(); i++) {
hash_build.append(String("[code:") + String(code_sections[i]) + "]");
hash_build.append(p_version->code_sections[code_sections[i]].get_data());
}
for (int i = 0; i < p_version->custom_defines.size(); i++) {
hash_build.append("[custom_defines:" + itos(i) + "]");
hash_build.append(p_version->custom_defines[i].get_data());
}
if (RasterizerGLES3::is_gles_over_gl()) {
hash_build.append("[gl]");
} else {
hash_build.append("[gles]");
}
return hash_build.as_string().sha1_text();
}
#ifndef WEB_ENABLED // not supported in webgl
static const char *shader_file_header = "GLSC";
static const uint32_t cache_file_version = 3;
#endif
bool ShaderGLES3::_load_from_cache(Version *p_version) {
#ifdef WEB_ENABLED // not supported in webgl
return false;
#else
#if !defined(ANDROID_ENABLED) && !defined(IOS_ENABLED)
if (RasterizerGLES3::is_gles_over_gl() && (glProgramBinary == nullptr)) { // ARB_get_program_binary extension not available.
return false;
}
#endif
String sha1 = _version_get_sha1(p_version);
String path = shader_cache_dir.path_join(name).path_join(base_sha256).path_join(sha1) + ".cache";
Ref<FileAccess> f = FileAccess::open(path, FileAccess::READ);
if (f.is_null()) {
return false;
}
char header[5] = {};
f->get_buffer((uint8_t *)header, 4);
ERR_FAIL_COND_V(header != String(shader_file_header), false);
uint32_t file_version = f->get_32();
if (file_version != cache_file_version) {
return false; // wrong version
}
int cache_variant_count = static_cast<int>(f->get_32());
ERR_FAIL_COND_V_MSG(cache_variant_count != variant_count, false, "shader cache variant count mismatch, expected " + itos(variant_count) + " got " + itos(cache_variant_count)); //should not happen but check
LocalVector<AHashMap<uint64_t, Version::Specialization>> variants;
for (int i = 0; i < cache_variant_count; i++) {
uint32_t cache_specialization_count = f->get_32();
AHashMap<uint64_t, Version::Specialization> variant;
for (uint32_t j = 0; j < cache_specialization_count; j++) {
uint64_t specialization_key = f->get_64();
uint32_t variant_size = f->get_32();
if (variant_size == 0) {
continue;
}
uint32_t variant_format = f->get_32();
Vector<uint8_t> variant_bytes;
variant_bytes.resize(variant_size);
uint32_t br = f->get_buffer(variant_bytes.ptrw(), variant_size);
ERR_FAIL_COND_V(br != variant_size, false);
Version::Specialization specialization;
specialization.id = glCreateProgram();
if (feedback_count) {
Vector<const char *> feedback;
for (int feedback_index = 0; feedback_index < feedback_count; feedback_index++) {
if (feedbacks[feedback_index].specialization == 0 || (feedbacks[feedback_index].specialization & specialization_key)) {
// Specialization for this feedback is enabled.
feedback.push_back(feedbacks[feedback_index].name);
}
}
if (!feedback.is_empty()) {
glTransformFeedbackVaryings(specialization.id, feedback.size(), feedback.ptr(), GL_INTERLEAVED_ATTRIBS);
}
}
glProgramBinary(specialization.id, variant_format, variant_bytes.ptr(), variant_bytes.size());
GLint link_status = 0;
glGetProgramiv(specialization.id, GL_LINK_STATUS, &link_status);
if (link_status != GL_TRUE) {
WARN_PRINT_ONCE("Failed to load cached shader, recompiling.");
return false;
}
_get_uniform_locations(specialization, p_version);
specialization.ok = true;
variant.insert(specialization_key, specialization);
}
variants.push_back(variant);
}
p_version->variants = variants;
return true;
#endif // WEB_ENABLED
}
void ShaderGLES3::_save_to_cache(Version *p_version) {
#ifdef WEB_ENABLED // not supported in webgl
return;
#else
ERR_FAIL_COND(!shader_cache_dir_valid);
#if !defined(ANDROID_ENABLED) && !defined(IOS_ENABLED)
if (RasterizerGLES3::is_gles_over_gl() && (glGetProgramBinary == nullptr)) { // ARB_get_program_binary extension not available.
return;
}
#endif
String sha1 = _version_get_sha1(p_version);
String path = shader_cache_dir.path_join(name).path_join(base_sha256).path_join(sha1) + ".cache";
Error error;
Ref<FileAccess> f = FileAccess::open(path, FileAccess::WRITE, &error);
ERR_FAIL_COND(f.is_null());
f->store_buffer((const uint8_t *)shader_file_header, 4);
f->store_32(cache_file_version);
f->store_32(variant_count);
for (int i = 0; i < variant_count; i++) {
int cache_specialization_count = p_version->variants[i].size();
f->store_32(cache_specialization_count);
for (KeyValue<uint64_t, ShaderGLES3::Version::Specialization> &kv : p_version->variants[i]) {
const uint64_t specialization_key = kv.key;
f->store_64(specialization_key);
const Version::Specialization *specialization = &kv.value;
GLint program_size = 0;
glGetProgramiv(specialization->id, GL_PROGRAM_BINARY_LENGTH, &program_size);
if (program_size == 0) {
f->store_32(0);
continue;
}
PackedByteArray compiled_program;
compiled_program.resize(program_size);
GLenum binary_format = 0;
glGetProgramBinary(specialization->id, program_size, nullptr, &binary_format, compiled_program.ptrw());
if (program_size != compiled_program.size()) {
f->store_32(0);
continue;
}
f->store_32(program_size);
f->store_32(binary_format);
f->store_buffer(compiled_program.ptr(), compiled_program.size());
}
}
#endif // WEB_ENABLED
}
void ShaderGLES3::_clear_version(Version *p_version) {
// Variants not compiled yet, just return
if (p_version->variants.is_empty()) {
return;
}
for (int i = 0; i < variant_count; i++) {
for (KeyValue<uint64_t, Version::Specialization> &kv : p_version->variants[i]) {
if (kv.value.id != 0) {
glDeleteShader(kv.value.vert_id);
glDeleteShader(kv.value.frag_id);
glDeleteProgram(kv.value.id);
}
}
}
p_version->variants.clear();
}
void ShaderGLES3::_initialize_version(Version *p_version) {
ERR_FAIL_COND(p_version->variants.size() > 0);
bool use_cache = shader_cache_dir_valid && !(feedback_count > 0 && GLES3::Config::get_singleton()->disable_transform_feedback_shader_cache);
if (use_cache && _load_from_cache(p_version)) {
return;
}
p_version->variants.reserve(variant_count);
for (int i = 0; i < variant_count; i++) {
AHashMap<uint64_t, Version::Specialization> variant;
p_version->variants.push_back(variant);
Version::Specialization spec;
_compile_specialization(spec, i, p_version, specialization_default_mask);
p_version->variants[i].insert(specialization_default_mask, spec);
}
if (use_cache) {
_save_to_cache(p_version);
}
}
void ShaderGLES3::version_set_code(RID p_version, const HashMap<String, String> &p_code, const String &p_uniforms, const String &p_vertex_globals, const String &p_fragment_globals, const Vector<String> &p_custom_defines, const LocalVector<ShaderGLES3::TextureUniformData> &p_texture_uniforms, bool p_initialize) {
Version *version = version_owner.get_or_null(p_version);
ERR_FAIL_NULL(version);
_clear_version(version); //clear if existing
version->vertex_globals = p_vertex_globals.utf8();
version->fragment_globals = p_fragment_globals.utf8();
version->uniforms = p_uniforms.utf8();
version->code_sections.clear();
version->texture_uniforms = p_texture_uniforms;
for (const KeyValue<String, String> &E : p_code) {
version->code_sections[StringName(E.key.to_upper())] = E.value.utf8();
}
version->custom_defines.clear();
for (int i = 0; i < p_custom_defines.size(); i++) {
version->custom_defines.push_back(p_custom_defines[i].utf8());
}
if (p_initialize) {
_initialize_version(version);
}
}
bool ShaderGLES3::version_is_valid(RID p_version) {
Version *version = version_owner.get_or_null(p_version);
return version != nullptr;
}
bool ShaderGLES3::version_free(RID p_version) {
if (version_owner.owns(p_version)) {
Version *version = version_owner.get_or_null(p_version);
_clear_version(version);
version_owner.free(p_version);
} else {
return false;
}
return true;
}
bool ShaderGLES3::shader_cache_cleanup_on_start = false;
ShaderGLES3::ShaderGLES3() {
}
void ShaderGLES3::initialize(const String &p_general_defines, int p_base_texture_index) {
general_defines = p_general_defines.utf8();
base_texture_index = p_base_texture_index;
_init();
if (shader_cache_dir != String()) {
StringBuilder hash_build;
hash_build.append("[base_hash]");
hash_build.append(base_sha256);
hash_build.append("[general_defines]");
hash_build.append(general_defines.get_data());
for (int i = 0; i < variant_count; i++) {
hash_build.append("[variant_defines:" + itos(i) + "]");
hash_build.append(variant_defines[i]);
}
base_sha256 = hash_build.as_string().sha256_text();
Ref<DirAccess> d = DirAccess::open(shader_cache_dir);
ERR_FAIL_COND(d.is_null());
if (d->change_dir(name) != OK) {
Error err = d->make_dir(name);
ERR_FAIL_COND(err != OK);
d->change_dir(name);
}
//erase other versions?
if (shader_cache_cleanup_on_start) {
}
//
if (d->change_dir(base_sha256) != OK) {
Error err = d->make_dir(base_sha256);
ERR_FAIL_COND(err != OK);
}
shader_cache_dir_valid = true;
print_verbose("Shader '" + name + "' SHA256: " + base_sha256);
}
GLES3::Config *config = GLES3::Config::get_singleton();
ERR_FAIL_NULL(config);
max_image_units = config->max_texture_image_units;
}
void ShaderGLES3::set_shader_cache_dir(const String &p_dir) {
shader_cache_dir = p_dir;
}
void ShaderGLES3::set_shader_cache_save_compressed(bool p_enable) {
shader_cache_save_compressed = p_enable;
}
void ShaderGLES3::set_shader_cache_save_compressed_zstd(bool p_enable) {
shader_cache_save_compressed_zstd = p_enable;
}
void ShaderGLES3::set_shader_cache_save_debug(bool p_enable) {
shader_cache_save_debug = p_enable;
}
String ShaderGLES3::shader_cache_dir;
bool ShaderGLES3::shader_cache_save_compressed = true;
bool ShaderGLES3::shader_cache_save_compressed_zstd = true;
bool ShaderGLES3::shader_cache_save_debug = true;
ShaderGLES3::~ShaderGLES3() {
LocalVector<RID> remaining = version_owner.get_owned_list();
if (remaining.size()) {
ERR_PRINT(itos(remaining.size()) + " shaders of type " + name + " were never freed");
for (RID &rid : remaining) {
version_free(rid);
}
}
}
#endif

View File

@@ -0,0 +1,259 @@
/**************************************************************************/
/* shader_gles3.h */
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#include "core/math/projection.h"
#include "core/os/mutex.h"
#include "core/string/string_builder.h"
#include "core/templates/hash_map.h"
#include "core/templates/local_vector.h"
#include "core/templates/rid_owner.h"
#include "servers/rendering_server.h"
#ifdef GLES3_ENABLED
#include "platform_gl.h"
class ShaderGLES3 {
public:
struct TextureUniformData {
StringName name;
int array_size;
};
protected:
struct TexUnitPair {
const char *name;
int index;
};
struct UBOPair {
const char *name;
int index;
};
struct Specialization {
const char *name;
bool default_value = false;
};
struct Feedback {
const char *name;
uint64_t specialization;
};
private:
//versions
CharString general_defines;
// A version is a high-level construct which is a combination of built-in and user-defined shader code, Each user-created Shader makes one version
// Variants use #ifdefs to toggle behavior on and off to change behavior of the shader
// All variants are compiled each time a new version is created
// Specializations use #ifdefs to toggle behavior on and off for performance, on supporting hardware, they will compile a version with everything enabled, and then compile more copies to improve performance
// Use specializations to enable and disabled advanced features, use variants to toggle behavior when different data may be used (e.g. using a samplerArray vs a sampler, or doing a depth prepass vs a color pass)
struct Version {
LocalVector<TextureUniformData> texture_uniforms;
CharString uniforms;
CharString vertex_globals;
CharString fragment_globals;
HashMap<StringName, CharString> code_sections;
Vector<CharString> custom_defines;
struct Specialization {
GLuint id;
GLuint vert_id;
GLuint frag_id;
LocalVector<GLint> uniform_location;
LocalVector<GLint> texture_uniform_locations;
bool build_queued = false;
bool ok = false;
Specialization() {
id = 0;
vert_id = 0;
frag_id = 0;
}
};
LocalVector<AHashMap<uint64_t, Specialization>> variants;
};
Mutex variant_set_mutex;
void _get_uniform_locations(Version::Specialization &spec, Version *p_version);
void _compile_specialization(Version::Specialization &spec, uint32_t p_variant, Version *p_version, uint64_t p_specialization);
void _clear_version(Version *p_version);
void _initialize_version(Version *p_version);
RID_Owner<Version, true> version_owner;
struct StageTemplate {
struct Chunk {
enum Type {
TYPE_MATERIAL_UNIFORMS,
TYPE_VERTEX_GLOBALS,
TYPE_FRAGMENT_GLOBALS,
TYPE_CODE,
TYPE_TEXT
};
Type type;
StringName code;
CharString text;
};
LocalVector<Chunk> chunks;
};
String name;
String base_sha256;
static String shader_cache_dir;
static bool shader_cache_cleanup_on_start;
static bool shader_cache_save_compressed;
static bool shader_cache_save_compressed_zstd;
static bool shader_cache_save_debug;
bool shader_cache_dir_valid = false;
GLint max_image_units = 0;
enum StageType {
STAGE_TYPE_VERTEX,
STAGE_TYPE_FRAGMENT,
STAGE_TYPE_MAX,
};
StageTemplate stage_templates[STAGE_TYPE_MAX];
void _build_variant_code(StringBuilder &p_builder, uint32_t p_variant, const Version *p_version, StageType p_stage_type, uint64_t p_specialization);
void _add_stage(const char *p_code, StageType p_stage_type);
String _version_get_sha1(Version *p_version) const;
bool _load_from_cache(Version *p_version);
void _save_to_cache(Version *p_version);
const char **uniform_names = nullptr;
int uniform_count = 0;
const UBOPair *ubo_pairs = nullptr;
int ubo_count = 0;
const Feedback *feedbacks;
int feedback_count = 0;
const TexUnitPair *texunit_pairs = nullptr;
int texunit_pair_count = 0;
int specialization_count = 0;
const Specialization *specializations = nullptr;
uint64_t specialization_default_mask = 0;
const char **variant_defines = nullptr;
int variant_count = 0;
int base_texture_index = 0;
Version::Specialization *current_shader = nullptr;
protected:
ShaderGLES3();
void _setup(const char *p_vertex_code, const char *p_fragment_code, const char *p_name, int p_uniform_count, const char **p_uniform_names, int p_ubo_count, const UBOPair *p_ubos, int p_feedback_count, const Feedback *p_feedback, int p_texture_count, const TexUnitPair *p_tex_units, int p_specialization_count, const Specialization *p_specializations, int p_variant_count, const char **p_variants);
_FORCE_INLINE_ bool _version_bind_shader(RID p_version, int p_variant, uint64_t p_specialization) {
ERR_FAIL_INDEX_V(p_variant, variant_count, false);
Version *version = version_owner.get_or_null(p_version);
ERR_FAIL_NULL_V(version, false);
if (version->variants.is_empty()) {
_initialize_version(version); //may lack initialization
}
Version::Specialization *spec = version->variants[p_variant].getptr(p_specialization);
if (!spec) {
if (false) {
// Queue load this specialization and use defaults in the meantime (TODO)
spec = version->variants[p_variant].getptr(specialization_default_mask);
} else {
// Compile on the spot
Version::Specialization s;
_compile_specialization(s, p_variant, version, p_specialization);
version->variants[p_variant].insert(p_specialization, s);
spec = version->variants[p_variant].getptr(p_specialization);
if (shader_cache_dir_valid) {
_save_to_cache(version);
}
}
} else if (spec->build_queued) {
// Still queued, wait
spec = version->variants[p_variant].getptr(specialization_default_mask);
}
if (!spec || !spec->ok) {
WARN_PRINT_ONCE("shader failed to compile, unable to bind shader.");
return false;
}
glUseProgram(spec->id);
current_shader = spec;
return true;
}
_FORCE_INLINE_ int _version_get_uniform(int p_which, RID p_version, int p_variant, uint64_t p_specialization) {
ERR_FAIL_INDEX_V(p_which, uniform_count, -1);
Version *version = version_owner.get_or_null(p_version);
ERR_FAIL_NULL_V(version, -1);
ERR_FAIL_INDEX_V(p_variant, int(version->variants.size()), -1);
Version::Specialization *spec = version->variants[p_variant].getptr(p_specialization);
ERR_FAIL_NULL_V(spec, -1);
ERR_FAIL_INDEX_V(p_which, int(spec->uniform_location.size()), -1);
return spec->uniform_location[p_which];
}
virtual void _init() = 0;
public:
RID version_create();
void version_set_code(RID p_version, const HashMap<String, String> &p_code, const String &p_uniforms, const String &p_vertex_globals, const String &p_fragment_globals, const Vector<String> &p_custom_defines, const LocalVector<ShaderGLES3::TextureUniformData> &p_texture_uniforms, bool p_initialize = false);
bool version_is_valid(RID p_version);
bool version_free(RID p_version);
static void set_shader_cache_dir(const String &p_dir);
static void set_shader_cache_save_compressed(bool p_enable);
static void set_shader_cache_save_compressed_zstd(bool p_enable);
static void set_shader_cache_save_debug(bool p_enable);
RS::ShaderNativeSourceCode version_get_native_source_code(RID p_version);
void initialize(const String &p_general_defines = "", int p_base_texture_index = 0);
virtual ~ShaderGLES3();
};
#endif // GLES3_ENABLED

View File

@@ -0,0 +1,34 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")
if "GLES3_GLSL" in env["BUILDERS"]:
# find all include files
gl_include_files = [str(f) for f in Glob("*_inc.glsl")]
# find all shader code(all glsl files excluding our include files)
glsl_files = [str(f) for f in Glob("*.glsl") if str(f) not in gl_include_files]
# make sure we recompile shaders if include files change
env.Depends([f + ".gen.h" for f in glsl_files], gl_include_files + ["#gles3_builders.py"])
# compile shaders
# as we have a few, not yet, converted files we name the ones we want to include:
env.GLES3_GLSL("canvas.glsl")
env.GLES3_GLSL("feed.glsl")
env.GLES3_GLSL("scene.glsl")
env.GLES3_GLSL("sky.glsl")
env.GLES3_GLSL("canvas_occlusion.glsl")
env.GLES3_GLSL("canvas_sdf.glsl")
env.GLES3_GLSL("particles.glsl")
env.GLES3_GLSL("particles_copy.glsl")
env.GLES3_GLSL("skeleton.glsl")
# once we finish conversion we can introduce this to cover all files:
# for glsl_file in glsl_files:
# env.GLES3_GLSL(glsl_file)
SConscript("effects/SCsub")

View File

@@ -0,0 +1,870 @@
/* clang-format off */
#[modes]
mode_default =
#[specializations]
DISABLE_LIGHTING = true
USE_RGBA_SHADOWS = false
USE_NINEPATCH = false
USE_PRIMITIVE = false
USE_ATTRIBUTES = false
USE_INSTANCING = false
#[vertex]
#ifdef USE_ATTRIBUTES
layout(location = 0) in vec2 vertex_attrib;
layout(location = 3) in vec4 color_attrib;
layout(location = 4) in vec2 uv_attrib;
#ifdef USE_INSTANCING
layout(location = 1) in highp vec4 instance_xform0;
layout(location = 2) in highp vec4 instance_xform1;
layout(location = 5) in highp uvec4 instance_color_custom_data; // Color packed into xy, custom_data packed into zw for compatibility with 3D
#endif // USE_INSTANCING
#endif // USE_ATTRIBUTES
#include "stdlib_inc.glsl"
#if defined(CUSTOM0_USED)
layout(location = 6) in highp vec4 custom0_attrib;
#endif
#if defined(CUSTOM1_USED)
layout(location = 7) in highp vec4 custom1_attrib;
#endif
layout(location = 8) in highp vec4 attrib_A;
layout(location = 9) in highp vec4 attrib_B;
layout(location = 10) in highp vec4 attrib_C;
layout(location = 11) in highp vec4 attrib_D;
layout(location = 12) in highp vec4 attrib_E;
#ifdef USE_PRIMITIVE
layout(location = 13) in highp uvec4 attrib_F;
#else
layout(location = 13) in highp vec4 attrib_F;
#endif
layout(location = 14) in highp uvec4 attrib_G;
layout(location = 15) in highp uvec4 attrib_H;
#define read_draw_data_world_x attrib_A.xy
#define read_draw_data_world_y attrib_A.zw
#define read_draw_data_world_ofs attrib_B.xy
#define read_draw_data_color_texture_pixel_size attrib_B.zw
#ifdef USE_PRIMITIVE
#define read_draw_data_point_a attrib_C.xy
#define read_draw_data_point_b attrib_C.zw
#define read_draw_data_point_c attrib_D.xy
#define read_draw_data_uv_a attrib_D.zw
#define read_draw_data_uv_b attrib_E.xy
#define read_draw_data_uv_c attrib_E.zw
#define read_draw_data_color_a_rg attrib_F.x
#define read_draw_data_color_a_ba attrib_F.y
#define read_draw_data_color_b_rg attrib_F.z
#define read_draw_data_color_b_ba attrib_F.w
#define read_draw_data_color_c_rg attrib_G.x
#define read_draw_data_color_c_ba attrib_G.y
#else
#define read_draw_data_modulation attrib_C
#define read_draw_data_ninepatch_margins attrib_D
#define read_draw_data_dst_rect attrib_E
#define read_draw_data_src_rect attrib_F
#endif
#define read_draw_data_flags attrib_G.z
#define read_draw_data_instance_offset attrib_G.w
#define read_draw_data_lights attrib_H
// Varyings so the per-instance info can be used in the fragment shader
flat out vec4 varying_A;
flat out vec2 varying_B;
#ifndef USE_PRIMITIVE
flat out vec4 varying_C;
#ifndef USE_ATTRIBUTES
#ifdef USE_NINEPATCH
flat out vec2 varying_D;
#endif
flat out vec4 varying_E;
#endif
#endif
flat out uvec2 varying_F;
flat out uvec4 varying_G;
// This needs to be outside clang-format so the ubo comment is in the right place
#ifdef MATERIAL_UNIFORMS_USED
layout(std140) uniform MaterialUniforms{ //ubo:4
#MATERIAL_UNIFORMS
};
#endif
uniform mediump uint batch_flags;
/* clang-format on */
#include "canvas_uniforms_inc.glsl"
out vec2 uv_interp;
out vec4 color_interp;
out vec2 vertex_interp;
#ifdef USE_NINEPATCH
out vec2 pixel_size_interp;
#endif
#GLOBALS
void main() {
varying_A = vec4(read_draw_data_world_x, read_draw_data_world_y);
varying_B = read_draw_data_color_texture_pixel_size;
#ifndef USE_PRIMITIVE
varying_C = read_draw_data_ninepatch_margins;
#ifndef USE_ATTRIBUTES
#ifdef USE_NINEPATCH
varying_D = vec2(read_draw_data_dst_rect.z, read_draw_data_dst_rect.w);
#endif // USE_NINEPATCH
varying_E = read_draw_data_src_rect;
#endif // !USE_ATTRIBUTES
#endif // USE_PRIMITIVE
varying_F = uvec2(read_draw_data_flags, read_draw_data_instance_offset);
varying_G = read_draw_data_lights;
vec4 instance_custom = vec4(0.0);
#if defined(CUSTOM0_USED)
vec4 custom0 = vec4(0.0);
#endif
#if defined(CUSTOM1_USED)
vec4 custom1 = vec4(0.0);
#endif
#ifdef USE_PRIMITIVE
vec2 vertex;
vec2 uv;
vec4 color;
if (gl_VertexID % 3 == 0) {
vertex = read_draw_data_point_a;
uv = read_draw_data_uv_a;
color.xy = unpackHalf2x16(read_draw_data_color_a_rg);
color.zw = unpackHalf2x16(read_draw_data_color_a_ba);
} else if (gl_VertexID % 3 == 1) {
vertex = read_draw_data_point_b;
uv = read_draw_data_uv_b;
color.xy = unpackHalf2x16(read_draw_data_color_b_rg);
color.zw = unpackHalf2x16(read_draw_data_color_b_ba);
} else {
vertex = read_draw_data_point_c;
uv = read_draw_data_uv_c;
color.xy = unpackHalf2x16(read_draw_data_color_c_rg);
color.zw = unpackHalf2x16(read_draw_data_color_c_ba);
}
#elif defined(USE_ATTRIBUTES)
vec2 vertex = vertex_attrib;
vec4 color = color_attrib * read_draw_data_modulation;
vec2 uv = uv_attrib;
#ifdef USE_INSTANCING
if (bool(batch_flags & BATCH_FLAGS_INSTANCING_HAS_COLORS)) {
vec4 instance_color;
instance_color.xy = unpackHalf2x16(uint(instance_color_custom_data.x));
instance_color.zw = unpackHalf2x16(uint(instance_color_custom_data.y));
color *= instance_color;
}
if (bool(batch_flags & BATCH_FLAGS_INSTANCING_HAS_CUSTOM_DATA)) {
instance_custom.xy = unpackHalf2x16(instance_color_custom_data.z);
instance_custom.zw = unpackHalf2x16(instance_color_custom_data.w);
}
#endif // !USE_INSTANCING
#else // !USE_ATTRIBUTES
// crash on Adreno 320/330
//vec2 vertex_base_arr[6] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0), vec2(0.0, 0.0), vec2(1.0, 1.0));
//vec2 vertex_base = vertex_base_arr[gl_VertexID % 6];
//-----------------------------------------
// ID | 0 | 1 | 2 | 3 | 4 | 5 |
//-----------------------------------------
// X | 0.0 | 0.0 | 1.0 | 1.0 | 0.0 | 1.0 |
// Y | 0.0 | 1.0 | 1.0 | 0.0 | 0.0 | 1.0 |
//-----------------------------------------
// no crash or freeze on all Adreno 3xx with 'if / else if' and slightly faster!
int vertex_id = gl_VertexID % 6;
vec2 vertex_base;
if (vertex_id == 0) {
vertex_base = vec2(0.0, 0.0);
} else if (vertex_id == 1) {
vertex_base = vec2(0.0, 1.0);
} else if (vertex_id == 2) {
vertex_base = vec2(1.0, 1.0);
} else if (vertex_id == 3) {
vertex_base = vec2(1.0, 0.0);
} else if (vertex_id == 4) {
vertex_base = vec2(0.0, 0.0);
} else if (vertex_id == 5) {
vertex_base = vec2(1.0, 1.0);
}
vec2 uv = read_draw_data_src_rect.xy + abs(read_draw_data_src_rect.zw) * ((read_draw_data_flags & INSTANCE_FLAGS_TRANSPOSE_RECT) != uint(0) ? vertex_base.yx : vertex_base.xy);
vec4 color = read_draw_data_modulation;
vec2 vertex = read_draw_data_dst_rect.xy + abs(read_draw_data_dst_rect.zw) * mix(vertex_base, vec2(1.0, 1.0) - vertex_base, lessThan(read_draw_data_src_rect.zw, vec2(0.0, 0.0)));
#endif // USE_ATTRIBUTES
#if defined(CUSTOM0_USED)
custom0 = custom0_attrib;
#endif
#if defined(CUSTOM1_USED)
custom1 = custom1_attrib;
#endif
mat4 model_matrix = mat4(vec4(read_draw_data_world_x, 0.0, 0.0), vec4(read_draw_data_world_y, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(read_draw_data_world_ofs, 0.0, 1.0));
#ifdef USE_INSTANCING
model_matrix = model_matrix * transpose(mat4(instance_xform0, instance_xform1, vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0)));
#endif // USE_INSTANCING
vec2 color_texture_pixel_size = read_draw_data_color_texture_pixel_size;
#ifdef USE_POINT_SIZE
float point_size = 1.0;
#endif
#ifdef USE_WORLD_VERTEX_COORDS
vertex = (model_matrix * vec4(vertex, 0.0, 1.0)).xy;
#endif
{
#CODE : VERTEX
}
#ifdef USE_NINEPATCH
pixel_size_interp = abs(read_draw_data_dst_rect.zw) * vertex_base;
#endif
#if !defined(SKIP_TRANSFORM_USED) && !defined(USE_WORLD_VERTEX_COORDS)
vertex = (model_matrix * vec4(vertex, 0.0, 1.0)).xy;
#endif
color_interp = color;
vertex = (canvas_transform * vec4(vertex, 0.0, 1.0)).xy;
if (use_pixel_snap) {
vertex = floor(vertex + 0.5);
// precision issue on some hardware creates artifacts within texture
// offset uv by a small amount to avoid
uv += 1e-5;
}
vertex_interp = vertex;
uv_interp = uv;
gl_Position = screen_transform * vec4(vertex, 0.0, 1.0);
#ifdef USE_POINT_SIZE
gl_PointSize = point_size;
#endif
}
#[fragment]
#include "canvas_uniforms_inc.glsl"
#include "stdlib_inc.glsl"
in vec2 uv_interp;
in vec2 vertex_interp;
in vec4 color_interp;
#ifdef USE_NINEPATCH
in vec2 pixel_size_interp;
#endif
// Can all be flat as they are the same for the whole batched instance
flat in vec4 varying_A;
flat in vec2 varying_B;
#define read_draw_data_world_x varying_A.xy
#define read_draw_data_world_y varying_A.zw
#define read_draw_data_color_texture_pixel_size varying_B
#ifndef USE_PRIMITIVE
flat in vec4 varying_C;
#define read_draw_data_ninepatch_margins varying_C
#ifndef USE_ATTRIBUTES
#ifdef USE_NINEPATCH
flat in vec2 varying_D;
#define read_draw_data_dst_rect_z varying_D.x
#define read_draw_data_dst_rect_w varying_D.y
#endif
flat in vec4 varying_E;
#define read_draw_data_src_rect varying_E
#endif // USE_ATTRIBUTES
#endif // USE_PRIMITIVE
flat in uvec2 varying_F;
flat in uvec4 varying_G;
#define read_draw_data_flags varying_F.x
#define read_draw_data_instance_offset varying_F.y
#define read_draw_data_lights varying_G
#ifndef DISABLE_LIGHTING
uniform sampler2D atlas_texture; //texunit:-2
uniform sampler2D shadow_atlas_texture; //texunit:-3
#endif // DISABLE_LIGHTING
uniform sampler2D color_buffer; //texunit:-4
uniform sampler2D sdf_texture; //texunit:-5
uniform sampler2D normal_texture; //texunit:-6
uniform sampler2D specular_texture; //texunit:-7
uniform sampler2D color_texture; //texunit:0
uniform mediump uint batch_flags;
uniform highp uint specular_shininess_in;
layout(location = 0) out vec4 frag_color;
/* clang-format off */
// This needs to be outside clang-format so the ubo comment is in the right place
#ifdef MATERIAL_UNIFORMS_USED
layout(std140) uniform MaterialUniforms{ //ubo:4
#MATERIAL_UNIFORMS
};
#endif
/* clang-format on */
#GLOBALS
float vec4_to_float(vec4 p_vec) {
return dot(p_vec, vec4(1.0 / (255.0 * 255.0 * 255.0), 1.0 / (255.0 * 255.0), 1.0 / 255.0, 1.0)) * 2.0 - 1.0;
}
vec2 screen_uv_to_sdf(vec2 p_uv) {
return screen_to_sdf * p_uv;
}
float texture_sdf(vec2 p_sdf) {
vec2 uv = p_sdf * sdf_to_tex.xy + sdf_to_tex.zw;
float d = vec4_to_float(texture(sdf_texture, uv));
d *= SDF_MAX_LENGTH;
return d * tex_to_sdf;
}
vec2 texture_sdf_normal(vec2 p_sdf) {
vec2 uv = p_sdf * sdf_to_tex.xy + sdf_to_tex.zw;
const float EPSILON = 0.001;
return normalize(vec2(
vec4_to_float(texture(sdf_texture, uv + vec2(EPSILON, 0.0))) - vec4_to_float(texture(sdf_texture, uv - vec2(EPSILON, 0.0))),
vec4_to_float(texture(sdf_texture, uv + vec2(0.0, EPSILON))) - vec4_to_float(texture(sdf_texture, uv - vec2(0.0, EPSILON)))));
}
vec2 sdf_to_screen_uv(vec2 p_sdf) {
return p_sdf * sdf_to_screen;
}
#ifndef DISABLE_LIGHTING
#ifdef LIGHT_CODE_USED
vec4 light_compute(
vec3 light_vertex,
vec3 light_position,
vec3 normal,
vec4 light_color,
float light_energy,
vec4 specular_shininess,
inout vec4 shadow_modulate,
vec2 screen_uv,
vec2 uv,
vec4 color, bool is_directional) {
vec4 light = vec4(0.0);
vec3 light_direction = vec3(0.0);
if (is_directional) {
light_direction = normalize(mix(vec3(light_position.xy, 0.0), vec3(0, 0, 1), light_position.z));
light_position = vec3(0.0);
} else {
light_direction = normalize(light_position - light_vertex);
}
#CODE : LIGHT
return light;
}
#endif
vec3 light_normal_compute(vec3 light_vec, vec3 normal, vec3 base_color, vec3 light_color, vec4 specular_shininess, bool specular_shininess_used) {
float cNdotL = max(0.0, dot(normal, light_vec));
if (specular_shininess_used) {
//blinn
vec3 view = vec3(0.0, 0.0, 1.0); // not great but good enough
vec3 half_vec = normalize(view + light_vec);
float cNdotV = max(dot(normal, view), 0.0);
float cNdotH = max(dot(normal, half_vec), 0.0);
float cVdotH = max(dot(view, half_vec), 0.0);
float cLdotH = max(dot(light_vec, half_vec), 0.0);
float shininess = exp2(15.0 * specular_shininess.a + 1.0) * 0.25;
float blinn = pow(cNdotH, shininess);
blinn *= (shininess + 8.0) * (1.0 / (8.0 * M_PI));
float s = (blinn) / max(4.0 * cNdotV * cNdotL, 0.75);
return specular_shininess.rgb * light_color * s + light_color * base_color * cNdotL;
} else {
return light_color * base_color * cNdotL;
}
}
#ifdef USE_RGBA_SHADOWS
#define SHADOW_DEPTH(m_uv) (dot(textureLod(shadow_atlas_texture, (m_uv), 0.0), vec4(1.0 / (255.0 * 255.0 * 255.0), 1.0 / (255.0 * 255.0), 1.0 / 255.0, 1.0)) * 2.0 - 1.0)
#else
#define SHADOW_DEPTH(m_uv) (textureLod(shadow_atlas_texture, (m_uv), 0.0).r)
#endif
/* clang-format off */
#define SHADOW_TEST(m_uv) { highp float sd = SHADOW_DEPTH(m_uv); shadow += step(sd, shadow_uv.z / shadow_uv.w); }
/* clang-format on */
//float distance = length(shadow_pos);
vec4 light_shadow_compute(uint light_base, vec4 light_color, vec4 shadow_uv
#ifdef LIGHT_CODE_USED
,
vec3 shadow_modulate
#endif
) {
float shadow = 0.0;
uint shadow_mode = light_array[light_base].flags & LIGHT_FLAGS_FILTER_MASK;
if (shadow_mode == LIGHT_FLAGS_SHADOW_NEAREST) {
SHADOW_TEST(shadow_uv.xy);
} else if (shadow_mode == LIGHT_FLAGS_SHADOW_PCF5) {
vec2 shadow_pixel_size = vec2(light_array[light_base].shadow_pixel_size, 0.0);
SHADOW_TEST(shadow_uv.xy - shadow_pixel_size * 2.0);
SHADOW_TEST(shadow_uv.xy - shadow_pixel_size);
SHADOW_TEST(shadow_uv.xy);
SHADOW_TEST(shadow_uv.xy + shadow_pixel_size);
SHADOW_TEST(shadow_uv.xy + shadow_pixel_size * 2.0);
shadow /= 5.0;
} else { //PCF13
vec2 shadow_pixel_size = vec2(light_array[light_base].shadow_pixel_size, 0.0);
SHADOW_TEST(shadow_uv.xy - shadow_pixel_size * 6.0);
SHADOW_TEST(shadow_uv.xy - shadow_pixel_size * 5.0);
SHADOW_TEST(shadow_uv.xy - shadow_pixel_size * 4.0);
SHADOW_TEST(shadow_uv.xy - shadow_pixel_size * 3.0);
SHADOW_TEST(shadow_uv.xy - shadow_pixel_size * 2.0);
SHADOW_TEST(shadow_uv.xy - shadow_pixel_size);
SHADOW_TEST(shadow_uv.xy);
SHADOW_TEST(shadow_uv.xy + shadow_pixel_size);
SHADOW_TEST(shadow_uv.xy + shadow_pixel_size * 2.0);
SHADOW_TEST(shadow_uv.xy + shadow_pixel_size * 3.0);
SHADOW_TEST(shadow_uv.xy + shadow_pixel_size * 4.0);
SHADOW_TEST(shadow_uv.xy + shadow_pixel_size * 5.0);
SHADOW_TEST(shadow_uv.xy + shadow_pixel_size * 6.0);
shadow /= 13.0;
}
vec4 shadow_color = godot_unpackUnorm4x8(light_array[light_base].shadow_color);
#ifdef LIGHT_CODE_USED
shadow_color.rgb *= shadow_modulate;
#endif
shadow_color.a *= light_color.a; //respect light alpha
return mix(light_color, shadow_color, shadow);
}
void light_blend_compute(uint light_base, vec4 light_color, inout vec3 color) {
uint blend_mode = light_array[light_base].flags & LIGHT_FLAGS_BLEND_MASK;
if (blend_mode == LIGHT_FLAGS_BLEND_MODE_ADD) {
color.rgb += light_color.rgb * light_color.a;
} else if (blend_mode == LIGHT_FLAGS_BLEND_MODE_SUB) {
color.rgb -= light_color.rgb * light_color.a;
} else if (blend_mode == LIGHT_FLAGS_BLEND_MODE_MIX) {
color.rgb = mix(color.rgb, light_color.rgb, light_color.a);
}
}
#endif
#ifdef USE_NINEPATCH
float map_ninepatch_axis(float pixel, float draw_size, float tex_pixel_size, float margin_begin, float margin_end, int np_repeat, inout int draw_center) {
float tex_size = 1.0 / tex_pixel_size;
if (pixel < margin_begin) {
return pixel * tex_pixel_size;
} else if (pixel >= draw_size - margin_end) {
return (tex_size - (draw_size - pixel)) * tex_pixel_size;
} else {
if (!bool(read_draw_data_flags & INSTANCE_FLAGS_NINEPATCH_DRAW_CENTER)) {
draw_center--;
}
// np_repeat is passed as uniform using NinePatchRect::AxisStretchMode enum.
if (np_repeat == 0) { // Stretch.
// Convert to ratio.
float ratio = (pixel - margin_begin) / (draw_size - margin_begin - margin_end);
// Scale to source texture.
return (margin_begin + ratio * (tex_size - margin_begin - margin_end)) * tex_pixel_size;
} else if (np_repeat == 1) { // Tile.
// Convert to offset.
float ofs = mod((pixel - margin_begin), tex_size - margin_begin - margin_end);
// Scale to source texture.
return (margin_begin + ofs) * tex_pixel_size;
} else if (np_repeat == 2) { // Tile Fit.
// Calculate scale.
float src_area = draw_size - margin_begin - margin_end;
float dst_area = tex_size - margin_begin - margin_end;
float scale = max(1.0, floor(src_area / max(dst_area, 0.0000001) + 0.5));
// Convert to ratio.
float ratio = (pixel - margin_begin) / src_area;
ratio = mod(ratio * scale, 1.0);
// Scale to source texture.
return (margin_begin + ratio * dst_area) * tex_pixel_size;
} else { // Shouldn't happen, but silences compiler warning.
return 0.0;
}
}
}
#endif
float msdf_median(float r, float g, float b) {
return max(min(r, g), min(max(r, g), b));
}
void main() {
vec4 color = color_interp;
vec2 uv = uv_interp;
vec2 vertex = vertex_interp;
#if !defined(USE_ATTRIBUTES) && !defined(USE_PRIMITIVE)
vec4 region_rect = read_draw_data_src_rect;
#else
vec4 region_rect = vec4(0.0, 0.0, 1.0 / read_draw_data_color_texture_pixel_size);
#endif
#if !defined(USE_ATTRIBUTES) && !defined(USE_PRIMITIVE)
#ifdef USE_NINEPATCH
int draw_center = 2;
uv = vec2(
map_ninepatch_axis(pixel_size_interp.x, abs(read_draw_data_dst_rect_z), read_draw_data_color_texture_pixel_size.x, read_draw_data_ninepatch_margins.x, read_draw_data_ninepatch_margins.z, int(read_draw_data_flags >> INSTANCE_FLAGS_NINEPATCH_H_MODE_SHIFT) & 0x3, draw_center),
map_ninepatch_axis(pixel_size_interp.y, abs(read_draw_data_dst_rect_w), read_draw_data_color_texture_pixel_size.y, read_draw_data_ninepatch_margins.y, read_draw_data_ninepatch_margins.w, int(read_draw_data_flags >> INSTANCE_FLAGS_NINEPATCH_V_MODE_SHIFT) & 0x3, draw_center));
if (draw_center == 0) {
color.a = 0.0;
}
uv = uv * read_draw_data_src_rect.zw + read_draw_data_src_rect.xy; //apply region if needed
#endif
if (bool(read_draw_data_flags & INSTANCE_FLAGS_CLIP_RECT_UV)) {
vec2 half_texpixel = read_draw_data_color_texture_pixel_size * 0.5;
uv = clamp(uv, read_draw_data_src_rect.xy + half_texpixel, read_draw_data_src_rect.xy + abs(read_draw_data_src_rect.zw) - half_texpixel);
}
#endif
#ifndef USE_PRIMITIVE
if (bool(read_draw_data_flags & INSTANCE_FLAGS_USE_MSDF)) {
float px_range = read_draw_data_ninepatch_margins.x;
float outline_thickness = read_draw_data_ninepatch_margins.y;
vec4 msdf_sample = texture(color_texture, uv);
vec2 msdf_size = vec2(textureSize(color_texture, 0));
vec2 dest_size = vec2(1.0) / fwidth(uv);
float px_size = max(0.5 * dot((vec2(px_range) / msdf_size), dest_size), 1.0);
float d = msdf_median(msdf_sample.r, msdf_sample.g, msdf_sample.b);
if (outline_thickness > 0.0) {
float cr = clamp(outline_thickness, 0.0, (px_range / 2.0) - 1.0) / px_range;
d = min(d, msdf_sample.a);
float a = clamp((d - 0.5 + cr) * px_size, 0.0, 1.0);
color.a = a * color.a;
} else {
float a = clamp((d - 0.5) * px_size + 0.5, 0.0, 1.0);
color.a = a * color.a;
}
} else if (bool(read_draw_data_flags & INSTANCE_FLAGS_USE_LCD)) {
vec4 lcd_sample = texture(color_texture, uv);
if (lcd_sample.a == 1.0) {
color.rgb = lcd_sample.rgb * color.a;
} else {
color = vec4(0.0, 0.0, 0.0, 0.0);
}
} else {
#else
{
#endif
color *= texture(color_texture, uv);
}
uint light_count = read_draw_data_flags & uint(0xF); // Max 16 lights.
bool using_light = light_count > 0u || directional_light_count > 0u;
vec3 normal;
#if defined(NORMAL_USED)
bool normal_used = true;
#else
bool normal_used = false;
#endif
if (normal_used || (using_light && bool(batch_flags & BATCH_FLAGS_DEFAULT_NORMAL_MAP_USED))) {
normal.xy = texture(normal_texture, uv).xy * vec2(2.0, -2.0) - vec2(1.0, -1.0);
#if !defined(USE_ATTRIBUTES) && !defined(USE_PRIMITIVE)
if (bool(read_draw_data_flags & INSTANCE_FLAGS_TRANSPOSE_RECT)) {
normal.xy = normal.yx;
}
normal.xy *= sign(read_draw_data_src_rect.zw);
#endif
normal.z = sqrt(max(0.0, 1.0 - dot(normal.xy, normal.xy)));
normal_used = true;
} else {
normal = vec3(0.0, 0.0, 1.0);
}
vec4 specular_shininess;
#if defined(SPECULAR_SHININESS_USED)
bool specular_shininess_used = true;
#else
bool specular_shininess_used = false;
#endif
if (specular_shininess_used || (using_light && normal_used && bool(batch_flags & BATCH_FLAGS_DEFAULT_SPECULAR_MAP_USED))) {
specular_shininess = texture(specular_texture, uv);
specular_shininess *= godot_unpackUnorm4x8(specular_shininess_in);
specular_shininess_used = true;
} else {
specular_shininess = vec4(1.0);
}
#if defined(SCREEN_UV_USED)
vec2 screen_uv = gl_FragCoord.xy * screen_pixel_size;
#else
vec2 screen_uv = vec2(0.0);
#endif
vec2 color_texture_pixel_size = read_draw_data_color_texture_pixel_size.xy;
vec3 light_vertex = vec3(vertex, 0.0);
vec2 shadow_vertex = vertex;
{
float normal_map_depth = 1.0;
#if defined(NORMAL_MAP_USED)
vec3 normal_map = vec3(0.0, 0.0, 1.0);
normal_used = true;
#endif
#CODE : FRAGMENT
#if defined(NORMAL_MAP_USED)
normal = mix(vec3(0.0, 0.0, 1.0), normal_map * vec3(2.0, -2.0, 1.0) - vec3(1.0, -1.0, 0.0), normal_map_depth);
#endif
}
if (normal_used) {
//convert by item transform
normal.xy = mat2(normalize(read_draw_data_world_x), normalize(read_draw_data_world_y)) * normal.xy;
//convert by canvas transform
normal = normalize((canvas_normal_transform * vec4(normal, 0.0)).xyz);
}
vec4 base_color = color;
#ifdef MODE_LIGHT_ONLY
float light_only_alpha = 0.0;
#elif !defined(MODE_UNSHADED)
color *= canvas_modulation;
#endif
#if !defined(DISABLE_LIGHTING) && !defined(MODE_UNSHADED)
// Directional Lights
for (uint i = 0u; i < directional_light_count; i++) {
uint light_base = i;
vec2 direction = light_array[light_base].position;
vec4 light_color = light_array[light_base].color;
#ifdef LIGHT_CODE_USED
vec4 shadow_modulate = vec4(1.0);
light_color = light_compute(light_vertex, vec3(direction, light_array[light_base].height), normal, light_color, light_color.a, specular_shininess, shadow_modulate, screen_uv, uv, base_color, true);
#else
if (normal_used) {
vec3 light_vec = normalize(mix(vec3(direction, 0.0), vec3(0, 0, 1), light_array[light_base].height));
light_color.rgb = light_normal_compute(light_vec, normal, base_color.rgb, light_color.rgb, specular_shininess, specular_shininess_used);
} else {
light_color.rgb *= base_color.rgb;
}
#endif
if (bool(light_array[light_base].flags & LIGHT_FLAGS_HAS_SHADOW)) {
vec2 shadow_pos = (vec4(shadow_vertex, 0.0, 1.0) * mat4(light_array[light_base].shadow_matrix[0], light_array[light_base].shadow_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations.
vec4 shadow_uv = vec4(shadow_pos.x, light_array[light_base].shadow_y_ofs, shadow_pos.y * light_array[light_base].shadow_zfar_inv, 1.0);
light_color = light_shadow_compute(light_base, light_color, shadow_uv
#ifdef LIGHT_CODE_USED
,
shadow_modulate.rgb
#endif
);
}
light_blend_compute(light_base, light_color, color.rgb);
#ifdef MODE_LIGHT_ONLY
light_only_alpha += light_color.a;
#endif
}
// Positional Lights
for (uint i = 0u; i < MAX_LIGHTS_PER_ITEM; i++) {
if (i >= light_count) {
break;
}
uint light_base;
if (i < 8u) {
if (i < 4u) {
light_base = read_draw_data_lights[0];
} else {
light_base = read_draw_data_lights[1];
}
} else {
if (i < 12u) {
light_base = read_draw_data_lights[2];
} else {
light_base = read_draw_data_lights[3];
}
}
light_base >>= (i & 3u) * 8u;
light_base &= uint(0xFF);
vec2 tex_uv = (vec4(vertex, 0.0, 1.0) * mat4(light_array[light_base].texture_matrix[0], light_array[light_base].texture_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations.
vec2 tex_uv_atlas = tex_uv * light_array[light_base].atlas_rect.zw + light_array[light_base].atlas_rect.xy;
if (any(lessThan(tex_uv, vec2(0.0, 0.0))) || any(greaterThanEqual(tex_uv, vec2(1.0, 1.0)))) {
//if outside the light texture, light color is zero
continue;
}
vec4 light_color = textureLod(atlas_texture, tex_uv_atlas, 0.0);
vec4 light_base_color = light_array[light_base].color;
#ifdef LIGHT_CODE_USED
vec4 shadow_modulate = vec4(1.0);
vec3 light_position = vec3(light_array[light_base].position, light_array[light_base].height);
light_color.rgb *= light_base_color.rgb;
light_color = light_compute(light_vertex, light_position, normal, light_color, light_base_color.a, specular_shininess, shadow_modulate, screen_uv, uv, base_color, false);
#else
light_color.rgb *= light_base_color.rgb * light_base_color.a;
if (normal_used) {
vec3 light_pos = vec3(light_array[light_base].position, light_array[light_base].height);
vec3 pos = light_vertex;
vec3 light_vec = normalize(light_pos - pos);
light_color.rgb = light_normal_compute(light_vec, normal, base_color.rgb, light_color.rgb, specular_shininess, specular_shininess_used);
} else {
light_color.rgb *= base_color.rgb;
}
#endif
if (bool(light_array[light_base].flags & LIGHT_FLAGS_HAS_SHADOW) && bool(read_draw_data_flags & uint(INSTANCE_FLAGS_SHADOW_MASKED << i))) {
vec2 shadow_pos = (vec4(shadow_vertex, 0.0, 1.0) * mat4(light_array[light_base].shadow_matrix[0], light_array[light_base].shadow_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations.
vec2 pos_norm = normalize(shadow_pos);
vec2 pos_abs = abs(pos_norm);
vec2 pos_box = pos_norm / max(pos_abs.x, pos_abs.y);
vec2 pos_rot = pos_norm * mat2(vec2(0.7071067811865476, -0.7071067811865476), vec2(0.7071067811865476, 0.7071067811865476)); //is there a faster way to 45 degrees rot?
float tex_ofs;
float dist;
if (pos_rot.y > 0.0) {
if (pos_rot.x > 0.0) {
tex_ofs = pos_box.y * 0.125 + 0.125;
dist = shadow_pos.x;
} else {
tex_ofs = pos_box.x * -0.125 + (0.25 + 0.125);
dist = shadow_pos.y;
}
} else {
if (pos_rot.x < 0.0) {
tex_ofs = pos_box.y * -0.125 + (0.5 + 0.125);
dist = -shadow_pos.x;
} else {
tex_ofs = pos_box.x * 0.125 + (0.75 + 0.125);
dist = -shadow_pos.y;
}
}
dist *= light_array[light_base].shadow_zfar_inv;
//float distance = length(shadow_pos);
vec4 shadow_uv = vec4(tex_ofs, light_array[light_base].shadow_y_ofs, dist, 1.0);
light_color = light_shadow_compute(light_base, light_color, shadow_uv
#ifdef LIGHT_CODE_USED
,
shadow_modulate.rgb
#endif
);
}
light_blend_compute(light_base, light_color, color.rgb);
#ifdef MODE_LIGHT_ONLY
light_only_alpha += light_color.a;
#endif
}
#endif
#ifdef MODE_LIGHT_ONLY
color.a *= light_only_alpha;
#endif
frag_color = color;
}

View File

@@ -0,0 +1,68 @@
/* clang-format off */
#[modes]
mode_sdf =
mode_shadow = #define MODE_SHADOW
mode_shadow_RGBA = #define MODE_SHADOW \n#define USE_RGBA_SHADOWS
#[specializations]
#[vertex]
layout(location = 0) in vec3 vertex;
uniform highp mat4 projection;
uniform highp vec4 modelview1;
uniform highp vec4 modelview2;
uniform highp vec2 direction;
uniform highp float z_far;
#ifdef MODE_SHADOW
out float depth;
#endif
void main() {
highp vec4 vtx = vec4(vertex, 1.0) * mat4(modelview1, modelview2, vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0));
#ifdef MODE_SHADOW
depth = dot(direction, vtx.xy);
#endif
gl_Position = projection * vtx;
}
#[fragment]
uniform highp mat4 projection;
uniform highp vec4 modelview1;
uniform highp vec4 modelview2;
uniform highp vec2 direction;
uniform highp float z_far;
#ifdef MODE_SHADOW
in highp float depth;
#endif
#ifdef USE_RGBA_SHADOWS
layout(location = 0) out lowp vec4 out_buf;
#else
layout(location = 0) out highp float out_buf;
#endif
void main() {
float out_depth = 1.0;
#ifdef MODE_SHADOW
out_depth = depth / z_far;
#endif
#ifdef USE_RGBA_SHADOWS
out_depth = clamp(out_depth, -1.0, 1.0);
out_depth = out_depth * 0.5 + 0.5;
highp vec4 comp = fract(out_depth * vec4(255.0 * 255.0 * 255.0, 255.0 * 255.0, 255.0, 1.0));
comp -= comp.xxyz * vec4(0.0, 1.0 / 255.0, 1.0 / 255.0, 1.0 / 255.0);
out_buf = comp;
#else
out_buf = out_depth;
#endif
}

View File

@@ -0,0 +1,205 @@
/* clang-format off */
#[modes]
mode_load = #define MODE_LOAD
mode_load_shrink = #define MODE_LOAD_SHRINK
mode_process = #define MODE_PROCESS
mode_store = #define MODE_STORE
mode_store_shrink = #define MODE_STORE_SHRINK
#[specializations]
#[vertex]
layout(location = 0) in vec2 vertex_attrib;
/* clang-format on */
uniform ivec2 size;
uniform int stride;
uniform int shift;
uniform ivec2 base_size;
void main() {
gl_Position = vec4(vertex_attrib, 1.0, 1.0);
}
/* clang-format off */
#[fragment]
#define SDF_MAX_LENGTH 16384.0
#if defined(MODE_LOAD) || defined(MODE_LOAD_SHRINK)
uniform lowp sampler2D src_pixels;//texunit:0
#else
uniform highp isampler2D src_process;//texunit:0
#endif
uniform ivec2 size;
uniform int stride;
uniform int shift;
uniform ivec2 base_size;
#if defined(MODE_LOAD) || defined(MODE_LOAD_SHRINK) || defined(MODE_PROCESS)
layout(location = 0) out ivec4 distance_field;
#else
layout(location = 0) out vec4 distance_field;
#endif
vec4 float_to_vec4(float p_float) {
highp vec4 comp = fract(p_float * vec4(255.0 * 255.0 * 255.0, 255.0 * 255.0, 255.0, 1.0));
comp -= comp.xxyz * vec4(0.0, 1.0 / 255.0, 1.0 / 255.0, 1.0 / 255.0);
return comp;
}
void main() {
ivec2 pos = ivec2(gl_FragCoord.xy);
#ifdef MODE_LOAD
bool solid = texelFetch(src_pixels, pos, 0).r > 0.5;
distance_field = solid ? ivec4(ivec2(-32767), 0, 0) : ivec4(ivec2(32767), 0, 0);
#endif
#ifdef MODE_LOAD_SHRINK
int s = 1 << shift;
ivec2 base = pos << shift;
ivec2 center = base + ivec2(shift);
ivec2 rel = ivec2(32767);
float d = 1e20;
int found = 0;
int solid_found = 0;
for (int i = 0; i < s; i++) {
for (int j = 0; j < s; j++) {
ivec2 src_pos = base + ivec2(i, j);
if (any(greaterThanEqual(src_pos, base_size))) {
continue;
}
bool solid = texelFetch(src_pixels, src_pos, 0).r > 0.5;
if (solid) {
float dist = length(vec2(src_pos - center));
if (dist < d) {
d = dist;
rel = src_pos;
}
solid_found++;
}
found++;
}
}
if (solid_found == found) {
//mark solid only if all are solid
rel = ivec2(-32767);
}
distance_field = ivec4(rel, 0, 0);
#endif
#ifdef MODE_PROCESS
ivec2 base = pos << shift;
ivec2 center = base + ivec2(shift);
ivec2 rel = texelFetch(src_process, pos, 0).xy;
bool solid = rel.x < 0;
if (solid) {
rel = -rel - ivec2(1);
}
if (center != rel) {
//only process if it does not point to itself
const int ofs_table_size = 8;
const ivec2 ofs_table[ofs_table_size] = ivec2[](
ivec2(-1, -1),
ivec2(0, -1),
ivec2(+1, -1),
ivec2(-1, 0),
ivec2(+1, 0),
ivec2(-1, +1),
ivec2(0, +1),
ivec2(+1, +1));
float dist = length(vec2(rel - center));
for (int i = 0; i < ofs_table_size; i++) {
ivec2 src_pos = pos + ofs_table[i] * stride;
if (any(lessThan(src_pos, ivec2(0))) || any(greaterThanEqual(src_pos, size))) {
continue;
}
ivec2 src_rel = texelFetch(src_process, src_pos, 0).xy;
bool src_solid = src_rel.x < 0;
if (src_solid) {
src_rel = -src_rel - ivec2(1);
}
if (src_solid != solid) {
src_rel = ivec2(src_pos << shift); //point to itself if of different type
}
float src_dist = length(vec2(src_rel - center));
if (src_dist < dist) {
dist = src_dist;
rel = src_rel;
}
}
}
if (solid) {
rel = -rel - ivec2(1);
}
distance_field = ivec4(rel, 0, 0);
#endif
#ifdef MODE_STORE
ivec2 rel = texelFetch(src_process, pos, 0).xy;
bool solid = rel.x < 0;
if (solid) {
rel = -rel - ivec2(1);
}
float d = length(vec2(rel - pos));
if (solid) {
d = -d;
}
d /= SDF_MAX_LENGTH;
d = clamp(d, -1.0, 1.0);
distance_field = float_to_vec4(d*0.5+0.5);
#endif
#ifdef MODE_STORE_SHRINK
ivec2 base = pos << shift;
ivec2 center = base + ivec2(shift);
ivec2 rel = texelFetch(src_process, pos, 0).xy;
bool solid = rel.x < 0;
if (solid) {
rel = -rel - ivec2(1);
}
float d = length(vec2(rel - center));
if (solid) {
d = -d;
}
d /= SDF_MAX_LENGTH;
d = clamp(d, -1.0, 1.0);
distance_field = float_to_vec4(d*0.5+0.5);
#endif
}

View File

@@ -0,0 +1,87 @@
#define MAX_LIGHTS_PER_ITEM uint(16)
#define M_PI 3.14159265359
#define SDF_MAX_LENGTH 16384.0
#define INSTANCE_FLAGS_LIGHT_COUNT_SHIFT 0 // 4 bits.
#define INSTANCE_FLAGS_CLIP_RECT_UV uint(1 << 4)
#define INSTANCE_FLAGS_TRANSPOSE_RECT uint(1 << 5)
#define INSTANCE_FLAGS_USE_MSDF uint(1 << 6)
#define INSTANCE_FLAGS_USE_LCD uint(1 << 7)
#define INSTANCE_FLAGS_NINEPATCH_DRAW_CENTER uint(1 << 8)
#define INSTANCE_FLAGS_NINEPATCH_H_MODE_SHIFT 9
#define INSTANCE_FLAGS_NINEPATCH_V_MODE_SHIFT 11
#define INSTANCE_FLAGS_SHADOW_MASKED_SHIFT 13u // 16 bits.
#define INSTANCE_FLAGS_SHADOW_MASKED uint(1 << INSTANCE_FLAGS_SHADOW_MASKED_SHIFT)
// 1 means enabled, 2+ means trails in use
#define BATCH_FLAGS_INSTANCING_MASK uint(0x7F)
#define BATCH_FLAGS_INSTANCING_HAS_COLORS_SHIFT 7
#define BATCH_FLAGS_INSTANCING_HAS_COLORS uint(1 << BATCH_FLAGS_INSTANCING_HAS_COLORS_SHIFT)
#define BATCH_FLAGS_INSTANCING_HAS_CUSTOM_DATA_SHIFT 8
#define BATCH_FLAGS_INSTANCING_HAS_CUSTOM_DATA uint(1 << BATCH_FLAGS_INSTANCING_HAS_CUSTOM_DATA_SHIFT)
#define BATCH_FLAGS_DEFAULT_NORMAL_MAP_USED uint(1 << 9)
#define BATCH_FLAGS_DEFAULT_SPECULAR_MAP_USED uint(1 << 10)
layout(std140) uniform GlobalShaderUniformData { //ubo:1
vec4 global_shader_uniforms[MAX_GLOBAL_SHADER_UNIFORMS];
};
layout(std140) uniform CanvasData { //ubo:0
mat4 canvas_transform;
mat4 screen_transform;
mat4 canvas_normal_transform;
vec4 canvas_modulation;
vec2 screen_pixel_size;
float time;
bool use_pixel_snap;
vec4 sdf_to_tex;
vec2 screen_to_sdf;
vec2 sdf_to_screen;
uint directional_light_count;
float tex_to_sdf;
uint pad1;
uint pad2;
};
#ifndef DISABLE_LIGHTING
#define LIGHT_FLAGS_BLEND_MASK uint(3 << 16)
#define LIGHT_FLAGS_BLEND_MODE_ADD uint(0 << 16)
#define LIGHT_FLAGS_BLEND_MODE_SUB uint(1 << 16)
#define LIGHT_FLAGS_BLEND_MODE_MIX uint(2 << 16)
#define LIGHT_FLAGS_BLEND_MODE_MASK uint(3 << 16)
#define LIGHT_FLAGS_HAS_SHADOW uint(1 << 20)
#define LIGHT_FLAGS_FILTER_SHIFT 22
#define LIGHT_FLAGS_FILTER_MASK uint(3 << 22)
#define LIGHT_FLAGS_SHADOW_NEAREST uint(0 << 22)
#define LIGHT_FLAGS_SHADOW_PCF5 uint(1 << 22)
#define LIGHT_FLAGS_SHADOW_PCF13 uint(2 << 22)
struct Light {
mat2x4 texture_matrix; //light to texture coordinate matrix (transposed)
mat2x4 shadow_matrix; //light to shadow coordinate matrix (transposed)
vec4 color;
uint shadow_color; // packed
uint flags; //index to light texture
float shadow_pixel_size;
float height;
vec2 position;
float shadow_zfar_inv;
float shadow_y_ofs;
vec4 atlas_rect;
};
layout(std140) uniform LightData { //ubo:2
Light light_array[MAX_LIGHTS];
};
#endif // DISABLE_LIGHTING

View File

@@ -0,0 +1,100 @@
/* clang-format off */
[vertex]
#ifdef USE_GLES_OVER_GL
#define lowp
#define mediump
#define highp
#else
precision mediump float;
precision mediump int;
#endif
layout(location = 0) in highp vec4 vertex_attrib;
/* clang-format on */
layout(location = 4) in vec2 uv_in;
out vec2 uv_interp;
void main() {
uv_interp = uv_in;
gl_Position = vertex_attrib;
}
/* clang-format off */
[fragment]
#ifdef USE_GLES_OVER_GL
#define lowp
#define mediump
#define highp
#else
#if defined(USE_HIGHP_PRECISION)
precision highp float;
precision highp int;
#else
precision mediump float;
precision mediump int;
#endif
#endif
uniform highp samplerCube source_cube; //texunit:0
/* clang-format on */
in vec2 uv_interp;
uniform bool z_flip;
uniform highp float z_far;
uniform highp float z_near;
uniform highp float bias;
void main() {
highp vec3 normal = vec3(uv_interp * 2.0 - 1.0, 0.0);
/*
if (z_flip) {
normal.z = 0.5 - 0.5 * ((normal.x * normal.x) + (normal.y * normal.y));
} else {
normal.z = -0.5 + 0.5 * ((normal.x * normal.x) + (normal.y * normal.y));
}
*/
//normal.z = sqrt(1.0 - dot(normal.xy, normal.xy));
//normal.xy *= 1.0 + normal.z;
normal.z = 0.5 - 0.5 * ((normal.x * normal.x) + (normal.y * normal.y));
normal = normalize(normal);
/*
normal.z = 0.5;
normal = normalize(normal);
*/
if (!z_flip) {
normal.z = -normal.z;
}
//normal = normalize(vec3( uv_interp * 2.0 - 1.0, 1.0 ));
float depth = textureCube(source_cube, normal).r;
// absolute values for direction cosines, bigger value equals closer to basis axis
vec3 unorm = abs(normal);
if ((unorm.x >= unorm.y) && (unorm.x >= unorm.z)) {
// x code
unorm = normal.x > 0.0 ? vec3(1.0, 0.0, 0.0) : vec3(-1.0, 0.0, 0.0);
} else if ((unorm.y > unorm.x) && (unorm.y >= unorm.z)) {
// y code
unorm = normal.y > 0.0 ? vec3(0.0, 1.0, 0.0) : vec3(0.0, -1.0, 0.0);
} else if ((unorm.z > unorm.x) && (unorm.z > unorm.y)) {
// z code
unorm = normal.z > 0.0 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 0.0, -1.0);
} else {
// oh-no we messed up code
// has to be
unorm = vec3(1.0, 0.0, 0.0);
}
float depth_fix = 1.0 / dot(normal, unorm);
depth = 2.0 * depth - 1.0;
float linear_depth = 2.0 * z_near * z_far / (z_far + z_near + depth * (z_far - z_near));
gl_FragDepth = (z_far - (linear_depth * depth_fix + bias)) / z_far;
}

View File

@@ -0,0 +1,291 @@
/* clang-format off */
[vertex]
#ifdef USE_GLES_OVER_GL
#define lowp
#define mediump
#define highp
#else
precision highp float;
precision highp int;
#endif
layout(location = 0) in vec2 vertex_attrib;
/* clang-format on */
layout(location = 4) in vec2 uv_in;
out vec2 uv_interp;
#ifdef USE_BLUR_SECTION
uniform vec4 blur_section;
#endif
void main() {
uv_interp = uv_in;
gl_Position = vec4(vertex_attrib, 0.0, 1.0);
#ifdef USE_BLUR_SECTION
uv_interp = blur_section.xy + uv_interp * blur_section.zw;
gl_Position.xy = (blur_section.xy + (gl_Position.xy * 0.5 + 0.5) * blur_section.zw) * 2.0 - 1.0;
#endif
}
/* clang-format off */
[fragment]
#ifdef USE_GLES_OVER_GL
#define lowp
#define mediump
#define highp
#else
#if defined(USE_HIGHP_PRECISION)
precision highp float;
precision highp int;
#else
precision mediump float;
precision mediump int;
#endif
#endif
in vec2 uv_interp;
/* clang-format on */
uniform sampler2D source_color; //texunit:0
uniform float lod;
uniform vec2 pixel_size;
#if defined(GLOW_GAUSSIAN_HORIZONTAL) || defined(GLOW_GAUSSIAN_VERTICAL)
uniform float glow_strength;
#endif
#if defined(DOF_FAR_BLUR) || defined(DOF_NEAR_BLUR)
#ifdef USE_GLES_OVER_GL
#ifdef DOF_QUALITY_LOW
const int dof_kernel_size = 5;
const int dof_kernel_from = 2;
const float dof_kernel[5] = float[](0.153388, 0.221461, 0.250301, 0.221461, 0.153388);
#endif
#ifdef DOF_QUALITY_MEDIUM
const int dof_kernel_size = 11;
const int dof_kernel_from = 5;
const float dof_kernel[11] = float[](0.055037, 0.072806, 0.090506, 0.105726, 0.116061, 0.119726, 0.116061, 0.105726, 0.090506, 0.072806, 0.055037);
#endif
#ifdef DOF_QUALITY_HIGH
const int dof_kernel_size = 21;
const int dof_kernel_from = 10;
const float dof_kernel[21] = float[](0.028174, 0.032676, 0.037311, 0.041944, 0.046421, 0.050582, 0.054261, 0.057307, 0.059587, 0.060998, 0.061476, 0.060998, 0.059587, 0.057307, 0.054261, 0.050582, 0.046421, 0.041944, 0.037311, 0.032676, 0.028174);
#endif
#endif
uniform sampler2D dof_source_depth; //texunit:1
uniform float dof_begin;
uniform float dof_end;
uniform vec2 dof_dir;
uniform float dof_radius;
#endif
#ifdef GLOW_FIRST_PASS
uniform highp float luminance_cap;
uniform float glow_bloom;
uniform float glow_hdr_threshold;
uniform float glow_hdr_scale;
#endif
uniform float camera_z_far;
uniform float camera_z_near;
layout(location = 0) out vec4 frag_color;
void main() {
#ifdef GLOW_GAUSSIAN_HORIZONTAL
vec2 pix_size = pixel_size;
pix_size *= 0.5; //reading from larger buffer, so use more samples
vec4 color = textureLod(source_color, uv_interp + vec2(0.0, 0.0) * pix_size, lod) * 0.174938;
color += textureLod(source_color, uv_interp + vec2(1.0, 0.0) * pix_size, lod) * 0.165569;
color += textureLod(source_color, uv_interp + vec2(2.0, 0.0) * pix_size, lod) * 0.140367;
color += textureLod(source_color, uv_interp + vec2(3.0, 0.0) * pix_size, lod) * 0.106595;
color += textureLod(source_color, uv_interp + vec2(-1.0, 0.0) * pix_size, lod) * 0.165569;
color += textureLod(source_color, uv_interp + vec2(-2.0, 0.0) * pix_size, lod) * 0.140367;
color += textureLod(source_color, uv_interp + vec2(-3.0, 0.0) * pix_size, lod) * 0.106595;
color *= glow_strength;
frag_color = color;
#endif
#ifdef GLOW_GAUSSIAN_VERTICAL
vec4 color = textureLod(source_color, uv_interp + vec2(0.0, 0.0) * pixel_size, lod) * 0.288713;
color += textureLod(source_color, uv_interp + vec2(0.0, 1.0) * pixel_size, lod) * 0.233062;
color += textureLod(source_color, uv_interp + vec2(0.0, 2.0) * pixel_size, lod) * 0.122581;
color += textureLod(source_color, uv_interp + vec2(0.0, -1.0) * pixel_size, lod) * 0.233062;
color += textureLod(source_color, uv_interp + vec2(0.0, -2.0) * pixel_size, lod) * 0.122581;
color *= glow_strength;
frag_color = color;
#endif
#ifndef USE_GLES_OVER_GL
#if defined(DOF_FAR_BLUR) || defined(DOF_NEAR_BLUR)
#ifdef DOF_QUALITY_LOW
const int dof_kernel_size = 5;
const int dof_kernel_from = 2;
float dof_kernel[5];
dof_kernel[0] = 0.153388;
dof_kernel[1] = 0.221461;
dof_kernel[2] = 0.250301;
dof_kernel[3] = 0.221461;
dof_kernel[4] = 0.153388;
#endif
#ifdef DOF_QUALITY_MEDIUM
const int dof_kernel_size = 11;
const int dof_kernel_from = 5;
float dof_kernel[11];
dof_kernel[0] = 0.055037;
dof_kernel[1] = 0.072806;
dof_kernel[2] = 0.090506;
dof_kernel[3] = 0.105726;
dof_kernel[4] = 0.116061;
dof_kernel[5] = 0.119726;
dof_kernel[6] = 0.116061;
dof_kernel[7] = 0.105726;
dof_kernel[8] = 0.090506;
dof_kernel[9] = 0.072806;
dof_kernel[10] = 0.055037;
#endif
#ifdef DOF_QUALITY_HIGH
const int dof_kernel_size = 21;
const int dof_kernel_from = 10;
float dof_kernel[21];
dof_kernel[0] = 0.028174;
dof_kernel[1] = 0.032676;
dof_kernel[2] = 0.037311;
dof_kernel[3] = 0.041944;
dof_kernel[4] = 0.046421;
dof_kernel[5] = 0.050582;
dof_kernel[6] = 0.054261;
dof_kernel[7] = 0.057307;
dof_kernel[8] = 0.059587;
dof_kernel[9] = 0.060998;
dof_kernel[10] = 0.061476;
dof_kernel[11] = 0.060998;
dof_kernel[12] = 0.059587;
dof_kernel[13] = 0.057307;
dof_kernel[14] = 0.054261;
dof_kernel[15] = 0.050582;
dof_kernel[16] = 0.046421;
dof_kernel[17] = 0.041944;
dof_kernel[18] = 0.037311;
dof_kernel[19] = 0.032676;
dof_kernel[20] = 0.028174;
#endif
#endif
#endif //!USE_GLES_OVER_GL
#ifdef DOF_FAR_BLUR
vec4 color_accum = vec4(0.0);
float depth = textureLod(dof_source_depth, uv_interp, 0.0).r;
depth = depth * 2.0 - 1.0;
#ifdef USE_ORTHOGONAL_PROJECTION
depth = ((depth + (camera_z_far + camera_z_near) / (camera_z_far - camera_z_near)) * (camera_z_far - camera_z_near)) / 2.0;
#else
depth = 2.0 * camera_z_near * camera_z_far / (camera_z_far + camera_z_near - depth * (camera_z_far - camera_z_near));
#endif
float amount = smoothstep(dof_begin, dof_end, depth);
float k_accum = 0.0;
for (int i = 0; i < dof_kernel_size; i++) {
int int_ofs = i - dof_kernel_from;
vec2 tap_uv = uv_interp + dof_dir * float(int_ofs) * amount * dof_radius;
float tap_k = dof_kernel[i];
float tap_depth = texture(dof_source_depth, tap_uv, 0.0).r;
tap_depth = tap_depth * 2.0 - 1.0;
#ifdef USE_ORTHOGONAL_PROJECTION
tap_depth = ((tap_depth + (camera_z_far + camera_z_near) / (camera_z_far - camera_z_near)) * (camera_z_far - camera_z_near)) / 2.0;
#else
tap_depth = 2.0 * camera_z_near * camera_z_far / (camera_z_far + camera_z_near - tap_depth * (camera_z_far - camera_z_near));
#endif
float tap_amount = int_ofs == 0 ? 1.0 : smoothstep(dof_begin, dof_end, tap_depth);
tap_amount *= tap_amount * tap_amount; //prevent undesired glow effect
vec4 tap_color = textureLod(source_color, tap_uv, 0.0) * tap_k;
k_accum += tap_k * tap_amount;
color_accum += tap_color * tap_amount;
}
if (k_accum > 0.0) {
color_accum /= k_accum;
}
frag_color = color_accum; ///k_accum;
#endif
#ifdef DOF_NEAR_BLUR
vec4 color_accum = vec4(0.0);
float max_accum = 0.0;
for (int i = 0; i < dof_kernel_size; i++) {
int int_ofs = i - dof_kernel_from;
vec2 tap_uv = uv_interp + dof_dir * float(int_ofs) * dof_radius;
float ofs_influence = max(0.0, 1.0 - abs(float(int_ofs)) / float(dof_kernel_from));
float tap_k = dof_kernel[i];
vec4 tap_color = textureLod(source_color, tap_uv, 0.0);
float tap_depth = texture(dof_source_depth, tap_uv, 0.0).r;
tap_depth = tap_depth * 2.0 - 1.0;
#ifdef USE_ORTHOGONAL_PROJECTION
tap_depth = ((tap_depth + (camera_z_far + camera_z_near) / (camera_z_far - camera_z_near)) * (camera_z_far - camera_z_near)) / 2.0;
#else
tap_depth = 2.0 * camera_z_near * camera_z_far / (camera_z_far + camera_z_near - tap_depth * (camera_z_far - camera_z_near));
#endif
float tap_amount = 1.0 - smoothstep(dof_end, dof_begin, tap_depth);
tap_amount *= tap_amount * tap_amount; //prevent undesired glow effect
#ifdef DOF_NEAR_FIRST_TAP
tap_color.a = 1.0 - smoothstep(dof_end, dof_begin, tap_depth);
#endif
max_accum = max(max_accum, tap_amount * ofs_influence);
color_accum += tap_color * tap_k;
}
color_accum.a = max(color_accum.a, sqrt(max_accum));
frag_color = color_accum;
#endif
#ifdef GLOW_FIRST_PASS
float luminance = max(frag_color.r, max(frag_color.g, frag_color.b));
float feedback = max(smoothstep(glow_hdr_threshold, glow_hdr_threshold + glow_hdr_scale, luminance), glow_bloom);
frag_color = min(frag_color * feedback, vec4(luminance_cap));
#endif
}

View File

@@ -0,0 +1,18 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")
if "GLES3_GLSL" in env["BUILDERS"]:
# find all include files
gl_include_files = [str(f) for f in Glob("*_inc.glsl")]
# find all shader code(all glsl files excluding our include files)
glsl_files = [str(f) for f in Glob("*.glsl") if str(f) not in gl_include_files]
# make sure we recompile shaders if include files change
env.Depends([f + ".gen.h" for f in glsl_files], gl_include_files + ["#gles3_builders.py"])
# compile shaders
for glsl_file in glsl_files:
env.GLES3_GLSL(glsl_file)

View File

@@ -0,0 +1,234 @@
/* clang-format off */
#[modes]
mode_default = #define MODE_SIMPLE_COPY
mode_copy_section = #define USE_COPY_SECTION \n#define MODE_SIMPLE_COPY
mode_copy_section_source = #define USE_COPY_SECTION \n#define MODE_SIMPLE_COPY \n#define MODE_COPY_FROM
mode_copy_section_3d = #define USE_COPY_SECTION \n#define MODE_SIMPLE_COPY \n#define USE_TEXTURE_3D
mode_copy_section_2d_array = #define USE_COPY_SECTION \n#define MODE_SIMPLE_COPY \n#define USE_TEXTURE_2D_ARRAY
mode_lens_distortion = #define USE_COPY_SECTION \n#define MODE_SIMPLE_COPY \n#define USE_TEXTURE_2D_ARRAY \n#define APPLY_LENS_DISTORTION
mode_screen = #define MODE_SIMPLE_COPY \n#define MODE_MULTIPLY
mode_gaussian_blur = #define MODE_GAUSSIAN_BLUR
mode_mipmap = #define MODE_MIPMAP
mode_simple_color = #define MODE_SIMPLE_COLOR \n#define USE_COPY_SECTION
mode_cube_to_octahedral = #define CUBE_TO_OCTAHEDRAL \n#define USE_COPY_SECTION
mode_cube_to_panorama = #define CUBE_TO_PANORAMA
#[specializations]
CONVERT_LINEAR_TO_SRGB = false
#[vertex]
layout(location = 0) in vec2 vertex_attrib;
out vec2 uv_interp;
/* clang-format on */
#if defined(USE_COPY_SECTION) || defined(MODE_GAUSSIAN_BLUR)
// Defined in 0-1 coords.
uniform highp vec4 copy_section;
#endif
#if defined(MODE_GAUSSIAN_BLUR) || defined(MODE_COPY_FROM)
uniform highp vec4 source_section;
#endif
void main() {
uv_interp = vertex_attrib * 0.5 + 0.5;
gl_Position = vec4(vertex_attrib, 1.0, 1.0);
#if defined(USE_COPY_SECTION) || defined(MODE_GAUSSIAN_BLUR)
gl_Position.xy = (copy_section.xy + uv_interp.xy * copy_section.zw) * 2.0 - 1.0;
#endif
#if defined(MODE_GAUSSIAN_BLUR) || defined(MODE_COPY_FROM)
uv_interp = source_section.xy + uv_interp * source_section.zw;
#endif
}
/* clang-format off */
#[fragment]
in vec2 uv_interp;
/* clang-format on */
#if defined(USE_TEXTURE_3D) || defined(USE_TEXTURE_2D_ARRAY)
uniform float layer;
uniform float lod;
#endif
#ifdef MODE_SIMPLE_COLOR
uniform vec4 color_in;
#endif
#ifdef MODE_MULTIPLY
uniform float multiply;
#endif
#ifdef MODE_GAUSSIAN_BLUR
// Defined in 0-1 coords.
uniform highp vec2 pixel_size;
#endif
#ifdef CUBE_TO_OCTAHEDRAL
vec3 oct_to_vec3(vec2 e) {
vec3 v = vec3(e.xy, 1.0 - abs(e.x) - abs(e.y));
float t = max(-v.z, 0.0);
v.xy += t * -sign(v.xy);
return normalize(v);
}
#endif
#ifdef CUBE_TO_PANORAMA
uniform lowp float mip_level;
#endif
#if defined(CUBE_TO_OCTAHEDRAL) || defined(CUBE_TO_PANORAMA)
uniform samplerCube source_cube; // texunit:0
#else // ~(defined(CUBE_TO_OCTAHEDRAL) || defined(CUBE_TO_PANORAMA))
#if defined(USE_TEXTURE_3D)
uniform sampler3D source_3d; // texunit:0
#elif defined(USE_TEXTURE_2D_ARRAY)
uniform sampler2DArray source_2d_array; // texunit:0
#else
uniform sampler2D source; // texunit:0
#endif
#endif // !(defined(CUBE_TO_OCTAHEDRAL) || defined(CUBE_TO_PANORAMA))
#ifdef APPLY_LENS_DISTORTION
uniform vec2 eye_center;
uniform float k1;
uniform float k2;
uniform float upscale;
uniform float aspect_ratio;
#endif // APPLY_LENS_DISTORTION
layout(location = 0) out vec4 frag_color;
// This expects 0-1 range input, outside that range it behaves poorly.
vec3 srgb_to_linear(vec3 color) {
// Approximation from http://chilliant.blogspot.com/2012/08/srgb-approximations-for-hlsl.html
return color * (color * (color * 0.305306011 + 0.682171111) + 0.012522878);
}
// This expects 0-1 range input.
vec3 linear_to_srgb(vec3 color) {
// Approximation from http://chilliant.blogspot.com/2012/08/srgb-approximations-for-hlsl.html
return max(vec3(1.055) * pow(color, vec3(0.416666667)) - vec3(0.055), vec3(0.0));
}
void main() {
#ifdef MODE_SIMPLE_COPY
vec2 uv = uv_interp;
#ifdef APPLY_LENS_DISTORTION
uv = uv * 2.0 - 1.0;
vec2 offset = uv - eye_center;
// take aspect ratio into account
offset.y /= aspect_ratio;
// distort
vec2 offset_sq = offset * offset;
float radius_sq = offset_sq.x + offset_sq.y;
float radius_s4 = radius_sq * radius_sq;
float distortion_scale = 1.0 + (k1 * radius_sq) + (k2 * radius_s4);
offset *= distortion_scale;
// reapply aspect ratio
offset.y *= aspect_ratio;
// add our eye center back in
uv = offset + eye_center;
uv /= upscale;
// and check our color
if (uv.x < -1.0 || uv.y < -1.0 || uv.x > 1.0 || uv.y > 1.0) {
frag_color = vec4(0.0, 0.0, 0.0, 1.0);
} else {
uv = uv * 0.5 + 0.5;
#endif // APPLY_LENS_DISTORTION
#ifdef USE_TEXTURE_3D
vec4 color = textureLod(source_3d, vec3(uv, layer), lod);
#elif defined(USE_TEXTURE_2D_ARRAY)
vec4 color = textureLod(source_2d_array, vec3(uv, layer), lod);
#else
vec4 color = texture(source, uv);
#endif // USE_TEXTURE_3D
#ifdef CONVERT_LINEAR_TO_SRGB
// Reading from a *_SRGB texture source will have converted data to linear,
// but we should output in sRGB!
color.rgb = linear_to_srgb(color.rgb);
#endif
#ifdef MODE_MULTIPLY
color *= multiply;
#endif // MODE_MULTIPLY
frag_color = color;
#ifdef APPLY_LENS_DISTORTION
}
#endif // APPLY_LENS_DISTORTION
#endif // MODE_SIMPLE_COPY
#ifdef MODE_SIMPLE_COLOR
frag_color = color_in;
#endif
// Efficient box filter from Jimenez: http://www.iryoku.com/next-generation-post-processing-in-call-of-duty-advanced-warfare
// Approximates a Gaussian in a single pass.
#ifdef MODE_GAUSSIAN_BLUR
vec4 A = textureLod(source, uv_interp + pixel_size * vec2(-1.0, -1.0), 0.0);
vec4 B = textureLod(source, uv_interp + pixel_size * vec2(0.0, -1.0), 0.0);
vec4 C = textureLod(source, uv_interp + pixel_size * vec2(1.0, -1.0), 0.0);
vec4 D = textureLod(source, uv_interp + pixel_size * vec2(-0.5, -0.5), 0.0);
vec4 E = textureLod(source, uv_interp + pixel_size * vec2(0.5, -0.5), 0.0);
vec4 F = textureLod(source, uv_interp + pixel_size * vec2(-1.0, 0.0), 0.0);
vec4 G = textureLod(source, uv_interp, 0.0);
vec4 H = textureLod(source, uv_interp + pixel_size * vec2(1.0, 0.0), 0.0);
vec4 I = textureLod(source, uv_interp + pixel_size * vec2(-0.5, 0.5), 0.0);
vec4 J = textureLod(source, uv_interp + pixel_size * vec2(0.5, 0.5), 0.0);
vec4 K = textureLod(source, uv_interp + pixel_size * vec2(-1.0, 1.0), 0.0);
vec4 L = textureLod(source, uv_interp + pixel_size * vec2(0.0, 1.0), 0.0);
vec4 M = textureLod(source, uv_interp + pixel_size * vec2(1.0, 1.0), 0.0);
float weight = 0.5 / 4.0;
float lesser_weight = 0.125 / 4.0;
frag_color = (D + E + I + J) * weight;
frag_color += (A + B + G + F) * lesser_weight;
frag_color += (B + C + H + G) * lesser_weight;
frag_color += (F + G + L + K) * lesser_weight;
frag_color += (G + H + M + L) * lesser_weight;
#endif
#ifdef CUBE_TO_OCTAHEDRAL
// Treat the UV coordinates as 0-1 encoded octahedral coordinates.
vec3 dir = oct_to_vec3(uv_interp * 2.0 - 1.0);
frag_color = texture(source_cube, dir);
#endif
#ifdef CUBE_TO_PANORAMA
const float PI = 3.14159265359;
float phi = uv_interp.x * 2.0 * PI;
float theta = uv_interp.y * PI;
vec3 normal;
normal.x = sin(phi) * sin(theta) * -1.0;
normal.y = cos(theta);
normal.z = cos(phi) * sin(theta) * -1.0;
vec3 color = srgb_to_linear(textureLod(source_cube, normal, mip_level).rgb);
frag_color = vec4(color, 1.0);
#endif
}

View File

@@ -0,0 +1,122 @@
/* clang-format off */
#[modes]
mode_default =
mode_copy = #define MODE_DIRECT_WRITE
#[specializations]
#[vertex]
layout(location = 0) in highp vec2 vertex_attrib;
/* clang-format on */
out highp vec2 uv_interp;
void main() {
uv_interp = vertex_attrib;
gl_Position = vec4(uv_interp, 0.0, 1.0);
}
/* clang-format off */
#[fragment]
#define M_PI 3.14159265359
uniform samplerCube source_cube; //texunit:0
/* clang-format on */
uniform int face_id;
#ifndef MODE_DIRECT_WRITE
uniform uint sample_count;
uniform vec4 sample_directions_mip[MAX_SAMPLE_COUNT];
uniform float weight;
#endif
in highp vec2 uv_interp;
layout(location = 0) out vec4 frag_color;
#define M_PI 3.14159265359
// Don't include tonemap_inc.glsl because all we want is these functions, we don't want the uniforms
vec3 linear_to_srgb(vec3 color) {
return max(vec3(1.055) * pow(color, vec3(0.416666667)) - vec3(0.055), vec3(0.0));
}
vec3 srgb_to_linear(vec3 color) {
return color * (color * (color * 0.305306011 + 0.682171111) + 0.012522878);
}
vec3 texelCoordToVec(vec2 uv, int faceID) {
mat3 faceUvVectors[6];
// -x
faceUvVectors[1][0] = vec3(0.0, 0.0, 1.0); // u -> +z
faceUvVectors[1][1] = vec3(0.0, -1.0, 0.0); // v -> -y
faceUvVectors[1][2] = vec3(-1.0, 0.0, 0.0); // -x face
// +x
faceUvVectors[0][0] = vec3(0.0, 0.0, -1.0); // u -> -z
faceUvVectors[0][1] = vec3(0.0, -1.0, 0.0); // v -> -y
faceUvVectors[0][2] = vec3(1.0, 0.0, 0.0); // +x face
// -y
faceUvVectors[3][0] = vec3(1.0, 0.0, 0.0); // u -> +x
faceUvVectors[3][1] = vec3(0.0, 0.0, -1.0); // v -> -z
faceUvVectors[3][2] = vec3(0.0, -1.0, 0.0); // -y face
// +y
faceUvVectors[2][0] = vec3(1.0, 0.0, 0.0); // u -> +x
faceUvVectors[2][1] = vec3(0.0, 0.0, 1.0); // v -> +z
faceUvVectors[2][2] = vec3(0.0, 1.0, 0.0); // +y face
// -z
faceUvVectors[5][0] = vec3(-1.0, 0.0, 0.0); // u -> -x
faceUvVectors[5][1] = vec3(0.0, -1.0, 0.0); // v -> -y
faceUvVectors[5][2] = vec3(0.0, 0.0, -1.0); // -z face
// +z
faceUvVectors[4][0] = vec3(1.0, 0.0, 0.0); // u -> +x
faceUvVectors[4][1] = vec3(0.0, -1.0, 0.0); // v -> -y
faceUvVectors[4][2] = vec3(0.0, 0.0, 1.0); // +z face
// out = u * s_faceUv[0] + v * s_faceUv[1] + s_faceUv[2].
vec3 result = (faceUvVectors[faceID][0] * uv.x) + (faceUvVectors[faceID][1] * uv.y) + faceUvVectors[faceID][2];
return normalize(result);
}
void main() {
vec3 color = vec3(0.0);
vec2 uv = uv_interp;
vec3 N = texelCoordToVec(uv, face_id);
#ifdef MODE_DIRECT_WRITE
frag_color = vec4(textureLod(source_cube, N, 0.0).rgb, 1.0);
#else
vec4 sum = vec4(0.0);
vec3 UpVector = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
mat3 T;
T[0] = normalize(cross(UpVector, N));
T[1] = cross(N, T[0]);
T[2] = N;
for (uint sample_num = 0u; sample_num < sample_count; sample_num++) {
vec4 sample_direction_mip = sample_directions_mip[sample_num];
vec3 L = T * sample_direction_mip.xyz;
vec3 val = textureLod(source_cube, L, sample_direction_mip.w).rgb;
// Mix using linear
val = srgb_to_linear(val);
sum.rgb += val * sample_direction_mip.z;
}
sum /= weight;
sum.rgb = linear_to_srgb(sum.rgb);
frag_color = vec4(sum.rgb, 1.0);
#endif
}

View File

@@ -0,0 +1,113 @@
/* clang-format off */
#[modes]
// Based on Dual filtering glow as explained in Marius Bjørge presentation at Siggraph 2015 "Bandwidth-Efficient Rendering"
mode_filter = #define MODE_FILTER
mode_downsample = #define MODE_DOWNSAMPLE
mode_upsample = #define MODE_UPSAMPLE
#[specializations]
USE_MULTIVIEW = false
#[vertex]
layout(location = 0) in vec2 vertex_attrib;
/* clang-format on */
out vec2 uv_interp;
void main() {
uv_interp = vertex_attrib * 0.5 + 0.5;
gl_Position = vec4(vertex_attrib, 1.0, 1.0);
}
/* clang-format off */
#[fragment]
/* clang-format on */
#ifdef MODE_FILTER
#ifdef USE_MULTIVIEW
uniform sampler2DArray source_color; // texunit:0
#else
uniform sampler2D source_color; // texunit:0
#endif // USE_MULTIVIEW
uniform float view;
uniform vec2 pixel_size;
uniform float luminance_multiplier;
uniform float glow_bloom;
uniform float glow_hdr_threshold;
uniform float glow_hdr_scale;
uniform float glow_luminance_cap;
#endif // MODE_FILTER
#ifdef MODE_DOWNSAMPLE
uniform sampler2D source_color; // texunit:0
uniform vec2 pixel_size;
#endif // MODE_DOWNSAMPLE
#ifdef MODE_UPSAMPLE
uniform sampler2D source_color; // texunit:0
uniform vec2 pixel_size;
#endif // MODE_UPSAMPLE
in vec2 uv_interp;
layout(location = 0) out vec4 frag_color;
void main() {
#ifdef MODE_FILTER
// Note, we read from an image with double resolution, so we average those out
#ifdef USE_MULTIVIEW
vec2 half_pixel = pixel_size * 0.5;
vec3 uv = vec3(uv_interp, view);
vec3 color = textureLod(source_color, uv, 0.0).rgb * 4.0;
color += textureLod(source_color, uv - vec3(half_pixel, 0.0), 0.0).rgb;
color += textureLod(source_color, uv + vec3(half_pixel, 0.0), 0.0).rgb;
color += textureLod(source_color, uv - vec3(half_pixel.x, -half_pixel.y, 0.0), 0.0).rgb;
color += textureLod(source_color, uv + vec3(half_pixel.x, -half_pixel.y, 0.0), 0.0).rgb;
#else
vec2 half_pixel = pixel_size * 0.5;
vec2 uv = uv_interp;
vec3 color = textureLod(source_color, uv, 0.0).rgb * 4.0;
color += textureLod(source_color, uv - half_pixel, 0.0).rgb;
color += textureLod(source_color, uv + half_pixel, 0.0).rgb;
color += textureLod(source_color, uv - vec2(half_pixel.x, -half_pixel.y), 0.0).rgb;
color += textureLod(source_color, uv + vec2(half_pixel.x, -half_pixel.y), 0.0).rgb;
#endif // USE_MULTIVIEW
color /= luminance_multiplier * 8.0;
float feedback_factor = max(color.r, max(color.g, color.b));
float feedback = max(smoothstep(glow_hdr_threshold, glow_hdr_threshold + glow_hdr_scale, feedback_factor), glow_bloom);
color = min(color * feedback, vec3(glow_luminance_cap));
frag_color = vec4(luminance_multiplier * color, 1.0);
#endif // MODE_FILTER
#ifdef MODE_DOWNSAMPLE
vec2 half_pixel = pixel_size * 0.5;
vec4 color = textureLod(source_color, uv_interp, 0.0) * 4.0;
color += textureLod(source_color, uv_interp - half_pixel, 0.0);
color += textureLod(source_color, uv_interp + half_pixel, 0.0);
color += textureLod(source_color, uv_interp - vec2(half_pixel.x, -half_pixel.y), 0.0);
color += textureLod(source_color, uv_interp + vec2(half_pixel.x, -half_pixel.y), 0.0);
frag_color = color / 8.0;
#endif // MODE_DOWNSAMPLE
#ifdef MODE_UPSAMPLE
vec2 half_pixel = pixel_size * 0.5;
vec4 color = textureLod(source_color, uv_interp + vec2(-half_pixel.x * 2.0, 0.0), 0.0);
color += textureLod(source_color, uv_interp + vec2(-half_pixel.x, half_pixel.y), 0.0) * 2.0;
color += textureLod(source_color, uv_interp + vec2(0.0, half_pixel.y * 2.0), 0.0);
color += textureLod(source_color, uv_interp + vec2(half_pixel.x, half_pixel.y), 0.0) * 2.0;
color += textureLod(source_color, uv_interp + vec2(half_pixel.x * 2.0, 0.0), 0.0);
color += textureLod(source_color, uv_interp + vec2(half_pixel.x, -half_pixel.y), 0.0) * 2.0;
color += textureLod(source_color, uv_interp + vec2(0.0, -half_pixel.y * 2.0), 0.0);
color += textureLod(source_color, uv_interp + vec2(-half_pixel.x, -half_pixel.y), 0.0) * 2.0;
frag_color = color / 12.0;
#endif // MODE_UPSAMPLE
}

View File

@@ -0,0 +1,130 @@
/* clang-format off */
#[modes]
mode_default =
#[specializations]
USE_MULTIVIEW = false
USE_GLOW = false
USE_LUMINANCE_MULTIPLIER = false
USE_BCS = false
USE_COLOR_CORRECTION = false
USE_1D_LUT = false
#[vertex]
layout(location = 0) in vec2 vertex_attrib;
/* clang-format on */
out vec2 uv_interp;
void main() {
uv_interp = vertex_attrib * 0.5 + 0.5;
gl_Position = vec4(vertex_attrib, 1.0, 1.0);
}
/* clang-format off */
#[fragment]
/* clang-format on */
// If we reach this code, we always tonemap.
#define APPLY_TONEMAPPING
#include "../tonemap_inc.glsl"
#ifdef USE_MULTIVIEW
uniform sampler2DArray source_color; // texunit:0
#else
uniform sampler2D source_color; // texunit:0
#endif // USE_MULTIVIEW
uniform float view;
uniform float luminance_multiplier;
#ifdef USE_GLOW
uniform sampler2D glow_color; // texunit:1
uniform vec2 pixel_size;
uniform float glow_intensity;
vec4 get_glow_color(vec2 uv) {
vec2 half_pixel = pixel_size * 0.5;
vec4 color = textureLod(glow_color, uv + vec2(-half_pixel.x * 2.0, 0.0), 0.0);
color += textureLod(glow_color, uv + vec2(-half_pixel.x, half_pixel.y), 0.0) * 2.0;
color += textureLod(glow_color, uv + vec2(0.0, half_pixel.y * 2.0), 0.0);
color += textureLod(glow_color, uv + vec2(half_pixel.x, half_pixel.y), 0.0) * 2.0;
color += textureLod(glow_color, uv + vec2(half_pixel.x * 2.0, 0.0), 0.0);
color += textureLod(glow_color, uv + vec2(half_pixel.x, -half_pixel.y), 0.0) * 2.0;
color += textureLod(glow_color, uv + vec2(0.0, -half_pixel.y * 2.0), 0.0);
color += textureLod(glow_color, uv + vec2(-half_pixel.x, -half_pixel.y), 0.0) * 2.0;
return color / 12.0;
}
#endif // USE_GLOW
#ifdef USE_COLOR_CORRECTION
#ifdef USE_1D_LUT
uniform sampler2D source_color_correction; //texunit:2
vec3 apply_color_correction(vec3 color) {
color.r = texture(source_color_correction, vec2(color.r, 0.0f)).r;
color.g = texture(source_color_correction, vec2(color.g, 0.0f)).g;
color.b = texture(source_color_correction, vec2(color.b, 0.0f)).b;
return color;
}
#else
uniform sampler3D source_color_correction; //texunit:2
vec3 apply_color_correction(vec3 color) {
return textureLod(source_color_correction, color, 0.0).rgb;
}
#endif // USE_1D_LUT
#endif // USE_COLOR_CORRECTION
#ifdef USE_BCS
vec3 apply_bcs(vec3 color) {
color = mix(vec3(0.0), color, brightness);
color = mix(vec3(0.5), color, contrast);
color = mix(vec3(dot(vec3(1.0), color) * 0.33333), color, saturation);
return color;
}
#endif
in vec2 uv_interp;
layout(location = 0) out vec4 frag_color;
void main() {
#ifdef USE_MULTIVIEW
vec4 color = texture(source_color, vec3(uv_interp, view));
#else
vec4 color = texture(source_color, uv_interp);
#endif
#ifdef USE_GLOW
vec4 glow = get_glow_color(uv_interp) * glow_intensity;
// Just use softlight...
glow.rgb = clamp(glow.rgb, vec3(0.0f), vec3(1.0f));
color.rgb = max((color.rgb + glow.rgb) - (color.rgb * glow.rgb), vec3(0.0));
#endif // USE_GLOW
#ifdef USE_LUMINANCE_MULTIPLIER
color = color / luminance_multiplier;
#endif
color.rgb = srgb_to_linear(color.rgb);
color.rgb = apply_tonemapping(color.rgb, white);
color.rgb = linear_to_srgb(color.rgb);
#ifdef USE_BCS
color.rgb = apply_bcs(color.rgb);
#endif
#ifdef USE_COLOR_CORRECTION
color.rgb = apply_color_correction(color.rgb);
#endif
frag_color = color;
}

View File

@@ -0,0 +1,39 @@
/* clang-format off */
#[modes]
mode_default =
#[specializations]
USE_EXTERNAL_SAMPLER = false
#[vertex]
layout(location = 0) in vec2 vertex_attrib;
out vec2 uv_interp;
void main() {
uv_interp = vertex_attrib * 0.5 + 0.5;
gl_Position = vec4(vertex_attrib, 1.0, 1.0);
}
/* clang-format off */
#[fragment]
layout(location = 0) out vec4 frag_color;
in vec2 uv_interp;
/* clang-format on */
#ifdef USE_EXTERNAL_SAMPLER
uniform samplerExternalOES sourceFeed; // texunit:0
#else
uniform sampler2D sourceFeed; // texunit:0
#endif
void main() {
vec4 color = texture(sourceFeed, uv_interp);
frag_color = color;
}

View File

@@ -0,0 +1,86 @@
/* clang-format off */
[vertex]
#ifdef USE_GLES_OVER_GL
#define lowp
#define mediump
#define highp
#else
precision highp float;
precision highp int;
#endif
layout(location = 0) in highp vec2 vertex;
/* clang-format on */
uniform vec2 offset;
uniform vec2 scale;
out vec2 uv_interp;
void main() {
uv_interp = vertex.xy * 2.0 - 1.0;
vec2 v = vertex.xy * scale + offset;
gl_Position = vec4(v, 0.0, 1.0);
}
/* clang-format off */
[fragment]
#ifdef USE_GLES_OVER_GL
#define lowp
#define mediump
#define highp
#else
#if defined(USE_HIGHP_PRECISION)
precision highp float;
precision highp int;
#else
precision mediump float;
precision mediump int;
#endif
#endif
uniform sampler2D source; //texunit:0
/* clang-format on */
uniform vec2 eye_center;
uniform float k1;
uniform float k2;
uniform float upscale;
uniform float aspect_ratio;
in vec2 uv_interp;
layout(location = 0) out vec4 frag_color;
void main() {
vec2 coords = uv_interp;
vec2 offset = coords - eye_center;
// take aspect ratio into account
offset.y /= aspect_ratio;
// distort
vec2 offset_sq = offset * offset;
float radius_sq = offset_sq.x + offset_sq.y;
float radius_s4 = radius_sq * radius_sq;
float distortion_scale = 1.0 + (k1 * radius_sq) + (k2 * radius_s4);
offset *= distortion_scale;
// reapply aspect ratio
offset.y *= aspect_ratio;
// add our eye center back in
coords = offset + eye_center;
coords /= upscale;
// and check our color
if (coords.x < -1.0 || coords.y < -1.0 || coords.x > 1.0 || coords.y > 1.0) {
frag_color = vec4(0.0, 0.0, 0.0, 1.0);
} else {
coords = (coords + vec2(1.0)) / vec2(2.0);
frag_color = texture(source, coords);
}
}

View File

@@ -0,0 +1,512 @@
/* clang-format off */
#[modes]
mode_default =
#[specializations]
MODE_3D = false
USERDATA1_USED = false
USERDATA2_USED = false
USERDATA3_USED = false
USERDATA4_USED = false
USERDATA5_USED = false
USERDATA6_USED = false
#[vertex]
#define SDF_MAX_LENGTH 16384.0
layout(std140) uniform GlobalShaderUniformData { //ubo:1
vec4 global_shader_uniforms[MAX_GLOBAL_SHADER_UNIFORMS];
};
// This needs to be outside clang-format so the ubo comment is in the right place
#ifdef MATERIAL_UNIFORMS_USED
layout(std140) uniform MaterialUniforms{ //ubo:2
#MATERIAL_UNIFORMS
};
#endif
/* clang-format on */
#define MAX_ATTRACTORS 32
#define ATTRACTOR_TYPE_SPHERE uint(0)
#define ATTRACTOR_TYPE_BOX uint(1)
#define ATTRACTOR_TYPE_VECTOR_FIELD uint(2)
struct Attractor {
mat4 transform;
vec4 extents; // Extents or radius. w-channel is padding.
uint type;
float strength;
float attenuation;
float directionality;
};
#define MAX_COLLIDERS 32
#define COLLIDER_TYPE_SPHERE uint(0)
#define COLLIDER_TYPE_BOX uint(1)
#define COLLIDER_TYPE_SDF uint(2)
#define COLLIDER_TYPE_HEIGHT_FIELD uint(3)
#define COLLIDER_TYPE_2D_SDF uint(4)
struct Collider {
mat4 transform;
vec4 extents; // Extents or radius. w-channel is padding.
uint type;
float scale;
float pad0;
float pad1;
};
layout(std140) uniform FrameData { //ubo:0
bool emitting;
uint cycle;
float system_phase;
float prev_system_phase;
float explosiveness;
float randomness;
float time;
float delta;
float particle_size;
float amount_ratio;
float pad1;
float pad2;
uint random_seed;
uint attractor_count;
uint collider_count;
uint frame;
mat4 emission_transform;
vec3 emitter_velocity;
float interp_to_end;
Attractor attractors[MAX_ATTRACTORS];
Collider colliders[MAX_COLLIDERS];
};
#define PARTICLE_FLAG_ACTIVE uint(1)
#define PARTICLE_FLAG_STARTED uint(2)
#define PARTICLE_FLAG_TRAILED uint(4)
#define PARTICLE_FRAME_MASK uint(0xFFFF)
#define PARTICLE_FRAME_SHIFT uint(16)
// ParticleData
layout(location = 0) in highp vec4 color;
layout(location = 1) in highp vec4 velocity_flags;
layout(location = 2) in highp vec4 custom;
layout(location = 3) in highp vec4 xform_1;
layout(location = 4) in highp vec4 xform_2;
#ifdef MODE_3D
layout(location = 5) in highp vec4 xform_3;
#endif
#ifdef USERDATA1_USED
in highp vec4 userdata1;
#endif
#ifdef USERDATA2_USED
in highp vec4 userdata2;
#endif
#ifdef USERDATA3_USED
in highp vec4 userdata3;
#endif
#ifdef USERDATA4_USED
in highp vec4 userdata4;
#endif
#ifdef USERDATA5_USED
in highp vec4 userdata5;
#endif
#ifdef USERDATA6_USED
in highp vec4 userdata6;
#endif
out highp vec4 out_color; //tfb:
out highp vec4 out_velocity_flags; //tfb:
out highp vec4 out_custom; //tfb:
out highp vec4 out_xform_1; //tfb:
out highp vec4 out_xform_2; //tfb:
#ifdef MODE_3D
out highp vec4 out_xform_3; //tfb:MODE_3D
#endif
#ifdef USERDATA1_USED
out highp vec4 out_userdata1; //tfb:USERDATA1_USED
#endif
#ifdef USERDATA2_USED
out highp vec4 out_userdata2; //tfb:USERDATA2_USED
#endif
#ifdef USERDATA3_USED
out highp vec4 out_userdata3; //tfb:USERDATA3_USED
#endif
#ifdef USERDATA4_USED
out highp vec4 out_userdata4; //tfb:USERDATA4_USED
#endif
#ifdef USERDATA5_USED
out highp vec4 out_userdata5; //tfb:USERDATA5_USED
#endif
#ifdef USERDATA6_USED
out highp vec4 out_userdata6; //tfb:USERDATA6_USED
#endif
uniform sampler2D height_field_texture; //texunit:0
uniform float lifetime;
uniform bool clear;
uniform uint total_particles;
uniform bool use_fractional_delta;
uint hash(uint x) {
x = ((x >> uint(16)) ^ x) * uint(0x45d9f3b);
x = ((x >> uint(16)) ^ x) * uint(0x45d9f3b);
x = (x >> uint(16)) ^ x;
return x;
}
vec3 safe_normalize(vec3 direction) {
const float EPSILON = 0.001;
if (length(direction) < EPSILON) {
return vec3(0.0);
}
return normalize(direction);
}
// Needed whenever 2D sdf texture is read from as it is packed in RGBA8.
float vec4_to_float(vec4 p_vec) {
return dot(p_vec, vec4(1.0 / (255.0 * 255.0 * 255.0), 1.0 / (255.0 * 255.0), 1.0 / 255.0, 1.0)) * 2.0 - 1.0;
}
#GLOBALS
void main() {
bool apply_forces = true;
bool apply_velocity = true;
float local_delta = delta;
float mass = 1.0;
bool restart = false;
bool restart_position = false;
bool restart_rotation_scale = false;
bool restart_velocity = false;
bool restart_color = false;
bool restart_custom = false;
mat4 xform = mat4(1.0);
uint flags = 0u;
if (clear) {
out_color = vec4(1.0);
out_custom = vec4(0.0);
out_velocity_flags = vec4(0.0);
} else {
out_color = color;
out_velocity_flags = velocity_flags;
out_custom = custom;
xform[0] = xform_1;
xform[1] = xform_2;
#ifdef MODE_3D
xform[2] = xform_3;
#endif
xform = transpose(xform);
flags = floatBitsToUint(velocity_flags.w);
#ifdef USERDATA1_USED
out_userdata1 = userdata1;
#endif
#ifdef USERDATA2_USED
out_userdata2 = userdata2;
#endif
#ifdef USERDATA3_USED
out_userdata3 = userdata3;
#endif
#ifdef USERDATA4_USED
out_userdata4 = userdata4;
#endif
#ifdef USERDATA5_USED
out_userdata5 = userdata5;
#endif
#ifdef USERDATA6_USED
out_userdata6 = userdata6;
#endif
}
//clear started flag if set
flags &= ~PARTICLE_FLAG_STARTED;
bool collided = false;
vec3 collision_normal = vec3(0.0);
float collision_depth = 0.0;
vec3 attractor_force = vec3(0.0);
#if !defined(DISABLE_VELOCITY)
if (bool(flags & PARTICLE_FLAG_ACTIVE)) {
xform[3].xyz += out_velocity_flags.xyz * local_delta;
}
#endif
uint index = uint(gl_VertexID);
if (emitting) {
float restart_phase = float(index) / float(total_particles);
if (randomness > 0.0) {
uint seed = cycle;
if (restart_phase >= system_phase) {
seed -= uint(1);
}
seed *= uint(total_particles);
seed += index;
float random = float(hash(seed) % uint(65536)) / 65536.0;
restart_phase += randomness * random * 1.0 / float(total_particles);
}
restart_phase *= (1.0 - explosiveness);
if (system_phase > prev_system_phase) {
// restart_phase >= prev_system_phase is used so particles emit in the first frame they are processed
if (restart_phase >= prev_system_phase && restart_phase < system_phase) {
restart = true;
if (use_fractional_delta) {
local_delta = (system_phase - restart_phase) * lifetime;
}
}
} else if (delta > 0.0) {
if (restart_phase >= prev_system_phase) {
restart = true;
if (use_fractional_delta) {
local_delta = (1.0 - restart_phase + system_phase) * lifetime;
}
} else if (restart_phase < system_phase) {
restart = true;
if (use_fractional_delta) {
local_delta = (system_phase - restart_phase) * lifetime;
}
}
}
if (restart) {
flags = emitting ? (PARTICLE_FLAG_ACTIVE | PARTICLE_FLAG_STARTED | (cycle << PARTICLE_FRAME_SHIFT)) : 0u;
restart_position = true;
restart_rotation_scale = true;
restart_velocity = true;
restart_color = true;
restart_custom = true;
}
}
bool particle_active = bool(flags & PARTICLE_FLAG_ACTIVE);
uint particle_number = (flags >> PARTICLE_FRAME_SHIFT) * uint(total_particles) + index;
if (restart && particle_active) {
#CODE : START
}
if (particle_active) {
for (uint i = 0u; i < attractor_count; i++) {
vec3 dir;
float amount;
vec3 rel_vec = xform[3].xyz - attractors[i].transform[3].xyz;
vec3 local_pos = rel_vec * mat3(attractors[i].transform);
if (attractors[i].type == ATTRACTOR_TYPE_SPHERE) {
dir = safe_normalize(rel_vec);
float d = length(local_pos) / attractors[i].extents.x;
if (d > 1.0) {
continue;
}
amount = max(0.0, 1.0 - d);
} else if (attractors[i].type == ATTRACTOR_TYPE_BOX) {
dir = safe_normalize(rel_vec);
vec3 abs_pos = abs(local_pos / attractors[i].extents.xyz);
float d = max(abs_pos.x, max(abs_pos.y, abs_pos.z));
if (d > 1.0) {
continue;
}
amount = max(0.0, 1.0 - d);
} else if (attractors[i].type == ATTRACTOR_TYPE_VECTOR_FIELD) {
}
mediump float attractor_attenuation = attractors[i].attenuation;
amount = pow(amount, attractor_attenuation);
dir = safe_normalize(mix(dir, attractors[i].transform[2].xyz, attractors[i].directionality));
attractor_force -= mass * amount * dir * attractors[i].strength;
}
float particle_size = particle_size;
#ifdef USE_COLLISION_SCALE
particle_size *= dot(vec3(length(xform[0].xyz), length(xform[1].xyz), length(xform[2].xyz)), vec3(0.33333333333));
#endif
if (collider_count == 1u && colliders[0].type == COLLIDER_TYPE_2D_SDF) {
//2D collision
vec2 pos = xform[3].xy;
vec4 to_sdf_x = colliders[0].transform[0];
vec4 to_sdf_y = colliders[0].transform[1];
vec2 sdf_pos = vec2(dot(vec4(pos, 0, 1), to_sdf_x), dot(vec4(pos, 0, 1), to_sdf_y));
vec4 sdf_to_screen = vec4(colliders[0].extents.xyz, colliders[0].scale);
vec2 uv_pos = sdf_pos * sdf_to_screen.xy + sdf_to_screen.zw;
if (all(greaterThan(uv_pos, vec2(0.0))) && all(lessThan(uv_pos, vec2(1.0)))) {
vec2 pos2 = pos + vec2(0, particle_size);
vec2 sdf_pos2 = vec2(dot(vec4(pos2, 0, 1), to_sdf_x), dot(vec4(pos2, 0, 1), to_sdf_y));
float sdf_particle_size = distance(sdf_pos, sdf_pos2);
float d = vec4_to_float(texture(height_field_texture, uv_pos)) * SDF_MAX_LENGTH;
// Allowing for a small epsilon to allow particle just touching colliders to count as collided
const float EPSILON = 0.001;
d -= sdf_particle_size;
if (d < EPSILON) {
vec2 n = normalize(vec2(
vec4_to_float(texture(height_field_texture, uv_pos + vec2(EPSILON, 0.0))) - vec4_to_float(texture(height_field_texture, uv_pos - vec2(EPSILON, 0.0))),
vec4_to_float(texture(height_field_texture, uv_pos + vec2(0.0, EPSILON))) - vec4_to_float(texture(height_field_texture, uv_pos - vec2(0.0, EPSILON)))));
collided = true;
sdf_pos2 = sdf_pos + n * d;
pos2 = vec2(dot(vec4(sdf_pos2, 0, 1), colliders[0].transform[2]), dot(vec4(sdf_pos2, 0, 1), colliders[0].transform[3]));
n = pos - pos2;
collision_normal = normalize(vec3(n, 0.0));
collision_depth = length(n);
}
}
} else {
for (uint i = 0u; i < collider_count; i++) {
vec3 normal;
float depth;
bool col = false;
vec3 rel_vec = xform[3].xyz - colliders[i].transform[3].xyz;
vec3 local_pos = rel_vec * mat3(colliders[i].transform);
// Allowing for a small epsilon to allow particle just touching colliders to count as collided
const float EPSILON = 0.001;
if (colliders[i].type == COLLIDER_TYPE_SPHERE) {
float d = length(rel_vec) - (particle_size + colliders[i].extents.x);
if (d < EPSILON) {
col = true;
depth = -d;
normal = normalize(rel_vec);
}
} else if (colliders[i].type == COLLIDER_TYPE_BOX) {
vec3 abs_pos = abs(local_pos);
vec3 sgn_pos = sign(local_pos);
if (any(greaterThan(abs_pos, colliders[i].extents.xyz))) {
//point outside box
vec3 closest = min(abs_pos, colliders[i].extents.xyz);
vec3 rel = abs_pos - closest;
depth = length(rel) - particle_size;
if (depth < EPSILON) {
col = true;
normal = mat3(colliders[i].transform) * (normalize(rel) * sgn_pos);
depth = -depth;
}
} else {
//point inside box
vec3 axis_len = colliders[i].extents.xyz - abs_pos;
// there has to be a faster way to do this?
if (all(lessThan(axis_len.xx, axis_len.yz))) {
normal = vec3(1, 0, 0);
} else if (all(lessThan(axis_len.yy, axis_len.xz))) {
normal = vec3(0, 1, 0);
} else {
normal = vec3(0, 0, 1);
}
col = true;
depth = dot(normal * axis_len, vec3(1)) + particle_size;
normal = mat3(colliders[i].transform) * (normal * sgn_pos);
}
} else if (colliders[i].type == COLLIDER_TYPE_SDF) {
} else if (colliders[i].type == COLLIDER_TYPE_HEIGHT_FIELD) {
vec3 local_pos_bottom = local_pos;
local_pos_bottom.y -= particle_size;
if (any(greaterThan(abs(local_pos_bottom), colliders[i].extents.xyz))) {
continue;
}
const float DELTA = 1.0 / 8192.0;
vec3 uvw_pos = vec3(local_pos_bottom / colliders[i].extents.xyz) * 0.5 + 0.5;
float y = texture(height_field_texture, uvw_pos.xz).r;
if (y + EPSILON > uvw_pos.y) {
//inside heightfield
vec3 pos1 = (vec3(uvw_pos.x, y, uvw_pos.z) * 2.0 - 1.0) * colliders[i].extents.xyz;
vec3 pos2 = (vec3(uvw_pos.x + DELTA, texture(height_field_texture, uvw_pos.xz + vec2(DELTA, 0)).r, uvw_pos.z) * 2.0 - 1.0) * colliders[i].extents.xyz;
vec3 pos3 = (vec3(uvw_pos.x, texture(height_field_texture, uvw_pos.xz + vec2(0, DELTA)).r, uvw_pos.z + DELTA) * 2.0 - 1.0) * colliders[i].extents.xyz;
normal = normalize(cross(pos1 - pos2, pos1 - pos3));
float local_y = (vec3(local_pos / colliders[i].extents.xyz) * 0.5 + 0.5).y;
col = true;
depth = dot(normal, pos1) - dot(normal, local_pos_bottom);
}
}
if (col) {
if (!collided) {
collided = true;
collision_normal = normal;
collision_depth = depth;
} else {
vec3 c = collision_normal * collision_depth;
c += normal * max(0.0, depth - dot(normal, c));
collision_normal = normalize(c);
collision_depth = length(c);
}
}
}
}
}
if (particle_active) {
#CODE : PROCESS
}
flags &= ~PARTICLE_FLAG_ACTIVE;
if (particle_active) {
flags |= PARTICLE_FLAG_ACTIVE;
}
xform = transpose(xform);
out_xform_1 = xform[0];
out_xform_2 = xform[1];
#ifdef MODE_3D
out_xform_3 = xform[2];
#endif
out_velocity_flags.w = uintBitsToFloat(flags);
}
/* clang-format off */
#[fragment]
void main() {
}
/* clang-format on */

View File

@@ -0,0 +1,121 @@
/* clang-format off */
#[modes]
mode_default =
#[specializations]
MODE_3D = false
#[vertex]
#include "stdlib_inc.glsl"
// ParticleData
layout(location = 0) in highp vec4 color;
layout(location = 1) in highp vec4 velocity_flags;
layout(location = 2) in highp vec4 custom;
layout(location = 3) in highp vec4 xform_1;
layout(location = 4) in highp vec4 xform_2;
#ifdef MODE_3D
layout(location = 5) in highp vec4 xform_3;
#endif
/* clang-format on */
out highp vec4 out_xform_1; //tfb:
out highp vec4 out_xform_2; //tfb:
#ifdef MODE_3D
out highp vec4 out_xform_3; //tfb:MODE_3D
#endif
flat out highp uvec4 instance_color_custom_data; //tfb:
uniform lowp vec3 sort_direction;
uniform highp float frame_remainder;
uniform highp vec3 align_up;
uniform highp uint align_mode;
uniform highp mat4 inv_emission_transform;
#define TRANSFORM_ALIGN_DISABLED uint(0)
#define TRANSFORM_ALIGN_Z_BILLBOARD uint(1)
#define TRANSFORM_ALIGN_Y_TO_VELOCITY uint(2)
#define TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY uint(3)
#define PARTICLE_FLAG_ACTIVE uint(1)
#define FLT_MAX float(3.402823466e+38)
void main() {
// Set scale to zero and translate to -INF so particle will be invisible
// even for materials that ignore rotation/scale (i.e. billboards).
mat4 txform = mat4(vec4(0.0), vec4(0.0), vec4(0.0), vec4(-FLT_MAX, -FLT_MAX, -FLT_MAX, 0.0));
if (bool(floatBitsToUint(velocity_flags.w) & PARTICLE_FLAG_ACTIVE)) {
#ifdef MODE_3D
txform = transpose(mat4(xform_1, xform_2, xform_3, vec4(0.0, 0.0, 0.0, 1.0)));
#else
txform = transpose(mat4(xform_1, xform_2, vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0)));
#endif
if (align_mode == TRANSFORM_ALIGN_DISABLED) {
// nothing
} else if (align_mode == TRANSFORM_ALIGN_Z_BILLBOARD) {
mat3 local = mat3(normalize(cross(align_up, sort_direction)), align_up, sort_direction);
local = local * mat3(txform);
txform[0].xyz = local[0];
txform[1].xyz = local[1];
txform[2].xyz = local[2];
} else if (align_mode == TRANSFORM_ALIGN_Y_TO_VELOCITY) {
vec3 v = velocity_flags.xyz;
float s = (length(txform[0]) + length(txform[1]) + length(txform[2])) / 3.0;
if (length(v) > 0.0) {
txform[1].xyz = normalize(v);
} else {
txform[1].xyz = normalize(txform[1].xyz);
}
txform[0].xyz = normalize(cross(txform[1].xyz, txform[2].xyz));
txform[2].xyz = vec3(0.0, 0.0, 1.0) * s;
txform[0].xyz *= s;
txform[1].xyz *= s;
} else if (align_mode == TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY) {
vec3 sv = velocity_flags.xyz - sort_direction * dot(sort_direction, velocity_flags.xyz); //screen velocity
if (length(sv) == 0.0) {
sv = align_up;
}
sv = normalize(sv);
txform[0].xyz = normalize(cross(sv, sort_direction)) * length(txform[0]);
txform[1].xyz = sv * length(txform[1]);
txform[2].xyz = sort_direction * length(txform[2]);
}
txform[3].xyz += velocity_flags.xyz * frame_remainder;
#ifndef MODE_3D
// In global mode, bring 2D particles to local coordinates
// as they will be drawn with the node position as origin.
txform = inv_emission_transform * txform;
#endif
}
txform = transpose(txform);
instance_color_custom_data.x = packHalf2x16(color.xy);
instance_color_custom_data.y = packHalf2x16(color.zw);
instance_color_custom_data.z = packHalf2x16(custom.xy);
instance_color_custom_data.w = packHalf2x16(custom.zw);
out_xform_1 = txform[0];
out_xform_2 = txform[1];
#ifdef MODE_3D
out_xform_3 = txform[2];
#endif
}
/* clang-format off */
#[fragment]
void main() {
}
/* clang-format on */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,282 @@
/* clang-format off */
#[modes]
mode_base_pass =
mode_blend_pass = #define MODE_BLEND_PASS
#[specializations]
MODE_2D = true
USE_BLEND_SHAPES = false
USE_SKELETON = false
USE_NORMAL = false
USE_TANGENT = false
FINAL_PASS = false
USE_EIGHT_WEIGHTS = false
#[vertex]
#include "stdlib_inc.glsl"
#ifdef MODE_2D
#define VFORMAT vec2
#else
#define VFORMAT vec3
#endif
#ifdef FINAL_PASS
#define OFORMAT vec2
#else
#define OFORMAT uvec2
#endif
// These come from the source mesh and the output from previous passes.
layout(location = 0) in highp VFORMAT in_vertex;
#ifdef MODE_BLEND_PASS
#ifdef USE_NORMAL
layout(location = 1) in highp uvec2 in_normal;
#endif
#ifdef USE_TANGENT
layout(location = 2) in highp uvec2 in_tangent;
#endif
#else // MODE_BLEND_PASS
#ifdef USE_NORMAL
layout(location = 1) in highp vec2 in_normal;
#endif
#ifdef USE_TANGENT
layout(location = 2) in highp vec2 in_tangent;
#endif
#endif // MODE_BLEND_PASS
#ifdef USE_SKELETON
#ifdef USE_EIGHT_WEIGHTS
layout(location = 10) in highp uvec4 in_bone_attrib;
layout(location = 11) in highp uvec4 in_bone_attrib2;
layout(location = 12) in mediump vec4 in_weight_attrib;
layout(location = 13) in mediump vec4 in_weight_attrib2;
#else
layout(location = 10) in highp uvec4 in_bone_attrib;
layout(location = 11) in mediump vec4 in_weight_attrib;
#endif
uniform highp sampler2D skeleton_texture; // texunit:0
#endif
/* clang-format on */
#ifdef MODE_BLEND_PASS
layout(location = 3) in highp VFORMAT blend_vertex;
#ifdef USE_NORMAL
layout(location = 4) in highp vec2 blend_normal;
#endif
#ifdef USE_TANGENT
layout(location = 5) in highp vec2 blend_tangent;
#endif
#endif // MODE_BLEND_PASS
out highp VFORMAT out_vertex; //tfb:
#ifdef USE_NORMAL
flat out highp OFORMAT out_normal; //tfb:USE_NORMAL
#endif
#ifdef USE_TANGENT
flat out highp OFORMAT out_tangent; //tfb:USE_TANGENT
#endif
#ifdef USE_BLEND_SHAPES
uniform highp float blend_weight;
uniform lowp float blend_shape_count;
#endif
#ifdef USE_SKELETON
uniform mediump vec2 skeleton_transform_x;
uniform mediump vec2 skeleton_transform_y;
uniform mediump vec2 skeleton_transform_offset;
uniform mediump vec2 inverse_transform_x;
uniform mediump vec2 inverse_transform_y;
uniform mediump vec2 inverse_transform_offset;
#endif
vec2 signNotZero(vec2 v) {
return mix(vec2(-1.0), vec2(1.0), greaterThanEqual(v.xy, vec2(0.0)));
}
vec3 oct_to_vec3(vec2 oct) {
oct = oct * 2.0 - 1.0;
vec3 v = vec3(oct.xy, 1.0 - abs(oct.x) - abs(oct.y));
if (v.z < 0.0) {
v.xy = (1.0 - abs(v.yx)) * signNotZero(v.xy);
}
return normalize(v);
}
vec2 vec3_to_oct(vec3 e) {
e /= abs(e.x) + abs(e.y) + abs(e.z);
vec2 oct = e.z >= 0.0f ? e.xy : (vec2(1.0f) - abs(e.yx)) * signNotZero(e.xy);
return oct * 0.5f + 0.5f;
}
vec4 oct_to_tang(vec2 oct_sign_encoded) {
// Binormal sign encoded in y component
vec2 oct = vec2(oct_sign_encoded.x, abs(oct_sign_encoded.y) * 2.0 - 1.0);
return vec4(oct_to_vec3(oct), sign(oct_sign_encoded.y));
}
vec2 tang_to_oct(vec4 base) {
vec2 oct = vec3_to_oct(base.xyz);
// Encode binormal sign in y component
oct.y = oct.y * 0.5f + 0.5f;
oct.y = base.w >= 0.0f ? oct.y : 1.0 - oct.y;
return oct;
}
// Our original input for normals and tangents is 2 16-bit floats.
// Transform Feedback has to write out 32-bits per channel.
// Octahedral compression requires normalized vectors, but we need to store
// non-normalized vectors until the very end.
// Therefore, we will compress our normals into 16 bits using signed-normalized
// fixed point precision. This works well, because we know that each normal
// is no larger than |1| so we can normalize by dividing by the number of blend
// shapes.
uvec2 vec4_to_vec2(vec4 p_vec) {
return uvec2(packSnorm2x16(p_vec.xy), packSnorm2x16(p_vec.zw));
}
vec4 vec2_to_vec4(uvec2 p_vec) {
return vec4(unpackSnorm2x16(p_vec.x), unpackSnorm2x16(p_vec.y));
}
void main() {
#ifdef MODE_2D
out_vertex = in_vertex;
#ifdef USE_BLEND_SHAPES
#ifdef MODE_BLEND_PASS
out_vertex = in_vertex + blend_vertex * blend_weight;
#else
out_vertex = in_vertex * blend_weight;
#endif
#ifdef FINAL_PASS
out_vertex = normalize(out_vertex);
#endif
#endif // USE_BLEND_SHAPES
#ifdef USE_SKELETON
#define TEX(m) texelFetch(skeleton_texture, ivec2(m % 256u, m / 256u), 0)
#define GET_BONE_MATRIX(a, b, w) mat2x4(TEX(a), TEX(b)) * w
uvec4 bones = in_bone_attrib * uvec4(2u);
uvec4 bones_a = bones + uvec4(1u);
highp mat2x4 m = GET_BONE_MATRIX(bones.x, bones_a.x, in_weight_attrib.x);
m += GET_BONE_MATRIX(bones.y, bones_a.y, in_weight_attrib.y);
m += GET_BONE_MATRIX(bones.z, bones_a.z, in_weight_attrib.z);
m += GET_BONE_MATRIX(bones.w, bones_a.w, in_weight_attrib.w);
mat4 skeleton_matrix = mat4(vec4(skeleton_transform_x, 0.0, 0.0), vec4(skeleton_transform_y, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(skeleton_transform_offset, 0.0, 1.0));
mat4 inverse_matrix = mat4(vec4(inverse_transform_x, 0.0, 0.0), vec4(inverse_transform_y, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(inverse_transform_offset, 0.0, 1.0));
mat4 bone_matrix = mat4(m[0], m[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0));
bone_matrix = skeleton_matrix * transpose(bone_matrix) * inverse_matrix;
out_vertex = (bone_matrix * vec4(out_vertex, 0.0, 1.0)).xy;
#endif // USE_SKELETON
#else // MODE_2D
#ifdef USE_BLEND_SHAPES
#ifdef MODE_BLEND_PASS
out_vertex = in_vertex + blend_vertex * blend_weight;
#ifdef USE_NORMAL
vec3 normal = vec2_to_vec4(in_normal).xyz * blend_shape_count;
vec3 normal_blend = oct_to_vec3(blend_normal) * blend_weight;
#ifdef FINAL_PASS
out_normal = vec3_to_oct(normalize(normal + normal_blend));
#else
out_normal = vec4_to_vec2(vec4(normal + normal_blend, 0.0) / blend_shape_count);
#endif
#endif // USE_NORMAL
#ifdef USE_TANGENT
vec4 tangent = vec2_to_vec4(in_tangent) * blend_shape_count;
vec4 tangent_blend = oct_to_tang(blend_tangent) * blend_weight;
#ifdef FINAL_PASS
out_tangent = tang_to_oct(vec4(normalize(tangent.xyz + tangent_blend.xyz), tangent.w));
#else
out_tangent = vec4_to_vec2(vec4((tangent.xyz + tangent_blend.xyz) / blend_shape_count, tangent.w));
#endif
#endif // USE_TANGENT
#else // MODE_BLEND_PASS
out_vertex = in_vertex * blend_weight;
#ifdef USE_NORMAL
vec3 normal = oct_to_vec3(in_normal);
out_normal = vec4_to_vec2(vec4(normal * blend_weight / blend_shape_count, 0.0));
#endif
#ifdef USE_TANGENT
vec4 tangent = oct_to_tang(in_tangent);
out_tangent = vec4_to_vec2(vec4(tangent.rgb * blend_weight / blend_shape_count, tangent.w));
#endif
#endif // MODE_BLEND_PASS
#else // USE_BLEND_SHAPES
// Make attributes available to the skeleton shader if not written by blend shapes.
out_vertex = in_vertex;
#ifdef USE_NORMAL
out_normal = in_normal;
#endif
#ifdef USE_TANGENT
out_tangent = in_tangent;
#endif
#endif // USE_BLEND_SHAPES
#ifdef USE_SKELETON
#define TEX(m) texelFetch(skeleton_texture, ivec2(m % 256u, m / 256u), 0)
#define GET_BONE_MATRIX(a, b, c, w) mat4(TEX(a), TEX(b), TEX(c), vec4(0.0, 0.0, 0.0, 1.0)) * w
uvec4 bones = in_bone_attrib * uvec4(3);
uvec4 bones_a = bones + uvec4(1);
uvec4 bones_b = bones + uvec4(2);
highp mat4 m;
m = GET_BONE_MATRIX(bones.x, bones_a.x, bones_b.x, in_weight_attrib.x);
m += GET_BONE_MATRIX(bones.y, bones_a.y, bones_b.y, in_weight_attrib.y);
m += GET_BONE_MATRIX(bones.z, bones_a.z, bones_b.z, in_weight_attrib.z);
m += GET_BONE_MATRIX(bones.w, bones_a.w, bones_b.w, in_weight_attrib.w);
#ifdef USE_EIGHT_WEIGHTS
bones = in_bone_attrib2 * uvec4(3);
bones_a = bones + uvec4(1);
bones_b = bones + uvec4(2);
m += GET_BONE_MATRIX(bones.x, bones_a.x, bones_b.x, in_weight_attrib2.x);
m += GET_BONE_MATRIX(bones.y, bones_a.y, bones_b.y, in_weight_attrib2.y);
m += GET_BONE_MATRIX(bones.z, bones_a.z, bones_b.z, in_weight_attrib2.z);
m += GET_BONE_MATRIX(bones.w, bones_a.w, bones_b.w, in_weight_attrib2.w);
#endif
// Reverse order because its transposed.
out_vertex = (vec4(out_vertex, 1.0) * m).xyz;
#ifdef USE_NORMAL
vec3 vertex_normal = oct_to_vec3(out_normal);
out_normal = vec3_to_oct(normalize((vec4(vertex_normal, 0.0) * m).xyz));
#endif // USE_NORMAL
#ifdef USE_TANGENT
vec4 vertex_tangent = oct_to_tang(out_tangent);
out_tangent = tang_to_oct(vec4(normalize((vec4(vertex_tangent.xyz, 0.0) * m).xyz), vertex_tangent.w));
#endif // USE_TANGENT
#endif // USE_SKELETON
#endif // MODE_2D
}
/* clang-format off */
#[fragment]
void main() {
}
/* clang-format on */

View File

@@ -0,0 +1,273 @@
/* clang-format off */
#[modes]
mode_background =
mode_cubemap = #define USE_CUBEMAP_PASS
#[specializations]
USE_MULTIVIEW = false
USE_INVERTED_Y = true
APPLY_TONEMAPPING = true
USE_QUARTER_RES_PASS = false
USE_HALF_RES_PASS = false
#[vertex]
layout(location = 0) in vec2 vertex_attrib;
out vec2 uv_interp;
/* clang-format on */
void main() {
#ifdef USE_INVERTED_Y
uv_interp = vertex_attrib;
#else
// We're doing clockwise culling so flip the order
uv_interp = vec2(vertex_attrib.x, vertex_attrib.y * -1.0);
#endif
gl_Position = vec4(uv_interp, -1.0, 1.0);
}
/* clang-format off */
#[fragment]
#define M_PI 3.14159265359
#include "tonemap_inc.glsl"
in vec2 uv_interp;
/* clang-format on */
uniform samplerCube radiance; //texunit:-1
#ifdef USE_CUBEMAP_PASS
uniform samplerCube half_res; //texunit:-2
uniform samplerCube quarter_res; //texunit:-3
#elif defined(USE_MULTIVIEW)
uniform sampler2DArray half_res; //texunit:-2
uniform sampler2DArray quarter_res; //texunit:-3
#else
uniform sampler2D half_res; //texunit:-2
uniform sampler2D quarter_res; //texunit:-3
#endif
layout(std140) uniform GlobalShaderUniformData { //ubo:1
vec4 global_shader_uniforms[MAX_GLOBAL_SHADER_UNIFORMS];
};
struct DirectionalLightData {
vec4 direction_energy;
vec4 color_size;
bool enabled;
};
layout(std140) uniform DirectionalLights { //ubo:4
DirectionalLightData data[MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS];
}
directional_lights;
/* clang-format off */
#ifdef MATERIAL_UNIFORMS_USED
layout(std140) uniform MaterialUniforms{ //ubo:3
#MATERIAL_UNIFORMS
};
#endif
/* clang-format on */
#GLOBALS
#ifdef USE_CUBEMAP_PASS
#define AT_CUBEMAP_PASS true
#else
#define AT_CUBEMAP_PASS false
#endif
#ifdef USE_HALF_RES_PASS
#define AT_HALF_RES_PASS true
#else
#define AT_HALF_RES_PASS false
#endif
#ifdef USE_QUARTER_RES_PASS
#define AT_QUARTER_RES_PASS true
#else
#define AT_QUARTER_RES_PASS false
#endif
// mat4 is a waste of space, but we don't have an easy way to set a mat3 uniform for now
uniform mat4 orientation;
uniform vec4 projection;
uniform vec3 position;
uniform float time;
uniform float sky_energy_multiplier;
uniform float luminance_multiplier;
uniform float fog_aerial_perspective;
uniform vec4 fog_light_color;
uniform float fog_sun_scatter;
uniform bool fog_enabled;
uniform float fog_density;
uniform float fog_sky_affect;
uniform uint directional_light_count;
#ifdef USE_MULTIVIEW
layout(std140) uniform MultiviewData { // ubo:11
highp mat4 projection_matrix_view[MAX_VIEWS];
highp mat4 inv_projection_matrix_view[MAX_VIEWS];
highp vec4 eye_offset[MAX_VIEWS];
}
multiview_data;
#endif
layout(location = 0) out vec4 frag_color;
#ifdef USE_DEBANDING
// https://www.iryoku.com/next-generation-post-processing-in-call-of-duty-advanced-warfare
vec3 interleaved_gradient_noise(vec2 pos) {
const vec3 magic = vec3(0.06711056f, 0.00583715f, 52.9829189f);
float res = fract(magic.z * fract(dot(pos, magic.xy))) * 2.0 - 1.0;
return vec3(res, -res, res) / 255.0;
}
#endif
#if !defined(DISABLE_FOG)
vec4 fog_process(vec3 view, vec3 sky_color) {
vec3 fog_color = mix(fog_light_color.rgb, sky_color, fog_aerial_perspective);
if (fog_sun_scatter > 0.001) {
vec4 sun_scatter = vec4(0.0);
float sun_total = 0.0;
for (uint i = 0u; i < directional_light_count; i++) {
vec3 light_color = srgb_to_linear(directional_lights.data[i].color_size.xyz) * directional_lights.data[i].direction_energy.w;
float light_amount = pow(max(dot(view, directional_lights.data[i].direction_energy.xyz), 0.0), 8.0) * M_PI;
fog_color += light_color * light_amount * fog_sun_scatter;
}
}
return vec4(fog_color, 1.0);
}
#endif // !DISABLE_FOG
// Eberly approximations from https://seblagarde.wordpress.com/2014/12/01/inverse-trigonometric-functions-gpu-optimization-for-amd-gcn-architecture/.
// input [-1, 1] and output [0, PI]
float acos_approx(float p_x) {
float x = abs(p_x);
float res = -0.156583f * x + (M_PI / 2.0);
res *= sqrt(1.0f - x);
return (p_x >= 0.0) ? res : M_PI - res;
}
// Based on https://math.stackexchange.com/questions/1098487/atan2-faster-approximation
// but using the Eberly coefficients from https://seblagarde.wordpress.com/2014/12/01/inverse-trigonometric-functions-gpu-optimization-for-amd-gcn-architecture/.
float atan2_approx(float y, float x) {
float a = min(abs(x), abs(y)) / max(abs(x), abs(y));
float s = a * a;
float poly = 0.0872929f;
poly = -0.301895f + poly * s;
poly = 1.0f + poly * s;
poly = poly * a;
float r = abs(y) > abs(x) ? (M_PI / 2.0) - poly : poly;
r = x < 0.0 ? M_PI - r : r;
r = y < 0.0 ? -r : r;
return r;
}
void main() {
vec3 cube_normal;
#ifdef USE_MULTIVIEW
// In multiview our projection matrices will contain positional and rotational offsets that we need to properly unproject.
vec4 unproject = vec4(uv_interp.xy, -1.0, 1.0); // unproject at the far plane
vec4 unprojected = multiview_data.inv_projection_matrix_view[ViewIndex] * unproject;
cube_normal = unprojected.xyz / unprojected.w;
// Unproject will give us the position between the eyes, need to re-offset.
cube_normal += multiview_data.eye_offset[ViewIndex].xyz;
#else
cube_normal.z = -1.0;
cube_normal.x = (uv_interp.x + projection.x) / projection.y;
cube_normal.y = (-uv_interp.y - projection.z) / projection.w;
#endif
cube_normal = mat3(orientation) * cube_normal;
cube_normal = normalize(cube_normal);
vec2 uv = gl_FragCoord.xy; // uv_interp * 0.5 + 0.5;
vec2 panorama_coords = vec2(atan2_approx(cube_normal.x, -cube_normal.z), acos_approx(cube_normal.y));
if (panorama_coords.x < 0.0) {
panorama_coords.x += M_PI * 2.0;
}
panorama_coords /= vec2(M_PI * 2.0, M_PI);
vec3 color = vec3(0.0, 0.0, 0.0);
float alpha = 1.0; // Only available to subpasses
vec4 half_res_color = vec4(1.0);
vec4 quarter_res_color = vec4(1.0);
vec4 custom_fog = vec4(0.0);
#ifdef USE_CUBEMAP_PASS
#ifdef USES_HALF_RES_COLOR
half_res_color = texture(samplerCube(half_res, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), cube_normal);
#endif
#ifdef USES_QUARTER_RES_COLOR
quarter_res_color = texture(samplerCube(quarter_res, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), cube_normal);
#endif
#else
#ifdef USES_HALF_RES_COLOR
#ifdef USE_MULTIVIEW
half_res_color = textureLod(sampler2DArray(half_res, SAMPLER_LINEAR_CLAMP), vec3(uv, ViewIndex), 0.0);
#else
half_res_color = textureLod(sampler2D(half_res, SAMPLER_LINEAR_CLAMP), uv, 0.0);
#endif
#endif
#ifdef USES_QUARTER_RES_COLOR
#ifdef USE_MULTIVIEW
quarter_res_color = textureLod(sampler2DArray(quarter_res, SAMPLER_LINEAR_CLAMP), vec3(uv, ViewIndex), 0.0);
#else
quarter_res_color = textureLod(sampler2D(quarter_res, SAMPLER_LINEAR_CLAMP), uv, 0.0);
#endif
#endif
#endif
{
#CODE : SKY
}
color *= sky_energy_multiplier;
// Convert to Linear for tonemapping so color matches scene shader better
color = srgb_to_linear(color);
#if !defined(DISABLE_FOG) && !defined(USE_CUBEMAP_PASS)
// Draw "fixed" fog before volumetric fog to ensure volumetric fog can appear in front of the sky.
if (fog_enabled) {
vec4 fog = fog_process(cube_normal, color.rgb);
color.rgb = mix(color.rgb, fog.rgb, fog.a * fog_sky_affect);
}
if (custom_fog.a > 0.0) {
color.rgb = mix(color.rgb, custom_fog.rgb, custom_fog.a);
}
#endif // DISABLE_FOG
color *= exposure;
#ifdef APPLY_TONEMAPPING
color = apply_tonemapping(color, white);
#endif
color = linear_to_srgb(color);
frag_color.rgb = color * luminance_multiplier;
frag_color.a = alpha;
#ifdef USE_DEBANDING
frag_color.rgb += interleaved_gradient_noise(gl_FragCoord.xy) * sky_energy_multiplier * luminance_multiplier;
#endif
}

View File

@@ -0,0 +1,89 @@
// Compatibility renames. These are exposed with the "godot_" prefix
// to work around two distinct Adreno bugs:
// 1. Some Adreno devices expose ES310 functions in ES300 shaders.
// Internally, we must use the "godot_" prefix, but user shaders
// will be mapped automatically.
// 2. Adreno 3XX devices have poor implementations of the other packing
// functions, so we just use our own there to keep it simple.
#ifdef USE_HALF2FLOAT
// Floating point pack/unpack functions are part of the GLSL ES 300 specification used by web and mobile.
// It appears to be safe to expose these on mobile, but when running through ANGLE this appears to break.
uint float2half(uint f) {
uint e = f & uint(0x7f800000);
if (e <= uint(0x38000000)) {
return uint(0);
} else {
return ((f >> uint(16)) & uint(0x8000)) |
(((e - uint(0x38000000)) >> uint(13)) & uint(0x7c00)) |
((f >> uint(13)) & uint(0x03ff));
}
}
uint half2float(uint h) {
uint h_e = h & uint(0x7c00);
return ((h & uint(0x8000)) << uint(16)) | uint((h_e >> uint(10)) != uint(0)) * (((h_e + uint(0x1c000)) << uint(13)) | ((h & uint(0x03ff)) << uint(13)));
}
uint godot_packHalf2x16(vec2 v) {
return float2half(floatBitsToUint(v.x)) | float2half(floatBitsToUint(v.y)) << uint(16);
}
vec2 godot_unpackHalf2x16(uint v) {
return vec2(uintBitsToFloat(half2float(v & uint(0xffff))),
uintBitsToFloat(half2float(v >> uint(16))));
}
uint godot_packUnorm2x16(vec2 v) {
uvec2 uv = uvec2(round(clamp(v, vec2(0.0), vec2(1.0)) * 65535.0));
return uv.x | uv.y << uint(16);
}
vec2 godot_unpackUnorm2x16(uint p) {
return vec2(float(p & uint(0xffff)), float(p >> uint(16))) * 0.000015259021; // 1.0 / 65535.0 optimization
}
uint godot_packSnorm2x16(vec2 v) {
uvec2 uv = uvec2(round(clamp(v, vec2(-1.0), vec2(1.0)) * 32767.0) + 32767.0);
return uv.x | uv.y << uint(16);
}
vec2 godot_unpackSnorm2x16(uint p) {
vec2 v = vec2(float(p & uint(0xffff)), float(p >> uint(16)));
return clamp((v - 32767.0) * vec2(0.00003051851), vec2(-1.0), vec2(1.0));
}
#define packHalf2x16 godot_packHalf2x16
#define unpackHalf2x16 godot_unpackHalf2x16
#define packUnorm2x16 godot_packUnorm2x16
#define unpackUnorm2x16 godot_unpackUnorm2x16
#define packSnorm2x16 godot_packSnorm2x16
#define unpackSnorm2x16 godot_unpackSnorm2x16
#endif // USE_HALF2FLOAT
// Always expose these as they are ES310 functions and not available in ES300 or GLSL 330.
uint godot_packUnorm4x8(vec4 v) {
uvec4 uv = uvec4(round(clamp(v, vec4(0.0), vec4(1.0)) * 255.0));
return uv.x | (uv.y << uint(8)) | (uv.z << uint(16)) | (uv.w << uint(24));
}
vec4 godot_unpackUnorm4x8(uint p) {
return vec4(float(p & uint(0xff)), float((p >> uint(8)) & uint(0xff)), float((p >> uint(16)) & uint(0xff)), float(p >> uint(24))) * 0.00392156862; // 1.0 / 255.0
}
uint godot_packSnorm4x8(vec4 v) {
uvec4 uv = uvec4(round(clamp(v, vec4(-1.0), vec4(1.0)) * 127.0) + 127.0);
return uv.x | uv.y << uint(8) | uv.z << uint(16) | uv.w << uint(24);
}
vec4 godot_unpackSnorm4x8(uint p) {
vec4 v = vec4(float(p & uint(0xff)), float((p >> uint(8)) & uint(0xff)), float((p >> uint(16)) & uint(0xff)), float(p >> uint(24)));
return clamp((v - vec4(127.0)) * vec4(0.00787401574), vec4(-1.0), vec4(1.0));
}
#define packUnorm4x8 godot_packUnorm4x8
#define unpackUnorm4x8 godot_unpackUnorm4x8
#define packSnorm4x8 godot_packSnorm4x8
#define unpackSnorm4x8 godot_unpackSnorm4x8

View File

@@ -0,0 +1,182 @@
layout(std140) uniform TonemapData { //ubo:0
float exposure;
float white;
int tonemapper;
int pad;
int pad2;
float brightness;
float contrast;
float saturation;
};
// This expects 0-1 range input.
vec3 linear_to_srgb(vec3 color) {
//color = clamp(color, vec3(0.0), vec3(1.0));
//const vec3 a = vec3(0.055f);
//return mix((vec3(1.0f) + a) * pow(color.rgb, vec3(1.0f / 2.4f)) - a, 12.92f * color.rgb, lessThan(color.rgb, vec3(0.0031308f)));
// Approximation from http://chilliant.blogspot.com/2012/08/srgb-approximations-for-hlsl.html
return max(vec3(1.055) * pow(color, vec3(0.416666667)) - vec3(0.055), vec3(0.0));
}
// This expects 0-1 range input, outside that range it behaves poorly.
vec3 srgb_to_linear(vec3 color) {
// Approximation from http://chilliant.blogspot.com/2012/08/srgb-approximations-for-hlsl.html
return color * (color * (color * 0.305306011 + 0.682171111) + 0.012522878);
}
#ifdef APPLY_TONEMAPPING
// Based on Reinhard's extended formula, see equation 4 in https://doi.org/cjbgrt
vec3 tonemap_reinhard(vec3 color, float p_white) {
float white_squared = p_white * p_white;
vec3 white_squared_color = white_squared * color;
// Equivalent to color * (1 + color / white_squared) / (1 + color)
return (white_squared_color + color * color) / (white_squared_color + white_squared);
}
vec3 tonemap_filmic(vec3 color, float p_white) {
// exposure bias: input scale (color *= bias, white *= bias) to make the brightness consistent with other tonemappers
// also useful to scale the input to the range that the tonemapper is designed for (some require very high input values)
// has no effect on the curve's general shape or visual properties
const float exposure_bias = 2.0f;
const float A = 0.22f * exposure_bias * exposure_bias; // bias baked into constants for performance
const float B = 0.30f * exposure_bias;
const float C = 0.10f;
const float D = 0.20f;
const float E = 0.01f;
const float F = 0.30f;
vec3 color_tonemapped = ((color * (A * color + C * B) + D * E) / (color * (A * color + B) + D * F)) - E / F;
float p_white_tonemapped = ((p_white * (A * p_white + C * B) + D * E) / (p_white * (A * p_white + B) + D * F)) - E / F;
return color_tonemapped / p_white_tonemapped;
}
// Adapted from https://github.com/TheRealMJP/BakingLab/blob/master/BakingLab/ACES.hlsl
// (MIT License).
vec3 tonemap_aces(vec3 color, float p_white) {
const float exposure_bias = 1.8f;
const float A = 0.0245786f;
const float B = 0.000090537f;
const float C = 0.983729f;
const float D = 0.432951f;
const float E = 0.238081f;
// Exposure bias baked into transform to save shader instructions. Equivalent to `color *= exposure_bias`
const mat3 rgb_to_rrt = mat3(
vec3(0.59719f * exposure_bias, 0.35458f * exposure_bias, 0.04823f * exposure_bias),
vec3(0.07600f * exposure_bias, 0.90834f * exposure_bias, 0.01566f * exposure_bias),
vec3(0.02840f * exposure_bias, 0.13383f * exposure_bias, 0.83777f * exposure_bias));
const mat3 odt_to_rgb = mat3(
vec3(1.60475f, -0.53108f, -0.07367f),
vec3(-0.10208f, 1.10813f, -0.00605f),
vec3(-0.00327f, -0.07276f, 1.07602f));
color *= rgb_to_rrt;
vec3 color_tonemapped = (color * (color + A) - B) / (color * (C * color + D) + E);
color_tonemapped *= odt_to_rgb;
p_white *= exposure_bias;
float p_white_tonemapped = (p_white * (p_white + A) - B) / (p_white * (C * p_white + D) + E);
return color_tonemapped / p_white_tonemapped;
}
// Polynomial approximation of EaryChow's AgX sigmoid curve.
// x must be within the range [0.0, 1.0]
vec3 agx_contrast_approx(vec3 x) {
// Generated with Excel trendline
// Input data: Generated using python sigmoid with EaryChow's configuration and 57 steps
// Additional padding values were added to give correct intersections at 0.0 and 1.0
// 6th order, intercept of 0.0 to remove an operation and ensure intersection at 0.0
vec3 x2 = x * x;
vec3 x4 = x2 * x2;
return 0.021 * x + 4.0111 * x2 - 25.682 * x2 * x + 70.359 * x4 - 74.778 * x4 * x + 27.069 * x4 * x2;
}
// This is an approximation and simplification of EaryChow's AgX implementation that is used by Blender.
// This code is based off of the script that generates the AgX_Base_sRGB.cube LUT that Blender uses.
// Source: https://github.com/EaryChow/AgX_LUT_Gen/blob/main/AgXBasesRGB.py
vec3 tonemap_agx(vec3 color) {
// Combined linear sRGB to linear Rec 2020 and Blender AgX inset matrices:
const mat3 srgb_to_rec2020_agx_inset_matrix = mat3(
0.54490813676363087053, 0.14044005884001287035, 0.088827411851915368603,
0.37377945959812267119, 0.75410959864013760045, 0.17887712465043811023,
0.081384976686407536266, 0.10543358536857773485, 0.73224999956948382528);
// Combined inverse AgX outset matrix and linear Rec 2020 to linear sRGB matrices.
const mat3 agx_outset_rec2020_to_srgb_matrix = mat3(
1.9645509602733325934, -0.29932243390911083839, -0.16436833806080403409,
-0.85585845117807513559, 1.3264510741502356555, -0.23822464068860595117,
-0.10886710826831608324, -0.027084020983874825605, 1.402665347143271889);
// LOG2_MIN = -10.0
// LOG2_MAX = +6.5
// MIDDLE_GRAY = 0.18
const float min_ev = -12.4739311883324; // log2(pow(2, LOG2_MIN) * MIDDLE_GRAY)
const float max_ev = 4.02606881166759; // log2(pow(2, LOG2_MAX) * MIDDLE_GRAY)
// Large negative values in one channel and large positive values in other
// channels can result in a colour that appears darker and more saturated than
// desired after passing it through the inset matrix. For this reason, it is
// best to prevent negative input values.
// This is done before the Rec. 2020 transform to allow the Rec. 2020
// transform to be combined with the AgX inset matrix. This results in a loss
// of color information that could be correctly interpreted within the
// Rec. 2020 color space as positive RGB values, but it is less common for Godot
// to provide this function with negative sRGB values and therefore not worth
// the performance cost of an additional matrix multiplication.
// A value of 2e-10 intentionally introduces insignificant error to prevent
// log2(0.0) after the inset matrix is applied; color will be >= 1e-10 after
// the matrix transform.
color = max(color, 2e-10);
// Do AGX in rec2020 to match Blender and then apply inset matrix.
color = srgb_to_rec2020_agx_inset_matrix * color;
// Log2 space encoding.
// Must be clamped because agx_contrast_approx may not work
// well with values outside of the range [0.0, 1.0]
color = clamp(log2(color), min_ev, max_ev);
color = (color - min_ev) / (max_ev - min_ev);
// Apply sigmoid function approximation.
color = agx_contrast_approx(color);
// Convert back to linear before applying outset matrix.
color = pow(color, vec3(2.4));
// Apply outset to make the result more chroma-laden and then go back to linear sRGB.
color = agx_outset_rec2020_to_srgb_matrix * color;
// Blender's lusRGB.compensate_low_side is too complex for this shader, so
// simply return the color, even if it has negative components. These negative
// components may be useful for subsequent color adjustments.
return color;
}
#define TONEMAPPER_LINEAR 0
#define TONEMAPPER_REINHARD 1
#define TONEMAPPER_FILMIC 2
#define TONEMAPPER_ACES 3
#define TONEMAPPER_AGX 4
vec3 apply_tonemapping(vec3 color, float p_white) { // inputs are LINEAR
// Ensure color values passed to tonemappers are positive.
// They can be negative in the case of negative lights, which leads to undesired behavior.
if (tonemapper == TONEMAPPER_LINEAR) {
return color;
} else if (tonemapper == TONEMAPPER_REINHARD) {
return tonemap_reinhard(max(vec3(0.0f), color), p_white);
} else if (tonemapper == TONEMAPPER_FILMIC) {
return tonemap_filmic(max(vec3(0.0f), color), p_white);
} else if (tonemapper == TONEMAPPER_ACES) {
return tonemap_aces(max(vec3(0.0f), color), p_white);
} else { // TONEMAPPER_AGX
return tonemap_agx(color);
}
}
#endif // APPLY_TONEMAPPING

View File

@@ -0,0 +1,6 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")
env.add_source_files(env.drivers_sources, "*.cpp")

View File

@@ -0,0 +1,251 @@
/**************************************************************************/
/* config.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 "config.h"
#include "../rasterizer_gles3.h"
#ifdef WEB_ENABLED
#include <emscripten/html5_webgl.h>
#endif
using namespace GLES3;
#define _GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF
Config *Config::singleton = nullptr;
Config::Config() {
singleton = this;
#ifdef WEB_ENABLED
// Starting with Emscripten 3.1.51, glGetStringi(GL_EXTENSIONS, i) will only ever return
// a fixed list of extensions, regardless of what additional extensions are enabled. This
// isn't very useful for us in determining which extensions we can rely on here. So, instead
// we use emscripten_webgl_get_supported_extensions() to get all supported extensions, which
// is what Emscripten 3.1.50 and earlier do.
{
char *extension_array_string = emscripten_webgl_get_supported_extensions();
PackedStringArray extension_array = String((const char *)extension_array_string).split(" ");
extensions.reserve(extension_array.size() * 2);
for (const String &s : extension_array) {
extensions.insert(s);
extensions.insert("GL_" + s);
}
free(extension_array_string);
}
#else
{
GLint max_extensions = 0;
glGetIntegerv(GL_NUM_EXTENSIONS, &max_extensions);
for (int i = 0; i < max_extensions; i++) {
const GLubyte *s = glGetStringi(GL_EXTENSIONS, i);
if (!s) {
break;
}
extensions.insert((const char *)s);
}
}
#endif
bptc_supported = extensions.has("GL_ARB_texture_compression_bptc") || extensions.has("GL_EXT_texture_compression_bptc");
astc_hdr_supported = extensions.has("GL_KHR_texture_compression_astc_hdr");
astc_supported = astc_hdr_supported || extensions.has("GL_KHR_texture_compression_astc") || extensions.has("GL_OES_texture_compression_astc") || extensions.has("GL_KHR_texture_compression_astc_ldr") || extensions.has("WEBGL_compressed_texture_astc");
astc_layered_supported = extensions.has("GL_KHR_texture_compression_astc_sliced_3d");
if (RasterizerGLES3::is_gles_over_gl()) {
float_texture_supported = true;
float_texture_linear_supported = true;
etc2_supported = false;
s3tc_supported = true;
rgtc_supported = true; //RGTC - core since OpenGL version 3.0
srgb_framebuffer_supported = true;
} else {
float_texture_supported = extensions.has("GL_EXT_color_buffer_float");
float_texture_linear_supported = extensions.has("GL_OES_texture_float_linear");
etc2_supported = true;
#if defined(ANDROID_ENABLED) || defined(IOS_ENABLED)
// Some Android devices report support for S3TC but we don't expect that and don't export the textures.
// This could be fixed but so few devices support it that it doesn't seem useful (and makes bigger APKs).
// For good measure we do the same hack for iOS, just in case.
s3tc_supported = false;
#else
s3tc_supported = extensions.has("GL_EXT_texture_compression_dxt1") || extensions.has("GL_EXT_texture_compression_s3tc") || extensions.has("WEBGL_compressed_texture_s3tc");
#endif
rgtc_supported = extensions.has("GL_EXT_texture_compression_rgtc") || extensions.has("GL_ARB_texture_compression_rgtc");
srgb_framebuffer_supported = extensions.has("GL_EXT_sRGB_write_control");
}
glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &max_vertex_texture_image_units);
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &max_texture_image_units);
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
glGetIntegerv(GL_MAX_VIEWPORT_DIMS, max_viewport_size);
glGetInteger64v(GL_MAX_UNIFORM_BLOCK_SIZE, &max_uniform_buffer_size);
GLint max_vertex_output;
glGetIntegerv(GL_MAX_VERTEX_OUTPUT_COMPONENTS, &max_vertex_output);
GLint max_fragment_input;
glGetIntegerv(GL_MAX_FRAGMENT_INPUT_COMPONENTS, &max_fragment_input);
max_shader_varyings = (uint32_t)MIN(max_vertex_output, max_fragment_input) / 4;
// sanity clamp buffer size to 16K..1MB
max_uniform_buffer_size = CLAMP(max_uniform_buffer_size, 16384, 1048576);
support_anisotropic_filter = extensions.has("GL_EXT_texture_filter_anisotropic");
if (support_anisotropic_filter) {
glGetFloatv(_GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &anisotropic_level);
anisotropic_level = MIN(float(1 << int(GLOBAL_GET("rendering/textures/default_filters/anisotropic_filtering_level"))), anisotropic_level);
}
glGetIntegerv(GL_MAX_SAMPLES, &msaa_max_samples);
#ifdef WEB_ENABLED
msaa_supported = (msaa_max_samples > 0);
#else
msaa_supported = true;
#endif
#ifndef IOS_ENABLED
#ifdef WEB_ENABLED
msaa_multiview_supported = extensions.has("OCULUS_multiview");
rt_msaa_multiview_supported = msaa_multiview_supported;
#else
msaa_multiview_supported = extensions.has("GL_EXT_multiview_texture_multisample");
#endif
multiview_supported = extensions.has("OCULUS_multiview") || extensions.has("GL_OVR_multiview2") || extensions.has("GL_OVR_multiview");
#endif
#ifdef ANDROID_ENABLED
// These are GLES only
rt_msaa_supported = extensions.has("GL_EXT_multisampled_render_to_texture");
rt_msaa_multiview_supported = extensions.has("GL_OVR_multiview_multisampled_render_to_texture");
external_texture_supported = extensions.has("GL_OES_EGL_image_external_essl3");
if (multiview_supported) {
eglFramebufferTextureMultiviewOVR = (PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)eglGetProcAddress("glFramebufferTextureMultiviewOVR");
if (eglFramebufferTextureMultiviewOVR == nullptr) {
multiview_supported = false;
}
}
if (msaa_multiview_supported) {
eglTexStorage3DMultisample = (PFNGLTEXSTORAGE3DMULTISAMPLEPROC)eglGetProcAddress("glTexStorage3DMultisample");
if (eglTexStorage3DMultisample == nullptr) {
msaa_multiview_supported = false;
}
}
if (rt_msaa_supported) {
eglFramebufferTexture2DMultisampleEXT = (PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC)eglGetProcAddress("glFramebufferTexture2DMultisampleEXT");
if (eglFramebufferTexture2DMultisampleEXT == nullptr) {
rt_msaa_supported = false;
}
}
if (rt_msaa_multiview_supported) {
eglFramebufferTextureMultisampleMultiviewOVR = (PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC)eglGetProcAddress("glFramebufferTextureMultisampleMultiviewOVR");
if (eglFramebufferTextureMultisampleMultiviewOVR == nullptr) {
rt_msaa_multiview_supported = false;
}
}
if (external_texture_supported) {
eglEGLImageTargetTexture2DOES = (PFNEGLIMAGETARGETTEXTURE2DOESPROC)eglGetProcAddress("glEGLImageTargetTexture2DOES");
if (eglEGLImageTargetTexture2DOES == nullptr) {
external_texture_supported = false;
}
}
#endif
force_vertex_shading = GLOBAL_GET("rendering/shading/overrides/force_vertex_shading");
specular_occlusion = GLOBAL_GET("rendering/reflections/specular_occlusion/enabled");
use_nearest_mip_filter = GLOBAL_GET("rendering/textures/default_filters/use_nearest_mipmap_filter");
use_depth_prepass = bool(GLOBAL_GET("rendering/driver/depth_prepass/enable"));
if (use_depth_prepass) {
String vendors = GLOBAL_GET("rendering/driver/depth_prepass/disable_for_vendors");
Vector<String> vendor_match = vendors.split(",");
const String &renderer = String::utf8((const char *)glGetString(GL_RENDERER));
for (int i = 0; i < vendor_match.size(); i++) {
String v = vendor_match[i].strip_edges();
if (v == String()) {
continue;
}
if (renderer.containsn(v)) {
use_depth_prepass = false;
}
}
}
max_renderable_elements = GLOBAL_GET("rendering/limits/opengl/max_renderable_elements");
max_renderable_lights = GLOBAL_GET("rendering/limits/opengl/max_renderable_lights");
max_lights_per_object = GLOBAL_GET("rendering/limits/opengl/max_lights_per_object");
//Adreno 3xx Compatibility
const String rendering_device_name = String::utf8((const char *)glGetString(GL_RENDERER));
if (rendering_device_name.left(13) == "Adreno (TM) 3") {
disable_particles_workaround = true;
// ignore driver version 331+
const String gl_version = String::utf8((const char *)glGetString(GL_VERSION));
// Adreno 3xx examples (https://opengles.gpuinfo.org/listreports.php):
// ===========================================================================
// OpenGL ES 3.0 V@84.0 AU@ (CL@)
// OpenGL ES 3.0 V@127.0 AU@ (GIT@I96aee987eb)
// OpenGL ES 3.0 V@140.0 AU@ (GIT@Ifd751822f5)
// OpenGL ES 3.0 V@251.0 AU@08.00.00.312.030 (GIT@Ie4790512f3)
// OpenGL ES 3.0 V@269.0 AU@ (GIT@I109c45a694)
// OpenGL ES 3.0 V@331.0 (GIT@35e467f, Ice9844a736) (Date:04/15/19)
// OpenGL ES 3.0 V@415.0 (GIT@d39f783, I79de86aa2c, 1591296226) (Date:06/04/20)
// OpenGL ES 3.0 V@0502.0 (GIT@09fef447e8, I1fe547a144, 1661493934) (Date:08/25/22)
String driver_version = gl_version.get_slice("V@", 1).get_slicec(' ', 0);
if (driver_version.is_valid_float() && driver_version.to_float() >= 331.0) {
//TODO: also 'GPUParticles'?
//https://github.com/godotengine/godot/issues/92662#issuecomment-2161199477
//disable_particles_workaround = false;
}
} else if (rendering_device_name == "PowerVR Rogue GE8320") {
disable_transform_feedback_shader_cache = true;
}
if (OS::get_singleton()->get_current_rendering_driver_name() == "opengl3_angle") {
polyfill_half2float = false;
}
#ifdef WEB_ENABLED
polyfill_half2float = false;
#endif
}
Config::~Config() {
singleton = nullptr;
}
#endif // GLES3_ENABLED

View File

@@ -0,0 +1,124 @@
/**************************************************************************/
/* config.h */
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#ifdef GLES3_ENABLED
#include "core/config/project_settings.h"
#include "core/string/ustring.h"
#include "core/templates/hash_set.h"
#include "platform_gl.h"
#ifdef ANDROID_ENABLED
typedef void (*PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)(GLenum, GLenum, GLuint, GLint, GLint, GLsizei);
typedef void (*PFNGLTEXSTORAGE3DMULTISAMPLEPROC)(GLenum, GLsizei, GLenum, GLsizei, GLsizei, GLsizei, GLboolean);
typedef void (*PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC)(GLenum, GLenum, GLenum, GLuint, GLint, GLsizei);
typedef void (*PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC)(GLenum, GLenum, GLuint, GLint, GLsizei, GLint, GLsizei);
typedef void (*PFNEGLIMAGETARGETTEXTURE2DOESPROC)(GLenum, void *);
#endif
namespace GLES3 {
class Config {
private:
static Config *singleton;
public:
bool use_nearest_mip_filter = false;
bool use_depth_prepass = true;
GLint max_vertex_texture_image_units = 0;
GLint max_texture_image_units = 0;
GLint max_texture_size = 0;
GLint max_viewport_size[2] = { 0, 0 };
GLint64 max_uniform_buffer_size = 0;
uint32_t max_shader_varyings = 0;
int64_t max_renderable_elements = 0;
int64_t max_renderable_lights = 0;
int64_t max_lights_per_object = 0;
bool generate_wireframes = false;
HashSet<String> extensions;
bool float_texture_supported = false;
bool float_texture_linear_supported = false;
bool s3tc_supported = false;
bool rgtc_supported = false;
bool bptc_supported = false;
bool etc2_supported = false;
bool astc_supported = false;
bool astc_hdr_supported = false;
bool astc_layered_supported = false;
bool srgb_framebuffer_supported = false;
bool force_vertex_shading = false;
bool specular_occlusion = false;
bool support_anisotropic_filter = false;
float anisotropic_level = 0.0f;
GLint msaa_max_samples = 0;
bool msaa_supported = false;
bool msaa_multiview_supported = false;
bool rt_msaa_supported = false;
bool rt_msaa_multiview_supported = false;
bool multiview_supported = false;
bool external_texture_supported = false;
// Adreno 3XX compatibility.
bool disable_particles_workaround = false; // Set to 'true' to disable 'GPUParticles'.
// PowerVR GE 8320 workaround.
bool disable_transform_feedback_shader_cache = false;
// ANGLE shader workaround.
bool polyfill_half2float = true;
#ifdef ANDROID_ENABLED
PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC eglFramebufferTextureMultiviewOVR = nullptr;
PFNGLTEXSTORAGE3DMULTISAMPLEPROC eglTexStorage3DMultisample = nullptr;
PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC eglFramebufferTexture2DMultisampleEXT = nullptr;
PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC eglFramebufferTextureMultisampleMultiviewOVR = nullptr;
PFNEGLIMAGETARGETTEXTURE2DOESPROC eglEGLImageTargetTexture2DOES = nullptr;
#endif
static Config *get_singleton() { return singleton; }
Config();
~Config();
};
} // namespace GLES3
#endif // GLES3_ENABLED

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,910 @@
/**************************************************************************/
/* light_storage.h */
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#ifdef GLES3_ENABLED
#include "platform_gl.h"
#include "render_scene_buffers_gles3.h"
#include "core/templates/local_vector.h"
#include "core/templates/rid_owner.h"
#include "drivers/gles3/storage/texture_storage.h"
#include "servers/rendering/storage/light_storage.h"
#include "servers/rendering/storage/utilities.h"
namespace GLES3 {
/* LIGHT */
struct Light {
RS::LightType type;
float param[RS::LIGHT_PARAM_MAX];
Color color = Color(1, 1, 1, 1);
RID projector;
bool shadow = false;
bool negative = false;
bool reverse_cull = false;
RS::LightBakeMode bake_mode = RS::LIGHT_BAKE_DYNAMIC;
uint32_t max_sdfgi_cascade = 2;
uint32_t cull_mask = 0xFFFFFFFF;
uint32_t shadow_caster_mask = 0xFFFFFFFF;
bool distance_fade = false;
real_t distance_fade_begin = 40.0;
real_t distance_fade_shadow = 50.0;
real_t distance_fade_length = 10.0;
RS::LightOmniShadowMode omni_shadow_mode = RS::LIGHT_OMNI_SHADOW_DUAL_PARABOLOID;
RS::LightDirectionalShadowMode directional_shadow_mode = RS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL;
bool directional_blend_splits = false;
RS::LightDirectionalSkyMode directional_sky_mode = RS::LIGHT_DIRECTIONAL_SKY_MODE_LIGHT_AND_SKY;
uint64_t version = 0;
Dependency dependency;
};
/* Light instance */
struct LightInstance {
struct ShadowTransform {
Projection camera;
Transform3D transform;
float farplane;
float split;
float bias_scale;
float shadow_texel_size;
float range_begin;
Rect2 atlas_rect;
Vector2 uv_scale;
};
ShadowTransform shadow_transform[6];
RS::LightType light_type = RS::LIGHT_DIRECTIONAL;
AABB aabb;
RID self;
RID light;
Transform3D transform;
uint64_t shadow_pass = 0;
uint64_t last_scene_pass = 0;
uint64_t last_scene_shadow_pass = 0;
uint64_t last_pass = 0;
uint32_t cull_mask = 0;
uint32_t light_directional_index = 0;
Rect2 directional_rect;
HashSet<RID> shadow_atlases; // Shadow atlases where this light is registered.
int32_t gl_id = -1;
int32_t shadow_id = -1;
LightInstance() {}
};
/* REFLECTION PROBE */
struct ReflectionProbe {
RS::ReflectionProbeUpdateMode update_mode = RS::REFLECTION_PROBE_UPDATE_ONCE;
int resolution = 256;
float intensity = 1.0;
float blend_distance = 1.0;
RS::ReflectionProbeAmbientMode ambient_mode = RS::REFLECTION_PROBE_AMBIENT_ENVIRONMENT;
Color ambient_color;
float ambient_color_energy = 1.0;
float max_distance = 0;
Vector3 size = Vector3(20, 20, 20);
Vector3 origin_offset;
bool interior = false;
bool box_projection = false;
bool enable_shadows = false;
uint32_t cull_mask = (1 << 20) - 1;
uint32_t reflection_mask = (1 << 20) - 1;
float mesh_lod_threshold = 0.01;
float baked_exposure = 1.0;
Dependency dependency;
};
/* REFLECTION ATLAS */
struct ReflectionAtlas {
int count = 0;
int size = 0;
int mipmap_count = 1; // number of mips, including original
int mipmap_size[8];
GLuint depth = 0;
struct Reflection {
RID owner;
GLuint color = 0;
GLuint radiance = 0;
GLuint fbos[7];
};
Vector<Reflection> reflections;
Ref<RenderSceneBuffersGLES3> render_buffers; // Further render buffers used.
};
/* REFLECTION PROBE INSTANCE */
struct ReflectionProbeInstance {
RID probe;
int atlas_index = -1;
RID atlas;
bool dirty = true;
bool rendering = false;
int processing_layer = 0;
uint64_t last_pass = 0;
uint32_t cull_mask = 0;
Transform3D transform;
};
/* LIGHTMAP */
struct Lightmap {
RID light_texture;
RID shadow_texture;
bool uses_spherical_harmonics = false;
bool interior = false;
AABB bounds = AABB(Vector3(), Vector3(1, 1, 1));
float baked_exposure = 1.0;
Vector2i light_texture_size;
int32_t array_index = -1; //unassigned
RS::ShadowmaskMode shadowmask_mode = RS::SHADOWMASK_MODE_NONE;
PackedVector3Array points;
PackedColorArray point_sh;
PackedInt32Array tetrahedra;
PackedInt32Array bsp_tree;
struct BSP {
static const int32_t EMPTY_LEAF = INT32_MIN;
float plane[4];
int32_t over = EMPTY_LEAF, under = EMPTY_LEAF;
};
Dependency dependency;
};
struct LightmapInstance {
RID lightmap;
Transform3D transform;
};
class LightStorage : public RendererLightStorage {
public:
enum ShadowAtlastQuadrant : uint32_t {
QUADRANT_SHIFT = 27,
OMNI_LIGHT_FLAG = 1 << 26,
SHADOW_INDEX_MASK = OMNI_LIGHT_FLAG - 1,
SHADOW_INVALID = 0xFFFFFFFF
};
private:
static LightStorage *singleton;
/* LIGHT */
mutable RID_Owner<Light, true> light_owner;
/* Light instance */
mutable RID_Owner<LightInstance> light_instance_owner;
/* REFLECTION PROBE */
mutable RID_Owner<ReflectionProbe, true> reflection_probe_owner;
/* REFLECTION ATLAS */
mutable RID_Owner<ReflectionAtlas> reflection_atlas_owner;
/* REFLECTION PROBE INSTANCE */
mutable RID_Owner<ReflectionProbeInstance> reflection_probe_instance_owner;
/* LIGHTMAP */
float lightmap_probe_capture_update_speed = 4;
mutable RID_Owner<Lightmap, true> lightmap_owner;
/* LIGHTMAP INSTANCE */
mutable RID_Owner<LightmapInstance> lightmap_instance_owner;
/* SHADOW ATLAS */
// Note: The ShadowAtlas in the OpenGL is virtual. Each light gets assigned its
// own texture which is the same size as it would be if it were in a real atlas.
// This allows us to maintain the same behavior as the other renderers.
struct ShadowAtlas {
struct Quadrant {
uint32_t subdivision = 0;
struct Shadow {
RID owner;
bool owner_is_omni = false;
uint64_t version = 0;
uint64_t alloc_tick = 0;
Shadow() {}
};
Vector<Shadow> shadows;
LocalVector<GLuint> textures;
LocalVector<GLuint> fbos;
Quadrant() {}
} quadrants[4];
// Ordered from smallest (worst) shadow size to largest (best).
int size_order[4] = { 0, 1, 2, 3 };
uint32_t smallest_subdiv = 0;
int size = 0;
bool use_16_bits = true;
GLuint debug_texture = 0;
GLuint debug_fbo = 0;
HashMap<RID, uint32_t> shadow_owners;
};
uint64_t shadow_atlas_realloc_tolerance_msec = 500;
RID_Owner<ShadowAtlas> shadow_atlas_owner;
void _shadow_atlas_invalidate_shadow(ShadowAtlas::Quadrant::Shadow *p_shadow, RID p_atlas, ShadowAtlas *p_shadow_atlas, uint32_t p_quadrant, uint32_t p_shadow_idx);
bool _shadow_atlas_find_shadow(ShadowAtlas *shadow_atlas, int *p_in_quadrants, int p_quadrant_count, int p_current_subdiv, uint64_t p_tick, bool p_omni, int &r_quadrant, int &r_shadow);
/* DIRECTIONAL SHADOW */
struct DirectionalShadow {
GLuint depth = 0;
GLuint fbo = 0;
int light_count = 0;
int size = 0;
bool use_16_bits = true;
int current_light = 0;
} directional_shadow;
public:
static LightStorage *get_singleton();
LightStorage();
virtual ~LightStorage();
/* Light API */
Light *get_light(RID p_rid) { return light_owner.get_or_null(p_rid); }
bool owns_light(RID p_rid) { return light_owner.owns(p_rid); }
void _light_initialize(RID p_rid, RS::LightType p_type);
virtual RID directional_light_allocate() override;
virtual void directional_light_initialize(RID p_rid) override;
virtual RID omni_light_allocate() override;
virtual void omni_light_initialize(RID p_rid) override;
virtual RID spot_light_allocate() override;
virtual void spot_light_initialize(RID p_rid) override;
virtual void light_free(RID p_rid) override;
virtual void light_set_color(RID p_light, const Color &p_color) override;
virtual void light_set_param(RID p_light, RS::LightParam p_param, float p_value) override;
virtual void light_set_shadow(RID p_light, bool p_enabled) override;
virtual void light_set_projector(RID p_light, RID p_texture) override;
virtual void light_set_negative(RID p_light, bool p_enable) override;
virtual void light_set_cull_mask(RID p_light, uint32_t p_mask) override;
virtual void light_set_distance_fade(RID p_light, bool p_enabled, float p_begin, float p_shadow, float p_length) override;
virtual void light_set_reverse_cull_face_mode(RID p_light, bool p_enabled) override;
virtual void light_set_shadow_caster_mask(RID p_light, uint32_t p_caster_mask) override;
virtual uint32_t light_get_shadow_caster_mask(RID p_light) const override;
virtual void light_set_bake_mode(RID p_light, RS::LightBakeMode p_bake_mode) override;
virtual void light_set_max_sdfgi_cascade(RID p_light, uint32_t p_cascade) override {}
virtual void light_omni_set_shadow_mode(RID p_light, RS::LightOmniShadowMode p_mode) override;
virtual void light_directional_set_shadow_mode(RID p_light, RS::LightDirectionalShadowMode p_mode) override;
virtual void light_directional_set_blend_splits(RID p_light, bool p_enable) override;
virtual bool light_directional_get_blend_splits(RID p_light) const override;
virtual void light_directional_set_sky_mode(RID p_light, RS::LightDirectionalSkyMode p_mode) override;
virtual RS::LightDirectionalSkyMode light_directional_get_sky_mode(RID p_light) const override;
virtual RS::LightDirectionalShadowMode light_directional_get_shadow_mode(RID p_light) override;
virtual RS::LightOmniShadowMode light_omni_get_shadow_mode(RID p_light) override;
virtual RS::LightType light_get_type(RID p_light) const override {
const Light *light = light_owner.get_or_null(p_light);
ERR_FAIL_NULL_V(light, RS::LIGHT_DIRECTIONAL);
return light->type;
}
virtual AABB light_get_aabb(RID p_light) const override;
virtual float light_get_param(RID p_light, RS::LightParam p_param) override {
const Light *light = light_owner.get_or_null(p_light);
ERR_FAIL_NULL_V(light, 0);
return light->param[p_param];
}
_FORCE_INLINE_ RID light_get_projector(RID p_light) {
const Light *light = light_owner.get_or_null(p_light);
ERR_FAIL_NULL_V(light, RID());
return light->projector;
}
virtual Color light_get_color(RID p_light) override {
const Light *light = light_owner.get_or_null(p_light);
ERR_FAIL_NULL_V(light, Color());
return light->color;
}
_FORCE_INLINE_ bool light_is_distance_fade_enabled(RID p_light) {
const Light *light = light_owner.get_or_null(p_light);
return light->distance_fade;
}
_FORCE_INLINE_ float light_get_distance_fade_begin(RID p_light) {
const Light *light = light_owner.get_or_null(p_light);
return light->distance_fade_begin;
}
_FORCE_INLINE_ float light_get_distance_fade_shadow(RID p_light) {
const Light *light = light_owner.get_or_null(p_light);
return light->distance_fade_shadow;
}
_FORCE_INLINE_ float light_get_distance_fade_length(RID p_light) {
const Light *light = light_owner.get_or_null(p_light);
return light->distance_fade_length;
}
virtual bool light_has_shadow(RID p_light) const override {
const Light *light = light_owner.get_or_null(p_light);
ERR_FAIL_NULL_V(light, RS::LIGHT_DIRECTIONAL);
return light->shadow;
}
virtual bool light_has_projector(RID p_light) const override {
const Light *light = light_owner.get_or_null(p_light);
ERR_FAIL_NULL_V(light, RS::LIGHT_DIRECTIONAL);
return TextureStorage::get_singleton()->owns_texture(light->projector);
}
_FORCE_INLINE_ bool light_is_negative(RID p_light) const {
const Light *light = light_owner.get_or_null(p_light);
ERR_FAIL_NULL_V(light, RS::LIGHT_DIRECTIONAL);
return light->negative;
}
_FORCE_INLINE_ float light_get_transmittance_bias(RID p_light) const {
const Light *light = light_owner.get_or_null(p_light);
ERR_FAIL_NULL_V(light, 0.0);
return light->param[RS::LIGHT_PARAM_TRANSMITTANCE_BIAS];
}
virtual bool light_get_reverse_cull_face_mode(RID p_light) const override {
const Light *light = light_owner.get_or_null(p_light);
ERR_FAIL_NULL_V(light, false);
return light->reverse_cull;
}
virtual RS::LightBakeMode light_get_bake_mode(RID p_light) override;
virtual uint32_t light_get_max_sdfgi_cascade(RID p_light) override { return 0; }
virtual uint64_t light_get_version(RID p_light) const override;
virtual uint32_t light_get_cull_mask(RID p_light) const override;
/* LIGHT INSTANCE API */
LightInstance *get_light_instance(RID p_rid) { return light_instance_owner.get_or_null(p_rid); }
bool owns_light_instance(RID p_rid) { return light_instance_owner.owns(p_rid); }
virtual RID light_instance_create(RID p_light) override;
virtual void light_instance_free(RID p_light_instance) override;
virtual void light_instance_set_transform(RID p_light_instance, const Transform3D &p_transform) override;
virtual void light_instance_set_aabb(RID p_light_instance, const AABB &p_aabb) override;
virtual void light_instance_set_shadow_transform(RID p_light_instance, const Projection &p_projection, const Transform3D &p_transform, float p_far, float p_split, int p_pass, float p_shadow_texel_size, float p_bias_scale = 1.0, float p_range_begin = 0, const Vector2 &p_uv_scale = Vector2()) override;
virtual void light_instance_mark_visible(RID p_light_instance) override;
virtual bool light_instance_is_shadow_visible_at_position(RID p_light_instance, const Vector3 &p_position) const override {
const LightInstance *light_instance = light_instance_owner.get_or_null(p_light_instance);
ERR_FAIL_NULL_V(light_instance, false);
const Light *light = light_owner.get_or_null(light_instance->light);
ERR_FAIL_NULL_V(light, false);
if (!light->shadow) {
return false;
}
if (!light->distance_fade) {
return true;
}
real_t distance = p_position.distance_to(light_instance->transform.origin);
if (distance > light->distance_fade_shadow + light->distance_fade_length) {
return false;
}
return true;
}
_FORCE_INLINE_ RID light_instance_get_base_light(RID p_light_instance) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->light;
}
_FORCE_INLINE_ Transform3D light_instance_get_base_transform(RID p_light_instance) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->transform;
}
_FORCE_INLINE_ AABB light_instance_get_base_aabb(RID p_light_instance) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->aabb;
}
_FORCE_INLINE_ void light_instance_set_cull_mask(RID p_light_instance, uint32_t p_cull_mask) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
li->cull_mask = p_cull_mask;
}
_FORCE_INLINE_ uint32_t light_instance_get_cull_mask(RID p_light_instance) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->cull_mask;
}
_FORCE_INLINE_ GLuint light_instance_get_shadow_texture(RID p_light_instance, RID p_shadow_atlas) {
#ifdef DEBUG_ENABLED
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
ERR_FAIL_COND_V(!li->shadow_atlases.has(p_shadow_atlas), 0);
#endif
ShadowAtlas *shadow_atlas = shadow_atlas_owner.get_or_null(p_shadow_atlas);
ERR_FAIL_NULL_V(shadow_atlas, 0);
#ifdef DEBUG_ENABLED
ERR_FAIL_COND_V(!shadow_atlas->shadow_owners.has(p_light_instance), 0);
#endif
uint32_t key = shadow_atlas->shadow_owners[p_light_instance];
uint32_t quadrant = (key >> QUADRANT_SHIFT) & 0x3;
uint32_t shadow = key & SHADOW_INDEX_MASK;
ERR_FAIL_COND_V(shadow >= (uint32_t)shadow_atlas->quadrants[quadrant].shadows.size(), 0);
return shadow_atlas_get_quadrant_shadow_texture(p_shadow_atlas, quadrant, shadow);
}
_FORCE_INLINE_ bool light_instance_has_shadow_atlas(RID p_light_instance, RID p_shadow_atlas) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->shadow_atlases.has(p_shadow_atlas);
}
_FORCE_INLINE_ float light_instance_get_shadow_texel_size(RID p_light_instance, RID p_shadow_atlas) {
#ifdef DEBUG_ENABLED
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
ERR_FAIL_COND_V(!li->shadow_atlases.has(p_shadow_atlas), 0);
#endif
ShadowAtlas *shadow_atlas = shadow_atlas_owner.get_or_null(p_shadow_atlas);
ERR_FAIL_NULL_V(shadow_atlas, 0);
#ifdef DEBUG_ENABLED
ERR_FAIL_COND_V(!shadow_atlas->shadow_owners.has(p_light_instance), 0);
#endif
uint32_t key = shadow_atlas->shadow_owners[p_light_instance];
uint32_t quadrant = (key >> QUADRANT_SHIFT) & 0x3;
uint32_t quadrant_size = shadow_atlas->size >> 1;
uint32_t shadow_size = (quadrant_size / shadow_atlas->quadrants[quadrant].subdivision);
return float(1.0) / shadow_size;
}
_FORCE_INLINE_ Projection light_instance_get_shadow_camera(RID p_light_instance, int p_index) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->shadow_transform[p_index].camera;
}
_FORCE_INLINE_ Transform3D light_instance_get_shadow_transform(RID p_light_instance, int p_index) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->shadow_transform[p_index].transform;
}
_FORCE_INLINE_ float light_instance_get_shadow_bias_scale(RID p_light_instance, int p_index) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->shadow_transform[p_index].bias_scale;
}
_FORCE_INLINE_ float light_instance_get_shadow_range(RID p_light_instance, int p_index) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->shadow_transform[p_index].farplane;
}
_FORCE_INLINE_ float light_instance_get_shadow_range_begin(RID p_light_instance, int p_index) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->shadow_transform[p_index].range_begin;
}
_FORCE_INLINE_ Vector2 light_instance_get_shadow_uv_scale(RID p_light_instance, int p_index) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->shadow_transform[p_index].uv_scale;
}
_FORCE_INLINE_ void light_instance_set_directional_shadow_atlas_rect(RID p_light_instance, int p_index, const Rect2 p_atlas_rect) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
li->shadow_transform[p_index].atlas_rect = p_atlas_rect;
}
_FORCE_INLINE_ Rect2 light_instance_get_directional_shadow_atlas_rect(RID p_light_instance, int p_index) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->shadow_transform[p_index].atlas_rect;
}
_FORCE_INLINE_ float light_instance_get_directional_shadow_split(RID p_light_instance, int p_index) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->shadow_transform[p_index].split;
}
_FORCE_INLINE_ float light_instance_get_directional_shadow_texel_size(RID p_light_instance, int p_index) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->shadow_transform[p_index].shadow_texel_size;
}
_FORCE_INLINE_ void light_instance_set_render_pass(RID p_light_instance, uint64_t p_pass) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
li->last_pass = p_pass;
}
_FORCE_INLINE_ uint64_t light_instance_get_render_pass(RID p_light_instance) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->last_pass;
}
_FORCE_INLINE_ void light_instance_set_shadow_pass(RID p_light_instance, uint64_t p_pass) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
li->last_scene_shadow_pass = p_pass;
}
_FORCE_INLINE_ uint64_t light_instance_get_shadow_pass(RID p_light_instance) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->last_scene_shadow_pass;
}
_FORCE_INLINE_ void light_instance_set_directional_rect(RID p_light_instance, const Rect2 &p_directional_rect) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
li->directional_rect = p_directional_rect;
}
_FORCE_INLINE_ Rect2 light_instance_get_directional_rect(RID p_light_instance) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->directional_rect;
}
_FORCE_INLINE_ RS::LightType light_instance_get_type(RID p_light_instance) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->light_type;
}
_FORCE_INLINE_ int32_t light_instance_get_gl_id(RID p_light_instance) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->gl_id;
}
_FORCE_INLINE_ int32_t light_instance_get_shadow_id(RID p_light_instance) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->shadow_id;
}
/* PROBE API */
ReflectionProbe *get_reflection_probe(RID p_rid) { return reflection_probe_owner.get_or_null(p_rid); }
bool owns_reflection_probe(RID p_rid) { return reflection_probe_owner.owns(p_rid); }
virtual RID reflection_probe_allocate() override;
virtual void reflection_probe_initialize(RID p_rid) override;
virtual void reflection_probe_free(RID p_rid) override;
virtual void reflection_probe_set_update_mode(RID p_probe, RS::ReflectionProbeUpdateMode p_mode) override;
virtual void reflection_probe_set_intensity(RID p_probe, float p_intensity) override;
virtual void reflection_probe_set_blend_distance(RID p_probe, float p_blend_distance) override;
virtual void reflection_probe_set_ambient_mode(RID p_probe, RS::ReflectionProbeAmbientMode p_mode) override;
virtual void reflection_probe_set_ambient_color(RID p_probe, const Color &p_color) override;
virtual void reflection_probe_set_ambient_energy(RID p_probe, float p_energy) override;
virtual void reflection_probe_set_max_distance(RID p_probe, float p_distance) override;
virtual void reflection_probe_set_size(RID p_probe, const Vector3 &p_size) override;
virtual void reflection_probe_set_origin_offset(RID p_probe, const Vector3 &p_offset) override;
virtual void reflection_probe_set_as_interior(RID p_probe, bool p_enable) override;
virtual void reflection_probe_set_enable_box_projection(RID p_probe, bool p_enable) override;
virtual void reflection_probe_set_enable_shadows(RID p_probe, bool p_enable) override;
virtual void reflection_probe_set_cull_mask(RID p_probe, uint32_t p_layers) override;
virtual void reflection_probe_set_reflection_mask(RID p_probe, uint32_t p_layers) override;
virtual void reflection_probe_set_resolution(RID p_probe, int p_resolution) override;
virtual void reflection_probe_set_mesh_lod_threshold(RID p_probe, float p_ratio) override;
virtual float reflection_probe_get_mesh_lod_threshold(RID p_probe) const override;
virtual AABB reflection_probe_get_aabb(RID p_probe) const override;
virtual RS::ReflectionProbeUpdateMode reflection_probe_get_update_mode(RID p_probe) const override;
virtual uint32_t reflection_probe_get_cull_mask(RID p_probe) const override;
virtual uint32_t reflection_probe_get_reflection_mask(RID p_probe) const override;
virtual Vector3 reflection_probe_get_size(RID p_probe) const override;
virtual Vector3 reflection_probe_get_origin_offset(RID p_probe) const override;
virtual float reflection_probe_get_origin_max_distance(RID p_probe) const override;
virtual bool reflection_probe_renders_shadows(RID p_probe) const override;
Dependency *reflection_probe_get_dependency(RID p_probe) const;
/* REFLECTION ATLAS */
bool owns_reflection_atlas(RID p_rid) { return reflection_atlas_owner.owns(p_rid); }
virtual RID reflection_atlas_create() override;
virtual void reflection_atlas_free(RID p_ref_atlas) override;
virtual int reflection_atlas_get_size(RID p_ref_atlas) const override;
virtual void reflection_atlas_set_size(RID p_ref_atlas, int p_reflection_size, int p_reflection_count) override;
/* REFLECTION PROBE INSTANCE */
bool owns_reflection_probe_instance(RID p_rid) { return reflection_probe_instance_owner.owns(p_rid); }
virtual RID reflection_probe_instance_create(RID p_probe) override;
virtual void reflection_probe_instance_free(RID p_instance) override;
virtual void reflection_probe_instance_set_transform(RID p_instance, const Transform3D &p_transform) override;
virtual bool reflection_probe_has_atlas_index(RID p_instance) override;
virtual void reflection_probe_release_atlas_index(RID p_instance) override;
virtual bool reflection_probe_instance_needs_redraw(RID p_instance) override;
virtual bool reflection_probe_instance_has_reflection(RID p_instance) override;
virtual bool reflection_probe_instance_begin_render(RID p_instance, RID p_reflection_atlas) override;
virtual Ref<RenderSceneBuffers> reflection_probe_atlas_get_render_buffers(RID p_reflection_atlas) override;
virtual bool reflection_probe_instance_postprocess_step(RID p_instance) override;
_FORCE_INLINE_ RID reflection_probe_instance_get_probe(RID p_instance) {
ReflectionProbeInstance *rpi = reflection_probe_instance_owner.get_or_null(p_instance);
ERR_FAIL_NULL_V(rpi, RID());
return rpi->probe;
}
_FORCE_INLINE_ RID reflection_probe_instance_get_atlas(RID p_instance) {
ReflectionProbeInstance *rpi = reflection_probe_instance_owner.get_or_null(p_instance);
ERR_FAIL_NULL_V(rpi, RID());
return rpi->atlas;
}
Transform3D reflection_probe_instance_get_transform(RID p_instance) {
ReflectionProbeInstance *rpi = reflection_probe_instance_owner.get_or_null(p_instance);
ERR_FAIL_NULL_V(rpi, Transform3D());
return rpi->transform;
}
GLuint reflection_probe_instance_get_texture(RID p_instance);
GLuint reflection_probe_instance_get_framebuffer(RID p_instance, int p_index);
/* LIGHTMAP CAPTURE */
Lightmap *get_lightmap(RID p_rid) { return lightmap_owner.get_or_null(p_rid); }
bool owns_lightmap(RID p_rid) { return lightmap_owner.owns(p_rid); }
virtual RID lightmap_allocate() override;
virtual void lightmap_initialize(RID p_rid) override;
virtual void lightmap_free(RID p_rid) override;
virtual void lightmap_set_textures(RID p_lightmap, RID p_light, bool p_uses_spherical_haromics) override;
virtual void lightmap_set_probe_bounds(RID p_lightmap, const AABB &p_bounds) override;
virtual void lightmap_set_probe_interior(RID p_lightmap, bool p_interior) override;
virtual void lightmap_set_probe_capture_data(RID p_lightmap, const PackedVector3Array &p_points, const PackedColorArray &p_point_sh, const PackedInt32Array &p_tetrahedra, const PackedInt32Array &p_bsp_tree) override;
virtual void lightmap_set_baked_exposure_normalization(RID p_lightmap, float p_exposure) override;
virtual PackedVector3Array lightmap_get_probe_capture_points(RID p_lightmap) const override;
virtual PackedColorArray lightmap_get_probe_capture_sh(RID p_lightmap) const override;
virtual PackedInt32Array lightmap_get_probe_capture_tetrahedra(RID p_lightmap) const override;
virtual PackedInt32Array lightmap_get_probe_capture_bsp_tree(RID p_lightmap) const override;
virtual AABB lightmap_get_aabb(RID p_lightmap) const override;
virtual void lightmap_tap_sh_light(RID p_lightmap, const Vector3 &p_point, Color *r_sh) override;
virtual bool lightmap_is_interior(RID p_lightmap) const override;
virtual void lightmap_set_probe_capture_update_speed(float p_speed) override;
virtual float lightmap_get_probe_capture_update_speed() const override;
virtual void lightmap_set_shadowmask_textures(RID p_lightmap, RID p_shadow) override;
virtual RS::ShadowmaskMode lightmap_get_shadowmask_mode(RID p_lightmap) override;
virtual void lightmap_set_shadowmask_mode(RID p_lightmap, RS::ShadowmaskMode p_mode) override;
/* LIGHTMAP INSTANCE */
LightmapInstance *get_lightmap_instance(RID p_rid) { return lightmap_instance_owner.get_or_null(p_rid); }
bool owns_lightmap_instance(RID p_rid) { return lightmap_instance_owner.owns(p_rid); }
virtual RID lightmap_instance_create(RID p_lightmap) override;
virtual void lightmap_instance_free(RID p_lightmap) override;
virtual void lightmap_instance_set_transform(RID p_lightmap, const Transform3D &p_transform) override;
/* SHADOW ATLAS API */
bool owns_shadow_atlas(RID p_rid) { return shadow_atlas_owner.owns(p_rid); }
virtual RID shadow_atlas_create() override;
virtual void shadow_atlas_free(RID p_atlas) override;
virtual void shadow_atlas_set_size(RID p_atlas, int p_size, bool p_16_bits = true) override;
virtual void shadow_atlas_set_quadrant_subdivision(RID p_atlas, int p_quadrant, int p_subdivision) override;
virtual bool shadow_atlas_update_light(RID p_atlas, RID p_light_instance, float p_coverage, uint64_t p_light_version) override;
_FORCE_INLINE_ bool shadow_atlas_owns_light_instance(RID p_atlas, RID p_light_instance) {
ShadowAtlas *atlas = shadow_atlas_owner.get_or_null(p_atlas);
ERR_FAIL_NULL_V(atlas, false);
return atlas->shadow_owners.has(p_light_instance);
}
_FORCE_INLINE_ uint32_t shadow_atlas_get_light_instance_key(RID p_atlas, RID p_light_instance) {
ShadowAtlas *atlas = shadow_atlas_owner.get_or_null(p_atlas);
ERR_FAIL_NULL_V(atlas, -1);
return atlas->shadow_owners[p_light_instance];
}
_FORCE_INLINE_ int shadow_atlas_get_size(RID p_atlas) {
ShadowAtlas *atlas = shadow_atlas_owner.get_or_null(p_atlas);
ERR_FAIL_NULL_V(atlas, 0);
return atlas->size;
}
_FORCE_INLINE_ GLuint shadow_atlas_get_debug_fb(RID p_atlas) {
ShadowAtlas *atlas = shadow_atlas_owner.get_or_null(p_atlas);
ERR_FAIL_NULL_V(atlas, 0);
if (atlas->debug_fbo != 0) {
return atlas->debug_fbo;
}
glGenFramebuffers(1, &atlas->debug_fbo);
glBindFramebuffer(GL_FRAMEBUFFER, atlas->debug_fbo);
if (atlas->debug_texture == 0) {
atlas->debug_texture = shadow_atlas_get_debug_texture(p_atlas);
}
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, atlas->debug_texture);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, atlas->debug_texture, 0);
glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
return atlas->debug_fbo;
}
_FORCE_INLINE_ GLuint shadow_atlas_get_debug_texture(RID p_atlas) {
ShadowAtlas *atlas = shadow_atlas_owner.get_or_null(p_atlas);
ERR_FAIL_NULL_V(atlas, 0);
if (atlas->debug_texture != 0) {
return atlas->debug_texture;
}
glGenTextures(1, &atlas->debug_texture);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, atlas->debug_texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, atlas->size, atlas->size, 0, GL_RED, GL_UNSIGNED_INT, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_RED);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_RED);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_ONE);
glBindTexture(GL_TEXTURE_2D, 0);
return atlas->debug_texture;
}
_FORCE_INLINE_ int shadow_atlas_get_quadrant_shadows_length(RID p_atlas, uint32_t p_quadrant) {
ShadowAtlas *atlas = shadow_atlas_owner.get_or_null(p_atlas);
ERR_FAIL_NULL_V(atlas, 0);
ERR_FAIL_UNSIGNED_INDEX_V(p_quadrant, 4, 0);
return atlas->quadrants[p_quadrant].shadows.size();
}
_FORCE_INLINE_ uint32_t shadow_atlas_get_quadrant_shadows_allocated(RID p_atlas, uint32_t p_quadrant) {
ShadowAtlas *atlas = shadow_atlas_owner.get_or_null(p_atlas);
ERR_FAIL_NULL_V(atlas, 0);
ERR_FAIL_UNSIGNED_INDEX_V(p_quadrant, 4, 0);
return atlas->quadrants[p_quadrant].textures.size();
}
_FORCE_INLINE_ uint32_t shadow_atlas_get_quadrant_subdivision(RID p_atlas, uint32_t p_quadrant) {
ShadowAtlas *atlas = shadow_atlas_owner.get_or_null(p_atlas);
ERR_FAIL_NULL_V(atlas, 0);
ERR_FAIL_UNSIGNED_INDEX_V(p_quadrant, 4, 0);
return atlas->quadrants[p_quadrant].subdivision;
}
_FORCE_INLINE_ GLuint shadow_atlas_get_quadrant_shadow_texture(RID p_atlas, uint32_t p_quadrant, uint32_t p_shadow) {
ShadowAtlas *atlas = shadow_atlas_owner.get_or_null(p_atlas);
ERR_FAIL_NULL_V(atlas, 0);
ERR_FAIL_UNSIGNED_INDEX_V(p_quadrant, 4, 0);
ERR_FAIL_UNSIGNED_INDEX_V(p_shadow, atlas->quadrants[p_quadrant].textures.size(), 0);
return atlas->quadrants[p_quadrant].textures[p_shadow];
}
_FORCE_INLINE_ GLuint shadow_atlas_get_quadrant_shadow_fb(RID p_atlas, uint32_t p_quadrant, uint32_t p_shadow) {
ShadowAtlas *atlas = shadow_atlas_owner.get_or_null(p_atlas);
ERR_FAIL_NULL_V(atlas, 0);
ERR_FAIL_UNSIGNED_INDEX_V(p_quadrant, 4, 0);
ERR_FAIL_UNSIGNED_INDEX_V(p_shadow, atlas->quadrants[p_quadrant].fbos.size(), 0);
return atlas->quadrants[p_quadrant].fbos[p_shadow];
}
_FORCE_INLINE_ int shadow_atlas_get_quadrant_shadow_size(RID p_atlas, uint32_t p_quadrant) {
ShadowAtlas *atlas = shadow_atlas_owner.get_or_null(p_atlas);
ERR_FAIL_NULL_V(atlas, 0);
ERR_FAIL_UNSIGNED_INDEX_V(p_quadrant, 4, 0);
return (atlas->size >> 1) / atlas->quadrants[p_quadrant].subdivision;
}
_FORCE_INLINE_ bool shadow_atlas_get_quadrant_shadow_is_omni(RID p_atlas, uint32_t p_quadrant, uint32_t p_shadow) {
ShadowAtlas *atlas = shadow_atlas_owner.get_or_null(p_atlas);
ERR_FAIL_NULL_V(atlas, false);
ERR_FAIL_UNSIGNED_INDEX_V(p_quadrant, 4, false);
ERR_FAIL_UNSIGNED_INDEX_V(p_shadow, (uint32_t)atlas->quadrants[p_quadrant].shadows.size(), false);
return atlas->quadrants[p_quadrant].shadows[p_shadow].owner_is_omni;
}
virtual void shadow_atlas_update(RID p_atlas) override;
virtual void directional_shadow_atlas_set_size(int p_size, bool p_16_bits = true) override;
virtual int get_directional_light_shadow_size(RID p_light_instance) override;
virtual void set_directional_shadow_count(int p_count) override;
Rect2i get_directional_shadow_rect();
void update_directional_shadow_atlas();
_FORCE_INLINE_ GLuint directional_shadow_get_texture() {
return directional_shadow.depth;
}
_FORCE_INLINE_ int directional_shadow_get_size() {
return directional_shadow.size;
}
_FORCE_INLINE_ GLuint direction_shadow_get_fb() {
return directional_shadow.fbo;
}
_FORCE_INLINE_ void directional_shadow_increase_current_light() {
directional_shadow.current_light++;
}
};
} // namespace GLES3
#endif // GLES3_ENABLED

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,663 @@
/**************************************************************************/
/* material_storage.h */
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#ifdef GLES3_ENABLED
#include "core/templates/rid_owner.h"
#include "core/templates/self_list.h"
#include "servers/rendering/shader_compiler.h"
#include "servers/rendering/shader_language.h"
#include "servers/rendering/storage/material_storage.h"
#include "servers/rendering/storage/utilities.h"
#include "drivers/gles3/shaders/canvas.glsl.gen.h"
#include "drivers/gles3/shaders/particles.glsl.gen.h"
#include "drivers/gles3/shaders/scene.glsl.gen.h"
#include "drivers/gles3/shaders/sky.glsl.gen.h"
namespace GLES3 {
/* Shader Structs */
struct ShaderData {
String path;
HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> uniforms;
HashMap<StringName, HashMap<int, RID>> default_texture_params;
virtual void set_path_hint(const String &p_hint);
virtual void set_default_texture_parameter(const StringName &p_name, RID p_texture, int p_index);
virtual Variant get_default_parameter(const StringName &p_parameter) const;
virtual void get_shader_uniform_list(List<PropertyInfo> *p_param_list) const;
virtual void get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const;
virtual bool is_parameter_texture(const StringName &p_param) const;
virtual void set_code(const String &p_Code) = 0;
virtual bool is_animated() const = 0;
virtual bool casts_shadows() const = 0;
virtual RS::ShaderNativeSourceCode get_native_source_code() const { return RS::ShaderNativeSourceCode(); }
virtual ~ShaderData() {}
};
typedef ShaderData *(*ShaderDataRequestFunction)();
struct Material;
struct Shader {
ShaderData *data = nullptr;
String code;
String path_hint;
RS::ShaderMode mode;
HashMap<StringName, HashMap<int, RID>> default_texture_parameter;
HashSet<Material *> owners;
};
/* Material structs */
struct MaterialData {
void 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);
void 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_use_linear_color);
virtual void set_render_priority(int p_priority) = 0;
virtual void set_next_pass(RID p_pass) = 0;
virtual void update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) = 0;
virtual void bind_uniforms() = 0;
virtual ~MaterialData();
// Used internally by all Materials
void 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);
protected:
Vector<uint8_t> ubo_data;
GLuint uniform_buffer = GLuint(0);
Vector<RID> texture_cache;
private:
friend class MaterialStorage;
RID self;
List<RID>::Element *global_buffer_E = nullptr;
List<RID>::Element *global_texture_E = nullptr;
uint64_t global_textures_pass = 0;
HashMap<StringName, uint64_t> used_global_textures;
};
typedef MaterialData *(*MaterialDataRequestFunction)(ShaderData *);
struct Material {
RID self;
MaterialData *data = nullptr;
Shader *shader = nullptr;
//shortcut to shader data and type
RS::ShaderMode shader_mode = RS::SHADER_MAX;
uint32_t shader_id = 0;
bool uniform_dirty = false;
bool texture_dirty = false;
HashMap<StringName, Variant> params;
int32_t priority = 0;
RID next_pass;
SelfList<Material> update_element;
Dependency dependency;
Material() :
update_element(this) {}
};
/* CanvasItem Materials */
struct CanvasShaderData : public ShaderData {
enum BlendMode { // Used internally.
BLEND_MODE_MIX,
BLEND_MODE_ADD,
BLEND_MODE_SUB,
BLEND_MODE_MUL,
BLEND_MODE_PMALPHA,
BLEND_MODE_DISABLED,
BLEND_MODE_LCD,
};
// All these members are (re)initialized in `set_code`.
// Make sure to add the init to `set_code` whenever adding new members.
bool valid;
RID version;
Vector<ShaderCompiler::GeneratedCode::Texture> texture_uniforms;
Vector<uint32_t> ubo_offsets;
uint32_t ubo_size;
String code;
BlendMode blend_mode;
bool uses_screen_texture;
bool uses_screen_texture_mipmaps;
bool uses_sdf;
bool uses_time;
bool uses_custom0;
bool uses_custom1;
uint64_t vertex_input_mask;
virtual void set_code(const String &p_Code);
virtual bool is_animated() const;
virtual bool casts_shadows() const;
virtual RS::ShaderNativeSourceCode get_native_source_code() const;
CanvasShaderData();
virtual ~CanvasShaderData();
};
ShaderData *_create_canvas_shader_func();
struct CanvasMaterialData : public MaterialData {
CanvasShaderData *shader_data = nullptr;
virtual void set_render_priority(int p_priority) {}
virtual void set_next_pass(RID p_pass) {}
virtual void update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty);
virtual void bind_uniforms();
virtual ~CanvasMaterialData();
};
MaterialData *_create_canvas_material_func(ShaderData *p_shader);
/* Sky Materials */
struct SkyShaderData : public ShaderData {
// All these members are (re)initialized in `set_code`.
// Make sure to add the init to `set_code` whenever adding new members.
bool valid;
RID version;
Vector<ShaderCompiler::GeneratedCode::Texture> texture_uniforms;
Vector<uint32_t> ubo_offsets;
uint32_t ubo_size;
String code;
bool uses_time;
bool uses_position;
bool uses_half_res;
bool uses_quarter_res;
bool uses_light;
virtual void set_code(const String &p_Code);
virtual bool is_animated() const;
virtual bool casts_shadows() const;
virtual RS::ShaderNativeSourceCode get_native_source_code() const;
SkyShaderData();
virtual ~SkyShaderData();
};
ShaderData *_create_sky_shader_func();
struct SkyMaterialData : public MaterialData {
SkyShaderData *shader_data = nullptr;
bool uniform_set_updated = false;
virtual void set_render_priority(int p_priority) {}
virtual void set_next_pass(RID p_pass) {}
virtual void update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty);
virtual void bind_uniforms();
virtual ~SkyMaterialData();
};
MaterialData *_create_sky_material_func(ShaderData *p_shader);
/* Scene Materials */
struct SceneShaderData : public ShaderData {
enum BlendMode { // Used internally.
BLEND_MODE_MIX,
BLEND_MODE_ADD,
BLEND_MODE_SUB,
BLEND_MODE_MUL,
BLEND_MODE_PREMULT_ALPHA,
BLEND_MODE_ALPHA_TO_COVERAGE
};
enum DepthDraw {
DEPTH_DRAW_DISABLED,
DEPTH_DRAW_OPAQUE,
DEPTH_DRAW_ALWAYS
};
enum DepthTest {
DEPTH_TEST_DISABLED,
DEPTH_TEST_ENABLED,
DEPTH_TEST_ENABLED_INVERTED,
};
enum StencilCompare {
STENCIL_COMPARE_LESS,
STENCIL_COMPARE_EQUAL,
STENCIL_COMPARE_LESS_OR_EQUAL,
STENCIL_COMPARE_GREATER,
STENCIL_COMPARE_NOT_EQUAL,
STENCIL_COMPARE_GREATER_OR_EQUAL,
STENCIL_COMPARE_ALWAYS,
STENCIL_COMPARE_MAX // not an actual operator, just the amount of operators
};
enum StencilFlags {
STENCIL_FLAG_READ = 1,
STENCIL_FLAG_WRITE = 2,
STENCIL_FLAG_WRITE_DEPTH_FAIL = 4,
};
enum AlphaAntiAliasing {
ALPHA_ANTIALIASING_OFF,
ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE,
ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE_AND_TO_ONE
};
// All these members are (re)initialized in `set_code`.
// Make sure to add the init to `set_code` whenever adding new members.
bool valid;
RID version;
Vector<ShaderCompiler::GeneratedCode::Texture> texture_uniforms;
Vector<uint32_t> ubo_offsets;
uint32_t ubo_size;
String code;
BlendMode blend_mode;
AlphaAntiAliasing alpha_antialiasing_mode;
DepthDraw depth_draw;
DepthTest depth_test;
RS::CullMode cull_mode;
StencilCompare stencil_compare;
uint32_t stencil_flags;
int32_t stencil_reference;
bool stencil_enabled;
bool uses_point_size;
bool uses_alpha;
bool uses_alpha_clip;
bool uses_blend_alpha;
bool uses_depth_prepass_alpha;
bool uses_discard;
bool uses_roughness;
bool uses_normal;
bool uses_particle_trails;
bool wireframe;
bool unshaded;
bool uses_vertex;
bool uses_position;
bool uses_sss;
bool uses_transmittance;
bool uses_screen_texture;
bool uses_screen_texture_mipmaps;
bool uses_depth_texture;
bool uses_normal_texture;
bool uses_bent_normal_texture;
bool uses_time;
bool uses_vertex_time;
bool uses_fragment_time;
bool writes_modelview_or_projection;
bool uses_world_coordinates;
bool uses_tangent;
bool uses_color;
bool uses_uv;
bool uses_uv2;
bool uses_custom0;
bool uses_custom1;
bool uses_custom2;
bool uses_custom3;
bool uses_bones;
bool uses_weights;
uint64_t vertex_input_mask;
virtual void set_code(const String &p_Code);
virtual bool is_animated() const;
virtual bool casts_shadows() const;
virtual RS::ShaderNativeSourceCode get_native_source_code() const;
SceneShaderData();
virtual ~SceneShaderData();
};
ShaderData *_create_scene_shader_func();
struct SceneMaterialData : public MaterialData {
SceneShaderData *shader_data = nullptr;
uint64_t last_pass = 0;
uint32_t index = 0;
RID next_pass;
uint8_t priority = 0;
virtual void set_render_priority(int p_priority);
virtual void set_next_pass(RID p_pass);
virtual void update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty);
virtual void bind_uniforms();
virtual ~SceneMaterialData();
};
MaterialData *_create_scene_material_func(ShaderData *p_shader);
/* Particle Shader */
enum {
PARTICLES_MAX_USERDATAS = 6
};
struct ParticlesShaderData : public ShaderData {
// All these members are (re)initialized in `set_code`.
// Make sure to add the init to `set_code` whenever adding new members.
bool valid;
RID version;
Vector<ShaderCompiler::GeneratedCode::Texture> texture_uniforms;
Vector<uint32_t> ubo_offsets;
uint32_t ubo_size;
String code;
bool uses_collision;
bool uses_time;
bool userdatas_used[PARTICLES_MAX_USERDATAS] = {};
uint32_t userdata_count;
virtual void set_code(const String &p_Code);
virtual bool is_animated() const;
virtual bool casts_shadows() const;
virtual RS::ShaderNativeSourceCode get_native_source_code() const;
ParticlesShaderData() {}
virtual ~ParticlesShaderData();
};
ShaderData *_create_particles_shader_func();
struct ParticleProcessMaterialData : public MaterialData {
ParticlesShaderData *shader_data = nullptr;
RID uniform_set;
virtual void set_render_priority(int p_priority) {}
virtual void set_next_pass(RID p_pass) {}
virtual void update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty);
virtual void bind_uniforms();
virtual ~ParticleProcessMaterialData();
};
MaterialData *_create_particles_material_func(ShaderData *p_shader);
/* Global shader uniform structs */
struct GlobalShaderUniforms {
enum {
BUFFER_DIRTY_REGION_SIZE = 1024
};
struct Variable {
HashSet<RID> texture_materials; // materials using this
RS::GlobalShaderParameterType type;
Variant value;
Variant override;
int32_t buffer_index; //for vectors
int32_t buffer_elements; //for vectors
};
HashMap<StringName, Variable> variables;
struct Value {
float x;
float y;
float z;
float w;
};
struct ValueInt {
int32_t x;
int32_t y;
int32_t z;
int32_t w;
};
struct ValueUInt {
uint32_t x;
uint32_t y;
uint32_t z;
uint32_t w;
};
struct ValueUsage {
uint32_t elements = 0;
};
List<RID> materials_using_buffer;
List<RID> materials_using_texture;
GLuint buffer = GLuint(0);
Value *buffer_values = nullptr;
ValueUsage *buffer_usage = nullptr;
bool *buffer_dirty_regions = nullptr;
uint32_t buffer_dirty_region_count = 0;
uint32_t buffer_size;
bool must_update_texture_materials = false;
bool must_update_buffer_materials = false;
HashMap<RID, int32_t> instance_buffer_pos;
};
class MaterialStorage : public RendererMaterialStorage {
private:
friend struct MaterialData;
static MaterialStorage *singleton;
/* GLOBAL SHADER UNIFORM API */
GlobalShaderUniforms global_shader_uniforms;
int32_t _global_shader_uniform_allocate(uint32_t p_elements);
void _global_shader_uniform_store_in_buffer(int32_t p_index, RS::GlobalShaderParameterType p_type, const Variant &p_value);
void _global_shader_uniform_mark_buffer_dirty(int32_t p_index, int32_t p_elements);
/* SHADER API */
ShaderDataRequestFunction shader_data_request_func[RS::SHADER_MAX];
mutable RID_Owner<Shader, true> shader_owner;
/* MATERIAL API */
MaterialDataRequestFunction material_data_request_func[RS::SHADER_MAX];
mutable RID_Owner<Material, true> material_owner;
SelfList<Material>::List material_update_list;
HashSet<RID> dummy_embedded_set;
public:
static MaterialStorage *get_singleton();
MaterialStorage();
virtual ~MaterialStorage();
static _FORCE_INLINE_ void store_transform(const Transform3D &p_mtx, float *p_array) {
p_array[0] = p_mtx.basis.rows[0][0];
p_array[1] = p_mtx.basis.rows[1][0];
p_array[2] = p_mtx.basis.rows[2][0];
p_array[3] = 0;
p_array[4] = p_mtx.basis.rows[0][1];
p_array[5] = p_mtx.basis.rows[1][1];
p_array[6] = p_mtx.basis.rows[2][1];
p_array[7] = 0;
p_array[8] = p_mtx.basis.rows[0][2];
p_array[9] = p_mtx.basis.rows[1][2];
p_array[10] = p_mtx.basis.rows[2][2];
p_array[11] = 0;
p_array[12] = p_mtx.origin.x;
p_array[13] = p_mtx.origin.y;
p_array[14] = p_mtx.origin.z;
p_array[15] = 1;
}
static _FORCE_INLINE_ void store_transform_3x3(const Basis &p_mtx, float *p_array) {
p_array[0] = p_mtx.rows[0][0];
p_array[1] = p_mtx.rows[1][0];
p_array[2] = p_mtx.rows[2][0];
p_array[3] = 0;
p_array[4] = p_mtx.rows[0][1];
p_array[5] = p_mtx.rows[1][1];
p_array[6] = p_mtx.rows[2][1];
p_array[7] = 0;
p_array[8] = p_mtx.rows[0][2];
p_array[9] = p_mtx.rows[1][2];
p_array[10] = p_mtx.rows[2][2];
p_array[11] = 0;
}
static _FORCE_INLINE_ void store_camera(const Projection &p_mtx, float *p_array) {
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
p_array[i * 4 + j] = p_mtx.columns[i][j];
}
}
}
struct Shaders {
CanvasShaderGLES3 canvas_shader;
SkyShaderGLES3 sky_shader;
SceneShaderGLES3 scene_shader;
ParticlesShaderGLES3 particles_process_shader;
ShaderCompiler compiler_canvas;
ShaderCompiler compiler_scene;
ShaderCompiler compiler_particles;
ShaderCompiler compiler_sky;
} shaders;
/* GLOBAL SHADER UNIFORM API */
void _update_global_shader_uniforms();
virtual void global_shader_parameter_add(const StringName &p_name, RS::GlobalShaderParameterType p_type, const Variant &p_value) override;
virtual void global_shader_parameter_remove(const StringName &p_name) override;
virtual Vector<StringName> global_shader_parameter_get_list() const override;
virtual void global_shader_parameter_set(const StringName &p_name, const Variant &p_value) override;
virtual void global_shader_parameter_set_override(const StringName &p_name, const Variant &p_value) override;
virtual Variant global_shader_parameter_get(const StringName &p_name) const override;
virtual RS::GlobalShaderParameterType global_shader_parameter_get_type(const StringName &p_name) const override;
RS::GlobalShaderParameterType global_shader_parameter_get_type_internal(const StringName &p_name) const;
virtual void global_shader_parameters_load_settings(bool p_load_textures = true) override;
virtual void global_shader_parameters_clear() override;
virtual int32_t global_shader_parameters_instance_allocate(RID p_instance) override;
virtual void global_shader_parameters_instance_free(RID p_instance) override;
virtual void global_shader_parameters_instance_update(RID p_instance, int p_index, const Variant &p_value, int p_flags_count = 0) override;
GLuint global_shader_parameters_get_uniform_buffer() const;
/* SHADER API */
Shader *get_shader(RID p_rid) { return shader_owner.get_or_null(p_rid); }
bool owns_shader(RID p_rid) { return shader_owner.owns(p_rid); }
void _shader_make_dirty(Shader *p_shader);
virtual RID shader_allocate() override;
virtual void shader_initialize(RID p_rid, bool p_embedded = true) override;
virtual void shader_free(RID p_rid) override;
virtual void shader_set_code(RID p_shader, const String &p_code) override;
virtual void shader_set_path_hint(RID p_shader, const String &p_path) override;
virtual String shader_get_code(RID p_shader) const override;
virtual void get_shader_parameter_list(RID p_shader, List<PropertyInfo> *p_param_list) const override;
virtual void shader_set_default_texture_parameter(RID p_shader, const StringName &p_name, RID p_texture, int p_index) override;
virtual RID shader_get_default_texture_parameter(RID p_shader, const StringName &p_name, int p_index) const override;
virtual Variant shader_get_parameter_default(RID p_shader, const StringName &p_name) const override;
virtual RS::ShaderNativeSourceCode shader_get_native_source_code(RID p_shader) const override;
virtual void shader_embedded_set_lock() override {}
virtual const HashSet<RID> &shader_embedded_set_get() const override { return dummy_embedded_set; }
virtual void shader_embedded_set_unlock() override {}
/* MATERIAL API */
Material *get_material(RID p_rid) { return material_owner.get_or_null(p_rid); }
bool owns_material(RID p_rid) { return material_owner.owns(p_rid); }
void _material_queue_update(Material *material, bool p_uniform, bool p_texture);
void _update_queued_materials();
virtual RID material_allocate() override;
virtual void material_initialize(RID p_rid) override;
virtual void material_free(RID p_rid) override;
virtual void material_set_shader(RID p_material, RID p_shader) override;
virtual void material_set_param(RID p_material, const StringName &p_param, const Variant &p_value) override;
virtual Variant material_get_param(RID p_material, const StringName &p_param) const override;
virtual void material_set_next_pass(RID p_material, RID p_next_material) override;
virtual void material_set_render_priority(RID p_material, int priority) override;
virtual bool material_is_animated(RID p_material) override;
virtual bool material_casts_shadows(RID p_material) override;
virtual RS::CullMode material_get_cull_mode(RID p_material) const override;
virtual void material_get_instance_shader_parameters(RID p_material, List<InstanceShaderParam> *r_parameters) override;
virtual void material_update_dependency(RID p_material, DependencyTracker *p_instance) override;
_FORCE_INLINE_ uint32_t material_get_shader_id(RID p_material) {
Material *material = material_owner.get_or_null(p_material);
return material->shader_id;
}
_FORCE_INLINE_ MaterialData *material_get_data(RID p_material, RS::ShaderMode p_shader_mode) {
Material *material = material_owner.get_or_null(p_material);
if (!material || material->shader_mode != p_shader_mode) {
return nullptr;
} else {
return material->data;
}
}
};
} // namespace GLES3
#endif // GLES3_ENABLED

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,615 @@
/**************************************************************************/
/* mesh_storage.h */
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#ifdef GLES3_ENABLED
#include "core/templates/local_vector.h"
#include "core/templates/rid_owner.h"
#include "core/templates/self_list.h"
#include "drivers/gles3/shaders/skeleton.glsl.gen.h"
#include "servers/rendering/storage/mesh_storage.h"
#include "servers/rendering/storage/utilities.h"
#include "platform_gl.h"
namespace GLES3 {
struct MeshInstance;
struct Mesh {
struct Surface {
struct Attrib {
bool enabled;
bool integer;
GLint size;
GLenum type;
GLboolean normalized;
GLsizei stride;
uint32_t offset;
};
RS::PrimitiveType primitive = RS::PRIMITIVE_POINTS;
uint64_t format = 0;
GLuint vertex_buffer = 0;
GLuint attribute_buffer = 0;
GLuint skin_buffer = 0;
uint32_t vertex_count = 0;
uint32_t vertex_buffer_size = 0;
uint32_t attribute_buffer_size = 0;
uint32_t skin_buffer_size = 0;
// Cache vertex arrays so they can be created
struct Version {
uint32_t input_mask = 0;
GLuint vertex_array = 0;
Attrib attribs[RS::ARRAY_MAX];
};
SpinLock version_lock; //needed to access versions
Version *versions = nullptr; //allocated on demand
uint32_t version_count = 0;
GLuint index_buffer = 0;
uint32_t index_count = 0;
uint32_t index_buffer_size = 0;
struct Wireframe {
GLuint index_buffer = 0;
uint32_t index_count = 0;
uint32_t index_buffer_size = 0;
};
Wireframe *wireframe = nullptr;
struct LOD {
float edge_length = 0.0;
uint32_t index_count = 0;
uint32_t index_buffer_size = 0;
GLuint index_buffer = 0;
};
LOD *lods = nullptr;
uint32_t lod_count = 0;
AABB aabb;
Vector<AABB> bone_aabbs;
// Transform used in runtime bone AABBs compute.
// As bone AABBs are saved in Mesh space, but bones animation is in Skeleton space.
Transform3D mesh_to_skeleton_xform;
Vector4 uv_scale;
struct BlendShape {
GLuint vertex_buffer = 0;
GLuint vertex_array = 0;
};
BlendShape *blend_shapes = nullptr;
GLuint skeleton_vertex_array = 0;
RID material;
};
uint32_t blend_shape_count = 0;
RS::BlendShapeMode blend_shape_mode = RS::BLEND_SHAPE_MODE_NORMALIZED;
Surface **surfaces = nullptr;
uint32_t surface_count = 0;
bool has_bone_weights = false;
AABB aabb;
AABB custom_aabb;
uint64_t skeleton_aabb_version = 0;
Vector<RID> material_cache;
List<MeshInstance *> instances;
RID shadow_mesh;
HashSet<Mesh *> shadow_owners;
String path;
Dependency dependency;
};
/* Mesh Instance */
struct MeshInstance {
Mesh *mesh = nullptr;
RID skeleton;
struct Surface {
GLuint vertex_buffers[2] = { 0, 0 };
GLuint vertex_arrays[2] = { 0, 0 };
GLuint vertex_buffer = 0;
int vertex_stride_cache = 0;
int vertex_size_cache = 0;
int vertex_normal_offset_cache = 0;
int vertex_tangent_offset_cache = 0;
uint64_t format_cache = 0;
Mesh::Surface::Version *versions = nullptr; //allocated on demand
uint32_t version_count = 0;
};
LocalVector<Surface> surfaces;
LocalVector<float> blend_weights;
List<MeshInstance *>::Element *I = nullptr; //used to erase itself
uint64_t skeleton_version = 0;
bool dirty = false;
bool weights_dirty = false;
SelfList<MeshInstance> weight_update_list;
SelfList<MeshInstance> array_update_list;
Transform2D canvas_item_transform_2d;
MeshInstance() :
weight_update_list(this), array_update_list(this) {}
};
/* MultiMesh */
struct MultiMesh {
RID mesh;
int instances = 0;
RS::MultimeshTransformFormat xform_format = RS::MULTIMESH_TRANSFORM_3D;
bool uses_colors = false;
bool uses_custom_data = false;
int visible_instances = -1;
AABB aabb;
AABB custom_aabb;
bool aabb_dirty = false;
bool buffer_set = false;
uint32_t stride_cache = 0;
uint32_t color_offset_cache = 0;
uint32_t custom_data_offset_cache = 0;
Vector<float> data_cache; //used if individual setting is used
bool *data_cache_dirty_regions = nullptr;
uint32_t data_cache_used_dirty_regions = 0;
GLuint buffer = 0;
bool dirty = false;
MultiMesh *dirty_list = nullptr;
RendererMeshStorage::MultiMeshInterpolator interpolator;
Dependency dependency;
};
struct Skeleton {
bool use_2d = false;
int size = 0;
int height = 0;
LocalVector<float> data;
bool dirty = false;
Skeleton *dirty_list = nullptr;
Transform2D base_transform_2d;
GLuint transforms_texture = 0;
uint64_t version = 1;
Dependency dependency;
};
class MeshStorage : public RendererMeshStorage {
private:
static MeshStorage *singleton;
struct {
SkeletonShaderGLES3 shader;
RID shader_version;
} skeleton_shader;
/* Mesh */
mutable RID_Owner<Mesh, true> mesh_owner;
void _mesh_surface_generate_version_for_input_mask(Mesh::Surface::Version &v, Mesh::Surface *s, uint64_t p_input_mask, MeshInstance::Surface *mis = nullptr);
void _mesh_surface_clear(Mesh *mesh, int p_surface);
/* Mesh Instance API */
mutable RID_Owner<MeshInstance> mesh_instance_owner;
void _mesh_instance_clear(MeshInstance *mi);
void _mesh_instance_add_surface(MeshInstance *mi, Mesh *mesh, uint32_t p_surface);
void _mesh_instance_remove_surface(MeshInstance *mi, int p_surface);
void _blend_shape_bind_mesh_instance_buffer(MeshInstance *p_mi, uint32_t p_surface);
SelfList<MeshInstance>::List dirty_mesh_instance_weights;
SelfList<MeshInstance>::List dirty_mesh_instance_arrays;
/* MultiMesh */
mutable RID_Owner<MultiMesh, true> multimesh_owner;
MultiMesh *multimesh_dirty_list = nullptr;
_FORCE_INLINE_ void _multimesh_make_local(MultiMesh *multimesh) const;
_FORCE_INLINE_ void _multimesh_mark_dirty(MultiMesh *multimesh, int p_index, bool p_aabb);
_FORCE_INLINE_ void _multimesh_mark_all_dirty(MultiMesh *multimesh, bool p_data, bool p_aabb);
_FORCE_INLINE_ void _multimesh_re_create_aabb(MultiMesh *multimesh, const float *p_data, int p_instances);
/* Skeleton */
mutable RID_Owner<Skeleton, true> skeleton_owner;
_FORCE_INLINE_ void _skeleton_make_dirty(Skeleton *skeleton);
void _compute_skeleton(MeshInstance *p_mi, Skeleton *p_sk, uint32_t p_surface);
Skeleton *skeleton_dirty_list = nullptr;
public:
static MeshStorage *get_singleton();
MeshStorage();
virtual ~MeshStorage();
/* MESH API */
Mesh *get_mesh(RID p_rid) { return mesh_owner.get_or_null(p_rid); }
bool owns_mesh(RID p_rid) { return mesh_owner.owns(p_rid); }
virtual RID mesh_allocate() override;
virtual void mesh_initialize(RID p_rid) override;
virtual void mesh_free(RID p_rid) override;
virtual void mesh_set_blend_shape_count(RID p_mesh, int p_blend_shape_count) override;
virtual bool mesh_needs_instance(RID p_mesh, bool p_has_skeleton) override;
virtual void mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface) override;
virtual int mesh_get_blend_shape_count(RID p_mesh) const override;
virtual void mesh_set_blend_shape_mode(RID p_mesh, RS::BlendShapeMode p_mode) override;
virtual RS::BlendShapeMode mesh_get_blend_shape_mode(RID p_mesh) const override;
virtual void mesh_surface_update_vertex_region(RID p_mesh, int p_surface, int p_offset, const Vector<uint8_t> &p_data) override;
virtual void mesh_surface_update_attribute_region(RID p_mesh, int p_surface, int p_offset, const Vector<uint8_t> &p_data) override;
virtual void mesh_surface_update_skin_region(RID p_mesh, int p_surface, int p_offset, const Vector<uint8_t> &p_data) override;
virtual void mesh_surface_update_index_region(RID p_mesh, int p_surface, int p_offset, const Vector<uint8_t> &p_data) override;
virtual void mesh_surface_set_material(RID p_mesh, int p_surface, RID p_material) override;
virtual RID mesh_surface_get_material(RID p_mesh, int p_surface) const override;
virtual RS::SurfaceData mesh_get_surface(RID p_mesh, int p_surface) const override;
virtual int mesh_get_surface_count(RID p_mesh) const override;
virtual void mesh_set_custom_aabb(RID p_mesh, const AABB &p_aabb) override;
virtual AABB mesh_get_custom_aabb(RID p_mesh) const override;
virtual AABB mesh_get_aabb(RID p_mesh, RID p_skeleton = RID()) override;
virtual void mesh_set_path(RID p_mesh, const String &p_path) override;
virtual String mesh_get_path(RID p_mesh) const override;
virtual void mesh_set_shadow_mesh(RID p_mesh, RID p_shadow_mesh) override;
virtual void mesh_clear(RID p_mesh) override;
virtual void mesh_surface_remove(RID p_mesh, int p_surface) override;
virtual void mesh_debug_usage(List<RS::MeshInfo> *r_info) override {}
_FORCE_INLINE_ const RID *mesh_get_surface_count_and_materials(RID p_mesh, uint32_t &r_surface_count) {
Mesh *mesh = mesh_owner.get_or_null(p_mesh);
ERR_FAIL_NULL_V(mesh, nullptr);
r_surface_count = mesh->surface_count;
if (r_surface_count == 0) {
return nullptr;
}
if (mesh->material_cache.is_empty()) {
mesh->material_cache.resize(mesh->surface_count);
for (uint32_t i = 0; i < r_surface_count; i++) {
mesh->material_cache.write[i] = mesh->surfaces[i]->material;
}
}
return mesh->material_cache.ptr();
}
_FORCE_INLINE_ void *mesh_get_surface(RID p_mesh, uint32_t p_surface_index) {
Mesh *mesh = mesh_owner.get_or_null(p_mesh);
ERR_FAIL_NULL_V(mesh, nullptr);
ERR_FAIL_UNSIGNED_INDEX_V(p_surface_index, mesh->surface_count, nullptr);
return mesh->surfaces[p_surface_index];
}
_FORCE_INLINE_ RID mesh_get_shadow_mesh(RID p_mesh) {
Mesh *mesh = mesh_owner.get_or_null(p_mesh);
ERR_FAIL_NULL_V(mesh, RID());
return mesh->shadow_mesh;
}
_FORCE_INLINE_ RS::PrimitiveType mesh_surface_get_primitive(void *p_surface) {
Mesh::Surface *surface = reinterpret_cast<Mesh::Surface *>(p_surface);
return surface->primitive;
}
_FORCE_INLINE_ bool mesh_surface_has_lod(void *p_surface) const {
Mesh::Surface *s = reinterpret_cast<Mesh::Surface *>(p_surface);
return s->lod_count > 0;
}
_FORCE_INLINE_ uint32_t mesh_surface_get_vertices_drawn_count(void *p_surface) const {
Mesh::Surface *s = reinterpret_cast<Mesh::Surface *>(p_surface);
return s->index_count ? s->index_count : s->vertex_count;
}
_FORCE_INLINE_ uint32_t mesh_surface_get_lod(void *p_surface, float p_model_scale, float p_distance_threshold, float p_mesh_lod_threshold, uint32_t &r_index_count) const {
Mesh::Surface *s = reinterpret_cast<Mesh::Surface *>(p_surface);
ERR_FAIL_NULL_V(s, 0);
int32_t current_lod = -1;
r_index_count = s->index_count;
for (uint32_t i = 0; i < s->lod_count; i++) {
float screen_size = s->lods[i].edge_length * p_model_scale / p_distance_threshold;
if (screen_size > p_mesh_lod_threshold) {
break;
}
current_lod = i;
}
if (current_lod == -1) {
return 0;
} else {
r_index_count = s->lods[current_lod].index_count;
return current_lod + 1;
}
}
_FORCE_INLINE_ GLuint mesh_surface_get_index_buffer(void *p_surface, uint32_t p_lod) const {
Mesh::Surface *s = reinterpret_cast<Mesh::Surface *>(p_surface);
if (p_lod == 0) {
return s->index_buffer;
} else {
return s->lods[p_lod - 1].index_buffer;
}
}
_FORCE_INLINE_ GLuint mesh_surface_get_index_buffer_wireframe(void *p_surface) const {
Mesh::Surface *s = reinterpret_cast<Mesh::Surface *>(p_surface);
if (s->wireframe) {
return s->wireframe->index_buffer;
}
return 0;
}
_FORCE_INLINE_ GLenum mesh_surface_get_index_type(void *p_surface) const {
Mesh::Surface *s = reinterpret_cast<Mesh::Surface *>(p_surface);
return (s->vertex_count <= 65536 && s->vertex_count > 0) ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT;
}
// Use this to cache Vertex Array Objects so they are only generated once
_FORCE_INLINE_ void mesh_surface_get_vertex_arrays_and_format(void *p_surface, uint64_t p_input_mask, GLuint &r_vertex_array_gl) {
Mesh::Surface *s = reinterpret_cast<Mesh::Surface *>(p_surface);
s->version_lock.lock();
// There will never be more than 3 or 4 versions, so iterating is the fastest way.
for (uint32_t i = 0; i < s->version_count; i++) {
if (s->versions[i].input_mask != p_input_mask) {
continue;
}
// We have this version, hooray.
r_vertex_array_gl = s->versions[i].vertex_array;
s->version_lock.unlock();
return;
}
uint32_t version = s->version_count;
s->version_count++;
s->versions = (Mesh::Surface::Version *)memrealloc(s->versions, sizeof(Mesh::Surface::Version) * s->version_count);
_mesh_surface_generate_version_for_input_mask(s->versions[version], s, p_input_mask);
r_vertex_array_gl = s->versions[version].vertex_array;
s->version_lock.unlock();
}
/* MESH INSTANCE API */
MeshInstance *get_mesh_instance(RID p_rid) { return mesh_instance_owner.get_or_null(p_rid); }
bool owns_mesh_instance(RID p_rid) { return mesh_instance_owner.owns(p_rid); }
virtual RID mesh_instance_create(RID p_base) override;
virtual void mesh_instance_free(RID p_rid) override;
virtual void mesh_instance_set_skeleton(RID p_mesh_instance, RID p_skeleton) override;
virtual void mesh_instance_set_blend_shape_weight(RID p_mesh_instance, int p_shape, float p_weight) override;
virtual void mesh_instance_check_for_update(RID p_mesh_instance) override;
virtual void mesh_instance_set_canvas_item_transform(RID p_mesh_instance, const Transform2D &p_transform) override;
virtual void update_mesh_instances() override;
// TODO: considering hashing versions with multimesh buffer RID.
// Doing so would allow us to avoid specifying multimesh buffer pointers every frame and may improve performance.
_FORCE_INLINE_ void mesh_instance_surface_get_vertex_arrays_and_format(RID p_mesh_instance, uint32_t p_surface_index, uint64_t p_input_mask, GLuint &r_vertex_array_gl) {
MeshInstance *mi = mesh_instance_owner.get_or_null(p_mesh_instance);
ERR_FAIL_NULL(mi);
Mesh *mesh = mi->mesh;
ERR_FAIL_UNSIGNED_INDEX(p_surface_index, mesh->surface_count);
MeshInstance::Surface *mis = &mi->surfaces[p_surface_index];
Mesh::Surface *s = mesh->surfaces[p_surface_index];
s->version_lock.lock();
//there will never be more than, at much, 3 or 4 versions, so iterating is the fastest way
for (uint32_t i = 0; i < mis->version_count; i++) {
if (mis->versions[i].input_mask != p_input_mask) {
continue;
}
//we have this version, hooray
r_vertex_array_gl = mis->versions[i].vertex_array;
s->version_lock.unlock();
return;
}
uint32_t version = mis->version_count;
mis->version_count++;
mis->versions = (Mesh::Surface::Version *)memrealloc(mis->versions, sizeof(Mesh::Surface::Version) * mis->version_count);
_mesh_surface_generate_version_for_input_mask(mis->versions[version], s, p_input_mask, mis);
r_vertex_array_gl = mis->versions[version].vertex_array;
s->version_lock.unlock();
}
/* MULTIMESH API */
MultiMesh *get_multimesh(RID p_rid) { return multimesh_owner.get_or_null(p_rid); }
bool owns_multimesh(RID p_rid) { return multimesh_owner.owns(p_rid); }
virtual RID _multimesh_allocate() override;
virtual void _multimesh_initialize(RID p_rid) override;
virtual void _multimesh_free(RID p_rid) override;
virtual void _multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors = false, bool p_use_custom_data = false, bool p_use_indirect = false) override;
virtual int _multimesh_get_instance_count(RID p_multimesh) const override;
virtual void _multimesh_set_mesh(RID p_multimesh, RID p_mesh) override;
virtual void _multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform) override;
virtual void _multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) override;
virtual void _multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) override;
virtual void _multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) override;
virtual RID _multimesh_get_mesh(RID p_multimesh) const override;
virtual void _multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb) override;
virtual AABB _multimesh_get_custom_aabb(RID p_multimesh) const override;
virtual AABB _multimesh_get_aabb(RID p_multimesh) override;
virtual Transform3D _multimesh_instance_get_transform(RID p_multimesh, int p_index) const override;
virtual Transform2D _multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const override;
virtual Color _multimesh_instance_get_color(RID p_multimesh, int p_index) const override;
virtual Color _multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const override;
virtual void _multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) override;
virtual RID _multimesh_get_command_buffer_rd_rid(RID p_multimesh) const override;
virtual RID _multimesh_get_buffer_rd_rid(RID p_multimesh) const override;
virtual Vector<float> _multimesh_get_buffer(RID p_multimesh) const override;
virtual void _multimesh_set_visible_instances(RID p_multimesh, int p_visible) override;
virtual int _multimesh_get_visible_instances(RID p_multimesh) const override;
virtual MultiMeshInterpolator *_multimesh_get_interpolator(RID p_multimesh) const override;
void _update_dirty_multimeshes();
_FORCE_INLINE_ RS::MultimeshTransformFormat multimesh_get_transform_format(RID p_multimesh) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V(multimesh, RS::MULTIMESH_TRANSFORM_3D);
return multimesh->xform_format;
}
_FORCE_INLINE_ bool multimesh_uses_colors(RID p_multimesh) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V(multimesh, false);
return multimesh->uses_colors;
}
_FORCE_INLINE_ bool multimesh_uses_custom_data(RID p_multimesh) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V(multimesh, false);
return multimesh->uses_custom_data;
}
_FORCE_INLINE_ uint32_t multimesh_get_instances_to_draw(RID p_multimesh) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V(multimesh, 0);
if (multimesh->visible_instances >= 0) {
return multimesh->visible_instances;
}
return multimesh->instances;
}
_FORCE_INLINE_ GLuint multimesh_get_gl_buffer(RID p_multimesh) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V(multimesh, 0);
return multimesh->buffer;
}
_FORCE_INLINE_ uint32_t multimesh_get_stride(RID p_multimesh) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V(multimesh, 0);
return multimesh->stride_cache;
}
_FORCE_INLINE_ uint32_t multimesh_get_color_offset(RID p_multimesh) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V(multimesh, 0);
return multimesh->color_offset_cache;
}
_FORCE_INLINE_ uint32_t multimesh_get_custom_data_offset(RID p_multimesh) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V(multimesh, 0);
return multimesh->custom_data_offset_cache;
}
/* SKELETON API */
Skeleton *get_skeleton(RID p_rid) { return skeleton_owner.get_or_null(p_rid); }
bool owns_skeleton(RID p_rid) { return skeleton_owner.owns(p_rid); }
virtual RID skeleton_allocate() override;
virtual void skeleton_initialize(RID p_rid) override;
virtual void skeleton_free(RID p_rid) override;
virtual void skeleton_allocate_data(RID p_skeleton, int p_bones, bool p_2d_skeleton = false) override;
virtual void skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform) override;
virtual int skeleton_get_bone_count(RID p_skeleton) const override;
virtual void skeleton_bone_set_transform(RID p_skeleton, int p_bone, const Transform3D &p_transform) override;
virtual Transform3D skeleton_bone_get_transform(RID p_skeleton, int p_bone) const override;
virtual void skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform) override;
virtual Transform2D skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const override;
virtual void skeleton_update_dependency(RID p_base, DependencyTracker *p_instance) override;
void _update_dirty_skeletons();
_FORCE_INLINE_ bool skeleton_is_valid(RID p_skeleton) {
return skeleton_owner.get_or_null(p_skeleton) != nullptr;
}
};
} // namespace GLES3
#endif // GLES3_ENABLED

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,462 @@
/**************************************************************************/
/* particles_storage.h */
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#ifdef GLES3_ENABLED
#include "core/templates/rid_owner.h"
#include "core/templates/self_list.h"
#include "drivers/gles3/shaders/particles_copy.glsl.gen.h"
#include "servers/rendering/storage/particles_storage.h"
#include "servers/rendering/storage/utilities.h"
#include "platform_gl.h"
namespace GLES3 {
enum ParticlesUniformLocation {
PARTICLES_FRAME_UNIFORM_LOCATION,
PARTICLES_GLOBALS_UNIFORM_LOCATION,
PARTICLES_MATERIAL_UNIFORM_LOCATION,
};
class ParticlesStorage : public RendererParticlesStorage {
private:
static ParticlesStorage *singleton;
/* PARTICLES */
struct ParticleInstanceData3D {
float xform[12];
float color[2]; // Color and custom are packed together into one vec4;
float custom[2];
};
struct ParticleInstanceData2D {
float xform[8];
float color[2]; // Color and custom are packed together into one vec4;
float custom[2];
};
struct ParticlesViewSort {
Vector3 z_dir;
bool operator()(const ParticleInstanceData3D &p_a, const ParticleInstanceData3D &p_b) const {
return z_dir.dot(Vector3(p_a.xform[3], p_a.xform[7], p_a.xform[11])) < z_dir.dot(Vector3(p_b.xform[3], p_b.xform[7], p_b.xform[11]));
}
};
struct ParticlesFrameParams {
enum {
MAX_ATTRACTORS = 32,
MAX_COLLIDERS = 32,
MAX_3D_TEXTURES = 0 // GLES3 renderer doesn't support using 3D textures for flow field or collisions.
};
enum AttractorType {
ATTRACTOR_TYPE_SPHERE,
ATTRACTOR_TYPE_BOX,
ATTRACTOR_TYPE_VECTOR_FIELD,
};
struct Attractor {
float transform[16];
float extents[4]; // Extents or radius. w-channel is padding.
uint32_t type;
float strength;
float attenuation;
float directionality;
};
enum CollisionType {
COLLISION_TYPE_SPHERE,
COLLISION_TYPE_BOX,
COLLISION_TYPE_SDF,
COLLISION_TYPE_HEIGHT_FIELD,
COLLISION_TYPE_2D_SDF,
};
struct Collider {
float transform[16];
float extents[4]; // Extents or radius. w-channel is padding.
uint32_t type;
float scale;
float pad0;
float pad1;
};
uint32_t emitting;
uint32_t cycle;
float system_phase;
float prev_system_phase;
float explosiveness;
float randomness;
float time;
float delta;
float particle_size;
float amount_ratio;
float pad1;
float pad2;
uint32_t random_seed;
uint32_t attractor_count;
uint32_t collider_count;
uint32_t frame;
float emission_transform[16];
float emitter_velocity[3];
float interp_to_end;
Attractor attractors[MAX_ATTRACTORS];
Collider colliders[MAX_COLLIDERS];
};
static_assert(sizeof(ParticlesFrameParams) % 16 == 0, "ParticlesFrameParams size must be a multiple of 16 bytes");
static_assert(sizeof(ParticlesFrameParams) < 16384, "ParticlesFrameParams must be 16384 bytes or smaller");
struct Particles {
RS::ParticlesMode mode = RS::PARTICLES_MODE_3D;
bool inactive = true;
double inactive_time = 0.0;
bool emitting = false;
bool one_shot = false;
float amount_ratio = 1.0;
int amount = 0;
double lifetime = 1.0;
double pre_process_time = 0.0;
real_t request_process_time = 0.0;
real_t explosiveness = 0.0;
real_t randomness = 0.0;
bool restart_request = false;
AABB custom_aabb = AABB(Vector3(-4, -4, -4), Vector3(8, 8, 8));
bool use_local_coords = false;
bool has_collision_cache = false;
bool has_sdf_collision = false;
Transform2D sdf_collision_transform;
Rect2 sdf_collision_to_screen;
GLuint sdf_collision_texture = 0;
RID process_material;
uint32_t frame_counter = 0;
RS::ParticlesTransformAlign transform_align = RS::PARTICLES_TRANSFORM_ALIGN_DISABLED;
RS::ParticlesDrawOrder draw_order = RS::PARTICLES_DRAW_ORDER_INDEX;
Vector<RID> draw_passes;
GLuint frame_params_ubo = 0;
// We may process particles multiple times each frame (if they have a fixed FPS higher than the game FPS).
// Unfortunately, this means we can't just use a round-robin system of 3 buffers.
// To ensure the sort buffer is accurate, we copy the last frame instance buffer just before processing.
// Transform Feedback buffer and VAO for rendering.
// Each frame we render to this one.
GLuint front_vertex_array = 0; // Binds process buffer. Used for processing.
GLuint front_process_buffer = 0; // Transform + color + custom data + userdata + velocity + flags. Only needed for processing.
GLuint front_instance_buffer = 0; // Transform + color + custom data. In packed format needed for rendering.
// VAO for transform feedback, contains last frame's data.
// Read from this one for particles process and then copy to last frame buffer.
GLuint back_vertex_array = 0; // Binds process buffer. Used for processing.
GLuint back_process_buffer = 0; // Transform + color + custom data + userdata + velocity + flags. Only needed for processing.
GLuint back_instance_buffer = 0; // Transform + color + custom data. In packed format needed for rendering.
uint32_t instance_buffer_size_cache = 0;
uint32_t instance_buffer_stride_cache = 0;
uint32_t num_attrib_arrays_cache = 0;
uint32_t process_buffer_stride_cache = 0;
// Only ever copied to, holds last frame's instance data, then swaps with sort_buffer.
GLuint last_frame_buffer = 0;
bool last_frame_buffer_filled = false;
float last_frame_phase = 0.0;
// The frame-before-last's instance buffer.
// Use this to copy data back for sorting or computing AABB.
GLuint sort_buffer = 0;
bool sort_buffer_filled = false;
float sort_buffer_phase = 0.0;
uint32_t userdata_count = 0;
bool dirty = false;
SelfList<Particles> update_list;
double phase = 0.0;
double prev_phase = 0.0;
uint64_t prev_ticks = 0;
uint32_t random_seed = 0;
uint32_t cycle_number = 0;
double speed_scale = 1.0;
int fixed_fps = 30;
bool interpolate = true;
bool fractional_delta = false;
double frame_remainder = 0;
real_t collision_base_size = 0.01;
bool clear = true;
Transform3D emission_transform;
Vector3 emitter_velocity;
float interp_to_end = 0.0;
HashSet<RID> collisions;
Dependency dependency;
double trail_length = 1.0;
bool trails_enabled = false;
Particles() :
update_list(this) {
random_seed = Math::rand();
}
};
void _particles_process(Particles *p_particles, double p_delta);
void _particles_free_data(Particles *particles);
void _particles_update_buffers(Particles *particles);
void _particles_allocate_history_buffers(Particles *particles);
void _particles_update_instance_buffer(Particles *particles, const Vector3 &p_axis, const Vector3 &p_up_axis);
template <typename T>
void _particles_reverse_lifetime_sort(Particles *particles);
struct ParticlesShader {
RID default_shader;
RID default_material;
RID default_shader_version;
ParticlesCopyShaderGLES3 copy_shader;
RID copy_shader_version;
} particles_shader;
SelfList<Particles>::List particle_update_list;
mutable RID_Owner<Particles, true> particles_owner;
/* Particles Collision */
struct ParticlesCollision {
RS::ParticlesCollisionType type = RS::PARTICLES_COLLISION_TYPE_SPHERE_ATTRACT;
uint32_t cull_mask = 0xFFFFFFFF;
float radius = 1.0;
Vector3 extents = Vector3(1, 1, 1);
float attractor_strength = 1.0;
float attractor_attenuation = 1.0;
float attractor_directionality = 0.0;
GLuint field_texture = 0;
GLuint heightfield_texture = 0;
GLuint heightfield_fb = 0;
Size2i heightfield_fb_size;
uint32_t heightfield_mask = (1 << 20) - 1;
RS::ParticlesCollisionHeightfieldResolution heightfield_resolution = RS::PARTICLES_COLLISION_HEIGHTFIELD_RESOLUTION_1024;
Dependency dependency;
};
struct ParticlesCollisionInstance {
RID collision;
Transform3D transform;
bool active = false;
};
mutable RID_Owner<ParticlesCollision, true> particles_collision_owner;
mutable RID_Owner<ParticlesCollisionInstance> particles_collision_instance_owner;
public:
static ParticlesStorage *get_singleton();
ParticlesStorage();
virtual ~ParticlesStorage();
bool free(RID p_rid);
/* PARTICLES */
bool owns_particles(RID p_rid) { return particles_owner.owns(p_rid); }
virtual RID particles_allocate() override;
virtual void particles_initialize(RID p_rid) override;
virtual void particles_free(RID p_rid) override;
virtual void particles_set_mode(RID p_particles, RS::ParticlesMode p_mode) override;
virtual void particles_emit(RID p_particles, const Transform3D &p_transform, const Vector3 &p_velocity, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags) override;
virtual void particles_set_emitting(RID p_particles, bool p_emitting) override;
virtual void particles_set_amount(RID p_particles, int p_amount) override;
virtual void particles_set_amount_ratio(RID p_particles, float p_amount_ratio) override;
virtual void particles_set_lifetime(RID p_particles, double p_lifetime) override;
virtual void particles_set_one_shot(RID p_particles, bool p_one_shot) override;
virtual void particles_set_pre_process_time(RID p_particles, double p_time) override;
virtual void particles_request_process_time(RID p_particles, real_t p_request_process_time) override;
virtual void particles_set_explosiveness_ratio(RID p_particles, real_t p_ratio) override;
virtual void particles_set_randomness_ratio(RID p_particles, real_t p_ratio) override;
virtual void particles_set_custom_aabb(RID p_particles, const AABB &p_aabb) override;
virtual void particles_set_speed_scale(RID p_particles, double p_scale) override;
virtual void particles_set_use_local_coordinates(RID p_particles, bool p_enable) override;
virtual void particles_set_process_material(RID p_particles, RID p_material) override;
virtual RID particles_get_process_material(RID p_particles) const override;
virtual void particles_set_fixed_fps(RID p_particles, int p_fps) override;
virtual void particles_set_interpolate(RID p_particles, bool p_enable) override;
virtual void particles_set_fractional_delta(RID p_particles, bool p_enable) override;
virtual void particles_set_subemitter(RID p_particles, RID p_subemitter_particles) override;
virtual void particles_set_view_axis(RID p_particles, const Vector3 &p_axis, const Vector3 &p_up_axis) override;
virtual void particles_set_collision_base_size(RID p_particles, real_t p_size) override;
virtual void particles_set_transform_align(RID p_particles, RS::ParticlesTransformAlign p_transform_align) override;
virtual void particles_set_seed(RID p_particles, uint32_t p_seed) override;
virtual void particles_set_trails(RID p_particles, bool p_enable, double p_length) override;
virtual void particles_set_trail_bind_poses(RID p_particles, const Vector<Transform3D> &p_bind_poses) override;
virtual void particles_restart(RID p_particles) override;
virtual void particles_set_draw_order(RID p_particles, RS::ParticlesDrawOrder p_order) override;
virtual void particles_set_draw_passes(RID p_particles, int p_count) override;
virtual void particles_set_draw_pass_mesh(RID p_particles, int p_pass, RID p_mesh) override;
virtual void particles_request_process(RID p_particles) override;
virtual AABB particles_get_current_aabb(RID p_particles) override;
virtual AABB particles_get_aabb(RID p_particles) const override;
virtual void particles_set_emission_transform(RID p_particles, const Transform3D &p_transform) override;
virtual void particles_set_emitter_velocity(RID p_particles, const Vector3 &p_velocity) override;
virtual void particles_set_interp_to_end(RID p_particles, float p_interp) override;
virtual bool particles_get_emitting(RID p_particles) override;
virtual int particles_get_draw_passes(RID p_particles) const override;
virtual RID particles_get_draw_pass_mesh(RID p_particles, int p_pass) const override;
virtual void particles_add_collision(RID p_particles, RID p_instance) override;
virtual void particles_remove_collision(RID p_particles, RID p_instance) override;
void particles_set_canvas_sdf_collision(RID p_particles, bool p_enable, const Transform2D &p_xform, const Rect2 &p_to_screen, GLuint p_texture);
virtual void update_particles() override;
virtual bool particles_is_inactive(RID p_particles) const override;
_FORCE_INLINE_ RS::ParticlesMode particles_get_mode(RID p_particles) {
Particles *particles = particles_owner.get_or_null(p_particles);
ERR_FAIL_NULL_V(particles, RS::PARTICLES_MODE_2D);
return particles->mode;
}
_FORCE_INLINE_ uint32_t particles_get_amount(RID p_particles) {
Particles *particles = particles_owner.get_or_null(p_particles);
ERR_FAIL_NULL_V(particles, 0);
return particles->amount;
}
_FORCE_INLINE_ GLuint particles_get_gl_buffer(RID p_particles) {
Particles *particles = particles_owner.get_or_null(p_particles);
if ((particles->draw_order == RS::PARTICLES_DRAW_ORDER_VIEW_DEPTH || particles->draw_order == RS::PARTICLES_DRAW_ORDER_REVERSE_LIFETIME) && particles->sort_buffer_filled) {
return particles->sort_buffer;
}
return particles->back_instance_buffer;
}
_FORCE_INLINE_ bool particles_has_collision(RID p_particles) {
Particles *particles = particles_owner.get_or_null(p_particles);
ERR_FAIL_NULL_V(particles, false);
return particles->has_collision_cache;
}
_FORCE_INLINE_ uint32_t particles_is_using_local_coords(RID p_particles) {
Particles *particles = particles_owner.get_or_null(p_particles);
ERR_FAIL_NULL_V(particles, false);
return particles->use_local_coords;
}
Dependency *particles_get_dependency(RID p_particles) const;
/* PARTICLES COLLISION */
bool owns_particles_collision(RID p_rid) { return particles_collision_owner.owns(p_rid); }
virtual RID particles_collision_allocate() override;
virtual void particles_collision_initialize(RID p_rid) override;
virtual void particles_collision_free(RID p_rid) override;
virtual void particles_collision_set_collision_type(RID p_particles_collision, RS::ParticlesCollisionType p_type) override;
virtual void particles_collision_set_cull_mask(RID p_particles_collision, uint32_t p_cull_mask) override;
virtual void particles_collision_set_sphere_radius(RID p_particles_collision, real_t p_radius) override;
virtual void particles_collision_set_box_extents(RID p_particles_collision, const Vector3 &p_extents) override;
virtual void particles_collision_set_attractor_strength(RID p_particles_collision, real_t p_strength) override;
virtual void particles_collision_set_attractor_directionality(RID p_particles_collision, real_t p_directionality) override;
virtual void particles_collision_set_attractor_attenuation(RID p_particles_collision, real_t p_curve) override;
virtual void particles_collision_set_field_texture(RID p_particles_collision, RID p_texture) override;
virtual void particles_collision_height_field_update(RID p_particles_collision) override;
virtual void particles_collision_set_height_field_resolution(RID p_particles_collision, RS::ParticlesCollisionHeightfieldResolution p_resolution) override;
virtual AABB particles_collision_get_aabb(RID p_particles_collision) const override;
Vector3 particles_collision_get_extents(RID p_particles_collision) const;
virtual bool particles_collision_is_heightfield(RID p_particles_collision) const override;
GLuint particles_collision_get_heightfield_framebuffer(RID p_particles_collision) const;
virtual uint32_t particles_collision_get_height_field_mask(RID p_particles_collision) const override;
virtual void particles_collision_set_height_field_mask(RID p_particles_collision, uint32_t p_heightfield_mask) override;
virtual uint32_t particles_collision_get_cull_mask(RID p_particles_collision) const override;
_FORCE_INLINE_ Size2i particles_collision_get_heightfield_size(RID p_particles_collision) const {
ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision);
ERR_FAIL_NULL_V(particles_collision, Size2i());
ERR_FAIL_COND_V(particles_collision->type != RS::PARTICLES_COLLISION_TYPE_HEIGHTFIELD_COLLIDE, Size2i());
return particles_collision->heightfield_fb_size;
}
Dependency *particles_collision_get_dependency(RID p_particles) const;
/* PARTICLES COLLISION INSTANCE*/
bool owns_particles_collision_instance(RID p_rid) { return particles_collision_instance_owner.owns(p_rid); }
virtual RID particles_collision_instance_create(RID p_collision) override;
virtual void particles_collision_instance_free(RID p_rid) override;
virtual void particles_collision_instance_set_transform(RID p_collision_instance, const Transform3D &p_transform) override;
virtual void particles_collision_instance_set_active(RID p_collision_instance, bool p_active) override;
};
} // namespace GLES3
#endif // GLES3_ENABLED

View File

@@ -0,0 +1,675 @@
/**************************************************************************/
/* render_scene_buffers_gles3.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 "render_scene_buffers_gles3.h"
#include "config.h"
#include "texture_storage.h"
#include "utilities.h"
#ifdef ANDROID_ENABLED
#define glFramebufferTextureMultiviewOVR GLES3::Config::get_singleton()->eglFramebufferTextureMultiviewOVR
#define glTexStorage3DMultisample GLES3::Config::get_singleton()->eglTexStorage3DMultisample
#define glFramebufferTexture2DMultisampleEXT GLES3::Config::get_singleton()->eglFramebufferTexture2DMultisampleEXT
#define glFramebufferTextureMultisampleMultiviewOVR GLES3::Config::get_singleton()->eglFramebufferTextureMultisampleMultiviewOVR
#endif // ANDROID_ENABLED
// Will only be defined if GLES 3.2 headers are included
#ifndef GL_TEXTURE_2D_MULTISAMPLE_ARRAY
#define GL_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9102
#endif
RenderSceneBuffersGLES3::RenderSceneBuffersGLES3() {
for (int i = 0; i < 4; i++) {
glow.levels[i].color = 0;
glow.levels[i].fbo = 0;
}
}
RenderSceneBuffersGLES3::~RenderSceneBuffersGLES3() {
free_render_buffer_data();
}
void RenderSceneBuffersGLES3::_rt_attach_textures(GLuint p_color, GLuint p_depth, GLsizei p_samples, uint32_t p_view_count, bool p_depth_has_stencil) {
if (p_view_count > 1) {
if (p_samples > 1) {
#if defined(ANDROID_ENABLED) || defined(WEB_ENABLED)
glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, p_color, 0, p_samples, 0, p_view_count);
glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, p_depth_has_stencil ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT, p_depth, 0, p_samples, 0, p_view_count);
#else
ERR_PRINT_ONCE("Multiview MSAA isn't supported on this platform.");
#endif
} else {
#ifndef IOS_ENABLED
glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, p_color, 0, 0, p_view_count);
glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, p_depth_has_stencil ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT, p_depth, 0, 0, p_view_count);
#else
ERR_PRINT_ONCE("Multiview isn't supported on this platform.");
#endif
}
} else {
if (p_samples > 1) {
#ifdef ANDROID_ENABLED
glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, p_color, 0, p_samples);
glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, p_depth_has_stencil ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, p_depth, 0, p_samples);
#else
ERR_PRINT_ONCE("MSAA via EXT_multisampled_render_to_texture isn't supported on this platform.");
#endif
} else {
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, p_color, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, p_depth_has_stencil ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, p_depth, 0);
}
}
}
GLuint RenderSceneBuffersGLES3::_rt_get_cached_fbo(GLuint p_color, GLuint p_depth, GLsizei p_samples, uint32_t p_view_count) {
FBDEF new_fbo;
#if defined(ANDROID_ENABLED) || defined(WEB_ENABLED)
// There shouldn't be more then 3 entries in this...
for (const FBDEF &cached_fbo : msaa3d.cached_fbos) {
if (cached_fbo.color == p_color && cached_fbo.depth == p_depth) {
return cached_fbo.fbo;
}
}
new_fbo.color = p_color;
new_fbo.depth = p_depth;
glGenFramebuffers(1, &new_fbo.fbo);
glBindFramebuffer(GL_FRAMEBUFFER, new_fbo.fbo);
_rt_attach_textures(p_color, p_depth, p_samples, p_view_count, true);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
WARN_PRINT("Could not create 3D MSAA framebuffer, status: " + GLES3::TextureStorage::get_singleton()->get_framebuffer_error(status));
glDeleteFramebuffers(1, &new_fbo.fbo);
new_fbo.fbo = 0;
} else {
// cache it!
msaa3d.cached_fbos.push_back(new_fbo);
}
glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
#endif
return new_fbo.fbo;
}
void RenderSceneBuffersGLES3::configure(const RenderSceneBuffersConfiguration *p_config) {
GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton();
GLES3::Config *config = GLES3::Config::get_singleton();
free_render_buffer_data();
internal_size = p_config->get_internal_size();
target_size = p_config->get_target_size();
scaling_3d_mode = p_config->get_scaling_3d_mode();
//fsr_sharpness = p_config->get_fsr_sharpness();
//texture_mipmap_bias = p_config->get_texture_mipmap_bias();
//anisotropic_filtering_level = p_config->get_anisotropic_filtering_level();
render_target = p_config->get_render_target();
msaa3d.mode = p_config->get_msaa_3d();
//screen_space_aa = p_config->get_screen_space_aa();
//use_debanding = p_config->get_use_debanding();
view_count = config->multiview_supported ? p_config->get_view_count() : 1;
bool use_multiview = view_count > 1;
// Get color format data from our render target so we match those
if (render_target.is_valid()) {
color_internal_format = texture_storage->render_target_get_color_internal_format(render_target);
color_format = texture_storage->render_target_get_color_format(render_target);
color_type = texture_storage->render_target_get_color_type(render_target);
color_format_size = texture_storage->render_target_get_color_format_size(render_target);
} else {
// reflection probe? or error?
color_internal_format = GL_RGBA8;
color_format = GL_RGBA;
color_type = GL_UNSIGNED_BYTE;
color_format_size = 4;
}
// Check our scaling mode
if (scaling_3d_mode != RS::VIEWPORT_SCALING_3D_MODE_OFF && internal_size.x == 0 && internal_size.y == 0) {
// Disable, no size set.
scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_OFF;
} else if (scaling_3d_mode != RS::VIEWPORT_SCALING_3D_MODE_OFF && internal_size == target_size) {
// If size matches, we won't use scaling.
scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_OFF;
} else if (scaling_3d_mode != RS::VIEWPORT_SCALING_3D_MODE_OFF && scaling_3d_mode != RS::VIEWPORT_SCALING_3D_MODE_BILINEAR) {
// We only support bilinear scaling atm.
WARN_PRINT_ONCE("GLES only supports bilinear scaling.");
scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_BILINEAR;
}
// Check if we support MSAA.
if (msaa3d.mode != RS::VIEWPORT_MSAA_DISABLED && internal_size.x == 0 && internal_size.y == 0) {
// Disable, no size set.
msaa3d.mode = RS::VIEWPORT_MSAA_DISABLED;
} else if (!use_multiview && msaa3d.mode != RS::VIEWPORT_MSAA_DISABLED && !config->msaa_supported && !config->rt_msaa_supported) {
WARN_PRINT_ONCE("MSAA is not supported on this device.");
msaa3d.mode = RS::VIEWPORT_MSAA_DISABLED;
} else if (use_multiview && msaa3d.mode != RS::VIEWPORT_MSAA_DISABLED && !config->msaa_multiview_supported && !config->rt_msaa_multiview_supported) {
WARN_PRINT_ONCE("Multiview MSAA is not supported on this device.");
msaa3d.mode = RS::VIEWPORT_MSAA_DISABLED;
}
// We don't create our buffers right away because post effects can be made active at any time and change our buffer configuration.
}
void RenderSceneBuffersGLES3::_check_render_buffers() {
GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton();
GLES3::Config *config = GLES3::Config::get_singleton();
ERR_FAIL_COND(view_count == 0);
bool use_internal_buffer = scaling_3d_mode != RS::VIEWPORT_SCALING_3D_MODE_OFF || apply_color_adjustments_in_post;
GLenum depth_format = GL_DEPTH24_STENCIL8;
uint32_t depth_format_size = 4;
bool use_multiview = view_count > 1;
if ((!use_internal_buffer || internal3d.color != 0) && (msaa3d.mode == RS::VIEWPORT_MSAA_DISABLED || msaa3d.color != 0)) {
// already setup!
return;
}
if (use_internal_buffer && internal3d.color == 0) {
// Setup our internal buffer.
GLenum texture_target = use_multiview ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D;
// Create our color buffer.
glGenTextures(1, &internal3d.color);
glBindTexture(texture_target, internal3d.color);
if (use_multiview) {
glTexImage3D(texture_target, 0, color_internal_format, internal_size.x, internal_size.y, view_count, 0, color_format, color_type, nullptr);
} else {
glTexImage2D(texture_target, 0, color_internal_format, internal_size.x, internal_size.y, 0, color_format, color_type, nullptr);
}
glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
GLES3::Utilities::get_singleton()->texture_allocated_data(internal3d.color, internal_size.x * internal_size.y * view_count * color_format_size, "3D color texture");
// Create our depth buffer.
glGenTextures(1, &internal3d.depth);
glBindTexture(texture_target, internal3d.depth);
if (use_multiview) {
glTexImage3D(texture_target, 0, depth_format, internal_size.x, internal_size.y, view_count, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, nullptr);
} else {
glTexImage2D(texture_target, 0, depth_format, internal_size.x, internal_size.y, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, nullptr);
}
glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
GLES3::Utilities::get_singleton()->texture_allocated_data(internal3d.depth, internal_size.x * internal_size.y * view_count * depth_format_size, "3D depth texture");
// Create our internal 3D FBO.
// Note that if MSAA is used and our rt_msaa_* extensions are available, this is only used for blitting and effects.
glGenFramebuffers(1, &internal3d.fbo);
glBindFramebuffer(GL_FRAMEBUFFER, internal3d.fbo);
#ifndef IOS_ENABLED
if (use_multiview) {
glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, internal3d.color, 0, 0, view_count);
glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, internal3d.depth, 0, 0, view_count);
} else {
#else
{
#endif
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture_target, internal3d.color, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, texture_target, internal3d.depth, 0);
}
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
_clear_intermediate_buffers();
WARN_PRINT("Could not create 3D internal buffers, status: " + texture_storage->get_framebuffer_error(status));
}
glBindTexture(texture_target, 0);
glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
}
if (msaa3d.mode != RS::VIEWPORT_MSAA_DISABLED && msaa3d.color == 0) {
// Setup MSAA.
const GLsizei samples[] = { 1, 2, 4, 8 };
msaa3d.samples = samples[msaa3d.mode];
// Constrain by limits of OpenGL driver.
if (msaa3d.samples > config->msaa_max_samples) {
msaa3d.samples = config->msaa_max_samples;
}
if (!use_multiview && !config->rt_msaa_supported) {
// Render to texture extensions not supported? fall back to MSAA framebuffer through GL_EXT_framebuffer_multisample.
// Note, if 2D MSAA matches 3D MSAA and we're not scaling, it would be ideal if we reuse our 2D MSAA buffer here.
// We can't however because we don't trigger a change in configuration if 2D MSAA changes.
// We'll accept the overhead in this situation.
msaa3d.needs_resolve = true;
msaa3d.check_fbo_cache = false;
// Create our color buffer.
glGenRenderbuffers(1, &msaa3d.color);
glBindRenderbuffer(GL_RENDERBUFFER, msaa3d.color);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, msaa3d.samples, color_internal_format, internal_size.x, internal_size.y);
GLES3::Utilities::get_singleton()->render_buffer_allocated_data(msaa3d.color, internal_size.x * internal_size.y * view_count * 4 * msaa3d.samples, "MSAA 3D color render buffer");
// Create our depth buffer.
glGenRenderbuffers(1, &msaa3d.depth);
glBindRenderbuffer(GL_RENDERBUFFER, msaa3d.depth);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, msaa3d.samples, depth_format, internal_size.x, internal_size.y);
GLES3::Utilities::get_singleton()->render_buffer_allocated_data(msaa3d.depth, internal_size.x * internal_size.y * view_count * depth_format_size * msaa3d.samples, "MSAA 3D depth render buffer");
// Create our MSAA 3D FBO.
glGenFramebuffers(1, &msaa3d.fbo);
glBindFramebuffer(GL_FRAMEBUFFER, msaa3d.fbo);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, msaa3d.color);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, msaa3d.depth);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
_clear_msaa3d_buffers();
msaa3d.mode = RS::VIEWPORT_MSAA_DISABLED;
WARN_PRINT("Could not create 3D MSAA buffers, status: " + texture_storage->get_framebuffer_error(status));
}
glBindRenderbuffer(GL_RENDERBUFFER, 0);
glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
#if !defined(IOS_ENABLED) && !defined(WEB_ENABLED)
} else if (use_multiview && !config->rt_msaa_multiview_supported) {
// Render to texture extensions not supported? fall back to MSAA textures through GL_EXT_multiview_texture_multisample.
msaa3d.needs_resolve = true;
msaa3d.check_fbo_cache = false;
// Create our color buffer.
glGenTextures(1, &msaa3d.color);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, msaa3d.color);
#ifdef ANDROID_ENABLED
glTexStorage3DMultisample(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, msaa3d.samples, color_internal_format, internal_size.x, internal_size.y, view_count, GL_TRUE);
#else
glTexImage3DMultisample(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, msaa3d.samples, color_internal_format, internal_size.x, internal_size.y, view_count, GL_TRUE);
#endif
GLES3::Utilities::get_singleton()->texture_allocated_data(msaa3d.color, internal_size.x * internal_size.y * view_count * color_format_size * msaa3d.samples, "MSAA 3D color texture");
// Create our depth buffer.
glGenTextures(1, &msaa3d.depth);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, msaa3d.depth);
#ifdef ANDROID_ENABLED
glTexStorage3DMultisample(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, msaa3d.samples, depth_format, internal_size.x, internal_size.y, view_count, GL_TRUE);
#else
glTexImage3DMultisample(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, msaa3d.samples, depth_format, internal_size.x, internal_size.y, view_count, GL_TRUE);
#endif
GLES3::Utilities::get_singleton()->texture_allocated_data(msaa3d.depth, internal_size.x * internal_size.y * view_count * depth_format_size * msaa3d.samples, "MSAA 3D depth texture");
// Create our MSAA 3D FBO.
glGenFramebuffers(1, &msaa3d.fbo);
glBindFramebuffer(GL_FRAMEBUFFER, msaa3d.fbo);
glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, msaa3d.color, 0, 0, view_count);
glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, msaa3d.depth, 0, 0, view_count);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
_clear_msaa3d_buffers();
msaa3d.mode = RS::VIEWPORT_MSAA_DISABLED;
WARN_PRINT("Could not create 3D MSAA buffers, status: " + texture_storage->get_framebuffer_error(status));
}
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, 0);
glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
#endif
#if defined(ANDROID_ENABLED) || defined(WEB_ENABLED) // Only supported on OpenGLES!
} else if (!use_internal_buffer) {
// We are going to render directly into our render target textures,
// these can change from frame to frame as we cycle through swapchains,
// hence we'll use our FBO cache here.
msaa3d.needs_resolve = false;
msaa3d.check_fbo_cache = true;
} else if (use_internal_buffer) {
// We can combine MSAA and scaling/effects.
msaa3d.needs_resolve = false;
msaa3d.check_fbo_cache = false;
// We render to our internal textures, MSAA is only done in tile memory only.
// On mobile this means MSAA never leaves tile memory = efficiency!
glGenFramebuffers(1, &msaa3d.fbo);
glBindFramebuffer(GL_FRAMEBUFFER, msaa3d.fbo);
_rt_attach_textures(internal3d.color, internal3d.depth, msaa3d.samples, view_count, true);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
_clear_msaa3d_buffers();
msaa3d.mode = RS::VIEWPORT_MSAA_DISABLED;
WARN_PRINT("Could not create 3D MSAA framebuffer, status: " + texture_storage->get_framebuffer_error(status));
}
glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
#endif
} else {
// HUH? how did we get here?
WARN_PRINT_ONCE("MSAA is not supported on this device.");
msaa3d.mode = RS::VIEWPORT_MSAA_DISABLED;
msaa3d.samples = 1;
msaa3d.check_fbo_cache = false;
}
} else {
msaa3d.samples = 1;
msaa3d.check_fbo_cache = false;
}
}
void RenderSceneBuffersGLES3::configure_for_probe(Size2i p_size) {
internal_size = p_size;
target_size = p_size;
scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_OFF;
view_count = 1;
}
void RenderSceneBuffersGLES3::_clear_msaa3d_buffers() {
for (const FBDEF &cached_fbo : msaa3d.cached_fbos) {
GLuint fbo = cached_fbo.fbo;
glDeleteFramebuffers(1, &fbo);
}
msaa3d.cached_fbos.clear();
if (msaa3d.fbo) {
glDeleteFramebuffers(1, &msaa3d.fbo);
msaa3d.fbo = 0;
}
if (msaa3d.color != 0) {
if (view_count == 1) {
GLES3::Utilities::get_singleton()->render_buffer_free_data(msaa3d.color);
} else {
GLES3::Utilities::get_singleton()->texture_free_data(msaa3d.color);
}
msaa3d.color = 0;
}
if (msaa3d.depth != 0) {
if (view_count == 1) {
GLES3::Utilities::get_singleton()->render_buffer_free_data(msaa3d.depth);
} else {
GLES3::Utilities::get_singleton()->texture_free_data(msaa3d.depth);
}
msaa3d.depth = 0;
}
}
void RenderSceneBuffersGLES3::_clear_intermediate_buffers() {
if (internal3d.fbo) {
glDeleteFramebuffers(1, &internal3d.fbo);
internal3d.fbo = 0;
}
if (internal3d.color != 0) {
GLES3::Utilities::get_singleton()->texture_free_data(internal3d.color);
internal3d.color = 0;
}
if (internal3d.depth != 0) {
GLES3::Utilities::get_singleton()->texture_free_data(internal3d.depth);
internal3d.depth = 0;
}
}
void RenderSceneBuffersGLES3::check_backbuffer(bool p_need_color, bool p_need_depth) {
GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton();
// Setup our back buffer
if (backbuffer3d.fbo == 0) {
glGenFramebuffers(1, &backbuffer3d.fbo);
}
glBindFramebuffer(GL_FRAMEBUFFER, backbuffer3d.fbo);
bool use_multiview = view_count > 1 && GLES3::Config::get_singleton()->multiview_supported;
GLenum texture_target = use_multiview ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D;
GLenum depth_format = GL_DEPTH24_STENCIL8;
uint32_t depth_format_size = 4;
if (backbuffer3d.color == 0 && p_need_color) {
glGenTextures(1, &backbuffer3d.color);
glBindTexture(texture_target, backbuffer3d.color);
if (use_multiview) {
glTexImage3D(texture_target, 0, color_internal_format, internal_size.x, internal_size.y, view_count, 0, color_format, color_type, nullptr);
} else {
glTexImage2D(texture_target, 0, color_internal_format, internal_size.x, internal_size.y, 0, color_format, color_type, nullptr);
}
glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
GLES3::Utilities::get_singleton()->texture_allocated_data(backbuffer3d.color, internal_size.x * internal_size.y * view_count * color_format_size, "3D Back buffer color texture");
#ifndef IOS_ENABLED
if (use_multiview) {
glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, backbuffer3d.color, 0, 0, view_count);
} else {
#else
{
#endif
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture_target, backbuffer3d.color, 0);
}
}
if (backbuffer3d.depth == 0 && p_need_depth) {
glGenTextures(1, &backbuffer3d.depth);
glBindTexture(texture_target, backbuffer3d.depth);
if (use_multiview) {
glTexImage3D(texture_target, 0, depth_format, internal_size.x, internal_size.y, view_count, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT, nullptr);
} else {
glTexImage2D(texture_target, 0, depth_format, internal_size.x, internal_size.y, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT, nullptr);
}
glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
GLES3::Utilities::get_singleton()->texture_allocated_data(backbuffer3d.depth, internal_size.x * internal_size.y * view_count * depth_format_size, "3D back buffer depth texture");
#ifndef IOS_ENABLED
if (use_multiview) {
glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, backbuffer3d.depth, 0, 0, view_count);
} else {
#else
{
#endif
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, texture_target, backbuffer3d.depth, 0);
}
}
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
_clear_back_buffers();
WARN_PRINT("Could not create 3D back buffers, status: " + texture_storage->get_framebuffer_error(status));
}
glBindTexture(texture_target, 0);
glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
}
void RenderSceneBuffersGLES3::_clear_back_buffers() {
if (backbuffer3d.fbo) {
glDeleteFramebuffers(1, &backbuffer3d.fbo);
backbuffer3d.fbo = 0;
}
if (backbuffer3d.color != 0) {
GLES3::Utilities::get_singleton()->texture_free_data(backbuffer3d.color);
backbuffer3d.color = 0;
}
if (backbuffer3d.depth != 0) {
GLES3::Utilities::get_singleton()->texture_free_data(backbuffer3d.depth);
backbuffer3d.depth = 0;
}
}
void RenderSceneBuffersGLES3::set_apply_color_adjustments_in_post(bool p_apply_in_post) {
apply_color_adjustments_in_post = p_apply_in_post;
}
void RenderSceneBuffersGLES3::check_glow_buffers() {
if (glow.levels[0].color != 0) {
// already have these setup..
return;
}
GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton();
Size2i level_size = internal_size;
for (int i = 0; i < 4; i++) {
level_size = Size2i(level_size.x >> 1, level_size.y >> 1).maxi(4);
glow.levels[i].size = level_size;
// Create our texture
glGenTextures(1, &glow.levels[i].color);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, glow.levels[i].color);
glTexImage2D(GL_TEXTURE_2D, 0, color_internal_format, level_size.x, level_size.y, 0, color_format, color_type, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
GLES3::Utilities::get_singleton()->texture_allocated_data(glow.levels[i].color, level_size.x * level_size.y * color_format_size, String("Glow buffer ") + String::num_int64(i));
// Create our FBO
glGenFramebuffers(1, &glow.levels[i].fbo);
glBindFramebuffer(GL_FRAMEBUFFER, glow.levels[i].fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, glow.levels[i].color, 0);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
WARN_PRINT("Could not create glow buffers, status: " + texture_storage->get_framebuffer_error(status));
_clear_glow_buffers();
break;
}
}
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
}
void RenderSceneBuffersGLES3::_clear_glow_buffers() {
for (int i = 0; i < 4; i++) {
if (glow.levels[i].fbo != 0) {
glDeleteFramebuffers(1, &glow.levels[i].fbo);
glow.levels[i].fbo = 0;
}
if (glow.levels[i].color != 0) {
GLES3::Utilities::get_singleton()->texture_free_data(glow.levels[i].color);
glow.levels[i].color = 0;
}
}
}
void RenderSceneBuffersGLES3::free_render_buffer_data() {
_clear_msaa3d_buffers();
_clear_intermediate_buffers();
_clear_back_buffers();
_clear_glow_buffers();
}
GLuint RenderSceneBuffersGLES3::get_render_fbo() {
GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton();
GLuint rt_fbo = 0;
_check_render_buffers();
if (msaa3d.check_fbo_cache) {
GLuint color = texture_storage->render_target_get_color(render_target);
GLuint depth = texture_storage->render_target_get_depth(render_target);
rt_fbo = _rt_get_cached_fbo(color, depth, msaa3d.samples, view_count);
if (rt_fbo == 0) {
// Somehow couldn't obtain this? Just render without MSAA.
rt_fbo = texture_storage->render_target_get_fbo(render_target);
}
} else if (msaa3d.fbo != 0) {
// We have an MSAA fbo, render to our MSAA buffer
return msaa3d.fbo;
} else if (internal3d.fbo != 0) {
// We have an internal buffer, render to our internal buffer!
return internal3d.fbo;
} else {
rt_fbo = texture_storage->render_target_get_fbo(render_target);
}
if (texture_storage->render_target_is_reattach_textures(render_target)) {
GLuint color = texture_storage->render_target_get_color(render_target);
GLuint depth = texture_storage->render_target_get_depth(render_target);
bool depth_has_stencil = texture_storage->render_target_get_depth_has_stencil(render_target);
glBindFramebuffer(GL_FRAMEBUFFER, rt_fbo);
_rt_attach_textures(color, depth, msaa3d.samples, view_count, depth_has_stencil);
glBindFramebuffer(GL_FRAMEBUFFER, texture_storage->system_fbo);
}
return rt_fbo;
}
#endif // GLES3_ENABLED

View File

@@ -0,0 +1,165 @@
/**************************************************************************/
/* render_scene_buffers_gles3.h */
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#ifdef GLES3_ENABLED
#include "drivers/gles3/effects/glow.h"
#include "servers/rendering/storage/render_scene_buffers.h"
#include "platform_gl.h"
class RenderSceneBuffersGLES3 : public RenderSceneBuffers {
GDCLASS(RenderSceneBuffersGLES3, RenderSceneBuffers);
public:
Size2i internal_size; // Size of the buffer we render 3D content to.
Size2i target_size; // Size of our output buffer (render target).
RS::ViewportScaling3DMode scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_OFF;
//float fsr_sharpness = 0.2f;
//RS::ViewportScreenSpaceAA screen_space_aa = RS::VIEWPORT_SCREEN_SPACE_AA_DISABLED;
//bool use_taa = false;
//bool use_debanding = false;
uint32_t view_count = 1;
bool apply_color_adjustments_in_post = false;
RID render_target;
// Color format details from our render target
GLuint color_internal_format = GL_RGBA8;
GLuint color_format = GL_RGBA;
GLuint color_type = GL_UNSIGNED_BYTE;
uint32_t color_format_size = 4;
struct FBDEF {
GLuint color = 0;
GLuint depth = 0;
GLuint fbo = 0;
};
struct RTMSAA3D {
RS::ViewportMSAA mode = RS::VIEWPORT_MSAA_DISABLED;
bool needs_resolve = false;
GLsizei samples = 1;
GLuint color = 0;
GLuint depth = 0;
GLuint fbo = 0;
bool check_fbo_cache = false;
Vector<FBDEF> cached_fbos;
} msaa3d; // MSAA buffers used to render 3D
FBDEF internal3d; // buffers used to either render 3D (scaled/post) or to resolve MSAA into
FBDEF backbuffer3d; // our back buffer
// Buffers for our glow implementation
struct GLOW {
GLES3::Glow::GLOWLEVEL levels[4];
} glow;
private:
void _check_render_buffers();
void _clear_msaa3d_buffers();
void _clear_intermediate_buffers();
void _clear_back_buffers();
void _clear_glow_buffers();
void _rt_attach_textures(GLuint p_color, GLuint p_depth, GLsizei p_samples, uint32_t p_view_count, bool p_depth_has_stencil);
GLuint _rt_get_cached_fbo(GLuint p_color, GLuint p_depth, GLsizei p_samples, uint32_t p_view_count);
public:
RenderSceneBuffersGLES3();
virtual ~RenderSceneBuffersGLES3();
virtual void configure(const RenderSceneBuffersConfiguration *p_config) override;
void configure_for_probe(Size2i p_size);
virtual void set_anisotropic_filtering_level(RS::ViewportAnisotropicFiltering p_anisotropic_filtering_level) override {}
virtual void set_fsr_sharpness(float p_fsr_sharpness) override {}
virtual void set_texture_mipmap_bias(float p_texture_mipmap_bias) override {}
virtual void set_use_debanding(bool p_use_debanding) override {}
void set_apply_color_adjustments_in_post(bool p_apply_in_post);
void free_render_buffer_data();
void check_backbuffer(bool p_need_color, bool p_need_depth); // Check if we need to initialize our backbuffer.
void check_glow_buffers(); // Check if we need to initialize our glow buffers.
GLuint get_render_fbo();
GLuint get_msaa3d_fbo() {
_check_render_buffers();
return msaa3d.fbo;
}
GLuint get_msaa3d_color() {
_check_render_buffers();
return msaa3d.color;
}
GLuint get_msaa3d_depth() {
_check_render_buffers();
return msaa3d.depth;
}
bool get_msaa_needs_resolve() {
_check_render_buffers();
return msaa3d.needs_resolve;
}
GLuint get_internal_fbo() {
_check_render_buffers();
return internal3d.fbo;
}
GLuint get_internal_color() {
_check_render_buffers();
return internal3d.color;
}
GLuint get_internal_depth() {
_check_render_buffers();
return internal3d.depth;
}
GLuint get_backbuffer_fbo() const { return backbuffer3d.fbo; }
GLuint get_backbuffer() const { return backbuffer3d.color; }
GLuint get_backbuffer_depth() const { return backbuffer3d.depth; }
const GLES3::Glow::GLOWLEVEL *get_glow_buffers() const { return &glow.levels[0]; }
// Getters
_FORCE_INLINE_ RID get_render_target() const { return render_target; }
_FORCE_INLINE_ uint32_t get_view_count() const { return view_count; }
_FORCE_INLINE_ Size2i get_internal_size() const { return internal_size; }
_FORCE_INLINE_ Size2i get_target_size() const { return target_size; }
_FORCE_INLINE_ RS::ViewportScaling3DMode get_scaling_3d_mode() const { return scaling_3d_mode; }
//_FORCE_INLINE_ float get_fsr_sharpness() const { return fsr_sharpness; }
_FORCE_INLINE_ RS::ViewportMSAA get_msaa_3d() const { return msaa3d.mode; }
//_FORCE_INLINE_ RS::ViewportScreenSpaceAA get_screen_space_aa() const { return screen_space_aa; }
//_FORCE_INLINE_ bool get_use_taa() const { return use_taa; }
//_FORCE_INLINE_ bool get_use_debanding() const { return use_debanding; }
};
#endif // GLES3_ENABLED

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,737 @@
/**************************************************************************/
/* texture_storage.h */
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#ifdef GLES3_ENABLED
#include "platform_gl.h"
#include "config.h"
#include "core/io/image.h"
#include "core/os/os.h"
#include "core/templates/rid_owner.h"
#include "servers/rendering/renderer_compositor.h"
#include "servers/rendering/storage/texture_storage.h"
#include "drivers/gles3/shaders/canvas_sdf.glsl.gen.h"
namespace GLES3 {
#define _GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE
#define _GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF
#define _EXT_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1
#define _EXT_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2
#define _EXT_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3
#define _EXT_COMPRESSED_RED_RGTC1_EXT 0x8DBB
#define _EXT_COMPRESSED_RED_RGTC1 0x8DBB
#define _EXT_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC
#define _EXT_COMPRESSED_RG_RGTC2 0x8DBD
#define _EXT_COMPRESSED_SIGNED_RG_RGTC2 0x8DBE
#define _EXT_COMPRESSED_SIGNED_RED_RGTC1_EXT 0x8DBC
#define _EXT_COMPRESSED_RED_GREEN_RGTC2_EXT 0x8DBD
#define _EXT_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT 0x8DBE
#define _EXT_ETC1_RGB8_OES 0x8D64
#define _EXT_COMPRESSED_RGBA_BPTC_UNORM 0x8E8C
#define _EXT_COMPRESSED_SRGB_ALPHA_BPTC_UNORM 0x8E8D
#define _EXT_COMPRESSED_RGB_BPTC_SIGNED_FLOAT 0x8E8E
#define _EXT_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT 0x8E8F
#define _EXT_COMPRESSED_R11_EAC 0x9270
#define _EXT_COMPRESSED_SIGNED_R11_EAC 0x9271
#define _EXT_COMPRESSED_RG11_EAC 0x9272
#define _EXT_COMPRESSED_SIGNED_RG11_EAC 0x9273
#define _EXT_COMPRESSED_RGB8_ETC2 0x9274
#define _EXT_COMPRESSED_SRGB8_ETC2 0x9275
#define _EXT_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276
#define _EXT_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9277
#define _EXT_COMPRESSED_RGBA8_ETC2_EAC 0x9278
#define _EXT_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279
#define _EXT_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93B0
#define _EXT_COMPRESSED_RGBA_ASTC_5x4_KHR 0x93B1
#define _EXT_COMPRESSED_RGBA_ASTC_5x5_KHR 0x93B2
#define _EXT_COMPRESSED_RGBA_ASTC_6x5_KHR 0x93B3
#define _EXT_COMPRESSED_RGBA_ASTC_6x6_KHR 0x93B4
#define _EXT_COMPRESSED_RGBA_ASTC_8x5_KHR 0x93B5
#define _EXT_COMPRESSED_RGBA_ASTC_8x6_KHR 0x93B6
#define _EXT_COMPRESSED_RGBA_ASTC_8x8_KHR 0x93B7
#define _EXT_COMPRESSED_RGBA_ASTC_10x5_KHR 0x93B8
#define _EXT_COMPRESSED_RGBA_ASTC_10x6_KHR 0x93B9
#define _EXT_COMPRESSED_RGBA_ASTC_10x8_KHR 0x93BA
#define _EXT_COMPRESSED_RGBA_ASTC_10x10_KHR 0x93BB
#define _EXT_COMPRESSED_RGBA_ASTC_12x10_KHR 0x93BC
#define _EXT_COMPRESSED_RGBA_ASTC_12x12_KHR 0x93BD
#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR 0x93D0
#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR 0x93D1
#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR 0x93D2
#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR 0x93D3
#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR 0x93D4
#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR 0x93D5
#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR 0x93D6
#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR 0x93D7
#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR 0x93D8
#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR 0x93D9
#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR 0x93DA
#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR 0x93DB
#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR 0x93DC
#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR 0x93DD
#define _GL_TEXTURE_EXTERNAL_OES 0x8D65
#define _EXT_TEXTURE_CUBE_MAP_SEAMLESS 0x884F
enum DefaultGLTexture {
DEFAULT_GL_TEXTURE_WHITE,
DEFAULT_GL_TEXTURE_BLACK,
DEFAULT_GL_TEXTURE_TRANSPARENT,
DEFAULT_GL_TEXTURE_NORMAL,
DEFAULT_GL_TEXTURE_ANISO,
DEFAULT_GL_TEXTURE_DEPTH,
DEFAULT_GL_TEXTURE_CUBEMAP_BLACK,
//DEFAULT_GL_TEXTURE_CUBEMAP_ARRAY_BLACK, // Cubemap Arrays not supported in GL 3.3 or GL ES 3.0
DEFAULT_GL_TEXTURE_CUBEMAP_WHITE,
DEFAULT_GL_TEXTURE_3D_WHITE,
DEFAULT_GL_TEXTURE_3D_BLACK,
DEFAULT_GL_TEXTURE_2D_ARRAY_WHITE,
DEFAULT_GL_TEXTURE_2D_UINT,
DEFAULT_GL_TEXTURE_EXT,
DEFAULT_GL_TEXTURE_MAX
};
struct CanvasTexture {
RID diffuse;
RID normal_map;
RID specular;
Color specular_color = Color(1, 1, 1, 1);
float shininess = 1.0;
RS::CanvasItemTextureFilter texture_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT;
RS::CanvasItemTextureRepeat texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT;
};
struct RenderTarget;
struct Texture {
RID self;
bool is_proxy = false;
bool is_from_native_handle = false;
bool is_render_target = false;
RID proxy_to;
Vector<RID> proxies;
String path;
int width = 0;
int height = 0;
int depth = 0;
int mipmaps = 1;
int layers = 1;
int alloc_width = 0;
int alloc_height = 0;
Image::Format format = Image::FORMAT_R8;
Image::Format real_format = Image::FORMAT_R8;
enum Type {
TYPE_2D,
TYPE_LAYERED,
TYPE_3D
};
Type type = TYPE_2D;
RS::TextureLayeredType layered_type = RS::TEXTURE_LAYERED_2D_ARRAY;
GLenum target = GL_TEXTURE_2D;
GLenum gl_format_cache = 0;
GLenum gl_internal_format_cache = 0;
GLenum gl_type_cache = 0;
int total_data_size = 0;
bool compressed = false;
bool resize_to_po2 = false;
bool active = false;
GLuint tex_id = 0;
uint16_t stored_cube_sides = 0;
RenderTarget *render_target = nullptr;
Ref<Image> image_cache_2d;
Vector<Ref<Image>> image_cache_3d;
bool redraw_if_visible = false;
RS::TextureDetectCallback detect_3d_callback = nullptr;
void *detect_3d_callback_ud = nullptr;
RS::TextureDetectCallback detect_normal_callback = nullptr;
void *detect_normal_callback_ud = nullptr;
RS::TextureDetectRoughnessCallback detect_roughness_callback = nullptr;
void *detect_roughness_callback_ud = nullptr;
CanvasTexture *canvas_texture = nullptr;
void copy_from(const Texture &o) {
proxy_to = o.proxy_to;
is_proxy = o.is_proxy;
is_from_native_handle = o.is_from_native_handle;
width = o.width;
height = o.height;
alloc_width = o.alloc_width;
alloc_height = o.alloc_height;
format = o.format;
type = o.type;
layered_type = o.layered_type;
target = o.target;
total_data_size = o.total_data_size;
compressed = o.compressed;
mipmaps = o.mipmaps;
resize_to_po2 = o.resize_to_po2;
active = o.active;
tex_id = o.tex_id;
stored_cube_sides = o.stored_cube_sides;
render_target = o.render_target;
is_render_target = o.is_render_target;
redraw_if_visible = o.redraw_if_visible;
detect_3d_callback = o.detect_3d_callback;
detect_3d_callback_ud = o.detect_3d_callback_ud;
detect_normal_callback = o.detect_normal_callback;
detect_normal_callback_ud = o.detect_normal_callback_ud;
detect_roughness_callback = o.detect_roughness_callback;
detect_roughness_callback_ud = o.detect_roughness_callback_ud;
}
// texture state
void gl_set_filter(RS::CanvasItemTextureFilter p_filter) {
if (p_filter == state_filter) {
return;
}
Config *config = Config::get_singleton();
state_filter = p_filter;
GLenum pmin = GL_NEAREST;
GLenum pmag = GL_NEAREST;
GLint max_lod = 0;
GLfloat anisotropy = 1.0f;
switch (state_filter) {
case RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST: {
pmin = GL_NEAREST;
pmag = GL_NEAREST;
max_lod = 0;
} break;
case RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR: {
pmin = GL_LINEAR;
pmag = GL_LINEAR;
max_lod = 0;
} break;
case RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC: {
anisotropy = config->anisotropic_level;
};
[[fallthrough]];
case RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS: {
pmag = GL_NEAREST;
if (mipmaps <= 1) {
pmin = GL_NEAREST;
max_lod = 0;
} else if (config->use_nearest_mip_filter) {
pmin = GL_NEAREST_MIPMAP_NEAREST;
max_lod = 1000;
} else {
pmin = GL_NEAREST_MIPMAP_LINEAR;
max_lod = 1000;
}
} break;
case RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC: {
anisotropy = config->anisotropic_level;
};
[[fallthrough]];
case RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS: {
pmag = GL_LINEAR;
if (mipmaps <= 1) {
pmin = GL_LINEAR;
max_lod = 0;
} else if (config->use_nearest_mip_filter) {
pmin = GL_LINEAR_MIPMAP_NEAREST;
max_lod = 1000;
} else {
pmin = GL_LINEAR_MIPMAP_LINEAR;
max_lod = 1000;
}
} break;
default: {
return;
} break;
}
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, pmin);
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, pmag);
glTexParameteri(target, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, max_lod);
if (config->support_anisotropic_filter) {
glTexParameterf(target, _GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotropy);
}
}
void gl_set_repeat(RS::CanvasItemTextureRepeat p_repeat) {
if (p_repeat == state_repeat) {
return;
}
state_repeat = p_repeat;
GLenum prep = GL_CLAMP_TO_EDGE;
switch (state_repeat) {
case RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED: {
prep = GL_CLAMP_TO_EDGE;
} break;
case RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED: {
prep = GL_REPEAT;
} break;
case RS::CANVAS_ITEM_TEXTURE_REPEAT_MIRROR: {
prep = GL_MIRRORED_REPEAT;
} break;
default: {
return;
} break;
}
glTexParameteri(target, GL_TEXTURE_WRAP_T, prep);
glTexParameteri(target, GL_TEXTURE_WRAP_R, prep);
glTexParameteri(target, GL_TEXTURE_WRAP_S, prep);
}
private:
RS::CanvasItemTextureFilter state_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_MAX;
RS::CanvasItemTextureRepeat state_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_MAX;
};
struct RenderTarget {
Point2i position = Point2i(0, 0);
Size2i size = Size2i(0, 0);
uint32_t view_count = 1;
int mipmap_count = 1;
RID self;
GLuint fbo = 0;
GLuint color = 0;
GLuint depth = 0;
GLuint backbuffer_fbo = 0;
GLuint backbuffer = 0;
GLuint backbuffer_depth = 0;
bool depth_has_stencil = true;
bool hdr = false; // For Compatibility this effects both 2D and 3D rendering!
GLuint color_internal_format = GL_RGBA8;
GLuint color_format = GL_RGBA;
GLuint color_type = GL_UNSIGNED_BYTE;
uint32_t color_format_size = 4;
Image::Format image_format = Image::FORMAT_RGBA8;
GLuint sdf_texture_write = 0;
GLuint sdf_texture_write_fb = 0;
GLuint sdf_texture_process[2] = { 0, 0 };
GLuint sdf_texture_read = 0;
RS::ViewportSDFOversize sdf_oversize = RS::VIEWPORT_SDF_OVERSIZE_120_PERCENT;
RS::ViewportSDFScale sdf_scale = RS::VIEWPORT_SDF_SCALE_50_PERCENT;
Size2i process_size;
bool sdf_enabled = false;
bool is_transparent = false;
bool direct_to_screen = false;
bool used_in_frame = false;
RS::ViewportMSAA msaa = RS::VIEWPORT_MSAA_DISABLED;
bool reattach_textures = false;
Rect2i render_region;
struct RTOverridden {
bool is_overridden = false;
bool depth_has_stencil = false;
RID color;
RID depth;
RID velocity;
struct FBOCacheEntry {
GLuint fbo;
GLuint color;
GLuint depth;
Size2i size;
Vector<GLuint> allocated_textures;
bool depth_has_stencil;
};
RBMap<uint32_t, FBOCacheEntry> fbo_cache;
} overridden;
RID texture;
Color clear_color = Color(1, 1, 1, 1);
bool clear_requested = false;
RenderTarget() {
}
};
class TextureStorage : public RendererTextureStorage {
private:
static TextureStorage *singleton;
RID default_gl_textures[DEFAULT_GL_TEXTURE_MAX];
/* Canvas Texture API */
RID_Owner<CanvasTexture, true> canvas_texture_owner;
/* Texture API */
// Textures can be created from threads, so this RID_Owner is thread safe.
mutable RID_Owner<Texture, true> texture_owner;
Ref<Image> _get_gl_image_and_format(const Ref<Image> &p_image, Image::Format p_format, Image::Format &r_real_format, GLenum &r_gl_format, GLenum &r_gl_internal_format, GLenum &r_gl_type, bool &r_compressed, bool p_force_decompress) const;
/* TEXTURE ATLAS API */
struct TextureAtlas {
struct Texture {
int users;
Rect2 uv_rect;
};
struct SortItem {
RID texture;
Size2i pixel_size;
Size2i size;
Point2i pos;
bool operator<(const SortItem &p_item) const {
//sort larger to smaller
if (size.height == p_item.size.height) {
return size.width > p_item.size.width;
} else {
return size.height > p_item.size.height;
}
}
};
HashMap<RID, Texture> textures;
bool dirty = true;
GLuint texture = 0;
GLuint framebuffer = 0;
Size2i size;
} texture_atlas;
/* Render Target API */
mutable RID_Owner<RenderTarget> render_target_owner;
void _clear_render_target(RenderTarget *rt);
void _update_render_target(RenderTarget *rt);
void _create_render_target_backbuffer(RenderTarget *rt);
void _render_target_allocate_sdf(RenderTarget *rt);
void _render_target_clear_sdf(RenderTarget *rt);
Rect2i _render_target_get_sdf_rect(const RenderTarget *rt) const;
void _texture_set_data(RID p_texture, const Ref<Image> &p_image, int p_layer, bool p_initialize);
void _texture_set_3d_data(RID p_texture, const Vector<Ref<Image>> &p_data, bool p_initialize);
void _texture_set_swizzle(Texture *p_texture, Image::Format p_real_format);
Vector<Ref<Image>> _texture_3d_read_framebuffer(Texture *p_texture) const;
struct RenderTargetSDF {
CanvasSdfShaderGLES3 shader;
RID shader_version;
} sdf_shader;
public:
static TextureStorage *get_singleton();
TextureStorage();
virtual ~TextureStorage();
_FORCE_INLINE_ RID texture_gl_get_default(DefaultGLTexture p_texture) {
return default_gl_textures[p_texture];
}
/* Canvas Texture API */
CanvasTexture *get_canvas_texture(RID p_rid) { return canvas_texture_owner.get_or_null(p_rid); }
bool owns_canvas_texture(RID p_rid) { return canvas_texture_owner.owns(p_rid); }
virtual RID canvas_texture_allocate() override;
virtual void canvas_texture_initialize(RID p_rid) override;
virtual void canvas_texture_free(RID p_rid) override;
virtual void canvas_texture_set_channel(RID p_canvas_texture, RS::CanvasTextureChannel p_channel, RID p_texture) override;
virtual void canvas_texture_set_shading_parameters(RID p_canvas_texture, const Color &p_base_color, float p_shininess) override;
virtual void canvas_texture_set_texture_filter(RID p_item, RS::CanvasItemTextureFilter p_filter) override;
virtual void canvas_texture_set_texture_repeat(RID p_item, RS::CanvasItemTextureRepeat p_repeat) override;
/* Texture API */
Texture *get_texture(RID p_rid) const {
Texture *texture = texture_owner.get_or_null(p_rid);
if (texture && texture->is_proxy) {
return texture_owner.get_or_null(texture->proxy_to);
}
return texture;
}
bool owns_texture(RID p_rid) { return texture_owner.owns(p_rid); }
void texture_2d_initialize_from_texture(RID p_texture, Texture &p_tex) {
texture_owner.initialize_rid(p_texture, p_tex);
}
virtual RID texture_allocate() override;
virtual void texture_free(RID p_rid) override;
virtual void texture_2d_initialize(RID p_texture, const Ref<Image> &p_image) override;
virtual void texture_2d_layered_initialize(RID p_texture, const Vector<Ref<Image>> &p_layers, RS::TextureLayeredType p_layered_type) override;
virtual void texture_3d_initialize(RID p_texture, Image::Format, int p_width, int p_height, int p_depth, bool p_mipmaps, const Vector<Ref<Image>> &p_data) override;
virtual void texture_external_initialize(RID p_texture, int p_width, int p_height, uint64_t p_external_buffer) override;
virtual void texture_proxy_initialize(RID p_texture, RID p_base) override; //all slices, then all the mipmaps, must be coherent
virtual RID texture_create_from_native_handle(RS::TextureType p_type, Image::Format p_format, uint64_t p_native_handle, int p_width, int p_height, int p_depth, int p_layers = 1, RS::TextureLayeredType p_layered_type = RS::TEXTURE_LAYERED_2D_ARRAY) override;
virtual void texture_2d_update(RID p_texture, const Ref<Image> &p_image, int p_layer = 0) override;
virtual void texture_3d_update(RID p_texture, const Vector<Ref<Image>> &p_data) override;
virtual void texture_external_update(RID p_texture, int p_width, int p_height, uint64_t p_external_buffer) override;
virtual void texture_proxy_update(RID p_proxy, RID p_base) override;
Ref<Image> texture_2d_placeholder;
Vector<Ref<Image>> texture_2d_array_placeholder;
Vector<Ref<Image>> cubemap_placeholder;
Vector<Ref<Image>> texture_3d_placeholder;
//these two APIs can be used together or in combination with the others.
virtual void texture_2d_placeholder_initialize(RID p_texture) override;
virtual void texture_2d_layered_placeholder_initialize(RID p_texture, RenderingServer::TextureLayeredType p_layered_type) override;
virtual void texture_3d_placeholder_initialize(RID p_texture) override;
virtual Ref<Image> texture_2d_get(RID p_texture) const override;
virtual Ref<Image> texture_2d_layer_get(RID p_texture, int p_layer) const override;
virtual Vector<Ref<Image>> texture_3d_get(RID p_texture) const override;
virtual void texture_replace(RID p_texture, RID p_by_texture) override;
virtual void texture_set_size_override(RID p_texture, int p_width, int p_height) override;
virtual void texture_set_path(RID p_texture, const String &p_path) override;
virtual String texture_get_path(RID p_texture) const override;
virtual void texture_set_detect_3d_callback(RID p_texture, RS::TextureDetectCallback p_callback, void *p_userdata) override;
void texture_set_detect_srgb_callback(RID p_texture, RS::TextureDetectCallback p_callback, void *p_userdata);
virtual void texture_set_detect_normal_callback(RID p_texture, RS::TextureDetectCallback p_callback, void *p_userdata) override;
virtual void texture_set_detect_roughness_callback(RID p_texture, RS::TextureDetectRoughnessCallback p_callback, void *p_userdata) override;
virtual void texture_debug_usage(List<RS::TextureInfo> *r_info) override;
virtual void texture_set_force_redraw_if_visible(RID p_texture, bool p_enable) override;
virtual Size2 texture_size_with_proxy(RID p_proxy) override;
virtual void texture_rd_initialize(RID p_texture, const RID &p_rd_texture, const RS::TextureLayeredType p_layer_type = RS::TEXTURE_LAYERED_2D_ARRAY) override;
virtual RID texture_get_rd_texture(RID p_texture, bool p_srgb = false) const override;
virtual uint64_t texture_get_native_handle(RID p_texture, bool p_srgb = false) const override;
void texture_set_data(RID p_texture, const Ref<Image> &p_image, int p_layer = 0);
virtual Image::Format texture_get_format(RID p_texture) const override;
uint32_t texture_get_texid(RID p_texture) const;
Vector3i texture_get_size(RID p_texture) const;
uint32_t texture_get_width(RID p_texture) const;
uint32_t texture_get_height(RID p_texture) const;
uint32_t texture_get_depth(RID p_texture) const;
void texture_bind(RID p_texture, uint32_t p_texture_no);
/* TEXTURE ATLAS API */
void update_texture_atlas();
GLuint texture_atlas_get_texture() const;
_FORCE_INLINE_ Rect2 texture_atlas_get_texture_rect(RID p_texture) {
TextureAtlas::Texture *t = texture_atlas.textures.getptr(p_texture);
if (!t) {
return Rect2();
}
return t->uv_rect;
}
void texture_add_to_texture_atlas(RID p_texture);
void texture_remove_from_texture_atlas(RID p_texture);
void texture_atlas_mark_dirty_on_texture(RID p_texture);
void texture_atlas_remove_texture(RID p_texture);
/* DECAL API */
virtual RID decal_allocate() override;
virtual void decal_initialize(RID p_rid) override;
virtual void decal_free(RID p_rid) override {}
virtual void decal_set_size(RID p_decal, const Vector3 &p_size) override;
virtual void decal_set_texture(RID p_decal, RS::DecalTexture p_type, RID p_texture) override;
virtual void decal_set_emission_energy(RID p_decal, float p_energy) override;
virtual void decal_set_albedo_mix(RID p_decal, float p_mix) override;
virtual void decal_set_modulate(RID p_decal, const Color &p_modulate) override;
virtual void decal_set_cull_mask(RID p_decal, uint32_t p_layers) override;
virtual void decal_set_distance_fade(RID p_decal, bool p_enabled, float p_begin, float p_length) override;
virtual void decal_set_fade(RID p_decal, float p_above, float p_below) override;
virtual void decal_set_normal_fade(RID p_decal, float p_fade) override;
virtual AABB decal_get_aabb(RID p_decal) const override;
virtual uint32_t decal_get_cull_mask(RID p_decal) const override { return 0; }
virtual void texture_add_to_decal_atlas(RID p_texture, bool p_panorama_to_dp = false) override {}
virtual void texture_remove_from_decal_atlas(RID p_texture, bool p_panorama_to_dp = false) override {}
/* DECAL INSTANCE */
virtual RID decal_instance_create(RID p_decal) override { return RID(); }
virtual void decal_instance_free(RID p_decal_instance) override {}
virtual void decal_instance_set_transform(RID p_decal, const Transform3D &p_transform) override {}
virtual void decal_instance_set_sorting_offset(RID p_decal_instance, float p_sorting_offset) override {}
/* RENDER TARGET API */
static GLuint system_fbo;
RenderTarget *get_render_target(RID p_rid) { return render_target_owner.get_or_null(p_rid); }
bool owns_render_target(RID p_rid) { return render_target_owner.owns(p_rid); }
void check_backbuffer(RenderTarget *rt, const bool uses_screen_texture, const bool uses_depth_texture);
virtual RID render_target_create() override;
virtual void render_target_free(RID p_rid) override;
virtual void render_target_set_position(RID p_render_target, int p_x, int p_y) override;
virtual Point2i render_target_get_position(RID p_render_target) const override;
virtual void render_target_set_size(RID p_render_target, int p_width, int p_height, uint32_t p_view_count) override;
virtual Size2i render_target_get_size(RID p_render_target) const override;
virtual void render_target_set_transparent(RID p_render_target, bool p_is_transparent) override;
virtual bool render_target_get_transparent(RID p_render_target) const override;
virtual void render_target_set_direct_to_screen(RID p_render_target, bool p_direct_to_screen) override;
virtual bool render_target_get_direct_to_screen(RID p_render_target) const override;
virtual bool render_target_was_used(RID p_render_target) const override;
void render_target_clear_used(RID p_render_target);
virtual void render_target_set_msaa(RID p_render_target, RS::ViewportMSAA p_msaa) override;
virtual RS::ViewportMSAA render_target_get_msaa(RID p_render_target) const override;
virtual void render_target_set_msaa_needs_resolve(RID p_render_target, bool p_needs_resolve) override {}
virtual bool render_target_get_msaa_needs_resolve(RID p_render_target) const override { return false; }
virtual void render_target_do_msaa_resolve(RID p_render_target) override {}
virtual void render_target_set_use_hdr(RID p_render_target, bool p_use_hdr_2d) override;
virtual bool render_target_is_using_hdr(RID p_render_target) const override;
virtual void render_target_set_use_debanding(RID p_render_target, bool p_use_debanding) override {}
virtual bool render_target_is_using_debanding(RID p_render_target) const override { return false; }
// new
void render_target_set_as_unused(RID p_render_target) override {
render_target_clear_used(p_render_target);
}
GLuint render_target_get_color_internal_format(RID p_render_target) const;
GLuint render_target_get_color_format(RID p_render_target) const;
GLuint render_target_get_color_type(RID p_render_target) const;
uint32_t render_target_get_color_format_size(RID p_render_target) const;
void render_target_request_clear(RID p_render_target, const Color &p_clear_color) override;
bool render_target_is_clear_requested(RID p_render_target) override;
Color render_target_get_clear_request_color(RID p_render_target) override;
void render_target_disable_clear_request(RID p_render_target) override;
void render_target_do_clear_request(RID p_render_target) override;
GLuint render_target_get_fbo(RID p_render_target) const;
GLuint render_target_get_color(RID p_render_target) const;
GLuint render_target_get_depth(RID p_render_target) const;
bool render_target_get_depth_has_stencil(RID p_render_target) const;
void render_target_set_reattach_textures(RID p_render_target, bool p_reattach_textures) const;
bool render_target_is_reattach_textures(RID p_render_target) const;
virtual void render_target_set_sdf_size_and_scale(RID p_render_target, RS::ViewportSDFOversize p_size, RS::ViewportSDFScale p_scale) override;
virtual Rect2i render_target_get_sdf_rect(RID p_render_target) const override;
GLuint render_target_get_sdf_texture(RID p_render_target);
GLuint render_target_get_sdf_framebuffer(RID p_render_target);
void render_target_sdf_process(RID p_render_target);
virtual void render_target_mark_sdf_enabled(RID p_render_target, bool p_enabled) override;
bool render_target_is_sdf_enabled(RID p_render_target) const;
void render_target_copy_to_back_buffer(RID p_render_target, const Rect2i &p_region, bool p_gen_mipmaps);
void render_target_clear_back_buffer(RID p_render_target, const Rect2i &p_region, const Color &p_color);
void render_target_gen_back_buffer_mipmaps(RID p_render_target, const Rect2i &p_region);
virtual void render_target_set_vrs_mode(RID p_render_target, RS::ViewportVRSMode p_mode) override {}
virtual RS::ViewportVRSMode render_target_get_vrs_mode(RID p_render_target) const override { return RS::VIEWPORT_VRS_DISABLED; }
virtual void render_target_set_vrs_update_mode(RID p_render_target, RS::ViewportVRSUpdateMode p_mode) override {}
virtual RS::ViewportVRSUpdateMode render_target_get_vrs_update_mode(RID p_render_target) const override { return RS::VIEWPORT_VRS_UPDATE_DISABLED; }
virtual void render_target_set_vrs_texture(RID p_render_target, RID p_texture) override {}
virtual RID render_target_get_vrs_texture(RID p_render_target) const override { return RID(); }
virtual void render_target_set_override(RID p_render_target, RID p_color_texture, RID p_depth_texture, RID p_velocity_texture, RID p_velocity_depth_texture) override;
virtual RID render_target_get_override_color(RID p_render_target) const override;
virtual RID render_target_get_override_depth(RID p_render_target) const override;
virtual RID render_target_get_override_velocity(RID p_render_target) const override;
virtual RID render_target_get_override_velocity_depth(RID p_render_target) const override { return RID(); }
virtual void render_target_set_render_region(RID p_render_target, const Rect2i &p_render_region) override;
virtual Rect2i render_target_get_render_region(RID p_render_target) const override;
virtual RID render_target_get_texture(RID p_render_target) override;
virtual void render_target_set_velocity_target_size(RID p_render_target, const Size2i &p_target_size) override {}
virtual Size2i render_target_get_velocity_target_size(RID p_render_target) const override { return Size2i(); }
void bind_framebuffer(GLuint framebuffer) {
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
}
void bind_framebuffer_system() {
glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
}
String get_framebuffer_error(GLenum p_status);
};
inline String TextureStorage::get_framebuffer_error(GLenum p_status) {
#if defined(DEBUG_ENABLED) && defined(GL_API_ENABLED)
if (p_status == GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT) {
return "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
} else if (p_status == GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT) {
return "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
} else if (p_status == GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER) {
return "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER";
} else if (p_status == GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER) {
return "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER";
}
#endif
return itos(p_status);
}
} // namespace GLES3
#endif // GLES3_ENABLED

View File

@@ -0,0 +1,483 @@
/**************************************************************************/
/* utilities.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 "utilities.h"
#include "../rasterizer_gles3.h"
#include "config.h"
#include "light_storage.h"
#include "material_storage.h"
#include "mesh_storage.h"
#include "particles_storage.h"
#include "texture_storage.h"
using namespace GLES3;
Utilities *Utilities::singleton = nullptr;
Utilities::Utilities() {
singleton = this;
frame = 0;
for (int i = 0; i < FRAME_COUNT; i++) {
frames[i].index = 0;
glGenQueries(max_timestamp_query_elements, frames[i].queries);
frames[i].timestamp_names.resize(max_timestamp_query_elements);
frames[i].timestamp_cpu_values.resize(max_timestamp_query_elements);
frames[i].timestamp_count = 0;
frames[i].timestamp_result_names.resize(max_timestamp_query_elements);
frames[i].timestamp_cpu_result_values.resize(max_timestamp_query_elements);
frames[i].timestamp_result_values.resize(max_timestamp_query_elements);
frames[i].timestamp_result_count = 0;
}
}
Utilities::~Utilities() {
singleton = nullptr;
for (int i = 0; i < FRAME_COUNT; i++) {
glDeleteQueries(max_timestamp_query_elements, frames[i].queries);
}
if (texture_mem_cache) {
uint32_t leaked_data_size = 0;
for (const KeyValue<GLuint, ResourceAllocation> &E : texture_allocs_cache) {
#ifdef DEV_ENABLED
ERR_PRINT(E.value.name + ": leaked " + itos(E.value.size) + " bytes.");
#else
ERR_PRINT("Texture with GL ID of " + itos(E.key) + ": leaked " + itos(E.value.size) + " bytes.");
#endif
leaked_data_size += E.value.size;
}
if (leaked_data_size < texture_mem_cache) {
ERR_PRINT("Texture cache is not empty. There may be an additional texture leak of " + itos(texture_mem_cache - leaked_data_size) + " bytes.");
}
}
if (render_buffer_mem_cache) {
uint32_t leaked_data_size = 0;
for (const KeyValue<GLuint, ResourceAllocation> &E : render_buffer_allocs_cache) {
#ifdef DEV_ENABLED
ERR_PRINT(E.value.name + ": leaked " + itos(E.value.size) + " bytes.");
#else
ERR_PRINT("Render buffer with GL ID of " + itos(E.key) + ": leaked " + itos(E.value.size) + " bytes.");
#endif
leaked_data_size += E.value.size;
}
if (leaked_data_size < render_buffer_mem_cache) {
ERR_PRINT("Render buffer cache is not empty. There may be an additional render buffer leak of " + itos(render_buffer_mem_cache - leaked_data_size) + " bytes.");
}
}
if (buffer_mem_cache) {
uint32_t leaked_data_size = 0;
for (const KeyValue<GLuint, ResourceAllocation> &E : buffer_allocs_cache) {
#ifdef DEV_ENABLED
ERR_PRINT(E.value.name + ": leaked " + itos(E.value.size) + " bytes.");
#else
ERR_PRINT("Buffer with GL ID of " + itos(E.key) + ": leaked " + itos(E.value.size) + " bytes.");
#endif
leaked_data_size += E.value.size;
}
if (leaked_data_size < buffer_mem_cache) {
ERR_PRINT("Buffer cache is not empty. There may be an additional buffer leak of " + itos(buffer_mem_cache - leaked_data_size) + " bytes.");
}
}
}
Vector<uint8_t> Utilities::buffer_get_data(GLenum p_target, GLuint p_buffer, uint32_t p_buffer_size) {
Vector<uint8_t> ret;
if (p_buffer_size == 0) {
return ret;
}
ret.resize(p_buffer_size);
glBindBuffer(p_target, p_buffer);
#if defined(__EMSCRIPTEN__)
{
uint8_t *w = ret.ptrw();
godot_webgl2_glGetBufferSubData(p_target, 0, p_buffer_size, w);
}
#else
void *data = glMapBufferRange(p_target, 0, p_buffer_size, GL_MAP_READ_BIT);
ERR_FAIL_NULL_V(data, Vector<uint8_t>());
{
uint8_t *w = ret.ptrw();
memcpy(w, data, p_buffer_size);
}
glUnmapBuffer(p_target);
#endif
glBindBuffer(p_target, 0);
return ret;
}
/* INSTANCES */
RS::InstanceType Utilities::get_base_type(RID p_rid) const {
if (GLES3::MeshStorage::get_singleton()->owns_mesh(p_rid)) {
return RS::INSTANCE_MESH;
} else if (GLES3::MeshStorage::get_singleton()->owns_multimesh(p_rid)) {
return RS::INSTANCE_MULTIMESH;
} else if (GLES3::LightStorage::get_singleton()->owns_light(p_rid)) {
return RS::INSTANCE_LIGHT;
} else if (GLES3::LightStorage::get_singleton()->owns_lightmap(p_rid)) {
return RS::INSTANCE_LIGHTMAP;
} else if (GLES3::ParticlesStorage::get_singleton()->owns_particles(p_rid)) {
return RS::INSTANCE_PARTICLES;
} else if (GLES3::LightStorage::get_singleton()->owns_reflection_probe(p_rid)) {
return RS::INSTANCE_REFLECTION_PROBE;
} else if (GLES3::ParticlesStorage::get_singleton()->owns_particles_collision(p_rid)) {
return RS::INSTANCE_PARTICLES_COLLISION;
} else if (owns_visibility_notifier(p_rid)) {
return RS::INSTANCE_VISIBLITY_NOTIFIER;
}
return RS::INSTANCE_NONE;
}
bool Utilities::free(RID p_rid) {
if (GLES3::TextureStorage::get_singleton()->owns_render_target(p_rid)) {
GLES3::TextureStorage::get_singleton()->render_target_free(p_rid);
return true;
} else if (GLES3::TextureStorage::get_singleton()->owns_texture(p_rid)) {
GLES3::TextureStorage::get_singleton()->texture_free(p_rid);
return true;
} else if (GLES3::TextureStorage::get_singleton()->owns_canvas_texture(p_rid)) {
GLES3::TextureStorage::get_singleton()->canvas_texture_free(p_rid);
return true;
} else if (GLES3::MaterialStorage::get_singleton()->owns_shader(p_rid)) {
GLES3::MaterialStorage::get_singleton()->shader_free(p_rid);
return true;
} else if (GLES3::MaterialStorage::get_singleton()->owns_material(p_rid)) {
GLES3::MaterialStorage::get_singleton()->material_free(p_rid);
return true;
} else if (GLES3::MeshStorage::get_singleton()->owns_mesh(p_rid)) {
GLES3::MeshStorage::get_singleton()->mesh_free(p_rid);
return true;
} else if (GLES3::MeshStorage::get_singleton()->owns_multimesh(p_rid)) {
GLES3::MeshStorage::get_singleton()->multimesh_free(p_rid);
return true;
} else if (GLES3::MeshStorage::get_singleton()->owns_mesh_instance(p_rid)) {
GLES3::MeshStorage::get_singleton()->mesh_instance_free(p_rid);
return true;
} else if (GLES3::LightStorage::get_singleton()->owns_light(p_rid)) {
GLES3::LightStorage::get_singleton()->light_free(p_rid);
return true;
} else if (GLES3::LightStorage::get_singleton()->owns_lightmap(p_rid)) {
GLES3::LightStorage::get_singleton()->lightmap_free(p_rid);
return true;
} else if (GLES3::LightStorage::get_singleton()->owns_reflection_probe(p_rid)) {
GLES3::LightStorage::get_singleton()->reflection_probe_free(p_rid);
return true;
} else if (GLES3::LightStorage::get_singleton()->owns_reflection_atlas(p_rid)) {
GLES3::LightStorage::get_singleton()->reflection_atlas_free(p_rid);
return true;
} else if (GLES3::LightStorage::get_singleton()->owns_reflection_probe_instance(p_rid)) {
GLES3::LightStorage::get_singleton()->reflection_probe_instance_free(p_rid);
return true;
} else if (GLES3::ParticlesStorage::get_singleton()->owns_particles(p_rid)) {
GLES3::ParticlesStorage::get_singleton()->particles_free(p_rid);
return true;
} else if (GLES3::ParticlesStorage::get_singleton()->owns_particles_collision(p_rid)) {
GLES3::ParticlesStorage::get_singleton()->particles_collision_free(p_rid);
return true;
} else if (GLES3::ParticlesStorage::get_singleton()->owns_particles_collision_instance(p_rid)) {
GLES3::ParticlesStorage::get_singleton()->particles_collision_instance_free(p_rid);
return true;
} else if (GLES3::MeshStorage::get_singleton()->owns_skeleton(p_rid)) {
GLES3::MeshStorage::get_singleton()->skeleton_free(p_rid);
return true;
} else if (owns_visibility_notifier(p_rid)) {
visibility_notifier_free(p_rid);
return true;
} else {
return false;
}
}
/* DEPENDENCIES */
void Utilities::base_update_dependency(RID p_base, DependencyTracker *p_instance) {
if (MeshStorage::get_singleton()->owns_mesh(p_base)) {
Mesh *mesh = MeshStorage::get_singleton()->get_mesh(p_base);
p_instance->update_dependency(&mesh->dependency);
} else if (MeshStorage::get_singleton()->owns_multimesh(p_base)) {
MultiMesh *multimesh = MeshStorage::get_singleton()->get_multimesh(p_base);
p_instance->update_dependency(&multimesh->dependency);
if (multimesh->mesh.is_valid()) {
base_update_dependency(multimesh->mesh, p_instance);
}
} else if (LightStorage::get_singleton()->owns_reflection_probe(p_base)) {
Dependency *dependency = LightStorage::get_singleton()->reflection_probe_get_dependency(p_base);
p_instance->update_dependency(dependency);
} else if (LightStorage::get_singleton()->owns_light(p_base)) {
Light *l = LightStorage::get_singleton()->get_light(p_base);
p_instance->update_dependency(&l->dependency);
} else if (ParticlesStorage::get_singleton()->owns_particles(p_base)) {
Dependency *dependency = ParticlesStorage::get_singleton()->particles_get_dependency(p_base);
p_instance->update_dependency(dependency);
} else if (ParticlesStorage::get_singleton()->owns_particles_collision(p_base)) {
Dependency *dependency = ParticlesStorage::get_singleton()->particles_collision_get_dependency(p_base);
p_instance->update_dependency(dependency);
} else if (owns_visibility_notifier(p_base)) {
VisibilityNotifier *vn = get_visibility_notifier(p_base);
p_instance->update_dependency(&vn->dependency);
}
}
/* VISIBILITY NOTIFIER */
RID Utilities::visibility_notifier_allocate() {
return visibility_notifier_owner.allocate_rid();
}
void Utilities::visibility_notifier_initialize(RID p_notifier) {
visibility_notifier_owner.initialize_rid(p_notifier, VisibilityNotifier());
}
void Utilities::visibility_notifier_free(RID p_notifier) {
VisibilityNotifier *vn = visibility_notifier_owner.get_or_null(p_notifier);
vn->dependency.deleted_notify(p_notifier);
visibility_notifier_owner.free(p_notifier);
}
void Utilities::visibility_notifier_set_aabb(RID p_notifier, const AABB &p_aabb) {
VisibilityNotifier *vn = visibility_notifier_owner.get_or_null(p_notifier);
ERR_FAIL_NULL(vn);
vn->aabb = p_aabb;
vn->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB);
}
void Utilities::visibility_notifier_set_callbacks(RID p_notifier, const Callable &p_enter_callbable, const Callable &p_exit_callable) {
VisibilityNotifier *vn = visibility_notifier_owner.get_or_null(p_notifier);
ERR_FAIL_NULL(vn);
vn->enter_callback = p_enter_callbable;
vn->exit_callback = p_exit_callable;
}
AABB Utilities::visibility_notifier_get_aabb(RID p_notifier) const {
const VisibilityNotifier *vn = visibility_notifier_owner.get_or_null(p_notifier);
ERR_FAIL_NULL_V(vn, AABB());
return vn->aabb;
}
void Utilities::visibility_notifier_call(RID p_notifier, bool p_enter, bool p_deferred) {
VisibilityNotifier *vn = visibility_notifier_owner.get_or_null(p_notifier);
ERR_FAIL_NULL(vn);
if (p_enter) {
if (vn->enter_callback.is_valid()) {
if (p_deferred) {
vn->enter_callback.call_deferred();
} else {
vn->enter_callback.call();
}
}
} else {
if (vn->exit_callback.is_valid()) {
if (p_deferred) {
vn->exit_callback.call_deferred();
} else {
vn->exit_callback.call();
}
}
}
}
/* TIMING */
void Utilities::capture_timestamps_begin() {
capture_timestamp("Frame Begin");
}
void Utilities::capture_timestamp(const String &p_name) {
ERR_FAIL_COND(frames[frame].timestamp_count >= max_timestamp_query_elements);
#ifdef GL_API_ENABLED
if (RasterizerGLES3::is_gles_over_gl()) {
glQueryCounter(frames[frame].queries[frames[frame].timestamp_count], GL_TIMESTAMP);
}
#endif // GL_API_ENABLED
frames[frame].timestamp_names[frames[frame].timestamp_count] = p_name;
frames[frame].timestamp_cpu_values[frames[frame].timestamp_count] = OS::get_singleton()->get_ticks_usec();
frames[frame].timestamp_count++;
}
void Utilities::_capture_timestamps_begin() {
// frame is incremented at the end of the frame so this gives us the queries for frame - 2. By then they should be ready.
if (frames[frame].timestamp_count) {
#ifdef GL_API_ENABLED
if (RasterizerGLES3::is_gles_over_gl()) {
for (uint32_t i = 0; i < frames[frame].timestamp_count; i++) {
uint64_t temp = 0;
glGetQueryObjectui64v(frames[frame].queries[i], GL_QUERY_RESULT, &temp);
frames[frame].timestamp_result_values[i] = temp;
}
}
#endif // GL_API_ENABLED
SWAP(frames[frame].timestamp_names, frames[frame].timestamp_result_names);
SWAP(frames[frame].timestamp_cpu_values, frames[frame].timestamp_cpu_result_values);
}
frames[frame].timestamp_result_count = frames[frame].timestamp_count;
frames[frame].timestamp_count = 0;
frames[frame].index = Engine::get_singleton()->get_frames_drawn();
capture_timestamp("Internal Begin");
}
void Utilities::capture_timestamps_end() {
capture_timestamp("Internal End");
frame = (frame + 1) % FRAME_COUNT;
}
uint32_t Utilities::get_captured_timestamps_count() const {
return frames[frame].timestamp_result_count;
}
uint64_t Utilities::get_captured_timestamps_frame() const {
return frames[frame].index;
}
uint64_t Utilities::get_captured_timestamp_gpu_time(uint32_t p_index) const {
ERR_FAIL_UNSIGNED_INDEX_V(p_index, frames[frame].timestamp_result_count, 0);
return frames[frame].timestamp_result_values[p_index];
}
uint64_t Utilities::get_captured_timestamp_cpu_time(uint32_t p_index) const {
ERR_FAIL_UNSIGNED_INDEX_V(p_index, frames[frame].timestamp_result_count, 0);
return frames[frame].timestamp_cpu_result_values[p_index];
}
String Utilities::get_captured_timestamp_name(uint32_t p_index) const {
ERR_FAIL_UNSIGNED_INDEX_V(p_index, frames[frame].timestamp_result_count, String());
return frames[frame].timestamp_result_names[p_index];
}
/* MISC */
void Utilities::update_dirty_resources() {
MaterialStorage::get_singleton()->_update_global_shader_uniforms();
MaterialStorage::get_singleton()->_update_queued_materials();
MeshStorage::get_singleton()->_update_dirty_skeletons();
MeshStorage::get_singleton()->_update_dirty_multimeshes();
TextureStorage::get_singleton()->update_texture_atlas();
}
void Utilities::set_debug_generate_wireframes(bool p_generate) {
Config *config = Config::get_singleton();
config->generate_wireframes = p_generate;
}
bool Utilities::has_os_feature(const String &p_feature) const {
Config *config = Config::get_singleton();
if (!config) {
return false;
}
if (p_feature == "rgtc") {
return config->rgtc_supported;
}
if (p_feature == "s3tc") {
return config->s3tc_supported;
}
if (p_feature == "bptc") {
return config->bptc_supported;
}
if (p_feature == "astc") {
return config->astc_supported;
}
if (p_feature == "etc2") {
return config->etc2_supported;
}
if (p_feature == "astc_hdr") {
return config->astc_hdr_supported;
}
return false;
}
void Utilities::update_memory_info() {
}
uint64_t Utilities::get_rendering_info(RS::RenderingInfo p_info) {
if (p_info == RS::RENDERING_INFO_TEXTURE_MEM_USED) {
return texture_mem_cache + render_buffer_mem_cache; // Add render buffer memory to our texture mem.
} else if (p_info == RS::RENDERING_INFO_BUFFER_MEM_USED) {
return buffer_mem_cache;
} else if (p_info == RS::RENDERING_INFO_VIDEO_MEM_USED) {
return texture_mem_cache + buffer_mem_cache + render_buffer_mem_cache;
}
return 0;
}
String Utilities::get_video_adapter_name() const {
const String rendering_device_name = String::utf8((const char *)glGetString(GL_RENDERER));
// NVIDIA suffixes all GPU model names with "/PCIe/SSE2" in OpenGL (but not Vulkan). This isn't necessary to display nowadays, so it can be trimmed.
return rendering_device_name.trim_suffix("/PCIe/SSE2");
}
String Utilities::get_video_adapter_vendor() const {
const String rendering_device_vendor = String::utf8((const char *)glGetString(GL_VENDOR));
// NVIDIA suffixes its vendor name with " Corporation". This is neither necessary to process nor display.
return rendering_device_vendor.trim_suffix(" Corporation");
}
RenderingDevice::DeviceType Utilities::get_video_adapter_type() const {
return RenderingDevice::DeviceType::DEVICE_TYPE_OTHER;
}
String Utilities::get_video_adapter_api_version() const {
return String::utf8((const char *)glGetString(GL_VERSION));
}
Size2i Utilities::get_maximum_viewport_size() const {
Config *config = Config::get_singleton();
ERR_FAIL_NULL_V(config, Size2i());
return Size2i(config->max_viewport_size[0], config->max_viewport_size[1]);
}
uint32_t Utilities::get_maximum_shader_varyings() const {
Config *config = Config::get_singleton();
ERR_FAIL_NULL_V(config, 31);
return config->max_shader_varyings;
}
uint64_t Utilities::get_maximum_uniform_buffer_size() const {
Config *config = Config::get_singleton();
ERR_FAIL_NULL_V(config, 65536);
return uint64_t(config->max_uniform_buffer_size);
}
#endif // GLES3_ENABLED

View File

@@ -0,0 +1,234 @@
/**************************************************************************/
/* utilities.h */
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#ifdef GLES3_ENABLED
#include "servers/rendering/storage/utilities.h"
#include "platform_gl.h"
namespace GLES3 {
/* VISIBILITY NOTIFIER */
struct VisibilityNotifier {
AABB aabb;
Callable enter_callback;
Callable exit_callback;
Dependency dependency;
};
class Utilities : public RendererUtilities {
private:
static Utilities *singleton;
/* VISIBILITY NOTIFIER */
mutable RID_Owner<VisibilityNotifier> visibility_notifier_owner;
/* MISC */
struct ResourceAllocation {
#ifdef DEV_ENABLED
String name;
#endif
uint32_t size = 0;
};
HashMap<GLuint, ResourceAllocation> buffer_allocs_cache;
HashMap<GLuint, ResourceAllocation> render_buffer_allocs_cache;
HashMap<GLuint, ResourceAllocation> texture_allocs_cache;
uint64_t buffer_mem_cache = 0;
uint64_t render_buffer_mem_cache = 0;
uint64_t texture_mem_cache = 0;
public:
static Utilities *get_singleton() { return singleton; }
Utilities();
~Utilities();
// Buffer size is specified in bytes
static Vector<uint8_t> buffer_get_data(GLenum p_target, GLuint p_buffer, uint32_t p_buffer_size);
// Allocate memory with glBufferData. Does not handle resizing.
_FORCE_INLINE_ void buffer_allocate_data(GLenum p_target, GLuint p_id, uint32_t p_size, const void *p_data, GLenum p_usage, String p_name = "") {
glBufferData(p_target, p_size, p_data, p_usage);
buffer_mem_cache += p_size;
#ifdef DEV_ENABLED
ERR_FAIL_COND_MSG(buffer_allocs_cache.has(p_id), "trying to allocate buffer with name " + p_name + " but ID already used by " + buffer_allocs_cache[p_id].name);
#endif
ResourceAllocation resource_allocation;
resource_allocation.size = p_size;
#ifdef DEV_ENABLED
resource_allocation.name = p_name + ": " + itos((uint64_t)p_id);
#endif
buffer_allocs_cache[p_id] = resource_allocation;
}
_FORCE_INLINE_ void buffer_free_data(GLuint p_id) {
ERR_FAIL_COND(!buffer_allocs_cache.has(p_id));
glDeleteBuffers(1, &p_id);
buffer_mem_cache -= buffer_allocs_cache[p_id].size;
buffer_allocs_cache.erase(p_id);
}
_FORCE_INLINE_ void render_buffer_allocated_data(GLuint p_id, uint32_t p_size, String p_name = "") {
render_buffer_mem_cache += p_size;
#ifdef DEV_ENABLED
ERR_FAIL_COND_MSG(render_buffer_allocs_cache.has(p_id), "trying to allocate render buffer with name " + p_name + " but ID already used by " + render_buffer_allocs_cache[p_id].name);
#endif
ResourceAllocation resource_allocation;
resource_allocation.size = p_size;
#ifdef DEV_ENABLED
resource_allocation.name = p_name + ": " + itos((uint64_t)p_id);
#endif
render_buffer_allocs_cache[p_id] = resource_allocation;
}
_FORCE_INLINE_ void render_buffer_free_data(GLuint p_id) {
ERR_FAIL_COND(!render_buffer_allocs_cache.has(p_id));
glDeleteRenderbuffers(1, &p_id);
render_buffer_mem_cache -= render_buffer_allocs_cache[p_id].size;
render_buffer_allocs_cache.erase(p_id);
}
// Records that data was allocated for state tracking purposes.
// Size is measured in bytes.
_FORCE_INLINE_ void texture_allocated_data(GLuint p_id, uint32_t p_size, String p_name = "") {
texture_mem_cache += p_size;
#ifdef DEV_ENABLED
ERR_FAIL_COND_MSG(texture_allocs_cache.has(p_id), "trying to allocate texture with name " + p_name + " but ID already used by " + texture_allocs_cache[p_id].name);
#endif
ResourceAllocation resource_allocation;
resource_allocation.size = p_size;
#ifdef DEV_ENABLED
resource_allocation.name = p_name + ": " + itos((uint64_t)p_id);
#endif
texture_allocs_cache[p_id] = resource_allocation;
}
_FORCE_INLINE_ void texture_free_data(GLuint p_id) {
ERR_FAIL_COND(!texture_allocs_cache.has(p_id));
glDeleteTextures(1, &p_id);
texture_mem_cache -= texture_allocs_cache[p_id].size;
texture_allocs_cache.erase(p_id);
}
_FORCE_INLINE_ void texture_resize_data(GLuint p_id, uint32_t p_size) {
ERR_FAIL_COND(!texture_allocs_cache.has(p_id));
texture_mem_cache -= texture_allocs_cache[p_id].size;
texture_mem_cache += p_size;
texture_allocs_cache[p_id].size = p_size;
}
/* INSTANCES */
virtual RS::InstanceType get_base_type(RID p_rid) const override;
virtual bool free(RID p_rid) override;
/* DEPENDENCIES */
virtual void base_update_dependency(RID p_base, DependencyTracker *p_instance) override;
/* VISIBILITY NOTIFIER */
VisibilityNotifier *get_visibility_notifier(RID p_rid) { return visibility_notifier_owner.get_or_null(p_rid); }
bool owns_visibility_notifier(RID p_rid) const { return visibility_notifier_owner.owns(p_rid); }
virtual RID visibility_notifier_allocate() override;
virtual void visibility_notifier_initialize(RID p_notifier) override;
virtual void visibility_notifier_free(RID p_notifier) override;
virtual void visibility_notifier_set_aabb(RID p_notifier, const AABB &p_aabb) override;
virtual void visibility_notifier_set_callbacks(RID p_notifier, const Callable &p_enter_callbable, const Callable &p_exit_callable) override;
virtual AABB visibility_notifier_get_aabb(RID p_notifier) const override;
virtual void visibility_notifier_call(RID p_notifier, bool p_enter, bool p_deferred) override;
/* TIMING */
#define MAX_QUERIES 256
#define FRAME_COUNT 3
struct Frame {
GLuint queries[MAX_QUERIES];
TightLocalVector<String> timestamp_names;
TightLocalVector<uint64_t> timestamp_cpu_values;
uint32_t timestamp_count = 0;
TightLocalVector<String> timestamp_result_names;
TightLocalVector<uint64_t> timestamp_cpu_result_values;
TightLocalVector<uint64_t> timestamp_result_values;
uint32_t timestamp_result_count = 0;
uint64_t index = 0;
};
const uint32_t max_timestamp_query_elements = MAX_QUERIES;
Frame frames[FRAME_COUNT]; // Frames for capturing timestamps. We use 3 so we don't need to wait for commands to complete
uint32_t frame = 0;
virtual void capture_timestamps_begin() override;
virtual void capture_timestamp(const String &p_name) override;
virtual uint32_t get_captured_timestamps_count() const override;
virtual uint64_t get_captured_timestamps_frame() const override;
virtual uint64_t get_captured_timestamp_gpu_time(uint32_t p_index) const override;
virtual uint64_t get_captured_timestamp_cpu_time(uint32_t p_index) const override;
virtual String get_captured_timestamp_name(uint32_t p_index) const override;
void _capture_timestamps_begin();
void capture_timestamps_end();
/* MISC */
virtual void update_dirty_resources() override;
virtual void set_debug_generate_wireframes(bool p_generate) override;
virtual bool has_os_feature(const String &p_feature) const override;
virtual void update_memory_info() override;
virtual uint64_t get_rendering_info(RS::RenderingInfo p_info) override;
virtual String get_video_adapter_name() const override;
virtual String get_video_adapter_vendor() const override;
virtual RenderingDevice::DeviceType get_video_adapter_type() const override;
virtual String get_video_adapter_api_version() const override;
virtual Size2i get_maximum_viewport_size() const override;
virtual uint32_t get_maximum_shader_varyings() const override;
virtual uint64_t get_maximum_uniform_buffer_size() const override;
};
} // namespace GLES3
#endif // GLES3_ENABLED