OpenGL backend (no more software rendering!)
This commit is contained in:
+18
-2
@@ -16,6 +16,8 @@ set(IMXUI_CORE_SOURCES
|
||||
src/XzpPackage.cpp
|
||||
src/XuiXmlWriter.cpp
|
||||
src/XurWriter.cpp
|
||||
src/Audio.cpp
|
||||
src/ImageManager.cpp
|
||||
)
|
||||
|
||||
|
||||
@@ -29,6 +31,7 @@ set(IMXUI_BACKEND_SOURCES
|
||||
src/Skin.cpp
|
||||
)
|
||||
|
||||
|
||||
set(IMXUI_HEADERS
|
||||
include/ImXui/ImXui.h
|
||||
include/ImXui/RenderCommands.h
|
||||
@@ -38,12 +41,14 @@ set(IMXUI_HEADERS
|
||||
include/ImXui/Skin.h
|
||||
include/ImXui/XurWriter.h
|
||||
include/ImXui/XuiXmlWriter.h
|
||||
include/ImXui/Audio.h
|
||||
)
|
||||
|
||||
|
||||
add_library(ImXuiCore STATIC ${IMXUI_CORE_SOURCES} ${IMXUI_HEADERS})
|
||||
target_include_directories(ImXuiCore PUBLIC include compat/include)
|
||||
target_include_directories(ImXuiCore PUBLIC include compat/include third_party)
|
||||
target_include_directories(ImXuiCore PRIVATE src)
|
||||
target_include_directories(ImXuiCore PUBLIC include)
|
||||
|
||||
set(IMXUI_HEADERS ${IMXUI_HEADERS} compat/include/xui.h compat/include/xuiapp.h
|
||||
compat/include/xuirender.h compat/include/xuiresource.h)
|
||||
@@ -51,13 +56,16 @@ set(IMXUI_HEADERS ${IMXUI_HEADERS} compat/include/xui.h compat/include/xuiapp.h
|
||||
|
||||
add_library(ImXuiSoft STATIC
|
||||
src/FontEngine.cpp
|
||||
src/ImageManager.cpp
|
||||
src/SoftwareRenderer.cpp
|
||||
)
|
||||
target_include_directories(ImXuiSoft PUBLIC include)
|
||||
target_include_directories(ImXuiSoft PRIVATE src third_party)
|
||||
target_link_libraries(ImXuiSoft PUBLIC ImXuiCore)
|
||||
|
||||
if(NOT WIN32)
|
||||
find_package(OpenGL)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
add_library(ImXui STATIC ${IMXUI_BACKEND_SOURCES} ${IMXUI_HEADERS})
|
||||
target_include_directories(ImXui PUBLIC include)
|
||||
@@ -78,6 +86,13 @@ if(WIN32)
|
||||
endif()
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
if(OpenGL_FOUND)
|
||||
target_sources(ImXuiSoft PRIVATE src/OpenGLRenderer.cpp)
|
||||
target_compile_definitions(ImXuiCore PRIVATE IMXUI_HAS_OPENGL IMXUI_HAS_GLX)
|
||||
target_compile_definitions(ImXuiSoft PRIVATE IMXUI_HAS_OPENGL IMXUI_HAS_GLX)
|
||||
target_compile_definitions(ImXuiSoft PRIVATE IMXUI_HAS_OPENGL IMXUI_HAS_GLX)
|
||||
target_link_libraries(ImXuiSoft PUBLIC OpenGL::GL)
|
||||
endif()
|
||||
add_executable(NotepadDemo examples/NotepadDemo.cpp)
|
||||
target_link_libraries(NotepadDemo PRIVATE ImXuiSoft ImXuiCore X11)
|
||||
target_include_directories(NotepadDemo PRIVATE include)
|
||||
@@ -127,4 +142,5 @@ target_include_directories(XUITool PRIVATE include third_party/zlib)
|
||||
if(WIN32)
|
||||
install(TARGETS ImXui LIBRARY DESTINATION lib ARCHIVE DESTINATION lib)
|
||||
endif()
|
||||
|
||||
install(DIRECTORY include/ DESTINATION include)
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
#ifdef _WIN32
|
||||
static ImXui::D3D11Renderer s_renderer;
|
||||
#else
|
||||
static ImXui::SoftwareRenderer s_renderer;
|
||||
static ImXui::OpenGLRenderer s_renderer;
|
||||
#endif
|
||||
static ImXui::Canvas s_canvas;
|
||||
static bool s_running = true;
|
||||
@@ -2773,30 +2773,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int nCmdShow) {
|
||||
CoUninitialize();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// 24-bit visuals expect BGRA byte order on x86, the renderer gives RGBA
|
||||
static void swizzleToBlit() {
|
||||
const uint8_t* fb = s_renderer.getFramebuffer();
|
||||
size_t n = s_blit.size();
|
||||
for (size_t i = 0; i < n; i += 4) {
|
||||
s_blit[i] = fb[i + 2];
|
||||
s_blit[i + 1] = fb[i + 1];
|
||||
s_blit[i + 2] = fb[i];
|
||||
s_blit[i + 3] = 255;
|
||||
}
|
||||
}
|
||||
|
||||
static void rebuildImage() {
|
||||
if (s_img) XDestroyImage(s_img);
|
||||
int w = s_renderer.getWidth();
|
||||
int h = s_renderer.getHeight();
|
||||
s_blit.resize((size_t)w * h * 4);
|
||||
s_img = XCreateImage(s_dpy, s_visual, s_depth, ZPixmap, 0,
|
||||
(char*)s_blit.data(), w, h, 32, w * 4);
|
||||
}
|
||||
|
||||
static int x11Vk(KeySym ks, bool ctrl) {
|
||||
switch (ks) {
|
||||
case XK_Left: return VK_LEFT;
|
||||
@@ -2838,7 +2815,6 @@ static void handleXEvent(XEvent& ev, Atom wmDelete) {
|
||||
(ev.xconfigure.width != s_renderer.getWidth() ||
|
||||
ev.xconfigure.height != s_renderer.getHeight())) {
|
||||
s_renderer.resize(ev.xconfigure.width, ev.xconfigure.height);
|
||||
rebuildImage();
|
||||
layoutUI();
|
||||
s_needRender = true;
|
||||
}
|
||||
@@ -3000,8 +2976,6 @@ int main() {
|
||||
ButtonPressMask | ButtonReleaseMask | PointerMotionMask |
|
||||
StructureNotifyMask);
|
||||
XMapWindow(s_dpy, s_win);
|
||||
s_gc = XCreateGC(s_dpy, s_win, 0, nullptr);
|
||||
|
||||
s_curArrow = XCreateFontCursor(s_dpy, XC_left_ptr);
|
||||
s_curIBeam = XCreateFontCursor(s_dpy, XC_xterm);
|
||||
s_curHand = XCreateFontCursor(s_dpy, XC_hand2);
|
||||
@@ -3018,7 +2992,7 @@ int main() {
|
||||
XSetInputFocus(s_dpy, s_win, RevertToParent, CurrentTime);
|
||||
XFlush(s_dpy);
|
||||
|
||||
if (!s_renderer.init(nullptr, 1280, 800)) {
|
||||
if (!s_renderer.init(s_dpy, 1280, 800)) {
|
||||
fprintf(stderr, "Renderer init failed\n");
|
||||
return 1;
|
||||
}
|
||||
@@ -3038,9 +3012,7 @@ int main() {
|
||||
|
||||
setFontIdx(1);
|
||||
layoutUI();
|
||||
scrollToCaret();
|
||||
s_rendererReady = true;
|
||||
rebuildImage();
|
||||
scrollToCaret(); s_rendererReady = true;
|
||||
|
||||
unsigned long lastBlink = nowMs();
|
||||
while (s_running) {
|
||||
@@ -3071,17 +3043,11 @@ int main() {
|
||||
drawFileDialog();
|
||||
s_renderer.endFrame(false);
|
||||
s_blinkOnly = false;
|
||||
swizzleToBlit();
|
||||
XPutImage(s_dpy, s_win, s_gc, s_img, 0, 0, 0, 0,
|
||||
s_renderer.getWidth(), s_renderer.getHeight());
|
||||
XFlush(s_dpy);
|
||||
}
|
||||
usleep(5000);
|
||||
if (!s_needRender && XPending(s_dpy) == 0) usleep(1000);
|
||||
}
|
||||
|
||||
s_renderer.shutdown();
|
||||
XDestroyImage(s_img);
|
||||
XFreeGC(s_dpy, s_gc);
|
||||
XDestroyWindow(s_dpy, s_win);
|
||||
XCloseDisplay(s_dpy);
|
||||
return 0;
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include "ImXui.h"
|
||||
|
||||
namespace ImXui {
|
||||
|
||||
class AudioBackend : public ISoundSink {
|
||||
public:
|
||||
virtual ~AudioBackend() {}
|
||||
virtual bool init() = 0;
|
||||
virtual void shutdown() = 0;
|
||||
};
|
||||
|
||||
class NullAudioBackend : public AudioBackend {
|
||||
public:
|
||||
bool init() override { return true; }
|
||||
void shutdown() override {}
|
||||
void playCue(const std::wstring&, const std::wstring&, const std::wstring&, float, bool) override {}
|
||||
void stopCue(const std::wstring&) override {}
|
||||
};
|
||||
|
||||
#if defined(_WIN32)
|
||||
class WinMMAudioBackend : public AudioBackend {
|
||||
public:
|
||||
bool init() override;
|
||||
void shutdown() override;
|
||||
void playCue(const std::wstring&, const std::wstring&, const std::wstring&, float, bool) override;
|
||||
void stopCue(const std::wstring&) override;
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
class CoreAudioBackend : public AudioBackend {
|
||||
public:
|
||||
bool init() override;
|
||||
void shutdown() override;
|
||||
void playCue(const std::wstring&, const std::wstring&, const std::wstring&, float, bool) override;
|
||||
void stopCue(const std::wstring&) override;
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(__linux__)
|
||||
class ALSAAudioBackend : public AudioBackend {
|
||||
public:
|
||||
bool init() override;
|
||||
void shutdown() override;
|
||||
void playCue(const std::wstring&, const std::wstring&, const std::wstring&, float, bool) override;
|
||||
void stopCue(const std::wstring&) override;
|
||||
};
|
||||
#endif
|
||||
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "ImXui.h"
|
||||
#include "TextureManager.h"
|
||||
#include "Skin.h"
|
||||
#include "RenderCommands.h"
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <map>
|
||||
@@ -77,6 +78,61 @@ public:
|
||||
};
|
||||
|
||||
|
||||
class OpenGLRenderer : public Renderer, public IRenderCommandSink {
|
||||
public:
|
||||
bool loadSystemFont(const std::string& name);
|
||||
float measureText(const std::wstring& text, float sizePx) const;
|
||||
float lineHeight(float sizePx) const;
|
||||
const uint8_t* getFramebuffer() const;
|
||||
OpenGLRenderer();
|
||||
~OpenGLRenderer();
|
||||
|
||||
bool init(void* nativeWindow, int width, int height) override;
|
||||
void shutdown() override;
|
||||
bool resize(int width, int height) override;
|
||||
bool beginFrame(float clearR = 0, float clearG = 0, float clearB = 0, float clearA = 1) override;
|
||||
void endFrame(bool vsync = true) override;
|
||||
void renderScene(const Scene* scene) override;
|
||||
void setTextureManager(TextureManager* mgr) override;
|
||||
void setSkin(const Skin* skin) override;
|
||||
const Skin* getSkin() const override;
|
||||
bool loadFont(const wchar_t* path) override;
|
||||
bool initTextRendering() override;
|
||||
void shutdownTextRendering() override;
|
||||
void drawTexturedQuad(float x, float y, float w, float h, const wchar_t* texturePath,
|
||||
float r = 1, float g = 1, float b = 1, float a = 1) override;
|
||||
void drawFigure(float x, float y, float w, float h,
|
||||
const std::vector<Vec2>& points, bool closed,
|
||||
float fillR, float fillG, float fillB, float fillA,
|
||||
float strokeR, float strokeG, float strokeB, float strokeA,
|
||||
float strokeWidth) override;
|
||||
void drawColoredQuad(float x, float y, float w, float h,
|
||||
float r, float g, float b, float a) override;
|
||||
void drawTextString(float x, float y, float w, float h,
|
||||
const std::wstring& text, const std::wstring& fontName, float fontSize,
|
||||
float r = 1, float g = 1, float b = 1, float a = 1,
|
||||
bool centered = false) override;
|
||||
int getWidth() const override;
|
||||
int getHeight() const override;
|
||||
|
||||
void beginScene(float width, float height) override;
|
||||
void drawColoredQuad(const Rect& rect, const Color& color, float opacity) override;
|
||||
void drawTexturedQuad(const Rect& rect, const std::wstring& resource, const Color& color, float opacity) override;
|
||||
void drawText(const Rect& rect, const std::wstring& text, const std::wstring& fontName, float fontSize, const Color& color, float opacity, bool centered) override;
|
||||
void endScene() override;
|
||||
void pushClip(const Rect& rect) override;
|
||||
void popClip() override;
|
||||
void drawNineGrid(const Rect& rect, const std::wstring& resource, const Color& color, float opacity, uint32_t leftInset, uint32_t topInset, uint32_t rightInset, uint32_t bottomInset, bool noCenter) override;
|
||||
void drawFigure(const Rect& rect, const std::vector<Vec2>& points, bool closed, const Color& fillColor, const Color& strokeColor, float opacity, float strokeWidth, uint32_t blendMode) override;
|
||||
void setBlendMode(uint32_t blendMode) override;
|
||||
void drawCustom(const std::wstring& drawId, const Rect& rect, float opacity) override;
|
||||
|
||||
struct Impl;
|
||||
private:
|
||||
Impl* m_impl;
|
||||
};
|
||||
|
||||
|
||||
class D3D11Renderer : public Renderer {
|
||||
public:
|
||||
D3D11Renderer();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "ImageManager.h"
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <cstdint>
|
||||
@@ -20,13 +21,14 @@ public:
|
||||
void setBasePath(const std::string& path) { m_basePath = path; }
|
||||
const std::string& getBasePath() const { return m_basePath; }
|
||||
|
||||
const ImageData* loadImage(const std::string& relativePath);
|
||||
ID3D11ShaderResourceView* load(const std::string& relativePath);
|
||||
|
||||
bool getSize(const std::string& relativePath, int& w, int& h) const;
|
||||
|
||||
private:
|
||||
ID3D11Device* m_device;
|
||||
std::string m_basePath;
|
||||
ImageManager m_images;
|
||||
std::unordered_map<std::string, ID3D11ShaderResourceView*> m_cache;
|
||||
std::unordered_map<std::string, int> m_widths;
|
||||
std::unordered_map<std::string, int> m_heights;
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
#include "ImXui/Audio.h"
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <windows.h>
|
||||
#include <mmsystem.h>
|
||||
#pragma comment(lib, "winmm.lib")
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <AudioToolbox/AudioToolbox.h>
|
||||
#endif
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <cstdlib>
|
||||
#endif
|
||||
|
||||
namespace ImXui {
|
||||
|
||||
#if defined(_WIN32)
|
||||
bool WinMMAudioBackend::init() { return true; }
|
||||
void WinMMAudioBackend::shutdown() { stopCue(std::wstring()); }
|
||||
void WinMMAudioBackend::playCue(const std::wstring& cue, const std::wstring&, const std::wstring&, float, bool loop) {
|
||||
if (cue.empty()) return;
|
||||
DWORD flags = SND_FILENAME | SND_ASYNC | SND_NODEFAULT;
|
||||
if (loop) flags |= SND_LOOP;
|
||||
PlaySoundW(cue.c_str(), nullptr, flags);
|
||||
}
|
||||
void WinMMAudioBackend::stopCue(const std::wstring&) { PlaySoundW(nullptr, nullptr, 0); }
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
bool CoreAudioBackend::init() { return true; }
|
||||
void CoreAudioBackend::shutdown() {}
|
||||
void CoreAudioBackend::playCue(const std::wstring& cue, const std::wstring&, const std::wstring&, float, bool) {
|
||||
if (cue.empty()) return;
|
||||
CFURLRef url = CFURLCreateFromFileSystemRepresentation(nullptr,
|
||||
reinterpret_cast<const UInt8*>(cue.c_str()), cue.size() * sizeof(wchar_t), false);
|
||||
if (!url) return;
|
||||
SystemSoundID sound = 0;
|
||||
if (AudioServicesCreateSystemSoundID(url, &sound) == kAudioServicesNoError) {
|
||||
AudioServicesPlaySystemSound(sound);
|
||||
AudioServicesDisposeSystemSoundID(sound);
|
||||
}
|
||||
CFRelease(url);
|
||||
}
|
||||
void CoreAudioBackend::stopCue(const std::wstring&) {}
|
||||
#endif
|
||||
|
||||
#if defined(__linux__)
|
||||
bool ALSAAudioBackend::init() { return true; }
|
||||
void ALSAAudioBackend::shutdown() {}
|
||||
void ALSAAudioBackend::playCue(const std::wstring& cue, const std::wstring&, const std::wstring&, float, bool) {
|
||||
if (cue.empty()) return;
|
||||
std::string path(cue.begin(), cue.end());
|
||||
std::string command = "aplay -q \"" + path + "\" >/dev/null 2>&1 &";
|
||||
std::system(command.c_str());
|
||||
}
|
||||
void ALSAAudioBackend::stopCue(const std::wstring&) {}
|
||||
#endif
|
||||
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
#include "ImXui/Renderer.h"
|
||||
#include "ImXui/RenderCommands.h"
|
||||
#include "ImXui/ImageManager.h"
|
||||
#include "ImXui/FontEngine.h"
|
||||
#if defined(IMXUI_HAS_OPENGL)
|
||||
#define GL_GLEXT_PROTOTYPES 1
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glext.h>
|
||||
#if defined(IMXUI_HAS_GLX)
|
||||
|
||||
#include <GL/glx.h>
|
||||
#include <X11/Xlib.h>
|
||||
#endif
|
||||
#endif
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <cwchar>
|
||||
|
||||
namespace ImXui {
|
||||
struct OpenGLRenderer::Impl {
|
||||
int width, height;
|
||||
TextureManager* textureManager;
|
||||
const Skin* skin;
|
||||
ImageManager images;
|
||||
FontEngine font;
|
||||
std::map<std::string, unsigned> textures;
|
||||
std::map<std::wstring, unsigned> textTextures;
|
||||
unsigned white;
|
||||
bool initialized;
|
||||
#if defined(IMXUI_HAS_OPENGL)
|
||||
unsigned program, vao, vbo;
|
||||
int projection;
|
||||
std::vector<RendererVertex2D> vertices;
|
||||
unsigned batchTexture;
|
||||
float batchBlendSrc, batchBlendDst;
|
||||
#endif
|
||||
#if defined(IMXUI_HAS_GLX)
|
||||
Display* display;
|
||||
GLXDrawable drawable;
|
||||
#endif
|
||||
Impl() : width(0), height(0), textureManager(nullptr), skin(nullptr), white(0), initialized(false)
|
||||
#if defined(IMXUI_HAS_OPENGL)
|
||||
, program(0), vao(0), vbo(0), projection(-1), batchTexture(0), batchBlendSrc(0), batchBlendDst(0)
|
||||
#endif
|
||||
#if defined(IMXUI_HAS_GLX)
|
||||
, display(nullptr), drawable(0)
|
||||
#endif
|
||||
{}
|
||||
};
|
||||
|
||||
namespace {
|
||||
static std::string narrow(const std::wstring& s) { return std::string(s.begin(), s.end()); }
|
||||
#if defined(IMXUI_HAS_OPENGL)
|
||||
static unsigned compileShader(unsigned type, const char* source) {
|
||||
unsigned shader = glCreateShader(type);
|
||||
glShaderSource(shader, 1, &source, nullptr);
|
||||
glCompileShader(shader);
|
||||
int ok = 0;
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &ok);
|
||||
if (!ok) { glDeleteShader(shader); return 0; }
|
||||
return shader;
|
||||
}
|
||||
static unsigned createProgram() {
|
||||
static const char* vs = "attribute vec2 a_position; attribute vec2 a_uv; attribute vec4 a_color; varying vec2 v_uv; varying vec4 v_color; uniform vec2 u_size; void main(){ vec2 p=a_position/u_size*2.0-1.0; gl_Position=vec4(p.x,-p.y,0.0,1.0); v_uv=a_uv; v_color=a_color; }";
|
||||
static const char* fs = "uniform sampler2D u_texture; varying vec2 v_uv; varying vec4 v_color; void main(){ gl_FragColor=texture2D(u_texture,v_uv)*v_color; }";
|
||||
unsigned v = compileShader(GL_VERTEX_SHADER, vs), f = compileShader(GL_FRAGMENT_SHADER, fs);
|
||||
if (!v || !f) { if (v) glDeleteShader(v); if (f) glDeleteShader(f); return 0; }
|
||||
unsigned p = glCreateProgram(); glAttachShader(p, v); glAttachShader(p, f); glBindAttribLocation(p, 0, "a_position"); glBindAttribLocation(p, 1, "a_uv"); glBindAttribLocation(p, 2, "a_color"); glLinkProgram(p); glDeleteShader(v); glDeleteShader(f);
|
||||
int ok = 0; glGetProgramiv(p, GL_LINK_STATUS, &ok); if (!ok) { glDeleteProgram(p); return 0; } return p;
|
||||
}
|
||||
static unsigned makeTexture(const ImageData& image) {
|
||||
unsigned texture = 0; glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.w, image.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, image.rgba.data()); return texture;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
OpenGLRenderer::OpenGLRenderer() : m_impl(new Impl()) {}
|
||||
OpenGLRenderer::~OpenGLRenderer() { shutdown(); delete m_impl; }
|
||||
|
||||
bool OpenGLRenderer::init(void* window, int w, int h) {
|
||||
shutdown(); if (w <= 0 || h <= 0) return false; m_impl->width = w; m_impl->height = h;
|
||||
#if defined(IMXUI_HAS_OPENGL)
|
||||
#if defined(IMXUI_HAS_GLX)
|
||||
m_impl->display = static_cast<Display*>(window); m_impl->drawable = m_impl->display ? glXGetCurrentDrawable() : 0;
|
||||
#else
|
||||
(void)window;
|
||||
#endif
|
||||
m_impl->program = createProgram(); if (!m_impl->program) return false;
|
||||
glGenVertexArrays(1, &m_impl->vao); glGenBuffers(1, &m_impl->vbo);
|
||||
glBindVertexArray(m_impl->vao); glBindBuffer(GL_ARRAY_BUFFER, m_impl->vbo); glBufferData(GL_ARRAY_BUFFER, sizeof(RendererVertex2D) * 4096, nullptr, GL_DYNAMIC_DRAW);
|
||||
glEnableVertexAttribArray(0); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(RendererVertex2D), (void*)0);
|
||||
glEnableVertexAttribArray(1); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(RendererVertex2D), (void*)(sizeof(float) * 2));
|
||||
glEnableVertexAttribArray(2); glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(RendererVertex2D), (void*)(sizeof(float) * 4));
|
||||
m_impl->projection = glGetUniformLocation(m_impl->program, "u_size");
|
||||
glUseProgram(m_impl->program); glUniform1i(glGetUniformLocation(m_impl->program, "u_texture"), 0);
|
||||
ImageData white; white.w = 1; white.h = 1; white.rgba.assign(4, 255); m_impl->white = makeTexture(white);
|
||||
m_impl->vertices.reserve(4096); m_impl->initialized = m_impl->white != 0; return m_impl->initialized;
|
||||
#else
|
||||
(void)window; return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void OpenGLRenderer::shutdown() {
|
||||
#if defined(IMXUI_HAS_OPENGL)
|
||||
if (!m_impl->initialized && !m_impl->program) return;
|
||||
glUseProgram(0); for (auto& x : m_impl->textures) if (x.second) glDeleteTextures(1, &x.second); for (auto& x : m_impl->textTextures) if (x.second) glDeleteTextures(1, &x.second); if (m_impl->white) glDeleteTextures(1, &m_impl->white); if (m_impl->vbo) glDeleteBuffers(1, &m_impl->vbo); if (m_impl->vao) glDeleteVertexArrays(1, &m_impl->vao); if (m_impl->program) glDeleteProgram(m_impl->program);
|
||||
#endif
|
||||
m_impl->textures.clear(); m_impl->textTextures.clear(); m_impl->white = 0; m_impl->initialized = false;
|
||||
#if defined(IMXUI_HAS_OPENGL)
|
||||
m_impl->program = m_impl->vao = m_impl->vbo = 0; m_impl->vertices.clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool OpenGLRenderer::resize(int w, int h) { if (w <= 0 || h <= 0) return false; m_impl->width = w; m_impl->height = h;
|
||||
#if defined(IMXUI_HAS_OPENGL)
|
||||
if (!m_impl->initialized) return false; glViewport(0, 0, w, h);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenGLRenderer::beginFrame(float r, float g, float b, float a) {
|
||||
#if defined(IMXUI_HAS_OPENGL)
|
||||
if (!m_impl->initialized) return false; glViewport(0, 0, m_impl->width, m_impl->height); glClearColor(r, g, b, a); glClear(GL_COLOR_BUFFER_BIT); glUseProgram(m_impl->program); glUniform2f(m_impl->projection, (float)m_impl->width, (float)m_impl->height); glBindVertexArray(m_impl->vao); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); m_impl->vertices.clear(); m_impl->batchTexture = 0; return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void OpenGLRenderer::endFrame(bool vsync) {
|
||||
#if defined(IMXUI_HAS_OPENGL)
|
||||
if (!m_impl->vertices.empty()) { glBindBuffer(GL_ARRAY_BUFFER, m_impl->vbo); glBufferSubData(GL_ARRAY_BUFFER, 0, (GLsizeiptr)(m_impl->vertices.size() * sizeof(RendererVertex2D)), m_impl->vertices.data()); glBindTexture(GL_TEXTURE_2D, m_impl->batchTexture); glDrawArrays(GL_TRIANGLES, 0, (GLsizei)m_impl->vertices.size()); m_impl->vertices.clear(); }
|
||||
#if defined(IMXUI_HAS_GLX)
|
||||
if (vsync && m_impl->display && m_impl->drawable) glXSwapBuffers(m_impl->display, m_impl->drawable);
|
||||
#else
|
||||
(void)vsync;
|
||||
#endif
|
||||
#else
|
||||
(void)vsync;
|
||||
#endif
|
||||
}
|
||||
|
||||
void OpenGLRenderer::renderScene(const Scene* s) { if (s) emitRenderCommands(s, static_cast<IRenderCommandSink*>(this)); }
|
||||
void OpenGLRenderer::setTextureManager(TextureManager* m) { m_impl->textureManager = m; }
|
||||
void OpenGLRenderer::setSkin(const Skin* s) { m_impl->skin = s; }
|
||||
const Skin* OpenGLRenderer::getSkin() const { return m_impl->skin; }
|
||||
bool OpenGLRenderer::loadFont(const wchar_t* p) { return p && m_impl->font.loadFontFile(narrow(p)); }
|
||||
bool OpenGLRenderer::loadSystemFont(const std::string& n) { return m_impl->font.loadSystemFont(n); }
|
||||
float OpenGLRenderer::measureText(const std::wstring& t, float s) const { return m_impl->font.measureText(t, s); }
|
||||
float OpenGLRenderer::lineHeight(float s) const { return m_impl->font.lineHeight(s); }
|
||||
const uint8_t* OpenGLRenderer::getFramebuffer() const { return nullptr; }
|
||||
bool OpenGLRenderer::initTextRendering() { return m_impl->font.isLoaded() || m_impl->font.loadSystemFont(""); }
|
||||
void OpenGLRenderer::shutdownTextRendering() {}
|
||||
|
||||
#if defined(IMXUI_HAS_OPENGL)
|
||||
static void appendQuad(OpenGLRenderer::Impl* i, unsigned texture, float x, float y, float w, float h, float u0, float v0, float u1, float v1, float r, float g, float b, float a) {
|
||||
if (i->batchTexture != texture && !i->vertices.empty()) { glBindBuffer(GL_ARRAY_BUFFER, i->vbo); glBufferSubData(GL_ARRAY_BUFFER, 0, (GLsizeiptr)(i->vertices.size() * sizeof(RendererVertex2D)), i->vertices.data()); glBindTexture(GL_TEXTURE_2D, i->batchTexture); glDrawArrays(GL_TRIANGLES, 0, (GLsizei)i->vertices.size()); i->vertices.clear(); }
|
||||
i->batchTexture = texture;
|
||||
RendererVertex2D v[6] = {{x,y,u0,v0,r,g,b,a},{x+w,y,u1,v0,r,g,b,a},{x+w,y+h,u1,v1,r,g,b,a},{x,y,u0,v0,r,g,b,a},{x+w,y+h,u1,v1,r,g,b,a},{x,y+h,u0,v1,r,g,b,a}};
|
||||
i->vertices.insert(i->vertices.end(), v, v + 6);
|
||||
}
|
||||
#endif
|
||||
|
||||
void OpenGLRenderer::drawTexturedQuad(float x, float y, float w, float h, const wchar_t* p, float r, float g, float b, float a) {
|
||||
if (!p) return; std::string path(p, p + wcslen(p)); const ImageData* image = m_impl->images.load(path);
|
||||
#if defined(IMXUI_HAS_OPENGL)
|
||||
if (!m_impl->initialized || !image || !image->valid()) return; unsigned texture = 0; auto it = m_impl->textures.find(path); if (it != m_impl->textures.end()) texture = it->second; if (!texture) { texture = makeTexture(*image); m_impl->textures[path] = texture; } appendQuad(m_impl, texture, x, y, w, h, 0, 0, 1, 1, r, g, b, a);
|
||||
#else
|
||||
(void)x; (void)y; (void)w; (void)h; (void)r; (void)g; (void)b; (void)a;
|
||||
#endif
|
||||
}
|
||||
void OpenGLRenderer::drawColoredQuad(float x, float y, float w, float h, float r, float g, float b, float a) {
|
||||
#if defined(IMXUI_HAS_OPENGL)
|
||||
if (m_impl->initialized) appendQuad(m_impl, m_impl->white, x, y, w, h, 0, 0, 1, 1, r, g, b, a);
|
||||
#else
|
||||
(void)x; (void)y; (void)w; (void)h; (void)r; (void)g; (void)b; (void)a;
|
||||
#endif
|
||||
}
|
||||
void OpenGLRenderer::drawTextString(float x, float y, float w, float h, const std::wstring& text, const std::wstring& name, float size, float r, float g, float b, float a, bool centered) {
|
||||
#if defined(IMXUI_HAS_OPENGL)
|
||||
if (!m_impl->initialized || text.empty()) return; if (!m_impl->font.isLoaded() && !m_impl->font.loadSystemFont(narrow(name))) return; auto it = m_impl->textTextures.find(text + L"|" + name + L"|" + std::to_wstring((int)(size * 10))); unsigned texture = it == m_impl->textTextures.end() ? 0 : it->second; int tw = 0, th = 0; if (!texture) { std::vector<uint8_t> pixels; if (!m_impl->font.renderText(text, size, pixels, tw, th)) return; ImageData image; image.w = tw; image.h = th; image.rgba.swap(pixels); texture = makeTexture(image); m_impl->textTextures[text + L"|" + name + L"|" + std::to_wstring((int)(size * 10))] = texture; } else { std::vector<uint8_t> ignored; m_impl->font.renderText(text, size, ignored, tw, th); } float dx = centered ? x + std::max(0.0f, (w - tw) * .5f) : x, dy = centered ? y + std::max(0.0f, (h - th) * .5f) : y; appendQuad(m_impl, texture, dx, dy, (float)tw, (float)th, 0, 0, 1, 1, r, g, b, a);
|
||||
#else
|
||||
(void)x; (void)y; (void)w; (void)h; (void)text; (void)name; (void)size; (void)r; (void)g; (void)b; (void)a; (void)centered;
|
||||
#endif
|
||||
}
|
||||
void OpenGLRenderer::drawFigure(float x, float y, float w, float h, const std::vector<Vec2>& points, bool closed, float fr, float fg, float fb, float fa, float sr, float sg, float sb, float sa, float sw) {
|
||||
#if defined(IMXUI_HAS_OPENGL)
|
||||
if (!m_impl->initialized || points.size() < 2) return; if (fa > .001f && points.size() > 2) { for (size_t n = 1; n + 1 < points.size(); ++n) { RendererVertex2D v[6] = {{x+points[0].x*w,y+points[0].y*h,0,0,fr,fg,fb,fa},{x+points[n].x*w,y+points[n].y*h,0,0,fr,fg,fb,fa},{x+points[n+1].x*w,y+points[n+1].y*h,0,0,fr,fg,fb,fa},{x+points[0].x*w,y+points[0].y*h,0,0,fr,fg,fb,fa},{x+points[n+1].x*w,y+points[n+1].y*h,0,0,fr,fg,fb,fa},{x+points[n].x*w,y+points[n].y*h,0,0,fr,fg,fb,fa}}; if (m_impl->batchTexture != m_impl->white && !m_impl->vertices.empty()) { glBindBuffer(GL_ARRAY_BUFFER, m_impl->vbo); glBufferSubData(GL_ARRAY_BUFFER,0,(GLsizeiptr)(m_impl->vertices.size()*sizeof(RendererVertex2D)),m_impl->vertices.data()); glBindTexture(GL_TEXTURE_2D,m_impl->batchTexture); glDrawArrays(GL_TRIANGLES,0,(GLsizei)m_impl->vertices.size()); m_impl->vertices.clear(); } m_impl->batchTexture=m_impl->white; m_impl->vertices.insert(m_impl->vertices.end(),v,v+6); } } if (sa > .001f && sw > 0) { glLineWidth(sw); std::vector<RendererVertex2D> v; for (size_t n=0; n + 1 < points.size(); ++n) { v.push_back({x+points[n].x*w,y+points[n].y*h,0,0,sr,sg,sb,sa}); v.push_back({x+points[n+1].x*w,y+points[n+1].y*h,0,0,sr,sg,sb,sa}); } if (closed) { v.push_back({x+points.back().x*w,y+points.back().y*h,0,0,sr,sg,sb,sa}); v.push_back({x+points[0].x*w,y+points[0].y*h,0,0,sr,sg,sb,sa}); } if (m_impl->batchTexture != m_impl->white && !m_impl->vertices.empty()) { glBindBuffer(GL_ARRAY_BUFFER,m_impl->vbo); glBufferSubData(GL_ARRAY_BUFFER,0,(GLsizeiptr)(m_impl->vertices.size()*sizeof(RendererVertex2D)),m_impl->vertices.data()); glBindTexture(GL_TEXTURE_2D,m_impl->batchTexture); glDrawArrays(GL_TRIANGLES,0,(GLsizei)m_impl->vertices.size()); m_impl->vertices.clear(); } m_impl->batchTexture=m_impl->white; glBindBuffer(GL_ARRAY_BUFFER,m_impl->vbo); glBufferSubData(GL_ARRAY_BUFFER,0,(GLsizeiptr)(v.size()*sizeof(RendererVertex2D)),v.data()); glDrawArrays(GL_LINES,0,(GLsizei)v.size()); }
|
||||
#else
|
||||
(void)x;(void)y;(void)w;(void)h;(void)points;(void)closed;(void)fr;(void)fg;(void)fb;(void)fa;(void)sr;(void)sg;(void)sb;(void)sa;(void)sw;
|
||||
#endif
|
||||
}
|
||||
int OpenGLRenderer::getWidth() const { return m_impl->width; }
|
||||
int OpenGLRenderer::getHeight() const { return m_impl->height; }
|
||||
void OpenGLRenderer::beginScene(float, float) {}
|
||||
void OpenGLRenderer::drawColoredQuad(const Rect& r, const Color& c, float o) { drawColoredQuad(r.x,r.y,r.w,r.h,c.r,c.g,c.b,c.a*o); }
|
||||
void OpenGLRenderer::drawTexturedQuad(const Rect& r, const std::wstring& p, const Color& c, float o) { drawTexturedQuad(r.x,r.y,r.w,r.h,p.c_str(),c.r,c.g,c.b,c.a*o); }
|
||||
void OpenGLRenderer::drawText(const Rect& r, const std::wstring& t, const std::wstring& f, float s, const Color& c, float o, bool center) { drawTextString(r.x,r.y,r.w,r.h,t,f,s,c.r,c.g,c.b,c.a*o,center); }
|
||||
void OpenGLRenderer::endScene() {}
|
||||
void OpenGLRenderer::pushClip(const Rect& r) {
|
||||
#if defined(IMXUI_HAS_OPENGL)
|
||||
glEnable(GL_SCISSOR_TEST); glScissor((int)r.x, (int)(m_impl->height-r.y-r.h), (int)r.w, (int)r.h);
|
||||
#else
|
||||
(void)r;
|
||||
#endif
|
||||
}
|
||||
void OpenGLRenderer::popClip() {
|
||||
#if defined(IMXUI_HAS_OPENGL)
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
#endif
|
||||
}
|
||||
void OpenGLRenderer::drawNineGrid(const Rect& r, const std::wstring& p, const Color& c, float o, uint32_t, uint32_t, uint32_t, uint32_t, bool) { drawTexturedQuad(r,p,c,o); }
|
||||
void OpenGLRenderer::drawFigure(const Rect& r, const std::vector<Vec2>& points, bool closed, const Color& fill, const Color& stroke, float opacity, float strokeWidth, uint32_t blendMode) { setBlendMode(blendMode); drawFigure(r.x,r.y,r.w,r.h,points,closed,fill.r,fill.g,fill.b,fill.a*opacity,stroke.r,stroke.g,stroke.b,stroke.a*opacity,strokeWidth); }
|
||||
void OpenGLRenderer::setBlendMode(uint32_t mode) {
|
||||
#if defined(IMXUI_HAS_OPENGL)
|
||||
glBlendFunc(GL_SRC_ALPHA, mode == 1 ? GL_ONE : GL_ONE_MINUS_SRC_ALPHA);
|
||||
#else
|
||||
(void)mode;
|
||||
#endif
|
||||
}
|
||||
void OpenGLRenderer::drawCustom(const std::wstring&, const Rect&, float) {}
|
||||
}
|
||||
@@ -10,9 +10,15 @@ namespace ImXui {
|
||||
|
||||
bool TextureManager::init(ID3D11Device* device) {
|
||||
m_device = device;
|
||||
m_images.setBasePath(m_basePath);
|
||||
return true;
|
||||
}
|
||||
|
||||
const ImageData* TextureManager::loadImage(const std::string& relativePath) {
|
||||
m_images.setBasePath(m_basePath);
|
||||
return m_images.load(relativePath);
|
||||
}
|
||||
|
||||
void TextureManager::shutdown() {
|
||||
for (std::unordered_map<std::string, ID3D11ShaderResourceView*>::iterator it = m_cache.begin(); it != m_cache.end(); ++it) {
|
||||
if (it->second) it->second->Release();
|
||||
@@ -29,6 +35,9 @@ ID3D11ShaderResourceView* TextureManager::load(const std::string& relativePath)
|
||||
auto it = m_cache.find(relativePath);
|
||||
if (it != m_cache.end()) return it->second;
|
||||
|
||||
const ImageData* decoded = loadImage(relativePath);
|
||||
if (!decoded) return nullptr;
|
||||
|
||||
std::string fullPath;
|
||||
if (!m_basePath.empty()) {
|
||||
fullPath = m_basePath;
|
||||
|
||||
Reference in New Issue
Block a user