Add Nvidia Workaround for GLES3

Ported GLES2 workaround code to GLES3.
This commit is contained in:
lawnjelly 2020-05-05 16:25:45 +01:00
parent 786e55738e
commit dcb19ed735
5 changed files with 160 additions and 74 deletions

View File

@ -1034,9 +1034,9 @@
<member name="rendering/limits/time/time_rollover_secs" type="float" setter="" getter="" default="3600">
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).
</member>
<member name="rendering/quality/2d/gles2_use_nvidia_rect_flicker_workaround" type="bool" setter="" getter="" default="false">
<member name="rendering/quality/2d/use_nvidia_rect_flicker_workaround" type="bool" setter="" getter="" default="false">
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.
</member>
<member name="rendering/quality/2d/use_pixel_snap" type="bool" setter="" getter="" default="false">
If [code]true[/code], forces snapping of polygons to pixels in 2D rendering. May help in some pixel art styles.

View File

@ -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;

View File

@ -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<Item::CommandRect *>(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<Item::CommandNinePatch *>(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<Item::CommandPrimitive *>(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
}

View File

@ -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();

View File

@ -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