Browse Source

Add caching of element clipping on the element to remove property lookups during render.

This more than doubled the frame rate on an iPod Touch while rendering clipped elements.
Lloyd Weehuizen 15 years ago
parent
commit
aa1d222245

+ 12 - 0
Include/Rocket/Core/Element.h

@@ -513,6 +513,11 @@ public:
 	/// @return The element's scrolling functionality.
 	/// @return The element's scrolling functionality.
 	ElementScroll* GetElementScroll() const;
 	ElementScroll* GetElementScroll() const;
 	//@}
 	//@}
+	
+	/// Returns true if this element requires clipping
+	int GetClippingIgnoreDepth();
+	/// Returns true if this element has clipping enabled
+	bool IsClippingEnabled();
 
 
 	/// Gets the render interface owned by this element's context.
 	/// Gets the render interface owned by this element's context.
 	/// @return The element's context's render interface.
 	/// @return The element's context's render interface.
@@ -595,6 +600,8 @@ private:
 	Element* parent;
 	Element* parent;
 	// Currently focused child object
 	// Currently focused child object
 	Element* focus;
 	Element* focus;
+	// The owning document
+	ElementDocument* owner_document;
 
 
 	// The event dispatcher for this element.
 	// The event dispatcher for this element.
 	EventDispatcher* event_dispatcher;
 	EventDispatcher* event_dispatcher;
@@ -651,6 +658,11 @@ private:
 
 
 	// The element's font face; used to render text and resolve em / ex properties.
 	// The element's font face; used to render text and resolve em / ex properties.
 	FontFaceHandle* font_face_handle;
 	FontFaceHandle* font_face_handle;
+	
+	// Cached rendering information
+	int clipping_ignore_depth;
+	bool clipping_enabled;
+	bool clipping_state_dirty;
 
 
 	friend class Context;
 	friend class Context;
 	friend class ElementStyle;
 	friend class ElementStyle;

+ 1 - 1
Include/Rocket/Core/StringUtilities.h

@@ -65,7 +65,7 @@ public:
 	/// @param[in] data Data to hash.
 	/// @param[in] data Data to hash.
 	/// @param[in] length Length of the string to hash. If this is -1, the data will be interpreted as a C string.
 	/// @param[in] length Length of the string to hash. If this is -1, the data will be interpreted as a C string.
 	/// @return Integer hash of the data.
 	/// @return Integer hash of the data.
-	static Hash FNVHash(const char* data, int length = -1);
+	static Hash FNVHash(const char* data);
 
 
 	/// Encodes a string with URL-encoding.
 	/// Encodes a string with URL-encoding.
 	/// @param[in] input Input ASCII string to encode.
 	/// @param[in] input Input ASCII string to encode.

+ 55 - 2
Source/Core/Element.cpp

@@ -79,12 +79,12 @@ Element::Element(const String& _tag) : absolute_offset(0, 0), relative_offset_ba
 	parent = NULL;
 	parent = NULL;
 	focus = NULL;
 	focus = NULL;
 	instancer = NULL;
 	instancer = NULL;
+	owner_document = NULL;
 
 
 	offset_fixed = false;
 	offset_fixed = false;
 	offset_parent = NULL;
 	offset_parent = NULL;
 	offset_dirty = true;
 	offset_dirty = true;
 
 
-//	client_area = Box::CONTENT;
 	client_area = Box::PADDING;
 	client_area = Box::PADDING;
 
 
 	num_non_dom_children = 0;
 	num_non_dom_children = 0;
@@ -98,6 +98,10 @@ Element::Element(const String& _tag) : absolute_offset(0, 0), relative_offset_ba
 	stacking_context_dirty = false;
 	stacking_context_dirty = false;
 
 
 	font_face_handle = NULL;
 	font_face_handle = NULL;
+	
+	clipping_ignore_depth = 0;
+	clipping_enabled = false;
+	clipping_state_dirty = true;
 
 
 	event_dispatcher = new EventDispatcher(this);
 	event_dispatcher = new EventDispatcher(this);
 	style = new ElementStyle(this);
 	style = new ElementStyle(this);
@@ -792,8 +796,13 @@ ElementDocument* Element::GetOwnerDocument()
 {
 {
 	if (parent == NULL)
 	if (parent == NULL)
 		return NULL;
 		return NULL;
+	
+	if (!owner_document)
+	{
+		owner_document = parent->GetOwnerDocument();
+	}
 
 
-	return parent->GetOwnerDocument();
+	return owner_document;
 }
 }
 
 
 // Gets this element's parent node.
 // Gets this element's parent node.
