From e09f663b9b230ca143dee2c9a9d238d691d60496 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20H=C3=BCbner?= Date: Thu, 4 Oct 2018 00:36:40 +0200 Subject: [PATCH] add stat of basic dissassemble --- dscanner.ini | 4 + source/app.d | 598 ++------------------------------------------- source/assembler.d | 223 +++++++++++++++++ source/gl.d | 344 ++++++++++++++++++++++++++ source/imgui.d | 193 +++++++++------ source/window.d | 6 +- 6 files changed, 719 insertions(+), 649 deletions(-) create mode 100644 dscanner.ini create mode 100644 source/assembler.d create mode 100644 source/gl.d diff --git a/dscanner.ini b/dscanner.ini new file mode 100644 index 0000000..c379c29 --- /dev/null +++ b/dscanner.ini @@ -0,0 +1,4 @@ +[analysis.config.StaticAnalysisConfig] + +undocumented_declaration_check="disabled" +long_line_check="disabled" \ No newline at end of file diff --git a/source/app.d b/source/app.d index 22593a4..65c0787 100644 --- a/source/app.d +++ b/source/app.d @@ -1,554 +1,11 @@ import std.stdio; import derelict.sdl2.sdl; import derelict.imgui.imgui; -import glad.gl.all; +import gl : PixelBuffer; import window; import imgui; -bool checkShaderError(GLuint shader, GLuint flag, bool is_program, in char[] shader_path) nothrow { - - GLint result; - - (is_program) ? glGetProgramiv(shader, flag, &result) - : glGetShaderiv(shader, flag, &result); - - if (result == GL_FALSE) { - - GLchar[256] log; //FIXME this is potentially fatal - (is_program) ? glGetProgramInfoLog(shader, log.sizeof, null, log.ptr) - : glGetShaderInfoLog(shader, log.sizeof, null, log.ptr); - - printf("[OpenGL] Error in %s: %s\n", shader_path.ptr, log.ptr); - return false; - - } - - return true; - -} // checkShaderError - -struct PixelBuffer { - - struct Shader { - - private { - GLuint shader_prog; - } - - @disable this(this); - - void compile(const (GLchar*)* vs_source, const (GLchar*)* fs_source) { - - import core.stdc.stdlib : exit; - - GLuint new_vs_shader = glCreateShader(GL_VERTEX_SHADER); - glShaderSource(new_vs_shader, 1, vs_source, null); - glCompileShader(new_vs_shader); - if (!checkShaderError(new_vs_shader, GL_COMPILE_STATUS, false, "vertex_shader")) { - exit(-1); - } - - GLuint new_fs_shader = glCreateShader(GL_FRAGMENT_SHADER); - glShaderSource(new_fs_shader, 1, fs_source, null); - glCompileShader(new_fs_shader); - if (!checkShaderError(new_fs_shader, GL_COMPILE_STATUS, false, "fragment_shader")) { - exit(-1); - } - - GLuint new_shader = glCreateProgram(); - glAttachShader(new_shader, new_vs_shader); - glAttachShader(new_shader, new_fs_shader); - - // glBindAttribLocation(new_shader, 0, "screen_size"); - - glLinkProgram(new_shader); - if (!checkShaderError(new_shader, GL_LINK_STATUS, true, "shader_program")) { - exit(-1); - } - - glValidateProgram(new_shader); - if (!checkShaderError(new_shader, GL_VALIDATE_STATUS, true, "shader_program")) { - exit(-1); - } - - shader_prog = new_shader; - - } // compile - - void setUniforms(int w, int h) { - auto attr_loc = glGetUniformLocation(shader_prog, "screen_size"); - glUniform2f(attr_loc, cast(float)w, cast(float)h); - } // setUniforms - - void bind() { - glUseProgram(shader_prog); - } // bind - - void unbind() { - glUseProgram(0); - } // unbind - - } // Shader - - struct Texture { - - import derelict.sdl2.sdl; - import derelict.sdl2.image; - - private { - - GLuint texture_; //OpenGL handle for texture - GLenum input_format_, output_format_, data_type_; - int width_, height_; - - } - - @property @nogc nothrow { - - int width() const { return width_; } - int height() const { return height_; } - GLuint handle() { return texture_; } - - } - - @disable this(this); - - nothrow @nogc - this(int width, int height, GLenum input_format, GLenum output_format, GLenum unpack_alignment) { - - width_ = width; - height_ = height; - input_format_ = input_format; - output_format_ = output_format; - data_type_ = GL_UNSIGNED_BYTE; - - glGenTextures(1, &texture_); - glBindTexture(GL_TEXTURE_2D, texture_); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - glPixelStorei(GL_UNPACK_ALIGNMENT, unpack_alignment); - glTexImage2D(GL_TEXTURE_2D, 0, input_format_, width_, height_, 0, output_format_, GL_UNSIGNED_BYTE, cast(void*)0); - - glBindTexture(GL_TEXTURE_2D, 0); - - } //this - - nothrow @nogc - void create(void* pixels, int width, int height, GLenum input_format = GL_RGB, GLenum output_format = GL_RGB, GLenum data_type = GL_UNSIGNED_BYTE) { - - width_ = width; - height_ = height; - input_format_ = input_format; - output_format_ = output_format; - data_type_ = data_type; - - //generate single texture, put handle in texture - glGenTextures(1, &texture_); - - //normal 2d texture, bind to our texture handle - glBindTexture(GL_TEXTURE_2D, texture_); - - //set texture parameters in currently bound texture, controls texture wrapping (or GL_CLAMP?) - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - //linearly interpolate between pixels, MIN if texture is too small for drawing area, MAG if drawing area is smaller than texture - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - - //texture type, level, format to store as, width, height, border, format loaded in - glTexImage2D(GL_TEXTURE_2D, 0, input_format_, width_, height_, 0, output_format_, data_type_, pixels); - - //UNBIND - glBindTexture(GL_TEXTURE_2D, 0); - - } // this - - ~this() nothrow @nogc { - - if (texture_ != 0) { - glDeleteTextures(1, &texture_); - } - - } // ~this - - /** - * Binds the texture handle, takes an argument for which texture unit to use. - */ - nothrow @nogc - void bind(int unit) { - - assert(unit >= 0 && unit <= 31); - glActiveTexture(GL_TEXTURE0 + unit); //since this is sequential, this works - glBindTexture(GL_TEXTURE_2D, texture_); - - } // bind - - nothrow @nogc - void unbind() { - - glBindTexture(GL_TEXTURE_2D, 0); - - } // unbind - - /** - * Updates the texture in place given the new texture buffer. - * Takes an optional offset to update only a part of the texture. - **/ - nothrow @nogc - void update(void[] pixels, size_t offset = 0) { - - bind(0); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width_, height_, input_format_, data_type_, pixels.ptr); - unbind(); - - } // update - - } // Texture - - /** - * Generic VertexArray structure, used to upload data of any given vertex type to the GPU. - */ - struct VertexArray { - - private { - - GLuint vao_; - GLuint vbo_; - GLenum type_; // type of vertex data, GL_TRIANGLES etc - uint num_vertices_; - - } - - @disable this(this); - - nothrow @nogc - void create(in float[2][] vertices, GLenum draw_type = GL_STATIC_DRAW, GLenum primitive = GL_TRIANGLES) { - - this.num_vertices_ = cast(uint)vertices.length; - this.type_ = primitive; - - glGenVertexArrays(1, &vao_); - glBindVertexArray(vao_); - - glGenBuffers(1, &vbo_); - glBindBuffer(GL_ARRAY_BUFFER, vbo_); - glBufferData(GL_ARRAY_BUFFER, vertices.length * vertices[0].sizeof, vertices.ptr, draw_type); - - // pos - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, - 2, - GL_FLOAT, - GL_FALSE, - float.sizeof * 2, - null - ); - - glBindVertexArray(0); - - } // this - - ~this() nothrow @nogc { - if (vao_ != 0) { - glDeleteVertexArrays(1, &vao_); - } - } // ~this - - void bind() nothrow @nogc { - glBindVertexArray(vao_); - } // bind - - void draw() nothrow @nogc { - glDrawArrays(type_, 0, num_vertices_); - } // draw - - void unbind() nothrow @nogc { - glBindVertexArray(0); - } // unbind - - } // VertexArray - - const char* vs_shader = q{ - #version 330 core - - uniform vec2 screen_size; - - layout(location = 0) in vec2 pos; - - out vec2 frag_uv; - - void main() { - gl_Position = vec4(pos, 0.0, 1.0); - frag_uv = clamp(pos, vec2(0.0, 0.0), vec2(1.0, 1.0)); - } - - }; - - const char* fs_shader = q{ - #version 330 core - - uniform sampler2D tex; - - in vec2 frag_uv; - - out vec4 out_col; - - void main() { - float v = texture(tex, frag_uv.st).r * 255; - out_col = vec4(v, v, v, 1.0); - } - - }; - - Shader shader; - VertexArray vao; - Texture tex; - - void create(void* pixels, int w, int h) { - - float[2][6] rect = [ - [-1.0f, -1.0f], // top left - [1.0f, -1.0f], // top right - [1.0f, 1.0f], // bottom right - - [-1.0f, -1.0f], // top left - [-1.0f, 1.0f], // bottom left - [1.0f, 1.0f], // bottom right - ]; - - vao.create(rect[]); - tex.create(pixels, w, h, GL_RED, GL_RED); - shader.compile(&vs_shader, &fs_shader); - - } // create - - void draw(int screen_w, int screen_h) { - - vao.bind(); - shader.bind(); - shader.setUniforms(screen_w, screen_h); - tex.bind(0); - vao.draw(); - - } // draw - - nothrow @nogc - void update(void[] pixels, size_t offset = 0) { - tex.update(pixels, offset); - } // update - -} // PixelBuffer - -struct Assembler { - - /* - Instruction Glossary: - 0x0nnn - SYS addr - 0x00E0 - CLS - 0x00EE - RET - 0x1nnn - JP addr - 0x2nnn - CALL addr - 0x3xkk - SE Vx, byte - 0x4xkk - SNE Vx, byte - 0x5xy0 - SE Vx, Vy - 0x6xkk - LD Vx, byte - 0x7xkk - ADD Vx, byte - 0x8xy0 - LD Vx, Vy - 0x8xy1 - OR Vx, Vy - 0x8xy2 - AND Vx, Vy - 0x8xy3 - XOR Vx, Vy - 0x8xy4 - ADD Vx, Vy - 0x8xy5 - SUB Vx, Vy - 0x8xy6 - SHR Vx {, Vy} - 0x8xy7 - SUBN Vx, Vy - 0x8xyE - SHL Vx {, Vy} - 0x9xy0 - SNE Vx, Vy - 0xAnnn - LD I, addr - 0xBnnn - JP V0, addr - 0xCxkk - RND Vx, byte - 0xDxyn - DRW Vx, Vy, nibble - 0xEx9E - SKP Vx - 0xExA1 - SKNP Vx - 0xFx07 - LD Vx, DT - 0xFx0A - LD Vx, K - 0xFx15 - LD DT, Vx - 0xFx18 - LD ST, Vx - 0xFx1E - ADD I, Vx - 0xFx29 - LD F, Vx - 0xFx33 - LD B, Vx - 0xFx55 - LD [I], Vx - 0xFx65 - LD Vx, [I] - */ - - enum OpCode { - - CLS, // CLS - RET, // RET - CALL, // CALL addr - - ADD, // ADD Vx, Vy - // ADD Vx, byte - // ADD I, Vx - - SUB, // SUB Vx, Vy - // SUB Vx, byte - - SUBN, // SUBN Vx, Vy - - SE, // SE Vx, Vy - // SE Vx, byte - - SNE, // SNE Vx, byte - SKP, // SKP Vx - SKNP, // SKNP Vx - - SHL, // SHL Vx {, Vy} - SHR, // SHR Vx {, Vy} - XOR, // XOR Vx, Vy - AND, // AND Vx, Vy - OR, // OR Vx, Vy - - LD, // LD Vx, DT - // LD DT, Vx - // LD ST, Vx - // LD Vx, K - // LD [I], Vx - // LD Vx, [I] - - RND // RND Vx, byte - - } - - enum Argument { - - Vx, - Vy, - - DT, - ST, - K, - - addr, - val - - } - - struct Instruction { - - } - - import std.array; - - Appender!string dissassembly_data; - - void assemble(const char* input) { - - } // assemble - - void assemble(const char[] input) { - - } // assemble - - const (char)[] disassemble(ubyte[] instructions) { - - import std.range : chunks; - - foreach (ref ubyte[] i; instructions.chunks(2)) { - - ushort opcode = (*(cast(ushort*)(i.ptr))); - - switch (opcode & 0xF000) { - case 0x0000: - switch (opcode & 0x0FFF) { - case 0x00E0: // 0x00E0 Clears the screen. - - case 0x00EE: // 0x00EE Returns from a subroutine. - default: // 0x0NNN Calls RCA 1802 program at address NNN. Not necessary for most ROMs. - //assert(0, "0x0NNN RCA 1802 program opcode not implemented!"); - break; - } - break; - case 0x1000: // 0x1NNN Jumps to address NNN. - case 0x2000: // 0x2NNN Calls subroutine at NNN. - case 0x3000: // 0x3XNN Skips the next instruction if VX equals NN. - case 0x4000: // 0x4XNN Skips the next instruction if VX doesn't equal NN. - case 0x5000: // 0x5XYO Skips the next instruction if VX equals VY. - case 0x6000: // 0x6XNN Sets VX to NN. - case 0x7000: // 0x7XNN Adds NN to VX. - case 0x8000: - switch (opcode) { - case 0x0000: // 0x8XY0 Sets VX to the value of VY. - case 0x0001: // 0x8XY1 Sets VX to VX or VY. - case 0x0002: // 0x8XY2 Sets VX to VX and VY. - case 0x0003: // 0x8XY3 Sets VX to VX xor VY. - case 0x0004: // 0x8XY4 Adds VY to VX. VF is set to 1 when there's a carry, and to 0 when there isn't. - case 0x0005: // 0x8XY5 VY is subtracted from VX. VF is set to 0 when there's a borrow, and 1 when there isn't. - case 0x0006: // 0x8XY6 Shifts VX right by one. VF is set to the value of the least significant bit of VX before the shift. - case 0x0007: // 0x8XY7 Sets VX to VY minus VX. VF is set to 0 when there's a borrow, and 1 when there isn't. - case 0x000E: // 0x8XYE Shifts VX left by one. VF is set to the value of the most significant bit of VX before the shift. - default: // unhandled for some reason - writefln("unknown opcode: 0x%x", opcode); - break; - } - break; - case 0x9000: // 0x9XYO Skips the next instruction if VX doesn't equal VY. - case 0xA000: // 0xANNN Sets I to the address NNN. - case 0xB000: // 0xBNNN Jumps to the address NNN plus V0. - case 0xC000: // 0xCXNN Sets VX to the result of a bitwise and operation on a random number and NN. - - // 0xDXYN - // Sprites stored in memory at location in index register (I), 8bits wide. - // Wraps around the screen. If when drawn, clears a pixel, register VF is set to 1 otherwise it is zero. - // All drawing is XOR drawing (i.e. it toggles the screen pixels). - // Sprites are drawn starting at position VX, VY. N is the number of 8bit rows that need to be drawn. - // If N is greater than 1, second line continues at position VX, VY+1, and so on. - case 0xD000: - case 0xE000: - switch (opcode & 0x000F) { - case 0x000E: // 0xEX9E Skips the next instruction if the key stored in VX is pressed. - case 0x0001: // 0xEXA1 Skips the next instruction if the key stored in VX isn't pressed. - default: // unhandled for some reason - writefln("unknown opcode: 0x%x", opcode); - break; - } - break; - case 0xF000: - switch (opcode & 0x00FF) { - case 0x0007: // 0xFX07 Sets VX to the value of the delay timer. - case 0x000A: // 0xFX0A A key press is awaited, and then stored in VX. - case 0x0015: // 0xFX15 Sets the delay timer to VX. - case 0x0018: // 0xFX18 Sets the sound timer to VX. - case 0x001E: // 0xFX1E Adds VX to I. - case 0x0029: // 0xFX29 Sets I to the location of the sprite for the character in VX. - // 0xFX33 Stores the Binary-coded decimal representation of VX, - // with the most significant of three digits at the address in I, - // the middle digit at I plus 1, and the least significant digit at I plus 2. - case 0x0033: - case 0x0055: // 0xFX55 Stores V0 to VX in memory starting at address I. - case 0x0065: // 0xFX65 Fills V0 to VX with values from memory starting at address I. - default: // unhandled for some reason - writefln("unknown opcode: 0x%x", opcode); - break; - } - break; - default: - writefln("unknown opcode: 0x%x", opcode); - } - } - - return ""; - - } // dissassemble - -} // Assembler - struct Chip8Status { // emu ptr @@ -768,23 +225,23 @@ struct Chip8Status { pure @property { - ubyte x(ref ushort oc) { + ubyte x(in ushort oc) { return (oc & 0x0F00) >> 8; } - ubyte y(ref ushort oc) { + ubyte y(in ushort oc) { return (oc & 0x00F0) >> 4; } - ubyte n(ref ushort oc) { + ubyte n(in ushort oc) { return oc & 0x000F; } - ubyte nn(ref ushort oc) { + ubyte nn(in ushort oc) { return (oc & 0x00FF); } - ushort nnn(ref ushort oc) { + ushort nnn(in ushort oc) { return (oc & 0x0FFF); } } @@ -824,7 +281,6 @@ struct Chip8 { KeyPad kp; ubyte[64*32] screen_buf; - ubyte[3][64*32] screen_data; bool run_flag; bool draw_flag; @@ -850,9 +306,7 @@ struct Chip8 { run_flag = run_flag.init; draw_flag = draw_flag.init; - screen_buf = screen_buf.init; - screen_data = screen_data.init; } // reset @@ -936,8 +390,8 @@ struct Chip8 { case 0x8000: - ubyte x = cpu.opcode.x; - ubyte y = cpu.opcode.y; + immutable ubyte x = cpu.opcode.x; + immutable ubyte y = cpu.opcode.y; switch (cpu.opcode.n) { @@ -958,8 +412,8 @@ struct Chip8 { break; case 0x0004: // 0x8XY4 Adds VY to VX. VF is set to 1 when there's a carry, and to 0 when there isn't. - ubyte vx = cpu.v[x]; - ubyte vy = cpu.v[y]; + immutable ubyte vx = cpu.v[x]; + immutable ubyte vy = cpu.v[y]; if (cast(ushort)vx + cast(ushort)vy > 255) { cpu.v[0xF] = 1; } else { @@ -969,8 +423,8 @@ struct Chip8 { break; case 0x0005: // 0x8XY5 VY is subtracted from VX. VF is set to 0 when there's a borrow, and 1 when there isn't. - ubyte vx = cpu.v[x]; - ubyte vy = cpu.v[y]; + immutable ubyte vx = cpu.v[x]; + immutable ubyte vy = cpu.v[y]; if (vx > vy) { cpu.v[0xF] = 1; } else { @@ -981,14 +435,14 @@ struct Chip8 { case 0x0006: // 0x8XY6 Shifts VX right by one. VF is set to the value of the least significant bit of VX before the shift. - ubyte vx = cpu.v[x]; + immutable ubyte vx = cpu.v[x]; cpu.v[0xF] = vx & 0x1; cpu.v[x] >>= 1; break; case 0x0007: // 0x8XY7 Sets VX to VY minus VX. VF is set to 0 when there's a borrow, and 1 when there isn't. - ubyte vx = cpu.v[x]; - ubyte vy = cpu.v[y]; + immutable ubyte vx = cpu.v[x]; + immutable ubyte vy = cpu.v[y]; if (vy > vx) { cpu.v[0xF] = 1; } else { @@ -999,7 +453,7 @@ struct Chip8 { case 0x000E: // 0x8XYE Shifts VX left by one. VF is set to the value of the most significant bit of VX before the shift. - ubyte vx = cpu.v[x]; + immutable ubyte vx = cpu.v[x]; cpu.v[0xF] = vx >> 7; cpu.v[x] <<= 1; break; @@ -1043,14 +497,14 @@ struct Chip8 { // If N is greater than 1, second line continues at position VX, VY+1, and so on. case 0xD000: - ProgramCounter spr_addr = cpu.i; - ubyte x = cpu.opcode.x; - ubyte y = cpu.opcode.y; - ubyte n = cpu.opcode.n; + immutable ProgramCounter spr_addr = cpu.i; + immutable ubyte x = cpu.opcode.x; + immutable ubyte y = cpu.opcode.y; + immutable ubyte n = cpu.opcode.n; foreach(int row; 0 .. n) { - ushort pixel = ram[spr_addr + row]; + immutable ushort pixel = ram[spr_addr + row]; foreach (int col; 0 .. 8) { if ((pixel & (0x80 >> col)) != 0) { @@ -1124,9 +578,9 @@ struct Chip8 { case 0x0029: // 0xFX29 Sets I to the location of the sprite for the character in VX. - ubyte vx = cpu.v[cpu.opcode.x]; - // ushort char_addr = 0x200 + (vx * 40); // base of char sprites + value of vx * bits per character - ushort char_addr = vx * 0x5; + immutable ubyte vx = cpu.v[cpu.opcode.x]; + immutable ushort char_addr = 0x200 + (vx * 40); // base of char sprites + value of vx * bits per character + // immutable ushort char_addr = vx * 0x5; cpu.i = char_addr; break; @@ -1135,7 +589,7 @@ struct Chip8 { // the middle digit at I plus 1, and the least significant digit at I plus 2. case 0x0033: - ubyte vx = cpu.v[cpu.opcode.x]; + immutable ubyte vx = cpu.v[cpu.opcode.x]; ram[cpu.i] = vx / 100; ram[cpu.i + 1] = (vx / 10) % 10; ram[cpu.i + 2] = (vx % 100) % 10; @@ -1320,8 +774,8 @@ struct Emulator { int w, h; window.windowSize(w, h); if (emu.draw_flag || emu.reset_flag) { - if (emu.reset_flag) emu.reset_flag = false; buf.update(emu.screen_buf); + emu.reset_flag = false; emu.draw_flag = false; } buf.draw(w, h); diff --git a/source/assembler.d b/source/assembler.d new file mode 100644 index 0000000..c7a08e6 --- /dev/null +++ b/source/assembler.d @@ -0,0 +1,223 @@ +module assembler; + +import std.stdio : writefln; + +struct Assembler { + + /* + Instruction Glossary: + 0x0nnn - SYS addr + 0x00E0 - CLS + 0x00EE - RET + 0x1nnn - JP addr + 0x2nnn - CALL addr + 0x3xkk - SE Vx, byte + 0x4xkk - SNE Vx, byte + 0x5xy0 - SE Vx, Vy + 0x6xkk - LD Vx, byte + 0x7xkk - ADD Vx, byte + 0x8xy0 - LD Vx, Vy + 0x8xy1 - OR Vx, Vy + 0x8xy2 - AND Vx, Vy + 0x8xy3 - XOR Vx, Vy + 0x8xy4 - ADD Vx, Vy + 0x8xy5 - SUB Vx, Vy + 0x8xy6 - SHR Vx {, Vy} + 0x8xy7 - SUBN Vx, Vy + 0x8xyE - SHL Vx {, Vy} + 0x9xy0 - SNE Vx, Vy + 0xAnnn - LD I, addr + 0xBnnn - JP V0, addr + 0xCxkk - RND Vx, byte + 0xDxyn - DRW Vx, Vy, nibble + 0xEx9E - SKP Vx + 0xExA1 - SKNP Vx + 0xFx07 - LD Vx, DT + 0xFx0A - LD Vx, K + 0xFx15 - LD DT, Vx + 0xFx18 - LD ST, Vx + 0xFx1E - ADD I, Vx + 0xFx29 - LD F, Vx + 0xFx33 - LD B, Vx + 0xFx55 - LD [I], Vx + 0xFx65 - LD Vx, [I] + */ + + enum OpCode { + + UNK, // UNKNOWN + + CLS, // CLS + RET, // RET + CALL, // CALL addr + + ADD, // ADD Vx, Vy + // ADD Vx, byte + // ADD I, Vx + + SUB, // SUB Vx, Vy + // SUB Vx, byte + + SUBN, // SUBN Vx, Vy + + SE, // SE Vx, Vy + // SE Vx, byte + + SNE, // SNE Vx, byte + SKP, // SKP Vx + SKNP, // SKNP Vx + + SHL, // SHL Vx {, Vy} + SHR, // SHR Vx {, Vy} + XOR, // XOR Vx, Vy + AND, // AND Vx, Vy + OR, // OR Vx, Vy + + LD, // LD Vx, DT + // LD DT, Vx + // LD ST, Vx + // LD Vx, K + // LD [I], Vx + // LD Vx, [I] + + RND // RND Vx, byte + + } + + enum Argument { + + Nil, + + Vx, + Vy, + + DT, + ST, + K, + + addr, + val + + } + + struct Instruction { + OpCode op; + Argument a1; + Argument a2; + } + + import std.array; + + private { + + Appender!(ubyte[]) assembled_data; + Appender!string dissassembled_data; + + } + + @disable this(this); + + ubyte[] assemble(const char* input) { + + return []; + + } // assemble + + ubyte[] assemble(const char[] input) { + + return []; + + } // assemble + + const (char)[] disassemble(ubyte[] instructions) { + + import std.range : chunks; + + foreach (ref ubyte[] i; instructions.chunks(2)) { + + ushort opcode = (*(cast(ushort*)(i.ptr))); + + switch (opcode & 0xF000) { + case 0x0000: + switch (opcode & 0x0FFF) { + case 0x00E0: // 0x00E0 Clears the screen. | CLS + + case 0x00EE: // 0x00EE Returns from a subroutine. | RET + default: // 0x0NNN Calls RCA 1802 program at address NNN. Not necessary for most ROMs. | SYS addr + //assert(0, "0x0NNN RCA 1802 program opcode not implemented!"); + break; + } + break; + case 0x1000: // 0x1NNN Jumps to address NNN. | JP addr + case 0x2000: // 0x2NNN Calls subroutine at NNN. | CALL addr + case 0x3000: // 0x3XNN Skips the next instruction if VX equals NN. | SE Vx, byte + case 0x4000: // 0x4XNN Skips the next instruction if VX doesn't equal NN. | SNE Vx, byte + case 0x5000: // 0x5XYO Skips the next instruction if VX equals VY. | SE Vx, Vy + case 0x6000: // 0x6XNN Sets VX to NN. | LD Vx, byte + case 0x7000: // 0x7XNN Adds NN to VX. | AD Vx, byte + case 0x8000: + switch (opcode) { + case 0x0000: // 0x8XY0 Sets VX to the value of VY. | LD Vx, Vy + case 0x0001: // 0x8XY1 Sets VX to VX or VY. | OR Vx, Vy + case 0x0002: // 0x8XY2 Sets VX to VX and VY. | AND Vx, Vy + case 0x0003: // 0x8XY3 Sets VX to VX xor VY. | XOR Vx, Vy + case 0x0004: // 0x8XY4 Adds VY to VX. VF is set to 1 when there's a carry, and to 0 when there isn't. | ADD Vx, Vy + case 0x0005: // 0x8XY5 VY is subtracted from VX. VF is set to 0 when there's a borrow, and 1 when there isn't. | SUB Vx, Vy + case 0x0006: // 0x8XY6 Shifts VX right by one. VF is set to the value of the least significant bit of VX before the shift. | SHR Vx {, Vy} + case 0x0007: // 0x8XY7 Sets VX to VY minus VX. VF is set to 0 when there's a borrow, and 1 when there isn't. | SUBN Vx, Vy + case 0x000E: // 0x8XYE Shifts VX left by one. VF is set to the value of the most significant bit of VX before the shift. | SHL Vx {, Vy} + default: // unhandled for some reason + writefln("unknown opcode: 0x%x", opcode); + break; + } + break; + case 0x9000: // 0x9XYO Skips the next instruction if VX doesn't equal VY. | SNE Vx, Vy + case 0xA000: // 0xANNN Sets I to the address NNN. | LD I, addr + case 0xB000: // 0xBNNN Jumps to the address NNN plus V0. | JP V0, addr + case 0xC000: // 0xCXNN Sets VX to the result of a bitwise and operation on a random number and NN. | RND Vx, byte + + // 0xDXYN | DRW Vx, Vy, nibble + // Sprites stored in memory at location in index register (I), 8bits wide. + // Wraps around the screen. If when drawn, clears a pixel, register VF is set to 1 otherwise it is zero. + // All drawing is XOR drawing (i.e. it toggles the screen pixels). + // Sprites are drawn starting at position VX, VY. N is the number of 8bit rows that need to be drawn. + // If N is greater than 1, second line continues at position VX, VY+1, and so on. + case 0xD000: + case 0xE000: + switch (opcode & 0x000F) { + case 0x000E: // 0xEX9E Skips the next instruction if the key stored in VX is pressed. | SKP Vx + case 0x0001: // 0xEXA1 Skips the next instruction if the key stored in VX isn't pressed. | SKNP Vx + default: // unhandled for some reason + writefln("unknown opcode: 0x%x", opcode); + break; + } + break; + case 0xF000: + switch (opcode & 0x00FF) { + case 0x0007: // 0xFX07 Sets VX to the value of the delay timer. | LD Vx, DT + case 0x000A: // 0xFX0A A key press is awaited, and then stored in VX. | LD Vx, K + case 0x0015: // 0xFX15 Sets the delay timer to VX. | LD DT, Vx + case 0x0018: // 0xFX18 Sets the sound timer to VX. | LD ST, Vx + case 0x001E: // 0xFX1E Adds VX to I. | ADD I, Vx + case 0x0029: // 0xFX29 Sets I to the location of the sprite for the character in VX. | LD F, Vx + // 0xFX33 Stores the Binary-coded decimal representation of VX, + // with the most significant of three digits at the address in I, + // the middle digit at I plus 1, and the least significant digit at I plus 2. + case 0x0033: // 0xFX33 ??? FIXME? | LD B, Vx + case 0x0055: // 0xFX55 Stores V0 to VX in memory starting at address I. | LD [I], Vx + case 0x0065: // 0xFX65 Fills V0 to VX with values from memory starting at address I. | LD Vx, [I] + default: // unhandled for some reason + writefln("unknown opcode: 0x%x", opcode); + break; + } + break; + default: + writefln("unknown opcode: 0x%x", opcode); + } + } + + return ""; + + } // dissassemble + +} // Assembler \ No newline at end of file diff --git a/source/gl.d b/source/gl.d new file mode 100644 index 0000000..7c35e0a --- /dev/null +++ b/source/gl.d @@ -0,0 +1,344 @@ +module gl; + +import glad.gl.all; +import core.stdc.stdio; + +bool checkShaderError(GLuint shader, GLuint flag, bool is_program, in char[] shader_path) nothrow { + + GLint result; + + (is_program) ? glGetProgramiv(shader, flag, &result) + : glGetShaderiv(shader, flag, &result); + + if (result == GL_FALSE) { + + GLchar[256] log; //FIXME this is potentially fatal + (is_program) ? glGetProgramInfoLog(shader, log.sizeof, null, log.ptr) + : glGetShaderInfoLog(shader, log.sizeof, null, log.ptr); + + printf("[OpenGL] Error in %s: %s\n", shader_path.ptr, log.ptr); + return false; + + } + + return true; + +} // checkShaderError + +struct PixelBuffer { + + struct Shader { + + private { + GLuint shader_prog; + } + + @disable this(this); + + void compile(const (GLchar*)* vs_source, const (GLchar*)* fs_source) { + + import core.stdc.stdlib : exit; + + GLuint new_vs_shader = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(new_vs_shader, 1, vs_source, null); + glCompileShader(new_vs_shader); + if (!checkShaderError(new_vs_shader, GL_COMPILE_STATUS, false, "vertex_shader")) { + exit(-1); + } + + GLuint new_fs_shader = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(new_fs_shader, 1, fs_source, null); + glCompileShader(new_fs_shader); + if (!checkShaderError(new_fs_shader, GL_COMPILE_STATUS, false, "fragment_shader")) { + exit(-1); + } + + GLuint new_shader = glCreateProgram(); + glAttachShader(new_shader, new_vs_shader); + glAttachShader(new_shader, new_fs_shader); + + // glBindAttribLocation(new_shader, 0, "screen_size"); + + glLinkProgram(new_shader); + if (!checkShaderError(new_shader, GL_LINK_STATUS, true, "shader_program")) { + exit(-1); + } + + glValidateProgram(new_shader); + if (!checkShaderError(new_shader, GL_VALIDATE_STATUS, true, "shader_program")) { + exit(-1); + } + + shader_prog = new_shader; + + } // compile + + void setUniforms(int w, int h) { + auto attr_loc = glGetUniformLocation(shader_prog, "screen_size"); + glUniform2f(attr_loc, cast(float)w, cast(float)h); + } // setUniforms + + void bind() { + glUseProgram(shader_prog); + } // bind + + void unbind() { + glUseProgram(0); + } // unbind + + } // Shader + + struct Texture { + + import derelict.sdl2.sdl; + import derelict.sdl2.image; + + private { + + GLuint texture_; //OpenGL handle for texture + GLenum input_format_, output_format_, data_type_; + int width_, height_; + + } + + @property @nogc nothrow { + + int width() const { return width_; } + int height() const { return height_; } + GLuint handle() { return texture_; } + + } + + @disable this(this); + + nothrow @nogc + this(int width, int height, GLenum input_format, GLenum output_format, GLenum unpack_alignment) { + + width_ = width; + height_ = height; + input_format_ = input_format; + output_format_ = output_format; + data_type_ = GL_UNSIGNED_BYTE; + + glGenTextures(1, &texture_); + glBindTexture(GL_TEXTURE_2D, texture_); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glPixelStorei(GL_UNPACK_ALIGNMENT, unpack_alignment); + glTexImage2D(GL_TEXTURE_2D, 0, input_format_, width_, height_, 0, output_format_, GL_UNSIGNED_BYTE, cast(void*)0); + + glBindTexture(GL_TEXTURE_2D, 0); + + } //this + + nothrow @nogc + void create(void* pixels, int width, int height, GLenum input_format = GL_RGB, GLenum output_format = GL_RGB, GLenum data_type = GL_UNSIGNED_BYTE) { + + width_ = width; + height_ = height; + input_format_ = input_format; + output_format_ = output_format; + data_type_ = data_type; + + //generate single texture, put handle in texture + glGenTextures(1, &texture_); + + //normal 2d texture, bind to our texture handle + glBindTexture(GL_TEXTURE_2D, texture_); + + //set texture parameters in currently bound texture, controls texture wrapping (or GL_CLAMP?) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + //linearly interpolate between pixels, MIN if texture is too small for drawing area, MAG if drawing area is smaller than texture + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + //texture type, level, format to store as, width, height, border, format loaded in + glTexImage2D(GL_TEXTURE_2D, 0, input_format_, width_, height_, 0, output_format_, data_type_, pixels); + + //UNBIND + glBindTexture(GL_TEXTURE_2D, 0); + + } // this + + ~this() nothrow @nogc { + + if (texture_ != 0) { + glDeleteTextures(1, &texture_); + } + + } // ~this + + /** + * Binds the texture handle, takes an argument for which texture unit to use. + */ + nothrow @nogc + void bind(int unit) { + + assert(unit >= 0 && unit <= 31); + glActiveTexture(GL_TEXTURE0 + unit); //since this is sequential, this works + glBindTexture(GL_TEXTURE_2D, texture_); + + } // bind + + nothrow @nogc + void unbind() { + + glBindTexture(GL_TEXTURE_2D, 0); + + } // unbind + + /** + * Updates the texture in place given the new texture buffer. + * Takes an optional offset to update only a part of the texture. + **/ + nothrow @nogc + void update(void[] pixels, size_t offset = 0) { + + bind(0); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width_, height_, input_format_, data_type_, pixels.ptr); + unbind(); + + } // update + + } // Texture + + /** + * Generic VertexArray structure, used to upload data of any given vertex type to the GPU. + */ + struct VertexArray { + + private { + + GLuint vao_; + GLuint vbo_; + GLenum type_; // type of vertex data, GL_TRIANGLES etc + uint num_vertices_; + + } + + @disable this(this); + + nothrow @nogc + void create(in float[2][] vertices, GLenum draw_type = GL_STATIC_DRAW, GLenum primitive = GL_TRIANGLES) { + + this.num_vertices_ = cast(uint)vertices.length; + this.type_ = primitive; + + glGenVertexArrays(1, &vao_); + glBindVertexArray(vao_); + + glGenBuffers(1, &vbo_); + glBindBuffer(GL_ARRAY_BUFFER, vbo_); + glBufferData(GL_ARRAY_BUFFER, vertices.length * vertices[0].sizeof, vertices.ptr, draw_type); + + // pos + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, + 2, + GL_FLOAT, + GL_FALSE, + float.sizeof * 2, + null + ); + + glBindVertexArray(0); + + } // this + + ~this() nothrow @nogc { + if (vao_ != 0) { + glDeleteVertexArrays(1, &vao_); + } + } // ~this + + void bind() nothrow @nogc { + glBindVertexArray(vao_); + } // bind + + void draw() nothrow @nogc { + glDrawArrays(type_, 0, num_vertices_); + } // draw + + void unbind() nothrow @nogc { + glBindVertexArray(0); + } // unbind + + } // VertexArray + + const char* vs_shader = q{ + #version 330 core + + uniform vec2 screen_size; + + layout(location = 0) in vec2 pos; + + out vec2 frag_uv; + + void main() { + gl_Position = vec4(pos, 0.0, 1.0); + frag_uv = clamp(pos, vec2(0.0, 0.0), vec2(1.0, 1.0)); + } + + }; + + const char* fs_shader = q{ + #version 330 core + + uniform sampler2D tex; + + in vec2 frag_uv; + + out vec4 out_col; + + void main() { + float v = texture(tex, frag_uv.st).r * 255; + out_col = vec4(v, v, v, 1.0); + } + + }; + + Shader shader; + VertexArray vao; + Texture tex; + + void create(void* pixels, int w, int h) { + + float[2][6] rect = [ + [-1.0f, -1.0f], // top left + [1.0f, -1.0f], // top right + [1.0f, 1.0f], // bottom right + + [-1.0f, -1.0f], // top left + [-1.0f, 1.0f], // bottom left + [1.0f, 1.0f], // bottom right + ]; + + vao.create(rect[]); + tex.create(pixels, w, h, GL_RED, GL_RED); + shader.compile(&vs_shader, &fs_shader); + + } // create + + void draw(int screen_w, int screen_h) { + + vao.bind(); + shader.bind(); + shader.setUniforms(screen_w, screen_h); + tex.bind(0); + vao.draw(); + + } // draw + + nothrow @nogc + void update(void[] pixels, size_t offset = 0) { + tex.update(pixels, offset); + } // update + +} // PixelBuffer diff --git a/source/imgui.d b/source/imgui.d index 973daca..2256029 100644 --- a/source/imgui.d +++ b/source/imgui.d @@ -23,11 +23,11 @@ auto bindDelegate(T, string file = __FILE__, size_t line = __LINE__)(T t) if(isD struct Imgui { // OpenGL data, cleanup soon - GLuint g_FontTexture = 0; - int g_ShaderHandle = 0, g_VertHandle = 0, g_FragHandle = 0; - int g_AttribLocationTex = 0, g_AttribLocationProjMtx = 0; - int g_AttribLocationPosition = 0, g_AttribLocationUV = 0, g_AttribLocationColor = 0; - uint g_VboHandle = 0, g_VaoHandle = 0, g_ElementsHandle = 0; + GLuint font_texture_ = 0; + int shander_handle_ = 0, vs_handle_ = 0, fs_handle_ = 0; + int shader_attrib_loc_tex_ = 0, shader_attrib_loc_projmtx_ = 0; + int shader_attrib_loc_pos_ = 0, shader_attrib_loc_uv_ = 0, shader_attrib_loc_color_ = 0; + uint vao_ = 0, vbo_ = 0, ebo_ = 0; // imgui input related state bool[3] mouse_buttons_pressed; @@ -74,14 +74,14 @@ struct Imgui { // upload texture to graphics system GLint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); - glGenTextures(1, &g_FontTexture); - glBindTexture(GL_TEXTURE_2D, g_FontTexture); + glGenTextures(1, &font_texture_); + glBindTexture(GL_TEXTURE_2D, font_texture_); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); // store our identifier - ImFontAtlas_SetTexID(io.Fonts, cast(void*)g_FontTexture); + ImFontAtlas_SetTexID(io.Fonts, cast(void*)font_texture_); // restore state glBindTexture(GL_TEXTURE_2D, last_texture); @@ -122,36 +122,36 @@ struct Imgui { } }; - g_ShaderHandle = glCreateProgram(); - g_VertHandle = glCreateShader(GL_VERTEX_SHADER); - g_FragHandle = glCreateShader(GL_FRAGMENT_SHADER); - glShaderSource(g_VertHandle, 1, &vertex_shader, null); - glShaderSource(g_FragHandle, 1, &fragment_shader, null); - glCompileShader(g_VertHandle); - glCompileShader(g_FragHandle); - glAttachShader(g_ShaderHandle, g_VertHandle); - glAttachShader(g_ShaderHandle, g_FragHandle); - glLinkProgram(g_ShaderHandle); + shander_handle_ = glCreateProgram(); + vs_handle_ = glCreateShader(GL_VERTEX_SHADER); + fs_handle_ = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(vs_handle_, 1, &vertex_shader, null); + glShaderSource(fs_handle_, 1, &fragment_shader, null); + glCompileShader(vs_handle_); + glCompileShader(fs_handle_); + glAttachShader(shander_handle_, vs_handle_); + glAttachShader(shander_handle_, fs_handle_); + glLinkProgram(shander_handle_); - g_AttribLocationTex = glGetUniformLocation(g_ShaderHandle, "Texture"); - g_AttribLocationProjMtx = glGetUniformLocation(g_ShaderHandle, "ProjMtx"); - g_AttribLocationPosition = glGetAttribLocation(g_ShaderHandle, "Position"); - g_AttribLocationUV = glGetAttribLocation(g_ShaderHandle, "UV"); - g_AttribLocationColor = glGetAttribLocation(g_ShaderHandle, "Color"); + shader_attrib_loc_tex_ = glGetUniformLocation(shander_handle_, "Texture"); + shader_attrib_loc_projmtx_ = glGetUniformLocation(shander_handle_, "ProjMtx"); + shader_attrib_loc_pos_ = glGetAttribLocation(shander_handle_, "Position"); + shader_attrib_loc_uv_ = glGetAttribLocation(shander_handle_, "UV"); + shader_attrib_loc_color_ = glGetAttribLocation(shander_handle_, "Color"); - glGenBuffers(1, &g_VboHandle); - glGenBuffers(1, &g_ElementsHandle); + glGenBuffers(1, &vbo_); + glGenBuffers(1, &ebo_); - glGenVertexArrays(1, &g_VaoHandle); - glBindVertexArray(g_VaoHandle); - glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle); - glEnableVertexAttribArray(g_AttribLocationPosition); - glEnableVertexAttribArray(g_AttribLocationUV); - glEnableVertexAttribArray(g_AttribLocationColor); + glGenVertexArrays(1, &vao_); + glBindVertexArray(vao_); + glBindBuffer(GL_ARRAY_BUFFER, vbo_); + glEnableVertexAttribArray(shader_attrib_loc_pos_); + glEnableVertexAttribArray(shader_attrib_loc_uv_); + glEnableVertexAttribArray(shader_attrib_loc_color_); - glVertexAttribPointer(g_AttribLocationPosition, 2, GL_FLOAT, GL_FALSE, ImDrawVert.sizeof, cast(void*)ImDrawVert.pos.offsetof); - glVertexAttribPointer(g_AttribLocationUV, 2, GL_FLOAT, GL_FALSE, ImDrawVert.sizeof, cast(void*)ImDrawVert.uv.offsetof); - glVertexAttribPointer(g_AttribLocationColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, ImDrawVert.sizeof, cast(void*)ImDrawVert.col.offsetof); + glVertexAttribPointer(shader_attrib_loc_pos_, 2, GL_FLOAT, GL_FALSE, ImDrawVert.sizeof, cast(void*)ImDrawVert.pos.offsetof); + glVertexAttribPointer(shader_attrib_loc_uv_, 2, GL_FLOAT, GL_FALSE, ImDrawVert.sizeof, cast(void*)ImDrawVert.uv.offsetof); + glVertexAttribPointer(shader_attrib_loc_color_, 4, GL_UNSIGNED_BYTE, GL_TRUE, ImDrawVert.sizeof, cast(void*)ImDrawVert.col.offsetof); createFontTexture(); @@ -162,6 +162,82 @@ struct Imgui { } + struct GLState { + GLenum last_active_texture; + GLint last_program; + GLint last_texture; + GLint last_array_buffer; + GLint last_element_array_buffer; + GLint last_vertex_array; + GLint[4] last_viewport; + GLint[4] last_scissor_box; + GLenum last_blend_src_rgb; + GLenum last_blend_dst_rgb; + GLenum last_blend_src_alpha; + GLenum last_blend_dst_alpha; + GLenum last_blend_equation_rgb; + GLenum last_blend_equation_alpha; + GLboolean last_enable_blend; + GLboolean last_enable_cull_face; + GLboolean last_enable_depth_test; + GLboolean last_enable_scissor_test; + } + + nothrow @nogc + GLState backupGLState() { + + GLState gl_state; + + // backup GL state + with (gl_state) { + glGetIntegerv(GL_ACTIVE_TEXTURE, cast(GLint*)&last_active_texture); + glActiveTexture(GL_TEXTURE0); + glGetIntegerv(GL_CURRENT_PROGRAM, &last_program); + glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); + glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer); + glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &last_element_array_buffer); + glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array); + glGetIntegerv(GL_VIEWPORT, last_viewport.ptr); + glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box.ptr); + glGetIntegerv(GL_BLEND_SRC_RGB, cast(GLint*)&last_blend_src_rgb); + glGetIntegerv(GL_BLEND_DST_RGB, cast(GLint*)&last_blend_dst_rgb); + glGetIntegerv(GL_BLEND_SRC_ALPHA, cast(GLint*)&last_blend_src_alpha); + glGetIntegerv(GL_BLEND_DST_ALPHA, cast(GLint*)&last_blend_dst_alpha); + glGetIntegerv(GL_BLEND_EQUATION_RGB, cast(GLint*)&last_blend_equation_rgb); + glGetIntegerv(GL_BLEND_EQUATION_ALPHA, cast(GLint*)&last_blend_equation_alpha); + last_enable_blend = glIsEnabled(GL_BLEND); + last_enable_cull_face = glIsEnabled(GL_CULL_FACE); + last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST); + last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST); + } + + return gl_state; + + } + + nothrow @nogc + void restoreGLState(GLState state) { + + // restore modified GL state + with (state) { + glUseProgram(last_program); + glBindTexture(GL_TEXTURE_2D, last_texture); + glActiveTexture(last_active_texture); + glBindVertexArray(last_vertex_array); + glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, last_element_array_buffer); + glBlendEquationSeparate(last_blend_equation_rgb, last_blend_equation_alpha); + glBlendFuncSeparate(last_blend_src_rgb, last_blend_dst_rgb, last_blend_src_alpha, last_blend_dst_alpha); + if (last_enable_blend) glEnable(GL_BLEND); else glDisable(GL_BLEND); + if (last_enable_cull_face) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE); + if (last_enable_depth_test) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST); + if (last_enable_scissor_test) glEnable(GL_SCISSOR_TEST); else glDisable(GL_SCISSOR_TEST); + glViewport(last_viewport[0], last_viewport[1], cast(GLsizei)last_viewport[2], cast(GLsizei)last_viewport[3]); + glScissor(last_scissor_box[0], last_scissor_box[1], cast(GLsizei)last_scissor_box[2], cast(GLsizei)last_scissor_box[3]); + } + + } + extern(C) nothrow void renderDrawLists(ImDrawData* draw_data) { @@ -173,25 +249,7 @@ struct Imgui { draw_data.ScaleClipRects(io.DisplayFramebufferScale); // backup GL state - GLenum last_active_texture; glGetIntegerv(GL_ACTIVE_TEXTURE, cast(GLint*)&last_active_texture); - glActiveTexture(GL_TEXTURE0); - GLint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, &last_program); - GLint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); - GLint last_array_buffer; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer); - GLint last_element_array_buffer; glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &last_element_array_buffer); - GLint last_vertex_array; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array); - GLint[4] last_viewport; glGetIntegerv(GL_VIEWPORT, last_viewport.ptr); - GLint[4] last_scissor_box; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box.ptr); - GLenum last_blend_src_rgb; glGetIntegerv(GL_BLEND_SRC_RGB, cast(GLint*)&last_blend_src_rgb); - GLenum last_blend_dst_rgb; glGetIntegerv(GL_BLEND_DST_RGB, cast(GLint*)&last_blend_dst_rgb); - GLenum last_blend_src_alpha; glGetIntegerv(GL_BLEND_SRC_ALPHA, cast(GLint*)&last_blend_src_alpha); - GLenum last_blend_dst_alpha; glGetIntegerv(GL_BLEND_DST_ALPHA, cast(GLint*)&last_blend_dst_alpha); - GLenum last_blend_equation_rgb; glGetIntegerv(GL_BLEND_EQUATION_RGB, cast(GLint*)&last_blend_equation_rgb); - GLenum last_blend_equation_alpha; glGetIntegerv(GL_BLEND_EQUATION_ALPHA, cast(GLint*)&last_blend_equation_alpha); - GLboolean last_enable_blend = glIsEnabled(GL_BLEND); - GLboolean last_enable_cull_face = glIsEnabled(GL_CULL_FACE); - GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST); - GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST); + GLState last_state = backupGLState(); // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled glEnable(GL_BLEND); @@ -210,10 +268,10 @@ struct Imgui { [-1.0f, 1.0f, 0.0f, 1.0f ], ]; - glUseProgram(g_ShaderHandle); - glUniform1i(g_AttribLocationTex, 0); - glUniformMatrix4fv(g_AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]); - glBindVertexArray(g_VaoHandle); + glUseProgram(shander_handle_); + glUniform1i(shader_attrib_loc_tex_, 0); + glUniformMatrix4fv(shader_attrib_loc_projmtx_, 1, GL_FALSE, &ortho_projection[0][0]); + glBindVertexArray(vao_); foreach (int n; 0 .. draw_data.CmdListsCount) { ImDrawList* cmd_list = draw_data.CmdLists[n]; @@ -222,10 +280,10 @@ struct Imgui { auto countVertices = ImDrawList_GetVertexBufferSize(cmd_list); auto countIndices = ImDrawList_GetIndexBufferSize(cmd_list); - glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle); + glBindBuffer(GL_ARRAY_BUFFER, vbo_); glBufferData(GL_ARRAY_BUFFER, countVertices * ImDrawVert.sizeof, cast(GLvoid*)ImDrawList_GetVertexPtr(cmd_list,0), GL_STREAM_DRAW); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_ElementsHandle); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo_); glBufferData(GL_ELEMENT_ARRAY_BUFFER, countIndices * ImDrawIdx.sizeof, cast(GLvoid*)ImDrawList_GetIndexPtr(cmd_list,0), GL_STREAM_DRAW); auto cmdCnt = ImDrawList_GetCmdSize(cmd_list); @@ -244,21 +302,8 @@ struct Imgui { } } - // Restore modified GL state - glUseProgram(last_program); - glBindTexture(GL_TEXTURE_2D, last_texture); - glActiveTexture(last_active_texture); - glBindVertexArray(last_vertex_array); - glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, last_element_array_buffer); - glBlendEquationSeparate(last_blend_equation_rgb, last_blend_equation_alpha); - glBlendFuncSeparate(last_blend_src_rgb, last_blend_dst_rgb, last_blend_src_alpha, last_blend_dst_alpha); - if (last_enable_blend) glEnable(GL_BLEND); else glDisable(GL_BLEND); - if (last_enable_cull_face) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE); - if (last_enable_depth_test) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST); - if (last_enable_scissor_test) glEnable(GL_SCISSOR_TEST); else glDisable(GL_SCISSOR_TEST); - glViewport(last_viewport[0], last_viewport[1], cast(GLsizei)last_viewport[2], cast(GLsizei)last_viewport[3]); - glScissor(last_scissor_box[0], last_scissor_box[1], cast(GLsizei)last_scissor_box[2], cast(GLsizei)last_scissor_box[3]); + // restore old state + restoreGLState(last_state); } diff --git a/source/window.d b/source/window.d index e622dd2..1f554f9 100644 --- a/source/window.d +++ b/source/window.d @@ -7,7 +7,8 @@ import glad.gl.all; * Converts an integer representing a colour, for example 0x428bca into a 4 element * int array for passing to OpenGL. */ -GLfloat[4] to(T : GLfloat[4])(int color, ubyte alpha = 255) nothrow @nogc pure { +nothrow @nogc pure +GLfloat[4] to(T : GLfloat[4])(int color, ubyte alpha = 255) { GLfloat[4] gl_color = [ //mask out r, g, b components from int cast(float)cast(ubyte)(color>>16)/255, @@ -73,8 +74,7 @@ struct Window { return; } - import glad.gl.loader; - import std.functional; + import glad.gl.loader : gladLoadGL; // Check OpenGL properties printf("OpenGL loaded\n");