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