-
Notifications
You must be signed in to change notification settings - Fork 93
Description
Hi there,
First off, I want to say thank you for this fantastic project. I'm a user from South Korea who happily switched from gInk to ppInk for its great features.
The Issue
I've noticed a significant performance issue when I draw a lot of strokes on a single canvas. The application works perfectly at first, but once I have around 200 strokes or more (like when writing a long math equation), it starts to lag very badly.
My PC specs are i5-10600kf and RTX 2060 Super.
What I've Tried
Before writing this, I searched on Google and through the repository's issues, but I couldn't find anyone else talking about this specific problem.
I should be clear that I'm not a developer. However, I enjoy this program so much that I was determined to see if I could solve this on my own just so I could continue using it smoothly. As I use AI assistants frequently, I spent the last five days trying to do just that with Gemini 2.5 Pro on Google Ai Studio. Unfortunately, none of my attempts were successful in the end.
I'm just wondering if the performance I'm seeing is normal, or if maybe most people don't draw with so many strokes at once.
Thanks again for your hard work on this tool. I've starred the repo to show my support and hope this report is helpful.
Best regards.
P.S. I am attaching the final prompt I used with the AI below. My approach was focused on improving the rendering, and I think I created almost 90 different chat sessions trying different prompts and methods haha. As a non-developer, my troubleshooting approach to the rendering might have been completely naive or unrealistic, and I apologize if that's the case. I'm simply sharing my full process on the off chance it provides any context at all. Please feel free to completely ignore the attachment if it isn't useful.
### **Project Kick-off: Optimizing ppInk's Rendering with a Cached, GPU-Accelerated Pipeline**
Hello. I need your assistance with a C# code optimization project. I will act as the project manager and tester, while you will be the primary coder.
**Project Goal:**
The core objective is to **optimize the rendering performance of the ppInk drawing application.** The current GDI+ implementation redraws all strokes on every frame, causing significant performance degradation, especially during rapid drawing.
To achieve this, we will implement the following architectural plan:
---
### **Core Components (The Architectural Plan)**
1. **Three Visual Layers, Kept Separate:**
* **The Clean Cache (`_cachedStrokesBitmap`):** A `Bitmap` holding the image of all strokes that are considered "settled."
* **The Dirty Cache (`_dynamicStrokesBitmap`):** A `Bitmap` holding only the new strokes created during a recent, rapid drawing session.
* **The In-Progress Stroke:** The live stroke currently being drawn by the user (pen is down).
2. **Robust, Artifact-Free GDI+ Rendering:**
* To prevent the "black outline" bug, the `Graphics.FromImage()` method will **not be used** for drawing strokes.
* Instead, a lower-level, more robust Win32 API approach will be used to render strokes into the cache bitmaps:
a. Create an in-memory Device Context (HDC) via `CreateCompatibleDC`.
b. Select the target `Bitmap` into this HDC.
c. Call `Microsoft.Ink.Renderer.Draw(hdc, strokes)`.
* This method is known to bypass the pre-multiplied alpha bugs, ensuring strokes are rendered cleanly onto the bitmap without the erroneous black fringe.
3. **Intelligent, Delayed Cache Regeneration:**
* When the user is drawing rapidly, each completed stroke on `MouseUp` is drawn **only to the fast "Dirty Cache"**.
* A **debounce timer** (e.g., 250ms) is started or reset on every `MouseUp`.
* The full, slow cache regeneration (merging the "Dirty" layer into the "Clean" layer) **only occurs after the user has paused drawing** for the duration of the timer.
4. **High-Performance GPU Compositing:**
* On every frame, the GPU (via GameOverlay.Net) is responsible for the final composition. It performs fast `DrawImage` calls for each layer:
1. Draw the `_cachedStrokesBitmap`.
2. Draw the `_dynamicStrokesBitmap` on top.
3. Draw the UI panels on top.
* If a stroke is actively being drawn, it is rendered as a fourth, temporary layer on the very top.
---
**Guiding Principles & Important Constraints:**
1. **Leverage Existing Renderer:** We will **not** re-implement stroke geometry or pressure sensitivity. We will use the existing `Microsoft.Ink.Renderer` as described in the architectural plan.
2. **CRITICAL: Avoid GDI+ Artifacts:** To reiterate the architectural plan, you **must not** use `Graphics.FromImage()` to get a `Graphics` object from a transparent `Bitmap`. All stroke rendering to the cache bitmaps **must** be done by obtaining a Win32 HDC.
3. **CRITICAL: No Manual Geometry:** Do not attempt to render strokes by manually reading packet data and drawing polygons. This approach has been tested and results in unacceptable visual artifacts. The `Microsoft.Ink.Renderer` is the sole authority for generating stroke visuals.
---
**Development & Testing Environment:**
* **OS:** Windows 10
* **IDE:** Visual Studio 2022
* **Hardware:** Intel i5-10600KF, NVIDIA RTX 2060 SUPER, 16GB RAM
* **Input Device:** Wacom Pen Tablet (using Microsoft Ink services)
---
**Role Division:**
* **My Role (Tester & Project Manager):**
* **Important Note:** I am a general user with limited programming knowledge, not a professional developer. Please provide clear, step-by-step instructions, as I will be the one handling the code integration and testing.
* I will compile and run the code after each modification you provide.
* I will test the functionality using my Wacom tablet.
* I can set breakpoints and provide you with debugging information (variable values, call stacks, etc.) if you guide me on how to do so.
* **Your Role (Coder):**
* You will write the C# code to implement the new cached rendering logic.
* You will analyze the provided code and ask for clarification or additional information when needed, keeping my level of expertise in mind.
---
**Workflow & Output Format:**
To ensure a smooth process, please adhere to the following format for all your responses that contain code:
1. **Checkpoint Numbering:** Every response that introduces **new functionality or a new migration step** must begin with a new checkpoint number (e.g., `**Checkpoint 1:**`, `**Checkpoint 2:**`). I will be committing each new checkpoint to Git.
2. **Revision Handling:** If a bug is found or an error needs correction within the *current* checkpoint, your next response should **not** advance the number. Instead, clearly label it as a revision, like this: `**Checkpoint 1 - Revision 1:**` or `**Checkpoint 2 - Revision A:**`.
3. **MANDATORY Code Formatting Instructions:**
* You **must** provide code changes in small, manageable snippets. **Do NOT output entire methods or classes.**
* Each snippet must include **exactly 4 lines of the original, unchanged code both *before* and *after* the modified section.** This provides the context I need to find the location using a text search.
* Within the snippet, **highlight the specific lines you have added or changed** by enclosing them with comments: `// --- START MODIFICATION ---` and `// --- END MODIFICATION ---`.
**Example of the REQUIRED format:**
*If the original code is:*
```csharp
// ...
LineA();
LineB();
LineC();
LineD();
LineToReplace();
LineF();
LineG();
LineH();
LineI();
// ...
```
*Your output MUST be:*
```csharp
// ... (original code)
LineA();
LineB();
LineC();
LineD();
// --- START MODIFICATION ---
NewLine1();
NewLine2();
// --- END MODIFICATION ---
LineF();
LineG();
LineH();
LineI();
// ... (original code)
```
---
**Attached Information:**
For your analysis, I am providing the source code for the key classes involved in the application's UI and rendering logic:
1. **`FormDisplay.cs`**
2. **`FormCollection.cs`**
3. **`Root.cs`**
---
**Let's begin.**
Based on the architectural plan and constraints we've defined, please start the implementation. As the developer, you should determine the best first steps to achieve our goal.
Please provide the first set of code changes for **Checkpoint 1**.