mirror of
https://github.com/ddnet/ddnet.git
synced 2024-10-20 15:58:18 +00:00
a14f32cf47
6369: Fix Move also requesting the current canvas size, which should report… r=Robyt3 a=Jupeyy … a resize event fixes #6368 For some reason KDE seems to set the current window'd size of the window (even if i use fullscreen) for the window when its minimized. And ::Move re-requests the canvas size. This resulted in the incorrect viewport. Funnily enough for me under KDE this also means that the check for GotResized, to only notify the components when the canvas actually resized is now useless. `@Robyt3` can you check if Windows is not doing this behavior? Else the previous patch is useless for the crash bug :D On the other hand, this *could* have been part of the text container bug, tho I cannot imagine yet why ## Checklist - [ ] Tested the change ingame - [ ] Provided screenshots if it is a visual change - [ ] Tested in combination with possibly related configuration options - [ ] Written a unit test (especially base/) or added coverage to integration test - [ ] Considered possible null pointers and out of bounds array indexing - [ ] Changed no physics that affect existing maps - [ ] Tested the change with [ASan+UBSan or valgrind's memcheck](https://github.com/ddnet/ddnet/#using-addresssanitizer--undefinedbehavioursanitizer-or-valgrinds-memcheck) (optional) Co-authored-by: Jupeyy <jupjopjap@gmail.com>
3295 lines
95 KiB
C++
3295 lines
95 KiB
C++
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
|
|
/* If you are missing that file, acquire a complete release at teeworlds.com. */
|
|
|
|
#include <base/detect.h>
|
|
#include <base/math.h>
|
|
|
|
#if defined(CONF_FAMILY_UNIX)
|
|
#include <pthread.h>
|
|
#endif
|
|
|
|
#include <base/system.h>
|
|
|
|
#include <engine/console.h>
|
|
#include <engine/gfx/image_loader.h>
|
|
#include <engine/gfx/image_manipulation.h>
|
|
#include <engine/graphics.h>
|
|
#include <engine/shared/config.h>
|
|
#include <engine/storage.h>
|
|
#include <game/generated/client_data.h>
|
|
#include <game/generated/client_data7.h>
|
|
#include <game/localization.h>
|
|
|
|
#if defined(CONF_VIDEORECORDER)
|
|
#include <engine/shared/video.h>
|
|
#endif
|
|
|
|
#include "graphics_threaded.h"
|
|
|
|
class CSemaphore;
|
|
|
|
static CVideoMode g_aFakeModes[] = {
|
|
{8192, 4320, 8192, 4320, 0, 8, 8, 8, 0}, {7680, 4320, 7680, 4320, 0, 8, 8, 8, 0}, {5120, 2880, 5120, 2880, 0, 8, 8, 8, 0},
|
|
{4096, 2160, 4096, 2160, 0, 8, 8, 8, 0}, {3840, 2160, 3840, 2160, 0, 8, 8, 8, 0}, {2560, 1440, 2560, 1440, 0, 8, 8, 8, 0},
|
|
{2048, 1536, 2048, 1536, 0, 8, 8, 8, 0}, {1920, 2400, 1920, 2400, 0, 8, 8, 8, 0}, {1920, 1440, 1920, 1440, 0, 8, 8, 8, 0},
|
|
{1920, 1200, 1920, 1200, 0, 8, 8, 8, 0}, {1920, 1080, 1920, 1080, 0, 8, 8, 8, 0}, {1856, 1392, 1856, 1392, 0, 8, 8, 8, 0},
|
|
{1800, 1440, 1800, 1440, 0, 8, 8, 8, 0}, {1792, 1344, 1792, 1344, 0, 8, 8, 8, 0}, {1680, 1050, 1680, 1050, 0, 8, 8, 8, 0},
|
|
{1600, 1200, 1600, 1200, 0, 8, 8, 8, 0}, {1600, 1000, 1600, 1000, 0, 8, 8, 8, 0}, {1440, 1050, 1440, 1050, 0, 8, 8, 8, 0},
|
|
{1440, 900, 1440, 900, 0, 8, 8, 8, 0}, {1400, 1050, 1400, 1050, 0, 8, 8, 8, 0}, {1368, 768, 1368, 768, 0, 8, 8, 8, 0},
|
|
{1280, 1024, 1280, 1024, 0, 8, 8, 8, 0}, {1280, 960, 1280, 960, 0, 8, 8, 8, 0}, {1280, 800, 1280, 800, 0, 8, 8, 8, 0},
|
|
{1280, 768, 1280, 768, 0, 8, 8, 8, 0}, {1152, 864, 1152, 864, 0, 8, 8, 8, 0}, {1024, 768, 1024, 768, 0, 8, 8, 8, 0},
|
|
{1024, 600, 1024, 600, 0, 8, 8, 8, 0}, {800, 600, 800, 600, 0, 8, 8, 8, 0}, {768, 576, 768, 576, 0, 8, 8, 8, 0},
|
|
{720, 400, 720, 400, 0, 8, 8, 8, 0}, {640, 480, 640, 480, 0, 8, 8, 8, 0}, {400, 300, 400, 300, 0, 8, 8, 8, 0},
|
|
{320, 240, 320, 240, 0, 8, 8, 8, 0},
|
|
|
|
{8192, 4320, 8192, 4320, 0, 5, 6, 5, 0}, {7680, 4320, 7680, 4320, 0, 5, 6, 5, 0}, {5120, 2880, 5120, 2880, 0, 5, 6, 5, 0},
|
|
{4096, 2160, 4096, 2160, 0, 5, 6, 5, 0}, {3840, 2160, 3840, 2160, 0, 5, 6, 5, 0}, {2560, 1440, 2560, 1440, 0, 5, 6, 5, 0},
|
|
{2048, 1536, 2048, 1536, 0, 5, 6, 5, 0}, {1920, 2400, 1920, 2400, 0, 5, 6, 5, 0}, {1920, 1440, 1920, 1440, 0, 5, 6, 5, 0},
|
|
{1920, 1200, 1920, 1200, 0, 5, 6, 5, 0}, {1920, 1080, 1920, 1080, 0, 5, 6, 5, 0}, {1856, 1392, 1856, 1392, 0, 5, 6, 5, 0},
|
|
{1800, 1440, 1800, 1440, 0, 5, 6, 5, 0}, {1792, 1344, 1792, 1344, 0, 5, 6, 5, 0}, {1680, 1050, 1680, 1050, 0, 5, 6, 5, 0},
|
|
{1600, 1200, 1600, 1200, 0, 5, 6, 5, 0}, {1600, 1000, 1600, 1000, 0, 5, 6, 5, 0}, {1440, 1050, 1440, 1050, 0, 5, 6, 5, 0},
|
|
{1440, 900, 1440, 900, 0, 5, 6, 5, 0}, {1400, 1050, 1400, 1050, 0, 5, 6, 5, 0}, {1368, 768, 1368, 768, 0, 5, 6, 5, 0},
|
|
{1280, 1024, 1280, 1024, 0, 5, 6, 5, 0}, {1280, 960, 1280, 960, 0, 5, 6, 5, 0}, {1280, 800, 1280, 800, 0, 5, 6, 5, 0},
|
|
{1280, 768, 1280, 768, 0, 5, 6, 5, 0}, {1152, 864, 1152, 864, 0, 5, 6, 5, 0}, {1024, 768, 1024, 768, 0, 5, 6, 5, 0},
|
|
{1024, 600, 1024, 600, 0, 5, 6, 5, 0}, {800, 600, 800, 600, 0, 5, 6, 5, 0}, {768, 576, 768, 576, 0, 5, 6, 5, 0},
|
|
{720, 400, 720, 400, 0, 5, 6, 5, 0}, {640, 480, 640, 480, 0, 5, 6, 5, 0}, {400, 300, 400, 300, 0, 5, 6, 5, 0},
|
|
{320, 240, 320, 240, 0, 5, 6, 5, 0}};
|
|
|
|
void CGraphics_Threaded::FlushVertices(bool KeepVertices)
|
|
{
|
|
CCommandBuffer::SCommand_Render Cmd;
|
|
int PrimType, PrimCount, NumVerts;
|
|
size_t VertSize = sizeof(CCommandBuffer::SVertex);
|
|
FlushVerticesImpl(KeepVertices, PrimType, PrimCount, NumVerts, Cmd, VertSize);
|
|
|
|
if(Cmd.m_pVertices != NULL)
|
|
{
|
|
mem_copy(Cmd.m_pVertices, m_aVertices, VertSize * NumVerts);
|
|
}
|
|
}
|
|
|
|
void CGraphics_Threaded::FlushVerticesTex3D()
|
|
{
|
|
CCommandBuffer::SCommand_RenderTex3D Cmd;
|
|
int PrimType, PrimCount, NumVerts;
|
|
size_t VertSize = sizeof(CCommandBuffer::SVertexTex3DStream);
|
|
FlushVerticesImpl(false, PrimType, PrimCount, NumVerts, Cmd, VertSize);
|
|
|
|
if(Cmd.m_pVertices != NULL)
|
|
{
|
|
mem_copy(Cmd.m_pVertices, m_aVerticesTex3D, VertSize * NumVerts);
|
|
}
|
|
}
|
|
|
|
void CGraphics_Threaded::AddVertices(int Count)
|
|
{
|
|
m_NumVertices += Count;
|
|
if((m_NumVertices + Count) >= CCommandBuffer::MAX_VERTICES)
|
|
FlushVertices();
|
|
}
|
|
|
|
void CGraphics_Threaded::AddVertices(int Count, CCommandBuffer::SVertex *pVertices)
|
|
{
|
|
AddVertices(Count);
|
|
}
|
|
|
|
void CGraphics_Threaded::AddVertices(int Count, CCommandBuffer::SVertexTex3DStream *pVertices)
|
|
{
|
|
m_NumVertices += Count;
|
|
if((m_NumVertices + Count) >= CCommandBuffer::MAX_VERTICES)
|
|
FlushVerticesTex3D();
|
|
}
|
|
|
|
CGraphics_Threaded::CGraphics_Threaded()
|
|
{
|
|
m_State.m_ScreenTL.x = 0;
|
|
m_State.m_ScreenTL.y = 0;
|
|
m_State.m_ScreenBR.x = 0;
|
|
m_State.m_ScreenBR.y = 0;
|
|
m_State.m_ClipEnable = false;
|
|
m_State.m_ClipX = 0;
|
|
m_State.m_ClipY = 0;
|
|
m_State.m_ClipW = 0;
|
|
m_State.m_ClipH = 0;
|
|
m_State.m_Texture = -1;
|
|
m_State.m_BlendMode = CCommandBuffer::BLEND_NONE;
|
|
m_State.m_WrapMode = CCommandBuffer::WRAP_REPEAT;
|
|
|
|
m_CurrentCommandBuffer = 0;
|
|
m_pCommandBuffer = 0x0;
|
|
m_apCommandBuffers[0] = 0x0;
|
|
m_apCommandBuffers[1] = 0x0;
|
|
|
|
m_NumVertices = 0;
|
|
|
|
m_ScreenWidth = -1;
|
|
m_ScreenHeight = -1;
|
|
m_ScreenRefreshRate = -1;
|
|
|
|
m_Rotation = 0;
|
|
m_Drawing = 0;
|
|
|
|
m_TextureMemoryUsage = 0;
|
|
|
|
m_RenderEnable = true;
|
|
m_DoScreenshot = false;
|
|
}
|
|
|
|
void CGraphics_Threaded::ClipEnable(int x, int y, int w, int h)
|
|
{
|
|
if(x < 0)
|
|
w += x;
|
|
if(y < 0)
|
|
h += y;
|
|
|
|
x = clamp(x, 0, ScreenWidth());
|
|
y = clamp(y, 0, ScreenHeight());
|
|
w = clamp(w, 0, ScreenWidth() - x);
|
|
h = clamp(h, 0, ScreenHeight() - y);
|
|
|
|
m_State.m_ClipEnable = true;
|
|
m_State.m_ClipX = x;
|
|
m_State.m_ClipY = ScreenHeight() - (y + h);
|
|
m_State.m_ClipW = w;
|
|
m_State.m_ClipH = h;
|
|
}
|
|
|
|
void CGraphics_Threaded::ClipDisable()
|
|
{
|
|
m_State.m_ClipEnable = false;
|
|
}
|
|
|
|
void CGraphics_Threaded::BlendNone()
|
|
{
|
|
m_State.m_BlendMode = CCommandBuffer::BLEND_NONE;
|
|
}
|
|
|
|
void CGraphics_Threaded::BlendNormal()
|
|
{
|
|
m_State.m_BlendMode = CCommandBuffer::BLEND_ALPHA;
|
|
}
|
|
|
|
void CGraphics_Threaded::BlendAdditive()
|
|
{
|
|
m_State.m_BlendMode = CCommandBuffer::BLEND_ADDITIVE;
|
|
}
|
|
|
|
void CGraphics_Threaded::WrapNormal()
|
|
{
|
|
m_State.m_WrapMode = CCommandBuffer::WRAP_REPEAT;
|
|
}
|
|
|
|
void CGraphics_Threaded::WrapClamp()
|
|
{
|
|
m_State.m_WrapMode = CCommandBuffer::WRAP_CLAMP;
|
|
}
|
|
|
|
uint64_t CGraphics_Threaded::TextureMemoryUsage() const
|
|
{
|
|
return m_pBackend->TextureMemoryUsage();
|
|
}
|
|
|
|
uint64_t CGraphics_Threaded::BufferMemoryUsage() const
|
|
{
|
|
return m_pBackend->BufferMemoryUsage();
|
|
}
|
|
|
|
uint64_t CGraphics_Threaded::StreamedMemoryUsage() const
|
|
{
|
|
return m_pBackend->StreamedMemoryUsage();
|
|
}
|
|
|
|
uint64_t CGraphics_Threaded::StagingMemoryUsage() const
|
|
{
|
|
return m_pBackend->StagingMemoryUsage();
|
|
}
|
|
|
|
const TTWGraphicsGPUList &CGraphics_Threaded::GetGPUs() const
|
|
{
|
|
return m_pBackend->GetGPUs();
|
|
}
|
|
|
|
void CGraphics_Threaded::MapScreen(float TopLeftX, float TopLeftY, float BottomRightX, float BottomRightY)
|
|
{
|
|
m_State.m_ScreenTL.x = TopLeftX;
|
|
m_State.m_ScreenTL.y = TopLeftY;
|
|
m_State.m_ScreenBR.x = BottomRightX;
|
|
m_State.m_ScreenBR.y = BottomRightY;
|
|
}
|
|
|
|
void CGraphics_Threaded::GetScreen(float *pTopLeftX, float *pTopLeftY, float *pBottomRightX, float *pBottomRightY)
|
|
{
|
|
*pTopLeftX = m_State.m_ScreenTL.x;
|
|
*pTopLeftY = m_State.m_ScreenTL.y;
|
|
*pBottomRightX = m_State.m_ScreenBR.x;
|
|
*pBottomRightY = m_State.m_ScreenBR.y;
|
|
}
|
|
|
|
void CGraphics_Threaded::LinesBegin()
|
|
{
|
|
dbg_assert(m_Drawing == 0, "called Graphics()->LinesBegin twice");
|
|
m_Drawing = DRAWING_LINES;
|
|
SetColor(1, 1, 1, 1);
|
|
}
|
|
|
|
void CGraphics_Threaded::LinesEnd()
|
|
{
|
|
dbg_assert(m_Drawing == DRAWING_LINES, "called Graphics()->LinesEnd without begin");
|
|
FlushVertices();
|
|
m_Drawing = 0;
|
|
}
|
|
|
|
void CGraphics_Threaded::LinesDraw(const CLineItem *pArray, int Num)
|
|
{
|
|
dbg_assert(m_Drawing == DRAWING_LINES, "called Graphics()->LinesDraw without begin");
|
|
|
|
for(int i = 0; i < Num; ++i)
|
|
{
|
|
m_aVertices[m_NumVertices + 2 * i].m_Pos.x = pArray[i].m_X0;
|
|
m_aVertices[m_NumVertices + 2 * i].m_Pos.y = pArray[i].m_Y0;
|
|
m_aVertices[m_NumVertices + 2 * i].m_Tex = m_aTexture[0];
|
|
SetColor(&m_aVertices[m_NumVertices + 2 * i], 0);
|
|
|
|
m_aVertices[m_NumVertices + 2 * i + 1].m_Pos.x = pArray[i].m_X1;
|
|
m_aVertices[m_NumVertices + 2 * i + 1].m_Pos.y = pArray[i].m_Y1;
|
|
m_aVertices[m_NumVertices + 2 * i + 1].m_Tex = m_aTexture[1];
|
|
SetColor(&m_aVertices[m_NumVertices + 2 * i + 1], 1);
|
|
}
|
|
|
|
AddVertices(2 * Num);
|
|
}
|
|
|
|
int CGraphics_Threaded::UnloadTexture(CTextureHandle *pIndex)
|
|
{
|
|
if(pIndex->Id() == m_InvalidTexture.Id())
|
|
return 0;
|
|
|
|
if(!pIndex->IsValid())
|
|
return 0;
|
|
|
|
CCommandBuffer::SCommand_Texture_Destroy Cmd;
|
|
Cmd.m_Slot = pIndex->Id();
|
|
AddCmd(
|
|
Cmd, [] { return true; }, "failed to unload texture.");
|
|
|
|
m_vTextureIndices[pIndex->Id()] = m_FirstFreeTexture;
|
|
m_FirstFreeTexture = pIndex->Id();
|
|
|
|
pIndex->Invalidate();
|
|
return 0;
|
|
}
|
|
|
|
static int ImageFormatToPixelSize(int Format)
|
|
{
|
|
switch(Format)
|
|
{
|
|
case CImageInfo::FORMAT_RGB: return 3;
|
|
case CImageInfo::FORMAT_SINGLE_COMPONENT: return 1;
|
|
default: return 4;
|
|
}
|
|
}
|
|
|
|
static bool ConvertToRGBA(uint8_t *pDest, const uint8_t *pSrc, size_t SrcWidth, size_t SrcHeight, int SrcFormat)
|
|
{
|
|
if(SrcFormat == CImageInfo::FORMAT_RGBA)
|
|
{
|
|
mem_copy(pDest, pSrc, SrcWidth * SrcHeight * 4);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
size_t SrcChannelCount = ImageFormatToPixelSize(SrcFormat);
|
|
size_t DstChannelCount = 4;
|
|
for(size_t Y = 0; Y < SrcHeight; ++Y)
|
|
{
|
|
for(size_t X = 0; X < SrcWidth; ++X)
|
|
{
|
|
size_t ImgOffsetSrc = (Y * SrcWidth * SrcChannelCount) + (X * SrcChannelCount);
|
|
size_t ImgOffsetDest = (Y * SrcWidth * DstChannelCount) + (X * DstChannelCount);
|
|
size_t CopySize = SrcChannelCount;
|
|
if(SrcChannelCount == 3)
|
|
{
|
|
mem_copy(&pDest[ImgOffsetDest], &pSrc[ImgOffsetSrc], CopySize);
|
|
pDest[ImgOffsetDest + 3] = 255;
|
|
}
|
|
else if(SrcChannelCount == 1)
|
|
{
|
|
pDest[ImgOffsetDest + 0] = 255;
|
|
pDest[ImgOffsetDest + 1] = 255;
|
|
pDest[ImgOffsetDest + 2] = 255;
|
|
pDest[ImgOffsetDest + 3] = pSrc[ImgOffsetSrc];
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
int CGraphics_Threaded::LoadTextureRawSub(CTextureHandle TextureID, int x, int y, int Width, int Height, int Format, const void *pData)
|
|
{
|
|
CCommandBuffer::SCommand_Texture_Update Cmd;
|
|
Cmd.m_Slot = TextureID.Id();
|
|
Cmd.m_X = x;
|
|
Cmd.m_Y = y;
|
|
Cmd.m_Width = Width;
|
|
Cmd.m_Height = Height;
|
|
Cmd.m_Format = CCommandBuffer::TEXFORMAT_RGBA;
|
|
|
|
// calculate memory usage
|
|
int MemSize = Width * Height * 4;
|
|
|
|
// copy texture data
|
|
void *pTmpData = malloc(MemSize);
|
|
ConvertToRGBA((uint8_t *)pTmpData, (const uint8_t *)pData, Width, Height, Format);
|
|
Cmd.m_pData = pTmpData;
|
|
|
|
AddCmd(
|
|
Cmd, [] { return true; }, "failed to load raw sub texture.");
|
|
return 0;
|
|
}
|
|
|
|
IGraphics::CTextureHandle CGraphics_Threaded::LoadSpriteTextureImpl(CImageInfo &FromImageInfo, int x, int y, int w, int h)
|
|
{
|
|
int bpp = ImageFormatToPixelSize(FromImageInfo.m_Format);
|
|
|
|
m_vSpriteHelper.resize((size_t)w * h * bpp);
|
|
|
|
CopyTextureFromTextureBufferSub(m_vSpriteHelper.data(), w, h, (uint8_t *)FromImageInfo.m_pData, FromImageInfo.m_Width, FromImageInfo.m_Height, bpp, x, y, w, h);
|
|
|
|
IGraphics::CTextureHandle RetHandle = LoadTextureRaw(w, h, FromImageInfo.m_Format, m_vSpriteHelper.data(), FromImageInfo.m_Format, 0);
|
|
|
|
return RetHandle;
|
|
}
|
|
|
|
IGraphics::CTextureHandle CGraphics_Threaded::LoadSpriteTexture(CImageInfo &FromImageInfo, CDataSprite *pSprite)
|
|
{
|
|
int imggx = FromImageInfo.m_Width / pSprite->m_pSet->m_Gridx;
|
|
int imggy = FromImageInfo.m_Height / pSprite->m_pSet->m_Gridy;
|
|
int x = pSprite->m_X * imggx;
|
|
int y = pSprite->m_Y * imggy;
|
|
int w = pSprite->m_W * imggx;
|
|
int h = pSprite->m_H * imggy;
|
|
return LoadSpriteTextureImpl(FromImageInfo, x, y, w, h);
|
|
}
|
|
|
|
IGraphics::CTextureHandle CGraphics_Threaded::LoadSpriteTexture(CImageInfo &FromImageInfo, client_data7::CDataSprite *pSprite)
|
|
{
|
|
int imggx = FromImageInfo.m_Width / pSprite->m_pSet->m_Gridx;
|
|
int imggy = FromImageInfo.m_Height / pSprite->m_pSet->m_Gridy;
|
|
int x = pSprite->m_X * imggx;
|
|
int y = pSprite->m_Y * imggy;
|
|
int w = pSprite->m_W * imggx;
|
|
int h = pSprite->m_H * imggy;
|
|
return LoadSpriteTextureImpl(FromImageInfo, x, y, w, h);
|
|
}
|
|
|
|
bool CGraphics_Threaded::IsImageSubFullyTransparent(CImageInfo &FromImageInfo, int x, int y, int w, int h)
|
|
{
|
|
if(FromImageInfo.m_Format == CImageInfo::FORMAT_SINGLE_COMPONENT || FromImageInfo.m_Format == CImageInfo::FORMAT_RGBA)
|
|
{
|
|
uint8_t *pImgData = (uint8_t *)FromImageInfo.m_pData;
|
|
int bpp = ImageFormatToPixelSize(FromImageInfo.m_Format);
|
|
for(int iy = 0; iy < h; ++iy)
|
|
{
|
|
for(int ix = 0; ix < w; ++ix)
|
|
{
|
|
int RealOffset = (x + ix) * bpp + (y + iy) * bpp * FromImageInfo.m_Width;
|
|
if(pImgData[RealOffset + (bpp - 1)] > 0)
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CGraphics_Threaded::IsSpriteTextureFullyTransparent(CImageInfo &FromImageInfo, client_data7::CDataSprite *pSprite)
|
|
{
|
|
int imggx = FromImageInfo.m_Width / pSprite->m_pSet->m_Gridx;
|
|
int imggy = FromImageInfo.m_Height / pSprite->m_pSet->m_Gridy;
|
|
int x = pSprite->m_X * imggx;
|
|
int y = pSprite->m_Y * imggy;
|
|
int w = pSprite->m_W * imggx;
|
|
int h = pSprite->m_H * imggy;
|
|
|
|
return IsImageSubFullyTransparent(FromImageInfo, x, y, w, h);
|
|
}
|
|
|
|
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
|
|
if(g_Config.m_DbgStress)
|
|
return m_InvalidTexture;
|
|
#endif
|
|
|
|
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)
|
|
{
|
|
SWarning NewWarning;
|
|
char aText[128];
|
|
aText[0] = '\0';
|
|
if(pTexName)
|
|
{
|
|
str_format(aText, sizeof(aText), "\"%s\"", pTexName);
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
|
|
if(Width == 0 || Height == 0)
|
|
return IGraphics::CTextureHandle();
|
|
|
|
// grab texture
|
|
int Tex = m_FirstFreeTexture;
|
|
if(Tex == -1)
|
|
{
|
|
size_t CurSize = m_vTextureIndices.size();
|
|
m_vTextureIndices.resize(CurSize * 2);
|
|
for(size_t i = 0; i < CurSize - 1; ++i)
|
|
{
|
|
m_vTextureIndices[CurSize + i] = CurSize + i + 1;
|
|
}
|
|
m_vTextureIndices.back() = -1;
|
|
|
|
Tex = CurSize;
|
|
}
|
|
m_FirstFreeTexture = m_vTextureIndices[Tex];
|
|
m_vTextureIndices[Tex] = -1;
|
|
|
|
CCommandBuffer::SCommand_Texture_Create Cmd;
|
|
Cmd.m_Slot = Tex;
|
|
Cmd.m_Width = Width;
|
|
Cmd.m_Height = Height;
|
|
Cmd.m_PixelSize = 4;
|
|
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;
|
|
if((Flags & IGraphics::TEXLOAD_TO_2D_ARRAY_TEXTURE) != 0)
|
|
Cmd.m_Flags |= CCommandBuffer::TEXFLAG_TO_2D_ARRAY_TEXTURE;
|
|
if((Flags & IGraphics::TEXLOAD_TO_3D_TEXTURE) != 0)
|
|
Cmd.m_Flags |= CCommandBuffer::TEXFLAG_TO_3D_TEXTURE;
|
|
if((Flags & IGraphics::TEXLOAD_TO_2D_ARRAY_TEXTURE_SINGLE_LAYER) != 0)
|
|
Cmd.m_Flags |= CCommandBuffer::TEXFLAG_TO_2D_ARRAY_TEXTURE_SINGLE_LAYER;
|
|
if((Flags & IGraphics::TEXLOAD_TO_3D_TEXTURE_SINGLE_LAYER) != 0)
|
|
Cmd.m_Flags |= CCommandBuffer::TEXFLAG_TO_3D_TEXTURE_SINGLE_LAYER;
|
|
if((Flags & IGraphics::TEXLOAD_NO_2D_TEXTURE) != 0)
|
|
Cmd.m_Flags |= CCommandBuffer::TEXFLAG_NO_2D_TEXTURE;
|
|
|
|
// copy texture data
|
|
int MemSize = Width * Height * Cmd.m_PixelSize;
|
|
void *pTmpData = malloc(MemSize);
|
|
if(!ConvertToRGBA((uint8_t *)pTmpData, (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)");
|
|
}
|
|
Cmd.m_pData = pTmpData;
|
|
|
|
AddCmd(
|
|
Cmd, [] { return true; }, "failed to load raw texture.");
|
|
|
|
return CreateTextureHandle(Tex);
|
|
}
|
|
|
|
// simple uncompressed RGBA loaders
|
|
IGraphics::CTextureHandle CGraphics_Threaded::LoadTexture(const char *pFilename, int StorageType, int StoreFormat, int Flags)
|
|
{
|
|
int l = str_length(pFilename);
|
|
IGraphics::CTextureHandle ID;
|
|
CImageInfo Img;
|
|
|
|
if(l < 3)
|
|
return CTextureHandle();
|
|
if(LoadPNG(&Img, pFilename, StorageType))
|
|
{
|
|
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, pFilename);
|
|
free(Img.m_pData);
|
|
if(ID.Id() != m_InvalidTexture.Id() && g_Config.m_Debug)
|
|
dbg_msg("graphics/texture", "loaded %s", pFilename);
|
|
return ID;
|
|
}
|
|
|
|
return m_InvalidTexture;
|
|
}
|
|
|
|
bool CGraphics_Threaded::LoadTextTextures(int Width, int Height, CTextureHandle &TextTexture, CTextureHandle &TextOutlineTexture, void *pTextData, void *pTextOutlineData)
|
|
{
|
|
if(Width == 0 || Height == 0)
|
|
return false;
|
|
|
|
// grab texture
|
|
int Tex = m_FirstFreeTexture;
|
|
if(Tex == -1)
|
|
{
|
|
size_t CurSize = m_vTextureIndices.size();
|
|
m_vTextureIndices.resize(CurSize * 2);
|
|
for(size_t i = 0; i < CurSize - 1; ++i)
|
|
{
|
|
m_vTextureIndices[CurSize + i] = CurSize + i + 1;
|
|
}
|
|
m_vTextureIndices.back() = -1;
|
|
|
|
Tex = CurSize;
|
|
}
|
|
m_FirstFreeTexture = m_vTextureIndices[Tex];
|
|
m_vTextureIndices[Tex] = -1;
|
|
|
|
int Tex2 = m_FirstFreeTexture;
|
|
if(Tex2 == -1)
|
|
{
|
|
size_t CurSize = m_vTextureIndices.size();
|
|
m_vTextureIndices.resize(CurSize * 2);
|
|
for(size_t i = 0; i < CurSize - 1; ++i)
|
|
{
|
|
m_vTextureIndices[CurSize + i] = CurSize + i + 1;
|
|
}
|
|
m_vTextureIndices.back() = -1;
|
|
|
|
Tex2 = CurSize;
|
|
}
|
|
m_FirstFreeTexture = m_vTextureIndices[Tex2];
|
|
m_vTextureIndices[Tex2] = -1;
|
|
|
|
CCommandBuffer::SCommand_TextTextures_Create Cmd;
|
|
Cmd.m_Slot = Tex;
|
|
Cmd.m_SlotOutline = Tex2;
|
|
Cmd.m_Width = Width;
|
|
Cmd.m_Height = Height;
|
|
|
|
Cmd.m_pTextData = pTextData;
|
|
Cmd.m_pTextOutlineData = pTextOutlineData;
|
|
|
|
AddCmd(
|
|
Cmd, [] { return true; }, "failed to load text textures.");
|
|
|
|
TextTexture = CreateTextureHandle(Tex);
|
|
TextOutlineTexture = CreateTextureHandle(Tex2);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CGraphics_Threaded::UnloadTextTextures(CTextureHandle &TextTexture, CTextureHandle &TextOutlineTexture)
|
|
{
|
|
CCommandBuffer::SCommand_TextTextures_Destroy Cmd;
|
|
Cmd.m_Slot = TextTexture.Id();
|
|
Cmd.m_SlotOutline = TextOutlineTexture.Id();
|
|
AddCmd(
|
|
Cmd, [] { return true; }, "failed to unload text textures.");
|
|
|
|
m_vTextureIndices[TextTexture.Id()] = m_FirstFreeTexture;
|
|
m_FirstFreeTexture = TextTexture.Id();
|
|
|
|
m_vTextureIndices[TextOutlineTexture.Id()] = m_FirstFreeTexture;
|
|
m_FirstFreeTexture = TextOutlineTexture.Id();
|
|
|
|
TextTexture.Invalidate();
|
|
TextOutlineTexture.Invalidate();
|
|
return true;
|
|
}
|
|
|
|
bool CGraphics_Threaded::UpdateTextTexture(CTextureHandle TextureID, int x, int y, int Width, int Height, const void *pData)
|
|
{
|
|
CCommandBuffer::SCommand_TextTexture_Update Cmd;
|
|
Cmd.m_Slot = TextureID.Id();
|
|
Cmd.m_X = x;
|
|
Cmd.m_Y = y;
|
|
Cmd.m_Width = Width;
|
|
Cmd.m_Height = Height;
|
|
|
|
// calculate memory usage
|
|
int MemSize = Width * Height;
|
|
|
|
// copy texture data
|
|
void *pTmpData = malloc(MemSize);
|
|
mem_copy(pTmpData, pData, MemSize);
|
|
Cmd.m_pData = pTmpData;
|
|
|
|
AddCmd(
|
|
Cmd, [] { return true; }, "failed to update text texture.");
|
|
return true;
|
|
}
|
|
|
|
int CGraphics_Threaded::LoadPNG(CImageInfo *pImg, const char *pFilename, int StorageType)
|
|
{
|
|
char aCompleteFilename[IO_MAX_PATH_LENGTH];
|
|
IOHANDLE File = m_pStorage->OpenFile(pFilename, IOFLAG_READ, StorageType, aCompleteFilename, sizeof(aCompleteFilename));
|
|
if(File)
|
|
{
|
|
io_seek(File, 0, IOSEEK_END);
|
|
unsigned int FileSize = io_tell(File);
|
|
io_seek(File, 0, IOSEEK_START);
|
|
|
|
TImageByteBuffer ByteBuffer;
|
|
SImageByteBuffer ImageByteBuffer(&ByteBuffer);
|
|
|
|
ByteBuffer.resize(FileSize);
|
|
io_read(File, &ByteBuffer.front(), FileSize);
|
|
|
|
io_close(File);
|
|
|
|
uint8_t *pImgBuffer = NULL;
|
|
EImageFormat ImageFormat;
|
|
int PngliteIncompatible;
|
|
if(::LoadPNG(ImageByteBuffer, pFilename, PngliteIncompatible, pImg->m_Width, pImg->m_Height, pImgBuffer, ImageFormat))
|
|
{
|
|
pImg->m_pData = pImgBuffer;
|
|
|
|
if(ImageFormat == IMAGE_FORMAT_RGB) // ignore_convention
|
|
pImg->m_Format = CImageInfo::FORMAT_RGB;
|
|
else if(ImageFormat == IMAGE_FORMAT_RGBA) // ignore_convention
|
|
pImg->m_Format = CImageInfo::FORMAT_RGBA;
|
|
else
|
|
{
|
|
free(pImgBuffer);
|
|
return 0;
|
|
}
|
|
|
|
if(m_WarnPngliteIncompatibleImages && PngliteIncompatible != 0)
|
|
{
|
|
SWarning Warning;
|
|
str_format(Warning.m_aWarningMsg, sizeof(Warning.m_aWarningMsg), Localize("\"%s\" is not compatible with pnglite and cannot be loaded by old DDNet versions: "), pFilename);
|
|
static const int FLAGS[] = {PNGLITE_COLOR_TYPE, PNGLITE_BIT_DEPTH, PNGLITE_INTERLACE_TYPE, PNGLITE_COMPRESSION_TYPE, PNGLITE_FILTER_TYPE};
|
|
static const char *EXPLANATION[] = {"color type", "bit depth", "interlace type", "compression type", "filter type"};
|
|
|
|
bool First = true;
|
|
for(int i = 0; i < (int)std::size(FLAGS); i++)
|
|
{
|
|
if((PngliteIncompatible & FLAGS[i]) != 0)
|
|
{
|
|
if(!First)
|
|
{
|
|
str_append(Warning.m_aWarningMsg, ", ", sizeof(Warning.m_aWarningMsg));
|
|
}
|
|
str_append(Warning.m_aWarningMsg, EXPLANATION[i], sizeof(Warning.m_aWarningMsg));
|
|
First = false;
|
|
}
|
|
}
|
|
str_append(Warning.m_aWarningMsg, " unsupported", sizeof(Warning.m_aWarningMsg));
|
|
m_vWarnings.emplace_back(Warning);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dbg_msg("game/png", "image had unsupported image format. filename='%s'", pFilename);
|
|
return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dbg_msg("game/png", "failed to open file. filename='%s'", pFilename);
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
void CGraphics_Threaded::FreePNG(CImageInfo *pImg)
|
|
{
|
|
free(pImg->m_pData);
|
|
pImg->m_pData = NULL;
|
|
}
|
|
|
|
bool CGraphics_Threaded::CheckImageDivisibility(const char *pFileName, CImageInfo &Img, int DivX, int DivY, bool AllowResize)
|
|
{
|
|
dbg_assert(DivX != 0 && DivY != 0, "Passing 0 to this function is not allowed.");
|
|
bool ImageIsValid = true;
|
|
bool WidthBroken = Img.m_Width == 0 || (Img.m_Width % DivX) != 0;
|
|
bool HeightBroken = Img.m_Height == 0 || (Img.m_Height % DivY) != 0;
|
|
if(WidthBroken || HeightBroken)
|
|
{
|
|
SWarning NewWarning;
|
|
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."), pFileName, DivX, DivY);
|
|
|
|
m_vWarnings.emplace_back(NewWarning);
|
|
|
|
ImageIsValid = false;
|
|
}
|
|
|
|
if(AllowResize && !ImageIsValid && Img.m_Width > 0 && Img.m_Height > 0)
|
|
{
|
|
int NewWidth = DivX;
|
|
int NewHeight = DivY;
|
|
if(WidthBroken)
|
|
{
|
|
NewWidth = maximum<int>(HighestBit(Img.m_Width), DivX);
|
|
NewHeight = (NewWidth / DivX) * DivY;
|
|
}
|
|
else
|
|
{
|
|
NewHeight = maximum<int>(HighestBit(Img.m_Height), DivY);
|
|
NewWidth = (NewHeight / DivY) * DivX;
|
|
}
|
|
|
|
int ColorChannelCount = 4;
|
|
if(Img.m_Format == CImageInfo::FORMAT_SINGLE_COMPONENT)
|
|
ColorChannelCount = 1;
|
|
else if(Img.m_Format == CImageInfo::FORMAT_RGB)
|
|
ColorChannelCount = 3;
|
|
else if(Img.m_Format == CImageInfo::FORMAT_RGBA)
|
|
ColorChannelCount = 4;
|
|
|
|
uint8_t *pNewImg = ResizeImage((uint8_t *)Img.m_pData, Img.m_Width, Img.m_Height, NewWidth, NewHeight, ColorChannelCount);
|
|
free(Img.m_pData);
|
|
Img.m_pData = pNewImg;
|
|
Img.m_Width = NewWidth;
|
|
Img.m_Height = NewHeight;
|
|
ImageIsValid = true;
|
|
}
|
|
|
|
return ImageIsValid;
|
|
}
|
|
|
|
bool CGraphics_Threaded::IsImageFormatRGBA(const char *pFileName, CImageInfo &Img)
|
|
{
|
|
if(Img.m_Format != CImageInfo::FORMAT_RGBA)
|
|
{
|
|
SWarning NewWarning;
|
|
char aText[128];
|
|
aText[0] = '\0';
|
|
if(pFileName)
|
|
{
|
|
str_format(aText, sizeof(aText), "\"%s\"", pFileName);
|
|
}
|
|
str_format(NewWarning.m_aWarningMsg, sizeof(NewWarning.m_aWarningMsg),
|
|
Localize("The format of texture %s is not RGBA which will cause visual bugs."), aText);
|
|
m_vWarnings.emplace_back(NewWarning);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void CGraphics_Threaded::CopyTextureBufferSub(uint8_t *pDestBuffer, uint8_t *pSourceBuffer, int FullWidth, int FullHeight, int ColorChannelCount, int SubOffsetX, int SubOffsetY, int SubCopyWidth, int SubCopyHeight)
|
|
{
|
|
for(int Y = 0; Y < SubCopyHeight; ++Y)
|
|
{
|
|
int ImgOffset = ((SubOffsetY + Y) * FullWidth * ColorChannelCount) + (SubOffsetX * ColorChannelCount);
|
|
int CopySize = SubCopyWidth * ColorChannelCount;
|
|
mem_copy(&pDestBuffer[ImgOffset], &pSourceBuffer[ImgOffset], CopySize);
|
|
}
|
|
}
|
|
|
|
void CGraphics_Threaded::CopyTextureFromTextureBufferSub(uint8_t *pDestBuffer, int DestWidth, int DestHeight, uint8_t *pSourceBuffer, int SrcWidth, int SrcHeight, int ColorChannelCount, int SrcSubOffsetX, int SrcSubOffsetY, int SrcSubCopyWidth, int SrcSubCopyHeight)
|
|
{
|
|
for(int Y = 0; Y < SrcSubCopyHeight; ++Y)
|
|
{
|
|
int SrcImgOffset = ((SrcSubOffsetY + Y) * SrcWidth * ColorChannelCount) + (SrcSubOffsetX * ColorChannelCount);
|
|
int DstImgOffset = (Y * DestWidth * ColorChannelCount);
|
|
int CopySize = SrcSubCopyWidth * ColorChannelCount;
|
|
mem_copy(&pDestBuffer[DstImgOffset], &pSourceBuffer[SrcImgOffset], CopySize);
|
|
}
|
|
}
|
|
|
|
void CGraphics_Threaded::KickCommandBuffer()
|
|
{
|
|
m_pBackend->RunBuffer(m_pCommandBuffer);
|
|
|
|
std::vector<std::string> WarningStrings;
|
|
if(m_pBackend->GetWarning(WarningStrings))
|
|
{
|
|
SWarning NewWarning;
|
|
std::string WarningStr;
|
|
for(const auto &WarnStr : WarningStrings)
|
|
WarningStr.append((WarnStr + "\n"));
|
|
str_copy(NewWarning.m_aWarningMsg, WarningStr.c_str());
|
|
m_vWarnings.emplace_back(NewWarning);
|
|
}
|
|
|
|
// swap buffer
|
|
m_CurrentCommandBuffer ^= 1;
|
|
m_pCommandBuffer = m_apCommandBuffers[m_CurrentCommandBuffer];
|
|
m_pCommandBuffer->Reset();
|
|
}
|
|
|
|
bool CGraphics_Threaded::ScreenshotDirect()
|
|
{
|
|
// add swap command
|
|
CImageInfo Image;
|
|
mem_zero(&Image, sizeof(Image));
|
|
|
|
bool DidSwap = false;
|
|
|
|
CCommandBuffer::SCommand_TrySwapAndScreenshot Cmd;
|
|
Cmd.m_pImage = &Image;
|
|
Cmd.m_pSwapped = &DidSwap;
|
|
AddCmd(
|
|
Cmd, [] { return true; }, "failed to take screenshot.");
|
|
|
|
// kick the buffer and wait for the result
|
|
KickCommandBuffer();
|
|
WaitForIdle();
|
|
|
|
if(Image.m_pData)
|
|
{
|
|
char aWholePath[IO_MAX_PATH_LENGTH];
|
|
char aBuf[64 + IO_MAX_PATH_LENGTH];
|
|
|
|
IOHANDLE File = m_pStorage->OpenFile(m_aScreenshotName, IOFLAG_WRITE, IStorage::TYPE_SAVE, aWholePath, sizeof(aWholePath));
|
|
if(File)
|
|
{
|
|
TImageByteBuffer ByteBuffer;
|
|
SImageByteBuffer ImageByteBuffer(&ByteBuffer);
|
|
|
|
if(SavePNG(IMAGE_FORMAT_RGBA, (const uint8_t *)Image.m_pData, ImageByteBuffer, Image.m_Width, Image.m_Height))
|
|
io_write(File, &ByteBuffer.front(), ByteBuffer.size());
|
|
io_close(File);
|
|
|
|
str_format(aBuf, sizeof(aBuf), "saved screenshot to '%s'", aWholePath);
|
|
}
|
|
else
|
|
{
|
|
str_format(aBuf, sizeof(aBuf), "failed to save screenshot to '%s'", aWholePath);
|
|
}
|
|
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "client", aBuf, ColorRGBA(1.0f, 0.6f, 0.3f, 1.0f));
|
|
|
|
free(Image.m_pData);
|
|
}
|
|
|
|
return DidSwap;
|
|
}
|
|
|
|
void CGraphics_Threaded::TextureSet(CTextureHandle TextureID)
|
|
{
|
|
dbg_assert(m_Drawing == 0, "called Graphics()->TextureSet within begin");
|
|
dbg_assert(!TextureID.IsValid() || m_vTextureIndices[TextureID.Id()] == -1, "Texture handle was not invalid, but also did not correlate to an existing texture.");
|
|
m_State.m_Texture = TextureID.Id();
|
|
}
|
|
|
|
void CGraphics_Threaded::Clear(float r, float g, float b, bool ForceClearNow)
|
|
{
|
|
CCommandBuffer::SCommand_Clear Cmd;
|
|
Cmd.m_Color.r = r;
|
|
Cmd.m_Color.g = g;
|
|
Cmd.m_Color.b = b;
|
|
Cmd.m_Color.a = 0;
|
|
Cmd.m_ForceClear = ForceClearNow;
|
|
AddCmd(
|
|
Cmd, [] { return true; }, "failed to clear graphics.");
|
|
}
|
|
|
|
void CGraphics_Threaded::QuadsBegin()
|
|
{
|
|
dbg_assert(m_Drawing == 0, "called Graphics()->QuadsBegin twice");
|
|
m_Drawing = DRAWING_QUADS;
|
|
|
|
QuadsSetSubset(0, 0, 1, 1);
|
|
QuadsSetRotation(0);
|
|
SetColor(1, 1, 1, 1);
|
|
}
|
|
|
|
void CGraphics_Threaded::QuadsEnd()
|
|
{
|
|
dbg_assert(m_Drawing == DRAWING_QUADS, "called Graphics()->QuadsEnd without begin");
|
|
FlushVertices();
|
|
m_Drawing = 0;
|
|
}
|
|
|
|
void CGraphics_Threaded::QuadsTex3DBegin()
|
|
{
|
|
QuadsBegin();
|
|
}
|
|
|
|
void CGraphics_Threaded::QuadsTex3DEnd()
|
|
{
|
|
dbg_assert(m_Drawing == DRAWING_QUADS, "called Graphics()->QuadsEnd without begin");
|
|
FlushVerticesTex3D();
|
|
m_Drawing = 0;
|
|
}
|
|
|
|
void CGraphics_Threaded::TrianglesBegin()
|
|
{
|
|
dbg_assert(m_Drawing == 0, "called Graphics()->TrianglesBegin twice");
|
|
m_Drawing = DRAWING_TRIANGLES;
|
|
|
|
QuadsSetSubset(0, 0, 1, 1);
|
|
QuadsSetRotation(0);
|
|
SetColor(1, 1, 1, 1);
|
|
}
|
|
|
|
void CGraphics_Threaded::TrianglesEnd()
|
|
{
|
|
dbg_assert(m_Drawing == DRAWING_TRIANGLES, "called Graphics()->TrianglesEnd without begin");
|
|
FlushVertices();
|
|
m_Drawing = 0;
|
|
}
|
|
|
|
void CGraphics_Threaded::QuadsEndKeepVertices()
|
|
{
|
|
dbg_assert(m_Drawing == DRAWING_QUADS, "called Graphics()->QuadsEndKeepVertices without begin");
|
|
FlushVertices(true);
|
|
m_Drawing = 0;
|
|
}
|
|
|
|
void CGraphics_Threaded::QuadsDrawCurrentVertices(bool KeepVertices)
|
|
{
|
|
m_Drawing = DRAWING_QUADS;
|
|
FlushVertices(KeepVertices);
|
|
m_Drawing = 0;
|
|
}
|
|
|
|
void CGraphics_Threaded::QuadsSetRotation(float Angle)
|
|
{
|
|
m_Rotation = Angle;
|
|
}
|
|
|
|
inline void clampf(float &Value, float Min, float Max)
|
|
{
|
|
if(Value > Max)
|
|
Value = Max;
|
|
else if(Value < Min)
|
|
Value = Min;
|
|
}
|
|
|
|
void CGraphics_Threaded::SetColorVertex(const CColorVertex *pArray, int Num)
|
|
{
|
|
dbg_assert(m_Drawing != 0, "called Graphics()->SetColorVertex without begin");
|
|
|
|
for(int i = 0; i < Num; ++i)
|
|
{
|
|
float r = pArray[i].m_R, g = pArray[i].m_G, b = pArray[i].m_B, a = pArray[i].m_A;
|
|
clampf(r, 0.f, 1.f);
|
|
clampf(g, 0.f, 1.f);
|
|
clampf(b, 0.f, 1.f);
|
|
clampf(a, 0.f, 1.f);
|
|
m_aColor[pArray[i].m_Index].r = (unsigned char)(r * 255.f);
|
|
m_aColor[pArray[i].m_Index].g = (unsigned char)(g * 255.f);
|
|
m_aColor[pArray[i].m_Index].b = (unsigned char)(b * 255.f);
|
|
m_aColor[pArray[i].m_Index].a = (unsigned char)(a * 255.f);
|
|
}
|
|
}
|
|
|
|
void CGraphics_Threaded::SetColor(float r, float g, float b, float a)
|
|
{
|
|
clampf(r, 0.f, 1.f);
|
|
clampf(g, 0.f, 1.f);
|
|
clampf(b, 0.f, 1.f);
|
|
clampf(a, 0.f, 1.f);
|
|
r *= 255.f;
|
|
g *= 255.f;
|
|
b *= 255.f;
|
|
a *= 255.f;
|
|
|
|
for(auto &Color : m_aColor)
|
|
{
|
|
Color.r = (unsigned char)(r);
|
|
Color.g = (unsigned char)(g);
|
|
Color.b = (unsigned char)(b);
|
|
Color.a = (unsigned char)(a);
|
|
}
|
|
}
|
|
|
|
void CGraphics_Threaded::SetColor(ColorRGBA Color)
|
|
{
|
|
SetColor(Color.r, Color.g, Color.b, Color.a);
|
|
}
|
|
|
|
void CGraphics_Threaded::SetColor4(ColorRGBA TopLeft, ColorRGBA TopRight, ColorRGBA BottomLeft, ColorRGBA BottomRight)
|
|
{
|
|
dbg_assert(m_Drawing != 0, "called Graphics()->SetColor without begin");
|
|
CColorVertex Array[4] = {
|
|
CColorVertex(0, TopLeft.r, TopLeft.g, TopLeft.b, TopLeft.a),
|
|
CColorVertex(1, TopRight.r, TopRight.g, TopRight.b, TopRight.a),
|
|
CColorVertex(2, BottomRight.r, BottomRight.g, BottomRight.b, BottomRight.a),
|
|
CColorVertex(3, BottomLeft.r, BottomLeft.g, BottomLeft.b, BottomLeft.a)};
|
|
SetColorVertex(Array, 4);
|
|
}
|
|
|
|
void CGraphics_Threaded::ChangeColorOfCurrentQuadVertices(float r, float g, float b, float a)
|
|
{
|
|
clampf(r, 0.f, 1.f);
|
|
clampf(g, 0.f, 1.f);
|
|
clampf(b, 0.f, 1.f);
|
|
clampf(a, 0.f, 1.f);
|
|
m_aColor[0].r = (unsigned char)(r * 255.f);
|
|
m_aColor[0].g = (unsigned char)(g * 255.f);
|
|
m_aColor[0].b = (unsigned char)(b * 255.f);
|
|
m_aColor[0].a = (unsigned char)(a * 255.f);
|
|
|
|
for(int i = 0; i < m_NumVertices; ++i)
|
|
{
|
|
SetColor(&m_aVertices[i], 0);
|
|
}
|
|
}
|
|
|
|
void CGraphics_Threaded::ChangeColorOfQuadVertices(int QuadOffset, unsigned char r, unsigned char g, unsigned char b, unsigned char a)
|
|
{
|
|
if(g_Config.m_GfxQuadAsTriangle && !m_GLUseTrianglesAsQuad)
|
|
{
|
|
m_aVertices[QuadOffset * 6].m_Color.r = r;
|
|
m_aVertices[QuadOffset * 6].m_Color.g = g;
|
|
m_aVertices[QuadOffset * 6].m_Color.b = b;
|
|
m_aVertices[QuadOffset * 6].m_Color.a = a;
|
|
|
|
m_aVertices[QuadOffset * 6 + 1].m_Color.r = r;
|
|
m_aVertices[QuadOffset * 6 + 1].m_Color.g = g;
|
|
m_aVertices[QuadOffset * 6 + 1].m_Color.b = b;
|
|
m_aVertices[QuadOffset * 6 + 1].m_Color.a = a;
|
|
|
|
m_aVertices[QuadOffset * 6 + 2].m_Color.r = r;
|
|
m_aVertices[QuadOffset * 6 + 2].m_Color.g = g;
|
|
m_aVertices[QuadOffset * 6 + 2].m_Color.b = b;
|
|
m_aVertices[QuadOffset * 6 + 2].m_Color.a = a;
|
|
|
|
m_aVertices[QuadOffset * 6 + 3].m_Color.r = r;
|
|
m_aVertices[QuadOffset * 6 + 3].m_Color.g = g;
|
|
m_aVertices[QuadOffset * 6 + 3].m_Color.b = b;
|
|
m_aVertices[QuadOffset * 6 + 3].m_Color.a = a;
|
|
|
|
m_aVertices[QuadOffset * 6 + 4].m_Color.r = r;
|
|
m_aVertices[QuadOffset * 6 + 4].m_Color.g = g;
|
|
m_aVertices[QuadOffset * 6 + 4].m_Color.b = b;
|
|
m_aVertices[QuadOffset * 6 + 4].m_Color.a = a;
|
|
|
|
m_aVertices[QuadOffset * 6 + 5].m_Color.r = r;
|
|
m_aVertices[QuadOffset * 6 + 5].m_Color.g = g;
|
|
m_aVertices[QuadOffset * 6 + 5].m_Color.b = b;
|
|
m_aVertices[QuadOffset * 6 + 5].m_Color.a = a;
|
|
}
|
|
else
|
|
{
|
|
m_aVertices[QuadOffset * 4].m_Color.r = r;
|
|
m_aVertices[QuadOffset * 4].m_Color.g = g;
|
|
m_aVertices[QuadOffset * 4].m_Color.b = b;
|
|
m_aVertices[QuadOffset * 4].m_Color.a = a;
|
|
|
|
m_aVertices[QuadOffset * 4 + 1].m_Color.r = r;
|
|
m_aVertices[QuadOffset * 4 + 1].m_Color.g = g;
|
|
m_aVertices[QuadOffset * 4 + 1].m_Color.b = b;
|
|
m_aVertices[QuadOffset * 4 + 1].m_Color.a = a;
|
|
|
|
m_aVertices[QuadOffset * 4 + 2].m_Color.r = r;
|
|
m_aVertices[QuadOffset * 4 + 2].m_Color.g = g;
|
|
m_aVertices[QuadOffset * 4 + 2].m_Color.b = b;
|
|
m_aVertices[QuadOffset * 4 + 2].m_Color.a = a;
|
|
|
|
m_aVertices[QuadOffset * 4 + 3].m_Color.r = r;
|
|
m_aVertices[QuadOffset * 4 + 3].m_Color.g = g;
|
|
m_aVertices[QuadOffset * 4 + 3].m_Color.b = b;
|
|
m_aVertices[QuadOffset * 4 + 3].m_Color.a = a;
|
|
}
|
|
}
|
|
|
|
void CGraphics_Threaded::QuadsSetSubset(float TlU, float TlV, float BrU, float BrV)
|
|
{
|
|
m_aTexture[0].u = TlU;
|
|
m_aTexture[1].u = BrU;
|
|
m_aTexture[0].v = TlV;
|
|
m_aTexture[1].v = TlV;
|
|
|
|
m_aTexture[3].u = TlU;
|
|
m_aTexture[2].u = BrU;
|
|
m_aTexture[3].v = BrV;
|
|
m_aTexture[2].v = BrV;
|
|
}
|
|
|
|
void CGraphics_Threaded::QuadsSetSubsetFree(
|
|
float x0, float y0, float x1, float y1,
|
|
float x2, float y2, float x3, float y3, int Index)
|
|
{
|
|
m_aTexture[0].u = x0;
|
|
m_aTexture[0].v = y0;
|
|
m_aTexture[1].u = x1;
|
|
m_aTexture[1].v = y1;
|
|
m_aTexture[2].u = x2;
|
|
m_aTexture[2].v = y2;
|
|
m_aTexture[3].u = x3;
|
|
m_aTexture[3].v = y3;
|
|
m_CurIndex = Index;
|
|
}
|
|
|
|
void CGraphics_Threaded::QuadsDraw(CQuadItem *pArray, int Num)
|
|
{
|
|
for(int i = 0; i < Num; ++i)
|
|
{
|
|
pArray[i].m_X -= pArray[i].m_Width / 2;
|
|
pArray[i].m_Y -= pArray[i].m_Height / 2;
|
|
}
|
|
|
|
QuadsDrawTL(pArray, Num);
|
|
}
|
|
|
|
void CGraphics_Threaded::QuadsDrawTL(const CQuadItem *pArray, int Num)
|
|
{
|
|
QuadsDrawTLImpl(m_aVertices, pArray, Num);
|
|
}
|
|
|
|
void CGraphics_Threaded::QuadsTex3DDrawTL(const CQuadItem *pArray, int Num)
|
|
{
|
|
int CurNumVert = m_NumVertices;
|
|
|
|
int VertNum = 0;
|
|
if(g_Config.m_GfxQuadAsTriangle && !m_GLUseTrianglesAsQuad)
|
|
{
|
|
VertNum = 6;
|
|
}
|
|
else
|
|
{
|
|
VertNum = 4;
|
|
}
|
|
|
|
for(int i = 0; i < Num; ++i)
|
|
{
|
|
for(int n = 0; n < VertNum; ++n)
|
|
{
|
|
if(HasTextureArrays())
|
|
m_aVerticesTex3D[CurNumVert + VertNum * i + n].m_Tex.w = (float)m_CurIndex;
|
|
else
|
|
m_aVerticesTex3D[CurNumVert + VertNum * i + n].m_Tex.w = ((float)m_CurIndex + 0.5f) / 256.f;
|
|
}
|
|
}
|
|
|
|
QuadsDrawTLImpl(m_aVerticesTex3D, pArray, Num);
|
|
}
|
|
|
|
void CGraphics_Threaded::QuadsDrawFreeform(const CFreeformItem *pArray, int Num)
|
|
{
|
|
dbg_assert(m_Drawing == DRAWING_QUADS || m_Drawing == DRAWING_TRIANGLES, "called Graphics()->QuadsDrawFreeform without begin");
|
|
|
|
if((g_Config.m_GfxQuadAsTriangle && !m_GLUseTrianglesAsQuad) || m_Drawing == DRAWING_TRIANGLES)
|
|
{
|
|
for(int i = 0; i < Num; ++i)
|
|
{
|
|
m_aVertices[m_NumVertices + 6 * i].m_Pos.x = pArray[i].m_X0;
|
|
m_aVertices[m_NumVertices + 6 * i].m_Pos.y = pArray[i].m_Y0;
|
|
m_aVertices[m_NumVertices + 6 * i].m_Tex = m_aTexture[0];
|
|
SetColor(&m_aVertices[m_NumVertices + 6 * i], 0);
|
|
|
|
m_aVertices[m_NumVertices + 6 * i + 1].m_Pos.x = pArray[i].m_X1;
|
|
m_aVertices[m_NumVertices + 6 * i + 1].m_Pos.y = pArray[i].m_Y1;
|
|
m_aVertices[m_NumVertices + 6 * i + 1].m_Tex = m_aTexture[1];
|
|
SetColor(&m_aVertices[m_NumVertices + 6 * i + 1], 1);
|
|
|
|
m_aVertices[m_NumVertices + 6 * i + 2].m_Pos.x = pArray[i].m_X3;
|
|
m_aVertices[m_NumVertices + 6 * i + 2].m_Pos.y = pArray[i].m_Y3;
|
|
m_aVertices[m_NumVertices + 6 * i + 2].m_Tex = m_aTexture[3];
|
|
SetColor(&m_aVertices[m_NumVertices + 6 * i + 2], 3);
|
|
|
|
m_aVertices[m_NumVertices + 6 * i + 3].m_Pos.x = pArray[i].m_X0;
|
|
m_aVertices[m_NumVertices + 6 * i + 3].m_Pos.y = pArray[i].m_Y0;
|
|
m_aVertices[m_NumVertices + 6 * i + 3].m_Tex = m_aTexture[0];
|
|
SetColor(&m_aVertices[m_NumVertices + 6 * i + 3], 0);
|
|
|
|
m_aVertices[m_NumVertices + 6 * i + 4].m_Pos.x = pArray[i].m_X3;
|
|
m_aVertices[m_NumVertices + 6 * i + 4].m_Pos.y = pArray[i].m_Y3;
|
|
m_aVertices[m_NumVertices + 6 * i + 4].m_Tex = m_aTexture[3];
|
|
SetColor(&m_aVertices[m_NumVertices + 6 * i + 4], 3);
|
|
|
|
m_aVertices[m_NumVertices + 6 * i + 5].m_Pos.x = pArray[i].m_X2;
|
|
m_aVertices[m_NumVertices + 6 * i + 5].m_Pos.y = pArray[i].m_Y2;
|
|
m_aVertices[m_NumVertices + 6 * i + 5].m_Tex = m_aTexture[2];
|
|
SetColor(&m_aVertices[m_NumVertices + 6 * i + 5], 2);
|
|
}
|
|
|
|
AddVertices(3 * 2 * Num);
|
|
}
|
|
else
|
|
{
|
|
for(int i = 0; i < Num; ++i)
|
|
{
|
|
m_aVertices[m_NumVertices + 4 * i].m_Pos.x = pArray[i].m_X0;
|
|
m_aVertices[m_NumVertices + 4 * i].m_Pos.y = pArray[i].m_Y0;
|
|
m_aVertices[m_NumVertices + 4 * i].m_Tex = m_aTexture[0];
|
|
SetColor(&m_aVertices[m_NumVertices + 4 * i], 0);
|
|
|
|
m_aVertices[m_NumVertices + 4 * i + 1].m_Pos.x = pArray[i].m_X1;
|
|
m_aVertices[m_NumVertices + 4 * i + 1].m_Pos.y = pArray[i].m_Y1;
|
|
m_aVertices[m_NumVertices + 4 * i + 1].m_Tex = m_aTexture[1];
|
|
SetColor(&m_aVertices[m_NumVertices + 4 * i + 1], 1);
|
|
|
|
m_aVertices[m_NumVertices + 4 * i + 2].m_Pos.x = pArray[i].m_X3;
|
|
m_aVertices[m_NumVertices + 4 * i + 2].m_Pos.y = pArray[i].m_Y3;
|
|
m_aVertices[m_NumVertices + 4 * i + 2].m_Tex = m_aTexture[3];
|
|
SetColor(&m_aVertices[m_NumVertices + 4 * i + 2], 3);
|
|
|
|
m_aVertices[m_NumVertices + 4 * i + 3].m_Pos.x = pArray[i].m_X2;
|
|
m_aVertices[m_NumVertices + 4 * i + 3].m_Pos.y = pArray[i].m_Y2;
|
|
m_aVertices[m_NumVertices + 4 * i + 3].m_Tex = m_aTexture[2];
|
|
SetColor(&m_aVertices[m_NumVertices + 4 * i + 3], 2);
|
|
}
|
|
|
|
AddVertices(4 * Num);
|
|
}
|
|
}
|
|
|
|
void CGraphics_Threaded::QuadsText(float x, float y, float Size, const char *pText)
|
|
{
|
|
float StartX = x;
|
|
|
|
while(*pText)
|
|
{
|
|
char c = *pText;
|
|
pText++;
|
|
|
|
if(c == '\n')
|
|
{
|
|
x = StartX;
|
|
y += Size;
|
|
}
|
|
else
|
|
{
|
|
QuadsSetSubset(
|
|
(c % 16) / 16.0f,
|
|
(c / 16) / 16.0f,
|
|
(c % 16) / 16.0f + 1.0f / 16.0f,
|
|
(c / 16) / 16.0f + 1.0f / 16.0f);
|
|
|
|
CQuadItem QuadItem(x, y, Size, Size);
|
|
QuadsDrawTL(&QuadItem, 1);
|
|
x += Size / 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CGraphics_Threaded::DrawRectExt(float x, float y, float w, float h, float r, int Corners)
|
|
{
|
|
const int NumSegments = 8;
|
|
const float SegmentsAngle = pi / 2 / NumSegments;
|
|
IGraphics::CFreeformItem aFreeform[NumSegments * 4];
|
|
size_t NumItems = 0;
|
|
|
|
for(int i = 0; i < NumSegments; i += 2)
|
|
{
|
|
float a1 = i * SegmentsAngle;
|
|
float a2 = (i + 1) * SegmentsAngle;
|
|
float a3 = (i + 2) * SegmentsAngle;
|
|
float Ca1 = std::cos(a1);
|
|
float Ca2 = std::cos(a2);
|
|
float Ca3 = std::cos(a3);
|
|
float Sa1 = std::sin(a1);
|
|
float Sa2 = std::sin(a2);
|
|
float Sa3 = std::sin(a3);
|
|
|
|
if(Corners & CORNER_TL)
|
|
aFreeform[NumItems++] = IGraphics::CFreeformItem(
|
|
x + r, y + r,
|
|
x + (1 - Ca1) * r, y + (1 - Sa1) * r,
|
|
x + (1 - Ca3) * r, y + (1 - Sa3) * r,
|
|
x + (1 - Ca2) * r, y + (1 - Sa2) * r);
|
|
|
|
if(Corners & CORNER_TR)
|
|
aFreeform[NumItems++] = IGraphics::CFreeformItem(
|
|
x + w - r, y + r,
|
|
x + w - r + Ca1 * r, y + (1 - Sa1) * r,
|
|
x + w - r + Ca3 * r, y + (1 - Sa3) * r,
|
|
x + w - r + Ca2 * r, y + (1 - Sa2) * r);
|
|
|
|
if(Corners & CORNER_BL)
|
|
aFreeform[NumItems++] = IGraphics::CFreeformItem(
|
|
x + r, y + h - r,
|
|
x + (1 - Ca1) * r, y + h - r + Sa1 * r,
|
|
x + (1 - Ca3) * r, y + h - r + Sa3 * r,
|
|
x + (1 - Ca2) * r, y + h - r + Sa2 * r);
|
|
|
|
if(Corners & CORNER_BR)
|
|
aFreeform[NumItems++] = IGraphics::CFreeformItem(
|
|
x + w - r, y + h - r,
|
|
x + w - r + Ca1 * r, y + h - r + Sa1 * r,
|
|
x + w - r + Ca3 * r, y + h - r + Sa3 * r,
|
|
x + w - r + Ca2 * r, y + h - r + Sa2 * r);
|
|
}
|
|
QuadsDrawFreeform(aFreeform, NumItems);
|
|
|
|
CQuadItem aQuads[9];
|
|
NumItems = 0;
|
|
aQuads[NumItems++] = CQuadItem(x + r, y + r, w - r * 2, h - r * 2); // center
|
|
aQuads[NumItems++] = CQuadItem(x + r, y, w - r * 2, r); // top
|
|
aQuads[NumItems++] = CQuadItem(x + r, y + h - r, w - r * 2, r); // bottom
|
|
aQuads[NumItems++] = CQuadItem(x, y + r, r, h - r * 2); // left
|
|
aQuads[NumItems++] = CQuadItem(x + w - r, y + r, r, h - r * 2); // right
|
|
|
|
if(!(Corners & CORNER_TL))
|
|
aQuads[NumItems++] = CQuadItem(x, y, r, r);
|
|
if(!(Corners & CORNER_TR))
|
|
aQuads[NumItems++] = CQuadItem(x + w, y, -r, r);
|
|
if(!(Corners & CORNER_BL))
|
|
aQuads[NumItems++] = CQuadItem(x, y + h, r, -r);
|
|
if(!(Corners & CORNER_BR))
|
|
aQuads[NumItems++] = CQuadItem(x + w, y + h, -r, -r);
|
|
|
|
QuadsDrawTL(aQuads, NumItems);
|
|
}
|
|
|
|
void CGraphics_Threaded::DrawRectExt4(float x, float y, float w, float h, ColorRGBA ColorTopLeft, ColorRGBA ColorTopRight, ColorRGBA ColorBottomLeft, ColorRGBA ColorBottomRight, float r, int Corners)
|
|
{
|
|
if(Corners == 0 || r == 0.0f)
|
|
{
|
|
SetColor4(ColorTopLeft, ColorTopRight, ColorBottomLeft, ColorBottomRight);
|
|
CQuadItem ItemQ = CQuadItem(x, y, w, h);
|
|
QuadsDrawTL(&ItemQ, 1);
|
|
return;
|
|
}
|
|
|
|
const int NumSegments = 8;
|
|
const float SegmentsAngle = pi / 2 / NumSegments;
|
|
for(int i = 0; i < NumSegments; i += 2)
|
|
{
|
|
float a1 = i * SegmentsAngle;
|
|
float a2 = (i + 1) * SegmentsAngle;
|
|
float a3 = (i + 2) * SegmentsAngle;
|
|
float Ca1 = std::cos(a1);
|
|
float Ca2 = std::cos(a2);
|
|
float Ca3 = std::cos(a3);
|
|
float Sa1 = std::sin(a1);
|
|
float Sa2 = std::sin(a2);
|
|
float Sa3 = std::sin(a3);
|
|
|
|
if(Corners & CORNER_TL)
|
|
{
|
|
SetColor(ColorTopLeft);
|
|
IGraphics::CFreeformItem ItemF = IGraphics::CFreeformItem(
|
|
x + r, y + r,
|
|
x + (1 - Ca1) * r, y + (1 - Sa1) * r,
|
|
x + (1 - Ca3) * r, y + (1 - Sa3) * r,
|
|
x + (1 - Ca2) * r, y + (1 - Sa2) * r);
|
|
QuadsDrawFreeform(&ItemF, 1);
|
|
}
|
|
|
|
if(Corners & CORNER_TR)
|
|
{
|
|
SetColor(ColorTopRight);
|
|
IGraphics::CFreeformItem ItemF = IGraphics::CFreeformItem(
|
|
x + w - r, y + r,
|
|
x + w - r + Ca1 * r, y + (1 - Sa1) * r,
|
|
x + w - r + Ca3 * r, y + (1 - Sa3) * r,
|
|
x + w - r + Ca2 * r, y + (1 - Sa2) * r);
|
|
QuadsDrawFreeform(&ItemF, 1);
|
|
}
|
|
|
|
if(Corners & CORNER_BL)
|
|
{
|
|
SetColor(ColorBottomLeft);
|
|
IGraphics::CFreeformItem ItemF = IGraphics::CFreeformItem(
|
|
x + r, y + h - r,
|
|
x + (1 - Ca1) * r, y + h - r + Sa1 * r,
|
|
x + (1 - Ca3) * r, y + h - r + Sa3 * r,
|
|
x + (1 - Ca2) * r, y + h - r + Sa2 * r);
|
|
QuadsDrawFreeform(&ItemF, 1);
|
|
}
|
|
|
|
if(Corners & CORNER_BR)
|
|
{
|
|
SetColor(ColorBottomRight);
|
|
IGraphics::CFreeformItem ItemF = IGraphics::CFreeformItem(
|
|
x + w - r, y + h - r,
|
|
x + w - r + Ca1 * r, y + h - r + Sa1 * r,
|
|
x + w - r + Ca3 * r, y + h - r + Sa3 * r,
|
|
x + w - r + Ca2 * r, y + h - r + Sa2 * r);
|
|
QuadsDrawFreeform(&ItemF, 1);
|
|
}
|
|
|
|
if(Corners & CORNER_ITL)
|
|
{
|
|
SetColor(ColorTopLeft);
|
|
IGraphics::CFreeformItem ItemF = IGraphics::CFreeformItem(
|
|
x, y,
|
|
x + (1 - Ca1) * r, y - r + Sa1 * r,
|
|
x + (1 - Ca3) * r, y - r + Sa3 * r,
|
|
x + (1 - Ca2) * r, y - r + Sa2 * r);
|
|
QuadsDrawFreeform(&ItemF, 1);
|
|
}
|
|
|
|
if(Corners & CORNER_ITR)
|
|
{
|
|
SetColor(ColorTopRight);
|
|
IGraphics::CFreeformItem ItemF = IGraphics::CFreeformItem(
|
|
x + w, y,
|
|
x + w - r + Ca1 * r, y - r + Sa1 * r,
|
|
x + w - r + Ca3 * r, y - r + Sa3 * r,
|
|
x + w - r + Ca2 * r, y - r + Sa2 * r);
|
|
QuadsDrawFreeform(&ItemF, 1);
|
|
}
|
|
|
|
if(Corners & CORNER_IBL)
|
|
{
|
|
SetColor(ColorBottomLeft);
|
|
IGraphics::CFreeformItem ItemF = IGraphics::CFreeformItem(
|
|
x, y + h,
|
|
x + (1 - Ca1) * r, y + h + (1 - Sa1) * r,
|
|
x + (1 - Ca3) * r, y + h + (1 - Sa3) * r,
|
|
x + (1 - Ca2) * r, y + h + (1 - Sa2) * r);
|
|
QuadsDrawFreeform(&ItemF, 1);
|
|
}
|
|
|
|
if(Corners & CORNER_IBR)
|
|
{
|
|
SetColor(ColorBottomRight);
|
|
IGraphics::CFreeformItem ItemF = IGraphics::CFreeformItem(
|
|
x + w, y + h,
|
|
x + w - r + Ca1 * r, y + h + (1 - Sa1) * r,
|
|
x + w - r + Ca3 * r, y + h + (1 - Sa3) * r,
|
|
x + w - r + Ca2 * r, y + h + (1 - Sa2) * r);
|
|
QuadsDrawFreeform(&ItemF, 1);
|
|
}
|
|
}
|
|
|
|
SetColor4(ColorTopLeft, ColorTopRight, ColorBottomLeft, ColorBottomRight);
|
|
CQuadItem ItemQ = CQuadItem(x + r, y + r, w - r * 2, h - r * 2); // center
|
|
QuadsDrawTL(&ItemQ, 1);
|
|
|
|
SetColor4(ColorTopLeft, ColorTopRight, ColorTopLeft, ColorTopRight);
|
|
ItemQ = CQuadItem(x + r, y, w - r * 2, r); // top
|
|
QuadsDrawTL(&ItemQ, 1);
|
|
|
|
SetColor4(ColorBottomLeft, ColorBottomRight, ColorBottomLeft, ColorBottomRight);
|
|
ItemQ = CQuadItem(x + r, y + h - r, w - r * 2, r); // bottom
|
|
QuadsDrawTL(&ItemQ, 1);
|
|
|
|
SetColor4(ColorTopLeft, ColorTopLeft, ColorBottomLeft, ColorBottomLeft);
|
|
ItemQ = CQuadItem(x, y + r, r, h - r * 2); // left
|
|
QuadsDrawTL(&ItemQ, 1);
|
|
|
|
SetColor4(ColorTopRight, ColorTopRight, ColorBottomRight, ColorBottomRight);
|
|
ItemQ = CQuadItem(x + w - r, y + r, r, h - r * 2); // right
|
|
QuadsDrawTL(&ItemQ, 1);
|
|
|
|
if(!(Corners & CORNER_TL))
|
|
{
|
|
SetColor(ColorTopLeft);
|
|
ItemQ = CQuadItem(x, y, r, r);
|
|
QuadsDrawTL(&ItemQ, 1);
|
|
}
|
|
|
|
if(!(Corners & CORNER_TR))
|
|
{
|
|
SetColor(ColorTopRight);
|
|
ItemQ = CQuadItem(x + w, y, -r, r);
|
|
QuadsDrawTL(&ItemQ, 1);
|
|
}
|
|
|
|
if(!(Corners & CORNER_BL))
|
|
{
|
|
SetColor(ColorBottomLeft);
|
|
ItemQ = CQuadItem(x, y + h, r, -r);
|
|
QuadsDrawTL(&ItemQ, 1);
|
|
}
|
|
|
|
if(!(Corners & CORNER_BR))
|
|
{
|
|
SetColor(ColorBottomRight);
|
|
ItemQ = CQuadItem(x + w, y + h, -r, -r);
|
|
QuadsDrawTL(&ItemQ, 1);
|
|
}
|
|
}
|
|
|
|
int CGraphics_Threaded::CreateRectQuadContainer(float x, float y, float w, float h, float r, int Corners)
|
|
{
|
|
int ContainerIndex = CreateQuadContainer(false);
|
|
|
|
if(Corners == 0 || r == 0.0f)
|
|
{
|
|
CQuadItem ItemQ = CQuadItem(x, y, w, h);
|
|
QuadContainerAddQuads(ContainerIndex, &ItemQ, 1);
|
|
QuadContainerUpload(ContainerIndex);
|
|
QuadContainerChangeAutomaticUpload(ContainerIndex, true);
|
|
return ContainerIndex;
|
|
}
|
|
|
|
const int NumSegments = 8;
|
|
const float SegmentsAngle = pi / 2 / NumSegments;
|
|
IGraphics::CFreeformItem aFreeform[NumSegments * 4];
|
|
size_t NumItems = 0;
|
|
|
|
for(int i = 0; i < NumSegments; i += 2)
|
|
{
|
|
float a1 = i * SegmentsAngle;
|
|
float a2 = (i + 1) * SegmentsAngle;
|
|
float a3 = (i + 2) * SegmentsAngle;
|
|
float Ca1 = std::cos(a1);
|
|
float Ca2 = std::cos(a2);
|
|
float Ca3 = std::cos(a3);
|
|
float Sa1 = std::sin(a1);
|
|
float Sa2 = std::sin(a2);
|
|
float Sa3 = std::sin(a3);
|
|
|
|
if(Corners & CORNER_TL)
|
|
aFreeform[NumItems++] = IGraphics::CFreeformItem(
|
|
x + r, y + r,
|
|
x + (1 - Ca1) * r, y + (1 - Sa1) * r,
|
|
x + (1 - Ca3) * r, y + (1 - Sa3) * r,
|
|
x + (1 - Ca2) * r, y + (1 - Sa2) * r);
|
|
|
|
if(Corners & CORNER_TR)
|
|
aFreeform[NumItems++] = IGraphics::CFreeformItem(
|
|
x + w - r, y + r,
|
|
x + w - r + Ca1 * r, y + (1 - Sa1) * r,
|
|
x + w - r + Ca3 * r, y + (1 - Sa3) * r,
|
|
x + w - r + Ca2 * r, y + (1 - Sa2) * r);
|
|
|
|
if(Corners & CORNER_BL)
|
|
aFreeform[NumItems++] = IGraphics::CFreeformItem(
|
|
x + r, y + h - r,
|
|
x + (1 - Ca1) * r, y + h - r + Sa1 * r,
|
|
x + (1 - Ca3) * r, y + h - r + Sa3 * r,
|
|
x + (1 - Ca2) * r, y + h - r + Sa2 * r);
|
|
|
|
if(Corners & CORNER_BR)
|
|
aFreeform[NumItems++] = IGraphics::CFreeformItem(
|
|
x + w - r, y + h - r,
|
|
x + w - r + Ca1 * r, y + h - r + Sa1 * r,
|
|
x + w - r + Ca3 * r, y + h - r + Sa3 * r,
|
|
x + w - r + Ca2 * r, y + h - r + Sa2 * r);
|
|
}
|
|
|
|
if(NumItems > 0)
|
|
QuadContainerAddQuads(ContainerIndex, aFreeform, NumItems);
|
|
|
|
CQuadItem aQuads[9];
|
|
NumItems = 0;
|
|
aQuads[NumItems++] = CQuadItem(x + r, y + r, w - r * 2, h - r * 2); // center
|
|
aQuads[NumItems++] = CQuadItem(x + r, y, w - r * 2, r); // top
|
|
aQuads[NumItems++] = CQuadItem(x + r, y + h - r, w - r * 2, r); // bottom
|
|
aQuads[NumItems++] = CQuadItem(x, y + r, r, h - r * 2); // left
|
|
aQuads[NumItems++] = CQuadItem(x + w - r, y + r, r, h - r * 2); // right
|
|
|
|
if(!(Corners & CORNER_TL))
|
|
aQuads[NumItems++] = CQuadItem(x, y, r, r);
|
|
if(!(Corners & CORNER_TR))
|
|
aQuads[NumItems++] = CQuadItem(x + w, y, -r, r);
|
|
if(!(Corners & CORNER_BL))
|
|
aQuads[NumItems++] = CQuadItem(x, y + h, r, -r);
|
|
if(!(Corners & CORNER_BR))
|
|
aQuads[NumItems++] = CQuadItem(x + w, y + h, -r, -r);
|
|
|
|
if(NumItems > 0)
|
|
QuadContainerAddQuads(ContainerIndex, aQuads, NumItems);
|
|
|
|
QuadContainerUpload(ContainerIndex);
|
|
QuadContainerChangeAutomaticUpload(ContainerIndex, true);
|
|
|
|
return ContainerIndex;
|
|
}
|
|
|
|
void CGraphics_Threaded::DrawRect(float x, float y, float w, float h, ColorRGBA Color, int Corners, float Rounding)
|
|
{
|
|
TextureClear();
|
|
QuadsBegin();
|
|
SetColor(Color);
|
|
DrawRectExt(x, y, w, h, Rounding, Corners);
|
|
QuadsEnd();
|
|
}
|
|
|
|
void CGraphics_Threaded::DrawRect4(float x, float y, float w, float h, ColorRGBA ColorTopLeft, ColorRGBA ColorTopRight, ColorRGBA ColorBottomLeft, ColorRGBA ColorBottomRight, int Corners, float Rounding)
|
|
{
|
|
TextureClear();
|
|
QuadsBegin();
|
|
DrawRectExt4(x, y, w, h, ColorTopLeft, ColorTopRight, ColorBottomLeft, ColorBottomRight, Rounding, Corners);
|
|
QuadsEnd();
|
|
}
|
|
|
|
void CGraphics_Threaded::DrawCircle(float CenterX, float CenterY, float Radius, int Segments)
|
|
{
|
|
IGraphics::CFreeformItem aItems[32];
|
|
size_t NumItems = 0;
|
|
const float SegmentsAngle = 2 * pi / Segments;
|
|
for(int i = 0; i < Segments; i += 2)
|
|
{
|
|
const float a1 = i * SegmentsAngle;
|
|
const float a2 = (i + 1) * SegmentsAngle;
|
|
const float a3 = (i + 2) * SegmentsAngle;
|
|
aItems[NumItems++] = IGraphics::CFreeformItem(
|
|
CenterX, CenterY,
|
|
CenterX + std::cos(a1) * Radius, CenterY + std::sin(a1) * Radius,
|
|
CenterX + std::cos(a3) * Radius, CenterY + std::sin(a3) * Radius,
|
|
CenterX + std::cos(a2) * Radius, CenterY + std::sin(a2) * Radius);
|
|
if(NumItems == std::size(aItems))
|
|
{
|
|
QuadsDrawFreeform(aItems, std::size(aItems));
|
|
NumItems = 0;
|
|
}
|
|
}
|
|
if(NumItems)
|
|
QuadsDrawFreeform(aItems, NumItems);
|
|
}
|
|
|
|
void CGraphics_Threaded::RenderTileLayer(int BufferContainerIndex, const ColorRGBA &Color, char **pOffsets, unsigned int *pIndicedVertexDrawNum, size_t NumIndicesOffset)
|
|
{
|
|
if(NumIndicesOffset == 0)
|
|
return;
|
|
|
|
// add the VertexArrays and draw
|
|
CCommandBuffer::SCommand_RenderTileLayer Cmd;
|
|
Cmd.m_State = m_State;
|
|
Cmd.m_IndicesDrawNum = NumIndicesOffset;
|
|
Cmd.m_BufferContainerIndex = BufferContainerIndex;
|
|
Cmd.m_Color = Color;
|
|
|
|
void *pData = m_pCommandBuffer->AllocData((sizeof(char *) + sizeof(unsigned int)) * NumIndicesOffset);
|
|
if(pData == 0x0)
|
|
{
|
|
// kick command buffer and try again
|
|
KickCommandBuffer();
|
|
|
|
pData = m_pCommandBuffer->AllocData((sizeof(char *) + sizeof(unsigned int)) * NumIndicesOffset);
|
|
if(pData == 0x0)
|
|
{
|
|
dbg_msg("graphics", "failed to allocate data for vertices");
|
|
return;
|
|
}
|
|
}
|
|
Cmd.m_pIndicesOffsets = (char **)pData;
|
|
Cmd.m_pDrawCount = (unsigned int *)(((char *)pData) + (sizeof(char *) * NumIndicesOffset));
|
|
|
|
if(!AddCmd(
|
|
Cmd, [&] {
|
|
pData = m_pCommandBuffer->AllocData((sizeof(char *) + sizeof(unsigned int)) * NumIndicesOffset);
|
|
if(pData == 0x0)
|
|
{
|
|
dbg_msg("graphics", "failed to allocate data for vertices");
|
|
return false;
|
|
}
|
|
Cmd.m_pIndicesOffsets = (char **)pData;
|
|
Cmd.m_pDrawCount = (unsigned int *)(((char *)pData) + (sizeof(char *) * NumIndicesOffset));
|
|
return true;
|
|
},
|
|
"failed to allocate memory for render command"))
|
|
{
|
|
return;
|
|
}
|
|
|
|
mem_copy(Cmd.m_pIndicesOffsets, pOffsets, sizeof(char *) * NumIndicesOffset);
|
|
mem_copy(Cmd.m_pDrawCount, pIndicedVertexDrawNum, sizeof(unsigned int) * NumIndicesOffset);
|
|
|
|
m_pCommandBuffer->AddRenderCalls(NumIndicesOffset);
|
|
// todo max indices group check!!
|
|
}
|
|
|
|
void CGraphics_Threaded::RenderBorderTiles(int BufferContainerIndex, const ColorRGBA &Color, char *pIndexBufferOffset, const vec2 &Offset, const vec2 &Dir, int JumpIndex, unsigned int DrawNum)
|
|
{
|
|
if(DrawNum == 0)
|
|
return;
|
|
// Draw a border tile a lot of times
|
|
CCommandBuffer::SCommand_RenderBorderTile Cmd;
|
|
Cmd.m_State = m_State;
|
|
Cmd.m_DrawNum = DrawNum;
|
|
Cmd.m_BufferContainerIndex = BufferContainerIndex;
|
|
Cmd.m_Color = Color;
|
|
|
|
Cmd.m_pIndicesOffset = pIndexBufferOffset;
|
|
Cmd.m_JumpIndex = JumpIndex;
|
|
|
|
Cmd.m_Offset = Offset;
|
|
Cmd.m_Dir = Dir;
|
|
|
|
// check if we have enough free memory in the commandbuffer
|
|
if(!AddCmd(
|
|
Cmd, [] { return true; }, "failed to allocate memory for render command"))
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_pCommandBuffer->AddRenderCalls(1);
|
|
}
|
|
|
|
void CGraphics_Threaded::RenderBorderTileLines(int BufferContainerIndex, const ColorRGBA &Color, char *pIndexBufferOffset, const vec2 &Offset, const vec2 &Dir, unsigned int IndexDrawNum, unsigned int RedrawNum)
|
|
{
|
|
if(IndexDrawNum == 0 || RedrawNum == 0)
|
|
return;
|
|
// Draw a border tile a lot of times
|
|
CCommandBuffer::SCommand_RenderBorderTileLine Cmd;
|
|
Cmd.m_State = m_State;
|
|
Cmd.m_IndexDrawNum = IndexDrawNum;
|
|
Cmd.m_DrawNum = RedrawNum;
|
|
Cmd.m_BufferContainerIndex = BufferContainerIndex;
|
|
Cmd.m_Color = Color;
|
|
|
|
Cmd.m_pIndicesOffset = pIndexBufferOffset;
|
|
|
|
Cmd.m_Offset = Offset;
|
|
Cmd.m_Dir = Dir;
|
|
|
|
// check if we have enough free memory in the commandbuffer
|
|
if(!AddCmd(
|
|
Cmd, [] { return true; }, "failed to allocate memory for render command"))
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_pCommandBuffer->AddRenderCalls(1);
|
|
}
|
|
|
|
void CGraphics_Threaded::RenderQuadLayer(int BufferContainerIndex, SQuadRenderInfo *pQuadInfo, int QuadNum, int QuadOffset)
|
|
{
|
|
if(QuadNum == 0)
|
|
return;
|
|
|
|
// add the VertexArrays and draw
|
|
CCommandBuffer::SCommand_RenderQuadLayer Cmd;
|
|
Cmd.m_State = m_State;
|
|
Cmd.m_QuadNum = QuadNum;
|
|
Cmd.m_QuadOffset = QuadOffset;
|
|
Cmd.m_BufferContainerIndex = BufferContainerIndex;
|
|
|
|
Cmd.m_pQuadInfo = (SQuadRenderInfo *)AllocCommandBufferData(QuadNum * sizeof(SQuadRenderInfo));
|
|
if(Cmd.m_pQuadInfo == 0x0)
|
|
return;
|
|
|
|
// check if we have enough free memory in the commandbuffer
|
|
if(!AddCmd(
|
|
Cmd, [&] {
|
|
Cmd.m_pQuadInfo = (SQuadRenderInfo *)m_pCommandBuffer->AllocData(QuadNum * sizeof(SQuadRenderInfo));
|
|
if(Cmd.m_pQuadInfo == 0x0)
|
|
{
|
|
dbg_msg("graphics", "failed to allocate data for the quad info");
|
|
return false;
|
|
}
|
|
return true;
|
|
},
|
|
"failed to allocate memory for render quad command"))
|
|
{
|
|
return;
|
|
}
|
|
|
|
mem_copy(Cmd.m_pQuadInfo, pQuadInfo, sizeof(SQuadRenderInfo) * QuadNum);
|
|
|
|
m_pCommandBuffer->AddRenderCalls(((QuadNum - 1) / gs_GraphicsMaxQuadsRenderCount) + 1);
|
|
}
|
|
|
|
void CGraphics_Threaded::RenderText(int BufferContainerIndex, int TextQuadNum, int TextureSize, int TextureTextIndex, int TextureTextOutlineIndex, const ColorRGBA &TextColor, const ColorRGBA &TextOutlineColor)
|
|
{
|
|
if(BufferContainerIndex == -1)
|
|
return;
|
|
|
|
CCommandBuffer::SCommand_RenderText Cmd;
|
|
Cmd.m_State = m_State;
|
|
Cmd.m_BufferContainerIndex = BufferContainerIndex;
|
|
Cmd.m_DrawNum = TextQuadNum * 6;
|
|
Cmd.m_TextureSize = TextureSize;
|
|
Cmd.m_TextTextureIndex = TextureTextIndex;
|
|
Cmd.m_TextOutlineTextureIndex = TextureTextOutlineIndex;
|
|
Cmd.m_TextColor = TextColor;
|
|
Cmd.m_TextOutlineColor = TextOutlineColor;
|
|
|
|
if(!AddCmd(
|
|
Cmd, [] { return true; }, "failed to allocate memory for render text command"))
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_pCommandBuffer->AddRenderCalls(1);
|
|
}
|
|
|
|
int CGraphics_Threaded::CreateQuadContainer(bool AutomaticUpload)
|
|
{
|
|
int Index = -1;
|
|
if(m_FirstFreeQuadContainer == -1)
|
|
{
|
|
Index = m_vQuadContainers.size();
|
|
m_vQuadContainers.emplace_back(AutomaticUpload);
|
|
}
|
|
else
|
|
{
|
|
Index = m_FirstFreeQuadContainer;
|
|
m_FirstFreeQuadContainer = m_vQuadContainers[Index].m_FreeIndex;
|
|
m_vQuadContainers[Index].m_FreeIndex = Index;
|
|
}
|
|
|
|
return Index;
|
|
}
|
|
|
|
void CGraphics_Threaded::QuadContainerChangeAutomaticUpload(int ContainerIndex, bool AutomaticUpload)
|
|
{
|
|
SQuadContainer &Container = m_vQuadContainers[ContainerIndex];
|
|
Container.m_AutomaticUpload = AutomaticUpload;
|
|
}
|
|
|
|
void CGraphics_Threaded::QuadContainerUpload(int ContainerIndex)
|
|
{
|
|
if(IsQuadContainerBufferingEnabled())
|
|
{
|
|
SQuadContainer &Container = m_vQuadContainers[ContainerIndex];
|
|
if(!Container.m_vQuads.empty())
|
|
{
|
|
if(Container.m_QuadBufferObjectIndex == -1)
|
|
{
|
|
size_t UploadDataSize = Container.m_vQuads.size() * sizeof(SQuadContainer::SQuad);
|
|
Container.m_QuadBufferObjectIndex = CreateBufferObject(UploadDataSize, Container.m_vQuads.data(), 0);
|
|
}
|
|
else
|
|
{
|
|
size_t UploadDataSize = Container.m_vQuads.size() * sizeof(SQuadContainer::SQuad);
|
|
RecreateBufferObject(Container.m_QuadBufferObjectIndex, UploadDataSize, Container.m_vQuads.data(), 0);
|
|
}
|
|
|
|
if(Container.m_QuadBufferContainerIndex == -1)
|
|
{
|
|
SBufferContainerInfo Info;
|
|
Info.m_Stride = sizeof(CCommandBuffer::SVertex);
|
|
Info.m_VertBufferBindingIndex = Container.m_QuadBufferObjectIndex;
|
|
|
|
Info.m_vAttributes.emplace_back();
|
|
SBufferContainerInfo::SAttribute *pAttr = &Info.m_vAttributes.back();
|
|
pAttr->m_DataTypeCount = 2;
|
|
pAttr->m_FuncType = 0;
|
|
pAttr->m_Normalized = false;
|
|
pAttr->m_pOffset = 0;
|
|
pAttr->m_Type = GRAPHICS_TYPE_FLOAT;
|
|
Info.m_vAttributes.emplace_back();
|
|
pAttr = &Info.m_vAttributes.back();
|
|
pAttr->m_DataTypeCount = 2;
|
|
pAttr->m_FuncType = 0;
|
|
pAttr->m_Normalized = false;
|
|
pAttr->m_pOffset = (void *)(sizeof(float) * 2);
|
|
pAttr->m_Type = GRAPHICS_TYPE_FLOAT;
|
|
Info.m_vAttributes.emplace_back();
|
|
pAttr = &Info.m_vAttributes.back();
|
|
pAttr->m_DataTypeCount = 4;
|
|
pAttr->m_FuncType = 0;
|
|
pAttr->m_Normalized = true;
|
|
pAttr->m_pOffset = (void *)(sizeof(float) * 2 + sizeof(float) * 2);
|
|
pAttr->m_Type = GRAPHICS_TYPE_UNSIGNED_BYTE;
|
|
|
|
Container.m_QuadBufferContainerIndex = CreateBufferContainer(&Info);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int CGraphics_Threaded::QuadContainerAddQuads(int ContainerIndex, CQuadItem *pArray, int Num)
|
|
{
|
|
SQuadContainer &Container = m_vQuadContainers[ContainerIndex];
|
|
|
|
if((int)Container.m_vQuads.size() > Num + CCommandBuffer::CCommandBuffer::MAX_VERTICES)
|
|
return -1;
|
|
|
|
int RetOff = (int)Container.m_vQuads.size();
|
|
|
|
for(int i = 0; i < Num; ++i)
|
|
{
|
|
Container.m_vQuads.emplace_back();
|
|
SQuadContainer::SQuad &Quad = Container.m_vQuads.back();
|
|
|
|
Quad.m_aVertices[0].m_Pos.x = pArray[i].m_X;
|
|
Quad.m_aVertices[0].m_Pos.y = pArray[i].m_Y;
|
|
Quad.m_aVertices[0].m_Tex = m_aTexture[0];
|
|
SetColor(&Quad.m_aVertices[0], 0);
|
|
|
|
Quad.m_aVertices[1].m_Pos.x = pArray[i].m_X + pArray[i].m_Width;
|
|
Quad.m_aVertices[1].m_Pos.y = pArray[i].m_Y;
|
|
Quad.m_aVertices[1].m_Tex = m_aTexture[1];
|
|
SetColor(&Quad.m_aVertices[1], 1);
|
|
|
|
Quad.m_aVertices[2].m_Pos.x = pArray[i].m_X + pArray[i].m_Width;
|
|
Quad.m_aVertices[2].m_Pos.y = pArray[i].m_Y + pArray[i].m_Height;
|
|
Quad.m_aVertices[2].m_Tex = m_aTexture[2];
|
|
SetColor(&Quad.m_aVertices[2], 2);
|
|
|
|
Quad.m_aVertices[3].m_Pos.x = pArray[i].m_X;
|
|
Quad.m_aVertices[3].m_Pos.y = pArray[i].m_Y + pArray[i].m_Height;
|
|
Quad.m_aVertices[3].m_Tex = m_aTexture[3];
|
|
SetColor(&Quad.m_aVertices[3], 3);
|
|
|
|
if(m_Rotation != 0)
|
|
{
|
|
CCommandBuffer::SPoint Center;
|
|
Center.x = pArray[i].m_X + pArray[i].m_Width / 2;
|
|
Center.y = pArray[i].m_Y + pArray[i].m_Height / 2;
|
|
|
|
Rotate(Center, Quad.m_aVertices, 4);
|
|
}
|
|
}
|
|
|
|
if(Container.m_AutomaticUpload)
|
|
QuadContainerUpload(ContainerIndex);
|
|
|
|
return RetOff;
|
|
}
|
|
|
|
int CGraphics_Threaded::QuadContainerAddQuads(int ContainerIndex, CFreeformItem *pArray, int Num)
|
|
{
|
|
SQuadContainer &Container = m_vQuadContainers[ContainerIndex];
|
|
|
|
if((int)Container.m_vQuads.size() > Num + CCommandBuffer::CCommandBuffer::MAX_VERTICES)
|
|
return -1;
|
|
|
|
int RetOff = (int)Container.m_vQuads.size();
|
|
|
|
for(int i = 0; i < Num; ++i)
|
|
{
|
|
Container.m_vQuads.emplace_back();
|
|
SQuadContainer::SQuad &Quad = Container.m_vQuads.back();
|
|
|
|
Quad.m_aVertices[0].m_Pos.x = pArray[i].m_X0;
|
|
Quad.m_aVertices[0].m_Pos.y = pArray[i].m_Y0;
|
|
Quad.m_aVertices[0].m_Tex = m_aTexture[0];
|
|
SetColor(&Quad.m_aVertices[0], 0);
|
|
|
|
Quad.m_aVertices[1].m_Pos.x = pArray[i].m_X1;
|
|
Quad.m_aVertices[1].m_Pos.y = pArray[i].m_Y1;
|
|
Quad.m_aVertices[1].m_Tex = m_aTexture[1];
|
|
SetColor(&Quad.m_aVertices[1], 1);
|
|
|
|
Quad.m_aVertices[2].m_Pos.x = pArray[i].m_X3;
|
|
Quad.m_aVertices[2].m_Pos.y = pArray[i].m_Y3;
|
|
Quad.m_aVertices[2].m_Tex = m_aTexture[3];
|
|
SetColor(&Quad.m_aVertices[2], 3);
|
|
|
|
Quad.m_aVertices[3].m_Pos.x = pArray[i].m_X2;
|
|
Quad.m_aVertices[3].m_Pos.y = pArray[i].m_Y2;
|
|
Quad.m_aVertices[3].m_Tex = m_aTexture[2];
|
|
SetColor(&Quad.m_aVertices[3], 2);
|
|
}
|
|
|
|
if(Container.m_AutomaticUpload)
|
|
QuadContainerUpload(ContainerIndex);
|
|
|
|
return RetOff;
|
|
}
|
|
|
|
void CGraphics_Threaded::QuadContainerReset(int ContainerIndex)
|
|
{
|
|
if(ContainerIndex == -1)
|
|
return;
|
|
|
|
SQuadContainer &Container = m_vQuadContainers[ContainerIndex];
|
|
if(IsQuadContainerBufferingEnabled())
|
|
DeleteBufferContainer(Container.m_QuadBufferContainerIndex, true);
|
|
Container.m_vQuads.clear();
|
|
Container.m_QuadBufferObjectIndex = -1;
|
|
}
|
|
|
|
void CGraphics_Threaded::DeleteQuadContainer(int &ContainerIndex)
|
|
{
|
|
if(ContainerIndex == -1)
|
|
return;
|
|
|
|
QuadContainerReset(ContainerIndex);
|
|
|
|
// also clear the container index
|
|
m_vQuadContainers[ContainerIndex].m_FreeIndex = m_FirstFreeQuadContainer;
|
|
m_FirstFreeQuadContainer = ContainerIndex;
|
|
ContainerIndex = -1;
|
|
}
|
|
|
|
void CGraphics_Threaded::RenderQuadContainer(int ContainerIndex, int QuadDrawNum)
|
|
{
|
|
RenderQuadContainer(ContainerIndex, 0, QuadDrawNum);
|
|
}
|
|
|
|
void CGraphics_Threaded::RenderQuadContainer(int ContainerIndex, int QuadOffset, int QuadDrawNum, bool ChangeWrapMode)
|
|
{
|
|
SQuadContainer &Container = m_vQuadContainers[ContainerIndex];
|
|
|
|
if(QuadDrawNum == -1)
|
|
QuadDrawNum = (int)Container.m_vQuads.size() - QuadOffset;
|
|
|
|
if((int)Container.m_vQuads.size() < QuadOffset + QuadDrawNum || QuadDrawNum == 0)
|
|
return;
|
|
|
|
if(IsQuadContainerBufferingEnabled())
|
|
{
|
|
if(Container.m_QuadBufferContainerIndex == -1)
|
|
return;
|
|
|
|
if(ChangeWrapMode)
|
|
WrapClamp();
|
|
|
|
CCommandBuffer::SCommand_RenderQuadContainer Cmd;
|
|
Cmd.m_State = m_State;
|
|
Cmd.m_DrawNum = (unsigned int)QuadDrawNum * 6;
|
|
Cmd.m_pOffset = (void *)(QuadOffset * 6 * sizeof(unsigned int));
|
|
Cmd.m_BufferContainerIndex = Container.m_QuadBufferContainerIndex;
|
|
|
|
if(!AddCmd(
|
|
Cmd, [] { return true; }, "failed to allocate memory for render quad container"))
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_pCommandBuffer->AddRenderCalls(1);
|
|
}
|
|
else
|
|
{
|
|
if(g_Config.m_GfxQuadAsTriangle)
|
|
{
|
|
for(int i = 0; i < QuadDrawNum; ++i)
|
|
{
|
|
SQuadContainer::SQuad &Quad = Container.m_vQuads[QuadOffset + i];
|
|
m_aVertices[i * 6] = Quad.m_aVertices[0];
|
|
m_aVertices[i * 6 + 1] = Quad.m_aVertices[1];
|
|
m_aVertices[i * 6 + 2] = Quad.m_aVertices[2];
|
|
m_aVertices[i * 6 + 3] = Quad.m_aVertices[0];
|
|
m_aVertices[i * 6 + 4] = Quad.m_aVertices[2];
|
|
m_aVertices[i * 6 + 5] = Quad.m_aVertices[3];
|
|
m_NumVertices += 6;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mem_copy(m_aVertices, &Container.m_vQuads[QuadOffset], sizeof(CCommandBuffer::SVertex) * 4 * QuadDrawNum);
|
|
m_NumVertices += 4 * QuadDrawNum;
|
|
}
|
|
m_Drawing = DRAWING_QUADS;
|
|
if(ChangeWrapMode)
|
|
WrapClamp();
|
|
FlushVertices(false);
|
|
m_Drawing = 0;
|
|
}
|
|
WrapNormal();
|
|
}
|
|
|
|
void CGraphics_Threaded::RenderQuadContainerEx(int ContainerIndex, int QuadOffset, int QuadDrawNum, float X, float Y, float ScaleX, float ScaleY)
|
|
{
|
|
SQuadContainer &Container = m_vQuadContainers[ContainerIndex];
|
|
|
|
if((int)Container.m_vQuads.size() < QuadOffset + 1)
|
|
return;
|
|
|
|
if(QuadDrawNum == -1)
|
|
QuadDrawNum = (int)Container.m_vQuads.size() - QuadOffset;
|
|
|
|
if(IsQuadContainerBufferingEnabled())
|
|
{
|
|
if(Container.m_QuadBufferContainerIndex == -1)
|
|
return;
|
|
|
|
SQuadContainer::SQuad &Quad = Container.m_vQuads[QuadOffset];
|
|
CCommandBuffer::SCommand_RenderQuadContainerEx Cmd;
|
|
|
|
WrapClamp();
|
|
|
|
float ScreenX0, ScreenY0, ScreenX1, ScreenY1;
|
|
GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1);
|
|
MapScreen((ScreenX0 - X) / ScaleX, (ScreenY0 - Y) / ScaleY, (ScreenX1 - X) / ScaleX, (ScreenY1 - Y) / ScaleY);
|
|
Cmd.m_State = m_State;
|
|
MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1);
|
|
|
|
Cmd.m_DrawNum = QuadDrawNum * 6;
|
|
Cmd.m_pOffset = (void *)(QuadOffset * 6 * sizeof(unsigned int));
|
|
Cmd.m_BufferContainerIndex = Container.m_QuadBufferContainerIndex;
|
|
|
|
Cmd.m_VertexColor.r = (float)m_aColor[0].r / 255.f;
|
|
Cmd.m_VertexColor.g = (float)m_aColor[0].g / 255.f;
|
|
Cmd.m_VertexColor.b = (float)m_aColor[0].b / 255.f;
|
|
Cmd.m_VertexColor.a = (float)m_aColor[0].a / 255.f;
|
|
|
|
Cmd.m_Rotation = m_Rotation;
|
|
|
|
// rotate before positioning
|
|
Cmd.m_Center.x = Quad.m_aVertices[0].m_Pos.x + (Quad.m_aVertices[1].m_Pos.x - Quad.m_aVertices[0].m_Pos.x) / 2.f;
|
|
Cmd.m_Center.y = Quad.m_aVertices[0].m_Pos.y + (Quad.m_aVertices[2].m_Pos.y - Quad.m_aVertices[0].m_Pos.y) / 2.f;
|
|
|
|
if(!AddCmd(
|
|
Cmd, [] { return true; }, "failed to allocate memory for render quad container extended"))
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_pCommandBuffer->AddRenderCalls(1);
|
|
}
|
|
else
|
|
{
|
|
if(g_Config.m_GfxQuadAsTriangle)
|
|
{
|
|
for(int i = 0; i < QuadDrawNum; ++i)
|
|
{
|
|
SQuadContainer::SQuad &Quad = Container.m_vQuads[QuadOffset + i];
|
|
m_aVertices[i * 6 + 0] = Quad.m_aVertices[0];
|
|
m_aVertices[i * 6 + 1] = Quad.m_aVertices[1];
|
|
m_aVertices[i * 6 + 2] = Quad.m_aVertices[2];
|
|
m_aVertices[i * 6 + 3] = Quad.m_aVertices[0];
|
|
m_aVertices[i * 6 + 4] = Quad.m_aVertices[2];
|
|
m_aVertices[i * 6 + 5] = Quad.m_aVertices[3];
|
|
|
|
for(int n = 0; n < 6; ++n)
|
|
{
|
|
m_aVertices[i * 6 + n].m_Pos.x *= ScaleX;
|
|
m_aVertices[i * 6 + n].m_Pos.y *= ScaleY;
|
|
|
|
SetColor(&m_aVertices[i * 6 + n], 0);
|
|
}
|
|
|
|
if(m_Rotation != 0)
|
|
{
|
|
CCommandBuffer::SPoint Center;
|
|
Center.x = m_aVertices[i * 6 + 0].m_Pos.x + (m_aVertices[i * 6 + 1].m_Pos.x - m_aVertices[i * 6 + 0].m_Pos.x) / 2.f;
|
|
Center.y = m_aVertices[i * 6 + 0].m_Pos.y + (m_aVertices[i * 6 + 2].m_Pos.y - m_aVertices[i * 6 + 0].m_Pos.y) / 2.f;
|
|
|
|
Rotate(Center, &m_aVertices[i * 6 + 0], 6);
|
|
}
|
|
|
|
for(int n = 0; n < 6; ++n)
|
|
{
|
|
m_aVertices[i * 6 + n].m_Pos.x += X;
|
|
m_aVertices[i * 6 + n].m_Pos.y += Y;
|
|
}
|
|
m_NumVertices += 6;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mem_copy(m_aVertices, &Container.m_vQuads[QuadOffset], sizeof(CCommandBuffer::SVertex) * 4 * QuadDrawNum);
|
|
for(int i = 0; i < QuadDrawNum; ++i)
|
|
{
|
|
for(int n = 0; n < 4; ++n)
|
|
{
|
|
m_aVertices[i * 4 + n].m_Pos.x *= ScaleX;
|
|
m_aVertices[i * 4 + n].m_Pos.y *= ScaleY;
|
|
SetColor(&m_aVertices[i * 4 + n], 0);
|
|
}
|
|
|
|
if(m_Rotation != 0)
|
|
{
|
|
CCommandBuffer::SPoint Center;
|
|
Center.x = m_aVertices[i * 4 + 0].m_Pos.x + (m_aVertices[i * 4 + 1].m_Pos.x - m_aVertices[i * 4 + 0].m_Pos.x) / 2.f;
|
|
Center.y = m_aVertices[i * 4 + 0].m_Pos.y + (m_aVertices[i * 4 + 2].m_Pos.y - m_aVertices[i * 4 + 0].m_Pos.y) / 2.f;
|
|
|
|
Rotate(Center, &m_aVertices[i * 4 + 0], 4);
|
|
}
|
|
|
|
for(int n = 0; n < 4; ++n)
|
|
{
|
|
m_aVertices[i * 4 + n].m_Pos.x += X;
|
|
m_aVertices[i * 4 + n].m_Pos.y += Y;
|
|
}
|
|
m_NumVertices += 4;
|
|
}
|
|
}
|
|
m_Drawing = DRAWING_QUADS;
|
|
WrapClamp();
|
|
FlushVertices(false);
|
|
m_Drawing = 0;
|
|
}
|
|
WrapNormal();
|
|
}
|
|
|
|
void CGraphics_Threaded::RenderQuadContainerAsSprite(int ContainerIndex, int QuadOffset, float X, float Y, float ScaleX, float ScaleY)
|
|
{
|
|
RenderQuadContainerEx(ContainerIndex, QuadOffset, 1, X, Y, ScaleX, ScaleY);
|
|
}
|
|
|
|
void CGraphics_Threaded::RenderQuadContainerAsSpriteMultiple(int ContainerIndex, int QuadOffset, int DrawCount, SRenderSpriteInfo *pRenderInfo)
|
|
{
|
|
SQuadContainer &Container = m_vQuadContainers[ContainerIndex];
|
|
|
|
if(DrawCount == 0)
|
|
return;
|
|
|
|
if(IsQuadContainerBufferingEnabled())
|
|
{
|
|
if(Container.m_QuadBufferContainerIndex == -1)
|
|
return;
|
|
|
|
WrapClamp();
|
|
SQuadContainer::SQuad &Quad = Container.m_vQuads[0];
|
|
CCommandBuffer::SCommand_RenderQuadContainerAsSpriteMultiple Cmd;
|
|
|
|
Cmd.m_State = m_State;
|
|
|
|
Cmd.m_DrawNum = 1 * 6;
|
|
Cmd.m_DrawCount = DrawCount;
|
|
Cmd.m_pOffset = (void *)(QuadOffset * 6 * sizeof(unsigned int));
|
|
Cmd.m_BufferContainerIndex = Container.m_QuadBufferContainerIndex;
|
|
|
|
Cmd.m_VertexColor.r = (float)m_aColor[0].r / 255.f;
|
|
Cmd.m_VertexColor.g = (float)m_aColor[0].g / 255.f;
|
|
Cmd.m_VertexColor.b = (float)m_aColor[0].b / 255.f;
|
|
Cmd.m_VertexColor.a = (float)m_aColor[0].a / 255.f;
|
|
|
|
// rotate before positioning
|
|
Cmd.m_Center.x = Quad.m_aVertices[0].m_Pos.x + (Quad.m_aVertices[1].m_Pos.x - Quad.m_aVertices[0].m_Pos.x) / 2.f;
|
|
Cmd.m_Center.y = Quad.m_aVertices[0].m_Pos.y + (Quad.m_aVertices[2].m_Pos.y - Quad.m_aVertices[0].m_Pos.y) / 2.f;
|
|
|
|
Cmd.m_pRenderInfo = (IGraphics::SRenderSpriteInfo *)m_pCommandBuffer->AllocData(sizeof(IGraphics::SRenderSpriteInfo) * DrawCount);
|
|
if(Cmd.m_pRenderInfo == 0x0)
|
|
{
|
|
// kick command buffer and try again
|
|
KickCommandBuffer();
|
|
|
|
Cmd.m_pRenderInfo = (IGraphics::SRenderSpriteInfo *)m_pCommandBuffer->AllocData(sizeof(IGraphics::SRenderSpriteInfo) * DrawCount);
|
|
if(Cmd.m_pRenderInfo == 0x0)
|
|
{
|
|
dbg_msg("graphics", "failed to allocate data for render info");
|
|
return;
|
|
}
|
|
}
|
|
|
|
if(!AddCmd(
|
|
Cmd, [&] {
|
|
Cmd.m_pRenderInfo = (IGraphics::SRenderSpriteInfo *)m_pCommandBuffer->AllocData(sizeof(IGraphics::SRenderSpriteInfo) * DrawCount);
|
|
if(Cmd.m_pRenderInfo == 0x0)
|
|
{
|
|
dbg_msg("graphics", "failed to allocate data for render info");
|
|
return false;
|
|
}
|
|
return true;
|
|
},
|
|
"failed to allocate memory for render quad container sprite"))
|
|
{
|
|
return;
|
|
}
|
|
|
|
mem_copy(Cmd.m_pRenderInfo, pRenderInfo, sizeof(IGraphics::SRenderSpriteInfo) * DrawCount);
|
|
|
|
m_pCommandBuffer->AddRenderCalls(((DrawCount - 1) / gs_GraphicsMaxParticlesRenderCount) + 1);
|
|
|
|
WrapNormal();
|
|
}
|
|
else
|
|
{
|
|
for(int i = 0; i < DrawCount; ++i)
|
|
{
|
|
QuadsSetRotation(pRenderInfo[i].m_Rotation);
|
|
RenderQuadContainerAsSprite(ContainerIndex, QuadOffset, pRenderInfo[i].m_Pos.x, pRenderInfo[i].m_Pos.y, pRenderInfo[i].m_Scale, pRenderInfo[i].m_Scale);
|
|
}
|
|
}
|
|
}
|
|
|
|
void *CGraphics_Threaded::AllocCommandBufferData(unsigned AllocSize)
|
|
{
|
|
void *pData = m_pCommandBuffer->AllocData(AllocSize);
|
|
if(pData == 0x0)
|
|
{
|
|
// kick command buffer and try again
|
|
KickCommandBuffer();
|
|
|
|
pData = m_pCommandBuffer->AllocData(AllocSize);
|
|
if(pData == 0x0)
|
|
{
|
|
dbg_msg("graphics", "failed to allocate data for command buffer");
|
|
return NULL;
|
|
}
|
|
}
|
|
return pData;
|
|
}
|
|
|
|
int CGraphics_Threaded::CreateBufferObject(size_t UploadDataSize, void *pUploadData, int CreateFlags, bool IsMovedPointer)
|
|
{
|
|
int Index = -1;
|
|
if(m_FirstFreeBufferObjectIndex == -1)
|
|
{
|
|
Index = m_vBufferObjectIndices.size();
|
|
m_vBufferObjectIndices.push_back(Index);
|
|
}
|
|
else
|
|
{
|
|
Index = m_FirstFreeBufferObjectIndex;
|
|
m_FirstFreeBufferObjectIndex = m_vBufferObjectIndices[Index];
|
|
m_vBufferObjectIndices[Index] = Index;
|
|
}
|
|
|
|
CCommandBuffer::SCommand_CreateBufferObject Cmd;
|
|
Cmd.m_BufferIndex = Index;
|
|
Cmd.m_DataSize = UploadDataSize;
|
|
Cmd.m_DeletePointer = IsMovedPointer;
|
|
Cmd.m_Flags = CreateFlags;
|
|
|
|
if(IsMovedPointer)
|
|
{
|
|
Cmd.m_pUploadData = pUploadData;
|
|
|
|
if(!AddCmd(
|
|
Cmd, [] { return true; }, "failed to allocate memory for update buffer object command"))
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(UploadDataSize <= CMD_BUFFER_DATA_BUFFER_SIZE)
|
|
{
|
|
Cmd.m_pUploadData = AllocCommandBufferData(UploadDataSize);
|
|
if(Cmd.m_pUploadData == NULL)
|
|
return -1;
|
|
|
|
if(!AddCmd(
|
|
Cmd, [&] {
|
|
Cmd.m_pUploadData = m_pCommandBuffer->AllocData(UploadDataSize);
|
|
if(Cmd.m_pUploadData == 0x0)
|
|
{
|
|
dbg_msg("graphics", "failed to allocate data for upload data");
|
|
return false;
|
|
}
|
|
return true;
|
|
},
|
|
"failed to allocate memory for create buffer object command"))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
mem_copy(Cmd.m_pUploadData, pUploadData, UploadDataSize);
|
|
}
|
|
else
|
|
{
|
|
Cmd.m_pUploadData = NULL;
|
|
|
|
if(!AddCmd(
|
|
Cmd, [] { return true; }, "failed to allocate memory for create buffer object command"))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
// update the buffer instead
|
|
size_t UploadDataOffset = 0;
|
|
while(UploadDataSize > 0)
|
|
{
|
|
size_t UpdateSize = (UploadDataSize > CMD_BUFFER_DATA_BUFFER_SIZE ? CMD_BUFFER_DATA_BUFFER_SIZE : UploadDataSize);
|
|
|
|
UpdateBufferObjectInternal(Index, UpdateSize, (((char *)pUploadData) + UploadDataOffset), (void *)UploadDataOffset);
|
|
|
|
UploadDataOffset += UpdateSize;
|
|
UploadDataSize -= UpdateSize;
|
|
}
|
|
}
|
|
}
|
|
|
|
return Index;
|
|
}
|
|
|
|
void CGraphics_Threaded::RecreateBufferObject(int BufferIndex, size_t UploadDataSize, void *pUploadData, int CreateFlags, bool IsMovedPointer)
|
|
{
|
|
CCommandBuffer::SCommand_RecreateBufferObject Cmd;
|
|
Cmd.m_BufferIndex = BufferIndex;
|
|
Cmd.m_DataSize = UploadDataSize;
|
|
Cmd.m_DeletePointer = IsMovedPointer;
|
|
Cmd.m_Flags = CreateFlags;
|
|
|
|
if(IsMovedPointer)
|
|
{
|
|
Cmd.m_pUploadData = pUploadData;
|
|
|
|
if(!AddCmd(
|
|
Cmd, [] { return true; }, "failed to allocate memory for recreate buffer object command"))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(UploadDataSize <= CMD_BUFFER_DATA_BUFFER_SIZE)
|
|
{
|
|
Cmd.m_pUploadData = AllocCommandBufferData(UploadDataSize);
|
|
if(Cmd.m_pUploadData == NULL)
|
|
return;
|
|
|
|
if(!AddCmd(
|
|
Cmd, [&] {
|
|
Cmd.m_pUploadData = m_pCommandBuffer->AllocData(UploadDataSize);
|
|
if(Cmd.m_pUploadData == 0x0)
|
|
{
|
|
dbg_msg("graphics", "failed to allocate data for upload data");
|
|
return false;
|
|
}
|
|
return true;
|
|
},
|
|
"failed to allocate memory for recreate buffer object command"))
|
|
{
|
|
return;
|
|
}
|
|
|
|
mem_copy(Cmd.m_pUploadData, pUploadData, UploadDataSize);
|
|
}
|
|
else
|
|
{
|
|
Cmd.m_pUploadData = NULL;
|
|
|
|
if(!AddCmd(
|
|
Cmd, [] { return true; }, "failed to allocate memory for update buffer object command"))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// update the buffer instead
|
|
size_t UploadDataOffset = 0;
|
|
while(UploadDataSize > 0)
|
|
{
|
|
size_t UpdateSize = (UploadDataSize > CMD_BUFFER_DATA_BUFFER_SIZE ? CMD_BUFFER_DATA_BUFFER_SIZE : UploadDataSize);
|
|
|
|
UpdateBufferObjectInternal(BufferIndex, UpdateSize, (((char *)pUploadData) + UploadDataOffset), (void *)UploadDataOffset);
|
|
|
|
UploadDataOffset += UpdateSize;
|
|
UploadDataSize -= UpdateSize;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CGraphics_Threaded::UpdateBufferObjectInternal(int BufferIndex, size_t UploadDataSize, void *pUploadData, void *pOffset, bool IsMovedPointer)
|
|
{
|
|
CCommandBuffer::SCommand_UpdateBufferObject Cmd;
|
|
Cmd.m_BufferIndex = BufferIndex;
|
|
Cmd.m_DataSize = UploadDataSize;
|
|
Cmd.m_pOffset = pOffset;
|
|
Cmd.m_DeletePointer = IsMovedPointer;
|
|
|
|
if(IsMovedPointer)
|
|
{
|
|
Cmd.m_pUploadData = pUploadData;
|
|
|
|
if(!AddCmd(
|
|
Cmd, [] { return true; }, "failed to allocate memory for update buffer object command"))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Cmd.m_pUploadData = AllocCommandBufferData(UploadDataSize);
|
|
if(Cmd.m_pUploadData == NULL)
|
|
return;
|
|
|
|
if(!AddCmd(
|
|
Cmd, [&] {
|
|
Cmd.m_pUploadData = m_pCommandBuffer->AllocData(UploadDataSize);
|
|
if(Cmd.m_pUploadData == 0x0)
|
|
{
|
|
dbg_msg("graphics", "failed to allocate data for upload data");
|
|
return false;
|
|
}
|
|
return true;
|
|
},
|
|
"failed to allocate memory for update buffer object command"))
|
|
{
|
|
return;
|
|
}
|
|
|
|
mem_copy(Cmd.m_pUploadData, pUploadData, UploadDataSize);
|
|
}
|
|
}
|
|
|
|
void CGraphics_Threaded::CopyBufferObjectInternal(int WriteBufferIndex, int ReadBufferIndex, size_t WriteOffset, size_t ReadOffset, size_t CopyDataSize)
|
|
{
|
|
CCommandBuffer::SCommand_CopyBufferObject Cmd;
|
|
Cmd.m_WriteBufferIndex = WriteBufferIndex;
|
|
Cmd.m_ReadBufferIndex = ReadBufferIndex;
|
|
Cmd.m_WriteOffset = WriteOffset;
|
|
Cmd.m_ReadOffset = ReadOffset;
|
|
Cmd.m_CopySize = CopyDataSize;
|
|
|
|
if(!AddCmd(
|
|
Cmd, [] { return true; }, "failed to allocate memory for copy buffer object command"))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
void CGraphics_Threaded::DeleteBufferObject(int BufferIndex)
|
|
{
|
|
if(BufferIndex == -1)
|
|
return;
|
|
|
|
CCommandBuffer::SCommand_DeleteBufferObject Cmd;
|
|
Cmd.m_BufferIndex = BufferIndex;
|
|
|
|
if(!AddCmd(
|
|
Cmd, [] { return true; }, "failed to allocate memory for delete buffer object command"))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// also clear the buffer object index
|
|
m_vBufferObjectIndices[BufferIndex] = m_FirstFreeBufferObjectIndex;
|
|
m_FirstFreeBufferObjectIndex = BufferIndex;
|
|
}
|
|
|
|
int CGraphics_Threaded::CreateBufferContainer(SBufferContainerInfo *pContainerInfo)
|
|
{
|
|
int Index = -1;
|
|
if(m_FirstFreeVertexArrayInfo == -1)
|
|
{
|
|
Index = m_vVertexArrayInfo.size();
|
|
m_vVertexArrayInfo.emplace_back();
|
|
}
|
|
else
|
|
{
|
|
Index = m_FirstFreeVertexArrayInfo;
|
|
m_FirstFreeVertexArrayInfo = m_vVertexArrayInfo[Index].m_FreeIndex;
|
|
m_vVertexArrayInfo[Index].m_FreeIndex = Index;
|
|
}
|
|
|
|
CCommandBuffer::SCommand_CreateBufferContainer Cmd;
|
|
Cmd.m_BufferContainerIndex = Index;
|
|
Cmd.m_AttrCount = (int)pContainerInfo->m_vAttributes.size();
|
|
Cmd.m_Stride = pContainerInfo->m_Stride;
|
|
Cmd.m_VertBufferBindingIndex = pContainerInfo->m_VertBufferBindingIndex;
|
|
|
|
Cmd.m_pAttributes = (SBufferContainerInfo::SAttribute *)AllocCommandBufferData(Cmd.m_AttrCount * sizeof(SBufferContainerInfo::SAttribute));
|
|
if(Cmd.m_pAttributes == nullptr)
|
|
return -1;
|
|
|
|
if(!AddCmd(
|
|
Cmd, [&] {
|
|
Cmd.m_pAttributes = (SBufferContainerInfo::SAttribute *)m_pCommandBuffer->AllocData(Cmd.m_AttrCount * sizeof(SBufferContainerInfo::SAttribute));
|
|
if(Cmd.m_pAttributes == nullptr)
|
|
{
|
|
dbg_msg("graphics", "failed to allocate data for upload data");
|
|
return false;
|
|
}
|
|
return true;
|
|
},
|
|
"failed to allocate memory for create buffer container command"))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
mem_copy(Cmd.m_pAttributes, pContainerInfo->m_vAttributes.data(), Cmd.m_AttrCount * sizeof(SBufferContainerInfo::SAttribute));
|
|
|
|
m_vVertexArrayInfo[Index].m_AssociatedBufferObjectIndex = pContainerInfo->m_VertBufferBindingIndex;
|
|
|
|
return Index;
|
|
}
|
|
|
|
void CGraphics_Threaded::DeleteBufferContainer(int &ContainerIndex, bool DestroyAllBO)
|
|
{
|
|
if(ContainerIndex == -1)
|
|
return;
|
|
|
|
CCommandBuffer::SCommand_DeleteBufferContainer Cmd;
|
|
Cmd.m_BufferContainerIndex = ContainerIndex;
|
|
Cmd.m_DestroyAllBO = DestroyAllBO;
|
|
|
|
if(!AddCmd(
|
|
Cmd, [] { return true; }, "failed to allocate memory for delete buffer container command"))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(DestroyAllBO)
|
|
{
|
|
// delete all associated references
|
|
int BufferObjectIndex = m_vVertexArrayInfo[ContainerIndex].m_AssociatedBufferObjectIndex;
|
|
if(BufferObjectIndex != -1)
|
|
{
|
|
// clear the buffer object index
|
|
m_vBufferObjectIndices[BufferObjectIndex] = m_FirstFreeBufferObjectIndex;
|
|
m_FirstFreeBufferObjectIndex = BufferObjectIndex;
|
|
}
|
|
}
|
|
m_vVertexArrayInfo[ContainerIndex].m_AssociatedBufferObjectIndex = -1;
|
|
|
|
// also clear the buffer object index
|
|
m_vVertexArrayInfo[ContainerIndex].m_FreeIndex = m_FirstFreeVertexArrayInfo;
|
|
m_FirstFreeVertexArrayInfo = ContainerIndex;
|
|
ContainerIndex = -1;
|
|
}
|
|
|
|
void CGraphics_Threaded::UpdateBufferContainerInternal(int ContainerIndex, SBufferContainerInfo *pContainerInfo)
|
|
{
|
|
CCommandBuffer::SCommand_UpdateBufferContainer Cmd;
|
|
Cmd.m_BufferContainerIndex = ContainerIndex;
|
|
Cmd.m_AttrCount = (int)pContainerInfo->m_vAttributes.size();
|
|
Cmd.m_Stride = pContainerInfo->m_Stride;
|
|
Cmd.m_VertBufferBindingIndex = pContainerInfo->m_VertBufferBindingIndex;
|
|
|
|
Cmd.m_pAttributes = (SBufferContainerInfo::SAttribute *)AllocCommandBufferData(Cmd.m_AttrCount * sizeof(SBufferContainerInfo::SAttribute));
|
|
if(Cmd.m_pAttributes == nullptr)
|
|
return;
|
|
|
|
if(!AddCmd(
|
|
Cmd, [&] {
|
|
Cmd.m_pAttributes = (SBufferContainerInfo::SAttribute *)m_pCommandBuffer->AllocData(Cmd.m_AttrCount * sizeof(SBufferContainerInfo::SAttribute));
|
|
if(Cmd.m_pAttributes == nullptr)
|
|
{
|
|
dbg_msg("graphics", "failed to allocate data for upload data");
|
|
return false;
|
|
}
|
|
return true;
|
|
},
|
|
"failed to allocate memory for update buffer container command"))
|
|
{
|
|
return;
|
|
}
|
|
|
|
mem_copy(Cmd.m_pAttributes, pContainerInfo->m_vAttributes.data(), Cmd.m_AttrCount * sizeof(SBufferContainerInfo::SAttribute));
|
|
|
|
m_vVertexArrayInfo[ContainerIndex].m_AssociatedBufferObjectIndex = pContainerInfo->m_VertBufferBindingIndex;
|
|
}
|
|
|
|
void CGraphics_Threaded::IndicesNumRequiredNotify(unsigned int RequiredIndicesCount)
|
|
{
|
|
CCommandBuffer::SCommand_IndicesRequiredNumNotify Cmd;
|
|
Cmd.m_RequiredIndicesNum = RequiredIndicesCount;
|
|
|
|
if(!AddCmd(
|
|
Cmd, [] { return true; }, "failed to allocate memory for indcies required count notify command"))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
int CGraphics_Threaded::IssueInit()
|
|
{
|
|
int Flags = 0;
|
|
|
|
bool IsPurlyWindowed = g_Config.m_GfxFullscreen == 0;
|
|
bool IsExclusiveFullscreen = g_Config.m_GfxFullscreen == 1;
|
|
bool IsDesktopFullscreen = g_Config.m_GfxFullscreen == 2;
|
|
#ifndef CONF_FAMILY_WINDOWS
|
|
// special mode for windows only
|
|
IsDesktopFullscreen |= g_Config.m_GfxFullscreen == 3;
|
|
#endif
|
|
|
|
if(g_Config.m_GfxBorderless)
|
|
Flags |= IGraphicsBackend::INITFLAG_BORDERLESS;
|
|
if(IsExclusiveFullscreen)
|
|
Flags |= IGraphicsBackend::INITFLAG_FULLSCREEN;
|
|
else if(IsDesktopFullscreen)
|
|
Flags |= IGraphicsBackend::INITFLAG_DESKTOP_FULLSCREEN;
|
|
if(IsPurlyWindowed || IsExclusiveFullscreen || IsDesktopFullscreen)
|
|
Flags |= IGraphicsBackend::INITFLAG_RESIZABLE;
|
|
if(g_Config.m_GfxVsync)
|
|
Flags |= IGraphicsBackend::INITFLAG_VSYNC;
|
|
if(g_Config.m_GfxHighdpi)
|
|
Flags |= IGraphicsBackend::INITFLAG_HIGHDPI;
|
|
|
|
int r = m_pBackend->Init("DDNet Client", &g_Config.m_GfxScreen, &g_Config.m_GfxScreenWidth, &g_Config.m_GfxScreenHeight, &g_Config.m_GfxScreenRefreshRate, &g_Config.m_GfxFsaaSamples, Flags, &g_Config.m_GfxDesktopWidth, &g_Config.m_GfxDesktopHeight, &m_ScreenWidth, &m_ScreenHeight, m_pStorage);
|
|
AddBackEndWarningIfExists();
|
|
if(r == 0)
|
|
{
|
|
m_GLUseTrianglesAsQuad = m_pBackend->UseTrianglesAsQuad();
|
|
m_GLTileBufferingEnabled = m_pBackend->HasTileBuffering();
|
|
m_GLQuadBufferingEnabled = m_pBackend->HasQuadBuffering();
|
|
m_GLQuadContainerBufferingEnabled = m_pBackend->HasQuadContainerBuffering();
|
|
m_GLTextBufferingEnabled = (m_GLQuadContainerBufferingEnabled && m_pBackend->HasTextBuffering());
|
|
m_GLHasTextureArrays = m_pBackend->Has2DTextureArrays();
|
|
m_ScreenHiDPIScale = m_ScreenWidth / (float)g_Config.m_GfxScreenWidth;
|
|
m_ScreenRefreshRate = g_Config.m_GfxScreenRefreshRate;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
void CGraphics_Threaded::AdjustViewport(bool SendViewportChangeToBackend)
|
|
{
|
|
// adjust the viewport to only allow certain aspect ratios
|
|
// keep this in sync with backend_vulkan GetSwapImageSize's check
|
|
if(m_ScreenHeight > 4 * m_ScreenWidth / 5)
|
|
{
|
|
m_IsForcedViewport = true;
|
|
m_ScreenHeight = 4 * m_ScreenWidth / 5;
|
|
|
|
if(SendViewportChangeToBackend)
|
|
{
|
|
UpdateViewport(0, 0, m_ScreenWidth, m_ScreenHeight, true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_IsForcedViewport = false;
|
|
}
|
|
}
|
|
|
|
void CGraphics_Threaded::UpdateViewport(int X, int Y, int W, int H, bool ByResize)
|
|
{
|
|
CCommandBuffer::SCommand_Update_Viewport Cmd;
|
|
Cmd.m_X = X;
|
|
Cmd.m_Y = Y;
|
|
Cmd.m_Width = W;
|
|
Cmd.m_Height = H;
|
|
Cmd.m_ByResize = ByResize;
|
|
|
|
if(!AddCmd(
|
|
Cmd, [] { return true; }, "failed to add resize command"))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
void CGraphics_Threaded::AddBackEndWarningIfExists()
|
|
{
|
|
const char *pErrStr = m_pBackend->GetErrorString();
|
|
if(pErrStr != NULL)
|
|
{
|
|
SWarning NewWarning;
|
|
str_copy(NewWarning.m_aWarningMsg, Localize(pErrStr));
|
|
m_vWarnings.emplace_back(NewWarning);
|
|
}
|
|
}
|
|
|
|
int CGraphics_Threaded::InitWindow()
|
|
{
|
|
int ErrorCode = IssueInit();
|
|
if(ErrorCode == 0)
|
|
return 0;
|
|
|
|
// try disabling fsaa
|
|
while(g_Config.m_GfxFsaaSamples)
|
|
{
|
|
// 4 is the minimum required by OpenGL ES spec (GL_MAX_SAMPLES - https://www.khronos.org/registry/OpenGL-Refpages/es3.0/html/glGet.xhtml), so can probably also be assumed for OpenGL
|
|
if(g_Config.m_GfxFsaaSamples > 4)
|
|
g_Config.m_GfxFsaaSamples = 4;
|
|
else
|
|
g_Config.m_GfxFsaaSamples = 0;
|
|
|
|
if(g_Config.m_GfxFsaaSamples)
|
|
dbg_msg("gfx", "lowering FSAA to %d and trying again", g_Config.m_GfxFsaaSamples);
|
|
else
|
|
dbg_msg("gfx", "disabling FSAA and trying again");
|
|
|
|
ErrorCode = IssueInit();
|
|
if(ErrorCode == 0)
|
|
return 0;
|
|
}
|
|
|
|
size_t GLInitTryCount = 0;
|
|
while(ErrorCode == EGraphicsBackendErrorCodes::GRAPHICS_BACKEND_ERROR_CODE_GL_CONTEXT_FAILED || ErrorCode == EGraphicsBackendErrorCodes::GRAPHICS_BACKEND_ERROR_CODE_GL_VERSION_FAILED)
|
|
{
|
|
if(ErrorCode == EGraphicsBackendErrorCodes::GRAPHICS_BACKEND_ERROR_CODE_GL_CONTEXT_FAILED)
|
|
{
|
|
// try next smaller major/minor or patch version
|
|
if(g_Config.m_GfxGLMajor >= 4)
|
|
{
|
|
g_Config.m_GfxGLMajor = 3;
|
|
g_Config.m_GfxGLMinor = 3;
|
|
g_Config.m_GfxGLPatch = 0;
|
|
}
|
|
else if(g_Config.m_GfxGLMajor == 3 && g_Config.m_GfxGLMinor >= 1)
|
|
{
|
|
g_Config.m_GfxGLMajor = 3;
|
|
g_Config.m_GfxGLMinor = 0;
|
|
g_Config.m_GfxGLPatch = 0;
|
|
}
|
|
else if(g_Config.m_GfxGLMajor == 3 && g_Config.m_GfxGLMinor == 0)
|
|
{
|
|
g_Config.m_GfxGLMajor = 2;
|
|
g_Config.m_GfxGLMinor = 1;
|
|
g_Config.m_GfxGLPatch = 0;
|
|
}
|
|
else if(g_Config.m_GfxGLMajor == 2 && g_Config.m_GfxGLMinor >= 1)
|
|
{
|
|
g_Config.m_GfxGLMajor = 2;
|
|
g_Config.m_GfxGLMinor = 0;
|
|
g_Config.m_GfxGLPatch = 0;
|
|
}
|
|
else if(g_Config.m_GfxGLMajor == 2 && g_Config.m_GfxGLMinor == 0)
|
|
{
|
|
g_Config.m_GfxGLMajor = 1;
|
|
g_Config.m_GfxGLMinor = 5;
|
|
g_Config.m_GfxGLPatch = 0;
|
|
}
|
|
else if(g_Config.m_GfxGLMajor == 1 && g_Config.m_GfxGLMinor == 5)
|
|
{
|
|
g_Config.m_GfxGLMajor = 1;
|
|
g_Config.m_GfxGLMinor = 4;
|
|
g_Config.m_GfxGLPatch = 0;
|
|
}
|
|
else if(g_Config.m_GfxGLMajor == 1 && g_Config.m_GfxGLMinor == 4)
|
|
{
|
|
g_Config.m_GfxGLMajor = 1;
|
|
g_Config.m_GfxGLMinor = 3;
|
|
g_Config.m_GfxGLPatch = 0;
|
|
}
|
|
else if(g_Config.m_GfxGLMajor == 1 && g_Config.m_GfxGLMinor == 3)
|
|
{
|
|
g_Config.m_GfxGLMajor = 1;
|
|
g_Config.m_GfxGLMinor = 2;
|
|
g_Config.m_GfxGLPatch = 1;
|
|
}
|
|
else if(g_Config.m_GfxGLMajor == 1 && g_Config.m_GfxGLMinor == 2)
|
|
{
|
|
g_Config.m_GfxGLMajor = 1;
|
|
g_Config.m_GfxGLMinor = 1;
|
|
g_Config.m_GfxGLPatch = 0;
|
|
}
|
|
}
|
|
|
|
// new gl version was set by backend, try again
|
|
ErrorCode = IssueInit();
|
|
if(ErrorCode == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if(++GLInitTryCount >= 9)
|
|
{
|
|
// try something else
|
|
break;
|
|
}
|
|
}
|
|
|
|
// try lowering the resolution
|
|
if(g_Config.m_GfxScreenWidth != 640 || g_Config.m_GfxScreenHeight != 480)
|
|
{
|
|
dbg_msg("gfx", "setting resolution to 640x480 and trying again");
|
|
g_Config.m_GfxScreenWidth = 640;
|
|
g_Config.m_GfxScreenHeight = 480;
|
|
|
|
if(IssueInit() == 0)
|
|
return 0;
|
|
}
|
|
|
|
// at the very end, just try to set to gl 1.4
|
|
{
|
|
g_Config.m_GfxGLMajor = 1;
|
|
g_Config.m_GfxGLMinor = 4;
|
|
g_Config.m_GfxGLPatch = 0;
|
|
|
|
if(IssueInit() == 0)
|
|
return 0;
|
|
}
|
|
|
|
dbg_msg("gfx", "out of ideas. failed to init graphics");
|
|
|
|
return -1;
|
|
}
|
|
|
|
int CGraphics_Threaded::Init()
|
|
{
|
|
// fetch pointers
|
|
m_pStorage = Kernel()->RequestInterface<IStorage>();
|
|
m_pConsole = Kernel()->RequestInterface<IConsole>();
|
|
|
|
// init textures
|
|
m_FirstFreeTexture = 0;
|
|
m_vTextureIndices.resize(CCommandBuffer::MAX_TEXTURES);
|
|
for(int i = 0; i < (int)m_vTextureIndices.size() - 1; i++)
|
|
m_vTextureIndices[i] = i + 1;
|
|
m_vTextureIndices.back() = -1;
|
|
|
|
m_FirstFreeVertexArrayInfo = -1;
|
|
m_FirstFreeBufferObjectIndex = -1;
|
|
m_FirstFreeQuadContainer = -1;
|
|
|
|
m_pBackend = CreateGraphicsBackend(Localize);
|
|
if(InitWindow() != 0)
|
|
return -1;
|
|
|
|
for(auto &FakeMode : g_aFakeModes)
|
|
{
|
|
FakeMode.m_WindowWidth = FakeMode.m_CanvasWidth / m_ScreenHiDPIScale;
|
|
FakeMode.m_WindowHeight = FakeMode.m_CanvasHeight / m_ScreenHiDPIScale;
|
|
FakeMode.m_RefreshRate = g_Config.m_GfxScreenRefreshRate;
|
|
}
|
|
|
|
// create command buffers
|
|
for(auto &pCommandBuffer : m_apCommandBuffers)
|
|
pCommandBuffer = new CCommandBuffer(CMD_BUFFER_CMD_BUFFER_SIZE, CMD_BUFFER_DATA_BUFFER_SIZE);
|
|
m_pCommandBuffer = m_apCommandBuffers[0];
|
|
|
|
// create null texture, will get id=0
|
|
static const unsigned char s_aNullTextureData[] = {
|
|
0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff,
|
|
0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff,
|
|
0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff,
|
|
0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff};
|
|
|
|
m_InvalidTexture = LoadTextureRaw(4, 4, CImageInfo::FORMAT_RGBA, s_aNullTextureData, CImageInfo::FORMAT_RGBA, 0);
|
|
|
|
ColorRGBA GPUInfoPrintColor{0.6f, 0.5f, 1.0f, 1.0f};
|
|
|
|
char aBuf[256];
|
|
str_format(aBuf, sizeof(aBuf), "GPU vendor: %s", GetVendorString());
|
|
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "gfx", aBuf, GPUInfoPrintColor);
|
|
|
|
str_format(aBuf, sizeof(aBuf), "GPU renderer: %s", GetRendererString());
|
|
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "gfx", aBuf, GPUInfoPrintColor);
|
|
|
|
str_format(aBuf, sizeof(aBuf), "GPU version: %s", GetVersionString());
|
|
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "gfx", aBuf, GPUInfoPrintColor);
|
|
|
|
AdjustViewport(true);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void CGraphics_Threaded::Shutdown()
|
|
{
|
|
// shutdown the backend
|
|
m_pBackend->Shutdown();
|
|
delete m_pBackend;
|
|
m_pBackend = 0x0;
|
|
|
|
// delete the command buffers
|
|
for(auto &pCommandBuffer : m_apCommandBuffers)
|
|
delete pCommandBuffer;
|
|
}
|
|
|
|
int CGraphics_Threaded::GetNumScreens() const
|
|
{
|
|
return m_pBackend->GetNumScreens();
|
|
}
|
|
|
|
void CGraphics_Threaded::Minimize()
|
|
{
|
|
m_pBackend->Minimize();
|
|
|
|
for(auto &PropChangedListener : m_vPropChangeListeners)
|
|
PropChangedListener();
|
|
}
|
|
|
|
void CGraphics_Threaded::Maximize()
|
|
{
|
|
// TODO: SDL
|
|
m_pBackend->Maximize();
|
|
|
|
for(auto &PropChangedListener : m_vPropChangeListeners)
|
|
PropChangedListener();
|
|
}
|
|
|
|
void CGraphics_Threaded::WarnPngliteIncompatibleImages(bool Warn)
|
|
{
|
|
m_WarnPngliteIncompatibleImages = Warn;
|
|
}
|
|
|
|
void CGraphics_Threaded::SetWindowParams(int FullscreenMode, bool IsBorderless, bool AllowResizing)
|
|
{
|
|
m_pBackend->SetWindowParams(FullscreenMode, IsBorderless, AllowResizing);
|
|
CVideoMode CurMode;
|
|
m_pBackend->GetCurrentVideoMode(CurMode, m_ScreenHiDPIScale, g_Config.m_GfxDesktopWidth, g_Config.m_GfxDesktopHeight, g_Config.m_GfxScreen);
|
|
GotResized(CurMode.m_WindowWidth, CurMode.m_WindowHeight, CurMode.m_RefreshRate);
|
|
|
|
for(auto &PropChangedListener : m_vPropChangeListeners)
|
|
PropChangedListener();
|
|
}
|
|
|
|
bool CGraphics_Threaded::SetWindowScreen(int Index)
|
|
{
|
|
if(!m_pBackend->SetWindowScreen(Index))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// send a got resized event so that the current canvas size is requested
|
|
GotResized(g_Config.m_GfxScreenWidth, g_Config.m_GfxScreenHeight, g_Config.m_GfxScreenRefreshRate);
|
|
|
|
for(auto &PropChangedListener : m_vPropChangeListeners)
|
|
PropChangedListener();
|
|
return true;
|
|
}
|
|
|
|
void CGraphics_Threaded::Move(int x, int y)
|
|
{
|
|
#if defined(CONF_VIDEORECORDER)
|
|
if(IVideo::Current() && IVideo::Current()->IsRecording())
|
|
return;
|
|
#endif
|
|
|
|
// Only handling CurScreen != m_GfxScreen doesn't work reliably
|
|
const int CurScreen = m_pBackend->GetWindowScreen();
|
|
m_pBackend->UpdateDisplayMode(CurScreen);
|
|
|
|
// send a got resized event so that the current canvas size is requested
|
|
GotResized(g_Config.m_GfxScreenWidth, g_Config.m_GfxScreenHeight, g_Config.m_GfxScreenRefreshRate);
|
|
|
|
for(auto &PropChangedListener : m_vPropChangeListeners)
|
|
PropChangedListener();
|
|
}
|
|
|
|
void CGraphics_Threaded::Resize(int w, int h, int RefreshRate)
|
|
{
|
|
#if defined(CONF_VIDEORECORDER)
|
|
if(IVideo::Current() && IVideo::Current()->IsRecording())
|
|
return;
|
|
#endif
|
|
|
|
if(WindowWidth() == w && WindowHeight() == h && RefreshRate == m_ScreenRefreshRate)
|
|
return;
|
|
|
|
// if the size is changed manually, only set the window resize, a window size changed event is triggered anyway
|
|
if(m_pBackend->ResizeWindow(w, h, RefreshRate))
|
|
{
|
|
CVideoMode CurMode;
|
|
m_pBackend->GetCurrentVideoMode(CurMode, m_ScreenHiDPIScale, g_Config.m_GfxDesktopWidth, g_Config.m_GfxDesktopHeight, g_Config.m_GfxScreen);
|
|
GotResized(w, h, RefreshRate);
|
|
}
|
|
}
|
|
|
|
void CGraphics_Threaded::GotResized(int w, int h, int RefreshRate)
|
|
{
|
|
#if defined(CONF_VIDEORECORDER)
|
|
if(IVideo::Current() && IVideo::Current()->IsRecording())
|
|
return;
|
|
#endif
|
|
|
|
// if RefreshRate is -1 use the current config refresh rate
|
|
if(RefreshRate == -1)
|
|
RefreshRate = g_Config.m_GfxScreenRefreshRate;
|
|
|
|
// if the size change event is triggered, set all parameters and change the viewport
|
|
auto PrevCanvasWidth = m_ScreenWidth;
|
|
auto PrevCanvasHeight = m_ScreenHeight;
|
|
m_pBackend->GetViewportSize(m_ScreenWidth, m_ScreenHeight);
|
|
|
|
AdjustViewport(false);
|
|
|
|
m_ScreenRefreshRate = RefreshRate;
|
|
|
|
g_Config.m_GfxScreenWidth = w;
|
|
g_Config.m_GfxScreenHeight = h;
|
|
g_Config.m_GfxScreenRefreshRate = m_ScreenRefreshRate;
|
|
m_ScreenHiDPIScale = m_ScreenWidth / (float)g_Config.m_GfxScreenWidth;
|
|
|
|
UpdateViewport(0, 0, m_ScreenWidth, m_ScreenHeight, true);
|
|
|
|
// kick the command buffer and wait
|
|
KickCommandBuffer();
|
|
WaitForIdle();
|
|
|
|
if(PrevCanvasWidth != m_ScreenWidth || PrevCanvasHeight != m_ScreenHeight)
|
|
{
|
|
for(auto &ResizeListener : m_vResizeListeners)
|
|
ResizeListener();
|
|
}
|
|
}
|
|
|
|
void CGraphics_Threaded::AddWindowResizeListener(WINDOW_RESIZE_FUNC pFunc)
|
|
{
|
|
m_vResizeListeners.emplace_back(pFunc);
|
|
}
|
|
|
|
void CGraphics_Threaded::AddWindowPropChangeListener(WINDOW_PROPS_CHANGED_FUNC pFunc)
|
|
{
|
|
m_vPropChangeListeners.emplace_back(pFunc);
|
|
}
|
|
|
|
int CGraphics_Threaded::GetWindowScreen()
|
|
{
|
|
return m_pBackend->GetWindowScreen();
|
|
}
|
|
|
|
void CGraphics_Threaded::WindowDestroyNtf(uint32_t WindowID)
|
|
{
|
|
m_pBackend->WindowDestroyNtf(WindowID);
|
|
|
|
CCommandBuffer::SCommand_WindowDestroyNtf Cmd;
|
|
Cmd.m_WindowID = WindowID;
|
|
|
|
if(!AddCmd(
|
|
Cmd, [] { return true; }, "failed to add window destroy notify command"))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// wait
|
|
KickCommandBuffer();
|
|
WaitForIdle();
|
|
}
|
|
|
|
void CGraphics_Threaded::WindowCreateNtf(uint32_t WindowID)
|
|
{
|
|
m_pBackend->WindowCreateNtf(WindowID);
|
|
|
|
CCommandBuffer::SCommand_WindowCreateNtf Cmd;
|
|
Cmd.m_WindowID = WindowID;
|
|
|
|
if(!AddCmd(
|
|
Cmd, [] { return true; }, "failed to add window create notify command"))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// wait
|
|
KickCommandBuffer();
|
|
WaitForIdle();
|
|
}
|
|
|
|
int CGraphics_Threaded::WindowActive()
|
|
{
|
|
return m_pBackend->WindowActive();
|
|
}
|
|
|
|
int CGraphics_Threaded::WindowOpen()
|
|
{
|
|
return m_pBackend->WindowOpen();
|
|
}
|
|
|
|
void CGraphics_Threaded::SetWindowGrab(bool Grab)
|
|
{
|
|
return m_pBackend->SetWindowGrab(Grab);
|
|
}
|
|
|
|
void CGraphics_Threaded::NotifyWindow()
|
|
{
|
|
return m_pBackend->NotifyWindow();
|
|
}
|
|
|
|
void CGraphics_Threaded::TakeScreenshot(const char *pFilename)
|
|
{
|
|
// TODO: screenshot support
|
|
char aDate[20];
|
|
str_timestamp(aDate, sizeof(aDate));
|
|
str_format(m_aScreenshotName, sizeof(m_aScreenshotName), "screenshots/%s_%s.png", pFilename ? pFilename : "screenshot", aDate);
|
|
m_DoScreenshot = true;
|
|
}
|
|
|
|
void CGraphics_Threaded::TakeCustomScreenshot(const char *pFilename)
|
|
{
|
|
str_copy(m_aScreenshotName, pFilename);
|
|
m_DoScreenshot = true;
|
|
}
|
|
|
|
void CGraphics_Threaded::Swap()
|
|
{
|
|
if(!m_vWarnings.empty())
|
|
{
|
|
SWarning *pCurWarning = GetCurWarning();
|
|
if(pCurWarning->m_WasShown)
|
|
{
|
|
m_vWarnings.erase(m_vWarnings.begin());
|
|
}
|
|
}
|
|
|
|
bool TookScreenshotAndSwapped = false;
|
|
|
|
if(m_DoScreenshot)
|
|
{
|
|
if(WindowActive())
|
|
TookScreenshotAndSwapped = ScreenshotDirect();
|
|
m_DoScreenshot = false;
|
|
}
|
|
|
|
if(!TookScreenshotAndSwapped)
|
|
{
|
|
// add swap command
|
|
CCommandBuffer::SCommand_Swap Cmd;
|
|
if(!AddCmd(
|
|
Cmd, [] { return true; }, "failed to add swap command"))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
if(g_Config.m_GfxFinish)
|
|
{
|
|
CCommandBuffer::SCommand_Finish Cmd;
|
|
if(!AddCmd(
|
|
Cmd, [] { return true; }, "failed to add finish command"))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
// kick the command buffer
|
|
KickCommandBuffer();
|
|
// TODO: Remove when https://github.com/libsdl-org/SDL/issues/5203 is fixed
|
|
#ifdef CONF_PLATFORM_MACOS
|
|
if(str_find(GetVersionString(), "Metal"))
|
|
WaitForIdle();
|
|
#endif
|
|
}
|
|
|
|
bool CGraphics_Threaded::SetVSync(bool State)
|
|
{
|
|
if(!m_pCommandBuffer)
|
|
return true;
|
|
|
|
// add vsync command
|
|
bool RetOk = false;
|
|
CCommandBuffer::SCommand_VSync Cmd;
|
|
Cmd.m_VSync = State ? 1 : 0;
|
|
Cmd.m_pRetOk = &RetOk;
|
|
|
|
if(!AddCmd(
|
|
Cmd, [] { return true; }, "failed to add vsync command"))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// kick the command buffer
|
|
KickCommandBuffer();
|
|
WaitForIdle();
|
|
return RetOk;
|
|
}
|
|
|
|
bool CGraphics_Threaded::SetMultiSampling(uint32_t ReqMultiSamplingCount, uint32_t &MultiSamplingCountBackend)
|
|
{
|
|
if(!m_pCommandBuffer)
|
|
return true;
|
|
|
|
// add multisampling command
|
|
bool RetOk = false;
|
|
CCommandBuffer::SCommand_MultiSampling Cmd;
|
|
Cmd.m_RequestedMultiSamplingCount = ReqMultiSamplingCount;
|
|
Cmd.m_pRetMultiSamplingCount = &MultiSamplingCountBackend;
|
|
Cmd.m_pRetOk = &RetOk;
|
|
|
|
if(!AddCmd(
|
|
Cmd, [] { return true; }, "failed to add multi sampling command"))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// kick the command buffer
|
|
KickCommandBuffer();
|
|
WaitForIdle();
|
|
return RetOk;
|
|
}
|
|
|
|
// synchronization
|
|
void CGraphics_Threaded::InsertSignal(CSemaphore *pSemaphore)
|
|
{
|
|
CCommandBuffer::SCommand_Signal Cmd;
|
|
Cmd.m_pSemaphore = pSemaphore;
|
|
|
|
if(!AddCmd(
|
|
Cmd, [] { return true; }, "failed to add signal command"))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
bool CGraphics_Threaded::IsIdle() const
|
|
{
|
|
return m_pBackend->IsIdle();
|
|
}
|
|
|
|
void CGraphics_Threaded::WaitForIdle()
|
|
{
|
|
m_pBackend->WaitForIdle();
|
|
}
|
|
|
|
SWarning *CGraphics_Threaded::GetCurWarning()
|
|
{
|
|
if(m_vWarnings.empty())
|
|
return NULL;
|
|
else
|
|
{
|
|
SWarning *pCurWarning = m_vWarnings.data();
|
|
return pCurWarning;
|
|
}
|
|
}
|
|
|
|
const char *CGraphics_Threaded::GetVendorString()
|
|
{
|
|
return m_pBackend->GetVendorString();
|
|
}
|
|
|
|
const char *CGraphics_Threaded::GetVersionString()
|
|
{
|
|
return m_pBackend->GetVersionString();
|
|
}
|
|
|
|
const char *CGraphics_Threaded::GetRendererString()
|
|
{
|
|
return m_pBackend->GetRendererString();
|
|
}
|
|
|
|
TGLBackendReadPresentedImageData &CGraphics_Threaded::GetReadPresentedImageDataFuncUnsafe()
|
|
{
|
|
return m_pBackend->GetReadPresentedImageDataFuncUnsafe();
|
|
}
|
|
|
|
int CGraphics_Threaded::GetVideoModes(CVideoMode *pModes, int MaxModes, int Screen)
|
|
{
|
|
if(g_Config.m_GfxDisplayAllVideoModes)
|
|
{
|
|
int Count = std::size(g_aFakeModes);
|
|
mem_copy(pModes, g_aFakeModes, sizeof(g_aFakeModes));
|
|
if(MaxModes < Count)
|
|
Count = MaxModes;
|
|
return Count;
|
|
}
|
|
|
|
// add videomodes command
|
|
CImageInfo Image;
|
|
mem_zero(&Image, sizeof(Image));
|
|
|
|
int NumModes = 0;
|
|
m_pBackend->GetVideoModes(pModes, MaxModes, &NumModes, m_ScreenHiDPIScale, g_Config.m_GfxDesktopWidth, g_Config.m_GfxDesktopHeight, Screen);
|
|
|
|
return NumModes;
|
|
}
|
|
|
|
extern IEngineGraphics *CreateEngineGraphicsThreaded()
|
|
{
|
|
return new CGraphics_Threaded();
|
|
}
|