From 2cbf8041bc26ca1e846772239015af4bea104754 Mon Sep 17 00:00:00 2001
From: Graham Northup <grissess@nexusg.org>
Date: Wed, 18 Dec 2019 13:05:13 -0500
Subject: Adding OpenGL render support (major speedup)

---
 render.py | 555 +++++++++++++++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 461 insertions(+), 94 deletions(-)

(limited to 'render.py')

diff --git a/render.py b/render.py
index 914af4b..1bbae53 100644
--- a/render.py
+++ b/render.py
@@ -8,10 +8,8 @@ import struct
 import colorsys
 import math
 
-import pygame
-import pygame.gfxdraw
-
 parser = optparse.OptionParser()
+parser.add_option('-E', '--engine', dest='engine', default='pygame', help='Rendering engine to use')
 parser.add_option('--map-file', dest='map_file', default='client_map', help='File mapped by -G mapped')
 parser.add_option('--map-samples', dest='map_samples', type='int', default=4096, help='Number of samples in the map file (MUST agree with client)')
 parser.add_option('--pg-samp-width', dest='samp_width', type='int', help='Set the width of the sample pane (by default display width / 2)')
@@ -42,107 +40,476 @@ STREAMS = strfmtsz / struct.calcsize('>Lf')
 strfmt = '>' + 'Lf' * STREAMS
 print 'Detected', STREAMS, 'streams'
 
-pygame.init()
-
-WIDTH, HEIGHT = 640, 480
-dispinfo = pygame.display.Info()
-if dispinfo.current_h > 0 and dispinfo.current_w > 0:
-    WIDTH, HEIGHT = dispinfo.current_w, dispinfo.current_h
-
-flags = 0
-if options.fullscreen:
-    flags |= pygame.FULLSCREEN
-
-disp = pygame.display.set_mode((WIDTH, HEIGHT), flags)
-WIDTH, HEIGHT = disp.get_size()
-SAMP_WIDTH = WIDTH / 2
-if options.samp_width:
-    SAMP_WIDTH = options.samp_width
-BGR_WIDTH = WIDTH - SAMP_WIDTH
-HALFH = HEIGHT / 2
-PFAC = HEIGHT / 128.0
-sampwin = pygame.Surface((SAMP_WIDTH, HEIGHT))
-sampwin.set_colorkey((0, 0, 0))
-lastsy = HALFH
-bgrwin = pygame.Surface((BGR_WIDTH, HEIGHT))
-bgrwin.set_colorkey((0, 0, 0))
-
-clock = pygame.time.Clock()
-font = pygame.font.SysFont(pygame.font.get_default_font(), 24)
-
-def rgb_for_freq_amp(f, a):
-    a = max((min((a, 1.0)), 0.0))
-    pitchval = float(f - options.low_freq) / (options.high_freq - options.low_freq)
-    if options.log_base == 0:
-        try:
-            pitchval = math.log(pitchval) / math.log(options.log_base)
-        except ValueError:
-            pass
-    bgcol = colorsys.hls_to_rgb(min((1.0, max((0.0, pitchval)))), 0.5 * (a ** 2), 1.0)
-    return [int(i*255) for i in bgcol]
-
-while True:
-    DISP_FACTOR = struct.unpack(fixfmt, mapping[:fixfmtsz])[0]
-    LAST_SAMPLES = struct.unpack(sigfmt, mapping[fixfmtsz:fixfmtsz+sigfmtsz])
-    VALUES = struct.unpack(strfmt, mapping[fixfmtsz+sigfmtsz:])
-    FREQS, AMPS = VALUES[::2], VALUES[1::2]
-    if options.no_colback:
-        disp.fill((0, 0, 0), (0, 0, WIDTH, HEIGHT))
-    else:
-        gap = WIDTH / STREAMS
+if options.engine == 'pygame':
+    import pygame
+    import pygame.gfxdraw
+
+    pygame.init()
+
+    WIDTH, HEIGHT = 640, 480
+    dispinfo = pygame.display.Info()
+    if dispinfo.current_h > 0 and dispinfo.current_w > 0:
+        WIDTH, HEIGHT = dispinfo.current_w, dispinfo.current_h
+
+    flags = 0
+    if options.fullscreen:
+        flags |= pygame.FULLSCREEN
+
+    disp = pygame.display.set_mode((WIDTH, HEIGHT), flags)
+    WIDTH, HEIGHT = disp.get_size()
+    SAMP_WIDTH = WIDTH / 2
+    if options.samp_width:
+        SAMP_WIDTH = options.samp_width
+    BGR_WIDTH = WIDTH - SAMP_WIDTH
+    HALFH = HEIGHT / 2
+    PFAC = HEIGHT / 128.0
+    sampwin = pygame.Surface((SAMP_WIDTH, HEIGHT))
+    sampwin.set_colorkey((0, 0, 0))
+    lastsy = HALFH
+    bgrwin = pygame.Surface((BGR_WIDTH, HEIGHT))
+    bgrwin.set_colorkey((0, 0, 0))
+
+    clock = pygame.time.Clock()
+    font = pygame.font.SysFont(pygame.font.get_default_font(), 24)
+
+    def rgb_for_freq_amp(f, a):
+        a = max((min((a, 1.0)), 0.0))
+        pitchval = float(f - options.low_freq) / (options.high_freq - options.low_freq)
+        if options.log_base == 0:
+            try:
+                pitchval = math.log(pitchval) / math.log(options.log_base)
+            except ValueError:
+                pass
+        bgcol = colorsys.hls_to_rgb(min((1.0, max((0.0, pitchval)))), 0.5 * (a ** 2), 1.0)
+        return [int(i*255) for i in bgcol]
+
+    while True:
+        DISP_FACTOR = struct.unpack(fixfmt, mapping[:fixfmtsz])[0]
+        LAST_SAMPLES = struct.unpack(sigfmt, mapping[fixfmtsz:fixfmtsz+sigfmtsz])
+        VALUES = struct.unpack(strfmt, mapping[fixfmtsz+sigfmtsz:])
+        FREQS, AMPS = VALUES[::2], VALUES[1::2]
+        if options.no_colback:
+            disp.fill((0, 0, 0), (0, 0, WIDTH, HEIGHT))
+        else:
+            gap = WIDTH / STREAMS
+            for i in xrange(STREAMS):
+                FREQ = FREQS[i]
+                AMP = AMPS[i]
+                if FREQ > 0:
+                    bgcol = rgb_for_freq_amp(FREQ, AMP)
+                else:
+                    bgcol = (0, 0, 0)
+                disp.fill(bgcol, (i*gap, 0, gap, HEIGHT))
+
+        bgrwin.scroll(-1, 0)
+        bgrwin.fill((0, 0, 0), (BGR_WIDTH - 1, 0, 1, HEIGHT))
         for i in xrange(STREAMS):
             FREQ = FREQS[i]
             AMP = AMPS[i]
             if FREQ > 0:
