//// GLBackendInput.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 "GLShared.h"#include "GLInputFormat.h"#include "GLBuffer.h" using namespace gpu;using namespace gpu::gl; void GLBackend::do_setInputFormat(const Batch& batch, size_t paramOffset) { const auto& format = batch._streamFormats.get(batch._params[paramOffset]._uint); if (!compare(_input._format, format)) { if (format) { assign(_input._format, format); auto inputFormat = GLInputFormat::sync((*format)); assert(inputFormat); if (_input._formatKey != inputFormat->key) { _input._formatKey = inputFormat->key; _input._invalidFormat = true; } } else { reset(_input._format); _input._formatKey.clear(); _input._invalidFormat = true; } }} void GLBackend::do_setInputBuffer(const Batch& batch, size_t paramOffset) { Offset stride = batch._params[paramOffset + 0]._uint; Offset offset = batch._params[paramOffset + 1]._uint; const auto& buffer = batch._buffers.get(batch._params[paramOffset + 2]._uint); uint32 channel = batch._params[paramOffset + 3]._uint; if (channel < getNumInputBuffers()) { bool isModified = false; if (!compare(_input._buffers[channel], buffer)) { assign(_input._buffers[channel], buffer); _input._bufferVBOs[channel] = getBufferIDUnsynced((*buffer)); isModified = true; } if (_input._bufferOffsets[channel] != offset) { _input._bufferOffsets[channel] = offset; isModified = true; } if (_input._bufferStrides[channel] != stride) { _input._bufferStrides[channel] = stride; isModified = true; } if (isModified) { _input._invalidBuffers.set(channel); } }} void GLBackend::initInput() { if(!_input._defaultVAO) { glGenVertexArrays(1, &_input._defaultVAO); } glBindVertexArray(_input._defaultVAO); (void) CHECK_GL_ERROR();} void GLBackend::killInput() { glBindVertexArray(0); if(_input._defaultVAO) { glDeleteVertexArrays(1, &_input._defaultVAO); } (void) CHECK_GL_ERROR();} void GLBackend::syncInputStateCache() { for (uint32_t i = 0; i < _input._attributeActivation.size(); i++) { GLint active = 0; glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &active); _input._attributeActivation[i] = active; } //_input._defaultVAO glBindVertexArray(_input._defaultVAO);} void GLBackend::resetInputStage() { // Reset index buffer _input._indexBufferType = UINT32; _input._indexBufferOffset = 0; reset(_input._indexBuffer); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); (void) CHECK_GL_ERROR(); // Reset vertex buffer and format reset(_input._format); _input._formatKey.clear(); _input._invalidFormat = false; _input._attributeActivation.reset(); for (uint32_t i = 0; i < _input._buffers.size(); i++) { reset(_input._buffers[i]); _input._bufferOffsets[i] = 0; _input._bufferStrides[i] = 0; _input._bufferVBOs[i] = 0; } _input._invalidBuffers.reset(); // THe vertex array binding MUST be reset in the specific Backend versions as they use different techniques} void GLBackend::do_setIndexBuffer(const Batch& batch, size_t paramOffset) { _input._indexBufferType = (Type)batch._params[paramOffset + 2]._uint; _input._indexBufferOffset = batch._params[paramOffset + 0]._uint; const auto& indexBuffer = batch._buffers.get(batch._params[paramOffset + 1]._uint); if (!compare(_input._indexBuffer, indexBuffer)) { assign(_input._indexBuffer, indexBuffer); if (indexBuffer) { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, getBufferIDUnsynced(*indexBuffer)); } else { // FIXME do we really need this? Is there ever a draw call where we care that the element buffer is null? glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } } (void) CHECK_GL_ERROR();} void GLBackend::do_setIndirectBuffer(const Batch& batch, size_t paramOffset) { _input._indirectBufferOffset = batch._params[paramOffset + 1]._uint; _input._indirectBufferStride = batch._params[paramOffset + 2]._uint; const auto& buffer = batch._buffers.get(batch._params[paramOffset]._uint); if (!compare(_input._indirectBuffer, buffer)) { assign(_input._indirectBuffer, buffer); if (buffer) { glBindBuffer(GL_DRAW_INDIRECT_BUFFER, getBufferIDUnsynced(*buffer)); } else { // FIXME do we really need this? Is there ever a draw call where we care that the element buffer is null? glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0); } } (void)CHECK_GL_ERROR();} void GLBackend::updateInput() { bool isStereoNow = isStereo(); // track stereo state change potentially happening without changing the input format // this is a rare case requesting to invalid the format#ifdef GPU_STEREO_DRAWCALL_INSTANCED _input._invalidFormat |= (isStereoNow != _input._lastUpdateStereoState);#endif _input._lastUpdateStereoState = isStereoNow; if (_input._invalidFormat) { InputStageState::ActivationCache newActivation; // Assign the vertex format required auto format = acquire(_input._format); if (format) { _input._attribBindingBuffers.reset(); const auto& attributes = format->getAttributes(); const auto& inputChannels = format->getChannels(); for (auto& channelIt : inputChannels) { auto bufferChannelNum = (channelIt).first; const Stream::Format::ChannelMap::value_type::second_type& channel = (channelIt).second; _input._attribBindingBuffers.set(bufferChannelNum); GLuint frequency = 0; for (unsigned int i = 0; i < channel._slots.size(); i++) { const Stream::Attribute& attrib = attributes.at(channel._slots[i]); GLuint slot = attrib._slot; GLuint count = attrib._element.getLocationScalarCount(); uint8_t locationCount = attrib._element.getLocationCount(); GLenum type = gl::ELEMENT_TYPE_TO_GL[attrib._element.getType()]; GLuint offset = (GLuint)attrib._offset; GLboolean isNormalized = attrib._element.isNormalized(); GLenum perLocationSize = attrib._element.getLocationSize(); for (GLuint locNum = 0; locNum < locationCount; ++locNum) { GLuint attriNum = (GLuint)(slot + locNum); newActivation.set(attriNum); if (!_input._attributeActivation[attriNum]) { _input._attributeActivation.set(attriNum); glEnableVertexAttribArray(attriNum); } if (attrib._element.isInteger()) { glVertexAttribIFormat(attriNum, count, type, offset + locNum * perLocationSize); } else { glVertexAttribFormat(attriNum, count, type, isNormalized, offset + locNum * perLocationSize); } glVertexAttribBinding(attriNum, attrib._channel); } if (i == 0) { frequency = attrib._frequency; } else { assert(frequency == attrib._frequency); } (void)CHECK_GL_ERROR(); }#ifdef GPU_STEREO_DRAWCALL_INSTANCED glVertexBindingDivisor(bufferChannelNum, frequency * (isStereoNow ? 2 : 1));#else glVertexBindingDivisor(bufferChannelNum, frequency);#endif } } // Manage Activation what was and what is expected now // This should only disable VertexAttribs since the one needed by the vertex format (if it exists) have been enabled above for (GLuint i = 0; i < (GLuint)newActivation.size(); i++) { bool newState = newActivation[i]; if (newState != _input._attributeActivation[i]) { if (newState) { glEnableVertexAttribArray(i); } else { glDisableVertexAttribArray(i); } _input._attributeActivation.flip(i); } } (void)CHECK_GL_ERROR(); _input._invalidFormat = false; _stats._ISNumFormatChanges++; } if (_input._invalidBuffers.any()) { auto vbo = _input._bufferVBOs.data(); auto offset = _input._bufferOffsets.data(); auto stride = _input._bufferStrides.data(); // Profile the count of buffers to update and use it to short cut the for loop int numInvalids = (int) _input._invalidBuffers.count(); _stats._ISNumInputBufferChanges += numInvalids; for (GLuint buffer = 0; buffer < _input._buffers.size(); buffer++, vbo++, offset++, stride++) { if (_input._invalidBuffers.test(buffer)) { glBindVertexBuffer(buffer, (*vbo), (*offset), (GLsizei)(*stride)); numInvalids--; if (numInvalids <= 0) { break; } } } _input._invalidBuffers.reset(); (void)CHECK_GL_ERROR(); }}
//
// GLBackendInput.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 "GLShared.h"
#include "GLInputFormat.h"
#include "GLBuffer.h"
using namespace gpu;
using namespace gpu::gl;
void GLBackend::do_setInputFormat(const Batch& batch, size_t paramOffset) {
const auto& format = batch._streamFormats.get(batch._params[paramOffset]._uint);
if (!compare(_input._format, format)) {
if (format) {
assign(_input._format, format);
auto inputFormat = GLInputFormat::sync((*format));
assert(inputFormat);
if (_input._formatKey != inputFormat->key) {
_input._formatKey = inputFormat->key;
_input._invalidFormat = true;
}
} else {
reset(_input._format);
_input._formatKey.clear();
_input._invalidFormat = true;
}
}
}
void GLBackend::do_setInputBuffer(const Batch& batch, size_t paramOffset) {
Offset stride = batch._params[paramOffset + 0]._uint;
Offset offset = batch._params[paramOffset + 1]._uint;
const auto& buffer = batch._buffers.get(batch._params[paramOffset + 2]._uint);
uint32 channel = batch._params[paramOffset + 3]._uint;
if (channel < getNumInputBuffers()) {
bool isModified = false;
if (!compare(_input._buffers[channel], buffer)) {
assign(_input._buffers[channel], buffer);
_input._bufferVBOs[channel] = getBufferIDUnsynced((*buffer));
isModified = true;
}
if (_input._bufferOffsets[channel] != offset) {
_input._bufferOffsets[channel] = offset;
isModified = true;
}
if (_input._bufferStrides[channel] != stride) {
_input._bufferStrides[channel] = stride;
isModified = true;
}
if (isModified) {
_input._invalidBuffers.set(channel);
}
}
}
void GLBackend::initInput() {
if(!_input._defaultVAO) {
glGenVertexArrays(1, &_input._defaultVAO);
}
glBindVertexArray(_input._defaultVAO);
(void) CHECK_GL_ERROR();
}
void GLBackend::killInput() {
glBindVertexArray(0);
if(_input._defaultVAO) {
glDeleteVertexArrays(1, &_input._defaultVAO);
}
(void) CHECK_GL_ERROR();
}
void GLBackend::syncInputStateCache() {
for (uint32_t i = 0; i < _input._attributeActivation.size(); i++) {
GLint active = 0;
glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &active);
_input._attributeActivation[i] = active;
}
//_input._defaultVAO
glBindVertexArray(_input._defaultVAO);
}
void GLBackend::resetInputStage() {
// Reset index buffer
_input._indexBufferType = UINT32;
_input._indexBufferOffset = 0;
reset(_input._indexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
(void) CHECK_GL_ERROR();
// Reset vertex buffer and format
reset(_input._format);
_input._formatKey.clear();
_input._invalidFormat = false;
_input._attributeActivation.reset();
for (uint32_t i = 0; i < _input._buffers.size(); i++) {
reset(_input._buffers[i]);
_input._bufferOffsets[i] = 0;
_input._bufferStrides[i] = 0;
_input._bufferVBOs[i] = 0;
}
_input._invalidBuffers.reset();
// THe vertex array binding MUST be reset in the specific Backend versions as they use different techniques
}
void GLBackend::do_setIndexBuffer(const Batch& batch, size_t paramOffset) {
_input._indexBufferType = (Type)batch._params[paramOffset + 2]._uint;
_input._indexBufferOffset = batch._params[paramOffset + 0]._uint;
const auto& indexBuffer = batch._buffers.get(batch._params[paramOffset + 1]._uint);
if (!compare(_input._indexBuffer, indexBuffer)) {
assign(_input._indexBuffer, indexBuffer);
if (indexBuffer) {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, getBufferIDUnsynced(*indexBuffer));
} else {
// FIXME do we really need this? Is there ever a draw call where we care that the element buffer is null?
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
}
(void) CHECK_GL_ERROR();
}
void GLBackend::do_setIndirectBuffer(const Batch& batch, size_t paramOffset) {
_input._indirectBufferOffset = batch._params[paramOffset + 1]._uint;
_input._indirectBufferStride = batch._params[paramOffset + 2]._uint;
const auto& buffer = batch._buffers.get(batch._params[paramOffset]._uint);
if (!compare(_input._indirectBuffer, buffer)) {
assign(_input._indirectBuffer, buffer);
if (buffer) {
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, getBufferIDUnsynced(*buffer));
} else {
// FIXME do we really need this? Is there ever a draw call where we care that the element buffer is null?
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0);
}
}
(void)CHECK_GL_ERROR();
}
void GLBackend::updateInput() {
bool isStereoNow = isStereo();
// track stereo state change potentially happening without changing the input format
// this is a rare case requesting to invalid the format
#ifdef GPU_STEREO_DRAWCALL_INSTANCED
_input._invalidFormat |= (isStereoNow != _input._lastUpdateStereoState);
#endif
_input._lastUpdateStereoState = isStereoNow;
if (_input._invalidFormat) {
InputStageState::ActivationCache newActivation;
// Assign the vertex format required
auto format = acquire(_input._format);
if (format) {
_input._attribBindingBuffers.reset();
const auto& attributes = format->getAttributes();
const auto& inputChannels = format->getChannels();
for (auto& channelIt : inputChannels) {
auto bufferChannelNum = (channelIt).first;
const Stream::Format::ChannelMap::value_type::second_type& channel = (channelIt).second;
_input._attribBindingBuffers.set(bufferChannelNum);
GLuint frequency = 0;
for (unsigned int i = 0; i < channel._slots.size(); i++) {
const Stream::Attribute& attrib = attributes.at(channel._slots[i]);
GLuint slot = attrib._slot;
GLuint count = attrib._element.getLocationScalarCount();
uint8_t locationCount = attrib._element.getLocationCount();
GLenum type = gl::ELEMENT_TYPE_TO_GL[attrib._element.getType()];
GLuint offset = (GLuint)attrib._offset;
GLboolean isNormalized = attrib._element.isNormalized();
GLenum perLocationSize = attrib._element.getLocationSize();
for (GLuint locNum = 0; locNum < locationCount; ++locNum) {
GLuint attriNum = (GLuint)(slot + locNum);
newActivation.set(attriNum);
if (!_input._attributeActivation[attriNum]) {
_input._attributeActivation.set(attriNum);
glEnableVertexAttribArray(attriNum);
}
if (attrib._element.isInteger()) {
glVertexAttribIFormat(attriNum, count, type, offset + locNum * perLocationSize);
} else {
glVertexAttribFormat(attriNum, count, type, isNormalized, offset + locNum * perLocationSize);
}
glVertexAttribBinding(attriNum, attrib._channel);
}
if (i == 0) {
frequency = attrib._frequency;
} else {
assert(frequency == attrib._frequency);
}
(void)CHECK_GL_ERROR();
}
#ifdef GPU_STEREO_DRAWCALL_INSTANCED
glVertexBindingDivisor(bufferChannelNum, frequency * (isStereoNow ? 2 : 1));
#else
glVertexBindingDivisor(bufferChannelNum, frequency);
#endif
}
}
// Manage Activation what was and what is expected now
// This should only disable VertexAttribs since the one needed by the vertex format (if it exists) have been enabled above
for (GLuint i = 0; i < (GLuint)newActivation.size(); i++) {
bool newState = newActivation[i];
if (newState != _input._attributeActivation[i]) {
if (newState) {
glEnableVertexAttribArray(i);
} else {
glDisableVertexAttribArray(i);
}
_input._attributeActivation.flip(i);
}
}
(void)CHECK_GL_ERROR();
_input._invalidFormat = false;
_stats._ISNumFormatChanges++;
}
if (_input._invalidBuffers.any()) {
auto vbo = _input._bufferVBOs.data();
auto offset = _input._bufferOffsets.data();
auto stride = _input._bufferStrides.data();
// Profile the count of buffers to update and use it to short cut the for loop
int numInvalids = (int) _input._invalidBuffers.count();
_stats._ISNumInputBufferChanges += numInvalids;
for (GLuint buffer = 0; buffer < _input._buffers.size(); buffer++, vbo++, offset++, stride++) {
if (_input._invalidBuffers.test(buffer)) {
glBindVertexBuffer(buffer, (*vbo), (*offset), (GLsizei)(*stride));
numInvalids--;
if (numInvalids <= 0) {
break;
}
}
}
_input._invalidBuffers.reset();
(void)CHECK_GL_ERROR();
}
}