]> git.sesse.net Git - narabu/commitdiff
Add the GPU decoder itself.
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Sat, 16 Sep 2017 13:19:13 +0000 (15:19 +0200)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Sat, 16 Sep 2017 13:58:08 +0000 (15:58 +0200)
decoder.shader [new file with mode: 0644]
narabu.cpp [new file with mode: 0644]
util.cpp [new file with mode: 0644]
util.h [new file with mode: 0644]

diff --git a/decoder.shader b/decoder.shader
new file mode 100644 (file)
index 0000000..6d54e4d
--- /dev/null
@@ -0,0 +1,327 @@
+#version 430
+#extension GL_ARB_shader_clock : enable
+
+#define ENABLE_TIMING 0
+
+layout(local_size_x = 8, local_size_y = 8) in;
+layout(r8ui) uniform restrict readonly uimage2D cum2sym_tex;
+layout(rg16ui) uniform restrict readonly uimage2D dsyms_tex;
+layout(r8) uniform restrict writeonly image2D out_tex;
+
+const uint prob_bits = 12;
+const uint prob_scale = 1 << prob_bits;
+const uint NUM_SYMS = 256;
+const uint ESCAPE_LIMIT = NUM_SYMS - 1;
+
+// These need to be folded into quant_matrix.
+const float dc_scalefac = 8.0;
+const float quant_scalefac = 4.0;
+
+const float quant_matrix[64] = {
+         8, 16, 19, 22, 26, 27, 29, 34,
+        16, 16, 22, 24, 27, 29, 34, 37,
+        19, 22, 26, 27, 29, 34, 34, 38,
+        22, 22, 26, 27, 29, 34, 37, 40,
+        22, 26, 27, 29, 32, 35, 40, 48,
+        26, 27, 29, 32, 35, 40, 48, 58,
+        26, 27, 29, 34, 38, 46, 56, 69,
+        27, 29, 35, 38, 46, 56, 69, 83
+};
+const uint ff_zigzag_direct[64] = {
+    0,   1,  8, 16,  9,  2,  3, 10,
+    17, 24, 32, 25, 18, 11,  4,  5,
+    12, 19, 26, 33, 40, 48, 41, 34,
+    27, 20, 13,  6,  7, 14, 21, 28,
+    35, 42, 49, 56, 57, 50, 43, 36,
+    29, 22, 15, 23, 30, 37, 44, 51,
+    58, 59, 52, 45, 38, 31, 39, 46,
+    53, 60, 61, 54, 47, 55, 62, 63
+};
+
+layout(std430, binding = 9) buffer layoutName
+{
+       uint data_SSBO[];
+};
+layout(std430, binding = 10) buffer layoutName2
+{
+       uvec2 timing[10 * 64];
+};
+
+struct CoeffStream {
+       uint src_offset, src_len, sign_offset, sign_len, extra_bits;
+};
+layout(std430, binding = 0) buffer whatever3
+{
+       CoeffStream streams[];
+};
+
+uniform uint src_offset, src_len, sign_offset, sign_len, extra_bits;
+
+const uint RANS_BYTE_L = (1u << 23);  // lower bound of our normalization interval
+
+uint last_offset = -1, ransbuf;
+
+uint get_rans_byte(uint offset)
+{
+       if (last_offset != (offset >> 2)) {
+               last_offset = offset >> 2;
+               ransbuf = data_SSBO[offset >> 2];
+       }
+       return bitfieldExtract(ransbuf, 8 * int(offset & 3u), 8);
+
+       // We assume little endian.
+//     return bitfieldExtract(data_SSBO[offset >> 2], 8 * int(offset & 3u), 8);
+}
+
+void RansDecInit(out uint r, inout uint offset)
+{
+       uint x;
+
+       x  = get_rans_byte(offset);
+       x |= get_rans_byte(offset + 1) << 8;
+       x |= get_rans_byte(offset + 2) << 16;
+       x |= get_rans_byte(offset + 3) << 24;
+       offset += 4;
+
+       r = x;
+}
+
+uint RansDecGet(uint r, uint scale_bits)
+{
+       return r & ((1u << scale_bits) - 1);
+}
+
+void RansDecAdvance(inout uint rans, inout uint offset, const uint start, const uint freq, uint prob_bits)
+{
+       const uint mask = (1u << prob_bits) - 1;
+       rans = freq * (rans >> prob_bits) + (rans & mask) - start;
+       
+       // renormalize
+       while (rans < RANS_BYTE_L) {
+               rans = (rans << 8) | get_rans_byte(offset++);
+       }
+}
+
+uint cum2sym(uint bits, uint table)
+{
+       return imageLoad(cum2sym_tex, ivec2(bits, table)).x;
+}
+
+uvec2 get_dsym(uint k, uint table)
+{
+       return imageLoad(dsyms_tex, ivec2(k, table)).xy;
+}
+
+void idct_1d(inout float y0, inout float y1, inout float y2, inout float y3, inout float y4, inout float y5, inout float y6, inout float y7)
+{
+       const float a1 = 0.7071067811865474;   // sqrt(2)
+       const float a2 = 0.5411961001461971;   // cos(3/8 pi) * sqrt(2)
+       const float a4 = 1.3065629648763766;   // cos(pi/8) * sqrt(2)
+       // static const float a5 = 0.5 * (a4 - a2);
+       const float a5 = 0.3826834323650897;
+
+       // phase 2 (phase 1 is just moving around)
+       const float p2_4 = y5 - y3;
+       const float p2_5 = y1 + y7;
+       const float p2_6 = y1 - y7;
+       const float p2_7 = y5 + y3;
+
+       // phase 3
+       const float p3_2 = y2 - y6;
+       const float p3_3 = y2 + y6;
+       const float p3_5 = p2_5 - p2_7;
+       const float p3_7 = p2_5 + p2_7;
+
+       // phase 4
+       const float p4_2 = a1 * p3_2;
+       const float p4_4 = p2_4 * a2 + (p2_4 + p2_6) * a5;  // Inverted.
+       const float p4_5 = a1 * p3_5;
+       const float p4_6 = p2_6 * a4 - (p2_4 + p2_6) * a5;
+
+       // phase 5
+       const float p5_0 = y0 + y4;
+       const float p5_1 = y0 - y4;
+       const float p5_3 = p4_2 + p3_3;
+
+       // phase 6
+       const float p6_0 = p5_0 + p5_3;
+       const float p6_1 = p5_1 + p4_2;
+       const float p6_2 = p5_1 - p4_2;
+       const float p6_3 = p5_0 - p5_3;
+       const float p6_5 = p4_5 + p4_4;
+       const float p6_6 = p4_5 + p4_6;
+       const float p6_7 = p4_6 + p3_7;
+
+       // phase 7
+       y0 = p6_0 + p6_7;
+       y1 = p6_1 + p6_6;
+       y2 = p6_2 + p6_5;
+       y3 = p6_3 - p4_4;
+       y4 = p6_3 + p4_4;
+       y5 = p6_2 - p6_5;
+       y6 = p6_1 - p6_6;
+       y7 = p6_0 - p6_7;
+}
+
+shared float temp[64 * 8];
+
+void pick_timer(inout uvec2 start, inout uvec2 t)
+{
+#if ENABLE_TIMING
+       uvec2 now = clock2x32ARB();
+
+       uvec2 delta = now - start;
+       if (now.x < start.x) {
+               --delta.y;
+       }
+
+       uvec2 new_t = t + delta;
+       if (new_t.x < t.x) {
+               ++new_t.y;
+       }
+       t = new_t;
+
+       start = clock2x32ARB();
+#endif
+}
+
+void main()
+{
+       uvec2 local_timing[10];
+#if ENABLE_TIMING
+       for (int timer_idx = 0; timer_idx < 10; ++timer_idx) {
+               local_timing[timer_idx] = uvec2(0, 0);
+       }
+       uvec2 start = clock2x32ARB();
+#else
+       uvec2 start;
+#endif
+
+       const uint num_blocks = 720 / 16;  // FIXME: make a uniform
+       const uint thread_num = gl_LocalInvocationID.y * 8 + gl_LocalInvocationID.x;
+
+       const uint block_row = gl_WorkGroupID.y;
+       //const uint coeff_num = ff_zigzag_direct[thread_num];
+       const uint coeff_num = thread_num;
+       const uint stream_num = coeff_num * num_blocks + block_row;
+       //const uint stream_num = block_row * num_blocks + coeff_num;  // HACK
+       const uint model_num = min((coeff_num % 8) + (coeff_num / 8), 7);
+
+       // Initialize rANS decoder.
+       uint offset = streams[stream_num].src_offset;
+       uint rans;
+       RansDecInit(rans, offset);
+
+       // Initialize sign bit decoder. TODO: this ought to be 32-bit-aligned instead!
+       uint soffset = streams[stream_num].sign_offset;
+       uint sign_buf = get_rans_byte(soffset++) >> streams[stream_num].extra_bits;
+       uint sign_bits_left = 8 - streams[stream_num].extra_bits;
+
+       float q = (coeff_num == 0) ? 1.0 : (quant_matrix[coeff_num] * quant_scalefac / 128.0 / sqrt(2.0));  // FIXME: fold
+       q *= (1.0 / 255.0);
+       //int w = (coeff_num == 0) ? 32 : int(quant_matrix[coeff_num]);
+       int last_k = 0;
+
+       pick_timer(start, local_timing[0]);
+
+       for (uint block_idx = 40; block_idx --> 0; ) {
+               uint block_x = block_idx % 20;
+               uint block_y = block_idx / 20;
+               if (block_x == 19) last_k = 0;
+
+               pick_timer(start, local_timing[1]);
+
+               // rANS decode one coefficient across eight blocks (so 64x8 coefficients).
+               for (uint subblock_idx = 8; subblock_idx --> 0; ) {
+                       // Read a symbol.
+                       int k = int(cum2sym(RansDecGet(rans, prob_bits), model_num));
+                       uvec2 sym = get_dsym(k, model_num);
+                       RansDecAdvance(rans, offset, sym.x, sym.y, prob_bits);
+
+                       if (k == ESCAPE_LIMIT) {
+                               k = int(RansDecGet(rans, prob_bits));
+                               RansDecAdvance(rans, offset, k, 1, prob_bits);
+                       }
+                       if (k != 0) {
+                               if (sign_bits_left == 0) {
+                                       sign_buf = get_rans_byte(soffset++);
+                                       sign_bits_left = 8;
+                               }
+                               if ((sign_buf & 1u) == 1u) k = -k;
+                               --sign_bits_left;
+                               sign_buf >>= 1;
+                       }
+
+                       if (coeff_num == 0) {
+                               k += last_k;
+                               last_k = k;
+                       }
+
+                       temp[subblock_idx * 64 + coeff_num] = k * q;
+                       //temp[subblock_idx * 64 + 8 * y + x] = (2 * k * w * 4) / 32;  // 100% matching unquant
+               }
+
+               pick_timer(start, local_timing[2]);
+
+               memoryBarrierShared();
+               barrier();
+
+               pick_timer(start, local_timing[3]);
+
+               // Horizontal DCT one row (so 64 rows).
+               idct_1d(temp[thread_num * 8 + 0],
+                       temp[thread_num * 8 + 1],
+                       temp[thread_num * 8 + 2],
+                       temp[thread_num * 8 + 3],
+                       temp[thread_num * 8 + 4],
+                       temp[thread_num * 8 + 5],
+                       temp[thread_num * 8 + 6],
+                       temp[thread_num * 8 + 7]);
+
+               pick_timer(start, local_timing[4]);
+
+               memoryBarrierShared();
+               barrier();
+
+               pick_timer(start, local_timing[5]);
+
+               // Vertical DCT one row (so 64 columns).
+               uint row_offset = gl_LocalInvocationID.y * 64 + gl_LocalInvocationID.x;
+               idct_1d(temp[row_offset + 0 * 8],
+                       temp[row_offset + 1 * 8],
+                       temp[row_offset + 2 * 8],
+                       temp[row_offset + 3 * 8],
+                       temp[row_offset + 4 * 8],
+                       temp[row_offset + 5 * 8],
+                       temp[row_offset + 6 * 8],
+                       temp[row_offset + 7 * 8]);
+
+               pick_timer(start, local_timing[6]);
+
+               uint y = block_row * 16 + block_y * 8;
+               uint x = block_x * 64 + gl_LocalInvocationID.y * 8 + gl_LocalInvocationID.x;
+               for (uint yl = 0; yl < 8; ++yl) {
+                       imageStore(out_tex, ivec2(x, yl + y), vec4(temp[row_offset + yl * 8], 0.0, 0.0, 1.0));
+               }
+
+               pick_timer(start, local_timing[7]);
+
+               memoryBarrierShared();  // is this needed?
+               barrier();
+
+               pick_timer(start, local_timing[8]);
+               pick_timer(start, local_timing[9]);  // should be nearly nothing
+       }
+
+#if ENABLE_TIMING
+       for (int timer_idx = 0; timer_idx < 10; ++timer_idx) {
+               uint global_idx = thread_num * 10 + timer_idx;
+
+               uint old_val = atomicAdd(timing[global_idx].x, local_timing[timer_idx].x);
+               if (old_val + local_timing[timer_idx].x < old_val) {
+                       ++local_timing[timer_idx].y;
+               }
+               atomicAdd(timing[global_idx].y, local_timing[timer_idx].y);
+       }
+#endif
+}
diff --git a/narabu.cpp b/narabu.cpp
new file mode 100644 (file)
index 0000000..4897366
--- /dev/null
@@ -0,0 +1,328 @@
+#include <stdio.h>
+#include <assert.h>
+#include <SDL2/SDL.h>
+#include <SDL2/SDL_error.h>
+#include <SDL2/SDL_video.h>
+#include <epoxy/gl.h>
+#include <string>
+#include <optional>
+#include <algorithm>
+#include <vector>
+#include <memory>
+
+#include "util.h"
+
+using namespace std;
+
+#define WIDTH 1280
+#define HEIGHT 720
+
+const unsigned prob_bits = 12;
+const unsigned prob_scale = 1 << prob_bits;
+const unsigned NUM_SYMS = 256;
+const unsigned NUM_TABLES = 16;
+
+struct RansDecSymbol {
+        unsigned sym_start;
+        unsigned sym_freq;
+};
+struct RansDecodeTable {
+        int cum2sym[prob_scale];
+        RansDecSymbol dsyms[NUM_SYMS];
+};
+RansDecodeTable decode_tables[NUM_TABLES];
+
+optional<uint32_t> read_varint(const char **ptr, const char *end)
+{
+       uint32_t x = 0;
+       int shift = 0;
+       while (*ptr < end) {
+               int ch = **ptr;
+               ++(*ptr);       
+
+               x |= (ch & 0x7f) << shift;
+               if ((ch & 0x80) == 0) return x;
+               shift += 7;
+               if (shift >= 32) {
+                       return nullopt;  // Error: Overlong int.
+               }
+       }
+       return nullopt;  // Error: EOF.
+}
+
+struct CoeffStream {
+        uint src_offset, src_len, sign_offset, sign_len, extra_bits;
+};
+CoeffStream streams[45 * 64];  // HACK
+
+int main(int argc, char **argv)
+{
+       // Set up an OpenGL context using SDL.
+       if (SDL_Init(SDL_INIT_VIDEO) == -1) {
+               fprintf(stderr, "SDL_Init failed: %s\n", SDL_GetError());
+               exit(1);
+       }
+       SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 0);
+       SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 0);
+       SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
+       SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
+       SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
+       SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 5);
+
+       SDL_Window *window = SDL_CreateWindow("OpenGL window for unit test",
+               SDL_WINDOWPOS_UNDEFINED,
+               SDL_WINDOWPOS_UNDEFINED,
+               32, 32,
+               SDL_WINDOW_OPENGL);
+       SDL_GLContext context = SDL_GL_CreateContext(window);
+       assert(context != nullptr);
+
+       //char buf[16] = { 0 };
+
+       GLint size;
+       glGetIntegerv(GL_MAX_COMPUTE_SHARED_MEMORY_SIZE, &size);
+       printf("shared_memory_size=%u\n", size);
+
+       string shader_src = read_file("decoder-pre-sign.shader");
+       GLuint shader_num = compile_shader(shader_src, GL_COMPUTE_SHADER);
+       GLuint glsl_program_num = glCreateProgram();
+       glAttachShader(glsl_program_num, shader_num);
+       glLinkProgram(glsl_program_num);
+
+       GLint success;
+       glGetProgramiv(glsl_program_num, GL_LINK_STATUS, &success);
+       if (success == GL_FALSE) {
+               GLchar error_log[1024] = {0};
+               glGetProgramInfoLog(glsl_program_num, 1024, nullptr, error_log);
+               fprintf(stderr, "Error linking program: %s\n", error_log);
+               exit(1);
+       }
+
+       glUseProgram(glsl_program_num);
+
+       string coded = read_file(argc >= 2 ? argv[1] : "coded.dat");
+       const char *ptr = &coded[0];
+       const char *end = ptr + coded.size();
+
+//     printf("first few bytes offs=%zu: %d %d %d %d %d %d %d %d\n", ptr - coded.data(),
+//             (uint8_t)ptr[0], (uint8_t)ptr[1], (uint8_t)ptr[2], (uint8_t)ptr[3],
+//             (uint8_t)ptr[4], (uint8_t)ptr[5], (uint8_t)ptr[6], (uint8_t)ptr[7]);
+
+       // read the rANS tables
+       for (unsigned table = 0; table < NUM_TABLES; ++table) {
+               uint32_t cum_freq = 0;
+               for (unsigned sym = 0; sym < NUM_SYMS; ++sym) {
+                       optional<uint32_t> freq = read_varint(&ptr, end);
+                       if (!freq) {
+                               fprintf(stderr, "Error parsing varint for table %d symbol %d\n", table, sym);
+                               exit(1);
+                       }
+
+                       decode_tables[table].dsyms[sym].sym_start = cum_freq;
+                       decode_tables[table].dsyms[sym].sym_freq = *freq;
+                       for (uint32_t i = 0; i < freq; ++i) {
+                               decode_tables[table].cum2sym[cum_freq++] = sym;
+                       }
+               }
+       }
+
+       // Make cum2sym texture.
+       unique_ptr<uint8_t[]> cum2sym_data(new uint8_t[prob_scale * NUM_TABLES]);
+       for (unsigned table = 0; table < NUM_TABLES; ++table) {
+               for (unsigned i = 0; i < prob_scale; ++i) {
+                       cum2sym_data[prob_scale * table + i] = decode_tables[table].cum2sym[i];
+               }
+       }
+       GLuint cum2sym_tex;
+       glGenTextures(1, &cum2sym_tex);
+        glBindTexture(GL_TEXTURE_2D, cum2sym_tex);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+        glTexImage2D(GL_TEXTURE_2D, 0, GL_R8UI, prob_scale, NUM_TABLES, 0, GL_RED_INTEGER, GL_UNSIGNED_BYTE, cum2sym_data.get());
+
+       // Make dsyms texture.
+       unique_ptr<pair<uint16_t, uint16_t>[]> dsyms_data(new pair<uint16_t, uint16_t>[NUM_SYMS * NUM_TABLES]);
+       for (unsigned table = 0; table < NUM_TABLES; ++table) {
+               for (unsigned sym = 0; sym < NUM_SYMS; ++sym) {
+                       dsyms_data[NUM_SYMS * table + sym].first = decode_tables[table].dsyms[sym].sym_start;
+                       dsyms_data[NUM_SYMS * table + sym].second = decode_tables[table].dsyms[sym].sym_freq;
+               }
+       }
+       GLuint dsyms_tex;
+       glGenTextures(1, &dsyms_tex);
+        glBindTexture(GL_TEXTURE_2D, dsyms_tex);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+        glTexImage2D(GL_TEXTURE_2D, 0, GL_RG16UI, NUM_SYMS, NUM_TABLES, 0, GL_RG_INTEGER, GL_UNSIGNED_SHORT, dsyms_data.get());
+
+       GLuint out_tex;
+       glGenTextures(1, &out_tex);
+        glBindTexture(GL_TEXTURE_2D, out_tex);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+        glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, 1280, 720, 0, GL_RED, GL_UNSIGNED_BYTE, nullptr);
+        //glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, 1280, 720, 0, GL_RED, GL_FLOAT, nullptr);
+
+       //GLint src_offset_pos = glGetUniformLocation(glsl_program_num, "src_offset");
+       //GLint sign_offset_pos = glGetUniformLocation(glsl_program_num, "sign_offset");
+       //GLint extra_bits_pos = glGetUniformLocation(glsl_program_num, "extra_bits");
+       GLint cum2sym_tex_pos = glGetUniformLocation(glsl_program_num, "cum2sym_tex");
+       GLint dsyms_tex_pos = glGetUniformLocation(glsl_program_num, "dsyms_tex");
+       GLint out_tex_pos = glGetUniformLocation(glsl_program_num, "out_tex");
+       printf("%d err=0x%x pos=%d,%d,%d\n", __LINE__, glGetError(), cum2sym_tex_pos, dsyms_tex_pos, out_tex_pos);
+
+       // Bind the textures.
+       glUniform1i(cum2sym_tex_pos, 0);
+       glUniform1i(dsyms_tex_pos, 1);
+       glUniform1i(out_tex_pos, 2);
+        glBindImageTexture(0, cum2sym_tex, 0, GL_FALSE, 0, GL_READ_ONLY, GL_R8UI);
+        glBindImageTexture(1, dsyms_tex, 0, GL_FALSE, 0, GL_READ_ONLY, GL_RG16UI);
+        glBindImageTexture(2, out_tex, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_R8);
+       printf("%d err=0x%x\n", __LINE__, glGetError());
+
+       // Decode all luma blocks.
+       unsigned num_blocks = (HEIGHT / 16);
+       for (unsigned y = 0; y < 8; ++y) {
+                for (unsigned x = 0; x < 8; ++x) {
+                       unsigned coeff_num = y * 8 + x;
+
+                       for (unsigned yb = 0; yb < HEIGHT; yb += 16) {
+                               optional<uint32_t> num_rans_bytes = read_varint(&ptr, end);
+                               if (!num_rans_bytes) {
+                                       fprintf(stderr, "Error parsing varint for block %d rANS bytes\n", yb);
+                                       exit(1);
+                               }
+
+                               CoeffStream *stream = &streams[coeff_num * num_blocks + (yb/16)];
+                               stream->src_offset = ptr - coded.data();
+                               stream->src_len = *num_rans_bytes;
+
+                               // TODO: check len
+                               ptr += *num_rans_bytes;
+
+                               optional<uint32_t> num_sign_bytes = read_varint(&ptr, end);
+                               if (!num_sign_bytes) {
+                                       fprintf(stderr, "Error parsing varint for block %d rANS bytes\n", yb);
+                                       exit(1);
+                               }
+
+                               stream->sign_offset = ptr - coded.data();
+                               stream->sign_len = *num_sign_bytes >> 3;
+                               stream->extra_bits = *num_sign_bytes & 0x7;
+
+                               // TODO: check len
+                               // TODO: free bits
+                               ptr += *num_sign_bytes >> 3;
+
+                               //printf("read %d rANS bytes, %d sign bytes\n", *num_rans_bytes, *num_sign_bytes);
+                       }
+               }
+       }
+
+       // put the coded data (as a whole) into an SSBO
+       printf("%d err=0x%x bufsize=%zu\n", __LINE__, glGetError(), coded.size());
+
+       GLuint ssbo_stream, ssbo, ssbo_out;
+
+       glGenBuffers(1, &ssbo_stream);
+       glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo_stream);
+       glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(streams), streams, GL_STREAM_DRAW);
+       glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, ssbo_stream);
+       printf("%d err=0x%x bufsize=%zu\n", __LINE__, glGetError(), coded.size());
+
+       glGenBuffers(1, &ssbo);
+       glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo);
+       glBufferData(GL_SHADER_STORAGE_BUFFER, coded.size(), coded.data(), GL_STREAM_DRAW);
+       glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 9, ssbo);
+       printf("%d err=0x%x bufsize=%zu\n", __LINE__, glGetError(), coded.size());
+
+       glGenBuffers(1, &ssbo_out);
+       glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo_out);
+       glBufferData(GL_SHADER_STORAGE_BUFFER, 16384, nullptr, GL_STREAM_DRAW);  // ??
+       glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 10, ssbo_out);
+
+       for (int i = 0; i < 10000; ++i)
+       glDispatchCompute(1, 45, 1);
+
+       unsigned *timing = (unsigned *)glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, 16384, GL_MAP_READ_BIT);
+       //setlocale(LC_ALL, "nb_NO.UTF-8");
+
+       string phases[] = {
+               "init",
+               "loop overhead",
+               "rANS decode",
+               "barrier after rANS decode",
+               "horizontal IDCT",
+               "barrier after horizontal IDCT",
+               "vertical IDCT",
+               "store to texture",
+               "barrier after store to texture",
+               "dummy timer for overhead measurement",
+       };
+       printf("\n");
+       for (int i = 0; i < 10; ++i) {
+               //printf("%d: %'18.0f  [%s]\n", i, double((uint64_t(timing[i * 2 + 1]) << 32) | timing[i * 2]), phases[i].c_str());
+               printf("%d,%s", i, phases[i].c_str());
+               for (int j = 0; j < 64; ++j) {
+                       int idx = (j * 10 + i) * 2;
+                       uint64_t val = (uint64_t(timing[idx + 1]) << 32) | timing[idx];
+               //      printf(" %'18.0f", double(val));
+               //      printf(" %'6.0f", double(val) * 1e-6);
+                       printf(",%.0f", double(val) * 1e-6);
+               }
+               printf("\n");
+               //printf("  [%s]\n", phases[i].c_str());
+       }
+       printf("\n");
+
+       unsigned char *data = new unsigned char[1280 * 720];
+       glGetTexImage(GL_TEXTURE_2D, 0, GL_RED, GL_UNSIGNED_BYTE, data);
+       printf("%d err=0x%x bufsize=%zu\n", __LINE__, glGetError(), coded.size());
+
+#if 0
+       for (int k = 0; k < 4; ++k) {
+               for (int y = 0; y < 8; ++y) {
+                       for (int x = 0; x < 8; ++x) {
+                               printf("%3d ", data[y * 1280 + x + k*8]);
+                       }
+                       printf("\n");
+               }
+               printf("\n");
+       }
+       printf("\n");
+#else
+       for (int k = 0; k < 4; ++k) {
+               for (int y = 0; y < 8; ++y) {
+                       for (int x = 0; x < 8; ++x) {
+                               //printf("%5.2f ", data[(y+8) * 1280 + x + (1272-k*8)]);
+                               printf("%3d ", data[y * 1280 + x + k*8]);
+                       }
+                       printf("\n");
+               }
+               printf("\n");
+       }
+       printf("\n");
+#endif
+
+       FILE *fp = fopen("narabu.pgm", "wb");
+       fprintf(fp, "P5\n1280 720\n255\n");
+       for (int y = 0; y < 720; ++y) {
+               for (int x = 0; x < 1280; ++x) {
+                       int k = lrintf(data[y * 1280 + x]);
+                       if (k < 0) k = 0;
+                       if (k > 255) k = 255;
+                       putc(k, fp);
+               }
+       }
+       fclose(fp);
+       
+       glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); // unbind
+       
+       printf("foo = 0x%x\n", glGetError());
+}
diff --git a/util.cpp b/util.cpp
new file mode 100644 (file)
index 0000000..5749f63
--- /dev/null
+++ b/util.cpp
@@ -0,0 +1,75 @@
+#include <string>
+#include <string.h>
+#include <stdlib.h>
+
+#include "util.h"
+
+using namespace std;
+
+string read_file(const string &filename)
+{
+       const string full_pathname = filename;
+
+       FILE *fp = fopen(full_pathname.c_str(), "r");
+       if (fp == NULL) {
+               perror(full_pathname.c_str());
+               exit(1);
+       }
+
+       int ret = fseek(fp, 0, SEEK_END);
+       if (ret == -1) {
+               perror("fseek(SEEK_END)");
+               exit(1);
+       }
+
+       int size = ftell(fp);
+
+       ret = fseek(fp, 0, SEEK_SET);
+       if (ret == -1) {
+               perror("fseek(SEEK_SET)");
+               exit(1);
+       }
+
+       string str;
+       str.resize(size);
+       ret = fread(&str[0], size, 1, fp);
+       if (ret == -1) {
+               perror("fread");
+               exit(1);
+       }
+       if (ret == 0) {
+               fprintf(stderr, "Short read when trying to read %d bytes from %s\n",
+                               size, full_pathname.c_str());
+               exit(1);
+       }
+       fclose(fp);
+
+       return str;
+}
+
+GLuint compile_shader(const string &shader_src, GLenum type)
+{
+       GLuint obj = glCreateShader(type);
+       const GLchar* source[] = { shader_src.data() };
+       const GLint length[] = { (GLint)shader_src.size() };
+       glShaderSource(obj, 1, source, length);
+       glCompileShader(obj);
+
+       GLchar info_log[4096];
+       GLsizei log_length = sizeof(info_log) - 1;
+       glGetShaderInfoLog(obj, log_length, &log_length, info_log);
+       info_log[log_length] = 0;
+       if (strlen(info_log) > 0) {
+               fprintf(stderr, "Shader compile log: %s\n", info_log);
+       }
+
+       GLint status;
+       glGetShaderiv(obj, GL_COMPILE_STATUS, &status);
+       if (status == GL_FALSE) {
+               fprintf(stderr, "Failed to compile shader: %s\n", shader_src.c_str());
+               exit(1);
+       }
+
+       return obj;
+}
+
diff --git a/util.h b/util.h
new file mode 100644 (file)
index 0000000..ebd416a
--- /dev/null
+++ b/util.h
@@ -0,0 +1,15 @@
+#ifndef _UTIL_H
+#define _UTIL_H 1
+
+#include <string>
+
+#include <epoxy/gl.h>
+
+// Read a file from disk and return its contents.
+// Dies if the file does not exist.
+std::string read_file(const std::string &filename);
+
+GLuint compile_shader(const std::string &shader_src, GLenum type);
+
+#endif // !defined(_UTIL_H)
+