Refining the custom shader pipeline design and registration mechanism

This commit is contained in:
Sam Gateau 2017-07-12 14:13:58 +02:00
parent 1f2f539507
commit 98cc8c0683
8 changed files with 87 additions and 71 deletions

View file

@ -13,6 +13,7 @@
#include <DependencyManager.h>
#include <PerfStat.h>
#include <GeometryCache.h>
#include <render/ShapePipeline.h>
#include <StencilMaskPass.h>
#include <AbstractViewStateInterface.h>
#include "EntitiesRendererLogging.h"
@ -29,6 +30,16 @@ class ParticlePayloadData {
public:
static const size_t VERTEX_PER_PARTICLE = 4;
static uint8_t CUSTOM_PIPELINE_NUMBER;
static render::ShapePipelinePointer shapePipelineFactory(const render::ShapePlumber& plumber, const render::ShapeKey& key);
static std::weak_ptr<gpu::Pipeline> _texturedPipeline;
static void registerShapePipeline() {
if (!CUSTOM_PIPELINE_NUMBER) {
ParticlePayloadData::CUSTOM_PIPELINE_NUMBER = render::ShapePipeline::registerCustomShapePipelineFactory(ParticlePayloadData::shapePipelineFactory);
}
}
template<typename T>
struct InterpolationData {
T start;
@ -70,9 +81,6 @@ public:
offsetof(ParticlePrimitive, uv), gpu::Stream::PER_INSTANCE);
}
void setPipeline(PipelinePointer pipeline) { _pipeline = pipeline; }
const PipelinePointer& getPipeline() const { return _pipeline; }
const Transform& getModelTransform() const { return _modelTransform; }
void setModelTransform(const Transform& modelTransform) { _modelTransform = modelTransform; }
@ -90,15 +98,16 @@ public:
bool getVisibleFlag() const { return _visibleFlag; }
void setVisibleFlag(bool visibleFlag) { _visibleFlag = visibleFlag; }
void render(RenderArgs* args) const {
assert(_pipeline);
gpu::Batch& batch = *args->_batch;
batch.setPipeline(_pipeline);
if (_texture) {
batch.setResourceTexture(0, _texture);
} else {
batch.setResourceTexture(0, DependencyManager::get<TextureCache>()->getWhiteTexture());
}
batch.setModelTransform(_modelTransform);
@ -144,30 +153,47 @@ namespace render {
}
template <>
const ShapeKey shapeGetShapeKey(const ParticlePayloadData::Pointer& payload) {
return ShapeKey::Builder().withCustom(75).build();
}
template <>
bool shapeDefineCustomShapePipeline(const ParticlePayloadData::Pointer& payload, ShapePlumber& plumber, const ShapeKey& key) {
return false;
return render::ShapeKey::Builder().withCustom(ParticlePayloadData::CUSTOM_PIPELINE_NUMBER).withTranslucent().build();
}
}
uint8_t ParticlePayloadData::CUSTOM_PIPELINE_NUMBER = 0;
std::weak_ptr<gpu::Pipeline> ParticlePayloadData::_texturedPipeline;
render::ShapePipelinePointer ParticlePayloadData::shapePipelineFactory(const render::ShapePlumber& plumber, const render::ShapeKey& key) {
auto texturedPipeline = _texturedPipeline.lock();
if (!texturedPipeline) {
auto state = std::make_shared<gpu::State>();
state->setCullMode(gpu::State::CULL_BACK);
state->setDepthTest(true, false, gpu::LESS_EQUAL);
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE,
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
PrepareStencil::testMask(*state);
auto vertShader = gpu::Shader::createVertex(std::string(textured_particle_vert));
auto fragShader = gpu::Shader::createPixel(std::string(textured_particle_frag));
auto program = gpu::Shader::createProgram(vertShader, fragShader);
_texturedPipeline = texturedPipeline = gpu::Pipeline::create(program, state);
}
return std::make_shared<render::ShapePipeline>(texturedPipeline, nullptr, nullptr, nullptr);
}
EntityItemPointer RenderableParticleEffectEntityItem::factory(const EntityItemID& entityID,
const EntityItemProperties& properties) {
auto entity = std::make_shared<RenderableParticleEffectEntityItem>(entityID);
entity->setProperties(properties);
// As we create the first ParticuleSystem entity, let s register its special shapePIpeline factory:
ParticlePayloadData::registerShapePipeline();
return entity;
}
RenderableParticleEffectEntityItem::RenderableParticleEffectEntityItem(const EntityItemID& entityItemID) :
ParticleEffectEntityItem(entityItemID) {
// lazy creation of particle system pipeline
if (!_untexturedPipeline || !_texturedPipeline) {
createPipelines();
}
}
bool RenderableParticleEffectEntityItem::addToScene(const EntityItemPointer& self,
@ -176,7 +202,6 @@ bool RenderableParticleEffectEntityItem::addToScene(const EntityItemPointer& sel
_scene = scene;
_renderItemId = _scene->allocateID();
auto particlePayloadData = std::make_shared<ParticlePayloadData>();
particlePayloadData->setPipeline(_untexturedPipeline);
auto renderPayload = std::make_shared<ParticlePayloadData::Payload>(particlePayloadData);
render::Item::Status::Getters statusGetters;
makeEntityItemStatusGetters(getThisPointer(), statusGetters);
@ -285,47 +310,14 @@ void RenderableParticleEffectEntityItem::updateRenderItem() {
if (_texture && _texture->isLoaded()) {
payload.setTexture(_texture->getGPUTexture());
payload.setPipeline(_texturedPipeline);
} else {
payload.setTexture(nullptr);
payload.setPipeline(_untexturedPipeline);
}
});
_scene->enqueueTransaction(transaction);
}
void RenderableParticleEffectEntityItem::createPipelines() {
if (!_untexturedPipeline) {
auto state = std::make_shared<gpu::State>();
state->setCullMode(gpu::State::CULL_BACK);
state->setDepthTest(true, false, gpu::LESS_EQUAL);
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE,
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
PrepareStencil::testMask(*state);
auto vertShader = gpu::Shader::createVertex(std::string(untextured_particle_vert));
auto fragShader = gpu::Shader::createPixel(std::string(untextured_particle_frag));
auto program = gpu::Shader::createProgram(vertShader, fragShader);
_untexturedPipeline = gpu::Pipeline::create(program, state);
}
if (!_texturedPipeline) {
auto state = std::make_shared<gpu::State>();
state->setCullMode(gpu::State::CULL_BACK);
state->setDepthTest(true, false, gpu::LESS_EQUAL);
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE,
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
PrepareStencil::testMask(*state);
auto vertShader = gpu::Shader::createVertex(std::string(textured_particle_vert));
auto fragShader = gpu::Shader::createPixel(std::string(textured_particle_frag));
auto program = gpu::Shader::createProgram(vertShader, fragShader);
_texturedPipeline = gpu::Pipeline::create(program, state);
}
}
void RenderableParticleEffectEntityItem::notifyBoundChanged() {
if (!render::Item::isValidID(_renderItemId)) {
return;

View file

@ -34,16 +34,13 @@ protected:
virtual void locationChanged(bool tellPhysics = true) override { EntityItem::locationChanged(tellPhysics); notifyBoundChanged(); }
virtual void dimensionsChanged() override { EntityItem::dimensionsChanged(); notifyBoundChanged(); }
void notifyBoundChanged();
void notifyBoundChanged();
void createPipelines();
render::ScenePointer _scene;
render::ItemID _renderItemId{ render::Item::INVALID_ITEM_ID };
NetworkTexturePointer _texture;
gpu::PipelinePointer _untexturedPipeline;
gpu::PipelinePointer _texturedPipeline;
};

View file

@ -9,10 +9,11 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
in vec4 _color;
in vec4 varColor;
in vec2 varTexcoord;
out vec4 outFragColor;
void main(void) {
outFragColor = _color;
outFragColor = varColor;
}

View file

@ -182,7 +182,6 @@ GLuint compileProgram(const std::vector<GLuint>& glshaders, std::string& error)
filestream.close();
}
*/
delete[] temp;
glDeleteProgram(glprogram);
return 0;

