mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-06-27 13:50:49 +02:00
268 lines
9.9 KiB
C++
268 lines
9.9 KiB
C++
//
|
|
// Created by Bradley Austin Davis on 2016/05/15
|
|
// 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 "GLTexture.h"
|
|
|
|
#include <QtCore/QThread>
|
|
#include <NumericalConstants.h>
|
|
|
|
#include "GLBackend.h"
|
|
|
|
using namespace gpu;
|
|
using namespace gpu::gl;
|
|
|
|
|
|
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_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
|
|
GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
|
|
};
|
|
|
|
const GLenum GLTexture::WRAP_MODES[Sampler::NUM_WRAP_MODES] = {
|
|
GL_REPEAT, // WRAP_REPEAT,
|
|
GL_MIRRORED_REPEAT, // WRAP_MIRROR,
|
|
GL_CLAMP_TO_EDGE, // WRAP_CLAMP,
|
|
GL_CLAMP_TO_BORDER, // WRAP_BORDER,
|
|
GL_MIRROR_CLAMP_TO_EDGE_EXT // WRAP_MIRROR_ONCE,
|
|
};
|
|
|
|
const GLFilterMode GLTexture::FILTER_MODES[Sampler::NUM_FILTERS] = {
|
|
{ GL_NEAREST, GL_NEAREST }, //FILTER_MIN_MAG_POINT,
|
|
{ GL_NEAREST, GL_LINEAR }, //FILTER_MIN_POINT_MAG_LINEAR,
|
|
{ GL_LINEAR, GL_NEAREST }, //FILTER_MIN_LINEAR_MAG_POINT,
|
|
{ GL_LINEAR, GL_LINEAR }, //FILTER_MIN_MAG_LINEAR,
|
|
|
|
{ GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST }, //FILTER_MIN_MAG_MIP_POINT,
|
|
{ GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST }, //FILTER_MIN_MAG_POINT_MIP_LINEAR,
|
|
{ GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR }, //FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT,
|
|
{ GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR }, //FILTER_MIN_POINT_MAG_MIP_LINEAR,
|
|
{ GL_LINEAR_MIPMAP_NEAREST, GL_NEAREST }, //FILTER_MIN_LINEAR_MAG_MIP_POINT,
|
|
{ GL_LINEAR_MIPMAP_LINEAR, GL_NEAREST }, //FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR,
|
|
{ GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR }, //FILTER_MIN_MAG_LINEAR_MIP_POINT,
|
|
{ GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR }, //FILTER_MIN_MAG_MIP_LINEAR,
|
|
{ GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR } //FILTER_ANISOTROPIC,
|
|
};
|
|
|
|
static constexpr size_t MAX_PIXEL_BYTE_SIZE{ 4 };
|
|
static constexpr size_t MAX_TRANSFER_DIMENSION{ 1024 };
|
|
|
|
const uvec3 GLVariableAllocationSupport::MAX_TRANSFER_DIMENSIONS{ MAX_TRANSFER_DIMENSION, MAX_TRANSFER_DIMENSION, 1 };
|
|
const uvec3 GLVariableAllocationSupport::INITIAL_MIP_TRANSFER_DIMENSIONS{ 64, 64, 1 };
|
|
const size_t GLVariableAllocationSupport::MAX_TRANSFER_SIZE = MAX_TRANSFER_DIMENSION * MAX_TRANSFER_DIMENSION * MAX_PIXEL_BYTE_SIZE;
|
|
const size_t GLVariableAllocationSupport::MAX_BUFFER_SIZE = MAX_TRANSFER_SIZE;
|
|
|
|
GLenum GLTexture::getGLTextureType(const Texture& texture) {
|
|
switch (texture.getType()) {
|
|
case Texture::TEX_2D:
|
|
if (!texture.isArray()) {
|
|
return GL_TEXTURE_2D;
|
|
} else {
|
|
return GL_TEXTURE_2D_ARRAY;
|
|
}
|
|
break;
|
|
|
|
case Texture::TEX_CUBE:
|
|
return GL_TEXTURE_CUBE_MAP;
|
|
break;
|
|
|
|
default:
|
|
qFatal("Unsupported texture type");
|
|
}
|
|
Q_UNREACHABLE();
|
|
return GL_TEXTURE_2D;
|
|
}
|
|
|
|
|
|
uint8_t GLTexture::getFaceCount(GLenum target) {
|
|
switch (target) {
|
|
case GL_TEXTURE_2D:
|
|
case GL_TEXTURE_2D_ARRAY:
|
|
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) {
|
|
static const std::vector<GLenum> cubeFaceTargets {
|
|
GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
|
|
GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
|
|
GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
|
|
};
|
|
static const std::vector<GLenum> faceTargets {
|
|
GL_TEXTURE_2D
|
|
};
|
|
static const std::vector<GLenum> arrayFaceTargets{
|
|
GL_TEXTURE_2D_ARRAY
|
|
};
|
|
switch (target) {
|
|
case GL_TEXTURE_2D:
|
|
return faceTargets;
|
|
case GL_TEXTURE_2D_ARRAY:
|
|
return arrayFaceTargets;
|
|
case GL_TEXTURE_CUBE_MAP:
|
|
return cubeFaceTargets;
|
|
default:
|
|
Q_UNREACHABLE();
|
|
break;
|
|
}
|
|
Q_UNREACHABLE();
|
|
return faceTargets;
|
|
}
|
|
|
|
GLTexture::GLTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture, GLuint id) :
|
|
GLObject(backend, texture, id),
|
|
_source(texture.source()),
|
|
_target(getGLTextureType(texture)),
|
|
_texelFormat(GLTexelFormat::evalGLTexelFormatInternal(texture.getTexelFormat()))
|
|
{
|
|
Backend::setGPUObject(texture, this);
|
|
}
|
|
|
|
GLTexture::~GLTexture() {
|
|
auto backend = _backend.lock();
|
|
if (backend && _id) {
|
|
backend->releaseTexture(_id, 0);
|
|
}
|
|
}
|
|
|
|
Size GLTexture::copyMipFaceFromTexture(uint16_t sourceMip, uint16_t targetMip, uint8_t face) const {
|
|
if (!_gpuObject.isStoredMipFaceAvailable(sourceMip)) {
|
|
return 0;
|
|
}
|
|
auto dim = _gpuObject.evalMipDimensions(sourceMip);
|
|
auto mipData = _gpuObject.accessStoredMipFace(sourceMip, face);
|
|
auto mipSize = _gpuObject.getStoredMipFaceSize(sourceMip, face);
|
|
if (mipData) {
|
|
GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), _gpuObject.getStoredMipFormat());
|
|
return copyMipFaceLinesFromTexture(targetMip, face, dim, 0, texelFormat.internalFormat, texelFormat.format, texelFormat.type, mipSize, mipData->readData());
|
|
} else {
|
|
qCDebug(gpugllogging) << "Missing mipData level=" << sourceMip << " face=" << (int)face << " for texture " << _gpuObject.source().c_str();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
GLExternalTexture::GLExternalTexture(const std::weak_ptr<GLBackend>& backend, const Texture& texture, GLuint id)
|
|
: Parent(backend, texture, id) {
|
|
Backend::textureExternalCount.increment();
|
|
}
|
|
|
|
GLExternalTexture::~GLExternalTexture() {
|
|
auto backend = _backend.lock();
|
|
if (backend) {
|
|
auto recycler = _gpuObject.getExternalRecycler();
|
|
if (recycler) {
|
|
backend->releaseExternalTexture(_id, recycler);
|
|
} else {
|
|
qCWarning(gpugllogging) << "No recycler available for texture " << _id << " possible leak";
|
|
}
|
|
const_cast<GLuint&>(_id) = 0;
|
|
}
|
|
Backend::textureExternalCount.decrement();
|
|
}
|
|
|
|
GLVariableAllocationSupport::GLVariableAllocationSupport() {
|
|
Backend::textureResourceCount.increment();
|
|
}
|
|
|
|
GLVariableAllocationSupport::~GLVariableAllocationSupport() {
|
|
Backend::textureResourceCount.decrement();
|
|
Backend::textureResourceGPUMemSize.update(_size, 0);
|
|
Backend::textureResourcePopulatedGPUMemSize.update(_populatedSize, 0);
|
|
}
|
|
|
|
void GLVariableAllocationSupport::incrementPopulatedSize(Size delta) const {
|
|
_populatedSize += delta;
|
|
// Keep the 2 code paths to be able to debug
|
|
if (_size < _populatedSize) {
|
|
Backend::textureResourcePopulatedGPUMemSize.update(0, delta);
|
|
} else {
|
|
Backend::textureResourcePopulatedGPUMemSize.update(0, delta);
|
|
}
|
|
}
|
|
|
|
void GLVariableAllocationSupport::decrementPopulatedSize(Size delta) const {
|
|
_populatedSize -= delta;
|
|
// Keep the 2 code paths to be able to debug
|
|
if (_size < _populatedSize) {
|
|
Backend::textureResourcePopulatedGPUMemSize.update(delta, 0);
|
|
} else {
|
|
Backend::textureResourcePopulatedGPUMemSize.update(delta, 0);
|
|
}
|
|
}
|
|
|
|
void GLVariableAllocationSupport::sanityCheck() const {
|
|
if (_populatedMip < _allocatedMip) {
|
|
qCWarning(gpugllogging) << "Invalid mip levels";
|
|
}
|
|
}
|
|
|
|
TransferJob::TransferJob(const Texture& texture,
|
|
uint16_t sourceMip,
|
|
uint16_t targetMip,
|
|
uint8_t face,
|
|
uint32_t lines,
|
|
uint32_t lineOffset) {
|
|
auto transferDimensions = texture.evalMipDimensions(sourceMip);
|
|
GLenum format;
|
|
GLenum internalFormat;
|
|
GLenum type;
|
|
GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(texture.getTexelFormat(), texture.getStoredMipFormat());
|
|
format = texelFormat.format;
|
|
internalFormat = texelFormat.internalFormat;
|
|
type = texelFormat.type;
|
|
_transferSize = texture.getStoredMipFaceSize(sourceMip, face);
|
|
|
|
// If we're copying a subsection of the mip, do additional calculations to find the size and offset of the segment
|
|
if (0 != lines) {
|
|
transferDimensions.y = lines;
|
|
auto dimensions = texture.evalMipDimensions(sourceMip);
|
|
auto bytesPerLine = (uint32_t)_transferSize / dimensions.y;
|
|
_transferOffset = bytesPerLine * lineOffset;
|
|
_transferSize = bytesPerLine * lines;
|
|
}
|
|
|
|
Backend::texturePendingGPUTransferMemSize.update(0, _transferSize);
|
|
|
|
if (_transferSize > GLVariableAllocationSupport::MAX_TRANSFER_SIZE) {
|
|
qCWarning(gpugllogging) << "Transfer size of " << _transferSize << " exceeds theoretical maximum transfer size";
|
|
}
|
|
|
|
// Buffering can invoke disk IO, so it should be off of the main and render threads
|
|
_bufferingLambda = [=](const TexturePointer& texture) {
|
|
auto mipStorage = texture->accessStoredMipFace(sourceMip, face);
|
|
if (mipStorage) {
|
|
_mipData = mipStorage->createView(_transferSize, _transferOffset);
|
|
} else {
|
|
qCWarning(gpugllogging) << "Buffering failed because mip could not be retrieved from texture "
|
|
<< texture->source().c_str();
|
|
}
|
|
};
|
|
|
|
_transferLambda = [=](const TexturePointer& texture) {
|
|
if (_mipData) {
|
|
auto gltexture = Backend::getGPUObject<GLTexture>(*texture);
|
|
gltexture->copyMipFaceLinesFromTexture(targetMip, face, transferDimensions, lineOffset, internalFormat, format,
|
|
type, _mipData->size(), _mipData->readData());
|
|
_mipData.reset();
|
|
} else {
|
|
qCWarning(gpugllogging) << "Transfer failed because mip could not be retrieved from texture "
|
|
<< texture->source().c_str();
|
|
}
|
|
};
|
|
}
|
|
|
|
TransferJob::TransferJob(uint16_t sourceMip, const std::function<void()>& transferLambda) :
|
|
_sourceMip(sourceMip), _bufferingRequired(false), _transferLambda([=](const TexturePointer&) { transferLambda(); }) {}
|
|
|
|
TransferJob::~TransferJob() {
|
|
Backend::texturePendingGPUTransferMemSize.update(_transferSize, 0);
|
|
}
|
|
|