Skip to content

Commit 22129c8

Browse files
author
Morgan Christiansson
committed
Draw UI with OpenGL, delete Surf_Display
Keep small surface per UI component that is drawn with OpenGL Avoids big 4K penalty while keeping change small
1 parent 1dd3c6e commit 22129c8

13 files changed

Lines changed: 247 additions & 122 deletions

CGame.cpp

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -77,24 +77,9 @@ int CGame::Execute()
7777

7878
void CGame::RenderPresent()
7979
{
80-
glBindTexture(GL_TEXTURE_2D, displayTex_);
81-
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, Surf_Display->w, Surf_Display->h, GL_BGRA, GL_UNSIGNED_BYTE,
82-
Surf_Display->pixels);
83-
// Screen was cleared at the start of Render(); GL-drawn backgrounds
84-
// are already in the framebuffer. The Surf_Display texture (with
85-
// alpha=0 for transparent areas) is blended on top via GL_BLEND.
86-
glBegin(GL_QUADS);
87-
glTexCoord2f(0, 0);
88-
glVertex2i(0, 0);
89-
glTexCoord2f(1, 0);
90-
glVertex2i(GameResolution.x, 0);
91-
glTexCoord2f(1, 1);
92-
glVertex2i(GameResolution.x, GameResolution.y);
93-
glTexCoord2f(0, 1);
94-
glVertex2i(0, GameResolution.y);
95-
glEnd();
96-
97-
// Draw cursor on top of everything
80+
// Used by callbacks.cpp to flush a "Please wait" window to screen immediately.
81+
// The caller is responsible for drawing (e.g. via renderGL()) before calling this.
82+
// We just draw the cursor on top and swap.
9883
{
9984
const auto& cursorImg = Cursor.clicked ? (Cursor.button.right ? cross_ : cursorClicked_) : cursor_;
10085
cursorImg.Draw(Cursor.pos.x, Cursor.pos.y);

CGame.h

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,7 @@ class CGame
2727

2828
bool Running;
2929
bool showLoadScreen;
30-
SdlSurface Surf_Display;
3130
SDL_GLContext glContext_ = nullptr;
32-
unsigned int displayTex_ = 0;
3331
SdlWindow window_;
3432

3533
private:
@@ -53,6 +51,8 @@ class CGame
5351
GlTexture cursor_;
5452
GlTexture cursorClicked_;
5553
GlTexture cross_;
54+
GlTexture fpsTex_; ///< GL texture for the FPS counter
55+
GlTexture mapTex_; ///< GL texture for the map/terrain (Surf_Map may be 8-bit)
5656

5757
// structure for mouse cursor
5858
struct
@@ -76,7 +76,6 @@ class CGame
7676
std::unique_ptr<CMap> MapObj;
7777

7878
void SetAppIcon();
79-
void RecreateDisplayResources();
8079
void setGLViewport();
8180

8281
public:
@@ -110,6 +109,5 @@ class CGame
110109
CMap* getMapObj();
111110
void delMapObj();
112111
void enterEditor(const boost::filesystem::path& filepath);
113-
SDL_Surface* getDisplaySurface() const { return Surf_Display.get(); };
114112
auto getRes() const { return GameResolution; }
115113
};

