From 0333804b0bb40a785a68c6d2e93a7bcefef11ab2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= <sybren@stuvel.eu>
Date: Wed, 30 Jan 2019 14:34:42 +0100
Subject: [PATCH] Updated progressive rendering to allow sample chunk ranges

This requires Flamenco Server 2.2 or newer. Progressive render jobs
generated by older versions of Flamenco Server are no longer supported.
---
 CHANGELOG.md                                  |  7 ++
 flamenco_worker/commands.py                   | 28 ++++++--
 ...est_commands_blender_render_progressive.py | 67 +++++++++++++++++++
 3 files changed, 95 insertions(+), 7 deletions(-)
 create mode 100644 tests/test_commands_blender_render_progressive.py

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 53d2af3d..37b09781 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,13 @@
 This file logs the changes that are actually interesting to users (new features,
 changed functionality, fixed bugs).
 
+## Version 2.3 (in development)
+
+- Changed how progressive rendering works. Nonuniform tasks are now supported. This requires
+  Flamenco Server 2.2 or newer. Progressive render jobs generated by older versions of Flamenco
+  Server are no longer supported.
+
+
 ## Version 2.2.1 (2019-01-14)
 
 - Fixed bug where an uncaught exception could make the Worker stop requesting tasks or go into an
diff --git a/flamenco_worker/commands.py b/flamenco_worker/commands.py
index 588b0481..b169a7dc 100644
--- a/flamenco_worker/commands.py
+++ b/flamenco_worker/commands.py
@@ -868,18 +868,31 @@ class BlenderRenderCommand(AbstractSubprocessCommand):
 @command_executor('blender_render_progressive')
 class BlenderRenderProgressiveCommand(BlenderRenderCommand):
     def validate(self, settings: Settings):
+        if 'cycles_chunk' in settings:
+            return '"cycles_chunk" is an obsolete setting. Recreate the job using Flamenco ' \
+                   'Server 2.2 or newer, or use an older Worker.'
+
         err = super().validate(settings)
-        if err: return err
+        if err:
+            return err
 
         cycles_num_chunks, err = self._setting(settings, 'cycles_num_chunks', True, int)
-        if err: return err
+        if err:
+            return err
         if cycles_num_chunks < 1:
             return '"cycles_num_chunks" must be a positive integer'
 
-        cycles_chunk, err = self._setting(settings, 'cycles_chunk', True, int)
-        if err: return err
-        if cycles_chunk < 1:
-            return '"cycles_chunk" must be a positive integer'
+        cycles_chunk_start, err = self._setting(settings, 'cycles_chunk_start', True, int)
+        if err:
+            return err
+        if cycles_chunk_start < 1:
+            return '"cycles_chunk_start" must be a positive integer'
+
+        cycles_chunk_end, err = self._setting(settings, 'cycles_chunk_end', True, int)
+        if err:
+            return err
+        if cycles_chunk_end < 1:
+            return '"cycles_chunk_end" must be a positive integer'
 
     async def _build_blender_cmd(self, settings):
         cmd = await super()._build_blender_cmd(settings)
@@ -887,7 +900,8 @@ class BlenderRenderProgressiveCommand(BlenderRenderCommand):
         return cmd + [
             '--',
             '--cycles-resumable-num-chunks', str(settings['cycles_num_chunks']),
-            '--cycles-resumable-current-chunk', str(settings['cycles_chunk']),
+            '--cycles-resumable-start-chunk', str(settings['cycles_chunk_start']),
+            '--cycles-resumable-end-chunk', str(settings['cycles_chunk_end']),
         ]
 
 
diff --git a/tests/test_commands_blender_render_progressive.py b/tests/test_commands_blender_render_progressive.py
new file mode 100644
index 00000000..f1368f01
--- /dev/null
+++ b/tests/test_commands_blender_render_progressive.py
@@ -0,0 +1,67 @@
+from pathlib import Path
+import subprocess
+import tempfile
+from unittest import mock
+
+from unittest.mock import patch
+
+from tests.test_runner import AbstractCommandTest
+
+
+class BlenderRenderProgressiveTest(AbstractCommandTest):
+    thisfile = Path(__file__).as_posix()
+
+    def setUp(self):
+        super().setUp()
+
+        from flamenco_worker.commands import BlenderRenderProgressiveCommand
+
+        self.cmd = BlenderRenderProgressiveCommand(
+            worker=self.fworker,
+            task_id='12345',
+            command_idx=0,
+        )
+
+    def test_cli_args(self):
+        """Test that CLI arguments in the blender_cmd setting are handled properly."""
+        from tests.mock_responses import CoroMock
+
+        filepath = Path(__file__).parent.as_posix()
+        settings = {
+            # Point blender_cmd to this file so that we're sure it exists.
+            'blender_cmd': f'{self.thisfile!r} --with --cli="args for CLI"',
+            'chunk_size': 100,
+            'frames': '1..2',
+            'format': 'EXR',
+            'filepath': filepath,
+            'render_output': '/some/path/there.exr',
+            'cycles_num_chunks': 400,
+            'cycles_chunk_start': 223,
+            'cycles_chunk_end': 311,
+        }
+
+        cse = CoroMock(...)
+        cse.coro.return_value.wait = CoroMock(return_value=0)
+        cse.coro.return_value.pid = 47
+        with patch('asyncio.create_subprocess_exec', new=cse) as mock_cse:
+            self.loop.run_until_complete(self.cmd.run(settings))
+
+            mock_cse.assert_called_once_with(
+                self.thisfile,
+                '--with',
+                '--cli=args for CLI',
+                '--enable-autoexec',
+                '-noaudio',
+                '--background',
+                filepath,
+                '--render-output', '/some/path/there.exr',
+                '--render-format', 'EXR',
+                '--render-frame', '1..2',
+                '--',
+                '--cycles-resumable-num-chunks', '400',
+                '--cycles-resumable-start-chunk', '223',
+                '--cycles-resumable-end-chunk', '311',
+                stdin=subprocess.DEVNULL,
+                stdout=subprocess.PIPE,
+                stderr=subprocess.STDOUT,
+            )
-- 
GitLab