mirror of
https://thingvellir.net/git/overte
synced 2025-03-27 23:52:03 +01:00
Merge pull request #13604 from huffman/feat/oven-etc2
Add ETC2 support to Oven
This commit is contained in:
commit
5f4f232602
9 changed files with 443 additions and 505 deletions
|
@ -138,7 +138,7 @@ void TextureBaker::processTexture() {
|
|||
// IMPORTANT: _originalTexture is empty past this point
|
||||
_originalTexture.clear();
|
||||
_outputFiles.push_back(originalCopyFilePath);
|
||||
meta.original = _metaTexturePathPrefix +_textureURL.fileName();
|
||||
meta.original = _metaTexturePathPrefix + _textureURL.fileName();
|
||||
}
|
||||
|
||||
auto buffer = std::static_pointer_cast<QIODevice>(std::make_shared<QFile>(originalCopyFilePath));
|
||||
|
@ -149,49 +149,56 @@ void TextureBaker::processTexture() {
|
|||
|
||||
// Compressed KTX
|
||||
if (_compressionEnabled) {
|
||||
auto processedTexture = image::processImage(buffer, _textureURL.toString().toStdString(),
|
||||
ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, _textureType, true, _abortProcessing);
|
||||
if (!processedTexture) {
|
||||
handleError("Could not process texture " + _textureURL.toString());
|
||||
return;
|
||||
}
|
||||
processedTexture->setSourceHash(hash);
|
||||
constexpr std::array<gpu::BackendTarget, 2> BACKEND_TARGETS {{
|
||||
gpu::BackendTarget::GL45,
|
||||
gpu::BackendTarget::GLES32
|
||||
}};
|
||||
for (auto target : BACKEND_TARGETS) {
|
||||
auto processedTexture = image::processImage(buffer, _textureURL.toString().toStdString(),
|
||||
ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, _textureType, true,
|
||||
target, _abortProcessing);
|
||||
if (!processedTexture) {
|
||||
handleError("Could not process texture " + _textureURL.toString());
|
||||
return;
|
||||
}
|
||||
processedTexture->setSourceHash(hash);
|
||||
|
||||
if (shouldStop()) {
|
||||
return;
|
||||
}
|
||||
if (shouldStop()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto memKTX = gpu::Texture::serialize(*processedTexture);
|
||||
if (!memKTX) {
|
||||
handleError("Could not serialize " + _textureURL.toString() + " to KTX");
|
||||
return;
|
||||
}
|
||||
auto memKTX = gpu::Texture::serialize(*processedTexture);
|
||||
if (!memKTX) {
|
||||
handleError("Could not serialize " + _textureURL.toString() + " to KTX");
|
||||
return;
|
||||
}
|
||||
|
||||
const char* name = khronos::gl::texture::toString(memKTX->_header.getGLInternaFormat());
|
||||
if (name == nullptr) {
|
||||
handleError("Could not determine internal format for compressed KTX: " + _textureURL.toString());
|
||||
return;
|
||||
}
|
||||
const char* name = khronos::gl::texture::toString(memKTX->_header.getGLInternaFormat());
|
||||
if (name == nullptr) {
|
||||
handleError("Could not determine internal format for compressed KTX: " + _textureURL.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
const char* data = reinterpret_cast<const char*>(memKTX->_storage->data());
|
||||
const size_t length = memKTX->_storage->size();
|
||||
const char* data = reinterpret_cast<const char*>(memKTX->_storage->data());
|
||||
const size_t length = memKTX->_storage->size();
|
||||
|
||||
auto fileName = _baseFilename + "_" + name + ".ktx";
|
||||
auto filePath = _outputDirectory.absoluteFilePath(fileName);
|
||||
QFile bakedTextureFile { filePath };
|
||||
if (!bakedTextureFile.open(QIODevice::WriteOnly) || bakedTextureFile.write(data, length) == -1) {
|
||||
handleError("Could not write baked texture for " + _textureURL.toString());
|
||||
return;
|
||||
auto fileName = _baseFilename + "_" + name + ".ktx";
|
||||
auto filePath = _outputDirectory.absoluteFilePath(fileName);
|
||||
QFile bakedTextureFile { filePath };
|
||||
if (!bakedTextureFile.open(QIODevice::WriteOnly) || bakedTextureFile.write(data, length) == -1) {
|
||||
handleError("Could not write baked texture for " + _textureURL.toString());
|
||||
return;
|
||||
}
|
||||
_outputFiles.push_back(filePath);
|
||||
meta.availableTextureTypes[memKTX->_header.getGLInternaFormat()] = _metaTexturePathPrefix + fileName;
|
||||
}
|
||||
_outputFiles.push_back(filePath);
|
||||
meta.availableTextureTypes[memKTX->_header.getGLInternaFormat()] = _metaTexturePathPrefix + fileName;
|
||||
}
|
||||
|
||||
// Uncompressed KTX
|
||||
if (_textureType == image::TextureUsage::Type::CUBE_TEXTURE) {
|
||||
buffer->reset();
|
||||
auto processedTexture = image::processImage(std::move(buffer), _textureURL.toString().toStdString(),
|
||||
ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, _textureType, false, _abortProcessing);
|
||||
ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, _textureType, false, gpu::BackendTarget::GL45, _abortProcessing);
|
||||
if (!processedTexture) {
|
||||
handleError("Could not process texture " + _textureURL.toString());
|
||||
return;
|
||||
|
|
|
@ -65,20 +65,24 @@ GLTexture* GLESBackend::syncGPUObject(const TexturePointer& texturePointer) {
|
|||
object = new GLESAttachmentTexture(shared_from_this(), texture);
|
||||
break;
|
||||
|
||||
case TextureUsageType::RESOURCE:
|
||||
// FIXME disabling variable allocation textures for now, while debugging android rendering
|
||||
// and crashes
|
||||
#if 0
|
||||
qCDebug(gpugllogging) << "variable / Strict texture " << texture.source().c_str();
|
||||
object = new GLESResourceTexture(shared_from_this(), texture);
|
||||
GLVariableAllocationSupport::addMemoryManagedTexture(texturePointer);
|
||||
break;
|
||||
#endif
|
||||
case TextureUsageType::STRICT_RESOURCE:
|
||||
qCDebug(gpugllogging) << "Strict texture " << texture.source().c_str();
|
||||
object = new GLESStrictResourceTexture(shared_from_this(), texture);
|
||||
break;
|
||||
|
||||
case TextureUsageType::RESOURCE: {
|
||||
auto &transferEngine = _textureManagement._transferEngine;
|
||||
if (transferEngine->allowCreate()) {
|
||||
object = new GLESResourceTexture(shared_from_this(), texture);
|
||||
transferEngine->addMemoryManagedTexture(texturePointer);
|
||||
} else {
|
||||
auto fallback = texturePointer->getFallbackTexture();
|
||||
if (fallback) {
|
||||
object = static_cast<GLESTexture *>(syncGPUObject(fallback));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
@ -195,7 +199,6 @@ Size GLESTexture::copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const
|
|||
glTexSubImage2D(target, mip, 0, yOffset, size.x, size.y, format, type, sourcePointer);
|
||||
}
|
||||
} else {
|
||||
// TODO: implement for android
|
||||
assert(false);
|
||||
amountCopied = 0;
|
||||
}
|
||||
|
@ -385,7 +388,6 @@ void GLESVariableAllocationTexture::allocateStorage(uint16 allocatedMip) {
|
|||
const auto totalMips = _gpuObject.getNumMips();
|
||||
const auto mips = totalMips - _allocatedMip;
|
||||
withPreservedTexture([&] {
|
||||
// FIXME technically GL 4.2, but OSX includes the ARB_texture_storage extension
|
||||
glTexStorage2D(_target, mips, texelFormat.internalFormat, dimensions.x, dimensions.y); CHECK_GL_ERROR();
|
||||
});
|
||||
auto mipLevels = _gpuObject.getNumMips();
|
||||
|
@ -426,139 +428,26 @@ void GLESVariableAllocationTexture::syncSampler() const {
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
void copyUncompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLuint srcId, GLuint destId, uint16_t numMips, uint16_t srcMipOffset, uint16_t destMipOffset, uint16_t populatedMips) {
|
||||
// DestID must be bound to the GLESBackend::RESOURCE_TRANSFER_TEX_UNIT
|
||||
|
||||
GLuint fbo { 0 };
|
||||
glGenFramebuffers(1, &fbo);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
|
||||
|
||||
uint16_t mips = numMips;
|
||||
// copy pre-existing mips
|
||||
for (uint16_t mip = populatedMips; mip < mips; ++mip) {
|
||||
void copyTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLuint srcId, GLuint destId, uint16_t numMips, uint16_t srcMipOffset, uint16_t destMipOffset, uint16_t populatedMips) {
|
||||
for (uint16_t mip = populatedMips; mip < numMips; ++mip) {
|
||||
auto mipDimensions = texture.evalMipDimensions(mip);
|
||||
uint16_t targetMip = mip - destMipOffset;
|
||||
uint16_t sourceMip = mip - srcMipOffset;
|
||||
for (GLenum target : GLTexture::getFaceTargets(texTarget)) {
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, srcId, sourceMip);
|
||||
(void)CHECK_GL_ERROR();
|
||||
glCopyTexSubImage2D(target, targetMip, 0, 0, 0, 0, mipDimensions.x, mipDimensions.y);
|
||||
auto faces = GLTexture::getFaceCount(texTarget);
|
||||
for (uint8_t face = 0; face < faces; ++face) {
|
||||
glCopyImageSubData(
|
||||
srcId, texTarget, sourceMip, 0, 0, face,
|
||||
destId, texTarget, targetMip, 0, 0, face,
|
||||
mipDimensions.x, mipDimensions.y, 1
|
||||
);
|
||||
(void)CHECK_GL_ERROR();
|
||||
}
|
||||
}
|
||||
|
||||
// destroy the transfer framebuffer
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
glDeleteFramebuffers(1, &fbo);
|
||||
}
|
||||
|
||||
void copyCompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLuint srcId, GLuint destId, uint16_t numMips, uint16_t srcMipOffset, uint16_t destMipOffset, uint16_t populatedMips) {
|
||||
// DestID must be bound to the GLESBackend::RESOURCE_TRANSFER_TEX_UNIT
|
||||
|
||||
struct MipDesc {
|
||||
GLint _faceSize;
|
||||
GLint _size;
|
||||
GLint _offset;
|
||||
GLint _width;
|
||||
GLint _height;
|
||||
};
|
||||
std::vector<MipDesc> sourceMips(numMips);
|
||||
|
||||
std::vector<GLubyte> bytes;
|
||||
|
||||
glActiveTexture(GL_TEXTURE0 + GLESBackend::RESOURCE_TRANSFER_EXTRA_TEX_UNIT);
|
||||
glBindTexture(texTarget, srcId);
|
||||
const auto& faceTargets = GLTexture::getFaceTargets(texTarget);
|
||||
GLint internalFormat { 0 };
|
||||
|
||||
// Collect the mip description from the source texture
|
||||
GLint bufferOffset { 0 };
|
||||
for (uint16_t mip = populatedMips; mip < numMips; ++mip) {
|
||||
auto& sourceMip = sourceMips[mip];
|
||||
|
||||
uint16_t sourceLevel = mip - srcMipOffset;
|
||||
|
||||
// Grab internal format once
|
||||
if (internalFormat == 0) {
|
||||
glGetTexLevelParameteriv(faceTargets[0], sourceLevel, GL_TEXTURE_INTERNAL_FORMAT, &internalFormat);
|
||||
}
|
||||
|
||||
// Collect the size of the first face, and then compute the total size offset needed for this mip level
|
||||
auto mipDimensions = texture.evalMipDimensions(mip);
|
||||
sourceMip._width = mipDimensions.x;
|
||||
sourceMip._height = mipDimensions.y;
|
||||
#ifdef DEBUG_COPY
|
||||
glGetTexLevelParameteriv(faceTargets.front(), sourceLevel, GL_TEXTURE_WIDTH, &sourceMip._width);
|
||||
glGetTexLevelParameteriv(faceTargets.front(), sourceLevel, GL_TEXTURE_HEIGHT, &sourceMip._height);
|
||||
#endif
|
||||
// TODO: retrieve the size of a compressed image
|
||||
assert(false);
|
||||
//glGetTexLevelParameteriv(faceTargets.front(), sourceLevel, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &sourceMip._faceSize);
|
||||
sourceMip._size = (GLint)faceTargets.size() * sourceMip._faceSize;
|
||||
sourceMip._offset = bufferOffset;
|
||||
bufferOffset += sourceMip._size;
|
||||
}
|
||||
(void)CHECK_GL_ERROR();
|
||||
|
||||
// Allocate the PBO to accomodate for all the mips to copy
|
||||
GLuint pbo { 0 };
|
||||
glGenBuffers(1, &pbo);
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo);
|
||||
glBufferData(GL_PIXEL_PACK_BUFFER, bufferOffset, nullptr, GL_STATIC_COPY);
|
||||
(void)CHECK_GL_ERROR();
|
||||
|
||||
// Transfer from source texture to pbo
|
||||
for (uint16_t mip = populatedMips; mip < numMips; ++mip) {
|
||||
auto& sourceMip = sourceMips[mip];
|
||||
|
||||
uint16_t sourceLevel = mip - srcMipOffset;
|
||||
|
||||
for (GLint f = 0; f < (GLint)faceTargets.size(); f++) {
|
||||
// TODO: implement for android
|
||||
//glGetCompressedTexImage(faceTargets[f], sourceLevel, BUFFER_OFFSET(sourceMip._offset + f * sourceMip._faceSize));
|
||||
}
|
||||
(void)CHECK_GL_ERROR();
|
||||
}
|
||||
|
||||
// Now populate the new texture from the pbo
|
||||
glBindTexture(texTarget, 0);
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0 + GLESBackend::RESOURCE_TRANSFER_TEX_UNIT);
|
||||
|
||||
// Transfer from pbo to new texture
|
||||
for (uint16_t mip = populatedMips; mip < numMips; ++mip) {
|
||||
auto& sourceMip = sourceMips[mip];
|
||||
|
||||
uint16_t destLevel = mip - destMipOffset;
|
||||
|
||||
for (GLint f = 0; f < (GLint)faceTargets.size(); f++) {
|
||||
#ifdef DEBUG_COPY
|
||||
GLint destWidth, destHeight, destSize;
|
||||
glGetTexLevelParameteriv(faceTargets.front(), destLevel, GL_TEXTURE_WIDTH, &destWidth);
|
||||
glGetTexLevelParameteriv(faceTargets.front(), destLevel, GL_TEXTURE_HEIGHT, &destHeight);
|
||||
glGetTexLevelParameteriv(faceTargets.front(), destLevel, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &destSize);
|
||||
#endif
|
||||
glCompressedTexSubImage2D(faceTargets[f], destLevel, 0, 0, sourceMip._width, sourceMip._height, internalFormat,
|
||||
sourceMip._faceSize, BUFFER_OFFSET(sourceMip._offset + f * sourceMip._faceSize));
|
||||
}
|
||||
}
|
||||
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
glDeleteBuffers(1, &pbo);
|
||||
}
|
||||
|
||||
void GLESVariableAllocationTexture::copyTextureMipsInGPUMem(GLuint srcId, GLuint destId, uint16_t srcMipOffset, uint16_t destMipOffset, uint16_t populatedMips) {
|
||||
uint16_t numMips = _gpuObject.getNumMips();
|
||||
withPreservedTexture([&] {
|
||||
if (_texelFormat.isCompressed()) {
|
||||
copyCompressedTexGPUMem(_gpuObject, _target, srcId, destId, numMips, srcMipOffset, destMipOffset, populatedMips);
|
||||
} else {
|
||||
copyUncompressedTexGPUMem(_gpuObject, _target, srcId, destId, numMips, srcMipOffset, destMipOffset, populatedMips);
|
||||
}
|
||||
});
|
||||
copyTexGPUMem(_gpuObject, _target, srcId, destId, numMips, srcMipOffset, destMipOffset, populatedMips);
|
||||
}
|
||||
|
||||
size_t GLESVariableAllocationTexture::promote() {
|
||||
|
|
|
@ -502,7 +502,7 @@ void Texture::setSampler(const Sampler& sampler) {
|
|||
}
|
||||
|
||||
|
||||
bool Texture::generateIrradiance() {
|
||||
bool Texture::generateIrradiance(gpu::BackendTarget target) {
|
||||
if (getType() != TEX_CUBE) {
|
||||
return false;
|
||||
}
|
||||
|
@ -513,7 +513,7 @@ bool Texture::generateIrradiance() {
|
|||
_irradiance = std::make_shared<SphericalHarmonics>();
|
||||
}
|
||||
|
||||
_irradiance->evalFromTexture(*this);
|
||||
_irradiance->evalFromTexture(*this, target);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -676,7 +676,7 @@ void sphericalHarmonicsEvaluateDirection(float * result, int order, const glm::
|
|||
result[8] = P_2_2 * ((double)dir.x * (double)dir.x - (double)dir.y * (double)dir.y);
|
||||
}
|
||||
|
||||
bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector<glm::vec3> & output, const uint order) {
|
||||
bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector<glm::vec3> & output, const uint order, gpu::BackendTarget target) {
|
||||
int width = cubeTexture.getWidth();
|
||||
if(width != cubeTexture.getHeight()) {
|
||||
return false;
|
||||
|
@ -684,22 +684,6 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector<
|
|||
|
||||
PROFILE_RANGE(render_gpu, "sphericalHarmonicsFromTexture");
|
||||
|
||||
#ifndef USE_GLES
|
||||
auto mipFormat = cubeTexture.getStoredMipFormat();
|
||||
std::function<glm::vec3(uint32)> unpackFunc;
|
||||
switch (mipFormat.getSemantic()) {
|
||||
case gpu::R11G11B10:
|
||||
unpackFunc = glm::unpackF2x11_1x10;
|
||||
break;
|
||||
case gpu::RGB9E5:
|
||||
unpackFunc = glm::unpackF3x9_E1x5;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
const uint sqOrder = order*order;
|
||||
|
||||
// allocate memory for calculations
|
||||
|
@ -733,11 +717,7 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector<
|
|||
for(int face=0; face < gpu::Texture::NUM_CUBE_FACES; face++) {
|
||||
PROFILE_RANGE(render_gpu, "ProcessFace");
|
||||
|
||||
#ifndef USE_GLES
|
||||
auto data = reinterpret_cast<const uint32*>( cubeTexture.accessStoredMipFace(0, face)->readData() );
|
||||
#else
|
||||
auto data = cubeTexture.accessStoredMipFace(0, face)->readData();
|
||||
#endif
|
||||
if (data == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
@ -819,20 +799,40 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector<
|
|||
|
||||
// get color from texture
|
||||
glm::vec3 color{ 0.0f, 0.0f, 0.0f };
|
||||
for (int i = 0; i < stride; ++i) {
|
||||
for (int j = 0; j < stride; ++j) {
|
||||
#ifndef USE_GLES
|
||||
int k = (int)(x + i - halfStride + (y + j - halfStride) * width);
|
||||
color += unpackFunc(data[k]);
|
||||
#else
|
||||
const int NUM_COMPONENTS_PER_PIXEL = 4;
|
||||
int k = NUM_COMPONENTS_PER_PIXEL * (int)(x + i - halfStride + (y + j - halfStride) * width);
|
||||
// BGRA -> RGBA
|
||||
color += glm::pow(glm::vec3(data[k + 2], data[k + 1], data[k]) / 255.0f, glm::vec3(2.2f));
|
||||
#endif
|
||||
|
||||
if (target != gpu::BackendTarget::GLES32) {
|
||||
auto mipFormat = cubeTexture.getStoredMipFormat();
|
||||
std::function<glm::vec3(uint32)> unpackFunc;
|
||||
switch (mipFormat.getSemantic()) {
|
||||
case gpu::R11G11B10:
|
||||
unpackFunc = glm::unpackF2x11_1x10;
|
||||
break;
|
||||
case gpu::RGB9E5:
|
||||
unpackFunc = glm::unpackF3x9_E1x5;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
auto data32 = reinterpret_cast<const uint32*>(data);
|
||||
for (int i = 0; i < stride; ++i) {
|
||||
for (int j = 0; j < stride; ++j) {
|
||||
int k = (int)(x + i - halfStride + (y + j - halfStride) * width);
|
||||
color += unpackFunc(data32[k]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// BGRA -> RGBA
|
||||
const int NUM_COMPONENTS_PER_PIXEL = 4;
|
||||
for (int i = 0; i < stride; ++i) {
|
||||
for (int j = 0; j < stride; ++j) {
|
||||
int k = NUM_COMPONENTS_PER_PIXEL * (int)(x + i - halfStride + (y + j - halfStride) * width);
|
||||
color += glm::pow(glm::vec3(data[k + 2], data[k + 1], data[k]) / 255.0f, glm::vec3(2.2f));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// scale color and add to previously accumulated coefficients
|
||||
// red
|
||||
sphericalHarmonicsScale(shBuffB.data(), order, shBuff.data(), color.r * fDiffSolid);
|
||||
|
@ -861,10 +861,10 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector<
|
|||
return true;
|
||||
}
|
||||
|
||||
void SphericalHarmonics::evalFromTexture(const Texture& texture) {
|
||||
void SphericalHarmonics::evalFromTexture(const Texture& texture, gpu::BackendTarget target) {
|
||||
if (texture.isDefined()) {
|
||||
std::vector< glm::vec3 > coefs;
|
||||
sphericalHarmonicsFromTexture(texture, coefs, 3);
|
||||
sphericalHarmonicsFromTexture(texture, coefs, 3, target);
|
||||
|
||||
L00 = coefs[0];
|
||||
L1m1 = coefs[1];
|
||||
|
|
|
@ -43,6 +43,11 @@ namespace khronos { namespace gl { namespace texture {
|
|||
|
||||
namespace gpu {
|
||||
|
||||
enum class BackendTarget {
|
||||
GL41,
|
||||
GL45,
|
||||
GLES32
|
||||
};
|
||||
|
||||
const std::string SOURCE_HASH_KEY { "hifi.sourceHash" };
|
||||
|
||||
|
@ -82,7 +87,7 @@ public:
|
|||
|
||||
void assignPreset(int p);
|
||||
|
||||
void evalFromTexture(const Texture& texture);
|
||||
void evalFromTexture(const Texture& texture, gpu::BackendTarget target);
|
||||
};
|
||||
typedef std::shared_ptr< SphericalHarmonics > SHPointer;
|
||||
|
||||
|
@ -541,7 +546,7 @@ public:
|
|||
Usage getUsage() const { return _usage; }
|
||||
|
||||
// For Cube Texture, it's possible to generate the irradiance spherical harmonics and make them availalbe with the texture
|
||||
bool generateIrradiance();
|
||||
bool generateIrradiance(gpu::BackendTarget target);
|
||||
const SHPointer& getIrradiance(uint16 slice = 0) const { return _irradiance; }
|
||||
void overrideIrradiance(SHPointer irradiance) { _irradiance = irradiance; }
|
||||
bool isIrradianceValid() const { return _isIrradianceValid; }
|
||||
|
|
|
@ -3,3 +3,9 @@ setup_hifi_library()
|
|||
link_hifi_libraries(shared gpu)
|
||||
target_nvtt()
|
||||
target_etc2comp()
|
||||
|
||||
if (UNIX AND NOT APPLE)
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads REQUIRED)
|
||||
target_link_libraries(image Threads::Threads)
|
||||
endif()
|
||||
|
|
|
@ -31,17 +31,13 @@ using namespace gpu;
|
|||
#define CPU_MIPMAPS 1
|
||||
#include <nvtt/nvtt.h>
|
||||
|
||||
#ifdef USE_GLES
|
||||
#undef _CRT_SECURE_NO_WARNINGS
|
||||
#include <Etc.h>
|
||||
#include <EtcFilter.h>
|
||||
#endif
|
||||
|
||||
static const glm::uvec2 SPARSE_PAGE_SIZE(128);
|
||||
#ifdef Q_OS_ANDROID
|
||||
static const glm::uvec2 MAX_TEXTURE_SIZE(2048);
|
||||
#else
|
||||
static const glm::uvec2 MAX_TEXTURE_SIZE(4096);
|
||||
#endif
|
||||
static const glm::uvec2 MAX_TEXTURE_SIZE_GLES(2048);
|
||||
static const glm::uvec2 MAX_TEXTURE_SIZE_GL(4096);
|
||||
bool DEV_DECIMATE_TEXTURES = false;
|
||||
std::atomic<size_t> DECIMATED_TEXTURE_COUNT{ 0 };
|
||||
std::atomic<size_t> RECTIFIED_TEXTURE_COUNT{ 0 };
|
||||
|
@ -83,11 +79,12 @@ const QStringList getSupportedFormats() {
|
|||
|
||||
|
||||
// On GLES, we don't use HDR skyboxes
|
||||
#ifndef USE_GLES
|
||||
QImage::Format QIMAGE_HDR_FORMAT = QImage::Format_RGB30;
|
||||
#else
|
||||
QImage::Format QIMAGE_HDR_FORMAT = QImage::Format_RGB32;
|
||||
#endif
|
||||
QImage::Format hdrFormatForTarget(BackendTarget target) {
|
||||
if (target == BackendTarget::GLES32) {
|
||||
return QImage::Format_RGB32;
|
||||
}
|
||||
return QImage::Format_RGB30;
|
||||
}
|
||||
|
||||
TextureUsage::TextureLoader TextureUsage::getTextureLoaderForType(Type type, const QVariantMap& options) {
|
||||
switch (type) {
|
||||
|
@ -123,63 +120,63 @@ TextureUsage::TextureLoader TextureUsage::getTextureLoaderForType(Type type, con
|
|||
}
|
||||
|
||||
gpu::TexturePointer TextureUsage::createStrict2DTextureFromImage(QImage&& srcImage, const std::string& srcImageName,
|
||||
bool compress, const std::atomic<bool>& abortProcessing) {
|
||||
return process2DTextureColorFromImage(std::move(srcImage), srcImageName, compress, true, abortProcessing);
|
||||
bool compress, BackendTarget target, const std::atomic<bool>& abortProcessing) {
|
||||
return process2DTextureColorFromImage(std::move(srcImage), srcImageName, compress, target, true, abortProcessing);
|
||||
}
|
||||
|
||||
gpu::TexturePointer TextureUsage::create2DTextureFromImage(QImage&& srcImage, const std::string& srcImageName,
|
||||
bool compress, const std::atomic<bool>& abortProcessing) {
|
||||
return process2DTextureColorFromImage(std::move(srcImage), srcImageName, compress, false, abortProcessing);
|
||||
bool compress, BackendTarget target, const std::atomic<bool>& abortProcessing) {
|
||||
return process2DTextureColorFromImage(std::move(srcImage), srcImageName, compress, target, false, abortProcessing);
|
||||
}
|
||||
|
||||
gpu::TexturePointer TextureUsage::createAlbedoTextureFromImage(QImage&& srcImage, const std::string& srcImageName,
|
||||
bool compress, const std::atomic<bool>& abortProcessing) {
|
||||
return process2DTextureColorFromImage(std::move(srcImage), srcImageName, compress, false, abortProcessing);
|
||||
bool compress, BackendTarget target, const std::atomic<bool>& abortProcessing) {
|
||||
return process2DTextureColorFromImage(std::move(srcImage), srcImageName, compress, target, false, abortProcessing);
|
||||
}
|
||||
|
||||
gpu::TexturePointer TextureUsage::createEmissiveTextureFromImage(QImage&& srcImage, const std::string& srcImageName,
|
||||
bool compress, const std::atomic<bool>& abortProcessing) {
|
||||
return process2DTextureColorFromImage(std::move(srcImage), srcImageName, compress, false, abortProcessing);
|
||||
bool compress, BackendTarget target, const std::atomic<bool>& abortProcessing) {
|
||||
return process2DTextureColorFromImage(std::move(srcImage), srcImageName, compress, target, false, abortProcessing);
|
||||
}
|
||||
|
||||
gpu::TexturePointer TextureUsage::createLightmapTextureFromImage(QImage&& srcImage, const std::string& srcImageName,
|
||||
bool compress, const std::atomic<bool>& abortProcessing) {
|
||||
return process2DTextureColorFromImage(std::move(srcImage), srcImageName, compress, false, abortProcessing);
|
||||
bool compress, BackendTarget target, const std::atomic<bool>& abortProcessing) {
|
||||
return process2DTextureColorFromImage(std::move(srcImage), srcImageName, compress, target, false, abortProcessing);
|
||||
}
|
||||
|
||||
gpu::TexturePointer TextureUsage::createNormalTextureFromNormalImage(QImage&& srcImage, const std::string& srcImageName,
|
||||
bool compress, const std::atomic<bool>& abortProcessing) {
|
||||
return process2DTextureNormalMapFromImage(std::move(srcImage), srcImageName, compress, false, abortProcessing);
|
||||
bool compress, BackendTarget target, const std::atomic<bool>& abortProcessing) {
|
||||
return process2DTextureNormalMapFromImage(std::move(srcImage), srcImageName, compress, target, false, abortProcessing);
|
||||
}
|
||||
|
||||
gpu::TexturePointer TextureUsage::createNormalTextureFromBumpImage(QImage&& srcImage, const std::string& srcImageName,
|
||||
bool compress, const std::atomic<bool>& abortProcessing) {
|
||||
return process2DTextureNormalMapFromImage(std::move(srcImage), srcImageName, compress, true, abortProcessing);
|
||||
bool compress, BackendTarget target, const std::atomic<bool>& abortProcessing) {
|
||||
return process2DTextureNormalMapFromImage(std::move(srcImage), srcImageName, compress, target, true, abortProcessing);
|
||||
}
|
||||
|
||||
gpu::TexturePointer TextureUsage::createRoughnessTextureFromImage(QImage&& srcImage, const std::string& srcImageName,
|
||||
bool compress, const std::atomic<bool>& abortProcessing) {
|
||||
return process2DTextureGrayscaleFromImage(std::move(srcImage), srcImageName, compress, false, abortProcessing);
|
||||
bool compress, BackendTarget target, const std::atomic<bool>& abortProcessing) {
|
||||
return process2DTextureGrayscaleFromImage(std::move(srcImage), srcImageName, compress, target, false, abortProcessing);
|
||||
}
|
||||
|
||||
gpu::TexturePointer TextureUsage::createRoughnessTextureFromGlossImage(QImage&& srcImage, const std::string& srcImageName,
|
||||
bool compress, const std::atomic<bool>& abortProcessing) {
|
||||
return process2DTextureGrayscaleFromImage(std::move(srcImage), srcImageName, compress, true, abortProcessing);
|
||||
bool compress, BackendTarget target, const std::atomic<bool>& abortProcessing) {
|
||||
return process2DTextureGrayscaleFromImage(std::move(srcImage), srcImageName, compress, target, true, abortProcessing);
|
||||
}
|
||||
|
||||
gpu::TexturePointer TextureUsage::createMetallicTextureFromImage(QImage&& srcImage, const std::string& srcImageName,
|
||||
bool compress, const std::atomic<bool>& abortProcessing) {
|
||||
return process2DTextureGrayscaleFromImage(std::move(srcImage), srcImageName, compress, false, abortProcessing);
|
||||
bool compress, BackendTarget target, const std::atomic<bool>& abortProcessing) {
|
||||
return process2DTextureGrayscaleFromImage(std::move(srcImage), srcImageName, compress, target, false, abortProcessing);
|
||||
}
|
||||
|
||||
gpu::TexturePointer TextureUsage::createCubeTextureFromImage(QImage&& srcImage, const std::string& srcImageName,
|
||||
bool compress, const std::atomic<bool>& abortProcessing) {
|
||||
return processCubeTextureColorFromImage(std::move(srcImage), srcImageName, compress, true, abortProcessing);
|
||||
bool compress, BackendTarget target, const std::atomic<bool>& abortProcessing) {
|
||||
return processCubeTextureColorFromImage(std::move(srcImage), srcImageName, compress, target, true, abortProcessing);
|
||||
}
|
||||
|
||||
gpu::TexturePointer TextureUsage::createCubeTextureFromImageWithoutIrradiance(QImage&& srcImage, const std::string& srcImageName,
|
||||
bool compress, const std::atomic<bool>& abortProcessing) {
|
||||
return processCubeTextureColorFromImage(std::move(srcImage), srcImageName, compress, false, abortProcessing);
|
||||
bool compress, BackendTarget target, const std::atomic<bool>& abortProcessing) {
|
||||
return processCubeTextureColorFromImage(std::move(srcImage), srcImageName, compress, target, false, abortProcessing);
|
||||
}
|
||||
|
||||
static float denormalize(float value, const float minValue) {
|
||||
|
@ -228,7 +225,7 @@ QImage processRawImageData(QIODevice& content, const std::string& filename) {
|
|||
|
||||
gpu::TexturePointer processImage(std::shared_ptr<QIODevice> content, const std::string& filename,
|
||||
int maxNumPixels, TextureUsage::Type textureType,
|
||||
bool compress, const std::atomic<bool>& abortProcessing) {
|
||||
bool compress, BackendTarget target, const std::atomic<bool>& abortProcessing) {
|
||||
|
||||
QImage image = processRawImageData(*content.get(), filename);
|
||||
// Texture content can take up a lot of memory. Here we release our ownership of that content
|
||||
|
@ -259,12 +256,12 @@ gpu::TexturePointer processImage(std::shared_ptr<QIODevice> content, const std::
|
|||
}
|
||||
|
||||
auto loader = TextureUsage::getTextureLoaderForType(textureType);
|
||||
auto texture = loader(std::move(image), filename, compress, abortProcessing);
|
||||
auto texture = loader(std::move(image), filename, compress, target, abortProcessing);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
QImage processSourceImage(QImage&& srcImage, bool cubemap) {
|
||||
QImage processSourceImage(QImage&& srcImage, bool cubemap, BackendTarget target) {
|
||||
PROFILE_RANGE(resource_parse, "processSourceImage");
|
||||
|
||||
// Take a local copy to force move construction
|
||||
|
@ -274,7 +271,8 @@ QImage processSourceImage(QImage&& srcImage, bool cubemap) {
|
|||
const glm::uvec2 srcImageSize = toGlm(localCopy.size());
|
||||
glm::uvec2 targetSize = srcImageSize;
|
||||
|
||||
while (glm::any(glm::greaterThan(targetSize, MAX_TEXTURE_SIZE))) {
|
||||
const auto maxTextureSize = target == BackendTarget::GLES32 ? MAX_TEXTURE_SIZE_GLES : MAX_TEXTURE_SIZE_GL;
|
||||
while (glm::any(glm::greaterThan(targetSize, maxTextureSize))) {
|
||||
targetSize /= 2;
|
||||
}
|
||||
if (targetSize != srcImageSize) {
|
||||
|
@ -406,12 +404,12 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
void generateHDRMips(gpu::Texture* texture, QImage&& image, 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
|
||||
// https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#f18-for-consume-parameters-pass-by-x-and-stdmove-the-parameter
|
||||
QImage localCopy = std::move(image);
|
||||
|
||||
assert(localCopy.format() == QIMAGE_HDR_FORMAT);
|
||||
assert(localCopy.format() == hdrFormatForTarget(target));
|
||||
|
||||
const int width = localCopy.width(), height = localCopy.height();
|
||||
std::vector<glm::vec4> data;
|
||||
|
@ -503,220 +501,219 @@ void generateHDRMips(gpu::Texture* texture, QImage&& image, const std::atomic<bo
|
|||
}
|
||||
}
|
||||
|
||||
void generateLDRMips(gpu::Texture* texture, QImage&& image, const std::atomic<bool>& abortProcessing, int face) {
|
||||
void generateLDRMips(gpu::Texture* texture, QImage&& image, BackendTarget target, const std::atomic<bool>& abortProcessing, int face) {
|
||||
// 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
|
||||
QImage localCopy = std::move(image);
|
||||
|
||||
if (localCopy.format() != QImage::Format_ARGB32 && localCopy.format() != QIMAGE_HDR_FORMAT) {
|
||||
if (localCopy.format() != QImage::Format_ARGB32 && localCopy.format() != hdrFormatForTarget(target)) {
|
||||
localCopy = localCopy.convertToFormat(QImage::Format_ARGB32);
|
||||
}
|
||||
|
||||
const int width = localCopy.width(), height = localCopy.height();
|
||||
auto mipFormat = texture->getStoredMipFormat();
|
||||
|
||||
#ifndef USE_GLES
|
||||
const void* data = static_cast<const void*>(localCopy.constBits());
|
||||
nvtt::TextureType textureType = nvtt::TextureType_2D;
|
||||
nvtt::InputFormat inputFormat = nvtt::InputFormat_BGRA_8UB;
|
||||
nvtt::WrapMode wrapMode = nvtt::WrapMode_Mirror;
|
||||
nvtt::RoundMode roundMode = nvtt::RoundMode_None;
|
||||
nvtt::AlphaMode alphaMode = nvtt::AlphaMode_None;
|
||||
if (target != BackendTarget::GLES32) {
|
||||
const void* data = static_cast<const void*>(localCopy.constBits());
|
||||
nvtt::TextureType textureType = nvtt::TextureType_2D;
|
||||
nvtt::InputFormat inputFormat = nvtt::InputFormat_BGRA_8UB;
|
||||
nvtt::WrapMode wrapMode = nvtt::WrapMode_Mirror;
|
||||
nvtt::RoundMode roundMode = nvtt::RoundMode_None;
|
||||
nvtt::AlphaMode alphaMode = nvtt::AlphaMode_None;
|
||||
|
||||
float inputGamma = 2.2f;
|
||||
float outputGamma = 2.2f;
|
||||
float inputGamma = 2.2f;
|
||||
float outputGamma = 2.2f;
|
||||
|
||||
nvtt::InputOptions inputOptions;
|
||||
inputOptions.setTextureLayout(textureType, width, height);
|
||||
nvtt::InputOptions inputOptions;
|
||||
inputOptions.setTextureLayout(textureType, width, height);
|
||||
|
||||
inputOptions.setMipmapData(data, width, height);
|
||||
// setMipmapData copies the memory, so free up the memory afterward to avoid bloating the heap
|
||||
data = nullptr;
|
||||
localCopy = QImage(); // QImage doesn't have a clear function, so override it with an empty one.
|
||||
inputOptions.setMipmapData(data, width, height);
|
||||
// setMipmapData copies the memory, so free up the memory afterward to avoid bloating the heap
|
||||
data = nullptr;
|
||||
localCopy = QImage(); // QImage doesn't have a clear function, so override it with an empty one.
|
||||
|
||||
inputOptions.setFormat(inputFormat);
|
||||
inputOptions.setGamma(inputGamma, outputGamma);
|
||||
inputOptions.setAlphaMode(alphaMode);
|
||||
inputOptions.setWrapMode(wrapMode);
|
||||
inputOptions.setRoundMode(roundMode);
|
||||
inputOptions.setFormat(inputFormat);
|
||||
inputOptions.setGamma(inputGamma, outputGamma);
|
||||
inputOptions.setAlphaMode(alphaMode);
|
||||
inputOptions.setWrapMode(wrapMode);
|
||||
inputOptions.setRoundMode(roundMode);
|
||||
|
||||
inputOptions.setMipmapGeneration(true);
|
||||
inputOptions.setMipmapFilter(nvtt::MipmapFilter_Box);
|
||||
inputOptions.setMipmapGeneration(true);
|
||||
inputOptions.setMipmapFilter(nvtt::MipmapFilter_Box);
|
||||
|
||||
nvtt::CompressionOptions compressionOptions;
|
||||
compressionOptions.setQuality(nvtt::Quality_Production);
|
||||
nvtt::CompressionOptions compressionOptions;
|
||||
compressionOptions.setQuality(nvtt::Quality_Production);
|
||||
|
||||
if (mipFormat == gpu::Element::COLOR_COMPRESSED_BCX_SRGB) {
|
||||
compressionOptions.setFormat(nvtt::Format_BC1);
|
||||
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_BCX_SRGBA_MASK) {
|
||||
alphaMode = nvtt::AlphaMode_Transparency;
|
||||
compressionOptions.setFormat(nvtt::Format_BC1a);
|
||||
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_BCX_SRGBA) {
|
||||
alphaMode = nvtt::AlphaMode_Transparency;
|
||||
compressionOptions.setFormat(nvtt::Format_BC3);
|
||||
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_BCX_RED) {
|
||||
compressionOptions.setFormat(nvtt::Format_BC4);
|
||||
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_BCX_XY) {
|
||||
compressionOptions.setFormat(nvtt::Format_BC5);
|
||||
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_BCX_SRGBA_HIGH) {
|
||||
alphaMode = nvtt::AlphaMode_Transparency;
|
||||
compressionOptions.setFormat(nvtt::Format_BC7);
|
||||
} else if (mipFormat == gpu::Element::COLOR_RGBA_32) {
|
||||
compressionOptions.setFormat(nvtt::Format_RGBA);
|
||||
compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm);
|
||||
compressionOptions.setPitchAlignment(4);
|
||||
compressionOptions.setPixelFormat(32,
|
||||
0x000000FF,
|
||||
0x0000FF00,
|
||||
0x00FF0000,
|
||||
0xFF000000);
|
||||
inputGamma = 1.0f;
|
||||
outputGamma = 1.0f;
|
||||
} else if (mipFormat == gpu::Element::COLOR_BGRA_32) {
|
||||
compressionOptions.setFormat(nvtt::Format_RGBA);
|
||||
compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm);
|
||||
compressionOptions.setPitchAlignment(4);
|
||||
compressionOptions.setPixelFormat(32,
|
||||
0x00FF0000,
|
||||
0x0000FF00,
|
||||
0x000000FF,
|
||||
0xFF000000);
|
||||
inputGamma = 1.0f;
|
||||
outputGamma = 1.0f;
|
||||
} else if (mipFormat == gpu::Element::COLOR_SRGBA_32) {
|
||||
compressionOptions.setFormat(nvtt::Format_RGBA);
|
||||
compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm);
|
||||
compressionOptions.setPitchAlignment(4);
|
||||
compressionOptions.setPixelFormat(32,
|
||||
0x000000FF,
|
||||
0x0000FF00,
|
||||
0x00FF0000,
|
||||
0xFF000000);
|
||||
} else if (mipFormat == gpu::Element::COLOR_SBGRA_32) {
|
||||
compressionOptions.setFormat(nvtt::Format_RGBA);
|
||||
compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm);
|
||||
compressionOptions.setPitchAlignment(4);
|
||||
compressionOptions.setPixelFormat(32,
|
||||
0x00FF0000,
|
||||
0x0000FF00,
|
||||
0x000000FF,
|
||||
0xFF000000);
|
||||
} else if (mipFormat == gpu::Element::COLOR_R_8) {
|
||||
compressionOptions.setFormat(nvtt::Format_RGB);
|
||||
compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm);
|
||||
compressionOptions.setPitchAlignment(4);
|
||||
compressionOptions.setPixelFormat(8, 0, 0, 0);
|
||||
} else if (mipFormat == gpu::Element::VEC2NU8_XY) {
|
||||
inputOptions.setNormalMap(true);
|
||||
compressionOptions.setFormat(nvtt::Format_RGBA);
|
||||
compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm);
|
||||
compressionOptions.setPitchAlignment(4);
|
||||
compressionOptions.setPixelFormat(8, 8, 0, 0);
|
||||
} else {
|
||||
qCWarning(imagelogging) << "Unknown mip format";
|
||||
Q_UNREACHABLE();
|
||||
return;
|
||||
}
|
||||
|
||||
nvtt::OutputOptions outputOptions;
|
||||
outputOptions.setOutputHeader(false);
|
||||
OutputHandler outputHandler(texture, face);
|
||||
outputOptions.setOutputHandler(&outputHandler);
|
||||
MyErrorHandler errorHandler;
|
||||
outputOptions.setErrorHandler(&errorHandler);
|
||||
|
||||
SequentialTaskDispatcher dispatcher(abortProcessing);
|
||||
nvtt::Compressor compressor;
|
||||
compressor.setTaskDispatcher(&dispatcher);
|
||||
compressor.process(inputOptions, compressionOptions, outputOptions);
|
||||
|
||||
#else
|
||||
int numMips = 1 + (int)log2(std::max(width, height));
|
||||
Etc::RawImage *mipMaps = new Etc::RawImage[numMips];
|
||||
Etc::Image::Format etcFormat = Etc::Image::Format::DEFAULT;
|
||||
|
||||
if (mipFormat == gpu::Element::COLOR_COMPRESSED_ETC2_RGB) {
|
||||
etcFormat = Etc::Image::Format::RGB8;
|
||||
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_ETC2_SRGB) {
|
||||
etcFormat = Etc::Image::Format::SRGB8;
|
||||
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_ETC2_RGB_PUNCHTHROUGH_ALPHA) {
|
||||
etcFormat = Etc::Image::Format::RGB8A1;
|
||||
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_ETC2_SRGB_PUNCHTHROUGH_ALPHA) {
|
||||
etcFormat = Etc::Image::Format::SRGB8A1;
|
||||
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_ETC2_RGBA) {
|
||||
etcFormat = Etc::Image::Format::RGBA8;
|
||||
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_ETC2_SRGBA) {
|
||||
etcFormat = Etc::Image::Format::SRGBA8;
|
||||
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_EAC_RED) {
|
||||
etcFormat = Etc::Image::Format::R11;
|
||||
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_EAC_RED_SIGNED) {
|
||||
etcFormat = Etc::Image::Format::SIGNED_R11;
|
||||
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_EAC_XY) {
|
||||
etcFormat = Etc::Image::Format::RG11;
|
||||
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_EAC_XY_SIGNED) {
|
||||
etcFormat = Etc::Image::Format::SIGNED_RG11;
|
||||
} else {
|
||||
qCWarning(imagelogging) << "Unknown mip format";
|
||||
Q_UNREACHABLE();
|
||||
return;
|
||||
}
|
||||
|
||||
const Etc::ErrorMetric errorMetric = Etc::ErrorMetric::RGBA;
|
||||
const float effort = 1.0f;
|
||||
const int numEncodeThreads = 4;
|
||||
int encodingTime;
|
||||
const float MAX_COLOR = 255.0f;
|
||||
|
||||
std::vector<vec4> floatData;
|
||||
floatData.resize(width * height);
|
||||
for (int y = 0; y < height; y++) {
|
||||
QRgb *line = (QRgb *) localCopy.scanLine(y);
|
||||
for (int x = 0; x < width; x++) {
|
||||
QRgb &pixel = line[x];
|
||||
floatData[x + y * width] = vec4(qRed(pixel), qGreen(pixel), qBlue(pixel), qAlpha(pixel)) / MAX_COLOR;
|
||||
if (mipFormat == gpu::Element::COLOR_COMPRESSED_BCX_SRGB) {
|
||||
compressionOptions.setFormat(nvtt::Format_BC1);
|
||||
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_BCX_SRGBA_MASK) {
|
||||
alphaMode = nvtt::AlphaMode_Transparency;
|
||||
compressionOptions.setFormat(nvtt::Format_BC1a);
|
||||
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_BCX_SRGBA) {
|
||||
alphaMode = nvtt::AlphaMode_Transparency;
|
||||
compressionOptions.setFormat(nvtt::Format_BC3);
|
||||
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_BCX_RED) {
|
||||
compressionOptions.setFormat(nvtt::Format_BC4);
|
||||
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_BCX_XY) {
|
||||
compressionOptions.setFormat(nvtt::Format_BC5);
|
||||
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_BCX_SRGBA_HIGH) {
|
||||
alphaMode = nvtt::AlphaMode_Transparency;
|
||||
compressionOptions.setFormat(nvtt::Format_BC7);
|
||||
} else if (mipFormat == gpu::Element::COLOR_RGBA_32) {
|
||||
compressionOptions.setFormat(nvtt::Format_RGBA);
|
||||
compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm);
|
||||
compressionOptions.setPitchAlignment(4);
|
||||
compressionOptions.setPixelFormat(32,
|
||||
0x000000FF,
|
||||
0x0000FF00,
|
||||
0x00FF0000,
|
||||
0xFF000000);
|
||||
inputGamma = 1.0f;
|
||||
outputGamma = 1.0f;
|
||||
} else if (mipFormat == gpu::Element::COLOR_BGRA_32) {
|
||||
compressionOptions.setFormat(nvtt::Format_RGBA);
|
||||
compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm);
|
||||
compressionOptions.setPitchAlignment(4);
|
||||
compressionOptions.setPixelFormat(32,
|
||||
0x00FF0000,
|
||||
0x0000FF00,
|
||||
0x000000FF,
|
||||
0xFF000000);
|
||||
inputGamma = 1.0f;
|
||||
outputGamma = 1.0f;
|
||||
} else if (mipFormat == gpu::Element::COLOR_SRGBA_32) {
|
||||
compressionOptions.setFormat(nvtt::Format_RGBA);
|
||||
compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm);
|
||||
compressionOptions.setPitchAlignment(4);
|
||||
compressionOptions.setPixelFormat(32,
|
||||
0x000000FF,
|
||||
0x0000FF00,
|
||||
0x00FF0000,
|
||||
0xFF000000);
|
||||
} else if (mipFormat == gpu::Element::COLOR_SBGRA_32) {
|
||||
compressionOptions.setFormat(nvtt::Format_RGBA);
|
||||
compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm);
|
||||
compressionOptions.setPitchAlignment(4);
|
||||
compressionOptions.setPixelFormat(32,
|
||||
0x00FF0000,
|
||||
0x0000FF00,
|
||||
0x000000FF,
|
||||
0xFF000000);
|
||||
} else if (mipFormat == gpu::Element::COLOR_R_8) {
|
||||
compressionOptions.setFormat(nvtt::Format_RGB);
|
||||
compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm);
|
||||
compressionOptions.setPitchAlignment(4);
|
||||
compressionOptions.setPixelFormat(8, 0, 0, 0);
|
||||
} else if (mipFormat == gpu::Element::VEC2NU8_XY) {
|
||||
inputOptions.setNormalMap(true);
|
||||
compressionOptions.setFormat(nvtt::Format_RGBA);
|
||||
compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm);
|
||||
compressionOptions.setPitchAlignment(4);
|
||||
compressionOptions.setPixelFormat(8, 8, 0, 0);
|
||||
} else {
|
||||
qCWarning(imagelogging) << "Unknown mip format";
|
||||
Q_UNREACHABLE();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// free up the memory afterward to avoid bloating the heap
|
||||
localCopy = QImage(); // QImage doesn't have a clear function, so override it with an empty one.
|
||||
nvtt::OutputOptions outputOptions;
|
||||
outputOptions.setOutputHeader(false);
|
||||
OutputHandler outputHandler(texture, face);
|
||||
outputOptions.setOutputHandler(&outputHandler);
|
||||
MyErrorHandler errorHandler;
|
||||
outputOptions.setErrorHandler(&errorHandler);
|
||||
|
||||
Etc::EncodeMipmaps(
|
||||
(float *)floatData.data(), width, height,
|
||||
etcFormat, errorMetric, effort,
|
||||
numEncodeThreads, numEncodeThreads,
|
||||
numMips, Etc::FILTER_WRAP_NONE,
|
||||
mipMaps, &encodingTime
|
||||
);
|
||||
SequentialTaskDispatcher dispatcher(abortProcessing);
|
||||
nvtt::Compressor compressor;
|
||||
compressor.setTaskDispatcher(&dispatcher);
|
||||
compressor.process(inputOptions, compressionOptions, outputOptions);
|
||||
} else {
|
||||
int numMips = 1 + (int)log2(std::max(width, height));
|
||||
Etc::RawImage *mipMaps = new Etc::RawImage[numMips];
|
||||
Etc::Image::Format etcFormat = Etc::Image::Format::DEFAULT;
|
||||
|
||||
for (int i = 0; i < numMips; i++) {
|
||||
if (mipMaps[i].paucEncodingBits.get()) {
|
||||
if (face >= 0) {
|
||||
texture->assignStoredMipFace(i, face, mipMaps[i].uiEncodingBitsBytes, static_cast<const gpu::Byte*>(mipMaps[i].paucEncodingBits.get()));
|
||||
} else {
|
||||
texture->assignStoredMip(i, mipMaps[i].uiEncodingBitsBytes, static_cast<const gpu::Byte*>(mipMaps[i].paucEncodingBits.get()));
|
||||
if (mipFormat == gpu::Element::COLOR_COMPRESSED_ETC2_RGB) {
|
||||
etcFormat = Etc::Image::Format::RGB8;
|
||||
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_ETC2_SRGB) {
|
||||
etcFormat = Etc::Image::Format::SRGB8;
|
||||
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_ETC2_RGB_PUNCHTHROUGH_ALPHA) {
|
||||
etcFormat = Etc::Image::Format::RGB8A1;
|
||||
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_ETC2_SRGB_PUNCHTHROUGH_ALPHA) {
|
||||
etcFormat = Etc::Image::Format::SRGB8A1;
|
||||
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_ETC2_RGBA) {
|
||||
etcFormat = Etc::Image::Format::RGBA8;
|
||||
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_ETC2_SRGBA) {
|
||||
etcFormat = Etc::Image::Format::SRGBA8;
|
||||
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_EAC_RED) {
|
||||
etcFormat = Etc::Image::Format::R11;
|
||||
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_EAC_RED_SIGNED) {
|
||||
etcFormat = Etc::Image::Format::SIGNED_R11;
|
||||
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_EAC_XY) {
|
||||
etcFormat = Etc::Image::Format::RG11;
|
||||
} else if (mipFormat == gpu::Element::COLOR_COMPRESSED_EAC_XY_SIGNED) {
|
||||
etcFormat = Etc::Image::Format::SIGNED_RG11;
|
||||
} else {
|
||||
qCWarning(imagelogging) << "Unknown mip format";
|
||||
Q_UNREACHABLE();
|
||||
return;
|
||||
}
|
||||
|
||||
const Etc::ErrorMetric errorMetric = Etc::ErrorMetric::RGBA;
|
||||
const float effort = 1.0f;
|
||||
const int numEncodeThreads = 4;
|
||||
int encodingTime;
|
||||
const float MAX_COLOR = 255.0f;
|
||||
|
||||
std::vector<vec4> floatData;
|
||||
floatData.resize(width * height);
|
||||
for (int y = 0; y < height; y++) {
|
||||
QRgb *line = (QRgb *)localCopy.scanLine(y);
|
||||
for (int x = 0; x < width; x++) {
|
||||
QRgb &pixel = line[x];
|
||||
floatData[x + y * width] = vec4(qRed(pixel), qGreen(pixel), qBlue(pixel), qAlpha(pixel)) / MAX_COLOR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete[] mipMaps;
|
||||
#endif
|
||||
// free up the memory afterward to avoid bloating the heap
|
||||
localCopy = QImage(); // QImage doesn't have a clear function, so override it with an empty one.
|
||||
|
||||
Etc::EncodeMipmaps(
|
||||
(float *)floatData.data(), width, height,
|
||||
etcFormat, errorMetric, effort,
|
||||
numEncodeThreads, numEncodeThreads,
|
||||
numMips, Etc::FILTER_WRAP_NONE,
|
||||
mipMaps, &encodingTime
|
||||
);
|
||||
|
||||
for (int i = 0; i < numMips; i++) {
|
||||
if (mipMaps[i].paucEncodingBits.get()) {
|
||||
if (face >= 0) {
|
||||
texture->assignStoredMipFace(i, face, mipMaps[i].uiEncodingBitsBytes, static_cast<const gpu::Byte*>(mipMaps[i].paucEncodingBits.get()));
|
||||
} else {
|
||||
texture->assignStoredMip(i, mipMaps[i].uiEncodingBitsBytes, static_cast<const gpu::Byte*>(mipMaps[i].paucEncodingBits.get()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete[] mipMaps;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void generateMips(gpu::Texture* texture, QImage&& image, const std::atomic<bool>& abortProcessing = false, int face = -1) {
|
||||
void generateMips(gpu::Texture* texture, QImage&& image, BackendTarget target, const std::atomic<bool>& abortProcessing = false, int face = -1) {
|
||||
#if CPU_MIPMAPS
|
||||
PROFILE_RANGE(resource_parse, "generateMips");
|
||||
|
||||
#ifndef USE_GLES
|
||||
if (image.format() == QIMAGE_HDR_FORMAT) {
|
||||
generateHDRMips(texture, std::move(image), abortProcessing, face);
|
||||
} else {
|
||||
generateLDRMips(texture, std::move(image), abortProcessing, face);
|
||||
if (target == BackendTarget::GLES32) {
|
||||
generateLDRMips(texture, std::move(image), target, abortProcessing, face);
|
||||
} else {
|
||||
if (image.format() == hdrFormatForTarget(target)) {
|
||||
generateHDRMips(texture, std::move(image), target, abortProcessing, face);
|
||||
} else {
|
||||
generateLDRMips(texture, std::move(image), target, abortProcessing, face);
|
||||
}
|
||||
}
|
||||
#else
|
||||
generateLDRMips(texture, std::move(image), abortProcessing, face);
|
||||
#endif
|
||||
#else
|
||||
texture->setAutoGenerateMips(true);
|
||||
#endif
|
||||
|
@ -750,9 +747,9 @@ void processTextureAlpha(const QImage& srcImage, bool& validAlpha, bool& alphaAs
|
|||
}
|
||||
|
||||
gpu::TexturePointer TextureUsage::process2DTextureColorFromImage(QImage&& srcImage, const std::string& srcImageName, bool compress,
|
||||
bool isStrict, const std::atomic<bool>& abortProcessing) {
|
||||
BackendTarget target, bool isStrict, const std::atomic<bool>& abortProcessing) {
|
||||
PROFILE_RANGE(resource_parse, "process2DTextureColorFromImage");
|
||||
QImage image = processSourceImage(std::move(srcImage), false);
|
||||
QImage image = processSourceImage(std::move(srcImage), false, target);
|
||||
|
||||
bool validAlpha = image.hasAlphaChannel();
|
||||
bool alphaAsMask = false;
|
||||
|
@ -771,23 +768,26 @@ gpu::TexturePointer TextureUsage::process2DTextureColorFromImage(QImage&& srcIma
|
|||
gpu::Element formatMip;
|
||||
gpu::Element formatGPU;
|
||||
if (compress) {
|
||||
if (validAlpha) {
|
||||
// NOTE: This disables BC1a compression because it was producing odd artifacts on text textures
|
||||
// for the tutorial. Instead we use BC3 (which is larger) but doesn't produce the same artifacts).
|
||||
formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_SRGBA;
|
||||
if (target == BackendTarget::GLES32) {
|
||||
// GLES does not support GL_BGRA
|
||||
formatGPU = gpu::Element::COLOR_COMPRESSED_ETC2_SRGBA;
|
||||
formatMip = formatGPU;
|
||||
} else {
|
||||
formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_SRGB;
|
||||
if (validAlpha) {
|
||||
// NOTE: This disables BC1a compression because it was producing odd artifacts on text textures
|
||||
// for the tutorial. Instead we use BC3 (which is larger) but doesn't produce the same artifacts).
|
||||
formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_SRGBA;
|
||||
} else {
|
||||
formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_SRGB;
|
||||
}
|
||||
formatMip = formatGPU;
|
||||
}
|
||||
formatMip = formatGPU;
|
||||
} else {
|
||||
#ifdef USE_GLES
|
||||
// GLES does not support GL_BGRA
|
||||
formatGPU = gpu::Element::COLOR_COMPRESSED_ETC2_SRGBA;
|
||||
formatMip = formatGPU;
|
||||
#else
|
||||
formatGPU = gpu::Element::COLOR_SRGBA_32;
|
||||
formatMip = gpu::Element::COLOR_SBGRA_32;
|
||||
#endif
|
||||
if (target == BackendTarget::GLES32) {
|
||||
} else {
|
||||
formatGPU = gpu::Element::COLOR_SRGBA_32;
|
||||
formatMip = gpu::Element::COLOR_SBGRA_32;
|
||||
}
|
||||
}
|
||||
|
||||
if (isStrict) {
|
||||
|
@ -806,7 +806,7 @@ gpu::TexturePointer TextureUsage::process2DTextureColorFromImage(QImage&& srcIma
|
|||
theTexture->setUsage(usage.build());
|
||||
theTexture->setStoredMipFormat(formatMip);
|
||||
theTexture->assignStoredMip(0, image.byteCount(), image.constBits());
|
||||
generateMips(theTexture.get(), std::move(image), abortProcessing);
|
||||
generateMips(theTexture.get(), std::move(image), target, abortProcessing);
|
||||
}
|
||||
|
||||
return theTexture;
|
||||
|
@ -887,10 +887,10 @@ QImage processBumpMap(QImage&& image) {
|
|||
return result;
|
||||
}
|
||||
gpu::TexturePointer TextureUsage::process2DTextureNormalMapFromImage(QImage&& srcImage, const std::string& srcImageName,
|
||||
bool compress, bool isBumpMap,
|
||||
bool compress, BackendTarget target, bool isBumpMap,
|
||||
const std::atomic<bool>& abortProcessing) {
|
||||
PROFILE_RANGE(resource_parse, "process2DTextureNormalMapFromImage");
|
||||
QImage image = processSourceImage(std::move(srcImage), false);
|
||||
QImage image = processSourceImage(std::move(srcImage), false, target);
|
||||
|
||||
if (isBumpMap) {
|
||||
image = processBumpMap(std::move(image));
|
||||
|
@ -906,13 +906,13 @@ gpu::TexturePointer TextureUsage::process2DTextureNormalMapFromImage(QImage&& sr
|
|||
gpu::Element formatMip;
|
||||
gpu::Element formatGPU;
|
||||
if (compress) {
|
||||
formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_XY;
|
||||
if (target == BackendTarget::GLES32) {
|
||||
formatGPU = gpu::Element::COLOR_COMPRESSED_EAC_XY;
|
||||
} else {
|
||||
formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_XY;
|
||||
}
|
||||
} else {
|
||||
#ifdef USE_GLES
|
||||
formatGPU = gpu::Element::COLOR_COMPRESSED_EAC_XY;
|
||||
#else
|
||||
formatGPU = gpu::Element::VEC2NU8_XY;
|
||||
#endif
|
||||
}
|
||||
formatMip = formatGPU;
|
||||
|
||||
|
@ -920,17 +920,17 @@ gpu::TexturePointer TextureUsage::process2DTextureNormalMapFromImage(QImage&& sr
|
|||
theTexture->setSource(srcImageName);
|
||||
theTexture->setStoredMipFormat(formatMip);
|
||||
theTexture->assignStoredMip(0, image.byteCount(), image.constBits());
|
||||
generateMips(theTexture.get(), std::move(image), abortProcessing);
|
||||
generateMips(theTexture.get(), std::move(image), target, abortProcessing);
|
||||
}
|
||||
|
||||
return theTexture;
|
||||
}
|
||||
|
||||
gpu::TexturePointer TextureUsage::process2DTextureGrayscaleFromImage(QImage&& srcImage, const std::string& srcImageName,
|
||||
bool compress, bool isInvertedPixels,
|
||||
bool compress, BackendTarget target, bool isInvertedPixels,
|
||||
const std::atomic<bool>& abortProcessing) {
|
||||
PROFILE_RANGE(resource_parse, "process2DTextureGrayscaleFromImage");
|
||||
QImage image = processSourceImage(std::move(srcImage), false);
|
||||
QImage image = processSourceImage(std::move(srcImage), false, target);
|
||||
|
||||
if (image.format() != QImage::Format_ARGB32) {
|
||||
image = image.convertToFormat(QImage::Format_ARGB32);
|
||||
|
@ -946,13 +946,13 @@ gpu::TexturePointer TextureUsage::process2DTextureGrayscaleFromImage(QImage&& sr
|
|||
gpu::Element formatMip;
|
||||
gpu::Element formatGPU;
|
||||
if (compress) {
|
||||
formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_RED;
|
||||
if (target == BackendTarget::GLES32) {
|
||||
formatGPU = gpu::Element::COLOR_COMPRESSED_EAC_RED;
|
||||
} else {
|
||||
formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_RED;
|
||||
}
|
||||
} else {
|
||||
#ifdef USE_GLES
|
||||
formatGPU = gpu::Element::COLOR_COMPRESSED_EAC_RED;
|
||||
#else
|
||||
formatGPU = gpu::Element::COLOR_R_8;
|
||||
#endif
|
||||
}
|
||||
formatMip = formatGPU;
|
||||
|
||||
|
@ -960,7 +960,7 @@ gpu::TexturePointer TextureUsage::process2DTextureGrayscaleFromImage(QImage&& sr
|
|||
theTexture->setSource(srcImageName);
|
||||
theTexture->setStoredMipFormat(formatMip);
|
||||
theTexture->assignStoredMip(0, image.byteCount(), image.constBits());
|
||||
generateMips(theTexture.get(), std::move(image), abortProcessing);
|
||||
generateMips(theTexture.get(), std::move(image), target, abortProcessing);
|
||||
}
|
||||
|
||||
return theTexture;
|
||||
|
@ -1233,12 +1233,12 @@ const int CubeLayout::NUM_CUBEMAP_LAYOUTS = sizeof(CubeLayout::CUBEMAP_LAYOUTS)
|
|||
|
||||
//#define DEBUG_COLOR_PACKING
|
||||
|
||||
QImage convertToHDRFormat(QImage&& srcImage, gpu::Element format) {
|
||||
QImage convertToHDRFormat(QImage&& srcImage, gpu::Element format, BackendTarget target) {
|
||||
// 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
|
||||
QImage localCopy = std::move(srcImage);
|
||||
|
||||
QImage hdrImage(localCopy.width(), localCopy.height(), (QImage::Format)QIMAGE_HDR_FORMAT);
|
||||
QImage hdrImage(localCopy.width(), localCopy.height(), hdrFormatForTarget(target));
|
||||
std::function<uint32(const glm::vec3&)> packFunc;
|
||||
#ifdef DEBUG_COLOR_PACKING
|
||||
std::function<glm::vec3(uint32)> unpackFunc;
|
||||
|
@ -1292,7 +1292,7 @@ QImage convertToHDRFormat(QImage&& srcImage, gpu::Element format) {
|
|||
}
|
||||
|
||||
gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(QImage&& srcImage, const std::string& srcImageName,
|
||||
bool compress, bool generateIrradiance,
|
||||
bool compress, BackendTarget target, bool generateIrradiance,
|
||||
const std::atomic<bool>& abortProcessing) {
|
||||
PROFILE_RANGE(resource_parse, "processCubeTextureColorFromImage");
|
||||
|
||||
|
@ -1308,27 +1308,28 @@ gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(QImage&& srcI
|
|||
|
||||
gpu::TexturePointer theTexture = nullptr;
|
||||
|
||||
QImage image = processSourceImage(std::move(localCopy), true);
|
||||
QImage image = processSourceImage(std::move(localCopy), true, target);
|
||||
|
||||
if (image.format() != QIMAGE_HDR_FORMAT) {
|
||||
#ifndef USE_GLES
|
||||
image = convertToHDRFormat(std::move(image), HDR_FORMAT);
|
||||
#else
|
||||
image = image.convertToFormat(QImage::Format_RGB32);
|
||||
#endif
|
||||
if (image.format() != hdrFormatForTarget(target)) {
|
||||
if (target == BackendTarget::GLES32) {
|
||||
image = image.convertToFormat(QImage::Format_RGB32);
|
||||
} else {
|
||||
image = convertToHDRFormat(std::move(image), HDR_FORMAT, target);
|
||||
}
|
||||
}
|
||||
|
||||
gpu::Element formatMip;
|
||||
gpu::Element formatGPU;
|
||||
if (compress) {
|
||||
formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_HDR_RGB;
|
||||
if (target == BackendTarget::GLES32) {
|
||||
formatGPU = gpu::Element::COLOR_COMPRESSED_ETC2_SRGB;
|
||||
} else {
|
||||
formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_HDR_RGB;
|
||||
}
|
||||
} else {
|
||||
#ifdef USE_GLES
|
||||
formatGPU = gpu::Element::COLOR_COMPRESSED_ETC2_SRGB;
|
||||
#else
|
||||
formatGPU = HDR_FORMAT;
|
||||
#endif
|
||||
}
|
||||
|
||||
formatMip = formatGPU;
|
||||
|
||||
// Find the layout of the cubemap in the 2D image
|
||||
|
@ -1378,11 +1379,12 @@ gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(QImage&& srcI
|
|||
PROFILE_RANGE(resource_parse, "generateIrradiance");
|
||||
gpu::Element irradianceFormat;
|
||||
// TODO: we could locally compress the irradiance texture on Android, but we don't need to
|
||||
#ifndef USE_GLES
|
||||
irradianceFormat = HDR_FORMAT;
|
||||
#else
|
||||
irradianceFormat = gpu::Element::COLOR_SRGBA_32;
|
||||
#endif
|
||||
if (target == BackendTarget::GLES32) {
|
||||
irradianceFormat = gpu::Element::COLOR_SRGBA_32;
|
||||
} else {
|
||||
irradianceFormat = HDR_FORMAT;
|
||||
}
|
||||
|
||||
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));
|
||||
irradianceTexture->setSource(srcImageName);
|
||||
irradianceTexture->setStoredMipFormat(irradianceFormat);
|
||||
|
@ -1390,14 +1392,14 @@ gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(QImage&& srcI
|
|||
irradianceTexture->assignStoredMipFace(0, face, faces[face].byteCount(), faces[face].constBits());
|
||||
}
|
||||
|
||||
irradianceTexture->generateIrradiance();
|
||||
irradianceTexture->generateIrradiance(target);
|
||||
|
||||
auto irradiance = irradianceTexture->getIrradiance();
|
||||
theTexture->overrideIrradiance(irradiance);
|
||||
}
|
||||
|
||||
for (uint8 face = 0; face < faces.size(); ++face) {
|
||||
generateMips(theTexture.get(), std::move(faces[face]), abortProcessing, face);
|
||||
generateMips(theTexture.get(), std::move(faces[face]), target, abortProcessing, face);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -41,42 +41,41 @@ enum Type {
|
|||
UNUSED_TEXTURE
|
||||
};
|
||||
|
||||
using TextureLoader = std::function<gpu::TexturePointer(QImage&&, const std::string&, bool, const std::atomic<bool>&)>;
|
||||
using TextureLoader = std::function<gpu::TexturePointer(QImage&&, const std::string&, bool, gpu::BackendTarget, const std::atomic<bool>&)>;
|
||||
TextureLoader getTextureLoaderForType(Type type, const QVariantMap& options = QVariantMap());
|
||||
|
||||
gpu::TexturePointer create2DTextureFromImage(QImage&& image, const std::string& srcImageName,
|
||||
bool compress, const std::atomic<bool>& abortProcessing);
|
||||
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing);
|
||||
gpu::TexturePointer createStrict2DTextureFromImage(QImage&& image, const std::string& srcImageName,
|
||||
bool compress, const std::atomic<bool>& abortProcessing);
|
||||
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing);
|
||||
gpu::TexturePointer createAlbedoTextureFromImage(QImage&& image, const std::string& srcImageName,
|
||||
bool compress, const std::atomic<bool>& abortProcessing);
|
||||
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing);
|
||||
gpu::TexturePointer createEmissiveTextureFromImage(QImage&& image, const std::string& srcImageName,
|
||||
bool compress, const std::atomic<bool>& abortProcessing);
|
||||
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing);
|
||||
gpu::TexturePointer createNormalTextureFromNormalImage(QImage&& image, const std::string& srcImageName,
|
||||
bool compress, const std::atomic<bool>& abortProcessing);
|
||||
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing);
|
||||
gpu::TexturePointer createNormalTextureFromBumpImage(QImage&& image, const std::string& srcImageName,
|
||||
bool compress, const std::atomic<bool>& abortProcessing);
|
||||
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing);
|
||||
gpu::TexturePointer createRoughnessTextureFromImage(QImage&& image, const std::string& srcImageName,
|
||||
bool compress, const std::atomic<bool>& abortProcessing);
|
||||
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing);
|
||||
gpu::TexturePointer createRoughnessTextureFromGlossImage(QImage&& image, const std::string& srcImageName,
|
||||
bool compress, const std::atomic<bool>& abortProcessing);
|
||||
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing);
|
||||
gpu::TexturePointer createMetallicTextureFromImage(QImage&& image, const std::string& srcImageName,
|
||||
bool compress, const std::atomic<bool>& abortProcessing);
|
||||
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing);
|
||||
gpu::TexturePointer createCubeTextureFromImage(QImage&& image, const std::string& srcImageName,
|
||||
bool compress, const std::atomic<bool>& abortProcessing);
|
||||
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing);
|
||||
gpu::TexturePointer createCubeTextureFromImageWithoutIrradiance(QImage&& image, const std::string& srcImageName,
|
||||
bool compress, const std::atomic<bool>& abortProcessing);
|
||||
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing);
|
||||
gpu::TexturePointer createLightmapTextureFromImage(QImage&& image, const std::string& srcImageName,
|
||||
bool compress, const std::atomic<bool>& abortProcessing);
|
||||
|
||||
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing);
|
||||
gpu::TexturePointer process2DTextureColorFromImage(QImage&& srcImage, const std::string& srcImageName, bool compress,
|
||||
bool isStrict, const std::atomic<bool>& abortProcessing);
|
||||
gpu::BackendTarget target, bool isStrict, const std::atomic<bool>& abortProcessing);
|
||||
gpu::TexturePointer process2DTextureNormalMapFromImage(QImage&& srcImage, const std::string& srcImageName, bool compress,
|
||||
bool isBumpMap, const std::atomic<bool>& abortProcessing);
|
||||
gpu::BackendTarget target, bool isBumpMap, const std::atomic<bool>& abortProcessing);
|
||||
gpu::TexturePointer process2DTextureGrayscaleFromImage(QImage&& srcImage, const std::string& srcImageName, bool compress,
|
||||
bool isInvertedPixels, const std::atomic<bool>& abortProcessing);
|
||||
gpu::BackendTarget target, bool isInvertedPixels, const std::atomic<bool>& abortProcessing);
|
||||
gpu::TexturePointer processCubeTextureColorFromImage(QImage&& srcImage, const std::string& srcImageName, bool compress,
|
||||
bool generateIrradiance, const std::atomic<bool>& abortProcessing);
|
||||
gpu::BackendTarget target, bool generateIrradiance, const std::atomic<bool>& abortProcessing);
|
||||
|
||||
} // namespace TextureUsage
|
||||
|
||||
|
@ -84,7 +83,7 @@ const QStringList getSupportedFormats();
|
|||
|
||||
gpu::TexturePointer processImage(std::shared_ptr<QIODevice> content, const std::string& url,
|
||||
int maxNumPixels, TextureUsage::Type textureType,
|
||||
bool compress = false, const std::atomic<bool>& abortProcessing = false);
|
||||
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing = false);
|
||||
|
||||
} // namespace image
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
set(TARGET_NAME model-networking)
|
||||
setup_hifi_library()
|
||||
link_hifi_libraries(shared networking graphics fbx ktx image)
|
||||
link_hifi_libraries(shared networking graphics fbx ktx image gl)
|
||||
include_hifi_library_headers(gpu)
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/random.hpp>
|
||||
|
||||
#include <gl/GLHelpers.h>
|
||||
#include <gpu/Batch.h>
|
||||
|
||||
#include <image/Image.h>
|
||||
|
@ -271,6 +272,20 @@ gpu::TexturePointer getFallbackTextureForType(image::TextureUsage::Type type) {
|
|||
return result;
|
||||
}
|
||||
|
||||
gpu::BackendTarget getBackendTarget() {
|
||||
#if defined(USE_GLES)
|
||||
gpu::BackendTarget target = gpu::BackendTarget::GLES32;
|
||||
#elif defined(Q_OS_MAC)
|
||||
gpu::BackendTarget target = gpu::BackendTarget::GL41;
|
||||
#else
|
||||
gpu::BackendTarget target = gpu::BackendTarget::GL45;
|
||||
if (gl::disableGl45()) {
|
||||
target = gpu::BackendTarget::GL41;
|
||||
}
|
||||
#endif
|
||||
return target;
|
||||
}
|
||||
|
||||
/// Returns a texture version of an image file
|
||||
gpu::TexturePointer TextureCache::getImageTexture(const QString& path, image::TextureUsage::Type type, QVariantMap options) {
|
||||
QImage image = QImage(path);
|
||||
|
@ -279,7 +294,15 @@ gpu::TexturePointer TextureCache::getImageTexture(const QString& path, image::Te
|
|||
return nullptr;
|
||||
}
|
||||
auto loader = image::TextureUsage::getTextureLoaderForType(type, options);
|
||||
return gpu::TexturePointer(loader(std::move(image), path.toStdString(), false, false));
|
||||
|
||||
#ifdef USE_GLES
|
||||
constexpr bool shouldCompress = true;
|
||||
#else
|
||||
constexpr bool shouldCompress = false;
|
||||
#endif
|
||||
auto target = getBackendTarget();
|
||||
|
||||
return gpu::TexturePointer(loader(std::move(image), path.toStdString(), shouldCompress, target, false));
|
||||
}
|
||||
|
||||
QSharedPointer<Resource> TextureCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback,
|
||||
|
@ -1160,7 +1183,14 @@ void ImageReader::read() {
|
|||
|
||||
// IMPORTANT: _content is empty past this point
|
||||
auto buffer = std::shared_ptr<QIODevice>((QIODevice*)new OwningBuffer(std::move(_content)));
|
||||
texture = image::processImage(std::move(buffer), _url.toString().toStdString(), _maxNumPixels, networkTexture->getTextureType());
|
||||
|
||||
#ifdef USE_GLES
|
||||
constexpr bool shouldCompress = true;
|
||||
#else
|
||||
constexpr bool shouldCompress = false;
|
||||
#endif
|
||||
auto target = getBackendTarget();
|
||||
texture = image::processImage(std::move(buffer), _url.toString().toStdString(), _maxNumPixels, networkTexture->getTextureType(), shouldCompress, target);
|
||||
|
||||
if (!texture) {
|
||||
qCWarning(modelnetworking) << "Could not process:" << _url;
|
||||
|
|
Loading…
Reference in a new issue