From dc525fbaa4fcc3c86ee4b77050eaf51897bc8800 Mon Sep 17 00:00:00 2001 From: Justin Harms Date: Fri, 13 Mar 2026 21:15:44 -0500 Subject: [PATCH] Add hardware compatibility flags for CPU rendering and HIPRT control - Introduced `--force-cpu-rendering` and `--disable-hiprt` flags to the runner command, allowing users to enforce CPU rendering and disable HIPRT acceleration. - Updated the runner initialization and context structures to accommodate the new flags, enhancing flexibility in rendering configurations. - Modified the rendering logic to respect these flags, improving compatibility and user control over rendering behavior in Blender. --- README.md | 3 +++ cmd/jiggablend/cmd/runner.go | 8 +++++++- internal/runner/runner.go | 12 +++++++++++- internal/runner/tasks/processor.go | 11 +++++++++++ internal/runner/tasks/render.go | 9 ++++++--- pkg/scripts/scripts/render_blender.py.template | 15 ++++++++++++++- 6 files changed, 52 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 063aa16..fa08bac 100644 --- a/README.md +++ b/README.md @@ -154,6 +154,9 @@ bin/jiggablend runner --api-key # With custom options bin/jiggablend runner --manager http://localhost:8080 --name my-runner --api-key --log-file runner.log +# Hardware compatibility flags (force CPU + disable HIPRT) +bin/jiggablend runner --api-key --force-cpu-rendering --disable-hiprt + # Using environment variables JIGGABLEND_MANAGER=http://localhost:8080 JIGGABLEND_API_KEY= bin/jiggablend runner ``` diff --git a/cmd/jiggablend/cmd/runner.go b/cmd/jiggablend/cmd/runner.go index 167b5ef..ef81e8e 100644 --- a/cmd/jiggablend/cmd/runner.go +++ b/cmd/jiggablend/cmd/runner.go @@ -37,6 +37,8 @@ func init() { runnerCmd.Flags().String("log-level", "info", "Log level (debug, info, warn, error)") runnerCmd.Flags().BoolP("verbose", "v", false, "Enable verbose logging (same as --log-level=debug)") runnerCmd.Flags().Duration("poll-interval", 5*time.Second, "Job polling interval") + runnerCmd.Flags().Bool("force-cpu-rendering", false, "Force CPU rendering for all jobs (disables GPU rendering)") + runnerCmd.Flags().Bool("disable-hiprt", false, "Disable HIPRT acceleration in Blender Cycles") // Bind flags to viper with JIGGABLEND_ prefix runnerViper.SetEnvPrefix("JIGGABLEND") @@ -51,6 +53,8 @@ func init() { runnerViper.BindPFlag("log_level", runnerCmd.Flags().Lookup("log-level")) runnerViper.BindPFlag("verbose", runnerCmd.Flags().Lookup("verbose")) runnerViper.BindPFlag("poll_interval", runnerCmd.Flags().Lookup("poll-interval")) + runnerViper.BindPFlag("force_cpu_rendering", runnerCmd.Flags().Lookup("force-cpu-rendering")) + runnerViper.BindPFlag("disable_hiprt", runnerCmd.Flags().Lookup("disable-hiprt")) } func runRunner(cmd *cobra.Command, args []string) { @@ -63,6 +67,8 @@ func runRunner(cmd *cobra.Command, args []string) { logLevel := runnerViper.GetString("log_level") verbose := runnerViper.GetBool("verbose") pollInterval := runnerViper.GetDuration("poll_interval") + forceCPURendering := runnerViper.GetBool("force_cpu_rendering") + disableHIPRT := runnerViper.GetBool("disable_hiprt") var r *runner.Runner @@ -118,7 +124,7 @@ func runRunner(cmd *cobra.Command, args []string) { } // Create runner - r = runner.New(managerURL, name, hostname) + r = runner.New(managerURL, name, hostname, forceCPURendering, disableHIPRT) // Check for required tools early to fail fast if err := r.CheckRequiredTools(); err != nil { diff --git a/internal/runner/runner.go b/internal/runner/runner.go index 507c10f..15b0295 100644 --- a/internal/runner/runner.go +++ b/internal/runner/runner.go @@ -54,10 +54,15 @@ type Runner struct { hasNVIDIA bool gpuBackendProbed bool gpuDetectionFailed bool + + // forceCPURendering forces CPU rendering for all jobs regardless of metadata/backend detection. + forceCPURendering bool + // disableHIPRT disables HIPRT acceleration when configuring Cycles HIP devices. + disableHIPRT bool } // New creates a new runner. -func New(managerURL, name, hostname string) *Runner { +func New(managerURL, name, hostname string, forceCPURendering, disableHIPRT bool) *Runner { manager := api.NewManagerClient(managerURL) r := &Runner{ @@ -67,6 +72,9 @@ func New(managerURL, name, hostname string) *Runner { processes: executils.NewProcessTracker(), stopChan: make(chan struct{}), processors: make(map[string]tasks.Processor), + + forceCPURendering: forceCPURendering, + disableHIPRT: disableHIPRT, } // Generate fingerprint @@ -307,6 +315,8 @@ func (r *Runner) executeJob(job *api.NextJobResponse) (err error) { r.IsGPULockedOut(), r.HasHIP(), r.GPUDetectionFailed(), + r.forceCPURendering, + r.disableHIPRT, func() { r.SetGPULockedOut(true) }, ) diff --git a/internal/runner/tasks/processor.go b/internal/runner/tasks/processor.go index f17be6c..4441637 100644 --- a/internal/runner/tasks/processor.go +++ b/internal/runner/tasks/processor.go @@ -49,6 +49,10 @@ type Context struct { GPUDetectionFailed bool // OnGPUError is called when a GPU error line is seen in render logs; typically sets runner GPU lockout. OnGPUError func() + // ForceCPURendering is a runner-level override that forces CPU rendering for all jobs. + ForceCPURendering bool + // DisableHIPRT is a runner-level override that disables HIPRT acceleration in Blender. + DisableHIPRT bool } // ErrJobCancelled indicates the manager-side job was cancelled during execution. @@ -73,6 +77,8 @@ func NewContext( gpuLockedOut bool, hasHIP bool, gpuDetectionFailed bool, + forceCPURendering bool, + disableHIPRT bool, onGPUError func(), ) *Context { if frameEnd < frameStart { @@ -97,6 +103,8 @@ func NewContext( GPULockedOut: gpuLockedOut, HasHIP: hasHIP, GPUDetectionFailed: gpuDetectionFailed, + ForceCPURendering: forceCPURendering, + DisableHIPRT: disableHIPRT, OnGPUError: onGPUError, } } @@ -182,6 +190,9 @@ func (c *Context) ShouldEnableExecution() bool { // (runner GPU lockout, GPU detection failed at startup for any version, metadata force_cpu, // or Blender < 4.x when the runner has HIP). func (c *Context) ShouldForceCPU() bool { + if c.ForceCPURendering { + return true + } if c.GPULockedOut { return true } diff --git a/internal/runner/tasks/render.go b/internal/runner/tasks/render.go index 20f6bee..d9e5bad 100644 --- a/internal/runner/tasks/render.go +++ b/internal/runner/tasks/render.go @@ -27,8 +27,8 @@ func NewRenderProcessor() *RenderProcessor { // gpuErrorSubstrings are log line substrings that indicate a GPU backend error (matched case-insensitively); any match triggers full GPU lockout. var gpuErrorSubstrings = []string{ - "illegal address in hip", // HIP (AMD) e.g. "Illegal address in HIP" or "Illegal address in hip" - "hiperror", // hipError* codes + "illegal address in hip", // HIP (AMD) e.g. "Illegal address in HIP" or "Illegal address in hip" + "hiperror", // hipError* codes "hip error", "cuda error", "cuerror", @@ -107,7 +107,9 @@ func (p *RenderProcessor) Process(ctx *Context) error { v := ctx.GetBlenderVersion() major := parseBlenderMajor(v) isPre4 := v != "" && major >= 0 && major < 4 - if ctx.GPUDetectionFailed { + if ctx.ForceCPURendering { + ctx.Info("Runner compatibility flag is enabled: forcing CPU rendering for this job") + } else if ctx.GPUDetectionFailed { ctx.Info("GPU backend detection failed at startup—we could not determine whether this machine has HIP (AMD) or NVIDIA GPUs, so rendering will use CPU to avoid compatibility issues") } else if isPre4 && ctx.HasHIP { ctx.Info("Blender < 4.x has no official HIP support: using CPU rendering only") @@ -193,6 +195,7 @@ func (p *RenderProcessor) createRenderScript(ctx *Context, renderFormat string) settingsMap = make(map[string]interface{}) } settingsMap["force_cpu"] = ctx.ShouldForceCPU() + settingsMap["disable_hiprt"] = ctx.DisableHIPRT settingsJSON, err := json.Marshal(settingsMap) if err == nil { if err := os.WriteFile(renderSettingsFilePath, settingsJSON, 0644); err != nil { diff --git a/pkg/scripts/scripts/render_blender.py.template b/pkg/scripts/scripts/render_blender.py.template index 437289c..a7a6235 100644 --- a/pkg/scripts/scripts/render_blender.py.template +++ b/pkg/scripts/scripts/render_blender.py.template @@ -175,9 +175,13 @@ if render_settings_override: if current_engine == 'CYCLES': # Check if CPU rendering is forced force_cpu = False + disable_hiprt = False if render_settings_override and render_settings_override.get('force_cpu'): force_cpu = render_settings_override.get('force_cpu', False) print("Force CPU rendering is enabled - skipping GPU detection") + if render_settings_override and render_settings_override.get('disable_hiprt'): + disable_hiprt = render_settings_override.get('disable_hiprt', False) + print("Disable HIPRT flag is enabled") # Ensure Cycles addon is enabled try: @@ -321,7 +325,16 @@ if current_engine == 'CYCLES': try: if best_device_type == 'HIP': # HIPRT (HIP Ray Tracing) for AMD GPUs - if hasattr(cycles_prefs, 'use_hiprt'): + if disable_hiprt: + if hasattr(cycles_prefs, 'use_hiprt'): + cycles_prefs.use_hiprt = False + print(f" Disabled HIPRT (HIP Ray Tracing) via runner compatibility flag") + elif hasattr(scene.cycles, 'use_hiprt'): + scene.cycles.use_hiprt = False + print(f" Disabled HIPRT (HIP Ray Tracing) via runner compatibility flag") + else: + print(f" HIPRT toggle not available on this Blender version") + elif hasattr(cycles_prefs, 'use_hiprt'): cycles_prefs.use_hiprt = True print(f" Enabled HIPRT (HIP Ray Tracing) for faster rendering") elif hasattr(scene.cycles, 'use_hiprt'):