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
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:
11
drivers/gles3/SCsub
Normal file
11
drivers/gles3/SCsub
Normal 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")
|
||||
6
drivers/gles3/effects/SCsub
Normal file
6
drivers/gles3/effects/SCsub
Normal 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")
|
||||
335
drivers/gles3/effects/copy_effects.cpp
Normal file
335
drivers/gles3/effects/copy_effects.cpp
Normal 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
|
||||
79
drivers/gles3/effects/copy_effects.h
Normal file
79
drivers/gles3/effects/copy_effects.h
Normal 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
|
||||
213
drivers/gles3/effects/cubemap_filter.cpp
Normal file
213
drivers/gles3/effects/cubemap_filter.cpp
Normal 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
|
||||
67
drivers/gles3/effects/cubemap_filter.h
Normal file
67
drivers/gles3/effects/cubemap_filter.h
Normal 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
|
||||
128
drivers/gles3/effects/feed_effects.cpp
Normal file
128
drivers/gles3/effects/feed_effects.cpp
Normal 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
|
||||
66
drivers/gles3/effects/feed_effects.h
Normal file
66
drivers/gles3/effects/feed_effects.h
Normal 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
|
||||
173
drivers/gles3/effects/glow.cpp
Normal file
173
drivers/gles3/effects/glow.cpp
Normal 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
|
||||
86
drivers/gles3/effects/glow.h
Normal file
86
drivers/gles3/effects/glow.h
Normal 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
|
||||
153
drivers/gles3/effects/post_effects.cpp
Normal file
153
drivers/gles3/effects/post_effects.cpp
Normal 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
|
||||
66
drivers/gles3/effects/post_effects.h
Normal file
66
drivers/gles3/effects/post_effects.h
Normal 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
|
||||
6
drivers/gles3/environment/SCsub
Normal file
6
drivers/gles3/environment/SCsub
Normal 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")
|
||||
66
drivers/gles3/environment/fog.cpp
Normal file
66
drivers/gles3/environment/fog.cpp
Normal 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
|
||||
56
drivers/gles3/environment/fog.h
Normal file
56
drivers/gles3/environment/fog.h
Normal 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
|
||||
143
drivers/gles3/environment/gi.cpp
Normal file
143
drivers/gles3/environment/gi.cpp
Normal 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
|
||||
88
drivers/gles3/environment/gi.h
Normal file
88
drivers/gles3/environment/gi.h
Normal 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
|
||||
2943
drivers/gles3/rasterizer_canvas_gles3.cpp
Normal file
2943
drivers/gles3/rasterizer_canvas_gles3.cpp
Normal file
File diff suppressed because it is too large
Load Diff
389
drivers/gles3/rasterizer_canvas_gles3.h
Normal file
389
drivers/gles3/rasterizer_canvas_gles3.h
Normal 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 *¤t_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
|
||||
529
drivers/gles3/rasterizer_gles3.cpp
Normal file
529
drivers/gles3/rasterizer_gles3.cpp
Normal 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
|
||||
143
drivers/gles3/rasterizer_gles3.h
Normal file
143
drivers/gles3/rasterizer_gles3.h
Normal 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
|
||||
4499
drivers/gles3/rasterizer_scene_gles3.cpp
Normal file
4499
drivers/gles3/rasterizer_scene_gles3.cpp
Normal file
File diff suppressed because it is too large
Load Diff
948
drivers/gles3/rasterizer_scene_gles3.h
Normal file
948
drivers/gles3/rasterizer_scene_gles3.h
Normal 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
|
||||
842
drivers/gles3/shader_gles3.cpp
Normal file
842
drivers/gles3/shader_gles3.cpp
Normal 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
|
||||
259
drivers/gles3/shader_gles3.h
Normal file
259
drivers/gles3/shader_gles3.h
Normal 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
|
||||
34
drivers/gles3/shaders/SCsub
Normal file
34
drivers/gles3/shaders/SCsub
Normal 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")
|
||||
870
drivers/gles3/shaders/canvas.glsl
Normal file
870
drivers/gles3/shaders/canvas.glsl
Normal 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;
|
||||
}
|
||||
68
drivers/gles3/shaders/canvas_occlusion.glsl
Normal file
68
drivers/gles3/shaders/canvas_occlusion.glsl
Normal 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
|
||||
}
|
||||
205
drivers/gles3/shaders/canvas_sdf.glsl
Normal file
205
drivers/gles3/shaders/canvas_sdf.glsl
Normal 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
|
||||
}
|
||||
87
drivers/gles3/shaders/canvas_uniforms_inc.glsl
Normal file
87
drivers/gles3/shaders/canvas_uniforms_inc.glsl
Normal 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
|
||||
100
drivers/gles3/shaders/cube_to_dp.glsl
Normal file
100
drivers/gles3/shaders/cube_to_dp.glsl
Normal 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;
|
||||
}
|
||||
291
drivers/gles3/shaders/effect_blur.glsl
Normal file
291
drivers/gles3/shaders/effect_blur.glsl
Normal 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
|
||||
}
|
||||
18
drivers/gles3/shaders/effects/SCsub
Normal file
18
drivers/gles3/shaders/effects/SCsub
Normal 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)
|
||||
234
drivers/gles3/shaders/effects/copy.glsl
Normal file
234
drivers/gles3/shaders/effects/copy.glsl
Normal 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
|
||||
}
|
||||
122
drivers/gles3/shaders/effects/cubemap_filter.glsl
Normal file
122
drivers/gles3/shaders/effects/cubemap_filter.glsl
Normal 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
|
||||
}
|
||||
113
drivers/gles3/shaders/effects/glow.glsl
Normal file
113
drivers/gles3/shaders/effects/glow.glsl
Normal 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
|
||||
}
|
||||
130
drivers/gles3/shaders/effects/post.glsl
Normal file
130
drivers/gles3/shaders/effects/post.glsl
Normal 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;
|
||||
}
|
||||
39
drivers/gles3/shaders/feed.glsl
Normal file
39
drivers/gles3/shaders/feed.glsl
Normal 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;
|
||||
}
|
||||
86
drivers/gles3/shaders/lens_distorted.glsl
Normal file
86
drivers/gles3/shaders/lens_distorted.glsl
Normal 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);
|
||||
}
|
||||
}
|
||||
512
drivers/gles3/shaders/particles.glsl
Normal file
512
drivers/gles3/shaders/particles.glsl
Normal 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 */
|
||||
121
drivers/gles3/shaders/particles_copy.glsl
Normal file
121
drivers/gles3/shaders/particles_copy.glsl
Normal 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 */
|
||||
2629
drivers/gles3/shaders/scene.glsl
Normal file
2629
drivers/gles3/shaders/scene.glsl
Normal file
File diff suppressed because it is too large
Load Diff
282
drivers/gles3/shaders/skeleton.glsl
Normal file
282
drivers/gles3/shaders/skeleton.glsl
Normal 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 */
|
||||
273
drivers/gles3/shaders/sky.glsl
Normal file
273
drivers/gles3/shaders/sky.glsl
Normal 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
|
||||
}
|
||||
89
drivers/gles3/shaders/stdlib_inc.glsl
Normal file
89
drivers/gles3/shaders/stdlib_inc.glsl
Normal 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
|
||||
182
drivers/gles3/shaders/tonemap_inc.glsl
Normal file
182
drivers/gles3/shaders/tonemap_inc.glsl
Normal 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
|
||||
6
drivers/gles3/storage/SCsub
Normal file
6
drivers/gles3/storage/SCsub
Normal 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")
|
||||
251
drivers/gles3/storage/config.cpp
Normal file
251
drivers/gles3/storage/config.cpp
Normal 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
|
||||
124
drivers/gles3/storage/config.h
Normal file
124
drivers/gles3/storage/config.h
Normal 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
|
||||
1736
drivers/gles3/storage/light_storage.cpp
Normal file
1736
drivers/gles3/storage/light_storage.cpp
Normal file
File diff suppressed because it is too large
Load Diff
910
drivers/gles3/storage/light_storage.h
Normal file
910
drivers/gles3/storage/light_storage.h
Normal 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
|
||||
3313
drivers/gles3/storage/material_storage.cpp
Normal file
3313
drivers/gles3/storage/material_storage.cpp
Normal file
File diff suppressed because it is too large
Load Diff
663
drivers/gles3/storage/material_storage.h
Normal file
663
drivers/gles3/storage/material_storage.h
Normal 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
|
||||
2441
drivers/gles3/storage/mesh_storage.cpp
Normal file
2441
drivers/gles3/storage/mesh_storage.cpp
Normal file
File diff suppressed because it is too large
Load Diff
615
drivers/gles3/storage/mesh_storage.h
Normal file
615
drivers/gles3/storage/mesh_storage.h
Normal 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
|
||||
1473
drivers/gles3/storage/particles_storage.cpp
Normal file
1473
drivers/gles3/storage/particles_storage.cpp
Normal file
File diff suppressed because it is too large
Load Diff
462
drivers/gles3/storage/particles_storage.h
Normal file
462
drivers/gles3/storage/particles_storage.h
Normal 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
|
||||
675
drivers/gles3/storage/render_scene_buffers_gles3.cpp
Normal file
675
drivers/gles3/storage/render_scene_buffers_gles3.cpp
Normal 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
|
||||
165
drivers/gles3/storage/render_scene_buffers_gles3.h
Normal file
165
drivers/gles3/storage/render_scene_buffers_gles3.h
Normal 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
|
||||
3252
drivers/gles3/storage/texture_storage.cpp
Normal file
3252
drivers/gles3/storage/texture_storage.cpp
Normal file
File diff suppressed because it is too large
Load Diff
737
drivers/gles3/storage/texture_storage.h
Normal file
737
drivers/gles3/storage/texture_storage.h
Normal 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
|
||||
483
drivers/gles3/storage/utilities.cpp
Normal file
483
drivers/gles3/storage/utilities.cpp
Normal 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
|
||||
234
drivers/gles3/storage/utilities.h
Normal file
234
drivers/gles3/storage/utilities.h
Normal 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
|
||||
Reference in New Issue
Block a user