CGame_Init.cpp

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,6 @@
1818
bool CGame::ReCreateWindow()
1919
{
2020
suppressResizeEvents_ = 3;
21-
if(displayTex_)
22-
{
23-
glDeleteTextures(1, &displayTex_);
24-
displayTex_ = 0;
25-
}
2621
if(glContext_)
2722
{
2823
SDL_GL_DeleteContext(glContext_);
@@ -46,32 +41,10 @@ bool CGame::ReCreateWindow()
4641
glClearColor(0, 0, 0, 1);
4742
setGLViewport();
4843

49-
RecreateDisplayResources();
50-
if(!displayTex_ || !Surf_Display)
51-
return false;
52-
5344
SetAppIcon();
5445
return true;
5546
}
5647

57-
void CGame::RecreateDisplayResources()
58-
{
59-
if(displayTex_)
60-
{
61-
glDeleteTextures(1, &displayTex_);
62-
displayTex_ = 0;
63-
}
64-
Surf_Display = makeRGBSurface(GameResolution.x, GameResolution.y, true);
65-
66-
glGenTextures(1, &displayTex_);
67-
glBindTexture(GL_TEXTURE_2D, displayTex_);
68-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
69-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
70-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
71-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
72-
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, GameResolution.x, GameResolution.y, 0, GL_BGRA, GL_UNSIGNED_BYTE, nullptr);
73-
}
74-
7548
void CGame::setGLViewport()
7649
{
7750
glViewport(0, 0, GameResolution.x, GameResolution.y);
@@ -85,7 +58,6 @@ void CGame::setGLViewport()
8558
void CGame::UpdateDisplaySize(const Extent& newSize)
8659
{
8760
GameResolution = newSize;
88-
RecreateDisplayResources();
8961
for(auto& menu : Menus)
9062
menu->resetSurface();
9163
for(auto& wnd : Windows)

CGame_Render.cpp

Lines changed: 75 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,10 @@ void CGame::Render()
4444
// Ensure viewport and ortho projection match current window size (handles resize)
4545
setGLViewport();
4646

47-
// Clear the framebuffer and the software overlay surface.
48-
// GL draws (backgrounds) and the final Surf_Display overlay are
49-
// composited via blending.
47+
// Clear the framebuffer.
5048
glClear(GL_COLOR_BUFFER_BIT);
51-
SDL_FillRect(Surf_Display.get(), nullptr, SDL_MapRGBA(Surf_Display->format, 0, 0, 0, 0));
5249

53-
if(Extent(Surf_Display->w, Surf_Display->h) != GameResolution
54-
|| fullscreen != ((SDL_GetWindowFlags(window_.get()) & SDL_WINDOW_FULLSCREEN) != 0))
50+
if(fullscreen != ((SDL_GetWindowFlags(window_.get()) & SDL_WINDOW_FULLSCREEN) != 0))
5551
{
5652
ReCreateWindow();
5753
}
@@ -64,71 +60,75 @@ void CGame::Render()
6460
return;
6561
}
6662

67-
// render the map if active
68-
if(MapObj && MapObj->isActive())
69-
{
70-
CSurface::Draw(Surf_Display, MapObj->getSurface(), 0, 0);
71-
std::array<char, 100> textBuffer;
72-
// text for x and y of vertex (shown in upper left corner)
73-
std::snprintf(textBuffer.data(), textBuffer.size(), "%d %d", MapObj->getVertexX(), MapObj->getVertexY());
74-
CFont::writeText(Surf_Display, textBuffer.data(), Position(20, 20));
75-
// text for MinReduceHeight and MaxRaiseHeight
76-
std::snprintf(textBuffer.data(), textBuffer.size(),
77-
"min. height: %#04x/0x3C max. height: %#04x/0x3C NormalNull: 0x0A",
78-
MapObj->getMinReduceHeight(), MapObj->getMaxRaiseHeight());
79-
CFont::writeText(Surf_Display, textBuffer.data(), Position(100, 20));
80-
// text for MovementLocked
81-
if(MapObj->isHorizontalMovementLocked() && MapObj->isVerticalMovementLocked())
82-
CFont::writeText(Surf_Display, "Movement locked (F9 or F10 to unlock)", Position(20, 40), FontSize::Large,
83-
FontColor::Orange);
84-
else if(MapObj->isHorizontalMovementLocked())
85-
CFont::writeText(Surf_Display, "Horizontal movement locked (F9 to unlock)", Position(20, 40),
86-
FontSize::Large, FontColor::Orange);
87-
else if(MapObj->isVerticalMovementLocked())
88-
CFont::writeText(Surf_Display, "Vertical movement locked (F10 to unlock)", Position(20, 40),
89-
FontSize::Large, FontColor::Orange);
90-
}
91-
92-
// render active menus
63+
// ---- 1. Draw menu backgrounds via OpenGL ----
9364
for(auto& Menu : Menus)
9465
{
9566
if(Menu->isActive())
9667
{
97-
// Draw menu background via OpenGL
9868
int bgIdx = Menu->getBackground();
9969
(bgIdx == SPLASHSCREEN_MAINMENU ? menuBgMain_ : menuBgSub_)
10070
.DrawFull(Rect(0, 0, GameResolution.x, GameResolution.y));
101-
// Draw UI overlay on top
102-
CSurface::Draw(Surf_Display, Menu->getSurface(), 0, 0);
10371
}
10472
}
10573

106-
// render windows ordered by priority
74+
// ---- 2. Render map (software) and draw via GL ----
75+
// Map renders directly to its own Surf_Map; we upload that to GL.
76+
// Text overlays are written onto Surf_Map before upload.
77+
const bool mapActive = (MapObj && MapObj->isActive());
78+
if(mapActive)
79+
{
80+
auto* mapSurf = MapObj->getSurface();
81+
if(mapSurf)
82+
{
83+
// Write text overlays directly onto the map surface
84+
std::array<char, 100> textBuffer;
85+
std::snprintf(textBuffer.data(), textBuffer.size(), "%d %d", MapObj->getVertexX(), MapObj->getVertexY());
86+
CFont::writeText(mapSurf, textBuffer.data(), 20, 20);
87+
std::snprintf(textBuffer.data(), textBuffer.size(),
88+
"min. height: %#04x/0x3C max. height: %#04x/0x3C NormalNull: 0x0A",
89+
MapObj->getMinReduceHeight(), MapObj->getMaxRaiseHeight());
90+
CFont::writeText(mapSurf, textBuffer.data(), 100, 20);
91+
if(MapObj->isHorizontalMovementLocked() && MapObj->isVerticalMovementLocked())
92+
CFont::writeText(mapSurf, "Movement locked (F9 or F10 to unlock)", 20, 40, FontSize::Large,
93+
FontColor::Orange);
94+
else if(MapObj->isHorizontalMovementLocked())
95+
CFont::writeText(mapSurf, "Horizontal movement locked (F9 to unlock)", 20, 40, FontSize::Large,
96+
FontColor::Orange);
97+
else if(MapObj->isVerticalMovementLocked())
98+
CFont::writeText(mapSurf, "Vertical movement locked (F10 to unlock)", 20, 40, FontSize::Large,
99+
FontColor::Orange);
100+
101+
// Upload map surface — handles 8-bit paletted (classic) and 32-bit
102+
mapTex_.load(mapSurf);
103+
mapTex_.DrawFull(Rect(0, 0, GameResolution.x, GameResolution.y));
104+
}
105+
}
106+
107+
// ---- 3. Draw UI controls via OpenGL ----
108+
for(auto& Menu : Menus)
109+
{
110+
if(Menu->isActive())
111+
Menu->renderGL(0, 0);
112+
}
113+
114+
// render windows ordered by priority, lowest first
107115
int highestPriority = 0;
108-
// first find the highest priority
109116
for(auto& Window : Windows)
110117
{
111118
if(Window->getPriority() > highestPriority)
112119
highestPriority = Window->getPriority();
113120
}
114-
// render from lowest priority to highest
115121
for(int actualPriority = 0; actualPriority <= highestPriority; actualPriority++)
116122
{
117123
for(auto& Window : Windows)
118124
{
119125
if(Window->getPriority() == actualPriority)
120-
CSurface::Draw(Surf_Display, Window->getSurface(), Window->getX(), Window->getY());
126+
Window->renderGL(Window->getX(), Window->getY());
121127
}
122128
}
123129

124-
// Cursor is drawn in RenderPresent via GL (after the Surf_Display overlay)
125-
126-
#ifdef _ADMINMODE
127-
FrameCounter++;
128-
#endif
129-
130+
// ---- 4. Draw FPS counter via GL ----
130131
++framesPassedSinceLastFps;
131-
132132
const auto curTicks = SDL_GetTicks();
133133
const auto diffTicks = curTicks - lastFpsTick;
134134
if(diffTicks > 1000)
@@ -137,9 +137,36 @@ void CGame::Render()
137137
framesPassedSinceLastFps = 0;
138138
lastFpsTick = curTicks;
139139
}
140-
CSurface::Draw(Surf_Display, lastFps.getSurface(), 0, 0);
140+
{
141+
auto* fpsSurf = lastFps.getSurface();
142+
if(fpsSurf)
143+
{
144+
fpsTex_.load(fpsSurf);
145+
glBindTexture(GL_TEXTURE_2D, fpsTex_.getHandle());
146+
glBegin(GL_QUADS);
147+
glTexCoord2f(0, 0);
148+
glVertex2i(0, 0);
149+
glTexCoord2f(1, 0);
150+
glVertex2i(fpsSurf->w, 0);
151+
glTexCoord2f(1, 1);
152+
glVertex2i(fpsSurf->w, fpsSurf->h);
153+
glTexCoord2f(0, 1);
154+
glVertex2i(0, fpsSurf->h);
155+
glEnd();
156+
}
157+
}
141158

