add stat of basic dissassemble

This commit is contained in:
Robin Hübner 2018-10-04 00:36:40 +02:00
parent a35c316800
commit e09f663b9b
6 changed files with 719 additions and 649 deletions

4
dscanner.ini Normal file
View File

@ -0,0 +1,4 @@
[analysis.config.StaticAnalysisConfig]
undocumented_declaration_check="disabled"
long_line_check="disabled"

View File

@ -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);

223
source/assembler.d Normal file
View File

@ -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

344
source/gl.d Normal file
View File

@ -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

View File

@ -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);
}

View File

@ -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");