GUI: render backend recovery, part 1

This commit is contained in:
tildearrow 2023-07-02 00:09:39 -05:00
parent 87f80cae1d
commit 5fde2e7e30
12 changed files with 195 additions and 98 deletions

View file

@ -536,6 +536,9 @@ void FurnaceGUI::drawDebug() {
if (ImGui::Button("Spoiler")) {
spoilerOpen=!spoilerOpen;
}
if (ImGui::Button("Kill Graphics")) {
killGraphics=true;
}
ImGui::TreePop();
}
if (ImGui::TreeNode("Performance")) {

View file

@ -3776,6 +3776,79 @@ bool FurnaceGUI::loop() {
});
}
// recover from dead graphics
if (rend->isDead() || killGraphics) {
killGraphics=false;
logW("graphics are dead! restarting...");
if (sampleTex!=NULL) {
rend->destroyTexture(sampleTex);
sampleTex=NULL;
}
if (chanOscGradTex!=NULL) {
rend->destroyTexture(chanOscGradTex);
chanOscGradTex=NULL;
}
for (auto& i: images) {
if (i.second->tex!=NULL) {
rend->destroyTexture(i.second->tex);
i.second->tex=NULL;
}
}
commitState();
rend->quitGUI();
rend->quit();
ImGui_ImplSDL2_Shutdown();
int initAttempts=0;
SDL_Delay(500);
logD("starting render backend...");
while (++initAttempts<=5) {
if (rend->init(sdlWin)) {
break;
}
SDL_Delay(1000);
logV("trying again...");
}
if (initAttempts>5) {
reportError("can't keep going without graphics! Furnace will quit now.");
quit=true;
break;
}
rend->clear(ImVec4(0.0,0.0,0.0,1.0));
rend->present();
logD("preparing user interface...");
rend->initGUI(sdlWin);
logD("building font...");
if (!ImGui::GetIO().Fonts->Build()) {
logE("error while building font atlas!");
showError("error while loading fonts! please check your settings.");
ImGui::GetIO().Fonts->Clear();
mainFont=ImGui::GetIO().Fonts->AddFontDefault();
patFont=mainFont;
if (rend) rend->destroyFontsTexture();
if (!ImGui::GetIO().Fonts->Build()) {
logE("error again while building font atlas!");
}
}
firstFrame=true;
mustClear=2;
initialScreenWipe=1.0f;
continue;
}
bool fontsFailed=false;
layoutTimeBegin=SDL_GetPerformanceCounter();
@ -6319,6 +6392,8 @@ bool FurnaceGUI::init() {
rend->present();
logD("preparing user interface...");
IMGUI_CHECKVERSION();
ImGui::CreateContext();
rend->initGUI(sdlWin);
applyUISettings();
@ -6556,8 +6631,8 @@ bool FurnaceGUI::finish() {
commitState();
rend->quitGUI();
ImGui_ImplSDL2_Shutdown();
ImGui::DestroyContext();
quitRender();
ImGui::DestroyContext();
SDL_DestroyWindow(sdlWin);
if (vibrator) {
@ -6617,6 +6692,7 @@ FurnaceGUI::FurnaceGUI():
modTableHex(false),
displayEditString(false),
mobileEdit(false),
killGraphics(false),
vgmExportVersion(0x171),
vgmExportTrailingTicks(-1),
drawHalt(10),

View file

@ -1210,9 +1210,12 @@ struct FurnaceGUIQueryResult {
}
};
class FurnaceGUITexture {
};
struct FurnaceGUIImage {
unsigned char* data;
void* tex;
FurnaceGUITexture* tex;
int width, height, ch;
FurnaceGUIImage():
@ -1243,13 +1246,13 @@ enum FurnaceGUIBlendMode {
class FurnaceGUIRender {
public:
virtual ImTextureID getTextureID(void* which);
virtual bool lockTexture(void* which, void** data, int* pitch);
virtual bool unlockTexture(void* which);
virtual bool updateTexture(void* which, void* data, int pitch);
virtual void* createTexture(bool dynamic, int width, int height);
virtual bool destroyTexture(void* which);
virtual void setTextureBlendMode(void* which, FurnaceGUIBlendMode mode);
virtual ImTextureID getTextureID(FurnaceGUITexture* which);
virtual bool lockTexture(FurnaceGUITexture* which, void** data, int* pitch);
virtual bool unlockTexture(FurnaceGUITexture* which);
virtual bool updateTexture(FurnaceGUITexture* which, void* data, int pitch);
virtual FurnaceGUITexture* createTexture(bool dynamic, int width, int height);
virtual bool destroyTexture(FurnaceGUITexture* which);
virtual void setTextureBlendMode(FurnaceGUITexture* which, FurnaceGUIBlendMode mode);
virtual void setBlendMode(FurnaceGUIBlendMode mode);
virtual void resized(const SDL_Event& ev);
virtual void clear(ImVec4 color);
@ -1280,7 +1283,7 @@ class FurnaceGUI {
SDL_Haptic* vibrator;
bool vibratorAvailable;
void* sampleTex;
FurnaceGUITexture* sampleTex;
int sampleTexW, sampleTexH;
bool updateSampleTex;
@ -1305,6 +1308,7 @@ class FurnaceGUI {
bool displayNew, fullScreen, preserveChanPos, wantScrollList, noteInputPoly;
bool displayPendingIns, pendingInsSingle, displayPendingRawSample, snesFilterHex, modTableHex, displayEditString;
bool mobileEdit;
bool killGraphics;
bool willExport[DIV_MAX_CHIPS];
int vgmExportVersion;
int vgmExportTrailingTicks;
@ -1960,7 +1964,7 @@ class FurnaceGUI {
String chanOscTextFormat;
ImVec4 chanOscColor, chanOscTextColor;
Gradient2D chanOscGrad;
void* chanOscGradTex;
FurnaceGUITexture* chanOscGradTex;
float chanOscLP0[DIV_MAX_CHANS];
float chanOscLP1[DIV_MAX_CHANS];
float chanOscVol[DIV_MAX_CHANS];
@ -2121,7 +2125,7 @@ class FurnaceGUI {
void highlightWindow(const char* winName);
FurnaceGUIImage* getImage(FurnaceGUIImages image);
void* getTexture(FurnaceGUIImages image, FurnaceGUIBlendMode blendMode=GUI_BLEND_MODE_BLEND);
FurnaceGUITexture* getTexture(FurnaceGUIImages image, FurnaceGUIBlendMode blendMode=GUI_BLEND_MODE_BLEND);
void drawImage(ImDrawList* dl, FurnaceGUIImages image, const ImVec2& pos, const ImVec2& scale, double rotate, const ImVec2& uvMin, const ImVec2& uvMax, const ImVec4& imgColor);
void drawMobileControls();

View file

@ -46,7 +46,7 @@ const unsigned int imageLen[GUI_IMAGE_MAX]={
image_pat_size
};
void* FurnaceGUI::getTexture(FurnaceGUIImages image, FurnaceGUIBlendMode blendMode) {
FurnaceGUITexture* FurnaceGUI::getTexture(FurnaceGUIImages image, FurnaceGUIBlendMode blendMode) {
FurnaceGUIImage* img=getImage(image);
if (img==NULL) return NULL;

View file

@ -25,7 +25,7 @@
void FurnaceGUI::drawImage(ImDrawList* dl, FurnaceGUIImages image, const ImVec2& pos, const ImVec2& scale, double rotate, const ImVec2& uvMin, const ImVec2& uvMax, const ImVec4& imgColor) {
FurnaceGUIImage* imgI=getImage(image);
void* img=getTexture(image);
FurnaceGUITexture* img=getTexture(image);
float squareSize=MAX(introMax.x-introMin.x,introMax.y-introMin.y);
float uDiff=uvMax.x-uvMin.x;

View file

@ -19,31 +19,31 @@
#include "../gui.h"
ImTextureID FurnaceGUIRender::getTextureID(void* which) {
ImTextureID FurnaceGUIRender::getTextureID(FurnaceGUITexture* which) {
return NULL;
}
bool FurnaceGUIRender::lockTexture(void* which, void** data, int* pitch) {
bool FurnaceGUIRender::lockTexture(FurnaceGUITexture* which, void** data, int* pitch) {
return false;
}
bool FurnaceGUIRender::unlockTexture(void* which) {
bool FurnaceGUIRender::unlockTexture(FurnaceGUITexture* which) {
return false;
}
bool FurnaceGUIRender::updateTexture(void* which, void* data, int pitch) {
bool FurnaceGUIRender::updateTexture(FurnaceGUITexture* which, void* data, int pitch) {
return false;
}
void* FurnaceGUIRender::createTexture(bool dynamic, int width, int height) {
FurnaceGUITexture* FurnaceGUIRender::createTexture(bool dynamic, int width, int height) {
return NULL;
}
bool FurnaceGUIRender::destroyTexture(void* which) {
bool FurnaceGUIRender::destroyTexture(FurnaceGUITexture* which) {
return false;
}
void FurnaceGUIRender::setTextureBlendMode(void* which, FurnaceGUIBlendMode mode) {
void FurnaceGUIRender::setTextureBlendMode(FurnaceGUITexture* which, FurnaceGUIBlendMode mode) {
}
void FurnaceGUIRender::setBlendMode(FurnaceGUIBlendMode mode) {

View file

@ -69,7 +69,8 @@ const D3D_FEATURE_LEVEL possibleFeatureLevels[2]={
D3D_FEATURE_LEVEL_10_0
};
struct FurnaceDXTexture {
class FurnaceDXTexture: public FurnaceGUITexture {
public:
ID3D11Texture2D* tex;
ID3D11ShaderResourceView* view;
int width, height;
@ -244,7 +245,6 @@ void* FurnaceGUIRenderDX11::createTexture(bool dynamic, int width, int height) {
ret->tex=tex;
ret->view=view;
ret->dynamic=dynamic;
textures.push_back(ret);
return ret;
}
@ -253,13 +253,6 @@ bool FurnaceGUIRenderDX11::destroyTexture(void* which) {
t->view->Release();
t->tex->Release();
delete t;
for (size_t i=0; i<textures.size(); i++) {
if (textures[i]==t) {
textures.erase(textures.begin()+i);
break;
}
}
return true;
}
@ -274,9 +267,14 @@ void FurnaceGUIRenderDX11::resized(const SDL_Event& ev) {
logI("DX11: resizing buffers");
HRESULT result=swapchain->ResizeBuffers(0,(unsigned int)ev.window.data1,(unsigned int)ev.window.data2,DXGI_FORMAT_UNKNOWN,DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH);
if (result!=S_OK) {
if (result==DXGI_ERROR_DEVICE_REMOVED || result==DXGI_ERROR_DEVICE_RESET) {
dead=true;
}
logW("error while resizing swapchain buffers! %.8x",result);
}
createRenderTarget();
if (!dead) {
createRenderTarget();
}
}
void FurnaceGUIRenderDX11::clear(ImVec4 color) {
@ -348,7 +346,12 @@ void FurnaceGUIRenderDX11::wipe(float alpha) {
}
void FurnaceGUIRenderDX11::present() {
swapchain->Present(1,0);
HRESULT result=swapchain->Present(1,0);
if (result==DXGI_ERROR_DEVICE_REMOVED || result==DXGI_ERROR_DEVICE_RESET) {
dead=true;
} else {
logE("DX11: present failed! %.8x",result)
}
}
bool FurnaceGUIRenderDX11::getOutputSize(int& w, int& h) {
@ -534,9 +537,6 @@ bool FurnaceGUIRenderDX11::init(SDL_Window* win) {
}
void FurnaceGUIRenderDX11::initGUI(SDL_Window* win) {
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGui_ImplSDL2_InitForD3D(win);
ImGui_ImplDX11_Init(device,context);
}
@ -544,13 +544,6 @@ void FurnaceGUIRenderDX11::initGUI(SDL_Window* win) {
bool FurnaceGUIRenderDX11::quit() {
destroyRenderTarget();
for (FurnaceDXTexture* i: textures) {
i->view->Release();
i->tex->Release();
delete i;
}
textures.clear();
if (swapchain!=NULL) {
swapchain->Release();
swapchain=NULL;
@ -563,9 +556,15 @@ bool FurnaceGUIRenderDX11::quit() {
device->Release();
device=NULL;
}
dead=false;
return true;
}
void FurnaceGUIRenderDX11::quitGUI() {
ImGui_ImplDX11_Shutdown();
}
void FurnaceGUIRenderDX11::isDead() {
return dead;
}

View file

@ -32,8 +32,6 @@ typedef void ID3D11InputLayout;
typedef void IDXGISwapChain;
#endif
struct FurnaceDXTexture;
class FurnaceGUIRenderDX11: public FurnaceGUIRender {
ID3D11Device* device;
ID3D11DeviceContext* context;
@ -45,6 +43,8 @@ class FurnaceGUIRenderDX11: public FurnaceGUIRender {
ID3D11Buffer* quadVertex;
int outW, outH;
bool dead;
// SHADERS //
// -> wipe
ID3D11VertexShader* sh_wipe_vertex;
@ -59,16 +59,14 @@ class FurnaceGUIRenderDX11: public FurnaceGUIRender {
bool destroyRenderTarget();
bool createRenderTarget();
std::vector<FurnaceDXTexture*> textures;
public:
ImTextureID getTextureID(void* which);
bool lockTexture(void* which, void** data, int* pitch);
bool unlockTexture(void* which);
bool updateTexture(void* which, void* data, int pitch);
void* createTexture(bool dynamic, int width, int height);
bool destroyTexture(void* which);
void setTextureBlendMode(void* which, FurnaceGUIBlendMode mode);
ImTextureID getTextureID(FurnaceGUITexture* which);
bool lockTexture(FurnaceGUITexture* which, void** data, int* pitch);
bool unlockTexture(FurnaceGUITexture* which);
bool updateTexture(FurnaceGUITexture* which, void* data, int pitch);
FurnaceGUITexture* createTexture(bool dynamic, int width, int height);
bool destroyTexture(FurnaceGUITexture* which);
void setTextureBlendMode(FurnaceGUITexture* which, FurnaceGUIBlendMode mode);
void setBlendMode(FurnaceGUIBlendMode mode);
void resized(const SDL_Event& ev);
void clear(ImVec4 color);
@ -85,6 +83,7 @@ class FurnaceGUIRenderDX11: public FurnaceGUIRender {
void initGUI(SDL_Window* win);
void quitGUI();
bool quit();
bool isDead();
FurnaceGUIRenderDX11():
device(NULL),
context(NULL),
@ -95,6 +94,7 @@ class FurnaceGUIRenderDX11: public FurnaceGUIRender {
quadVertex(NULL),
outW(0),
outH(0),
dead(false),
sh_wipe_vertex(NULL),
sh_wipe_fragment(NULL),
sh_wipe_inputLayout(NULL),

View file

@ -51,7 +51,8 @@ PFNGLGETUNIFORMLOCATIONPROC furGetUniformLocation=NULL;
PFNGLUNIFORM1FPROC furUniform1f=NULL;
PFNGLGETSHADERINFOLOGPROC furGetShaderInfoLog=NULL;
struct FurnaceGLTexture {
class FurnaceGLTexture: public FurnaceGUITexture {
public:
GLuint id;
int width, height;
unsigned char* lockedData;
@ -138,12 +139,12 @@ bool FurnaceGUIRenderGL::createShader(const char* vertexS, const char* fragmentS
return true;
}
ImTextureID FurnaceGUIRenderGL::getTextureID(void* which) {
ImTextureID FurnaceGUIRenderGL::getTextureID(FurnaceGUITexture* which) {
intptr_t ret=((FurnaceGLTexture*)which)->id;
return (ImTextureID)ret;
}
bool FurnaceGUIRenderGL::lockTexture(void* which, void** data, int* pitch) {
bool FurnaceGUIRenderGL::lockTexture(FurnaceGUITexture* which, void** data, int* pitch) {
FurnaceGLTexture* t=(FurnaceGLTexture*)which;
if (t->lockedData!=NULL) return false;
t->lockedData=new unsigned char[t->width*t->height*4];
@ -153,7 +154,7 @@ bool FurnaceGUIRenderGL::lockTexture(void* which, void** data, int* pitch) {
return true;
}
bool FurnaceGUIRenderGL::unlockTexture(void* which) {
bool FurnaceGUIRenderGL::unlockTexture(FurnaceGUITexture* which) {
FurnaceGLTexture* t=(FurnaceGLTexture*)which;
if (t->lockedData==NULL) return false;
@ -167,7 +168,7 @@ bool FurnaceGUIRenderGL::unlockTexture(void* which) {
return true;
}
bool FurnaceGUIRenderGL::updateTexture(void* which, void* data, int pitch) {
bool FurnaceGUIRenderGL::updateTexture(FurnaceGUITexture* which, void* data, int pitch) {
FurnaceGLTexture* t=(FurnaceGLTexture*)which;
if (t->width*4!=pitch) return false;
@ -177,7 +178,7 @@ bool FurnaceGUIRenderGL::updateTexture(void* which, void* data, int pitch) {
return true;
}
void* FurnaceGUIRenderGL::createTexture(bool dynamic, int width, int height) {
FurnaceGUITexture* FurnaceGUIRenderGL::createTexture(bool dynamic, int width, int height) {
FurnaceGLTexture* t=new FurnaceGLTexture;
C(glGenTextures(1,&t->id));
C(glBindTexture(GL_TEXTURE_2D,t->id));
@ -190,14 +191,14 @@ void* FurnaceGUIRenderGL::createTexture(bool dynamic, int width, int height) {
return t;
}
bool FurnaceGUIRenderGL::destroyTexture(void* which) {
bool FurnaceGUIRenderGL::destroyTexture(FurnaceGUITexture* which) {
FurnaceGLTexture* t=(FurnaceGLTexture*)which;
C(glDeleteTextures(1,&t->id));
delete t;
return true;
}
void FurnaceGUIRenderGL::setTextureBlendMode(void* which, FurnaceGUIBlendMode mode) {
void FurnaceGUIRenderGL::setTextureBlendMode(FurnaceGUITexture* which, FurnaceGUIBlendMode mode) {
}
void FurnaceGUIRenderGL::setBlendMode(FurnaceGUIBlendMode mode) {
@ -368,9 +369,6 @@ bool FurnaceGUIRenderGL::init(SDL_Window* win) {
}
void FurnaceGUIRenderGL::initGUI(SDL_Window* win) {
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGui_ImplSDL2_InitForOpenGL(win,context);
ImGui_ImplOpenGL3_Init();
}

View file

@ -35,13 +35,13 @@ class FurnaceGUIRenderGL: public FurnaceGUIRender {
bool createShader(const char* vertexS, const char* fragmentS, int& vertex, int& fragment, int& program);
public:
ImTextureID getTextureID(void* which);
bool lockTexture(void* which, void** data, int* pitch);
bool unlockTexture(void* which);
bool updateTexture(void* which, void* data, int pitch);
void* createTexture(bool dynamic, int width, int height);
bool destroyTexture(void* which);
void setTextureBlendMode(void* which, FurnaceGUIBlendMode mode);
ImTextureID getTextureID(FurnaceGUITexture* which);
bool lockTexture(FurnaceGUITexture* which, void** data, int* pitch);
bool unlockTexture(FurnaceGUITexture* which);
bool updateTexture(FurnaceGUITexture* which, void* data, int pitch);
FurnaceGUITexture* createTexture(bool dynamic, int width, int height);
bool destroyTexture(FurnaceGUITexture* which);
void setTextureBlendMode(FurnaceGUITexture* which, FurnaceGUIBlendMode mode);
void setBlendMode(FurnaceGUIBlendMode mode);
void clear(ImVec4 color);
bool newFrame();

View file

@ -20,45 +20,65 @@
#include "renderSDL.h"
#include "backends/imgui_impl_sdlrenderer2.h"
ImTextureID FurnaceGUIRenderSDL::getTextureID(void* which) {
return which;
class FurnaceSDLTexture: public FurnaceGUITexture {
public:
SDL_Texture* tex;
FurnaceSDLTexture():
tex(NULL) {}
};
ImTextureID FurnaceGUIRenderSDL::getTextureID(FurnaceGUITexture* which) {
FurnaceSDLTexture* t=(FurnaceSDLTexture*)which;
return t->tex;
}
bool FurnaceGUIRenderSDL::lockTexture(void* which, void** data, int* pitch) {
return SDL_LockTexture((SDL_Texture*)which,NULL,data,pitch)==0;
bool FurnaceGUIRenderSDL::lockTexture(FurnaceGUITexture* which, void** data, int* pitch) {
FurnaceSDLTexture* t=(FurnaceSDLTexture*)which;
return SDL_LockTexture(t->tex,NULL,data,pitch)==0;
}
bool FurnaceGUIRenderSDL::unlockTexture(void* which) {
SDL_UnlockTexture((SDL_Texture*)which);
bool FurnaceGUIRenderSDL::unlockTexture(FurnaceGUITexture* which) {
FurnaceSDLTexture* t=(FurnaceSDLTexture*)which;
SDL_UnlockTexture(t->tex);
return true;
}
bool FurnaceGUIRenderSDL::updateTexture(void* which, void* data, int pitch) {
return SDL_UpdateTexture((SDL_Texture*)which,NULL,data,pitch)==0;
bool FurnaceGUIRenderSDL::updateTexture(FurnaceGUITexture* which, void* data, int pitch) {
FurnaceSDLTexture* t=(FurnaceSDLTexture*)which;
return SDL_UpdateTexture(t->tex,NULL,data,pitch)==0;
}
void* FurnaceGUIRenderSDL::createTexture(bool dynamic, int width, int height) {
return SDL_CreateTexture(sdlRend,SDL_PIXELFORMAT_ABGR8888,dynamic?SDL_TEXTUREACCESS_STREAMING:SDL_TEXTUREACCESS_STATIC,width,height);
FurnaceGUITexture* FurnaceGUIRenderSDL::createTexture(bool dynamic, int width, int height) {
SDL_Texture* t=SDL_CreateTexture(sdlRend,SDL_PIXELFORMAT_ABGR8888,dynamic?SDL_TEXTUREACCESS_STREAMING:SDL_TEXTUREACCESS_STATIC,width,height);
if (t==NULL) return NULL;
FurnaceSDLTexture* ret=new FurnaceSDLTexture;
ret->tex=t;
return ret;
}
bool FurnaceGUIRenderSDL::destroyTexture(void* which) {
SDL_DestroyTexture((SDL_Texture*)which);
bool FurnaceGUIRenderSDL::destroyTexture(FurnaceGUITexture* which) {
FurnaceSDLTexture* t=(FurnaceSDLTexture*)which;
SDL_DestroyTexture(t->tex);
delete t;
return true;
}
void FurnaceGUIRenderSDL::setTextureBlendMode(void* which, FurnaceGUIBlendMode mode) {
void FurnaceGUIRenderSDL::setTextureBlendMode(FurnaceGUITexture* which, FurnaceGUIBlendMode mode) {
FurnaceSDLTexture* t=(FurnaceSDLTexture*)which;
switch (mode) {
case GUI_BLEND_MODE_NONE:
SDL_SetTextureBlendMode((SDL_Texture*)which,SDL_BLENDMODE_NONE);
SDL_SetTextureBlendMode(t->tex,SDL_BLENDMODE_NONE);
break;
case GUI_BLEND_MODE_BLEND:
SDL_SetTextureBlendMode((SDL_Texture*)which,SDL_BLENDMODE_BLEND);
SDL_SetTextureBlendMode(t->tex,SDL_BLENDMODE_BLEND);
break;
case GUI_BLEND_MODE_ADD:
SDL_SetTextureBlendMode((SDL_Texture*)which,SDL_BLENDMODE_ADD);
SDL_SetTextureBlendMode(t->tex,SDL_BLENDMODE_ADD);
break;
case GUI_BLEND_MODE_MULTIPLY:
SDL_SetTextureBlendMode((SDL_Texture*)which,SDL_BLENDMODE_MOD);
SDL_SetTextureBlendMode(t->tex,SDL_BLENDMODE_MOD);
break;
}
}
@ -128,9 +148,6 @@ bool FurnaceGUIRenderSDL::init(SDL_Window* win) {
}
void FurnaceGUIRenderSDL::initGUI(SDL_Window* win) {
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGui_ImplSDL2_InitForSDLRenderer(win,sdlRend);
ImGui_ImplSDLRenderer2_Init(sdlRend);
}

View file

@ -22,13 +22,13 @@
class FurnaceGUIRenderSDL: public FurnaceGUIRender {
SDL_Renderer* sdlRend;
public:
ImTextureID getTextureID(void* which);
bool lockTexture(void* which, void** data, int* pitch);
bool unlockTexture(void* which);
bool updateTexture(void* which, void* data, int pitch);
void* createTexture(bool dynamic, int width, int height);
bool destroyTexture(void* which);
void setTextureBlendMode(void* which, FurnaceGUIBlendMode mode);
ImTextureID getTextureID(FurnaceGUITexture* which);
bool lockTexture(FurnaceGUITexture* which, void** data, int* pitch);
bool unlockTexture(FurnaceGUITexture* which);
bool updateTexture(FurnaceGUITexture* which, void* data, int pitch);
FurnaceGUITexture* createTexture(bool dynamic, int width, int height);
bool destroyTexture(FurnaceGUITexture* which);
void setTextureBlendMode(FurnaceGUITexture* which, FurnaceGUIBlendMode mode);
void setBlendMode(FurnaceGUIBlendMode mode);
void clear(ImVec4 color);
bool newFrame();