diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index e3084c09caa..1f364dcd20f 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -1034,9 +1034,9 @@ Shaders have a time variable that constantly increases. At some point, it needs to be rolled back to zero to avoid precision errors on shader animations. This setting specifies when (in seconds). - + Some NVIDIA GPU drivers have a bug which produces flickering issues for the [code]draw_rect[/code] method, especially as used in [TileMap]. Refer to [url=https://github.com/godotengine/godot/issues/9913]GitHub issue 9913[/url] for details. - If [code]true[/code], this option enables a "safe" code path for such NVIDIA GPUs at the cost of performance. This option only impacts the GLES2 rendering backend (so the bug stays if you use GLES3), and only desktop platforms. + If [code]true[/code], this option enables a "safe" code path for such NVIDIA GPUs at the cost of performance. This option affects GLES2 and GLES3 rendering, but only on desktop platforms. If [code]true[/code], forces snapping of polygons to pixels in 2D rendering. May help in some pixel art styles. diff --git a/drivers/gles2/rasterizer_canvas_base_gles2.cpp b/drivers/gles2/rasterizer_canvas_base_gles2.cpp index 1c5e9ce4566..5ecbcf0e137 100644 --- a/drivers/gles2/rasterizer_canvas_base_gles2.cpp +++ b/drivers/gles2/rasterizer_canvas_base_gles2.cpp @@ -1008,7 +1008,7 @@ void RasterizerCanvasBaseGLES2::finalize() { RasterizerCanvasBaseGLES2::RasterizerCanvasBaseGLES2() { #ifdef GLES_OVER_GL - use_nvidia_rect_workaround = GLOBAL_GET("rendering/quality/2d/gles2_use_nvidia_rect_flicker_workaround"); + 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; diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp index fd99d9e1517..71c85bc9728 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.cpp +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -621,6 +621,82 @@ static const GLenum gl_primitive[] = { GL_TRIANGLE_FAN }; +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 untile = false; + + if (p_rect->flags & CANVAS_RECT_TILE && !(p_texture->flags & VS::TEXTURE_FLAG_REPEAT)) { + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + untile = true; + } + + Size2 texpixel_size(1.0 / p_texture->width, 1.0 / p_texture->height); + + state.canvas_shader.set_uniform(CanvasShaderGLES3::CLIP_RECT_UV, p_rect->flags & CANVAS_RECT_CLIP_UV); + + Vector2 points[4] = { + p_rect->rect.position, + p_rect->rect.position + Vector2(p_rect->rect.size.x, 0.0), + p_rect->rect.position + p_rect->rect.size, + p_rect->rect.position + Vector2(0.0, p_rect->rect.size.y), + }; + + if (p_rect->rect.size.x < 0) { + SWAP(points[0], points[1]); + SWAP(points[2], points[3]); + } + if (p_rect->rect.size.y < 0) { + SWAP(points[0], points[3]); + SWAP(points[1], points[2]); + } + Rect2 src_rect = (p_rect->flags & CANVAS_RECT_REGION) ? Rect2(p_rect->source.position * texpixel_size, p_rect->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), + }; + + if (p_rect->flags & CANVAS_RECT_TRANSPOSE) { + SWAP(uvs[1], uvs[3]); + } + + if (p_rect->flags & CANVAS_RECT_FLIP_H) { + SWAP(uvs[0], uvs[1]); + SWAP(uvs[2], uvs[3]); + } + if (p_rect->flags & CANVAS_RECT_FLIP_V) { + SWAP(uvs[0], uvs[3]); + SWAP(uvs[1], uvs[2]); + } + + _draw_gui_primitive(4, points, NULL, uvs); + + if (untile) { + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + + } else { + state.canvas_shader.set_uniform(CanvasShaderGLES3::CLIP_RECT_UV, false); + + Vector2 points[4] = { + p_rect->rect.position, + p_rect->rect.position + Vector2(p_rect->rect.size.x, 0.0), + p_rect->rect.position + p_rect->rect.size, + p_rect->rect.position + Vector2(0.0, p_rect->rect.size.y), + }; + + _draw_gui_primitive(4, points, NULL, nullptr); + } +} + void RasterizerCanvasGLES3::_canvas_item_render_commands(Item *p_item, Item *current_clip, bool &reclip) { int cc = p_item->commands.size(); @@ -740,83 +816,86 @@ void RasterizerCanvasGLES3::_canvas_item_render_commands(Item *p_item, Item *cur Item::CommandRect *rect = static_cast(c); - _set_texture_rect_mode(true); - //set color glVertexAttrib4f(VS::ARRAY_COLOR, rect->modulate.r, rect->modulate.g, rect->modulate.b, rect->modulate.a); RasterizerStorageGLES3::Texture *texture = _bind_canvas_texture(rect->texture, rect->normal_map); - if (texture) { - - bool untile = false; - - if (rect->flags & CANVAS_RECT_TILE && !(texture->flags & VS::TEXTURE_FLAG_REPEAT)) { - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - untile = true; - } - - Size2 texpixel_size(1.0 / texture->width, 1.0 / texture->height); - Rect2 src_rect = (rect->flags & CANVAS_RECT_REGION) ? Rect2(rect->source.position * texpixel_size, rect->source.size * texpixel_size) : Rect2(0, 0, 1, 1); - Rect2 dst_rect = Rect2(rect->rect.position, rect->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 (rect->flags & CANVAS_RECT_FLIP_H) { - src_rect.size.x *= -1; - } - - if (rect->flags & CANVAS_RECT_FLIP_V) { - src_rect.size.y *= -1; - } - - if (rect->flags & CANVAS_RECT_TRANSPOSE) { - dst_rect.size.x *= -1; // Encoding in the dst_rect.z uniform - } - - state.canvas_shader.set_uniform(CanvasShaderGLES3::COLOR_TEXPIXEL_SIZE, texpixel_size); - - 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)); - state.canvas_shader.set_uniform(CanvasShaderGLES3::CLIP_RECT_UV, rect->flags & CANVAS_RECT_CLIP_UV); - - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - storage->info.render._2d_draw_call_count++; - - if (untile) { - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - } - + if (use_nvidia_rect_workaround) { + render_rect_nvidia_workaround(rect, texture); } else { - Rect2 dst_rect = Rect2(rect->rect.position, rect->rect.size); - if (dst_rect.size.width < 0) { - dst_rect.position.x += dst_rect.size.width; - dst_rect.size.width *= -1; + _set_texture_rect_mode(true); + + if (texture) { + + bool untile = false; + + if (rect->flags & CANVAS_RECT_TILE && !(texture->flags & VS::TEXTURE_FLAG_REPEAT)) { + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + untile = true; + } + + Size2 texpixel_size(1.0 / texture->width, 1.0 / texture->height); + Rect2 src_rect = (rect->flags & CANVAS_RECT_REGION) ? Rect2(rect->source.position * texpixel_size, rect->source.size * texpixel_size) : Rect2(0, 0, 1, 1); + Rect2 dst_rect = Rect2(rect->rect.position, rect->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 (rect->flags & CANVAS_RECT_FLIP_H) { + src_rect.size.x *= -1; + } + + if (rect->flags & CANVAS_RECT_FLIP_V) { + src_rect.size.y *= -1; + } + + if (rect->flags & CANVAS_RECT_TRANSPOSE) { + dst_rect.size.x *= -1; // Encoding in the dst_rect.z uniform + } + + state.canvas_shader.set_uniform(CanvasShaderGLES3::COLOR_TEXPIXEL_SIZE, texpixel_size); + + 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)); + state.canvas_shader.set_uniform(CanvasShaderGLES3::CLIP_RECT_UV, rect->flags & CANVAS_RECT_CLIP_UV); + + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + storage->info.render._2d_draw_call_count++; + + if (untile) { + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + + } else { + Rect2 dst_rect = Rect2(rect->rect.position, rect->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)); + state.canvas_shader.set_uniform(CanvasShaderGLES3::CLIP_RECT_UV, false); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + storage->info.render._2d_draw_call_count++; } - 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)); - state.canvas_shader.set_uniform(CanvasShaderGLES3::CLIP_RECT_UV, false); - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - storage->info.render._2d_draw_call_count++; - } - + } // if not use nvidia workaround } break; - case Item::Command::TYPE_NINEPATCH: { Item::CommandNinePatch *np = static_cast(c); @@ -858,7 +937,6 @@ void RasterizerCanvasGLES3::_canvas_item_render_commands(Item *p_item, Item *cur storage->info.render._2d_draw_call_count++; } break; - case Item::Command::TYPE_PRIMITIVE: { Item::CommandPrimitive *primitive = static_cast(c); @@ -2265,4 +2343,10 @@ void RasterizerCanvasGLES3::finalize() { } RasterizerCanvasGLES3::RasterizerCanvasGLES3() { + // Not needed (a priori) on GLES devices + use_nvidia_rect_workaround = false; + +#ifdef GLES_OVER_GL + use_nvidia_rect_workaround = GLOBAL_GET("rendering/quality/2d/use_nvidia_rect_flicker_workaround"); +#endif } diff --git a/drivers/gles3/rasterizer_canvas_gles3.h b/drivers/gles3/rasterizer_canvas_gles3.h index 929867337dc..f67d900da33 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.h +++ b/drivers/gles3/rasterizer_canvas_gles3.h @@ -95,6 +95,7 @@ public: } state; RasterizerStorageGLES3 *storage; + bool use_nvidia_rect_workaround; struct LightInternal : public RID_Data { @@ -146,6 +147,7 @@ public: 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); + void render_rect_nvidia_workaround(const Item::CommandRect *p_rect, const RasterizerStorageGLES3::Texture *p_texture); void initialize(); void finalize(); diff --git a/main/main.cpp b/main/main.cpp index b3dc453bee0..362a7c7c9e1 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -995,8 +995,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph GLOBAL_DEF("rendering/quality/driver/fallback_to_gles2", false); - // Assigning here even though it's GLES2-specific, to be sure that it appears in docs - GLOBAL_DEF("rendering/quality/2d/gles2_use_nvidia_rect_flicker_workaround", false); + // Assigning here, to be sure that it appears in docs + GLOBAL_DEF("rendering/quality/2d/use_nvidia_rect_flicker_workaround", false); GLOBAL_DEF("display/window/size/width", 1024); ProjectSettings::get_singleton()->set_custom_property_info("display/window/size/width", PropertyInfo(Variant::INT, "display/window/size/width", PROPERTY_HINT_RANGE, "0,7680,or_greater")); // 8K resolution