Add texture management as sampler2D uniform to ImageFilter and related components

This commit is contained in:
brunoherbelin
2025-11-08 14:06:09 +01:00
parent 9b432f3fc9
commit bdc313cd1f
6 changed files with 231 additions and 7 deletions

View File

@@ -1293,6 +1293,39 @@ void ImGuiVisitor::visit (AlphaFilter& f)
}
void list_textures_(ImageFilter &f, std::ostringstream &oss)
{
std::map<std::string, uint64_t > filter_textures = f.program().textures();
for (auto tex = filter_textures.rbegin(); tex != filter_textures.rend(); ++tex)
{
ImGui::PushID( tex->first.c_str() );
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
Source *source = Mixer::manager().findSource( tex->second );
std::string label = source == nullptr ? "Select source" : ""
+ std::string( source->initials() )
+ " - "
+ source->name();
if (ImGui::BeginCombo(tex->first.c_str(), label.c_str()) )
{
SourceList::iterator iter;
for (iter = Mixer::manager().session()->begin(); iter != Mixer::manager().session()->end(); ++iter)
{
label = std::string((*iter)->initials()) + " - " + (*iter)->name();
if (ImGui::Selectable( label.c_str())) {
f.setProgramTexture(tex->first, (*iter)->id() );
oss << " Texture " << tex->first << " " << label;
Action::manager().store(oss.str());
}
}
ImGui::EndCombo();
}
ImGui::PopID();
}
}
void ImGuiVisitor::visit (ImageFilter& f)
{
// Open Editor
@@ -1301,9 +1334,10 @@ void ImGuiVisitor::visit (ImageFilter& f)
ImGui::SameLine(0, IMGUI_SAME_LINE);
ImGui::Text("Code");
// List of parameters
// List of parameters & textures
oss << "Custom ";
list_parameters_(f, oss);
list_textures_(f, oss);
}

View File

