overte-JulianGro/libraries/render/src/render/DrawTask.cpp
2015-06-17 15:52:11 +02:00

575 lines
18 KiB
C++
Executable file

//
// DrawTask.cpp
// render/src/render
//
// Created by Sam Gateau on 5/21/15.
// Copyright 20154 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <algorithm>
#include <assert.h>
#include "DrawTask.h"
#include <PerfStat.h>
#include "gpu/Batch.h"
#include "gpu/Context.h"
#include "ViewFrustum.h"
#include "RenderArgs.h"
using namespace render;
DrawSceneTask::DrawSceneTask() : Task() {
_jobs.push_back(Job(new Job::Model<DrawOpaque>()));
_jobs.push_back(Job(new Job::Model<DrawLight>()));
_jobs.push_back(Job(new Job::Model<DrawTransparent>()));
_jobs.push_back(Job(new Job::Model<ResetGLState>()));
}
DrawSceneTask::~DrawSceneTask() {
}
void DrawSceneTask::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {
// sanity checks
assert(sceneContext);
if (!sceneContext->_scene) {
return;
}
// Is it possible that we render without a viewFrustum ?
if (!(renderContext->args && renderContext->args->_viewFrustum)) {
return;
}
for (auto job : _jobs) {
job.run(sceneContext, renderContext);
}
};
Job::~Job() {
}
void render::cullItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ItemIDsBounds& outItems) {
PerformanceTimer perfTimer("cullItems");
assert(renderContext->args);
assert(renderContext->args->_viewFrustum);
auto& scene = sceneContext->_scene;
RenderArgs* args = renderContext->args;
auto renderDetails = renderContext->args->_details._item;
renderDetails->_considered += inItems.size();
// Culling / LOD
for (auto item : inItems) {
if (item.bounds.isNull()) {
outItems.emplace_back(item); // One more Item to render
continue;
}
// TODO: some entity types (like lights) might want to be rendered even
// when they are outside of the view frustum...
bool outOfView;
{
PerformanceTimer perfTimer("boxInFrustum");
outOfView = args->_viewFrustum->boxInFrustum(item.bounds) == ViewFrustum::OUTSIDE;
}
if (!outOfView) {
bool bigEnoughToRender;
{
PerformanceTimer perfTimer("shouldRender");
bigEnoughToRender = (args->_shouldRender) ? args->_shouldRender(args, item.bounds) : true;
}
if (bigEnoughToRender) {
outItems.emplace_back(item); // One more Item to render
} else {
renderDetails->_tooSmall++;
}
} else {
renderDetails->_outOfView++;
}
}
renderDetails->_rendered += outItems.size();
}
void FetchItems::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, ItemIDsBounds& outItems) {
PerformanceTimer perfTimer("FetchItems::run");
auto& scene = sceneContext->_scene;
auto& items = scene->getMasterBucket().at(_filter);
auto& renderDetails = renderContext->args->_details;
outItems.clear();
outItems.reserve(items.size());
for (auto id : items) {
auto& item = scene->getItem(id);
/* AABox bound;
{
// PerformanceTimer perfTimer("getBound");
bound = item.getBound();
}*/
outItems.emplace_back(ItemIDAndBounds(id, item.getBound()));
}
}
void CullItems::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ItemIDsBounds& outItems) {
PerformanceTimer perfTimer("CullItems::run");
outItems.clear();
outItems.reserve(inItems.size());
cullItems(sceneContext, renderContext, inItems, outItems);
}
struct ItemBound {
float _centerDepth = 0.0f;
float _nearDepth = 0.0f;
float _farDepth = 0.0f;
ItemID _id = 0;
ItemBound() {}
ItemBound(float centerDepth, float nearDepth, float farDepth, ItemID id) : _centerDepth(centerDepth), _nearDepth(nearDepth), _farDepth(farDepth), _id(id) {}
};
struct FrontToBackSort {
bool operator() (const ItemBound& left, const ItemBound& right) {
return (left._centerDepth < right._centerDepth);
}
};
struct BackToFrontSort {
bool operator() (const ItemBound& left, const ItemBound& right) {
return (left._centerDepth > right._centerDepth);
}
};
void render::depthSortItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, bool frontToBack, const ItemIDsBounds& inItems, ItemIDsBounds& outItems) {
PerformanceTimer perfTimer("depthSortItems");
assert(renderContext->args);
assert(renderContext->args->_viewFrustum);
auto& scene = sceneContext->_scene;
RenderArgs* args = renderContext->args;
// Allocate and simply copy
outItems.clear();
outItems.reserve(inItems.size());
// Make a local dataset of the center distance and closest point distance
std::vector<ItemBound> itemBounds;
itemBounds.reserve(outItems.size());
for (auto itemDetails : inItems) {
auto item = scene->getItem(itemDetails.id);
auto bound = itemDetails.bounds; // item.getBound();
float distance = args->_viewFrustum->distanceToCamera(bound.calcCenter());
itemBounds.emplace_back(ItemBound(distance, distance, distance, itemDetails.id));
}
// sort against Z
if (frontToBack) {
FrontToBackSort frontToBackSort;
std::sort (itemBounds.begin(), itemBounds.end(), frontToBackSort);
} else {
BackToFrontSort backToFrontSort;
std::sort (itemBounds.begin(), itemBounds.end(), backToFrontSort);
}
// FInally once sorted result to a list of itemID
for (auto& itemBound : itemBounds) {
outItems.emplace_back(itemBound._id);
}
}
void DepthSortItems::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ItemIDsBounds& outItems) {
outItems.clear();
outItems.reserve(inItems.size());
depthSortItems(sceneContext, renderContext, _frontToBack, inItems, outItems);
}
void render::renderItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, int maxDrawnItems) {
PerformanceTimer perfTimer("renderItems");
auto& scene = sceneContext->_scene;
RenderArgs* args = renderContext->args;
// render
if ((maxDrawnItems < 0) || (maxDrawnItems > (int) inItems.size())) {
for (auto itemDetails : inItems) {
auto item = scene->getItem(itemDetails.id);
item.render(args);
}
} else {
int numItems = 0;
for (auto itemDetails : inItems) {
auto item = scene->getItem(itemDetails.id);
if (numItems + 1 >= maxDrawnItems) {
item.render(args);
return;
}
item.render(args);
numItems++;
if (numItems >= maxDrawnItems) {
return;
}
}
}
}
void addClearStateCommands(gpu::Batch& batch) {
batch._glDepthMask(true);
batch._glDepthFunc(GL_LESS);
batch._glDisable(GL_CULL_FACE);
batch._glActiveTexture(GL_TEXTURE0 + 1);
batch._glBindTexture(GL_TEXTURE_2D, 0);
batch._glActiveTexture(GL_TEXTURE0 + 2);
batch._glBindTexture(GL_TEXTURE_2D, 0);
batch._glActiveTexture(GL_TEXTURE0 + 3);
batch._glBindTexture(GL_TEXTURE_2D, 0);
batch._glActiveTexture(GL_TEXTURE0);
batch._glBindTexture(GL_TEXTURE_2D, 0);
// deactivate vertex arrays after drawing
batch._glDisableClientState(GL_NORMAL_ARRAY);
batch._glDisableClientState(GL_VERTEX_ARRAY);
batch._glDisableClientState(GL_TEXTURE_COORD_ARRAY);
batch._glDisableClientState(GL_COLOR_ARRAY);
batch._glDisableVertexAttribArray(gpu::Stream::TANGENT);
batch._glDisableVertexAttribArray(gpu::Stream::SKIN_CLUSTER_INDEX);
batch._glDisableVertexAttribArray(gpu::Stream::SKIN_CLUSTER_WEIGHT);
// bind with 0 to switch back to normal operation
batch._glBindBuffer(GL_ARRAY_BUFFER, 0);
batch._glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
batch._glBindTexture(GL_TEXTURE_2D, 0);
// Back to no program
batch._glUseProgram(0);
}
void ResetGLState::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {
gpu::Batch theBatch;
addClearStateCommands(theBatch);
assert(renderContext->args);
renderContext->args->_context->render(theBatch);
}
template <> void render::jobRun(DrawOpaque& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {
PerformanceTimer perfTimer("DrawOpaque");
assert(renderContext->args);
assert(renderContext->args->_viewFrustum);
// render opaques
auto& scene = sceneContext->_scene;
auto& items = scene->getMasterBucket().at(ItemFilter::Builder::opaqueShape());
auto& renderDetails = renderContext->args->_details;
ItemIDsBounds inItems;
inItems.reserve(items.size());
for (auto id : items) {
auto item = scene->getItem(id);
AABox bound;
{
PerformanceTimer perfTimer("getBound");
bound = item.getBound();
}
inItems.emplace_back(ItemIDAndBounds(id, bound));
}
ItemIDsBounds& renderedItems = inItems;
renderContext->_numFeedOpaqueItems = renderedItems.size();
ItemIDsBounds culledItems;
culledItems.reserve(inItems.size());
if (renderContext->_cullOpaque) {
renderDetails.pointTo(RenderDetails::OPAQUE_ITEM);
cullItems(sceneContext, renderContext, renderedItems, culledItems);
renderDetails.pointTo(RenderDetails::OTHER_ITEM);
renderedItems = culledItems;
}
renderContext->_numDrawnOpaqueItems = renderedItems.size();
ItemIDsBounds sortedItems;
sortedItems.reserve(culledItems.size());
if (renderContext->_sortOpaque) {
depthSortItems(sceneContext, renderContext, true, renderedItems, sortedItems); // Sort Front to back opaque items!
renderedItems = sortedItems;
}
if (renderContext->_renderOpaque) {
RenderArgs* args = renderContext->args;
gpu::Batch batch;
args->_batch = &batch;
glm::mat4 projMat;
Transform viewMat;
args->_viewFrustum->evalProjectionMatrix(projMat);
args->_viewFrustum->evalViewTransform(viewMat);
if (args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) {
viewMat.postScale(glm::vec3(-1.0f, 1.0f, 1.0f));
}
batch.setProjectionTransform(projMat);
batch.setViewTransform(viewMat);
{
GLenum buffers[3];
int bufferCount = 0;
buffers[bufferCount++] = GL_COLOR_ATTACHMENT0;
buffers[bufferCount++] = GL_COLOR_ATTACHMENT1;
buffers[bufferCount++] = GL_COLOR_ATTACHMENT2;
batch._glDrawBuffers(bufferCount, buffers);
}
renderItems(sceneContext, renderContext, renderedItems, renderContext->_maxDrawnOpaqueItems);
args->_context->render((*args->_batch));
args->_batch = nullptr;
}
}
template <> void render::jobRun(DrawTransparent& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {
PerformanceTimer perfTimer("DrawTransparent");
assert(renderContext->args);
assert(renderContext->args->_viewFrustum);
// render transparents
auto& scene = sceneContext->_scene;
auto& items = scene->getMasterBucket().at(ItemFilter::Builder::transparentShape());
auto& renderDetails = renderContext->args->_details;
ItemIDsBounds inItems;
inItems.reserve(items.size());
for (auto id : items) {
auto item = scene->getItem(id);
AABox bound;
{
PerformanceTimer perfTimer("getBound");
bound = item.getBound();
}
inItems.emplace_back(ItemIDAndBounds(id, bound));
}
ItemIDsBounds& renderedItems = inItems;
renderContext->_numFeedTransparentItems = renderedItems.size();
ItemIDsBounds culledItems;
culledItems.reserve(inItems.size());
if (renderContext->_cullTransparent) {
renderDetails.pointTo(RenderDetails::TRANSLUCENT_ITEM);
cullItems(sceneContext, renderContext, inItems, culledItems);
renderDetails.pointTo(RenderDetails::OTHER_ITEM);
renderedItems = culledItems;
}
renderContext->_numDrawnTransparentItems = renderedItems.size();
ItemIDsBounds sortedItems;
sortedItems.reserve(culledItems.size());
if (renderContext->_sortTransparent) {
depthSortItems(sceneContext, renderContext, false, renderedItems, sortedItems); // Sort Back to front transparent items!
renderedItems = sortedItems;
}
if (renderContext->_renderTransparent) {
RenderArgs* args = renderContext->args;
gpu::Batch batch;
args->_batch = &batch;
glm::mat4 projMat;
Transform viewMat;
args->_viewFrustum->evalProjectionMatrix(projMat);
args->_viewFrustum->evalViewTransform(viewMat);
if (args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) {
viewMat.postScale(glm::vec3(-1.0f, 1.0f, 1.0f));
}
batch.setProjectionTransform(projMat);
batch.setViewTransform(viewMat);
const float MOSTLY_OPAQUE_THRESHOLD = 0.75f;
const float TRANSPARENT_ALPHA_THRESHOLD = 0.0f;
// render translucent meshes afterwards
{
GLenum buffers[2];
int bufferCount = 0;
buffers[bufferCount++] = GL_COLOR_ATTACHMENT1;
buffers[bufferCount++] = GL_COLOR_ATTACHMENT2;
batch._glDrawBuffers(bufferCount, buffers);
args->_alphaThreshold = MOSTLY_OPAQUE_THRESHOLD;
}
renderItems(sceneContext, renderContext, renderedItems, renderContext->_maxDrawnTransparentItems);
{
GLenum buffers[3];
int bufferCount = 0;
buffers[bufferCount++] = GL_COLOR_ATTACHMENT0;
batch._glDrawBuffers(bufferCount, buffers);
args->_alphaThreshold = TRANSPARENT_ALPHA_THRESHOLD;
}
renderItems(sceneContext, renderContext, renderedItems, renderContext->_maxDrawnTransparentItems);
args->_context->render((*args->_batch));
args->_batch = nullptr;
}
}
void DrawLight::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {
PerformanceTimer perfTimer("DrawLight");
assert(renderContext->args);
assert(renderContext->args->_viewFrustum);
// render lights
auto& scene = sceneContext->_scene;
auto& items = scene->getMasterBucket().at(ItemFilter::Builder::light());
ItemIDsBounds inItems;
inItems.reserve(items.size());
for (auto id : items) {
auto item = scene->getItem(id);
AABox bound;
{
PerformanceTimer perfTimer("getBound");
bound = item.getBound();
}
inItems.emplace_back(ItemIDAndBounds(id, bound));
}
ItemIDsBounds culledItems;
culledItems.reserve(inItems.size());
cullItems(sceneContext, renderContext, inItems, culledItems);
RenderArgs* args = renderContext->args;
gpu::Batch theBatch;
args->_batch = &theBatch;
renderItems(sceneContext, renderContext, culledItems);
args->_context->render((*args->_batch));
args->_batch = nullptr;
}
void DrawBackground::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {
PerformanceTimer perfTimer("DrawBackground");
assert(renderContext->args);
assert(renderContext->args->_viewFrustum);
// render backgrounds
auto& scene = sceneContext->_scene;
auto& items = scene->getMasterBucket().at(ItemFilter::Builder::background());
ItemIDsBounds inItems;
inItems.reserve(items.size());
for (auto id : items) {
inItems.emplace_back(id);
}
RenderArgs* args = renderContext->args;
gpu::Batch batch;
args->_batch = &batch;
glm::mat4 projMat;
Transform viewMat;
args->_viewFrustum->evalProjectionMatrix(projMat);
args->_viewFrustum->evalViewTransform(viewMat);
if (args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) {
viewMat.postScale(glm::vec3(-1.0f, 1.0f, 1.0f));
}
batch.setProjectionTransform(projMat);
batch.setViewTransform(viewMat);
renderItems(sceneContext, renderContext, inItems);
args->_context->render((*args->_batch));
args->_batch = nullptr;
// Force the context sync
args->_context->syncCache();
}
void DrawPostLayered::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {
PerformanceTimer perfTimer("DrawPostLayered");
assert(renderContext->args);
assert(renderContext->args->_viewFrustum);
// render backgrounds
auto& scene = sceneContext->_scene;
auto& items = scene->getMasterBucket().at(ItemFilter::Builder::opaqueShape().withLayered());
ItemIDsBounds inItems;
inItems.reserve(items.size());
for (auto id : items) {
auto& item = scene->getItem(id);
if (item.getKey().isVisible() && (item.getLayer() > 0)) {
auto item = scene->getItem(id);
AABox bound;
{
PerformanceTimer perfTimer("getBound");
bound = item.getBound();
}
inItems.emplace_back(ItemIDAndBounds(id, bound));
}
}
if (inItems.empty()) {
return;
}
RenderArgs* args = renderContext->args;
gpu::Batch batch;
args->_batch = &batch;
glm::mat4 projMat;
Transform viewMat;
args->_viewFrustum->evalProjectionMatrix(projMat);
args->_viewFrustum->evalViewTransform(viewMat);
if (args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) {
viewMat.postScale(glm::vec3(-1.0f, 1.0f, 1.0f));
}
batch.setProjectionTransform(projMat);
batch.setViewTransform(viewMat);
batch.clearFramebuffer(gpu::Framebuffer::BUFFER_DEPTH, glm::vec4(), 1.f, 0);
renderItems(sceneContext, renderContext, inItems);
// Force the context sync
args->_context->syncCache();
args->_context->render((*args->_batch));
args->_batch = nullptr;
}
void ItemMaterialBucketMap::insert(const ItemID& id, const model::MaterialKey& key) {
// Insert the itemID in every bucket where it filters true
for (auto& bucket : (*this)) {
if (bucket.first.test(key)) {
bucket.second.push_back(id);
}
}
}
void ItemMaterialBucketMap::allocateStandardMaterialBuckets() {
(*this)[model::MaterialFilter::Builder::opaqueDiffuse()];
}