diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp index f7254cede56..535f118b05b 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.cpp +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -170,6 +170,7 @@ void RasterizerCanvasGLES3::canvas_begin() { state.canvas_shader.set_conditional(CanvasShaderGLES3::SHADOW_FILTER_PCF13, false); state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_DISTANCE_FIELD, false); state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_NINEPATCH, false); + state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_LIGHT_ANGLE, false); state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_SKELETON, false); state.canvas_shader.set_custom_shader(0); @@ -191,6 +192,7 @@ void RasterizerCanvasGLES3::canvas_begin() { glBindVertexArray(data.canvas_quad_array); state.using_texture_rect = true; state.using_ninepatch = false; + state.using_light_angle = false; state.using_skeleton = false; } @@ -204,6 +206,7 @@ void RasterizerCanvasGLES3::canvas_end() { state.using_texture_rect = false; state.using_ninepatch = false; + state.using_light_angle = false; } RasterizerStorageGLES3::Texture *RasterizerCanvasGLES3::_bind_canvas_texture(const RID &p_texture, const RID &p_normal_map, bool p_force) { @@ -288,9 +291,9 @@ RasterizerStorageGLES3::Texture *RasterizerCanvasGLES3::_bind_canvas_texture(con return tex_return; } -void RasterizerCanvasGLES3::_set_texture_rect_mode(bool p_enable, bool p_ninepatch) { +void RasterizerCanvasGLES3::_set_texture_rect_mode(bool p_enable, bool p_ninepatch, bool p_light_angle) { - if (state.using_texture_rect == p_enable && state.using_ninepatch == p_ninepatch) + if (state.using_texture_rect == p_enable && state.using_ninepatch == p_ninepatch && state.using_light_angle == p_light_angle) return; if (p_enable) { @@ -304,6 +307,7 @@ void RasterizerCanvasGLES3::_set_texture_rect_mode(bool p_enable, bool p_ninepat state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_NINEPATCH, p_ninepatch && p_enable); state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_TEXTURE_RECT, p_enable); + state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_LIGHT_ANGLE, p_light_angle); state.canvas_shader.bind(); state.canvas_shader.set_uniform(CanvasShaderGLES3::FINAL_MODULATE, state.canvas_item_modulate); state.canvas_shader.set_uniform(CanvasShaderGLES3::MODELVIEW_MATRIX, state.final_transform); @@ -319,6 +323,7 @@ void RasterizerCanvasGLES3::_set_texture_rect_mode(bool p_enable, bool p_ninepat } state.using_texture_rect = p_enable; state.using_ninepatch = p_ninepatch; + state.using_light_angle = p_light_angle; } void RasterizerCanvasGLES3::_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 int *p_bones, const float *p_weights) { @@ -548,7 +553,7 @@ void RasterizerCanvasGLES3::_draw_generic_indices(GLuint p_primitive, const int glBindBuffer(GL_ARRAY_BUFFER, 0); } -void RasterizerCanvasGLES3::_draw_gui_primitive(int p_points, const Vector2 *p_vertices, const Color *p_colors, const Vector2 *p_uvs) { +void RasterizerCanvasGLES3::_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 }; @@ -557,6 +562,7 @@ void RasterizerCanvasGLES3::_draw_gui_primitive(int p_points, const Vector2 *p_v int version = 0; int color_ofs = 0; int uv_ofs = 0; + int light_angle_ofs = 0; int stride = 2; if (p_colors) { //color @@ -571,7 +577,13 @@ void RasterizerCanvasGLES3::_draw_gui_primitive(int p_points, const Vector2 *p_v stride += 2; } - float b[(2 + 2 + 4) * 4]; + if (p_light_angles) { //light_angles + version |= 4; + light_angle_ofs = stride; + stride += 1; + } + + float b[(2 + 2 + 4 + 1) * 4]; for (int i = 0; i < p_points; i++) { b[stride * i + 0] = p_vertices[i].x; @@ -596,6 +608,13 @@ void RasterizerCanvasGLES3::_draw_gui_primitive(int p_points, const Vector2 *p_v } } + if (p_light_angles) { + + for (int i = 0; i < p_points; i++) { + b[stride * i + light_angle_ofs] = p_light_angles[i]; + } + } + glBindBuffer(GL_ARRAY_BUFFER, data.polygon_buffer); #ifndef GLES_OVER_GL // Orphan the buffer to avoid CPU/GPU sync points caused by glBufferSubData @@ -623,10 +642,19 @@ static const GLenum gl_primitive[] = { void RasterizerCanvasGLES3::render_rect_nvidia_workaround(const Item::CommandRect *p_rect, const RasterizerStorageGLES3::Texture *p_texture) { - _set_texture_rect_mode(false); - if (p_texture) { + 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; + } + + // we don't want to use texture rect, and we want to send light angles if we are using normal mapping + _set_texture_rect_mode(false, false, send_light_angles); + bool untile = false; if (p_rect->flags & CANVAS_RECT_TILE && !(p_texture->flags & VS::TEXTURE_FLAG_REPEAT)) { @@ -663,6 +691,10 @@ void RasterizerCanvasGLES3::render_rect_nvidia_workaround(const Item::CommandRec src_rect.position + Vector2(0.0, src_rect.size.y), }; + // for encoding in light angle + bool flip_h = false; + bool flip_v = false; + if (p_rect->flags & CANVAS_RECT_TRANSPOSE) { SWAP(uvs[1], uvs[3]); } @@ -670,13 +702,42 @@ void RasterizerCanvasGLES3::render_rect_nvidia_workaround(const Item::CommandRec if (p_rect->flags & CANVAS_RECT_FLIP_H) { SWAP(uvs[0], uvs[1]); SWAP(uvs[2], uvs[3]); + flip_h = true; + flip_v = !flip_v; } if (p_rect->flags & CANVAS_RECT_FLIP_V) { SWAP(uvs[0], uvs[3]); SWAP(uvs[1], uvs[2]); + flip_v = !flip_v; } - _draw_gui_primitive(4, points, NULL, uvs); + 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) { glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); @@ -684,6 +745,8 @@ void RasterizerCanvasGLES3::render_rect_nvidia_workaround(const Item::CommandRec } } else { + _set_texture_rect_mode(false); + state.canvas_shader.set_uniform(CanvasShaderGLES3::CLIP_RECT_UV, false); Vector2 points[4] = { @@ -2262,7 +2325,7 @@ void RasterizerCanvasGLES3::initialize() { uint32_t poly_size = 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 *= 1024; //kb - poly_size = MAX(poly_size, (2 + 2 + 4) * 4 * sizeof(float)); + poly_size = MAX(poly_size, (2 + 2 + 4 + 1) * 4 * sizeof(float)); glGenBuffers(1, &data.polygon_buffer); glBindBuffer(GL_ARRAY_BUFFER, data.polygon_buffer); glBufferData(GL_ARRAY_BUFFER, poly_size, NULL, GL_DYNAMIC_DRAW); //allocate max size @@ -2270,13 +2333,14 @@ void RasterizerCanvasGLES3::initialize() { data.polygon_buffer_size = poly_size; //quad arrays - for (int i = 0; i < 4; i++) { + 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 @@ -2289,6 +2353,11 @@ void RasterizerCanvasGLES3::initialize() { stride += 2 * 4; } + if (i & 4) { //light_angle + light_angle_ofs = stride; + stride += 1 * 4; + } + glEnableVertexAttribArray(VS::ARRAY_VERTEX); glVertexAttribPointer(VS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, stride, NULL); @@ -2302,6 +2371,12 @@ void RasterizerCanvasGLES3::initialize() { glVertexAttribPointer(VS::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(VS::ARRAY_TANGENT); + glVertexAttribPointer(VS::ARRAY_TANGENT, 1, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(light_angle_ofs)); + } + glBindVertexArray(0); } diff --git a/drivers/gles3/rasterizer_canvas_gles3.h b/drivers/gles3/rasterizer_canvas_gles3.h index f67d900da33..5179e48332f 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.h +++ b/drivers/gles3/rasterizer_canvas_gles3.h @@ -52,11 +52,13 @@ public: struct Data { + enum { NUM_QUAD_ARRAY_VARIATIONS = 8 }; + GLuint canvas_quad_vertices; GLuint canvas_quad_array; GLuint polygon_buffer; - GLuint polygon_buffer_quad_arrays[4]; + GLuint polygon_buffer_quad_arrays[NUM_QUAD_ARRAY_VARIATIONS]; GLuint polygon_buffer_pointer_array; GLuint polygon_index_buffer; @@ -78,6 +80,7 @@ public: bool using_texture_rect; bool using_ninepatch; + bool using_light_angle; RID current_tex; RID current_normal; @@ -127,10 +130,10 @@ public: virtual void canvas_begin(); virtual void canvas_end(); - _FORCE_INLINE_ void _set_texture_rect_mode(bool p_enable, bool p_ninepatch = false); + _FORCE_INLINE_ void _set_texture_rect_mode(bool p_enable, bool p_ninepatch = false, bool p_light_angle = false); _FORCE_INLINE_ RasterizerStorageGLES3::Texture *_bind_canvas_texture(const RID &p_texture, const RID &p_normal_map, bool p_force = false); - _FORCE_INLINE_ void _draw_gui_primitive(int p_points, const Vector2 *p_vertices, const Color *p_colors, const Vector2 *p_uvs); + _FORCE_INLINE_ 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); _FORCE_INLINE_ 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 int *p_bones, const float *p_weights); _FORCE_INLINE_ 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); _FORCE_INLINE_ 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); diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp index a2f1b54e4d2..967e8a6645f 100644 --- a/drivers/gles3/rasterizer_gles3.cpp +++ b/drivers/gles3/rasterizer_gles3.cpp @@ -413,6 +413,42 @@ void RasterizerGLES3::register_config() { ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/filters/anisotropic_filter_level", PropertyInfo(Variant::INT, "rendering/quality/filters/anisotropic_filter_level", PROPERTY_HINT_RANGE, "1,16,1")); } +// returns NULL if no error, or an error string +const char *RasterizerGLES3::gl_check_for_error(bool p_print_error) { + GLenum err = glGetError(); + + const char *err_string = nullptr; + + switch (err) { + default: { + // not recognised + } break; + case GL_NO_ERROR: { + } break; + case GL_INVALID_ENUM: { + err_string = "GL_INVALID_ENUM"; + } break; + case GL_INVALID_VALUE: { + err_string = "GL_INVALID_VALUE"; + } break; + case GL_INVALID_OPERATION: { + err_string = "GL_INVALID_OPERATION"; + } break; + case GL_INVALID_FRAMEBUFFER_OPERATION: { + err_string = "GL_INVALID_FRAMEBUFFER_OPERATION"; + } break; + case GL_OUT_OF_MEMORY: { + err_string = "GL_OUT_OF_MEMORY"; + } break; + } + + if (p_print_error && err_string) { + print_line(err_string); + } + + return err_string; +} + RasterizerGLES3::RasterizerGLES3() { storage = memnew(RasterizerStorageGLES3); diff --git a/drivers/gles3/rasterizer_gles3.h b/drivers/gles3/rasterizer_gles3.h index eff733f8443..b15e5af2947 100644 --- a/drivers/gles3/rasterizer_gles3.h +++ b/drivers/gles3/rasterizer_gles3.h @@ -71,6 +71,8 @@ public: virtual bool is_low_end() const { return false; } + const char *gl_check_for_error(bool p_print_error = true); + RasterizerGLES3(); ~RasterizerGLES3(); }; diff --git a/drivers/gles3/shaders/canvas.glsl b/drivers/gles3/shaders/canvas.glsl index e3a9e6d5d97..92f580a2b64 100644 --- a/drivers/gles3/shaders/canvas.glsl +++ b/drivers/gles3/shaders/canvas.glsl @@ -2,6 +2,11 @@ [vertex] layout(location = 0) in highp vec2 vertex; + +#ifdef USE_LIGHT_ANGLE +layout(location = 2) in highp float light_angle; +#endif + /* clang-format on */ layout(location = 3) in vec4 color_attrib; @@ -248,12 +253,32 @@ VERTEX_SHADER_CODE pos = outvec.xy; #endif +#ifdef USE_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); + 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 #endif }