Having artifacts when changing display_mode or resolution

Godot Version

4.3

Question

Hi, I am relatively new to the engine and godot as a whole so I have troubles figuring out the causes of my current issues I am currently developing a sort of 2D action/adventure game with a pixel art-y aesthetic.
Until now I ran my project with these settings:

window/size/viewport_width=640
window/size/viewport_height=360
window/size/window_width_override=1920
window/size/window_height_override=1080
window/stretch/mode="canvas_items"
window/stretch/aspect="expand"
window/stretch/scale_mode="fractional"

I just recently added options in-game for Display Mode and resolution.
The options I provide are full screen, borderless windowed, windowed. With different resolution options. Current implementation is as such:


const RESOLUTION_OPTIONS: Array[Dictionary] = [
	{"value": Vector2i(1280, 720), "label": "1280x720"},
	{"value": Vector2i(1920, 1080), "label": "1920x1080"},
	{"value": Vector2i(2560, 1440), "label": "2560x1440"}
]


const WINDOW_MODE_BORDERLESS_WINDOWED: int = -1
const WINDOW_MODE_BORDERLESS_MAXIMIZED: int = -2
func _update_display() -> void:
	var val: Variant = get_value(DISPLAY_MODE)
	if typeof(val) != TYPE_INT:
		return

	match val:
		DisplayServer.WindowMode.WINDOW_MODE_FULLSCREEN:
			DisplayServer.window_set_mode(DisplayServer.WindowMode.WINDOW_MODE_FULLSCREEN)
		DisplayServer.WindowMode.WINDOW_MODE_EXCLUSIVE_FULLSCREEN:
			DisplayServer.window_set_mode(DisplayServer.WindowMode.WINDOW_MODE_EXCLUSIVE_FULLSCREEN)
		DisplayServer.WindowMode.WINDOW_MODE_WINDOWED:
			DisplayServer.window_set_mode(DisplayServer.WindowMode.WINDOW_MODE_WINDOWED)
			DisplayServer.window_set_flag(DisplayServer.WINDOW_FLAG_BORDERLESS, false)
			_update_resolution()
		DisplayServer.WindowMode.WINDOW_MODE_MAXIMIZED:
			DisplayServer.window_set_flag(DisplayServer.WINDOW_FLAG_BORDERLESS, false)
			DisplayServer.window_set_mode(DisplayServer.WindowMode.WINDOW_MODE_MAXIMIZED)
		WINDOW_MODE_BORDERLESS_WINDOWED:
			DisplayServer.window_set_mode(DisplayServer.WindowMode.WINDOW_MODE_WINDOWED)
			DisplayServer.window_set_flag(DisplayServer.WINDOW_FLAG_BORDERLESS, false)
			_update_resolution()
			DisplayServer.window_set_flag(DisplayServer.WINDOW_FLAG_BORDERLESS, true)
		WINDOW_MODE_BORDERLESS_MAXIMIZED:
			DisplayServer.window_set_flag(DisplayServer.WINDOW_FLAG_BORDERLESS, false)
			DisplayServer.window_set_mode(DisplayServer.WindowMode.WINDOW_MODE_MAXIMIZED)
			_update_resolution()
			DisplayServer.window_set_flag(DisplayServer.WINDOW_FLAG_BORDERLESS, true)

		_:
			Log.e("Display value not supported")

the code for my camera is


extends Camera2D

var following_player: bool = true

var drag_vertically: bool = true
var drag_horizontally: bool = true
var horizontal_offset: float = 0.0
var vertical_offset: float = 0.0
var margin_left: float = 0.2
var margin_top: float = 0.2
var margin_right: float = 0.2
var margin_bottom: float = 0.2

var shake_fade: float = 5.0
var shake_max_intensity: float = 2.0
var shake_intensity: float = 0.0
var shake_offset: Vector2 = Vector2.ZERO

var default_zoom: Vector2 = Vector2(1, 1)  # Normal zoom
var interaction_zoom: Vector2 = Vector2(2, 2)  # Zoomed in

@onready var shake_timer: Timer = %ShakeTimer


func _ready() -> void:
	_refresh_user_settings()
	update_camera_properties()
	shake_timer.timeout.connect(_on_shake_timeout)
	EventBus.shake_please.connect(_shake)
	EventBus.save_loaded.connect(_refresh_user_settings)
	EventBus.config_change.connect(_user_settings_change)
	EventBus.request_zoom.connect(_start_zoom_body)
	EventBus.request_zoom_end.connect(_end_zoom)