@@ -20,6 +20,7 @@
#include <algorithm>
#include <regex>
#include <glad/glad.h>
#include <glm/gtc/matrix_access.hpp>
#include <glm/gtc/matrix_transform.hpp>
@@ -30,6 +31,7 @@
#include "Resource.h"
#include "SystemToolkit.h"
#include "Visitor.h"
#include "Source.h"
#include "Mixer.h"
@@ -120,14 +122,17 @@ FilteringProgram::FilteringProgram() : name_("Default"), filename_(""),
}
FilteringProgram::FilteringProgram(const std::string &name, const std::string &first_pass, const std::string &second_pass,
const std::map<std::string, float> &parameters, const std::string &filename) :
name_(name), filename_(filename), code_({first_pass, second_pass}), parameters_(parameters)
const std::map<std::string, float> &parameters, const std::string &filename,
const std::map<std::string, uint64_t> &textures ) :
name_(name), filename_(filename), code_({first_pass, second_pass}), parameters_(parameters), textures_(textures)
{
two_pass_filter_ = !second_pass.empty();
}
FilteringProgram::FilteringProgram(const FilteringProgram &other) :
name_(other.name_), filename_(other.filename_), code_(other.code_), two_pass_filter_(other.two_pass_filter_), parameters_(other.parameters_)
name_(other.name_), filename_(other.filename_), code_(other.code_),
two_pass_filter_(other.two_pass_filter_), parameters_(other.parameters_),
textures_(other.textures_)
{
}
@@ -140,6 +145,8 @@ FilteringProgram& FilteringProgram::operator= (const FilteringProgram& other)
this->code_ = other.code_;
this->parameters_.clear();
this->parameters_ = other.parameters_;
this->textures_.clear();
this->textures_ = other.textures_;
this->two_pass_filter_ = other.two_pass_filter_;
}
@@ -178,6 +185,17 @@ void FilteringProgram::removeParameter(const std::string &p)
parameters_.erase(p);
}
bool FilteringProgram::hasTexture(const std::string &t)
{
return textures_.find(t) != textures_.end();
}
void FilteringProgram::removeTexture(const std::string &t)
{
if (hasTexture(t))
textures_.erase(t);
}
////////////////////////////////////////
///// //
@@ -251,11 +269,26 @@ void ImageFilteringShader::use()
// uniform variable could be set, keep it
++u;
else {
// uniform variable does not exist in code, remove it
// uniform variable is not used in code, remove it
u = uniforms_.erase(u);
uniforms_changed_ = true;
}
}
// loop over sampler2D uniforms for channels (start at 2)
int sampler_index = 2;
for (auto u = sampler2D_.begin(); u != sampler2D_.end(); ) {
// set uniform sampler2D to current sampler_index
if ( program_->setUniform(u->first, sampler_index++) )
// uniform variable could be set, keep it
++u;
else {
// uniform variable is not used in code, remove it
u = sampler2D_.erase(u);
uniforms_changed_ = true;
}
}
}
void ImageFilteringShader::reset()
@@ -356,6 +389,15 @@ void ImageFilter::update (float dt)
if (shaders_.first->uniforms_.count(param->first) < 1)
program_.removeParameter(param->first);
}
// loop over the textures of the program...
std::map<std::string, uint64_t > __T = program_.textures();
for (auto tex = __T.begin(); tex != __T.end(); ++tex) {
// .. and remove the textures that are not valid sampler2D
if (shaders_.first->sampler2D_.count(tex->first) < 1)
program_.removeTexture(tex->first);
}
// done
shaders_.first->uniforms_changed_ = false;
}
@@ -408,6 +450,8 @@ void ImageFilter::draw (FrameBuffer *input)
if (buffers_.second != nullptr)
delete buffers_.second;
buffers_.second = new FrameBuffer( buffers_.first->resolution(), buffers_.first->flags() );
// force update
updateParameters();
// forced draw
forced = true;
}
@@ -417,6 +461,18 @@ void ImageFilter::draw (FrameBuffer *input)
// FIRST PASS
if (channel1_output_session)
shaders_.first->secondary_texture = Mixer::manager().session()->frame()->texture();
// loop over sampler2D uniforms to bind textures
uint texture_unit = 0;
for (auto u = shaders_.first->sampler2D_.begin(); u != shaders_.first->sampler2D_.end(); ++u) {
// setup mask texture
glActiveTexture(GL_TEXTURE2 + texture_unit++);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindTexture (GL_TEXTURE_2D, u->second);
glActiveTexture(GL_TEXTURE0);
}
// render input surface into frame buffer
buffers_.first->begin();
surfaces_.first->draw(glm::identity<glm::mat4>(), buffers_.first->projection());
@@ -449,6 +505,7 @@ FilteringProgram ImageFilter::program () const
#define REGEX_UNIFORM_DECLARATION "uniform\\s+float\\s+"
#define REGEX_VARIABLE_NAME "[a-zA-Z_][\\w]+"
#define REGEX_UNIFORM_VALUE "(\\s*=\\s*[[:digit:]]+(\\.[[:digit:]]*)?)?\\s*\\;"
#define REGEX_SAMPLER_DECLARATION "uniform\\s+sampler2D\\s+"
void ImageFilter::setProgram(const FilteringProgram &f, std::promise<std::string> *ret)
{
@@ -512,11 +569,35 @@ void ImageFilter::setProgram(const FilteringProgram &f, std::promise<std::string
else
program_.setParameter(varname, 0.f);
}
else
program_.setParameter(varname, 0.f);
}
// keep parsing
glslcode = found_uniform.suffix().str();
}
// Parse code to detect additional declaration of uniform sampler2D
// Search for "uniform sampler2D" and a variable name
glslcode = codes.first;
std::smatch found_sampler2D;
std::regex is_a_sampler2D(REGEX_SAMPLER_DECLARATION REGEX_VARIABLE_NAME);
// loop over every uniform declarations in the GLSL code
while (std::regex_search(glslcode, found_sampler2D, is_a_sampler2D)) {
// found a complete declaration of uniform sampler2D
std::string declaration = found_sampler2D.str();
// extract variable name by erasing everything else
std::string varname =
std::regex_replace(declaration,std::regex(REGEX_SAMPLER_DECLARATION),"");
if (!varname.empty()) {
// add new detected texture with unknown source
if ( !program_.hasTexture(varname) ){
program_.setTexture(varname, 0);
}
}
// keep parsing
glslcode = found_sampler2D.suffix().str();
}
// SECOND PASS
if ( program_.isTwoPass() )
// set the code to the shader for second-pass
@@ -531,6 +612,35 @@ void ImageFilter::updateParameters()
// change uniforms
shaders_.first->uniforms_ = program_.parameters();
// change textures into sampler2D
auto texturelist = program_.textures();
if ( !texturelist.empty() ) {
auto copy_sampler2D = shaders_.first->sampler2D_;
for (auto T = texturelist.begin(); T != texturelist.end(); ++T) {
// get texture id from source
uint texture_id = Resource::getTextureBlack();
Source *s = Mixer::manager().findSource(T->second);
if ( s != nullptr )
texture_id = s->texture();
// set or insert a texture id into sampler2D list
shaders_.first->sampler2D_[T->first] = texture_id;
// remove from copy list
if ( copy_sampler2D.find(T->first) != copy_sampler2D.end() ) {
copy_sampler2D.erase(T->first);
}
}
// remove textures that are not used anymore
for (auto S = copy_sampler2D.begin(); S != copy_sampler2D.end(); ++S) {
shaders_.first->sampler2D_.erase(S->first);
}
}
else {
// no texture, clear sampler2D list
shaders_.first->sampler2D_.clear();
}
if ( program_.isTwoPass() )
shaders_.second->uniforms_ = program_.parameters();
}
@@ -555,6 +665,26 @@ void ImageFilter::setProgramParameter(const std::string &p, float value)
updateParameters();
}
void ImageFilter::setProgramTextures(const std::map< std::string, uint64_t > &textures)
{
for (const auto &p : textures) {
if (p.first.empty())
return;
}
program_.setTextures(textures);
updateParameters();
}
void ImageFilter::setProgramTexture(const std::string &p, uint64_t id)
{
if (p.empty())
return;
program_.setTexture(p, id);
updateParameters();
}
////////////////////////////////////////
///// //
//// RESAMPLING FILTERS ///

