vn/code/opengl_render.cpp

537 lines
17 KiB
C++

// sixten: Functions implemented by the platform layer
static void *OpenGL_AllocateMemory(umm Size);
inline void *OpenGL_LoadFunction(char *Name);
static void OpenGL_DeallocateMemory(void *Memory);
//- sixten: OpenGL Render Layer
static void OpenGL_DebugMessageCallback(GLenum Source, GLenum Type, GLuint ID, GLenum Severity,
GLsizei Length, const GLchar *Message, const void *UserParam)
{
if(Severity == GL_DEBUG_SEVERITY_HIGH)
{
__debugbreak();
}
}
inline render_handle OpenGL_GetHandleFromTexture(opengl_texture Texture)
{
render_handle Result = {};
Result.U32[0] = Texture.ID;
Result.U32[1] = Texture.Format;
Result.U32[2] = Texture.Dim.x;
Result.U32[3] = Texture.Dim.y;
return(Result);
}
inline opengl_texture OpenGL_GetTextureFromHandle(render_handle Handle)
{
opengl_texture Result = {};
Result.ID = Handle.U32[0];
Result.Format = (render_texture_format)Handle.U32[1];
Result.Dim.x = Handle.U32[2];
Result.Dim.y = Handle.U32[3];
return(Result);
}
inline u32 OpenGL_GetInternalFormatFromTextureFormat(render_texture_format Format)
{
u32 InternalFormat = GL_INVALID_ENUM;
if(Format == Render_TextureFormat_R8)
{
InternalFormat = GL_RED;
}
else if(Format == Render_TextureFormat_RGB8)
{
InternalFormat = GL_RGB;
}
else if(Format == Render_TextureFormat_RGBA8)
{
InternalFormat = GL_RGBA;
}
else
{
InvalidCodepath;
}
return(InternalFormat);
}
global GLint Global_OpenGL_SwizzleMaskR8[4] = {GL_ONE, GL_ONE, GL_ONE, GL_RED};
global GLint Global_OpenGL_SwizzleMaskRGB8[4] = {GL_RED, GL_GREEN, GL_BLUE, GL_ONE};
global GLint Global_OpenGL_SwizzleMaskRGBA8[4] = {GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA};
inline GLint *OpenGL_GetSwizzleFromTextureFormat(render_texture_format Format)
{
GLint *Swizzle = 0;
if(Format == Render_TextureFormat_R8)
{
Swizzle = Global_OpenGL_SwizzleMaskR8;
}
else if(Format == Render_TextureFormat_RGB8)
{
Swizzle = Global_OpenGL_SwizzleMaskRGB8;
}
else if(Format == Render_TextureFormat_RGBA8)
{
Swizzle = Global_OpenGL_SwizzleMaskRGBA8;
}
else
{
InvalidCodepath;
}
return(Swizzle);
}
static RENDER_ALLOCATE_TEXTURE(OpenGL_AllocateTexture)
{
opengl_texture Texture = {};
Texture.Dim = Dim;
Texture.Format = Format;
glGenTextures(1, &Texture.ID);
Assert(Texture.ID);
u32 InternalFormat = OpenGL_GetInternalFormatFromTextureFormat(Format);
GLint *SwizzleMask = OpenGL_GetSwizzleFromTextureFormat(Format);
glBindTexture(GL_TEXTURE_2D, Texture.ID);
glTexImage2D(GL_TEXTURE_2D, 0, InternalFormat, Dim.x, Dim.y, 0, InternalFormat, GL_UNSIGNED_BYTE, Data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, SwizzleMask);
render_handle Handle = OpenGL_GetHandleFromTexture(Texture);
return(Handle);
}
static RENDER_FILL_REGION(OpenGL_FillRegion)
{
opengl_texture Texture = OpenGL_GetTextureFromHandle(Handle);
glBindTexture(GL_TEXTURE_2D, Texture.ID);
glTexSubImage2D(GL_TEXTURE_2D, 0,
DestP.x, DestP.y, DestDim.x, DestDim.y,
OpenGL_GetInternalFormatFromTextureFormat(Texture.Format),
GL_UNSIGNED_BYTE, Data);
glBindTexture(GL_TEXTURE_2D, 0);
}
global char *OpenGL_ShaderHeader =
R"GLSL(
#version 330 core
#define s32 int
#define u32 uint
#define v2s ivec2
#define v2u uvec2
#define r32 float
#define v2 vec2
#define v3 vec3
#define v4 vec4
#define m4x4 mat4x4
#define V2 vec2
#define V3 vec3
#define V4 vec4
#define Min(a, b) min(a, b)
#define Max(a, b) max(a, b)
#define Clamp(x, min, max), clamp(x, min, max)
#define Clamp01(x), Clamp(x, 0, 1)
#define LinearBlend(A, B, C) mix(A, B, C)
#define AbsoluteValue(x) abs(x)
#define Length(x) length(x)
#define Pow(x, y) pow(x, y)
)GLSL";
static u32 OpenGL_CompileShaderProgram(char *VertexSource, char *FragmentSource)
{
u32 Result = 0;
u32 VertexShader = glCreateShader(GL_VERTEX_SHADER);
char *VertexSources[] =
{
OpenGL_ShaderHeader,
VertexSource
};
glShaderSource(VertexShader, ArrayCount(VertexSources), VertexSources, 0);
glCompileShader(VertexShader);
s32 VertexCompilationStatus;
glGetShaderiv(VertexShader, GL_COMPILE_STATUS, &VertexCompilationStatus);
if(VertexCompilationStatus)
{
u32 FragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
char *FragmentSources[] =
{
OpenGL_ShaderHeader,
FragmentSource
};
glShaderSource(FragmentShader, ArrayCount(FragmentSources), FragmentSources, 0);
glCompileShader(FragmentShader);
s32 FragmentCompilationStatus;
glGetShaderiv(FragmentShader, GL_COMPILE_STATUS, &FragmentCompilationStatus);
if(FragmentCompilationStatus)
{
u32 Program = glCreateProgram();
glAttachShader(Program, VertexShader);
glAttachShader(Program, FragmentShader);
glLinkProgram(Program);
glDeleteShader(VertexShader);
glDeleteShader(FragmentShader);
s32 ProgramLinkStatus;
glGetProgramiv(Program, GL_LINK_STATUS, &ProgramLinkStatus);
if(ProgramLinkStatus)
{
Result = Program;
}
else
{
char InfoLog[1024];
glGetProgramInfoLog(Program, ArrayCount(InfoLog), 0, InfoLog);
InvalidCodepath;
}
}
else
{
char InfoLog[1024];
glGetShaderInfoLog(FragmentShader, ArrayCount(InfoLog), 0, InfoLog);
InvalidCodepath;
}
}
else
{
char InfoLog[1024];
glGetShaderInfoLog(VertexShader, ArrayCount(InfoLog), 0, InfoLog);
InvalidCodepath;
}
return(Result);
}
static quad_program OpenGL_CompileQuadProgram(void)
{
char *VertexSource =
R"GLSL(
in v2 In_P;
in v2 In_SourceP;
in s32 In_TextureIndex;
in u32 In_Color;
in v2 In_ToCenter;
in v2 In_HalfSize;
in r32 In_CornerRadius;
in r32 In_EdgeSoftness;
in r32 In_BorderThickness;
)GLSL"
"uniform sampler2D TextureSamplers[" Stringify(MAX_BOUND_TEXTURES) "];"
R"GLSL(
uniform v2 Uniform_Resolution;
flat out s32 TextureIndex;
out v2 DestP;
out v2 DestHalfSize;
out v2 DestCenter;
out v2 SourceP;
out v4 Color;
out r32 CornerRadius;
out r32 EdgeSoftness;
out r32 BorderThickness;
void main()
{
DestP = In_P;
DestCenter = In_P + In_ToCenter;
DestHalfSize = In_HalfSize;
SourceP = In_SourceP;
v2 ScreenP = V2(DestP.x / Uniform_Resolution.x, DestP.y / Uniform_Resolution.y);
ScreenP = ScreenP*2 - 1;
ScreenP.y = -ScreenP.y;
gl_Position = V4(ScreenP, 0, 1);
Color.r = r32((In_Color >> 24) & 255u)/255.0;
Color.g = r32((In_Color >> 16) & 255u)/255.0;
Color.b = r32((In_Color >> 8) & 255u)/255.0;
Color.a = r32((In_Color >> 0) & 255u)/255.0;
TextureIndex = In_TextureIndex;
CornerRadius = In_CornerRadius;
EdgeSoftness = In_EdgeSoftness;
BorderThickness = In_BorderThickness;
})GLSL";
char *FragmentSource =
R"GLSL(
flat in s32 TextureIndex;
in v2 DestP;
in v2 DestHalfSize;
in v2 DestCenter;
in v2 SourceP;
in v4 Color;
in r32 CornerRadius;
in r32 EdgeSoftness;
in r32 BorderThickness;
)GLSL"
"uniform sampler2D TextureSamplers[" Stringify(MAX_BOUND_TEXTURES) "];"
R"GLSL(
out v4 Out_Color;
r32 RoundedRect(v2 P, v2 Center, v2 HalfSize, r32 r)
{
v2 d2 = AbsoluteValue(Center - P) - HalfSize + r;
r32 Result = Min(Max(d2.x, d2.y), 0) + Length(Max(d2, 0)) - r;
return(Result);
}
void main()
{
r32 SoftnessPadding = Max(0, EdgeSoftness*2-1);
r32 Dist = RoundedRect(DestP, DestCenter, DestHalfSize - SoftnessPadding, CornerRadius);
r32 SDFFactor = 1 - smoothstep(0, 2*EdgeSoftness, Dist);
r32 BorderFactor = 1;
if(BorderThickness != 0)
{
v2 InteriorHalfSize = DestHalfSize - BorderThickness;
r32 InteriorRadiusReduce = Min(InteriorHalfSize.x / DestHalfSize.x, InteriorHalfSize.y / DestHalfSize.y);
r32 InteriorCornerRadius = CornerRadius*InteriorRadiusReduce*InteriorRadiusReduce;
r32 InsideDist = RoundedRect(DestP, DestCenter, InteriorHalfSize - SoftnessPadding, InteriorCornerRadius);
BorderFactor = smoothstep(0, 2*EdgeSoftness, InsideDist);
}
v4 TextureFactor = texture(TextureSamplers[TextureIndex], SourceP);
Out_Color = Color*TextureFactor*BorderFactor*SDFFactor;
})GLSL";
quad_program Program = {};
Program.ID = OpenGL_CompileShaderProgram(VertexSource, FragmentSource);
Program.PID = glGetAttribLocation(Program.ID, "In_P");
Program.SourcePID = glGetAttribLocation(Program.ID, "In_SourceP");
Program.TextureIndexID = glGetAttribLocation(Program.ID, "In_TextureIndex");
Program.ColorID = glGetAttribLocation(Program.ID, "In_Color");
Program.ToCenterID = glGetAttribLocation(Program.ID, "In_ToCenter");
Program.HalfSizeID = glGetAttribLocation(Program.ID, "In_HalfSize");
Program.CornerRadiusID = glGetAttribLocation(Program.ID, "In_CornerRadius");
Program.EdgeSoftnessID = glGetAttribLocation(Program.ID, "In_EdgeSoftness");
Program.BorderThicknessID = glGetAttribLocation(Program.ID, "In_BorderThickness");
glUseProgram(Program.ID);
Program.UniformResolutionLocation = glGetUniformLocation(Program.ID, "Uniform_Resolution");
temporary_memory Scratch = GetScratch(0, 0);
for(s32 TextureIndex = 0;
TextureIndex < MAX_BOUND_TEXTURES;
++TextureIndex)
{
string Name = PushFormat(Scratch.Arena, "TextureSamplers[%i]", TextureIndex);
s32 Location = glGetUniformLocation(Program.ID, (char *)Name.Data);
glUniform1i(Location, TextureIndex);
}
ReleaseScratch(Scratch);
glUseProgram(0);
return(Program);
}
#define OpenGL_EnableFloatVertexAttribute(Index, Size, Type, type, Member)\
if(Index != -1)\
{\
glEnableVertexAttribArray(Index);\
glVertexAttribPointer(Index, Size, Type, GL_FALSE, sizeof(type), (void *)OffsetOf(type, Member));\
}
#define OpenGL_EnableIntegerVertexAttribute(Index, Size, Type, type, Member)\
if(Index != -1)\
{\
glEnableVertexAttribArray(Index);\
glVertexAttribIPointer(Index, Size, Type, sizeof(type), (void *)OffsetOf(type, Member));\
}
#define OpenGL_DisableVertexAttribute(Index)\
if(Index != -1)\
{\
glDisableVertexAttribArray(Index);\
}
static void OpenGL_BeginProgram(quad_program *Program)
{
glUseProgram(Program->ID);
OpenGL_EnableFloatVertexAttribute(Program->PID, 2, GL_FLOAT, quad_vertex, P);
OpenGL_EnableFloatVertexAttribute(Program->SourcePID, 2, GL_FLOAT, quad_vertex, SourceP);
OpenGL_EnableIntegerVertexAttribute(Program->TextureIndexID, 1, GL_UNSIGNED_INT, quad_vertex, TextureIndex);
OpenGL_EnableIntegerVertexAttribute(Program->ColorID, 1, GL_UNSIGNED_INT, quad_vertex, Color);
OpenGL_EnableFloatVertexAttribute(Program->ToCenterID, 2, GL_FLOAT, quad_vertex, ToCenter);
OpenGL_EnableFloatVertexAttribute(Program->HalfSizeID, 2, GL_FLOAT, quad_vertex, HalfSize);
OpenGL_EnableFloatVertexAttribute(Program->CornerRadiusID, 1, GL_FLOAT, quad_vertex, CornerRadius);
OpenGL_EnableFloatVertexAttribute(Program->EdgeSoftnessID, 1, GL_FLOAT, quad_vertex, EdgeSoftness);
OpenGL_EnableFloatVertexAttribute(Program->BorderThicknessID, 1, GL_FLOAT, quad_vertex, BorderThickness);
}
static void OpenGL_EndProgram(quad_program *Program)
{
OpenGL_DisableVertexAttribute(Program->PID);
OpenGL_DisableVertexAttribute(Program->SourcePID);
OpenGL_DisableVertexAttribute(Program->TextureIndexID);
OpenGL_DisableVertexAttribute(Program->ColorID);
OpenGL_DisableVertexAttribute(Program->ToCenterID);
OpenGL_DisableVertexAttribute(Program->HalfSizeID);
OpenGL_DisableVertexAttribute(Program->CornerRadiusID);
OpenGL_DisableVertexAttribute(Program->EdgeSoftnessID);
OpenGL_DisableVertexAttribute(Program->BorderThicknessID);
glUseProgram(0);
}
static opengl_context OpenGL_SetupContext(vn_render_commands *RenderCommands, umm MaxPushBufferSize)
{
opengl_context Context = {};
RenderCommands->MaxPushBufferSize = MaxPushBufferSize;
RenderCommands->PushBufferBase = (u8 *)OpenGL_AllocateMemory(RenderCommands->MaxPushBufferSize);
RenderCommands->MaxQuadVertexCount = MAX_QUAD_COUNT*4;
RenderCommands->QuadVertexBase = (quad_vertex *)OpenGL_AllocateMemory(RenderCommands->MaxQuadVertexCount*sizeof(quad_vertex));
RenderCommands->MaxQuadIndexCount = MAX_QUAD_COUNT*6;
RenderCommands->QuadIndexBase = (s32 *)OpenGL_AllocateMemory(RenderCommands->MaxQuadIndexCount*sizeof(s32));
Context.QuadProgram = OpenGL_CompileQuadProgram();
glGenBuffers(1, &Context.VertexBuffer);
glGenBuffers(1, &Context.IndexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, Context.VertexBuffer);
glBufferData(GL_ARRAY_BUFFER, RenderCommands->MaxQuadVertexCount*sizeof(quad_vertex), 0, GL_STREAM_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, Context.IndexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, RenderCommands->MaxQuadIndexCount*sizeof(s32), 0, GL_STREAM_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
u32 WhiteData = 0xFFFFFFFF;
RenderCommands->WhiteTexture = OpenGL_AllocateTexture(V2S(1, 1), Render_TextureFormat_RGBA8, &WhiteData);
RenderCommands->AllocateTexture = OpenGL_AllocateTexture;
RenderCommands->FillRegion = OpenGL_FillRegion;
#if VN_SLOW
glEnable(GL_DEBUG_OUTPUT);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
glDebugMessageCallback(OpenGL_DebugMessageCallback, 0);
#endif
u32 DummyVertexArray;
glGenVertexArrays(1, &DummyVertexArray);
glBindVertexArray(DummyVertexArray);
return(Context);
}
static void OpenGL_BeginFrame(vn_render_commands *RenderCommands, v2 RenderDim)
{
RenderCommands->PushBufferAt = RenderCommands->PushBufferBase;
RenderCommands->QuadVertexCount = 0;
RenderCommands->QuadIndexCount = 0;
RenderCommands->RenderDim = RenderDim;
}
static void OpenGL_EndFrame(opengl_context *Context, vn_render_commands *RenderCommands)
{
glViewport(0, 0, RenderCommands->RenderDim.x, RenderCommands->RenderDim.y);
glScissor(0, 0, RenderCommands->RenderDim.x, RenderCommands->RenderDim.y);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_SCISSOR_TEST);
glBindBuffer(GL_ARRAY_BUFFER, Context->VertexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, Context->IndexBuffer);
void *VertexData = RenderCommands->QuadVertexBase;
umm VertexSize = RenderCommands->QuadVertexCount*sizeof(quad_vertex);
glBufferSubData(GL_ARRAY_BUFFER, 0, VertexSize, VertexData);
void *IndexData = RenderCommands->QuadIndexBase;
umm IndexSize = RenderCommands->QuadIndexCount*sizeof(s32);
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, IndexSize, IndexData);
OpenGL_BeginProgram(&Context->QuadProgram);
glUniform2f(Context->QuadProgram.UniformResolutionLocation,
RenderCommands->RenderDim.x, RenderCommands->RenderDim.y);
for(u8 *PushBufferAt = RenderCommands->PushBufferBase;
PushBufferAt < RenderCommands->PushBufferAt;)
{
render_command_header *Header = (render_command_header *)PushBufferAt;
PushBufferAt += sizeof(*Header);
switch(Header->Type)
{
case Render_Command_render_command_clear:
{
render_command_clear *Command = (render_command_clear *)PushBufferAt;
PushBufferAt += sizeof(*Command);
glClearColor(Command->Color.r, Command->Color.g, Command->Color.b, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
} break;
case Render_Command_render_command_quads:
{
render_command_quads *Command = (render_command_quads *)PushBufferAt;
PushBufferAt += sizeof(*Command);
for(s32 TextureIndex = 0;
TextureIndex < Command->TexturesUsed;
++TextureIndex)
{
opengl_texture Texture = OpenGL_GetTextureFromHandle(Command->Textures[TextureIndex]);
glActiveTexture(GL_TEXTURE0 + TextureIndex);
glBindTexture(GL_TEXTURE_2D, Texture.ID);
}
glDrawElements(GL_TRIANGLES, Command->QuadCount*6, GL_UNSIGNED_INT, 0);
glBindTexture(GL_TEXTURE_2D, 0);
} break;
case Render_Command_render_command_clip:
{
render_command_clip *Command = (render_command_clip *)PushBufferAt;
PushBufferAt += sizeof(*Command);
#if 0
glScissor(Command->ClipRect.P.x, RenderCommands->RenderDim.y - Command->ClipRect.P.y - Command->ClipRect.Dim.y,
Command->ClipRect.Dim.x, Command->ClipRect.Dim.y);
#endif
} break;
}
}
OpenGL_EndProgram(&Context->QuadProgram);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}