mirror of
https://github.com/Armored-Dragon/overte.git
synced 2025-03-11 16:13:16 +01:00
First pass at threaded rendering
This commit is contained in:
parent
8a28f7cdac
commit
274321de8a
46 changed files with 1216 additions and 1013 deletions
|
@ -6,18 +6,27 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#version 410 core
|
||||
|
||||
uniform sampler2D sampler;
|
||||
uniform float alpha = 1.0;
|
||||
uniform vec4 glowPoints = vec4(-1);
|
||||
uniform vec4 glowColors[2];
|
||||
uniform vec2 resolution = vec2(3960.0, 1188.0);
|
||||
uniform float radius = 0.005;
|
||||
|
||||
struct OverlayData {
|
||||
mat4 mvp;
|
||||
vec4 glowPoints;
|
||||
vec4 glowColors[2];
|
||||
vec4 resolutionRadiusAlpha;
|
||||
};
|
||||
|
||||
layout(std140) uniform overlayBuffer {
|
||||
OverlayData overlay;
|
||||
};
|
||||
|
||||
vec2 resolution = overlay.resolutionRadiusAlpha.xy;
|
||||
float radius = overlay.resolutionRadiusAlpha.z;
|
||||
float alpha = overlay.resolutionRadiusAlpha.w;
|
||||
vec4 glowPoints = overlay.glowPoints;
|
||||
vec4 glowColors[2] = overlay.glowColors;
|
||||
|
||||
in vec3 vPosition;
|
||||
in vec2 vTexCoord;
|
||||
in vec4 vGlowPoints;
|
||||
|
||||
out vec4 FragColor;
|
||||
|
||||
|
@ -31,9 +40,10 @@ float easeInOutCubic(float f) {
|
|||
}
|
||||
|
||||
void main() {
|
||||
FragColor = texture(sampler, vTexCoord);
|
||||
|
||||
vec2 aspect = resolution;
|
||||
aspect /= resolution.x;
|
||||
FragColor = texture(sampler, vTexCoord);
|
||||
|
||||
float glowIntensity = 0.0;
|
||||
float dist1 = distance(vTexCoord * aspect, glowPoints.xy * aspect);
|
||||
|
|
|
@ -6,12 +6,21 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#version 410 core
|
||||
struct OverlayData {
|
||||
mat4 mvp;
|
||||
vec4 glowPoints;
|
||||
vec4 glowColors[2];
|
||||
vec4 resolutionRadiusAlpha;
|
||||
};
|
||||
|
||||
uniform mat4 mvp = mat4(1);
|
||||
layout(std140) uniform overlayBuffer {
|
||||
OverlayData overlay;
|
||||
};
|
||||
|
||||
in vec3 Position;
|
||||
in vec2 TexCoord;
|
||||
mat4 mvp = overlay.mvp;
|
||||
|
||||
layout(location = 0) in vec3 Position;
|
||||
layout(location = 3) in vec2 TexCoord;
|
||||
|
||||
out vec3 vPosition;
|
||||
out vec2 vTexCoord;
|
||||
|
|
|
@ -779,16 +779,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
|||
_glWidget->makeCurrent();
|
||||
_glWidget->initializeGL();
|
||||
|
||||
_chromiumShareContext = new OffscreenGLCanvas();
|
||||
_chromiumShareContext->create(_glWidget->context()->contextHandle());
|
||||
_chromiumShareContext->makeCurrent();
|
||||
qt_gl_set_global_share_context(_chromiumShareContext->getContext());
|
||||
|
||||
_offscreenContext = new OffscreenGLCanvas();
|
||||
_offscreenContext->create(_glWidget->context()->contextHandle());
|
||||
_offscreenContext->makeCurrent();
|
||||
initializeGL();
|
||||
_offscreenContext->makeCurrent();
|
||||
// Make sure we don't time out during slow operations at startup
|
||||
updateHeartbeat();
|
||||
|
||||
|
@ -1498,11 +1489,18 @@ void Application::initializeGL() {
|
|||
_isGLInitialized = true;
|
||||
}
|
||||
|
||||
_glWidget->makeCurrent();
|
||||
_chromiumShareContext = new OffscreenGLCanvas();
|
||||
_chromiumShareContext->create(_glWidget->context()->contextHandle());
|
||||
_chromiumShareContext->makeCurrent();
|
||||
qt_gl_set_global_share_context(_chromiumShareContext->getContext());
|
||||
|
||||
_glWidget->makeCurrent();
|
||||
gpu::Context::init<gpu::gl::GLBackend>();
|
||||
_gpuContext = std::make_shared<gpu::Context>();
|
||||
// The gpu context can make child contexts for transfers, so
|
||||
// we need to restore primary rendering context
|
||||
_offscreenContext->makeCurrent();
|
||||
_glWidget->makeCurrent();
|
||||
|
||||
initDisplay();
|
||||
qCDebug(interfaceapp, "Initialized Display.");
|
||||
|
@ -1521,7 +1519,8 @@ void Application::initializeGL() {
|
|||
// Needs to happen AFTER the render engine initialization to access its configuration
|
||||
initializeUi();
|
||||
qCDebug(interfaceapp, "Initialized Offscreen UI.");
|
||||
_offscreenContext->makeCurrent();
|
||||
_glWidget->makeCurrent();
|
||||
|
||||
|
||||
// call Menu getInstance static method to set up the menu
|
||||
// Needs to happen AFTER the QML UI initialization
|
||||
|
@ -1537,8 +1536,13 @@ void Application::initializeGL() {
|
|||
|
||||
_idleLoopStdev.reset();
|
||||
|
||||
_offscreenContext = new OffscreenGLCanvas();
|
||||
_offscreenContext->create(_glWidget->context()->contextHandle());
|
||||
_offscreenContext->makeCurrent();
|
||||
|
||||
// update before the first render
|
||||
update(0);
|
||||
|
||||
}
|
||||
|
||||
FrameTimingsScriptingInterface _frameTimingsScriptingInterface;
|
||||
|
@ -1555,7 +1559,7 @@ void Application::initializeUi() {
|
|||
|
||||
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
offscreenUi->create(_offscreenContext->getContext());
|
||||
offscreenUi->create(_glWidget->context()->contextHandle());
|
||||
|
||||
auto rootContext = offscreenUi->getRootContext();
|
||||
|
||||
|
@ -1726,17 +1730,7 @@ void Application::paintGL() {
|
|||
PerformanceWarning warn(showWarnings, "Application::paintGL()");
|
||||
resizeGL();
|
||||
|
||||
// Before anything else, let's sync up the gpuContext with the true glcontext used in case anything happened
|
||||
{
|
||||
PerformanceTimer perfTimer("syncCache");
|
||||
renderArgs._context->syncCache();
|
||||
}
|
||||
|
||||
auto framebufferCache = DependencyManager::get<FramebufferCache>();
|
||||
// Final framebuffer that will be handled to the display-plugin
|
||||
auto finalFramebuffer = framebufferCache->getFramebuffer();
|
||||
|
||||
_gpuContext->beginFrame(finalFramebuffer, getHMDSensorPose());
|
||||
_gpuContext->beginFrame(getHMDSensorPose());
|
||||
// Reset the gpu::Context Stages
|
||||
// Back to the default framebuffer;
|
||||
gpu::doInBatch(_gpuContext, [&](gpu::Batch& batch) {
|
||||
|
@ -1866,7 +1860,10 @@ void Application::paintGL() {
|
|||
getApplicationCompositor().setFrameInfo(_frameCount, _myCamera.getTransform());
|
||||
|
||||
// Primary rendering pass
|
||||
auto framebufferCache = DependencyManager::get<FramebufferCache>();
|
||||
const QSize size = framebufferCache->getFrameBufferSize();
|
||||
// Final framebuffer that will be handled to the display-plugin
|
||||
auto finalFramebuffer = framebufferCache->getFramebuffer();
|
||||
|
||||
{
|
||||
PROFILE_RANGE(__FUNCTION__ "/mainRender");
|
||||
|
@ -1907,13 +1904,6 @@ void Application::paintGL() {
|
|||
// Apply IPD scaling
|
||||
mat4 eyeOffsetTransform = glm::translate(mat4(), eyeOffset * -1.0f * IPDScale);
|
||||
eyeOffsets[eye] = eyeOffsetTransform;
|
||||
|
||||
// Tell the plugin what pose we're using to render. In this case we're just using the
|
||||
// unmodified head pose because the only plugin that cares (the Oculus plugin) uses it
|
||||
// for rotational timewarp. If we move to support positonal timewarp, we need to
|
||||
// ensure this contains the full pose composed with the eye offsets.
|
||||
displayPlugin->setEyeRenderPose(_frameCount, eye, headPose * glm::inverse(eyeOffsetTransform));
|
||||
|
||||
eyeProjections[eye] = displayPlugin->getEyeProjection(eye, baseProjection);
|
||||
});
|
||||
renderArgs._context->setStereoProjections(eyeProjections);
|
||||
|
@ -1921,36 +1911,26 @@ void Application::paintGL() {
|
|||
}
|
||||
renderArgs._blitFramebuffer = finalFramebuffer;
|
||||
displaySide(&renderArgs, _myCamera);
|
||||
|
||||
renderArgs._blitFramebuffer.reset();
|
||||
renderArgs._context->enableStereo(false);
|
||||
}
|
||||
|
||||
_gpuContext->endFrame();
|
||||
|
||||
gpu::TexturePointer overlayTexture = _applicationOverlay.acquireOverlay();
|
||||
if (overlayTexture) {
|
||||
displayPlugin->submitOverlayTexture(overlayTexture);
|
||||
}
|
||||
|
||||
// deliver final composited scene to the display plugin
|
||||
auto frame = _gpuContext->endFrame();
|
||||
frame->frameIndex = _frameCount;
|
||||
frame->framebuffer = finalFramebuffer;
|
||||
frame->framebufferRecycler = [](const gpu::FramebufferPointer& framebuffer){
|
||||
DependencyManager::get<FramebufferCache>()->releaseFramebuffer(framebuffer);
|
||||
};
|
||||
frame->overlay = _applicationOverlay.getOverlayTexture();
|
||||
// deliver final scene rendering commands to the display plugin
|
||||
{
|
||||
PROFILE_RANGE(__FUNCTION__ "/pluginOutput");
|
||||
PerformanceTimer perfTimer("pluginOutput");
|
||||
|
||||
auto finalTexture = finalFramebuffer->getRenderBuffer(0);
|
||||
Q_ASSERT(!_lockedFramebufferMap.contains(finalTexture));
|
||||
_lockedFramebufferMap[finalTexture] = finalFramebuffer;
|
||||
|
||||
Q_ASSERT(isCurrentContext(_offscreenContext->getContext()));
|
||||
{
|
||||
PROFILE_RANGE(__FUNCTION__ "/pluginSubmitScene");
|
||||
PerformanceTimer perfTimer("pluginSubmitScene");
|
||||
displayPlugin->submitSceneTexture(_frameCount, finalTexture);
|
||||
}
|
||||
Q_ASSERT(isCurrentContext(_offscreenContext->getContext()));
|
||||
displayPlugin->submitFrame(frame);
|
||||
}
|
||||
|
||||
// Reset the framebuffer and stereo state
|
||||
renderArgs._blitFramebuffer.reset();
|
||||
renderArgs._context->enableStereo(false);
|
||||
|
||||
{
|
||||
Stats::getInstance()->setRenderDetails(renderArgs._details);
|
||||
}
|
||||
|
@ -5405,6 +5385,7 @@ void Application::updateDisplayMode() {
|
|||
DisplayPluginList advanced;
|
||||
DisplayPluginList developer;
|
||||
foreach(auto displayPlugin, displayPlugins) {
|
||||
displayPlugin->setBackend(_gpuContext->getBackend());
|
||||
auto grouping = displayPlugin->getGrouping();
|
||||
switch (grouping) {
|
||||
case Plugin::ADVANCED:
|
||||
|
@ -5474,9 +5455,6 @@ void Application::updateDisplayMode() {
|
|||
_displayPlugin->deactivate();
|
||||
}
|
||||
|
||||
// FIXME probably excessive and useless context switching
|
||||
_offscreenContext->makeCurrent();
|
||||
|
||||
bool active = newDisplayPlugin->activate();
|
||||
|
||||
if (!active) {
|
||||
|
@ -5621,20 +5599,6 @@ bool Application::makeRenderingContextCurrent() {
|
|||
return _offscreenContext->makeCurrent();
|
||||
}
|
||||
|
||||
void Application::releaseSceneTexture(const gpu::TexturePointer& texture) {
|
||||
Q_ASSERT(QThread::currentThread() == thread());
|
||||
auto& framebufferMap = _lockedFramebufferMap;
|
||||
Q_ASSERT(framebufferMap.contains(texture));
|
||||
auto framebufferPointer = framebufferMap[texture];
|
||||
framebufferMap.remove(texture);
|
||||
auto framebufferCache = DependencyManager::get<FramebufferCache>();
|
||||
framebufferCache->releaseFramebuffer(framebufferPointer);
|
||||
}
|
||||
|
||||
void Application::releaseOverlayTexture(const gpu::TexturePointer& texture) {
|
||||
_applicationOverlay.releaseOverlay(texture);
|
||||
}
|
||||
|
||||
bool Application::isForeground() const {
|
||||
return _isForeground && !_window->isMinimized();
|
||||
}
|
||||
|
|
|
@ -112,8 +112,6 @@ public:
|
|||
virtual MainWindow* getPrimaryWindow() override;
|
||||
virtual QOpenGLContext* getPrimaryContext() override;
|
||||
virtual bool makeRenderingContextCurrent() override;
|
||||
virtual void releaseSceneTexture(const gpu::TexturePointer& texture) override;
|
||||
virtual void releaseOverlayTexture(const gpu::TexturePointer& texture) override;
|
||||
virtual bool isForeground() const override;
|
||||
|
||||
virtual DisplayPluginPointer getActiveDisplayPlugin() const override;
|
||||
|
@ -434,7 +432,6 @@ private:
|
|||
InputPluginList _activeInputPlugins;
|
||||
|
||||
bool _activatingDisplayPlugin { false };
|
||||
QMap<gpu::TexturePointer, gpu::FramebufferPointer> _lockedFramebufferMap;
|
||||
|
||||
QUndoStack _undoStack;
|
||||
UndoStackScriptingInterface _undoStackScriptingInterface;
|
||||
|
|
|
@ -67,7 +67,9 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) {
|
|||
|
||||
// Execute the batch into our framebuffer
|
||||
doInBatch(renderArgs->_context, [&](gpu::Batch& batch) {
|
||||
PROFILE_RANGE_BATCH(batch, "ApplicationOverlayRender");
|
||||
renderArgs->_batch = &batch;
|
||||
batch.enableStereo(false);
|
||||
|
||||
int width = _overlayFramebuffer->getWidth();
|
||||
int height = _overlayFramebuffer->getHeight();
|
||||
|
@ -246,10 +248,6 @@ static const auto COLOR_FORMAT = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)
|
|||
static const auto DEFAULT_SAMPLER = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR);
|
||||
static const auto DEPTH_FORMAT = gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPTH);
|
||||
|
||||
std::mutex _textureGuard;
|
||||
using Lock = std::unique_lock<std::mutex>;
|
||||
std::queue<gpu::TexturePointer> _availableTextures;
|
||||
|
||||
void ApplicationOverlay::buildFramebufferObject() {
|
||||
PROFILE_RANGE(__FUNCTION__);
|
||||
|
||||
|
@ -265,22 +263,6 @@ void ApplicationOverlay::buildFramebufferObject() {
|
|||
_overlayFramebuffer->setDepthStencilBuffer(overlayDepthTexture, DEPTH_FORMAT);
|
||||
}
|
||||
|
||||
if (!_overlayFramebuffer->getRenderBuffer(0)) {
|
||||
gpu::TexturePointer newColorAttachment;
|
||||
{
|
||||
Lock lock(_textureGuard);
|
||||
if (!_availableTextures.empty()) {
|
||||
newColorAttachment = _availableTextures.front();
|
||||
_availableTextures.pop();
|
||||
}
|
||||
}
|
||||
if (newColorAttachment) {
|
||||
newColorAttachment->resize2D(width, height, newColorAttachment->getNumSamples());
|
||||
_overlayFramebuffer->setRenderBuffer(0, newColorAttachment);
|
||||
}
|
||||
}
|
||||
|
||||
// If the overlay framebuffer still has no color attachment, no textures were available for rendering, so build a new one
|
||||
if (!_overlayFramebuffer->getRenderBuffer(0)) {
|
||||
const gpu::Sampler OVERLAY_SAMPLER(gpu::Sampler::FILTER_MIN_MAG_LINEAR, gpu::Sampler::WRAP_CLAMP);
|
||||
auto colorBuffer = gpu::TexturePointer(gpu::Texture::create2D(COLOR_FORMAT, width, height, OVERLAY_SAMPLER));
|
||||
|
@ -288,20 +270,9 @@ void ApplicationOverlay::buildFramebufferObject() {
|
|||
}
|
||||
}
|
||||
|
||||
gpu::TexturePointer ApplicationOverlay::acquireOverlay() {
|
||||
gpu::TexturePointer ApplicationOverlay::getOverlayTexture() {
|
||||
if (!_overlayFramebuffer) {
|
||||
return gpu::TexturePointer();
|
||||
}
|
||||
auto result = _overlayFramebuffer->getRenderBuffer(0);
|
||||
_overlayFramebuffer->setRenderBuffer(0, gpu::TexturePointer());
|
||||
return result;
|
||||
}
|
||||
|
||||
void ApplicationOverlay::releaseOverlay(gpu::TexturePointer texture) {
|
||||
if (texture) {
|
||||
Lock lock(_textureGuard);
|
||||
_availableTextures.push(texture);
|
||||
} else {
|
||||
qWarning() << "Attempted to release null texture";
|
||||
}
|
||||
}
|
||||
return _overlayFramebuffer->getRenderBuffer(0);
|
||||
}
|
|
@ -26,8 +26,7 @@ public:
|
|||
|
||||
void renderOverlay(RenderArgs* renderArgs);
|
||||
|
||||
gpu::TexturePointer acquireOverlay();
|
||||
void releaseOverlay(gpu::TexturePointer pointer);
|
||||
gpu::TexturePointer getOverlayTexture();
|
||||
|
||||
private:
|
||||
void renderStatsAndLogs(RenderArgs* renderArgs);
|
||||
|
|
|
@ -33,9 +33,9 @@ bool Basic2DWindowOpenGLDisplayPlugin::internalActivate() {
|
|||
return Parent::internalActivate();
|
||||
}
|
||||
|
||||
void Basic2DWindowOpenGLDisplayPlugin::submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) {
|
||||
void Basic2DWindowOpenGLDisplayPlugin::submitFrame(const gpu::FramePointer& newFrame) {
|
||||
_wantVsync = true; // always
|
||||
Parent::submitSceneTexture(frameIndex, sceneTexture);
|
||||
Parent::submitFrame(newFrame);
|
||||
}
|
||||
|
||||
void Basic2DWindowOpenGLDisplayPlugin::internalPresent() {
|
||||
|
|
|
@ -24,7 +24,7 @@ public:
|
|||
|
||||
virtual bool internalActivate() override;
|
||||
|
||||
virtual void submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) override;
|
||||
void submitFrame(const gpu::FramePointer& newFrame) override;
|
||||
|
||||
virtual void internalPresent() override;
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include <QtGui/QImage>
|
||||
#include <ui-plugins/PluginContainer.h>
|
||||
#include <FramebufferCache.h>
|
||||
|
||||
const QString NullDisplayPlugin::NAME("NullDisplayPlugin");
|
||||
|
||||
|
@ -22,12 +23,7 @@ bool NullDisplayPlugin::hasFocus() const {
|
|||
return false;
|
||||
}
|
||||
|
||||
void NullDisplayPlugin::submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) {
|
||||
_container->releaseSceneTexture(sceneTexture);
|
||||
}
|
||||
|
||||
void NullDisplayPlugin::submitOverlayTexture(const gpu::TexturePointer& overlayTexture) {
|
||||
_container->releaseOverlayTexture(overlayTexture);
|
||||
void NullDisplayPlugin::submitFrame(const gpu::FramePointer& resultFramebuffer) {
|
||||
}
|
||||
|
||||
QImage NullDisplayPlugin::getScreenshot() const {
|
||||
|
|
|
@ -11,16 +11,14 @@
|
|||
|
||||
class NullDisplayPlugin : public DisplayPlugin {
|
||||
public:
|
||||
~NullDisplayPlugin() final {}
|
||||
const QString& getName() const override { return NAME; }
|
||||
grouping getGrouping() const override { return DEVELOPER; }
|
||||
|
||||
virtual ~NullDisplayPlugin() final {}
|
||||
virtual const QString& getName() const override { return NAME; }
|
||||
virtual grouping getGrouping() const override { return DEVELOPER; }
|
||||
|
||||
virtual glm::uvec2 getRecommendedRenderSize() const override;
|
||||
virtual bool hasFocus() const override;
|
||||
virtual void submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) override;
|
||||
virtual void submitOverlayTexture(const gpu::TexturePointer& overlayTexture) override;
|
||||
virtual QImage getScreenshot() const override;
|
||||
glm::uvec2 getRecommendedRenderSize() const override;
|
||||
bool hasFocus() const override;
|
||||
void submitFrame(const gpu::FramePointer& newFrame) override;
|
||||
QImage getScreenshot() const override;
|
||||
private:
|
||||
static const QString NAME;
|
||||
};
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "OpenGLDisplayPlugin.h"
|
||||
|
||||
#include <condition_variable>
|
||||
#include <queue>
|
||||
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QThread>
|
||||
|
@ -19,26 +20,43 @@
|
|||
#if defined(Q_OS_MAC)
|
||||
#include <OpenGL/CGLCurrent.h>
|
||||
#endif
|
||||
#include <gl/QOpenGLContextWrapper.h>
|
||||
#include <gpu/Texture.h>
|
||||
#include <gl/GLWidget.h>
|
||||
|
||||
#include <NumericalConstants.h>
|
||||
#include <DependencyManager.h>
|
||||
#include <shared/NsightHelpers.h>
|
||||
#include <ui-plugins/PluginContainer.h>
|
||||
#include <GLMHelpers.h>
|
||||
|
||||
#include <gl/QOpenGLContextWrapper.h>
|
||||
#include <gl/GLWidget.h>
|
||||
#include <gl/Config.h>
|
||||
#include <gl/GLEscrow.h>
|
||||
#include <GLMHelpers.h>
|
||||
#include <CursorManager.h>
|
||||
#include "CompositorHelper.h"
|
||||
|
||||
#include <gpu/Texture.h>
|
||||
#include <gpu/StandardShaderLib.h>
|
||||
#include <gpu/gl/GLShared.h>
|
||||
#include <GeometryCache.h>
|
||||
|
||||
#include <FramebufferCache.h>
|
||||
#include <shared/NsightHelpers.h>
|
||||
#include <ui-plugins/PluginContainer.h>
|
||||
#include <ui/Menu.h>
|
||||
#include <CursorManager.h>
|
||||
|
||||
#include "CompositorHelper.h"
|
||||
|
||||
#if THREADED_PRESENT
|
||||
const char* SRGB_TO_LINEAR_FRAG = R"SCRIBE(
|
||||
|
||||
// FIXME, for display plugins that don't block on something like vsync, just
|
||||
// cap the present rate at 200
|
||||
// const static unsigned int MAX_PRESENT_RATE = 200;
|
||||
uniform sampler2D colorMap;
|
||||
|
||||
in vec2 varTexCoord0;
|
||||
|
||||
out vec4 outFragColor;
|
||||
|
||||
void main(void) {
|
||||
outFragColor = vec4(pow(texture(colorMap, varTexCoord0).rgb, vec3(2.2)), 1.0);
|
||||
}
|
||||
)SCRIBE";
|
||||
|
||||
QOpenGLContext* mainContext;
|
||||
|
||||
class PresentThread : public QThread, public Dependency {
|
||||
using Mutex = std::mutex;
|
||||
|
@ -87,8 +105,8 @@ public:
|
|||
|
||||
virtual void run() override {
|
||||
OpenGLDisplayPlugin* currentPlugin{ nullptr };
|
||||
thread()->setPriority(QThread::HighestPriority);
|
||||
Q_ASSERT(_context);
|
||||
mainContext = _context->contextHandle();
|
||||
while (!_shutdown) {
|
||||
if (_pendingMainThreadOperation) {
|
||||
{
|
||||
|
@ -118,19 +136,13 @@ public:
|
|||
if (newPlugin != currentPlugin) {
|
||||
// Deactivate the old plugin
|
||||
if (currentPlugin != nullptr) {
|
||||
try {
|
||||
currentPlugin->uncustomizeContext();
|
||||
} catch (const oglplus::Error& error) {
|
||||
qWarning() << "OpenGL error in uncustomizeContext: " << error.what();
|
||||
}
|
||||
currentPlugin->uncustomizeContext();
|
||||
CHECK_GL_ERROR();
|
||||
}
|
||||
|
||||
if (newPlugin) {
|
||||
try {
|
||||
newPlugin->customizeContext();
|
||||
} catch (const oglplus::Error& error) {
|
||||
qWarning() << "OpenGL error in customizeContext: " << error.what();
|
||||
}
|
||||
newPlugin->customizeContext();
|
||||
CHECK_GL_ERROR();
|
||||
}
|
||||
currentPlugin = newPlugin;
|
||||
_newPluginQueue.pop();
|
||||
|
@ -150,11 +162,8 @@ public:
|
|||
// take the latest texture and present it
|
||||
_context->makeCurrent();
|
||||
if (isCurrentContext(_context->contextHandle())) {
|
||||
try {
|
||||
currentPlugin->present();
|
||||
} catch (const oglplus::Error& error) {
|
||||
qWarning() << "OpenGL error in presentation: " << error.what();
|
||||
}
|
||||
currentPlugin->present();
|
||||
CHECK_GL_ERROR();
|
||||
_context->doneCurrent();
|
||||
} else {
|
||||
qWarning() << "Makecurrent failed";
|
||||
|
@ -204,27 +213,13 @@ private:
|
|||
QGLContext* _context { nullptr };
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
bool OpenGLDisplayPlugin::isRenderThread() const {
|
||||
return QThread::currentThread() == DependencyManager::get<PresentThread>()->thread();
|
||||
}
|
||||
|
||||
OpenGLDisplayPlugin::OpenGLDisplayPlugin() {
|
||||
_sceneTextureEscrow.setRecycler([this](const gpu::TexturePointer& texture){
|
||||
cleanupForSceneTexture(texture);
|
||||
_container->releaseSceneTexture(texture);
|
||||
});
|
||||
_overlayTextureEscrow.setRecycler([this](const gpu::TexturePointer& texture) {
|
||||
_container->releaseOverlayTexture(texture);
|
||||
});
|
||||
}
|
||||
|
||||
void OpenGLDisplayPlugin::cleanupForSceneTexture(const gpu::TexturePointer& sceneTexture) {
|
||||
withRenderThreadLock([&] {
|
||||
Q_ASSERT(_sceneTextureToFrameIndexMap.contains(sceneTexture));
|
||||
_sceneTextureToFrameIndexMap.remove(sceneTexture);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
bool OpenGLDisplayPlugin::activate() {
|
||||
if (!_cursorsData.size()) {
|
||||
auto& cursorManager = Cursor::Manager::instance();
|
||||
|
@ -244,7 +239,6 @@ bool OpenGLDisplayPlugin::activate() {
|
|||
}
|
||||
_vsyncSupported = _container->getPrimaryWidget()->isVsyncSupported();
|
||||
|
||||
#if THREADED_PRESENT
|
||||
// Start the present thread if necessary
|
||||
QSharedPointer<PresentThread> presentThread;
|
||||
if (DependencyManager::isSet<PresentThread>()) {
|
||||
|
@ -259,7 +253,6 @@ bool OpenGLDisplayPlugin::activate() {
|
|||
presentThread->start();
|
||||
}
|
||||
_presentThread = presentThread.data();
|
||||
#endif
|
||||
|
||||
// Child classes may override this in order to do things like initialize
|
||||
// libraries, etc
|
||||
|
@ -267,17 +260,10 @@ bool OpenGLDisplayPlugin::activate() {
|
|||
return false;
|
||||
}
|
||||
|
||||
#if THREADED_PRESENT
|
||||
|
||||
// This should not return until the new context has been customized
|
||||
// and the old context (if any) has been uncustomized
|
||||
presentThread->setNewDisplayPlugin(this);
|
||||
#else
|
||||
static auto widget = _container->getPrimaryWidget();
|
||||
widget->makeCurrent();
|
||||
customizeContext();
|
||||
_container->makeRenderingContextCurrent();
|
||||
#endif
|
||||
|
||||
auto compositorHelper = DependencyManager::get<CompositorHelper>();
|
||||
connect(compositorHelper.data(), &CompositorHelper::alphaChanged, [this] {
|
||||
|
@ -300,16 +286,9 @@ void OpenGLDisplayPlugin::deactivate() {
|
|||
auto compositorHelper = DependencyManager::get<CompositorHelper>();
|
||||
disconnect(compositorHelper.data());
|
||||
|
||||
#if THREADED_PRESENT
|
||||
auto presentThread = DependencyManager::get<PresentThread>();
|
||||
// Does not return until the GL transition has completeed
|
||||
presentThread->setNewDisplayPlugin(nullptr);
|
||||
#else
|
||||
static auto widget = _container->getPrimaryWidget();
|
||||
widget->makeCurrent();
|
||||
uncustomizeContext();
|
||||
_container->makeRenderingContextCurrent();
|
||||
#endif
|
||||
internalDeactivate();
|
||||
|
||||
_container->showDisplayPluginsTools(false);
|
||||
|
@ -325,56 +304,74 @@ void OpenGLDisplayPlugin::deactivate() {
|
|||
|
||||
|
||||
void OpenGLDisplayPlugin::customizeContext() {
|
||||
#if THREADED_PRESENT
|
||||
auto presentThread = DependencyManager::get<PresentThread>();
|
||||
Q_ASSERT(thread() == presentThread->thread());
|
||||
#endif
|
||||
enableVsync();
|
||||
|
||||
for (auto& cursorValue : _cursorsData) {
|
||||
auto& cursorData = cursorValue.second;
|
||||
if (!cursorData.texture) {
|
||||
const auto& image = cursorData.image;
|
||||
glGenTextures(1, &cursorData.texture);
|
||||
glBindTexture(GL_TEXTURE_2D, cursorData.texture);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, image.constBits());
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
auto image = cursorData.image;
|
||||
if (image.format() != QImage::Format_ARGB32) {
|
||||
image = image.convertToFormat(QImage::Format_ARGB32);
|
||||
}
|
||||
if ((image.width() > 0) && (image.height() > 0)) {
|
||||
|
||||
cursorData.texture.reset(
|
||||
gpu::Texture::create2D(
|
||||
gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA),
|
||||
image.width(), image.height(),
|
||||
gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)));
|
||||
auto usage = gpu::Texture::Usage::Builder().withColor().withAlpha();
|
||||
cursorData.texture->setUsage(usage.build());
|
||||
cursorData.texture->assignStoredMip(0, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), image.byteCount(), image.constBits());
|
||||
cursorData.texture->autoGenerateMips(-1);
|
||||
}
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
using namespace oglplus;
|
||||
Context::BlendFunc(BlendFunction::SrcAlpha, BlendFunction::OneMinusSrcAlpha);
|
||||
Context::Disable(Capability::Blend);
|
||||
Context::Disable(Capability::DepthTest);
|
||||
Context::Disable(Capability::CullFace);
|
||||
|
||||
_program = loadDefaultShader();
|
||||
|
||||
auto uniforms = _program->ActiveUniforms();
|
||||
while (!uniforms.Empty()) {
|
||||
auto uniform = uniforms.Front();
|
||||
if (uniform.Name() == "mvp") {
|
||||
_mvpUniform = uniform.Index();
|
||||
if (!_presentPipeline) {
|
||||
{
|
||||
auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS();
|
||||
auto ps = gpu::Shader::createPixel(std::string(SRGB_TO_LINEAR_FRAG));
|
||||
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
|
||||
gpu::Shader::makeProgram(*program);
|
||||
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||
state->setDepthTest(gpu::State::DepthTest(false));
|
||||
_presentPipeline = gpu::Pipeline::create(program, state);
|
||||
}
|
||||
if (uniform.Name() == "alpha") {
|
||||
_alphaUniform = uniform.Index();
|
||||
|
||||
{
|
||||
auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS();
|
||||
auto ps = gpu::StandardShaderLib::getDrawTexturePS();
|
||||
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
|
||||
gpu::Shader::makeProgram(*program);
|
||||
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||
state->setDepthTest(gpu::State::DepthTest(false));
|
||||
state->setBlendFunction(true,
|
||||
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
|
||||
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
||||
_overlayPipeline = gpu::Pipeline::create(program, state);
|
||||
}
|
||||
|
||||
{
|
||||
auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS();
|
||||
auto ps = gpu::StandardShaderLib::getDrawTexturePS();
|
||||
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
|
||||
gpu::Shader::makeProgram(*program);
|
||||
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||
state->setDepthTest(gpu::State::DepthTest(false));
|
||||
state->setBlendFunction(true,
|
||||
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
|
||||
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
||||
_cursorPipeline = gpu::Pipeline::create(program, state);
|
||||
}
|
||||
uniforms.Next();
|
||||
}
|
||||
|
||||
_plane = loadPlane(_program);
|
||||
|
||||
_compositeFramebuffer = std::make_shared<BasicFramebufferWrapper>();
|
||||
_compositeFramebuffer->Init(getRecommendedRenderSize());
|
||||
}
|
||||
|
||||
void OpenGLDisplayPlugin::uncustomizeContext() {
|
||||
_compositeFramebuffer.reset();
|
||||
_program.reset();
|
||||
_plane.reset();
|
||||
_presentPipeline.reset();
|
||||
}
|
||||
|
||||
|
||||
|
@ -420,172 +417,142 @@ bool OpenGLDisplayPlugin::eventFilter(QObject* receiver, QEvent* event) {
|
|||
return false;
|
||||
}
|
||||
|
||||
void OpenGLDisplayPlugin::submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) {
|
||||
|
||||
void OpenGLDisplayPlugin::submitFrame(const gpu::FramePointer& newFrame) {
|
||||
if (_lockCurrentTexture) {
|
||||
_container->releaseSceneTexture(sceneTexture);
|
||||
return;
|
||||
}
|
||||
|
||||
withRenderThreadLock([&] {
|
||||
_sceneTextureToFrameIndexMap[sceneTexture] = frameIndex;
|
||||
withNonPresentThreadLock([&] {
|
||||
_newFrameQueue.push(newFrame);
|
||||
});
|
||||
|
||||
// Submit it to the presentation thread via escrow
|
||||
_sceneTextureEscrow.submit(sceneTexture);
|
||||
|
||||
#if THREADED_PRESENT
|
||||
#else
|
||||
static auto widget = _container->getPrimaryWidget();
|
||||
widget->makeCurrent();
|
||||
present();
|
||||
_container->makeRenderingContextCurrent();
|
||||
#endif
|
||||
}
|
||||
|
||||
void OpenGLDisplayPlugin::submitOverlayTexture(const gpu::TexturePointer& overlayTexture) {
|
||||
// Submit it to the presentation thread via escrow
|
||||
_overlayTextureEscrow.submit(overlayTexture);
|
||||
}
|
||||
|
||||
void OpenGLDisplayPlugin::updateTextures() {
|
||||
// FIXME intrduce a GPU wait instead of a CPU/GPU sync point?
|
||||
#if THREADED_PRESENT
|
||||
if (_sceneTextureEscrow.fetchSignaledAndRelease(_currentSceneTexture)) {
|
||||
#else
|
||||
if (_sceneTextureEscrow.fetchAndReleaseWithGpuWait(_currentSceneTexture)) {
|
||||
#endif
|
||||
updateFrameData();
|
||||
_newFrameRate.increment();
|
||||
}
|
||||
|
||||
_overlayTextureEscrow.fetchSignaledAndRelease(_currentOverlayTexture);
|
||||
}
|
||||
|
||||
void OpenGLDisplayPlugin::updateFrameData() {
|
||||
withPresentThreadLock([&] {
|
||||
auto previousFrameIndex = _currentPresentFrameIndex;
|
||||
_currentPresentFrameIndex = _sceneTextureToFrameIndexMap[_currentSceneTexture];
|
||||
auto skippedCount = (_currentPresentFrameIndex - previousFrameIndex) - 1;
|
||||
gpu::FramePointer oldFrame = _currentFrame;
|
||||
uint32_t skippedCount = 0;
|
||||
while (!_newFrameQueue.empty()) {
|
||||
_currentFrame = _newFrameQueue.front();
|
||||
_currentFrame->preRender();
|
||||
_newFrameQueue.pop();
|
||||
|
||||
_newFrameQueue = std::queue<gpu::FramePointer>();
|
||||
if (_currentFrame && oldFrame) {
|
||||
skippedCount = (_currentFrame->frameIndex - oldFrame->frameIndex) - 1;
|
||||
}
|
||||
}
|
||||
_droppedFrameRate.increment(skippedCount);
|
||||
});
|
||||
}
|
||||
|
||||
void OpenGLDisplayPlugin::compositeOverlay() {
|
||||
using namespace oglplus;
|
||||
|
||||
auto compositorHelper = DependencyManager::get<CompositorHelper>();
|
||||
|
||||
useProgram(_program);
|
||||
// set the alpha
|
||||
Uniform<float>(*_program, _alphaUniform).Set(_compositeOverlayAlpha);
|
||||
// check the alpha
|
||||
// Overlay draw
|
||||
gpu::Batch batch;
|
||||
batch.enableStereo(false);
|
||||
batch.setFramebuffer(_currentFrame->framebuffer);
|
||||
batch.setPipeline(_overlayPipeline);
|
||||
batch.setResourceTexture(0, _currentFrame->overlay);
|
||||
if (isStereo()) {
|
||||
Uniform<glm::mat4>(*_program, _mvpUniform).Set(mat4());
|
||||
for_each_eye([&](Eye eye) {
|
||||
eyeViewport(eye);
|
||||
drawUnitQuad();
|
||||
batch.setViewportTransform(eyeViewport(eye));
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
});
|
||||
} else {
|
||||
// Overlay draw
|
||||
Uniform<glm::mat4>(*_program, _mvpUniform).Set(mat4());
|
||||
drawUnitQuad();
|
||||
batch.setViewportTransform(ivec4(uvec2(0), _currentFrame->framebuffer->getSize()));
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
}
|
||||
// restore the alpha
|
||||
Uniform<float>(*_program, _alphaUniform).Set(1.0);
|
||||
_backend->render(batch);
|
||||
}
|
||||
|
||||
void OpenGLDisplayPlugin::compositePointer() {
|
||||
using namespace oglplus;
|
||||
auto compositorHelper = DependencyManager::get<CompositorHelper>();
|
||||
|
||||
useProgram(_program);
|
||||
// set the alpha
|
||||
Uniform<float>(*_program, _alphaUniform).Set(_compositeOverlayAlpha);
|
||||
Uniform<glm::mat4>(*_program, _mvpUniform).Set(compositorHelper->getReticleTransform(glm::mat4()));
|
||||
auto& cursorManager = Cursor::Manager::instance();
|
||||
const auto& cursorData = _cursorsData[cursorManager.getCursor()->getIcon()];
|
||||
auto cursorTransform = DependencyManager::get<CompositorHelper>()->getReticleTransform(glm::mat4());
|
||||
gpu::Batch batch;
|
||||
batch.enableStereo(false);
|
||||
batch.setProjectionTransform(mat4());
|
||||
batch.setFramebuffer(_currentFrame->framebuffer);
|
||||
batch.setPipeline(_cursorPipeline);
|
||||
batch.setResourceTexture(0, cursorData.texture);
|
||||
batch.setViewTransform(Transform());
|
||||
batch.setModelTransform(cursorTransform);
|
||||
if (isStereo()) {
|
||||
for_each_eye([&](Eye eye) {
|
||||
eyeViewport(eye);
|
||||
drawUnitQuad();
|
||||
batch.setViewportTransform(eyeViewport(eye));
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
});
|
||||
} else {
|
||||
drawUnitQuad();
|
||||
batch.setViewportTransform(ivec4(uvec2(0), _currentFrame->framebuffer->getSize()));
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
}
|
||||
Uniform<glm::mat4>(*_program, _mvpUniform).Set(mat4());
|
||||
// restore the alpha
|
||||
Uniform<float>(*_program, _alphaUniform).Set(1.0);
|
||||
_backend->render(batch);
|
||||
}
|
||||
|
||||
void OpenGLDisplayPlugin::compositeScene() {
|
||||
using namespace oglplus;
|
||||
useProgram(_program);
|
||||
Uniform<glm::mat4>(*_program, _mvpUniform).Set(mat4());
|
||||
drawUnitQuad();
|
||||
}
|
||||
|
||||
void OpenGLDisplayPlugin::compositeLayers() {
|
||||
using namespace oglplus;
|
||||
auto targetRenderSize = getRecommendedRenderSize();
|
||||
if (!_compositeFramebuffer || _compositeFramebuffer->size != targetRenderSize) {
|
||||
_compositeFramebuffer = std::make_shared<BasicFramebufferWrapper>();
|
||||
_compositeFramebuffer->Init(targetRenderSize);
|
||||
}
|
||||
_compositeFramebuffer->Bound(Framebuffer::Target::Draw, [&] {
|
||||
Context::Viewport(targetRenderSize.x, targetRenderSize.y);
|
||||
auto sceneTextureId = getSceneTextureId();
|
||||
auto overlayTextureId = getOverlayTextureId();
|
||||
glBindTexture(GL_TEXTURE_2D, sceneTextureId);
|
||||
{
|
||||
PROFILE_RANGE_EX("compositeScene", 0xff0077ff, (uint64_t)presentCount())
|
||||
compositeScene();
|
||||
if (overlayTextureId) {
|
||||
glBindTexture(GL_TEXTURE_2D, overlayTextureId);
|
||||
Context::Enable(Capability::Blend);
|
||||
Context::BlendFunc(BlendFunction::SrcAlpha, BlendFunction::OneMinusSrcAlpha);
|
||||
compositeOverlay();
|
||||
|
||||
auto compositorHelper = DependencyManager::get<CompositorHelper>();
|
||||
if (compositorHelper->getReticleVisible()) {
|
||||
auto& cursorManager = Cursor::Manager::instance();
|
||||
const auto& cursorData = _cursorsData[cursorManager.getCursor()->getIcon()];
|
||||
glBindTexture(GL_TEXTURE_2D, cursorData.texture);
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(GL_TEXTURE_2D, overlayTextureId);
|
||||
compositePointer();
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
Context::Disable(Capability::Blend);
|
||||
}
|
||||
}
|
||||
{
|
||||
PROFILE_RANGE_EX("compositeOverlay", 0xff0077ff, (uint64_t)presentCount())
|
||||
compositeOverlay();
|
||||
}
|
||||
auto compositorHelper = DependencyManager::get<CompositorHelper>();
|
||||
if (compositorHelper->getReticleVisible()) {
|
||||
PROFILE_RANGE_EX("compositePointer", 0xff0077ff, (uint64_t)presentCount())
|
||||
compositePointer();
|
||||
}
|
||||
{
|
||||
PROFILE_RANGE_EX("compositeExtra", 0xff0077ff, (uint64_t)presentCount())
|
||||
compositeExtra();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLDisplayPlugin::internalPresent() {
|
||||
using namespace oglplus;
|
||||
const uvec2& srcSize = _compositeFramebuffer->size;
|
||||
uvec2 dstSize = getSurfacePixels();
|
||||
_compositeFramebuffer->Bound(FramebufferTarget::Read, [&] {
|
||||
Context::BlitFramebuffer(
|
||||
0, 0, srcSize.x, srcSize.y,
|
||||
0, 0, dstSize.x, dstSize.y,
|
||||
BufferSelectBit::ColorBuffer, BlitFilter::Nearest);
|
||||
});
|
||||
gpu::Batch presentBatch;
|
||||
presentBatch.enableStereo(false);
|
||||
presentBatch.setViewTransform(Transform());
|
||||
presentBatch.setFramebuffer(gpu::FramebufferPointer());
|
||||
presentBatch.setViewportTransform(ivec4(uvec2(0), getSurfacePixels()));
|
||||
presentBatch.setResourceTexture(0, _currentFrame->framebuffer->getRenderBuffer(0));
|
||||
presentBatch.setPipeline(_presentPipeline);
|
||||
presentBatch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
_backend->render(presentBatch);
|
||||
swapBuffers();
|
||||
}
|
||||
|
||||
void OpenGLDisplayPlugin::present() {
|
||||
PROFILE_RANGE_EX(__FUNCTION__, 0xffffff00, (uint64_t)presentCount())
|
||||
incrementPresentCount();
|
||||
|
||||
PROFILE_RANGE_EX(__FUNCTION__, 0xff00ff00, (uint64_t)presentCount())
|
||||
updateFrameData();
|
||||
if (_currentFrame) {
|
||||
_backend->syncCache();
|
||||
_backend->setStereoState(_currentFrame->stereoState);
|
||||
{
|
||||
PROFILE_RANGE_EX("execute", 0xff00ff00, (uint64_t)presentCount())
|
||||
// Execute the frame rendering commands
|
||||
for (auto& batch : _currentFrame->batches) {
|
||||
_backend->render(batch);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
updateTextures();
|
||||
if (_currentSceneTexture) {
|
||||
// Write all layers to a local framebuffer
|
||||
compositeLayers();
|
||||
{
|
||||
PROFILE_RANGE_EX("composite", 0xff00ffff, (uint64_t)presentCount())
|
||||
compositeLayers();
|
||||
}
|
||||
|
||||
// Take the composite framebuffer and send it to the output device
|
||||
internalPresent();
|
||||
{
|
||||
PROFILE_RANGE_EX("internalPresent", 0xff00ffff, (uint64_t)presentCount())
|
||||
internalPresent();
|
||||
}
|
||||
_presentRate.increment();
|
||||
_activeProgram.reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -595,7 +562,7 @@ float OpenGLDisplayPlugin::newFramePresentRate() const {
|
|||
|
||||
float OpenGLDisplayPlugin::droppedFrameRate() const {
|
||||
float result;
|
||||
withRenderThreadLock([&] {
|
||||
withNonPresentThreadLock([&] {
|
||||
result = _droppedFrameRate.rate();
|
||||
});
|
||||
return result;
|
||||
|
@ -605,11 +572,6 @@ float OpenGLDisplayPlugin::presentRate() const {
|
|||
return _presentRate.rate();
|
||||
}
|
||||
|
||||
void OpenGLDisplayPlugin::drawUnitQuad() {
|
||||
useProgram(_program);
|
||||
_plane->Use();
|
||||
_plane->Draw();
|
||||
}
|
||||
|
||||
void OpenGLDisplayPlugin::enableVsync(bool enable) {
|
||||
if (!_vsyncSupported) {
|
||||
|
@ -626,6 +588,7 @@ void OpenGLDisplayPlugin::enableVsync(bool enable) {
|
|||
#endif
|
||||
}
|
||||
|
||||
|
||||
bool OpenGLDisplayPlugin::isVsyncEnabled() {
|
||||
if (!_vsyncSupported) {
|
||||
return true;
|
||||
|
@ -648,19 +611,13 @@ void OpenGLDisplayPlugin::swapBuffers() {
|
|||
}
|
||||
|
||||
void OpenGLDisplayPlugin::withMainThreadContext(std::function<void()> f) const {
|
||||
#if THREADED_PRESENT
|
||||
static auto presentThread = DependencyManager::get<PresentThread>();
|
||||
presentThread->withMainThreadContext(f);
|
||||
_container->makeRenderingContextCurrent();
|
||||
#else
|
||||
static auto widget = _container->getPrimaryWidget();
|
||||
widget->makeCurrent();
|
||||
f();
|
||||
_container->makeRenderingContextCurrent();
|
||||
#endif
|
||||
}
|
||||
|
||||
QImage OpenGLDisplayPlugin::getScreenshot() const {
|
||||
#if 0
|
||||
using namespace oglplus;
|
||||
QImage screenshot(_compositeFramebuffer->size.x, _compositeFramebuffer->size.y, QImage::Format_RGBA8888);
|
||||
withMainThreadContext([&] {
|
||||
|
@ -668,32 +625,9 @@ QImage OpenGLDisplayPlugin::getScreenshot() const {
|
|||
Context::ReadPixels(0, 0, _compositeFramebuffer->size.x, _compositeFramebuffer->size.y, enums::PixelDataFormat::RGBA, enums::PixelDataType::UnsignedByte, screenshot.bits());
|
||||
});
|
||||
return screenshot.mirrored(false, true);
|
||||
}
|
||||
|
||||
uint32_t OpenGLDisplayPlugin::getSceneTextureId() const {
|
||||
if (!_currentSceneTexture) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return _currentSceneTexture->getHardwareId();
|
||||
}
|
||||
|
||||
uint32_t OpenGLDisplayPlugin::getOverlayTextureId() const {
|
||||
if (!_currentOverlayTexture) {
|
||||
return 0;
|
||||
}
|
||||
return _currentOverlayTexture->getHardwareId();
|
||||
}
|
||||
|
||||
void OpenGLDisplayPlugin::eyeViewport(Eye eye) const {
|
||||
using namespace oglplus;
|
||||
uvec2 vpSize = _compositeFramebuffer->size;
|
||||
vpSize.x /= 2;
|
||||
uvec2 vpPos;
|
||||
if (eye == Eye::Right) {
|
||||
vpPos.x = vpSize.x;
|
||||
}
|
||||
Context::Viewport(vpPos.x, vpPos.y, vpSize.x, vpSize.y);
|
||||
#else
|
||||
return QImage();
|
||||
#endif
|
||||
}
|
||||
|
||||
glm::uvec2 OpenGLDisplayPlugin::getSurfacePixels() const {
|
||||
|
@ -719,14 +653,7 @@ bool OpenGLDisplayPlugin::hasFocus() const {
|
|||
return window ? window->hasFocus() : false;
|
||||
}
|
||||
|
||||
void OpenGLDisplayPlugin::useProgram(const ProgramPtr& program) {
|
||||
if (_activeProgram != program) {
|
||||
program->Bind();
|
||||
_activeProgram = program;
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLDisplayPlugin::assertIsRenderThread() const {
|
||||
void OpenGLDisplayPlugin::assertNotPresentThread() const {
|
||||
Q_ASSERT(QThread::currentThread() != _presentThread);
|
||||
}
|
||||
|
||||
|
@ -735,8 +662,18 @@ void OpenGLDisplayPlugin::assertIsPresentThread() const {
|
|||
}
|
||||
|
||||
bool OpenGLDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
|
||||
withRenderThreadLock([&] {
|
||||
withNonPresentThreadLock([&] {
|
||||
_compositeOverlayAlpha = _overlayAlpha;
|
||||
});
|
||||
return Parent::beginFrameRender(frameIndex);
|
||||
}
|
||||
|
||||
ivec4 OpenGLDisplayPlugin::eyeViewport(Eye eye) const {
|
||||
uvec2 vpSize = _currentFrame->framebuffer->getSize();
|
||||
vpSize.x /= 2;
|
||||
uvec2 vpPos;
|
||||
if (eye == Eye::Right) {
|
||||
vpPos.x = vpSize.x;
|
||||
}
|
||||
return ivec4(vpPos, vpSize);
|
||||
}
|
||||
|
|
|
@ -11,18 +11,16 @@
|
|||
|
||||
#include <condition_variable>
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtGui/QImage>
|
||||
|
||||
#include <GLMHelpers.h>
|
||||
#include <SimpleMovingAverage.h>
|
||||
#include <gl/OglplusHelpers.h>
|
||||
#include <gl/GLEscrow.h>
|
||||
#include <shared/RateCounter.h>
|
||||
|
||||
#define THREADED_PRESENT 1
|
||||
|
||||
class OpenGLDisplayPlugin : public DisplayPlugin {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(float overlayAlpha MEMBER _overlayAlpha)
|
||||
|
@ -39,13 +37,12 @@ public:
|
|||
// between the main thread and the presentation thread
|
||||
bool activate() override final;
|
||||
void deactivate() override final;
|
||||
bool isRenderThread() const override final;
|
||||
|
||||
bool eventFilter(QObject* receiver, QEvent* event) override;
|
||||
bool isDisplayVisible() const override { return true; }
|
||||
|
||||
|
||||
void submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) override;
|
||||
void submitOverlayTexture(const gpu::TexturePointer& overlayTexture) override;
|
||||
void submitFrame(const gpu::FramePointer& newFrame) override;
|
||||
|
||||
glm::uvec2 getRecommendedRenderSize() const override {
|
||||
return getSurfacePixels();
|
||||
|
@ -65,11 +62,7 @@ public:
|
|||
|
||||
bool beginFrameRender(uint32_t frameIndex) override;
|
||||
protected:
|
||||
#if THREADED_PRESENT
|
||||
friend class PresentThread;
|
||||
#endif
|
||||
uint32_t getSceneTextureId() const;
|
||||
uint32_t getOverlayTextureId() const;
|
||||
|
||||
glm::uvec2 getSurfaceSize() const;
|
||||
glm::uvec2 getSurfacePixels() const;
|
||||
|
@ -93,39 +86,29 @@ protected:
|
|||
// Returns true on successful activation
|
||||
virtual bool internalActivate() { return true; }
|
||||
virtual void internalDeactivate() {}
|
||||
virtual void cleanupForSceneTexture(const gpu::TexturePointer& sceneTexture);
|
||||
|
||||
// Plugin specific functionality to send the composed scene to the output window or device
|
||||
virtual void internalPresent();
|
||||
|
||||
void withMainThreadContext(std::function<void()> f) const;
|
||||
|
||||
void useProgram(const ProgramPtr& program);
|
||||
void present();
|
||||
void updateTextures();
|
||||
void drawUnitQuad();
|
||||
void swapBuffers();
|
||||
void eyeViewport(Eye eye) const;
|
||||
|
||||
virtual void updateFrameData();
|
||||
|
||||
QThread* _presentThread{ nullptr };
|
||||
ProgramPtr _program;
|
||||
int32_t _mvpUniform { -1 };
|
||||
int32_t _alphaUniform { -1 };
|
||||
ShapeWrapperPtr _plane;
|
||||
void withMainThreadContext(std::function<void()> f) const;
|
||||
|
||||
void present();
|
||||
void swapBuffers();
|
||||
ivec4 eyeViewport(Eye eye) const;
|
||||
|
||||
QThread* _presentThread{ nullptr };
|
||||
std::queue<gpu::FramePointer> _newFrameQueue;
|
||||
RateCounter<> _droppedFrameRate;
|
||||
RateCounter<> _newFrameRate;
|
||||
RateCounter<> _presentRate;
|
||||
QMap<gpu::TexturePointer, uint32_t> _sceneTextureToFrameIndexMap;
|
||||
uint32_t _currentPresentFrameIndex { 0 };
|
||||
float _compositeOverlayAlpha{ 1.0f };
|
||||
gpu::FramePointer _currentFrame;
|
||||
gpu::PipelinePointer _overlayPipeline;
|
||||
gpu::PipelinePointer _presentPipeline;
|
||||
gpu::PipelinePointer _cursorPipeline;
|
||||
float _compositeOverlayAlpha { 1.0f };
|
||||
|
||||
gpu::TexturePointer _currentSceneTexture;
|
||||
gpu::TexturePointer _currentOverlayTexture;
|
||||
|
||||
TextureEscrow _sceneTextureEscrow;
|
||||
TextureEscrow _overlayTextureEscrow;
|
||||
|
||||
bool _vsyncSupported { false };
|
||||
|
||||
|
@ -133,14 +116,13 @@ protected:
|
|||
QImage image;
|
||||
vec2 hotSpot;
|
||||
uvec2 size;
|
||||
uint32_t texture { 0 };
|
||||
gpu::TexturePointer texture;
|
||||
};
|
||||
|
||||
std::map<uint16_t, CursorData> _cursorsData;
|
||||
BasicFramebufferWrapperPtr _compositeFramebuffer;
|
||||
bool _lockCurrentTexture { false };
|
||||
|
||||
void assertIsRenderThread() const;
|
||||
void assertNotPresentThread() const;
|
||||
void assertIsPresentThread() const;
|
||||
|
||||
template<typename F>
|
||||
|
@ -151,8 +133,8 @@ protected:
|
|||
}
|
||||
|
||||
template<typename F>
|
||||
void withRenderThreadLock(F f) const {
|
||||
assertIsRenderThread();
|
||||
void withNonPresentThreadLock(F f) const {
|
||||
assertNotPresentThread();
|
||||
Lock lock(_presentMutex);
|
||||
f();
|
||||
}
|
||||
|
@ -161,7 +143,6 @@ private:
|
|||
// Any resource shared by the main thread and the presentation thread must
|
||||
// be serialized through this mutex
|
||||
mutable Mutex _presentMutex;
|
||||
ProgramPtr _activeProgram;
|
||||
float _overlayAlpha{ 1.0f };
|
||||
};
|
||||
|
||||
|
|
|
@ -22,9 +22,10 @@
|
|||
#include <CursorManager.h>
|
||||
#include <gl/GLWidget.h>
|
||||
#include <shared/NsightHelpers.h>
|
||||
#include <GeometryCache.h>
|
||||
|
||||
#include <gpu/DrawUnitQuadTexcoord_vert.h>
|
||||
#include <gpu/DrawTexture_frag.h>
|
||||
#include <gpu/Context.h>
|
||||
#include <gpu/gl/GLBackend.h>
|
||||
|
||||
#include <PathUtils.h>
|
||||
|
||||
|
@ -39,6 +40,15 @@ static const bool DEFAULT_MONO_VIEW = true;
|
|||
static const int NUMBER_OF_HANDS = 2;
|
||||
static const glm::mat4 IDENTITY_MATRIX;
|
||||
|
||||
//#define LIVE_SHADER_RELOAD 1
|
||||
|
||||
static QString readFile(const QString& filename) {
|
||||
QFile file(filename);
|
||||
file.open(QFile::Text | QFile::ReadOnly);
|
||||
QString result;
|
||||
result.append(QTextStream(&file).readAll());
|
||||
return result;
|
||||
}
|
||||
|
||||
glm::uvec2 HmdDisplayPlugin::getRecommendedUiSize() const {
|
||||
return CompositorHelper::VIRTUAL_SCREEN_SIZE;
|
||||
|
@ -68,6 +78,7 @@ bool HmdDisplayPlugin::internalActivate() {
|
|||
_eyeInverseProjections[eye] = glm::inverse(_eyeProjections[eye]);
|
||||
});
|
||||
|
||||
#if 0
|
||||
if (_previewTextureID == 0) {
|
||||
QImage previewTexture(PathUtils::resourcesPath() + "images/preview.png");
|
||||
if (!previewTexture.isNull()) {
|
||||
|
@ -83,18 +94,138 @@ bool HmdDisplayPlugin::internalActivate() {
|
|||
_firstPreview = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return Parent::internalActivate();
|
||||
}
|
||||
|
||||
void HmdDisplayPlugin::internalDeactivate() {
|
||||
if (_previewTextureID != 0) {
|
||||
glDeleteTextures(1, &_previewTextureID);
|
||||
_previewTextureID = 0;
|
||||
}
|
||||
Parent::internalDeactivate();
|
||||
}
|
||||
|
||||
extern glm::vec3 getPoint(float yaw, float pitch);
|
||||
|
||||
void HmdDisplayPlugin::OverlayRender::build() {
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
vertices = std::make_shared<gpu::Buffer>();
|
||||
indices = std::make_shared<gpu::Buffer>();
|
||||
|
||||
//UV mapping source: http://www.mvps.org/directx/articles/spheremap.htm
|
||||
|
||||
static const float fov = CompositorHelper::VIRTUAL_UI_TARGET_FOV.y;
|
||||
static const float aspectRatio = CompositorHelper::VIRTUAL_UI_ASPECT_RATIO;
|
||||
static const uint16_t stacks = 128;
|
||||
static const uint16_t slices = 64;
|
||||
|
||||
Vertex vertex;
|
||||
|
||||
// Compute vertices positions and texture UV coordinate
|
||||
// Create and write to buffer
|
||||
for (int i = 0; i < stacks; i++) {
|
||||
vertex.uv.y = (float)i / (float)(stacks - 1); // First stack is 0.0f, last stack is 1.0f
|
||||
// abs(theta) <= fov / 2.0f
|
||||
float pitch = -fov * (vertex.uv.y - 0.5f);
|
||||
for (int j = 0; j < slices; j++) {
|
||||
vertex.uv.x = (float)j / (float)(slices - 1); // First slice is 0.0f, last slice is 1.0f
|
||||
// abs(phi) <= fov * aspectRatio / 2.0f
|
||||
float yaw = -fov * aspectRatio * (vertex.uv.x - 0.5f);
|
||||
vertex.pos = getPoint(yaw, pitch);
|
||||
vertices->append(sizeof(Vertex), (gpu::Byte*)&vertex);
|
||||
}
|
||||
}
|
||||
|
||||
// Compute number of indices needed
|
||||
static const int VERTEX_PER_TRANGLE = 3;
|
||||
static const int TRIANGLE_PER_RECTANGLE = 2;
|
||||
int numberOfRectangles = (slices - 1) * (stacks - 1);
|
||||
indexCount = numberOfRectangles * TRIANGLE_PER_RECTANGLE * VERTEX_PER_TRANGLE;
|
||||
|
||||
// Compute indices order
|
||||
std::vector<GLushort> indices;
|
||||
for (int i = 0; i < stacks - 1; i++) {
|
||||
for (int j = 0; j < slices - 1; j++) {
|
||||
GLushort bottomLeftIndex = i * slices + j;
|
||||
GLushort bottomRightIndex = bottomLeftIndex + 1;
|
||||
GLushort topLeftIndex = bottomLeftIndex + slices;
|
||||
GLushort topRightIndex = topLeftIndex + 1;
|
||||
// FIXME make a z-order curve for better vertex cache locality
|
||||
indices.push_back(topLeftIndex);
|
||||
indices.push_back(bottomLeftIndex);
|
||||
indices.push_back(topRightIndex);
|
||||
|
||||
indices.push_back(topRightIndex);
|
||||
indices.push_back(bottomLeftIndex);
|
||||
indices.push_back(bottomRightIndex);
|
||||
}
|
||||
}
|
||||
this->indices->append(indices);
|
||||
format = std::make_shared<gpu::Stream::Format>(); // 1 for everyone
|
||||
format->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0);
|
||||
format->setAttribute(gpu::Stream::TEXCOORD, gpu::Stream::TEXCOORD, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV));
|
||||
uniformBuffers[0] = std::make_shared<gpu::Buffer>(sizeof(Uniforms), nullptr);
|
||||
uniformBuffers[1] = std::make_shared<gpu::Buffer>(sizeof(Uniforms), nullptr);
|
||||
updatePipeline();
|
||||
}
|
||||
|
||||
void HmdDisplayPlugin::OverlayRender::updatePipeline() {
|
||||
static const QString vsFile = PathUtils::resourcesPath() + "/shaders/hmd_ui_glow.vert";
|
||||
static const QString fsFile = PathUtils::resourcesPath() + "/shaders/hmd_ui_glow.frag";
|
||||
|
||||
#if LIVE_SHADER_RELOAD
|
||||
static qint64 vsBuiltAge = 0;
|
||||
static qint64 fsBuiltAge = 0;
|
||||
QFileInfo vsInfo(vsFile);
|
||||
QFileInfo fsInfo(fsFile);
|
||||
auto vsAge = vsInfo.lastModified().toMSecsSinceEpoch();
|
||||
auto fsAge = fsInfo.lastModified().toMSecsSinceEpoch();
|
||||
if (!pipeline || vsAge > vsBuiltAge || fsAge > fsBuiltAge) {
|
||||
vsBuiltAge = vsAge;
|
||||
fsBuiltAge = fsAge;
|
||||
#else
|
||||
if (!pipeline) {
|
||||
#endif
|
||||
QString vsSource = readFile(vsFile);
|
||||
QString fsSource = readFile(fsFile);
|
||||
auto vs = gpu::Shader::createVertex(vsSource.toLocal8Bit().toStdString());
|
||||
auto ps = gpu::Shader::createPixel(fsSource.toLocal8Bit().toStdString());
|
||||
auto program = gpu::Shader::createProgram(vs, ps);
|
||||
gpu::gl::GLBackend::makeProgram(*program, gpu::Shader::BindingSet());
|
||||
this->uniformsLocation = program->getBuffers().findLocation("overlayBuffer");
|
||||
|
||||
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||
state->setDepthTest(gpu::State::DepthTest(false));
|
||||
state->setBlendFunction(true,
|
||||
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
|
||||
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
||||
|
||||
pipeline = gpu::Pipeline::create(program, state);
|
||||
}
|
||||
}
|
||||
|
||||
void HmdDisplayPlugin::OverlayRender::render() {
|
||||
for_each_eye([&](Eye eye){
|
||||
uniforms.mvp = mvps[eye];
|
||||
uniformBuffers[eye]->setSubData(0, uniforms);
|
||||
});
|
||||
gpu::Batch batch;
|
||||
batch.enableStereo(false);
|
||||
batch.setResourceTexture(0, plugin._currentFrame->overlay);
|
||||
batch.setPipeline(pipeline);
|
||||
batch.setInputFormat(format);
|
||||
gpu::BufferView posView(vertices, VERTEX_OFFSET, vertices->getSize(), VERTEX_STRIDE, format->getAttributes().at(gpu::Stream::POSITION)._element);
|
||||
gpu::BufferView uvView(vertices, TEXTURE_OFFSET, vertices->getSize(), VERTEX_STRIDE, format->getAttributes().at(gpu::Stream::TEXCOORD)._element);
|
||||
batch.setInputBuffer(gpu::Stream::POSITION, posView);
|
||||
batch.setInputBuffer(gpu::Stream::TEXCOORD, uvView);
|
||||
batch.setIndexBuffer(gpu::UINT16, indices, 0);
|
||||
for_each_eye([&](Eye eye){
|
||||
batch.setUniformBuffer(uniformsLocation, uniformBuffers[eye]);
|
||||
batch.setViewportTransform(plugin.eyeViewport(eye));
|
||||
batch.drawIndexed(gpu::TRIANGLES, indexCount);
|
||||
});
|
||||
// FIXME use stereo information input to set both MVPs in the uniforms
|
||||
plugin._backend->render(batch);
|
||||
}
|
||||
|
||||
void HmdDisplayPlugin::customizeContext() {
|
||||
Parent::customizeContext();
|
||||
// Only enable mirroring if we know vsync is disabled
|
||||
|
@ -103,32 +234,15 @@ void HmdDisplayPlugin::customizeContext() {
|
|||
enableVsync(false);
|
||||
#endif
|
||||
_enablePreview = !isVsyncEnabled();
|
||||
_sphereSection = loadSphereSection(_program, CompositorHelper::VIRTUAL_UI_TARGET_FOV.y, CompositorHelper::VIRTUAL_UI_ASPECT_RATIO);
|
||||
using namespace oglplus;
|
||||
if (!_enablePreview) {
|
||||
const std::string version("#version 410 core\n");
|
||||
compileProgram(_previewProgram, version + DrawUnitQuadTexcoord_vert, version + DrawTexture_frag);
|
||||
_previewUniforms.previewTexture = Uniform<int>(*_previewProgram, "colorMap").Location();
|
||||
}
|
||||
|
||||
_overlay.build();
|
||||
#if 0
|
||||
updateReprojectionProgram();
|
||||
updateOverlayProgram();
|
||||
#ifdef HMD_HAND_LASER_SUPPORT
|
||||
updateLaserProgram();
|
||||
_laserGeometry = loadLaser(_laserProgram);
|
||||
#endif
|
||||
}
|
||||
|
||||
//#define LIVE_SHADER_RELOAD 1
|
||||
|
||||
static QString readFile(const QString& filename) {
|
||||
QFile file(filename);
|
||||
file.open(QFile::Text | QFile::ReadOnly);
|
||||
QString result;
|
||||
result.append(QTextStream(&file).readAll());
|
||||
return result;
|
||||
}
|
||||
|
||||
#if 0
|
||||
void HmdDisplayPlugin::updateReprojectionProgram() {
|
||||
static const QString vsFile = PathUtils::resourcesPath() + "/shaders/hmd_reproject.vert";
|
||||
static const QString fsFile = PathUtils::resourcesPath() + "/shaders/hmd_reproject.frag";
|
||||
|
@ -161,11 +275,11 @@ void HmdDisplayPlugin::updateReprojectionProgram() {
|
|||
qWarning() << "Error building reprojection shader " << error.what();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HMD_HAND_LASER_SUPPORT
|
||||
void HmdDisplayPlugin::updateLaserProgram() {
|
||||
#if 0
|
||||
static const QString vsFile = PathUtils::resourcesPath() + "/shaders/hmd_hand_lasers.vert";
|
||||
static const QString gsFile = PathUtils::resourcesPath() + "/shaders/hmd_hand_lasers.geom";
|
||||
static const QString fsFile = PathUtils::resourcesPath() + "/shaders/hmd_hand_lasers.frag";
|
||||
|
@ -204,56 +318,16 @@ void HmdDisplayPlugin::updateLaserProgram() {
|
|||
qWarning() << "Error building hand laser composite shader " << error.what();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void HmdDisplayPlugin::updateOverlayProgram() {
|
||||
static const QString vsFile = PathUtils::resourcesPath() + "/shaders/hmd_ui_glow.vert";
|
||||
static const QString fsFile = PathUtils::resourcesPath() + "/shaders/hmd_ui_glow.frag";
|
||||
|
||||
#if LIVE_SHADER_RELOAD
|
||||
static qint64 vsBuiltAge = 0;
|
||||
static qint64 fsBuiltAge = 0;
|
||||
QFileInfo vsInfo(vsFile);
|
||||
QFileInfo fsInfo(fsFile);
|
||||
auto vsAge = vsInfo.lastModified().toMSecsSinceEpoch();
|
||||
auto fsAge = fsInfo.lastModified().toMSecsSinceEpoch();
|
||||
if (!_overlayProgram || vsAge > vsBuiltAge || fsAge > fsBuiltAge) {
|
||||
vsBuiltAge = vsAge;
|
||||
fsBuiltAge = fsAge;
|
||||
#else
|
||||
if (!_overlayProgram) {
|
||||
#endif
|
||||
QString vsSource = readFile(vsFile);
|
||||
QString fsSource = readFile(fsFile);
|
||||
ProgramPtr program;
|
||||
try {
|
||||
compileProgram(program, vsSource.toLocal8Bit().toStdString(), fsSource.toLocal8Bit().toStdString());
|
||||
if (program) {
|
||||
using namespace oglplus;
|
||||
_overlayUniforms.mvp = Uniform<glm::mat4>(*program, "mvp").Location();
|
||||
_overlayUniforms.alpha = Uniform<float>(*program, "alpha").Location();
|
||||
_overlayUniforms.glowColors = Uniform<glm::vec4>(*program, "glowColors").Location();
|
||||
_overlayUniforms.glowPoints = Uniform<glm::vec4>(*program, "glowPoints").Location();
|
||||
_overlayUniforms.resolution = Uniform<glm::vec2>(*program, "resolution").Location();
|
||||
_overlayUniforms.radius = Uniform<float>(*program, "radius").Location();
|
||||
_overlayProgram = program;
|
||||
useProgram(_overlayProgram);
|
||||
Uniform<glm::vec2>(*_overlayProgram, _overlayUniforms.resolution).Set(CompositorHelper::VIRTUAL_SCREEN_SIZE);
|
||||
}
|
||||
} catch (std::runtime_error& error) {
|
||||
qWarning() << "Error building overlay composite shader " << error.what();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HmdDisplayPlugin::uncustomizeContext() {
|
||||
#if 0
|
||||
_overlayProgram.reset();
|
||||
_sphereSection.reset();
|
||||
_compositeFramebuffer.reset();
|
||||
_previewProgram.reset();
|
||||
_reprojectionProgram.reset();
|
||||
#ifdef HMD_HAND_LASER_SUPPORT
|
||||
_laserProgram.reset();
|
||||
_laserGeometry.reset();
|
||||
#endif
|
||||
|
@ -277,6 +351,7 @@ void HmdDisplayPlugin::compositeScene() {
|
|||
#ifdef DEBUG_REPROJECTION_SHADER
|
||||
_reprojectionProgram = getReprojectionProgram();
|
||||
#endif
|
||||
#if 0
|
||||
useProgram(_reprojectionProgram);
|
||||
|
||||
using namespace oglplus;
|
||||
|
@ -290,20 +365,23 @@ void HmdDisplayPlugin::compositeScene() {
|
|||
glUniformMatrix4fv(_reprojectionUniforms.projectionMatrix, 2, GL_FALSE, &(_eyeProjections[0][0][0]));
|
||||
_plane->UseInProgram(*_reprojectionProgram);
|
||||
_plane->Draw();
|
||||
#endif
|
||||
}
|
||||
|
||||
void HmdDisplayPlugin::compositeOverlay() {
|
||||
using namespace oglplus;
|
||||
if (!_currentFrame) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto compositorHelper = DependencyManager::get<CompositorHelper>();
|
||||
glm::mat4 modelMat = compositorHelper->getModelTransform().getMatrix();
|
||||
|
||||
withPresentThreadLock([&] {
|
||||
_presentHandLasers = _handLasers;
|
||||
_presentHandPoses = _handPoses;
|
||||
_presentUiModelTransform = _uiModelTransform;
|
||||
});
|
||||
std::array<vec2, NUMBER_OF_HANDS> handGlowPoints { { vec2(-1), vec2(-1) } };
|
||||
|
||||
std::array<vec2, NUMBER_OF_HANDS> handGlowPoints{ { vec2(-1), vec2(-1) } };
|
||||
// compute the glow point interesections
|
||||
for (int i = 0; i < NUMBER_OF_HANDS; ++i) {
|
||||
if (_presentHandPoses[i] == IDENTITY_MATRIX) {
|
||||
|
@ -353,65 +431,49 @@ void HmdDisplayPlugin::compositeOverlay() {
|
|||
handGlowPoints[i] = yawPitch;
|
||||
}
|
||||
|
||||
updateOverlayProgram();
|
||||
if (!_overlayProgram) {
|
||||
if (!_currentFrame->overlay) {
|
||||
return;
|
||||
}
|
||||
|
||||
useProgram(_overlayProgram);
|
||||
for_each_eye([&](Eye eye){
|
||||
auto modelView = glm::inverse(_currentPresentFrameInfo.presentPose * getEyeToHeadTransform(eye)) * modelMat;
|
||||
_overlay.mvps[eye] = _eyeProjections[eye] * modelView;
|
||||
});
|
||||
|
||||
// Setup the uniforms
|
||||
{
|
||||
if (_overlayUniforms.alpha >= 0) {
|
||||
Uniform<float>(*_overlayProgram, _overlayUniforms.alpha).Set(_compositeOverlayAlpha);
|
||||
}
|
||||
if (_overlayUniforms.glowPoints >= 0) {
|
||||
vec4 glowPoints(handGlowPoints[0], handGlowPoints[1]);
|
||||
Uniform<glm::vec4>(*_overlayProgram, _overlayUniforms.glowPoints).Set(glowPoints);
|
||||
}
|
||||
if (_overlayUniforms.glowColors >= 0) {
|
||||
std::array<glm::vec4, NUMBER_OF_HANDS> glowColors;
|
||||
glowColors[0] = _presentHandLasers[0].color;
|
||||
glowColors[1] = _presentHandLasers[1].color;
|
||||
glProgramUniform4fv(GetName(*_overlayProgram), _overlayUniforms.glowColors, 2, &glowColors[0].r);
|
||||
}
|
||||
_overlay.uniforms.alpha = _compositeOverlayAlpha;
|
||||
_overlay.uniforms.glowPoints = vec4(handGlowPoints[0], handGlowPoints[1]);
|
||||
_overlay.uniforms.glowColors[0] = _presentHandLasers[0].color;
|
||||
_overlay.uniforms.glowColors[1] = _presentHandLasers[1].color;
|
||||
}
|
||||
|
||||
_sphereSection->Use();
|
||||
for_each_eye([&](Eye eye) {
|
||||
eyeViewport(eye);
|
||||
auto modelView = glm::inverse(_currentPresentFrameInfo.presentPose * getEyeToHeadTransform(eye)) * modelMat;
|
||||
auto mvp = _eyeProjections[eye] * modelView;
|
||||
Uniform<glm::mat4>(*_overlayProgram, _overlayUniforms.mvp).Set(mvp);
|
||||
_sphereSection->Draw();
|
||||
});
|
||||
_overlay.render();
|
||||
}
|
||||
|
||||
void HmdDisplayPlugin::compositePointer() {
|
||||
using namespace oglplus;
|
||||
|
||||
auto& cursorManager = Cursor::Manager::instance();
|
||||
const auto& cursorData = _cursorsData[cursorManager.getCursor()->getIcon()];
|
||||
auto compositorHelper = DependencyManager::get<CompositorHelper>();
|
||||
|
||||
useProgram(_program);
|
||||
// set the alpha
|
||||
Uniform<float>(*_program, _alphaUniform).Set(_compositeOverlayAlpha);
|
||||
|
||||
// Mouse pointer
|
||||
_plane->Use();
|
||||
// Reconstruct the headpose from the eye poses
|
||||
auto headPosition = vec3(_currentPresentFrameInfo.presentPose[3]);
|
||||
gpu::Batch batch;
|
||||
batch.enableStereo(false);
|
||||
batch.setProjectionTransform(mat4());
|
||||
batch.setFramebuffer(_currentFrame->framebuffer);
|
||||
batch.setPipeline(_cursorPipeline);
|
||||
batch.setResourceTexture(0, cursorData.texture);
|
||||
batch.setViewTransform(Transform());
|
||||
for_each_eye([&](Eye eye) {
|
||||
eyeViewport(eye);
|
||||
auto eyePose = _currentPresentFrameInfo.presentPose * getEyeToHeadTransform(eye);
|
||||
auto reticleTransform = compositorHelper->getReticleTransform(eyePose, headPosition);
|
||||
auto mvp = _eyeProjections[eye] * reticleTransform;
|
||||
Uniform<glm::mat4>(*_program, _mvpUniform).Set(mvp);
|
||||
_plane->Draw();
|
||||
batch.setViewportTransform(eyeViewport(eye));
|
||||
batch.setModelTransform(reticleTransform);
|
||||
batch.setProjectionTransform(_eyeProjections[eye]);
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
});
|
||||
// restore the alpha
|
||||
Uniform<float>(*_program, _alphaUniform).Set(1.0);
|
||||
_backend->render(batch);
|
||||
}
|
||||
|
||||
|
||||
void HmdDisplayPlugin::internalPresent() {
|
||||
|
||||
PROFILE_RANGE_EX(__FUNCTION__, 0xff00ff00, (uint64_t)presentCount())
|
||||
|
@ -445,58 +507,45 @@ void HmdDisplayPlugin::internalPresent() {
|
|||
targetViewportPosition.y = (windowSize.y - targetViewportSize.y) / 2;
|
||||
}
|
||||
|
||||
|
||||
if (_enablePreview) {
|
||||
using namespace oglplus;
|
||||
Context::Clear().ColorBuffer();
|
||||
auto sourceSize = _compositeFramebuffer->size;
|
||||
if (_monoPreview) {
|
||||
sourceSize.x /= 2;
|
||||
}
|
||||
_compositeFramebuffer->Bound(Framebuffer::Target::Read, [&] {
|
||||
Context::BlitFramebuffer(
|
||||
0, 0, sourceSize.x, sourceSize.y,
|
||||
targetViewportPosition.x, targetViewportPosition.y,
|
||||
targetViewportPosition.x + targetViewportSize.x, targetViewportPosition.y + targetViewportSize.y,
|
||||
BufferSelectBit::ColorBuffer, BlitFilter::Nearest);
|
||||
});
|
||||
swapBuffers();
|
||||
} else if (_firstPreview || windowSize != _prevWindowSize || devicePixelRatio != _prevDevicePixelRatio) {
|
||||
useProgram(_previewProgram);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
|
||||
glClearColor(0, 0, 0, 1);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glViewport(targetViewportPosition.x, targetViewportPosition.y, targetViewportSize.x, targetViewportSize.y);
|
||||
glUniform1i(_previewUniforms.previewTexture, 0);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, _previewTextureID);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
swapBuffers();
|
||||
_firstPreview = false;
|
||||
_prevWindowSize = windowSize;
|
||||
_prevDevicePixelRatio = devicePixelRatio;
|
||||
}
|
||||
Parent::internalPresent();
|
||||
//gpu::Batch presentBatch;
|
||||
//presentBatch.enableStereo(false);
|
||||
//presentBatch.setViewTransform(Transform());
|
||||
//presentBatch.setFramebuffer(gpu::FramebufferPointer());
|
||||
//presentBatch.setViewportTransform(ivec4(targetViewportPosition, targetViewportSize));
|
||||
//presentBatch.setResourceTexture(0, _currentFrame->framebuffer->getRenderBuffer(0));
|
||||
//presentBatch.setPipeline(_presentPipeline);
|
||||
//presentBatch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
//_backend->render(presentBatch);
|
||||
//swapBuffers();
|
||||
}
|
||||
|
||||
postPreview();
|
||||
}
|
||||
|
||||
void HmdDisplayPlugin::setEyeRenderPose(uint32_t frameIndex, Eye eye, const glm::mat4& pose) {
|
||||
}
|
||||
|
||||
void HmdDisplayPlugin::updateFrameData() {
|
||||
// Check if we have old frame data to discard
|
||||
withPresentThreadLock([&] {
|
||||
auto itr = _frameInfos.find(_currentPresentFrameIndex);
|
||||
if (itr != _frameInfos.end()) {
|
||||
_frameInfos.erase(itr);
|
||||
}
|
||||
});
|
||||
static const uint32_t INVALID_FRAME = (uint32_t)(~0);
|
||||
uint32_t oldFrameIndex = _currentFrame ? _currentFrame->frameIndex : INVALID_FRAME;
|
||||
|
||||
Parent::updateFrameData();
|
||||
uint32_t newFrameIndex = _currentFrame ? _currentFrame->frameIndex : INVALID_FRAME;
|
||||
|
||||
withPresentThreadLock([&] {
|
||||
_currentPresentFrameInfo = _frameInfos[_currentPresentFrameIndex];
|
||||
});
|
||||
if (oldFrameIndex != newFrameIndex) {
|
||||
withPresentThreadLock([&] {
|
||||
if (oldFrameIndex != INVALID_FRAME) {
|
||||
auto itr = _frameInfos.find(oldFrameIndex);
|
||||
if (itr != _frameInfos.end()) {
|
||||
_frameInfos.erase(itr);
|
||||
}
|
||||
}
|
||||
if (newFrameIndex != INVALID_FRAME) {
|
||||
_currentPresentFrameInfo = _frameInfos[newFrameIndex];
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
glm::mat4 HmdDisplayPlugin::getHeadPose() const {
|
||||
|
@ -508,7 +557,7 @@ bool HmdDisplayPlugin::setHandLaser(uint32_t hands, HandLaserMode mode, const ve
|
|||
info.mode = mode;
|
||||
info.color = color;
|
||||
info.direction = direction;
|
||||
withRenderThreadLock([&] {
|
||||
withNonPresentThreadLock([&] {
|
||||
if (hands & Hand::LeftHand) {
|
||||
_handLasers[0] = info;
|
||||
}
|
||||
|
@ -522,7 +571,7 @@ bool HmdDisplayPlugin::setHandLaser(uint32_t hands, HandLaserMode mode, const ve
|
|||
}
|
||||
|
||||
void HmdDisplayPlugin::compositeExtra() {
|
||||
#ifdef HMD_HAND_LASER_SUPPORT
|
||||
#if 0
|
||||
// If neither hand laser is activated, exit
|
||||
if (!_presentHandLasers[0].valid() && !_presentHandLasers[1].valid()) {
|
||||
return;
|
||||
|
@ -592,4 +641,6 @@ void HmdDisplayPlugin::compositeExtra() {
|
|||
});
|
||||
glDisable(GL_BLEND);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -9,18 +9,21 @@
|
|||
|
||||
#include <ThreadSafeValueCache.h>
|
||||
|
||||
#include <array>
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <Transform.h>
|
||||
|
||||
#include "../OpenGLDisplayPlugin.h"
|
||||
#include <gpu/Format.h>
|
||||
#include <gpu/Stream.h>
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#define HMD_HAND_LASER_SUPPORT
|
||||
#endif
|
||||
#include "../CompositorHelper.h"
|
||||
#include "../OpenGLDisplayPlugin.h"
|
||||
|
||||
class HmdDisplayPlugin : public OpenGLDisplayPlugin {
|
||||
using Parent = OpenGLDisplayPlugin;
|
||||
public:
|
||||
HmdDisplayPlugin() : _overlay( *this ) {}
|
||||
bool isHmd() const override final { return true; }
|
||||
float getIPD() const override final { return _ipd; }
|
||||
glm::mat4 getEyeToHeadTransform(Eye eye) const override final { return _eyeOffsets[eye]; }
|
||||
|
@ -28,7 +31,6 @@ public:
|
|||
glm::mat4 getCullingProjection(const glm::mat4& baseProjection) const override final { return _cullingProjection; }
|
||||
glm::uvec2 getRecommendedUiSize() const override final;
|
||||
glm::uvec2 getRecommendedRenderSize() const override final { return _renderTargetSize; }
|
||||
void setEyeRenderPose(uint32_t frameIndex, Eye eye, const glm::mat4& pose) override final;
|
||||
bool isDisplayVisible() const override { return isHmdMounted(); }
|
||||
|
||||
QRect getRecommendedOverlayRect() const override final;
|
||||
|
@ -65,6 +67,9 @@ protected:
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
Transform _uiModelTransform;
|
||||
std::array<HandLaserInfo, 2> _handLasers;
|
||||
std::array<glm::mat4, 2> _handPoses;
|
||||
|
@ -96,10 +101,7 @@ protected:
|
|||
FrameInfo _currentRenderFrameInfo;
|
||||
|
||||
private:
|
||||
void updateOverlayProgram();
|
||||
#ifdef HMD_HAND_LASER_SUPPORT
|
||||
void updateLaserProgram();
|
||||
#endif
|
||||
void updateReprojectionProgram();
|
||||
|
||||
bool _enablePreview { false };
|
||||
|
@ -107,26 +109,53 @@ private:
|
|||
bool _enableReprojection { true };
|
||||
bool _firstPreview { true };
|
||||
|
||||
ProgramPtr _overlayProgram;
|
||||
struct OverlayUniforms {
|
||||
int32_t mvp { -1 };
|
||||
int32_t alpha { -1 };
|
||||
int32_t glowColors { -1 };
|
||||
int32_t glowPoints { -1 };
|
||||
int32_t resolution { -1 };
|
||||
int32_t radius { -1 };
|
||||
} _overlayUniforms;
|
||||
float _previewAspect { 0 };
|
||||
glm::uvec2 _prevWindowSize { 0, 0 };
|
||||
qreal _prevDevicePixelRatio { 0 };
|
||||
|
||||
|
||||
struct OverlayRender {
|
||||
OverlayRender(HmdDisplayPlugin& plugin) : plugin(plugin) {};
|
||||
HmdDisplayPlugin& plugin;
|
||||
gpu::Stream::FormatPointer format;
|
||||
gpu::BufferPointer vertices;
|
||||
gpu::BufferPointer indices;
|
||||
uint32_t indexCount { 0 };
|
||||
gpu::PipelinePointer pipeline;
|
||||
int32_t uniformsLocation { -1 };
|
||||
|
||||
// FIXME this is stupid, use the built in transformation pipeline
|
||||
std::array<gpu::BufferPointer, 2> uniformBuffers;
|
||||
std::array<mat4, 2> mvps;
|
||||
|
||||
struct Uniforms {
|
||||
mat4 mvp;
|
||||
vec4 glowPoints { -1 };
|
||||
vec4 glowColors[2];
|
||||
vec2 resolution { CompositorHelper::VIRTUAL_SCREEN_SIZE };
|
||||
float radius { 0.005f };
|
||||
float alpha { 1.0f };
|
||||
} uniforms;
|
||||
|
||||
struct Vertex {
|
||||
vec3 pos;
|
||||
vec2 uv;
|
||||
} vertex;
|
||||
|
||||
static const size_t VERTEX_OFFSET { offsetof(Vertex, pos) };
|
||||
static const size_t TEXTURE_OFFSET { offsetof(Vertex, uv) };
|
||||
static const int VERTEX_STRIDE { sizeof(Vertex) };
|
||||
|
||||
void build();
|
||||
void updatePipeline();
|
||||
void render();
|
||||
} _overlay;
|
||||
#if 0
|
||||
ProgramPtr _previewProgram;
|
||||
struct PreviewUniforms {
|
||||
int32_t previewTexture { -1 };
|
||||
} _previewUniforms;
|
||||
|
||||
float _previewAspect { 0 };
|
||||
GLuint _previewTextureID { 0 };
|
||||
glm::uvec2 _prevWindowSize { 0, 0 };
|
||||
qreal _prevDevicePixelRatio { 0 };
|
||||
|
||||
ProgramPtr _reprojectionProgram;
|
||||
struct ReprojectionUniforms {
|
||||
int32_t reprojectionMatrix { -1 };
|
||||
|
@ -134,9 +163,6 @@ private:
|
|||
int32_t projectionMatrix { -1 };
|
||||
} _reprojectionUniforms;
|
||||
|
||||
ShapeWrapperPtr _sphereSection;
|
||||
|
||||
#ifdef HMD_HAND_LASER_SUPPORT
|
||||
ProgramPtr _laserProgram;
|
||||
struct LaserUniforms {
|
||||
int32_t mvp { -1 };
|
||||
|
@ -145,4 +171,3 @@ private:
|
|||
ShapeWrapperPtr _laserGeometry;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -8,52 +8,56 @@
|
|||
|
||||
#include "InterleavedStereoDisplayPlugin.h"
|
||||
|
||||
static const char * INTERLEAVED_TEXTURED_VS = R"VS(#version 410 core
|
||||
#pragma line __LINE__
|
||||
#include <gpu/StandardShaderLib.h>
|
||||
#include <gpu/Pipeline.h>
|
||||
#include <gpu/Batch.h>
|
||||
#include <gpu/Context.h>
|
||||
|
||||
in vec3 Position;
|
||||
in vec2 TexCoord;
|
||||
static const char* INTERLEAVED_SRGB_TO_LINEAR_FRAG = R"SCRIBE(
|
||||
|
||||
out vec2 vTexCoord;
|
||||
struct TextureData {
|
||||
ivec2 textureSize;
|
||||
};
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(Position, 1);
|
||||
vTexCoord = TexCoord;
|
||||
}
|
||||
layout(std140) uniform textureDataBuffer {
|
||||
TextureData textureData;
|
||||
};
|
||||
|
||||
)VS";
|
||||
uniform sampler2D colorMap;
|
||||
|
||||
static const char * INTERLEAVED_TEXTURED_FS = R"FS(#version 410 core
|
||||
#pragma line __LINE__
|
||||
in vec2 varTexCoord0;
|
||||
|
||||
uniform sampler2D sampler;
|
||||
uniform ivec2 textureSize;
|
||||
out vec4 outFragColor;
|
||||
|
||||
in vec2 vTexCoord;
|
||||
out vec4 FragColor;
|
||||
|
||||
void main() {
|
||||
ivec2 texCoord = ivec2(floor(vTexCoord * textureSize));
|
||||
void main(void) {
|
||||
ivec2 texCoord = ivec2(floor(varTexCoord0 * textureData.textureSize));
|
||||
texCoord.x /= 2;
|
||||
int row = int(floor(gl_FragCoord.y));
|
||||
if (row % 2 > 0) {
|
||||
texCoord.x += (textureSize.x / 2);
|
||||
texCoord.x += (textureData.textureSize.x / 2);
|
||||
}
|
||||
FragColor = texelFetch(sampler, texCoord, 0); //texture(sampler, texCoord);
|
||||
outFragColor = vec4(pow(texelFetch(colorMap, texCoord, 0).rgb, vec3(2.2)), 1.0);
|
||||
}
|
||||
|
||||
)FS";
|
||||
)SCRIBE";
|
||||
|
||||
const QString InterleavedStereoDisplayPlugin::NAME("3D TV - Interleaved");
|
||||
|
||||
void InterleavedStereoDisplayPlugin::customizeContext() {
|
||||
StereoDisplayPlugin::customizeContext();
|
||||
// Set up the stencil buffers? Or use a custom shader?
|
||||
compileProgram(_interleavedProgram, INTERLEAVED_TEXTURED_VS, INTERLEAVED_TEXTURED_FS);
|
||||
if (!_interleavedPresentPipeline) {
|
||||
auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS();
|
||||
auto ps = gpu::Shader::createPixel(std::string(INTERLEAVED_SRGB_TO_LINEAR_FRAG));
|
||||
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
|
||||
gpu::Shader::makeProgram(*program);
|
||||
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||
state->setDepthTest(gpu::State::DepthTest(false));
|
||||
_interleavedPresentPipeline = gpu::Pipeline::create(program, state);
|
||||
}
|
||||
}
|
||||
|
||||
void InterleavedStereoDisplayPlugin::uncustomizeContext() {
|
||||
_interleavedProgram.reset();
|
||||
_interleavedPresentPipeline.reset();
|
||||
StereoDisplayPlugin::uncustomizeContext();
|
||||
}
|
||||
|
||||
|
@ -65,15 +69,14 @@ glm::uvec2 InterleavedStereoDisplayPlugin::getRecommendedRenderSize() const {
|
|||
}
|
||||
|
||||
void InterleavedStereoDisplayPlugin::internalPresent() {
|
||||
using namespace oglplus;
|
||||
auto sceneSize = getRecommendedRenderSize();
|
||||
_interleavedProgram->Bind();
|
||||
Uniform<ivec2>(*_interleavedProgram, "textureSize").SetValue(sceneSize);
|
||||
auto surfaceSize = getSurfacePixels();
|
||||
Context::Viewport(0, 0, surfaceSize.x, surfaceSize.y);
|
||||
glBindTexture(GL_TEXTURE_2D, GetName(_compositeFramebuffer->color));
|
||||
_plane->Use();
|
||||
_plane->Draw();
|
||||
gpu::Batch presentBatch;
|
||||
presentBatch.enableStereo(false);
|
||||
presentBatch.setViewTransform(Transform());
|
||||
presentBatch.setFramebuffer(gpu::FramebufferPointer());
|
||||
presentBatch.setViewportTransform(ivec4(uvec2(0), getSurfacePixels()));
|
||||
presentBatch.setResourceTexture(0, _currentFrame->framebuffer->getRenderBuffer(0));
|
||||
presentBatch.setPipeline(_interleavedPresentPipeline);
|
||||
presentBatch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
_backend->render(presentBatch);
|
||||
swapBuffers();
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ protected:
|
|||
void internalPresent() override;
|
||||
|
||||
private:
|
||||
ProgramPtr _interleavedProgram;
|
||||
static const QString NAME;
|
||||
gpu::PipelinePointer _interleavedPresentPipeline;
|
||||
gpu::BufferPointer _textureDataBuffer;
|
||||
};
|
||||
|
|
|
@ -7,16 +7,11 @@
|
|||
//
|
||||
|
||||
#include "SideBySideStereoDisplayPlugin.h"
|
||||
#include <GLMHelpers.h>
|
||||
#include <CursorManager.h>
|
||||
#include <ui-plugins/PluginContainer.h>
|
||||
#include <gl/GLWidget.h>
|
||||
#include "../CompositorHelper.h"
|
||||
|
||||
const QString SideBySideStereoDisplayPlugin::NAME("3D TV - Side by Side Stereo");
|
||||
|
||||
glm::uvec2 SideBySideStereoDisplayPlugin::getRecommendedRenderSize() const {
|
||||
uvec2 result = Parent::getRecommendedRenderSize();
|
||||
result.x *= 2;
|
||||
//result.x *= 2;
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -101,4 +101,3 @@ void StereoDisplayPlugin::internalDeactivate() {
|
|||
float StereoDisplayPlugin::getRecommendedAspectRatio() const {
|
||||
return aspect(Parent::getRecommendedRenderSize());
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
#include <SharedUtil.h>
|
||||
#include <NumericalConstants.h>
|
||||
|
||||
#include "Config.h"
|
||||
|
||||
// The GLEscrow class provides a simple mechanism for producer GL contexts to provide
|
||||
// content to a consumer where the consumer is assumed to be connected to a display and
|
||||
// therefore must never be blocked.
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <QtGui/QOpenGLContext>
|
||||
|
||||
#include "GLHelpers.h"
|
||||
#include "QOpenGLDebugLoggerWrapper.h"
|
||||
|
||||
#ifdef DEBUG
|
||||
static bool enableDebugLogger = true;
|
||||
|
@ -80,7 +81,7 @@ bool OffscreenGLCanvas::makeCurrent() {
|
|||
_logger = new QOpenGLDebugLogger(this);
|
||||
if (_logger->initialize()) {
|
||||
connect(_logger, &QOpenGLDebugLogger::messageLogged, [](const QOpenGLDebugMessage& message) {
|
||||
qDebug() << message;
|
||||
OpenGLDebug::log(message);
|
||||
});
|
||||
_logger->disableMessages(QOpenGLDebugMessage::AnySource, QOpenGLDebugMessage::AnyType, QOpenGLDebugMessage::NotificationSeverity);
|
||||
_logger->startLogging(QOpenGLDebugLogger::LoggingMode::SynchronousLogging);
|
||||
|
@ -101,4 +102,4 @@ QObject* OffscreenGLCanvas::getContextObject() {
|
|||
void OffscreenGLCanvas::moveToThreadWithContext(QThread* thread) {
|
||||
moveToThread(thread);
|
||||
_context->moveToThread(thread);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -120,24 +120,9 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] =
|
|||
(&::gpu::gl::GLBackend::do_popProfileRange),
|
||||
};
|
||||
|
||||
extern std::function<uint32(const Texture& texture)> TEXTURE_ID_RESOLVER;
|
||||
|
||||
void GLBackend::init() {
|
||||
static std::once_flag once;
|
||||
std::call_once(once, [] {
|
||||
|
||||
TEXTURE_ID_RESOLVER = [](const Texture& texture)->uint32 {
|
||||
auto object = Backend::getGPUObject<GLTexture>(texture);
|
||||
if (!object) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (object->getSyncState() != GLSyncState::Idle) {
|
||||
return object->_downsampleSource._texture;
|
||||
}
|
||||
return object->_texture;
|
||||
};
|
||||
|
||||
QString vendor{ (const char*)glGetString(GL_VENDOR) };
|
||||
QString renderer{ (const char*)glGetString(GL_RENDERER) };
|
||||
qCDebug(gpugllogging) << "GL Version: " << QString((const char*) glGetString(GL_VERSION));
|
||||
|
|
|
@ -35,12 +35,13 @@ class GLBackend : public Backend {
|
|||
friend class gpu::Context;
|
||||
static void init();
|
||||
static Backend* createBackend();
|
||||
static bool makeProgram(Shader& shader, const Shader::BindingSet& slotBindings);
|
||||
|
||||
protected:
|
||||
explicit GLBackend(bool syncCache);
|
||||
GLBackend();
|
||||
public:
|
||||
static bool makeProgram(Shader& shader, const Shader::BindingSet& slotBindings = Shader::BindingSet());
|
||||
|
||||
~GLBackend();
|
||||
|
||||
void render(Batch& batch) final;
|
||||
|
@ -159,9 +160,10 @@ public:
|
|||
virtual void do_setStateBlendFactor(Batch& batch, size_t paramOffset) final;
|
||||
virtual void do_setStateScissorRect(Batch& batch, size_t paramOffset) final;
|
||||
|
||||
virtual GLuint getFramebufferID(const FramebufferPointer& framebuffer) = 0;
|
||||
|
||||
protected:
|
||||
|
||||
virtual GLuint getFramebufferID(const FramebufferPointer& framebuffer) = 0;
|
||||
virtual GLFramebuffer* syncGPUObject(const Framebuffer& framebuffer) = 0;
|
||||
|
||||
virtual GLuint getBufferID(const Buffer& buffer) = 0;
|
||||
|
|
|
@ -23,7 +23,7 @@ public:
|
|||
object = new GLBufferType(buffer, object);
|
||||
}
|
||||
|
||||
if (0 != (buffer._flags & Buffer::DIRTY)) {
|
||||
if (0 != (buffer._renderPages._flags & PageManager::DIRTY)) {
|
||||
object->transfer();
|
||||
}
|
||||
|
||||
|
|
|
@ -42,14 +42,14 @@ public:
|
|||
Size offset;
|
||||
Size size;
|
||||
Size currentPage { 0 };
|
||||
auto data = _gpuObject.getSysmem().readData();
|
||||
while (_gpuObject.getNextTransferBlock(offset, size, currentPage)) {
|
||||
auto data = _gpuObject._renderSysmem.readData();
|
||||
while (_gpuObject._renderPages.getNextTransferBlock(offset, size, currentPage)) {
|
||||
glBufferSubData(GL_ARRAY_BUFFER, offset, size, data + offset);
|
||||
(void)CHECK_GL_ERROR();
|
||||
}
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
(void)CHECK_GL_ERROR();
|
||||
_gpuObject._flags &= ~Buffer::DIRTY;
|
||||
_gpuObject._renderPages._flags &= ~PageManager::DIRTY;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -32,12 +32,12 @@ public:
|
|||
Size offset;
|
||||
Size size;
|
||||
Size currentPage { 0 };
|
||||
auto data = _gpuObject.getSysmem().readData();
|
||||
while (_gpuObject.getNextTransferBlock(offset, size, currentPage)) {
|
||||
auto data = _gpuObject._renderSysmem.readData();
|
||||
while (_gpuObject._renderPages.getNextTransferBlock(offset, size, currentPage)) {
|
||||
glNamedBufferSubData(_buffer, (GLintptr)offset, (GLsizeiptr)size, data + offset);
|
||||
}
|
||||
(void)CHECK_GL_ERROR();
|
||||
_gpuObject._flags &= ~Buffer::DIRTY;
|
||||
_gpuObject._renderPages._flags &= ~PageManager::DIRTY;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -20,13 +20,6 @@ Context::Context() {
|
|||
if (_createBackendCallback) {
|
||||
_backend.reset(_createBackendCallback());
|
||||
}
|
||||
|
||||
_frameHandler = [this](Frame& frame){
|
||||
for (size_t i = 0; i < frame.batches.size(); ++i) {
|
||||
_backend->_stereo = frame.stereoStates[i];
|
||||
_backend->render(frame.batches[i]);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Context::Context(const Context& context) {
|
||||
|
@ -35,40 +28,30 @@ Context::Context(const Context& context) {
|
|||
Context::~Context() {
|
||||
}
|
||||
|
||||
void Context::setFrameHandler(FrameHandler handler) {
|
||||
_frameHandler = handler;
|
||||
}
|
||||
|
||||
#define DEFERRED_RENDERING
|
||||
|
||||
void Context::beginFrame(const FramebufferPointer& outputFramebuffer, const glm::mat4& renderPose) {
|
||||
_currentFrame = Frame();
|
||||
_currentFrame.framebuffer = outputFramebuffer;
|
||||
_currentFrame.pose = renderPose;
|
||||
void Context::beginFrame(const glm::mat4& renderPose) {
|
||||
assert(!_frameActive);
|
||||
_frameActive = true;
|
||||
_currentFrame = std::make_shared<Frame>();
|
||||
_currentFrame->pose = renderPose;
|
||||
}
|
||||
|
||||
void Context::append(Batch& batch) {
|
||||
if (!_frameActive) {
|
||||
qWarning() << "Batch executed outside of frame boundaries";
|
||||
return;
|
||||
}
|
||||
#ifdef DEFERRED_RENDERING
|
||||
_currentFrame.batches.emplace_back(batch);
|
||||
_currentFrame.stereoStates.emplace_back(_stereo);
|
||||
#else
|
||||
_backend->_stereo = _stereo;
|
||||
_backend->render(batch);
|
||||
#endif
|
||||
_currentFrame->batches.push_back(batch);
|
||||
}
|
||||
|
||||
void Context::endFrame() {
|
||||
#ifdef DEFERRED_RENDERING
|
||||
if (_frameHandler) {
|
||||
_frameHandler(_currentFrame);
|
||||
}
|
||||
#endif
|
||||
_currentFrame = Frame();
|
||||
FramePointer Context::endFrame() {
|
||||
assert(_frameActive);
|
||||
auto result = _currentFrame;
|
||||
_currentFrame.reset();
|
||||
_frameActive = false;
|
||||
|
||||
result->stereoState = _stereo;
|
||||
result->finish();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
@ -111,16 +94,10 @@ void Context::getStereoViews(mat4* eyeViews) const {
|
|||
}
|
||||
}
|
||||
|
||||
void Context::syncCache() {
|
||||
PROFILE_RANGE(__FUNCTION__);
|
||||
_backend->syncCache();
|
||||
}
|
||||
|
||||
void Context::downloadFramebuffer(const FramebufferPointer& srcFramebuffer, const Vec4i& region, QImage& destImage) {
|
||||
_backend->downloadFramebuffer(srcFramebuffer, region, destImage);
|
||||
}
|
||||
|
||||
|
||||
void Context::getStats(ContextStats& stats) const {
|
||||
_backend->getStats(stats);
|
||||
}
|
||||
|
|
|
@ -51,8 +51,9 @@ class Backend {
|
|||
public:
|
||||
virtual~ Backend() {};
|
||||
|
||||
virtual void render(Batch& batch) = 0;
|
||||
void setStereoState(const StereoState& stereo) { _stereo = stereo; }
|
||||
|
||||
virtual void render(Batch& batch) = 0;
|
||||
virtual void syncCache() = 0;
|
||||
virtual void downloadFramebuffer(const FramebufferPointer& srcFramebuffer, const Vec4i& region, QImage& destImage) = 0;
|
||||
|
||||
|
@ -139,10 +140,11 @@ public:
|
|||
Context();
|
||||
~Context();
|
||||
|
||||
void setFrameHandler(FrameHandler handler);
|
||||
void beginFrame(const FramebufferPointer& outputFramebuffer, const glm::mat4& renderPose = glm::mat4());
|
||||
void beginFrame(const glm::mat4& renderPose = glm::mat4());
|
||||
void append(Batch& batch);
|
||||
void endFrame();
|
||||
FramePointer endFrame();
|
||||
|
||||
const BackendPointer& getBackend() const { return _backend; }
|
||||
|
||||
void enableStereo(bool enable = true);
|
||||
bool isStereo();
|
||||
|
@ -150,7 +152,6 @@ public:
|
|||
void setStereoViews(const mat4 eyeViews[2]);
|
||||
void getStereoProjections(mat4* eyeProjections) const;
|
||||
void getStereoViews(mat4* eyeViews) const;
|
||||
void syncCache();
|
||||
|
||||
// Downloading the Framebuffer is a synchronous action that is not efficient.
|
||||
// It s here for convenience to easily capture a snapshot
|
||||
|
@ -171,10 +172,9 @@ public:
|
|||
protected:
|
||||
Context(const Context& context);
|
||||
|
||||
std::unique_ptr<Backend> _backend;
|
||||
std::shared_ptr<Backend> _backend;
|
||||
bool _frameActive { false };
|
||||
Frame _currentFrame;
|
||||
FrameHandler _frameHandler;
|
||||
FramePointer _currentFrame;
|
||||
StereoState _stereo;
|
||||
|
||||
// This function can only be called by "static Shader::makeProgram()"
|
||||
|
|
|
@ -11,20 +11,23 @@
|
|||
|
||||
#include <stdint.h>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
namespace gpu {
|
||||
using Mutex = std::mutex;
|
||||
using Lock = std::unique_lock<Mutex>;
|
||||
|
||||
class Batch;
|
||||
class Backend;
|
||||
using BackendPointer = std::shared_ptr<Backend>;
|
||||
class Context;
|
||||
using ContextPointer = std::shared_ptr<Context>;
|
||||
class GPUObject;
|
||||
class Frame;
|
||||
using FramePointer = std::shared_ptr<Frame>;
|
||||
using FrameHandler = std::function<void(Frame& frame)>;
|
||||
|
||||
using Stamp = int;
|
||||
using uint32 = uint32_t;
|
||||
|
|
53
libraries/gpu/src/gpu/Frame.cpp
Normal file
53
libraries/gpu/src/gpu/Frame.cpp
Normal 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();
|
||||
}
|
|
@ -8,20 +8,44 @@
|
|||
#ifndef hifi_gpu_Frame_h
|
||||
#define hifi_gpu_Frame_h
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "Forward.h"
|
||||
#include "Batch.h"
|
||||
#include "Resource.h"
|
||||
|
||||
namespace gpu {
|
||||
|
||||
class Frame {
|
||||
public:
|
||||
/// The sensor pose used for rendering the frame, only applicable for HMDs
|
||||
glm::mat4 pose;
|
||||
/// The collection of batches which make up the frame
|
||||
std::vector<Batch> batches;
|
||||
std::vector<StereoState> stereoStates;
|
||||
/// The destination framebuffer in which the results will be placed
|
||||
FramebufferPointer framebuffer;
|
||||
};
|
||||
class Frame {
|
||||
public:
|
||||
using Batches = std::vector<Batch>;
|
||||
using FramebufferRecycler = std::function<void(const FramebufferPointer&)>;
|
||||
using OverlayRecycler = std::function<void(const TexturePointer&)>;
|
||||
using BufferUpdate = std::pair<BufferPointer, Buffer::Update>;
|
||||
using BufferUpdates = std::vector<BufferUpdate>;
|
||||
|
||||
virtual ~Frame();
|
||||
void finish();
|
||||
void preRender();
|
||||
|
||||
StereoState stereoState;
|
||||
uint32_t frameIndex{ 0 };
|
||||
/// The sensor pose used for rendering the frame, only applicable for HMDs
|
||||
Mat4 pose;
|
||||
/// The collection of batches which make up the frame
|
||||
Batches batches;
|
||||
/// The destination framebuffer in which the results will be placed
|
||||
FramebufferPointer framebuffer;
|
||||
/// The destination texture containing the 2D overlay
|
||||
TexturePointer overlay;
|
||||
|
||||
/// How to process the framebuffer when the frame dies. MUST BE THREAD SAFE
|
||||
FramebufferRecycler framebufferRecycler;
|
||||
/// How to process the overlay texture when the frame dies. MUST BE THREAD SAFE
|
||||
OverlayRecycler overlayRecycler;
|
||||
BufferUpdates bufferUpdates;
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -78,7 +78,7 @@ const float AllocationDebugger::K = 1024.0f;
|
|||
|
||||
static AllocationDebugger allocationDebugger;
|
||||
|
||||
Resource::Size Resource::Sysmem::allocateMemory(Byte** dataAllocated, Size size) {
|
||||
Size Sysmem::allocateMemory(Byte** dataAllocated, Size size) {
|
||||
allocationDebugger += size;
|
||||
if ( !dataAllocated ) {
|
||||
qWarning() << "Buffer::Sysmem::allocateMemory() : Must have a valid dataAllocated pointer.";
|
||||
|
@ -102,40 +102,40 @@ Resource::Size Resource::Sysmem::allocateMemory(Byte** dataAllocated, Size size)
|
|||
return newSize;
|
||||
}
|
||||
|
||||
void Resource::Sysmem::deallocateMemory(Byte* dataAllocated, Size size) {
|
||||
void Sysmem::deallocateMemory(Byte* dataAllocated, Size size) {
|
||||
allocationDebugger -= size;
|
||||
if (dataAllocated) {
|
||||
delete[] dataAllocated;
|
||||
}
|
||||
}
|
||||
|
||||
Resource::Sysmem::Sysmem() {}
|
||||
Sysmem::Sysmem() {}
|
||||
|
||||
Resource::Sysmem::Sysmem(Size size, const Byte* bytes) {
|
||||
Sysmem::Sysmem(Size size, const Byte* bytes) {
|
||||
if (size > 0 && bytes) {
|
||||
setData(_size, bytes);
|
||||
}
|
||||
}
|
||||
|
||||
Resource::Sysmem::Sysmem(const Sysmem& sysmem) {
|
||||
Sysmem::Sysmem(const Sysmem& sysmem) {
|
||||
if (sysmem.getSize() > 0) {
|
||||
allocate(sysmem._size);
|
||||
setData(_size, sysmem._data);
|
||||
}
|
||||
}
|
||||
|
||||
Resource::Sysmem& Resource::Sysmem::operator=(const Sysmem& sysmem) {
|
||||
Sysmem& Sysmem::operator=(const Sysmem& sysmem) {
|
||||
setData(sysmem.getSize(), sysmem.readData());
|
||||
return (*this);
|
||||
}
|
||||
|
||||
Resource::Sysmem::~Sysmem() {
|
||||
Sysmem::~Sysmem() {
|
||||
deallocateMemory( _data, _size );
|
||||
_data = NULL;
|
||||
_size = 0;
|
||||
}
|
||||
|
||||
Resource::Size Resource::Sysmem::allocate(Size size) {
|
||||
Size Sysmem::allocate(Size size) {
|
||||
if (size != _size) {
|
||||
Byte* newData = NULL;
|
||||
Size newSize = 0;
|
||||
|
@ -156,7 +156,7 @@ Resource::Size Resource::Sysmem::allocate(Size size) {
|
|||
return _size;
|
||||
}
|
||||
|
||||
Resource::Size Resource::Sysmem::resize(Size size) {
|
||||
Size Sysmem::resize(Size size) {
|
||||
if (size != _size) {
|
||||
Byte* newData = NULL;
|
||||
Size newSize = 0;
|
||||
|
@ -182,7 +182,7 @@ Resource::Size Resource::Sysmem::resize(Size size) {
|
|||
return _size;
|
||||
}
|
||||
|
||||
Resource::Size Resource::Sysmem::setData( Size size, const Byte* bytes ) {
|
||||
Size Sysmem::setData( Size size, const Byte* bytes ) {
|
||||
if (allocate(size) == size) {
|
||||
if (size && bytes) {
|
||||
memcpy( _data, bytes, _size );
|
||||
|
@ -191,7 +191,7 @@ Resource::Size Resource::Sysmem::setData( Size size, const Byte* bytes ) {
|
|||
return _size;
|
||||
}
|
||||
|
||||
Resource::Size Resource::Sysmem::setSubData( Size offset, Size size, const Byte* bytes) {
|
||||
Size Sysmem::setSubData( Size offset, Size size, const Byte* bytes) {
|
||||
if (size && ((offset + size) <= getSize()) && bytes) {
|
||||
memcpy( _data + offset, bytes, size );
|
||||
return size;
|
||||
|
@ -199,7 +199,7 @@ Resource::Size Resource::Sysmem::setSubData( Size offset, Size size, const Byte*
|
|||
return 0;
|
||||
}
|
||||
|
||||
Resource::Size Resource::Sysmem::append(Size size, const Byte* bytes) {
|
||||
Size Sysmem::append(Size size, const Byte* bytes) {
|
||||
if (size > 0) {
|
||||
Size oldSize = getSize();
|
||||
Size totalSize = oldSize + size;
|
||||
|
@ -241,7 +241,7 @@ Buffer::Size Buffer::getBufferGPUMemoryUsage() {
|
|||
}
|
||||
|
||||
Buffer::Buffer(Size pageSize) :
|
||||
_pageSize(pageSize) {
|
||||
_pages(pageSize) {
|
||||
_bufferCPUCount++;
|
||||
}
|
||||
|
||||
|
@ -249,12 +249,12 @@ Buffer::Buffer(Size size, const Byte* bytes, Size pageSize) : Buffer(pageSize) {
|
|||
setData(size, bytes);
|
||||
}
|
||||
|
||||
Buffer::Buffer(const Buffer& buf) : Buffer(buf._pageSize) {
|
||||
Buffer::Buffer(const Buffer& buf) : Buffer(buf._pages._pageSize) {
|
||||
setData(buf.getSize(), buf.getData());
|
||||
}
|
||||
|
||||
Buffer& Buffer::operator=(const Buffer& buf) {
|
||||
const_cast<Size&>(_pageSize) = buf._pageSize;
|
||||
const_cast<Size&>(_pages._pageSize) = buf._pages._pageSize;
|
||||
setData(buf.getSize(), buf.getData());
|
||||
return (*this);
|
||||
}
|
||||
|
@ -266,14 +266,10 @@ Buffer::~Buffer() {
|
|||
|
||||
Buffer::Size Buffer::resize(Size size) {
|
||||
_end = size;
|
||||
auto prevSize = editSysmem().getSize();
|
||||
auto prevSize = _sysmem.getSize();
|
||||
if (prevSize < size) {
|
||||
auto newPages = getRequiredPageCount();
|
||||
auto newSize = newPages * _pageSize;
|
||||
editSysmem().resize(newSize);
|
||||
// All new pages start off as clean, because they haven't been populated by data
|
||||
_pages.resize(newPages, 0);
|
||||
Buffer::updateBufferCPUMemoryUsage(prevSize, newSize);
|
||||
_sysmem.resize(_pages.accommodate(_end));
|
||||
Buffer::updateBufferCPUMemoryUsage(prevSize, _sysmem.getSize());
|
||||
}
|
||||
return _end;
|
||||
}
|
||||
|
@ -282,28 +278,45 @@ void Buffer::markDirty(Size offset, Size bytes) {
|
|||
if (!bytes) {
|
||||
return;
|
||||
}
|
||||
_flags |= DIRTY;
|
||||
// Find the starting page
|
||||
Size startPage = (offset / _pageSize);
|
||||
// Non-zero byte count, so at least one page is dirty
|
||||
Size pageCount = 1;
|
||||
// How much of the page is after the offset?
|
||||
Size remainder = _pageSize - (offset % _pageSize);
|
||||
// If there are more bytes than page space remaining, we need to increase the page count
|
||||
if (bytes > remainder) {
|
||||
// Get rid of the amount that will fit in the current page
|
||||
bytes -= remainder;
|
||||
|
||||
pageCount += (bytes / _pageSize);
|
||||
if (bytes % _pageSize) {
|
||||
++pageCount;
|
||||
_pages.markRegion(offset, bytes);
|
||||
}
|
||||
|
||||
void Buffer::applyUpdate(const Update& update) {
|
||||
_renderSysmem.resize(update.size);
|
||||
_renderPages = update.pages;
|
||||
update.updateOperator(_renderSysmem);
|
||||
}
|
||||
|
||||
Buffer::Update Buffer::getUpdate() const {
|
||||
static Update EMPTY_UPDATE;
|
||||
if (!_pages) {
|
||||
return EMPTY_UPDATE;
|
||||
}
|
||||
|
||||
Update result;
|
||||
result.pages = _pages;
|
||||
result.size = _sysmem.getSize();
|
||||
Size pageSize = _pages._pageSize;
|
||||
PageManager::Pages dirtyPages = _pages.getMarkedPages();
|
||||
std::vector<uint8> dirtyPageData;
|
||||
dirtyPageData.resize(dirtyPages.size() * pageSize);
|
||||
for (Size i = 0; i < dirtyPages.size(); ++i) {
|
||||
Size page = dirtyPages[i];
|
||||
Size sourceOffset = page * pageSize;
|
||||
Size destOffset = i * pageSize;
|
||||
memcpy(dirtyPageData.data() + destOffset, _sysmem.readData() + sourceOffset, pageSize);
|
||||
}
|
||||
|
||||
result.updateOperator = [pageSize, dirtyPages, dirtyPageData](Sysmem& dest){
|
||||
for (Size i = 0; i < dirtyPages.size(); ++i) {
|
||||
Size page = dirtyPages[i];
|
||||
Size sourceOffset = i * pageSize;
|
||||
Size destOffset = page * pageSize;
|
||||
memcpy(dest.editData() + destOffset, dirtyPageData.data() + sourceOffset, pageSize);
|
||||
}
|
||||
}
|
||||
|
||||
// Mark the pages dirty
|
||||
for (Size i = 0; i < pageCount; ++i) {
|
||||
_pages[i + startPage] |= DIRTY;
|
||||
}
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
@ -333,14 +346,6 @@ Buffer::Size Buffer::getSize() const {
|
|||
return _end;
|
||||
}
|
||||
|
||||
Buffer::Size Buffer::getRequiredPageCount() const {
|
||||
Size result = _end / _pageSize;
|
||||
if (_end % _pageSize) {
|
||||
++result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
const Element BufferView::DEFAULT_ELEMENT = Element( gpu::SCALAR, gpu::UINT8, gpu::RAW );
|
||||
|
||||
BufferView::BufferView() :
|
||||
|
|
|
@ -25,11 +25,69 @@
|
|||
|
||||
namespace gpu {
|
||||
|
||||
// Sysmem is the underneath cache for the data in ram of a resource.
|
||||
class Sysmem {
|
||||
public:
|
||||
static const Size NOT_ALLOCATED = (Size)-1;
|
||||
|
||||
Sysmem();
|
||||
Sysmem(Size size, const Byte* bytes);
|
||||
Sysmem(const Sysmem& sysmem); // deep copy of the sysmem buffer
|
||||
Sysmem& operator=(const Sysmem& sysmem); // deep copy of the sysmem buffer
|
||||
~Sysmem();
|
||||
|
||||
Size getSize() const { return _size; }
|
||||
|
||||
// Allocate the byte array
|
||||
// \param pSize The nb of bytes to allocate, if already exist, content is lost.
|
||||
// \return The nb of bytes allocated, nothing if allready the appropriate size.
|
||||
Size allocate(Size pSize);
|
||||
|
||||
// Resize the byte array
|
||||
// Keep previous data [0 to min(pSize, mSize)]
|
||||
Size resize(Size pSize);
|
||||
|
||||
// Assign data bytes and size (allocate for size, then copy bytes if exists)
|
||||
Size setData(Size size, const Byte* bytes);
|
||||
|
||||
// Update Sub data,
|
||||
// doesn't allocate and only copy size * bytes at the offset location
|
||||
// only if all fits in the existing allocated buffer
|
||||
Size setSubData(Size offset, Size size, const Byte* bytes);
|
||||
|
||||
// Append new data at the end of the current buffer
|
||||
// do a resize( size + getSIze) and copy the new data
|
||||
// \return the number of bytes copied
|
||||
Size append(Size size, const Byte* data);
|
||||
|
||||
// Access the byte array.
|
||||
// The edit version allow to map data.
|
||||
const Byte* readData() const { return _data; }
|
||||
Byte* editData() { return _data; }
|
||||
|
||||
template< typename T > const T* read() const { return reinterpret_cast< T* > (_data); }
|
||||
template< typename T > T* edit() { return reinterpret_cast< T* > (_data); }
|
||||
|
||||
// Access the current version of the sysmem, used to compare if copies are in sync
|
||||
Stamp getStamp() const { return _stamp; }
|
||||
|
||||
static Size allocateMemory(Byte** memAllocated, Size size);
|
||||
static void deallocateMemory(Byte* memDeallocated, Size size);
|
||||
|
||||
bool isAvailable() const { return (_data != 0); }
|
||||
|
||||
using Operator = std::function<void(Sysmem& sysmem)>;
|
||||
private:
|
||||
Stamp _stamp{ 0 };
|
||||
Size _size{ 0 };
|
||||
Byte* _data{ nullptr };
|
||||
}; // Sysmem
|
||||
|
||||
class Resource {
|
||||
public:
|
||||
typedef size_t Size;
|
||||
|
||||
static const Size NOT_ALLOCATED = (Size)-1;
|
||||
static const Size NOT_ALLOCATED = Sysmem::NOT_ALLOCATED;
|
||||
|
||||
// The size in bytes of data stored in the resource
|
||||
virtual Size getSize() const = 0;
|
||||
|
@ -47,88 +105,178 @@ public:
|
|||
};
|
||||
|
||||
protected:
|
||||
using Sysmem = gpu::Sysmem;
|
||||
|
||||
Resource() {}
|
||||
virtual ~Resource() {}
|
||||
|
||||
// Sysmem is the underneath cache for the data in ram of a resource.
|
||||
class Sysmem {
|
||||
public:
|
||||
}; // Resource
|
||||
|
||||
Sysmem();
|
||||
Sysmem(Size size, const Byte* bytes);
|
||||
Sysmem(const Sysmem& sysmem); // deep copy of the sysmem buffer
|
||||
Sysmem& operator=(const Sysmem& sysmem); // deep copy of the sysmem buffer
|
||||
~Sysmem();
|
||||
|
||||
Size getSize() const { return _size; }
|
||||
struct PageManager {
|
||||
static const Size DEFAULT_PAGE_SIZE = 4096;
|
||||
|
||||
// Allocate the byte array
|
||||
// \param pSize The nb of bytes to allocate, if already exist, content is lost.
|
||||
// \return The nb of bytes allocated, nothing if allready the appropriate size.
|
||||
Size allocate(Size pSize);
|
||||
|
||||
// Resize the byte array
|
||||
// Keep previous data [0 to min(pSize, mSize)]
|
||||
Size resize(Size pSize);
|
||||
|
||||
// Assign data bytes and size (allocate for size, then copy bytes if exists)
|
||||
Size setData(Size size, const Byte* bytes );
|
||||
|
||||
// Update Sub data,
|
||||
// doesn't allocate and only copy size * bytes at the offset location
|
||||
// only if all fits in the existing allocated buffer
|
||||
Size setSubData(Size offset, Size size, const Byte* bytes);
|
||||
|
||||
// Append new data at the end of the current buffer
|
||||
// do a resize( size + getSIze) and copy the new data
|
||||
// \return the number of bytes copied
|
||||
Size append(Size size, const Byte* data);
|
||||
|
||||
// Access the byte array.
|
||||
// The edit version allow to map data.
|
||||
const Byte* readData() const { return _data; }
|
||||
Byte* editData() { return _data; }
|
||||
|
||||
template< typename T > const T* read() const { return reinterpret_cast< T* > ( _data ); }
|
||||
template< typename T > T* edit() { return reinterpret_cast< T* > ( _data ); }
|
||||
|
||||
// Access the current version of the sysmem, used to compare if copies are in sync
|
||||
Stamp getStamp() const { return _stamp; }
|
||||
|
||||
static Size allocateMemory(Byte** memAllocated, Size size);
|
||||
static void deallocateMemory(Byte* memDeallocated, Size size);
|
||||
|
||||
bool isAvailable() const { return (_data != 0); }
|
||||
|
||||
private:
|
||||
Stamp _stamp { 0 };
|
||||
Size _size { 0 };
|
||||
Byte* _data { nullptr };
|
||||
enum Flag {
|
||||
DIRTY = 0x01,
|
||||
};
|
||||
|
||||
PageManager(Size pageSize = DEFAULT_PAGE_SIZE) : _pageSize(pageSize) {}
|
||||
PageManager& operator=(const PageManager& other) {
|
||||
assert(other._pageSize == _pageSize);
|
||||
_pages = other._pages;
|
||||
_flags = other._flags;
|
||||
return *this;
|
||||
}
|
||||
|
||||
using Vector = std::vector<uint8_t>;
|
||||
using Pages = std::vector<Size>;
|
||||
Vector _pages;
|
||||
|
||||
uint8 _flags{ 0 };
|
||||
const Size _pageSize;
|
||||
|
||||
operator bool const() {
|
||||
return (*this)(DIRTY);
|
||||
}
|
||||
|
||||
bool operator()(uint8 desiredFlags) const {
|
||||
return (desiredFlags == (_flags & desiredFlags));
|
||||
}
|
||||
|
||||
void markPage(Size index, uint8 markFlags = DIRTY) {
|
||||
assert(_pages.size() > index);
|
||||
_pages[index] |= markFlags;
|
||||
_flags |= markFlags;
|
||||
}
|
||||
|
||||
void markRegion(Size offset, Size bytes, uint8 markFlags = DIRTY) {
|
||||
if (!bytes) {
|
||||
return;
|
||||
}
|
||||
_flags |= markFlags;
|
||||
// Find the starting page
|
||||
Size startPage = (offset / _pageSize);
|
||||
// Non-zero byte count, so at least one page is dirty
|
||||
Size pageCount = 1;
|
||||
// How much of the page is after the offset?
|
||||
Size remainder = _pageSize - (offset % _pageSize);
|
||||
// If there are more bytes than page space remaining, we need to increase the page count
|
||||
if (bytes > remainder) {
|
||||
// Get rid of the amount that will fit in the current page
|
||||
bytes -= remainder;
|
||||
|
||||
pageCount += (bytes / _pageSize);
|
||||
if (bytes % _pageSize) {
|
||||
++pageCount;
|
||||
}
|
||||
}
|
||||
|
||||
// Mark the pages dirty
|
||||
for (Size i = 0; i < pageCount; ++i) {
|
||||
_pages[i + startPage] |= DIRTY;
|
||||
}
|
||||
}
|
||||
|
||||
Size getPageCount(uint8_t desiredFlags = DIRTY) const {
|
||||
Size result = 0;
|
||||
for (auto pageFlags : _pages) {
|
||||
if (desiredFlags == (pageFlags & desiredFlags)) {
|
||||
++result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Size getSize(uint8_t desiredFlags = DIRTY) const {
|
||||
return getPageCount(desiredFlags) * _pageSize;
|
||||
}
|
||||
|
||||
void setPageCount(Size count) {
|
||||
_pages.resize(count);
|
||||
}
|
||||
|
||||
Size getRequiredPageCount(Size size) const {
|
||||
Size result = size / _pageSize;
|
||||
if (size % _pageSize) {
|
||||
++result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Size getRequiredSize(Size size) const {
|
||||
return getRequiredPageCount(size) * _pageSize;
|
||||
}
|
||||
|
||||
Size accommodate(Size size) {
|
||||
Size newPageCount = getRequiredPageCount(size);
|
||||
Size newSize = newPageCount * _pageSize;
|
||||
_pages.resize(newPageCount, 0);
|
||||
return newSize;
|
||||
}
|
||||
|
||||
// Get pages with the specified flags, optionally clearing the flags as we go
|
||||
Pages getMarkedPages(uint8_t desiredFlags = DIRTY, bool clear = true) {
|
||||
Pages result;
|
||||
if (desiredFlags == (_flags & desiredFlags)) {
|
||||
_flags &= ~desiredFlags;
|
||||
result.reserve(_pages.size());
|
||||
for (Size i = 0; i < _pages.size(); ++i) {
|
||||
if (desiredFlags == (_pages[i] & desiredFlags)) {
|
||||
result.push_back(i);
|
||||
if (clear) {
|
||||
_pages[i] &= ~desiredFlags;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool getNextTransferBlock(Size& outOffset, Size& outSize, Size& currentPage) {
|
||||
Size pageCount = _pages.size();
|
||||
// Advance to the first dirty page
|
||||
while (currentPage < pageCount && (0 == (DIRTY & _pages[currentPage]))) {
|
||||
++currentPage;
|
||||
}
|
||||
|
||||
// If we got to the end, we're done
|
||||
if (currentPage >= pageCount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Advance to the next clean page
|
||||
outOffset = static_cast<Size>(currentPage * _pageSize);
|
||||
while (currentPage < pageCount && (0 != (DIRTY & _pages[currentPage]))) {
|
||||
_pages[currentPage] &= ~DIRTY;
|
||||
++currentPage;
|
||||
}
|
||||
outSize = static_cast<Size>((currentPage * _pageSize) - outOffset);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class Buffer : public Resource {
|
||||
static std::atomic<uint32_t> _bufferCPUCount;
|
||||
static std::atomic<Size> _bufferCPUMemoryUsage;
|
||||
static void updateBufferCPUMemoryUsage(Size prevObjectSize, Size newObjectSize);
|
||||
|
||||
public:
|
||||
enum Flag {
|
||||
DIRTY = 0x01,
|
||||
using Flag = PageManager::Flag;
|
||||
struct Update {
|
||||
Size size;
|
||||
PageManager pages;
|
||||
Sysmem::Operator updateOperator;
|
||||
};
|
||||
|
||||
// Currently only one flag... 'dirty'
|
||||
using PageFlags = std::vector<uint8_t>;
|
||||
static const Size DEFAULT_PAGE_SIZE = 4096;
|
||||
static uint32_t getBufferCPUCount();
|
||||
static Size getBufferCPUMemoryUsage();
|
||||
static uint32_t getBufferGPUCount();
|
||||
static Size getBufferGPUMemoryUsage();
|
||||
|
||||
Buffer(Size pageSize = DEFAULT_PAGE_SIZE);
|
||||
Buffer(Size size, const Byte* bytes, Size pageSize = DEFAULT_PAGE_SIZE);
|
||||
Buffer(Size pageSize = PageManager::DEFAULT_PAGE_SIZE);
|
||||
Buffer(Size size, const Byte* bytes, Size pageSize = PageManager::DEFAULT_PAGE_SIZE);
|
||||
Buffer(const Buffer& buf); // deep copy of the sysmem buffer
|
||||
Buffer& operator=(const Buffer& buf); // deep copy of the sysmem buffer
|
||||
~Buffer();
|
||||
|
@ -184,34 +332,24 @@ public:
|
|||
return append(sizeof(T) * t.size(), reinterpret_cast<const Byte*>(&t[0]));
|
||||
}
|
||||
|
||||
bool getNextTransferBlock(Size& outOffset, Size& outSize, Size& currentPage) const {
|
||||
Size pageCount = _pages.size();
|
||||
// Advance to the first dirty page
|
||||
while (currentPage < pageCount && (0 == (Buffer::DIRTY & _pages[currentPage]))) {
|
||||
++currentPage;
|
||||
}
|
||||
|
||||
// If we got to the end, we're done
|
||||
if (currentPage >= pageCount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Advance to the next clean page
|
||||
outOffset = static_cast<Size>(currentPage * _pageSize);
|
||||
while (currentPage < pageCount && (0 != (Buffer::DIRTY & _pages[currentPage]))) {
|
||||
_pages[currentPage] &= ~Buffer::DIRTY;
|
||||
++currentPage;
|
||||
}
|
||||
outSize = static_cast<Size>((currentPage * _pageSize) - outOffset);
|
||||
return true;
|
||||
}
|
||||
|
||||
const GPUObjectPointer gpuObject {};
|
||||
|
||||
// Access the sysmem object, limited to ourselves and GPUObject derived classes
|
||||
const Sysmem& getSysmem() const { return _sysmem; }
|
||||
// FIXME find a better access mechanism for clearing this
|
||||
mutable uint8_t _flags;
|
||||
|
||||
bool isDirty() const {
|
||||
return _pages(PageManager::DIRTY);
|
||||
}
|
||||
|
||||
void applyUpdate(const Update& update);
|
||||
|
||||
// Main thread operation to say that the buffer is ready to be used as a frame
|
||||
Update getUpdate() const;
|
||||
|
||||
mutable PageManager _renderPages;
|
||||
Sysmem _renderSysmem;
|
||||
|
||||
protected:
|
||||
void markDirty(Size offset, Size bytes);
|
||||
|
||||
|
@ -223,16 +361,15 @@ protected:
|
|||
Sysmem& editSysmem() { return _sysmem; }
|
||||
Byte* editData() { return editSysmem().editData(); }
|
||||
|
||||
Size getRequiredPageCount() const;
|
||||
|
||||
Size _end { 0 };
|
||||
mutable PageFlags _pages;
|
||||
const Size _pageSize;
|
||||
mutable PageManager _pages;
|
||||
Size _end{ 0 };
|
||||
Sysmem _sysmem;
|
||||
|
||||
|
||||
// FIXME find a more generic way to do this.
|
||||
friend class gl::GLBuffer;
|
||||
friend class BufferView;
|
||||
friend class Frame;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<Buffer> BufferPointer;
|
||||
|
|
|
@ -880,11 +880,3 @@ Vec3u Texture::evalMipDimensions(uint16 level) const {
|
|||
return glm::max(dimensions, Vec3u(1));
|
||||
}
|
||||
|
||||
std::function<uint32(const gpu::Texture& texture)> TEXTURE_ID_RESOLVER;
|
||||
|
||||
uint32 Texture::getHardwareId() const {
|
||||
if (TEXTURE_ID_RESOLVER) {
|
||||
return TEXTURE_ID_RESOLVER(*this);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -449,8 +449,6 @@ public:
|
|||
|
||||
const GPUObjectPointer gpuObject {};
|
||||
|
||||
uint32 getHardwareId() const;
|
||||
|
||||
protected:
|
||||
std::unique_ptr< Storage > _storage;
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
set(TARGET_NAME plugins)
|
||||
setup_hifi_library(OpenGL)
|
||||
link_hifi_libraries(shared)
|
||||
include_hifi_library_headers(gpu)
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <GLMHelpers.h>
|
||||
#include <RegisteredMetaTypes.h>
|
||||
#include <shared/Bilateral.h>
|
||||
#include <gpu/Forward.h>
|
||||
|
||||
#include "Plugin.h"
|
||||
|
||||
|
@ -91,11 +92,6 @@ public:
|
|||
return glm::mat4();
|
||||
}
|
||||
|
||||
// Needed for timewarp style features
|
||||
virtual void setEyeRenderPose(uint32_t frameIndex, Eye eye, const glm::mat4& pose) {
|
||||
// NOOP
|
||||
}
|
||||
|
||||
virtual void abandonCalibration() {}
|
||||
|
||||
virtual void resetSensors() {}
|
||||
|
@ -132,6 +128,7 @@ public:
|
|||
Present = QEvent::User + 1
|
||||
};
|
||||
|
||||
virtual bool isRenderThread() const { return false; }
|
||||
virtual bool isHmd() const { return false; }
|
||||
virtual int getHmdScreen() const { return -1; }
|
||||
/// By default, all HMDs are stereo
|
||||
|
@ -149,16 +146,8 @@ public:
|
|||
virtual QString getPreferredAudioOutDevice() const { return QString(); }
|
||||
|
||||
// Rendering support
|
||||
|
||||
/**
|
||||
* Sends the scene texture to the display plugin.
|
||||
*/
|
||||
virtual void submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) = 0;
|
||||
|
||||
/**
|
||||
* Sends the scene texture to the display plugin.
|
||||
*/
|
||||
virtual void submitOverlayTexture(const gpu::TexturePointer& overlayTexture) = 0;
|
||||
virtual void setBackend(const gpu::BackendPointer& backend) final { _backend = backend; }
|
||||
virtual void submitFrame(const gpu::FramePointer& newFrame) = 0;
|
||||
|
||||
// Does the rendering surface have current focus?
|
||||
virtual bool hasFocus() const = 0;
|
||||
|
@ -212,6 +201,8 @@ signals:
|
|||
protected:
|
||||
void incrementPresentCount();
|
||||
|
||||
gpu::BackendPointer _backend;
|
||||
|
||||
private:
|
||||
std::atomic<uint32_t> _presentedFrameIndex;
|
||||
mutable std::mutex _paintDelayMutex;
|
||||
|
|
|
@ -53,9 +53,6 @@ void Engine::load() {
|
|||
}
|
||||
|
||||
void Engine::run() {
|
||||
// Sync GPU state before beginning to render
|
||||
_renderContext->args->_context->syncCache();
|
||||
|
||||
for (auto job : _jobs) {
|
||||
job.run(_sceneContext, _renderContext);
|
||||
}
|
||||
|
|
|
@ -11,12 +11,23 @@
|
|||
#ifdef _WIN32
|
||||
#if defined(NSIGHT_FOUND)
|
||||
#include "nvToolsExt.h"
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QThread>
|
||||
|
||||
extern bool isRenderThread();
|
||||
|
||||
ProfileRange::ProfileRange(const char *name) {
|
||||
if (!isRenderThread()) {
|
||||
return;
|
||||
}
|
||||
|
||||
nvtxRangePush(name);
|
||||
}
|
||||
|
||||
ProfileRange::ProfileRange(const char *name, uint32_t argbColor, uint64_t payload) {
|
||||
if (!isRenderThread()) {
|
||||
return;
|
||||
}
|
||||
|
||||
nvtxEventAttributes_t eventAttrib = {0};
|
||||
eventAttrib.version = NVTX_VERSION;
|
||||
|
@ -32,6 +43,9 @@ ProfileRange::ProfileRange(const char *name, uint32_t argbColor, uint64_t payloa
|
|||
}
|
||||
|
||||
ProfileRange::~ProfileRange() {
|
||||
if (!isRenderThread()) {
|
||||
return;
|
||||
}
|
||||
nvtxRangePop();
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include <ui/Menu.h>
|
||||
#include <MainWindow.h>
|
||||
#include <plugins/DisplayPlugin.h>
|
||||
|
||||
static PluginContainer* INSTANCE{ nullptr };
|
||||
|
||||
|
@ -159,3 +160,8 @@ void PluginContainer::setBoolSetting(const QString& settingName, bool value) {
|
|||
Setting::Handle<bool> settingValue(settingName, value);
|
||||
return settingValue.set(value);
|
||||
}
|
||||
|
||||
bool isRenderThread() {
|
||||
auto displayPlugin = PluginContainer::getInstance().getActiveDisplayPlugin();
|
||||
return displayPlugin && displayPlugin->isRenderThread();
|
||||
}
|
|
@ -58,8 +58,6 @@ public:
|
|||
virtual void showDisplayPluginsTools(bool show = true) = 0;
|
||||
virtual void requestReset() = 0;
|
||||
virtual bool makeRenderingContextCurrent() = 0;
|
||||
virtual void releaseSceneTexture(const gpu::TexturePointer& texture) = 0;
|
||||
virtual void releaseOverlayTexture(const gpu::TexturePointer& texture) = 0;
|
||||
virtual GLWidget* getPrimaryWidget() = 0;
|
||||
virtual MainWindow* getPrimaryWindow() = 0;
|
||||
virtual QOpenGLContext* getPrimaryContext() = 0;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
# See the accompanying file LICENSE or http:#www.apache.org/licenses/LICENSE-2.0.html
|
||||
#
|
||||
|
||||
if (FALSE)
|
||||
if (WIN32)
|
||||
|
||||
# we're using static GLEW, so define GLEW_STATIC
|
||||
|
@ -23,4 +24,5 @@ if (WIN32)
|
|||
target_link_libraries(${TARGET_NAME} ${LIBOVR_LIBRARIES})
|
||||
target_link_libraries(${TARGET_NAME} Winmm.lib)
|
||||
|
||||
endif()
|
||||
endif()
|
|
@ -8,6 +8,8 @@
|
|||
|
||||
# Windows doesn't need this, and building it currently make Linux unstable.
|
||||
# if (NOT WIN32)
|
||||
|
||||
if (FALSE)
|
||||
if (APPLE)
|
||||
|
||||
set(TARGET_NAME oculusLegacy)
|
||||
|
@ -26,3 +28,4 @@ if (APPLE)
|
|||
|
||||
|
||||
endif()
|
||||
endif()
|
|
@ -6,6 +6,7 @@
|
|||
# See the accompanying file LICENSE or http:#www.apache.org/licenses/LICENSE-2.0.html
|
||||
#
|
||||
|
||||
if (FALSE)
|
||||
if (WIN32)
|
||||
# we're using static GLEW, so define GLEW_STATIC
|
||||
add_definitions(-DGLEW_STATIC)
|
||||
|
@ -22,3 +23,4 @@ if (WIN32)
|
|||
target_include_directories(${TARGET_NAME} PRIVATE ${OPENVR_INCLUDE_DIRS})
|
||||
target_link_libraries(${TARGET_NAME} ${OPENVR_LIBRARIES})
|
||||
endif()
|
||||
endif()
|
|
@ -85,8 +85,6 @@ public:
|
|||
virtual void showDisplayPluginsTools(bool show) override {}
|
||||
virtual void requestReset() override {}
|
||||
virtual bool makeRenderingContextCurrent() override { return true; }
|
||||
virtual void releaseSceneTexture(const gpu::TexturePointer& texture) override {}
|
||||
virtual void releaseOverlayTexture(const gpu::TexturePointer& texture) override {}
|
||||
virtual GLWidget* getPrimaryWidget() override { return nullptr; }
|
||||
virtual MainWindow* getPrimaryWindow() override { return nullptr; }
|
||||
virtual QOpenGLContext* getPrimaryContext() override { return nullptr; }
|
||||
|
|
|
@ -93,7 +93,6 @@ void TestWindow::resizeWindow(const QSize& size) {
|
|||
}
|
||||
|
||||
void TestWindow::beginFrame() {
|
||||
_renderArgs->_context->syncCache();
|
||||
|
||||
#ifdef DEFERRED_LIGHTING
|
||||
|
||||
|
|
|
@ -30,7 +30,6 @@
|
|||
#include <AssetClient.h>
|
||||
|
||||
#include <gl/OffscreenGLCanvas.h>
|
||||
#include <gl/OglplusHelpers.h>
|
||||
#include <gl/GLHelpers.h>
|
||||
#include <gl/QOpenGLContextWrapper.h>
|
||||
#include <gl/QOpenGLDebugLoggerWrapper.h>
|
||||
|
@ -38,6 +37,7 @@
|
|||
#include <gpu/gl/GLBackend.h>
|
||||
#include <gpu/gl/GLFramebuffer.h>
|
||||
#include <gpu/gl/GLTexture.h>
|
||||
#include <gpu/StandardShaderLib.h>
|
||||
|
||||
#include <WebEntityItem.h>
|
||||
#include <OctreeUtils.h>
|
||||
|
@ -157,7 +157,118 @@ static QString toHumanSize(size_t size, size_t maxUnit = std::numeric_limits<siz
|
|||
return QString("%1 %2").arg(size).arg(SUFFIXES[suffixIndex]);
|
||||
}
|
||||
|
||||
const char* SRGB_TO_LINEAR_FRAG = R"SCRIBE(
|
||||
|
||||
uniform sampler2D colorMap;
|
||||
|
||||
in vec2 varTexCoord0;
|
||||
|
||||
out vec4 outFragColor;
|
||||
|
||||
void main(void) {
|
||||
outFragColor = vec4(pow(texture(colorMap, varTexCoord0).rgb, vec3(2.2)), 1.0);
|
||||
}
|
||||
)SCRIBE";
|
||||
|
||||
|
||||
class RenderThread : public GenericQueueThread<gpu::FramePointer> {
|
||||
using Parent = GenericQueueThread<gpu::FramePointer>;
|
||||
public:
|
||||
QOpenGLContextWrapper* _displayContext{ nullptr };
|
||||
QSurface* _displaySurface{ nullptr };
|
||||
gpu::PipelinePointer _presentPipeline;
|
||||
gpu::ContextPointer _gpuContext; // initialized during window creation
|
||||
std::atomic<size_t> _presentCount;
|
||||
QElapsedTimer _elapsed;
|
||||
std::atomic<uint16_t> _fps;
|
||||
RateCounter<200> _fpsCounter;
|
||||
std::mutex _mutex;
|
||||
std::shared_ptr<gpu::Backend> _backend;
|
||||
|
||||
|
||||
void initialize(QOpenGLContextWrapper* displayContext, QWindow* surface) {
|
||||
setObjectName("RenderThread");
|
||||
_displayContext = displayContext;
|
||||
_displaySurface = surface;
|
||||
_displayContext->makeCurrent(_displaySurface);
|
||||
// GPU library init
|
||||
gpu::Context::init<gpu::gl::GLBackend>();
|
||||
_gpuContext = std::make_shared<gpu::Context>();
|
||||
_backend = _gpuContext->getBackend();
|
||||
_displayContext->makeCurrent(_displaySurface);
|
||||
DependencyManager::get<DeferredLightingEffect>()->init();
|
||||
_displayContext->doneCurrent();
|
||||
Parent::initialize();
|
||||
if (isThreaded()) {
|
||||
_displayContext->moveToThread(thread());
|
||||
}
|
||||
}
|
||||
|
||||
void setup() override {
|
||||
_displayContext->makeCurrent(_displaySurface);
|
||||
glewExperimental = true;
|
||||
glewInit();
|
||||
glGetError();
|
||||
|
||||
{
|
||||
auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS();
|
||||
auto ps = gpu::Shader::createPixel(std::string(SRGB_TO_LINEAR_FRAG));
|
||||
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
|
||||
gpu::Shader::BindingSet slotBindings;
|
||||
gpu::Shader::makeProgram(*program, slotBindings);
|
||||
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||
_presentPipeline = gpu::Pipeline::create(program, state);
|
||||
}
|
||||
|
||||
//_textOverlay = new TextOverlay(glm::uvec2(800, 600));
|
||||
glViewport(0, 0, 800, 600);
|
||||
_elapsed.start();
|
||||
}
|
||||
|
||||
void shutdown() override {
|
||||
}
|
||||
|
||||
void renderFrame(gpu::FramePointer& frame) {
|
||||
++_presentCount;
|
||||
_displayContext->makeCurrent(_displaySurface);
|
||||
|
||||
if (frame && !frame->batches.empty()) {
|
||||
_backend->syncCache();
|
||||
_backend->setStereoState(frame->stereoState);
|
||||
for (auto& batch : frame->batches) {
|
||||
_backend->render(batch);
|
||||
}
|
||||
{
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
gpu::Batch presentBatch;
|
||||
presentBatch.setViewTransform(Transform());
|
||||
presentBatch.setFramebuffer(gpu::FramebufferPointer());
|
||||
presentBatch.setResourceTexture(0, frame->framebuffer->getRenderBuffer(0));
|
||||
presentBatch.setPipeline(_presentPipeline);
|
||||
presentBatch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
_backend->render(presentBatch);
|
||||
}
|
||||
}
|
||||
{
|
||||
//_textOverlay->render();
|
||||
}
|
||||
_displayContext->swapBuffers(_displaySurface);
|
||||
_fpsCounter.increment();
|
||||
static size_t _frameCount{ 0 };
|
||||
++_frameCount;
|
||||
if (_elapsed.elapsed() >= 500) {
|
||||
_fps = _fpsCounter.rate();
|
||||
_frameCount = 0;
|
||||
_elapsed.restart();
|
||||
}
|
||||
}
|
||||
|
||||
bool processQueueItems(const Queue& items) override {
|
||||
auto frame = items.last();
|
||||
renderFrame(frame);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// Create a simple OpenGL window that renders text in various ways
|
||||
class QTestWindow : public QWindow, public AbstractViewStateInterface {
|
||||
|
@ -185,6 +296,7 @@ protected:
|
|||
}
|
||||
|
||||
void postLambdaEvent(std::function<void()> f) override {}
|
||||
|
||||
qreal getDevicePixelRatio() override {
|
||||
return 1.0f;
|
||||
}
|
||||
|
@ -192,6 +304,7 @@ protected:
|
|||
render::ScenePointer getMain3DScene() override {
|
||||
return _main3DScene;
|
||||
}
|
||||
|
||||
render::EnginePointer getRenderEngine() override {
|
||||
return _renderEngine;
|
||||
}
|
||||
|
@ -221,12 +334,13 @@ public:
|
|||
}
|
||||
|
||||
QTestWindow() {
|
||||
_camera.movementSpeed = 50.0f;
|
||||
QThread::currentThread()->setPriority(QThread::HighestPriority);
|
||||
AbstractViewStateInterface::setInstance(this);
|
||||
_octree = DependencyManager::set<EntityTreeRenderer>(false, this, nullptr);
|
||||
_octree->init();
|
||||
// Prevent web entities from rendering
|
||||
REGISTER_ENTITY_TYPE_WITH_FACTORY(Web, WebEntityItem::factory)
|
||||
REGISTER_ENTITY_TYPE_WITH_FACTORY(Web, WebEntityItem::factory);
|
||||
|
||||
DependencyManager::set<ParentFinder>(_octree->getTree());
|
||||
getEntities()->setViewFrustum(_viewFrustum);
|
||||
|
@ -241,11 +355,12 @@ public:
|
|||
format.setOption(QSurfaceFormat::DebugContext);
|
||||
setFormat(format);
|
||||
|
||||
_context.setFormat(format);
|
||||
_context.create();
|
||||
resize(QSize(800, 600));
|
||||
show();
|
||||
makeCurrent();
|
||||
|
||||
_context.setFormat(format);
|
||||
_context.create();
|
||||
_context.makeCurrent(this);
|
||||
glewExperimental = true;
|
||||
glewInit();
|
||||
glGetError();
|
||||
|
@ -253,40 +368,24 @@ public:
|
|||
#ifdef Q_OS_WIN
|
||||
wglSwapIntervalEXT(0);
|
||||
#endif
|
||||
{
|
||||
makeCurrent();
|
||||
_quadProgram = loadDefaultShader();
|
||||
_plane = loadPlane(_quadProgram);
|
||||
_textOverlay = new TextOverlay(glm::uvec2(800, 600));
|
||||
glViewport(0, 0, 800, 600);
|
||||
}
|
||||
_context.doneCurrent();
|
||||
|
||||
_camera.movementSpeed = 50.0f;
|
||||
|
||||
|
||||
// GPU library init
|
||||
{
|
||||
_offscreenContext = new OffscreenGLCanvas();
|
||||
_offscreenContext->create(_context.getContext());
|
||||
_offscreenContext->makeCurrent();
|
||||
gpu::Context::init<gpu::gl::GLBackend>();
|
||||
_gpuContext = std::make_shared<gpu::Context>();
|
||||
}
|
||||
_initContext.create(_context.getContext());
|
||||
_renderThread.initialize(&_context, this);
|
||||
// FIXME use a wait condition
|
||||
QThread::msleep(1000);
|
||||
_renderThread.queueItem(gpu::FramePointer());
|
||||
_initContext.makeCurrent();
|
||||
// Render engine init
|
||||
_renderEngine->addJob<RenderShadowTask>("RenderShadowTask", _cullFunctor);
|
||||
_renderEngine->addJob<RenderDeferredTask>("RenderDeferredTask", _cullFunctor);
|
||||
_renderEngine->load();
|
||||
_renderEngine->registerScene(_main3DScene);
|
||||
|
||||
// Render engine library init
|
||||
{
|
||||
_offscreenContext->makeCurrent();
|
||||
DependencyManager::get<DeferredLightingEffect>()->init();
|
||||
_renderEngine->addJob<RenderShadowTask>("RenderShadowTask", _cullFunctor);
|
||||
_renderEngine->addJob<RenderDeferredTask>("RenderDeferredTask", _cullFunctor);
|
||||
_renderEngine->load();
|
||||
_renderEngine->registerScene(_main3DScene);
|
||||
}
|
||||
|
||||
reloadScene();
|
||||
restorePosition();
|
||||
|
||||
_elapsed.start();
|
||||
QTimer* timer = new QTimer(this);
|
||||
timer->setInterval(0);
|
||||
connect(timer, &QTimer::timeout, this, [this] {
|
||||
|
@ -298,8 +397,6 @@ public:
|
|||
|
||||
virtual ~QTestWindow() {
|
||||
ResourceManager::cleanup();
|
||||
try { _quadProgram.reset(); } catch (std::runtime_error&) {}
|
||||
try { _plane.reset(); } catch (std::runtime_error&) {}
|
||||
}
|
||||
|
||||
protected:
|
||||
|
@ -363,6 +460,7 @@ private:
|
|||
return (renderAccuracy > 0.0f);
|
||||
}
|
||||
|
||||
uint16_t _fps;
|
||||
void draw() {
|
||||
if (!_ready) {
|
||||
return;
|
||||
|
@ -370,17 +468,21 @@ private:
|
|||
if (!isVisible()) {
|
||||
return;
|
||||
}
|
||||
if (_renderCount.load() >= _renderThread._presentCount.load()) {
|
||||
return;
|
||||
}
|
||||
_renderCount = _renderThread._presentCount.load();
|
||||
update();
|
||||
|
||||
_offscreenContext->makeCurrent();
|
||||
|
||||
RenderArgs renderArgs(_gpuContext, _octree.data(), DEFAULT_OCTREE_SIZE_SCALE,
|
||||
RenderArgs renderArgs(_renderThread._gpuContext, _octree.data(), DEFAULT_OCTREE_SIZE_SCALE,
|
||||
0, RenderArgs::DEFAULT_RENDER_MODE,
|
||||
RenderArgs::MONO, RenderArgs::RENDER_DEBUG_NONE);
|
||||
|
||||
auto framebufferCache = DependencyManager::get<FramebufferCache>();
|
||||
QSize windowSize = size();
|
||||
framebufferCache->setFrameBufferSize(windowSize);
|
||||
|
||||
renderArgs._blitFramebuffer = framebufferCache->getFramebuffer();
|
||||
// Viewport is assigned to the size of the framebuffer
|
||||
renderArgs._viewport = ivec4(0, 0, windowSize.width(), windowSize.height());
|
||||
|
||||
|
@ -398,49 +500,12 @@ private:
|
|||
}
|
||||
|
||||
// Final framebuffer that will be handled to the display-plugin
|
||||
{
|
||||
auto finalFramebuffer = framebufferCache->getFramebuffer();
|
||||
renderArgs._blitFramebuffer = finalFramebuffer;
|
||||
}
|
||||
|
||||
_gpuContext->beginFrame(renderArgs._blitFramebuffer);
|
||||
gpu::doInBatch(renderArgs._context, [&](gpu::Batch& batch) {
|
||||
batch.resetStages();
|
||||
});
|
||||
render(&renderArgs);
|
||||
_gpuContext->endFrame();
|
||||
GLuint glTex;
|
||||
{
|
||||
auto gpuTex = renderArgs._blitFramebuffer->getRenderBuffer(0);
|
||||
glTex = gpu::Backend::getGPUObject<gpu::gl::GLTexture>(*gpuTex)->_id;
|
||||
}
|
||||
|
||||
makeCurrent();
|
||||
{
|
||||
glBindTexture(GL_TEXTURE_2D, glTex);
|
||||
_quadProgram->Use();
|
||||
_plane->Use();
|
||||
_plane->Draw();
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
{
|
||||
//_textOverlay->render();
|
||||
}
|
||||
|
||||
_context.swapBuffers(this);
|
||||
|
||||
_offscreenContext->makeCurrent();
|
||||
framebufferCache->releaseFramebuffer(renderArgs._blitFramebuffer);
|
||||
renderArgs._blitFramebuffer.reset();
|
||||
_fpsCounter.increment();
|
||||
static size_t _frameCount { 0 };
|
||||
++_frameCount;
|
||||
if (_elapsed.elapsed() >= 500) {
|
||||
_fps = _fpsCounter.rate();
|
||||
if (_fps != _renderThread._fps) {
|
||||
_fps = _renderThread._fps;
|
||||
updateText();
|
||||
_frameCount = 0;
|
||||
_elapsed.restart();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -467,7 +532,12 @@ private:
|
|||
|
||||
|
||||
void updateText() {
|
||||
//qDebug() << "FPS " << fps.rate();
|
||||
setTitle(QString("FPS %1 Culling %2 TextureMemory GPU %3 CPU %4")
|
||||
.arg(_fps).arg(_cullingEnabled)
|
||||
.arg(toHumanSize(gpu::Context::getTextureGPUMemoryUsage(), 2))
|
||||
.arg(toHumanSize(gpu::Texture::getTextureCPUMemoryUsage(), 2)));
|
||||
|
||||
#if 0
|
||||
{
|
||||
_textBlocks.erase(TextBlock::Info);
|
||||
auto& infoTextBlock = _textBlocks[TextBlock::Info];
|
||||
|
@ -475,13 +545,10 @@ private:
|
|||
infoTextBlock.push_back({ vec2(100, 10), std::to_string((uint32_t)_fps), TextOverlay::alignLeft });
|
||||
infoTextBlock.push_back({ vec2(98, 30), "Culling: ", TextOverlay::alignRight });
|
||||
infoTextBlock.push_back({ vec2(100, 30), _cullingEnabled ? "Enabled" : "Disabled", TextOverlay::alignLeft });
|
||||
|
||||
setTitle(QString("FPS %1 Culling %2 TextureMemory GPU %3 CPU %4")
|
||||
.arg(_fps).arg(_cullingEnabled)
|
||||
.arg(toHumanSize(gpu::Context::getTextureGPUMemoryUsage(), 2))
|
||||
.arg(toHumanSize(gpu::Texture::getTextureCPUMemoryUsage(), 2)));
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
_textOverlay->beginTextUpdate();
|
||||
for (const auto& e : _textBlocks) {
|
||||
for (const auto& b : e.second) {
|
||||
|
@ -489,6 +556,7 @@ private:
|
|||
}
|
||||
}
|
||||
_textOverlay->endTextUpdate();
|
||||
#endif
|
||||
}
|
||||
|
||||
void update() {
|
||||
|
@ -525,6 +593,11 @@ private:
|
|||
}
|
||||
|
||||
void render(RenderArgs* renderArgs) {
|
||||
auto& gpuContext = renderArgs->_context;
|
||||
gpuContext->beginFrame();
|
||||
gpu::doInBatch(gpuContext, [&](gpu::Batch& batch) {
|
||||
batch.resetStages();
|
||||
});
|
||||
PROFILE_RANGE(__FUNCTION__);
|
||||
PerformanceTimer perfTimer("draw");
|
||||
// The pending changes collecting the changes here
|
||||
|
@ -544,6 +617,14 @@ private:
|
|||
// Before the deferred pass, let's try to use the render engine
|
||||
_renderEngine->run();
|
||||
}
|
||||
auto frame = gpuContext->endFrame();
|
||||
frame->framebuffer = renderArgs->_blitFramebuffer;
|
||||
frame->framebufferRecycler = [](const gpu::FramebufferPointer& framebuffer){
|
||||
DependencyManager::get<FramebufferCache>()->releaseFramebuffer(framebuffer);
|
||||
};
|
||||
_renderThread.queueItem(frame);
|
||||
|
||||
|
||||
}
|
||||
|
||||
bool makeCurrent() {
|
||||
|
@ -558,9 +639,8 @@ private:
|
|||
if (!_ready) {
|
||||
return;
|
||||
}
|
||||
_textOverlay->resize(toGlm(_size));
|
||||
makeCurrent();
|
||||
glViewport(0, 0, size.width(), size.height());
|
||||
//_textOverlay->resize(toGlm(_size));
|
||||
//glViewport(0, 0, size.width(), size.height());
|
||||
}
|
||||
|
||||
void parsePath(const QString& viewpointString) {
|
||||
|
@ -704,32 +784,29 @@ private:
|
|||
|
||||
std::map<TextBlock, std::list<TextElement>> _textBlocks;
|
||||
|
||||
gpu::ContextPointer _gpuContext; // initialized during window creation
|
||||
render::EnginePointer _renderEngine { new render::Engine() };
|
||||
render::ScenePointer _main3DScene { new render::Scene(glm::vec3(-0.5f * (float)TREE_SCALE), (float)TREE_SCALE) };
|
||||
OffscreenGLCanvas* _offscreenContext { nullptr };
|
||||
QOpenGLContextWrapper _context;
|
||||
QSize _size;
|
||||
RateCounter<200> _fpsCounter;
|
||||
QSettings _settings;
|
||||
|
||||
ProgramPtr _quadProgram;
|
||||
ShapeWrapperPtr _plane;
|
||||
|
||||
std::atomic<size_t> _renderCount;
|
||||
OffscreenGLCanvas _initContext;
|
||||
RenderThread _renderThread;
|
||||
QWindowCamera _camera;
|
||||
ViewFrustum _viewFrustum; // current state of view frustum, perspective, orientation, etc.
|
||||
ViewFrustum _shadowViewFrustum; // current state of view frustum, perspective, orientation, etc.
|
||||
model::SunSkyStage _sunSkyStage;
|
||||
model::LightPointer _globalLight { std::make_shared<model::Light>() };
|
||||
QElapsedTimer _elapsed;
|
||||
bool _ready { false };
|
||||
float _fps { 0 };
|
||||
TextOverlay* _textOverlay;
|
||||
bool _cullingEnabled { true };
|
||||
//TextOverlay* _textOverlay;
|
||||
static bool _cullingEnabled;
|
||||
bool _stereoEnabled { false };
|
||||
QSharedPointer<EntityTreeRenderer> _octree;
|
||||
};
|
||||
|
||||
bool QTestWindow::_cullingEnabled = false;
|
||||
|
||||
void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) {
|
||||
if (!message.isEmpty()) {
|
||||
#ifdef Q_OS_WIN
|
||||
|
|
Loading…
Reference in a new issue