monitor texture and shader file writes using inotify: reload shaders v_0
authorFrantišek Kučera <franta-hg@frantovo.cz>
Sat, 02 Dec 2023 15:43:51 +0100
branchv_0
changeset 8 80ad08521091
parent 7 e6065118326f
child 9 53ba7e52c67c
monitor texture and shader file writes using inotify: reload shaders
Shark.cpp
Shark.h
--- 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<Program> 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;
+}
--- 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<Program> loadShaders();
+	bool reloadShader(const std::string& fileName);
+	void setTitle(const std::string& suffix = "");
 public:
 	Shark(const Configuration& cfg);
 	virtual ~Shark();