Avoid copying texture memory when possible

Add separate `IGraphics::LoadTextureRawMove` function with non-`const` `void *pData` argument in addition to existing `LoadTextureRaw` function with `const void *pData` argument. The former function takes ownership of the data and avoids copying the texture data into an additional buffer, if the texture data is already in RGBA format. Non-RGBA texture data always needs to be converted and therefore also copied.

The `LoadTextureRaw` function is split into smaller functions to share common code with the `LoadTextureRawMove` function. Alternatively to this, a flag `TEXLOAD_MOVE_DATA` could have been added to the existing `LoadTextureRaw` function, which would have required the use of `const_cast` to free the texture data.
This commit is contained in:
Robert Müller 2023-11-03 22:56:15 +01:00
parent 022cae6f20
commit abc7b602b2
8 changed files with 58 additions and 36 deletions

View file

@ -290,10 +290,7 @@ void CGraphics_Threaded::FreeTextureIndex(CTextureHandle *pIndex)
int CGraphics_Threaded::UnloadTexture(CTextureHandle *pIndex)
{
if(pIndex->Id() == m_NullTexture.Id())
return 0;
if(!pIndex->IsValid())
if(pIndex->IsNullTexture() || !pIndex->IsValid())
return 0;
CCommandBuffer::SCommand_Texture_Destroy Cmd;
@ -416,7 +413,7 @@ bool CGraphics_Threaded::IsSpriteTextureFullyTransparent(CImageInfo &FromImageIn
return IsImageSubFullyTransparent(FromImageInfo, x, y, w, h);
}
IGraphics::CTextureHandle CGraphics_Threaded::LoadTextureRaw(size_t Width, size_t Height, CImageInfo::EImageFormat Format, const void *pData, int Flags, const char *pTexName)
static void LoadTextureAddWarning(size_t Width, size_t Height, int Flags, const char *pTexName, std::vector<SWarning> &vWarnings)
{
if((Flags & IGraphics::TEXLOAD_TO_2D_ARRAY_TEXTURE) != 0 || (Flags & IGraphics::TEXLOAD_TO_3D_TEXTURE) != 0)
{
@ -424,30 +421,22 @@ IGraphics::CTextureHandle CGraphics_Threaded::LoadTextureRaw(size_t Width, size_
{
SWarning NewWarning;
char aText[128];
aText[0] = '\0';
if(pTexName)
{
str_format(aText, sizeof(aText), "\"%s\"", pTexName);
}
str_format(aText, sizeof(aText), "\"%s\"", pTexName ? pTexName : "(no name)");
str_format(NewWarning.m_aWarningMsg, sizeof(NewWarning.m_aWarningMsg), Localize("The width of texture %s is not divisible by %d, or the height is not divisible by %d, which might cause visual bugs."), aText, 16, 16);
m_vWarnings.emplace_back(NewWarning);
vWarnings.emplace_back(NewWarning);
}
}
}
if(Width == 0 || Height == 0)
return IGraphics::CTextureHandle();
IGraphics::CTextureHandle TextureHandle = FindFreeTextureIndex();
static CCommandBuffer::SCommand_Texture_Create LoadTextureCreateCommand(int TextureId, size_t Width, size_t Height, int Flags)
{
CCommandBuffer::SCommand_Texture_Create Cmd;
Cmd.m_Slot = TextureHandle.Id();
Cmd.m_Slot = TextureId;
Cmd.m_Width = Width;
Cmd.m_Height = Height;
Cmd.m_Format = CCommandBuffer::TEXFORMAT_RGBA;
Cmd.m_StoreFormat = CCommandBuffer::TEXFORMAT_RGBA;
// flags
Cmd.m_Flags = 0;
if(Flags & IGraphics::TEXLOAD_NOMIPMAPS)
Cmd.m_Flags |= CCommandBuffer::TEXFLAG_NOMIPMAPS;
@ -458,14 +447,51 @@ IGraphics::CTextureHandle CGraphics_Threaded::LoadTextureRaw(size_t Width, size_
if((Flags & IGraphics::TEXLOAD_NO_2D_TEXTURE) != 0)
Cmd.m_Flags |= CCommandBuffer::TEXFLAG_NO_2D_TEXTURE;
// copy texture data
return Cmd;
}
IGraphics::CTextureHandle CGraphics_Threaded::LoadTextureRaw(size_t Width, size_t Height, CImageInfo::EImageFormat Format, const void *pData, int Flags, const char *pTexName)
{
LoadTextureAddWarning(Width, Height, Flags, pTexName, m_vWarnings);
if(Width == 0 || Height == 0)
return IGraphics::CTextureHandle();
IGraphics::CTextureHandle TextureHandle = FindFreeTextureIndex();
CCommandBuffer::SCommand_Texture_Create Cmd = LoadTextureCreateCommand(TextureHandle.Id(), Width, Height, Flags);
// Copy texture data and convert if necessary
const size_t MemSize = Width * Height * CImageInfo::PixelSize(CImageInfo::FORMAT_RGBA);
void *pTmpData = malloc(MemSize);
if(!ConvertToRGBA((uint8_t *)pTmpData, (const uint8_t *)pData, Width, Height, Format))
if(!ConvertToRGBA(static_cast<uint8_t *>(pTmpData), static_cast<const uint8_t *>(pData), Width, Height, Format))
{
dbg_msg("graphics", "converted image %s to RGBA, consider making its file format RGBA", pTexName ? pTexName : "(no name)");
dbg_msg("graphics", "converted image '%s' to RGBA, consider making its file format RGBA", pTexName ? pTexName : "(no name)");
}
Cmd.m_pData = pTmpData;
AddCmd(Cmd);
return TextureHandle;
}
IGraphics::CTextureHandle CGraphics_Threaded::LoadTextureRawMove(size_t Width, size_t Height, CImageInfo::EImageFormat Format, void *pData, int Flags, const char *pTexName)
{
if(Format != CImageInfo::FORMAT_RGBA)
{
// Moving not possible, texture needs to be converted
return LoadTextureRaw(Width, Height, Format, pData, Flags, pTexName);
}
LoadTextureAddWarning(Width, Height, Flags, pTexName, m_vWarnings);
if(Width == 0 || Height == 0)
return IGraphics::CTextureHandle();
IGraphics::CTextureHandle TextureHandle = FindFreeTextureIndex();
CCommandBuffer::SCommand_Texture_Create Cmd = LoadTextureCreateCommand(TextureHandle.Id(), Width, Height, Flags);
Cmd.m_pData = pData;
AddCmd(Cmd);
return TextureHandle;
@ -479,8 +505,7 @@ IGraphics::CTextureHandle CGraphics_Threaded::LoadTexture(const char *pFilename,
CImageInfo Img;
if(LoadPNG(&Img, pFilename, StorageType))
{
CTextureHandle ID = LoadTextureRaw(Img.m_Width, Img.m_Height, Img.m_Format, Img.m_pData, Flags, pFilename);
FreePNG(&Img);
CTextureHandle ID = LoadTextureRawMove(Img.m_Width, Img.m_Height, Img.m_Format, Img.m_pData, Flags, pFilename);
if(ID.IsValid())
{
if(g_Config.m_Debug)

View file

@ -951,6 +951,7 @@ public:
void FreeTextureIndex(CTextureHandle *pIndex);
int UnloadTexture(IGraphics::CTextureHandle *pIndex) override;
IGraphics::CTextureHandle LoadTextureRaw(size_t Width, size_t Height, CImageInfo::EImageFormat Format, const void *pData, int Flags, const char *pTexName = nullptr) override;
IGraphics::CTextureHandle LoadTextureRawMove(size_t Width, size_t Height, CImageInfo::EImageFormat Format, void *pData, int Flags, const char *pTexName = nullptr) override;
int LoadTextureRawSub(IGraphics::CTextureHandle TextureID, int x, int y, size_t Width, size_t Height, CImageInfo::EImageFormat Format, const void *pData) override;
IGraphics::CTextureHandle NullTexture() const override;

View file

@ -335,6 +335,7 @@ public:
virtual int UnloadTexture(CTextureHandle *pIndex) = 0;
virtual CTextureHandle LoadTextureRaw(size_t Width, size_t Height, CImageInfo::EImageFormat Format, const void *pData, int Flags, const char *pTexName = nullptr) = 0;
virtual CTextureHandle LoadTextureRawMove(size_t Width, size_t Height, CImageInfo::EImageFormat Format, void *pData, int Flags, const char *pTexName = nullptr) = 0;
virtual int LoadTextureRawSub(CTextureHandle TextureID, int x, int y, size_t Width, size_t Height, CImageInfo::EImageFormat Format, const void *pData) = 0;
virtual CTextureHandle LoadTexture(const char *pFilename, int StorageType, int Flags = 0) = 0;
virtual CTextureHandle NullTexture() const = 0;

View file

@ -75,8 +75,7 @@ void CCountryFlags::LoadCountryflagsIndexfile()
CCountryFlag CountryFlag;
CountryFlag.m_CountryCode = CountryCode;
str_copy(CountryFlag.m_aCountryCodeString, aOrigin);
CountryFlag.m_Texture = Graphics()->LoadTextureRaw(Info.m_Width, Info.m_Height, Info.m_Format, Info.m_pData, 0, aOrigin);
Graphics()->FreePNG(&Info);
CountryFlag.m_Texture = Graphics()->LoadTextureRawMove(Info.m_Width, Info.m_Height, Info.m_Format, Info.m_pData, 0, aOrigin);
if(g_Config.m_Debug)
{

View file

@ -445,10 +445,7 @@ IGraphics::CTextureHandle CMapImages::UploadEntityLayerText(int TextureSize, int
UpdateEntityLayerText(pMem, PixelSize, Width, Height, TextureSize, MaxWidth, YOffset, 2, 255);
const int TextureLoadFlag = (Graphics()->Uses2DTextureArrays() ? IGraphics::TEXLOAD_TO_2D_ARRAY_TEXTURE : IGraphics::TEXLOAD_TO_3D_TEXTURE) | IGraphics::TEXLOAD_NO_2D_TEXTURE;
IGraphics::CTextureHandle Texture = Graphics()->LoadTextureRaw(Width, Height, CImageInfo::FORMAT_RGBA, pMem, TextureLoadFlag);
free(pMem);
return Texture;
return Graphics()->LoadTextureRawMove(Width, Height, CImageInfo::FORMAT_RGBA, pMem, TextureLoadFlag);
}
void CMapImages::UpdateEntityLayerText(void *pTexBuffer, size_t PixelSize, size_t TexWidth, size_t TexHeight, int TextureSize, int MaxWidth, int YOffset, int NumbersPower, int MaxNumber)

View file

@ -2197,8 +2197,7 @@ int CMenus::MenuImageScan(const char *pName, int IsDir, int DirType, void *pUser
pData[i * Step + 1] = v;
pData[i * Step + 2] = v;
}
MenuImage.m_GreyTexture = pSelf->Graphics()->LoadTextureRaw(Info.m_Width, Info.m_Height, Info.m_Format, Info.m_pData, 0);
pSelf->Graphics()->FreePNG(&Info);
MenuImage.m_GreyTexture = pSelf->Graphics()->LoadTextureRawMove(Info.m_Width, Info.m_Height, Info.m_Format, Info.m_pData, 0);
str_truncate(MenuImage.m_aName, sizeof(MenuImage.m_aName), pName, str_length(pName) - str_length(pExtension));
pSelf->m_vMenuImages.push_back(MenuImage);

View file

@ -1908,8 +1908,8 @@ void CMenus::LoadCommunityIconFinish(const char *pCommunityId, CImageInfo &&Info
pData[i * Step + 1] = v;
pData[i * Step + 2] = v;
}
CommunityIcon.m_GreyTexture = Graphics()->LoadTextureRaw(Info.m_Width, Info.m_Height, Info.m_Format, Info.m_pData, 0);
Graphics()->FreePNG(&Info);
CommunityIcon.m_GreyTexture = Graphics()->LoadTextureRawMove(Info.m_Width, Info.m_Height, Info.m_Format, Info.m_pData, 0);
Info.m_pData = nullptr;
auto ExistingIcon = std::find_if(m_vCommunityIcons.begin(), m_vCommunityIcons.end(), [pCommunityId](const SCommunityIcon &Element) {
return str_comp(Element.m_aCommunityId, pCommunityId) == 0;

View file

@ -5166,8 +5166,8 @@ void CEditor::RenderFileDialog()
if(Graphics()->LoadPNG(&m_FilePreviewImageInfo, aBuffer, m_vpFilteredFileList[m_FilesSelectedIndex]->m_StorageType))
{
Graphics()->UnloadTexture(&m_FilePreviewImage);
m_FilePreviewImage = Graphics()->LoadTextureRaw(m_FilePreviewImageInfo.m_Width, m_FilePreviewImageInfo.m_Height, m_FilePreviewImageInfo.m_Format, m_FilePreviewImageInfo.m_pData, 0);
Graphics()->FreePNG(&m_FilePreviewImageInfo);
m_FilePreviewImage = Graphics()->LoadTextureRawMove(m_FilePreviewImageInfo.m_Width, m_FilePreviewImageInfo.m_Height, m_FilePreviewImageInfo.m_Format, m_FilePreviewImageInfo.m_pData, 0);
m_FilePreviewImageInfo.m_pData = nullptr;
m_FilePreviewState = PREVIEW_LOADED;
}
else