1
0
mirror of synced 2024-11-30 16:54:30 +01:00

Add debugging components options to AFP parser and renderer.

This commit is contained in:
Jennifer Taylor 2021-09-20 01:16:40 +00:00
parent 22e5885004
commit a6e3ec524a
3 changed files with 210 additions and 202 deletions

View File

@ -601,14 +601,14 @@ class AFPRenderer(VerboseOutput):
} }
registers: List[Any] = [UNDEFINED] * 256 registers: List[Any] = [UNDEFINED] * 256
self.vprint(f"{prefix}Bytecode engine starting.") self.vprint(f"{prefix}Bytecode engine starting.", component="bytecode")
while location < len(bytecode.actions): while location < len(bytecode.actions):
action = bytecode.actions[location] action = bytecode.actions[location]
if action.opcode == AP2Action.END: if action.opcode == AP2Action.END:
# End the execution. # End the execution.
self.vprint(f"{prefix} Ending bytecode execution.") self.vprint(f"{prefix} Ending bytecode execution.", component="bytecode")
break break
elif action.opcode == AP2Action.GET_VARIABLE: elif action.opcode == AP2Action.GET_VARIABLE:
varname = stack.pop() varname = stack.pop()
@ -627,7 +627,7 @@ class AFPRenderer(VerboseOutput):
if not hasattr(obj, attribute): if not hasattr(obj, attribute):
print(f"WARNING: Tried to set attribute {attribute} on {obj} but that attribute doesn't exist!") print(f"WARNING: Tried to set attribute {attribute} on {obj} but that attribute doesn't exist!")
else: else:
self.vprint(f"{prefix} Setting attribute {attribute} on {obj} to {set_value}") self.vprint(f"{prefix} Setting attribute {attribute} on {obj} to {set_value}", component="bytecode")
setattr(obj, attribute, set_value) setattr(obj, attribute, set_value)
elif action.opcode == AP2Action.CALL_METHOD: elif action.opcode == AP2Action.CALL_METHOD:
# Grab the method name. # Grab the method name.
@ -646,7 +646,7 @@ class AFPRenderer(VerboseOutput):
# Look up the python function we're calling. # Look up the python function we're calling.
try: try:
self.vprint(f"{prefix} Calling method {methname}({', '.join(repr(s) for s in params)}) on {obj}") self.vprint(f"{prefix} Calling method {methname}({', '.join(repr(s) for s in params)}) on {obj}", component="bytecode")
meth = getattr(obj, methname) meth = getattr(obj, methname)
# Call it, set the return on the stack. # Call it, set the return on the stack.
@ -669,7 +669,7 @@ class AFPRenderer(VerboseOutput):
# Look up the python function we're calling. # Look up the python function we're calling.
try: try:
self.vprint(f"{prefix} Calling global function {funcname}({', '.join(repr(s) for s in params)})") self.vprint(f"{prefix} Calling global function {funcname}({', '.join(repr(s) for s in params)})", component="bytecode")
func = getattr(globalobj, funcname) func = getattr(globalobj, funcname)
# Call it, set the return on the stack. # Call it, set the return on the stack.
@ -720,13 +720,13 @@ class AFPRenderer(VerboseOutput):
# Next opcode! # Next opcode!
location += 1 location += 1
self.vprint(f"{prefix}Bytecode engine finished.") self.vprint(f"{prefix}Bytecode engine finished.", component="bytecode")
def __place(self, tag: Tag, operating_clip: PlacedClip, prefix: str = "") -> Tuple[Optional[PlacedClip], bool]: def __place(self, tag: Tag, operating_clip: PlacedClip, prefix: str = "") -> Tuple[Optional[PlacedClip], bool]:
# "Place" a tag on the screen. Most of the time, this means performing the action of the tag, # "Place" a tag on the screen. Most of the time, this means performing the action of the tag,
# such as defining a shape (registering it with our shape list) or adding/removing an object. # such as defining a shape (registering it with our shape list) or adding/removing an object.
if isinstance(tag, AP2ShapeTag): if isinstance(tag, AP2ShapeTag):
self.vprint(f"{prefix} Loading {tag.reference} into object slot {tag.id}") self.vprint(f"{prefix} Loading {tag.reference} into object slot {tag.id}", component="tags")
if tag.reference not in self.shapes: if tag.reference not in self.shapes:
raise Exception(f"Cannot find shape reference {tag.reference}!") raise Exception(f"Cannot find shape reference {tag.reference}!")
@ -743,7 +743,7 @@ class AFPRenderer(VerboseOutput):
return None, False return None, False
elif isinstance(tag, AP2ImageTag): elif isinstance(tag, AP2ImageTag):
self.vprint(f"{prefix} Loading {tag.reference} into object slot {tag.id}") self.vprint(f"{prefix} Loading {tag.reference} into object slot {tag.id}", component="tags")
if tag.reference not in self.textures: if tag.reference not in self.textures:
raise Exception(f"Cannot find texture reference {tag.reference}!") raise Exception(f"Cannot find texture reference {tag.reference}!")
@ -757,7 +757,7 @@ class AFPRenderer(VerboseOutput):
return None, False return None, False
elif isinstance(tag, AP2DefineSpriteTag): elif isinstance(tag, AP2DefineSpriteTag):
self.vprint(f"{prefix} Loading Sprite into object slot {tag.id}") self.vprint(f"{prefix} Loading Sprite into object slot {tag.id}", component="tags")
# Register a new clip that we might reference to execute. # Register a new clip that we might reference to execute.
self.__registered_objects[tag.id] = RegisteredClip(tag.id, tag.frames, tag.tags, tag.labels) self.__registered_objects[tag.id] = RegisteredClip(tag.id, tag.frames, tag.tags, tag.labels)
@ -786,7 +786,7 @@ class AFPRenderer(VerboseOutput):
if tag.source_tag_id is not None and tag.source_tag_id != obj.source.tag_id: if tag.source_tag_id is not None and tag.source_tag_id != obj.source.tag_id:
# This completely updates the pointed-at object. # This completely updates the pointed-at object.
self.vprint(f"{prefix} Replacing Object source {obj.source.tag_id} with {tag.source_tag_id} on object with Object ID {tag.object_id} onto Depth {tag.depth}") self.vprint(f"{prefix} Replacing Object source {obj.source.tag_id} with {tag.source_tag_id} on object with Object ID {tag.object_id} onto Depth {tag.depth}", component="tags")
newobj = self.__registered_objects[tag.source_tag_id] newobj = self.__registered_objects[tag.source_tag_id]
if isinstance(newobj, RegisteredShape): if isinstance(newobj, RegisteredShape):
@ -858,7 +858,7 @@ class AFPRenderer(VerboseOutput):
raise Exception(f"Unrecognized object with Tag ID {tag.source_tag_id}!") raise Exception(f"Unrecognized object with Tag ID {tag.source_tag_id}!")
else: else:
# As far as I can tell, pretty much only color and matrix stuff can be updated. # As far as I can tell, pretty much only color and matrix stuff can be updated.
self.vprint(f"{prefix} Updating Object ID {tag.object_id} on Depth {tag.depth}") self.vprint(f"{prefix} Updating Object ID {tag.object_id} on Depth {tag.depth}", component="tags")
obj.mult_color = new_mult_color obj.mult_color = new_mult_color
obj.add_color = new_add_color obj.add_color = new_add_color
obj.transform = new_transform obj.transform = new_transform
@ -875,7 +875,7 @@ class AFPRenderer(VerboseOutput):
raise Exception("Cannot place a tag with no source ID and no update flags!") raise Exception("Cannot place a tag with no source ID and no update flags!")
if tag.source_tag_id in self.__registered_objects: if tag.source_tag_id in self.__registered_objects:
self.vprint(f"{prefix} Placing Object {tag.source_tag_id} with Object ID {tag.object_id} onto Depth {tag.depth}") self.vprint(f"{prefix} Placing Object {tag.source_tag_id} with Object ID {tag.object_id} onto Depth {tag.depth}", component="tags")
newobj = self.__registered_objects[tag.source_tag_id] newobj = self.__registered_objects[tag.source_tag_id]
if isinstance(newobj, RegisteredShape): if isinstance(newobj, RegisteredShape):
@ -962,7 +962,7 @@ class AFPRenderer(VerboseOutput):
raise Exception(f"Cannot find a shape or sprite with Tag ID {tag.source_tag_id}!") raise Exception(f"Cannot find a shape or sprite with Tag ID {tag.source_tag_id}!")
elif isinstance(tag, AP2RemoveObjectTag): elif isinstance(tag, AP2RemoveObjectTag):
self.vprint(f"{prefix} Removing Object ID {tag.object_id} from Depth {tag.depth}") self.vprint(f"{prefix} Removing Object ID {tag.object_id} from Depth {tag.depth}", component="tags")
if tag.object_id != 0: if tag.object_id != 0:
# Remove the identified object by object ID and depth. # Remove the identified object by object ID and depth.
@ -1000,7 +1000,7 @@ class AFPRenderer(VerboseOutput):
return None, True return None, True
elif isinstance(tag, AP2DoActionTag): elif isinstance(tag, AP2DoActionTag):
self.vprint(f"{prefix} Execution action tag.") self.vprint(f"{prefix} Execution action tag.", component="tags")
self.__execute_bytecode(tag.bytecode, operating_clip, prefix=prefix + " ") self.__execute_bytecode(tag.bytecode, operating_clip, prefix=prefix + " ")
# Didn't place a new clip. # Didn't place a new clip.
@ -1029,7 +1029,7 @@ class AFPRenderer(VerboseOutput):
return None, False return None, False
elif isinstance(tag, AP2PlaceCameraTag): elif isinstance(tag, AP2PlaceCameraTag):
self.vprint(f"{prefix} Place camera tag.") self.vprint(f"{prefix} Place camera tag.", component="tags")
self.__camera = PlacedCamera( self.__camera = PlacedCamera(
tag.center, tag.center,
tag.focal_length, tag.focal_length,
@ -1131,10 +1131,10 @@ class AFPRenderer(VerboseOutput):
prefix: str="", prefix: str="",
) -> Image.Image: ) -> Image.Image:
if not renderable.visible: if not renderable.visible:
self.vprint(f"{prefix} Ignoring invisible placed object ID {renderable.object_id} from sprite {renderable.source.tag_id} on Depth {renderable.depth}") self.vprint(f"{prefix} Ignoring invisible placed object ID {renderable.object_id} from sprite {renderable.source.tag_id} on Depth {renderable.depth}", component="render")
return img return img
self.vprint(f"{prefix} Rendering placed object ID {renderable.object_id} from sprite {renderable.source.tag_id} onto Depth {renderable.depth}") self.vprint(f"{prefix} Rendering placed object ID {renderable.object_id} from sprite {renderable.source.tag_id} onto Depth {renderable.depth}", component="render")
# Compute the affine transformation matrix for this object. # Compute the affine transformation matrix for this object.
transform = renderable.transform.multiply(parent_transform).translate(Point.identity().subtract(renderable.rotation_origin)) transform = renderable.transform.multiply(parent_transform).translate(Point.identity().subtract(renderable.rotation_origin))
@ -1360,7 +1360,7 @@ class AFPRenderer(VerboseOutput):
return False return False
def __process_tags(self, clip: PlacedClip, only_dirty: bool, prefix: str = " ") -> bool: def __process_tags(self, clip: PlacedClip, only_dirty: bool, prefix: str = " ") -> bool:
self.vprint(f"{prefix}Handling {'dirty updates on ' if only_dirty else ''}placed clip {clip.object_id} at depth {clip.depth}") self.vprint(f"{prefix}Handling {'dirty updates on ' if only_dirty else ''}placed clip {clip.object_id} at depth {clip.depth}", component="tags")
# Track whether anything in ourselves or our children changes during this processing. # Track whether anything in ourselves or our children changes during this processing.
changed = False changed = False
@ -1385,7 +1385,7 @@ class AFPRenderer(VerboseOutput):
print("WARNING: Root clip was rewound, its possible this animation plays forever!") print("WARNING: Root clip was rewound, its possible this animation plays forever!")
clip.rewind() clip.rewind()
self.vprint(f"{prefix} Processing frame {clip.frame} on our way to frame {clip.requested_frame}") self.vprint(f"{prefix} Processing frame {clip.frame} on our way to frame {clip.requested_frame}", component="tags")
# Clips that are part of our own placed objects which we should handle. # Clips that are part of our own placed objects which we should handle.
child_clips = [c for c in clip.placed_objects if isinstance(c, PlacedClip)] child_clips = [c for c in clip.placed_objects if isinstance(c, PlacedClip)]
@ -1399,7 +1399,7 @@ class AFPRenderer(VerboseOutput):
# See if we have any orphans that need to be placed before this frame will work. # See if we have any orphans that need to be placed before this frame will work.
for unplayed_tag in clip.unplayed_tags: for unplayed_tag in clip.unplayed_tags:
if unplayed_tag < frame.start_tag_offset: if unplayed_tag < frame.start_tag_offset:
self.vprint(f"{prefix} Including orphaned tag {unplayed_tag} in frame evaluation") self.vprint(f"{prefix} Including orphaned tag {unplayed_tag} in frame evaluation", component="tags")
played_tags.add(unplayed_tag) played_tags.add(unplayed_tag)
orphans.append(clip.source.tags[unplayed_tag]) orphans.append(clip.source.tags[unplayed_tag])
@ -1413,7 +1413,7 @@ class AFPRenderer(VerboseOutput):
tags = orphans + clip.source.tags[frame.start_tag_offset:(frame.start_tag_offset + frame.num_tags)] tags = orphans + clip.source.tags[frame.start_tag_offset:(frame.start_tag_offset + frame.num_tags)]
for tagno, tag in enumerate(tags): for tagno, tag in enumerate(tags):
# Perform the action of this tag. # Perform the action of this tag.
self.vprint(f"{prefix} Sprite Tag ID: {clip.source.tag_id}, Current Tag: {frame.start_tag_offset + tagno}, Num Tags: {frame.num_tags}") self.vprint(f"{prefix} Sprite Tag ID: {clip.source.tag_id}, Current Tag: {frame.start_tag_offset + tagno}, Num Tags: {frame.num_tags}", component="tags")
new_clip, clip_changed = self.__place(tag, clip, prefix=prefix) new_clip, clip_changed = self.__place(tag, clip, prefix=prefix)
changed = changed or clip_changed changed = changed or clip_changed
@ -1434,7 +1434,7 @@ class AFPRenderer(VerboseOutput):
clip.requested_frame = None clip.requested_frame = None
break break
self.vprint(f"{prefix}Finished handling {'dirty updates on ' if only_dirty else ''}placed clip {clip.object_id} at depth {clip.depth}") self.vprint(f"{prefix}Finished handling {'dirty updates on ' if only_dirty else ''}placed clip {clip.object_id} at depth {clip.depth}", component="tags")
# Return if anything was modified. # Return if anything was modified.
return changed return changed
@ -1635,7 +1635,7 @@ class AFPRenderer(VerboseOutput):
try: try:
while root_clip.playing and not root_clip.finished: while root_clip.playing and not root_clip.finished:
# Create a new image to render into. # Create a new image to render into.
self.vprint(f"Rendering frame {frameno + 1}/{len(root_clip.source.frames)}") self.vprint(f"Rendering frame {frameno + 1}/{len(root_clip.source.frames)}", component="core")
# Go through all registered clips, place all needed tags. # Go through all registered clips, place all needed tags.
changed = self.__process_tags(root_clip, False) changed = self.__process_tags(root_clip, False)
@ -1658,7 +1658,7 @@ class AFPRenderer(VerboseOutput):
# If we're only rendering some frames, don't bother to do the draw operations # If we're only rendering some frames, don't bother to do the draw operations
# if we aren't going to return the frame. # if we aren't going to return the frame.
if only_frames and (frameno + 1) not in only_frames: if only_frames and (frameno + 1) not in only_frames:
self.vprint(f"Skipped rendering frame {frameno + 1}/{len(root_clip.source.frames)}") self.vprint(f"Skipped rendering frame {frameno + 1}/{len(root_clip.source.frames)}", component="core")
last_rendered_frame = None last_rendered_frame = None
frameno += 1 frameno += 1
continue continue
@ -1680,11 +1680,11 @@ class AFPRenderer(VerboseOutput):
) )
else: else:
# Nothing changed, make a copy of the previous render. # Nothing changed, make a copy of the previous render.
self.vprint(" Using previous frame render") self.vprint(" Using previous frame render", component="core")
curimage = last_rendered_frame.copy() curimage = last_rendered_frame.copy()
# Return that frame, advance our bookkeeping. # Return that frame, advance our bookkeeping.
self.vprint(f"Finished rendering frame {frameno + 1}/{len(root_clip.source.frames)}") self.vprint(f"Finished rendering frame {frameno + 1}/{len(root_clip.source.frames)}", component="core")
last_rendered_frame = curimage last_rendered_frame = curimage
frameno += 1 frameno += 1
yield curimage yield curimage

