GLES2: Make Nvidia flicker workaround opt-in
It has a big impact on 2D and text rendering performance (cf. #24466) so the solution seems worse than the bug it aims to work around. It's now opt-in via "rendering/quality/2d/gles2_use_nvidia_rect_flicker_workaround" for those who need it and have a simple enough game for the performance drop not to be an issue. Fixes #24466.
This commit is contained in:
parent
6f884cc884
commit
02ffc59270
5 changed files with 163 additions and 147 deletions
|
@ -1163,8 +1163,6 @@ ProjectSettings::ProjectSettings() {
|
|||
GLOBAL_DEF("input/ui_end", action);
|
||||
input_presets.push_back("input/ui_end");
|
||||
|
||||
//GLOBAL_DEF("display/window/handheld/orientation", "landscape");
|
||||
|
||||
custom_prop_info["display/window/handheld/orientation"] = PropertyInfo(Variant::STRING, "display/window/handheld/orientation", PROPERTY_HINT_ENUM, "landscape,portrait,reverse_landscape,reverse_portrait,sensor_landscape,sensor_portrait,sensor");
|
||||
custom_prop_info["rendering/threads/thread_model"] = PropertyInfo(Variant::INT, "rendering/threads/thread_model", PROPERTY_HINT_ENUM, "Single-Unsafe,Single-Safe,Multi-Threaded");
|
||||
custom_prop_info["physics/2d/thread_model"] = PropertyInfo(Variant::INT, "physics/2d/thread_model", PROPERTY_HINT_ENUM, "Single-Unsafe,Single-Safe,Multi-Threaded");
|
||||
|
|
|
@ -670,6 +670,10 @@
|
|||
<member name="rendering/limits/time/time_rollover_secs" type="float" setter="" getter="">
|
||||
Shaders have a time variable that constantly increases. At some point it needs to be rolled back to zero to avoid numerical errors on shader animations. This setting specifies when.
|
||||
</member>
|
||||
<member name="rendering/quality/2d/gles2_use_nvidia_rect_flicker_workaround" type="bool" setter="" getter="">
|
||||
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 https://github.com/godotengine/godot/issues/9913 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. Default value: [code]false[/code].
|
||||
</member>
|
||||
<member name="rendering/quality/2d/use_pixel_snap" type="bool" setter="" getter="">
|
||||
Force snapping of polygons to pixels in 2D rendering. May help in some pixel art styles.
|
||||
</member>
|
||||
|
|
|
@ -489,161 +489,164 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur
|
|||
glDisableVertexAttribArray(VS::ARRAY_COLOR);
|
||||
glVertexAttrib4fv(VS::ARRAY_COLOR, r->modulate.components);
|
||||
|
||||
//use a more compatible workaround, as this does not fail on nvidia
|
||||
#ifdef GLES_OVER_GL
|
||||
//more compatible
|
||||
state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, 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) {
|
||||
state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, false);
|
||||
|
||||
if (state.canvas_shader.bind()) {
|
||||
_set_uniforms();
|
||||
state.canvas_shader.use_material((void *)p_material);
|
||||
}
|
||||
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]);
|
||||
}
|
||||
|
||||
RasterizerStorageGLES2::Texture *texture = _bind_canvas_texture(r->texture, r->normal_map);
|
||||
|
||||
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),
|
||||
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->flags & CANVAS_RECT_TRANSPOSE) {
|
||||
SWAP(uvs[1], uvs[3]);
|
||||
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]);
|
||||
}
|
||||
|
||||
if (r->flags & CANVAS_RECT_FLIP_H) {
|
||||
SWAP(uvs[0], uvs[1]);
|
||||
SWAP(uvs[2], uvs[3]);
|
||||
}
|
||||
if (r->flags & CANVAS_RECT_FLIP_V) {
|
||||
SWAP(uvs[0], uvs[3]);
|
||||
SWAP(uvs[1], uvs[2]);
|
||||
RasterizerStorageGLES2::Texture *texture = _bind_canvas_texture(r->texture, r->normal_map);
|
||||
|
||||
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),
|
||||
};
|
||||
|
||||
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]);
|
||||
}
|
||||
if (r->flags & CANVAS_RECT_FLIP_V) {
|
||||
SWAP(uvs[0], uvs[3]);
|
||||
SWAP(uvs[1], uvs[2]);
|
||||
}
|
||||
|
||||
state.canvas_shader.set_uniform(CanvasShaderGLES2::COLOR_TEXPIXEL_SIZE, texpixel_size);
|
||||
|
||||
bool untile = false;
|
||||
|
||||
if (r->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;
|
||||
}
|
||||
|
||||
_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(CanvasShaderGLES2::COLOR_TEXPIXEL_SIZE, Vector2());
|
||||
_draw_gui_primitive(4, points, NULL, NULL);
|
||||
}
|
||||
|
||||
state.canvas_shader.set_uniform(CanvasShaderGLES2::COLOR_TEXPIXEL_SIZE, texpixel_size);
|
||||
|
||||
bool untile = false;
|
||||
|
||||
if (r->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;
|
||||
}
|
||||
|
||||
_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(CanvasShaderGLES2::COLOR_TEXPIXEL_SIZE, Vector2());
|
||||
_draw_gui_primitive(4, points, NULL, NULL);
|
||||
// This branch is better for performance, but can produce flicker on Nvidia, see above comment.
|
||||
_bind_quad_buffer();
|
||||
|
||||
state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, true);
|
||||
|
||||
if (state.canvas_shader.bind()) {
|
||||
_set_uniforms();
|
||||
state.canvas_shader.use_material((void *)p_material);
|
||||
}
|
||||
|
||||
RasterizerStorageGLES2::Texture *tex = _bind_canvas_texture(r->texture, r->normal_map);
|
||||
|
||||
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(CanvasShaderGLES2::DST_RECT, Color(dst_rect.position.x, dst_rect.position.y, dst_rect.size.x, dst_rect.size.y));
|
||||
state.canvas_shader.set_uniform(CanvasShaderGLES2::SRC_RECT, Color(0, 0, 1, 1));
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
||||
} else {
|
||||
|
||||
bool untile = false;
|
||||
|
||||
if (r->flags & CANVAS_RECT_TILE && !(tex->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 / 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 (r->flags & CANVAS_RECT_FLIP_V) {
|
||||
src_rect.size.y *= -1;
|
||||
}
|
||||
|
||||
if (r->flags & CANVAS_RECT_TRANSPOSE) {
|
||||
dst_rect.size.x *= -1; // Encoding in the dst_rect.z uniform
|
||||
}
|
||||
|
||||
state.canvas_shader.set_uniform(CanvasShaderGLES2::COLOR_TEXPIXEL_SIZE, texpixel_size);
|
||||
|
||||
state.canvas_shader.set_uniform(CanvasShaderGLES2::DST_RECT, Color(dst_rect.position.x, dst_rect.position.y, dst_rect.size.x, dst_rect.size.y));
|
||||
state.canvas_shader.set_uniform(CanvasShaderGLES2::SRC_RECT, Color(src_rect.position.x, src_rect.position.y, src_rect.size.x, src_rect.size.y));
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
}
|
||||
|
||||
#else
|
||||
//disabled because it fails on buggy nvidia drivers
|
||||
_bind_quad_buffer();
|
||||
|
||||
state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, true);
|
||||
|
||||
if (state.canvas_shader.bind()) {
|
||||
_set_uniforms();
|
||||
state.canvas_shader.use_material((void *)p_material);
|
||||
}
|
||||
|
||||
RasterizerStorageGLES2::Texture *tex = _bind_canvas_texture(r->texture, r->normal_map);
|
||||
|
||||
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(CanvasShaderGLES2::DST_RECT, Color(dst_rect.position.x, dst_rect.position.y, dst_rect.size.x, dst_rect.size.y));
|
||||
state.canvas_shader.set_uniform(CanvasShaderGLES2::SRC_RECT, Color(0, 0, 1, 1));
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
||||
} else {
|
||||
|
||||
bool untile = false;
|
||||
|
||||
if (r->flags & CANVAS_RECT_TILE && !(tex->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 / 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 (r->flags & CANVAS_RECT_FLIP_V) {
|
||||
src_rect.size.y *= -1;
|
||||
}
|
||||
|
||||
if (r->flags & CANVAS_RECT_TRANSPOSE) {
|
||||
dst_rect.size.x *= -1; // Encoding in the dst_rect.z uniform
|
||||
}
|
||||
|
||||
state.canvas_shader.set_uniform(CanvasShaderGLES2::COLOR_TEXPIXEL_SIZE, texpixel_size);
|
||||
|
||||
state.canvas_shader.set_uniform(CanvasShaderGLES2::DST_RECT, Color(dst_rect.position.x, dst_rect.position.y, dst_rect.size.x, dst_rect.size.y));
|
||||
state.canvas_shader.set_uniform(CanvasShaderGLES2::SRC_RECT, Color(src_rect.position.x, src_rect.position.y, src_rect.size.x, src_rect.size.y));
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
#endif
|
||||
} break;
|
||||
|
||||
case Item::Command::TYPE_NINEPATCH: {
|
||||
|
@ -1856,4 +1859,10 @@ void RasterizerCanvasGLES2::finalize() {
|
|||
}
|
||||
|
||||
RasterizerCanvasGLES2::RasterizerCanvasGLES2() {
|
||||
#ifdef GLES_OVER_GL
|
||||
use_nvidia_rect_workaround = GLOBAL_GET("rendering/quality/2d/gles2_use_nvidia_rect_flicker_workaround");
|
||||
#else
|
||||
// Not needed (a priori) on GLES devices
|
||||
use_nvidia_rect_workaround = false;
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -102,6 +102,8 @@ public:
|
|||
|
||||
RasterizerStorageGLES2 *storage;
|
||||
|
||||
bool use_nvidia_rect_workaround;
|
||||
|
||||
virtual RID light_internal_create();
|
||||
virtual void light_internal_update(RID p_rid, Light *p_light);
|
||||
virtual void light_internal_free(RID p_rid);
|
||||
|
|
|
@ -880,6 +880,9 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
|
|||
GLOBAL_DEF("rendering/quality/driver/driver_fallback", "Best");
|
||||
ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/driver/driver_fallback", PropertyInfo(Variant::STRING, "rendering/quality/driver/driver_fallback", PROPERTY_HINT_ENUM, "Best,Never"));
|
||||
|
||||
// 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);
|
||||
|
||||
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
|
||||
GLOBAL_DEF("display/window/size/height", 600);
|
||||
|
|
Loading…
Reference in a new issue