// // GLBackendPipeline.cpp // libraries/gpu/src/gpu // // Created by Sam Gateau on 3/8/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 #include #include "GLShared.h" #include "GLPipeline.h" #include "GLShader.h" #include "GLState.h" #include "GLBuffer.h" #include "GLTexture.h" using namespace gpu; using namespace gpu::gl; void GLBackend::do_setPipeline(const Batch& batch, size_t paramOffset) { const auto& pipeline = batch._pipelines.get(batch._params[paramOffset + 0]._uint); if (compare(_pipeline._pipeline, pipeline)) { return; } // A true new Pipeline _stats._PSNumSetPipelines++; // null pipeline == reset if (!pipeline) { reset(_pipeline._pipeline); _pipeline._program = 0; _pipeline._cameraCorrection = false; _pipeline._programShader = nullptr; _pipeline._invalidProgram = true; _pipeline._state = nullptr; _pipeline._invalidState = true; } else { auto pipelineObject = GLPipeline::sync(*this, *pipeline); if (!pipelineObject) { return; } // check the program cache // pick the program version // check the program cache // pick the program version #ifdef GPU_STEREO_CAMERA_BUFFER GLuint glprogram = pipelineObject->_program->getProgram(getShaderVariant()); #else GLuint glprogram = pipelineObject->_program->getProgram(); #endif if (_pipeline._program != glprogram) { _pipeline._program = glprogram; _pipeline._programShader = pipelineObject->_program; _pipeline._invalidProgram = true; _pipeline._cameraCorrection = pipelineObject->_cameraCorrection; } // Now for the state if (_pipeline._state != pipelineObject->_state) { _pipeline._state = pipelineObject->_state; _pipeline._invalidState = true; } // Remember the new pipeline assign(_pipeline._pipeline, pipeline); } // THis should be done on Pipeline::update... if (_pipeline._invalidProgram) { glUseProgram(_pipeline._program); if (_pipeline._cameraCorrection) { // Invalidate uniform buffer cache slot _uniform._buffers[gpu::slot::buffer::CameraCorrection].reset(); auto& cameraCorrectionBuffer = _transform._viewCorrectionEnabled ? _pipeline._cameraCorrectionBuffer._buffer : _pipeline._cameraCorrectionBufferIdentity._buffer; // Because we don't sync Buffers in the bindUniformBuffer, let s force this buffer synced getBufferID(*cameraCorrectionBuffer); bindUniformBuffer(gpu::slot::buffer::CameraCorrection, cameraCorrectionBuffer, 0, sizeof(CameraCorrection)); } (void)CHECK_GL_ERROR(); _pipeline._invalidProgram = false; } } void GLBackend::updatePipeline() { if (_pipeline._invalidProgram) { // doing it here is aproblem for calls to glUniform.... so will do it on assing... glUseProgram(_pipeline._program); (void)CHECK_GL_ERROR(); _pipeline._invalidProgram = false; } if (_pipeline._invalidState) { if (_pipeline._state) { // first reset to default what should be // the fields which were not to default and are default now resetPipelineState(_pipeline._state->_signature); // Update the signature cache with what's going to be touched _pipeline._stateSignatureCache |= _pipeline._state->_signature; // And perform for (const auto& command : _pipeline._state->_commands) { command->run(this); } } else { // No state ? anyway just reset everything resetPipelineState(0); } _pipeline._invalidState = false; } } void GLBackend::resetPipelineStage() { // First reset State to default State::Signature resetSignature(0); resetPipelineState(resetSignature); _pipeline._state = nullptr; _pipeline._invalidState = false; // Second the shader side _pipeline._invalidProgram = false; _pipeline._program = 0; _pipeline._programShader = nullptr; reset(_pipeline._pipeline); glUseProgram(0); } void GLBackend::releaseUniformBuffer(uint32_t slot) { auto& bufferState = _uniform._buffers[slot]; if (valid(bufferState.buffer)) { glBindBufferBase(GL_UNIFORM_BUFFER, slot, 0); // RELEASE (void)CHECK_GL_ERROR(); } bufferState.reset(); } void GLBackend::resetUniformStage() { for (uint32_t i = 0; i < _uniform._buffers.size(); i++) { releaseUniformBuffer(i); } } void GLBackend::bindUniformBuffer(uint32_t slot, const BufferPointer& buffer, GLintptr offset, GLsizeiptr size) { if (!buffer) { releaseUniformBuffer(slot); return; } auto& currentBufferState = _uniform._buffers[slot]; // check cache before thinking if (currentBufferState.compare(buffer, offset, size)) { return; } // Grab the true gl Buffer object auto glBO = getBufferIDUnsynced(*buffer); if (glBO) { glBindBufferRange(GL_UNIFORM_BUFFER, slot, glBO, offset, size); assign(currentBufferState.buffer, buffer); currentBufferState.offset = offset; currentBufferState.size = size; (void)CHECK_GL_ERROR(); } else { releaseUniformBuffer(slot); return; } } void GLBackend::do_setUniformBuffer(const Batch& batch, size_t paramOffset) { GLuint slot = batch._params[paramOffset + 3]._uint; if (slot > (GLuint)MAX_NUM_UNIFORM_BUFFERS) { qCDebug(gpugllogging) << "GLBackend::do_setUniformBuffer: Trying to set a uniform Buffer at slot #" << slot << " which doesn't exist. MaxNumUniformBuffers = " << getMaxNumUniformBuffers(); return; } const auto& uniformBuffer = batch._buffers.get(batch._params[paramOffset + 2]._uint); GLintptr rangeStart = batch._params[paramOffset + 1]._uint; GLsizeiptr rangeSize = batch._params[paramOffset + 0]._uint; bindUniformBuffer(slot, uniformBuffer, rangeStart, rangeSize); } void GLBackend::releaseResourceTexture(uint32_t slot) { auto& textureState = _resource._textures[slot]; if (valid(textureState._texture)) { glActiveTexture(GL_TEXTURE0 + slot); glBindTexture(textureState._target, 0); // RELEASE (void)CHECK_GL_ERROR(); reset(textureState._texture); } } void GLBackend::resetResourceStage() { uint32_t i; for (i = 0; i < _resource._buffers.size(); i++) { releaseResourceBuffer(i); } for (i = 0; i < _resource._textures.size(); i++) { releaseResourceTexture(i); } } void GLBackend::do_setResourceBuffer(const Batch& batch, size_t paramOffset) { GLuint slot = batch._params[paramOffset + 1]._uint; if (slot >= (GLuint)MAX_NUM_RESOURCE_BUFFERS) { qCDebug(gpugllogging) << "GLBackend::do_setResourceBuffer: Trying to set a resource Buffer at slot #" << slot << " which doesn't exist. MaxNumResourceBuffers = " << getMaxNumResourceBuffers(); return; } const auto& resourceBuffer = batch._buffers.get(batch._params[paramOffset + 0]._uint); if (!resourceBuffer) { releaseResourceBuffer(slot); return; } // check cache before thinking if (compare(_resource._buffers[slot], resourceBuffer)) { return; } // One more True Buffer bound _stats._RSNumResourceBufferBounded++; // If successful bind then cache it if (bindResourceBuffer(slot, resourceBuffer)) { assign(_resource._buffers[slot], resourceBuffer); } else { // else clear slot and cache releaseResourceBuffer(slot); return; } } void GLBackend::do_setResourceTexture(const Batch& batch, size_t paramOffset) { GLuint slot = batch._params[paramOffset + 1]._uint; if (slot >= (GLuint)MAX_NUM_RESOURCE_TEXTURES) { qCDebug(gpugllogging) << "GLBackend::do_setResourceTexture: Trying to set a resource Texture at slot #" << slot << " which doesn't exist. MaxNumResourceTextures = " << getMaxNumResourceTextures(); return; } const auto& resourceTexture = batch._textures.get(batch._params[paramOffset + 0]._uint); bindResourceTexture(slot, resourceTexture); } void GLBackend::bindResourceTexture(uint32_t slot, const TexturePointer& resourceTexture) { if (!resourceTexture) { releaseResourceTexture(slot); return; } setResourceTexture(slot, resourceTexture); } void GLBackend::do_setResourceFramebufferSwapChainTexture(const Batch& batch, size_t paramOffset) { GLuint slot = batch._params[paramOffset + 1]._uint; if (slot >= (GLuint)MAX_NUM_RESOURCE_TEXTURES) { qCDebug(gpugllogging) << "GLBackend::do_setResourceFramebufferSwapChainTexture: Trying to set a resource Texture at slot #" << slot << " which doesn't exist. MaxNumResourceTextures = " << getMaxNumResourceTextures(); return; } auto swapChain = std::static_pointer_cast(batch._swapChains.get(batch._params[paramOffset + 0]._uint)); if (!swapChain) { releaseResourceTexture(slot); return; } auto index = batch._params[paramOffset + 2]._uint; auto renderBufferSlot = batch._params[paramOffset + 3]._uint; const auto& resourceFramebuffer = swapChain->get(index); const auto& resourceTexture = resourceFramebuffer->getRenderBuffer(renderBufferSlot); setResourceTexture(slot, resourceTexture); } void GLBackend::setResourceTexture(unsigned int slot, const TexturePointer& resourceTexture) { auto& textureState = _resource._textures[slot]; // check cache before thinking if (compare(textureState._texture, resourceTexture)) { return; } // One more True texture bound _stats._RSNumTextureBounded++; // Always make sure the GLObject is in sync GLTexture* object = syncGPUObject(resourceTexture); if (object) { assign(textureState._texture, resourceTexture); GLuint to = object->_texture; textureState._target = object->_target; glActiveTexture(GL_TEXTURE0 + slot); glBindTexture(textureState._target, to); (void)CHECK_GL_ERROR(); _stats._RSAmountTextureMemoryBounded += (int)object->size(); } else { releaseResourceTexture(slot); return; } } void GLBackend::do_setResourceTextureTable(const Batch& batch, size_t paramOffset) { const auto& textureTablePointer = batch._textureTables.get(batch._params[paramOffset]._uint); if (!textureTablePointer) { return; } const auto& textureTable = *textureTablePointer; const auto& textures = textureTable.getTextures(); for (GLuint slot = 0; slot < textures.size(); ++slot) { bindResourceTexture(slot, textures[slot]); } } int GLBackend::ResourceStageState::findEmptyTextureSlot() const { // start from the end of the slots, try to find an empty one that can be used for (auto i = MAX_NUM_RESOURCE_TEXTURES - 1; i > 0; i--) { if (!valid(_textures[i]._texture)) { return i; } } return -1; }