Skip to content

Commit

Permalink
Fixes #4720: Display scaling, live window resizing, and high-DPI support
Browse files Browse the repository at this point in the history
- Internal enhancements to support screen / game / rendering scaling factors.
- Internal enhancements to support live window resizing / resolution changes.
- Internal enhancements to support UI layout recalculation.
- [SDL backend] Support for live window resizing / resolution changes. (*Cannot currently change between Windowed / Fullscreen without restarting Warzone because of SDL quirks, but live resolution changes in the current mode are supported.)
- [SDL backend] New "displayScale" variable for config file (can also be set as "Display Scale" in video options), offering 100%-500%* display scaling options (*scaling options are limited by the window/screen size, as no combination can result in a logical screen size lower than the minimum supported by Warzone).
- [macOS] Automatic high-DPI ("retina") display support. (Currently limited to macOS because of SDL 2.0.x high-DPI support limitations.)
- Better caching of text rasterization (through judicious use of the improved WzText).
  • Loading branch information
past-due authored and perim committed Feb 11, 2018
1 parent ead89ac commit 619e84d
Show file tree
Hide file tree
Showing 70 changed files with 2,753 additions and 487 deletions.
2 changes: 1 addition & 1 deletion configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ fi
# Checks for modules:

if test "$backend" = "sdl"; then
PKG_CHECK_MODULES([SDL], [sdl2 >= 2.0.0])
PKG_CHECK_MODULES([SDL], [sdl2 >= 2.0.5])
fi
PKG_CHECK_MODULES([PNG], [libpng >= 1.2])
PKG_CHECK_MODULES([THEORA], [theora >= 1.0])
Expand Down
12 changes: 11 additions & 1 deletion lib/framework/wzapp.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,21 @@ struct screeninfo
};

void wzMain(int &argc, char **argv);
bool wzMainScreenSetup(int antialiasing = 0, bool fullscreen = false, bool vsync = true);
bool wzMainScreenSetup(int antialiasing = 0, bool fullscreen = false, bool vsync = true, bool highDPI = true);
void wzGetGameToRendererScaleFactor(float *horizScaleFactor, float *vertScaleFactor);
void wzMainEventLoop();
void wzQuit(); ///< Quit game
void wzShutdown();
void wzToggleFullscreen();
bool wzIsFullscreen();
void wzSetWindowIsResizable(bool resizable);
bool wzIsWindowResizable();
bool wzSupportsLiveResolutionChanges();
bool wzChangeDisplayScale(unsigned int displayScale);
bool wzChangeWindowResolution(int screen, unsigned int width, unsigned int height);
unsigned int wzGetMaximumDisplayScaleForWindowSize(unsigned int windowWidth, unsigned int windowHeight);
unsigned int wzGetCurrentDisplayScale();
void wzGetWindowResolution(int *screen, unsigned int *width, unsigned int *height);
void wzSetCursor(CURSOR index);
void wzScreenFlip(); ///< Swap the graphics buffers
void wzShowMouse(bool visible); ///< Show the Mouse?
Expand All @@ -53,6 +62,7 @@ int wzGetTicks(); ///< Milliseconds since start of game
WZ_DECL_NONNULL(1) void wzFatalDialog(const char *text); ///< Throw up a modal warning dialog

std::vector<screeninfo> wzAvailableResolutions();
std::vector<unsigned int> wzAvailableDisplayScales();
void wzSetSwapInterval(int swap);
int wzGetSwapInterval();
QString wzGetSelection();
Expand Down
27 changes: 23 additions & 4 deletions lib/ivis_opengl/bitimage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,23 @@ ImageDef *iV_GetImage(const QString &filename)
return images.value(filename);
}

// Used to provide empty space between sprites arranged on the page, to avoid display glitches when scaling + drawing
#define SPRITE_BUFFER_PIXELS 1

