Worker: check BLENDER_CMD environment variable (for multi-GPU Eevee rendering) #104193
No reviewers
Labels
No Label
Good First Issue
Priority
High
Priority
Low
Priority
Normal
Status
Archived
Status
Confirmed
Status
Needs Info from Developers
Status
Needs Information from User
Status
Needs Triage
Status
Resolved
Type
Bug
Type
Design
Type
Job Type
Type
Known Issue
Type
Patch
Type
Report
Type
To Do
No Milestone
No Assignees
3 Participants
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: studio/flamenco#104193
Loading…
Reference in New Issue
Block a user
No description provided.
Delete Branch "MKRelax/flamenco:worker-use-blender-from-env"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Worker update: if "blender" is specified by the manager, check the
BLENDER_CMD
environment variable for a Blender path (before checking$PATH
and.blend
file association).Use case: Assigning a specific GPU to each worker, e.g. for Eevee rendering on multiple GPUs at the same time.
Usage:
blender.exe
to e.g.blender-gpu0.exe
andblender-gpu1.exe
;OpenGL Rendering GPU
for Eevee and/orCUDA Rendering GPU
for Cycles);FLAMECO_HOME
andFLAMENCO_WORKERNAME
, but alsoBLENDER_CMD
with a full path to the desiredblender-gpu?.exe
blender-gpu?.exe
and in turn, use its own GPU for rendering.Known issues / improvements:
find_blender.CheckBlender()
should be modified, but that code is (too) tightly coupled withmeta.FindBlenderExePath()
, which is also used by the manager.__NV_PRIME_RENDER_OFFLOAD
for similar behaviour.Test cases are provided. Please review the code because this is my first Go code ever ;)
Thanks for the PR! This looks useful indeed.
I think the environment var should be named
FLAMENCO_BLENDER_PATH
. This would make it consistent with the otherFLAMENCO_...
variables, and makes it clear that it should contain a path. For me a 'command' would be something that could also include CLI arguments to run.And extra 👍 for writing unit tests :)
This is an issue, indeed. If you want to refactor/redesign
meta.FindBlenderExePath()
or how it is coupled withfind_blender.CheckBlender()
that would be great.Good to know :)
@ -52,1 +52,4 @@
// EnvironmentVariable returns the content of the BLENDER_CMD environment variable.
func EnvironmentVariable() string {
return os.Getenv("BLENDER_CMD")
I think the name of this variable should be in a constant. Can be in the same file. That way the string literal doesn't get repeated everywhere.
@ -44,0 +46,4 @@
func TestGetBlenderCommandFromEnvironment(t *testing.T) {
// Without environment variable, we expect an empty string
path := EnvironmentVariable()
This should check that the environment variable isn't already set while running the test ;-)
@ -44,0 +52,4 @@
// Try finding the blender path in the BLENDER_CMD environment variable
err := os.Setenv("BLENDER_CMD", "/path/specified/in/env/to/blender")
if err != nil {
t.Fatal("Could not set BLENDER_CMD environment variable")
The
if
andt.Fatal()
can be replaced withrequire.NoError(t, err, "Could not set BLENDER_CMD environment variable")
.@ -93,0 +90,4 @@
// No directory path given. Check that the executable can be found in the
// environment variable BLENDER_CMD or on the path.
if path := find_blender.EnvironmentVariable(); path != "" {
logger.Info().Str("path", path).Msg("found Blender in environment")
I wouldn't say "found Blender" here, because the path wasn't checked. Blender may not even exist there.
Maybe
find_blender.EnvironmentVariable()
can return two strings,(name, path)
wherename
is the name of the environment variable, andpath
is the actual value. That way you can log here "using Blender from {environment variable name}".I did change the log message to "using Blender from FLAMENCO_BLENDER_PATH".
However, since the name of the variable is (now) a constant I think there is no need to return it from
find_blender.EnvironmentVariable()
. Most code would not be interested in it anyway and probably ignore it using a_
variable. So the log message simply uses the constant.I think it's still good to return the variable name. That way you get less coupling between the different parts of the code, making things more maintainable.
da2ed4e69b
toa2d9612dc2
a2d9612dc2
to070ef06921
070ef06921
to3416a5ff54
All of your suggestions have been implemented including renaming to
FLAMENCO_BLENDER_PATH
, see commits.I agree that the worker MUST report the correct Blender version that it would use if the manager does not specify a path. That has been implemented by adding a
FLAMENCO_BLENDER_PATH
check tofind_blender.CheckBlender()
. However, this code is also used by the manager and there are a few considerations:find_blender.CheckBlender()
, the full_path check (if crosspath.Dir(exename) != "."
) was moved above the file association check (if exename==""
). This works even whenexename
is empty, becausecrosspath.Dir("")
returns"."
in that case and the execution flow continues as before.The
$FLAMENCO_BLENDER_PATH
check has been inserted between the two, so the order is 1) full_path, 2)$FLAMENCO_BLENDER_PATH
, 3).blend
association, 4)$PATH
. This also leaves theCheckBlenderExePath
API call unaffected, which uses: 1) full_path.The
$FLAMENCO_BLENDER_PATH
environment variable would normally not be present on the manager. But IF it is, that could impact the managers Setup Assistant:api.BlenderPathSourceInputPath
(input_path
).We could add a new type to the
api.BlenderPathSource
(e.g.blender_path_env
), but such a "Blender Path Source" has no meaning on the manager and does not belong in the API imo. Also thepath_envvar
type did not apply since a) the executable may not be on the $PATH and s) when selected, the manager would store "" in the config file;Improving this would require a refactoring of the blender-path / blender-version stuff
find_blender.CheckBlender()
,meta.FindBlenderExePath()
andmeta.CheckBlenderExePath()
. I'm not sure if I'm up to that, because I have no Go experience whatsoever. But I could try if you want me to!For now, this pull request should implement the
FLAMENCO_BLENDER_PATH
functionality on the worker, including correct logging output, with minimal impact on the manager.Please review again, feedback is welcome! Also, I could squash commits if that would make things easier, just let me know.
WIP: Worker: check BLENDER_CMD environment variable (for multi-GPU Eevee rendering)to Worker: check BLENDER_CMD environment variable (for multi-GPU Eevee rendering)I'll do another review this week, thanks for the patch and the clear description of your reasonings. That'll make things a lot easier :)
Squashing the commits isn't necessary, in the end it's going to be squashed anyway.
Things are improving, but the API changes on the
find_blender
module could be better.There is still a strong coupling between
find_blender.EnvironmentVariable()
and the code ininternal/worker/command_blender.go
. Moving things into a constant hasn't changed this, as the code incommand_blender.go
still needs to know thatEnvironmentVariable()
is using that particular constant.This is a rather weak argument, as currently literally 100% of the production (i.e. non-test) code that uses this function is interested in it.
Looking at the code a bit more, I think it would be a good idea to push things a little further, and make
EnvironmentVariable()
return a tuple(CheckBlenderResult, error)
, just like the other functions. Theapi.BlenderPathSource
enum-like type can be expanded to include this new environment variable, and then it's officially part of the API.Ok, I fully agree.
Adding this to the API would mean that the Manager would need to handle this kind of path whenever it receives it. That would be the case if
$FLAMENCO_BLENDER_PATH
happens to be set in the managers' environment. Again I don't see a use case for that, but IF that variable is set, I guess we would need to present that path and its source/reason to the user just like with 'file association' and 'path'.There's one small thing that bothers me about that. IF the variable is set, a user would probably target worker(s) on that machine to use it. If the manager picks up on it, put that path in
flamenco-manager.yaml
and pass it to workers, that would effectively prevent workers from using$FLAMENCO_BLENDER_PATH
themselves, because they only to that if they get 'blender' from the manager (which basically means "use your own Blender").I'm not sure, but I believe if the manager finds Blender through its own
$PATH
, it passes 'blender' (not the path itself) to workers to instruct them to use (their)$PATH
as well. I think we should do the same if the manager finds blender through the$FLAMENCO_BLENDER_PATH
: also pass 'blender' to workers to allow them to use their own$FLAMENCO_BLENDER_PATH
or$PATH
.Regarding the
BlenderPathSource
enum, would you preferblender_path_envvar
(which is short, but might be too similar to the existingpath_envvar
) orflamenco_blender_path_envvar
(which is long, but consistent becausepath_envvar
corresponds to$PATH
andflamenco_blender_path_envvar
corresponds to$FLAMENCO_BLENDER_PATH
)?👍
I agree that this would be better to split up somehow, and only have the Workers look at
$FLAMENCO_BLENDER_PATH
. It would be a bigger change, but I agree that this would be cleaner.Having said that, that (unification of Manager and Worker behaviour) wasn't my intention with 'adding it to the API'. My intention was to turn this:
into something like this (code untested, just to illustrate my thoughts):
For this to work, the
BlenderPathSource
enum inpkg/api/flamenco-openapi.yaml
would have to be expanded withenv_variable
.Yeah, that's a good thing to prevent. How I see it (but feel free to come up with counter-arguments): if the Manager passes a path, the Worker should use that. If the Manager just says
blender
, the Worker is free to use$FLAMENCO_BLENDER_PATH
,$PATH
,.blend
file associations, etc.With the above, I think this is no longer relevant, as I think the Manager simply shouldn't use that environment variable.
This does touch on #100195 though. Maybe it's something you would find interesting working on at some point?
I think
env_variable
is good enough. I don't think we'll be adding even more environment variables for this, so just having an option that indicates "that one variable that we use for this stuff" is IMO good enough.We've briefly discussed this this week, then you explained that the design is to have "Workers as dumb as possible, with as little configuration as possible". I agree with that, however the backside of this is that the Manager is trying to be "too smart" while it actually is not. A few thoughts:
"C:\Bin\Blender 3.4\blender.exe"
on Windows), the manager currently copies that path inflamenco-manager.yaml
not only for thewindows
platform (which again, might or might not be a reasonable suggestion for workers) but also for thelinux
anddarwin
platforms (which is definitely wrong). Similarly on Linux, it would use the given Linux path (say"/usr/local/bin/blender"
) for thewindows
anddarwin
platforms (which is definitely wrong for Windows workers). Fact is, a manager simply has no clue about reasonable paths for other platforms, so it should not try to be smart about it. The best it could do is pass "blender" to other platforms (which would be better than passing Windows paths to Linux workers and vice versa).blender
is any different from runningffmpeg
,echo
,sleep
or some other executable likegaffer
(in the chat recently). At the momentecho
andsleep
use the OS commands andffmpeg
is bundled, but for any other executables likegaffer
, workers would probably use their $PATH. Questions are: if the managers tell workers whichblender
path they should use, would it also force whichgaffer
path to use? Or the other way: if we wouldn't pass hardcodedgaffer
paths, why do we do that forblender
?My suggestions would be:
"blender"
implementation (or more generic""
: not supplying a path) should be the default for all executables on all platforms, i.e. workers decide for themselves which executables they use, using a)flamenco-worker.yaml
, b) environment variable likeFLAMENCO_{cmd}_PATH
e.g.FLAMENCO_BLENDER_PATH
orFLAMENCO_GAFFER_PATH
, c) bundled files likeffmpeg
,d) file association if available, e)$PATH
.$PATH
, which keeps them "dumb" enough IMO.flamenco-worker.yaml
would be quite logical and straightforward. Using environment variables would be more 'advanced', but still quite common practice in bat/sh/bash scripts. Both would keep system administrators in control of which executables are run by workers.Feel free to move this discussion somewhere else, but the essence of my PR is that in some cases, workers want to decide for themselves which executables to use (in this case: to force which GPU to use, but that's just one of many reasons). So relevant to this PR.
In the mean time, I will work on the
func EnvironmentVariable()
API. I agree with that, thanks for the feedback.Let's not discuss the topic of #100195 right here. IMO those are two separate things.
This is exactly the setup we have at Blender Animation Studio, where all machines use Blender from the same path. It's quite simple to work with.
Yes, indeed. Instead of configuring each and every worker individually, Flamenco assumes that you have configured them identically and keeps as much as the configuration in a single location.
This is something that is out of scope for Flamenco itself. Flamenco is made for individual and smaller studios. If bigger studios can/want to use it, that's fantastic and certainly welcome. It's not something that the design was aiming at, though.
You can read more about this in https://studio.blender.org/blog/flamenco-3-a-new-beginning/, I'll have to put that into the Flamenco documentation as well at some point.
This is entirely out of scope for the project. Again, if you want to use Flamenco like this and it works for you, that's amazing, but it's not part of Flamenco's design.
Just like you can run Blender with
blender --python-expr 'import os; os.removedirs('/')
and it'll have the same effect and work in a cross-platform way as well.Flamenco was made with specific goals in mind: rendering films with Blender. 90% of it was implemented in just 7 months by a single person. Of course there are going to be limitations to what it can do.
This is in no way meant to be a rant, and I apologize if it came across like that.
With multi-GPU Eevee rendering as goal, I am (lacking better options) looking for a way to have a worker use a specific executable. This contradicts with the paradigm "if the Manager passes a path, the Worker should use that" and you asked me to "feel free to come up with counter-arguments". That's all I intended to do. I very much respect Flamenco, the redesign of v3, the effort you put in it and you as a developer.
For this PR, I will adjust the
func EnvironmentVariable()
as per your comments and addenv_variable
toBlenderPathSource
enumeration. I the (rare) case the a Manager receives such aenv_variable
path, it would behave the same as withfile association
or$PATH
paths and put"blender"
in the configuration file.🙏
My thought was that with a setup like yours, you could use
"blender"
as the configured path in the Manager, in which case the environment variable would be used on the Worker. Wouldn't that suit your needs? Or would it be more convenient to have that envvar override whatever the Manager is giving?Now that I think of it some more, since envvars can't change while the Worker is running, it can log the location of Blender it would use. That would make things more visible, working around the issue that environment variables are (for most users) invisible things.
Thanks.
That's fine; if it turns out to be a practical issue, we can look at solutions then.
Hi @MKRelax , do you still have an interest in working on this?
@dr.sybren It's been a while indeed...
From a functional point of view, I think it's very valueable to have workers use their own instance of the Blender executable. In my case, I have 8 workers on 5 machines each running exclusively on their own GPU.
On Linux+NVidia, one could set
__NV_PRIME_RENDER_OFFLOAD
environment variable to configure which GPU to use for which process. On Windows workers, one needs separate executables e.g.blender-gpu0.exe
andblender-gpu1.exe
, each configured to use a specific GPU.To keep the knowledge about the exact
.exe
names on the worker, and to keep things similar to Linux scripts, using environment variables to configure workers seems a viable solution.If I remember correctly, there were a few hurdles to overcome:
blender-gpu0.exe
in its environment and instruct all workers to use that. I'd rather have a) the manager not look atFLAMENCO_BLENDER_PATH
at all or b) the workers being able to overrule the managers suggestion for the blender path (which seems a bit against the 'dumb worker' philosophy).EnvironmentVariable()
function should return agetResultWithVersion
. However, to do that, we would need to addenv_variable
to theBlenderPathSource
options of the OpenAPI. That would make "setting an environment variable" a full-fledged way to configure the blender path, also on the manager. Similar to 1), we would need to decide on what that would actually mean, what purpose it serves and how workers should deal with it if they have environment settings of their own.CheckBlender
function might need some refactoring since it already calls itself recursively and also checking for environment variables makes things "too messed up".From a technical/practical point of view, I'm not a Go developer and I'm usually on Windows which makes compiling Flamenco a bit of a hassle (had to modify the makefile). For example. I have no idea how to regenerate the API codebase after adding to
BlenderPathSource
. I might switch over to MinGW-w64, WSL or an Ubuntu machine to make things easier, but it is a reason I left this aside for some time. Also I don't really need to, because the proposed changes have been working beautifully for me for many months now. I mainly just wanted to share the idea and the code. I would like to make it a better fit to the codebase but I lack both Go experience and insights on Flamenco's architecture.From an architecture point of view, you made clear that Flamenco was developed from a studio perspective (where a manager actively manages "dumb" workers) while I personally have a "internet-of-workers" perspective (where all my machines (Windows desktops/laptops/game PC's, Linux servers, mining rigs) become workers that serve one manager today (e.g. from my home office) and another manager tomorrow (e.g. from some client office). Given your (now deleted) remark about me "ranting" about things that are "out of scope", there is a bit of a mismatch there. But let me state again that it's great that Flamenco serves both use cases.
Given that the technical changes are relatively small and the multi-GPU setup is discussed in the FAQ of this project (and some people reached out and copied my setup), it might be worthwhile to persue this, but also more efficient to have someone work on this with more Go/Flamenco experience.
3416a5ff54
to9d77497e51
I moved my files to WSL (Linux) which makes development much easier. Updated the OAPI and now returning a
api.BlenderPathSourceEnvVariable
for environment paths. TheEnvironmentVariable
function also returns the name of the variable and anderr
if the file does not exist or is a directory. In the testcases, I pass the go file itself as Blender executable because it must exist, a bit of a hack.Since configuring by
env_variable
is officially part of the API now, it is also being picked up by the manager so for completeness, I have added this option to the manager Setup Assistant. It has no usecase IMO but if we leave it in, it should probably be documented.The
FindBlender
code has not been refactored so as @victorasm commented before, it is probably still "too messed up".I will test this PR in practice the next couple of days.
42660941e5
to46ab0075fb
That's easy to separate. Also there's #100195 for that.
The other two points I agree with, it's better to clean some things up before expanding the functionality.
And yeah, building on Windows is a bit sub-par, that's why I put "Convert the build toolchain to Mage to simplify development and packaging on Windows." on the Google Summer of Code ideas list. Hopefully somebody can help with that.
That sounds good!
Which go file? (I haven't had time to actually look at the code changes yet)
👍
Ok. As long as it's covered with unit tests, then refactoring later should be fine.
👍 Just press the "Re-request Review" button if you feel the patch is ready for reviewing. It's in the reviewers list next to my name.
Checkout
From your project repository, check out a new branch and test the changes.