// // GLBackendTexture.cpp // libraries/gpu/src/gpu // // Created by Sam Gateau on 1/19/2015. // Copyright 2014 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "GLBackend.h" #include "GLTexture.h" using namespace gpu; using namespace gpu::gl; GLuint GLBackend::getTextureID(const TexturePointer& texture) { GLTexture* object = syncGPUObject(texture); if (!object) { return 0; } return object->_id; } GLTexture* GLBackend::syncGPUObject(const TexturePointer& texturePointer) { const Texture& texture = *texturePointer; // Special case external textures if (TextureUsageType::EXTERNAL == texture.getUsageType()) { Texture::ExternalUpdates updates = texture.getUpdates(); if (!updates.empty()) { Texture::ExternalRecycler recycler = texture.getExternalRecycler(); Q_ASSERT(recycler); // Discard any superfluous updates while (updates.size() > 1) { const auto& update = updates.front(); // Superfluous updates will never have been read, but we want to ensure the previous // writes to them are complete before they're written again, so return them with the // same fences they arrived with. This can happen on any thread because no GL context // work is involved recycler(update.first, update.second); updates.pop_front(); } // The last texture remaining is the one we'll use to create the GLTexture const auto& update = updates.front(); // Check for a fence, and if it exists, inject a wait into the command stream, then destroy the fence if (update.second) { GLsync fence = static_cast(update.second); glWaitSync(fence, 0, GL_TIMEOUT_IGNORED); glDeleteSync(fence); } // Create the new texture object (replaces any previous texture object) new GLExternalTexture(shared_from_this(), texture, update.first); } // Return the texture object (if any) associated with the texture, without extensive logic // (external textures are return Backend::getGPUObject(texture); } return nullptr; } void GLBackend::do_generateTextureMips(const Batch& batch, size_t paramOffset) { const auto& resourceTexture = batch._textures.get(batch._params[paramOffset + 0]._uint); if (!resourceTexture) { return; } // DO not transfer the texture, this call is expected for rendering texture GLTexture* object = syncGPUObject(resourceTexture); if (!object) { return; } object->generateMips(); } void GLBackend::do_generateTextureMipsWithPipeline(const Batch& batch, size_t paramOffset) { TexturePointer resourceTexture = batch._textures.get(batch._params[paramOffset + 0]._uint); if (!resourceTexture) { return; } // Always make sure the GLObject is in sync GLTexture* object = syncGPUObject(resourceTexture); if (object) { GLuint to = object->_texture; glActiveTexture(GL_TEXTURE0 + gpu::slot::texture::MipCreationInput); glBindTexture(object->_target, to); (void)CHECK_GL_ERROR(); } else { return; } auto numMips = batch._params[paramOffset + 1]._int; if (numMips < 0) { numMips = resourceTexture->getNumMips(); } else { numMips = std::min(numMips, (int)resourceTexture->getNumMips()); } if (_mipGenerationFramebufferId == 0) { glGenFramebuffers(1, &_mipGenerationFramebufferId); Q_ASSERT(_mipGenerationFramebufferId > 0); } glBindFramebuffer(GL_FRAMEBUFFER, _mipGenerationFramebufferId); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0); for (int level = 1; level < numMips; level++) { glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, object->_id, level); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, level - 1); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, level - 1); const auto mipDimensions = resourceTexture->evalMipDimensions(level); glViewport(0, 0, mipDimensions.x, mipDimensions.y); draw(GL_TRIANGLE_STRIP, 4, 0); } glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, numMips - 1); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0); resetOutputStage(); // Restore viewport ivec4& vp = _transform._viewport; glViewport(vp.x, vp.y, vp.z, vp.w); }