Job authoring: handling of task dependencies + some bugfixes

This commit is contained in:
Sybren A. Stüvel 2022-02-01 16:23:54 +01:00
parent 8063a3e169
commit fad2dc3042
5 changed files with 53 additions and 9 deletions

View File

@ -24,6 +24,7 @@ import (
"time"
"github.com/dop251/goja"
"github.com/google/uuid"
"github.com/rs/zerolog/log"
"gitlab.com/blender/flamenco-ng-poc/pkg/api"
)
@ -52,6 +53,11 @@ type JobSettings map[string]interface{}
type JobMetadata map[string]string
type AuthoredTask struct {
// Tasks already get their UUID in the authoring stage. This makes it simpler
// to store the dependencies, as the code doesn't have to worry about value
// vs. pointer semantics. Tasks can always be unambiguously referenced by
// their UUID.
UUID string
Name string
Type string
Priority int
@ -69,6 +75,7 @@ type AuthoredCommandParameters map[string]interface{}
func (a *Author) Task(name string, taskType string) (*AuthoredTask, error) {
at := AuthoredTask{
uuid.New().String(),
name,
taskType,
50, // TODO: handle default priority somehow.

View File

@ -44,7 +44,7 @@ func exampleSubmittedJob() api.SubmittedJob {
"frames": "1-10",
"images_or_video": "images",
"output_file_extension": ".png",
"render_output": "/render/sf/frames/scene123",
"render_output": "/render/sprites/farm_output/promo/square_ellie/square_ellie.lighting_light_breakdown2/######",
}}
metadata := api.JobMetadata{
AdditionalProperties: map[string]string{
@ -101,14 +101,15 @@ func TestSimpleBlenderRenderHappy(t *testing.T) {
settings := sj.Settings.AdditionalProperties
// Tasks should have been created to render the frames.
assert.Equal(t, 4, len(aj.Tasks))
// Tasks should have been created to render the frames: 1-3, 4-6, 7-9, 10, video-encoding
assert.Equal(t, 5, len(aj.Tasks))
t0 := aj.Tasks[0]
expectCliArgs := []interface{}{ // They are strings, but Goja doesn't know that and will produce an []interface{}.
"--render-output", "/render/sf__intermediate-2006-01-02_090405/frames",
"--render-output", "/render/sprites/farm_output/promo/square_ellie/square_ellie.lighting_light_breakdown2__intermediate-2006-01-02_090405/######",
"--render-format", settings["format"].(string),
"--render-frame", "1-3",
}
assert.NotEmpty(t, t0.UUID)
assert.Equal(t, "render-1-3", t0.Name)
assert.Equal(t, 1, len(t0.Commands))
assert.Equal(t, "blender-render", t0.Commands[0].Type)
@ -117,4 +118,32 @@ func TestSimpleBlenderRenderHappy(t *testing.T) {
"blendfile": settings["filepath"].(string),
"args": expectCliArgs,
}, t0.Commands[0].Parameters)
tVideo := aj.Tasks[4] // This should be a video encoding task
assert.NotEmpty(t, tVideo.UUID)
assert.Equal(t, "create-video", tVideo.Name)
assert.Equal(t, 1, len(tVideo.Commands))
assert.Equal(t, "create-video", tVideo.Commands[0].Type)
assert.EqualValues(t, AuthoredCommandParameters{
"input_files": "/render/sprites/farm_output/promo/square_ellie/square_ellie.lighting_light_breakdown2__intermediate-2006-01-02_090405/*.png",
"output_file": "/render/sprites/farm_output/promo/square_ellie/square_ellie.lighting_light_breakdown2__intermediate-2006-01-02_090405/scene123-1-10.mp4",
"fps": int64(24),
}, tVideo.Commands[0].Parameters)
for index, task := range aj.Tasks {
if index == 0 {
continue
}
assert.NotEqual(t, t0.UUID, task.UUID, "Task UUIDs should be unique")
}
// Check dependencies
assert.Empty(t, aj.Tasks[0].Dependencies)
assert.Empty(t, aj.Tasks[1].Dependencies)
assert.Empty(t, aj.Tasks[2].Dependencies)
assert.Equal(t, 4, len(tVideo.Dependencies))
expectDeps := []*AuthoredTask{
&aj.Tasks[0], &aj.Tasks[1], &aj.Tasks[2], &aj.Tasks[3],
}
assert.Equal(t, expectDeps, tVideo.Dependencies)
}

View File

@ -59,13 +59,16 @@ function compileJob(job) {
// The render path contains a filename pattern, most likely '######' or
// something similar. This has to be removed, so that we end up with
// the directory that will contain the frames.
const renderOutput = path.dirname(settings.render_output);
const renderOutput = settings.render_output;
const finalDir = path.dirname(renderOutput);
const renderDir = intermediatePath(job, finalDir);
const renderTasks = authorRenderTasks(settings, renderDir, renderOutput);
const videoTask = authorCreateVideoTask(renderTasks, renderDir);
const videoTask = authorCreateVideoTask(settings, renderDir);
for (const rt of renderTasks) {
job.addTask(rt);
}
if (videoTask) {
// If there is a video task, all other tasks have to be done first.
for (const rt of renderTasks) {
@ -73,9 +76,6 @@ function compileJob(job) {
}
job.addTask(videoTask);
}
for (const rt of renderTasks) {
job.addTask(rt);
}
}
// Determine the intermediate render output path.
@ -86,6 +86,7 @@ function intermediatePath(job, finalDir) {
}
function authorRenderTasks(settings, renderDir, renderOutput) {
print("authorRenderTasks(", renderDir, renderOutput, ")");
let renderTasks = [];
let chunks = frameChunker(settings.frames, settings.chunk_size);
for (let chunk of chunks) {
@ -107,9 +108,11 @@ function authorRenderTasks(settings, renderDir, renderOutput) {
function authorCreateVideoTask(settings, renderDir) {
if (ffmpegIncompatibleImageFormats.has(settings.format)) {
print("Not authoring video task, FFmpeg-incompatible render output")
return;
}
if (!settings.fps || !settings.output_file_extension) {
print("Not authoring video task, no FPS or output file extension setting:", settings)
return;
}

View File

@ -50,6 +50,7 @@ type StringStringMap map[string]string
type Task struct {
gorm.Model
UUID string `gorm:"type:char(36);not null;unique;index"`
Name string `gorm:"type:varchar(64);not null"`
Type string `gorm:"type:varchar(32);not null"`
@ -137,6 +138,7 @@ func (db *DB) StoreAuthoredJob(ctx context.Context, authoredJob job_compilers.Au
dbTask := Task{
Name: authoredTask.Name,
Type: authoredTask.Type,
UUID: authoredTask.UUID,
Job: &dbJob,
Priority: authoredTask.Priority,
Status: string(api.TaskStatusProcessing), // TODO: is this the right place to set the default status?

View File

@ -40,6 +40,7 @@ func TestStoreAuthoredJob(t *testing.T) {
task1 := job_compilers.AuthoredTask{
Name: "render-1-3",
Type: "blender",
UUID: "db1f5481-4ef5-4084-8571-8460c547ecaa",
Commands: []job_compilers.AuthoredCommand{
{
Type: "blender-render",
@ -57,11 +58,13 @@ func TestStoreAuthoredJob(t *testing.T) {
task2 := task1
task2.Name = "render-4-6"
task2.UUID = "d75ac779-151b-4bc2-b8f1-d153a9c4ac69"
task2.Commands[0].Parameters["frames"] = "4-6"
task3 := job_compilers.AuthoredTask{
Name: "preview-video",
Type: "ffmpeg",
UUID: "4915fb05-72f5-463e-a2f4-7efdb2584a1e",
Commands: []job_compilers.AuthoredCommand{
{
Type: "merge-frames-to-video",