1278 lines
39 KiB
C
1278 lines
39 KiB
C
#include "OpenGL.h"
|
|
#include "ogl.h"
|
|
#ifdef KINC_EGL
|
|
#define EGL_NO_PLATFORM_SPECIFIC_TYPES
|
|
#include <EGL/egl.h>
|
|
#endif
|
|
|
|
#include <kinc/backend/graphics4/vertexbuffer.h>
|
|
|
|
#include <kinc/graphics4/constantbuffer.h>
|
|
#include <kinc/graphics4/indexbuffer.h>
|
|
#include <kinc/graphics4/pipeline.h>
|
|
#include <kinc/graphics4/rendertarget.h>
|
|
#include <kinc/graphics4/texture.h>
|
|
#include <kinc/graphics4/texturearray.h>
|
|
#include <kinc/graphics4/vertexbuffer.h>
|
|
|
|
#include <kinc/error.h>
|
|
#include <kinc/log.h>
|
|
#include <kinc/math/core.h>
|
|
#include <kinc/system.h>
|
|
#include <kinc/window.h>
|
|
|
|
#include "OpenGLWindow.h"
|
|
|
|
#ifdef KORE_WINDOWS
|
|
#include <kinc/backend/Windows.h>
|
|
#endif
|
|
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#ifdef KORE_IOS
|
|
#include <OpenGLES/ES2/glext.h>
|
|
#endif
|
|
|
|
#ifdef KORE_WINDOWS
|
|
#include <GL/wglew.h>
|
|
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#define NOMINMAX
|
|
#include <Windows.h>
|
|
|
|
#pragma comment(lib, "opengl32.lib")
|
|
#pragma comment(lib, "glu32.lib")
|
|
#endif
|
|
|
|
#ifndef GL_MAX_COLOR_ATTACHMENTS
|
|
#define GL_MAX_COLOR_ATTACHMENTS 0x8CDF
|
|
#endif
|
|
#ifndef GL_TEXTURE_MAX_ANISOTROPY_EXT
|
|
#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE
|
|
#endif
|
|
#ifndef GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT
|
|
#define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF
|
|
#endif
|
|
#ifndef GL_TEXTURE_COMPARE_MODE
|
|
#define GL_TEXTURE_COMPARE_MODE 0x884C
|
|
#endif
|
|
#ifndef GL_TEXTURE_COMPARE_FUNC
|
|
#define GL_TEXTURE_COMPARE_FUNC 0x884D
|
|
#endif
|
|
#ifndef GL_COMPARE_REF_TO_TEXTURE
|
|
#define GL_COMPARE_REF_TO_TEXTURE 0x884E
|
|
#endif
|
|
|
|
#if !defined(KORE_IOS) && !defined(KORE_ANDROID)
|
|
bool Kinc_Internal_ProgramUsesTessellation;
|
|
#endif
|
|
bool Kinc_Internal_SupportsConservativeRaster = false;
|
|
bool Kinc_Internal_SupportsDepthTexture = true;
|
|
|
|
#if defined(KORE_OPENGL_ES) && defined(KORE_ANDROID) && KORE_ANDROID_API >= 18
|
|
void *glesVertexAttribDivisor;
|
|
|
|
GL_APICALL void (*GL_APIENTRY glesGenQueries)(GLsizei n, GLuint *ids);
|
|
GL_APICALL void (*GL_APIENTRY glesDeleteQueries)(GLsizei n, const GLuint *ids);
|
|
GL_APICALL void (*GL_APIENTRY glesBeginQuery)(GLenum target, GLuint id);
|
|
GL_APICALL void (*GL_APIENTRY glesEndQuery)(GLenum target);
|
|
GL_APICALL void (*GL_APIENTRY glesGetQueryObjectuiv)(GLuint id, GLenum pname, GLuint *params);
|
|
#endif
|
|
|
|
#if defined(KORE_WINDOWS) && !defined(NDEBUG)
|
|
static void __stdcall debugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam) {
|
|
kinc_log(KINC_LOG_LEVEL_INFO, "OpenGL: %s", message);
|
|
}
|
|
#endif
|
|
|
|
#ifdef KORE_WINDOWS
|
|
static HINSTANCE instance = 0;
|
|
#endif
|
|
|
|
static int currentWindow = 0;
|
|
|
|
static kinc_g4_texture_filter_t minFilters[32];
|
|
static kinc_g4_mipmap_filter_t mipFilters[32];
|
|
|
|
static int _renderTargetWidth;
|
|
static int _renderTargetHeight;
|
|
static bool renderToBackbuffer;
|
|
|
|
static int maxColorAttachments;
|
|
|
|
static kinc_g4_pipeline_t *lastPipeline = NULL;
|
|
|
|
#if defined(KORE_OPENGL_ES) && defined(KORE_ANDROID) && KORE_ANDROID_API >= 18
|
|
static void *glesDrawBuffers;
|
|
static void *glesDrawElementsInstanced;
|
|
#endif
|
|
|
|
static int texModesU[256];
|
|
static int texModesV[256];
|
|
|
|
void kinc_internal_resize(int window, int width, int height) {
|
|
#ifdef KORE_WINDOWS
|
|
Kinc_Internal_resizeWindowRenderTarget(window, width, height);
|
|
#endif
|
|
glViewport(0, 0, width, height);
|
|
}
|
|
|
|
void kinc_internal_change_framebuffer(int window, kinc_framebuffer_options_t *frame) {
|
|
#ifdef KORE_WINDOWS
|
|
if (window == 0) {
|
|
#ifdef KORE_VR
|
|
frame->vertical_sync = false;
|
|
#endif
|
|
if (wglSwapIntervalEXT != NULL)
|
|
wglSwapIntervalEXT(frame->vertical_sync);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifdef KINC_EGL
|
|
static EGLDisplay egl_display = EGL_NO_DISPLAY;
|
|
static EGLContext egl_context = EGL_NO_CONTEXT;
|
|
static EGLConfig egl_config = NULL;
|
|
|
|
struct {
|
|
EGLSurface surface;
|
|
} kinc_egl_windows[16] = {0};
|
|
|
|
EGLDisplay kinc_egl_get_display(void);
|
|
EGLNativeWindowType kinc_egl_get_native_window(EGLDisplay, EGLConfig, int);
|
|
void kinc_egl_init();
|
|
void kinc_egl_init_window(int window);
|
|
void kinc_egl_destroy_window(int window);
|
|
#endif
|
|
|
|
#ifdef KINC_EGL
|
|
#define EGL_CHECK_ERROR() \
|
|
{ \
|
|
EGLint error = eglGetError(); \
|
|
if (error != EGL_SUCCESS) { \
|
|
kinc_log(KINC_LOG_LEVEL_ERROR, "EGL Error at line %i: %i", __LINE__, error); \
|
|
kinc_debug_break(); \
|
|
exit(1); \
|
|
} \
|
|
}
|
|
#endif
|
|
|
|
void kinc_g4_internal_destroy() {
|
|
#ifdef KINC_EGL
|
|
if (egl_display != EGL_NO_DISPLAY) {
|
|
eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
|
for (int i = 0; i < 16; i++) {
|
|
if (kinc_egl_windows[i].surface != EGL_NO_SURFACE) {
|
|
eglDestroySurface(egl_display, kinc_egl_windows[i].surface);
|
|
kinc_egl_windows[i].surface = EGL_NO_SURFACE;
|
|
}
|
|
}
|
|
if (egl_context != EGL_NO_CONTEXT) {
|
|
eglDestroyContext(egl_display, egl_context);
|
|
egl_context = EGL_NO_CONTEXT;
|
|
}
|
|
eglTerminate(egl_display);
|
|
}
|
|
|
|
egl_display = EGL_NO_DISPLAY;
|
|
#endif
|
|
}
|
|
|
|
void kinc_g4_internal_destroy_window(int window) {
|
|
#ifdef KINC_EGL
|
|
kinc_egl_destroy_window(window);
|
|
#endif
|
|
#ifdef KORE_WINDOWS
|
|
if (Kinc_Internal_windows[window].glContext) {
|
|
assert(wglMakeCurrent(NULL, NULL));
|
|
assert(wglDeleteContext(Kinc_Internal_windows[window].glContext));
|
|
Kinc_Internal_windows[window].glContext = NULL;
|
|
}
|
|
|
|
HWND windowHandle = kinc_windows_window_handle(window);
|
|
|
|
if (Kinc_Internal_windows[window].deviceContext != NULL) {
|
|
ReleaseDC(windowHandle, Kinc_Internal_windows[window].deviceContext);
|
|
Kinc_Internal_windows[window].deviceContext = NULL;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifdef CreateWindow
|
|
#undef CreateWindow
|
|
#endif
|
|
|
|
void kinc_g4_internal_init() {
|
|
#ifdef KINC_EGL
|
|
#if !defined(KORE_OPENGL_ES)
|
|
eglBindAPI(EGL_OPENGL_API);
|
|
#else
|
|
eglBindAPI(EGL_OPENGL_ES_API);
|
|
#endif
|
|
kinc_egl_init();
|
|
#endif
|
|
|
|
#ifndef VR_RIFT
|
|
for (int i = 0; i < 32; ++i) {
|
|
minFilters[i] = KINC_G4_TEXTURE_FILTER_LINEAR;
|
|
mipFilters[i] = KINC_G4_MIPMAP_FILTER_NONE;
|
|
}
|
|
#endif
|
|
for (int i = 0; i < 256; ++i) {
|
|
texModesU[i] = GL_CLAMP_TO_EDGE;
|
|
texModesV[i] = GL_CLAMP_TO_EDGE;
|
|
}
|
|
|
|
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &kinc_internal_opengl_max_vertex_attribute_arrays);
|
|
}
|
|
|
|
#ifdef KINC_EGL
|
|
EGLDisplay kinc_egl_get_display(void);
|
|
EGLNativeWindowType kinc_egl_get_native_window(EGLDisplay, EGLConfig, int);
|
|
#endif
|
|
|
|
extern bool kinc_internal_opengl_force_16bit_index_buffer;
|
|
|
|
#ifdef KORE_OPENGL_ES
|
|
int gles_version = 2;
|
|
#endif
|
|
|
|
void kinc_g4_internal_init_window(int windowId, int depthBufferBits, int stencilBufferBits, bool vsync) {
|
|
#ifdef KORE_WINDOWS
|
|
Kinc_Internal_initWindowsGLContext(windowId, depthBufferBits, stencilBufferBits);
|
|
#endif
|
|
#ifdef KINC_EGL
|
|
kinc_egl_init_window(windowId);
|
|
#endif
|
|
|
|
#ifdef KORE_WINDOWS
|
|
if (windowId == 0) {
|
|
#ifdef KORE_VR
|
|
vsync = false;
|
|
#endif
|
|
if (wglSwapIntervalEXT != NULL)
|
|
wglSwapIntervalEXT(vsync);
|
|
}
|
|
#endif
|
|
|
|
renderToBackbuffer = true;
|
|
|
|
#if defined(KORE_OPENGL_ES) && defined(KORE_ANDROID) && KORE_ANDROID_API >= 18
|
|
glesDrawBuffers = (void *)eglGetProcAddress("glDrawBuffers");
|
|
glesDrawElementsInstanced = (void *)eglGetProcAddress("glDrawElementsInstanced");
|
|
glesVertexAttribDivisor = (void *)eglGetProcAddress("glVertexAttribDivisor");
|
|
|
|
glesGenQueries = (void *)eglGetProcAddress("glGenQueries");
|
|
glesDeleteQueries = (void *)eglGetProcAddress("glDeleteQueries");
|
|
glesBeginQuery = (void *)eglGetProcAddress("glBeginQuery");
|
|
glesEndQuery = (void *)eglGetProcAddress("glEndQuery");
|
|
glesGetQueryObjectuiv = (void *)eglGetProcAddress("glGetQueryObjectuiv");
|
|
#endif
|
|
|
|
#if defined(KORE_WINDOWS) && !defined(NDEBUG)
|
|
glEnable(GL_DEBUG_OUTPUT);
|
|
glDebugMessageCallback(debugCallback, NULL);
|
|
#endif
|
|
|
|
#ifndef KORE_OPENGL_ES
|
|
int extensions = 0;
|
|
glGetIntegerv(GL_NUM_EXTENSIONS, &extensions);
|
|
if (glGetError() != GL_NO_ERROR) {
|
|
for (int i = 0; i < extensions; ++i) {
|
|
const char *extension = (const char *)glGetStringi(GL_EXTENSIONS, i);
|
|
if (extension != NULL && strcmp(extension, "GL_NV_conservative_raster") == 0) {
|
|
Kinc_Internal_SupportsConservativeRaster = true;
|
|
}
|
|
}
|
|
}
|
|
maxColorAttachments = 8;
|
|
#endif
|
|
|
|
#ifdef KORE_OPENGL_ES
|
|
{
|
|
int major = -1;
|
|
glGetIntegerv(GL_MAJOR_VERSION, &major);
|
|
glCheckErrors();
|
|
gles_version = major;
|
|
char *exts = (char *)glGetString(GL_EXTENSIONS);
|
|
|
|
Kinc_Internal_SupportsDepthTexture = major >= 3 || (exts != NULL && strstr(exts, "GL_OES_depth_texture") != NULL);
|
|
maxColorAttachments = 4;
|
|
kinc_internal_opengl_force_16bit_index_buffer = major < 3 && strstr(exts, "GL_OES_element_index_uint") == NULL;
|
|
}
|
|
#else
|
|
kinc_internal_opengl_force_16bit_index_buffer = false;
|
|
#endif
|
|
|
|
glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &maxColorAttachments);
|
|
|
|
lastPipeline = NULL;
|
|
|
|
#ifndef KORE_OPENGL_ES
|
|
int major = -1, minor = -1;
|
|
glGetIntegerv(GL_MAJOR_VERSION, &major);
|
|
glGetIntegerv(GL_MINOR_VERSION, &minor);
|
|
if (major < 0 || minor < 0) {
|
|
const GLubyte *version = glGetString(GL_VERSION);
|
|
if (version != NULL) {
|
|
major = version[0] - '0';
|
|
}
|
|
else {
|
|
major = 2;
|
|
}
|
|
minor = 0;
|
|
}
|
|
int gl_version = major * 100 + minor * 10;
|
|
#endif
|
|
|
|
#if defined(KORE_LINUX) || defined(KORE_MACOS)
|
|
if (gl_version >= 300) {
|
|
unsigned vertexArray;
|
|
glGenVertexArrays(1, &vertexArray);
|
|
glCheckErrors();
|
|
glBindVertexArray(vertexArray);
|
|
glCheckErrors();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
bool kinc_window_vsynced(int window) {
|
|
#ifdef KORE_WINDOWS
|
|
return wglGetSwapIntervalEXT();
|
|
#else
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
void kinc_g4_set_bool(kinc_g4_constant_location_t location, bool value) {
|
|
glUniform1i(location.impl.location, value ? 1 : 0);
|
|
glCheckErrors();
|
|
}
|
|
|
|
void kinc_g4_set_int(kinc_g4_constant_location_t location, int value) {
|
|
glUniform1i(location.impl.location, value);
|
|
glCheckErrors();
|
|
}
|
|
|
|
void kinc_g4_set_int2(kinc_g4_constant_location_t location, int value1, int value2) {
|
|
glUniform2i(location.impl.location, value1, value2);
|
|
glCheckErrors();
|
|
}
|
|
|
|
void kinc_g4_set_int3(kinc_g4_constant_location_t location, int value1, int value2, int value3) {
|
|
glUniform3i(location.impl.location, value1, value2, value3);
|
|
glCheckErrors();
|
|
}
|
|
|
|
void kinc_g4_set_int4(kinc_g4_constant_location_t location, int value1, int value2, int value3, int value4) {
|
|
glUniform4i(location.impl.location, value1, value2, value3, value4);
|
|
glCheckErrors();
|
|
}
|
|
|
|
void kinc_g4_set_ints(kinc_g4_constant_location_t location, int *values, int count) {
|
|
switch (location.impl.type) {
|
|
case GL_INT_VEC2:
|
|
glUniform2iv(location.impl.location, count / 2, values);
|
|
break;
|
|
case GL_INT_VEC3:
|
|
glUniform3iv(location.impl.location, count / 3, values);
|
|
break;
|
|
case GL_INT_VEC4:
|
|
glUniform4iv(location.impl.location, count / 4, values);
|
|
break;
|
|
default:
|
|
glUniform1iv(location.impl.location, count, values);
|
|
break;
|
|
}
|
|
glCheckErrors();
|
|
}
|
|
|
|
void kinc_g4_set_float(kinc_g4_constant_location_t location, float value) {
|
|
glUniform1f(location.impl.location, value);
|
|
glCheckErrors();
|
|
}
|
|
|
|
void kinc_g4_set_float2(kinc_g4_constant_location_t location, float value1, float value2) {
|
|
glUniform2f(location.impl.location, value1, value2);
|
|
glCheckErrors();
|
|
}
|
|
|
|
void kinc_g4_set_float3(kinc_g4_constant_location_t location, float value1, float value2, float value3) {
|
|
glUniform3f(location.impl.location, value1, value2, value3);
|
|
glCheckErrors();
|
|
}
|
|
|
|
void kinc_g4_set_float4(kinc_g4_constant_location_t location, float value1, float value2, float value3, float value4) {
|
|
glUniform4f(location.impl.location, value1, value2, value3, value4);
|
|
glCheckErrors();
|
|
}
|
|
|
|
void kinc_g4_set_floats(kinc_g4_constant_location_t location, float *values, int count) {
|
|
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();
|
|
}
|
|
|
|
void kinc_g4_set_matrix4(kinc_g4_constant_location_t location, kinc_matrix4x4_t *value) {
|
|
glUniformMatrix4fv(location.impl.location, 1, GL_FALSE, value->m);
|
|
glCheckErrors();
|
|
}
|
|
|
|
void kinc_g4_set_matrix3(kinc_g4_constant_location_t location, kinc_matrix3x3_t *value) {
|
|
glUniformMatrix3fv(location.impl.location, 1, GL_FALSE, value->m);
|
|
glCheckErrors();
|
|
}
|
|
|
|
kinc_g4_index_buffer_t *Kinc_Internal_CurrentIndexBuffer;
|
|
|
|
void kinc_g4_draw_indexed_vertices() {
|
|
kinc_g4_draw_indexed_vertices_from_to(0, kinc_g4_index_buffer_count(Kinc_Internal_CurrentIndexBuffer));
|
|
}
|
|
|
|
void kinc_g4_draw_indexed_vertices_from_to(int start, int count) {
|
|
bool sixteen = Kinc_Internal_CurrentIndexBuffer->impl.format == KINC_G4_INDEX_BUFFER_FORMAT_16BIT || kinc_internal_opengl_force_16bit_index_buffer;
|
|
GLenum type = sixteen ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT;
|
|
void *_start = sixteen ? (void *)(start * sizeof(uint16_t)) : (void *)(start * sizeof(uint32_t));
|
|
|
|
#ifndef KORE_OPENGL_ES
|
|
if (Kinc_Internal_ProgramUsesTessellation) {
|
|
glDrawElements(GL_PATCHES, count, type, _start);
|
|
glCheckErrors();
|
|
}
|
|
else {
|
|
#endif
|
|
glDrawElements(GL_TRIANGLES, count, type, _start);
|
|
glCheckErrors();
|
|
#ifndef KORE_OPENGL_ES
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void kinc_g4_draw_indexed_vertices_from_to_from(int start, int count, int vertex_offset) {
|
|
bool sixteen = Kinc_Internal_CurrentIndexBuffer->impl.format == KINC_G4_INDEX_BUFFER_FORMAT_16BIT || kinc_internal_opengl_force_16bit_index_buffer;
|
|
GLenum type = sixteen ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT;
|
|
void *_start = sixteen ? (void *)(start * sizeof(uint16_t)) : (void *)(start * sizeof(uint32_t));
|
|
#ifdef KORE_OPENGL_ES
|
|
glDrawElements(GL_TRIANGLES, count, type, _start);
|
|
glCheckErrors();
|
|
#else
|
|
if (Kinc_Internal_ProgramUsesTessellation) {
|
|
glDrawElementsBaseVertex(GL_PATCHES, count, type, _start, vertex_offset);
|
|
glCheckErrors();
|
|
}
|
|
else {
|
|
glDrawElementsBaseVertex(GL_TRIANGLES, count, type, _start, vertex_offset);
|
|
glCheckErrors();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void kinc_g4_draw_indexed_vertices_instanced(int instanceCount) {
|
|
kinc_g4_draw_indexed_vertices_instanced_from_to(instanceCount, 0, kinc_g4_index_buffer_count(Kinc_Internal_CurrentIndexBuffer));
|
|
}
|
|
|
|
void kinc_g4_draw_indexed_vertices_instanced_from_to(int instanceCount, int start, int count) {
|
|
bool sixteen = Kinc_Internal_CurrentIndexBuffer->impl.format == KINC_G4_INDEX_BUFFER_FORMAT_16BIT || kinc_internal_opengl_force_16bit_index_buffer;
|
|
GLenum type = sixteen ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT;
|
|
void *_start = sixteen ? (void *)(start * sizeof(uint16_t)) : (void *)(start * sizeof(uint32_t));
|
|
#if defined(KORE_OPENGL_ES) && defined(KORE_ANDROID) && KORE_ANDROID_API >= 18
|
|
((void (*)(GLenum, GLsizei, GLenum, void *, GLsizei))glesDrawElementsInstanced)(GL_TRIANGLES, count, type, _start, instanceCount);
|
|
#elif defined(KORE_OPENGL_ES) && defined(KORE_WASM)
|
|
glDrawElementsInstanced(GL_TRIANGLES, count, type, _start, instanceCount);
|
|
glCheckErrors();
|
|
#elif !defined(KORE_OPENGL_ES)
|
|
if (Kinc_Internal_ProgramUsesTessellation) {
|
|
glDrawElementsInstanced(GL_PATCHES, count, type, _start, instanceCount);
|
|
glCheckErrors();
|
|
}
|
|
else {
|
|
glDrawElementsInstanced(GL_TRIANGLES, count, type, _start, instanceCount);
|
|
glCheckErrors();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifdef KORE_MACOS
|
|
void swapBuffersMac(int window);
|
|
#endif
|
|
|
|
#ifdef KORE_IOS
|
|
void swapBuffersiOS();
|
|
#endif
|
|
|
|
#ifdef KINC_EGL
|
|
static EGLint egl_major = 0;
|
|
static EGLint egl_minor = 0;
|
|
static int egl_depth_size = 0;
|
|
|
|
void kinc_egl_init() {
|
|
egl_display = kinc_egl_get_display();
|
|
eglInitialize(egl_display, &egl_major, &egl_minor);
|
|
EGL_CHECK_ERROR()
|
|
|
|
// clang-format off
|
|
const EGLint attribs[] = {
|
|
#if !defined(KORE_OPENGL_ES)
|
|
EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
|
|
#else
|
|
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
|
|
#endif
|
|
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
|
|
EGL_BLUE_SIZE, 8,
|
|
EGL_GREEN_SIZE, 8,
|
|
EGL_RED_SIZE, 8,
|
|
EGL_DEPTH_SIZE, 24,
|
|
EGL_STENCIL_SIZE, 8,
|
|
EGL_NONE,
|
|
};
|
|
// clang-format on
|
|
egl_depth_size = 24;
|
|
|
|
EGLint num_configs = 0;
|
|
eglChooseConfig(egl_display, attribs, &egl_config, 1, &num_configs);
|
|
EGL_CHECK_ERROR()
|
|
|
|
if (!num_configs) {
|
|
// clang-format off
|
|
const EGLint attribs[] = {
|
|
#if !defined(KORE_OPENGL_ES)
|
|
EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
|
|
#else
|
|
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
|
|
#endif
|
|
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
|
|
EGL_BLUE_SIZE, 8,
|
|
EGL_GREEN_SIZE, 8,
|
|
EGL_RED_SIZE, 8,
|
|
EGL_DEPTH_SIZE, 16,
|
|
EGL_STENCIL_SIZE, 8,
|
|
EGL_NONE,
|
|
};
|
|
// clang-format on
|
|
eglChooseConfig(egl_display, attribs, &egl_config, 1, &num_configs);
|
|
EGL_CHECK_ERROR()
|
|
egl_depth_size = 16;
|
|
}
|
|
|
|
if (!num_configs) {
|
|
kinc_log(KINC_LOG_LEVEL_ERROR, "Unable to choose EGL config");
|
|
}
|
|
|
|
#if !defined(KORE_OPENGL_ES)
|
|
EGLint gl_versions[][2] = {{4, 6}, {4, 5}, {4, 4}, {4, 3}, {4, 2}, {4, 1}, {4, 0}, {3, 3}, {3, 2}, {3, 1}, {3, 0}, {2, 1}, {2, 0}};
|
|
bool gl_initialized = false;
|
|
for (int i = 0; i < sizeof(gl_versions) / sizeof(EGLint) / 2; ++i) {
|
|
{
|
|
EGLint contextAttribs[] = {EGL_CONTEXT_MAJOR_VERSION,
|
|
gl_versions[i][0],
|
|
EGL_CONTEXT_MINOR_VERSION,
|
|
gl_versions[i][1],
|
|
EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE,
|
|
EGL_TRUE,
|
|
EGL_NONE};
|
|
egl_context = eglCreateContext(egl_display, egl_config, EGL_NO_CONTEXT, contextAttribs);
|
|
EGLint error = eglGetError();
|
|
if (error == EGL_SUCCESS) {
|
|
gl_initialized = true;
|
|
kinc_log(KINC_LOG_LEVEL_INFO, "Using OpenGL version %i.%i (forward-compatible).", gl_versions[i][0], gl_versions[i][1]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
{
|
|
EGLint contextAttribs[] = {EGL_CONTEXT_MAJOR_VERSION, gl_versions[i][0], EGL_CONTEXT_MINOR_VERSION, gl_versions[i][1], EGL_NONE};
|
|
egl_context = eglCreateContext(egl_display, egl_config, EGL_NO_CONTEXT, contextAttribs);
|
|
EGLint error = eglGetError();
|
|
if (error == EGL_SUCCESS) {
|
|
gl_initialized = true;
|
|
kinc_log(KINC_LOG_LEVEL_INFO, "Using OpenGL version %i.%i.", gl_versions[i][0], gl_versions[i][1]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!gl_initialized) {
|
|
kinc_log(KINC_LOG_LEVEL_ERROR, "Could not create OpenGL-context.");
|
|
exit(1);
|
|
}
|
|
#else
|
|
EGLint contextAttribs[] = {EGL_CONTEXT_MAJOR_VERSION, 2, EGL_NONE};
|
|
egl_context = eglCreateContext(egl_display, egl_config, EGL_NO_CONTEXT, contextAttribs);
|
|
EGL_CHECK_ERROR()
|
|
#endif
|
|
}
|
|
|
|
int kinc_egl_width(int window) {
|
|
EGLint w = 0;
|
|
eglQuerySurface(egl_display, kinc_egl_windows[window].surface, EGL_WIDTH, &w);
|
|
return w;
|
|
}
|
|
|
|
int kinc_egl_height(int window) {
|
|
EGLint h = 0;
|
|
eglQuerySurface(egl_display, kinc_egl_windows[window].surface, EGL_HEIGHT, &h);
|
|
return h;
|
|
}
|
|
|
|
void kinc_egl_init_window(int window) {
|
|
EGLSurface egl_surface = eglCreateWindowSurface(egl_display, egl_config, kinc_egl_get_native_window(egl_display, egl_config, window), NULL);
|
|
EGL_CHECK_ERROR()
|
|
eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
|
|
EGL_CHECK_ERROR()
|
|
kinc_egl_windows[window].surface = egl_surface;
|
|
}
|
|
|
|
void kinc_egl_destroy_window(int window) {
|
|
eglMakeCurrent(egl_display, kinc_egl_windows[window].surface, kinc_egl_windows[window].surface, egl_context);
|
|
EGL_CHECK_ERROR()
|
|
eglDestroySurface(egl_display, kinc_egl_windows[window].surface);
|
|
EGL_CHECK_ERROR()
|
|
kinc_egl_windows[window].surface = NULL;
|
|
}
|
|
#endif
|
|
|
|
bool kinc_g4_swap_buffers() {
|
|
#ifdef KORE_WINDOWS
|
|
for (int i = 9; i >= 0; --i) {
|
|
if (Kinc_Internal_windows[i].deviceContext != NULL) {
|
|
wglMakeCurrent(Kinc_Internal_windows[i].deviceContext, Kinc_Internal_windows[i].glContext);
|
|
if (i != 0) {
|
|
Kinc_Internal_blitWindowContent(i);
|
|
}
|
|
SwapBuffers(Kinc_Internal_windows[i].deviceContext);
|
|
}
|
|
}
|
|
#elif defined(KINC_EGL)
|
|
for (int window = 15; window >= 0; --window) {
|
|
if (kinc_egl_windows[window].surface) {
|
|
eglMakeCurrent(egl_display, kinc_egl_windows[window].surface, kinc_egl_windows[window].surface, egl_context);
|
|
EGL_CHECK_ERROR()
|
|
if (!eglSwapBuffers(egl_display, kinc_egl_windows[window].surface)) {
|
|
EGLint error = eglGetError();
|
|
if (error == EGL_BAD_SURFACE) {
|
|
kinc_log(KINC_LOG_LEVEL_WARNING, "Recreating surface.");
|
|
kinc_egl_init_window(window);
|
|
}
|
|
else if (error == EGL_CONTEXT_LOST || error == EGL_BAD_CONTEXT) {
|
|
kinc_log(KINC_LOG_LEVEL_ERROR, "Context lost.");
|
|
return false;
|
|
}
|
|
}
|
|
EGL_CHECK_ERROR()
|
|
}
|
|
}
|
|
#elif defined(KORE_MACOS)
|
|
swapBuffersMac(0);
|
|
#elif defined(KORE_IOS)
|
|
swapBuffersiOS();
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
#ifdef KORE_IOS
|
|
int kinc_ios_gl_framebuffer = -1;
|
|
|
|
void beginGL();
|
|
#endif
|
|
|
|
void kinc_g4_begin(int window) {
|
|
currentWindow = window;
|
|
|
|
#ifdef KINC_EGL
|
|
eglMakeCurrent(egl_display, kinc_egl_windows[window].surface, kinc_egl_windows[window].surface, egl_context);
|
|
EGL_CHECK_ERROR()
|
|
#endif
|
|
#ifdef KORE_IOS
|
|
beginGL();
|
|
#endif
|
|
kinc_g4_restore_render_target();
|
|
glCheckErrors();
|
|
#ifdef KORE_ANDROID
|
|
// if rendered to a texture, strange things happen if the backbuffer is not cleared
|
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
#endif
|
|
}
|
|
|
|
void kinc_g4_viewport(int x, int y, int width, int height) {
|
|
glViewport(x, _renderTargetHeight - y - height, width, height);
|
|
}
|
|
|
|
static bool scissor_on = false;
|
|
|
|
void kinc_g4_scissor(int x, int y, int width, int height) {
|
|
scissor_on = true;
|
|
glEnable(GL_SCISSOR_TEST);
|
|
if (renderToBackbuffer) {
|
|
glScissor(x, _renderTargetHeight - y - height, width, height);
|
|
}
|
|
else {
|
|
glScissor(x, y, width, height);
|
|
}
|
|
}
|
|
|
|
void kinc_g4_disable_scissor() {
|
|
scissor_on = false;
|
|
glDisable(GL_SCISSOR_TEST);
|
|
}
|
|
|
|
void kinc_g4_end(int windowId) {
|
|
currentWindow = 0;
|
|
glCheckErrors();
|
|
}
|
|
|
|
void kinc_g4_clear(unsigned flags, unsigned color, float depth, int stencil) {
|
|
glColorMask(true, true, true, true);
|
|
glCheckErrors();
|
|
glClearColor(((color & 0x00ff0000) >> 16) / 255.0f, ((color & 0x0000ff00) >> 8) / 255.0f, (color & 0x000000ff) / 255.0f,
|
|
((color & 0xff000000) >> 24) / 255.0f);
|
|
glCheckErrors();
|
|
if (flags & KINC_G4_CLEAR_DEPTH) {
|
|
glEnable(GL_DEPTH_TEST);
|
|
glDepthMask(GL_TRUE);
|
|
glCheckErrors();
|
|
}
|
|
#ifdef KORE_OPENGL_ES
|
|
glClearDepthf(depth);
|
|
#else
|
|
glClearDepth(depth);
|
|
#endif
|
|
glCheckErrors();
|
|
glStencilMask(0xff);
|
|
glCheckErrors();
|
|
glClearStencil(stencil);
|
|
glCheckErrors();
|
|
GLbitfield oglflags = ((flags & KINC_G4_CLEAR_COLOR) ? GL_COLOR_BUFFER_BIT : 0) | ((flags & KINC_G4_CLEAR_DEPTH) ? GL_DEPTH_BUFFER_BIT : 0) |
|
|
((flags & KINC_G4_CLEAR_STENCIL) ? GL_STENCIL_BUFFER_BIT : 0);
|
|
|
|
if (scissor_on) {
|
|
glDisable(GL_SCISSOR_TEST);
|
|
}
|
|
|
|
glClear(oglflags);
|
|
glCheckErrors();
|
|
|
|
if (scissor_on) {
|
|
glEnable(GL_SCISSOR_TEST);
|
|
}
|
|
|
|
if (lastPipeline != NULL) {
|
|
kinc_g4_set_pipeline(lastPipeline);
|
|
}
|
|
}
|
|
|
|
void kinc_g4_set_vertex_buffers(kinc_g4_vertex_buffer_t **vertexBuffers, int count) {
|
|
int offset = 0;
|
|
for (int i = 0; i < count; ++i) {
|
|
offset += kinc_internal_g4_vertex_buffer_set(vertexBuffers[i], offset);
|
|
}
|
|
}
|
|
|
|
void kinc_g4_set_index_buffer(kinc_g4_index_buffer_t *indexBuffer) {
|
|
kinc_internal_g4_index_buffer_set(indexBuffer);
|
|
}
|
|
|
|
void Kinc_G4_Internal_TextureImageSet(kinc_g4_texture_t *texture, kinc_g4_texture_unit_t unit);
|
|
|
|
#ifdef KINC_KONG
|
|
void Kinc_G4_Internal_TextureSet(kinc_g4_texture_t *texture, uint32_t unit);
|
|
|
|
void kinc_g4_set_texture(uint32_t unit, kinc_g4_texture_t *texture) {
|
|
Kinc_G4_Internal_TextureSet(texture, unit);
|
|
}
|
|
#else
|
|
void Kinc_G4_Internal_TextureSet(kinc_g4_texture_t *texture, kinc_g4_texture_unit_t unit);
|
|
|
|
void kinc_g4_set_texture(kinc_g4_texture_unit_t unit, kinc_g4_texture_t *texture) {
|
|
Kinc_G4_Internal_TextureSet(texture, unit);
|
|
}
|
|
#endif
|
|
|
|
void kinc_g4_set_image_texture(kinc_g4_texture_unit_t unit, kinc_g4_texture_t *texture) {
|
|
Kinc_G4_Internal_TextureImageSet(texture, unit);
|
|
}
|
|
|
|
int kinc_g4_max_bound_textures(void) {
|
|
int units;
|
|
glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &units);
|
|
return units;
|
|
}
|
|
|
|
static void setTextureAddressingInternal(GLenum target, kinc_g4_texture_unit_t unit, kinc_g4_texture_direction_t dir, kinc_g4_texture_addressing_t addressing) {
|
|
glActiveTexture(GL_TEXTURE0 + unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]);
|
|
GLenum texDir;
|
|
switch (dir) {
|
|
case KINC_G4_TEXTURE_DIRECTION_U:
|
|
texDir = GL_TEXTURE_WRAP_S;
|
|
break;
|
|
case KINC_G4_TEXTURE_DIRECTION_V:
|
|
texDir = GL_TEXTURE_WRAP_T;
|
|
break;
|
|
case KINC_G4_TEXTURE_DIRECTION_W:
|
|
#ifndef KORE_OPENGL_ES
|
|
texDir = GL_TEXTURE_WRAP_R;
|
|
#endif
|
|
break;
|
|
}
|
|
switch (addressing) {
|
|
case KINC_G4_TEXTURE_ADDRESSING_CLAMP:
|
|
glTexParameteri(target, texDir, GL_CLAMP_TO_EDGE);
|
|
if (dir == KINC_G4_TEXTURE_DIRECTION_U) {
|
|
texModesU[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]] = GL_CLAMP_TO_EDGE;
|
|
}
|
|
else {
|
|
texModesV[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]] = GL_CLAMP_TO_EDGE;
|
|
}
|
|
break;
|
|
case KINC_G4_TEXTURE_ADDRESSING_REPEAT:
|
|
glTexParameteri(target, texDir, GL_REPEAT);
|
|
if (dir == KINC_G4_TEXTURE_DIRECTION_U) {
|
|
texModesU[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]] = GL_REPEAT;
|
|
}
|
|
else {
|
|
texModesV[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]] = GL_REPEAT;
|
|
}
|
|
break;
|
|
case KINC_G4_TEXTURE_ADDRESSING_BORDER:
|
|
// unsupported
|
|
glTexParameteri(target, texDir, GL_CLAMP_TO_EDGE);
|
|
if (dir == KINC_G4_TEXTURE_DIRECTION_U) {
|
|
texModesU[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]] = GL_CLAMP_TO_EDGE;
|
|
}
|
|
else {
|
|
texModesV[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]] = GL_CLAMP_TO_EDGE;
|
|
}
|
|
break;
|
|
case KINC_G4_TEXTURE_ADDRESSING_MIRROR:
|
|
// unsupported
|
|
glTexParameteri(target, texDir, GL_REPEAT);
|
|
if (dir == KINC_G4_TEXTURE_DIRECTION_U) {
|
|
texModesU[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]] = GL_REPEAT;
|
|
}
|
|
else {
|
|
texModesV[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]] = GL_REPEAT;
|
|
}
|
|
break;
|
|
}
|
|
glCheckErrors();
|
|
}
|
|
|
|
#ifdef KINC_KONG
|
|
int Kinc_G4_Internal_TextureAddressingU(uint32_t unit) {
|
|
return texModesU[unit];
|
|
}
|
|
|
|
int Kinc_G4_Internal_TextureAddressingV(uint32_t unit) {
|
|
return texModesV[unit];
|
|
}
|
|
#else
|
|
int Kinc_G4_Internal_TextureAddressingU(kinc_g4_texture_unit_t unit) {
|
|
return texModesU[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]];
|
|
}
|
|
|
|
int Kinc_G4_Internal_TextureAddressingV(kinc_g4_texture_unit_t unit) {
|
|
return texModesV[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]];
|
|
}
|
|
#endif
|
|
|
|
void kinc_g4_set_texture_addressing(kinc_g4_texture_unit_t unit, kinc_g4_texture_direction_t dir, kinc_g4_texture_addressing_t addressing) {
|
|
setTextureAddressingInternal(GL_TEXTURE_2D, unit, dir, addressing);
|
|
}
|
|
|
|
void kinc_g4_set_texture3d_addressing(kinc_g4_texture_unit_t unit, kinc_g4_texture_direction_t dir, kinc_g4_texture_addressing_t addressing) {
|
|
#ifndef KORE_OPENGL_ES
|
|
setTextureAddressingInternal(GL_TEXTURE_3D, unit, dir, addressing);
|
|
#endif
|
|
}
|
|
|
|
static void setTextureMagnificationFilterInternal(GLenum target, kinc_g4_texture_unit_t texunit, kinc_g4_texture_filter_t filter) {
|
|
glActiveTexture(GL_TEXTURE0 + texunit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]);
|
|
glCheckErrors();
|
|
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:
|
|
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
break;
|
|
}
|
|
glCheckErrors();
|
|
}
|
|
|
|
void kinc_g4_set_texture_magnification_filter(kinc_g4_texture_unit_t texunit, kinc_g4_texture_filter_t filter) {
|
|
setTextureMagnificationFilterInternal(GL_TEXTURE_2D, texunit, filter);
|
|
}
|
|
|
|
void kinc_g4_set_texture3d_magnification_filter(kinc_g4_texture_unit_t texunit, kinc_g4_texture_filter_t filter) {
|
|
#ifndef KORE_OPENGL_ES
|
|
setTextureMagnificationFilterInternal(GL_TEXTURE_3D, texunit, filter);
|
|
#endif
|
|
}
|
|
|
|
static void setMinMipFilters(GLenum target, int unit) {
|
|
glActiveTexture(GL_TEXTURE0 + unit);
|
|
glCheckErrors();
|
|
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();
|
|
}
|
|
|
|
void kinc_g4_set_texture_minification_filter(kinc_g4_texture_unit_t texunit, kinc_g4_texture_filter_t filter) {
|
|
minFilters[texunit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]] = filter;
|
|
setMinMipFilters(GL_TEXTURE_2D, texunit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]);
|
|
}
|
|
|
|
void kinc_g4_set_texture3d_minification_filter(kinc_g4_texture_unit_t texunit, kinc_g4_texture_filter_t filter) {
|
|
minFilters[texunit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]] = filter;
|
|
#ifndef KORE_OPENGL_ES
|
|
setMinMipFilters(GL_TEXTURE_3D, texunit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]);
|
|
#endif
|
|
}
|
|
|
|
void kinc_g4_set_texture_mipmap_filter(kinc_g4_texture_unit_t texunit, kinc_g4_mipmap_filter_t filter) {
|
|
mipFilters[texunit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]] = filter;
|
|
setMinMipFilters(GL_TEXTURE_2D, texunit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]);
|
|
}
|
|
|
|
void kinc_g4_set_texture3d_mipmap_filter(kinc_g4_texture_unit_t texunit, kinc_g4_mipmap_filter_t filter) {
|
|
mipFilters[texunit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]] = filter;
|
|
#ifndef KORE_OPENGL_ES
|
|
setMinMipFilters(GL_TEXTURE_3D, texunit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]);
|
|
#endif
|
|
}
|
|
|
|
void kinc_g4_set_texture_compare_mode(kinc_g4_texture_unit_t texunit, bool enabled) {
|
|
if (texunit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] < 0)
|
|
return;
|
|
if (enabled) {
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
|
|
}
|
|
else {
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE);
|
|
}
|
|
}
|
|
|
|
void kinc_g4_set_texture_compare_func(kinc_g4_texture_unit_t unit, kinc_g4_compare_mode_t mode) {
|
|
if (unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] < 0)
|
|
return;
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, Kinc_G4_Internal_StencilFunc(mode));
|
|
}
|
|
|
|
void kinc_g4_set_cubemap_compare_mode(kinc_g4_texture_unit_t texunit, bool enabled) {
|
|
if (texunit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] < 0)
|
|
return;
|
|
if (enabled) {
|
|
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
|
|
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
|
|
}
|
|
else {
|
|
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_MODE, GL_NONE);
|
|
}
|
|
}
|
|
|
|
void kinc_g4_set_cubemap_compare_func(kinc_g4_texture_unit_t unit, kinc_g4_compare_mode_t mode) {
|
|
if (unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] < 0)
|
|
return;
|
|
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_FUNC, Kinc_G4_Internal_StencilFunc(mode));
|
|
}
|
|
|
|
void kinc_g4_set_texture_max_anisotropy(kinc_g4_texture_unit_t unit, uint16_t max_anisotropy) {
|
|
if (unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] < 0)
|
|
return;
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy);
|
|
}
|
|
|
|
void kinc_g4_set_cubemap_max_anisotropy(kinc_g4_texture_unit_t unit, uint16_t max_anisotropy) {
|
|
if (unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] < 0)
|
|
return;
|
|
glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy);
|
|
}
|
|
|
|
void kinc_g4_set_texture_lod(kinc_g4_texture_unit_t unit, float lod_min_clamp, float lod_max_clamp) {
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, lod_min_clamp);
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, lod_max_clamp);
|
|
}
|
|
|
|
void kinc_g4_set_cubemap_lod(kinc_g4_texture_unit_t unit, float lod_min_clamp, float lod_max_clamp) {
|
|
glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_LOD, lod_min_clamp);
|
|
glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_LOD, lod_max_clamp);
|
|
}
|
|
|
|
void kinc_g4_set_render_targets(kinc_g4_render_target_t **targets, int count) {
|
|
glBindFramebuffer(GL_FRAMEBUFFER, targets[0]->impl._framebuffer);
|
|
glCheckErrors();
|
|
#ifndef KORE_OPENGL_ES
|
|
if (targets[0]->isCubeMap)
|
|
glFramebufferTexture(GL_FRAMEBUFFER, targets[0]->isDepthAttachment ? GL_DEPTH_ATTACHMENT : GL_COLOR_ATTACHMENT0, targets[0]->impl._texture,
|
|
0); // Layered
|
|
#endif
|
|
glViewport(0, 0, targets[0]->width, targets[0]->height);
|
|
_renderTargetWidth = targets[0]->width;
|
|
_renderTargetHeight = targets[0]->height;
|
|
renderToBackbuffer = false;
|
|
glCheckErrors();
|
|
|
|
if (count > 1) {
|
|
for (int i = 0; i < count; ++i) {
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, targets[i]->impl._texture, 0);
|
|
glCheckErrors();
|
|
}
|
|
|
|
GLenum buffers[16];
|
|
for (int i = 0; i < count; ++i)
|
|
buffers[i] = GL_COLOR_ATTACHMENT0 + i;
|
|
#if defined(KORE_OPENGL_ES) && defined(KORE_ANDROID) && KORE_ANDROID_API >= 18
|
|
((void (*)(GLsizei, GLenum *))glesDrawBuffers)(count, buffers);
|
|
#elif !defined(KORE_OPENGL_ES) || defined(KORE_EMSCRIPTEN) || defined(KORE_WASM)
|
|
glDrawBuffers(count, buffers);
|
|
#endif
|
|
glCheckErrors();
|
|
}
|
|
|
|
for (int i = count; i < maxColorAttachments; ++i) {
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, 0, 0);
|
|
glCheckErrors();
|
|
}
|
|
}
|
|
|
|
void kinc_g4_set_render_target_face(kinc_g4_render_target_t *texture, int face) {
|
|
glBindFramebuffer(GL_FRAMEBUFFER, texture->impl._framebuffer);
|
|
glCheckErrors();
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, texture->isDepthAttachment ? GL_DEPTH_ATTACHMENT : GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + face,
|
|
texture->impl._texture, 0);
|
|
glViewport(0, 0, texture->width, texture->height);
|
|
_renderTargetWidth = texture->width;
|
|
_renderTargetHeight = texture->height;
|
|
renderToBackbuffer = false;
|
|
glCheckErrors();
|
|
}
|
|
|
|
void kinc_g4_restore_render_target() {
|
|
#ifdef KORE_IOS
|
|
glBindFramebuffer(GL_FRAMEBUFFER, kinc_ios_gl_framebuffer);
|
|
#else
|
|
glBindFramebuffer(GL_FRAMEBUFFER, GL_NONE);
|
|
#endif
|
|
glCheckErrors();
|
|
int w = kinc_window_width(currentWindow);
|
|
int h = kinc_window_height(currentWindow);
|
|
glViewport(0, 0, w, h);
|
|
_renderTargetWidth = w;
|
|
_renderTargetHeight = h;
|
|
renderToBackbuffer = true;
|
|
glCheckErrors();
|
|
#ifdef KORE_WINDOWS
|
|
Kinc_Internal_setWindowRenderTarget(currentWindow);
|
|
#endif
|
|
}
|
|
|
|
#if (defined(KORE_OPENGL) && !defined(KORE_PI) && !defined(KORE_ANDROID)) || (defined(KORE_ANDROID) && KORE_ANDROID_API >= 18)
|
|
bool kinc_g4_init_occlusion_query(unsigned *occlusionQuery) {
|
|
#if defined(KORE_OPENGL_ES) && defined(KORE_ANDROID) && KORE_ANDROID_API >= 18
|
|
if (gles_version >= 3 && glesGenQueries) {
|
|
glesGenQueries(1, occlusionQuery);
|
|
}
|
|
#else
|
|
glGenQueries(1, occlusionQuery);
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
void kinc_g4_delete_occlusion_query(unsigned occlusionQuery) {
|
|
#if defined(KORE_OPENGL_ES) && defined(KORE_ANDROID) && KORE_ANDROID_API >= 18
|
|
if (gles_version >= 3 && glesGenQueries) {
|
|
glesDeleteQueries(1, &occlusionQuery);
|
|
}
|
|
#else
|
|
glDeleteQueries(1, &occlusionQuery);
|
|
#endif
|
|
}
|
|
|
|
#if defined(KORE_OPENGL_ES)
|
|
#define SAMPLES_PASSED GL_ANY_SAMPLES_PASSED
|
|
#else
|
|
#define SAMPLES_PASSED GL_SAMPLES_PASSED
|
|
#endif
|
|
|
|
void kinc_g4_render_occlusion_query(unsigned occlusionQuery, int triangles) {
|
|
#if defined(KORE_OPENGL_ES) && defined(KORE_ANDROID) && KORE_ANDROID_API >= 18
|
|
if (gles_version >= 3 && glesGenQueries) {
|
|
glesBeginQuery(SAMPLES_PASSED, occlusionQuery);
|
|
glDrawArrays(GL_TRIANGLES, 0, triangles);
|
|
glCheckErrors();
|
|
glesEndQuery(SAMPLES_PASSED);
|
|
}
|
|
#else
|
|
glBeginQuery(SAMPLES_PASSED, occlusionQuery);
|
|
glDrawArrays(GL_TRIANGLES, 0, triangles);
|
|
glCheckErrors();
|
|
glEndQuery(SAMPLES_PASSED);
|
|
#endif
|
|
}
|
|
|
|
bool kinc_g4_are_query_results_available(unsigned occlusionQuery) {
|
|
unsigned available = 0;
|
|
#if defined(KORE_OPENGL_ES) && defined(KORE_ANDROID) && KORE_ANDROID_API >= 18
|
|
if (gles_version >= 3 && glesGetQueryObjectuiv) {
|
|
glesGetQueryObjectuiv(occlusionQuery, GL_QUERY_RESULT_AVAILABLE, &available);
|
|
}
|
|
#else
|
|
glGetQueryObjectuiv(occlusionQuery, GL_QUERY_RESULT_AVAILABLE, &available);
|
|
#endif
|
|
return available != 0;
|
|
}
|
|
|
|
void kinc_g4_get_query_results(unsigned occlusionQuery, unsigned *pixelCount) {
|
|
#if defined(KORE_OPENGL_ES) && defined(KORE_ANDROID) && KORE_ANDROID_API >= 18
|
|
if (gles_version >= 3 && glesGetQueryObjectuiv) {
|
|
glesGetQueryObjectuiv(occlusionQuery, GL_QUERY_RESULT, pixelCount);
|
|
}
|
|
#else
|
|
glGetQueryObjectuiv(occlusionQuery, GL_QUERY_RESULT, pixelCount);
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
void kinc_g4_flush() {
|
|
glFlush();
|
|
glCheckErrors();
|
|
}
|
|
|
|
void kinc_g4_set_pipeline(kinc_g4_pipeline_t *pipeline) {
|
|
kinc_g4_internal_set_pipeline(pipeline);
|
|
lastPipeline = pipeline;
|
|
}
|
|
|
|
void kinc_g4_set_blend_constant(float r, float g, float b, float a) {
|
|
glBlendColor(r, g, b, a);
|
|
}
|
|
|
|
void kinc_g4_set_stencil_reference_value(int value) {
|
|
glStencilFuncSeparate(GL_FRONT, Kinc_G4_Internal_StencilFunc(lastPipeline->stencil_front_mode), value, lastPipeline->stencil_read_mask);
|
|
glStencilFuncSeparate(GL_BACK, Kinc_G4_Internal_StencilFunc(lastPipeline->stencil_back_mode), value, lastPipeline->stencil_read_mask);
|
|
}
|
|
|
|
void Kinc_G4_Internal_TextureArraySet(kinc_g4_texture_array_t *array, kinc_g4_texture_unit_t unit);
|
|
|
|
void kinc_g4_set_texture_array(kinc_g4_texture_unit_t unit, kinc_g4_texture_array_t *array) {
|
|
Kinc_G4_Internal_TextureArraySet(array, unit);
|
|
}
|
|
|
|
int Kinc_G4_Internal_StencilFunc(kinc_g4_compare_mode_t mode) {
|
|
switch (mode) {
|
|
case KINC_G4_COMPARE_ALWAYS:
|
|
return GL_ALWAYS;
|
|
case KINC_G4_COMPARE_EQUAL:
|
|
return GL_EQUAL;
|
|
case KINC_G4_COMPARE_GREATER:
|
|
return GL_GREATER;
|
|
case KINC_G4_COMPARE_GREATER_EQUAL:
|
|
return GL_GEQUAL;
|
|
case KINC_G4_COMPARE_LESS:
|
|
return GL_LESS;
|
|
case KINC_G4_COMPARE_LESS_EQUAL:
|
|
return GL_LEQUAL;
|
|
case KINC_G4_COMPARE_NEVER:
|
|
return GL_NEVER;
|
|
case KINC_G4_COMPARE_NOT_EQUAL:
|
|
return GL_NOTEQUAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
extern bool kinc_internal_gl_has_compute;
|
|
|
|
bool kinc_g4_supports_instanced_rendering() {
|
|
#if defined(KORE_OPENGL_ES) && defined(KORE_ANDROID)
|
|
#if KORE_ANDROID_API >= 18
|
|
return glesDrawElementsInstanced != NULL;
|
|
#else
|
|
return false;
|
|
#endif
|
|
#else
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
bool kinc_g4_supports_compute_shaders() {
|
|
return kinc_internal_gl_has_compute;
|
|
}
|
|
|
|
bool kinc_g4_supports_blend_constants() {
|
|
return true;
|
|
}
|
|
|
|
bool kinc_g4_supports_non_pow2_textures() {
|
|
// we use OpenGL 2.0+, which should supports NPOT textures.
|
|
// in practice certain very old hardware doesn't,
|
|
// but detecting that is not practical
|
|
return true;
|
|
}
|
|
|
|
bool kinc_g4_render_targets_inverted_y(void) {
|
|
return true;
|
|
}
|
|
|
|
#ifdef KINC_KONG
|
|
void kinc_g4_set_constant_buffer(uint32_t id, struct kinc_g4_constant_buffer *buffer) {
|
|
glBindBufferBase(GL_UNIFORM_BUFFER, id, buffer->impl.buffer);
|
|
}
|
|
|
|
void kinc_g4_internal_opengl_setup_uniform_block(unsigned program, const char *name, unsigned binding) {
|
|
unsigned index = glGetUniformBlockIndex(program, name);
|
|
glUniformBlockBinding(program, index, binding);
|
|
}
|
|
#endif
|