func _process(delta: float) -> void:
	if shake_intensity > 0:
		shake_intensity = lerpf(shake_intensity, 0, shake_fade * delta)
		offset = _get_shake_offset()


func _physics_process(_delta: float) -> void:
	if following_player and Lib.player_reference != null:
		var target_position: Vector2 = (
			(Lib.player_reference.global_position if Lib.player_reference != null else Vector2.ZERO)
			+ Vector2(horizontal_offset, vertical_offset)
		)
		position = position.lerp(target_position + shake_offset, 0.1)


func _start_zoom_body(item: PhysicsBody2D) -> void:
	following_player = false
	position = item.get_global_position()  # Center on NPC
	set_zoom(default_zoom * 2)


func _end_zoom() -> void:
	following_player = true
	set_zoom(default_zoom)


func update_camera_properties() -> void:
	drag_horizontal_enabled = drag_horizontally
	drag_vertical_enabled = drag_vertically

	drag_left_margin = round(margin_left * default_zoom.x)
	drag_top_margin = round(margin_top * default_zoom.y)
	drag_right_margin = round(margin_right * default_zoom.x)
	drag_bottom_margin = round(margin_bottom * default_zoom.y)


func _shake(duration: float = 0.3) -> void:
	shake_timer.wait_time = duration
	shake_timer.start()
	shake_intensity = shake_max_intensity


func _on_shake_timeout() -> void:
	offset = Vector2.ZERO
	shake_intensity = 0


func _get_shake_offset() -> Vector2:
	return Vector2(randf_range(-shake_intensity, shake_intensity), randf_range(-shake_intensity, shake_intensity)).round()


func _refresh_user_settings() -> void:
	var screen_shake_value: int = Configuration.game.get_value(ConfigurationGame.SCREEN_SHAKE)
	shake_max_intensity = float(screen_shake_value / 8.0)


func _user_settings_change(_section: String, key: String, _value: Variant) -> void:
	if key == ConfigurationGame.SCREEN_SHAKE:
		_refresh_user_settings()

If it helps I have a NVIDIA Geforce RTX 3060ti and a ryzen 9 5900x

Ideally I want my game to:

  • Resize always to the size of the viewport (full screen…)
  • Do not show black bars on the sides
  • Do not show any artifact on the sprite and UI sides
  • Be able to use svgs for icons and UI elements
  • Have text scale and UI elements scale correctly

This is where my issues began. I quickly realized I have A LOT of artifacts when changing displays and/or resolution.
For example, keeping the same settings in my project, going into fullscreen I now have sprites/images being a lot more jittery and displaying some artifacts

Image from Gyazo
Image from Gyazo
Image from Gyazo
Image from Gyazo

So, I tried playing around with the settings.
Using the Snapping transform to pixels and vertices to pixels introduced a lot more issues: Movement looks a lot more stuttery and artifacts are still there

Image from Gyazo

So after this I tried to switch to “integer” for the scale mode but artifacts still remain on both sprites and UI

Image from Gyazo
Image from Gyazo

Just switching to “viewport” as stretch mode with fractional does not work either, UI elements now have artifacts, sprites are jumping up and down, svgs are basically not usable etc…

Image from Gyazo
Image from Gyazo

Making it integer artifact issues for sprites but problems still remain with UI elements having now artifacts, sprites suddenly “jumping”, black bars on the sides, and artifacts on the side of the camera …

Image from Gyazo
Image from Gyazo
Image from Gyazo

So, honestly I am lost at this point. The “best” result I got was still canvas item with fractional,
I am not sure what is the cause, how to fix it or if I have to rethink my whole approach in terms of design approach.

Should I drop Svgs and what not and go full pixel with viewport etc…
Should I drop the pixel aesthetic…
Should I drop 2D entirely and just move to 3D.

Is there anyone knowledgeable to know what could be the issue?
If it’s an issue with settings? With my code? With my approach to designs/ui elements?

Nevermind. It seems like it is not an isolated issue and may be due to NVIDIA driver.

https://www.reddit.com/r/godot/comments/1ifzi3i/nvidia_driver_57216_bug_thread

Closing the topic for now until I can confirm etc…