mirror of
https://github.com/overte-org/overte.git
synced 2025-05-29 13:10:33 +02:00
Working on new texture management strategy
This commit is contained in:
parent
5bef6ea029
commit
7fb7aa87eb
40 changed files with 1508 additions and 1360 deletions
|
@ -99,7 +99,7 @@ void ApplicationOverlay::renderQmlUi(RenderArgs* renderArgs) {
|
||||||
PROFILE_RANGE(app, __FUNCTION__);
|
PROFILE_RANGE(app, __FUNCTION__);
|
||||||
|
|
||||||
if (!_uiTexture) {
|
if (!_uiTexture) {
|
||||||
_uiTexture = gpu::TexturePointer(gpu::Texture::createExternal2D(OffscreenQmlSurface::getDiscardLambda()));
|
_uiTexture = gpu::TexturePointer(gpu::Texture::createExternal(OffscreenQmlSurface::getDiscardLambda()));
|
||||||
_uiTexture->setSource(__FUNCTION__);
|
_uiTexture->setSource(__FUNCTION__);
|
||||||
}
|
}
|
||||||
// Once we move UI rendering and screen rendering to different
|
// Once we move UI rendering and screen rendering to different
|
||||||
|
@ -272,13 +272,13 @@ void ApplicationOverlay::buildFramebufferObject() {
|
||||||
auto width = uiSize.x;
|
auto width = uiSize.x;
|
||||||
auto height = uiSize.y;
|
auto height = uiSize.y;
|
||||||
if (!_overlayFramebuffer->getDepthStencilBuffer()) {
|
if (!_overlayFramebuffer->getDepthStencilBuffer()) {
|
||||||
auto overlayDepthTexture = gpu::TexturePointer(gpu::Texture::create2D(DEPTH_FORMAT, width, height, DEFAULT_SAMPLER));
|
auto overlayDepthTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(DEPTH_FORMAT, width, height, DEFAULT_SAMPLER));
|
||||||
_overlayFramebuffer->setDepthStencilBuffer(overlayDepthTexture, DEPTH_FORMAT);
|
_overlayFramebuffer->setDepthStencilBuffer(overlayDepthTexture, DEPTH_FORMAT);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_overlayFramebuffer->getRenderBuffer(0)) {
|
if (!_overlayFramebuffer->getRenderBuffer(0)) {
|
||||||
const gpu::Sampler OVERLAY_SAMPLER(gpu::Sampler::FILTER_MIN_MAG_LINEAR, gpu::Sampler::WRAP_CLAMP);
|
const gpu::Sampler OVERLAY_SAMPLER(gpu::Sampler::FILTER_MIN_MAG_LINEAR, gpu::Sampler::WRAP_CLAMP);
|
||||||
auto colorBuffer = gpu::TexturePointer(gpu::Texture::create2D(COLOR_FORMAT, width, height, OVERLAY_SAMPLER));
|
auto colorBuffer = gpu::TexturePointer(gpu::Texture::createRenderBuffer(COLOR_FORMAT, width, height, OVERLAY_SAMPLER));
|
||||||
_overlayFramebuffer->setRenderBuffer(0, colorBuffer);
|
_overlayFramebuffer->setRenderBuffer(0, colorBuffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -251,7 +251,7 @@ void Web3DOverlay::render(RenderArgs* args) {
|
||||||
|
|
||||||
if (!_texture) {
|
if (!_texture) {
|
||||||
auto webSurface = _webSurface;
|
auto webSurface = _webSurface;
|
||||||
_texture = gpu::TexturePointer(gpu::Texture::createExternal2D(OffscreenQmlSurface::getDiscardLambda()));
|
_texture = gpu::TexturePointer(gpu::Texture::createExternal(OffscreenQmlSurface::getDiscardLambda()));
|
||||||
_texture->setSource(__FUNCTION__);
|
_texture->setSource(__FUNCTION__);
|
||||||
}
|
}
|
||||||
OffscreenQmlSurface::TextureAndFence newTextureAndFence;
|
OffscreenQmlSurface::TextureAndFence newTextureAndFence;
|
||||||
|
|
|
@ -355,7 +355,7 @@ void OpenGLDisplayPlugin::customizeContext() {
|
||||||
if ((image.width() > 0) && (image.height() > 0)) {
|
if ((image.width() > 0) && (image.height() > 0)) {
|
||||||
|
|
||||||
cursorData.texture.reset(
|
cursorData.texture.reset(
|
||||||
gpu::Texture::create2D(
|
gpu::Texture::createStrict(
|
||||||
gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA),
|
gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA),
|
||||||
image.width(), image.height(),
|
image.width(), image.height(),
|
||||||
gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)));
|
gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)));
|
||||||
|
@ -363,6 +363,7 @@ void OpenGLDisplayPlugin::customizeContext() {
|
||||||
auto usage = gpu::Texture::Usage::Builder().withColor().withAlpha();
|
auto usage = gpu::Texture::Usage::Builder().withColor().withAlpha();
|
||||||
cursorData.texture->setUsage(usage.build());
|
cursorData.texture->setUsage(usage.build());
|
||||||
cursorData.texture->assignStoredMip(0, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), image.byteCount(), image.constBits());
|
cursorData.texture->assignStoredMip(0, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), image.byteCount(), image.constBits());
|
||||||
|
cursorData.texture->autoGenerateMips(-1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -296,7 +296,7 @@ void HmdDisplayPlugin::internalPresent() {
|
||||||
image = image.convertToFormat(QImage::Format_RGBA8888);
|
image = image.convertToFormat(QImage::Format_RGBA8888);
|
||||||
if (!_previewTexture) {
|
if (!_previewTexture) {
|
||||||
_previewTexture.reset(
|
_previewTexture.reset(
|
||||||
gpu::Texture::create2D(
|
gpu::Texture::createStrict(
|
||||||
gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA),
|
gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA),
|
||||||
image.width(), image.height(),
|
image.width(), image.height(),
|
||||||
gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)));
|
gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)));
|
||||||
|
@ -306,23 +306,21 @@ void HmdDisplayPlugin::internalPresent() {
|
||||||
_previewTexture->autoGenerateMips(-1);
|
_previewTexture->autoGenerateMips(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getGLBackend()->isTextureReady(_previewTexture)) {
|
auto viewport = getViewportForSourceSize(uvec2(_previewTexture->getDimensions()));
|
||||||
auto viewport = getViewportForSourceSize(uvec2(_previewTexture->getDimensions()));
|
|
||||||
|
|
||||||
render([&](gpu::Batch& batch) {
|
render([&](gpu::Batch& batch) {
|
||||||
batch.enableStereo(false);
|
batch.enableStereo(false);
|
||||||
batch.resetViewTransform();
|
batch.resetViewTransform();
|
||||||
batch.setFramebuffer(gpu::FramebufferPointer());
|
batch.setFramebuffer(gpu::FramebufferPointer());
|
||||||
batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, vec4(0));
|
batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, vec4(0));
|
||||||
batch.setStateScissorRect(viewport);
|
batch.setStateScissorRect(viewport);
|
||||||
batch.setViewportTransform(viewport);
|
batch.setViewportTransform(viewport);
|
||||||
batch.setResourceTexture(0, _previewTexture);
|
batch.setResourceTexture(0, _previewTexture);
|
||||||
batch.setPipeline(_presentPipeline);
|
batch.setPipeline(_presentPipeline);
|
||||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||||
});
|
});
|
||||||
_clearPreviewFlag = false;
|
_clearPreviewFlag = false;
|
||||||
swapBuffers();
|
swapBuffers();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
postPreview();
|
postPreview();
|
||||||
|
|
||||||
|
|
|
@ -214,7 +214,7 @@ void RenderableWebEntityItem::render(RenderArgs* args) {
|
||||||
|
|
||||||
if (!_texture) {
|
if (!_texture) {
|
||||||
auto webSurface = _webSurface;
|
auto webSurface = _webSurface;
|
||||||
_texture = gpu::TexturePointer(gpu::Texture::createExternal2D(OffscreenQmlSurface::getDiscardLambda()));
|
_texture = gpu::TexturePointer(gpu::Texture::createExternal(OffscreenQmlSurface::getDiscardLambda()));
|
||||||
_texture->setSource(__FUNCTION__);
|
_texture->setSource(__FUNCTION__);
|
||||||
}
|
}
|
||||||
OffscreenQmlSurface::TextureAndFence newTextureAndFence;
|
OffscreenQmlSurface::TextureAndFence newTextureAndFence;
|
||||||
|
|
|
@ -62,8 +62,6 @@ BackendPointer GLBackend::createBackend() {
|
||||||
INSTANCE = result.get();
|
INSTANCE = result.get();
|
||||||
void* voidInstance = &(*result);
|
void* voidInstance = &(*result);
|
||||||
qApp->setProperty(hifi::properties::gl::BACKEND, QVariant::fromValue(voidInstance));
|
qApp->setProperty(hifi::properties::gl::BACKEND, QVariant::fromValue(voidInstance));
|
||||||
|
|
||||||
gl::GLTexture::initTextureTransferHelper();
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -623,6 +621,7 @@ void GLBackend::queueLambda(const std::function<void()> lambda) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLBackend::recycle() const {
|
void GLBackend::recycle() const {
|
||||||
|
PROFILE_RANGE(render_gpu_gl, __FUNCTION__)
|
||||||
{
|
{
|
||||||
std::list<std::function<void()>> lamdbasTrash;
|
std::list<std::function<void()>> lamdbasTrash;
|
||||||
{
|
{
|
||||||
|
@ -745,10 +744,6 @@ void GLBackend::recycle() const {
|
||||||
glDeleteQueries((GLsizei)ids.size(), ids.data());
|
glDeleteQueries((GLsizei)ids.size(), ids.data());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef THREADED_TEXTURE_TRANSFER
|
|
||||||
gl::GLTexture::_textureTransferHelper->process();
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLBackend::setCameraCorrection(const Mat4& correction) {
|
void GLBackend::setCameraCorrection(const Mat4& correction) {
|
||||||
|
|
|
@ -187,10 +187,15 @@ public:
|
||||||
virtual void do_setStateScissorRect(const Batch& batch, size_t paramOffset) final;
|
virtual void do_setStateScissorRect(const Batch& batch, size_t paramOffset) final;
|
||||||
|
|
||||||
virtual GLuint getFramebufferID(const FramebufferPointer& framebuffer) = 0;
|
virtual GLuint getFramebufferID(const FramebufferPointer& framebuffer) = 0;
|
||||||
virtual GLuint getTextureID(const TexturePointer& texture, bool needTransfer = true) = 0;
|
virtual GLuint getTextureID(const TexturePointer& texture) final;
|
||||||
virtual GLuint getBufferID(const Buffer& buffer) = 0;
|
virtual GLuint getBufferID(const Buffer& buffer) = 0;
|
||||||
virtual GLuint getQueryID(const QueryPointer& query) = 0;
|
virtual GLuint getQueryID(const QueryPointer& query) = 0;
|
||||||
virtual bool isTextureReady(const TexturePointer& texture);
|
|
||||||
|
virtual GLFramebuffer* syncGPUObject(const Framebuffer& framebuffer) = 0;
|
||||||
|
virtual GLBuffer* syncGPUObject(const Buffer& buffer) = 0;
|
||||||
|
virtual GLTexture* syncGPUObject(const TexturePointer& texture);
|
||||||
|
virtual GLQuery* syncGPUObject(const Query& query) = 0;
|
||||||
|
//virtual bool isTextureReady(const TexturePointer& texture);
|
||||||
|
|
||||||
virtual void releaseBuffer(GLuint id, Size size) const;
|
virtual void releaseBuffer(GLuint id, Size size) const;
|
||||||
virtual void releaseExternalTexture(GLuint id, const Texture::ExternalRecycler& recycler) const;
|
virtual void releaseExternalTexture(GLuint id, const Texture::ExternalRecycler& recycler) const;
|
||||||
|
@ -206,10 +211,6 @@ public:
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
void recycle() const override;
|
void recycle() const override;
|
||||||
virtual GLFramebuffer* syncGPUObject(const Framebuffer& framebuffer) = 0;
|
|
||||||
virtual GLBuffer* syncGPUObject(const Buffer& buffer) = 0;
|
|
||||||
virtual GLTexture* syncGPUObject(const TexturePointer& texture, bool sync = true) = 0;
|
|
||||||
virtual GLQuery* syncGPUObject(const Query& query) = 0;
|
|
||||||
|
|
||||||
static const size_t INVALID_OFFSET = (size_t)-1;
|
static const size_t INVALID_OFFSET = (size_t)-1;
|
||||||
bool _inRenderTransferPass { false };
|
bool _inRenderTransferPass { false };
|
||||||
|
|
|
@ -14,12 +14,56 @@
|
||||||
using namespace gpu;
|
using namespace gpu;
|
||||||
using namespace gpu::gl;
|
using namespace gpu::gl;
|
||||||
|
|
||||||
bool GLBackend::isTextureReady(const TexturePointer& texture) {
|
|
||||||
// DO not transfer the texture, this call is expected for rendering texture
|
GLuint GLBackend::getTextureID(const TexturePointer& texture) {
|
||||||
GLTexture* object = syncGPUObject(texture, true);
|
GLTexture* object = syncGPUObject(texture);
|
||||||
return object && object->isReady();
|
|
||||||
|
if (!object) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return object->_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GLTexture* GLBackend::syncGPUObject(const TexturePointer& texturePointer) {
|
||||||
|
const Texture& texture = *texturePointer;
|
||||||
|
// Special case external textures
|
||||||
|
if (TextureUsageType::EXTERNAL == texture.getUsageType()) {
|
||||||
|
Texture::ExternalUpdates updates = texture.getUpdates();
|
||||||
|
if (!updates.empty()) {
|
||||||
|
Texture::ExternalRecycler recycler = texture.getExternalRecycler();
|
||||||
|
Q_ASSERT(recycler);
|
||||||
|
// Discard any superfluous updates
|
||||||
|
while (updates.size() > 1) {
|
||||||
|
const auto& update = updates.front();
|
||||||
|
// Superfluous updates will never have been read, but we want to ensure the previous
|
||||||
|
// writes to them are complete before they're written again, so return them with the
|
||||||
|
// same fences they arrived with. This can happen on any thread because no GL context
|
||||||
|
// work is involved
|
||||||
|
recycler(update.first, update.second);
|
||||||
|
updates.pop_front();
|
||||||
|
}
|
||||||
|
|
||||||
|
// The last texture remaining is the one we'll use to create the GLTexture
|
||||||
|
const auto& update = updates.front();
|
||||||
|
// Check for a fence, and if it exists, inject a wait into the command stream, then destroy the fence
|
||||||
|
if (update.second) {
|
||||||
|
GLsync fence = static_cast<GLsync>(update.second);
|
||||||
|
glWaitSync(fence, 0, GL_TIMEOUT_IGNORED);
|
||||||
|
glDeleteSync(fence);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the new texture object (replaces any previous texture object)
|
||||||
|
new GLExternalTexture(shared_from_this(), texture, update.first);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the texture object (if any) associated with the texture, without extensive logic
|
||||||
|
// (external textures are
|
||||||
|
return Backend::getGPUObject<GLTexture>(texture);
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
void GLBackend::do_generateTextureMips(const Batch& batch, size_t paramOffset) {
|
void GLBackend::do_generateTextureMips(const 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);
|
||||||
|
@ -28,7 +72,7 @@ void GLBackend::do_generateTextureMips(const Batch& batch, size_t paramOffset) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// DO not transfer the texture, this call is expected for rendering texture
|
// DO not transfer the texture, this call is expected for rendering texture
|
||||||
GLTexture* object = syncGPUObject(resourceTexture, false);
|
GLTexture* object = syncGPUObject(resourceTexture);
|
||||||
if (!object) {
|
if (!object) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,13 +21,12 @@ GLFramebuffer::~GLFramebuffer() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GLFramebuffer::checkStatus(GLenum target) const {
|
bool GLFramebuffer::checkStatus() const {
|
||||||
bool result = false;
|
|
||||||
switch (_status) {
|
switch (_status) {
|
||||||
case GL_FRAMEBUFFER_COMPLETE:
|
case GL_FRAMEBUFFER_COMPLETE:
|
||||||
// Success !
|
// Success !
|
||||||
result = true;
|
return true;
|
||||||
break;
|
|
||||||
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
|
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
|
||||||
qCWarning(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT.";
|
qCWarning(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT.";
|
||||||
break;
|
break;
|
||||||
|
@ -44,5 +43,5 @@ bool GLFramebuffer::checkStatus(GLenum target) const {
|
||||||
qCWarning(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_UNSUPPORTED.";
|
qCWarning(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_UNSUPPORTED.";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return result;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,7 +64,7 @@ public:
|
||||||
protected:
|
protected:
|
||||||
GLenum _status { GL_FRAMEBUFFER_COMPLETE };
|
GLenum _status { GL_FRAMEBUFFER_COMPLETE };
|
||||||
virtual void update() = 0;
|
virtual void update() = 0;
|
||||||
bool checkStatus(GLenum target) const;
|
bool checkStatus() const;
|
||||||
|
|
||||||
GLFramebuffer(const std::weak_ptr<GLBackend>& backend, const Framebuffer& framebuffer, GLuint id) : GLObject(backend, framebuffer, id) {}
|
GLFramebuffer(const std::weak_ptr<GLBackend>& backend, const Framebuffer& framebuffer, GLuint id) : GLObject(backend, framebuffer, id) {}
|
||||||
~GLFramebuffer();
|
~GLFramebuffer();
|
||||||
|
|
|
@ -10,15 +10,13 @@
|
||||||
|
|
||||||
#include <NumericalConstants.h>
|
#include <NumericalConstants.h>
|
||||||
|
|
||||||
#include "GLTextureTransfer.h"
|
|
||||||
#include "GLBackend.h"
|
#include "GLBackend.h"
|
||||||
|
|
||||||
using namespace gpu;
|
using namespace gpu;
|
||||||
using namespace gpu::gl;
|
using namespace gpu::gl;
|
||||||
|
|
||||||
std::shared_ptr<GLTextureTransferHelper> GLTexture::_textureTransferHelper;
|
|
||||||
|
|
||||||
const GLenum GLTexture::CUBE_FACE_LAYOUT[6] = {
|
const GLenum GLTexture::CUBE_FACE_LAYOUT[GLTexture::TEXTURE_CUBE_NUM_FACES] = {
|
||||||
GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
|
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_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
|
||||||
GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
|
GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
|
||||||
|
@ -67,6 +65,17 @@ GLenum GLTexture::getGLTextureType(const Texture& texture) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t GLTexture::getFaceCount(GLenum target) {
|
||||||
|
switch (target) {
|
||||||
|
case GL_TEXTURE_2D:
|
||||||
|
return TEXTURE_2D_NUM_FACES;
|
||||||
|
case GL_TEXTURE_CUBE_MAP:
|
||||||
|
return TEXTURE_CUBE_NUM_FACES;
|
||||||
|
default:
|
||||||
|
Q_UNREACHABLE();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
const std::vector<GLenum>& GLTexture::getFaceTargets(GLenum target) {
|
const std::vector<GLenum>& GLTexture::getFaceTargets(GLenum target) {
|
||||||
static std::vector<GLenum> cubeFaceTargets {
|
static std::vector<GLenum> cubeFaceTargets {
|
||||||
GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
|
GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
|
||||||
|
@ -89,216 +98,34 @@ const std::vector<GLenum>& GLTexture::getFaceTargets(GLenum target) {
|
||||||
return faceTargets;
|
return faceTargets;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default texture memory = GPU total memory - 2GB
|
|
||||||
#define GPU_MEMORY_RESERVE_BYTES MB_TO_BYTES(2048)
|
|
||||||
// Minimum texture memory = 1GB
|
|
||||||
#define TEXTURE_MEMORY_MIN_BYTES MB_TO_BYTES(1024)
|
|
||||||
|
|
||||||
|
|
||||||
float GLTexture::getMemoryPressure() {
|
|
||||||
// Check for an explicit memory limit
|
|
||||||
auto availableTextureMemory = Texture::getAllowedGPUMemoryUsage();
|
|
||||||
|
|
||||||
|
|
||||||
// If no memory limit has been set, use a percentage of the total dedicated memory
|
|
||||||
if (!availableTextureMemory) {
|
|
||||||
#if 0
|
|
||||||
auto totalMemory = getDedicatedMemory();
|
|
||||||
if ((GPU_MEMORY_RESERVE_BYTES + TEXTURE_MEMORY_MIN_BYTES) > totalMemory) {
|
|
||||||
availableTextureMemory = TEXTURE_MEMORY_MIN_BYTES;
|
|
||||||
} else {
|
|
||||||
availableTextureMemory = totalMemory - GPU_MEMORY_RESERVE_BYTES;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
// Hardcode texture limit for sparse textures at 1 GB for now
|
|
||||||
availableTextureMemory = TEXTURE_MEMORY_MIN_BYTES;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the consumed texture memory divided by the available texture memory.
|
|
||||||
auto consumedGpuMemory = Context::getTextureGPUMemoryUsage() - Context::getTextureGPUFramebufferMemoryUsage();
|
|
||||||
float memoryPressure = (float)consumedGpuMemory / (float)availableTextureMemory;
|
|
||||||
static Context::Size lastConsumedGpuMemory = 0;
|
|
||||||
if (memoryPressure > 1.0f && lastConsumedGpuMemory != consumedGpuMemory) {
|
|
||||||
lastConsumedGpuMemory = consumedGpuMemory;
|
|
||||||
qCDebug(gpugllogging) << "Exceeded max allowed texture memory: " << consumedGpuMemory << " / " << availableTextureMemory;
|
|
||||||
}
|
|
||||||
return memoryPressure;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Create the texture and allocate storage
|
|
||||||
GLTexture::GLTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture, GLuint id, bool transferrable) :
|
|
||||||
GLObject(backend, texture, id),
|
|
||||||
_external(false),
|
|
||||||
_source(texture.source()),
|
|
||||||
_storageStamp(texture.getStamp()),
|
|
||||||
_target(getGLTextureType(texture)),
|
|
||||||
_internalFormat(gl::GLTexelFormat::evalGLTexelFormatInternal(texture.getTexelFormat())),
|
|
||||||
_maxMip(texture.maxMip()),
|
|
||||||
_minMip(texture.minMip()),
|
|
||||||
_virtualSize(texture.evalTotalSize()),
|
|
||||||
_transferrable(transferrable)
|
|
||||||
{
|
|
||||||
auto strongBackend = _backend.lock();
|
|
||||||
strongBackend->recycle();
|
|
||||||
Backend::incrementTextureGPUCount();
|
|
||||||
Backend::updateTextureGPUVirtualMemoryUsage(0, _virtualSize);
|
|
||||||
Backend::setGPUObject(texture, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
GLTexture::GLTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture, GLuint id) :
|
GLTexture::GLTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture, GLuint id) :
|
||||||
GLObject(backend, texture, id),
|
GLObject(backend, texture, id),
|
||||||
_external(true),
|
|
||||||
_source(texture.source()),
|
_source(texture.source()),
|
||||||
_storageStamp(0),
|
_target(getGLTextureType(texture))
|
||||||
_target(getGLTextureType(texture)),
|
|
||||||
_internalFormat(GL_RGBA8),
|
|
||||||
// FIXME force mips to 0?
|
|
||||||
_maxMip(texture.maxMip()),
|
|
||||||
_minMip(texture.minMip()),
|
|
||||||
_virtualSize(0),
|
|
||||||
_transferrable(false)
|
|
||||||
{
|
{
|
||||||
Backend::setGPUObject(texture, this);
|
Backend::setGPUObject(texture, this);
|
||||||
|
|
||||||
// FIXME Is this necessary?
|
|
||||||
//withPreservedTexture([this] {
|
|
||||||
// syncSampler();
|
|
||||||
// if (_gpuObject.isAutogenerateMips()) {
|
|
||||||
// generateMips();
|
|
||||||
// }
|
|
||||||
//});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GLTexture::~GLTexture() {
|
GLTexture::~GLTexture() {
|
||||||
|
auto backend = _backend.lock();
|
||||||
|
if (backend && _id) {
|
||||||
|
backend->releaseTexture(_id, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
GLExternalTexture::GLExternalTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture, GLuint id)
|
||||||
|
: Parent(backend, texture, id) { }
|
||||||
|
|
||||||
|
GLExternalTexture::~GLExternalTexture() {
|
||||||
auto backend = _backend.lock();
|
auto backend = _backend.lock();
|
||||||
if (backend) {
|
if (backend) {
|
||||||
if (_external) {
|
auto recycler = _gpuObject.getExternalRecycler();
|
||||||
auto recycler = _gpuObject.getExternalRecycler();
|
if (recycler) {
|
||||||
if (recycler) {
|
backend->releaseExternalTexture(_id, recycler);
|
||||||
backend->releaseExternalTexture(_id, recycler);
|
} else {
|
||||||
} else {
|
qWarning() << "No recycler available for texture " << _id << " possible leak";
|
||||||
qWarning() << "No recycler available for texture " << _id << " possible leak";
|
|
||||||
}
|
|
||||||
} else if (_id) {
|
|
||||||
// WARNING! Sparse textures do not use this code path. See GL45BackendTexture for
|
|
||||||
// the GL45Texture destructor for doing any required work tracking GPU stats
|
|
||||||
backend->releaseTexture(_id, _size);
|
|
||||||
}
|
}
|
||||||
|
const_cast<GLuint&>(_id) = 0;
|
||||||
if (!_external && !_transferrable) {
|
|
||||||
Backend::updateTextureGPUFramebufferMemoryUsage(_size, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Backend::updateTextureGPUVirtualMemoryUsage(_virtualSize, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GLTexture::createTexture() {
|
|
||||||
withPreservedTexture([&] {
|
|
||||||
allocateStorage();
|
|
||||||
(void)CHECK_GL_ERROR();
|
|
||||||
syncSampler();
|
|
||||||
(void)CHECK_GL_ERROR();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void GLTexture::withPreservedTexture(std::function<void()> f) const {
|
|
||||||
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();
|
|
||||||
|
|
||||||
glBindTexture(_target, _texture);
|
|
||||||
f();
|
|
||||||
glBindTexture(_target, boundTex);
|
|
||||||
(void)CHECK_GL_ERROR();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GLTexture::setSize(GLuint size) const {
|
|
||||||
if (!_external && !_transferrable) {
|
|
||||||
Backend::updateTextureGPUFramebufferMemoryUsage(_size, size);
|
|
||||||
}
|
|
||||||
Backend::updateTextureGPUMemoryUsage(_size, size);
|
|
||||||
const_cast<GLuint&>(_size) = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GLTexture::isInvalid() const {
|
|
||||||
return _storageStamp < _gpuObject.getStamp();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GLTexture::isOutdated() const {
|
|
||||||
return GLSyncState::Idle == _syncState && _contentStamp < _gpuObject.getDataStamp();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GLTexture::isReady() const {
|
|
||||||
// If we have an invalid texture, we're never ready
|
|
||||||
if (isInvalid()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto syncState = _syncState.load();
|
|
||||||
if (isOutdated() || Idle != syncState) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Do any post-transfer operations that might be required on the main context / rendering thread
|
|
||||||
void GLTexture::postTransfer() {
|
|
||||||
setSyncState(GLSyncState::Idle);
|
|
||||||
++_transferCount;
|
|
||||||
|
|
||||||
// At this point the mip pixels have been loaded, we can notify the gpu texture to abandon it's memory
|
|
||||||
switch (_gpuObject.getType()) {
|
|
||||||
case Texture::TEX_2D:
|
|
||||||
for (uint16_t i = 0; i < Sampler::MAX_MIP_LEVEL; ++i) {
|
|
||||||
if (_gpuObject.isStoredMipFaceAvailable(i)) {
|
|
||||||
_gpuObject.notifyMipFaceGPULoaded(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Texture::TEX_CUBE:
|
|
||||||
// transfer pixels from each faces
|
|
||||||
for (uint8_t f = 0; f < CUBE_NUM_FACES; f++) {
|
|
||||||
for (uint16_t i = 0; i < Sampler::MAX_MIP_LEVEL; ++i) {
|
|
||||||
if (_gpuObject.isStoredMipFaceAvailable(i, f)) {
|
|
||||||
_gpuObject.notifyMipFaceGPULoaded(i, f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
qCWarning(gpugllogging) << __FUNCTION__ << " case for Texture Type " << _gpuObject.getType() << " not supported";
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLTexture::initTextureTransferHelper() {
|
|
||||||
_textureTransferHelper = std::make_shared<GLTextureTransferHelper>();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GLTexture::startTransfer() {
|
|
||||||
createTexture();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GLTexture::finishTransfer() {
|
|
||||||
if (_gpuObject.isAutogenerateMips()) {
|
|
||||||
generateMips();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
#define hifi_gpu_gl_GLTexture_h
|
#define hifi_gpu_gl_GLTexture_h
|
||||||
|
|
||||||
#include "GLShared.h"
|
#include "GLShared.h"
|
||||||
#include "GLTextureTransfer.h"
|
|
||||||
#include "GLBackend.h"
|
#include "GLBackend.h"
|
||||||
#include "GLTexelFormat.h"
|
#include "GLTexelFormat.h"
|
||||||
|
|
||||||
|
@ -20,210 +19,48 @@ struct GLFilterMode {
|
||||||
GLint magFilter;
|
GLint magFilter;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class GLTexture : public GLObject<Texture> {
|
class GLTexture : public GLObject<Texture> {
|
||||||
|
using Parent = GLObject<Texture>;
|
||||||
|
friend class GLBackend;
|
||||||
public:
|
public:
|
||||||
static const uint16_t INVALID_MIP { (uint16_t)-1 };
|
static const uint16_t INVALID_MIP { (uint16_t)-1 };
|
||||||
static const uint8_t INVALID_FACE { (uint8_t)-1 };
|
static const uint8_t INVALID_FACE { (uint8_t)-1 };
|
||||||
|
|
||||||
static void initTextureTransferHelper();
|
|
||||||
static std::shared_ptr<GLTextureTransferHelper> _textureTransferHelper;
|
|
||||||
|
|
||||||
template <typename GLTextureType>
|
|
||||||
static GLTexture* sync(GLBackend& backend, const TexturePointer& texturePointer, bool needTransfer) {
|
|
||||||
const Texture& texture = *texturePointer;
|
|
||||||
|
|
||||||
// Special case external textures
|
|
||||||
if (texture.getUsage().isExternal()) {
|
|
||||||
Texture::ExternalUpdates updates = texture.getUpdates();
|
|
||||||
if (!updates.empty()) {
|
|
||||||
Texture::ExternalRecycler recycler = texture.getExternalRecycler();
|
|
||||||
Q_ASSERT(recycler);
|
|
||||||
// Discard any superfluous updates
|
|
||||||
while (updates.size() > 1) {
|
|
||||||
const auto& update = updates.front();
|
|
||||||
// Superfluous updates will never have been read, but we want to ensure the previous
|
|
||||||
// writes to them are complete before they're written again, so return them with the
|
|
||||||
// same fences they arrived with. This can happen on any thread because no GL context
|
|
||||||
// work is involved
|
|
||||||
recycler(update.first, update.second);
|
|
||||||
updates.pop_front();
|
|
||||||
}
|
|
||||||
|
|
||||||
// The last texture remaining is the one we'll use to create the GLTexture
|
|
||||||
const auto& update = updates.front();
|
|
||||||
// Check for a fence, and if it exists, inject a wait into the command stream, then destroy the fence
|
|
||||||
if (update.second) {
|
|
||||||
GLsync fence = static_cast<GLsync>(update.second);
|
|
||||||
glWaitSync(fence, 0, GL_TIMEOUT_IGNORED);
|
|
||||||
glDeleteSync(fence);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the new texture object (replaces any previous texture object)
|
|
||||||
new GLTextureType(backend.shared_from_this(), texture, update.first);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the texture object (if any) associated with the texture, without extensive logic
|
|
||||||
// (external textures are
|
|
||||||
return Backend::getGPUObject<GLTextureType>(texture);
|
|
||||||
}
|
|
||||||
|
|
||||||
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<GLTextureType>(texture);
|
|
||||||
|
|
||||||
// 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 any previous texture
|
|
||||||
object = new GLTextureType(backend.shared_from_this(), texture, needTransfer);
|
|
||||||
if (!object->_transferrable) {
|
|
||||||
object->createTexture();
|
|
||||||
object->_contentStamp = texture.getDataStamp();
|
|
||||||
object->updateSize();
|
|
||||||
object->postTransfer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Object maybe doens't neet to be tranasferred after creation
|
|
||||||
if (!object->_transferrable) {
|
|
||||||
return object;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we just did a transfer, return the object after doing post-transfer work
|
|
||||||
if (GLSyncState::Transferred == object->getSyncState()) {
|
|
||||||
object->postTransfer();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (object->isOutdated()) {
|
|
||||||
// Object might be outdated, if so, start the transfer
|
|
||||||
// (outdated objects that are already in transfer will have reported 'true' for ready()
|
|
||||||
_textureTransferHelper->transferTexture(texturePointer);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!object->isReady()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
((GLTexture*)object)->updateMips();
|
|
||||||
|
|
||||||
return object;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename GLTextureType>
|
|
||||||
static GLuint getId(GLBackend& backend, const TexturePointer& texture, bool shouldSync) {
|
|
||||||
if (!texture) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
GLTexture* object { nullptr };
|
|
||||||
if (shouldSync) {
|
|
||||||
object = sync<GLTextureType>(backend, texture, shouldSync);
|
|
||||||
} else {
|
|
||||||
object = Backend::getGPUObject<GLTextureType>(*texture);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!object) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!shouldSync) {
|
|
||||||
return object->_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't return textures that are in transfer state
|
|
||||||
if ((object->getSyncState() != GLSyncState::Idle) ||
|
|
||||||
// Don't return transferrable textures that have never completed transfer
|
|
||||||
(!object->_transferrable || 0 != object->_transferCount)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return object->_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
~GLTexture();
|
~GLTexture();
|
||||||
|
|
||||||
// Is this texture generated outside the GPU library?
|
|
||||||
const bool _external;
|
|
||||||
const GLuint& _texture { _id };
|
const GLuint& _texture { _id };
|
||||||
const std::string _source;
|
const std::string _source;
|
||||||
const Stamp _storageStamp;
|
|
||||||
const GLenum _target;
|
const GLenum _target;
|
||||||
const GLenum _internalFormat;
|
|
||||||
const uint16 _maxMip;
|
|
||||||
uint16 _minMip;
|
|
||||||
const GLuint _virtualSize; // theoretical size as expected
|
|
||||||
Stamp _contentStamp { 0 };
|
|
||||||
const bool _transferrable;
|
|
||||||
Size _transferCount { 0 };
|
|
||||||
GLuint size() const { return _size; }
|
|
||||||
GLSyncState getSyncState() const { return _syncState; }
|
|
||||||
|
|
||||||
// Is the storage out of date relative to the gpu texture?
|
static const std::vector<GLenum>& getFaceTargets(GLenum textureType);
|
||||||
bool isInvalid() const;
|
static uint8_t getFaceCount(GLenum textureType);
|
||||||
|
static GLenum getGLTextureType(const Texture& texture);
|
||||||
|
|
||||||
// Is the content out of date relative to the gpu texture?
|
static const uint8_t TEXTURE_2D_NUM_FACES = 1;
|
||||||
bool isOutdated() const;
|
static const uint8_t TEXTURE_CUBE_NUM_FACES = 6;
|
||||||
|
static const GLenum CUBE_FACE_LAYOUT[TEXTURE_CUBE_NUM_FACES];
|
||||||
// Is the texture in a state where it can be rendered with no work?
|
|
||||||
bool isReady() const;
|
|
||||||
|
|
||||||
// Execute any post-move operations that must occur only on the main thread
|
|
||||||
virtual void postTransfer();
|
|
||||||
|
|
||||||
uint16 usedMipLevels() const { return (_maxMip - _minMip) + 1; }
|
|
||||||
|
|
||||||
static const size_t CUBE_NUM_FACES = 6;
|
|
||||||
static const GLenum CUBE_FACE_LAYOUT[6];
|
|
||||||
static const GLFilterMode FILTER_MODES[Sampler::NUM_FILTERS];
|
static const GLFilterMode FILTER_MODES[Sampler::NUM_FILTERS];
|
||||||
static const GLenum WRAP_MODES[Sampler::NUM_WRAP_MODES];
|
static const GLenum WRAP_MODES[Sampler::NUM_WRAP_MODES];
|
||||||
|
|
||||||
// Return a floating point value indicating how much of the allowed
|
|
||||||
// texture memory we are currently consuming. A value of 0 indicates
|
|
||||||
// no texture memory usage, while a value of 1 indicates all available / allowed memory
|
|
||||||
// is consumed. A value above 1 indicates that there is a problem.
|
|
||||||
static float getMemoryPressure();
|
|
||||||
protected:
|
protected:
|
||||||
|
virtual uint32 size() const = 0;
|
||||||
static const std::vector<GLenum>& getFaceTargets(GLenum textureType);
|
|
||||||
|
|
||||||
static GLenum getGLTextureType(const Texture& texture);
|
|
||||||
|
|
||||||
|
|
||||||
const GLuint _size { 0 }; // true size as reported by the gl api
|
|
||||||
std::atomic<GLSyncState> _syncState { GLSyncState::Idle };
|
|
||||||
|
|
||||||
GLTexture(const std::weak_ptr<gl::GLBackend>& backend, const Texture& texture, GLuint id, bool transferrable);
|
|
||||||
GLTexture(const std::weak_ptr<gl::GLBackend>& backend, const Texture& texture, GLuint id);
|
|
||||||
|
|
||||||
void setSyncState(GLSyncState syncState) { _syncState = syncState; }
|
|
||||||
|
|
||||||
void createTexture();
|
|
||||||
|
|
||||||
virtual void updateMips() {}
|
|
||||||
virtual void allocateStorage() const = 0;
|
|
||||||
virtual void updateSize() const = 0;
|
|
||||||
virtual void syncSampler() const = 0;
|
|
||||||
virtual void generateMips() const = 0;
|
virtual void generateMips() const = 0;
|
||||||
virtual void withPreservedTexture(std::function<void()> f) const;
|
|
||||||
|
|
||||||
protected:
|
GLTexture(const std::weak_ptr<gl::GLBackend>& backend, const Texture& texture, GLuint id);
|
||||||
void setSize(GLuint size) const;
|
|
||||||
|
|
||||||
virtual void startTransfer();
|
|
||||||
// Returns true if this is the last block required to complete transfer
|
|
||||||
virtual bool continueTransfer() { return false; }
|
|
||||||
virtual void finishTransfer();
|
|
||||||
|
|
||||||
private:
|
|
||||||
friend class GLTextureTransferHelper;
|
|
||||||
friend class GLBackend;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class GLExternalTexture : public GLTexture {
|
||||||
|
using Parent = GLTexture;
|
||||||
|
friend class GLBackend;
|
||||||
|
public:
|
||||||
|
~GLExternalTexture();
|
||||||
|
protected:
|
||||||
|
GLExternalTexture(const std::weak_ptr<gl::GLBackend>& backend, const Texture& texture, GLuint id);
|
||||||
|
void generateMips() const override {}
|
||||||
|
uint32 size() const override { return 0; }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
} }
|
} }
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,208 +0,0 @@
|
||||||
//
|
|
||||||
// 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 "GLTextureTransfer.h"
|
|
||||||
|
|
||||||
#include <gl/GLHelpers.h>
|
|
||||||
#include <gl/Context.h>
|
|
||||||
|
|
||||||
#include <gpu/GPULogging.h>
|
|
||||||
|
|
||||||
#include "GLShared.h"
|
|
||||||
#include "GLTexture.h"
|
|
||||||
|
|
||||||
#ifdef HAVE_NSIGHT
|
|
||||||
#include "nvToolsExt.h"
|
|
||||||
std::unordered_map<TexturePointer, nvtxRangeId_t> _map;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef TEXTURE_TRANSFER_PBOS
|
|
||||||
#define TEXTURE_TRANSFER_BLOCK_SIZE (64 * 1024)
|
|
||||||
#define TEXTURE_TRANSFER_PBO_COUNT 128
|
|
||||||
#endif
|
|
||||||
|
|
||||||
using namespace gpu;
|
|
||||||
using namespace gpu::gl;
|
|
||||||
|
|
||||||
GLTextureTransferHelper::GLTextureTransferHelper() {
|
|
||||||
#ifdef THREADED_TEXTURE_TRANSFER
|
|
||||||
setObjectName("TextureTransferThread");
|
|
||||||
_context.create();
|
|
||||||
initialize(true, QThread::LowPriority);
|
|
||||||
// Clean shutdown on UNIX, otherwise _canvas is freed early
|
|
||||||
connect(qApp, &QCoreApplication::aboutToQuit, [&] { terminate(); });
|
|
||||||
#else
|
|
||||||
initialize(false, QThread::LowPriority);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
GLTextureTransferHelper::~GLTextureTransferHelper() {
|
|
||||||
#ifdef THREADED_TEXTURE_TRANSFER
|
|
||||||
if (isStillRunning()) {
|
|
||||||
terminate();
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
terminate();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void GLTextureTransferHelper::transferTexture(const gpu::TexturePointer& texturePointer) {
|
|
||||||
GLTexture* object = Backend::getGPUObject<GLTexture>(*texturePointer);
|
|
||||||
|
|
||||||
Backend::incrementTextureGPUTransferCount();
|
|
||||||
object->setSyncState(GLSyncState::Pending);
|
|
||||||
Lock lock(_mutex);
|
|
||||||
_pendingTextures.push_back(texturePointer);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GLTextureTransferHelper::setup() {
|
|
||||||
#ifdef THREADED_TEXTURE_TRANSFER
|
|
||||||
_context.makeCurrent();
|
|
||||||
|
|
||||||
#ifdef TEXTURE_TRANSFER_FORCE_DRAW
|
|
||||||
// FIXME don't use opengl 4.5 DSA functionality without verifying it's present
|
|
||||||
glCreateRenderbuffers(1, &_drawRenderbuffer);
|
|
||||||
glNamedRenderbufferStorage(_drawRenderbuffer, GL_RGBA8, 128, 128);
|
|
||||||
glCreateFramebuffers(1, &_drawFramebuffer);
|
|
||||||
glNamedFramebufferRenderbuffer(_drawFramebuffer, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _drawRenderbuffer);
|
|
||||||
glCreateFramebuffers(1, &_readFramebuffer);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef TEXTURE_TRANSFER_PBOS
|
|
||||||
std::array<GLuint, TEXTURE_TRANSFER_PBO_COUNT> pbos;
|
|
||||||
glCreateBuffers(TEXTURE_TRANSFER_PBO_COUNT, &pbos[0]);
|
|
||||||
for (uint32_t i = 0; i < TEXTURE_TRANSFER_PBO_COUNT; ++i) {
|
|
||||||
TextureTransferBlock newBlock;
|
|
||||||
newBlock._pbo = pbos[i];
|
|
||||||
glNamedBufferStorage(newBlock._pbo, TEXTURE_TRANSFER_BLOCK_SIZE, 0, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT);
|
|
||||||
newBlock._mapped = glMapNamedBufferRange(newBlock._pbo, 0, TEXTURE_TRANSFER_BLOCK_SIZE, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT);
|
|
||||||
_readyQueue.push(newBlock);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void GLTextureTransferHelper::shutdown() {
|
|
||||||
#ifdef THREADED_TEXTURE_TRANSFER
|
|
||||||
_context.makeCurrent();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef TEXTURE_TRANSFER_FORCE_DRAW
|
|
||||||
glNamedFramebufferRenderbuffer(_drawFramebuffer, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, 0);
|
|
||||||
glDeleteFramebuffers(1, &_drawFramebuffer);
|
|
||||||
_drawFramebuffer = 0;
|
|
||||||
glDeleteFramebuffers(1, &_readFramebuffer);
|
|
||||||
_readFramebuffer = 0;
|
|
||||||
|
|
||||||
glNamedFramebufferTexture(_readFramebuffer, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0);
|
|
||||||
glDeleteRenderbuffers(1, &_drawRenderbuffer);
|
|
||||||
_drawRenderbuffer = 0;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void GLTextureTransferHelper::queueExecution(VoidLambda lambda) {
|
|
||||||
Lock lock(_mutex);
|
|
||||||
_pendingCommands.push_back(lambda);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define MAX_TRANSFERS_PER_PASS 2
|
|
||||||
|
|
||||||
bool GLTextureTransferHelper::process() {
|
|
||||||
// Take any new textures or commands off the queue
|
|
||||||
VoidLambdaList pendingCommands;
|
|
||||||
TextureList newTransferTextures;
|
|
||||||
{
|
|
||||||
Lock lock(_mutex);
|
|
||||||
newTransferTextures.swap(_pendingTextures);
|
|
||||||
pendingCommands.swap(_pendingCommands);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pendingCommands.empty()) {
|
|
||||||
for (auto command : pendingCommands) {
|
|
||||||
command();
|
|
||||||
}
|
|
||||||
glFlush();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!newTransferTextures.empty()) {
|
|
||||||
for (auto& texturePointer : newTransferTextures) {
|
|
||||||
#ifdef HAVE_NSIGHT
|
|
||||||
_map[texturePointer] = nvtxRangeStart("TextureTansfer");
|
|
||||||
#endif
|
|
||||||
GLTexture* object = Backend::getGPUObject<GLTexture>(*texturePointer);
|
|
||||||
object->startTransfer();
|
|
||||||
_transferringTextures.push_back(texturePointer);
|
|
||||||
_textureIterator = _transferringTextures.begin();
|
|
||||||
}
|
|
||||||
_transferringTextures.sort([](const gpu::TexturePointer& a, const gpu::TexturePointer& b)->bool {
|
|
||||||
return a->getSize() < b->getSize();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// No transfers in progress, sleep
|
|
||||||
if (_transferringTextures.empty()) {
|
|
||||||
#ifdef THREADED_TEXTURE_TRANSFER
|
|
||||||
QThread::usleep(1);
|
|
||||||
#endif
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
PROFILE_COUNTER_IF_CHANGED(render_gpu_gl, "transferringTextures", int, (int) _transferringTextures.size())
|
|
||||||
|
|
||||||
static auto lastReport = usecTimestampNow();
|
|
||||||
auto now = usecTimestampNow();
|
|
||||||
auto lastReportInterval = now - lastReport;
|
|
||||||
if (lastReportInterval > USECS_PER_SECOND * 4) {
|
|
||||||
lastReport = now;
|
|
||||||
qCDebug(gpulogging) << "Texture list " << _transferringTextures.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t transferCount = 0;
|
|
||||||
for (_textureIterator = _transferringTextures.begin(); _textureIterator != _transferringTextures.end();) {
|
|
||||||
if (++transferCount > MAX_TRANSFERS_PER_PASS) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
auto texture = *_textureIterator;
|
|
||||||
GLTexture* gltexture = Backend::getGPUObject<GLTexture>(*texture);
|
|
||||||
if (gltexture->continueTransfer()) {
|
|
||||||
++_textureIterator;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
gltexture->finishTransfer();
|
|
||||||
|
|
||||||
#ifdef TEXTURE_TRANSFER_FORCE_DRAW
|
|
||||||
// FIXME force a draw on the texture transfer thread before passing the texture to the main thread for use
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef THREADED_TEXTURE_TRANSFER
|
|
||||||
clientWait();
|
|
||||||
#endif
|
|
||||||
gltexture->_contentStamp = gltexture->_gpuObject.getDataStamp();
|
|
||||||
gltexture->updateSize();
|
|
||||||
gltexture->setSyncState(gpu::gl::GLSyncState::Transferred);
|
|
||||||
Backend::decrementTextureGPUTransferCount();
|
|
||||||
#ifdef HAVE_NSIGHT
|
|
||||||
// Mark the texture as transferred
|
|
||||||
nvtxRangeEnd(_map[texture]);
|
|
||||||
_map.erase(texture);
|
|
||||||
#endif
|
|
||||||
_textureIterator = _transferringTextures.erase(_textureIterator);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef THREADED_TEXTURE_TRANSFER
|
|
||||||
if (!_transferringTextures.empty()) {
|
|
||||||
// Don't saturate the GPU
|
|
||||||
clientWait();
|
|
||||||
} else {
|
|
||||||
// Don't saturate the CPU
|
|
||||||
QThread::msleep(1);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
|
@ -1,78 +0,0 @@
|
||||||
//
|
|
||||||
// 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
|
|
||||||
//
|
|
||||||
#ifndef hifi_gpu_gl_GLTextureTransfer_h
|
|
||||||
#define hifi_gpu_gl_GLTextureTransfer_h
|
|
||||||
|
|
||||||
#include <QtGlobal>
|
|
||||||
#include <QtCore/QSharedPointer>
|
|
||||||
|
|
||||||
#include <GenericQueueThread.h>
|
|
||||||
|
|
||||||
#include <gl/Context.h>
|
|
||||||
|
|
||||||
#include "GLShared.h"
|
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
#define THREADED_TEXTURE_TRANSFER
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef THREADED_TEXTURE_TRANSFER
|
|
||||||
// FIXME when sparse textures are enabled, it's harder to force a draw on the transfer thread
|
|
||||||
// also, the current draw code is implicitly using OpenGL 4.5 functionality
|
|
||||||
//#define TEXTURE_TRANSFER_FORCE_DRAW
|
|
||||||
// FIXME PBO's increase the complexity and don't seem to work reliably
|
|
||||||
//#define TEXTURE_TRANSFER_PBOS
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace gpu { namespace gl {
|
|
||||||
|
|
||||||
using TextureList = std::list<TexturePointer>;
|
|
||||||
using TextureListIterator = TextureList::iterator;
|
|
||||||
|
|
||||||
class GLTextureTransferHelper : public GenericThread {
|
|
||||||
public:
|
|
||||||
using VoidLambda = std::function<void()>;
|
|
||||||
using VoidLambdaList = std::list<VoidLambda>;
|
|
||||||
using Pointer = std::shared_ptr<GLTextureTransferHelper>;
|
|
||||||
GLTextureTransferHelper();
|
|
||||||
~GLTextureTransferHelper();
|
|
||||||
void transferTexture(const gpu::TexturePointer& texturePointer);
|
|
||||||
void queueExecution(VoidLambda lambda);
|
|
||||||
|
|
||||||
void setup() override;
|
|
||||||
void shutdown() override;
|
|
||||||
bool process() override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
#ifdef THREADED_TEXTURE_TRANSFER
|
|
||||||
::gl::OffscreenContext _context;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef TEXTURE_TRANSFER_FORCE_DRAW
|
|
||||||
// Framebuffers / renderbuffers for forcing access to the texture on the transfer thread
|
|
||||||
GLuint _drawRenderbuffer { 0 };
|
|
||||||
GLuint _drawFramebuffer { 0 };
|
|
||||||
GLuint _readFramebuffer { 0 };
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// A mutex for protecting items access on the render and transfer threads
|
|
||||||
Mutex _mutex;
|
|
||||||
// Commands that have been submitted for execution on the texture transfer thread
|
|
||||||
VoidLambdaList _pendingCommands;
|
|
||||||
// Textures that have been submitted for transfer
|
|
||||||
TextureList _pendingTextures;
|
|
||||||
// Textures currently in the transfer process
|
|
||||||
// Only used on the transfer thread
|
|
||||||
TextureList _transferringTextures;
|
|
||||||
TextureListIterator _textureIterator;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
} }
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -40,18 +40,28 @@ public:
|
||||||
|
|
||||||
class GL41Texture : public GLTexture {
|
class GL41Texture : public GLTexture {
|
||||||
using Parent = GLTexture;
|
using Parent = GLTexture;
|
||||||
GLuint allocate();
|
static GLuint allocate();
|
||||||
public:
|
|
||||||
GL41Texture(const std::weak_ptr<GLBackend>& backend, const Texture& buffer, GLuint externalId);
|
public:
|
||||||
GL41Texture(const std::weak_ptr<GLBackend>& backend, const Texture& buffer, bool transferrable);
|
~GL41Texture();
|
||||||
|
|
||||||
|
private:
|
||||||
|
GL41Texture(const std::weak_ptr<GLBackend>& backend, const Texture& buffer);
|
||||||
|
|
||||||
protected:
|
|
||||||
void transferMip(uint16_t mipLevel, uint8_t face) const;
|
|
||||||
void startTransfer() override;
|
|
||||||
void allocateStorage() const override;
|
|
||||||
void updateSize() const override;
|
|
||||||
void syncSampler() const override;
|
|
||||||
void generateMips() const override;
|
void generateMips() const override;
|
||||||
|
uint32 size() const override;
|
||||||
|
|
||||||
|
friend class GL41Backend;
|
||||||
|
const Stamp _storageStamp;
|
||||||
|
mutable Stamp _contentStamp { 0 };
|
||||||
|
mutable Stamp _samplerStamp { 0 };
|
||||||
|
const uint32 _size;
|
||||||
|
|
||||||
|
|
||||||
|
bool isOutdated() const;
|
||||||
|
void withPreservedTexture(std::function<void()> f) const;
|
||||||
|
void syncContent() const;
|
||||||
|
void syncSampler() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -62,8 +72,7 @@ protected:
|
||||||
GLuint getBufferID(const Buffer& buffer) override;
|
GLuint getBufferID(const Buffer& buffer) override;
|
||||||
GLBuffer* syncGPUObject(const Buffer& buffer) override;
|
GLBuffer* syncGPUObject(const Buffer& buffer) override;
|
||||||
|
|
||||||
GLuint getTextureID(const TexturePointer& texture, bool needTransfer = true) override;
|
GLTexture* syncGPUObject(const TexturePointer& texture) override;
|
||||||
GLTexture* syncGPUObject(const TexturePointer& texture, bool sync = true) override;
|
|
||||||
|
|
||||||
GLuint getQueryID(const QueryPointer& query) override;
|
GLuint getQueryID(const QueryPointer& query) override;
|
||||||
GLQuery* syncGPUObject(const Query& query) override;
|
GLQuery* syncGPUObject(const Query& query) override;
|
||||||
|
|
|
@ -53,10 +53,12 @@ public:
|
||||||
GL_COLOR_ATTACHMENT15 };
|
GL_COLOR_ATTACHMENT15 };
|
||||||
|
|
||||||
int unit = 0;
|
int unit = 0;
|
||||||
|
auto backend = _backend.lock();
|
||||||
for (auto& b : _gpuObject.getRenderBuffers()) {
|
for (auto& b : _gpuObject.getRenderBuffers()) {
|
||||||
surface = b._texture;
|
surface = b._texture;
|
||||||
if (surface) {
|
if (surface) {
|
||||||
gltexture = gl::GLTexture::sync<GL41Backend::GL41Texture>(*_backend.lock().get(), surface, false); // Grab the gltexture and don't transfer
|
Q_ASSERT(TextureUsageType::RENDERBUFFER == surface->getUsageType());
|
||||||
|
gltexture = backend->syncGPUObject(surface);
|
||||||
} else {
|
} else {
|
||||||
gltexture = nullptr;
|
gltexture = nullptr;
|
||||||
}
|
}
|
||||||
|
@ -81,9 +83,11 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_gpuObject.getDepthStamp() != _depthStamp) {
|
if (_gpuObject.getDepthStamp() != _depthStamp) {
|
||||||
|
auto backend = _backend.lock();
|
||||||
auto surface = _gpuObject.getDepthStencilBuffer();
|
auto surface = _gpuObject.getDepthStencilBuffer();
|
||||||
if (_gpuObject.hasDepthStencil() && surface) {
|
if (_gpuObject.hasDepthStencil() && surface) {
|
||||||
gltexture = gl::GLTexture::sync<GL41Backend::GL41Texture>(*_backend.lock().get(), surface, false); // Grab the gltexture and don't transfer
|
Q_ASSERT(TextureUsageType::RENDERBUFFER == surface->getUsageType());
|
||||||
|
gltexture = backend->syncGPUObject(surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gltexture) {
|
if (gltexture) {
|
||||||
|
@ -110,7 +114,7 @@ public:
|
||||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, currentFBO);
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, currentFBO);
|
||||||
}
|
}
|
||||||
|
|
||||||
checkStatus(GL_DRAW_FRAMEBUFFER);
|
checkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -29,20 +29,97 @@ GLuint GL41Texture::allocate() {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
GLuint GL41Backend::getTextureID(const TexturePointer& texture, bool transfer) {
|
GLTexture* GL41Backend::syncGPUObject(const TexturePointer& texturePointer) {
|
||||||
return GL41Texture::getId<GL41Texture>(*this, texture, transfer);
|
if (!texturePointer) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
const Texture& texture = *texturePointer;
|
||||||
|
if (TextureUsageType::EXTERNAL == texture.getUsageType()) {
|
||||||
|
return Parent::syncGPUObject(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
|
||||||
|
GL41Texture* object = Backend::getGPUObject<GL41Texture>(texture);
|
||||||
|
if (!object || object->_storageStamp < texture.getStamp()) {
|
||||||
|
// This automatically any previous texture
|
||||||
|
object = new GL41Texture(shared_from_this(), texture);
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME internalize to GL41Texture 'sync' function
|
||||||
|
if (object->isOutdated()) {
|
||||||
|
object->withPreservedTexture([&] {
|
||||||
|
if (object->_contentStamp < texture.getDataStamp()) {
|
||||||
|
// FIXME implement synchronous texture transfer here
|
||||||
|
object->syncContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (object->_samplerStamp < texture.getSamplerStamp()) {
|
||||||
|
object->syncSampler();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return object;
|
||||||
}
|
}
|
||||||
|
|
||||||
GLTexture* GL41Backend::syncGPUObject(const TexturePointer& texture, bool transfer) {
|
GL41Texture::GL41Texture(const std::weak_ptr<GLBackend>& backend, const Texture& texture)
|
||||||
return GL41Texture::sync<GL41Texture>(*this, texture, transfer);
|
: GLTexture(backend, texture, allocate()), _storageStamp { texture.getStamp() }, _size(texture.evalTotalSize()) {
|
||||||
|
|
||||||
|
withPreservedTexture([&] {
|
||||||
|
GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat());
|
||||||
|
const Sampler& sampler = _gpuObject.getSampler();
|
||||||
|
auto minMip = sampler.getMinMip();
|
||||||
|
auto maxMip = sampler.getMaxMip();
|
||||||
|
for (uint16_t l = minMip; l <= maxMip; l++) {
|
||||||
|
// Get the mip level dimensions, accounting for the downgrade level
|
||||||
|
Vec3u dimensions = _gpuObject.evalMipDimensions(l);
|
||||||
|
for (GLenum target : getFaceTargets(_target)) {
|
||||||
|
glTexImage2D(target, l - minMip, texelFormat.internalFormat, dimensions.x, dimensions.y, 0, texelFormat.format, texelFormat.type, NULL);
|
||||||
|
(void)CHECK_GL_ERROR();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
GL41Texture::GL41Texture(const std::weak_ptr<GLBackend>& backend, const Texture& texture, GLuint externalId)
|
GL41Texture::~GL41Texture() {
|
||||||
: GLTexture(backend, texture, externalId) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GL41Texture::GL41Texture(const std::weak_ptr<GLBackend>& backend, const Texture& texture, bool transferrable)
|
bool GL41Texture::isOutdated() const {
|
||||||
: GLTexture(backend, texture, allocate(), transferrable) {
|
if (_samplerStamp <= _gpuObject.getSamplerStamp()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (TextureUsageType::RESOURCE == _gpuObject.getUsageType() && _contentStamp <= _gpuObject.getDataStamp()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GL41Texture::withPreservedTexture(std::function<void()> f) const {
|
||||||
|
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();
|
||||||
|
|
||||||
|
glBindTexture(_target, _texture);
|
||||||
|
f();
|
||||||
|
glBindTexture(_target, boundTex);
|
||||||
|
(void)CHECK_GL_ERROR();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GL41Texture::generateMips() const {
|
void GL41Texture::generateMips() const {
|
||||||
|
@ -52,94 +129,12 @@ void GL41Texture::generateMips() const {
|
||||||
(void)CHECK_GL_ERROR();
|
(void)CHECK_GL_ERROR();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GL41Texture::allocateStorage() const {
|
void GL41Texture::syncContent() const {
|
||||||
GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat());
|
// FIXME actually copy the texture data
|
||||||
glTexParameteri(_target, GL_TEXTURE_BASE_LEVEL, 0);
|
_contentStamp = _gpuObject.getDataStamp() + 1;
|
||||||
(void)CHECK_GL_ERROR();
|
|
||||||
glTexParameteri(_target, GL_TEXTURE_MAX_LEVEL, _maxMip - _minMip);
|
|
||||||
(void)CHECK_GL_ERROR();
|
|
||||||
if (GLEW_VERSION_4_2 && !_gpuObject.getTexelFormat().isCompressed()) {
|
|
||||||
// Get the dimensions, accounting for the downgrade level
|
|
||||||
Vec3u dimensions = _gpuObject.evalMipDimensions(_minMip);
|
|
||||||
glTexStorage2D(_target, usedMipLevels(), texelFormat.internalFormat, dimensions.x, dimensions.y);
|
|
||||||
(void)CHECK_GL_ERROR();
|
|
||||||
} else {
|
|
||||||
for (uint16_t l = _minMip; l <= _maxMip; l++) {
|
|
||||||
// Get the mip level dimensions, accounting for the downgrade level
|
|
||||||
Vec3u dimensions = _gpuObject.evalMipDimensions(l);
|
|
||||||
for (GLenum target : getFaceTargets(_target)) {
|
|
||||||
glTexImage2D(target, l - _minMip, texelFormat.internalFormat, dimensions.x, dimensions.y, 0, texelFormat.format, texelFormat.type, NULL);
|
|
||||||
(void)CHECK_GL_ERROR();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GL41Texture::updateSize() const {
|
void GL41Texture::syncSampler() const {
|
||||||
setSize(_virtualSize);
|
|
||||||
if (!_id) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_gpuObject.getTexelFormat().isCompressed()) {
|
|
||||||
GLenum proxyType = GL_TEXTURE_2D;
|
|
||||||
GLuint numFaces = 1;
|
|
||||||
if (_gpuObject.getType() == gpu::Texture::TEX_CUBE) {
|
|
||||||
proxyType = CUBE_FACE_LAYOUT[0];
|
|
||||||
numFaces = (GLuint)CUBE_NUM_FACES;
|
|
||||||
}
|
|
||||||
GLint gpuSize{ 0 };
|
|
||||||
glGetTexLevelParameteriv(proxyType, 0, GL_TEXTURE_COMPRESSED, &gpuSize);
|
|
||||||
(void)CHECK_GL_ERROR();
|
|
||||||
|
|
||||||
if (gpuSize) {
|
|
||||||
for (GLuint level = _minMip; level < _maxMip; level++) {
|
|
||||||
GLint levelSize{ 0 };
|
|
||||||
glGetTexLevelParameteriv(proxyType, level, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &levelSize);
|
|
||||||
levelSize *= numFaces;
|
|
||||||
|
|
||||||
if (levelSize <= 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
gpuSize += levelSize;
|
|
||||||
}
|
|
||||||
(void)CHECK_GL_ERROR();
|
|
||||||
setSize(gpuSize);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move content bits from the CPU to the GPU for a given mip / face
|
|
||||||
void GL41Texture::transferMip(uint16_t mipLevel, uint8_t face) const {
|
|
||||||
auto mip = _gpuObject.accessStoredMipFace(mipLevel, face);
|
|
||||||
GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), mip->getFormat());
|
|
||||||
//GLenum target = getFaceTargets()[face];
|
|
||||||
GLenum target = _target == GL_TEXTURE_2D ? GL_TEXTURE_2D : CUBE_FACE_LAYOUT[face];
|
|
||||||
auto size = _gpuObject.evalMipDimensions(mipLevel);
|
|
||||||
glTexSubImage2D(target, mipLevel, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mip->readData());
|
|
||||||
(void)CHECK_GL_ERROR();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GL41Texture::startTransfer() {
|
|
||||||
PROFILE_RANGE(render_gpu_gl, __FUNCTION__);
|
|
||||||
Parent::startTransfer();
|
|
||||||
|
|
||||||
glBindTexture(_target, _id);
|
|
||||||
(void)CHECK_GL_ERROR();
|
|
||||||
|
|
||||||
// transfer pixels from each faces
|
|
||||||
uint8_t numFaces = (Texture::TEX_CUBE == _gpuObject.getType()) ? CUBE_NUM_FACES : 1;
|
|
||||||
for (uint8_t f = 0; f < numFaces; f++) {
|
|
||||||
for (uint16_t i = 0; i < Sampler::MAX_MIP_LEVEL; ++i) {
|
|
||||||
if (_gpuObject.isStoredMipFaceAvailable(i, f)) {
|
|
||||||
transferMip(i, f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GL41Backend::GL41Texture::syncSampler() const {
|
|
||||||
const Sampler& sampler = _gpuObject.getSampler();
|
const Sampler& sampler = _gpuObject.getSampler();
|
||||||
const auto& fm = FILTER_MODES[sampler.getFilter()];
|
const auto& fm = FILTER_MODES[sampler.getFilter()];
|
||||||
glTexParameteri(_target, GL_TEXTURE_MIN_FILTER, fm.minFilter);
|
glTexParameteri(_target, GL_TEXTURE_MIN_FILTER, fm.minFilter);
|
||||||
|
@ -161,5 +156,9 @@ void GL41Backend::GL41Texture::syncSampler() const {
|
||||||
glTexParameterf(_target, GL_TEXTURE_MIN_LOD, (float)sampler.getMinMip());
|
glTexParameterf(_target, GL_TEXTURE_MIN_LOD, (float)sampler.getMinMip());
|
||||||
glTexParameterf(_target, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip()));
|
glTexParameterf(_target, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip()));
|
||||||
glTexParameterf(_target, GL_TEXTURE_MAX_ANISOTROPY_EXT, sampler.getMaxAnisotropy());
|
glTexParameterf(_target, GL_TEXTURE_MAX_ANISOTROPY_EXT, sampler.getMaxAnisotropy());
|
||||||
|
_samplerStamp = _gpuObject.getSamplerStamp() + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32 GL41Texture::size() const {
|
||||||
|
return _size;
|
||||||
|
}
|
||||||
|
|
|
@ -163,8 +163,3 @@ void GL45Backend::do_multiDrawIndexedIndirect(const Batch& batch, size_t paramOf
|
||||||
_stats._DSNumAPIDrawcalls++;
|
_stats._DSNumAPIDrawcalls++;
|
||||||
(void)CHECK_GL_ERROR();
|
(void)CHECK_GL_ERROR();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GL45Backend::recycle() const {
|
|
||||||
Parent::recycle();
|
|
||||||
derezTextures();
|
|
||||||
}
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
// Distributed under the Apache License, Version 2.0.
|
// Distributed under the Apache License, Version 2.0.
|
||||||
// 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
|
||||||
//
|
//
|
||||||
|
#pragma once
|
||||||
#ifndef hifi_gpu_45_GL45Backend_h
|
#ifndef hifi_gpu_45_GL45Backend_h
|
||||||
#define hifi_gpu_45_GL45Backend_h
|
#define hifi_gpu_45_GL45Backend_h
|
||||||
|
|
||||||
|
@ -19,6 +20,7 @@
|
||||||
namespace gpu { namespace gl45 {
|
namespace gpu { namespace gl45 {
|
||||||
|
|
||||||
using namespace gpu::gl;
|
using namespace gpu::gl;
|
||||||
|
using TextureWeakPointer = std::weak_ptr<Texture>;
|
||||||
|
|
||||||
class GL45Backend : public GLBackend {
|
class GL45Backend : public GLBackend {
|
||||||
using Parent = GLBackend;
|
using Parent = GLBackend;
|
||||||
|
@ -31,60 +33,152 @@ public:
|
||||||
|
|
||||||
class GL45Texture : public GLTexture {
|
class GL45Texture : public GLTexture {
|
||||||
using Parent = GLTexture;
|
using Parent = GLTexture;
|
||||||
|
friend class GL45Backend;
|
||||||
static GLuint allocate(const Texture& texture);
|
static GLuint allocate(const Texture& texture);
|
||||||
static const uint32_t DEFAULT_PAGE_DIMENSION = 128;
|
protected:
|
||||||
static const uint32_t DEFAULT_MAX_SPARSE_LEVEL = 0xFFFF;
|
GL45Texture(const std::weak_ptr<GLBackend>& backend, const Texture& texture);
|
||||||
|
void generateMips() const override;
|
||||||
|
void copyMipFromTexture(uint16_t sourceMip, uint16_t targetMip) const;
|
||||||
|
virtual void syncSampler() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Textures that have fixed allocation sizes and cannot be managed at runtime
|
||||||
|
//
|
||||||
|
|
||||||
|
class GL45FixedAllocationTexture : public GL45Texture {
|
||||||
|
using Parent = GL45Texture;
|
||||||
|
friend class GL45Backend;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
GL45Texture(const std::weak_ptr<GLBackend>& backend, const Texture& texture, GLuint externalId);
|
GL45FixedAllocationTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture);
|
||||||
GL45Texture(const std::weak_ptr<GLBackend>& backend, const Texture& texture, bool transferrable);
|
~GL45FixedAllocationTexture();
|
||||||
~GL45Texture();
|
|
||||||
|
|
||||||
void postTransfer() override;
|
protected:
|
||||||
|
uint32 size() const override { return _size; }
|
||||||
|
void allocateStorage() const;
|
||||||
|
void syncSampler() const override;
|
||||||
|
const uint32 _size { 0 };
|
||||||
|
};
|
||||||
|
|
||||||
struct SparseInfo {
|
class GL45AttachmentTexture : public GL45FixedAllocationTexture {
|
||||||
SparseInfo(GL45Texture& texture);
|
using Parent = GL45FixedAllocationTexture;
|
||||||
void maybeMakeSparse();
|
friend class GL45Backend;
|
||||||
void update();
|
protected:
|
||||||
uvec3 getPageCounts(const uvec3& dimensions) const;
|
GL45AttachmentTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture);
|
||||||
uint32_t getPageCount(const uvec3& dimensions) const;
|
~GL45AttachmentTexture();
|
||||||
uint32_t getSize() const;
|
};
|
||||||
|
|
||||||
GL45Texture& texture;
|
class GL45StrictResourceTexture : public GL45FixedAllocationTexture {
|
||||||
bool sparse { false };
|
using Parent = GL45FixedAllocationTexture;
|
||||||
uvec3 pageDimensions { DEFAULT_PAGE_DIMENSION };
|
friend class GL45Backend;
|
||||||
GLuint maxSparseLevel { DEFAULT_MAX_SPARSE_LEVEL };
|
protected:
|
||||||
uint32_t allocatedPages { 0 };
|
GL45StrictResourceTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture);
|
||||||
uint32_t maxPages { 0 };
|
};
|
||||||
uint32_t pageBytes { 0 };
|
|
||||||
GLint pageDimensionsIndex { 0 };
|
//
|
||||||
|
// Textures that can be managed at runtime to increase or decrease their memory load
|
||||||
|
//
|
||||||
|
|
||||||
|
class GL45VariableAllocationTexture : public GL45Texture {
|
||||||
|
using Parent = GL45Texture;
|
||||||
|
friend class GL45Backend;
|
||||||
|
using PromoteLambda = std::function<void()>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum class MemoryPressureState {
|
||||||
|
Idle,
|
||||||
|
Transfer,
|
||||||
|
Oversubscribed,
|
||||||
|
Undersubscribed,
|
||||||
};
|
};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void updateMips() override;
|
static std::atomic<bool> _memoryPressureStateStale;
|
||||||
void stripToMip(uint16_t newMinMip);
|
static MemoryPressureState _memoryPressureState;
|
||||||
void startTransfer() override;
|
static std::list<TextureWeakPointer> _memoryManagedTextures;
|
||||||
bool continueTransfer() override;
|
static const uvec3 INITIAL_MIP_TRANSFER_DIMENSIONS;
|
||||||
void finishTransfer() override;
|
|
||||||
void incrementalTransfer(const uvec3& size, const gpu::Texture::PixelsPointer& mip, std::function<void(const ivec3& offset, const uvec3& size)> f) const;
|
|
||||||
void transferMip(uint16_t mipLevel, uint8_t face = 0) const;
|
|
||||||
void allocateMip(uint16_t mipLevel, uint8_t face = 0) const;
|
|
||||||
void allocateStorage() const override;
|
|
||||||
void updateSize() const override;
|
|
||||||
void syncSampler() const override;
|
|
||||||
void generateMips() const override;
|
|
||||||
void withPreservedTexture(std::function<void()> f) const override;
|
|
||||||
void derez();
|
|
||||||
|
|
||||||
SparseInfo _sparseInfo;
|
static void updateMemoryPressure();
|
||||||
uint16_t _mipOffset { 0 };
|
static void processWorkQueues();
|
||||||
friend class GL45Backend;
|
static void addMemoryManagedTexture(const TexturePointer& texturePointer);
|
||||||
|
|
||||||
|
static void manageMemory();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
GL45VariableAllocationTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture);
|
||||||
|
~GL45VariableAllocationTexture();
|
||||||
|
//bool canPromoteNoAllocate() const { return _allocatedMip < _populatedMip; }
|
||||||
|
bool canPromote() const { return _allocatedMip > 0; }
|
||||||
|
bool canDemote() const { return _allocatedMip < _maxAllocatedMip; }
|
||||||
|
bool hasPendingTransfers() const { return !_pendingTransfers.empty(); }
|
||||||
|
void executeNextTransfer();
|
||||||
|
uint32 size() const override { return _size; }
|
||||||
|
virtual void populateTransferQueue() = 0;
|
||||||
|
virtual void promote() = 0;
|
||||||
|
virtual void demote() = 0;
|
||||||
|
|
||||||
|
uint16 _populatedMip { 0 };
|
||||||
|
uint16 _allocatedMip { 0 };
|
||||||
|
uint16 _maxAllocatedMip { 0 };
|
||||||
|
uint32 _size { 0 };
|
||||||
|
std::queue<PromoteLambda> _pendingTransfers;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class GL45ResourceTexture : public GL45VariableAllocationTexture {
|
||||||
|
using Parent = GL45VariableAllocationTexture;
|
||||||
|
friend class GL45Backend;
|
||||||
|
protected:
|
||||||
|
GL45ResourceTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture);
|
||||||
|
|
||||||
|
void syncSampler() const override;
|
||||||
|
void promote() override;
|
||||||
|
void demote() override;
|
||||||
|
void populateTransferQueue() override;
|
||||||
|
|
||||||
|
void allocateStorage(uint16 mip);
|
||||||
|
void copyMipsFromTexture();
|
||||||
|
private:
|
||||||
|
};
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
class GL45SparseResourceTexture : public GL45VariableAllocationTexture {
|
||||||
|
using Parent = GL45VariableAllocationTexture;
|
||||||
|
friend class GL45Backend;
|
||||||
|
using TextureTypeFormat = std::pair<GLenum, GLenum>;
|
||||||
|
using PageDimensions = std::vector<uvec3>;
|
||||||
|
using PageDimensionsMap = std::map<TextureTypeFormat, PageDimensions>;
|
||||||
|
static PageDimensionsMap pageDimensionsByFormat;
|
||||||
|
static Mutex pageDimensionsMutex;
|
||||||
|
|
||||||
|
static bool isSparseEligible(const Texture& texture);
|
||||||
|
static PageDimensions getPageDimensionsForFormat(const TextureTypeFormat& typeFormat);
|
||||||
|
static PageDimensions getPageDimensionsForFormat(GLenum type, GLenum format);
|
||||||
|
static const uint32_t DEFAULT_PAGE_DIMENSION = 128;
|
||||||
|
static const uint32_t DEFAULT_MAX_SPARSE_LEVEL = 0xFFFF;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
GL45SparseResourceTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture);
|
||||||
|
~GL45SparseResourceTexture();
|
||||||
|
uint32 size() const override { return _allocatedPages * _pageBytes; }
|
||||||
|
void promote() override;
|
||||||
|
void demote() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
uvec3 getPageCounts(const uvec3& dimensions) const;
|
||||||
|
uint32_t getPageCount(const uvec3& dimensions) const;
|
||||||
|
|
||||||
|
uint32_t _allocatedPages { 0 };
|
||||||
|
uint32_t _pageBytes { 0 };
|
||||||
|
uvec3 _pageDimensions { DEFAULT_PAGE_DIMENSION };
|
||||||
|
GLuint _maxSparseLevel { DEFAULT_MAX_SPARSE_LEVEL };
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
void recycle() const override;
|
void recycle() const override;
|
||||||
void derezTextures() const;
|
|
||||||
|
|
||||||
GLuint getFramebufferID(const FramebufferPointer& framebuffer) override;
|
GLuint getFramebufferID(const FramebufferPointer& framebuffer) override;
|
||||||
GLFramebuffer* syncGPUObject(const Framebuffer& framebuffer) override;
|
GLFramebuffer* syncGPUObject(const Framebuffer& framebuffer) override;
|
||||||
|
@ -92,8 +186,7 @@ protected:
|
||||||
GLuint getBufferID(const Buffer& buffer) override;
|
GLuint getBufferID(const Buffer& buffer) override;
|
||||||
GLBuffer* syncGPUObject(const Buffer& buffer) override;
|
GLBuffer* syncGPUObject(const Buffer& buffer) override;
|
||||||
|
|
||||||
GLuint getTextureID(const TexturePointer& texture, bool needTransfer = true) override;
|
GLTexture* syncGPUObject(const TexturePointer& texture) override;
|
||||||
GLTexture* syncGPUObject(const TexturePointer& texture, bool sync = true) override;
|
|
||||||
|
|
||||||
GLuint getQueryID(const QueryPointer& query) override;
|
GLuint getQueryID(const QueryPointer& query) override;
|
||||||
GLQuery* syncGPUObject(const Query& query) override;
|
GLQuery* syncGPUObject(const Query& query) override;
|
||||||
|
@ -126,5 +219,5 @@ protected:
|
||||||
|
|
||||||
Q_DECLARE_LOGGING_CATEGORY(gpugl45logging)
|
Q_DECLARE_LOGGING_CATEGORY(gpugl45logging)
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -49,10 +49,12 @@ public:
|
||||||
GL_COLOR_ATTACHMENT15 };
|
GL_COLOR_ATTACHMENT15 };
|
||||||
|
|
||||||
int unit = 0;
|
int unit = 0;
|
||||||
|
auto backend = _backend.lock();
|
||||||
for (auto& b : _gpuObject.getRenderBuffers()) {
|
for (auto& b : _gpuObject.getRenderBuffers()) {
|
||||||
surface = b._texture;
|
surface = b._texture;
|
||||||
if (surface) {
|
if (surface) {
|
||||||
gltexture = gl::GLTexture::sync<GL45Backend::GL45Texture>(*_backend.lock().get(), surface, false); // Grab the gltexture and don't transfer
|
Q_ASSERT(TextureUsageType::RENDERBUFFER == surface->getUsageType());
|
||||||
|
gltexture = backend->syncGPUObject(surface);
|
||||||
} else {
|
} else {
|
||||||
gltexture = nullptr;
|
gltexture = nullptr;
|
||||||
}
|
}
|
||||||
|
@ -78,8 +80,10 @@ public:
|
||||||
|
|
||||||
if (_gpuObject.getDepthStamp() != _depthStamp) {
|
if (_gpuObject.getDepthStamp() != _depthStamp) {
|
||||||
auto surface = _gpuObject.getDepthStencilBuffer();
|
auto surface = _gpuObject.getDepthStencilBuffer();
|
||||||
|
auto backend = _backend.lock();
|
||||||
if (_gpuObject.hasDepthStencil() && surface) {
|
if (_gpuObject.hasDepthStencil() && surface) {
|
||||||
gltexture = gl::GLTexture::sync<GL45Backend::GL45Texture>(*_backend.lock().get(), surface, false); // Grab the gltexture and don't transfer
|
Q_ASSERT(TextureUsageType::RENDERBUFFER == surface->getUsageType());
|
||||||
|
gltexture = backend->syncGPUObject(surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gltexture) {
|
if (gltexture) {
|
||||||
|
@ -102,7 +106,7 @@ public:
|
||||||
_status = glCheckNamedFramebufferStatus(_id, GL_DRAW_FRAMEBUFFER);
|
_status = glCheckNamedFramebufferStatus(_id, GL_DRAW_FRAMEBUFFER);
|
||||||
|
|
||||||
// restore the current framebuffer
|
// restore the current framebuffer
|
||||||
checkStatus(GL_DRAW_FRAMEBUFFER);
|
checkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -8,9 +8,10 @@
|
||||||
// Distributed under the Apache License, Version 2.0.
|
// Distributed under the Apache License, Version 2.0.
|
||||||
// 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 "GL45Backend.h"
|
|
||||||
|
|
||||||
|
#include "GL45Backend.h"
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <algorithm>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
@ -19,142 +20,73 @@
|
||||||
#include <QtCore/QDebug>
|
#include <QtCore/QDebug>
|
||||||
#include <QtCore/QThread>
|
#include <QtCore/QThread>
|
||||||
|
|
||||||
|
#include <NumericalConstants.h>
|
||||||
#include "../gl/GLTexelFormat.h"
|
#include "../gl/GLTexelFormat.h"
|
||||||
|
|
||||||
using namespace gpu;
|
using namespace gpu;
|
||||||
using namespace gpu::gl;
|
using namespace gpu::gl;
|
||||||
using namespace gpu::gl45;
|
using namespace gpu::gl45;
|
||||||
|
|
||||||
// Allocate 1 MB of buffer space for paged transfers
|
|
||||||
#define DEFAULT_PAGE_BUFFER_SIZE (1024*1024)
|
|
||||||
#define DEFAULT_GL_PIXEL_ALIGNMENT 4
|
|
||||||
|
|
||||||
using GL45Texture = GL45Backend::GL45Texture;
|
|
||||||
|
|
||||||
static std::map<uint16_t, std::unordered_set<GL45Texture*>> texturesByMipCounts;
|
|
||||||
static Mutex texturesByMipCountsMutex;
|
|
||||||
using TextureTypeFormat = std::pair<GLenum, GLenum>;
|
|
||||||
std::map<TextureTypeFormat, std::vector<uvec3>> sparsePageDimensionsByFormat;
|
|
||||||
Mutex sparsePageDimensionsByFormatMutex;
|
|
||||||
|
|
||||||
static std::vector<uvec3> getPageDimensionsForFormat(const TextureTypeFormat& typeFormat) {
|
|
||||||
{
|
|
||||||
Lock lock(sparsePageDimensionsByFormatMutex);
|
|
||||||
if (sparsePageDimensionsByFormat.count(typeFormat)) {
|
|
||||||
return sparsePageDimensionsByFormat[typeFormat];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
GLint count = 0;
|
|
||||||
glGetInternalformativ(typeFormat.first, typeFormat.second, GL_NUM_VIRTUAL_PAGE_SIZES_ARB, 1, &count);
|
|
||||||
|
|
||||||
std::vector<uvec3> result;
|
|
||||||
if (count > 0) {
|
|
||||||
std::vector<GLint> x, y, z;
|
|
||||||
x.resize(count);
|
|
||||||
glGetInternalformativ(typeFormat.first, typeFormat.second, GL_VIRTUAL_PAGE_SIZE_X_ARB, 1, &x[0]);
|
|
||||||
y.resize(count);
|
|
||||||
glGetInternalformativ(typeFormat.first, typeFormat.second, GL_VIRTUAL_PAGE_SIZE_Y_ARB, 1, &y[0]);
|
|
||||||
z.resize(count);
|
|
||||||
glGetInternalformativ(typeFormat.first, typeFormat.second, GL_VIRTUAL_PAGE_SIZE_Z_ARB, 1, &z[0]);
|
|
||||||
|
|
||||||
result.resize(count);
|
|
||||||
for (GLint i = 0; i < count; ++i) {
|
|
||||||
result[i] = uvec3(x[i], y[i], z[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Lock lock(sparsePageDimensionsByFormatMutex);
|
|
||||||
if (0 == sparsePageDimensionsByFormat.count(typeFormat)) {
|
|
||||||
sparsePageDimensionsByFormat[typeFormat] = result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::vector<uvec3> getPageDimensionsForFormat(GLenum target, GLenum format) {
|
|
||||||
return getPageDimensionsForFormat({ target, format });
|
|
||||||
}
|
|
||||||
|
|
||||||
GLTexture* GL45Backend::syncGPUObject(const TexturePointer& texture, bool transfer) {
|
|
||||||
return GL45Texture::sync<GL45Texture>(*this, texture, transfer);
|
|
||||||
}
|
|
||||||
|
|
||||||
using SparseInfo = GL45Backend::GL45Texture::SparseInfo;
|
|
||||||
|
|
||||||
SparseInfo::SparseInfo(GL45Texture& texture)
|
|
||||||
: texture(texture) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void SparseInfo::maybeMakeSparse() {
|
|
||||||
// Don't enable sparse for objects with explicitly managed mip levels
|
|
||||||
if (!texture._gpuObject.isAutogenerateMips()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
|
|
||||||
const uvec3 dimensions = texture._gpuObject.getDimensions();
|
|
||||||
auto allowedPageDimensions = getPageDimensionsForFormat(texture._target, texture._internalFormat);
|
|
||||||
// In order to enable sparse the texture size must be an integer multiple of the page size
|
|
||||||
for (size_t i = 0; i < allowedPageDimensions.size(); ++i) {
|
|
||||||
pageDimensionsIndex = (uint32_t) i;
|
|
||||||
pageDimensions = allowedPageDimensions[i];
|
|
||||||
// Is this texture an integer multiple of page dimensions?
|
|
||||||
if (uvec3(0) == (dimensions % pageDimensions)) {
|
|
||||||
qCDebug(gpugl45logging) << "Enabling sparse for texture " << texture._source.c_str();
|
|
||||||
sparse = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sparse) {
|
|
||||||
glTextureParameteri(texture._id, GL_TEXTURE_SPARSE_ARB, GL_TRUE);
|
|
||||||
glTextureParameteri(texture._id, GL_VIRTUAL_PAGE_SIZE_INDEX_ARB, pageDimensionsIndex);
|
|
||||||
} else {
|
|
||||||
qCDebug(gpugl45logging) << "Size " << dimensions.x << " x " << dimensions.y <<
|
|
||||||
" is not supported by any sparse page size for texture" << texture._source.c_str();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#define SPARSE_PAGE_SIZE_OVERHEAD_ESTIMATE 1.3f
|
#define SPARSE_PAGE_SIZE_OVERHEAD_ESTIMATE 1.3f
|
||||||
|
|
||||||
// This can only be called after we've established our storage size
|
GLTexture* GL45Backend::syncGPUObject(const TexturePointer& texturePointer) {
|
||||||
void SparseInfo::update() {
|
if (!texturePointer) {
|
||||||
if (!sparse) {
|
return nullptr;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
glGetTextureParameterIuiv(texture._id, GL_NUM_SPARSE_LEVELS_ARB, &maxSparseLevel);
|
|
||||||
pageBytes = texture._gpuObject.getTexelFormat().getSize();
|
|
||||||
pageBytes *= pageDimensions.x * pageDimensions.y * pageDimensions.z;
|
|
||||||
// Testing with a simple texture allocating app shows an estimated 20% GPU memory overhead for
|
|
||||||
// sparse textures as compared to non-sparse, so we acount for that here.
|
|
||||||
pageBytes = (uint32_t)(pageBytes * SPARSE_PAGE_SIZE_OVERHEAD_ESTIMATE);
|
|
||||||
|
|
||||||
for (uint16_t mipLevel = 0; mipLevel <= maxSparseLevel; ++mipLevel) {
|
const Texture& texture = *texturePointer;
|
||||||
auto mipDimensions = texture._gpuObject.evalMipDimensions(mipLevel);
|
if (std::string("cursor texture") == texture.source()) {
|
||||||
auto mipPageCount = getPageCount(mipDimensions);
|
qDebug() << "Loading cursor texture";
|
||||||
maxPages += mipPageCount;
|
|
||||||
}
|
}
|
||||||
if (texture._target == GL_TEXTURE_CUBE_MAP) {
|
if (TextureUsageType::EXTERNAL == texture.getUsageType()) {
|
||||||
maxPages *= GLTexture::CUBE_NUM_FACES;
|
return Parent::syncGPUObject(texturePointer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!texture.isDefined()) {
|
||||||
|
// NO texture definition yet so let's avoid thinking
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
GL45Texture* object = Backend::getGPUObject<GL45Texture>(texture);
|
||||||
|
if (!object) {
|
||||||
|
switch (texture.getUsageType()) {
|
||||||
|
case TextureUsageType::RENDERBUFFER:
|
||||||
|
object = new GL45AttachmentTexture(shared_from_this(), texture);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TextureUsageType::STRICT_RESOURCE:
|
||||||
|
qCDebug(gpugllogging) << "Strict texture " << texture.source().c_str();
|
||||||
|
object = new GL45StrictResourceTexture(shared_from_this(), texture);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TextureUsageType::RESOURCE: {
|
||||||
|
|
||||||
|
GL45VariableAllocationTexture* varObject { nullptr };
|
||||||
|
#if 0
|
||||||
|
if (isTextureManagementSparseEnabled() && GL45Texture::isSparseEligible(texture)) {
|
||||||
|
varObject = new GL45SparseResourceTexture(shared_from_this(), texture);
|
||||||
|
} else {
|
||||||
|
varObject = new GL45ResourceTexture(shared_from_this(), texture);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
varObject = new GL45ResourceTexture(shared_from_this(), texture);
|
||||||
|
#endif
|
||||||
|
GL45VariableAllocationTexture::addMemoryManagedTexture(texturePointer);
|
||||||
|
object = varObject;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
Q_UNREACHABLE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return object;
|
||||||
}
|
}
|
||||||
|
|
||||||
uvec3 SparseInfo::getPageCounts(const uvec3& dimensions) const {
|
void GL45Backend::recycle() const {
|
||||||
auto result = (dimensions / pageDimensions) +
|
Parent::recycle();
|
||||||
glm::clamp(dimensions % pageDimensions, glm::uvec3(0), glm::uvec3(1));
|
GL45VariableAllocationTexture::manageMemory();
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t SparseInfo::getPageCount(const uvec3& dimensions) const {
|
|
||||||
auto pageCounts = getPageCounts(dimensions);
|
|
||||||
return pageCounts.x * pageCounts.y * pageCounts.z;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
uint32_t SparseInfo::getSize() const {
|
|
||||||
return allocatedPages * pageBytes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GL45Backend::initTextureManagementStage() {
|
void GL45Backend::initTextureManagementStage() {
|
||||||
|
@ -171,6 +103,11 @@ void GL45Backend::initTextureManagementStage() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using GL45Texture = GL45Backend::GL45Texture;
|
||||||
|
|
||||||
|
GL45Texture::GL45Texture(const std::weak_ptr<GLBackend>& backend, const Texture& texture)
|
||||||
|
: GLTexture(backend, texture, allocate(texture)) {
|
||||||
|
}
|
||||||
|
|
||||||
GLuint GL45Texture::allocate(const Texture& texture) {
|
GLuint GL45Texture::allocate(const Texture& texture) {
|
||||||
GLuint result;
|
GLuint result;
|
||||||
|
@ -178,162 +115,37 @@ GLuint GL45Texture::allocate(const Texture& texture) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
GLuint GL45Backend::getTextureID(const TexturePointer& texture, bool transfer) {
|
|
||||||
return GL45Texture::getId<GL45Texture>(*this, texture, transfer);
|
|
||||||
}
|
|
||||||
|
|
||||||
GL45Texture::GL45Texture(const std::weak_ptr<GLBackend>& backend, const Texture& texture, GLuint externalId)
|
|
||||||
: GLTexture(backend, texture, externalId), _sparseInfo(*this)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
GL45Texture::GL45Texture(const std::weak_ptr<GLBackend>& backend, const Texture& texture, bool transferrable)
|
|
||||||
: GLTexture(backend, texture, allocate(texture), transferrable), _sparseInfo(*this)
|
|
||||||
{
|
|
||||||
|
|
||||||
auto theBackend = _backend.lock();
|
|
||||||
if (_transferrable && theBackend && theBackend->isTextureManagementSparseEnabled()) {
|
|
||||||
_sparseInfo.maybeMakeSparse();
|
|
||||||
if (_sparseInfo.sparse) {
|
|
||||||
Backend::incrementTextureGPUSparseCount();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GL45Texture::~GL45Texture() {
|
|
||||||
// Remove this texture from the candidate list of derezzable textures
|
|
||||||
if (_transferrable) {
|
|
||||||
auto mipLevels = usedMipLevels();
|
|
||||||
Lock lock(texturesByMipCountsMutex);
|
|
||||||
if (texturesByMipCounts.count(mipLevels)) {
|
|
||||||
auto& textures = texturesByMipCounts[mipLevels];
|
|
||||||
textures.erase(this);
|
|
||||||
if (textures.empty()) {
|
|
||||||
texturesByMipCounts.erase(mipLevels);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_sparseInfo.sparse) {
|
|
||||||
Backend::decrementTextureGPUSparseCount();
|
|
||||||
|
|
||||||
// Experimenation suggests that allocating sparse textures on one context/thread and deallocating
|
|
||||||
// them on another is buggy. So for sparse textures we need to queue a lambda with the deallocation
|
|
||||||
// callls to the transfer thread
|
|
||||||
auto id = _id;
|
|
||||||
// Set the class _id to 0 so we don't try to double delete
|
|
||||||
const_cast<GLuint&>(_id) = 0;
|
|
||||||
std::list<std::function<void()>> destructionFunctions;
|
|
||||||
|
|
||||||
uint8_t maxFace = (uint8_t)((_target == GL_TEXTURE_CUBE_MAP) ? GLTexture::CUBE_NUM_FACES : 1);
|
|
||||||
auto maxSparseMip = std::min<uint16_t>(_maxMip, _sparseInfo.maxSparseLevel);
|
|
||||||
for (uint16_t mipLevel = _minMip; mipLevel <= maxSparseMip; ++mipLevel) {
|
|
||||||
auto mipDimensions = _gpuObject.evalMipDimensions(mipLevel);
|
|
||||||
destructionFunctions.push_back([id, maxFace, mipLevel, mipDimensions] {
|
|
||||||
glTexturePageCommitmentEXT(id, mipLevel, 0, 0, 0, mipDimensions.x, mipDimensions.y, maxFace, GL_FALSE);
|
|
||||||
});
|
|
||||||
|
|
||||||
auto deallocatedPages = _sparseInfo.getPageCount(mipDimensions) * maxFace;
|
|
||||||
assert(deallocatedPages <= _sparseInfo.allocatedPages);
|
|
||||||
_sparseInfo.allocatedPages -= deallocatedPages;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (0 != _sparseInfo.allocatedPages) {
|
|
||||||
qCWarning(gpugl45logging) << "Allocated pages remaining " << _id << " " << _sparseInfo.allocatedPages;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto size = _size;
|
|
||||||
const_cast<GLuint&>(_size) = 0;
|
|
||||||
_textureTransferHelper->queueExecution([id, size, destructionFunctions] {
|
|
||||||
for (auto function : destructionFunctions) {
|
|
||||||
function();
|
|
||||||
}
|
|
||||||
glDeleteTextures(1, &id);
|
|
||||||
Backend::decrementTextureGPUCount();
|
|
||||||
Backend::updateTextureGPUMemoryUsage(size, 0);
|
|
||||||
Backend::updateTextureGPUSparseMemoryUsage(size, 0);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GL45Texture::withPreservedTexture(std::function<void()> f) const {
|
|
||||||
f();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GL45Texture::generateMips() const {
|
void GL45Texture::generateMips() const {
|
||||||
glGenerateTextureMipmap(_id);
|
glGenerateTextureMipmap(_id);
|
||||||
(void)CHECK_GL_ERROR();
|
(void)CHECK_GL_ERROR();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GL45Texture::allocateStorage() const {
|
void GL45Texture::copyMipFromTexture(uint16_t sourceMip, uint16_t targetMip) const {
|
||||||
if (_gpuObject.getTexelFormat().isCompressed()) {
|
const auto& texture = _gpuObject;
|
||||||
qFatal("Compressed textures not yet supported");
|
if (!texture.isStoredMipFaceAvailable(sourceMip)) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, 0);
|
size_t maxFace = GLTexture::getFaceCount(_target);
|
||||||
glTextureParameteri(_id, GL_TEXTURE_MAX_LEVEL, _maxMip - _minMip);
|
|
||||||
// Get the dimensions, accounting for the downgrade level
|
|
||||||
Vec3u dimensions = _gpuObject.evalMipDimensions(_minMip + _mipOffset);
|
|
||||||
glTextureStorage2D(_id, usedMipLevels(), _internalFormat, dimensions.x, dimensions.y);
|
|
||||||
(void)CHECK_GL_ERROR();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GL45Texture::updateSize() const {
|
|
||||||
if (_gpuObject.getTexelFormat().isCompressed()) {
|
|
||||||
qFatal("Compressed textures not yet supported");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_transferrable && _sparseInfo.sparse) {
|
|
||||||
auto size = _sparseInfo.getSize();
|
|
||||||
Backend::updateTextureGPUSparseMemoryUsage(_size, size);
|
|
||||||
setSize(size);
|
|
||||||
} else {
|
|
||||||
setSize(_gpuObject.evalTotalSize(_mipOffset));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GL45Texture::startTransfer() {
|
|
||||||
Parent::startTransfer();
|
|
||||||
_sparseInfo.update();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GL45Texture::continueTransfer() {
|
|
||||||
PROFILE_RANGE(render_gpu_gl, "continueTransfer")
|
|
||||||
size_t maxFace = GL_TEXTURE_CUBE_MAP == _target ? CUBE_NUM_FACES : 1;
|
|
||||||
for (uint8_t face = 0; face < maxFace; ++face) {
|
for (uint8_t face = 0; face < maxFace; ++face) {
|
||||||
for (uint16_t mipLevel = _minMip; mipLevel <= _maxMip; ++mipLevel) {
|
auto size = texture.evalMipDimensions(sourceMip);
|
||||||
auto size = _gpuObject.evalMipDimensions(mipLevel);
|
auto mipData = texture.accessStoredMipFace(sourceMip, face);
|
||||||
if (_sparseInfo.sparse && mipLevel <= _sparseInfo.maxSparseLevel) {
|
GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(texture.getTexelFormat(), mipData->getFormat());
|
||||||
glTexturePageCommitmentEXT(_id, mipLevel, 0, 0, face, size.x, size.y, 1, GL_TRUE);
|
if (GL_TEXTURE_2D == _target) {
|
||||||
_sparseInfo.allocatedPages += _sparseInfo.getPageCount(size);
|
glTextureSubImage2D(_id, targetMip, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mipData->readData());
|
||||||
}
|
} else if (GL_TEXTURE_CUBE_MAP == _target) {
|
||||||
if (_gpuObject.isStoredMipFaceAvailable(mipLevel, face)) {
|
// DSA ARB does not work on AMD, so use EXT
|
||||||
PROFILE_RANGE_EX(render_gpu_gl, "texSubImage", 0x0000ffff, (size.x * size.y * maxFace / 1024));
|
// unless EXT is not available on the driver
|
||||||
|
if (glTextureSubImage2DEXT) {
|
||||||
auto mip = _gpuObject.accessStoredMipFace(mipLevel, face);
|
auto target = GLTexture::CUBE_FACE_LAYOUT[face];
|
||||||
GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), mip->getFormat());
|
glTextureSubImage2DEXT(_id, target, targetMip, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mipData->readData());
|
||||||
if (GL_TEXTURE_2D == _target) {
|
} else {
|
||||||
glTextureSubImage2D(_id, mipLevel, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mip->readData());
|
glTextureSubImage3D(_id, targetMip, 0, 0, face, size.x, size.y, 1, texelFormat.format, texelFormat.type, mipData->readData());
|
||||||
} else if (GL_TEXTURE_CUBE_MAP == _target) {
|
|
||||||
// DSA ARB does not work on AMD, so use EXT
|
|
||||||
// unless EXT is not available on the driver
|
|
||||||
if (glTextureSubImage2DEXT) {
|
|
||||||
auto target = CUBE_FACE_LAYOUT[face];
|
|
||||||
glTextureSubImage2DEXT(_id, target, mipLevel, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mip->readData());
|
|
||||||
} else {
|
|
||||||
glTextureSubImage3D(_id, mipLevel, 0, 0, face, size.x, size.y, 1, texelFormat.format, texelFormat.type, mip->readData());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Q_ASSERT(false);
|
|
||||||
}
|
|
||||||
(void)CHECK_GL_ERROR();
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Q_ASSERT(false);
|
||||||
}
|
}
|
||||||
|
(void)CHECK_GL_ERROR();
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GL45Texture::finishTransfer() {
|
|
||||||
Parent::finishTransfer();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GL45Texture::syncSampler() const {
|
void GL45Texture::syncSampler() const {
|
||||||
|
@ -353,163 +165,66 @@ void GL45Texture::syncSampler() const {
|
||||||
glTextureParameteri(_id, GL_TEXTURE_WRAP_S, WRAP_MODES[sampler.getWrapModeU()]);
|
glTextureParameteri(_id, GL_TEXTURE_WRAP_S, WRAP_MODES[sampler.getWrapModeU()]);
|
||||||
glTextureParameteri(_id, GL_TEXTURE_WRAP_T, WRAP_MODES[sampler.getWrapModeV()]);
|
glTextureParameteri(_id, GL_TEXTURE_WRAP_T, WRAP_MODES[sampler.getWrapModeV()]);
|
||||||
glTextureParameteri(_id, GL_TEXTURE_WRAP_R, WRAP_MODES[sampler.getWrapModeW()]);
|
glTextureParameteri(_id, GL_TEXTURE_WRAP_R, WRAP_MODES[sampler.getWrapModeW()]);
|
||||||
|
glTextureParameterf(_id, GL_TEXTURE_MAX_ANISOTROPY_EXT, sampler.getMaxAnisotropy());
|
||||||
glTextureParameterfv(_id, GL_TEXTURE_BORDER_COLOR, (const float*)&sampler.getBorderColor());
|
glTextureParameterfv(_id, GL_TEXTURE_BORDER_COLOR, (const float*)&sampler.getBorderColor());
|
||||||
|
|
||||||
|
#if 0
|
||||||
// FIXME account for mip offsets here
|
// FIXME account for mip offsets here
|
||||||
auto baseMip = std::max<uint16_t>(sampler.getMipOffset(), _minMip);
|
auto baseMip = std::max<uint16_t>(sampler.getMipOffset(), _minMip);
|
||||||
glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, baseMip);
|
glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, baseMip);
|
||||||
glTextureParameterf(_id, GL_TEXTURE_MIN_LOD, (float)sampler.getMinMip());
|
glTextureParameterf(_id, GL_TEXTURE_MIN_LOD, (float)sampler.getMinMip());
|
||||||
glTextureParameterf(_id, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip() - _mipOffset));
|
glTextureParameterf(_id, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip() - _mipOffset));
|
||||||
glTextureParameterf(_id, GL_TEXTURE_MAX_ANISOTROPY_EXT, sampler.getMaxAnisotropy());
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void GL45Texture::postTransfer() {
|
using GL45FixedAllocationTexture = GL45Backend::GL45FixedAllocationTexture;
|
||||||
Parent::postTransfer();
|
|
||||||
auto mipLevels = usedMipLevels();
|
|
||||||
if (_transferrable && mipLevels > 1 && _minMip < _sparseInfo.maxSparseLevel) {
|
|
||||||
Lock lock(texturesByMipCountsMutex);
|
|
||||||
texturesByMipCounts[mipLevels].insert(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GL45Texture::stripToMip(uint16_t newMinMip) {
|
GL45FixedAllocationTexture::GL45FixedAllocationTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture) : GL45Texture(backend, texture), _size(texture.evalTotalSize()) {
|
||||||
if (newMinMip < _minMip) {
|
allocateStorage();
|
||||||
qCWarning(gpugl45logging) << "Cannot decrease the min mip";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_sparseInfo.sparse && newMinMip > _sparseInfo.maxSparseLevel) {
|
|
||||||
qCWarning(gpugl45logging) << "Cannot increase the min mip into the mip tail";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
PROFILE_RANGE(render_gpu_gl, "GL45Texture::stripToMip");
|
|
||||||
|
|
||||||
auto mipLevels = usedMipLevels();
|
|
||||||
{
|
|
||||||
Lock lock(texturesByMipCountsMutex);
|
|
||||||
assert(0 != texturesByMipCounts.count(mipLevels));
|
|
||||||
assert(0 != texturesByMipCounts[mipLevels].count(this));
|
|
||||||
texturesByMipCounts[mipLevels].erase(this);
|
|
||||||
if (texturesByMipCounts[mipLevels].empty()) {
|
|
||||||
texturesByMipCounts.erase(mipLevels);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we weren't generating mips before, we need to now that we're stripping down mip levels.
|
|
||||||
if (!_gpuObject.isAutogenerateMips()) {
|
|
||||||
qCDebug(gpugl45logging) << "Force mip generation for texture";
|
|
||||||
glGenerateTextureMipmap(_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
uint8_t maxFace = (uint8_t)((_target == GL_TEXTURE_CUBE_MAP) ? GLTexture::CUBE_NUM_FACES : 1);
|
|
||||||
if (_sparseInfo.sparse) {
|
|
||||||
for (uint16_t mip = _minMip; mip < newMinMip; ++mip) {
|
|
||||||
auto id = _id;
|
|
||||||
auto mipDimensions = _gpuObject.evalMipDimensions(mip);
|
|
||||||
_textureTransferHelper->queueExecution([id, mip, mipDimensions, maxFace] {
|
|
||||||
glTexturePageCommitmentEXT(id, mip, 0, 0, 0, mipDimensions.x, mipDimensions.y, maxFace, GL_FALSE);
|
|
||||||
});
|
|
||||||
|
|
||||||
auto deallocatedPages = _sparseInfo.getPageCount(mipDimensions) * maxFace;
|
|
||||||
assert(deallocatedPages < _sparseInfo.allocatedPages);
|
|
||||||
_sparseInfo.allocatedPages -= deallocatedPages;
|
|
||||||
}
|
|
||||||
_minMip = newMinMip;
|
|
||||||
} else {
|
|
||||||
GLuint oldId = _id;
|
|
||||||
// Find the distance between the old min mip and the new one
|
|
||||||
uint16 mipDelta = newMinMip - _minMip;
|
|
||||||
_mipOffset += mipDelta;
|
|
||||||
const_cast<uint16&>(_maxMip) -= mipDelta;
|
|
||||||
auto newLevels = usedMipLevels();
|
|
||||||
|
|
||||||
// Create and setup the new texture (allocate)
|
|
||||||
{
|
|
||||||
Vec3u newDimensions = _gpuObject.evalMipDimensions(_mipOffset);
|
|
||||||
PROFILE_RANGE_EX(render_gpu_gl, "Re-Allocate", 0xff0000ff, (newDimensions.x * newDimensions.y));
|
|
||||||
|
|
||||||
glCreateTextures(_target, 1, &const_cast<GLuint&>(_id));
|
|
||||||
glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, 0);
|
|
||||||
glTextureParameteri(_id, GL_TEXTURE_MAX_LEVEL, _maxMip - _minMip);
|
|
||||||
glTextureStorage2D(_id, newLevels, _internalFormat, newDimensions.x, newDimensions.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy the contents of the old texture to the new
|
|
||||||
{
|
|
||||||
PROFILE_RANGE(render_gpu_gl, "Blit");
|
|
||||||
// Preferred path only available in 4.3
|
|
||||||
for (uint16 targetMip = _minMip; targetMip <= _maxMip; ++targetMip) {
|
|
||||||
uint16 sourceMip = targetMip + mipDelta;
|
|
||||||
Vec3u mipDimensions = _gpuObject.evalMipDimensions(targetMip + _mipOffset);
|
|
||||||
for (GLenum target : getFaceTargets(_target)) {
|
|
||||||
glCopyImageSubData(
|
|
||||||
oldId, target, sourceMip, 0, 0, 0,
|
|
||||||
_id, target, targetMip, 0, 0, 0,
|
|
||||||
mipDimensions.x, mipDimensions.y, 1
|
|
||||||
);
|
|
||||||
(void)CHECK_GL_ERROR();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
glDeleteTextures(1, &oldId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Re-sync the sampler to force access to the new mip level
|
|
||||||
syncSampler();
|
syncSampler();
|
||||||
updateSize();
|
}
|
||||||
|
|
||||||
// Re-insert into the texture-by-mips map if appropriate
|
GL45FixedAllocationTexture::~GL45FixedAllocationTexture() {
|
||||||
mipLevels = usedMipLevels();
|
}
|
||||||
if (mipLevels > 1 && (!_sparseInfo.sparse || _minMip < _sparseInfo.maxSparseLevel)) {
|
|
||||||
Lock lock(texturesByMipCountsMutex);
|
void GL45FixedAllocationTexture::allocateStorage() const {
|
||||||
texturesByMipCounts[mipLevels].insert(this);
|
const GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat());
|
||||||
|
const auto dimensions = _gpuObject.getDimensions();
|
||||||
|
const auto mips = _gpuObject.evalNumMips();
|
||||||
|
glTextureStorage2D(_id, mips, texelFormat.internalFormat, dimensions.x, dimensions.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GL45FixedAllocationTexture::syncSampler() const {
|
||||||
|
Parent::syncSampler();
|
||||||
|
const Sampler& sampler = _gpuObject.getSampler();
|
||||||
|
auto baseMip = std::max<uint16_t>(sampler.getMipOffset(), sampler.getMinMip());
|
||||||
|
glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, baseMip);
|
||||||
|
glTextureParameterf(_id, GL_TEXTURE_MIN_LOD, (float)sampler.getMinMip());
|
||||||
|
glTextureParameterf(_id, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Renderbuffer attachment textures
|
||||||
|
using GL45AttachmentTexture = GL45Backend::GL45AttachmentTexture;
|
||||||
|
|
||||||
|
GL45AttachmentTexture::GL45AttachmentTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture) : GL45FixedAllocationTexture(backend, texture) {
|
||||||
|
Backend::updateTextureGPUFramebufferMemoryUsage(0, size());
|
||||||
|
}
|
||||||
|
|
||||||
|
GL45AttachmentTexture::~GL45AttachmentTexture() {
|
||||||
|
Backend::updateTextureGPUFramebufferMemoryUsage(size(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strict resource textures
|
||||||
|
using GL45StrictResourceTexture = GL45Backend::GL45StrictResourceTexture;
|
||||||
|
|
||||||
|
GL45StrictResourceTexture::GL45StrictResourceTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture) : GL45FixedAllocationTexture(backend, texture) {
|
||||||
|
auto mipLevels = _gpuObject.evalNumMips();
|
||||||
|
for (uint16_t sourceMip = 0; sourceMip < mipLevels; ++sourceMip) {
|
||||||
|
uint16_t targetMip = sourceMip;
|
||||||
|
copyMipFromTexture(sourceMip, targetMip);
|
||||||
|
}
|
||||||
|
if (texture.isAutogenerateMips()) {
|
||||||
|
generateMips();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GL45Texture::updateMips() {
|
|
||||||
if (!_sparseInfo.sparse) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto newMinMip = std::min<uint16_t>(_gpuObject.minMip(), _sparseInfo.maxSparseLevel);
|
|
||||||
if (_minMip < newMinMip) {
|
|
||||||
stripToMip(newMinMip);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GL45Texture::derez() {
|
|
||||||
if (_sparseInfo.sparse) {
|
|
||||||
assert(_minMip < _sparseInfo.maxSparseLevel);
|
|
||||||
}
|
|
||||||
assert(_minMip < _maxMip);
|
|
||||||
assert(_transferrable);
|
|
||||||
stripToMip(_minMip + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GL45Backend::derezTextures() const {
|
|
||||||
if (GLTexture::getMemoryPressure() < 1.0f) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Lock lock(texturesByMipCountsMutex);
|
|
||||||
if (texturesByMipCounts.empty()) {
|
|
||||||
// No available textures to derez
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto mipLevel = texturesByMipCounts.rbegin()->first;
|
|
||||||
if (mipLevel <= 1) {
|
|
||||||
// No mips available to remove
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
GL45Texture* targetTexture = nullptr;
|
|
||||||
{
|
|
||||||
auto& textures = texturesByMipCounts[mipLevel];
|
|
||||||
assert(!textures.empty());
|
|
||||||
targetTexture = *textures.begin();
|
|
||||||
}
|
|
||||||
lock.unlock();
|
|
||||||
targetTexture->derez();
|
|
||||||
}
|
|
||||||
|
|
888
libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp
Normal file
888
libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp
Normal file
|
@ -0,0 +1,888 @@
|
||||||
|
//
|
||||||
|
// GL45BackendTexture.cpp
|
||||||
|
// libraries/gpu/src/gpu
|
||||||
|
//
|
||||||
|
// Created by Sam Gateau on 1/19/2015.
|
||||||
|
// Copyright 2014 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 "GL45Backend.h"
|
||||||
|
#include <mutex>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <glm/gtx/component_wise.hpp>
|
||||||
|
|
||||||
|
#include <QtCore/QDebug>
|
||||||
|
#include <QtCore/QThread>
|
||||||
|
|
||||||
|
#include <NumericalConstants.h>
|
||||||
|
#include "../gl/GLTexelFormat.h"
|
||||||
|
|
||||||
|
using namespace gpu;
|
||||||
|
using namespace gpu::gl;
|
||||||
|
using namespace gpu::gl45;
|
||||||
|
|
||||||
|
// Variable sized textures
|
||||||
|
using GL45VariableAllocationTexture = GL45Backend::GL45VariableAllocationTexture;
|
||||||
|
using MemoryPressureState = GL45VariableAllocationTexture::MemoryPressureState;
|
||||||
|
|
||||||
|
std::list<TextureWeakPointer> GL45VariableAllocationTexture::_memoryManagedTextures;
|
||||||
|
MemoryPressureState GL45VariableAllocationTexture::_memoryPressureState = MemoryPressureState::Idle;
|
||||||
|
std::atomic<bool> GL45VariableAllocationTexture::_memoryPressureStateStale { false };
|
||||||
|
const uvec3 GL45VariableAllocationTexture::INITIAL_MIP_TRANSFER_DIMENSIONS { 64, 64, 1 };
|
||||||
|
|
||||||
|
#define OVERSUBSCRIBED_PRESSURE_VALUE 0.95f
|
||||||
|
#define UNDERSUBSCRIBED_PRESSURE_VALUE 0.85f
|
||||||
|
#define DEFAULT_ALLOWED_TEXTURE_MEMORY_MB ((size_t)1024)
|
||||||
|
|
||||||
|
static const size_t DEFAULT_ALLOWED_TEXTURE_MEMORY = MB_TO_BYTES(DEFAULT_ALLOWED_TEXTURE_MEMORY_MB);
|
||||||
|
|
||||||
|
using QueuePair = std::pair<TextureWeakPointer, uint32_t>;
|
||||||
|
class QueuePairLess {
|
||||||
|
public:
|
||||||
|
bool operator()(const QueuePair& a, const QueuePair& b) {
|
||||||
|
return a.second < b.second;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
class QueuePairGreater {
|
||||||
|
public:
|
||||||
|
bool operator()(const QueuePair& a, const QueuePair& b) {
|
||||||
|
return a.second > b.second;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
using DemoteQueue = std::priority_queue<QueuePair, std::vector<QueuePair>, QueuePairLess>;
|
||||||
|
using PromoteQueue = std::priority_queue<QueuePair, std::vector<QueuePair>, QueuePairGreater>;
|
||||||
|
using TransferQueue = std::queue<TextureWeakPointer>;
|
||||||
|
static DemoteQueue demoteQueue;
|
||||||
|
static PromoteQueue promoteQueue;
|
||||||
|
static TransferQueue transferQueue;
|
||||||
|
|
||||||
|
void GL45VariableAllocationTexture::addMemoryManagedTexture(const TexturePointer& texturePointer) {
|
||||||
|
_memoryManagedTextures.push_back(texturePointer);
|
||||||
|
GL45VariableAllocationTexture* object = Backend::getGPUObject<GL45VariableAllocationTexture>(*texturePointer);
|
||||||
|
switch (_memoryPressureState) {
|
||||||
|
case MemoryPressureState::Oversubscribed:
|
||||||
|
if (object->canDemote()) {
|
||||||
|
demoteQueue.push({ texturePointer, object->size() });
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MemoryPressureState::Undersubscribed:
|
||||||
|
if (object->canPromote()) {
|
||||||
|
promoteQueue.push({ texturePointer, object->size() });
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MemoryPressureState::Transfer:
|
||||||
|
if (object->hasPendingTransfers()) {
|
||||||
|
transferQueue.push( texturePointer );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MemoryPressureState::Idle:
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Q_UNREACHABLE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GL45VariableAllocationTexture::updateMemoryPressure() {
|
||||||
|
static size_t lastAllowedMemoryAllocation = gpu::Texture::getAllowedGPUMemoryUsage();
|
||||||
|
size_t allowedMemoryAllocation = gpu::Texture::getAllowedGPUMemoryUsage();
|
||||||
|
if (allowedMemoryAllocation != lastAllowedMemoryAllocation) {
|
||||||
|
_memoryPressureStateStale = true;
|
||||||
|
lastAllowedMemoryAllocation = allowedMemoryAllocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_memoryPressureStateStale) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_memoryPressureStateStale = false;
|
||||||
|
// Clear any defunct textures
|
||||||
|
_memoryManagedTextures.remove_if([&](const TextureWeakPointer& weakPointer) {
|
||||||
|
return weakPointer.expired();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Convert weak pointers to strong
|
||||||
|
std::list<TexturePointer> strongTextures; {
|
||||||
|
std::transform(
|
||||||
|
_memoryManagedTextures.begin(), _memoryManagedTextures.end(),
|
||||||
|
std::back_inserter(strongTextures),
|
||||||
|
[](const TextureWeakPointer& p) { return p.lock(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t totalVariableMemoryAllocation = 0;
|
||||||
|
size_t idealMemoryAllocation = 0;
|
||||||
|
bool canDemote = false;
|
||||||
|
bool canPromote = false;
|
||||||
|
bool hasTransfers = false;
|
||||||
|
for (const auto& texture : strongTextures) {
|
||||||
|
// Race conditions can still leave nulls in the list, so we need to check
|
||||||
|
if (!texture) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
idealMemoryAllocation += texture->evalTotalSize();
|
||||||
|
GL45VariableAllocationTexture* object = Backend::getGPUObject<GL45VariableAllocationTexture>(*texture);
|
||||||
|
totalVariableMemoryAllocation += object->size();
|
||||||
|
canDemote |= object->canDemote();
|
||||||
|
canPromote |= object->canPromote();
|
||||||
|
hasTransfers |= object->hasPendingTransfers();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t unallocated = idealMemoryAllocation - totalVariableMemoryAllocation;
|
||||||
|
if (0 == allowedMemoryAllocation) {
|
||||||
|
allowedMemoryAllocation = DEFAULT_ALLOWED_TEXTURE_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
float pressure = (float)totalVariableMemoryAllocation / (float)allowedMemoryAllocation;
|
||||||
|
|
||||||
|
auto newState = MemoryPressureState::Idle;
|
||||||
|
if (pressure > OVERSUBSCRIBED_PRESSURE_VALUE && canDemote) {
|
||||||
|
newState = MemoryPressureState::Oversubscribed;
|
||||||
|
} else if (pressure < UNDERSUBSCRIBED_PRESSURE_VALUE && unallocated != 0 && canPromote) {
|
||||||
|
newState = MemoryPressureState::Undersubscribed;
|
||||||
|
} else if (hasTransfers) {
|
||||||
|
newState = MemoryPressureState::Transfer;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newState != _memoryPressureState) {
|
||||||
|
_memoryPressureState = newState;
|
||||||
|
|
||||||
|
demoteQueue = DemoteQueue();
|
||||||
|
promoteQueue = PromoteQueue();
|
||||||
|
transferQueue = TransferQueue();
|
||||||
|
|
||||||
|
switch (_memoryPressureState) {
|
||||||
|
case MemoryPressureState::Idle:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MemoryPressureState::Oversubscribed:
|
||||||
|
for (const auto& texture : strongTextures) {
|
||||||
|
if (!texture) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
GL45VariableAllocationTexture* object = Backend::getGPUObject<GL45VariableAllocationTexture>(*texture);
|
||||||
|
if (object->canDemote()) {
|
||||||
|
demoteQueue.push({ texture, object->size() });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MemoryPressureState::Undersubscribed:
|
||||||
|
for (const auto& texture : strongTextures) {
|
||||||
|
if (!texture) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
GL45VariableAllocationTexture* object = Backend::getGPUObject<GL45VariableAllocationTexture>(*texture);
|
||||||
|
if (object->canPromote()) {
|
||||||
|
promoteQueue.push({ texture, object->size() });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MemoryPressureState::Transfer:
|
||||||
|
for (const auto& texture : strongTextures) {
|
||||||
|
if (!texture) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
GL45VariableAllocationTexture* object = Backend::getGPUObject<GL45VariableAllocationTexture>(*texture);
|
||||||
|
if (object->hasPendingTransfers()) {
|
||||||
|
transferQueue.push(texture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Q_UNREACHABLE();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GL45VariableAllocationTexture::processWorkQueues() {
|
||||||
|
switch (_memoryPressureState) {
|
||||||
|
case MemoryPressureState::Idle:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MemoryPressureState::Oversubscribed:
|
||||||
|
// Grab the first item off the demote queue
|
||||||
|
while (!demoteQueue.empty()) {
|
||||||
|
auto demoteTarget = demoteQueue.top();
|
||||||
|
demoteQueue.pop();
|
||||||
|
auto texture = demoteTarget.first.lock();
|
||||||
|
if (!texture) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
GL45VariableAllocationTexture* object = Backend::getGPUObject<GL45VariableAllocationTexture>(*texture);
|
||||||
|
if (!object->canDemote()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//qDebug() << "QQQ executing demote for " << texture->source().c_str();
|
||||||
|
object->demote();
|
||||||
|
// if the object can be further demoted, reinsert into the queue
|
||||||
|
if (object->canDemote()) {
|
||||||
|
demoteQueue.push({ demoteTarget.first, object->size() });
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (demoteQueue.empty()) {
|
||||||
|
_memoryPressureState = MemoryPressureState::Idle;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MemoryPressureState::Undersubscribed:
|
||||||
|
while (!promoteQueue.empty()) {
|
||||||
|
auto promoteTarget = promoteQueue.top();
|
||||||
|
promoteQueue.pop();
|
||||||
|
auto texture = promoteTarget.first.lock();
|
||||||
|
if (!texture) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
GL45VariableAllocationTexture* object = Backend::getGPUObject<GL45VariableAllocationTexture>(*texture);
|
||||||
|
if (!object->canPromote()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
//qDebug() << "QQQ executing promote for " << texture->source().c_str();
|
||||||
|
object->promote();
|
||||||
|
if (object->canPromote()) {
|
||||||
|
promoteQueue.push({ promoteTarget.first, object->size() });
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (promoteQueue.empty()) {
|
||||||
|
_memoryPressureState = MemoryPressureState::Idle;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MemoryPressureState::Transfer:
|
||||||
|
while (!transferQueue.empty()) {
|
||||||
|
auto weakTexture = transferQueue.front();
|
||||||
|
transferQueue.pop();
|
||||||
|
auto texture = weakTexture.lock();
|
||||||
|
if (!texture) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
GL45VariableAllocationTexture* object = Backend::getGPUObject<GL45VariableAllocationTexture>(*texture);
|
||||||
|
if (!object->hasPendingTransfers()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
//qDebug() << "QQQ executing transfer for " << texture->source().c_str();
|
||||||
|
object->executeNextTransfer();
|
||||||
|
if (object->hasPendingTransfers()) {
|
||||||
|
transferQueue.push(weakTexture);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (transferQueue.empty()) {
|
||||||
|
_memoryPressureState = MemoryPressureState::Idle;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Q_UNREACHABLE();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GL45VariableAllocationTexture::manageMemory() {
|
||||||
|
static auto lastProcessTime = usecTimestampNow();
|
||||||
|
auto now = usecTimestampNow();
|
||||||
|
auto interval = now - lastProcessTime;
|
||||||
|
if (interval > (USECS_PER_MSEC * 20)) {
|
||||||
|
lastProcessTime = now;
|
||||||
|
updateMemoryPressure();
|
||||||
|
processWorkQueues();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GL45VariableAllocationTexture::GL45VariableAllocationTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture) : GL45Texture(backend, texture) {
|
||||||
|
}
|
||||||
|
|
||||||
|
GL45VariableAllocationTexture::~GL45VariableAllocationTexture() {
|
||||||
|
_memoryPressureStateStale = true;
|
||||||
|
Backend::updateTextureGPUMemoryUsage(_size, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GL45VariableAllocationTexture::executeNextTransfer() {
|
||||||
|
if (!_pendingTransfers.empty()) {
|
||||||
|
_pendingTransfers.front()();
|
||||||
|
_pendingTransfers.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Managed size resource textures
|
||||||
|
using GL45ResourceTexture = GL45Backend::GL45ResourceTexture;
|
||||||
|
|
||||||
|
GL45ResourceTexture::GL45ResourceTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture) : GL45VariableAllocationTexture(backend, texture) {
|
||||||
|
auto mipLevels = texture.evalNumMips();
|
||||||
|
_allocatedMip = mipLevels;
|
||||||
|
uvec3 mipDimensions;
|
||||||
|
for (uint16_t mip = 0; mip < mipLevels; ++mip) {
|
||||||
|
if (glm::all(glm::lessThanEqual(texture.evalMipDimensions(mip), INITIAL_MIP_TRANSFER_DIMENSIONS))) {
|
||||||
|
_maxAllocatedMip = _populatedMip = mip;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t allocatedMip = _populatedMip - std::min<uint16_t>(_populatedMip, 2);
|
||||||
|
allocateStorage(allocatedMip);
|
||||||
|
_memoryPressureStateStale = true;
|
||||||
|
copyMipsFromTexture();
|
||||||
|
syncSampler();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void GL45ResourceTexture::allocateStorage(uint16 allocatedMip) {
|
||||||
|
_allocatedMip = allocatedMip;
|
||||||
|
const GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat());
|
||||||
|
const auto dimensions = _gpuObject.evalMipDimensions(_allocatedMip);
|
||||||
|
const auto totalMips = _gpuObject.evalNumMips();
|
||||||
|
const auto mips = totalMips - _allocatedMip;
|
||||||
|
glTextureStorage2D(_id, mips, texelFormat.internalFormat, dimensions.x, dimensions.y);
|
||||||
|
auto mipLevels = _gpuObject.evalNumMips();
|
||||||
|
_size = 0;
|
||||||
|
for (uint16_t mip = _allocatedMip; mip < mipLevels; ++mip) {
|
||||||
|
_size += _gpuObject.evalMipSize(mip);
|
||||||
|
}
|
||||||
|
Backend::updateTextureGPUMemoryUsage(0, _size);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void GL45ResourceTexture::copyMipsFromTexture() {
|
||||||
|
auto mipLevels = _gpuObject.evalNumMips();
|
||||||
|
for (uint16_t sourceMip = _populatedMip; sourceMip < mipLevels; ++sourceMip) {
|
||||||
|
uint16_t targetMip = sourceMip - _allocatedMip;
|
||||||
|
copyMipFromTexture(sourceMip, targetMip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GL45ResourceTexture::syncSampler() const {
|
||||||
|
Parent::syncSampler();
|
||||||
|
const Sampler& sampler = _gpuObject.getSampler();
|
||||||
|
uint16_t maxMip = _gpuObject.evalNumMips() - _allocatedMip;
|
||||||
|
auto minMip = std::max<uint16_t>(sampler.getMipOffset(), sampler.getMinMip());
|
||||||
|
minMip = std::min<uint16_t>(minMip, maxMip);
|
||||||
|
glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, _populatedMip - _allocatedMip);
|
||||||
|
glTextureParameterf(_id, GL_TEXTURE_MIN_LOD, (float)minMip);
|
||||||
|
glTextureParameterf(_id, GL_TEXTURE_MAX_LOD, (float)maxMip);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GL45ResourceTexture::promote() {
|
||||||
|
Q_ASSERT(_allocatedMip > 0);
|
||||||
|
GLuint oldId = _id;
|
||||||
|
uint32_t oldSize = _size;
|
||||||
|
// create new texture
|
||||||
|
const_cast<GLuint&>(_id) = allocate(_gpuObject);
|
||||||
|
uint16_t oldAllocatedMip = _allocatedMip;
|
||||||
|
// allocate storage for new level
|
||||||
|
allocateStorage(_allocatedMip - std::min<uint16_t>(_allocatedMip, 2));
|
||||||
|
uint16_t mips = _gpuObject.evalNumMips();
|
||||||
|
// copy pre-existing mips
|
||||||
|
for (uint16_t mip = _populatedMip; mip < mips; ++mip) {
|
||||||
|
auto mipDimensions = _gpuObject.evalMipDimensions(mip);
|
||||||
|
uint16_t targetMip = mip - _allocatedMip;
|
||||||
|
uint16_t sourceMip = mip - oldAllocatedMip;
|
||||||
|
auto faces = getFaceCount(_target);
|
||||||
|
for (uint8_t face = 0; face < faces; ++face) {
|
||||||
|
glCopyImageSubData(
|
||||||
|
oldId, _target, sourceMip, 0, 0, face,
|
||||||
|
_id, _target, targetMip, 0, 0, face,
|
||||||
|
mipDimensions.x, mipDimensions.y, 1
|
||||||
|
);
|
||||||
|
(void)CHECK_GL_ERROR();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// destroy the old texture
|
||||||
|
glDeleteTextures(1, &oldId);
|
||||||
|
// update the memory usage
|
||||||
|
Backend::updateTextureGPUMemoryUsage(oldSize, 0);
|
||||||
|
_memoryPressureStateStale = true;
|
||||||
|
syncSampler();
|
||||||
|
populateTransferQueue();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GL45ResourceTexture::demote() {
|
||||||
|
Q_ASSERT(_allocatedMip < _maxAllocatedMip);
|
||||||
|
auto oldId = _id;
|
||||||
|
auto oldSize = _size;
|
||||||
|
const_cast<GLuint&>(_id) = allocate(_gpuObject);
|
||||||
|
allocateStorage(_allocatedMip + 1);
|
||||||
|
_populatedMip = std::max(_populatedMip, _allocatedMip);
|
||||||
|
uint16_t mips = _gpuObject.evalNumMips();
|
||||||
|
// copy pre-existing mips
|
||||||
|
for (uint16_t mip = _populatedMip; mip < mips; ++mip) {
|
||||||
|
auto mipDimensions = _gpuObject.evalMipDimensions(mip);
|
||||||
|
uint16_t targetMip = mip - _allocatedMip;
|
||||||
|
uint16_t sourceMip = targetMip + 1;
|
||||||
|
auto faces = getFaceCount(_target);
|
||||||
|
for (uint8_t face = 0; face < faces; ++face) {
|
||||||
|
glCopyImageSubData(
|
||||||
|
oldId, _target, sourceMip, 0, 0, face,
|
||||||
|
_id, _target, targetMip, 0, 0, face,
|
||||||
|
mipDimensions.x, mipDimensions.y, 1
|
||||||
|
);
|
||||||
|
(void)CHECK_GL_ERROR();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// destroy the old texture
|
||||||
|
glDeleteTextures(1, &oldId);
|
||||||
|
// update the memory usage
|
||||||
|
Backend::updateTextureGPUMemoryUsage(oldSize, 0);
|
||||||
|
_memoryPressureStateStale = true;
|
||||||
|
syncSampler();
|
||||||
|
populateTransferQueue();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GL45ResourceTexture::populateTransferQueue() {
|
||||||
|
_pendingTransfers = std::queue<PromoteLambda>();
|
||||||
|
if (_populatedMip <= _allocatedMip) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int16_t mip = _populatedMip - 1; mip >= _allocatedMip; --mip) {
|
||||||
|
// FIXME break down the transfers into chunks so that no single transfer is
|
||||||
|
// consuming more than X bandwidth
|
||||||
|
_pendingTransfers.push([mip, this] {
|
||||||
|
Q_ASSERT(mip >= _allocatedMip);
|
||||||
|
// FIXME modify the copy mechanism to be incremental
|
||||||
|
copyMipFromTexture(mip, mip - _allocatedMip);
|
||||||
|
_populatedMip = mip;
|
||||||
|
syncSampler();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sparsely allocated, managed size resource textures
|
||||||
|
#if 0
|
||||||
|
#define SPARSE_PAGE_SIZE_OVERHEAD_ESTIMATE 1.3f
|
||||||
|
|
||||||
|
using GL45SparseResourceTexture = GL45Backend::GL45SparseResourceTexture;
|
||||||
|
|
||||||
|
GL45Texture::PageDimensionsMap GL45Texture::pageDimensionsByFormat;
|
||||||
|
Mutex GL45Texture::pageDimensionsMutex;
|
||||||
|
|
||||||
|
GL45Texture::PageDimensions GL45Texture::getPageDimensionsForFormat(const TextureTypeFormat& typeFormat) {
|
||||||
|
{
|
||||||
|
Lock lock(pageDimensionsMutex);
|
||||||
|
if (pageDimensionsByFormat.count(typeFormat)) {
|
||||||
|
return pageDimensionsByFormat[typeFormat];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GLint count = 0;
|
||||||
|
glGetInternalformativ(typeFormat.first, typeFormat.second, GL_NUM_VIRTUAL_PAGE_SIZES_ARB, 1, &count);
|
||||||
|
|
||||||
|
std::vector<uvec3> result;
|
||||||
|
if (count > 0) {
|
||||||
|
std::vector<GLint> x, y, z;
|
||||||
|
x.resize(count);
|
||||||
|
glGetInternalformativ(typeFormat.first, typeFormat.second, GL_VIRTUAL_PAGE_SIZE_X_ARB, 1, &x[0]);
|
||||||
|
y.resize(count);
|
||||||
|
glGetInternalformativ(typeFormat.first, typeFormat.second, GL_VIRTUAL_PAGE_SIZE_Y_ARB, 1, &y[0]);
|
||||||
|
z.resize(count);
|
||||||
|
glGetInternalformativ(typeFormat.first, typeFormat.second, GL_VIRTUAL_PAGE_SIZE_Z_ARB, 1, &z[0]);
|
||||||
|
|
||||||
|
result.resize(count);
|
||||||
|
for (GLint i = 0; i < count; ++i) {
|
||||||
|
result[i] = uvec3(x[i], y[i], z[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Lock lock(pageDimensionsMutex);
|
||||||
|
if (0 == pageDimensionsByFormat.count(typeFormat)) {
|
||||||
|
pageDimensionsByFormat[typeFormat] = result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
GL45Texture::PageDimensions GL45Texture::getPageDimensionsForFormat(GLenum target, GLenum format) {
|
||||||
|
return getPageDimensionsForFormat({ target, format });
|
||||||
|
}
|
||||||
|
bool GL45Texture::isSparseEligible(const Texture& texture) {
|
||||||
|
Q_ASSERT(TextureUsageType::RESOURCE == texture.getUsageType());
|
||||||
|
|
||||||
|
// Disabling sparse for the momemnt
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const auto allowedPageDimensions = getPageDimensionsForFormat(getGLTextureType(texture),
|
||||||
|
gl::GLTexelFormat::evalGLTexelFormatInternal(texture.getTexelFormat()));
|
||||||
|
const auto textureDimensions = texture.getDimensions();
|
||||||
|
for (const auto& pageDimensions : allowedPageDimensions) {
|
||||||
|
if (uvec3(0) == (textureDimensions % pageDimensions)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
GL45SparseResourceTexture::GL45SparseResourceTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture) : GL45VariableAllocationTexture(backend, texture) {
|
||||||
|
const GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat());
|
||||||
|
const uvec3 dimensions = _gpuObject.getDimensions();
|
||||||
|
auto allowedPageDimensions = getPageDimensionsForFormat(_target, texelFormat.internalFormat);
|
||||||
|
uint32_t pageDimensionsIndex = 0;
|
||||||
|
// In order to enable sparse the texture size must be an integer multiple of the page size
|
||||||
|
for (size_t i = 0; i < allowedPageDimensions.size(); ++i) {
|
||||||
|
pageDimensionsIndex = (uint32_t)i;
|
||||||
|
_pageDimensions = allowedPageDimensions[i];
|
||||||
|
// Is this texture an integer multiple of page dimensions?
|
||||||
|
if (uvec3(0) == (dimensions % _pageDimensions)) {
|
||||||
|
qCDebug(gpugl45logging) << "Enabling sparse for texture " << _gpuObject.source().c_str();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
glTextureParameteri(_id, GL_TEXTURE_SPARSE_ARB, GL_TRUE);
|
||||||
|
glTextureParameteri(_id, GL_VIRTUAL_PAGE_SIZE_INDEX_ARB, pageDimensionsIndex);
|
||||||
|
glGetTextureParameterIuiv(_id, GL_NUM_SPARSE_LEVELS_ARB, &_maxSparseLevel);
|
||||||
|
|
||||||
|
_pageBytes = _gpuObject.getTexelFormat().getSize();
|
||||||
|
_pageBytes *= _pageDimensions.x * _pageDimensions.y * _pageDimensions.z;
|
||||||
|
// Testing with a simple texture allocating app shows an estimated 20% GPU memory overhead for
|
||||||
|
// sparse textures as compared to non-sparse, so we acount for that here.
|
||||||
|
_pageBytes = (uint32_t)(_pageBytes * SPARSE_PAGE_SIZE_OVERHEAD_ESTIMATE);
|
||||||
|
|
||||||
|
//allocateStorage();
|
||||||
|
syncSampler();
|
||||||
|
}
|
||||||
|
|
||||||
|
GL45SparseResourceTexture::~GL45SparseResourceTexture() {
|
||||||
|
Backend::updateTextureGPUVirtualMemoryUsage(size(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
uvec3 GL45SparseResourceTexture::getPageCounts(const uvec3& dimensions) const {
|
||||||
|
auto result = (dimensions / _pageDimensions) +
|
||||||
|
glm::clamp(dimensions % _pageDimensions, glm::uvec3(0), glm::uvec3(1));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t GL45SparseResourceTexture::getPageCount(const uvec3& dimensions) const {
|
||||||
|
auto pageCounts = getPageCounts(dimensions);
|
||||||
|
return pageCounts.x * pageCounts.y * pageCounts.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GL45SparseResourceTexture::promote() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void GL45SparseResourceTexture::demote() {
|
||||||
|
}
|
||||||
|
|
||||||
|
SparseInfo::SparseInfo(GL45Texture& texture)
|
||||||
|
: texture(texture) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void SparseInfo::maybeMakeSparse() {
|
||||||
|
// Don't enable sparse for objects with explicitly managed mip levels
|
||||||
|
if (!texture._gpuObject.isAutogenerateMips()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uvec3 dimensions = texture._gpuObject.getDimensions();
|
||||||
|
auto allowedPageDimensions = getPageDimensionsForFormat(texture._target, texture._internalFormat);
|
||||||
|
// In order to enable sparse the texture size must be an integer multiple of the page size
|
||||||
|
for (size_t i = 0; i < allowedPageDimensions.size(); ++i) {
|
||||||
|
pageDimensionsIndex = (uint32_t)i;
|
||||||
|
pageDimensions = allowedPageDimensions[i];
|
||||||
|
// Is this texture an integer multiple of page dimensions?
|
||||||
|
if (uvec3(0) == (dimensions % pageDimensions)) {
|
||||||
|
qCDebug(gpugl45logging) << "Enabling sparse for texture " << texture._source.c_str();
|
||||||
|
sparse = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sparse) {
|
||||||
|
glTextureParameteri(texture._id, GL_TEXTURE_SPARSE_ARB, GL_TRUE);
|
||||||
|
glTextureParameteri(texture._id, GL_VIRTUAL_PAGE_SIZE_INDEX_ARB, pageDimensionsIndex);
|
||||||
|
} else {
|
||||||
|
qCDebug(gpugl45logging) << "Size " << dimensions.x << " x " << dimensions.y <<
|
||||||
|
" is not supported by any sparse page size for texture" << texture._source.c_str();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// This can only be called after we've established our storage size
|
||||||
|
void SparseInfo::update() {
|
||||||
|
if (!sparse) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
glGetTextureParameterIuiv(texture._id, GL_NUM_SPARSE_LEVELS_ARB, &maxSparseLevel);
|
||||||
|
|
||||||
|
for (uint16_t mipLevel = 0; mipLevel <= maxSparseLevel; ++mipLevel) {
|
||||||
|
auto mipDimensions = texture._gpuObject.evalMipDimensions(mipLevel);
|
||||||
|
auto mipPageCount = getPageCount(mipDimensions);
|
||||||
|
maxPages += mipPageCount;
|
||||||
|
}
|
||||||
|
if (texture._target == GL_TEXTURE_CUBE_MAP) {
|
||||||
|
maxPages *= GLTexture::CUBE_NUM_FACES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SparseInfo::allocateToMip(uint16_t targetMip) {
|
||||||
|
// Not sparse, do nothing
|
||||||
|
if (!sparse) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allocatedMip == INVALID_MIP) {
|
||||||
|
allocatedMip = maxSparseLevel + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't try to allocate below the maximum sparse level
|
||||||
|
if (targetMip > maxSparseLevel) {
|
||||||
|
targetMip = maxSparseLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Already allocated this level
|
||||||
|
if (allocatedMip <= targetMip) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t maxFace = (uint32_t)(GL_TEXTURE_CUBE_MAP == texture._target ? CUBE_NUM_FACES : 1);
|
||||||
|
for (uint16_t mip = targetMip; mip < allocatedMip; ++mip) {
|
||||||
|
auto size = texture._gpuObject.evalMipDimensions(mip);
|
||||||
|
glTexturePageCommitmentEXT(texture._id, mip, 0, 0, 0, size.x, size.y, maxFace, GL_TRUE);
|
||||||
|
allocatedPages += getPageCount(size);
|
||||||
|
}
|
||||||
|
allocatedMip = targetMip;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t SparseInfo::getSize() const {
|
||||||
|
return allocatedPages * pageBytes;
|
||||||
|
}
|
||||||
|
using SparseInfo = GL45Backend::GL45Texture::SparseInfo;
|
||||||
|
|
||||||
|
void GL45Texture::updateSize() const {
|
||||||
|
if (_gpuObject.getTexelFormat().isCompressed()) {
|
||||||
|
qFatal("Compressed textures not yet supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_transferrable && _sparseInfo.sparse) {
|
||||||
|
auto size = _sparseInfo.getSize();
|
||||||
|
Backend::updateTextureGPUSparseMemoryUsage(_size, size);
|
||||||
|
setSize(size);
|
||||||
|
} else {
|
||||||
|
setSize(_gpuObject.evalTotalSize(_mipOffset));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GL45Texture::startTransfer() {
|
||||||
|
Parent::startTransfer();
|
||||||
|
_sparseInfo.update();
|
||||||
|
_populatedMip = _maxMip + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GL45Texture::continueTransfer() {
|
||||||
|
size_t maxFace = GL_TEXTURE_CUBE_MAP == _target ? CUBE_NUM_FACES : 1;
|
||||||
|
if (_populatedMip == _minMip) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t targetMip = _populatedMip - 1;
|
||||||
|
while (targetMip > 0 && !_gpuObject.isStoredMipFaceAvailable(targetMip)) {
|
||||||
|
--targetMip;
|
||||||
|
}
|
||||||
|
|
||||||
|
_sparseInfo.allocateToMip(targetMip);
|
||||||
|
for (uint8_t face = 0; face < maxFace; ++face) {
|
||||||
|
auto size = _gpuObject.evalMipDimensions(targetMip);
|
||||||
|
if (_gpuObject.isStoredMipFaceAvailable(targetMip, face)) {
|
||||||
|
auto mip = _gpuObject.accessStoredMipFace(targetMip, face);
|
||||||
|
GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), mip->getFormat());
|
||||||
|
if (GL_TEXTURE_2D == _target) {
|
||||||
|
glTextureSubImage2D(_id, targetMip, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mip->readData());
|
||||||
|
} else if (GL_TEXTURE_CUBE_MAP == _target) {
|
||||||
|
// DSA ARB does not work on AMD, so use EXT
|
||||||
|
// unless EXT is not available on the driver
|
||||||
|
if (glTextureSubImage2DEXT) {
|
||||||
|
auto target = CUBE_FACE_LAYOUT[face];
|
||||||
|
glTextureSubImage2DEXT(_id, target, targetMip, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mip->readData());
|
||||||
|
} else {
|
||||||
|
glTextureSubImage3D(_id, targetMip, 0, 0, face, size.x, size.y, 1, texelFormat.format, texelFormat.type, mip->readData());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Q_ASSERT(false);
|
||||||
|
}
|
||||||
|
(void)CHECK_GL_ERROR();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_populatedMip = targetMip;
|
||||||
|
return _populatedMip != _minMip;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GL45Texture::finishTransfer() {
|
||||||
|
Parent::finishTransfer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GL45Texture::postTransfer() {
|
||||||
|
Parent::postTransfer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GL45Texture::stripToMip(uint16_t newMinMip) {
|
||||||
|
if (newMinMip < _minMip) {
|
||||||
|
qCWarning(gpugl45logging) << "Cannot decrease the min mip";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_sparseInfo.sparse && newMinMip > _sparseInfo.maxSparseLevel) {
|
||||||
|
qCWarning(gpugl45logging) << "Cannot increase the min mip into the mip tail";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we weren't generating mips before, we need to now that we're stripping down mip levels.
|
||||||
|
if (!_gpuObject.isAutogenerateMips()) {
|
||||||
|
qCDebug(gpugl45logging) << "Force mip generation for texture";
|
||||||
|
glGenerateTextureMipmap(_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t maxFace = (uint8_t)((_target == GL_TEXTURE_CUBE_MAP) ? GLTexture::CUBE_NUM_FACES : 1);
|
||||||
|
if (_sparseInfo.sparse) {
|
||||||
|
for (uint16_t mip = _minMip; mip < newMinMip; ++mip) {
|
||||||
|
auto id = _id;
|
||||||
|
auto mipDimensions = _gpuObject.evalMipDimensions(mip);
|
||||||
|
glTexturePageCommitmentEXT(id, mip, 0, 0, 0, mipDimensions.x, mipDimensions.y, maxFace, GL_FALSE);
|
||||||
|
auto deallocatedPages = _sparseInfo.getPageCount(mipDimensions) * maxFace;
|
||||||
|
assert(deallocatedPages < _sparseInfo.allocatedPages);
|
||||||
|
_sparseInfo.allocatedPages -= deallocatedPages;
|
||||||
|
}
|
||||||
|
_minMip = newMinMip;
|
||||||
|
} else {
|
||||||
|
GLuint oldId = _id;
|
||||||
|
// Find the distance between the old min mip and the new one
|
||||||
|
uint16 mipDelta = newMinMip - _minMip;
|
||||||
|
_mipOffset += mipDelta;
|
||||||
|
const_cast<uint16&>(_maxMip) -= mipDelta;
|
||||||
|
auto newLevels = usedMipLevels();
|
||||||
|
|
||||||
|
// Create and setup the new texture (allocate)
|
||||||
|
glCreateTextures(_target, 1, &const_cast<GLuint&>(_id));
|
||||||
|
glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, 0);
|
||||||
|
glTextureParameteri(_id, GL_TEXTURE_MAX_LEVEL, _maxMip - _minMip);
|
||||||
|
Vec3u newDimensions = _gpuObject.evalMipDimensions(_mipOffset);
|
||||||
|
glTextureStorage2D(_id, newLevels, _internalFormat, newDimensions.x, newDimensions.y);
|
||||||
|
|
||||||
|
// Copy the contents of the old texture to the new
|
||||||
|
GLuint fbo { 0 };
|
||||||
|
glCreateFramebuffers(1, &fbo);
|
||||||
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
|
||||||
|
for (uint16 targetMip = _minMip; targetMip <= _maxMip; ++targetMip) {
|
||||||
|
uint16 sourceMip = targetMip + mipDelta;
|
||||||
|
Vec3u mipDimensions = _gpuObject.evalMipDimensions(targetMip + _mipOffset);
|
||||||
|
for (GLenum target : getFaceTargets(_target)) {
|
||||||
|
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, oldId, sourceMip);
|
||||||
|
(void)CHECK_GL_ERROR();
|
||||||
|
glCopyTextureSubImage2D(_id, targetMip, 0, 0, 0, 0, mipDimensions.x, mipDimensions.y);
|
||||||
|
(void)CHECK_GL_ERROR();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||||
|
glDeleteFramebuffers(1, &fbo);
|
||||||
|
glDeleteTextures(1, &oldId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-sync the sampler to force access to the new mip level
|
||||||
|
syncSampler();
|
||||||
|
updateSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GL45Texture::derezable() const {
|
||||||
|
if (_external) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto maxMinMip = _sparseInfo.sparse ? _sparseInfo.maxSparseLevel : _maxMip;
|
||||||
|
return _transferrable && (_targetMinMip < maxMinMip);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t GL45Texture::getMipByteCount(uint16_t mip) const {
|
||||||
|
if (!_sparseInfo.sparse) {
|
||||||
|
return Parent::getMipByteCount(mip);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto dimensions = _gpuObject.evalMipDimensions(_targetMinMip);
|
||||||
|
return _sparseInfo.getPageCount(dimensions) * _sparseInfo.pageBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<size_t, bool> GL45Texture::preDerez() {
|
||||||
|
assert(!_sparseInfo.sparse || _targetMinMip < _sparseInfo.maxSparseLevel);
|
||||||
|
size_t freedMemory = getMipByteCount(_targetMinMip);
|
||||||
|
bool liveMip = _populatedMip != INVALID_MIP && _populatedMip <= _targetMinMip;
|
||||||
|
++_targetMinMip;
|
||||||
|
return { freedMemory, liveMip };
|
||||||
|
}
|
||||||
|
|
||||||
|
void GL45Texture::derez() {
|
||||||
|
if (_sparseInfo.sparse) {
|
||||||
|
assert(_minMip < _sparseInfo.maxSparseLevel);
|
||||||
|
}
|
||||||
|
assert(_minMip < _maxMip);
|
||||||
|
assert(_transferrable);
|
||||||
|
stripToMip(_minMip + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t GL45Texture::getCurrentGpuSize() const {
|
||||||
|
if (!_sparseInfo.sparse) {
|
||||||
|
return Parent::getCurrentGpuSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
return _sparseInfo.getSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t GL45Texture::getTargetGpuSize() const {
|
||||||
|
if (!_sparseInfo.sparse) {
|
||||||
|
return Parent::getTargetGpuSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t result = 0;
|
||||||
|
for (auto mip = _targetMinMip; mip <= _sparseInfo.maxSparseLevel; ++mip) {
|
||||||
|
result += (_sparseInfo.pageBytes * _sparseInfo.getPageCount(_gpuObject.evalMipDimensions(mip)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
GL45Texture::~GL45Texture() {
|
||||||
|
if (_sparseInfo.sparse) {
|
||||||
|
uint8_t maxFace = (uint8_t)((_target == GL_TEXTURE_CUBE_MAP) ? GLTexture::CUBE_NUM_FACES : 1);
|
||||||
|
auto maxSparseMip = std::min<uint16_t>(_maxMip, _sparseInfo.maxSparseLevel);
|
||||||
|
for (uint16_t mipLevel = _minMip; mipLevel <= maxSparseMip; ++mipLevel) {
|
||||||
|
auto mipDimensions = _gpuObject.evalMipDimensions(mipLevel);
|
||||||
|
glTexturePageCommitmentEXT(_texture, mipLevel, 0, 0, 0, mipDimensions.x, mipDimensions.y, maxFace, GL_FALSE);
|
||||||
|
auto deallocatedPages = _sparseInfo.getPageCount(mipDimensions) * maxFace;
|
||||||
|
assert(deallocatedPages <= _sparseInfo.allocatedPages);
|
||||||
|
_sparseInfo.allocatedPages -= deallocatedPages;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 != _sparseInfo.allocatedPages) {
|
||||||
|
qCWarning(gpugl45logging) << "Allocated pages remaining " << _id << " " << _sparseInfo.allocatedPages;
|
||||||
|
}
|
||||||
|
Backend::decrementTextureGPUSparseCount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GL45Texture::GL45Texture(const std::weak_ptr<GLBackend>& backend, const Texture& texture)
|
||||||
|
: GLTexture(backend, texture, allocate(texture)), _sparseInfo(*this), _targetMinMip(_minMip)
|
||||||
|
{
|
||||||
|
|
||||||
|
auto theBackend = _backend.lock();
|
||||||
|
if (_transferrable && theBackend && theBackend->isTextureManagementSparseEnabled()) {
|
||||||
|
_sparseInfo.maybeMakeSparse();
|
||||||
|
if (_sparseInfo.sparse) {
|
||||||
|
Backend::incrementTextureGPUSparseCount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -292,15 +292,8 @@ void Batch::setUniformBuffer(uint32 slot, const BufferView& view) {
|
||||||
setUniformBuffer(slot, view._buffer, view._offset, view._size);
|
setUniformBuffer(slot, view._buffer, view._offset, view._size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Batch::setResourceTexture(uint32 slot, const TexturePointer& texture) {
|
void Batch::setResourceTexture(uint32 slot, const TexturePointer& texture) {
|
||||||
if (texture && texture->getUsage().isExternal()) {
|
|
||||||
auto recycler = texture->getExternalRecycler();
|
|
||||||
Q_ASSERT(recycler);
|
|
||||||
}
|
|
||||||
|
|
||||||
ADD_COMMAND(setResourceTexture);
|
ADD_COMMAND(setResourceTexture);
|
||||||
|
|
||||||
_params.emplace_back(_textures.cache(texture));
|
_params.emplace_back(_textures.cache(texture));
|
||||||
_params.emplace_back(slot);
|
_params.emplace_back(slot);
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ Framebuffer* Framebuffer::create(const std::string& name) {
|
||||||
Framebuffer* Framebuffer::create(const std::string& name, const Format& colorBufferFormat, uint16 width, uint16 height) {
|
Framebuffer* Framebuffer::create(const std::string& name, const Format& colorBufferFormat, uint16 width, uint16 height) {
|
||||||
auto framebuffer = Framebuffer::create(name);
|
auto framebuffer = Framebuffer::create(name);
|
||||||
|
|
||||||
auto colorTexture = TexturePointer(Texture::create2D(colorBufferFormat, width, height, Sampler(Sampler::FILTER_MIN_MAG_POINT)));
|
auto colorTexture = TexturePointer(Texture::createRenderBuffer(colorBufferFormat, width, height, Sampler(Sampler::FILTER_MIN_MAG_POINT)));
|
||||||
colorTexture->setSource("Framebuffer::colorTexture");
|
colorTexture->setSource("Framebuffer::colorTexture");
|
||||||
|
|
||||||
framebuffer->setRenderBuffer(0, colorTexture);
|
framebuffer->setRenderBuffer(0, colorTexture);
|
||||||
|
@ -43,8 +43,8 @@ Framebuffer* Framebuffer::create(const std::string& name, const Format& colorBuf
|
||||||
Framebuffer* Framebuffer::create(const std::string& name, const Format& colorBufferFormat, const Format& depthStencilBufferFormat, uint16 width, uint16 height) {
|
Framebuffer* Framebuffer::create(const std::string& name, const Format& colorBufferFormat, const Format& depthStencilBufferFormat, uint16 width, uint16 height) {
|
||||||
auto framebuffer = Framebuffer::create(name);
|
auto framebuffer = Framebuffer::create(name);
|
||||||
|
|
||||||
auto colorTexture = TexturePointer(Texture::create2D(colorBufferFormat, width, height, Sampler(Sampler::FILTER_MIN_MAG_POINT)));
|
auto colorTexture = TexturePointer(Texture::createRenderBuffer(colorBufferFormat, width, height, Sampler(Sampler::FILTER_MIN_MAG_POINT)));
|
||||||
auto depthTexture = TexturePointer(Texture::create2D(depthStencilBufferFormat, width, height, Sampler(Sampler::FILTER_MIN_MAG_POINT)));
|
auto depthTexture = TexturePointer(Texture::createRenderBuffer(depthStencilBufferFormat, width, height, Sampler(Sampler::FILTER_MIN_MAG_POINT)));
|
||||||
framebuffer->setRenderBuffer(0, colorTexture);
|
framebuffer->setRenderBuffer(0, colorTexture);
|
||||||
framebuffer->setDepthStencilBuffer(depthTexture, depthStencilBufferFormat);
|
framebuffer->setDepthStencilBuffer(depthTexture, depthStencilBufferFormat);
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ Framebuffer* Framebuffer::createShadowmap(uint16 width) {
|
||||||
auto framebuffer = Framebuffer::create("Shadowmap");
|
auto framebuffer = Framebuffer::create("Shadowmap");
|
||||||
|
|
||||||
auto depthFormat = Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPTH); // Depth32 texel format
|
auto depthFormat = Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPTH); // Depth32 texel format
|
||||||
auto depthTexture = TexturePointer(Texture::create2D(depthFormat, width, width));
|
auto depthTexture = TexturePointer(Texture::createRenderBuffer(depthFormat, width, width));
|
||||||
Sampler::Desc samplerDesc;
|
Sampler::Desc samplerDesc;
|
||||||
samplerDesc._borderColor = glm::vec4(1.0f);
|
samplerDesc._borderColor = glm::vec4(1.0f);
|
||||||
samplerDesc._wrapModeU = Sampler::WRAP_BORDER;
|
samplerDesc._wrapModeU = Sampler::WRAP_BORDER;
|
||||||
|
@ -143,6 +143,8 @@ int Framebuffer::setRenderBuffer(uint32 slot, const TexturePointer& texture, uin
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Q_ASSERT(!texture || TextureUsageType::RENDERBUFFER == texture->getUsageType());
|
||||||
|
|
||||||
// Check for the slot
|
// Check for the slot
|
||||||
if (slot >= getMaxNumRenderBuffers()) {
|
if (slot >= getMaxNumRenderBuffers()) {
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -222,6 +224,8 @@ bool Framebuffer::setDepthStencilBuffer(const TexturePointer& texture, const For
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Q_ASSERT(!texture || TextureUsageType::RENDERBUFFER == texture->getUsageType());
|
||||||
|
|
||||||
// Check for the compatibility of size
|
// Check for the compatibility of size
|
||||||
if (texture) {
|
if (texture) {
|
||||||
if (!validateTargetCompatibility(*texture)) {
|
if (!validateTargetCompatibility(*texture)) {
|
||||||
|
|
|
@ -253,35 +253,42 @@ bool Texture::Storage::assignMipFaceData(uint16 level, const Element& format, Si
|
||||||
return allocated == size;
|
return allocated == size;
|
||||||
}
|
}
|
||||||
|
|
||||||
Texture* Texture::createExternal2D(const ExternalRecycler& recycler, const Sampler& sampler) {
|
Texture* Texture::createExternal(const ExternalRecycler& recycler, const Sampler& sampler) {
|
||||||
Texture* tex = new Texture();
|
Texture* tex = new Texture(TextureUsageType::EXTERNAL);
|
||||||
tex->_type = TEX_2D;
|
tex->_type = TEX_2D;
|
||||||
tex->_maxMip = 0;
|
tex->_maxMip = 0;
|
||||||
tex->_sampler = sampler;
|
tex->_sampler = sampler;
|
||||||
tex->setUsage(Usage::Builder().withExternal().withColor());
|
|
||||||
tex->setExternalRecycler(recycler);
|
tex->setExternalRecycler(recycler);
|
||||||
return tex;
|
return tex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Texture* Texture::createRenderBuffer(const Element& texelFormat, uint16 width, uint16 height, const Sampler& sampler) {
|
||||||
|
return create(TextureUsageType::RENDERBUFFER, TEX_2D, texelFormat, width, height, 1, 1, 1, sampler);
|
||||||
|
}
|
||||||
|
|
||||||
Texture* Texture::create1D(const Element& texelFormat, uint16 width, const Sampler& sampler) {
|
Texture* Texture::create1D(const Element& texelFormat, uint16 width, const Sampler& sampler) {
|
||||||
return create(TEX_1D, texelFormat, width, 1, 1, 1, 1, sampler);
|
return create(TextureUsageType::RESOURCE, TEX_1D, texelFormat, width, 1, 1, 1, 1, sampler);
|
||||||
}
|
}
|
||||||
|
|
||||||
Texture* Texture::create2D(const Element& texelFormat, uint16 width, uint16 height, const Sampler& sampler) {
|
Texture* Texture::create2D(const Element& texelFormat, uint16 width, uint16 height, const Sampler& sampler) {
|
||||||
return create(TEX_2D, texelFormat, width, height, 1, 1, 1, sampler);
|
return create(TextureUsageType::RESOURCE, TEX_2D, texelFormat, width, height, 1, 1, 1, sampler);
|
||||||
|
}
|
||||||
|
|
||||||
|
Texture* Texture::createStrict(const Element& texelFormat, uint16 width, uint16 height, const Sampler& sampler) {
|
||||||
|
return create(TextureUsageType::STRICT_RESOURCE, TEX_2D, texelFormat, width, height, 1, 1, 1, sampler);
|
||||||
}
|
}
|
||||||
|
|
||||||
Texture* Texture::create3D(const Element& texelFormat, uint16 width, uint16 height, uint16 depth, const Sampler& sampler) {
|
Texture* Texture::create3D(const Element& texelFormat, uint16 width, uint16 height, uint16 depth, const Sampler& sampler) {
|
||||||
return create(TEX_3D, texelFormat, width, height, depth, 1, 1, sampler);
|
return create(TextureUsageType::RESOURCE, TEX_3D, texelFormat, width, height, depth, 1, 1, sampler);
|
||||||
}
|
}
|
||||||
|
|
||||||
Texture* Texture::createCube(const Element& texelFormat, uint16 width, const Sampler& sampler) {
|
Texture* Texture::createCube(const Element& texelFormat, uint16 width, const Sampler& sampler) {
|
||||||
return create(TEX_CUBE, texelFormat, width, width, 1, 1, 1, sampler);
|
return create(TextureUsageType::RESOURCE, TEX_CUBE, texelFormat, width, width, 1, 1, 1, sampler);
|
||||||
}
|
}
|
||||||
|
|
||||||
Texture* Texture::create(Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices, const Sampler& sampler)
|
Texture* Texture::create(TextureUsageType usageType, Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices, const Sampler& sampler)
|
||||||
{
|
{
|
||||||
Texture* tex = new Texture();
|
Texture* tex = new Texture(usageType);
|
||||||
tex->_storage.reset(new Storage());
|
tex->_storage.reset(new Storage());
|
||||||
tex->_type = type;
|
tex->_type = type;
|
||||||
tex->_storage->assignTexture(tex);
|
tex->_storage->assignTexture(tex);
|
||||||
|
@ -293,16 +300,14 @@ Texture* Texture::create(Type type, const Element& texelFormat, uint16 width, ui
|
||||||
return tex;
|
return tex;
|
||||||
}
|
}
|
||||||
|
|
||||||
Texture::Texture():
|
Texture::Texture(TextureUsageType usageType) :
|
||||||
Resource()
|
Resource(), _usageType(usageType) {
|
||||||
{
|
|
||||||
_textureCPUCount++;
|
_textureCPUCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
Texture::~Texture()
|
Texture::~Texture() {
|
||||||
{
|
|
||||||
_textureCPUCount--;
|
_textureCPUCount--;
|
||||||
if (getUsage().isExternal()) {
|
if (_usageType == TextureUsageType::EXTERNAL) {
|
||||||
Texture::ExternalUpdates externalUpdates;
|
Texture::ExternalUpdates externalUpdates;
|
||||||
{
|
{
|
||||||
Lock lock(_externalMutex);
|
Lock lock(_externalMutex);
|
||||||
|
|
|
@ -139,6 +139,13 @@ protected:
|
||||||
Desc _desc;
|
Desc _desc;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class TextureUsageType {
|
||||||
|
RENDERBUFFER, // Used as attachments to a framebuffer
|
||||||
|
RESOURCE, // Resource textures, like materials... subject to memory manipulation
|
||||||
|
STRICT_RESOURCE, // Resource textures not subject to manipulation, like the normal fitting texture
|
||||||
|
EXTERNAL,
|
||||||
|
};
|
||||||
|
|
||||||
class Texture : public Resource {
|
class Texture : public Resource {
|
||||||
static std::atomic<uint32_t> _textureCPUCount;
|
static std::atomic<uint32_t> _textureCPUCount;
|
||||||
static std::atomic<Size> _textureCPUMemoryUsage;
|
static std::atomic<Size> _textureCPUMemoryUsage;
|
||||||
|
@ -173,9 +180,9 @@ public:
|
||||||
NORMAL, // Texture is a normal map
|
NORMAL, // Texture is a normal map
|
||||||
ALPHA, // Texture has an alpha channel
|
ALPHA, // Texture has an alpha channel
|
||||||
ALPHA_MASK, // Texture alpha channel is a Mask 0/1
|
ALPHA_MASK, // Texture alpha channel is a Mask 0/1
|
||||||
EXTERNAL,
|
|
||||||
NUM_FLAGS,
|
NUM_FLAGS,
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::bitset<NUM_FLAGS> Flags;
|
typedef std::bitset<NUM_FLAGS> Flags;
|
||||||
|
|
||||||
// The key is the Flags
|
// The key is the Flags
|
||||||
|
@ -199,7 +206,6 @@ public:
|
||||||
Builder& withNormal() { _flags.set(NORMAL); return (*this); }
|
Builder& withNormal() { _flags.set(NORMAL); return (*this); }
|
||||||
Builder& withAlpha() { _flags.set(ALPHA); return (*this); }
|
Builder& withAlpha() { _flags.set(ALPHA); return (*this); }
|
||||||
Builder& withAlphaMask() { _flags.set(ALPHA_MASK); return (*this); }
|
Builder& withAlphaMask() { _flags.set(ALPHA_MASK); return (*this); }
|
||||||
Builder& withExternal() { _flags.set(EXTERNAL); return (*this); }
|
|
||||||
};
|
};
|
||||||
Usage(const Builder& builder) : Usage(builder._flags) {}
|
Usage(const Builder& builder) : Usage(builder._flags) {}
|
||||||
|
|
||||||
|
@ -208,8 +214,6 @@ public:
|
||||||
|
|
||||||
bool isAlpha() const { return _flags[ALPHA]; }
|
bool isAlpha() const { return _flags[ALPHA]; }
|
||||||
bool isAlphaMask() const { return _flags[ALPHA_MASK]; }
|
bool isAlphaMask() const { return _flags[ALPHA_MASK]; }
|
||||||
bool isExternal() const { return _flags[EXTERNAL]; }
|
|
||||||
|
|
||||||
|
|
||||||
bool operator==(const Usage& usage) { return (_flags == usage._flags); }
|
bool operator==(const Usage& usage) { return (_flags == usage._flags); }
|
||||||
bool operator!=(const Usage& usage) { return (_flags != usage._flags); }
|
bool operator!=(const Usage& usage) { return (_flags != usage._flags); }
|
||||||
|
@ -298,9 +302,11 @@ public:
|
||||||
static Texture* create2D(const Element& texelFormat, uint16 width, uint16 height, const Sampler& sampler = Sampler());
|
static Texture* create2D(const Element& texelFormat, uint16 width, uint16 height, const Sampler& sampler = Sampler());
|
||||||
static Texture* create3D(const Element& texelFormat, uint16 width, uint16 height, uint16 depth, const Sampler& sampler = Sampler());
|
static Texture* create3D(const Element& texelFormat, uint16 width, uint16 height, uint16 depth, const Sampler& sampler = Sampler());
|
||||||
static Texture* createCube(const Element& texelFormat, uint16 width, const Sampler& sampler = Sampler());
|
static Texture* createCube(const Element& texelFormat, uint16 width, const Sampler& sampler = Sampler());
|
||||||
static Texture* createExternal2D(const ExternalRecycler& recycler, const Sampler& sampler = Sampler());
|
static Texture* createRenderBuffer(const Element& texelFormat, uint16 width, uint16 height, const Sampler& sampler = Sampler());
|
||||||
|
static Texture* createStrict(const Element& texelFormat, uint16 width, uint16 height, const Sampler& sampler = Sampler());
|
||||||
|
static Texture* createExternal(const ExternalRecycler& recycler, const Sampler& sampler = Sampler());
|
||||||
|
|
||||||
Texture();
|
Texture(TextureUsageType usageType);
|
||||||
Texture(const Texture& buf); // deep copy of the sysmem texture
|
Texture(const Texture& buf); // deep copy of the sysmem texture
|
||||||
Texture& operator=(const Texture& buf); // deep copy of the sysmem texture
|
Texture& operator=(const Texture& buf); // deep copy of the sysmem texture
|
||||||
~Texture();
|
~Texture();
|
||||||
|
@ -325,6 +331,7 @@ public:
|
||||||
|
|
||||||
// Size and format
|
// Size and format
|
||||||
Type getType() const { return _type; }
|
Type getType() const { return _type; }
|
||||||
|
TextureUsageType getUsageType() const { return _usageType; }
|
||||||
|
|
||||||
bool isColorRenderTarget() const;
|
bool isColorRenderTarget() const;
|
||||||
bool isDepthStencilRenderTarget() const;
|
bool isDepthStencilRenderTarget() const;
|
||||||
|
@ -476,6 +483,8 @@ public:
|
||||||
ExternalUpdates getUpdates() const;
|
ExternalUpdates getUpdates() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
const TextureUsageType _usageType;
|
||||||
|
|
||||||
// Should only be accessed internally or by the backend sync function
|
// Should only be accessed internally or by the backend sync function
|
||||||
mutable Mutex _externalMutex;
|
mutable Mutex _externalMutex;
|
||||||
mutable std::list<ExternalIdAndFence> _externalUpdates;
|
mutable std::list<ExternalIdAndFence> _externalUpdates;
|
||||||
|
@ -513,7 +522,7 @@ protected:
|
||||||
bool _isIrradianceValid = false;
|
bool _isIrradianceValid = false;
|
||||||
bool _defined = false;
|
bool _defined = false;
|
||||||
|
|
||||||
static Texture* create(Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices, const Sampler& sampler);
|
static Texture* create(TextureUsageType usageType, Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices, const Sampler& sampler);
|
||||||
|
|
||||||
Size resize(Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices);
|
Size resize(Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices);
|
||||||
};
|
};
|
||||||
|
|
|
@ -120,7 +120,7 @@ const unsigned char OPAQUE_BLACK[] = { 0x00, 0x00, 0x00, 0xFF };
|
||||||
|
|
||||||
const gpu::TexturePointer& TextureCache::getWhiteTexture() {
|
const gpu::TexturePointer& TextureCache::getWhiteTexture() {
|
||||||
if (!_whiteTexture) {
|
if (!_whiteTexture) {
|
||||||
_whiteTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, 1, 1));
|
_whiteTexture = gpu::TexturePointer(gpu::Texture::createStrict(gpu::Element::COLOR_RGBA_32, 1, 1));
|
||||||
_whiteTexture->setSource("TextureCache::_whiteTexture");
|
_whiteTexture->setSource("TextureCache::_whiteTexture");
|
||||||
_whiteTexture->assignStoredMip(0, _whiteTexture->getTexelFormat(), sizeof(OPAQUE_WHITE), OPAQUE_WHITE);
|
_whiteTexture->assignStoredMip(0, _whiteTexture->getTexelFormat(), sizeof(OPAQUE_WHITE), OPAQUE_WHITE);
|
||||||
}
|
}
|
||||||
|
@ -129,7 +129,7 @@ const gpu::TexturePointer& TextureCache::getWhiteTexture() {
|
||||||
|
|
||||||
const gpu::TexturePointer& TextureCache::getGrayTexture() {
|
const gpu::TexturePointer& TextureCache::getGrayTexture() {
|
||||||
if (!_grayTexture) {
|
if (!_grayTexture) {
|
||||||
_grayTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, 1, 1));
|
_grayTexture = gpu::TexturePointer(gpu::Texture::createStrict(gpu::Element::COLOR_RGBA_32, 1, 1));
|
||||||
_grayTexture->setSource("TextureCache::_grayTexture");
|
_grayTexture->setSource("TextureCache::_grayTexture");
|
||||||
_grayTexture->assignStoredMip(0, _grayTexture->getTexelFormat(), sizeof(OPAQUE_GRAY), OPAQUE_GRAY);
|
_grayTexture->assignStoredMip(0, _grayTexture->getTexelFormat(), sizeof(OPAQUE_GRAY), OPAQUE_GRAY);
|
||||||
}
|
}
|
||||||
|
@ -138,7 +138,7 @@ const gpu::TexturePointer& TextureCache::getGrayTexture() {
|
||||||
|
|
||||||
const gpu::TexturePointer& TextureCache::getBlueTexture() {
|
const gpu::TexturePointer& TextureCache::getBlueTexture() {
|
||||||
if (!_blueTexture) {
|
if (!_blueTexture) {
|
||||||
_blueTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, 1, 1));
|
_blueTexture = gpu::TexturePointer(gpu::Texture::createStrict(gpu::Element::COLOR_RGBA_32, 1, 1));
|
||||||
_blueTexture->setSource("TextureCache::_blueTexture");
|
_blueTexture->setSource("TextureCache::_blueTexture");
|
||||||
_blueTexture->assignStoredMip(0, _blueTexture->getTexelFormat(), sizeof(OPAQUE_BLUE), OPAQUE_BLUE);
|
_blueTexture->assignStoredMip(0, _blueTexture->getTexelFormat(), sizeof(OPAQUE_BLUE), OPAQUE_BLUE);
|
||||||
}
|
}
|
||||||
|
@ -147,7 +147,7 @@ const gpu::TexturePointer& TextureCache::getBlueTexture() {
|
||||||
|
|
||||||
const gpu::TexturePointer& TextureCache::getBlackTexture() {
|
const gpu::TexturePointer& TextureCache::getBlackTexture() {
|
||||||
if (!_blackTexture) {
|
if (!_blackTexture) {
|
||||||
_blackTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, 1, 1));
|
_blackTexture = gpu::TexturePointer(gpu::Texture::createStrict(gpu::Element::COLOR_RGBA_32, 1, 1));
|
||||||
_blackTexture->setSource("TextureCache::_blackTexture");
|
_blackTexture->setSource("TextureCache::_blackTexture");
|
||||||
_blackTexture->assignStoredMip(0, _blackTexture->getTexelFormat(), sizeof(OPAQUE_BLACK), OPAQUE_BLACK);
|
_blackTexture->assignStoredMip(0, _blackTexture->getTexelFormat(), sizeof(OPAQUE_BLACK), OPAQUE_BLACK);
|
||||||
}
|
}
|
||||||
|
@ -157,7 +157,7 @@ const gpu::TexturePointer& TextureCache::getBlackTexture() {
|
||||||
|
|
||||||
const gpu::TexturePointer& TextureCache::getNormalFittingTexture() {
|
const gpu::TexturePointer& TextureCache::getNormalFittingTexture() {
|
||||||
if (!_normalFittingTexture) {
|
if (!_normalFittingTexture) {
|
||||||
_normalFittingTexture = getImageTexture(PathUtils::resourcesPath() + "images/normalFittingScale.dds");
|
_normalFittingTexture = getImageTexture(PathUtils::resourcesPath() + "images/normalFittingScale.dds", NetworkTexture::STRICT_TEXTURE);
|
||||||
}
|
}
|
||||||
return _normalFittingTexture;
|
return _normalFittingTexture;
|
||||||
}
|
}
|
||||||
|
@ -227,11 +227,16 @@ NetworkTexture::TextureLoaderFunc getTextureLoaderForType(NetworkTexture::Type t
|
||||||
return model::TextureUsage::createMetallicTextureFromImage;
|
return model::TextureUsage::createMetallicTextureFromImage;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case Type::STRICT_TEXTURE: {
|
||||||
|
return model::TextureUsage::createStrict2DTextureFromImage;
|
||||||
|
break;
|
||||||
|
}
|
||||||
case Type::CUSTOM_TEXTURE: {
|
case Type::CUSTOM_TEXTURE: {
|
||||||
Q_ASSERT(false);
|
Q_ASSERT(false);
|
||||||
return NetworkTexture::TextureLoaderFunc();
|
return NetworkTexture::TextureLoaderFunc();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Type::DEFAULT_TEXTURE:
|
case Type::DEFAULT_TEXTURE:
|
||||||
default: {
|
default: {
|
||||||
return model::TextureUsage::create2DTextureFromImage;
|
return model::TextureUsage::create2DTextureFromImage;
|
||||||
|
|
|
@ -43,6 +43,7 @@ class NetworkTexture : public Resource, public Texture {
|
||||||
public:
|
public:
|
||||||
enum Type {
|
enum Type {
|
||||||
DEFAULT_TEXTURE,
|
DEFAULT_TEXTURE,
|
||||||
|
STRICT_TEXTURE,
|
||||||
ALBEDO_TEXTURE,
|
ALBEDO_TEXTURE,
|
||||||
NORMAL_TEXTURE,
|
NORMAL_TEXTURE,
|
||||||
BUMP_TEXTURE,
|
BUMP_TEXTURE,
|
||||||
|
|
|
@ -235,7 +235,7 @@ void generateFaceMips(gpu::Texture* texture, QImage& image, gpu::Element formatM
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
gpu::Texture* TextureUsage::process2DTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool isLinear, bool doCompress, bool generateMips) {
|
gpu::Texture* TextureUsage::process2DTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool isLinear, bool doCompress, bool generateMips, bool isStrict) {
|
||||||
PROFILE_RANGE(resource_parse, "process2DTextureColorFromImage");
|
PROFILE_RANGE(resource_parse, "process2DTextureColorFromImage");
|
||||||
bool validAlpha = false;
|
bool validAlpha = false;
|
||||||
bool alphaAsMask = true;
|
bool alphaAsMask = true;
|
||||||
|
@ -248,7 +248,11 @@ gpu::Texture* TextureUsage::process2DTextureColorFromImage(const QImage& srcImag
|
||||||
gpu::Element formatMip;
|
gpu::Element formatMip;
|
||||||
defineColorTexelFormats(formatGPU, formatMip, image, isLinear, doCompress);
|
defineColorTexelFormats(formatGPU, formatMip, image, isLinear, doCompress);
|
||||||
|
|
||||||
theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)));
|
if (isStrict) {
|
||||||
|
theTexture = (gpu::Texture::createStrict(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)));
|
||||||
|
} else {
|
||||||
|
theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)));
|
||||||
|
}
|
||||||
theTexture->setSource(srcImageName);
|
theTexture->setSource(srcImageName);
|
||||||
auto usage = gpu::Texture::Usage::Builder().withColor();
|
auto usage = gpu::Texture::Usage::Builder().withColor();
|
||||||
if (validAlpha) {
|
if (validAlpha) {
|
||||||
|
@ -269,11 +273,14 @@ gpu::Texture* TextureUsage::process2DTextureColorFromImage(const QImage& srcImag
|
||||||
return theTexture;
|
return theTexture;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gpu::Texture* TextureUsage::createStrict2DTextureFromImage(const QImage& srcImage, const std::string& srcImageName) {
|
||||||
|
return process2DTextureColorFromImage(srcImage, srcImageName, false, false, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
gpu::Texture* TextureUsage::create2DTextureFromImage(const QImage& srcImage, const std::string& srcImageName) {
|
gpu::Texture* TextureUsage::create2DTextureFromImage(const QImage& srcImage, const std::string& srcImageName) {
|
||||||
return process2DTextureColorFromImage(srcImage, srcImageName, false, false, true);
|
return process2DTextureColorFromImage(srcImage, srcImageName, false, false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
gpu::Texture* TextureUsage::createAlbedoTextureFromImage(const QImage& srcImage, const std::string& srcImageName) {
|
gpu::Texture* TextureUsage::createAlbedoTextureFromImage(const QImage& srcImage, const std::string& srcImageName) {
|
||||||
return process2DTextureColorFromImage(srcImage, srcImageName, false, true, true);
|
return process2DTextureColorFromImage(srcImage, srcImageName, false, true, true);
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ public:
|
||||||
int _environmentUsage = 0;
|
int _environmentUsage = 0;
|
||||||
|
|
||||||
static gpu::Texture* create2DTextureFromImage(const QImage& image, const std::string& srcImageName);
|
static gpu::Texture* create2DTextureFromImage(const QImage& image, const std::string& srcImageName);
|
||||||
|
static gpu::Texture* createStrict2DTextureFromImage(const QImage& image, const std::string& srcImageName);
|
||||||
static gpu::Texture* createAlbedoTextureFromImage(const QImage& image, const std::string& srcImageName);
|
static gpu::Texture* createAlbedoTextureFromImage(const QImage& image, const std::string& srcImageName);
|
||||||
static gpu::Texture* createEmissiveTextureFromImage(const QImage& image, const std::string& srcImageName);
|
static gpu::Texture* createEmissiveTextureFromImage(const QImage& image, const std::string& srcImageName);
|
||||||
static gpu::Texture* createNormalTextureFromNormalImage(const QImage& image, const std::string& srcImageName);
|
static gpu::Texture* createNormalTextureFromNormalImage(const QImage& image, const std::string& srcImageName);
|
||||||
|
@ -47,7 +48,7 @@ public:
|
||||||
static const QImage process2DImageColor(const QImage& srcImage, bool& validAlpha, bool& alphaAsMask);
|
static const QImage process2DImageColor(const QImage& srcImage, bool& validAlpha, bool& alphaAsMask);
|
||||||
static void defineColorTexelFormats(gpu::Element& formatGPU, gpu::Element& formatMip,
|
static void defineColorTexelFormats(gpu::Element& formatGPU, gpu::Element& formatMip,
|
||||||
const QImage& srcImage, bool isLinear, bool doCompress);
|
const QImage& srcImage, bool isLinear, bool doCompress);
|
||||||
static gpu::Texture* process2DTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool isLinear, bool doCompress, bool generateMips);
|
static gpu::Texture* process2DTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool isLinear, bool doCompress, bool generateMips, bool isStrict = false);
|
||||||
static gpu::Texture* processCubeTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool isLinear, bool doCompress, bool generateMips, bool generateIrradiance);
|
static gpu::Texture* processCubeTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool isLinear, bool doCompress, bool generateMips, bool generateIrradiance);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -52,7 +52,7 @@ const gpu::PipelinePointer& Antialiasing::getAntialiasingPipeline() {
|
||||||
_antialiasingBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("antialiasing"));
|
_antialiasingBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("antialiasing"));
|
||||||
auto format = gpu::Element::COLOR_SRGBA_32; // DependencyManager::get<FramebufferCache>()->getLightingTexture()->getTexelFormat();
|
auto format = gpu::Element::COLOR_SRGBA_32; // DependencyManager::get<FramebufferCache>()->getLightingTexture()->getTexelFormat();
|
||||||
auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT);
|
auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT);
|
||||||
_antialiasingTexture = gpu::TexturePointer(gpu::Texture::create2D(format, width, height, defaultSampler));
|
_antialiasingTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(format, width, height, defaultSampler));
|
||||||
_antialiasingBuffer->setRenderBuffer(0, _antialiasingTexture);
|
_antialiasingBuffer->setRenderBuffer(0, _antialiasingTexture);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -53,9 +53,9 @@ void DeferredFramebuffer::allocate() {
|
||||||
|
|
||||||
auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT);
|
auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT);
|
||||||
|
|
||||||
_deferredColorTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width, height, defaultSampler));
|
_deferredColorTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(colorFormat, width, height, defaultSampler));
|
||||||
_deferredNormalTexture = gpu::TexturePointer(gpu::Texture::create2D(linearFormat, width, height, defaultSampler));
|
_deferredNormalTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(linearFormat, width, height, defaultSampler));
|
||||||
_deferredSpecularTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width, height, defaultSampler));
|
_deferredSpecularTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(colorFormat, width, height, defaultSampler));
|
||||||
|
|
||||||
_deferredFramebuffer->setRenderBuffer(0, _deferredColorTexture);
|
_deferredFramebuffer->setRenderBuffer(0, _deferredColorTexture);
|
||||||
_deferredFramebuffer->setRenderBuffer(1, _deferredNormalTexture);
|
_deferredFramebuffer->setRenderBuffer(1, _deferredNormalTexture);
|
||||||
|
@ -65,7 +65,7 @@ void DeferredFramebuffer::allocate() {
|
||||||
|
|
||||||
auto depthFormat = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::DEPTH_STENCIL); // Depth24_Stencil8 texel format
|
auto depthFormat = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::DEPTH_STENCIL); // Depth24_Stencil8 texel format
|
||||||
if (!_primaryDepthTexture) {
|
if (!_primaryDepthTexture) {
|
||||||
_primaryDepthTexture = gpu::TexturePointer(gpu::Texture::create2D(depthFormat, width, height, defaultSampler));
|
_primaryDepthTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(depthFormat, width, height, defaultSampler));
|
||||||
}
|
}
|
||||||
|
|
||||||
_deferredFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, depthFormat);
|
_deferredFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, depthFormat);
|
||||||
|
@ -75,7 +75,7 @@ void DeferredFramebuffer::allocate() {
|
||||||
|
|
||||||
auto smoothSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR);
|
auto smoothSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR);
|
||||||
|
|
||||||
_lightingTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::R11G11B10), width, height, defaultSampler));
|
_lightingTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::R11G11B10), width, height, defaultSampler));
|
||||||
_lightingFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("lighting"));
|
_lightingFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("lighting"));
|
||||||
_lightingFramebuffer->setRenderBuffer(0, _lightingTexture);
|
_lightingFramebuffer->setRenderBuffer(0, _lightingTexture);
|
||||||
_lightingFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, depthFormat);
|
_lightingFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, depthFormat);
|
||||||
|
|
|
@ -496,14 +496,14 @@ void PreparePrimaryFramebuffer::run(const SceneContextPointer& sceneContext, con
|
||||||
auto colorFormat = gpu::Element::COLOR_SRGBA_32;
|
auto colorFormat = gpu::Element::COLOR_SRGBA_32;
|
||||||
|
|
||||||
auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT);
|
auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT);
|
||||||
auto primaryColorTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, frameSize.x, frameSize.y, defaultSampler));
|
auto primaryColorTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(colorFormat, frameSize.x, frameSize.y, defaultSampler));
|
||||||
|
|
||||||
|
|
||||||
_primaryFramebuffer->setRenderBuffer(0, primaryColorTexture);
|
_primaryFramebuffer->setRenderBuffer(0, primaryColorTexture);
|
||||||
|
|
||||||
|
|
||||||
auto depthFormat = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::DEPTH_STENCIL); // Depth24_Stencil8 texel format
|
auto depthFormat = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::DEPTH_STENCIL); // Depth24_Stencil8 texel format
|
||||||
auto primaryDepthTexture = gpu::TexturePointer(gpu::Texture::create2D(depthFormat, frameSize.x, frameSize.y, defaultSampler));
|
auto primaryDepthTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(depthFormat, frameSize.x, frameSize.y, defaultSampler));
|
||||||
|
|
||||||
_primaryFramebuffer->setDepthStencilBuffer(primaryDepthTexture, depthFormat);
|
_primaryFramebuffer->setDepthStencilBuffer(primaryDepthTexture, depthFormat);
|
||||||
}
|
}
|
||||||
|
|
|
@ -190,7 +190,7 @@ RenderDeferredTask::RenderDeferredTask(RenderFetchCullSortTask::Output items) {
|
||||||
{
|
{
|
||||||
// Grab a texture map representing the different status icons and assign that to the drawStatsuJob
|
// Grab a texture map representing the different status icons and assign that to the drawStatsuJob
|
||||||
auto iconMapPath = PathUtils::resourcesPath() + "icons/statusIconAtlas.svg";
|
auto iconMapPath = PathUtils::resourcesPath() + "icons/statusIconAtlas.svg";
|
||||||
auto statusIconMap = DependencyManager::get<TextureCache>()->getImageTexture(iconMapPath);
|
auto statusIconMap = DependencyManager::get<TextureCache>()->getImageTexture(iconMapPath, NetworkTexture::STRICT_TEXTURE);
|
||||||
addJob<DrawStatus>("DrawStatus", opaques, DrawStatus(statusIconMap));
|
addJob<DrawStatus>("DrawStatus", opaques, DrawStatus(statusIconMap));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -414,7 +414,7 @@ gpu::TexturePointer SubsurfaceScatteringResource::generateScatteringProfile(Rend
|
||||||
const int PROFILE_RESOLUTION = 512;
|
const int PROFILE_RESOLUTION = 512;
|
||||||
// const auto pixelFormat = gpu::Element::COLOR_SRGBA_32;
|
// const auto pixelFormat = gpu::Element::COLOR_SRGBA_32;
|
||||||
const auto pixelFormat = gpu::Element::COLOR_R11G11B10;
|
const auto pixelFormat = gpu::Element::COLOR_R11G11B10;
|
||||||
auto profileMap = gpu::TexturePointer(gpu::Texture::create2D(pixelFormat, PROFILE_RESOLUTION, 1, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP)));
|
auto profileMap = gpu::TexturePointer(gpu::Texture::createRenderBuffer(pixelFormat, PROFILE_RESOLUTION, 1, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP)));
|
||||||
profileMap->setSource("Generated Scattering Profile");
|
profileMap->setSource("Generated Scattering Profile");
|
||||||
diffuseProfileGPU(profileMap, args);
|
diffuseProfileGPU(profileMap, args);
|
||||||
return profileMap;
|
return profileMap;
|
||||||
|
@ -425,7 +425,7 @@ gpu::TexturePointer SubsurfaceScatteringResource::generatePreIntegratedScatterin
|
||||||
const int TABLE_RESOLUTION = 512;
|
const int TABLE_RESOLUTION = 512;
|
||||||
// const auto pixelFormat = gpu::Element::COLOR_SRGBA_32;
|
// const auto pixelFormat = gpu::Element::COLOR_SRGBA_32;
|
||||||
const auto pixelFormat = gpu::Element::COLOR_R11G11B10;
|
const auto pixelFormat = gpu::Element::COLOR_R11G11B10;
|
||||||
auto scatteringLUT = gpu::TexturePointer(gpu::Texture::create2D(pixelFormat, TABLE_RESOLUTION, TABLE_RESOLUTION, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP)));
|
auto scatteringLUT = gpu::TexturePointer(gpu::Texture::createRenderBuffer(pixelFormat, TABLE_RESOLUTION, TABLE_RESOLUTION, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP)));
|
||||||
//diffuseScatter(scatteringLUT);
|
//diffuseScatter(scatteringLUT);
|
||||||
scatteringLUT->setSource("Generated pre-integrated scattering");
|
scatteringLUT->setSource("Generated pre-integrated scattering");
|
||||||
diffuseScatterGPU(profile, scatteringLUT, args);
|
diffuseScatterGPU(profile, scatteringLUT, args);
|
||||||
|
@ -434,7 +434,7 @@ gpu::TexturePointer SubsurfaceScatteringResource::generatePreIntegratedScatterin
|
||||||
|
|
||||||
gpu::TexturePointer SubsurfaceScatteringResource::generateScatteringSpecularBeckmann(RenderArgs* args) {
|
gpu::TexturePointer SubsurfaceScatteringResource::generateScatteringSpecularBeckmann(RenderArgs* args) {
|
||||||
const int SPECULAR_RESOLUTION = 256;
|
const int SPECULAR_RESOLUTION = 256;
|
||||||
auto beckmannMap = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32 /*gpu::Element(gpu::SCALAR, gpu::HALF, gpu::RGB)*/, SPECULAR_RESOLUTION, SPECULAR_RESOLUTION, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP)));
|
auto beckmannMap = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32 /*gpu::Element(gpu::SCALAR, gpu::HALF, gpu::RGB)*/, SPECULAR_RESOLUTION, SPECULAR_RESOLUTION, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP)));
|
||||||
beckmannMap->setSource("Generated beckmannMap");
|
beckmannMap->setSource("Generated beckmannMap");
|
||||||
computeSpecularBeckmannGPU(beckmannMap, args);
|
computeSpecularBeckmannGPU(beckmannMap, args);
|
||||||
return beckmannMap;
|
return beckmannMap;
|
||||||
|
|
|
@ -72,18 +72,18 @@ void LinearDepthFramebuffer::allocate() {
|
||||||
auto height = _frameSize.y;
|
auto height = _frameSize.y;
|
||||||
|
|
||||||
// For Linear Depth:
|
// For Linear Depth:
|
||||||
_linearDepthTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::RGB), width, height,
|
_linearDepthTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::RGB), width, height,
|
||||||
gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT)));
|
gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT)));
|
||||||
_linearDepthFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("linearDepth"));
|
_linearDepthFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("linearDepth"));
|
||||||
_linearDepthFramebuffer->setRenderBuffer(0, _linearDepthTexture);
|
_linearDepthFramebuffer->setRenderBuffer(0, _linearDepthTexture);
|
||||||
_linearDepthFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, _primaryDepthTexture->getTexelFormat());
|
_linearDepthFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, _primaryDepthTexture->getTexelFormat());
|
||||||
|
|
||||||
// For Downsampling:
|
// For Downsampling:
|
||||||
_halfLinearDepthTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::RGB), _halfFrameSize.x, _halfFrameSize.y,
|
_halfLinearDepthTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::RGB), _halfFrameSize.x, _halfFrameSize.y,
|
||||||
gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT)));
|
gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT)));
|
||||||
_halfLinearDepthTexture->autoGenerateMips(5);
|
_halfLinearDepthTexture->autoGenerateMips(5);
|
||||||
|
|
||||||
_halfNormalTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB), _halfFrameSize.x, _halfFrameSize.y,
|
_halfNormalTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB), _halfFrameSize.x, _halfFrameSize.y,
|
||||||
gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT)));
|
gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT)));
|
||||||
|
|
||||||
_downsampleFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("halfLinearDepth"));
|
_downsampleFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("halfLinearDepth"));
|
||||||
|
@ -304,15 +304,15 @@ void SurfaceGeometryFramebuffer::allocate() {
|
||||||
auto width = _frameSize.x;
|
auto width = _frameSize.x;
|
||||||
auto height = _frameSize.y;
|
auto height = _frameSize.y;
|
||||||
|
|
||||||
_curvatureTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, width, height, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT)));
|
_curvatureTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, width, height, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT)));
|
||||||
_curvatureFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("surfaceGeometry::curvature"));
|
_curvatureFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("surfaceGeometry::curvature"));
|
||||||
_curvatureFramebuffer->setRenderBuffer(0, _curvatureTexture);
|
_curvatureFramebuffer->setRenderBuffer(0, _curvatureTexture);
|
||||||
|
|
||||||
_lowCurvatureTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, width, height, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT)));
|
_lowCurvatureTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, width, height, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT)));
|
||||||
_lowCurvatureFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("surfaceGeometry::lowCurvature"));
|
_lowCurvatureFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("surfaceGeometry::lowCurvature"));
|
||||||
_lowCurvatureFramebuffer->setRenderBuffer(0, _lowCurvatureTexture);
|
_lowCurvatureFramebuffer->setRenderBuffer(0, _lowCurvatureTexture);
|
||||||
|
|
||||||
_blurringTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, width, height, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT)));
|
_blurringTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, width, height, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT)));
|
||||||
_blurringFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("surfaceGeometry::blurring"));
|
_blurringFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("surfaceGeometry::blurring"));
|
||||||
_blurringFramebuffer->setRenderBuffer(0, _blurringTexture);
|
_blurringFramebuffer->setRenderBuffer(0, _blurringTexture);
|
||||||
}
|
}
|
||||||
|
|
|
@ -255,7 +255,7 @@ void OculusLegacyDisplayPlugin::hmdPresent() {
|
||||||
memset(eyePoses, 0, sizeof(ovrPosef) * 2);
|
memset(eyePoses, 0, sizeof(ovrPosef) * 2);
|
||||||
eyePoses[0].Orientation = eyePoses[1].Orientation = ovrRotation;
|
eyePoses[0].Orientation = eyePoses[1].Orientation = ovrRotation;
|
||||||
|
|
||||||
GLint texture = getGLBackend()->getTextureID(_compositeFramebuffer->getRenderBuffer(0), false);
|
GLint texture = getGLBackend()->getTextureID(_compositeFramebuffer->getRenderBuffer(0));
|
||||||
auto sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
auto sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||||
glFlush();
|
glFlush();
|
||||||
if (_hmdWindow->makeCurrent()) {
|
if (_hmdWindow->makeCurrent()) {
|
||||||
|
|
|
@ -494,9 +494,9 @@ void OpenVrDisplayPlugin::customizeContext() {
|
||||||
_compositeInfos[0].texture = _compositeFramebuffer->getRenderBuffer(0);
|
_compositeInfos[0].texture = _compositeFramebuffer->getRenderBuffer(0);
|
||||||
for (size_t i = 0; i < COMPOSITING_BUFFER_SIZE; ++i) {
|
for (size_t i = 0; i < COMPOSITING_BUFFER_SIZE; ++i) {
|
||||||
if (0 != i) {
|
if (0 != i) {
|
||||||
_compositeInfos[i].texture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, _renderTargetSize.x, _renderTargetSize.y, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT)));
|
_compositeInfos[i].texture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, _renderTargetSize.x, _renderTargetSize.y, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT)));
|
||||||
}
|
}
|
||||||
_compositeInfos[i].textureID = getGLBackend()->getTextureID(_compositeInfos[i].texture, false);
|
_compositeInfos[i].textureID = getGLBackend()->getTextureID(_compositeInfos[i].texture);
|
||||||
}
|
}
|
||||||
_submitThread->_canvas = _submitCanvas;
|
_submitThread->_canvas = _submitCanvas;
|
||||||
_submitThread->start(QThread::HighPriority);
|
_submitThread->start(QThread::HighPriority);
|
||||||
|
@ -624,7 +624,7 @@ void OpenVrDisplayPlugin::compositeLayers() {
|
||||||
glFlush();
|
glFlush();
|
||||||
|
|
||||||
if (!newComposite.textureID) {
|
if (!newComposite.textureID) {
|
||||||
newComposite.textureID = getGLBackend()->getTextureID(newComposite.texture, false);
|
newComposite.textureID = getGLBackend()->getTextureID(newComposite.texture);
|
||||||
}
|
}
|
||||||
withPresentThreadLock([&] {
|
withPresentThreadLock([&] {
|
||||||
_submitThread->update(newComposite);
|
_submitThread->update(newComposite);
|
||||||
|
@ -638,7 +638,7 @@ void OpenVrDisplayPlugin::hmdPresent() {
|
||||||
if (_threadedSubmit) {
|
if (_threadedSubmit) {
|
||||||
_submitThread->waitForPresent();
|
_submitThread->waitForPresent();
|
||||||
} else {
|
} else {
|
||||||
GLuint glTexId = getGLBackend()->getTextureID(_compositeFramebuffer->getRenderBuffer(0), false);
|
GLuint glTexId = getGLBackend()->getTextureID(_compositeFramebuffer->getRenderBuffer(0));
|
||||||
vr::Texture_t vrTexture { (void*)glTexId, vr::API_OpenGL, vr::ColorSpace_Auto };
|
vr::Texture_t vrTexture { (void*)glTexId, vr::API_OpenGL, vr::ColorSpace_Auto };
|
||||||
vr::VRCompositor()->Submit(vr::Eye_Left, &vrTexture, &OPENVR_TEXTURE_BOUNDS_LEFT);
|
vr::VRCompositor()->Submit(vr::Eye_Left, &vrTexture, &OPENVR_TEXTURE_BOUNDS_LEFT);
|
||||||
vr::VRCompositor()->Submit(vr::Eye_Right, &vrTexture, &OPENVR_TEXTURE_BOUNDS_RIGHT);
|
vr::VRCompositor()->Submit(vr::Eye_Right, &vrTexture, &OPENVR_TEXTURE_BOUNDS_RIGHT);
|
||||||
|
|
|
@ -642,7 +642,6 @@ protected:
|
||||||
gpu::Texture::setAllowedGPUMemoryUsage(MB_TO_BYTES(64));
|
gpu::Texture::setAllowedGPUMemoryUsage(MB_TO_BYTES(64));
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,6 +48,7 @@
|
||||||
#include <gpu/gl/GLTexture.h>
|
#include <gpu/gl/GLTexture.h>
|
||||||
#include <gpu/StandardShaderLib.h>
|
#include <gpu/StandardShaderLib.h>
|
||||||
|
|
||||||
|
#include <GenericThread.h>
|
||||||
#include <AddressManager.h>
|
#include <AddressManager.h>
|
||||||
#include <NodeList.h>
|
#include <NodeList.h>
|
||||||
#include <TextureCache.h>
|
#include <TextureCache.h>
|
||||||
|
|
Loading…
Reference in a new issue