diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b7db467153..c3e7e9f458 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -875,6 +875,30 @@ void Application::paintGL() { PerformanceWarning warn(showWarnings, "Application::paintGL()"); resizeGL(); + // Before anything else, let's sync up the gpuContext with the true glcontext used in case anything happened + renderArgs._context->syncCache(); + + if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { + auto primaryFbo = DependencyManager::get()->getPrimaryFramebufferDepthColor(); + + renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE; + renderRearViewMirror(&renderArgs, _mirrorViewRect); + renderArgs._renderMode = RenderArgs::DEFAULT_RENDER_MODE; + + { + float ratio = ((float)QApplication::desktop()->windowHandle()->devicePixelRatio() * getRenderResolutionScale()); + auto mirrorViewport = glm::ivec4(0, 0, _mirrorViewRect.width() * ratio, _mirrorViewRect.height() * ratio); + auto mirrorViewportDest = mirrorViewport; + + auto selfieFbo = DependencyManager::get()->getSelfieFramebuffer(); + gpu::Batch batch; + batch.setFramebuffer(selfieFbo); + batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, glm::vec4(0.0f, 0.0f, 0.0f, 0.0f)); + batch.blit(primaryFbo, mirrorViewport, selfieFbo, mirrorViewportDest); + batch.setFramebuffer(nullptr); + renderArgs._context->render(batch); + } + } { PerformanceTimer perfTimer("renderOverlay"); @@ -892,6 +916,9 @@ void Application::paintGL() { Application::getInstance()->cameraMenuChanged(); } + // The render mode is default or mirror if the camera is in mirror mode, assigned further below + renderArgs._renderMode = RenderArgs::DEFAULT_RENDER_MODE; + if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) { // Always use the default eye position, not the actual head eye position. // Using the latter will cause the camera to wobble with idle animations, @@ -928,6 +955,7 @@ void Application::paintGL() { glm::vec3(0, _raiseMirror * _myAvatar->getScale(), 0) + (_myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, _rotateMirror, 0.0f))) * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror); + renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE; } // Update camera position @@ -935,12 +963,6 @@ void Application::paintGL() { _myCamera.update(1.0f / _fps); } - // Sync up the View Furstum with the camera - loadViewFrustum(_myCamera, _viewFrustum); - - - renderArgs._renderMode = RenderArgs::DEFAULT_RENDER_MODE; - if (OculusManager::isConnected()) { //When in mirror mode, use camera rotation. Otherwise, use body rotation if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { @@ -952,47 +974,28 @@ void Application::paintGL() { TV3DManager::display(&renderArgs, _myCamera); } else { PROFILE_RANGE(__FUNCTION__ "/mainRender"); + // Viewport is assigned to the size of the framebuffer + QSize size = DependencyManager::get()->getFrameBufferSize(); + renderArgs._viewport = glm::ivec4(0, 0, size.width(), size.height()); - { - gpu::Batch batch; - auto primaryFbo = DependencyManager::get()->getPrimaryFramebuffer(); - batch.setFramebuffer(primaryFbo); - // clear the normal and specular buffers - batch.clearFramebuffer( - gpu::Framebuffer::BUFFER_COLOR0 | - gpu::Framebuffer::BUFFER_COLOR1 | - gpu::Framebuffer::BUFFER_COLOR2 | - gpu::Framebuffer::BUFFER_DEPTH, - vec4(vec3(0), 1), 1.0, 0.0); - - // Viewport is assigned to the size of the framebuffer - QSize size = DependencyManager::get()->getFrameBufferSize(); - renderArgs._viewport = glm::ivec4(0, 0, size.width(), size.height()); - batch.setViewportTransform(renderArgs._viewport); - renderArgs._context->render(batch); - } displaySide(&renderArgs, _myCamera); - if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { - renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE; - renderRearViewMirror(&renderArgs, _mirrorViewRect); - renderArgs._renderMode = RenderArgs::NORMAL_RENDER_MODE; - } - { auto geometryCache = DependencyManager::get(); - auto primaryFbo = DependencyManager::get()->getPrimaryFramebuffer(); + auto primaryFbo = DependencyManager::get()->getPrimaryFramebufferDepthColor(); gpu::Batch batch; batch.blit(primaryFbo, glm::ivec4(0, 0, _renderResolution.x, _renderResolution.y), nullptr, glm::ivec4(0, 0, _glWidget->getDeviceSize().width(), _glWidget->getDeviceSize().height())); + + batch.setFramebuffer(nullptr); + renderArgs._context->render(batch); } _compositor.displayOverlayTexture(&renderArgs); } - - + if (!OculusManager::isConnected() || OculusManager::allowSwap()) { PROFILE_RANGE(__FUNCTION__ "/bufferSwap"); _glWidget->swapBuffers(); @@ -1002,6 +1005,7 @@ void Application::paintGL() { OculusManager::endFrameTiming(); } _frameCount++; + _numFramesSinceLastResize++; Stats::getInstance()->setRenderDetails(renderArgs._details); } @@ -1055,6 +1059,7 @@ void Application::resizeGL() { } if (_renderResolution != toGlm(renderSize)) { + _numFramesSinceLastResize = 0; _renderResolution = toGlm(renderSize); DependencyManager::get()->setFrameBufferSize(renderSize); @@ -1068,7 +1073,6 @@ void Application::resizeGL() { auto canvasSize = _glWidget->size(); offscreenUi->resize(canvasSize); _glWidget->makeCurrent(); - } bool Application::importSVOFromURL(const QString& urlString) { @@ -2975,25 +2979,19 @@ PickRay Application::computePickRay(float x, float y) const { } QImage Application::renderAvatarBillboard(RenderArgs* renderArgs) { - auto primaryFramebuffer = DependencyManager::get()->getPrimaryFramebuffer(); - glBindFramebuffer(GL_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(primaryFramebuffer)); - - // clear the alpha channel so the background is transparent - glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE); - glClearColor(0.0, 0.0, 0.0, 0.0); - glClear(GL_COLOR_BUFFER_BIT); - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE); const int BILLBOARD_SIZE = 64; - // TODO: Pass a RenderArgs to renderAvatarBillboard - renderRearViewMirror(renderArgs, QRect(0, _glWidget->getDeviceHeight() - BILLBOARD_SIZE, - BILLBOARD_SIZE, BILLBOARD_SIZE), - true); - QImage image(BILLBOARD_SIZE, BILLBOARD_SIZE, QImage::Format_ARGB32); - glReadPixels(0, 0, BILLBOARD_SIZE, BILLBOARD_SIZE, GL_BGRA, GL_UNSIGNED_BYTE, image.bits()); - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - glBindFramebuffer(GL_FRAMEBUFFER, 0); + // Need to make sure the gl context is current here + _glWidget->makeCurrent(); + + renderArgs->_renderMode = RenderArgs::DEFAULT_RENDER_MODE; + renderRearViewMirror(renderArgs, QRect(0, 0, BILLBOARD_SIZE, BILLBOARD_SIZE), true); + + auto primaryFbo = DependencyManager::get()->getPrimaryFramebufferDepthColor(); + QImage image(BILLBOARD_SIZE, BILLBOARD_SIZE, QImage::Format_ARGB32); + renderArgs->_context->downloadFramebuffer(primaryFbo, glm::ivec4(0, 0, BILLBOARD_SIZE, BILLBOARD_SIZE), image); + return image; } @@ -3407,35 +3405,23 @@ void Application::renderRearViewMirror(RenderArgs* renderArgs, const QRect& regi gpu::Vec4i viewport; if (billboard) { QSize size = DependencyManager::get()->getFrameBufferSize(); - viewport = gpu::Vec4i(region.x(), size.height() - region.y() - region.height(), region.width(), region.height()); + viewport = gpu::Vec4i(0, 0, region.width(), region.height()); } else { // if not rendering the billboard, the region is in device independent coordinates; must convert to device QSize size = DependencyManager::get()->getFrameBufferSize(); float ratio = (float)QApplication::desktop()->windowHandle()->devicePixelRatio() * getRenderResolutionScale(); - int x = region.x() * ratio, y = region.y() * ratio, width = region.width() * ratio, height = region.height() * ratio; - viewport = gpu::Vec4i(x, size.height() - y - height, width, height); + int x = region.x() * ratio; + int y = region.y() * ratio; + int width = region.width() * ratio; + int height = region.height() * ratio; + viewport = gpu::Vec4i(0, 0, width, height); } renderArgs->_viewport = viewport; - { - gpu::Batch batch; - batch.setViewportTransform(viewport); - batch.setStateScissorRect(viewport); - batch.clearFramebuffer( - gpu::Framebuffer::BUFFER_COLOR0 | - gpu::Framebuffer::BUFFER_COLOR1 | - gpu::Framebuffer::BUFFER_COLOR2 | - gpu::Framebuffer::BUFFER_DEPTH, - vec4(vec3(0), 1), 1.0, 0.0, true); - // Viewport is assigned to the size of the framebuffer - renderArgs->_context->render(batch); - } - // render rear mirror view displaySide(renderArgs, _mirrorCamera, true, billboard); renderArgs->_viewport = originalViewport; - } void Application::resetSensors() { diff --git a/interface/src/Application.h b/interface/src/Application.h index 3a07fb656d..d1886862d2 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -328,6 +328,8 @@ public: gpu::ContextPointer getGPUContext() const { return _gpuContext; } + const QRect& getMirrorViewRect() const { return _mirrorViewRect; } + signals: /// Fired when we're simulating; allows external parties to hook in. @@ -640,6 +642,7 @@ private: Overlays _overlays; ApplicationOverlay _applicationOverlay; ApplicationCompositor _compositor; + int _numFramesSinceLastResize = 0; }; #endif // hifi_Application_h diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 980a4d3e14..c6c6919325 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1551,6 +1551,9 @@ void MyAvatar::maybeUpdateBillboard() { QBuffer buffer(&_billboard); buffer.open(QIODevice::WriteOnly); image.save(&buffer, "PNG"); +#ifdef DEBUG + image.save("billboard.png", "PNG"); +#endif _billboardValid = true; sendBillboardPacket(); diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index a7f03db24f..bc10b555e4 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -93,6 +94,7 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) { // Now render the overlay components together into a single texture renderDomainConnectionStatusBorder(renderArgs); // renders the connected domain line renderAudioScope(renderArgs); // audio scope in the very back + renderRearView(renderArgs); // renders the mirror view selfie renderQmlUi(renderArgs); // renders a unit quad with the QML UI texture, and the text overlays from scripts renderOverlays(renderArgs); // renders Scripts Overlay and AudioScope renderStatsAndLogs(renderArgs); // currently renders nothing @@ -167,6 +169,39 @@ void ApplicationOverlay::renderRearViewToFbo(RenderArgs* renderArgs) { } void ApplicationOverlay::renderRearView(RenderArgs* renderArgs) { + if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { + gpu::Batch& batch = *renderArgs->_batch; + + auto geometryCache = DependencyManager::get(); + + auto framebuffer = DependencyManager::get(); + auto selfieTexture = framebuffer->getSelfieFramebuffer()->getRenderBuffer(0); + + int width = renderArgs->_viewport.z; + int height = renderArgs->_viewport.w; + mat4 legacyProjection = glm::ortho(0, width, height, 0, ORTHO_NEAR_CLIP, ORTHO_FAR_CLIP); + batch.setProjectionTransform(legacyProjection); + batch.setModelTransform(Transform()); + batch.setViewTransform(Transform()); + + float screenRatio = ((float)qApp->getDevicePixelRatio()); + float renderRatio = ((float)screenRatio * qApp->getRenderResolutionScale()); + + auto viewport = qApp->getMirrorViewRect(); + glm::vec2 bottomLeft(viewport.left(), viewport.top() + viewport.height()); + glm::vec2 topRight(viewport.left() + viewport.width(), viewport.top()); + bottomLeft *= screenRatio; + topRight *= screenRatio; + glm::vec2 texCoordMinCorner(0.0f, 0.0f); + glm::vec2 texCoordMaxCorner(viewport.width() * renderRatio / float(selfieTexture->getWidth()), viewport.height() * renderRatio / float(selfieTexture->getHeight())); + + geometryCache->useSimpleDrawPipeline(batch, true); + batch.setResourceTexture(0, selfieTexture); + geometryCache->renderQuad(batch, bottomLeft, topRight, texCoordMinCorner, texCoordMaxCorner, glm::vec4(1.0f, 1.0f, 1.0f, 1.0f)); + + batch.setResourceTexture(0, renderArgs->_whiteTexture); + geometryCache->useSimpleDrawPipeline(batch, false); + } } void ApplicationOverlay::renderStatsAndLogs(RenderArgs* renderArgs) { diff --git a/libraries/gpu/src/gpu/Batch.cpp b/libraries/gpu/src/gpu/Batch.cpp index 01c3c4ade7..567ce66cd8 100644 --- a/libraries/gpu/src/gpu/Batch.cpp +++ b/libraries/gpu/src/gpu/Batch.cpp @@ -220,10 +220,7 @@ void Batch::setStateBlendFactor(const Vec4& factor) { void Batch::setStateScissorRect(const Vec4i& rect) { ADD_COMMAND(setStateScissorRect); - _params.push_back(rect.x); - _params.push_back(rect.y); - _params.push_back(rect.z); - _params.push_back(rect.w); + _params.push_back(cacheData(sizeof(Vec4i), &rect)); } void Batch::setUniformBuffer(uint32 slot, const BufferPointer& buffer, Offset offset, Offset size) { diff --git a/libraries/gpu/src/gpu/Batch.h b/libraries/gpu/src/gpu/Batch.h index 2f1d2e8ece..acc1f6fdac 100644 --- a/libraries/gpu/src/gpu/Batch.h +++ b/libraries/gpu/src/gpu/Batch.h @@ -108,7 +108,6 @@ public: void blit(const FramebufferPointer& src, const Vec4i& srcViewport, const FramebufferPointer& dst, const Vec4i& dstViewport); - // Query Section void beginQuery(const QueryPointer& query); void endQuery(const QueryPointer& query); diff --git a/libraries/gpu/src/gpu/Context.cpp b/libraries/gpu/src/gpu/Context.cpp index 51335f78df..604f39f46f 100644 --- a/libraries/gpu/src/gpu/Context.cpp +++ b/libraries/gpu/src/gpu/Context.cpp @@ -40,4 +40,8 @@ void Context::render(Batch& batch) { void Context::syncCache() { PROFILE_RANGE(__FUNCTION__); _backend->syncCache(); -} \ No newline at end of file +} + +void Context::downloadFramebuffer(const FramebufferPointer& srcFramebuffer, const Vec4i& region, QImage& destImage) { + _backend->downloadFramebuffer(srcFramebuffer, region, destImage); +} diff --git a/libraries/gpu/src/gpu/Context.h b/libraries/gpu/src/gpu/Context.h index 484c772c71..ab7a1d1c11 100644 --- a/libraries/gpu/src/gpu/Context.h +++ b/libraries/gpu/src/gpu/Context.h @@ -20,6 +20,8 @@ #include "Pipeline.h" #include "Framebuffer.h" +class QImage; + namespace gpu { class Backend { @@ -28,6 +30,8 @@ public: virtual~ Backend() {}; virtual void render(Batch& batch) = 0; virtual void syncCache() = 0; + virtual void downloadFramebuffer(const FramebufferPointer& srcFramebuffer, const Vec4i& region, QImage& destImage) = 0; + class TransformObject { public: @@ -121,6 +125,10 @@ public: void syncCache(); + // Downloading the Framebuffer is a synchronous action that is not efficient. + // It s here for convenience to easily capture a snapshot + void downloadFramebuffer(const FramebufferPointer& srcFramebuffer, const Vec4i& region, QImage& destImage); + protected: Context(const Context& context); diff --git a/libraries/gpu/src/gpu/DrawTextureOpaque.slf b/libraries/gpu/src/gpu/DrawTextureOpaque.slf new file mode 100755 index 0000000000..1ce3a34c02 --- /dev/null +++ b/libraries/gpu/src/gpu/DrawTextureOpaque.slf @@ -0,0 +1,22 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// Draw texture 0 fetched at texcoord.xy +// Alpha is 1 +// +// Created by Sam Gateau on 6/22/2015 +// Copyright 2015 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 +// + + +uniform sampler2D colorMap; + +varying vec2 varTexcoord; + +void main(void) { + gl_FragColor = vec4(texture2D(colorMap, varTexcoord).xyz, 1.0); +} diff --git a/libraries/gpu/src/gpu/Framebuffer.h b/libraries/gpu/src/gpu/Framebuffer.h index 5a1504d25c..6f2b762bb0 100755 --- a/libraries/gpu/src/gpu/Framebuffer.h +++ b/libraries/gpu/src/gpu/Framebuffer.h @@ -14,8 +14,6 @@ #include "Texture.h" #include -class QImage; - namespace gpu { typedef Element Format; @@ -134,8 +132,6 @@ public: static const uint32 MAX_NUM_RENDER_BUFFERS = 8; static uint32 getMaxNumRenderBuffers() { return MAX_NUM_RENDER_BUFFERS; } - void getImage(QImage* result) const; - protected: SwapchainPointer _swapchain; diff --git a/libraries/gpu/src/gpu/GLBackend.cpp b/libraries/gpu/src/gpu/GLBackend.cpp index 7fd0f9be76..ae50b96bc5 100644 --- a/libraries/gpu/src/gpu/GLBackend.cpp +++ b/libraries/gpu/src/gpu/GLBackend.cpp @@ -192,6 +192,7 @@ void GLBackend::syncCache() { syncTransformStateCache(); syncPipelineStateCache(); syncInputStateCache(); + syncOutputStateCache(); glEnable(GL_LINE_SMOOTH); } @@ -281,6 +282,9 @@ void GLBackend::do_clearFramebuffer(Batch& batch, uint32 paramOffset) { glClearColor(color.x, color.y, color.z, color.w); glmask |= GL_COLOR_BUFFER_BIT; } + + // Force the color mask cache to WRITE_ALL if not the case + do_setStateColorWriteMask(State::ColorMask::WRITE_ALL); } // Apply scissor if needed and if not already on diff --git a/libraries/gpu/src/gpu/GLBackend.h b/libraries/gpu/src/gpu/GLBackend.h index 0e6f181028..894e2c4548 100644 --- a/libraries/gpu/src/gpu/GLBackend.h +++ b/libraries/gpu/src/gpu/GLBackend.h @@ -38,6 +38,10 @@ public: // Let's try to avoid to do that as much as possible! virtual void syncCache(); + // This is the ugly "download the pixels to sysmem for taking a snapshot" + // Just avoid using it, it's ugly and will break performances + virtual void downloadFramebuffer(const FramebufferPointer& srcFramebuffer, const Vec4i& region, QImage& destImage); + static bool checkGLError(const char* name = nullptr); // Only checks in debug builds @@ -383,11 +387,14 @@ protected: void do_setFramebuffer(Batch& batch, uint32 paramOffset); void do_blit(Batch& batch, uint32 paramOffset); - + // Synchronize the state cache of this Backend with the actual real state of the GL Context + void syncOutputStateCache(); + struct OutputStageState { FramebufferPointer _framebuffer = nullptr; - + GLuint _drawFBO = 0; + OutputStageState() {} } _output; diff --git a/libraries/gpu/src/gpu/GLBackendOutput.cpp b/libraries/gpu/src/gpu/GLBackendOutput.cpp index 8876db95ac..1b22649ad6 100755 --- a/libraries/gpu/src/gpu/GLBackendOutput.cpp +++ b/libraries/gpu/src/gpu/GLBackendOutput.cpp @@ -8,9 +8,12 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include + #include "GPULogging.h" #include "GLBackendShared.h" + using namespace gpu; GLBackend::GLFramebuffer::GLFramebuffer() {} @@ -34,6 +37,9 @@ GLBackend::GLFramebuffer* GLBackend::syncGPUObject(const Framebuffer& framebuffe // need to have a gpu object? if (!object) { + GLint currentFBO; + glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, ¤tFBO); + GLuint fbo; glGenFramebuffers(1, &fbo); (void) CHECK_GL_ERROR(); @@ -84,6 +90,8 @@ GLBackend::GLFramebuffer* GLBackend::syncGPUObject(const Framebuffer& framebuffe glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderBuffer); (void) CHECK_GL_ERROR(); } + + // glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); #endif @@ -139,6 +147,9 @@ GLBackend::GLFramebuffer* GLBackend::syncGPUObject(const Framebuffer& framebuffe object->_fbo = fbo; object->_colorBuffers = colorBuffers; Backend::setGPUObject(framebuffer, object); + + // restore the current framebuffer + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, currentFBO); } return object; @@ -158,11 +169,24 @@ GLuint GLBackend::getFramebufferID(const FramebufferPointer& framebuffer) { } } +void GLBackend::syncOutputStateCache() { + GLint currentFBO; + glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, ¤tFBO); + + _output._drawFBO = currentFBO; + _output._framebuffer.reset(); +} + + void GLBackend::do_setFramebuffer(Batch& batch, uint32 paramOffset) { auto framebuffer = batch._framebuffers.get(batch._params[paramOffset]._uint); if (_output._framebuffer != framebuffer) { - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, getFramebufferID(framebuffer)); + auto newFBO = getFramebufferID(framebuffer); + if (_output._drawFBO != newFBO) { + _output._drawFBO = newFBO; + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, newFBO); + } _output._framebuffer = framebuffer; } } @@ -184,4 +208,38 @@ void GLBackend::do_blit(Batch& batch, uint32 paramOffset) { glBlitFramebuffer(srcvp.x, srcvp.y, srcvp.z, srcvp.w, dstvp.x, dstvp.y, dstvp.z, dstvp.w, GL_COLOR_BUFFER_BIT, GL_LINEAR); + + (void) CHECK_GL_ERROR(); + + if (_output._framebuffer) { + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, getFramebufferID(_output._framebuffer)); + } } + + +void GLBackend::downloadFramebuffer(const FramebufferPointer& srcFramebuffer, const Vec4i& region, QImage& destImage) { + auto readFBO = gpu::GLBackend::getFramebufferID(srcFramebuffer); + if (srcFramebuffer && readFBO) { + if ((srcFramebuffer->getWidth() < (region.x + region.z)) || (srcFramebuffer->getHeight() < (region.y + region.w))) { + qCDebug(gpulogging) << "GLBackend::downloadFramebuffer : srcFramebuffer is too small to provide the region queried"; + return; + } + } + + if ((destImage.width() < region.z) || (destImage.height() < region.w)) { + qCDebug(gpulogging) << "GLBackend::downloadFramebuffer : destImage is too small to receive the region of the framebuffer"; + return; + } + + GLenum format = GL_BGRA; + if (destImage.format() != QImage::Format_ARGB32) { + qCDebug(gpulogging) << "GLBackend::downloadFramebuffer : destImage format must be FORMAT_ARGB32 to receive the region of the framebuffer"; + return; + } + + glBindFramebuffer(GL_READ_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(srcFramebuffer)); + glReadPixels(region.x, region.y, region.z, region.w, format, GL_UNSIGNED_BYTE, destImage.bits()); + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + + (void) CHECK_GL_ERROR(); +} \ No newline at end of file diff --git a/libraries/gpu/src/gpu/GLBackendState.cpp b/libraries/gpu/src/gpu/GLBackendState.cpp index f4f27a5ee9..18fc9ddd3c 100644 --- a/libraries/gpu/src/gpu/GLBackendState.cpp +++ b/libraries/gpu/src/gpu/GLBackendState.cpp @@ -757,11 +757,8 @@ void GLBackend::do_setStateBlendFactor(Batch& batch, uint32 paramOffset) { } void GLBackend::do_setStateScissorRect(Batch& batch, uint32 paramOffset) { - - Vec4 rect(batch._params[paramOffset + 0]._float, - batch._params[paramOffset + 1]._float, - batch._params[paramOffset + 2]._float, - batch._params[paramOffset + 3]._float); + Vec4i rect; + memcpy(&rect, batch.editData(batch._params[paramOffset]._uint), sizeof(Vec4i)); glScissor(rect.x, rect.y, rect.z, rect.w); (void) CHECK_GL_ERROR(); diff --git a/libraries/gpu/src/gpu/StandardShaderLib.cpp b/libraries/gpu/src/gpu/StandardShaderLib.cpp index 1c1cca458a..3f27a7fc35 100755 --- a/libraries/gpu/src/gpu/StandardShaderLib.cpp +++ b/libraries/gpu/src/gpu/StandardShaderLib.cpp @@ -16,6 +16,7 @@ #include "DrawTexcoordRectTransformUnitQuad_vert.h" #include "DrawViewportQuadTransformTexcoord_vert.h" #include "DrawTexture_frag.h" +#include "DrawTextureOpaque_frag.h" #include "DrawColoredTexture_frag.h" using namespace gpu; @@ -24,6 +25,7 @@ ShaderPointer StandardShaderLib::_drawTransformUnitQuadVS; ShaderPointer StandardShaderLib::_drawTexcoordRectTransformUnitQuadVS; ShaderPointer StandardShaderLib::_drawViewportQuadTransformTexcoordVS; ShaderPointer StandardShaderLib::_drawTexturePS; +ShaderPointer StandardShaderLib::_drawTextureOpaquePS; ShaderPointer StandardShaderLib::_drawColoredTexturePS; StandardShaderLib::ProgramMap StandardShaderLib::_programs; @@ -82,6 +84,15 @@ ShaderPointer StandardShaderLib::getDrawTexturePS() { return _drawTexturePS; } +ShaderPointer StandardShaderLib::getDrawTextureOpaquePS() { + if (!_drawTextureOpaquePS) { + _drawTextureOpaquePS = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(DrawTextureOpaque_frag))); + } + return _drawTextureOpaquePS; +} + + + ShaderPointer StandardShaderLib::getDrawColoredTexturePS() { if (!_drawColoredTexturePS) { _drawColoredTexturePS = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(DrawColoredTexture_frag))); diff --git a/libraries/gpu/src/gpu/StandardShaderLib.h b/libraries/gpu/src/gpu/StandardShaderLib.h index d5a3685feb..2d9c168473 100755 --- a/libraries/gpu/src/gpu/StandardShaderLib.h +++ b/libraries/gpu/src/gpu/StandardShaderLib.h @@ -35,6 +35,7 @@ public: static ShaderPointer getDrawViewportQuadTransformTexcoordVS(); static ShaderPointer getDrawTexturePS(); + static ShaderPointer getDrawTextureOpaquePS(); static ShaderPointer getDrawColoredTexturePS(); // The shader program combining the shaders available above, so they are unique @@ -47,6 +48,7 @@ protected: static ShaderPointer _drawTexcoordRectTransformUnitQuadVS; static ShaderPointer _drawViewportQuadTransformTexcoordVS; static ShaderPointer _drawTexturePS; + static ShaderPointer _drawTextureOpaquePS; static ShaderPointer _drawColoredTexturePS; typedef std::map, ShaderPointer> ProgramMap; diff --git a/libraries/model/src/model/Skybox.cpp b/libraries/model/src/model/Skybox.cpp index 1464afa45d..314492881f 100755 --- a/libraries/model/src/model/Skybox.cpp +++ b/libraries/model/src/model/Skybox.cpp @@ -110,7 +110,7 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Sky } else { // skybox has no cubemap, just clear the color buffer auto color = skybox.getColor(); - batch.clearFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, glm::vec4(color, 0.0f), 0.0f, 0); + batch.clearFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, glm::vec4(color, 0.0f), 0.0f, 0, true); } } diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index 9a45ea488e..c14bbfcb1d 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -220,13 +220,17 @@ void DeferredLightingEffect::addSpotLight(const glm::vec3& position, float radiu void DeferredLightingEffect::prepare(RenderArgs* args) { gpu::Batch batch; - - // clear the normal and specular buffers - batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR1, glm::vec4(0.0f, 0.0f, 0.0f, 0.0f)); - const float MAX_SPECULAR_EXPONENT = 128.0f; - batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR2, glm::vec4(0.0f, 0.0f, 0.0f, 1.0f / MAX_SPECULAR_EXPONENT)); - args->_context->syncCache(); + batch.setStateScissorRect(args->_viewport); + + auto primaryFbo = DependencyManager::get()->getPrimaryFramebuffer(); + + batch.setFramebuffer(primaryFbo); + // clear the normal and specular buffers + batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR1, glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), true); + const float MAX_SPECULAR_EXPONENT = 128.0f; + batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR2, glm::vec4(0.0f, 0.0f, 0.0f, 1.0f / MAX_SPECULAR_EXPONENT), true); + args->_context->render(batch); } @@ -245,8 +249,9 @@ void DeferredLightingEffect::render(RenderArgs* args) { batch.setFramebuffer(_copyFBO); batch.setViewportTransform(args->_viewport); + batch.setStateScissorRect(args->_viewport); - batch.clearColorFramebuffer(_copyFBO->getBufferMask(), glm::vec4(0.0f, 0.0f, 0.0f, 0.0f)); + batch.clearColorFramebuffer(_copyFBO->getBufferMask(), glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), true); batch.setResourceTexture(0, framebufferCache->getPrimaryColorTexture()); @@ -533,7 +538,6 @@ void DeferredLightingEffect::render(RenderArgs* args) { batch.setResourceTexture(2, nullptr); batch.setResourceTexture(3, nullptr); - args->_context->syncCache(); args->_context->render(batch); // End of the Lighting pass @@ -546,7 +550,8 @@ void DeferredLightingEffect::copyBack(RenderArgs* args) { QSize framebufferSize = framebufferCache->getFrameBufferSize(); // TODO why doesn't this blit work? It only seems to affect a small area below the rear view mirror. - auto destFbo = framebufferCache->getPrimaryFramebuffer(); + // auto destFbo = framebufferCache->getPrimaryFramebuffer(); + auto destFbo = framebufferCache->getPrimaryFramebufferDepthColor(); // gpu::Vec4i vp = args->_viewport; // batch.blit(_copyFBO, vp, framebufferCache->getPrimaryFramebuffer(), vp); batch.setFramebuffer(destFbo); @@ -565,11 +570,6 @@ void DeferredLightingEffect::copyBack(RenderArgs* args) { batch.setModelTransform(model); } - GLenum buffers[3]; - int bufferCount = 0; - buffers[bufferCount++] = GL_COLOR_ATTACHMENT0; - batch._glDrawBuffers(bufferCount, buffers); - batch.setResourceTexture(0, _copyFBO->getRenderBuffer(0)); batch.draw(gpu::TRIANGLE_STRIP, 4); diff --git a/libraries/render-utils/src/FramebufferCache.cpp b/libraries/render-utils/src/FramebufferCache.cpp index c7cb6451f7..601d99108d 100644 --- a/libraries/render-utils/src/FramebufferCache.cpp +++ b/libraries/render-utils/src/FramebufferCache.cpp @@ -34,17 +34,20 @@ void FramebufferCache::setFrameBufferSize(QSize frameBufferSize) { //If the size changed, we need to delete our FBOs if (_frameBufferSize != frameBufferSize) { _frameBufferSize = frameBufferSize; - _primaryFramebuffer.reset(); + _primaryFramebufferFull.reset(); + _primaryFramebufferDepthColor.reset(); _primaryDepthTexture.reset(); _primaryColorTexture.reset(); _primaryNormalTexture.reset(); _primarySpecularTexture.reset(); + _selfieFramebuffer.reset(); _cachedFramebuffers.clear(); } } void FramebufferCache::createPrimaryFramebuffer() { - _primaryFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create()); + _primaryFramebufferFull = gpu::FramebufferPointer(gpu::Framebuffer::create()); + _primaryFramebufferDepthColor = gpu::FramebufferPointer(gpu::Framebuffer::create()); auto colorFormat = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA); auto width = _frameBufferSize.width(); @@ -55,24 +58,37 @@ void FramebufferCache::createPrimaryFramebuffer() { _primaryNormalTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width, height, defaultSampler)); _primarySpecularTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width, height, defaultSampler)); - _primaryFramebuffer->setRenderBuffer(0, _primaryColorTexture); - _primaryFramebuffer->setRenderBuffer(1, _primaryNormalTexture); - _primaryFramebuffer->setRenderBuffer(2, _primarySpecularTexture); + _primaryFramebufferFull->setRenderBuffer(0, _primaryColorTexture); + _primaryFramebufferFull->setRenderBuffer(1, _primaryNormalTexture); + _primaryFramebufferFull->setRenderBuffer(2, _primarySpecularTexture); + _primaryFramebufferDepthColor->setRenderBuffer(0, _primaryColorTexture); auto depthFormat = gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPTH); _primaryDepthTexture = gpu::TexturePointer(gpu::Texture::create2D(depthFormat, width, height, defaultSampler)); - _primaryFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, depthFormat); + _primaryFramebufferFull->setDepthStencilBuffer(_primaryDepthTexture, depthFormat); + + _primaryFramebufferDepthColor->setDepthStencilBuffer(_primaryDepthTexture, depthFormat); + + _selfieFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create()); + auto tex = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width * 0.5, height * 0.5, defaultSampler)); + _selfieFramebuffer->setRenderBuffer(0, tex); } gpu::FramebufferPointer FramebufferCache::getPrimaryFramebuffer() { - if (!_primaryFramebuffer) { + if (!_primaryFramebufferFull) { createPrimaryFramebuffer(); } - return _primaryFramebuffer; + return _primaryFramebufferFull; } +gpu::FramebufferPointer FramebufferCache::getPrimaryFramebufferDepthColor() { + if (!_primaryFramebufferDepthColor) { + createPrimaryFramebuffer(); + } + return _primaryFramebufferDepthColor; +} gpu::TexturePointer FramebufferCache::getPrimaryDepthTexture() { @@ -112,7 +128,6 @@ gpu::FramebufferPointer FramebufferCache::getFramebuffer() { return result; } - void FramebufferCache::releaseFramebuffer(const gpu::FramebufferPointer& framebuffer) { if (QSize(framebuffer->getSize().x, framebuffer->getSize().y) == _frameBufferSize) { _cachedFramebuffers.push_back(framebuffer); @@ -126,3 +141,10 @@ gpu::FramebufferPointer FramebufferCache::getShadowFramebuffer() { } return _shadowFramebuffer; } + +gpu::FramebufferPointer FramebufferCache::getSelfieFramebuffer() { + if (!_selfieFramebuffer) { + createPrimaryFramebuffer(); + } + return _selfieFramebuffer; +} diff --git a/libraries/render-utils/src/FramebufferCache.h b/libraries/render-utils/src/FramebufferCache.h index ca01a470d9..c2274a77e8 100644 --- a/libraries/render-utils/src/FramebufferCache.h +++ b/libraries/render-utils/src/FramebufferCache.h @@ -30,6 +30,7 @@ public: /// Returns a pointer to the primary framebuffer object. This render target includes a depth component, and is /// used for scene rendering. gpu::FramebufferPointer getPrimaryFramebuffer(); + gpu::FramebufferPointer getPrimaryFramebufferDepthColor(); gpu::TexturePointer getPrimaryDepthTexture(); gpu::TexturePointer getPrimaryColorTexture(); @@ -39,8 +40,12 @@ public: /// Returns the framebuffer object used to render shadow maps; gpu::FramebufferPointer getShadowFramebuffer(); + /// Returns the framebuffer object used to render selfie maps; + gpu::FramebufferPointer getSelfieFramebuffer(); + /// Returns a free framebuffer with a single color attachment for temp or intra-frame operations gpu::FramebufferPointer getFramebuffer(); + // TODO add sync functionality to the release, so we don't reuse a framebuffer being read from /// Releases a free framebuffer back for reuse void releaseFramebuffer(const gpu::FramebufferPointer& framebuffer); @@ -51,13 +56,17 @@ private: void createPrimaryFramebuffer(); - gpu::FramebufferPointer _primaryFramebuffer; + gpu::FramebufferPointer _primaryFramebufferFull; + gpu::FramebufferPointer _primaryFramebufferDepthColor; gpu::TexturePointer _primaryDepthTexture; gpu::TexturePointer _primaryColorTexture; gpu::TexturePointer _primaryNormalTexture; gpu::TexturePointer _primarySpecularTexture; gpu::FramebufferPointer _shadowFramebuffer; + + gpu::FramebufferPointer _selfieFramebuffer; + QSize _frameBufferSize{ 100, 100 }; }; diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index c43cddff6e..8550f8d8b6 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -29,6 +29,8 @@ #include "standardTransformPNTC_vert.h" #include "standardDrawTexture_frag.h" +#include "gpu/StandardShaderLib.h" + //#define WANT_DEBUG const int GeometryCache::UNKNOWN_ID = -1; @@ -1651,7 +1653,7 @@ QSharedPointer GeometryCache::createResource(const QUrl& url, const QS return geometry.staticCast(); } -void GeometryCache::useSimpleDrawPipeline(gpu::Batch& batch) { +void GeometryCache::useSimpleDrawPipeline(gpu::Batch& batch, bool noBlend) { if (!_standardDrawPipeline) { auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(standardTransformPNTC_vert))); auto ps = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(standardDrawTexture_frag))); @@ -1660,12 +1662,24 @@ void GeometryCache::useSimpleDrawPipeline(gpu::Batch& batch) { auto state = std::make_shared(); + // enable decal blend state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA); _standardDrawPipeline.reset(gpu::Pipeline::create(program, state)); + + + auto stateNoBlend = std::make_shared(); + auto noBlendPS = gpu::StandardShaderLib::getDrawTextureOpaquePS(); + auto programNoBlend = gpu::ShaderPointer(gpu::Shader::createProgram(vs, noBlendPS)); + gpu::Shader::makeProgram((*programNoBlend)); + _standardDrawPipelineNoBlend.reset(gpu::Pipeline::create(programNoBlend, stateNoBlend)); + } + if (noBlend) { + batch.setPipeline(_standardDrawPipelineNoBlend); + } else { + batch.setPipeline(_standardDrawPipeline); } - batch.setPipeline(_standardDrawPipeline); } const float NetworkGeometry::NO_HYSTERESIS = -1.0f; diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h index 2f90c33adf..812d12b846 100644 --- a/libraries/render-utils/src/GeometryCache.h +++ b/libraries/render-utils/src/GeometryCache.h @@ -206,7 +206,7 @@ public: QSharedPointer getGeometry(const QUrl& url, const QUrl& fallback = QUrl(), bool delayLoad = false); /// Set a batch to the simple pipeline, returning the previous pipeline - void useSimpleDrawPipeline(gpu::Batch& batch); + void useSimpleDrawPipeline(gpu::Batch& batch, bool noBlend = false); protected: @@ -221,6 +221,7 @@ private: typedef QPair VerticesIndices; gpu::PipelinePointer _standardDrawPipeline; + gpu::PipelinePointer _standardDrawPipelineNoBlend; QHash _cubeVerticies; QHash _cubeColors; gpu::BufferPointer _wireCubeIndexBuffer; diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 6a627bb6ab..0c8d19250b 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -17,6 +17,7 @@ #include #include +#include "FramebufferCache.h" #include "DeferredLightingEffect.h" #include "TextureCache.h" @@ -27,6 +28,26 @@ using namespace render; +void SetupDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + RenderArgs* args = renderContext->args; + + auto primaryFbo = DependencyManager::get()->getPrimaryFramebufferDepthColor(); + + gpu::Batch batch; + batch.setFramebuffer(nullptr); + batch.setFramebuffer(primaryFbo); + + batch.setViewportTransform(args->_viewport); + batch.setStateScissorRect(args->_viewport); + + batch.clearFramebuffer( + gpu::Framebuffer::BUFFER_COLOR0 | + gpu::Framebuffer::BUFFER_DEPTH, + vec4(vec3(0), 1), 1.0, 0.0, true); + + args->_context->render(batch); +} + void PrepareDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { DependencyManager::get()->prepare(renderContext->args); } @@ -41,6 +62,7 @@ void ResolveDeferred::run(const SceneContextPointer& sceneContext, const RenderC } RenderDeferredTask::RenderDeferredTask() : Task() { + _jobs.push_back(Job(new SetupDeferred::JobModel("SetupFramebuffer"))); _jobs.push_back(Job(new DrawBackground::JobModel("DrawBackground"))); _jobs.push_back(Job(new PrepareDeferred::JobModel("PrepareDeferred"))); @@ -56,7 +78,6 @@ RenderDeferredTask::RenderDeferredTask() : Task() { auto& renderedOpaques = _jobs.back().getOutput(); _jobs.push_back(Job(new DrawOpaqueDeferred::JobModel("DrawOpaqueDeferred", _jobs.back().getOutput()))); _jobs.push_back(Job(new DrawLight::JobModel("DrawLight"))); - _jobs.push_back(Job(new ResetGLState::JobModel())); _jobs.push_back(Job(new RenderDeferred::JobModel("RenderDeferred"))); _jobs.push_back(Job(new ResolveDeferred::JobModel("ResolveDeferred"))); _jobs.push_back(Job(new FetchItems::JobModel("FetchTransparent", @@ -133,21 +154,12 @@ void DrawOpaqueDeferred::run(const SceneContextPointer& sceneContext, const Rend 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); const float OPAQUE_ALPHA_THRESHOLD = 0.5f; args->_alphaThreshold = OPAQUE_ALPHA_THRESHOLD; } renderItems(sceneContext, renderContext, inItems, renderContext->_maxDrawnOpaqueItems); - // Before rendering the batch make sure we re in sync with gl state - args->_context->syncCache(); - renderContext->args->_context->syncCache(); args->_context->render((*args->_batch)); args->_batch = nullptr; } @@ -171,21 +183,15 @@ void DrawTransparentDeferred::run(const SceneContextPointer& sceneContext, const } batch.setProjectionTransform(projMat); batch.setViewTransform(viewMat); - const float TRANSPARENT_ALPHA_THRESHOLD = 0.0f; { - GLenum buffers[3]; - int bufferCount = 0; - buffers[bufferCount++] = GL_COLOR_ATTACHMENT0; - batch._glDrawBuffers(bufferCount, buffers); + const float TRANSPARENT_ALPHA_THRESHOLD = 0.0f; args->_alphaThreshold = TRANSPARENT_ALPHA_THRESHOLD; } renderItems(sceneContext, renderContext, inItems, renderContext->_maxDrawnTransparentItems); - - // Before rendering the batch make sure we re in sync with gl state - args->_context->syncCache(); + args->_context->render((*args->_batch)); args->_batch = nullptr; } @@ -239,17 +245,17 @@ void DrawOverlay3D::run(const SceneContextPointer& sceneContext, const RenderCon } batch.setProjectionTransform(projMat); batch.setViewTransform(viewMat); + batch.setViewportTransform(args->_viewport); + batch.setStateScissorRect(args->_viewport); batch.setPipeline(getOpaquePipeline()); batch.setResourceTexture(0, args->_whiteTexture); if (!inItems.empty()) { - batch.clearFramebuffer(gpu::Framebuffer::BUFFER_DEPTH, glm::vec4(), 1.f, 0); + batch.clearFramebuffer(gpu::Framebuffer::BUFFER_DEPTH, glm::vec4(), 1.f, 0, true); renderItems(sceneContext, renderContext, inItems, renderContext->_maxDrawnOverlay3DItems); } - // Before rendering the batch make sure we re in sync with gl state - args->_context->syncCache(); args->_context->render((*args->_batch)); args->_batch = nullptr; args->_whiteTexture.reset(); diff --git a/libraries/render-utils/src/RenderDeferredTask.h b/libraries/render-utils/src/RenderDeferredTask.h index 4040606c62..1fec1c936f 100755 --- a/libraries/render-utils/src/RenderDeferredTask.h +++ b/libraries/render-utils/src/RenderDeferredTask.h @@ -16,6 +16,13 @@ #include "gpu/Pipeline.h" +class SetupDeferred { +public: + void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); + + typedef render::Job::Model JobModel; +}; + class PrepareDeferred { public: void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext);