Skip to content
Snippets Groups Projects
Commit 053b7e85 authored by Sybren A. Stüvel's avatar Sybren A. Stüvel
Browse files

Worker: added BlenderRenderProgressiveCommand and renamed MergeExrCommand

MergeExrCommand is now called MergeProgressiveRendersCommand, to be
consistent with the job compiler on Server.
parent 146eaa0f
No related branches found
No related tags found
No related merge requests found
...@@ -6,6 +6,7 @@ import asyncio.subprocess ...@@ -6,6 +6,7 @@ import asyncio.subprocess
import logging import logging
import re import re
import typing import typing
from pathlib import Path
import attr import attr
...@@ -454,7 +455,6 @@ class BlenderRenderCommand(AbstractSubprocessCommand): ...@@ -454,7 +455,6 @@ class BlenderRenderCommand(AbstractSubprocessCommand):
self.re_path_not_found = re.compile(r"Warning: Path '.*' not found") self.re_path_not_found = re.compile(r"Warning: Path '.*' not found")
def validate(self, settings: dict): def validate(self, settings: dict):
from pathlib import Path
import shlex import shlex
blender_cmd, err = self._setting(settings, 'blender_cmd', True) blender_cmd, err = self._setting(settings, 'blender_cmd', True)
...@@ -491,6 +491,12 @@ class BlenderRenderCommand(AbstractSubprocessCommand): ...@@ -491,6 +491,12 @@ class BlenderRenderCommand(AbstractSubprocessCommand):
return None return None
async def execute(self, settings: dict): async def execute(self, settings: dict):
cmd = self._build_blender_cmd(settings)
await self.worker.register_task_update(activity='Starting Blender')
await self.subprocess(cmd)
def _build_blender_cmd(self, settings):
cmd = settings['blender_cmd'][:] cmd = settings['blender_cmd'][:]
cmd += [ cmd += [
'--factory-startup', '--factory-startup',
...@@ -506,9 +512,7 @@ class BlenderRenderCommand(AbstractSubprocessCommand): ...@@ -506,9 +512,7 @@ class BlenderRenderCommand(AbstractSubprocessCommand):
cmd.extend(['--render-format', settings['format']]) cmd.extend(['--render-format', settings['format']])
if settings.get('frames'): if settings.get('frames'):
cmd.extend(['--render-frame', settings['frames']]) cmd.extend(['--render-frame', settings['frames']])
return cmd
await self.worker.register_task_update(activity='Starting Blender')
await self.subprocess(cmd)
def parse_render_line(self, line: str) -> typing.Optional[dict]: def parse_render_line(self, line: str) -> typing.Optional[dict]:
"""Parses a single line of render progress. """Parses a single line of render progress.
...@@ -567,10 +571,35 @@ class BlenderRenderCommand(AbstractSubprocessCommand): ...@@ -567,10 +571,35 @@ class BlenderRenderCommand(AbstractSubprocessCommand):
return '> %s' % line return '> %s' % line
@command_executor('merge-exr') @command_executor('blender_render_progressive')
class MergeExrCommand(AbstractSubprocessCommand): class BlenderRenderProgressiveCommand(BlenderRenderCommand):
def validate(self, settings: dict):
err = super().validate(settings)
if err: return err
cycles_num_chunks, err = self._setting(settings, 'cycles_num_chunks', True, int)
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'
def _build_blender_cmd(self, settings):
cmd = super()._build_blender_cmd(settings)
return cmd + [
'--',
'--cycles-resumable-num-chunks', str(settings['cycles_num_chunks']),
'--cycles-resumable-current-chunk', str(settings['cycles_chunk']),
]
@command_executor('merge_progressive_renders')
class MergeProgressiveRendersCommand(AbstractSubprocessCommand):
def validate(self, settings: dict): def validate(self, settings: dict):
from pathlib import Path
import shlex import shlex
blender_cmd, err = self._setting(settings, 'blender_cmd', True) blender_cmd, err = self._setting(settings, 'blender_cmd', True)
...@@ -606,8 +635,6 @@ class MergeExrCommand(AbstractSubprocessCommand): ...@@ -606,8 +635,6 @@ class MergeExrCommand(AbstractSubprocessCommand):
if err: return err if err: return err
async def execute(self, settings: dict): async def execute(self, settings: dict):
from pathlib import Path
import shutil
import tempfile import tempfile
blendpath = Path(__file__).with_name('merge-exr.blend') blendpath = Path(__file__).with_name('merge-exr.blend')
...@@ -623,9 +650,9 @@ class MergeExrCommand(AbstractSubprocessCommand): ...@@ -623,9 +650,9 @@ class MergeExrCommand(AbstractSubprocessCommand):
# set up node properties and render settings. # set up node properties and render settings.
output = Path(settings['output']) output = Path(settings['output'])
output.mkdir(parents=True, exist_ok=True) output.parent.mkdir(parents=True, exist_ok=True)
with tempfile.TemporaryDirectory(dir=str(output)) as tmpdir: with tempfile.TemporaryDirectory(dir=str(output.parent)) as tmpdir:
tmppath = Path(tmpdir) tmppath = Path(tmpdir)
assert tmppath.exists() assert tmppath.exists()
...@@ -638,6 +665,19 @@ class MergeExrCommand(AbstractSubprocessCommand): ...@@ -638,6 +665,19 @@ class MergeExrCommand(AbstractSubprocessCommand):
await self.subprocess(cmd) await self.subprocess(cmd)
# move output files into the correct spot. # move output files into the correct spot.
shutil.move(str(tmppath / 'merged0001.exr'), str(output)) await self.move(tmppath / 'merged0001.exr', output)
shutil.move(str(tmppath / 'preview.jpg'), str(output.with_suffix('.jpg'))) await self.move(tmppath / 'preview.jpg', output.with_suffix('.jpg'))
async def move(self, src: Path, dst: Path):
"""Moves a file to another location."""
import shutil
self._log.info('Moving %s to %s', src, dst)
assert src.is_file()
assert not dst.exists() or dst.is_file()
assert dst.exists() or dst.parent.exists()
await self.worker.register_log('Moving %s to %s', src, dst)
shutil.move(str(src), str(dst))
...@@ -65,38 +65,29 @@ class BlenderRenderTest(AbstractCommandTest): ...@@ -65,38 +65,29 @@ class BlenderRenderTest(AbstractCommandTest):
self.loop.run_until_complete(self.cmd.process_line(line)) self.loop.run_until_complete(self.cmd.process_line(line))
self.fworker.register_task_update.assert_called_once_with(activity=line) self.fworker.register_task_update.assert_called_once_with(activity=line)
@patch('pathlib.Path') def test_cli_args(self):
def test_cli_args(self, mock_path):
"""Test that CLI arguments in the blender_cmd setting are handled properly.""" """Test that CLI arguments in the blender_cmd setting are handled properly."""
from pathlib import Path
import subprocess import subprocess
from mock_responses import CoroMock from mock_responses import CoroMock
filepath = str(Path(__file__).parent)
settings = { settings = {
'blender_cmd': '/path/to/blender --with --cli="args for CLI"', # Point blender_cmd to this file so that we're sure it exists.
'blender_cmd': '%s --with --cli="args for CLI"' % __file__,
'chunk_size': 100, 'chunk_size': 100,
'frames': '1..2', 'frames': '1..2',
'format': 'JPEG', 'format': 'JPEG',
'filepath': '/path/to/output', 'filepath': filepath,
} }
mock_path.Path.exists.side_effect = [True, True]
cse = CoroMock() cse = CoroMock()
cse.coro.return_value.wait = CoroMock(return_value=0) cse.coro.return_value.wait = CoroMock(return_value=0)
with patch('asyncio.create_subprocess_exec', new=cse) as mock_cse: with patch('asyncio.create_subprocess_exec', new=cse) as mock_cse:
self.loop.run_until_complete(self.cmd.run(settings)) self.loop.run_until_complete(self.cmd.run(settings))
mock_path.assert_has_calls([
call('/path/to/blender'),
call().exists(),
call().exists().__bool__(),
call('/path/to/output'),
call().exists(),
call().exists().__bool__(),
])
mock_cse.assert_called_once_with( mock_cse.assert_called_once_with(
'/path/to/blender', __file__,
'--with', '--with',
'--cli=args for CLI', '--cli=args for CLI',
'--factory-startup', '--factory-startup',
...@@ -104,7 +95,7 @@ class BlenderRenderTest(AbstractCommandTest): ...@@ -104,7 +95,7 @@ class BlenderRenderTest(AbstractCommandTest):
'--debug-cycles', '--debug-cycles',
'-noaudio', '-noaudio',
'--background', '--background',
'/path/to/output', filepath,
'--render-format', 'JPEG', '--render-format', 'JPEG',
'--render-frame', '1..2', '--render-frame', '1..2',
stdin=subprocess.DEVNULL, stdin=subprocess.DEVNULL,
......
...@@ -3,17 +3,17 @@ from pathlib import Path ...@@ -3,17 +3,17 @@ from pathlib import Path
from test_runner import AbstractCommandTest from test_runner import AbstractCommandTest
class MergeExrCommandTest(AbstractCommandTest): class MergeProgressiveRendersCommandTest(AbstractCommandTest):
def setUp(self): def setUp(self):
super().setUp() super().setUp()
from flamenco_worker.runner import MergeExrCommand from flamenco_worker.runner import MergeProgressiveRendersCommand
import tempfile import tempfile
self.tmpdir = tempfile.TemporaryDirectory() self.tmpdir = tempfile.TemporaryDirectory()
self.mypath = Path(__file__).parent self.mypath = Path(__file__).parent
self.cmd = MergeExrCommand( self.cmd = MergeProgressiveRendersCommand(
worker=self.fworker, worker=self.fworker,
task_id='12345', task_id='12345',
command_idx=0, command_idx=0,
...@@ -41,4 +41,6 @@ class MergeExrCommandTest(AbstractCommandTest): ...@@ -41,4 +41,6 @@ class MergeExrCommandTest(AbstractCommandTest):
# Assuming that if the files exist, the merge was ok. # Assuming that if the files exist, the merge was ok.
self.assertTrue(output.exists()) self.assertTrue(output.exists())
self.assertTrue(output.is_file())
self.assertTrue(output.with_suffix('.jpg').exists()) self.assertTrue(output.with_suffix('.jpg').exists())
self.assertTrue(output.with_suffix('.jpg').is_file())
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment