RetroArch/source/custom_shaders.c
Francisco José García García 51922ea5be Squashed 'deps/vitaGL/' changes from c816fec50f..2934af8af0
2934af8af0 Added Patreon sponsor link.
c8f18b6f0f Getting current program only when required for vglDrawObjects.
4c5d136b0d Added directive to enable vitaShaRK usage from cmd.
4a10df3be5 Minor adjustments and bugfixes.
14a0124acf Added GL_TEXTURE_LOD_BIAS support.
40c8c6205e Added GL_NONE def and fixed glUniform4f impl.
868079c51e Added glUniform4f implementation.
0a682cbad2 Typo fix.
be3ce61ae7 Added GL_DEPTH_BITS and GL_STENCIL_BITS support.
21e6d1d330 Added runtime shader compiler support.
696e40bc62 Beautify error handler code.
537b37b110 Added glUniform3fv implementation.
7dd1403015 Fixed GLenum size and added missing types defines.
0c75f27ff1 Moved to NEON optimized memcpy usage.
98951895de Added gluPerspective implementation.
23e0b0b309 Fix for vglInitExtended not working on sys app mode.
4989c33ef5 Run clang-format.
429f1c1d8a Added system mode support.
9231680d02 Initializing sceGxm before free mem checking on vglInitExtended.
091e5e7882 Added vglInitWithCustomSizes.
f4c646ea78 Added vglSetParamBufferSize.
1b9a063c41 Beautify some code.
089e81efc5 Fix for duplicated symbols
789dcbf812 Typo fix in readRGBA4444.
1514a4b2cb Disabling lto due to it being broken on vitasdk with gcc 9.1.
fca18d9ab7 Added support for RGBA4444 texture format.
d449f12808 Added support for RGB565 texture format.

git-subtree-dir: deps/vitaGL
git-subtree-split: 2934af8af083a9acf598ab75233c518a251c6f0d
2020-07-05 11:43:47 +02:00

574 lines
16 KiB
C

