diff options
Diffstat (limited to 'drivers/gles3')
33 files changed, 3080 insertions, 8544 deletions
diff --git a/drivers/gles3/rasterizer_array.h b/drivers/gles3/rasterizer_array.h deleted file mode 100644 index 9762e78d54..0000000000 --- a/drivers/gles3/rasterizer_array.h +++ /dev/null @@ -1,421 +0,0 @@ -/*************************************************************************/ -/* rasterizer_array.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef RASTERIZER_ARRAY_H -#define RASTERIZER_ARRAY_H - -/** - * Fast single-threaded growable array for POD types. - * For use in render drivers, not for general use. - * TO BE REPLACED by local_vector. - */ - -#include "core/os/memory.h" -#include <string.h> - -#include "core/templates/local_vector.h" -#include "core/templates/vector.h" - -// very simple non-growable array, that keeps track of the size of a 'unit' -// which can be cast to whatever vertex format FVF required, and is initially -// created with enough memory to hold the biggest FVF. -// This allows multiple FVFs to use the same array. -class RasterizerUnitArrayGLES3 { -public: - RasterizerUnitArrayGLES3() { - _list = nullptr; - free(); - } - ~RasterizerUnitArrayGLES3() { free(); } - - uint8_t *get_unit(unsigned int ui) { return &_list[ui * _unit_size_bytes]; } - const uint8_t *get_unit(unsigned int ui) const { return &_list[ui * _unit_size_bytes]; } - - int size() const { return _size; } - int max_size() const { return _max_size; } - - void free() { - if (_list) { - memdelete_arr(_list); - _list = 0; - } - _size = 0; - _max_size = 0; - _max_size_bytes = 0; - _unit_size_bytes = 0; - } - - void create(int p_max_size_units, int p_max_unit_size_bytes) { - free(); - - _max_unit_size_bytes = p_max_unit_size_bytes; - _max_size = p_max_size_units; - _max_size_bytes = p_max_size_units * p_max_unit_size_bytes; - - if (_max_size_bytes) { - _list = memnew_arr(uint8_t, _max_size_bytes); - } - } - - void prepare(int p_unit_size_bytes) { - _unit_size_bytes = p_unit_size_bytes; - _size = 0; - } - - // several items at a time - uint8_t *request(int p_num_items = 1) { - int old_size = _size; - _size += p_num_items; - - if (_size <= _max_size) { - return get_unit(old_size); - } - - // revert - _size = old_size; - return nullptr; - } - -private: - uint8_t *_list; - int _size; // in units - int _max_size; // in units - int _max_size_bytes; - int _unit_size_bytes; - int _max_unit_size_bytes; -}; - -template <class T> -class RasterizerArray { -public: - RasterizerArray() { - _list = 0; - _size = 0; - _max_size = 0; - } - ~RasterizerArray() { free(); } - - T &operator[](unsigned int ui) { return _list[ui]; } - const T &operator[](unsigned int ui) const { return _list[ui]; } - - void free() { - if (_list) { - memdelete_arr(_list); - _list = 0; - } - _size = 0; - _max_size = 0; - } - - void create(int p_size) { - free(); - if (p_size) { - _list = memnew_arr(T, p_size); - } - _size = 0; - _max_size = p_size; - } - - void reset() { _size = 0; } - - T *request_with_grow() { - T *p = request(); - if (!p) { - grow(); - return request_with_grow(); - } - return p; - } - - // none of that inefficient pass by value stuff here, thanks - T *request() { - if (_size < _max_size) { - return &_list[_size++]; - } - return 0; - } - - // several items at a time - T *request(int p_num_items) { - int old_size = _size; - _size += p_num_items; - - if (_size <= _max_size) { - return &_list[old_size]; - } - - // revert - _size = old_size; - return 0; - } - - int size() const { return _size; } - int max_size() const { return _max_size; } - const T *get_data() const { return _list; } - - bool copy_from(const RasterizerArray<T> &o) { - // no resizing done here, it should be done manually - if (o.size() > _max_size) - return false; - - // pod types only please! - memcpy(_list, o.get_data(), o.size() * sizeof(T)); - _size = o.size(); - return true; - } - - // if you want this to be cheap, call reset before grow, - // to ensure there is no data to copy - void grow() { - unsigned int new_max_size = _max_size * 2; - if (!new_max_size) - new_max_size = 1; - - T *new_list = memnew_arr(T, new_max_size); - - // copy .. pod types only - if (_list) { - memcpy(new_list, _list, _size * sizeof(T)); - } - - unsigned int new_size = size(); - free(); - _list = new_list; - _size = new_size; - _max_size = new_max_size; - } - -private: - T *_list; - int _size; - int _max_size; -}; - -template <class T> -class RasterizerArray_non_pod { -public: - RasterizerArray_non_pod() { - _size = 0; - } - - const T &operator[](unsigned int ui) const { return _list[ui]; } - - void create(int p_size) { - _list.resize(p_size); - _size = 0; - } - void reset() { _size = 0; } - - void push_back(const T &val) { - while (true) { - if (_size < max_size()) { - _list.set(_size, val); - _size++; - return; - } - - grow(); - } - } - - int size() const { return _size; } - int max_size() const { return _list.size(); } - -private: - void grow() { - unsigned int new_max_size = _list.size() * 2; - if (!new_max_size) - new_max_size = 1; - _list.resize(new_max_size); - } - - Vector<T> _list; - int _size; -}; - -// very simple non-growable array, that keeps track of the size of a 'unit' -// which can be cast to whatever vertex format FVF required, and is initially -// created with enough memory to hold the biggest FVF. -// This allows multiple FVFs to use the same array. -class RasterizerUnitArray { -public: - RasterizerUnitArray() { - _list = nullptr; - free(); - } - ~RasterizerUnitArray() { free(); } - - uint8_t *get_unit(unsigned int ui) { return &_list[ui * _unit_size_bytes]; } - const uint8_t *get_unit(unsigned int ui) const { return &_list[ui * _unit_size_bytes]; } - - int size() const { return _size; } - int max_size() const { return _max_size; } - int get_unit_size_bytes() const { return _unit_size_bytes; } - - void free() { - if (_list) { - memdelete_arr(_list); - _list = 0; - } - _size = 0; - _max_size = 0; - _max_size_bytes = 0; - _unit_size_bytes = 0; - } - - void create(int p_max_size_units, int p_max_unit_size_bytes) { - free(); - - _max_unit_size_bytes = p_max_unit_size_bytes; - _max_size = p_max_size_units; - _max_size_bytes = p_max_size_units * p_max_unit_size_bytes; - - if (_max_size_bytes) { - _list = memnew_arr(uint8_t, _max_size_bytes); - } - } - - void prepare(int p_unit_size_bytes) { - _unit_size_bytes = p_unit_size_bytes; - _size = 0; - } - - // several items at a time - uint8_t *request(int p_num_items = 1) { - int old_size = _size; - _size += p_num_items; - - if (_size <= _max_size) { - return get_unit(old_size); - } - - // revert - _size = old_size; - return nullptr; - } - -private: - uint8_t *_list; - int _size; // in units - int _max_size; // in units - int _max_size_bytes; - int _unit_size_bytes; - int _max_unit_size_bytes; -}; - -template <class T, bool force_trivial = false> -class RasterizerPooledList { - LocalVector<T, uint32_t, force_trivial> list; - LocalVector<uint32_t, uint32_t, true> freelist; - - // not all list members are necessarily used - int _used_size; - -public: - RasterizerPooledList() { - _used_size = 0; - } - - int estimate_memory_use() const { - return (list.size() * sizeof(T)) + (freelist.size() * sizeof(uint32_t)); - } - - const T &operator[](uint32_t p_index) const { - return list[p_index]; - } - T &operator[](uint32_t p_index) { - return list[p_index]; - } - - int size() const { return _used_size; } - - // returns the list id of the allocated item - uint32_t alloc() { - uint32_t id = 0; - _used_size++; - - if (freelist.size()) { - // pop from freelist - int new_size = freelist.size() - 1; - id = freelist[new_size]; - freelist.resize(new_size); - return id; - // return &list[r_id]; - } - - id = list.size(); - list.resize(id + 1); - return id; - // return &list[r_id]; - } - void free(const uint32_t &p_id) { - // should not be on free list already - CRASH_COND(p_id >= list.size()); - freelist.push_back(p_id); - _used_size--; - } -}; - -template <class T, bool force_trivial = false> -class RasterizerPooledIndirectList { -public: - const T &operator[](uint32_t p_index) const { - return *_list[p_index]; - } - T &operator[](uint32_t p_index) { - return *_list[p_index]; - } - - uint32_t alloc() { - uint32_t id = _list.alloc(); - _list[id] = memnew(T); - return id; - } - void free(const uint32_t &p_id) { - CRASH_COND(!_list[p_id]); - memdelete_notnull(_list[p_id]); - _list[p_id] = nullptr; - _list.free(p_id); - } - - ~RasterizerPooledIndirectList() { - // autodelete - for (int n = 0; n < _list.size(); n++) { - if (_list[n]) { - memdelete_notnull(_list[n]); - } - } - } - -private: - RasterizerPooledList<T *, true> _list; -}; - -#endif // RASTERIZER_ARRAY_H diff --git a/drivers/gles3/rasterizer_asserts.h b/drivers/gles3/rasterizer_asserts.h deleted file mode 100644 index b39357bffd..0000000000 --- a/drivers/gles3/rasterizer_asserts.h +++ /dev/null @@ -1,67 +0,0 @@ -/*************************************************************************/ -/* rasterizer_asserts.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef RASTERIZER_ASSERTS_H -#define RASTERIZER_ASSERTS_H - -// For flow control checking, we want an easy way to apply asserts that occur in debug development builds only. -// This is enforced by outputting a warning which will fail CI checks if the define is set in a PR. -#if defined(TOOLS_ENABLED) && defined(DEBUG_ENABLED) -// only uncomment this define for error checking in development, not in the main repository -// as these checks will slow things down in debug builds. -//#define RASTERIZER_EXTRA_CHECKS -#endif - -#ifdef RASTERIZER_EXTRA_CHECKS -#ifndef _MSC_VER -#warning do not define RASTERIZER_EXTRA_CHECKS in main repository builds -#endif -#define RAST_DEV_DEBUG_ASSERT(a) CRASH_COND(!(a)) -#else -#define RAST_DEV_DEBUG_ASSERT(a) -#endif - -// Also very useful, an assert check that only occurs in debug tools builds -#if defined(TOOLS_ENABLED) && defined(DEBUG_ENABLED) -#define RAST_DEBUG_ASSERT(a) CRASH_COND(!(a)) -#else -#define RAST_DEBUG_ASSERT(a) -#endif - -// Thin wrapper around ERR_FAIL_COND to allow us to make it debug only -#ifdef DEBUG_ENABLED -#define RAST_FAIL_COND(m_cond) ERR_FAIL_COND(m_cond) -#else -#define RAST_FAIL_COND(m_cond) \ - if (m_cond) { \ - } -#endif - -#endif // RASTERIZER_ASSERTS_H diff --git a/drivers/gles3/rasterizer_canvas_base_gles3.cpp b/drivers/gles3/rasterizer_canvas_base_gles3.cpp deleted file mode 100644 index 899e89cbce..0000000000 --- a/drivers/gles3/rasterizer_canvas_base_gles3.cpp +++ /dev/null @@ -1,1354 +0,0 @@ -/*************************************************************************/ -/* rasterizer_canvas_base_gles3.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "rasterizer_canvas_base_gles3.h" -#ifdef GLES3_BACKEND_ENABLED - -#include "core/os/os.h" -#include "drivers/gles3/rasterizer_asserts.h" -#include "rasterizer_scene_gles3.h" - -#include "core/config/project_settings.h" -#include "servers/rendering/rendering_server_default.h" - -#ifndef GLES_OVER_GL -#define glClearDepth glClearDepthf -#endif - -static _FORCE_INLINE_ void store_transform3d(const Transform3D &p_mtx, float *p_array) { - p_array[0] = p_mtx.basis.elements[0][0]; - p_array[1] = p_mtx.basis.elements[1][0]; - p_array[2] = p_mtx.basis.elements[2][0]; - p_array[3] = 0; - p_array[4] = p_mtx.basis.elements[0][1]; - p_array[5] = p_mtx.basis.elements[1][1]; - p_array[6] = p_mtx.basis.elements[2][1]; - p_array[7] = 0; - p_array[8] = p_mtx.basis.elements[0][2]; - p_array[9] = p_mtx.basis.elements[1][2]; - p_array[10] = p_mtx.basis.elements[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; -} - -RID RasterizerCanvasBaseGLES3::light_internal_create() { - return RID(); -} - -void RasterizerCanvasBaseGLES3::light_internal_update(RID p_rid, Light *p_light) { -} - -void RasterizerCanvasBaseGLES3::light_internal_free(RID p_rid) { -} - -RID RasterizerCanvasBaseGLES3::light_create() { - return RID(); -} - -void RasterizerCanvasBaseGLES3::light_set_texture(RID p_rid, RID p_texture) { -} - -void RasterizerCanvasBaseGLES3::light_set_use_shadow(RID p_rid, bool p_enable) { -} - -void RasterizerCanvasBaseGLES3::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) { -} - -void RasterizerCanvasBaseGLES3::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) { -} - -void RasterizerCanvasBaseGLES3::render_sdf(RID p_render_target, LightOccluderInstance *p_occluders) { -} - -RID RasterizerCanvasBaseGLES3::occluder_polygon_create() { - return RID(); -} - -void RasterizerCanvasBaseGLES3::occluder_polygon_set_shape(RID p_occluder, const Vector<Vector2> &p_points, bool p_closed) { -} - -void RasterizerCanvasBaseGLES3::occluder_polygon_set_cull_mode(RID p_occluder, RS::CanvasOccluderPolygonCullMode p_mode) { -} - -void RasterizerCanvasBaseGLES3::set_shadow_texture_size(int p_size) { -} - -bool RasterizerCanvasBaseGLES3::free(RID p_rid) { - return true; -} - -void RasterizerCanvasBaseGLES3::update() { -} - -void RasterizerCanvasBaseGLES3::canvas_begin() { - state.using_transparent_rt = false; - - // always start with light_angle unset - state.using_light_angle = false; - state.using_large_vertex = false; - state.using_modulate = false; - - state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_ATTRIB_LIGHT_ANGLE, false); - state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_ATTRIB_MODULATE, false); - state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_ATTRIB_LARGE_VERTEX, false); - state.canvas_shader.bind(); - - int viewport_x, viewport_y, viewport_width, viewport_height; - - if (storage->frame.current_rt) { - storage->bind_framebuffer(storage->frame.current_rt->fbo); - state.using_transparent_rt = storage->frame.current_rt->flags[RendererStorage::RENDER_TARGET_TRANSPARENT]; - - if (storage->frame.current_rt->flags[RendererStorage::RENDER_TARGET_DIRECT_TO_SCREEN]) { - // set Viewport and Scissor when rendering directly to screen - viewport_width = storage->_dims.rt_width; - viewport_height = storage->_dims.rt_height; - viewport_x = storage->frame.current_rt->x; - // FTODO - // viewport_y = OS::get_singleton()->get_window_size().height - viewport_height - storage->frame.current_rt->y; - viewport_y = storage->frame.current_rt->y; - - // viewport_x = 0; - // viewport_y = 0; - - glScissor(viewport_x, viewport_y, viewport_width, viewport_height); - glViewport(viewport_x, viewport_y, viewport_width, viewport_height); - glEnable(GL_SCISSOR_TEST); - } - } - - // FTODO .. this was commented out to try and get the clear color correct - //#ifdef GODOT3 - // OLD METHOD .. now done by render target rather than frame -#if 0 - if (storage->frame.clear_request) { - glClearColor(storage->frame.clear_request_color.r, - storage->frame.clear_request_color.g, - storage->frame.clear_request_color.b, - state.using_transparent_rt ? storage->frame.clear_request_color.a : 1.0); - glClear(GL_COLOR_BUFFER_BIT); - storage->frame.clear_request = false; - } -#endif - - // NEW METHOD - if (storage->frame.current_rt && storage->frame.current_rt->clear_requested) { - const Color &col = storage->frame.current_rt->clear_color; - glClearColor(col.r, col.g, col.b, col.a); - - // clear EVERYTHING. - // not clearing everything can be devastating on tiled renderers especially, - // because if anything is preserved, often the whole frame buffer needs to be preserved. - // Not sure if GL_ACCUM_BUFFER_BIT is needed or supported in GLES. - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - storage->frame.current_rt->clear_requested = false; - } - - //#endif - - /* - if (storage->frame.current_rt) { - glBindFramebuffer(GL_FRAMEBUFFER, storage->frame.current_rt->fbo); - glColorMask(1, 1, 1, 1); - } - */ - - reset_canvas(); - - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, storage->resources.white_tex); - - glVertexAttrib4f(RS::ARRAY_COLOR, 1, 1, 1, 1); - glDisableVertexAttribArray(RS::ARRAY_COLOR); - - // set up default uniforms - - Transform3D canvas_transform; - - if (storage->frame.current_rt) { - float csy = 1.0; - // FTODO - // if (storage->frame.current_rt && storage->frame.current_rt->flags[RendererStorage::RENDER_TARGET_VFLIP]) { - // csy = -1.0; - // } - canvas_transform.translate(-(storage->frame.current_rt->width / 2.0f), -(storage->frame.current_rt->height / 2.0f), 0.0f); - canvas_transform.scale(Vector3(2.0f / storage->frame.current_rt->width, csy * -2.0f / storage->frame.current_rt->height, 1.0f)); - } else { - // FTODO - // Vector2 ssize = OS::get_singleton()->get_window_size(); - Vector2 ssize; - ssize.x = storage->_dims.win_width; - ssize.y = storage->_dims.win_height; - - canvas_transform.translate(-(ssize.width / 2.0f), -(ssize.height / 2.0f), 0.0f); - canvas_transform.scale(Vector3(2.0f / ssize.width, -2.0f / ssize.height, 1.0f)); - } - - state.uniforms.projection_matrix = canvas_transform; - - state.uniforms.final_modulate = Color(1, 1, 1, 1); - - state.uniforms.modelview_matrix = Transform2D(); - state.uniforms.extra_matrix = Transform2D(); - - _set_uniforms(); - _bind_quad_buffer(); - - glBindBufferBase(GL_UNIFORM_BUFFER, 0, state.canvas_item_ubo); - glBindVertexArray(data.canvas_quad_array); -} - -void RasterizerCanvasBaseGLES3::canvas_end() { - glBindBuffer(GL_ARRAY_BUFFER, 0); - - if (storage->frame.current_rt && storage->frame.current_rt->flags[RendererStorage::RENDER_TARGET_DIRECT_TO_SCREEN]) { - //reset viewport to full window size - // int viewport_width = OS::get_singleton()->get_window_size().width; - // int viewport_height = OS::get_singleton()->get_window_size().height; - int viewport_width = storage->_dims.win_width; - int viewport_height = storage->_dims.win_height; - glViewport(0, 0, viewport_width, viewport_height); - glScissor(0, 0, viewport_width, viewport_height); - } - - state.using_texture_rect = false; - state.using_skeleton = false; - state.using_ninepatch = false; - state.using_transparent_rt = false; -} - -void RasterizerCanvasBaseGLES3::draw_generic_textured_rect(const Rect2 &p_rect, const Rect2 &p_src) { - state.canvas_shader.set_uniform(CanvasShaderGLES3::DST_RECT, Color(p_rect.position.x, p_rect.position.y, p_rect.size.x, p_rect.size.y)); - state.canvas_shader.set_uniform(CanvasShaderGLES3::SRC_RECT, Color(p_src.position.x, p_src.position.y, p_src.size.x, p_src.size.y)); - _bind_quad_buffer(); - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); -} - -void RasterizerCanvasBaseGLES3::_set_texture_rect_mode(bool p_texture_rect, bool p_light_angle, bool p_modulate, bool p_large_vertex) { - // always set this directly (this could be state checked) - state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_TEXTURE_RECT, p_texture_rect); - - if (state.using_light_angle != p_light_angle) { - state.using_light_angle = p_light_angle; - state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_ATTRIB_LIGHT_ANGLE, p_light_angle); - } - - if (state.using_modulate != p_modulate) { - state.using_modulate = p_modulate; - state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_ATTRIB_MODULATE, p_modulate); - } - - if (state.using_large_vertex != p_large_vertex) { - state.using_large_vertex = p_large_vertex; - state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_ATTRIB_LARGE_VERTEX, p_large_vertex); - } -} - -RasterizerStorageGLES3::Texture *RasterizerCanvasBaseGLES3::_bind_canvas_texture(const RID &p_texture, const RID &p_normal_map) { - RasterizerStorageGLES3::Texture *tex_return = NULL; - - if (p_texture.is_valid()) { - RasterizerStorageGLES3::Texture *texture = storage->texture_owner.get_or_null(p_texture); - - if (!texture) { - state.current_tex = RID(); - state.current_tex_ptr = NULL; - - glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 1); - glBindTexture(GL_TEXTURE_2D, storage->resources.white_tex); - - } else { - if (texture->redraw_if_visible) { - RenderingServerDefault::redraw_request(); - } - - texture = texture->get_ptr(); - - if (texture->render_target) { - texture->render_target->used_in_frame = true; - } - - glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 1); - glBindTexture(GL_TEXTURE_2D, texture->tex_id); - - state.current_tex = p_texture; - state.current_tex_ptr = texture; - - // new for Godot 4. Set the texture min mag filter and repeat per item - // we use a wrapper to avoid noop GL state changes - texture->GLSetFilter(GL_TEXTURE_2D, state.current_filter); - - tex_return = texture; - } - } else { - state.current_tex = RID(); - state.current_tex_ptr = NULL; - - glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 1); - glBindTexture(GL_TEXTURE_2D, storage->resources.white_tex); - } - - if (p_normal_map == state.current_normal) { - //do none - state.canvas_shader.set_uniform(CanvasShaderGLES3::USE_DEFAULT_NORMAL, state.current_normal.is_valid()); - - } else if (p_normal_map.is_valid()) { - RasterizerStorageGLES3::Texture *normal_map = storage->texture_owner.get_or_null(p_normal_map); - - if (!normal_map) { - state.current_normal = RID(); - glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 2); - glBindTexture(GL_TEXTURE_2D, storage->resources.normal_tex); - state.canvas_shader.set_uniform(CanvasShaderGLES3::USE_DEFAULT_NORMAL, false); - - } else { - if (normal_map->redraw_if_visible) { //check before proxy, because this is usually used with proxies - RenderingServerDefault::redraw_request(); - } - - normal_map = normal_map->get_ptr(); - - glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 2); - glBindTexture(GL_TEXTURE_2D, normal_map->tex_id); - state.current_normal = p_normal_map; - state.canvas_shader.set_uniform(CanvasShaderGLES3::USE_DEFAULT_NORMAL, true); - } - - } else { - state.current_normal = RID(); - glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 2); - glBindTexture(GL_TEXTURE_2D, storage->resources.normal_tex); - state.canvas_shader.set_uniform(CanvasShaderGLES3::USE_DEFAULT_NORMAL, false); - } - - return tex_return; -} - -/* -void RasterizerCanvasBaseGLES3::draw_window_margins(int *black_margin, RID *black_image) { - return; - - // FTODO - int window_w = storage->_dims.rt_width; - int window_h = storage->_dims.rt_height; - //Vector2 window_size = Vector2(window_w, window_h); - - // int window_h = window_size.height; - // int window_w = window_size.width; - - // glBindFramebuffer(GL_FRAMEBUFFER, storage->system_fbo); - // glViewport(0, 0, window_size.width, window_size.height); - - canvas_begin(); - - if (black_image[SIDE_LEFT].is_valid()) { - _bind_canvas_texture(black_image[SIDE_LEFT], RID()); - Size2 sz(storage->texture_get_width(black_image[SIDE_LEFT]), storage->texture_get_height(black_image[SIDE_LEFT])); - draw_generic_textured_rect(Rect2(0, 0, black_margin[SIDE_LEFT], window_h), - Rect2(0, 0, (float)black_margin[SIDE_LEFT] / sz.x, (float)(window_h) / sz.y)); - } else if (black_margin[SIDE_LEFT]) { - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, storage->resources.black_tex); - - draw_generic_textured_rect(Rect2(0, 0, black_margin[SIDE_LEFT], window_h), Rect2(0, 0, 1, 1)); - } - - if (black_image[SIDE_RIGHT].is_valid()) { - _bind_canvas_texture(black_image[SIDE_RIGHT], RID()); - Size2 sz(storage->texture_get_width(black_image[SIDE_RIGHT]), storage->texture_get_height(black_image[SIDE_RIGHT])); - draw_generic_textured_rect(Rect2(window_w - black_margin[SIDE_RIGHT], 0, black_margin[SIDE_RIGHT], window_h), - Rect2(0, 0, (float)black_margin[SIDE_RIGHT] / sz.x, (float)window_h / sz.y)); - } else if (black_margin[SIDE_RIGHT]) { - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, storage->resources.black_tex); - - draw_generic_textured_rect(Rect2(window_w - black_margin[SIDE_RIGHT], 0, black_margin[SIDE_RIGHT], window_h), Rect2(0, 0, 1, 1)); - } - - if (black_image[SIDE_TOP].is_valid()) { - _bind_canvas_texture(black_image[SIDE_TOP], RID()); - - Size2 sz(storage->texture_get_width(black_image[SIDE_TOP]), storage->texture_get_height(black_image[SIDE_TOP])); - draw_generic_textured_rect(Rect2(0, 0, window_w, black_margin[SIDE_TOP]), - Rect2(0, 0, (float)window_w / sz.x, (float)black_margin[SIDE_TOP] / sz.y)); - - } else if (black_margin[SIDE_TOP]) { - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, storage->resources.black_tex); - - draw_generic_textured_rect(Rect2(0, 0, window_w, black_margin[SIDE_TOP]), Rect2(0, 0, 1, 1)); - } - - if (black_image[SIDE_BOTTOM].is_valid()) { - _bind_canvas_texture(black_image[SIDE_BOTTOM], RID()); - - Size2 sz(storage->texture_get_width(black_image[SIDE_BOTTOM]), storage->texture_get_height(black_image[SIDE_BOTTOM])); - draw_generic_textured_rect(Rect2(0, window_h - black_margin[SIDE_BOTTOM], window_w, black_margin[SIDE_BOTTOM]), - Rect2(0, 0, (float)window_w / sz.x, (float)black_margin[SIDE_BOTTOM] / sz.y)); - - } else if (black_margin[SIDE_BOTTOM]) { - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, storage->resources.black_tex); - - draw_generic_textured_rect(Rect2(0, window_h - black_margin[SIDE_BOTTOM], window_w, black_margin[SIDE_BOTTOM]), Rect2(0, 0, 1, 1)); - } - - canvas_end(); -} -*/ - -void RasterizerCanvasBaseGLES3::_bind_quad_buffer() { - glBindVertexArray(data.canvas_quad_array); -} - -void RasterizerCanvasBaseGLES3::_set_uniforms() { - state.canvas_shader.set_uniform(CanvasShaderGLES3::PROJECTION_MATRIX, state.uniforms.projection_matrix); - state.canvas_shader.set_uniform(CanvasShaderGLES3::MODELVIEW_MATRIX, state.uniforms.modelview_matrix); - state.canvas_shader.set_uniform(CanvasShaderGLES3::EXTRA_MATRIX, state.uniforms.extra_matrix); - - state.canvas_shader.set_uniform(CanvasShaderGLES3::FINAL_MODULATE, state.uniforms.final_modulate); - - state.canvas_shader.set_uniform(CanvasShaderGLES3::TIME, storage->frame.time[0]); - - if (storage->frame.current_rt) { - Vector2 screen_pixel_size; - screen_pixel_size.x = 1.0 / storage->frame.current_rt->width; - screen_pixel_size.y = 1.0 / storage->frame.current_rt->height; - - state.canvas_shader.set_uniform(CanvasShaderGLES3::SCREEN_PIXEL_SIZE, screen_pixel_size); - } - - if (state.using_skeleton) { - state.canvas_shader.set_uniform(CanvasShaderGLES3::SKELETON_TRANSFORM, state.skeleton_transform); - state.canvas_shader.set_uniform(CanvasShaderGLES3::SKELETON_TRANSFORM_INVERSE, state.skeleton_transform_inverse); - state.canvas_shader.set_uniform(CanvasShaderGLES3::SKELETON_TEXTURE_SIZE, state.skeleton_texture_size); - } - - if (state.using_light) { - Light *light = state.using_light; - state.canvas_shader.set_uniform(CanvasShaderGLES3::LIGHT_MATRIX, light->light_shader_xform); - Transform2D basis_inverse = light->light_shader_xform.affine_inverse().orthonormalized(); - basis_inverse.elements[2] = Vector2(); - state.canvas_shader.set_uniform(CanvasShaderGLES3::LIGHT_MATRIX_INVERSE, basis_inverse); - state.canvas_shader.set_uniform(CanvasShaderGLES3::LIGHT_LOCAL_MATRIX, light->xform_cache.affine_inverse()); - state.canvas_shader.set_uniform(CanvasShaderGLES3::LIGHT_COLOR, light->color * light->energy); - // state.canvas_shader.set_uniform(CanvasShaderGLES3::LIGHT_POS, light->light_shader_pos); - // FTODO - state.canvas_shader.set_uniform(CanvasShaderGLES3::LIGHT_POS, light->light_shader_xform.elements[2]); - state.canvas_shader.set_uniform(CanvasShaderGLES3::LIGHT_HEIGHT, light->height); - - // FTODO - //state.canvas_shader.set_uniform(CanvasShaderGLES3::LIGHT_OUTSIDE_ALPHA, light->mode == RS::CANVAS_LIGHT_MODE_MASK ? 1.0 : 0.0); - state.canvas_shader.set_uniform(CanvasShaderGLES3::LIGHT_OUTSIDE_ALPHA, 0.0f); - - if (state.using_shadow) { - // FTODO -#if 0 - RasterizerStorageGLES3::CanvasLightShadow *cls = storage->canvas_light_shadow_owner.get(light->shadow_buffer); - glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 5); - glBindTexture(GL_TEXTURE_2D, cls->distance); - state.canvas_shader.set_uniform(CanvasShaderGLES3::SHADOW_MATRIX, light->shadow_matrix_cache); - state.canvas_shader.set_uniform(CanvasShaderGLES3::LIGHT_SHADOW_COLOR, light->shadow_color); - - state.canvas_shader.set_uniform(CanvasShaderGLES3::SHADOWPIXEL_SIZE, (1.0 / light->shadow_buffer_size) * (1.0 + light->shadow_smooth)); - if (light->radius_cache == 0) { - state.canvas_shader.set_uniform(CanvasShaderGLES3::SHADOW_GRADIENT, 0.0); - } else { - state.canvas_shader.set_uniform(CanvasShaderGLES3::SHADOW_GRADIENT, light->shadow_gradient_length / (light->radius_cache * 1.1)); - } - state.canvas_shader.set_uniform(CanvasShaderGLES3::SHADOW_DISTANCE_MULT, light->radius_cache * 1.1); -#endif - } - } -} - -void RasterizerCanvasBaseGLES3::reset_canvas() { - glDisable(GL_CULL_FACE); - glDisable(GL_DEPTH_TEST); - glDisable(GL_SCISSOR_TEST); - glDisable(GL_DITHER); - glEnable(GL_BLEND); - - if (storage->frame.current_rt && storage->frame.current_rt->flags[RendererStorage::RENDER_TARGET_TRANSPARENT]) { - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - } else { - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - } - - // bind the back buffer to a texture so shaders can use it. - // It should probably use texture unit -3 (as OpenGL does as well) but currently that's buggy. - // keeping this for now as there's nothing else that uses texture unit 2 - // TODO ^ - if (storage->frame.current_rt) { - // glActiveTexture(GL_TEXTURE0 + 2); - // glBindTexture(GL_TEXTURE_2D, storage->frame.current_rt->copy_screen_effect.color); - } - - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); -} - -void RasterizerCanvasBaseGLES3::canvas_debug_viewport_shadows(Light *p_lights_with_shadow) { -} - -void RasterizerCanvasBaseGLES3::_copy_texscreen(const Rect2 &p_rect) { - state.canvas_texscreen_used = true; - - _copy_screen(p_rect); - - // back to canvas, force rebind - state.using_texture_rect = false; - state.canvas_shader.bind(); - _bind_canvas_texture(state.current_tex, state.current_normal); - _set_uniforms(); -} - -void RasterizerCanvasBaseGLES3::_draw_polygon(const int *p_indices, int p_index_count, int p_vertex_count, const Vector2 *p_vertices, const Vector2 *p_uvs, const Color *p_colors, bool p_singlecolor, const float *p_weights, const int *p_bones) { - glBindVertexArray(data.polygon_buffer_pointer_array); - glBindBuffer(GL_ARRAY_BUFFER, data.polygon_buffer); - - uint32_t buffer_ofs = 0; - uint32_t buffer_ofs_after = buffer_ofs + (sizeof(Vector2) * p_vertex_count); -#ifdef DEBUG_ENABLED - ERR_FAIL_COND(buffer_ofs_after > data.polygon_buffer_size); -#endif - - storage->buffer_orphan_and_upload(data.polygon_buffer_size, 0, sizeof(Vector2) * p_vertex_count, p_vertices, GL_ARRAY_BUFFER, _buffer_upload_usage_flag, true); - - glEnableVertexAttribArray(RS::ARRAY_VERTEX); - glVertexAttribPointer(RS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(Vector2), NULL); - buffer_ofs = buffer_ofs_after; - - if (p_singlecolor) { - glDisableVertexAttribArray(RS::ARRAY_COLOR); - Color m = *p_colors; - glVertexAttrib4f(RS::ARRAY_COLOR, m.r, m.g, m.b, m.a); - } else if (!p_colors) { - glDisableVertexAttribArray(RS::ARRAY_COLOR); - glVertexAttrib4f(RS::ARRAY_COLOR, 1, 1, 1, 1); - } else { - RAST_FAIL_COND(!storage->safe_buffer_sub_data(data.polygon_buffer_size, GL_ARRAY_BUFFER, buffer_ofs, sizeof(Color) * p_vertex_count, p_colors, buffer_ofs_after)); - glEnableVertexAttribArray(RS::ARRAY_COLOR); - glVertexAttribPointer(RS::ARRAY_COLOR, 4, GL_FLOAT, GL_FALSE, sizeof(Color), CAST_INT_TO_UCHAR_PTR(buffer_ofs)); - buffer_ofs = buffer_ofs_after; - } - - if (p_uvs) { - RAST_FAIL_COND(!storage->safe_buffer_sub_data(data.polygon_buffer_size, GL_ARRAY_BUFFER, buffer_ofs, sizeof(Vector2) * p_vertex_count, p_uvs, buffer_ofs_after)); - glEnableVertexAttribArray(RS::ARRAY_TEX_UV); - glVertexAttribPointer(RS::ARRAY_TEX_UV, 2, GL_FLOAT, GL_FALSE, sizeof(Vector2), CAST_INT_TO_UCHAR_PTR(buffer_ofs)); - buffer_ofs = buffer_ofs_after; - } else { - glDisableVertexAttribArray(RS::ARRAY_TEX_UV); - } - - if (p_weights && p_bones) { - RAST_FAIL_COND(!storage->safe_buffer_sub_data(data.polygon_buffer_size, GL_ARRAY_BUFFER, buffer_ofs, sizeof(float) * 4 * p_vertex_count, p_weights, buffer_ofs_after)); - glEnableVertexAttribArray(RS::ARRAY_WEIGHTS); - glVertexAttribPointer(RS::ARRAY_WEIGHTS, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 4, CAST_INT_TO_UCHAR_PTR(buffer_ofs)); - buffer_ofs = buffer_ofs_after; - - RAST_FAIL_COND(!storage->safe_buffer_sub_data(data.polygon_buffer_size, GL_ARRAY_BUFFER, buffer_ofs, sizeof(int) * 4 * p_vertex_count, p_bones, buffer_ofs_after)); - glEnableVertexAttribArray(RS::ARRAY_BONES); - glVertexAttribPointer(RS::ARRAY_BONES, 4, GL_UNSIGNED_INT, GL_FALSE, sizeof(int) * 4, CAST_INT_TO_UCHAR_PTR(buffer_ofs)); - buffer_ofs = buffer_ofs_after; - - } else { - glDisableVertexAttribArray(RS::ARRAY_WEIGHTS); - glDisableVertexAttribArray(RS::ARRAY_BONES); - } - - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.polygon_index_buffer); - - if (storage->config.support_32_bits_indices) { //should check for -#ifdef DEBUG_ENABLED - ERR_FAIL_COND((sizeof(int) * p_index_count) > data.polygon_index_buffer_size); -#endif - storage->buffer_orphan_and_upload(data.polygon_index_buffer_size, 0, sizeof(int) * p_index_count, p_indices, GL_ELEMENT_ARRAY_BUFFER, _buffer_upload_usage_flag, true); - - glDrawElements(GL_TRIANGLES, p_index_count, GL_UNSIGNED_INT, 0); - storage->info.render._2d_draw_call_count++; - } else { -#ifdef DEBUG_ENABLED - ERR_FAIL_COND((sizeof(uint16_t) * p_index_count) > data.polygon_index_buffer_size); -#endif - uint16_t *index16 = (uint16_t *)alloca(sizeof(uint16_t) * p_index_count); - for (int i = 0; i < p_index_count; i++) { - index16[i] = uint16_t(p_indices[i]); - } - storage->buffer_orphan_and_upload(data.polygon_index_buffer_size, 0, sizeof(uint16_t) * p_index_count, index16, GL_ELEMENT_ARRAY_BUFFER, _buffer_upload_usage_flag, true); - glDrawElements(GL_TRIANGLES, p_index_count, GL_UNSIGNED_SHORT, 0); - storage->info.render._2d_draw_call_count++; - } - glBindVertexArray(0); - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); -} - -void RasterizerCanvasBaseGLES3::_draw_generic(GLuint p_primitive, int p_vertex_count, const Vector2 *p_vertices, const Vector2 *p_uvs, const Color *p_colors, bool p_singlecolor) { - glBindVertexArray(data.polygon_buffer_pointer_array); - glBindBuffer(GL_ARRAY_BUFFER, data.polygon_buffer); - - uint32_t buffer_ofs = 0; - uint32_t buffer_ofs_after = buffer_ofs + (sizeof(Vector2) * p_vertex_count); -#ifdef DEBUG_ENABLED - ERR_FAIL_COND(buffer_ofs_after > data.polygon_buffer_size); -#endif - storage->buffer_orphan_and_upload(data.polygon_buffer_size, 0, sizeof(Vector2) * p_vertex_count, p_vertices, GL_ARRAY_BUFFER, _buffer_upload_usage_flag, true); - - glEnableVertexAttribArray(RS::ARRAY_VERTEX); - glVertexAttribPointer(RS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(Vector2), NULL); - buffer_ofs = buffer_ofs_after; - - if (p_singlecolor) { - glDisableVertexAttribArray(RS::ARRAY_COLOR); - Color m = *p_colors; - glVertexAttrib4f(RS::ARRAY_COLOR, m.r, m.g, m.b, m.a); - } else if (!p_colors) { - glDisableVertexAttribArray(RS::ARRAY_COLOR); - glVertexAttrib4f(RS::ARRAY_COLOR, 1, 1, 1, 1); - } else { - RAST_FAIL_COND(!storage->safe_buffer_sub_data(data.polygon_buffer_size, GL_ARRAY_BUFFER, buffer_ofs, sizeof(Color) * p_vertex_count, p_colors, buffer_ofs_after)); - glEnableVertexAttribArray(RS::ARRAY_COLOR); - glVertexAttribPointer(RS::ARRAY_COLOR, 4, GL_FLOAT, GL_FALSE, sizeof(Color), CAST_INT_TO_UCHAR_PTR(buffer_ofs)); - buffer_ofs = buffer_ofs_after; - } - - if (p_uvs) { - RAST_FAIL_COND(!storage->safe_buffer_sub_data(data.polygon_buffer_size, GL_ARRAY_BUFFER, buffer_ofs, sizeof(Vector2) * p_vertex_count, p_uvs, buffer_ofs_after)); - glEnableVertexAttribArray(RS::ARRAY_TEX_UV); - glVertexAttribPointer(RS::ARRAY_TEX_UV, 2, GL_FLOAT, GL_FALSE, sizeof(Vector2), CAST_INT_TO_UCHAR_PTR(buffer_ofs)); - buffer_ofs = buffer_ofs_after; - } else { - glDisableVertexAttribArray(RS::ARRAY_TEX_UV); - } - - glDrawArrays(p_primitive, 0, p_vertex_count); - storage->info.render._2d_draw_call_count++; - - glBindVertexArray(0); - glBindBuffer(GL_ARRAY_BUFFER, 0); -} - -void RasterizerCanvasBaseGLES3::_draw_generic_indices(GLuint p_primitive, const int *p_indices, int p_index_count, int p_vertex_count, const Vector2 *p_vertices, const Vector2 *p_uvs, const Color *p_colors, bool p_singlecolor) { - glBindVertexArray(data.polygon_buffer_pointer_array); - glBindBuffer(GL_ARRAY_BUFFER, data.polygon_buffer); - - uint32_t buffer_ofs = 0; - uint32_t buffer_ofs_after = buffer_ofs + (sizeof(Vector2) * p_vertex_count); -#ifdef DEBUG_ENABLED - ERR_FAIL_COND(buffer_ofs_after > data.polygon_buffer_size); -#endif - storage->buffer_orphan_and_upload(data.polygon_buffer_size, 0, sizeof(Vector2) * p_vertex_count, p_vertices, GL_ARRAY_BUFFER, _buffer_upload_usage_flag, true); - - glEnableVertexAttribArray(RS::ARRAY_VERTEX); - glVertexAttribPointer(RS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(Vector2), NULL); - buffer_ofs = buffer_ofs_after; - - if (p_singlecolor) { - glDisableVertexAttribArray(RS::ARRAY_COLOR); - Color m = *p_colors; - glVertexAttrib4f(RS::ARRAY_COLOR, m.r, m.g, m.b, m.a); - } else if (!p_colors) { - glDisableVertexAttribArray(RS::ARRAY_COLOR); - glVertexAttrib4f(RS::ARRAY_COLOR, 1, 1, 1, 1); - } else { - RAST_FAIL_COND(!storage->safe_buffer_sub_data(data.polygon_buffer_size, GL_ARRAY_BUFFER, buffer_ofs, sizeof(Color) * p_vertex_count, p_colors, buffer_ofs_after)); - glEnableVertexAttribArray(RS::ARRAY_COLOR); - glVertexAttribPointer(RS::ARRAY_COLOR, 4, GL_FLOAT, GL_FALSE, sizeof(Color), CAST_INT_TO_UCHAR_PTR(buffer_ofs)); - buffer_ofs = buffer_ofs_after; - } - - if (p_uvs) { - RAST_FAIL_COND(!storage->safe_buffer_sub_data(data.polygon_buffer_size, GL_ARRAY_BUFFER, buffer_ofs, sizeof(Vector2) * p_vertex_count, p_uvs, buffer_ofs_after)); - glEnableVertexAttribArray(RS::ARRAY_TEX_UV); - glVertexAttribPointer(RS::ARRAY_TEX_UV, 2, GL_FLOAT, GL_FALSE, sizeof(Vector2), CAST_INT_TO_UCHAR_PTR(buffer_ofs)); - buffer_ofs = buffer_ofs_after; - } else { - glDisableVertexAttribArray(RS::ARRAY_TEX_UV); - } - -#ifdef RASTERIZER_EXTRA_CHECKS - // very slow, do not enable in normal use - for (int n = 0; n < p_index_count; n++) { - RAST_DEV_DEBUG_ASSERT(p_indices[n] < p_vertex_count); - } -#endif - - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.polygon_index_buffer); - - if (storage->config.support_32_bits_indices) { //should check for -#ifdef DEBUG_ENABLED - ERR_FAIL_COND((sizeof(int) * p_index_count) > data.polygon_index_buffer_size); -#endif - storage->buffer_orphan_and_upload(data.polygon_index_buffer_size, 0, sizeof(int) * p_index_count, p_indices, GL_ELEMENT_ARRAY_BUFFER, _buffer_upload_usage_flag, true); - glDrawElements(p_primitive, p_index_count, GL_UNSIGNED_INT, 0); - storage->info.render._2d_draw_call_count++; - } else { -#ifdef DEBUG_ENABLED - ERR_FAIL_COND((sizeof(uint16_t) * p_index_count) > data.polygon_index_buffer_size); -#endif - uint16_t *index16 = (uint16_t *)alloca(sizeof(uint16_t) * p_index_count); - for (int i = 0; i < p_index_count; i++) { - index16[i] = uint16_t(p_indices[i]); - } - storage->buffer_orphan_and_upload(data.polygon_index_buffer_size, 0, sizeof(uint16_t) * p_index_count, index16, GL_ELEMENT_ARRAY_BUFFER, _buffer_upload_usage_flag, true); - glDrawElements(p_primitive, p_index_count, GL_UNSIGNED_SHORT, 0); - storage->info.render._2d_draw_call_count++; - } - glBindVertexArray(0); - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); -} - -void RasterizerCanvasBaseGLES3::_legacy_draw_poly_triangles(Item::CommandPolygon *p_poly, RasterizerStorageGLES3::Material *p_material) { - // return; - - const PolyData &pd = _polydata[p_poly->polygon.polygon_id]; - - _set_texture_rect_mode(false); - - if (state.canvas_shader.bind()) { - _set_uniforms(); - state.canvas_shader.use_material((void *)p_material); - } - - // FTODO - //RasterizerStorageGLES3::Texture *texture = _bind_canvas_texture(polygon->texture, polygon->normal_map); - RasterizerStorageGLES3::Texture *texture = _bind_canvas_texture(p_poly->texture, RID()); - - if (texture) { - Size2 texpixel_size(1.0 / texture->width, 1.0 / texture->height); - state.canvas_shader.set_uniform(CanvasShaderGLES3::COLOR_TEXPIXEL_SIZE, texpixel_size); - } - - _draw_polygon(pd.indices.ptr(), pd.indices.size(), pd.points.size(), pd.points.ptr(), pd.uvs.ptr(), pd.colors.ptr(), pd.colors.size() == 1, nullptr, nullptr); - -// _draw_polygon(polygon->indices.ptr(), polygon->count, polygon->points.size(), polygon->points.ptr(), polygon->uvs.ptr(), polygon->colors.ptr(), polygon->colors.size() == 1, polygon->weights.ptr(), polygon->bones.ptr()); -#ifdef GLES_OVER_GL -#if 0 - if (polygon->antialiased) { - glEnable(GL_LINE_SMOOTH); - if (polygon->antialiasing_use_indices) { - _draw_generic_indices(GL_LINE_STRIP, polygon->indices.ptr(), polygon->count, polygon->points.size(), polygon->points.ptr(), polygon->uvs.ptr(), polygon->colors.ptr(), polygon->colors.size() == 1); - } else { - _draw_generic(GL_LINE_LOOP, polygon->points.size(), polygon->points.ptr(), polygon->uvs.ptr(), polygon->colors.ptr(), polygon->colors.size() == 1); - } - glDisable(GL_LINE_SMOOTH); - } -#endif -#endif -} - -void RasterizerCanvasBaseGLES3::_legacy_draw_primitive(Item::CommandPrimitive *p_pr, RasterizerStorageGLES3::Material *p_material) { - // return; - - if (p_pr->point_count != 4) - return; // not sure if supported - - _set_texture_rect_mode(false); - - if (state.canvas_shader.bind()) { - _set_uniforms(); - state.canvas_shader.use_material((void *)p_material); - } - - _bind_canvas_texture(RID(), RID()); - - glDisableVertexAttribArray(RS::ARRAY_COLOR); - glVertexAttrib4fv(RS::ARRAY_COLOR, p_pr->colors[0].components); - - state.canvas_shader.set_uniform(CanvasShaderGLES3::MODELVIEW_MATRIX, state.uniforms.modelview_matrix); - - _draw_gui_primitive(p_pr->point_count, p_pr->points, NULL, NULL); -} - -void RasterizerCanvasBaseGLES3::_legacy_draw_line(Item::CommandPrimitive *p_pr, RasterizerStorageGLES3::Material *p_material) { - _set_texture_rect_mode(false); - - if (state.canvas_shader.bind()) { - _set_uniforms(); - state.canvas_shader.use_material((void *)p_material); - } - - _bind_canvas_texture(RID(), RID()); - - glDisableVertexAttribArray(RS::ARRAY_COLOR); - glVertexAttrib4fv(RS::ARRAY_COLOR, p_pr->colors[0].components); - - state.canvas_shader.set_uniform(CanvasShaderGLES3::MODELVIEW_MATRIX, state.uniforms.modelview_matrix); - -#ifdef GLES_OVER_GL -// if (line->antialiased) -// glEnable(GL_LINE_SMOOTH); -#endif - _draw_gui_primitive(2, p_pr->points, NULL, NULL); - -#ifdef GLES_OVER_GL -// if (line->antialiased) -// glDisable(GL_LINE_SMOOTH); -#endif -} - -void RasterizerCanvasBaseGLES3::_draw_gui_primitive(int p_points, const Vector2 *p_vertices, const Color *p_colors, const Vector2 *p_uvs, const float *p_light_angles) { - static const GLenum prim[5] = { GL_POINTS, GL_POINTS, GL_LINES, GL_TRIANGLES, GL_TRIANGLE_FAN }; - - int version = 0; - int color_offset = 0; - int uv_offset = 0; - int light_angle_offset = 0; - int stride = 2; - - if (p_colors) { - version |= 1; - color_offset = stride; - stride += 4; - } - - if (p_uvs) { - version |= 2; - uv_offset = stride; - stride += 2; - } - - if (p_light_angles) { //light_angles - version |= 4; - light_angle_offset = stride; - stride += 1; - } - - RAST_DEV_DEBUG_ASSERT(p_points <= 4); - float buffer_data[(2 + 2 + 4 + 1) * 4]; - - for (int i = 0; i < p_points; i++) { - buffer_data[stride * i + 0] = p_vertices[i].x; - buffer_data[stride * i + 1] = p_vertices[i].y; - } - - if (p_colors) { - for (int i = 0; i < p_points; i++) { - buffer_data[stride * i + color_offset + 0] = p_colors[i].r; - buffer_data[stride * i + color_offset + 1] = p_colors[i].g; - buffer_data[stride * i + color_offset + 2] = p_colors[i].b; - buffer_data[stride * i + color_offset + 3] = p_colors[i].a; - } - } - - if (p_uvs) { - for (int i = 0; i < p_points; i++) { - buffer_data[stride * i + uv_offset + 0] = p_uvs[i].x; - buffer_data[stride * i + uv_offset + 1] = p_uvs[i].y; - } - } - - if (p_light_angles) { - for (int i = 0; i < p_points; i++) { - buffer_data[stride * i + light_angle_offset + 0] = p_light_angles[i]; - } - } - - glBindBuffer(GL_ARRAY_BUFFER, data.polygon_buffer); - storage->buffer_orphan_and_upload(data.polygon_buffer_size, 0, p_points * stride * 4 * sizeof(float), buffer_data, GL_ARRAY_BUFFER, _buffer_upload_usage_flag, true); - - glBindVertexArray(data.polygon_buffer_quad_arrays[version]); - - glDrawArrays(prim[p_points], 0, p_points); - storage->info.render._2d_draw_call_count++; - glBindVertexArray(0); - glBindBuffer(GL_ARRAY_BUFFER, 0); -} - -void RasterizerCanvasBaseGLES3::_copy_screen(const Rect2 &p_rect) { - if (storage->frame.current_rt->flags[RendererStorage::RENDER_TARGET_DIRECT_TO_SCREEN]) { - ERR_PRINT_ONCE("Cannot use screen texture copying in render target set to render direct to screen."); - return; - } - - ERR_FAIL_COND_MSG(storage->frame.current_rt->copy_screen_effect.color == 0, "Can't use screen texture copying in a render target configured without copy buffers."); - - glDisable(GL_BLEND); - - Vector2 wh(storage->frame.current_rt->width, storage->frame.current_rt->height); - - Color copy_section(p_rect.position.x / wh.x, p_rect.position.y / wh.y, p_rect.size.x / wh.x, p_rect.size.y / wh.y); - - if (p_rect != Rect2()) { - storage->shaders.copy.set_conditional(CopyShaderGLES3::USE_COPY_SECTION, true); - } - - storage->shaders.copy.set_conditional(CopyShaderGLES3::USE_NO_ALPHA, !state.using_transparent_rt); - - storage->bind_framebuffer(storage->frame.current_rt->copy_screen_effect.fbo); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, storage->frame.current_rt->color); - - storage->shaders.copy.bind(); - storage->shaders.copy.set_uniform(CopyShaderGLES3::COPY_SECTION, copy_section); - - const Vector2 vertpos[4] = { - Vector2(-1, -1), - Vector2(-1, 1), - Vector2(1, 1), - Vector2(1, -1), - }; - - const Vector2 uvpos[4] = { - Vector2(0, 0), - Vector2(0, 1), - Vector2(1, 1), - Vector2(1, 0) - }; - - const int indexpos[6] = { - 0, 1, 2, - 2, 3, 0 - }; - - _draw_polygon(indexpos, 6, 4, vertpos, uvpos, NULL, false); - - storage->shaders.copy.set_conditional(CopyShaderGLES3::USE_COPY_SECTION, false); - storage->shaders.copy.set_conditional(CopyShaderGLES3::USE_NO_ALPHA, false); - - storage->bind_framebuffer(storage->frame.current_rt->fbo); - glEnable(GL_BLEND); -} - -void RasterizerCanvasBaseGLES3::canvas_light_shadow_buffer_update(RID p_buffer, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders, CameraMatrix *p_xform_cache) { -#if 0 - RasterizerStorageGLES3::CanvasLightShadow *cls = storage->canvas_light_shadow_owner.get(p_buffer); - ERR_FAIL_COND(!cls); - - glDisable(GL_BLEND); - glDisable(GL_SCISSOR_TEST); - glDisable(GL_DITHER); - glDisable(GL_CULL_FACE); - glDepthFunc(GL_LEQUAL); - glEnable(GL_DEPTH_TEST); - glDepthMask(true); - - glBindFramebuffer(GL_FRAMEBUFFER, cls->fbo); - - state.canvas_shadow_shader.set_conditional(CanvasShadowShaderGLES3::USE_RGBA_SHADOWS, storage->config.use_rgba_2d_shadows); - state.canvas_shadow_shader.bind(); - - glViewport(0, 0, cls->size, cls->height); - glClearDepth(1.0f); - glClearColor(1, 1, 1, 1); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - RS::CanvasOccluderPolygonCullMode cull = RS::CANVAS_OCCLUDER_POLYGON_CULL_DISABLED; - - for (int i = 0; i < 4; i++) { - //make sure it remains orthogonal, makes easy to read angle later - - Transform3D light; - light.origin[0] = p_light_xform[2][0]; - light.origin[1] = p_light_xform[2][1]; - light.basis[0][0] = p_light_xform[0][0]; - light.basis[0][1] = p_light_xform[1][0]; - light.basis[1][0] = p_light_xform[0][1]; - light.basis[1][1] = p_light_xform[1][1]; - - //light.basis.scale(Vector3(to_light.elements[0].length(),to_light.elements[1].length(),1)); - - //p_near=1; - CameraMatrix projection; - { - real_t fov = 90; - real_t nearp = p_near; - real_t farp = p_far; - real_t aspect = 1.0; - - real_t ymax = nearp * Math::tan(Math::deg2rad(fov * 0.5)); - real_t ymin = -ymax; - real_t xmin = ymin * aspect; - real_t xmax = ymax * aspect; - - projection.set_frustum(xmin, xmax, ymin, ymax, nearp, farp); - } - - Vector3 cam_target = Basis(Vector3(0, 0, Math_PI * 2 * (i / 4.0))).xform(Vector3(0, 1, 0)); - projection = projection * CameraMatrix(Transform3D().looking_at(cam_target, Vector3(0, 0, -1)).affine_inverse()); - - state.canvas_shadow_shader.set_uniform(CanvasShadowShaderGLES3::PROJECTION_MATRIX, projection); - state.canvas_shadow_shader.set_uniform(CanvasShadowShaderGLES3::LIGHT_MATRIX, light); - state.canvas_shadow_shader.set_uniform(CanvasShadowShaderGLES3::DISTANCE_NORM, 1.0 / p_far); - - if (i == 0) - *p_xform_cache = projection; - - glViewport(0, (cls->height / 4) * i, cls->size, cls->height / 4); - - LightOccluderInstance *instance = p_occluders; - - while (instance) { - RasterizerStorageGLES3::CanvasOccluder *cc = storage->canvas_occluder_owner.get_or_null(instance->polygon_buffer); - if (!cc || cc->len == 0 || !(p_light_mask & instance->light_mask)) { - instance = instance->next; - continue; - } - - state.canvas_shadow_shader.set_uniform(CanvasShadowShaderGLES3::WORLD_MATRIX, instance->xform_cache); - - RS::CanvasOccluderPolygonCullMode transformed_cull_cache = instance->cull_cache; - - if (transformed_cull_cache != RS::CANVAS_OCCLUDER_POLYGON_CULL_DISABLED && - (p_light_xform.basis_determinant() * instance->xform_cache.basis_determinant()) < 0) { - transformed_cull_cache = - transformed_cull_cache == RS::CANVAS_OCCLUDER_POLYGON_CULL_CLOCKWISE ? - RS::CANVAS_OCCLUDER_POLYGON_CULL_COUNTER_CLOCKWISE : - RS::CANVAS_OCCLUDER_POLYGON_CULL_CLOCKWISE; - } - - if (cull != transformed_cull_cache) { - cull = transformed_cull_cache; - switch (cull) { - case RS::CANVAS_OCCLUDER_POLYGON_CULL_DISABLED: { - glDisable(GL_CULL_FACE); - - } break; - case RS::CANVAS_OCCLUDER_POLYGON_CULL_CLOCKWISE: { - glEnable(GL_CULL_FACE); - glCullFace(GL_FRONT); - } break; - case RS::CANVAS_OCCLUDER_POLYGON_CULL_COUNTER_CLOCKWISE: { - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); - - } break; - } - } - - glBindBuffer(GL_ARRAY_BUFFER, cc->vertex_id); - glEnableVertexAttribArray(RS::ARRAY_VERTEX); - glVertexAttribPointer(RS::ARRAY_VERTEX, 3, GL_FLOAT, false, 0, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cc->index_id); - - glDrawElements(GL_TRIANGLES, cc->len * 3, GL_UNSIGNED_SHORT, 0); - - instance = instance->next; - } - } - - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); -#endif -} - -void RasterizerCanvasBaseGLES3::draw_lens_distortion_rect(const Rect2 &p_rect, float p_k1, float p_k2, const Vector2 &p_eye_center, float p_oversample) { - Vector2 half_size; - if (storage->frame.current_rt) { - half_size = Vector2(storage->frame.current_rt->width, storage->frame.current_rt->height); - } else { - // half_size = OS::get_singleton()->get_window_size(); - half_size = Vector2(storage->_dims.win_width, storage->_dims.win_height); - } - half_size *= 0.5; - Vector2 offset((p_rect.position.x - half_size.x) / half_size.x, (p_rect.position.y - half_size.y) / half_size.y); - Vector2 scale(p_rect.size.x / half_size.x, p_rect.size.y / half_size.y); - - float aspect_ratio = p_rect.size.x / p_rect.size.y; - - // setup our lens shader - state.lens_shader.bind(); - state.lens_shader.set_uniform(LensDistortedShaderGLES3::OFFSET, offset); - state.lens_shader.set_uniform(LensDistortedShaderGLES3::SCALE, scale); - state.lens_shader.set_uniform(LensDistortedShaderGLES3::K1, p_k1); - state.lens_shader.set_uniform(LensDistortedShaderGLES3::K2, p_k2); - state.lens_shader.set_uniform(LensDistortedShaderGLES3::EYE_CENTER, p_eye_center); - state.lens_shader.set_uniform(LensDistortedShaderGLES3::UPSCALE, p_oversample); - state.lens_shader.set_uniform(LensDistortedShaderGLES3::ASPECT_RATIO, aspect_ratio); - - // bind our quad buffer - _bind_quad_buffer(); - - // and draw - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - - // and cleanup - glBindBuffer(GL_ARRAY_BUFFER, 0); -} - -void RasterizerCanvasBaseGLES3::initialize() { - bool flag_stream = false; - //flag_stream = GLOBAL_GET("rendering/options/api_usage_legacy/flag_stream"); - if (flag_stream) - _buffer_upload_usage_flag = GL_STREAM_DRAW; - else - _buffer_upload_usage_flag = GL_DYNAMIC_DRAW; - - // quad buffer - { - glGenBuffers(1, &data.canvas_quad_vertices); - glBindBuffer(GL_ARRAY_BUFFER, data.canvas_quad_vertices); - - const float qv[8] = { - 0, 0, - 0, 1, - 1, 1, - 1, 0 - }; - - glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 8, qv, GL_STATIC_DRAW); - - glBindBuffer(GL_ARRAY_BUFFER, 0); - - glGenVertexArrays(1, &data.canvas_quad_array); - glBindVertexArray(data.canvas_quad_array); - glBindBuffer(GL_ARRAY_BUFFER, data.canvas_quad_vertices); - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, nullptr); - glEnableVertexAttribArray(0); - glBindVertexArray(0); - glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind - } - - { - //particle quad buffers - - glGenBuffers(1, &data.particle_quad_vertices); - glBindBuffer(GL_ARRAY_BUFFER, data.particle_quad_vertices); - { - //quad of size 1, with pivot on the center for particles, then regular UVS. Color is general plus fetched from particle - const float qv[16] = { - -0.5, -0.5, - 0.0, 0.0, - -0.5, 0.5, - 0.0, 1.0, - 0.5, 0.5, - 1.0, 1.0, - 0.5, -0.5, - 1.0, 0.0 - }; - - glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 16, qv, GL_STATIC_DRAW); - } - - glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind - - glGenVertexArrays(1, &data.particle_quad_array); - glBindVertexArray(data.particle_quad_array); - glBindBuffer(GL_ARRAY_BUFFER, data.particle_quad_vertices); - glEnableVertexAttribArray(RS::ARRAY_VERTEX); - glVertexAttribPointer(RS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, nullptr); - glEnableVertexAttribArray(RS::ARRAY_TEX_UV); - glVertexAttribPointer(RS::ARRAY_TEX_UV, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, CAST_INT_TO_UCHAR_PTR(8)); - glBindVertexArray(0); - glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind - } - - // polygon buffer - { - uint32_t poly_size = 128; //GLOBAL_DEF_RST("rendering/limits/buffers/canvas_polygon_buffer_size_kb", 128); - ProjectSettings::get_singleton()->set_custom_property_info("rendering/limits/buffers/canvas_polygon_buffer_size_kb", PropertyInfo(Variant::INT, "rendering/limits/buffers/canvas_polygon_buffer_size_kb", PROPERTY_HINT_RANGE, "0,256,1,or_greater")); - poly_size = MAX(poly_size, 2); // minimum 2k, may still see anomalies in editor - poly_size *= 1024; //kb - glGenBuffers(1, &data.polygon_buffer); - glBindBuffer(GL_ARRAY_BUFFER, data.polygon_buffer); - glBufferData(GL_ARRAY_BUFFER, poly_size, nullptr, GL_DYNAMIC_DRAW); //allocate max size - glBindBuffer(GL_ARRAY_BUFFER, 0); - data.polygon_buffer_size = poly_size; - - //quad arrays - for (int i = 0; i < Data::NUM_QUAD_ARRAY_VARIATIONS; i++) { - glGenVertexArrays(1, &data.polygon_buffer_quad_arrays[i]); - glBindVertexArray(data.polygon_buffer_quad_arrays[i]); - glBindBuffer(GL_ARRAY_BUFFER, data.polygon_buffer); - - int uv_ofs = 0; - int color_ofs = 0; - int light_angle_ofs = 0; - int stride = 2 * 4; - - if (i & 1) { //color - color_ofs = stride; - stride += 4 * 4; - } - - if (i & 2) { //uv - uv_ofs = stride; - stride += 2 * 4; - } - - if (i & 4) { //light_angle - light_angle_ofs = stride; - stride += 1 * 4; - } - - glEnableVertexAttribArray(RS::ARRAY_VERTEX); - glVertexAttribPointer(RS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, stride, nullptr); - - if (i & 1) { - glEnableVertexAttribArray(RS::ARRAY_COLOR); - glVertexAttribPointer(RS::ARRAY_COLOR, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(color_ofs)); - } - - if (i & 2) { - glEnableVertexAttribArray(RS::ARRAY_TEX_UV); - glVertexAttribPointer(RS::ARRAY_TEX_UV, 2, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(uv_ofs)); - } - - if (i & 4) { - // reusing tangent for light_angle - glEnableVertexAttribArray(RS::ARRAY_TANGENT); - glVertexAttribPointer(RS::ARRAY_TANGENT, 1, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(light_angle_ofs)); - } - - glBindVertexArray(0); - } - - glGenVertexArrays(1, &data.polygon_buffer_pointer_array); - - uint32_t index_size = 128; //GLOBAL_DEF_RST("rendering/limits/buffers/canvas_polygon_index_buffer_size_kb", 128); - ProjectSettings::get_singleton()->set_custom_property_info("rendering/limits/buffers/canvas_polygon_index_buffer_size_kb", PropertyInfo(Variant::INT, "rendering/limits/buffers/canvas_polygon_index_buffer_size_kb", PROPERTY_HINT_RANGE, "0,256,1,or_greater")); - index_size = MAX(index_size, 2); - index_size *= 1024; //kb - glGenBuffers(1, &data.polygon_index_buffer); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.polygon_index_buffer); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, index_size, nullptr, GL_DYNAMIC_DRAW); //allocate max size - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - - data.polygon_index_buffer_size = index_size; - } - - // ninepatch buffers - { - // array buffer - glGenBuffers(1, &data.ninepatch_vertices); - glBindBuffer(GL_ARRAY_BUFFER, data.ninepatch_vertices); - - glBufferData(GL_ARRAY_BUFFER, sizeof(float) * (16 + 16) * 2, NULL, GL_DYNAMIC_DRAW); - - glBindBuffer(GL_ARRAY_BUFFER, 0); - - // element buffer - glGenBuffers(1, &data.ninepatch_elements); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.ninepatch_elements); - -#define _EIDX(y, x) (y * 4 + x) - uint8_t elems[3 * 2 * 9] = { - // first row - - _EIDX(0, 0), _EIDX(0, 1), _EIDX(1, 1), - _EIDX(1, 1), _EIDX(1, 0), _EIDX(0, 0), - - _EIDX(0, 1), _EIDX(0, 2), _EIDX(1, 2), - _EIDX(1, 2), _EIDX(1, 1), _EIDX(0, 1), - - _EIDX(0, 2), _EIDX(0, 3), _EIDX(1, 3), - _EIDX(1, 3), _EIDX(1, 2), _EIDX(0, 2), - - // second row - - _EIDX(1, 0), _EIDX(1, 1), _EIDX(2, 1), - _EIDX(2, 1), _EIDX(2, 0), _EIDX(1, 0), - - // the center one would be here, but we'll put it at the end - // so it's easier to disable the center and be able to use - // one draw call for both - - _EIDX(1, 2), _EIDX(1, 3), _EIDX(2, 3), - _EIDX(2, 3), _EIDX(2, 2), _EIDX(1, 2), - - // third row - - _EIDX(2, 0), _EIDX(2, 1), _EIDX(3, 1), - _EIDX(3, 1), _EIDX(3, 0), _EIDX(2, 0), - - _EIDX(2, 1), _EIDX(2, 2), _EIDX(3, 2), - _EIDX(3, 2), _EIDX(3, 1), _EIDX(2, 1), - - _EIDX(2, 2), _EIDX(2, 3), _EIDX(3, 3), - _EIDX(3, 3), _EIDX(3, 2), _EIDX(2, 2), - - // center field - - _EIDX(1, 1), _EIDX(1, 2), _EIDX(2, 2), - _EIDX(2, 2), _EIDX(2, 1), _EIDX(1, 1) - }; -#undef _EIDX - - glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elems), elems, GL_STATIC_DRAW); - - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - } - - store_transform3d(Transform3D(), state.canvas_item_ubo_data.projection_matrix); - - glGenBuffers(1, &state.canvas_item_ubo); - glBindBuffer(GL_UNIFORM_BUFFER, state.canvas_item_ubo); - glBufferData(GL_UNIFORM_BUFFER, sizeof(CanvasItemUBO), &state.canvas_item_ubo_data, GL_DYNAMIC_DRAW); - glBindBuffer(GL_UNIFORM_BUFFER, 0); - - state.canvas_shadow_shader.init(); - state.canvas_shader.init(); - _set_texture_rect_mode(true); - state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_RGBA_SHADOWS, storage->config.use_rgba_2d_shadows); - - state.canvas_shader.bind(); - - state.lens_shader.init(); - - state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_PIXEL_SNAP, GLOBAL_DEF("rendering/quality/2d/use_pixel_snap", false)); - - state.using_light = NULL; - state.using_transparent_rt = false; - state.using_skeleton = false; -} - -RendererCanvasRender::PolygonID RasterizerCanvasBaseGLES3::request_polygon(const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, const Vector<int> &p_bones, const Vector<float> &p_weights) { - uint32_t id = _polydata.alloc(); - PolyData &pd = _polydata[id]; - pd.indices = p_indices; - pd.points = p_points; - pd.colors = p_colors; - pd.uvs = p_uvs; - return id; -} -void RasterizerCanvasBaseGLES3::free_polygon(PolygonID p_polygon) { - _polydata.free(p_polygon); -} - -void RasterizerCanvasBaseGLES3::finalize() { - glDeleteBuffers(1, &data.canvas_quad_vertices); - glDeleteVertexArrays(1, &data.canvas_quad_array); - - glDeleteBuffers(1, &data.canvas_quad_vertices); - glDeleteVertexArrays(1, &data.canvas_quad_array); - - glDeleteVertexArrays(1, &data.polygon_buffer_pointer_array); -} - -RasterizerCanvasBaseGLES3::RasterizerCanvasBaseGLES3() { -} - -#endif // GLES3_BACKEND_ENABLED diff --git a/drivers/gles3/rasterizer_canvas_base_gles3.h b/drivers/gles3/rasterizer_canvas_base_gles3.h deleted file mode 100644 index 60292ff875..0000000000 --- a/drivers/gles3/rasterizer_canvas_base_gles3.h +++ /dev/null @@ -1,213 +0,0 @@ -/*************************************************************************/ -/* rasterizer_canvas_base_gles3.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef RASTERIZER_CANVAS_BASE_OPENGL_H -#define RASTERIZER_CANVAS_BASE_OPENGL_H - -#include "drivers/gles3/rasterizer_platforms.h" -#ifdef GLES3_BACKEND_ENABLED - -#include "drivers/gles3/rasterizer_array.h" -#include "drivers/gles3/rasterizer_storage_common.h" -#include "rasterizer_scene_gles3.h" -#include "rasterizer_storage_gles3.h" -#include "servers/rendering/renderer_canvas_render.h" -#include "servers/rendering/renderer_compositor.h" - -#include "shaders/canvas.glsl.gen.h" -#include "shaders/canvas_shadow.glsl.gen.h" -#include "shaders/lens_distorted.glsl.gen.h" - -class RasterizerCanvasBaseGLES3 : public RendererCanvasRender { -public: - enum { - INSTANCE_ATTRIB_BASE = 8, - }; - - struct Uniforms { - Transform3D projection_matrix; - - Transform2D modelview_matrix; - Transform2D extra_matrix; - - Color final_modulate; - - float time; - }; - - struct CanvasItemUBO { - float projection_matrix[16]; - float time; - uint8_t padding[12]; - }; - - struct Data { - enum { NUM_QUAD_ARRAY_VARIATIONS = 8 }; - - GLuint canvas_quad_vertices; - GLuint canvas_quad_array; - - GLuint polygon_buffer; - GLuint polygon_buffer_quad_arrays[NUM_QUAD_ARRAY_VARIATIONS]; - GLuint polygon_buffer_pointer_array; - GLuint polygon_index_buffer; - - GLuint particle_quad_vertices; - GLuint particle_quad_array; - - uint32_t polygon_buffer_size; - uint32_t polygon_index_buffer_size; - - GLuint ninepatch_vertices; - GLuint ninepatch_elements; - } data; - - struct State { - Uniforms uniforms; - CanvasItemUBO canvas_item_ubo_data; - GLuint canvas_item_ubo; - bool canvas_texscreen_used; - CanvasShaderGLES3 canvas_shader; - CanvasShadowShaderGLES3 canvas_shadow_shader; - LensDistortedShaderGLES3 lens_shader; - - bool using_texture_rect; - - bool using_light_angle; - bool using_modulate; - bool using_large_vertex; - - bool using_ninepatch; - bool using_skeleton; - - Transform2D skeleton_transform; - Transform2D skeleton_transform_inverse; - Size2i skeleton_texture_size; - - RID current_tex; - RID current_normal; - RasterizerStorageGLES3::Texture *current_tex_ptr; - - Transform3D vp; - Light *using_light; - bool using_shadow; - bool using_transparent_rt; - - // new for Godot 4.0 - // min mag filter is per item, and repeat - RS::CanvasItemTextureFilter current_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR; - RS::CanvasItemTextureRepeat current_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED; - } state; - - typedef void Texture; - - RasterizerSceneGLES3 *scene_render; - - RasterizerStorageGLES3 *storage; - - // allow user to choose api usage - GLenum _buffer_upload_usage_flag; - - void _set_uniforms(); - - virtual RID light_internal_create(); - virtual void light_internal_update(RID p_rid, Light *p_light); - virtual void light_internal_free(RID p_rid); - - virtual void canvas_begin(); - virtual void canvas_end(); - -protected: - void _legacy_draw_primitive(Item::CommandPrimitive *p_pr, RasterizerStorageGLES3::Material *p_material); - void _legacy_draw_line(Item::CommandPrimitive *p_pr, RasterizerStorageGLES3::Material *p_material); - void _legacy_draw_poly_triangles(Item::CommandPolygon *p_poly, RasterizerStorageGLES3::Material *p_material); - -public: - void _draw_gui_primitive(int p_points, const Vector2 *p_vertices, const Color *p_colors, const Vector2 *p_uvs, const float *p_light_angles = nullptr); - void _draw_polygon(const int *p_indices, int p_index_count, int p_vertex_count, const Vector2 *p_vertices, const Vector2 *p_uvs, const Color *p_colors, bool p_singlecolor, const float *p_weights = NULL, const int *p_bones = NULL); - void _draw_generic(GLuint p_primitive, int p_vertex_count, const Vector2 *p_vertices, const Vector2 *p_uvs, const Color *p_colors, bool p_singlecolor); - void _draw_generic_indices(GLuint p_primitive, const int *p_indices, int p_index_count, int p_vertex_count, const Vector2 *p_vertices, const Vector2 *p_uvs, const Color *p_colors, bool p_singlecolor); - - void _bind_quad_buffer(); - void _copy_texscreen(const Rect2 &p_rect); - void _copy_screen(const Rect2 &p_rect); - - //virtual void draw_window_margins(int *black_margin, RID *black_image) override; - void draw_generic_textured_rect(const Rect2 &p_rect, const Rect2 &p_src); - void draw_lens_distortion_rect(const Rect2 &p_rect, float p_k1, float p_k2, const Vector2 &p_eye_center, float p_oversample); - - virtual void reset_canvas(); - virtual void canvas_light_shadow_buffer_update(RID p_buffer, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders, CameraMatrix *p_xform_cache); - - // Copied from RasterizerCanvasDummy: - virtual void canvas_debug_viewport_shadows(Light *p_lights_with_shadow) override; - - 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) 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; - // End copied from RasterizerCanvasDummy. - - RasterizerStorageGLES3::Texture *_bind_canvas_texture(const RID &p_texture, const RID &p_normal_map); - void _set_texture_rect_mode(bool p_texture_rect, bool p_light_angle = false, bool p_modulate = false, bool p_large_vertex = false); - - // NEW API - struct PolyData { - LocalVector<int> indices; - LocalVector<Point2> points; - LocalVector<Color> colors; - LocalVector<Point2> uvs; - }; - - 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>()) override; - void free_polygon(PolygonID p_polygon) override; - - RasterizerPooledIndirectList<PolyData> _polydata; - - ////////////////////// - void initialize(); - void finalize(); - - RasterizerCanvasBaseGLES3(); -}; - -#endif // GLES3_BACKEND_ENABLED - -#endif // RASTERIZER_CANVAS_BASE_OPENGL_H diff --git a/drivers/gles3/rasterizer_canvas_batcher.h b/drivers/gles3/rasterizer_canvas_batcher.h deleted file mode 100644 index c505d46859..0000000000 --- a/drivers/gles3/rasterizer_canvas_batcher.h +++ /dev/null @@ -1,1560 +0,0 @@ -/*************************************************************************/ -/* rasterizer_canvas_batcher.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef RASTERIZER_CANVAS_BATCHER_H -#define RASTERIZER_CANVAS_BATCHER_H - -#include "core/os/os.h" -#include "core/templates/local_vector.h" -#include "rasterizer_array.h" -#include "rasterizer_asserts.h" -#include "rasterizer_storage_common.h" - -#include "core/config/project_settings.h" -#include "servers/rendering/renderer_compositor.h" - -// We are using the curiously recurring template pattern -// https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern -// For static polymorphism. - -// This makes it super easy to access -// data / call funcs in the derived rasterizers from the base without writing and -// maintaining a boatload of virtual functions. -// In addition it assures that vtable will not be used and the function calls can be optimized, -// because it gives compile time static polymorphism. - -// These macros makes it simpler and less verbose to define (and redefine) the inline functions -// template preamble -#define T_PREAMBLE template <class T, typename T_STORAGE> -// class preamble -#define C_PREAMBLE RasterizerCanvasBatcher<T, T_STORAGE> -// generic preamble -#define PREAMBLE(RET_T) \ - T_PREAMBLE \ - RET_T C_PREAMBLE - -template <class T, typename T_STORAGE> -class RasterizerCanvasBatcher { -public: - // used to determine whether we use hardware transform (none) - // software transform all verts, or software transform just a translate - // (no rotate or scale) - enum TransformMode { - TM_NONE, - TM_ALL, - TM_TRANSLATE, - }; - - // pod versions of vector and color and RID, need to be 32 bit for vertex format - struct BatchVector2 { - float x, y; - void set(float xx, float yy) { - x = xx; - y = yy; - } - void set(const Vector2 &p_o) { - x = p_o.x; - y = p_o.y; - } - void to(Vector2 &r_o) const { - r_o.x = x; - r_o.y = y; - } - }; - - struct BatchColor { - float r, g, b, a; - void set_white() { - r = 1.0f; - g = 1.0f; - b = 1.0f; - a = 1.0f; - } - void set(const Color &p_c) { - r = p_c.r; - g = p_c.g; - b = p_c.b; - a = p_c.a; - } - void set(float rr, float gg, float bb, float aa) { - r = rr; - g = gg; - b = bb; - a = aa; - } - bool operator==(const BatchColor &p_c) const { - return (r == p_c.r) && (g == p_c.g) && (b == p_c.b) && (a == p_c.a); - } - bool operator!=(const BatchColor &p_c) const { return (*this == p_c) == false; } - bool equals(const Color &p_c) const { - return (r == p_c.r) && (g == p_c.g) && (b == p_c.b) && (a == p_c.a); - } - const float *get_data() const { return &r; } - String to_string() const { - String sz = "{"; - const float *data = get_data(); - for (int c = 0; c < 4; c++) { - float f = data[c]; - int val = ((f * 255.0f) + 0.5f); - sz += String(Variant(val)) + " "; - } - sz += "}"; - return sz; - } - }; - - // simplest FVF - local or baked position - struct BatchVertex { - // must be 32 bit pod - BatchVector2 pos; - BatchVector2 uv; - }; - - // simple FVF but also incorporating baked color - struct BatchVertexColored : public BatchVertex { - // must be 32 bit pod - BatchColor col; - }; - - // if we are using normal mapping, we need light angles to be sent - struct BatchVertexLightAngled : public BatchVertexColored { - // must be pod - float light_angle; - }; - - // CUSTOM SHADER vertex formats. These are larger but will probably - // be needed with custom shaders in order to have the data accessible in the shader. - - // if we are using COLOR in vertex shader but not position (VERTEX) - struct BatchVertexModulated : public BatchVertexLightAngled { - BatchColor modulate; - }; - - struct BatchTransform { - BatchVector2 translate; - BatchVector2 basis[2]; - }; - - // last resort, specially for custom shader, we put everything possible into a huge FVF - // not very efficient, but better than no batching at all. - struct BatchVertexLarge : public BatchVertexModulated { - // must be pod - BatchTransform transform; - }; - - // Batch should be as small as possible, and ideally nicely aligned (is 32 bytes at the moment) - struct Batch { - RasterizerStorageCommon::BatchType type; // should be 16 bit - uint16_t batch_texture_id; - - // also item reference number - uint32_t first_command; - - // in the case of DEFAULT, this is num commands. - // with rects, is number of command and rects. - // with lines, is number of lines - uint32_t num_commands; - - // first vertex of this batch in the vertex lists - uint32_t first_vert; - - BatchColor color; - }; - - struct BatchTex { - enum TileMode : uint32_t { - TILE_OFF, - TILE_NORMAL, - TILE_FORCE_REPEAT, - }; - RID RID_texture; - RID RID_normal; - TileMode tile_mode; - BatchVector2 tex_pixel_size; - uint32_t flags; - }; - - // items in a list to be sorted prior to joining - struct BSortItem { - // have a function to keep as pod, rather than operator - void assign(const BSortItem &o) { - item = o.item; - z_index = o.z_index; - } - RendererCanvasRender::Item *item; - int z_index; - }; - - // batch item may represent 1 or more items - struct BItemJoined { - uint32_t first_item_ref; - uint32_t num_item_refs; - - Rect2 bounding_rect; - - // note the z_index may only be correct for the first of the joined item references - // this has implications for light culling with z ranged lights. - int16_t z_index; - - // these are defined in RasterizerStorageCommon::BatchFlags - uint16_t flags; - - // we are always splitting items with lots of commands, - // and items with unhandled primitives (default) - bool use_hardware_transform() const { return num_item_refs == 1; } - }; - - struct BItemRef { - RendererCanvasRender::Item *item; - Color final_modulate; - }; - - struct BLightRegion { - void reset() { - light_bitfield = 0; - shadow_bitfield = 0; - too_many_lights = false; - } - uint64_t light_bitfield; - uint64_t shadow_bitfield; - bool too_many_lights; // we can only do light region optimization if there are 64 or less lights - }; - - struct BatchData { - BatchData() { - reset_flush(); - reset_joined_item(); - - gl_vertex_buffer = 0; - gl_index_buffer = 0; - max_quads = 0; - vertex_buffer_size_units = 0; - vertex_buffer_size_bytes = 0; - index_buffer_size_units = 0; - index_buffer_size_bytes = 0; - - use_colored_vertices = false; - - settings_use_batching = false; - settings_max_join_item_commands = 0; - settings_colored_vertex_format_threshold = 0.0f; - settings_batch_buffer_num_verts = 0; - scissor_threshold_area = 0.0f; - joined_item_batch_flags = 0; - diagnose_frame = false; - next_diagnose_tick = 10000; - diagnose_frame_number = 9999999999; // some high number - join_across_z_indices = true; - settings_item_reordering_lookahead = 0; - - settings_use_batching_original_choice = false; - settings_flash_batching = false; - settings_diagnose_frame = false; - settings_scissor_lights = false; - settings_scissor_threshold = -1.0f; - settings_use_single_rect_fallback = false; - settings_use_software_skinning = true; - settings_ninepatch_mode = 0; // default - settings_light_max_join_items = 16; - - settings_uv_contract = false; - settings_uv_contract_amount = 0.0f; - - buffer_mode_batch_upload_send_null = true; - buffer_mode_batch_upload_flag_stream = false; - - stats_items_sorted = 0; - stats_light_items_joined = 0; - } - - // called for each joined item - void reset_joined_item() { - // noop but left in as a stub - } - - // called after each flush - void reset_flush() { - batches.reset(); - batch_textures.reset(); - - vertices.reset(); - light_angles.reset(); - vertex_colors.reset(); - vertex_modulates.reset(); - vertex_transforms.reset(); - - total_quads = 0; - total_verts = 0; - total_color_changes = 0; - - use_light_angles = false; - use_modulate = false; - use_large_verts = false; - fvf = RasterizerStorageCommon::FVF_REGULAR; - } - - unsigned int gl_vertex_buffer; - unsigned int gl_index_buffer; - - uint32_t max_quads; - uint32_t vertex_buffer_size_units; - uint32_t vertex_buffer_size_bytes; - uint32_t index_buffer_size_units; - uint32_t index_buffer_size_bytes; - - // small vertex FVF type - pos and UV. - // This will always be written to initially, but can be translated - // to larger FVFs if necessary. - RasterizerArray<BatchVertex> vertices; - - // extra data which can be stored during prefilling, for later translation to larger FVFs - RasterizerArray<float> light_angles; - RasterizerArray<BatchColor> vertex_colors; // these aren't usually used, but are for polys - RasterizerArray<BatchColor> vertex_modulates; - RasterizerArray<BatchTransform> vertex_transforms; - - // instead of having a different buffer for each vertex FVF type - // we have a special array big enough for the biggest FVF - // which can have a changeable unit size, and reuse it. - RasterizerUnitArray unit_vertices; - - RasterizerArray<Batch> batches; - RasterizerArray<Batch> batches_temp; // used for translating to colored vertex batches - RasterizerArray_non_pod<BatchTex> batch_textures; // the only reason this is non-POD is because of RIDs - - // SHOULD THESE BE IN FILLSTATE? - // flexible vertex format. - // all verts have pos and UV. - // some have color, some light angles etc. - RasterizerStorageCommon::FVF fvf; - bool use_colored_vertices; - bool use_light_angles; - bool use_modulate; - bool use_large_verts; - - // if the shader is using MODULATE, we prevent baking color so the final_modulate can - // be read in the shader. - // if the shader is reading VERTEX, we prevent baking vertex positions with extra matrices etc - // to prevent the read position being incorrect. - // These flags are defined in RasterizerStorageCommon::BatchFlags - uint32_t joined_item_batch_flags; - - RasterizerArray<BItemJoined> items_joined; - RasterizerArray<BItemRef> item_refs; - - // items are sorted prior to joining - RasterizerArray<BSortItem> sort_items; - - // new for Godot 4 .. the client outputs a linked list so we need to convert this - // to a linear array - LocalVector<RendererCanvasRender::Item::Command *> command_shortlist; - - // counts - int total_quads; - int total_verts; - - // we keep a record of how many color changes caused new batches - // if the colors are causing an excessive number of batches, we switch - // to alternate batching method and add color to the vertex format. - int total_color_changes; - - // measured in pixels, recalculated each frame - float scissor_threshold_area; - - // diagnose this frame, every nTh frame when settings_diagnose_frame is on - bool diagnose_frame; - String frame_string; - uint32_t next_diagnose_tick; - uint64_t diagnose_frame_number; - - // whether to join items across z_indices - this can interfere with z ranged lights, - // so has to be disabled in some circumstances - bool join_across_z_indices; - - // global settings - bool settings_use_batching; // the current use_batching (affected by flash) - bool settings_use_batching_original_choice; // the choice entered in project settings - bool settings_flash_batching; // for regression testing, flash between non-batched and batched renderer - bool settings_diagnose_frame; // print out batches to help optimize / regression test - int settings_max_join_item_commands; - float settings_colored_vertex_format_threshold; - int settings_batch_buffer_num_verts; - bool settings_scissor_lights; - float settings_scissor_threshold; // 0.0 to 1.0 - int settings_item_reordering_lookahead; - bool settings_use_single_rect_fallback; - bool settings_use_software_skinning; - int settings_light_max_join_items; - int settings_ninepatch_mode; - - // buffer orphaning modes - bool buffer_mode_batch_upload_send_null; - bool buffer_mode_batch_upload_flag_stream; - - // uv contraction - bool settings_uv_contract; - float settings_uv_contract_amount; - - // only done on diagnose frame - void reset_stats() { - stats_items_sorted = 0; - stats_light_items_joined = 0; - } - - // frame stats (just for monitoring and debugging) - int stats_items_sorted; - int stats_light_items_joined; - } bdata; - - struct FillState { - void reset_flush() { - // don't reset members that need to be preserved after flushing - // half way through a list of commands - curr_batch = 0; - batch_tex_id = -1; - texpixel_size = Vector2(1, 1); - contract_uvs = false; - - sequence_batch_type_flags = 0; - } - - void reset_joined_item(bool p_use_hardware_transform) { - reset_flush(); - use_hardware_transform = p_use_hardware_transform; - extra_matrix_sent = false; - } - - // for batching multiple types, we don't allow mixing RECTs / LINEs etc. - // using flags allows quicker rejection of sequences with different batch types - uint32_t sequence_batch_type_flags; - - Batch *curr_batch; - int batch_tex_id; - bool use_hardware_transform; - bool contract_uvs; - Vector2 texpixel_size; - Color final_modulate; - TransformMode transform_mode; - TransformMode orig_transform_mode; - - // support for extra matrices - bool extra_matrix_sent; // whether sent on this item (in which case software transform can't be used untl end of item) - int transform_extra_command_number_p1; // plus one to allow fast checking against zero - Transform2D transform_combined; // final * extra - }; - - // used during try_join - struct RenderItemState { - RenderItemState() { reset(); } - void reset() { - current_clip = nullptr; - shader_cache = nullptr; - rebind_shader = true; - prev_use_skeleton = false; - last_blend_mode = -1; - canvas_last_material = RID(); - item_group_z = 0; - item_group_light = nullptr; - final_modulate = Color(-1.0, -1.0, -1.0, -1.0); // just something unlikely - - joined_item_batch_type_flags_curr = 0; - joined_item_batch_type_flags_prev = 0; - - joined_item = nullptr; - } - - RendererCanvasRender::Item *current_clip; - typename T_STORAGE::Shader *shader_cache; - bool rebind_shader; - bool prev_use_skeleton; - bool prev_distance_field; - int last_blend_mode; - RID canvas_last_material; - Color final_modulate; - - // used for joining items only - BItemJoined *joined_item; - bool join_batch_break; - BLightRegion light_region; - - // we need some logic to prevent joining items that have vastly different batch types - // these are defined in RasterizerStorageCommon::BatchTypeFlags - uint32_t joined_item_batch_type_flags_curr; - uint32_t joined_item_batch_type_flags_prev; - - // 'item group' is data over a single call to canvas_render_items - int item_group_z; - Color item_group_modulate; - RendererCanvasRender::Light *item_group_light; - Transform2D item_group_base_transform; - } _render_item_state; - - bool use_nvidia_rect_workaround; - - ////////////////////////////////////////////////////////////////////////////// - // End of structs used by the batcher. Beginning of funcs. -private: - // curiously recurring template pattern - allows access to functions in the DERIVED class - // this is kind of like using virtual functions but more efficient as they are resolved at compile time - T_STORAGE *get_storage() { return static_cast<const T *>(this)->storage; } - const T_STORAGE *get_storage() const { return static_cast<const T *>(this)->storage; } - T *get_this() { return static_cast<T *>(this); } - const T *get_this() const { return static_cast<const T *>(this); } - -protected: - // main functions called from the rasterizer canvas - void batch_constructor(); - void batch_initialize(); - - void batch_canvas_begin(); - void batch_canvas_end(); - void batch_canvas_render_items_begin(const Color &p_modulate, RendererCanvasRender::Light *p_light, const Transform2D &p_base_transform); - void batch_canvas_render_items_end(); - void batch_canvas_render_items(RendererCanvasRender::Item *p_item_list, int p_z, const Color &p_modulate, RendererCanvasRender::Light *p_light, const Transform2D &p_base_transform); - - // recording and sorting items from the initial pass - void record_items(RendererCanvasRender::Item *p_item_list, int p_z); - void join_sorted_items(); - void sort_items(); - bool _sort_items_match(const BSortItem &p_a, const BSortItem &p_b) const; - bool sort_items_from(int p_start); - - // joining logic - bool _disallow_item_join_if_batch_types_too_different(RenderItemState &r_ris, uint32_t btf_allowed); - bool _detect_item_batch_break(RenderItemState &r_ris, RendererCanvasRender::Item *p_ci, bool &r_batch_break); - - // drives the loop filling batches and flushing - void render_joined_item_commands(const BItemJoined &p_bij, RendererCanvasRender::Item *p_current_clip, bool &r_reclip, typename T_STORAGE::Material *p_material, bool p_lit); - -private: - // flush once full or end of joined item - void flush_render_batches(RendererCanvasRender::Item *p_first_item, RendererCanvasRender::Item *p_current_clip, bool &r_reclip, typename T_STORAGE::Material *p_material, uint32_t p_sequence_batch_type_flags); - - // a single joined item can contain multiple itemrefs, and thus create lots of batches - // command start given a separate name to make easier to tell apart godot 3 and 4 - bool prefill_joined_item(FillState &r_fill_state, RendererCanvasRender::Item::Command **r_first_command, RendererCanvasRender::Item *p_item, RendererCanvasRender::Item *p_current_clip, bool &r_reclip, typename T_STORAGE::Material *p_material); - - // prefilling different types of batch - - // default batch is an 'unhandled' legacy type batch that will be drawn with the legacy path, - // all other batches are accelerated. - void _prefill_default_batch(FillState &r_fill_state, int p_command_num, const RendererCanvasRender::Item &p_item); - - // accelerated batches - bool _prefill_rect(RendererCanvasRender::Item::CommandRect *rect, FillState &r_fill_state, int &r_command_start, int command_num, int command_count, RendererCanvasRender::Item::Command *const *commands, RendererCanvasRender::Item *p_item, bool multiply_final_modulate); - - // dealing with textures - int _batch_find_or_create_tex(const RID &p_texture, const RID &p_normal, bool p_tile, int p_previous_match); - -protected: - // legacy support for non batched mode - void _legacy_canvas_item_render_commands(RendererCanvasRender::Item *p_item, RendererCanvasRender::Item *p_current_clip, bool &r_reclip, typename T_STORAGE::Material *p_material); - - // light scissoring - bool _light_scissor_begin(const Rect2 &p_item_rect, const Transform2D &p_light_xform, const Rect2 &p_light_rect) const; - bool _light_find_intersection(const Rect2 &p_item_rect, const Transform2D &p_light_xform, const Rect2 &p_light_rect, Rect2 &r_cliprect) const; - void _calculate_scissor_threshold_area(); - -private: - // translating vertex formats prior to rendering - void _translate_batches_to_vertex_colored_FVF(); - template <class BATCH_VERTEX_TYPE, bool INCLUDE_LIGHT_ANGLES, bool INCLUDE_MODULATE, bool INCLUDE_LARGE> - void _translate_batches_to_larger_FVF(uint32_t p_sequence_batch_type_flags); - -protected: - // accessory funcs - void _software_transform_vertex(BatchVector2 &r_v, const Transform2D &p_tr) const; - void _software_transform_vertex(Vector2 &r_v, const Transform2D &p_tr) const; - TransformMode _find_transform_mode(const Transform2D &p_tr) const { - // decided whether to do translate only for software transform - if ((p_tr.elements[0].x == 1.0f) && - (p_tr.elements[0].y == 0.0f) && - (p_tr.elements[1].x == 0.0f) && - (p_tr.elements[1].y == 1.0f)) { - return TM_TRANSLATE; - } - - return TM_ALL; - } - - typename T_STORAGE::Texture *_get_canvas_texture(const RID &p_texture) const { - if (p_texture.is_valid()) { - typename T_STORAGE::Texture *texture = get_storage()->texture_owner.get_or_null(p_texture); - - if (texture) { - return texture->get_ptr(); - } - } - - return 0; - } - -public: - Batch *_batch_request_new(bool p_blank = true) { - Batch *batch = bdata.batches.request(); - if (!batch) { - // grow the batches - bdata.batches.grow(); - - // and the temporary batches (used for color verts) - bdata.batches_temp.reset(); - bdata.batches_temp.grow(); - - // this should always succeed after growing - batch = bdata.batches.request(); - RAST_DEBUG_ASSERT(batch); - } - - if (p_blank) - memset(batch, 0, sizeof(Batch)); - - return batch; - } - - BatchVertex *_batch_vertex_request_new() { - return bdata.vertices.request(); - } - -protected: - int godot4_commands_count(RendererCanvasRender::Item::Command *p_comm) const { - int count = 0; - while (p_comm) { - count++; - p_comm = p_comm->next; - } - return count; - } - - unsigned int godot4_commands_to_vector(RendererCanvasRender::Item::Command *p_comm, LocalVector<RendererCanvasRender::Item::Command *> &p_list) { - p_list.clear(); - while (p_comm) { - p_list.push_back(p_comm); - p_comm = p_comm->next; - } - return p_list.size(); - } -}; - -PREAMBLE(void)::batch_canvas_begin() { - // diagnose_frame? - bdata.frame_string = ""; // just in case, always set this as we don't want a string leak in release... -#if defined(TOOLS_ENABLED) && defined(DEBUG_ENABLED) - if (bdata.settings_diagnose_frame) { - bdata.diagnose_frame = false; - - uint32_t tick = OS::get_singleton()->get_ticks_msec(); - uint64_t frame = Engine::get_singleton()->get_frames_drawn(); - - if (tick >= bdata.next_diagnose_tick) { - bdata.next_diagnose_tick = tick + 10000; - - // the plus one is prevent starting diagnosis half way through frame - bdata.diagnose_frame_number = frame + 1; - } - - if (frame == bdata.diagnose_frame_number) { - bdata.diagnose_frame = true; - bdata.reset_stats(); - } - - if (bdata.diagnose_frame) { - bdata.frame_string = "canvas_begin FRAME " + itos(frame) + "\n"; - } - } -#endif -} - -PREAMBLE(void)::batch_canvas_end() { -#if defined(TOOLS_ENABLED) && defined(DEBUG_ENABLED) - if (bdata.diagnose_frame) { - bdata.frame_string += "canvas_end\n"; - if (bdata.stats_items_sorted) { - bdata.frame_string += "\titems reordered: " + itos(bdata.stats_items_sorted) + "\n"; - } - if (bdata.stats_light_items_joined) { - bdata.frame_string += "\tlight items joined: " + itos(bdata.stats_light_items_joined) + "\n"; - } - - print_line(bdata.frame_string); - } -#endif -} - -PREAMBLE(void)::batch_canvas_render_items_begin(const Color &p_modulate, RendererCanvasRender::Light *p_light, const Transform2D &p_base_transform) { - // if we are debugging, flash each frame between batching renderer and old version to compare for regressions - if (bdata.settings_flash_batching) { - if ((Engine::get_singleton()->get_frames_drawn() % 2) == 0) - bdata.settings_use_batching = true; - else - bdata.settings_use_batching = false; - } - - if (!bdata.settings_use_batching) { - return; - } - - // this only needs to be done when screen size changes, but this should be - // infrequent enough - _calculate_scissor_threshold_area(); - - // set up render item state for all the z_indexes (this is common to all z_indexes) - _render_item_state.reset(); - _render_item_state.item_group_modulate = p_modulate; - _render_item_state.item_group_light = p_light; - _render_item_state.item_group_base_transform = p_base_transform; - _render_item_state.light_region.reset(); - - // batch break must be preserved over the different z indices, - // to prevent joining to an item on a previous index if not allowed - _render_item_state.join_batch_break = false; - - // whether to join across z indices depends on whether there are z ranged lights. - // joined z_index items can be wrongly classified with z ranged lights. - bdata.join_across_z_indices = true; - - int light_count = 0; - while (p_light) { - light_count++; - - if ((p_light->z_min != RS::CANVAS_ITEM_Z_MIN) || (p_light->z_max != RS::CANVAS_ITEM_Z_MAX)) { - // prevent joining across z indices. This would have caused visual regressions - bdata.join_across_z_indices = false; - } - - p_light = p_light->next_ptr; - } - - // can't use the light region bitfield if there are too many lights - // hopefully most games won't blow this limit.. - // if they do they will work but it won't batch join items just in case - if (light_count > 64) { - _render_item_state.light_region.too_many_lights = true; - } -} - -PREAMBLE(void)::batch_canvas_render_items_end() { - if (!bdata.settings_use_batching) { - return; - } - - join_sorted_items(); - -#if defined(TOOLS_ENABLED) && defined(DEBUG_ENABLED) - if (bdata.diagnose_frame) { - bdata.frame_string += "items\n"; - } -#endif - - // batching render is deferred until after going through all the z_indices, joining all the items - get_this()->canvas_render_items_implementation(0, 0, _render_item_state.item_group_modulate, - _render_item_state.item_group_light, - _render_item_state.item_group_base_transform); - - bdata.items_joined.reset(); - bdata.item_refs.reset(); - bdata.sort_items.reset(); -} - -PREAMBLE(void)::batch_canvas_render_items(RendererCanvasRender::Item *p_item_list, int p_z, const Color &p_modulate, RendererCanvasRender::Light *p_light, const Transform2D &p_base_transform) { - // stage 1 : join similar items, so that their state changes are not repeated, - // and commands from joined items can be batched together - if (bdata.settings_use_batching) { - record_items(p_item_list, p_z); - return; - } - - // only legacy renders at this stage, batched renderer doesn't render until canvas_render_items_end() - get_this()->canvas_render_items_implementation(p_item_list, p_z, p_modulate, p_light, p_base_transform); -} - -// Default batches will not occur in software transform only items -// EXCEPT IN THE CASE OF SINGLE RECTS (and this may well not occur, check the logic in prefill_join_item TYPE_RECT) -// but can occur where transform commands have been sent during hardware batch -PREAMBLE(void)::_prefill_default_batch(FillState &r_fill_state, int p_command_num, const RendererCanvasRender::Item &p_item) { - if (r_fill_state.curr_batch->type == RasterizerStorageCommon::BT_DEFAULT) { - // don't need to flush an extra transform command? - if (!r_fill_state.transform_extra_command_number_p1) { - // another default command, just add to the existing batch - r_fill_state.curr_batch->num_commands++; - } else { -#if defined(TOOLS_ENABLED) && defined(DEBUG_ENABLED) - if (r_fill_state.transform_extra_command_number_p1 != p_command_num) { - WARN_PRINT_ONCE("_prefill_default_batch : transform_extra_command_number_p1 != p_command_num"); - } -#endif - // if the first member of the batch is a transform we have to be careful - if (!r_fill_state.curr_batch->num_commands) { - // there can be leading useless extra transforms (sometimes happens with debug collision polys) - // we need to rejig the first_command for the first useful transform - r_fill_state.curr_batch->first_command += r_fill_state.transform_extra_command_number_p1 - 1; - } - - // we do have a pending extra transform command to flush - // either the extra transform is in the prior command, or not, in which case we need 2 batches - r_fill_state.curr_batch->num_commands += 2; - - r_fill_state.transform_extra_command_number_p1 = 0; // mark as sent - r_fill_state.extra_matrix_sent = true; - - // the original mode should always be hardware transform .. - // test this assumption - //CRASH_COND(r_fill_state.orig_transform_mode != TM_NONE); - r_fill_state.transform_mode = r_fill_state.orig_transform_mode; - - // do we need to restore anything else? - } - } else { - // end of previous different type batch, so start new default batch - - // first consider whether there is a dirty extra matrix to send - if (r_fill_state.transform_extra_command_number_p1) { - // get which command the extra is in, and blank all the records as it no longer is stored CPU side - int extra_command = r_fill_state.transform_extra_command_number_p1 - 1; // plus 1 based - r_fill_state.transform_extra_command_number_p1 = 0; - r_fill_state.extra_matrix_sent = true; - - // send the extra to the GPU in a batch - r_fill_state.curr_batch = _batch_request_new(); - r_fill_state.curr_batch->type = RasterizerStorageCommon::BT_DEFAULT; - r_fill_state.curr_batch->first_command = extra_command; - r_fill_state.curr_batch->num_commands = 1; - - // revert to the original transform mode - // e.g. go back to NONE if we were in hardware transform mode - r_fill_state.transform_mode = r_fill_state.orig_transform_mode; - - // reset the original transform if we are going back to software mode, - // because the extra is now done on the GPU... - // (any subsequent extras are sent directly to the GPU, no deferring) - if (r_fill_state.orig_transform_mode != TM_NONE) { - r_fill_state.transform_combined = p_item.final_transform; - } - - // can possibly combine batch with the next one in some cases - // this is more efficient than having an extra batch especially for the extra - if ((extra_command + 1) == p_command_num) { - r_fill_state.curr_batch->num_commands = 2; - return; - } - } - - // start default batch - r_fill_state.curr_batch = _batch_request_new(); - r_fill_state.curr_batch->type = RasterizerStorageCommon::BT_DEFAULT; - r_fill_state.curr_batch->first_command = p_command_num; - r_fill_state.curr_batch->num_commands = 1; - } -} - -PREAMBLE(int)::_batch_find_or_create_tex(const RID &p_texture, const RID &p_normal, bool p_tile, int p_previous_match) { - // optimization .. in 99% cases the last matched value will be the same, so no need to traverse the list - if (p_previous_match > 0) // if it is zero, it will get hit first in the linear search anyway - { - const BatchTex &batch_texture = bdata.batch_textures[p_previous_match]; - - // note for future reference, if RID implementation changes, this could become more expensive - if ((batch_texture.RID_texture == p_texture) && (batch_texture.RID_normal == p_normal)) { - // tiling mode must also match - bool tiles = batch_texture.tile_mode != BatchTex::TILE_OFF; - - if (tiles == p_tile) - // match! - return p_previous_match; - } - } - - // not the previous match .. we will do a linear search ... slower, but should happen - // not very often except with non-batchable runs, which are going to be slow anyway - // n.b. could possibly be replaced later by a fast hash table - for (int n = 0; n < bdata.batch_textures.size(); n++) { - const BatchTex &batch_texture = bdata.batch_textures[n]; - if ((batch_texture.RID_texture == p_texture) && (batch_texture.RID_normal == p_normal)) { - // tiling mode must also match - bool tiles = batch_texture.tile_mode != BatchTex::TILE_OFF; - - if (tiles == p_tile) - // match! - return n; - } - } - - // pushing back from local variable .. not ideal but has to use a Vector because non pod - // due to RIDs - BatchTex new_batch_tex; - new_batch_tex.RID_texture = p_texture; - new_batch_tex.RID_normal = p_normal; - - // get the texture - typename T_STORAGE::Texture *texture = _get_canvas_texture(p_texture); - - if (texture) { - // special case, there can be textures with no width or height - int w = texture->width; - int h = texture->height; - - if (!w || !h) { - w = 1; - h = 1; - } - - new_batch_tex.tex_pixel_size.x = 1.0 / w; - new_batch_tex.tex_pixel_size.y = 1.0 / h; - new_batch_tex.flags = texture->flags; - } else { - // maybe doesn't need doing... - new_batch_tex.tex_pixel_size.x = 1.0f; - new_batch_tex.tex_pixel_size.y = 1.0f; - new_batch_tex.flags = 0; - } - - if (p_tile) { - if (texture) { - // default - new_batch_tex.tile_mode = BatchTex::TILE_NORMAL; - - // no hardware support for non power of 2 tiling - if (!get_storage()->config.support_npot_repeat_mipmap) { - if (next_power_of_2(texture->alloc_width) != (unsigned int)texture->alloc_width && next_power_of_2(texture->alloc_height) != (unsigned int)texture->alloc_height) { - new_batch_tex.tile_mode = BatchTex::TILE_FORCE_REPEAT; - } - } - } else { - // this should not happen? - new_batch_tex.tile_mode = BatchTex::TILE_OFF; - } - } else { - new_batch_tex.tile_mode = BatchTex::TILE_OFF; - } - - // push back - bdata.batch_textures.push_back(new_batch_tex); - - return bdata.batch_textures.size() - 1; -} - -PREAMBLE(void)::batch_constructor() { - bdata.settings_use_batching = false; - -#ifdef GLES_OVER_GL - use_nvidia_rect_workaround = GLOBAL_GET("rendering/quality/2d/use_nvidia_rect_flicker_workaround"); -#else - // Not needed (a priori) on GLES devices - use_nvidia_rect_workaround = false; -#endif -} - -PREAMBLE(void)::batch_initialize() { -#define BATCHING_LOAD_PROJECT_SETTINGS - -#ifdef BATCHING_LOAD_PROJECT_SETTINGS - bdata.settings_use_batching = GLOBAL_GET("rendering/batching/options/use_batching"); - bdata.settings_max_join_item_commands = GLOBAL_GET("rendering/batching/parameters/max_join_item_commands"); - bdata.settings_colored_vertex_format_threshold = GLOBAL_GET("rendering/batching/parameters/colored_vertex_format_threshold"); - bdata.settings_item_reordering_lookahead = GLOBAL_GET("rendering/batching/parameters/item_reordering_lookahead"); - bdata.settings_light_max_join_items = GLOBAL_GET("rendering/batching/lights/max_join_items"); - bdata.settings_use_single_rect_fallback = GLOBAL_GET("rendering/batching/options/single_rect_fallback"); - bdata.settings_use_software_skinning = GLOBAL_GET("rendering/quality/2d/use_software_skinning"); - bdata.settings_ninepatch_mode = GLOBAL_GET("rendering/quality/2d/ninepatch_mode"); - - // alternatively only enable uv contract if pixel snap in use, - // but with this enable bool, it should not be necessary - bdata.settings_uv_contract = GLOBAL_GET("rendering/batching/precision/uv_contract"); - bdata.settings_uv_contract_amount = (float)GLOBAL_GET("rendering/batching/precision/uv_contract_amount") / 1000000.0f; - - // we can use the threshold to determine whether to turn scissoring off or on - bdata.settings_scissor_threshold = GLOBAL_GET("rendering/batching/lights/scissor_area_threshold"); -#endif - - if (bdata.settings_scissor_threshold > 0.999f) { - bdata.settings_scissor_lights = false; - } else { - bdata.settings_scissor_lights = true; - - // apply power of 4 relationship for the area, as most of the important changes - // will be happening at low values of scissor threshold - bdata.settings_scissor_threshold *= bdata.settings_scissor_threshold; - bdata.settings_scissor_threshold *= bdata.settings_scissor_threshold; - } - - // The sweet spot on my desktop for cache is actually smaller than the max, and this - // is the default. This saves memory too so we will use it for now, needs testing to see whether this varies according - // to device / platform. -#ifdef BATCHING_LOAD_PROJECT_SETTINGS - bdata.settings_batch_buffer_num_verts = GLOBAL_GET("rendering/batching/parameters/batch_buffer_size"); - - // override the use_batching setting in the editor - // (note that if the editor can't start, you can't change the use_batching project setting!) - if (Engine::get_singleton()->is_editor_hint()) { - bool use_in_editor = GLOBAL_GET("rendering/batching/options/use_batching_in_editor"); - bdata.settings_use_batching = use_in_editor; - - // fix some settings in the editor, as the performance not worth the risk - bdata.settings_use_single_rect_fallback = false; - } -#endif - - // if we are using batching, we will purposefully disable the nvidia workaround. - // This is because the only reason to use the single rect fallback is the approx 2x speed - // of the uniform drawing technique. If we used nvidia workaround, speed would be - // approx equal to the batcher drawing technique (indexed primitive + VB). - if (bdata.settings_use_batching) { - use_nvidia_rect_workaround = false; - } - - // For debugging, if flash is set in project settings, it will flash on alternate frames - // between the non-batched renderer and the batched renderer, - // in order to find regressions. - // This should not be used except during development. - // make a note of the original choice in case we are flashing on and off the batching - bdata.settings_use_batching_original_choice = bdata.settings_use_batching; - -#ifdef BATCHING_LOAD_PROJECT_SETTINGS - bdata.settings_flash_batching = GLOBAL_GET("rendering/batching/debug/flash_batching"); -#endif - if (!bdata.settings_use_batching) { - // no flash when batching turned off - bdata.settings_flash_batching = false; - } - - // frame diagnosis. print out the batches every nth frame - bdata.settings_diagnose_frame = false; - if (!Engine::get_singleton()->is_editor_hint() && bdata.settings_use_batching) { -#ifdef BATCHING_LOAD_PROJECT_SETTINGS - bdata.settings_diagnose_frame = GLOBAL_GET("rendering/batching/debug/diagnose_frame"); -#endif - } - - // the maximum num quads in a batch is limited by GLES2. We can have only 16 bit indices, - // which means we can address a vertex buffer of max size 65535. 4 vertices are needed per quad. - - // Note this determines the memory use by the vertex buffer vector. max quads (65536/4)-1 - // but can be reduced to save memory if really required (will result in more batches though) - const int max_possible_quads = (65536 / 4) - 1; - const int min_possible_quads = 8; // some reasonable small value - - // value from project settings - int max_quads = bdata.settings_batch_buffer_num_verts / 4; - - // sanity checks - max_quads = CLAMP(max_quads, min_possible_quads, max_possible_quads); - bdata.settings_max_join_item_commands = CLAMP(bdata.settings_max_join_item_commands, 0, 65535); - bdata.settings_colored_vertex_format_threshold = CLAMP(bdata.settings_colored_vertex_format_threshold, 0.0f, 1.0f); - bdata.settings_scissor_threshold = CLAMP(bdata.settings_scissor_threshold, 0.0f, 1.0f); - bdata.settings_light_max_join_items = CLAMP(bdata.settings_light_max_join_items, 0, 65535); - bdata.settings_item_reordering_lookahead = CLAMP(bdata.settings_item_reordering_lookahead, 0, 65535); - - // allow user to override the api usage techniques using project settings - // bdata.buffer_mode_batch_upload_send_null = GLOBAL_GET("rendering/options/api_usage_batching/send_null"); - // bdata.buffer_mode_batch_upload_flag_stream = GLOBAL_GET("rendering/options/api_usage_batching/flag_stream"); - - // for debug purposes, output a string with the batching options - String batching_options_string = "OpenGL ES Batching: "; - if (bdata.settings_use_batching) { - batching_options_string += "ON"; - - if (OS::get_singleton()->is_stdout_verbose()) { - batching_options_string += "\n\tOPTIONS\n"; - batching_options_string += "\tmax_join_item_commands " + itos(bdata.settings_max_join_item_commands) + "\n"; - batching_options_string += "\tcolored_vertex_format_threshold " + String(Variant(bdata.settings_colored_vertex_format_threshold)) + "\n"; - batching_options_string += "\tbatch_buffer_size " + itos(bdata.settings_batch_buffer_num_verts) + "\n"; - batching_options_string += "\tlight_scissor_area_threshold " + String(Variant(bdata.settings_scissor_threshold)) + "\n"; - - batching_options_string += "\titem_reordering_lookahead " + itos(bdata.settings_item_reordering_lookahead) + "\n"; - batching_options_string += "\tlight_max_join_items " + itos(bdata.settings_light_max_join_items) + "\n"; - batching_options_string += "\tsingle_rect_fallback " + String(Variant(bdata.settings_use_single_rect_fallback)) + "\n"; - - batching_options_string += "\tdebug_flash " + String(Variant(bdata.settings_flash_batching)) + "\n"; - batching_options_string += "\tdiagnose_frame " + String(Variant(bdata.settings_diagnose_frame)); - } - - print_line(batching_options_string); - } - - // special case, for colored vertex format threshold. - // as the comparison is >=, we want to be able to totally turn on or off - // conversion to colored vertex format at the extremes, so we will force - // 1.0 to be just above 1.0 - if (bdata.settings_colored_vertex_format_threshold > 0.995f) { - bdata.settings_colored_vertex_format_threshold = 1.01f; - } - - // save memory when batching off - if (!bdata.settings_use_batching) { - max_quads = 0; - } - - uint32_t sizeof_batch_vert = sizeof(BatchVertex); - - bdata.max_quads = max_quads; - - // 4 verts per quad - bdata.vertex_buffer_size_units = max_quads * 4; - - // the index buffer can be longer than 65535, only the indices need to be within this range - bdata.index_buffer_size_units = max_quads * 6; - - const int max_verts = bdata.vertex_buffer_size_units; - - // this comes out at approx 64K for non-colored vertex buffer, and 128K for colored vertex buffer - bdata.vertex_buffer_size_bytes = max_verts * sizeof_batch_vert; - bdata.index_buffer_size_bytes = bdata.index_buffer_size_units * 2; // 16 bit inds - - // create equal number of normal and (max) unit sized verts (as the normal may need to be translated to a larger FVF) - bdata.vertices.create(max_verts); // 512k - bdata.unit_vertices.create(max_verts, sizeof(BatchVertexLarge)); - - // extra data per vert needed for larger FVFs - bdata.light_angles.create(max_verts); - bdata.vertex_colors.create(max_verts); - bdata.vertex_modulates.create(max_verts); - bdata.vertex_transforms.create(max_verts); - - // num batches will be auto increased dynamically if required - bdata.batches.create(1024); - bdata.batches_temp.create(bdata.batches.max_size()); - - // batch textures can also be increased dynamically - bdata.batch_textures.create(32); -} - -PREAMBLE(bool)::_light_scissor_begin(const Rect2 &p_item_rect, const Transform2D &p_light_xform, const Rect2 &p_light_rect) const { - float area_item = p_item_rect.size.x * p_item_rect.size.y; // double check these are always positive - - // quick reject .. the area of pixels saved can never be more than the area of the item - if (area_item < bdata.scissor_threshold_area) { - return false; - } - - Rect2 cliprect; - if (!_light_find_intersection(p_item_rect, p_light_xform, p_light_rect, cliprect)) { - // should not really occur .. but just in case - cliprect = Rect2(0, 0, 0, 0); - } else { - // some conditions not to scissor - // determine the area (fill rate) that will be saved - float area_cliprect = cliprect.size.x * cliprect.size.y; - float area_saved = area_item - area_cliprect; - - // if area saved is too small, don't scissor - if (area_saved < bdata.scissor_threshold_area) { - return false; - } - } - - int rh = get_storage()->frame.current_rt->height; - - int y = rh - (cliprect.position.y + cliprect.size.y); - get_this()->gl_enable_scissor(cliprect.position.x, y, cliprect.size.width, cliprect.size.height); - - return true; -} - -PREAMBLE(bool)::_light_find_intersection(const Rect2 &p_item_rect, const Transform2D &p_light_xform, const Rect2 &p_light_rect, Rect2 &r_cliprect) const { - // transform light to world space (note this is done in the earlier intersection test, so could - // be made more efficient) - Vector2 pts[4] = { - p_light_xform.xform(p_light_rect.position), - p_light_xform.xform(Vector2(p_light_rect.position.x + p_light_rect.size.x, p_light_rect.position.y)), - p_light_xform.xform(Vector2(p_light_rect.position.x, p_light_rect.position.y + p_light_rect.size.y)), - p_light_xform.xform(Vector2(p_light_rect.position.x + p_light_rect.size.x, p_light_rect.position.y + p_light_rect.size.y)), - }; - - // calculate the light bound rect in world space - Rect2 lrect(pts[0].x, pts[0].y, 0, 0); - for (int n = 1; n < 4; n++) { - lrect.expand_to(pts[n]); - } - - // intersection between the 2 rects - // they should probably always intersect, because of earlier check, but just in case... - if (!p_item_rect.intersects(lrect)) - return false; - - // note this does almost the same as Rect2.clip but slightly more efficient for our use case - r_cliprect.position.x = MAX(p_item_rect.position.x, lrect.position.x); - r_cliprect.position.y = MAX(p_item_rect.position.y, lrect.position.y); - - Point2 item_rect_end = p_item_rect.position + p_item_rect.size; - Point2 lrect_end = lrect.position + lrect.size; - - r_cliprect.size.x = MIN(item_rect_end.x, lrect_end.x) - r_cliprect.position.x; - r_cliprect.size.y = MIN(item_rect_end.y, lrect_end.y) - r_cliprect.position.y; - - return true; -} - -PREAMBLE(void)::_calculate_scissor_threshold_area() { - if (!bdata.settings_scissor_lights) { - return; - } - - // scissor area threshold is 0.0 to 1.0 in the settings for ease of use. - // we need to translate to an absolute area to determine quickly whether - // to scissor. - if (bdata.settings_scissor_threshold < 0.0001f) { - bdata.scissor_threshold_area = -1.0f; // will always pass - } else { - // in pixels - int w = get_storage()->frame.current_rt->width; - int h = get_storage()->frame.current_rt->height; - - int screen_area = w * h; - - bdata.scissor_threshold_area = bdata.settings_scissor_threshold * screen_area; - } -} - -PREAMBLE(void)::render_joined_item_commands(const BItemJoined &p_bij, RendererCanvasRender::Item *p_current_clip, bool &r_reclip, typename T_STORAGE::Material *p_material, bool p_lit) { - RendererCanvasRender::Item *item = 0; - RendererCanvasRender::Item *first_item = bdata.item_refs[p_bij.first_item_ref].item; - - // fill_state and bdata have once off setup per joined item, and a smaller reset on flush - FillState fill_state; - fill_state.reset_joined_item(p_bij.use_hardware_transform()); - - bdata.reset_joined_item(); - - // should this joined item be using large FVF? - if (p_bij.flags & RasterizerStorageCommon::USE_MODULATE_FVF) { - bdata.use_modulate = true; - bdata.fvf = RasterizerStorageCommon::FVF_MODULATED; - } - if (p_bij.flags & RasterizerStorageCommon::USE_LARGE_FVF) { - bdata.use_modulate = true; - bdata.use_large_verts = true; - bdata.fvf = RasterizerStorageCommon::FVF_LARGE; - } - - // in the special case of custom shaders that read from VERTEX (i.e. vertex position) - // we want to disable software transform of extra matrix - if (bdata.joined_item_batch_flags & RasterizerStorageCommon::PREVENT_VERTEX_BAKING) { - fill_state.extra_matrix_sent = true; - } - - for (unsigned int i = 0; i < p_bij.num_item_refs; i++) { - const BItemRef &ref = bdata.item_refs[p_bij.first_item_ref + i]; - item = ref.item; - - if (!p_lit) { - // if not lit we use the complex calculated final modulate - fill_state.final_modulate = ref.final_modulate; - } else { - // if lit we ignore canvas modulate and just use the item modulate - fill_state.final_modulate = item->final_modulate; - } - - // ONCE OFF fill state setup, that will be retained over multiple calls to - // prefill_joined_item() - fill_state.transform_combined = item->final_transform; - - // decide the initial transform mode, and make a backup - // in orig_transform_mode in case we need to switch back - if (!fill_state.use_hardware_transform) { - fill_state.transform_mode = _find_transform_mode(fill_state.transform_combined); - } else { - fill_state.transform_mode = TM_NONE; - } - fill_state.orig_transform_mode = fill_state.transform_mode; - - // keep track of when we added an extra matrix - // so we can defer sending until we see a default command - fill_state.transform_extra_command_number_p1 = 0; - - RendererCanvasRender::Item::Command *current_command = item->commands; - while (current_command) { - // fill as many batches as possible (until all done, or the vertex buffer is full) - bool bFull = get_this()->prefill_joined_item(fill_state, current_command, item, p_current_clip, r_reclip, p_material); - - if (bFull) { - // always pass first item (commands for default are always first item) - flush_render_batches(first_item, p_current_clip, r_reclip, p_material, fill_state.sequence_batch_type_flags); - - // zero all the batch data ready for a new run - bdata.reset_flush(); - - // don't zero all the fill state, some may need to be preserved - fill_state.reset_flush(); - } - } - } - - // flush if any left - flush_render_batches(first_item, p_current_clip, r_reclip, p_material, fill_state.sequence_batch_type_flags); - - // zero all the batch data ready for a new run - bdata.reset_flush(); -} - -PREAMBLE(void)::_legacy_canvas_item_render_commands(RendererCanvasRender::Item *p_item, RendererCanvasRender::Item *p_current_clip, bool &r_reclip, typename T_STORAGE::Material *p_material) { - // reuse the same list each time to prevent needless dynamic allocations - unsigned int command_count = godot4_commands_to_vector(p_item->commands, bdata.command_shortlist); - RendererCanvasRender::Item::Command *const *commands = nullptr; - if (command_count) { - commands = &bdata.command_shortlist[0]; - } - - // legacy .. just create one massive batch and render everything as before - bdata.batches.reset(); - Batch *batch = _batch_request_new(); - batch->type = RasterizerStorageCommon::BT_DEFAULT; - batch->num_commands = command_count; - - get_this()->render_batches(commands, p_current_clip, r_reclip, p_material); - bdata.reset_flush(); -} - -PREAMBLE(void)::record_items(RendererCanvasRender::Item *p_item_list, int p_z) { - while (p_item_list) { - BSortItem *s = bdata.sort_items.request_with_grow(); - - s->item = p_item_list; - s->z_index = p_z; - - p_item_list = p_item_list->next; - } -} - -PREAMBLE(void)::join_sorted_items() { -} - -PREAMBLE(void)::_software_transform_vertex(BatchVector2 &r_v, const Transform2D &p_tr) const { - Vector2 vc(r_v.x, r_v.y); - vc = p_tr.xform(vc); - r_v.set(vc); -} - -PREAMBLE(void)::_software_transform_vertex(Vector2 &r_v, const Transform2D &p_tr) const { - r_v = p_tr.xform(r_v); -} - -PREAMBLE(void)::_translate_batches_to_vertex_colored_FVF() { - // zeros the size and sets up how big each unit is - bdata.unit_vertices.prepare(sizeof(BatchVertexColored)); - - const BatchColor *source_vertex_colors = &bdata.vertex_colors[0]; - RAST_DEBUG_ASSERT(bdata.vertex_colors.size() == bdata.vertices.size()); - - int num_verts = bdata.vertices.size(); - - for (int n = 0; n < num_verts; n++) { - const BatchVertex &bv = bdata.vertices[n]; - - BatchVertexColored *cv = (BatchVertexColored *)bdata.unit_vertices.request(); - - cv->pos = bv.pos; - cv->uv = bv.uv; - cv->col = *source_vertex_colors++; - } -} - -// Translation always involved adding color to the FVF, which enables -// joining of batches that have different colors. -// There is a trade off. Non colored verts are smaller so work faster, but -// there comes a point where it is better to just use colored verts to avoid lots of -// batches. -// In addition this can optionally add light angles to the FVF, necessary for normal mapping. -T_PREAMBLE -template <class BATCH_VERTEX_TYPE, bool INCLUDE_LIGHT_ANGLES, bool INCLUDE_MODULATE, bool INCLUDE_LARGE> -void C_PREAMBLE::_translate_batches_to_larger_FVF(uint32_t p_sequence_batch_type_flags) { - bool include_poly_color = false; - - // we ONLY want to include the color verts in translation when using polys, - // as rects do not write vertex colors, only colors per batch. - if (p_sequence_batch_type_flags & RasterizerStorageCommon::BTF_POLY) { - include_poly_color = INCLUDE_LIGHT_ANGLES | INCLUDE_MODULATE | INCLUDE_LARGE; - } - - // zeros the size and sets up how big each unit is - bdata.unit_vertices.prepare(sizeof(BATCH_VERTEX_TYPE)); - bdata.batches_temp.reset(); - - // As the vertices_colored and batches_temp are 'mirrors' of the non-colored version, - // the sizes should be equal, and allocations should never fail. Hence the use of debug - // asserts to check program flow, these should not occur at runtime unless the allocation - // code has been altered. - RAST_DEBUG_ASSERT(bdata.unit_vertices.max_size() == bdata.vertices.max_size()); - RAST_DEBUG_ASSERT(bdata.batches_temp.max_size() == bdata.batches.max_size()); - - Color curr_col(-1.0f, -1.0f, -1.0f, -1.0f); - - Batch *dest_batch = nullptr; - - const BatchColor *source_vertex_colors = &bdata.vertex_colors[0]; - const float *source_light_angles = &bdata.light_angles[0]; - const BatchColor *source_vertex_modulates = &bdata.vertex_modulates[0]; - const BatchTransform *source_vertex_transforms = &bdata.vertex_transforms[0]; - - // translate the batches into vertex colored batches - for (int n = 0; n < bdata.batches.size(); n++) { - const Batch &source_batch = bdata.batches[n]; - - // does source batch use light angles? - const BatchTex &btex = bdata.batch_textures[source_batch.batch_texture_id]; - bool source_batch_uses_light_angles = btex.RID_normal != RID(); - - bool needs_new_batch = true; - - if (dest_batch) { - if (dest_batch->type == source_batch.type) { - if (source_batch.type == RasterizerStorageCommon::BT_RECT) { - if (dest_batch->batch_texture_id == source_batch.batch_texture_id) { - // add to previous batch - dest_batch->num_commands += source_batch.num_commands; - needs_new_batch = false; - - // create the colored verts (only if not default) - //int first_vert = source_batch.first_quad * 4; - //int end_vert = 4 * (source_batch.first_quad + source_batch.num_commands); - int first_vert = source_batch.first_vert; - int end_vert = first_vert + (4 * source_batch.num_commands); - - for (int v = first_vert; v < end_vert; v++) { - RAST_DEV_DEBUG_ASSERT(bdata.vertices.size()); - const BatchVertex &bv = bdata.vertices[v]; - BATCH_VERTEX_TYPE *cv = (BATCH_VERTEX_TYPE *)bdata.unit_vertices.request(); - RAST_DEBUG_ASSERT(cv); - cv->pos = bv.pos; - cv->uv = bv.uv; - cv->col = source_batch.color; - - if (INCLUDE_LIGHT_ANGLES) { - RAST_DEV_DEBUG_ASSERT(bdata.light_angles.size()); - // this is required to allow compilation with non light angle vertex. - // it should be compiled out. - BatchVertexLightAngled *lv = (BatchVertexLightAngled *)cv; - if (source_batch_uses_light_angles) - lv->light_angle = *source_light_angles++; - else - lv->light_angle = 0.0f; // dummy, unused in vertex shader (could possibly be left uninitialized, but probably bad idea) - } // if including light angles - - if (INCLUDE_MODULATE) { - RAST_DEV_DEBUG_ASSERT(bdata.vertex_modulates.size()); - BatchVertexModulated *mv = (BatchVertexModulated *)cv; - mv->modulate = *source_vertex_modulates++; - } // including modulate - - if (INCLUDE_LARGE) { - RAST_DEV_DEBUG_ASSERT(bdata.vertex_transforms.size()); - BatchVertexLarge *lv = (BatchVertexLarge *)cv; - lv->transform = *source_vertex_transforms++; - } // if including large - } - } // textures match - } else { - // default - // we can still join, but only under special circumstances - // does this ever happen? not sure at this stage, but left for future expansion - uint32_t source_last_command = source_batch.first_command + source_batch.num_commands; - if (source_last_command == dest_batch->first_command) { - dest_batch->num_commands += source_batch.num_commands; - needs_new_batch = false; - } // if the commands line up exactly - } - } // if both batches are the same type - - } // if dest batch is valid - - if (needs_new_batch) { - dest_batch = bdata.batches_temp.request(); - RAST_DEBUG_ASSERT(dest_batch); - - *dest_batch = source_batch; - - // create the colored verts (only if not default) - if (source_batch.type != RasterizerStorageCommon::BT_DEFAULT) { - // int first_vert = source_batch.first_quad * 4; - // int end_vert = 4 * (source_batch.first_quad + source_batch.num_commands); - int first_vert = source_batch.first_vert; - int end_vert = first_vert + (4 * source_batch.num_commands); - - for (int v = first_vert; v < end_vert; v++) { - RAST_DEV_DEBUG_ASSERT(bdata.vertices.size()); - const BatchVertex &bv = bdata.vertices[v]; - BATCH_VERTEX_TYPE *cv = (BATCH_VERTEX_TYPE *)bdata.unit_vertices.request(); - RAST_DEBUG_ASSERT(cv); - cv->pos = bv.pos; - cv->uv = bv.uv; - - // polys are special, they can have per vertex colors - if (!include_poly_color) { - cv->col = source_batch.color; - } else { - RAST_DEV_DEBUG_ASSERT(bdata.vertex_colors.size()); - cv->col = *source_vertex_colors++; - } - - if (INCLUDE_LIGHT_ANGLES) { - RAST_DEV_DEBUG_ASSERT(bdata.light_angles.size()); - // this is required to allow compilation with non light angle vertex. - // it should be compiled out. - BatchVertexLightAngled *lv = (BatchVertexLightAngled *)cv; - if (source_batch_uses_light_angles) - lv->light_angle = *source_light_angles++; - else - lv->light_angle = 0.0f; // dummy, unused in vertex shader (could possibly be left uninitialized, but probably bad idea) - } // if using light angles - - if (INCLUDE_MODULATE) { - RAST_DEV_DEBUG_ASSERT(bdata.vertex_modulates.size()); - BatchVertexModulated *mv = (BatchVertexModulated *)cv; - mv->modulate = *source_vertex_modulates++; - } // including modulate - - if (INCLUDE_LARGE) { - RAST_DEV_DEBUG_ASSERT(bdata.vertex_transforms.size()); - BatchVertexLarge *lv = (BatchVertexLarge *)cv; - lv->transform = *source_vertex_transforms++; - } // if including large - } - } - } - } - - // copy the temporary batches to the master batch list (this could be avoided but it makes the code cleaner) - bdata.batches.copy_from(bdata.batches_temp); -} - -PREAMBLE(bool)::_disallow_item_join_if_batch_types_too_different(RenderItemState &r_ris, uint32_t btf_allowed) { - r_ris.joined_item_batch_type_flags_curr |= btf_allowed; - - bool disallow = false; - - if (r_ris.joined_item_batch_type_flags_prev & (~btf_allowed)) - disallow = true; - - return disallow; -} - -#undef PREAMBLE -#undef T_PREAMBLE -#undef C_PREAMBLE - -#endif // RASTERIZER_CANVAS_BATCHER_H diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp index 686910e1c6..270725ee63 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.cpp +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -29,17 +29,20 @@ /*************************************************************************/ #include "rasterizer_canvas_gles3.h" -#include "drivers/gles3/rasterizer_platforms.h" -#ifdef GLES3_BACKEND_ENABLED + +#ifdef GLES3_ENABLED #include "core/os/os.h" -#include "drivers/gles3/rasterizer_asserts.h" #include "rasterizer_scene_gles3.h" #include "rasterizer_storage_gles3.h" #include "core/config/project_settings.h" #include "servers/rendering/rendering_server_default.h" +#ifndef GLES_OVER_GL +#define glClearDepth glClearDepthf +#endif + //static const GLenum gl_primitive[] = { // GL_POINTS, // GL_LINES, @@ -50,1660 +53,1417 @@ // GL_TRIANGLE_FAN //}; -#if 0 -void RasterizerCanvasGLES3::_batch_upload_buffers() { - // noop? - if (!bdata.vertices.size()) - return; +void RasterizerCanvasGLES3::_update_transform_2d_to_mat4(const Transform2D &p_transform, float *p_mat4) { + p_mat4[0] = p_transform.elements[0][0]; + p_mat4[1] = p_transform.elements[0][1]; + p_mat4[2] = 0; + p_mat4[3] = 0; + p_mat4[4] = p_transform.elements[1][0]; + p_mat4[5] = p_transform.elements[1][1]; + p_mat4[6] = 0; + p_mat4[7] = 0; + p_mat4[8] = 0; + p_mat4[9] = 0; + p_mat4[10] = 1; + p_mat4[11] = 0; + p_mat4[12] = p_transform.elements[2][0]; + p_mat4[13] = p_transform.elements[2][1]; + p_mat4[14] = 0; + p_mat4[15] = 1; +} - glBindBuffer(GL_ARRAY_BUFFER, bdata.gl_vertex_buffer); +void RasterizerCanvasGLES3::_update_transform_2d_to_mat2x4(const Transform2D &p_transform, float *p_mat2x4) { + p_mat2x4[0] = p_transform.elements[0][0]; + p_mat2x4[1] = p_transform.elements[1][0]; + p_mat2x4[2] = 0; + p_mat2x4[3] = p_transform.elements[2][0]; - // usage flag is a project setting - GLenum buffer_usage_flag = GL_DYNAMIC_DRAW; - if (bdata.buffer_mode_batch_upload_flag_stream) { - buffer_usage_flag = GL_STREAM_DRAW; - } - - // orphan the old (for now) - if (bdata.buffer_mode_batch_upload_send_null) { - glBufferData(GL_ARRAY_BUFFER, 0, 0, buffer_usage_flag); // GL_DYNAMIC_DRAW); - } + p_mat2x4[4] = p_transform.elements[0][1]; + p_mat2x4[5] = p_transform.elements[1][1]; + p_mat2x4[6] = 0; + p_mat2x4[7] = p_transform.elements[2][1]; +} - switch (bdata.fvf) { - case RasterizerStorageCommon::FVF_UNBATCHED: // should not happen - break; - case RasterizerStorageCommon::FVF_REGULAR: // no change - glBufferData(GL_ARRAY_BUFFER, sizeof(BatchVertex) * bdata.vertices.size(), bdata.vertices.get_data(), buffer_usage_flag); - break; - case RasterizerStorageCommon::FVF_COLOR: - glBufferData(GL_ARRAY_BUFFER, sizeof(BatchVertexColored) * bdata.unit_vertices.size(), bdata.unit_vertices.get_unit(0), buffer_usage_flag); - break; - case RasterizerStorageCommon::FVF_LIGHT_ANGLE: - glBufferData(GL_ARRAY_BUFFER, sizeof(BatchVertexLightAngled) * bdata.unit_vertices.size(), bdata.unit_vertices.get_unit(0), buffer_usage_flag); - break; - case RasterizerStorageCommon::FVF_MODULATED: - glBufferData(GL_ARRAY_BUFFER, sizeof(BatchVertexModulated) * bdata.unit_vertices.size(), bdata.unit_vertices.get_unit(0), buffer_usage_flag); - break; - case RasterizerStorageCommon::FVF_LARGE: - glBufferData(GL_ARRAY_BUFFER, sizeof(BatchVertexLarge) * bdata.unit_vertices.size(), bdata.unit_vertices.get_unit(0), buffer_usage_flag); - break; - } +void RasterizerCanvasGLES3::_update_transform_2d_to_mat2x3(const Transform2D &p_transform, float *p_mat2x3) { + p_mat2x3[0] = p_transform.elements[0][0]; + p_mat2x3[1] = p_transform.elements[0][1]; + p_mat2x3[2] = p_transform.elements[1][0]; + p_mat2x3[3] = p_transform.elements[1][1]; + p_mat2x3[4] = p_transform.elements[2][0]; + p_mat2x3[5] = p_transform.elements[2][1]; +} - // might not be necessary - glBindBuffer(GL_ARRAY_BUFFER, 0); +void RasterizerCanvasGLES3::_update_transform_to_mat4(const Transform3D &p_transform, float *p_mat4) { + p_mat4[0] = p_transform.basis.elements[0][0]; + p_mat4[1] = p_transform.basis.elements[1][0]; + p_mat4[2] = p_transform.basis.elements[2][0]; + p_mat4[3] = 0; + p_mat4[4] = p_transform.basis.elements[0][1]; + p_mat4[5] = p_transform.basis.elements[1][1]; + p_mat4[6] = p_transform.basis.elements[2][1]; + p_mat4[7] = 0; + p_mat4[8] = p_transform.basis.elements[0][2]; + p_mat4[9] = p_transform.basis.elements[1][2]; + p_mat4[10] = p_transform.basis.elements[2][2]; + p_mat4[11] = 0; + p_mat4[12] = p_transform.origin.x; + p_mat4[13] = p_transform.origin.y; + p_mat4[14] = p_transform.origin.z; + p_mat4[15] = 1; } -void RasterizerCanvasGLES3::_batch_render_lines(const Batch &p_batch, RasterizerStorageGLES3::Material *p_material, bool p_anti_alias) { - _set_texture_rect_mode(false); +void RasterizerCanvasGLES3::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) { + storage->frame.current_rt = nullptr; - if (state.canvas_shader.bind()) { - _set_uniforms(); - state.canvas_shader.use_material((void *)p_material); - } + storage->_set_current_render_target(p_to_render_target); - _bind_canvas_texture(RID(), RID()); + Transform2D canvas_transform_inverse = p_canvas_transform.affine_inverse(); - glDisableVertexAttribArray(RS::ARRAY_COLOR); - glVertexAttrib4fv(RS::ARRAY_COLOR, (float *)&p_batch.color); + // TODO: Setup Directional Lights -#ifdef GLES_OVER_GL - if (p_anti_alias) - glEnable(GL_LINE_SMOOTH); -#endif + // TODO: Setup lights - int sizeof_vert = sizeof(BatchVertex); + { + //update canvas state uniform buffer + StateBuffer state_buffer; - // bind the index and vertex buffer - glBindBuffer(GL_ARRAY_BUFFER, bdata.gl_vertex_buffer); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bdata.gl_index_buffer); + Size2i ssize = storage->render_target_get_size(p_to_render_target); - uint64_t pointer = 0; - glVertexAttribPointer(RS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof_vert, (const void *)pointer); + Transform3D screen_transform; + screen_transform.translate(-(ssize.width / 2.0f), -(ssize.height / 2.0f), 0.0f); + screen_transform.scale(Vector3(2.0f / ssize.width, 2.0f / ssize.height, 1.0f)); + _update_transform_to_mat4(screen_transform, state_buffer.screen_transform); + _update_transform_2d_to_mat4(p_canvas_transform, state_buffer.canvas_transform); - glDisableVertexAttribArray(RS::ARRAY_TEX_UV); + Transform2D normal_transform = p_canvas_transform; + normal_transform.elements[0].normalize(); + normal_transform.elements[1].normalize(); + normal_transform.elements[2] = Vector2(); + _update_transform_2d_to_mat4(normal_transform, state_buffer.canvas_normal_transform); - int64_t offset = p_batch.first_vert; // 6 inds per quad at 2 bytes each + state_buffer.canvas_modulate[0] = p_modulate.r; + state_buffer.canvas_modulate[1] = p_modulate.g; + state_buffer.canvas_modulate[2] = p_modulate.b; + state_buffer.canvas_modulate[3] = p_modulate.a; - int num_elements = p_batch.num_commands * 2; - glDrawArrays(GL_LINES, offset, num_elements); + Size2 render_target_size = storage->render_target_get_size(p_to_render_target); + state_buffer.screen_pixel_size[0] = 1.0 / render_target_size.x; + state_buffer.screen_pixel_size[1] = 1.0 / render_target_size.y; - storage->info.render._2d_draw_call_count++; + state_buffer.time = storage->frame.time; + state_buffer.use_pixel_snap = p_snap_2d_vertices_to_pixel; - // may not be necessary .. state change optimization still TODO - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + state_buffer.directional_light_count = 0; //directional_light_count; -#ifdef GLES_OVER_GL - if (p_anti_alias) - glDisable(GL_LINE_SMOOTH); -#endif -} + Vector2 canvas_scale = p_canvas_transform.get_scale(); -void RasterizerCanvasGLES3::_batch_render_generic(const Batch &p_batch, RasterizerStorageGLES3::Material *p_material) { - ERR_FAIL_COND(p_batch.num_commands <= 0); - - const bool &use_light_angles = bdata.use_light_angles; - const bool &use_modulate = bdata.use_modulate; - const bool &use_large_verts = bdata.use_large_verts; - const bool &colored_verts = bdata.use_colored_vertices | use_light_angles | use_modulate | use_large_verts; - - int sizeof_vert; - - switch (bdata.fvf) { - default: - sizeof_vert = 0; // prevent compiler warning - this should never happen - break; - case RasterizerStorageCommon::FVF_UNBATCHED: { - sizeof_vert = 0; // prevent compiler warning - this should never happen - return; - } break; - case RasterizerStorageCommon::FVF_REGULAR: // no change - sizeof_vert = sizeof(BatchVertex); - break; - case RasterizerStorageCommon::FVF_COLOR: - sizeof_vert = sizeof(BatchVertexColored); - break; - case RasterizerStorageCommon::FVF_LIGHT_ANGLE: - sizeof_vert = sizeof(BatchVertexLightAngled); - break; - case RasterizerStorageCommon::FVF_MODULATED: - sizeof_vert = sizeof(BatchVertexModulated); - break; - case RasterizerStorageCommon::FVF_LARGE: - sizeof_vert = sizeof(BatchVertexLarge); - break; - } + state_buffer.sdf_to_screen[0] = render_target_size.width / canvas_scale.x; + state_buffer.sdf_to_screen[1] = render_target_size.height / canvas_scale.y; - // make sure to set all conditionals BEFORE binding the shader - _set_texture_rect_mode(false, use_light_angles, use_modulate, use_large_verts); + state_buffer.screen_to_sdf[0] = 1.0 / state_buffer.sdf_to_screen[0]; + state_buffer.screen_to_sdf[1] = 1.0 / state_buffer.sdf_to_screen[1]; - // batch tex - const BatchTex &tex = bdata.batch_textures[p_batch.batch_texture_id]; - //VSG::rasterizer->gl_check_for_error(); + Rect2 sdf_rect = storage->render_target_get_sdf_rect(p_to_render_target); + Rect2 sdf_tex_rect(sdf_rect.position / canvas_scale, sdf_rect.size / canvas_scale); - // force repeat is set if non power of 2 texture, and repeat is needed if hardware doesn't support npot - if (tex.tile_mode == BatchTex::TILE_FORCE_REPEAT) { - state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_FORCE_REPEAT, true); - } + state_buffer.sdf_to_tex[0] = 1.0 / sdf_tex_rect.size.width; + state_buffer.sdf_to_tex[1] = 1.0 / sdf_tex_rect.size.height; + state_buffer.sdf_to_tex[2] = -sdf_tex_rect.position.x / sdf_tex_rect.size.width; + state_buffer.sdf_to_tex[3] = -sdf_tex_rect.position.y / sdf_tex_rect.size.height; - if (state.canvas_shader.bind()) { - _set_uniforms(); - state.canvas_shader.use_material((void *)p_material); + //print_line("w: " + itos(ssize.width) + " s: " + rtos(canvas_scale)); + state_buffer.tex_to_sdf = 1.0 / ((canvas_scale.x + canvas_scale.y) * 0.5); + glBindBufferBase(GL_UNIFORM_BUFFER, 0, state.canvas_state_buffer); + glBufferData(GL_UNIFORM_BUFFER, sizeof(StateBuffer), &state_buffer, GL_STREAM_DRAW); + glBindBuffer(GL_UNIFORM_BUFFER, 0); } - _bind_canvas_texture(tex.RID_texture, tex.RID_normal); - - // bind the index and vertex buffer - glBindBuffer(GL_ARRAY_BUFFER, bdata.gl_vertex_buffer); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bdata.gl_index_buffer); + { + state.default_filter = p_default_filter; + state.default_repeat = p_default_repeat; + } - uint64_t pointer = 0; - glVertexAttribPointer(RS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof_vert, (const void *)pointer); + state.current_tex = RID(); + state.current_tex_ptr = NULL; + state.current_normal = RID(); + state.current_specular = RID(); + state.canvas_texscreen_used = false; - // always send UVs, even within a texture specified because a shader can still use UVs - glEnableVertexAttribArray(RS::ARRAY_TEX_UV); - glVertexAttribPointer(RS::ARRAY_TEX_UV, 2, GL_FLOAT, GL_FALSE, sizeof_vert, CAST_INT_TO_UCHAR_PTR(pointer + (2 * 4))); + r_sdf_used = false; + int item_count = 0; - // color - if (!colored_verts) { - glDisableVertexAttribArray(RS::ARRAY_COLOR); - glVertexAttrib4fv(RS::ARRAY_COLOR, p_batch.color.get_data()); - } else { - glEnableVertexAttribArray(RS::ARRAY_COLOR); - glVertexAttribPointer(RS::ARRAY_COLOR, 4, GL_FLOAT, GL_FALSE, sizeof_vert, CAST_INT_TO_UCHAR_PTR(pointer + (4 * 4))); - } - - if (use_light_angles) { - glEnableVertexAttribArray(RS::ARRAY_TANGENT); - glVertexAttribPointer(RS::ARRAY_TANGENT, 1, GL_FLOAT, GL_FALSE, sizeof_vert, CAST_INT_TO_UCHAR_PTR(pointer + (8 * 4))); - } + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, storage->resources.white_tex); + Item *ci = p_item_list; + while (ci) { + // just add all items for now + items[item_count++] = ci; + + if (!ci->next || item_count == MAX_RENDER_ITEMS - 1) { + _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list); + //then reset + item_count = 0; + } - if (use_modulate) { - glEnableVertexAttribArray(RS::ARRAY_TEX_UV2); - glVertexAttribPointer(RS::ARRAY_TEX_UV2, 4, GL_FLOAT, GL_FALSE, sizeof_vert, CAST_INT_TO_UCHAR_PTR(pointer + (9 * 4))); + ci = ci->next; } +} - if (use_large_verts) { - glEnableVertexAttribArray(RS::ARRAY_BONES); - glVertexAttribPointer(RS::ARRAY_BONES, 2, GL_FLOAT, GL_FALSE, sizeof_vert, CAST_INT_TO_UCHAR_PTR(pointer + (13 * 4))); - glEnableVertexAttribArray(RS::ARRAY_WEIGHTS); - glVertexAttribPointer(RS::ARRAY_WEIGHTS, 4, GL_FLOAT, GL_FALSE, sizeof_vert, CAST_INT_TO_UCHAR_PTR(pointer + (15 * 4))); - } +void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool p_to_backbuffer) { + Item *current_clip = nullptr; - // We only want to set the GL wrapping mode if the texture is not already tiled (i.e. set in Import). - // This is an optimization left over from the legacy renderer. - // If we DID set tiling in the API, and reverted to clamped, then the next draw using this texture - // may use clamped mode incorrectly. - bool tex_is_already_tiled = tex.flags & RasterizerStorageGLES3::TEXTURE_FLAG_REPEAT; - - if (tex.tile_mode == BatchTex::TILE_NORMAL) { - // if the texture is imported as tiled, no need to set GL state, as it will already be bound with repeat - if (!tex_is_already_tiled) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - } - } + Transform2D canvas_transform_inverse = p_canvas_transform_inverse; - // we need to convert explicitly from pod Vec2 to Vector2 ... - // could use a cast but this might be unsafe in future - Vector2 tps; - tex.tex_pixel_size.to(tps); - state.canvas_shader.set_uniform(CanvasShaderGLES3::COLOR_TEXPIXEL_SIZE, tps); - - switch (p_batch.type) { - default: { - // prevent compiler warning - } break; - case RasterizerStorageCommon::BT_RECT: { - int64_t offset = p_batch.first_vert * 3; - - int num_elements = p_batch.num_commands * 6; - glDrawElements(GL_TRIANGLES, num_elements, GL_UNSIGNED_SHORT, (void *)offset); - } break; - case RasterizerStorageCommon::BT_POLY: { - int64_t offset = p_batch.first_vert; - - int num_elements = p_batch.num_commands; - glDrawArrays(GL_TRIANGLES, offset, num_elements); - } break; - } + RID framebuffer; + Vector<Color> clear_colors; - storage->info.render._2d_draw_call_count++; - - switch (tex.tile_mode) { - case BatchTex::TILE_FORCE_REPEAT: { - state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_FORCE_REPEAT, false); - } break; - case BatchTex::TILE_NORMAL: { - // if the texture is imported as tiled, no need to revert GL state - if (!tex_is_already_tiled) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - } - } break; - default: { - } break; - } + canvas_begin(); - // could these have ifs? - glDisableVertexAttribArray(RS::ARRAY_TEX_UV); - glDisableVertexAttribArray(RS::ARRAY_COLOR); - glDisableVertexAttribArray(RS::ARRAY_TANGENT); - glDisableVertexAttribArray(RS::ARRAY_TEX_UV2); - glDisableVertexAttribArray(RS::ARRAY_BONES); - glDisableVertexAttribArray(RS::ARRAY_WEIGHTS); + RID prev_material; + uint32_t index = 0; - // may not be necessary .. state change optimization still TODO - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); -} -#endif -void RasterizerCanvasGLES3::render_batches(Item::Command *const *p_commands, Item *p_current_clip, bool &r_reclip, RasterizerStorageGLES3::Material *p_material) { - int num_batches = bdata.batches.size(); + for (int i = 0; i < p_item_count; i++) { + Item *ci = items[i]; - for (int batch_num = 0; batch_num < num_batches; batch_num++) { - const Batch &batch = bdata.batches[batch_num]; + RID material = ci->material_owner == nullptr ? ci->material : ci->material_owner->material; + RasterizerStorageGLES3::Material *material_ptr = storage->material_owner.get_or_null(material); - switch (batch.type) { - case RasterizerStorageCommon::BT_RECT: { - //_batch_render_generic(batch, p_material); - } break; - case RasterizerStorageCommon::BT_POLY: { - //_batch_render_generic(batch, p_material); - } break; - case RasterizerStorageCommon::BT_LINE: { - //_batch_render_lines(batch, p_material, false); - } break; - case RasterizerStorageCommon::BT_LINE_AA: { - //_batch_render_lines(batch, p_material, true); - } break; - default: { - int end_command = batch.first_command + batch.num_commands; + if (material.is_null() && ci->canvas_group != nullptr) { + material = default_canvas_group_material; + } - for (int i = batch.first_command; i < end_command; i++) { - Item::Command *command = p_commands[i]; + if (material != prev_material) { + RasterizerStorageGLES3::Shader *shader_ptr = NULL; - switch (command->type) { -#if 0 - case Item::Command::TYPE_LINE: { - Item::CommandLine *line = static_cast<Item::CommandLine *>(command); + if (material_ptr) { + shader_ptr = material_ptr->shader; - _set_texture_rect_mode(false); + if (shader_ptr && shader_ptr->mode != RS::SHADER_CANVAS_ITEM) { + shader_ptr = NULL; // not a canvas item shader, don't use. + } + } - if (state.canvas_shader.bind()) { - _set_uniforms(); - state.canvas_shader.use_material((void *)p_material); - } + if (shader_ptr) { + if (true) { //check that shader has changed + if (shader_ptr->canvas_item.uses_time) { + RenderingServerDefault::redraw_request(); + } + //state.canvas_shader.version_bind_shader(shader_ptr->version, CanvasShaderGLES3::MODE_QUAD); + state.current_shader_version = shader_ptr->version; + } - _bind_canvas_texture(RID(), RID()); + int tc = material_ptr->textures.size(); + Pair<StringName, RID> *textures = material_ptr->textures.ptrw(); + + ShaderCompiler::GeneratedCode::Texture *texture_uniforms = shader_ptr->texture_uniforms.ptrw(); + + for (int ti = 0; ti < tc; i++) { + glActiveTexture(GL_TEXTURE0 + ti); + + RasterizerStorageGLES3::Texture *t = storage->texture_owner.get_or_null(textures[ti].second); + + if (!t) { + switch (texture_uniforms[i].hint) { + case ShaderLanguage::ShaderNode::Uniform::HINT_BLACK_ALBEDO: + case ShaderLanguage::ShaderNode::Uniform::HINT_BLACK: { + glBindTexture(GL_TEXTURE_2D, storage->resources.black_tex); + } break; + case ShaderLanguage::ShaderNode::Uniform::HINT_ANISOTROPY: { + glBindTexture(GL_TEXTURE_2D, storage->resources.aniso_tex); + } break; + case ShaderLanguage::ShaderNode::Uniform::HINT_NORMAL: { + glBindTexture(GL_TEXTURE_2D, storage->resources.normal_tex); + } break; + default: { + glBindTexture(GL_TEXTURE_2D, storage->resources.white_tex); + } break; + } + + continue; + } - glDisableVertexAttribArray(RS::ARRAY_COLOR); - glVertexAttrib4fv(RS::ARRAY_COLOR, line->color.components); + //Set texture filter and repeat texture_uniforms[i].filter texture_uniforms[i].repeat - state.canvas_shader.set_uniform(CanvasShaderGLES3::MODELVIEW_MATRIX, state.uniforms.modelview_matrix); + if (t->redraw_if_visible) { + RenderingServerDefault::redraw_request(); + } - if (line->width <= 1) { - Vector2 verts[2] = { - Vector2(line->from.x, line->from.y), - Vector2(line->to.x, line->to.y) - }; + t = t->get_ptr(); -#ifdef GLES_OVER_GL - if (line->antialiased) - glEnable(GL_LINE_SMOOTH); +#ifdef TOOLS_ENABLED + if (t->detect_normal && texture_uniforms[i].hint == ShaderLanguage::ShaderNode::Uniform::HINT_NORMAL) { + t->detect_normal(t->detect_normal_ud); + } #endif - _draw_gui_primitive(2, verts, NULL, NULL); + if (t->render_target) + t->render_target->used_in_frame = true; -#ifdef GLES_OVER_GL - if (line->antialiased) - glDisable(GL_LINE_SMOOTH); -#endif - } else { - Vector2 t = (line->from - line->to).normalized().tangent() * line->width * 0.5; - - Vector2 verts[4] = { - line->from - t, - line->from + t, - line->to + t, - line->to - t - }; - - _draw_gui_primitive(4, verts, NULL, NULL); -#ifdef GLES_OVER_GL - if (line->antialiased) { - glEnable(GL_LINE_SMOOTH); - for (int j = 0; j < 4; j++) { - Vector2 vertsl[2] = { - verts[j], - verts[(j + 1) % 4], - }; - _draw_gui_primitive(2, vertsl, NULL, NULL); - } - glDisable(GL_LINE_SMOOTH); - } -#endif - } - } break; -#endif - case Item::Command::TYPE_PRIMITIVE: { - Item::CommandPrimitive *pr = static_cast<Item::CommandPrimitive *>(command); - - switch (pr->point_count) { - case 2: { - _legacy_draw_line(pr, p_material); - } break; - default: { - _legacy_draw_primitive(pr, p_material); - } break; - } - - } break; - - case Item::Command::TYPE_RECT: { - Item::CommandRect *r = static_cast<Item::CommandRect *>(command); - _bind_quad_buffer(); - glDisableVertexAttribArray(RS::ARRAY_COLOR); - glVertexAttrib4fv(RS::ARRAY_COLOR, r->modulate.components); - - bool can_tile = true; - - // we will take account of render target textures which need to be drawn upside down - // quirk of opengl - bool upside_down = r->flags & CANVAS_RECT_FLIP_V; - - // very inefficient, improve this - if (r->texture.is_valid()) { - RasterizerStorageGLES3::Texture *texture = storage->texture_owner.get_or_null(r->texture); - - if (texture) { - if (texture->is_upside_down()) - upside_down = true; - } - } - - if (r->texture.is_valid() && r->flags & CANVAS_RECT_TILE && !storage->config.support_npot_repeat_mipmap) { - // workaround for when setting tiling does not work due to hardware limitation - - RasterizerStorageGLES3::Texture *texture = storage->texture_owner.get_or_null(r->texture); - - if (texture) { - texture = texture->get_ptr(); - - if (next_power_of_2(texture->alloc_width) != (unsigned int)texture->alloc_width && next_power_of_2(texture->alloc_height) != (unsigned int)texture->alloc_height) { - state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_FORCE_REPEAT, true); - can_tile = false; - } - } - } - - // On some widespread Nvidia cards, the normal draw method can produce some - // flickering in draw_rect and especially TileMap rendering (tiles randomly flicker). - // See GH-9913. - // To work it around, we use a simpler draw method which does not flicker, but gives - // a non negligible performance hit, so it's opt-in (GH-24466). - if (use_nvidia_rect_workaround) { - // are we using normal maps, if so we want to use light angle - bool send_light_angles = false; - - // only need to use light angles when normal mapping - // otherwise we can use the default shader - if (state.current_normal != RID()) { - send_light_angles = true; - } - - _set_texture_rect_mode(false, send_light_angles); - - if (state.canvas_shader.bind()) { - _set_uniforms(); - state.canvas_shader.use_material((void *)p_material); - } - - Vector2 points[4] = { - r->rect.position, - r->rect.position + Vector2(r->rect.size.x, 0.0), - r->rect.position + r->rect.size, - r->rect.position + Vector2(0.0, r->rect.size.y), - }; - - if (r->rect.size.x < 0) { - SWAP(points[0], points[1]); - SWAP(points[2], points[3]); - } - if (r->rect.size.y < 0) { - SWAP(points[0], points[3]); - SWAP(points[1], points[2]); - } - - // FTODO - //RasterizerStorageGLES3::Texture *texture = _bind_canvas_texture(r->texture, r->normal_map); - RasterizerStorageGLES3::Texture *texture = _bind_canvas_texture(r->texture, RID()); - - if (texture) { - Size2 texpixel_size(1.0 / texture->width, 1.0 / texture->height); - - Rect2 src_rect = (r->flags & CANVAS_RECT_REGION) ? Rect2(r->source.position * texpixel_size, r->source.size * texpixel_size) : Rect2(0, 0, 1, 1); - - Vector2 uvs[4] = { - src_rect.position, - src_rect.position + Vector2(src_rect.size.x, 0.0), - src_rect.position + src_rect.size, - src_rect.position + Vector2(0.0, src_rect.size.y), - }; - - // for encoding in light angle - bool flip_h = false; - bool flip_v = false; - - if (r->flags & CANVAS_RECT_TRANSPOSE) { - SWAP(uvs[1], uvs[3]); - } - - if (r->flags & CANVAS_RECT_FLIP_H) { - SWAP(uvs[0], uvs[1]); - SWAP(uvs[2], uvs[3]); - flip_h = true; - flip_v = !flip_v; - } - if (upside_down) { - SWAP(uvs[0], uvs[3]); - SWAP(uvs[1], uvs[2]); - flip_v = !flip_v; - } - - state.canvas_shader.set_uniform(CanvasShaderGLES3::COLOR_TEXPIXEL_SIZE, texpixel_size); - - bool untile = false; - - if (can_tile && r->flags & CANVAS_RECT_TILE && !(texture->flags & RasterizerStorageGLES3::TEXTURE_FLAG_REPEAT)) { - texture->GLSetRepeat(RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED); - untile = true; - } - - if (send_light_angles) { - // for single rects, there is no need to fully utilize the light angle, - // we only need it to encode flips (horz and vert). But the shader can be reused with - // batching in which case the angle encodes the transform as well as - // the flips. - // Note transpose is NYI. I don't think it worked either with the non-nvidia method. - - // if horizontal flip, angle is 180 - float angle = 0.0f; - if (flip_h) - angle = Math_PI; - - // add 1 (to take care of zero floating point error with sign) - angle += 1.0f; - - // flip if necessary - if (flip_v) - angle *= -1.0f; - - // light angle must be sent for each vert, instead as a single uniform in the uniform draw method - // this has the benefit of enabling batching with light angles. - float light_angles[4] = { angle, angle, angle, angle }; - - _draw_gui_primitive(4, points, NULL, uvs, light_angles); - } else { - _draw_gui_primitive(4, points, NULL, uvs); - } - - if (untile) { - texture->GLSetRepeat(RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } - } else { - static const Vector2 uvs[4] = { - Vector2(0.0, 0.0), - Vector2(0.0, 1.0), - Vector2(1.0, 1.0), - Vector2(1.0, 0.0), - }; - - state.canvas_shader.set_uniform(CanvasShaderGLES3::COLOR_TEXPIXEL_SIZE, Vector2()); - _draw_gui_primitive(4, points, NULL, uvs); - } - - } else { - // This branch is better for performance, but can produce flicker on Nvidia, see above comment. - - _set_texture_rect_mode(true); - - if (state.canvas_shader.bind()) { - _set_uniforms(); - state.canvas_shader.use_material((void *)p_material); - } - _bind_quad_buffer(); - - // FTODO - //RasterizerStorageGLES3::Texture *tex = _bind_canvas_texture(r->texture, r->normal_map); - RasterizerStorageGLES3::Texture *tex = _bind_canvas_texture(r->texture, RID()); - - if (!tex) { - Rect2 dst_rect = Rect2(r->rect.position, r->rect.size); - - if (dst_rect.size.width < 0) { - dst_rect.position.x += dst_rect.size.width; - dst_rect.size.width *= -1; - } - if (dst_rect.size.height < 0) { - dst_rect.position.y += dst_rect.size.height; - dst_rect.size.height *= -1; - } - - state.canvas_shader.set_uniform(CanvasShaderGLES3::DST_RECT, Color(dst_rect.position.x, dst_rect.position.y, dst_rect.size.x, dst_rect.size.y)); - state.canvas_shader.set_uniform(CanvasShaderGLES3::SRC_RECT, Color(0, 0, 1, 1)); - - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - storage->info.render._2d_draw_call_count++; - } else { - bool untile = false; - - if (can_tile && r->flags & CANVAS_RECT_TILE && !(tex->flags & RasterizerStorageGLES3::TEXTURE_FLAG_REPEAT)) { - tex->GLSetRepeat(RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED); - untile = true; - } - - Size2 texpixel_size(1.0 / tex->width, 1.0 / tex->height); - Rect2 src_rect = (r->flags & CANVAS_RECT_REGION) ? Rect2(r->source.position * texpixel_size, r->source.size * texpixel_size) : Rect2(0, 0, 1, 1); - - Rect2 dst_rect = Rect2(r->rect.position, r->rect.size); - - if (dst_rect.size.width < 0) { - dst_rect.position.x += dst_rect.size.width; - dst_rect.size.width *= -1; - } - if (dst_rect.size.height < 0) { - dst_rect.position.y += dst_rect.size.height; - dst_rect.size.height *= -1; - } - - if (r->flags & CANVAS_RECT_FLIP_H) { - src_rect.size.x *= -1; - } - - if (upside_down) { - src_rect.size.y *= -1; - } + glBindTexture(t->target, t->tex_id); + } - if (r->flags & CANVAS_RECT_TRANSPOSE) { - dst_rect.size.x *= -1; // Encoding in the dst_rect.z uniform - } + } else { + //state.canvas_shader.version_bind_shader(state.canvas_shader_default_version, CanvasShaderGLES3::MODE_QUAD); + state.current_shader_version = state.canvas_shader_default_version; + } + prev_material = material; + } - state.canvas_shader.set_uniform(CanvasShaderGLES3::COLOR_TEXPIXEL_SIZE, texpixel_size); + _render_item(p_to_render_target, ci, canvas_transform_inverse, current_clip, p_lights, index); + } + // Render last command + state.end_batch = true; + _render_batch(index); - state.canvas_shader.set_uniform(CanvasShaderGLES3::DST_RECT, Color(dst_rect.position.x, dst_rect.position.y, dst_rect.size.x, dst_rect.size.y)); - state.canvas_shader.set_uniform(CanvasShaderGLES3::SRC_RECT, Color(src_rect.position.x, src_rect.position.y, src_rect.size.x, src_rect.size.y)); + canvas_end(); +} - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - storage->info.render._2d_draw_call_count++; +void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, Light *p_lights, uint32_t &r_index) { + RS::CanvasItemTextureFilter current_filter = state.default_filter; + RS::CanvasItemTextureRepeat current_repeat = state.default_repeat; - if (untile) { - tex->GLSetRepeat(RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } - } + if (p_item->texture_filter != RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT) { + current_filter = p_item->texture_filter; + } - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - } + if (p_item->texture_repeat != RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT) { + current_repeat = p_item->texture_repeat; + } - state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_FORCE_REPEAT, false); + Transform2D base_transform = p_canvas_transform_inverse * p_item->final_transform; + Transform2D draw_transform; // Used by transform command - } break; - case Item::Command::TYPE_NINEPATCH: { - Item::CommandNinePatch *np = static_cast<Item::CommandNinePatch *>(command); + Color base_color = p_item->final_modulate; - _set_texture_rect_mode(false); - if (state.canvas_shader.bind()) { - _set_uniforms(); - state.canvas_shader.use_material((void *)p_material); - } - _bind_quad_buffer(); + uint32_t base_flags = 0; - glDisableVertexAttribArray(RS::ARRAY_COLOR); - glVertexAttrib4fv(RS::ARRAY_COLOR, np->color.components); + RID last_texture; + Size2 texpixel_size; - // FTODO - //RasterizerStorageGLES3::Texture *tex = _bind_canvas_texture(np->texture, np->normal_map); - RasterizerStorageGLES3::Texture *tex = _bind_canvas_texture(np->texture, RID()); + bool skipping = false; - if (!tex) { - // FIXME: Handle textureless ninepatch gracefully - WARN_PRINT("NinePatch without texture not supported yet in OpenGL backend, skipping."); - continue; - } - if (tex->width == 0 || tex->height == 0) { - WARN_PRINT("Cannot set empty texture to NinePatch."); - continue; - } + const Item::Command *c = p_item->commands; + while (c) { + if (skipping && c->type != Item::Command::TYPE_ANIMATION_SLICE) { + c = c->next; + continue; + } - Size2 texpixel_size(1.0 / tex->width, 1.0 / tex->height); + _update_transform_2d_to_mat2x3(base_transform * draw_transform, state.instance_data_array[r_index].world); + + for (int i = 0; i < 4; i++) { + state.instance_data_array[r_index].modulation[i] = 0.0; + state.instance_data_array[r_index].ninepatch_margins[i] = 0.0; + state.instance_data_array[r_index].src_rect[i] = 0.0; + state.instance_data_array[r_index].dst_rect[i] = 0.0; + state.instance_data_array[r_index].lights[i] = uint32_t(0); + } + state.instance_data_array[r_index].flags = base_flags; + state.instance_data_array[r_index].color_texture_pixel_size[0] = 0.0; + state.instance_data_array[r_index].color_texture_pixel_size[1] = 0.0; - // state.canvas_shader.set_uniform(CanvasShaderGLES3::MODELVIEW_MATRIX, state.uniforms.modelview_matrix); - state.canvas_shader.set_uniform(CanvasShaderGLES3::COLOR_TEXPIXEL_SIZE, texpixel_size); + state.instance_data_array[r_index].pad[0] = 0.0; + state.instance_data_array[r_index].pad[1] = 0.0; - Rect2 source = np->source; - if (source.size.x == 0 && source.size.y == 0) { - source.size.x = tex->width; - source.size.y = tex->height; - } + state.instance_data_array[r_index].flags = base_flags | (state.instance_data_array[r_index == 0 ? 0 : r_index - 1].flags & (FLAGS_DEFAULT_NORMAL_MAP_USED | FLAGS_DEFAULT_SPECULAR_MAP_USED)); //reset on each command for sanity, keep canvastexture binding config - float screen_scale = 1.0; + switch (c->type) { + case Item::Command::TYPE_RECT: { + const Item::CommandRect *rect = static_cast<const Item::CommandRect *>(c); - if ((bdata.settings_ninepatch_mode == 1) && (source.size.x != 0) && (source.size.y != 0)) { - screen_scale = MIN(np->rect.size.x / source.size.x, np->rect.size.y / source.size.y); - screen_scale = MIN(1.0, screen_scale); - } + if (rect->flags & CANVAS_RECT_TILE) { + current_repeat = RenderingServer::CanvasItemTextureRepeat::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED; + } - // prepare vertex buffer + if (rect->texture != last_texture || state.current_primitive_points != 0 || state.current_command != Item::Command::TYPE_RECT) { + state.end_batch = true; + _render_batch(r_index); - // this buffer contains [ POS POS UV UV ] * + state.current_primitive_points = 0; + state.current_command = Item::Command::TYPE_RECT; + } + _bind_canvas_texture(rect->texture, current_filter, current_repeat, r_index, last_texture, texpixel_size); + state.canvas_shader.version_bind_shader(state.current_shader_version, CanvasShaderGLES3::MODE_QUAD); - float buffer[16 * 2 + 16 * 2]; + Rect2 src_rect; + Rect2 dst_rect; - { - // first row + if (rect->texture != RID()) { + src_rect = (rect->flags & CANVAS_RECT_REGION) ? Rect2(rect->source.position * texpixel_size, rect->source.size * texpixel_size) : Rect2(0, 0, 1, 1); + dst_rect = Rect2(rect->rect.position, rect->rect.size); - buffer[(0 * 4 * 4) + 0] = np->rect.position.x; - buffer[(0 * 4 * 4) + 1] = np->rect.position.y; + if (dst_rect.size.width < 0) { + dst_rect.position.x += dst_rect.size.width; + dst_rect.size.width *= -1; + } + if (dst_rect.size.height < 0) { + dst_rect.position.y += dst_rect.size.height; + dst_rect.size.height *= -1; + } - buffer[(0 * 4 * 4) + 2] = source.position.x * texpixel_size.x; - buffer[(0 * 4 * 4) + 3] = source.position.y * texpixel_size.y; + if (rect->flags & CANVAS_RECT_FLIP_H) { + src_rect.size.x *= -1; + } - buffer[(0 * 4 * 4) + 4] = np->rect.position.x + np->margin[SIDE_LEFT] * screen_scale; - buffer[(0 * 4 * 4) + 5] = np->rect.position.y; + if (rect->flags & CANVAS_RECT_FLIP_V) { + src_rect.size.y *= -1; + } - buffer[(0 * 4 * 4) + 6] = (source.position.x + np->margin[SIDE_LEFT]) * texpixel_size.x; - buffer[(0 * 4 * 4) + 7] = source.position.y * texpixel_size.y; + if (rect->flags & CANVAS_RECT_TRANSPOSE) { + dst_rect.size.x *= -1; // Encoding in the dst_rect.z uniform + } - buffer[(0 * 4 * 4) + 8] = np->rect.position.x + np->rect.size.x - np->margin[SIDE_RIGHT] * screen_scale; - buffer[(0 * 4 * 4) + 9] = np->rect.position.y; + if (rect->flags & CANVAS_RECT_CLIP_UV) { + state.instance_data_array[r_index].flags |= FLAGS_CLIP_RECT_UV; + } - buffer[(0 * 4 * 4) + 10] = (source.position.x + source.size.x - np->margin[SIDE_RIGHT]) * texpixel_size.x; - buffer[(0 * 4 * 4) + 11] = source.position.y * texpixel_size.y; + } else { + dst_rect = Rect2(rect->rect.position, rect->rect.size); - buffer[(0 * 4 * 4) + 12] = np->rect.position.x + np->rect.size.x; - buffer[(0 * 4 * 4) + 13] = np->rect.position.y; + if (dst_rect.size.width < 0) { + dst_rect.position.x += dst_rect.size.width; + dst_rect.size.width *= -1; + } + if (dst_rect.size.height < 0) { + dst_rect.position.y += dst_rect.size.height; + dst_rect.size.height *= -1; + } - buffer[(0 * 4 * 4) + 14] = (source.position.x + source.size.x) * texpixel_size.x; - buffer[(0 * 4 * 4) + 15] = source.position.y * texpixel_size.y; + src_rect = Rect2(0, 0, 1, 1); + } - // second row + if (rect->flags & CANVAS_RECT_MSDF) { + state.instance_data_array[r_index].flags |= FLAGS_USE_MSDF; + state.instance_data_array[r_index].msdf[0] = rect->px_range; // Pixel range. + state.instance_data_array[r_index].msdf[1] = rect->outline; // Outline size. + state.instance_data_array[r_index].msdf[2] = 0.f; // Reserved. + state.instance_data_array[r_index].msdf[3] = 0.f; // Reserved. + } - buffer[(1 * 4 * 4) + 0] = np->rect.position.x; - buffer[(1 * 4 * 4) + 1] = np->rect.position.y + np->margin[SIDE_TOP] * screen_scale; + state.instance_data_array[r_index].modulation[0] = rect->modulate.r * base_color.r; + state.instance_data_array[r_index].modulation[1] = rect->modulate.g * base_color.g; + state.instance_data_array[r_index].modulation[2] = rect->modulate.b * base_color.b; + state.instance_data_array[r_index].modulation[3] = rect->modulate.a * base_color.a; + + state.instance_data_array[r_index].src_rect[0] = src_rect.position.x; + state.instance_data_array[r_index].src_rect[1] = src_rect.position.y; + state.instance_data_array[r_index].src_rect[2] = src_rect.size.width; + state.instance_data_array[r_index].src_rect[3] = src_rect.size.height; + + state.instance_data_array[r_index].dst_rect[0] = dst_rect.position.x; + state.instance_data_array[r_index].dst_rect[1] = dst_rect.position.y; + state.instance_data_array[r_index].dst_rect[2] = dst_rect.size.width; + state.instance_data_array[r_index].dst_rect[3] = dst_rect.size.height; + //_render_batch(r_index); + r_index++; + if (r_index >= state.max_instances_per_batch - 1) { + //r_index--; + state.end_batch = true; + _render_batch(r_index); + } + } break; - buffer[(1 * 4 * 4) + 2] = source.position.x * texpixel_size.x; - buffer[(1 * 4 * 4) + 3] = (source.position.y + np->margin[SIDE_TOP]) * texpixel_size.y; + case Item::Command::TYPE_NINEPATCH: { + /* + const Item::CommandNinePatch *np = static_cast<const Item::CommandNinePatch *>(c); - buffer[(1 * 4 * 4) + 4] = np->rect.position.x + np->margin[SIDE_LEFT] * screen_scale; - buffer[(1 * 4 * 4) + 5] = np->rect.position.y + np->margin[SIDE_TOP] * screen_scale; + //bind pipeline + { + RID pipeline = pipeline_variants->variants[light_mode][PIPELINE_VARIANT_NINEPATCH].get_render_pipeline(RD::INVALID_ID, p_framebuffer_format); + RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, pipeline); + } - buffer[(1 * 4 * 4) + 6] = (source.position.x + np->margin[SIDE_LEFT]) * texpixel_size.x; - buffer[(1 * 4 * 4) + 7] = (source.position.y + np->margin[SIDE_TOP]) * texpixel_size.y; + //bind textures - buffer[(1 * 4 * 4) + 8] = np->rect.position.x + np->rect.size.x - np->margin[SIDE_RIGHT] * screen_scale; - buffer[(1 * 4 * 4) + 9] = np->rect.position.y + np->margin[SIDE_TOP] * screen_scale; + _bind_canvas_texture(p_draw_list, np->texture, current_filter, current_repeat, index, last_texture, texpixel_size); - buffer[(1 * 4 * 4) + 10] = (source.position.x + source.size.x - np->margin[SIDE_RIGHT]) * texpixel_size.x; - buffer[(1 * 4 * 4) + 11] = (source.position.y + np->margin[SIDE_TOP]) * texpixel_size.y; + Rect2 src_rect; + Rect2 dst_rect(np->rect.position.x, np->rect.position.y, np->rect.size.x, np->rect.size.y); - buffer[(1 * 4 * 4) + 12] = np->rect.position.x + np->rect.size.x; - buffer[(1 * 4 * 4) + 13] = np->rect.position.y + np->margin[SIDE_TOP] * screen_scale; + if (np->texture == RID()) { + texpixel_size = Size2(1, 1); + src_rect = Rect2(0, 0, 1, 1); - buffer[(1 * 4 * 4) + 14] = (source.position.x + source.size.x) * texpixel_size.x; - buffer[(1 * 4 * 4) + 15] = (source.position.y + np->margin[SIDE_TOP]) * texpixel_size.y; + } else { + if (np->source != Rect2()) { + src_rect = Rect2(np->source.position.x * texpixel_size.width, np->source.position.y * texpixel_size.height, np->source.size.x * texpixel_size.width, np->source.size.y * texpixel_size.height); + state.instance_data_array[r_index].color_texture_pixel_size[0] = 1.0 / np->source.size.width; + state.instance_data_array[r_index].color_texture_pixel_size[1] = 1.0 / np->source.size.height; - // third row + } else { + src_rect = Rect2(0, 0, 1, 1); + } + } - buffer[(2 * 4 * 4) + 0] = np->rect.position.x; - buffer[(2 * 4 * 4) + 1] = np->rect.position.y + np->rect.size.y - np->margin[SIDE_BOTTOM] * screen_scale; + state.instance_data_array[r_index].modulation[0] = np->color.r * base_color.r; + state.instance_data_array[r_index].modulation[1] = np->color.g * base_color.g; + state.instance_data_array[r_index].modulation[2] = np->color.b * base_color.b; + state.instance_data_array[r_index].modulation[3] = np->color.a * base_color.a; - buffer[(2 * 4 * 4) + 2] = source.position.x * texpixel_size.x; - buffer[(2 * 4 * 4) + 3] = (source.position.y + source.size.y - np->margin[SIDE_BOTTOM]) * texpixel_size.y; + state.instance_data_array[r_index].src_rect[0] = src_rect.position.x; + state.instance_data_array[r_index].src_rect[1] = src_rect.position.y; + state.instance_data_array[r_index].src_rect[2] = src_rect.size.width; + state.instance_data_array[r_index].src_rect[3] = src_rect.size.height; - buffer[(2 * 4 * 4) + 4] = np->rect.position.x + np->margin[SIDE_LEFT] * screen_scale; - buffer[(2 * 4 * 4) + 5] = np->rect.position.y + np->rect.size.y - np->margin[SIDE_BOTTOM] * screen_scale; + state.instance_data_array[r_index].dst_rect[0] = dst_rect.position.x; + state.instance_data_array[r_index].dst_rect[1] = dst_rect.position.y; + state.instance_data_array[r_index].dst_rect[2] = dst_rect.size.width; + state.instance_data_array[r_index].dst_rect[3] = dst_rect.size.height; - buffer[(2 * 4 * 4) + 6] = (source.position.x + np->margin[SIDE_LEFT]) * texpixel_size.x; - buffer[(2 * 4 * 4) + 7] = (source.position.y + source.size.y - np->margin[SIDE_BOTTOM]) * texpixel_size.y; + state.instance_data_array[r_index].flags |= int(np->axis_x) << FLAGS_NINEPATCH_H_MODE_SHIFT; + state.instance_data_array[r_index].flags |= int(np->axis_y) << FLAGS_NINEPATCH_V_MODE_SHIFT; - buffer[(2 * 4 * 4) + 8] = np->rect.position.x + np->rect.size.x - np->margin[SIDE_RIGHT] * screen_scale; - buffer[(2 * 4 * 4) + 9] = np->rect.position.y + np->rect.size.y - np->margin[SIDE_BOTTOM] * screen_scale; + if (np->draw_center) { + state.instance_data_array[r_index].flags |= FLAGS_NINEPACH_DRAW_CENTER; + } - buffer[(2 * 4 * 4) + 10] = (source.position.x + source.size.x - np->margin[SIDE_RIGHT]) * texpixel_size.x; - buffer[(2 * 4 * 4) + 11] = (source.position.y + source.size.y - np->margin[SIDE_BOTTOM]) * texpixel_size.y; + state.instance_data_array[r_index].ninepatch_margins[0] = np->margin[SIDE_LEFT]; + state.instance_data_array[r_index].ninepatch_margins[1] = np->margin[SIDE_TOP]; + state.instance_data_array[r_index].ninepatch_margins[2] = np->margin[SIDE_RIGHT]; + state.instance_data_array[r_index].ninepatch_margins[3] = np->margin[SIDE_BOTTOM]; - buffer[(2 * 4 * 4) + 12] = np->rect.position.x + np->rect.size.x; - buffer[(2 * 4 * 4) + 13] = np->rect.position.y + np->rect.size.y - np->margin[SIDE_BOTTOM] * screen_scale; + RD::get_singleton()->draw_list_set_state.instance_data_array[r_index](p_draw_list, &state.instance_data_array[r_index], sizeof(PushConstant)); + RD::get_singleton()->draw_list_bind_index_array(p_draw_list, shader.quad_index_array); + RD::get_singleton()->draw_list_draw(p_draw_list, true); - buffer[(2 * 4 * 4) + 14] = (source.position.x + source.size.x) * texpixel_size.x; - buffer[(2 * 4 * 4) + 15] = (source.position.y + source.size.y - np->margin[SIDE_BOTTOM]) * texpixel_size.y; + // Restore if overridden. + state.instance_data_array[r_index].color_texture_pixel_size[0] = texpixel_size.x; + state.instance_data_array[r_index].color_texture_pixel_size[1] = texpixel_size.y; +*/ + } break; - // fourth row + case Item::Command::TYPE_POLYGON: { + const Item::CommandPolygon *polygon = static_cast<const Item::CommandPolygon *>(c); - buffer[(3 * 4 * 4) + 0] = np->rect.position.x; - buffer[(3 * 4 * 4) + 1] = np->rect.position.y + np->rect.size.y; + PolygonBuffers *pb = polygon_buffers.polygons.getptr(polygon->polygon.polygon_id); + ERR_CONTINUE(!pb); - buffer[(3 * 4 * 4) + 2] = source.position.x * texpixel_size.x; - buffer[(3 * 4 * 4) + 3] = (source.position.y + source.size.y) * texpixel_size.y; + if (polygon->texture != last_texture || state.current_primitive_points != 0 || state.current_command != Item::Command::TYPE_POLYGON) { + state.end_batch = true; + _render_batch(r_index); - buffer[(3 * 4 * 4) + 4] = np->rect.position.x + np->margin[SIDE_LEFT] * screen_scale; - buffer[(3 * 4 * 4) + 5] = np->rect.position.y + np->rect.size.y; + state.current_primitive_points = 0; + state.current_command = Item::Command::TYPE_POLYGON; + } + _bind_canvas_texture(polygon->texture, current_filter, current_repeat, r_index, last_texture, texpixel_size); + state.canvas_shader.version_bind_shader(state.current_shader_version, CanvasShaderGLES3::MODE_ATTRIBUTES); + + state.current_primitive = polygon->primitive; + state.instance_data_array[r_index].modulation[0] = base_color.r; + state.instance_data_array[r_index].modulation[1] = base_color.g; + state.instance_data_array[r_index].modulation[2] = base_color.b; + state.instance_data_array[r_index].modulation[3] = base_color.a; + + for (int j = 0; j < 4; j++) { + state.instance_data_array[r_index].src_rect[j] = 0; + state.instance_data_array[r_index].dst_rect[j] = 0; + state.instance_data_array[r_index].ninepatch_margins[j] = 0; + } - buffer[(3 * 4 * 4) + 6] = (source.position.x + np->margin[SIDE_LEFT]) * texpixel_size.x; - buffer[(3 * 4 * 4) + 7] = (source.position.y + source.size.y) * texpixel_size.y; + // If the previous operation is not done yet, allocated a new buffer + GLint syncStatus; + glGetSynciv(state.fences[state.current_buffer], GL_SYNC_STATUS, sizeof(GLint), nullptr, &syncStatus); + if (syncStatus == GL_UNSIGNALED) { + _allocate_instance_data_buffer(); + } else { + glDeleteSync(state.fences[state.current_buffer]); + } - buffer[(3 * 4 * 4) + 8] = np->rect.position.x + np->rect.size.x - np->margin[SIDE_RIGHT] * screen_scale; - buffer[(3 * 4 * 4) + 9] = np->rect.position.y + np->rect.size.y; + glBindBufferBase(GL_UNIFORM_BUFFER, 3, state.canvas_instance_data_buffers[state.current_buffer]); +#ifdef JAVASCRIPT_ENABLED + //WebGL 2.0 does not support mapping buffers, so use slow glBufferData instead + glBufferData(GL_UNIFORM_BUFFER, sizeof(InstanceData), &state.instance_data_array[0], GL_DYNAMIC_DRAW); +#else + void *ubo = glMapBufferRange(GL_UNIFORM_BUFFER, 0, sizeof(InstanceData), GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT); + memcpy(ubo, &state.instance_data_array[0], sizeof(InstanceData)); + glUnmapBuffer(GL_UNIFORM_BUFFER); +#endif + glBindVertexArray(pb->vertex_array); - buffer[(3 * 4 * 4) + 10] = (source.position.x + source.size.x - np->margin[SIDE_RIGHT]) * texpixel_size.x; - buffer[(3 * 4 * 4) + 11] = (source.position.y + source.size.y) * texpixel_size.y; + static const GLenum prim[5] = { GL_POINTS, GL_LINES, GL_LINE_STRIP, GL_TRIANGLES, GL_TRIANGLE_STRIP }; - buffer[(3 * 4 * 4) + 12] = np->rect.position.x + np->rect.size.x; - buffer[(3 * 4 * 4) + 13] = np->rect.position.y + np->rect.size.y; + if (pb->index_buffer != 0) { + glDrawElements(prim[polygon->primitive], pb->count, GL_UNSIGNED_INT, 0); + } else { + glDrawArrays(prim[polygon->primitive], 0, pb->count); + } + glBindVertexArray(0); + state.fences[state.current_buffer] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); - buffer[(3 * 4 * 4) + 14] = (source.position.x + source.size.x) * texpixel_size.x; - buffer[(3 * 4 * 4) + 15] = (source.position.y + source.size.y) * texpixel_size.y; - } + state.current_buffer = (state.current_buffer + 1) % state.canvas_instance_data_buffers.size(); + } break; - glBindBuffer(GL_ARRAY_BUFFER, data.ninepatch_vertices); - glBufferData(GL_ARRAY_BUFFER, sizeof(float) * (16 + 16) * 2, buffer, _buffer_upload_usage_flag); + case Item::Command::TYPE_PRIMITIVE: { + const Item::CommandPrimitive *primitive = static_cast<const Item::CommandPrimitive *>(c); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.ninepatch_elements); + if (last_texture != default_canvas_texture || state.current_primitive_points != primitive->point_count || state.current_command != Item::Command::TYPE_PRIMITIVE) { + state.end_batch = true; + _render_batch(r_index); + state.current_primitive_points = primitive->point_count; + state.current_command = Item::Command::TYPE_PRIMITIVE; + } + _bind_canvas_texture(RID(), current_filter, current_repeat, r_index, last_texture, texpixel_size); + state.canvas_shader.version_bind_shader(state.current_shader_version, CanvasShaderGLES3::MODE_PRIMITIVE); + + for (uint32_t j = 0; j < MIN(3, primitive->point_count); j++) { + state.instance_data_array[r_index].points[j * 2 + 0] = primitive->points[j].x; + state.instance_data_array[r_index].points[j * 2 + 1] = primitive->points[j].y; + state.instance_data_array[r_index].uvs[j * 2 + 0] = primitive->uvs[j].x; + state.instance_data_array[r_index].uvs[j * 2 + 1] = primitive->uvs[j].y; + Color col = primitive->colors[j] * base_color; + state.instance_data_array[r_index].colors[j * 2 + 0] = (uint32_t(Math::make_half_float(col.g)) << 16) | Math::make_half_float(col.r); + state.instance_data_array[r_index].colors[j * 2 + 1] = (uint32_t(Math::make_half_float(col.a)) << 16) | Math::make_half_float(col.b); + } + r_index++; + if (primitive->point_count == 4) { + // Reset base data + _update_transform_2d_to_mat2x3(base_transform * draw_transform, state.instance_data_array[r_index].world); + state.instance_data_array[r_index].color_texture_pixel_size[0] = 0.0; + state.instance_data_array[r_index].color_texture_pixel_size[1] = 0.0; + + state.instance_data_array[r_index].flags = base_flags | (state.instance_data_array[r_index == 0 ? 0 : r_index - 1].flags & (FLAGS_DEFAULT_NORMAL_MAP_USED | FLAGS_DEFAULT_SPECULAR_MAP_USED)); //reset on each command for sanity, keep canvastexture binding config + + for (uint32_t j = 0; j < 3; j++) { + //second half of triangle + state.instance_data_array[r_index].points[j * 2 + 0] = primitive->points[j + 1].x; + state.instance_data_array[r_index].points[j * 2 + 1] = primitive->points[j + 1].y; + state.instance_data_array[r_index].uvs[j * 2 + 0] = primitive->uvs[j + 1].x; + state.instance_data_array[r_index].uvs[j * 2 + 1] = primitive->uvs[j + 1].y; + Color col = primitive->colors[j + 1] * base_color; + state.instance_data_array[r_index].colors[j * 2 + 0] = (uint32_t(Math::make_half_float(col.g)) << 16) | Math::make_half_float(col.r); + state.instance_data_array[r_index].colors[j * 2 + 1] = (uint32_t(Math::make_half_float(col.a)) << 16) | Math::make_half_float(col.b); + } + r_index++; + } + if (r_index >= state.max_instances_per_batch - 1) { + //r_index--; + state.end_batch = true; + _render_batch(r_index); + } + } break; - //glEnableVertexAttribArray(RS::ARRAY_VERTEX); - glEnableVertexAttribArray(RS::ARRAY_TEX_UV); + case Item::Command::TYPE_MESH: + case Item::Command::TYPE_MULTIMESH: + case Item::Command::TYPE_PARTICLES: { + /* + RID mesh; + RID mesh_instance; + RID texture; + Color modulate(1, 1, 1, 1); + int instance_count = 1; + + if (c->type == Item::Command::TYPE_MESH) { + const Item::CommandMesh *m = static_cast<const Item::CommandMesh *>(c); + mesh = m->mesh; + mesh_instance = m->mesh_instance; + texture = m->texture; + modulate = m->modulate; + _update_transform_2d_to_mat2x3(base_transform * draw_transform * m->transform, state.instance_data_array[r_index].world); + } else if (c->type == Item::Command::TYPE_MULTIMESH) { + const Item::CommandMultiMesh *mm = static_cast<const Item::CommandMultiMesh *>(c); + RID multimesh = mm->multimesh; + mesh = storage->multimesh_get_mesh(multimesh); + texture = mm->texture; + + if (storage->multimesh_get_transform_format(multimesh) != RS::MULTIMESH_TRANSFORM_2D) { + break; + } - //glVertexAttribPointer(RS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), NULL); - glVertexAttribPointer(RS::ARRAY_TEX_UV, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), CAST_INT_TO_UCHAR_PTR((sizeof(float) * 2))); + instance_count = storage->multimesh_get_instances_to_draw(multimesh); - //glDrawElements(GL_TRIANGLES, 18 * 3 - (np->draw_center ? 0 : 6), GL_UNSIGNED_SHORT, NULL); + if (instance_count == 0) { + break; + } - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - storage->info.render._2d_draw_call_count++; + state.instance_data_array[r_index].flags |= 1; //multimesh, trails disabled + if (storage->multimesh_uses_colors(multimesh)) { + state.instance_data_array[r_index].flags |= FLAGS_INSTANCING_HAS_COLORS; + } + if (storage->multimesh_uses_custom_data(multimesh)) { + state.instance_data_array[r_index].flags |= FLAGS_INSTANCING_HAS_CUSTOM_DATA; + } + } - } break; -#if 0 - case Item::Command::TYPE_CIRCLE: { - Item::CommandCircle *circle = static_cast<Item::CommandCircle *>(command); + // TODO: implement particles here - _set_texture_rect_mode(false); + if (mesh.is_null()) { + break; + } - if (state.canvas_shader.bind()) { - _set_uniforms(); - state.canvas_shader.use_material((void *)p_material); - } + if (texture != last_texture || state.current_primitive_points != 0 || state.current_command != Item::Command::TYPE_PRIMITIVE) { + state.end_batch = true; + _render_batch(r_index); + state.current_primitive_points = 0; + state.current_command = c->type; + } - static const int num_points = 32; + _bind_canvas_texture(texture, current_filter, current_repeat, r_index, last_texture, texpixel_size); - Vector2 points[num_points + 1]; - points[num_points] = circle->pos; + uint32_t surf_count = storage->mesh_get_surface_count(mesh); - int indices[num_points * 3]; + state.instance_data_array[r_index].modulation[0] = base_color.r * modulate.r; + state.instance_data_array[r_index].modulation[1] = base_color.g * modulate.g; + state.instance_data_array[r_index].modulation[2] = base_color.b * modulate.b; + state.instance_data_array[r_index].modulation[3] = base_color.a * modulate.a; - for (int j = 0; j < num_points; j++) { - points[j] = circle->pos + Vector2(Math::sin(j * Math_PI * 2.0 / num_points), Math::cos(j * Math_PI * 2.0 / num_points)) * circle->radius; - indices[j * 3 + 0] = j; - indices[j * 3 + 1] = (j + 1) % num_points; - indices[j * 3 + 2] = num_points; - } + for (int j = 0; j < 4; j++) { + state.instance_data_array[r_index].src_rect[j] = 0; + state.instance_data_array[r_index].dst_rect[j] = 0; + state.instance_data_array[r_index].ninepatch_margins[j] = 0; + } - _bind_canvas_texture(RID(), RID()); + for (uint32_t j = 0; j < surf_count; j++) { + RS::SurfaceData *surface = storage->mesh_get_surface(mesh, j); - _draw_polygon(indices, num_points * 3, num_points + 1, points, NULL, &circle->color, true); - } break; -#endif - case Item::Command::TYPE_POLYGON: { - Item::CommandPolygon *polygon = static_cast<Item::CommandPolygon *>(command); - //const PolyData &pd = _polydata[polygon->polygon.polygon_id]; - - switch (polygon->primitive) { - case RS::PRIMITIVE_TRIANGLES: { - _legacy_draw_poly_triangles(polygon, p_material); - } break; - default: - break; - } - - } break; -#if 0 - case Item::Command::TYPE_MESH: { - Item::CommandMesh *mesh = static_cast<Item::CommandMesh *>(command); - - _set_texture_rect_mode(false); - - if (state.canvas_shader.bind()) { - _set_uniforms(); - state.canvas_shader.use_material((void *)p_material); - } - - RasterizerStorageGLES3::Texture *texture = _bind_canvas_texture(mesh->texture, mesh->normal_map); - - if (texture) { - Size2 texpixel_size(1.0 / texture->width, 1.0 / texture->height); - state.canvas_shader.set_uniform(CanvasShaderGLES3::COLOR_TEXPIXEL_SIZE, texpixel_size); - } + RS::PrimitiveType primitive = storage->mesh_surface_get_primitive(surface); + ERR_CONTINUE(primitive < 0 || primitive >= RS::PRIMITIVE_MAX); - RasterizerStorageGLES3::Mesh *mesh_data = storage->mesh_owner.get_or_null(mesh->mesh); - if (mesh_data) { - for (int j = 0; j < mesh_data->surfaces.size(); j++) { - RasterizerStorageGLES3::Surface *s = mesh_data->surfaces[j]; - // materials are ignored in 2D meshes, could be added but many things (ie, lighting mode, reading from screen, etc) would break as they are not meant be set up at this point of drawing - - glBindBuffer(GL_ARRAY_BUFFER, s->vertex_id); - - if (s->index_array_len > 0) { - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, s->index_id); - } - - for (int k = 0; k < RS::ARRAY_MAX - 1; k++) { - if (s->attribs[k].enabled) { - glEnableVertexAttribArray(k); - glVertexAttribPointer(s->attribs[k].index, s->attribs[k].size, s->attribs[k].type, s->attribs[k].normalized, s->attribs[k].stride, CAST_INT_TO_UCHAR_PTR(s->attribs[k].offset)); - } else { - glDisableVertexAttribArray(k); - switch (k) { - case RS::ARRAY_NORMAL: { - glVertexAttrib4f(RS::ARRAY_NORMAL, 0.0, 0.0, 1, 1); - } break; - case RS::ARRAY_COLOR: { - glVertexAttrib4f(RS::ARRAY_COLOR, 1, 1, 1, 1); - - } break; - default: { - } - } - } - } - - if (s->index_array_len > 0) { - glDrawElements(gl_primitive[s->primitive], s->index_array_len, (s->array_len >= (1 << 16)) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT, 0); - } else { - glDrawArrays(gl_primitive[s->primitive], 0, s->array_len); - } - } - - for (int j = 1; j < RS::ARRAY_MAX - 1; j++) { - glDisableVertexAttribArray(j); - } - } - - storage->info.render._2d_draw_call_count++; - } break; - case Item::Command::TYPE_MULTIMESH: { - Item::CommandMultiMesh *mmesh = static_cast<Item::CommandMultiMesh *>(command); - - RasterizerStorageGLES3::MultiMesh *multi_mesh = storage->multimesh_owner.get_or_null(mmesh->multimesh); - - if (!multi_mesh) - break; - - RasterizerStorageGLES3::Mesh *mesh_data = storage->mesh_owner.get_or_null(multi_mesh->mesh); - - if (!mesh_data) - break; - - state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCE_CUSTOM, multi_mesh->custom_data_format != RS::MULTIMESH_CUSTOM_DATA_NONE); - state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCING, true); - _set_texture_rect_mode(false); - - if (state.canvas_shader.bind()) { - _set_uniforms(); - state.canvas_shader.use_material((void *)p_material); - } - - RasterizerStorageGLES3::Texture *texture = _bind_canvas_texture(mmesh->texture, mmesh->normal_map); - - if (texture) { - Size2 texpixel_size(1.0 / texture->width, 1.0 / texture->height); - state.canvas_shader.set_uniform(CanvasShaderGLES3::COLOR_TEXPIXEL_SIZE, texpixel_size); - } - - //reset shader and force rebind - - int amount = MIN(multi_mesh->size, multi_mesh->visible_instances); - - if (amount == -1) { - amount = multi_mesh->size; - } - - int stride = multi_mesh->color_floats + multi_mesh->custom_data_floats + multi_mesh->xform_floats; - - int color_ofs = multi_mesh->xform_floats; - int custom_data_ofs = color_ofs + multi_mesh->color_floats; - - // drawing - - const float *base_buffer = multi_mesh->data.ptr(); - - for (int j = 0; j < mesh_data->surfaces.size(); j++) { - RasterizerStorageGLES3::Surface *s = mesh_data->surfaces[j]; - // materials are ignored in 2D meshes, could be added but many things (ie, lighting mode, reading from screen, etc) would break as they are not meant be set up at this point of drawing - - //bind buffers for mesh surface - glBindBuffer(GL_ARRAY_BUFFER, s->vertex_id); - - if (s->index_array_len > 0) { - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, s->index_id); - } - - for (int k = 0; k < RS::ARRAY_MAX - 1; k++) { - if (s->attribs[k].enabled) { - glEnableVertexAttribArray(k); - glVertexAttribPointer(s->attribs[k].index, s->attribs[k].size, s->attribs[k].type, s->attribs[k].normalized, s->attribs[k].stride, CAST_INT_TO_UCHAR_PTR(s->attribs[k].offset)); - } else { - glDisableVertexAttribArray(k); - switch (k) { - case RS::ARRAY_NORMAL: { - glVertexAttrib4f(RS::ARRAY_NORMAL, 0.0, 0.0, 1, 1); - } break; - case RS::ARRAY_COLOR: { - glVertexAttrib4f(RS::ARRAY_COLOR, 1, 1, 1, 1); - - } break; - default: { - } - } - } - } - - for (int k = 0; k < amount; k++) { - const float *buffer = base_buffer + k * stride; - - { - glVertexAttrib4fv(INSTANCE_ATTRIB_BASE + 0, &buffer[0]); - glVertexAttrib4fv(INSTANCE_ATTRIB_BASE + 1, &buffer[4]); - if (multi_mesh->transform_format == RS::MULTIMESH_TRANSFORM_3D) { - glVertexAttrib4fv(INSTANCE_ATTRIB_BASE + 2, &buffer[8]); - } else { - glVertexAttrib4f(INSTANCE_ATTRIB_BASE + 2, 0.0, 0.0, 1.0, 0.0); - } - } - - if (multi_mesh->color_floats) { - if (multi_mesh->color_format == RS::MULTIMESH_COLOR_8BIT) { - uint8_t *color_data = (uint8_t *)(buffer + color_ofs); - glVertexAttrib4f(INSTANCE_ATTRIB_BASE + 3, color_data[0] / 255.0, color_data[1] / 255.0, color_data[2] / 255.0, color_data[3] / 255.0); - } else { - glVertexAttrib4fv(INSTANCE_ATTRIB_BASE + 3, buffer + color_ofs); - } - } else { - glVertexAttrib4f(INSTANCE_ATTRIB_BASE + 3, 1.0, 1.0, 1.0, 1.0); - } - - if (multi_mesh->custom_data_floats) { - if (multi_mesh->custom_data_format == RS::MULTIMESH_CUSTOM_DATA_8BIT) { - uint8_t *custom_data = (uint8_t *)(buffer + custom_data_ofs); - glVertexAttrib4f(INSTANCE_ATTRIB_BASE + 4, custom_data[0] / 255.0, custom_data[1] / 255.0, custom_data[2] / 255.0, custom_data[3] / 255.0); - } else { - glVertexAttrib4fv(INSTANCE_ATTRIB_BASE + 4, buffer + custom_data_ofs); - } - } - - if (s->index_array_len > 0) { - glDrawElements(gl_primitive[s->primitive], s->index_array_len, (s->array_len >= (1 << 16)) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT, 0); - } else { - glDrawArrays(gl_primitive[s->primitive], 0, s->array_len); - } - } - } - - // LIGHT ANGLE PR replaced USE_INSTANCE_CUSTOM line with below .. think it was a typo, - // but just in case, made this note. - //_set_texture_rect_mode(false); - state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCE_CUSTOM, false); - state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCING, false); - - storage->info.render._2d_draw_call_count++; - } break; - case Item::Command::TYPE_POLYLINE: { - Item::CommandPolyLine *pline = static_cast<Item::CommandPolyLine *>(command); - - _set_texture_rect_mode(false); - - if (state.canvas_shader.bind()) { - _set_uniforms(); - state.canvas_shader.use_material((void *)p_material); - } - - _bind_canvas_texture(RID(), RID()); - - if (pline->triangles.size()) { - _draw_generic(GL_TRIANGLE_STRIP, pline->triangles.size(), pline->triangles.ptr(), NULL, pline->triangle_colors.ptr(), pline->triangle_colors.size() == 1); -#ifdef GLES_OVER_GL - glEnable(GL_LINE_SMOOTH); - if (pline->multiline) { - //needs to be different - } else { - _draw_generic(GL_LINE_LOOP, pline->lines.size(), pline->lines.ptr(), NULL, pline->line_colors.ptr(), pline->line_colors.size() == 1); - } - glDisable(GL_LINE_SMOOTH); -#endif - } else { -#ifdef GLES_OVER_GL - if (pline->antialiased) - glEnable(GL_LINE_SMOOTH); -#endif + glBindVertexArray(surface->vertex_array); + static const GLenum prim[5] = { GL_POINTS, GL_LINES, GL_LINE_STRIP, GL_TRIANGLES, GL_TRIANGLE_STRIP }; - if (pline->multiline) { - int todo = pline->lines.size() / 2; - int max_per_call = data.polygon_buffer_size / (sizeof(real_t) * 4); - int offset = 0; - - while (todo) { - int to_draw = MIN(max_per_call, todo); - _draw_generic(GL_LINES, to_draw * 2, &pline->lines.ptr()[offset], NULL, pline->line_colors.size() == 1 ? pline->line_colors.ptr() : &pline->line_colors.ptr()[offset], pline->line_colors.size() == 1); - todo -= to_draw; - offset += to_draw * 2; - } - } else { - _draw_generic(GL_LINE_STRIP, pline->lines.size(), pline->lines.ptr(), NULL, pline->line_colors.ptr(), pline->line_colors.size() == 1); - } - -#ifdef GLES_OVER_GL - if (pline->antialiased) - glDisable(GL_LINE_SMOOTH); -#endif - } - } break; - - case Item::Command::TYPE_PRIMITIVE: { - Item::CommandPrimitive *primitive = static_cast<Item::CommandPrimitive *>(command); - _set_texture_rect_mode(false); - - if (state.canvas_shader.bind()) { - _set_uniforms(); - state.canvas_shader.use_material((void *)p_material); - } - - ERR_CONTINUE(primitive->points.size() < 1); - - RasterizerStorageGLES3::Texture *texture = _bind_canvas_texture(primitive->texture, primitive->normal_map); - - if (texture) { - Size2 texpixel_size(1.0 / texture->width, 1.0 / texture->height); - state.canvas_shader.set_uniform(CanvasShaderGLES3::COLOR_TEXPIXEL_SIZE, texpixel_size); - } - - // we need a temporary because this must be nulled out - // if only a single color specified - const Color *colors = primitive->colors.ptr(); - if (primitive->colors.size() == 1 && primitive->points.size() > 1) { - Color c = primitive->colors[0]; - glVertexAttrib4f(RS::ARRAY_COLOR, c.r, c.g, c.b, c.a); - colors = nullptr; - } else if (primitive->colors.empty()) { - glVertexAttrib4f(RS::ARRAY_COLOR, 1, 1, 1, 1); - } -#ifdef RASTERIZER_EXTRA_CHECKS - else { - RAST_DEV_DEBUG_ASSERT(primitive->colors.size() == primitive->points.size()); - } - - if (primitive->uvs.ptr()) { - RAST_DEV_DEBUG_ASSERT(primitive->uvs.size() == primitive->points.size()); - } -#endif + // Draw directly, no need to batch + } + */ + } break; + case Item::Command::TYPE_TRANSFORM: { + const Item::CommandTransform *transform = static_cast<const Item::CommandTransform *>(c); + draw_transform = transform->xform; + } break; - _draw_gui_primitive(primitive->points.size(), primitive->points.ptr(), colors, primitive->uvs.ptr()); - } break; -#endif - case Item::Command::TYPE_TRANSFORM: { - Item::CommandTransform *transform = static_cast<Item::CommandTransform *>(command); - state.uniforms.extra_matrix = transform->xform; - state.canvas_shader.set_uniform(CanvasShaderGLES3::EXTRA_MATRIX, state.uniforms.extra_matrix); - } break; - - case Item::Command::TYPE_PARTICLES: { - } break; - case Item::Command::TYPE_CLIP_IGNORE: { - Item::CommandClipIgnore *ci = static_cast<Item::CommandClipIgnore *>(command); - if (p_current_clip) { - if (ci->ignore != r_reclip) { - if (ci->ignore) { - glDisable(GL_SCISSOR_TEST); - r_reclip = true; - } else { - glEnable(GL_SCISSOR_TEST); - - int x = p_current_clip->final_clip_rect.position.x; - int y = storage->frame.current_rt->height - (p_current_clip->final_clip_rect.position.y + p_current_clip->final_clip_rect.size.y); - int w = p_current_clip->final_clip_rect.size.x; - int h = p_current_clip->final_clip_rect.size.y; - - // FTODO - // if (storage->frame.current_rt->flags[RendererStorage::RENDER_TARGET_VFLIP]) - // y = p_current_clip->final_clip_rect.position.y; - - glScissor(x, y, w, h); - - r_reclip = false; - } - } - } - - } break; - default: { - // FIXME: Proper error handling if relevant - //print_line("other"); - } break; + case Item::Command::TYPE_CLIP_IGNORE: { + /* + const Item::CommandClipIgnore *ci = static_cast<const Item::CommandClipIgnore *>(c); + if (current_clip) { + if (ci->ignore != reclip) { + if (ci->ignore) { + RD::get_singleton()->draw_list_disable_scissor(p_draw_list); + reclip = true; + } else { + RD::get_singleton()->draw_list_enable_scissor(p_draw_list, current_clip->final_clip_rect); + reclip = false; + } } } + */ + } break; + case Item::Command::TYPE_ANIMATION_SLICE: { + /* + const Item::CommandAnimationSlice *as = static_cast<const Item::CommandAnimationSlice *>(c); + double current_time = RendererCompositorRD::singleton->get_total_time(); + double local_time = Math::fposmod(current_time - as->offset, as->animation_length); + skipping = !(local_time >= as->slice_begin && local_time < as->slice_end); + + RenderingServerDefault::redraw_request(); // animation visible means redraw request + */ + } break; + } - } // default - break; + c = c->next; + } +} + +void RasterizerCanvasGLES3::_render_batch(uint32_t &r_index) { + if (state.end_batch && r_index > 0) { + // If the previous operation is not done yet, allocate a new buffer + GLint syncStatus; + glGetSynciv(state.fences[state.current_buffer], GL_SYNC_STATUS, sizeof(GLint), nullptr, &syncStatus); + if (syncStatus == GL_UNSIGNALED) { + _allocate_instance_data_buffer(); + } else { + glDeleteSync(state.fences[state.current_buffer]); } + + glBindBufferBase(GL_UNIFORM_BUFFER, 3, state.canvas_instance_data_buffers[state.current_buffer]); +#ifdef JAVASCRIPT_ENABLED + //WebGL 2.0 does not support mapping buffers, so use slow glBufferData instead + glBufferData(GL_UNIFORM_BUFFER, sizeof(InstanceData) * r_index, state.instance_data_array, GL_DYNAMIC_DRAW); +#else + void *ubo = glMapBufferRange(GL_UNIFORM_BUFFER, 0, sizeof(InstanceData) * r_index, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT); + memcpy(ubo, state.instance_data_array, sizeof(InstanceData) * r_index); + glUnmapBuffer(GL_UNIFORM_BUFFER); +#endif + glBindVertexArray(data.canvas_quad_array); + if (state.current_primitive_points == 0) { + glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, r_index); + } else { + static const GLenum prim[5] = { GL_POINTS, GL_POINTS, GL_LINES, GL_TRIANGLES, GL_TRIANGLES }; + glDrawArraysInstanced(prim[state.current_primitive_points], 0, state.current_primitive_points, r_index); + } + glBindBuffer(GL_UNIFORM_BUFFER, 0); + + state.fences[state.current_buffer] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + state.current_buffer = (state.current_buffer + 1) % state.canvas_instance_data_buffers.size(); + state.end_batch = false; + //copy the new data into the base of the batch + for (int i = 0; i < 4; i++) { + state.instance_data_array[0].modulation[i] = state.instance_data_array[r_index].modulation[i]; + state.instance_data_array[0].ninepatch_margins[i] = state.instance_data_array[r_index].ninepatch_margins[i]; + state.instance_data_array[0].src_rect[i] = state.instance_data_array[r_index].src_rect[i]; + state.instance_data_array[0].dst_rect[i] = state.instance_data_array[r_index].dst_rect[i]; + state.instance_data_array[0].lights[i] = state.instance_data_array[r_index].lights[i]; + } + state.instance_data_array[0].flags = state.instance_data_array[r_index].flags; + state.instance_data_array[0].color_texture_pixel_size[0] = state.instance_data_array[r_index].color_texture_pixel_size[0]; + state.instance_data_array[0].color_texture_pixel_size[1] = state.instance_data_array[r_index].color_texture_pixel_size[1]; + + state.instance_data_array[0].pad[0] = state.instance_data_array[r_index].pad[0]; + state.instance_data_array[0].pad[1] = state.instance_data_array[r_index].pad[1]; + for (int i = 0; i < 6; i++) { + state.instance_data_array[0].world[i] = state.instance_data_array[r_index].world[i]; + } + + r_index = 0; } } -void RasterizerCanvasGLES3::canvas_end() { - batch_canvas_end(); - RasterizerCanvasBaseGLES3::canvas_end(); +// TODO maybe dont use +void RasterizerCanvasGLES3::_end_batch(uint32_t &r_index) { + for (int i = 0; i < 4; i++) { + state.instance_data_array[r_index].modulation[i] = 0.0; + state.instance_data_array[r_index].ninepatch_margins[i] = 0.0; + state.instance_data_array[r_index].src_rect[i] = 0.0; + state.instance_data_array[r_index].dst_rect[i] = 0.0; + } + state.instance_data_array[r_index].flags = uint32_t(0); + state.instance_data_array[r_index].color_texture_pixel_size[0] = 0.0; + state.instance_data_array[r_index].color_texture_pixel_size[1] = 0.0; + + state.instance_data_array[r_index].pad[0] = 0.0; + state.instance_data_array[r_index].pad[1] = 0.0; + + state.instance_data_array[r_index].lights[0] = uint32_t(0); + state.instance_data_array[r_index].lights[1] = uint32_t(0); + state.instance_data_array[r_index].lights[2] = uint32_t(0); + state.instance_data_array[r_index].lights[3] = uint32_t(0); } -void RasterizerCanvasGLES3::canvas_begin() { - batch_canvas_begin(); - RasterizerCanvasBaseGLES3::canvas_begin(); +RID RasterizerCanvasGLES3::light_create() { + return RID(); } -void RasterizerCanvasGLES3::canvas_render_items_begin(const Color &p_modulate, Light *p_light, const Transform2D &p_base_transform) { - batch_canvas_render_items_begin(p_modulate, p_light, p_base_transform); +void RasterizerCanvasGLES3::light_set_texture(RID p_rid, RID p_texture) { } -void RasterizerCanvasGLES3::canvas_render_items_end() { - batch_canvas_render_items_end(); +void RasterizerCanvasGLES3::light_set_use_shadow(RID p_rid, bool p_enable) { } -void RasterizerCanvasGLES3::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) { - storage->frame.current_rt = nullptr; +void RasterizerCanvasGLES3::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) { +} - // first set the current render target - storage->_set_current_render_target(p_to_render_target); +void RasterizerCanvasGLES3::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) { +} - // binds the render target (framebuffer) - canvas_begin(); +void RasterizerCanvasGLES3::render_sdf(RID p_render_target, LightOccluderInstance *p_occluders) { +} - canvas_render_items_begin(p_modulate, p_light_list, p_canvas_transform); - canvas_render_items_internal(p_item_list, 0, p_modulate, p_light_list, p_canvas_transform); - canvas_render_items_end(); +RID RasterizerCanvasGLES3::occluder_polygon_create() { + return RID(); +} - canvas_end(); +void RasterizerCanvasGLES3::occluder_polygon_set_shape(RID p_occluder, const Vector<Vector2> &p_points, bool p_closed) { +} - // not sure why these are needed to get frame to render? - storage->_set_current_render_target(RID()); - // storage->frame.current_rt = nullptr; - // canvas_begin(); - // canvas_end(); +void RasterizerCanvasGLES3::occluder_polygon_set_cull_mode(RID p_occluder, RS::CanvasOccluderPolygonCullMode p_mode) { } -void RasterizerCanvasGLES3::canvas_render_items_internal(Item *p_item_list, int p_z, const Color &p_modulate, Light *p_light, const Transform2D &p_base_transform) { - batch_canvas_render_items(p_item_list, p_z, p_modulate, p_light, p_base_transform); +void RasterizerCanvasGLES3::set_shadow_texture_size(int p_size) { +} - //glClearColor(Math::randf(), 0, 1, 1); +bool RasterizerCanvasGLES3::free(RID p_rid) { + return true; } -void RasterizerCanvasGLES3::canvas_render_items_implementation(Item *p_item_list, int p_z, const Color &p_modulate, Light *p_light, const Transform2D &p_base_transform) { - // parameters are easier to pass around in a structure - RenderItemState ris; - ris.item_group_z = p_z; - ris.item_group_modulate = p_modulate; - ris.item_group_light = p_light; - ris.item_group_base_transform = p_base_transform; +void RasterizerCanvasGLES3::update() { +} - state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_SKELETON, false); +void RasterizerCanvasGLES3::canvas_begin() { + state.using_transparent_rt = false; - state.current_tex = RID(); - state.current_tex_ptr = NULL; - state.current_normal = RID(); - state.canvas_texscreen_used = false; + if (storage->frame.current_rt) { + storage->bind_framebuffer(storage->frame.current_rt->fbo); + state.using_transparent_rt = storage->frame.current_rt->flags[RendererStorage::RENDER_TARGET_TRANSPARENT]; + } + + if (storage->frame.current_rt && storage->frame.current_rt->clear_requested) { + const Color &col = storage->frame.current_rt->clear_color; + glClearColor(col.r, col.g, col.b, col.a); + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + storage->frame.current_rt->clear_requested = false; + } + + reset_canvas(); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, storage->resources.white_tex); +} - while (p_item_list) { - Item *ci = p_item_list; - _legacy_canvas_render_item(ci, ris); - p_item_list = p_item_list->next; +void RasterizerCanvasGLES3::canvas_end() { + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_UNIFORM_BUFFER, 0); +} + +void RasterizerCanvasGLES3::_bind_canvas_texture(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, uint32_t &r_index, RID &r_last_texture, Size2 &r_texpixel_size) { + if (p_texture == RID()) { + p_texture = default_canvas_texture; } - if (ris.current_clip) { - glDisable(GL_SCISSOR_TEST); + if (r_last_texture == p_texture) { + return; //nothing to do, its the same } - state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_SKELETON, false); -} + state.end_batch = true; + _render_batch(r_index); -// Legacy non-batched implementation for regression testing. -// Should be removed after testing phase to avoid duplicate codepaths. -void RasterizerCanvasGLES3::_legacy_canvas_render_item(Item *p_ci, RenderItemState &r_ris) { - storage->info.render._2d_item_count++; + RasterizerStorageGLES3::CanvasTexture *ct = nullptr; - // defaults - state.current_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR; - state.current_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED; + RasterizerStorageGLES3::Texture *t = storage->texture_owner.get_or_null(p_texture); + + if (t) { + //regular texture + if (!t->canvas_texture) { + t->canvas_texture = memnew(RasterizerStorageGLES3::CanvasTexture); + t->canvas_texture->diffuse = p_texture; + } - if (p_ci->texture_filter != RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT) { - state.current_filter = p_ci->texture_filter; + ct = t->canvas_texture; + } else { + ct = storage->canvas_texture_owner.get_or_null(p_texture); } - if (p_ci->texture_repeat != RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT) { - state.current_repeat = p_ci->texture_repeat; + if (!ct) { + // Invalid Texture RID. + _bind_canvas_texture(default_canvas_texture, p_base_filter, p_base_repeat, r_index, r_last_texture, r_texpixel_size); + return; } - if (r_ris.current_clip != p_ci->final_clip_owner) { - r_ris.current_clip = p_ci->final_clip_owner; - - if (r_ris.current_clip) { - glEnable(GL_SCISSOR_TEST); - int y = storage->_dims.rt_height - (r_ris.current_clip->final_clip_rect.position.y + r_ris.current_clip->final_clip_rect.size.y); - // int y = storage->frame.current_rt->height - (r_ris.current_clip->final_clip_rect.position.y + r_ris.current_clip->final_clip_rect.size.y); - // FTODO - // if (storage->frame.current_rt->flags[RendererStorage::RENDER_TARGET_VFLIP]) - // y = r_ris.current_clip->final_clip_rect.position.y; - glScissor(r_ris.current_clip->final_clip_rect.position.x, y, r_ris.current_clip->final_clip_rect.size.width, r_ris.current_clip->final_clip_rect.size.height); - - // debug VFLIP - // if ((r_ris.current_clip->final_clip_rect.position.x == 223) - // && (y == 54) - // && (r_ris.current_clip->final_clip_rect.size.width == 1383)) - // { - // glScissor(r_ris.current_clip->final_clip_rect.position.x, y, r_ris.current_clip->final_clip_rect.size.width, r_ris.current_clip->final_clip_rect.size.height); - // } + RS::CanvasItemTextureFilter filter = ct->texture_filter != RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT ? ct->texture_filter : p_base_filter; + ERR_FAIL_COND(filter == RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT); - } else { - glDisable(GL_SCISSOR_TEST); - } + RS::CanvasItemTextureRepeat repeat = ct->texture_repeat != RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT ? ct->texture_repeat : p_base_repeat; + ERR_FAIL_COND(repeat == RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT); + + RasterizerStorageGLES3::Texture *texture = storage->texture_owner.get_or_null(ct->diffuse); + + if (!texture) { + state.current_tex = RID(); + state.current_tex_ptr = NULL; + ct->size_cache = Size2i(1, 1); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, storage->resources.white_tex); + + } else { + texture = texture->get_ptr(); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture->tex_id); + + state.current_tex = ct->diffuse; + state.current_tex_ptr = texture; + ct->size_cache = Size2i(texture->width, texture->height); + + texture->GLSetFilter(GL_TEXTURE_2D, filter); + texture->GLSetRepeat(GL_TEXTURE_2D, repeat); } - // TODO: copy back buffer + RasterizerStorageGLES3::Texture *normal_map = storage->texture_owner.get_or_null(ct->normal_map); - if (p_ci->copy_back_buffer) { - if (p_ci->copy_back_buffer->full) { - _copy_texscreen(Rect2()); - } else { - _copy_texscreen(p_ci->copy_back_buffer->rect); - } + if (!normal_map) { + state.current_normal = RID(); + ct->use_normal_cache = false; + glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 6); + glBindTexture(GL_TEXTURE_2D, storage->resources.normal_tex); + + } else { + normal_map = normal_map->get_ptr(); + + glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 6); + glBindTexture(GL_TEXTURE_2D, normal_map->tex_id); + state.current_normal = ct->normal_map; + ct->use_normal_cache = true; + texture->GLSetFilter(GL_TEXTURE_2D, filter); + texture->GLSetRepeat(GL_TEXTURE_2D, repeat); } -#if 0 - RasterizerStorageGLES3::Skeleton *skeleton = NULL; + RasterizerStorageGLES3::Texture *specular_map = storage->texture_owner.get_or_null(ct->specular); - { - //skeleton handling - if (p_ci->skeleton.is_valid() && storage->skeleton_owner.owns(p_ci->skeleton)) { - skeleton = storage->skeleton_owner.get(p_ci->skeleton); - if (!skeleton->use_2d) { - skeleton = NULL; - } else { - state.skeleton_transform = r_ris.item_group_base_transform * skeleton->base_transform_2d; - state.skeleton_transform_inverse = state.skeleton_transform.affine_inverse(); - state.skeleton_texture_size = Vector2(skeleton->size * 2, 0); - } - } + if (!specular_map) { + state.current_specular = RID(); + ct->use_specular_cache = false; + glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 7); + glBindTexture(GL_TEXTURE_2D, storage->resources.white_tex); - bool use_skeleton = skeleton != NULL; - if (r_ris.prev_use_skeleton != use_skeleton) { - r_ris.rebind_shader = true; - state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_SKELETON, use_skeleton); - r_ris.prev_use_skeleton = use_skeleton; - } + } else { + specular_map = specular_map->get_ptr(); + + glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 7); + glBindTexture(GL_TEXTURE_2D, specular_map->tex_id); + state.current_specular = ct->specular; + ct->use_specular_cache = true; + texture->GLSetFilter(GL_TEXTURE_2D, filter); + texture->GLSetRepeat(GL_TEXTURE_2D, repeat); + } - if (skeleton) { - glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 3); - glBindTexture(GL_TEXTURE_2D, skeleton->tex_id); - state.using_skeleton = true; - } else { - state.using_skeleton = false; - } + if (ct->use_specular_cache) { + state.instance_data_array[r_index].flags |= FLAGS_DEFAULT_SPECULAR_MAP_USED; + } else { + state.instance_data_array[r_index].flags &= ~FLAGS_DEFAULT_SPECULAR_MAP_USED; } -#endif - Item *material_owner = p_ci->material_owner ? p_ci->material_owner : p_ci; + if (ct->use_normal_cache) { + state.instance_data_array[r_index].flags |= FLAGS_DEFAULT_NORMAL_MAP_USED; + } else { + state.instance_data_array[r_index].flags &= ~FLAGS_DEFAULT_NORMAL_MAP_USED; + } - RID material = material_owner->material; - RasterizerStorageGLES3::Material *material_ptr = storage->material_owner.get_or_null(material); + state.instance_data_array[r_index].specular_shininess = uint32_t(CLAMP(ct->specular_color.a * 255.0, 0, 255)) << 24; + state.instance_data_array[r_index].specular_shininess |= uint32_t(CLAMP(ct->specular_color.b * 255.0, 0, 255)) << 16; + state.instance_data_array[r_index].specular_shininess |= uint32_t(CLAMP(ct->specular_color.g * 255.0, 0, 255)) << 8; + state.instance_data_array[r_index].specular_shininess |= uint32_t(CLAMP(ct->specular_color.r * 255.0, 0, 255)); - if (material != r_ris.canvas_last_material || r_ris.rebind_shader) { - RasterizerStorageGLES3::Shader *shader_ptr = NULL; + r_texpixel_size.x = 1.0 / float(ct->size_cache.x); + r_texpixel_size.y = 1.0 / float(ct->size_cache.y); - if (material_ptr) { - shader_ptr = material_ptr->shader; + state.instance_data_array[r_index].color_texture_pixel_size[0] = r_texpixel_size.x; + state.instance_data_array[r_index].color_texture_pixel_size[1] = r_texpixel_size.y; - if (shader_ptr && shader_ptr->mode != RS::SHADER_CANVAS_ITEM) { - shader_ptr = NULL; // not a canvas item shader, don't use. - } - } + r_last_texture = p_texture; +} - if (shader_ptr) { - if (shader_ptr->canvas_item.uses_screen_texture) { - if (!state.canvas_texscreen_used) { - //copy if not copied before - _copy_texscreen(Rect2()); +void RasterizerCanvasGLES3::_set_uniforms() { +} - // blend mode will have been enabled so make sure we disable it again later on - //last_blend_mode = last_blend_mode != RasterizerStorageGLES3::Shader::CanvasItem::BLEND_MODE_DISABLED ? last_blend_mode : -1; - } +void RasterizerCanvasGLES3::reset_canvas() { + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glDisable(GL_SCISSOR_TEST); + glDisable(GL_DITHER); + glEnable(GL_BLEND); - if (storage->frame.current_rt->copy_screen_effect.color) { - glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 4); - glBindTexture(GL_TEXTURE_2D, storage->frame.current_rt->copy_screen_effect.color); - } + // Default to Mix. + glBlendEquation(GL_FUNC_ADD); + if (storage->frame.current_rt && storage->frame.current_rt->flags[RendererStorage::RENDER_TARGET_TRANSPARENT]) { + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + } else { + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE); + } + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); +} + +void RasterizerCanvasGLES3::canvas_debug_viewport_shadows(Light *p_lights_with_shadow) { +} + +void RasterizerCanvasGLES3::canvas_light_shadow_buffer_update(RID p_buffer, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders, CameraMatrix *p_xform_cache) { +} + +void RasterizerCanvasGLES3::draw_lens_distortion_rect(const Rect2 &p_rect, float p_k1, float p_k2, const Vector2 &p_eye_center, float p_oversample) { +} + +RendererCanvasRender::PolygonID RasterizerCanvasGLES3::request_polygon(const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, const Vector<int> &p_bones, const Vector<float> &p_weights) { + // We interleave the vertex data into one big VBO to improve cache coherence + uint32_t vertex_count = p_points.size(); + uint32_t stride = 2; + if ((uint32_t)p_colors.size() == vertex_count) { + stride += 4; + } + if ((uint32_t)p_uvs.size() == vertex_count) { + stride += 2; + } + if ((uint32_t)p_bones.size() == vertex_count * 4 && (uint32_t)p_weights.size() == vertex_count * 4) { + stride += 4; + } + + PolygonBuffers pb; + glGenBuffers(1, &pb.vertex_buffer); + glGenVertexArrays(1, &pb.vertex_array); + glBindVertexArray(pb.vertex_array); + pb.count = vertex_count; + pb.index_buffer = 0; + + uint32_t buffer_size = stride * p_points.size(); + + Vector<uint8_t> polygon_buffer; + polygon_buffer.resize(buffer_size * sizeof(float)); + { + glBindBuffer(GL_ARRAY_BUFFER, pb.vertex_buffer); + glBufferData(GL_ARRAY_BUFFER, stride * vertex_count * sizeof(float), nullptr, GL_STATIC_DRAW); // TODO may not be necessary + const uint8_t *r = polygon_buffer.ptr(); + float *fptr = (float *)r; + uint32_t *uptr = (uint32_t *)r; + uint32_t base_offset = 0; + { + // Always uses vertex positions + glEnableVertexAttribArray(RS::ARRAY_VERTEX); + glVertexAttribPointer(RS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, stride * sizeof(float), NULL); + const Vector2 *points_ptr = p_points.ptr(); + + for (uint32_t i = 0; i < vertex_count; i++) { + fptr[base_offset + i * stride + 0] = points_ptr[i].x; + fptr[base_offset + i * stride + 1] = points_ptr[i].y; } - if (shader_ptr != r_ris.shader_cache) { - if (shader_ptr->canvas_item.uses_time) { - RenderingServerDefault::redraw_request(); - } + base_offset += 2; + } - state.canvas_shader.set_custom_shader(shader_ptr->custom_code_id); - state.canvas_shader.bind(); + // Next add colors + if (p_colors.size() == 1) { + glDisableVertexAttribArray(RS::ARRAY_COLOR); + Color m = p_colors[0]; + glVertexAttrib4f(RS::ARRAY_COLOR, m.r, m.g, m.b, m.a); + } else if ((uint32_t)p_colors.size() == vertex_count) { + glEnableVertexAttribArray(RS::ARRAY_COLOR); + glVertexAttribPointer(RS::ARRAY_COLOR, 4, GL_FLOAT, GL_FALSE, stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(base_offset * sizeof(float))); + + const Color *color_ptr = p_colors.ptr(); + + for (uint32_t i = 0; i < vertex_count; i++) { + fptr[base_offset + i * stride + 0] = color_ptr[i].r; + fptr[base_offset + i * stride + 1] = color_ptr[i].g; + fptr[base_offset + i * stride + 2] = color_ptr[i].b; + fptr[base_offset + i * stride + 3] = color_ptr[i].a; } + base_offset += 4; + } else { + glDisableVertexAttribArray(RS::ARRAY_COLOR); + glVertexAttrib4f(RS::ARRAY_COLOR, 1.0, 1.0, 1.0, 1.0); + } - int tc = material_ptr->textures.size(); - Pair<StringName, RID> *textures = material_ptr->textures.ptrw(); - - ShaderLanguage::ShaderNode::Uniform::Hint *texture_hints = shader_ptr->texture_hints.ptrw(); - - for (int i = 0; i < tc; i++) { - glActiveTexture(GL_TEXTURE0 + i); - - RasterizerStorageGLES3::Texture *t = storage->texture_owner.get_or_null(textures[i].second); - - if (!t) { - switch (texture_hints[i]) { - case ShaderLanguage::ShaderNode::Uniform::HINT_BLACK_ALBEDO: - case ShaderLanguage::ShaderNode::Uniform::HINT_BLACK: { - glBindTexture(GL_TEXTURE_2D, storage->resources.black_tex); - } break; - case ShaderLanguage::ShaderNode::Uniform::HINT_ANISOTROPY: { - glBindTexture(GL_TEXTURE_2D, storage->resources.aniso_tex); - } break; - case ShaderLanguage::ShaderNode::Uniform::HINT_NORMAL: { - glBindTexture(GL_TEXTURE_2D, storage->resources.normal_tex); - } break; - default: { - glBindTexture(GL_TEXTURE_2D, storage->resources.white_tex); - } break; - } + if ((uint32_t)p_uvs.size() == vertex_count) { + glEnableVertexAttribArray(RS::ARRAY_TEX_UV); + glVertexAttribPointer(RS::ARRAY_TEX_UV, 2, GL_FLOAT, GL_FALSE, stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(base_offset * sizeof(float))); - continue; - } + const Vector2 *uv_ptr = p_uvs.ptr(); - if (t->redraw_if_visible) { - RenderingServerDefault::redraw_request(); - } + for (uint32_t i = 0; i < vertex_count; i++) { + fptr[base_offset + i * stride + 0] = uv_ptr[i].x; + fptr[base_offset + i * stride + 1] = uv_ptr[i].y; + } - t = t->get_ptr(); + base_offset += 2; + } else { + glDisableVertexAttribArray(RS::ARRAY_TEX_UV); + } -#ifdef TOOLS_ENABLED - if (t->detect_normal && texture_hints[i] == ShaderLanguage::ShaderNode::Uniform::HINT_NORMAL) { - t->detect_normal(t->detect_normal_ud); - } -#endif - if (t->render_target) - t->render_target->used_in_frame = true; + if ((uint32_t)p_indices.size() == vertex_count * 4 && (uint32_t)p_weights.size() == vertex_count * 4) { + glEnableVertexAttribArray(RS::ARRAY_BONES); + glVertexAttribPointer(RS::ARRAY_BONES, 4, GL_UNSIGNED_INT, GL_FALSE, stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(base_offset * sizeof(float))); - glBindTexture(t->target, t->tex_id); + const int *bone_ptr = p_bones.ptr(); + + for (uint32_t i = 0; i < vertex_count; i++) { + uint16_t *bone16w = (uint16_t *)&uptr[base_offset + i * stride]; + + bone16w[0] = bone_ptr[i * 4 + 0]; + bone16w[1] = bone_ptr[i * 4 + 1]; + bone16w[2] = bone_ptr[i * 4 + 2]; + bone16w[3] = bone_ptr[i * 4 + 3]; } + base_offset += 2; } else { - state.canvas_shader.set_custom_shader(0); - state.canvas_shader.bind(); + glDisableVertexAttribArray(RS::ARRAY_BONES); } - state.canvas_shader.use_material((void *)material_ptr); - r_ris.shader_cache = shader_ptr; + if ((uint32_t)p_weights.size() == vertex_count * 4) { + glEnableVertexAttribArray(RS::ARRAY_WEIGHTS); + glVertexAttribPointer(RS::ARRAY_WEIGHTS, 4, GL_FLOAT, GL_FALSE, stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(base_offset * sizeof(float))); - r_ris.canvas_last_material = material; + const float *weight_ptr = p_weights.ptr(); - r_ris.rebind_shader = false; - } + for (uint32_t i = 0; i < vertex_count; i++) { + uint16_t *weight16w = (uint16_t *)&uptr[base_offset + i * stride]; - int blend_mode = r_ris.shader_cache ? r_ris.shader_cache->canvas_item.blend_mode : RasterizerStorageGLES3::Shader::CanvasItem::BLEND_MODE_MIX; - bool unshaded = r_ris.shader_cache && (r_ris.shader_cache->canvas_item.light_mode == RasterizerStorageGLES3::Shader::CanvasItem::LIGHT_MODE_UNSHADED || (blend_mode != RasterizerStorageGLES3::Shader::CanvasItem::BLEND_MODE_MIX && blend_mode != RasterizerStorageGLES3::Shader::CanvasItem::BLEND_MODE_PMALPHA)); - bool reclip = false; + weight16w[0] = CLAMP(weight_ptr[i * 4 + 0] * 65535, 0, 65535); + weight16w[1] = CLAMP(weight_ptr[i * 4 + 1] * 65535, 0, 65535); + weight16w[2] = CLAMP(weight_ptr[i * 4 + 2] * 65535, 0, 65535); + weight16w[3] = CLAMP(weight_ptr[i * 4 + 3] * 65535, 0, 65535); + } - if (r_ris.last_blend_mode != blend_mode) { - switch (blend_mode) { - case RasterizerStorageGLES3::Shader::CanvasItem::BLEND_MODE_MIX: { - glBlendEquation(GL_FUNC_ADD); - if (storage->frame.current_rt && storage->frame.current_rt->flags[RendererStorage::RENDER_TARGET_TRANSPARENT]) { - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - } else { - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE); - } + base_offset += 2; + } else { + glDisableVertexAttribArray(RS::ARRAY_WEIGHTS); + } - } break; - case RasterizerStorageGLES3::Shader::CanvasItem::BLEND_MODE_ADD: { - glBlendEquation(GL_FUNC_ADD); - if (storage->frame.current_rt && storage->frame.current_rt->flags[RendererStorage::RENDER_TARGET_TRANSPARENT]) { - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, GL_SRC_ALPHA, GL_ONE); - } else { - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, GL_ZERO, GL_ONE); - } + ERR_FAIL_COND_V(base_offset != stride, 0); + glBufferData(GL_ARRAY_BUFFER, vertex_count * stride * sizeof(float), polygon_buffer.ptr(), GL_STATIC_DRAW); + } - } break; - case RasterizerStorageGLES3::Shader::CanvasItem::BLEND_MODE_SUB: { - glBlendEquation(GL_FUNC_REVERSE_SUBTRACT); - if (storage->frame.current_rt && storage->frame.current_rt->flags[RendererStorage::RENDER_TARGET_TRANSPARENT]) { - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, GL_SRC_ALPHA, GL_ONE); - } else { - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, GL_ZERO, GL_ONE); - } - } break; - case RasterizerStorageGLES3::Shader::CanvasItem::BLEND_MODE_MUL: { - glBlendEquation(GL_FUNC_ADD); - if (storage->frame.current_rt && storage->frame.current_rt->flags[RendererStorage::RENDER_TARGET_TRANSPARENT]) { - glBlendFuncSeparate(GL_DST_COLOR, GL_ZERO, GL_DST_ALPHA, GL_ZERO); - } else { - glBlendFuncSeparate(GL_DST_COLOR, GL_ZERO, GL_ZERO, GL_ONE); - } - } break; - case RasterizerStorageGLES3::Shader::CanvasItem::BLEND_MODE_PMALPHA: { - glBlendEquation(GL_FUNC_ADD); - if (storage->frame.current_rt && storage->frame.current_rt->flags[RendererStorage::RENDER_TARGET_TRANSPARENT]) { - glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - } else { - glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE); - } - } break; + if (p_indices.size()) { + //create indices, as indices were requested + Vector<uint8_t> index_buffer; + index_buffer.resize(p_indices.size() * sizeof(int32_t)); + { + uint8_t *w = index_buffer.ptrw(); + memcpy(w, p_indices.ptr(), sizeof(int32_t) * p_indices.size()); } + glGenBuffers(1, &pb.index_buffer); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, pb.index_buffer); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, p_indices.size() * 4, nullptr, GL_STATIC_DRAW); // TODO may not be necessary + glBufferData(GL_ELEMENT_ARRAY_BUFFER, p_indices.size() * 4, index_buffer.ptr(), GL_STATIC_DRAW); + pb.count = p_indices.size(); } - state.uniforms.final_modulate = unshaded ? p_ci->final_modulate : Color(p_ci->final_modulate.r * r_ris.item_group_modulate.r, p_ci->final_modulate.g * r_ris.item_group_modulate.g, p_ci->final_modulate.b * r_ris.item_group_modulate.b, p_ci->final_modulate.a * r_ris.item_group_modulate.a); + glBindVertexArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - state.uniforms.modelview_matrix = p_ci->final_transform; - state.uniforms.extra_matrix = Transform2D(); + PolygonID id = polygon_buffers.last_id++; - _set_uniforms(); + polygon_buffers.polygons[id] = pb; - if (unshaded || (state.uniforms.final_modulate.a > 0.001 && (!r_ris.shader_cache || r_ris.shader_cache->canvas_item.light_mode != RasterizerStorageGLES3::Shader::CanvasItem::LIGHT_MODE_LIGHT_ONLY) && !p_ci->light_masked)) - _legacy_canvas_item_render_commands(p_ci, NULL, reclip, material_ptr); + return id; +} +void RasterizerCanvasGLES3::free_polygon(PolygonID p_polygon) { + PolygonBuffers *pb_ptr = polygon_buffers.polygons.getptr(p_polygon); + ERR_FAIL_COND(!pb_ptr); - r_ris.rebind_shader = true; // hacked in for now. + PolygonBuffers &pb = *pb_ptr; - if ((blend_mode == RasterizerStorageGLES3::Shader::CanvasItem::BLEND_MODE_MIX || blend_mode == RasterizerStorageGLES3::Shader::CanvasItem::BLEND_MODE_PMALPHA) && r_ris.item_group_light && !unshaded) { - Light *light = r_ris.item_group_light; - bool light_used = false; - RS::CanvasLightBlendMode bmode = RS::CANVAS_LIGHT_BLEND_MODE_ADD; - state.uniforms.final_modulate = p_ci->final_modulate; // remove the canvas modulate + if (pb.index_buffer != 0) { + glDeleteBuffers(1, &pb.index_buffer); + } - while (light) { - if (p_ci->light_mask & light->item_mask && r_ris.item_group_z >= light->z_min && r_ris.item_group_z <= light->z_max && p_ci->global_rect_cache.intersects_transformed(light->xform_cache, light->rect_cache)) { - //intersects this light + glDeleteVertexArrays(1, &pb.vertex_array); + glDeleteBuffers(1, &pb.vertex_buffer); - if (!light_used || bmode != light->blend_mode) { - bmode = light->blend_mode; + polygon_buffers.polygons.erase(p_polygon); +} - switch (bmode) { - case RS::CANVAS_LIGHT_BLEND_MODE_ADD: { - glBlendEquation(GL_FUNC_ADD); - glBlendFunc(GL_SRC_ALPHA, GL_ONE); +// Creates a new uniform buffer and uses it right away +// This expands the instance buffer continually +// In theory allocations can reach as high as number_of_draw_calls * 3 frames +// because OpenGL can start rendering subsequent frames before finishing the current one +void RasterizerCanvasGLES3::_allocate_instance_data_buffer() { + GLuint new_buffer; + glGenBuffers(1, &new_buffer); + glBindBuffer(GL_UNIFORM_BUFFER, new_buffer); + glBufferData(GL_UNIFORM_BUFFER, sizeof(InstanceData) * state.max_instances_per_batch, nullptr, GL_DYNAMIC_DRAW); + state.current_buffer = (state.current_buffer + 1); + state.canvas_instance_data_buffers.insert(state.current_buffer, new_buffer); + state.fences.insert(state.current_buffer, GLsync()); + state.current_buffer = state.current_buffer % state.canvas_instance_data_buffers.size(); + glBindBuffer(GL_UNIFORM_BUFFER, 0); +} - } break; - case RS::CANVAS_LIGHT_BLEND_MODE_SUB: { - glBlendEquation(GL_FUNC_REVERSE_SUBTRACT); - glBlendFunc(GL_SRC_ALPHA, GL_ONE); - } break; - case RS::CANVAS_LIGHT_BLEND_MODE_MIX: { - // case RS::CANVAS_LIGHT_MODE_MASK: { - glBlendEquation(GL_FUNC_ADD); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +void RasterizerCanvasGLES3::initialize() { + // quad buffer + { + glGenBuffers(1, &data.canvas_quad_vertices); + glBindBuffer(GL_ARRAY_BUFFER, data.canvas_quad_vertices); - } break; - } - } + const float qv[8] = { + 0, 0, + 0, 1, + 1, 1, + 1, 0 + }; - if (!light_used) { - state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_LIGHTING, true); - light_used = true; - } + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 8, qv, GL_STATIC_DRAW); - // FTODO - //bool has_shadow = light->shadow_buffer.is_valid() && p_ci->light_mask & light->item_shadow_mask; - bool has_shadow = light->use_shadow && p_ci->light_mask & light->item_shadow_mask; - - state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_SHADOWS, has_shadow); - if (has_shadow) { - // FTODO - //state.canvas_shader.set_conditional(CanvasShaderGLES3::SHADOW_USE_GRADIENT, light->shadow_gradient_length > 0); - state.canvas_shader.set_conditional(CanvasShaderGLES3::SHADOW_USE_GRADIENT, false); - state.canvas_shader.set_conditional(CanvasShaderGLES3::SHADOW_FILTER_NEAREST, light->shadow_filter == RS::CANVAS_LIGHT_FILTER_NONE); - //state.canvas_shader.set_conditional(CanvasShaderGLES3::SHADOW_FILTER_PCF3, light->shadow_filter == RS::CANVAS_LIGHT_FILTER_PCF3); - state.canvas_shader.set_conditional(CanvasShaderGLES3::SHADOW_FILTER_PCF3, false); - state.canvas_shader.set_conditional(CanvasShaderGLES3::SHADOW_FILTER_PCF5, light->shadow_filter == RS::CANVAS_LIGHT_FILTER_PCF5); - state.canvas_shader.set_conditional(CanvasShaderGLES3::SHADOW_FILTER_PCF7, false); - //state.canvas_shader.set_conditional(CanvasShaderGLES3::SHADOW_FILTER_PCF7, light->shadow_filter == RS::CANVAS_LIGHT_FILTER_PCF7); - //state.canvas_shader.set_conditional(CanvasShaderGLES3::SHADOW_FILTER_PCF9, light->shadow_filter == RS::CANVAS_LIGHT_FILTER_PCF9); - state.canvas_shader.set_conditional(CanvasShaderGLES3::SHADOW_FILTER_PCF9, false); - state.canvas_shader.set_conditional(CanvasShaderGLES3::SHADOW_FILTER_PCF13, light->shadow_filter == RS::CANVAS_LIGHT_FILTER_PCF13); - } + glBindBuffer(GL_ARRAY_BUFFER, 0); - state.canvas_shader.bind(); - state.using_light = light; - state.using_shadow = has_shadow; + glGenVertexArrays(1, &data.canvas_quad_array); + glBindVertexArray(data.canvas_quad_array); + glBindBuffer(GL_ARRAY_BUFFER, data.canvas_quad_vertices); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, nullptr); + glEnableVertexAttribArray(0); + glBindVertexArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind + } - //always re-set uniforms, since light parameters changed - _set_uniforms(); - state.canvas_shader.use_material((void *)material_ptr); + { + //particle quad buffers + + glGenBuffers(1, &data.particle_quad_vertices); + glBindBuffer(GL_ARRAY_BUFFER, data.particle_quad_vertices); + { + //quad of size 1, with pivot on the center for particles, then regular UVS. Color is general plus fetched from particle + const float qv[16] = { + -0.5, -0.5, + 0.0, 0.0, + -0.5, 0.5, + 0.0, 1.0, + 0.5, 0.5, + 1.0, 1.0, + 0.5, -0.5, + 1.0, 0.0 + }; + + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 16, qv, GL_STATIC_DRAW); + } - glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 6); - RasterizerStorageGLES3::Texture *t = storage->texture_owner.get_or_null(light->texture); - if (!t) { - glBindTexture(GL_TEXTURE_2D, storage->resources.white_tex); - } else { - t = t->get_ptr(); + glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind + + glGenVertexArrays(1, &data.particle_quad_array); + glBindVertexArray(data.particle_quad_array); + glBindBuffer(GL_ARRAY_BUFFER, data.particle_quad_vertices); + glEnableVertexAttribArray(RS::ARRAY_VERTEX); + glVertexAttribPointer(RS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, nullptr); + glEnableVertexAttribArray(RS::ARRAY_TEX_UV); + glVertexAttribPointer(RS::ARRAY_TEX_UV, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, CAST_INT_TO_UCHAR_PTR(8)); + glBindVertexArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind + } - glBindTexture(t->target, t->tex_id); - } + // ninepatch buffers + { + // array buffer + glGenBuffers(1, &data.ninepatch_vertices); + glBindBuffer(GL_ARRAY_BUFFER, data.ninepatch_vertices); - glActiveTexture(GL_TEXTURE0); - _legacy_canvas_item_render_commands(p_ci, NULL, reclip, material_ptr); //redraw using light + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * (16 + 16) * 2, NULL, GL_DYNAMIC_DRAW); - state.using_light = NULL; - } + glBindBuffer(GL_ARRAY_BUFFER, 0); - light = light->next_ptr; - } + // element buffer + glGenBuffers(1, &data.ninepatch_elements); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.ninepatch_elements); - if (light_used) { - state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_LIGHTING, false); - state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_SHADOWS, false); - state.canvas_shader.set_conditional(CanvasShaderGLES3::SHADOW_FILTER_NEAREST, false); - state.canvas_shader.set_conditional(CanvasShaderGLES3::SHADOW_FILTER_PCF3, false); - state.canvas_shader.set_conditional(CanvasShaderGLES3::SHADOW_FILTER_PCF5, false); - state.canvas_shader.set_conditional(CanvasShaderGLES3::SHADOW_FILTER_PCF7, false); - state.canvas_shader.set_conditional(CanvasShaderGLES3::SHADOW_FILTER_PCF9, false); - state.canvas_shader.set_conditional(CanvasShaderGLES3::SHADOW_FILTER_PCF13, false); +#define _EIDX(y, x) (y * 4 + x) + uint8_t elems[3 * 2 * 9] = { + // first row - state.canvas_shader.bind(); + _EIDX(0, 0), _EIDX(0, 1), _EIDX(1, 1), + _EIDX(1, 1), _EIDX(1, 0), _EIDX(0, 0), - r_ris.last_blend_mode = -1; + _EIDX(0, 1), _EIDX(0, 2), _EIDX(1, 2), + _EIDX(1, 2), _EIDX(1, 1), _EIDX(0, 1), -#if 0 - //this is set again, so it should not be needed anyway? - state.canvas_item_modulate = unshaded ? ci->final_modulate : Color(ci->final_modulate.r * p_modulate.r, ci->final_modulate.g * p_modulate.g, ci->final_modulate.b * p_modulate.b, ci->final_modulate.a * p_modulate.a); + _EIDX(0, 2), _EIDX(0, 3), _EIDX(1, 3), + _EIDX(1, 3), _EIDX(1, 2), _EIDX(0, 2), - state.canvas_shader.set_uniform(CanvasShaderGLES3::MODELVIEW_MATRIX, state.final_transform); - state.canvas_shader.set_uniform(CanvasShaderGLES3::EXTRA_MATRIX, Transform2D()); - state.canvas_shader.set_uniform(CanvasShaderGLES3::FINAL_MODULATE, state.canvas_item_modulate); + // second row - glBlendEquation(GL_FUNC_ADD); + _EIDX(1, 0), _EIDX(1, 1), _EIDX(2, 1), + _EIDX(2, 1), _EIDX(2, 0), _EIDX(1, 0), - if (storage->frame.current_rt->flags[RendererStorage::RENDER_TARGET_TRANSPARENT]) { - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - } else { - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - } + // the center one would be here, but we'll put it at the end + // so it's easier to disable the center and be able to use + // one draw call for both - //@TODO RESET canvas_blend_mode -#endif - } + _EIDX(1, 2), _EIDX(1, 3), _EIDX(2, 3), + _EIDX(2, 3), _EIDX(2, 2), _EIDX(1, 2), + + // third row + + _EIDX(2, 0), _EIDX(2, 1), _EIDX(3, 1), + _EIDX(3, 1), _EIDX(3, 0), _EIDX(2, 0), + + _EIDX(2, 1), _EIDX(2, 2), _EIDX(3, 2), + _EIDX(3, 2), _EIDX(3, 1), _EIDX(2, 1), + + _EIDX(2, 2), _EIDX(2, 3), _EIDX(3, 3), + _EIDX(3, 3), _EIDX(3, 2), _EIDX(2, 2), + + // center field + + _EIDX(1, 1), _EIDX(1, 2), _EIDX(2, 2), + _EIDX(2, 2), _EIDX(2, 1), _EIDX(1, 1) + }; +#undef _EIDX + + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elems), elems, GL_STATIC_DRAW); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } - if (reclip) { - glEnable(GL_SCISSOR_TEST); - int y = storage->frame.current_rt->height - (r_ris.current_clip->final_clip_rect.position.y + r_ris.current_clip->final_clip_rect.size.y); - // FTODO - // if (storage->frame.current_rt->flags[RendererStorage::RENDER_TARGET_VFLIP]) - // y = r_ris.current_clip->final_clip_rect.position.y; - glScissor(r_ris.current_clip->final_clip_rect.position.x, y, r_ris.current_clip->final_clip_rect.size.width, r_ris.current_clip->final_clip_rect.size.height); + //state.canvas_shadow_shader.init(); + + int uniform_max_size; + glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &uniform_max_size); + if (uniform_max_size < 65536) { + state.max_lights_per_render = 64; + state.max_instances_per_batch = 128; + } else { + state.max_lights_per_render = 256; + state.max_instances_per_batch = 512; } -} -void RasterizerCanvasGLES3::gl_enable_scissor(int p_x, int p_y, int p_width, int p_height) const { - glEnable(GL_SCISSOR_TEST); - glScissor(p_x, p_y, p_width, p_height); -} + // Reserve 64 Uniform Buffers for instance data + state.canvas_instance_data_buffers.resize(64); + state.fences.resize(64); + glGenBuffers(64, state.canvas_instance_data_buffers.ptr()); + for (int i = 0; i < 64; i++) { + glBindBuffer(GL_UNIFORM_BUFFER, state.canvas_instance_data_buffers[i]); + glBufferData(GL_UNIFORM_BUFFER, sizeof(InstanceData) * state.max_instances_per_batch, nullptr, GL_DYNAMIC_DRAW); + } + glBindBuffer(GL_UNIFORM_BUFFER, 0); -void RasterizerCanvasGLES3::gl_disable_scissor() const { - glDisable(GL_SCISSOR_TEST); -} + state.instance_data_array = memnew_arr(InstanceData, state.max_instances_per_batch); -void RasterizerCanvasGLES3::initialize() { - RasterizerCanvasBaseGLES3::initialize(); + glGenBuffers(1, &state.canvas_state_buffer); + glBindBuffer(GL_UNIFORM_BUFFER, state.canvas_state_buffer); + glBufferData(GL_UNIFORM_BUFFER, sizeof(StateBuffer), nullptr, GL_STREAM_DRAW); + glBindBuffer(GL_UNIFORM_BUFFER, 0); - batch_initialize(); + String global_defines; + global_defines += "#define MAX_GLOBAL_VARIABLES 256\n"; // TODO: this is arbitrary for now + global_defines += "#define MAX_LIGHTS " + itos(state.max_instances_per_batch) + "\n"; + global_defines += "#define MAX_DRAW_DATA_INSTANCES " + itos(state.max_instances_per_batch) + "\n"; - // just reserve some space (may not be needed as we are orphaning, but hey ho) - glGenBuffers(1, &bdata.gl_vertex_buffer); + state.canvas_shader.initialize(global_defines); + state.canvas_shader_default_version = state.canvas_shader.version_create(); + state.canvas_shader.version_bind_shader(state.canvas_shader_default_version, CanvasShaderGLES3::MODE_QUAD); - if (bdata.vertex_buffer_size_bytes) { - glBindBuffer(GL_ARRAY_BUFFER, bdata.gl_vertex_buffer); - glBufferData(GL_ARRAY_BUFFER, bdata.vertex_buffer_size_bytes, NULL, GL_DYNAMIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, 0); + //state.canvas_shader.set_conditional(CanvasOldShaderGLES3::USE_RGBA_SHADOWS, storage->config.use_rgba_2d_shadows); - // pre fill index buffer, the indices never need to change so can be static - glGenBuffers(1, &bdata.gl_index_buffer); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bdata.gl_index_buffer); - - Vector<uint16_t> indices; - indices.resize(bdata.index_buffer_size_units); - - for (unsigned int q = 0; q < bdata.max_quads; q++) { - int i_pos = q * 6; // 6 inds per quad - int q_pos = q * 4; // 4 verts per quad - indices.set(i_pos, q_pos); - indices.set(i_pos + 1, q_pos + 1); - indices.set(i_pos + 2, q_pos + 2); - indices.set(i_pos + 3, q_pos); - indices.set(i_pos + 4, q_pos + 2); - indices.set(i_pos + 5, q_pos + 3); - - // we can only use 16 bit indices in OpenGL! -#ifdef DEBUG_ENABLED - CRASH_COND((q_pos + 3) > 65535); -#endif - } + //state.canvas_shader.bind(); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, bdata.index_buffer_size_bytes, &indices[0], GL_STATIC_DRAW); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + //state.lens_shader.init(); + + //state.canvas_shader.set_conditional(CanvasOldShaderGLES3::USE_PIXEL_SNAP, GLOBAL_DEF("rendering/quality/2d/use_pixel_snap", false)); - } // only if there is a vertex buffer (batching is on) + { + default_canvas_group_shader = storage->shader_allocate(); + storage->shader_initialize(default_canvas_group_shader); + + storage->shader_set_code(default_canvas_group_shader, R"( +// Default CanvasGroup shader. + +shader_type canvas_item; + +void fragment() { + vec4 c = textureLod(SCREEN_TEXTURE, SCREEN_UV, 0.0); + + if (c.a > 0.0001) { + c.rgb /= c.a; + } + + COLOR *= c; +} +)"); + default_canvas_group_material = storage->material_allocate(); + storage->material_initialize(default_canvas_group_material); + + storage->material_set_shader(default_canvas_group_material, default_canvas_group_shader); + } + + default_canvas_texture = storage->canvas_texture_allocate(); + storage->canvas_texture_initialize(default_canvas_texture); + + state.using_light = NULL; + state.using_transparent_rt = false; + state.using_skeleton = false; + state.current_shader_version = state.canvas_shader_default_version; } RasterizerCanvasGLES3::RasterizerCanvasGLES3() { - batch_constructor(); +} +RasterizerCanvasGLES3::~RasterizerCanvasGLES3() { + state.canvas_shader.version_free(state.canvas_shader_default_version); + storage->free(default_canvas_group_material); + storage->free(default_canvas_group_shader); + storage->free(default_canvas_texture); +} + +void RasterizerCanvasGLES3::finalize() { + glDeleteBuffers(1, &data.canvas_quad_vertices); + glDeleteVertexArrays(1, &data.canvas_quad_array); + + glDeleteBuffers(1, &data.canvas_quad_vertices); + glDeleteVertexArrays(1, &data.canvas_quad_array); } -#endif // GLES3_BACKEND_ENABLED +#endif // GLES3_ENABLED diff --git a/drivers/gles3/rasterizer_canvas_gles3.h b/drivers/gles3/rasterizer_canvas_gles3.h index 5e85f84bde..908d79f9f8 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.h +++ b/drivers/gles3/rasterizer_canvas_gles3.h @@ -31,41 +31,251 @@ #ifndef RASTERIZER_CANVAS_OPENGL_H #define RASTERIZER_CANVAS_OPENGL_H -#include "drivers/gles3/rasterizer_platforms.h" -#ifdef GLES3_BACKEND_ENABLED +#ifdef GLES3_ENABLED -#include "drivers/gles3/rasterizer_canvas_batcher.h" -#include "rasterizer_canvas_base_gles3.h" +#include "rasterizer_scene_gles3.h" +#include "rasterizer_storage_gles3.h" +#include "servers/rendering/renderer_canvas_render.h" +#include "servers/rendering/renderer_compositor.h" + +#include "shaders/canvas.glsl.gen.h" class RasterizerSceneGLES3; -class RasterizerCanvasGLES3 : public RasterizerCanvasBaseGLES3, public RasterizerCanvasBatcher<RasterizerCanvasGLES3, RasterizerStorageGLES3> { - friend class RasterizerCanvasBatcher<RasterizerCanvasGLES3, RasterizerStorageGLES3>; +class RasterizerCanvasGLES3 : public RendererCanvasRender { + _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 { + BASE_UNIFORM_BUFFER_OBJECT = 0, + MATERIAL_UNIFORM_BUFFER_OBJECT = 1, + TRANSFORMS_UNIFORM_BUFFER_OBJECT = 2, + CANVAS_TEXTURE_UNIFORM_BUFFER_OBJECT = 3, + }; + + enum { + + FLAGS_INSTANCING_MASK = 0x7F, + FLAGS_INSTANCING_HAS_COLORS = (1 << 7), + FLAGS_INSTANCING_HAS_CUSTOM_DATA = (1 << 8), + + FLAGS_CLIP_RECT_UV = (1 << 9), + FLAGS_TRANSPOSE_RECT = (1 << 10), -private: - // legacy codepath .. to remove after testing - void _legacy_canvas_render_item(Item *p_ci, RenderItemState &r_ris); + FLAGS_NINEPACH_DRAW_CENTER = (1 << 12), + FLAGS_USING_PARTICLES = (1 << 13), - // high level batch funcs - void canvas_render_items_implementation(Item *p_item_list, int p_z, const Color &p_modulate, Light *p_light, const Transform2D &p_base_transform); - void render_batches(Item::Command *const *p_commands, Item *p_current_clip, bool &r_reclip, RasterizerStorageGLES3::Material *p_material); + FLAGS_USE_SKELETON = (1 << 15), + FLAGS_NINEPATCH_H_MODE_SHIFT = 16, + FLAGS_NINEPATCH_V_MODE_SHIFT = 18, + FLAGS_LIGHT_COUNT_SHIFT = 20, - // funcs used from rasterizer_canvas_batcher template - void gl_enable_scissor(int p_x, int p_y, int p_width, int p_height) const; - void gl_disable_scissor() const; + FLAGS_DEFAULT_NORMAL_MAP_USED = (1 << 26), + FLAGS_DEFAULT_SPECULAR_MAP_USED = (1 << 27), + + FLAGS_USE_MSDF = (1 << 28), + }; + + 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, + }; public: - void canvas_render_items_begin(const Color &p_modulate, Light *p_light, const Transform2D &p_base_transform); - void canvas_render_items_end(); - void canvas_render_items_internal(Item *p_item_list, int p_z, const Color &p_modulate, Light *p_light, const Transform2D &p_base_transform); - void canvas_begin() override; - void canvas_end() override; + 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; + }; + + 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 specular_shininess; + uint32_t lights[4]; + }; + + struct Data { + GLuint canvas_quad_vertices; + GLuint canvas_quad_array; + + GLuint particle_quad_vertices; + GLuint particle_quad_array; + + GLuint ninepatch_vertices; + GLuint ninepatch_elements; + } data; + + struct State { + GLuint canvas_state_buffer; + LocalVector<GLuint> canvas_instance_data_buffers; + LocalVector<GLsync> fences; + uint32_t current_buffer = 0; + + InstanceData *instance_data_array; + bool canvas_texscreen_used; + CanvasShaderGLES3 canvas_shader; + RID canvas_shader_current_version; + RID canvas_shader_default_version; + //CanvasShadowShaderGLES3 canvas_shadow_shader; + //LensDistortedShaderGLES3 lens_shader; + + bool using_texture_rect; + + bool using_ninepatch; + bool using_skeleton; + + Transform2D skeleton_transform; + Transform2D skeleton_transform_inverse; + Size2i skeleton_texture_size; + + RID current_tex = RID(); + RID current_normal = RID(); + RID current_specular = RID(); + RasterizerStorageGLES3::Texture *current_tex_ptr; + RID current_shader_version = RID(); + RS::PrimitiveType current_primitive = RS::PRIMITIVE_MAX; + uint32_t current_primitive_points = 0; + Item::Command::Type current_command = Item::Command::TYPE_RECT; + + bool end_batch = false; + + Transform3D vp; + Light *using_light; + bool using_shadow; + bool using_transparent_rt; + + // FROM RD Renderer + + uint32_t max_lights_per_render; + uint32_t max_lights_per_item; + uint32_t max_instances_per_batch; + + 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; + + typedef void Texture; + + RasterizerSceneGLES3 *scene_render; + + RasterizerStorageGLES3 *storage; + + void _set_uniforms(); + + void canvas_begin(); + void canvas_end(); + + //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); + + virtual void reset_canvas(); + virtual void canvas_light_shadow_buffer_update(RID p_buffer, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders, CameraMatrix *p_xform_cache); + + virtual void canvas_debug_viewport_shadows(Light *p_lights_with_shadow) override; + + 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) 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, uint32_t &r_index, RID &r_last_texture, Size2 &r_texpixel_size); + + struct PolygonBuffers { + GLuint vertex_buffer; + GLuint vertex_array; + GLuint index_buffer; + int count; + }; + + struct { + HashMap<PolygonID, PolygonBuffers> polygons; + PolygonID last_id; + } 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>()) override; + void free_polygon(PolygonID p_polygon) override; 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) override; + void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool p_to_backbuffer = false); + void _render_item(RID p_render_target, const Item *p_item, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, Light *p_lights, uint32_t &r_index); + void _render_batch(uint32_t &p_max_index); + void _end_batch(uint32_t &p_max_index); + void _allocate_instance_data_buffer(); void initialize(); + void finalize(); RasterizerCanvasGLES3(); + ~RasterizerCanvasGLES3(); }; -#endif // GLES3_BACKEND_ENABLED +#endif // GLES3_ENABLED #endif // RASTERIZER_CANVAS_OPENGL_H diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp index 0840d03e44..32ead8aa7e 100644 --- a/drivers/gles3/rasterizer_gles3.cpp +++ b/drivers/gles3/rasterizer_gles3.cpp @@ -30,8 +30,7 @@ #include "rasterizer_gles3.h" -#ifdef GLES3_BACKEND_ENABLED -#include "shader_gles3.h" +#ifdef GLES3_ENABLED #include "core/config/project_settings.h" #include "core/os/os.h" @@ -91,21 +90,12 @@ void RasterizerGLES3::begin_frame(double frame_step) { frame++; delta = frame_step; - // from 3.2 - time_total += frame_step * time_scale; - - if (frame_step == 0) { - //to avoid hiccups - frame_step = 0.001; - } + time_total += frame_step; double time_roll_over = GLOBAL_GET("rendering/limits/time/time_rollover_secs"); time_total = Math::fmod(time_total, time_roll_over); - storage.frame.time[0] = time_total; - storage.frame.time[1] = Math::fmod(time_total, 3600); - storage.frame.time[2] = Math::fmod(time_total, 900); - storage.frame.time[3] = Math::fmod(time_total, 60); + storage.frame.time = time_total; storage.frame.count++; storage.frame.delta = frame_step; @@ -131,10 +121,11 @@ void RasterizerGLES3::end_frame(bool p_swap_buffers) { // glClearColor(1, 0, 0, 1); // glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_ACCUM_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - if (p_swap_buffers) + if (p_swap_buffers) { DisplayServer::get_singleton()->swap_buffers(); - else + } else { glFinish(); + } } #ifdef CAN_DEBUG @@ -272,32 +263,22 @@ RasterizerGLES3::RasterizerGLES3() { void RasterizerGLES3::prepare_for_blitting_render_targets() { } -void RasterizerGLES3::_blit_render_target_to_screen(RID p_render_target, const Rect2 &p_screen_rect) { +void RasterizerGLES3::_blit_render_target_to_screen(RID p_render_target, DisplayServer::WindowID p_screen, const Rect2 &p_screen_rect) { ERR_FAIL_COND(storage.frame.current_rt); - // print_line("_blit_render_target_to_screen " + itos (p_screen) + ", rect " + String(Variant(p_screen_rect))); - RasterizerStorageGLES3::RenderTarget *rt = storage.render_target_owner.get_or_null(p_render_target); ERR_FAIL_COND(!rt); - canvas._set_texture_rect_mode(true); - canvas.state.canvas_shader.set_custom_shader(0); - canvas.state.canvas_shader.bind(); - - canvas.canvas_begin(); + // TODO: do we need a keep 3d linear option? - glDisable(GL_BLEND); - storage.bind_framebuffer_system(); - glActiveTexture(GL_TEXTURE0 + storage.config.max_texture_image_units - 1); if (rt->external.fbo != 0) { - glBindTexture(GL_TEXTURE_2D, rt->external.color); + glBindFramebuffer(GL_READ_FRAMEBUFFER, rt->external.fbo); } else { - glBindTexture(GL_TEXTURE_2D, rt->color); + glBindFramebuffer(GL_READ_FRAMEBUFFER, rt->fbo); } - canvas.draw_generic_textured_rect(p_screen_rect, Rect2(0, 0, 1, -1)); - glBindTexture(GL_TEXTURE_2D, 0); - - canvas.canvas_end(); + glReadBuffer(GL_COLOR_ATTACHMENT0); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, RasterizerStorageGLES3::system_fbo); + glBlitFramebuffer(0, 0, rt->width, rt->height, 0, p_screen_rect.size.y, p_screen_rect.size.x, 0, GL_COLOR_BUFFER_BIT, GL_NEAREST); } // is this p_screen useless in a multi window environment? @@ -313,7 +294,7 @@ void RasterizerGLES3::blit_render_targets_to_screen(DisplayServer::WindowID p_sc RID rid_rt = blit.render_target; Rect2 dst_rect = blit.dst_rect; - _blit_render_target_to_screen(rid_rt, dst_rect); + _blit_render_target_to_screen(rid_rt, p_screen, dst_rect); } } @@ -321,11 +302,10 @@ void RasterizerGLES3::set_boot_image(const Ref<Image> &p_image, const Color &p_c if (p_image.is_null() || p_image->is_empty()) return; - int window_w = 640; //OS::get_singleton()->get_video_mode(0).width; - int window_h = 480; //OS::get_singleton()->get_video_mode(0).height; + Size2i win_size = DisplayServer::get_singleton()->screen_get_size(); glBindFramebuffer(GL_FRAMEBUFFER, 0); - glViewport(0, 0, window_w, window_h); + glViewport(0, 0, win_size.width, win_size.height); glDisable(GL_BLEND); glDepthMask(GL_FALSE); if (false) { @@ -346,27 +326,26 @@ void RasterizerGLES3::set_boot_image(const Ref<Image> &p_image, const Color &p_c Rect2 imgrect(0, 0, p_image->get_width(), p_image->get_height()); Rect2 screenrect; if (p_scale) { - if (window_w > window_h) { + if (win_size.width > win_size.height) { //scale horizontally - screenrect.size.y = window_h; - screenrect.size.x = imgrect.size.x * window_h / imgrect.size.y; - screenrect.position.x = (window_w - screenrect.size.x) / 2; + 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 = window_w; - screenrect.size.y = imgrect.size.y * window_w / imgrect.size.x; - screenrect.position.y = (window_h - screenrect.size.y) / 2; + 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(window_w, window_h) - screenrect.size) / 2.0).floor(); + screenrect.position += ((Size2(win_size.width, win_size.height) - screenrect.size) / 2.0).floor(); } RasterizerStorageGLES3::Texture *t = storage.texture_owner.get_or_null(texture); glActiveTexture(GL_TEXTURE0 + storage.config.max_texture_image_units - 1); glBindTexture(GL_TEXTURE_2D, t->tex_id); - canvas.draw_generic_textured_rect(screenrect, Rect2(0, 0, 1, 1)); glBindTexture(GL_TEXTURE_2D, 0); canvas.canvas_end(); @@ -375,4 +354,4 @@ void RasterizerGLES3::set_boot_image(const Ref<Image> &p_image, const Color &p_c end_frame(true); } -#endif // GLES3_BACKEND_ENABLED +#endif // GLES3_ENABLED diff --git a/drivers/gles3/rasterizer_gles3.h b/drivers/gles3/rasterizer_gles3.h index e2f3e0bdd0..a641e189c5 100644 --- a/drivers/gles3/rasterizer_gles3.h +++ b/drivers/gles3/rasterizer_gles3.h @@ -31,8 +31,7 @@ #ifndef RASTERIZER_OPENGL_H #define RASTERIZER_OPENGL_H -#include "drivers/gles3/rasterizer_platforms.h" -#ifdef GLES3_BACKEND_ENABLED +#ifdef GLES3_ENABLED #include "rasterizer_canvas_gles3.h" #include "rasterizer_scene_gles3.h" @@ -45,14 +44,13 @@ private: float delta = 0; double time_total = 0.0; - double time_scale = 1.0; protected: - RasterizerCanvasGLES3 canvas; RasterizerStorageGLES3 storage; + RasterizerCanvasGLES3 canvas; RasterizerSceneGLES3 scene; - void _blit_render_target_to_screen(RID p_render_target, const Rect2 &p_screen_rect); + void _blit_render_target_to_screen(RID p_render_target, DisplayServer::WindowID p_screen, const Rect2 &p_screen_rect); public: RendererStorage *get_storage() { return &storage; } @@ -87,6 +85,6 @@ public: ~RasterizerGLES3() {} }; -#endif // GLES3_BACKEND_ENABLED +#endif // GLES3_ENABLED #endif diff --git a/drivers/gles3/rasterizer_platforms.h b/drivers/gles3/rasterizer_platforms.h deleted file mode 100644 index 97a205e90d..0000000000 --- a/drivers/gles3/rasterizer_platforms.h +++ /dev/null @@ -1,48 +0,0 @@ -/*************************************************************************/ -/* rasterizer_platforms.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef RASTERIZER_PLATFORMS_H -#define RASTERIZER_PLATFORMS_H - -///////////////////////////////////////////////////// -// override for intellisense .. ONLY FOR DEVELOPMENT -//#ifndef X11_ENABLED -//#define X11_ENABLED -//#endif -//#define GLES3_BACKEND_ENABLED -///////////////////////////////////////////////////// - -#if defined(GLES3_ENABLED) || defined(GLES_ENABLED) - -#define GLES3_BACKEND_ENABLED - -#endif // defined(GLES3_ENABLED) || defined(GLES_ENABLED) - -#endif // RASTERIZER_PLATFORMS_H diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index 77e0366f0e..9e7d4f5435 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "rasterizer_scene_gles3.h" -#ifdef GLES3_BACKEND_ENABLED +#ifdef GLES3_ENABLED // TODO: 3D support not implemented yet. @@ -471,4 +471,4 @@ void RasterizerSceneGLES3::light_projectors_set_filter(RS::LightProjectorFilter RasterizerSceneGLES3::RasterizerSceneGLES3() { } -#endif // GLES3_BACKEND_ENABLED +#endif // GLES3_ENABLED diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h index 9b356f28df..b73c053bc7 100644 --- a/drivers/gles3/rasterizer_scene_gles3.h +++ b/drivers/gles3/rasterizer_scene_gles3.h @@ -31,8 +31,7 @@ #ifndef RASTERIZER_SCENE_OPENGL_H #define RASTERIZER_SCENE_OPENGL_H -#include "drivers/gles3/rasterizer_platforms.h" -#ifdef GLES3_BACKEND_ENABLED +#ifdef GLES3_ENABLED #include "core/math/camera_matrix.h" #include "core/templates/rid_owner.h" @@ -41,12 +40,11 @@ #include "servers/rendering/renderer_compositor.h" #include "servers/rendering/renderer_scene_render.h" #include "servers/rendering_server.h" -#include "shaders/scene.glsl.gen.h" class RasterizerSceneGLES3 : public RendererSceneRender { public: struct State { - SceneShaderGLES3 scene_shader; + //SceneShaderGLES3 scene_shader; } state; GeometryInstance *geometry_instance_create(RID p_base) override; @@ -227,6 +225,6 @@ public: RasterizerSceneGLES3(); }; -#endif // GLES3_BACKEND_ENABLED +#endif // GLES3_ENABLED #endif // RASTERIZER_SCENE_OPENGL_H diff --git a/drivers/gles3/rasterizer_storage_common.h b/drivers/gles3/rasterizer_storage_common.h deleted file mode 100644 index d9a756af1f..0000000000 --- a/drivers/gles3/rasterizer_storage_common.h +++ /dev/null @@ -1,77 +0,0 @@ -/*************************************************************************/ -/* rasterizer_storage_common.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef RASTERIZER_STORAGE_COMMON_H -#define RASTERIZER_STORAGE_COMMON_H - -class RasterizerStorageCommon { -public: - enum FVF { - FVF_UNBATCHED, - FVF_REGULAR, - FVF_COLOR, - FVF_LIGHT_ANGLE, - FVF_MODULATED, - FVF_LARGE, - }; - - // these flags are specifically for batching - // some of the logic is thus in rasterizer_storage.cpp - // we could alternatively set bitflags for each 'uses' and test on the fly - enum BatchFlags { - PREVENT_COLOR_BAKING = 1 << 0, - PREVENT_VERTEX_BAKING = 1 << 1, - - // custom vertex shaders using BUILTINS that vary per item - PREVENT_ITEM_JOINING = 1 << 2, - - USE_MODULATE_FVF = 1 << 3, - USE_LARGE_FVF = 1 << 4, - }; - - enum BatchType : uint16_t { - BT_DEFAULT = 0, - BT_RECT = 1, - BT_LINE = 2, - BT_LINE_AA = 3, - BT_POLY = 4, - BT_DUMMY = 5, // dummy batch is just used to keep the batch creation loop simple - }; - - enum BatchTypeFlags { - BTF_DEFAULT = 1 << BT_DEFAULT, - BTF_RECT = 1 << BT_RECT, - BTF_LINE = 1 << BT_LINE, - BTF_LINE_AA = 1 << BT_LINE_AA, - BTF_POLY = 1 << BT_POLY, - }; -}; - -#endif // RASTERIZER_STORAGE_COMMON_H diff --git a/drivers/gles3/rasterizer_storage_gles3.cpp b/drivers/gles3/rasterizer_storage_gles3.cpp index e010e55307..db449b7a08 100644 --- a/drivers/gles3/rasterizer_storage_gles3.cpp +++ b/drivers/gles3/rasterizer_storage_gles3.cpp @@ -28,14 +28,11 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -//#define OPENGL_DISABLE_RENDER_TARGETS - #include "rasterizer_storage_gles3.h" -#ifdef GLES3_BACKEND_ENABLED +#ifdef GLES3_ENABLED #include "core/config/project_settings.h" #include "core/math/transform_3d.h" -#include "drivers/gles3/rasterizer_storage_common.h" #include "rasterizer_canvas_gles3.h" #include "rasterizer_scene_gles3.h" #include "servers/rendering/shader_language.h" @@ -1061,12 +1058,12 @@ void RasterizerStorageGLES3::_texture_set_state_from_flags(Texture *p_tex) { if (((p_tex->flags & TEXTURE_FLAG_REPEAT) || (p_tex->flags & TEXTURE_FLAG_MIRRORED_REPEAT)) && p_tex->target != GL_TEXTURE_CUBE_MAP) { if (p_tex->flags & TEXTURE_FLAG_MIRRORED_REPEAT) { - p_tex->GLSetRepeat(RS::CANVAS_ITEM_TEXTURE_REPEAT_MIRROR); + p_tex->GLSetRepeat(p_tex->target, RS::CANVAS_ITEM_TEXTURE_REPEAT_MIRROR); } else { - p_tex->GLSetRepeat(RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED); + p_tex->GLSetRepeat(p_tex->target, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED); } } else { - p_tex->GLSetRepeat(RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + p_tex->GLSetRepeat(p_tex->target, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); } } @@ -1285,21 +1282,43 @@ RID RasterizerStorageGLES3::texture_create_radiance_cubemap(RID p_source, int p_ } RID RasterizerStorageGLES3::canvas_texture_allocate() { - return RID(); + return canvas_texture_owner.allocate_rid(); } void RasterizerStorageGLES3::canvas_texture_initialize(RID p_rid) { + canvas_texture_owner.initialize_rid(p_rid); } void RasterizerStorageGLES3::canvas_texture_set_channel(RID p_canvas_texture, RS::CanvasTextureChannel p_channel, RID p_texture) { + CanvasTexture *ct = canvas_texture_owner.get_or_null(p_canvas_texture); + switch (p_channel) { + case RS::CANVAS_TEXTURE_CHANNEL_DIFFUSE: { + ct->diffuse = p_texture; + } break; + case RS::CANVAS_TEXTURE_CHANNEL_NORMAL: { + ct->normal_map = p_texture; + } break; + case RS::CANVAS_TEXTURE_CHANNEL_SPECULAR: { + ct->specular = p_texture; + } break; + } } -void RasterizerStorageGLES3::canvas_texture_set_shading_parameters(RID p_canvas_texture, const Color &p_base_color, float p_shininess) { +void RasterizerStorageGLES3::canvas_texture_set_shading_parameters(RID p_canvas_texture, const Color &p_specular_color, float p_shininess) { + CanvasTexture *ct = canvas_texture_owner.get_or_null(p_canvas_texture); + ct->specular_color.r = p_specular_color.r; + ct->specular_color.g = p_specular_color.g; + ct->specular_color.b = p_specular_color.b; + ct->specular_color.a = p_shininess; } -void RasterizerStorageGLES3::canvas_texture_set_texture_filter(RID p_item, RS::CanvasItemTextureFilter p_filter) { +void RasterizerStorageGLES3::canvas_texture_set_texture_filter(RID p_canvas_texture, RS::CanvasItemTextureFilter p_filter) { + CanvasTexture *ct = canvas_texture_owner.get_or_null(p_canvas_texture); + ct->texture_filter = p_filter; } -void RasterizerStorageGLES3::canvas_texture_set_texture_repeat(RID p_item, RS::CanvasItemTextureRepeat p_repeat) { +void RasterizerStorageGLES3::canvas_texture_set_texture_repeat(RID p_canvas_texture, RS::CanvasItemTextureRepeat p_repeat) { + CanvasTexture *ct = canvas_texture_owner.get_or_null(p_canvas_texture); + ct->texture_repeat = p_repeat; } RID RasterizerStorageGLES3::sky_create() { @@ -1309,169 +1328,14 @@ RID RasterizerStorageGLES3::sky_create() { } void RasterizerStorageGLES3::sky_set_texture(RID p_sky, RID p_panorama, int p_radiance_size) { - Sky *sky = sky_owner.get_or_null(p_sky); - ERR_FAIL_COND(!sky); - - if (sky->panorama.is_valid()) { - sky->panorama = RID(); - glDeleteTextures(1, &sky->radiance); - sky->radiance = 0; - } - - sky->panorama = p_panorama; - if (!sky->panorama.is_valid()) { - return; // the panorama was cleared - } - - Texture *texture = texture_owner.get_or_null(sky->panorama); - if (!texture) { - sky->panorama = RID(); - ERR_FAIL_COND(!texture); - } - - // glBindVertexArray(0) and more - { - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - glDisable(GL_CULL_FACE); - glDisable(GL_DEPTH_TEST); - glDisable(GL_SCISSOR_TEST); - glDisable(GL_BLEND); - - for (int i = 0; i < RS::ARRAY_MAX - 1; i++) { - //glDisableVertexAttribArray(i); - } - } - - glActiveTexture(GL_TEXTURE0); - glBindTexture(texture->target, texture->tex_id); - - 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_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); //need this for proper sampling - - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, resources.radical_inverse_vdc_cache_tex); - - 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_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - - // New cubemap that will hold the mipmaps with different roughness values - glActiveTexture(GL_TEXTURE2); - glGenTextures(1, &sky->radiance); - glBindTexture(GL_TEXTURE_CUBE_MAP, sky->radiance); - - int size = p_radiance_size / 2; //divide by two because its a cubemap (this is an approximation because GLES3 uses a dual paraboloid) - - GLenum internal_format = GL_RGB; - GLenum format = GL_RGB; - GLenum type = GL_UNSIGNED_BYTE; - - // Set the initial (empty) mipmaps - // Mobile hardware (PowerVR specially) prefers this approach, - // the previous approach with manual lod levels kills the game. - for (int i = 0; i < 6; i++) { - glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, internal_format, size, size, 0, format, type, NULL); - } - - glGenerateMipmap(GL_TEXTURE_CUBE_MAP); - - // No filters for now - glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - // Framebuffer - - bind_framebuffer(resources.mipmap_blur_fbo); - - int mipmaps = 6; - int lod = 0; - int mm_level = mipmaps; - size = p_radiance_size / 2; - shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_SOURCE_PANORAMA, true); - shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_DIRECT_WRITE, true); - shaders.cubemap_filter.bind(); - - // third, render to the framebuffer using separate textures, then copy to mipmaps - while (size >= 1) { - //make framebuffer size the texture size, need to use a separate texture for compatibility - glActiveTexture(GL_TEXTURE3); - glBindTexture(GL_TEXTURE_2D, resources.mipmap_blur_color); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, size, size, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, resources.mipmap_blur_color, 0); - - if (lod == 1) { - //bind panorama for smaller lods - - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_CUBE_MAP, sky->radiance); - shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_SOURCE_PANORAMA, false); - shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_DIRECT_WRITE, false); - shaders.cubemap_filter.bind(); - } - glViewport(0, 0, size, size); - bind_quad_array(); - - glActiveTexture(GL_TEXTURE2); //back to panorama - - for (int i = 0; i < 6; i++) { - shaders.cubemap_filter.set_uniform(CubemapFilterShaderGLES3::FACE_ID, i); - - float roughness = mm_level >= 0 ? lod / (float)(mipmaps - 1) : 1; - roughness = MIN(1.0, roughness); //keep max at 1 - shaders.cubemap_filter.set_uniform(CubemapFilterShaderGLES3::ROUGHNESS, roughness); - shaders.cubemap_filter.set_uniform(CubemapFilterShaderGLES3::Z_FLIP, false); - - //glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - - glCopyTexSubImage2D(_cube_side_enum[i], lod, 0, 0, 0, 0, size, size); - } - - size >>= 1; - - mm_level--; - - lod++; - } - - shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_SOURCE_PANORAMA, false); - shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::USE_DIRECT_WRITE, false); - - // restore ranges - glActiveTexture(GL_TEXTURE2); //back to panorama - - glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - glBindTexture(GL_TEXTURE_2D, 0); - glActiveTexture(GL_TEXTURE3); //back to panorama - glBindTexture(GL_TEXTURE_2D, 0); - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, 0); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, 0); - - //reset flags on Sky Texture that may have changed - texture_set_flags(sky->panorama, texture->flags); - - // Framebuffer did its job. thank mr framebuffer - glActiveTexture(GL_TEXTURE0); //back to panorama - bind_framebuffer_system(); } /* SHADER API */ RID RasterizerStorageGLES3::shader_allocate() { Shader *shader = memnew(Shader); - shader->mode = RS::SHADER_SPATIAL; - shader->shader = &scene->state.scene_shader; + shader->mode = RS::SHADER_CANVAS_ITEM; + //shader->shader = &scene->state.scene_shader; RID rid = shader_owner.make_rid(shader); _shader_make_dirty(shader); shader->self = rid; @@ -1510,16 +1374,22 @@ void RasterizerStorageGLES3::shader_set_code(RID p_shader, const String &p_code) String mode_string = ShaderLanguage::get_shader_type(p_code); RS::ShaderMode mode; - if (mode_string == "canvas_item") + if (mode_string == "canvas_item") { mode = RS::SHADER_CANVAS_ITEM; - else if (mode_string == "particles") + } else if (mode_string == "particles") { mode = RS::SHADER_PARTICLES; - else + } else if (mode_string == "sky") { + mode = RS::SHADER_SKY; + } else if (mode_string == "spatial") { mode = RS::SHADER_SPATIAL; + } else { + mode = RS::SHADER_MAX; + ERR_PRINT("shader type " + mode_string + " not supported in OpenGL renderer"); + } - if (shader->custom_code_id && mode != shader->mode) { - shader->shader->free_custom_shader(shader->custom_code_id); - shader->custom_code_id = 0; + if (shader->version.is_valid() && mode != shader->mode) { + shader->shader->version_free(shader->version); + shader->version = RID(); } shader->mode = mode; @@ -1529,13 +1399,15 @@ void RasterizerStorageGLES3::shader_set_code(RID p_shader, const String &p_code) shader->shader = &canvas->state.canvas_shader; } else if (mode == RS::SHADER_SPATIAL) { - shader->shader = &scene->state.scene_shader; + //shader->shader = &scene->state.scene_shader; + } else if (mode == RS::SHADER_PARTICLES) { + } else if (mode == RS::SHADER_SKY) { } else { return; } - if (shader->custom_code_id == 0) { - shader->custom_code_id = shader->shader->create_custom_shader(); + if (shader->version.is_null() && shader->shader) { + shader->version = shader->shader->version_create(); } _shader_make_dirty(shader); @@ -1559,8 +1431,8 @@ void RasterizerStorageGLES3::_update_shader(Shader *p_shader) const { return; //just invalid, but no error } - ShaderCompilerGLES3::GeneratedCode gen_code; - ShaderCompilerGLES3::IdentifierActions *actions = NULL; + ShaderCompiler::GeneratedCode gen_code; + ShaderCompiler::IdentifierActions *actions = NULL; switch (p_shader->mode) { case RS::SHADER_CANVAS_ITEM: { @@ -1573,7 +1445,6 @@ void RasterizerStorageGLES3::_update_shader(Shader *p_shader) const { p_shader->canvas_item.uses_modulate = false; p_shader->canvas_item.uses_color = false; p_shader->canvas_item.uses_vertex = false; - p_shader->canvas_item.batch_flags = 0; p_shader->canvas_item.uses_world_matrix = false; p_shader->canvas_item.uses_extra_matrix = false; @@ -1608,6 +1479,8 @@ void RasterizerStorageGLES3::_update_shader(Shader *p_shader) const { } break; case RS::SHADER_SPATIAL: { + // TODO remove once 3D is added back + return; p_shader->spatial.blend_mode = Shader::Spatial::BLEND_MODE_MIX; p_shader->spatial.depth_draw_mode = Shader::Spatial::DEPTH_DRAW_OPAQUE; p_shader->spatial.cull_mode = Shader::Spatial::CULL_MODE_BACK; @@ -1670,14 +1543,6 @@ void RasterizerStorageGLES3::_update_shader(Shader *p_shader) const { actions = &shaders.actions_scene; actions->uniforms = &p_shader->uniforms; - - if (p_shader->spatial.uses_screen_texture && p_shader->spatial.uses_depth_texture) { - ERR_PRINT_ONCE("Using both SCREEN_TEXTURE and DEPTH_TEXTURE is not supported in OpenGL"); - } - - if (p_shader->spatial.uses_depth_texture && !config.support_depth_texture) { - ERR_PRINT_ONCE("Using DEPTH_TEXTURE is not permitted on this hardware, operation will fail."); - } } break; default: { @@ -1690,38 +1555,23 @@ void RasterizerStorageGLES3::_update_shader(Shader *p_shader) const { return; } - p_shader->shader->set_custom_shader_code(p_shader->custom_code_id, gen_code.vertex, gen_code.vertex_global, gen_code.fragment, gen_code.light, gen_code.fragment_global, gen_code.uniforms, gen_code.texture_uniforms, gen_code.custom_defines); + Vector<StringName> texture_uniform_names; + for (int i = 0; i < gen_code.texture_uniforms.size(); i++) { + texture_uniform_names.push_back(gen_code.texture_uniforms[i].name); + } + + p_shader->shader->version_set_code(p_shader->version, gen_code.code, gen_code.uniforms, gen_code.stage_globals[ShaderCompiler::STAGE_VERTEX], gen_code.stage_globals[ShaderCompiler::STAGE_FRAGMENT], gen_code.defines, texture_uniform_names); - p_shader->texture_count = gen_code.texture_uniforms.size(); - p_shader->texture_hints = gen_code.texture_hints; + p_shader->texture_uniforms = gen_code.texture_uniforms; p_shader->uses_vertex_time = gen_code.uses_vertex_time; p_shader->uses_fragment_time = gen_code.uses_fragment_time; - // some logic for batching - if (p_shader->mode == RS::SHADER_CANVAS_ITEM) { - if (p_shader->canvas_item.uses_modulate | p_shader->canvas_item.uses_color) { - p_shader->canvas_item.batch_flags |= RasterizerStorageCommon::PREVENT_COLOR_BAKING; - } - if (p_shader->canvas_item.uses_vertex) { - p_shader->canvas_item.batch_flags |= RasterizerStorageCommon::PREVENT_VERTEX_BAKING; - } - if (p_shader->canvas_item.uses_world_matrix | p_shader->canvas_item.uses_extra_matrix | p_shader->canvas_item.uses_projection_matrix | p_shader->canvas_item.uses_instance_custom) { - p_shader->canvas_item.batch_flags |= RasterizerStorageCommon::PREVENT_ITEM_JOINING; - } - } - - p_shader->shader->set_custom_shader(p_shader->custom_code_id); - p_shader->shader->bind(); - - // cache uniform locations - for (SelfList<Material> *E = p_shader->materials.first(); E; E = E->next()) { _material_make_dirty(E->self()); } p_shader->valid = true; - p_shader->version++; } void RasterizerStorageGLES3::update_dirty_shaders() { @@ -1905,31 +1755,6 @@ RID RasterizerStorageGLES3::shader_get_default_texture_param(RID p_shader, const return RID(); } -void RasterizerStorageGLES3::shader_add_custom_define(RID p_shader, const String &p_define) { - Shader *shader = shader_owner.get_or_null(p_shader); - ERR_FAIL_COND(!shader); - - shader->shader->add_custom_define(p_define); - - _shader_make_dirty(shader); -} - -void RasterizerStorageGLES3::shader_get_custom_defines(RID p_shader, Vector<String> *p_defines) const { - Shader *shader = shader_owner.get_or_null(p_shader); - ERR_FAIL_COND(!shader); - - shader->shader->get_custom_defines(p_defines); -} - -void RasterizerStorageGLES3::shader_remove_custom_define(RID p_shader, const String &p_define) { - Shader *shader = shader_owner.get_or_null(p_shader); - ERR_FAIL_COND(!shader); - - shader->shader->remove_custom_define(p_define); - - _shader_make_dirty(shader); -} - /* COMMON MATERIAL API */ void RasterizerStorageGLES3::_material_make_dirty(Material *p_material) const { @@ -2186,8 +2011,8 @@ void RasterizerStorageGLES3::_update_material(Material *p_material) { // uniforms and other things will be set in the use_material method in ShaderGLES3 - if (p_material->shader && p_material->shader->texture_count > 0) { - p_material->textures.resize(p_material->shader->texture_count); + if (p_material->shader && p_material->shader->texture_uniforms.size() > 0) { + p_material->textures.resize(p_material->shader->texture_uniforms.size()); for (Map<StringName, ShaderLanguage::ShaderNode::Uniform>::Element *E = p_material->shader->uniforms.front(); E; E = E->next()) { if (E->get().texture_order < 0) @@ -2325,7 +2150,7 @@ RS::SurfaceData RasterizerStorageGLES3::mesh_get_surface(RID p_mesh, int p_surfa } int RasterizerStorageGLES3::mesh_get_surface_count(RID p_mesh) const { - return 0; + return 1; } void RasterizerStorageGLES3::mesh_set_custom_aabb(RID p_mesh, const AABB &p_aabb) { @@ -3125,39 +2950,20 @@ bool RasterizerStorageGLES3::particles_is_inactive(RID p_particles) const { /* RENDER TARGET */ void RasterizerStorageGLES3::_set_current_render_target(RID p_render_target) { -#ifdef OPENGL_DISABLE_RENDER_TARGETS - return; -#endif - RenderTarget *rt = render_target_owner.get_or_null(p_render_target); - // FTODO - // if (!p_render_target.is_valid() && storage->frame.current_rt && storage->frame.clear_request) { - // // pending clear request. Do that first. - // glBindFramebuffer(GL_FRAMEBUFFER, storage->frame.current_rt->fbo); - // glClearColor(storage->frame.clear_request_color.r, - // storage->frame.clear_request_color.g, - // storage->frame.clear_request_color.b, - // storage->frame.clear_request_color.a); - // glClear(GL_COLOR_BUFFER_BIT); - // } - if (rt) { if (rt->allocate_is_dirty) { rt->allocate_is_dirty = false; _render_target_allocate(rt); } - // if (p_render_target.is_valid()) { - // RasterizerStorageGLES3::RenderTarget *rt = storage.render_target_owner.get_or_null(p_render_target); frame.current_rt = rt; ERR_FAIL_COND(!rt); frame.clear_request = false; glViewport(0, 0, rt->width, rt->height); - // print_line("_set_current_render_target w " + itos(rt->width) + " h " + itos(rt->height)); - _dims.rt_width = rt->width; _dims.rt_height = rt->height; _dims.win_width = rt->width; @@ -3166,17 +2972,11 @@ void RasterizerStorageGLES3::_set_current_render_target(RID p_render_target) { } else { frame.current_rt = NULL; frame.clear_request = false; - // FTODO - // glViewport(0, 0, OS::get_singleton()->get_window_size().width, OS::get_singleton()->get_window_size().height); bind_framebuffer_system(); } } void RasterizerStorageGLES3::_render_target_allocate(RenderTarget *rt) { -#ifdef OPENGL_DISABLE_RENDER_TARGETS - return; -#endif - // do not allocate a render target with no size if (rt->width <= 0 || rt->height <= 0) return; @@ -3515,10 +3315,6 @@ void RasterizerStorageGLES3::_render_target_allocate(RenderTarget *rt) { } void RasterizerStorageGLES3::_render_target_clear(RenderTarget *rt) { -#ifdef OPENGL_DISABLE_RENDER_TARGETS - return; -#endif - // there is nothing to clear when DIRECT_TO_SCREEN is used if (rt->flags[RENDER_TARGET_DIRECT_TO_SCREEN]) return; @@ -3599,10 +3395,6 @@ void RasterizerStorageGLES3::_render_target_clear(RenderTarget *rt) { } RID RasterizerStorageGLES3::render_target_create() { -#ifdef OPENGL_DISABLE_RENDER_TARGETS -// return RID(); -#endif - RenderTarget *rt = memnew(RenderTarget); Texture *t = memnew(Texture); @@ -3631,10 +3423,6 @@ RID RasterizerStorageGLES3::render_target_create() { } void RasterizerStorageGLES3::render_target_set_position(RID p_render_target, int p_x, int p_y) { -#ifdef OPENGL_DISABLE_RENDER_TARGETS - return; -#endif - RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_COND(!rt); @@ -3643,10 +3431,6 @@ void RasterizerStorageGLES3::render_target_set_position(RID p_render_target, int } void RasterizerStorageGLES3::render_target_set_size(RID p_render_target, int p_width, int p_height, uint32_t p_view_count) { -#ifdef OPENGL_DISABLE_RENDER_TARGETS - return; -#endif - RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_COND(!rt); @@ -3664,11 +3448,15 @@ void RasterizerStorageGLES3::render_target_set_size(RID p_render_target, int p_w //_render_target_allocate(rt); } -RID RasterizerStorageGLES3::render_target_get_texture(RID p_render_target) { -#ifdef OPENGL_DISABLE_RENDER_TARGETS - return RID(); -#endif +// TODO: convert to Size2i internally +Size2i RasterizerStorageGLES3::render_target_get_size(RID p_render_target) { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND_V(!rt, Size2()); + + return Size2i(rt->width, rt->height); +} +RID RasterizerStorageGLES3::render_target_get_texture(RID p_render_target) { RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_COND_V(!rt, RID()); @@ -3680,10 +3468,6 @@ RID RasterizerStorageGLES3::render_target_get_texture(RID p_render_target) { } void RasterizerStorageGLES3::render_target_set_external_texture(RID p_render_target, unsigned int p_texture_id) { -#ifdef OPENGL_DISABLE_RENDER_TARGETS - return; -#endif - RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_COND(!rt); @@ -3789,10 +3573,6 @@ void RasterizerStorageGLES3::render_target_set_external_texture(RID p_render_tar } void RasterizerStorageGLES3::render_target_set_flag(RID p_render_target, RenderTargetFlags p_flag, bool p_value) { -#ifdef OPENGL_DISABLE_RENDER_TARGETS - return; -#endif - RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_COND(!rt); @@ -3825,10 +3605,6 @@ void RasterizerStorageGLES3::render_target_set_flag(RID p_render_target, RenderT } bool RasterizerStorageGLES3::render_target_was_used(RID p_render_target) { -#ifdef OPENGL_DISABLE_RENDER_TARGETS - return false; -#endif - RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_COND_V(!rt, false); @@ -3836,10 +3612,6 @@ bool RasterizerStorageGLES3::render_target_was_used(RID p_render_target) { } void RasterizerStorageGLES3::render_target_clear_used(RID p_render_target) { -#ifdef OPENGL_DISABLE_RENDER_TARGETS - return; -#endif - RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_COND(!rt); @@ -3847,10 +3619,6 @@ void RasterizerStorageGLES3::render_target_clear_used(RID p_render_target) { } void RasterizerStorageGLES3::render_target_set_msaa(RID p_render_target, RS::ViewportMSAA p_msaa) { -#ifdef OPENGL_DISABLE_RENDER_TARGETS - return; -#endif - RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_COND(!rt); @@ -3868,10 +3636,6 @@ void RasterizerStorageGLES3::render_target_set_msaa(RID p_render_target, RS::Vie //} void RasterizerStorageGLES3::render_target_set_use_fxaa(RID p_render_target, bool p_fxaa) { -#ifdef OPENGL_DISABLE_RENDER_TARGETS - return; -#endif - RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_COND(!rt); @@ -3879,10 +3643,6 @@ void RasterizerStorageGLES3::render_target_set_use_fxaa(RID p_render_target, boo } void RasterizerStorageGLES3::render_target_set_use_debanding(RID p_render_target, bool p_debanding) { -#ifdef OPENGL_DISABLE_RENDER_TARGETS - return; -#endif - RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_COND(!rt); @@ -3894,10 +3654,6 @@ void RasterizerStorageGLES3::render_target_set_use_debanding(RID p_render_target } void RasterizerStorageGLES3::render_target_request_clear(RID p_render_target, const Color &p_clear_color) { -#ifdef OPENGL_DISABLE_RENDER_TARGETS - return; -#endif - RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_COND(!rt); rt->clear_requested = true; @@ -3909,55 +3665,23 @@ void RasterizerStorageGLES3::render_target_request_clear(RID p_render_target, co } bool RasterizerStorageGLES3::render_target_is_clear_requested(RID p_render_target) { -#ifdef OPENGL_DISABLE_RENDER_TARGETS - return false; -#endif RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_COND_V(!rt, false); return rt->clear_requested; } Color RasterizerStorageGLES3::render_target_get_clear_request_color(RID p_render_target) { -#ifdef OPENGL_DISABLE_RENDER_TARGETS - return Color(); -#endif - RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_COND_V(!rt, Color()); return rt->clear_color; } void RasterizerStorageGLES3::render_target_disable_clear_request(RID p_render_target) { -#ifdef OPENGL_DISABLE_RENDER_TARGETS - return; -#endif RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_COND(!rt); rt->clear_requested = false; } void RasterizerStorageGLES3::render_target_do_clear_request(RID p_render_target) { -#ifdef OPENGL_DISABLE_RENDER_TARGETS - return; -#endif - - // NEW for GLES... - // This is being called at the wrong time. Instead it will be performed - // at canvas begin - return; - - /* - RenderTarget *rt = render_target_owner.get_or_null(p_render_target); - ERR_FAIL_COND(!rt); - if (!rt->clear_requested) { - return; - } - - const Color &c = rt->clear_color; - - glClearColor(c.r, c.g, c.b, c.a); - // more bits? - glClear(GL_COLOR_BUFFER_BIT); - */ } void RasterizerStorageGLES3::render_target_set_sdf_size_and_scale(RID p_render_target, RS::ViewportSDFOversize p_size, RS::ViewportSDFScale p_scale) { @@ -4160,12 +3884,18 @@ bool RasterizerStorageGLES3::free(RID p_rid) { Texture *t = texture_owner.get_or_null(p_rid); // can't free a render target texture ERR_FAIL_COND_V(t->render_target, true); + if (t->canvas_texture) { + memdelete(t->canvas_texture); + } info.texture_mem -= t->total_data_size; texture_owner.free(p_rid); memdelete(t); return true; + } else if (canvas_texture_owner.owns(p_rid)) { + canvas_texture_owner.free(p_rid); + return true; } else if (sky_owner.owns(p_rid)) { Sky *sky = sky_owner.get_or_null(p_rid); sky_set_texture(p_rid, RID(), 256); @@ -4176,8 +3906,8 @@ bool RasterizerStorageGLES3::free(RID p_rid) { } else if (shader_owner.owns(p_rid)) { Shader *shader = shader_owner.get_or_null(p_rid); - if (shader->shader && shader->custom_code_id) { - shader->shader->free_custom_shader(shader->custom_code_id); + if (shader->shader && shader->version.is_valid()) { + shader->shader->version_free(shader->version); } if (shader->dirty_list.in_list()) { @@ -4482,7 +4212,6 @@ void RasterizerStorageGLES3::initialize() { } } - // FTODO config.keep_original_textures = true; // false config.shrink_textures_x2 = false; config.depth_internalformat = GL_DEPTH_COMPONENT; @@ -4654,10 +4383,12 @@ void RasterizerStorageGLES3::initialize() { // OR max_vertex_texture_image_units is zero config.use_skeleton_software = (config.float_texture_supported == false) || (config.max_vertex_texture_image_units == 0); - shaders.copy.init(); - shaders.cubemap_filter.init(); - bool ggx_hq = false; //GLOBAL_GET("rendering/quality/reflections/high_quality_ggx"); - shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::LOW_QUALITY, !ggx_hq); + shaders.copy.initialize(); + shaders.copy_version = shaders.copy.version_create(); //TODO + shaders.copy.version_bind_shader(shaders.copy_version, CopyShaderGLES3::MODE_COPY_SECTION); + //shaders.cubemap_filter.init(); + //bool ggx_hq = GLOBAL_GET("rendering/quality/reflections/high_quality_ggx"); + //shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::LOW_QUALITY, !ggx_hq); { // quad for copying stuff @@ -4831,4 +4562,8 @@ RasterizerStorageGLES3::RasterizerStorageGLES3() { config.should_orphan = true; } -#endif // GLES3_BACKEND_ENABLED +RasterizerStorageGLES3::~RasterizerStorageGLES3() { + shaders.copy.version_free(shaders.copy_version); +} + +#endif // GLES3_ENABLED diff --git a/drivers/gles3/rasterizer_storage_gles3.h b/drivers/gles3/rasterizer_storage_gles3.h index 807789586b..c080d28f94 100644 --- a/drivers/gles3/rasterizer_storage_gles3.h +++ b/drivers/gles3/rasterizer_storage_gles3.h @@ -31,21 +31,17 @@ #ifndef RASTERIZER_STORAGE_OPENGL_H #define RASTERIZER_STORAGE_OPENGL_H -#include "drivers/gles3/rasterizer_platforms.h" -#ifdef GLES3_BACKEND_ENABLED +#ifdef GLES3_ENABLED #include "core/templates/local_vector.h" #include "core/templates/rid_owner.h" #include "core/templates/self_list.h" -#include "drivers/gles3/rasterizer_asserts.h" #include "servers/rendering/renderer_compositor.h" #include "servers/rendering/renderer_storage.h" +#include "servers/rendering/shader_compiler.h" #include "servers/rendering/shader_language.h" -#include "shader_compiler_gles3.h" -#include "shader_gles3.h" #include "shaders/copy.glsl.gen.h" -#include "shaders/cubemap_filter.glsl.gen.h" class RasterizerCanvasGLES3; class RasterizerSceneGLES3; @@ -134,14 +130,15 @@ public: } resources; mutable struct Shaders { - ShaderCompilerGLES3 compiler; + ShaderCompiler compiler; CopyShaderGLES3 copy; - CubemapFilterShaderGLES3 cubemap_filter; + RID copy_version; + //CubemapFilterShaderGLES3 cubemap_filter; - ShaderCompilerGLES3::IdentifierActions actions_canvas; - ShaderCompilerGLES3::IdentifierActions actions_scene; - ShaderCompilerGLES3::IdentifierActions actions_particles; + ShaderCompiler::IdentifierActions actions_canvas; + ShaderCompiler::IdentifierActions actions_scene; + ShaderCompiler::IdentifierActions actions_particles; } shaders; @@ -183,62 +180,6 @@ public: void bind_quad_array() const; ///////////////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////DATA/////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////////////////// - - /* - struct Instantiable { - RID self; - - SelfList<InstanceBaseDependency>::List instance_list; - - _FORCE_INLINE_ void instance_change_notify(bool p_aabb, bool p_materials) { - SelfList<InstanceBaseDependency> *instances = instance_list.first(); - while (instances) { - instances->self()->base_changed(p_aabb, p_materials); - instances = instances->next(); - } - } - - _FORCE_INLINE_ void instance_remove_deps() { - SelfList<InstanceBaseDependency> *instances = instance_list.first(); - - while (instances) { - instances->self()->base_removed(); - instances = instances->next(); - } - } - - Instantiable() {} - - ~Instantiable() {} - }; - - struct GeometryOwner : public Instantiable { - }; - - struct Geometry : public Instantiable { - enum Type { - GEOMETRY_INVALID, - GEOMETRY_SURFACE, - GEOMETRY_IMMEDIATE, - GEOMETRY_MULTISURFACE - }; - - Type type; - RID material; - uint64_t last_pass; - uint32_t index; - - void material_changed_notify() {} - - Geometry() { - last_pass = 0; - index = 0; - } - }; -*/ - ///////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////API//////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////// @@ -257,6 +198,26 @@ public: TEXTURE_FLAGS_DEFAULT = TEXTURE_FLAG_REPEAT | TEXTURE_FLAG_MIPMAPS | TEXTURE_FLAG_FILTER }; + /* CANVAS TEXTURE API (2D) */ + + 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; + + Size2i size_cache = Size2i(1, 1); + bool use_normal_cache = false; + bool use_specular_cache = false; + bool cleared_cache = true; + }; + + RID_Owner<CanvasTexture, true> canvas_texture_owner; + struct RenderTarget; struct Texture { @@ -309,6 +270,8 @@ public: RS::TextureDetectCallback detect_normal; void *detect_normal_ud; + CanvasTexture *canvas_texture = nullptr; + // some silly opengl shenanigans where // texture coords start from bottom left, means we need to draw render target textures upside down // to be compatible with vulkan etc. @@ -436,7 +399,7 @@ public: glTexParameteri(p_target, GL_TEXTURE_MIN_FILTER, pmin); glTexParameteri(p_target, GL_TEXTURE_MAG_FILTER, pmag); } - void GLSetRepeat(RS::CanvasItemTextureRepeat p_repeat) { + void GLSetRepeat(GLenum p_target, RS::CanvasItemTextureRepeat p_repeat) { if (p_repeat == state_repeat) return; state_repeat = p_repeat; @@ -451,8 +414,8 @@ public: prep = GL_MIRRORED_REPEAT; } break; } - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, prep); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, prep); + glTexParameteri(p_target, GL_TEXTURE_WRAP_S, prep); + glTexParameteri(p_target, GL_TEXTURE_WRAP_T, prep); } private: @@ -540,10 +503,10 @@ public: void canvas_texture_initialize(RID p_rid) override; void canvas_texture_set_channel(RID p_canvas_texture, RS::CanvasTextureChannel p_channel, RID p_texture) override; - void canvas_texture_set_shading_parameters(RID p_canvas_texture, const Color &p_base_color, float p_shininess) override; + void canvas_texture_set_shading_parameters(RID p_canvas_texture, const Color &p_specular_color, float p_shininess) override; - void canvas_texture_set_texture_filter(RID p_item, RS::CanvasItemTextureFilter p_filter) override; - void canvas_texture_set_texture_repeat(RID p_item, RS::CanvasItemTextureRepeat p_repeat) override; + void canvas_texture_set_texture_filter(RID p_canvas_texture, RS::CanvasItemTextureFilter p_filter) override; + void canvas_texture_set_texture_repeat(RID p_canvas_texture, RS::CanvasItemTextureRepeat p_repeat) override; /* SKY API */ // not sure if used in godot 4? @@ -573,16 +536,13 @@ public: Map<StringName, ShaderLanguage::ShaderNode::Uniform> uniforms; - uint32_t texture_count; - - uint32_t custom_code_id; - uint32_t version; + RID version; SelfList<Shader> dirty_list; Map<StringName, Map<int, RID>> default_textures; - Vector<ShaderLanguage::ShaderNode::Uniform::Hint> texture_hints; + Vector<ShaderCompiler::GeneratedCode::Texture> texture_uniforms; bool valid; @@ -610,12 +570,6 @@ public: int light_mode; - // these flags are specifically for batching - // some of the logic is thus in rasterizer_storage.cpp - // we could alternatively set bitflags for each 'uses' and test on the fly - // defined in RasterizerStorageCommon::BatchFlags - unsigned int batch_flags; - bool uses_screen_texture; bool uses_screen_uv; bool uses_time; @@ -686,8 +640,7 @@ public: dirty_list(this) { shader = NULL; valid = false; - custom_code_id = 0; - version = 1; + version = RID(); last_pass = 0; } }; @@ -711,10 +664,6 @@ public: RS::ShaderNativeSourceCode shader_get_native_source_code(RID p_shader) const override { return RS::ShaderNativeSourceCode(); }; - void shader_add_custom_define(RID p_shader, const String &p_define); - void shader_get_custom_defines(RID p_shader, Vector<String> *p_defines) const; - void shader_remove_custom_define(RID p_shader, const String &p_define); - void _update_shader(Shader *p_shader) const; void update_dirty_shaders(); @@ -838,6 +787,44 @@ public: /* MULTIMESH API */ + 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; + 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; + + RID buffer; //storage buffer + RID uniform_set_3d; + RID uniform_set_2d; + + bool dirty = false; + MultiMesh *dirty_list = nullptr; + + Dependency dependency; + }; + + 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); + void _update_dirty_multimeshes(); + RID multimesh_allocate() override; void multimesh_initialize(RID p_rid) override; 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) override; @@ -862,6 +849,29 @@ public: void multimesh_set_visible_instances(RID p_multimesh, int p_visible) override; int multimesh_get_visible_instances(RID p_multimesh) const override; + _FORCE_INLINE_ RS::MultimeshTransformFormat multimesh_get_transform_format(RID p_multimesh) const { + MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); + return multimesh->xform_format; + } + + _FORCE_INLINE_ bool multimesh_uses_colors(RID p_multimesh) const { + MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); + return multimesh->uses_colors; + } + + _FORCE_INLINE_ bool multimesh_uses_custom_data(RID p_multimesh) const { + MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); + 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); + if (multimesh->visible_instances >= 0) { + return multimesh->visible_instances; + } + return multimesh->instances; + } + /* SKELETON API */ RID skeleton_allocate() override; @@ -1258,6 +1268,7 @@ public: RID render_target_create() override; void render_target_set_position(RID p_render_target, int p_x, int p_y) override; void render_target_set_size(RID p_render_target, int p_width, int p_height, uint32_t p_view_count) override; + Size2i render_target_get_size(RID p_render_target); RID render_target_get_texture(RID p_render_target) override; void render_target_set_external_texture(RID p_render_target, unsigned int p_texture_id) override; @@ -1330,7 +1341,7 @@ public: bool clear_request; Color clear_request_color; - float time[4]; + float time; float delta; uint64_t count; @@ -1410,6 +1421,7 @@ public: } RasterizerStorageGLES3(); + ~RasterizerStorageGLES3(); }; inline bool RasterizerStorageGLES3::safe_buffer_sub_data(unsigned int p_total_buffer_size, GLenum p_target, unsigned int p_offset, unsigned int p_data_size, const void *p_data, unsigned int &r_offset_after) const { @@ -1445,10 +1457,9 @@ inline void RasterizerStorageGLES3::buffer_orphan_and_upload(unsigned int p_buff } #endif } - RAST_DEV_DEBUG_ASSERT((p_offset + p_data_size) <= p_buffer_size); glBufferSubData(p_target, p_offset, p_data_size, p_data); } -#endif // GLES3_BACKEND_ENABLED +#endif // GLES3_ENABLED #endif // RASTERIZER_STORAGE_OPENGL_H diff --git a/drivers/gles3/shader_compiler_gles3.cpp b/drivers/gles3/shader_compiler_gles3.cpp deleted file mode 100644 index 555ed6ebd2..0000000000 --- a/drivers/gles3/shader_compiler_gles3.cpp +++ /dev/null @@ -1,1136 +0,0 @@ -/*************************************************************************/ -/* shader_compiler_gles3.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "shader_compiler_gles3.h" -#ifdef GLES3_BACKEND_ENABLED - -#include "core/config/project_settings.h" -#include "core/os/os.h" -#include "core/string/string_buffer.h" -#include "core/string/string_builder.h" - -#define SL ShaderLanguage - -static String _mktab(int p_level) { - String tb; - for (int i = 0; i < p_level; i++) { - tb += "\t"; - } - - return tb; -} - -static String _typestr(SL::DataType p_type) { - return ShaderLanguage::get_datatype_name(p_type); -} - -static String _prestr(SL::DataPrecision p_pres) { - switch (p_pres) { - case SL::PRECISION_LOWP: - return "lowp "; - case SL::PRECISION_MEDIUMP: - return "mediump "; - case SL::PRECISION_HIGHP: - return "highp "; - case SL::PRECISION_DEFAULT: - return ""; - } - return ""; -} - -static String _constr(bool p_is_const) { - if (p_is_const) { - return "const "; - } - return ""; -} - -static String _qualstr(SL::ArgumentQualifier p_qual) { - switch (p_qual) { - case SL::ARGUMENT_QUALIFIER_IN: - return "in "; - case SL::ARGUMENT_QUALIFIER_OUT: - return "out "; - case SL::ARGUMENT_QUALIFIER_INOUT: - return "inout "; - } - return ""; -} - -static String _opstr(SL::Operator p_op) { - return SL::get_operator_text(p_op); -} - -static String _mkid(const String &p_id) { - String id = "m_" + p_id.replace("__", "_dus_"); - return id.replace("__", "_dus_"); //doubleunderscore is reserved in glsl -} - -static String f2sp0(float p_float) { - String num = rtoss(p_float); - if (num.find(".") == -1 && num.find("e") == -1) { - num += ".0"; - } - return num; -} - -static String get_constant_text(SL::DataType p_type, const Vector<SL::ConstantNode::Value> &p_values) { - switch (p_type) { - case SL::TYPE_BOOL: - return p_values[0].boolean ? "true" : "false"; - case SL::TYPE_BVEC2: - case SL::TYPE_BVEC3: - case SL::TYPE_BVEC4: { - StringBuffer<> text; - - text += "bvec"; - text += itos(p_type - SL::TYPE_BOOL + 1); - text += "("; - - for (int i = 0; i < p_values.size(); i++) { - if (i > 0) - text += ","; - - text += p_values[i].boolean ? "true" : "false"; - } - text += ")"; - return text.as_string(); - } - - // GLSL ES 2 doesn't support uints, so we just use signed ints instead... - case SL::TYPE_UINT: - return itos(p_values[0].uint); - case SL::TYPE_UVEC2: - case SL::TYPE_UVEC3: - case SL::TYPE_UVEC4: { - StringBuffer<> text; - - text += "ivec"; - text += itos(p_type - SL::TYPE_UINT + 1); - text += "("; - - for (int i = 0; i < p_values.size(); i++) { - if (i > 0) - text += ","; - - text += itos(p_values[i].uint); - } - text += ")"; - return text.as_string(); - - } break; - - case SL::TYPE_INT: - return itos(p_values[0].sint); - case SL::TYPE_IVEC2: - case SL::TYPE_IVEC3: - case SL::TYPE_IVEC4: { - StringBuffer<> text; - - text += "ivec"; - text += itos(p_type - SL::TYPE_INT + 1); - text += "("; - - for (int i = 0; i < p_values.size(); i++) { - if (i > 0) - text += ","; - - text += itos(p_values[i].sint); - } - text += ")"; - return text.as_string(); - - } break; - case SL::TYPE_FLOAT: - return f2sp0(p_values[0].real); - case SL::TYPE_VEC2: - case SL::TYPE_VEC3: - case SL::TYPE_VEC4: { - StringBuffer<> text; - - text += "vec"; - text += itos(p_type - SL::TYPE_FLOAT + 1); - text += "("; - - for (int i = 0; i < p_values.size(); i++) { - if (i > 0) - text += ","; - - text += f2sp0(p_values[i].real); - } - text += ")"; - return text.as_string(); - - } break; - case SL::TYPE_MAT2: - case SL::TYPE_MAT3: - case SL::TYPE_MAT4: { - StringBuffer<> text; - - text += "mat"; - text += itos(p_type - SL::TYPE_MAT2 + 2); - text += "("; - - for (int i = 0; i < p_values.size(); i++) { - if (i > 0) - text += ","; - - text += f2sp0(p_values[i].real); - } - text += ")"; - return text.as_string(); - - } break; - default: - ERR_FAIL_V(String()); - } -} - -void ShaderCompilerGLES3::_dump_function_deps(SL::ShaderNode *p_node, const StringName &p_for_func, const Map<StringName, String> &p_func_code, StringBuilder &r_to_add, Set<StringName> &r_added) { - int fidx = -1; - - for (int i = 0; i < p_node->functions.size(); i++) { - if (p_node->functions[i].name == p_for_func) { - fidx = i; - break; - } - } - - ERR_FAIL_COND(fidx == -1); - - for (Set<StringName>::Element *E = p_node->functions[fidx].uses_function.front(); E; E = E->next()) { - if (r_added.has(E->get())) { - continue; - } - - _dump_function_deps(p_node, E->get(), p_func_code, r_to_add, r_added); - - SL::FunctionNode *fnode = NULL; - - for (int i = 0; i < p_node->functions.size(); i++) { - if (p_node->functions[i].name == E->get()) { - fnode = p_node->functions[i].function; - break; - } - } - - ERR_FAIL_COND(!fnode); - - r_to_add += "\n"; - - StringBuffer<128> header; - - header += _typestr(fnode->return_type); - header += " "; - header += _mkid(fnode->name); - header += "("; - - for (int i = 0; i < fnode->arguments.size(); i++) { - if (i > 0) { - header += ", "; - } - header += _constr(fnode->arguments[i].is_const); - header += _qualstr(fnode->arguments[i].qualifier); - header += _prestr(fnode->arguments[i].precision); - header += _typestr(fnode->arguments[i].type); - header += " "; - header += _mkid(fnode->arguments[i].name); - } - - header += ")\n"; - r_to_add += header.as_string(); - r_to_add += p_func_code[E->get()]; - - r_added.insert(E->get()); - } -} - -String ShaderCompilerGLES3::_dump_node_code(SL::Node *p_node, int p_level, GeneratedCode &r_gen_code, IdentifierActions &p_actions, const DefaultIdentifierActions &p_default_actions, bool p_assigning, bool p_use_scope) { - StringBuilder code; - - switch (p_node->type) { - default: { - } break; - case SL::Node::TYPE_SHADER: { - SL::ShaderNode *snode = (SL::ShaderNode *)p_node; - - for (int i = 0; i < snode->render_modes.size(); i++) { - if (p_default_actions.render_mode_defines.has(snode->render_modes[i]) && !used_rmode_defines.has(snode->render_modes[i])) { - r_gen_code.custom_defines.push_back(p_default_actions.render_mode_defines[snode->render_modes[i]].utf8()); - used_rmode_defines.insert(snode->render_modes[i]); - } - - if (p_actions.render_mode_flags.has(snode->render_modes[i])) { - *p_actions.render_mode_flags[snode->render_modes[i]] = true; - } - - if (p_actions.render_mode_values.has(snode->render_modes[i])) { - Pair<int *, int> &p = p_actions.render_mode_values[snode->render_modes[i]]; - *p.first = p.second; - } - } - - int max_texture_uniforms = 0; - int max_uniforms = 0; - - for (Map<StringName, SL::ShaderNode::Uniform>::Element *E = snode->uniforms.front(); E; E = E->next()) { - if (SL::is_sampler_type(E->get().type)) - max_texture_uniforms++; - else - max_uniforms++; - } - - r_gen_code.texture_uniforms.resize(max_texture_uniforms); - r_gen_code.texture_hints.resize(max_texture_uniforms); - - r_gen_code.uniforms.resize(max_uniforms + max_texture_uniforms); - - StringBuilder vertex_global; - StringBuilder fragment_global; - - // uniforms - - for (Map<StringName, SL::ShaderNode::Uniform>::Element *E = snode->uniforms.front(); E; E = E->next()) { - StringBuffer<> uniform_code; - - // use highp if no precision is specified to prevent different default values in fragment and vertex shader - SL::DataPrecision precision = E->get().precision; - if (precision == SL::PRECISION_DEFAULT && E->get().type != SL::TYPE_BOOL) { - precision = SL::PRECISION_HIGHP; - } - - uniform_code += "uniform "; - uniform_code += _prestr(precision); - uniform_code += _typestr(E->get().type); - uniform_code += " "; - uniform_code += _mkid(E->key()); - uniform_code += ";\n"; - - if (SL::is_sampler_type(E->get().type)) { - r_gen_code.texture_uniforms.write[E->get().texture_order] = E->key(); - r_gen_code.texture_hints.write[E->get().texture_order] = E->get().hint; - } else { - r_gen_code.uniforms.write[E->get().order] = E->key(); - } - - vertex_global += uniform_code.as_string(); - fragment_global += uniform_code.as_string(); - - p_actions.uniforms->insert(E->key(), E->get()); - } - - // varyings - - for (Map<StringName, SL::ShaderNode::Varying>::Element *E = snode->varyings.front(); E; E = E->next()) { - StringBuffer<> varying_code; - - varying_code += "varying "; - varying_code += _prestr(E->get().precision); - varying_code += _typestr(E->get().type); - varying_code += " "; - varying_code += _mkid(E->key()); - if (E->get().array_size > 0) { - varying_code += "["; - varying_code += itos(E->get().array_size); - varying_code += "]"; - } - varying_code += ";\n"; - - String final_code = varying_code.as_string(); - - vertex_global += final_code; - fragment_global += final_code; - } - - // constants - - for (int i = 0; i < snode->vconstants.size(); i++) { - String gcode; - gcode += _constr(true); - gcode += _prestr(snode->vconstants[i].precision); - gcode += _typestr(snode->vconstants[i].type); - gcode += " " + _mkid(String(snode->vconstants[i].name)); - gcode += "="; - gcode += _dump_node_code(snode->vconstants[i].initializer, p_level, r_gen_code, p_actions, p_default_actions, p_assigning); - gcode += ";\n"; - vertex_global += gcode; - fragment_global += gcode; - } - - // functions - - Map<StringName, String> function_code; - - for (int i = 0; i < snode->functions.size(); i++) { - SL::FunctionNode *fnode = snode->functions[i].function; - current_func_name = fnode->name; - function_code[fnode->name] = _dump_node_code(fnode->body, 1, r_gen_code, p_actions, p_default_actions, p_assigning); - } - - Set<StringName> added_vertex; - Set<StringName> added_fragment; - - for (int i = 0; i < snode->functions.size(); i++) { - SL::FunctionNode *fnode = snode->functions[i].function; - - current_func_name = fnode->name; - - if (fnode->name == vertex_name) { - _dump_function_deps(snode, fnode->name, function_code, vertex_global, added_vertex); - r_gen_code.vertex = function_code[vertex_name]; - - } else if (fnode->name == fragment_name) { - _dump_function_deps(snode, fnode->name, function_code, fragment_global, added_fragment); - r_gen_code.fragment = function_code[fragment_name]; - - } else if (fnode->name == light_name) { - _dump_function_deps(snode, fnode->name, function_code, fragment_global, added_fragment); - r_gen_code.light = function_code[light_name]; - } - } - - r_gen_code.vertex_global = vertex_global.as_string(); - r_gen_code.fragment_global = fragment_global.as_string(); - - } break; - - case SL::Node::TYPE_FUNCTION: { - } break; - - case SL::Node::TYPE_BLOCK: { - SL::BlockNode *bnode = (SL::BlockNode *)p_node; - - if (!bnode->single_statement) { - code += _mktab(p_level - 1); - code += "{\n"; - } - - for (int i = 0; i < bnode->statements.size(); i++) { - String statement_code = _dump_node_code(bnode->statements[i], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); - - if (bnode->statements[i]->type == SL::Node::TYPE_CONTROL_FLOW || bnode->single_statement) { - code += statement_code; - } else { - code += _mktab(p_level); - code += statement_code; - code += ";\n"; - } - } - - if (!bnode->single_statement) { - code += _mktab(p_level - 1); - code += "}\n"; - } - } break; - - case SL::Node::TYPE_VARIABLE_DECLARATION: { - SL::VariableDeclarationNode *var_dec_node = (SL::VariableDeclarationNode *)p_node; - - StringBuffer<> declaration; - declaration += _constr(var_dec_node->is_const); - declaration += _prestr(var_dec_node->precision); - declaration += _typestr(var_dec_node->datatype); - - for (int i = 0; i < var_dec_node->declarations.size(); i++) { - if (i > 0) { - declaration += ","; - } - - declaration += " "; - - declaration += _mkid(var_dec_node->declarations[i].name); - - if (var_dec_node->declarations[i].initializer) { - declaration += " = "; - declaration += _dump_node_code(var_dec_node->declarations[i].initializer, p_level, r_gen_code, p_actions, p_default_actions, p_assigning); - } - } - - code += declaration.as_string(); - } break; - - case SL::Node::TYPE_VARIABLE: { - SL::VariableNode *var_node = (SL::VariableNode *)p_node; - - if (p_assigning && p_actions.write_flag_pointers.has(var_node->name)) { - *p_actions.write_flag_pointers[var_node->name] = true; - } - - if (p_default_actions.usage_defines.has(var_node->name) && !used_name_defines.has(var_node->name)) { - String define = p_default_actions.usage_defines[var_node->name]; - String node_name = define.substr(1, define.length()); - - if (define.begins_with("@")) { - define = p_default_actions.usage_defines[node_name]; - } - - if (!used_name_defines.has(node_name)) { - r_gen_code.custom_defines.push_back(define.utf8()); - } - used_name_defines.insert(var_node->name); - } - - if (p_actions.usage_flag_pointers.has(var_node->name) && !used_flag_pointers.has(var_node->name)) { - *p_actions.usage_flag_pointers[var_node->name] = true; - used_flag_pointers.insert(var_node->name); - } - - if (p_default_actions.renames.has(var_node->name)) { - code += p_default_actions.renames[var_node->name]; - } else { - code += _mkid(var_node->name); - } - - if (var_node->name == time_name) { - if (current_func_name == vertex_name) { - r_gen_code.uses_vertex_time = true; - } - if (current_func_name == fragment_name || current_func_name == light_name) { - r_gen_code.uses_fragment_time = true; - } - } - } break; - case SL::Node::TYPE_ARRAY_DECLARATION: { - SL::ArrayDeclarationNode *arr_dec_node = (SL::ArrayDeclarationNode *)p_node; - - StringBuffer<> declaration; - declaration += _prestr(arr_dec_node->precision); - declaration += _typestr(arr_dec_node->datatype); - - for (int i = 0; i < arr_dec_node->declarations.size(); i++) { - if (i > 0) { - declaration += ","; - } - - declaration += " "; - - declaration += _mkid(arr_dec_node->declarations[i].name); - declaration += "["; - declaration += itos(arr_dec_node->declarations[i].size); - declaration += "]"; - } - - code += declaration.as_string(); - } break; - case SL::Node::TYPE_ARRAY: { - SL::ArrayNode *arr_node = (SL::ArrayNode *)p_node; - - if (p_assigning && p_actions.write_flag_pointers.has(arr_node->name)) { - *p_actions.write_flag_pointers[arr_node->name] = true; - } - - if (p_default_actions.usage_defines.has(arr_node->name) && !used_name_defines.has(arr_node->name)) { - String define = p_default_actions.usage_defines[arr_node->name]; - String node_name = define.substr(1, define.length()); - - if (define.begins_with("@")) { - define = p_default_actions.usage_defines[node_name]; - } - - if (!used_name_defines.has(node_name)) { - r_gen_code.custom_defines.push_back(define.utf8()); - } - used_name_defines.insert(arr_node->name); - } - - if (p_actions.usage_flag_pointers.has(arr_node->name) && !used_flag_pointers.has(arr_node->name)) { - *p_actions.usage_flag_pointers[arr_node->name] = true; - used_flag_pointers.insert(arr_node->name); - } - - if (p_default_actions.renames.has(arr_node->name)) { - code += p_default_actions.renames[arr_node->name]; - } else { - code += _mkid(arr_node->name); - } - - if (arr_node->call_expression != NULL) { - code += "."; - code += _dump_node_code(arr_node->call_expression, p_level, r_gen_code, p_actions, p_default_actions, p_assigning, false); - } - - if (arr_node->index_expression != NULL) { - code += "["; - code += _dump_node_code(arr_node->index_expression, p_level, r_gen_code, p_actions, p_default_actions, p_assigning); - code += "]"; - } - - if (arr_node->name == time_name) { - if (current_func_name == vertex_name) { - r_gen_code.uses_vertex_time = true; - } - if (current_func_name == fragment_name || current_func_name == light_name) { - r_gen_code.uses_fragment_time = true; - } - } - - } break; - case SL::Node::TYPE_CONSTANT: { - SL::ConstantNode *const_node = (SL::ConstantNode *)p_node; - - return get_constant_text(const_node->datatype, const_node->values); - } break; - - case SL::Node::TYPE_OPERATOR: { - SL::OperatorNode *op_node = (SL::OperatorNode *)p_node; - - switch (op_node->op) { - case SL::OP_ASSIGN: - case SL::OP_ASSIGN_ADD: - case SL::OP_ASSIGN_SUB: - case SL::OP_ASSIGN_MUL: - case SL::OP_ASSIGN_DIV: - case SL::OP_ASSIGN_SHIFT_LEFT: - case SL::OP_ASSIGN_SHIFT_RIGHT: - case SL::OP_ASSIGN_BIT_AND: - case SL::OP_ASSIGN_BIT_OR: - case SL::OP_ASSIGN_BIT_XOR: { - code += _dump_node_code(op_node->arguments[0], p_level, r_gen_code, p_actions, p_default_actions, true); - code += " "; - code += _opstr(op_node->op); - code += " "; - code += _dump_node_code(op_node->arguments[1], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); - } break; - - case SL::OP_ASSIGN_MOD: { - String a = _dump_node_code(op_node->arguments[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); - String n = _dump_node_code(op_node->arguments[1], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); - code += a + " = " + n + " == 0 ? 0 : "; - code += a + " - " + n + " * (" + a + " / " + n + ")"; - } break; - - case SL::OP_BIT_INVERT: - case SL::OP_NEGATE: - case SL::OP_NOT: - case SL::OP_DECREMENT: - case SL::OP_INCREMENT: { - code += _opstr(op_node->op); - code += _dump_node_code(op_node->arguments[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); - } break; - - case SL::OP_POST_DECREMENT: - case SL::OP_POST_INCREMENT: { - code += _dump_node_code(op_node->arguments[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); - code += _opstr(op_node->op); - } break; - - case SL::OP_CALL: - case SL::OP_CONSTRUCT: { - ERR_FAIL_COND_V(op_node->arguments[0]->type != SL::Node::TYPE_VARIABLE, String()); - - SL::VariableNode *var_node = (SL::VariableNode *)op_node->arguments[0]; - - if (op_node->op == SL::OP_CONSTRUCT) { - code += var_node->name; - } else { - if (var_node->name == "texture") { - // emit texture call - - if (op_node->arguments[1]->get_datatype() == SL::TYPE_SAMPLER2D) { // || - // op_node->arguments[1]->get_datatype() == SL::TYPE_SAMPLEREXT) { - code += "texture"; - } else if (op_node->arguments[1]->get_datatype() == SL::TYPE_SAMPLERCUBE) { - code += "textureCube"; - } - - } else if (var_node->name == "textureLod") { - // emit texture call - - if (op_node->arguments[1]->get_datatype() == SL::TYPE_SAMPLER2D) { - code += "textureLod"; - } else if (op_node->arguments[1]->get_datatype() == SL::TYPE_SAMPLERCUBE) { - code += "textureCubeLod"; - } - - } else if (var_node->name == "mix") { - switch (op_node->arguments[3]->get_datatype()) { - case SL::TYPE_BVEC2: { - code += "select2"; - } break; - - case SL::TYPE_BVEC3: { - code += "select3"; - } break; - - case SL::TYPE_BVEC4: { - code += "select4"; - } break; - - case SL::TYPE_VEC2: - case SL::TYPE_VEC3: - case SL::TYPE_VEC4: - case SL::TYPE_FLOAT: { - code += "mix"; - } break; - - default: { - SL::DataType type = op_node->arguments[3]->get_datatype(); - // FIXME: Proper error print or graceful handling - print_line(String("uhhhh invalid mix with type: ") + itos(type)); - } break; - } - - } else if (p_default_actions.renames.has(var_node->name)) { - code += p_default_actions.renames[var_node->name]; - } else if (internal_functions.has(var_node->name)) { - code += var_node->name; - } else { - code += _mkid(var_node->name); - } - } - - code += "("; - - for (int i = 1; i < op_node->arguments.size(); i++) { - if (i > 1) { - code += ", "; - } - - code += _dump_node_code(op_node->arguments[i], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); - } - - code += ")"; - - if (p_default_actions.usage_defines.has(var_node->name) && !used_name_defines.has(var_node->name)) { - String define = p_default_actions.usage_defines[var_node->name]; - String node_name = define.substr(1, define.length()); - - if (define.begins_with("@")) { - define = p_default_actions.usage_defines[node_name]; - } - - if (!used_name_defines.has(node_name)) { - r_gen_code.custom_defines.push_back(define.utf8()); - } - used_name_defines.insert(var_node->name); - } - - } break; - - case SL::OP_INDEX: { - code += _dump_node_code(op_node->arguments[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); - code += "["; - code += _dump_node_code(op_node->arguments[1], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); - code += "]"; - } break; - - case SL::OP_SELECT_IF: { - code += "("; - code += _dump_node_code(op_node->arguments[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); - code += " ? "; - code += _dump_node_code(op_node->arguments[1], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); - code += " : "; - code += _dump_node_code(op_node->arguments[2], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); - code += ")"; - } break; - - case SL::OP_MOD: { - String a = _dump_node_code(op_node->arguments[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); - String n = _dump_node_code(op_node->arguments[1], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); - code += "(" + n + " == 0 ? 0 : "; - code += a + " - " + n + " * (" + a + " / " + n + "))"; - } break; - - case SL::OP_EMPTY: { - // Semicolon (or empty statement) - ignored. - } break; - - default: { - if (p_use_scope) { - code += "("; - } - code += _dump_node_code(op_node->arguments[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); - code += " "; - code += _opstr(op_node->op); - code += " "; - code += _dump_node_code(op_node->arguments[1], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); - if (p_use_scope) { - code += ")"; - } - } break; - } - } break; - - case SL::Node::TYPE_CONTROL_FLOW: { - SL::ControlFlowNode *cf_node = (SL::ControlFlowNode *)p_node; - - if (cf_node->flow_op == SL::FLOW_OP_IF) { - code += _mktab(p_level); - code += "if ("; - code += _dump_node_code(cf_node->expressions[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); - code += ")\n"; - code += _dump_node_code(cf_node->blocks[0], p_level + 1, r_gen_code, p_actions, p_default_actions, p_assigning); - - if (cf_node->blocks.size() == 2) { - code += _mktab(p_level); - code += "else\n"; - code += _dump_node_code(cf_node->blocks[1], p_level + 1, r_gen_code, p_actions, p_default_actions, p_assigning); - } - } else if (cf_node->flow_op == SL::FLOW_OP_DO) { - code += _mktab(p_level); - code += "do"; - code += _dump_node_code(cf_node->blocks[0], p_level + 1, r_gen_code, p_actions, p_default_actions, p_assigning); - code += _mktab(p_level); - code += "while ("; - code += _dump_node_code(cf_node->expressions[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); - code += ");"; - } else if (cf_node->flow_op == SL::FLOW_OP_WHILE) { - code += _mktab(p_level); - code += "while ("; - code += _dump_node_code(cf_node->expressions[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); - code += ")\n"; - code += _dump_node_code(cf_node->blocks[0], p_level + 1, r_gen_code, p_actions, p_default_actions, p_assigning); - } else if (cf_node->flow_op == SL::FLOW_OP_FOR) { - code += _mktab(p_level); - code += "for ("; - code += _dump_node_code(cf_node->blocks[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); - code += "; "; - code += _dump_node_code(cf_node->expressions[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); - code += "; "; - code += _dump_node_code(cf_node->expressions[1], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); - code += ")\n"; - - code += _dump_node_code(cf_node->blocks[1], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); - - } else if (cf_node->flow_op == SL::FLOW_OP_RETURN) { - code += _mktab(p_level); - code += "return"; - - if (cf_node->expressions.size()) { - code += " "; - code += _dump_node_code(cf_node->expressions[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); - } - code += ";\n"; - } else if (cf_node->flow_op == SL::FLOW_OP_DISCARD) { - if (p_actions.usage_flag_pointers.has("DISCARD") && !used_flag_pointers.has("DISCARD")) { - *p_actions.usage_flag_pointers["DISCARD"] = true; - used_flag_pointers.insert("DISCARD"); - } - code += "discard;"; - } else if (cf_node->flow_op == SL::FLOW_OP_CONTINUE) { - code += "continue;"; - } else if (cf_node->flow_op == SL::FLOW_OP_BREAK) { - code += "break;"; - } - } break; - - case SL::Node::TYPE_MEMBER: { - SL::MemberNode *member_node = (SL::MemberNode *)p_node; - code += _dump_node_code(member_node->owner, p_level, r_gen_code, p_actions, p_default_actions, p_assigning); - code += "."; - code += member_node->name; - } break; - } - - return code.as_string(); -} - -ShaderLanguage::DataType ShaderCompilerGLES3::_get_variable_type(const StringName &p_type) { - // RS::GlobalVariableType gvt = ((RasterizerStorageRD *)(RendererStorage::base_singleton))->global_variable_get_type_internal(p_type); - RS::GlobalVariableType gvt = RS::GLOBAL_VAR_TYPE_MAX; - return RS::global_variable_type_get_shader_datatype(gvt); -} - -Error ShaderCompilerGLES3::compile(RS::ShaderMode p_mode, const String &p_code, IdentifierActions *p_actions, const String &p_path, GeneratedCode &r_gen_code) { - ShaderLanguage::VaryingFunctionNames var_names; - - ShaderLanguage::ShaderCompileInfo info; - info.functions = ShaderTypes::get_singleton()->get_functions(p_mode); - info.render_modes = ShaderTypes::get_singleton()->get_modes(p_mode); - info.shader_types = ShaderTypes::get_singleton()->get_types(); - info.global_variable_type_func = _get_variable_type; - - Error err = parser.compile(p_code, info); - - // Error ShaderLanguage::compile(const String &p_code, const Map<StringName, FunctionInfo> &p_functions, const Vector<StringName> &p_render_modes, const Set<String> &p_shader_types, GlobalVariableGetTypeFunc p_global_variable_type_func) { - if (err != OK) { - Vector<String> shader = p_code.split("\n"); - for (int i = 0; i < shader.size(); i++) { - print_line(itos(i + 1) + " " + shader[i]); - } - - _err_print_error(NULL, p_path.utf8().get_data(), parser.get_error_line(), parser.get_error_text().utf8().get_data(), false, ERR_HANDLER_SHADER); - return err; - } - - r_gen_code.custom_defines.clear(); - r_gen_code.uniforms.clear(); - r_gen_code.texture_uniforms.clear(); - r_gen_code.texture_hints.clear(); - r_gen_code.vertex = String(); - r_gen_code.vertex_global = String(); - r_gen_code.fragment = String(); - r_gen_code.fragment_global = String(); - r_gen_code.light = String(); - r_gen_code.uses_fragment_time = false; - r_gen_code.uses_vertex_time = false; - - used_name_defines.clear(); - used_rmode_defines.clear(); - used_flag_pointers.clear(); - - _dump_node_code(parser.get_shader(), 1, r_gen_code, *p_actions, actions[p_mode], false); - - return OK; -} - -ShaderCompilerGLES3::ShaderCompilerGLES3() { - /** CANVAS ITEM SHADER **/ - - actions[RS::SHADER_CANVAS_ITEM].renames["VERTEX"] = "outvec.xy"; - actions[RS::SHADER_CANVAS_ITEM].renames["UV"] = "uv"; - actions[RS::SHADER_CANVAS_ITEM].renames["POINT_SIZE"] = "point_size"; - - actions[RS::SHADER_CANVAS_ITEM].renames["WORLD_MATRIX"] = "modelview_matrix"; - actions[RS::SHADER_CANVAS_ITEM].renames["PROJECTION_MATRIX"] = "projection_matrix"; - actions[RS::SHADER_CANVAS_ITEM].renames["EXTRA_MATRIX"] = "extra_matrix_instance"; - actions[RS::SHADER_CANVAS_ITEM].renames["TIME"] = "time"; - actions[RS::SHADER_CANVAS_ITEM].renames["PI"] = _MKSTR(Math_PI); - actions[RS::SHADER_CANVAS_ITEM].renames["TAU"] = _MKSTR(Math_TAU); - actions[RS::SHADER_CANVAS_ITEM].renames["E"] = _MKSTR(Math_E); - actions[RS::SHADER_CANVAS_ITEM].renames["AT_LIGHT_PASS"] = "at_light_pass"; - actions[RS::SHADER_CANVAS_ITEM].renames["INSTANCE_CUSTOM"] = "instance_custom"; - - actions[RS::SHADER_CANVAS_ITEM].renames["COLOR"] = "color"; - actions[RS::SHADER_CANVAS_ITEM].renames["MODULATE"] = "final_modulate"; - actions[RS::SHADER_CANVAS_ITEM].renames["NORMAL"] = "normal"; - actions[RS::SHADER_CANVAS_ITEM].renames["NORMALMAP"] = "normal_map"; - actions[RS::SHADER_CANVAS_ITEM].renames["NORMALMAP_DEPTH"] = "normal_depth"; - actions[RS::SHADER_CANVAS_ITEM].renames["TEXTURE"] = "color_texture"; - actions[RS::SHADER_CANVAS_ITEM].renames["TEXTURE_PIXEL_SIZE"] = "color_texpixel_size"; - actions[RS::SHADER_CANVAS_ITEM].renames["NORMAL_TEXTURE"] = "normal_texture"; - actions[RS::SHADER_CANVAS_ITEM].renames["SCREEN_UV"] = "screen_uv"; - actions[RS::SHADER_CANVAS_ITEM].renames["SCREEN_TEXTURE"] = "screen_texture"; - actions[RS::SHADER_CANVAS_ITEM].renames["SCREEN_PIXEL_SIZE"] = "screen_pixel_size"; - actions[RS::SHADER_CANVAS_ITEM].renames["FRAGCOORD"] = "gl_FragCoord"; - actions[RS::SHADER_CANVAS_ITEM].renames["POINT_COORD"] = "gl_PointCoord"; - - actions[RS::SHADER_CANVAS_ITEM].renames["LIGHT_VEC"] = "light_vec"; - actions[RS::SHADER_CANVAS_ITEM].renames["LIGHT_HEIGHT"] = "light_height"; - actions[RS::SHADER_CANVAS_ITEM].renames["LIGHT_COLOR"] = "light_color"; - actions[RS::SHADER_CANVAS_ITEM].renames["LIGHT_UV"] = "light_uv"; - actions[RS::SHADER_CANVAS_ITEM].renames["LIGHT"] = "light"; - actions[RS::SHADER_CANVAS_ITEM].renames["SHADOW_COLOR"] = "shadow_color"; - actions[RS::SHADER_CANVAS_ITEM].renames["SHADOW_VEC"] = "shadow_vec"; - - actions[RS::SHADER_CANVAS_ITEM].usage_defines["COLOR"] = "#define COLOR_USED\n"; - actions[RS::SHADER_CANVAS_ITEM].usage_defines["MODULATE"] = "#define MODULATE_USED\n"; - actions[RS::SHADER_CANVAS_ITEM].usage_defines["SCREEN_TEXTURE"] = "#define SCREEN_TEXTURE_USED\n"; - actions[RS::SHADER_CANVAS_ITEM].usage_defines["SCREEN_UV"] = "#define SCREEN_UV_USED\n"; - actions[RS::SHADER_CANVAS_ITEM].usage_defines["SCREEN_PIXEL_SIZE"] = "@SCREEN_UV"; - actions[RS::SHADER_CANVAS_ITEM].usage_defines["NORMAL"] = "#define NORMAL_USED\n"; - actions[RS::SHADER_CANVAS_ITEM].usage_defines["NORMALMAP"] = "#define NORMALMAP_USED\n"; - actions[RS::SHADER_CANVAS_ITEM].usage_defines["LIGHT"] = "#define USE_LIGHT_SHADER_CODE\n"; - actions[RS::SHADER_CANVAS_ITEM].render_mode_defines["skip_vertex_transform"] = "#define SKIP_TRANSFORM_USED\n"; - actions[RS::SHADER_CANVAS_ITEM].usage_defines["SHADOW_VEC"] = "#define SHADOW_VEC_USED\n"; - - // Ported from GLES3 - - actions[RS::SHADER_CANVAS_ITEM].usage_defines["sinh"] = "#define SINH_USED\n"; - actions[RS::SHADER_CANVAS_ITEM].usage_defines["cosh"] = "#define COSH_USED\n"; - actions[RS::SHADER_CANVAS_ITEM].usage_defines["tanh"] = "#define TANH_USED\n"; - actions[RS::SHADER_CANVAS_ITEM].usage_defines["asinh"] = "#define ASINH_USED\n"; - actions[RS::SHADER_CANVAS_ITEM].usage_defines["acosh"] = "#define ACOSH_USED\n"; - actions[RS::SHADER_CANVAS_ITEM].usage_defines["atanh"] = "#define ATANH_USED\n"; - actions[RS::SHADER_CANVAS_ITEM].usage_defines["determinant"] = "#define DETERMINANT_USED\n"; - actions[RS::SHADER_CANVAS_ITEM].usage_defines["transpose"] = "#define TRANSPOSE_USED\n"; - actions[RS::SHADER_CANVAS_ITEM].usage_defines["outerProduct"] = "#define OUTER_PRODUCT_USED\n"; - actions[RS::SHADER_CANVAS_ITEM].usage_defines["round"] = "#define ROUND_USED\n"; - actions[RS::SHADER_CANVAS_ITEM].usage_defines["roundEven"] = "#define ROUND_EVEN_USED\n"; - actions[RS::SHADER_CANVAS_ITEM].usage_defines["inverse"] = "#define INVERSE_USED\n"; - actions[RS::SHADER_CANVAS_ITEM].usage_defines["isinf"] = "#define IS_INF_USED\n"; - actions[RS::SHADER_CANVAS_ITEM].usage_defines["isnan"] = "#define IS_NAN_USED\n"; - actions[RS::SHADER_CANVAS_ITEM].usage_defines["trunc"] = "#define TRUNC_USED\n"; - - /** SPATIAL SHADER **/ - - actions[RS::SHADER_SPATIAL].renames["WORLD_MATRIX"] = "world_transform"; - actions[RS::SHADER_SPATIAL].renames["INV_CAMERA_MATRIX"] = "camera_inverse_matrix"; - actions[RS::SHADER_SPATIAL].renames["CAMERA_MATRIX"] = "camera_matrix"; - actions[RS::SHADER_SPATIAL].renames["PROJECTION_MATRIX"] = "projection_matrix"; - actions[RS::SHADER_SPATIAL].renames["INV_PROJECTION_MATRIX"] = "projection_inverse_matrix"; - actions[RS::SHADER_SPATIAL].renames["MODELVIEW_MATRIX"] = "modelview"; - - actions[RS::SHADER_SPATIAL].renames["VERTEX"] = "vertex.xyz"; - actions[RS::SHADER_SPATIAL].renames["NORMAL"] = "normal"; - actions[RS::SHADER_SPATIAL].renames["TANGENT"] = "tangent"; - actions[RS::SHADER_SPATIAL].renames["BINORMAL"] = "binormal"; - actions[RS::SHADER_SPATIAL].renames["POSITION"] = "position"; - actions[RS::SHADER_SPATIAL].renames["UV"] = "uv_interp"; - actions[RS::SHADER_SPATIAL].renames["UV2"] = "uv2_interp"; - actions[RS::SHADER_SPATIAL].renames["COLOR"] = "color_interp"; - actions[RS::SHADER_SPATIAL].renames["POINT_SIZE"] = "point_size"; - // gl_InstanceID is not available in OpenGL ES 2.0 - actions[RS::SHADER_SPATIAL].renames["INSTANCE_ID"] = "0"; - - //builtins - - actions[RS::SHADER_SPATIAL].renames["TIME"] = "time"; - actions[RS::SHADER_SPATIAL].renames["PI"] = _MKSTR(Math_PI); - actions[RS::SHADER_SPATIAL].renames["TAU"] = _MKSTR(Math_TAU); - actions[RS::SHADER_SPATIAL].renames["E"] = _MKSTR(Math_E); - actions[RS::SHADER_SPATIAL].renames["VIEWPORT_SIZE"] = "viewport_size"; - - actions[RS::SHADER_SPATIAL].renames["FRAGCOORD"] = "gl_FragCoord"; - actions[RS::SHADER_SPATIAL].renames["FRONT_FACING"] = "gl_FrontFacing"; - actions[RS::SHADER_SPATIAL].renames["NORMALMAP"] = "normalmap"; - actions[RS::SHADER_SPATIAL].renames["NORMALMAP_DEPTH"] = "normaldepth"; - actions[RS::SHADER_SPATIAL].renames["ALBEDO"] = "albedo"; - actions[RS::SHADER_SPATIAL].renames["ALPHA"] = "alpha"; - actions[RS::SHADER_SPATIAL].renames["METALLIC"] = "metallic"; - actions[RS::SHADER_SPATIAL].renames["SPECULAR"] = "specular"; - actions[RS::SHADER_SPATIAL].renames["ROUGHNESS"] = "roughness"; - actions[RS::SHADER_SPATIAL].renames["RIM"] = "rim"; - actions[RS::SHADER_SPATIAL].renames["RIM_TINT"] = "rim_tint"; - actions[RS::SHADER_SPATIAL].renames["CLEARCOAT"] = "clearcoat"; - actions[RS::SHADER_SPATIAL].renames["CLEARCOAT_GLOSS"] = "clearcoat_gloss"; - actions[RS::SHADER_SPATIAL].renames["ANISOTROPY"] = "anisotropy"; - actions[RS::SHADER_SPATIAL].renames["ANISOTROPY_FLOW"] = "anisotropy_flow"; - actions[RS::SHADER_SPATIAL].renames["SSS_STRENGTH"] = "sss_strength"; - actions[RS::SHADER_SPATIAL].renames["TRANSMISSION"] = "transmission"; - actions[RS::SHADER_SPATIAL].renames["AO"] = "ao"; - actions[RS::SHADER_SPATIAL].renames["AO_LIGHT_AFFECT"] = "ao_light_affect"; - actions[RS::SHADER_SPATIAL].renames["EMISSION"] = "emission"; - actions[RS::SHADER_SPATIAL].renames["POINT_COORD"] = "gl_PointCoord"; - actions[RS::SHADER_SPATIAL].renames["INSTANCE_CUSTOM"] = "instance_custom"; - actions[RS::SHADER_SPATIAL].renames["SCREEN_UV"] = "screen_uv"; - actions[RS::SHADER_SPATIAL].renames["SCREEN_TEXTURE"] = "screen_texture"; - actions[RS::SHADER_SPATIAL].renames["DEPTH_TEXTURE"] = "depth_texture"; - // Defined in GLES3, but not available in GLES2 - //actions[RS::SHADER_SPATIAL].renames["DEPTH"] = "gl_FragDepth"; - actions[RS::SHADER_SPATIAL].renames["ALPHA_SCISSOR"] = "alpha_scissor"; - actions[RS::SHADER_SPATIAL].renames["OUTPUT_IS_SRGB"] = "SHADER_IS_SRGB"; - - //for light - actions[RS::SHADER_SPATIAL].renames["VIEW"] = "view"; - actions[RS::SHADER_SPATIAL].renames["LIGHT_COLOR"] = "light_color"; - actions[RS::SHADER_SPATIAL].renames["LIGHT"] = "light"; - actions[RS::SHADER_SPATIAL].renames["ATTENUATION"] = "attenuation"; - actions[RS::SHADER_SPATIAL].renames["DIFFUSE_LIGHT"] = "diffuse_light"; - actions[RS::SHADER_SPATIAL].renames["SPECULAR_LIGHT"] = "specular_light"; - - actions[RS::SHADER_SPATIAL].usage_defines["TANGENT"] = "#define ENABLE_TANGENT_INTERP\n"; - actions[RS::SHADER_SPATIAL].usage_defines["BINORMAL"] = "@TANGENT"; - actions[RS::SHADER_SPATIAL].usage_defines["RIM"] = "#define LIGHT_USE_RIM\n"; - actions[RS::SHADER_SPATIAL].usage_defines["RIM_TINT"] = "@RIM"; - actions[RS::SHADER_SPATIAL].usage_defines["CLEARCOAT"] = "#define LIGHT_USE_CLEARCOAT\n"; - actions[RS::SHADER_SPATIAL].usage_defines["CLEARCOAT_GLOSS"] = "@CLEARCOAT"; - actions[RS::SHADER_SPATIAL].usage_defines["ANISOTROPY"] = "#define LIGHT_USE_ANISOTROPY\n"; - actions[RS::SHADER_SPATIAL].usage_defines["ANISOTROPY_FLOW"] = "@ANISOTROPY"; - actions[RS::SHADER_SPATIAL].usage_defines["AO"] = "#define ENABLE_AO\n"; - actions[RS::SHADER_SPATIAL].usage_defines["AO_LIGHT_AFFECT"] = "#define ENABLE_AO\n"; - actions[RS::SHADER_SPATIAL].usage_defines["UV"] = "#define ENABLE_UV_INTERP\n"; - actions[RS::SHADER_SPATIAL].usage_defines["UV2"] = "#define ENABLE_UV2_INTERP\n"; - actions[RS::SHADER_SPATIAL].usage_defines["NORMALMAP"] = "#define ENABLE_NORMALMAP\n"; - actions[RS::SHADER_SPATIAL].usage_defines["NORMALMAP_DEPTH"] = "@NORMALMAP"; - actions[RS::SHADER_SPATIAL].usage_defines["COLOR"] = "#define ENABLE_COLOR_INTERP\n"; - actions[RS::SHADER_SPATIAL].usage_defines["INSTANCE_CUSTOM"] = "#define ENABLE_INSTANCE_CUSTOM\n"; - actions[RS::SHADER_SPATIAL].usage_defines["ALPHA_SCISSOR"] = "#define ALPHA_SCISSOR_USED\n"; - actions[RS::SHADER_SPATIAL].usage_defines["POSITION"] = "#define OVERRIDE_POSITION\n"; - - actions[RS::SHADER_SPATIAL].usage_defines["SSS_STRENGTH"] = "#define ENABLE_SSS\n"; - actions[RS::SHADER_SPATIAL].usage_defines["TRANSMISSION"] = "#define TRANSMISSION_USED\n"; - actions[RS::SHADER_SPATIAL].usage_defines["SCREEN_TEXTURE"] = "#define SCREEN_TEXTURE_USED\n"; - actions[RS::SHADER_SPATIAL].usage_defines["DEPTH_TEXTURE"] = "#define DEPTH_TEXTURE_USED\n"; - actions[RS::SHADER_SPATIAL].usage_defines["SCREEN_UV"] = "#define SCREEN_UV_USED\n"; - - actions[RS::SHADER_SPATIAL].usage_defines["DIFFUSE_LIGHT"] = "#define USE_LIGHT_SHADER_CODE\n"; - actions[RS::SHADER_SPATIAL].usage_defines["SPECULAR_LIGHT"] = "#define USE_LIGHT_SHADER_CODE\n"; - - // Ported from GLES3 - - actions[RS::SHADER_SPATIAL].usage_defines["sinh"] = "#define SINH_USED\n"; - actions[RS::SHADER_SPATIAL].usage_defines["cosh"] = "#define COSH_USED\n"; - actions[RS::SHADER_SPATIAL].usage_defines["tanh"] = "#define TANH_USED\n"; - actions[RS::SHADER_SPATIAL].usage_defines["asinh"] = "#define ASINH_USED\n"; - actions[RS::SHADER_SPATIAL].usage_defines["acosh"] = "#define ACOSH_USED\n"; - actions[RS::SHADER_SPATIAL].usage_defines["atanh"] = "#define ATANH_USED\n"; - actions[RS::SHADER_SPATIAL].usage_defines["determinant"] = "#define DETERMINANT_USED\n"; - actions[RS::SHADER_SPATIAL].usage_defines["transpose"] = "#define TRANSPOSE_USED\n"; - actions[RS::SHADER_SPATIAL].usage_defines["outerProduct"] = "#define OUTER_PRODUCT_USED\n"; - actions[RS::SHADER_SPATIAL].usage_defines["round"] = "#define ROUND_USED\n"; - actions[RS::SHADER_SPATIAL].usage_defines["roundEven"] = "#define ROUND_EVEN_USED\n"; - actions[RS::SHADER_SPATIAL].usage_defines["inverse"] = "#define INVERSE_USED\n"; - actions[RS::SHADER_SPATIAL].usage_defines["isinf"] = "#define IS_INF_USED\n"; - actions[RS::SHADER_SPATIAL].usage_defines["isnan"] = "#define IS_NAN_USED\n"; - actions[RS::SHADER_SPATIAL].usage_defines["trunc"] = "#define TRUNC_USED\n"; - - actions[RS::SHADER_SPATIAL].render_mode_defines["skip_vertex_transform"] = "#define SKIP_TRANSFORM_USED\n"; - actions[RS::SHADER_SPATIAL].render_mode_defines["world_vertex_coords"] = "#define VERTEX_WORLD_COORDS_USED\n"; - - // Defined in GLES3, could be implemented in GLES2 too if there's a need for it - //actions[RS::SHADER_SPATIAL].render_mode_defines["ensure_correct_normals"] = "#define ENSURE_CORRECT_NORMALS\n"; - // Defined in GLES3, might not be possible in GLES2 as gl_FrontFacing is not available - //actions[RS::SHADER_SPATIAL].render_mode_defines["cull_front"] = "#define DO_SIDE_CHECK\n"; - //actions[RS::SHADER_SPATIAL].render_mode_defines["cull_disabled"] = "#define DO_SIDE_CHECK\n"; - - bool force_lambert = GLOBAL_GET("rendering/quality/shading/force_lambert_over_burley"); - - if (!force_lambert) { - actions[RS::SHADER_SPATIAL].render_mode_defines["diffuse_burley"] = "#define DIFFUSE_BURLEY\n"; - } - - actions[RS::SHADER_SPATIAL].render_mode_defines["diffuse_oren_nayar"] = "#define DIFFUSE_OREN_NAYAR\n"; - actions[RS::SHADER_SPATIAL].render_mode_defines["diffuse_lambert_wrap"] = "#define DIFFUSE_LAMBERT_WRAP\n"; - actions[RS::SHADER_SPATIAL].render_mode_defines["diffuse_toon"] = "#define DIFFUSE_TOON\n"; - - bool force_blinn = GLOBAL_GET("rendering/quality/shading/force_blinn_over_ggx"); - - if (!force_blinn) { - actions[RS::SHADER_SPATIAL].render_mode_defines["specular_schlick_ggx"] = "#define SPECULAR_SCHLICK_GGX\n"; - } else { - actions[RS::SHADER_SPATIAL].render_mode_defines["specular_schlick_ggx"] = "#define SPECULAR_BLINN\n"; - } - - actions[RS::SHADER_SPATIAL].render_mode_defines["specular_blinn"] = "#define SPECULAR_BLINN\n"; - actions[RS::SHADER_SPATIAL].render_mode_defines["specular_phong"] = "#define SPECULAR_PHONG\n"; - actions[RS::SHADER_SPATIAL].render_mode_defines["specular_toon"] = "#define SPECULAR_TOON\n"; - actions[RS::SHADER_SPATIAL].render_mode_defines["specular_disabled"] = "#define SPECULAR_DISABLED\n"; - actions[RS::SHADER_SPATIAL].render_mode_defines["shadows_disabled"] = "#define SHADOWS_DISABLED\n"; - actions[RS::SHADER_SPATIAL].render_mode_defines["ambient_light_disabled"] = "#define AMBIENT_LIGHT_DISABLED\n"; - actions[RS::SHADER_SPATIAL].render_mode_defines["shadow_to_opacity"] = "#define USE_SHADOW_TO_OPACITY\n"; - - // No defines for particle shaders in OpenGL, there are no GPU particles - - vertex_name = "vertex"; - fragment_name = "fragment"; - light_name = "light"; - time_name = "TIME"; - - List<String> func_list; - - ShaderLanguage::get_builtin_funcs(&func_list); - - for (List<String>::Element *E = func_list.front(); E; E = E->next()) { - internal_functions.insert(E->get()); - } -} - -#endif // GLES3_BACKEND_ENABLED diff --git a/drivers/gles3/shader_compiler_gles3.h b/drivers/gles3/shader_compiler_gles3.h deleted file mode 100644 index 7ed882d03d..0000000000 --- a/drivers/gles3/shader_compiler_gles3.h +++ /dev/null @@ -1,106 +0,0 @@ -/*************************************************************************/ -/* shader_compiler_gles3.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef SHADER_COMPILER_OPENGL_H -#define SHADER_COMPILER_OPENGL_H - -#include "drivers/gles3/rasterizer_platforms.h" -#ifdef GLES3_BACKEND_ENABLED - -#include "core/string/string_builder.h" -#include "core/templates/pair.h" -#include "servers/rendering/shader_language.h" -#include "servers/rendering/shader_types.h" -#include "servers/rendering_server.h" - -class ShaderCompilerGLES3 { -public: - struct IdentifierActions { - Map<StringName, Pair<int *, int>> render_mode_values; - Map<StringName, bool *> render_mode_flags; - Map<StringName, bool *> usage_flag_pointers; - Map<StringName, bool *> write_flag_pointers; - - Map<StringName, ShaderLanguage::ShaderNode::Uniform> *uniforms; - }; - - struct GeneratedCode { - Vector<CharString> custom_defines; - Vector<StringName> uniforms; - Vector<StringName> texture_uniforms; - Vector<ShaderLanguage::ShaderNode::Uniform::Hint> texture_hints; - - String vertex_global; - String vertex; - String fragment_global; - String fragment; - String light; - - bool uses_fragment_time; - bool uses_vertex_time; - }; - -private: - ShaderLanguage parser; - - struct DefaultIdentifierActions { - Map<StringName, String> renames; - Map<StringName, String> render_mode_defines; - Map<StringName, String> usage_defines; - }; - - void _dump_function_deps(ShaderLanguage::ShaderNode *p_node, const StringName &p_for_func, const Map<StringName, String> &p_func_code, StringBuilder &r_to_add, Set<StringName> &r_added); - String _dump_node_code(ShaderLanguage::Node *p_node, int p_level, GeneratedCode &r_gen_code, IdentifierActions &p_actions, const DefaultIdentifierActions &p_default_actions, bool p_assigning, bool p_use_scope = true); - - StringName current_func_name; - StringName vertex_name; - StringName fragment_name; - StringName light_name; - StringName time_name; - - Set<StringName> used_name_defines; - Set<StringName> used_flag_pointers; - Set<StringName> used_rmode_defines; - Set<StringName> internal_functions; - - DefaultIdentifierActions actions[RS::SHADER_MAX]; - - // compatibility with godot 4 - static ShaderLanguage::DataType _get_variable_type(const StringName &p_type); - -public: - Error compile(RS::ShaderMode p_mode, const String &p_code, IdentifierActions *p_actions, const String &p_path, GeneratedCode &r_gen_code); - - ShaderCompilerGLES3(); -}; - -#endif // GLES3_BACKEND_ENABLED - -#endif // SHADER_COMPILER_OPENGL_H diff --git a/drivers/gles3/shader_gles3.cpp b/drivers/gles3/shader_gles3.cpp index 474a80aca1..7ae8b4e3bf 100644 --- a/drivers/gles3/shader_gles3.cpp +++ b/drivers/gles3/shader_gles3.cpp @@ -29,378 +29,305 @@ /*************************************************************************/ #include "shader_gles3.h" -#include "drivers/gles3/rasterizer_platforms.h" -#ifdef GLES3_BACKEND_ENABLED +#ifdef GLES3_ENABLED -#include "rasterizer_gles3.h" -#include "rasterizer_storage_gles3.h" +#include "core/io/compression.h" +#include "core/io/dir_access.h" +#include "core/io/file_access.h" -#include "core/config/project_settings.h" -#include "core/os/memory.h" -#include "core/string/print_string.h" -#include "core/string/string_builder.h" +void ShaderGLES3::_add_stage(const char *p_code, StageType p_stage_type) { + Vector<String> lines = String(p_code).split("\n"); -// #define DEBUG_OPENGL + String text; -// #include "shaders/copy.glsl.gen.h" + for (int i = 0; i < lines.size(); i++) { + String l = lines[i]; + bool push_chunk = false; -#ifdef DEBUG_OPENGL + StageTemplate::Chunk chunk; -#define DEBUG_TEST_ERROR(m_section) \ - { \ - uint32_t err = glGetError(); \ - if (err) { \ - print_line("OpenGL Error #" + itos(err) + " at: " + m_section); \ - } \ - } -#else - -#define DEBUG_TEST_ERROR(m_section) - -#endif - -ShaderGLES3 *ShaderGLES3::active = NULL; - -//#define DEBUG_SHADER - -#ifdef DEBUG_SHADER - -#define DEBUG_PRINT(m_text) print_line(m_text); - -#else - -#define DEBUG_PRINT(m_text) - -#endif - -GLint ShaderGLES3::get_uniform_location(int p_index) const { - ERR_FAIL_COND_V(!version, -1); - - return version->uniform_location[p_index]; -} + 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: { + } + } -bool ShaderGLES3::bind() { - if (active != this || !version || new_conditional_version.key != conditional_version.key) { - conditional_version = new_conditional_version; - version = get_current_version(); - } else { - return false; - } + 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()).replace(":", "").strip_edges().to_upper(); + } else { + text += l + "\n"; + } - ERR_FAIL_COND_V(!version, false); + 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 (!version->ok) { //broken, unable to bind (do not throw error, you saw it before already when it failed compilation). - glUseProgram(0); - return false; + 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(); + } } - - glUseProgram(version->id); - - DEBUG_TEST_ERROR("use program"); - - active = this; - uniforms_dirty = true; - - return true; } -void ShaderGLES3::unbind() { - version = NULL; - glUseProgram(0); - uniforms_dirty = true; - active = NULL; -} - -static void _display_error_with_code(const String &p_error, const Vector<const char *> &p_code) { - int line = 1; - String total_code; +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_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; - for (int i = 0; i < p_code.size(); i++) { - total_code += String(p_code[i]); - } - - Vector<String> lines = String(total_code).split("\n"); - - for (int j = 0; j < lines.size(); j++) { - print_line(itos(line) + ": " + lines[j]); - line++; + if (p_vertex_code) { + _add_stage(p_vertex_code, STAGE_TYPE_VERTEX); } - - ERR_PRINT(p_error); -} - -static String _mkid(const String &p_id) { - String id = "m_" + p_id; - return id.replace("__", "_dus_"); //doubleunderscore is reserved in glsl -} - -ShaderGLES3::Version *ShaderGLES3::get_current_version() { - if (!valid) - return nullptr; - - Version *_v = version_map.getptr(conditional_version); - - if (_v) { - if (conditional_version.code_version != 0) { - CustomCode *cc = custom_code_map.getptr(conditional_version.code_version); - ERR_FAIL_COND_V(!cc, _v); - if (cc->version == _v->code_version) - return _v; - } else { - return _v; - } + if (p_fragment_code) { + _add_stage(p_fragment_code, STAGE_TYPE_FRAGMENT); } - if (!_v) - version_map[conditional_version] = Version(); - - Version &v = version_map[conditional_version]; - - if (!_v) { - v.uniform_location = memnew_arr(GLint, uniform_count); - } else { - if (v.ok) { - glDeleteShader(v.vert_id); - glDeleteShader(v.frag_id); - glDeleteProgram(v.id); - v.id = 0; + 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; + + StringBuilder tohash; + /* + tohash.append("[SpirvCacheKey]"); + tohash.append(RenderingDevice::get_singleton()->shader_get_spirv_cache_key()); + tohash.append("[BinaryCacheKey]"); + tohash.append(RenderingDevice::get_singleton()->shader_get_binary_cache_key()); + */ + tohash.append("[Vertex]"); + tohash.append(p_vertex_code ? p_vertex_code : ""); + tohash.append("[Fragment]"); + tohash.append(p_fragment_code ? p_fragment_code : ""); + + base_sha256 = tohash.as_string().sha256_text(); +} - v.ok = false; +RID ShaderGLES3::version_create() { + //initialize() was never called + ERR_FAIL_COND_V(variant_count == 0, RID()); - Vector<const char *> strings; + Version version; + return version_owner.make_rid(version); +} +void ShaderGLES3::_build_variant_code(StringBuilder &builder, uint32_t p_variant, const Version *p_version, const StageTemplate &p_template, uint64_t p_specialization) { #ifdef GLES_OVER_GL - strings.push_back("#version 330\n"); - strings.push_back("#define USE_GLES_OVER_GL\n"); + builder.append("#version 330\n"); + builder.append("#define USE_GLES_OVER_GL\n"); #else - strings.push_back("#version 300 es\n"); -//angle does not like -#ifdef JAVASCRIPT_ENABLED - strings.push_back("#define USE_HIGHP_PRECISION\n"); -#endif - - //if (GLOBAL_GET("rendering/opengl/compatibility/enable_high_float.Android")) { - // enable USE_HIGHP_PRECISION but safeguarded by an availability check as highp support is optional in OpenGL - // see Section 4.5.4 of the GLSL_ES_Specification_1.00 - //strings.push_back("#ifdef GL_FRAGMENT_PRECISION_HIGH\n #define USE_HIGHP_PRECISION\n#endif\n"); - //} - + builder.append("#version 300 es\n"); #endif -#ifdef ANDROID_ENABLED - strings.push_back("#define ANDROID_ENABLED\n"); -#endif - - for (int i = 0; i < custom_defines.size(); i++) { - strings.push_back(custom_defines[i].get_data()); - strings.push_back("\n"); - } - - for (int j = 0; j < conditional_count; j++) { - bool enable = (conditional_version.version & (1 << j)) > 0; - - if (enable) { - strings.push_back(conditional_defines[j]); - DEBUG_PRINT(conditional_defines[j]); + 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"); } } - - // keep them around during the function - CharString code_string; - CharString code_string2; - CharString code_globals; - - CustomCode *cc = NULL; - - if (conditional_version.code_version > 0) { - cc = custom_code_map.getptr(conditional_version.code_version); - - ERR_FAIL_COND_V(!cc, NULL); - v.code_version = cc->version; - } - - // program - - v.id = glCreateProgram(); - ERR_FAIL_COND_V(v.id == 0, NULL); - - if (cc) { - for (int i = 0; i < cc->custom_defines.size(); i++) { - strings.push_back(cc->custom_defines.write[i]); - DEBUG_PRINT("CD #" + itos(i) + ": " + String(cc->custom_defines[i].get_data())); + 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]); + 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 + + for (uint32_t i = 0; i < p_template.chunks.size(); i++) { + const StageTemplate::Chunk &chunk = p_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; } } +} - // vertex shader - - int string_base_size = strings.size(); - - strings.push_back(vertex_code0.get_data()); - - if (cc) { - code_globals = cc->vertex_globals.ascii(); - strings.push_back(code_globals.get_data()); - } - - strings.push_back(vertex_code1.get_data()); +static void _display_error_with_code(const String &p_error, const String &p_code) { + int line = 1; + Vector<String> lines = p_code.split("\n"); - if (cc) { - code_string = cc->vertex.ascii(); - strings.push_back(code_string.get_data()); + for (int j = 0; j < lines.size(); j++) { + print_line(itos(line) + ": " + lines[j]); + line++; } - strings.push_back(vertex_code2.get_data()); - -#ifdef DEBUG_SHADER - - DEBUG_PRINT("\nVertex Code:\n\n" + String(code_string.get_data())); - -#endif - - v.vert_id = glCreateShader(GL_VERTEX_SHADER); - glShaderSource(v.vert_id, strings.size(), &strings[0], NULL); - glCompileShader(v.vert_id); + ERR_PRINT(p_error); +} +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; - glGetShaderiv(v.vert_id, GL_COMPILE_STATUS, &status); - if (status == GL_FALSE) { - GLsizei iloglen; - glGetShaderiv(v.vert_id, GL_INFO_LOG_LENGTH, &iloglen); - - if (iloglen < 0) { - glDeleteShader(v.vert_id); - glDeleteProgram(v.id); - v.id = 0; + //vertex stage + { + StringBuilder builder; + _build_variant_code(builder, p_variant, p_version, stage_templates[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(); + glShaderSource(spec.vert_id, 1, &cstr, nullptr); + 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+) + } - ERR_PRINT("No OpenGL vertex shader compiler log. What the frick?"); - } else { - if (iloglen == 0) { - iloglen = 4096; // buggy driver (Adreno 220+) - } + char *ilogmem = (char *)Memory::alloc_static(iloglen + 1); + ilogmem[iloglen] = '\0'; + glGetShaderInfoLog(spec.vert_id, iloglen, &iloglen, ilogmem); - char *ilogmem = (char *)Memory::alloc_static(iloglen + 1); - ilogmem[iloglen] = '\0'; - glGetShaderInfoLog(v.vert_id, iloglen, &iloglen, ilogmem); + String err_string = name + ": Vertex shader compilation failed:\n"; - String err_string = get_shader_name() + ": Vertex shader compilation failed:\n"; + err_string += ilogmem; - err_string += ilogmem; + _display_error_with_code(err_string, builder_string); - _display_error_with_code(err_string, strings); + Memory::free_static(ilogmem); + glDeleteShader(spec.vert_id); + glDeleteProgram(spec.id); + spec.id = 0; + } - Memory::free_static(ilogmem); - glDeleteShader(v.vert_id); - glDeleteProgram(v.id); - v.id = 0; + ERR_FAIL(); } - - ERR_FAIL_V(NULL); - } - - strings.resize(string_base_size); - - // fragment shader - - strings.push_back(fragment_code0.get_data()); - - if (cc) { - code_globals = cc->fragment_globals.ascii(); - strings.push_back(code_globals.get_data()); - } - - strings.push_back(fragment_code1.get_data()); - - if (cc) { - code_string = cc->light.ascii(); - strings.push_back(code_string.get_data()); - } - - strings.push_back(fragment_code2.get_data()); - - if (cc) { - code_string2 = cc->fragment.ascii(); - strings.push_back(code_string2.get_data()); } - strings.push_back(fragment_code3.get_data()); - -#ifdef DEBUG_SHADER + //fragment stage + { + StringBuilder builder; + _build_variant_code(builder, p_variant, p_version, stage_templates[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(); + glShaderSource(spec.frag_id, 1, &cstr, nullptr); + 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+) + } - if (cc) { - DEBUG_PRINT("\nFragment Code:\n\n" + String(cc->fragment_globals)); - } - DEBUG_PRINT("\nFragment Code:\n\n" + String(code_string.get_data())); -#endif + char *ilogmem = (char *)Memory::alloc_static(iloglen + 1); + ilogmem[iloglen] = '\0'; + glGetShaderInfoLog(spec.frag_id, iloglen, &iloglen, ilogmem); - v.frag_id = glCreateShader(GL_FRAGMENT_SHADER); - glShaderSource(v.frag_id, strings.size(), &strings[0], NULL); - glCompileShader(v.frag_id); + String err_string = name + ": Fragment shader compilation failed:\n"; - glGetShaderiv(v.frag_id, GL_COMPILE_STATUS, &status); - if (status == GL_FALSE) { - GLsizei iloglen; - glGetShaderiv(v.frag_id, GL_INFO_LOG_LENGTH, &iloglen); + err_string += ilogmem; - if (iloglen < 0) { - glDeleteShader(v.frag_id); - glDeleteShader(v.vert_id); - glDeleteProgram(v.id); - v.id = 0; + _display_error_with_code(err_string, builder_string); - ERR_PRINT("No OpenGL fragment shader compiler log. What the frick?"); - } else { - if (iloglen == 0) { - iloglen = 4096; // buggy driver (Adreno 220+) + Memory::free_static(ilogmem); + glDeleteShader(spec.frag_id); + glDeleteProgram(spec.id); + spec.id = 0; } - char *ilogmem = (char *)Memory::alloc_static(iloglen + 1); - ilogmem[iloglen] = '\0'; - glGetShaderInfoLog(v.frag_id, iloglen, &iloglen, ilogmem); - - String err_string = get_shader_name() + ": Fragment shader compilation failed:\n"; - - err_string += ilogmem; - - _display_error_with_code(err_string, strings); - - Memory::free_static(ilogmem); - glDeleteShader(v.frag_id); - glDeleteShader(v.vert_id); - glDeleteProgram(v.id); - v.id = 0; + ERR_FAIL(); } - - ERR_FAIL_V(NULL); } - glAttachShader(v.id, v.frag_id); - glAttachShader(v.id, v.vert_id); + glAttachShader(spec.id, spec.frag_id); + glAttachShader(spec.id, spec.vert_id); - // bind the attribute locations. This has to be done before linking so that the - // linker doesn't assign some random indices - - for (int i = 0; i < attribute_pair_count; i++) { - glBindAttribLocation(v.id, attribute_pairs[i].index, attribute_pairs[i].name); - } + //for (int i = 0; i < attribute_pair_count; i++) { + // glBindAttribLocation(v.id, attribute_pairs[i].index, attribute_pairs[i].name); + //} - glLinkProgram(v.id); + glLinkProgram(spec.id); - glGetProgramiv(v.id, GL_LINK_STATUS, &status); + glGetProgramiv(spec.id, GL_LINK_STATUS, &status); if (status == GL_FALSE) { GLsizei iloglen; - glGetProgramiv(v.id, GL_INFO_LOG_LENGTH, &iloglen); + glGetProgramiv(spec.id, GL_INFO_LOG_LENGTH, &iloglen); if (iloglen < 0) { - glDeleteShader(v.frag_id); - glDeleteShader(v.vert_id); - glDeleteProgram(v.id); - v.id = 0; + glDeleteShader(spec.frag_id); + glDeleteShader(spec.vert_id); + glDeleteProgram(spec.id); + spec.id = 0; ERR_PRINT("No OpenGL program link log. What the frick?"); - ERR_FAIL_V(NULL); + ERR_FAIL(); } if (iloglen == 0) { @@ -409,33 +336,34 @@ ShaderGLES3::Version *ShaderGLES3::get_current_version() { char *ilogmem = (char *)Memory::alloc_static(iloglen + 1); ilogmem[iloglen] = '\0'; - glGetProgramInfoLog(v.id, iloglen, &iloglen, ilogmem); + glGetProgramInfoLog(spec.id, iloglen, &iloglen, ilogmem); - String err_string = get_shader_name() + ": Program linking failed:\n"; + String err_string = name + ": Program linking failed:\n"; err_string += ilogmem; - _display_error_with_code(err_string, strings); + _display_error_with_code(err_string, String()); Memory::free_static(ilogmem); - glDeleteShader(v.frag_id); - glDeleteShader(v.vert_id); - glDeleteProgram(v.id); - v.id = 0; + glDeleteShader(spec.frag_id); + glDeleteShader(spec.vert_id); + glDeleteProgram(spec.id); + spec.id = 0; - ERR_FAIL_V(NULL); + ERR_FAIL(); } // get uniform locations - glUseProgram(v.id); + glUseProgram(spec.id); + spec.uniform_location.resize(uniform_count); for (int i = 0; i < uniform_count; i++) { - v.uniform_location[i] = glGetUniformLocation(v.id, uniform_names[i]); + spec.uniform_location[i] = glGetUniformLocation(spec.id, uniform_names[i]); } for (int i = 0; i < texunit_pair_count; i++) { - GLint loc = glGetUniformLocation(v.id, texunit_pairs[i].name); + 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); @@ -445,672 +373,330 @@ ShaderGLES3::Version *ShaderGLES3::get_current_version() { } } - if (cc) { - // uniforms - for (int i = 0; i < cc->custom_uniforms.size(); i++) { - String native_uniform_name = _mkid(cc->custom_uniforms[i]); - GLint location = glGetUniformLocation(v.id, (native_uniform_name).ascii().get_data()); - v.custom_uniform_locations[cc->custom_uniforms[i]] = location; - } - - // textures - for (int i = 0; i < cc->texture_uniforms.size(); i++) { - String native_uniform_name = _mkid(cc->texture_uniforms[i]); - GLint location = glGetUniformLocation(v.id, (native_uniform_name).ascii().get_data()); - v.custom_uniform_locations[cc->texture_uniforms[i]] = location; - glUniform1i(location, i); + 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); } } - - glUseProgram(0); - v.ok = true; - - if (cc) { - cc->versions.insert(conditional_version.version); + // textures + for (int i = 0; i < p_version->texture_uniforms.size(); i++) { + String native_uniform_name = p_version->texture_uniforms[i]; + GLint location = glGetUniformLocation(spec.id, (native_uniform_name).ascii().get_data()); + glUniform1i(location, i + base_texture_index); } - return &v; + glUseProgram(0); + spec.ok = true; } -GLint ShaderGLES3::get_uniform_location(const String &p_name) const { - ERR_FAIL_COND_V(!version, -1); - return glGetUniformLocation(version->id, p_name.ascii().get_data()); -} +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_COND_V(!version, source_code); -void ShaderGLES3::setup( - const char **p_conditional_defines, - int p_conditional_count, - const char **p_uniform_names, - int p_uniform_count, - const AttributePair *p_attribute_pairs, - int p_attribute_count, - const TexUnitPair *p_texunit_pairs, - int p_texunit_pair_count, - const char *p_vertex_code, - const char *p_fragment_code, - int p_vertex_code_start, - int p_fragment_code_start) { - ERR_FAIL_COND(version); - - conditional_version.key = 0; - new_conditional_version.key = 0; - uniform_count = p_uniform_count; - conditional_count = p_conditional_count; - conditional_defines = p_conditional_defines; - uniform_names = p_uniform_names; - vertex_code = p_vertex_code; - fragment_code = p_fragment_code; - texunit_pairs = p_texunit_pairs; - texunit_pair_count = p_texunit_pair_count; - vertex_code_start = p_vertex_code_start; - fragment_code_start = p_fragment_code_start; - attribute_pairs = p_attribute_pairs; - attribute_pair_count = p_attribute_count; + source_code.versions.resize(variant_count); - { - String globals_tag = "\nVERTEX_SHADER_GLOBALS"; - String code_tag = "\nVERTEX_SHADER_CODE"; - String code = vertex_code; - int cpos = code.find(globals_tag); - if (cpos == -1) { - vertex_code0 = code.ascii(); - } else { - vertex_code0 = code.substr(0, cpos).ascii(); - code = code.substr(cpos + globals_tag.length(), code.length()); + for (int i = 0; i < source_code.versions.size(); i++) { + //vertex stage - cpos = code.find(code_tag); + { + StringBuilder builder; + _build_variant_code(builder, i, version, stage_templates[STAGE_TYPE_VERTEX], specialization_default_mask); - if (cpos == -1) { - vertex_code1 = code.ascii(); - } else { - vertex_code1 = code.substr(0, cpos).ascii(); - vertex_code2 = code.substr(cpos + code_tag.length(), code.length()).ascii(); - } - } - } - - { - String globals_tag = "\nFRAGMENT_SHADER_GLOBALS"; - String code_tag = "\nFRAGMENT_SHADER_CODE"; - String light_code_tag = "\nLIGHT_SHADER_CODE"; - String code = fragment_code; - int cpos = code.find(globals_tag); - if (cpos == -1) { - fragment_code0 = code.ascii(); - } else { - fragment_code0 = code.substr(0, cpos).ascii(); - code = code.substr(cpos + globals_tag.length(), code.length()); + RS::ShaderNativeSourceCode::Version::Stage stage; + stage.name = "vertex"; + stage.code = builder.as_string(); - cpos = code.find(light_code_tag); + source_code.versions.write[i].stages.push_back(stage); + } - String code2; + //fragment stage + { + StringBuilder builder; + _build_variant_code(builder, i, version, stage_templates[STAGE_TYPE_FRAGMENT], specialization_default_mask); - if (cpos != -1) { - fragment_code1 = code.substr(0, cpos).ascii(); - code2 = code.substr(cpos + light_code_tag.length(), code.length()); - } else { - code2 = code; - } + RS::ShaderNativeSourceCode::Version::Stage stage; + stage.name = "fragment"; + stage.code = builder.as_string(); - cpos = code2.find(code_tag); - if (cpos == -1) { - fragment_code2 = code2.ascii(); - } else { - fragment_code2 = code2.substr(0, cpos).ascii(); - fragment_code3 = code2.substr(cpos + code_tag.length(), code2.length()).ascii(); - } + source_code.versions.write[i].stages.push_back(stage); } } - glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &max_image_units); - - valid = true; + return source_code; } -void ShaderGLES3::finish() { - const VersionKey *V = NULL; +String ShaderGLES3::_version_get_sha1(Version *p_version) const { + StringBuilder hash_build; - while ((V = version_map.next(V))) { - Version &v = version_map[*V]; - glDeleteShader(v.vert_id); - glDeleteShader(v.frag_id); - glDeleteProgram(v.id); + 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()); - if (v.uniform_location) - memdelete_arr(v.uniform_location); + Vector<StringName> code_sections; + for (const KeyValue<StringName, CharString> &E : p_version->code_sections) { + code_sections.push_back(E.key); } -} - -void ShaderGLES3::clear_caches() { - const VersionKey *V = NULL; + code_sections.sort_custom<StringName::AlphCompare>(); - while ((V = version_map.next(V))) { - Version &v = version_map[*V]; - glDeleteShader(v.vert_id); - glDeleteShader(v.frag_id); - glDeleteProgram(v.id); - memdelete_arr(v.uniform_location); + 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()); } - - version_map.clear(); - - custom_code_map.clear(); - version = NULL; - last_custom_code = 1; - uniforms_dirty = true; -} - -uint32_t ShaderGLES3::create_custom_shader() { - custom_code_map[last_custom_code] = CustomCode(); - custom_code_map[last_custom_code].version = 1; - return last_custom_code++; -} - -void ShaderGLES3::set_custom_shader_code(uint32_t p_code_id, - const String &p_vertex, - const String &p_vertex_globals, - const String &p_fragment, - const String &p_light, - const String &p_fragment_globals, - const Vector<StringName> &p_uniforms, - const Vector<StringName> &p_texture_uniforms, - const Vector<CharString> &p_custom_defines) { - CustomCode *cc = custom_code_map.getptr(p_code_id); - ERR_FAIL_COND(!cc); - - cc->vertex = p_vertex; - cc->vertex_globals = p_vertex_globals; - cc->fragment = p_fragment; - cc->fragment_globals = p_fragment_globals; - cc->light = p_light; - cc->custom_uniforms = p_uniforms; - cc->custom_defines = p_custom_defines; - cc->texture_uniforms = p_texture_uniforms; - cc->version++; -} - -void ShaderGLES3::set_custom_shader(uint32_t p_code_id) { - new_conditional_version.code_version = p_code_id; -} - -void ShaderGLES3::free_custom_shader(uint32_t p_code_id) { - ERR_FAIL_COND(!custom_code_map.has(p_code_id)); - if (conditional_version.code_version == p_code_id) { - conditional_version.code_version = 0; //do not keep using a version that is going away - unbind(); + 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()); } - VersionKey key; - key.code_version = p_code_id; - for (Set<uint32_t>::Element *E = custom_code_map[p_code_id].versions.front(); E; E = E->next()) { - key.version = E->get(); - ERR_CONTINUE(!version_map.has(key)); - Version &v = version_map[key]; - - glDeleteShader(v.vert_id); - glDeleteShader(v.frag_id); - glDeleteProgram(v.id); - memdelete_arr(v.uniform_location); - v.id = 0; - - version_map.erase(key); - } - - custom_code_map.erase(p_code_id); + return hash_build.as_string().sha1_text(); } -void ShaderGLES3::use_material(void *p_material) { - RasterizerStorageGLES3::Material *material = (RasterizerStorageGLES3::Material *)p_material; +//static const char *shader_file_header = "GLSC"; +//static const uint32_t cache_file_version = 2; - if (!material) { - return; - } +bool ShaderGLES3::_load_from_cache(Version *p_version) { +#if 0 + String sha1 = _version_get_sha1(p_version); + String path = shader_cache_dir.plus_file(name).plus_file(base_sha256).plus_file(sha1) + ".cache"; - if (!material->shader) { - return; + FileAccessRef f = FileAccess::open(path, FileAccess::READ); + if (!f) { + return false; } - Version *v = version_map.getptr(conditional_version); - - // bind uniforms - for (Map<StringName, ShaderLanguage::ShaderNode::Uniform>::Element *E = material->shader->uniforms.front(); E; E = E->next()) { - if (E->get().texture_order >= 0) - continue; // this is a texture, doesn't go here - - Map<StringName, GLint>::Element *L = v->custom_uniform_locations.find(E->key()); - if (!L || L->get() < 0) - continue; //uniform not valid - - GLuint location = L->get(); - - Map<StringName, Variant>::Element *V = material->params.find(E->key()); - - if (V) { - switch (E->get().type) { - case ShaderLanguage::TYPE_BOOL: { - bool boolean = V->get(); - glUniform1i(location, boolean ? 1 : 0); - } break; - - case ShaderLanguage::TYPE_BVEC2: { - int flags = V->get(); - glUniform2i(location, (flags & 1) ? 1 : 0, (flags & 2) ? 1 : 0); - } break; - - case ShaderLanguage::TYPE_BVEC3: { - int flags = V->get(); - glUniform3i(location, (flags & 1) ? 1 : 0, (flags & 2) ? 1 : 0, (flags & 4) ? 1 : 0); - - } break; - - case ShaderLanguage::TYPE_BVEC4: { - int flags = V->get(); - glUniform4i(location, (flags & 1) ? 1 : 0, (flags & 2) ? 1 : 0, (flags & 4) ? 1 : 0, (flags & 8) ? 1 : 0); - - } break; - - case ShaderLanguage::TYPE_INT: - case ShaderLanguage::TYPE_UINT: { - int value = V->get(); - glUniform1i(location, value); - } break; - - case ShaderLanguage::TYPE_IVEC2: - case ShaderLanguage::TYPE_UVEC2: { - Array r = V->get(); - const int count = 2; - if (r.size() == count) { - int values[count]; - for (int i = 0; i < count; i++) { - values[i] = r[i]; - } - glUniform2i(location, values[0], values[1]); - } - - } break; - - case ShaderLanguage::TYPE_IVEC3: - case ShaderLanguage::TYPE_UVEC3: { - Array r = V->get(); - const int count = 3; - if (r.size() == count) { - int values[count]; - for (int i = 0; i < count; i++) { - values[i] = r[i]; - } - glUniform3i(location, values[0], values[1], values[2]); - } - - } break; - - case ShaderLanguage::TYPE_IVEC4: - case ShaderLanguage::TYPE_UVEC4: { - Array r = V->get(); - const int count = 4; - if (r.size() == count) { - int values[count]; - for (int i = 0; i < count; i++) { - values[i] = r[i]; - } - glUniform4i(location, values[0], values[1], values[2], values[3]); - } - - } break; - - case ShaderLanguage::TYPE_FLOAT: { - float value = V->get(); - glUniform1f(location, value); - - } break; - - case ShaderLanguage::TYPE_VEC2: { - Vector2 value = V->get(); - glUniform2f(location, value.x, value.y); - } break; - - case ShaderLanguage::TYPE_VEC3: { - Vector3 value = V->get(); - glUniform3f(location, value.x, value.y, value.z); - } break; - - case ShaderLanguage::TYPE_VEC4: { - if (V->get().get_type() == Variant::COLOR) { - Color value = V->get(); - glUniform4f(location, value.r, value.g, value.b, value.a); - } else if (V->get().get_type() == Variant::QUATERNION) { - Quaternion value = V->get(); - glUniform4f(location, value.x, value.y, value.z, value.w); - } else { - Plane value = V->get(); - glUniform4f(location, value.normal.x, value.normal.y, value.normal.z, value.d); - } - - } break; - - case ShaderLanguage::TYPE_MAT2: { - Transform2D tr = V->get(); - GLfloat matrix[4] = { - /* build a 16x16 matrix */ - (GLfloat)tr.elements[0][0], - (GLfloat)tr.elements[0][1], - (GLfloat)tr.elements[1][0], - (GLfloat)tr.elements[1][1], - }; - glUniformMatrix2fv(location, 1, GL_FALSE, matrix); - - } break; - - case ShaderLanguage::TYPE_MAT3: { - Basis val = V->get(); - - GLfloat mat[9] = { - (GLfloat)val.elements[0][0], - (GLfloat)val.elements[1][0], - (GLfloat)val.elements[2][0], - (GLfloat)val.elements[0][1], - (GLfloat)val.elements[1][1], - (GLfloat)val.elements[2][1], - (GLfloat)val.elements[0][2], - (GLfloat)val.elements[1][2], - (GLfloat)val.elements[2][2], - }; - - glUniformMatrix3fv(location, 1, GL_FALSE, mat); - - } break; - - case ShaderLanguage::TYPE_MAT4: { - Transform2D tr = V->get(); - GLfloat matrix[16] = { /* build a 16x16 matrix */ - (GLfloat)tr.elements[0][0], - (GLfloat)tr.elements[0][1], - (GLfloat)0, - (GLfloat)0, - (GLfloat)tr.elements[1][0], - (GLfloat)tr.elements[1][1], - (GLfloat)0, - (GLfloat)0, - (GLfloat)0, - (GLfloat)0, - (GLfloat)1, - (GLfloat)0, - (GLfloat)tr.elements[2][0], - (GLfloat)tr.elements[2][1], - (GLfloat)0, - (GLfloat)1 - }; - - glUniformMatrix4fv(location, 1, GL_FALSE, matrix); - - } break; - - default: { - ERR_PRINT("ShaderNode type missing, bug?"); - } break; - } - } else if (E->get().default_value.size()) { - const Vector<ShaderLanguage::ConstantNode::Value> &values = E->get().default_value; - switch (E->get().type) { - case ShaderLanguage::TYPE_BOOL: { - glUniform1i(location, values[0].boolean); - } break; - - case ShaderLanguage::TYPE_BVEC2: { - glUniform2i(location, values[0].boolean, values[1].boolean); - } break; - - case ShaderLanguage::TYPE_BVEC3: { - glUniform3i(location, values[0].boolean, values[1].boolean, values[2].boolean); - } break; - - case ShaderLanguage::TYPE_BVEC4: { - glUniform4i(location, values[0].boolean, values[1].boolean, values[2].boolean, values[3].boolean); - } break; - - case ShaderLanguage::TYPE_INT: { - glUniform1i(location, values[0].sint); - } break; - - case ShaderLanguage::TYPE_IVEC2: { - glUniform2i(location, values[0].sint, values[1].sint); - } break; - - case ShaderLanguage::TYPE_IVEC3: { - glUniform3i(location, values[0].sint, values[1].sint, values[2].sint); - } break; - - case ShaderLanguage::TYPE_IVEC4: { - glUniform4i(location, values[0].sint, values[1].sint, values[2].sint, values[3].sint); - } break; - - case ShaderLanguage::TYPE_UINT: { - glUniform1i(location, values[0].uint); - } break; - - case ShaderLanguage::TYPE_UVEC2: { - glUniform2i(location, values[0].uint, values[1].uint); - } break; - - case ShaderLanguage::TYPE_UVEC3: { - glUniform3i(location, values[0].uint, values[1].uint, values[2].uint); - } break; - - case ShaderLanguage::TYPE_UVEC4: { - glUniform4i(location, values[0].uint, values[1].uint, values[2].uint, values[3].uint); - } break; - - case ShaderLanguage::TYPE_FLOAT: { - glUniform1f(location, values[0].real); - } break; - - case ShaderLanguage::TYPE_VEC2: { - glUniform2f(location, values[0].real, values[1].real); - } break; + char header[5] = { 0, 0, 0, 0, 0 }; + f->get_buffer((uint8_t *)header, 4); + ERR_FAIL_COND_V(header != String(shader_file_header), false); - case ShaderLanguage::TYPE_VEC3: { - glUniform3f(location, values[0].real, values[1].real, values[2].real); - } break; - - case ShaderLanguage::TYPE_VEC4: { - glUniform4f(location, values[0].real, values[1].real, values[2].real, values[3].real); - } break; - - case ShaderLanguage::TYPE_MAT2: { - GLfloat mat[4]; - - for (int i = 0; i < 4; i++) { - mat[i] = values[i].real; - } - - glUniformMatrix2fv(location, 1, GL_FALSE, mat); - } break; - - case ShaderLanguage::TYPE_MAT3: { - GLfloat mat[9]; - - for (int i = 0; i < 9; i++) { - mat[i] = values[i].real; - } - - glUniformMatrix3fv(location, 1, GL_FALSE, mat); - - } break; - - case ShaderLanguage::TYPE_MAT4: { - GLfloat mat[16]; - - for (int i = 0; i < 16; i++) { - mat[i] = values[i].real; - } - - glUniformMatrix4fv(location, 1, GL_FALSE, mat); + uint32_t file_version = f->get_32(); + if (file_version != cache_file_version) { + return false; // wrong version + } - } break; + uint32_t variant_count = f->get_32(); - case ShaderLanguage::TYPE_SAMPLER2D: { - } break; + ERR_FAIL_COND_V(variant_count != (uint32_t)variant_count, false); //should not happen but check - /* - case ShaderLanguage::TYPE_SAMPLEREXT: { - } break; -*/ - case ShaderLanguage::TYPE_ISAMPLER2D: { - } break; + for (uint32_t i = 0; i < variant_count; i++) { + uint32_t variant_size = f->get_32(); + ERR_FAIL_COND_V(variant_size == 0 && variants_enabled[i], false); + if (!variants_enabled[i]) { + continue; + } + Vector<uint8_t> variant_bytes; + variant_bytes.resize(variant_size); - case ShaderLanguage::TYPE_USAMPLER2D: { - } break; + uint32_t br = f->get_buffer(variant_bytes.ptrw(), variant_size); - case ShaderLanguage::TYPE_SAMPLERCUBE: { - } break; + ERR_FAIL_COND_V(br != variant_size, false); - case ShaderLanguage::TYPE_SAMPLER2DARRAY: - case ShaderLanguage::TYPE_ISAMPLER2DARRAY: - case ShaderLanguage::TYPE_USAMPLER2DARRAY: - case ShaderLanguage::TYPE_SAMPLER3D: - case ShaderLanguage::TYPE_ISAMPLER3D: - case ShaderLanguage::TYPE_USAMPLER3D: { - // Not implemented in OpenGL - } break; + p_version->variant_data[i] = variant_bytes; + } - case ShaderLanguage::TYPE_VOID: { - // Nothing to do? - } break; - default: { - ERR_PRINT("ShaderNode type missing, bug?"); - } break; + for (uint32_t i = 0; i < variant_count; i++) { + if (!variants_enabled[i]) { + MutexLock lock(variant_set_mutex); + p_version->variants[i] = RID(); + continue; + } + RID shader = GLES3::get_singleton()->shader_create_from_bytecode(p_version->variant_data[i]); + if (shader.is_null()) { + for (uint32_t j = 0; j < i; j++) { + GLES3::get_singleton()->free(p_version->variants[i]); } - } else { //zero + ERR_FAIL_COND_V(shader.is_null(), false); + } + { + MutexLock lock(variant_set_mutex); + p_version->variants[i] = shader; + } + } - switch (E->get().type) { - case ShaderLanguage::TYPE_BOOL: { - glUniform1i(location, GL_FALSE); - } break; + memdelete_arr(p_version->variant_data); //clear stages + p_version->variant_data = nullptr; + p_version->valid = true; + return true; +#endif + return false; +} - case ShaderLanguage::TYPE_BVEC2: { - glUniform2i(location, GL_FALSE, GL_FALSE); - } break; +void ShaderGLES3::_save_to_cache(Version *p_version) { +#if 0 + String sha1 = _version_get_sha1(p_version); + String path = shader_cache_dir.plus_file(name).plus_file(base_sha256).plus_file(sha1) + ".cache"; - case ShaderLanguage::TYPE_BVEC3: { - glUniform3i(location, GL_FALSE, GL_FALSE, GL_FALSE); - } break; + FileAccessRef f = FileAccess::open(path, FileAccess::WRITE); + ERR_FAIL_COND(!f); + f->store_buffer((const uint8_t *)shader_file_header, 4); + f->store_32(cache_file_version); //file version + uint32_t variant_count = variant_count; + f->store_32(variant_count); //variant count - case ShaderLanguage::TYPE_BVEC4: { - glUniform4i(location, GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); - } break; + for (uint32_t i = 0; i < variant_count; i++) { + f->store_32(p_version->variant_data[i].size()); //stage count + f->store_buffer(p_version->variant_data[i].ptr(), p_version->variant_data[i].size()); + } - case ShaderLanguage::TYPE_INT: { - glUniform1i(location, 0); - } break; + f->close(); +#endif +} - case ShaderLanguage::TYPE_IVEC2: { - glUniform2i(location, 0, 0); - } break; +void ShaderGLES3::_clear_version(Version *p_version) { + // Variants not compiled yet, just return + if (p_version->variants.size() == 0) { + return; + } - case ShaderLanguage::TYPE_IVEC3: { - glUniform3i(location, 0, 0, 0); - } break; + for (int i = 0; i < variant_count; i++) { + for (OAHashMap<uint64_t, Version::Specialization>::Iterator it = p_version->variants[i].iter(); it.valid; it = p_version->variants[i].next_iter(it)) { + if (it.valid) { + glDeleteShader(it.value->vert_id); + glDeleteShader(it.value->frag_id); + glDeleteProgram(it.value->id); + } + } + } - case ShaderLanguage::TYPE_IVEC4: { - glUniform4i(location, 0, 0, 0, 0); - } break; + p_version->variants.clear(); +} - case ShaderLanguage::TYPE_UINT: { - glUniform1i(location, 0); - } break; +void ShaderGLES3::_initialize_version(Version *p_version) { + ERR_FAIL_COND(p_version->variants.size() > 0); + p_version->variants.reserve(variant_count); + for (int i = 0; i < variant_count; i++) { + OAHashMap<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); + } +} - case ShaderLanguage::TYPE_UVEC2: { - glUniform2i(location, 0, 0); - } break; +void ShaderGLES3::version_set_code(RID p_version, const Map<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 Vector<StringName> &p_texture_uniforms, bool p_initialize) { + Version *version = version_owner.get_or_null(p_version); + ERR_FAIL_COND(!version); - case ShaderLanguage::TYPE_UVEC3: { - glUniform3i(location, 0, 0, 0); - } break; + _clear_version(version); //clear if existing - case ShaderLanguage::TYPE_UVEC4: { - glUniform4i(location, 0, 0, 0, 0); - } break; + 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(); + } - case ShaderLanguage::TYPE_FLOAT: { - glUniform1f(location, 0); - } break; + version->custom_defines.clear(); + for (int i = 0; i < p_custom_defines.size(); i++) { + version->custom_defines.push_back(p_custom_defines[i].utf8()); + } - case ShaderLanguage::TYPE_VEC2: { - glUniform2f(location, 0, 0); - } break; + if (p_initialize) { + _initialize_version(version); + } +} - case ShaderLanguage::TYPE_VEC3: { - glUniform3f(location, 0, 0, 0); - } break; +bool ShaderGLES3::version_is_valid(RID p_version) { + Version *version = version_owner.get_or_null(p_version); + return version != nullptr; +} - case ShaderLanguage::TYPE_VEC4: { - glUniform4f(location, 0, 0, 0, 0); - } break; +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; + } - case ShaderLanguage::TYPE_MAT2: { - GLfloat mat[4] = { 0, 0, 0, 0 }; + return true; +} - glUniformMatrix2fv(location, 1, GL_FALSE, mat); - } break; +bool ShaderGLES3::shader_cache_cleanup_on_start = false; - case ShaderLanguage::TYPE_MAT3: { - GLfloat mat[9] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +ShaderGLES3::ShaderGLES3() { +} - glUniformMatrix3fv(location, 1, GL_FALSE, mat); +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; - } break; + _init(); - case ShaderLanguage::TYPE_MAT4: { - GLfloat mat[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + if (shader_cache_dir != String()) { + StringBuilder hash_build; - glUniformMatrix4fv(location, 1, GL_FALSE, mat); + 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]); + } - } break; + base_sha256 = hash_build.as_string().sha256_text(); - case ShaderLanguage::TYPE_SAMPLER2D: { - } break; + DirAccessRef d = DirAccess::open(shader_cache_dir); + ERR_FAIL_COND(!d); + if (d->change_dir(name) != OK) { + Error err = d->make_dir(name); + ERR_FAIL_COND(err != OK); + d->change_dir(name); + } - /* - case ShaderLanguage::TYPE_SAMPLEREXT: { - } break; -*/ + //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; - case ShaderLanguage::TYPE_ISAMPLER2D: { - } break; + print_verbose("Shader '" + name + "' SHA256: " + base_sha256); + } - case ShaderLanguage::TYPE_USAMPLER2D: { - } break; + glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &max_image_units); +} - case ShaderLanguage::TYPE_SAMPLERCUBE: { - } break; +void ShaderGLES3::set_shader_cache_dir(const String &p_dir) { + shader_cache_dir = p_dir; +} - case ShaderLanguage::TYPE_SAMPLER2DARRAY: - case ShaderLanguage::TYPE_ISAMPLER2DARRAY: - case ShaderLanguage::TYPE_USAMPLER2DARRAY: - case ShaderLanguage::TYPE_SAMPLER3D: - case ShaderLanguage::TYPE_ISAMPLER3D: - case ShaderLanguage::TYPE_USAMPLER3D: { - // Not implemented in OpenGL - } break; +void ShaderGLES3::set_shader_cache_save_compressed(bool p_enable) { + shader_cache_save_compressed = p_enable; +} - case ShaderLanguage::TYPE_VOID: { - // Nothing to do? - } break; - default: { - ERR_PRINT("ShaderNode type missing, bug?"); - } break; - } - } - } +void ShaderGLES3::set_shader_cache_save_compressed_zstd(bool p_enable) { + shader_cache_save_compressed_zstd = p_enable; } -ShaderGLES3::ShaderGLES3() { - version = NULL; - last_custom_code = 1; - uniforms_dirty = true; +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() { - finish(); + List<RID> remaining; + version_owner.get_owned_list(&remaining); + if (remaining.size()) { + ERR_PRINT(itos(remaining.size()) + " shaders of type " + name + " were never freed"); + while (remaining.size()) { + version_free(remaining.front()->get()); + remaining.pop_front(); + } + } } - -#endif // GLES3_BACKEND_ENABLED +#endif diff --git a/drivers/gles3/shader_gles3.h b/drivers/gles3/shader_gles3.h index 3b9177b4eb..bc593fb187 100644 --- a/drivers/gles3/shader_gles3.h +++ b/drivers/gles3/shader_gles3.h @@ -31,8 +31,16 @@ #ifndef SHADER_OPENGL_H #define SHADER_OPENGL_H -#include "drivers/gles3/rasterizer_platforms.h" -#ifdef GLES3_BACKEND_ENABLED +#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/map.h" +#include "core/templates/rid_owner.h" +#include "core/variant/variant.h" +#include "servers/rendering_server.h" + +#ifdef GLES3_ENABLED // This must come first to avoid windows.h mess #include "platform_config.h" @@ -42,236 +50,200 @@ #include OPENGL_INCLUDE_H #endif -#include "core/math/camera_matrix.h" -#include "core/templates/hash_map.h" -#include "core/templates/map.h" -#include "core/templates/pair.h" -#include "core/variant/variant.h" -#include "servers/rendering/shader_language.h" - #include <stdio.h> - -class RasterizerStorageGLES3; +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ class ShaderGLES3 { protected: - struct Enum { - uint64_t mask; - uint64_t shift; - const char *defines[16]; - }; - - struct EnumValue { - uint64_t set_mask; - uint64_t clear_mask; - }; - - struct AttributePair { + struct TexUnitPair { const char *name; int index; }; - struct UniformPair { + struct UBOPair { const char *name; - Variant::Type type_hint; + int index; }; - struct TexUnitPair { + struct Specialization { const char *name; - int index; + bool default_value = false; }; - bool uniforms_dirty; - private: - bool valid = false; - - //@TODO Optimize to a fixed set of shader pools and use a LRU - int uniform_count; - int texunit_pair_count; - int conditional_count; - int vertex_code_start; - int fragment_code_start; - int attribute_pair_count; - - struct CustomCode { - String vertex; - String vertex_globals; - String fragment; - String fragment_globals; - String light; - uint32_t version; - Vector<StringName> texture_uniforms; - Vector<StringName> custom_uniforms; - Vector<CharString> custom_defines; - Set<uint32_t> versions; - }; + //versions + CharString general_defines; + // A version is a high-level construct which is a combination of built-in and user-defined shader code + // Variants use #idefs to toggle behaviour on and off to change behaviour of the shader + // Specializations use #ifdefs to toggle behaviour 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 behaviour when different data may be used (e.g. using a samplerArray vs a sampler) struct Version { - GLuint id; - GLuint vert_id; - GLuint frag_id; - GLint *uniform_location; - Vector<GLint> texture_uniform_locations; - Map<StringName, GLint> custom_uniform_locations; - uint32_t code_version; - bool ok; - Version() { - id = 0; - vert_id = 0; - frag_id = 0; - uniform_location = NULL; - code_version = 0; - ok = false; - } - }; - - Version *version; + Vector<StringName> texture_uniforms; + CharString uniforms; + CharString vertex_globals; + CharString fragment_globals; + Map<StringName, CharString> code_sections; + Vector<CharString> custom_defines; - union VersionKey { - struct { - uint32_t version; - uint32_t code_version; + struct Specialization { + GLuint id; + GLuint vert_id; + GLuint frag_id; + LocalVector<GLint> uniform_location; + LocalVector<GLint> texture_uniform_locations; + Map<StringName, GLint> custom_uniform_locations; + bool build_queued = false; + bool ok = false; + Specialization() { + id = 0; + vert_id = 0; + frag_id = 0; + } }; - uint64_t key; - bool operator==(const VersionKey &p_key) const { return key == p_key.key; } - bool operator<(const VersionKey &p_key) const { return key < p_key.key; } - }; - struct VersionKeyHash { - static _FORCE_INLINE_ uint32_t hash(const VersionKey &p_key) { return HashMapHasherDefault::hash(p_key.key); } + LocalVector<OAHashMap<uint64_t, Specialization>> variants; }; - //this should use a way more cachefriendly version.. - HashMap<VersionKey, Version, VersionKeyHash> version_map; - - HashMap<uint32_t, CustomCode> custom_code_map; - uint32_t last_custom_code; + Mutex variant_set_mutex; - VersionKey conditional_version; - VersionKey new_conditional_version; + void _compile_specialization(Version::Specialization &spec, uint32_t p_variant, Version *p_version, uint64_t p_specialization); - virtual String get_shader_name() const = 0; + void _clear_version(Version *p_version); + void _initialize_version(Version *p_version); - const char **conditional_defines; - const char **uniform_names; - const AttributePair *attribute_pairs; - const TexUnitPair *texunit_pairs; - const char *vertex_code; - const char *fragment_code; - CharString fragment_code0; - CharString fragment_code1; - CharString fragment_code2; - CharString fragment_code3; + RID_Owner<Version> version_owner; - CharString vertex_code0; - CharString vertex_code1; - CharString vertex_code2; + struct StageTemplate { + struct Chunk { + enum Type { + TYPE_MATERIAL_UNIFORMS, + TYPE_VERTEX_GLOBALS, + TYPE_FRAGMENT_GLOBALS, + TYPE_CODE, + TYPE_TEXT + }; - Vector<CharString> custom_defines; + Type type; + StringName code; + CharString text; + }; + LocalVector<Chunk> chunks; + }; - Version *get_current_version(); + String name; - static ShaderGLES3 *active; + String base_sha256; - int max_image_units; + 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; - Map<StringName, Pair<ShaderLanguage::DataType, Vector<ShaderLanguage::ConstantNode::Value>>> uniform_values; + GLint max_image_units; -protected: - _FORCE_INLINE_ int _get_uniform(int p_which) const; - _FORCE_INLINE_ void _set_conditional(int p_which, bool p_value); - - void setup(const char **p_conditional_defines, - int p_conditional_count, - const char **p_uniform_names, - int p_uniform_count, - const AttributePair *p_attribute_pairs, - int p_attribute_count, - const TexUnitPair *p_texunit_pairs, - int p_texunit_pair_count, - const char *p_vertex_code, - const char *p_fragment_code, - int p_vertex_code_start, - int p_fragment_code_start); + enum StageType { + STAGE_TYPE_VERTEX, + STAGE_TYPE_FRAGMENT, + STAGE_TYPE_MAX, + }; - ShaderGLES3(); + StageTemplate stage_templates[STAGE_TYPE_MAX]; -public: - enum { - CUSTOM_SHADER_DISABLED = 0 - }; + void _build_variant_code(StringBuilder &p_builder, uint32_t p_variant, const Version *p_version, const StageTemplate &p_template, uint64_t p_specialization); - GLint get_uniform_location(const String &p_name) const; - GLint get_uniform_location(int p_index) const; + void _add_stage(const char *p_code, StageType p_stage_type); - static _FORCE_INLINE_ ShaderGLES3 *get_active() { return active; } - bool bind(); - void unbind(); + String _version_get_sha1(Version *p_version) const; + bool _load_from_cache(Version *p_version); + void _save_to_cache(Version *p_version); - inline GLuint get_program() const { return version ? version->id : 0; } + const char **uniform_names = nullptr; + int uniform_count = 0; + const UBOPair *ubo_pairs = nullptr; + int ubo_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; - void clear_caches(); + int base_texture_index = 0; + Version::Specialization *current_shader = nullptr; - uint32_t create_custom_shader(); - void set_custom_shader_code(uint32_t p_code_id, - const String &p_vertex, - const String &p_vertex_globals, - const String &p_fragment, - const String &p_light, - const String &p_fragment_globals, - const Vector<StringName> &p_uniforms, - const Vector<StringName> &p_texture_uniforms, - const Vector<CharString> &p_custom_defines); +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_texture_count, const TexUnitPair *p_tex_units, int p_specialization_count, const Specialization *p_specializations, int p_variant_count, const char **p_variants); - void set_custom_shader(uint32_t p_code_id); - void free_custom_shader(uint32_t p_code_id); + _FORCE_INLINE_ void _version_bind_shader(RID p_version, int p_variant, uint64_t p_specialization) { + ERR_FAIL_INDEX(p_variant, variant_count); - uint32_t get_version_key() const { return conditional_version.version; } + Version *version = version_owner.get_or_null(p_version); + ERR_FAIL_COND(!version); - // this void* is actually a RasterizerStorageGLES3::Material, but C++ doesn't - // like forward declared nested classes. - void use_material(void *p_material); + if (version->variants.size() == 0) { + _initialize_version(version); //may lack initialization + } - _FORCE_INLINE_ uint32_t get_version() const { return new_conditional_version.version; } - _FORCE_INLINE_ bool is_version_valid() const { return version && version->ok; } + Version::Specialization *spec = version->variants[p_variant].lookup_ptr(p_specialization); + if (!spec) { + if (false) { + // Queue load this specialization and use defaults in the meantime (TODO) + + spec = version->variants[p_variant].lookup_ptr(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].lookup_ptr(p_specialization); + } + } else if (spec->build_queued) { + // Still queued, wait + spec = version->variants[p_variant].lookup_ptr(specialization_default_mask); + } - virtual void init() = 0; - void finish(); + ERR_FAIL_COND(!spec); // Should never happen + ERR_FAIL_COND(!spec->ok); // Should never happen - void add_custom_define(const String &p_define) { - custom_defines.push_back(p_define.utf8()); + glUseProgram(spec->id); + current_shader = spec; } - void get_custom_defines(Vector<String> *p_defines) { - for (int i = 0; i < custom_defines.size(); i++) { - p_defines->push_back(custom_defines[i].get_data()); - } + _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_COND_V(!version, -1); + return version->variants[p_variant].lookup_ptr(p_specialization)->uniform_location[p_which]; } - void remove_custom_define(const String &p_define) { - custom_defines.erase(p_define.utf8()); - } + virtual void _init() = 0; - virtual ~ShaderGLES3(); -}; +public: + RID version_create(); + + void version_set_code(RID p_version, const Map<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 Vector<StringName> &p_texture_uniforms, bool p_initialize = false); + + bool version_is_valid(RID p_version); -// called a lot, made inline + bool version_free(RID p_version); -int ShaderGLES3::_get_uniform(int p_which) const { - ERR_FAIL_INDEX_V(p_which, uniform_count, -1); - ERR_FAIL_COND_V(!version, -1); - return version->uniform_location[p_which]; -} + 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); -void ShaderGLES3::_set_conditional(int p_which, bool p_value) { - ERR_FAIL_INDEX(p_which, conditional_count); - if (p_value) - new_conditional_version.version |= (1 << p_which); - else - new_conditional_version.version &= ~(1 << p_which); -} + RS::ShaderNativeSourceCode version_get_native_source_code(RID p_version); -#endif // GLES3_BACKEND_ENABLED + void initialize(const String &p_general_defines = "", int p_base_texture_index = 0); + virtual ~ShaderGLES3(); +}; #endif // SHADER_OPENGL_H +#endif diff --git a/drivers/gles3/shaders/SCsub b/drivers/gles3/shaders/SCsub index 47d56b9947..2f56b77bdc 100644 --- a/drivers/gles3/shaders/SCsub +++ b/drivers/gles3/shaders/SCsub @@ -3,12 +3,5 @@ Import("env") if "GLES3_GLSL" in env["BUILDERS"]: - env.GLES3_GLSL("copy.glsl") env.GLES3_GLSL("canvas.glsl") - env.GLES3_GLSL("canvas_shadow.glsl") - env.GLES3_GLSL("scene.glsl") - env.GLES3_GLSL("cubemap_filter.glsl") - env.GLES3_GLSL("cube_to_dp.glsl") - env.GLES3_GLSL("effect_blur.glsl") - env.GLES3_GLSL("tonemap.glsl") - env.GLES3_GLSL("lens_distorted.glsl") + env.GLES3_GLSL("copy.glsl") diff --git a/drivers/gles3/shaders/canvas.glsl b/drivers/gles3/shaders/canvas.glsl index f2b141252a..a18c451858 100644 --- a/drivers/gles3/shaders/canvas.glsl +++ b/drivers/gles3/shaders/canvas.glsl @@ -1,665 +1,753 @@ /* clang-format off */ -[vertex] +#[modes] -#ifdef USE_GLES_OVER_GL -#define lowp -#define mediump -#define highp -#else -precision highp float; -precision highp int; -#endif +mode_quad = +mode_ninepatch = #define USE_NINEPATCH +mode_primitive = #define USE_PRIMITIVE +mode_attributes = #define USE_ATTRIBUTES -uniform highp mat4 projection_matrix; -/* clang-format on */ +#[specializations] -uniform highp mat4 modelview_matrix; -uniform highp mat4 extra_matrix; -layout(location = 0) in highp vec2 vertex; +DISABLE_LIGHTING = false -#ifdef USE_ATTRIB_LIGHT_ANGLE -// shared with tangent, not used in canvas shader -layout(location = 2) in highp float light_angle; -#endif +#[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_ATTRIB_MODULATE -layout(location = 5) in highp vec4 modulate_attrib; -#endif +layout(location = 10) in uvec4 bone_attrib; +layout(location = 11) in vec4 weight_attrib; -#ifdef USE_ATTRIB_LARGE_VERTEX -// shared with skeleton attributes, not used in batched shader -layout(location = 6) in highp vec2 translate_attrib; -layout(location = 7) in highp vec4 basis_attrib; #endif +/* clang-format on */ +#include "canvas_uniforms_inc.glsl" +#include "stdlib_inc.glsl" -#ifdef USE_SKELETON -layout(location = 6) in highp vec4 bone_indices; -layout(location = 7) in highp vec4 bone_weights; -#endif +uniform sampler2D transforms_texture; //texunit:-1 -#ifdef USE_INSTANCING +out vec2 uv_interp; +out vec4 color_interp; +out vec2 vertex_interp; +flat out int draw_data_instance; -layout(location = 8) in highp vec4 instance_xform0; -layout(location = 9) in highp vec4 instance_xform1; -layout(location = 10) in highp vec4 instance_xform2; -layout(location = 11) in highp vec4 instance_color; +#ifdef USE_NINEPATCH -#ifdef USE_INSTANCE_CUSTOM -layout(location = 12) in highp vec4 instance_custom_data; -#endif +out vec2 pixel_size_interp; #endif -#ifdef USE_SKELETON -uniform highp sampler2D skeleton_texture; // texunit:-3 -uniform highp ivec2 skeleton_texture_size; -uniform highp mat4 skeleton_transform; -uniform highp mat4 skeleton_transform_inverse; -#endif +#ifdef MATERIAL_UNIFORMS_USED +layout(std140) uniform MaterialUniforms{ +//ubo:4 -out vec2 uv_interp; -out vec4 color_interp; +#MATERIAL_UNIFORMS -#ifdef USE_ATTRIB_MODULATE -// modulate doesn't need interpolating but we need to send it to the fragment shader -flat out vec4 modulate_interp; +}; #endif -#ifdef MODULATE_USED -uniform vec4 final_modulate; -#endif - -uniform highp vec2 color_texpixel_size; +#GLOBALS -#ifdef USE_TEXTURE_RECT +void main() { + vec4 instance_custom = vec4(0.0); + draw_data_instance = gl_InstanceID; +#ifdef USE_PRIMITIVE -uniform vec4 dst_rect; -uniform vec4 src_rect; + //weird bug, + //this works + vec2 vertex; + vec2 uv; + vec4 color; + + if (gl_VertexID == 0) { + vertex = draw_data[draw_data_instance].point_a; + uv = draw_data[draw_data_instance].uv_a; + color = vec4(unpackHalf2x16(draw_data[draw_data_instance].color_a_rg), unpackHalf2x16(draw_data[draw_data_instance].color_a_ba)); + } else if (gl_VertexID == 1) { + vertex = draw_data[draw_data_instance].point_b; + uv = draw_data[draw_data_instance].uv_b; + color = vec4(unpackHalf2x16(draw_data[draw_data_instance].color_b_rg), unpackHalf2x16(draw_data[draw_data_instance].color_b_ba)); + } else { + vertex = draw_data[draw_data_instance].point_c; + uv = draw_data[draw_data_instance].uv_c; + color = vec4(unpackHalf2x16(draw_data[draw_data_instance].color_c_rg), unpackHalf2x16(draw_data[draw_data_instance].color_c_ba)); + } + uvec4 bones = uvec4(0, 0, 0, 0); + vec4 bone_weights = vec4(0.0); -#endif +#elif defined(USE_ATTRIBUTES) -uniform highp float time; - -#ifdef USE_LIGHTING - -// light matrices -uniform highp mat4 light_matrix; -uniform highp mat4 light_matrix_inverse; -uniform highp mat4 light_local_matrix; -uniform highp mat4 shadow_matrix; -uniform highp vec4 light_color; -uniform highp vec4 light_shadow_color; -uniform highp vec2 light_pos; -uniform highp float shadowpixel_size; -uniform highp float shadow_gradient; -uniform highp float light_height; -uniform highp float light_outside_alpha; -uniform highp float shadow_distance_mult; - -out vec4 light_uv_interp; -out vec2 transformed_light_uv; -out vec4 local_rot; - -#ifdef USE_SHADOWS -out highp vec2 pos; -#endif + vec2 vertex = vertex_attrib; + vec4 color = color_attrib * draw_data[draw_data_instance].modulation; + vec2 uv = uv_attrib; -const bool at_light_pass = true; + uvec4 bones = bone_attrib; + vec4 bone_weights = weight_attrib; #else -const bool at_light_pass = false; -#endif -/* clang-format off */ + vec2 vertex_base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0)); + vec2 vertex_base = vertex_base_arr[gl_VertexID]; -VERTEX_SHADER_GLOBALS + vec2 uv = draw_data[draw_data_instance].src_rect.xy + abs(draw_data[draw_data_instance].src_rect.zw) * ((draw_data[draw_data_instance].flags & FLAGS_TRANSPOSE_RECT) != uint(0) ? vertex_base.yx : vertex_base.xy); + vec4 color = draw_data[draw_data_instance].modulation; + vec2 vertex = draw_data[draw_data_instance].dst_rect.xy + abs(draw_data[draw_data_instance].dst_rect.zw) * mix(vertex_base, vec2(1.0, 1.0) - vertex_base, lessThan(draw_data[draw_data_instance].src_rect.zw, vec2(0.0, 0.0))); + uvec4 bones = uvec4(0, 0, 0, 0); -/* clang-format on */ +#endif -vec2 select(vec2 a, vec2 b, bvec2 c) { - vec2 ret; + mat4 world_matrix = mat4(vec4(draw_data[draw_data_instance].world_x, 0.0, 0.0), vec4(draw_data[draw_data_instance].world_y, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(draw_data[draw_data_instance].world_ofs, 0.0, 1.0)); - ret.x = c.x ? b.x : a.x; - ret.y = c.y ? b.y : a.y; + // MultiMeshes don't batch, so always read from draw_data[0] + uint instancing = draw_data[0].flags & FLAGS_INSTANCING_MASK; - return ret; -} +#ifdef USE_ATTRIBUTES +/* + if (instancing > 1) { + // trails -void main() { - vec4 color = color_attrib; - vec2 uv; + uint stride = 2 + 1 + 1; //particles always uses this format -#ifdef USE_INSTANCING - mat4 extra_matrix_instance = extra_matrix * transpose(mat4(instance_xform0, instance_xform1, instance_xform2, vec4(0.0, 0.0, 0.0, 1.0))); - color *= instance_color; + uint trail_size = instancing; -#ifdef USE_INSTANCE_CUSTOM - vec4 instance_custom = instance_custom_data; -#else - vec4 instance_custom = vec4(0.0); -#endif + uint offset = trail_size * stride * gl_InstanceID; -#else - mat4 extra_matrix_instance = extra_matrix; - vec4 instance_custom = vec4(0.0); -#endif + vec4 pcolor; + vec2 new_vertex; + { + uint boffset = offset + bone_attrib.x * stride; + new_vertex = (vec4(vertex, 0.0, 1.0) * mat4(transforms.data[boffset + 0], transforms.data[boffset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy * weight_attrib.x; + pcolor = transforms.data[boffset + 2] * weight_attrib.x; + } + if (weight_attrib.y > 0.001) { + uint boffset = offset + bone_attrib.y * stride; + new_vertex += (vec4(vertex, 0.0, 1.0) * mat4(transforms.data[boffset + 0], transforms.data[boffset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy * weight_attrib.y; + pcolor += transforms.data[boffset + 2] * weight_attrib.y; + } + if (weight_attrib.z > 0.001) { + uint boffset = offset + bone_attrib.z * stride; + new_vertex += (vec4(vertex, 0.0, 1.0) * mat4(transforms.data[boffset + 0], transforms.data[boffset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy * weight_attrib.z; + pcolor += transforms.data[boffset + 2] * weight_attrib.z; + } + if (weight_attrib.w > 0.001) { + uint boffset = offset + bone_attrib.w * stride; + new_vertex += (vec4(vertex, 0.0, 1.0) * mat4(transforms.data[boffset + 0], transforms.data[boffset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy * weight_attrib.w; + pcolor += transforms.data[boffset + 2] * weight_attrib.w; + } -#ifdef USE_TEXTURE_RECT + instance_custom = transforms.data[offset + 3]; - if (dst_rect.z < 0.0) { // Transpose is encoded as negative dst_rect.z - uv = src_rect.xy + abs(src_rect.zw) * vertex.yx; - } else { - uv = src_rect.xy + abs(src_rect.zw) * vertex; + vertex = new_vertex; + color *= pcolor; + } else*/ +#endif // USE_ATTRIBUTES +/* + { + if (instancing == 1) { + uint stride = 2; + { + if (bool(draw_data[0].flags & FLAGS_INSTANCING_HAS_COLORS)) { + stride += 1; + } + if (bool(draw_data[0].flags & FLAGS_INSTANCING_HAS_CUSTOM_DATA)) { + stride += 1; + } + } + + uint offset = stride * gl_InstanceID; + + mat4 matrix = mat4(transforms.data[offset + 0], transforms.data[offset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0)); + offset += 2; + + if (bool(draw_data[0].flags & FLAGS_INSTANCING_HAS_COLORS)) { + color *= transforms.data[offset]; + offset += 1; + } + + if (bool(draw_data[0].flags & FLAGS_INSTANCING_HAS_CUSTOM_DATA)) { + instance_custom = transforms.data[offset]; + } + + matrix = transpose(matrix); + world_matrix = world_matrix * matrix; + } + } +*/ +#if !defined(USE_ATTRIBUTES) && !defined(USE_PRIMITIVE) + if (bool(draw_data[draw_data_instance].flags & FLAGS_USING_PARTICLES)) { + //scale by texture size + vertex /= draw_data[draw_data_instance].color_texture_pixel_size; } - - vec4 outvec = vec4(0.0, 0.0, 0.0, 1.0); - - // This is what is done in the GLES 3 bindings and should - // take care of flipped rects. - // - // But it doesn't. - // I don't know why, will need to investigate further. - - outvec.xy = dst_rect.xy + abs(dst_rect.zw) * select(vertex, vec2(1.0, 1.0) - vertex, lessThan(src_rect.zw, vec2(0.0, 0.0))); - - // outvec.xy = dst_rect.xy + abs(dst_rect.zw) * vertex; -#else - vec4 outvec = vec4(vertex.xy, 0.0, 1.0); - - uv = uv_attrib; #endif +#ifdef USE_POINT_SIZE float point_size = 1.0; - +#endif { - vec2 src_vtx = outvec.xy; - /* clang-format off */ - -VERTEX_SHADER_CODE - - /* clang-format on */ +#CODE : VERTEX } - gl_PointSize = point_size; +#ifdef USE_NINEPATCH + pixel_size_interp = abs(draw_data[draw_data_instance].dst_rect.zw) * vertex_base; +#endif -#ifdef USE_ATTRIB_MODULATE - // modulate doesn't need interpolating but we need to send it to the fragment shader - modulate_interp = modulate_attrib; +#if !defined(SKIP_TRANSFORM_USED) + vertex = (world_matrix * vec4(vertex, 0.0, 1.0)).xy; #endif -#ifdef USE_ATTRIB_LARGE_VERTEX - // transform is in attributes - vec2 temp; + color_interp = color; - temp = outvec.xy; - temp.x = (outvec.x * basis_attrib.x) + (outvec.y * basis_attrib.z); - temp.y = (outvec.x * basis_attrib.y) + (outvec.y * basis_attrib.w); + 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; + } - temp += translate_attrib; - outvec.xy = temp; +#ifdef USE_ATTRIBUTES +#if 0 + if (bool(draw_data[draw_data_instance].flags & FLAGS_USE_SKELETON) && bone_weights != vec4(0.0)) { //must be a valid bone + //skeleton transform + ivec4 bone_indicesi = ivec4(bone_indices); -#else + uvec2 tex_ofs = bone_indicesi.x * 2; - // transform is in uniforms -#if !defined(SKIP_TRANSFORM_USED) - outvec = extra_matrix_instance * outvec; - outvec = modelview_matrix * outvec; -#endif + mat2x4 m; + m = mat2x4( + texelFetch(skeleton_buffer, tex_ofs + 0), + texelFetch(skeleton_buffer, tex_ofs + 1)) * + bone_weights.x; -#endif // not large integer + tex_ofs = bone_indicesi.y * 2; - color_interp = color; + m += mat2x4( + texelFetch(skeleton_buffer, tex_ofs + 0), + texelFetch(skeleton_buffer, tex_ofs + 1)) * + bone_weights.y; -#ifdef USE_PIXEL_SNAP - outvec.xy = floor(outvec + 0.5).xy; - // precision issue on some hardware creates artifacts within texture - // offset uv by a small amount to avoid - uv += 1e-5; -#endif + tex_ofs = bone_indicesi.z * 2; -#ifdef USE_SKELETON + m += mat2x4( + texelFetch(skeleton_buffer, tex_ofs + 0), + texelFetch(skeleton_buffer, tex_ofs + 1)) * + bone_weights.z; - // look up transform from the "pose texture" - if (bone_weights != vec4(0.0)) { - highp mat4 bone_transform = mat4(0.0); + tex_ofs = bone_indicesi.w * 2; - for (int i = 0; i < 4; i++) { - ivec2 tex_ofs = ivec2(int(bone_indices[i]) * 2, 0); + m += mat2x4( + texelFetch(skeleton_buffer, tex_ofs + 0), + texelFetch(skeleton_buffer, tex_ofs + 1)) * + bone_weights.w; - highp mat4 b = mat4( - texel2DFetch(skeleton_texture, skeleton_texture_size, tex_ofs + ivec2(0, 0)), - texel2DFetch(skeleton_texture, skeleton_texture_size, tex_ofs + ivec2(1, 0)), - vec4(0.0, 0.0, 1.0, 0.0), - vec4(0.0, 0.0, 0.0, 1.0)); + mat4 bone_matrix = skeleton_data.skeleton_transform * transpose(mat4(m[0], m[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))) * skeleton_data.skeleton_transform_inverse; - bone_transform += b * bone_weights[i]; - } - - mat4 bone_matrix = skeleton_transform * transpose(bone_transform) * skeleton_transform_inverse; - - outvec = bone_matrix * outvec; + //outvec = bone_matrix * outvec; } - #endif +#endif + + vertex = (canvas_transform * vec4(vertex, 0.0, 1.0)).xy; + vertex_interp = vertex; uv_interp = uv; - gl_Position = projection_matrix * outvec; -#ifdef USE_LIGHTING + gl_Position = screen_transform * vec4(vertex, 0.0, 1.0); - light_uv_interp.xy = (light_matrix * outvec).xy; - light_uv_interp.zw = (light_local_matrix * outvec).xy; +#ifdef USE_POINT_SIZE + gl_PointSize = point_size; +#endif +} - transformed_light_uv = (mat3(light_matrix_inverse) * vec3(light_uv_interp.zw, 0.0)).xy; //for normal mapping +#[fragment] -#ifdef USE_SHADOWS - pos = outvec.xy; -#endif +#include "canvas_uniforms_inc.glsl" +#include "stdlib_inc.glsl" -#ifdef USE_ATTRIB_LIGHT_ANGLE - // we add a fixed offset because we are using the sign later, - // and don't want floating point error around 0.0 - float la = abs(light_angle) - 1.0; - - // vector light angle - vec4 vla; - vla.xy = vec2(cos(la), sin(la)); - vla.zw = vec2(-vla.y, vla.x); - - // vertical flip encoded in the sign - vla.zw *= sign(light_angle); - - // apply the transform matrix. - // The rotate will be encoded in the transform matrix for single rects, - // and just the flips in the light angle. - // For batching we will encode the rotation and the flips - // in the light angle, and can use the same shader. - local_rot.xy = normalize((modelview_matrix * (extra_matrix_instance * vec4(vla.xy, 0.0, 0.0))).xy); - local_rot.zw = normalize((modelview_matrix * (extra_matrix_instance * vec4(vla.zw, 0.0, 0.0))).xy); -#else - local_rot.xy = normalize((modelview_matrix * (extra_matrix_instance * vec4(1.0, 0.0, 0.0, 0.0))).xy); - local_rot.zw = normalize((modelview_matrix * (extra_matrix_instance * vec4(0.0, 1.0, 0.0, 0.0))).xy); -#ifdef USE_TEXTURE_RECT - local_rot.xy *= sign(src_rect.z); - local_rot.zw *= sign(src_rect.w); -#endif -#endif // not using light angle +uniform sampler2D atlas_texture; //texunit:-2 +uniform sampler2D shadow_atlas_texture; //texunit:-3 +uniform sampler2D screen_texture; //texunit:-4 +uniform sampler2D sdf_texture; //texunit:-5 +uniform sampler2D normal_texture; //texunit:-6 +uniform sampler2D specular_texture; //texunit:-7 -#endif -} +uniform sampler2D color_texture; //texunit:0 -/* clang-format off */ -[fragment] +in vec2 uv_interp; +in vec4 color_interp; +in vec2 vertex_interp; +flat in int draw_data_instance; + +#ifdef USE_NINEPATCH + +in vec2 pixel_size_interp; -#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 color_texture; // texunit:-1 -/* clang-format on */ -uniform highp vec2 color_texpixel_size; -uniform mediump sampler2D normal_texture; // texunit:-2 +layout(location = 0) out vec4 frag_color; + +#ifdef MATERIAL_UNIFORMS_USED +uniform MaterialUniforms{ +//ubo:4 -in mediump vec2 uv_interp; -in mediump vec4 color_interp; +#MATERIAL_UNIFORMS -#ifdef USE_ATTRIB_MODULATE -in mediump vec4 modulate_interp; +}; #endif -uniform highp float time; +vec2 screen_uv_to_sdf(vec2 p_uv) { + return screen_to_sdf * p_uv; +} -uniform vec4 final_modulate; +float texture_sdf(vec2 p_sdf) { + vec2 uv = p_sdf * sdf_to_tex.xy + sdf_to_tex.zw; + float d = texture(sdf_texture, uv).r; + d *= SDF_MAX_LENGTH; + return d * tex_to_sdf; +} -#ifdef SCREEN_TEXTURE_USED +vec2 texture_sdf_normal(vec2 p_sdf) { + vec2 uv = p_sdf * sdf_to_tex.xy + sdf_to_tex.zw; -uniform sampler2D screen_texture; // texunit:-4 + const float EPSILON = 0.001; + return normalize(vec2( + texture(sdf_texture, uv + vec2(EPSILON, 0.0)).r - texture(sdf_texture, uv - vec2(EPSILON, 0.0)).r, + texture(sdf_texture, uv + vec2(0.0, EPSILON)).r - texture(sdf_texture, uv - vec2(0.0, EPSILON)).r)); +} -#endif +vec2 sdf_to_screen_uv(vec2 p_sdf) { + return p_sdf * sdf_to_screen; +} -#ifdef SCREEN_UV_USED +#GLOBALS -uniform vec2 screen_pixel_size; +#ifdef LIGHT_CODE_USED -#endif +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); -#ifdef USE_LIGHTING +#CODE : LIGHT -uniform highp mat4 light_matrix; -uniform highp mat4 light_local_matrix; -uniform highp mat4 shadow_matrix; -uniform highp vec4 light_color; -uniform highp vec4 light_shadow_color; -uniform highp vec2 light_pos; -uniform highp float shadowpixel_size; -uniform highp float shadow_gradient; -uniform highp float light_height; -uniform highp float light_outside_alpha; -uniform highp float shadow_distance_mult; + return light; +} -uniform lowp sampler2D light_texture; // texunit:-6 -in vec4 light_uv_interp; -in vec2 transformed_light_uv; +#endif -in vec4 local_rot; +#ifdef USE_NINEPATCH -#ifdef USE_SHADOWS +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; -uniform highp sampler2D shadow_texture; // texunit:-5 -in highp vec2 pos; + 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(draw_data[draw_data_instance].flags & FLAGS_NINEPACH_DRAW_CENTER)) { + draw_center--; + } -#endif + // 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; + } + } +} -const bool at_light_pass = true; -#else -const bool at_light_pass = false; #endif -uniform bool use_default_normal; +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)); -layout(location = 0) out mediump vec4 frag_color; + 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); -/* clang-format off */ + 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); -FRAGMENT_SHADER_GLOBALS + return specular_shininess.rgb * light_color * s + light_color * base_color * cNdotL; + } else { + return light_color * base_color * cNdotL; + } +} -/* 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; + uint shadow_mode = light_data[light_base].flags & LIGHT_FLAGS_FILTER_MASK; + + if (shadow_mode == LIGHT_FLAGS_SHADOW_NEAREST) { + shadow = textureProjLod(shadow_atlas_texture, shadow_uv, 0.0).x; + } else if (shadow_mode == LIGHT_FLAGS_SHADOW_PCF5) { + vec4 shadow_pixel_size = vec4(light_data[light_base].shadow_pixel_size, 0.0, 0.0, 0.0); + shadow = 0.0; + shadow += textureProjLod(shadow_atlas_texture, shadow_uv - shadow_pixel_size * 2.0, 0.0).x; + shadow += textureProjLod(shadow_atlas_texture, shadow_uv - shadow_pixel_size, 0.0).x; + shadow += textureProjLod(shadow_atlas_texture, shadow_uv, 0.0).x; + shadow += textureProjLod(shadow_atlas_texture, shadow_uv + shadow_pixel_size, 0.0).x; + shadow += textureProjLod(shadow_atlas_texture, shadow_uv + shadow_pixel_size * 2.0, 0.0).x; + shadow /= 5.0; + } else { //PCF13 + vec4 shadow_pixel_size = vec4(light_data[light_base].shadow_pixel_size, 0.0, 0.0, 0.0); + shadow = 0.0; + shadow += textureProjLod(shadow_atlas_texture, shadow_uv - shadow_pixel_size * 6.0, 0.0).x; + shadow += textureProjLod(shadow_atlas_texture, shadow_uv - shadow_pixel_size * 5.0, 0.0).x; + shadow += textureProjLod(shadow_atlas_texture, shadow_uv - shadow_pixel_size * 4.0, 0.0).x; + shadow += textureProjLod(shadow_atlas_texture, shadow_uv - shadow_pixel_size * 3.0, 0.0).x; + shadow += textureProjLod(shadow_atlas_texture, shadow_uv - shadow_pixel_size * 2.0, 0.0).x; + shadow += textureProjLod(shadow_atlas_texture, shadow_uv - shadow_pixel_size, 0.0).x; + shadow += textureProjLod(shadow_atlas_texture, shadow_uv, 0.0).x; + shadow += textureProjLod(shadow_atlas_texture, shadow_uv + shadow_pixel_size, 0.0).x; + shadow += textureProjLod(shadow_atlas_texture, shadow_uv + shadow_pixel_size * 2.0, 0.0).x; + shadow += textureProjLod(shadow_atlas_texture, shadow_uv + shadow_pixel_size * 3.0, 0.0).x; + shadow += textureProjLod(shadow_atlas_texture, shadow_uv + shadow_pixel_size * 4.0, 0.0).x; + shadow += textureProjLod(shadow_atlas_texture, shadow_uv + shadow_pixel_size * 5.0, 0.0).x; + shadow += textureProjLod(shadow_atlas_texture, shadow_uv + shadow_pixel_size * 6.0, 0.0).x; + shadow /= 13.0; + } -void light_compute( - inout vec4 light, - inout vec2 light_vec, - inout float light_height, - inout vec4 light_color, - vec2 light_uv, - inout vec4 shadow_color, - inout vec2 shadow_vec, - vec3 normal, - vec2 uv, -#if defined(SCREEN_UV_USED) - vec2 screen_uv, + vec4 shadow_color = unpackUnorm4x8(light_data[light_base].shadow_color); +#ifdef LIGHT_CODE_USED + shadow_color.rgb *= shadow_modulate; #endif - vec4 color) { -#if defined(USE_LIGHT_SHADER_CODE) + shadow_color.a *= light_color.a; //respect light alpha - /* clang-format off */ + return mix(light_color, shadow_color, shadow); +} -LIGHT_SHADER_CODE +void light_blend_compute(uint light_base, vec4 light_color, inout vec3 color) { + uint blend_mode = light_data[light_base].flags & LIGHT_FLAGS_BLEND_MASK; + + switch (blend_mode) { + case LIGHT_FLAGS_BLEND_MODE_ADD: { + color.rgb += light_color.rgb * light_color.a; + } break; + case LIGHT_FLAGS_BLEND_MODE_SUB: { + color.rgb -= light_color.rgb * light_color.a; + } break; + case LIGHT_FLAGS_BLEND_MODE_MIX: { + color.rgb = mix(color.rgb, light_color.rgb, light_color.a); + } break; + } +} - /* clang-format on */ +float msdf_median(float r, float g, float b, float a) { + return min(max(min(r, g), min(max(r, g), b)), a); +} -#endif +vec2 msdf_map(vec2 value, vec2 in_min, vec2 in_max, vec2 out_min, vec2 out_max) { + return out_min + (out_max - out_min) * (value - in_min) / (in_max - in_min); } void main() { vec4 color = color_interp; vec2 uv = uv_interp; -#ifdef USE_FORCE_REPEAT - //needs to use this to workaround GLES2/WebGL1 forcing tiling that textures that don't support it - uv = mod(uv, vec2(1.0, 1.0)); + vec2 vertex = vertex_interp; + +#if !defined(USE_ATTRIBUTES) && !defined(USE_PRIMITIVE) + +#ifdef USE_NINEPATCH + + int draw_center = 2; + uv = vec2( + map_ninepatch_axis(pixel_size_interp.x, abs(draw_data[draw_data_instance].dst_rect.z), draw_data[draw_data_instance].color_texture_pixel_size.x, draw_data[draw_data_instance].ninepatch_margins.x, draw_data[draw_data_instance].ninepatch_margins.z, int(draw_data[draw_data_instance].flags >> FLAGS_NINEPATCH_H_MODE_SHIFT) & 0x3, draw_center), + map_ninepatch_axis(pixel_size_interp.y, abs(draw_data[draw_data_instance].dst_rect.w), draw_data[draw_data_instance].color_texture_pixel_size.y, draw_data[draw_data_instance].ninepatch_margins.y, draw_data[draw_data_instance].ninepatch_margins.w, int(draw_data[draw_data_instance].flags >> FLAGS_NINEPATCH_V_MODE_SHIFT) & 0x3, draw_center)); + + if (draw_center == 0) { + color.a = 0.0; + } + + uv = uv * draw_data[draw_data_instance].src_rect.zw + draw_data[draw_data_instance].src_rect.xy; //apply region if needed + #endif + if (bool(draw_data[draw_data_instance].flags & FLAGS_CLIP_RECT_UV)) { + uv = clamp(uv, draw_data[draw_data_instance].src_rect.xy, draw_data[draw_data_instance].src_rect.xy + abs(draw_data[draw_data_instance].src_rect.zw)); + } -#if !defined(COLOR_USED) - //default behavior, texture by color - color *= texture(color_texture, uv); #endif -#ifdef SCREEN_UV_USED - vec2 screen_uv = gl_FragCoord.xy * screen_pixel_size; +#ifndef USE_PRIMITIVE + if (bool(draw_data[draw_data_instance].flags & FLAGS_USE_MSDF)) { + float px_range = draw_data[draw_data_instance].ninepatch_margins.x; + float outline_thickness = draw_data[draw_data_instance].ninepatch_margins.y; + //float reserved1 = draw_data[draw_data_instance].ninepatch_margins.z; + //float reserved2 = draw_data[draw_data_instance].ninepatch_margins.w; + + 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, msdf_sample.a) - 0.5; + + if (outline_thickness > 0) { + float cr = clamp(outline_thickness, 0.0, px_range / 2) / px_range; + float a = clamp((d + cr) * px_size, 0.0, 1.0); + color.a = a * color.a; + } else { + float a = clamp(d * px_size + 0.5, 0.0, 1.0); + color.a = a * color.a; + } + + } else { +#else + { #endif + color *= texture(color_texture, uv); + } + + uint light_count = (draw_data[draw_data_instance].flags >> FLAGS_LIGHT_COUNT_SHIFT) & uint(0xF); //max 16 lights + bool using_light = light_count > uint(0) || directional_light_count > uint(0); vec3 normal; #if defined(NORMAL_USED) - bool normal_used = true; #else bool normal_used = false; #endif - if (use_default_normal) { - normal.xy = texture(normal_texture, uv).xy * 2.0 - 1.0; + if (normal_used || (using_light && bool(draw_data[draw_data_instance].flags & FLAGS_DEFAULT_NORMAL_MAP_USED))) { + normal.xy = texture(normal_texture, uv).xy * vec2(2.0, -2.0) - vec2(1.0, -1.0); normal.z = sqrt(1.0 - dot(normal.xy, normal.xy)); normal_used = true; } else { normal = vec3(0.0, 0.0, 1.0); } - { - float normal_depth = 1.0; - -#if defined(NORMALMAP_USED) - vec3 normal_map = vec3(0.0, 0.0, 1.0); - normal_used = true; -#endif - - /* clang-format off */ + vec4 specular_shininess; -FRAGMENT_SHADER_CODE +#if defined(SPECULAR_SHININESS_USED) - /* clang-format on */ - -#if defined(NORMALMAP_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_depth); -#endif - } - -#ifdef USE_ATTRIB_MODULATE - color *= modulate_interp; + bool specular_shininess_used = true; #else -#if !defined(MODULATE_USED) - color *= final_modulate; -#endif + bool specular_shininess_used = false; #endif -#ifdef USE_LIGHTING - - vec2 light_vec = transformed_light_uv; - vec2 shadow_vec = transformed_light_uv; - - if (normal_used) { - normal.xy = mat2(local_rot.xy, local_rot.zw) * normal.xy; + if (specular_shininess_used || (using_light && normal_used && bool(draw_data[draw_data_instance].flags & FLAGS_DEFAULT_SPECULAR_MAP_USED))) { + specular_shininess = texture(specular_texture, uv); + specular_shininess *= unpackUnorm4x8(draw_data[draw_data_instance].specular_shininess); + specular_shininess_used = true; + } else { + specular_shininess = vec4(1.0); } - float att = 1.0; - - vec2 light_uv = light_uv_interp.xy; - vec4 light = texture(light_texture, light_uv); - - if (any(lessThan(light_uv_interp.xy, vec2(0.0, 0.0))) || any(greaterThanEqual(light_uv_interp.xy, vec2(1.0, 1.0)))) { - color.a *= light_outside_alpha; //invisible - - } else { - float real_light_height = light_height; - vec4 real_light_color = light_color; - vec4 real_light_shadow_color = light_shadow_color; - -#if defined(USE_LIGHT_SHADER_CODE) - //light is written by the light shader - light_compute( - light, - light_vec, - real_light_height, - real_light_color, - light_uv, - real_light_shadow_color, - shadow_vec, - normal, - uv, #if defined(SCREEN_UV_USED) - screen_uv, -#endif - color); + vec2 screen_uv = gl_FragCoord.xy * screen_pixel_size; +#else + vec2 screen_uv = vec2(0.0); #endif - light *= real_light_color; - - if (normal_used) { - vec3 light_normal = normalize(vec3(light_vec, -real_light_height)); - light *= max(dot(-light_normal, normal), 0.0); - } + vec3 light_vertex = vec3(vertex, 0.0); + vec2 shadow_vertex = vertex; - color *= light; - -#ifdef USE_SHADOWS + { + float normal_map_depth = 1.0; -#ifdef SHADOW_VEC_USED - mat3 inverse_light_matrix = mat3(light_matrix); - inverse_light_matrix[0] = normalize(inverse_light_matrix[0]); - inverse_light_matrix[1] = normalize(inverse_light_matrix[1]); - inverse_light_matrix[2] = normalize(inverse_light_matrix[2]); - shadow_vec = (inverse_light_matrix * vec3(shadow_vec, 0.0)).xy; -#else - shadow_vec = light_uv_interp.zw; +#if defined(NORMAL_MAP_USED) + vec3 normal_map = vec3(0.0, 0.0, 1.0); + normal_used = true; #endif - float angle_to_light = -atan(shadow_vec.x, shadow_vec.y); - float PI = 3.14159265358979323846264; - /*int i = int(mod(floor((angle_to_light+7.0*PI/6.0)/(4.0*PI/6.0))+1.0, 3.0)); // +1 pq os indices estao em ordem 2,0,1 nos arrays - float ang*/ - - float su, sz; - - float abs_angle = abs(angle_to_light); - vec2 point; - float sh; - if (abs_angle < 45.0 * PI / 180.0) { - point = shadow_vec; - sh = 0.0 + (1.0 / 8.0); - } else if (abs_angle > 135.0 * PI / 180.0) { - point = -shadow_vec; - sh = 0.5 + (1.0 / 8.0); - } else if (angle_to_light > 0.0) { - point = vec2(shadow_vec.y, -shadow_vec.x); - sh = 0.25 + (1.0 / 8.0); - } else { - point = vec2(-shadow_vec.y, shadow_vec.x); - sh = 0.75 + (1.0 / 8.0); - } +#CODE : FRAGMENT - highp vec4 s = shadow_matrix * vec4(point, 0.0, 1.0); - s.xyz /= s.w; - su = s.x * 0.5 + 0.5; - sz = s.z * 0.5 + 0.5; - //sz=lightlength(light_vec); +#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 + } - highp float shadow_attenuation = 0.0; + if (normal_used) { + //convert by item transform + normal.xy = mat2(normalize(draw_data[draw_data_instance].world_x), normalize(draw_data[draw_data_instance].world_y)) * normal.xy; + //convert by canvas transform + normal = normalize((canvas_normal_transform * vec4(normal, 0.0)).xyz); + } -#ifdef USE_RGBA_SHADOWS -#define SHADOW_DEPTH(m_tex, m_uv) dot(texture((m_tex), (m_uv)), vec4(1.0 / (255.0 * 255.0 * 255.0), 1.0 / (255.0 * 255.0), 1.0 / 255.0, 1.0)) + vec3 base_color = color.rgb; + if (bool(draw_data[draw_data_instance].flags & FLAGS_USING_LIGHT_MASK)) { + color = vec4(0.0); //invisible by default due to using light mask + } +#ifdef MODE_LIGHT_ONLY + color = vec4(0.0); #else + color *= canvas_modulation; +#endif -#define SHADOW_DEPTH(m_tex, m_uv) (texture((m_tex), (m_uv)).r) +#if !defined(DISABLE_LIGHTING) && !defined(MODE_UNSHADED) -#endif + for (uint i = uint(0); i < directional_light_count; i++) { + uint light_base = i; -#ifdef SHADOW_USE_GRADIENT + vec2 direction = light_data[light_base].position; + vec4 light_color = light_data[light_base].color; - /* clang-format off */ - /* GLSL es 100 doesn't support line continuation characters(backslashes) */ -#define SHADOW_TEST(m_ofs) { highp float sd = SHADOW_DEPTH(shadow_texture, vec2(m_ofs, sh)); shadow_attenuation += 1.0 - smoothstep(sd, sd + shadow_gradient, sz); } +#ifdef LIGHT_CODE_USED + vec4 shadow_modulate = vec4(1.0); + light_color = light_compute(light_vertex, vec3(direction, light_data[light_base].height), normal, light_color, light_color.a, specular_shininess, shadow_modulate, screen_uv, uv, color, true); #else -#define SHADOW_TEST(m_ofs) { highp float sd = SHADOW_DEPTH(shadow_texture, vec2(m_ofs, sh)); shadow_attenuation += step(sz, sd); } - /* clang-format on */ - + if (normal_used) { + vec3 light_vec = normalize(mix(vec3(direction, 0.0), vec3(0, 0, 1), light_data[light_base].height)); + light_color.rgb = light_normal_compute(light_vec, normal, base_color, light_color.rgb, specular_shininess, specular_shininess_used); + } #endif -#ifdef SHADOW_FILTER_NEAREST + if (bool(light_data[light_base].flags & LIGHT_FLAGS_HAS_SHADOW)) { + vec2 shadow_pos = (vec4(shadow_vertex, 0.0, 1.0) * mat4(light_data[light_base].shadow_matrix[0], light_data[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. - SHADOW_TEST(su); + vec4 shadow_uv = vec4(shadow_pos.x, light_data[light_base].shadow_y_ofs, shadow_pos.y * light_data[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 + ); + } -#ifdef SHADOW_FILTER_PCF3 - - SHADOW_TEST(su + shadowpixel_size); - SHADOW_TEST(su); - SHADOW_TEST(su - shadowpixel_size); - shadow_attenuation /= 3.0; + light_blend_compute(light_base, light_color, color.rgb); + } -#endif + // Positional Lights -#ifdef SHADOW_FILTER_PCF5 + for (uint i = uint(0); i < MAX_LIGHTS_PER_ITEM; i++) { + if (i >= light_count) { + break; + } + uint light_base; + if (i < uint(8)) { + if (i < uint(4)) { + light_base = draw_data[draw_data_instance].lights.x; + } else { + light_base = draw_data[draw_data_instance].lights.y; + } + } else { + if (i < uint(12)) { + light_base = draw_data[draw_data_instance].lights.z; + } else { + light_base = draw_data[draw_data_instance].lights.w; + } + } + light_base >>= (i & uint(3)) * uint(8); + light_base &= uint(0xFF); - SHADOW_TEST(su + shadowpixel_size * 2.0); - SHADOW_TEST(su + shadowpixel_size); - SHADOW_TEST(su); - SHADOW_TEST(su - shadowpixel_size); - SHADOW_TEST(su - shadowpixel_size * 2.0); - shadow_attenuation /= 5.0; + vec2 tex_uv = (vec4(vertex, 0.0, 1.0) * mat4(light_data[light_base].texture_matrix[0], light_data[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_data[light_base].atlas_rect.zw + light_data[light_base].atlas_rect.xy; + vec4 light_color = textureLod(atlas_texture, tex_uv_atlas, 0.0); + vec4 light_base_color = light_data[light_base].color; -#endif +#ifdef LIGHT_CODE_USED -#ifdef SHADOW_FILTER_PCF7 + vec4 shadow_modulate = vec4(1.0); + vec3 light_position = vec3(light_data[light_base].position, light_data[light_base].height); - SHADOW_TEST(su + shadowpixel_size * 3.0); - SHADOW_TEST(su + shadowpixel_size * 2.0); - SHADOW_TEST(su + shadowpixel_size); - SHADOW_TEST(su); - SHADOW_TEST(su - shadowpixel_size); - SHADOW_TEST(su - shadowpixel_size * 2.0); - SHADOW_TEST(su - shadowpixel_size * 3.0); - shadow_attenuation /= 7.0; - -#endif + 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, color, false); +#else -#ifdef SHADOW_FILTER_PCF9 + light_color.rgb *= light_base_color.rgb * light_base_color.a; - SHADOW_TEST(su + shadowpixel_size * 4.0); - SHADOW_TEST(su + shadowpixel_size * 3.0); - SHADOW_TEST(su + shadowpixel_size * 2.0); - SHADOW_TEST(su + shadowpixel_size); - SHADOW_TEST(su); - SHADOW_TEST(su - shadowpixel_size); - SHADOW_TEST(su - shadowpixel_size * 2.0); - SHADOW_TEST(su - shadowpixel_size * 3.0); - SHADOW_TEST(su - shadowpixel_size * 4.0); - shadow_attenuation /= 9.0; + if (normal_used) { + vec3 light_pos = vec3(light_data[light_base].position, light_data[light_base].height); + vec3 pos = light_vertex; + vec3 light_vec = normalize(light_pos - pos); + float cNdotL = max(0.0, dot(normal, light_vec)); + light_color.rgb = light_normal_compute(light_vec, normal, base_color, light_color.rgb, specular_shininess, specular_shininess_used); + } #endif + 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 + light_color.a = 0.0; + } -#ifdef SHADOW_FILTER_PCF13 - - SHADOW_TEST(su + shadowpixel_size * 6.0); - SHADOW_TEST(su + shadowpixel_size * 5.0); - SHADOW_TEST(su + shadowpixel_size * 4.0); - SHADOW_TEST(su + shadowpixel_size * 3.0); - SHADOW_TEST(su + shadowpixel_size * 2.0); - SHADOW_TEST(su + shadowpixel_size); - SHADOW_TEST(su); - SHADOW_TEST(su - shadowpixel_size); - SHADOW_TEST(su - shadowpixel_size * 2.0); - SHADOW_TEST(su - shadowpixel_size * 3.0); - SHADOW_TEST(su - shadowpixel_size * 4.0); - SHADOW_TEST(su - shadowpixel_size * 5.0); - SHADOW_TEST(su - shadowpixel_size * 6.0); - shadow_attenuation /= 13.0; - -#endif + if (bool(light_data[light_base].flags & LIGHT_FLAGS_HAS_SHADOW)) { + vec2 shadow_pos = (vec4(shadow_vertex, 0.0, 1.0) * mat4(light_data[light_base].shadow_matrix[0], light_data[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 distance; + if (pos_rot.y > 0) { + if (pos_rot.x > 0) { + tex_ofs = pos_box.y * 0.125 + 0.125; + distance = shadow_pos.x; + } else { + tex_ofs = pos_box.x * -0.125 + (0.25 + 0.125); + distance = shadow_pos.y; + } + } else { + if (pos_rot.x < 0) { + tex_ofs = pos_box.y * -0.125 + (0.5 + 0.125); + distance = -shadow_pos.x; + } else { + tex_ofs = pos_box.x * 0.125 + (0.75 + 0.125); + distance = -shadow_pos.y; + } + } + + distance *= light_data[light_base].shadow_zfar_inv; + + //float distance = length(shadow_pos); + vec4 shadow_uv = vec4(tex_ofs, light_data[light_base].shadow_y_ofs, distance, 1.0); + + light_color = light_shadow_compute(light_base, light_color, shadow_uv +#ifdef LIGHT_CODE_USED + , + shadow_modulate.rgb +#endif + ); + } - //color *= shadow_attenuation; - color = mix(real_light_shadow_color, color, shadow_attenuation); -//use shadows -#endif + light_blend_compute(light_base, light_color, color.rgb); } - -//use lighting -#endif +#endif // UNSHADED frag_color = color; } diff --git a/drivers/gles3/shaders/canvas_shadow.glsl b/drivers/gles3/shaders/canvas_shadow.glsl index 2b3be43f6e..65389c211a 100644 --- a/drivers/gles3/shaders/canvas_shadow.glsl +++ b/drivers/gles3/shaders/canvas_shadow.glsl @@ -10,7 +10,7 @@ precision highp float; precision highp int; #endif -layout(location = 0) highp vec3 vertex; +layout(location = 0) in highp vec3 vertex; uniform highp mat4 projection_matrix; /* clang-format on */ diff --git a/drivers/gles3/shaders/canvas_uniforms_inc.glsl b/drivers/gles3/shaders/canvas_uniforms_inc.glsl new file mode 100644 index 0000000000..e08a15e59d --- /dev/null +++ b/drivers/gles3/shaders/canvas_uniforms_inc.glsl @@ -0,0 +1,120 @@ + +#define MAX_LIGHTS_PER_ITEM uint(16) + +#define M_PI 3.14159265359 + +#define SDF_MAX_LENGTH 16384.0 + +//1 means enabled, 2+ means trails in use +#define FLAGS_INSTANCING_MASK uint(0x7F) +#define FLAGS_INSTANCING_HAS_COLORS uint(1 << 7) +#define FLAGS_INSTANCING_HAS_CUSTOM_DATA uint(1 << 8) + +#define FLAGS_CLIP_RECT_UV uint(1 << 9) +#define FLAGS_TRANSPOSE_RECT uint(1 << 10) +#define FLAGS_USING_LIGHT_MASK uint(1 << 11) +#define FLAGS_NINEPACH_DRAW_CENTER uint(1 << 12) +#define FLAGS_USING_PARTICLES uint(1 << 13) + +#define FLAGS_NINEPATCH_H_MODE_SHIFT 16 +#define FLAGS_NINEPATCH_V_MODE_SHIFT 18 + +#define FLAGS_LIGHT_COUNT_SHIFT 20 + +#define FLAGS_DEFAULT_NORMAL_MAP_USED uint(1 << 26) +#define FLAGS_DEFAULT_SPECULAR_MAP_USED uint(1 << 27) + +#define FLAGS_USE_MSDF uint(1 << 28) + +// must be always 128 bytes long +struct DrawData { + vec2 world_x; + vec2 world_y; + vec2 world_ofs; + vec2 color_texture_pixel_size; +#ifdef USE_PRIMITIVE + vec2 point_a; + vec2 point_b; + vec2 point_c; + vec2 uv_a; + vec2 uv_b; + vec2 uv_c; + uint color_a_rg; + uint color_a_ba; + uint color_b_rg; + uint color_b_ba; + uint color_c_rg; + uint color_c_ba; +#else + vec4 modulation; + vec4 ninepatch_margins; + vec4 dst_rect; //for built-in rect and UV + vec4 src_rect; + uint pad; + uint pad2; +#endif + uint flags; + uint specular_shininess; + uvec4 lights; +}; + +layout(std140) uniform GlobalVariableData { //ubo:1 + vec4 global_variables[MAX_GLOBAL_VARIABLES]; +}; + +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; +}; + +#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_data[MAX_LIGHTS]; +}; + +layout(std140) uniform DrawDataInstances { //ubo:3 + + DrawData draw_data[MAX_DRAW_DATA_INSTANCES]; +}; diff --git a/drivers/gles3/shaders/copy.glsl b/drivers/gles3/shaders/copy.glsl index 598c6fd614..62332a15a7 100644 --- a/drivers/gles3/shaders/copy.glsl +++ b/drivers/gles3/shaders/copy.glsl @@ -1,5 +1,21 @@ /* clang-format off */ -[vertex] +#[modes] + +mode_default = +mode_cubemap = #define USE_CUBEMAP +mode_panorama = #define USE_PANORAMA +mode_copy_section = #define USE_COPY_SECTION +mode_asym_pano = #define USE_ASYM_PANO +mode_no_alpha = #define USE_NO_ALPHA +mode_custom_alpha = #define USE_CUSTOM_ALPHA +mode_multiplier = #define USE_MULTIPLIER +mode_sep_cbcr_texture = #define USE_SEP_CBCR_TEXTURE +mode_ycbcr_to_rgb = #define USE_YCBCR_TO_RGB + +#[specializations] + + +#[vertex] #ifdef USE_GLES_OVER_GL #define lowp @@ -10,16 +26,16 @@ precision highp float; precision highp int; #endif -layout(location = 0) highp vec4 vertex_attrib; +layout(location = 0) in highp vec4 vertex_attrib; /* clang-format on */ #if defined(USE_CUBEMAP) || defined(USE_PANORAMA) -layout(location = 4) vec3 cube_in; +layout(location = 4) in vec3 cube_in; #else -layout(location = 4) vec2 uv_in; +layout(location = 4) in vec2 uv_in; #endif -layout(location = 5) vec2 uv2_in; +layout(location = 5) in vec2 uv2_in; #if defined(USE_CUBEMAP) || defined(USE_PANORAMA) out vec3 cube_interp; @@ -28,11 +44,6 @@ out vec2 uv_interp; #endif out vec2 uv2_interp; -// These definitions are here because the shader-wrapper builder does -// not understand `#elif defined()` -#ifdef USE_DISPLAY_TRANSFORM -#endif - #ifdef USE_COPY_SECTION uniform highp vec4 copy_section; #elif defined(USE_DISPLAY_TRANSFORM) @@ -60,7 +71,7 @@ void main() { } /* clang-format off */ -[fragment] +#[fragment] #define M_PI 3.14159265359 @@ -96,7 +107,7 @@ uniform samplerCube source_cube; // texunit:0 uniform sampler2D source; // texunit:0 #endif -#ifdef SEP_CBCR_TEXTURE +#ifdef USE_SEP_CBCR_TEXTURE uniform sampler2D CbCr; //texunit:1 #endif @@ -156,8 +167,8 @@ void main() { vec4 color = texturePanorama(source, normalize(cube_normal.xyz)); #elif defined(USE_CUBEMAP) - vec4 color = textureCube(source_cube, normalize(cube_interp)); -#elif defined(SEP_CBCR_TEXTURE) + vec4 color = texture(source_cube, normalize(cube_interp)); +#elif defined(USE_SEP_CBCR_TEXTURE) vec4 color; color.r = texture(source, uv_interp).r; color.gb = texture(CbCr, uv_interp).rg - vec2(0.5, 0.5); @@ -166,7 +177,7 @@ void main() { vec4 color = texture(source, uv_interp); #endif -#ifdef YCBCR_TO_RGB +#ifdef USE_YCBCR_TO_RGB // YCbCr -> RGB conversion // Using BT.601, which is the standard for SDTV is provided as a reference diff --git a/drivers/gles3/shaders/cube_to_dp.glsl b/drivers/gles3/shaders/cube_to_dp.glsl index ea4df79d4e..2384529a89 100644 --- a/drivers/gles3/shaders/cube_to_dp.glsl +++ b/drivers/gles3/shaders/cube_to_dp.glsl @@ -10,9 +10,9 @@ precision mediump float; precision mediump int; #endif -layout(location = 0) highp vec4 vertex_attrib; +layout(location = 0) in highp vec4 vertex_attrib; /* clang-format on */ -layout(location = 4) vec2 uv_in; +layout(location = 4) in vec2 uv_in; out vec2 uv_interp; diff --git a/drivers/gles3/shaders/cubemap_filter.glsl b/drivers/gles3/shaders/cubemap_filter.glsl index 04bf3ebf02..2081abfef6 100644 --- a/drivers/gles3/shaders/cubemap_filter.glsl +++ b/drivers/gles3/shaders/cubemap_filter.glsl @@ -10,9 +10,9 @@ precision highp float; precision highp int; #endif -layout(location = 0) highp vec2 vertex; +layout(location = 0) in highp vec2 vertex; /* clang-format on */ -layout(location = 4) highp vec2 uv; +layout(location = 4) in highp vec2 uv; out highp vec2 uv_interp; diff --git a/drivers/gles3/shaders/effect_blur.glsl b/drivers/gles3/shaders/effect_blur.glsl index 80063a7175..c9184cca77 100644 --- a/drivers/gles3/shaders/effect_blur.glsl +++ b/drivers/gles3/shaders/effect_blur.glsl @@ -10,9 +10,9 @@ precision highp float; precision highp int; #endif -layout(location = 0) vec2 vertex_attrib; +layout(location = 0) in vec2 vertex_attrib; /* clang-format on */ -layout(location = 4) vec2 uv_in; +layout(location = 4) in vec2 uv_in; out vec2 uv_interp; diff --git a/drivers/gles3/shaders/lens_distorted.glsl b/drivers/gles3/shaders/lens_distorted.glsl index 64c2d70cc8..3aaf1050e5 100644 --- a/drivers/gles3/shaders/lens_distorted.glsl +++ b/drivers/gles3/shaders/lens_distorted.glsl @@ -10,7 +10,7 @@ precision highp float; precision highp int; #endif -layout(location = 0) highp vec2 vertex; +layout(location = 0) in highp vec2 vertex; /* clang-format on */ uniform vec2 offset; diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl index c2f3908395..98c92a1d99 100644 --- a/drivers/gles3/shaders/scene.glsl +++ b/drivers/gles3/shaders/scene.glsl @@ -18,38 +18,38 @@ precision highp int; // attributes // -layout(location = 0) highp vec4 vertex_attrib; +layout(location = 0) in highp vec4 vertex_attrib; /* clang-format on */ -layout(location = 1) vec3 normal_attrib; +layout(location = 1) in vec3 normal_attrib; #if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) -layout(location = 2) vec4 tangent_attrib; +layout(location = 2) in vec4 tangent_attrib; #endif #if defined(ENABLE_COLOR_INTERP) -layout(location = 3) vec4 color_attrib; +layout(location = 3) in vec4 color_attrib; #endif #if defined(ENABLE_UV_INTERP) -layout(location = 4) vec2 uv_attrib; +layout(location = 4) in vec2 uv_attrib; #endif #if defined(ENABLE_UV2_INTERP) || defined(USE_LIGHTMAP) -layout(location = 5) vec2 uv2_attrib; +layout(location = 5) in vec2 uv2_attrib; #endif #ifdef USE_SKELETON #ifdef USE_SKELETON_SOFTWARE -layout(location = 13) highp vec4 bone_transform_row_0; -layout(location = 14) highp vec4 bone_transform_row_1; -layout(location = 15) highp vec4 bone_transform_row_2; +layout(location = 13) in highp vec4 bone_transform_row_0; +layout(location = 14) in highp vec4 bone_transform_row_1; +layout(location = 15) in highp vec4 bone_transform_row_2; #else -layout(location = 6) vec4 bone_ids; -layout(location = 7) highp vec4 bone_weights; +layout(location = 6) in vec4 bone_ids; +layout(location = 7) in highp vec4 bone_weights; uniform highp sampler2D bone_transforms; // texunit:-1 uniform ivec2 skeleton_texture_size; @@ -60,12 +60,12 @@ uniform ivec2 skeleton_texture_size; #ifdef USE_INSTANCING -layout(location = 8) highp vec4 instance_xform_row_0; -layout(location = 9) highp vec4 instance_xform_row_1; -layout(location = 10) highp vec4 instance_xform_row_2; +layout(location = 8) in highp vec4 instance_xform_row_0; +layout(location = 9) in highp vec4 instance_xform_row_1; +layout(location = 10) in highp vec4 instance_xform_row_2; -layout(location = 11) highp vec4 instance_color; -layout(location = 12) highp vec4 instance_custom_data; +layout(location = 11) in highp vec4 instance_color; +layout(location = 12) in highp vec4 instance_custom_data; #endif diff --git a/drivers/gles3/shaders/stdlib_inc.glsl b/drivers/gles3/shaders/stdlib_inc.glsl new file mode 100644 index 0000000000..2eddf9d479 --- /dev/null +++ b/drivers/gles3/shaders/stdlib_inc.glsl @@ -0,0 +1,58 @@ +//TODO: only needed by GLES_OVER_GL + +uint float2half(uint f) { + return ((f >> uint(16)) & uint(0x8000)) | + ((((f & uint(0x7f800000)) - uint(0x38000000)) >> uint(13)) & uint(0x7c00)) | + ((f >> uint(13)) & uint(0x03ff)); +} + +uint half2float(uint h) { + return ((h & uint(0x8000)) << uint(16)) | (((h & uint(0x7c00)) + uint(0x1c000)) << uint(13)) | ((h & uint(0x03ff)) << uint(13)); +} + +uint packHalf2x16(vec2 v) { + return float2half(floatBitsToUint(v.x)) | float2half(floatBitsToUint(v.y)) << uint(16); +} + +vec2 unpackHalf2x16(uint v) { + return vec2(uintBitsToFloat(half2float(v & uint(0xffff))), + uintBitsToFloat(half2float(v >> uint(16)))); +} + +uint 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 unpackUnorm2x16(uint p) { + return vec2(float(p & uint(0xffff)), float(p >> uint(16))) * 0.000015259021; // 1.0 / 65535.0 optimization +} + +uint 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 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)); +} + +uint 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 unpackUnorm4x8(uint p) { + return vec4(float(p & uint(0xffff)), float((p >> uint(8)) & uint(0xffff)), float((p >> uint(16)) & uint(0xffff)), float(p >> uint(24))) * 0.00392156862; // 1.0 / 255.0 +} + +uint 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 unpackSnorm4x8(uint p) { + vec4 v = vec4(float(p & uint(0xffff)), float((p >> uint(8)) & uint(0xffff)), float((p >> uint(16)) & uint(0xffff)), float(p >> uint(24))); + return clamp((v - vec4(127.0)) * vec4(0.00787401574), vec4(-1.0), vec4(1.0)); +} diff --git a/drivers/gles3/shaders/tonemap.glsl b/drivers/gles3/shaders/tonemap.glsl index 8b3aa4d309..4f962626a3 100644 --- a/drivers/gles3/shaders/tonemap.glsl +++ b/drivers/gles3/shaders/tonemap.glsl @@ -10,9 +10,9 @@ precision highp float; precision highp int; #endif -layout(location = 0) vec2 vertex_attrib; +layout(location = 0) in vec2 vertex_attrib; /* clang-format on */ -layout(location = 4) vec2 uv_in; +layout(location = 4) in vec2 uv_in; out vec2 uv_interp; diff --git a/drivers/gles3/texture_loader_gles3.cpp b/drivers/gles3/texture_loader_gles3.cpp index f4ae6decab..ca52eaeacb 100644 --- a/drivers/gles3/texture_loader_gles3.cpp +++ b/drivers/gles3/texture_loader_gles3.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "texture_loader_gles3.h" -#ifdef GLES3_BACKEND_ENABLED +#ifdef GLES3_ENABLED #include "core/io/file_access.h" #include "core/string/print_string.h" diff --git a/drivers/gles3/texture_loader_gles3.h b/drivers/gles3/texture_loader_gles3.h index 68540fc5c0..54ddf80a96 100644 --- a/drivers/gles3/texture_loader_gles3.h +++ b/drivers/gles3/texture_loader_gles3.h @@ -31,8 +31,7 @@ #ifndef TEXTURE_LOADER_OPENGL_H #define TEXTURE_LOADER_OPENGL_H -#include "drivers/gles3/rasterizer_platforms.h" -#ifdef GLES3_BACKEND_ENABLED +#ifdef GLES3_ENABLED #include "core/io/resource_loader.h" #include "scene/resources/texture.h" @@ -47,6 +46,6 @@ public: virtual ~ResourceFormatGLES2Texture() {} }; -#endif // GLES3_BACKEND_ENABLED +#endif // GLES3_ENABLED #endif // TEXTURE_LOADER_OPENGL_H |