WIP: Single-frame job compiler #104194

Draft
k8ie wants to merge 30 commits from k8ie/flamenco:single-frame into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
Showing only changes of commit 703c2d3ffd - Show all commits

View File

@ -6,7 +6,7 @@ const JOB_TYPE = {
// Settings for artists to determine:
{ key: "frame", type: "int32", required: true, eval: "C.scene.frame_current",
description: "Frame to render"},
{ key: "chunk_size", type: "int32", default: 128, description: "Number of samples to render in one Blender render task",
{ key: "chunk_size", type: "int32", default: 128, propargs: {min: 1}, description: "Number of samples to render in one Blender render task",
visible: "submission" },
{ key: "denoising", type: "bool", required: true, default: false,
description: "Toggles OpenImageDenoise" },
@ -23,6 +23,10 @@ const JOB_TYPE = {
// Automatically evaluated settings:
{ key: "blendfile", type: "string", required: true, description: "Path of the Blend file to render", visible: "web" },
{ key: "format", type: "string", required: true, eval: "C.scene.render.image_settings.file_format", visible: "web" },
{ key: "uses_compositing", type: "bool", required: true, eval: "C.scene.use_nodes and C.scene.render.use_compositing", visible: "web" },
{ key: "image_file_extension", type: "string", required: true, eval: "C.scene.render.file_extension", visible: "hidden",
description: "File extension used for the final export" },
{ key: "samples", type: "string", required: true, eval: "f'1-{C.scene.cycles.samples}'", visible: "web",
description: "Total number of samples in the job" },
]
@ -43,6 +47,7 @@ function compileJob(job) {
const renderTasks = authorRenderTasks(settings, renderDir, renderOutput);
const mergeTask = authorCreateMergeTask(settings, renderOutput);
const denoiseTask = authorCreateDenoiseTask(settings, renderOutput);
const compositeTask = authorCreateCompositeTask(settings, renderOutput);
for (const rt of renderTasks) {
job.addTask(rt);
@ -57,7 +62,12 @@ function compileJob(job) {
if (denoiseTask) {
denoiseTask.addDependency(mergeTask);
job.addTask(denoiseTask);
compositeTask.addDependency(denoiseTask);
}
else {
compositeTask.addDependency(mergeTask);
}
job.addTask(compositeTask);
}
// Do field replacement on the render output path.
@ -82,18 +92,18 @@ function authorRenderTasks(settings, renderDir, renderOutput) {
let chunks = frameChunker(settings.samples, settings.chunk_size);
for (let chunk of chunks) {
const task = author.Task(`render-${chunk}`, "blender");
let chunk_arr = chunk.split("-", 2)
let chunk_end
let chunk_start
let chunk_arr = chunk.split("-", 2);
let chunk_end;
let chunk_start;
if (chunk_arr.length < 2) {
chunk_end = chunk_arr[0]
chunk_start = chunk_arr[0] - 1
chunk_end = chunk_arr[0];
chunk_start = chunk_arr[0] - 1;
}
else {
chunk_end = chunk_arr[1]
chunk_start = chunk_arr[0] - 1
chunk_end = chunk_arr[1];
chunk_start = chunk_arr[0] - 1;
}
let chunk_size = chunk_end - chunk_start
let chunk_size = chunk_end - chunk_start;
let pythonExpression = `
import bpy
bpy.context.scene.render.engine = 'CYCLES'
@ -115,6 +125,7 @@ for layer in bpy.context.scene.view_layers:
argsBefore: [],
blendfile: settings.blendfile,
args: [
"--python-exit-code", 1,
"--python-expr", pythonExpression,
"--render-output", path.join(renderDir, path.basename(renderOutput), chunk),
"--render-frame", settings.frame
@ -170,6 +181,7 @@ pathlib.Path(tmp + str(index-1)+'.exr').rename(basepath + 'MERGED.exr')`;
argsBefore: [],
blendfile: settings.blendfile,
args: [
"--python-exit-code", 1,
"--python-expr", pythonExpression
]
});
@ -192,6 +204,97 @@ bpy.ops.cycles.denoise_animation(input_filepath="${renderOutput}/MERGED.exr", ou
argsBefore: [],
blendfile: settings.blendfile,
args: [
"--python-exit-code", 1,
"--python-expr", pythonExpression
]
});
task.addCommand(command);
return task;
}
function authorCreateCompositeTask(settings, renderOutput) {
let filename;
if (settings.denoising) {
filename = "DENOISED.exr";
}
else {
filename = "MERGED.exr";
}
let pythonExpression = `
import pathlib
import bpy
C = bpy.context
basepath = "${renderOutput}/"
filename = "${filename}"`;
if (settings.uses_compositing) {
// Do the full composite+export pipeline
// uses snippets from
// https://github.com/state-of-the-art/BlendNet/blob/master/BlendNet/script-compose.py#L94
pythonExpression += `
bpy.ops.image.open(filepath=basepath + filename, use_sequence_detection=False)
image = bpy.data.images[bpy.path.basename(filename)]
image_node = C.scene.node_tree.nodes.new(type='CompositorNodeImage')
image_node.image = image
nodes_to_remove = []
links_to_create = []
for node in C.scene.node_tree.nodes:
print('DEBUG: Checking node %s' % (node,))
if not isinstance(node, bpy.types.CompositorNodeRLayers) or node.scene != C.scene:
continue
nodes_to_remove.append(node)
print('INFO: Reconnecting %s links to render image' % (node,))
for link in C.scene.node_tree.links:
print('DEBUG: Checking link %s - %s' % (link.from_node, link.to_node))
if link.from_node != node:
continue
print('DEBUG: Found link %s - %s' % (link.from_socket, link.to_socket))
link_name = "Combined"
for output in image_node.outputs:
print('DEBUG: Checking output:', output.name, link_name)
if output.name != link_name:
continue
links_to_create.append((output, link))
break
for output, link in links_to_create:
print('INFO: Connecting "%s" output to %s.%s input' % (
output, link.to_node, link.to_socket
))
C.scene.node_tree.links.new(output, link.to_socket)
print("Removing the nodes could potentially break the pipeline")
for node in nodes_to_remove:
print('INFO: Removing %s' % (node,))
C.scene.node_tree.nodes.remove(node)
C.scene.render.filepath = basepath + "FINAL"
bpy.ops.render.render(write_still=True)`;
}
else {
pythonExpression += `
print("Compositing is disabled")`;
if (settings.format == "OPEN_EXR_MULTILAYER") {
// Only rename
pythonExpression += `
print('Renaming ' + basepath + filename + ' to ' + basepath + 'FINAL.exr')
pathlib.Path(basepath + filename).rename(basepath + 'FINAL.exr')`;
}
else {
// Only export
pythonExpression += `
bpy.ops.image.open(filepath=basepath + filename, use_sequence_detection=False)
image = bpy.data.images[bpy.path.basename(filename)]
print("Saving final image to " + basepath + "FINAL" + "${settings.image_file_extension}")
image.save_render(basepath + "FINAL" + "${settings.image_file_extension}")`;
}
}
const task = author.Task(`composite`, "blender");
const command = author.Command("blender-render", {
exe: "{blender}",
exeArgs: "{blenderArgs}",
argsBefore: [],
blendfile: settings.blendfile,
args: [
"--python-exit-code", 1,
"--python-expr", pythonExpression
]
});