-                bgcol = rgb_for_freq_amp(FREQ, AMP)
+                try:
+                    pitch = 12 * math.log(FREQ / 440.0, 2) + 69
+                except ValueError:
+                    pitch = 0
             else:
-                bgcol = (0, 0, 0)
-            disp.fill(bgcol, (i*gap, 0, gap, HEIGHT))
-
-    bgrwin.scroll(-1, 0)
-    bgrwin.fill((0, 0, 0), (BGR_WIDTH - 1, 0, 1, HEIGHT))
-    for i in xrange(STREAMS):
-        FREQ = FREQS[i]
-        AMP = AMPS[i]
-        if FREQ > 0:
-            try:
-                pitch = 12 * math.log(FREQ / 440.0, 2) + 69
-            except ValueError:
                 pitch = 0
-        else:
-            pitch = 0
-        col = [min(max(int(AMP * 255), 0), 255)] * 3
-        bgrwin.fill(col, (BGR_WIDTH - 1, HEIGHT - pitch * PFAC - PFAC, 1, PFAC))
-
-    sampwin.fill((0, 0, 0), (0, 0, SAMP_WIDTH, HEIGHT))
-    x = 0
-    for i in LAST_SAMPLES:
-        sy = int(i * HALFH + HALFH)
-        pygame.gfxdraw.line(sampwin, x - 1, lastsy, x, sy, (0, 255, 0))
-        x += 1
-        lastsy = sy
-
-    disp.blit(bgrwin, (0, 0))
-    disp.blit(sampwin, (BGR_WIDTH, 0))
-
-    if DISP_FACTOR != 0:
-        tsurf = font.render('%+011.6g'%(DISP_FACTOR,), True, (255, 255, 255), (0, 0, 0))
-        disp.fill((0, 0, 0), tsurf.get_rect())
-        disp.blit(tsurf, (0, 0))
-
-    pygame.display.flip()
-
-    for ev in pygame.event.get():
-        if ev.type == pygame.KEYDOWN:
-            if ev.key == pygame.K_ESCAPE:
+            col = [min(max(int(AMP * 255), 0), 255)] * 3
+            bgrwin.fill(col, (BGR_WIDTH - 1, HEIGHT - pitch * PFAC - PFAC, 1, PFAC))
+
+        sampwin.fill((0, 0, 0), (0, 0, SAMP_WIDTH, HEIGHT))
+        x = 0
+        for i in LAST_SAMPLES:
+            sy = int(i * HALFH + HALFH)
+            pygame.gfxdraw.line(sampwin, x - 1, lastsy, x, sy, (0, 255, 0))
+            x += 1
+            lastsy = sy
+
+        disp.blit(bgrwin, (0, 0))
+        disp.blit(sampwin, (BGR_WIDTH, 0))
+
+        if DISP_FACTOR != 0:
+            tsurf = font.render('%+011.6g'%(DISP_FACTOR,), True, (255, 255, 255), (0, 0, 0))
+            disp.fill((0, 0, 0), tsurf.get_rect())
+            disp.blit(tsurf, (0, 0))
+
+        pygame.display.flip()
+
+        for ev in pygame.event.get():
+            if ev.type == pygame.KEYDOWN:
+                if ev.key == pygame.K_ESCAPE:
+                    pygame.quit()
+                    exit()
+            elif ev.type == pygame.QUIT:
                 pygame.quit()
                 exit()
