From d98abbc7df5d05b72a29a5015c81445df4f89499 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sun, 3 Apr 2016 01:04:05 -0700 Subject: [PATCH 1/6] First pass at threaded texture transfers --- interface/src/Application.cpp | 3 + libraries/gl/src/gl/GLHelpers.cpp | 4 +- libraries/gl/src/gl/OffscreenGLCanvas.cpp | 5 + libraries/gl/src/gl/OffscreenGLCanvas.h | 1 + libraries/gl/src/gl/OglplusHelpers.cpp | 40 +- libraries/gl/src/gl/OglplusHelpers.h | 3 +- libraries/gl/src/gl/QOpenGLContextWrapper.cpp | 3 + libraries/gl/src/gl/QOpenGLContextWrapper.h | 3 +- libraries/gpu/src/gpu/GLBackend.cpp | 1 + libraries/gpu/src/gpu/GLBackend.h | 65 ++- libraries/gpu/src/gpu/GLBackendOutput.cpp | 4 +- libraries/gpu/src/gpu/GLBackendPipeline.cpp | 2 +- libraries/gpu/src/gpu/GLBackendTexture.cpp | 441 +++++++++--------- .../gpu/src/gpu/GLBackendTextureTransfer.cpp | 109 +++++ .../gpu/src/gpu/GLBackendTextureTransfer.h | 61 +++ libraries/gpu/src/gpu/GLTexelFormat.h | 3 + libraries/shared/src/GenericThread.cpp | 19 +- libraries/shared/src/GenericThread.h | 7 +- 18 files changed, 540 insertions(+), 234 deletions(-) create mode 100644 libraries/gpu/src/gpu/GLBackendTextureTransfer.cpp create mode 100644 libraries/gpu/src/gpu/GLBackendTextureTransfer.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0172b3ce3a..80173b7d9d 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1255,6 +1255,9 @@ void Application::initializeGL() { // Where the gpuContext is initialized and where the TRUE Backend is created and assigned gpu::Context::init(); _gpuContext = std::make_shared(); + // The gpu context can make child contexts for transfers, so + // we need to restore primary rendering context + _offscreenContext->makeCurrent(); initDisplay(); qCDebug(interfaceapp, "Initialized Display."); diff --git a/libraries/gl/src/gl/GLHelpers.cpp b/libraries/gl/src/gl/GLHelpers.cpp index 6ad7f816b8..c9de3ccd90 100644 --- a/libraries/gl/src/gl/GLHelpers.cpp +++ b/libraries/gl/src/gl/GLHelpers.cpp @@ -12,7 +12,7 @@ const QSurfaceFormat& getDefaultOpenGLSurfaceFormat() { // Qt Quick may need a depth and stencil buffer. Always make sure these are available. format.setDepthBufferSize(DEFAULT_GL_DEPTH_BUFFER_BITS); format.setStencilBufferSize(DEFAULT_GL_STENCIL_BUFFER_BITS); - format.setVersion(4, 1); + format.setVersion(4, 5); #ifdef DEBUG format.setOption(QSurfaceFormat::DebugContext); #endif @@ -27,7 +27,7 @@ const QGLFormat& getDefaultGLFormat() { static QGLFormat glFormat; static std::once_flag once; std::call_once(once, [] { - glFormat.setVersion(4, 1); + glFormat.setVersion(4, 5); glFormat.setProfile(QGLFormat::CoreProfile); // Requires >=Qt-4.8.0 glFormat.setSampleBuffers(false); glFormat.setDepth(false); diff --git a/libraries/gl/src/gl/OffscreenGLCanvas.cpp b/libraries/gl/src/gl/OffscreenGLCanvas.cpp index 8e5579f90b..90ff369cd6 100644 --- a/libraries/gl/src/gl/OffscreenGLCanvas.cpp +++ b/libraries/gl/src/gl/OffscreenGLCanvas.cpp @@ -83,3 +83,8 @@ void OffscreenGLCanvas::doneCurrent() { QObject* OffscreenGLCanvas::getContextObject() { return _context; } + +void OffscreenGLCanvas::moveToThreadWithContext(QThread* thread) { + moveToThread(thread); + _context->moveToThread(thread); +} \ No newline at end of file diff --git a/libraries/gl/src/gl/OffscreenGLCanvas.h b/libraries/gl/src/gl/OffscreenGLCanvas.h index 9858c7cefa..387804bf56 100644 --- a/libraries/gl/src/gl/OffscreenGLCanvas.h +++ b/libraries/gl/src/gl/OffscreenGLCanvas.h @@ -26,6 +26,7 @@ public: bool create(QOpenGLContext* sharedContext = nullptr); bool makeCurrent(); void doneCurrent(); + void moveToThreadWithContext(QThread* thread); QOpenGLContext* getContext() { return _context; } diff --git a/libraries/gl/src/gl/OglplusHelpers.cpp b/libraries/gl/src/gl/OglplusHelpers.cpp index 1dd7068448..11c4f2fe3d 100644 --- a/libraries/gl/src/gl/OglplusHelpers.cpp +++ b/libraries/gl/src/gl/OglplusHelpers.cpp @@ -6,8 +6,10 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "OglplusHelpers.h" -#include + #include +#include +#include using namespace oglplus; using namespace oglplus::shapes; @@ -20,11 +22,13 @@ uniform mat4 mvp = mat4(1); in vec3 Position; in vec2 TexCoord; +out vec3 vPosition; out vec2 vTexCoord; void main() { gl_Position = mvp * vec4(Position, 1); - vTexCoord = TexCoord ; + vTexCoord = TexCoord; + vPosition = Position; } )VS"; @@ -35,7 +39,9 @@ static const char * SIMPLE_TEXTURED_FS = R"FS(#version 410 core uniform sampler2D sampler; uniform float alpha = 1.0; +in vec3 vPosition; in vec2 vTexCoord; + out vec4 FragColor; void main() { @@ -47,12 +53,38 @@ void main() { )FS"; +static const char * SIMPLE_TEXTURED_CUBEMAP_FS = R"FS(#version 410 core +#pragma line __LINE__ + +uniform samplerCube sampler; +uniform float alpha = 1.0; + +in vec3 vPosition; +in vec3 vTexCoord; + +out vec4 FragColor; + +void main() { + + FragColor = texture(sampler, vPosition); + FragColor.a *= alpha; +} + +)FS"; + + ProgramPtr loadDefaultShader() { ProgramPtr result; compileProgram(result, SIMPLE_TEXTURED_VS, SIMPLE_TEXTURED_FS); return result; } +ProgramPtr loadCubemapShader() { + ProgramPtr result; + compileProgram(result, SIMPLE_TEXTURED_VS, SIMPLE_TEXTURED_CUBEMAP_FS); + return result; +} + void compileProgram(ProgramPtr & result, const std::string& vs, const std::string& fs) { using namespace oglplus; try { @@ -93,6 +125,10 @@ ShapeWrapperPtr loadPlane(ProgramPtr program, float aspect) { ); } +ShapeWrapperPtr loadSkybox(ProgramPtr program) { + return ShapeWrapperPtr(new shapes::ShapeWrapper({ { "Position" } }, shapes::SkyBox(), *program)); +} + // Return a point's cartesian coordinates on a sphere from pitch and yaw static glm::vec3 getPoint(float yaw, float pitch) { return glm::vec3(glm::cos(-pitch) * (-glm::sin(yaw)), diff --git a/libraries/gl/src/gl/OglplusHelpers.h b/libraries/gl/src/gl/OglplusHelpers.h index 4734b8b213..b599e8270d 100644 --- a/libraries/gl/src/gl/OglplusHelpers.h +++ b/libraries/gl/src/gl/OglplusHelpers.h @@ -37,7 +37,6 @@ #include #include #include -#include #ifdef _WIN32 #pragma warning(pop) @@ -55,7 +54,9 @@ using ProgramPtr = std::shared_ptr; using Mat4Uniform = oglplus::Uniform; ProgramPtr loadDefaultShader(); +ProgramPtr loadCubemapShader(); void compileProgram(ProgramPtr & result, const std::string& vs, const std::string& fs); +ShapeWrapperPtr loadSkybox(ProgramPtr program); ShapeWrapperPtr loadPlane(ProgramPtr program, float aspect = 1.0f); ShapeWrapperPtr loadSphereSection(ProgramPtr program, float fov = PI / 3.0f * 2.0f, float aspect = 16.0f / 9.0f, int slices = 32, int stacks = 32); diff --git a/libraries/gl/src/gl/QOpenGLContextWrapper.cpp b/libraries/gl/src/gl/QOpenGLContextWrapper.cpp index 6397d30e13..185fdaf7f4 100644 --- a/libraries/gl/src/gl/QOpenGLContextWrapper.cpp +++ b/libraries/gl/src/gl/QOpenGLContextWrapper.cpp @@ -13,6 +13,9 @@ #include +QOpenGLContext* QOpenGLContextWrapper::currentContext() { + return QOpenGLContext::currentContext(); +} QOpenGLContextWrapper::QOpenGLContextWrapper() : _context(new QOpenGLContext) diff --git a/libraries/gl/src/gl/QOpenGLContextWrapper.h b/libraries/gl/src/gl/QOpenGLContextWrapper.h index b736253213..09f1d67280 100644 --- a/libraries/gl/src/gl/QOpenGLContextWrapper.h +++ b/libraries/gl/src/gl/QOpenGLContextWrapper.h @@ -19,7 +19,6 @@ class QSurfaceFormat; class QOpenGLContextWrapper { public: QOpenGLContextWrapper(); - void setFormat(const QSurfaceFormat& format); bool create(); void swapBuffers(QSurface* surface); @@ -27,6 +26,8 @@ public: void doneCurrent(); void setShareContext(QOpenGLContext* otherContext); + static QOpenGLContext* currentContext(); + QOpenGLContext* getContext() { return _context; } diff --git a/libraries/gpu/src/gpu/GLBackend.cpp b/libraries/gpu/src/gpu/GLBackend.cpp index b5b6437ed8..f51448f8fd 100644 --- a/libraries/gpu/src/gpu/GLBackend.cpp +++ b/libraries/gpu/src/gpu/GLBackend.cpp @@ -125,6 +125,7 @@ GLBackend::GLBackend() { glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &_uboAlignment); initInput(); initTransform(); + initTextureTransferHelper(); } GLBackend::~GLBackend() { diff --git a/libraries/gpu/src/gpu/GLBackend.h b/libraries/gpu/src/gpu/GLBackend.h index f5abacd279..4f22a68631 100644 --- a/libraries/gpu/src/gpu/GLBackend.h +++ b/libraries/gpu/src/gpu/GLBackend.h @@ -24,6 +24,8 @@ namespace gpu { +class GLTextureTransferHelper; + class GLBackend : public Backend { // Context Backend static interface required @@ -35,7 +37,6 @@ class GLBackend : public Backend { explicit GLBackend(bool syncCache); GLBackend(); public: - virtual ~GLBackend(); virtual void render(Batch& batch); @@ -75,25 +76,63 @@ public: class GLTexture : public GPUObject { public: - Stamp _storageStamp; - Stamp _contentStamp; - GLuint _texture; - GLenum _target; + const Stamp _storageStamp; + Stamp _contentStamp { 0 }; + const GLuint _texture; + const GLenum _target; - GLTexture(); + GLTexture(const gpu::Texture& gpuTexture); ~GLTexture(); - void setSize(GLuint size); GLuint size() const { return _size; } + enum SyncState { + // The texture is currently undergoing no processing, although it's content + // may be out of date, or it's storage may be invalid relative to the + // owning GPU texture + Idle, + // The texture has been queued for transfer to the GPU + Pending, + // The texture has been transferred to the GPU, but is awaiting + // any post transfer operations that may need to occur on the + // primary rendering thread + Transferred, + }; + + void setSyncState(SyncState syncState) { _syncState = syncState; } + SyncState getSyncState() const { return _syncState; } + + // Is the storage out of date relative to the gpu texture? + bool invalid() const; + + // Is the content out of date relative to the gpu texture? + bool outdated() const; + + // Is the texture in a state where it can be rendered with no work? + bool ready() const; + + // Move the image bits from the CPU to the GPU + void transfer() const; + + // Execute any post-move operations that must occur only on the main thread + void postTransfer(); + + static const size_t CUBE_NUM_FACES = 6; + static const GLenum CUBE_FACE_LAYOUT[6]; + private: - GLuint _size; + void transferMip(GLenum target, const Texture::PixelsPointer& mip) const; + + const GLuint _size; + // The owning texture + const Texture& _gpuTexture; + std::atomic _syncState { SyncState::Idle }; }; - static GLTexture* syncGPUObject(const Texture& texture); + static GLTexture* syncGPUObject(const TexturePointer& texture); static GLuint getTextureID(const TexturePointer& texture, bool sync = true); // very specific for now - static void syncSampler(const Sampler& sampler, Texture::Type type, GLTexture* object); + static void syncSampler(const Sampler& sampler, Texture::Type type, const GLTexture* object); class GLShader : public GPUObject { public: @@ -241,6 +280,11 @@ protected: void renderPassTransfer(Batch& batch); void renderPassDraw(Batch& batch); + void initTextureTransferHelper(); + static void transferGPUObject(const TexturePointer& texture); + + static std::shared_ptr _textureTransferHelper; + // Draw Stage void do_draw(Batch& batch, size_t paramOffset); void do_drawIndexed(Batch& batch, size_t paramOffset); @@ -484,6 +528,7 @@ protected: typedef void (GLBackend::*CommandCall)(Batch&, size_t); static CommandCall _commandCalls[Batch::NUM_COMMANDS]; + }; }; diff --git a/libraries/gpu/src/gpu/GLBackendOutput.cpp b/libraries/gpu/src/gpu/GLBackendOutput.cpp index 37a10e670b..4f714fb53d 100755 --- a/libraries/gpu/src/gpu/GLBackendOutput.cpp +++ b/libraries/gpu/src/gpu/GLBackendOutput.cpp @@ -83,7 +83,7 @@ GLBackend::GLFramebuffer* GLBackend::syncGPUObject(const Framebuffer& framebuffe for (auto& b : framebuffer.getRenderBuffers()) { surface = b._texture; if (surface) { - gltexture = GLBackend::syncGPUObject(*surface); + gltexture = GLBackend::syncGPUObject(surface); } else { gltexture = nullptr; } @@ -123,7 +123,7 @@ GLBackend::GLFramebuffer* GLBackend::syncGPUObject(const Framebuffer& framebuffe if (framebuffer.getDepthStamp() != object->_depthStamp) { auto surface = framebuffer.getDepthStencilBuffer(); if (framebuffer.hasDepthStencil() && surface) { - gltexture = GLBackend::syncGPUObject(*surface); + gltexture = GLBackend::syncGPUObject(surface); } if (gltexture) { diff --git a/libraries/gpu/src/gpu/GLBackendPipeline.cpp b/libraries/gpu/src/gpu/GLBackendPipeline.cpp index 046f1ff0e5..10845d1129 100755 --- a/libraries/gpu/src/gpu/GLBackendPipeline.cpp +++ b/libraries/gpu/src/gpu/GLBackendPipeline.cpp @@ -255,7 +255,7 @@ void GLBackend::do_setResourceTexture(Batch& batch, size_t paramOffset) { _stats._RSNumTextureBounded++; // Always make sure the GLObject is in sync - GLTexture* object = GLBackend::syncGPUObject(*resourceTexture); + GLTexture* object = GLBackend::syncGPUObject(resourceTexture); if (object) { GLuint to = object->_texture; GLuint target = object->_target; diff --git a/libraries/gpu/src/gpu/GLBackendTexture.cpp b/libraries/gpu/src/gpu/GLBackendTexture.cpp index 242b9100e1..92123a68be 100755 --- a/libraries/gpu/src/gpu/GLBackendTexture.cpp +++ b/libraries/gpu/src/gpu/GLBackendTexture.cpp @@ -9,19 +9,81 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "GPULogging.h" + +#include + #include "GLBackendShared.h" #include "GLTexelFormat.h" +#include "GLBackendTextureTransfer.h" using namespace gpu; -GLBackend::GLTexture::GLTexture() : - _storageStamp(0), - _contentStamp(0), - _texture(0), - _target(GL_TEXTURE_2D), - _size(0) +GLenum gpuToGLTextureType(const Texture& texture) { + // If we get here, we need to allocate and or update the content of the texture + // or it's already being transferred + switch (texture.getType()) { + case Texture::TEX_2D: + return GL_TEXTURE_2D; + break; + + case Texture::TEX_CUBE: + return GL_TEXTURE_CUBE_MAP; + break; + + default: + qFatal("Unsupported texture type"); + } + Q_UNREACHABLE(); + return GL_TEXTURE_2D; +} + +GLuint allocateSingleTexture() { + GLuint result; + glGenTextures(1, &result); + return result; +} + +const GLenum GLBackend::GLTexture::CUBE_FACE_LAYOUT[6] = { + GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, + GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, + GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z +}; + +// Create the texture and allocate storage +GLBackend::GLTexture::GLTexture(const Texture& texture) : + _storageStamp(texture.getStamp()), _texture(allocateSingleTexture()), + _target(gpuToGLTextureType(texture)), _size((GLuint)texture.getSize()), _gpuTexture(texture) { Backend::incrementTextureGPUCount(); + Backend::updateTextureGPUMemoryUsage(0, _size); + Backend::setGPUObject(texture, this); + + GLsizei width = texture.getWidth(); + GLsizei height = texture.getHeight(); + GLsizei levels = 1; + if (texture.maxMip() > 0) { + if (texture.isAutogenerateMips()) { + while ((width | height) >> levels) { + ++levels; + } + } + levels = std::max(1, std::min(texture.maxMip() + 1, levels)); + } + + GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(texture.getTexelFormat()); + withPreservedTexture(_target, [&] { + glBindTexture(_target, _texture); + (void)CHECK_GL_ERROR(); + // GO through the process of allocating the correct storage + if (GLEW_VERSION_4_2) { + glTexStorage2D(_target, levels, texelFormat.internalFormat, width, height); + } else { + glTexImage2D(_target, 0, texelFormat.internalFormat, width, height, 0, texelFormat.format, texelFormat.type, 0); + } + (void)CHECK_GL_ERROR(); + syncSampler(texture.getSampler(), texture.getType(), this); + (void)CHECK_GL_ERROR(); + }); } GLBackend::GLTexture::~GLTexture() { @@ -32,199 +94,164 @@ GLBackend::GLTexture::~GLTexture() { Backend::decrementTextureGPUCount(); } -void GLBackend::GLTexture::setSize(GLuint size) { - Backend::updateTextureGPUMemoryUsage(_size, size); - _size = size; +bool GLBackend::GLTexture::invalid() const { + return _storageStamp < _gpuTexture.getStamp(); } -GLBackend::GLTexture* GLBackend::syncGPUObject(const Texture& texture) { - GLTexture* object = Backend::getGPUObject(texture); +bool GLBackend::GLTexture::outdated() const { + return _contentStamp < _gpuTexture.getDataStamp(); +} - // If GPU object already created and in sync - bool needUpdate = false; - if (object && (object->_storageStamp == texture.getStamp())) { - // If gpu object info is in sync with sysmem version - if (object->_contentStamp >= texture.getDataStamp()) { - // Then all good, GPU object is ready to be used - return object; - } else { - // Need to update the content of the GPU object from the source sysmem of the texture - needUpdate = true; - } - } else if (!texture.isDefined()) { +bool GLBackend::GLTexture::ready() const { + // If we have an invalid texture, we're never ready + if (invalid()) { + return false; + } + + // If we're out of date, but the transfer is in progress, report ready + // as a special case + auto syncState = _syncState.load(); + + if (outdated()) { + return Pending == syncState; + } + + return Idle == syncState; +} + +//#define USE_PBO + +// Move content bits from the CPU to the GPU for a given mip / face +void GLBackend::GLTexture::transferMip(GLenum target, const Texture::PixelsPointer& mip) const { + GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuTexture.getTexelFormat(), mip->getFormat()); +#ifdef USE_PBO + GLuint pixelBufferID; + glGenBuffers(1, &pixelBufferID); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pixelBufferID); + //if (GLEW_VERSION_4_4) { + // glBufferStorage(GL_PIXEL_UNPACK_BUFFER, mip->getSize(), nullptr, GL_STREAM_DRAW); + //} else { + glBufferData(GL_PIXEL_UNPACK_BUFFER, mip->getSize(), nullptr, GL_STREAM_DRAW); + //} + void* mappedBuffer = glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY); + memcpy(mappedBuffer, mip->readData(), mip->getSize()); + //// use while PBO is still bound, assumes GL_TEXTURE_2D and offset 0 + glTexSubImage2D(target, 0, 0, 0, _gpuTexture.getWidth(), _gpuTexture.getHeight(), texelFormat.format, texelFormat.type, 0); + glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + glDeleteBuffers(1, &pixelBufferID); +#else + //glTexImage2D(target, 0, internalFormat, texture.getWidth(), texture.getHeight(), 0, texelFormat.format, texelFormat.type, bytes); + glTexSubImage2D(target, 0, 0, 0, _gpuTexture.getWidth(), _gpuTexture.getHeight(), texelFormat.format, texelFormat.type, mip->readData()); + (void)CHECK_GL_ERROR(); +#endif +} + +// Move content bits from the CPU to the GPU +void GLBackend::GLTexture::transfer() const { + PROFILE_RANGE(__FUNCTION__); + qDebug() << "Transferring texture: " << _texture; + // Need to update the content of the GPU object from the source sysmem of the texture + if (_contentStamp >= _gpuTexture.getDataStamp()) { + return; + } + + glBindTexture(_target, _texture); + // GO through the process of allocating the correct storage and/or update the content + switch (_gpuTexture.getType()) { + case Texture::TEX_2D: + if (_gpuTexture.isStoredMipFaceAvailable(0)) { + transferMip(GL_TEXTURE_2D, _gpuTexture.accessStoredMipFace(0)); + } + break; + + case Texture::TEX_CUBE: + // transfer pixels from each faces + for (int f = 0; f < CUBE_NUM_FACES; f++) { + if (_gpuTexture.isStoredMipFaceAvailable(0, f)) { + transferMip(CUBE_FACE_LAYOUT[f], _gpuTexture.accessStoredMipFace(0, f)); + } + } + break; + + default: + qCWarning(gpulogging) << __FUNCTION__ << " case for Texture Type " << _gpuTexture.getType() << " not supported"; + break; + } + + if (_gpuTexture.isAutogenerateMips()) { + glGenerateMipmap(_target); + (void)CHECK_GL_ERROR(); + } +} + +// Do any post-transfer operations that might be required on the main context / rendering thread +void GLBackend::GLTexture::postTransfer() { + setSyncState(GLTexture::Idle); + switch (_gpuTexture.getType()) { + case Texture::TEX_2D: + // At this point the mip piels have been loaded, we can notify + _gpuTexture.notifyMipFaceGPULoaded(0, 0); + break; + + case Texture::TEX_CUBE: + for (uint8_t f = 0; f < CUBE_NUM_FACES; ++f) { + // At this point the mip piels have been loaded, we can notify + _gpuTexture.notifyMipFaceGPULoaded(0, f); + } + break; + + default: + qCWarning(gpulogging) << __FUNCTION__ << " case for Texture Type " << _gpuTexture.getType() << " not supported"; + break; + } +} + +GLBackend::GLTexture* GLBackend::syncGPUObject(const TexturePointer& texturePointer) { + const Texture& texture = *texturePointer; + if (!texture.isDefined()) { // NO texture definition yet so let's avoid thinking return nullptr; } + // If the object hasn't been created, or the object definition is out of date, drop and re-create + GLTexture* object = Backend::getGPUObject(texture); + if (object && object->ready()) { + return object; + } + + // Object isn't ready, check what we need to do... + + // Create the texture if need be (force re-creation if the storage stamp changes + // for easier use of immutable storage) + if (!object || object->invalid()) { + // This automatically destroys the old texture + object = new GLTexture(texture); + } + // need to have a gpu object? - if (!object) { - object = new GLTexture(); - glGenTextures(1, &object->_texture); - (void) CHECK_GL_ERROR(); - Backend::setGPUObject(texture, object); + if (texture.getNumSlices() != 1) { + return object; } - // GO through the process of allocating the correct storage and/or update the content - switch (texture.getType()) { - case Texture::TEX_2D: { - if (texture.getNumSlices() == 1) { - GLint boundTex = -1; - glGetIntegerv(GL_TEXTURE_BINDING_2D, &boundTex); - glBindTexture(GL_TEXTURE_2D, object->_texture); - - if (needUpdate) { - if (texture.isStoredMipFaceAvailable(0)) { - Texture::PixelsPointer mip = texture.accessStoredMipFace(0); - const GLvoid* bytes = mip->readData(); - Element srcFormat = mip->getFormat(); - - GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(texture.getTexelFormat(), srcFormat); - - glBindTexture(GL_TEXTURE_2D, object->_texture); - glTexSubImage2D(GL_TEXTURE_2D, 0, - texelFormat.internalFormat, texture.getWidth(), texture.getHeight(), 0, - texelFormat.format, texelFormat.type, bytes); - - if (texture.isAutogenerateMips()) { - glGenerateMipmap(GL_TEXTURE_2D); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - } - - object->_target = GL_TEXTURE_2D; - - syncSampler(texture.getSampler(), texture.getType(), object); - - - // At this point the mip piels have been loaded, we can notify - texture.notifyMipFaceGPULoaded(0, 0); - - object->_contentStamp = texture.getDataStamp(); - } - } else { - const GLvoid* bytes = 0; - Element srcFormat = texture.getTexelFormat(); - if (texture.isStoredMipFaceAvailable(0)) { - Texture::PixelsPointer mip = texture.accessStoredMipFace(0); - - bytes = mip->readData(); - srcFormat = mip->getFormat(); - - object->_contentStamp = texture.getDataStamp(); - } - - GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(texture.getTexelFormat(), srcFormat); - - glTexImage2D(GL_TEXTURE_2D, 0, - texelFormat.internalFormat, texture.getWidth(), texture.getHeight(), 0, - texelFormat.format, texelFormat.type, bytes); - - if (bytes && texture.isAutogenerateMips()) { - glGenerateMipmap(GL_TEXTURE_2D); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - } - object->_target = GL_TEXTURE_2D; - - syncSampler(texture.getSampler(), texture.getType(), object); - - // At this point the mip pixels have been loaded, we can notify - texture.notifyMipFaceGPULoaded(0, 0); - - object->_storageStamp = texture.getStamp(); - object->_contentStamp = texture.getDataStamp(); - object->setSize((GLuint)texture.getSize()); - } - - glBindTexture(GL_TEXTURE_2D, boundTex); - } - break; + // Object might be outdated, if so, start the transfer + // (outdated objects that are already in transfer will have reported 'true' for ready() + if (object->outdated()) { + _textureTransferHelper->transferTexture(texturePointer); } - case Texture::TEX_CUBE: { - if (texture.getNumSlices() == 1) { - GLint boundTex = -1; - glGetIntegerv(GL_TEXTURE_BINDING_CUBE_MAP, &boundTex); - glBindTexture(GL_TEXTURE_CUBE_MAP, object->_texture); - const int NUM_FACES = 6; - const GLenum FACE_LAYOUT[] = { - GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, - GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, - GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z }; - if (needUpdate) { - glBindTexture(GL_TEXTURE_CUBE_MAP, object->_texture); - // transfer pixels from each faces - for (int f = 0; f < NUM_FACES; f++) { - if (texture.isStoredMipFaceAvailable(0, f)) { - Texture::PixelsPointer mipFace = texture.accessStoredMipFace(0, f); - Element srcFormat = mipFace->getFormat(); - GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(texture.getTexelFormat(), srcFormat); - - glTexSubImage2D(FACE_LAYOUT[f], 0, texelFormat.internalFormat, texture.getWidth(), texture.getWidth(), 0, - texelFormat.format, texelFormat.type, (GLvoid*) (mipFace->readData())); - - // At this point the mip pixels have been loaded, we can notify - texture.notifyMipFaceGPULoaded(0, f); - } - } - - if (texture.isAutogenerateMips()) { - glGenerateMipmap(GL_TEXTURE_CUBE_MAP); - glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - } - - object->_target = GL_TEXTURE_CUBE_MAP; - - syncSampler(texture.getSampler(), texture.getType(), object); - - object->_contentStamp = texture.getDataStamp(); - - } else { - glBindTexture(GL_TEXTURE_CUBE_MAP, object->_texture); - - // transfer pixels from each faces - for (int f = 0; f < NUM_FACES; f++) { - if (texture.isStoredMipFaceAvailable(0, f)) { - Texture::PixelsPointer mipFace = texture.accessStoredMipFace(0, f); - Element srcFormat = mipFace->getFormat(); - GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(texture.getTexelFormat(), srcFormat); - - glTexImage2D(FACE_LAYOUT[f], 0, texelFormat.internalFormat, texture.getWidth(), texture.getWidth(), 0, - texelFormat.format, texelFormat.type, (GLvoid*) (mipFace->readData())); - - // At this point the mip pixels have been loaded, we can notify - texture.notifyMipFaceGPULoaded(0, f); - } - } - - if (texture.isAutogenerateMips()) { - glGenerateMipmap(GL_TEXTURE_CUBE_MAP); - glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - } else { - glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - } - - object->_target = GL_TEXTURE_CUBE_MAP; - - syncSampler(texture.getSampler(), texture.getType(), object); - - object->_storageStamp = texture.getStamp(); - object->_contentStamp = texture.getDataStamp(); - object->setSize((GLuint)texture.getSize()); - } - - glBindTexture(GL_TEXTURE_CUBE_MAP, boundTex); - } - break; + if (GLTexture::Transferred == object->getSyncState()) { + object->postTransfer(); } - default: - qCDebug(gpulogging) << "GLBackend::syncGPUObject(const Texture&) case for Texture Type " << texture.getType() << " not supported"; - } - (void) CHECK_GL_ERROR(); return object; } +std::shared_ptr GLBackend::_textureTransferHelper; +void GLBackend::initTextureTransferHelper() { + _textureTransferHelper = std::make_shared(); +} GLuint GLBackend::getTextureID(const TexturePointer& texture, bool sync) { if (!texture) { @@ -232,7 +259,7 @@ GLuint GLBackend::getTextureID(const TexturePointer& texture, bool sync) { } GLTexture* object { nullptr }; if (sync) { - object = GLBackend::syncGPUObject(*texture); + object = GLBackend::syncGPUObject(texture); } else { object = Backend::getGPUObject(*texture); } @@ -243,38 +270,37 @@ GLuint GLBackend::getTextureID(const TexturePointer& texture, bool sync) { } } -void GLBackend::syncSampler(const Sampler& sampler, Texture::Type type, GLTexture* object) { +void GLBackend::syncSampler(const Sampler& sampler, Texture::Type type, const GLTexture* object) { if (!object) return; - if (!object->_texture) return; class GLFilterMode { public: GLint minFilter; GLint magFilter; }; - static const GLFilterMode filterModes[] = { - {GL_NEAREST, GL_NEAREST}, //FILTER_MIN_MAG_POINT, - {GL_NEAREST, GL_LINEAR}, //FILTER_MIN_POINT_MAG_LINEAR, - {GL_LINEAR, GL_NEAREST}, //FILTER_MIN_LINEAR_MAG_POINT, - {GL_LINEAR, GL_LINEAR}, //FILTER_MIN_MAG_LINEAR, - - {GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST}, //FILTER_MIN_MAG_MIP_POINT, - {GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST}, //FILTER_MIN_MAG_MIP_POINT, - {GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST}, //FILTER_MIN_MAG_POINT_MIP_LINEAR, - {GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR}, //FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT, - {GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR}, //FILTER_MIN_POINT_MAG_MIP_LINEAR, - {GL_LINEAR_MIPMAP_NEAREST, GL_NEAREST}, //FILTER_MIN_LINEAR_MAG_MIP_POINT, - {GL_LINEAR_MIPMAP_LINEAR, GL_NEAREST}, //FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR, - {GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR}, //FILTER_MIN_MAG_LINEAR_MIP_POINT, - {GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR}, //FILTER_MIN_MAG_MIP_LINEAR, - {GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR} //FILTER_ANISOTROPIC, + static const GLFilterMode filterModes[] = { + { GL_NEAREST, GL_NEAREST }, //FILTER_MIN_MAG_POINT, + { GL_NEAREST, GL_LINEAR }, //FILTER_MIN_POINT_MAG_LINEAR, + { GL_LINEAR, GL_NEAREST }, //FILTER_MIN_LINEAR_MAG_POINT, + { GL_LINEAR, GL_LINEAR }, //FILTER_MIN_MAG_LINEAR, + + { GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST }, //FILTER_MIN_MAG_MIP_POINT, + { GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST }, //FILTER_MIN_MAG_MIP_POINT, + { GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST }, //FILTER_MIN_MAG_POINT_MIP_LINEAR, + { GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR }, //FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT, + { GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR }, //FILTER_MIN_POINT_MAG_MIP_LINEAR, + { GL_LINEAR_MIPMAP_NEAREST, GL_NEAREST }, //FILTER_MIN_LINEAR_MAG_MIP_POINT, + { GL_LINEAR_MIPMAP_LINEAR, GL_NEAREST }, //FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR, + { GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR }, //FILTER_MIN_MAG_LINEAR_MIP_POINT, + { GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR }, //FILTER_MIN_MAG_MIP_LINEAR, + { GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR } //FILTER_ANISOTROPIC, }; auto fm = filterModes[sampler.getFilter()]; glTexParameteri(object->_target, GL_TEXTURE_MIN_FILTER, fm.minFilter); glTexParameteri(object->_target, GL_TEXTURE_MAG_FILTER, fm.magFilter); - static const GLenum comparisonFuncs[] = { + static const GLenum comparisonFuncs[] = { GL_NEVER, GL_LESS, GL_EQUAL, @@ -291,7 +317,7 @@ void GLBackend::syncSampler(const Sampler& sampler, Texture::Type type, GLTextur glTexParameteri(object->_target, GL_TEXTURE_COMPARE_MODE, GL_NONE); } - static const GLenum wrapModes[] = { + static const GLenum wrapModes[] = { GL_REPEAT, // WRAP_REPEAT, GL_MIRRORED_REPEAT, // WRAP_MIRROR, GL_CLAMP_TO_EDGE, // WRAP_CLAMP, @@ -302,23 +328,20 @@ void GLBackend::syncSampler(const Sampler& sampler, Texture::Type type, GLTextur glTexParameteri(object->_target, GL_TEXTURE_WRAP_T, wrapModes[sampler.getWrapModeV()]); glTexParameteri(object->_target, GL_TEXTURE_WRAP_R, wrapModes[sampler.getWrapModeW()]); - glTexParameterfv(object->_target, GL_TEXTURE_BORDER_COLOR, (const float*) &sampler.getBorderColor()); + glTexParameterfv(object->_target, GL_TEXTURE_BORDER_COLOR, (const float*)&sampler.getBorderColor()); glTexParameteri(object->_target, GL_TEXTURE_BASE_LEVEL, sampler.getMipOffset()); - glTexParameterf(object->_target, GL_TEXTURE_MIN_LOD, (float) sampler.getMinMip()); + glTexParameterf(object->_target, GL_TEXTURE_MIN_LOD, (float)sampler.getMinMip()); glTexParameterf(object->_target, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip())); glTexParameterf(object->_target, GL_TEXTURE_MAX_ANISOTROPY_EXT, sampler.getMaxAnisotropy()); - } - - void GLBackend::do_generateTextureMips(Batch& batch, size_t paramOffset) { TexturePointer resourceTexture = batch._textures.get(batch._params[paramOffset + 0]._uint); if (!resourceTexture) { return; } - GLTexture* object = GLBackend::syncGPUObject(*resourceTexture); + GLTexture* object = GLBackend::syncGPUObject(resourceTexture); if (!object) { return; } @@ -333,7 +356,7 @@ void GLBackend::do_generateTextureMips(Batch& batch, size_t paramOffset) { if (freeSlot < 0) { // If had to use slot 0 then restore state - GLTexture* boundObject = GLBackend::syncGPUObject(*_resource._textures[0]); + GLTexture* boundObject = GLBackend::syncGPUObject(_resource._textures[0]); if (boundObject) { glBindTexture(boundObject->_target, boundObject->_texture); } diff --git a/libraries/gpu/src/gpu/GLBackendTextureTransfer.cpp b/libraries/gpu/src/gpu/GLBackendTextureTransfer.cpp new file mode 100644 index 0000000000..aa5839b5b2 --- /dev/null +++ b/libraries/gpu/src/gpu/GLBackendTextureTransfer.cpp @@ -0,0 +1,109 @@ +// +// Created by Bradley Austin Davis on 2016/04/03 +// 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 "GLBackendTextureTransfer.h" + +#include "GLBackendShared.h" +#include "GLTexelFormat.h" + +#ifdef THREADED_TEXTURE_TRANSFER +#include +#include +#include +#endif + +using namespace gpu; + +static ProgramPtr _program; +static ProgramPtr _cubeProgram; +static ShapeWrapperPtr _plane; +static ShapeWrapperPtr _skybox; +static BasicFramebufferWrapperPtr _framebuffer; + +GLTextureTransferHelper::GLTextureTransferHelper() { +#ifdef THREADED_TEXTURE_TRANSFER + _canvas = std::make_shared(); + _canvas->create(QOpenGLContextWrapper::currentContext()); + if (!_canvas->makeCurrent()) { + qFatal("Unable to create texture transfer context"); + } + _canvas->doneCurrent(); + initialize(true, QThread::LowPriority); + _canvas->moveToThreadWithContext(_thread); +#endif +} + +void GLTextureTransferHelper::transferTexture(const gpu::TexturePointer& texturePointer) { + GLBackend::GLTexture* object = Backend::getGPUObject(*texturePointer); +#ifdef THREADED_TEXTURE_TRANSFER + TextureTransferPackage package { texturePointer, glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0) }; + glFlush(); + object->setSyncState(GLBackend::GLTexture::Pending); + queueItem(package); +#else + object->transfer(); + object->postTransfer(); +#endif +} + +void GLTextureTransferHelper::setup() { +#ifdef THREADED_TEXTURE_TRANSFER + _canvas->makeCurrent(); + _program = loadDefaultShader(); + _plane = loadPlane(_program); + _cubeProgram = loadCubemapShader(); + _skybox = loadSkybox(_cubeProgram); + _framebuffer = std::make_shared(); + _framebuffer->Init({ 100, 100 }); + _framebuffer->fbo.Bind(oglplus::FramebufferTarget::Draw); +#endif +} + +bool GLTextureTransferHelper::processQueueItems(const Queue& messages) { + for (auto package : messages) { + glWaitSync(package.fence, 0, GL_TIMEOUT_IGNORED); + glDeleteSync(package.fence); + TexturePointer texturePointer = package.texture.lock(); + // Texture no longer exists, move on to the next + if (!texturePointer) { + continue; + } + + GLBackend::GLTexture* object = Backend::getGPUObject(*texturePointer); + object->transfer(); + + // Now force a draw using the texture + try { + switch (texturePointer->getType()) { + case Texture::TEX_2D: + _program->Use(); + _plane->Use(); + _plane->Draw(); + break; + + case Texture::TEX_CUBE: + _cubeProgram->Use(); + _skybox->Use(); + _skybox->Draw(); + break; + + default: + qCWarning(gpulogging) << __FUNCTION__ << " case for Texture Type " << texturePointer->getType() << " not supported"; + break; + } + } catch (const std::runtime_error& error) { + qWarning() << "Failed to render texture on background thread: " << error.what(); + } + + glBindTexture(object->_target, 0); + glFinish(); + object->_contentStamp = texturePointer->getDataStamp(); + object->setSyncState(GLBackend::GLTexture::Transferred); + } + return true; +} diff --git a/libraries/gpu/src/gpu/GLBackendTextureTransfer.h b/libraries/gpu/src/gpu/GLBackendTextureTransfer.h new file mode 100644 index 0000000000..3a147defdf --- /dev/null +++ b/libraries/gpu/src/gpu/GLBackendTextureTransfer.h @@ -0,0 +1,61 @@ +// +// Created by Bradley Austin Davis on 2016/04/03 +// 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 +#include "GLBackendShared.h" + +#define THREADED_TEXTURE_TRANSFER + +class OffscreenGLCanvas; + +namespace gpu { + +struct TextureTransferPackage { + std::weak_ptr texture; + GLsync fence; +}; + +class GLTextureTransferHelper : public GenericQueueThread { +public: + GLTextureTransferHelper(); + void transferTexture(const gpu::TexturePointer& texturePointer); + void postTransfer(const gpu::TexturePointer& texturePointer); + +protected: + void setup() override; + bool processQueueItems(const Queue& messages) override; + void transferTextureSynchronous(const gpu::Texture& texture); + +private: + std::shared_ptr _canvas; +}; + +template +void withPreservedTexture(GLenum target, F f) { + GLint boundTex = -1; + switch (target) { + case GL_TEXTURE_2D: + glGetIntegerv(GL_TEXTURE_BINDING_2D, &boundTex); + break; + + case GL_TEXTURE_CUBE_MAP: + glGetIntegerv(GL_TEXTURE_BINDING_CUBE_MAP, &boundTex); + break; + + default: + qFatal("Unsupported texture type"); + } + (void)CHECK_GL_ERROR(); + + f(); + + glBindTexture(target, boundTex); + (void)CHECK_GL_ERROR(); +} + +} diff --git a/libraries/gpu/src/gpu/GLTexelFormat.h b/libraries/gpu/src/gpu/GLTexelFormat.h index 274b5eeba3..189189f68b 100644 --- a/libraries/gpu/src/gpu/GLTexelFormat.h +++ b/libraries/gpu/src/gpu/GLTexelFormat.h @@ -14,6 +14,9 @@ public: GLenum format; GLenum type; + static GLTexelFormat evalGLTexelFormat(const gpu::Element& dstFormat) { + return evalGLTexelFormat(dstFormat, dstFormat); + } static GLTexelFormat evalGLTexelFormat(const gpu::Element& dstFormat, const gpu::Element& srcFormat) { using namespace gpu; if (dstFormat != srcFormat) { diff --git a/libraries/shared/src/GenericThread.cpp b/libraries/shared/src/GenericThread.cpp index c1c31ab50a..00a80a2864 100644 --- a/libraries/shared/src/GenericThread.cpp +++ b/libraries/shared/src/GenericThread.cpp @@ -46,6 +46,8 @@ void GenericThread::initialize(bool isThreaded, QThread::Priority priority) { _thread->start(); _thread->setPriority(priority); + } else { + setup(); } } @@ -60,10 +62,16 @@ void GenericThread::terminate() { _thread->deleteLater(); _thread = NULL; } + } else { + shutdown(); } } void GenericThread::threadRoutine() { + if (_isThreaded) { + setup(); + } + while (!_stopThread) { // override this function to do whatever your class actually does, return false to exit thread early @@ -78,8 +86,13 @@ void GenericThread::threadRoutine() { } } - // If we were on a thread, then quit our thread - if (_isThreaded && _thread) { - _thread->quit(); + if (_isThreaded) { + shutdown(); + + // If we were on a thread, then quit our thread + if (_thread) { + _thread->quit(); + } } + } diff --git a/libraries/shared/src/GenericThread.h b/libraries/shared/src/GenericThread.h index 8362d3ba57..47f6d9dacd 100644 --- a/libraries/shared/src/GenericThread.h +++ b/libraries/shared/src/GenericThread.h @@ -33,9 +33,6 @@ public: /// Call to stop the thread void terminate(); - /// Override this function to do whatever your class actually does, return false to exit thread early. - virtual bool process() = 0; - virtual void terminating() { }; // lets your subclass know we're terminating, and it should respond appropriately bool isThreaded() const { return _isThreaded; } @@ -48,6 +45,10 @@ signals: void finished(); protected: + /// Override this function to do whatever your class actually does, return false to exit thread early. + virtual bool process() = 0; + virtual void setup() {}; + virtual void shutdown() {}; /// Locks all the resources of the thread. void lock() { _mutex.lock(); } From c168e2cc5894be2d612b50f15d9bfebcb8e8de40 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 4 Apr 2016 13:37:18 -0700 Subject: [PATCH 2/6] PR feedback --- libraries/gpu/src/gpu/GLBackend.h | 6 ++-- libraries/gpu/src/gpu/GLBackendTexture.cpp | 23 +++++++-------- .../gpu/src/gpu/GLBackendTextureTransfer.cpp | 29 +++++++++++++++---- 3 files changed, 36 insertions(+), 22 deletions(-) diff --git a/libraries/gpu/src/gpu/GLBackend.h b/libraries/gpu/src/gpu/GLBackend.h index 4f22a68631..f9fd299827 100644 --- a/libraries/gpu/src/gpu/GLBackend.h +++ b/libraries/gpu/src/gpu/GLBackend.h @@ -103,13 +103,13 @@ public: SyncState getSyncState() const { return _syncState; } // Is the storage out of date relative to the gpu texture? - bool invalid() const; + bool isInvalid() const; // Is the content out of date relative to the gpu texture? - bool outdated() const; + bool isOutdated() const; // Is the texture in a state where it can be rendered with no work? - bool ready() const; + bool isReady() const; // Move the image bits from the CPU to the GPU void transfer() const; diff --git a/libraries/gpu/src/gpu/GLBackendTexture.cpp b/libraries/gpu/src/gpu/GLBackendTexture.cpp index 92123a68be..f38cc89a8c 100755 --- a/libraries/gpu/src/gpu/GLBackendTexture.cpp +++ b/libraries/gpu/src/gpu/GLBackendTexture.cpp @@ -19,8 +19,6 @@ using namespace gpu; GLenum gpuToGLTextureType(const Texture& texture) { - // If we get here, we need to allocate and or update the content of the texture - // or it's already being transferred switch (texture.getType()) { case Texture::TEX_2D: return GL_TEXTURE_2D; @@ -94,17 +92,17 @@ GLBackend::GLTexture::~GLTexture() { Backend::decrementTextureGPUCount(); } -bool GLBackend::GLTexture::invalid() const { +bool GLBackend::GLTexture::isInvalid() const { return _storageStamp < _gpuTexture.getStamp(); } -bool GLBackend::GLTexture::outdated() const { +bool GLBackend::GLTexture::isOutdated() const { return _contentStamp < _gpuTexture.getDataStamp(); } -bool GLBackend::GLTexture::ready() const { +bool GLBackend::GLTexture::isReady() const { // If we have an invalid texture, we're never ready - if (invalid()) { + if (isInvalid()) { return false; } @@ -112,7 +110,7 @@ bool GLBackend::GLTexture::ready() const { // as a special case auto syncState = _syncState.load(); - if (outdated()) { + if (isOutdated()) { return Pending == syncState; } @@ -167,7 +165,7 @@ void GLBackend::GLTexture::transfer() const { case Texture::TEX_CUBE: // transfer pixels from each faces - for (int f = 0; f < CUBE_NUM_FACES; f++) { + for (uint8_t f = 0; f < CUBE_NUM_FACES; f++) { if (_gpuTexture.isStoredMipFaceAvailable(0, f)) { transferMip(CUBE_FACE_LAYOUT[f], _gpuTexture.accessStoredMipFace(0, f)); } @@ -188,15 +186,14 @@ void GLBackend::GLTexture::transfer() const { // Do any post-transfer operations that might be required on the main context / rendering thread void GLBackend::GLTexture::postTransfer() { setSyncState(GLTexture::Idle); + // At this point the mip pixels have been loaded, we can notify the gpu texture to abandon it's memory switch (_gpuTexture.getType()) { case Texture::TEX_2D: - // At this point the mip piels have been loaded, we can notify _gpuTexture.notifyMipFaceGPULoaded(0, 0); break; case Texture::TEX_CUBE: for (uint8_t f = 0; f < CUBE_NUM_FACES; ++f) { - // At this point the mip piels have been loaded, we can notify _gpuTexture.notifyMipFaceGPULoaded(0, f); } break; @@ -216,7 +213,7 @@ GLBackend::GLTexture* GLBackend::syncGPUObject(const TexturePointer& texturePoin // If the object hasn't been created, or the object definition is out of date, drop and re-create GLTexture* object = Backend::getGPUObject(texture); - if (object && object->ready()) { + if (object && object->isReady()) { return object; } @@ -224,7 +221,7 @@ GLBackend::GLTexture* GLBackend::syncGPUObject(const TexturePointer& texturePoin // Create the texture if need be (force re-creation if the storage stamp changes // for easier use of immutable storage) - if (!object || object->invalid()) { + if (!object || object->isInvalid()) { // This automatically destroys the old texture object = new GLTexture(texture); } @@ -236,7 +233,7 @@ GLBackend::GLTexture* GLBackend::syncGPUObject(const TexturePointer& texturePoin // Object might be outdated, if so, start the transfer // (outdated objects that are already in transfer will have reported 'true' for ready() - if (object->outdated()) { + if (object->isOutdated()) { _textureTransferHelper->transferTexture(texturePointer); } diff --git a/libraries/gpu/src/gpu/GLBackendTextureTransfer.cpp b/libraries/gpu/src/gpu/GLBackendTextureTransfer.cpp index aa5839b5b2..2610d9bc4a 100644 --- a/libraries/gpu/src/gpu/GLBackendTextureTransfer.cpp +++ b/libraries/gpu/src/gpu/GLBackendTextureTransfer.cpp @@ -12,12 +12,15 @@ #include "GLTexelFormat.h" #ifdef THREADED_TEXTURE_TRANSFER -#include -#include -#include -#endif -using namespace gpu; +#include +#include + +//#define FORCE_DRAW_AFTER_TRANSFER + +#ifdef FORCE_DRAW_AFTER_TRANSFER + +#include static ProgramPtr _program; static ProgramPtr _cubeProgram; @@ -25,6 +28,12 @@ static ShapeWrapperPtr _plane; static ShapeWrapperPtr _skybox; static BasicFramebufferWrapperPtr _framebuffer; +#endif + +#endif + +using namespace gpu; + GLTextureTransferHelper::GLTextureTransferHelper() { #ifdef THREADED_TEXTURE_TRANSFER _canvas = std::make_shared(); @@ -54,6 +63,8 @@ void GLTextureTransferHelper::transferTexture(const gpu::TexturePointer& texture void GLTextureTransferHelper::setup() { #ifdef THREADED_TEXTURE_TRANSFER _canvas->makeCurrent(); + +#ifdef FORCE_DRAW_AFTER_TRANSFER _program = loadDefaultShader(); _plane = loadPlane(_program); _cubeProgram = loadCubemapShader(); @@ -62,6 +73,8 @@ void GLTextureTransferHelper::setup() { _framebuffer->Init({ 100, 100 }); _framebuffer->fbo.Bind(oglplus::FramebufferTarget::Draw); #endif + +#endif } bool GLTextureTransferHelper::processQueueItems(const Queue& messages) { @@ -77,6 +90,7 @@ bool GLTextureTransferHelper::processQueueItems(const Queue& messages) { GLBackend::GLTexture* object = Backend::getGPUObject(*texturePointer); object->transfer(); +#ifdef FORCE_DRAW_AFTER_TRANSFER // Now force a draw using the texture try { switch (texturePointer->getType()) { @@ -99,9 +113,12 @@ bool GLTextureTransferHelper::processQueueItems(const Queue& messages) { } catch (const std::runtime_error& error) { qWarning() << "Failed to render texture on background thread: " << error.what(); } +#endif glBindTexture(object->_target, 0); - glFinish(); + auto writeSync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + glClientWaitSync(writeSync, GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED); + glDeleteSync(writeSync); object->_contentStamp = texturePointer->getDataStamp(); object->setSyncState(GLBackend::GLTexture::Transferred); } From ac30aed9482d9a4117b8d4c5a94b57a6ed8aa55f Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Mon, 4 Apr 2016 17:52:41 -0700 Subject: [PATCH 3/6] Hold onto octree child after creation --- libraries/octree/src/Octree.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index ffbc2e6709..051e4f8791 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -384,9 +384,10 @@ int Octree::readElementData(OctreeElementPointer destinationElement, const unsig // check the exists mask to see if we have a child to traverse into if (oneAtBit(childInBufferMask, childIndex)) { - if (!destinationElement->getChildAtIndex(childIndex)) { + auto childAt = destinationElement->getChildAtIndex(childIndex); + if (!childAt) { // add a child at that index, if it doesn't exist - destinationElement->addChildAtIndex(childIndex); + childAt = destinationElement->addChildAtIndex(childIndex); bool nodeIsDirty = destinationElement->isDirty(); if (nodeIsDirty) { _isDirty = true; @@ -394,8 +395,7 @@ int Octree::readElementData(OctreeElementPointer destinationElement, const unsig } // tell the child to read the subsequent data - int lowerLevelBytes = readElementData(destinationElement->getChildAtIndex(childIndex), - nodeData + bytesRead, bytesLeftToRead, args); + int lowerLevelBytes = readElementData(childAt, nodeData + bytesRead, bytesLeftToRead, args); bytesRead += lowerLevelBytes; bytesLeftToRead -= lowerLevelBytes; From ffc2c75534345f0bc099004e847e96b3b8b23e9b Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 5 Apr 2016 09:59:12 -0700 Subject: [PATCH 4/6] Don't release the Oculus plugin when disabling the plugin --- plugins/oculus/src/OculusHelpers.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/oculus/src/OculusHelpers.cpp b/plugins/oculus/src/OculusHelpers.cpp index c9e702ecd0..9d3f95a979 100644 --- a/plugins/oculus/src/OculusHelpers.cpp +++ b/plugins/oculus/src/OculusHelpers.cpp @@ -90,12 +90,14 @@ ovrSession acquireOculusSession() { void releaseOculusSession() { Q_ASSERT(refCount > 0 && session); +#if 0 if (!--refCount) { qCDebug(oculus) << "oculus: zero refcount, shutdown SDK and session"; ovr_Destroy(session); ovr_Shutdown(); session = nullptr; } +#endif } From 71e6f9bdd67f2f97f9c6e49452fd420a8c9a1c76 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 5 Apr 2016 13:03:14 -0700 Subject: [PATCH 5/6] Display a blank frame before switching away from oculus --- plugins/oculus/src/OculusDisplayPlugin.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/plugins/oculus/src/OculusDisplayPlugin.cpp b/plugins/oculus/src/OculusDisplayPlugin.cpp index a53643ba21..78a38d2b6a 100644 --- a/plugins/oculus/src/OculusDisplayPlugin.cpp +++ b/plugins/oculus/src/OculusDisplayPlugin.cpp @@ -52,6 +52,16 @@ void OculusDisplayPlugin::customizeContext() { } void OculusDisplayPlugin::uncustomizeContext() { + using namespace oglplus; + + // Present a final black frame to the HMD + _compositeFramebuffer->Bound(FramebufferTarget::Draw, [] { + Context::ClearColor(0, 0, 0, 1); + Context::Clear().ColorBuffer(); + }); + + hmdPresent(); + #if (OVR_MAJOR_VERSION >= 6) _sceneFbo.reset(); #endif From fd4a374e088decbaca7780b692519d449032eef0 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 5 Apr 2016 13:04:17 -0700 Subject: [PATCH 6/6] Add comment --- plugins/oculus/src/OculusHelpers.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/oculus/src/OculusHelpers.cpp b/plugins/oculus/src/OculusHelpers.cpp index 9d3f95a979..71342f6e1b 100644 --- a/plugins/oculus/src/OculusHelpers.cpp +++ b/plugins/oculus/src/OculusHelpers.cpp @@ -90,6 +90,8 @@ ovrSession acquireOculusSession() { void releaseOculusSession() { Q_ASSERT(refCount > 0 && session); + // HACK the Oculus runtime doesn't seem to play well with repeated shutdown / restart. + // So for now we'll just hold on to the session #if 0 if (!--refCount) { qCDebug(oculus) << "oculus: zero refcount, shutdown SDK and session";