diff --git a/src/engine/client/backend_sdl.cpp b/src/engine/client/backend_sdl.cpp index 190f11835..48c8bc705 100644 --- a/src/engine/client/backend_sdl.cpp +++ b/src/engine/client/backend_sdl.cpp @@ -706,11 +706,31 @@ void CCommandProcessorFragment_OpenGL::Cmd_Texture_Create(const CCommandBuffer:: p3DImageData = (uint8_t*)malloc(ImageColorChannels * Width * Height); int Image3DWidth, Image3DHeight; - if(IsSingleLayer || (Width != 0 && Width % 16 == 0 && Height != 0 && Height % 16 == 0 && Texture2DTo3D(pTexData, Width, Height, ImageColorChannels, 16, 16, p3DImageData, Image3DWidth, Image3DHeight))) + int ConvertWidth = Width; + int ConvertHeight = Height; + + if(!IsSingleLayer) + { + if(ConvertWidth == 0 || (ConvertWidth % 16) != 0 || ConvertHeight == 0 || (ConvertHeight % 16) != 0) + { + dbg_msg("gfx", "3D/2D array texture was resized"); + int NewWidth = maximum(HighestBit(ConvertWidth), 16); + int NewHeight = maximum(HighestBit(ConvertHeight), 16); + uint8_t* pNewTexData = (uint8_t*)Resize(ConvertWidth, ConvertHeight, NewWidth, NewHeight, pCommand->m_Format, (const uint8_t*)pTexData); + + ConvertWidth = NewWidth; + ConvertHeight = NewHeight; + + free(pTexData); + pTexData = pNewTexData; + } + } + + if(IsSingleLayer || (Texture2DTo3D(pTexData, ConvertWidth, ConvertHeight, ImageColorChannels, 16, 16, p3DImageData, Image3DWidth, Image3DHeight))) { if(IsSingleLayer) { - glTexImage3D(Target, 0, StoreOglformat, Width, Height, 1, 0, Oglformat, GL_UNSIGNED_BYTE, pTexData); + glTexImage3D(Target, 0, StoreOglformat, ConvertWidth, ConvertHeight, 1, 0, Oglformat, GL_UNSIGNED_BYTE, pTexData); } else { @@ -2253,11 +2273,31 @@ void CCommandProcessorFragment_OpenGL3_3::Cmd_Texture_Create(const CCommandBuffe p3DImageData = (uint8_t*)malloc(ImageColorChannels * Width * Height); int Image3DWidth, Image3DHeight; - if(IsSingleLayer || (Width != 0 && Width % 16 == 0 && Height != 0 && Height % 16 == 0 && Texture2DTo3D(pTexData, Width, Height, ImageColorChannels, 16, 16, p3DImageData, Image3DWidth, Image3DHeight))) + int ConvertWidth = Width; + int ConvertHeight = Height; + + if(!IsSingleLayer) + { + if(ConvertWidth == 0 || (ConvertWidth % 16) != 0 || ConvertHeight == 0 || (ConvertHeight % 16) != 0) + { + dbg_msg("gfx", "3D/2D array texture was resized"); + int NewWidth = maximum(HighestBit(ConvertWidth), 16); + int NewHeight = maximum(HighestBit(ConvertHeight), 16); + uint8_t* pNewTexData = (uint8_t*)Resize(ConvertWidth, ConvertHeight, NewWidth, NewHeight, pCommand->m_Format, (const uint8_t*)pTexData); + + ConvertWidth = NewWidth; + ConvertHeight = NewHeight; + + free(pTexData); + pTexData = pNewTexData; + } + } + + if(IsSingleLayer || (Texture2DTo3D(pTexData, ConvertWidth, ConvertHeight, ImageColorChannels, 16, 16, p3DImageData, Image3DWidth, Image3DHeight))) { if(IsSingleLayer) { - glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, StoreOglformat, Width, Height, 1, 0, Oglformat, GL_UNSIGNED_BYTE, pTexData); + glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, StoreOglformat, ConvertWidth, ConvertHeight, 1, 0, Oglformat, GL_UNSIGNED_BYTE, pTexData); } else { diff --git a/src/engine/client/graphics_threaded.cpp b/src/engine/client/graphics_threaded.cpp index 46bcc16a3..257bec741 100644 --- a/src/engine/client/graphics_threaded.cpp +++ b/src/engine/client/graphics_threaded.cpp @@ -408,7 +408,7 @@ int CGraphics_Threaded::LoadTextureRawSub(CTextureHandle TextureID, int x, int y return 0; } -IGraphics::CTextureHandle CGraphics_Threaded::LoadTextureRaw(int Width, int Height, int Format, const void *pData, int StoreFormat, int Flags) +IGraphics::CTextureHandle CGraphics_Threaded::LoadTextureRaw(int Width, int Height, int Format, const void *pData, int StoreFormat, int Flags, const char *pTexName) { // don't waste memory on texture if we are stress testing #ifdef CONF_DEBUG @@ -448,6 +448,27 @@ IGraphics::CTextureHandle CGraphics_Threaded::LoadTextureRaw(int Width, int Heig if((Flags&IGraphics::TEXLOAD_NO_2D_TEXTURE) != 0) Cmd.m_Flags |= CCommandBuffer::TEXFLAG_NO_2D_TEXTURE; + if((Flags&IGraphics::TEXLOAD_TO_2D_ARRAY_TEXTURE) != 0 || (Flags&IGraphics::TEXLOAD_TO_3D_TEXTURE) != 0) + { + if(Width == 0 || (Width % 16) != 0 || Height == 0 || (Height % 16) != 0) + { + SGraphicsWarning NewWarning; + char aText[128]; + if(pTexName && *pTexName) + { + str_format(aText, sizeof(aText), ":\n\"%s\"\n", pTexName); + } + else + { + aText[0] = ' '; + aText[1] = 0; + } + str_format(NewWarning.m_aWarningMsg, sizeof(NewWarning.m_aWarningMsg), "The width and height of texture%sare not divisible by 16, which might cause visual bugs.", aText); + + m_Warnings.emplace_back(NewWarning); + } + } + // copy texture data int MemSize = Width*Height*Cmd.m_PixelSize; void *pTmpData = malloc(MemSize); @@ -479,7 +500,7 @@ IGraphics::CTextureHandle CGraphics_Threaded::LoadTexture(const char *pFilename, if(StoreFormat == CImageInfo::FORMAT_AUTO) StoreFormat = Img.m_Format; - ID = LoadTextureRaw(Img.m_Width, Img.m_Height, Img.m_Format, Img.m_pData, StoreFormat, Flags); + ID = LoadTextureRaw(Img.m_Width, Img.m_Height, Img.m_Format, Img.m_pData, StoreFormat, Flags, pFilename); free(Img.m_pData); if(ID != m_InvalidTexture && g_Config.m_Debug) dbg_msg("graphics/texture", "loaded %s", pFilename); @@ -2318,6 +2339,15 @@ void CGraphics_Threaded::TakeCustomScreenshot(const char *pFilename) void CGraphics_Threaded::Swap() { + if(!m_Warnings.empty()) + { + SGraphicsWarning* pCurWarning = GetCurWarning(); + if(pCurWarning->m_WasShown) + { + m_Warnings.erase(m_Warnings.begin()); + } + } + // TODO: screenshot support if(m_DoScreenshot) { @@ -2371,6 +2401,17 @@ void CGraphics_Threaded::WaitForIdle() m_pBackend->WaitForIdle(); } +SGraphicsWarning *CGraphics_Threaded::GetCurWarning() +{ + if(m_Warnings.empty()) + return NULL; + else + { + SGraphicsWarning* pCurWarning = &m_Warnings[0]; + return pCurWarning; + } +} + int CGraphics_Threaded::GetVideoModes(CVideoMode *pModes, int MaxModes, int Screen) { if(g_Config.m_GfxDisplayAllModes) diff --git a/src/engine/client/graphics_threaded.h b/src/engine/client/graphics_threaded.h index 576b1d82d..d0779b0af 100644 --- a/src/engine/client/graphics_threaded.h +++ b/src/engine/client/graphics_threaded.h @@ -663,6 +663,8 @@ class CGraphics_Threaded : public IEngineGraphics int m_FirstFreeTexture; int m_TextureMemoryUsage; + std::vector m_Warnings; + struct SVertexArrayInfo { SVertexArrayInfo() : m_FreeIndex(-1) {} @@ -742,7 +744,7 @@ public: virtual void LinesDraw(const CLineItem *pArray, int Num); virtual int UnloadTexture(IGraphics::CTextureHandle Index); - virtual IGraphics::CTextureHandle LoadTextureRaw(int Width, int Height, int Format, const void *pData, int StoreFormat, int Flags); + virtual IGraphics::CTextureHandle LoadTextureRaw(int Width, int Height, int Format, const void *pData, int StoreFormat, int Flags, const char *pTexName = NULL); virtual int LoadTextureRawSub(IGraphics::CTextureHandle TextureID, int x, int y, int Width, int Height, int Format, const void *pData); // simple uncompressed RGBA loaders @@ -851,6 +853,8 @@ public: virtual bool IsIdle(); virtual void WaitForIdle(); + virtual SGraphicsWarning *GetCurWarning(); + virtual bool IsTileBufferingEnabled() { return m_OpenGLTileBufferingEnabled; } virtual bool IsQuadBufferingEnabled() { return m_OpenGLQuadBufferingEnabled; } virtual bool IsTextBufferingEnabled() { return m_OpenGLTextBufferingEnabled; } diff --git a/src/engine/graphics.h b/src/engine/graphics.h index abc5a5ca0..5a19047c9 100644 --- a/src/engine/graphics.h +++ b/src/engine/graphics.h @@ -6,6 +6,7 @@ #include "kernel.h" #include +#include #include #define GRAPHICS_TYPE_UNSIGNED_BYTE 0x1401 @@ -116,6 +117,13 @@ struct GL_SVertexTex3D GL_STexCoord3D m_Tex; }; +struct SGraphicsWarning +{ + SGraphicsWarning() : m_WasShown(false) {} + char m_aWarningMsg[128]; + bool m_WasShown; +}; + typedef void(*WINDOW_RESIZE_FUNC)(void *pUser); class IGraphics : public IInterface @@ -186,7 +194,7 @@ public: virtual int LoadPNG(CImageInfo *pImg, const char *pFilename, int StorageType) = 0; virtual int UnloadTexture(CTextureHandle Index) = 0; - virtual CTextureHandle LoadTextureRaw(int Width, int Height, int Format, const void *pData, int StoreFormat, int Flags) = 0; + virtual CTextureHandle LoadTextureRaw(int Width, int Height, int Format, const void *pData, int StoreFormat, int Flags, const char *pTexName = NULL) = 0; virtual int LoadTextureRawSub(CTextureHandle TextureID, int x, int y, int Width, int Height, int Format, const void *pData) = 0; virtual CTextureHandle LoadTexture(const char *pFilename, int StorageType, int StoreFormat, int Flags) = 0; virtual void TextureSet(CTextureHandle Texture) = 0; @@ -309,6 +317,7 @@ public: virtual void SetWindowGrab(bool Grab) = 0; virtual void NotifyWindow() = 0; + virtual SGraphicsWarning *GetCurWarning() = 0; protected: inline CTextureHandle CreateTextureHandle(int Index) { diff --git a/src/engine/shared/config_variables.h b/src/engine/shared/config_variables.h index d64534dbe..a5f0c89a6 100644 --- a/src/engine/shared/config_variables.h +++ b/src/engine/shared/config_variables.h @@ -116,6 +116,7 @@ MACRO_CONFIG_INT(GfxTextOverlay, gfx_text_overlay, 10, 1, 100, CFGFLAG_SAVE|CFGF MACRO_CONFIG_INT(GfxAsyncRenderOld, gfx_asyncrender_old, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Do rendering async from the the update") MACRO_CONFIG_INT(GfxTuneOverlay, gfx_tune_overlay, 20, 1, 100, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Stop rendering text overlay in tuning zone in editor: high value = less details = more speed") MACRO_CONFIG_INT(GfxQuadAsTriangle, gfx_quad_as_triangle, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Render quads as triangles (fixes quad coloring on some GPUs)") +MACRO_CONFIG_INT(GfxShowWarnings, gfx_show_warnings, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Render gfx warnings to screen") MACRO_CONFIG_INT(InpMousesens, inp_mousesens, 200, 1, 100000, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Mouse sensitivity") MACRO_CONFIG_INT(InpMouseOld, inp_mouseold, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Use old mouse mode (warp mouse instead of raw input)") diff --git a/src/game/client/components/mapimages.cpp b/src/game/client/components/mapimages.cpp index 7c87fb099..2fb75ccb6 100644 --- a/src/game/client/components/mapimages.cpp +++ b/src/game/client/components/mapimages.cpp @@ -90,7 +90,10 @@ void CMapImages::OnMapLoadImpl(class CLayers *pLayers, IMap *pMap) else { void *pData = pMap->GetData(pImg->m_ImageData); - m_aTextures[i] = Graphics()->LoadTextureRaw(pImg->m_Width, pImg->m_Height, CImageInfo::FORMAT_RGBA, pData, CImageInfo::FORMAT_RGBA, LoadFlag); + char *pName = (char *)pMap->GetData(pImg->m_ImageName); + char aTexName[128]; + str_format(aTexName, sizeof(aTexName), "%s %s", "embedded:", pName); + m_aTextures[i] = Graphics()->LoadTextureRaw(pImg->m_Width, pImg->m_Height, CImageInfo::FORMAT_RGBA, pData, CImageInfo::FORMAT_RGBA, LoadFlag, aTexName); pMap->UnloadData(pImg->m_ImageData); } } diff --git a/src/game/client/components/menus.cpp b/src/game/client/components/menus.cpp index e5256ab9e..45d25d649 100644 --- a/src/game/client/components/menus.cpp +++ b/src/game/client/components/menus.cpp @@ -974,12 +974,33 @@ void CMenus::PopupMessage(const char *pTopic, const char *pBody, const char *pBu str_copy(m_aMessageTopic, pTopic, sizeof(m_aMessageTopic)); str_copy(m_aMessageBody, pBody, sizeof(m_aMessageBody)); str_copy(m_aMessageButton, pButton, sizeof(m_aMessageButton)); - m_Popup = POPUP_MESSAGE; } +void CMenus::PopupWarning(const char *pTopic, const char *pBody, const char *pButton, int64 Duration) +{ + // reset active item + UI()->SetActiveItem(0); + + str_copy(m_aMessageTopic, pTopic, sizeof(m_aMessageTopic)); + str_copy(m_aMessageBody, pBody, sizeof(m_aMessageBody)); + str_copy(m_aMessageButton, pButton, sizeof(m_aMessageButton)); + m_Popup = POPUP_WARNING; + SetActive(true); + + m_PopupWarningDuration = Duration; + m_PopupWarningLastTime = time_get_microseconds(); +} + +bool CMenus::CanDisplayWarning() +{ + return m_Popup == POPUP_NONE && (Client()->State() == IClient::STATE_DEMOPLAYBACK || Client()->State() == IClient::STATE_ONLINE); +} int CMenus::Render() { + if(Client()->State() == IClient::STATE_DEMOPLAYBACK && m_Popup == POPUP_NONE) + return 0; + CUIRect Screen = *UI()->Screen(); Graphics()->MapScreen(Screen.x, Screen.y, Screen.w, Screen.h); @@ -1008,7 +1029,7 @@ int CMenus::Render() ServerBrowser()->Refresh(IServerBrowser::TYPE_KOG); } - if(Client()->State() == IClient::STATE_ONLINE) + if(Client()->State() >= IClient::STATE_ONLINE) { ms_ColorTabbarInactive = ms_ColorTabbarInactiveIngame; ms_ColorTabbarActive = ms_ColorTabbarActiveIngame; @@ -1095,6 +1116,7 @@ int CMenus::Render() const char *pButtonText = ""; int ExtraAlign = 0; + ColorRGBA BgColor = ColorRGBA{0.0f, 0.0f, 0.0f, 0.5f}; if(m_Popup == POPUP_MESSAGE) { pTitle = m_aMessageTopic; @@ -1213,6 +1235,14 @@ int CMenus::Render() pButtonText = Localize("Ok"); ExtraAlign = -1; } + else if(m_Popup == POPUP_WARNING) + { + BgColor = ColorRGBA{0.5f, 0.0f, 0.0f, 0.7f}; + pTitle = m_aMessageTopic; + pExtraText = m_aMessageBody; + pButtonText = m_aMessageButton; + ExtraAlign = -1; + } CUIRect Box, Part; Box = Screen; @@ -1223,7 +1253,7 @@ int CMenus::Render() } // render the box - RenderTools()->DrawUIRect(&Box, ColorRGBA(0,0,0,0.5f), CUI::CORNER_ALL, 15.0f); + RenderTools()->DrawUIRect(&Box, BgColor, CUI::CORNER_ALL, 15.0f); Box.HSplitTop(20.f/UI()->Scale(), &Part, &Box); Box.HSplitTop(24.f/UI()->Scale(), &Part, &Box); @@ -1807,6 +1837,19 @@ int CMenus::Render() static float Offset = 0.0f; DoEditBox(&g_Config.m_PlayerName, &TextBox, g_Config.m_PlayerName, sizeof(g_Config.m_PlayerName), 12.0f, &Offset, false, CUI::CORNER_ALL, Client()->PlayerName()); } + else if(m_Popup == POPUP_WARNING) + { + Box.HSplitBottom(20.f, &Box, &Part); + Box.HSplitBottom(24.f, &Box, &Part); + Part.VMargin(120.0f, &Part); + + static int s_Button = 0; + if(DoButton_Menu(&s_Button, pButtonText, 0, &Part) || m_EscapePressed || m_EnterPressed || (time_get_microseconds() - m_PopupWarningLastTime >= m_PopupWarningDuration)) + { + m_Popup = POPUP_NONE; + SetActive(false); + } + } else { Box.HSplitBottom(20.f, &Box, &Part); @@ -1954,8 +1997,11 @@ void CMenus::OnStateChange(int NewState, int OldState) m_Popup = POPUP_CONNECTING; else if(NewState == IClient::STATE_ONLINE || NewState == IClient::STATE_DEMOPLAYBACK) { - m_Popup = POPUP_NONE; - SetActive(false); + if(m_Popup != POPUP_WARNING) + { + m_Popup = POPUP_NONE; + SetActive(false); + } } } @@ -2025,8 +2071,7 @@ void CMenus::OnRender() UI()->Update(mx,my,mx*3.0f,my*3.0f,Buttons); // render - if(Client()->State() != IClient::STATE_DEMOPLAYBACK) - Render(); + Render(); // render cursor Graphics()->TextureSet(g_pData->m_aImages[IMAGE_CURSOR].m_Id); diff --git a/src/game/client/components/menus.h b/src/game/client/components/menus.h index d3d89d2cd..af352fed0 100644 --- a/src/game/client/components/menus.h +++ b/src/game/client/components/menus.h @@ -396,6 +396,13 @@ public: void DeleteGhostItem(int Index); void setPopup(int Popup) { m_Popup = Popup; } + int GetCurPopup() { return m_Popup; } + bool CanDisplayWarning(); + + void PopupWarning(const char *pTopic, const char *pBody, const char *pButton, int64 Duration); + + int64 m_PopupWarningLastTime; + int64 m_PopupWarningDuration; int m_DemoPlayerState; char m_aDemoPlayerPopupHint[256]; @@ -420,6 +427,7 @@ public: POPUP_QUIT, POPUP_DISCONNECT, POPUP_DISCONNECT_DUMMY, + POPUP_WARNING, // demo player states DEMOPLAYER_NONE=0, diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp index e2158e073..c2471081d 100644 --- a/src/game/client/gameclient.cpp +++ b/src/game/client/gameclient.cpp @@ -623,6 +623,21 @@ void CGameClient::OnRender() // update the local character and spectate position UpdatePositions(); + // display gfx warnings + if(g_Config.m_GfxShowWarnings == 1) + { + SGraphicsWarning* pWarning = Graphics()->GetCurWarning(); + if(pWarning != NULL) + { + if(m_pMenus->CanDisplayWarning()) + { + m_pMenus->PopupWarning("Warning!", pWarning->m_aWarningMsg, "Ok", 10000000); + + pWarning->m_WasShown = true; + } + } + } + // render all systems for(int i = 0; i < m_All.m_Num; i++) m_All.m_paComponents[i]->OnRender();