Constrain procedural updates to render thread

This commit is contained in:
Zach Pomerantz 2016-03-09 17:33:19 -08:00
parent 40fbe2d1e5
commit 4022474b48
2 changed files with 132 additions and 67 deletions

View file

@ -69,74 +69,108 @@ Procedural::Procedural(const QString& userDataJson) {
}
void Procedural::parse(const QString& userDataJson) {
_enabled = false;
auto proceduralData = getProceduralData(userDataJson);
if (proceduralData.isObject()) {
parse(proceduralData.toObject());
}
// Instead of parsing, prep for a parse on the rendering thread
// This will be called by Procedural::ready
_proceduralData = proceduralData.toObject();
_proceduralDataDirty = true;
}
void Procedural::parse(const QJsonObject& proceduralData) {
// grab the version number
{
auto version = proceduralData[VERSION_KEY];
if (version.isDouble()) {
_version = (uint8_t)(floor(version.toDouble()));
}
bool Procedural::parseVersion(const QJsonValue& version) {
if (version.isDouble()) {
_version = (uint8_t)(floor(version.toDouble()));
} else {
// All unversioned shaders default to V1
_version = 1;
}
return (_version == 1 || _version == 2);
}
bool Procedural::parseUrl(const QUrl& shaderUrl) {
if (!shaderUrl.isValid()) {
qWarning() << "Invalid shader URL: " << shaderUrl;
return false;
}
// Get the path to the shader
{
QString shaderUrl = proceduralData[URL_KEY].toString();
shaderUrl = ResourceManager::normalizeURL(shaderUrl);
_shaderUrl = QUrl(shaderUrl);
if (!_shaderUrl.isValid()) {
qWarning() << "Invalid shader URL: " << shaderUrl;
return;
}
if (_shaderUrl.isLocalFile()) {
_shaderPath = _shaderUrl.toLocalFile();
qDebug() << "Shader path: " << _shaderPath;
if (!QFile(_shaderPath).exists()) {
return;
}
} else {
qDebug() << "Shader url: " << _shaderUrl;
_networkShader = ShaderCache::instance().getShader(_shaderUrl);
}
if (_shaderUrl == shaderUrl) {
return true;
}
// Grab any custom uniforms
{
auto uniforms = proceduralData[UNIFORMS_KEY];
if (uniforms.isObject()) {
_parsedUniforms = uniforms.toObject();
_shaderUrl = shaderUrl;
if (_shaderUrl.isLocalFile()) {
_shaderPath = _shaderUrl.toLocalFile();
qDebug() << "Shader path: " << _shaderPath;
if (!QFile(_shaderPath).exists()) {
return false;;
}
} else {
qDebug() << "Shader url: " << _shaderUrl;
_networkShader = ShaderCache::instance().getShader(_shaderUrl);
}
// Grab any textures
{
auto channels = proceduralData[CHANNELS_KEY];
if (channels.isArray()) {
auto textureCache = DependencyManager::get<TextureCache>();
_parsedChannels = channels.toArray();
size_t channelCount = std::min(MAX_PROCEDURAL_TEXTURE_CHANNELS, (size_t)_parsedChannels.size());
for (size_t i = 0; i < channelCount; ++i) {
QString url = _parsedChannels.at((int)i).toString();
_channels[i] = textureCache->getTexture(QUrl(url));
}
}
return true;
}
bool Procedural::parseUniforms(const QJsonObject& uniforms) {
if (_parsedUniforms != uniforms) {
_parsedUniforms = uniforms;
_uniformsDirty = true;
}
return true;
}
bool Procedural::parseTextures(const QJsonArray& channels) {
if (_parsedChannels != channels) {
_parsedChannels = channels;
auto textureCache = DependencyManager::get<TextureCache>();
size_t channelCount = std::min(MAX_PROCEDURAL_TEXTURE_CHANNELS, (size_t)_parsedChannels.size());
for (size_t i = 0; i < channelCount; ++i) {
QString url = _parsedChannels.at((int)i).toString();
_channels[i] = textureCache->getTexture(QUrl(url));
}
_channelsDirty = true;
}
return true;
}
void Procedural::parse(QJsonObject proceduralData) {
_enabled = false;
if (proceduralData.isEmpty()) {
return;
}
auto version = proceduralData[VERSION_KEY];
auto shaderUrl = proceduralData[URL_KEY].toString();
shaderUrl = ResourceManager::normalizeURL(shaderUrl);
auto uniforms = proceduralData[UNIFORMS_KEY].toObject();
auto channels = proceduralData[CHANNELS_KEY].toArray();
if (parseVersion(proceduralData[VERSION_KEY]) &&
parseUrl(shaderUrl) &&
parseUniforms(uniforms) &&
parseTextures(channels)) {
_enabled = true;
}
_enabled = true;
}
bool Procedural::ready() {
// Load any changes to the procedural
if (_proceduralDataDirty) {
parse(_proceduralData);
_proceduralDataDirty = false;
}
if (!_enabled) {
return false;
}
// Do we have a network or local shader
// Do we have a network or local shader, and if so, is it loaded?
if (_shaderPath.isEmpty() && (!_networkShader || !_networkShader->isLoaded())) {
return false;
}
@ -160,15 +194,14 @@ void Procedural::prepare(gpu::Batch& batch, const glm::vec3& position, const glm
QFile file(_shaderPath);
file.open(QIODevice::ReadOnly);
_shaderSource = QTextStream(&file).readAll();
_pipelineDirty = true;
_shaderDirty = true;
_shaderModified = lastModified;
}
} else if (_networkShader && _networkShader->isLoaded()) {
_shaderSource = _networkShader->_source;
}
if (!_pipeline || _pipelineDirty) {
_pipelineDirty = true;
if (!_pipeline || _shaderDirty) {
if (!_vertexShader) {
_vertexShader = gpu::Shader::createVertex(_vertexSource);
}
@ -214,11 +247,15 @@ void Procedural::prepare(gpu::Batch& batch, const glm::vec3& position, const glm
batch.setPipeline(_pipeline);
if (_pipelineDirty) {
_pipelineDirty = false;
if (_shaderDirty || _uniformsDirty) {
setupUniforms();
}
if (_shaderDirty || _uniformsDirty || _channelsDirty) {
setupChannels(_shaderDirty || _uniformsDirty);
}
_shaderDirty = _uniformsDirty = _channelsDirty = false;
for (auto lambda : _uniforms) {
lambda(batch);
@ -359,8 +396,14 @@ void Procedural::setupUniforms() {
batch._glUniform(_standardUniformSlots[POSITION], _entityPosition);
});
}
}
void Procedural::setupChannels(bool shouldCreate) {
if (gpu::Shader::INVALID_LOCATION != _standardUniformSlots[CHANNEL_RESOLUTION]) {
if (!shouldCreate) {
// Instead of modifying the last element, just remove and recreate it.
_uniforms.pop_back();
}
_uniforms.push_back([=](gpu::Batch& batch) {
vec3 channelSizes[MAX_PROCEDURAL_TEXTURE_CHANNELS];
for (size_t i = 0; i < MAX_PROCEDURAL_TEXTURE_CHANNELS; ++i) {

View file

@ -28,27 +28,24 @@ const size_t MAX_PROCEDURAL_TEXTURE_CHANNELS{ 4 };
// FIXME better encapsulation
// FIXME better mechanism for extending to things rendered using shaders other than simple.slv
struct Procedural {
public:
static QJsonValue getProceduralData(const QString& proceduralJson);
Procedural(const QString& userDataJson);
void parse(const QString& userDataJson);
void parse(const QJsonObject&);
bool ready();
void prepare(gpu::Batch& batch, const glm::vec3& position, const glm::vec3& size);
void setupUniforms();
glm::vec4 getColor(const glm::vec4& entityColor);
bool _enabled{ false };
uint8_t _version{ 1 };
bool _enabled { false };
uint8_t _version { 1 };
std::string _vertexSource;
std::string _fragmentSource;
QString _shaderSource;
QString _shaderPath;
QUrl _shaderUrl;
quint64 _shaderModified{ 0 };
bool _pipelineDirty{ true };
gpu::StatePointer _state;
enum StandardUniforms {
DATE,
@ -60,23 +57,48 @@ struct Procedural {
NUM_STANDARD_UNIFORMS
};
int32_t _standardUniformSlots[NUM_STANDARD_UNIFORMS];
protected:
// Procedural metadata
uint64_t _start{ 0 };
int32_t _frameCount{ 0 };
// Rendering object descriptions, from userData
QJsonObject _proceduralData;
QString _shaderSource;
QString _shaderPath;
QUrl _shaderUrl;
quint64 _shaderModified { 0 };
NetworkShaderPointer _networkShader;
QJsonObject _parsedUniforms;
QJsonArray _parsedChannels;
bool _proceduralDataDirty { true };
bool _shaderDirty { true };
bool _uniformsDirty { true };
bool _channelsDirty { true };
// Rendering objects
UniformLambdas _uniforms;
int32_t _standardUniformSlots[NUM_STANDARD_UNIFORMS];
NetworkTexturePointer _channels[MAX_PROCEDURAL_TEXTURE_CHANNELS];
gpu::PipelinePointer _pipeline;
gpu::ShaderPointer _vertexShader;
gpu::ShaderPointer _fragmentShader;
gpu::ShaderPointer _shader;
gpu::StatePointer _state;
// Entity metadata
glm::vec3 _entityDimensions;
glm::vec3 _entityPosition;
private:
// This should only be called from the render thread, as it shares data with Procedural::prepare
void parse(QJsonObject);
bool parseVersion(const QJsonValue& version);
bool parseUrl(const QUrl& url);
bool parseUniforms(const QJsonObject& uniforms);
bool parseTextures(const QJsonArray& channels);
void setupUniforms();
void setupChannels(bool shouldCreate);
};
#endif