# HG changeset patch # User František Kučera # Date 1701528231 -3600 # Node ID 80ad08521091a49606f01c736c4843be8de76797 # Parent e6065118326fdcc251bc3645683dc0f2ff78a8c0 monitor texture and shader file writes using inotify: reload shaders diff -r e6065118326f -r 80ad08521091 Shark.cpp --- a/Shark.cpp Sat Dec 02 15:02:56 2023 +0100 +++ b/Shark.cpp Sat Dec 02 15:43:51 2023 +0100 @@ -36,6 +36,14 @@ // std::cerr << "~Shark()" << std::endl; } +void Shark::setTitle(const std::string& suffix) { + std::stringstream title; + title << "ShaderShark"; + if (suffix.size()) title << ": " << suffix.c_str(); + XStoreName(dpy, win, title.str().c_str()); + XFlush(dpy); +} + void Shark::run() { dpy = XOpenDisplay(NULL); @@ -68,7 +76,7 @@ CWColormap | CWEventMask, &swa); XMapWindow(dpy, win); - XStoreName(dpy, win, "ShaderShark"); + setTitle(); setX11PID(dpy, win); // XSetWindowBackground(dpy, win, 0) vs. glClearColor() @@ -117,6 +125,7 @@ int epollEventCount = epoll.wait(); //std::cout << "trace: epoll.wait() = " << epollEventCount << std::endl; for (int epollEvent = 0; epollEvent < epollEventCount; epollEvent++) { + bool redraw = false; if (epoll[epollEvent].data.fd == x11fd) { if (!XPending(dpy)) { // otherwise STDIN events are held until the first X11 event @@ -125,7 +134,6 @@ } XWindowAttributes gwa; XNextEvent(dpy, &xev); - bool redraw = false; if (xev.type == Expose) { std::cout << "XEvent: Expose" << std::endl; @@ -193,11 +201,6 @@ } else { std::cout << "XEvent: type=" << xev.type << std::endl; } - - if (redraw) { - runShaders(); - glXSwapBuffers(dpy, win); - } } else if (epoll[epollEvent].data.fd == STDIN_FILENO) { int epollFD = epoll[epollEvent].data.fd; logOutput << "other event: fd=" << epollFD << " data="; @@ -215,10 +218,20 @@ } else if (epoll[epollEvent].data.fd == fileMonitor.getFD()) { std::cout << "FileMonitor event:" << std::endl; for (FileEvent fe; fileMonitor.readEvent(fe);) { - std::cout << " " - << " file: " << fe.fileName - << " mask: " << fe.mask + logOutput << " " + << " file=" << fe.fileName + << " mask=" << fe.mask << std::endl; + try { + redraw |= reloadTexture(fe.fileName); + redraw |= reloadShader(fe.fileName); + setTitle(); + } catch (const std::exception& e) { + setTitle("[ERROR]"); + logOutput << "error while reloading '" + << fe.fileName.c_str() + << "': " << e.what() << std::endl; + } } } else { logOutput @@ -226,6 +239,11 @@ << epoll[epollEvent].data.fd << std::endl; } + + if (redraw) { + runShaders(); + glXSwapBuffers(dpy, win); + } } } } @@ -370,6 +388,16 @@ } } +bool Shark::reloadTexture(const std::string& fileName) { + for (const Configuration::Texture& tex : cfg.textures) { + if (tex.fileName == fileName) { + logOutput << "TODO: reload texture: " << fileName.c_str() << "\n"; + return true; + } + } + return false; +} + std::shared_ptr Shark::loadShaders() { try { // Vertex Array Object (VAO) @@ -412,6 +440,8 @@ shaders.push_back(shader); watchedFiles.push_back(fileMonitor.watch(fileName)); std::cerr << "GLSL loaded: " << fileName.c_str() << std::endl; + // We may detach and delete shaders, + // but our shaders are small, so we keep them for later reloading. } // GLSL compiler does very efficient / aggressive optimization. @@ -441,3 +471,13 @@ throw std::logic_error("GLSL: loadShaders() failed"); } +bool Shark::reloadShader(const std::string& fileName) { + for (auto shader : shaders) { + if (shader->getFileName() == fileName) { + shader->update(MappedFile(fileName)); + shaderProgram->link(); + return true; + } + } + return false; +} diff -r e6065118326f -r 80ad08521091 Shark.h --- a/Shark.h Sat Dec 02 15:02:56 2023 +0100 +++ b/Shark.h Sat Dec 02 15:43:51 2023 +0100 @@ -193,8 +193,11 @@ int setNonBlocking(int fd); void loadVertices(); Texture loadTexture(const std::string& fileName); + bool reloadTexture(const std::string& fileName); void loadTextures(); std::shared_ptr loadShaders(); + bool reloadShader(const std::string& fileName); + void setTitle(const std::string& suffix = ""); public: Shark(const Configuration& cfg); virtual ~Shark();