vulkan: uniforms in custom shaders

things like `uniform float time;` or `uniform sampler2D tex` now work
This commit is contained in:
niki
2022-08-14 22:15:24 +02:00
parent 84df8fee03
commit bc0b5f81f5
2 changed files with 93 additions and 119 deletions
+89 -115
View File
@@ -322,6 +322,20 @@ int Shader::getVertexAttributeIndex(const std::string& name) {
return 0; return 0;
} }
const Shader::UniformInfo* Shader::getUniformInfo(const std::string& name) const {
return &uniformInfos.at(name);
}
const Shader::UniformInfo* Shader::getUniformInfo(BuiltinUniform builtin) const {
return builtinUniformInfo[builtin];
}
void Shader::sendTextures(const UniformInfo* info, graphics::Texture** textures, int count) {
for (unsigned i = 0; i < count; i++) {
info->textures[i] = textures[i];
}
}
void Shader::calculateUniformBufferSizeAligned() { void Shader::calculateUniformBufferSizeAligned() {
gfx = Module::getInstance<Graphics>(Module::ModuleType::M_GRAPHICS); gfx = Module::getInstance<Graphics>(Module::ModuleType::M_GRAPHICS);
auto vgfx = (Graphics*)gfx; auto vgfx = (Graphics*)gfx;
@@ -341,7 +355,7 @@ void Shader::buildLocalUniforms(spirv_cross::Compiler& comp, const spirv_cross::
const auto& membertypes = type.member_types; const auto& membertypes = type.member_types;
for (size_t uindex = 0; uindex < membertypes.size(); uindex) { for (size_t uindex = 0; uindex < membertypes.size(); uindex++) {
const auto& memberType = comp.get_type(membertypes[uindex]); const auto& memberType = comp.get_type(membertypes[uindex]);
size_t memberSize = comp.get_declared_struct_member_size(type, uindex); size_t memberSize = comp.get_declared_struct_member_size(type, uindex);
size_t offset = baseoff + comp.type_struct_member_offset(type, uindex); size_t offset = baseoff + comp.type_struct_member_offset(type, uindex);
@@ -361,7 +375,7 @@ void Shader::buildLocalUniforms(spirv_cross::Compiler& comp, const spirv_cross::
continue; continue;
} }
UniformInfo u = {}; UniformInfo u{};
u.name = name; u.name = name;
u.dataSize = memberSize; u.dataSize = memberSize;
u.count = memberType.array.empty() ? 1 : memberType.array[0]; u.count = memberType.array.empty() ? 1 : memberType.array[0];
@@ -397,8 +411,6 @@ void Shader::buildLocalUniforms(spirv_cross::Compiler& comp, const spirv_cross::
} }
builtinUniformInfo[builtin] = &uniformInfos[u.name]; builtinUniformInfo[builtin] = &uniformInfos[u.name];
} }
// update uniform.
} }
} }
@@ -509,129 +521,87 @@ void Shader::compileShaders() {
auto shaderResources = comp.get_shader_resources(active); auto shaderResources = comp.get_shader_resources(active);
comp.set_enabled_interface_variables(std::move(active)); comp.set_enabled_interface_variables(std::move(active));
std::string builtinUniformName = "love_UniformsPerDraw";
for (const auto& resource : shaderResources.uniform_buffers) { for (const auto& resource : shaderResources.uniform_buffers) {
size_t uniformBufferObjectSize = comp.get_declared_struct_size(comp.get_type(resource.base_type_id)); if (resource.name == "gl_DefaultUniformBlock") {
const auto& type = comp.get_type(resource.base_type_id);
size_t uniformBufferObjectSize = comp.get_declared_struct_size(type);
auto defaultUniformBlockSize = comp.get_declared_struct_size(type);
localUniformStagingData.resize(defaultUniformBlockSize);
const auto& resourceType = comp.get_type(resource.type_id); memset(localUniformStagingData.data(), 0, defaultUniformBlockSize);
unsigned memberCount = resourceType.member_types.size();
for (unsigned i = 0; i < memberCount; i++) {
auto& type = comp.get_type(resourceType.member_types[i]);
auto baseType = type.basetype;
const std::string& name = comp.get_member_name(resourceType.self, i);
if (name == "gl_DefaultUniformBlock") { std::string basename("");
auto defaultUniformBlockSize = comp.get_declared_struct_size(type); buildLocalUniforms(comp, type, 0, basename);
localUniformStagingData.resize(defaultUniformBlockSize); }
else {
throw love::Exception("unimplemented: non default uniform blocks.");
}
}
std::string basename(""); for (const auto& r : shaderResources.sampled_images) {
buildLocalUniforms(comp, type, 0, basename); const SPIRType& basetype = comp.get_type(r.base_type_id);
} const SPIRType& type = comp.get_type(r.type_id);
else if (name == builtinUniformName) { const SPIRType& imagetype = comp.get_type(basetype.image.type);
UniformInfo u{};
u.name = name;
u.dataSize = sizeof(BuiltinUniformData);
localUniformStagingData.resize(u.dataSize); graphics::Shader::UniformInfo info;
builtinUniformDataOffset = 0; info.location = comp.get_decoration(r.id, spv::DecorationBinding);
info.baseType = UNIFORM_SAMPLER;
info.name = r.name;
info.count = type.array.empty() ? 1 : type.array[0];
info.isDepthSampler = type.image.depth;
info.components = 1;
u.count = type.array.empty() ? 1 : type.array[0]; switch (imagetype.basetype) {
u.components = 1; case SPIRType::Float:
u.data = localUniformStagingData.data(); info.dataBaseType = DATA_BASETYPE_FLOAT;
break;
if (type.columns == 1) { case SPIRType::Int:
if (type.basetype == SPIRType::Int) { info.dataBaseType = DATA_BASETYPE_INT;
u.baseType = UNIFORM_INT; break;
} case SPIRType::UInt:
else if (type.basetype == SPIRType::UInt) { info.dataBaseType = DATA_BASETYPE_UINT;
u.baseType = UNIFORM_UINT; break;
} default:
else { break;
u.baseType = UNIFORM_FLOAT;
}
u.components = type.vecsize;
}
else {
u.baseType = UNIFORM_MATRIX;
u.matrix.rows = type.vecsize;
u.matrix.columns = type.columns;
}
uniformInfos[u.name] = u;
builtinUniformInfo[BUILTIN_UNIFORMS_PER_DRAW] = &uniformInfos[u.name];
}
else {
throw love::Exception("unimplemented: non default uniform blocks.");
}
} }
for (const auto& r : shaderResources.sampled_images) { switch (basetype.image.dim) {
const SPIRType& basetype = comp.get_type(r.base_type_id); case spv::Dim2D:
const SPIRType& type = comp.get_type(r.type_id); info.textureType = basetype.image.arrayed ? TEXTURE_2D_ARRAY : TEXTURE_2D;
const SPIRType& imagetype = comp.get_type(basetype.image.type); info.textures = new love::graphics::Texture * [info.count];
break;
graphics::Shader::UniformInfo info; case spv::Dim3D:
info.location = comp.get_decoration(r.id, spv::DecorationBinding); info.textureType = TEXTURE_VOLUME;
info.baseType = UNIFORM_SAMPLER; info.textures = new love::graphics::Texture * [info.count];
info.name = r.name; break;
info.count = type.array.empty() ? 1 : type.array[0]; case spv::DimCube:
info.isDepthSampler = type.image.depth; if (basetype.image.arrayed) {
info.components = 1; throw love::Exception("cubemap arrays are not currently supported");
switch (imagetype.basetype) {
case SPIRType::Float:
info.dataBaseType = DATA_BASETYPE_FLOAT;
break;
case SPIRType::Int:
info.dataBaseType = DATA_BASETYPE_INT;
break;
case SPIRType::UInt:
info.dataBaseType = DATA_BASETYPE_UINT;
break;
default:
break;
} }
info.textureType = TEXTURE_CUBE;
info.textures = new love::graphics::Texture * [info.count];
break;
case spv::DimBuffer:
throw love::Exception("dim buffers not implemented yet");
default:
throw love::Exception("unknown dim");
}
switch (basetype.image.dim) { if (info.baseType == UNIFORM_SAMPLER) {
case spv::Dim2D: auto tex = vgfx->getDefaultTexture();
info.textureType = basetype.image.arrayed ? TEXTURE_2D_ARRAY : TEXTURE_2D; for (int i = 0; i < info.count; i++) {
info.textures = new love::graphics::Texture * [info.count]; info.textures[i] = tex;
break;
case spv::Dim3D:
info.textureType = TEXTURE_VOLUME;
info.textures = new love::graphics::Texture * [info.count];
break;
case spv::DimCube:
if (basetype.image.arrayed) {
throw love::Exception("cubemap arrays are not currently supported");
}
info.textureType = TEXTURE_CUBE;
info.textures = new love::graphics::Texture * [info.count];
break;
case spv::DimBuffer:
throw love::Exception("dim buffers not implemented yet");
default:
throw love::Exception("unknown dim");
} }
}
// fixme
else if (info.baseType == UNIFORM_TEXELBUFFER) {
throw love::Exception("texel buffers not supported yet");
}
if (info.baseType == UNIFORM_SAMPLER) { uniformInfos[r.name] = info;
auto tex = vgfx->getDefaultTexture(); BuiltinUniform builtin;
for (int i = 0; i < info.count; i++) { if (getConstant(r.name.c_str(), builtin)) {
info.textures[i] = tex; builtinUniformInfo[builtin] = &uniformInfos[info.name];
}
}
// fixme
else if (info.baseType == UNIFORM_TEXELBUFFER) {
throw love::Exception("texel buffers not supported yet");
}
uniformInfos[r.name] = info;
BuiltinUniform builtin;
if (getConstant(r.name.c_str(), builtin)) {
builtinUniformInfo[builtin] = &uniformInfos[info.name];
}
} }
} }
} }
@@ -704,6 +674,10 @@ void Shader::setVideoTextures(graphics::Texture* ytexture, graphics::Texture* cb
} }
} }
bool Shader::hasUniform(const std::string& name) const {
return uniformInfos.find(name) != uniformInfos.end();
}
void Shader::setUniformData(BuiltinUniformData& data) { void Shader::setUniformData(BuiltinUniformData& data) {
char* ptr = (char*) builtinUniformInfo[BUILTIN_UNIFORMS_PER_DRAW]->data + builtinUniformDataOffset; char* ptr = (char*) builtinUniformInfo[BUILTIN_UNIFORMS_PER_DRAW]->data + builtinUniformDataOffset;
memcpy(ptr, &data, sizeof(BuiltinUniformData)); memcpy(ptr, &data, sizeof(BuiltinUniformData));
+4 -4
View File
@@ -39,18 +39,18 @@ public:
int getVertexAttributeIndex(const std::string& name) override; int getVertexAttributeIndex(const std::string& name) override;
const UniformInfo* getUniformInfo(const std::string& name) const override { return nullptr; } const UniformInfo* getUniformInfo(const std::string& name) const override;
const UniformInfo* getUniformInfo(BuiltinUniform builtin) const override { return nullptr; } const UniformInfo* getUniformInfo(BuiltinUniform builtin) const override;
// Not needed right now, since the logic that links the values of the uniforms to the shader is done in cmdPushDescriptorSets // Not needed right now, since the logic that links the values of the uniforms to the shader is done in cmdPushDescriptorSets
// which gets called from the vulkan::Graphics class whenever a draw call happens. // which gets called from the vulkan::Graphics class whenever a draw call happens.
// I'll have to reevaluate the use of this function in the future though. // I'll have to reevaluate the use of this function in the future though.
void updateUniform(const UniformInfo* info, int count) override {} void updateUniform(const UniformInfo* info, int count) override {}
void sendTextures(const UniformInfo* info, graphics::Texture** textures, int count) override {} void sendTextures(const UniformInfo* info, graphics::Texture** textures, int count) override;
void sendBuffers(const UniformInfo* info, love::graphics::Buffer** buffers, int count) override {} void sendBuffers(const UniformInfo* info, love::graphics::Buffer** buffers, int count) override {}
bool hasUniform(const std::string& name) const override { return false; } bool hasUniform(const std::string& name) const override;
void setVideoTextures(graphics::Texture* ytexture, graphics::Texture* cbtexture, graphics::Texture* crtexture) override; void setVideoTextures(graphics::Texture* ytexture, graphics::Texture* cbtexture, graphics::Texture* crtexture) override;