-        elif ev.type == pygame.QUIT:
+
+        if not os.path.exists(options.map_file):
             pygame.quit()
             exit()
 
-    if not os.path.exists(options.map_file):
-        pygame.quit()
+        clock.tick(60)
+
+elif options.engine == 'glfw':
+    import array, ctypes
+    import glfw
+    from OpenGL import GL
+    from OpenGL.GL import *
+
+    if not glfw.init():
+        print 'GLFW: Init failed'
         exit()
 
-    clock.tick(60)
+    monitor = glfw.get_primary_monitor()
+    mode = glfw.get_video_mode(monitor)
+
+    glfw.window_hint(glfw.RED_BITS, mode.bits.red)
+    glfw.window_hint(glfw.GREEN_BITS, mode.bits.green)
+    glfw.window_hint(glfw.BLUE_BITS, mode.bits.blue)
+    glfw.window_hint(glfw.REFRESH_RATE, mode.refresh_rate)
+    glfw.window_hint(glfw.CONTEXT_VERSION_MAJOR, 4)
+    glfw.window_hint(glfw.CONTEXT_VERSION_MINOR, 3)
+    glfw.window_hint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
+    
+    win = glfw.create_window(mode.size.width, mode.size.height, 'render', monitor, None)
+    if not win:
+        print 'GLFW: Window creation failed'
+        glfw.terminate()
+        exit()
+
+    glfw.make_context_current(win)
+
+    print 'Version:', glGetString(GL_VERSION)
+    print 'Renderer:', glGetString(GL_RENDERER)
+
+    rect_data = array.array('f', [
+        -1.0, -1.0,
+        1.0, -1.0,
+        1.0, 1.0,
+        -1.0, -1.0,
+        1.0, 1.0,
+        -1.0, 1.0,
+    ])
+
+    rect = glGenBuffers(1)
+    glBindBuffer(GL_ARRAY_BUFFER, rect)
+    glBufferData(GL_ARRAY_BUFFER, rect_data.tostring(), GL_STATIC_DRAW)
+
+    rect_zo_data = array.array('f', [
+        0.0, 0.0,
+        1.0, 0.0,
+        1.0, 1.0,
+        0.0, 0.0,
+        1.0, 1.0,
+        0.0, 1.0,
+    ])
+
+    rect_zo = glGenBuffers(1)
+    glBindBuffer(GL_ARRAY_BUFFER, rect_zo)
+    glBufferData(GL_ARRAY_BUFFER, rect_zo_data.tostring(), GL_STATIC_DRAW)
+
+    samp_buf = glGenBuffers(1)
+    glBindBuffer(GL_ARRAY_BUFFER, samp_buf)
+    glBufferData(GL_ARRAY_BUFFER, mapping[fixfmtsz:fixfmtsz+sigfmtsz], GL_STREAM_DRAW)
+    glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, samp_buf)
+
+    freq_buf = glGenBuffers(1)
+    glBindBuffer(GL_ARRAY_BUFFER, freq_buf)
+    glBufferData(GL_ARRAY_BUFFER, STREAMS * 4, None, GL_STREAM_DRAW)
+    glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, freq_buf)
+
+    amp_buf = glGenBuffers(1)
+    glBindBuffer(GL_ARRAY_BUFFER, amp_buf)
+    glBufferData(GL_ARRAY_BUFFER, STREAMS * 4, None, GL_STREAM_DRAW)
+    glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, amp_buf)
+
+    lin_buf = glGenBuffers(1)
+    glBindBuffer(GL_ARRAY_BUFFER, lin_buf)
+    lin_arr = array.array('f', [(float(i) / options.map_samples) * 2.0 - 1.0 for i in range(options.map_samples)])
+    #print lin_arr
+    glBufferData(GL_ARRAY_BUFFER, lin_arr.tostring(), GL_STATIC_DRAW)
+
+    bg_tex = glGenTextures(1)
+    glBindTexture(GL_TEXTURE_2D, bg_tex)
+    bg_data = array.array('B', [0 for i in range(mode.size.width * mode.size.height)])
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, mode.size.width, mode.size.height, 0, GL_RED, GL_UNSIGNED_BYTE, bg_data.tostring())
+    glTexParameter(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
+    glTexParameter(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
+    glTexParameter(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
+    glTexParameter(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
+
+    bg_swp = glGenTextures(1)
+    glBindTexture(GL_TEXTURE_2D, bg_swp)
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, mode.size.width, mode.size.height, 0, GL_RED, GL_UNSIGNED_BYTE, bg_data.tostring())
+    glTexParameter(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
+    glTexParameter(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
+    glTexParameter(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
+    glTexParameter(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
+
+    # Some *4s below because the texture is packed as RGBA8 despite only indexing R
+
+    bar_pfac = mode.size.height / 128.0
+    bg_bar_h = int(bar_pfac)
+    bg_bar_sz = (bg_bar_h*4)
+
+    block_d = '\x7f' * (64*64*4)
+    clear_d = '\x00' * (mode.size.height*4)
+    
+    fbs = glGenFramebuffers(1)
+    fbd = glGenFramebuffers(1)
+
+    def make_prog(vss, fss):
+        vs = glCreateShader(GL_VERTEX_SHADER)
+        glShaderSource(vs, vss)
+        glCompileShader(vs)
+        if not glGetShaderiv(vs, GL_COMPILE_STATUS):
+            print 'Vertex error:', glGetShaderInfoLog(vs)
+            exit()
+
+        fs = glCreateShader(GL_FRAGMENT_SHADER)
+        glShaderSource(fs, fss)
+        glCompileShader(fs)
+        if not glGetShaderiv(fs, GL_COMPILE_STATUS):
+            print 'Fragment error:', glGetShaderInfoLog(fs)
+            exit()
+
+        prog = glCreateProgram()
+        glAttachShader(prog, vs)
+        glAttachShader(prog, fs)
+        glLinkProgram(prog)
+        if not glGetProgramiv(prog, GL_LINK_STATUS):
+            print 'Program error:', glGetProgramInfoLog(prog)
+            exit()
+
+        return prog
+
+    prog_bg = make_prog('''
+#version 430
+
+in vec2 vPosition;
+in vec2 vTex;
+
+out vec2 vUV;
+
+void main(void) {
+    gl_Position = vec4(vPosition,0.0,1.0);
+    vUV = vTex;
+}''', '''
+#version 430
+
+in vec2 vUV;
+
+layout (location = 0) out vec4 FragColor;
+layout (std430, binding = 2) buffer bfreq {
+    uint freq[];
+};
+layout (std430, binding = 3) buffer bamp {
+    float amp[];
+};
+
+vec3 map_col(uint fr, float intensity) {
+    if(fr == 0) return vec3(0.0,0.0,0.0);
+    return vec3(
+        0.66 * clamp((float(fr) - 40.0) / (1500.0 - 40.0), 0.0, 1.0),
+        1.0,
+        clamp(intensity, 0.0, 1.0)
+    );
+}
+
+vec3 hsv2rgb(vec3 c)
+{
+    vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
+    vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
+    return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
+}
+
+void main(void) {
+    float zox = (vUV.x + 1.0) / 2.0;
+    uint v = uint(zox * freq.length());
+    FragColor = vec4(
+        hsv2rgb(map_col(freq[v], amp[v])),
+        1.0
+    );
+}''')
+
+    glUseProgram(prog_bg)
+    vao_bg = glGenVertexArrays(1)
+    glBindVertexArray(vao_bg)
+    glBindBuffer(GL_ARRAY_BUFFER, rect)
+    a_vPosition = glGetProgramResourceLocation(prog_bg, GL_PROGRAM_INPUT, 'vPosition')
+    print 'prog_bg a_vPosition', a_vPosition
+    glVertexAttribPointer(a_vPosition, 2, GL_FLOAT, False, 0, None)
+    glEnableVertexAttribArray(a_vPosition)
+    a_vTex = glGetProgramResourceLocation(prog_bg, GL_PROGRAM_INPUT, 'vTex')
+    print 'prog_bg a_vTex', a_vTex
+    glVertexAttribPointer(a_vTex, 2, GL_FLOAT, False, 0, None)
+    glEnableVertexAttribArray(a_vTex)
+
+    prog_scope = make_prog('''
+#version 430
+
+in float vX;
+in float vY;
+
+void main(void) {
+    gl_Position = vec4(vX, vY, 0.0, 1.0);
+}
+''', '''
+#version 430
+
+layout (location = 0) out vec4 FragColor;
+
+void main(void) {
+    FragColor = vec4(0.0, 1.0, 0.0, 1.0);
+}
+''')
+
+    glUseProgram(prog_scope)
+    vao_scope = glGenVertexArrays(1)
+    glBindVertexArray(vao_scope)
+    glBindBuffer(GL_ARRAY_BUFFER, lin_buf)
+    a_vX = glGetProgramResourceLocation(prog_scope, GL_PROGRAM_INPUT, 'vX')
+    print 'prog_scope a_vX', a_vX
+    glVertexAttribPointer(a_vX, 1, GL_FLOAT, False, 0, None)
+    glEnableVertexAttribArray(a_vX)
+    glBindBuffer(GL_ARRAY_BUFFER, samp_buf)
+    a_vY = glGetProgramResourceLocation(prog_scope, GL_PROGRAM_INPUT, 'vY')
+    print 'prog_scope a_vY', a_vY
+    glVertexAttribPointer(a_vY, 1, GL_FLOAT, False, 0, None)
+    glEnableVertexAttribArray(a_vY)
+
+    prog_bar = make_prog('''
+#version 430
+
+in vec2 vPosition;
+in vec2 vTex;
+
+out vec2 vUV;
+
+void main(void) {
+    gl_Position = vec4(vPosition, 0.0, 1.0);
+    vUV = vTex;
+}''', '''
+#version 430
+
+in vec2 vUV;
+
+layout (location = 0) out vec4 FragColor;
+
+uniform sampler2D uTex;
+
+void main(void) {
+    vec4 col = texture(uTex, vUV);
+    FragColor = vec4(1.0, 1.0, 1.0, col.r);
+}''')
+
+    glUseProgram(prog_bar)
+    vao_bar = glGenVertexArrays(1)
+    glBindVertexArray(vao_bar)
+    glBindBuffer(GL_ARRAY_BUFFER, rect)
+    a_vPosition = glGetProgramResourceLocation(prog_bar, GL_PROGRAM_INPUT, 'vPosition')
+    print 'prog_bar a_vPosition', a_vPosition
+    glVertexAttribPointer(a_vPosition, 2, GL_FLOAT, False, 0, None)
+    glEnableVertexAttribArray(a_vPosition)
+    a_vTex = glGetProgramResourceLocation(prog_bar, GL_PROGRAM_INPUT, 'vTex')
+    print 'prog_bar a_vTex', a_vTex
+    glBindBuffer(GL_ARRAY_BUFFER, rect_zo)
+    glVertexAttribPointer(a_vTex, 2, GL_FLOAT, False, 0, None)
+    glEnableVertexAttribArray(a_vTex)
+    u_uTex = glGetProgramResourceLocation(prog_bar, GL_UNIFORM, 'uTex')
+    print 'prog_bar u_uTex', u_uTex
+    glUniform1i(u_uTex, 0)
+    glActiveTexture(GL_TEXTURE0)
+    glBindTexture(GL_TEXTURE_2D, bg_tex)
+
+    glClearColor(0.2, 0.0, 0.0, 1.0)
+    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
+
+    while not glfw.window_should_close(win):
+        glfw.make_context_current(win)
+        glClear(GL_COLOR_BUFFER_BIT)
+
+        arr = array.array('f')
+        arr.fromstring(mapping[fixfmtsz:fixfmtsz+sigfmtsz])
+        arr.byteswap()
+        glBindBuffer(GL_ARRAY_BUFFER, samp_buf)
+        glBufferSubData(GL_ARRAY_BUFFER, 0, arr.tostring())
+
+        arr = array.array('I')
+        arr.fromstring(mapping[fixfmtsz+sigfmtsz:])
+        #print len(arr)
+        arr.byteswap()
+        glBindBuffer(GL_ARRAY_BUFFER, freq_buf)
+        glBufferSubData(GL_ARRAY_BUFFER, 0, arr[::2].tostring())
+        arr_fq = arr[::2]
+
+        arr = array.array('f')
+        arr.fromstring(mapping[fixfmtsz+sigfmtsz:])
+        #print len(arr)
+        arr.byteswap()
+        glBindBuffer(GL_ARRAY_BUFFER, amp_buf)
+        glBufferSubData(GL_ARRAY_BUFFER, 0, arr[1::2].tostring())
+        arr_am = arr[1::2]
+
+        #print len(arr_fq), len(arr_am)
+        #print zip(arr_fq, arr_am)
+
+        glBindFramebuffer(GL_READ_FRAMEBUFFER, fbs)
+        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbd)
+        glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, bg_tex, 0)
+        glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, bg_swp, 0)
+        stat = glCheckFramebufferStatus(GL_READ_FRAMEBUFFER)
+        if stat != GL_FRAMEBUFFER_COMPLETE:
+            print 'Incomplete read buffer:', stat
+        stat = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER)
+        if stat != GL_FRAMEBUFFER_COMPLETE:
+            print 'Incomplete draw buffer:', stat
+        glBlitFramebuffer(
+            1, 0, mode.size.width, mode.size.height,
+            0, 0, mode.size.width - 1, mode.size.height,
+            GL_COLOR_BUFFER_BIT, GL_NEAREST
+        )
+        glBindFramebuffer(GL_FRAMEBUFFER, 0)
+
+        bg_swp, bg_tex = bg_tex, bg_swp
+        glBindTexture(GL_TEXTURE_2D, bg_tex)
+        glTexSubImage2D(GL_TEXTURE_2D, 0, mode.size.width - 1, 0, 1, mode.size.height, GL_RED, GL_UNSIGNED_BYTE, clear_d)
+        for f, a in zip(arr_fq, arr_am):
+            if f == 0:
+                continue
+            try:
+                pitch = 12 * math.log(f / 440.0, 2) + 69
+            except ValueError:
+                pitch = 0
+            bg_bar_d = chr(int(255 * max((0.0, min((1.0, a)))))) * bg_bar_sz
+            glTexSubImage2D(GL_TEXTURE_2D, 0, mode.size.width - 1, int(pitch * bar_pfac), 1, bg_bar_h, GL_RED, GL_UNSIGNED_BYTE, bg_bar_d)
+            #print 'plot', mode.size.width - 1, int(pitch * bg_bar_h), 1, bg_bar_h, repr(bg_bar_d)
+            #glTexSubImage2D(GL_TEXTURE_2D, 0, mode.size.width - 64, int(pitch * bg_bar_h), 64, 64, GL_RED, GL_UNSIGNED_BYTE, block_d)
+
+        #glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 64, 64, GL_RED, GL_UNSIGNED_BYTE, block_d)
+        #glTexSubImage2D(GL_TEXTURE_2D, 0, mode.size.width - 64, mode.size.height - 64, 64, 64, GL_RED, GL_UNSIGNED_BYTE, block_d)
+
+        glActiveTexture(GL_TEXTURE0)
+        glBindTexture(GL_TEXTURE_2D, 0)
+
+        glUseProgram(prog_bg)
+        glBindVertexArray(vao_bg)
+        glDrawArrays(GL_TRIANGLES, 0, 6)
+
+        glUseProgram(prog_bar)
+        glBindVertexArray(vao_bar)
+        glBindTexture(GL_TEXTURE_2D, bg_tex)
+        #print bg_tex, bg_swp
+        #print glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH), glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT)
+        glEnable(GL_BLEND)
+        glDrawArrays(GL_TRIANGLES, 0, 6)
+        glDisable(GL_BLEND)
+        glBindTexture(GL_TEXTURE_2D, 0)
+
+        glUseProgram(prog_scope)
+        glBindVertexArray(vao_scope)
+        glDrawArrays(GL_LINE_STRIP, 0, options.map_samples)
+
+        glfw.swap_buffers(win)
+        glfw.poll_events()
+
+    glfw.terminate()
-- 
cgit v1.2.3-70-g09d2