standalone_srgb_antialiasing/src/standalone_srgb_antialiasin...

2122 lines
62 KiB
C

#include <Windows.h>
#include <math.h>
////////////////////////////////
//- Experiment Configuration
// When this is 0 colors in vertex attributes and uniforms
// that are converted (sRGB -> Linear) are converted using
// the approximate (sRGB -> Linear) (f(x)=x^2.2); When this
// is 1 those colors are converted using an accurate
// (sRGB -> Linear) conversion.
// This has no effect on colors sampled from sRGB textures
// which are not converted by shader code, but by the OpenGL
// implementation itself. By default this is zero to show the
// difference between the approximate and accurate
// conversions in test #3.
#define USE_ACCURATE_CONVERSIONS_IN_SHADER 0
////////////////////////////////
//- Mini-Base
#include <stdint.h>
typedef int8_t S8;
typedef int16_t S16;
typedef int32_t S32;
typedef int64_t S64;
typedef uint8_t U8;
typedef uint16_t U16;
typedef uint32_t U32;
typedef uint64_t U64;
typedef S8 B8;
typedef S16 B16;
typedef S32 B32;
typedef S64 B64;
typedef float F32;
typedef double F64;
#define AssertBreak() (*(volatile int*)0 = 0)
#define Assert(c) do{ if (!(c)){ AssertBreak(); } }while(0)
#define U64FromPtr(ptr) (U64)(ptr)
#define PtrFromU64(ptr) (void*)(x)
#define ArrayCount(a) ((sizeof(a))/(sizeof(*a)))
#define Member(T,m) ((T*)0)->m
#define PtrOffsetOf(T,m) (void*)(&Member(T,m))
#define OffsetOf(T,m) U64FromPtr(&Member(T,m))
#define Clamp(a,x,b) (((x)<(a))?(a):((b)<(x))?(b):(x))
#define allocate_array(T,c) (T*)(malloc(sizeof(T)*(c)))
////////////////////////////////
//- Types, Functions & Globals
static void error_message(char *msg);
//- wgl stuff
static HGLRC graphics_context = 0;
static HWND graphics_window = 0;
static HDC graphics_dc = 0;
static F32 graphics_w = 0;
static F32 graphics_h = 0;
static void wgl_helper_make_graphics_window(HINSTANCE hInstance);
static void wgl_helper_begin_render(void);
static void wgl_helper_end_render(void);
//- opengl render stuff
typedef struct Vertex{
F32 px;
F32 py;
F32 uvx;
F32 uvy;
F32 cr;
F32 cg;
F32 cb;
F32 ca;
F32 rx0;
F32 ry0;
F32 rx1;
F32 ry1;
} Vertex;
static void opengl_render_init(void);
static void opengl_draw_basic_geometry(Vertex *v, U64 count);
static void opengl_draw_srgb_in_geometry(Vertex *v, U64 count);
static void opengl_draw_rect_geometry(Vertex *v, U64 count);
static void opengl_draw_texture_geometry(Vertex *v, U64 count, U32 texture);
static void opengl_draw_texrgb_geometry(Vertex *v, U64 count, U32 texture,
F32 r, F32 g, F32 b, F32 a);
////////////////////////////////
//- Circle Generator
static F32
integral_indef(F32 r, F32 x){
Assert(fabs(x) <= fabs(r));
F32 t = asinf(x/r);
F32 result = r*r*0.5f*(t + 0.5f*sinf(2*t));
return(result);
}
static F32
integral_def(F32 r, F32 min_x, F32 max_x){
F32 max_v = integral_indef(r, max_x);
F32 min_v = integral_indef(r, min_x);
F32 result = max_v - min_v;
return(result);
}
static B32
approx_equal(F32 a, F32 b){
B32 result = (a - 0.0078125 <= b && b <= a + 0.0078125);
return(result);
}
static void
sanity_test_integrals(void){
{
F32 a = integral_def(1.f, -1.f, 1.f);
Assert(approx_equal(a, 3.141593f*0.5f));
}
{
F32 a = integral_def(2.f, -2.f, 2.f);
Assert(approx_equal(a, 3.141593f*2.f));
}
{
F32 a = integral_def(2.f, 0.f, 2.f);
Assert(approx_equal(a, 3.141593f));
}
{
F32 a0 = integral_def(3.f, 0.f, 1.f);
F32 a1 = integral_def(3.f, 1.f, 2.f);
F32 a2 = integral_def(3.f, 2.f, 3.f);
Assert(approx_equal(a0 + a1 + a2, 3.141593f*9.f/4.f));
}
}
static F32
intersection_area(F32 cir_r, F32 unit_box_x0, F32 unit_box_y0){
F32 rr = cir_r*cir_r;
F32 rel_y0 = unit_box_y0;
F32 rel_y1 = unit_box_y0 + 1.f;
F32 rel_x0 = unit_box_x0;
F32 rel_x1 = unit_box_x0 + 1.f;
F32 y0y0 = rel_y0*rel_y0;
F32 y1y1 = rel_y1*rel_y1;
F32 x0x0 = rel_x0*rel_x0;
F32 x1x1 = rel_x1*rel_x1;
// check each corner for hitting the circle
B32 corner_bl = ((y0y0 + x0x0) <= rr);
B32 corner_tl = ((y1y1 + x0x0) <= rr);
B32 corner_br = ((y0y0 + x1x1) <= rr);
B32 corner_tr = ((y1y1 + x1x1) <= rr);
// calculate area
F32 a = 0.f;
#define COMB(a,b,c,d) ((a)|((b)<<1)|((c)<<2)|((d)<<3))
switch (COMB(corner_bl, corner_tl, corner_br, corner_tr)){
// full miss
case 0x0: a = 0.f; break;
// full hit
case 0xF: a = 1.f; break;
// 1 corner hit
case COMB(1,0,0,0): case COMB(0,1,0,0):
case COMB(0,0,1,0): case COMB(0,0,0,1):
{
F32 intersection_y = 0.f;
if (corner_bl || corner_br){
intersection_y = rel_y0;
}
else{
intersection_y = rel_y1;
}
F32 intersection_x = sqrtf(rr - intersection_y*intersection_y);
if (intersection_x < rel_x0 || rel_x1 < intersection_x){
intersection_x = -intersection_x;
}
F32 x_min = 0.f;
F32 x_max = 0.f;
if (corner_bl || corner_tl){
x_min = rel_x0;
x_max = intersection_x;
}
else{
x_min = intersection_x;
x_max = rel_x1;
}
F32 h = fabs(intersection_y);
a = integral_def(cir_r, x_min, x_max) - h*(x_max - x_min);
}break;
// 2 corner hits (horizontal edge)
case COMB(1,0,1,0): case COMB(0,1,0,1):
{
F32 h = 0.f;
if (corner_bl){
h = rel_y0;
}
else{
h = -rel_y1;
}
a = integral_def(cir_r, rel_x0, rel_x1) - h;
}break;
// 2 corner hits (vertical edge)
case COMB(1,1,0,0): case COMB(0,0,1,1):
{
F32 w = 0.f;
if (corner_bl){
w = rel_x0;
}
else{
w = -rel_x1;
}
a = integral_def(cir_r, rel_y0, rel_y1) - w;
}break;
// 3 corner hits
case COMB(0,1,1,1): case COMB(1,0,1,1):
case COMB(1,1,0,1): case COMB(1,1,1,0):
{
F32 intersection_y = 0.f;
if (!corner_bl || !corner_br){
intersection_y = rel_y0;
}
else{
intersection_y = rel_y1;
}
F32 intersection_x = sqrtf(rr - intersection_y*intersection_y);
if (intersection_x < rel_x0 || rel_x1 < intersection_x){
intersection_x = -intersection_x;
}
F32 w_extra = 0.f;
F32 x_min = 0.f;
F32 x_max = 0.f;
if (!corner_bl || !corner_tl){
x_min = rel_x0;
x_max = intersection_x;
w_extra = rel_x1 - intersection_x;
}
else{
x_min = intersection_x;
x_max = rel_x1;
w_extra = intersection_x - rel_x0;
}
F32 h = fabs(intersection_y) - 1.f;
a = integral_def(cir_r, x_min, x_max) - h*(x_max - x_min) + w_extra;
}break;
// these cases should be impossible
default:
{
a = -1.f;
}break;
}
#undef COMB
return(a);
}
static void
sanity_test_circle_area(void){
{
F32 cr = 5.f;
F32 cx = 5.f;
F32 cy = 5.f;
F32 sum_area = 0.f;
for (U32 y = 0; y < 10; y += 1){
F32 box_y0 = (F32)y;
for (U32 x = 0; x < 10; x += 1){
F32 box_x0 = (F32)x;
F32 a = intersection_area(cr, box_x0 - cx, box_y0 - cy);
Assert(0.f <= a && a <= 1.f);
sum_area += a;
}
}
F32 formula_area = 3.141593f*cr*cr;
Assert(approx_equal(sum_area, formula_area));
}
{
F32 cr = 8.f;
F32 cx = 8.1f;
F32 cy = 8.7f;
F32 sum_area = 0.f;
for (U32 y = 0; y < 20; y += 1){
F32 box_y0 = (F32)y;
for (U32 x = 0; x < 20; x += 1){
F32 box_x0 = (F32)x;
F32 a = intersection_area(cr, box_x0 - cx, box_y0 - cy);
Assert(0.f <= a && a <= 1.f);
sum_area += a;
}
}
F32 formula_area = 3.141593f*cr*cr;
Assert(approx_equal(sum_area, formula_area));
}
}
static void
render_circle_in_buffer(F32 cir_r, F32 cir_x, F32 cir_y,
U8 *buf, U32 buf_side_length){
for (U32 y = 0; y < buf_side_length; y += 1){
F32 box_y0 = (F32)y;
for (U32 x = 0; x < buf_side_length; x += 1){
F32 box_x0 = (F32)x;
// calculate area of the intersection (circle & box)
F32 a = intersection_area(cir_r, box_x0 - cir_x, box_y0 - cir_y);
Assert(0.f <= a && a <= 1.f);
U32 vraw = (U32)(256.f*a);
U8 v = (vraw <= 255)?((U8)vraw):(U8)255;
buf[x + buf_side_length*y] = v;
}
}
}
////////////////////////////////
//- GL Definitions
//- gl types
typedef U32 GLenum;
typedef F32 GLfloat;
typedef U32 GLbitfield;
typedef U32 GLuint;
typedef S32 GLint;
typedef S32 GLsizei;
typedef S64 GLsizeiptr;
typedef S64 GLintptr;
typedef char GLchar;
typedef unsigned char GLboolean;
//- gl constants
#define GL_COLOR_BUFFER_BIT 0x00004000
#define GL_TRIANGLES 0x0004
#define GL_SRC_ALPHA 0x0302
#define GL_ONE_MINUS_SRC_ALPHA 0x0303
#define GL_BLEND 0x0BE2
#define GL_TEXTURE_2D 0x0DE1
#define GL_UNSIGNED_BYTE 0x1401
#define GL_FLOAT 0x1406
#define GL_DEPTH_COMPONENT 0x1902
#define GL_RED 0x1903
#define GL_RGB 0x1907
#define GL_RGBA 0x1908
#define GL_NEAREST 0x2600
#define GL_LINEAR 0x2601
#define GL_TEXTURE_MAG_FILTER 0x2800
#define GL_TEXTURE_MIN_FILTER 0x2801
#define GL_TEXTURE_WRAP_S 0x2802
#define GL_TEXTURE_WRAP_T 0x2803
#define GL_MULTISAMPLE 0x809D
#define GL_CLAMP_TO_EDGE 0x812F
#define GL_TEXTURE0 0x84C0
#define GL_STREAM_DRAW 0x88E0
#define GL_ARRAY_BUFFER 0x8892
#define GL_FRAGMENT_SHADER 0x8B30
#define GL_VERTEX_SHADER 0x8B31
#define GL_COMPILE_STATUS 0x8B81
#define GL_LINK_STATUS 0x8B82
#define GL_INFO_LOG_LENGTH 0x8B84
#define GL_SRGB 0x8C40
#define GL_SRGB8 0x8C41
#define GL_READ_FRAMEBUFFER 0x8CA8
#define GL_DRAW_FRAMEBUFFER 0x8CA9
#define GL_COLOR_ATTACHMENT0 0x8CE0
#define GL_FRAMEBUFFER 0x8D40
#define GL_FRAMEBUFFER_SRGB 0x8DB9
#define GL_TEXTURE_2D_MULTISAMPLE 0x9100
//- gl func x-list
#define GL_FUNC_X_LIST() \
/* common stuff */ \
X(glGetError, GLenum, (void)) \
X(glViewport, void, (GLint x, GLint y, GLsizei width, GLsizei height)) \
X(glScissor, void, (GLint x, GLint y, GLsizei width, GLsizei height)) \
X(glClearColor, void, (GLfloat r,GLfloat g,GLfloat b,GLfloat a)) \
X(glClear, void, (GLbitfield mask)) \
X(glBlendFunc, void, (GLenum sfactor, GLenum dfactor)) \
X(glBlendFuncSeparate, void, (GLenum srcRGB,GLenum dstRGB,GLenum srcAlpha,GLenum dstAlpha)) \
X(glBlendEquation, void, (GLenum mode)) \
X(glBlendEquationSeparate, void, (GLenum modeRGB, GLenum modeAlpha)) \
X(glDisable, void, (GLenum cap)) \
X(glEnable, void, (GLenum cap)) \
X(glPixelStorei, void, (GLenum pname, GLint param)) \
X(glReadPixels, void, (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels)) \
/* buffers */ \
X(glGenBuffers, void, (GLsizei n, GLuint *buffers)) \
X(glDeleteBuffers, void, (GLsizei n, const GLuint *buffers)) \
X(glBindBuffer, void, (GLenum target, GLuint buffer)) \
X(glBufferData, void, (GLenum target, GLsizeiptr size, const void *data, GLenum usage)) \
X(glBufferSubData, void, (GLenum target, GLintptr offset, GLsizeiptr size, const void *data)) \
X(glGenVertexArrays, void, (GLsizei n, GLuint *arrays)) \
X(glDeleteVertexArrays, void, (GLsizei n, const GLuint *arrays)) \
X(glBindVertexArray, void, (GLuint array)) \
/* textures */ \
X(glGenTextures, void, (GLsizei n, GLuint *textures)) \
X(glActiveTexture, void, (GLenum texture)) \
X(glDeleteTextures, void, (GLsizei n, const GLuint *textures)) \
X(glBindTexture, void, (GLenum target, GLuint texture)) \
X(glIsTexture, GLboolean, (GLuint texture)) \
X(glTexParameteri, void, (GLenum target, GLenum pname, GLint param)) \
X(glTexImage1D, void, (GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels)) \
X(glTexImage2D, void, (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels)) \
X(glTexSubImage1D, void, (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels)) \
X(glTexSubImage2D, void, (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels)) \
X(glTexImage2DMultisample, void, (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations)) \
/* shaders */ \
X(glAttachShader, void, (GLuint program, GLuint shader)) \
X(glCompileShader, void, (GLuint shader)) \
X(glCreateProgram, GLuint, (void)) \
X(glCreateShader, GLuint, (GLenum type)) \
X(glDeleteProgram, void, (GLuint program)) \
X(glDeleteShader, void, (GLuint shader)) \
X(glGetProgramiv, void, (GLuint program, GLenum pname, GLint *params)) \
X(glGetProgramInfoLog, void, (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog)) \
X(glGetShaderiv, void, (GLuint shader, GLenum pname, GLint *params)) \
X(glGetShaderInfoLog, void, (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog)) \
X(glLinkProgram, void, (GLuint program)) \
X(glShaderSource, void, (GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length)) \
X(glUseProgram, void, (GLuint program)) \
X(glGetUniformLocation, GLint, (GLuint program, const GLchar *name)) \
/* draw calls */ \
X(glDrawArrays, void, (GLenum mode, GLint first, GLsizei count)) \
X(glDrawElements, void, (GLenum mode, GLsizei count, GLenum type, const void *indices)) \
X(glDrawArraysInstanced, void, (GLenum mode, GLint first, GLsizei count, GLsizei instancecount)) \
X(glDrawElementsInstanced, void, (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount)) \
X(glEnableVertexAttribArray, void, (GLuint index)) \
X(glDisableVertexAttribArray, void, (GLuint index)) \
X(glVertexAttribPointer, void, (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer)) \
X(glVertexAttribIPointer, void, (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer)) \
X(glVertexAttribDivisor, void, (GLuint index, GLuint divisor)) \
X(glUniform1f, void, (GLint location, GLfloat v0)) \
X(glUniform2f, void, (GLint location, GLfloat v0, GLfloat v1)) \
X(glUniform3f, void, (GLint location, GLfloat v0, GLfloat v1, GLfloat v2)) \
X(glUniform4f, void, (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3)) \
X(glUniform1i, void, (GLint location, GLint v0)) \
X(glUniform1fv, void, (GLint location, GLsizei count, const GLfloat *value)) \
X(glUniform2fv, void, (GLint location, GLsizei count, const GLfloat *value)) \
X(glUniform3fv, void, (GLint location, GLsizei count, const GLfloat *value)) \
X(glDrawBuffers, void, (GLsizei n, const GLenum *bufs)) \
/* framebuffers */ \
X(glGenFramebuffers, void, (GLsizei n, GLuint *framebuffers)) \
X(glDeleteFramebuffers, void, (GLsizei n, const GLuint *framebuffers)) \
X(glBindFramebuffer, void, (GLenum target, GLuint framebuffer)) \
X(glIsFramebuffer, GLboolean, (GLuint framebuffer)) \
X(glCheckFramebufferStatus, GLenum, (GLenum target)) \
X(glFramebufferTexture1D, void, (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level)) \
X(glFramebufferTexture2D, void, (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level)) \
X(glFramebufferTexture3D, void, (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset)) \
X(glFramebufferRenderbuffer, void, (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer)) \
X(glFramebufferTexture, void, (GLenum target, GLenum attachment, GLuint texture, GLint level)) \
X(glBlitFramebuffer, void, (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter)) \
X(glGetFramebufferAttachmentParameteriv, void, (GLenum target, GLenum attachment, GLenum pname, GLint *params))
//- gl function types
#define X(N,R,P) typedef R GL_##N P;
GL_FUNC_X_LIST()
#undef X
//- gl function pointers
#define X(N,R,P) static GL_##N * N = 0;
GL_FUNC_X_LIST()
#undef X
////////////////////////////////
//- OpenGL Helper Declarations
static GLuint opengl_helper_make_shader(char *src, GLenum shader_type);
static GLuint opengl_helper_make_program(GLuint *shaders, U32 count);
////////////////////////////////
//- OpenGL Render
static char basic_vshader[] =
"#version 330\n"
"uniform vec2 u_view_xform;\n"
"layout (location = 0) in vec2 v_p;\n"
"layout (location = 1) in vec4 v_c;\n"
"out vec4 f_c;\n"
"void main(){\n"
"vec2 norm_pos = v_p*u_view_xform + vec2(-1.0, -1.0);\n"
"gl_Position = vec4(norm_pos, 0.0, 1.0);\n"
"f_c = v_c;\n"
"}\n"
;
static char basic_fshader[] =
"#version 330\n"
"in vec4 f_c;\n"
"out vec4 out_color;\n"
"void main(){\n"
"out_color = f_c;\n"
"}\n"
;
static GLuint basic_program = 0;
static GLint basic_u_view_xform = -1;
static char srgb_in_vshader[] =
"#version 330\n"
"\n"
#if USE_ACCURATE_CONVERSIONS_IN_SHADER
"float lin_from_srgb(float x){\n"
"float r = 0;\n"
"if (x <= 0.04045) r = x/12.92;\n"
"else r = pow(((x + 0.055)/1.055), 2.4);\n"
"return(r);\n"
"}\n"
"\n"
"float srgb_from_lin(float x){\n"
"float r = 0;\n"
"if (x <= 0.0031308) r = x*12.92;\n"
"else r = pow(x, 1/2.4)*1.055 - 0.055;\n"
"return(r);\n"
"}\n"
#endif
"\n"
"uniform vec2 u_view_xform;\n"
"layout (location = 0) in vec2 v_p;\n"
"layout (location = 1) in vec4 v_c;\n"
"out vec4 f_c;\n"
"void main(){\n"
"vec2 norm_pos = v_p*u_view_xform + vec2(-1.0, -1.0);\n"
#if USE_ACCURATE_CONVERSIONS_IN_SHADER
"float lin_r = lin_from_srgb(v_c.r);\n"
"float lin_g = lin_from_srgb(v_c.g);\n"
"float lin_b = lin_from_srgb(v_c.b);\n"
#else
"float lin_r = pow(v_c.r, 2.2);\n"
"float lin_g = pow(v_c.g, 2.2);\n"
"float lin_b = pow(v_c.b, 2.2);\n"
#endif
"gl_Position = vec4(norm_pos, 0.0, 1.0);\n"
"f_c = vec4(lin_r, lin_g, lin_b, v_c.a);\n"
"}\n"
;
static char srgb_in_fshader[] =
"#version 330\n"
"in vec4 f_c;\n"
"out vec4 out_color;\n"
"void main(){\n"
"out_color = f_c;\n"
"}\n"
;
static GLuint srgb_in_program = 0;
static GLint srgb_in_u_view_xform = -1;
static char rect_vshader[] =
"#version 330\n"
"uniform vec2 u_view_xform;\n"
"layout (location = 0) in vec2 v_p;\n"
"layout (location = 1) in vec4 v_r;\n"
"out vec4 f_r;\n"
"void main(){\n"
"vec2 norm_pos = v_p*u_view_xform + vec2(-1.0, -1.0);\n"
"gl_Position = vec4(norm_pos, 0.0, 1.0);\n"
"f_r = v_r;\n"
"}\n"
;
static char rect_fshader[] =
"#version 330\n"
"in vec4 gl_FragCoord;\n"
"in vec4 f_r;\n"
"out vec4 out_color;\n"
"void main(){\n"
"float pix_x0 = gl_FragCoord.x - 0.5;\n"
"float pix_y0 = gl_FragCoord.y - 0.5;\n"
"float pix_x1 = gl_FragCoord.x + 0.5;\n"
"float pix_y1 = gl_FragCoord.y + 0.5;\n"
"float int_x0 = max(pix_x0, f_r.x);\n"
"float int_y0 = max(pix_y0, f_r.y);\n"
"float int_x1 = min(pix_x1, f_r.z);\n"
"float int_y1 = min(pix_y1, f_r.w);\n"
"float cover = (int_x1 - int_x0)*(int_y1 - int_y0);\n"
"out_color = vec4(1, 1, 1, cover);\n"
"}\n"
;
static GLuint rect_program = 0;
static GLint rect_u_view_xform = -1;
static char texture_vshader[] =
"#version 330\n"
"uniform vec2 u_view_xform;\n"
"layout (location = 0) in vec2 v_p;\n"
"layout (location = 1) in vec2 v_uv;\n"
"out vec2 f_uv;\n"
"void main(){\n"
"vec2 norm_pos = v_p*u_view_xform + vec2(-1.0, -1.0);\n"
"gl_Position = vec4(norm_pos, 0.0, 1.0);\n"
"f_uv = v_uv;\n"
"}\n"
;
static char texture_fshader[] =
"#version 330\n"
"uniform sampler2D u_texture;\n"
"in vec2 f_uv;\n"
"out vec4 out_color;\n"
"void main(){\n"
"out_color = vec4(texture(u_texture, f_uv).rgb, 1);\n"
"}\n"
;
static GLuint texture_program = 0;
static GLint texture_u_view_xform = -1;
static GLint texture_u_texture = -1;
static char texrgb_vshader[] =
"#version 330\n"
"uniform vec2 u_view_xform;\n"
"layout (location = 0) in vec2 v_p;\n"
"layout (location = 1) in vec2 v_uv;\n"
"out vec2 f_uv;\n"
"void main(){\n"
"vec2 norm_pos = v_p*u_view_xform + vec2(-1.0, -1.0);\n"
"gl_Position = vec4(norm_pos, 0.0, 1.0);\n"
"f_uv = v_uv;\n"
"}\n"
;
static char texrgb_fshader[] =
"#version 330\n"
"uniform sampler2D u_texture;\n"
"uniform vec4 u_color;\n"
"in vec2 f_uv;\n"
"out vec4 out_color;\n"
"void main(){\n"
"float texture_a = texture(u_texture, f_uv).r;\n"
"out_color = vec4(u_color.rgb, u_color.a*texture_a);\n"
"}\n"
;
static GLuint texrgb_program = 0;
static GLint texrgb_u_view_xform = -1;
static GLint texrgb_u_texture = -1;
static GLint texrgb_u_color = -1;
static GLuint canvas_texture = 0;
static GLuint canvas_framebuffer = 0;
static GLuint test3_texture = 0;
#define CIRCLE5_RADIUS 5.f
#define CIRCLE5_X 6.f
#define CIRCLE5_Y 6.f
#define CIRCLE5_SIDE 12
static GLuint circle5_texture = 0;
#define CIRCLE25_RADIUS 25.f
#define CIRCLE25_X 30.f
#define CIRCLE25_Y 30.f
#define CIRCLE25_SIDE 60
static GLuint circle25_texture = 0;
#define CIRCLE125_RADIUS 125.f
#define CIRCLE125_X 150.f
#define CIRCLE125_Y 150.f
#define CIRCLE125_SIDE 300
static GLuint circle125_texture = 0;
static void
opengl_render_init(void){
//- setup basic shader program
{
GLuint shaders[2] = {0};
shaders[0] = opengl_helper_make_shader(basic_vshader, GL_VERTEX_SHADER);
shaders[1] = opengl_helper_make_shader(basic_fshader, GL_FRAGMENT_SHADER);
basic_program = opengl_helper_make_program(shaders, 2);
basic_u_view_xform = glGetUniformLocation(basic_program, "u_view_xform");
}
//- setup "srgb in" shader program
{
GLuint shaders[2] = {0};
shaders[0] = opengl_helper_make_shader(srgb_in_vshader, GL_VERTEX_SHADER);
shaders[1] = opengl_helper_make_shader(srgb_in_fshader, GL_FRAGMENT_SHADER);
srgb_in_program = opengl_helper_make_program(shaders, 2);
srgb_in_u_view_xform = glGetUniformLocation(basic_program, "u_view_xform");
}
//- setup rect shader program
{
GLuint shaders[2] = {0};
shaders[0] = opengl_helper_make_shader(rect_vshader, GL_VERTEX_SHADER);
shaders[1] = opengl_helper_make_shader(rect_fshader, GL_FRAGMENT_SHADER);
rect_program = opengl_helper_make_program(shaders, 2);
rect_u_view_xform = glGetUniformLocation(rect_program, "u_view_xform");
}
//- setup texture shader program
{
GLuint shaders[2] = {0};
shaders[0] = opengl_helper_make_shader(texture_vshader, GL_VERTEX_SHADER);
shaders[1] = opengl_helper_make_shader(texture_fshader, GL_FRAGMENT_SHADER);
texture_program = opengl_helper_make_program(shaders, 2);
texture_u_view_xform = glGetUniformLocation(texture_program, "u_view_xform");
texture_u_texture = glGetUniformLocation(texture_program, "u_texture");
}
//- setup texture rgb shader program
{
GLuint shaders[2] = {0};
shaders[0] = opengl_helper_make_shader(texrgb_vshader, GL_VERTEX_SHADER);
shaders[1] = opengl_helper_make_shader(texrgb_fshader, GL_FRAGMENT_SHADER);
texrgb_program = opengl_helper_make_program(shaders, 2);
texrgb_u_view_xform = glGetUniformLocation(texrgb_program, "u_view_xform");
texrgb_u_texture = glGetUniformLocation(texrgb_program, "u_texture");
texrgb_u_color = glGetUniformLocation(texrgb_program, "u_color");
}
//- vertex array object
{
GLuint vao = 0;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
}
//- vertex array buffer
{
GLuint vertex_buffer = 0;
glGenBuffers(1, &vertex_buffer);
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
}
//- canvas texture
{
glGenTextures(1, &canvas_texture);
glBindTexture(GL_TEXTURE_2D, canvas_texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 2048, 2048, 0,
GL_RGB, GL_FLOAT, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
//- canvas framebuffer
{
glGenFramebuffers(1, &canvas_framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, canvas_framebuffer);
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
canvas_texture, 0);
}
//- enable alpha blend
{
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
}
//- test3 texture
{
U8 test3_img[16*3] = {0};
for (U32 i = 0; i < 16; i += 1){
U32 i3 = 3*i;
for (U32 j = 0; j < 3; j += 1){
test3_img[i3 + j] = 0x08 + 0x10*i;
}
}
glGenTextures(1, &test3_texture);
glBindTexture(GL_TEXTURE_2D, test3_texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB8, 16, 1, 0,
GL_RGB, GL_UNSIGNED_BYTE, test3_img);
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_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
//- circle5 texture
{
U8 buf[CIRCLE5_SIDE*CIRCLE5_SIDE] = {0};
render_circle_in_buffer(CIRCLE5_RADIUS, CIRCLE5_X, CIRCLE5_Y,
buf, CIRCLE5_SIDE);
U8 img[CIRCLE5_SIDE*CIRCLE5_SIDE*3] = {0};
for (U32 i = 0; i < CIRCLE5_SIDE*CIRCLE5_SIDE; i += 1){
U8 c = buf[i];
img[i*3 + 0] = c;
img[i*3 + 1] = c;
img[i*3 + 2] = c;
}
glGenTextures(1, &circle5_texture);
glBindTexture(GL_TEXTURE_2D, circle5_texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, CIRCLE5_SIDE, CIRCLE5_SIDE, 0,
GL_RGB, GL_UNSIGNED_BYTE, img);
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_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
//- circle25 texture
{
U8 buf[CIRCLE25_SIDE*CIRCLE25_SIDE] = {0};
render_circle_in_buffer(CIRCLE25_RADIUS, CIRCLE25_X, CIRCLE25_Y,
buf, CIRCLE25_SIDE);
U8 img[CIRCLE25_SIDE*CIRCLE25_SIDE*3] = {0};
for (U32 i = 0; i < CIRCLE25_SIDE*CIRCLE25_SIDE; i += 1){
U8 c = buf[i];
img[i*3 + 0] = c;
img[i*3 + 1] = c;
img[i*3 + 2] = c;
}
glGenTextures(1, &circle25_texture);
glBindTexture(GL_TEXTURE_2D, circle25_texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, CIRCLE25_SIDE, CIRCLE25_SIDE, 0,
GL_RGB, GL_UNSIGNED_BYTE, img);
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_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
//- circle125 texture
{
U8 buf[CIRCLE125_SIDE*CIRCLE125_SIDE] = {0};
render_circle_in_buffer(CIRCLE125_RADIUS, CIRCLE125_X, CIRCLE125_Y,
buf, CIRCLE125_SIDE);
U8 img[CIRCLE125_SIDE*CIRCLE125_SIDE*3] = {0};
for (U32 i = 0; i < CIRCLE125_SIDE*CIRCLE125_SIDE; i += 1){
U8 c = buf[i];
img[i*3 + 0] = c;
img[i*3 + 1] = c;
img[i*3 + 2] = c;
}
glGenTextures(1, &circle125_texture);
glBindTexture(GL_TEXTURE_2D, circle125_texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, CIRCLE125_SIDE, CIRCLE125_SIDE, 0,
GL_RGB, GL_UNSIGNED_BYTE, img);
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_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
//- check for setup errors
if (glGetError() != 0){
error_message("error in opengl renderer initialization");
}
}
static void
opengl_draw_basic_geometry(Vertex *v, U64 count){
glBufferData(GL_ARRAY_BUFFER, sizeof(*v)*count, v, GL_STREAM_DRAW);
glUseProgram(basic_program);
glUniform2f(basic_u_view_xform, 2.f/graphics_w, 2.f/graphics_h);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, 0,
sizeof(Vertex), PtrOffsetOf(Vertex, px));
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 4, GL_FLOAT, 0,
sizeof(Vertex), PtrOffsetOf(Vertex, cr));
glDrawArrays(GL_TRIANGLES, 0, count);
}
static void
opengl_draw_srgb_in_geometry(Vertex *v, U64 count){
glBufferData(GL_ARRAY_BUFFER, sizeof(*v)*count, v, GL_STREAM_DRAW);
glUseProgram(srgb_in_program);
glUniform2f(srgb_in_u_view_xform, 2.f/graphics_w, 2.f/graphics_h);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, 0,
sizeof(Vertex), PtrOffsetOf(Vertex, px));
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 4, GL_FLOAT, 0,
sizeof(Vertex), PtrOffsetOf(Vertex, cr));
glDrawArrays(GL_TRIANGLES, 0, count);
}
static void
opengl_draw_rect_geometry(Vertex *v, U64 count){
glBufferData(GL_ARRAY_BUFFER, sizeof(*v)*count, v, GL_STREAM_DRAW);
glUseProgram(rect_program);
glUniform2f(rect_u_view_xform, 2.f/graphics_w, 2.f/graphics_h);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, 0,
sizeof(Vertex), PtrOffsetOf(Vertex, px));
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 4, GL_FLOAT, 0,
sizeof(Vertex), PtrOffsetOf(Vertex, rx0));
glDrawArrays(GL_TRIANGLES, 0, count);
}
static void
opengl_draw_texture_geometry(Vertex *v, U64 count, U32 texture){
glBufferData(GL_ARRAY_BUFFER, sizeof(*v)*count, v, GL_STREAM_DRAW);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glUseProgram(texture_program);
glUniform2f(texture_u_view_xform, 2.f/graphics_w, 2.f/graphics_h);
glUniform1i(texture_u_texture, 0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, 0,
sizeof(Vertex), PtrOffsetOf(Vertex, px));
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, 0,
sizeof(Vertex), PtrOffsetOf(Vertex, uvx));
glDrawArrays(GL_TRIANGLES, 0, count);
}
static void
opengl_draw_texrgb_geometry(Vertex *v, U64 count, U32 texture,
F32 r, F32 g, F32 b, F32 a){
glBufferData(GL_ARRAY_BUFFER, sizeof(*v)*count, v, GL_STREAM_DRAW);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glUseProgram(texrgb_program);
glUniform2f(texrgb_u_view_xform, 2.f/graphics_w, 2.f/graphics_h);
glUniform1i(texrgb_u_texture, 0);
glUniform4f(texrgb_u_color, r, g, b, a);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, 0,
sizeof(Vertex), PtrOffsetOf(Vertex, px));
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, 0,
sizeof(Vertex), PtrOffsetOf(Vertex, uvx));
glDrawArrays(GL_TRIANGLES, 0, count);
}
////////////////////////////////
//- Main
typedef struct Rect{
F32 x0;
F32 y0;
F32 x1;
F32 y1;
F32 r;
F32 g;
F32 b;
F32 a;
} Rect;
int
WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nShowCmd){
sanity_test_integrals();
sanity_test_circle_area();
// do graphics tests
wgl_helper_make_graphics_window(hInstance);
U64 frame_counter = 0;
for (;;){
// wait for input
MSG msg = {0};
BOOL get_message = GetMessage(&msg, 0, 0, 0);
if (get_message <= 0){
break;
}
DispatchMessage(&msg);
// begin render
wgl_helper_begin_render();
// initialization
if (frame_counter == 0){
opengl_render_init();
}
// screen layout
F32 center_x = graphics_w*0.5f;
F32 divider_half_w = 3.f;
F32 divider_min_x = (F32)(S32)(center_x - divider_half_w);
F32 divider_max_x = divider_min_x + 2.f*divider_half_w;
F32 lside_grad_x0 = 352.f;
F32 lside_grad_x1 = lside_grad_x0 + 256.f;
F32 rside_grad_x0 = 1312.f;
F32 rside_grad_x1 = rside_grad_x0 + 256.f;
F32 test1_y = graphics_h - 10.f;
F32 test2_y = graphics_h - 60.f;
F32 test3_y = graphics_h - 110.f;
F32 test4_y = graphics_h - 160.f;
F32 test5_y = graphics_h - 340.f;
F32 test6_y = graphics_h - 450.f;
F32 test7_y = graphics_h - 480.f;
F32 test8_y = graphics_h - 800.f;
// render to canvas frame buffer
{
glBindFramebuffer(GL_FRAMEBUFFER, canvas_framebuffer);
glViewport(0, 0, (S32)graphics_w, (S32)graphics_h);
// black background everywhere
glClearColor(0.f, 0.f, 0.f, 1.f);
glClear(GL_COLOR_BUFFER_BIT);
// draw divider
{
Vertex v[6];
v[0].px = divider_min_x; v[0].py = 0.f;
v[1].px = divider_min_x; v[1].py = graphics_h;
v[2].px = divider_max_x; v[2].py = 0.f;
v[3].px = v[1].px; v[3].py = v[1].py;
v[4].px = v[2].px; v[4].py = v[2].py;
v[5].px = divider_max_x; v[5].py = graphics_h;
for (U32 i = 0; i < 6; i += 1){
v[i].cr = 1.f;
v[i].cg = 1.f;
v[i].cb = 1.f;
v[i].ca = 1.f;
}
opengl_draw_basic_geometry(v, 6);
}
// two sided test
for (U32 test_idx = 0; test_idx < 2; test_idx += 1){
F32 grad_x0 = lside_grad_x0;
F32 grad_x1 = lside_grad_x1;
if (test_idx == 1){
grad_x0 = rside_grad_x0;
grad_x1 = rside_grad_x1;
}
// 1: draw gradient
{
F32 grad_y1 = test1_y;
F32 grad_y0 = test1_y - 40.f;
Vertex v[6];
v[0].px = grad_x0; v[0].py = grad_y0;
v[1].px = grad_x0; v[1].py = grad_y1;
v[2].px = grad_x1; v[2].py = grad_y0;
v[3].px = v[1].px; v[3].py = v[1].py;
v[4].px = v[2].px; v[4].py = v[2].py;
v[5].px = grad_x1; v[5].py = grad_y1;
for (U32 i = 0; i < 6; i += 1){
if (v[i].px == grad_x0){
v[i].cr = 1.f; v[i].cg = 1.f; v[i].cb = 1.f; v[i].ca = 1.f;
}
else{
v[i].cr = 0.f; v[i].cg = 0.f; v[i].cb = 0.f; v[i].ca = 1.f;
}
}
opengl_draw_basic_geometry(v, 6);
}
// 2: draw extremes and midpoint
{
F32 box_x[4] = {0};
box_x[0] = grad_x0;
box_x[1] = (F32)(S32)((grad_x0*2.f + grad_x1)/3.f);
box_x[2] = (F32)(S32)((grad_x0 + grad_x1*2.f)/3.f);
box_x[3] = grad_x1;
F32 val[3] = { 1.f, 0.5f, 0.f };
F32 grad_y1 = test2_y;
F32 grad_y0 = test2_y - 40.f;
for (U32 j = 0; j < 3; j += 1){
F32 box_x0 = box_x[j];
F32 box_x1 = box_x[j + 1];
F32 valj = val[j];
Vertex v[6];
v[0].px = box_x0; v[0].py = grad_y0;
v[1].px = box_x0; v[1].py = grad_y1;
v[2].px = box_x1; v[2].py = grad_y0;
v[3].px = v[1].px; v[3].py = v[1].py;
v[4].px = v[2].px; v[4].py = v[2].py;
v[5].px = box_x1; v[5].py = grad_y1;
for (U32 i = 0; i < 6; i += 1){
v[i].cr = valj; v[i].cg = valj; v[i].cb = valj; v[i].ca = 1.f;
}
opengl_draw_basic_geometry(v, 6);
}
}
// 3: compare x^2.2 with texture sampling for (sRGB -> Linear)
if (test_idx == 1){
F32 grad_y2 = test3_y;
F32 grad_y1 = test3_y - 20.f;
F32 grad_y0 = test3_y - 40.f;
// convert (sRGB -> Linear) by texture sampling
{
Vertex v[6];
v[0].px = grad_x0; v[0].py = grad_y1;
v[0].uvx = 0.f; v[0].uvy = 1.f;
v[1].px = grad_x0; v[1].py = grad_y2;
v[1].uvx = 0.f; v[1].uvy = 0.f;
v[2].px = grad_x1; v[2].py = grad_y1;
v[2].uvx = 1.f; v[2].uvy = 1.f;
v[5].px = grad_x1; v[5].py = grad_y2;
v[5].uvx = 1.f; v[5].uvy = 0.f;
v[3] = v[1];
v[4] = v[2];
opengl_draw_texture_geometry(v, 6, test3_texture);
}
// convert (sRGB -> Linear) by pow(x, 2.2) in shader
{
F32 delta_x = (grad_x1 - grad_x0)/16.f;
for (U32 i = 0; i < 16; i += 1){
F32 l = (F32)(S32)(grad_x0 + i*delta_x);
F32 r = (F32)(S32)(l + delta_x);
Vertex v[6];
v[0].px = l; v[0].py = grad_y0;
v[1].px = l; v[1].py = grad_y1;
v[2].px = r; v[2].py = grad_y0;
v[5].px = r; v[5].py = grad_y1;
v[3] = v[1];
v[4] = v[2];
F32 val = (F32)(0x08 + 0x10*i)/255.f;
for (U32 j = 0; j < 6; j += 1){
v[j].cr = val;
v[j].cg = val;
v[j].cb = val;
v[j].ca = 1.f;
}
opengl_draw_srgb_in_geometry(v, 6);
}
}
}
// 4: color gradient
{
F32 color_pairs[12][3] = {
{1.f, 0.f, 0.f}, {1.f, 1.f, 0.f},
{1.f, 1.f, 0.f}, {0.f, 1.f, 0.f},
{0.f, 1.f, 0.f}, {0.f, 1.f, 1.f},
{0.f, 1.f, 1.f}, {0.f, 0.f, 1.f},
{0.f, 0.f, 1.f}, {1.f, 0.f, 1.f},
{1.f, 0.f, 1.f}, {1.f, 0.f, 0.f},
};
F32 cursor_y = test4_y;
for (U32 j = 0; j < 12; j += 2){
F32 grad_y1 = cursor_y;
F32 grad_y0 = cursor_y - 20.f;
cursor_y -= 30.f;
F32 *col[2] = {0};
col[0] = color_pairs[j];
col[1] = color_pairs[j + 1];
Vertex v[6];
v[0].px = grad_x0; v[0].py = grad_y0;
v[1].px = grad_x0; v[1].py = grad_y1;
v[2].px = grad_x1; v[2].py = grad_y0;
v[3].px = v[1].px; v[3].py = v[1].py;
v[4].px = v[2].px; v[4].py = v[2].py;
v[5].px = grad_x1; v[5].py = grad_y1;
for (U32 i = 0; i < 6; i += 1){
U32 k = 0;
if (v[i].px == grad_x1){
k = 1;
}
v[i].cr = col[k][0];
v[i].cg = col[k][1];
v[i].cb = col[k][2];
v[i].ca = 1.f;
}
if (test_idx == 0){
opengl_draw_basic_geometry(v, 6);
}
else{
opengl_draw_srgb_in_geometry(v, 6);
}
}
}
// 5: transparency blending
{
Rect rect[20] = {0};
U32 recti = 0;
F32 top_y = test5_y;
F32 bot_y = top_y - 100.f;
rect[recti].x0 = grad_x0;
rect[recti].y0 = bot_y;
rect[recti].x1 = grad_x1;
rect[recti].y1 = top_y;
rect[recti].r = 1.f;
rect[recti].g = 1.f;
rect[recti].b = 1.f;
rect[recti].a = 1.f;
recti += 1;
for (U32 i = 0; i < 3; i += 1){
rect[recti].x0 = grad_x0;
rect[recti].y0 = top_y - 10.f - 30.f*i;
rect[recti].x1 = grad_x1;
rect[recti].y1 = top_y - 30.f - 30.f*i;
rect[recti].r = (i == 0)?1.f:0.f;
rect[recti].g = (i == 1)?1.f:0.f;
rect[recti].b = (i == 2)?1.f:0.f;
rect[recti].a = 1.f;
recti += 1;
}
for (U32 j = 0; j < 2; j += 1){
for (U32 i = 0; i < 3; i += 1){
rect[recti].x0 = grad_x0 + 10.f + 30.f*i + 100.f*j;
rect[recti].y0 = bot_y;
rect[recti].x1 = grad_x0 + 30.f + 30.f*i + 100.f*j;
rect[recti].y1 = top_y;
rect[recti].r = (i == 0)?1.f:0.f;
rect[recti].g = (i == 1)?1.f:0.f;
rect[recti].b = (i == 2)?1.f:0.f;
rect[recti].a = (j == 0)?0.5f:0.75f;
recti += 1;
}
}
for (U32 j = 0; j < recti; j += 1){
Vertex v[6] = {0};
v[0].px = rect[j].x0; v[0].py = rect[j].y0;
v[1].px = rect[j].x0; v[1].py = rect[j].y1;
v[2].px = rect[j].x1; v[2].py = rect[j].y0;
v[3].px = rect[j].x0; v[3].py = rect[j].y1;
v[4].px = rect[j].x1; v[4].py = rect[j].y0;
v[5].px = rect[j].x1; v[5].py = rect[j].y1;
for (U32 k = 0; k < 6; k += 1){
v[k].cr = rect[j].r;
v[k].cg = rect[j].g;
v[k].cb = rect[j].b;
v[k].ca = rect[j].a;
}
if (test_idx == 0){
opengl_draw_basic_geometry(v, 6);
}
else{
opengl_draw_srgb_in_geometry(v, 6);
}
}
}
// 6: sub-pixel rectangle anti-aliasing
{
F32 top_y = test6_y;
F32 bot_y = test6_y - 20.f;
F32 x_cursor = grad_x0;
for (U32 i = 0; i < 16; i += 1){
F32 l = x_cursor + 4.0625*i;
F32 r = l + 2.f;
F32 floor_l = (F32)(S32)l;
F32 ceil_r = (F32)(S32)r;
if (ceil_r < (F32)r){
ceil_r += 1.f;
}
Vertex v[6];
v[0].px = floor_l; v[0].py = bot_y;
v[1].px = floor_l; v[1].py = top_y;
v[2].px = ceil_r; v[2].py = bot_y;
v[3].px = floor_l; v[3].py = top_y;
v[4].px = ceil_r; v[4].py = bot_y;
v[5].px = ceil_r; v[5].py = top_y;
for (U32 j = 0; j < 6; j += 1){
v[j].rx0 = l;
v[j].ry0 = bot_y;
v[j].rx1 = r;
v[j].ry1 = top_y;
}
opengl_draw_rect_geometry(v, 6);
}
}
// 7: circles rendered with analytical antialiasing via integration
{
F32 y = test7_y;
F32 x = grad_x0;
// circle5
{
F32 y1 = y;
F32 y0 = y - CIRCLE5_SIDE;
F32 x0 = x;
F32 x1 = x + CIRCLE5_SIDE;
x += CIRCLE5_SIDE + 10;
Vertex v[6];
v[0].px = x0; v[0].py = y0;
v[0].uvx = 0.f; v[0].uvy = 0.f;
v[1].px = x0; v[1].py = y1;
v[1].uvx = 0.f; v[1].uvy = 1.f;
v[2].px = x1; v[2].py = y0;
v[2].uvx = 1.f; v[2].uvy = 0.f;
v[3].px = x0; v[3].py = y1;
v[3].uvx = 0.f; v[3].uvy = 1.f;
v[4].px = x1; v[4].py = y0;
v[4].uvx = 1.f; v[4].uvy = 0.f;
v[5].px = x1; v[5].py = y1;
v[5].uvx = 1.f; v[5].uvy = 1.f;
opengl_draw_texture_geometry(v, 6, circle5_texture);
}
// circle25
{
F32 y1 = y;
F32 y0 = y - CIRCLE25_SIDE;
F32 x0 = x;
F32 x1 = x + CIRCLE25_SIDE;
x += CIRCLE25_SIDE + 10;
Vertex v[6];
v[0].px = x0; v[0].py = y0;
v[0].uvx = 0.f; v[0].uvy = 0.f;
v[1].px = x0; v[1].py = y1;
v[1].uvx = 0.f; v[1].uvy = 1.f;
v[2].px = x1; v[2].py = y0;
v[2].uvx = 1.f; v[2].uvy = 0.f;
v[3].px = x0; v[3].py = y1;
v[3].uvx = 0.f; v[3].uvy = 1.f;
v[4].px = x1; v[4].py = y0;
v[4].uvx = 1.f; v[4].uvy = 0.f;
v[5].px = x1; v[5].py = y1;
v[5].uvx = 1.f; v[5].uvy = 1.f;
opengl_draw_texture_geometry(v, 6, circle25_texture);
}
// circle125
{
F32 y1 = y;
F32 y0 = y - CIRCLE125_SIDE;
F32 x0 = x;
F32 x1 = x + CIRCLE125_SIDE;
x += CIRCLE125_SIDE + 10;
Vertex v[6];
v[0].px = x0; v[0].py = y0;
v[0].uvx = 0.f; v[0].uvy = 0.f;
v[1].px = x0; v[1].py = y1;
v[1].uvx = 0.f; v[1].uvy = 1.f;
v[2].px = x1; v[2].py = y0;
v[2].uvx = 1.f; v[2].uvy = 0.f;
v[3].px = x0; v[3].py = y1;
v[3].uvx = 0.f; v[3].uvy = 1.f;
v[4].px = x1; v[4].py = y0;
v[4].uvx = 1.f; v[4].uvy = 0.f;
v[5].px = x1; v[5].py = y1;
v[5].uvx = 1.f; v[5].uvy = 1.f;
opengl_draw_texture_geometry(v, 6, circle125_texture);
}
}
// 8: color on color circles
{
F32 back[][4] = {
{1.f, 0.f, 0.f, 1.f},
{1.f, 0.f, 0.f, 1.f},
{0.f, 1.f, 0.f, 1.f},
{0.f, 1.f, 0.f, 1.f},
{0.f, 0.f, 1.f, 1.f},
{0.f, 0.f, 1.f, 1.f},
};
F32 fore[][4] = {
{0.f, 1.f, 0.f, 1.f},
{0.f, 0.f, 1.f, 1.f},
{1.f, 0.f, 0.f, 1.f},
{0.f, 0.f, 1.f, 1.f},
{1.f, 0.f, 0.f, 1.f},
{0.f, 1.f, 0.f, 1.f},
};
F32 y = test8_y;
F32 x = grad_x0;
for (U32 k = 0; k < 6; k += 1){
// background
{
F32 x0 = x;
F32 x1 = x + CIRCLE5_SIDE + CIRCLE25_SIDE + 20.f;
F32 y1 = y;
F32 y0 = y - CIRCLE25_SIDE;
Vertex v[6];
v[0].px = x0; v[0].py = y0;
v[1].px = x0; v[1].py = y1;
v[2].px = x1; v[2].py = y0;
v[3].px = x0; v[3].py = y1;
v[4].px = x1; v[4].py = y0;
v[5].px = x1; v[5].py = y1;
for (U32 i = 0; i < 6; i += 1){
v[i].cr = back[k][0];
v[i].cg = back[k][1];
v[i].cb = back[k][2];
v[i].ca = back[k][3];
}
if (test_idx == 0){
opengl_draw_basic_geometry(v, 6);
}
else{
opengl_draw_srgb_in_geometry(v, 6);
}
}
// circle5
{
F32 y1 = y;
F32 y0 = y - CIRCLE5_SIDE;
F32 x0 = x;
F32 x1 = x + CIRCLE5_SIDE;
x += CIRCLE5_SIDE + 10;
Vertex v[6];
v[0].px = x0; v[0].py = y0;
v[0].uvx = 0.f; v[0].uvy = 0.f;
v[1].px = x0; v[1].py = y1;
v[1].uvx = 0.f; v[1].uvy = 1.f;
v[2].px = x1; v[2].py = y0;
v[2].uvx = 1.f; v[2].uvy = 0.f;
v[3].px = x0; v[3].py = y1;
v[3].uvx = 0.f; v[3].uvy = 1.f;
v[4].px = x1; v[4].py = y0;
v[4].uvx = 1.f; v[4].uvy = 0.f;
v[5].px = x1; v[5].py = y1;
v[5].uvx = 1.f; v[5].uvy = 1.f;
opengl_draw_texrgb_geometry(v, 6, circle5_texture,
fore[k][0], fore[k][1],
fore[k][2], fore[k][3]);
}
// circle25
{
F32 y1 = y;
F32 y0 = y - CIRCLE25_SIDE;
F32 x0 = x;
F32 x1 = x + CIRCLE25_SIDE;
x += CIRCLE25_SIDE + 10;
Vertex v[6];
v[0].px = x0; v[0].py = y0;
v[0].uvx = 0.f; v[0].uvy = 0.f;
v[1].px = x0; v[1].py = y1;
v[1].uvx = 0.f; v[1].uvy = 1.f;
v[2].px = x1; v[2].py = y0;
v[2].uvx = 1.f; v[2].uvy = 0.f;
v[3].px = x0; v[3].py = y1;
v[3].uvx = 0.f; v[3].uvy = 1.f;
v[4].px = x1; v[4].py = y0;
v[4].uvx = 1.f; v[4].uvy = 0.f;
v[5].px = x1; v[5].py = y1;
v[5].uvx = 1.f; v[5].uvy = 1.f;
opengl_draw_texrgb_geometry(v, 6, circle25_texture,
fore[k][0], fore[k][1],
fore[k][2], fore[k][3]);
}
// move y down after 2
if ((k + 1) % 2 == 0){
x = grad_x0;
y -= CIRCLE25_SIDE;
}
}
}
}
}
// resolve canvas to screen
{
glBindFramebuffer(GL_READ_FRAMEBUFFER, canvas_framebuffer);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
S32 w = (S32)graphics_w;
S32 h = (S32)graphics_h;
// left side resolve *without* GL_FRAMEBUFFER_SRGB
glBlitFramebuffer(0, 0, w/2, h,
0, 0, w/2, h,
GL_COLOR_BUFFER_BIT,
GL_NEAREST);
// right side resolve *with* GL_FRAMEBUFFER_SRGB
glEnable(GL_FRAMEBUFFER_SRGB);
glBlitFramebuffer(w/2, 0, w, h,
w/2, 0, w, h,
GL_COLOR_BUFFER_BIT,
GL_NEAREST);
glDisable(GL_FRAMEBUFFER_SRGB);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
// after render numerical value checks
{
// 1: each value differs from it's neighbors by exactly 1
// (w/ 4-byte stride; alpha channel not included)
U8 buf[256*4] = {0};
glReadPixels((S32)lside_grad_x0, (S32)test1_y - 5, 256, 1,
GL_RGBA, GL_UNSIGNED_BYTE, buf);
for (U32 i = 1; i < 256; i += 1){
Assert(buf[(i - 1)*4] - 1 == buf[i*4]);
}
}
// check for gl errors from this frame
if (glGetError() != 0){
error_message("error in opengl frame render");
}
// end render
wgl_helper_end_render();
// frame counter
frame_counter += 1;
}
return(0);
}
////////////////////////////////
//- OpenGL Helpers
static GLuint
opengl_helper_make_shader(char *src, GLenum shader_type){
// create shader
GLuint shader = glCreateShader(shader_type);
GLenum error = glGetError();
// set source
glShaderSource(shader, 1, &src, 0);
// compile
glCompileShader(shader);
// read log
GLint info_log_length = 0;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &info_log_length);
if (info_log_length > 0){
char *buffer = allocate_array(char, info_log_length + 1);
GLint length = 0;
glGetShaderInfoLog(shader, info_log_length + 1, &length, buffer);
error_message(buffer);
}
// check status
{
GLint compile_status = 0;
glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_status);
if (compile_status == 0){
error_message("could not create one of the shaders");
}
}
return(shader);
}
static GLuint
opengl_helper_make_program(GLuint *shaders, U32 count){
// link program
GLuint program = glCreateProgram();
for (U32 i = 0; i < count; i += 1){
glAttachShader(program, shaders[i]);
}
glLinkProgram(program);
// read log
GLint info_log_length = 0;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &info_log_length);
if (info_log_length > 0){
char *buffer = allocate_array(char, info_log_length + 1);
GLint length = 0;
glGetProgramInfoLog(program, info_log_length + 1, &length, buffer);
error_message(buffer);
}
// check status
{
GLint link_status = 0;
glGetProgramiv(program, GL_LINK_STATUS, &link_status);
if (link_status == 0){
error_message("could not create one of the GPU programs");
}
}
return(program);
}
////////////////////////////////
//- WGL Definitions
#define WGL_DRAW_TO_WINDOW_ARB 0x2001
#define WGL_ACCELERATION_ARB 0x2003
#define WGL_SWAP_METHOD_ARB 0x2007
#define WGL_SUPPORT_OPENGL_ARB 0x2010
#define WGL_DOUBLE_BUFFER_ARB 0x2011
#define WGL_PIXEL_TYPE_ARB 0x2013
#define WGL_COLOR_BITS_ARB 0x2014
#define WGL_RED_BITS_ARB 0x2015
#define WGL_GREEN_BITS_ARB 0x2017
#define WGL_BLUE_BITS_ARB 0x2019
#define WGL_FULL_ACCELERATION_ARB 0x2027
#define WGL_SWAP_EXCHANGE_ARB 0x2028
#define WGL_TYPE_RGBA_ARB 0x202B
//- WGL_ARB_create_context constants
#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
#define WGL_CONTEXT_FLAGS_ARB 0x2094
#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126
#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002
#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
//- wgl funcs x-list
#define WGL_FUNCS_X_LIST() \
X(wglCreateContext, HGLRC, (HDC dc)) \
X(wglDeleteContext, BOOL, (HGLRC glrc)) \
X(wglMakeCurrent, BOOL, (HDC dc,HGLRC glrc)) \
X(wglGetProcAddress, PROC, (LPCSTR name))
//- wgl extension funcs x-list
#define WGL_EXT_FUNCS_X_LIST() \
X(wglChoosePixelFormatARB, BOOL, (HDC,int*,FLOAT*,UINT,int*,UINT*)) \
X(wglCreateContextAttribsARB, HGLRC, (HDC dc,HGLRC share,int*atri))
//- wgl function types
#define X(N,R,P) typedef R W32_##N P;
WGL_FUNCS_X_LIST()
WGL_EXT_FUNCS_X_LIST()
#undef X
//- wgl function pointers
#define X(N,R,P) static W32_##N * w32_##N = 0;
WGL_FUNCS_X_LIST()
WGL_EXT_FUNCS_X_LIST()
#undef X
////////////////////////////////
//- WGL Helper
#define GET_PROC_ADDR(v,m,s) (*(PROC*)(&(v))) = GetProcAddress((m),(s))
#define WGL_GET_PROC_ADDR(v,s) (*(PROC*)(&(v))) = w32_wglGetProcAddress(s)
#define GL_FULL_PROC_ADDR(v,m,s) (*(PROC*)(&(v))) = wgl_helper_load((m),(s))
#define BOOTSTRAP_WINDOW_CLASS_NAME L"mr4th-opengl-bootstrap"
#define GRAPHICS_WINDOW_CLASS_NAME L"mr4th-opengl-graphics"
static LRESULT
w32_graphics_window_proc(HWND wnd,
UINT msg,
WPARAM wparam,
LPARAM lparam){
LRESULT result = 0;
switch (msg){
case WM_CLOSE:
{
ExitProcess(0);
}break;
case WM_SIZE:
{
// Here we need to re-render IF we want the window not
// to have ugly black borders during resizing.
// begin render
HDC dc = GetDC(wnd);
w32_wglMakeCurrent(dc, graphics_context);
// render
glClearColor(0.f, 0.f, 0.f, 1.f);
glClear(GL_COLOR_BUFFER_BIT);
// end render
SwapBuffers(dc);
ReleaseDC(wnd, dc);
}break;
default:
{
result = DefWindowProcW(wnd, msg, wparam, lparam);
}break;
}
return(result);
}
static PROC
wgl_helper_load(HMODULE opengl_module, char *name){
PROC result = (PROC)GetProcAddress(opengl_module, name);
if (result == 0){
result = (PROC)w32_wglGetProcAddress(name);
}
return(result);
}
static void
wgl_helper_make_graphics_window(HINSTANCE hInstance){
//- load opengl module
HMODULE w32_opengl_module = LoadLibraryA("opengl32.dll");
if (w32_opengl_module == 0){
error_message("failed to initialize opengl32.dll");
}
//- load & check wgl functions
{
#define X(N,R,P) GET_PROC_ADDR(w32_##N, w32_opengl_module, #N);
WGL_FUNCS_X_LIST()
#undef X
B32 missing_wgl_func = 0;
#define X(N,R,P) if (w32_##N == 0){ missing_wgl_func = 1; }
WGL_FUNCS_X_LIST()
#undef X
if (missing_wgl_func){
error_message("failed to load wgl function(s)");
}
}
// register bootstrap class
WNDCLASSW bs_wnd_class = {0};
bs_wnd_class.lpfnWndProc = DefWindowProcW;
bs_wnd_class.hInstance = hInstance;
bs_wnd_class.lpszClassName = BOOTSTRAP_WINDOW_CLASS_NAME;
ATOM bs_atom = RegisterClassW(&bs_wnd_class);
if (bs_atom == 0){
error_message("failed to register bootstrap class");
}
// create bootstrap window
HWND bootstrap_window =
CreateWindowW(BOOTSTRAP_WINDOW_CLASS_NAME,
L"mr4th-opengl-bootstrap",
0, 0, 0, 0, 0, // style, x,y,w,h
0, 0, hInstance, 0 // parent, menu, inst, param
);
if (bootstrap_window == 0){
error_message("failed to create bootstrap window");
}
//- load wgl extension functions
{
HDC dc = GetDC(bootstrap_window);
// create bootstrap context
HGLRC hglrc = 0;
{
PIXELFORMATDESCRIPTOR format_desc = {0};
format_desc.nSize = sizeof(format_desc);
format_desc.nVersion = 1;
format_desc.dwFlags = PFD_SUPPORT_OPENGL;
format_desc.cColorBits = 24;
format_desc.cRedBits = 8;
format_desc.cRedShift = 0;
format_desc.cGreenBits = 8;
format_desc.cGreenShift = 8;
format_desc.cBlueBits = 8;
format_desc.cBlueShift = 16;
int format_idx = ChoosePixelFormat(dc, &format_desc);
if (format_idx != 0){
BOOL spf = SetPixelFormat(dc, format_idx, &format_desc);
if (spf){
hglrc = w32_wglCreateContext(dc);
}
}
}
if (hglrc == 0){
error_message("failed to create bootstrap context");
}
// load wgl extension functions
w32_wglMakeCurrent(dc, hglrc);
#define X(N,R,P) WGL_GET_PROC_ADDR(w32_##N, #N);
WGL_EXT_FUNCS_X_LIST()
#undef X
w32_wglMakeCurrent(0, 0);
// check wgl extension functions
B32 missing_wgl_ext_func = 0;
#define X(N,R,P) if (w32_##N == 0){ missing_wgl_ext_func = 1; }
WGL_EXT_FUNCS_X_LIST()
#undef X
// report error
if (missing_wgl_ext_func){
error_message("failed to load wgl extension function(s)");
}
// delete context
{
BOOL delete_context = w32_wglDeleteContext(hglrc);
Assert(delete_context);
}
// cleanup dc
ReleaseDC(bootstrap_window, dc);
}
//- destroy bootstrap window, and class
{
BOOL destroy_window = DestroyWindow(bootstrap_window);
Assert(destroy_window);
BOOL unregister = UnregisterClassW(BOOTSTRAP_WINDOW_CLASS_NAME, hInstance);
Assert(unregister);
}
//- register graphics class
WNDCLASSW gr_wnd_class = {0};
gr_wnd_class.lpfnWndProc = w32_graphics_window_proc;
gr_wnd_class.hInstance = hInstance;
gr_wnd_class.lpszClassName = GRAPHICS_WINDOW_CLASS_NAME;
ATOM gr_atom = RegisterClassW(&gr_wnd_class);
if (gr_atom == 0){
error_message("failed to register graphics class");
}
//- create graphics context
int format_idx = 0;
{
// create dummy window
HWND dummy_window =
CreateWindowW(GRAPHICS_WINDOW_CLASS_NAME,
L"DUMMY",
WS_TILEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, // x,y
CW_USEDEFAULT, CW_USEDEFAULT, // w,h
0, 0, hInstance, 0 // parent, menu, inst, param
);
if (dummy_window == 0){
error_message("failed to create dummy window");
}
HDC dc = GetDC(dummy_window);
// choose pixel format
int format_attribs[] = {
WGL_DRAW_TO_WINDOW_ARB, TRUE,
WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
WGL_SWAP_METHOD_ARB, WGL_SWAP_EXCHANGE_ARB,
WGL_SUPPORT_OPENGL_ARB, TRUE,
WGL_DOUBLE_BUFFER_ARB, TRUE,
WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
WGL_COLOR_BITS_ARB, 24,
WGL_RED_BITS_ARB, 8,
WGL_GREEN_BITS_ARB, 8,
WGL_BLUE_BITS_ARB, 8,
0
};
UINT num_formats = 0;
BOOL cpf = w32_wglChoosePixelFormatARB(dc, format_attribs, 0,
1, &format_idx, &num_formats);
if (cpf && num_formats > 0){
// set pixel format
PIXELFORMATDESCRIPTOR format_desc = {0};
BOOL spf = SetPixelFormat(dc, format_idx, &format_desc);
if (spf){
// create context
int attribs[] = {
WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
WGL_CONTEXT_MINOR_VERSION_ARB, 3,
WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
0
};
graphics_context = w32_wglCreateContextAttribsARB(dc, 0, attribs);
}
}
//- load & check gl functions
w32_wglMakeCurrent(dc, graphics_context);
#define X(N,R,P) GL_FULL_PROC_ADDR(N, w32_opengl_module, #N);
GL_FUNC_X_LIST()
#undef X
w32_wglMakeCurrent(0, 0);
B32 missing_gl_func = 0;
#define X(N,R,P) if (N == 0){ missing_gl_func = 1; }
GL_FUNC_X_LIST()
#undef X
if (missing_gl_func){
error_message("failed to load gl functions");
}
// cleanup
ReleaseDC(dummy_window, dc);
DestroyWindow(dummy_window);
}
if (graphics_context == 0){
error_message("failed to create graphics context");
}
//- create the window
graphics_window =
CreateWindowW(GRAPHICS_WINDOW_CLASS_NAME,
L"TITLE ME",
WS_TILEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, // x,y
CW_USEDEFAULT, CW_USEDEFAULT, // w,h
0, 0, hInstance, 0 // parent, menu, inst, param
);
if (graphics_window == 0){
error_message("failed to create graphics window");
}
//- set pixel format
{
HDC dc = GetDC(graphics_window);
PIXELFORMATDESCRIPTOR format_desc = {0};
SetPixelFormat(dc, format_idx, &format_desc);
ReleaseDC(graphics_window, dc);
}
//- make the window full screen
{
DWORD style = GetWindowLongW(graphics_window, GWL_STYLE);
HMONITOR mon = MonitorFromWindow(graphics_window, MONITOR_DEFAULTTOPRIMARY);
if (mon != 0){
MONITORINFO moninfo = {0};
moninfo.cbSize = sizeof(moninfo);
if (GetMonitorInfoW(mon, &moninfo)){
SetWindowLongW(graphics_window, GWL_STYLE, style & ~WS_TILEDWINDOW);
SetWindowPos(graphics_window, HWND_TOP,
moninfo.rcMonitor.left, moninfo.rcMonitor.top,
moninfo.rcMonitor.right - moninfo.rcMonitor.left,
moninfo.rcMonitor.bottom - moninfo.rcMonitor.top,
SWP_NOOWNERZORDER | SWP_FRAMECHANGED);
}
}
}
//- show the window
ShowWindow(graphics_window, SW_SHOW);
}
static void
wgl_helper_begin_render(void){
graphics_dc = GetDC(graphics_window);
w32_wglMakeCurrent(graphics_dc, graphics_context);
RECT rect = {0};
if (GetClientRect(graphics_window, &rect)){
graphics_w = (F32)(rect.right - rect.left);
graphics_h = (F32)(rect.bottom - rect.top);
}
}
static void
wgl_helper_end_render(void){
SwapBuffers(graphics_dc);
ReleaseDC(graphics_window, graphics_dc);
}
////////////////////////////////
//- Error Message
static void
error_message(char *msg){
MessageBoxA(0, msg, "Error", MB_OK);
ExitProcess(1);
}
//$ graphical //