shader optimization; won some frames on big lights
[dd2d.git] / xmain_d2d.d
blob2bcb0152e10ce829f8e85da014c83cb59d3ec335
1 module xmain_d2d is aliced;
3 private:
4 import core.atomic;
5 import core.thread;
6 import core.time;
8 import glbinds;
9 import glutils;
10 import wadarc;
12 import iv.stream;
14 import d2dmap;
17 // ////////////////////////////////////////////////////////////////////////// //
18 import arsd.color;
19 import arsd.png;
22 // ////////////////////////////////////////////////////////////////////////// //
23 __gshared LevelMap map;
26 // ////////////////////////////////////////////////////////////////////////// //
27 __gshared SimpleWindow sdwindow;
30 public enum vlWidth = 800;
31 public enum vlHeight = 800;
32 __gshared int scale = 1;
33 __gshared bool scanlines = false;
36 // ////////////////////////////////////////////////////////////////////////// //
37 enum MaxLightSize = 512;
40 // ////////////////////////////////////////////////////////////////////////// //
41 // for light
42 __gshared FBO[MaxLightSize+1] fboOccluders, fboShadowMap;
43 __gshared Shader shadToPolar, shadBlur;
45 __gshared FBO fboLevel;
46 __gshared Shader shadScanlines, shadLiquid;
49 // ////////////////////////////////////////////////////////////////////////// //
50 void initOpenGL () {
51 glEnable(GL_TEXTURE_2D);
52 glDisable(GL_LIGHTING);
53 glDisable(GL_DITHER);
54 glDisable(GL_BLEND);
55 glDisable(GL_DEPTH_TEST);
57 // create shaders
58 shadScanlines = new Shader("scanlines", import("scanlines.frag"));
59 shadLiquid = new Shader("liquid", import("srliquid.frag"));
61 // lights
62 shadToPolar = new Shader("topolar", import("srlight_topolar.frag"));
63 shadBlur = new Shader("blur", import("srlight_blur.frag"));
64 shadBlur.exec({
65 shadBlur["tex0"] = 0;
66 shadBlur["tex1"] = 1;
67 });
68 foreach (int sz; 1..MaxLightSize+1) {
69 // create occluders FBO
70 fboOccluders[sz] = new FBO(sz, sz);
71 // create 1d shadowmap FBO
72 fboShadowMap[sz] = new FBO(sz, 1);
75 // setup matrices
76 glMatrixMode(GL_MODELVIEW);
77 glLoadIdentity();
79 map.oglBuildMega();
80 fboLevel = new FBO(map.width*8, map.height*8, Texture.Option.Nearest);
82 // build noise texture for liquid shaders
83 glActiveTexture(GL_TEXTURE0+1);
85 import std.random : uniform;
86 auto img = new TrueColorImage(64, 64);
87 foreach (int y; 0..img.width) {
88 foreach (int x; 0..img.height) {
89 ubyte b = cast(ubyte)uniform(0, 256);
90 img.imageData.colors[y*img.width+x] = Color(b, b, b, 255);
93 auto tex = new Texture(img);
94 tex.activate;
97 glActiveTexture(GL_TEXTURE0+0);
98 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
99 orthoCamera(vlWidth, vlHeight);
103 // ////////////////////////////////////////////////////////////////////////// //
104 void renderLight() (int lightX, int lightY, in auto ref SVec4F lcol, int lightSize) {
105 if (lightSize < 2) return;
106 lightSize *= 2;
107 if (lightSize > MaxLightSize) lightSize = MaxLightSize;
108 // is this light visible?
109 if (lightX <= -lightSize/2 || lightY <= -lightSize/2 || lightX-lightSize/2 >= map.width*8 || lightY-lightSize/2 >= map.height*8) return;
111 // draw shadow casters to fboOccludersId, light should be in the center
112 glUseProgram(0);
113 fboOccluders[lightSize].exec({
114 glColor3f(0.0f, 0.0f, 0.0f);
115 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
116 glClear(GL_COLOR_BUFFER_BIT);
117 orthoCamera(lightSize, lightSize);
118 drawAtXY(map.texgl.ptr[map.LightMask], lightSize/2-lightX, lightSize/2-lightY);
121 // build 1d shadow map to fboShadowMapId
122 fboShadowMap[lightSize].exec({
123 shadToPolar.exec({
124 glColor3f(0.0f, 0.0f, 0.0f);
125 shadToPolar["lightTexSize"] = SVec2F(lightSize, lightSize);
126 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
127 glClear(GL_COLOR_BUFFER_BIT);
128 orthoCamera(lightSize, 1);
129 drawAtXY(fboOccluders[lightSize].tex.tid, 0, 0, lightSize, 1);
133 // blend light
134 fboLevel.exec({
135 shadBlur.exec({
136 shadBlur["lightTexSize"] = SVec2F(lightSize, lightSize);
137 glEnable(GL_BLEND);
138 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
139 // set light color
140 //glColor3f(1.0f, 1.0f, 0.0f);
141 glColor4f(lcol.x, lcol.y, lcol.z, lcol.w);
142 orthoCamera(map.width*8, map.height*8);
143 drawAtXY(fboShadowMap[lightSize].tex.tid, lightX-lightSize/2, lightY-lightSize/2, lightSize, lightSize, mirrorY:true);
149 // ////////////////////////////////////////////////////////////////////////// //
150 __gshared int lightX = vlWidth/2, lightY = vlHeight/2;
151 __gshared int mapOfsX, mapOfsY;
152 __gshared bool movement = false;
155 void renderScene () {
156 enum BackIntens = 0.05f;
158 fboLevel.exec({
159 glDisable(GL_BLEND);
160 glClearColor(BackIntens, BackIntens, BackIntens, 1.0f);
161 glClear(GL_COLOR_BUFFER_BIT);
162 //glColor3f(0.0f, 0.0f, 0.0f);
163 glEnable(GL_BLEND);
164 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
165 orthoCamera(map.width*8, map.height*8);
167 // draw background
168 glColor4f(1.0f, 1.0f, 1.0f, 0.0f);
169 //drawAtXY(map.texgl.ptr[map.Back], 0, 0);
170 // water, acid, lava
171 shadLiquid.exec({
172 __gshared float iGlobalTime = 0.0;
173 iGlobalTime += 0.04;
174 //shadLiquid["iResolution"] = SVec2F(vlWidth, vlHeight);
175 shadLiquid["iResolution"] = SVec2F(map.texgl.ptr[map.Water].width, map.texgl.ptr[map.Water].height);
176 shadLiquid["iGlobalTime"] = iGlobalTime;
177 shadLiquid["iChannel0"] = 0;
178 shadLiquid["iChannel1"] = 1;
179 shadLiquid["liquidColorMul"] = SVec4F(1.0f, 1.0f, 1.0f, 1.0f);
180 shadLiquid["liquidColor"] = SVec4F(0.0f, 0.0f, 0.4f, 1.0f);
181 drawAtXY(map.texgl.ptr[map.Water], 0, 0);
182 shadLiquid["liquidColorMul"] = SVec4F(0.6f, 0.6f, 0.6f, 1.0f);
183 shadLiquid["liquidColor"] = SVec4F(0.6f, 0.1f, 0.0f, 1.0f);
184 drawAtXY(map.texgl.ptr[map.Lava], 0, 0);
185 shadLiquid["liquidColorMul"] = SVec4F(1.0f, 1.0f, 1.0f, 1.0f);
186 shadLiquid["liquidColor"] = SVec4F(0.1f, 0.4f, 0.0f, 1.0f);
187 drawAtXY(map.texgl.ptr[map.Acid], 0, 0);
189 //drawAtXY(map.texgl.ptr[map.LightMask], 0, 0);
192 // additive lights
193 glEnable(GL_BLEND);
194 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
196 glActiveTexture(GL_TEXTURE0+1);
197 glBindTexture(GL_TEXTURE_2D, map.texgl.ptr[map.Back].tid);
198 glActiveTexture(GL_TEXTURE0+0);
200 renderLight( 27, map.height*8-1-391-0, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 100);
201 renderLight(542, map.height*8-1-424-0, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 100);
202 renderLight(377, map.height*8-1-368-0, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 32);
203 renderLight(147, map.height*8-1-288-0, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 64);
204 renderLight( 71, map.height*8-1-200-0, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 128);
205 renderLight(249, map.height*8-1-200-0, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 128);
206 renderLight(426, map.height*8-1-200-0, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 128);
207 renderLight(624, map.height*8-1-200-0, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 128);
208 renderLight(549, map.height*8-1-298-0, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 64);
209 renderLight( 74, map.height*8-1-304-0, SVec4F(0.0f, 0.0f, 0.0f, 0.0f), 32);
211 renderLight(24*8+4, map.height*8-1-(24+18)*8-2, SVec4F(0.6f, 0.0f, 0.0f, 1.0f), 128);
212 foreach (immutable _; 0..20) {
213 renderLight(lightX, lightY, SVec4F(0.3f, 0.3f, 0.0f, 1.0f), 256);
216 // restore blending
217 fboLevel.exec({
218 glEnable(GL_BLEND);
219 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
220 glColor3f(1.0f, 1.0f, 1.0f);
221 orthoCamera(map.width*8, map.height*8);
222 drawAtXY(map.texgl.ptr[map.LightMask], 0, 0);
223 // foreground
224 drawAtXY(map.texgl.ptr[map.Front], 0, 0);
227 // scanlines shader
228 glClearColor(BackIntens, BackIntens, BackIntens, 1.0f);
229 glClear(GL_COLOR_BUFFER_BIT);
230 glUseProgram(shadScanlines.prg);
231 shadScanlines["scanlines"] = scanlines;
232 orthoCamera(vlWidth, vlHeight);
233 drawAtXY(fboLevel.tex.tid, -mapOfsX, -mapOfsY, map.width*8*scale, map.height*8*scale);
235 glUseProgram(0);
239 // ////////////////////////////////////////////////////////////////////////// //
240 // rendering thread
241 shared int diedie = 0;
244 void renderThread () {
245 MonoTime prevFrameStartTime = MonoTime.currTime;
246 version(use_vsync) {} else MonoTime ltt = MonoTime.currTime;
247 MonoTime lasttime = MonoTime.currTime;
248 MonoTime time;
249 enum MaxFPSFrames = 16;
250 float frtimes = 0.0f;
251 int framenum = 0;
252 int prevFPS = -1;
253 bool first = true;
254 for (;;) {
255 if (sdwindow.closed) break;
256 if (atomicLoad(diedie) > 0) break;
258 time = MonoTime.currTime;
259 version(use_vsync) {
260 } else {
261 // max 60 FPS; capped by vsync
262 //{ import core.stdc.stdio; printf(" spent only %d msecs\n", cast(int)((time-ltt).total!"msecs")); }
263 if (!first && (time-ltt).total!"msecs" < 16) {
264 //{ import core.stdc.stdio; printf(" spent only %d msecs\n", cast(int)((time-ltt).total!"msecs")); }
265 import core.sys.posix.signal : timespec;
266 import core.sys.posix.time : nanosleep;
267 timespec ts = void;
268 ts.tv_sec = 0;
269 ts.tv_nsec = (16-cast(int)((time-ltt).total!"msecs"))*1000*1000; // milli to nano
270 nanosleep(&ts, null); // idc how much time was passed
271 time = MonoTime.currTime;
273 ltt = time;
274 first = false;
278 auto frameTime = cast(float)(time-prevFrameStartTime).total!"msecs"/1000.0f;
279 prevFrameStartTime = time;
281 //{ import core.stdc.stdio; printf("frametime: %f\n", frameTime*1000.0f); }
282 //{ import core.stdc.stdio; printf("FPS: %d\n", cast(int)(1.0f/frameTime)); }
283 frtimes += frameTime;
284 if (++framenum >= MaxFPSFrames || frtimes >= 3.0f) {
285 import std.string : format;
286 int newFPS = cast(int)(cast(float)MaxFPSFrames/frtimes+0.5);
287 if (newFPS != prevFPS) {
288 sdwindow.title = "%s / FPS:%s".format("D2D", newFPS);
289 prevFPS = newFPS;
291 framenum = 0;
292 frtimes = 0.0f;
295 XLockDisplay(sdwindow.display);
296 if (glXMakeCurrent(sdwindow.display, sdwindow.window, sdwindow.glc) == 0) {
297 XUnlockDisplay(sdwindow.display);
298 { import core.stdc.stdio; printf(" FUUUU\n"); }
299 //glXMakeCurrent(sdwindow.display, 0, null);
300 import core.sys.posix.signal : timespec;
301 import core.sys.posix.time : nanosleep;
302 timespec ts = void;
303 ts.tv_sec = 0;
304 ts.tv_nsec = 16*1000*1000; // milli to nano
305 nanosleep(&ts, null); // idc how much time was passed
306 time = MonoTime.currTime;
307 continue;
309 XUnlockDisplay(sdwindow.display);
310 renderScene();
311 XLockDisplay(sdwindow.display);
312 glXSwapBuffers(sdwindow.display, sdwindow.window);
313 glFinish();
314 glXMakeCurrent(sdwindow.display, 0, null);
315 XUnlockDisplay(sdwindow.display);
318 atomicStore(diedie, 2);
322 // ////////////////////////////////////////////////////////////////////////// //
323 void closeWindow () {
324 if (atomicLoad(diedie) != 2) {
325 atomicStore(diedie, 1);
326 while (atomicLoad(diedie) != 2) {}
328 if (!sdwindow.closed) {
329 flushGui();
330 sdwindow.close();
335 // ////////////////////////////////////////////////////////////////////////// //
336 __gshared Thread renderTid;
339 void main () {
340 static void setDP () {
341 version(rdmd) {
342 setDataPath("data");
343 } else {
344 import std.file : thisExePath;
345 import std.path : dirName;
346 setDataPath(thisExePath.dirName);
348 addWad("/home/ketmar/k8prj/doom2d-tl/data/doom2d.wad");
349 //addWad("/home/ketmar/k8prj/doom2d-tl/data/meat.wad");
350 //addWad("/home/ketmar/k8prj/doom2d-tl/data/megadm.wad");
351 //addWad("/home/ketmar/k8prj/doom2d-tl/data/megadm1.wad");
352 //addWad("/home/ketmar/k8prj/doom2d-tl/data/superdm.wad");
353 //addWad("/home/ketmar/k8prj/doom2d-tl/data/zadoomka.wad");
356 setDP();
357 loadPalette();
359 map = new LevelMap("maps/map01.d2m");
360 mapOfsX = 8*36;
361 mapOfsY = 8*56;
363 sdwindow = new SimpleWindow(vlWidth, vlHeight, "D2D", OpenGlOptions.yes, Resizablity.fixedSize);
365 sdwindow.visibleForTheFirstTime = delegate () {
366 sdwindow.setAsCurrentOpenGlContext(); // make this window active
367 sdwindow.vsync = false;
368 //sdwindow.useGLFinish = false;
369 initOpenGL();
370 //sdwindow.redrawOpenGlScene();
371 if (!renderTid) {
372 if (glXMakeCurrent(sdwindow.display, 0, null) == 0) { import core.stdc.stdio; printf("can't release OpenGL context(1)\n"); }
373 renderTid = new Thread(&renderThread);
374 renderTid.start();
378 //sdwindow.redrawOpenGlScene = delegate () { renderScene(); };
380 enum MSecsPerFrame = 1000/30; /* 30 is FPS */
382 uint[8] frameTimes = 1000;
383 enum { Left, Right, Up, Down }
384 bool[4] pressed = false;
386 sdwindow.eventLoop(MSecsPerFrame,
387 delegate () {
388 if (sdwindow.closed) return;
389 if (pressed[Left]) mapOfsX -= 8;
390 if (pressed[Right]) mapOfsX += 8;
391 if (pressed[Up]) mapOfsY += 8;
392 if (pressed[Down]) mapOfsY -= 8;
393 import std.math : cos, sin;
394 __gshared float itime = 0.0;
395 itime += 0.02;
396 if (movement) {
397 mapOfsX = cast(int)(800.0/2.0+cos(itime)*220.0);
398 mapOfsY = cast(int)(800.0/2.0+120.0+sin(itime)*160.0);
400 if (scale == 1) mapOfsX = mapOfsY = 0;
401 //sdwindow.redrawOpenGlSceneNow();
403 delegate (KeyEvent event) {
404 if (sdwindow.closed) return;
405 if (event.pressed && event.key == Key.Escape) { closeWindow(); return; }
406 switch (event.key) {
407 case Key.Left: pressed[Left] = event.pressed; break;
408 case Key.Right: pressed[Right] = event.pressed; break;
409 case Key.Up: pressed[Up] = event.pressed; break;
410 case Key.Down: pressed[Down] = event.pressed; break;
411 default:
414 delegate (MouseEvent event) {
415 lightX = event.x/scale;
416 lightY = vlHeight/scale-event.y/scale;
417 lightX += mapOfsX/scale;
418 lightY += mapOfsY/scale;
420 delegate (dchar ch) {
421 if (ch == 'q') { closeWindow(); return; }
422 if (ch == 's') scanlines = !scanlines;
423 if (ch == '1') scale = 1;
424 if (ch == '2') scale = 2;
425 if (ch == ' ') movement = !movement;
428 closeWindow();
429 flushGui();