Merge branch 'feature/event_driven_updates' into develop
This commit is contained in:
commit
b3230177d8
@ -8,5 +8,8 @@ build:
|
|||||||
name: multi_user
|
name: multi_user
|
||||||
paths:
|
paths:
|
||||||
- multi_user
|
- multi_user
|
||||||
|
only:
|
||||||
|
refs:
|
||||||
|
- master
|
||||||
|
- develop
|
||||||
|
|
||||||
|
@ -11,4 +11,7 @@ test:
|
|||||||
- python -m pip install blender-addon-tester
|
- python -m pip install blender-addon-tester
|
||||||
- python scripts/test_addon.py
|
- python scripts/test_addon.py
|
||||||
|
|
||||||
|
only:
|
||||||
|
refs:
|
||||||
|
- master
|
||||||
|
- develop
|
||||||
|
@ -44,7 +44,7 @@ from . import environment, utils
|
|||||||
|
|
||||||
|
|
||||||
DEPENDENCIES = {
|
DEPENDENCIES = {
|
||||||
("replication", '0.0.21a4'),
|
("replication", '0.0.21a5'),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -85,20 +85,22 @@ class ApplyTimer(Timer):
|
|||||||
super().__init__(timout)
|
super().__init__(timout)
|
||||||
|
|
||||||
def execute(self):
|
def execute(self):
|
||||||
client = operators.client
|
client = operators.client
|
||||||
if client and client.state['STATE'] == STATE_ACTIVE:
|
if client and client.state['STATE'] == STATE_ACTIVE:
|
||||||
nodes = client.list(filter=self._type)
|
if self._type:
|
||||||
|
nodes = client.list(filter=self._type)
|
||||||
|
else:
|
||||||
|
nodes = client.list()
|
||||||
|
|
||||||
for node in nodes:
|
for node in nodes:
|
||||||
node_ref = client.get(uuid=node)
|
node_ref = client.get(uuid=node)
|
||||||
|
|
||||||
if node_ref.state == FETCHED:
|
if node_ref.state == FETCHED:
|
||||||
try:
|
try:
|
||||||
client.apply(node)
|
client.apply(node, force=True)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"Fail to apply {node_ref.uuid}: {e}")
|
logging.error(f"Fail to apply {node_ref.uuid}: {e}")
|
||||||
|
|
||||||
|
|
||||||
class DynamicRightSelectTimer(Timer):
|
class DynamicRightSelectTimer(Timer):
|
||||||
def __init__(self, timout=.1):
|
def __init__(self, timout=.1):
|
||||||
super().__init__(timout)
|
super().__init__(timout)
|
||||||
@ -239,7 +241,7 @@ class DrawClient(Draw):
|
|||||||
|
|
||||||
|
|
||||||
class ClientUpdate(Timer):
|
class ClientUpdate(Timer):
|
||||||
def __init__(self, timout=.032):
|
def __init__(self, timout=.1):
|
||||||
super().__init__(timout)
|
super().__init__(timout)
|
||||||
self.handle_quit = False
|
self.handle_quit = False
|
||||||
self.users_metadata = {}
|
self.users_metadata = {}
|
||||||
|
@ -35,7 +35,7 @@ from bpy.app.handlers import persistent
|
|||||||
from . import bl_types, delayable, environment, presence, ui, utils
|
from . import bl_types, delayable, environment, presence, ui, utils
|
||||||
from replication.constants import (FETCHED, STATE_ACTIVE,
|
from replication.constants import (FETCHED, STATE_ACTIVE,
|
||||||
STATE_INITIAL,
|
STATE_INITIAL,
|
||||||
STATE_SYNCING)
|
STATE_SYNCING, RP_COMMON, UP)
|
||||||
from replication.data import ReplicatedDataFactory
|
from replication.data import ReplicatedDataFactory
|
||||||
from replication.exception import NonAuthorizedOperationError
|
from replication.exception import NonAuthorizedOperationError
|
||||||
from replication.interface import Session
|
from replication.interface import Session
|
||||||
@ -67,7 +67,7 @@ class SessionStartOperator(bpy.types.Operator):
|
|||||||
runtime_settings = context.window_manager.session
|
runtime_settings = context.window_manager.session
|
||||||
users = bpy.data.window_managers['WinMan'].online_users
|
users = bpy.data.window_managers['WinMan'].online_users
|
||||||
admin_pass = runtime_settings.password
|
admin_pass = runtime_settings.password
|
||||||
|
use_extern_update = settings.update_method == 'DEPSGRAPH'
|
||||||
users.clear()
|
users.clear()
|
||||||
delayables.clear()
|
delayables.clear()
|
||||||
|
|
||||||
@ -88,18 +88,23 @@ class SessionStartOperator(bpy.types.Operator):
|
|||||||
bpy_factory.register_type(
|
bpy_factory.register_type(
|
||||||
type_module_class.bl_class,
|
type_module_class.bl_class,
|
||||||
type_module_class,
|
type_module_class,
|
||||||
timer=type_local_config.bl_delay_refresh,
|
timer=type_local_config.bl_delay_refresh*1000,
|
||||||
automatic=type_local_config.auto_push)
|
automatic=type_local_config.auto_push)
|
||||||
|
|
||||||
if type_local_config.bl_delay_apply > 0:
|
if settings.update_method == 'DEFAULT':
|
||||||
delayables.append(
|
if type_local_config.bl_delay_apply > 0:
|
||||||
delayable.ApplyTimer(
|
delayables.append(
|
||||||
timout=type_local_config.bl_delay_apply,
|
delayable.ApplyTimer(
|
||||||
target_type=type_module_class))
|
timout=type_local_config.bl_delay_apply,
|
||||||
|
target_type=type_module_class))
|
||||||
|
|
||||||
client = Session(
|
client = Session(
|
||||||
factory=bpy_factory,
|
factory=bpy_factory,
|
||||||
python_path=bpy.app.binary_path_python)
|
python_path=bpy.app.binary_path_python,
|
||||||
|
external_update_handling=use_extern_update)
|
||||||
|
|
||||||
|
if settings.update_method == 'DEPSGRAPH':
|
||||||
|
delayables.append(delayable.ApplyTimer(settings.depsgraph_update_rate/1000))
|
||||||
|
|
||||||
# Host a session
|
# Host a session
|
||||||
if self.host:
|
if self.host:
|
||||||
@ -145,7 +150,6 @@ class SessionStartOperator(bpy.types.Operator):
|
|||||||
logging.error(str(e))
|
logging.error(str(e))
|
||||||
|
|
||||||
# Background client updates service
|
# Background client updates service
|
||||||
#TODO: Refactoring
|
|
||||||
delayables.append(delayable.ClientUpdate())
|
delayables.append(delayable.ClientUpdate())
|
||||||
delayables.append(delayable.DrawClient())
|
delayables.append(delayable.DrawClient())
|
||||||
delayables.append(delayable.DynamicRightSelectTimer())
|
delayables.append(delayable.DynamicRightSelectTimer())
|
||||||
@ -161,6 +165,8 @@ class SessionStartOperator(bpy.types.Operator):
|
|||||||
|
|
||||||
@client.register('on_connection')
|
@client.register('on_connection')
|
||||||
def initialize_session():
|
def initialize_session():
|
||||||
|
settings = utils.get_preferences()
|
||||||
|
|
||||||
for node in client._graph.list_ordered():
|
for node in client._graph.list_ordered():
|
||||||
node_ref = client.get(node)
|
node_ref = client.get(node)
|
||||||
if node_ref.state == FETCHED:
|
if node_ref.state == FETCHED:
|
||||||
@ -179,10 +185,14 @@ class SessionStartOperator(bpy.types.Operator):
|
|||||||
# Register blender main thread tools
|
# Register blender main thread tools
|
||||||
for d in delayables:
|
for d in delayables:
|
||||||
d.register()
|
d.register()
|
||||||
|
|
||||||
|
if settings.update_method == 'DEPSGRAPH':
|
||||||
|
bpy.app.handlers.depsgraph_update_post.append(depsgraph_evaluation)
|
||||||
|
|
||||||
@client.register('on_exit')
|
@client.register('on_exit')
|
||||||
def desinitialize_session():
|
def desinitialize_session():
|
||||||
global delayables, stop_modal_executor
|
global delayables, stop_modal_executor
|
||||||
|
settings = utils.get_preferences()
|
||||||
|
|
||||||
for d in delayables:
|
for d in delayables:
|
||||||
try:
|
try:
|
||||||
@ -193,6 +203,9 @@ class SessionStartOperator(bpy.types.Operator):
|
|||||||
stop_modal_executor = True
|
stop_modal_executor = True
|
||||||
presence.renderer.stop()
|
presence.renderer.stop()
|
||||||
|
|
||||||
|
if settings.update_method == 'DEPSGRAPH':
|
||||||
|
bpy.app.handlers.depsgraph_update_post.remove(depsgraph_evaluation)
|
||||||
|
|
||||||
bpy.ops.session.apply_armature_operator()
|
bpy.ops.session.apply_armature_operator()
|
||||||
|
|
||||||
self.report(
|
self.report(
|
||||||
@ -612,6 +625,39 @@ def update_client_frame(scene):
|
|||||||
'frame_current': scene.frame_current
|
'frame_current': scene.frame_current
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@persistent
|
||||||
|
def depsgraph_evaluation(scene):
|
||||||
|
if client and client.state['STATE'] == STATE_ACTIVE:
|
||||||
|
context = bpy.context
|
||||||
|
blender_depsgraph = bpy.context.view_layer.depsgraph
|
||||||
|
dependency_updates = [u for u in blender_depsgraph.updates]
|
||||||
|
session_infos = utils.get_preferences()
|
||||||
|
|
||||||
|
# NOTE: maybe we don't need to check each update but only the first
|
||||||
|
|
||||||
|
for update in reversed(dependency_updates):
|
||||||
|
# Is the object tracked ?
|
||||||
|
if update.id.uuid:
|
||||||
|
# Retrieve local version
|
||||||
|
node = client.get(update.id.uuid)
|
||||||
|
|
||||||
|
# Check our right on this update:
|
||||||
|
# - if its ours or ( under common and diff), launch the
|
||||||
|
# update process
|
||||||
|
# - if its to someone else, ignore the update (go deeper ?)
|
||||||
|
if node and node.owner in [client.id, RP_COMMON] and node.state == UP:
|
||||||
|
# Avoid slow geometry update
|
||||||
|
if 'EDIT' in context.mode:
|
||||||
|
break
|
||||||
|
|
||||||
|
client.stash(node.uuid)
|
||||||
|
else:
|
||||||
|
# Distant update
|
||||||
|
continue
|
||||||
|
# else:
|
||||||
|
# # New items !
|
||||||
|
# logger.error("UPDATE: ADD")
|
||||||
|
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
from bpy.utils import register_class
|
from bpy.utils import register_class
|
||||||
@ -624,6 +670,8 @@ def register():
|
|||||||
bpy.app.handlers.load_pre.append(load_pre_handler)
|
bpy.app.handlers.load_pre.append(load_pre_handler)
|
||||||
bpy.app.handlers.frame_change_pre.append(update_client_frame)
|
bpy.app.handlers.frame_change_pre.append(update_client_frame)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
global client
|
global client
|
||||||
@ -641,7 +689,4 @@ def unregister():
|
|||||||
|
|
||||||
bpy.app.handlers.load_pre.remove(load_pre_handler)
|
bpy.app.handlers.load_pre.remove(load_pre_handler)
|
||||||
bpy.app.handlers.frame_change_pre.remove(update_client_frame)
|
bpy.app.handlers.frame_change_pre.remove(update_client_frame)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
register()
|
|
@ -129,6 +129,19 @@ class SessionPrefs(bpy.types.AddonPreferences):
|
|||||||
description='connection timeout before disconnection',
|
description='connection timeout before disconnection',
|
||||||
default=1000
|
default=1000
|
||||||
)
|
)
|
||||||
|
update_method: bpy.props.EnumProperty(
|
||||||
|
name='update method',
|
||||||
|
description='replication update method',
|
||||||
|
items=[
|
||||||
|
('DEFAULT', "Default", "Default: Use threads to monitor databloc changes"),
|
||||||
|
('DEPSGRAPH', "Depsgraph", "Experimental: Use the blender dependency graph to trigger updates"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
depsgraph_update_rate: bpy.props.IntProperty(
|
||||||
|
name='depsgraph update rate',
|
||||||
|
description='Dependency graph uppdate rate (milliseconds)',
|
||||||
|
default=100
|
||||||
|
)
|
||||||
# for UI
|
# for UI
|
||||||
category: bpy.props.EnumProperty(
|
category: bpy.props.EnumProperty(
|
||||||
name="Category",
|
name="Category",
|
||||||
@ -250,10 +263,13 @@ class SessionPrefs(bpy.types.AddonPreferences):
|
|||||||
box.row().prop(self, "ip", text="Address")
|
box.row().prop(self, "ip", text="Address")
|
||||||
row = box.row()
|
row = box.row()
|
||||||
row.label(text="Port:")
|
row.label(text="Port:")
|
||||||
row.prop(self, "port", text="Address")
|
row.prop(self, "port", text="")
|
||||||
row = box.row()
|
row = box.row()
|
||||||
row.label(text="Init the session from:")
|
row.label(text="Init the session from:")
|
||||||
row.prop(self, "init_method", text="")
|
row.prop(self, "init_method", text="")
|
||||||
|
row = box.row()
|
||||||
|
row.label(text="Update method:")
|
||||||
|
row.prop(self, "update_method", text="")
|
||||||
|
|
||||||
table = box.box()
|
table = box.box()
|
||||||
table.row().prop(
|
table.row().prop(
|
||||||
|
@ -303,24 +303,32 @@ class SESSION_PT_advanced_settings(bpy.types.Panel):
|
|||||||
replication_section_row.prop(settings.sync_flags, "sync_render_settings")
|
replication_section_row.prop(settings.sync_flags, "sync_render_settings")
|
||||||
|
|
||||||
replication_section_row = replication_section.row()
|
replication_section_row = replication_section.row()
|
||||||
replication_section_row.label(text="Per data type timers:")
|
replication_section_row.label(text="Update method:")
|
||||||
|
replication_section_row.prop(settings, "update_method", text="")
|
||||||
replication_section_row = replication_section.row()
|
replication_section_row = replication_section.row()
|
||||||
# Replication frequencies
|
replication_timers = replication_section_row.box()
|
||||||
flow = replication_section_row .grid_flow(
|
replication_timers.label(text="Per data type timers", icon='TIME')
|
||||||
row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
|
if settings.update_method == "DEFAULT":
|
||||||
line = flow.row(align=True)
|
replication_timers = replication_timers.row()
|
||||||
line.label(text=" ")
|
# Replication frequencies
|
||||||
line.separator()
|
flow = replication_timers.grid_flow(
|
||||||
line.label(text="refresh (sec)")
|
row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
|
||||||
line.label(text="apply (sec)")
|
|
||||||
|
|
||||||
for item in settings.supported_datablocks:
|
|
||||||
line = flow.row(align=True)
|
line = flow.row(align=True)
|
||||||
line.prop(item, "auto_push", text="", icon=item.icon)
|
line.label(text=" ")
|
||||||
line.separator()
|
line.separator()
|
||||||
line.prop(item, "bl_delay_refresh", text="")
|
line.label(text="refresh (sec)")
|
||||||
line.prop(item, "bl_delay_apply", text="")
|
line.label(text="apply (sec)")
|
||||||
|
|
||||||
|
for item in settings.supported_datablocks:
|
||||||
|
line = flow.row(align=True)
|
||||||
|
line.prop(item, "auto_push", text="", icon=item.icon)
|
||||||
|
line.separator()
|
||||||
|
line.prop(item, "bl_delay_refresh", text="")
|
||||||
|
line.prop(item, "bl_delay_apply", text="")
|
||||||
|
else:
|
||||||
|
replication_timers = replication_timers.row()
|
||||||
|
replication_timers.label(text="Update rate (ms):")
|
||||||
|
replication_timers.prop(settings, "depsgraph_update_rate", text="")
|
||||||
|
|
||||||
class SESSION_PT_user(bpy.types.Panel):
|
class SESSION_PT_user(bpy.types.Panel):
|
||||||
bl_idname = "MULTIUSER_USER_PT_panel"
|
bl_idname = "MULTIUSER_USER_PT_panel"
|
||||||
|
Loading…
Reference in New Issue
Block a user