mirror of
https://github.com/overte-org/overte.git
synced 2025-06-20 23:01:03 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into red
This commit is contained in:
commit
71fa898654
21 changed files with 613 additions and 272 deletions
|
@ -107,12 +107,15 @@ elseif(WIN32)
|
||||||
# add an executable that also has the icon itself and the configured rc file as resources
|
# add an executable that also has the icon itself and the configured rc file as resources
|
||||||
add_executable(${TARGET_NAME} WIN32 ${INTERFACE_SRCS} ${QM} ${CONFIGURE_ICON_RC_OUTPUT})
|
add_executable(${TARGET_NAME} WIN32 ${INTERFACE_SRCS} ${QM} ${CONFIGURE_ICON_RC_OUTPUT})
|
||||||
|
|
||||||
add_custom_command(
|
if ( NOT DEV_BUILD )
|
||||||
TARGET ${TARGET_NAME}
|
add_custom_command(
|
||||||
POST_BUILD
|
TARGET ${TARGET_NAME}
|
||||||
COMMAND "mt.exe" -manifest "${CMAKE_CURRENT_SOURCE_DIR}/interface.exe.manifest" -inputresource:"$<TARGET_FILE:${TARGET_NAME}>"\;\#1 -outputresource:"$<TARGET_FILE:${TARGET_NAME}>"\;\#1
|
POST_BUILD
|
||||||
COMMENT "Adding OS version support manifest to exe"
|
COMMAND "mt.exe" -manifest "${CMAKE_CURRENT_SOURCE_DIR}/interface.exe.manifest" -inputresource:"$<TARGET_FILE:${TARGET_NAME}>"\;\#1 -outputresource:"$<TARGET_FILE:${TARGET_NAME}>"\;\#1
|
||||||
)
|
COMMENT "Adding OS version support manifest to exe"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
else()
|
else()
|
||||||
add_executable(${TARGET_NAME} ${INTERFACE_SRCS} ${QM})
|
add_executable(${TARGET_NAME} ${INTERFACE_SRCS} ${QM})
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -1253,6 +1253,9 @@ void Application::initializeGL() {
|
||||||
// Where the gpuContext is initialized and where the TRUE Backend is created and assigned
|
// Where the gpuContext is initialized and where the TRUE Backend is created and assigned
|
||||||
gpu::Context::init<gpu::GLBackend>();
|
gpu::Context::init<gpu::GLBackend>();
|
||||||
_gpuContext = std::make_shared<gpu::Context>();
|
_gpuContext = std::make_shared<gpu::Context>();
|
||||||
|
// The gpu context can make child contexts for transfers, so
|
||||||
|
// we need to restore primary rendering context
|
||||||
|
_offscreenContext->makeCurrent();
|
||||||
|
|
||||||
initDisplay();
|
initDisplay();
|
||||||
qCDebug(interfaceapp, "Initialized Display.");
|
qCDebug(interfaceapp, "Initialized Display.");
|
||||||
|
|
|
@ -12,7 +12,7 @@ const QSurfaceFormat& getDefaultOpenGLSurfaceFormat() {
|
||||||
// Qt Quick may need a depth and stencil buffer. Always make sure these are available.
|
// Qt Quick may need a depth and stencil buffer. Always make sure these are available.
|
||||||
format.setDepthBufferSize(DEFAULT_GL_DEPTH_BUFFER_BITS);
|
format.setDepthBufferSize(DEFAULT_GL_DEPTH_BUFFER_BITS);
|
||||||
format.setStencilBufferSize(DEFAULT_GL_STENCIL_BUFFER_BITS);
|
format.setStencilBufferSize(DEFAULT_GL_STENCIL_BUFFER_BITS);
|
||||||
format.setVersion(4, 1);
|
format.setVersion(4, 5);
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
format.setOption(QSurfaceFormat::DebugContext);
|
format.setOption(QSurfaceFormat::DebugContext);
|
||||||
#endif
|
#endif
|
||||||
|
@ -27,7 +27,7 @@ const QGLFormat& getDefaultGLFormat() {
|
||||||
static QGLFormat glFormat;
|
static QGLFormat glFormat;
|
||||||
static std::once_flag once;
|
static std::once_flag once;
|
||||||
std::call_once(once, [] {
|
std::call_once(once, [] {
|
||||||
glFormat.setVersion(4, 1);
|
glFormat.setVersion(4, 5);
|
||||||
glFormat.setProfile(QGLFormat::CoreProfile); // Requires >=Qt-4.8.0
|
glFormat.setProfile(QGLFormat::CoreProfile); // Requires >=Qt-4.8.0
|
||||||
glFormat.setSampleBuffers(false);
|
glFormat.setSampleBuffers(false);
|
||||||
glFormat.setDepth(false);
|
glFormat.setDepth(false);
|
||||||
|
|
|
@ -83,3 +83,8 @@ void OffscreenGLCanvas::doneCurrent() {
|
||||||
QObject* OffscreenGLCanvas::getContextObject() {
|
QObject* OffscreenGLCanvas::getContextObject() {
|
||||||
return _context;
|
return _context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OffscreenGLCanvas::moveToThreadWithContext(QThread* thread) {
|
||||||
|
moveToThread(thread);
|
||||||
|
_context->moveToThread(thread);
|
||||||
|
}
|
|
@ -26,6 +26,7 @@ public:
|
||||||
bool create(QOpenGLContext* sharedContext = nullptr);
|
bool create(QOpenGLContext* sharedContext = nullptr);
|
||||||
bool makeCurrent();
|
bool makeCurrent();
|
||||||
void doneCurrent();
|
void doneCurrent();
|
||||||
|
void moveToThreadWithContext(QThread* thread);
|
||||||
QOpenGLContext* getContext() {
|
QOpenGLContext* getContext() {
|
||||||
return _context;
|
return _context;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,10 @@
|
||||||
// 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
|
||||||
//
|
//
|
||||||
#include "OglplusHelpers.h"
|
#include "OglplusHelpers.h"
|
||||||
#include <QSharedPointer>
|
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <oglplus/shapes/plane.hpp>
|
||||||
|
#include <oglplus/shapes/sky_box.hpp>
|
||||||
|
|
||||||
using namespace oglplus;
|
using namespace oglplus;
|
||||||
using namespace oglplus::shapes;
|
using namespace oglplus::shapes;
|
||||||
|
@ -20,11 +22,13 @@ uniform mat4 mvp = mat4(1);
|
||||||
in vec3 Position;
|
in vec3 Position;
|
||||||
in vec2 TexCoord;
|
in vec2 TexCoord;
|
||||||
|
|
||||||
|
out vec3 vPosition;
|
||||||
out vec2 vTexCoord;
|
out vec2 vTexCoord;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
gl_Position = mvp * vec4(Position, 1);
|
gl_Position = mvp * vec4(Position, 1);
|
||||||
vTexCoord = TexCoord ;
|
vTexCoord = TexCoord;
|
||||||
|
vPosition = Position;
|
||||||
}
|
}
|
||||||
|
|
||||||
)VS";
|
)VS";
|
||||||
|
@ -35,7 +39,9 @@ static const char * SIMPLE_TEXTURED_FS = R"FS(#version 410 core
|
||||||
uniform sampler2D sampler;
|
uniform sampler2D sampler;
|
||||||
uniform float alpha = 1.0;
|
uniform float alpha = 1.0;
|
||||||
|
|
||||||
|
in vec3 vPosition;
|
||||||
in vec2 vTexCoord;
|
in vec2 vTexCoord;
|
||||||
|
|
||||||
out vec4 FragColor;
|
out vec4 FragColor;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
@ -47,12 +53,38 @@ void main() {
|
||||||
)FS";
|
)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 loadDefaultShader() {
|
||||||
ProgramPtr result;
|
ProgramPtr result;
|
||||||
compileProgram(result, SIMPLE_TEXTURED_VS, SIMPLE_TEXTURED_FS);
|
compileProgram(result, SIMPLE_TEXTURED_VS, SIMPLE_TEXTURED_FS);
|
||||||
return result;
|
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) {
|
void compileProgram(ProgramPtr & result, const std::string& vs, const std::string& fs) {
|
||||||
using namespace oglplus;
|
using namespace oglplus;
|
||||||
try {
|
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
|
// Return a point's cartesian coordinates on a sphere from pitch and yaw
|
||||||
static glm::vec3 getPoint(float yaw, float pitch) {
|
static glm::vec3 getPoint(float yaw, float pitch) {
|
||||||
return glm::vec3(glm::cos(-pitch) * (-glm::sin(yaw)),
|
return glm::vec3(glm::cos(-pitch) * (-glm::sin(yaw)),
|
||||||
|
|
|
@ -37,7 +37,6 @@
|
||||||
#include <oglplus/bound/framebuffer.hpp>
|
#include <oglplus/bound/framebuffer.hpp>
|
||||||
#include <oglplus/bound/renderbuffer.hpp>
|
#include <oglplus/bound/renderbuffer.hpp>
|
||||||
#include <oglplus/shapes/wrapper.hpp>
|
#include <oglplus/shapes/wrapper.hpp>
|
||||||
#include <oglplus/shapes/plane.hpp>
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#pragma warning(pop)
|
#pragma warning(pop)
|
||||||
|
@ -55,7 +54,9 @@ using ProgramPtr = std::shared_ptr<oglplus::Program>;
|
||||||
using Mat4Uniform = oglplus::Uniform<mat4>;
|
using Mat4Uniform = oglplus::Uniform<mat4>;
|
||||||
|
|
||||||
ProgramPtr loadDefaultShader();
|
ProgramPtr loadDefaultShader();
|
||||||
|
ProgramPtr loadCubemapShader();
|
||||||
void compileProgram(ProgramPtr & result, const std::string& vs, const std::string& fs);
|
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 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);
|
ShapeWrapperPtr loadSphereSection(ProgramPtr program, float fov = PI / 3.0f * 2.0f, float aspect = 16.0f / 9.0f, int slices = 32, int stacks = 32);
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,9 @@
|
||||||
|
|
||||||
#include <QOpenGLContext>
|
#include <QOpenGLContext>
|
||||||
|
|
||||||
|
QOpenGLContext* QOpenGLContextWrapper::currentContext() {
|
||||||
|
return QOpenGLContext::currentContext();
|
||||||
|
}
|
||||||
|
|
||||||
QOpenGLContextWrapper::QOpenGLContextWrapper() :
|
QOpenGLContextWrapper::QOpenGLContextWrapper() :
|
||||||
_context(new QOpenGLContext)
|
_context(new QOpenGLContext)
|
||||||
|
|
|
@ -19,7 +19,6 @@ class QSurfaceFormat;
|
||||||
class QOpenGLContextWrapper {
|
class QOpenGLContextWrapper {
|
||||||
public:
|
public:
|
||||||
QOpenGLContextWrapper();
|
QOpenGLContextWrapper();
|
||||||
|
|
||||||
void setFormat(const QSurfaceFormat& format);
|
void setFormat(const QSurfaceFormat& format);
|
||||||
bool create();
|
bool create();
|
||||||
void swapBuffers(QSurface* surface);
|
void swapBuffers(QSurface* surface);
|
||||||
|
@ -27,6 +26,8 @@ public:
|
||||||
void doneCurrent();
|
void doneCurrent();
|
||||||
void setShareContext(QOpenGLContext* otherContext);
|
void setShareContext(QOpenGLContext* otherContext);
|
||||||
|
|
||||||
|
static QOpenGLContext* currentContext();
|
||||||
|
|
||||||
QOpenGLContext* getContext() {
|
QOpenGLContext* getContext() {
|
||||||
return _context;
|
return _context;
|
||||||
}
|
}
|
||||||
|
|
|
@ -125,6 +125,7 @@ GLBackend::GLBackend() {
|
||||||
glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &_uboAlignment);
|
glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &_uboAlignment);
|
||||||
initInput();
|
initInput();
|
||||||
initTransform();
|
initTransform();
|
||||||
|
initTextureTransferHelper();
|
||||||
}
|
}
|
||||||
|
|
||||||
GLBackend::~GLBackend() {
|
GLBackend::~GLBackend() {
|
||||||
|
|
|
@ -25,6 +25,8 @@
|
||||||
|
|
||||||
namespace gpu {
|
namespace gpu {
|
||||||
|
|
||||||
|
class GLTextureTransferHelper;
|
||||||
|
|
||||||
class GLBackend : public Backend {
|
class GLBackend : public Backend {
|
||||||
|
|
||||||
// Context Backend static interface required
|
// Context Backend static interface required
|
||||||
|
@ -36,7 +38,6 @@ class GLBackend : public Backend {
|
||||||
explicit GLBackend(bool syncCache);
|
explicit GLBackend(bool syncCache);
|
||||||
GLBackend();
|
GLBackend();
|
||||||
public:
|
public:
|
||||||
|
|
||||||
virtual ~GLBackend();
|
virtual ~GLBackend();
|
||||||
|
|
||||||
virtual void render(Batch& batch);
|
virtual void render(Batch& batch);
|
||||||
|
@ -76,25 +77,63 @@ public:
|
||||||
|
|
||||||
class GLTexture : public GPUObject {
|
class GLTexture : public GPUObject {
|
||||||
public:
|
public:
|
||||||
Stamp _storageStamp;
|
const Stamp _storageStamp;
|
||||||
Stamp _contentStamp;
|
Stamp _contentStamp { 0 };
|
||||||
GLuint _texture;
|
const GLuint _texture;
|
||||||
GLenum _target;
|
const GLenum _target;
|
||||||
|
|
||||||
GLTexture();
|
GLTexture(const gpu::Texture& gpuTexture);
|
||||||
~GLTexture();
|
~GLTexture();
|
||||||
|
|
||||||
void setSize(GLuint size);
|
|
||||||
GLuint size() const { return _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 isInvalid() const;
|
||||||
|
|
||||||
|
// Is the content out of date relative to the gpu texture?
|
||||||
|
bool isOutdated() const;
|
||||||
|
|
||||||
|
// Is the texture in a state where it can be rendered with no work?
|
||||||
|
bool isReady() 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:
|
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 { SyncState::Idle };
|
||||||
};
|
};
|
||||||
static GLTexture* syncGPUObject(const Texture& texture);
|
static GLTexture* syncGPUObject(const TexturePointer& texture);
|
||||||
static GLuint getTextureID(const TexturePointer& texture, bool sync = true);
|
static GLuint getTextureID(const TexturePointer& texture, bool sync = true);
|
||||||
|
|
||||||
// very specific for now
|
// 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 {
|
class GLShader : public GPUObject {
|
||||||
public:
|
public:
|
||||||
|
@ -261,6 +300,11 @@ protected:
|
||||||
void renderPassTransfer(Batch& batch);
|
void renderPassTransfer(Batch& batch);
|
||||||
void renderPassDraw(Batch& batch);
|
void renderPassDraw(Batch& batch);
|
||||||
|
|
||||||
|
void initTextureTransferHelper();
|
||||||
|
static void transferGPUObject(const TexturePointer& texture);
|
||||||
|
|
||||||
|
static std::shared_ptr<GLTextureTransferHelper> _textureTransferHelper;
|
||||||
|
|
||||||
// Draw Stage
|
// Draw Stage
|
||||||
void do_draw(Batch& batch, size_t paramOffset);
|
void do_draw(Batch& batch, size_t paramOffset);
|
||||||
void do_drawIndexed(Batch& batch, size_t paramOffset);
|
void do_drawIndexed(Batch& batch, size_t paramOffset);
|
||||||
|
@ -504,6 +548,7 @@ protected:
|
||||||
|
|
||||||
typedef void (GLBackend::*CommandCall)(Batch&, size_t);
|
typedef void (GLBackend::*CommandCall)(Batch&, size_t);
|
||||||
static CommandCall _commandCalls[Batch::NUM_COMMANDS];
|
static CommandCall _commandCalls[Batch::NUM_COMMANDS];
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -83,7 +83,7 @@ GLBackend::GLFramebuffer* GLBackend::syncGPUObject(const Framebuffer& framebuffe
|
||||||
for (auto& b : framebuffer.getRenderBuffers()) {
|
for (auto& b : framebuffer.getRenderBuffers()) {
|
||||||
surface = b._texture;
|
surface = b._texture;
|
||||||
if (surface) {
|
if (surface) {
|
||||||
gltexture = GLBackend::syncGPUObject(*surface);
|
gltexture = GLBackend::syncGPUObject(surface);
|
||||||
} else {
|
} else {
|
||||||
gltexture = nullptr;
|
gltexture = nullptr;
|
||||||
}
|
}
|
||||||
|
@ -123,7 +123,7 @@ GLBackend::GLFramebuffer* GLBackend::syncGPUObject(const Framebuffer& framebuffe
|
||||||
if (framebuffer.getDepthStamp() != object->_depthStamp) {
|
if (framebuffer.getDepthStamp() != object->_depthStamp) {
|
||||||
auto surface = framebuffer.getDepthStencilBuffer();
|
auto surface = framebuffer.getDepthStencilBuffer();
|
||||||
if (framebuffer.hasDepthStencil() && surface) {
|
if (framebuffer.hasDepthStencil() && surface) {
|
||||||
gltexture = GLBackend::syncGPUObject(*surface);
|
gltexture = GLBackend::syncGPUObject(surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gltexture) {
|
if (gltexture) {
|
||||||
|
|
|
@ -257,7 +257,7 @@ void GLBackend::do_setResourceTexture(Batch& batch, size_t paramOffset) {
|
||||||
_stats._RSNumTextureBounded++;
|
_stats._RSNumTextureBounded++;
|
||||||
|
|
||||||
// Always make sure the GLObject is in sync
|
// Always make sure the GLObject is in sync
|
||||||
GLTexture* object = GLBackend::syncGPUObject(*resourceTexture);
|
GLTexture* object = GLBackend::syncGPUObject(resourceTexture);
|
||||||
if (object) {
|
if (object) {
|
||||||
GLuint to = object->_texture;
|
GLuint to = object->_texture;
|
||||||
GLuint target = object->_target;
|
GLuint target = object->_target;
|
||||||
|
|
|
@ -9,19 +9,79 @@
|
||||||
// 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
|
||||||
//
|
//
|
||||||
#include "GPULogging.h"
|
#include "GPULogging.h"
|
||||||
|
|
||||||
|
#include <QtCore/QThread>
|
||||||
|
|
||||||
#include "GLBackendShared.h"
|
#include "GLBackendShared.h"
|
||||||
#include "GLTexelFormat.h"
|
#include "GLTexelFormat.h"
|
||||||
|
#include "GLBackendTextureTransfer.h"
|
||||||
|
|
||||||
using namespace gpu;
|
using namespace gpu;
|
||||||
|
|
||||||
GLBackend::GLTexture::GLTexture() :
|
GLenum gpuToGLTextureType(const Texture& texture) {
|
||||||
_storageStamp(0),
|
switch (texture.getType()) {
|
||||||
_contentStamp(0),
|
case Texture::TEX_2D:
|
||||||
_texture(0),
|
return GL_TEXTURE_2D;
|
||||||
_target(GL_TEXTURE_2D),
|
break;
|
||||||
_size(0)
|
|
||||||
|
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::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() {
|
GLBackend::GLTexture::~GLTexture() {
|
||||||
|
@ -32,199 +92,163 @@ GLBackend::GLTexture::~GLTexture() {
|
||||||
Backend::decrementTextureGPUCount();
|
Backend::decrementTextureGPUCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLBackend::GLTexture::setSize(GLuint size) {
|
bool GLBackend::GLTexture::isInvalid() const {
|
||||||
Backend::updateTextureGPUMemoryUsage(_size, size);
|
return _storageStamp < _gpuTexture.getStamp();
|
||||||
_size = size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GLBackend::GLTexture* GLBackend::syncGPUObject(const Texture& texture) {
|
bool GLBackend::GLTexture::isOutdated() const {
|
||||||
GLTexture* object = Backend::getGPUObject<GLBackend::GLTexture>(texture);
|
return _contentStamp < _gpuTexture.getDataStamp();
|
||||||
|
}
|
||||||
|
|
||||||
// If GPU object already created and in sync
|
bool GLBackend::GLTexture::isReady() const {
|
||||||
bool needUpdate = false;
|
// If we have an invalid texture, we're never ready
|
||||||
if (object && (object->_storageStamp == texture.getStamp())) {
|
if (isInvalid()) {
|
||||||
// If gpu object info is in sync with sysmem version
|
return false;
|
||||||
if (object->_contentStamp >= texture.getDataStamp()) {
|
}
|
||||||
// Then all good, GPU object is ready to be used
|
|
||||||
return object;
|
// If we're out of date, but the transfer is in progress, report ready
|
||||||
} else {
|
// as a special case
|
||||||
// Need to update the content of the GPU object from the source sysmem of the texture
|
auto syncState = _syncState.load();
|
||||||
needUpdate = true;
|
|
||||||
}
|
if (isOutdated()) {
|
||||||
} else if (!texture.isDefined()) {
|
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 (uint8_t 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);
|
||||||
|
// 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:
|
||||||
|
_gpuTexture.notifyMipFaceGPULoaded(0, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Texture::TEX_CUBE:
|
||||||
|
for (uint8_t f = 0; f < CUBE_NUM_FACES; ++f) {
|
||||||
|
_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
|
// NO texture definition yet so let's avoid thinking
|
||||||
return nullptr;
|
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<GLBackend::GLTexture>(texture);
|
||||||
|
if (object && object->isReady()) {
|
||||||
|
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->isInvalid()) {
|
||||||
|
// This automatically destroys the old texture
|
||||||
|
object = new GLTexture(texture);
|
||||||
|
}
|
||||||
|
|
||||||
// need to have a gpu object?
|
// need to have a gpu object?
|
||||||
if (!object) {
|
if (texture.getNumSlices() != 1) {
|
||||||
object = new GLTexture();
|
return object;
|
||||||
glGenTextures(1, &object->_texture);
|
|
||||||
(void) CHECK_GL_ERROR();
|
|
||||||
Backend::setGPUObject(texture, object);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GO through the process of allocating the correct storage and/or update the content
|
// Object might be outdated, if so, start the transfer
|
||||||
switch (texture.getType()) {
|
// (outdated objects that are already in transfer will have reported 'true' for ready()
|
||||||
case Texture::TEX_2D: {
|
if (object->isOutdated()) {
|
||||||
if (texture.getNumSlices() == 1) {
|
_textureTransferHelper->transferTexture(texturePointer);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
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
|
if (GLTexture::Transferred == object->getSyncState()) {
|
||||||
for (int f = 0; f < NUM_FACES; f++) {
|
object->postTransfer();
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
default:
|
|
||||||
qCDebug(gpulogging) << "GLBackend::syncGPUObject(const Texture&) case for Texture Type " << texture.getType() << " not supported";
|
|
||||||
}
|
|
||||||
(void) CHECK_GL_ERROR();
|
|
||||||
|
|
||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<GLTextureTransferHelper> GLBackend::_textureTransferHelper;
|
||||||
|
|
||||||
|
void GLBackend::initTextureTransferHelper() {
|
||||||
|
_textureTransferHelper = std::make_shared<GLTextureTransferHelper>();
|
||||||
|
}
|
||||||
|
|
||||||
GLuint GLBackend::getTextureID(const TexturePointer& texture, bool sync) {
|
GLuint GLBackend::getTextureID(const TexturePointer& texture, bool sync) {
|
||||||
if (!texture) {
|
if (!texture) {
|
||||||
|
@ -232,7 +256,7 @@ GLuint GLBackend::getTextureID(const TexturePointer& texture, bool sync) {
|
||||||
}
|
}
|
||||||
GLTexture* object { nullptr };
|
GLTexture* object { nullptr };
|
||||||
if (sync) {
|
if (sync) {
|
||||||
object = GLBackend::syncGPUObject(*texture);
|
object = GLBackend::syncGPUObject(texture);
|
||||||
} else {
|
} else {
|
||||||
object = Backend::getGPUObject<GLBackend::GLTexture>(*texture);
|
object = Backend::getGPUObject<GLBackend::GLTexture>(*texture);
|
||||||
}
|
}
|
||||||
|
@ -243,9 +267,8 @@ 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) return;
|
||||||
if (!object->_texture) return;
|
|
||||||
|
|
||||||
class GLFilterMode {
|
class GLFilterMode {
|
||||||
public:
|
public:
|
||||||
|
@ -253,21 +276,21 @@ void GLBackend::syncSampler(const Sampler& sampler, Texture::Type type, GLTextur
|
||||||
GLint magFilter;
|
GLint magFilter;
|
||||||
};
|
};
|
||||||
static const GLFilterMode filterModes[] = {
|
static const GLFilterMode filterModes[] = {
|
||||||
{GL_NEAREST, GL_NEAREST}, //FILTER_MIN_MAG_POINT,
|
{ GL_NEAREST, GL_NEAREST }, //FILTER_MIN_MAG_POINT,
|
||||||
{GL_NEAREST, GL_LINEAR}, //FILTER_MIN_POINT_MAG_LINEAR,
|
{ GL_NEAREST, GL_LINEAR }, //FILTER_MIN_POINT_MAG_LINEAR,
|
||||||
{GL_LINEAR, GL_NEAREST}, //FILTER_MIN_LINEAR_MAG_POINT,
|
{ GL_LINEAR, GL_NEAREST }, //FILTER_MIN_LINEAR_MAG_POINT,
|
||||||
{GL_LINEAR, GL_LINEAR}, //FILTER_MIN_MAG_LINEAR,
|
{ 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_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_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_NEAREST, GL_LINEAR }, //FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT,
|
||||||
{GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR}, //FILTER_MIN_POINT_MAG_MIP_LINEAR,
|
{ 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_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_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_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_MIN_MAG_MIP_LINEAR,
|
||||||
{GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR} //FILTER_ANISOTROPIC,
|
{ GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR } //FILTER_ANISOTROPIC,
|
||||||
};
|
};
|
||||||
|
|
||||||
auto fm = filterModes[sampler.getFilter()];
|
auto fm = filterModes[sampler.getFilter()];
|
||||||
|
@ -302,23 +325,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_T, wrapModes[sampler.getWrapModeV()]);
|
||||||
glTexParameteri(object->_target, GL_TEXTURE_WRAP_R, wrapModes[sampler.getWrapModeW()]);
|
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());
|
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_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip()));
|
||||||
glTexParameterf(object->_target, GL_TEXTURE_MAX_ANISOTROPY_EXT, sampler.getMaxAnisotropy());
|
glTexParameterf(object->_target, GL_TEXTURE_MAX_ANISOTROPY_EXT, sampler.getMaxAnisotropy());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void GLBackend::do_generateTextureMips(Batch& batch, size_t paramOffset) {
|
void GLBackend::do_generateTextureMips(Batch& batch, size_t paramOffset) {
|
||||||
TexturePointer resourceTexture = batch._textures.get(batch._params[paramOffset + 0]._uint);
|
TexturePointer resourceTexture = batch._textures.get(batch._params[paramOffset + 0]._uint);
|
||||||
if (!resourceTexture) {
|
if (!resourceTexture) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
GLTexture* object = GLBackend::syncGPUObject(*resourceTexture);
|
GLTexture* object = GLBackend::syncGPUObject(resourceTexture);
|
||||||
if (!object) {
|
if (!object) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -333,7 +353,7 @@ void GLBackend::do_generateTextureMips(Batch& batch, size_t paramOffset) {
|
||||||
|
|
||||||
if (freeSlot < 0) {
|
if (freeSlot < 0) {
|
||||||
// If had to use slot 0 then restore state
|
// 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) {
|
if (boundObject) {
|
||||||
glBindTexture(boundObject->_target, boundObject->_texture);
|
glBindTexture(boundObject->_target, boundObject->_texture);
|
||||||
}
|
}
|
||||||
|
|
126
libraries/gpu/src/gpu/GLBackendTextureTransfer.cpp
Normal file
126
libraries/gpu/src/gpu/GLBackendTextureTransfer.cpp
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
//
|
||||||
|
// 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 <gl/OffscreenGLCanvas.h>
|
||||||
|
#include <gl/QOpenGLContextWrapper.h>
|
||||||
|
|
||||||
|
//#define FORCE_DRAW_AFTER_TRANSFER
|
||||||
|
|
||||||
|
#ifdef FORCE_DRAW_AFTER_TRANSFER
|
||||||
|
|
||||||
|
#include <gl/OglplusHelpers.h>
|
||||||
|
|
||||||
|
static ProgramPtr _program;
|
||||||
|
static ProgramPtr _cubeProgram;
|
||||||
|
static ShapeWrapperPtr _plane;
|
||||||
|
static ShapeWrapperPtr _skybox;
|
||||||
|
static BasicFramebufferWrapperPtr _framebuffer;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using namespace gpu;
|
||||||
|
|
||||||
|
GLTextureTransferHelper::GLTextureTransferHelper() {
|
||||||
|
#ifdef THREADED_TEXTURE_TRANSFER
|
||||||
|
_canvas = std::make_shared<OffscreenGLCanvas>();
|
||||||
|
_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<GLBackend::GLTexture>(*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();
|
||||||
|
|
||||||
|
#ifdef FORCE_DRAW_AFTER_TRANSFER
|
||||||
|
_program = loadDefaultShader();
|
||||||
|
_plane = loadPlane(_program);
|
||||||
|
_cubeProgram = loadCubemapShader();
|
||||||
|
_skybox = loadSkybox(_cubeProgram);
|
||||||
|
_framebuffer = std::make_shared<BasicFramebufferWrapper>();
|
||||||
|
_framebuffer->Init({ 100, 100 });
|
||||||
|
_framebuffer->fbo.Bind(oglplus::FramebufferTarget::Draw);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#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<GLBackend::GLTexture>(*texturePointer);
|
||||||
|
object->transfer();
|
||||||
|
|
||||||
|
#ifdef FORCE_DRAW_AFTER_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();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
glBindTexture(object->_target, 0);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
61
libraries/gpu/src/gpu/GLBackendTextureTransfer.h
Normal file
61
libraries/gpu/src/gpu/GLBackendTextureTransfer.h
Normal file
|
@ -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 <GenericQueueThread.h>
|
||||||
|
#include "GLBackendShared.h"
|
||||||
|
|
||||||
|
#define THREADED_TEXTURE_TRANSFER
|
||||||
|
|
||||||
|
class OffscreenGLCanvas;
|
||||||
|
|
||||||
|
namespace gpu {
|
||||||
|
|
||||||
|
struct TextureTransferPackage {
|
||||||
|
std::weak_ptr<Texture> texture;
|
||||||
|
GLsync fence;
|
||||||
|
};
|
||||||
|
|
||||||
|
class GLTextureTransferHelper : public GenericQueueThread<TextureTransferPackage> {
|
||||||
|
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<OffscreenGLCanvas> _canvas;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -14,6 +14,9 @@ public:
|
||||||
GLenum format;
|
GLenum format;
|
||||||
GLenum type;
|
GLenum type;
|
||||||
|
|
||||||
|
static GLTexelFormat evalGLTexelFormat(const gpu::Element& dstFormat) {
|
||||||
|
return evalGLTexelFormat(dstFormat, dstFormat);
|
||||||
|
}
|
||||||
static GLTexelFormat evalGLTexelFormat(const gpu::Element& dstFormat, const gpu::Element& srcFormat) {
|
static GLTexelFormat evalGLTexelFormat(const gpu::Element& dstFormat, const gpu::Element& srcFormat) {
|
||||||
using namespace gpu;
|
using namespace gpu;
|
||||||
if (dstFormat != srcFormat) {
|
if (dstFormat != srcFormat) {
|
||||||
|
|
|
@ -13,20 +13,22 @@
|
||||||
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
#include <glm/gtc/random.hpp>
|
|
||||||
|
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QRunnable>
|
#include <QRunnable>
|
||||||
#include <QThreadPool>
|
#include <QThreadPool>
|
||||||
#include <qimagereader.h>
|
#include <QImageReader>
|
||||||
|
|
||||||
#include <shared/NsightHelpers.h>
|
#include <glm/glm.hpp>
|
||||||
#include <PathUtils.h>
|
#include <glm/gtc/random.hpp>
|
||||||
|
|
||||||
#include <gpu/Batch.h>
|
#include <gpu/Batch.h>
|
||||||
|
|
||||||
|
#include <shared/NsightHelpers.h>
|
||||||
|
|
||||||
|
#include <Finally.h>
|
||||||
|
#include <PathUtils.h>
|
||||||
|
|
||||||
#include "ModelNetworkingLogging.h"
|
#include "ModelNetworkingLogging.h"
|
||||||
|
|
||||||
TextureCache::TextureCache() {
|
TextureCache::TextureCache() {
|
||||||
|
@ -242,14 +244,14 @@ NetworkTexture::TextureLoaderFunc NetworkTexture::getTextureLoader() const {
|
||||||
class ImageReader : public QRunnable {
|
class ImageReader : public QRunnable {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
ImageReader(const QWeakPointer<Resource>& texture, const QByteArray& data, const QUrl& url = QUrl());
|
ImageReader(const QWeakPointer<Resource>& resource, const QByteArray& data, const QUrl& url = QUrl());
|
||||||
|
|
||||||
virtual void run();
|
virtual void run();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void listSupportedImageFormats();
|
static void listSupportedImageFormats();
|
||||||
|
|
||||||
QWeakPointer<Resource> _texture;
|
QWeakPointer<Resource> _resource;
|
||||||
QUrl _url;
|
QUrl _url;
|
||||||
QByteArray _content;
|
QByteArray _content;
|
||||||
};
|
};
|
||||||
|
@ -263,9 +265,9 @@ void NetworkTexture::loadContent(const QByteArray& content) {
|
||||||
QThreadPool::globalInstance()->start(new ImageReader(_self, content, _url));
|
QThreadPool::globalInstance()->start(new ImageReader(_self, content, _url));
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageReader::ImageReader(const QWeakPointer<Resource>& texture, const QByteArray& data,
|
ImageReader::ImageReader(const QWeakPointer<Resource>& resource, const QByteArray& data,
|
||||||
const QUrl& url) :
|
const QUrl& url) :
|
||||||
_texture(texture),
|
_resource(resource),
|
||||||
_url(url),
|
_url(url),
|
||||||
_content(data)
|
_content(data)
|
||||||
{
|
{
|
||||||
|
@ -286,17 +288,19 @@ void ImageReader::run() {
|
||||||
originalPriority = QThread::NormalPriority;
|
originalPriority = QThread::NormalPriority;
|
||||||
}
|
}
|
||||||
QThread::currentThread()->setPriority(QThread::LowPriority);
|
QThread::currentThread()->setPriority(QThread::LowPriority);
|
||||||
|
Finally restorePriority([originalPriority]{
|
||||||
|
QThread::currentThread()->setPriority(originalPriority);
|
||||||
|
});
|
||||||
|
|
||||||
auto texture = _texture.toStrongRef();
|
if (!_resource.data()) {
|
||||||
if (!texture) {
|
qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref";
|
||||||
qCWarning(modelnetworking) << "Could not get strong ref";
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
listSupportedImageFormats();
|
listSupportedImageFormats();
|
||||||
|
|
||||||
// try to help the QImage loader by extracting the image file format from the url filename ext
|
// Help the QImage loader by extracting the image file format from the url filename ext.
|
||||||
// Some tga are not created properly for example without it
|
// Some tga are not created properly without it.
|
||||||
auto filename = _url.fileName().toStdString();
|
auto filename = _url.fileName().toStdString();
|
||||||
auto filenameExtension = filename.substr(filename.find_last_of('.') + 1);
|
auto filenameExtension = filename.substr(filename.find_last_of('.') + 1);
|
||||||
QImage image = QImage::fromData(_content, filenameExtension.c_str());
|
QImage image = QImage::fromData(_content, filenameExtension.c_str());
|
||||||
|
@ -315,17 +319,31 @@ void ImageReader::run() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
gpu::Texture* theTexture = nullptr;
|
gpu::Texture* texture = nullptr;
|
||||||
auto ntex = texture.dynamicCast<NetworkTexture>();
|
{
|
||||||
if (ntex) {
|
// Double-check the resource still exists between long operations.
|
||||||
|
auto resource = _resource.toStrongRef();
|
||||||
|
if (!resource) {
|
||||||
|
qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto url = _url.toString().toStdString();
|
||||||
|
|
||||||
PROFILE_RANGE_EX(__FUNCTION__"::textureLoader", 0xffffff00, nullptr);
|
PROFILE_RANGE_EX(__FUNCTION__"::textureLoader", 0xffffff00, nullptr);
|
||||||
theTexture = ntex->getTextureLoader()(image, _url.toString().toStdString());
|
texture = resource.dynamicCast<NetworkTexture>()->getTextureLoader()(image, url);
|
||||||
}
|
}
|
||||||
|
|
||||||
QMetaObject::invokeMethod(texture.data(), "setImage",
|
// Ensure the resource has not been deleted, and won't be while invokeMethod is in flight.
|
||||||
Q_ARG(void*, theTexture),
|
auto resource = _resource.toStrongRef();
|
||||||
Q_ARG(int, originalWidth), Q_ARG(int, originalHeight));
|
if (!resource) {
|
||||||
QThread::currentThread()->setPriority(originalPriority);
|
qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref";
|
||||||
|
delete texture;
|
||||||
|
} else {
|
||||||
|
QMetaObject::invokeMethod(resource.data(), "setImage", Qt::BlockingQueuedConnection,
|
||||||
|
Q_ARG(void*, texture),
|
||||||
|
Q_ARG(int, originalWidth), Q_ARG(int, originalHeight));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkTexture::setImage(void* voidTexture, int originalWidth,
|
void NetworkTexture::setImage(void* voidTexture, int originalWidth,
|
||||||
|
|
|
@ -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
|
// check the exists mask to see if we have a child to traverse into
|
||||||
|
|
||||||
if (oneAtBit(childInBufferMask, childIndex)) {
|
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
|
// add a child at that index, if it doesn't exist
|
||||||
destinationElement->addChildAtIndex(childIndex);
|
childAt = destinationElement->addChildAtIndex(childIndex);
|
||||||
bool nodeIsDirty = destinationElement->isDirty();
|
bool nodeIsDirty = destinationElement->isDirty();
|
||||||
if (nodeIsDirty) {
|
if (nodeIsDirty) {
|
||||||
_isDirty = true;
|
_isDirty = true;
|
||||||
|
@ -394,8 +395,7 @@ int Octree::readElementData(OctreeElementPointer destinationElement, const unsig
|
||||||
}
|
}
|
||||||
|
|
||||||
// tell the child to read the subsequent data
|
// tell the child to read the subsequent data
|
||||||
int lowerLevelBytes = readElementData(destinationElement->getChildAtIndex(childIndex),
|
int lowerLevelBytes = readElementData(childAt, nodeData + bytesRead, bytesLeftToRead, args);
|
||||||
nodeData + bytesRead, bytesLeftToRead, args);
|
|
||||||
|
|
||||||
bytesRead += lowerLevelBytes;
|
bytesRead += lowerLevelBytes;
|
||||||
bytesLeftToRead -= lowerLevelBytes;
|
bytesLeftToRead -= lowerLevelBytes;
|
||||||
|
|
|
@ -46,6 +46,8 @@ void GenericThread::initialize(bool isThreaded, QThread::Priority priority) {
|
||||||
_thread->start();
|
_thread->start();
|
||||||
|
|
||||||
_thread->setPriority(priority);
|
_thread->setPriority(priority);
|
||||||
|
} else {
|
||||||
|
setup();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,10 +62,16 @@ void GenericThread::terminate() {
|
||||||
_thread->deleteLater();
|
_thread->deleteLater();
|
||||||
_thread = NULL;
|
_thread = NULL;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
shutdown();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GenericThread::threadRoutine() {
|
void GenericThread::threadRoutine() {
|
||||||
|
if (_isThreaded) {
|
||||||
|
setup();
|
||||||
|
}
|
||||||
|
|
||||||
while (!_stopThread) {
|
while (!_stopThread) {
|
||||||
|
|
||||||
// override this function to do whatever your class actually does, return false to exit thread early
|
// 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) {
|
||||||
if (_isThreaded && _thread) {
|
shutdown();
|
||||||
_thread->quit();
|
|
||||||
|
// If we were on a thread, then quit our thread
|
||||||
|
if (_thread) {
|
||||||
|
_thread->quit();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,9 +33,6 @@ public:
|
||||||
/// Call to stop the thread
|
/// Call to stop the thread
|
||||||
void terminate();
|
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
|
virtual void terminating() { }; // lets your subclass know we're terminating, and it should respond appropriately
|
||||||
|
|
||||||
bool isThreaded() const { return _isThreaded; }
|
bool isThreaded() const { return _isThreaded; }
|
||||||
|
@ -48,6 +45,10 @@ signals:
|
||||||
void finished();
|
void finished();
|
||||||
|
|
||||||
protected:
|
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.
|
/// Locks all the resources of the thread.
|
||||||
void lock() { _mutex.lock(); }
|
void lock() { _mutex.lock(); }
|
||||||
|
|
Loading…
Reference in a new issue