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 // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
#version 410 core
uniform sampler2D sampler; uniform sampler2D sampler;
uniform float alpha = 1.0;
uniform vec4 glowPoints = vec4(-1); struct OverlayData {
uniform vec4 glowColors[2]; mat4 mvp;
uniform vec2 resolution = vec2(3960.0, 1188.0); vec4 glowPoints;
uniform float radius = 0.005; 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 vec3 vPosition;
in vec2 vTexCoord; in vec2 vTexCoord;
in vec4 vGlowPoints;
out vec4 FragColor; out vec4 FragColor;
@ -31,9 +40,10 @@ float easeInOutCubic(float f) {
} }
void main() { void main() {
FragColor = texture(sampler, vTexCoord);
vec2 aspect = resolution; vec2 aspect = resolution;
aspect /= resolution.x; aspect /= resolution.x;
FragColor = texture(sampler, vTexCoord);
float glowIntensity = 0.0; float glowIntensity = 0.0;
float dist1 = distance(vTexCoord * aspect, glowPoints.xy * aspect); 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 // 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; mat4 mvp = overlay.mvp;
in vec2 TexCoord;
layout(location = 0) in vec3 Position;
layout(location = 3) in vec2 TexCoord;
out vec3 vPosition; out vec3 vPosition;
out vec2 vTexCoord; out vec2 vTexCoord;

View file

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

View file

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

View file

@ -67,7 +67,9 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) {
// Execute the batch into our framebuffer // Execute the batch into our framebuffer
doInBatch(renderArgs->_context, [&](gpu::Batch& batch) { doInBatch(renderArgs->_context, [&](gpu::Batch& batch) {
PROFILE_RANGE_BATCH(batch, "ApplicationOverlayRender");
renderArgs->_batch = &batch; renderArgs->_batch = &batch;
batch.enableStereo(false);
int width = _overlayFramebuffer->getWidth(); int width = _overlayFramebuffer->getWidth();
int height = _overlayFramebuffer->getHeight(); 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 DEFAULT_SAMPLER = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR);
static const auto DEPTH_FORMAT = gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPTH); 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() { void ApplicationOverlay::buildFramebufferObject() {
PROFILE_RANGE(__FUNCTION__); PROFILE_RANGE(__FUNCTION__);
@ -265,22 +263,6 @@ void ApplicationOverlay::buildFramebufferObject() {
_overlayFramebuffer->setDepthStencilBuffer(overlayDepthTexture, DEPTH_FORMAT); _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)) { if (!_overlayFramebuffer->getRenderBuffer(0)) {
const gpu::Sampler OVERLAY_SAMPLER(gpu::Sampler::FILTER_MIN_MAG_LINEAR, gpu::Sampler::WRAP_CLAMP); 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)); 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) { if (!_overlayFramebuffer) {
return gpu::TexturePointer(); return gpu::TexturePointer();
} }
auto result = _overlayFramebuffer->getRenderBuffer(0); return _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";
}
}

View file

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

View file

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

View file

@ -24,7 +24,7 @@ public:
virtual bool internalActivate() override; 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; virtual void internalPresent() override;

View file

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

View file

@ -11,16 +11,14 @@
class NullDisplayPlugin : public DisplayPlugin { class NullDisplayPlugin : public DisplayPlugin {
public: public:
~NullDisplayPlugin() final {}
const QString& getName() const override { return NAME; }
grouping getGrouping() const override { return DEVELOPER; }
virtual ~NullDisplayPlugin() final {} glm::uvec2 getRecommendedRenderSize() const override;
virtual const QString& getName() const override { return NAME; } bool hasFocus() const override;
virtual grouping getGrouping() const override { return DEVELOPER; } void submitFrame(const gpu::FramePointer& newFrame) override;
QImage getScreenshot() const override;
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;
private: private:
static const QString NAME; static const QString NAME;
}; };

View file

@ -8,6 +8,7 @@
#include "OpenGLDisplayPlugin.h" #include "OpenGLDisplayPlugin.h"
#include <condition_variable> #include <condition_variable>
#include <queue>
#include <QtCore/QCoreApplication> #include <QtCore/QCoreApplication>
#include <QtCore/QThread> #include <QtCore/QThread>
@ -19,26 +20,43 @@
#if defined(Q_OS_MAC) #if defined(Q_OS_MAC)
#include <OpenGL/CGLCurrent.h> #include <OpenGL/CGLCurrent.h>
#endif #endif
#include <gl/QOpenGLContextWrapper.h>
#include <gpu/Texture.h>
#include <gl/GLWidget.h>
#include <NumericalConstants.h> #include <NumericalConstants.h>
#include <DependencyManager.h> #include <DependencyManager.h>
#include <shared/NsightHelpers.h> #include <GLMHelpers.h>
#include <ui-plugins/PluginContainer.h>
#include <gl/QOpenGLContextWrapper.h>
#include <gl/GLWidget.h>
#include <gl/Config.h> #include <gl/Config.h>
#include <gl/GLEscrow.h> #include <gl/GLEscrow.h>
#include <GLMHelpers.h>
#include <CursorManager.h> #include <gpu/Texture.h>
#include "CompositorHelper.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 <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 uniform sampler2D colorMap;
// cap the present rate at 200
// const static unsigned int MAX_PRESENT_RATE = 200; 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 { class PresentThread : public QThread, public Dependency {
using Mutex = std::mutex; using Mutex = std::mutex;
@ -87,8 +105,8 @@ public:
virtual void run() override { virtual void run() override {
OpenGLDisplayPlugin* currentPlugin{ nullptr }; OpenGLDisplayPlugin* currentPlugin{ nullptr };
thread()->setPriority(QThread::HighestPriority);
Q_ASSERT(_context); Q_ASSERT(_context);
mainContext = _context->contextHandle();
while (!_shutdown) { while (!_shutdown) {
if (_pendingMainThreadOperation) { if (_pendingMainThreadOperation) {
{ {
@ -118,19 +136,13 @@ public:
if (newPlugin != currentPlugin) { if (newPlugin != currentPlugin) {
// Deactivate the old plugin // Deactivate the old plugin
if (currentPlugin != nullptr) { if (currentPlugin != nullptr) {
try { currentPlugin->uncustomizeContext();
currentPlugin->uncustomizeContext(); CHECK_GL_ERROR();
} catch (const oglplus::Error& error) {
qWarning() << "OpenGL error in uncustomizeContext: " << error.what();
}
} }
if (newPlugin) { if (newPlugin) {
try { newPlugin->customizeContext();
newPlugin->customizeContext(); CHECK_GL_ERROR();
} catch (const oglplus::Error& error) {
qWarning() << "OpenGL error in customizeContext: " << error.what();
}
} }
currentPlugin = newPlugin; currentPlugin = newPlugin;
_newPluginQueue.pop(); _newPluginQueue.pop();
@ -150,11 +162,8 @@ public:
// take the latest texture and present it // take the latest texture and present it
_context->makeCurrent(); _context->makeCurrent();
if (isCurrentContext(_context->contextHandle())) { if (isCurrentContext(_context->contextHandle())) {
try { currentPlugin->present();
currentPlugin->present(); CHECK_GL_ERROR();
} catch (const oglplus::Error& error) {
qWarning() << "OpenGL error in presentation: " << error.what();
}
_context->doneCurrent(); _context->doneCurrent();
} else { } else {
qWarning() << "Makecurrent failed"; qWarning() << "Makecurrent failed";
@ -204,27 +213,13 @@ private:
QGLContext* _context { nullptr }; QGLContext* _context { nullptr };
}; };
#endif bool OpenGLDisplayPlugin::isRenderThread() const {
return QThread::currentThread() == DependencyManager::get<PresentThread>()->thread();
}
OpenGLDisplayPlugin::OpenGLDisplayPlugin() { 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() { bool OpenGLDisplayPlugin::activate() {
if (!_cursorsData.size()) { if (!_cursorsData.size()) {
auto& cursorManager = Cursor::Manager::instance(); auto& cursorManager = Cursor::Manager::instance();
@ -244,7 +239,6 @@ bool OpenGLDisplayPlugin::activate() {
} }
_vsyncSupported = _container->getPrimaryWidget()->isVsyncSupported(); _vsyncSupported = _container->getPrimaryWidget()->isVsyncSupported();
#if THREADED_PRESENT
// Start the present thread if necessary // Start the present thread if necessary
QSharedPointer<PresentThread> presentThread; QSharedPointer<PresentThread> presentThread;
if (DependencyManager::isSet<PresentThread>()) { if (DependencyManager::isSet<PresentThread>()) {
@ -259,7 +253,6 @@ bool OpenGLDisplayPlugin::activate() {
presentThread->start(); presentThread->start();
} }
_presentThread = presentThread.data(); _presentThread = presentThread.data();
#endif
// Child classes may override this in order to do things like initialize // Child classes may override this in order to do things like initialize
// libraries, etc // libraries, etc
@ -267,17 +260,10 @@ bool OpenGLDisplayPlugin::activate() {
return false; return false;
} }
#if THREADED_PRESENT
// This should not return until the new context has been customized // This should not return until the new context has been customized
// and the old context (if any) has been uncustomized // and the old context (if any) has been uncustomized
presentThread->setNewDisplayPlugin(this); presentThread->setNewDisplayPlugin(this);
#else
static auto widget = _container->getPrimaryWidget();
widget->makeCurrent();
customizeContext();
_container->makeRenderingContextCurrent();
#endif
auto compositorHelper = DependencyManager::get<CompositorHelper>(); auto compositorHelper = DependencyManager::get<CompositorHelper>();
connect(compositorHelper.data(), &CompositorHelper::alphaChanged, [this] { connect(compositorHelper.data(), &CompositorHelper::alphaChanged, [this] {
@ -300,16 +286,9 @@ void OpenGLDisplayPlugin::deactivate() {
auto compositorHelper = DependencyManager::get<CompositorHelper>(); auto compositorHelper = DependencyManager::get<CompositorHelper>();
disconnect(compositorHelper.data()); disconnect(compositorHelper.data());
#if THREADED_PRESENT
auto presentThread = DependencyManager::get<PresentThread>(); auto presentThread = DependencyManager::get<PresentThread>();
// Does not return until the GL transition has completeed // Does not return until the GL transition has completeed
presentThread->setNewDisplayPlugin(nullptr); presentThread->setNewDisplayPlugin(nullptr);
#else
static auto widget = _container->getPrimaryWidget();
widget->makeCurrent();
uncustomizeContext();
_container->makeRenderingContextCurrent();
#endif
internalDeactivate(); internalDeactivate();
_container->showDisplayPluginsTools(false); _container->showDisplayPluginsTools(false);
@ -325,56 +304,74 @@ void OpenGLDisplayPlugin::deactivate() {
void OpenGLDisplayPlugin::customizeContext() { void OpenGLDisplayPlugin::customizeContext() {
#if THREADED_PRESENT
auto presentThread = DependencyManager::get<PresentThread>(); auto presentThread = DependencyManager::get<PresentThread>();
Q_ASSERT(thread() == presentThread->thread()); Q_ASSERT(thread() == presentThread->thread());
#endif
enableVsync(); enableVsync();
for (auto& cursorValue : _cursorsData) { for (auto& cursorValue : _cursorsData) {
auto& cursorData = cursorValue.second; auto& cursorData = cursorValue.second;
if (!cursorData.texture) { if (!cursorData.texture) {
const auto& image = cursorData.image; auto image = cursorData.image;
glGenTextures(1, &cursorData.texture); if (image.format() != QImage::Format_ARGB32) {
glBindTexture(GL_TEXTURE_2D, cursorData.texture); image = image.convertToFormat(QImage::Format_ARGB32);
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); if ((image.width() > 0) && (image.height() > 0)) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glGenerateMipmap(GL_TEXTURE_2D); 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; if (!_presentPipeline) {
Context::BlendFunc(BlendFunction::SrcAlpha, BlendFunction::OneMinusSrcAlpha); {
Context::Disable(Capability::Blend); auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS();
Context::Disable(Capability::DepthTest); auto ps = gpu::Shader::createPixel(std::string(SRGB_TO_LINEAR_FRAG));
Context::Disable(Capability::CullFace); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
gpu::Shader::makeProgram(*program);
_program = loadDefaultShader(); gpu::StatePointer state = gpu::StatePointer(new gpu::State());
state->setDepthTest(gpu::State::DepthTest(false));
auto uniforms = _program->ActiveUniforms(); _presentPipeline = gpu::Pipeline::create(program, state);
while (!uniforms.Empty()) {
auto uniform = uniforms.Front();
if (uniform.Name() == "mvp") {
_mvpUniform = uniform.Index();
} }
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() { void OpenGLDisplayPlugin::uncustomizeContext() {
_compositeFramebuffer.reset(); _presentPipeline.reset();
_program.reset();
_plane.reset();
} }
@ -420,172 +417,142 @@ bool OpenGLDisplayPlugin::eventFilter(QObject* receiver, QEvent* event) {
return false; return false;
} }
void OpenGLDisplayPlugin::submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) {
void OpenGLDisplayPlugin::submitFrame(const gpu::FramePointer& newFrame) {
if (_lockCurrentTexture) { if (_lockCurrentTexture) {
_container->releaseSceneTexture(sceneTexture);
return; return;
} }
withRenderThreadLock([&] { withNonPresentThreadLock([&] {
_sceneTextureToFrameIndexMap[sceneTexture] = frameIndex; _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() { void OpenGLDisplayPlugin::updateFrameData() {
withPresentThreadLock([&] { withPresentThreadLock([&] {
auto previousFrameIndex = _currentPresentFrameIndex; gpu::FramePointer oldFrame = _currentFrame;
_currentPresentFrameIndex = _sceneTextureToFrameIndexMap[_currentSceneTexture]; uint32_t skippedCount = 0;
auto skippedCount = (_currentPresentFrameIndex - previousFrameIndex) - 1; 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); _droppedFrameRate.increment(skippedCount);
}); });
} }
void OpenGLDisplayPlugin::compositeOverlay() { void OpenGLDisplayPlugin::compositeOverlay() {
using namespace oglplus; gpu::Batch batch;
batch.enableStereo(false);
auto compositorHelper = DependencyManager::get<CompositorHelper>(); batch.setFramebuffer(_currentFrame->framebuffer);
batch.setPipeline(_overlayPipeline);
useProgram(_program); batch.setResourceTexture(0, _currentFrame->overlay);
// set the alpha
Uniform<float>(*_program, _alphaUniform).Set(_compositeOverlayAlpha);
// check the alpha
// Overlay draw
if (isStereo()) { if (isStereo()) {
Uniform<glm::mat4>(*_program, _mvpUniform).Set(mat4());
for_each_eye([&](Eye eye) { for_each_eye([&](Eye eye) {
eyeViewport(eye); batch.setViewportTransform(eyeViewport(eye));
drawUnitQuad(); batch.draw(gpu::TRIANGLE_STRIP, 4);
}); });
} else { } else {
// Overlay draw batch.setViewportTransform(ivec4(uvec2(0), _currentFrame->framebuffer->getSize()));
Uniform<glm::mat4>(*_program, _mvpUniform).Set(mat4()); batch.draw(gpu::TRIANGLE_STRIP, 4);
drawUnitQuad();
} }
// restore the alpha _backend->render(batch);
Uniform<float>(*_program, _alphaUniform).Set(1.0);
} }
void OpenGLDisplayPlugin::compositePointer() { void OpenGLDisplayPlugin::compositePointer() {
using namespace oglplus; auto& cursorManager = Cursor::Manager::instance();
auto compositorHelper = DependencyManager::get<CompositorHelper>(); const auto& cursorData = _cursorsData[cursorManager.getCursor()->getIcon()];
auto cursorTransform = DependencyManager::get<CompositorHelper>()->getReticleTransform(glm::mat4());
useProgram(_program); gpu::Batch batch;
// set the alpha batch.enableStereo(false);
Uniform<float>(*_program, _alphaUniform).Set(_compositeOverlayAlpha); batch.setProjectionTransform(mat4());
Uniform<glm::mat4>(*_program, _mvpUniform).Set(compositorHelper->getReticleTransform(glm::mat4())); batch.setFramebuffer(_currentFrame->framebuffer);
batch.setPipeline(_cursorPipeline);
batch.setResourceTexture(0, cursorData.texture);
batch.setViewTransform(Transform());
batch.setModelTransform(cursorTransform);
if (isStereo()) { if (isStereo()) {
for_each_eye([&](Eye eye) { for_each_eye([&](Eye eye) {
eyeViewport(eye); batch.setViewportTransform(eyeViewport(eye));
drawUnitQuad(); batch.draw(gpu::TRIANGLE_STRIP, 4);
}); });
} else { } else {
drawUnitQuad(); batch.setViewportTransform(ivec4(uvec2(0), _currentFrame->framebuffer->getSize()));
batch.draw(gpu::TRIANGLE_STRIP, 4);
} }
Uniform<glm::mat4>(*_program, _mvpUniform).Set(mat4()); _backend->render(batch);
// restore the alpha
Uniform<float>(*_program, _alphaUniform).Set(1.0);
} }
void OpenGLDisplayPlugin::compositeScene() { void OpenGLDisplayPlugin::compositeScene() {
using namespace oglplus;
useProgram(_program);
Uniform<glm::mat4>(*_program, _mvpUniform).Set(mat4());
drawUnitQuad();
} }
void OpenGLDisplayPlugin::compositeLayers() { void OpenGLDisplayPlugin::compositeLayers() {
using namespace oglplus; {
auto targetRenderSize = getRecommendedRenderSize(); PROFILE_RANGE_EX("compositeScene", 0xff0077ff, (uint64_t)presentCount())
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);
compositeScene(); compositeScene();
if (overlayTextureId) { }
glBindTexture(GL_TEXTURE_2D, overlayTextureId); {
Context::Enable(Capability::Blend); PROFILE_RANGE_EX("compositeOverlay", 0xff0077ff, (uint64_t)presentCount())
Context::BlendFunc(BlendFunction::SrcAlpha, BlendFunction::OneMinusSrcAlpha); compositeOverlay();
compositeOverlay(); }
auto compositorHelper = DependencyManager::get<CompositorHelper>();
auto compositorHelper = DependencyManager::get<CompositorHelper>(); if (compositorHelper->getReticleVisible()) {
if (compositorHelper->getReticleVisible()) { PROFILE_RANGE_EX("compositePointer", 0xff0077ff, (uint64_t)presentCount())
auto& cursorManager = Cursor::Manager::instance(); compositePointer();
const auto& cursorData = _cursorsData[cursorManager.getCursor()->getIcon()]; }
glBindTexture(GL_TEXTURE_2D, cursorData.texture); {
glActiveTexture(GL_TEXTURE1); PROFILE_RANGE_EX("compositeExtra", 0xff0077ff, (uint64_t)presentCount())
glBindTexture(GL_TEXTURE_2D, overlayTextureId);
compositePointer();
glBindTexture(GL_TEXTURE_2D, 0);
glActiveTexture(GL_TEXTURE0);
}
glBindTexture(GL_TEXTURE_2D, 0);
Context::Disable(Capability::Blend);
}
compositeExtra(); compositeExtra();
}); }
} }
void OpenGLDisplayPlugin::internalPresent() { void OpenGLDisplayPlugin::internalPresent() {
using namespace oglplus; gpu::Batch presentBatch;
const uvec2& srcSize = _compositeFramebuffer->size; presentBatch.enableStereo(false);
uvec2 dstSize = getSurfacePixels(); presentBatch.setViewTransform(Transform());
_compositeFramebuffer->Bound(FramebufferTarget::Read, [&] { presentBatch.setFramebuffer(gpu::FramebufferPointer());
Context::BlitFramebuffer( presentBatch.setViewportTransform(ivec4(uvec2(0), getSurfacePixels()));
0, 0, srcSize.x, srcSize.y, presentBatch.setResourceTexture(0, _currentFrame->framebuffer->getRenderBuffer(0));
0, 0, dstSize.x, dstSize.y, presentBatch.setPipeline(_presentPipeline);
BufferSelectBit::ColorBuffer, BlitFilter::Nearest); presentBatch.draw(gpu::TRIANGLE_STRIP, 4);
}); _backend->render(presentBatch);
swapBuffers(); swapBuffers();
} }
void OpenGLDisplayPlugin::present() { void OpenGLDisplayPlugin::present() {
PROFILE_RANGE_EX(__FUNCTION__, 0xffffff00, (uint64_t)presentCount())
incrementPresentCount(); 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 // 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 // Take the composite framebuffer and send it to the output device
internalPresent(); {
PROFILE_RANGE_EX("internalPresent", 0xff00ffff, (uint64_t)presentCount())
internalPresent();
}
_presentRate.increment(); _presentRate.increment();
_activeProgram.reset();
} }
} }
@ -595,7 +562,7 @@ float OpenGLDisplayPlugin::newFramePresentRate() const {
float OpenGLDisplayPlugin::droppedFrameRate() const { float OpenGLDisplayPlugin::droppedFrameRate() const {
float result; float result;
withRenderThreadLock([&] { withNonPresentThreadLock([&] {
result = _droppedFrameRate.rate(); result = _droppedFrameRate.rate();
}); });
return result; return result;
@ -605,11 +572,6 @@ float OpenGLDisplayPlugin::presentRate() const {
return _presentRate.rate(); return _presentRate.rate();
} }
void OpenGLDisplayPlugin::drawUnitQuad() {
useProgram(_program);
_plane->Use();
_plane->Draw();
}
void OpenGLDisplayPlugin::enableVsync(bool enable) { void OpenGLDisplayPlugin::enableVsync(bool enable) {
if (!_vsyncSupported) { if (!_vsyncSupported) {
@ -626,6 +588,7 @@ void OpenGLDisplayPlugin::enableVsync(bool enable) {
#endif #endif
} }
bool OpenGLDisplayPlugin::isVsyncEnabled() { bool OpenGLDisplayPlugin::isVsyncEnabled() {
if (!_vsyncSupported) { if (!_vsyncSupported) {
return true; return true;
@ -648,19 +611,13 @@ void OpenGLDisplayPlugin::swapBuffers() {
} }
void OpenGLDisplayPlugin::withMainThreadContext(std::function<void()> f) const { void OpenGLDisplayPlugin::withMainThreadContext(std::function<void()> f) const {
#if THREADED_PRESENT
static auto presentThread = DependencyManager::get<PresentThread>(); static auto presentThread = DependencyManager::get<PresentThread>();
presentThread->withMainThreadContext(f); presentThread->withMainThreadContext(f);
_container->makeRenderingContextCurrent(); _container->makeRenderingContextCurrent();
#else
static auto widget = _container->getPrimaryWidget();
widget->makeCurrent();
f();
_container->makeRenderingContextCurrent();
#endif
} }
QImage OpenGLDisplayPlugin::getScreenshot() const { QImage OpenGLDisplayPlugin::getScreenshot() const {
#if 0
using namespace oglplus; using namespace oglplus;
QImage screenshot(_compositeFramebuffer->size.x, _compositeFramebuffer->size.y, QImage::Format_RGBA8888); QImage screenshot(_compositeFramebuffer->size.x, _compositeFramebuffer->size.y, QImage::Format_RGBA8888);
withMainThreadContext([&] { 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()); Context::ReadPixels(0, 0, _compositeFramebuffer->size.x, _compositeFramebuffer->size.y, enums::PixelDataFormat::RGBA, enums::PixelDataType::UnsignedByte, screenshot.bits());
}); });
return screenshot.mirrored(false, true); return screenshot.mirrored(false, true);
} #else
return QImage();
uint32_t OpenGLDisplayPlugin::getSceneTextureId() const { #endif
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);
} }
glm::uvec2 OpenGLDisplayPlugin::getSurfacePixels() const { glm::uvec2 OpenGLDisplayPlugin::getSurfacePixels() const {
@ -719,14 +653,7 @@ bool OpenGLDisplayPlugin::hasFocus() const {
return window ? window->hasFocus() : false; return window ? window->hasFocus() : false;
} }
void OpenGLDisplayPlugin::useProgram(const ProgramPtr& program) { void OpenGLDisplayPlugin::assertNotPresentThread() const {
if (_activeProgram != program) {
program->Bind();
_activeProgram = program;
}
}
void OpenGLDisplayPlugin::assertIsRenderThread() const {
Q_ASSERT(QThread::currentThread() != _presentThread); Q_ASSERT(QThread::currentThread() != _presentThread);
} }
@ -735,8 +662,18 @@ void OpenGLDisplayPlugin::assertIsPresentThread() const {
} }
bool OpenGLDisplayPlugin::beginFrameRender(uint32_t frameIndex) { bool OpenGLDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
withRenderThreadLock([&] { withNonPresentThreadLock([&] {
_compositeOverlayAlpha = _overlayAlpha; _compositeOverlayAlpha = _overlayAlpha;
}); });
return Parent::beginFrameRender(frameIndex); 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 <condition_variable>
#include <memory> #include <memory>
#include <queue>
#include <QtCore/QTimer> #include <QtCore/QTimer>
#include <QtGui/QImage> #include <QtGui/QImage>
#include <GLMHelpers.h> #include <GLMHelpers.h>
#include <SimpleMovingAverage.h> #include <SimpleMovingAverage.h>
#include <gl/OglplusHelpers.h>
#include <gl/GLEscrow.h> #include <gl/GLEscrow.h>
#include <shared/RateCounter.h> #include <shared/RateCounter.h>
#define THREADED_PRESENT 1
class OpenGLDisplayPlugin : public DisplayPlugin { class OpenGLDisplayPlugin : public DisplayPlugin {
Q_OBJECT Q_OBJECT
Q_PROPERTY(float overlayAlpha MEMBER _overlayAlpha) Q_PROPERTY(float overlayAlpha MEMBER _overlayAlpha)
@ -39,13 +37,12 @@ public:
// between the main thread and the presentation thread // between the main thread and the presentation thread
bool activate() override final; bool activate() override final;
void deactivate() override final; void deactivate() override final;
bool isRenderThread() const override final;
bool eventFilter(QObject* receiver, QEvent* event) override; bool eventFilter(QObject* receiver, QEvent* event) override;
bool isDisplayVisible() const override { return true; } bool isDisplayVisible() const override { return true; }
void submitFrame(const gpu::FramePointer& newFrame) override;
void submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) override;
void submitOverlayTexture(const gpu::TexturePointer& overlayTexture) override;
glm::uvec2 getRecommendedRenderSize() const override { glm::uvec2 getRecommendedRenderSize() const override {
return getSurfacePixels(); return getSurfacePixels();
@ -65,11 +62,7 @@ public:
bool beginFrameRender(uint32_t frameIndex) override; bool beginFrameRender(uint32_t frameIndex) override;
protected: protected:
#if THREADED_PRESENT
friend class PresentThread; friend class PresentThread;
#endif
uint32_t getSceneTextureId() const;
uint32_t getOverlayTextureId() const;
glm::uvec2 getSurfaceSize() const; glm::uvec2 getSurfaceSize() const;
glm::uvec2 getSurfacePixels() const; glm::uvec2 getSurfacePixels() const;
@ -93,39 +86,29 @@ protected:
// Returns true on successful activation // Returns true on successful activation
virtual bool internalActivate() { return true; } virtual bool internalActivate() { return true; }
virtual void internalDeactivate() {} virtual void internalDeactivate() {}
virtual void cleanupForSceneTexture(const gpu::TexturePointer& sceneTexture);
// Plugin specific functionality to send the composed scene to the output window or device // Plugin specific functionality to send the composed scene to the output window or device
virtual void internalPresent(); 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(); virtual void updateFrameData();
QThread* _presentThread{ nullptr }; void withMainThreadContext(std::function<void()> f) const;
ProgramPtr _program;
int32_t _mvpUniform { -1 };
int32_t _alphaUniform { -1 };
ShapeWrapperPtr _plane;
void present();
void swapBuffers();
ivec4 eyeViewport(Eye eye) const;
QThread* _presentThread{ nullptr };
std::queue<gpu::FramePointer> _newFrameQueue;
RateCounter<> _droppedFrameRate; RateCounter<> _droppedFrameRate;
RateCounter<> _newFrameRate; RateCounter<> _newFrameRate;
RateCounter<> _presentRate; RateCounter<> _presentRate;
QMap<gpu::TexturePointer, uint32_t> _sceneTextureToFrameIndexMap; gpu::FramePointer _currentFrame;
uint32_t _currentPresentFrameIndex { 0 }; gpu::PipelinePointer _overlayPipeline;
float _compositeOverlayAlpha{ 1.0f }; gpu::PipelinePointer _presentPipeline;
gpu::PipelinePointer _cursorPipeline;
float _compositeOverlayAlpha { 1.0f };
gpu::TexturePointer _currentSceneTexture;
gpu::TexturePointer _currentOverlayTexture;
TextureEscrow _sceneTextureEscrow;
TextureEscrow _overlayTextureEscrow;
bool _vsyncSupported { false }; bool _vsyncSupported { false };
@ -133,14 +116,13 @@ protected:
QImage image; QImage image;
vec2 hotSpot; vec2 hotSpot;
uvec2 size; uvec2 size;
uint32_t texture { 0 }; gpu::TexturePointer texture;
}; };
std::map<uint16_t, CursorData> _cursorsData; std::map<uint16_t, CursorData> _cursorsData;
BasicFramebufferWrapperPtr _compositeFramebuffer;
bool _lockCurrentTexture { false }; bool _lockCurrentTexture { false };
void assertIsRenderThread() const; void assertNotPresentThread() const;
void assertIsPresentThread() const; void assertIsPresentThread() const;
template<typename F> template<typename F>
@ -151,8 +133,8 @@ protected:
} }
template<typename F> template<typename F>
void withRenderThreadLock(F f) const { void withNonPresentThreadLock(F f) const {
assertIsRenderThread(); assertNotPresentThread();
Lock lock(_presentMutex); Lock lock(_presentMutex);
f(); f();
} }
@ -161,7 +143,6 @@ private:
// Any resource shared by the main thread and the presentation thread must // Any resource shared by the main thread and the presentation thread must
// be serialized through this mutex // be serialized through this mutex
mutable Mutex _presentMutex; mutable Mutex _presentMutex;
ProgramPtr _activeProgram;
float _overlayAlpha{ 1.0f }; float _overlayAlpha{ 1.0f };
}; };

View file

@ -22,9 +22,10 @@
#include <CursorManager.h> #include <CursorManager.h>
#include <gl/GLWidget.h> #include <gl/GLWidget.h>
#include <shared/NsightHelpers.h> #include <shared/NsightHelpers.h>
#include <GeometryCache.h>
#include <gpu/DrawUnitQuadTexcoord_vert.h> #include <gpu/Context.h>
#include <gpu/DrawTexture_frag.h> #include <gpu/gl/GLBackend.h>
#include <PathUtils.h> #include <PathUtils.h>
@ -39,6 +40,15 @@ static const bool DEFAULT_MONO_VIEW = true;
static const int NUMBER_OF_HANDS = 2; static const int NUMBER_OF_HANDS = 2;
static const glm::mat4 IDENTITY_MATRIX; 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 { glm::uvec2 HmdDisplayPlugin::getRecommendedUiSize() const {
return CompositorHelper::VIRTUAL_SCREEN_SIZE; return CompositorHelper::VIRTUAL_SCREEN_SIZE;
@ -68,6 +78,7 @@ bool HmdDisplayPlugin::internalActivate() {
_eyeInverseProjections[eye] = glm::inverse(_eyeProjections[eye]); _eyeInverseProjections[eye] = glm::inverse(_eyeProjections[eye]);
}); });
#if 0
if (_previewTextureID == 0) { if (_previewTextureID == 0) {
QImage previewTexture(PathUtils::resourcesPath() + "images/preview.png"); QImage previewTexture(PathUtils::resourcesPath() + "images/preview.png");
if (!previewTexture.isNull()) { if (!previewTexture.isNull()) {
@ -83,18 +94,138 @@ bool HmdDisplayPlugin::internalActivate() {
_firstPreview = true; _firstPreview = true;
} }
} }
#endif
return Parent::internalActivate(); return Parent::internalActivate();
} }
void HmdDisplayPlugin::internalDeactivate() { void HmdDisplayPlugin::internalDeactivate() {
if (_previewTextureID != 0) {
glDeleteTextures(1, &_previewTextureID);
_previewTextureID = 0;
}
Parent::internalDeactivate(); 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() { void HmdDisplayPlugin::customizeContext() {
Parent::customizeContext(); Parent::customizeContext();
// Only enable mirroring if we know vsync is disabled // Only enable mirroring if we know vsync is disabled
@ -103,32 +234,15 @@ void HmdDisplayPlugin::customizeContext() {
enableVsync(false); enableVsync(false);
#endif #endif
_enablePreview = !isVsyncEnabled(); _enablePreview = !isVsyncEnabled();
_sphereSection = loadSphereSection(_program, CompositorHelper::VIRTUAL_UI_TARGET_FOV.y, CompositorHelper::VIRTUAL_UI_ASPECT_RATIO); _overlay.build();
using namespace oglplus; #if 0
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();
}
updateReprojectionProgram(); updateReprojectionProgram();
updateOverlayProgram();
#ifdef HMD_HAND_LASER_SUPPORT
updateLaserProgram(); updateLaserProgram();
_laserGeometry = loadLaser(_laserProgram); _laserGeometry = loadLaser(_laserProgram);
#endif #endif
} }
//#define LIVE_SHADER_RELOAD 1 #if 0
static QString readFile(const QString& filename) {
QFile file(filename);
file.open(QFile::Text | QFile::ReadOnly);
QString result;
result.append(QTextStream(&file).readAll());
return result;
}
void HmdDisplayPlugin::updateReprojectionProgram() { void HmdDisplayPlugin::updateReprojectionProgram() {
static const QString vsFile = PathUtils::resourcesPath() + "/shaders/hmd_reproject.vert"; static const QString vsFile = PathUtils::resourcesPath() + "/shaders/hmd_reproject.vert";
static const QString fsFile = PathUtils::resourcesPath() + "/shaders/hmd_reproject.frag"; static const QString fsFile = PathUtils::resourcesPath() + "/shaders/hmd_reproject.frag";
@ -161,11 +275,11 @@ void HmdDisplayPlugin::updateReprojectionProgram() {
qWarning() << "Error building reprojection shader " << error.what(); qWarning() << "Error building reprojection shader " << error.what();
} }
} }
} }
#endif
#ifdef HMD_HAND_LASER_SUPPORT
void HmdDisplayPlugin::updateLaserProgram() { void HmdDisplayPlugin::updateLaserProgram() {
#if 0
static const QString vsFile = PathUtils::resourcesPath() + "/shaders/hmd_hand_lasers.vert"; 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 gsFile = PathUtils::resourcesPath() + "/shaders/hmd_hand_lasers.geom";
static const QString fsFile = PathUtils::resourcesPath() + "/shaders/hmd_hand_lasers.frag"; 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(); qWarning() << "Error building hand laser composite shader " << error.what();
} }
} }
}
#endif #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() { void HmdDisplayPlugin::uncustomizeContext() {
#if 0
_overlayProgram.reset(); _overlayProgram.reset();
_sphereSection.reset(); _sphereSection.reset();
_compositeFramebuffer.reset(); _compositeFramebuffer.reset();
_previewProgram.reset(); _previewProgram.reset();
_reprojectionProgram.reset(); _reprojectionProgram.reset();
#ifdef HMD_HAND_LASER_SUPPORT
_laserProgram.reset(); _laserProgram.reset();
_laserGeometry.reset(); _laserGeometry.reset();
#endif #endif
@ -277,6 +351,7 @@ void HmdDisplayPlugin::compositeScene() {
#ifdef DEBUG_REPROJECTION_SHADER #ifdef DEBUG_REPROJECTION_SHADER
_reprojectionProgram = getReprojectionProgram(); _reprojectionProgram = getReprojectionProgram();
#endif #endif
#if 0
useProgram(_reprojectionProgram); useProgram(_reprojectionProgram);
using namespace oglplus; using namespace oglplus;
@ -290,20 +365,23 @@ void HmdDisplayPlugin::compositeScene() {
glUniformMatrix4fv(_reprojectionUniforms.projectionMatrix, 2, GL_FALSE, &(_eyeProjections[0][0][0])); glUniformMatrix4fv(_reprojectionUniforms.projectionMatrix, 2, GL_FALSE, &(_eyeProjections[0][0][0]));
_plane->UseInProgram(*_reprojectionProgram); _plane->UseInProgram(*_reprojectionProgram);
_plane->Draw(); _plane->Draw();
#endif
} }
void HmdDisplayPlugin::compositeOverlay() { void HmdDisplayPlugin::compositeOverlay() {
using namespace oglplus; if (!_currentFrame) {
return;
}
auto compositorHelper = DependencyManager::get<CompositorHelper>(); auto compositorHelper = DependencyManager::get<CompositorHelper>();
glm::mat4 modelMat = compositorHelper->getModelTransform().getMatrix(); glm::mat4 modelMat = compositorHelper->getModelTransform().getMatrix();
withPresentThreadLock([&] { withPresentThreadLock([&] {
_presentHandLasers = _handLasers; _presentHandLasers = _handLasers;
_presentHandPoses = _handPoses; _presentHandPoses = _handPoses;
_presentUiModelTransform = _uiModelTransform; _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 // compute the glow point interesections
for (int i = 0; i < NUMBER_OF_HANDS; ++i) { for (int i = 0; i < NUMBER_OF_HANDS; ++i) {
if (_presentHandPoses[i] == IDENTITY_MATRIX) { if (_presentHandPoses[i] == IDENTITY_MATRIX) {
@ -353,65 +431,49 @@ void HmdDisplayPlugin::compositeOverlay() {
handGlowPoints[i] = yawPitch; handGlowPoints[i] = yawPitch;
} }
updateOverlayProgram(); if (!_currentFrame->overlay) {
if (!_overlayProgram) {
return; 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 // Setup the uniforms
{ {
if (_overlayUniforms.alpha >= 0) { _overlay.uniforms.alpha = _compositeOverlayAlpha;
Uniform<float>(*_overlayProgram, _overlayUniforms.alpha).Set(_compositeOverlayAlpha); _overlay.uniforms.glowPoints = vec4(handGlowPoints[0], handGlowPoints[1]);
} _overlay.uniforms.glowColors[0] = _presentHandLasers[0].color;
if (_overlayUniforms.glowPoints >= 0) { _overlay.uniforms.glowColors[1] = _presentHandLasers[1].color;
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.render();
_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();
});
} }
void HmdDisplayPlugin::compositePointer() { void HmdDisplayPlugin::compositePointer() {
using namespace oglplus; auto& cursorManager = Cursor::Manager::instance();
const auto& cursorData = _cursorsData[cursorManager.getCursor()->getIcon()];
auto compositorHelper = DependencyManager::get<CompositorHelper>(); 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 // Reconstruct the headpose from the eye poses
auto headPosition = vec3(_currentPresentFrameInfo.presentPose[3]); 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) { for_each_eye([&](Eye eye) {
eyeViewport(eye);
auto eyePose = _currentPresentFrameInfo.presentPose * getEyeToHeadTransform(eye); auto eyePose = _currentPresentFrameInfo.presentPose * getEyeToHeadTransform(eye);
auto reticleTransform = compositorHelper->getReticleTransform(eyePose, headPosition); auto reticleTransform = compositorHelper->getReticleTransform(eyePose, headPosition);
auto mvp = _eyeProjections[eye] * reticleTransform; batch.setViewportTransform(eyeViewport(eye));
Uniform<glm::mat4>(*_program, _mvpUniform).Set(mvp); batch.setModelTransform(reticleTransform);
_plane->Draw(); batch.setProjectionTransform(_eyeProjections[eye]);
batch.draw(gpu::TRIANGLE_STRIP, 4);
}); });
// restore the alpha _backend->render(batch);
Uniform<float>(*_program, _alphaUniform).Set(1.0);
} }
void HmdDisplayPlugin::internalPresent() { void HmdDisplayPlugin::internalPresent() {
PROFILE_RANGE_EX(__FUNCTION__, 0xff00ff00, (uint64_t)presentCount()) PROFILE_RANGE_EX(__FUNCTION__, 0xff00ff00, (uint64_t)presentCount())
@ -445,58 +507,45 @@ void HmdDisplayPlugin::internalPresent() {
targetViewportPosition.y = (windowSize.y - targetViewportSize.y) / 2; targetViewportPosition.y = (windowSize.y - targetViewportSize.y) / 2;
} }
if (_enablePreview) { if (_enablePreview) {
using namespace oglplus; Parent::internalPresent();
Context::Clear().ColorBuffer(); //gpu::Batch presentBatch;
auto sourceSize = _compositeFramebuffer->size; //presentBatch.enableStereo(false);
if (_monoPreview) { //presentBatch.setViewTransform(Transform());
sourceSize.x /= 2; //presentBatch.setFramebuffer(gpu::FramebufferPointer());
} //presentBatch.setViewportTransform(ivec4(targetViewportPosition, targetViewportSize));
_compositeFramebuffer->Bound(Framebuffer::Target::Read, [&] { //presentBatch.setResourceTexture(0, _currentFrame->framebuffer->getRenderBuffer(0));
Context::BlitFramebuffer( //presentBatch.setPipeline(_presentPipeline);
0, 0, sourceSize.x, sourceSize.y, //presentBatch.draw(gpu::TRIANGLE_STRIP, 4);
targetViewportPosition.x, targetViewportPosition.y, //_backend->render(presentBatch);
targetViewportPosition.x + targetViewportSize.x, targetViewportPosition.y + targetViewportSize.y, //swapBuffers();
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;
}
postPreview(); postPreview();
} }
void HmdDisplayPlugin::setEyeRenderPose(uint32_t frameIndex, Eye eye, const glm::mat4& pose) {
}
void HmdDisplayPlugin::updateFrameData() { void HmdDisplayPlugin::updateFrameData() {
// Check if we have old frame data to discard // Check if we have old frame data to discard
withPresentThreadLock([&] { static const uint32_t INVALID_FRAME = (uint32_t)(~0);
auto itr = _frameInfos.find(_currentPresentFrameIndex); uint32_t oldFrameIndex = _currentFrame ? _currentFrame->frameIndex : INVALID_FRAME;
if (itr != _frameInfos.end()) {
_frameInfos.erase(itr);
}
});
Parent::updateFrameData(); Parent::updateFrameData();
uint32_t newFrameIndex = _currentFrame ? _currentFrame->frameIndex : INVALID_FRAME;
withPresentThreadLock([&] { if (oldFrameIndex != newFrameIndex) {
_currentPresentFrameInfo = _frameInfos[_currentPresentFrameIndex]; 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 { glm::mat4 HmdDisplayPlugin::getHeadPose() const {
@ -508,7 +557,7 @@ bool HmdDisplayPlugin::setHandLaser(uint32_t hands, HandLaserMode mode, const ve
info.mode = mode; info.mode = mode;
info.color = color; info.color = color;
info.direction = direction; info.direction = direction;
withRenderThreadLock([&] { withNonPresentThreadLock([&] {
if (hands & Hand::LeftHand) { if (hands & Hand::LeftHand) {
_handLasers[0] = info; _handLasers[0] = info;
} }
@ -522,7 +571,7 @@ bool HmdDisplayPlugin::setHandLaser(uint32_t hands, HandLaserMode mode, const ve
} }
void HmdDisplayPlugin::compositeExtra() { void HmdDisplayPlugin::compositeExtra() {
#ifdef HMD_HAND_LASER_SUPPORT #if 0
// If neither hand laser is activated, exit // If neither hand laser is activated, exit
if (!_presentHandLasers[0].valid() && !_presentHandLasers[1].valid()) { if (!_presentHandLasers[0].valid() && !_presentHandLasers[1].valid()) {
return; return;
@ -592,4 +641,6 @@ void HmdDisplayPlugin::compositeExtra() {
}); });
glDisable(GL_BLEND); glDisable(GL_BLEND);
#endif #endif
} }

View file

@ -9,18 +9,21 @@
#include <ThreadSafeValueCache.h> #include <ThreadSafeValueCache.h>
#include <array>
#include <QtGlobal> #include <QtGlobal>
#include <Transform.h> #include <Transform.h>
#include "../OpenGLDisplayPlugin.h" #include <gpu/Format.h>
#include <gpu/Stream.h>
#ifdef Q_OS_WIN #include "../CompositorHelper.h"
#define HMD_HAND_LASER_SUPPORT #include "../OpenGLDisplayPlugin.h"
#endif
class HmdDisplayPlugin : public OpenGLDisplayPlugin { class HmdDisplayPlugin : public OpenGLDisplayPlugin {
using Parent = OpenGLDisplayPlugin; using Parent = OpenGLDisplayPlugin;
public: public:
HmdDisplayPlugin() : _overlay( *this ) {}
bool isHmd() const override final { return true; } bool isHmd() const override final { return true; }
float getIPD() const override final { return _ipd; } float getIPD() const override final { return _ipd; }
glm::mat4 getEyeToHeadTransform(Eye eye) const override final { return _eyeOffsets[eye]; } 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::mat4 getCullingProjection(const glm::mat4& baseProjection) const override final { return _cullingProjection; }
glm::uvec2 getRecommendedUiSize() const override final; glm::uvec2 getRecommendedUiSize() const override final;
glm::uvec2 getRecommendedRenderSize() const override final { return _renderTargetSize; } 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(); } bool isDisplayVisible() const override { return isHmdMounted(); }
QRect getRecommendedOverlayRect() const override final; QRect getRecommendedOverlayRect() const override final;
@ -65,6 +67,9 @@ protected:
} }
}; };
Transform _uiModelTransform; Transform _uiModelTransform;
std::array<HandLaserInfo, 2> _handLasers; std::array<HandLaserInfo, 2> _handLasers;
std::array<glm::mat4, 2> _handPoses; std::array<glm::mat4, 2> _handPoses;
@ -96,10 +101,7 @@ protected:
FrameInfo _currentRenderFrameInfo; FrameInfo _currentRenderFrameInfo;
private: private:
void updateOverlayProgram();
#ifdef HMD_HAND_LASER_SUPPORT
void updateLaserProgram(); void updateLaserProgram();
#endif
void updateReprojectionProgram(); void updateReprojectionProgram();
bool _enablePreview { false }; bool _enablePreview { false };
@ -107,26 +109,53 @@ private:
bool _enableReprojection { true }; bool _enableReprojection { true };
bool _firstPreview { true }; bool _firstPreview { true };
ProgramPtr _overlayProgram; float _previewAspect { 0 };
struct OverlayUniforms { glm::uvec2 _prevWindowSize { 0, 0 };
int32_t mvp { -1 }; qreal _prevDevicePixelRatio { 0 };
int32_t alpha { -1 };
int32_t glowColors { -1 };
int32_t glowPoints { -1 };
int32_t resolution { -1 };
int32_t radius { -1 };
} _overlayUniforms;
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; ProgramPtr _previewProgram;
struct PreviewUniforms { struct PreviewUniforms {
int32_t previewTexture { -1 }; int32_t previewTexture { -1 };
} _previewUniforms; } _previewUniforms;
float _previewAspect { 0 };
GLuint _previewTextureID { 0 };
glm::uvec2 _prevWindowSize { 0, 0 };
qreal _prevDevicePixelRatio { 0 };
ProgramPtr _reprojectionProgram; ProgramPtr _reprojectionProgram;
struct ReprojectionUniforms { struct ReprojectionUniforms {
int32_t reprojectionMatrix { -1 }; int32_t reprojectionMatrix { -1 };
@ -134,9 +163,6 @@ private:
int32_t projectionMatrix { -1 }; int32_t projectionMatrix { -1 };
} _reprojectionUniforms; } _reprojectionUniforms;
ShapeWrapperPtr _sphereSection;
#ifdef HMD_HAND_LASER_SUPPORT
ProgramPtr _laserProgram; ProgramPtr _laserProgram;
struct LaserUniforms { struct LaserUniforms {
int32_t mvp { -1 }; int32_t mvp { -1 };
@ -145,4 +171,3 @@ private:
ShapeWrapperPtr _laserGeometry; ShapeWrapperPtr _laserGeometry;
#endif #endif
}; };

View file

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

View file

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

View file

@ -7,16 +7,11 @@
// //
#include "SideBySideStereoDisplayPlugin.h" #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"); const QString SideBySideStereoDisplayPlugin::NAME("3D TV - Side by Side Stereo");
glm::uvec2 SideBySideStereoDisplayPlugin::getRecommendedRenderSize() const { glm::uvec2 SideBySideStereoDisplayPlugin::getRecommendedRenderSize() const {
uvec2 result = Parent::getRecommendedRenderSize(); uvec2 result = Parent::getRecommendedRenderSize();
result.x *= 2; //result.x *= 2;
return result; return result;
} }

View file

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

View file

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

View file

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

View file

@ -120,24 +120,9 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] =
(&::gpu::gl::GLBackend::do_popProfileRange), (&::gpu::gl::GLBackend::do_popProfileRange),
}; };
extern std::function<uint32(const Texture& texture)> TEXTURE_ID_RESOLVER;
void GLBackend::init() { void GLBackend::init() {
static std::once_flag once; static std::once_flag once;
std::call_once(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 vendor{ (const char*)glGetString(GL_VENDOR) };
QString renderer{ (const char*)glGetString(GL_RENDERER) }; QString renderer{ (const char*)glGetString(GL_RENDERER) };
qCDebug(gpugllogging) << "GL Version: " << QString((const char*) glGetString(GL_VERSION)); qCDebug(gpugllogging) << "GL Version: " << QString((const char*) glGetString(GL_VERSION));

View file

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

View file

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

View file

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

View file

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

View file

@ -20,13 +20,6 @@ Context::Context() {
if (_createBackendCallback) { if (_createBackendCallback) {
_backend.reset(_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) { Context::Context(const Context& context) {
@ -35,40 +28,30 @@ Context::Context(const Context& context) {
Context::~Context() { Context::~Context() {
} }
void Context::setFrameHandler(FrameHandler handler) { void Context::beginFrame(const glm::mat4& renderPose) {
_frameHandler = handler; assert(!_frameActive);
}
#define DEFERRED_RENDERING
void Context::beginFrame(const FramebufferPointer& outputFramebuffer, const glm::mat4& renderPose) {
_currentFrame = Frame();
_currentFrame.framebuffer = outputFramebuffer;
_currentFrame.pose = renderPose;
_frameActive = true; _frameActive = true;
_currentFrame = std::make_shared<Frame>();
_currentFrame->pose = renderPose;
} }
void Context::append(Batch& batch) { void Context::append(Batch& batch) {
if (!_frameActive) { if (!_frameActive) {
qWarning() << "Batch executed outside of frame boundaries"; qWarning() << "Batch executed outside of frame boundaries";
return;
} }
#ifdef DEFERRED_RENDERING _currentFrame->batches.push_back(batch);
_currentFrame.batches.emplace_back(batch);
_currentFrame.stereoStates.emplace_back(_stereo);
#else
_backend->_stereo = _stereo;
_backend->render(batch);
#endif
} }
void Context::endFrame() { FramePointer Context::endFrame() {
#ifdef DEFERRED_RENDERING assert(_frameActive);
if (_frameHandler) { auto result = _currentFrame;
_frameHandler(_currentFrame); _currentFrame.reset();
}
#endif
_currentFrame = Frame();
_frameActive = false; _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) { void Context::downloadFramebuffer(const FramebufferPointer& srcFramebuffer, const Vec4i& region, QImage& destImage) {
_backend->downloadFramebuffer(srcFramebuffer, region, destImage); _backend->downloadFramebuffer(srcFramebuffer, region, destImage);
} }
void Context::getStats(ContextStats& stats) const { void Context::getStats(ContextStats& stats) const {
_backend->getStats(stats); _backend->getStats(stats);
} }

View file

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

View file

@ -11,20 +11,23 @@
#include <stdint.h> #include <stdint.h>
#include <memory> #include <memory>
#include <mutex>
#include <vector> #include <vector>
#include <functional>
#include <glm/glm.hpp> #include <glm/glm.hpp>
namespace gpu { namespace gpu {
using Mutex = std::mutex;
using Lock = std::unique_lock<Mutex>;
class Batch; class Batch;
class Backend; class Backend;
using BackendPointer = std::shared_ptr<Backend>;
class Context; class Context;
using ContextPointer = std::shared_ptr<Context>; using ContextPointer = std::shared_ptr<Context>;
class GPUObject; class GPUObject;
class Frame; class Frame;
using FramePointer = std::shared_ptr<Frame>; using FramePointer = std::shared_ptr<Frame>;
using FrameHandler = std::function<void(Frame& frame)>;
using Stamp = int; using Stamp = int;
using uint32 = uint32_t; 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 #ifndef hifi_gpu_Frame_h
#define hifi_gpu_Frame_h #define hifi_gpu_Frame_h
#include <functional>
#include "Forward.h" #include "Forward.h"
#include "Batch.h"
#include "Resource.h"
namespace gpu { namespace gpu {
class Frame { class Frame {
public: public:
/// The sensor pose used for rendering the frame, only applicable for HMDs using Batches = std::vector<Batch>;
glm::mat4 pose; using FramebufferRecycler = std::function<void(const FramebufferPointer&)>;
/// The collection of batches which make up the frame using OverlayRecycler = std::function<void(const TexturePointer&)>;
std::vector<Batch> batches; using BufferUpdate = std::pair<BufferPointer, Buffer::Update>;
std::vector<StereoState> stereoStates; using BufferUpdates = std::vector<BufferUpdate>;
/// The destination framebuffer in which the results will be placed
FramebufferPointer framebuffer; 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; static AllocationDebugger allocationDebugger;
Resource::Size Resource::Sysmem::allocateMemory(Byte** dataAllocated, Size size) { Size Sysmem::allocateMemory(Byte** dataAllocated, Size size) {
allocationDebugger += size; allocationDebugger += size;
if ( !dataAllocated ) { if ( !dataAllocated ) {
qWarning() << "Buffer::Sysmem::allocateMemory() : Must have a valid dataAllocated pointer."; 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; return newSize;
} }
void Resource::Sysmem::deallocateMemory(Byte* dataAllocated, Size size) { void Sysmem::deallocateMemory(Byte* dataAllocated, Size size) {
allocationDebugger -= size; allocationDebugger -= size;
if (dataAllocated) { if (dataAllocated) {
delete[] 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) { if (size > 0 && bytes) {
setData(_size, bytes); setData(_size, bytes);
} }
} }
Resource::Sysmem::Sysmem(const Sysmem& sysmem) { Sysmem::Sysmem(const Sysmem& sysmem) {
if (sysmem.getSize() > 0) { if (sysmem.getSize() > 0) {
allocate(sysmem._size); allocate(sysmem._size);
setData(_size, sysmem._data); setData(_size, sysmem._data);
} }
} }
Resource::Sysmem& Resource::Sysmem::operator=(const Sysmem& sysmem) { Sysmem& Sysmem::operator=(const Sysmem& sysmem) {
setData(sysmem.getSize(), sysmem.readData()); setData(sysmem.getSize(), sysmem.readData());
return (*this); return (*this);
} }
Resource::Sysmem::~Sysmem() { Sysmem::~Sysmem() {
deallocateMemory( _data, _size ); deallocateMemory( _data, _size );
_data = NULL; _data = NULL;
_size = 0; _size = 0;
} }
Resource::Size Resource::Sysmem::allocate(Size size) { Size Sysmem::allocate(Size size) {
if (size != _size) { if (size != _size) {
Byte* newData = NULL; Byte* newData = NULL;
Size newSize = 0; Size newSize = 0;
@ -156,7 +156,7 @@ Resource::Size Resource::Sysmem::allocate(Size size) {
return _size; return _size;
} }
Resource::Size Resource::Sysmem::resize(Size size) { Size Sysmem::resize(Size size) {
if (size != _size) { if (size != _size) {
Byte* newData = NULL; Byte* newData = NULL;
Size newSize = 0; Size newSize = 0;
@ -182,7 +182,7 @@ Resource::Size Resource::Sysmem::resize(Size size) {
return _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 (allocate(size) == size) {
if (size && bytes) { if (size && bytes) {
memcpy( _data, bytes, _size ); memcpy( _data, bytes, _size );
@ -191,7 +191,7 @@ Resource::Size Resource::Sysmem::setData( Size size, const Byte* bytes ) {
return _size; 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) { if (size && ((offset + size) <= getSize()) && bytes) {
memcpy( _data + offset, bytes, size ); memcpy( _data + offset, bytes, size );
return size; return size;
@ -199,7 +199,7 @@ Resource::Size Resource::Sysmem::setSubData( Size offset, Size size, const Byte*
return 0; return 0;
} }
Resource::Size Resource::Sysmem::append(Size size, const Byte* bytes) { Size Sysmem::append(Size size, const Byte* bytes) {
if (size > 0) { if (size > 0) {
Size oldSize = getSize(); Size oldSize = getSize();
Size totalSize = oldSize + size; Size totalSize = oldSize + size;
@ -241,7 +241,7 @@ Buffer::Size Buffer::getBufferGPUMemoryUsage() {
} }
Buffer::Buffer(Size pageSize) : Buffer::Buffer(Size pageSize) :
_pageSize(pageSize) { _pages(pageSize) {
_bufferCPUCount++; _bufferCPUCount++;
} }
@ -249,12 +249,12 @@ Buffer::Buffer(Size size, const Byte* bytes, Size pageSize) : Buffer(pageSize) {
setData(size, bytes); 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()); setData(buf.getSize(), buf.getData());
} }
Buffer& Buffer::operator=(const Buffer& buf) { 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()); setData(buf.getSize(), buf.getData());
return (*this); return (*this);
} }
@ -266,14 +266,10 @@ Buffer::~Buffer() {
Buffer::Size Buffer::resize(Size size) { Buffer::Size Buffer::resize(Size size) {
_end = size; _end = size;
auto prevSize = editSysmem().getSize(); auto prevSize = _sysmem.getSize();
if (prevSize < size) { if (prevSize < size) {
auto newPages = getRequiredPageCount(); _sysmem.resize(_pages.accommodate(_end));
auto newSize = newPages * _pageSize; Buffer::updateBufferCPUMemoryUsage(prevSize, _sysmem.getSize());
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);
} }
return _end; return _end;
} }
@ -282,28 +278,45 @@ void Buffer::markDirty(Size offset, Size bytes) {
if (!bytes) { if (!bytes) {
return; 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); _pages.markRegion(offset, bytes);
if (bytes % _pageSize) { }
++pageCount;
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);
} }
} };
return result;
// Mark the pages dirty
for (Size i = 0; i < pageCount; ++i) {
_pages[i + startPage] |= DIRTY;
}
} }
@ -333,14 +346,6 @@ Buffer::Size Buffer::getSize() const {
return _end; 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 ); const Element BufferView::DEFAULT_ELEMENT = Element( gpu::SCALAR, gpu::UINT8, gpu::RAW );
BufferView::BufferView() : BufferView::BufferView() :

View file

@ -25,11 +25,69 @@
namespace gpu { 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 { class Resource {
public: public:
typedef size_t Size; 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 // The size in bytes of data stored in the resource
virtual Size getSize() const = 0; virtual Size getSize() const = 0;
@ -47,88 +105,178 @@ public:
}; };
protected: protected:
using Sysmem = gpu::Sysmem;
Resource() {} Resource() {}
virtual ~Resource() {} virtual ~Resource() {}
// Sysmem is the underneath cache for the data in ram of a resource. }; // Resource
class Sysmem {
public:
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 enum Flag {
// \param pSize The nb of bytes to allocate, if already exist, content is lost. DIRTY = 0x01,
// \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 };
}; };
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 { class Buffer : public Resource {
static std::atomic<uint32_t> _bufferCPUCount; static std::atomic<uint32_t> _bufferCPUCount;
static std::atomic<Size> _bufferCPUMemoryUsage; static std::atomic<Size> _bufferCPUMemoryUsage;
static void updateBufferCPUMemoryUsage(Size prevObjectSize, Size newObjectSize); static void updateBufferCPUMemoryUsage(Size prevObjectSize, Size newObjectSize);
public: public:
enum Flag { using Flag = PageManager::Flag;
DIRTY = 0x01, struct Update {
Size size;
PageManager pages;
Sysmem::Operator updateOperator;
}; };
// Currently only one flag... 'dirty' // Currently only one flag... 'dirty'
using PageFlags = std::vector<uint8_t>;
static const Size DEFAULT_PAGE_SIZE = 4096;
static uint32_t getBufferCPUCount(); static uint32_t getBufferCPUCount();
static Size getBufferCPUMemoryUsage(); static Size getBufferCPUMemoryUsage();
static uint32_t getBufferGPUCount(); static uint32_t getBufferGPUCount();
static Size getBufferGPUMemoryUsage(); static Size getBufferGPUMemoryUsage();
Buffer(Size pageSize = DEFAULT_PAGE_SIZE); Buffer(Size pageSize = PageManager::DEFAULT_PAGE_SIZE);
Buffer(Size size, const Byte* bytes, Size pageSize = 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(const Buffer& buf); // deep copy of the sysmem buffer
Buffer& operator=(const Buffer& buf); // deep copy of the sysmem buffer Buffer& operator=(const Buffer& buf); // deep copy of the sysmem buffer
~Buffer(); ~Buffer();
@ -184,34 +332,24 @@ public:
return append(sizeof(T) * t.size(), reinterpret_cast<const Byte*>(&t[0])); 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 {}; const GPUObjectPointer gpuObject {};
// Access the sysmem object, limited to ourselves and GPUObject derived classes // Access the sysmem object, limited to ourselves and GPUObject derived classes
const Sysmem& getSysmem() const { return _sysmem; } 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: protected:
void markDirty(Size offset, Size bytes); void markDirty(Size offset, Size bytes);
@ -223,16 +361,15 @@ protected:
Sysmem& editSysmem() { return _sysmem; } Sysmem& editSysmem() { return _sysmem; }
Byte* editData() { return editSysmem().editData(); } Byte* editData() { return editSysmem().editData(); }
Size getRequiredPageCount() const; mutable PageManager _pages;
Size _end{ 0 };
Size _end { 0 };
mutable PageFlags _pages;
const Size _pageSize;
Sysmem _sysmem; Sysmem _sysmem;
// FIXME find a more generic way to do this. // FIXME find a more generic way to do this.
friend class gl::GLBuffer; friend class gl::GLBuffer;
friend class BufferView; friend class BufferView;
friend class Frame;
}; };
typedef std::shared_ptr<Buffer> BufferPointer; typedef std::shared_ptr<Buffer> BufferPointer;

View file

@ -880,11 +880,3 @@ Vec3u Texture::evalMipDimensions(uint16 level) const {
return glm::max(dimensions, Vec3u(1)); 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 {}; const GPUObjectPointer gpuObject {};
uint32 getHardwareId() const;
protected: protected:
std::unique_ptr< Storage > _storage; std::unique_ptr< Storage > _storage;

View file

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

View file

@ -20,6 +20,7 @@
#include <GLMHelpers.h> #include <GLMHelpers.h>
#include <RegisteredMetaTypes.h> #include <RegisteredMetaTypes.h>
#include <shared/Bilateral.h> #include <shared/Bilateral.h>
#include <gpu/Forward.h>
#include "Plugin.h" #include "Plugin.h"
@ -91,11 +92,6 @@ public:
return glm::mat4(); 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 abandonCalibration() {}
virtual void resetSensors() {} virtual void resetSensors() {}
@ -132,6 +128,7 @@ public:
Present = QEvent::User + 1 Present = QEvent::User + 1
}; };
virtual bool isRenderThread() const { return false; }
virtual bool isHmd() const { return false; } virtual bool isHmd() const { return false; }
virtual int getHmdScreen() const { return -1; } virtual int getHmdScreen() const { return -1; }
/// By default, all HMDs are stereo /// By default, all HMDs are stereo
@ -149,16 +146,8 @@ public:
virtual QString getPreferredAudioOutDevice() const { return QString(); } virtual QString getPreferredAudioOutDevice() const { return QString(); }
// Rendering support // Rendering support
virtual void setBackend(const gpu::BackendPointer& backend) final { _backend = backend; }
/** virtual void submitFrame(const gpu::FramePointer& newFrame) = 0;
* 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;
// Does the rendering surface have current focus? // Does the rendering surface have current focus?
virtual bool hasFocus() const = 0; virtual bool hasFocus() const = 0;
@ -212,6 +201,8 @@ signals:
protected: protected:
void incrementPresentCount(); void incrementPresentCount();
gpu::BackendPointer _backend;
private: private:
std::atomic<uint32_t> _presentedFrameIndex; std::atomic<uint32_t> _presentedFrameIndex;
mutable std::mutex _paintDelayMutex; mutable std::mutex _paintDelayMutex;

View file

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

View file

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

View file

@ -14,6 +14,7 @@
#include <ui/Menu.h> #include <ui/Menu.h>
#include <MainWindow.h> #include <MainWindow.h>
#include <plugins/DisplayPlugin.h>
static PluginContainer* INSTANCE{ nullptr }; static PluginContainer* INSTANCE{ nullptr };
@ -159,3 +160,8 @@ void PluginContainer::setBoolSetting(const QString& settingName, bool value) {
Setting::Handle<bool> settingValue(settingName, value); Setting::Handle<bool> settingValue(settingName, value);
return settingValue.set(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 showDisplayPluginsTools(bool show = true) = 0;
virtual void requestReset() = 0; virtual void requestReset() = 0;
virtual bool makeRenderingContextCurrent() = 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 GLWidget* getPrimaryWidget() = 0;
virtual MainWindow* getPrimaryWindow() = 0; virtual MainWindow* getPrimaryWindow() = 0;
virtual QOpenGLContext* getPrimaryContext() = 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 # See the accompanying file LICENSE or http:#www.apache.org/licenses/LICENSE-2.0.html
# #
if (FALSE)
if (WIN32) if (WIN32)
# we're using static GLEW, so define GLEW_STATIC # 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} ${LIBOVR_LIBRARIES})
target_link_libraries(${TARGET_NAME} Winmm.lib) target_link_libraries(${TARGET_NAME} Winmm.lib)
endif()
endif() endif()

View file

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

View file

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

View file

@ -85,8 +85,6 @@ public:
virtual void showDisplayPluginsTools(bool show) override {} virtual void showDisplayPluginsTools(bool show) override {}
virtual void requestReset() override {} virtual void requestReset() override {}
virtual bool makeRenderingContextCurrent() override { return true; } 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 GLWidget* getPrimaryWidget() override { return nullptr; }
virtual MainWindow* getPrimaryWindow() override { return nullptr; } virtual MainWindow* getPrimaryWindow() override { return nullptr; }
virtual QOpenGLContext* getPrimaryContext() override { return nullptr; } virtual QOpenGLContext* getPrimaryContext() override { return nullptr; }

View file

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

View file

@ -30,7 +30,6 @@
#include <AssetClient.h> #include <AssetClient.h>
#include <gl/OffscreenGLCanvas.h> #include <gl/OffscreenGLCanvas.h>
#include <gl/OglplusHelpers.h>
#include <gl/GLHelpers.h> #include <gl/GLHelpers.h>
#include <gl/QOpenGLContextWrapper.h> #include <gl/QOpenGLContextWrapper.h>
#include <gl/QOpenGLDebugLoggerWrapper.h> #include <gl/QOpenGLDebugLoggerWrapper.h>
@ -38,6 +37,7 @@
#include <gpu/gl/GLBackend.h> #include <gpu/gl/GLBackend.h>
#include <gpu/gl/GLFramebuffer.h> #include <gpu/gl/GLFramebuffer.h>
#include <gpu/gl/GLTexture.h> #include <gpu/gl/GLTexture.h>
#include <gpu/StandardShaderLib.h>
#include <WebEntityItem.h> #include <WebEntityItem.h>
#include <OctreeUtils.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]); 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 // Create a simple OpenGL window that renders text in various ways
class QTestWindow : public QWindow, public AbstractViewStateInterface { class QTestWindow : public QWindow, public AbstractViewStateInterface {
@ -185,6 +296,7 @@ protected:
} }
void postLambdaEvent(std::function<void()> f) override {} void postLambdaEvent(std::function<void()> f) override {}
qreal getDevicePixelRatio() override { qreal getDevicePixelRatio() override {
return 1.0f; return 1.0f;
} }
@ -192,6 +304,7 @@ protected:
render::ScenePointer getMain3DScene() override { render::ScenePointer getMain3DScene() override {
return _main3DScene; return _main3DScene;
} }
render::EnginePointer getRenderEngine() override { render::EnginePointer getRenderEngine() override {
return _renderEngine; return _renderEngine;
} }
@ -221,12 +334,13 @@ public:
} }
QTestWindow() { QTestWindow() {
_camera.movementSpeed = 50.0f;
QThread::currentThread()->setPriority(QThread::HighestPriority); QThread::currentThread()->setPriority(QThread::HighestPriority);
AbstractViewStateInterface::setInstance(this); AbstractViewStateInterface::setInstance(this);
_octree = DependencyManager::set<EntityTreeRenderer>(false, this, nullptr); _octree = DependencyManager::set<EntityTreeRenderer>(false, this, nullptr);
_octree->init(); _octree->init();
// Prevent web entities from rendering // 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()); DependencyManager::set<ParentFinder>(_octree->getTree());
getEntities()->setViewFrustum(_viewFrustum); getEntities()->setViewFrustum(_viewFrustum);
@ -241,11 +355,12 @@ public:
format.setOption(QSurfaceFormat::DebugContext); format.setOption(QSurfaceFormat::DebugContext);
setFormat(format); setFormat(format);
_context.setFormat(format);
_context.create();
resize(QSize(800, 600)); resize(QSize(800, 600));
show(); show();
makeCurrent();
_context.setFormat(format);
_context.create();
_context.makeCurrent(this);
glewExperimental = true; glewExperimental = true;
glewInit(); glewInit();
glGetError(); glGetError();
@ -253,40 +368,24 @@ public:
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
wglSwapIntervalEXT(0); wglSwapIntervalEXT(0);
#endif #endif
{ _context.doneCurrent();
makeCurrent();
_quadProgram = loadDefaultShader();
_plane = loadPlane(_quadProgram);
_textOverlay = new TextOverlay(glm::uvec2(800, 600));
glViewport(0, 0, 800, 600);
}
_camera.movementSpeed = 50.0f; _initContext.create(_context.getContext());
_renderThread.initialize(&_context, this);
// FIXME use a wait condition
// GPU library init QThread::msleep(1000);
{ _renderThread.queueItem(gpu::FramePointer());
_offscreenContext = new OffscreenGLCanvas(); _initContext.makeCurrent();
_offscreenContext->create(_context.getContext()); // Render engine init
_offscreenContext->makeCurrent(); _renderEngine->addJob<RenderShadowTask>("RenderShadowTask", _cullFunctor);
gpu::Context::init<gpu::gl::GLBackend>(); _renderEngine->addJob<RenderDeferredTask>("RenderDeferredTask", _cullFunctor);
_gpuContext = std::make_shared<gpu::Context>(); _renderEngine->load();
} _renderEngine->registerScene(_main3DScene);
// Render engine library init // 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(); reloadScene();
restorePosition(); restorePosition();
_elapsed.start();
QTimer* timer = new QTimer(this); QTimer* timer = new QTimer(this);
timer->setInterval(0); timer->setInterval(0);
connect(timer, &QTimer::timeout, this, [this] { connect(timer, &QTimer::timeout, this, [this] {
@ -298,8 +397,6 @@ public:
virtual ~QTestWindow() { virtual ~QTestWindow() {
ResourceManager::cleanup(); ResourceManager::cleanup();
try { _quadProgram.reset(); } catch (std::runtime_error&) {}
try { _plane.reset(); } catch (std::runtime_error&) {}
} }
protected: protected:
@ -363,6 +460,7 @@ private:
return (renderAccuracy > 0.0f); return (renderAccuracy > 0.0f);
} }
uint16_t _fps;
void draw() { void draw() {
if (!_ready) { if (!_ready) {
return; return;
@ -370,17 +468,21 @@ private:
if (!isVisible()) { if (!isVisible()) {
return; return;
} }
if (_renderCount.load() >= _renderThread._presentCount.load()) {
return;
}
_renderCount = _renderThread._presentCount.load();
update(); update();
_offscreenContext->makeCurrent(); RenderArgs renderArgs(_renderThread._gpuContext, _octree.data(), DEFAULT_OCTREE_SIZE_SCALE,
RenderArgs renderArgs(_gpuContext, _octree.data(), DEFAULT_OCTREE_SIZE_SCALE,
0, RenderArgs::DEFAULT_RENDER_MODE, 0, RenderArgs::DEFAULT_RENDER_MODE,
RenderArgs::MONO, RenderArgs::RENDER_DEBUG_NONE); RenderArgs::MONO, RenderArgs::RENDER_DEBUG_NONE);
auto framebufferCache = DependencyManager::get<FramebufferCache>(); auto framebufferCache = DependencyManager::get<FramebufferCache>();
QSize windowSize = size(); QSize windowSize = size();
framebufferCache->setFrameBufferSize(windowSize); framebufferCache->setFrameBufferSize(windowSize);
renderArgs._blitFramebuffer = framebufferCache->getFramebuffer();
// Viewport is assigned to the size of the framebuffer // Viewport is assigned to the size of the framebuffer
renderArgs._viewport = ivec4(0, 0, windowSize.width(), windowSize.height()); renderArgs._viewport = ivec4(0, 0, windowSize.width(), windowSize.height());
@ -398,49 +500,12 @@ private:
} }
// Final framebuffer that will be handled to the display-plugin // 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); render(&renderArgs);
_gpuContext->endFrame();
GLuint glTex;
{
auto gpuTex = renderArgs._blitFramebuffer->getRenderBuffer(0);
glTex = gpu::Backend::getGPUObject<gpu::gl::GLTexture>(*gpuTex)->_id;
}
makeCurrent(); if (_fps != _renderThread._fps) {
{ _fps = _renderThread._fps;
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();
updateText(); updateText();
_frameCount = 0;
_elapsed.restart();
} }
} }
@ -467,7 +532,12 @@ private:
void updateText() { 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); _textBlocks.erase(TextBlock::Info);
auto& infoTextBlock = _textBlocks[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(100, 10), std::to_string((uint32_t)_fps), TextOverlay::alignLeft });
infoTextBlock.push_back({ vec2(98, 30), "Culling: ", TextOverlay::alignRight }); infoTextBlock.push_back({ vec2(98, 30), "Culling: ", TextOverlay::alignRight });
infoTextBlock.push_back({ vec2(100, 30), _cullingEnabled ? "Enabled" : "Disabled", TextOverlay::alignLeft }); 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(); _textOverlay->beginTextUpdate();
for (const auto& e : _textBlocks) { for (const auto& e : _textBlocks) {
for (const auto& b : e.second) { for (const auto& b : e.second) {
@ -489,6 +556,7 @@ private:
} }
} }
_textOverlay->endTextUpdate(); _textOverlay->endTextUpdate();
#endif
} }
void update() { void update() {
@ -525,6 +593,11 @@ private:
} }
void render(RenderArgs* renderArgs) { void render(RenderArgs* renderArgs) {
auto& gpuContext = renderArgs->_context;
gpuContext->beginFrame();
gpu::doInBatch(gpuContext, [&](gpu::Batch& batch) {
batch.resetStages();
});
PROFILE_RANGE(__FUNCTION__); PROFILE_RANGE(__FUNCTION__);
PerformanceTimer perfTimer("draw"); PerformanceTimer perfTimer("draw");
// The pending changes collecting the changes here // The pending changes collecting the changes here
@ -544,6 +617,14 @@ private:
// Before the deferred pass, let's try to use the render engine // Before the deferred pass, let's try to use the render engine
_renderEngine->run(); _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() { bool makeCurrent() {
@ -558,9 +639,8 @@ private:
if (!_ready) { if (!_ready) {
return; return;
} }
_textOverlay->resize(toGlm(_size)); //_textOverlay->resize(toGlm(_size));
makeCurrent(); //glViewport(0, 0, size.width(), size.height());
glViewport(0, 0, size.width(), size.height());
} }
void parsePath(const QString& viewpointString) { void parsePath(const QString& viewpointString) {
@ -704,32 +784,29 @@ private:
std::map<TextBlock, std::list<TextElement>> _textBlocks; std::map<TextBlock, std::list<TextElement>> _textBlocks;
gpu::ContextPointer _gpuContext; // initialized during window creation
render::EnginePointer _renderEngine { new render::Engine() }; render::EnginePointer _renderEngine { new render::Engine() };
render::ScenePointer _main3DScene { new render::Scene(glm::vec3(-0.5f * (float)TREE_SCALE), (float)TREE_SCALE) }; render::ScenePointer _main3DScene { new render::Scene(glm::vec3(-0.5f * (float)TREE_SCALE), (float)TREE_SCALE) };
OffscreenGLCanvas* _offscreenContext { nullptr };
QOpenGLContextWrapper _context; QOpenGLContextWrapper _context;
QSize _size; QSize _size;
RateCounter<200> _fpsCounter;
QSettings _settings; QSettings _settings;
ProgramPtr _quadProgram; std::atomic<size_t> _renderCount;
ShapeWrapperPtr _plane; OffscreenGLCanvas _initContext;
RenderThread _renderThread;
QWindowCamera _camera; QWindowCamera _camera;
ViewFrustum _viewFrustum; // current state of view frustum, perspective, orientation, etc. ViewFrustum _viewFrustum; // current state of view frustum, perspective, orientation, etc.
ViewFrustum _shadowViewFrustum; // current state of view frustum, perspective, orientation, etc. ViewFrustum _shadowViewFrustum; // current state of view frustum, perspective, orientation, etc.
model::SunSkyStage _sunSkyStage; model::SunSkyStage _sunSkyStage;
model::LightPointer _globalLight { std::make_shared<model::Light>() }; model::LightPointer _globalLight { std::make_shared<model::Light>() };
QElapsedTimer _elapsed;
bool _ready { false }; bool _ready { false };
float _fps { 0 }; //TextOverlay* _textOverlay;
TextOverlay* _textOverlay; static bool _cullingEnabled;
bool _cullingEnabled { true };
bool _stereoEnabled { false }; bool _stereoEnabled { false };
QSharedPointer<EntityTreeRenderer> _octree; QSharedPointer<EntityTreeRenderer> _octree;
}; };
bool QTestWindow::_cullingEnabled = false;
void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) { void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) {
if (!message.isEmpty()) { if (!message.isEmpty()) {
#ifdef Q_OS_WIN #ifdef Q_OS_WIN