View File

@@ -27,11 +27,15 @@ class FilteringProgram
// list of parameters : uniforms names and values
std::map< std::string, float > parameters_;
// list of texture inputs : uniform sampler2D names and source id
std::map< std::string, uint64_t > textures_;
public:
FilteringProgram();
FilteringProgram(const std::string &name, const std::string &first_pass, const std::string &second_pass,
const std::map<std::string, float> &parameters, const std::string &filename = std::string());
const std::map<std::string, float> &parameters, const std::string &filename = std::string(),
const std::map<std::string, uint64_t> &textures = std::map<std::string, uint64_t>() );
FilteringProgram(const FilteringProgram &other);
FilteringProgram& operator= (const FilteringProgram& other);
@@ -67,6 +71,18 @@ public:
bool hasParameter(const std::string &p);
void removeParameter(const std::string &p);
// set the list of textures
inline void setTextures(const std::map< std::string, uint64_t > &textures) { textures_ = textures; }
// get the list of textures
inline std::map< std::string, uint64_t > textures() const { return textures_; }
// get / set texture
inline void clearTextures() { textures_.clear(); }
inline void setTexture(const std::string &t, uint64_t id) { textures_[t] = id; }
bool hasTexture(const std::string &t);
void removeTexture(const std::string &t);
// globals
static std::string getFilterCodeInputs();
static std::string getFilterCodeDefault();
@@ -95,6 +111,8 @@ public:
// list of uniforms to control shader
std::map< std::string, float > uniforms_;
// list of uniforms to control shader textures id
std::map< std::string, uint > sampler2D_;
bool uniforms_changed_;
ImageFilteringShader();
@@ -131,6 +149,10 @@ public:
void setProgramParameters(const std::map< std::string, float > &parameters);
void setProgramParameter(const std::string &p, float value);
// update textures of program
void setProgramTextures(const std::map< std::string, uint64_t > &textures);
void setProgramTexture(const std::string &t, uint64_t id);
// implementation of FrameBufferFilter
Type type() const override { return FrameBufferFilter::FILTER_IMAGE; }
uint texture () const override;

