Merge branch 'feature/event_driven_updates' into develop

This commit is contained in:
Swann 2020-09-03 15:59:19 +02:00
commit b3230177d8
No known key found for this signature in database
GPG Key ID: E1D3641A7C43AACB
7 changed files with 115 additions and 38 deletions

View File

@ -8,5 +8,8 @@ build:
name: multi_user
paths:
- multi_user
only:
refs:
- master
- develop

View File

@ -11,4 +11,7 @@ test:
- python -m pip install blender-addon-tester
- python scripts/test_addon.py
only:
refs:
- master
- develop

View File

@ -44,7 +44,7 @@ from . import environment, utils
DEPENDENCIES = {
("replication", '0.0.21a4'),
("replication", '0.0.21a5'),
}

View File

@ -85,20 +85,22 @@ class ApplyTimer(Timer):
super().__init__(timout)
def execute(self):
client = operators.client
if client and client.state['STATE'] == STATE_ACTIVE:
nodes = client.list(filter=self._type)
client = operators.client
if client and client.state['STATE'] == STATE_ACTIVE:
if self._type:
nodes = client.list(filter=self._type)
else:
nodes = client.list()
for node in nodes:
node_ref = client.get(uuid=node)
if node_ref.state == FETCHED:
try:
client.apply(node)
client.apply(node, force=True)
except Exception as e:
logging.error(f"Fail to apply {node_ref.uuid}: {e}")
class DynamicRightSelectTimer(Timer):
def __init__(self, timout=.1):
super().__init__(timout)
@ -239,7 +241,7 @@ class DrawClient(Draw):
class ClientUpdate(Timer):
def __init__(self, timout=.032):
def __init__(self, timout=.1):
super().__init__(timout)
self.handle_quit = False
self.users_metadata = {}

View File

@ -35,7 +35,7 @@ from bpy.app.handlers import persistent
from . import bl_types, delayable, environment, presence, ui, utils
from replication.constants import (FETCHED, STATE_ACTIVE,
STATE_INITIAL,
STATE_SYNCING)
STATE_SYNCING, RP_COMMON, UP)
from replication.data import ReplicatedDataFactory
from replication.exception import NonAuthorizedOperationError
from replication.interface import Session
@ -67,7 +67,7 @@ class SessionStartOperator(bpy.types.Operator):
runtime_settings = context.window_manager.session
users = bpy.data.window_managers['WinMan'].online_users
admin_pass = runtime_settings.password
use_extern_update = settings.update_method == 'DEPSGRAPH'
users.clear()
delayables.clear()
@ -88,18 +88,23 @@ class SessionStartOperator(bpy.types.Operator):
bpy_factory.register_type(
type_module_class.bl_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)
if type_local_config.bl_delay_apply > 0:
delayables.append(
delayable.ApplyTimer(
timout=type_local_config.bl_delay_apply,
target_type=type_module_class))
if settings.update_method == 'DEFAULT':
if type_local_config.bl_delay_apply > 0:
delayables.append(
delayable.ApplyTimer(
timout=type_local_config.bl_delay_apply,
target_type=type_module_class))
client = Session(
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
if self.host:
@ -145,7 +150,6 @@ class SessionStartOperator(bpy.types.Operator):
logging.error(str(e))
# Background client updates service
#TODO: Refactoring
delayables.append(delayable.ClientUpdate())
delayables.append(delayable.DrawClient())
delayables.append(delayable.DynamicRightSelectTimer())
@ -161,6 +165,8 @@ class SessionStartOperator(bpy.types.Operator):
@client.register('on_connection')
def initialize_session():
settings = utils.get_preferences()
for node in client._graph.list_ordered():
node_ref = client.get(node)
if node_ref.state == FETCHED:
@ -180,9 +186,13 @@ class SessionStartOperator(bpy.types.Operator):
for d in delayables:
d.register()
if settings.update_method == 'DEPSGRAPH':
bpy.app.handlers.depsgraph_update_post.append(depsgraph_evaluation)
@client.register('on_exit')
def desinitialize_session():
global delayables, stop_modal_executor
settings = utils.get_preferences()
for d in delayables:
try:
@ -193,6 +203,9 @@ class SessionStartOperator(bpy.types.Operator):
stop_modal_executor = True
presence.renderer.stop()
if settings.update_method == 'DEPSGRAPH':
bpy.app.handlers.depsgraph_update_post.remove(depsgraph_evaluation)
bpy.ops.session.apply_armature_operator()
self.report(
@ -612,6 +625,39 @@ def update_client_frame(scene):
'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():
from bpy.utils import register_class
@ -625,6 +671,8 @@ def register():
bpy.app.handlers.frame_change_pre.append(update_client_frame)
def unregister():
global client
@ -642,6 +690,3 @@ def unregister():
bpy.app.handlers.load_pre.remove(load_pre_handler)
bpy.app.handlers.frame_change_pre.remove(update_client_frame)
if __name__ == "__main__":
register()

View File

@ -129,6 +129,19 @@ class SessionPrefs(bpy.types.AddonPreferences):
description='connection timeout before disconnection',
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
category: bpy.props.EnumProperty(
name="Category",
@ -250,10 +263,13 @@ class SessionPrefs(bpy.types.AddonPreferences):
box.row().prop(self, "ip", text="Address")
row = box.row()
row.label(text="Port:")
row.prop(self, "port", text="Address")
row.prop(self, "port", text="")
row = box.row()
row.label(text="Init the session from:")
row.prop(self, "init_method", text="")
row = box.row()
row.label(text="Update method:")
row.prop(self, "update_method", text="")
table = box.box()
table.row().prop(

View File

@ -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 = 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 frequencies
flow = replication_section_row .grid_flow(
row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
line = flow.row(align=True)
line.label(text=" ")
line.separator()
line.label(text="refresh (sec)")
line.label(text="apply (sec)")
for item in settings.supported_datablocks:
replication_timers = replication_section_row.box()
replication_timers.label(text="Per data type timers", icon='TIME')
if settings.update_method == "DEFAULT":
replication_timers = replication_timers.row()
# Replication frequencies
flow = replication_timers.grid_flow(
row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
line = flow.row(align=True)
line.prop(item, "auto_push", text="", icon=item.icon)
line.label(text=" ")
line.separator()
line.prop(item, "bl_delay_refresh", text="")
line.prop(item, "bl_delay_apply", text="")
line.label(text="refresh (sec)")
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):
bl_idname = "MULTIUSER_USER_PT_panel"