580 lines · 21.5 KB · C++
RawDownload
////  TextureCache.cpp//  interface/src/renderer////  Created by Andrzej Kapolka on 8/6/13.//  Copyright 2013 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 this before QGLWidget, which includes an earlier version of OpenGL#include <gpu/GPUConfig.h> #include <QEvent>#include <QGLWidget>#include <QNetworkReply>#include <QOpenGLFramebufferObject>#include <QResizeEvent>#include <QRunnable>#include <QThreadPool>#include <QOpenGLContext> #include <glm/glm.hpp>#include <glm/gtc/random.hpp> #include "RenderUtilsLogging.h"#include "TextureCache.h" #include "gpu/GLBackend.h" TextureCache::TextureCache() :    _permutationNormalTexture(0),    _whiteTexture(0),    _blueTexture(0),    _frameBufferSize(100, 100){    const qint64 TEXTURE_DEFAULT_UNUSED_MAX_SIZE = DEFAULT_UNUSED_MAX_SIZE;    setUnusedResourceCacheSize(TEXTURE_DEFAULT_UNUSED_MAX_SIZE);} TextureCache::~TextureCache() {} void TextureCache::setFrameBufferSize(QSize frameBufferSize) {    //If the size changed, we need to delete our FBOs    if (_frameBufferSize != frameBufferSize) {        _frameBufferSize = frameBufferSize;         _primaryFramebuffer.reset();        _primaryDepthTexture.reset();        _primaryColorTexture.reset();        _primaryNormalTexture.reset();        _primarySpecularTexture.reset();         _secondaryFramebuffer.reset();         _tertiaryFramebuffer.reset();    }} // use fixed table of permutations. Could also make ordered list programmatically// and then shuffle algorithm. For testing, this ensures consistent behavior in each run.// this list taken from Ken Perlin's Improved Noise reference implementation (orig. in Java) at// http://mrl.nyu.edu/~perlin/noise/ const int permutation[256] = {    151, 160, 137,  91,  90,  15, 131,  13, 201,  95,  96,  53, 194, 233,   7, 225,    140,  36, 103,  30,  69, 142,   8,  99,  37, 240,  21,  10,  23, 190,   6, 148,    247, 120, 234,  75,   0,  26, 197,  62,  94, 252, 219, 203, 117,  35,  11,  32,     57, 177,  33,  88, 237, 149,  56,  87, 174,  20, 125, 136, 171, 168,  68, 175,     74, 165,  71, 134, 139,  48,  27, 166,  77, 146, 158, 231,  83, 111, 229, 122,     60, 211, 133, 230, 220, 105,  92,  41,  55,  46, 245,  40, 244, 102, 143,  54,     65,  25,  63, 161,   1, 216,  80,  73, 209,  76, 132, 187, 208,  89,  18, 169,     200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186,   3,  64,     52, 217, 226, 250, 124, 123,   5, 202,  38, 147, 118, 126, 255,  82,  85, 212,    207, 206,  59, 227,  47,  16,  58,  17, 182, 189,  28,  42, 223, 183, 170, 213,    119, 248, 152,   2,  44, 154, 163,  70, 221, 153, 101, 155, 167,  43, 172,   9,    129,  22,  39, 253,  19,  98, 108, 110,  79, 113, 224, 232, 178, 185, 112, 104,    218, 246,  97, 228, 251,  34, 242, 193, 238, 210, 144,  12, 191, 179, 162, 241,     81,  51, 145, 235, 249,  14, 239, 107,  49, 192, 214,  31, 181, 199, 106, 157,    184,  84, 204, 176, 115, 121,  50,  45, 127,   4, 150, 254, 138, 236, 205,  93,    222, 114,  67,  29,  24,  72, 243, 141, 128, 195,  78,  66, 215,  61, 156, 180}; #define USE_CHRIS_NOISE 1 const gpu::TexturePointer& TextureCache::getPermutationNormalTexture() {    if (_permutationNormalTexture.isNull()) {         // the first line consists of random permutation offsets        unsigned char data[256 * 2 * 3];#if (USE_CHRIS_NOISE==1)        for (int i = 0; i < 256; i++) {            data[3*i+0] = permutation[i];            data[3*i+1] = permutation[i];            data[3*i+2] = permutation[i];#else        for (int i = 0; i < 256 * 3; i++) {            data[i] = rand() % 256;#endif        }         for (int i = 256 * 3; i < 256 * 3 * 2; i += 3) {            glm::vec3 randvec = glm::sphericalRand(1.0f);            data[i] = ((randvec.x + 1.0f) / 2.0f) * 255.0f;            data[i + 1] = ((randvec.y + 1.0f) / 2.0f) * 255.0f;            data[i + 2] = ((randvec.z + 1.0f) / 2.0f) * 255.0f;        }         _permutationNormalTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC3, gpu::UINT8, gpu::RGB), 256, 2));        _permutationNormalTexture->assignStoredMip(0, _blueTexture->getTexelFormat(), sizeof(data), data);        // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);       // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);    }    return _permutationNormalTexture;} const unsigned char OPAQUE_WHITE[] = { 0xFF, 0xFF, 0xFF, 0xFF };//const unsigned char TRANSPARENT_WHITE[] = { 0xFF, 0xFF, 0xFF, 0x0 };//const unsigned char OPAQUE_BLACK[] = { 0x0, 0x0, 0x0, 0xFF };const unsigned char OPAQUE_BLUE[] = { 0x80, 0x80, 0xFF, 0xFF }; /*static void loadSingleColorTexture(const unsigned char* color) {    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, color);    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);}*/ const gpu::TexturePointer& TextureCache::getWhiteTexture() {    if (_whiteTexture.isNull()) {        _whiteTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC4, gpu::UINT8, gpu::RGBA), 1, 1));        _whiteTexture->assignStoredMip(0, _whiteTexture->getTexelFormat(), sizeof(OPAQUE_WHITE), OPAQUE_WHITE);    }    return _whiteTexture;} const gpu::TexturePointer& TextureCache::getBlueTexture() {    if (_blueTexture.isNull()) {        _blueTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC4, gpu::UINT8, gpu::RGBA), 1, 1));        _blueTexture->assignStoredMip(0, _blueTexture->getTexelFormat(), sizeof(OPAQUE_BLUE), OPAQUE_BLUE);    }    return _blueTexture;} /// Extra data for creating textures.class TextureExtra {public:    TextureType type;    const QByteArray& content;}; NetworkTexturePointer TextureCache::getTexture(const QUrl& url, TextureType type, bool dilatable, const QByteArray& content) {    if (!dilatable) {        TextureExtra extra = { type, content };        return ResourceCache::getResource(url, QUrl(), false, &extra).staticCast<NetworkTexture>();    }    NetworkTexturePointer texture = _dilatableNetworkTextures.value(url);    if (texture.isNull()) {        texture = NetworkTexturePointer(new DilatableNetworkTexture(url, content), &Resource::allReferencesCleared);        texture->setSelf(texture);        texture->setCache(this);        _dilatableNetworkTextures.insert(url, texture);    } else {        removeUnusedResource(texture);    }    return texture;} void TextureCache::createPrimaryFramebuffer() {    _primaryFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create());     auto colorFormat = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA);    auto width = _frameBufferSize.width();    auto height = _frameBufferSize.height();     auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT);    _primaryColorTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width, height, defaultSampler));    _primaryNormalTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width, height, defaultSampler));    _primarySpecularTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width, height, defaultSampler));     _primaryFramebuffer->setRenderBuffer(0, _primaryColorTexture);    _primaryFramebuffer->setRenderBuffer(1, _primaryNormalTexture);    _primaryFramebuffer->setRenderBuffer(2, _primarySpecularTexture);      auto depthFormat = gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPTH);    _primaryDepthTexture = gpu::TexturePointer(gpu::Texture::create2D(depthFormat, width, height, defaultSampler));     _primaryFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, depthFormat);} gpu::FramebufferPointer TextureCache::getPrimaryFramebuffer() {    if (!_primaryFramebuffer) {        createPrimaryFramebuffer();    }    return _primaryFramebuffer;} gpu::TexturePointer TextureCache::getPrimaryDepthTexture() {    if (!_primaryDepthTexture) {        createPrimaryFramebuffer();    }    return _primaryDepthTexture;} gpu::TexturePointer TextureCache::getPrimaryColorTexture() {    if (!_primaryColorTexture) {        createPrimaryFramebuffer();    }    return _primaryColorTexture;} gpu::TexturePointer TextureCache::getPrimaryNormalTexture() {    if (!_primaryNormalTexture) {        createPrimaryFramebuffer();    }    return _primaryNormalTexture;} gpu::TexturePointer TextureCache::getPrimarySpecularTexture() {    if (!_primarySpecularTexture) {        createPrimaryFramebuffer();    }    return _primarySpecularTexture;} GLuint TextureCache::getPrimaryDepthTextureID() {    return gpu::GLBackend::getTextureID(getPrimaryDepthTexture());} GLuint TextureCache::getPrimaryColorTextureID() {    return gpu::GLBackend::getTextureID(getPrimaryColorTexture());} GLuint TextureCache::getPrimaryNormalTextureID() {    return gpu::GLBackend::getTextureID(getPrimaryNormalTexture());} GLuint TextureCache::getPrimarySpecularTextureID() {    return gpu::GLBackend::getTextureID(getPrimarySpecularTexture());} void TextureCache::setPrimaryDrawBuffers(bool color, bool normal, bool specular) {    GLenum buffers[3];    int bufferCount = 0;    if (color) {        buffers[bufferCount++] = GL_COLOR_ATTACHMENT0;    }    if (normal) {        buffers[bufferCount++] = GL_COLOR_ATTACHMENT1;    }    if (specular) {        buffers[bufferCount++] = GL_COLOR_ATTACHMENT2;    }    glDrawBuffers(bufferCount, buffers);} gpu::FramebufferPointer TextureCache::getSecondaryFramebuffer() {    if (!_secondaryFramebuffer) {        _secondaryFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create(gpu::Element::COLOR_RGBA_32, _frameBufferSize.width(), _frameBufferSize.height()));    }    return _secondaryFramebuffer;} gpu::FramebufferPointer TextureCache::getTertiaryFramebuffer() {    if (!_tertiaryFramebuffer) {        _tertiaryFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create(gpu::Element::COLOR_RGBA_32, _frameBufferSize.width(), _frameBufferSize.height()));    }    return _tertiaryFramebuffer;}  gpu::FramebufferPointer TextureCache::getShadowFramebuffer() {    if (!_shadowFramebuffer) {        const int SHADOW_MAP_SIZE = 2048;        _shadowFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::createShadowmap(SHADOW_MAP_SIZE));         _shadowTexture = _shadowFramebuffer->getDepthStencilBuffer();    }    return _shadowFramebuffer;} GLuint TextureCache::getShadowDepthTextureID() {    // ensure that the shadow framebuffer object is initialized before returning the depth texture id    getShadowFramebuffer();    return gpu::GLBackend::getTextureID(_shadowTexture);} /// Returns a texture version of an image filegpu::TexturePointer TextureCache::getImageTexture(const QString & path) {    QImage image(path);    gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::UINT8, gpu::RGB);    gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::UINT8, gpu::RGB);    if (image.hasAlphaChannel()) {        formatGPU = gpu::Element(gpu::VEC4, gpu::UINT8, gpu::RGBA);        formatMip = gpu::Element(gpu::VEC4, gpu::UINT8, gpu::BGRA);    }    gpu::TexturePointer texture = gpu::TexturePointer(        gpu::Texture::create2D(formatGPU, image.width(), image.height(),             gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)));    texture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits());    texture->autoGenerateMips(-1);    return texture;} QSharedPointer<Resource> TextureCache::createResource(const QUrl& url,        const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) {    const TextureExtra* textureExtra = static_cast<const TextureExtra*>(extra);    return QSharedPointer<Resource>(new NetworkTexture(url, textureExtra->type, textureExtra->content),        &Resource::allReferencesCleared);} Texture::Texture() {} Texture::~Texture() {} GLuint Texture::getID() const {    return gpu::GLBackend::getTextureID(_gpuTexture);} NetworkTexture::NetworkTexture(const QUrl& url, TextureType type, const QByteArray& content) :    Resource(url, !content.isEmpty()),    _type(type),    _translucent(false),    _width(0),    _height(0) {        if (!url.isValid()) {        _loaded = true;    }     // default to white/blue/black  /*  glBindTexture(GL_TEXTURE_2D, getID());    switch (type) {        case NORMAL_TEXTURE:            loadSingleColorTexture(OPAQUE_BLUE);              break;                case SPECULAR_TEXTURE:            loadSingleColorTexture(OPAQUE_BLACK);              break;                    case SPLAT_TEXTURE:            loadSingleColorTexture(TRANSPARENT_WHITE);               break;                    default:            loadSingleColorTexture(OPAQUE_WHITE);                    break;    }    glBindTexture(GL_TEXTURE_2D, 0);    */    // if we have content, load it after we have our self pointer    if (!content.isEmpty()) {        _startedLoading = true;        QMetaObject::invokeMethod(this, "loadContent", Qt::QueuedConnection, Q_ARG(const QByteArray&, content));    }} class ImageReader : public QRunnable {public:     ImageReader(const QWeakPointer<Resource>& texture, QNetworkReply* reply, const QUrl& url = QUrl(),        const QByteArray& content = QByteArray());        virtual void run(); private:        QWeakPointer<Resource> _texture;    QNetworkReply* _reply;    QUrl _url;    QByteArray _content;}; ImageReader::ImageReader(const QWeakPointer<Resource>& texture, QNetworkReply* reply,        const QUrl& url, const QByteArray& content) :    _texture(texture),    _reply(reply),    _url(url),    _content(content) {} void ImageReader::run() {    QSharedPointer<Resource> texture = _texture.toStrongRef();    if (texture.isNull()) {        if (_reply) {            _reply->deleteLater();        }        return;    }    if (_reply) {        _url = _reply->url();        _content = _reply->readAll();        _reply->deleteLater();    }    QImage image = QImage::fromData(_content);     int originalWidth = image.width();    int originalHeight = image.height();        // enforce a fixed maximum area (1024 * 2048)    const int MAXIMUM_AREA_SIZE = 2097152;    int imageArea = image.width() * image.height();    if (imageArea > MAXIMUM_AREA_SIZE) {        float scaleRatio = sqrtf((float)MAXIMUM_AREA_SIZE) / sqrtf((float)imageArea);        int resizeWidth = static_cast<int>(std::floor(scaleRatio * static_cast<float>(image.width())));        int resizeHeight = static_cast<int>(std::floor(scaleRatio * static_cast<float>(image.height())));        qCDebug(renderutils) << "Image greater than maximum size:" << _url << image.width() << image.height() <<            " scaled to:" << resizeWidth << resizeHeight;        image = image.scaled(resizeWidth, resizeHeight, Qt::IgnoreAspectRatio);        imageArea = image.width() * image.height();    }        const int EIGHT_BIT_MAXIMUM = 255;    if (!image.hasAlphaChannel()) {        if (image.format() != QImage::Format_RGB888) {            image = image.convertToFormat(QImage::Format_RGB888);        }        int redTotal = 0, greenTotal = 0, blueTotal = 0;        for (int y = 0; y < image.height(); y++) {            for (int x = 0; x < image.width(); x++) {                QRgb rgb = image.pixel(x, y);                redTotal += qRed(rgb);                greenTotal += qGreen(rgb);                blueTotal += qBlue(rgb);            }        }        QColor averageColor(EIGHT_BIT_MAXIMUM, EIGHT_BIT_MAXIMUM, EIGHT_BIT_MAXIMUM);        if (imageArea > 0) {            averageColor.setRgb(redTotal / imageArea, greenTotal / imageArea, blueTotal / imageArea);        }        QMetaObject::invokeMethod(texture.data(), "setImage", Q_ARG(const QImage&, image), Q_ARG(bool, false),            Q_ARG(const QColor&, averageColor), Q_ARG(int, originalWidth), Q_ARG(int, originalHeight));        return;    }    if (image.format() != QImage::Format_ARGB32) {        image = image.convertToFormat(QImage::Format_ARGB32);    }        // check for translucency/false transparency    int opaquePixels = 0;    int translucentPixels = 0;    int redTotal = 0, greenTotal = 0, blueTotal = 0, alphaTotal = 0;    for (int y = 0; y < image.height(); y++) {        for (int x = 0; x < image.width(); x++) {            QRgb rgb = image.pixel(x, y);            redTotal += qRed(rgb);            greenTotal += qGreen(rgb);            blueTotal += qBlue(rgb);            int alpha = qAlpha(rgb);            alphaTotal += alpha;            if (alpha == EIGHT_BIT_MAXIMUM) {                opaquePixels++;            } else if (alpha != 0) {                translucentPixels++;            }        }    }    if (opaquePixels == imageArea) {        qCDebug(renderutils) << "Image with alpha channel is completely opaque:" << _url;        image = image.convertToFormat(QImage::Format_RGB888);    }    QMetaObject::invokeMethod(texture.data(), "setImage", Q_ARG(const QImage&, image),        Q_ARG(bool, translucentPixels >= imageArea / 2), Q_ARG(const QColor&, QColor(redTotal / imageArea,            greenTotal / imageArea, blueTotal / imageArea, alphaTotal / imageArea)),        Q_ARG(int, originalWidth), Q_ARG(int, originalHeight));} void NetworkTexture::downloadFinished(QNetworkReply* reply) {    // send the reader off to the thread pool    QThreadPool::globalInstance()->start(new ImageReader(_self, reply));} void NetworkTexture::loadContent(const QByteArray& content) {    QThreadPool::globalInstance()->start(new ImageReader(_self, NULL, _url, content));} void NetworkTexture::setImage(const QImage& image, bool translucent, const QColor& averageColor, int originalWidth,                              int originalHeight) {    _translucent = translucent;    _averageColor = averageColor;    _originalWidth = originalWidth;    _originalHeight = originalHeight;    _width = image.width();    _height = image.height();        finishedLoading(true);    imageLoaded(image);     if ((_width > 0) && (_height > 0)) {        bool isLinearRGB = true; //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE);         gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::UINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB));

Lines 1–500 of 580.