View File

@@ -1555,6 +1555,23 @@ void SessionLoader::visit (AlphaFilter& f)
f.setProgramParameters( get_parameters_(xmlCurrent_->FirstChildElement("parameters")) );
}
std::map< std::string, uint64_t > get_textures_(XMLElement* textures)
{
std::map< std::string, uint64_t > filter_params;
if (textures) {
XMLElement* param = textures->FirstChildElement("sampler2D");
for( ; param ; param = param->NextSiblingElement())
{
uint64_t val = 0.f;
param->QueryUnsigned64Attribute("id", &val);
const char * name;
if ( param->QueryStringAttribute("name", &name) == XML_SUCCESS && name != NULL)
filter_params[name] = val;
}
}
return filter_params;
}
void SessionLoader::visit (ImageFilter& f)
{
std::pair< std::string, std::string > filter_codes;
@@ -1586,9 +1603,12 @@ void SessionLoader::visit (ImageFilter& f)
// image filter parameters
std::map< std::string, float > filter_params = get_parameters_(xmlCurrent_->FirstChildElement("parameters"));
// image filter textures
std::map< std::string, uint64_t > filter_textures = get_textures_(xmlCurrent_->FirstChildElement("textures"));
// set image filter program and parameters
f.setProgram( FilteringProgram(filter_name, filter_codes.first, filter_codes.second,
filter_params, filter_filename) );
filter_params, filter_filename, filter_textures) );
// set global iMouse
XMLElement* imouse = xmlCurrent_->FirstChildElement("iMouse");

View File

@@ -810,6 +810,20 @@ void SessionVisitor::visit (AlphaFilter& f)
xmlCurrent_->InsertEndChild( list_parameters_(xmlDoc_, f.program().parameters()) );
}
XMLElement *list_textures_(tinyxml2::XMLDocument *doc, std::map< std::string, uint64_t > filter_params)
{
XMLElement *parameters = doc->NewElement( "textures" );
for (auto p = filter_params.begin(); p != filter_params.end(); ++p)
{
XMLElement *param = doc->NewElement( "sampler2D" );
param->SetAttribute("name", p->first.c_str() );
param->SetAttribute("id", p->second );
parameters->InsertEndChild(param);
}
return parameters;
}
void SessionVisitor::visit (ImageFilter& f)
{
xmlCurrent_->SetAttribute("name", f.program().name().c_str() );
@@ -834,6 +848,9 @@ void SessionVisitor::visit (ImageFilter& f)
// image filter parameters
xmlCurrent_->InsertEndChild( list_parameters_(xmlDoc_, f.program().parameters()) );
// image filter texture inputs
xmlCurrent_->InsertEndChild( list_textures_(xmlDoc_, f.program().textures()) );
// global iMouse
XMLElement *imouse = xmlDoc_->NewElement("iMouse");
imouse->InsertEndChild( XMLElementFromGLM(xmlDoc_, FilteringProgram::iMouse) );

View File

@@ -167,6 +167,7 @@ void ShaderEditWindow::BuildShader()
// set parameters
filters_[current_].setParameters(current_->program().parameters());
filters_[current_].setTextures(current_->program().textures());
// change the filter of the current image filter
// => this triggers compilation of shader