immersive-home/app/addons/rdot/graph.gd

226 lines
6.4 KiB
GDScript3
Raw Normal View History

2024-04-09 16:30:23 +03:00
extends RefCounted
class_name RdotGraph
static var instance: RdotGraph = null
static func getInstance() -> RdotGraph:
2024-04-18 13:35:57 +03:00
if instance == null:
instance = RdotGraph.new()
return instance
2024-04-09 16:30:23 +03:00
var activeConsumer: RdotNode = null
var inNotificationPhase := false
var epoch := 1
var postSignalSetFn := Callable()
var watcherPending := false
var watcher = R.Watcher.new(func(_arg):
2024-04-18 13:35:57 +03:00
if watcherPending:
return
2024-04-09 16:30:23 +03:00
2024-04-18 13:35:57 +03:00
watcherPending=true
var endOfFrame=func():
2024-04-09 16:30:23 +03:00
2024-04-18 13:35:57 +03:00
watcherPending=false
for s in watcher.getPending():
s.do_get()
2024-04-09 16:30:23 +03:00
2024-04-18 13:35:57 +03:00
watcher.watch()
2024-04-09 16:30:23 +03:00
2024-04-18 13:35:57 +03:00
endOfFrame.call_deferred()
2024-04-09 16:30:23 +03:00
)
func setActiveConsumer(consumer: RdotNode) -> RdotNode:
2024-04-18 13:35:57 +03:00
var prev = activeConsumer
activeConsumer = consumer
return prev
2024-04-09 16:30:23 +03:00
func getActiveConsumer() -> RdotNode:
2024-04-18 13:35:57 +03:00
return activeConsumer
2024-04-09 16:30:23 +03:00
func isInNotificationPhase() -> bool:
2024-04-18 13:35:57 +03:00
return inNotificationPhase
2024-04-09 16:30:23 +03:00
func producerAccessed(node: RdotNode):
2024-04-18 13:35:57 +03:00
assert(inNotificationPhase == false, "Signal read during notification phase")
2024-04-09 16:30:23 +03:00
2024-04-18 13:35:57 +03:00
if activeConsumer == null:
return
2024-04-09 16:30:23 +03:00
2024-04-18 13:35:57 +03:00
if activeConsumer.consumerOnSignalRead.is_null() == false:
activeConsumer.consumerOnSignalRead.call(node)
2024-04-09 16:30:23 +03:00
2024-04-18 13:35:57 +03:00
var idx = activeConsumer.nextProducerIndex;
activeConsumer.nextProducerIndex += 1
2024-04-09 16:30:23 +03:00
2024-04-18 13:35:57 +03:00
assertConsumerNode(activeConsumer)
2024-04-09 16:30:23 +03:00
2024-04-18 13:35:57 +03:00
if idx < activeConsumer.producerNode.size()&&activeConsumer.producerNode[idx] != node:
if consumerIsLive(activeConsumer):
var staleProducer = activeConsumer.producerNode[idx]
producerRemoveLiveConsumerAtIndex(staleProducer, activeConsumer.producerIndexOfThis[idx])
2024-04-09 16:30:23 +03:00
2024-04-18 13:35:57 +03:00
if RdotArray.do_get(activeConsumer.producerNode, idx) != node:
RdotArray.do_set(activeConsumer.producerNode, idx, node)
RdotArray.do_set(activeConsumer.producerIndexOfThis, idx, producerAddLiveConsumer(node, activeConsumer, idx) if consumerIsLive(activeConsumer) else 0)
2024-04-09 16:30:23 +03:00
2024-04-18 13:35:57 +03:00
RdotArray.do_set(activeConsumer.producerLastReadVersion, idx, node.version)
2024-04-09 16:30:23 +03:00
func producerIncrementEpoch():
2024-04-18 13:35:57 +03:00
epoch += 1
2024-04-09 16:30:23 +03:00
func producerUpdateValueVersion(node: RdotNode):
2024-04-18 13:35:57 +03:00
if consumerIsLive(node)&&!node.dirty:
return
2024-04-09 16:30:23 +03:00
2024-04-18 13:35:57 +03:00
if !node.dirty&&node.lastCleanEpoch == epoch:
return
2024-04-09 16:30:23 +03:00
2024-04-18 13:35:57 +03:00
if !node.producerMustRecompute(node)&&!consumerPollProducersForChange(node):
node.dirty = false;
node.lastCleanEpoch = epoch
return
2024-04-09 16:30:23 +03:00
2024-04-18 13:35:57 +03:00
if node.producerRecomputeValue.is_null() == false:
node.producerRecomputeValue.call(node)
2024-04-09 16:30:23 +03:00
2024-04-18 13:35:57 +03:00
node.dirty = false
node.lastCleanEpoch = epoch
2024-04-09 16:30:23 +03:00
func producerNotifyConsumers(node: RdotNode):
2024-04-18 13:35:57 +03:00
if node.liveConsumerNode == null:
return
2024-04-09 16:30:23 +03:00
2024-04-18 13:35:57 +03:00
var prev = inNotificationPhase
inNotificationPhase = true
2024-04-09 16:30:23 +03:00
2024-04-18 13:35:57 +03:00
for consumer in node.liveConsumerNode:
if !consumer.dirty:
consumerMarkDirty(consumer)
2024-04-09 16:30:23 +03:00
2024-04-18 13:35:57 +03:00
inNotificationPhase = prev
2024-04-09 16:30:23 +03:00
func producerUpdatesAllowed() -> bool:
2024-04-18 13:35:57 +03:00
return activeConsumer == null||activeConsumer.consumerAllowSignalWrites != false
2024-04-09 16:30:23 +03:00
func consumerMarkDirty(node: RdotNode):
2024-04-18 13:35:57 +03:00
node.dirty = true
producerNotifyConsumers(node)
2024-04-09 16:30:23 +03:00
2024-04-18 13:35:57 +03:00
if node.consumerMarkedDirty.is_null() == false:
node.consumerMarkedDirty.call(node)
2024-04-09 16:30:23 +03:00
func consumerBeforeComputation(node: RdotNode) -> RdotNode:
2024-04-18 13:35:57 +03:00
if node:
node.nextProducerIndex = 0
2024-04-09 16:30:23 +03:00
2024-04-18 13:35:57 +03:00
return setActiveConsumer(node)
2024-04-09 16:30:23 +03:00
func consumerAfterComputation(node: RdotNode, prevConsumer: RdotNode):
2024-04-18 13:35:57 +03:00
setActiveConsumer(prevConsumer)
2024-04-09 16:30:23 +03:00
2024-04-18 13:35:57 +03:00
if node == null||node.producerNode == null||node.producerIndexOfThis == null||node.producerLastReadVersion == null:
return
2024-04-09 16:30:23 +03:00
2024-04-18 13:35:57 +03:00
if consumerIsLive(node):
for i in range(node.nextProducerIndex, node.producerNode.size()):
producerRemoveLiveConsumerAtIndex(node.producerNode[i], node.producerIndexOfThis[i])
2024-04-09 16:30:23 +03:00
2024-04-18 13:35:57 +03:00
while node.producerNode.size() > node.nextProducerIndex:
node.producerNode.pop_back()
node.producerLastReadVersion.pop_back()
node.producerIndexOfThis.pop_back()
2024-04-09 16:30:23 +03:00
func consumerPollProducersForChange(node: RdotNode) -> bool:
2024-04-18 13:35:57 +03:00
assertConsumerNode(node)
2024-04-09 16:30:23 +03:00
2024-04-18 13:35:57 +03:00
for i in range(node.producerNode.size()):
var producer = node.producerNode[i]
var seenVersion = node.producerLastReadVersion[i]
2024-04-09 16:30:23 +03:00
2024-04-18 13:35:57 +03:00
if seenVersion != producer.version:
return true
2024-04-09 16:30:23 +03:00
2024-04-18 13:35:57 +03:00
producerUpdateValueVersion(producer)
2024-04-09 16:30:23 +03:00
2024-04-18 13:35:57 +03:00
if seenVersion != producer.version:
return true
2024-04-09 16:30:23 +03:00
2024-04-18 13:35:57 +03:00
return false
2024-04-09 16:30:23 +03:00
func consumerDestroy(node: RdotNode):
2024-04-18 13:35:57 +03:00
assertConsumerNode(node)
2024-04-09 16:30:23 +03:00
2024-04-18 13:35:57 +03:00
if consumerIsLive(node):
for i in range(node.producerNode.size()):
producerRemoveLiveConsumerAtIndex(node.producerNode[i], node.producerIndexOfThis[i])
2024-04-09 16:30:23 +03:00
2024-04-18 13:35:57 +03:00
node.producerNode.clear()
node.producerLastReadVersion.clear()
node.producerIndexOfThis.clear()
2024-04-09 16:30:23 +03:00
2024-04-18 13:35:57 +03:00
if node.liveConsumerNode:
node.liveConsumerNode.clear()
node.liveConsumerIndexOfThis.clear()
2024-04-09 16:30:23 +03:00
static func producerAddLiveConsumer(node: RdotNode, consumer: RdotNode, indexOfThis: int) -> int:
2024-04-18 13:35:57 +03:00
assertProducerNode(node)
assertConsumerNode(node)
2024-04-09 16:30:23 +03:00
2024-04-18 13:35:57 +03:00
if node.liveConsumerNode.size() == 0:
if node.watched.is_null() == false:
node.watched.call(node.wrapper)
2024-04-09 16:30:23 +03:00
2024-04-18 13:35:57 +03:00
for i in range(node.producerNode.size()):
node.producerIndexOfThis[i] = producerAddLiveConsumer(node.producerNode[i], node, i)
2024-04-09 16:30:23 +03:00
2024-04-18 13:35:57 +03:00
node.liveConsumerIndexOfThis.push_back(indexOfThis)
node.liveConsumerNode.push_back(consumer)
2024-04-09 16:30:23 +03:00
2024-04-18 13:35:57 +03:00
return node.liveConsumerNode.size() - 1
2024-04-09 16:30:23 +03:00
static func producerRemoveLiveConsumerAtIndex(node: RdotNode, idx: int):
2024-04-18 13:35:57 +03:00
assertProducerNode(node)
assertConsumerNode(node)
2024-04-09 16:30:23 +03:00
2024-04-18 13:35:57 +03:00
assert(idx < node.liveConsumerNode.size(), "active consumer index %s is out of bounds of %s consumers)" % [idx, node.liveConsumerNode.size()])
2024-04-09 16:30:23 +03:00
2024-04-18 13:35:57 +03:00
if node.liveConsumerNode.size() == 1:
if node.unwatched.is_null() == false:
node.unwatched.call(node.wrapper)
2024-04-09 16:30:23 +03:00
2024-04-18 13:35:57 +03:00
for i in range(node.producerNode.size()):
producerRemoveLiveConsumerAtIndex(node.producerNode[i], node.producerIndexOfThis[i])
2024-04-09 16:30:23 +03:00
2024-04-18 13:35:57 +03:00
var lastIdx = node.liveConsumerNode.size() - 1
node.liveConsumerNode[idx] = node.liveConsumerNode[lastIdx]
node.liveConsumerIndexOfThis[idx] = node.liveConsumerIndexOfThis[lastIdx]
2024-04-09 16:30:23 +03:00
2024-04-18 13:35:57 +03:00
node.liveConsumerNode.pop_back()
node.liveConsumerIndexOfThis.pop_back()
2024-04-09 16:30:23 +03:00
2024-04-18 13:35:57 +03:00
if idx < node.liveConsumerNode.size():
var idxProducer = node.liveConsumerIndexOfThis[idx]
var consumer = node.liveConsumerNode[idx]
assertConsumerNode(consumer)
consumer.producerIndexOfThis[idxProducer] = idx
2024-04-09 16:30:23 +03:00
static func consumerIsLive(node: RdotNode) -> bool:
2024-04-18 13:35:57 +03:00
return node.consumerIsAlwaysLive||(node.liveConsumerNode != null&&node.liveConsumerNode.size() > 0)
2024-04-09 16:30:23 +03:00
static func assertConsumerNode(node: RdotNode):
2024-04-18 13:35:57 +03:00
if node.producerNode == null:
node.producerNode = []
if node.producerIndexOfThis == null:
node.producerIndexOfThis = []
if node.producerLastReadVersion == null:
node.producerLastReadVersion = []
2024-04-09 16:30:23 +03:00
static func assertProducerNode(node: RdotNode):
2024-04-18 13:35:57 +03:00
if node.liveConsumerNode == null:
node.liveConsumerNode = []
if node.liveConsumerIndexOfThis == null:
node.liveConsumerIndexOfThis = []