void checkRect(const ImageMergeRectangle& rect, const int pageSize)
{
if ((rect.loc.x + rect.siz.x) > pageSize)
{
debug(LOG_ERROR, "Merge rectangle bounds extend outside of pageSize bounds: %d > %d", (rect.loc.x + rect.siz.x), pageSize);
}
if ((rect.loc.y + rect.siz.y) > pageSize)
{
debug(LOG_ERROR, "Merge rectangle bounds extend outside of pageSize bounds: %d > %d", (rect.loc.y + rect.siz.y), pageSize);
}
assert(rect.loc.x >= 0);
assert(rect.loc.y >= 0);
}

inline void ImageMerge::arrange()
{
std::multiset<ImageMergeRectangle> freeSpace;
Expand Down Expand Up @@ -112,10 +129,10 @@ inline void ImageMerge::arrange()
ImageMergeRectangle spDown;
spRight.page = f->page;
spDown.page = f->page;
spRight.loc = f->loc + Vector2i(r->siz.x, 0);
spDown.loc = f->loc + Vector2i(0, r->siz.y);
spRight.siz = Vector2i(f->siz.x - r->siz.x, r->siz.y);
spDown.siz = Vector2i(r->siz.x, f->siz.y - r->siz.y);
spRight.loc = f->loc + Vector2i(r->siz.x + SPRITE_BUFFER_PIXELS, 0);
spDown.loc = f->loc + Vector2i(0, r->siz.y + SPRITE_BUFFER_PIXELS);
spRight.siz = Vector2i(f->siz.x - (r->siz.x + SPRITE_BUFFER_PIXELS), r->siz.y);
spDown.siz = Vector2i(r->siz.x, f->siz.y - (r->siz.y + SPRITE_BUFFER_PIXELS));
if (spRight.siz.x <= spDown.siz.y)
{
// Split horizontally.
Expand All @@ -126,6 +143,8 @@ inline void ImageMerge::arrange()
// Split vertically.
spRight.siz.y = f->siz.y;
}
checkRect(spDown, pages.at(spDown.page));
checkRect(spRight, pages.at(spRight.page));
if (spRight.siz.x > 0 && spRight.siz.y > 0)
{
freeSpace.insert(spRight);
Expand Down
53 changes: 32 additions & 21 deletions lib/ivis_opengl/pieblitfunc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -340,22 +340,22 @@ static bool assertValidImage(IMAGEFILE *imageFile, unsigned id)
return true;
}

static void iv_DrawImageImpl(Vector2i offset, Vector2i size, Vector2f TextureUV, Vector2f TextureSize, PIELIGHT colour, const glm::mat4 &modelViewProjection, SHADER_MODE mode = SHADER_TEXRECT)
static void iv_DrawImageImpl(Vector2f offset, Vector2f size, Vector2f TextureUV, Vector2f TextureSize, PIELIGHT colour, const glm::mat4 &modelViewProjection, SHADER_MODE mode = SHADER_TEXRECT)
{
glm::mat4 transformMat = modelViewProjection * glm::translate(offset.x, offset.y, 0) * glm::scale(size.x, size.y, 1);
glm::mat4 transformMat = modelViewProjection * glm::translate(offset.x, offset.y, 0.f) * glm::scale(size.x, size.y, 1.f);

pie_ActivateShader(mode,
transformMat,
Vector2f(TextureUV.x, TextureUV.y),
Vector2f(TextureSize.x, TextureSize.y),
TextureUV,
TextureSize,
glm::vec4(colour.vector[0] / 255.f, colour.vector[1] / 255.f, colour.vector[2] / 255.f, colour.vector[3] / 255.f), 0);
enableRect();
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
disableRect();
pie_DeactivateShader();
}

void iV_DrawImage(GLuint TextureID, Vector2i Position, Vector2i offset, Vector2i size, float angle, REND_MODE mode, PIELIGHT colour)
void iV_DrawImage(GLuint TextureID, Vector2i Position, Vector2f offset, Vector2f size, float angle, REND_MODE mode, PIELIGHT colour)
{
pie_SetRendMode(mode);
pie_SetTexturePage(TEXPAGE_EXTERN);
Expand All @@ -366,7 +366,7 @@ void iV_DrawImage(GLuint TextureID, Vector2i Position, Vector2i offset, Vector2i
iv_DrawImageImpl(offset, size, Vector2f(0.f, 0.f), Vector2f(1.f, 1.f), colour, mvp);
}

void iV_DrawImageText(gfx_api::texture& TextureID, Vector2i Position, Vector2i offset, Vector2i size, float angle, REND_MODE mode, PIELIGHT colour)
void iV_DrawImageText(gfx_api::texture& TextureID, Vector2i Position, Vector2f offset, Vector2f size, float angle, REND_MODE mode, PIELIGHT colour)
{
pie_SetRendMode(mode);
pie_SetTexturePage(TEXPAGE_EXTERN);
Expand All @@ -377,20 +377,20 @@ void iV_DrawImageText(gfx_api::texture& TextureID, Vector2i Position, Vector2i o
iv_DrawImageImpl(offset, size, Vector2f(0.f, 0.f), Vector2f(1.f, 1.f), colour, mvp, SHADER_TEXT);
}

static void pie_DrawImage(IMAGEFILE *imageFile, int id, Vector2i size, const PIERECT *dest, PIELIGHT colour, const glm::mat4 &modelViewProjection)
static void pie_DrawImage(IMAGEFILE *imageFile, int id, Vector2i size, const PIERECT *dest, PIELIGHT colour, const glm::mat4 &modelViewProjection, Vector2i textureInset = Vector2i(0, 0))
{
ImageDef const &image2 = imageFile->imageDefs[id];
GLuint texPage = imageFile->pages[image2.TPageID].id;
GLfloat invTextureSize = 1.f / imageFile->pages[image2.TPageID].size;
float tu = image2.Tu * invTextureSize;
float tv = image2.Tv * invTextureSize;
float su = size.x * invTextureSize;
float sv = size.y * invTextureSize;
GLfloat invTextureSize = 1.f / (float)imageFile->pages[image2.TPageID].size;
float tu = (float)(image2.Tu + textureInset.x) * invTextureSize;
float tv = (float)(image2.Tv + textureInset.y) * invTextureSize;
float su = (float)(size.x - (textureInset.x * 2)) * invTextureSize;
float sv = (float)(size.y - (textureInset.y * 2)) * invTextureSize;

glm::mat4 mvp = modelViewProjection * glm::translate(dest->x, dest->y, 0);
glm::mat4 mvp = modelViewProjection * glm::translate((float)dest->x, (float)dest->y, 0.f);

pie_SetTexturePage(texPage);
iv_DrawImageImpl(Vector2i(0, 0), Vector2i(dest->w, dest->h), Vector2f(tu, tv), Vector2f(su, sv), colour, mvp);
iv_DrawImageImpl(Vector2f(0.f, 0.f), Vector2f(dest->w, dest->h), Vector2f(tu, tv), Vector2f(su, sv), colour, mvp);
}

static Vector2i makePieImage(IMAGEFILE *imageFile, unsigned id, PIERECT *dest, int x, int y)
Expand Down Expand Up @@ -423,7 +423,7 @@ void iV_DrawImage2(const QString &filename, float x, float y, float width, float
pie_SetRendMode(REND_ALPHA);

glm::mat4 mvp = defaultProjectionMatrix() * glm::translate(x, y, 0);
iv_DrawImageImpl(Vector2i(0, 0), Vector2i(w, h),
iv_DrawImageImpl(Vector2f(0.f, 0.f), Vector2f(w, h),
Vector2f(tu * invTextureSize, tv * invTextureSize),
Vector2f(image->Width * invTextureSize, image->Height * invTextureSize),
WZCOL_WHITE, mvp);
Expand Down Expand Up @@ -462,7 +462,7 @@ void iV_DrawImageTc(Image image, Image imageTc, int x, int y, PIELIGHT colour, c
}

// Repeat a texture
void iV_DrawImageRepeatX(IMAGEFILE *ImageFile, UWORD ID, int x, int y, int Width, const glm::mat4 &modelViewProjection)
void iV_DrawImageRepeatX(IMAGEFILE *ImageFile, UWORD ID, int x, int y, int Width, const glm::mat4 &modelViewProjection, bool enableHorizontalTilingSeamWorkaround)
{
assertValidImage(ImageFile, ID);
const ImageDef *Image = &ImageFile->imageDefs[ID];
Expand All @@ -472,20 +472,31 @@ void iV_DrawImageRepeatX(IMAGEFILE *ImageFile, UWORD ID, int x, int y, int Width
PIERECT dest;
Vector2i pieImage = makePieImage(ImageFile, ID, &dest, x, y);

unsigned hRemainder = Width % Image->Width;
unsigned int usableImageWidth = Image->Width;
unsigned int imageXInset = 0;
if (enableHorizontalTilingSeamWorkaround)
{
// Inset the portion of the image that is used by 1 on both the left + right sides
usableImageWidth -= 2;
imageXInset = 1;
dest.w -= 2;
}
assert(usableImageWidth > 0);

unsigned hRemainder = (Width % usableImageWidth);

for (unsigned hRep = 0; hRep < Width / Image->Width; hRep++)
for (unsigned hRep = 0; hRep < Width / usableImageWidth; hRep++)
{
pie_DrawImage(ImageFile, ID, pieImage, &dest, WZCOL_WHITE, modelViewProjection);
dest.x += Image->Width;
pie_DrawImage(ImageFile, ID, pieImage, &dest, WZCOL_WHITE, modelViewProjection, Vector2i(imageXInset, 0));
dest.x += usableImageWidth;
}

// draw remainder
if (hRemainder > 0)
{
pieImage.x = hRemainder;
dest.w = hRemainder;
pie_DrawImage(ImageFile, ID, pieImage, &dest, WZCOL_WHITE, modelViewProjection);
pie_DrawImage(ImageFile, ID, pieImage, &dest, WZCOL_WHITE, modelViewProjection, Vector2i(imageXInset, 0));
}
}

Expand Down
6 changes: 3 additions & 3 deletions lib/ivis_opengl/pieblitfunc.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,12 +120,12 @@ static inline void iV_Box(int x0, int y0, int x1, int y1, PIELIGHT first)
iV_Box2(x0, y0, x1, y1, first, first);
}
void pie_BoxFill(int x0, int y0, int x1, int y1, PIELIGHT colour, REND_MODE rendermode = REND_OPAQUE);
void iV_DrawImage(GLuint TextureID, Vector2i position, Vector2i offset, Vector2i size, float angle, REND_MODE mode, PIELIGHT colour);
void iV_DrawImageText(gfx_api::texture& TextureID, Vector2i position, Vector2i offset, Vector2i size, float angle, REND_MODE mode, PIELIGHT colour);
void iV_DrawImage(GLuint TextureID, Vector2i position, Vector2f offset, Vector2i size, float angle, REND_MODE mode, PIELIGHT colour);
void iV_DrawImageText(gfx_api::texture& TextureID, Vector2i Position, Vector2f offset, Vector2f size, float angle, REND_MODE mode, PIELIGHT colour);
void iV_DrawImage(IMAGEFILE *ImageFile, UWORD ID, int x, int y, const glm::mat4 &modelViewProjection = defaultProjectionMatrix());
void iV_DrawImage2(const QString &filename, float x, float y, float width = -0.0f, float height = -0.0f);
void iV_DrawImageTc(Image image, Image imageTc, int x, int y, PIELIGHT colour, const glm::mat4 &modelViewProjection = defaultProjectionMatrix());
void iV_DrawImageRepeatX(IMAGEFILE *ImageFile, UWORD ID, int x, int y, int Width, const glm::mat4 &modelViewProjection = defaultProjectionMatrix());
void iV_DrawImageRepeatX(IMAGEFILE *ImageFile, UWORD ID, int x, int y, int Width, const glm::mat4 &modelViewProjection = defaultProjectionMatrix(), bool enableHorizontalTilingSeamWorkaround = false);
void iV_DrawImageRepeatY(IMAGEFILE *ImageFile, UWORD ID, int x, int y, int Height, const glm::mat4 &modelViewProjection = defaultProjectionMatrix());

static inline void iV_DrawImage(Image image, int x, int y)
Expand Down
2 changes: 1 addition & 1 deletion lib/ivis_opengl/piedef.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ struct iIMDShape;
* Global ProtoTypes
*/
/***************************************************************************/
void pie_Draw3DShape(iIMDShape *shape, int frame, int team, PIELIGHT colour, int pieFlag, int pieFlagData, const glm::mat4 &modelView);
bool pie_Draw3DShape(iIMDShape *shape, int frame, int team, PIELIGHT colour, int pieFlag, int pieFlagData, const glm::mat4 &modelView);

void pie_GetResetCounts(unsigned int *pPieCount, unsigned int *pPolyCount);

Expand Down
4 changes: 3 additions & 1 deletion lib/ivis_opengl/piedraw.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@ void pie_CleanUp()
scshapes.clear();
}

void pie_Draw3DShape(iIMDShape *shape, int frame, int team, PIELIGHT colour, int pieFlag, int pieFlagData, const glm::mat4 &modelView)
bool pie_Draw3DShape(iIMDShape *shape, int frame, int team, PIELIGHT colour, int pieFlag, int pieFlagData, const glm::mat4 &modelView)
{
pieCount++;

Expand Down Expand Up @@ -493,6 +493,8 @@ void pie_Draw3DShape(iIMDShape *shape, int frame, int team, PIELIGHT colour, int
shapes.push_back(tshape);
}
}

return true;
}

static void pie_ShadowDrawLoop()
Expand Down
21 changes: 13 additions & 8 deletions lib/ivis_opengl/piemode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,18 @@

iSurface rendSurface;

void pie_UpdateSurfaceGeometry()
{
rendSurface.width = pie_GetVideoBufferWidth();
rendSurface.height = pie_GetVideoBufferHeight();
rendSurface.xcentre = pie_GetVideoBufferWidth() / 2;
rendSurface.ycentre = pie_GetVideoBufferHeight() / 2;
rendSurface.clip.left = 0;
rendSurface.clip.top = 0;
rendSurface.clip.right = pie_GetVideoBufferWidth();
rendSurface.clip.bottom = pie_GetVideoBufferHeight();
}

bool pie_Initialise()
{
pie_SetUp();
Expand All @@ -62,14 +74,7 @@ bool pie_Initialise()
debug(LOG_TEXTURE, "Texture compression: No");
}

rendSurface.width = pie_GetVideoBufferWidth();
rendSurface.height = pie_GetVideoBufferHeight();
rendSurface.xcentre = pie_GetVideoBufferWidth() / 2;
rendSurface.ycentre = pie_GetVideoBufferHeight() / 2;
rendSurface.clip.left = 0;
rendSurface.clip.top = 0;
rendSurface.clip.right = pie_GetVideoBufferWidth();
rendSurface.clip.bottom = pie_GetVideoBufferHeight();
pie_UpdateSurfaceGeometry();

pie_SetDefaultStates();
debug(LOG_3D, "xcentre %d; ycentre %d", rendSurface.xcentre, rendSurface.ycentre);
Expand Down
1 change: 1 addition & 0 deletions lib/ivis_opengl/piemode.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ extern iSurface rendSurface;
bool pie_Initialise();
void pie_ShutDown();
void pie_ScreenFlip(int ClearMode);
void pie_UpdateSurfaceGeometry();
UDWORD pie_GetResScalingFactor();

#endif
47 changes: 36 additions & 11 deletions lib/ivis_opengl/screen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ static char screendump_filename[PATH_MAX];
static bool screendump_required = false;

static GFX *backdropGfx = nullptr;
static bool backdropIsMapPreview = false;

static bool perfStarted = false;
static GLuint perfpos[PERF_COUNT];
Expand All @@ -88,6 +89,7 @@ static PERF_POINT queryActive = PERF_COUNT;

static int preview_width = 0, preview_height = 0;
static Vector2i player_pos[MAX_PLAYERS];
static WzText player_Text[MAX_PLAYERS];
static bool mappreview = false;
OPENGL_DATA opengl;
static bool khr_debug = false;
Expand Down Expand Up @@ -500,13 +502,12 @@ void screen_Display()
x = screenWidth / 2 - w / 2 + x * scale;
y = screenHeight / 2 - h / 2 + y * scale;
ssprintf(text, "%d", i);
iV_SetTextColour(WZCOL_BLACK);
iV_DrawText(text, x - 1, y - 1, font_large);
iV_DrawText(text, x + 1, y - 1, font_large);
iV_DrawText(text, x - 1, y + 1, font_large);
iV_DrawText(text, x + 1, y + 1, font_large);
iV_SetTextColour(WZCOL_WHITE);
iV_DrawText(text, x, y, font_large);
player_Text[i].setText(text, font_large);
player_Text[i].render(x - 1, y - 1, WZCOL_BLACK);
player_Text[i].render(x + 1, y - 1, WZCOL_BLACK);
player_Text[i].render(x - 1, y + 1, WZCOL_BLACK);
player_Text[i].render(x + 1, y + 1, WZCOL_BLACK);
player_Text[i].render(x, y, WZCOL_WHITE);
}
}
pie_SetDepthBufferStatus(DEPTH_CMP_LEQ_WRT_ON);
Expand All @@ -515,8 +516,10 @@ void screen_Display()
//******************************************************************
//slight hack to display maps (or whatever) in background.
//bitmap MUST be (BACKDROP_HACK_WIDTH * BACKDROP_HACK_HEIGHT) for now.
void screen_Upload(const char *newBackDropBmp)
void screen_GenerateCoordinatesAndVBOs()
{
assert(backdropGfx != nullptr);

GLfloat x1 = 0, x2 = screenWidth, y1 = 0, y2 = screenHeight;
GLfloat tx = 1, ty = 1;
int scale = 0, w = 0, h = 0;
Expand All @@ -535,10 +538,8 @@ void screen_Upload(const char *newBackDropBmp)
y2 -= offset;
}

if (newBackDropBmp) // preview
if (backdropIsMapPreview) // preview
{
backdropGfx->makeTexture(BACKDROP_HACK_WIDTH, BACKDROP_HACK_HEIGHT, GL_NEAREST, gfx_api::pixel_format::rgb, newBackDropBmp);

int s1 = screenWidth / preview_width;
int s2 = screenHeight / preview_height;
scale = MIN(s1, s2);
Expand All @@ -560,6 +561,30 @@ void screen_Upload(const char *newBackDropBmp)
backdropGfx->buffers(4, vertices, texcoords);
}

void screen_Upload(const char *newBackDropBmp)
{
backdropIsMapPreview = false;

if (newBackDropBmp) // preview
{
// Slight hack to display maps previews in background.
// Bitmap MUST be (BACKDROP_HACK_WIDTH * BACKDROP_HACK_HEIGHT) for now.
backdropGfx->makeTexture(BACKDROP_HACK_WIDTH, BACKDROP_HACK_HEIGHT, GL_NEAREST, gfx_api::pixel_format::rgb, newBackDropBmp);
backdropIsMapPreview = true;
}

// Generate coordinates and put them into VBOs
screen_GenerateCoordinatesAndVBOs();
}

void screen_updateGeometry()
{
if (backdropGfx != nullptr)
{
screen_GenerateCoordinatesAndVBOs();
}
}

void screen_enableMapPreview(int width, int height, Vector2i *playerpositions)
{
int i;
Expand Down
2 changes: 2 additions & 0 deletions lib/ivis_opengl/screen.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ void screenDoDumpToDiskIfRequired();
void screen_enableMapPreview(int width, int height, Vector2i *playerpositions);
void screen_disableMapPreview();

void screen_updateGeometry();

/// graphics performance measurement points
enum PERF_POINT
{
Expand Down
Loading

0 comments on commit 619e84d

Please sign in to comment.