2025-06-22 21:05:09 +02:00
|
|
|
#pragma once
|
|
|
|
|
|
2026-03-16 06:37:51 +01:00
|
|
|
#include <pd/core/mat.hpp>
|
|
|
|
|
#include <pd/drivers/interface.hpp>
|
2026-09-05 22:52:59 +02:00
|
|
|
#include <pd/lithium/drawlist.hpp>
|
|
|
|
|
#include <pd/lithium/pools.hpp>
|
2026-03-18 09:31:47 +01:00
|
|
|
#include <pd/lithium/texture.hpp>
|
2025-06-22 21:05:09 +02:00
|
|
|
|
2026-04-03 12:52:30 +02:00
|
|
|
using PDGfxBackendFlags = PD::u32;
|
|
|
|
|
enum PDGfxBackendFlags_ {
|
|
|
|
|
PDGfxBackendFlags_None = 0,
|
2026-09-13 01:24:55 +02:00
|
|
|
PDGfxBackendFlags_FlipUV_Y = 1 << 0, // Essential for font loading
|
|
|
|
|
PDGfxBackendFlags_WindingCW = 1 << 1, // Use CW instead of CCW winding
|
|
|
|
|
PDGfxBackendFlags_HasClipRect = 1 << 2, // Clip rects supported
|
|
|
|
|
PDGfxBackendFlags_ReqPow2 = 1 << 3, // Tex size must be pow of 2
|
|
|
|
|
PDGfxBackendFlags_ApproxSDF = 1 << 4, // Fake SDF for non smoothstep systems
|
2025-06-22 21:05:09 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
namespace PD {
|
2026-03-16 06:37:51 +01:00
|
|
|
|
2026-03-16 15:19:12 +01:00
|
|
|
// Pre interface class
|
2026-03-16 06:37:51 +01:00
|
|
|
class PD_API GfxDriver : public DriverInterface {
|
2025-06-22 21:05:09 +02:00
|
|
|
public:
|
2026-03-16 15:19:12 +01:00
|
|
|
GfxDriver(std::string_view name);
|
2026-03-18 09:31:47 +01:00
|
|
|
virtual ~GfxDriver();
|
2025-06-22 21:05:09 +02:00
|
|
|
|
|
|
|
|
virtual void Init() {}
|
2026-03-21 10:11:10 +01:00
|
|
|
virtual void Deinit() {
|
|
|
|
|
DeleteTexture(pWhite);
|
|
|
|
|
SysDeinit();
|
|
|
|
|
}
|
2026-03-16 15:19:12 +01:00
|
|
|
|
|
|
|
|
void SetViewPort(const ivec2& size);
|
|
|
|
|
void SetViewPort(int x, int y);
|
2026-03-16 06:37:51 +01:00
|
|
|
virtual void BindTexture(TextureID id) {}
|
2026-03-16 15:19:12 +01:00
|
|
|
void Reset();
|
2026-03-18 09:31:47 +01:00
|
|
|
virtual Li::Texture LoadTexture(
|
|
|
|
|
const std::vector<PD::u8>& pixels, int w, int h,
|
|
|
|
|
TextureFormat type = TextureFormat::RGBA32,
|
|
|
|
|
TextureFilter filter = TextureFilter::Linear) {
|
|
|
|
|
return Li::Texture();
|
2026-03-16 15:19:12 +01:00
|
|
|
}
|
2026-03-18 09:31:47 +01:00
|
|
|
virtual void DeleteTexture(const Li::Texture& tex) {}
|
2026-09-05 22:52:59 +02:00
|
|
|
virtual void Draw(const Li::Drawlist& commands) {}
|
2026-03-19 22:06:58 +01:00
|
|
|
Li::Texture::Ptr GetWhiteTexture() { return &pWhite; }
|
2026-04-03 12:52:30 +02:00
|
|
|
PDGfxBackendFlags GetFlags() { return Flags; }
|
2026-03-16 15:19:12 +01:00
|
|
|
|
2026-03-25 20:27:23 +01:00
|
|
|
size_t GetNumVertices() const { return CountVertices; }
|
|
|
|
|
size_t GetNumIndices() const { return CountIndices; }
|
|
|
|
|
size_t GetNumDrawcalls() const { return CountDrawcalls; }
|
|
|
|
|
size_t GetNumCommands() const { return CountCommands; }
|
|
|
|
|
|
2026-09-02 08:04:50 +02:00
|
|
|
// Global Pool Interface
|
|
|
|
|
virtual size_t AllocateVertices(size_t count, PD::ptr accessor) = 0;
|
|
|
|
|
virtual size_t AllocateIndices(size_t count, PD::ptr accessor) = 0;
|
|
|
|
|
virtual bool ExpandVertices(size_t count, PD::ptr accessor) = 0;
|
|
|
|
|
virtual bool ExpandIndices(size_t count, PD::ptr accessor) = 0;
|
|
|
|
|
virtual void PutVertex(size_t loc, const Li::Vertex& vtx,
|
|
|
|
|
PD::ptr accessor) = 0;
|
|
|
|
|
virtual void PutIndex(size_t loc, u16 idx, PD::ptr accessor) = 0;
|
|
|
|
|
virtual const Li::Vertex& GetVertex(size_t loc) const = 0;
|
|
|
|
|
virtual const u16& GetIndex(size_t loc) const = 0;
|
2026-09-03 08:12:34 +02:00
|
|
|
virtual void ResetPools() = 0;
|
2026-09-02 08:04:50 +02:00
|
|
|
|
2026-03-16 15:19:12 +01:00
|
|
|
protected:
|
|
|
|
|
virtual void SysDeinit() {}
|
|
|
|
|
virtual void SysInit() {}
|
|
|
|
|
virtual void SysReset() {}
|
|
|
|
|
virtual void Submit(size_t count, size_t start) {}
|
2026-09-03 09:18:49 +02:00
|
|
|
virtual void UploadPools() {} // not every driver requires it
|
2026-09-08 21:15:21 +02:00
|
|
|
virtual void ClipRect() {}
|
2026-03-18 09:31:47 +01:00
|
|
|
void RegisterTexture(const Li::Texture& tex);
|
|
|
|
|
void UnregisterTexture(const Li::Texture& tex);
|
2026-03-16 15:19:12 +01:00
|
|
|
|
|
|
|
|
// Counters
|
|
|
|
|
size_t CountDrawcalls = 0;
|
|
|
|
|
size_t CountCommands = 0;
|
|
|
|
|
size_t CountVertices = 0;
|
|
|
|
|
size_t CountIndices = 0;
|
|
|
|
|
size_t CurrentIndex = 0;
|
|
|
|
|
size_t CurrentVertex = 0;
|
2026-03-25 20:27:23 +01:00
|
|
|
size_t pCountDrawcalls = 0;
|
|
|
|
|
size_t pCountCommands = 0;
|
2026-09-05 11:56:13 +02:00
|
|
|
// State Variables oder so
|
2026-03-16 15:19:12 +01:00
|
|
|
TextureID CurrentTex = 0;
|
2026-09-05 11:56:13 +02:00
|
|
|
bool CurrentTexIsSDF = false;
|
2026-09-08 21:15:21 +02:00
|
|
|
bool CurrentHasClip = false;
|
|
|
|
|
fvec4 CurrentClip = 0;
|
2026-03-16 15:19:12 +01:00
|
|
|
Mat4 Projection;
|
|
|
|
|
ivec2 ViewPort;
|
2026-03-18 09:31:47 +01:00
|
|
|
std::unordered_map<TextureID, Li::Texture> pTextureRegestry;
|
2026-03-19 22:06:58 +01:00
|
|
|
Li::Texture pWhite;
|
2026-04-03 12:52:30 +02:00
|
|
|
PDGfxBackendFlags Flags = 0;
|
2026-03-16 15:19:12 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct DefaultGfxConfig {
|
|
|
|
|
// Vertex Allocator
|
|
|
|
|
template <typename T>
|
|
|
|
|
using VertexAlloc = std::allocator<T>;
|
|
|
|
|
// Index Allocator
|
|
|
|
|
template <typename T>
|
|
|
|
|
using IndexAlloc = std::allocator<T>;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <typename Config = DefaultGfxConfig>
|
2026-03-17 16:47:19 +01:00
|
|
|
class GfxDriverBase : public GfxDriver {
|
2026-03-16 15:19:12 +01:00
|
|
|
public:
|
|
|
|
|
using VtxPool =
|
|
|
|
|
Pool<Li::Vertex, typename Config::template VertexAlloc<Li::Vertex>>;
|
2026-03-17 16:47:19 +01:00
|
|
|
using IdxPool = Pool<u16, typename Config::template VertexAlloc<u16>>;
|
2026-03-16 15:19:12 +01:00
|
|
|
GfxDriverBase(std::string_view name = "Default") : GfxDriver(name) {}
|
|
|
|
|
virtual ~GfxDriverBase() {}
|
|
|
|
|
|
|
|
|
|
void Init() override {
|
|
|
|
|
SysInit();
|
2026-03-19 22:06:58 +01:00
|
|
|
std::vector<u8> img(16 * 16 * 4, 0xff);
|
|
|
|
|
pWhite = LoadTexture(img, 16, 16);
|
2026-03-16 15:19:12 +01:00
|
|
|
}
|
|
|
|
|
|
2026-09-05 22:52:59 +02:00
|
|
|
void Draw(const Li::Drawlist& dl) override {
|
|
|
|
|
const auto& commands = dl.Data();
|
|
|
|
|
if (commands.size() == 0) return;
|
2026-03-25 20:27:23 +01:00
|
|
|
pCountCommands += commands.size();
|
2026-09-05 22:52:59 +02:00
|
|
|
|
|
|
|
|
size_t vtotal = dl.GetNumVertices();
|
|
|
|
|
size_t itotal = dl.GetNumIndices();
|
|
|
|
|
|
|
|
|
|
size_t start_vtx = pVtxPool.size();
|
|
|
|
|
pVtxPool.Allocate(vtotal);
|
|
|
|
|
CurrentVertex += vtotal;
|
|
|
|
|
|
|
|
|
|
size_t start_idx = pIdxPool.size();
|
|
|
|
|
pIdxPool.Allocate(itotal);
|
|
|
|
|
CurrentIndex += itotal;
|
|
|
|
|
|
|
|
|
|
size_t current_vtx = start_vtx;
|
|
|
|
|
size_t current_idx = start_idx;
|
|
|
|
|
|
|
|
|
|
const auto& vpool = Li::GetVertexPool();
|
|
|
|
|
const auto& ipool = Li::GetIndexPool();
|
|
|
|
|
/** Build Pools */
|
|
|
|
|
for (size_t i = 0; i < commands.size(); i++) {
|
|
|
|
|
const auto& cmd = commands[i];
|
|
|
|
|
if (cmd.VertexCount > 0) {
|
2026-09-08 21:15:21 +02:00
|
|
|
std::memcpy(reinterpret_cast<void*>(pVtxPool.begin() + current_vtx),
|
2026-09-05 22:52:59 +02:00
|
|
|
vpool.begin() + cmd.FirstVertex,
|
|
|
|
|
cmd.VertexCount * sizeof(Li::Vertex));
|
|
|
|
|
}
|
|
|
|
|
for (size_t idx = 0; idx < cmd.IndexCount; idx++) {
|
|
|
|
|
u16 local_idx = ipool[cmd.FirstIndex + idx];
|
|
|
|
|
pIdxPool[current_idx + idx] = static_cast<u16>(current_vtx + local_idx);
|
|
|
|
|
}
|
|
|
|
|
current_vtx += cmd.VertexCount;
|
|
|
|
|
current_idx += cmd.IndexCount;
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-03 09:18:49 +02:00
|
|
|
UploadPools();
|
2026-09-05 22:52:59 +02:00
|
|
|
|
|
|
|
|
size_t index = 0;
|
|
|
|
|
size_t ioff = start_idx;
|
|
|
|
|
|
2026-03-16 06:37:51 +01:00
|
|
|
while (index < commands.size()) {
|
|
|
|
|
CurrentTex = commands[index].Tex;
|
2026-09-05 11:56:13 +02:00
|
|
|
CurrentTexIsSDF = commands[index].SDF;
|
2026-09-08 21:15:21 +02:00
|
|
|
CurrentHasClip = commands[index].ClipRectUsed;
|
|
|
|
|
CurrentClip = commands[index].ClipRect;
|
2026-03-16 06:37:51 +01:00
|
|
|
if (!CurrentTex) {
|
2026-03-19 22:06:58 +01:00
|
|
|
CurrentTex = pWhite.GetID();
|
2026-03-16 06:37:51 +01:00
|
|
|
}
|
2026-09-02 08:04:50 +02:00
|
|
|
size_t num_indices = 0;
|
2026-09-08 21:15:21 +02:00
|
|
|
ClipRect();
|
2026-03-19 22:06:58 +01:00
|
|
|
while (index < commands.size() &&
|
2026-09-05 11:56:13 +02:00
|
|
|
CurrentTexIsSDF == commands[index].SDF &&
|
2026-03-19 22:06:58 +01:00
|
|
|
(CurrentTex == commands[index].Tex ||
|
2026-09-08 21:15:21 +02:00
|
|
|
(CurrentTex == pWhite.GetID() && commands[index].Tex == 0)) &&
|
|
|
|
|
CurrentClip == commands[index].ClipRect &&
|
|
|
|
|
CurrentHasClip == commands[index].ClipRectUsed) {
|
2026-09-02 08:04:50 +02:00
|
|
|
num_indices += commands[index].IndexCount;
|
2026-03-16 15:19:12 +01:00
|
|
|
index++;
|
|
|
|
|
}
|
2026-09-05 22:52:59 +02:00
|
|
|
if (num_indices > 0) {
|
|
|
|
|
Submit(num_indices, ioff);
|
|
|
|
|
ioff += num_indices;
|
|
|
|
|
pCountDrawcalls++;
|
|
|
|
|
}
|
2026-03-16 06:37:51 +01:00
|
|
|
}
|
2025-06-22 21:05:09 +02:00
|
|
|
}
|
|
|
|
|
|
2026-09-02 08:04:50 +02:00
|
|
|
size_t AllocateVertices(size_t count, PD::ptr accessor) override {
|
|
|
|
|
pVertexAccessor = accessor;
|
|
|
|
|
size_t loc = pVtxPool.size();
|
|
|
|
|
pVtxPool.Allocate(count);
|
|
|
|
|
return loc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t AllocateIndices(size_t count, PD::ptr accessor) override {
|
|
|
|
|
pIndexAccessor = accessor;
|
|
|
|
|
size_t loc = pIdxPool.size();
|
|
|
|
|
pIdxPool.Allocate(count);
|
|
|
|
|
return loc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool ExpandVertices(size_t count, PD::ptr accessor) override {
|
|
|
|
|
if (pVertexAccessor != accessor) return false;
|
|
|
|
|
pVtxPool.Allocate(count);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool ExpandIndices(size_t count, PD::ptr accessor) override {
|
|
|
|
|
if (pIndexAccessor != accessor) return false;
|
|
|
|
|
pIdxPool.Allocate(count);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PutVertex(size_t loc, const Li::Vertex& vtx, PD::ptr accessor) override {
|
|
|
|
|
if (pVertexAccessor != accessor) return;
|
|
|
|
|
pVtxPool.Put(loc, vtx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PutIndex(size_t loc, u16 idx, PD::ptr accessor) override {
|
|
|
|
|
if (pIndexAccessor != accessor) return;
|
|
|
|
|
pIdxPool.Put(loc, idx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const Li::Vertex& GetVertex(size_t loc) const override {
|
|
|
|
|
return pVtxPool[loc];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const u16& GetIndex(size_t loc) const override { return pIdxPool[loc]; }
|
|
|
|
|
|
2026-09-03 08:12:34 +02:00
|
|
|
void ResetPools() override {
|
|
|
|
|
pVtxPool.NoReset();
|
|
|
|
|
pIdxPool.NoReset();
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-16 15:19:12 +01:00
|
|
|
protected:
|
2026-03-17 16:47:19 +01:00
|
|
|
u16* GetIndexBufPtr(size_t start) { return &pIdxPool[start]; }
|
2026-03-16 15:19:12 +01:00
|
|
|
Li::Vertex* GetVertexBufPtr(size_t start) { return &pVtxPool[start]; }
|
2026-03-21 10:11:10 +01:00
|
|
|
size_t GetVertexPoolSize() const { return pVtxPool.size(); }
|
|
|
|
|
size_t GetIndexPoolSize() const { return pIdxPool.size(); }
|
2025-06-22 21:05:09 +02:00
|
|
|
|
2026-03-16 15:19:12 +01:00
|
|
|
private:
|
|
|
|
|
VtxPool pVtxPool;
|
|
|
|
|
IdxPool pIdxPool;
|
2026-09-02 08:04:50 +02:00
|
|
|
PD::ptr pVertexAccessor = 0;
|
|
|
|
|
PD::ptr pIndexAccessor = 0;
|
2026-03-16 06:37:51 +01:00
|
|
|
};
|
2025-06-22 21:05:09 +02:00
|
|
|
|
2026-03-16 06:37:51 +01:00
|
|
|
class PD_API Gfx {
|
|
|
|
|
public:
|
|
|
|
|
Gfx() = default;
|
|
|
|
|
~Gfx() = default;
|
|
|
|
|
|
|
|
|
|
template <typename T, typename... Args>
|
|
|
|
|
static void UseDriver(Args&&... args) {
|
|
|
|
|
// assert(driver == nullptr && "OS Driver already set");
|
|
|
|
|
driver = std::make_unique<T>(std::forward<Args>(args)...);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-16 17:33:46 +01:00
|
|
|
static void Init() { driver->Init(); }
|
|
|
|
|
static void Deinit() { driver->Deinit(); }
|
|
|
|
|
static void SetViewPort(const ivec2& vp) { driver->SetViewPort(vp); }
|
|
|
|
|
static void SetViewPort(int w, int h) { driver->SetViewPort(w, h); }
|
|
|
|
|
static void Reset() { driver->Reset(); }
|
2026-09-05 22:52:59 +02:00
|
|
|
static void Draw(const Li::Drawlist& dl) { driver->Draw(dl); }
|
2026-03-18 09:31:47 +01:00
|
|
|
static Li::Texture LoadTexture(const std::vector<PD::u8>& pixels, int w,
|
|
|
|
|
int h,
|
|
|
|
|
TextureFormat type = TextureFormat::RGBA32,
|
|
|
|
|
TextureFilter filter = TextureFilter::Linear) {
|
2026-03-16 17:33:46 +01:00
|
|
|
return driver->LoadTexture(pixels, w, h, type, filter);
|
|
|
|
|
}
|
2026-03-18 09:31:47 +01:00
|
|
|
static void DeleteTexture(const Li::Texture& tex) {
|
|
|
|
|
driver->DeleteTexture(tex);
|
|
|
|
|
}
|
2026-03-19 22:06:58 +01:00
|
|
|
static Li::Texture::Ptr GetWhiteTexture() {
|
|
|
|
|
return driver->GetWhiteTexture();
|
|
|
|
|
}
|
2026-03-16 17:33:46 +01:00
|
|
|
|
2026-04-03 12:52:30 +02:00
|
|
|
static PDGfxBackendFlags GetFlags() { return driver->GetFlags(); }
|
2026-03-22 21:50:53 +01:00
|
|
|
|
2026-03-16 17:33:46 +01:00
|
|
|
static const char* GetDriverName() { return driver->GetName(); }
|
|
|
|
|
|
2026-03-25 20:27:23 +01:00
|
|
|
static size_t GetNumVertices() { return driver->GetNumVertices(); }
|
|
|
|
|
static size_t GetNumIndices() { return driver->GetNumIndices(); }
|
|
|
|
|
static size_t GetNumDrawcalls() { return driver->GetNumDrawcalls(); }
|
|
|
|
|
static size_t GetNumCommands() { return driver->GetNumCommands(); }
|
|
|
|
|
|
2026-09-02 08:04:50 +02:00
|
|
|
// Gloabal Pool Interface
|
|
|
|
|
static size_t AllocateVertices(size_t count, PD::ptr accessor) {
|
|
|
|
|
return driver->AllocateVertices(count, accessor);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static size_t AllocateIndices(size_t count, PD::ptr accessor) {
|
|
|
|
|
return driver->AllocateIndices(count, accessor);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool ExpandVertices(size_t count, PD::ptr accessor) {
|
|
|
|
|
return driver->ExpandVertices(count, accessor);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool ExpandIndices(size_t count, PD::ptr accessor) {
|
|
|
|
|
return driver->ExpandIndices(count, accessor);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void PutVertex(size_t loc, const Li::Vertex& vtx, PD::ptr accessor) {
|
|
|
|
|
driver->PutVertex(loc, vtx, accessor);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void PutIndex(size_t loc, u16 idx, PD::ptr accessor) {
|
|
|
|
|
driver->PutIndex(loc, idx, accessor);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const Li::Vertex& GetVertex(size_t loc) {
|
|
|
|
|
return driver->GetVertex(loc);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const u16& GetIndex(size_t loc) { return driver->GetIndex(loc); }
|
|
|
|
|
|
2026-09-03 08:12:34 +02:00
|
|
|
static void NewFrame() { driver->ResetPools(); }
|
|
|
|
|
|
2026-03-16 06:37:51 +01:00
|
|
|
private:
|
|
|
|
|
static std::unique_ptr<GfxDriver> driver;
|
2025-06-22 21:05:09 +02:00
|
|
|
};
|
|
|
|
|
} // namespace PD
|