From 18240e2bb031551e9c72a55c7d974904d209760a Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Tue, 19 Dec 2017 18:24:48 +0200 Subject: [PATCH] Add a system for creating Pipelines from loadable templates --- source/bloom.cpp | 22 +++++ source/bloom.h | 17 ++++ source/colorcurve.cpp | 41 +++++++++ source/colorcurve.h | 23 ++++++ source/pipeline.cpp | 15 +++- source/pipeline.h | 16 +++- source/pipelinebuilder.cpp | 65 +++++++++++++++ source/pipelinebuilder.h | 33 ++++++++ source/pipelinetemplate.cpp | 161 ++++++++++++++++++++++++++++++++++++ source/pipelinetemplate.h | 89 ++++++++++++++++++++ source/postprocessor.cpp | 12 +++ source/postprocessor.h | 19 +++++ source/resources.cpp | 2 + 13 files changed, 512 insertions(+), 3 deletions(-) create mode 100644 source/pipelinebuilder.cpp create mode 100644 source/pipelinebuilder.h create mode 100644 source/pipelinetemplate.cpp create mode 100644 source/pipelinetemplate.h diff --git a/source/bloom.cpp b/source/bloom.cpp index 7537b0a6..e00fd0e1 100644 --- a/source/bloom.cpp +++ b/source/bloom.cpp @@ -84,5 +84,27 @@ void Bloom::render(Renderer &renderer, const Texture2D &src, const Texture2D &) quad.draw(renderer); } + +Bloom::Template::Template(): + radius(2.0f), + strength(0.2f) +{ } + +Bloom *Bloom::Template::create(unsigned width, unsigned height) const +{ + RefPtr bloom = new Bloom(width/size_divisor, height/size_divisor); + bloom->set_radius(radius); + bloom->set_strength(strength); + return bloom.release(); +} + + +Bloom::Template::Loader::Loader(Template &t): + DataFile::DerivedObjectLoader(t) +{ + add("strength", &Template::strength); + add("radius", &Template::radius); +} + } // namespace GL } // namespace Msp diff --git a/source/bloom.h b/source/bloom.h index b3841c73..d8bb61ac 100644 --- a/source/bloom.h +++ b/source/bloom.h @@ -23,6 +23,23 @@ a blur filter. */ class Bloom: public PostProcessor { +public: + struct Template: public PostProcessor::Template + { + class Loader: public DataFile::DerivedObjectLoader + { + public: + Loader(Template &); + }; + + float radius; + float strength; + + Template(); + + virtual Bloom *create(unsigned, unsigned) const; + }; + private: RenderTarget *target[2]; ProgramData common_shdata; diff --git a/source/colorcurve.cpp b/source/colorcurve.cpp index f1c09030..40f61b81 100644 --- a/source/colorcurve.cpp +++ b/source/colorcurve.cpp @@ -88,5 +88,46 @@ void ColorCurve::render(Renderer &renderer, const Texture2D &color_buf, const Te quad.draw(renderer); } + +ColorCurve::Template::Template(): + exposure_adjust(0.0f), + brightness_response(0.4f), + gamma(1.0f), + srgb(false) +{ } + +ColorCurve *ColorCurve::Template::create(unsigned, unsigned) const +{ + RefPtr colorcurve = new ColorCurve; + colorcurve->set_exposure_adjust(exposure_adjust); + colorcurve->set_brightness_response(brightness_response); + if(srgb) + colorcurve->set_srgb(); + else + colorcurve->set_gamma(gamma); + return colorcurve.release(); +} + + +ColorCurve::Template::Loader::Loader(Template &t): + DataFile::DerivedObjectLoader(t) +{ + add("brightness_response", &Template::brightness_response); + add("exposure_adjust", &Template::exposure_adjust); + add("gamma", &Loader::gamma); + add("srgb", &Loader::srgb); +} + +void ColorCurve::Template::Loader::gamma(float g) +{ + obj.gamma = g; + obj.srgb = false; +} + +void ColorCurve::Template::Loader::srgb() +{ + obj.srgb = true; +} + } // namespace GL } // namespace Msp diff --git a/source/colorcurve.h b/source/colorcurve.h index 608af448..dac0ac08 100644 --- a/source/colorcurve.h +++ b/source/colorcurve.h @@ -21,6 +21,29 @@ space and converting to sRGB for display. */ class ColorCurve: public PostProcessor { +public: + struct Template: public PostProcessor::Template + { + class Loader: public DataFile::DerivedObjectLoader + { + public: + Loader(Template &); + + private: + void gamma(float); + void srgb(); + }; + + float exposure_adjust; + float brightness_response; + float gamma; + bool srgb; + + Template(); + + virtual ColorCurve *create(unsigned, unsigned) const; + }; + private: Program shprog; ProgramData shdata; diff --git a/source/pipeline.cpp b/source/pipeline.cpp index 9e2976ab..037741d2 100644 --- a/source/pipeline.cpp +++ b/source/pipeline.cpp @@ -136,7 +136,19 @@ Pipeline::Pass &Pipeline::add_pass(const Tag &tag, Renderable &r) void Pipeline::add_postprocessor(PostProcessor &pp) { - postproc.push_back(&pp); + add_postprocessor(&pp, true); +} + +void Pipeline::add_postprocessor_owned(PostProcessor *pp) +{ + add_postprocessor(pp, false); +} + +void Pipeline::add_postprocessor(PostProcessor *pp, bool keep) +{ + postproc.push_back(pp); + if(keep) + postproc.back().keep(); try { create_targets(0); @@ -192,7 +204,6 @@ void Pipeline::render(Renderer &renderer, const Tag &tag) const fbo.clear(COLOR_BUFFER_BIT|DEPTH_BUFFER_BIT); } - for(PassList::const_iterator i=passes.begin(); i!=passes.end(); ++i) { if(const DepthTest *dt = i->get_depth_test()) diff --git a/source/pipeline.h b/source/pipeline.h index 35d10a41..9ac03efb 100644 --- a/source/pipeline.h +++ b/source/pipeline.h @@ -76,7 +76,7 @@ private: PassList passes; const Camera *camera; std::vector renderables; - std::vector postproc; + std::vector > postproc; unsigned width; unsigned height; bool hdr; @@ -98,6 +98,11 @@ public: void set_multisample(unsigned); + unsigned get_width() const { return width; } + unsigned get_height() const { return height; } + bool get_hdr() const { return hdr; } + unsigned get_multisample() const { return samples; } + // Deprecated void set_camera(const Camera *); Pass &add_pass(const Tag &tag); @@ -112,6 +117,15 @@ public: /** Adds a postprocessor to the pipeline. */ void add_postprocessor(PostProcessor &); + /** Adds a postprocessor to the pipeline, transferring ownership. The + postprocessor will be deleted together with with pipeline. It is also + deleted if this call throws an exception. */ + void add_postprocessor_owned(PostProcessor *); + +private: + void add_postprocessor(PostProcessor *, bool); + +public: virtual void setup_frame(Renderer &); virtual void finish_frame(); diff --git a/source/pipelinebuilder.cpp b/source/pipelinebuilder.cpp new file mode 100644 index 00000000..cbf181fe --- /dev/null +++ b/source/pipelinebuilder.cpp @@ -0,0 +1,65 @@ +#include +#include +#include "error.h" +#include "pipeline.h" +#include "pipelinebuilder.h" +#include "pipelinetemplate.h" +#include "renderbuffer.h" + +using namespace std; + +namespace Msp { +namespace GL { + +PipelineBuilder::PipelineBuilder(const PipelineTemplate &t): + tmpl(t) +{ + const vector &passes = tmpl.get_passes(); + for(vector::const_iterator i=passes.begin(); i!=passes.end(); ++i) + renderables[i->renderable_name] = 0; +} + +void PipelineBuilder::set_renderable(const string &name, Renderable &rend) +{ + get_item(renderables, name) = &rend; +} + +void PipelineBuilder::build(Pipeline &pipeline) const +{ + pipeline.set_hdr(tmpl.get_hdr()); + unsigned samples = min(tmpl.get_maximum_multisample(), Renderbuffer::get_max_samples()); + if(samplesrenderable_name); + if(!renderable) + continue; + + Pipeline::Pass &pass = pipeline.add_pass(i->tag, *renderable); + pass.set_blend(i->blend.get()); + pass.set_depth_test(i->depth_test.get()); + pass.set_lighting(i->lighting.get()); + } + + const PipelineTemplate::PostProcessorArray &postprocs = tmpl.get_postprocessors(); + for(PipelineTemplate::PostProcessorArray::const_iterator i=postprocs.begin(); i!=postprocs.end(); ++i) + { + PostProcessor *proc = (*i)->create(pipeline.get_width(), pipeline.get_height()); + pipeline.add_postprocessor_owned(proc); + } +} + +Pipeline *PipelineBuilder::build(const View &view) const +{ + RefPtr pipeline = new Pipeline(view); + build(*pipeline); + return pipeline.release(); +} + +} // namespace GL +} // namespace Msp diff --git a/source/pipelinebuilder.h b/source/pipelinebuilder.h new file mode 100644 index 00000000..5ad420e3 --- /dev/null +++ b/source/pipelinebuilder.h @@ -0,0 +1,33 @@ +#ifndef PIPELINEBUILDER_H_ +#define PIPELINEBUILDER_H_ + +#include +#include + +namespace Msp { +namespace GL { + +class Pipeline; +class PipelineTemplate; +class Renderable; +class View; + +class PipelineBuilder +{ +private: + const PipelineTemplate &tmpl; + std::map renderables; + +public: + PipelineBuilder(const PipelineTemplate &); + + void set_renderable(const std::string &, Renderable &); + + void build(Pipeline &) const; + Pipeline *build(const View &) const; +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/pipelinetemplate.cpp b/source/pipelinetemplate.cpp new file mode 100644 index 00000000..6dbfd0a2 --- /dev/null +++ b/source/pipelinetemplate.cpp @@ -0,0 +1,161 @@ +#include +#include +#include "blend.h" +#include "bloom.h" +#include "colorcurve.h" +#include "lighting.h" +#include "pipelinetemplate.h" +#include "tests.h" + +using namespace std; + +namespace Msp { +namespace GL { + +PipelineTemplate::PipelineTemplate(): + hdr(false), + required_multisample(0), + max_multisample(0) +{ } + + +PipelineTemplate::Pass::~Pass() +{ } + + +PipelineTemplate::Loader::Loader(PipelineTemplate &t): + DataFile::CollectionObjectLoader(t, 0) +{ + init(); +} + +PipelineTemplate::Loader::Loader(PipelineTemplate &t, Collection &c): + DataFile::CollectionObjectLoader(t, &c) +{ + init(); +} + +void PipelineTemplate::Loader::init() +{ + add("bloom", &Loader::postprocessor); + add("colorcurve", &Loader::postprocessor); + add("hdr", &PipelineTemplate::hdr); + add("multisample", &Loader::multisample); + add("multisample", &Loader::multisample_range); + add("pass", &Loader::pass); +} + +void PipelineTemplate::Loader::multisample(unsigned samples) +{ + obj.required_multisample = samples; + obj.max_multisample = samples; +} + +void PipelineTemplate::Loader::multisample_range(unsigned req, unsigned max) +{ + obj.required_multisample = req; + obj.max_multisample = max; +} + +void PipelineTemplate::Loader::pass(const string &tag, const string &rend) +{ + Pass pss;; + pss.tag = tag; + pss.renderable_name = rend; + if(coll) + load_sub(pss, *coll); + else + load_sub(pss); + + obj.passes.push_back(pss); +} + +template +void PipelineTemplate::Loader::postprocessor() +{ + RefPtr postproc = new typename T::Template; + load_sub(*postproc); + obj.postprocessors.push_back(postproc.release()); +} + + +PipelineTemplate::Pass::Loader::Loader(Pass &p): + DataFile::CollectionObjectLoader(p, 0) +{ + init(); +} + +PipelineTemplate::Pass::Loader::Loader(Pass &p, Collection &c): + DataFile::CollectionObjectLoader(p, &c) +{ + init(); +} + +void PipelineTemplate::Pass::Loader::init() +{ + add("blend", &Loader::blend); + add("blend", &Loader::blend_predefined); + add("depth_test", &Loader::depth_test); + add("depth_test", &Loader::depth_test_predefined); + add("lighting", &Loader::lighting); + add("lighting", &Loader::lighting_inline); +} + +void PipelineTemplate::Pass::Loader::blend_predefined(const string &name) +{ + const Blend *blend = 0; + if(name=="alpha") + blend = &Blend::alpha(); + else if(name=="additive") + blend = &Blend::additive(); + else if(name=="additive_alpha") + blend = &Blend::additive_alpha(); + else + throw key_error(name); + + obj.blend = blend; + obj.blend.keep(); +} + +void PipelineTemplate::Pass::Loader::blend(BlendFactor src, BlendFactor dest) +{ + obj.blend = new Blend(src, dest); +} + +void PipelineTemplate::Pass::Loader::depth_test_predefined(const string &name) +{ + const DepthTest *dtest = 0; + if(name=="lequal") + dtest = &DepthTest::lequal(); + else + throw key_error(name); + + obj.depth_test = dtest; + obj.depth_test.keep(); +} + +void PipelineTemplate::Pass::Loader::depth_test(Predicate pred) +{ + obj.depth_test = new DepthTest(pred); +} + +void PipelineTemplate::Pass::Loader::lighting_inline() +{ + RefPtr lightn = new Lighting; + load_sub(*lightn); + obj.lighting = lightn; +} + +void PipelineTemplate::Pass::Loader::lighting(const string &name) +{ + obj.lighting = &get_collection().get(name); + obj.lighting.keep(); +} + +/*void PipelineTemplate::Pass::Loader::scene(const string &name) +{ + obj.default_renderable = get_collection().get(name); +}*/ + +} // namespace GL +} // namespace Msp diff --git a/source/pipelinetemplate.h b/source/pipelinetemplate.h new file mode 100644 index 00000000..56c4da19 --- /dev/null +++ b/source/pipelinetemplate.h @@ -0,0 +1,89 @@ +#ifndef PIPELINETEMPLATE_H_ +#define PIPELINETEMPLATE_H_ + +#include +#include +#include +#include "blend.h" +#include "postprocessor.h" +#include "predicate.h" + +namespace Msp { +namespace GL { + +class DepthTest; +class Lighting; + +class PipelineTemplate +{ +public: + class Loader: public DataFile::CollectionObjectLoader + { + public: + Loader(PipelineTemplate &); + Loader(PipelineTemplate &, Collection &); + private: + void init(); + + void multisample(unsigned); + void multisample_range(unsigned, unsigned); + void pass(const std::string &, const std::string &); + + template + void postprocessor(); + }; + + struct Pass + { + class Loader: public DataFile::CollectionObjectLoader + { + public: + Loader(Pass &); + Loader(Pass &, Collection &); + private: + void init(); + + void blend(BlendFactor, BlendFactor); + void blend_predefined(const std::string &); + void depth_test(Predicate); + void depth_test_predefined(const std::string &); + void lighting(const std::string &); + void lighting_inline(); + // TODO requires support for scenes in Resources + //void scene(const std::string &); + }; + + std::string tag; + RefPtr lighting; + RefPtr depth_test; + RefPtr blend; + std::string renderable_name; + //Renderable *default_renderable; + + ~Pass(); + }; + + typedef std::vector PassArray; + typedef std::vector PostProcessorArray; + +private: + bool hdr; + unsigned required_multisample; + unsigned max_multisample; + PassArray passes; + PostProcessorArray postprocessors; + +public: + PipelineTemplate(); + + bool get_hdr() const { return hdr; } + unsigned get_required_multisample() const { return required_multisample; } + unsigned get_maximum_multisample() const { return max_multisample; } + const PassArray &get_passes() const { return passes; } + const PostProcessorArray &get_postprocessors() const { return postprocessors; } +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/postprocessor.cpp b/source/postprocessor.cpp index 26330a4d..28ae71b0 100644 --- a/source/postprocessor.cpp +++ b/source/postprocessor.cpp @@ -50,5 +50,17 @@ const Mesh &PostProcessor::create_fullscreen_quad() return mesh; } + +PostProcessor::Template::Template(): + size_divisor(1) +{ } + + +PostProcessor::Template::Loader::Loader(Template &t): + DataFile::ObjectLoader