/*
* This file is part of vitaGL
* Copyright 2017, 2018, 2019, 2020 Rinnegatamante
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* custom_shaders.c:
* Implementation for custom shaders feature
*/
#include "shared.h"
#define MAX_CUSTOM_SHADERS 64 // Maximum number of linkable custom shaders
#define MAX_SHADER_PARAMS 8 // Maximum number of parameters per custom shader
// Internal stuffs
void *frag_uniforms = NULL;
void *vert_uniforms = NULL;
uint8_t use_shark = 1; // Flag to check if vitaShaRK should be initialized at vitaGL boot
uint8_t is_shark_online = 0; // Current vitaShaRK status
GLuint cur_program = 0; // Current in use custom program (0 = No custom program)
// Uniform struct
typedef struct uniform {
GLboolean isVertex;
const SceGxmProgramParameter *ptr;
void *chain;
} uniform;
// Generic shader struct
typedef struct shader {
GLenum type;
GLboolean valid;
SceGxmShaderPatcherId id;
const SceGxmProgram *prog;
uint32_t size;
} shader;
// Program struct holding vertex/fragment shader info
typedef struct program {
shader *vshader;
shader *fshader;
GLboolean valid;
SceGxmVertexAttribute attr[MAX_SHADER_PARAMS];
SceGxmVertexStream stream[MAX_SHADER_PARAMS];
SceGxmVertexProgram *vprog;
SceGxmFragmentProgram *fprog;
GLuint attr_num;
const SceGxmProgramParameter *wvp;
uniform *uniforms;
uniform *last_uniform;
} program;
// Internal shaders array
static shader shaders[MAX_CUSTOM_SHADERS];
// Internal programs array
static program progs[MAX_CUSTOM_SHADERS / 2];
void resetCustomShaders(void) {
// Init custom shaders
int i;
for (i = 0; i < MAX_CUSTOM_SHADERS; i++) {
shaders[i].valid = 0;
progs[i >> 1].valid = 0;
}
}
void changeCustomShadersBlend(SceGxmBlendInfo *blend_info) {
int j;
for (j = 0; j < MAX_CUSTOM_SHADERS / 2; j++) {
program *p = &progs[j];
if (p->valid) {
sceGxmShaderPatcherCreateFragmentProgram(gxm_shader_patcher,
p->fshader->id,
SCE_GXM_OUTPUT_REGISTER_FORMAT_UCHAR4,
msaa_mode,
blend_info,
p->vshader->prog,
&p->fprog);
}
}
}
void reloadCustomShader(void) {
if (cur_program == 0)
return;
program *p = &progs[cur_program - 1];
sceGxmSetVertexProgram(gxm_context, p->vprog);
sceGxmSetFragmentProgram(gxm_context, p->fprog);
}
void _vglDrawObjects_CustomShadersIMPL(GLenum mode, GLsizei count, GLboolean implicit_wvp) {
if (implicit_wvp) {
program *p = &progs[cur_program - 1];
if (mvp_modified) {
matrix4x4_multiply(mvp_matrix, projection_matrix, modelview_matrix);
mvp_modified = GL_FALSE;
}
if (vert_uniforms == NULL)
sceGxmReserveVertexDefaultUniformBuffer(gxm_context, &vert_uniforms);
if (p->wvp == NULL)
p->wvp = sceGxmProgramFindParameterByName(p->vshader->prog, "wvp");
sceGxmSetUniformDataF(vert_uniforms, p->wvp, 0, 16, (const float *)mvp_matrix);
}
}
/*
* ------------------------------
* - IMPLEMENTATION STARTS HERE -
* ------------------------------
*/
void vglEnableRuntimeShaderCompiler(GLboolean usage) {
use_shark = usage;
}
GLuint glCreateShader(GLenum shaderType) {
// Looking for a free shader slot
GLuint i, res = 0;
for (i = 1; i < MAX_CUSTOM_SHADERS; i++) {
if (!(shaders[i - 1].valid)) {
res = i;
break;
}
}
// All shader slots are busy, exiting call
if (res == 0)
return res;
// Reserving and initializing shader slot
switch (shaderType) {
case GL_FRAGMENT_SHADER:
shaders[res - 1].type = GL_FRAGMENT_SHADER;
break;
case GL_VERTEX_SHADER:
shaders[res - 1].type = GL_VERTEX_SHADER;
break;
default:
vgl_error = GL_INVALID_ENUM;
return 0;
break;
}
shaders[res - 1].valid = GL_TRUE;
return res;
}
void glGetShaderiv(GLuint handle, GLenum pname, GLint *params) {
// Grabbing passed shader
shader *s = &shaders[handle - 1];
switch (pname) {
case GL_SHADER_TYPE:
*params = s->type;
break;
case GL_COMPILE_STATUS:
*params = s->prog ? GL_TRUE : GL_FALSE;
break;
default:
SET_GL_ERROR(GL_INVALID_ENUM)
break;
}
}
void glShaderSource(GLuint handle, GLsizei count, const GLchar * const *string, const GLint *length) {
#ifndef SKIP_ERROR_HANDLING
if (count < 0) {
SET_GL_ERROR(GL_INVALID_VALUE)
}
#endif
if (!is_shark_online) {
SET_GL_ERROR(GL_INVALID_OPERATION)
}
// Grabbing passed shader
shader *s = &shaders[handle - 1];
// Temporarily setting prog to point to the shader source
s->prog = (SceGxmProgram *)string;
s->size = *length;
}
void glShaderBinary(GLsizei count, const GLuint *handles, GLenum binaryFormat, const void *binary, GLsizei length) {
// Grabbing passed shader
shader *s = &shaders[handles[0] - 1];
// Allocating compiled shader on RAM and registering it into sceGxmShaderPatcher
s->prog = (SceGxmProgram *)malloc(length);
memcpy_neon((void *)s->prog, binary, length);
sceGxmShaderPatcherRegisterProgram(gxm_shader_patcher, s->prog, &s->id);
s->prog = sceGxmShaderPatcherGetProgramFromId(s->id);
}
void glCompileShader(GLuint handle) {
// If vitaShaRK is not enabled, we just error out
if (!is_shark_online) {
SET_GL_ERROR(GL_INVALID_OPERATION)
}
#ifdef HAVE_SHARK
// Grabbing passed shader
shader *s = &shaders[handle - 1];
// Compiling shader source
s->prog = shark_compile_shader((const char*)s->prog, &s->size, s->type == GL_FRAGMENT_SHADER ? SHARK_FRAGMENT_SHADER : SHARK_VERTEX_SHADER);
if (s->prog) {
SceGxmProgram *res = (SceGxmProgram *)malloc(s->size);
memcpy_neon((void *)res, (void *)s->prog, s->size);
s->prog = res;
sceGxmShaderPatcherRegisterProgram(gxm_shader_patcher, s->prog, &s->id);
s->prog = sceGxmShaderPatcherGetProgramFromId(s->id);
}
shark_clear_output();
#endif
}
void glDeleteShader(GLuint shad) {
// Grabbing passed shader
shader *s = &shaders[shad - 1];
// Deallocating shader and unregistering it from sceGxmShaderPatcher
if (s->valid) {
sceGxmShaderPatcherForceUnregisterProgram(gxm_shader_patcher, s->id);
free((void *)s->prog);
}
s->valid = GL_FALSE;
}
void glAttachShader(GLuint prog, GLuint shad) {
// Grabbing passed shader and program
shader *s = &shaders[shad - 1];
program *p = &progs[prog - 1];
// Attaching shader to desired program
if (p->valid && s->valid) {
switch (s->type) {
case GL_VERTEX_SHADER:
p->vshader = s;
break;
case GL_FRAGMENT_SHADER:
p->fshader = s;
break;
default:
break;
}
} else {
SET_GL_ERROR(GL_INVALID_VALUE)
}
}
GLuint glCreateProgram(void) {
// Looking for a free program slot
GLuint i, res = 0;
for (i = 1; i < (MAX_CUSTOM_SHADERS / 2); i++) {
// Program slot found, reserving and initializing it
if (!(progs[i - 1].valid)) {
res = i;
progs[i - 1].valid = GL_TRUE;
progs[i - 1].attr_num = 0;
progs[i - 1].wvp = NULL;
progs[i - 1].uniforms = NULL;
progs[i - 1].last_uniform = NULL;
break;
}
}
return res;
}
void glDeleteProgram(GLuint prog) {
// Grabbing passed program
program *p = &progs[prog - 1];
// Releasing both vertex and fragment programs from sceGxmShaderPatcher
if (p->valid) {
unsigned int count, i;
sceGxmShaderPatcherGetFragmentProgramRefCount(gxm_shader_patcher, p->fprog, &count);
for (i = 0; i < count; i++) {
sceGxmShaderPatcherReleaseFragmentProgram(gxm_shader_patcher, p->fprog);
sceGxmShaderPatcherReleaseVertexProgram(gxm_shader_patcher, p->vprog);
}
while (p->uniforms != NULL) {
uniform *old = p->uniforms;
p->uniforms = (uniform *)p->uniforms->chain;
free(old);
}
}
p->valid = GL_FALSE;
}
void glLinkProgram(GLuint progr) {
// Grabbing passed program
program *p = &progs[progr - 1];
// Creating fragment and vertex program via sceGxmShaderPatcher
sceGxmShaderPatcherCreateVertexProgram(gxm_shader_patcher,
p->vshader->id, p->attr, p->attr_num,
p->stream, p->attr_num, &p->vprog);
sceGxmShaderPatcherCreateFragmentProgram(gxm_shader_patcher,
p->fshader->id, SCE_GXM_OUTPUT_REGISTER_FORMAT_UCHAR4,
msaa_mode, NULL, p->vshader->prog,
&p->fprog);
}
void glUseProgram(GLuint prog) {
// Setting current custom program to passed program
cur_program = prog;
// Setting in-use vertex and fragment program in sceGxm
reloadCustomShader();
}
GLint glGetUniformLocation(GLuint prog, const GLchar *name) {
// Grabbing passed program
program *p = &progs[prog - 1];
uniform *res = (uniform *)malloc(sizeof(uniform));
res->chain = NULL;
if (p->last_uniform != NULL)
p->last_uniform->chain = (void *)res;
p->last_uniform = res;
// Checking if parameter is a vertex or fragment related one
res->ptr = sceGxmProgramFindParameterByName(p->vshader->prog, name);
res->isVertex = GL_TRUE;
if (res->ptr == NULL) {
res->ptr = sceGxmProgramFindParameterByName(p->fshader->prog, name);
res->isVertex = GL_FALSE;
}
return (GLint)res;
}
void glUniform1i(GLint location, GLint v0) {
// Grabbing passed uniform
uniform *u = (uniform *)location;
if (u->ptr == NULL)
return;
// Setting passed value to desired uniform
if (u->isVertex) {
if (vert_uniforms == NULL)
sceGxmReserveVertexDefaultUniformBuffer(gxm_context, &vert_uniforms);
float v0_f = (float)v0;
sceGxmSetUniformDataF(vert_uniforms, u->ptr, 0, 1, &v0_f);
} else {
if (frag_uniforms == NULL)
sceGxmReserveFragmentDefaultUniformBuffer(gxm_context, &frag_uniforms);
float v0_f = (float)v0;
sceGxmSetUniformDataF(frag_uniforms, u->ptr, 0, 1, &v0_f);
}
}
void glUniform1f(GLint location, GLfloat v0) {
// Grabbing passed uniform
uniform *u = (uniform *)location;
if (u->ptr == NULL)
return;
// Setting passed value to desired uniform
if (u->isVertex) {
if (vert_uniforms == NULL)
sceGxmReserveVertexDefaultUniformBuffer(gxm_context, &vert_uniforms);
sceGxmSetUniformDataF(vert_uniforms, u->ptr, 0, 1, &v0);
} else {
if (frag_uniforms == NULL)
sceGxmReserveFragmentDefaultUniformBuffer(gxm_context, &frag_uniforms);
sceGxmSetUniformDataF(frag_uniforms, u->ptr, 0, 1, &v0);
}
}
void glUniform2fv(GLint location, GLsizei count, const GLfloat *value) {
// Grabbing passed uniform
uniform *u = (uniform *)location;
if (u->ptr == NULL)
return;
// Setting passed value to desired uniform
if (u->isVertex) {
if (vert_uniforms == NULL)
sceGxmReserveVertexDefaultUniformBuffer(gxm_context, &vert_uniforms);
sceGxmSetUniformDataF(vert_uniforms, u->ptr, 0, 2 * count, value);
} else {
if (frag_uniforms == NULL)
sceGxmReserveFragmentDefaultUniformBuffer(gxm_context, &frag_uniforms);
sceGxmSetUniformDataF(frag_uniforms, u->ptr, 0, 2 * count, value);
}
}
void glUniform3fv(GLint location, GLsizei count, const GLfloat *value) {
// Grabbing passed uniform
uniform *u = (uniform *)location;
if (u->ptr == NULL)
return;
// Setting passed value to desired uniform
if (u->isVertex) {
if (vert_uniforms == NULL)
sceGxmReserveVertexDefaultUniformBuffer(gxm_context, &vert_uniforms);
sceGxmSetUniformDataF(vert_uniforms, u->ptr, 0, 3 * count, value);
} else {
if (frag_uniforms == NULL)
sceGxmReserveFragmentDefaultUniformBuffer(gxm_context, &frag_uniforms);
sceGxmSetUniformDataF(frag_uniforms, u->ptr, 0, 3 * count, value);
}
}
void glUniform4f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3) {
// Grabbing passed uniform
uniform *u = (uniform *)location;
if (u->ptr == NULL)
return;
// Setting passed value to desired uniform
float v[4] = {v0, v1, v2, v3};
if (u->isVertex) {
if (vert_uniforms == NULL)
sceGxmReserveVertexDefaultUniformBuffer(gxm_context, &vert_uniforms);
sceGxmSetUniformDataF(vert_uniforms, u->ptr, 0, 4, v);
} else {
if (frag_uniforms == NULL)
sceGxmReserveFragmentDefaultUniformBuffer(gxm_context, &frag_uniforms);
sceGxmSetUniformDataF(frag_uniforms, u->ptr, 0, 4, v);
}
}
void glUniform4fv(GLint location, GLsizei count, const GLfloat *value) {
// Grabbing passed uniform
uniform *u = (uniform *)location;
if (u->ptr == NULL)
return;
// Setting passed value to desired uniform
if (u->isVertex) {
if (vert_uniforms == NULL)
sceGxmReserveVertexDefaultUniformBuffer(gxm_context, &vert_uniforms);
sceGxmSetUniformDataF(vert_uniforms, u->ptr, 0, 4 * count, value);
} else {
if (frag_uniforms == NULL)
sceGxmReserveFragmentDefaultUniformBuffer(gxm_context, &frag_uniforms);
sceGxmSetUniformDataF(frag_uniforms, u->ptr, 0, 4 * count, value);
}
}
void glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) {
// Grabbing passed uniform
uniform *u = (uniform *)location;
if (u->ptr == NULL)
return;
// Setting passed value to desired uniform
if (u->isVertex) {
if (vert_uniforms == NULL)
sceGxmReserveVertexDefaultUniformBuffer(gxm_context, &vert_uniforms);
sceGxmSetUniformDataF(vert_uniforms, u->ptr, 0, 16 * count, value);
} else {
if (frag_uniforms == NULL)
sceGxmReserveFragmentDefaultUniformBuffer(gxm_context, &frag_uniforms);
sceGxmSetUniformDataF(frag_uniforms, u->ptr, 0, 16 * count, value);
}
}
/*
* ------------------------------
* - VGL_EXT_gxp_shaders -
* ------------------------------
*/
void vglBindPackedAttribLocation(GLuint prog, GLuint index, const GLchar *name, const GLuint num, const GLenum type, GLuint offset, GLint stride) {
// Grabbing passed program
program *p = &progs[prog - 1];
SceGxmVertexAttribute *attributes = &p->attr[index];
SceGxmVertexStream *streams = &p->stream[index];
// Looking for desired parameter in requested program
const SceGxmProgramParameter *param = sceGxmProgramFindParameterByName(p->vshader->prog, name);
// Setting stream index and offset values
attributes->streamIndex = index;
attributes->offset = offset;
// Detecting attribute format and size
int bpe;
switch (type) {
case GL_FLOAT:
attributes->format = SCE_GXM_ATTRIBUTE_FORMAT_F32;
bpe = sizeof(float);
break;
case GL_UNSIGNED_BYTE:
attributes->format = SCE_GXM_ATTRIBUTE_FORMAT_U8N;
bpe = sizeof(uint8_t);
break;
default:
SET_GL_ERROR(GL_INVALID_ENUM)
break;
}
// Setting various info about the stream
attributes->componentCount = num;
attributes->regIndex = sceGxmProgramParameterGetResourceIndex(param);
streams->stride = stride ? stride : bpe * num;
streams->indexSource = SCE_GXM_INDEX_SOURCE_INDEX_16BIT;
if (index >= p->attr_num)
p->attr_num = index + 1;
}
// Equivalent of glBindAttribLocation but for sceGxm architecture
void vglBindAttribLocation(GLuint prog, GLuint index, const GLchar *name, const GLuint num, const GLenum type) {
vglBindPackedAttribLocation(prog, index, name, num, type, 0, 0);
}
// Equivalent of glVertexAttribPointer but for sceGxm architecture
void vglVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLuint count, const GLvoid *pointer) {
#ifndef SKIP_ERROR_HANDLING
// Error handling
if (stride < 0) {
SET_GL_ERROR(GL_INVALID_VALUE)
}
#endif
// Detecting type size
int bpe;
switch (type) {
case GL_FLOAT:
bpe = sizeof(GLfloat);
break;
case GL_SHORT:
bpe = sizeof(GLshort);
break;
default:
SET_GL_ERROR(GL_INVALID_ENUM)
break;
}
// Allocating enough memory on vitaGL mempool
void *ptr = gpu_pool_memalign(count * bpe * size, bpe * size);
// Copying passed data to vitaGL mempool
if (stride == 0)
memcpy_neon(ptr, pointer, count * bpe * size); // Faster if stride == 0
else {
int i;
uint8_t *dst = (uint8_t *)ptr;
uint8_t *src = (uint8_t *)pointer;
for (i = 0; i < count; i++) {
memcpy_neon(dst, src, bpe * size);
dst += (bpe * size);
src += stride;
}
}
// Setting vertex stream to passed index in sceGxm
sceGxmSetVertexStream(gxm_context, index, ptr);
}
void vglVertexAttribPointerMapped(GLuint index, const GLvoid *pointer) {
// Setting vertex stream to passed index in sceGxm
sceGxmSetVertexStream(gxm_context, index, pointer);
}