View file

@ -44,13 +44,13 @@ void renderShape(RenderArgs* args, const ShapePlumberPointer& shapeContext, cons
auto key = item.getShapeKey() | globalKey;
if (key.isValid() && !key.hasOwnPipeline()) {
args->_shapePipeline = shapeContext->pickPipeline(args, key);
if (!args->_shapePipeline) {
/* if (!args->_shapePipeline) {
if (key.isCustom()) {
if (item.defineCustomShapePipeline(*shapeContext, key)) {
args->_shapePipeline = shapeContext->pickPipeline(args, key);
}
}
}
}*/
if (args->_shapePipeline) {
args->_shapePipeline->prepareShapeItem(args, key, item);
item.render(args);
@ -119,7 +119,7 @@ void render::renderStateSortShapes(const RenderContextPointer& renderContext,
auto& bucket = sortedShapes[pipelineKey];
args->_shapePipeline = shapeContext->pickPipeline(args, pipelineKey);
if (!args->_shapePipeline) {
if (pipelineKey.isCustom()) {
/* if (pipelineKey.isCustom()) {
if (bucket.front().defineCustomShapePipeline(*shapeContext, pipelineKey)) {
args->_shapePipeline = shapeContext->pickPipeline(args, pipelineKey);
if (!args->_shapePipeline) {
@ -130,7 +130,7 @@ void render::renderStateSortShapes(const RenderContextPointer& renderContext,
} else {
continue;
}
}
}*/
continue;
}
for (auto& item : bucket) {

View file

@ -320,7 +320,6 @@ public:
virtual void render(RenderArgs* args) = 0;
virtual const ShapeKey getShapeKey() const = 0;
virtual bool defineCustomShapePipeline(ShapePlumber& plumber, const ShapeKey& key) const = 0;
virtual uint32_t fetchMetaSubItems(ItemIDs& subItems) const = 0;
@ -370,7 +369,6 @@ public:
// Shape Type Interface
const ShapeKey getShapeKey() const { return _payload->getShapeKey(); }
bool defineCustomShapePipeline(ShapePlumber& plumber, const ShapeKey& key) const { return _payload->defineCustomShapePipeline(plumber, key); }
// Meta Type Interface
uint32_t fetchMetaSubItems(ItemIDs& subItems) const { return _payload->fetchMetaSubItems(subItems); }
@ -417,7 +415,6 @@ template <class T> void payloadRender(const std::shared_ptr<T>& payloadData, Ren
// When creating a new shape payload you need to create a specialized version, or the ShapeKey will be ownPipeline,
// implying that the shape will setup its own pipeline without the use of the ShapeKey.
template <class T> const ShapeKey shapeGetShapeKey(const std::shared_ptr<T>& payloadData) { return ShapeKey::Builder::ownPipeline(); }
template <class T> bool shapeDefineCustomShapePipeline(const std::shared_ptr<T>& payloadData, ShapePlumber& plumber, const ShapeKey& key) { return false; }
// Meta Type Interface
// Meta items act as the grouping object for several sub items (typically shapes).
@ -444,7 +441,6 @@ public:
// Shape Type interface
virtual const ShapeKey getShapeKey() const override { return shapeGetShapeKey<T>(_data); }
virtual bool defineCustomShapePipeline(ShapePlumber& plumber, const ShapeKey& key) const override { return shapeDefineCustomShapePipeline<T>(_data, plumber, key); }
// Meta Type Interface
virtual uint32_t fetchMetaSubItems(ItemIDs& subItems) const override { return metaFetchMetaSubItems<T>(_data, subItems); }

View file

@ -17,6 +17,15 @@
using namespace render;
ShapePipeline::CustomFactoryMap ShapePipeline::_globalCustomFactoryMap;
ShapePipeline::CustomKey ShapePipeline::registerCustomShapePipelineFactory(CustomFactory factory) {
ShapePipeline::CustomKey custom = (ShapePipeline::CustomKey) _globalCustomFactoryMap.size() + 1;
_globalCustomFactoryMap[custom] = factory;
return custom;
}
void ShapePipeline::prepare(gpu::Batch& batch, RenderArgs* args) {
if (_batchSetter) {
_batchSetter(*this, batch, args);
@ -35,7 +44,7 @@ ShapeKey::Filter::Builder::Builder() {
_mask.set(INVALID);
}
void ShapePlumber::addPipelineHelper(const Filter& filter, ShapeKey key, int bit, const PipelinePointer& pipeline) {
void ShapePlumber::addPipelineHelper(const Filter& filter, ShapeKey key, int bit, const PipelinePointer& pipeline) const {
// Iterate over all keys
if (bit < (int)ShapeKey::FlagBit::NUM_FLAGS) {
addPipelineHelper(filter, key, bit + 1, pipeline);
@ -110,10 +119,22 @@ const ShapePipelinePointer ShapePlumber::pickPipeline(RenderArgs* args, const Ke
const auto& pipelineIterator = _pipelineMap.find(key);
if (pipelineIterator == _pipelineMap.end()) {
// The first time we can't find a pipeline, we should log it
// The first time we can't find a pipeline, we should try things to solve that
if (_missingKeys.find(key) == _missingKeys.end()) {
_missingKeys.insert(key);
qCDebug(renderlogging) << "Couldn't find a pipeline for" << key;
if (key.isCustom()) {
auto factoryIt = ShapePipeline::_globalCustomFactoryMap.find(key.getCustom());
if ((factoryIt != ShapePipeline::_globalCustomFactoryMap.end()) && (factoryIt)->second) {
// found a factory for the custom key, can now generate a shape pipeline for this case:
addPipelineHelper(Filter(key), key, 0, (factoryIt)->second(*this, key));
return pickPipeline(args, key);
} else {
qCDebug(renderlogging) << "ShapePlumber::Couldn't find a custom pipeline factory for " << key.getCustom() << " key is: " << key;
}
}
_missingKeys.insert(key);
qCDebug(renderlogging) << "ShapePlumber::Couldn't find a pipeline for" << key;
}
return PipelinePointer(nullptr);
}

View file

@ -285,6 +285,15 @@ protected:
BatchSetter _batchSetter;
ItemSetter _itemSetter;
public:
using CustomKey = uint8_t;
using CustomFactory = std::function<std::shared_ptr<ShapePipeline> (const ShapePlumber& plumber, const ShapeKey& key)>;
using CustomFactoryMap = std::map<CustomKey, CustomFactory>;
static CustomFactoryMap _globalCustomFactoryMap;
static CustomKey registerCustomShapePipelineFactory(CustomFactory factory);
};
using ShapePipelinePointer = std::shared_ptr<ShapePipeline>;
@ -309,13 +318,14 @@ public:
const PipelinePointer pickPipeline(RenderArgs* args, const Key& key) const;
protected:
void addPipelineHelper(const Filter& filter, Key key, int bit, const PipelinePointer& pipeline);
PipelineMap _pipelineMap;
void addPipelineHelper(const Filter& filter, Key key, int bit, const PipelinePointer& pipeline) const;
mutable PipelineMap _pipelineMap;
private:
mutable std::unordered_set<Key, Key::Hash, Key::KeyEqual> _missingKeys;
};
using ShapePlumberPointer = std::shared_ptr<ShapePlumber>;
}