mirror of https://github.com/bjornbytes/lovr.git
261 lines
8.0 KiB
C
261 lines
8.0 KiB
C
#include "graphics/shader.h"
|
|
#include "graphics/graphics.h"
|
|
#include "graphics/buffer.h"
|
|
#include "math/math.h"
|
|
#include "resources/shaders.h"
|
|
#include <math.h>
|
|
|
|
static size_t getUniformTypeLength(const Uniform* uniform) {
|
|
size_t size = 0;
|
|
|
|
if (uniform->count > 1) {
|
|
size += 2 + floor(log10(uniform->count)) + 1; // "[count]"
|
|
}
|
|
|
|
switch (uniform->type) {
|
|
case UNIFORM_MATRIX: size += 4; break;
|
|
case UNIFORM_FLOAT: size += uniform->components == 1 ? 5 : 4; break;
|
|
case UNIFORM_INT: size += uniform->components == 1 ? 3 : 5; break;
|
|
default: break;
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
static const char* getUniformTypeName(const Uniform* uniform) {
|
|
switch (uniform->type) {
|
|
case UNIFORM_FLOAT:
|
|
switch (uniform->components) {
|
|
case 1: return "float";
|
|
case 2: return "vec2";
|
|
case 3: return "vec3";
|
|
case 4: return "vec4";
|
|
}
|
|
break;
|
|
|
|
case UNIFORM_INT:
|
|
switch (uniform->components) {
|
|
case 1: return "int";
|
|
case 2: return "ivec2";
|
|
case 3: return "ivec3";
|
|
case 4: return "ivec4";
|
|
}
|
|
break;
|
|
|
|
case UNIFORM_MATRIX:
|
|
switch (uniform->components) {
|
|
case 2: return "mat2";
|
|
case 3: return "mat3";
|
|
case 4: return "mat4";
|
|
}
|
|
break;
|
|
|
|
default: break;
|
|
}
|
|
|
|
lovrThrow("Unreachable");
|
|
return "";
|
|
}
|
|
|
|
Shader* lovrShaderInitDefault(Shader* shader, DefaultShader type) {
|
|
switch (type) {
|
|
case SHADER_DEFAULT: return lovrShaderInitGraphics(shader, NULL, NULL);
|
|
case SHADER_CUBE: return lovrShaderInitGraphics(shader, lovrCubeVertexShader, lovrCubeFragmentShader);
|
|
case SHADER_PANO: return lovrShaderInitGraphics(shader, lovrCubeVertexShader, lovrPanoFragmentShader);
|
|
case SHADER_FONT: return lovrShaderInitGraphics(shader, NULL, lovrFontFragmentShader);
|
|
case SHADER_FILL: return lovrShaderInitGraphics(shader, lovrFillVertexShader, NULL);
|
|
default: lovrThrow("Unknown default shader type"); return NULL;
|
|
}
|
|
}
|
|
|
|
ShaderType lovrShaderGetType(Shader* shader) {
|
|
return shader->type;
|
|
}
|
|
|
|
int lovrShaderGetAttributeLocation(Shader* shader, const char* name) {
|
|
int* location = map_get(&shader->attributes, name);
|
|
return location ? *location : -1;
|
|
}
|
|
|
|
bool lovrShaderHasUniform(Shader* shader, const char* name) {
|
|
return map_get(&shader->uniformMap, name) != NULL;
|
|
}
|
|
|
|
const Uniform* lovrShaderGetUniform(Shader* shader, const char* name) {
|
|
int* index = map_get(&shader->uniformMap, name);
|
|
if (!index) {
|
|
return false;
|
|
}
|
|
|
|
return &shader->uniforms.data[*index];
|
|
}
|
|
|
|
static void lovrShaderSetUniform(Shader* shader, const char* name, UniformType type, void* data, int start, int count, int size, const char* debug) {
|
|
int* index = map_get(&shader->uniformMap, name);
|
|
if (!index) {
|
|
return;
|
|
}
|
|
|
|
Uniform* uniform = &shader->uniforms.data[*index];
|
|
lovrAssert(uniform->type == type, "Unable to send %ss to uniform %s", debug, name);
|
|
lovrAssert((start + count) * size <= uniform->size, "Too many %ss for uniform %s, maximum is %d", debug, name, uniform->size / size);
|
|
|
|
void* dest = uniform->value.bytes + start * size;
|
|
if (memcmp(dest, data, count * size)) {
|
|
lovrGraphicsFlushShader(shader);
|
|
memcpy(dest, data, count * size);
|
|
uniform->dirty = true;
|
|
}
|
|
}
|
|
|
|
void lovrShaderSetFloats(Shader* shader, const char* name, float* data, int start, int count) {
|
|
lovrShaderSetUniform(shader, name, UNIFORM_FLOAT, data, start, count, sizeof(float), "float");
|
|
}
|
|
|
|
void lovrShaderSetInts(Shader* shader, const char* name, int* data, int start, int count) {
|
|
lovrShaderSetUniform(shader, name, UNIFORM_INT, data, start, count, sizeof(int), "int");
|
|
}
|
|
|
|
void lovrShaderSetMatrices(Shader* shader, const char* name, float* data, int start, int count) {
|
|
lovrShaderSetUniform(shader, name, UNIFORM_MATRIX, data, start, count, sizeof(float), "float");
|
|
}
|
|
|
|
void lovrShaderSetTextures(Shader* shader, const char* name, Texture** data, int start, int count) {
|
|
lovrShaderSetUniform(shader, name, UNIFORM_SAMPLER, data, start, count, sizeof(Texture*), "texture");
|
|
}
|
|
|
|
void lovrShaderSetImages(Shader* shader, const char* name, Image* data, int start, int count) {
|
|
lovrShaderSetUniform(shader, name, UNIFORM_IMAGE, data, start, count, sizeof(Image), "image");
|
|
}
|
|
|
|
void lovrShaderSetColor(Shader* shader, const char* name, Color color) {
|
|
if (lovrGraphicsIsGammaCorrect()) {
|
|
color.r = lovrMathGammaToLinear(color.r);
|
|
color.g = lovrMathGammaToLinear(color.g);
|
|
color.b = lovrMathGammaToLinear(color.b);
|
|
}
|
|
|
|
lovrShaderSetUniform(shader, name, UNIFORM_FLOAT, (float*) &color, 0, 4, sizeof(float), "float");
|
|
}
|
|
|
|
void lovrShaderSetBlock(Shader* shader, const char* name, Buffer* buffer, size_t offset, size_t size, UniformAccess access) {
|
|
int* id = map_get(&shader->blockMap, name);
|
|
if (!id) return;
|
|
|
|
int type = *id & 1;
|
|
int index = *id >> 1;
|
|
UniformBlock* block = &shader->blocks[type].data[index];
|
|
|
|
if (block->source != buffer || block->offset != offset || block->size != size) {
|
|
lovrGraphicsFlushShader(shader);
|
|
lovrRetain(buffer);
|
|
lovrRelease(Buffer, block->source);
|
|
block->access = access;
|
|
block->source = buffer;
|
|
block->offset = offset;
|
|
block->size = size;
|
|
}
|
|
}
|
|
|
|
// ShaderBlock
|
|
|
|
// Calculates uniform size and byte offsets using std140 rules, returning the total buffer size
|
|
size_t lovrShaderComputeUniformLayout(vec_uniform_t* uniforms) {
|
|
size_t size = 0;
|
|
Uniform* uniform; int i;
|
|
vec_foreach_ptr(uniforms, uniform, i) {
|
|
int align;
|
|
if (uniform->count > 1 || uniform->type == UNIFORM_MATRIX) {
|
|
align = 16 * (uniform->type == UNIFORM_MATRIX ? uniform->components : 1);
|
|
uniform->size = align * uniform->count;
|
|
} else {
|
|
align = (uniform->components + (uniform->components == 3)) * 4;
|
|
uniform->size = uniform->components * 4;
|
|
}
|
|
uniform->offset = (size + (align - 1)) & -align;
|
|
size = uniform->offset + uniform->size;
|
|
}
|
|
return size;
|
|
}
|
|
|
|
ShaderBlock* lovrShaderBlockInit(ShaderBlock* block, BlockType type, Buffer* buffer, vec_uniform_t* uniforms) {
|
|
vec_init(&block->uniforms);
|
|
map_init(&block->uniformMap);
|
|
|
|
Uniform* uniform; int i;
|
|
vec_extend(&block->uniforms, uniforms);
|
|
vec_foreach_ptr(&block->uniforms, uniform, i) {
|
|
map_set(&block->uniformMap, uniform->name, i);
|
|
}
|
|
|
|
block->type = type;
|
|
block->buffer = buffer;
|
|
lovrRetain(buffer);
|
|
return block;
|
|
}
|
|
|
|
void lovrShaderBlockDestroy(void* ref) {
|
|
ShaderBlock* block = ref;
|
|
lovrRelease(Buffer, block->buffer);
|
|
vec_deinit(&block->uniforms);
|
|
map_deinit(&block->uniformMap);
|
|
free(block);
|
|
}
|
|
|
|
BlockType lovrShaderBlockGetType(ShaderBlock* block) {
|
|
return block->type;
|
|
}
|
|
|
|
// TODO use sds!
|
|
char* lovrShaderBlockGetShaderCode(ShaderBlock* block, const char* blockName, size_t* length) {
|
|
|
|
// Calculate
|
|
size_t size = 0;
|
|
size_t tab = 2;
|
|
size += 15; // "layout(std140) "
|
|
size += block->type == BLOCK_UNIFORM ? 7 : 6; // "uniform" || "buffer"
|
|
size += 1; // " "
|
|
size += strlen(blockName);
|
|
size += 3; // " {\n"
|
|
for (int i = 0; i < block->uniforms.length; i++) {
|
|
size += tab;
|
|
size += getUniformTypeLength(&block->uniforms.data[i]);
|
|
size += 1; // " "
|
|
size += strlen(block->uniforms.data[i].name);
|
|
size += 2; // ";\n"
|
|
}
|
|
size += 3; // "};\n"
|
|
|
|
// Allocate
|
|
char* code = malloc(size + 1);
|
|
lovrAssert(code, "Out of memory");
|
|
|
|
// Concatenate
|
|
char* s = code;
|
|
s += sprintf(s, "layout(std140) %s %s {\n", block->type == BLOCK_UNIFORM ? "uniform" : "buffer", blockName);
|
|
for (int i = 0; i < block->uniforms.length; i++) {
|
|
const Uniform* uniform = &block->uniforms.data[i];
|
|
if (uniform->count > 1) {
|
|
s += sprintf(s, " %s %s[%d];\n", getUniformTypeName(uniform), uniform->name, uniform->count);
|
|
} else {
|
|
s += sprintf(s, " %s %s;\n", getUniformTypeName(uniform), uniform->name);
|
|
}
|
|
}
|
|
s += sprintf(s, "};\n");
|
|
*s = '\0';
|
|
|
|
*length = size;
|
|
return code;
|
|
}
|
|
|
|
const Uniform* lovrShaderBlockGetUniform(ShaderBlock* block, const char* name) {
|
|
int* index = map_get(&block->uniformMap, name);
|
|
if (!index) return NULL;
|
|
|
|
return &block->uniforms.data[*index];
|
|
}
|
|
|
|
Buffer* lovrShaderBlockGetBuffer(ShaderBlock* block) {
|
|
return block->buffer;
|
|
}
|