Shader:sendImage;

Can be used to bind images with specific slices, mipmap levels,
and access hints.
This commit is contained in:
bjorn 2018-08-17 19:52:34 -07:00
parent cab12ba945
commit d2b6def5c2
4 changed files with 194 additions and 135 deletions

View File

@ -119,3 +119,4 @@ void luax_checkvariant(lua_State* L, int index, Variant* variant);
int luax_pushvariant(lua_State* L, Variant* variant);
int luax_checkuniform(lua_State* L, int index, const Uniform* uniform, void* dest, const char* debug);
void luax_checkuniformtype(lua_State* L, int index, UniformType* baseType, int* components);
int luax_optmipmap(lua_State* L, int index, Texture* texture);

View File

@ -7,6 +7,7 @@ struct TempData {
int size;
};
// Not thread safe
static struct TempData tempData;
int luax_checkuniform(lua_State* L, int index, const Uniform* uniform, void* dest, const char* debug) {
@ -37,8 +38,8 @@ int luax_checkuniform(lua_State* L, int index, const Uniform* uniform, void* des
memcpy(dest, blob->data, elements * sizeof(int));
break;
case UNIFORM_TEXTURE:
lovrThrow("Texture uniform '%s' can not be updated with a Blob", debug);
case UNIFORM_SAMPLER: lovrThrow("Sampler uniform '%s' can not be updated with a Blob", debug);
case UNIFORM_IMAGE: lovrThrow("Image uniform '%s' can not be updated with a Blob", debug);
}
return 0;
@ -58,11 +59,23 @@ int luax_checkuniform(lua_State* L, int index, const Uniform* uniform, void* des
switch (uniform->type) {
case UNIFORM_FLOAT: *((float*) dest + i) = luaL_checknumber(L, j); break;
case UNIFORM_INT: *((int*) dest + i) = luaL_checkinteger(L, j); break;
case UNIFORM_TEXTURE:
case UNIFORM_SAMPLER:
*((Texture**) dest + i) = luax_checktype(L, j, Texture);
TextureType type = lovrTextureGetType(*((Texture**) dest + i));
lovrAssert(type == uniform->textureType, "Attempt to send %s texture to %s texture uniform", TextureTypes[type], TextureTypes[uniform->textureType]);
lovrAssert(type == uniform->textureType, "Attempt to send %s texture to %s sampler uniform", TextureTypes[type], TextureTypes[uniform->textureType]);
break;
case UNIFORM_IMAGE: {
Image* image = (Image*) dest + i;
image->texture = luax_checktype(L, j, Texture);
image->slice = 0;
image->mipmap = 0;
image->access = ACCESS_READ_WRITE;
TextureType type = lovrTextureGetType(image->texture);
lovrAssert(type == uniform->textureType, "Attempt to send %s texture to %s image uniform", TextureTypes[type], TextureTypes[uniform->textureType]);
break;
}
default: break;
}
@ -99,7 +112,9 @@ int luax_checkuniform(lua_State* L, int index, const Uniform* uniform, void* des
*((int*) dest + i * components + j) = luaL_checkinteger(L, -1);
break;
case UNIFORM_TEXTURE: lovrThrow("Unreachable");
case UNIFORM_SAMPLER:
case UNIFORM_IMAGE:
lovrThrow("Unreachable");
}
lua_pop(L, 1);
}
@ -126,7 +141,9 @@ int luax_checkuniform(lua_State* L, int index, const Uniform* uniform, void* des
*((float*) dest + i * components + j) = luaL_checknumber(L, -1);
break;
case UNIFORM_TEXTURE: lovrThrow("Unreachable");
case UNIFORM_SAMPLER:
case UNIFORM_IMAGE:
lovrThrow("Unreachable");
}
}
}
@ -191,23 +208,16 @@ int l_lovrShaderSend(lua_State* L) {
luax_checkuniform(L, 3, uniform, tempData.data, name);
switch (uniform->type) {
case UNIFORM_FLOAT: lovrShaderSetFloat(shader, uniform->name, tempData.data, uniform->count * uniform->components); break;
case UNIFORM_INT: lovrShaderSetInt(shader, uniform->name, tempData.data, uniform->count * uniform->components); break;
case UNIFORM_MATRIX: lovrShaderSetMatrix(shader, uniform->name, tempData.data, uniform->count * uniform->components * uniform->components); break;
case UNIFORM_TEXTURE: lovrShaderSetTexture(shader, uniform->name, tempData.data, uniform->count); break;
case UNIFORM_FLOAT: lovrShaderSetFloats(shader, uniform->name, tempData.data, 0, uniform->count * uniform->components); break;
case UNIFORM_INT: lovrShaderSetInts(shader, uniform->name, tempData.data, 0, uniform->count * uniform->components); break;
case UNIFORM_MATRIX: lovrShaderSetMatrices(shader, uniform->name, tempData.data, 0, uniform->count * uniform->components * uniform->components); break;
case UNIFORM_SAMPLER: lovrShaderSetTextures(shader, uniform->name, tempData.data, 0, uniform->count); break;
case UNIFORM_IMAGE: lovrShaderSetImages(shader, uniform->name, tempData.data, 0, uniform->count); break;
}
return 0;
}
int l_lovrShaderGetBlock(lua_State* L) {
Shader* shader = luax_checktype(L, 1, Shader);
const char* name = luaL_checkstring(L, 2);
ShaderBlock* block = lovrShaderGetBlock(shader, name);
luax_pushobject(L, block);
return 1;
}
int l_lovrShaderSetBlock(lua_State* L) {
int l_lovrShaderSendBlock(lua_State* L) {
Shader* shader = luax_checktype(L, 1, Shader);
const char* name = luaL_checkstring(L, 2);
ShaderBlock* block = luax_checktype(L, 3, ShaderBlock);
@ -216,11 +226,30 @@ int l_lovrShaderSetBlock(lua_State* L) {
return 0;
}
int l_lovrShaderSendImage(lua_State* L) {
int index = 1;
Shader* shader = luax_checktype(L, index++, Shader);
const char* name = luaL_checkstring(L, index++);
int start = 0;
if (lua_type(L, index) == LUA_TNUMBER) {
start = lua_tointeger(L, index++);
}
Texture* texture = luax_checktype(L, index++, Texture);
int slice = luaL_optinteger(L, index++, 0) - 1; // Default is -1
int mipmap = luax_optmipmap(L, index++, texture);
UniformAccess access = luaL_checkoption(L, index++, "readwrite", UniformAccesses);
Image image = { .texture = texture, .slice = slice, .mipmap = mipmap, .access = access };
lovrShaderSetImages(shader, name, &image, start, 1);
return 0;
}
const luaL_Reg lovrShader[] = {
{ "getType", l_lovrShaderGetType },
{ "hasUniform", l_lovrShaderHasUniform },
{ "send", l_lovrShaderSend },
{ "getBlock", l_lovrShaderGetBlock },
{ "setBlock", l_lovrShaderSetBlock },
{ "sendBlock", l_lovrShaderSendBlock },
{ "sendImage", l_lovrShaderSendImage },
{ NULL, NULL }
};

View File

@ -55,7 +55,7 @@ static struct {
uint32_t indexBuffer;
uint32_t program;
Texture* textures[MAX_TEXTURES];
Texture* images[MAX_IMAGES];
Image images[MAX_IMAGES];
uint32_t blockBuffers[2][MAX_BLOCK_BUFFERS];
uint32_t vertexArray;
uint32_t vertexBuffer;
@ -81,16 +81,6 @@ struct ShaderBlock {
uint8_t incoherent;
};
typedef struct {
int index;
int binding;
ShaderBlock* source;
vec_uniform_t uniforms;
UniformAccess access;
} UniformBlock;
typedef vec_t(UniformBlock) vec_block_t;
struct Shader {
Ref ref;
ShaderType type;
@ -326,13 +316,14 @@ static UniformType getUniformType(GLenum type, const char* debug) {
case GL_SAMPLER_3D:
case GL_SAMPLER_CUBE:
case GL_SAMPLER_2D_ARRAY:
return UNIFORM_SAMPLER;
#ifdef GL_ARB_shader_image_load_store
case GL_IMAGE_2D:
case GL_IMAGE_3D:
case GL_IMAGE_CUBE:
case GL_IMAGE_2D_ARRAY:
return UNIFORM_IMAGE;
#endif
return UNIFORM_TEXTURE;
default:
lovrThrow("Unsupported uniform type for uniform '%s'", debug);
return UNIFORM_FLOAT;
@ -360,18 +351,16 @@ static int getUniformComponents(GLenum type) {
static TextureType getUniformTextureType(GLenum type) {
switch (type) {
case GL_SAMPLER_2D:
case GL_IMAGE_2D:
return TEXTURE_2D;
case GL_SAMPLER_3D:
case GL_IMAGE_3D:
return TEXTURE_VOLUME;
case GL_SAMPLER_CUBE:
case GL_IMAGE_CUBE:
return TEXTURE_CUBE;
case GL_SAMPLER_2D_ARRAY:
case GL_IMAGE_2D_ARRAY:
return TEXTURE_ARRAY;
case GL_SAMPLER_2D: return TEXTURE_2D;
case GL_SAMPLER_3D: return TEXTURE_VOLUME;
case GL_SAMPLER_CUBE: return TEXTURE_CUBE;
case GL_SAMPLER_2D_ARRAY: return TEXTURE_ARRAY;
#ifdef GL_ARB_shader_image_load_store
case GL_IMAGE_2D: return TEXTURE_2D;
case GL_IMAGE_3D: return TEXTURE_VOLUME;
case GL_IMAGE_CUBE: return TEXTURE_CUBE;
case GL_IMAGE_2D_ARRAY: return TEXTURE_ARRAY;
#endif
default: return -1;
}
}
@ -428,6 +417,7 @@ static const char* getUniformTypeName(const Uniform* uniform) {
return "";
}
// TODO really ought to have TextureType-specific default textures
static Texture* lovrGpuGetDefaultTexture() {
if (!state.defaultTexture) {
TextureData* textureData = lovrTextureDataGetBlank(1, 1, 0xff, FORMAT_RGBA);
@ -490,34 +480,38 @@ void lovrGpuDirtyTexture(int slot) {
state.textures[slot] = NULL;
}
static void lovrGpuBindImage(Texture* texture, int slot, UniformAccess access) {
static void lovrGpuBindImage(Image* image, int slot) {
#ifndef EMSCRIPTEN
lovrThrow("Shaders can not write to textures on this system");
#endif
lovrAssert(slot >= 0 && slot < MAX_IMAGES, "Invalid image slot %d", slot);
texture = texture ? texture : lovrGpuGetDefaultTexture();
if (texture != state.images[slot]) {
// This is a risky way to compare the two structs
if (memcmp(state.images + slot, image, sizeof(Image))) {
Texture* texture = image->texture ? image->texture : lovrGpuGetDefaultTexture();
lovrAssert(!texture->srgb, "sRGB textures can not be used as image uniforms");
lovrAssert(!isTextureFormatCompressed(texture->format), "Compressed textures can not be used as image uniforms");
lovrAssert(texture->format != FORMAT_RGB && texture->format != FORMAT_RGBA4 && texture->format != FORMAT_RGB5A1, "Unsupported texture format for image uniform");
GLenum glAccess = convertAccess(access);
lovrAssert(image->mipmap >= 0 && image->mipmap < texture->mipmapCount, "Invalid mipmap level '%d' for image uniform", image->mipmap);
lovrAssert(image->slice < texture->depth, "Invalid texture slice '%d' for image uniform", image->slice);
GLenum glAccess = convertAccess(image->access);
GLenum glFormat = convertTextureFormatInternal(texture->format, false);
lovrRetain(texture);
lovrRelease(state.images[slot]);
state.images[slot] = texture;
glBindImageTexture(slot, texture->id, 0, true, 0, glAccess, glFormat);
lovrRelease(state.images[slot].texture);
glBindImageTexture(slot, texture->id, image->mipmap, image->slice == -1, image->slice, glAccess, glFormat);
memcpy(state.images + slot, image, sizeof(Image));
}
#endif
}
static void lovrGpuBindBlockBuffer(BlockType type, uint32_t buffer, int slot) {
#ifndef EMSCRIPTEN
lovrAssert(type == BLOCK_UNIFORM, "Writable ShaderBlocks are not supported on this system");
#endif
if (state.blockBuffers[type][slot] != buffer) {
state.blockBuffers[type][slot] = buffer;
#ifdef EMSCRIPTEN
glBindBufferBase(GL_UNIFORM_BUFFER, slot, buffer);
#else
glBindBufferBase(type == BLOCK_UNIFORM ? GL_UNIFORM_BUFFER : GL_SHADER_STORAGE_BUFFER, slot, buffer);
#endif
}
}
@ -588,6 +582,9 @@ void lovrGpuDestroy() {
for (int i = 0; i < MAX_TEXTURES; i++) {
lovrRelease(state.textures[i]);
}
for (int i = 0; i < MAX_IMAGES; i++) {
lovrRelease(state.images[i].texture);
}
for (int i = 0; i < MAX_BARRIERS; i++) {
vec_deinit(&state.incoherents[i]);
}
@ -797,14 +794,14 @@ void lovrGpuDraw(DrawCommand* command) {
}
// Transform
lovrShaderSetMatrix(shader, "lovrModel", command->transform, 16);
lovrShaderSetMatrix(shader, "lovrViews", command->camera.viewMatrix[0], 32);
lovrShaderSetMatrix(shader, "lovrProjections", command->camera.projection[0], 32);
lovrShaderSetMatrices(shader, "lovrModel", command->transform, 0, 16);
lovrShaderSetMatrices(shader, "lovrViews", command->camera.viewMatrix[0], 0, 32);
lovrShaderSetMatrices(shader, "lovrProjections", command->camera.projection[0], 0, 32);
float modelView[32];
mat4_multiply(mat4_set(modelView, command->camera.viewMatrix[0]), command->transform);
mat4_multiply(mat4_set(modelView + 16, command->camera.viewMatrix[1]), command->transform);
lovrShaderSetMatrix(shader, "lovrTransforms", modelView, 32);
lovrShaderSetMatrices(shader, "lovrTransforms", modelView, 0, 32);
if (lovrShaderHasUniform(shader, "lovrNormalMatrices")) {
if (mat4_invert(modelView) && mat4_invert(modelView + 16)) {
@ -825,47 +822,47 @@ void lovrGpuDraw(DrawCommand* command) {
modelView[24], modelView[25], modelView[26]
};
lovrShaderSetMatrix(shader, "lovrNormalMatrices", normalMatrices, 18);
lovrShaderSetMatrices(shader, "lovrNormalMatrices", normalMatrices, 0, 18);
}
// Pose
float* pose = lovrMeshGetPose(mesh);
if (pose) {
lovrShaderSetMatrix(shader, "lovrPose", pose, MAX_BONES * 16);
lovrShaderSetMatrices(shader, "lovrPose", pose, 0, MAX_BONES * 16);
} else {
float identity[16];
mat4_identity(identity);
lovrShaderSetMatrix(shader, "lovrPose", identity, 16);
lovrShaderSetMatrices(shader, "lovrPose", identity, 0, 16);
}
// Point size
lovrShaderSetFloat(shader, "lovrPointSize", &pipeline->pointSize, 1);
lovrShaderSetFloats(shader, "lovrPointSize", &pipeline->pointSize, 0, 1);
// Color
Color color = pipeline->color;
gammaCorrectColor(&color);
float data[4] = { color.r, color.g, color.b, color.a };
lovrShaderSetFloat(shader, "lovrColor", data, 4);
lovrShaderSetFloats(shader, "lovrColor", data, 0, 4);
// Material
for (int i = 0; i < MAX_MATERIAL_SCALARS; i++) {
float value = lovrMaterialGetScalar(material, i);
lovrShaderSetFloat(shader, lovrShaderScalarUniforms[i], &value, 1);
lovrShaderSetFloats(shader, lovrShaderScalarUniforms[i], &value, 0, 1);
}
for (int i = 0; i < MAX_MATERIAL_COLORS; i++) {
Color color = lovrMaterialGetColor(material, i);
gammaCorrectColor(&color);
float data[4] = { color.r, color.g, color.b, color.a };
lovrShaderSetFloat(shader, lovrShaderColorUniforms[i], data, 4);
lovrShaderSetFloats(shader, lovrShaderColorUniforms[i], data, 0, 4);
}
for (int i = 0; i < MAX_MATERIAL_TEXTURES; i++) {
Texture* texture = lovrMaterialGetTexture(material, i);
lovrShaderSetTexture(shader, lovrShaderTextureUniforms[i], &texture, 1);
lovrShaderSetTextures(shader, lovrShaderTextureUniforms[i], &texture, 0, 1);
}
lovrShaderSetMatrix(shader, "lovrMaterialTransform", material->transform, 9);
lovrShaderSetMatrices(shader, "lovrMaterialTransform", material->transform, 0, 9);
// Canvas
Canvas** canvas = pipeline->canvasCount > 0 ? pipeline->canvas : &command->camera.canvas;
@ -929,7 +926,7 @@ void lovrGpuDraw(DrawCommand* command) {
// Bind uniforms
int eye = (stereo && state.supportsSinglepass) ? -1 : i;
lovrShaderSetInt(shader, "lovrEye", &eye, 1);
lovrShaderSetInts(shader, "lovrEye", &eye, 0, 1);
lovrShaderBind(shader);
uint32_t rangeStart, rangeCount;
@ -1468,8 +1465,8 @@ static void lovrShaderSetupUniforms(Shader* shader) {
vec_init(uniformBlocks);
vec_reserve(uniformBlocks, blockCount);
for (int i = 0; i < blockCount; i++) {
UniformBlock block = { .index = i, .binding = i, .source = NULL };
glUniformBlockBinding(program, block.index, block.binding);
UniformBlock block = { .slot = i, .source = NULL };
glUniformBlockBinding(program, i, block.slot);
vec_init(&block.uniforms);
char name[LOVR_MAX_UNIFORM_LENGTH];
@ -1491,8 +1488,8 @@ static void lovrShaderSetupUniforms(Shader* shader) {
lovrAssert(storageCount <= MAX_BLOCK_BUFFERS, "Shader has too many writable blocks (%d) the max is %d", storageCount, MAX_BLOCK_BUFFERS);
vec_reserve(storageBlocks, storageCount);
for (int i = 0; i < storageCount; i++) {
UniformBlock block = { .index = i, .binding = i, .source = NULL };
glShaderStorageBlockBinding(program, block.index, block.binding);
UniformBlock block = { .slot = i, .source = NULL };
glShaderStorageBlockBinding(program, i, block.slot);
vec_init(&block.uniforms);
char name[LOVR_MAX_UNIFORM_LENGTH];
@ -1531,6 +1528,7 @@ static void lovrShaderSetupUniforms(Shader* shader) {
// Uniform introspection
int32_t uniformCount;
int textureSlot = 0;
int imageSlot = 0;
map_init(&shader->uniformMap);
vec_init(&shader->uniforms);
glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &uniformCount);
@ -1552,8 +1550,8 @@ static void lovrShaderSetupUniforms(Shader* shader) {
#else
uniform.image = glType == GL_IMAGE_2D || glType == GL_IMAGE_3D || glType == GL_IMAGE_CUBE || glType == GL_IMAGE_2D_ARRAY;
#endif
uniform.textureType = (uniform.type == UNIFORM_TEXTURE) ? getUniformTextureType(glType) : -1;
uniform.baseTextureSlot = uniform.type == UNIFORM_TEXTURE ? textureSlot : -1;
uniform.textureType = getUniformTextureType(glType);
uniform.baseSlot = uniform.type == UNIFORM_SAMPLER ? textureSlot : (uniform.type == UNIFORM_IMAGE ? imageSlot : -1);
int blockIndex;
glGetActiveUniformsiv(program, 1, &i, GL_UNIFORM_BLOCK_INDEX, &blockIndex);
@ -1595,15 +1593,17 @@ static void lovrShaderSetupUniforms(Shader* shader) {
uniform.value.data = calloc(1, uniform.size);
break;
case UNIFORM_TEXTURE:
uniform.size = uniform.components * uniform.count * MAX(sizeof(Texture*), sizeof(int));
case UNIFORM_SAMPLER:
case UNIFORM_IMAGE:
uniform.size = uniform.count * (uniform.type == UNIFORM_SAMPLER ? sizeof(Texture*) : sizeof(Image));
uniform.value.data = calloc(1, uniform.size);
// Use the value for ints to bind texture slots, but use the value for textures afterwards.
for (int i = 0; i < uniform.count; i++) {
uniform.value.ints[i] = uniform.baseTextureSlot + i;
uniform.value.ints[i] = uniform.baseSlot + i;
}
glUniform1iv(uniform.location, uniform.count, uniform.value.ints);
memset(uniform.value.data, 0, uniform.size);
break;
}
@ -1633,14 +1633,14 @@ static void lovrShaderSetupUniforms(Shader* shader) {
offset += uniform.components * uniform.components;
break;
default:
break;
default: break;
}
}
map_set(&shader->uniformMap, uniform.name, shader->uniforms.length);
vec_push(&shader->uniforms, uniform);
textureSlot += uniform.type == UNIFORM_TEXTURE ? uniform.count : 0;
textureSlot += uniform.type == UNIFORM_SAMPLER ? uniform.count : 0;
imageSlot += uniform.type == UNIFORM_IMAGE ? uniform.count : 0;
}
}
@ -1772,12 +1772,24 @@ void lovrShaderBind(Shader* shader) {
}
vec_foreach_ptr(&shader->uniforms, uniform, i) {
if (uniform->type == UNIFORM_TEXTURE) {
if (uniform->type == UNIFORM_SAMPLER) {
for (int i = 0; i < uniform->count; i++) {
Texture* texture = uniform->value.textures[i];
Barrier barrier = uniform->image ? BARRIER_UNIFORM_IMAGE : BARRIER_UNIFORM_TEXTURE;
if (texture && (texture->incoherent >> barrier) & 1) {
flags |= 1 << barrier;
if (texture && texture->incoherent && (texture->incoherent >> BARRIER_UNIFORM_TEXTURE) & 1) {
flags |= 1 << BARRIER_UNIFORM_TEXTURE;
if (flags & (1 << BARRIER_UNIFORM_IMAGE)) {
break;
}
}
}
} else if (uniform->type == UNIFORM_IMAGE) {
for (int i = 0; i < uniform->count; i++) {
Texture* texture = uniform->value.images[i].texture;
if (texture && texture->incoherent && (texture->incoherent >> BARRIER_UNIFORM_IMAGE) & 1) {
flags |= 1 << BARRIER_UNIFORM_IMAGE;
if (flags & (1 << BARRIER_UNIFORM_TEXTURE)) {
break;
}
}
}
}
@ -1787,7 +1799,7 @@ void lovrShaderBind(Shader* shader) {
// Bind uniforms
vec_foreach_ptr(&shader->uniforms, uniform, i) {
if (uniform->type != UNIFORM_TEXTURE && !uniform->dirty) {
if (uniform->type != UNIFORM_SAMPLER && uniform->type != UNIFORM_IMAGE && !uniform->dirty) {
continue;
}
@ -1822,25 +1834,29 @@ void lovrShaderBind(Shader* shader) {
}
break;
case UNIFORM_TEXTURE:
case UNIFORM_IMAGE:
for (int i = 0; i < count; i++) {
Image* image = &uniform->value.images[i];
Texture* texture = image->texture;
lovrAssert(!texture || texture->type == uniform->textureType, "Uniform texture type mismatch for uniform %s", uniform->name);
// If the Shader can write to the texture, mark it as incoherent
if (texture && image->access != ACCESS_READ) {
texture->incoherent |= 1 << BARRIER_UNIFORM_TEXTURE;
texture->incoherent |= 1 << BARRIER_UNIFORM_IMAGE;
texture->incoherent |= 1 << BARRIER_TEXTURE;
texture->incoherent |= 1 << BARRIER_CANVAS;
}
lovrGpuBindImage(image, uniform->baseSlot + i);
}
break;
case UNIFORM_SAMPLER:
for (int i = 0; i < count; i++) {
Texture* texture = uniform->value.textures[i];
lovrAssert(!texture || texture->type == uniform->textureType, "Uniform texture type mismatch");
if (uniform->image) {
// If the Shader can write to the texture, mark it as incoherent
if (texture && uniform->access != ACCESS_READ) {
texture->incoherent |= 1 << BARRIER_UNIFORM_TEXTURE;
texture->incoherent |= 1 << BARRIER_UNIFORM_IMAGE;
texture->incoherent |= 1 << BARRIER_TEXTURE;
texture->incoherent |= 1 << BARRIER_CANVAS;
}
lovrGpuBindImage(texture, uniform->baseTextureSlot + i, uniform->access);
} else {
lovrGpuBindTexture(texture, uniform->baseTextureSlot + i);
}
lovrAssert(!texture || texture->type == uniform->textureType, "Uniform texture type mismatch for uniform %s", uniform->name);
lovrGpuBindTexture(texture, uniform->baseSlot + i);
}
break;
}
@ -1855,9 +1871,9 @@ void lovrShaderBind(Shader* shader) {
bool writable = type == BLOCK_STORAGE && block->access != ACCESS_READ;
block->source->incoherent |= writable ? (1 << BARRIER_BLOCK) : 0;
lovrShaderBlockUnmap(block->source);
lovrGpuBindBlockBuffer(type, block->source->buffer, block->binding);
lovrGpuBindBlockBuffer(type, block->source->buffer, block->slot);
} else {
lovrGpuBindBlockBuffer(type, 0, block->binding);
lovrGpuBindBlockBuffer(type, 0, block->slot);
}
}
}
@ -1881,7 +1897,7 @@ const Uniform* lovrShaderGetUniform(Shader* shader, const char* name) {
return &shader->uniforms.data[*index];
}
static void lovrShaderSetUniform(Shader* shader, const char* name, UniformType type, void* data, int count, int size, const char* debug) {
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;
@ -1890,40 +1906,35 @@ static void lovrShaderSetUniform(Shader* shader, const char* name, UniformType t
Uniform* uniform = &shader->uniforms.data[*index];
const char* plural = (uniform->size / size) > 1 ? "s" : "";
lovrAssert(uniform->type == type, "Unable to send %ss to uniform %s", debug, name);
lovrAssert(count * size <= uniform->size, "Expected at most %d %s%s for uniform %s, got %d", uniform->size / size, debug, plural, name, count);
lovrAssert((start + count) * size <= uniform->size, "Too many %s%s for uniform %s, maximum is %d", debug, plural, name, uniform->size / size);
if (!uniform->dirty && !memcmp(uniform->value.data, data, count * size)) {
void* dest = uniform->value.bytes + start * size;
if (!uniform->dirty && !memcmp(dest, data, count * size)) {
return;
}
memcpy(uniform->value.data, data, count * size);
memcpy(dest, data, count * size);
uniform->dirty = true;
}
void lovrShaderSetFloat(Shader* shader, const char* name, float* data, int count) {
lovrShaderSetUniform(shader, name, UNIFORM_FLOAT, data, count, sizeof(float), "float");
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 lovrShaderSetInt(Shader* shader, const char* name, int* data, int count) {
lovrShaderSetUniform(shader, name, UNIFORM_INT, data, count, sizeof(int), "int");
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 lovrShaderSetMatrix(Shader* shader, const char* name, float* data, int count) {
lovrShaderSetUniform(shader, name, UNIFORM_MATRIX, data, count, sizeof(float), "float");
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 lovrShaderSetTexture(Shader* shader, const char* name, Texture** data, int count) {
lovrShaderSetUniform(shader, name, UNIFORM_TEXTURE, data, count, sizeof(Texture*), "texture");
void lovrShaderSetTextures(Shader* shader, const char* name, Texture** data, int start, int count) {
lovrShaderSetUniform(shader, name, UNIFORM_SAMPLER, data, start, count, sizeof(Texture*), "texture");
}
ShaderBlock* lovrShaderGetBlock(Shader* shader, const char* name) {
int* id = map_get(&shader->blockMap, name);
lovrAssert(id, "No shader block named '%s'", name);
int type = *id & 1;
int index = *id >> 1;
UniformBlock* block = &shader->blocks[type].data[index];
return block->source;
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 lovrShaderSetBlock(Shader* shader, const char* name, ShaderBlock* source, UniformAccess access) {

View File

@ -29,7 +29,8 @@ typedef enum {
UNIFORM_FLOAT,
UNIFORM_MATRIX,
UNIFORM_INT,
UNIFORM_TEXTURE
UNIFORM_SAMPLER,
UNIFORM_IMAGE
} UniformType;
typedef enum {
@ -46,6 +47,13 @@ typedef enum {
MAX_DEFAULT_SHADERS
} DefaultShader;
typedef struct {
Texture* texture;
int slice;
int mipmap;
UniformAccess access;
} Image;
typedef struct {
char name[LOVR_MAX_UNIFORM_LENGTH];
UniformType type;
@ -56,13 +64,14 @@ typedef struct {
int size;
union {
void* data;
char* bytes;
int* ints;
float* floats;
Texture** textures;
Image* images;
} value;
TextureType textureType;
UniformAccess access;
int baseTextureSlot;
int baseSlot;
bool image;
bool dirty;
} Uniform;
@ -72,6 +81,15 @@ typedef vec_t(Uniform) vec_uniform_t;
typedef struct Shader Shader;
typedef struct ShaderBlock ShaderBlock;
typedef struct {
vec_uniform_t uniforms;
int slot;
ShaderBlock* source;
UniformAccess access;
} UniformBlock;
typedef vec_t(UniformBlock) vec_block_t;
Shader* lovrShaderCreateGraphics(const char* vertexSource, const char* fragmentSource);
Shader* lovrShaderCreateCompute(const char* source);
Shader* lovrShaderCreateDefault(DefaultShader type);
@ -81,11 +99,11 @@ void lovrShaderBind(Shader* shader);
int lovrShaderGetAttributeId(Shader* shader, const char* name);
bool lovrShaderHasUniform(Shader* shader, const char* name);
const Uniform* lovrShaderGetUniform(Shader* shader, const char* name);
void lovrShaderSetFloat(Shader* shader, const char* name, float* data, int count);
void lovrShaderSetInt(Shader* shader, const char* name, int* data, int count);
void lovrShaderSetMatrix(Shader* shader, const char* name, float* data, int count);
void lovrShaderSetTexture(Shader* shader, const char* name, Texture** data, int count);
ShaderBlock* lovrShaderGetBlock(Shader* shader, const char* name);
void lovrShaderSetFloats(Shader* shader, const char* name, float* data, int start, int count);
void lovrShaderSetInts(Shader* shader, const char* name, int* data, int start, int count);
void lovrShaderSetMatrices(Shader* shader, const char* name, float* data, int start, int count);
void lovrShaderSetTextures(Shader* shader, const char* name, Texture** data, int start, int count);
void lovrShaderSetImages(Shader* shader, const char* name, Image* data, int start, int count);
void lovrShaderSetBlock(Shader* shader, const char* name, ShaderBlock* block, UniformAccess access);
ShaderBlock* lovrShaderBlockCreate(vec_uniform_t* uniforms, BlockType type, BufferUsage usage);