452 lines
15 KiB
C
452 lines
15 KiB
C
#include <kinc/graphics4/compute.h>
|
|
#include <kinc/graphics4/graphics.h>
|
|
#include <kinc/graphics4/rendertarget.h>
|
|
#include <kinc/graphics4/texture.h>
|
|
#include <kinc/image.h>
|
|
#include <kinc/log.h>
|
|
#include <kinc/math/core.h>
|
|
|
|
#include <kinc/backend/graphics4/ogl.h>
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#if defined(KORE_WINDOWS) || (defined(KORE_LINUX) && defined(GL_VERSION_4_3)) || (defined(KORE_ANDROID) && defined(GL_ES_VERSION_3_1))
|
|
#define HAS_COMPUTE
|
|
bool kinc_internal_gl_has_compute = true;
|
|
#else
|
|
bool kinc_internal_gl_has_compute = false;
|
|
#endif
|
|
|
|
#ifdef HAS_COMPUTE
|
|
static int convertInternalImageFormat(kinc_image_format_t format) {
|
|
switch (format) {
|
|
case KINC_IMAGE_FORMAT_RGBA128:
|
|
return GL_RGBA32F;
|
|
case KINC_IMAGE_FORMAT_RGBA64:
|
|
return GL_RGBA16F;
|
|
case KINC_IMAGE_FORMAT_RGBA32:
|
|
default:
|
|
return GL_RGBA8;
|
|
case KINC_IMAGE_FORMAT_A32:
|
|
return GL_R32F;
|
|
case KINC_IMAGE_FORMAT_A16:
|
|
return GL_R16F;
|
|
case KINC_IMAGE_FORMAT_GREY8:
|
|
return GL_R8;
|
|
}
|
|
}
|
|
|
|
static int convertInternalRTFormat(kinc_g4_render_target_format_t format) {
|
|
switch (format) {
|
|
case KINC_G4_RENDER_TARGET_FORMAT_64BIT_FLOAT:
|
|
return GL_RGBA16F;
|
|
case KINC_G4_RENDER_TARGET_FORMAT_32BIT_RED_FLOAT:
|
|
return GL_R32F;
|
|
case KINC_G4_RENDER_TARGET_FORMAT_128BIT_FLOAT:
|
|
return GL_RGBA32F;
|
|
case KINC_G4_RENDER_TARGET_FORMAT_16BIT_DEPTH:
|
|
return GL_DEPTH_COMPONENT16;
|
|
case KINC_G4_RENDER_TARGET_FORMAT_8BIT_RED:
|
|
return GL_RED;
|
|
case KINC_G4_RENDER_TARGET_FORMAT_16BIT_RED_FLOAT:
|
|
return GL_R16F;
|
|
case KINC_G4_RENDER_TARGET_FORMAT_32BIT:
|
|
default:
|
|
return GL_RGBA;
|
|
}
|
|
}
|
|
|
|
static void setTextureAddressingInternal(GLenum target, kinc_compute_texture_unit_t unit, kinc_g4_texture_direction_t dir,
|
|
kinc_g4_texture_addressing_t addressing) {
|
|
glActiveTexture(GL_TEXTURE0 + unit.impl.unit);
|
|
GLenum texDir = dir == KINC_G4_TEXTURE_DIRECTION_U ? GL_TEXTURE_WRAP_S : (KINC_G4_TEXTURE_DIRECTION_V ? GL_TEXTURE_WRAP_T : GL_TEXTURE_WRAP_R);
|
|
switch (addressing) {
|
|
case KINC_G4_TEXTURE_ADDRESSING_CLAMP:
|
|
glTexParameteri(target, texDir, GL_CLAMP_TO_EDGE);
|
|
break;
|
|
case KINC_G4_TEXTURE_ADDRESSING_REPEAT:
|
|
default:
|
|
glTexParameteri(target, texDir, GL_REPEAT);
|
|
break;
|
|
}
|
|
glCheckErrors();
|
|
}
|
|
|
|
static void setTextureMagnificationFilterInternal(GLenum target, kinc_compute_texture_unit_t unit, kinc_g4_texture_filter_t filter) {
|
|
glActiveTexture(GL_TEXTURE0 + unit.impl.unit);
|
|
switch (filter) {
|
|
case KINC_G4_TEXTURE_FILTER_POINT:
|
|
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
break;
|
|
case KINC_G4_TEXTURE_FILTER_LINEAR:
|
|
case KINC_G4_TEXTURE_FILTER_ANISOTROPIC:
|
|
default:
|
|
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
break;
|
|
}
|
|
glCheckErrors();
|
|
}
|
|
|
|
static kinc_g4_texture_filter_t minFilters[32] = {KINC_G4_TEXTURE_FILTER_POINT};
|
|
static kinc_g4_mipmap_filter_t mipFilters[32] = {KINC_G4_MIPMAP_FILTER_NONE};
|
|
|
|
static void setMinMipFilters(GLenum target, int unit) {
|
|
glActiveTexture(GL_TEXTURE0 + unit);
|
|
switch (minFilters[unit]) {
|
|
case KINC_G4_TEXTURE_FILTER_POINT:
|
|
switch (mipFilters[unit]) {
|
|
case KINC_G4_MIPMAP_FILTER_NONE:
|
|
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
break;
|
|
case KINC_G4_MIPMAP_FILTER_POINT:
|
|
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
|
|
break;
|
|
case KINC_G4_MIPMAP_FILTER_LINEAR:
|
|
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
|
|
break;
|
|
}
|
|
break;
|
|
case KINC_G4_TEXTURE_FILTER_LINEAR:
|
|
case KINC_G4_TEXTURE_FILTER_ANISOTROPIC:
|
|
switch (mipFilters[unit]) {
|
|
case KINC_G4_MIPMAP_FILTER_NONE:
|
|
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
break;
|
|
case KINC_G4_MIPMAP_FILTER_POINT:
|
|
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
|
|
break;
|
|
case KINC_G4_MIPMAP_FILTER_LINEAR:
|
|
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
|
break;
|
|
}
|
|
if (minFilters[unit] == KINC_G4_TEXTURE_FILTER_ANISOTROPIC) {
|
|
float maxAniso = 0.0f;
|
|
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAniso);
|
|
glTexParameterf(target, GL_TEXTURE_MAX_ANISOTROPY_EXT, maxAniso);
|
|
}
|
|
break;
|
|
}
|
|
glCheckErrors();
|
|
}
|
|
#endif
|
|
|
|
void kinc_compute_shader_init(kinc_compute_shader_t *shader, void *source, int length) {
|
|
shader->impl._length = length;
|
|
shader->impl.textureCount = 0;
|
|
shader->impl._source = (char *)malloc(sizeof(char) * (length + 1));
|
|
for (int i = 0; i < length; ++i) {
|
|
shader->impl._source[i] = ((char *)source)[i];
|
|
}
|
|
shader->impl._source[length] = 0;
|
|
|
|
#ifdef HAS_COMPUTE
|
|
shader->impl._id = glCreateShader(GL_COMPUTE_SHADER);
|
|
glCheckErrors();
|
|
glShaderSource(shader->impl._id, 1, (const GLchar **)&shader->impl._source, NULL);
|
|
glCompileShader(shader->impl._id);
|
|
|
|
int result;
|
|
glGetShaderiv(shader->impl._id, GL_COMPILE_STATUS, &result);
|
|
if (result != GL_TRUE) {
|
|
int length;
|
|
glGetShaderiv(shader->impl._id, GL_INFO_LOG_LENGTH, &length);
|
|
char *errormessage = (char *)malloc(sizeof(char) * length);
|
|
glGetShaderInfoLog(shader->impl._id, length, NULL, errormessage);
|
|
kinc_log(KINC_LOG_LEVEL_ERROR, "GLSL compiler error: %s\n", errormessage);
|
|
free(errormessage);
|
|
}
|
|
|
|
shader->impl._programid = glCreateProgram();
|
|
glAttachShader(shader->impl._programid, shader->impl._id);
|
|
glLinkProgram(shader->impl._programid);
|
|
|
|
glGetProgramiv(shader->impl._programid, GL_LINK_STATUS, &result);
|
|
if (result != GL_TRUE) {
|
|
int length;
|
|
glGetProgramiv(shader->impl._programid, GL_INFO_LOG_LENGTH, &length);
|
|
char *errormessage = (char *)malloc(sizeof(char) * length);
|
|
glGetProgramInfoLog(shader->impl._programid, length, NULL, errormessage);
|
|
kinc_log(KINC_LOG_LEVEL_ERROR, "GLSL linker error: %s\n", errormessage);
|
|
free(errormessage);
|
|
}
|
|
#endif
|
|
|
|
// TODO: Get rid of allocations
|
|
shader->impl.textures = (char **)malloc(sizeof(char *) * 16);
|
|
for (int i = 0; i < 16; ++i) {
|
|
shader->impl.textures[i] = (char *)malloc(sizeof(char) * 128);
|
|
shader->impl.textures[i][0] = 0;
|
|
}
|
|
shader->impl.textureValues = (int *)malloc(sizeof(int) * 16);
|
|
}
|
|
|
|
void kinc_compute_shader_destroy(kinc_compute_shader_t *shader) {
|
|
free(shader->impl._source);
|
|
shader->impl._source = NULL;
|
|
#ifdef HAS_COMPUTE
|
|
glDeleteProgram(shader->impl._programid);
|
|
glDeleteShader(shader->impl._id);
|
|
#endif
|
|
}
|
|
kinc_compute_constant_location_t kinc_compute_shader_get_constant_location(kinc_compute_shader_t *shader, const char *name) {
|
|
kinc_compute_constant_location_t location;
|
|
#ifdef HAS_COMPUTE
|
|
location.impl.location = glGetUniformLocation(shader->impl._programid, name);
|
|
location.impl.type = GL_FLOAT;
|
|
GLint count = 0;
|
|
glGetProgramiv(shader->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(shader->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);
|
|
}
|
|
#endif
|
|
return location;
|
|
}
|
|
|
|
static int findTexture(kinc_compute_shader_t *shader, const char *name) {
|
|
for (int index = 0; index < shader->impl.textureCount; ++index) {
|
|
if (strcmp(shader->impl.textures[index], name) == 0) return index;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
kinc_compute_texture_unit_t kinc_compute_shader_get_texture_unit(kinc_compute_shader_t *shader, const char *name) {
|
|
int index = findTexture(shader, name);
|
|
if (index < 0) {
|
|
int location = glGetUniformLocation(shader->impl._programid, name);
|
|
glCheckErrors();
|
|
index = shader->impl.textureCount;
|
|
shader->impl.textureValues[index] = location;
|
|
strcpy(shader->impl.textures[index], name);
|
|
++shader->impl.textureCount;
|
|
}
|
|
kinc_compute_texture_unit_t unit;
|
|
unit.impl.unit = index;
|
|
return unit;
|
|
}
|
|
|
|
void kinc_compute_set_bool(kinc_compute_constant_location_t location, bool value) {
|
|
#ifdef HAS_COMPUTE
|
|
glUniform1i(location.impl.location, value ? 1 : 0);
|
|
glCheckErrors();
|
|
#endif
|
|
}
|
|
|
|
void kinc_compute_set_int(kinc_compute_constant_location_t location, int value) {
|
|
#ifdef HAS_COMPUTE
|
|
glUniform1i(location.impl.location, value);
|
|
glCheckErrors();
|
|
#endif
|
|
}
|
|
|
|
void kinc_compute_set_float(kinc_compute_constant_location_t location, float value) {
|
|
#ifdef HAS_COMPUTE
|
|
glUniform1f(location.impl.location, value);
|
|
glCheckErrors();
|
|
#endif
|
|
}
|
|
|
|
void kinc_compute_set_float2(kinc_compute_constant_location_t location, float value1, float value2) {
|
|
#ifdef HAS_COMPUTE
|
|
glUniform2f(location.impl.location, value1, value2);
|
|
glCheckErrors();
|
|
#endif
|
|
}
|
|
|
|
void kinc_compute_set_float3(kinc_compute_constant_location_t location, float value1, float value2, float value3) {
|
|
#ifdef HAS_COMPUTE
|
|
glUniform3f(location.impl.location, value1, value2, value3);
|
|
glCheckErrors();
|
|
#endif
|
|
}
|
|
|
|
void kinc_compute_set_float4(kinc_compute_constant_location_t location, float value1, float value2, float value3, float value4) {
|
|
#ifdef HAS_COMPUTE
|
|
glUniform4f(location.impl.location, value1, value2, value3, value4);
|
|
glCheckErrors();
|
|
#endif
|
|
}
|
|
|
|
void kinc_compute_set_floats(kinc_compute_constant_location_t location, float *values, int count) {
|
|
#ifdef HAS_COMPUTE
|
|
switch (location.impl.type) {
|
|
case GL_FLOAT_VEC2:
|
|
glUniform2fv(location.impl.location, count / 2, values);
|
|
break;
|
|
case GL_FLOAT_VEC3:
|
|
glUniform3fv(location.impl.location, count / 3, values);
|
|
break;
|
|
case GL_FLOAT_VEC4:
|
|
glUniform4fv(location.impl.location, count / 4, values);
|
|
break;
|
|
case GL_FLOAT_MAT4:
|
|
glUniformMatrix4fv(location.impl.location, count / 16, false, values);
|
|
break;
|
|
default:
|
|
glUniform1fv(location.impl.location, count, values);
|
|
break;
|
|
}
|
|
glCheckErrors();
|
|
#endif
|
|
}
|
|
|
|
void kinc_compute_set_matrix4(kinc_compute_constant_location_t location, kinc_matrix4x4_t *value) {
|
|
#ifdef HAS_COMPUTE
|
|
glUniformMatrix4fv(location.impl.location, 1, GL_FALSE, &value->m[0]);
|
|
glCheckErrors();
|
|
#endif
|
|
}
|
|
|
|
void kinc_compute_set_matrix3(kinc_compute_constant_location_t location, kinc_matrix3x3_t *value) {
|
|
#ifdef HAS_COMPUTE
|
|
glUniformMatrix3fv(location.impl.location, 1, GL_FALSE, &value->m[0]);
|
|
glCheckErrors();
|
|
#endif
|
|
}
|
|
|
|
void kinc_compute_set_buffer(kinc_shader_storage_buffer_t *buffer, int index) {
|
|
#ifdef HAS_COMPUTE
|
|
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, index, buffer->impl.bufferId);
|
|
glCheckErrors();
|
|
#endif
|
|
}
|
|
|
|
void kinc_compute_set_texture(kinc_compute_texture_unit_t unit, kinc_g4_texture_t *texture, kinc_compute_access_t access) {
|
|
#ifdef HAS_COMPUTE
|
|
glActiveTexture(GL_TEXTURE0 + unit.impl.unit);
|
|
glCheckErrors();
|
|
GLenum glaccess = access == KINC_COMPUTE_ACCESS_READ ? GL_READ_ONLY : (access == KINC_COMPUTE_ACCESS_WRITE ? GL_WRITE_ONLY : GL_READ_WRITE);
|
|
glBindImageTexture(unit.impl.unit, texture->impl.texture, 0, GL_FALSE, 0, glaccess, convertInternalImageFormat(texture->format));
|
|
glCheckErrors();
|
|
#endif
|
|
}
|
|
|
|
void kinc_compute_set_render_target(kinc_compute_texture_unit_t unit, kinc_g4_render_target_t *texture, kinc_compute_access_t access) {
|
|
#ifdef HAS_COMPUTE
|
|
glActiveTexture(GL_TEXTURE0 + unit.impl.unit);
|
|
glCheckErrors();
|
|
GLenum glaccess = access == KINC_COMPUTE_ACCESS_READ ? GL_READ_ONLY : (access == KINC_COMPUTE_ACCESS_WRITE ? GL_WRITE_ONLY : GL_READ_WRITE);
|
|
glBindImageTexture(unit.impl.unit, texture->impl._texture, 0, GL_FALSE, 0, glaccess,
|
|
convertInternalRTFormat((kinc_g4_render_target_format_t)texture->impl.format));
|
|
glCheckErrors();
|
|
#endif
|
|
}
|
|
|
|
void kinc_compute_set_sampled_texture(kinc_compute_texture_unit_t unit, kinc_g4_texture_t *texture) {
|
|
#ifdef HAS_COMPUTE
|
|
glActiveTexture(GL_TEXTURE0 + unit.impl.unit);
|
|
glCheckErrors();
|
|
GLenum gltarget = texture->tex_depth > 1 ? GL_TEXTURE_3D : GL_TEXTURE_2D;
|
|
glBindTexture(gltarget, texture->impl.texture);
|
|
glCheckErrors();
|
|
#endif
|
|
}
|
|
|
|
void kinc_compute_set_sampled_render_target(kinc_compute_texture_unit_t unit, kinc_g4_render_target_t *target) {
|
|
#ifdef HAS_COMPUTE
|
|
glActiveTexture(GL_TEXTURE0 + unit.impl.unit);
|
|
glCheckErrors();
|
|
glBindTexture(target->isCubeMap ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D, target->impl._texture);
|
|
glCheckErrors();
|
|
#endif
|
|
}
|
|
|
|
void kinc_compute_set_sampled_depth_from_render_target(kinc_compute_texture_unit_t unit, kinc_g4_render_target_t *target) {
|
|
#ifdef HAS_COMPUTE
|
|
glActiveTexture(GL_TEXTURE0 + unit.impl.unit);
|
|
glCheckErrors();
|
|
glBindTexture(target->isCubeMap ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D, target->impl._depthTexture);
|
|
glCheckErrors();
|
|
#endif
|
|
}
|
|
|
|
void kinc_compute_set_texture_addressing(kinc_compute_texture_unit_t unit, kinc_g4_texture_direction_t dir, kinc_g4_texture_addressing_t addressing) {
|
|
#ifdef HAS_COMPUTE
|
|
setTextureAddressingInternal(GL_TEXTURE_2D, unit, dir, addressing);
|
|
#endif
|
|
}
|
|
|
|
void kinc_compute_set_texture3d_addressing(kinc_compute_texture_unit_t unit, kinc_g4_texture_direction_t dir, kinc_g4_texture_addressing_t addressing) {
|
|
#ifdef HAS_COMPUTE
|
|
setTextureAddressingInternal(GL_TEXTURE_3D, unit, dir, addressing);
|
|
#endif
|
|
}
|
|
|
|
void kinc_compute_set_texture_magnification_filter(kinc_compute_texture_unit_t unit, kinc_g4_texture_filter_t filter) {
|
|
#ifdef HAS_COMPUTE
|
|
setTextureMagnificationFilterInternal(GL_TEXTURE_2D, unit, filter);
|
|
#endif
|
|
}
|
|
|
|
void kinc_compute_set_texture3d_magnification_filter(kinc_compute_texture_unit_t unit, kinc_g4_texture_filter_t filter) {
|
|
#ifdef HAS_COMPUTE
|
|
setTextureMagnificationFilterInternal(GL_TEXTURE_3D, unit, filter);
|
|
#endif
|
|
}
|
|
|
|
void kinc_compute_set_texture_minification_filter(kinc_compute_texture_unit_t unit, kinc_g4_texture_filter_t filter) {
|
|
#ifdef HAS_COMPUTE
|
|
minFilters[unit.impl.unit] = filter;
|
|
setMinMipFilters(GL_TEXTURE_2D, unit.impl.unit);
|
|
#endif
|
|
}
|
|
|
|
void kinc_compute_set_texture3d_minification_filter(kinc_compute_texture_unit_t unit, kinc_g4_texture_filter_t filter) {
|
|
#ifdef HAS_COMPUTE
|
|
minFilters[unit.impl.unit] = filter;
|
|
setMinMipFilters(GL_TEXTURE_3D, unit.impl.unit);
|
|
#endif
|
|
}
|
|
|
|
void kinc_compute_set_texture_mipmap_filter(kinc_compute_texture_unit_t unit, kinc_g4_mipmap_filter_t filter) {
|
|
#ifdef HAS_COMPUTE
|
|
mipFilters[unit.impl.unit] = filter;
|
|
setMinMipFilters(GL_TEXTURE_2D, unit.impl.unit);
|
|
#endif
|
|
}
|
|
|
|
void kinc_compute_set_texture3d_mipmap_filter(kinc_compute_texture_unit_t unit, kinc_g4_mipmap_filter_t filter) {
|
|
#ifdef HAS_COMPUTE
|
|
mipFilters[unit.impl.unit] = filter;
|
|
setMinMipFilters(GL_TEXTURE_3D, unit.impl.unit);
|
|
#endif
|
|
}
|
|
|
|
void kinc_compute_set_shader(kinc_compute_shader_t *shader) {
|
|
#ifdef HAS_COMPUTE
|
|
glUseProgram(shader->impl._programid);
|
|
glCheckErrors();
|
|
|
|
for (int index = 0; index < shader->impl.textureCount; ++index) {
|
|
glUniform1i(shader->impl.textureValues[index], index);
|
|
glCheckErrors();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void kinc_compute(int x, int y, int z) {
|
|
#ifdef HAS_COMPUTE
|
|
glDispatchCompute(x, y, z);
|
|
glCheckErrors();
|
|
glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
|
|
glCheckErrors();
|
|
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
|
|
glCheckErrors();
|
|
#endif
|
|
}
|