Files
LNXRNT/Kinc/Backends/Graphics4/OpenGL/Sources/kinc/backend/graphics4/rendertarget.c.h
2025-01-29 10:55:49 +01:00

422 lines
16 KiB
C

#include <kinc/graphics4/rendertarget.h>
#include "ogl.h"
#include <kinc/backend/graphics4/OpenGL.h>
#include <kinc/graphics4/graphics.h>
#include <kinc/log.h>
#include <kinc/system.h>
#ifndef GL_RGBA16F_EXT
#define GL_RGBA16F_EXT 0x881A
#endif
#ifndef GL_RGBA32F_EXT
#define GL_RGBA32F_EXT 0x8814
#endif
#ifndef GL_R16F_EXT
#define GL_R16F_EXT 0x822D
#endif
#ifndef GL_R32F_EXT
#define GL_R32F_EXT 0x822E
#endif
#ifndef GL_HALF_FLOAT
#define GL_HALF_FLOAT 0x140B
#endif
#ifndef GL_RED
#define GL_RED GL_LUMINANCE
#endif
#ifndef GL_R8
#define GL_R8 GL_RED
#endif
extern bool Kinc_Internal_SupportsDepthTexture;
static int pow2(int pow) {
int ret = 1;
for (int i = 0; i < pow; ++i)
ret *= 2;
return ret;
}
static int getPower2(int i) {
for (int power = 0;; ++power)
if (pow2(power) >= i)
return pow2(power);
}
#ifdef KORE_OPENGL_ES
extern int gles_version;
#endif
bool kinc_opengl_internal_nonPow2RenderTargetsSupported() {
#ifdef KORE_OPENGL_ES
return gles_version >= 3;
#else
return true;
#endif
}
static void setupDepthStencil(kinc_g4_render_target_t *renderTarget, GLenum texType, int depthBufferBits, int stencilBufferBits, int width, int height) {
if (depthBufferBits > 0 && stencilBufferBits > 0) {
renderTarget->impl._hasDepth = true;
#if defined(KORE_OPENGL_ES) && !defined(KORE_PI) && !defined(KORE_EMSCRIPTEN)
GLenum internalFormat = GL_DEPTH24_STENCIL8_OES;
#elif defined(KORE_OPENGL_ES)
GLenum internalFormat = 0x88F0; // GL_DEPTH24_STENCIL8_OES
#else
GLenum internalFormat;
if (depthBufferBits == 24)
internalFormat = GL_DEPTH24_STENCIL8;
else
internalFormat = GL_DEPTH32F_STENCIL8;
#endif
// Renderbuffer
// glGenRenderbuffers(1, &_depthRenderbuffer);
// glCheckErrors();
// glBindRenderbuffer(GL_RENDERBUFFER, _depthRenderbuffer);
// glCheckErrors();
// glRenderbufferStorage(GL_RENDERBUFFER, internalFormat, width, height);
// glCheckErrors();
// #ifdef KORE_OPENGL_ES
// glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _depthRenderbuffer);
// glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, _depthRenderbuffer);
// #else
// glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, _depthRenderbuffer);
// #endif
// glCheckErrors();
// Texture
glGenTextures(1, &renderTarget->impl._depthTexture);
glCheckErrors();
glBindTexture(texType, renderTarget->impl._depthTexture);
glCheckErrors();
glTexImage2D(texType, 0, internalFormat, width, height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, 0);
glCheckErrors();
glTexParameteri(texType, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(texType, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(texType, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(texType, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glCheckErrors();
glBindFramebuffer(GL_FRAMEBUFFER, renderTarget->impl._framebuffer);
glCheckErrors();
#ifdef KORE_OPENGL_ES
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, texType, renderTarget->impl._depthTexture, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, texType, renderTarget->impl._depthTexture, 0);
#else
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, texType, renderTarget->impl._depthTexture, 0);
#endif
glCheckErrors();
}
else if (depthBufferBits > 0) {
renderTarget->impl._hasDepth = true;
if (!Kinc_Internal_SupportsDepthTexture) {
// Renderbuffer
glGenRenderbuffers(1, &renderTarget->impl._depthTexture);
glCheckErrors();
glBindRenderbuffer(GL_RENDERBUFFER, renderTarget->impl._depthTexture);
glCheckErrors();
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);
glCheckErrors();
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, renderTarget->impl._depthTexture);
glCheckErrors();
}
else {
// Texture
glGenTextures(1, &renderTarget->impl._depthTexture);
glCheckErrors();
glBindTexture(texType, renderTarget->impl._depthTexture);
glCheckErrors();
#if defined(KORE_EMSCRIPTEN) || defined(KORE_WASM)
GLint format = GL_DEPTH_COMPONENT16;
#else
GLint format = depthBufferBits == 16 ? GL_DEPTH_COMPONENT16 : GL_DEPTH_COMPONENT;
#endif
glTexImage2D(texType, 0, format, width, height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, 0);
glCheckErrors();
glTexParameteri(texType, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(texType, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(texType, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(texType, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glCheckErrors();
glBindFramebuffer(GL_FRAMEBUFFER, renderTarget->impl._framebuffer);
glCheckErrors();
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, texType, renderTarget->impl._depthTexture, 0);
glCheckErrors();
}
}
}
void kinc_g4_render_target_init_with_multisampling(kinc_g4_render_target_t *renderTarget, int width, int height, kinc_g4_render_target_format_t format,
int depthBufferBits, int stencilBufferBits, int samples_per_pixel) {
renderTarget->width = width;
renderTarget->height = height;
renderTarget->isCubeMap = false;
renderTarget->isDepthAttachment = false;
renderTarget->impl._hasDepth = false;
if (kinc_opengl_internal_nonPow2RenderTargetsSupported()) {
renderTarget->texWidth = width;
renderTarget->texHeight = height;
}
else {
renderTarget->texWidth = getPower2(width);
renderTarget->texHeight = getPower2(height);
}
renderTarget->impl.format = (int)format;
glGenTextures(1, &renderTarget->impl._texture);
glCheckErrors();
glBindTexture(GL_TEXTURE_2D, renderTarget->impl._texture);
glCheckErrors();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glCheckErrors();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glCheckErrors();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glCheckErrors();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glCheckErrors();
switch (format) {
case KINC_G4_RENDER_TARGET_FORMAT_128BIT_FLOAT:
#ifdef KORE_OPENGL_ES
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F_EXT, renderTarget->texWidth, renderTarget->texHeight, 0, GL_RGBA, GL_FLOAT, 0);
#else
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, renderTarget->texWidth, renderTarget->texHeight, 0, GL_RGBA, GL_FLOAT, 0);
#endif
break;
case KINC_G4_RENDER_TARGET_FORMAT_64BIT_FLOAT:
#ifdef KORE_OPENGL_ES
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F_EXT, renderTarget->texWidth, renderTarget->texHeight, 0, GL_RGBA, GL_HALF_FLOAT, 0);
#else
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, renderTarget->texWidth, renderTarget->texHeight, 0, GL_RGBA, GL_HALF_FLOAT, 0);
#endif
break;
case KINC_G4_RENDER_TARGET_FORMAT_16BIT_DEPTH:
#ifdef KORE_OPENGL_ES
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
#endif
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16, renderTarget->texWidth, renderTarget->texHeight, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, 0);
break;
case KINC_G4_RENDER_TARGET_FORMAT_8BIT_RED:
#ifdef KORE_IOS
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, renderTarget->texWidth, renderTarget->texHeight, 0, GL_RED, GL_UNSIGNED_BYTE, 0);
#else
glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, renderTarget->texWidth, renderTarget->texHeight, 0, GL_RED, GL_UNSIGNED_BYTE, 0);
#endif
break;
case KINC_G4_RENDER_TARGET_FORMAT_16BIT_RED_FLOAT:
glTexImage2D(GL_TEXTURE_2D, 0, GL_R16F_EXT, renderTarget->texWidth, renderTarget->texHeight, 0, GL_RED, GL_HALF_FLOAT, 0);
break;
case KINC_G4_RENDER_TARGET_FORMAT_32BIT_RED_FLOAT:
glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F_EXT, renderTarget->texWidth, renderTarget->texHeight, 0, GL_RED, GL_FLOAT, 0);
break;
case KINC_G4_RENDER_TARGET_FORMAT_32BIT:
default:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, renderTarget->texWidth, renderTarget->texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
}
glCheckErrors();
glGenFramebuffers(1, &renderTarget->impl._framebuffer);
glCheckErrors();
glBindFramebuffer(GL_FRAMEBUFFER, renderTarget->impl._framebuffer);
glCheckErrors();
setupDepthStencil(renderTarget, GL_TEXTURE_2D, depthBufferBits, stencilBufferBits, renderTarget->texWidth, renderTarget->texHeight);
if (format == KINC_G4_RENDER_TARGET_FORMAT_16BIT_DEPTH) {
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, renderTarget->impl._texture, 0);
#ifndef KORE_OPENGL_ES
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
#endif
}
else {
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, renderTarget->impl._texture, 0);
}
glCheckErrors();
// GLenum drawBuffers[1] = { GL_COLOR_ATTACHMENT0 };
// glDrawBuffers(1, drawBuffers);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glCheckErrors();
glBindTexture(GL_TEXTURE_2D, 0);
glCheckErrors();
}
void kinc_g4_render_target_init_cube_with_multisampling(kinc_g4_render_target_t *renderTarget, int cubeMapSize, kinc_g4_render_target_format_t format,
int depthBufferBits, int stencilBufferBits, int samples_per_pixel) {
renderTarget->width = cubeMapSize;
renderTarget->height = cubeMapSize;
renderTarget->isCubeMap = true;
renderTarget->isDepthAttachment = false;
renderTarget->impl._hasDepth = false;
if (kinc_opengl_internal_nonPow2RenderTargetsSupported()) {
renderTarget->texWidth = renderTarget->width;
renderTarget->texHeight = renderTarget->height;
}
else {
renderTarget->texWidth = getPower2(renderTarget->width);
renderTarget->texHeight = getPower2(renderTarget->height);
}
renderTarget->impl.format = (int)format;
glGenTextures(1, &renderTarget->impl._texture);
glCheckErrors();
glBindTexture(GL_TEXTURE_CUBE_MAP, renderTarget->impl._texture);
glCheckErrors();
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glCheckErrors();
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glCheckErrors();
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glCheckErrors();
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glCheckErrors();
switch (format) {
case KINC_G4_RENDER_TARGET_FORMAT_128BIT_FLOAT:
#ifdef KORE_OPENGL_ES
for (int i = 0; i < 6; i++)
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGBA32F_EXT, renderTarget->texWidth, renderTarget->texHeight, 0, GL_RGBA, GL_FLOAT, 0);
#else
for (int i = 0; i < 6; i++)
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGBA32F, renderTarget->texWidth, renderTarget->texHeight, 0, GL_RGBA, GL_FLOAT, 0);
#endif
break;
case KINC_G4_RENDER_TARGET_FORMAT_64BIT_FLOAT:
#ifdef KORE_OPENGL_ES
for (int i = 0; i < 6; i++)
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGBA16F_EXT, renderTarget->texWidth, renderTarget->texHeight, 0, GL_RGBA, GL_HALF_FLOAT, 0);
#else
for (int i = 0; i < 6; i++)
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGBA16F, renderTarget->texWidth, renderTarget->texHeight, 0, GL_RGBA, GL_HALF_FLOAT, 0);
#endif
break;
case KINC_G4_RENDER_TARGET_FORMAT_16BIT_DEPTH:
#ifdef KORE_OPENGL_ES
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
#endif
for (int i = 0; i < 6; i++)
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_DEPTH_COMPONENT16, renderTarget->texWidth, renderTarget->texHeight, 0, GL_DEPTH_COMPONENT,
GL_UNSIGNED_INT, 0);
break;
case KINC_G4_RENDER_TARGET_FORMAT_32BIT:
default:
for (int i = 0; i < 6; i++)
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGBA, renderTarget->texWidth, renderTarget->texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
}
glCheckErrors();
glGenFramebuffers(1, &renderTarget->impl._framebuffer);
glCheckErrors();
glBindFramebuffer(GL_FRAMEBUFFER, renderTarget->impl._framebuffer);
glCheckErrors();
setupDepthStencil(renderTarget, GL_TEXTURE_CUBE_MAP, depthBufferBits, stencilBufferBits, renderTarget->texWidth, renderTarget->texHeight);
if (format == KINC_G4_RENDER_TARGET_FORMAT_16BIT_DEPTH) {
renderTarget->isDepthAttachment = true;
#ifndef KORE_OPENGL_ES
glDrawBuffer(GL_NONE);
glCheckErrors();
glReadBuffer(GL_NONE);
glCheckErrors();
#endif
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glCheckErrors();
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
glCheckErrors();
}
void kinc_g4_render_target_destroy(kinc_g4_render_target_t *renderTarget) {
{
GLuint textures[] = {renderTarget->impl._texture};
glDeleteTextures(1, textures);
}
if (renderTarget->impl._hasDepth) {
GLuint textures[] = {renderTarget->impl._depthTexture};
glDeleteTextures(1, textures);
}
GLuint framebuffers[] = {renderTarget->impl._framebuffer};
glDeleteFramebuffers(1, framebuffers);
}
#ifdef KINC_KONG
void kinc_g4_render_target_use_color_as_texture(kinc_g4_render_target_t *renderTarget, uint32_t unit) {
glActiveTexture(GL_TEXTURE0 + unit);
glCheckErrors();
glBindTexture(renderTarget->isCubeMap ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D, renderTarget->impl._texture);
glCheckErrors();
}
#else
void kinc_g4_render_target_use_color_as_texture(kinc_g4_render_target_t *renderTarget, kinc_g4_texture_unit_t unit) {
glActiveTexture(GL_TEXTURE0 + unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]);
glCheckErrors();
glBindTexture(renderTarget->isCubeMap ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D, renderTarget->impl._texture);
glCheckErrors();
}
#endif
void kinc_g4_render_target_use_depth_as_texture(kinc_g4_render_target_t *renderTarget, kinc_g4_texture_unit_t unit) {
glActiveTexture(GL_TEXTURE0 + unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]);
glCheckErrors();
glBindTexture(renderTarget->isCubeMap ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D, renderTarget->impl._depthTexture);
glCheckErrors();
}
void kinc_g4_render_target_set_depth_stencil_from(kinc_g4_render_target_t *renderTarget, kinc_g4_render_target_t *source) {
renderTarget->impl._depthTexture = source->impl._depthTexture;
glBindFramebuffer(GL_FRAMEBUFFER, renderTarget->impl._framebuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, renderTarget->isCubeMap ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D, renderTarget->impl._depthTexture,
0);
}
void kinc_g4_render_target_get_pixels(kinc_g4_render_target_t *renderTarget, uint8_t *data) {
glBindFramebuffer(GL_FRAMEBUFFER, renderTarget->impl._framebuffer);
switch ((kinc_g4_render_target_format_t)renderTarget->impl.format) {
case KINC_G4_RENDER_TARGET_FORMAT_128BIT_FLOAT:
glReadPixels(0, 0, renderTarget->texWidth, renderTarget->texHeight, GL_RGBA, GL_FLOAT, data);
break;
case KINC_G4_RENDER_TARGET_FORMAT_64BIT_FLOAT:
glReadPixels(0, 0, renderTarget->texWidth, renderTarget->texHeight, GL_RGBA, GL_HALF_FLOAT, data);
break;
case KINC_G4_RENDER_TARGET_FORMAT_8BIT_RED:
glReadPixels(0, 0, renderTarget->texWidth, renderTarget->texHeight, GL_RED, GL_UNSIGNED_BYTE, data);
break;
case KINC_G4_RENDER_TARGET_FORMAT_16BIT_RED_FLOAT:
glReadPixels(0, 0, renderTarget->texWidth, renderTarget->texHeight, GL_RED, GL_HALF_FLOAT, data);
break;
case KINC_G4_RENDER_TARGET_FORMAT_32BIT_RED_FLOAT:
glReadPixels(0, 0, renderTarget->texWidth, renderTarget->texHeight, GL_RED, GL_FLOAT, data);
break;
case KINC_G4_RENDER_TARGET_FORMAT_32BIT:
default:
glReadPixels(0, 0, renderTarget->texWidth, renderTarget->texHeight, GL_RGBA, GL_UNSIGNED_BYTE, data);
}
}
void kinc_g4_render_target_generate_mipmaps(kinc_g4_render_target_t *renderTarget, int levels) {
glBindTexture(GL_TEXTURE_2D, renderTarget->impl._texture);
glCheckErrors();
glGenerateMipmap(GL_TEXTURE_2D);
glCheckErrors();
}