@@ -1215,6 +1224,38 @@ ElementScroll* Element::GetElementScroll() const
 {
 {
 	return scroll;
 	return scroll;
 }
 }
+	
+int Element::GetClippingIgnoreDepth()
+{
+	if (clipping_state_dirty)
+	{
+		IsClippingEnabled();
+	}
+	
+	return clipping_ignore_depth;
+}
+	
+bool Element::IsClippingEnabled()
+{
+	if (clipping_state_dirty)
+	{
+		// Is clipping enabled for this element, yes unless both overlow properties are set to visible
+		clipping_enabled = style->GetProperty(OVERFLOW_X)->Get< int >() != OVERFLOW_VISIBLE 
+							|| style->GetProperty(OVERFLOW_Y)->Get< int >() != OVERFLOW_VISIBLE;
+		
+		// Get the clipping ignore depth from the clip property
+		clipping_ignore_depth = 0;
+		const Property* clip_property = GetProperty(CLIP);
+		if (clip_property->unit == Property::NUMBER)
+			clipping_ignore_depth = clip_property->Get< int >();
+		else if (clip_property->Get< int >() == CLIP_NONE)
+			clipping_ignore_depth = -1;
+		
+		clipping_state_dirty = false;
+	}
+	
+	return clipping_enabled;
+}
 
 
 // Gets the render interface owned by this element's context.
 // Gets the render interface owned by this element's context.
 RenderInterface* Element::GetRenderInterface()
 RenderInterface* Element::GetRenderInterface()
@@ -1446,6 +1487,14 @@ void Element::OnPropertyChange(const PropertyNameList& changed_properties)
 		else if (new_font_face_handle != NULL)
 		else if (new_font_face_handle != NULL)
 			new_font_face_handle->RemoveReference();
 			new_font_face_handle->RemoveReference();
 	}
 	}
+	
+	// Check for clipping state changes
+	if (changed_properties.find(CLIP) != changed_properties.end() ||
+		changed_properties.find(OVERFLOW_X) != changed_properties.end() ||
+		changed_properties.find(OVERFLOW_Y) != changed_properties.end())
+	{
+		clipping_state_dirty = true;
+	}
 }
 }
 
 
 // Called when a child node has been added somewhere in the hierarchy
 // Called when a child node has been added somewhere in the hierarchy
@@ -1758,6 +1807,10 @@ void Element::DirtyStackingContext()
 
 
 void Element::DirtyStructure()
 void Element::DirtyStructure()
 {
 {
+	// Clear the cached owner document
+	owner_document = NULL;
+	
+	// Inform all children that the structure is drity
 	for (size_t i = 0; i < children.size(); ++i)
 	for (size_t i = 0; i < children.size(); ++i)
 	{
 	{
 		const ElementDefinition* element_definition = children[i]->GetStyle()->GetDefinition();
 		const ElementDefinition* element_definition = children[i]->GetStyle()->GetDefinition();

+ 18 - 52
Source/Core/ElementUtilities.cpp

@@ -45,32 +45,15 @@ struct ClipRegion
 	{
 	{
 	}
 	}
 
 
-	int AddClipElement(Rocket::Core::Element* element)
+	void AddClipElement(Rocket::Core::Element* element)
 	{
 	{
-		int num_ignored_clips = 0;
-		const Property* clip_property = element->GetProperty(CLIP);
-		if (clip_property->unit == Property::NUMBER)
-			num_ignored_clips = clip_property->Get< int >();
-		else if (clip_property->Get< int >() == CLIP_NONE)
-			num_ignored_clips = -1;
-
-		// Ignore nodes that don't clip.
-		if ((element->GetProperty(OVERFLOW_X)->Get< int >() == OVERFLOW_VISIBLE &&
-			 element->GetProperty(OVERFLOW_Y)->Get< int >() == OVERFLOW_VISIBLE) ||
-			(element->GetClientWidth() > element->GetScrollWidth() &&
-			 element->GetClientHeight() > element->GetScrollHeight()))
-		{
-			return num_ignored_clips;
-		}
-
-		Vector2f element_origin_f = element->GetAbsoluteOffset(/*clipping_element->GetClientArea()*/ Box::CONTENT);
-		Vector2f element_dimensions_f = element->GetBox().GetSize(/*clipping_element->GetClientArea()*/ Box::CONTENT);
+		Vector2f element_origin_f = element->GetAbsoluteOffset(Box::CONTENT);
+		Vector2f element_dimensions_f = element->GetBox().GetSize(Box::CONTENT);
 
 
 		Vector2i element_origin(Math::RealToInteger(element_origin_f.x), Math::RealToInteger(element_origin_f.y));
 		Vector2i element_origin(Math::RealToInteger(element_origin_f.x), Math::RealToInteger(element_origin_f.y));
 		Vector2i element_dimensions(Math::RealToInteger(element_dimensions_f.x), Math::RealToInteger(element_dimensions_f.y));
 		Vector2i element_dimensions(Math::RealToInteger(element_dimensions_f.x), Math::RealToInteger(element_dimensions_f.y));
 
 
-		if (origin == Vector2i(-1, -1) &&
-			dimensions == Vector2i(-1, -1))
+		if (origin == Vector2i(-1, -1) && dimensions == Vector2i(-1, -1))
 		{
 		{
 			origin = element_origin;
 			origin = element_origin;
 			dimensions = element_dimensions;
 			dimensions = element_dimensions;
@@ -87,16 +70,12 @@ struct ClipRegion
 			dimensions.x = Math::Max(0, bottom_right.x - top_left.x);
 			dimensions.x = Math::Max(0, bottom_right.x - top_left.x);
 			dimensions.y = Math::Max(0, bottom_right.y - top_left.y);
 			dimensions.y = Math::Max(0, bottom_right.y - top_left.y);
 		}
 		}
-
-		return num_ignored_clips;
 	}
 	}
 
 
 	Vector2i origin;
 	Vector2i origin;
 	Vector2i dimensions;
 	Vector2i dimensions;
 };
 };
 
 
-//typedef std::vector< ClippingRegion > ClippingRegionStack;
-
 struct ClipState
 struct ClipState
 {
 {
 	ClipState() : clip_region(Vector2i(-1, -1), Vector2i(-1, -1))
 	ClipState() : clip_region(Vector2i(-1, -1), Vector2i(-1, -1))
@@ -112,9 +91,6 @@ struct ClipState
 typedef std::map< RenderInterface*, ClipState > ClipStateMap;
 typedef std::map< RenderInterface*, ClipState > ClipStateMap;
 ClipStateMap clip_states;
 ClipStateMap clip_states;
 
 
-// Returns true if the element is visible within the current clipping region (if any), false if not.
-//static bool IsElementVisible(const Element* element);
-
 // Builds and sets the box for an element.
 // Builds and sets the box for an element.
 static void SetBox(Element* element);
 static void SetBox(Element* element);
 // Positions an element relative to an offset parent.
 // Positions an element relative to an offset parent.
@@ -252,26 +228,13 @@ void ElementUtilities::BindEventAttributes(Element* element)
 bool ElementUtilities::GetClippingRegion(Vector2i& clip_origin, Vector2i& clip_dimensions, Element* element)
 bool ElementUtilities::GetClippingRegion(Vector2i& clip_origin, Vector2i& clip_dimensions, Element* element)
 {
 {
 	ClipRegion clip_region;
 	ClipRegion clip_region;
-//	ClippingRegionStack clipping_regions;
 
 
 	// Check the clip property of the clipped element; if it is not clipped, then the clip root will be NULL.
 	// Check the clip property of the clipped element; if it is not clipped, then the clip root will be NULL.
 	if (element != NULL)
 	if (element != NULL)
 	{
 	{
-		const Property* clip_property = element->GetProperty(CLIP);
-
-		// Does this element ignore all clipping?
-		if (clip_property->unit == Property::KEYWORD &&
-			clip_property->value.Get< int >() == CLIP_NONE)
-		{
+		int num_ignored_clips = element->GetClippingIgnoreDepth();
+		if (num_ignored_clips < 0)
 			return false;
 			return false;
-		}
-
-		int num_ignored_clips = 0;
-		if (clip_property->unit == Property::NUMBER)
-		{
-			if (clip_property->unit == Property::NUMBER)
-				num_ignored_clips = Math::RealToInteger(clip_property->value.Get< float >());
-		}
 
 
 		// Search through the element's ancestors, finding all elements that clip their overflow and have overflow to clip.
 		// Search through the element's ancestors, finding all elements that clip their overflow and have overflow to clip.
 		// For each that we find, we combine their clipping region with the existing clipping region, and so build up a
 		// For each that we find, we combine their clipping region with the existing clipping region, and so build up a
@@ -281,25 +244,28 @@ bool ElementUtilities::GetClippingRegion(Vector2i& clip_origin, Vector2i& clip_d
 		while (clipping_element != NULL)
 		while (clipping_element != NULL)
 		{
 		{
 			// Merge the existing clip region with the current clip region if we aren't ignoring clip regions.
 			// Merge the existing clip region with the current clip region if we aren't ignoring clip regions.
-			if (num_ignored_clips == 0)
-				clip_region.AddClipElement(clipping_element);
+			if (num_ignored_clips == 0 && clipping_element->IsClippingEnabled())
+			{
+				// Ignore nodes that don't clip.
+				if (clipping_element->GetClientWidth() < clipping_element->GetScrollWidth()
+					|| clipping_element->GetClientHeight() < clipping_element->GetScrollHeight())				 
+					clip_region.AddClipElement(clipping_element);
+			}
 
 
 			// If this region is meant to clip and we're skipping regions, update the counter.
 			// If this region is meant to clip and we're skipping regions, update the counter.
 			if (num_ignored_clips > 0)
 			if (num_ignored_clips > 0)
 			{
 			{
-				if (clipping_element->GetProperty(OVERFLOW_X)->Get< int >() != OVERFLOW_VISIBLE ||
-					clipping_element->GetProperty(OVERFLOW_Y)->Get< int >() != OVERFLOW_VISIBLE)
+				if (clipping_element->IsClippingEnabled())
 					num_ignored_clips--;
 					num_ignored_clips--;
 			}
 			}
 
 
 			// Determine how many clip regions this ancestor ignores, and inherit the value. If this region ignores all
 			// Determine how many clip regions this ancestor ignores, and inherit the value. If this region ignores all
 			// clipping regions, then we do too.
 			// clipping regions, then we do too.
-			int num_ignored_clips = 0;
-			const Property* clip_property = clipping_element->GetProperty(CLIP);
-			if (clip_property->unit == Property::NUMBER)
-				num_ignored_clips = Math::Max(num_ignored_clips, clip_property->Get< int >());
-			else if (clip_property->Get< int >() == CLIP_NONE)
+			int clipping_element_ignore_clips = clipping_element->GetClippingIgnoreDepth();
+			if (clipping_element_ignore_clips < 0)
 				break;
 				break;
+			
+			num_ignored_clips = Math::Max(num_ignored_clips, clipping_element_ignore_clips);
 
 
 			// Climb the tree to this region's parent.
 			// Climb the tree to this region's parent.
 			clipping_element = clipping_element->GetParentNode();
 			clipping_element = clipping_element->GetParentNode();

+ 2 - 3
Source/Core/StringUtilities.cpp

@@ -109,15 +109,14 @@ String StringUtilities::MD5Hash(const char* data, int length)
 }
 }
 
 
 // Hashes a string of data to an integer value using the FNV algorithm.
 // Hashes a string of data to an integer value using the FNV algorithm.
-Hash StringUtilities::FNVHash(const char *string, int length)
+Hash StringUtilities::FNVHash(const char *string)
 {
 {
 	// FNV-1 hash algorithm
 	// FNV-1 hash algorithm
 	Hash hval = 0;
 	Hash hval = 0;
 	unsigned char *bp = (unsigned char *)string;	// start of buffer
 	unsigned char *bp = (unsigned char *)string;	// start of buffer
-	unsigned char *be = bp + ( length < 0 ? strlen(string) : length );	// beyond end of buffer
     
     
 	// FNV-1 hash each octet in the buffer
 	// FNV-1 hash each octet in the buffer
-	while (bp < be) 
+	while (*bp) 
 	{
 	{
 		// multiply by the 32 bit FNV magic prime mod 2^32 
 		// multiply by the 32 bit FNV magic prime mod 2^32 
 		hval += (hval<<1) + (hval<<4) + (hval<<7) + (hval<<8) + (hval<<24);
 		hval += (hval<<1) + (hval<<4) + (hval<<7) + (hval<<8) + (hval<<24);