Merge pull request #39256 from lawnjelly/tilemap_pixel2
GLES2 batching - Add UV precision adjustment for tilemaps
This commit is contained in:
commit
3c7e03510c
|
@ -1003,6 +1003,14 @@
|
||||||
<member name="rendering/batching/parameters/max_join_item_commands" type="int" setter="" getter="" default="16">
|
<member name="rendering/batching/parameters/max_join_item_commands" type="int" setter="" getter="" default="16">
|
||||||
Sets the number of commands to lookahead to determine whether to batch render items. A value of 1 can join items consisting of single commands, 0 turns off joining. Higher values are in theory more likely to join, however this has diminishing returns and has a runtime cost so a small value is recommended.
|
Sets the number of commands to lookahead to determine whether to batch render items. A value of 1 can join items consisting of single commands, 0 turns off joining. Higher values are in theory more likely to join, however this has diminishing returns and has a runtime cost so a small value is recommended.
|
||||||
</member>
|
</member>
|
||||||
|
<member name="rendering/batching/precision/uv_contract" type="bool" setter="" getter="" default="false">
|
||||||
|
On some platforms (especially mobile), precision issues in shaders can lead to reading 1 texel outside of bounds, particularly where rects are scaled. This can particularly lead to border artifacts around tiles in tilemaps.
|
||||||
|
This adjustment corrects for this by making a small contraction to the UV coordinates used. Note that this can result in a slight squashing of border texels.
|
||||||
|
</member>
|
||||||
|
<member name="rendering/batching/precision/uv_contract_amount" type="int" setter="" getter="" default="100">
|
||||||
|
The amount of UV contraction. This figure is divided by 1000000, and is a proportion of the total texture dimensions, where the width and height are both ranged from 0.0 to 1.0.
|
||||||
|
Use the default unless correcting for a problem on particular hardware.
|
||||||
|
</member>
|
||||||
<member name="rendering/environment/default_clear_color" type="Color" setter="" getter="" default="Color( 0.3, 0.3, 0.3, 1 )">
|
<member name="rendering/environment/default_clear_color" type="Color" setter="" getter="" default="Color( 0.3, 0.3, 0.3, 1 )">
|
||||||
Default background clear color. Overridable per [Viewport] using its [Environment]. See [member Environment.background_mode] and [member Environment.background_color] in particular. To change this default color programmatically, use [method VisualServer.set_default_clear_color].
|
Default background clear color. Overridable per [Viewport] using its [Environment]. See [member Environment.background_mode] and [member Environment.background_color] in particular. To change this default color programmatically, use [method VisualServer.set_default_clear_color].
|
||||||
</member>
|
</member>
|
||||||
|
@ -1042,6 +1050,7 @@
|
||||||
</member>
|
</member>
|
||||||
<member name="rendering/quality/2d/use_pixel_snap" type="bool" setter="" getter="" default="false">
|
<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.
|
If [code]true[/code], forces snapping of polygons to pixels in 2D rendering. May help in some pixel art styles.
|
||||||
|
Consider using the project setting [member rendering/batching/precision/uv_contract] to prevent artifacts.
|
||||||
</member>
|
</member>
|
||||||
<member name="rendering/quality/depth/hdr" type="bool" setter="" getter="" default="true">
|
<member name="rendering/quality/depth/hdr" type="bool" setter="" getter="" default="true">
|
||||||
If [code]true[/code], allocates the main framebuffer with high dynamic range. High dynamic range allows the use of [Color] values greater than 1.
|
If [code]true[/code], allocates the main framebuffer with high dynamic range. High dynamic range allows the use of [Color] values greater than 1.
|
||||||
|
|
|
@ -75,6 +75,9 @@ RasterizerCanvasGLES2::BatchData::BatchData() {
|
||||||
settings_use_single_rect_fallback = false;
|
settings_use_single_rect_fallback = false;
|
||||||
settings_light_max_join_items = 16;
|
settings_light_max_join_items = 16;
|
||||||
|
|
||||||
|
settings_uv_contract = false;
|
||||||
|
settings_uv_contract_amount = 0.0f;
|
||||||
|
|
||||||
stats_items_sorted = 0;
|
stats_items_sorted = 0;
|
||||||
stats_light_items_joined = 0;
|
stats_light_items_joined = 0;
|
||||||
}
|
}
|
||||||
|
@ -165,10 +168,12 @@ int RasterizerCanvasGLES2::_batch_find_or_create_tex(const RID &p_texture, const
|
||||||
if (texture) {
|
if (texture) {
|
||||||
new_batch_tex.tex_pixel_size.x = 1.0 / texture->width;
|
new_batch_tex.tex_pixel_size.x = 1.0 / texture->width;
|
||||||
new_batch_tex.tex_pixel_size.y = 1.0 / texture->height;
|
new_batch_tex.tex_pixel_size.y = 1.0 / texture->height;
|
||||||
|
new_batch_tex.flags = texture->flags;
|
||||||
} else {
|
} else {
|
||||||
// maybe doesn't need doing...
|
// maybe doesn't need doing...
|
||||||
new_batch_tex.tex_pixel_size.x = 1.0;
|
new_batch_tex.tex_pixel_size.x = 1.0;
|
||||||
new_batch_tex.tex_pixel_size.y = 1.0;
|
new_batch_tex.tex_pixel_size.y = 1.0;
|
||||||
|
new_batch_tex.flags = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p_tile) {
|
if (p_tile) {
|
||||||
|
@ -246,8 +251,9 @@ bool RasterizerCanvasGLES2::prefill_joined_item(FillState &r_fill_state, int &r_
|
||||||
int command_count = p_item->commands.size();
|
int command_count = p_item->commands.size();
|
||||||
Item::Command *const *commands = p_item->commands.ptr();
|
Item::Command *const *commands = p_item->commands.ptr();
|
||||||
|
|
||||||
// just a local, might be more efficient in a register (check)
|
// locals, might be more efficient in a register (check)
|
||||||
Vector2 texpixel_size = r_fill_state.texpixel_size;
|
Vector2 texpixel_size = r_fill_state.texpixel_size;
|
||||||
|
const float uv_epsilon = bdata.settings_uv_contract_amount;
|
||||||
|
|
||||||
// checking the color for not being white makes it 92/90 times faster in the case where it is white
|
// checking the color for not being white makes it 92/90 times faster in the case where it is white
|
||||||
bool multiply_final_modulate = false;
|
bool multiply_final_modulate = false;
|
||||||
|
@ -382,7 +388,12 @@ bool RasterizerCanvasGLES2::prefill_joined_item(FillState &r_fill_state, int &r_
|
||||||
|
|
||||||
if (change_batch) {
|
if (change_batch) {
|
||||||
// put the tex pixel size in a local (less verbose and can be a register)
|
// put the tex pixel size in a local (less verbose and can be a register)
|
||||||
bdata.batch_textures[r_fill_state.batch_tex_id].tex_pixel_size.to(texpixel_size);
|
const BatchTex &batchtex = bdata.batch_textures[r_fill_state.batch_tex_id];
|
||||||
|
batchtex.tex_pixel_size.to(texpixel_size);
|
||||||
|
|
||||||
|
if (bdata.settings_uv_contract) {
|
||||||
|
r_fill_state.contract_uvs = (batchtex.flags & VS::TEXTURE_FLAG_FILTER) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
// need to preserve texpixel_size between items
|
// need to preserve texpixel_size between items
|
||||||
r_fill_state.texpixel_size = texpixel_size;
|
r_fill_state.texpixel_size = texpixel_size;
|
||||||
|
@ -445,15 +456,34 @@ bool RasterizerCanvasGLES2::prefill_joined_item(FillState &r_fill_state, int &r_
|
||||||
}
|
}
|
||||||
|
|
||||||
// uvs
|
// uvs
|
||||||
Rect2 src_rect = (rect->flags & CANVAS_RECT_REGION) ? Rect2(rect->source.position * texpixel_size, rect->source.size * texpixel_size) : Rect2(0, 0, 1, 1);
|
Vector2 src_min;
|
||||||
|
Vector2 src_max;
|
||||||
|
if (rect->flags & CANVAS_RECT_REGION) {
|
||||||
|
src_min = rect->source.position;
|
||||||
|
src_max = src_min + rect->source.size;
|
||||||
|
|
||||||
|
src_min *= texpixel_size;
|
||||||
|
src_max *= texpixel_size;
|
||||||
|
|
||||||
|
// nudge offset for the maximum to prevent precision error on GPU reading into line outside the source rect
|
||||||
|
// this is very difficult to get right.
|
||||||
|
if (r_fill_state.contract_uvs) {
|
||||||
|
src_min.x += uv_epsilon;
|
||||||
|
src_min.y += uv_epsilon;
|
||||||
|
src_max.x -= uv_epsilon;
|
||||||
|
src_max.y -= uv_epsilon;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
src_min = Vector2(0, 0);
|
||||||
|
src_max = Vector2(1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
// 10% faster calculating the max first
|
// 10% faster calculating the max first
|
||||||
Vector2 pos_max = src_rect.position + src_rect.size;
|
|
||||||
Vector2 uvs[4] = {
|
Vector2 uvs[4] = {
|
||||||
src_rect.position,
|
src_min,
|
||||||
Vector2(pos_max.x, src_rect.position.y),
|
Vector2(src_max.x, src_min.y),
|
||||||
pos_max,
|
src_max,
|
||||||
Vector2(src_rect.position.x, pos_max.y),
|
Vector2(src_min.x, src_max.y),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (rect->flags & CANVAS_RECT_TRANSPOSE) {
|
if (rect->flags & CANVAS_RECT_TRANSPOSE) {
|
||||||
|
@ -2810,7 +2840,6 @@ void RasterizerCanvasGLES2::_canvas_render_item(Item *p_ci, RenderItemState &r_r
|
||||||
ci->final_modulate.b * p_modulate.b,
|
ci->final_modulate.b * p_modulate.b,
|
||||||
ci->final_modulate.a * p_modulate.a );
|
ci->final_modulate.a * p_modulate.a );
|
||||||
|
|
||||||
|
|
||||||
state.canvas_shader.set_uniform(CanvasShaderGLES2::MODELVIEW_MATRIX,state.final_transform);
|
state.canvas_shader.set_uniform(CanvasShaderGLES2::MODELVIEW_MATRIX,state.final_transform);
|
||||||
state.canvas_shader.set_uniform(CanvasShaderGLES2::EXTRA_MATRIX,Transform2D());
|
state.canvas_shader.set_uniform(CanvasShaderGLES2::EXTRA_MATRIX,Transform2D());
|
||||||
state.canvas_shader.set_uniform(CanvasShaderGLES2::FINAL_MODULATE,state.canvas_item_modulate);
|
state.canvas_shader.set_uniform(CanvasShaderGLES2::FINAL_MODULATE,state.canvas_item_modulate);
|
||||||
|
@ -3220,7 +3249,6 @@ void RasterizerCanvasGLES2::render_joined_item(const BItemJoined &p_bij, RenderI
|
||||||
ci->final_modulate.b * p_modulate.b,
|
ci->final_modulate.b * p_modulate.b,
|
||||||
ci->final_modulate.a * p_modulate.a );
|
ci->final_modulate.a * p_modulate.a );
|
||||||
|
|
||||||
|
|
||||||
state.canvas_shader.set_uniform(CanvasShaderGLES2::MODELVIEW_MATRIX,state.final_transform);
|
state.canvas_shader.set_uniform(CanvasShaderGLES2::MODELVIEW_MATRIX,state.final_transform);
|
||||||
state.canvas_shader.set_uniform(CanvasShaderGLES2::EXTRA_MATRIX,Transform2D());
|
state.canvas_shader.set_uniform(CanvasShaderGLES2::EXTRA_MATRIX,Transform2D());
|
||||||
state.canvas_shader.set_uniform(CanvasShaderGLES2::FINAL_MODULATE,state.canvas_item_modulate);
|
state.canvas_shader.set_uniform(CanvasShaderGLES2::FINAL_MODULATE,state.canvas_item_modulate);
|
||||||
|
@ -3346,6 +3374,11 @@ void RasterizerCanvasGLES2::initialize() {
|
||||||
bdata.settings_light_max_join_items = GLOBAL_GET("rendering/batching/lights/max_join_items");
|
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_single_rect_fallback = GLOBAL_GET("rendering/batching/options/single_rect_fallback");
|
||||||
|
|
||||||
|
// 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
|
// 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");
|
bdata.settings_scissor_threshold = GLOBAL_GET("rendering/batching/lights/scissor_area_threshold");
|
||||||
if (bdata.settings_scissor_threshold > 0.999f) {
|
if (bdata.settings_scissor_threshold > 0.999f) {
|
||||||
|
|
|
@ -113,6 +113,7 @@ class RasterizerCanvasGLES2 : public RasterizerCanvasBaseGLES2 {
|
||||||
RID RID_normal;
|
RID RID_normal;
|
||||||
TileMode tile_mode;
|
TileMode tile_mode;
|
||||||
BatchVector2 tex_pixel_size;
|
BatchVector2 tex_pixel_size;
|
||||||
|
uint32_t flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
// items in a list to be sorted prior to joining
|
// items in a list to be sorted prior to joining
|
||||||
|
@ -237,6 +238,10 @@ class RasterizerCanvasGLES2 : public RasterizerCanvasBaseGLES2 {
|
||||||
bool settings_use_single_rect_fallback;
|
bool settings_use_single_rect_fallback;
|
||||||
int settings_light_max_join_items;
|
int settings_light_max_join_items;
|
||||||
|
|
||||||
|
// uv contraction
|
||||||
|
bool settings_uv_contract;
|
||||||
|
float settings_uv_contract_amount;
|
||||||
|
|
||||||
// only done on diagnose frame
|
// only done on diagnose frame
|
||||||
void reset_stats() {
|
void reset_stats() {
|
||||||
stats_items_sorted = 0;
|
stats_items_sorted = 0;
|
||||||
|
@ -278,10 +283,12 @@ class RasterizerCanvasGLES2 : public RasterizerCanvasBaseGLES2 {
|
||||||
curr_batch = 0;
|
curr_batch = 0;
|
||||||
batch_tex_id = -1;
|
batch_tex_id = -1;
|
||||||
texpixel_size = Vector2(1, 1);
|
texpixel_size = Vector2(1, 1);
|
||||||
|
contract_uvs = false;
|
||||||
}
|
}
|
||||||
Batch *curr_batch;
|
Batch *curr_batch;
|
||||||
int batch_tex_id;
|
int batch_tex_id;
|
||||||
bool use_hardware_transform;
|
bool use_hardware_transform;
|
||||||
|
bool contract_uvs;
|
||||||
Vector2 texpixel_size;
|
Vector2 texpixel_size;
|
||||||
Color final_modulate;
|
Color final_modulate;
|
||||||
TransformMode transform_mode;
|
TransformMode transform_mode;
|
||||||
|
|
|
@ -2432,6 +2432,8 @@ VisualServer::VisualServer() {
|
||||||
GLOBAL_DEF("rendering/batching/debug/flash_batching", false);
|
GLOBAL_DEF("rendering/batching/debug/flash_batching", false);
|
||||||
GLOBAL_DEF("rendering/batching/debug/diagnose_frame", false);
|
GLOBAL_DEF("rendering/batching/debug/diagnose_frame", false);
|
||||||
GLOBAL_DEF("rendering/gles2/compatibility/disable_half_float", false);
|
GLOBAL_DEF("rendering/gles2/compatibility/disable_half_float", false);
|
||||||
|
GLOBAL_DEF("rendering/batching/precision/uv_contract", false);
|
||||||
|
GLOBAL_DEF("rendering/batching/precision/uv_contract_amount", 100);
|
||||||
|
|
||||||
ProjectSettings::get_singleton()->set_custom_property_info("rendering/batching/parameters/max_join_item_commands", PropertyInfo(Variant::INT, "rendering/batching/parameters/max_join_item_commands", PROPERTY_HINT_RANGE, "0,65535"));
|
ProjectSettings::get_singleton()->set_custom_property_info("rendering/batching/parameters/max_join_item_commands", PropertyInfo(Variant::INT, "rendering/batching/parameters/max_join_item_commands", PROPERTY_HINT_RANGE, "0,65535"));
|
||||||
ProjectSettings::get_singleton()->set_custom_property_info("rendering/batching/parameters/colored_vertex_format_threshold", PropertyInfo(Variant::REAL, "rendering/batching/parameters/colored_vertex_format_threshold", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"));
|
ProjectSettings::get_singleton()->set_custom_property_info("rendering/batching/parameters/colored_vertex_format_threshold", PropertyInfo(Variant::REAL, "rendering/batching/parameters/colored_vertex_format_threshold", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"));
|
||||||
|
@ -2439,6 +2441,7 @@ VisualServer::VisualServer() {
|
||||||
ProjectSettings::get_singleton()->set_custom_property_info("rendering/batching/lights/scissor_area_threshold", PropertyInfo(Variant::REAL, "rendering/batching/lights/scissor_area_threshold", PROPERTY_HINT_RANGE, "0.0,1.0"));
|
ProjectSettings::get_singleton()->set_custom_property_info("rendering/batching/lights/scissor_area_threshold", PropertyInfo(Variant::REAL, "rendering/batching/lights/scissor_area_threshold", PROPERTY_HINT_RANGE, "0.0,1.0"));
|
||||||
ProjectSettings::get_singleton()->set_custom_property_info("rendering/batching/lights/max_join_items", PropertyInfo(Variant::INT, "rendering/batching/lights/max_join_items", PROPERTY_HINT_RANGE, "0,512"));
|
ProjectSettings::get_singleton()->set_custom_property_info("rendering/batching/lights/max_join_items", PropertyInfo(Variant::INT, "rendering/batching/lights/max_join_items", PROPERTY_HINT_RANGE, "0,512"));
|
||||||
ProjectSettings::get_singleton()->set_custom_property_info("rendering/batching/parameters/item_reordering_lookahead", PropertyInfo(Variant::INT, "rendering/batching/parameters/item_reordering_lookahead", PROPERTY_HINT_RANGE, "0,256"));
|
ProjectSettings::get_singleton()->set_custom_property_info("rendering/batching/parameters/item_reordering_lookahead", PropertyInfo(Variant::INT, "rendering/batching/parameters/item_reordering_lookahead", PROPERTY_HINT_RANGE, "0,256"));
|
||||||
|
ProjectSettings::get_singleton()->set_custom_property_info("rendering/batching/precision/uv_contract_amount", PropertyInfo(Variant::INT, "rendering/batching/precision/uv_contract_amount", PROPERTY_HINT_RANGE, "0,10000"));
|
||||||
}
|
}
|
||||||
|
|
||||||
VisualServer::~VisualServer() {
|
VisualServer::~VisualServer() {
|
||||||
|
|
Loading…
Reference in New Issue