File diff suppressed because it is too large Load Diff

View File

@ -50,6 +50,8 @@ class TrackedCoverageManager:
class TrackedCoverage: class TrackedCoverage:
def __init__(self) -> None: def __init__(self) -> None:
super().__init__()
self.coverage: List[bool] = [] self.coverage: List[bool] = []
self._tracking: bool = False self._tracking: bool = False
@ -138,12 +140,17 @@ class VerboseOutputManager:
class VerboseOutput: class VerboseOutput:
def __init__(self) -> None: def __init__(self, components: List[str] = []) -> None:
super().__init__()
self.verbose: bool = False self.verbose: bool = False
self.components: List[str] = components or []
def debugging(self, verbose: bool) -> VerboseOutputManager: def debugging(self, verbose: bool) -> VerboseOutputManager:
return VerboseOutputManager(self, verbose) return VerboseOutputManager(self, verbose)
def vprint(self, *args: Any, **kwargs: Any) -> None: def vprint(self, *args: Any, **kwargs: Any) -> None:
if self.verbose: should_print = self.verbose or (kwargs.get('component', None) in self.components)
kwargs = {k: v for k, v in kwargs.items() if k != 'component'}
if should_print:
print(*args, **kwargs, file=sys.stderr) print(*args, **kwargs, file=sys.stderr)