OpenGL UI components#52
Conversation
|
Also I can push this PR all the way replacing the per-component SDL_Surface with OpenGL. It's not hard at all with AI agent. I only left it out of scope for simpler review. Main benefit is already achieved. |
Can you give a bit more background? This code is old with old design decisions etc. which I don't remember especially as it wasn't written by me. IIRC SDL does use OpenGL as the backend/renderer, so the code alread is "using OpenGL" (to some extent) |
|
s25edit doesn't use OpenGL at all. And say I want to use ctrlButton from libsiedler2 instead of s25edit CButton. |
22129c8 to
fbd0a59
Compare
9319866 to
6dd57e4
Compare
Keep small surface per UI component that is drawn with OpenGL Avoids big 4K penalty while keeping change small
|
Hmm this PR is currently really small and nice. But replacing getSurface() with getTexture() is keeping with 2D blitting patterns, s25client uses Draw() which enables components to draw directly. Much bigger PR tho. And hmm maybe #47 gl2-terrain should really be parallel PR and not on top of this one. It doesn't change UI components at all. |
6dd57e4 to
3af4317
Compare
3af4317 to
76531bd
Compare
|
Ok I went ahead and implemented Draw() with removed getSurface() and getTexture() bigger change but removing 280 lines! |
Flamefire
left a comment
There was a problem hiding this comment.
I admit this does simplify some things.
Added comments where AI overlooked stuff or we can be more succinct or consistent.
Also remove mentions like "uses OpenGL". That looks like AI added it because it changed it. Not because it is useful information at that point.
Especially given now most is rendered with OGL it would be noteworthy if something does not. But that is usually clear by e.g. passing a destination surface
0397f02 to
c1c7b98
Compare
|
59 comments! I reviewed them with the AI guiding me and grouping them. The AI might have missed something tho, cause you know, computers have shit memory and forget thing nowadays. |
There's two full screen SDL_Surfaces left:
This branch gl2-ui deletes Surf_Display and makes Surf_Map the only full screen surface. |
Flamefire
left a comment
There was a problem hiding this comment.
But that is usually clear by e.g. passing a destination surface
There's two full screen SDL_Surfaces left:
yeah, just do a quick grep for comments and mentions of OpenGL/OGL on the diff to check for such issues please.
The AI might have missed something tho, cause you know, computers have shit memory and forget thing nowadays.
They are becoming too human 😆
Most things are (still) about using Position/Extent. I'm caring because we had subtle bugs where a C&P used e.g. "x" twice instead of "y". Those classes avoid such cases completely and make code (usually) more readable through the ops. They can even be combined like base types Position() + Extent() yields a Position, because one of those is signed which is usually what you want. Although it is not like int + unsigned yielding unsigned
| { | ||
| static std::vector<std::unique_ptr<Texture>> cache; | ||
| static std::vector<bool> linearFlags; | ||
| if(idx < 0 || idx >= static_cast<int>(global::bmpArray.size())) |
There was a problem hiding this comment.
want to do that (in a separate commit) or is that change to large?
| return false; | ||
| } | ||
| const Position origin = pos_; | ||
| const Rect winRect(origin, size_); |
There was a problem hiding this comment.
Can use the method directly at the single place this is used, maybe also origin/getPos and using Position arithmetic
There was a problem hiding this comment.
inlined origin and winRect
c01d7c5 to
345ddc6
Compare
Flamefire
left a comment
There was a problem hiding this comment.
few left-over from last round and some new after changes
| } | ||
| // Clamp window position to screen bounds | ||
| const auto res = global::s2->getRes(); | ||
| const int maxX = static_cast<int>(res.x - size_.x) - 1; |
There was a problem hiding this comment.
Just thinking about the cast: Do we ensure size < res? Otherwise we might need:
elMax(Extent::all(1), res - elMin(size_, res))
otherwise we have UB (large uint->int, lo>hi in clamp)
| { | ||
| return (global::s2->getRes() - size) / 2; | ||
| } else |
There was a problem hiding this comment.
| { | |
| return (global::s2->getRes() - size) / 2; | |
| } else | |
| return (global::s2->getRes() - size) / 2; | |
| else |
| CSurface::Draw(surface, global::bmpArray[resizebutton].surface, | ||
| Position(w_, h_) - global::bmpArray[resizebutton].getSize()); | ||
| getBmpTexture(resizebutton) | ||
| .draw(origin + Position(static_cast<int>(size_.x), static_cast<int>(size_.y)) |
There was a problem hiding this comment.
| .draw(origin + Position(static_cast<int>(size_.x), static_cast<int>(size_.y)) | |
| .draw(origin + Position(size_)) |
| return *cache[idx]; | ||
| } | ||
|
|
||
| void ensureBmpTex(int idx) |
| return; | ||
|
|
||
| const Extent tileSize = getSize(); | ||
| if(static_cast<int>(tileSize.x) <= 0 || static_cast<int>(tileSize.y) <= 0) |
There was a problem hiding this comment.
(half-)invalid check, check for zero only
| if(static_cast<int>(tileSize.x) <= 0 || static_cast<int>(tileSize.y) <= 0) | ||
| return; | ||
|
|
||
| glColor4f(1, 1, 1, 1); |
There was a problem hiding this comment.
color does need to be reset in some places. But can be moved to those specific places rather than reset globally.
There was a problem hiding this comment.
Yes, OGl calls are costly. So don't do them if not necessary, but only where you actually change things
There was a problem hiding this comment.
It's very cheap compared to copying 33MB surface from system RAM 60 times per second. 2GB/s.
Also gl commands are queued until glSwapBuffers()
(I didn't know this, just asked my AI)
I'm actually adding more gl calls in gl2-terrain #59 to improve reviewability of diff, reverting to one DrawTriangle() at a time. Essential for reviewability. And actually fixed a bug when reverting to make it closer to original.
I think it's fine but can improve it post-merge.
There was a problem hiding this comment.
Sure, but then driving your car around the block for no reason is also cheap compared to flying overseas ;-)
Just want to avoid "premature pessimization" by adding unnecessary things that take more cycles than a couple additions.
There was a problem hiding this comment.
Yep. I fixed it's good call. OCD matters for code.
| #include <Point.h> | ||
| #include <SDL.h> | ||
|
|
||
| /// Wraps a texture with RAII and provides draw methods. |
There was a problem hiding this comment.
40407bd5d0a385d3e6b6fbac531a352b33b7cb0b07809f9213765ac09c71df6c removed the docstrings.
Those were relevant
There was a problem hiding this comment.
I thought it was mostly redundant. This comment doesn't really add anything?
There was a problem hiding this comment.
Docstrings do help as they get rendered in IDEs at usage site.
The class one could mention it wraps an "OpenGL texture (handle)" as "texture" is quite generic: All the bmps are textures
And e.g. for the load method:
/// Load from a 32-bit or 8-bit paletted SDL surface.
/// For 8-bit surfaces, optional colorkey is respected (keyed pixels become transparent).
That is more than the the signature provides. So a short sentence per method in the header is useful
Especially e.g.:
/// Draw the texture stretched to fill the given rect.
void draw(const Rect& destRect) const;
How else would you know it is stretched?
There was a problem hiding this comment.
I mean Texture class name is self-explanatory.
draw with target size is implied stretched.
it was called drawStretched in previous PR but you wanted it just draw which is fine.
There was a problem hiding this comment.
I mean Texture class name is self-explanatory.
As I said: It could mean a graphics file or a hardware texture. So knowing this is an "OpenGL texture handle" at the cost of 1 line is fine.
My point about comments/docstrings is whether they save time.
Bad:
/// Get color
unsigned getColor();
// Make ARGB: alpha<<24 | red <<16 [...]
color = a << 24 | r << 16 | g << 8 | b
But the short docstrings quoted above do add information/context:
- OpenGL texture handle -> ready to use with OpenGL
- support for 8 & 32 bit with transparency
- stretching semantics
So for docstrings in headers I'd lean to including them (but keep them succinct) unless they state literally the same as the method name, like getColor above, but even then: "Get color as ARGB value" would add value.
See the borderBeginSize case: In 4yrs time you won't remember what you meant even when it's obvious now ;-)
There was a problem hiding this comment.
Ok fair. I've not used docstrings in c++ before and AI started adding them unprompted and I thought it was more slop comments.
| const int arrowIdx = MAPPIC_ARROWCROSS_ORANGE; | ||
| const auto& dispRect = map->getDisplayRect(); | ||
| const Position arrowCenter = | ||
| dispRect.getOrigin() + Position(dispRect.getSize().x / 2, dispRect.getSize().y / 2); |
There was a problem hiding this comment.
| dispRect.getOrigin() + Position(dispRect.getSize().x / 2, dispRect.getSize().y / 2); | |
| dispRect.getOrigin() + dispRect.getSize() / 2u; |
Draw all UI components with OpenGL.
Keeps UI component SDL_Surface for a smaller PR but avoids 4K penalty of uploading a 4K 32bit 33MB surface each frame 2GB per second. Only small component surfaces are uploaded each frame.
Splash, main menu and options screens are entirely OpenGL.
Replacing SDL_Surface in each component with OpenGL to be addressed in future PR.
Terrain editor Surf_Map remains 2D/SDL/SGE in this branch.