mirror of
https://github.com/overte-org/overte.git
synced 2025-04-14 10:47:09 +02:00
Fixed compression when convolving
This commit is contained in:
parent
cef9e454d5
commit
5f6f178438
4 changed files with 216 additions and 115 deletions
|
@ -16,6 +16,9 @@
|
||||||
|
|
||||||
#include "RandomAndNoise.h"
|
#include "RandomAndNoise.h"
|
||||||
#include "Image.h"
|
#include "Image.h"
|
||||||
|
#include "ImageLogging.h"
|
||||||
|
|
||||||
|
#include <nvtt/nvtt.h>
|
||||||
|
|
||||||
#ifndef M_PI
|
#ifndef M_PI
|
||||||
#define M_PI 3.14159265359
|
#define M_PI 3.14159265359
|
||||||
|
@ -57,11 +60,16 @@ static const glm::vec3 FACE_NORMALS[24] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CubeFaceMip {
|
struct CubeFaceMip {
|
||||||
|
|
||||||
CubeFaceMip(gpu::uint16 level, const CubeMap* cubemap) {
|
CubeFaceMip(gpu::uint16 level, const CubeMap* cubemap) {
|
||||||
_dims = cubemap->getMipDimensions(level);
|
_dims = cubemap->getMipDimensions(level);
|
||||||
_lineStride = _dims.x + 2;
|
_lineStride = _dims.x + 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CubeFaceMip(const CubeFaceMip& other) : _dims(other._dims), _lineStride(other._lineStride) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
gpu::Vec2i _dims;
|
gpu::Vec2i _dims;
|
||||||
int _lineStride;
|
int _lineStride;
|
||||||
};
|
};
|
||||||
|
@ -101,10 +109,13 @@ private:
|
||||||
class CubeMap::Mip : public CubeFaceMip {
|
class CubeMap::Mip : public CubeFaceMip {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Mip(gpu::uint16 level, CubeMap* cubemap) :
|
explicit Mip(gpu::uint16 level, CubeMap* cubemap) :
|
||||||
CubeFaceMip(level, cubemap), _faces(cubemap->_mips[level]) {
|
CubeFaceMip(level, cubemap), _faces(cubemap->_mips[level]) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Mip(const Mip& other) : CubeFaceMip(other), _faces(other._faces) {
|
||||||
|
}
|
||||||
|
|
||||||
void applySeams() {
|
void applySeams() {
|
||||||
// Copy edge rows and columns from neighbouring faces to fix seam filtering issues
|
// Copy edge rows and columns from neighbouring faces to fix seam filtering issues
|
||||||
seamColumnAndRow(gpu::Texture::CUBE_FACE_TOP_POS_Y, _dims.x, gpu::Texture::CUBE_FACE_RIGHT_POS_X, -1, -1);
|
seamColumnAndRow(gpu::Texture::CUBE_FACE_TOP_POS_Y, _dims.x, gpu::Texture::CUBE_FACE_RIGHT_POS_X, -1, -1);
|
||||||
|
@ -139,6 +150,14 @@ private:
|
||||||
|
|
||||||
Faces& _faces;
|
Faces& _faces;
|
||||||
|
|
||||||
|
inline static void copy(const glm::vec4* srcFirst, const glm::vec4* srcLast, int srcStride, glm::vec4* dstBegin, int dstStride) {
|
||||||
|
while (srcFirst <= srcLast) {
|
||||||
|
*dstBegin = *srcFirst;
|
||||||
|
srcFirst += srcStride;
|
||||||
|
dstBegin += dstStride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static std::pair<int, int> getSrcAndDst(int dim, int value) {
|
static std::pair<int, int> getSrcAndDst(int dim, int value) {
|
||||||
int src;
|
int src;
|
||||||
int dst;
|
int dst;
|
||||||
|
@ -177,14 +196,6 @@ private:
|
||||||
copyRowToRow(face1, coords1.first, face0, coords0.second, inc);
|
copyRowToRow(face1, coords1.first, face0, coords0.second, inc);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline static void copy(const glm::vec4* srcFirst, const glm::vec4* srcLast, int srcStride, glm::vec4* dstBegin, int dstStride) {
|
|
||||||
while (srcFirst <= srcLast) {
|
|
||||||
*dstBegin = *srcFirst;
|
|
||||||
srcFirst += srcStride;
|
|
||||||
dstBegin += dstStride;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void copyColumnToColumn(int srcFace, int srcCol, int dstFace, int dstCol, const int dstInc) {
|
void copyColumnToColumn(int srcFace, int srcCol, int dstFace, int dstCol, const int dstInc) {
|
||||||
const auto lastOffset = _lineStride * (_dims.y - 1);
|
const auto lastOffset = _lineStride * (_dims.y - 1);
|
||||||
auto srcFirst = _faces[srcFace].data() + srcCol + _lineStride;
|
auto srcFirst = _faces[srcFace].data() + srcCol + _lineStride;
|
||||||
|
@ -254,33 +265,83 @@ CubeMap::CubeMap(int width, int height, int mipCount) {
|
||||||
reset(width, height, mipCount);
|
reset(width, height, mipCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
CubeMap::CubeMap(gpu::Texture* texture, const std::atomic<bool>& abortProcessing) {
|
struct CubeMap::MipMapOutputHandler : public nvtt::OutputHandler {
|
||||||
reset(texture->getWidth(), texture->getHeight(), texture->getNumMips());
|
MipMapOutputHandler(CubeMap* cube) : _cubemap(cube) {}
|
||||||
|
|
||||||
|
void beginImage(int size, int width, int height, int depth, int face, int miplevel) override {
|
||||||
|
_data = _cubemap->editFace(miplevel, face);
|
||||||
|
_current = _data;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool writeData(const void* data, int size) override {
|
||||||
|
assert((size % sizeof(glm::vec4)) == 0);
|
||||||
|
memcpy(_current, data, size);
|
||||||
|
_current += size / sizeof(glm::vec4);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void endImage() override {
|
||||||
|
_data = nullptr;
|
||||||
|
_current = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
CubeMap* _cubemap{ nullptr };
|
||||||
|
glm::vec4* _data{ nullptr };
|
||||||
|
glm::vec4* _current{ nullptr };
|
||||||
|
};
|
||||||
|
|
||||||
|
CubeMap::CubeMap(const std::vector<QImage>& faces, gpu::Element srcTextureFormat, int mipCount, const std::atomic<bool>& abortProcessing) {
|
||||||
|
reset(faces.front().width(), faces.front().height(), mipCount);
|
||||||
|
|
||||||
const auto srcTextureFormat = texture->getTexelFormat();
|
|
||||||
int face;
|
int face;
|
||||||
|
|
||||||
for (gpu::uint16 mipLevel = 0; mipLevel < texture->getNumMips(); ++mipLevel) {
|
struct MipMapErrorHandler : public nvtt::ErrorHandler {
|
||||||
auto mipDims = texture->evalMipDimensions(mipLevel);
|
virtual void error(nvtt::Error e) override {
|
||||||
auto srcLineStride = (int) (sizeof(gpu::uint32)*mipDims.x);
|
qCWarning(imagelogging) << "Texture mip map creation error:" << nvtt::errorString(e);
|
||||||
auto dstLineStride = getFaceLineStride(mipLevel);
|
|
||||||
|
|
||||||
for (face = 0; face < 6; face++) {
|
|
||||||
auto sourcePixels = texture->accessStoredMipFace(mipLevel, face)->data();
|
|
||||||
auto destPixels = editFace(mipLevel, face);
|
|
||||||
|
|
||||||
convertToFloat(sourcePixels, mipDims.x, mipDims.y, srcLineStride, srcTextureFormat, destPixels, dstLineStride);
|
|
||||||
if (abortProcessing.load()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Compute mips
|
||||||
|
for (face = 0; face < 6; face++) {
|
||||||
|
auto sourcePixels = faces[face].bits();
|
||||||
|
auto floatPixels = editFace(0, face);
|
||||||
|
|
||||||
|
convertToFloat(sourcePixels, _width, _height, faces[face].bytesPerLine(), srcTextureFormat, floatPixels, _width);
|
||||||
|
|
||||||
|
nvtt::Surface surface;
|
||||||
|
surface.setImage(nvtt::InputFormat_RGBA_32F, _width, _height, 1, floatPixels);
|
||||||
|
surface.setAlphaMode(nvtt::AlphaMode_None);
|
||||||
|
surface.setWrapMode(nvtt::WrapMode_Clamp);
|
||||||
|
|
||||||
|
auto mipLevel = 0;
|
||||||
|
copyFace(_width, _height, reinterpret_cast<const glm::vec4*>(surface.data()), surface.width(), editFace(0, face), getFaceLineStride(0));
|
||||||
|
|
||||||
|
while (surface.canMakeNextMipmap() && !abortProcessing.load()) {
|
||||||
|
surface.buildNextMipmap(nvtt::MipmapFilter_Box);
|
||||||
|
mipLevel++;
|
||||||
|
|
||||||
|
copyFace(surface.width(), surface.height(), reinterpret_cast<const glm::vec4*>(surface.data()), surface.width(), editFace(mipLevel, face), getFaceLineStride(mipLevel));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (abortProcessing.load()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (gpu::uint16 mipLevel = 0; mipLevel < mipCount; ++mipLevel) {
|
||||||
Mip mip(mipLevel, this);
|
Mip mip(mipLevel, this);
|
||||||
|
|
||||||
mip.applySeams();
|
mip.applySeams();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CubeMap::copyFace(int width, int height, const glm::vec4* source, int srcLineStride, glm::vec4* dest, int dstLineStride) {
|
||||||
|
for (int y = 0; y < height; y++) {
|
||||||
|
std::copy(source, source + width, dest);
|
||||||
|
source += srcLineStride;
|
||||||
|
dest += dstLineStride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CubeMap::reset(int width, int height, int mipCount) {
|
void CubeMap::reset(int width, int height, int mipCount) {
|
||||||
assert(mipCount >0 && _width > 0 && _height > 0);
|
assert(mipCount >0 && _width > 0 && _height > 0);
|
||||||
_width = width;
|
_width = width;
|
||||||
|
@ -301,29 +362,45 @@ void CubeMap::reset(int width, int height, int mipCount) {
|
||||||
void CubeMap::copyTo(gpu::Texture* texture, const std::atomic<bool>& abortProcessing) const {
|
void CubeMap::copyTo(gpu::Texture* texture, const std::atomic<bool>& abortProcessing) const {
|
||||||
assert(_width == texture->getWidth() && _height == texture->getHeight() && texture->getNumMips() == _mips.size());
|
assert(_width == texture->getWidth() && _height == texture->getHeight() && texture->getNumMips() == _mips.size());
|
||||||
|
|
||||||
// Convert all mip data back from float
|
struct CompressionpErrorHandler : public nvtt::ErrorHandler {
|
||||||
unsigned char* convertedPixels = new unsigned char[_width * _height * sizeof(gpu::uint32)];
|
virtual void error(nvtt::Error e) override {
|
||||||
const auto textureFormat = texture->getTexelFormat();
|
qCWarning(imagelogging) << "Texture compression error:" << nvtt::errorString(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
for (gpu::uint16 mipLevel = 0; mipLevel < texture->getNumMips(); ++mipLevel) {
|
CompressionpErrorHandler errorHandler;
|
||||||
auto mipDims = texture->evalMipDimensions(mipLevel);
|
nvtt::OutputOptions outputOptions;
|
||||||
auto mipSize = texture->evalMipFaceSize(mipLevel);
|
outputOptions.setOutputHeader(false);
|
||||||
auto srcLineStride = getFaceLineStride(mipLevel);
|
outputOptions.setErrorHandler(&errorHandler);
|
||||||
auto dstLineStride = (int)(sizeof(gpu::uint32)*mipDims.x);
|
|
||||||
|
|
||||||
for (auto face = 0; face < 6; face++) {
|
nvtt::Surface surface;
|
||||||
auto srcPixels = getFace(mipLevel, face);
|
surface.setAlphaMode(nvtt::AlphaMode_None);
|
||||||
|
surface.setWrapMode(nvtt::WrapMode_Clamp);
|
||||||
|
|
||||||
convertFromFloat(convertedPixels, mipDims.x, mipDims.y, dstLineStride, textureFormat, srcPixels, srcLineStride);
|
glm::vec4* packedPixels = new glm::vec4[_width * _height];
|
||||||
texture->assignStoredMipFace(mipLevel, face, mipSize, convertedPixels);
|
for (int face = 0; face < 6; face++) {
|
||||||
if (abortProcessing.load()) {
|
nvtt::CompressionOptions compressionOptions;
|
||||||
delete[] convertedPixels;
|
std::unique_ptr<nvtt::OutputHandler> outputHandler{ getNVTTCompressionOutputHandler(texture, face, compressionOptions) };
|
||||||
return;
|
|
||||||
}
|
outputOptions.setOutputHandler(outputHandler.get());
|
||||||
|
|
||||||
|
SequentialTaskDispatcher dispatcher(abortProcessing);
|
||||||
|
nvtt::Context context;
|
||||||
|
context.setTaskDispatcher(&dispatcher);
|
||||||
|
|
||||||
|
for (gpu::uint16 mipLevel = 0; mipLevel < _mips.size() && !abortProcessing.load(); mipLevel++) {
|
||||||
|
auto mipDims = getMipDimensions(mipLevel);
|
||||||
|
|
||||||
|
copyFace(mipDims.x, mipDims.y, getFace(mipLevel, face), getFaceLineStride(mipLevel), packedPixels, mipDims.x);
|
||||||
|
surface.setImage(nvtt::InputFormat_RGBA_32F, mipDims.x, mipDims.y, 1, packedPixels);
|
||||||
|
context.compress(surface, face, mipLevel, compressionOptions, outputOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (abortProcessing.load()) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
delete[] packedPixels;
|
||||||
delete[] convertedPixels;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CubeMap::getFaceUV(const glm::vec3& dir, int* index, glm::vec2* uv) {
|
void CubeMap::getFaceUV(const glm::vec3& dir, int* index, glm::vec2* uv) {
|
||||||
|
|
|
@ -18,13 +18,15 @@
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
|
||||||
|
#include <QImage>
|
||||||
|
|
||||||
namespace image {
|
namespace image {
|
||||||
|
|
||||||
class CubeMap {
|
class CubeMap {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
CubeMap(int width, int height, int mipCount);
|
CubeMap(int width, int height, int mipCount);
|
||||||
CubeMap(gpu::Texture* texture, const std::atomic<bool>& abortProcessing = false);
|
CubeMap(const std::vector<QImage>& faces, gpu::Element faceFormat, int mipCount, const std::atomic<bool>& abortProcessing = false);
|
||||||
|
|
||||||
void reset(int width, int height, int mipCount);
|
void reset(int width, int height, int mipCount);
|
||||||
void copyTo(gpu::Texture* texture, const std::atomic<bool>& abortProcessing = false) const;
|
void copyTo(gpu::Texture* texture, const std::atomic<bool>& abortProcessing = false) const;
|
||||||
|
@ -58,6 +60,7 @@ namespace image {
|
||||||
private:
|
private:
|
||||||
|
|
||||||
struct GGXSamples;
|
struct GGXSamples;
|
||||||
|
struct MipMapOutputHandler;
|
||||||
class Mip;
|
class Mip;
|
||||||
class ConstMip;
|
class ConstMip;
|
||||||
|
|
||||||
|
@ -70,6 +73,7 @@ namespace image {
|
||||||
|
|
||||||
static void getFaceUV(const glm::vec3& dir, int* index, glm::vec2* uv);
|
static void getFaceUV(const glm::vec3& dir, int* index, glm::vec2* uv);
|
||||||
static void generateGGXSamples(GGXSamples& data, float roughness, const int resolution);
|
static void generateGGXSamples(GGXSamples& data, float roughness, const int resolution);
|
||||||
|
static void copyFace(int width, int height, const glm::vec4* source, int srcLineStride, glm::vec4* dest, int dstLineStride);
|
||||||
void convolveMipFaceForGGX(const GGXSamples& samples, CubeMap& output, gpu::uint16 mipLevel, int face, const std::atomic<bool>& abortProcessing) const;
|
void convolveMipFaceForGGX(const GGXSamples& samples, CubeMap& output, gpu::uint16 mipLevel, int face, const std::atomic<bool>& abortProcessing) const;
|
||||||
glm::vec4 computeConvolution(const glm::vec3& normal, const GGXSamples& samples) const;
|
glm::vec4 computeConvolution(const glm::vec3& normal, const GGXSamples& samples) const;
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,6 @@
|
||||||
using namespace gpu;
|
using namespace gpu;
|
||||||
|
|
||||||
#define CPU_MIPMAPS 1
|
#define CPU_MIPMAPS 1
|
||||||
#include <nvtt/nvtt.h>
|
|
||||||
|
|
||||||
#undef _CRT_SECURE_NO_WARNINGS
|
#undef _CRT_SECURE_NO_WARNINGS
|
||||||
#include <Etc2/Etc.h>
|
#include <Etc2/Etc.h>
|
||||||
|
@ -50,7 +49,7 @@ std::atomic<size_t> RECTIFIED_TEXTURE_COUNT{ 0 };
|
||||||
|
|
||||||
// we use a ref here to work around static order initialization
|
// we use a ref here to work around static order initialization
|
||||||
// possibly causing the element not to be constructed yet
|
// possibly causing the element not to be constructed yet
|
||||||
static const auto& HDR_FORMAT = gpu::Element::COLOR_R11G11B10;
|
static const auto& GPUTEXTURE_HDRFORMAT = gpu::Element::COLOR_R11G11B10;
|
||||||
const QImage::Format image::QIMAGE_HDRFORMAT = QImage::Format_RGB30;
|
const QImage::Format image::QIMAGE_HDRFORMAT = QImage::Format_RGB30;
|
||||||
|
|
||||||
uint rectifyDimension(const uint& dimension) {
|
uint rectifyDimension(const uint& dimension) {
|
||||||
|
@ -236,7 +235,7 @@ static std::function<uint32(const glm::vec3&)> getPackingFunction(const gpu::Ele
|
||||||
}
|
}
|
||||||
|
|
||||||
std::function<uint32(const glm::vec3&)> getHDRPackingFunction() {
|
std::function<uint32(const glm::vec3&)> getHDRPackingFunction() {
|
||||||
return getPackingFunction(HDR_FORMAT);
|
return getPackingFunction(GPUTEXTURE_HDRFORMAT);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::function<glm::vec3(gpu::uint32)> getUnpackingFunction(const gpu::Element& format) {
|
std::function<glm::vec3(gpu::uint32)> getUnpackingFunction(const gpu::Element& format) {
|
||||||
|
@ -254,7 +253,7 @@ std::function<glm::vec3(gpu::uint32)> getUnpackingFunction(const gpu::Element& f
|
||||||
}
|
}
|
||||||
|
|
||||||
std::function<glm::vec3(gpu::uint32)> getHDRUnpackingFunction() {
|
std::function<glm::vec3(gpu::uint32)> getHDRUnpackingFunction() {
|
||||||
return getUnpackingFunction(HDR_FORMAT);
|
return getUnpackingFunction(GPUTEXTURE_HDRFORMAT);
|
||||||
}
|
}
|
||||||
|
|
||||||
QImage processRawImageData(QIODevice& content, const std::string& filename) {
|
QImage processRawImageData(QIODevice& content, const std::string& filename) {
|
||||||
|
@ -504,22 +503,18 @@ struct MyErrorHandler : public nvtt::ErrorHandler {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class SequentialTaskDispatcher : public nvtt::TaskDispatcher {
|
SequentialTaskDispatcher::SequentialTaskDispatcher(const std::atomic<bool>& abortProcessing) : _abortProcessing(abortProcessing) {
|
||||||
public:
|
}
|
||||||
SequentialTaskDispatcher(const std::atomic<bool>& abortProcessing) : _abortProcessing(abortProcessing) {};
|
|
||||||
|
|
||||||
const std::atomic<bool>& _abortProcessing;
|
void SequentialTaskDispatcher::dispatch(nvtt::Task* task, void* context, int count) {
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
virtual void dispatch(nvtt::Task* task, void* context, int count) override {
|
if (!_abortProcessing.load()) {
|
||||||
for (int i = 0; i < count; i++) {
|
task(context, i);
|
||||||
if (!_abortProcessing.load()) {
|
} else {
|
||||||
task(context, i);
|
break;
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
void image::convertToFloat(const unsigned char* source, int width, int height, size_t srcLineByteStride, gpu::Element sourceFormat,
|
void image::convertToFloat(const unsigned char* source, int width, int height, size_t srcLineByteStride, gpu::Element sourceFormat,
|
||||||
glm::vec4* output, size_t outputLinePixelStride) {
|
glm::vec4* output, size_t outputLinePixelStride) {
|
||||||
|
@ -561,6 +556,40 @@ void image::convertFromFloat(unsigned char* output, int width, int height, size_
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nvtt::OutputHandler* getNVTTCompressionOutputHandler(gpu::Texture* outputTexture, int face, nvtt::CompressionOptions& compressionOptions) {
|
||||||
|
auto outputFormat = outputTexture->getStoredMipFormat();
|
||||||
|
|
||||||
|
nvtt::InputFormat inputFormat = nvtt::InputFormat_RGBA_32F;
|
||||||
|
nvtt::WrapMode wrapMode = nvtt::WrapMode_Mirror;
|
||||||
|
nvtt::AlphaMode alphaMode = nvtt::AlphaMode_None;
|
||||||
|
|
||||||
|
compressionOptions.setQuality(nvtt::Quality_Production);
|
||||||
|
|
||||||
|
// TODO: gles: generate ETC mips instead?
|
||||||
|
if (outputFormat == gpu::Element::COLOR_COMPRESSED_BCX_HDR_RGB) {
|
||||||
|
compressionOptions.setFormat(nvtt::Format_BC6);
|
||||||
|
} else if (outputFormat == gpu::Element::COLOR_RGB9E5) {
|
||||||
|
compressionOptions.setFormat(nvtt::Format_RGB);
|
||||||
|
compressionOptions.setPixelType(nvtt::PixelType_Float);
|
||||||
|
compressionOptions.setPixelFormat(32, 32, 32, 0);
|
||||||
|
} else if (outputFormat == gpu::Element::COLOR_R11G11B10) {
|
||||||
|
compressionOptions.setFormat(nvtt::Format_RGB);
|
||||||
|
compressionOptions.setPixelType(nvtt::PixelType_Float);
|
||||||
|
compressionOptions.setPixelFormat(32, 32, 32, 0);
|
||||||
|
} else {
|
||||||
|
qCWarning(imagelogging) << "Unknown mip format";
|
||||||
|
Q_UNREACHABLE();
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outputFormat == gpu::Element::COLOR_RGB9E5 || outputFormat == gpu::Element::COLOR_R11G11B10) {
|
||||||
|
// Don't use NVTT (at least version 2.1) as it outputs wrong RGB9E5 and R11G11B10F values from floats
|
||||||
|
return new PackedFloatOutputHandler(outputTexture, face, outputFormat);
|
||||||
|
} else {
|
||||||
|
return new OutputHandler(outputTexture, face);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void generateHDRMips(gpu::Texture* texture, QImage&& image, BackendTarget target, const std::atomic<bool>& abortProcessing, int face) {
|
void generateHDRMips(gpu::Texture* texture, QImage&& image, BackendTarget target, const std::atomic<bool>& abortProcessing, int face) {
|
||||||
// Take a local copy to force move construction
|
// Take a local copy to force move construction
|
||||||
// https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#f18-for-consume-parameters-pass-by-x-and-stdmove-the-parameter
|
// https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#f18-for-consume-parameters-pass-by-x-and-stdmove-the-parameter
|
||||||
|
@ -577,47 +606,23 @@ void generateHDRMips(gpu::Texture* texture, QImage&& image, BackendTarget target
|
||||||
nvtt::WrapMode wrapMode = nvtt::WrapMode_Mirror;
|
nvtt::WrapMode wrapMode = nvtt::WrapMode_Mirror;
|
||||||
nvtt::AlphaMode alphaMode = nvtt::AlphaMode_None;
|
nvtt::AlphaMode alphaMode = nvtt::AlphaMode_None;
|
||||||
|
|
||||||
nvtt::CompressionOptions compressionOptions;
|
|
||||||
compressionOptions.setQuality(nvtt::Quality_Production);
|
|
||||||
|
|
||||||
// TODO: gles: generate ETC mips instead?
|
|
||||||
if (mipFormat == gpu::Element::COLOR_COMPRESSED_BCX_HDR_RGB) {
|
|
||||||
compressionOptions.setFormat(nvtt::Format_BC6);
|
|
||||||
} else if (mipFormat == gpu::Element::COLOR_RGB9E5) {
|
|
||||||
compressionOptions.setFormat(nvtt::Format_RGB);
|
|
||||||
compressionOptions.setPixelType(nvtt::PixelType_Float);
|
|
||||||
compressionOptions.setPixelFormat(32, 32, 32, 0);
|
|
||||||
} else if (mipFormat == gpu::Element::COLOR_R11G11B10) {
|
|
||||||
compressionOptions.setFormat(nvtt::Format_RGB);
|
|
||||||
compressionOptions.setPixelType(nvtt::PixelType_Float);
|
|
||||||
compressionOptions.setPixelFormat(32, 32, 32, 0);
|
|
||||||
} else {
|
|
||||||
qCWarning(imagelogging) << "Unknown mip format";
|
|
||||||
Q_UNREACHABLE();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
data.resize(width * height);
|
data.resize(width * height);
|
||||||
convertToFloat(localCopy.bits(), width, height, localCopy.bytesPerLine(), HDR_FORMAT, data.data(), width);
|
convertToFloat(localCopy.bits(), width, height, localCopy.bytesPerLine(), GPUTEXTURE_HDRFORMAT, data.data(), width);
|
||||||
|
|
||||||
// We're done with the localCopy, free up the memory to avoid bloating the heap
|
// We're done with the localCopy, free up the memory to avoid bloating the heap
|
||||||
localCopy = QImage(); // QImage doesn't have a clear function, so override it with an empty one.
|
localCopy = QImage(); // QImage doesn't have a clear function, so override it with an empty one.
|
||||||
|
|
||||||
nvtt::OutputOptions outputOptions;
|
nvtt::OutputOptions outputOptions;
|
||||||
outputOptions.setOutputHeader(false);
|
outputOptions.setOutputHeader(false);
|
||||||
std::unique_ptr<nvtt::OutputHandler> outputHandler;
|
|
||||||
|
nvtt::CompressionOptions compressionOptions;
|
||||||
|
std::unique_ptr<nvtt::OutputHandler> outputHandler{ getNVTTCompressionOutputHandler(texture, face, compressionOptions) };
|
||||||
|
|
||||||
MyErrorHandler errorHandler;
|
MyErrorHandler errorHandler;
|
||||||
outputOptions.setErrorHandler(&errorHandler);
|
outputOptions.setErrorHandler(&errorHandler);
|
||||||
nvtt::Context context;
|
nvtt::Context context;
|
||||||
int mipLevel = 0;
|
int mipLevel = 0;
|
||||||
|
|
||||||
if (mipFormat == gpu::Element::COLOR_RGB9E5 || mipFormat == gpu::Element::COLOR_R11G11B10) {
|
|
||||||
// Don't use NVTT (at least version 2.1) as it outputs wrong RGB9E5 and R11G11B10F values from floats
|
|
||||||
outputHandler.reset(new PackedFloatOutputHandler(texture, face, mipFormat));
|
|
||||||
} else {
|
|
||||||
outputHandler.reset(new OutputHandler(texture, face));
|
|
||||||
}
|
|
||||||
|
|
||||||
outputOptions.setOutputHandler(outputHandler.get());
|
outputOptions.setOutputHandler(outputHandler.get());
|
||||||
|
|
||||||
nvtt::Surface surface;
|
nvtt::Surface surface;
|
||||||
|
@ -836,27 +841,27 @@ void generateLDRMips(gpu::Texture* texture, QImage&& image, BackendTarget target
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void generateMips(gpu::Texture* texture, QImage&& image, BackendTarget target, const std::atomic<bool>& abortProcessing = false, int face = -1, bool forceCPUBuild = false) {
|
void generateMips(gpu::Texture* texture, QImage&& image, BackendTarget target, const std::atomic<bool>& abortProcessing = false, int face = -1) {
|
||||||
if (forceCPUBuild || CPU_MIPMAPS) {
|
#if CPU_MIPMAPS
|
||||||
PROFILE_RANGE(resource_parse, "generateMips");
|
PROFILE_RANGE(resource_parse, "generateMips");
|
||||||
|
|
||||||
if (target == BackendTarget::GLES32) {
|
if (target == BackendTarget::GLES32) {
|
||||||
generateLDRMips(texture, std::move(image), target, abortProcessing, face);
|
generateLDRMips(texture, std::move(image), target, abortProcessing, face);
|
||||||
} else {
|
|
||||||
if (image.format() == QIMAGE_HDRFORMAT) {
|
|
||||||
generateHDRMips(texture, std::move(image), target, abortProcessing, face);
|
|
||||||
} else {
|
|
||||||
generateLDRMips(texture, std::move(image), target, abortProcessing, face);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
texture->setAutoGenerateMips(true);
|
if (image.format() == QIMAGE_HDRFORMAT) {
|
||||||
|
generateHDRMips(texture, std::move(image), target, abortProcessing, face);
|
||||||
|
} else {
|
||||||
|
generateLDRMips(texture, std::move(image), target, abortProcessing, face);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
texture->setAutoGenerateMips(true);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void convolveForGGX(gpu::Texture* texture, BackendTarget target, const std::atomic<bool>& abortProcessing = false) {
|
void convolveForGGX(const std::vector<QImage>& faces, gpu::Element faceFormat, gpu::Texture* texture, const std::atomic<bool>& abortProcessing = false) {
|
||||||
PROFILE_RANGE(resource_parse, "convolveForGGX");
|
PROFILE_RANGE(resource_parse, "convolveForGGX");
|
||||||
CubeMap source(texture, abortProcessing);
|
CubeMap source(faces, faceFormat, texture->getNumMips(), abortProcessing);
|
||||||
CubeMap output(texture->getWidth(), texture->getHeight(), texture->getNumMips());
|
CubeMap output(texture->getWidth(), texture->getHeight(), texture->getNumMips());
|
||||||
|
|
||||||
source.convolveForGGX(output, abortProcessing);
|
source.convolveForGGX(output, abortProcessing);
|
||||||
|
@ -1488,7 +1493,7 @@ gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(QImage&& srcI
|
||||||
if (targetCubemapFormat == QIMAGE_HDRFORMAT && image.format() != targetCubemapFormat) {
|
if (targetCubemapFormat == QIMAGE_HDRFORMAT && image.format() != targetCubemapFormat) {
|
||||||
// If the target format is HDR but the image isn't, we need to convert the
|
// If the target format is HDR but the image isn't, we need to convert the
|
||||||
// image to HDR.
|
// image to HDR.
|
||||||
image = convertToHDRFormat(std::move(image), HDR_FORMAT);
|
image = convertToHDRFormat(std::move(image), GPUTEXTURE_HDRFORMAT);
|
||||||
} else if (image.format() == QIMAGE_HDRFORMAT && image.format() != targetCubemapFormat) {
|
} else if (image.format() == QIMAGE_HDRFORMAT && image.format() != targetCubemapFormat) {
|
||||||
// If the target format isn't HDR (such as on GLES) but the image is, we need to
|
// If the target format isn't HDR (such as on GLES) but the image is, we need to
|
||||||
// convert the image to LDR
|
// convert the image to LDR
|
||||||
|
@ -1504,7 +1509,7 @@ gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(QImage&& srcI
|
||||||
formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_HDR_RGB;
|
formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_HDR_RGB;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
formatGPU = HDR_FORMAT;
|
formatGPU = GPUTEXTURE_HDRFORMAT;
|
||||||
}
|
}
|
||||||
|
|
||||||
formatMip = formatGPU;
|
formatMip = formatGPU;
|
||||||
|
@ -1559,7 +1564,7 @@ gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(QImage&& srcI
|
||||||
if (target == BackendTarget::GLES32) {
|
if (target == BackendTarget::GLES32) {
|
||||||
irradianceFormat = gpu::Element::COLOR_SRGBA_32;
|
irradianceFormat = gpu::Element::COLOR_SRGBA_32;
|
||||||
} else {
|
} else {
|
||||||
irradianceFormat = HDR_FORMAT;
|
irradianceFormat = GPUTEXTURE_HDRFORMAT;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto irradianceTexture = gpu::Texture::createCube(irradianceFormat, faces[0].width(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP));
|
auto irradianceTexture = gpu::Texture::createCube(irradianceFormat, faces[0].width(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP));
|
||||||
|
@ -1575,14 +1580,16 @@ gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(QImage&& srcI
|
||||||
theTexture->overrideIrradiance(irradiance);
|
theTexture->overrideIrradiance(irradiance);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint8 face = 0; face < faces.size(); ++face) {
|
if (options & CUBE_GGX_CONVOLVE) {
|
||||||
// Force building the mip maps right now on CPU if we are convolving for GGX later on
|
convolveForGGX(faces, GPUTEXTURE_HDRFORMAT, theTexture.get(), abortProcessing);
|
||||||
generateMips(theTexture.get(), std::move(faces[face]), target, abortProcessing, face, (options & CUBE_GGX_CONVOLVE) == CUBE_GGX_CONVOLVE);
|
} else {
|
||||||
|
// Create mip maps and compress to final format in one go
|
||||||
|
for (uint8 face = 0; face < faces.size(); ++face) {
|
||||||
|
// Force building the mip maps right now on CPU if we are convolving for GGX later on
|
||||||
|
generateMips(theTexture.get(), std::move(faces[face]), target, abortProcessing, face);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options & CUBE_GGX_CONVOLVE) {
|
|
||||||
convolveForGGX(theTexture.get(), target, abortProcessing);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return theTexture;
|
return theTexture;
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
|
#include <nvtt/nvtt.h>
|
||||||
|
|
||||||
#include <gpu/Texture.h>
|
#include <gpu/Texture.h>
|
||||||
|
|
||||||
|
@ -107,6 +108,18 @@ gpu::TexturePointer processImage(std::shared_ptr<QIODevice> content, const std::
|
||||||
int maxNumPixels, TextureUsage::Type textureType,
|
int maxNumPixels, TextureUsage::Type textureType,
|
||||||
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing = false);
|
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing = false);
|
||||||
|
|
||||||
|
#if defined(NVTT_API)
|
||||||
|
class SequentialTaskDispatcher : public nvtt::TaskDispatcher {
|
||||||
|
public:
|
||||||
|
SequentialTaskDispatcher(const std::atomic<bool>& abortProcessing);
|
||||||
|
|
||||||
|
const std::atomic<bool>& _abortProcessing;
|
||||||
|
|
||||||
|
void dispatch(nvtt::Task* task, void* context, int count) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
nvtt::OutputHandler* getNVTTCompressionOutputHandler(gpu::Texture* outputTexture, int face, nvtt::CompressionOptions& compressOptions);
|
||||||
|
#endif
|
||||||
} // namespace image
|
} // namespace image
|
||||||
|
|
||||||
#endif // hifi_image_Image_h
|
#endif // hifi_image_Image_h
|
||||||
|
|
Loading…
Reference in a new issue