mirror of
https://github.com/lubosz/overte.git
synced 2025-04-07 09:42:06 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into hdr
This commit is contained in:
commit
92a9037b07
6 changed files with 849 additions and 222 deletions
|
@ -24,33 +24,34 @@
|
|||
#include "AudioRingBuffer.h"
|
||||
|
||||
static const QString RING_BUFFER_OVERFLOW_DEBUG { "AudioRingBuffer::writeData has overflown the buffer. Overwriting old data." };
|
||||
static const QString DROPPED_SILENT_DEBUG { "AudioRingBuffer::addSilentSamples dropping silent samples to prevent overflow." };
|
||||
|
||||
AudioRingBuffer::AudioRingBuffer(int numFrameSamples, bool randomAccessMode, int numFramesCapacity) :
|
||||
AudioRingBuffer::AudioRingBuffer(int numFrameSamples, int numFramesCapacity) :
|
||||
_numFrameSamples(numFrameSamples),
|
||||
_frameCapacity(numFramesCapacity),
|
||||
_sampleCapacity(numFrameSamples * numFramesCapacity),
|
||||
_bufferLength(numFrameSamples * (numFramesCapacity + 1)),
|
||||
_numFrameSamples(numFrameSamples),
|
||||
_randomAccessMode(randomAccessMode),
|
||||
_overflowCount(0)
|
||||
_bufferLength(numFrameSamples * (numFramesCapacity + 1))
|
||||
{
|
||||
if (numFrameSamples) {
|
||||
_buffer = new int16_t[_bufferLength];
|
||||
memset(_buffer, 0, _bufferLength * sizeof(int16_t));
|
||||
_nextOutput = _buffer;
|
||||
_endOfLastWrite = _buffer;
|
||||
} else {
|
||||
_buffer = NULL;
|
||||
_nextOutput = NULL;
|
||||
_endOfLastWrite = NULL;
|
||||
}
|
||||
|
||||
static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex(RING_BUFFER_OVERFLOW_DEBUG);
|
||||
static QString repeatedOverflowMessage = LogHandler::getInstance().addRepeatedMessageRegex(RING_BUFFER_OVERFLOW_DEBUG);
|
||||
static QString repeatedDroppedMessage = LogHandler::getInstance().addRepeatedMessageRegex(DROPPED_SILENT_DEBUG);
|
||||
};
|
||||
|
||||
AudioRingBuffer::~AudioRingBuffer() {
|
||||
delete[] _buffer;
|
||||
}
|
||||
|
||||
void AudioRingBuffer::clear() {
|
||||
_endOfLastWrite = _buffer;
|
||||
_nextOutput = _buffer;
|
||||
}
|
||||
|
||||
void AudioRingBuffer::reset() {
|
||||
clear();
|
||||
_overflowCount = 0;
|
||||
|
@ -58,109 +59,82 @@ void AudioRingBuffer::reset() {
|
|||
|
||||
void AudioRingBuffer::resizeForFrameSize(int numFrameSamples) {
|
||||
delete[] _buffer;
|
||||
_numFrameSamples = numFrameSamples;
|
||||
_sampleCapacity = numFrameSamples * _frameCapacity;
|
||||
_bufferLength = numFrameSamples * (_frameCapacity + 1);
|
||||
_numFrameSamples = numFrameSamples;
|
||||
_buffer = new int16_t[_bufferLength];
|
||||
memset(_buffer, 0, _bufferLength * sizeof(int16_t));
|
||||
if (_randomAccessMode) {
|
||||
memset(_buffer, 0, _bufferLength * sizeof(int16_t));
|
||||
}
|
||||
reset();
|
||||
}
|
||||
|
||||
void AudioRingBuffer::clear() {
|
||||
_endOfLastWrite = _buffer;
|
||||
_nextOutput = _buffer;
|
||||
if (numFrameSamples) {
|
||||
_buffer = new int16_t[_bufferLength];
|
||||
memset(_buffer, 0, _bufferLength * sizeof(int16_t));
|
||||
} else {
|
||||
_buffer = nullptr;
|
||||
}
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
int AudioRingBuffer::readSamples(int16_t* destination, int maxSamples) {
|
||||
return readData((char*)destination, maxSamples * sizeof(int16_t)) / sizeof(int16_t);
|
||||
}
|
||||
|
||||
int AudioRingBuffer::writeSamples(const int16_t* source, int maxSamples) {
|
||||
return writeData((char*)source, maxSamples * sizeof(int16_t)) / sizeof(int16_t);
|
||||
}
|
||||
|
||||
int AudioRingBuffer::readData(char *data, int maxSize) {
|
||||
|
||||
// only copy up to the number of samples we have available
|
||||
int numReadSamples = std::min((int)(maxSize / sizeof(int16_t)), samplesAvailable());
|
||||
|
||||
// If we're in random access mode, then we consider our number of available read samples slightly
|
||||
// differently. Namely, if anything has been written, we say we have as many samples as they ask for
|
||||
// otherwise we say we have nothing available
|
||||
if (_randomAccessMode) {
|
||||
numReadSamples = _endOfLastWrite ? (maxSize / sizeof(int16_t)) : 0;
|
||||
}
|
||||
int maxSamples = maxSize / sizeof(int16_t);
|
||||
int numReadSamples = std::min(maxSamples, samplesAvailable());
|
||||
|
||||
if (_nextOutput + numReadSamples > _buffer + _bufferLength) {
|
||||
// we're going to need to do two reads to get this data, it wraps around the edge
|
||||
int numSamplesToEnd = (_buffer + _bufferLength) - _nextOutput;
|
||||
|
||||
// read to the end of the buffer
|
||||
int numSamplesToEnd = (_buffer + _bufferLength) - _nextOutput;
|
||||
memcpy(data, _nextOutput, numSamplesToEnd * sizeof(int16_t));
|
||||
if (_randomAccessMode) {
|
||||
memset(_nextOutput, 0, numSamplesToEnd * sizeof(int16_t)); // clear it
|
||||
}
|
||||
|
||||
// read the rest from the beginning of the buffer
|
||||
memcpy(data + (numSamplesToEnd * sizeof(int16_t)), _buffer, (numReadSamples - numSamplesToEnd) * sizeof(int16_t));
|
||||
if (_randomAccessMode) {
|
||||
memset(_buffer, 0, (numReadSamples - numSamplesToEnd) * sizeof(int16_t)); // clear it
|
||||
}
|
||||
} else {
|
||||
// read the data
|
||||
memcpy(data, _nextOutput, numReadSamples * sizeof(int16_t));
|
||||
if (_randomAccessMode) {
|
||||
memset(_nextOutput, 0, numReadSamples * sizeof(int16_t)); // clear it
|
||||
}
|
||||
}
|
||||
|
||||
// push the position of _nextOutput by the number of samples read
|
||||
_nextOutput = shiftedPositionAccomodatingWrap(_nextOutput, numReadSamples);
|
||||
shiftReadPosition(numReadSamples);
|
||||
|
||||
return numReadSamples * sizeof(int16_t);
|
||||
}
|
||||
|
||||
int AudioRingBuffer::writeSamples(const int16_t* source, int maxSamples) {
|
||||
return writeData((const char*)source, maxSamples * sizeof(int16_t)) / sizeof(int16_t);
|
||||
}
|
||||
|
||||
int AudioRingBuffer::writeData(const char* data, int maxSize) {
|
||||
// make sure we have enough bytes left for this to be the right amount of audio
|
||||
// otherwise we should not copy that data, and leave the buffer pointers where they are
|
||||
int samplesToCopy = std::min((int)(maxSize / sizeof(int16_t)), _sampleCapacity);
|
||||
|
||||
// only copy up to the number of samples we have capacity for
|
||||
int maxSamples = maxSize / sizeof(int16_t);
|
||||
int numWriteSamples = std::min(maxSamples, _sampleCapacity);
|
||||
int samplesRoomFor = _sampleCapacity - samplesAvailable();
|
||||
if (samplesToCopy > samplesRoomFor) {
|
||||
// there's not enough room for this write. erase old data to make room for this new data
|
||||
int samplesToDelete = samplesToCopy - samplesRoomFor;
|
||||
|
||||
if (numWriteSamples > samplesRoomFor) {
|
||||
// there's not enough room for this write. erase old data to make room for this new data
|
||||
int samplesToDelete = numWriteSamples - samplesRoomFor;
|
||||
_nextOutput = shiftedPositionAccomodatingWrap(_nextOutput, samplesToDelete);
|
||||
_overflowCount++;
|
||||
|
||||
qCDebug(audio) << qPrintable(RING_BUFFER_OVERFLOW_DEBUG);
|
||||
}
|
||||
|
||||
if (_endOfLastWrite + samplesToCopy <= _buffer + _bufferLength) {
|
||||
memcpy(_endOfLastWrite, data, samplesToCopy * sizeof(int16_t));
|
||||
} else {
|
||||
if (_endOfLastWrite + numWriteSamples > _buffer + _bufferLength) {
|
||||
// we're going to need to do two writes to set this data, it wraps around the edge
|
||||
int numSamplesToEnd = (_buffer + _bufferLength) - _endOfLastWrite;
|
||||
|
||||
// write to the end of the buffer
|
||||
memcpy(_endOfLastWrite, data, numSamplesToEnd * sizeof(int16_t));
|
||||
memcpy(_buffer, data + (numSamplesToEnd * sizeof(int16_t)), (samplesToCopy - numSamplesToEnd) * sizeof(int16_t));
|
||||
|
||||
// write the rest to the beginning of the buffer
|
||||
memcpy(_buffer, data + (numSamplesToEnd * sizeof(int16_t)), (numWriteSamples - numSamplesToEnd) * sizeof(int16_t));
|
||||
} else {
|
||||
memcpy(_endOfLastWrite, data, numWriteSamples * sizeof(int16_t));
|
||||
}
|
||||
|
||||
_endOfLastWrite = shiftedPositionAccomodatingWrap(_endOfLastWrite, samplesToCopy);
|
||||
_endOfLastWrite = shiftedPositionAccomodatingWrap(_endOfLastWrite, numWriteSamples);
|
||||
|
||||
return samplesToCopy * sizeof(int16_t);
|
||||
}
|
||||
|
||||
int16_t& AudioRingBuffer::operator[](const int index) {
|
||||
return *shiftedPositionAccomodatingWrap(_nextOutput, index);
|
||||
}
|
||||
|
||||
const int16_t& AudioRingBuffer::operator[] (const int index) const {
|
||||
return *shiftedPositionAccomodatingWrap(_nextOutput, index);
|
||||
}
|
||||
|
||||
void AudioRingBuffer::shiftReadPosition(unsigned int numSamples) {
|
||||
_nextOutput = shiftedPositionAccomodatingWrap(_nextOutput, numSamples);
|
||||
return numWriteSamples * sizeof(int16_t);
|
||||
}
|
||||
|
||||
int AudioRingBuffer::samplesAvailable() const {
|
||||
|
@ -176,35 +150,31 @@ int AudioRingBuffer::samplesAvailable() const {
|
|||
}
|
||||
|
||||
int AudioRingBuffer::addSilentSamples(int silentSamples) {
|
||||
|
||||
// NOTE: This implementation is nearly identical to writeData save for s/memcpy/memset, refer to comments there
|
||||
int numWriteSamples = std::min(silentSamples, _sampleCapacity);
|
||||
int samplesRoomFor = _sampleCapacity - samplesAvailable();
|
||||
if (silentSamples > samplesRoomFor) {
|
||||
// there's not enough room for this write. write as many silent samples as we have room for
|
||||
silentSamples = samplesRoomFor;
|
||||
|
||||
static const QString DROPPED_SILENT_DEBUG {
|
||||
"AudioRingBuffer::addSilentSamples dropping silent samples to prevent overflow."
|
||||
};
|
||||
static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex(DROPPED_SILENT_DEBUG);
|
||||
if (numWriteSamples > samplesRoomFor) {
|
||||
numWriteSamples = samplesRoomFor;
|
||||
|
||||
qCDebug(audio) << qPrintable(DROPPED_SILENT_DEBUG);
|
||||
}
|
||||
|
||||
// memset zeroes into the buffer, accomodate a wrap around the end
|
||||
// push the _endOfLastWrite to the correct spot
|
||||
if (_endOfLastWrite + silentSamples <= _buffer + _bufferLength) {
|
||||
memset(_endOfLastWrite, 0, silentSamples * sizeof(int16_t));
|
||||
} else {
|
||||
if (_endOfLastWrite + numWriteSamples > _buffer + _bufferLength) {
|
||||
int numSamplesToEnd = (_buffer + _bufferLength) - _endOfLastWrite;
|
||||
memset(_endOfLastWrite, 0, numSamplesToEnd * sizeof(int16_t));
|
||||
memset(_buffer, 0, (silentSamples - numSamplesToEnd) * sizeof(int16_t));
|
||||
memset(_buffer, 0, (numWriteSamples - numSamplesToEnd) * sizeof(int16_t));
|
||||
} else {
|
||||
memset(_endOfLastWrite, 0, numWriteSamples * sizeof(int16_t));
|
||||
}
|
||||
_endOfLastWrite = shiftedPositionAccomodatingWrap(_endOfLastWrite, silentSamples);
|
||||
|
||||
return silentSamples;
|
||||
_endOfLastWrite = shiftedPositionAccomodatingWrap(_endOfLastWrite, numWriteSamples);
|
||||
|
||||
return numWriteSamples;
|
||||
}
|
||||
|
||||
int16_t* AudioRingBuffer::shiftedPositionAccomodatingWrap(int16_t* position, int numSamplesShift) const {
|
||||
|
||||
// NOTE: It is possible to shift out-of-bounds if (|numSamplesShift| > 2 * _bufferLength), but this should not occur
|
||||
if (numSamplesShift > 0 && position + numSamplesShift >= _buffer + _bufferLength) {
|
||||
// this shift will wrap the position around to the beginning of the ring
|
||||
return position + numSamplesShift - _bufferLength;
|
||||
|
@ -217,13 +187,15 @@ int16_t* AudioRingBuffer::shiftedPositionAccomodatingWrap(int16_t* position, int
|
|||
}
|
||||
|
||||
float AudioRingBuffer::getFrameLoudness(const int16_t* frameStart) const {
|
||||
// FIXME: This is a bad measure of loudness - normal estimation uses sqrt(sum(x*x))
|
||||
float loudness = 0.0f;
|
||||
const int16_t* sampleAt = frameStart;
|
||||
const int16_t* _bufferLastAt = _buffer + _bufferLength - 1;
|
||||
const int16_t* bufferLastAt = _buffer + _bufferLength - 1;
|
||||
|
||||
for (int i = 0; i < _numFrameSamples; ++i) {
|
||||
loudness += (float) std::abs(*sampleAt);
|
||||
sampleAt = sampleAt == _bufferLastAt ? _buffer : sampleAt + 1;
|
||||
// wrap if necessary
|
||||
sampleAt = sampleAt == bufferLastAt ? _buffer : sampleAt + 1;
|
||||
}
|
||||
loudness /= _numFrameSamples;
|
||||
loudness /= AudioConstants::MAX_SAMPLE_VALUE;
|
||||
|
@ -238,10 +210,6 @@ float AudioRingBuffer::getFrameLoudness(ConstIterator frameStart) const {
|
|||
return getFrameLoudness(&(*frameStart));
|
||||
}
|
||||
|
||||
float AudioRingBuffer::getNextOutputFrameLoudness() const {
|
||||
return getFrameLoudness(_nextOutput);
|
||||
}
|
||||
|
||||
int AudioRingBuffer::writeSamples(ConstIterator source, int maxSamples) {
|
||||
int samplesToCopy = std::min(maxSamples, _sampleCapacity);
|
||||
int samplesRoomFor = _sampleCapacity - samplesAvailable();
|
||||
|
|
|
@ -23,73 +23,69 @@ const int DEFAULT_RING_BUFFER_FRAME_CAPACITY = 10;
|
|||
|
||||
class AudioRingBuffer {
|
||||
public:
|
||||
AudioRingBuffer(int numFrameSamples, bool randomAccessMode = false, int numFramesCapacity = DEFAULT_RING_BUFFER_FRAME_CAPACITY);
|
||||
AudioRingBuffer(int numFrameSamples, int numFramesCapacity = DEFAULT_RING_BUFFER_FRAME_CAPACITY);
|
||||
~AudioRingBuffer();
|
||||
|
||||
void reset();
|
||||
void resizeForFrameSize(int numFrameSamples);
|
||||
// disallow copying
|
||||
AudioRingBuffer(const AudioRingBuffer&) = delete;
|
||||
AudioRingBuffer(AudioRingBuffer&&) = delete;
|
||||
AudioRingBuffer& operator=(const AudioRingBuffer&) = delete;
|
||||
|
||||
/// Invalidate any data in the buffer
|
||||
void clear();
|
||||
|
||||
int getSampleCapacity() const { return _sampleCapacity; }
|
||||
int getFrameCapacity() const { return _frameCapacity; }
|
||||
/// Clear and reset the overflow count
|
||||
void reset();
|
||||
|
||||
/// Resize frame size (causes a reset())
|
||||
// FIXME: discards any data in the buffer
|
||||
void resizeForFrameSize(int numFrameSamples);
|
||||
|
||||
/// Read up to maxSamples into destination (will only read up to samplesAvailable())
|
||||
/// Returns number of read samples
|
||||
int readSamples(int16_t* destination, int maxSamples);
|
||||
|
||||
/// Write up to maxSamples from source (will only write up to sample capacity)
|
||||
/// Returns number of written samples
|
||||
int writeSamples(const int16_t* source, int maxSamples);
|
||||
|
||||
int readData(char* data, int maxSize);
|
||||
int writeData(const char* data, int maxSize);
|
||||
/// Write up to maxSamples silent samples (will only write until other data exists in the buffer)
|
||||
/// This method will not overwrite existing data in the buffer, instead dropping silent samples that would overflow
|
||||
/// Returns number of written silent samples
|
||||
int addSilentSamples(int maxSamples);
|
||||
|
||||
int16_t& operator[](const int index);
|
||||
const int16_t& operator[] (const int index) const;
|
||||
/// Read up to maxSize into destination
|
||||
/// Returns number of read bytes
|
||||
int readData(char* destination, int maxSize);
|
||||
|
||||
void shiftReadPosition(unsigned int numSamples);
|
||||
/// Write up to maxSize from source
|
||||
/// Returns number of written bytes
|
||||
int writeData(const char* source, int maxSize);
|
||||
|
||||
float getNextOutputFrameLoudness() const;
|
||||
/// Returns a reference to the index-th sample offset from the current read sample
|
||||
int16_t& operator[](const int index) { return *shiftedPositionAccomodatingWrap(_nextOutput, index); }
|
||||
const int16_t& operator[] (const int index) const { return *shiftedPositionAccomodatingWrap(_nextOutput, index); }
|
||||
|
||||
/// Essentially discards the next numSamples from the ring buffer
|
||||
/// NOTE: This is not checked - it is possible to shift past written data
|
||||
/// Use samplesAvailable() to see the distance a valid shift can go
|
||||
void shiftReadPosition(unsigned int numSamples) { _nextOutput = shiftedPositionAccomodatingWrap(_nextOutput, numSamples); }
|
||||
|
||||
int samplesAvailable() const;
|
||||
int framesAvailable() const { return (_numFrameSamples == 0) ? 0 : samplesAvailable() / _numFrameSamples; }
|
||||
float getNextOutputFrameLoudness() const { return getFrameLoudness(_nextOutput); }
|
||||
|
||||
|
||||
int getNumFrameSamples() const { return _numFrameSamples; }
|
||||
int getFrameCapacity() const { return _frameCapacity; }
|
||||
int getSampleCapacity() const { return _sampleCapacity; }
|
||||
/// Return times the ring buffer has overwritten old data
|
||||
int getOverflowCount() const { return _overflowCount; }
|
||||
|
||||
int getOverflowCount() const { return _overflowCount; } /// how many times has the ring buffer has overwritten old data
|
||||
|
||||
int addSilentSamples(int samples);
|
||||
|
||||
private:
|
||||
float getFrameLoudness(const int16_t* frameStart) const;
|
||||
|
||||
protected:
|
||||
// disallow copying of AudioRingBuffer objects
|
||||
AudioRingBuffer(const AudioRingBuffer&);
|
||||
AudioRingBuffer& operator= (const AudioRingBuffer&);
|
||||
|
||||
int16_t* shiftedPositionAccomodatingWrap(int16_t* position, int numSamplesShift) const;
|
||||
|
||||
int _frameCapacity;
|
||||
int _sampleCapacity;
|
||||
int _bufferLength; // actual length of _buffer: will be one frame larger than _sampleCapacity
|
||||
int _numFrameSamples;
|
||||
int16_t* _nextOutput;
|
||||
int16_t* _endOfLastWrite;
|
||||
int16_t* _buffer;
|
||||
bool _randomAccessMode; /// will this ringbuffer be used for random access? if so, do some special processing
|
||||
|
||||
int _overflowCount; /// how many times has the ring buffer has overwritten old data
|
||||
|
||||
public:
|
||||
class ConstIterator { //public std::iterator < std::forward_iterator_tag, int16_t > {
|
||||
class ConstIterator {
|
||||
public:
|
||||
ConstIterator()
|
||||
: _bufferLength(0),
|
||||
_bufferFirst(NULL),
|
||||
_bufferLast(NULL),
|
||||
_at(NULL) {}
|
||||
ConstIterator(int16_t* bufferFirst, int capacity, int16_t* at)
|
||||
: _bufferLength(capacity),
|
||||
_bufferFirst(bufferFirst),
|
||||
_bufferLast(bufferFirst + capacity - 1),
|
||||
_at(at) {}
|
||||
ConstIterator();
|
||||
ConstIterator(int16_t* bufferFirst, int capacity, int16_t* at);
|
||||
ConstIterator(const ConstIterator& rhs) = default;
|
||||
|
||||
bool isNull() const { return _at == NULL; }
|
||||
|
@ -98,95 +94,143 @@ public:
|
|||
bool operator!=(const ConstIterator& rhs) { return _at != rhs._at; }
|
||||
const int16_t& operator*() { return *_at; }
|
||||
|
||||
ConstIterator& operator=(const ConstIterator& rhs) {
|
||||
_bufferLength = rhs._bufferLength;
|
||||
_bufferFirst = rhs._bufferFirst;
|
||||
_bufferLast = rhs._bufferLast;
|
||||
_at = rhs._at;
|
||||
return *this;
|
||||
}
|
||||
ConstIterator& operator=(const ConstIterator& rhs);
|
||||
ConstIterator& operator++();
|
||||
ConstIterator operator++(int);
|
||||
ConstIterator& operator--();
|
||||
ConstIterator operator--(int);
|
||||
const int16_t& operator[] (int i);
|
||||
ConstIterator operator+(int i);
|
||||
ConstIterator operator-(int i);
|
||||
|
||||
ConstIterator& operator++() {
|
||||
_at = (_at == _bufferLast) ? _bufferFirst : _at + 1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ConstIterator operator++(int) {
|
||||
ConstIterator tmp(*this);
|
||||
++(*this);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
ConstIterator& operator--() {
|
||||
_at = (_at == _bufferFirst) ? _bufferLast : _at - 1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ConstIterator operator--(int) {
|
||||
ConstIterator tmp(*this);
|
||||
--(*this);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
const int16_t& operator[] (int i) {
|
||||
return *atShiftedBy(i);
|
||||
}
|
||||
|
||||
ConstIterator operator+(int i) {
|
||||
return ConstIterator(_bufferFirst, _bufferLength, atShiftedBy(i));
|
||||
}
|
||||
|
||||
ConstIterator operator-(int i) {
|
||||
return ConstIterator(_bufferFirst, _bufferLength, atShiftedBy(-i));
|
||||
}
|
||||
|
||||
void readSamples(int16_t* dest, int numSamples) {
|
||||
auto samplesToEnd = _bufferLast - _at + 1;
|
||||
|
||||
if (samplesToEnd >= numSamples) {
|
||||
memcpy(dest, _at, numSamples * sizeof(int16_t));
|
||||
_at += numSamples;
|
||||
} else {
|
||||
auto samplesFromStart = numSamples - samplesToEnd;
|
||||
memcpy(dest, _at, samplesToEnd * sizeof(int16_t));
|
||||
memcpy(dest + samplesToEnd, _bufferFirst, samplesFromStart * sizeof(int16_t));
|
||||
|
||||
_at = _bufferFirst + samplesFromStart;
|
||||
}
|
||||
}
|
||||
|
||||
void readSamplesWithFade(int16_t* dest, int numSamples, float fade) {
|
||||
int16_t* at = _at;
|
||||
for (int i = 0; i < numSamples; i++) {
|
||||
*dest = (float)*at * fade;
|
||||
++dest;
|
||||
at = (at == _bufferLast) ? _bufferFirst : at + 1;
|
||||
}
|
||||
}
|
||||
void readSamples(int16_t* dest, int numSamples);
|
||||
void readSamplesWithFade(int16_t* dest, int numSamples, float fade);
|
||||
|
||||
private:
|
||||
int16_t* atShiftedBy(int i) {
|
||||
i = (_at - _bufferFirst + i) % _bufferLength;
|
||||
if (i < 0) {
|
||||
i += _bufferLength;
|
||||
}
|
||||
return _bufferFirst + i;
|
||||
}
|
||||
int16_t* atShiftedBy(int i);
|
||||
|
||||
private:
|
||||
int _bufferLength;
|
||||
int16_t* _bufferFirst;
|
||||
int16_t* _bufferLast;
|
||||
int16_t* _at;
|
||||
};
|
||||
|
||||
ConstIterator nextOutput() const { return ConstIterator(_buffer, _bufferLength, _nextOutput); }
|
||||
ConstIterator lastFrameWritten() const { return ConstIterator(_buffer, _bufferLength, _endOfLastWrite) - _numFrameSamples; }
|
||||
|
||||
float getFrameLoudness(ConstIterator frameStart) const;
|
||||
ConstIterator nextOutput() const;
|
||||
ConstIterator lastFrameWritten() const;
|
||||
|
||||
int writeSamples(ConstIterator source, int maxSamples);
|
||||
int writeSamplesWithFade(ConstIterator source, int maxSamples, float fade);
|
||||
|
||||
float getFrameLoudness(ConstIterator frameStart) const;
|
||||
|
||||
protected:
|
||||
int16_t* shiftedPositionAccomodatingWrap(int16_t* position, int numSamplesShift) const;
|
||||
float getFrameLoudness(const int16_t* frameStart) const;
|
||||
|
||||
int _numFrameSamples;
|
||||
int _frameCapacity;
|
||||
int _sampleCapacity;
|
||||
int _bufferLength; // actual _buffer length (_sampleCapacity + 1)
|
||||
int _overflowCount{ 0 }; // times the ring buffer has overwritten data
|
||||
|
||||
int16_t* _nextOutput{ nullptr };
|
||||
int16_t* _endOfLastWrite{ nullptr };
|
||||
int16_t* _buffer{ nullptr };
|
||||
};
|
||||
|
||||
// inline the iterator:
|
||||
inline AudioRingBuffer::ConstIterator::ConstIterator() :
|
||||
_bufferLength(0),
|
||||
_bufferFirst(NULL),
|
||||
_bufferLast(NULL),
|
||||
_at(NULL) {}
|
||||
|
||||
inline AudioRingBuffer::ConstIterator::ConstIterator(int16_t* bufferFirst, int capacity, int16_t* at) :
|
||||
_bufferLength(capacity),
|
||||
_bufferFirst(bufferFirst),
|
||||
_bufferLast(bufferFirst + capacity - 1),
|
||||
_at(at) {}
|
||||
|
||||
inline AudioRingBuffer::ConstIterator& AudioRingBuffer::ConstIterator::operator=(const ConstIterator& rhs) {
|
||||
_bufferLength = rhs._bufferLength;
|
||||
_bufferFirst = rhs._bufferFirst;
|
||||
_bufferLast = rhs._bufferLast;
|
||||
_at = rhs._at;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline AudioRingBuffer::ConstIterator& AudioRingBuffer::ConstIterator::operator++() {
|
||||
_at = (_at == _bufferLast) ? _bufferFirst : _at + 1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline AudioRingBuffer::ConstIterator AudioRingBuffer::ConstIterator::operator++(int) {
|
||||
ConstIterator tmp(*this);
|
||||
++(*this);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
inline AudioRingBuffer::ConstIterator& AudioRingBuffer::ConstIterator::operator--() {
|
||||
_at = (_at == _bufferFirst) ? _bufferLast : _at - 1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline AudioRingBuffer::ConstIterator AudioRingBuffer::ConstIterator::operator--(int) {
|
||||
ConstIterator tmp(*this);
|
||||
--(*this);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
inline const int16_t& AudioRingBuffer::ConstIterator::operator[] (int i) {
|
||||
return *atShiftedBy(i);
|
||||
}
|
||||
|
||||
inline AudioRingBuffer::ConstIterator AudioRingBuffer::ConstIterator::operator+(int i) {
|
||||
return ConstIterator(_bufferFirst, _bufferLength, atShiftedBy(i));
|
||||
}
|
||||
|
||||
inline AudioRingBuffer::ConstIterator AudioRingBuffer::ConstIterator::operator-(int i) {
|
||||
return ConstIterator(_bufferFirst, _bufferLength, atShiftedBy(-i));
|
||||
}
|
||||
|
||||
inline int16_t* AudioRingBuffer::ConstIterator::atShiftedBy(int i) {
|
||||
i = (_at - _bufferFirst + i) % _bufferLength;
|
||||
if (i < 0) {
|
||||
i += _bufferLength;
|
||||
}
|
||||
return _bufferFirst + i;
|
||||
}
|
||||
|
||||
inline void AudioRingBuffer::ConstIterator::readSamples(int16_t* dest, int numSamples) {
|
||||
auto samplesToEnd = _bufferLast - _at + 1;
|
||||
|
||||
if (samplesToEnd >= numSamples) {
|
||||
memcpy(dest, _at, numSamples * sizeof(int16_t));
|
||||
_at += numSamples;
|
||||
} else {
|
||||
auto samplesFromStart = numSamples - samplesToEnd;
|
||||
memcpy(dest, _at, samplesToEnd * sizeof(int16_t));
|
||||
memcpy(dest + samplesToEnd, _bufferFirst, samplesFromStart * sizeof(int16_t));
|
||||
|
||||
_at = _bufferFirst + samplesFromStart;
|
||||
}
|
||||
}
|
||||
|
||||
inline void AudioRingBuffer::ConstIterator::readSamplesWithFade(int16_t* dest, int numSamples, float fade) {
|
||||
int16_t* at = _at;
|
||||
for (int i = 0; i < numSamples; i++) {
|
||||
*dest = (float)*at * fade;
|
||||
++dest;
|
||||
at = (at == _bufferLast) ? _bufferFirst : at + 1;
|
||||
}
|
||||
}
|
||||
|
||||
inline AudioRingBuffer::ConstIterator AudioRingBuffer::nextOutput() const {
|
||||
return ConstIterator(_buffer, _bufferLength, _nextOutput);
|
||||
}
|
||||
|
||||
inline AudioRingBuffer::ConstIterator AudioRingBuffer::lastFrameWritten() const {
|
||||
return ConstIterator(_buffer, _bufferLength, _endOfLastWrite) - _numFrameSamples;
|
||||
}
|
||||
|
||||
#endif // hifi_AudioRingBuffer_h
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
const int STARVE_HISTORY_CAPACITY = 50;
|
||||
|
||||
InboundAudioStream::InboundAudioStream(int numFrameSamples, int numFramesCapacity, const Settings& settings) :
|
||||
_ringBuffer(numFrameSamples, false, numFramesCapacity),
|
||||
_ringBuffer(numFrameSamples, numFramesCapacity),
|
||||
_lastPopSucceeded(false),
|
||||
_lastPopOutput(),
|
||||
_dynamicJitterBuffers(settings._dynamicJitterBuffers),
|
||||
|
|
|
@ -205,8 +205,10 @@ void Context::create() {
|
|||
formatAttribs.push_back(24);
|
||||
formatAttribs.push_back(WGL_STENCIL_BITS_ARB);
|
||||
formatAttribs.push_back(8);
|
||||
formatAttribs.push_back(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB);
|
||||
formatAttribs.push_back(GL_TRUE);
|
||||
#ifdef NATIVE_SRGB_FRAMEBUFFER
|
||||
// formatAttribs.push_back(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB);
|
||||
// formatAttribs.push_back(GL_TRUE);
|
||||
#endif
|
||||
// terminate the list
|
||||
formatAttribs.push_back(0);
|
||||
UINT numFormats;
|
||||
|
|
28
tests/render-texture-load/CMakeLists.txt
Normal file
28
tests/render-texture-load/CMakeLists.txt
Normal file
|
@ -0,0 +1,28 @@
|
|||
|
||||
set(TARGET_NAME render-texture-load)
|
||||
|
||||
if (WIN32)
|
||||
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /ignore:4049 /ignore:4217")
|
||||
endif()
|
||||
|
||||
# This is not a testcase -- just set it up as a regular hifi project
|
||||
setup_hifi_project(Quick Gui OpenGL)
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/")
|
||||
|
||||
# link in the shared libraries
|
||||
link_hifi_libraries(shared octree gl gpu gpu-gl render model model-networking networking render-utils fbx entities entities-renderer animation audio avatars script-engine physics)
|
||||
|
||||
package_libraries_for_deployment()
|
||||
|
||||
target_zlib()
|
||||
add_dependency_external_projects(quazip)
|
||||
find_package(QuaZip REQUIRED)
|
||||
target_include_directories(${TARGET_NAME} SYSTEM PUBLIC ${QUAZIP_INCLUDE_DIRS})
|
||||
target_link_libraries(${TARGET_NAME} ${QUAZIP_LIBRARIES})
|
||||
|
||||
if (WIN32)
|
||||
add_paths_to_fixup_libs(${QUAZIP_DLL_PATH})
|
||||
endif ()
|
||||
|
||||
|
||||
target_bullet()
|
585
tests/render-texture-load/src/main.cpp
Normal file
585
tests/render-texture-load/src/main.cpp
Normal file
|
@ -0,0 +1,585 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2016/07/01
|
||||
// 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 <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
|
||||
#include <gl/Config.h>
|
||||
#include <gl/Context.h>
|
||||
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QElapsedTimer>
|
||||
#include <QtCore/QLoggingCategory>
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtCore/QThread>
|
||||
#include <QtCore/QThreadPool>
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QByteArray>
|
||||
#include <QtCore/QTemporaryDir>
|
||||
#include <QtCore/QTemporaryFile>
|
||||
#include <QtNetwork/QNetworkAccessManager>
|
||||
#include <QtNetwork/QNetworkRequest>
|
||||
#include <QtNetwork/QNetworkReply>
|
||||
|
||||
#include <QtGui/QGuiApplication>
|
||||
#include <QtGui/QResizeEvent>
|
||||
#include <QtGui/QWindow>
|
||||
|
||||
#include <QtWidgets/QFileDialog>
|
||||
#include <QtWidgets/QInputDialog>
|
||||
#include <QtWidgets/QMessageBox>
|
||||
#include <QtWidgets/QApplication>
|
||||
|
||||
#include <quazip5/quazip.h>
|
||||
#include <quazip5/JlCompress.h>
|
||||
|
||||
|
||||
#include <shared/RateCounter.h>
|
||||
#include <AssetClient.h>
|
||||
#include <PathUtils.h>
|
||||
|
||||
#include <gpu/gl/GLBackend.h>
|
||||
#include <gpu/gl/GLFramebuffer.h>
|
||||
#include <gpu/gl/GLTexture.h>
|
||||
#include <gpu/StandardShaderLib.h>
|
||||
|
||||
#include <AddressManager.h>
|
||||
#include <NodeList.h>
|
||||
#include <TextureCache.h>
|
||||
#include <FramebufferCache.h>
|
||||
#include <GeometryCache.h>
|
||||
#include <DeferredLightingEffect.h>
|
||||
#include <RenderShadowTask.h>
|
||||
#include <RenderDeferredTask.h>
|
||||
|
||||
extern QThread* RENDER_THREAD;
|
||||
|
||||
static const QString DATA_SET = "https://hifi-content.s3.amazonaws.com/austin/textures.zip";
|
||||
static const QTemporaryDir DATA_DIR;
|
||||
|
||||
|
||||
class FileDownloader : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
using Handler = std::function<void(const QByteArray& data)>;
|
||||
|
||||
FileDownloader(QUrl url, const Handler& handler, QObject *parent = 0) : QObject(parent), _handler(handler) {
|
||||
connect(&_accessManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(fileDownloaded(QNetworkReply*)));
|
||||
_accessManager.get(QNetworkRequest(url));
|
||||
}
|
||||
|
||||
void waitForDownload() {
|
||||
while (!_complete) {
|
||||
QCoreApplication::processEvents();
|
||||
}
|
||||
}
|
||||
|
||||
private slots:
|
||||
void fileDownloaded(QNetworkReply* pReply) {
|
||||
_handler(pReply->readAll());
|
||||
pReply->deleteLater();
|
||||
_complete = true;
|
||||
}
|
||||
|
||||
private:
|
||||
QNetworkAccessManager _accessManager;
|
||||
Handler _handler;
|
||||
bool _complete { false };
|
||||
};
|
||||
|
||||
class RenderThread : public GenericThread {
|
||||
using Parent = GenericThread;
|
||||
public:
|
||||
gl::Context _context;
|
||||
gpu::PipelinePointer _presentPipeline;
|
||||
gpu::ContextPointer _gpuContext; // initialized during window creation
|
||||
std::atomic<size_t> _presentCount;
|
||||
QElapsedTimer _elapsed;
|
||||
std::atomic<uint16_t> _fps{ 1 };
|
||||
RateCounter<200> _fpsCounter;
|
||||
std::mutex _mutex;
|
||||
std::shared_ptr<gpu::Backend> _backend;
|
||||
std::vector<uint64_t> _frameTimes;
|
||||
size_t _frameIndex;
|
||||
std::mutex _frameLock;
|
||||
std::queue<gpu::FramePointer> _pendingFrames;
|
||||
gpu::FramePointer _activeFrame;
|
||||
QSize _size;
|
||||
static const size_t FRAME_TIME_BUFFER_SIZE{ 1024 };
|
||||
|
||||
void submitFrame(const gpu::FramePointer& frame) {
|
||||
std::unique_lock<std::mutex> lock(_frameLock);
|
||||
_pendingFrames.push(frame);
|
||||
}
|
||||
|
||||
|
||||
void initialize(QWindow* window, gl::Context& initContext) {
|
||||
setObjectName("RenderThread");
|
||||
_context.setWindow(window);
|
||||
_context.create();
|
||||
_context.makeCurrent();
|
||||
window->setSurfaceType(QSurface::OpenGLSurface);
|
||||
_context.makeCurrent(_context.qglContext(), window);
|
||||
// GPU library init
|
||||
gpu::Context::init<gpu::gl::GLBackend>();
|
||||
_gpuContext = std::make_shared<gpu::Context>();
|
||||
_backend = _gpuContext->getBackend();
|
||||
_context.makeCurrent();
|
||||
DependencyManager::get<DeferredLightingEffect>()->init();
|
||||
_context.makeCurrent();
|
||||
initContext.create();
|
||||
_context.doneCurrent();
|
||||
std::unique_lock<std::mutex> lock(_mutex);
|
||||
Parent::initialize();
|
||||
_context.moveToThread(_thread);
|
||||
}
|
||||
|
||||
void setup() override {
|
||||
RENDER_THREAD = QThread::currentThread();
|
||||
|
||||
// Wait until the context has been moved to this thread
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_mutex);
|
||||
}
|
||||
|
||||
_context.makeCurrent();
|
||||
glewExperimental = true;
|
||||
glewInit();
|
||||
glGetError();
|
||||
|
||||
//wglSwapIntervalEXT(0);
|
||||
_frameTimes.resize(FRAME_TIME_BUFFER_SIZE, 0);
|
||||
{
|
||||
auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS();
|
||||
auto ps = gpu::StandardShaderLib::getDrawTexturePS();
|
||||
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
|
||||
gpu::Shader::BindingSet slotBindings;
|
||||
gpu::Shader::makeProgram(*program, slotBindings);
|
||||
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||
_presentPipeline = gpu::Pipeline::create(program, state);
|
||||
}
|
||||
|
||||
//_textOverlay = new TextOverlay(glm::uvec2(800, 600));
|
||||
glViewport(0, 0, 800, 600);
|
||||
(void)CHECK_GL_ERROR();
|
||||
_elapsed.start();
|
||||
}
|
||||
|
||||
void shutdown() override {
|
||||
_activeFrame.reset();
|
||||
while (!_pendingFrames.empty()) {
|
||||
_gpuContext->consumeFrameUpdates(_pendingFrames.front());
|
||||
_pendingFrames.pop();
|
||||
}
|
||||
_presentPipeline.reset();
|
||||
_gpuContext.reset();
|
||||
}
|
||||
|
||||
void renderFrame(gpu::FramePointer& frame) {
|
||||
++_presentCount;
|
||||
_context.makeCurrent();
|
||||
_backend->recycle();
|
||||
_backend->syncCache();
|
||||
if (frame && !frame->batches.empty()) {
|
||||
_gpuContext->executeFrame(frame);
|
||||
|
||||
{
|
||||
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
gpu::Batch presentBatch;
|
||||
presentBatch.setViewportTransform({ 0, 0, _size.width(), _size.height() });
|
||||
presentBatch.enableStereo(false);
|
||||
presentBatch.resetViewTransform();
|
||||
presentBatch.setFramebuffer(gpu::FramebufferPointer());
|
||||
presentBatch.setResourceTexture(0, frame->framebuffer->getRenderBuffer(0));
|
||||
presentBatch.setPipeline(_presentPipeline);
|
||||
presentBatch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
_gpuContext->executeBatch(presentBatch);
|
||||
}
|
||||
(void)CHECK_GL_ERROR();
|
||||
}
|
||||
_context.makeCurrent();
|
||||
_context.swapBuffers();
|
||||
_fpsCounter.increment();
|
||||
static size_t _frameCount{ 0 };
|
||||
++_frameCount;
|
||||
if (_elapsed.elapsed() >= 500) {
|
||||
_fps = _fpsCounter.rate();
|
||||
_frameCount = 0;
|
||||
_elapsed.restart();
|
||||
}
|
||||
(void)CHECK_GL_ERROR();
|
||||
_context.doneCurrent();
|
||||
}
|
||||
|
||||
void report() {
|
||||
uint64_t total = 0;
|
||||
for (const auto& t : _frameTimes) {
|
||||
total += t;
|
||||
}
|
||||
auto averageFrameTime = total / FRAME_TIME_BUFFER_SIZE;
|
||||
qDebug() << "Average frame " << averageFrameTime;
|
||||
|
||||
std::list<std::pair<uint64_t, size_t>> sortedHighFrames;
|
||||
for (size_t i = 0; i < _frameTimes.size(); ++i) {
|
||||
const auto& t = _frameTimes[i];
|
||||
if (t > averageFrameTime * 6) {
|
||||
sortedHighFrames.push_back({ t, i } );
|
||||
}
|
||||
}
|
||||
|
||||
sortedHighFrames.sort();
|
||||
for (const auto& p : sortedHighFrames) {
|
||||
qDebug() << "Long frame " << p.first << " " << p.second;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool process() override {
|
||||
std::queue<gpu::FramePointer> pendingFrames;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_frameLock);
|
||||
pendingFrames.swap(_pendingFrames);
|
||||
}
|
||||
|
||||
while (!pendingFrames.empty()) {
|
||||
_activeFrame = pendingFrames.front();
|
||||
if (_activeFrame) {
|
||||
_gpuContext->consumeFrameUpdates(_activeFrame);
|
||||
}
|
||||
pendingFrames.pop();
|
||||
}
|
||||
|
||||
if (!_activeFrame) {
|
||||
QThread::msleep(1);
|
||||
return true;
|
||||
}
|
||||
|
||||
{
|
||||
auto start = usecTimestampNow();
|
||||
renderFrame(_activeFrame);
|
||||
auto duration = usecTimestampNow() - start;
|
||||
auto frameBufferIndex = _frameIndex % FRAME_TIME_BUFFER_SIZE;
|
||||
_frameTimes[frameBufferIndex] = duration;
|
||||
++_frameIndex;
|
||||
if (0 == _frameIndex % FRAME_TIME_BUFFER_SIZE) {
|
||||
report();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
QString fileForPath(const QString& name) {
|
||||
QCryptographicHash hash(QCryptographicHash::Md5);
|
||||
hash.addData(name.toLocal8Bit().data(), name.length());
|
||||
QString hashStr = QString(hash.result().toHex());
|
||||
auto dot = name.lastIndexOf('.');
|
||||
QString extension = name.right(name.length() - dot);
|
||||
QString result = DATA_DIR.path() + "/" + hashStr + extension;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Create a simple OpenGL window that renders text in various ways
|
||||
class QTestWindow : public QWindow {
|
||||
public:
|
||||
//"/-17.2049,-8.08629,-19.4153/0,0.881994,0,-0.47126"
|
||||
static void setup() {
|
||||
DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
|
||||
//DependencyManager::registerInheritance<SpatialParentFinder, ParentFinder>();
|
||||
DependencyManager::set<AddressManager>();
|
||||
DependencyManager::set<NodeList>(NodeType::Agent, 0);
|
||||
DependencyManager::set<DeferredLightingEffect>();
|
||||
DependencyManager::set<ResourceCacheSharedItems>();
|
||||
DependencyManager::set<TextureCache>();
|
||||
DependencyManager::set<FramebufferCache>();
|
||||
DependencyManager::set<GeometryCache>();
|
||||
DependencyManager::set<ModelCache>();
|
||||
DependencyManager::set<PathUtils>();
|
||||
}
|
||||
|
||||
struct TextureLoad {
|
||||
uint32_t time;
|
||||
QString file;
|
||||
QString src;
|
||||
};
|
||||
|
||||
QTestWindow() {
|
||||
|
||||
_currentTexture = _textures.end();
|
||||
{
|
||||
QStringList stringList;
|
||||
QFile textFile("h:/textures/loads.txt");
|
||||
textFile.open(QFile::ReadOnly);
|
||||
//... (open the file for reading, etc.)
|
||||
QTextStream textStream(&textFile);
|
||||
while (true) {
|
||||
QString line = textStream.readLine();
|
||||
if (line.isNull())
|
||||
break;
|
||||
else
|
||||
stringList.append(line);
|
||||
}
|
||||
|
||||
for (QString s : stringList) {
|
||||
auto index = s.indexOf(" ");
|
||||
QString timeStr = s.left(index);
|
||||
auto time = timeStr.toUInt();
|
||||
QString path = s.right(s.length() - index).trimmed();
|
||||
path = fileForPath(path);
|
||||
qDebug() << "Path " << path;
|
||||
if (!QFileInfo(path).exists()) {
|
||||
continue;
|
||||
}
|
||||
_textureLoads.push({ time, path, s });
|
||||
}
|
||||
}
|
||||
|
||||
installEventFilter(this);
|
||||
QThreadPool::globalInstance()->setMaxThreadCount(2);
|
||||
QThread::currentThread()->setPriority(QThread::HighestPriority);
|
||||
ResourceManager::init();
|
||||
setFlags(Qt::MSWindowsOwnDC | Qt::Window | Qt::Dialog | Qt::WindowMinMaxButtonsHint | Qt::WindowTitleHint);
|
||||
_size = QSize(800, 600);
|
||||
_renderThread._size = _size;
|
||||
setGeometry(QRect(QPoint(), _size));
|
||||
create();
|
||||
show();
|
||||
QCoreApplication::processEvents();
|
||||
// Create the initial context
|
||||
_renderThread.initialize(this, _initContext);
|
||||
_initContext.makeCurrent();
|
||||
// FIXME use a wait condition
|
||||
QThread::msleep(1000);
|
||||
_renderThread.submitFrame(gpu::FramePointer());
|
||||
_initContext.makeCurrent();
|
||||
{
|
||||
auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS();
|
||||
auto ps = gpu::StandardShaderLib::getDrawTexturePS();
|
||||
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
|
||||
gpu::Shader::makeProgram(*program);
|
||||
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||
state->setDepthTest(gpu::State::DepthTest(false));
|
||||
state->setScissorEnable(true);
|
||||
_simplePipeline = gpu::Pipeline::create(program, state);
|
||||
}
|
||||
|
||||
QTimer* timer = new QTimer(this);
|
||||
timer->setInterval(0);
|
||||
connect(timer, &QTimer::timeout, this, [this] {
|
||||
draw();
|
||||
});
|
||||
timer->start();
|
||||
_ready = true;
|
||||
}
|
||||
|
||||
virtual ~QTestWindow() {
|
||||
DependencyManager::destroy<FramebufferCache>();
|
||||
DependencyManager::destroy<TextureCache>();
|
||||
DependencyManager::destroy<ModelCache>();
|
||||
DependencyManager::destroy<GeometryCache>();
|
||||
ResourceManager::cleanup();
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
bool eventFilter(QObject *obj, QEvent *event) override {
|
||||
if (event->type() == QEvent::Close) {
|
||||
_renderThread.terminate();
|
||||
}
|
||||
|
||||
return QWindow::eventFilter(obj, event);
|
||||
}
|
||||
|
||||
void keyPressEvent(QKeyEvent* event) override {
|
||||
}
|
||||
|
||||
void keyReleaseEvent(QKeyEvent* event) override {
|
||||
}
|
||||
|
||||
void mouseMoveEvent(QMouseEvent* event) override {
|
||||
}
|
||||
|
||||
void resizeEvent(QResizeEvent* ev) override {
|
||||
resizeWindow(ev->size());
|
||||
}
|
||||
|
||||
private:
|
||||
std::queue<TextureLoad> _textureLoads;
|
||||
std::list<gpu::TexturePointer> _textures;
|
||||
std::list<gpu::TexturePointer>::iterator _currentTexture;
|
||||
|
||||
uint16_t _fps;
|
||||
gpu::PipelinePointer _simplePipeline;
|
||||
|
||||
void draw() {
|
||||
if (!_ready) {
|
||||
return;
|
||||
}
|
||||
if (!isVisible()) {
|
||||
return;
|
||||
}
|
||||
if (_renderCount.load() != 0 && _renderCount.load() >= _renderThread._presentCount.load()) {
|
||||
QThread::usleep(1);
|
||||
return;
|
||||
}
|
||||
_renderCount = _renderThread._presentCount.load();
|
||||
update();
|
||||
|
||||
QSize windowSize = _size;
|
||||
auto framebufferCache = DependencyManager::get<FramebufferCache>();
|
||||
framebufferCache->setFrameBufferSize(windowSize);
|
||||
|
||||
// Final framebuffer that will be handled to the display-plugin
|
||||
render();
|
||||
|
||||
if (_fps != _renderThread._fps) {
|
||||
_fps = _renderThread._fps;
|
||||
updateText();
|
||||
}
|
||||
}
|
||||
|
||||
void updateText() {
|
||||
setTitle(QString("FPS %1").arg(_fps));
|
||||
}
|
||||
|
||||
void update() {
|
||||
auto now = usecTimestampNow();
|
||||
static auto last = now;
|
||||
auto delta = (now - last) / USECS_PER_MSEC;
|
||||
if (!_textureLoads.empty()) {
|
||||
const auto& front = _textureLoads.front();
|
||||
if (delta >= front.time) {
|
||||
QFileInfo fileInfo(front.file);
|
||||
if (!fileInfo.exists()) {
|
||||
qDebug() << "Missing file " << front.file;
|
||||
} else {
|
||||
qDebug() << "Loading " << front.src;
|
||||
_textures.push_back(DependencyManager::get<TextureCache>()->getImageTexture(front.file));
|
||||
_currentTexture = _textures.begin();
|
||||
}
|
||||
_textureLoads.pop();
|
||||
if (_textureLoads.empty()) {
|
||||
qDebug() << "Done";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void render() {
|
||||
auto& gpuContext = _renderThread._gpuContext;
|
||||
gpuContext->beginFrame();
|
||||
gpu::doInBatch(gpuContext, [&](gpu::Batch& batch) {
|
||||
batch.resetStages();
|
||||
});
|
||||
PROFILE_RANGE(__FUNCTION__);
|
||||
auto framebuffer = DependencyManager::get<FramebufferCache>()->getFramebuffer();
|
||||
|
||||
gpu::doInBatch(gpuContext, [&](gpu::Batch& batch) {
|
||||
batch.enableStereo(false);
|
||||
batch.setFramebuffer(framebuffer);
|
||||
batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, vec4(1, 0, 0, 1));
|
||||
auto vpsize = framebuffer->getSize();
|
||||
auto vppos = ivec2(0);
|
||||
batch.setViewportTransform(ivec4(vppos, vpsize));
|
||||
if (_currentTexture != _textures.end()) {
|
||||
++_currentTexture;
|
||||
}
|
||||
if (_currentTexture == _textures.end()) {
|
||||
_currentTexture = _textures.begin();
|
||||
}
|
||||
|
||||
if (_currentTexture != _textures.end()) {
|
||||
batch.setResourceTexture(0, *_currentTexture);
|
||||
}
|
||||
batch.setPipeline(_simplePipeline);
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
});
|
||||
|
||||
auto frame = gpuContext->endFrame();
|
||||
frame->framebuffer = framebuffer;
|
||||
frame->framebufferRecycler = [](const gpu::FramebufferPointer& framebuffer){
|
||||
DependencyManager::get<FramebufferCache>()->releaseFramebuffer(framebuffer);
|
||||
};
|
||||
_renderThread.submitFrame(frame);
|
||||
if (!_renderThread.isThreaded()) {
|
||||
_renderThread.process();
|
||||
}
|
||||
}
|
||||
|
||||
void resizeWindow(const QSize& size) {
|
||||
_size = size;
|
||||
if (!_ready) {
|
||||
return;
|
||||
}
|
||||
_renderThread._size = size;
|
||||
}
|
||||
|
||||
private:
|
||||
QSize _size;
|
||||
std::atomic<size_t> _renderCount;
|
||||
gl::OffscreenContext _initContext;
|
||||
RenderThread _renderThread;
|
||||
ViewFrustum _viewFrustum; // current state of view frustum, perspective, orientation, etc.
|
||||
bool _ready { false };
|
||||
};
|
||||
|
||||
void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) {
|
||||
if (!message.isEmpty()) {
|
||||
#ifdef Q_OS_WIN
|
||||
OutputDebugStringA(message.toLocal8Bit().constData());
|
||||
OutputDebugStringA("\n");
|
||||
#endif
|
||||
std::cout << message.toLocal8Bit().constData() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
const char * LOG_FILTER_RULES = R"V0G0N(
|
||||
hifi.gpu=true
|
||||
)V0G0N";
|
||||
|
||||
void unzipTestData(const QByteArray& zipData) {
|
||||
QTemporaryFile zipFile;
|
||||
if (zipFile.open()) {
|
||||
zipFile.write(zipData);
|
||||
zipFile.close();
|
||||
}
|
||||
qDebug() << zipFile.fileName();
|
||||
if (!DATA_DIR.isValid()) {
|
||||
qFatal("Unable to create temp dir");
|
||||
}
|
||||
|
||||
//auto files = JlCompress::getFileList(zipData);
|
||||
auto files = JlCompress::extractDir(zipFile.fileName(), DATA_DIR.path());
|
||||
qDebug() << DATA_DIR.path();
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
QApplication app(argc, argv);
|
||||
QCoreApplication::setApplicationName("RenderPerf");
|
||||
QCoreApplication::setOrganizationName("High Fidelity");
|
||||
QCoreApplication::setOrganizationDomain("highfidelity.com");
|
||||
qInstallMessageHandler(messageHandler);
|
||||
QLoggingCategory::setFilterRules(LOG_FILTER_RULES);
|
||||
|
||||
|
||||
FileDownloader(DATA_SET, [&](const QByteArray& data) {
|
||||
qDebug() << "Fetched size " << data.size();
|
||||
unzipTestData(data);
|
||||
}).waitForDownload();
|
||||
|
||||
QTestWindow::setup();
|
||||
QTestWindow window;
|
||||
app.exec();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include "main.moc"
|
Loading…
Reference in a new issue