Shader refactoring.

Move Milkdrop-specific shader code into MilkdropPreset, only keep generic and potentially reusable stuff in Renderer.

Also adding some abstraction to reduce the number of direct OpenGL calls to make it easier to add other rendering APIs in the future.
This commit is contained in:
Kai Blaschke
2023-02-16 23:41:35 +01:00
parent b9ef6a3974
commit 3a0dcffcee
15 changed files with 641 additions and 510 deletions

View File

@ -75,20 +75,20 @@ void CustomShape::Initialize(FileParser& parsedFile, int index)
m_thickOutline = parsedFile.GetInt(shapecodePrefix + "thickOutline", m_thickOutline);
m_textured = parsedFile.GetInt(shapecodePrefix + "textured", m_textured);
m_instances = parsedFile.GetInt(shapecodePrefix + "num_inst", m_instances);
m_x = parsedFile.GetBool(shapecodePrefix + "x", m_x);
m_y = parsedFile.GetBool(shapecodePrefix + "y", m_y);
m_radius = parsedFile.GetBool(shapecodePrefix + "rad", m_radius);
m_angle = parsedFile.GetBool(shapecodePrefix + "ang", m_angle);
m_tex_ang = parsedFile.GetBool(shapecodePrefix + "tex_ang", m_tex_ang);
m_tex_zoom = parsedFile.GetBool(shapecodePrefix + "tex_zoom", m_tex_zoom);
m_x = parsedFile.GetFloat(shapecodePrefix + "x", m_x);
m_y = parsedFile.GetFloat(shapecodePrefix + "y", m_y);
m_radius = parsedFile.GetFloat(shapecodePrefix + "rad", m_radius);
m_angle = parsedFile.GetFloat(shapecodePrefix + "ang", m_angle);
m_tex_ang = parsedFile.GetFloat(shapecodePrefix + "tex_ang", m_tex_ang);
m_tex_zoom = parsedFile.GetFloat(shapecodePrefix + "tex_zoom", m_tex_zoom);
m_r = parsedFile.GetFloat(shapecodePrefix + "r", m_r);
m_g = parsedFile.GetFloat(shapecodePrefix + "g", m_g);
m_b = parsedFile.GetFloat(shapecodePrefix + "b", m_b);
m_a = parsedFile.GetFloat(shapecodePrefix + "a", m_a);
m_r2 = parsedFile.GetBool(shapecodePrefix + "r2", m_r2);
m_g2 = parsedFile.GetBool(shapecodePrefix + "g2", m_g2);
m_b2 = parsedFile.GetBool(shapecodePrefix + "b2", m_b2);
m_a2 = parsedFile.GetBool(shapecodePrefix + "a2", m_a2);
m_r2 = parsedFile.GetFloat(shapecodePrefix + "r2", m_r2);
m_g2 = parsedFile.GetFloat(shapecodePrefix + "g2", m_g2);
m_b2 = parsedFile.GetFloat(shapecodePrefix + "b2", m_b2);
m_a2 = parsedFile.GetFloat(shapecodePrefix + "a2", m_a2);
m_border_r = parsedFile.GetFloat(shapecodePrefix + "border_r", m_border_r);
m_border_g = parsedFile.GetFloat(shapecodePrefix + "border_g", m_border_g);
m_border_b = parsedFile.GetFloat(shapecodePrefix + "border_b", m_border_b);

View File

@ -23,13 +23,6 @@
*/
#pragma once
#ifdef DEBUG
/* 0 for no debugging, 1 for normal, 2 for insane */
#define MILKDROP_PRESET_DEBUG 1
#else
#define MILKDROP_PRESET_DEBUG 0
#endif
#include "CustomShape.hpp"
#include "CustomWaveform.hpp"
#include "PerFrameContext.hpp"
@ -51,14 +44,14 @@ class MilkdropPreset : public Preset
public:
/**
* @brief Load a MilkdropPreset by filename with input and output buffers specified.
* @brief LoadCode a MilkdropPreset by filename with input and output buffers specified.
* @param factory The factory class that created this preset instance.
* @param absoluteFilePath the absolute file path of a MilkdropPreset to load from the file system
*/
MilkdropPreset(MilkdropPresetFactory* factory, const std::string& absoluteFilePath);
/**
* @brief Load a MilkdropPreset from an input stream with input and output buffers specified.
* @brief LoadCode a MilkdropPreset from an input stream with input and output buffers specified.
* @param presetData an already initialized input stream to read the MilkdropPreset file from
* @param presetOutputs initialized and filled with data parsed from a MilkdropPreset
*/

View File

