Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Need suggestion for wdDrawString performance increase #24

Closed
sduplichan opened this issue Dec 28, 2019 · 3 comments
Closed

Need suggestion for wdDrawString performance increase #24

sduplichan opened this issue Dec 28, 2019 · 3 comments
Labels

Comments

@sduplichan
Copy link

Hello and thanks for this library. Though I am an experienced software engineer, this is my first time using Windows graphics. My app needs to render a large window pixel by pixel at 60 frames per second and this direct2d library is getting the job done. The problem is that after the frame is rendered, it needs to be overlayed with many text labels. The text never changes. Right now, I am calling wdDrawString many times for each frame. This makes 60 frames per second difficult to achieve. Because the text never changes, it seems like there should be a way to prepare it one time, and reuse it over and over. But I do not see how to do that. If I could prepare the text overlay once and convert it to a bit map form, then I could merged it into the rendered bitmap efficiently.

@mity
Copy link
Owner

mity commented Dec 28, 2019

I admit the lib was created with "ease of use" rather then "being as fast as possible" in mind, for purposes of GUI controls in mctrl, where the speed is not that critical. Also, quite often some design choices were dictated by limitations of GDI+ back-end support.

Additionally, the support for old Windows versions (which had to use GDI+) was recently removed from mctrl code base and mctrl now uses Direct2D directly. Therefore my motivation to implement new features in WinDrawLib is quite low.

That said, feel free to make a pull request. Writing the code should not be that difficult and I can point you in right directions if needed.

Generally, I can see the following options:

  1. If the scene/background where the text lives changes relatively sporadically, use the canvas caching (assuming you use wdCreateCanvasWithPaintStruct()). When reusing the cached canvas, paint only those parts of the scene which do change between the frames. Direct2D implicitly uses double buffering and retains the contents of the ID2D1HwndRenderTarget which is under the hood of the canvas created with the function.

  2. Or, adding a new wdCreateBitmapCanvas() so that you can paint the strings into it and then just copy the pre-painted bitmaps. It would use ID2D1Factory::CreateWicBitmapRenderTarget() with Direct2D backend or gdix_canvas_alloc() with GDI+ backend under the hood. See wdCreateCanvasWithHDC() in src/canvas.c, the new function would be likely quite similar.

    This approach would have one drawback: Direct2D supports several modes of text aliasing. One which paints directly to the target background and uses that for achieving better aliasing quality; or a universal mode which does not use any target info and which is intended it for painting on any background. This would have to use the latter. I'm not sure how big impact on quality this would bring.

  3. Or, adding an ability to store and reuse IDWriteTextLayout. Currently, wdDrawString() in src/string.c creates it, uses it and destroys it. So, some new wdCreateTextLayout(), wdDrawTextLayout() and wdDestroyTextLayout() could be implemented to break it into separate operations so that all the expensive glyph layouting work can be reused if the strings do not change between the frames.

    Unfortunately, AFAIK, GDI+ does not support anything like that so it would have to be emulated to keep the function parity. wdCreateTextLayout() would likely just allocate some helper structure to store the text layout parameters passed to it so that wdDrawTextLayout() could retrieve them later via the handle representing the "text layout".

    (Also see WIP: Add new advanced text api (D2D1 + DWrite back-end only) #17. It was meant to do something similar and also much more then that, but it was too ambitious and it would probably never fully support GDI+ backend, that's why mCtrl swtiched to Direct2D directly and I abandoned the pull request. But some simplified version of it, in order to just break the single big operations into separate ones, should not be a problem and quite likely few initial commits of that PR more or less do that.)

@mity mity added the question label Jan 6, 2020
@mity mity closed this as completed Jan 6, 2020
@sduplichan
Copy link
Author

sduplichan commented Jan 29, 2020 via email

@mity
Copy link
Owner

mity commented Jan 29, 2020

The part I don't understand about choice 2 is how to merge the graphic image created with wdCreateImageFromBuffer and the text overlay image created with the new function wdCreateBitmapCanvas. I would like to overlay the text onto the graphic image and then display the result.

  1. You create a WIC bitmap. WinDrawLib already knows how to do it.
  2. In the new wdCreateBitmapCanvas(), you pass the WIC bitmap into ID2D1Factory::CreateWicBitmapRenderTarget() so that the bitmap canvas uses the bitmap as the storage of the created graphics.
  3. Your app clears the canvas with fully transparent color and paints the string into the bitmap canvas.
  4. Your app transforms the final bitmap into the cached image (wdCreateCachedImage()). You keep multiple cached images (e.g. for all digits to paint a game score).
  5. In the real time, you only blit the pre-painted bitmaps (wdBitBltCachedImage()) to the right position to the target bitmap.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants