First pass at threaded rendering

This commit is contained in:
Bradley Austin Davis 2016-07-27 13:55:24 -07:00 committed by Brad Davis
parent 8a28f7cdac
commit 274321de8a
46 changed files with 1216 additions and 1013 deletions

View file

@ -6,18 +6,27 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#version 410 core
uniform sampler2D sampler;
uniform float alpha = 1.0;
uniform vec4 glowPoints = vec4(-1);
uniform vec4 glowColors[2];
uniform vec2 resolution = vec2(3960.0, 1188.0);
uniform float radius = 0.005;
struct OverlayData {
mat4 mvp;
vec4 glowPoints;
vec4 glowColors[2];
vec4 resolutionRadiusAlpha;
};
layout(std140) uniform overlayBuffer {
OverlayData overlay;
};
vec2 resolution = overlay.resolutionRadiusAlpha.xy;
float radius = overlay.resolutionRadiusAlpha.z;
float alpha = overlay.resolutionRadiusAlpha.w;
vec4 glowPoints = overlay.glowPoints;
vec4 glowColors[2] = overlay.glowColors;
in vec3 vPosition;
in vec2 vTexCoord;
in vec4 vGlowPoints;
out vec4 FragColor;
@ -31,9 +40,10 @@ float easeInOutCubic(float f) {
}
void main() {
FragColor = texture(sampler, vTexCoord);
vec2 aspect = resolution;
aspect /= resolution.x;
FragColor = texture(sampler, vTexCoord);
float glowIntensity = 0.0;
float dist1 = distance(vTexCoord * aspect, glowPoints.xy * aspect);

View file

@ -6,12 +6,21 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#version 410 core
struct OverlayData {
mat4 mvp;
vec4 glowPoints;
vec4 glowColors[2];
vec4 resolutionRadiusAlpha;
};
uniform mat4 mvp = mat4(1);
layout(std140) uniform overlayBuffer {
OverlayData overlay;
};
in vec3 Position;
in vec2 TexCoord;
mat4 mvp = overlay.mvp;
layout(location = 0) in vec3 Position;
layout(location = 3) in vec2 TexCoord;
out vec3 vPosition;
out vec2 vTexCoord;

View file

@ -779,16 +779,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
_glWidget->makeCurrent();
_glWidget->initializeGL();
_chromiumShareContext = new OffscreenGLCanvas();
_chromiumShareContext->create(_glWidget->context()->contextHandle());
_chromiumShareContext->makeCurrent();
qt_gl_set_global_share_context(_chromiumShareContext->getContext());
_offscreenContext = new OffscreenGLCanvas();
_offscreenContext->create(_glWidget->context()->contextHandle());
_offscreenContext->makeCurrent();
initializeGL();
_offscreenContext->makeCurrent();
// Make sure we don't time out during slow operations at startup
updateHeartbeat();
@ -1498,11 +1489,18 @@ void Application::initializeGL() {
_isGLInitialized = true;
}
_glWidget->makeCurrent();
_chromiumShareContext = new OffscreenGLCanvas();
_chromiumShareContext->create(_glWidget->context()->contextHandle());
_chromiumShareContext->makeCurrent();
qt_gl_set_global_share_context(_chromiumShareContext->getContext());
_glWidget->makeCurrent();
gpu::Context::init<gpu::gl::GLBackend>();
_gpuContext = std::make_shared<gpu::Context>();
// The gpu context can make child contexts for transfers, so
// we need to restore primary rendering context
_offscreenContext->makeCurrent();
_glWidget->makeCurrent();
initDisplay();
qCDebug(interfaceapp, "Initialized Display.");
@ -1521,7 +1519,8 @@ void Application::initializeGL() {
// Needs to happen AFTER the render engine initialization to access its configuration
initializeUi();
qCDebug(interfaceapp, "Initialized Offscreen UI.");
_offscreenContext->makeCurrent();
_glWidget->makeCurrent();
// call Menu getInstance static method to set up the menu
// Needs to happen AFTER the QML UI initialization
@ -1537,8 +1536,13 @@ void Application::initializeGL() {
_idleLoopStdev.reset();
_offscreenContext = new OffscreenGLCanvas();
_offscreenContext->create(_glWidget->context()->contextHandle());
_offscreenContext->makeCurrent();
// update before the first render
update(0);
}
FrameTimingsScriptingInterface _frameTimingsScriptingInterface;
@ -1555,7 +1559,7 @@ void Application::initializeUi() {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->create(_offscreenContext->getContext());
offscreenUi->create(_glWidget->context()->contextHandle());
auto rootContext = offscreenUi->getRootContext();
@ -1726,17 +1730,7 @@ 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
{
PerformanceTimer perfTimer("syncCache");
renderArgs._context->syncCache();
}
auto framebufferCache = DependencyManager::get<FramebufferCache>();
// Final framebuffer that will be handled to the display-plugin
auto finalFramebuffer = framebufferCache->getFramebuffer();
_gpuContext->beginFrame(finalFramebuffer, getHMDSensorPose());
_gpuContext->beginFrame(getHMDSensorPose());
// Reset the gpu::Context Stages
// Back to the default framebuffer;
gpu::doInBatch(_gpuContext, [&](gpu::Batch& batch) {
@ -1866,7 +1860,10 @@ void Application::paintGL() {
getApplicationCompositor().setFrameInfo(_frameCount, _myCamera.getTransform());
// Primary rendering pass
auto framebufferCache = DependencyManager::get<FramebufferCache>();
const QSize size = framebufferCache->getFrameBufferSize();
// Final framebuffer that will be handled to the display-plugin
auto finalFramebuffer = framebufferCache->getFramebuffer();
{
PROFILE_RANGE(__FUNCTION__ "/mainRender");
@ -1907,13 +1904,6 @@ void Application::paintGL() {
// Apply IPD scaling
mat4 eyeOffsetTransform = glm::translate(mat4(), eyeOffset * -1.0f * IPDScale);
eyeOffsets[eye] = eyeOffsetTransform;
// Tell the plugin what pose we're using to render. In this case we're just using the
// unmodified head pose because the only plugin that cares (the Oculus plugin) uses it
// for rotational timewarp. If we move to support positonal timewarp, we need to
// ensure this contains the full pose composed with the eye offsets.
displayPlugin->setEyeRenderPose(_frameCount, eye, headPose * glm::inverse(eyeOffsetTransform));
eyeProjections[eye] = displayPlugin->getEyeProjection(eye, baseProjection);
});
renderArgs._context->setStereoProjections(eyeProjections);
@ -1921,36 +1911,26 @@ void Application::paintGL() {
}
renderArgs._blitFramebuffer = finalFramebuffer;
displaySide(&renderArgs, _myCamera);
renderArgs._blitFramebuffer.reset();
renderArgs._context->enableStereo(false);
}
_gpuContext->endFrame();
gpu::TexturePointer overlayTexture = _applicationOverlay.acquireOverlay();
if (overlayTexture) {
displayPlugin->submitOverlayTexture(overlayTexture);
}
// deliver final composited scene to the display plugin
auto frame = _gpuContext->endFrame();
frame->frameIndex = _frameCount;
frame->framebuffer = finalFramebuffer;
frame->framebufferRecycler = [](const gpu::FramebufferPointer& framebuffer){
DependencyManager::get<FramebufferCache>()->releaseFramebuffer(framebuffer);
};
frame->overlay = _applicationOverlay.getOverlayTexture();
// deliver final scene rendering commands to the display plugin
{
PROFILE_RANGE(__FUNCTION__ "/pluginOutput");
PerformanceTimer perfTimer("pluginOutput");
auto finalTexture = finalFramebuffer->getRenderBuffer(0);
Q_ASSERT(!_lockedFramebufferMap.contains(finalTexture));
_lockedFramebufferMap[finalTexture] = finalFramebuffer;
Q_ASSERT(isCurrentContext(_offscreenContext->getContext()));
{
PROFILE_RANGE(__FUNCTION__ "/pluginSubmitScene");
PerformanceTimer perfTimer("pluginSubmitScene");
displayPlugin->submitSceneTexture(_frameCount, finalTexture);
}
Q_ASSERT(isCurrentContext(_offscreenContext->getContext()));
displayPlugin->submitFrame(frame);
}
// Reset the framebuffer and stereo state
renderArgs._blitFramebuffer.reset();
renderArgs._context->enableStereo(false);
{
Stats::getInstance()->setRenderDetails(renderArgs._details);
}
@ -5405,6 +5385,7 @@ void Application::updateDisplayMode() {
DisplayPluginList advanced;
DisplayPluginList developer;
foreach(auto displayPlugin, displayPlugins) {
displayPlugin->setBackend(_gpuContext->getBackend());
auto grouping = displayPlugin->getGrouping();
switch (grouping) {
case Plugin::ADVANCED:
@ -5474,9 +5455,6 @@ void Application::updateDisplayMode() {
_displayPlugin->deactivate();
}
// FIXME probably excessive and useless context switching
_offscreenContext->makeCurrent();
bool active = newDisplayPlugin->activate();
if (!active) {
@ -5621,20 +5599,6 @@ bool Application::makeRenderingContextCurrent() {
return _offscreenContext->makeCurrent();
}
void Application::releaseSceneTexture(const gpu::TexturePointer& texture) {
Q_ASSERT(QThread::currentThread() == thread());
auto& framebufferMap = _lockedFramebufferMap;
Q_ASSERT(framebufferMap.contains(texture));
auto framebufferPointer = framebufferMap[texture];
framebufferMap.remove(texture);
auto framebufferCache = DependencyManager::get<FramebufferCache>();
framebufferCache->releaseFramebuffer(framebufferPointer);
}
void Application::releaseOverlayTexture(const gpu::TexturePointer& texture) {
_applicationOverlay.releaseOverlay(texture);
}
bool Application::isForeground() const {
return _isForeground && !_window->isMinimized();
}

View file

@ -112,8 +112,6 @@ public:
virtual MainWindow* getPrimaryWindow() override;
virtual QOpenGLContext* getPrimaryContext() override;
virtual bool makeRenderingContextCurrent() override;
virtual void releaseSceneTexture(const gpu::TexturePointer& texture) override;
virtual void releaseOverlayTexture(const gpu::TexturePointer& texture) override;
virtual bool isForeground() const override;
virtual DisplayPluginPointer getActiveDisplayPlugin() const override;
@ -434,7 +432,6 @@ private:
InputPluginList _activeInputPlugins;
bool _activatingDisplayPlugin { false };
QMap<gpu::TexturePointer, gpu::FramebufferPointer> _lockedFramebufferMap;
QUndoStack _undoStack;
UndoStackScriptingInterface _undoStackScriptingInterface;

View file

@ -67,7 +67,9 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) {
// Execute the batch into our framebuffer
doInBatch(renderArgs->_context, [&](gpu::Batch& batch) {
PROFILE_RANGE_BATCH(batch, "ApplicationOverlayRender");
renderArgs->_batch = &batch;
batch.enableStereo(false);
int width = _overlayFramebuffer->getWidth();
int height = _overlayFramebuffer->getHeight();
@ -246,10 +248,6 @@ static const auto COLOR_FORMAT = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)
static const auto DEFAULT_SAMPLER = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR);
static const auto DEPTH_FORMAT = gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPTH);
std::mutex _textureGuard;
using Lock = std::unique_lock<std::mutex>;
std::queue<gpu::TexturePointer> _availableTextures;
void ApplicationOverlay::buildFramebufferObject() {
PROFILE_RANGE(__FUNCTION__);
@ -265,22 +263,6 @@ void ApplicationOverlay::buildFramebufferObject() {
_overlayFramebuffer->setDepthStencilBuffer(overlayDepthTexture, DEPTH_FORMAT);
}
if (!_overlayFramebuffer->getRenderBuffer(0)) {
gpu::TexturePointer newColorAttachment;
{
Lock lock(_textureGuard);
if (!_availableTextures.empty()) {
newColorAttachment = _availableTextures.front();
_availableTextures.pop();
}
}
if (newColorAttachment) {
newColorAttachment->resize2D(width, height, newColorAttachment->getNumSamples());
_overlayFramebuffer->setRenderBuffer(0, newColorAttachment);
}
}
// If the overlay framebuffer still has no color attachment, no textures were available for rendering, so build a new one
if (!_overlayFramebuffer->getRenderBuffer(0)) {
const gpu::Sampler OVERLAY_SAMPLER(gpu::Sampler::FILTER_MIN_MAG_LINEAR, gpu::Sampler::WRAP_CLAMP);
auto colorBuffer = gpu::TexturePointer(gpu::Texture::create2D(COLOR_FORMAT, width, height, OVERLAY_SAMPLER));
@ -288,20 +270,9 @@ void ApplicationOverlay::buildFramebufferObject() {
}
}
gpu::TexturePointer ApplicationOverlay::acquireOverlay() {
gpu::TexturePointer ApplicationOverlay::getOverlayTexture() {
if (!_overlayFramebuffer) {
return gpu::TexturePointer();
}
auto result = _overlayFramebuffer->getRenderBuffer(0);
_overlayFramebuffer->setRenderBuffer(0, gpu::TexturePointer());
return result;
}
void ApplicationOverlay::releaseOverlay(gpu::TexturePointer texture) {
if (texture) {
Lock lock(_textureGuard);
_availableTextures.push(texture);
} else {
qWarning() << "Attempted to release null texture";
}
}
return _overlayFramebuffer->getRenderBuffer(0);
}

View file

@ -26,8 +26,7 @@ public:
void renderOverlay(RenderArgs* renderArgs);
gpu::TexturePointer acquireOverlay();
void releaseOverlay(gpu::TexturePointer pointer);
gpu::TexturePointer getOverlayTexture();
private:
void renderStatsAndLogs(RenderArgs* renderArgs);

View file

@ -33,9 +33,9 @@ bool Basic2DWindowOpenGLDisplayPlugin::internalActivate() {
return Parent::internalActivate();
}
void Basic2DWindowOpenGLDisplayPlugin::submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) {
void Basic2DWindowOpenGLDisplayPlugin::submitFrame(const gpu::FramePointer& newFrame) {
_wantVsync = true; // always
Parent::submitSceneTexture(frameIndex, sceneTexture);
Parent::submitFrame(newFrame);
}
void Basic2DWindowOpenGLDisplayPlugin::internalPresent() {

View file

@ -24,7 +24,7 @@ public:
virtual bool internalActivate() override;
virtual void submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) override;
void submitFrame(const gpu::FramePointer& newFrame) override;
virtual void internalPresent() override;

View file

@ -11,6 +11,7 @@
#include <QtGui/QImage>
#include <ui-plugins/PluginContainer.h>
#include <FramebufferCache.h>
const QString NullDisplayPlugin::NAME("NullDisplayPlugin");
@ -22,12 +23,7 @@ bool NullDisplayPlugin::hasFocus() const {
return false;
}
void NullDisplayPlugin::submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) {
_container->releaseSceneTexture(sceneTexture);
}
void NullDisplayPlugin::submitOverlayTexture(const gpu::TexturePointer& overlayTexture) {
_container->releaseOverlayTexture(overlayTexture);
void NullDisplayPlugin::submitFrame(const gpu::FramePointer& resultFramebuffer) {
}
QImage NullDisplayPlugin::getScreenshot() const {

View file

@ -11,16 +11,14 @@
class NullDisplayPlugin : public DisplayPlugin {
public:
~NullDisplayPlugin() final {}
const QString& getName() const override { return NAME; }
grouping getGrouping() const override { return DEVELOPER; }
virtual ~NullDisplayPlugin() final {}
virtual const QString& getName() const override { return NAME; }
virtual grouping getGrouping() const override { return DEVELOPER; }
virtual glm::uvec2 getRecommendedRenderSize() const override;
virtual bool hasFocus() const override;
virtual void submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) override;
virtual void submitOverlayTexture(const gpu::TexturePointer& overlayTexture) override;
virtual QImage getScreenshot() const override;
glm::uvec2 getRecommendedRenderSize() const override;
bool hasFocus() const override;
void submitFrame(const gpu::FramePointer& newFrame) override;
QImage getScreenshot() const override;
private:
static const QString NAME;
};

View file

@ -8,6 +8,7 @@
#include "OpenGLDisplayPlugin.h"
#include <condition_variable>
#include <queue>
#include <QtCore/QCoreApplication>
#include <QtCore/QThread>
@ -19,26 +20,43 @@
#if defined(Q_OS_MAC)
#include <OpenGL/CGLCurrent.h>
#endif
#include <gl/QOpenGLContextWrapper.h>
#include <gpu/Texture.h>
#include <gl/GLWidget.h>
#include <NumericalConstants.h>
#include <DependencyManager.h>
#include <shared/NsightHelpers.h>
#include <ui-plugins/PluginContainer.h>
#include <GLMHelpers.h>
#include <gl/QOpenGLContextWrapper.h>
#include <gl/GLWidget.h>
#include <gl/Config.h>
#include <gl/GLEscrow.h>
#include <GLMHelpers.h>
#include <CursorManager.h>
#include "CompositorHelper.h"
#include <gpu/Texture.h>
#include <gpu/StandardShaderLib.h>
#include <gpu/gl/GLShared.h>
#include <GeometryCache.h>
#include <FramebufferCache.h>
#include <shared/NsightHelpers.h>
#include <ui-plugins/PluginContainer.h>
#include <ui/Menu.h>
#include <CursorManager.h>
#include "CompositorHelper.h"
#if THREADED_PRESENT
const char* SRGB_TO_LINEAR_FRAG = R"SCRIBE(
// FIXME, for display plugins that don't block on something like vsync, just
// cap the present rate at 200
// const static unsigned int MAX_PRESENT_RATE = 200;
uniform sampler2D colorMap;
in vec2 varTexCoord0;
out vec4 outFragColor;
void main(void) {
outFragColor = vec4(pow(texture(colorMap, varTexCoord0).rgb, vec3(2.2)), 1.0);
}
)SCRIBE";
QOpenGLContext* mainContext;
class PresentThread : public QThread, public Dependency {
using Mutex = std::mutex;
@ -87,8 +105,8 @@ public:
virtual void run() override {
OpenGLDisplayPlugin* currentPlugin{ nullptr };
thread()->setPriority(QThread::HighestPriority);
Q_ASSERT(_context);
mainContext = _context->contextHandle();
while (!_shutdown) {
if (_pendingMainThreadOperation) {
{
@ -118,19 +136,13 @@ public:
if (newPlugin != currentPlugin) {
// Deactivate the old plugin
if (currentPlugin != nullptr) {
try {
currentPlugin->uncustomizeContext();
} catch (const oglplus::Error& error) {
qWarning() << "OpenGL error in uncustomizeContext: " << error.what();
}
currentPlugin->uncustomizeContext();
CHECK_GL_ERROR();
}
if (newPlugin) {
try {
newPlugin->customizeContext();
} catch (const oglplus::Error& error) {
qWarning() << "OpenGL error in customizeContext: " << error.what();
}
newPlugin->customizeContext();
CHECK_GL_ERROR();
}
currentPlugin = newPlugin;
_newPluginQueue.pop();
@ -150,11 +162,8 @@ public:
// take the latest texture and present it
_context->makeCurrent();
if (isCurrentContext(_context->contextHandle())) {
try {
currentPlugin->present();
} catch (const oglplus::Error& error) {
qWarning() << "OpenGL error in presentation: " << error.what();
}
currentPlugin->present();
CHECK_GL_ERROR();
_context->doneCurrent();
} else {
qWarning() << "Makecurrent failed";
@ -204,27 +213,13 @@ private:
QGLContext* _context { nullptr };
};
#endif
bool OpenGLDisplayPlugin::isRenderThread() const {
return QThread::currentThread() == DependencyManager::get<PresentThread>()->thread();
}
OpenGLDisplayPlugin::OpenGLDisplayPlugin() {
_sceneTextureEscrow.setRecycler([this](const gpu::TexturePointer& texture){
cleanupForSceneTexture(texture);
_container->releaseSceneTexture(texture);
});
_overlayTextureEscrow.setRecycler([this](const gpu::TexturePointer& texture) {
_container->releaseOverlayTexture(texture);
});
}
void OpenGLDisplayPlugin::cleanupForSceneTexture(const gpu::TexturePointer& sceneTexture) {
withRenderThreadLock([&] {
Q_ASSERT(_sceneTextureToFrameIndexMap.contains(sceneTexture));
_sceneTextureToFrameIndexMap.remove(sceneTexture);
});
}
bool OpenGLDisplayPlugin::activate() {
if (!_cursorsData.size()) {
auto& cursorManager = Cursor::Manager::instance();
@ -244,7 +239,6 @@ bool OpenGLDisplayPlugin::activate() {
}
_vsyncSupported = _container->getPrimaryWidget()->isVsyncSupported();
#if THREADED_PRESENT
// Start the present thread if necessary
QSharedPointer<PresentThread> presentThread;
if (DependencyManager::isSet<PresentThread>()) {
@ -259,7 +253,6 @@ bool OpenGLDisplayPlugin::activate() {
presentThread->start();
}
_presentThread = presentThread.data();
#endif
// Child classes may override this in order to do things like initialize
// libraries, etc
@ -267,17 +260,10 @@ bool OpenGLDisplayPlugin::activate() {
return false;
}
#if THREADED_PRESENT
// This should not return until the new context has been customized
// and the old context (if any) has been uncustomized
presentThread->setNewDisplayPlugin(this);
#else
static auto widget = _container->getPrimaryWidget();
widget->makeCurrent();
customizeContext();
_container->makeRenderingContextCurrent();
#endif
auto compositorHelper = DependencyManager::get<CompositorHelper>();
connect(compositorHelper.data(), &CompositorHelper::alphaChanged, [this] {
@ -300,16 +286,9 @@ void OpenGLDisplayPlugin::deactivate() {
auto compositorHelper = DependencyManager::get<CompositorHelper>();
disconnect(compositorHelper.data());
#if THREADED_PRESENT
auto presentThread = DependencyManager::get<PresentThread>();
// Does not return until the GL transition has completeed
presentThread->setNewDisplayPlugin(nullptr);
#else
static auto widget = _container->getPrimaryWidget();
widget->makeCurrent();
uncustomizeContext();
_container->makeRenderingContextCurrent();
#endif
internalDeactivate();
_container->showDisplayPluginsTools(false);
@ -325,56 +304,74 @@ void OpenGLDisplayPlugin::deactivate() {
void OpenGLDisplayPlugin::customizeContext() {
#if THREADED_PRESENT
auto presentThread = DependencyManager::get<PresentThread>();
Q_ASSERT(thread() == presentThread->thread());
#endif
enableVsync();
for (auto& cursorValue : _cursorsData) {
auto& cursorData = cursorValue.second;
if (!cursorData.texture) {
const auto& image = cursorData.image;
glGenTextures(1, &cursorData.texture);
glBindTexture(GL_TEXTURE_2D, cursorData.texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, image.constBits());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glGenerateMipmap(GL_TEXTURE_2D);
auto image = cursorData.image;
if (image.format() != QImage::Format_ARGB32) {
image = image.convertToFormat(QImage::Format_ARGB32);
}
if ((image.width() > 0) && (image.height() > 0)) {
cursorData.texture.reset(
gpu::Texture::create2D(
gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA),
image.width(), image.height(),
gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)));
auto usage = gpu::Texture::Usage::Builder().withColor().withAlpha();
cursorData.texture->setUsage(usage.build());
cursorData.texture->assignStoredMip(0, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), image.byteCount(), image.constBits());
cursorData.texture->autoGenerateMips(-1);
}
}
glBindTexture(GL_TEXTURE_2D, 0);
}
using namespace oglplus;
Context::BlendFunc(BlendFunction::SrcAlpha, BlendFunction::OneMinusSrcAlpha);
Context::Disable(Capability::Blend);
Context::Disable(Capability::DepthTest);
Context::Disable(Capability::CullFace);
_program = loadDefaultShader();
auto uniforms = _program->ActiveUniforms();
while (!uniforms.Empty()) {
auto uniform = uniforms.Front();
if (uniform.Name() == "mvp") {
_mvpUniform = uniform.Index();
if (!_presentPipeline) {
{
auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS();
auto ps = gpu::Shader::createPixel(std::string(SRGB_TO_LINEAR_FRAG));
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
gpu::Shader::makeProgram(*program);
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
state->setDepthTest(gpu::State::DepthTest(false));
_presentPipeline = gpu::Pipeline::create(program, state);
}
if (uniform.Name() == "alpha") {
_alphaUniform = uniform.Index();
{
auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS();
auto ps = gpu::StandardShaderLib::getDrawTexturePS();
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
gpu::Shader::makeProgram(*program);
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
state->setDepthTest(gpu::State::DepthTest(false));
state->setBlendFunction(true,
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
_overlayPipeline = gpu::Pipeline::create(program, state);
}
{
auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS();
auto ps = gpu::StandardShaderLib::getDrawTexturePS();
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
gpu::Shader::makeProgram(*program);
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
state->setDepthTest(gpu::State::DepthTest(false));
state->setBlendFunction(true,
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
_cursorPipeline = gpu::Pipeline::create(program, state);
}
uniforms.Next();
}
_plane = loadPlane(_program);
_compositeFramebuffer = std::make_shared<BasicFramebufferWrapper>();
_compositeFramebuffer->Init(getRecommendedRenderSize());
}
void OpenGLDisplayPlugin::uncustomizeContext() {
_compositeFramebuffer.reset();
_program.reset();
_plane.reset();
_presentPipeline.reset();
}
@ -420,172 +417,142 @@ bool OpenGLDisplayPlugin::eventFilter(QObject* receiver, QEvent* event) {
return false;
}
void OpenGLDisplayPlugin::submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) {
void OpenGLDisplayPlugin::submitFrame(const gpu::FramePointer& newFrame) {
if (_lockCurrentTexture) {
_container->releaseSceneTexture(sceneTexture);
return;
}
withRenderThreadLock([&] {
_sceneTextureToFrameIndexMap[sceneTexture] = frameIndex;
withNonPresentThreadLock([&] {
_newFrameQueue.push(newFrame);
});
// Submit it to the presentation thread via escrow
_sceneTextureEscrow.submit(sceneTexture);
#if THREADED_PRESENT
#else
static auto widget = _container->getPrimaryWidget();
widget->makeCurrent();
present();
_container->makeRenderingContextCurrent();
#endif
}
void OpenGLDisplayPlugin::submitOverlayTexture(const gpu::TexturePointer& overlayTexture) {
// Submit it to the presentation thread via escrow
_overlayTextureEscrow.submit(overlayTexture);
}
void OpenGLDisplayPlugin::updateTextures() {
// FIXME intrduce a GPU wait instead of a CPU/GPU sync point?
#if THREADED_PRESENT
if (_sceneTextureEscrow.fetchSignaledAndRelease(_currentSceneTexture)) {
#else
if (_sceneTextureEscrow.fetchAndReleaseWithGpuWait(_currentSceneTexture)) {
#endif
updateFrameData();
_newFrameRate.increment();
}
_overlayTextureEscrow.fetchSignaledAndRelease(_currentOverlayTexture);
}
void OpenGLDisplayPlugin::updateFrameData() {
withPresentThreadLock([&] {
auto previousFrameIndex = _currentPresentFrameIndex;
_currentPresentFrameIndex = _sceneTextureToFrameIndexMap[_currentSceneTexture];
auto skippedCount = (_currentPresentFrameIndex - previousFrameIndex) - 1;
gpu::FramePointer oldFrame = _currentFrame;
uint32_t skippedCount = 0;
while (!_newFrameQueue.empty()) {
_currentFrame = _newFrameQueue.front();
_currentFrame->preRender();
_newFrameQueue.pop();
_newFrameQueue = std::queue<gpu::FramePointer>();
if (_currentFrame && oldFrame) {
skippedCount = (_currentFrame->frameIndex - oldFrame->frameIndex) - 1;
}
}
_droppedFrameRate.increment(skippedCount);
});
}
void OpenGLDisplayPlugin::compositeOverlay() {
using namespace oglplus;
auto compositorHelper = DependencyManager::get<CompositorHelper>();
useProgram(_program);
// set the alpha
Uniform<float>(*_program, _alphaUniform).Set(_compositeOverlayAlpha);
// check the alpha
// Overlay draw
gpu::Batch batch;
batch.enableStereo(false);
batch.setFramebuffer(_currentFrame->framebuffer);
batch.setPipeline(_overlayPipeline);
batch.setResourceTexture(0, _currentFrame->overlay);
if (isStereo()) {
Uniform<glm::mat4>(*_program, _mvpUniform).Set(mat4());
for_each_eye([&](Eye eye) {
eyeViewport(eye);
drawUnitQuad();
batch.setViewportTransform(eyeViewport(eye));
batch.draw(gpu::TRIANGLE_STRIP, 4);
});
} else {
// Overlay draw
Uniform<glm::mat4>(*_program, _mvpUniform).Set(mat4());
drawUnitQuad();
batch.setViewportTransform(ivec4(uvec2(0), _currentFrame->framebuffer->getSize()));
batch.draw(gpu::TRIANGLE_STRIP, 4);
}
// restore the alpha
Uniform<float>(*_program, _alphaUniform).Set(1.0);
_backend->render(batch);
}
void OpenGLDisplayPlugin::compositePointer() {
using namespace oglplus;
auto compositorHelper = DependencyManager::get<CompositorHelper>();
useProgram(_program);
// set the alpha
Uniform<float>(*_program, _alphaUniform).Set(_compositeOverlayAlpha);
Uniform<glm::mat4>(*_program, _mvpUniform).Set(compositorHelper->getReticleTransform(glm::mat4()));
auto& cursorManager = Cursor::Manager::instance();
const auto& cursorData = _cursorsData[cursorManager.getCursor()->getIcon()];
auto cursorTransform = DependencyManager::get<CompositorHelper>()->getReticleTransform(glm::mat4());
gpu::Batch batch;
batch.enableStereo(false);
batch.setProjectionTransform(mat4());
batch.setFramebuffer(_currentFrame->framebuffer);
batch.setPipeline(_cursorPipeline);
batch.setResourceTexture(0, cursorData.texture);
batch.setViewTransform(Transform());
batch.setModelTransform(cursorTransform);
if (isStereo()) {
for_each_eye([&](Eye eye) {
eyeViewport(eye);
drawUnitQuad();
batch.setViewportTransform(eyeViewport(eye));
batch.draw(gpu::TRIANGLE_STRIP, 4);
});
} else {
drawUnitQuad();
batch.setViewportTransform(ivec4(uvec2(0), _currentFrame->framebuffer->getSize()));
batch.draw(gpu::TRIANGLE_STRIP, 4);
}
Uniform<glm::mat4>(*_program, _mvpUniform).Set(mat4());
// restore the alpha
Uniform<float>(*_program, _alphaUniform).Set(1.0);
_backend->render(batch);
}
void OpenGLDisplayPlugin::compositeScene() {
using namespace oglplus;
useProgram(_program);
Uniform<glm::mat4>(*_program, _mvpUniform).Set(mat4());
drawUnitQuad();
}
void OpenGLDisplayPlugin::compositeLayers() {
using namespace oglplus;
auto targetRenderSize = getRecommendedRenderSize();
if (!_compositeFramebuffer || _compositeFramebuffer->size != targetRenderSize) {
_compositeFramebuffer = std::make_shared<BasicFramebufferWrapper>();
_compositeFramebuffer->Init(targetRenderSize);
}
_compositeFramebuffer->Bound(Framebuffer::Target::Draw, [&] {
Context::Viewport(targetRenderSize.x, targetRenderSize.y);
auto sceneTextureId = getSceneTextureId();
auto overlayTextureId = getOverlayTextureId();
glBindTexture(GL_TEXTURE_2D, sceneTextureId);
{
PROFILE_RANGE_EX("compositeScene", 0xff0077ff, (uint64_t)presentCount())
compositeScene();
if (overlayTextureId) {
glBindTexture(GL_TEXTURE_2D, overlayTextureId);
Context::Enable(Capability::Blend);
Context::BlendFunc(BlendFunction::SrcAlpha, BlendFunction::OneMinusSrcAlpha);
compositeOverlay();
auto compositorHelper = DependencyManager::get<CompositorHelper>();
if (compositorHelper->getReticleVisible()) {
auto& cursorManager = Cursor::Manager::instance();
const auto& cursorData = _cursorsData[cursorManager.getCursor()->getIcon()];
glBindTexture(GL_TEXTURE_2D, cursorData.texture);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, overlayTextureId);
compositePointer();
glBindTexture(GL_TEXTURE_2D, 0);
glActiveTexture(GL_TEXTURE0);
}
glBindTexture(GL_TEXTURE_2D, 0);
Context::Disable(Capability::Blend);
}
}
{
PROFILE_RANGE_EX("compositeOverlay", 0xff0077ff, (uint64_t)presentCount())
compositeOverlay();
}
auto compositorHelper = DependencyManager::get<CompositorHelper>();
if (compositorHelper->getReticleVisible()) {
PROFILE_RANGE_EX("compositePointer", 0xff0077ff, (uint64_t)presentCount())
compositePointer();
}
{
PROFILE_RANGE_EX("compositeExtra", 0xff0077ff, (uint64_t)presentCount())
compositeExtra();
});
}
}
void OpenGLDisplayPlugin::internalPresent() {
using namespace oglplus;
const uvec2& srcSize = _compositeFramebuffer->size;
uvec2 dstSize = getSurfacePixels();
_compositeFramebuffer->Bound(FramebufferTarget::Read, [&] {
Context::BlitFramebuffer(
0, 0, srcSize.x, srcSize.y,
0, 0, dstSize.x, dstSize.y,
BufferSelectBit::ColorBuffer, BlitFilter::Nearest);
});
gpu::Batch presentBatch;
presentBatch.enableStereo(false);
presentBatch.setViewTransform(Transform());
presentBatch.setFramebuffer(gpu::FramebufferPointer());
presentBatch.setViewportTransform(ivec4(uvec2(0), getSurfacePixels()));
presentBatch.setResourceTexture(0, _currentFrame->framebuffer->getRenderBuffer(0));
presentBatch.setPipeline(_presentPipeline);
presentBatch.draw(gpu::TRIANGLE_STRIP, 4);
_backend->render(presentBatch);
swapBuffers();
}
void OpenGLDisplayPlugin::present() {
PROFILE_RANGE_EX(__FUNCTION__, 0xffffff00, (uint64_t)presentCount())
incrementPresentCount();
PROFILE_RANGE_EX(__FUNCTION__, 0xff00ff00, (uint64_t)presentCount())
updateFrameData();
if (_currentFrame) {
_backend->syncCache();
_backend->setStereoState(_currentFrame->stereoState);
{
PROFILE_RANGE_EX("execute", 0xff00ff00, (uint64_t)presentCount())
// Execute the frame rendering commands
for (auto& batch : _currentFrame->batches) {
_backend->render(batch);
}
}
updateTextures();
if (_currentSceneTexture) {
// Write all layers to a local framebuffer
compositeLayers();
{
PROFILE_RANGE_EX("composite", 0xff00ffff, (uint64_t)presentCount())
compositeLayers();
}
// Take the composite framebuffer and send it to the output device
internalPresent();
{
PROFILE_RANGE_EX("internalPresent", 0xff00ffff, (uint64_t)presentCount())
internalPresent();
}
_presentRate.increment();
_activeProgram.reset();
}
}
@ -595,7 +562,7 @@ float OpenGLDisplayPlugin::newFramePresentRate() const {
float OpenGLDisplayPlugin::droppedFrameRate() const {
float result;
withRenderThreadLock([&] {
withNonPresentThreadLock([&] {
result = _droppedFrameRate.rate();
});
return result;
@ -605,11 +572,6 @@ float OpenGLDisplayPlugin::presentRate() const {
return _presentRate.rate();
}
void OpenGLDisplayPlugin::drawUnitQuad() {
useProgram(_program);
_plane->Use();
_plane->Draw();
}
void OpenGLDisplayPlugin::enableVsync(bool enable) {
if (!_vsyncSupported) {
@ -626,6 +588,7 @@ void OpenGLDisplayPlugin::enableVsync(bool enable) {
#endif
}
bool OpenGLDisplayPlugin::isVsyncEnabled() {
if (!_vsyncSupported) {
return true;
@ -648,19 +611,13 @@ void OpenGLDisplayPlugin::swapBuffers() {
}
void OpenGLDisplayPlugin::withMainThreadContext(std::function<void()> f) const {
#if THREADED_PRESENT
static auto presentThread = DependencyManager::get<PresentThread>();
presentThread->withMainThreadContext(f);
_container->makeRenderingContextCurrent();
#else
static auto widget = _container->getPrimaryWidget();
widget->makeCurrent();
f();
_container->makeRenderingContextCurrent();
#endif
}
QImage OpenGLDisplayPlugin::getScreenshot() const {
#if 0
using namespace oglplus;
QImage screenshot(_compositeFramebuffer->size.x, _compositeFramebuffer->size.y, QImage::Format_RGBA8888);
withMainThreadContext([&] {
@ -668,32 +625,9 @@ QImage OpenGLDisplayPlugin::getScreenshot() const {
Context::ReadPixels(0, 0, _compositeFramebuffer->size.x, _compositeFramebuffer->size.y, enums::PixelDataFormat::RGBA, enums::PixelDataType::UnsignedByte, screenshot.bits());
});
return screenshot.mirrored(false, true);
}
uint32_t OpenGLDisplayPlugin::getSceneTextureId() const {
if (!_currentSceneTexture) {
return 0;
}
return _currentSceneTexture->getHardwareId();
}
uint32_t OpenGLDisplayPlugin::getOverlayTextureId() const {
if (!_currentOverlayTexture) {
return 0;
}
return _currentOverlayTexture->getHardwareId();
}
void OpenGLDisplayPlugin::eyeViewport(Eye eye) const {
using namespace oglplus;
uvec2 vpSize = _compositeFramebuffer->size;
vpSize.x /= 2;
uvec2 vpPos;
if (eye == Eye::Right) {
vpPos.x = vpSize.x;
}
Context::Viewport(vpPos.x, vpPos.y, vpSize.x, vpSize.y);
#else
return QImage();
#endif
}
glm::uvec2 OpenGLDisplayPlugin::getSurfacePixels() const {
@ -719,14 +653,7 @@ bool OpenGLDisplayPlugin::hasFocus() const {
return window ? window->hasFocus() : false;
}
void OpenGLDisplayPlugin::useProgram(const ProgramPtr& program) {
if (_activeProgram != program) {
program->Bind();
_activeProgram = program;
}
}
void OpenGLDisplayPlugin::assertIsRenderThread() const {
void OpenGLDisplayPlugin::assertNotPresentThread() const {
Q_ASSERT(QThread::currentThread() != _presentThread);
}
@ -735,8 +662,18 @@ void OpenGLDisplayPlugin::assertIsPresentThread() const {
}
bool OpenGLDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
withRenderThreadLock([&] {
withNonPresentThreadLock([&] {
_compositeOverlayAlpha = _overlayAlpha;
});
return Parent::beginFrameRender(frameIndex);
}
ivec4 OpenGLDisplayPlugin::eyeViewport(Eye eye) const {
uvec2 vpSize = _currentFrame->framebuffer->getSize();
vpSize.x /= 2;
uvec2 vpPos;
if (eye == Eye::Right) {
vpPos.x = vpSize.x;
}
return ivec4(vpPos, vpSize);
}

View file

@ -11,18 +11,16 @@
#include <condition_variable>
#include <memory>
#include <queue>
#include <QtCore/QTimer>
#include <QtGui/QImage>
#include <GLMHelpers.h>
#include <SimpleMovingAverage.h>
#include <gl/OglplusHelpers.h>
#include <gl/GLEscrow.h>
#include <shared/RateCounter.h>
#define THREADED_PRESENT 1
class OpenGLDisplayPlugin : public DisplayPlugin {
Q_OBJECT
Q_PROPERTY(float overlayAlpha MEMBER _overlayAlpha)
@ -39,13 +37,12 @@ public:
// between the main thread and the presentation thread
bool activate() override final;
void deactivate() override final;
bool isRenderThread() const override final;
bool eventFilter(QObject* receiver, QEvent* event) override;
bool isDisplayVisible() const override { return true; }
void submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) override;
void submitOverlayTexture(const gpu::TexturePointer& overlayTexture) override;
void submitFrame(const gpu::FramePointer& newFrame) override;
glm::uvec2 getRecommendedRenderSize() const override {
return getSurfacePixels();
@ -65,11 +62,7 @@ public:
bool beginFrameRender(uint32_t frameIndex) override;
protected:
#if THREADED_PRESENT
friend class PresentThread;
#endif
uint32_t getSceneTextureId() const;
uint32_t getOverlayTextureId() const;
glm::uvec2 getSurfaceSize() const;
glm::uvec2 getSurfacePixels() const;
@ -93,39 +86,29 @@ protected:
// Returns true on successful activation
virtual bool internalActivate() { return true; }
virtual void internalDeactivate() {}
virtual void cleanupForSceneTexture(const gpu::TexturePointer& sceneTexture);
// Plugin specific functionality to send the composed scene to the output window or device
virtual void internalPresent();
void withMainThreadContext(std::function<void()> f) const;
void useProgram(const ProgramPtr& program);
void present();
void updateTextures();
void drawUnitQuad();
void swapBuffers();
void eyeViewport(Eye eye) const;
virtual void updateFrameData();
QThread* _presentThread{ nullptr };
ProgramPtr _program;
int32_t _mvpUniform { -1 };
int32_t _alphaUniform { -1 };
ShapeWrapperPtr _plane;
void withMainThreadContext(std::function<void()> f) const;
void present();
void swapBuffers();
ivec4 eyeViewport(Eye eye) const;
QThread* _presentThread{ nullptr };
std::queue<gpu::FramePointer> _newFrameQueue;
RateCounter<> _droppedFrameRate;
RateCounter<> _newFrameRate;
RateCounter<> _presentRate;
QMap<gpu::TexturePointer, uint32_t> _sceneTextureToFrameIndexMap;
uint32_t _currentPresentFrameIndex { 0 };
float _compositeOverlayAlpha{ 1.0f };
gpu::FramePointer _currentFrame;
gpu::PipelinePointer _overlayPipeline;
gpu::PipelinePointer _presentPipeline;
gpu::PipelinePointer _cursorPipeline;
float _compositeOverlayAlpha { 1.0f };
gpu::TexturePointer _currentSceneTexture;
gpu::TexturePointer _currentOverlayTexture;
TextureEscrow _sceneTextureEscrow;
TextureEscrow _overlayTextureEscrow;
bool _vsyncSupported { false };
@ -133,14 +116,13 @@ protected:
QImage image;
vec2 hotSpot;
uvec2 size;
uint32_t texture { 0 };
gpu::TexturePointer texture;
};
std::map<uint16_t, CursorData> _cursorsData;
BasicFramebufferWrapperPtr _compositeFramebuffer;
bool _lockCurrentTexture { false };
void assertIsRenderThread() const;
void assertNotPresentThread() const;
void assertIsPresentThread() const;
template<typename F>
@ -151,8 +133,8 @@ protected:
}
template<typename F>
void withRenderThreadLock(F f) const {
assertIsRenderThread();
void withNonPresentThreadLock(F f) const {
assertNotPresentThread();
Lock lock(_presentMutex);
f();
}
@ -161,7 +143,6 @@ private:
// Any resource shared by the main thread and the presentation thread must
// be serialized through this mutex
mutable Mutex _presentMutex;
ProgramPtr _activeProgram;
float _overlayAlpha{ 1.0f };
};

View file

@ -22,9 +22,10 @@
#include <CursorManager.h>
#include <gl/GLWidget.h>
#include <shared/NsightHelpers.h>
#include <GeometryCache.h>
#include <gpu/DrawUnitQuadTexcoord_vert.h>
#include <gpu/DrawTexture_frag.h>
#include <gpu/Context.h>
#include <gpu/gl/GLBackend.h>
#include <PathUtils.h>
@ -39,6 +40,15 @@ static const bool DEFAULT_MONO_VIEW = true;
static const int NUMBER_OF_HANDS = 2;
static const glm::mat4 IDENTITY_MATRIX;
//#define LIVE_SHADER_RELOAD 1
static QString readFile(const QString& filename) {
QFile file(filename);
file.open(QFile::Text | QFile::ReadOnly);
QString result;
result.append(QTextStream(&file).readAll());
return result;
}
glm::uvec2 HmdDisplayPlugin::getRecommendedUiSize() const {
return CompositorHelper::VIRTUAL_SCREEN_SIZE;
@ -68,6 +78,7 @@ bool HmdDisplayPlugin::internalActivate() {
_eyeInverseProjections[eye] = glm::inverse(_eyeProjections[eye]);
});
#if 0
if (_previewTextureID == 0) {
QImage previewTexture(PathUtils::resourcesPath() + "images/preview.png");
if (!previewTexture.isNull()) {
@ -83,18 +94,138 @@ bool HmdDisplayPlugin::internalActivate() {
_firstPreview = true;
}
}
#endif
return Parent::internalActivate();
}
void HmdDisplayPlugin::internalDeactivate() {
if (_previewTextureID != 0) {
glDeleteTextures(1, &_previewTextureID);
_previewTextureID = 0;
}
Parent::internalDeactivate();
}
extern glm::vec3 getPoint(float yaw, float pitch);
void HmdDisplayPlugin::OverlayRender::build() {
auto geometryCache = DependencyManager::get<GeometryCache>();
vertices = std::make_shared<gpu::Buffer>();
indices = std::make_shared<gpu::Buffer>();
//UV mapping source: http://www.mvps.org/directx/articles/spheremap.htm
static const float fov = CompositorHelper::VIRTUAL_UI_TARGET_FOV.y;
static const float aspectRatio = CompositorHelper::VIRTUAL_UI_ASPECT_RATIO;
static const uint16_t stacks = 128;
static const uint16_t slices = 64;
Vertex vertex;
// Compute vertices positions and texture UV coordinate
// Create and write to buffer
for (int i = 0; i < stacks; i++) {
vertex.uv.y = (float)i / (float)(stacks - 1); // First stack is 0.0f, last stack is 1.0f
// abs(theta) <= fov / 2.0f
float pitch = -fov * (vertex.uv.y - 0.5f);
for (int j = 0; j < slices; j++) {
vertex.uv.x = (float)j / (float)(slices - 1); // First slice is 0.0f, last slice is 1.0f
// abs(phi) <= fov * aspectRatio / 2.0f
float yaw = -fov * aspectRatio * (vertex.uv.x - 0.5f);
vertex.pos = getPoint(yaw, pitch);
vertices->append(sizeof(Vertex), (gpu::Byte*)&vertex);
}
}
// Compute number of indices needed
static const int VERTEX_PER_TRANGLE = 3;
static const int TRIANGLE_PER_RECTANGLE = 2;
int numberOfRectangles = (slices - 1) * (stacks - 1);
indexCount = numberOfRectangles * TRIANGLE_PER_RECTANGLE * VERTEX_PER_TRANGLE;
// Compute indices order
std::vector<GLushort> indices;
for (int i = 0; i < stacks - 1; i++) {
for (int j = 0; j < slices - 1; j++) {
GLushort bottomLeftIndex = i * slices + j;
GLushort bottomRightIndex = bottomLeftIndex + 1;
GLushort topLeftIndex = bottomLeftIndex + slices;
GLushort topRightIndex = topLeftIndex + 1;
// FIXME make a z-order curve for better vertex cache locality
indices.push_back(topLeftIndex);
indices.push_back(bottomLeftIndex);
indices.push_back(topRightIndex);
indices.push_back(topRightIndex);
indices.push_back(bottomLeftIndex);
indices.push_back(bottomRightIndex);
}
}
this->indices->append(indices);
format = std::make_shared<gpu::Stream::Format>(); // 1 for everyone
format->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0);
format->setAttribute(gpu::Stream::TEXCOORD, gpu::Stream::TEXCOORD, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV));
uniformBuffers[0] = std::make_shared<gpu::Buffer>(sizeof(Uniforms), nullptr);
uniformBuffers[1] = std::make_shared<gpu::Buffer>(sizeof(Uniforms), nullptr);
updatePipeline();
}
void HmdDisplayPlugin::OverlayRender::updatePipeline() {
static const QString vsFile = PathUtils::resourcesPath() + "/shaders/hmd_ui_glow.vert";
static const QString fsFile = PathUtils::resourcesPath() + "/shaders/hmd_ui_glow.frag";
#if LIVE_SHADER_RELOAD
static qint64 vsBuiltAge = 0;
static qint64 fsBuiltAge = 0;
QFileInfo vsInfo(vsFile);
QFileInfo fsInfo(fsFile);
auto vsAge = vsInfo.lastModified().toMSecsSinceEpoch();
auto fsAge = fsInfo.lastModified().toMSecsSinceEpoch();
if (!pipeline || vsAge > vsBuiltAge || fsAge > fsBuiltAge) {
vsBuiltAge = vsAge;
fsBuiltAge = fsAge;
#else
if (!pipeline) {
#endif
QString vsSource = readFile(vsFile);
QString fsSource = readFile(fsFile);
auto vs = gpu::Shader::createVertex(vsSource.toLocal8Bit().toStdString());
auto ps = gpu::Shader::createPixel(fsSource.toLocal8Bit().toStdString());
auto program = gpu::Shader::createProgram(vs, ps);
gpu::gl::GLBackend::makeProgram(*program, gpu::Shader::BindingSet());
this->uniformsLocation = program->getBuffers().findLocation("overlayBuffer");
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
state->setDepthTest(gpu::State::DepthTest(false));
state->setBlendFunction(true,
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
pipeline = gpu::Pipeline::create(program, state);
}
}
void HmdDisplayPlugin::OverlayRender::render() {
for_each_eye([&](Eye eye){
uniforms.mvp = mvps[eye];
uniformBuffers[eye]->setSubData(0, uniforms);
});
gpu::Batch batch;
batch.enableStereo(false);
batch.setResourceTexture(0, plugin._currentFrame->overlay);
batch.setPipeline(pipeline);
batch.setInputFormat(format);
gpu::BufferView posView(vertices, VERTEX_OFFSET, vertices->getSize(), VERTEX_STRIDE, format->getAttributes().at(gpu::Stream::POSITION)._element);
gpu::BufferView uvView(vertices, TEXTURE_OFFSET, vertices->getSize(), VERTEX_STRIDE, format->getAttributes().at(gpu::Stream::TEXCOORD)._element);
batch.setInputBuffer(gpu::Stream::POSITION, posView);
batch.setInputBuffer(gpu::Stream::TEXCOORD, uvView);
batch.setIndexBuffer(gpu::UINT16, indices, 0);
for_each_eye([&](Eye eye){
batch.setUniformBuffer(uniformsLocation, uniformBuffers[eye]);
batch.setViewportTransform(plugin.eyeViewport(eye));
batch.drawIndexed(gpu::TRIANGLES, indexCount);
});
// FIXME use stereo information input to set both MVPs in the uniforms
plugin._backend->render(batch);
}
void HmdDisplayPlugin::customizeContext() {
Parent::customizeContext();
// Only enable mirroring if we know vsync is disabled
@ -103,32 +234,15 @@ void HmdDisplayPlugin::customizeContext() {
enableVsync(false);
#endif
_enablePreview = !isVsyncEnabled();
_sphereSection = loadSphereSection(_program, CompositorHelper::VIRTUAL_UI_TARGET_FOV.y, CompositorHelper::VIRTUAL_UI_ASPECT_RATIO);
using namespace oglplus;
if (!_enablePreview) {
const std::string version("#version 410 core\n");
compileProgram(_previewProgram, version + DrawUnitQuadTexcoord_vert, version + DrawTexture_frag);
_previewUniforms.previewTexture = Uniform<int>(*_previewProgram, "colorMap").Location();
}
_overlay.build();
#if 0
updateReprojectionProgram();
updateOverlayProgram();
#ifdef HMD_HAND_LASER_SUPPORT
updateLaserProgram();
_laserGeometry = loadLaser(_laserProgram);
#endif
}
//#define LIVE_SHADER_RELOAD 1
static QString readFile(const QString& filename) {
QFile file(filename);
file.open(QFile::Text | QFile::ReadOnly);
QString result;
result.append(QTextStream(&file).readAll());
return result;
}
#if 0
void HmdDisplayPlugin::updateReprojectionProgram() {
static const QString vsFile = PathUtils::resourcesPath() + "/shaders/hmd_reproject.vert";
static const QString fsFile = PathUtils::resourcesPath() + "/shaders/hmd_reproject.frag";
@ -161,11 +275,11 @@ void HmdDisplayPlugin::updateReprojectionProgram() {
qWarning() << "Error building reprojection shader " << error.what();
}
}
}
#endif
#ifdef HMD_HAND_LASER_SUPPORT
void HmdDisplayPlugin::updateLaserProgram() {
#if 0
static const QString vsFile = PathUtils::resourcesPath() + "/shaders/hmd_hand_lasers.vert";
static const QString gsFile = PathUtils::resourcesPath() + "/shaders/hmd_hand_lasers.geom";
static const QString fsFile = PathUtils::resourcesPath() + "/shaders/hmd_hand_lasers.frag";
@ -204,56 +318,16 @@ void HmdDisplayPlugin::updateLaserProgram() {
qWarning() << "Error building hand laser composite shader " << error.what();
}
}
}
#endif
void HmdDisplayPlugin::updateOverlayProgram() {
static const QString vsFile = PathUtils::resourcesPath() + "/shaders/hmd_ui_glow.vert";
static const QString fsFile = PathUtils::resourcesPath() + "/shaders/hmd_ui_glow.frag";
#if LIVE_SHADER_RELOAD
static qint64 vsBuiltAge = 0;
static qint64 fsBuiltAge = 0;
QFileInfo vsInfo(vsFile);
QFileInfo fsInfo(fsFile);
auto vsAge = vsInfo.lastModified().toMSecsSinceEpoch();
auto fsAge = fsInfo.lastModified().toMSecsSinceEpoch();
if (!_overlayProgram || vsAge > vsBuiltAge || fsAge > fsBuiltAge) {
vsBuiltAge = vsAge;
fsBuiltAge = fsAge;
#else
if (!_overlayProgram) {
#endif
QString vsSource = readFile(vsFile);
QString fsSource = readFile(fsFile);
ProgramPtr program;
try {
compileProgram(program, vsSource.toLocal8Bit().toStdString(), fsSource.toLocal8Bit().toStdString());
if (program) {
using namespace oglplus;
_overlayUniforms.mvp = Uniform<glm::mat4>(*program, "mvp").Location();
_overlayUniforms.alpha = Uniform<float>(*program, "alpha").Location();
_overlayUniforms.glowColors = Uniform<glm::vec4>(*program, "glowColors").Location();
_overlayUniforms.glowPoints = Uniform<glm::vec4>(*program, "glowPoints").Location();
_overlayUniforms.resolution = Uniform<glm::vec2>(*program, "resolution").Location();
_overlayUniforms.radius = Uniform<float>(*program, "radius").Location();
_overlayProgram = program;
useProgram(_overlayProgram);
Uniform<glm::vec2>(*_overlayProgram, _overlayUniforms.resolution).Set(CompositorHelper::VIRTUAL_SCREEN_SIZE);
}
} catch (std::runtime_error& error) {
qWarning() << "Error building overlay composite shader " << error.what();
}
}
}
void HmdDisplayPlugin::uncustomizeContext() {
#if 0
_overlayProgram.reset();
_sphereSection.reset();
_compositeFramebuffer.reset();
_previewProgram.reset();
_reprojectionProgram.reset();
#ifdef HMD_HAND_LASER_SUPPORT
_laserProgram.reset();
_laserGeometry.reset();
#endif
@ -277,6 +351,7 @@ void HmdDisplayPlugin::compositeScene() {
#ifdef DEBUG_REPROJECTION_SHADER
_reprojectionProgram = getReprojectionProgram();
#endif
#if 0
useProgram(_reprojectionProgram);
using namespace oglplus;
@ -290,20 +365,23 @@ void HmdDisplayPlugin::compositeScene() {
glUniformMatrix4fv(_reprojectionUniforms.projectionMatrix, 2, GL_FALSE, &(_eyeProjections[0][0][0]));
_plane->UseInProgram(*_reprojectionProgram);
_plane->Draw();
#endif
}
void HmdDisplayPlugin::compositeOverlay() {
using namespace oglplus;
if (!_currentFrame) {
return;
}
auto compositorHelper = DependencyManager::get<CompositorHelper>();
glm::mat4 modelMat = compositorHelper->getModelTransform().getMatrix();
withPresentThreadLock([&] {
_presentHandLasers = _handLasers;
_presentHandPoses = _handPoses;
_presentUiModelTransform = _uiModelTransform;
});
std::array<vec2, NUMBER_OF_HANDS> handGlowPoints { { vec2(-1), vec2(-1) } };
std::array<vec2, NUMBER_OF_HANDS> handGlowPoints{ { vec2(-1), vec2(-1) } };
// compute the glow point interesections
for (int i = 0; i < NUMBER_OF_HANDS; ++i) {
if (_presentHandPoses[i] == IDENTITY_MATRIX) {
@ -353,65 +431,49 @@ void HmdDisplayPlugin::compositeOverlay() {
handGlowPoints[i] = yawPitch;
}
updateOverlayProgram();
if (!_overlayProgram) {
if (!_currentFrame->overlay) {
return;
}
useProgram(_overlayProgram);
for_each_eye([&](Eye eye){
auto modelView = glm::inverse(_currentPresentFrameInfo.presentPose * getEyeToHeadTransform(eye)) * modelMat;
_overlay.mvps[eye] = _eyeProjections[eye] * modelView;
});
// Setup the uniforms
{
if (_overlayUniforms.alpha >= 0) {
Uniform<float>(*_overlayProgram, _overlayUniforms.alpha).Set(_compositeOverlayAlpha);
}
if (_overlayUniforms.glowPoints >= 0) {
vec4 glowPoints(handGlowPoints[0], handGlowPoints[1]);
Uniform<glm::vec4>(*_overlayProgram, _overlayUniforms.glowPoints).Set(glowPoints);
}
if (_overlayUniforms.glowColors >= 0) {
std::array<glm::vec4, NUMBER_OF_HANDS> glowColors;
glowColors[0] = _presentHandLasers[0].color;
glowColors[1] = _presentHandLasers[1].color;
glProgramUniform4fv(GetName(*_overlayProgram), _overlayUniforms.glowColors, 2, &glowColors[0].r);
}
_overlay.uniforms.alpha = _compositeOverlayAlpha;
_overlay.uniforms.glowPoints = vec4(handGlowPoints[0], handGlowPoints[1]);
_overlay.uniforms.glowColors[0] = _presentHandLasers[0].color;
_overlay.uniforms.glowColors[1] = _presentHandLasers[1].color;
}
_sphereSection->Use();
for_each_eye([&](Eye eye) {
eyeViewport(eye);
auto modelView = glm::inverse(_currentPresentFrameInfo.presentPose * getEyeToHeadTransform(eye)) * modelMat;
auto mvp = _eyeProjections[eye] * modelView;
Uniform<glm::mat4>(*_overlayProgram, _overlayUniforms.mvp).Set(mvp);
_sphereSection->Draw();
});
_overlay.render();
}
void HmdDisplayPlugin::compositePointer() {
using namespace oglplus;
auto& cursorManager = Cursor::Manager::instance();
const auto& cursorData = _cursorsData[cursorManager.getCursor()->getIcon()];
auto compositorHelper = DependencyManager::get<CompositorHelper>();
useProgram(_program);
// set the alpha
Uniform<float>(*_program, _alphaUniform).Set(_compositeOverlayAlpha);
// Mouse pointer
_plane->Use();
// Reconstruct the headpose from the eye poses
auto headPosition = vec3(_currentPresentFrameInfo.presentPose[3]);
gpu::Batch batch;
batch.enableStereo(false);
batch.setProjectionTransform(mat4());
batch.setFramebuffer(_currentFrame->framebuffer);
batch.setPipeline(_cursorPipeline);
batch.setResourceTexture(0, cursorData.texture);
batch.setViewTransform(Transform());
for_each_eye([&](Eye eye) {
eyeViewport(eye);
auto eyePose = _currentPresentFrameInfo.presentPose * getEyeToHeadTransform(eye);
auto reticleTransform = compositorHelper->getReticleTransform(eyePose, headPosition);
auto mvp = _eyeProjections[eye] * reticleTransform;
Uniform<glm::mat4>(*_program, _mvpUniform).Set(mvp);
_plane->Draw();
batch.setViewportTransform(eyeViewport(eye));
batch.setModelTransform(reticleTransform);
batch.setProjectionTransform(_eyeProjections[eye]);
batch.draw(gpu::TRIANGLE_STRIP, 4);
});
// restore the alpha
Uniform<float>(*_program, _alphaUniform).Set(1.0);
_backend->render(batch);
}
void HmdDisplayPlugin::internalPresent() {
PROFILE_RANGE_EX(__FUNCTION__, 0xff00ff00, (uint64_t)presentCount())
@ -445,58 +507,45 @@ void HmdDisplayPlugin::internalPresent() {
targetViewportPosition.y = (windowSize.y - targetViewportSize.y) / 2;
}
if (_enablePreview) {
using namespace oglplus;
Context::Clear().ColorBuffer();
auto sourceSize = _compositeFramebuffer->size;
if (_monoPreview) {
sourceSize.x /= 2;
}
_compositeFramebuffer->Bound(Framebuffer::Target::Read, [&] {
Context::BlitFramebuffer(
0, 0, sourceSize.x, sourceSize.y,
targetViewportPosition.x, targetViewportPosition.y,
targetViewportPosition.x + targetViewportSize.x, targetViewportPosition.y + targetViewportSize.y,
BufferSelectBit::ColorBuffer, BlitFilter::Nearest);
});
swapBuffers();
} else if (_firstPreview || windowSize != _prevWindowSize || devicePixelRatio != _prevDevicePixelRatio) {
useProgram(_previewProgram);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
glViewport(targetViewportPosition.x, targetViewportPosition.y, targetViewportSize.x, targetViewportSize.y);
glUniform1i(_previewUniforms.previewTexture, 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, _previewTextureID);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
swapBuffers();
_firstPreview = false;
_prevWindowSize = windowSize;
_prevDevicePixelRatio = devicePixelRatio;
}
Parent::internalPresent();
//gpu::Batch presentBatch;
//presentBatch.enableStereo(false);
//presentBatch.setViewTransform(Transform());
//presentBatch.setFramebuffer(gpu::FramebufferPointer());
//presentBatch.setViewportTransform(ivec4(targetViewportPosition, targetViewportSize));
//presentBatch.setResourceTexture(0, _currentFrame->framebuffer->getRenderBuffer(0));
//presentBatch.setPipeline(_presentPipeline);
//presentBatch.draw(gpu::TRIANGLE_STRIP, 4);
//_backend->render(presentBatch);
//swapBuffers();
}
postPreview();
}
void HmdDisplayPlugin::setEyeRenderPose(uint32_t frameIndex, Eye eye, const glm::mat4& pose) {
}
void HmdDisplayPlugin::updateFrameData() {
// Check if we have old frame data to discard
withPresentThreadLock([&] {
auto itr = _frameInfos.find(_currentPresentFrameIndex);
if (itr != _frameInfos.end()) {
_frameInfos.erase(itr);
}
});
static const uint32_t INVALID_FRAME = (uint32_t)(~0);
uint32_t oldFrameIndex = _currentFrame ? _currentFrame->frameIndex : INVALID_FRAME;
Parent::updateFrameData();
uint32_t newFrameIndex = _currentFrame ? _currentFrame->frameIndex : INVALID_FRAME;
withPresentThreadLock([&] {
_currentPresentFrameInfo = _frameInfos[_currentPresentFrameIndex];
});
if (oldFrameIndex != newFrameIndex) {
withPresentThreadLock([&] {
if (oldFrameIndex != INVALID_FRAME) {
auto itr = _frameInfos.find(oldFrameIndex);
if (itr != _frameInfos.end()) {
_frameInfos.erase(itr);
}
}
if (newFrameIndex != INVALID_FRAME) {
_currentPresentFrameInfo = _frameInfos[newFrameIndex];
}
});
}
}
glm::mat4 HmdDisplayPlugin::getHeadPose() const {
@ -508,7 +557,7 @@ bool HmdDisplayPlugin::setHandLaser(uint32_t hands, HandLaserMode mode, const ve
info.mode = mode;
info.color = color;
info.direction = direction;
withRenderThreadLock([&] {
withNonPresentThreadLock([&] {
if (hands & Hand::LeftHand) {
_handLasers[0] = info;
}
@ -522,7 +571,7 @@ bool HmdDisplayPlugin::setHandLaser(uint32_t hands, HandLaserMode mode, const ve
}
void HmdDisplayPlugin::compositeExtra() {
#ifdef HMD_HAND_LASER_SUPPORT
#if 0
// If neither hand laser is activated, exit
if (!_presentHandLasers[0].valid() && !_presentHandLasers[1].valid()) {
return;
@ -592,4 +641,6 @@ void HmdDisplayPlugin::compositeExtra() {
});
glDisable(GL_BLEND);
#endif
}

View file

@ -9,18 +9,21 @@
#include <ThreadSafeValueCache.h>
#include <array>
#include <QtGlobal>
#include <Transform.h>
#include "../OpenGLDisplayPlugin.h"
#include <gpu/Format.h>
#include <gpu/Stream.h>
#ifdef Q_OS_WIN
#define HMD_HAND_LASER_SUPPORT
#endif
#include "../CompositorHelper.h"
#include "../OpenGLDisplayPlugin.h"
class HmdDisplayPlugin : public OpenGLDisplayPlugin {
using Parent = OpenGLDisplayPlugin;
public:
HmdDisplayPlugin() : _overlay( *this ) {}
bool isHmd() const override final { return true; }
float getIPD() const override final { return _ipd; }
glm::mat4 getEyeToHeadTransform(Eye eye) const override final { return _eyeOffsets[eye]; }
@ -28,7 +31,6 @@ public:
glm::mat4 getCullingProjection(const glm::mat4& baseProjection) const override final { return _cullingProjection; }
glm::uvec2 getRecommendedUiSize() const override final;
glm::uvec2 getRecommendedRenderSize() const override final { return _renderTargetSize; }
void setEyeRenderPose(uint32_t frameIndex, Eye eye, const glm::mat4& pose) override final;
bool isDisplayVisible() const override { return isHmdMounted(); }
QRect getRecommendedOverlayRect() const override final;
@ -65,6 +67,9 @@ protected:
}
};
Transform _uiModelTransform;
std::array<HandLaserInfo, 2> _handLasers;
std::array<glm::mat4, 2> _handPoses;
@ -96,10 +101,7 @@ protected:
FrameInfo _currentRenderFrameInfo;
private:
void updateOverlayProgram();
#ifdef HMD_HAND_LASER_SUPPORT
void updateLaserProgram();
#endif
void updateReprojectionProgram();
bool _enablePreview { false };
@ -107,26 +109,53 @@ private:
bool _enableReprojection { true };
bool _firstPreview { true };
ProgramPtr _overlayProgram;
struct OverlayUniforms {
int32_t mvp { -1 };
int32_t alpha { -1 };
int32_t glowColors { -1 };
int32_t glowPoints { -1 };
int32_t resolution { -1 };
int32_t radius { -1 };
} _overlayUniforms;
float _previewAspect { 0 };
glm::uvec2 _prevWindowSize { 0, 0 };
qreal _prevDevicePixelRatio { 0 };
struct OverlayRender {
OverlayRender(HmdDisplayPlugin& plugin) : plugin(plugin) {};
HmdDisplayPlugin& plugin;
gpu::Stream::FormatPointer format;
gpu::BufferPointer vertices;
gpu::BufferPointer indices;
uint32_t indexCount { 0 };
gpu::PipelinePointer pipeline;
int32_t uniformsLocation { -1 };
// FIXME this is stupid, use the built in transformation pipeline
std::array<gpu::BufferPointer, 2> uniformBuffers;
std::array<mat4, 2> mvps;
struct Uniforms {
mat4 mvp;
vec4 glowPoints { -1 };
vec4 glowColors[2];
vec2 resolution { CompositorHelper::VIRTUAL_SCREEN_SIZE };
float radius { 0.005f };
float alpha { 1.0f };
} uniforms;
struct Vertex {
vec3 pos;
vec2 uv;
} vertex;
static const size_t VERTEX_OFFSET { offsetof(Vertex, pos) };
static const size_t TEXTURE_OFFSET { offsetof(Vertex, uv) };
static const int VERTEX_STRIDE { sizeof(Vertex) };
void build();
void updatePipeline();
void render();
} _overlay;
#if 0
ProgramPtr _previewProgram;
struct PreviewUniforms {
int32_t previewTexture { -1 };
} _previewUniforms;
float _previewAspect { 0 };
GLuint _previewTextureID { 0 };
glm::uvec2 _prevWindowSize { 0, 0 };
qreal _prevDevicePixelRatio { 0 };
ProgramPtr _reprojectionProgram;
struct ReprojectionUniforms {
int32_t reprojectionMatrix { -1 };
@ -134,9 +163,6 @@ private:
int32_t projectionMatrix { -1 };
} _reprojectionUniforms;
ShapeWrapperPtr _sphereSection;
#ifdef HMD_HAND_LASER_SUPPORT
ProgramPtr _laserProgram;
struct LaserUniforms {
int32_t mvp { -1 };
@ -145,4 +171,3 @@ private:
ShapeWrapperPtr _laserGeometry;
#endif
};

View file

@ -8,52 +8,56 @@
#include "InterleavedStereoDisplayPlugin.h"
static const char * INTERLEAVED_TEXTURED_VS = R"VS(#version 410 core
#pragma line __LINE__
#include <gpu/StandardShaderLib.h>
#include <gpu/Pipeline.h>
#include <gpu/Batch.h>
#include <gpu/Context.h>
in vec3 Position;
in vec2 TexCoord;
static const char* INTERLEAVED_SRGB_TO_LINEAR_FRAG = R"SCRIBE(
out vec2 vTexCoord;
struct TextureData {
ivec2 textureSize;
};
void main() {
gl_Position = vec4(Position, 1);
vTexCoord = TexCoord;
}
layout(std140) uniform textureDataBuffer {
TextureData textureData;
};
)VS";
uniform sampler2D colorMap;
static const char * INTERLEAVED_TEXTURED_FS = R"FS(#version 410 core
#pragma line __LINE__
in vec2 varTexCoord0;
uniform sampler2D sampler;
uniform ivec2 textureSize;
out vec4 outFragColor;
in vec2 vTexCoord;
out vec4 FragColor;
void main() {
ivec2 texCoord = ivec2(floor(vTexCoord * textureSize));
void main(void) {
ivec2 texCoord = ivec2(floor(varTexCoord0 * textureData.textureSize));
texCoord.x /= 2;
int row = int(floor(gl_FragCoord.y));
if (row % 2 > 0) {
texCoord.x += (textureSize.x / 2);
texCoord.x += (textureData.textureSize.x / 2);
}
FragColor = texelFetch(sampler, texCoord, 0); //texture(sampler, texCoord);
outFragColor = vec4(pow(texelFetch(colorMap, texCoord, 0).rgb, vec3(2.2)), 1.0);
}
)FS";
)SCRIBE";
const QString InterleavedStereoDisplayPlugin::NAME("3D TV - Interleaved");
void InterleavedStereoDisplayPlugin::customizeContext() {
StereoDisplayPlugin::customizeContext();
// Set up the stencil buffers? Or use a custom shader?
compileProgram(_interleavedProgram, INTERLEAVED_TEXTURED_VS, INTERLEAVED_TEXTURED_FS);
if (!_interleavedPresentPipeline) {
auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS();
auto ps = gpu::Shader::createPixel(std::string(INTERLEAVED_SRGB_TO_LINEAR_FRAG));
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
gpu::Shader::makeProgram(*program);
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
state->setDepthTest(gpu::State::DepthTest(false));
_interleavedPresentPipeline = gpu::Pipeline::create(program, state);
}
}
void InterleavedStereoDisplayPlugin::uncustomizeContext() {
_interleavedProgram.reset();
_interleavedPresentPipeline.reset();
StereoDisplayPlugin::uncustomizeContext();
}
@ -65,15 +69,14 @@ glm::uvec2 InterleavedStereoDisplayPlugin::getRecommendedRenderSize() const {
}
void InterleavedStereoDisplayPlugin::internalPresent() {
using namespace oglplus;
auto sceneSize = getRecommendedRenderSize();
_interleavedProgram->Bind();
Uniform<ivec2>(*_interleavedProgram, "textureSize").SetValue(sceneSize);
auto surfaceSize = getSurfacePixels();
Context::Viewport(0, 0, surfaceSize.x, surfaceSize.y);
glBindTexture(GL_TEXTURE_2D, GetName(_compositeFramebuffer->color));
_plane->Use();
_plane->Draw();
gpu::Batch presentBatch;
presentBatch.enableStereo(false);
presentBatch.setViewTransform(Transform());
presentBatch.setFramebuffer(gpu::FramebufferPointer());
presentBatch.setViewportTransform(ivec4(uvec2(0), getSurfacePixels()));
presentBatch.setResourceTexture(0, _currentFrame->framebuffer->getRenderBuffer(0));
presentBatch.setPipeline(_interleavedPresentPipeline);
presentBatch.draw(gpu::TRIANGLE_STRIP, 4);
_backend->render(presentBatch);
swapBuffers();
}

View file

@ -24,6 +24,7 @@ protected:
void internalPresent() override;
private:
ProgramPtr _interleavedProgram;
static const QString NAME;
gpu::PipelinePointer _interleavedPresentPipeline;
gpu::BufferPointer _textureDataBuffer;
};

View file

@ -7,16 +7,11 @@
//
#include "SideBySideStereoDisplayPlugin.h"
#include <GLMHelpers.h>
#include <CursorManager.h>
#include <ui-plugins/PluginContainer.h>
#include <gl/GLWidget.h>
#include "../CompositorHelper.h"
const QString SideBySideStereoDisplayPlugin::NAME("3D TV - Side by Side Stereo");
glm::uvec2 SideBySideStereoDisplayPlugin::getRecommendedRenderSize() const {
uvec2 result = Parent::getRecommendedRenderSize();
result.x *= 2;
//result.x *= 2;
return result;
}

View file

@ -101,4 +101,3 @@ void StereoDisplayPlugin::internalDeactivate() {
float StereoDisplayPlugin::getRecommendedAspectRatio() const {
return aspect(Parent::getRecommendedRenderSize());
}

View file

@ -20,6 +20,8 @@
#include <SharedUtil.h>
#include <NumericalConstants.h>
#include "Config.h"
// The GLEscrow class provides a simple mechanism for producer GL contexts to provide
// content to a consumer where the consumer is assumed to be connected to a display and
// therefore must never be blocked.

View file

@ -18,6 +18,7 @@
#include <QtGui/QOpenGLContext>
#include "GLHelpers.h"
#include "QOpenGLDebugLoggerWrapper.h"
#ifdef DEBUG
static bool enableDebugLogger = true;
@ -80,7 +81,7 @@ bool OffscreenGLCanvas::makeCurrent() {
_logger = new QOpenGLDebugLogger(this);
if (_logger->initialize()) {
connect(_logger, &QOpenGLDebugLogger::messageLogged, [](const QOpenGLDebugMessage& message) {
qDebug() << message;
OpenGLDebug::log(message);
});
_logger->disableMessages(QOpenGLDebugMessage::AnySource, QOpenGLDebugMessage::AnyType, QOpenGLDebugMessage::NotificationSeverity);
_logger->startLogging(QOpenGLDebugLogger::LoggingMode::SynchronousLogging);
@ -101,4 +102,4 @@ QObject* OffscreenGLCanvas::getContextObject() {
void OffscreenGLCanvas::moveToThreadWithContext(QThread* thread) {
moveToThread(thread);
_context->moveToThread(thread);
}
}

View file

@ -120,24 +120,9 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] =
(&::gpu::gl::GLBackend::do_popProfileRange),
};
extern std::function<uint32(const Texture& texture)> TEXTURE_ID_RESOLVER;
void GLBackend::init() {
static std::once_flag once;
std::call_once(once, [] {
TEXTURE_ID_RESOLVER = [](const Texture& texture)->uint32 {
auto object = Backend::getGPUObject<GLTexture>(texture);
if (!object) {
return 0;
}
if (object->getSyncState() != GLSyncState::Idle) {
return object->_downsampleSource._texture;
}
return object->_texture;
};
QString vendor{ (const char*)glGetString(GL_VENDOR) };
QString renderer{ (const char*)glGetString(GL_RENDERER) };
qCDebug(gpugllogging) << "GL Version: " << QString((const char*) glGetString(GL_VERSION));

View file

@ -35,12 +35,13 @@ class GLBackend : public Backend {
friend class gpu::Context;
static void init();
static Backend* createBackend();
static bool makeProgram(Shader& shader, const Shader::BindingSet& slotBindings);
protected:
explicit GLBackend(bool syncCache);
GLBackend();
public:
static bool makeProgram(Shader& shader, const Shader::BindingSet& slotBindings = Shader::BindingSet());
~GLBackend();
void render(Batch& batch) final;
@ -159,9 +160,10 @@ public:
virtual void do_setStateBlendFactor(Batch& batch, size_t paramOffset) final;
virtual void do_setStateScissorRect(Batch& batch, size_t paramOffset) final;
virtual GLuint getFramebufferID(const FramebufferPointer& framebuffer) = 0;
protected:
virtual GLuint getFramebufferID(const FramebufferPointer& framebuffer) = 0;
virtual GLFramebuffer* syncGPUObject(const Framebuffer& framebuffer) = 0;
virtual GLuint getBufferID(const Buffer& buffer) = 0;

View file

@ -23,7 +23,7 @@ public:
object = new GLBufferType(buffer, object);
}
if (0 != (buffer._flags & Buffer::DIRTY)) {
if (0 != (buffer._renderPages._flags & PageManager::DIRTY)) {
object->transfer();
}

View file

@ -42,14 +42,14 @@ public:
Size offset;
Size size;
Size currentPage { 0 };
auto data = _gpuObject.getSysmem().readData();
while (_gpuObject.getNextTransferBlock(offset, size, currentPage)) {
auto data = _gpuObject._renderSysmem.readData();
while (_gpuObject._renderPages.getNextTransferBlock(offset, size, currentPage)) {
glBufferSubData(GL_ARRAY_BUFFER, offset, size, data + offset);
(void)CHECK_GL_ERROR();
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
(void)CHECK_GL_ERROR();
_gpuObject._flags &= ~Buffer::DIRTY;
_gpuObject._renderPages._flags &= ~PageManager::DIRTY;
}
};

View file

@ -32,12 +32,12 @@ public:
Size offset;
Size size;
Size currentPage { 0 };
auto data = _gpuObject.getSysmem().readData();
while (_gpuObject.getNextTransferBlock(offset, size, currentPage)) {
auto data = _gpuObject._renderSysmem.readData();
while (_gpuObject._renderPages.getNextTransferBlock(offset, size, currentPage)) {
glNamedBufferSubData(_buffer, (GLintptr)offset, (GLsizeiptr)size, data + offset);
}
(void)CHECK_GL_ERROR();
_gpuObject._flags &= ~Buffer::DIRTY;
_gpuObject._renderPages._flags &= ~PageManager::DIRTY;
}
};

View file

@ -20,13 +20,6 @@ Context::Context() {
if (_createBackendCallback) {
_backend.reset(_createBackendCallback());
}
_frameHandler = [this](Frame& frame){
for (size_t i = 0; i < frame.batches.size(); ++i) {
_backend->_stereo = frame.stereoStates[i];
_backend->render(frame.batches[i]);
}
};
}
Context::Context(const Context& context) {
@ -35,40 +28,30 @@ Context::Context(const Context& context) {
Context::~Context() {
}
void Context::setFrameHandler(FrameHandler handler) {
_frameHandler = handler;
}
#define DEFERRED_RENDERING
void Context::beginFrame(const FramebufferPointer& outputFramebuffer, const glm::mat4& renderPose) {
_currentFrame = Frame();
_currentFrame.framebuffer = outputFramebuffer;
_currentFrame.pose = renderPose;
void Context::beginFrame(const glm::mat4& renderPose) {
assert(!_frameActive);
_frameActive = true;
_currentFrame = std::make_shared<Frame>();
_currentFrame->pose = renderPose;
}
void Context::append(Batch& batch) {
if (!_frameActive) {
qWarning() << "Batch executed outside of frame boundaries";
return;
}
#ifdef DEFERRED_RENDERING
_currentFrame.batches.emplace_back(batch);
_currentFrame.stereoStates.emplace_back(_stereo);
#else
_backend->_stereo = _stereo;
_backend->render(batch);
#endif
_currentFrame->batches.push_back(batch);
}
void Context::endFrame() {
#ifdef DEFERRED_RENDERING
if (_frameHandler) {
_frameHandler(_currentFrame);
}
#endif
_currentFrame = Frame();
FramePointer Context::endFrame() {
assert(_frameActive);
auto result = _currentFrame;
_currentFrame.reset();
_frameActive = false;
result->stereoState = _stereo;
result->finish();
return result;
}
@ -111,16 +94,10 @@ void Context::getStereoViews(mat4* eyeViews) const {
}
}
void Context::syncCache() {
PROFILE_RANGE(__FUNCTION__);
_backend->syncCache();
}
void Context::downloadFramebuffer(const FramebufferPointer& srcFramebuffer, const Vec4i& region, QImage& destImage) {
_backend->downloadFramebuffer(srcFramebuffer, region, destImage);
}
void Context::getStats(ContextStats& stats) const {
_backend->getStats(stats);
}

View file

@ -51,8 +51,9 @@ class Backend {
public:
virtual~ Backend() {};
virtual void render(Batch& batch) = 0;
void setStereoState(const StereoState& stereo) { _stereo = stereo; }
virtual void render(Batch& batch) = 0;
virtual void syncCache() = 0;
virtual void downloadFramebuffer(const FramebufferPointer& srcFramebuffer, const Vec4i& region, QImage& destImage) = 0;
@ -139,10 +140,11 @@ public:
Context();
~Context();
void setFrameHandler(FrameHandler handler);
void beginFrame(const FramebufferPointer& outputFramebuffer, const glm::mat4& renderPose = glm::mat4());
void beginFrame(const glm::mat4& renderPose = glm::mat4());
void append(Batch& batch);
void endFrame();
FramePointer endFrame();
const BackendPointer& getBackend() const { return _backend; }
void enableStereo(bool enable = true);
bool isStereo();
@ -150,7 +152,6 @@ public:
void setStereoViews(const mat4 eyeViews[2]);
void getStereoProjections(mat4* eyeProjections) const;
void getStereoViews(mat4* eyeViews) const;
void syncCache();
// Downloading the Framebuffer is a synchronous action that is not efficient.
// It s here for convenience to easily capture a snapshot
@ -171,10 +172,9 @@ public:
protected:
Context(const Context& context);
std::unique_ptr<Backend> _backend;
std::shared_ptr<Backend> _backend;
bool _frameActive { false };
Frame _currentFrame;
FrameHandler _frameHandler;
FramePointer _currentFrame;
StereoState _stereo;
// This function can only be called by "static Shader::makeProgram()"

View file

@ -11,20 +11,23 @@
#include <stdint.h>
#include <memory>
#include <mutex>
#include <vector>
#include <functional>
#include <glm/glm.hpp>
namespace gpu {
using Mutex = std::mutex;
using Lock = std::unique_lock<Mutex>;
class Batch;
class Backend;
using BackendPointer = std::shared_ptr<Backend>;
class Context;
using ContextPointer = std::shared_ptr<Context>;
class GPUObject;
class Frame;
using FramePointer = std::shared_ptr<Frame>;
using FrameHandler = std::function<void(Frame& frame)>;
using Stamp = int;
using uint32 = uint32_t;

View file

@ -0,0 +1,53 @@
//
// Created by Bradley Austin Davis on 2016/07/26
// Copyright 2013-2016 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 "Frame.h"
#include <unordered_set>
using namespace gpu;
Frame::~Frame() {
if (framebuffer && framebufferRecycler) {
framebufferRecycler(framebuffer);
framebuffer.reset();
}
if (overlay && overlayRecycler) {
overlayRecycler(overlay);
overlay.reset();
}
}
void Frame::finish() {
std::unordered_set<Buffer*> seenBuffers;
for (Batch& batch : batches) {
for (auto& bufferCacheItem : batch._buffers._items) {
const BufferPointer& buffer = bufferCacheItem._data;
if (!buffer) {
continue;
}
if (!buffer->isDirty()) {
continue;
}
if (seenBuffers.count(buffer.get())) {
continue;
}
seenBuffers.insert(buffer.get());
bufferUpdates.push_back({ buffer, buffer->getUpdate() });
}
}
}
void Frame::preRender() {
for (auto& bufferUpdate : bufferUpdates) {
const BufferPointer& buffer = bufferUpdate.first;
const Buffer::Update& update = bufferUpdate.second;
buffer->applyUpdate(update);
}
bufferUpdates.clear();
}

View file

@ -8,20 +8,44 @@
#ifndef hifi_gpu_Frame_h
#define hifi_gpu_Frame_h
#include <functional>
#include "Forward.h"
#include "Batch.h"
#include "Resource.h"
namespace gpu {
class Frame {
public:
/// The sensor pose used for rendering the frame, only applicable for HMDs
glm::mat4 pose;
/// The collection of batches which make up the frame
std::vector<Batch> batches;
std::vector<StereoState> stereoStates;
/// The destination framebuffer in which the results will be placed
FramebufferPointer framebuffer;
};
class Frame {
public:
using Batches = std::vector<Batch>;
using FramebufferRecycler = std::function<void(const FramebufferPointer&)>;
using OverlayRecycler = std::function<void(const TexturePointer&)>;
using BufferUpdate = std::pair<BufferPointer, Buffer::Update>;
using BufferUpdates = std::vector<BufferUpdate>;
virtual ~Frame();
void finish();
void preRender();
StereoState stereoState;
uint32_t frameIndex{ 0 };
/// The sensor pose used for rendering the frame, only applicable for HMDs
Mat4 pose;
/// The collection of batches which make up the frame
Batches batches;
/// The destination framebuffer in which the results will be placed
FramebufferPointer framebuffer;
/// The destination texture containing the 2D overlay
TexturePointer overlay;
/// How to process the framebuffer when the frame dies. MUST BE THREAD SAFE
FramebufferRecycler framebufferRecycler;
/// How to process the overlay texture when the frame dies. MUST BE THREAD SAFE
OverlayRecycler overlayRecycler;
BufferUpdates bufferUpdates;
};
};

View file

@ -78,7 +78,7 @@ const float AllocationDebugger::K = 1024.0f;
static AllocationDebugger allocationDebugger;
Resource::Size Resource::Sysmem::allocateMemory(Byte** dataAllocated, Size size) {
Size Sysmem::allocateMemory(Byte** dataAllocated, Size size) {
allocationDebugger += size;
if ( !dataAllocated ) {
qWarning() << "Buffer::Sysmem::allocateMemory() : Must have a valid dataAllocated pointer.";
@ -102,40 +102,40 @@ Resource::Size Resource::Sysmem::allocateMemory(Byte** dataAllocated, Size size)
return newSize;
}
void Resource::Sysmem::deallocateMemory(Byte* dataAllocated, Size size) {
void Sysmem::deallocateMemory(Byte* dataAllocated, Size size) {
allocationDebugger -= size;
if (dataAllocated) {
delete[] dataAllocated;
}
}
Resource::Sysmem::Sysmem() {}
Sysmem::Sysmem() {}
Resource::Sysmem::Sysmem(Size size, const Byte* bytes) {
Sysmem::Sysmem(Size size, const Byte* bytes) {
if (size > 0 && bytes) {
setData(_size, bytes);
}
}
Resource::Sysmem::Sysmem(const Sysmem& sysmem) {
Sysmem::Sysmem(const Sysmem& sysmem) {
if (sysmem.getSize() > 0) {
allocate(sysmem._size);
setData(_size, sysmem._data);
}
}
Resource::Sysmem& Resource::Sysmem::operator=(const Sysmem& sysmem) {
Sysmem& Sysmem::operator=(const Sysmem& sysmem) {
setData(sysmem.getSize(), sysmem.readData());
return (*this);
}
Resource::Sysmem::~Sysmem() {
Sysmem::~Sysmem() {
deallocateMemory( _data, _size );
_data = NULL;
_size = 0;
}
Resource::Size Resource::Sysmem::allocate(Size size) {
Size Sysmem::allocate(Size size) {
if (size != _size) {
Byte* newData = NULL;
Size newSize = 0;
@ -156,7 +156,7 @@ Resource::Size Resource::Sysmem::allocate(Size size) {
return _size;
}
Resource::Size Resource::Sysmem::resize(Size size) {
Size Sysmem::resize(Size size) {
if (size != _size) {
Byte* newData = NULL;
Size newSize = 0;
@ -182,7 +182,7 @@ Resource::Size Resource::Sysmem::resize(Size size) {
return _size;
}
Resource::Size Resource::Sysmem::setData( Size size, const Byte* bytes ) {
Size Sysmem::setData( Size size, const Byte* bytes ) {
if (allocate(size) == size) {
if (size && bytes) {
memcpy( _data, bytes, _size );
@ -191,7 +191,7 @@ Resource::Size Resource::Sysmem::setData( Size size, const Byte* bytes ) {
return _size;
}
Resource::Size Resource::Sysmem::setSubData( Size offset, Size size, const Byte* bytes) {
Size Sysmem::setSubData( Size offset, Size size, const Byte* bytes) {
if (size && ((offset + size) <= getSize()) && bytes) {
memcpy( _data + offset, bytes, size );
return size;
@ -199,7 +199,7 @@ Resource::Size Resource::Sysmem::setSubData( Size offset, Size size, const Byte*
return 0;
}
Resource::Size Resource::Sysmem::append(Size size, const Byte* bytes) {
Size Sysmem::append(Size size, const Byte* bytes) {
if (size > 0) {
Size oldSize = getSize();
Size totalSize = oldSize + size;
@ -241,7 +241,7 @@ Buffer::Size Buffer::getBufferGPUMemoryUsage() {
}
Buffer::Buffer(Size pageSize) :
_pageSize(pageSize) {
_pages(pageSize) {
_bufferCPUCount++;
}
@ -249,12 +249,12 @@ Buffer::Buffer(Size size, const Byte* bytes, Size pageSize) : Buffer(pageSize) {
setData(size, bytes);
}
Buffer::Buffer(const Buffer& buf) : Buffer(buf._pageSize) {
Buffer::Buffer(const Buffer& buf) : Buffer(buf._pages._pageSize) {
setData(buf.getSize(), buf.getData());
}
Buffer& Buffer::operator=(const Buffer& buf) {
const_cast<Size&>(_pageSize) = buf._pageSize;
const_cast<Size&>(_pages._pageSize) = buf._pages._pageSize;
setData(buf.getSize(), buf.getData());
return (*this);
}
@ -266,14 +266,10 @@ Buffer::~Buffer() {
Buffer::Size Buffer::resize(Size size) {
_end = size;
auto prevSize = editSysmem().getSize();
auto prevSize = _sysmem.getSize();
if (prevSize < size) {
auto newPages = getRequiredPageCount();
auto newSize = newPages * _pageSize;
editSysmem().resize(newSize);
// All new pages start off as clean, because they haven't been populated by data
_pages.resize(newPages, 0);
Buffer::updateBufferCPUMemoryUsage(prevSize, newSize);
_sysmem.resize(_pages.accommodate(_end));
Buffer::updateBufferCPUMemoryUsage(prevSize, _sysmem.getSize());
}
return _end;
}
@ -282,28 +278,45 @@ void Buffer::markDirty(Size offset, Size bytes) {
if (!bytes) {
return;
}
_flags |= DIRTY;
// Find the starting page
Size startPage = (offset / _pageSize);
// Non-zero byte count, so at least one page is dirty
Size pageCount = 1;
// How much of the page is after the offset?
Size remainder = _pageSize - (offset % _pageSize);
// If there are more bytes than page space remaining, we need to increase the page count
if (bytes > remainder) {
// Get rid of the amount that will fit in the current page
bytes -= remainder;
pageCount += (bytes / _pageSize);
if (bytes % _pageSize) {
++pageCount;
_pages.markRegion(offset, bytes);
}
void Buffer::applyUpdate(const Update& update) {
_renderSysmem.resize(update.size);
_renderPages = update.pages;
update.updateOperator(_renderSysmem);
}
Buffer::Update Buffer::getUpdate() const {
static Update EMPTY_UPDATE;
if (!_pages) {
return EMPTY_UPDATE;
}
Update result;
result.pages = _pages;
result.size = _sysmem.getSize();
Size pageSize = _pages._pageSize;
PageManager::Pages dirtyPages = _pages.getMarkedPages();
std::vector<uint8> dirtyPageData;
dirtyPageData.resize(dirtyPages.size() * pageSize);
for (Size i = 0; i < dirtyPages.size(); ++i) {
Size page = dirtyPages[i];
Size sourceOffset = page * pageSize;
Size destOffset = i * pageSize;
memcpy(dirtyPageData.data() + destOffset, _sysmem.readData() + sourceOffset, pageSize);
}
result.updateOperator = [pageSize, dirtyPages, dirtyPageData](Sysmem& dest){
for (Size i = 0; i < dirtyPages.size(); ++i) {
Size page = dirtyPages[i];
Size sourceOffset = i * pageSize;
Size destOffset = page * pageSize;
memcpy(dest.editData() + destOffset, dirtyPageData.data() + sourceOffset, pageSize);
}
}
// Mark the pages dirty
for (Size i = 0; i < pageCount; ++i) {
_pages[i + startPage] |= DIRTY;
}
};
return result;
}
@ -333,14 +346,6 @@ Buffer::Size Buffer::getSize() const {
return _end;
}
Buffer::Size Buffer::getRequiredPageCount() const {
Size result = _end / _pageSize;
if (_end % _pageSize) {
++result;
}
return result;
}
const Element BufferView::DEFAULT_ELEMENT = Element( gpu::SCALAR, gpu::UINT8, gpu::RAW );
BufferView::BufferView() :

View file

@ -25,11 +25,69 @@
namespace gpu {
// Sysmem is the underneath cache for the data in ram of a resource.
class Sysmem {
public:
static const Size NOT_ALLOCATED = (Size)-1;
Sysmem();
Sysmem(Size size, const Byte* bytes);
Sysmem(const Sysmem& sysmem); // deep copy of the sysmem buffer
Sysmem& operator=(const Sysmem& sysmem); // deep copy of the sysmem buffer
~Sysmem();
Size getSize() const { return _size; }
// Allocate the byte array
// \param pSize The nb of bytes to allocate, if already exist, content is lost.
// \return The nb of bytes allocated, nothing if allready the appropriate size.
Size allocate(Size pSize);
// Resize the byte array
// Keep previous data [0 to min(pSize, mSize)]
Size resize(Size pSize);
// Assign data bytes and size (allocate for size, then copy bytes if exists)
Size setData(Size size, const Byte* bytes);
// Update Sub data,
// doesn't allocate and only copy size * bytes at the offset location
// only if all fits in the existing allocated buffer
Size setSubData(Size offset, Size size, const Byte* bytes);
// Append new data at the end of the current buffer
// do a resize( size + getSIze) and copy the new data
// \return the number of bytes copied
Size append(Size size, const Byte* data);
// Access the byte array.
// The edit version allow to map data.
const Byte* readData() const { return _data; }
Byte* editData() { return _data; }
template< typename T > const T* read() const { return reinterpret_cast< T* > (_data); }
template< typename T > T* edit() { return reinterpret_cast< T* > (_data); }
// Access the current version of the sysmem, used to compare if copies are in sync
Stamp getStamp() const { return _stamp; }
static Size allocateMemory(Byte** memAllocated, Size size);
static void deallocateMemory(Byte* memDeallocated, Size size);
bool isAvailable() const { return (_data != 0); }
using Operator = std::function<void(Sysmem& sysmem)>;
private:
Stamp _stamp{ 0 };
Size _size{ 0 };
Byte* _data{ nullptr };
}; // Sysmem
class Resource {
public:
typedef size_t Size;
static const Size NOT_ALLOCATED = (Size)-1;
static const Size NOT_ALLOCATED = Sysmem::NOT_ALLOCATED;
// The size in bytes of data stored in the resource
virtual Size getSize() const = 0;
@ -47,88 +105,178 @@ public:
};
protected:
using Sysmem = gpu::Sysmem;
Resource() {}
virtual ~Resource() {}
// Sysmem is the underneath cache for the data in ram of a resource.
class Sysmem {
public:
}; // Resource
Sysmem();
Sysmem(Size size, const Byte* bytes);
Sysmem(const Sysmem& sysmem); // deep copy of the sysmem buffer
Sysmem& operator=(const Sysmem& sysmem); // deep copy of the sysmem buffer
~Sysmem();
Size getSize() const { return _size; }
struct PageManager {
static const Size DEFAULT_PAGE_SIZE = 4096;
// Allocate the byte array
// \param pSize The nb of bytes to allocate, if already exist, content is lost.
// \return The nb of bytes allocated, nothing if allready the appropriate size.
Size allocate(Size pSize);
// Resize the byte array
// Keep previous data [0 to min(pSize, mSize)]
Size resize(Size pSize);
// Assign data bytes and size (allocate for size, then copy bytes if exists)
Size setData(Size size, const Byte* bytes );
// Update Sub data,
// doesn't allocate and only copy size * bytes at the offset location
// only if all fits in the existing allocated buffer
Size setSubData(Size offset, Size size, const Byte* bytes);
// Append new data at the end of the current buffer
// do a resize( size + getSIze) and copy the new data
// \return the number of bytes copied
Size append(Size size, const Byte* data);
// Access the byte array.
// The edit version allow to map data.
const Byte* readData() const { return _data; }
Byte* editData() { return _data; }
template< typename T > const T* read() const { return reinterpret_cast< T* > ( _data ); }
template< typename T > T* edit() { return reinterpret_cast< T* > ( _data ); }
// Access the current version of the sysmem, used to compare if copies are in sync
Stamp getStamp() const { return _stamp; }
static Size allocateMemory(Byte** memAllocated, Size size);
static void deallocateMemory(Byte* memDeallocated, Size size);
bool isAvailable() const { return (_data != 0); }
private:
Stamp _stamp { 0 };
Size _size { 0 };
Byte* _data { nullptr };
enum Flag {
DIRTY = 0x01,
};
PageManager(Size pageSize = DEFAULT_PAGE_SIZE) : _pageSize(pageSize) {}
PageManager& operator=(const PageManager& other) {
assert(other._pageSize == _pageSize);
_pages = other._pages;
_flags = other._flags;
return *this;
}
using Vector = std::vector<uint8_t>;
using Pages = std::vector<Size>;
Vector _pages;
uint8 _flags{ 0 };
const Size _pageSize;
operator bool const() {
return (*this)(DIRTY);
}
bool operator()(uint8 desiredFlags) const {
return (desiredFlags == (_flags & desiredFlags));
}
void markPage(Size index, uint8 markFlags = DIRTY) {
assert(_pages.size() > index);
_pages[index] |= markFlags;
_flags |= markFlags;
}
void markRegion(Size offset, Size bytes, uint8 markFlags = DIRTY) {
if (!bytes) {
return;
}
_flags |= markFlags;
// Find the starting page
Size startPage = (offset / _pageSize);
// Non-zero byte count, so at least one page is dirty
Size pageCount = 1;
// How much of the page is after the offset?
Size remainder = _pageSize - (offset % _pageSize);
// If there are more bytes than page space remaining, we need to increase the page count
if (bytes > remainder) {
// Get rid of the amount that will fit in the current page
bytes -= remainder;
pageCount += (bytes / _pageSize);
if (bytes % _pageSize) {
++pageCount;
}
}
// Mark the pages dirty
for (Size i = 0; i < pageCount; ++i) {
_pages[i + startPage] |= DIRTY;
}
}
Size getPageCount(uint8_t desiredFlags = DIRTY) const {
Size result = 0;
for (auto pageFlags : _pages) {
if (desiredFlags == (pageFlags & desiredFlags)) {
++result;
}
}
return result;
}
Size getSize(uint8_t desiredFlags = DIRTY) const {
return getPageCount(desiredFlags) * _pageSize;
}
void setPageCount(Size count) {
_pages.resize(count);
}
Size getRequiredPageCount(Size size) const {
Size result = size / _pageSize;
if (size % _pageSize) {
++result;
}
return result;
}
Size getRequiredSize(Size size) const {
return getRequiredPageCount(size) * _pageSize;
}
Size accommodate(Size size) {
Size newPageCount = getRequiredPageCount(size);
Size newSize = newPageCount * _pageSize;
_pages.resize(newPageCount, 0);
return newSize;
}
// Get pages with the specified flags, optionally clearing the flags as we go
Pages getMarkedPages(uint8_t desiredFlags = DIRTY, bool clear = true) {
Pages result;
if (desiredFlags == (_flags & desiredFlags)) {
_flags &= ~desiredFlags;
result.reserve(_pages.size());
for (Size i = 0; i < _pages.size(); ++i) {
if (desiredFlags == (_pages[i] & desiredFlags)) {
result.push_back(i);
if (clear) {
_pages[i] &= ~desiredFlags;
}
}
}
}
return result;
}
bool getNextTransferBlock(Size& outOffset, Size& outSize, Size& currentPage) {
Size pageCount = _pages.size();
// Advance to the first dirty page
while (currentPage < pageCount && (0 == (DIRTY & _pages[currentPage]))) {
++currentPage;
}
// If we got to the end, we're done
if (currentPage >= pageCount) {
return false;
}
// Advance to the next clean page
outOffset = static_cast<Size>(currentPage * _pageSize);
while (currentPage < pageCount && (0 != (DIRTY & _pages[currentPage]))) {
_pages[currentPage] &= ~DIRTY;
++currentPage;
}
outSize = static_cast<Size>((currentPage * _pageSize) - outOffset);
return true;
}
};
class Buffer : public Resource {
static std::atomic<uint32_t> _bufferCPUCount;
static std::atomic<Size> _bufferCPUMemoryUsage;
static void updateBufferCPUMemoryUsage(Size prevObjectSize, Size newObjectSize);
public:
enum Flag {
DIRTY = 0x01,
using Flag = PageManager::Flag;
struct Update {
Size size;
PageManager pages;
Sysmem::Operator updateOperator;
};
// Currently only one flag... 'dirty'
using PageFlags = std::vector<uint8_t>;
static const Size DEFAULT_PAGE_SIZE = 4096;
static uint32_t getBufferCPUCount();
static Size getBufferCPUMemoryUsage();
static uint32_t getBufferGPUCount();
static Size getBufferGPUMemoryUsage();
Buffer(Size pageSize = DEFAULT_PAGE_SIZE);
Buffer(Size size, const Byte* bytes, Size pageSize = DEFAULT_PAGE_SIZE);
Buffer(Size pageSize = PageManager::DEFAULT_PAGE_SIZE);
Buffer(Size size, const Byte* bytes, Size pageSize = PageManager::DEFAULT_PAGE_SIZE);
Buffer(const Buffer& buf); // deep copy of the sysmem buffer
Buffer& operator=(const Buffer& buf); // deep copy of the sysmem buffer
~Buffer();
@ -184,34 +332,24 @@ public:
return append(sizeof(T) * t.size(), reinterpret_cast<const Byte*>(&t[0]));
}
bool getNextTransferBlock(Size& outOffset, Size& outSize, Size& currentPage) const {
Size pageCount = _pages.size();
// Advance to the first dirty page
while (currentPage < pageCount && (0 == (Buffer::DIRTY & _pages[currentPage]))) {
++currentPage;
}
// If we got to the end, we're done
if (currentPage >= pageCount) {
return false;
}
// Advance to the next clean page
outOffset = static_cast<Size>(currentPage * _pageSize);
while (currentPage < pageCount && (0 != (Buffer::DIRTY & _pages[currentPage]))) {
_pages[currentPage] &= ~Buffer::DIRTY;
++currentPage;
}
outSize = static_cast<Size>((currentPage * _pageSize) - outOffset);
return true;
}
const GPUObjectPointer gpuObject {};
// Access the sysmem object, limited to ourselves and GPUObject derived classes
const Sysmem& getSysmem() const { return _sysmem; }
// FIXME find a better access mechanism for clearing this
mutable uint8_t _flags;
bool isDirty() const {
return _pages(PageManager::DIRTY);
}
void applyUpdate(const Update& update);
// Main thread operation to say that the buffer is ready to be used as a frame
Update getUpdate() const;
mutable PageManager _renderPages;
Sysmem _renderSysmem;
protected:
void markDirty(Size offset, Size bytes);
@ -223,16 +361,15 @@ protected:
Sysmem& editSysmem() { return _sysmem; }
Byte* editData() { return editSysmem().editData(); }
Size getRequiredPageCount() const;
Size _end { 0 };
mutable PageFlags _pages;
const Size _pageSize;
mutable PageManager _pages;
Size _end{ 0 };
Sysmem _sysmem;
// FIXME find a more generic way to do this.
friend class gl::GLBuffer;
friend class BufferView;
friend class Frame;
};
typedef std::shared_ptr<Buffer> BufferPointer;

View file

@ -880,11 +880,3 @@ Vec3u Texture::evalMipDimensions(uint16 level) const {
return glm::max(dimensions, Vec3u(1));
}
std::function<uint32(const gpu::Texture& texture)> TEXTURE_ID_RESOLVER;
uint32 Texture::getHardwareId() const {
if (TEXTURE_ID_RESOLVER) {
return TEXTURE_ID_RESOLVER(*this);
}
return 0;
}

View file

@ -449,8 +449,6 @@ public:
const GPUObjectPointer gpuObject {};
uint32 getHardwareId() const;
protected:
std::unique_ptr< Storage > _storage;

View file

@ -1,3 +1,4 @@
set(TARGET_NAME plugins)
setup_hifi_library(OpenGL)
link_hifi_libraries(shared)
include_hifi_library_headers(gpu)

View file

@ -20,6 +20,7 @@
#include <GLMHelpers.h>
#include <RegisteredMetaTypes.h>
#include <shared/Bilateral.h>
#include <gpu/Forward.h>
#include "Plugin.h"
@ -91,11 +92,6 @@ public:
return glm::mat4();
}
// Needed for timewarp style features
virtual void setEyeRenderPose(uint32_t frameIndex, Eye eye, const glm::mat4& pose) {
// NOOP
}
virtual void abandonCalibration() {}
virtual void resetSensors() {}
@ -132,6 +128,7 @@ public:
Present = QEvent::User + 1
};
virtual bool isRenderThread() const { return false; }
virtual bool isHmd() const { return false; }
virtual int getHmdScreen() const { return -1; }
/// By default, all HMDs are stereo
@ -149,16 +146,8 @@ public:
virtual QString getPreferredAudioOutDevice() const { return QString(); }
// Rendering support
/**
* Sends the scene texture to the display plugin.
*/
virtual void submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) = 0;
/**
* Sends the scene texture to the display plugin.
*/
virtual void submitOverlayTexture(const gpu::TexturePointer& overlayTexture) = 0;
virtual void setBackend(const gpu::BackendPointer& backend) final { _backend = backend; }
virtual void submitFrame(const gpu::FramePointer& newFrame) = 0;
// Does the rendering surface have current focus?
virtual bool hasFocus() const = 0;
@ -212,6 +201,8 @@ signals:
protected:
void incrementPresentCount();
gpu::BackendPointer _backend;
private:
std::atomic<uint32_t> _presentedFrameIndex;
mutable std::mutex _paintDelayMutex;

View file

@ -53,9 +53,6 @@ void Engine::load() {
}
void Engine::run() {
// Sync GPU state before beginning to render
_renderContext->args->_context->syncCache();
for (auto job : _jobs) {
job.run(_sceneContext, _renderContext);
}

View file

@ -11,12 +11,23 @@
#ifdef _WIN32
#if defined(NSIGHT_FOUND)
#include "nvToolsExt.h"
#include <QtCore/QCoreApplication>
#include <QtCore/QThread>
extern bool isRenderThread();
ProfileRange::ProfileRange(const char *name) {
if (!isRenderThread()) {
return;
}
nvtxRangePush(name);
}
ProfileRange::ProfileRange(const char *name, uint32_t argbColor, uint64_t payload) {
if (!isRenderThread()) {
return;
}
nvtxEventAttributes_t eventAttrib = {0};
eventAttrib.version = NVTX_VERSION;
@ -32,6 +43,9 @@ ProfileRange::ProfileRange(const char *name, uint32_t argbColor, uint64_t payloa
}
ProfileRange::~ProfileRange() {
if (!isRenderThread()) {
return;
}
nvtxRangePop();
}

View file

@ -14,6 +14,7 @@
#include <ui/Menu.h>
#include <MainWindow.h>
#include <plugins/DisplayPlugin.h>
static PluginContainer* INSTANCE{ nullptr };
@ -159,3 +160,8 @@ void PluginContainer::setBoolSetting(const QString& settingName, bool value) {
Setting::Handle<bool> settingValue(settingName, value);
return settingValue.set(value);
}
bool isRenderThread() {
auto displayPlugin = PluginContainer::getInstance().getActiveDisplayPlugin();
return displayPlugin && displayPlugin->isRenderThread();
}

View file

@ -58,8 +58,6 @@ public:
virtual void showDisplayPluginsTools(bool show = true) = 0;
virtual void requestReset() = 0;
virtual bool makeRenderingContextCurrent() = 0;
virtual void releaseSceneTexture(const gpu::TexturePointer& texture) = 0;
virtual void releaseOverlayTexture(const gpu::TexturePointer& texture) = 0;
virtual GLWidget* getPrimaryWidget() = 0;
virtual MainWindow* getPrimaryWindow() = 0;
virtual QOpenGLContext* getPrimaryContext() = 0;

View file

@ -6,6 +6,7 @@
# See the accompanying file LICENSE or http:#www.apache.org/licenses/LICENSE-2.0.html
#
if (FALSE)
if (WIN32)
# we're using static GLEW, so define GLEW_STATIC
@ -23,4 +24,5 @@ if (WIN32)
target_link_libraries(${TARGET_NAME} ${LIBOVR_LIBRARIES})
target_link_libraries(${TARGET_NAME} Winmm.lib)
endif()
endif()

View file

@ -8,6 +8,8 @@
# Windows doesn't need this, and building it currently make Linux unstable.
# if (NOT WIN32)
if (FALSE)
if (APPLE)
set(TARGET_NAME oculusLegacy)
@ -26,3 +28,4 @@ if (APPLE)
endif()
endif()

View file

@ -6,6 +6,7 @@
# See the accompanying file LICENSE or http:#www.apache.org/licenses/LICENSE-2.0.html
#
if (FALSE)
if (WIN32)
# we're using static GLEW, so define GLEW_STATIC
add_definitions(-DGLEW_STATIC)
@ -22,3 +23,4 @@ if (WIN32)
target_include_directories(${TARGET_NAME} PRIVATE ${OPENVR_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME} ${OPENVR_LIBRARIES})
endif()
endif()

View file

@ -85,8 +85,6 @@ public:
virtual void showDisplayPluginsTools(bool show) override {}
virtual void requestReset() override {}
virtual bool makeRenderingContextCurrent() override { return true; }
virtual void releaseSceneTexture(const gpu::TexturePointer& texture) override {}
virtual void releaseOverlayTexture(const gpu::TexturePointer& texture) override {}
virtual GLWidget* getPrimaryWidget() override { return nullptr; }
virtual MainWindow* getPrimaryWindow() override { return nullptr; }
virtual QOpenGLContext* getPrimaryContext() override { return nullptr; }

View file

@ -93,7 +93,6 @@ void TestWindow::resizeWindow(const QSize& size) {
}
void TestWindow::beginFrame() {
_renderArgs->_context->syncCache();
#ifdef DEFERRED_LIGHTING

View file

@ -30,7 +30,6 @@
#include <AssetClient.h>
#include <gl/OffscreenGLCanvas.h>
#include <gl/OglplusHelpers.h>
#include <gl/GLHelpers.h>
#include <gl/QOpenGLContextWrapper.h>
#include <gl/QOpenGLDebugLoggerWrapper.h>
@ -38,6 +37,7 @@
#include <gpu/gl/GLBackend.h>
#include <gpu/gl/GLFramebuffer.h>
#include <gpu/gl/GLTexture.h>
#include <gpu/StandardShaderLib.h>
#include <WebEntityItem.h>
#include <OctreeUtils.h>
@ -157,7 +157,118 @@ static QString toHumanSize(size_t size, size_t maxUnit = std::numeric_limits<siz
return QString("%1 %2").arg(size).arg(SUFFIXES[suffixIndex]);
}
const char* SRGB_TO_LINEAR_FRAG = R"SCRIBE(
uniform sampler2D colorMap;
in vec2 varTexCoord0;
out vec4 outFragColor;
void main(void) {
outFragColor = vec4(pow(texture(colorMap, varTexCoord0).rgb, vec3(2.2)), 1.0);
}
)SCRIBE";
class RenderThread : public GenericQueueThread<gpu::FramePointer> {
using Parent = GenericQueueThread<gpu::FramePointer>;
public:
QOpenGLContextWrapper* _displayContext{ nullptr };
QSurface* _displaySurface{ nullptr };
gpu::PipelinePointer _presentPipeline;
gpu::ContextPointer _gpuContext; // initialized during window creation
std::atomic<size_t> _presentCount;
QElapsedTimer _elapsed;
std::atomic<uint16_t> _fps;
RateCounter<200> _fpsCounter;
std::mutex _mutex;
std::shared_ptr<gpu::Backend> _backend;
void initialize(QOpenGLContextWrapper* displayContext, QWindow* surface) {
setObjectName("RenderThread");
_displayContext = displayContext;
_displaySurface = surface;
_displayContext->makeCurrent(_displaySurface);
// GPU library init
gpu::Context::init<gpu::gl::GLBackend>();
_gpuContext = std::make_shared<gpu::Context>();
_backend = _gpuContext->getBackend();
_displayContext->makeCurrent(_displaySurface);
DependencyManager::get<DeferredLightingEffect>()->init();
_displayContext->doneCurrent();
Parent::initialize();
if (isThreaded()) {
_displayContext->moveToThread(thread());
}
}
void setup() override {
_displayContext->makeCurrent(_displaySurface);
glewExperimental = true;
glewInit();
glGetError();
{
auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS();
auto ps = gpu::Shader::createPixel(std::string(SRGB_TO_LINEAR_FRAG));
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
gpu::Shader::BindingSet slotBindings;
gpu::Shader::makeProgram(*program, slotBindings);
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
_presentPipeline = gpu::Pipeline::create(program, state);
}
//_textOverlay = new TextOverlay(glm::uvec2(800, 600));
glViewport(0, 0, 800, 600);
_elapsed.start();
}
void shutdown() override {
}
void renderFrame(gpu::FramePointer& frame) {
++_presentCount;
_displayContext->makeCurrent(_displaySurface);
if (frame && !frame->batches.empty()) {
_backend->syncCache();
_backend->setStereoState(frame->stereoState);
for (auto& batch : frame->batches) {
_backend->render(batch);
}
{
auto geometryCache = DependencyManager::get<GeometryCache>();
gpu::Batch presentBatch;
presentBatch.setViewTransform(Transform());
presentBatch.setFramebuffer(gpu::FramebufferPointer());
presentBatch.setResourceTexture(0, frame->framebuffer->getRenderBuffer(0));
presentBatch.setPipeline(_presentPipeline);
presentBatch.draw(gpu::TRIANGLE_STRIP, 4);
_backend->render(presentBatch);
}
}
{
//_textOverlay->render();
}
_displayContext->swapBuffers(_displaySurface);
_fpsCounter.increment();
static size_t _frameCount{ 0 };
++_frameCount;
if (_elapsed.elapsed() >= 500) {
_fps = _fpsCounter.rate();
_frameCount = 0;
_elapsed.restart();
}
}
bool processQueueItems(const Queue& items) override {
auto frame = items.last();
renderFrame(frame);
return true;
}
};
// Create a simple OpenGL window that renders text in various ways
class QTestWindow : public QWindow, public AbstractViewStateInterface {
@ -185,6 +296,7 @@ protected:
}
void postLambdaEvent(std::function<void()> f) override {}
qreal getDevicePixelRatio() override {
return 1.0f;
}
@ -192,6 +304,7 @@ protected:
render::ScenePointer getMain3DScene() override {
return _main3DScene;
}
render::EnginePointer getRenderEngine() override {
return _renderEngine;
}
@ -221,12 +334,13 @@ public:
}
QTestWindow() {
_camera.movementSpeed = 50.0f;
QThread::currentThread()->setPriority(QThread::HighestPriority);
AbstractViewStateInterface::setInstance(this);
_octree = DependencyManager::set<EntityTreeRenderer>(false, this, nullptr);
_octree->init();
// Prevent web entities from rendering
REGISTER_ENTITY_TYPE_WITH_FACTORY(Web, WebEntityItem::factory)
REGISTER_ENTITY_TYPE_WITH_FACTORY(Web, WebEntityItem::factory);
DependencyManager::set<ParentFinder>(_octree->getTree());
getEntities()->setViewFrustum(_viewFrustum);
@ -241,11 +355,12 @@ public:
format.setOption(QSurfaceFormat::DebugContext);
setFormat(format);
_context.setFormat(format);
_context.create();
resize(QSize(800, 600));
show();
makeCurrent();
_context.setFormat(format);
_context.create();
_context.makeCurrent(this);
glewExperimental = true;
glewInit();
glGetError();
@ -253,40 +368,24 @@ public:
#ifdef Q_OS_WIN
wglSwapIntervalEXT(0);
#endif
{
makeCurrent();
_quadProgram = loadDefaultShader();
_plane = loadPlane(_quadProgram);
_textOverlay = new TextOverlay(glm::uvec2(800, 600));
glViewport(0, 0, 800, 600);
}
_context.doneCurrent();
_camera.movementSpeed = 50.0f;
// GPU library init
{
_offscreenContext = new OffscreenGLCanvas();
_offscreenContext->create(_context.getContext());
_offscreenContext->makeCurrent();
gpu::Context::init<gpu::gl::GLBackend>();
_gpuContext = std::make_shared<gpu::Context>();
}
_initContext.create(_context.getContext());
_renderThread.initialize(&_context, this);
// FIXME use a wait condition
QThread::msleep(1000);
_renderThread.queueItem(gpu::FramePointer());
_initContext.makeCurrent();
// Render engine init
_renderEngine->addJob<RenderShadowTask>("RenderShadowTask", _cullFunctor);
_renderEngine->addJob<RenderDeferredTask>("RenderDeferredTask", _cullFunctor);
_renderEngine->load();
_renderEngine->registerScene(_main3DScene);
// Render engine library init
{
_offscreenContext->makeCurrent();
DependencyManager::get<DeferredLightingEffect>()->init();
_renderEngine->addJob<RenderShadowTask>("RenderShadowTask", _cullFunctor);
_renderEngine->addJob<RenderDeferredTask>("RenderDeferredTask", _cullFunctor);
_renderEngine->load();
_renderEngine->registerScene(_main3DScene);
}
reloadScene();
restorePosition();
_elapsed.start();
QTimer* timer = new QTimer(this);
timer->setInterval(0);
connect(timer, &QTimer::timeout, this, [this] {
@ -298,8 +397,6 @@ public:
virtual ~QTestWindow() {
ResourceManager::cleanup();
try { _quadProgram.reset(); } catch (std::runtime_error&) {}
try { _plane.reset(); } catch (std::runtime_error&) {}
}
protected:
@ -363,6 +460,7 @@ private:
return (renderAccuracy > 0.0f);
}
uint16_t _fps;
void draw() {
if (!_ready) {
return;
@ -370,17 +468,21 @@ private:
if (!isVisible()) {
return;
}
if (_renderCount.load() >= _renderThread._presentCount.load()) {
return;
}
_renderCount = _renderThread._presentCount.load();
update();
_offscreenContext->makeCurrent();
RenderArgs renderArgs(_gpuContext, _octree.data(), DEFAULT_OCTREE_SIZE_SCALE,
RenderArgs renderArgs(_renderThread._gpuContext, _octree.data(), DEFAULT_OCTREE_SIZE_SCALE,
0, RenderArgs::DEFAULT_RENDER_MODE,
RenderArgs::MONO, RenderArgs::RENDER_DEBUG_NONE);
auto framebufferCache = DependencyManager::get<FramebufferCache>();
QSize windowSize = size();
framebufferCache->setFrameBufferSize(windowSize);
renderArgs._blitFramebuffer = framebufferCache->getFramebuffer();
// Viewport is assigned to the size of the framebuffer
renderArgs._viewport = ivec4(0, 0, windowSize.width(), windowSize.height());
@ -398,49 +500,12 @@ private:
}
// Final framebuffer that will be handled to the display-plugin
{
auto finalFramebuffer = framebufferCache->getFramebuffer();
renderArgs._blitFramebuffer = finalFramebuffer;
}
_gpuContext->beginFrame(renderArgs._blitFramebuffer);
gpu::doInBatch(renderArgs._context, [&](gpu::Batch& batch) {
batch.resetStages();
});
render(&renderArgs);
_gpuContext->endFrame();
GLuint glTex;
{
auto gpuTex = renderArgs._blitFramebuffer->getRenderBuffer(0);
glTex = gpu::Backend::getGPUObject<gpu::gl::GLTexture>(*gpuTex)->_id;
}
makeCurrent();
{
glBindTexture(GL_TEXTURE_2D, glTex);
_quadProgram->Use();
_plane->Use();
_plane->Draw();
glBindVertexArray(0);
}
{
//_textOverlay->render();
}
_context.swapBuffers(this);
_offscreenContext->makeCurrent();
framebufferCache->releaseFramebuffer(renderArgs._blitFramebuffer);
renderArgs._blitFramebuffer.reset();
_fpsCounter.increment();
static size_t _frameCount { 0 };
++_frameCount;
if (_elapsed.elapsed() >= 500) {
_fps = _fpsCounter.rate();
if (_fps != _renderThread._fps) {
_fps = _renderThread._fps;
updateText();
_frameCount = 0;
_elapsed.restart();
}
}
@ -467,7 +532,12 @@ private:
void updateText() {
//qDebug() << "FPS " << fps.rate();
setTitle(QString("FPS %1 Culling %2 TextureMemory GPU %3 CPU %4")
.arg(_fps).arg(_cullingEnabled)
.arg(toHumanSize(gpu::Context::getTextureGPUMemoryUsage(), 2))
.arg(toHumanSize(gpu::Texture::getTextureCPUMemoryUsage(), 2)));
#if 0
{
_textBlocks.erase(TextBlock::Info);
auto& infoTextBlock = _textBlocks[TextBlock::Info];
@ -475,13 +545,10 @@ private:
infoTextBlock.push_back({ vec2(100, 10), std::to_string((uint32_t)_fps), TextOverlay::alignLeft });
infoTextBlock.push_back({ vec2(98, 30), "Culling: ", TextOverlay::alignRight });
infoTextBlock.push_back({ vec2(100, 30), _cullingEnabled ? "Enabled" : "Disabled", TextOverlay::alignLeft });
setTitle(QString("FPS %1 Culling %2 TextureMemory GPU %3 CPU %4")
.arg(_fps).arg(_cullingEnabled)
.arg(toHumanSize(gpu::Context::getTextureGPUMemoryUsage(), 2))
.arg(toHumanSize(gpu::Texture::getTextureCPUMemoryUsage(), 2)));
}
#endif
#if 0
_textOverlay->beginTextUpdate();
for (const auto& e : _textBlocks) {
for (const auto& b : e.second) {
@ -489,6 +556,7 @@ private:
}
}
_textOverlay->endTextUpdate();
#endif
}
void update() {
@ -525,6 +593,11 @@ private:
}
void render(RenderArgs* renderArgs) {
auto& gpuContext = renderArgs->_context;
gpuContext->beginFrame();
gpu::doInBatch(gpuContext, [&](gpu::Batch& batch) {
batch.resetStages();
});
PROFILE_RANGE(__FUNCTION__);
PerformanceTimer perfTimer("draw");
// The pending changes collecting the changes here
@ -544,6 +617,14 @@ private:
// Before the deferred pass, let's try to use the render engine
_renderEngine->run();
}
auto frame = gpuContext->endFrame();
frame->framebuffer = renderArgs->_blitFramebuffer;
frame->framebufferRecycler = [](const gpu::FramebufferPointer& framebuffer){
DependencyManager::get<FramebufferCache>()->releaseFramebuffer(framebuffer);
};
_renderThread.queueItem(frame);
}
bool makeCurrent() {
@ -558,9 +639,8 @@ private:
if (!_ready) {
return;
}
_textOverlay->resize(toGlm(_size));
makeCurrent();
glViewport(0, 0, size.width(), size.height());
//_textOverlay->resize(toGlm(_size));
//glViewport(0, 0, size.width(), size.height());
}
void parsePath(const QString& viewpointString) {
@ -704,32 +784,29 @@ private:
std::map<TextBlock, std::list<TextElement>> _textBlocks;
gpu::ContextPointer _gpuContext; // initialized during window creation
render::EnginePointer _renderEngine { new render::Engine() };
render::ScenePointer _main3DScene { new render::Scene(glm::vec3(-0.5f * (float)TREE_SCALE), (float)TREE_SCALE) };
OffscreenGLCanvas* _offscreenContext { nullptr };
QOpenGLContextWrapper _context;
QSize _size;
RateCounter<200> _fpsCounter;
QSettings _settings;
ProgramPtr _quadProgram;
ShapeWrapperPtr _plane;
std::atomic<size_t> _renderCount;
OffscreenGLCanvas _initContext;
RenderThread _renderThread;
QWindowCamera _camera;
ViewFrustum _viewFrustum; // current state of view frustum, perspective, orientation, etc.
ViewFrustum _shadowViewFrustum; // current state of view frustum, perspective, orientation, etc.
model::SunSkyStage _sunSkyStage;
model::LightPointer _globalLight { std::make_shared<model::Light>() };
QElapsedTimer _elapsed;
bool _ready { false };
float _fps { 0 };
TextOverlay* _textOverlay;
bool _cullingEnabled { true };
//TextOverlay* _textOverlay;
static bool _cullingEnabled;
bool _stereoEnabled { false };
QSharedPointer<EntityTreeRenderer> _octree;
};
bool QTestWindow::_cullingEnabled = false;
void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) {
if (!message.isEmpty()) {
#ifdef Q_OS_WIN