@ -11,225 +11,32 @@
//
//
#include "MilkdropPresetFactory.hpp"
#include "MilkdropPreset.hpp"
#include "IdlePreset.hpp"
#include "PresetFrameIO.hpp"
MilkdropPresetFactory::MilkdropPresetFactory(int meshX, int meshY)
: m_meshX(meshX)
, m_meshY(meshY)
{
/* Initializes the builtin function database */
BuiltinFuncs::init_builtin_func_db();
/* Initializes all infix operators */
Eval::init_infix_ops();
}
MilkdropPresetFactory::~MilkdropPresetFactory()
{
Eval::destroy_infix_ops();
BuiltinFuncs::destroy_builtin_func_db();
}
/* Reinitializes the engine variables to a default (conservative and sane) value */
void MilkdropPresetFactory::ResetPresetOutputs(PresetOutputs* presetOutputs)
{
if (presetOutputs == nullptr)
{
return;
}
presetOutputs->zoom = 1.0;
presetOutputs->zoomexp = 1.0;
presetOutputs->rot = 0.0;
presetOutputs->warp = 0.0;
presetOutputs->sx = 1.0;
presetOutputs->sy = 1.0;
presetOutputs->dx = 0.0;
presetOutputs->dy = 0.0;
presetOutputs->cx = 0.5;
presetOutputs->cy = 0.5;
presetOutputs->screenDecay = .98;
presetOutputs->wave.r = 1.0;
presetOutputs->wave.g = 0.2;
presetOutputs->wave.b = 0.0;
presetOutputs->wave.x = 0.5;
presetOutputs->wave.y = 0.5;
presetOutputs->wave.mystery = 0.0;
presetOutputs->border.outer_size = 0.0;
presetOutputs->border.outer_r = 0.0;
presetOutputs->border.outer_g = 0.0;
presetOutputs->border.outer_b = 0.0;
presetOutputs->border.outer_a = 0.0;
presetOutputs->border.inner_size = 0.0;
presetOutputs->border.inner_r = 0.0;
presetOutputs->border.inner_g = 0.0;
presetOutputs->border.inner_b = 0.0;
presetOutputs->border.inner_a = 0.0;
presetOutputs->mv.a = 0.0;
presetOutputs->mv.r = 0.0;
presetOutputs->mv.g = 0.0;
presetOutputs->mv.b = 0.0;
presetOutputs->mv.length = 1.0;
presetOutputs->mv.x_num = 16.0;
presetOutputs->mv.y_num = 12.0;
presetOutputs->mv.x_offset = 0.02;
presetOutputs->mv.y_offset = 0.02;
/* PER_FRAME CONSTANTS END */
presetOutputs->fRating = 0;
presetOutputs->fGammaAdj = 1.0;
presetOutputs->videoEcho.zoom = 1.0;
presetOutputs->videoEcho.a = 0;
presetOutputs->videoEcho.orientation = Normal;
presetOutputs->wave.additive = false;
presetOutputs->wave.dots = false;
presetOutputs->wave.thick = false;
presetOutputs->wave.modulateAlphaByVolume = 0;
presetOutputs->wave.maximizeColors = 0;
presetOutputs->textureWrap = 0;
presetOutputs->bDarkenCenter = 0;
presetOutputs->bRedBlueStereo = 0;
presetOutputs->bBrighten = 0;
presetOutputs->bDarken = 0;
presetOutputs->bSolarize = 0;
presetOutputs->bInvert = 0;
presetOutputs->bMotionVectorsOn = 1;
presetOutputs->wave.a = 1.0;
presetOutputs->wave.scale = 1.0;
presetOutputs->wave.smoothing = 0;
presetOutputs->wave.mystery = 0;
presetOutputs->wave.modOpacityEnd = 0;
presetOutputs->wave.modOpacityStart = 0;
presetOutputs->fWarpAnimSpeed = 0;
presetOutputs->fWarpScale = 0;
presetOutputs->fShader = 0;
/* PER_PIXEL CONSTANT END */
/* Q VARIABLES START */
for (int i = 0; i < 32; i++)
{
presetOutputs->q[i] = 0;
}
// for ( std::vector<CustomWave*>::iterator pos = presetOutputs->customWaves.begin();
// pos != presetOutputs->customWaves.end(); ++pos )
// if ( *pos != 0 ) delete ( *pos );
// for ( std::vector<CustomShape*>::iterator pos = presetOutputs->customShapes.begin();
// pos != presetOutputs->customShapes.end(); ++pos )
// if ( *pos != 0 ) delete ( *pos );
presetOutputs->customWaves.clear();
presetOutputs->customShapes.clear();
/* Q VARIABLES END */
}
/* Reinitializes the engine variables to a default (conservative and sane) value */
void MilkdropPresetFactory::reset()
{
if (m_presetOutputsCache)
{
ResetPresetOutputs(m_presetOutputsCache);
}
}
PresetOutputs* MilkdropPresetFactory::CreatePresetOutputs(int meshX, int meshY)
{
auto* presetOutputs = new PresetOutputs();
presetOutputs->Initialize(meshX, meshY);
/* PER FRAME CONSTANTS BEGIN */
presetOutputs->zoom = 1.0;
presetOutputs->zoomexp = 1.0;
presetOutputs->rot = 0.0;
presetOutputs->warp = 0.0;
presetOutputs->sx = 1.0;
presetOutputs->sy = 1.0;
presetOutputs->dx = 0.0;
presetOutputs->dy = 0.0;
presetOutputs->cx = 0.5;
presetOutputs->cy = 0.5;
presetOutputs->screenDecay = 0.98f;
/* PER_FRAME CONSTANTS END */
presetOutputs->fRating = 0.0f;
presetOutputs->fGammaAdj = 1.0f;
presetOutputs->videoEcho.zoom = 1.0f;
presetOutputs->videoEcho.a = 0.0f;
presetOutputs->videoEcho.orientation = Orientation::Normal;
presetOutputs->textureWrap = false;
presetOutputs->bDarkenCenter = false;
presetOutputs->bRedBlueStereo = false;
presetOutputs->bBrighten = false;
presetOutputs->bDarken = false;
presetOutputs->bSolarize = false;
presetOutputs->bInvert = false;
presetOutputs->bMotionVectorsOn = true;
presetOutputs->fWarpAnimSpeed = 0.0f;
presetOutputs->fWarpScale = 0.0f;
presetOutputs->fShader = 0.0f;
/* PER_PIXEL CONSTANTS BEGIN */
/* PER_PIXEL CONSTANT END */
/* Q AND T VARIABLES START */
for (unsigned int i = 0; i < numQVariables; i++)
{
presetOutputs->q[i] = 0;
}
/* Q AND T VARIABLES END */
return presetOutputs;
}
std::unique_ptr<Preset>
MilkdropPresetFactory::LoadPresetFromFile(const std::string& filename)
{
PresetOutputs* presetOutputs;
// use cached PresetOutputs if there is one, otherwise allocate
if (m_presetOutputsCache)
{
presetOutputs = m_presetOutputsCache;
m_presetOutputsCache = nullptr;
}
else
{
presetOutputs = CreatePresetOutputs(m_meshX, m_meshY);
}
ResetPresetOutputs(presetOutputs);
std::string path;
auto protocol = PresetFactory::Protocol(filename, path);
if (protocol == PresetFactory::IDLE_PRESET_PROTOCOL)
{
return IdlePresets::allocate(this, presetOutputs);
return IdlePresets::allocate(this);
}
else if (protocol == "" || protocol == "file")
{
return std::make_unique<MilkdropPreset>(this, path, presetOutputs);
return std::make_unique<MilkdropPreset>(this, path);
}
else
{
@ -240,42 +47,5 @@ MilkdropPresetFactory::LoadPresetFromFile(const std::string& filename)
std::unique_ptr<Preset> MilkdropPresetFactory::LoadPresetFromStream(std::istream& data)
{
PresetOutputs* presetOutputs;
// use cached PresetOutputs if there is one, otherwise allocate
if (m_presetOutputsCache)
{
presetOutputs = m_presetOutputsCache;
m_presetOutputsCache = nullptr;
}
else
{
presetOutputs = CreatePresetOutputs(m_meshX, m_meshY);
}
ResetPresetOutputs(presetOutputs);
return std::make_unique<MilkdropPreset>(this, data, presetOutputs);
return std::make_unique<MilkdropPreset>(this, data);
}
// this gives the preset a way to return the PresetOutput w/o dependency on class projectM behavior
void MilkdropPresetFactory::releasePreset(Preset* preset)
{
auto milkdropPreset = dynamic_cast<MilkdropPreset*>(preset);
if (!milkdropPreset || !milkdropPreset->_presetOutputs)
{
// Instance is not a MilkdropPreset or has no PresetOutput object.
return;
}
// return PresetOutputs to the cache
if (!m_presetOutputsCache)
{
m_presetOutputsCache = milkdropPreset->_presetOutputs;
}
else
{
delete milkdropPreset->_presetOutputs;
}
}

View File

@ -1 +1,164 @@
#include "MilkdropShader.hpp"
#include <algorithm>
MilkdropShader::MilkdropShader(ShaderType type)
: m_type(type)
{
}
void MilkdropShader::LoadCode(std::string presetShaderCode)
{
m_presetShaderCode = std::move(presetShaderCode);
std::string program = m_presetShaderCode;
PreprocessPresetShader(program);
}
void MilkdropShader::LoadTextures(TextureManager& textureManager)
{
// Add Milkdrop built-in textures
m_shader.SetUniformTexture("main", textureManager.getTexture("main", GL_REPEAT, GL_LINEAR));
m_shader.SetUniformTexture("main", textureManager->getTexture("main", GL_REPEAT, GL_LINEAR));
m_shader.SetUniformTexture("fc_main", textureManager->getTexture("main", GL_CLAMP_TO_EDGE, GL_LINEAR));
m_shader.SetUniformTexture("pc_main", textureManager->getTexture("main", GL_CLAMP_TO_EDGE, GL_NEAREST));
m_shader.SetUniformTexture("fw_main", textureManager->getTexture("main", GL_REPEAT, GL_LINEAR));
m_shader.SetUniformTexture("pw_main", textureManager->getTexture("main", GL_REPEAT, GL_NEAREST));
m_shader.SetUniformTexture("noise_lq", textureManager->getTexture("noise_lq", GL_REPEAT, GL_LINEAR));
m_shader.SetUniformTexture("noise_lq_lite", textureManager->getTexture("noise_lq_lite", GL_REPEAT, GL_LINEAR));
m_shader.SetUniformTexture("noise_mq", textureManager->getTexture("noise_mq", GL_REPEAT, GL_LINEAR));
m_shader.SetUniformTexture("noise_hq", textureManager->getTexture("noise_hq", GL_REPEAT, GL_LINEAR));
m_shader.SetUniformTexture("noisevol_lq", textureManager->getTexture("noisevol_lq", GL_REPEAT, GL_LINEAR));
m_shader.SetUniformTexture("noisevol_hq", textureManager->getTexture("noisevol_hq", GL_REPEAT, GL_LINEAR));
}
void MilkdropShader::PreprocessPresetShader(std::string& program)
{
if (program.length() <= 0)
{
throw ShaderException("Preset shader is declared, but empty.");
}
size_t found;
// replace shader_body with entry point function
found = program.find("shader_body");
if (found != std::string::npos)
{
#ifdef MILKDROP_PRESET_DEBUG
std::cerr << "[MilkdropShader] First 'shader_body' found at: " << int(found) << std::endl;
#endif
if (m_type == ShaderType::WarpShader)
{
program.replace(int(found), 11, "void PS(float4 _vDiffuse : COLOR, float4 _uv : TEXCOORD0, float2 _rad_ang : TEXCOORD1, out float4 _return_value : COLOR)\n");
}
else
{
program.replace(int(found), 11, "void PS(float4 _vDiffuse : COLOR, float2 _uv : TEXCOORD0, float2 _rad_ang : TEXCOORD1, out float4 _return_value : COLOR)\n");
}
}
else
{
throw ShaderException("Preset shader is missing \"shader_body\" entry point.");
}
// replace the "{" immediately following shader_body with some variable declarations
found = program.find('{', found);
if (found != std::string::npos)
{
#ifdef MILKDROP_PRESET_DEBUG
std::cerr << "[MilkdropShader] First '{' found at: " << int(found) << std::endl;
#endif
const char* progMain =
"{\n"
"float3 ret = 0;\n";
program.replace(int(found), 1, progMain);
}
else
{
throw ShaderException("Preset shader has no opening braces.");
}
// replace "}" with return statement (this can probably be optimized for the GLSL conversion...)
found = program.rfind('}');
if (found != std::string::npos)
{
#ifdef MILKDROP_PRESET_DEBUG
std::cerr << "[MilkdropShader] Last '}' found at: " << int(found) << std::endl;
#endif
program.replace(int(found), 1, "_return_value = float4(ret.xyz, 1.0);\n"
"}\n");
}
else
{
throw ShaderException("Preset shader has no closing brace.");
}
// Find matching closing brace and cut off excess text after shader's main function
int bracesOpen = 1;
size_t pos = found + 1;
for (; pos < program.length() && bracesOpen > 0; ++pos)
{
switch (program.at(pos))
{
case '/':
// Skip comments until EoL to prevent false counting
if (pos < program.length() - 1 && program.at(pos + 1) == '/')
{
for (; pos < program.length(); ++pos)
{
if (program.at(pos) == '\n')
{
break;
}
}
}
continue;
case '{':
bracesOpen++;
continue;
case '}':
bracesOpen--;
}
}
if (pos < program.length() - 1)
{
program.resize(pos);
}
}
void MilkdropShader::GetReferencedSamplers(const std::string& program)
{
// set up texture samplers for all samplers references in the shader program
auto found = program.find("sampler_", 0);
while (found != std::string::npos)
{
found += 8;
size_t end = program.find_first_of(" ;,\n\r)", found);
if (end != std::string::npos)
{
std::string sampler = program.substr((int) found, (int) end - found);
std::string lowerCaseName(sampler);
std::transform(lowerCaseName.begin(), lowerCaseName.end(), lowerCaseName.begin(), tolower);
m_samplerNames.push_back(lowerCaseName);
}
found = program.find("sampler_", found);
}
found = program.find("GetBlur3");
}

View File

@ -4,6 +4,9 @@
*/
#pragma once
#include "Renderer/Shader.hpp"
#include "Renderer/TextureManager.hpp"
/**
* @brief Holds a warp or composite shader of Milkdrop presets.
* Also does the required shader translation from HLSL to GLSL using hlslparser.
@ -11,7 +14,57 @@
class MilkdropShader
{
public:
enum class ShaderType
{
WarpShader, //!< Warp shader
CompositeShader //!< Composite shader
};
/**
* Maximum main texture blur level used in the shader
*/
enum class BlurLevel : int
{
None, //!< No blur used.
Blur1, //!< First blur level (2 passes)
Blur2, //!< Second blur level (4 passes)
Blur3 //!< Third blur level (6 passes)
};
/**
* constructor.
* @param type The preset shader type.
*/
explicit MilkdropShader(ShaderType type);
/**
* @brief Translates and compiles the shader code.
* @param presetShaderCode The preset shader code.
*/
void LoadCode(std::string presetShaderCode);
/**
* @brief Loads the required texture references into the shader.
*/
void LoadTextures(TextureManager& textureManager);
private:
/**
* @brief Prepares the shader code to be translated into GLSL.
* @param program The program code to work on.
*/
void PreprocessPresetShader(std::string& program);
/**
* @brief Searches for sampler references in the program and stores them in m_samplerNames.
* @param program The program code to work on.
*/
void GetReferencedSamplers(const std::string& program);
ShaderType m_type{ShaderType::WarpShader}; //!< Type of this shader.
std::string m_presetShaderCode; //!< The original preset shader code.
std::vector<std::string> m_samplerNames; //!< Names of all referenced samplers in the shader code.
Shader m_shader;
};

View File

@ -12,149 +12,3 @@
#include "Waveform.hpp"
#include <vector>
/// Container for all *read only* engine variables a preset requires to
/// evaluate milkdrop equations. Every preset object needs a reference to one of these.
class PresetInputs : public PipelineContext {
public:
/* PER_PIXEL VARIBLES BEGIN */
float x_per_pixel;
float y_per_pixel;
float rad_per_pixel;
float ang_per_pixel;
/* PER_PIXEL VARIBLES END */
float bass;
float mid;
float treb;
float bass_att;
float mid_att;
float treb_att;
/* variables were added in milkdrop 1.04 */
int gx, gy;
float **x_mesh;
float **y_mesh;
int pixelsx;
int pixelsy;
float aspectx;
float aspecty;
float **rad_mesh;
float **theta_mesh;
float **origtheta; //grid containing interpolated mesh reference values
float **origrad;
float **origx; //original mesh
float **origy;
void resetMesh();
~PresetInputs();
PresetInputs();
/// Initializes this preset inputs given a mesh size.
/// \param gx the width of the mesh
/// \param gy the height of the mesh
/// \note This must be called before reading values from this class
void Initialize(int _gx, int _gy);
/// Updates this preset inputs with the latest values from the
/// the pipeline context and beat detection unit
void update (const BeatDetect & music, const PipelineContext & context);
private:
};
/// Container class for all preset writeable engine variables. This is the important glue
/// between the presets and renderer to facilitate smooth preset switching
/// Every preset object needs a reference to one of these.
class PresetOutputs : public Pipeline {
public:
typedef std::vector<CustomWaveform*> cwave_container;
typedef std::vector<CustomShape*> cshape_container;
cwave_container customWaves;
cshape_container customShapes;
void Initialize(int _gx, int _gy);
PresetOutputs();
~PresetOutputs();
virtual void Render(const BeatDetect &music, const PipelineContext &context);
void PerPixelMath( const PipelineContext &context);
/* PER FRAME VARIABLES BEGIN */
float zoom;
float zoomexp;
float rot;
float warp;
float sx;
float sy;
float dx;
float dy;
float cx;
float cy;
VideoEcho videoEcho;
Waveform wave;
Border border;
MotionVectors mv;
DarkenCenter darkenCenter;
Brighten brighten;
Darken darken;
Invert invert;
Solarize solarize;
// ToDo: Check if redeclaration here is wanted, as the Pipeline base class also defines these.
int gy;
int gx;
/* PER_FRAME VARIABLES END */
float fRating;
float fGammaAdj;
bool bDarkenCenter;
bool bRedBlueStereo;
bool bBrighten;
bool bDarken;
bool bSolarize;
bool bInvert;
bool bMotionVectorsOn;
float fWarpAnimSpeed;
float fWarpScale;
float fShader;
float **zoom_mesh;
float **zoomexp_mesh;
float **rot_mesh;
float **sx_mesh;
float **sy_mesh;
float **dx_mesh;
float **dy_mesh;
float **cx_mesh;
float **cy_mesh;
float **warp_mesh;
float **orig_x; //original mesh
float **orig_y;
float **rad_mesh;
private:
void PerPixelMath_c( const PipelineContext &context);
#ifdef __SSE2__
void PerPixelMath_sse( const PipelineContext &context);
#endif
};

View File

@ -77,14 +77,10 @@ void PipelineMerger::mergePipelines(const Pipeline& a, const Pipeline& b, Pipeli
{
out.compositeShader = a.compositeShader;
out.warpShader = a.warpShader;
out.warpShaderFilename = a.warpShaderFilename;
out.compositeShaderFilename = a.compositeShaderFilename;
}
else
{
out.compositeShader = b.compositeShader;
out.warpShader = b.warpShader;
out.warpShaderFilename = b.warpShaderFilename;
out.compositeShaderFilename = b.compositeShaderFilename;
}
}

View File

@ -48,7 +48,11 @@
class BackgroundWorkerSync;
namespace libprojectM {
namespace Audio {
class BeatDetect;
}
} // namespace libprojectM
class Pcm;
@ -240,7 +244,7 @@ private:
#endif
class Pcm m_pcm; //!< Audio data buffer and analyzer instance.
class libprojectM::Audio::PCM m_pcm; //!< Audio data buffer and analyzer instance.
size_t m_meshX{32}; //!< Per-point mesh horizontal resolution.
size_t m_meshY{24}; //!< Per-point mesh vertical resolution.
@ -269,11 +273,11 @@ private:
std::unique_ptr<class PipelineContext> m_pipelineContext; //!< Pipeline context for the first/current preset.
std::unique_ptr<class PipelineContext> m_pipelineContext2; //!< Pipeline context for the next/transitioning preset.
std::unique_ptr<Renderer> m_renderer; //!< The Preset renderer.
std::unique_ptr<BeatDetect> m_beatDetect; //!< The beat detection class.
std::unique_ptr<Preset> m_activePreset; //!< Currently loaded preset.
std::unique_ptr<Preset> m_transitioningPreset; //!< Destination preset when smooth preset switching.
std::unique_ptr<TimeKeeper> m_timeKeeper; //!< Keeps the different timers used to render and switch presets.
std::unique_ptr<Renderer> m_renderer; //!< The Preset renderer.
std::unique_ptr<libprojectM::Audio::BeatDetect> m_beatDetect; //!< The beat detection class.
std::unique_ptr<Preset> m_activePreset; //!< Currently loaded preset.
std::unique_ptr<Preset> m_transitioningPreset; //!< Destination preset when smooth preset switching.
std::unique_ptr<TimeKeeper> m_timeKeeper; //!< Keeps the different timers used to render and switch presets.
#if PROJECTM_USE_THREADS
mutable std::recursive_mutex m_presetSwitchMutex; //!< Mutex for locking preset switching while rendering and vice versa.

View File

@ -41,9 +41,7 @@ public:
float blur1ed;
Shader warpShader;
std::string warpShaderFilename;
Shader compositeShader;
std::string compositeShaderFilename;
std::vector<RenderItem*> drawables;
std::vector<RenderItem*> compositeDrawables;

View File

@ -174,7 +174,6 @@ void Renderer::RenderItems(const Pipeline& pipeline, const PipelineContext& pipe
m_renderContext.invAspectX = m_fInvAspectX;
m_renderContext.invAspectY = m_fInvAspectY;
m_renderContext.textureManager = m_textureManager.get();
m_renderContext.beatDetect = m_beatDetect;
for (std::vector<RenderItem*>::const_iterator pos = pipeline.drawables.begin(); pos != pipeline.drawables.end(); ++pos)
{

View File

@ -1,5 +1,4 @@
#ifndef Renderer_HPP
#define Renderer_HPP
#pragma once
#include "Audio/BeatDetect.hpp"
#include "Pipeline.hpp"
@ -174,5 +173,3 @@ private:
float m_fInvAspectX{1.0};
float m_fInvAspectY{1.0};
};
#endif

View File

@ -1,10 +1,219 @@
/*
* Shader.cpp
*
* Created on: Jun 29, 2008
* Author: pete
*/
#include "Shader.hpp"
Shader::Shader() {}
#include <glm/gtc/type_ptr.hpp>
Shader::Shader()
: m_shaderProgram(glCreateProgram())
{
}
Shader::~Shader()
{
if (m_shaderProgram)
{
glDeleteProgram(m_shaderProgram);
}
}
void Shader::CompileProgram(const std::string& vertexShaderSource, std::string& fragmentShaderSource)
{
auto vertexShader = CompileShader(vertexShaderSource, GL_VERTEX_SHADER);
auto fragmentShader = CompileShader(fragmentShaderSource, GL_FRAGMENT_SHADER);
glAttachShader(m_shaderProgram, vertexShader);
glAttachShader(m_shaderProgram, fragmentShader);
glLinkProgram(m_shaderProgram);
// Shader objects are no longer needed after linking, free the memory.
glDetachShader(m_shaderProgram, vertexShader);
glDetachShader(m_shaderProgram, fragmentShader);
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
GLint programLinked;
glGetProgramiv(m_shaderProgram, GL_LINK_STATUS, &programLinked);
if (programLinked == GL_TRUE)
{
return;
}
GLint infoLogLength{};
glGetProgramiv(m_shaderProgram, GL_INFO_LOG_LENGTH, &infoLogLength);
std::vector<char> message(infoLogLength + 1);
glGetProgramInfoLog(m_shaderProgram, infoLogLength, nullptr, message.data());
throw ShaderException("Error compiling shader: " + std::string(message.data()));
}
bool Shader::Validate(std::string& validationMessage) const
{
GLint result{GL_FALSE};
int infoLogLength;
glValidateProgram(m_shaderProgram);
glGetProgramiv(m_shaderProgram, GL_VALIDATE_STATUS, &result);
glGetProgramiv(m_shaderProgram, GL_INFO_LOG_LENGTH, &infoLogLength);
if (infoLogLength > 0)
{
std::vector<char> validationErrorMessage(infoLogLength + 1);
glGetProgramInfoLog(m_shaderProgram, infoLogLength, nullptr, validationErrorMessage.data());
validationMessage = std::string(validationErrorMessage.data());
}
return result;
}
void Shader::Bind()
{
if (m_shaderProgram > 0)
{
glUseProgram(m_shaderProgram);
}
}
void Shader::Unbind()
{
glUseProgram(0);
}
void Shader::BindTextures()
{
int texNum{0};
std::map<std::string, Texture*> texSizes;
// Set samplers
for (const auto& samplerIt : m_textures)
{
std::string const texName = samplerIt.first;
Texture* texture = samplerIt.second.first;
Sampler* sampler = samplerIt.second.second;
std::string const samplerName = "sampler_" + texName;
texSizes[texName] = texture;
texSizes[texture->name] = texture;
// https://www.khronos.org/opengl/wiki/Sampler_(GLSL)#Binding_textures_to_samplers
glActiveTexture(GL_TEXTURE0 + texNum);
glBindTexture(texture->type, texture->texID);
glBindSampler(texNum, sampler->samplerID);
SetUniformInt(samplerName.c_str(), texNum);
texNum++;
}
// Set texture size uniforms
for (const auto& texSize : texSizes)
{
Texture* texture = texSize.second;
std::string const texSizeName = "texsize_" + texSize.first;
SetUniformFloat4(texSizeName.c_str(), {texture->width,
texture->height,
1 / (float) texture->width,
1 / (float) texture->height});
}
}
void Shader::SetUniformTexture(const char* uniform, TextureSamplerDesc texture)
{
}
void Shader::SetUniformFloat(const char* uniform, float value)
{
auto location = glGetUniformLocation(m_shaderProgram, uniform);
if (location < 0)
{
return;
}
glUniform1fv(location, 1, &value);
}
void Shader::SetUniformInt(const char* uniform, int value)
{
auto location = glGetUniformLocation(m_shaderProgram, uniform);
if (location < 0)
{
return;
}
glUniform1iv(location, 1, &value);
}
void Shader::SetUniformFloat2(const char* uniform, const glm::vec2& values)
{
auto location = glGetUniformLocation(m_shaderProgram, uniform);
if (location < 0)
{
return;
}
glUniform2fv(location, 1, glm::value_ptr(values));
}
void Shader::SetUniformFloat3(const char* uniform, const glm::vec3& values)
{
auto location = glGetUniformLocation(m_shaderProgram, uniform);
if (location < 0)
{
return;
}
glUniform3fv(location, 1, glm::value_ptr(values));
}
void Shader::SetUniformFloat4(const char* uniform, const glm::vec4& values)
{
auto location = glGetUniformLocation(m_shaderProgram, uniform);
if (location < 0)
{
return;
}
glUniform4fv(location, 1, glm::value_ptr(values));
}
void Shader::SetUniformMat3x4(const char* uniform, const glm::mat3x4& values)
{
auto location = glGetUniformLocation(m_shaderProgram, uniform);
if (location < 0)
{
return;
}
glUniformMatrix3x4fv(location, 1, GL_FALSE, glm::value_ptr(values));
}
void Shader::SetUniformMat4x4(const char* uniform, const glm::mat4x4& values)
{
auto location = glGetUniformLocation(m_shaderProgram, uniform);
if (location < 0)
{
return;
}
glUniformMatrix4fv(location, 1, GL_FALSE, glm::value_ptr(values));
}
GLuint Shader::CompileShader(const std::string& source, GLenum type)
{
GLint shaderCompiled{};
auto shader = glCreateShader(type);
const auto *shaderSourceCStr = source.c_str();
glShaderSource(shader, 1, &shaderSourceCStr, nullptr);
glCompileShader(shader);
glGetShaderiv(shader, GL_COMPILE_STATUS, &shaderCompiled);
if (shaderCompiled == GL_TRUE)
{
return shader;
}
GLint infoLogLength{};
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLength);
std::vector<char> message(infoLogLength + 1);
glGetShaderInfoLog(shader, infoLogLength, nullptr, message.data());
glDeleteShader(shader);
throw ShaderException("Error compiling shader: " + std::string(message.data()));
}

View File

@ -1,27 +1,160 @@
/*
* Shader.hpp
*
* Created on: Jun 29, 2008
* Author: pete
/**
* @file Shader.hpp
* @brief Implements an interface to a single shader program instance.
*/
#pragma once
#ifndef SHADER_HPP_
#define SHADER_HPP_
#include <string>
#include <map>
#include "Texture.hpp"
#include <glm/vec2.hpp>
#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <glm/mat3x4.hpp>
#include <glm/mat4x4.hpp>
#include <map>
#include <string>
/**
* @brief Shader compilation exception.
*/
class ShaderException : public std::exception
{
public:
inline ShaderException(std::string message)
: m_message(std::move(message))
{
}
virtual ~ShaderException() = default;
const std::string& message() const
{
return m_message;
}
private:
std::string m_message;
};
/**
* @brief Base class containing a shader program, consisting of a vertex and fragment shader.
*/
class Shader
{
public:
/**
* Creates a new shader.
*/
Shader();
std::map<std::string, TextureSamplerDesc> textures;
/**
* Destructor.
*/
~Shader();
std::string programSource;
std::string presetPath;
/**
* @brief Compiles a vertex and fragment shader into a program.
* @throws ShaderException Thrown if compilation of a shader or program linking failed.
* @param vertexShaderSource The vertex shader source.
* @param fragmentShaderSource The fragment shader source.
*/
void CompileProgram(const std::string& vertexShaderSource, std::string& fragmentShaderSource);
Shader();
/**
* @brief Validates that the program can run in the current state.
* @param validationMessage The error message if validation failed.
* @return true if the shader program is valid and can run, false if it broken.
*/
bool Validate(std::string& validationMessage) const;
/**
* Binds the program into the current context.
*/
void Bind();
/**
* Unbinds the program.
*/
static void Unbind();
/**
* @brief Binds the registered textures to the current program context.
* The program must be bound before calling this method!
*/
void BindTextures();
void SetUniformTexture(const char* uniform, TextureSamplerDesc texture);
/**
* @brief Sets a single float uniform.
* The program must be bound before calling this method!
* @param uniform The uniform name
* @param value The value to set.
*/
void SetUniformFloat(const char* uniform, float value);
/**
* @brief Sets a single integer uniform.
* The program must be bound before calling this method!
* @param uniform The uniform name
* @param value The value to set.
*/
void SetUniformInt(const char* uniform, int value);
/**
* @brief Sets a float vec2 uniform.
* The program must be bound before calling this method!
* @param uniform The uniform name
* @param values The values to set.
*/
void SetUniformFloat2(const char* uniform, const glm::vec2& values);
/**
* @brief Sets a float vec3 uniform.
* The program must be bound before calling this method!
* @param uniform The uniform name
* @param values The values to set.
*/
void SetUniformFloat3(const char* uniform, const glm::vec3& values);
/**
* @brief Sets a float vec4 uniform.
* The program must be bound before calling this method!
* @param uniform The uniform name
* @param values The values to set.
*/
void SetUniformFloat4(const char* uniform, const glm::vec4& values);
/**
* @brief Sets a float 3x4 matrix uniform.
* The program must be bound before calling this method!
* @param uniform The uniform name
* @param values The matrix to set.
*/
void SetUniformMat3x4(const char* uniform, const glm::mat3x4& values);
/**
* @brief Sets a float 4x4 matrix uniform.
* The program must be bound before calling this method!
* @param uniform The uniform name
* @param values The matrix to set.
*/
void SetUniformMat4x4(const char* uniform, const glm::mat4x4& values);
private:
/**
* @brief Compiles a single shader.
* @throws ShaderException Thrown if compilation of the shader failed.
* @param source The shader source.
* @param type The shader type, e.g. GL_VERTEX_SHADER.
* @return The shader ID.
*/
auto CompileShader(const std::string& source, GLenum type) -> GLuint;
std::map<std::string, TextureSamplerDesc> m_textures; //!< Textures used in this program.
GLuint m_shaderProgram{}; //!< The program ID.
};
#endif /* SHADER_HPP_ */

View File

@ -127,7 +127,7 @@ void ShaderEngine::setParams(int _texsizeX, int _texsizeY,
// compile a user-defined shader from a preset. returns program ID if successful.
GLuint ShaderEngine::compilePresetShader(const PresentShaderType shaderType, Shader &pmShader, const std::string &shaderFilename) {
std::string program = pmShader.programSource;
std::string program = pmShader.m_programSource;
if (program.length() <= 0)
{
@ -218,22 +218,22 @@ GLuint ShaderEngine::compilePresetShader(const PresentShaderType shaderType, Sha
program.resize(pos);
}
pmShader.textures.clear();
pmShader.m_textures.clear();
// Add builtin textures
pmShader.textures["main"] = textureManager->getTexture("main", GL_REPEAT, GL_LINEAR);
pmShader.textures["fc_main"] = textureManager->getTexture("main", GL_CLAMP_TO_EDGE, GL_LINEAR);
pmShader.textures["pc_main"] = textureManager->getTexture("main", GL_CLAMP_TO_EDGE, GL_NEAREST);
pmShader.textures["fw_main"] = textureManager->getTexture("main", GL_REPEAT, GL_LINEAR);
pmShader.textures["pw_main"] = textureManager->getTexture("main", GL_REPEAT, GL_NEAREST);
pmShader.m_textures["main"] = textureManager->getTexture("main", GL_REPEAT, GL_LINEAR);
pmShader.m_textures["fc_main"] = textureManager->getTexture("main", GL_CLAMP_TO_EDGE, GL_LINEAR);
pmShader.m_textures["pc_main"] = textureManager->getTexture("main", GL_CLAMP_TO_EDGE, GL_NEAREST);
pmShader.m_textures["fw_main"] = textureManager->getTexture("main", GL_REPEAT, GL_LINEAR);
pmShader.m_textures["pw_main"] = textureManager->getTexture("main", GL_REPEAT, GL_NEAREST);
pmShader.textures["noise_lq"] = textureManager->getTexture("noise_lq", GL_REPEAT, GL_LINEAR);
pmShader.textures["noise_lq_lite"] = textureManager->getTexture("noise_lq_lite", GL_REPEAT, GL_LINEAR);
pmShader.textures["noise_mq"] = textureManager->getTexture("noise_mq", GL_REPEAT, GL_LINEAR);
pmShader.textures["noise_hq"] = textureManager->getTexture("noise_hq", GL_REPEAT, GL_LINEAR);
pmShader.textures["noisevol_lq"] = textureManager->getTexture("noisevol_lq", GL_REPEAT, GL_LINEAR);
pmShader.textures["noisevol_hq"] = textureManager->getTexture("noisevol_hq", GL_REPEAT, GL_LINEAR);
pmShader.m_textures["noise_lq"] = textureManager->getTexture("noise_lq", GL_REPEAT, GL_LINEAR);
pmShader.m_textures["noise_lq_lite"] = textureManager->getTexture("noise_lq_lite", GL_REPEAT, GL_LINEAR);
pmShader.m_textures["noise_mq"] = textureManager->getTexture("noise_mq", GL_REPEAT, GL_LINEAR);
pmShader.m_textures["noise_hq"] = textureManager->getTexture("noise_hq", GL_REPEAT, GL_LINEAR);
pmShader.m_textures["noisevol_lq"] = textureManager->getTexture("noisevol_lq", GL_REPEAT, GL_LINEAR);
pmShader.m_textures["noisevol_hq"] = textureManager->getTexture("noisevol_hq", GL_REPEAT, GL_LINEAR);
@ -272,15 +272,15 @@ GLuint ShaderEngine::compilePresetShader(const PresentShaderType shaderType, Sha
}
else
{
std::map<std::string, TextureSamplerDesc>::const_iterator iter = pmShader.textures.cbegin();
for ( ; iter != pmShader.textures.cend(); ++iter)
std::map<std::string, TextureSamplerDesc>::const_iterator iter = pmShader.m_textures.cbegin();
for ( ; iter != pmShader.m_textures.cend(); ++iter)
{
if (iter->first == sampler)
break;
}
if (iter == pmShader.textures.cend())
pmShader.textures[sampler] = texDesc;
if (iter == pmShader.m_textures.cend())
pmShader.m_textures[sampler] = texDesc;
}
}
@ -293,9 +293,9 @@ GLuint ShaderEngine::compilePresetShader(const PresentShaderType shaderType, Sha
if (found != std::string::npos)
{
blur1_enabled = blur2_enabled = blur3_enabled = true;
pmShader.textures["blur3"] = textureManager->getTexture("blur3", GL_REPEAT, GL_LINEAR);
pmShader.textures["blur2"] = textureManager->getTexture("blur2", GL_REPEAT, GL_LINEAR);
pmShader.textures["blur1"] = textureManager->getTexture("blur1", GL_REPEAT, GL_LINEAR);
pmShader.m_textures["blur3"] = textureManager->getTexture("blur3", GL_REPEAT, GL_LINEAR);
pmShader.m_textures["blur2"] = textureManager->getTexture("blur2", GL_REPEAT, GL_LINEAR);
pmShader.m_textures["blur1"] = textureManager->getTexture("blur1", GL_REPEAT, GL_LINEAR);
}
else
{
@ -303,8 +303,8 @@ GLuint ShaderEngine::compilePresetShader(const PresentShaderType shaderType, Sha
if (found != std::string::npos)
{
blur1_enabled = blur2_enabled = true;
pmShader.textures["blur2"] = textureManager->getTexture("blur2", GL_REPEAT, GL_LINEAR);
pmShader.textures["blur1"] = textureManager->getTexture("blur1", GL_REPEAT, GL_LINEAR);
pmShader.m_textures["blur2"] = textureManager->getTexture("blur2", GL_REPEAT, GL_LINEAR);
pmShader.m_textures["blur1"] = textureManager->getTexture("blur1", GL_REPEAT, GL_LINEAR);
}
else
{
@ -312,7 +312,7 @@ GLuint ShaderEngine::compilePresetShader(const PresentShaderType shaderType, Sha
if (found != std::string::npos)
{
blur1_enabled = true;
pmShader.textures["blur1"] = textureManager->getTexture("blur1", GL_REPEAT, GL_LINEAR);
pmShader.m_textures["blur1"] = textureManager->getTexture("blur1", GL_REPEAT, GL_LINEAR);
}
}
}
@ -377,8 +377,8 @@ GLuint ShaderEngine::compilePresetShader(const PresentShaderType shaderType, Sha
// Declare samplers
std::set<std::string> texsizes;
std::map<std::string, TextureSamplerDesc>::const_iterator iter_samplers = pmShader.textures.cbegin();
for ( ; iter_samplers != pmShader.textures.cend(); ++iter_samplers)
std::map<std::string, TextureSamplerDesc>::const_iterator iter_samplers = pmShader.m_textures.cbegin();
for ( ; iter_samplers != pmShader.m_textures.cend(); ++iter_samplers)
{
Texture * texture = iter_samplers->second.first;
@ -572,8 +572,8 @@ void ShaderEngine::SetupTextures(GLuint program, const Shader &shader)
std::map<std::string, Texture*> texsizes;
// Set samplers
for (std::map<std::string, TextureSamplerDesc>::const_iterator iter_samplers = shader.textures.begin(); iter_samplers
!= shader.textures.end(); ++iter_samplers)
for (std::map<std::string, TextureSamplerDesc>::const_iterator iter_samplers = shader.m_textures.begin(); iter_samplers
!= shader.m_textures.end(); ++iter_samplers)
{
std::string texName = iter_samplers->first;
Texture * texture = iter_samplers->second.first;
@ -813,13 +813,13 @@ void ShaderEngine::loadPresetShaders(Pipeline &pipeline)
programID_presetComp = GL_FALSE;
// compile and link warp and composite shaders from pipeline
if (!pipeline.warpShader.programSource.empty()) {
if (!pipeline.warpShader.m_programSource.empty()) {
programID_presetWarp = loadPresetShader(PresentWarpShader, pipeline.warpShader, pipeline.warpShaderFilename);
uniform_vertex_transf_warp_shader = glGetUniformLocation(programID_presetWarp, "vertex_transformation");
presetWarpShaderLoaded = true;
}
if (!pipeline.compositeShader.programSource.empty()) {
if (!pipeline.compositeShader.m_programSource.empty()) {
programID_presetComp = loadPresetShader(PresentCompositeShader, pipeline.compositeShader, pipeline.compositeShaderFilename);
presetCompShaderLoaded = true;
}
@ -927,28 +927,14 @@ GLuint ShaderEngine::CompileShaderProgram(const std::string & VertexShaderCode,
return linkOK ? programID : GL_FALSE;
}
void ShaderEngine::validateProgram(const GLuint programID) {
GLint Result = GL_FALSE;
int InfoLogLength;
glValidateProgram(programID);
// Check the program
glGetProgramiv(programID, GL_VALIDATE_STATUS, &Result);
glGetProgramiv(programID, GL_INFO_LOG_LENGTH, &InfoLogLength);
if ( InfoLogLength > 0 ){
std::vector<char> ProgramErrorMessage(InfoLogLength+1);
glGetProgramInfoLog(programID, InfoLogLength, NULL, &ProgramErrorMessage[0]);
fprintf(stderr, "%s\n", &ProgramErrorMessage[0]);
}
}
// use the appropriate shader program for rendering the interpolation.
// it will use the preset shader if available, otherwise the textured shader
bool ShaderEngine::enableWarpShader(Shader &shader, const Pipeline &pipeline, const PipelineContext &pipelineContext, const glm::mat4 & mat_ortho) {
if (presetWarpShaderLoaded) {
glUseProgram(programID_presetWarp);
shader.Bind();
shader.BindTextures();
SetupTextures(programID_presetWarp, shader);
SetupShaderVariables(programID_presetWarp, pipeline, pipelineContext);

View File

@ -23,26 +23,6 @@ class ShaderEngine;
#include "Shader.hpp"
#include <glm/vec3.hpp>
class ShaderException : public std::exception
{
public:
inline ShaderException(std::string message)
: m_message(std::move(message))
{
}
virtual ~ShaderException() = default;
const std::string& message() const
{
return m_message;
}
private:
std::string m_message;
};
class ShaderEngine
{
public:
@ -56,6 +36,7 @@ public:
ShaderEngine();
virtual ~ShaderEngine();
void loadPresetShaders(Pipeline &pipeline);
bool enableWarpShader(Shader &shader, const Pipeline &pipeline, const PipelineContext &pipelineContext, const glm::mat4 & mat_ortho);
bool enableCompositeShader(Shader &shader, const Pipeline &pipeline, const PipelineContext &pipelineContext);
@ -88,7 +69,6 @@ private:
int texsizeY;
float aspectX;
float aspectY;
BeatDetect *beatDetect;
TextureManager *textureManager;
GLint uniform_vertex_transf_warp_shader;
@ -127,10 +107,6 @@ private:
void disablePresetShaders();
GLuint loadPresetShader(const PresentShaderType shaderType, Shader &shader, std::string &shaderFilename);
void deletePresetShader(Shader &shader);
void validateProgram(const GLuint programID);
// programs generated from preset shader code
GLuint programID_presetComp, programID_presetWarp;