From 3682fac7b6f43b1417236d4e7d69035feb6e8e31 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 29 May 2025 00:29:33 +0800 Subject: [PATCH] system/ui: optimizes the draw_polygon() and improving code maintainability (#35366) improve draw_polygon --- system/ui/lib/shader_polygon.py | 132 ++++++++++++++++---------------- 1 file changed, 67 insertions(+), 65 deletions(-) diff --git a/system/ui/lib/shader_polygon.py b/system/ui/lib/shader_polygon.py index dfbfa969d5..23f54efcd7 100644 --- a/system/ui/lib/shader_polygon.py +++ b/system/ui/lib/shader_polygon.py @@ -152,8 +152,14 @@ void main() { """ +UNIFORM_INT = rl.ShaderUniformDataType.SHADER_UNIFORM_INT +UNIFORM_FLOAT = rl.ShaderUniformDataType.SHADER_UNIFORM_FLOAT +UNIFORM_VEC2 = rl.ShaderUniformDataType.SHADER_UNIFORM_VEC2 +UNIFORM_VEC4 = rl.ShaderUniformDataType.SHADER_UNIFORM_VEC4 + + class ShaderState: - _instance: Any = None + _instance: Any = None @classmethod def get_instance(cls): @@ -184,6 +190,19 @@ class ShaderState: 'mvp': None, } + # Pre-allocated FFI objects + self.point_count_ptr = rl.ffi.new("int[]", [0]) + self.resolution_ptr = rl.ffi.new("float[]", [0.0, 0.0]) + self.fill_color_ptr = rl.ffi.new("float[]", [0.0, 0.0, 0.0, 0.0]) + self.use_gradient_ptr = rl.ffi.new("int[]", [0]) + self.gradient_start_ptr = rl.ffi.new("float[]", [0.0, 0.0]) + self.gradient_end_ptr = rl.ffi.new("float[]", [0.0, 0.0]) + self.color_count_ptr = rl.ffi.new("int[]", [0]) + + # Pre-allocate gradient arrays (max 8 colors) + self.gradient_colors_ptr = rl.ffi.new("float[]", 32) # 8 colors * 4 components + self.gradient_stops_ptr = rl.ffi.new("float[]", 8) + def initialize(self): if self.initialized: return @@ -222,12 +241,46 @@ class ShaderState: self.initialized = False +def _configure_shader_color(state, color, gradient): + """Configure shader uniforms for solid color or gradient rendering""" + state.use_gradient_ptr[0] = 1 if gradient else 0 + rl.set_shader_value(state.shader, state.locations['useGradient'], state.use_gradient_ptr, UNIFORM_INT) + + if gradient: + # Set gradient start/end + state.gradient_start_ptr[0:2] = gradient['start'] + state.gradient_end_ptr[0:2] = gradient['end'] + rl.set_shader_value(state.shader, state.locations['gradientStart'], state.gradient_start_ptr, UNIFORM_VEC2) + rl.set_shader_value(state.shader, state.locations['gradientEnd'], state.gradient_end_ptr, UNIFORM_VEC2) + + # Set gradient colors + colors = gradient['colors'] + color_count = min(len(colors), 8) # Max 8 colors + for i, c in enumerate(colors[:color_count]): + base_idx = i * 4 + state.gradient_colors_ptr[base_idx:base_idx+4] = [c.r / 255.0, c.g / 255.0, c.b / 255.0, c.a / 255.0] + rl.set_shader_value_v(state.shader, state.locations['gradientColors'], state.gradient_colors_ptr, UNIFORM_VEC4, color_count) + + # Set gradient stops + stops = gradient.get('stops', [i / (color_count - 1) for i in range(color_count)]) + state.gradient_stops_ptr[0:color_count] = stops[:color_count] + rl.set_shader_value_v(state.shader, state.locations['gradientStops'], state.gradient_stops_ptr, UNIFORM_FLOAT, color_count) + + # Set color count + state.color_count_ptr[0] = color_count + rl.set_shader_value(state.shader, state.locations['gradientColorCount'], state.color_count_ptr, UNIFORM_INT) + else: + color = color or rl.WHITE # Default to white if no color provided + state.fill_color_ptr[0:4] = [color.r / 255.0, color.g / 255.0, color.b / 255.0, color.a / 255.0] + rl.set_shader_value(state.shader, state.locations['fillColor'], state.fill_color_ptr, UNIFORM_VEC4) + + def draw_polygon(points: np.ndarray, color=None, gradient=None): """ Draw a complex polygon using shader-based even-odd fill rule Args: - points: List of (x,y) points defining the polygon + points: numpy array of (x,y) points defining the polygon color: Solid fill color (rl.Color) gradient: Dict with gradient parameters: { @@ -240,92 +293,41 @@ def draw_polygon(points: np.ndarray, color=None, gradient=None): if len(points) < 3: return - # Get shader state singleton state = ShaderState.get_instance() - - # Initialize shader if not already done if not state.initialized: state.initialize() # Find bounding box min_xy = np.min(points, axis=0) - min_x, min_y = min_xy - max_x, max_y = np.max(points, axis=0) + max_xy = np.max(points, axis=0) - width = max(1, max_x - min_x) - height = max(1, max_y - min_y) + width = max(1, max_xy[0] - min_xy[0]) + height = max(1, max_xy[1] - min_xy[1]) # Transform points to shader space transformed_points = points - min_xy - # Set basic shader uniforms using cached locations - point_count_ptr = rl.ffi.new("int[]", [len(transformed_points)]) - rl.set_shader_value(state.shader, state.locations['pointCount'], point_count_ptr, rl.ShaderUniformDataType.SHADER_UNIFORM_INT) + state.point_count_ptr[0] = len(transformed_points) + rl.set_shader_value(state.shader, state.locations['pointCount'], state.point_count_ptr, UNIFORM_INT) + + state.resolution_ptr[0:2] = [width, height] + rl.set_shader_value(state.shader, state.locations['resolution'], state.resolution_ptr, UNIFORM_VEC2) - resolution_ptr = rl.ffi.new("float[]", [width, height]) - rl.set_shader_value(state.shader, state.locations['resolution'], resolution_ptr, rl.ShaderUniformDataType.SHADER_UNIFORM_VEC2) # Set points flat_points = np.ascontiguousarray(transformed_points.flatten().astype(np.float32)) points_ptr = rl.ffi.cast("float *", flat_points.ctypes.data) - rl.set_shader_value_v( - state.shader, state.locations['points'], points_ptr, rl.ShaderUniformDataType.SHADER_UNIFORM_VEC2, len(transformed_points) - ) - - # Set gradient or solid color based on what was provided - if gradient: - # Enable gradient - use_gradient_ptr = rl.ffi.new("int[]", [1]) - rl.set_shader_value(state.shader, state.locations['useGradient'], use_gradient_ptr, rl.ShaderUniformDataType.SHADER_UNIFORM_INT) - - # Set gradient start/end - start_ptr = rl.ffi.new("float[]", [gradient['start'][0], gradient['start'][1]]) - end_ptr = rl.ffi.new("float[]", [gradient['end'][0], gradient['end'][1]]) - rl.set_shader_value(state.shader, state.locations['gradientStart'], start_ptr, rl.ShaderUniformDataType.SHADER_UNIFORM_VEC2) - rl.set_shader_value(state.shader, state.locations['gradientEnd'], end_ptr, rl.ShaderUniformDataType.SHADER_UNIFORM_VEC2) - - # Set gradient colors - colors = gradient['colors'] - color_count = min(len(colors), 8) # Max 8 colors - colors_ptr = rl.ffi.new("float[]", color_count * 4) - for i, c in enumerate(colors[:color_count]): - colors_ptr[i * 4] = c.r / 255.0 - colors_ptr[i * 4 + 1] = c.g / 255.0 - colors_ptr[i * 4 + 2] = c.b / 255.0 - colors_ptr[i * 4 + 3] = c.a / 255.0 - rl.set_shader_value_v( - state.shader, state.locations['gradientColors'], colors_ptr, rl.ShaderUniformDataType.SHADER_UNIFORM_VEC4, color_count - ) - - # Set gradient stops - stops = gradient.get('stops', [i / (color_count - 1) for i in range(color_count)]) - stops_ptr = rl.ffi.new("float[]", color_count) - for i, s in enumerate(stops[:color_count]): - stops_ptr[i] = s - rl.set_shader_value_v( - state.shader, state.locations['gradientStops'], stops_ptr, rl.ShaderUniformDataType.SHADER_UNIFORM_FLOAT, color_count - ) - - # Set color count - color_count_ptr = rl.ffi.new("int[]", [color_count]) - rl.set_shader_value(state.shader, state.locations['gradientColorCount'], color_count_ptr, rl.ShaderUniformDataType.SHADER_UNIFORM_INT) - else: - # Disable gradient - use_gradient_ptr = rl.ffi.new("int[]", [0]) - rl.set_shader_value(state.shader, state.locations['useGradient'], use_gradient_ptr, rl.ShaderUniformDataType.SHADER_UNIFORM_INT) + rl.set_shader_value_v(state.shader, state.locations['points'], points_ptr, UNIFORM_VEC2, len(transformed_points)) - # Set solid color - if color is None: - color = rl.WHITE - fill_color_ptr = rl.ffi.new("float[]", [color.r / 255.0, color.g / 255.0, color.b / 255.0, color.a / 255.0]) - rl.set_shader_value(state.shader, state.locations['fillColor'], fill_color_ptr, rl.ShaderUniformDataType.SHADER_UNIFORM_VEC4) + # Configure color/gradient uniforms + _configure_shader_color(state, color, gradient) # Draw with shader rl.begin_shader_mode(state.shader) rl.draw_texture_pro( state.white_texture, rl.Rectangle(0, 0, 2, 2), - rl.Rectangle(int(min_x), int(min_y), int(width), int(height)), + rl.Rectangle(int(min_xy[0]), int(min_xy[1]), int(width), int(height)), rl.Vector2(0, 0), 0.0, rl.WHITE,