Skip to content

Commit

Permalink
Merge branch 'icvr/adaptor_improvements' into icvr/perforce
Browse files Browse the repository at this point in the history
# Conflicts:
#	auto-update-plugin.bat
#	pyproject.toml
#	src/deadline/unreal_submitter/common.py
#	src/deadline/unreal_submitter/exceptions.py
#	src/deadline/unreal_submitter/perforce_api.py
#	src/deadline/unreal_submitter/unreal_open_job/unreal_open_job.py
#	src/deadline/unreal_submitter/unreal_open_job/unreal_open_job_step.py
#	src/unreal_plugin/Source/UnrealDeadlineCloudService/Private/DeadlineCloudJobSettings/DeadlineCloudDetailsWidgetsHelper.cpp
#	test/deadline_submitter_for_unreal/unit/test_preforce_api.py
  • Loading branch information
alf-devnull committed Feb 20, 2025
2 parents 6d8a789 + 328680a commit 3bfef92
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 82 deletions.
2 changes: 1 addition & 1 deletion hatch.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ pre-install-commands = [
[envs.default.scripts]
sync = "pip install -r requirements-testing.txt"
test = "pytest --cov-config pyproject.toml {args:test}"
typing = "mypy --implicit-optional {args:src test}"
typing = "mypy {args:src test}"
style = [
"ruff check {args:.}",
"black --check --diff {args:.}",
Expand Down
28 changes: 17 additions & 11 deletions src/deadline/unreal_adaptor/UnrealAdaptor/adaptor.py
Original file line number Diff line number Diff line change
Expand Up @@ -345,12 +345,17 @@ def _start_unreal_client(self) -> None:
"-log",
"-unattended",
"-stdout",
"-NoLoadingScreen",
"-NoScreenMessages",
"-RenderOffscreen",
"-allowstdoutlogverbosity",
]

remote_execution = os.getenv("REMOTE_EXECUTION", "True")
if remote_execution == "True":
log_args += [
"-NoLoadingScreen",
"-NoScreenMessages",
"-RenderOffscreen"
]

extra_cmd_args = extra_cmd_str.split(" ")

args = [unreal_exe, unreal_project_path]
Expand Down Expand Up @@ -503,14 +508,15 @@ def on_run(self, run_data: dict) -> None:
# This is always an error case because the Unreal Client should still be running and
# waiting for the next command. If the thread finished, then we cannot continue
exit_code = self._unreal_client.returncode
self._record_error_and_raise(
exc=RuntimeError(
"Unreal exited early and did not render successfully, please check render logs. "
f"Exit code {exit_code}"
),
exception_scope="on_run",
exit_code=exit_code,
)
if exit_code != 0:
self._record_error_and_raise(
exc=RuntimeError(
"Unreal exited early and did not render successfully, please check render logs. "
f"Exit code {exit_code}"
),
exception_scope="on_run",
exit_code=exit_code,
)

def on_stop(self) -> None:
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"job_configuration_path": { "type": "string" },
"queue_manifest_path": { "type": "string" },
"script_path": { "type": "string" },
"script_args": { "type": "object" },
"script_args": { "type": "string" },
"chunk_size": { "type": "integer" },
"chunk_id": { "type": "integer" },
"output_path": { "type": "string" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ def regex_pattern_progress() -> list[re.Pattern]:

@staticmethod
def regex_pattern_complete() -> list[re.Pattern]:
return [re.compile(".*Custom Step Executor: Complete")]
return [
re.compile(".*Custom Step Executor: Complete"),
re.compile(".*QUIT EDITOR")
]

@staticmethod
def regex_pattern_error() -> list[re.Pattern]:
Expand Down Expand Up @@ -60,18 +63,33 @@ def validate_script(script_path: str) -> ModuleType:

def run_script(self, args: dict) -> bool:
"""
Executing a script using the provided arguments.
Executing a script using the provided arguments by calling the
https://dev.epicgames.com/documentation/en-us/unreal-engine/python-api/class/PythonScriptLibrary?application_version=5.4#unreal.PythonScriptLibrary.execute_python_command_ex
:param args: A dictionary that contains the arguments for running the script.
:return: boolean indicating the script run successfully or not.
"""

try:
script_module = UnrealCustomStepHandler.validate_script(script_path=args["script_path"])
script_args = args.get("script_args", {})
result = script_module.main(**script_args)
logger.info(f"Custom Step Executor: Complete: {result}")
import unreal
result = unreal.PythonScriptLibrary.execute_python_command_ex(
f"{args['script_path']} {args.get('script_args', '')}",
execution_mode=unreal.PythonCommandExecutionMode.EXECUTE_FILE,
file_execution_scope=unreal.PythonFileExecutionScope.PUBLIC
)

if result:
failure, _ = result

# If the command ran successfully, this will return None else the
# failure
# https://dev.epicgames.com/documentation/en-us/unreal-engine/python-api/class/PythonScriptLibrary?application_version=5.4#unreal.PythonScriptLibrary.execute_python_command_ex
if failure:
raise RuntimeError(failure)

logger.info(f"Custom Step Executor Result: {result}")
return True

except Exception as e:
logger.info(
f"Custom Step Executor: Error: "
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,11 @@ def run_script(self, args: dict) -> bool:
movie_pipeline_queue_subsystem=subsystem,
queue_manifest_path=args["queue_manifest_path"],
)
elif args.get("queue_path"):
UnrealRenderStepHandler.create_queue_from_queue_asset(
movie_pipeline_queue_subsystem=subsystem,
movie_pipeline_queue_asset_path=args["queue_path"]
)
else:
UnrealRenderStepHandler.create_queue_from_job_args(
movie_pipeline_queue_subsystem=subsystem,
Expand Down
79 changes: 47 additions & 32 deletions src/deadline/unreal_adaptor/UnrealClient/unreal_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,29 @@

logger = get_logger()

# Global variables for keep UnrealClient running and prevent deletion by Unreal garbage collector
MESSAGE_POLL_INTERVAL = float(os.getenv("MESSAGE_POLL_INTERVAL", 1.0))
unreal_client = None
client_handler = None


class UnrealClient(WinClientInterface):
"""
Socket DCC client implementation for UnrealEngine that send requests for actions and execute them.
"""

def __init__(self, socket_path: str) -> None:
def __init__(
self, socket_path: str, message_poll_interval: float = MESSAGE_POLL_INTERVAL
) -> None:
super().__init__(socket_path)
self.handler: BaseStepHandler
self.actions.update({"set_handler": self.set_handler, "client_loaded": self.client_loaded})
self.message_poll_interval = message_poll_interval
self.time_elapsed = 0.0

def client_loaded(self, *args, **kwargs) -> None:
"""Log the message that UnrealClient loaded"""

logger.info(f"{self.__class__.__name__} loaded")

def set_handler(self, handler_dict: dict) -> None:
Expand All @@ -53,36 +63,55 @@ def close(self, args: Optional[dict] = None) -> None:
import unreal

logger.info("Quit the Editor: normal shutdown")

global client_handler

if client_handler:
unreal.unregister_slate_post_tick_callback(client_handler)

unreal.SystemLibrary.quit_editor()

def graceful_shutdown(self, *args, **kwargs) -> None:
"""Close the Unreal Engine if the UnrealAdaptor terminate the client with 0s grace time"""
import unreal

logger.info("Quit the Editor: graceful shutdown")

global client_handler

if client_handler:
unreal.unregister_slate_post_tick_callback(client_handler)

unreal.SystemLibrary.quit_editor()

def poll(self) -> None:
def poll(self, delta_time: float) -> None:
"""
This function will poll the server for the next task. If the server is in between Subtasks
(no actions in the queue), a backoff function will be called to add a delay between the
requests.
:param delta_time: Time increment after previous tick in Unreal Slate
:type delta_time: float
"""
status, reason, action = self._request_next_action()
if status == HTTPStatus.OK:
if action is not None:

self.time_elapsed += delta_time
if self.time_elapsed >= self.message_poll_interval:
self.time_elapsed = 0
status, reason, action = self._request_next_action()
if status == HTTPStatus.OK:
if action is not None:
print(
f"Performing action: {action}",
flush=True,
)
self._perform_action(action)
else: # Any other status or reason
print(
f"Performing action: {action}",
f"ERROR: An error was raised when trying to connect to the server: {status} "
f"{reason}",
file=sys.stderr,
flush=True,
)
self._perform_action(action)
else: # Any other status or reason
print(
f"ERROR: An error was raised when trying to connect to the server: {status} "
f"{reason}",
file=sys.stderr,
flush=True,
)


def main():
Expand All @@ -103,25 +132,11 @@ def main():
f"{os.environ['UNREAL_ADAPTOR_SOCKET_PATH']}"
)

@unreal.uclass()
class OnTickThreadExecutorImplementation(unreal.PythonGameThreadExecutor):
"""
Python implementation of the OnTickThreadExecutor class that runs the
:meth:`deadline.unreal_adaptor.UnrealClient.unreal_client.UnrealClient.poll()`
"""

client = UnrealClient(socket_path)
time_elapsed = unreal.uproperty(float)

def _post_init(self):
self.time_elapsed = 0
global unreal_client
global client_handler

@unreal.ufunction(override=True)
def execute(self, delta_time: float):
self.time_elapsed += delta_time
if self.time_elapsed >= 1:
self.time_elapsed = 0
self.client.poll()
unreal_client = UnrealClient(socket_path)
client_handler = unreal.register_slate_post_tick_callback(unreal_client.poll)


if __name__ == "__main__": # pragma: no cover
Expand Down
31 changes: 0 additions & 31 deletions src/deadline/unreal_submitter/unreal_open_job/__init__.py
Original file line number Diff line number Diff line change
@@ -1,32 +1 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# from deadline.unreal_submitter.unreal_open_job.unreal_open_job_entity import (
# UnrealOpenJobEntity,
# OpenJobParameterNames,
# OpenJobStepParameterNames,
# PARAMETER_DEFINITION_MAPPING
# )
# from deadline.unreal_submitter.unreal_open_job.unreal_open_job import (
# UnrealOpenJob,
# RenderUnrealOpenJob,
# UnrealOpenJobParameterDefinition
# )
# from deadline.unreal_submitter.unreal_open_job.unreal_open_job_step import (
# UnrealOpenJobStep,
# RenderUnrealOpenJobStep,
# UnrealOpenJobStepParameterDefinition
# )
# from deadline.unreal_submitter.unreal_open_job.unreal_open_job_environment import (
# UnrealOpenJobEnvironment,
# UnrealOpenJobUgsEnvironment
# )
# from deadline.unreal_submitter.unreal_open_job.unreal_open_job_shared_settings import (
# JobSharedSettings
# )
# from deadline.unreal_submitter.unreal_open_job.unreal_open_job_step_host_requirements import (
# HostRequirements
# )
# from deadline.unreal_submitter.unreal_open_job.unreal_open_job_parameters_consistency import (
# ParametersConsistencyChecker,
# ParametersConsistencyCheckResult
# )

0 comments on commit 3bfef92

Please sign in to comment.