Merge pull request #108954 from Calinou/moviewriter-resize-frames-if-different

Ensure all MovieWriter frames have the same resolution as the first frame
This commit is contained in:
Rémi Verschelde 2026-01-08 15:16:53 +01:00
commit 2b394f4aab
No known key found for this signature in database
GPG key ID: C3336907360768E1
3 changed files with 53 additions and 15 deletions

View file

@ -4704,7 +4704,21 @@ int Main::start() {
}
if (movie_writer) {
movie_writer->begin(DisplayServer::get_singleton()->window_get_size(), fixed_fps, Engine::get_singleton()->get_write_movie_path());
Size2i movie_size = Size2i(GLOBAL_GET("display/window/size/viewport_width"), GLOBAL_GET("display/window/size/viewport_height"));
String stretch_mode = GLOBAL_GET("display/window/stretch/mode");
if (stretch_mode != "viewport") {
// `canvas_items` and `disabled` modes use the window size override instead,
// which allows for higher resolution recording with 2D elements designed for a lower resolution.
const int window_width_override = GLOBAL_GET("display/window/size/window_width_override");
if (window_width_override > 0) {
movie_size.width = window_width_override;
}
const int window_height_override = GLOBAL_GET("display/window/size/window_height_override");
if (window_height_override > 0) {
movie_size.height = window_height_override;
}
}
movie_writer->begin(movie_size, fixed_fps, Engine::get_singleton()->get_write_movie_path());
}
GDExtensionManager::get_singleton()->startup();

View file

@ -97,20 +97,9 @@ void MovieWriter::get_supported_extensions(List<String> *r_extensions) const {
void MovieWriter::begin(const Size2i &p_movie_size, uint32_t p_fps, const String &p_base_path) {
project_name = GLOBAL_GET("application/config/name");
movie_size = p_movie_size;
print_line(vformat("Movie Maker mode enabled, recording movie at %d FPS...", p_fps));
// When using Display/Window/Stretch/Mode = Viewport, use the project's
// configured viewport size instead of the size of the window in the OS
Size2i actual_movie_size = p_movie_size;
String stretch_mode = GLOBAL_GET("display/window/stretch/mode");
if (stretch_mode == "viewport") {
actual_movie_size.width = GLOBAL_GET("display/window/size/viewport_width");
actual_movie_size.height = GLOBAL_GET("display/window/size/viewport_height");
print_line(vformat("Movie Maker mode using project viewport size: %dx%d",
actual_movie_size.width, actual_movie_size.height));
}
print_line(vformat(U"Movie Maker mode enabled, recording movie in %s×%s @ %d FPS...", movie_size.width, movie_size.height, p_fps));
// Check for available disk space and warn the user if needed.
Ref<DirAccess> dir = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
@ -139,7 +128,7 @@ void MovieWriter::begin(const Size2i &p_movie_size, uint32_t p_fps, const String
audio_channels = AudioDriverDummy::get_dummy_singleton()->get_channels();
audio_mix_buffer.resize(mix_rate * audio_channels / fps);
write_begin(actual_movie_size, p_fps, p_base_path);
write_begin(movie_size, p_fps, p_base_path);
}
void MovieWriter::_bind_methods() {
@ -206,6 +195,37 @@ void MovieWriter::add_frame() {
RID main_vp_rid = RenderingServer::get_singleton()->viewport_find_from_screen_attachment(DisplayServer::MAIN_WINDOW_ID);
RID main_vp_texture = RenderingServer::get_singleton()->viewport_get_texture(main_vp_rid);
Ref<Image> vp_tex = RenderingServer::get_singleton()->texture_2d_get(main_vp_texture);
if (vp_tex->get_size() != movie_size) {
// Resize the texture to the output resolution if it differs from the current viewport size.
// This ensures all frames have the same resolution, as not all video formats and players
// support resolution changes during playback.
const float src_aspect = vp_tex->get_size().aspect();
const float dst_aspect = movie_size.aspect();
int crop_width = vp_tex->get_size().width;
int crop_height = vp_tex->get_size().height;
int crop_x = 0;
int crop_y = 0;
// If the aspect ratio differs, crop the image to cover the base resolution's aspect ratio
// in a way similar to `TextureRect.STRETCH_KEEP_ASPECT_COVERED`.
if (src_aspect > dst_aspect) {
// Source is wider, crop horizontally.
crop_width = int(vp_tex->get_size().height * dst_aspect);
crop_x = (vp_tex->get_size().width - crop_width) / 2;
vp_tex->crop_from_point(crop_x, crop_y, crop_width, crop_height);
} else if (src_aspect < dst_aspect) {
// Source is taller, crop vertically.
crop_height = int(vp_tex->get_size().width / dst_aspect);
crop_y = (vp_tex->get_size().height - crop_height) / 2;
vp_tex->crop_from_point(crop_x, crop_y, crop_width, crop_height);
}
vp_tex->resize(movie_size.width, movie_size.height, Image::INTERPOLATE_BILINEAR);
}
if (RenderingServer::get_singleton()->viewport_is_using_hdr_2d(main_vp_rid)) {
vp_tex->convert(Image::FORMAT_RGBA8);
vp_tex->linear_to_srgb();

View file

@ -41,6 +41,10 @@ class MovieWriter : public Object {
uint64_t mix_rate = 0;
uint32_t audio_channels = 0;
// The output resolution, which can differ from the window size.
// Used as a base for resizing all subsequent frames if their resolution differs.
Vector2i movie_size;
float cpu_time = 0.0f;
float gpu_time = 0.0f;
uint64_t encoding_time_usec = 0;