Files
2025-01-29 10:55:49 +01:00

483 lines
15 KiB
C

#include <kinc/global.h>
#include "ogl.h"
#include <kinc/graphics4/graphics.h>
#include <kinc/graphics4/pipeline.h>
#include <kinc/graphics4/shader.h>
#include <kinc/graphics4/vertexstructure.h>
#include <kinc/log.h>
#include <kinc/backend/graphics4/OpenGL.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef KORE_OPENGL_ES
bool Kinc_Internal_ProgramUsesTessellation = false;
#endif
extern bool Kinc_Internal_SupportsConservativeRaster;
static GLenum convertStencilAction(kinc_g4_stencil_action_t action) {
switch (action) {
default:
case KINC_G4_STENCIL_DECREMENT:
return GL_DECR;
case KINC_G4_STENCIL_DECREMENT_WRAP:
return GL_DECR_WRAP;
case KINC_G4_STENCIL_INCREMENT:
return GL_INCR;
case KINC_G4_STENCIL_INCREMENT_WRAP:
return GL_INCR_WRAP;
case KINC_G4_STENCIL_INVERT:
return GL_INVERT;
case KINC_G4_STENCIL_KEEP:
return GL_KEEP;
case KINC_G4_STENCIL_REPLACE:
return GL_REPLACE;
case KINC_G4_STENCIL_ZERO:
return GL_ZERO;
}
}
static GLenum convert_blend_factor(kinc_g4_blending_factor_t factor) {
switch (factor) {
case KINC_G4_BLEND_ZERO:
return GL_ZERO;
case KINC_G4_BLEND_ONE:
return GL_ONE;
case KINC_G4_BLEND_SOURCE_ALPHA:
return GL_SRC_ALPHA;
case KINC_G4_BLEND_DEST_ALPHA:
return GL_DST_ALPHA;
case KINC_G4_BLEND_INV_SOURCE_ALPHA:
return GL_ONE_MINUS_SRC_ALPHA;
case KINC_G4_BLEND_INV_DEST_ALPHA:
return GL_ONE_MINUS_DST_ALPHA;
case KINC_G4_BLEND_SOURCE_COLOR:
return GL_SRC_COLOR;
case KINC_G4_BLEND_DEST_COLOR:
return GL_DST_COLOR;
case KINC_G4_BLEND_INV_SOURCE_COLOR:
return GL_ONE_MINUS_SRC_COLOR;
case KINC_G4_BLEND_INV_DEST_COLOR:
return GL_ONE_MINUS_DST_COLOR;
case KINC_G4_BLEND_CONSTANT:
return GL_CONSTANT_COLOR;
case KINC_G4_BLEND_INV_CONSTANT:
return GL_ONE_MINUS_CONSTANT_COLOR;
default:
assert(false);
return GL_ONE;
}
}
static GLenum convert_blend_operation(kinc_g4_blending_operation_t operation) {
switch (operation) {
case KINC_G4_BLENDOP_ADD:
return GL_FUNC_ADD;
case KINC_G4_BLENDOP_SUBTRACT:
return GL_FUNC_SUBTRACT;
case KINC_G4_BLENDOP_REVERSE_SUBTRACT:
return GL_FUNC_REVERSE_SUBTRACT;
case KINC_G4_BLENDOP_MIN:
return GL_MIN;
case KINC_G4_BLENDOP_MAX:
return GL_MAX;
default:
assert(false);
return GL_FUNC_ADD;
}
}
void kinc_g4_pipeline_init(kinc_g4_pipeline_t *state) {
memset(state, 0, sizeof(kinc_g4_pipeline_t));
kinc_g4_internal_pipeline_set_defaults(state);
state->impl.textureCount = 0;
// TODO: Get rid of allocations
state->impl.textures = (char **)malloc(sizeof(char *) * 16);
for (int i = 0; i < 16; ++i) {
state->impl.textures[i] = (char *)malloc(sizeof(char) * 128);
state->impl.textures[i][0] = 0;
}
state->impl.textureValues = (int *)malloc(sizeof(int) * 16);
state->impl.programId = glCreateProgram();
glCheckErrors();
}
void kinc_g4_pipeline_destroy(kinc_g4_pipeline_t *state) {
for (int i = 0; i < 16; ++i) {
free(state->impl.textures[i]);
}
free(state->impl.textures);
free(state->impl.textureValues);
glDeleteProgram(state->impl.programId);
}
static int toGlShader(kinc_g4_shader_type_t type) {
switch (type) {
case KINC_G4_SHADER_TYPE_VERTEX:
default:
return GL_VERTEX_SHADER;
case KINC_G4_SHADER_TYPE_FRAGMENT:
return GL_FRAGMENT_SHADER;
#ifndef KORE_OPENGL_ES
case KINC_G4_SHADER_TYPE_GEOMETRY:
return GL_GEOMETRY_SHADER;
case KINC_G4_SHADER_TYPE_TESSELLATION_CONTROL:
return GL_TESS_CONTROL_SHADER;
case KINC_G4_SHADER_TYPE_TESSELLATION_EVALUATION:
return GL_TESS_EVALUATION_SHADER;
#endif
}
}
static void compileShader(unsigned *id, const char *source, size_t length, kinc_g4_shader_type_t type) {
*id = glCreateShader(toGlShader(type));
glCheckErrors();
glShaderSource(*id, 1, (const GLchar **)&source, 0);
glCompileShader(*id);
int result;
glGetShaderiv(*id, GL_COMPILE_STATUS, &result);
if (result != GL_TRUE) {
int length = 0;
glGetShaderiv(*id, GL_INFO_LOG_LENGTH, &length);
char *errormessage = (char *)malloc(length);
glGetShaderInfoLog(*id, length, NULL, errormessage);
kinc_log(KINC_LOG_LEVEL_ERROR, "GLSL compiler error: %s", errormessage);
free(errormessage);
}
}
void kinc_g4_pipeline_compile(kinc_g4_pipeline_t *state) {
compileShader(&state->vertex_shader->impl._glid, state->vertex_shader->impl.source, state->vertex_shader->impl.length, KINC_G4_SHADER_TYPE_VERTEX);
compileShader(&state->fragment_shader->impl._glid, state->fragment_shader->impl.source, state->fragment_shader->impl.length, KINC_G4_SHADER_TYPE_FRAGMENT);
#ifndef KORE_OPENGL_ES
if (state->geometry_shader != NULL) {
compileShader(&state->geometry_shader->impl._glid, state->geometry_shader->impl.source, state->geometry_shader->impl.length,
KINC_G4_SHADER_TYPE_GEOMETRY);
}
if (state->tessellation_control_shader != NULL) {
compileShader(&state->tessellation_control_shader->impl._glid, state->tessellation_control_shader->impl.source,
state->tessellation_control_shader->impl.length, KINC_G4_SHADER_TYPE_TESSELLATION_CONTROL);
}
if (state->tessellation_evaluation_shader != NULL) {
compileShader(&state->tessellation_evaluation_shader->impl._glid, state->tessellation_evaluation_shader->impl.source,
state->tessellation_evaluation_shader->impl.length, KINC_G4_SHADER_TYPE_TESSELLATION_EVALUATION);
}
#endif
glAttachShader(state->impl.programId, state->vertex_shader->impl._glid);
glAttachShader(state->impl.programId, state->fragment_shader->impl._glid);
#ifndef KORE_OPENGL_ES
if (state->geometry_shader != NULL) {
glAttachShader(state->impl.programId, state->geometry_shader->impl._glid);
}
if (state->tessellation_control_shader != NULL) {
glAttachShader(state->impl.programId, state->tessellation_control_shader->impl._glid);
}
if (state->tessellation_evaluation_shader != NULL) {
glAttachShader(state->impl.programId, state->tessellation_evaluation_shader->impl._glid);
}
#endif
glCheckErrors();
int index = 0;
for (int i1 = 0; state->input_layout[i1] != NULL; ++i1) {
for (int i2 = 0; i2 < state->input_layout[i1]->size; ++i2) {
kinc_g4_vertex_element_t element = state->input_layout[i1]->elements[i2];
glBindAttribLocation(state->impl.programId, index, element.name);
glCheckErrors();
if (element.data == KINC_G4_VERTEX_DATA_F32_4X4) {
index += 4;
}
else {
++index;
}
}
}
glLinkProgram(state->impl.programId);
int result;
glGetProgramiv(state->impl.programId, GL_LINK_STATUS, &result);
if (result != GL_TRUE) {
int length = 0;
glGetProgramiv(state->impl.programId, GL_INFO_LOG_LENGTH, &length);
char *errormessage = (char *)malloc(length);
glGetProgramInfoLog(state->impl.programId, length, NULL, errormessage);
kinc_log(KINC_LOG_LEVEL_ERROR, "GLSL linker error: %s", errormessage);
free(errormessage);
}
#ifndef KORE_OPENGL_ES
#ifndef KORE_LINUX
if (state->tessellation_control_shader != NULL) {
glPatchParameteri(GL_PATCH_VERTICES, 3);
glCheckErrors();
}
#endif
#endif
}
void kinc_g4_internal_set_pipeline(kinc_g4_pipeline_t *pipeline) {
#ifndef KORE_OPENGL_ES
Kinc_Internal_ProgramUsesTessellation = pipeline->tessellation_control_shader != NULL;
#endif
glUseProgram(pipeline->impl.programId);
glCheckErrors();
for (int index = 0; index < pipeline->impl.textureCount; ++index) {
glUniform1i(pipeline->impl.textureValues[index], index);
glCheckErrors();
}
if (pipeline->stencil_front_mode == KINC_G4_COMPARE_ALWAYS && pipeline->stencil_back_mode == KINC_G4_COMPARE_ALWAYS &&
pipeline->stencil_front_both_pass == KINC_G4_STENCIL_KEEP && pipeline->stencil_back_both_pass == KINC_G4_STENCIL_KEEP &&
pipeline->stencil_front_depth_fail == KINC_G4_STENCIL_KEEP && pipeline->stencil_back_depth_fail == KINC_G4_STENCIL_KEEP &&
pipeline->stencil_front_fail == KINC_G4_STENCIL_KEEP && pipeline->stencil_back_fail == KINC_G4_STENCIL_KEEP) {
glDisable(GL_STENCIL_TEST);
}
else {
glEnable(GL_STENCIL_TEST);
glStencilMaskSeparate(GL_FRONT, pipeline->stencil_write_mask);
glStencilOpSeparate(GL_FRONT, convertStencilAction(pipeline->stencil_front_fail), convertStencilAction(pipeline->stencil_front_depth_fail),
convertStencilAction(pipeline->stencil_front_both_pass));
glStencilFuncSeparate(GL_FRONT, Kinc_G4_Internal_StencilFunc(pipeline->stencil_front_mode), pipeline->stencil_reference_value,
pipeline->stencil_read_mask);
glStencilMaskSeparate(GL_BACK, pipeline->stencil_write_mask);
glStencilOpSeparate(GL_BACK, convertStencilAction(pipeline->stencil_back_fail), convertStencilAction(pipeline->stencil_back_depth_fail),
convertStencilAction(pipeline->stencil_back_both_pass));
glStencilFuncSeparate(GL_BACK, Kinc_G4_Internal_StencilFunc(pipeline->stencil_back_mode), pipeline->stencil_reference_value,
pipeline->stencil_read_mask);
}
#ifdef KORE_OPENGL_ES
glColorMask(pipeline->color_write_mask_red[0], pipeline->color_write_mask_green[0], pipeline->color_write_mask_blue[0],
pipeline->color_write_mask_alpha[0]);
#else
for (int i = 0; i < 8; ++i)
glColorMaski(i, pipeline->color_write_mask_red[i], pipeline->color_write_mask_green[i], pipeline->color_write_mask_blue[i],
pipeline->color_write_mask_alpha[i]);
#endif
if (Kinc_Internal_SupportsConservativeRaster) {
if (pipeline->conservative_rasterization) {
glEnable(0x9346); // GL_CONSERVATIVE_RASTERIZATION_NV
}
else {
glDisable(0x9346);
}
}
glCheckErrors();
/*switch (state) {
case Normalize:
device->SetRenderState(D3DRS_NORMALIZENORMALS, on ? TRUE : FALSE);
break;
case BackfaceCulling:
if (on) device->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);
else device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
break;
case FogState:
device->SetRenderState(D3DRS_FOGENABLE, on ? TRUE : FALSE);
break;
case ScissorTestState:
device->SetRenderState(D3DRS_SCISSORTESTENABLE, on ? TRUE : FALSE);
break;
case AlphaTestState:
device->SetRenderState(D3DRS_ALPHATESTENABLE, on ? TRUE : FALSE);
device->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL);
break;
default:
throw Exception();
}*/
if (pipeline->depth_write) {
glDepthMask(GL_TRUE);
}
else {
glDepthMask(GL_FALSE);
}
if (pipeline->depth_mode != KINC_G4_COMPARE_ALWAYS) {
glEnable(GL_DEPTH_TEST);
}
else {
glDisable(GL_DEPTH_TEST);
}
GLenum func = GL_ALWAYS;
switch (pipeline->depth_mode) {
default:
case KINC_G4_COMPARE_ALWAYS:
func = GL_ALWAYS;
break;
case KINC_G4_COMPARE_NEVER:
func = GL_NEVER;
break;
case KINC_G4_COMPARE_EQUAL:
func = GL_EQUAL;
break;
case KINC_G4_COMPARE_NOT_EQUAL:
func = GL_NOTEQUAL;
break;
case KINC_G4_COMPARE_LESS:
func = GL_LESS;
break;
case KINC_G4_COMPARE_LESS_EQUAL:
func = GL_LEQUAL;
break;
case KINC_G4_COMPARE_GREATER:
func = GL_GREATER;
break;
case KINC_G4_COMPARE_GREATER_EQUAL:
func = GL_GEQUAL;
break;
}
glDepthFunc(func);
glCheckErrors();
switch (pipeline->cull_mode) {
case KINC_G4_CULL_CLOCKWISE:
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
glCheckErrors();
break;
case KINC_G4_CULL_COUNTER_CLOCKWISE:
glEnable(GL_CULL_FACE);
glCullFace(GL_FRONT);
glCheckErrors();
break;
case KINC_G4_CULL_NOTHING:
glDisable(GL_CULL_FACE);
glCheckErrors();
break;
default:
break;
}
/*switch (state) {
case DepthTestCompare:
switch (v) {
// TODO: Cmp-Konstanten systemabhaengig abgleichen
default:
case ZCmp_Always : v = D3DCMP_ALWAYS; break;
case ZCmp_Never : v = D3DCMP_NEVER; break;
case ZCmp_Equal : v = D3DCMP_EQUAL; break;
case ZCmp_NotEqual : v = D3DCMP_NOTEQUAL; break;
case ZCmp_Less : v = D3DCMP_LESS; break;
case ZCmp_LessEqual : v = D3DCMP_LESSEQUAL; break;
case ZCmp_Greater : v = D3DCMP_GREATER; break;
case ZCmp_GreaterEqual: v = D3DCMP_GREATEREQUAL; break;
}
device->SetRenderState(D3DRS_ZFUNC, v);
break;
case FogTypeState:
switch (v) {
case LinearFog:
device->SetRenderState(D3DRS_FOGVERTEXMODE, D3DFOG_LINEAR);
}
break;
case AlphaReferenceState:
device->SetRenderState(D3DRS_ALPHAREF, (DWORD)v);
break;
default:
throw Exception();
}*/
if (pipeline->blend_source != KINC_G4_BLEND_ONE || pipeline->blend_destination != KINC_G4_BLEND_ZERO || pipeline->alpha_blend_source != KINC_G4_BLEND_ONE ||
pipeline->alpha_blend_destination != KINC_G4_BLEND_ZERO) {
glEnable(GL_BLEND);
}
else {
glDisable(GL_BLEND);
}
// glBlendFunc(convert(pipeline->blendSource), convert(pipeline->blendDestination));
glBlendFuncSeparate(convert_blend_factor(pipeline->blend_source), convert_blend_factor(pipeline->blend_destination),
convert_blend_factor(pipeline->alpha_blend_source), convert_blend_factor(pipeline->alpha_blend_destination));
glBlendEquationSeparate(convert_blend_operation(pipeline->blend_operation), convert_blend_operation(pipeline->alpha_blend_operation));
}
void kinc_g4_pipeline_get_constant_locations(kinc_g4_pipeline_t *state, kinc_g4_constant_location_t *vertex_locations,
kinc_g4_constant_location_t *fragment_locations, int *vertex_sizes, int *fragment_sizes, int *max_vertex,
int *max_fragment) {
// GLint count = 0;
// glGetProgramiv(state->impl.programId, GL_ACTIVE_UNIFORMS, &count);
// if (locations == NULL || sizes == NULL) {
// *max_count = count;
// }
// else {
// for (GLint i = 0; i < count; ++i) {
// GLenum type;
// char uniformName[1024];
// GLsizei length;
// GLint size;
// glGetActiveUniform(state->impl.programId, i, 1024 - 1, &length, &size, &type, uniformName);
// locations[i].impl.location = glGetUniformLocation(state->impl.programId, uniformName);
// locations[i].impl.type = type;
// sizes[i] = size;
// }
// }
}
kinc_g4_constant_location_t kinc_g4_pipeline_get_constant_location(kinc_g4_pipeline_t *state, const char *name) {
kinc_g4_constant_location_t location;
location.impl.location = glGetUniformLocation(state->impl.programId, name);
location.impl.type = GL_FLOAT;
GLint count = 0;
glGetProgramiv(state->impl.programId, GL_ACTIVE_UNIFORMS, &count);
char arrayName[1024];
strcpy(arrayName, name);
strcat(arrayName, "[0]");
for (GLint i = 0; i < count; ++i) {
GLenum type;
char uniformName[1024];
GLsizei length;
GLint size;
glGetActiveUniform(state->impl.programId, i, 1024 - 1, &length, &size, &type, uniformName);
if (strcmp(uniformName, name) == 0 || strcmp(uniformName, arrayName) == 0) {
location.impl.type = type;
break;
}
}
glCheckErrors();
if (location.impl.location < 0) {
kinc_log(KINC_LOG_LEVEL_WARNING, "Uniform %s not found.", name);
}
return location;
}
static int findTexture(kinc_g4_pipeline_t *state, const char *name) {
for (int index = 0; index < state->impl.textureCount; ++index) {
if (strcmp(state->impl.textures[index], name) == 0)
return index;
}
return -1;
}
kinc_g4_texture_unit_t kinc_g4_pipeline_get_texture_unit(kinc_g4_pipeline_t *state, const char *name) {
int index = findTexture(state, name);
if (index < 0) {
int location = glGetUniformLocation(state->impl.programId, name);
glCheckErrors();
index = state->impl.textureCount;
state->impl.textureValues[index] = location;
strcpy(state->impl.textures[index], name);
++state->impl.textureCount;
}
kinc_g4_texture_unit_t unit;
for (int i = 0; i < KINC_G4_SHADER_TYPE_COUNT; ++i) {
unit.stages[i] = -1;
}
unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] = index;
return unit;
}