142-
RenderPresent();
159+
// ---- 5. Cursor on top of everything ----
160+
{
161+
const auto& cursorImg = Cursor.clicked ? (Cursor.button.right ? cross_ : cursorClicked_) : cursor_;
162+
cursorImg.Draw(Cursor.pos.x, Cursor.pos.y);
163+
}
164+
165+
SDL_GL_SwapWindow(window_.get());
166+
167+
#ifdef _ADMINMODE
168+
FrameCounter++;
169+
#endif
143170

144171
if(msWait)
145172
SDL_Delay(msWait);

CIO/CControlContainer.cpp

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "CSelectBox.h"
1313
#include "CTextfield.h"
1414
#include "helpers/containerUtils.h"
15+
#include <glad/glad.h>
1516

1617
CControlContainer::CControlContainer(int pic_background)
1718
: CControlContainer(pic_background, Extent::all(0), Extent::all(0))
@@ -203,3 +204,84 @@ void CControlContainer::renderElements()
203204
for(const auto& static_picture : static_pictures)
204205
CSurface::Draw(surface, global::bmpArray[static_picture.pic].surface, static_picture.pos);
205206
}
207+
208+
static void drawTexturedQuad(int x, int y, int w, int h)
209+
{
210+
glBegin(GL_QUADS);
211+
glTexCoord2f(0, 0);
212+
glVertex2i(x, y);
213+
glTexCoord2f(1, 0);
214+
glVertex2i(x + w, y);
215+
glTexCoord2f(1, 1);
216+
glVertex2i(x + w, y + h);
217+
glTexCoord2f(0, 1);
218+
glVertex2i(x, y + h);
219+
glEnd();
220+
}
221+
222+
void CControlContainer::renderGL(int baseX, int baseY)
223+
{
224+
for(const auto& picture : pictures)
225+
{
226+
auto* surf = picture->getSurface();
227+
if(!surf)
228+
continue;
229+
auto& entry = childTexCache_[surf];
230+
entry.tex.load(surf);
231+
glBindTexture(GL_TEXTURE_2D, entry.tex.getHandle());
232+
drawTexturedQuad(baseX + picture->getX(), baseY + picture->getY(), entry.tex.getWidth(), entry.tex.getHeight());
233+
}
234+
for(const auto& text : texts)
235+
{
236+
auto* surf = text->getSurface();
237+
if(!surf)
238+
continue;
239+
auto& entry = childTexCache_[surf];
240+
entry.tex.load(surf);
241+
glBindTexture(GL_TEXTURE_2D, entry.tex.getHandle());
242+
drawTexturedQuad(baseX + text->getX(), baseY + text->getY(), entry.tex.getWidth(), entry.tex.getHeight());
243+
}
244+
for(const auto& textfield : textfields)
245+
{
246+
auto* surf = textfield->getSurface();
247+
if(!surf)
248+
continue;
249+
auto& entry = childTexCache_[surf];
250+
entry.tex.load(surf);
251+
glBindTexture(GL_TEXTURE_2D, entry.tex.getHandle());
252+
drawTexturedQuad(baseX + textfield->getX(), baseY + textfield->getY(), entry.tex.getWidth(),
253+
entry.tex.getHeight());
254+
}
255+
for(const auto& selectbox : selectboxes)
256+
{
257+
SDL_Surface* surf = selectbox->getSurface().get();
258+
if(!surf)
259+
continue;
260+
auto& entry = childTexCache_[surf];
261+
entry.tex.load(surf);
262+
glBindTexture(GL_TEXTURE_2D, entry.tex.getHandle());
263+
const auto& pos = selectbox->getPos();
264+
drawTexturedQuad(baseX + pos.x, baseY + pos.y, entry.tex.getWidth(), entry.tex.getHeight());
265+
}
266+
for(const auto& button : buttons)
267+
{
268+
auto* surf = button->getSurface();
269+
if(!surf)
270+
continue;
271+
auto& entry = childTexCache_[surf];
272+
entry.tex.load(surf);
273+
glBindTexture(GL_TEXTURE_2D, entry.tex.getHandle());
274+
drawTexturedQuad(baseX + button->getX(), baseY + button->getY(), entry.tex.getWidth(), entry.tex.getHeight());
275+
}
276+
for(const auto& static_picture : static_pictures)
277+
{
278+
auto* srcSurf = global::bmpArray[static_picture.pic].surface.get();
279+
if(!srcSurf)
280+
continue;
281+
auto& entry = staticTexCache_[static_picture.pic];
282+
entry.load(srcSurf);
283+
glBindTexture(GL_TEXTURE_2D, entry.getHandle());
284+
drawTexturedQuad(baseX + static_picture.pos.x, baseY + static_picture.pos.y, entry.getWidth(),
285+
entry.getHeight());
286+
}
287+
}

0 commit comments

Comments
 (0)