From b337e0ff6f3eab8df4fe6c82ef8a2b3e778236b9 Mon Sep 17 00:00:00 2001 From: rcoaxil Date: Tue, 18 Oct 2016 05:04:46 +0300 Subject: [PATCH 01/37] consistified seeking to invalid location behavior removed few redundant operations and checks moved sound start seeking from prepare to play fixed potential bug with sources that failed to play not releasing AL source back into the pool sources now stop if seeked to invalid location looped sources play from 0 if seeked to invalid location while playing --HG-- branch : minor --- src/modules/audio/openal/Source.cpp | 63 +++++++++++++++++++---------- 1 file changed, 42 insertions(+), 21 deletions(-) diff --git a/src/modules/audio/openal/Source.cpp b/src/modules/audio/openal/Source.cpp index 1de16979e..55148b78e 100644 --- a/src/modules/audio/openal/Source.cpp +++ b/src/modules/audio/openal/Source.cpp @@ -280,9 +280,6 @@ bool Source::update() switch (type) { case TYPE_STATIC: - // Looping mode could have changed. - // FIXME: make looping mode not change without you noticing so that this is not needed - alSourcei(source, AL_LOOPING, isLooping() ? AL_TRUE : AL_FALSE); return !isFinished(); case TYPE_STREAM: if (!isFinished()) @@ -401,24 +398,26 @@ void Source::seekAtomic(float offset, void *unit) break; } + bool wasPlaying = isPlaying(); switch (type) { case TYPE_STATIC: - alSourcef(source, AL_SAMPLE_OFFSET, offsetSamples); - offsetSamples = offsetSeconds = 0; + if (valid) + { + alSourcef(source, AL_SAMPLE_OFFSET, offsetSamples); + offsetSamples = offsetSeconds = 0; + } break; case TYPE_STREAM: { - bool wasPlaying = isPlaying(); - // To drain all buffers if (valid) - stopAtomic(); + stop(); decoder->seek(offsetSeconds); if (wasPlaying) - playAtomic(source); + play(); break; } @@ -454,6 +453,13 @@ void Source::seekAtomic(float offset, void *unit) case TYPE_MAX_ENUM: break; } + if (wasPlaying && (alGetError() == AL_INVALID_VALUE || (type == TYPE_STREAM && !isPlaying()))) + { + stop(); + if (isLooping()) + play(); + return; + } this->offsetSamples = offsetSamples; this->offsetSeconds = offsetSeconds; } @@ -734,9 +740,6 @@ void Source::prepareAtomic() { case TYPE_STATIC: alSourcei(source, AL_BUFFER, staticBuffer->getBuffer()); - //source can be seeked while not valid - if (offsetSamples >= 0) - alSourcef(source, AL_SAMPLE_OFFSET, offsetSamples); break; case TYPE_STREAM: while (unusedBufferPeek() != AL_NONE) @@ -760,8 +763,6 @@ void Source::prepareAtomic() for (unsigned int i = top + 1; i < MAX_BUFFERS; i++) unusedBufferPush(unusedBuffers[i]); - if (offsetSamples >= 0) - alSourcef(source, AL_SAMPLE_OFFSET, offsetSamples); break; } case TYPE_MAX_ENUM: @@ -774,7 +775,6 @@ void Source::teardownAtomic() switch (type) { case TYPE_STATIC: - alSourcef(source, AL_SAMPLE_OFFSET, 0); break; case TYPE_STREAM: { @@ -832,16 +832,32 @@ bool Source::playAtomic(ALuint source) alSourcePlay(source); - // alSourcePlay may fail if the system has reached its limit of simultaneous - // playing sources. bool success = alGetError() == AL_NO_ERROR; - valid = true; //if it fails it will be set to false again - //but this prevents a horrible, horrible bug + if (type == TYPE_STREAM) + { + valid = true; //isPlaying() needs source to be valid + if (!isPlaying()) + success = false; + } + else if (success) + { + alSourcef(source, AL_SAMPLE_OFFSET, offsetSamples); + success = alGetError() == AL_NO_ERROR; + } - if (type != TYPE_STREAM) + if (!success) + { + valid = true; //stop() needs source to be valid + stop(); + } + else if (type != TYPE_STREAM) offsetSamples = offsetSeconds = 0; + //this is set to success state afterwards anyway, but setting it here + //to true preemptively avoids race condition with update bug + valid = true; + return success; } @@ -862,7 +878,12 @@ void Source::pauseAtomic() void Source::resumeAtomic() { if (valid && !isPlaying()) + { alSourcePlay(source); + + if (alGetError() == AL_INVALID_VALUE || (type == TYPE_STREAM && unusedBufferTop == MAX_BUFFERS - 1)) + stop(); + } } bool Source::playAtomic(const std::vector &sources, const std::vector &ids, const std::vector &wasPlaying) @@ -941,7 +962,7 @@ void Source::pauseAtomic(const std::vector &sources) void Source::reset() { - alSourcei(source, AL_BUFFER, 0); + alSourcei(source, AL_BUFFER, AL_NONE); alSourcefv(source, AL_POSITION, position); alSourcefv(source, AL_VELOCITY, velocity); alSourcefv(source, AL_DIRECTION, direction); From 9bed2ac33a3c5d6dfa0484ebaf07fe9c5728a15e Mon Sep 17 00:00:00 2001 From: rcoaxil Date: Tue, 18 Oct 2016 05:33:34 +0300 Subject: [PATCH 02/37] moved back update cycle constant looping override for static sources --HG-- branch : minor --- src/libraries/stb/stb_image.h | 5 +++++ src/modules/audio/openal/Source.cpp | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/src/libraries/stb/stb_image.h b/src/libraries/stb/stb_image.h index fd4c872bc..93600d783 100644 --- a/src/libraries/stb/stb_image.h +++ b/src/libraries/stb/stb_image.h @@ -712,9 +712,14 @@ static int stbi__sse2_available() static int stbi__sse2_available() { +/* #if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 408 // GCC 4.8 or later // GCC 4.8+ has a nice way to do this return __builtin_cpu_supports("sse2"); +*/ +#if defined(STBI__X64_TARGET) + // GCC 5 has this function buggy for a moment, but an x64 CPU can be assumed to have SSE2 anyway + return 1; #else // portable way to do this, preferably without using GCC inline ASM? // just bail for now. diff --git a/src/modules/audio/openal/Source.cpp b/src/modules/audio/openal/Source.cpp index 55148b78e..e17f80b80 100644 --- a/src/modules/audio/openal/Source.cpp +++ b/src/modules/audio/openal/Source.cpp @@ -280,7 +280,12 @@ bool Source::update() switch (type) { case TYPE_STATIC: + { + // Looping mode could have changed. +- // FIXME: make looping mode change atomically so this is not needed + alSourcei(source, AL_LOOPING, isLooping() ? AL_TRUE : AL_FALSE); return !isFinished(); + } case TYPE_STREAM: if (!isFinished()) { From 329e10d35b06fd8a0ecc78dd99de960f35dfd6c6 Mon Sep 17 00:00:00 2001 From: rcoaxil Date: Tue, 18 Oct 2016 05:36:28 +0300 Subject: [PATCH 03/37] stb hack revert --HG-- branch : minor --- src/libraries/stb/stb_image.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/libraries/stb/stb_image.h b/src/libraries/stb/stb_image.h index 93600d783..fd4c872bc 100644 --- a/src/libraries/stb/stb_image.h +++ b/src/libraries/stb/stb_image.h @@ -712,14 +712,9 @@ static int stbi__sse2_available() static int stbi__sse2_available() { -/* #if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 408 // GCC 4.8 or later // GCC 4.8+ has a nice way to do this return __builtin_cpu_supports("sse2"); -*/ -#if defined(STBI__X64_TARGET) - // GCC 5 has this function buggy for a moment, but an x64 CPU can be assumed to have SSE2 anyway - return 1; #else // portable way to do this, preferably without using GCC inline ASM? // just bail for now. From 3c413a293b7edea05a200c863b253d9f7592bc50 Mon Sep 17 00:00:00 2001 From: rcoaxil Date: Wed, 19 Oct 2016 05:13:30 +0300 Subject: [PATCH 04/37] stray minus sign --HG-- branch : minor --- src/modules/audio/openal/Source.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/audio/openal/Source.cpp b/src/modules/audio/openal/Source.cpp index e17f80b80..093b0fd0c 100644 --- a/src/modules/audio/openal/Source.cpp +++ b/src/modules/audio/openal/Source.cpp @@ -282,7 +282,7 @@ bool Source::update() case TYPE_STATIC: { // Looping mode could have changed. -- // FIXME: make looping mode change atomically so this is not needed + // FIXME: make looping mode change atomically so this is not needed alSourcei(source, AL_LOOPING, isLooping() ? AL_TRUE : AL_FALSE); return !isFinished(); } From e5be49243101e357bf1216169427824ccd0de21e Mon Sep 17 00:00:00 2001 From: Raidho Date: Wed, 19 Oct 2016 02:33:03 +0000 Subject: [PATCH 05/37] Created new branch minor-mic-input --HG-- branch : minor-mic-input From 87f0b2811efa8577cafeeb7a34be8a557b8adfd1 Mon Sep 17 00:00:00 2001 From: Raidho Date: Sun, 23 Oct 2016 17:56:52 +0300 Subject: [PATCH 06/37] Microphone input implemented. RecordingDevice class added (number)love.audio.getRecordingDeviceCount() and (table)love.audio.getRecordingDevices() exposed (bool):startRecording([samples, sampleRate, bitDepth, channels], (SoundData):stopRecording([SoundData]), (SoundData):getData([SoundData]), (number):getSampleCount(), (number):getSampleRate(), (number):getBitDepth(), (number):getChannels(), (string):getName(), (number):getID(), (bool):isRecording() member functions exposed previously existed stub implementation removed getFormat moved to love::audio::openal::Audio getFormat arguments order swapped to (bitDepth, channels) getFormat now returns AL_NONE if format is invalid --HG-- branch : minor-mic-input --- CMakeLists.txt | 8 + src/common/types.cpp | 1 + src/common/types.h | 1 + src/modules/audio/Audio.h | 29 +-- src/modules/audio/RecordingDevice.cpp | 37 ++++ src/modules/audio/RecordingDevice.h | 107 ++++++++++ src/modules/audio/null/Audio.cpp | 16 +- src/modules/audio/null/Audio.h | 8 +- src/modules/audio/null/RecordingDevice.cpp | 97 +++++++++ src/modules/audio/null/RecordingDevice.h | 59 ++++++ src/modules/audio/openal/Audio.cpp | 125 ++++++------ src/modules/audio/openal/Audio.h | 21 +- src/modules/audio/openal/RecordingDevice.cpp | 156 ++++++++++++++ src/modules/audio/openal/RecordingDevice.h | 78 +++++++ src/modules/audio/openal/Source.cpp | 47 +---- src/modules/audio/openal/Source.h | 9 - src/modules/audio/wrap_Audio.cpp | 70 +++---- src/modules/audio/wrap_Audio.h | 1 + src/modules/audio/wrap_RecordingDevice.cpp | 201 +++++++++++++++++++ src/modules/audio/wrap_RecordingDevice.h | 40 ++++ src/modules/sound/SoundData.h | 3 +- 21 files changed, 922 insertions(+), 192 deletions(-) create mode 100644 src/modules/audio/RecordingDevice.cpp create mode 100644 src/modules/audio/RecordingDevice.h create mode 100644 src/modules/audio/null/RecordingDevice.cpp create mode 100644 src/modules/audio/null/RecordingDevice.h create mode 100644 src/modules/audio/openal/RecordingDevice.cpp create mode 100644 src/modules/audio/openal/RecordingDevice.h create mode 100644 src/modules/audio/wrap_RecordingDevice.cpp create mode 100644 src/modules/audio/wrap_RecordingDevice.h diff --git a/CMakeLists.txt b/CMakeLists.txt index a74fd7056..27fc045bf 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -312,10 +312,14 @@ set(LOVE_SRC_MODULE_AUDIO_ROOT src/modules/audio/Audio.h src/modules/audio/Source.cpp src/modules/audio/Source.h + src/modules/audio/RecordingDevice.cpp + src/modules/audio/RecordingDevice.h src/modules/audio/wrap_Audio.cpp src/modules/audio/wrap_Audio.h src/modules/audio/wrap_Source.cpp src/modules/audio/wrap_Source.h + src/modules/audio/wrap_RecordingDevice.cpp + src/modules/audio/wrap_RecordingDevice.h ) set(LOVE_SRC_MODULE_AUDIO_NULL @@ -323,6 +327,8 @@ set(LOVE_SRC_MODULE_AUDIO_NULL src/modules/audio/null/Audio.h src/modules/audio/null/Source.cpp src/modules/audio/null/Source.h + src/modules/audio/null/RecordingDevice.cpp + src/modules/audio/null/RecordingDevice.h ) set(LOVE_SRC_MODULE_AUDIO_OPENAL @@ -332,6 +338,8 @@ set(LOVE_SRC_MODULE_AUDIO_OPENAL src/modules/audio/openal/Pool.h src/modules/audio/openal/Source.cpp src/modules/audio/openal/Source.h + src/modules/audio/openal/RecordingDevice.cpp + src/modules/audio/openal/RecordingDevice.h ) set(LOVE_SRC_MODULE_AUDIO diff --git a/src/common/types.cpp b/src/common/types.cpp index 4bb44ecce..2385315c9 100644 --- a/src/common/types.cpp +++ b/src/common/types.cpp @@ -72,6 +72,7 @@ static const TypeBits *createTypeFlags() // Audio. b[AUDIO_SOURCE_ID] = (one << AUDIO_SOURCE_ID) | b[OBJECT_ID]; + b[AUDIO_RECORDING_DEVICE_ID] = (one << AUDIO_RECORDING_DEVICE_ID) | b[OBJECT_ID]; // Sound. b[SOUND_SOUND_DATA_ID] = (one << SOUND_SOUND_DATA_ID) | b[DATA_ID]; diff --git a/src/common/types.h b/src/common/types.h index a18191947..7b7d1e1ee 100644 --- a/src/common/types.h +++ b/src/common/types.h @@ -73,6 +73,7 @@ enum Type // Audio AUDIO_SOURCE_ID, + AUDIO_RECORDING_DEVICE_ID, // Sound SOUND_SOUND_DATA_ID, diff --git a/src/modules/audio/Audio.h b/src/modules/audio/Audio.h index 0a114a8ad..9a0d43aba 100644 --- a/src/modules/audio/Audio.h +++ b/src/modules/audio/Audio.h @@ -28,6 +28,7 @@ #include "common/Module.h" #include "common/StringMap.h" #include "Source.h" +#include "RecordingDevice.h" namespace love { @@ -189,34 +190,20 @@ public: virtual float getDopplerScale() const = 0; /** - * Begins recording audio input from the microphone. + * @return Number of recording devices. **/ - virtual void record() = 0; + virtual int getRecordingDeviceCount() const = 0; /** - * Gets a section of recorded audio. - * Per OpenAL, the measurement begins from the start of the - * audio data in memory, which is after the last time this function - * was called. If this function has not been called yet this recording - * session, it just grabs from the beginning. - * @return All the recorded SoundData thus far. + * @param index Index number of recording device. 0 is default device. + * @return Selected recording device. **/ - virtual love::sound::SoundData *getRecordedData() = 0; + virtual RecordingDevice *getRecordingDevice(int index) const = 0; /** - * Stops recording and, if passed true, returns all the recorded audio - * not already gotten by getRecordedData. - * @param returnData Whether to return recorded audio. - * @return if returnData, all the recorded audio yet to be gotten, - * otherwise NULL. + * @return Index number of a recording device, -1 if not present. **/ - virtual love::sound::SoundData *stopRecording(bool returnData) = 0; - - /** - * Checks whether LOVE is able to record audio input. - * @return hasMic Whether LOVE has a microphone enabled. - **/ - virtual bool canRecord() = 0; + virtual int getRecordingDeviceIndex(RecordingDevice *device) const = 0; /** * Gets the distance model used for attenuation. diff --git a/src/modules/audio/RecordingDevice.cpp b/src/modules/audio/RecordingDevice.cpp new file mode 100644 index 000000000..f03470218 --- /dev/null +++ b/src/modules/audio/RecordingDevice.cpp @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2006-2016 LOVE Development Team + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + **/ + +#include "RecordingDevice.h" + +namespace love +{ +namespace audio +{ + +RecordingDevice::RecordingDevice() +{ +} + +RecordingDevice::~RecordingDevice() +{ +} + +} //audio +} //love diff --git a/src/modules/audio/RecordingDevice.h b/src/modules/audio/RecordingDevice.h new file mode 100644 index 000000000..ac2d9150c --- /dev/null +++ b/src/modules/audio/RecordingDevice.h @@ -0,0 +1,107 @@ +/** + * Copyright (c) 2006-2016 LOVE Development Team + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented = 0; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + **/ + +#ifndef LOVE_AUDIO_RECORDING_DEVICE_H +#define LOVE_AUDIO_RECORDING_DEVICE_H + +#include "common/Object.h" +#include "sound/SoundData.h" + +#include + +namespace love +{ +namespace audio +{ + +class RecordingDevice : public love::Object +{ +public: + RecordingDevice(); + virtual ~RecordingDevice(); + + /** + * Begins audio input recording process. using default (previous) parameters. + * @return True if recording started successfully. + **/ + virtual bool startRecording() = 0; + + /** + * Begins audio input recording process. + * @param samples Number of samples to buffer. + * @param sampleRate Desired sample rate. + * @param bitDepth Desired bit depth (8 or 16). + * @param channels Desired number of channels. + * @return True if recording started successfully. + **/ + virtual bool startRecording(int samples, int sampleRate, int bitDepth, int channels) = 0; + + /** + * Stops audio input recording. + **/ + virtual void stopRecording() = 0; + + /** + * Retreives recorded data. + * @param soundData Reference to a SoundData to fill. + * @return number of samples obtained from device. + **/ + virtual int getData(love::sound::SoundData *soundData) = 0; + + /** + * @return C string device name. + **/ + virtual const char *getName() const = 0; + + /** + * @return Unique ID number. + **/ + virtual int getID() const = 0; + + /** + * @return Number of samples currently recorded. + **/ + virtual int getSampleCount() const = 0; + + /** + * @return Sample rate for recording. + **/ + virtual int getSampleRate() const = 0; + + /** + * @return Bit depth for recording. + **/ + virtual int getBitDepth() const = 0; + + /** + * @return Number of channels for recording. + **/ + virtual int getChannels() const = 0; + + /** + * @return True if currently recording. + **/ + virtual bool isRecording() const = 0; +}; //RecordingDevice + +} //audio +} //love + +#endif //LOVE_AUDIO_RECORDING_DEVICE_H diff --git a/src/modules/audio/null/Audio.cpp b/src/modules/audio/null/Audio.cpp index b2a0ffe4a..85d48c629 100644 --- a/src/modules/audio/null/Audio.cpp +++ b/src/modules/audio/null/Audio.cpp @@ -144,23 +144,19 @@ float Audio::getDopplerScale() const return 1.0f; } -void Audio::record() +int Audio::getRecordingDeviceCount() const { + return 0; } -love::sound::SoundData *Audio::getRecordedData() +love::audio::RecordingDevice *Audio::getRecordingDevice(int index) const { - return NULL; + return nullptr; } -love::sound::SoundData *Audio::stopRecording(bool) +int Audio::getRecordingDeviceIndex(love::audio::RecordingDevice *device) const { - return NULL; -} - -bool Audio::canRecord() -{ - return false; + return -1; } Audio::DistanceModel Audio::getDistanceModel() const diff --git a/src/modules/audio/null/Audio.h b/src/modules/audio/null/Audio.h index e4143d447..a2c82e031 100644 --- a/src/modules/audio/null/Audio.h +++ b/src/modules/audio/null/Audio.h @@ -24,6 +24,7 @@ // LOVE #include "audio/Audio.h" +#include "RecordingDevice.h" #include "Source.h" namespace love @@ -70,10 +71,9 @@ public: void setDopplerScale(float scale); float getDopplerScale() const; - void record(); - love::sound::SoundData *getRecordedData(); - love::sound::SoundData *stopRecording(bool returnData); - bool canRecord(); + int getRecordingDeviceCount() const; + love::audio::RecordingDevice *getRecordingDevice(int index) const; + int getRecordingDeviceIndex(love::audio::RecordingDevice *device) const; DistanceModel getDistanceModel() const; void setDistanceModel(DistanceModel distanceModel); diff --git a/src/modules/audio/null/RecordingDevice.cpp b/src/modules/audio/null/RecordingDevice.cpp new file mode 100644 index 000000000..9e62b4168 --- /dev/null +++ b/src/modules/audio/null/RecordingDevice.cpp @@ -0,0 +1,97 @@ +/** + * Copyright (c) 2006-2016 LOVE Development Team + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented = 0; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + **/ + +#include "RecordingDevice.h" +#include "Audio.h" + +namespace love +{ +namespace audio +{ +namespace null +{ + +const char *RecordingDevice::name = "null"; + +RecordingDevice::RecordingDevice(const char *, int) +{ +} + +RecordingDevice::~RecordingDevice() +{ +} + +bool RecordingDevice::startRecording() +{ + return false; +} + +bool RecordingDevice::startRecording(int, int, int, int) +{ + return false; +} + +void RecordingDevice::stopRecording() +{ +} + +int RecordingDevice::getData(love::sound::SoundData*) +{ + return 0; +} + +int RecordingDevice::getSampleCount() const +{ + return 0; +} + +int RecordingDevice::getSampleRate() const +{ + return 8000; +} + +int RecordingDevice::getBitDepth() const +{ + return 16; +} + +int RecordingDevice::getChannels() const +{ + return 1; +} + +const char *RecordingDevice::getName() const +{ + return name; +} + +int RecordingDevice::getID() const +{ + return 0; +} + +bool RecordingDevice::isRecording() const +{ + return false; +} + +} //null +} //audio +} //love diff --git a/src/modules/audio/null/RecordingDevice.h b/src/modules/audio/null/RecordingDevice.h new file mode 100644 index 000000000..8fcb1bdad --- /dev/null +++ b/src/modules/audio/null/RecordingDevice.h @@ -0,0 +1,59 @@ +/** + * Copyright (c) 2006-2016 LOVE Development Team + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented = 0; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + **/ + +#ifndef LOVE_AUDIO_NULL_RECORDING_DEVICE_H +#define LOVE_AUDIO_NULL_RECORDING_DEVICE_H + +#include "audio/RecordingDevice.h" +#include "sound/SoundData.h" + +namespace love +{ +namespace audio +{ +namespace null +{ + +class RecordingDevice : public love::audio::RecordingDevice +{ +public: + RecordingDevice(const char *name, int id); + virtual ~RecordingDevice(); + virtual bool startRecording(); + virtual bool startRecording(int samples, int sampleRate, int bitDepth, int channels); + virtual void stopRecording(); + virtual int getData(love::sound::SoundData *soundData); + virtual const char *getName() const; + virtual int getID() const; + virtual int getSampleCount() const; + virtual int getSampleRate() const; + virtual int getBitDepth() const; + virtual int getChannels() const; + virtual bool isRecording() const; + +private: + static const char *name; +}; //RecordingDevice + +} //null +} //audio +} //love + +#endif //LOVE_AUDIO_NULL_RECORDING_DEVICE_H diff --git a/src/modules/audio/openal/Audio.cpp b/src/modules/audio/openal/Audio.cpp index c3b1bd05e..cb9b45f09 100644 --- a/src/modules/audio/openal/Audio.cpp +++ b/src/modules/audio/openal/Audio.cpp @@ -20,10 +20,11 @@ #include "Audio.h" #include "common/delay.h" - +#include "RecordingDevice.h" #include "sound/Decoder.h" #include +#include namespace love { @@ -67,9 +68,29 @@ void Audio::PoolThread::setFinish() finish = true; } +ALenum Audio::getFormat(int bitDepth, int channels) +{ + if (bitDepth != 8 && bitDepth != 16) + return AL_NONE; + + if (channels == 1) + return bitDepth == 8 ? AL_FORMAT_MONO8 : AL_FORMAT_MONO16; + else if (channels == 2) + return bitDepth == 8 ? AL_FORMAT_STEREO8 : AL_FORMAT_STEREO16; + #ifdef AL_EXT_MCFORMATS + else if (alIsExtensionPresent("AL_EXT_MCFORMATS")) + { + if (channels == 6) + return bitDepth == 8 ? AL_FORMAT_51CHN8 : AL_FORMAT_51CHN16; + else if (channels == 8) + return bitDepth == 8 ? AL_FORMAT_71CHN8 : AL_FORMAT_71CHN16; + } + #endif + return AL_NONE; +} + Audio::Audio() : device(nullptr) - , capture(nullptr) , context(nullptr) , pool(nullptr) , poolThread(nullptr) @@ -89,25 +110,28 @@ Audio::Audio() if (!alcMakeContextCurrent(context) || alcGetError(device) != ALC_NO_ERROR) throw love::Exception("Could not make context current."); - /*std::string captureName(alcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER)); - const ALCchar * devices = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER); - while (*devices) + //find and push default capture device + //AL may not return actual device name string when asked directly + std::string defaultname; + ALCdevice *defaultdevice = alcCaptureOpenDevice(NULL, 8000, 8, 1); + if (alGetError() == AL_NO_ERROR) { - std::string device(devices); - devices += device.size() + 1; - if (device.find("Mic") != std::string::npos || device.find("mic") != std::string::npos) - { - captureName = device; - } + defaultname = alcGetString(defaultdevice, ALC_CAPTURE_DEVICE_SPECIFIER); + alcCaptureCloseDevice(defaultdevice); + capture.push_back(new RecordingDevice(defaultname.c_str(), 0)); } - capture = alcCaptureOpenDevice(captureName.c_str(), 8000, AL_FORMAT_MONO16, 262144); // about 32 seconds - - if (!capture) + const ALCchar *devstr = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER); + size_t offset = 0; + while (true) { - // We're not going to prevent LOVE from running without a microphone, but we should warn, at least - std::cerr << "Warning, couldn't open capture device! No audio input!" << std::endl; - }*/ + if (devstr[offset] == '\0') + break; + std::string str((ALCchar*)&devstr[offset]); + if (str != defaultname) + capture.push_back(new RecordingDevice(str.c_str(), capture.size())); + offset += str.length() + 1; + } // pool must be allocated after AL context. try @@ -118,8 +142,10 @@ Audio::Audio() { alcMakeContextCurrent(nullptr); alcDestroyContext(context); - //if (capture) alcCaptureCloseDevice(capture); alcCloseDevice(device); + for (unsigned int i = 0; i < capture.size(); i++) + delete capture[i]; + throw; } @@ -137,8 +163,9 @@ Audio::~Audio() alcMakeContextCurrent(nullptr); alcDestroyContext(context); - //if (capture) alcCaptureCloseDevice(capture); alcCloseDevice(device); + for (unsigned int i = 0; i < capture.size(); i++) + delete capture[i]; } @@ -265,44 +292,6 @@ float Audio::getDopplerScale() const return alGetFloat(AL_DOPPLER_FACTOR); } -void Audio::record() -{ - if (!canRecord()) return; - alcCaptureStart(capture); -} - -love::sound::SoundData *Audio::getRecordedData() -{ - if (!canRecord()) - return NULL; - int samplerate = 8000; - ALCint samples; - alcGetIntegerv(capture, ALC_CAPTURE_SAMPLES, 4, &samples); - void *data = malloc(samples * (2/sizeof(char))); - alcCaptureSamples(capture, data, samples); - love::sound::SoundData *sd = new love::sound::SoundData(data, samples, samplerate, 16, 1); - free(data); - return sd; -} - -love::sound::SoundData *Audio::stopRecording(bool returnData) -{ - if (!canRecord()) - return NULL; - love::sound::SoundData *sd = NULL; - if (returnData) - { - sd = getRecordedData(); - } - alcCaptureStop(capture); - return sd; -} - -bool Audio::canRecord() -{ - return (capture != NULL); -} - Audio::DistanceModel Audio::getDistanceModel() const { return distanceModel; @@ -347,6 +336,28 @@ void Audio::setDistanceModel(DistanceModel distanceModel) } } +int Audio::getRecordingDeviceCount() const +{ + return capture.size(); +} + +love::audio::RecordingDevice *Audio::getRecordingDevice(int index) const +{ + if (index < 0 || (unsigned int)index >= capture.size()) + return nullptr; + + return capture[index]; +} + +int Audio::getRecordingDeviceIndex(love::audio::RecordingDevice *device) const +{ + for (unsigned int i = 0; i < capture.size(); i++) + if (device == capture[i]) + return i; + + return -1; +} + } // openal } // audio } // love diff --git a/src/modules/audio/openal/Audio.h b/src/modules/audio/openal/Audio.h index b676abf8c..846e0e54c 100644 --- a/src/modules/audio/openal/Audio.h +++ b/src/modules/audio/openal/Audio.h @@ -29,6 +29,7 @@ // LOVE #include "audio/Audio.h" +#include "audio/RecordingDevice.h" #include "common/config.h" #include "sound/SoundData.h" @@ -65,6 +66,15 @@ public: Audio(); ~Audio(); + /** + * Gets the OpenAL format identifier based on number of + * channels and bits. + * @param channels. + * @param bitDepth Either 8-bit samples, or 16-bit samples. + * @return One of AL_FORMAT_*, or AL_NONE if unsupported format. + **/ + static ALenum getFormat(int bitDepth, int channels); + // Implements Module. const char *getName() const; @@ -95,10 +105,9 @@ public: void setDopplerScale(float scale); float getDopplerScale() const; - void record(); - love::sound::SoundData *getRecordedData(); - love::sound::SoundData *stopRecording(bool returnData); - bool canRecord(); + int getRecordingDeviceCount() const; + love::audio::RecordingDevice *getRecordingDevice(int index) const; + int getRecordingDeviceIndex(love::audio::RecordingDevice *device) const; DistanceModel getDistanceModel() const; void setDistanceModel(DistanceModel distanceModel); @@ -108,8 +117,8 @@ private: // The OpenAL device. ALCdevice *device; - // The OpenAL capture device (microphone). - ALCdevice *capture; + // The OpenAL capture devices. + std::vector capture; // The OpenAL context. ALCcontext *context; diff --git a/src/modules/audio/openal/RecordingDevice.cpp b/src/modules/audio/openal/RecordingDevice.cpp new file mode 100644 index 000000000..afe06ef69 --- /dev/null +++ b/src/modules/audio/openal/RecordingDevice.cpp @@ -0,0 +1,156 @@ +/** + * Copyright (c) 2006-2016 LOVE Development Team + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented = 0; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + **/ + +#include "RecordingDevice.h" +#include "Audio.h" + +namespace love +{ +namespace audio +{ +namespace openal +{ + +class InvalidFormatException : public love::Exception +{ +public: + + InvalidFormatException(int channels, int bitdepth) + : Exception("Recording %d channels with %d bits per sample is not supported.", channels, bitdepth) + { + } + +}; + +RecordingDevice::RecordingDevice(const char *name, int id) + : name(name) + , id(id) +{ +} + +RecordingDevice::~RecordingDevice() +{ + if (!isRecording()) + return; + + alcCaptureStop(device); + alcCaptureCloseDevice(device); +} + +bool RecordingDevice::startRecording() +{ + return startRecording(samples, sampleRate, bitDepth, channels); +} + +bool RecordingDevice::startRecording(int samples, int sampleRate, int bitDepth, int channels) +{ + if (isRecording()) + { + alcCaptureStop(device); + alcCaptureCloseDevice(device); + } + + ALenum format = Audio::getFormat(bitDepth, channels); + if (format == AL_NONE) + throw InvalidFormatException(channels, bitDepth); + + device = alcCaptureOpenDevice(name.c_str(), sampleRate, format, samples); + if (device == nullptr) + return false; + + alcCaptureStart(device); + this->samples = samples; + this->sampleRate = sampleRate; + this->bitDepth = bitDepth; + this->channels = channels; + return true; +} + +void RecordingDevice::stopRecording() +{ + if (!isRecording()) + return; + + alcCaptureStop(device); + alcCaptureCloseDevice(device); + device = nullptr; +} + +int RecordingDevice::getData(love::sound::SoundData *soundData) +{ + if (!isRecording()) + return 0; + + int samples = getSampleCount(); + if (samples == 0) + return 0; + + //resize internal buffer to proper size + if (samples != soundData->getSampleCount()) + soundData->load(samples, sampleRate, bitDepth, channels); + + alcCaptureSamples(device, soundData->getData(), samples); + + return samples; +} + +int RecordingDevice::getSampleCount() const +{ + if (!isRecording()) + return 0; + + ALCint samples; + alcGetIntegerv(device, ALC_CAPTURE_SAMPLES, sizeof(ALCint), &samples); + return (int)samples; +} + +int RecordingDevice::getSampleRate() const +{ + return sampleRate; +} + +int RecordingDevice::getBitDepth() const +{ + return bitDepth; +} + +int RecordingDevice::getChannels() const +{ + return channels; +} + +const char *RecordingDevice::getName() const +{ + return name.c_str(); +} + +int RecordingDevice::getID() const +{ + return id; +} + +bool RecordingDevice::isRecording() const +{ + return device == nullptr ? false : true; +} + +} //openal +} //audio +} //love diff --git a/src/modules/audio/openal/RecordingDevice.h b/src/modules/audio/openal/RecordingDevice.h new file mode 100644 index 000000000..3e4ae14ca --- /dev/null +++ b/src/modules/audio/openal/RecordingDevice.h @@ -0,0 +1,78 @@ +/** + * Copyright (c) 2006-2016 LOVE Development Team + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented = 0; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + **/ + +#ifndef LOVE_AUDIO_OPENAL_RECORDING_DEVICE_H +#define LOVE_AUDIO_OPENAL_RECORDING_DEVICE_H + +#ifdef LOVE_APPLE_USE_FRAMEWORKS +#ifdef LOVE_IOS +#include +#include +#else +#include +#include +#endif +#else +#include +#include +#endif + +#include "audio/RecordingDevice.h" +#include "sound/SoundData.h" + +namespace love +{ +namespace audio +{ +namespace openal +{ + +class RecordingDevice : public love::audio::RecordingDevice +{ +public: + RecordingDevice(const char *name, int id); + virtual ~RecordingDevice(); + virtual bool startRecording(); + virtual bool startRecording(int samples, int sampleRate, int bitDepth, int channels); + virtual void stopRecording(); + virtual int getData(love::sound::SoundData *soundData); + virtual const char *getName() const; + virtual int getID() const; + virtual int getSampleCount() const; + virtual int getSampleRate() const; + virtual int getBitDepth() const; + virtual int getChannels() const; + virtual bool isRecording() const; + +private: + int samples = 8192; + int sampleRate = 8000; + int bitDepth = 16; + int channels = 1; + std::string name; + int id; + ALCdevice *device = nullptr; +}; //RecordingDevice + +} //openal +} //audio +} //love + +#endif //LOVE_AUDIO_OPENAL_RECORDING_DEVICE_H diff --git a/src/modules/audio/openal/Source.cpp b/src/modules/audio/openal/Source.cpp index 47169c583..8c981489d 100644 --- a/src/modules/audio/openal/Source.cpp +++ b/src/modules/audio/openal/Source.cpp @@ -20,6 +20,7 @@ #include "Source.h" #include "Pool.h" +#include "Audio.h" #include "common/math.h" // STD @@ -119,8 +120,8 @@ Source::Source(Pool *pool, love::sound::SoundData *soundData) , channels(soundData->getChannels()) , bitDepth(soundData->getBitDepth()) { - ALenum fmt = getFormat(soundData->getChannels(), soundData->getBitDepth()); - if (fmt == 0) + ALenum fmt = Audio::getFormat(soundData->getBitDepth(), soundData->getChannels()); + if (fmt == AL_NONE) throw InvalidFormatException(soundData->getChannels(), soundData->getBitDepth()); staticBuffer.set(new StaticDataBuffer(fmt, soundData->getData(), (ALsizei) soundData->getSize(), sampleRate), Acquire::NORETAIN); @@ -141,7 +142,7 @@ Source::Source(Pool *pool, love::sound::Decoder *decoder) , decoder(decoder) , unusedBufferTop(MAX_BUFFERS - 1) { - if (getFormat(decoder->getChannels(), decoder->getBitDepth()) == 0) + if (Audio::getFormat(decoder->getBitDepth(), decoder->getChannels()) == AL_NONE) throw InvalidFormatException(decoder->getChannels(), decoder->getBitDepth()); alGenBuffers(MAX_BUFFERS, streamBuffers); @@ -162,8 +163,8 @@ Source::Source(Pool *pool, int sampleRate, int bitDepth, int channels) , channels(channels) , bitDepth(bitDepth) { - ALenum fmt = getFormat(channels, bitDepth); - if (fmt == 0) + ALenum fmt = Audio::getFormat(bitDepth, channels); + if (fmt == AL_NONE) throw InvalidFormatException(channels, bitDepth); alGenBuffers(MAX_BUFFERS, streamBuffers); @@ -688,7 +689,7 @@ bool Source::queueAtomic(void *data, ALsizei length) if (buffer == AL_NONE) return false; - alBufferData(buffer, getFormat(channels, bitDepth), data, length, sampleRate); + alBufferData(buffer, Audio::getFormat(bitDepth, channels), data, length, sampleRate); alSourceQueueBuffers(source, 1, &buffer); unusedBufferPop(); } @@ -699,7 +700,7 @@ bool Source::queueAtomic(void *data, ALsizei length) return false; //stack acts as queue while stopped - alBufferData(buffer, getFormat(channels, bitDepth), data, length, sampleRate); + alBufferData(buffer, Audio::getFormat(bitDepth, channels), data, length, sampleRate); unusedBufferQueue(buffer); } bufferedBytes += length; @@ -966,34 +967,6 @@ void Source::setFloatv(float *dst, const float *src) const dst[2] = src[2]; } -ALenum Source::getFormat(int channels, int bitDepth) const -{ - if (channels == 1 && bitDepth == 8) - return AL_FORMAT_MONO8; - else if (channels == 1 && bitDepth == 16) - return AL_FORMAT_MONO16; - else if (channels == 2 && bitDepth == 8) - return AL_FORMAT_STEREO8; - else if (channels == 2 && bitDepth == 16) - return AL_FORMAT_STEREO16; - -#ifdef AL_EXT_MCFORMATS - if (alIsExtensionPresent("AL_EXT_MCFORMATS")) - { - if (channels == 6 && bitDepth == 8) - return AL_FORMAT_51CHN8; - else if (channels == 6 && bitDepth == 16) - return AL_FORMAT_51CHN16; - else if (channels == 8 && bitDepth == 8) - return AL_FORMAT_71CHN8; - else if (channels == 8 && bitDepth == 16) - return AL_FORMAT_71CHN16; - } -#endif - - return 0; -} - ALuint Source::unusedBufferPeek() { return (unusedBufferTop < 0) ? AL_NONE : unusedBuffers[unusedBufferTop]; @@ -1029,9 +1002,9 @@ int Source::streamAtomic(ALuint buffer, love::sound::Decoder *d) // OpenAL implementations are allowed to ignore 0-size alBufferData calls. if (decoded > 0) { - int fmt = getFormat(d->getChannels(), d->getBitDepth()); + int fmt = Audio::getFormat(d->getBitDepth(), d->getChannels()); - if (fmt != 0) + if (fmt != AL_NONE) alBufferData(buffer, fmt, d->getBuffer(), decoded, d->getSampleRate()); else decoded = 0; diff --git a/src/modules/audio/openal/Source.h b/src/modules/audio/openal/Source.h index 9bddfb73d..680cf5ddb 100644 --- a/src/modules/audio/openal/Source.h +++ b/src/modules/audio/openal/Source.h @@ -163,15 +163,6 @@ private: void setFloatv(float *dst, const float *src) const; - /** - * Gets the OpenAL format identifier based on number of - * channels and bits. - * @param channels Either 1 (mono) or 2 (stereo). - * @param bitDepth Either 8-bit samples, or 16-bit samples. - * @return One of AL_FORMAT_*, or 0 if unsupported format. - **/ - ALenum getFormat(int channels, int bitDepth) const; - int streamAtomic(ALuint buffer, love::sound::Decoder *d); ALuint unusedBufferPeek(); diff --git a/src/modules/audio/wrap_Audio.cpp b/src/modules/audio/wrap_Audio.cpp index 7a178abc8..90ee82a82 100644 --- a/src/modules/audio/wrap_Audio.cpp +++ b/src/modules/audio/wrap_Audio.cpp @@ -257,49 +257,6 @@ int w_getDopplerScale(lua_State *L) return 1; } -int w_record(lua_State *) -{ - instance()->record(); - return 0; -} - -int w_getRecordedData(lua_State *L) -{ - love::sound::SoundData *sd = instance()->getRecordedData(); - if (!sd) - lua_pushnil(L); - else - { - luax_pushtype(L, SOUND_SOUND_DATA_ID, sd); - sd->release(); - } - return 1; -} - -int w_stopRecording(lua_State *L) -{ - if (luax_optboolean(L, 1, true)) - { - love::sound::SoundData *sd = instance()->stopRecording(true); - if (!sd) - lua_pushnil(L); - else - { - luax_pushtype(L, SOUND_SOUND_DATA_ID, sd); - sd->release(); - } - return 1; - } - instance()->stopRecording(false); - return 0; -} - -int w_canRecord(lua_State *L) -{ - luax_pushboolean(L, instance()->canRecord()); - return 1; -} - int w_setDistanceModel(lua_State *L) { const char *modelStr = luaL_checkstring(L, 1); @@ -320,6 +277,27 @@ int w_getDistanceModel(lua_State *L) return 1; } +int w_getRecordingDeviceCount(lua_State *L) +{ + lua_pushnumber(L, instance()->getRecordingDeviceCount()); + return 1; +} + +int w_getRecordingDevices(lua_State *L) +{ + int count = instance()->getRecordingDeviceCount(); + lua_createtable(L, count, 0); + + for (int i = 0; i < count; i++) + { + RecordingDevice *device = instance()->getRecordingDevice(i); + luax_pushtype(L, AUDIO_RECORDING_DEVICE_ID, device); + lua_rawseti(L, -2, i + 1); + } + + return 1; +} + // List of functions to wrap. static const luaL_Reg functions[] = { @@ -339,17 +317,17 @@ static const luaL_Reg functions[] = { "getVelocity", w_getVelocity }, { "setDopplerScale", w_setDopplerScale }, { "getDopplerScale", w_getDopplerScale }, - /*{ "record", w_record }, - { "getRecordedData", w_getRecordedData }, - { "stopRecording", w_stopRecording },*/ { "setDistanceModel", w_setDistanceModel }, { "getDistanceModel", w_getDistanceModel }, + { "getRecordingDeviceCount", w_getRecordingDeviceCount }, + { "getRecordingDevices", w_getRecordingDevices }, { 0, 0 } }; static const lua_CFunction types[] = { luaopen_source, + luaopen_recordingdevice, 0 }; diff --git a/src/modules/audio/wrap_Audio.h b/src/modules/audio/wrap_Audio.h index 0f358517d..3990f29d6 100644 --- a/src/modules/audio/wrap_Audio.h +++ b/src/modules/audio/wrap_Audio.h @@ -26,6 +26,7 @@ #include "common/runtime.h" #include "Audio.h" #include "wrap_Source.h" +#include "wrap_RecordingDevice.h" namespace love { diff --git a/src/modules/audio/wrap_RecordingDevice.cpp b/src/modules/audio/wrap_RecordingDevice.cpp new file mode 100644 index 000000000..fbb5c3016 --- /dev/null +++ b/src/modules/audio/wrap_RecordingDevice.cpp @@ -0,0 +1,201 @@ +/** + * Copyright (c) 2006-2016 LOVE Development Team + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + **/ + +#include "wrap_RecordingDevice.h" +#include "wrap_Audio.h" + +#include "sound/SoundData.h" +#include "sound/Sound.h" + +#define soundInstance() (Module::getInstance(Module::M_SOUND)) + +namespace love +{ +namespace audio +{ + +RecordingDevice *luax_checkrecordingdevice(lua_State *L, int idx) +{ + return luax_checktype(L, idx, AUDIO_RECORDING_DEVICE_ID); +} + +int w_RecordingDevice_startRecording(lua_State *L) +{ + RecordingDevice *d = luax_checkrecordingdevice(L, 1); + if (lua_gettop(L) > 1) + { + int samples = luaL_checkinteger(L, 2); + int sampleRate = luaL_checkinteger(L, 3); + int bitDepth = luaL_checkinteger(L, 4); + int channels = luaL_checkinteger(L, 5); + luax_catchexcept(L, [&](){ + lua_pushboolean(L, d->startRecording(samples, sampleRate, bitDepth, channels)); + }); + } + else + luax_catchexcept(L, [&](){ + lua_pushboolean(L, d->startRecording()); + }); + return 1; +} + +int w_RecordingDevice_stopRecording(lua_State *L) +{ + RecordingDevice *d = luax_checkrecordingdevice(L, 1); + int samples = d->getSampleCount(); + if (samples == 0) + { + lua_pushnil(L); + return 1; + } + + love::sound::SoundData *s = nullptr; + if (lua_gettop(L) > 1) + { + if (luax_istype(L, 2, SOUND_SOUND_DATA_ID)) + s = luax_totype(L, 2, SOUND_SOUND_DATA_ID); + else + return luaL_typerror(L, 2, "SoundData"); + + s->retain(); + } + else + { + luax_catchexcept(L, [&](){ + s = soundInstance()->newSoundData(samples, d->getSampleRate(), d->getBitDepth(), d->getChannels()); + }); + } + + luax_catchexcept(L, [&](){ + d->getData(s); + d->stopRecording(); + }); + + luax_pushtype(L, SOUND_SOUND_DATA_ID, s); + s->release(); + return 1; +} + +int w_RecordingDevice_getData(lua_State *L) +{ + RecordingDevice *d = luax_checkrecordingdevice(L, 1); + int samples = d->getSampleCount(); + if (samples == 0) + { + lua_pushnil(L); + return 1; + } + + love::sound::SoundData *s = nullptr; + if (lua_gettop(L) > 1) + { + if (luax_istype(L, 2, SOUND_SOUND_DATA_ID)) + s = luax_totype(L, 2, SOUND_SOUND_DATA_ID); + else + return luaL_typerror(L, 2, "SoundData"); + + s->retain(); + } + else + { + luax_catchexcept(L, [&](){ + s = soundInstance()->newSoundData(samples, d->getSampleRate(), d->getBitDepth(), d->getChannels()); + }); + } + + luax_catchexcept(L, [&](){ d->getData(s); }); + + luax_pushtype(L, SOUND_SOUND_DATA_ID, s); + s->release(); + return 1; +} + +int w_RecordingDevice_getSampleCount(lua_State *L) +{ + RecordingDevice *d = luax_checkrecordingdevice(L, 1); + lua_pushnumber(L, d->getSampleCount()); + return 1; +} + +int w_RecordingDevice_getSampleRate(lua_State *L) +{ + RecordingDevice *d = luax_checkrecordingdevice(L, 1); + lua_pushnumber(L, d->getSampleRate()); + return 1; +} + +int w_RecordingDevice_getBitDepth(lua_State *L) +{ + RecordingDevice *d = luax_checkrecordingdevice(L, 1); + lua_pushnumber(L, d->getBitDepth()); + return 1; +} + +int w_RecordingDevice_getChannels(lua_State *L) +{ + RecordingDevice *d = luax_checkrecordingdevice(L, 1); + lua_pushnumber(L, d->getChannels()); + return 1; +} + +int w_RecordingDevice_getName(lua_State *L) +{ + RecordingDevice *d = luax_checkrecordingdevice(L, 1); + lua_pushstring(L, d->getName()); + return 1; +} + +int w_RecordingDevice_getID(lua_State *L) +{ + RecordingDevice *d = luax_checkrecordingdevice(L, 1); + lua_pushnumber(L, d->getID()); + return 1; +} + +int w_RecordingDevice_isRecording(lua_State *L) +{ + RecordingDevice *d = luax_checkrecordingdevice(L, 1); + lua_pushboolean(L, d->isRecording()); + return 1; +} + +static const luaL_Reg w_RecordingDevice_functions[] = +{ + { "startRecording", w_RecordingDevice_startRecording }, + { "stopRecording", w_RecordingDevice_stopRecording }, + { "getData", w_RecordingDevice_getData }, + { "getSampleCount", w_RecordingDevice_getSampleCount }, + { "getSampleRate", w_RecordingDevice_getSampleRate }, + { "getBitDepth", w_RecordingDevice_getBitDepth }, + { "getChannels", w_RecordingDevice_getChannels }, + { "getName", w_RecordingDevice_getName }, + { "getID", w_RecordingDevice_getID }, + { "isRecording", w_RecordingDevice_isRecording }, + { 0, 0 } +}; + +extern "C" int luaopen_recordingdevice(lua_State *L) +{ + int ret = luax_register_type(L, AUDIO_RECORDING_DEVICE_ID, "RecordingDevice", w_RecordingDevice_functions, nullptr); + return ret; +} + +} //audio +} //love diff --git a/src/modules/audio/wrap_RecordingDevice.h b/src/modules/audio/wrap_RecordingDevice.h new file mode 100644 index 000000000..d9dc9c2e4 --- /dev/null +++ b/src/modules/audio/wrap_RecordingDevice.h @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2006-2016 LOVE Development Team + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + **/ + +#ifndef LOVE_AUDIO_WRAP_RECORDING_DEVICE_H +#define LOVE_AUDIO_WRAP_RECORDING_DEVICE_H + +// LOVE +#include "common/runtime.h" +#include "RecordingDevice.h" + +namespace love +{ +namespace audio +{ + +RecordingDevice *luax_checkrecordingdevice(lua_State *L, int idx); +extern "C" int luaopen_recordingdevice(lua_State *L); + +} // audio +} // love + +#endif //LOVE_AUDIO_WRAP_RECORDING_DEVICE_H + diff --git a/src/modules/sound/SoundData.h b/src/modules/sound/SoundData.h index 07b13c2d3..821fb291e 100644 --- a/src/modules/sound/SoundData.h +++ b/src/modules/sound/SoundData.h @@ -54,11 +54,10 @@ public: void setSample(int i, float sample); float getSample(int i) const; + void load(int samples, int sampleRate, int bitDepth, int channels, void *newData = 0); private: - void load(int samples, int sampleRate, int bitDepth, int channels, void *newData = 0); - uint8 *data; size_t size; From 9a090e8df84e8b83474d02bc1db8ec2d8b75e88d Mon Sep 17 00:00:00 2001 From: Raidho Date: Sun, 23 Oct 2016 18:53:02 +0300 Subject: [PATCH 07/37] made RecordingDevice create new SoundData every time captured samples are aquired --HG-- branch : minor-mic-input --- src/modules/audio/openal/RecordingDevice.cpp | 5 +-- src/modules/audio/wrap_RecordingDevice.cpp | 38 +++----------------- 2 files changed, 8 insertions(+), 35 deletions(-) diff --git a/src/modules/audio/openal/RecordingDevice.cpp b/src/modules/audio/openal/RecordingDevice.cpp index afe06ef69..5eb77f648 100644 --- a/src/modules/audio/openal/RecordingDevice.cpp +++ b/src/modules/audio/openal/RecordingDevice.cpp @@ -102,8 +102,9 @@ int RecordingDevice::getData(love::sound::SoundData *soundData) if (samples == 0) return 0; - //resize internal buffer to proper size - if (samples != soundData->getSampleCount()) + //reinitialize soundData if necessary + if (samples != soundData->getSampleCount() || sampleRate != soundData->getSampleRate() || + bitDepth != soundData->getBitDepth() || channels != soundData->getChannels()) soundData->load(samples, sampleRate, bitDepth, channels); alcCaptureSamples(device, soundData->getData(), samples); diff --git a/src/modules/audio/wrap_RecordingDevice.cpp b/src/modules/audio/wrap_RecordingDevice.cpp index fbb5c3016..b78dc3bc1 100644 --- a/src/modules/audio/wrap_RecordingDevice.cpp +++ b/src/modules/audio/wrap_RecordingDevice.cpp @@ -67,23 +67,8 @@ int w_RecordingDevice_stopRecording(lua_State *L) } love::sound::SoundData *s = nullptr; - if (lua_gettop(L) > 1) - { - if (luax_istype(L, 2, SOUND_SOUND_DATA_ID)) - s = luax_totype(L, 2, SOUND_SOUND_DATA_ID); - else - return luaL_typerror(L, 2, "SoundData"); - - s->retain(); - } - else - { - luax_catchexcept(L, [&](){ - s = soundInstance()->newSoundData(samples, d->getSampleRate(), d->getBitDepth(), d->getChannels()); - }); - } - luax_catchexcept(L, [&](){ + s = soundInstance()->newSoundData(samples, d->getSampleRate(), d->getBitDepth(), d->getChannels()); d->getData(s); d->stopRecording(); }); @@ -104,23 +89,10 @@ int w_RecordingDevice_getData(lua_State *L) } love::sound::SoundData *s = nullptr; - if (lua_gettop(L) > 1) - { - if (luax_istype(L, 2, SOUND_SOUND_DATA_ID)) - s = luax_totype(L, 2, SOUND_SOUND_DATA_ID); - else - return luaL_typerror(L, 2, "SoundData"); - - s->retain(); - } - else - { - luax_catchexcept(L, [&](){ - s = soundInstance()->newSoundData(samples, d->getSampleRate(), d->getBitDepth(), d->getChannels()); - }); - } - - luax_catchexcept(L, [&](){ d->getData(s); }); + luax_catchexcept(L, [&](){ + s = soundInstance()->newSoundData(samples, d->getSampleRate(), d->getBitDepth(), d->getChannels()); + d->getData(s); + }); luax_pushtype(L, SOUND_SOUND_DATA_ID, s); s->release(); From 424d2704ec67664bda33770448a8a7b6f64c8cf0 Mon Sep 17 00:00:00 2001 From: Raidho Date: Sun, 23 Oct 2016 19:22:56 +0300 Subject: [PATCH 08/37] made RecordingDevice throw errors on invalid parameters --HG-- branch : minor-mic-input --- src/modules/audio/openal/RecordingDevice.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/modules/audio/openal/RecordingDevice.cpp b/src/modules/audio/openal/RecordingDevice.cpp index 5eb77f648..abae69571 100644 --- a/src/modules/audio/openal/RecordingDevice.cpp +++ b/src/modules/audio/openal/RecordingDevice.cpp @@ -71,6 +71,12 @@ bool RecordingDevice::startRecording(int samples, int sampleRate, int bitDepth, if (format == AL_NONE) throw InvalidFormatException(channels, bitDepth); + if (samples <= 0) + throw love::Exception("Invalid number of samples."); + + if (sampleRate <= 0) + throw love::Exception("Invalid sample rate."); + device = alcCaptureOpenDevice(name.c_str(), sampleRate, format, samples); if (device == nullptr) return false; From 3de68707d1f8cf9ab7ae84617429d5e5af2566c1 Mon Sep 17 00:00:00 2001 From: Raidho Date: Tue, 25 Oct 2016 02:05:53 +0300 Subject: [PATCH 09/37] made device enumeration lazy made enumerator to first attempt aquire default device name by standard means, then to fall back to read name from opened device getRecordingDeviceCount removed for being redundant --HG-- branch : minor-mic-input --- src/modules/audio/Audio.h | 15 +--- src/modules/audio/null/Audio.cpp | 14 +--- src/modules/audio/null/Audio.h | 5 +- src/modules/audio/openal/Audio.cpp | 72 +++++++++----------- src/modules/audio/openal/Audio.h | 4 +- src/modules/audio/openal/RecordingDevice.cpp | 2 +- src/modules/audio/wrap_Audio.cpp | 17 ++--- 7 files changed, 45 insertions(+), 84 deletions(-) diff --git a/src/modules/audio/Audio.h b/src/modules/audio/Audio.h index 9a0d43aba..c10ffe813 100644 --- a/src/modules/audio/Audio.h +++ b/src/modules/audio/Audio.h @@ -190,20 +190,9 @@ public: virtual float getDopplerScale() const = 0; /** - * @return Number of recording devices. + * @return Reference to a vector of pointers to recording devices. May be empty. **/ - virtual int getRecordingDeviceCount() const = 0; - - /** - * @param index Index number of recording device. 0 is default device. - * @return Selected recording device. - **/ - virtual RecordingDevice *getRecordingDevice(int index) const = 0; - - /** - * @return Index number of a recording device, -1 if not present. - **/ - virtual int getRecordingDeviceIndex(RecordingDevice *device) const = 0; + virtual std::vector *getRecordingDevices() = 0; /** * Gets the distance model used for attenuation. diff --git a/src/modules/audio/null/Audio.cpp b/src/modules/audio/null/Audio.cpp index 85d48c629..957129978 100644 --- a/src/modules/audio/null/Audio.cpp +++ b/src/modules/audio/null/Audio.cpp @@ -144,19 +144,9 @@ float Audio::getDopplerScale() const return 1.0f; } -int Audio::getRecordingDeviceCount() const +std::vector *Audio::getRecordingDevices() { - return 0; -} - -love::audio::RecordingDevice *Audio::getRecordingDevice(int index) const -{ - return nullptr; -} - -int Audio::getRecordingDeviceIndex(love::audio::RecordingDevice *device) const -{ - return -1; + return &capture; } Audio::DistanceModel Audio::getDistanceModel() const diff --git a/src/modules/audio/null/Audio.h b/src/modules/audio/null/Audio.h index a2c82e031..3f841fec5 100644 --- a/src/modules/audio/null/Audio.h +++ b/src/modules/audio/null/Audio.h @@ -71,9 +71,7 @@ public: void setDopplerScale(float scale); float getDopplerScale() const; - int getRecordingDeviceCount() const; - love::audio::RecordingDevice *getRecordingDevice(int index) const; - int getRecordingDeviceIndex(love::audio::RecordingDevice *device) const; + std::vector *getRecordingDevices(); DistanceModel getDistanceModel() const; void setDistanceModel(DistanceModel distanceModel); @@ -81,6 +79,7 @@ public: private: float volume; DistanceModel distanceModel; + std::vector capture; }; // Audio diff --git a/src/modules/audio/openal/Audio.cpp b/src/modules/audio/openal/Audio.cpp index cb9b45f09..6283b293b 100644 --- a/src/modules/audio/openal/Audio.cpp +++ b/src/modules/audio/openal/Audio.cpp @@ -110,29 +110,6 @@ Audio::Audio() if (!alcMakeContextCurrent(context) || alcGetError(device) != ALC_NO_ERROR) throw love::Exception("Could not make context current."); - //find and push default capture device - //AL may not return actual device name string when asked directly - std::string defaultname; - ALCdevice *defaultdevice = alcCaptureOpenDevice(NULL, 8000, 8, 1); - if (alGetError() == AL_NO_ERROR) - { - defaultname = alcGetString(defaultdevice, ALC_CAPTURE_DEVICE_SPECIFIER); - alcCaptureCloseDevice(defaultdevice); - capture.push_back(new RecordingDevice(defaultname.c_str(), 0)); - } - - const ALCchar *devstr = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER); - size_t offset = 0; - while (true) - { - if (devstr[offset] == '\0') - break; - std::string str((ALCchar*)&devstr[offset]); - if (str != defaultname) - capture.push_back(new RecordingDevice(str.c_str(), capture.size())); - offset += str.length() + 1; - } - // pool must be allocated after AL context. try { @@ -336,26 +313,41 @@ void Audio::setDistanceModel(DistanceModel distanceModel) } } -int Audio::getRecordingDeviceCount() const +std::vector *Audio::getRecordingDevices() { - return capture.size(); -} + if (capture.size() == 0) + { + std::string defaultname(alcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER)); -love::audio::RecordingDevice *Audio::getRecordingDevice(int index) const -{ - if (index < 0 || (unsigned int)index >= capture.size()) - return nullptr; + //no device name obtained from AL, fallback to reading from device + if (defaultname.length() == 0) + { + //use some safe basic parameters - 8 kHz, 8 bits, 1 channel + ALCdevice *defaultdevice = alcCaptureOpenDevice(NULL, 8000, 8, 1); + if (alGetError() == AL_NO_ERROR) + { + defaultname = alcGetString(defaultdevice, ALC_CAPTURE_DEVICE_SPECIFIER); + alcCaptureCloseDevice(defaultdevice); + } + else + //failed to open default recording device - bail, return empty list + return &capture; + } + capture.push_back(new RecordingDevice(defaultname.c_str(), 0)); - return capture[index]; -} - -int Audio::getRecordingDeviceIndex(love::audio::RecordingDevice *device) const -{ - for (unsigned int i = 0; i < capture.size(); i++) - if (device == capture[i]) - return i; - - return -1; + const ALCchar *devstr = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER); + size_t offset = 0; + while (true) + { + if (devstr[offset] == '\0') + break; + std::string str((ALCchar*)&devstr[offset]); + if (str != defaultname) + capture.push_back(new RecordingDevice(str.c_str(), capture.size())); + offset += str.length() + 1; + } + } + return &capture; } } // openal diff --git a/src/modules/audio/openal/Audio.h b/src/modules/audio/openal/Audio.h index 846e0e54c..469586d59 100644 --- a/src/modules/audio/openal/Audio.h +++ b/src/modules/audio/openal/Audio.h @@ -105,9 +105,7 @@ public: void setDopplerScale(float scale); float getDopplerScale() const; - int getRecordingDeviceCount() const; - love::audio::RecordingDevice *getRecordingDevice(int index) const; - int getRecordingDeviceIndex(love::audio::RecordingDevice *device) const; + std::vector *getRecordingDevices(); DistanceModel getDistanceModel() const; void setDistanceModel(DistanceModel distanceModel); diff --git a/src/modules/audio/openal/RecordingDevice.cpp b/src/modules/audio/openal/RecordingDevice.cpp index abae69571..b8d29f27c 100644 --- a/src/modules/audio/openal/RecordingDevice.cpp +++ b/src/modules/audio/openal/RecordingDevice.cpp @@ -155,7 +155,7 @@ int RecordingDevice::getID() const bool RecordingDevice::isRecording() const { - return device == nullptr ? false : true; + return device != nullptr; } } //openal diff --git a/src/modules/audio/wrap_Audio.cpp b/src/modules/audio/wrap_Audio.cpp index 90ee82a82..175f1d4af 100644 --- a/src/modules/audio/wrap_Audio.cpp +++ b/src/modules/audio/wrap_Audio.cpp @@ -277,21 +277,15 @@ int w_getDistanceModel(lua_State *L) return 1; } -int w_getRecordingDeviceCount(lua_State *L) -{ - lua_pushnumber(L, instance()->getRecordingDeviceCount()); - return 1; -} - int w_getRecordingDevices(lua_State *L) { - int count = instance()->getRecordingDeviceCount(); - lua_createtable(L, count, 0); + std::vector *devices = instance()->getRecordingDevices(); - for (int i = 0; i < count; i++) + lua_createtable(L, (*devices).size(), 0); + + for (unsigned int i = 0; i < (*devices).size(); i++) { - RecordingDevice *device = instance()->getRecordingDevice(i); - luax_pushtype(L, AUDIO_RECORDING_DEVICE_ID, device); + luax_pushtype(L, AUDIO_RECORDING_DEVICE_ID, (*devices)[i]); lua_rawseti(L, -2, i + 1); } @@ -319,7 +313,6 @@ static const luaL_Reg functions[] = { "getDopplerScale", w_getDopplerScale }, { "setDistanceModel", w_setDistanceModel }, { "getDistanceModel", w_getDistanceModel }, - { "getRecordingDeviceCount", w_getRecordingDeviceCount }, { "getRecordingDevices", w_getRecordingDevices }, { 0, 0 } }; From a99c7576529d484eb7ed7254a99f52f857c35d0b Mon Sep 17 00:00:00 2001 From: Raidho Date: Tue, 25 Oct 2016 02:39:39 +0300 Subject: [PATCH 10/37] made :getData produce SoundData directly moved SoundData::load back to private --HG-- branch : minor-mic-input --- src/modules/audio/RecordingDevice.h | 5 +-- src/modules/audio/null/RecordingDevice.cpp | 4 +- src/modules/audio/null/RecordingDevice.h | 2 +- src/modules/audio/openal/RecordingDevice.cpp | 16 +++---- src/modules/audio/openal/RecordingDevice.h | 2 +- src/modules/audio/wrap_RecordingDevice.cpp | 46 +++++++++----------- src/modules/sound/SoundData.h | 3 +- 7 files changed, 36 insertions(+), 42 deletions(-) diff --git a/src/modules/audio/RecordingDevice.h b/src/modules/audio/RecordingDevice.h index ac2d9150c..83af9580e 100644 --- a/src/modules/audio/RecordingDevice.h +++ b/src/modules/audio/RecordingDevice.h @@ -60,10 +60,9 @@ public: /** * Retreives recorded data. - * @param soundData Reference to a SoundData to fill. - * @return number of samples obtained from device. + * @return SoundData containing data obtained from recording device. **/ - virtual int getData(love::sound::SoundData *soundData) = 0; + virtual love::sound::SoundData *getData() = 0; /** * @return C string device name. diff --git a/src/modules/audio/null/RecordingDevice.cpp b/src/modules/audio/null/RecordingDevice.cpp index 9e62b4168..f68f08903 100644 --- a/src/modules/audio/null/RecordingDevice.cpp +++ b/src/modules/audio/null/RecordingDevice.cpp @@ -52,9 +52,9 @@ void RecordingDevice::stopRecording() { } -int RecordingDevice::getData(love::sound::SoundData*) +love::sound::SoundData *RecordingDevice::getData() { - return 0; + return nullptr; } int RecordingDevice::getSampleCount() const diff --git a/src/modules/audio/null/RecordingDevice.h b/src/modules/audio/null/RecordingDevice.h index 8fcb1bdad..da5e14ec1 100644 --- a/src/modules/audio/null/RecordingDevice.h +++ b/src/modules/audio/null/RecordingDevice.h @@ -39,7 +39,7 @@ public: virtual bool startRecording(); virtual bool startRecording(int samples, int sampleRate, int bitDepth, int channels); virtual void stopRecording(); - virtual int getData(love::sound::SoundData *soundData); + virtual love::sound::SoundData *getData(); virtual const char *getName() const; virtual int getID() const; virtual int getSampleCount() const; diff --git a/src/modules/audio/openal/RecordingDevice.cpp b/src/modules/audio/openal/RecordingDevice.cpp index b8d29f27c..5e9eba7f2 100644 --- a/src/modules/audio/openal/RecordingDevice.cpp +++ b/src/modules/audio/openal/RecordingDevice.cpp @@ -20,6 +20,7 @@ #include "RecordingDevice.h" #include "Audio.h" +#include "sound/Sound.h" namespace love { @@ -28,6 +29,8 @@ namespace audio namespace openal { +#define soundInstance() (Module::getInstance(Module::M_SOUND)) + class InvalidFormatException : public love::Exception { public: @@ -99,23 +102,20 @@ void RecordingDevice::stopRecording() device = nullptr; } -int RecordingDevice::getData(love::sound::SoundData *soundData) +love::sound::SoundData *RecordingDevice::getData() { if (!isRecording()) - return 0; + return nullptr; int samples = getSampleCount(); if (samples == 0) - return 0; + return nullptr; - //reinitialize soundData if necessary - if (samples != soundData->getSampleCount() || sampleRate != soundData->getSampleRate() || - bitDepth != soundData->getBitDepth() || channels != soundData->getChannels()) - soundData->load(samples, sampleRate, bitDepth, channels); + love::sound::SoundData *soundData = soundInstance()->newSoundData(samples, sampleRate, bitDepth, channels); alcCaptureSamples(device, soundData->getData(), samples); - return samples; + return soundData; } int RecordingDevice::getSampleCount() const diff --git a/src/modules/audio/openal/RecordingDevice.h b/src/modules/audio/openal/RecordingDevice.h index 3e4ae14ca..00488a8f4 100644 --- a/src/modules/audio/openal/RecordingDevice.h +++ b/src/modules/audio/openal/RecordingDevice.h @@ -52,7 +52,7 @@ public: virtual bool startRecording(); virtual bool startRecording(int samples, int sampleRate, int bitDepth, int channels); virtual void stopRecording(); - virtual int getData(love::sound::SoundData *soundData); + virtual love::sound::SoundData *getData(); virtual const char *getName() const; virtual int getID() const; virtual int getSampleCount() const; diff --git a/src/modules/audio/wrap_RecordingDevice.cpp b/src/modules/audio/wrap_RecordingDevice.cpp index b78dc3bc1..256ef7e5a 100644 --- a/src/modules/audio/wrap_RecordingDevice.cpp +++ b/src/modules/audio/wrap_RecordingDevice.cpp @@ -22,10 +22,6 @@ #include "wrap_Audio.h" #include "sound/SoundData.h" -#include "sound/Sound.h" - -#define soundInstance() (Module::getInstance(Module::M_SOUND)) - namespace love { namespace audio @@ -59,43 +55,41 @@ int w_RecordingDevice_startRecording(lua_State *L) int w_RecordingDevice_stopRecording(lua_State *L) { RecordingDevice *d = luax_checkrecordingdevice(L, 1); - int samples = d->getSampleCount(); - if (samples == 0) - { - lua_pushnil(L); - return 1; - } - love::sound::SoundData *s = nullptr; + luax_catchexcept(L, [&](){ - s = soundInstance()->newSoundData(samples, d->getSampleRate(), d->getBitDepth(), d->getChannels()); - d->getData(s); + s = d->getData(); d->stopRecording(); }); - luax_pushtype(L, SOUND_SOUND_DATA_ID, s); - s->release(); + if (s != nullptr) + { + luax_pushtype(L, SOUND_SOUND_DATA_ID, s); + s->release(); + } + else + lua_pushnil(L); + return 1; } int w_RecordingDevice_getData(lua_State *L) { RecordingDevice *d = luax_checkrecordingdevice(L, 1); - int samples = d->getSampleCount(); - if (samples == 0) - { - lua_pushnil(L); - return 1; - } - love::sound::SoundData *s = nullptr; + luax_catchexcept(L, [&](){ - s = soundInstance()->newSoundData(samples, d->getSampleRate(), d->getBitDepth(), d->getChannels()); - d->getData(s); + s = d->getData(); }); - luax_pushtype(L, SOUND_SOUND_DATA_ID, s); - s->release(); + if (s != nullptr) + { + luax_pushtype(L, SOUND_SOUND_DATA_ID, s); + s->release(); + } + else + lua_pushnil(L); + return 1; } diff --git a/src/modules/sound/SoundData.h b/src/modules/sound/SoundData.h index 821fb291e..07b13c2d3 100644 --- a/src/modules/sound/SoundData.h +++ b/src/modules/sound/SoundData.h @@ -54,10 +54,11 @@ public: void setSample(int i, float sample); float getSample(int i) const; - void load(int samples, int sampleRate, int bitDepth, int channels, void *newData = 0); private: + void load(int samples, int sampleRate, int bitDepth, int channels, void *newData = 0); + uint8 *data; size_t size; From 9e139779956ddba89333eece8104bd7b1b4ff2e7 Mon Sep 17 00:00:00 2001 From: Bart van Strien Date: Mon, 31 Oct 2016 20:23:52 +0100 Subject: [PATCH 11/37] Added release date for 0.10.2 --- changes.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changes.txt b/changes.txt index 66a501d4f..5b50368ca 100644 --- a/changes.txt +++ b/changes.txt @@ -1,7 +1,7 @@ LOVE 0.10.2 [Super Toast] ------------------------- -Released: N/A +Released: 2016-10-31 * Added lovec.exe in Windows. It is the same as love.exe but built with the Console subsystem, so it always uses or provides a console. * Added the ability to restart the game via love.event.quit("restart"). From 3336accb8fbfdaf35f2220a451fc5389feefcb44 Mon Sep 17 00:00:00 2001 From: Bart van Strien Date: Mon, 31 Oct 2016 20:23:56 +0100 Subject: [PATCH 12/37] Added tag 0.10.2 for changeset afc69c4f7145 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 67d680e02..69c60352e 100644 --- a/.hgtags +++ b/.hgtags @@ -10,3 +10,4 @@ e0f98d53debb62347c6433ca0534a0f77f15f76f 0.8.0 a5e405cdf14d030b71d09a105086797942ae91a9 0.9.2 14717f0fb5d96d380c10fe6f41b74b7652c7d836 0.10.0 3cb3559eebb89803c0b7fa83d51d8e48a2b20b7a 0.10.1 +afc69c4f714534c64e6f8c8660b724b5b736d7f3 0.10.2 From 57d02dec2b12bea4282b6ccaa1d125958a6d741c Mon Sep 17 00:00:00 2001 From: Raidho Date: Thu, 3 Nov 2016 06:31:31 +0300 Subject: [PATCH 13/37] Addressed some criticism getRecordingDevices internal function is now returns constant reference, also re-enumerates devices every time it is called; this is a slow operation getID removed for not being useful --HG-- branch : minor-mic-input --- src/modules/audio/Audio.h | 2 +- src/modules/audio/RecordingDevice.h | 5 -- src/modules/audio/null/Audio.cpp | 4 +- src/modules/audio/null/Audio.h | 2 +- src/modules/audio/null/RecordingDevice.cpp | 5 -- src/modules/audio/null/RecordingDevice.h | 1 - src/modules/audio/openal/Audio.cpp | 60 ++++++++++---------- src/modules/audio/openal/Audio.h | 2 +- src/modules/audio/openal/RecordingDevice.cpp | 5 -- src/modules/audio/openal/RecordingDevice.h | 1 - src/modules/audio/wrap_Audio.cpp | 8 +-- src/modules/audio/wrap_RecordingDevice.cpp | 8 --- 12 files changed, 39 insertions(+), 64 deletions(-) diff --git a/src/modules/audio/Audio.h b/src/modules/audio/Audio.h index c10ffe813..811845bd4 100644 --- a/src/modules/audio/Audio.h +++ b/src/modules/audio/Audio.h @@ -192,7 +192,7 @@ public: /** * @return Reference to a vector of pointers to recording devices. May be empty. **/ - virtual std::vector *getRecordingDevices() = 0; + virtual const std::vector &getRecordingDevices() = 0; /** * Gets the distance model used for attenuation. diff --git a/src/modules/audio/RecordingDevice.h b/src/modules/audio/RecordingDevice.h index 83af9580e..48c7d41cd 100644 --- a/src/modules/audio/RecordingDevice.h +++ b/src/modules/audio/RecordingDevice.h @@ -69,11 +69,6 @@ public: **/ virtual const char *getName() const = 0; - /** - * @return Unique ID number. - **/ - virtual int getID() const = 0; - /** * @return Number of samples currently recorded. **/ diff --git a/src/modules/audio/null/Audio.cpp b/src/modules/audio/null/Audio.cpp index 957129978..9db2e67b1 100644 --- a/src/modules/audio/null/Audio.cpp +++ b/src/modules/audio/null/Audio.cpp @@ -144,9 +144,9 @@ float Audio::getDopplerScale() const return 1.0f; } -std::vector *Audio::getRecordingDevices() +const std::vector &Audio::getRecordingDevices() { - return &capture; + return capture; } Audio::DistanceModel Audio::getDistanceModel() const diff --git a/src/modules/audio/null/Audio.h b/src/modules/audio/null/Audio.h index 3f841fec5..fdd8325fc 100644 --- a/src/modules/audio/null/Audio.h +++ b/src/modules/audio/null/Audio.h @@ -71,7 +71,7 @@ public: void setDopplerScale(float scale); float getDopplerScale() const; - std::vector *getRecordingDevices(); + const std::vector &getRecordingDevices(); DistanceModel getDistanceModel() const; void setDistanceModel(DistanceModel distanceModel); diff --git a/src/modules/audio/null/RecordingDevice.cpp b/src/modules/audio/null/RecordingDevice.cpp index f68f08903..0d135fa95 100644 --- a/src/modules/audio/null/RecordingDevice.cpp +++ b/src/modules/audio/null/RecordingDevice.cpp @@ -82,11 +82,6 @@ const char *RecordingDevice::getName() const return name; } -int RecordingDevice::getID() const -{ - return 0; -} - bool RecordingDevice::isRecording() const { return false; diff --git a/src/modules/audio/null/RecordingDevice.h b/src/modules/audio/null/RecordingDevice.h index da5e14ec1..dd4ccc205 100644 --- a/src/modules/audio/null/RecordingDevice.h +++ b/src/modules/audio/null/RecordingDevice.h @@ -41,7 +41,6 @@ public: virtual void stopRecording(); virtual love::sound::SoundData *getData(); virtual const char *getName() const; - virtual int getID() const; virtual int getSampleCount() const; virtual int getSampleRate() const; virtual int getBitDepth() const; diff --git a/src/modules/audio/openal/Audio.cpp b/src/modules/audio/openal/Audio.cpp index 6283b293b..8658f5957 100644 --- a/src/modules/audio/openal/Audio.cpp +++ b/src/modules/audio/openal/Audio.cpp @@ -313,41 +313,41 @@ void Audio::setDistanceModel(DistanceModel distanceModel) } } -std::vector *Audio::getRecordingDevices() +const std::vector &Audio::getRecordingDevices() { - if (capture.size() == 0) + capture.clear(); + + std::string defaultname(alcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER)); + + //no device name obtained from AL, fallback to reading from device + if (defaultname.length() == 0) { - std::string defaultname(alcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER)); - - //no device name obtained from AL, fallback to reading from device - if (defaultname.length() == 0) + //use some safe basic parameters - 8 kHz, 8 bits, 1 channel + ALCdevice *defaultdevice = alcCaptureOpenDevice(NULL, 8000, 8, 1); + if (alGetError() == AL_NO_ERROR) { - //use some safe basic parameters - 8 kHz, 8 bits, 1 channel - ALCdevice *defaultdevice = alcCaptureOpenDevice(NULL, 8000, 8, 1); - if (alGetError() == AL_NO_ERROR) - { - defaultname = alcGetString(defaultdevice, ALC_CAPTURE_DEVICE_SPECIFIER); - alcCaptureCloseDevice(defaultdevice); - } - else - //failed to open default recording device - bail, return empty list - return &capture; - } - capture.push_back(new RecordingDevice(defaultname.c_str(), 0)); - - const ALCchar *devstr = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER); - size_t offset = 0; - while (true) - { - if (devstr[offset] == '\0') - break; - std::string str((ALCchar*)&devstr[offset]); - if (str != defaultname) - capture.push_back(new RecordingDevice(str.c_str(), capture.size())); - offset += str.length() + 1; + defaultname = alcGetString(defaultdevice, ALC_CAPTURE_DEVICE_SPECIFIER); + alcCaptureCloseDevice(defaultdevice); } + else + //failed to open default recording device - bail, return empty list + return capture; } - return &capture; + capture.push_back(new RecordingDevice(defaultname.c_str(), 0)); + + const ALCchar *devstr = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER); + size_t offset = 0; + while (true) + { + if (devstr[offset] == '\0') + break; + std::string str((ALCchar*)&devstr[offset]); + if (str != defaultname) + capture.push_back(new RecordingDevice(str.c_str(), capture.size())); + offset += str.length() + 1; + } + + return capture; } } // openal diff --git a/src/modules/audio/openal/Audio.h b/src/modules/audio/openal/Audio.h index 469586d59..aaa675fdc 100644 --- a/src/modules/audio/openal/Audio.h +++ b/src/modules/audio/openal/Audio.h @@ -105,7 +105,7 @@ public: void setDopplerScale(float scale); float getDopplerScale() const; - std::vector *getRecordingDevices(); + const std::vector &getRecordingDevices(); DistanceModel getDistanceModel() const; void setDistanceModel(DistanceModel distanceModel); diff --git a/src/modules/audio/openal/RecordingDevice.cpp b/src/modules/audio/openal/RecordingDevice.cpp index 5e9eba7f2..80bfc08ad 100644 --- a/src/modules/audio/openal/RecordingDevice.cpp +++ b/src/modules/audio/openal/RecordingDevice.cpp @@ -148,11 +148,6 @@ const char *RecordingDevice::getName() const return name.c_str(); } -int RecordingDevice::getID() const -{ - return id; -} - bool RecordingDevice::isRecording() const { return device != nullptr; diff --git a/src/modules/audio/openal/RecordingDevice.h b/src/modules/audio/openal/RecordingDevice.h index 00488a8f4..c6005601f 100644 --- a/src/modules/audio/openal/RecordingDevice.h +++ b/src/modules/audio/openal/RecordingDevice.h @@ -54,7 +54,6 @@ public: virtual void stopRecording(); virtual love::sound::SoundData *getData(); virtual const char *getName() const; - virtual int getID() const; virtual int getSampleCount() const; virtual int getSampleRate() const; virtual int getBitDepth() const; diff --git a/src/modules/audio/wrap_Audio.cpp b/src/modules/audio/wrap_Audio.cpp index 175f1d4af..24db7df8a 100644 --- a/src/modules/audio/wrap_Audio.cpp +++ b/src/modules/audio/wrap_Audio.cpp @@ -279,13 +279,13 @@ int w_getDistanceModel(lua_State *L) int w_getRecordingDevices(lua_State *L) { - std::vector *devices = instance()->getRecordingDevices(); + const std::vector &devices = instance()->getRecordingDevices(); - lua_createtable(L, (*devices).size(), 0); + lua_createtable(L, devices.size(), 0); - for (unsigned int i = 0; i < (*devices).size(); i++) + for (unsigned int i = 0; i < devices.size(); i++) { - luax_pushtype(L, AUDIO_RECORDING_DEVICE_ID, (*devices)[i]); + luax_pushtype(L, AUDIO_RECORDING_DEVICE_ID, devices[i]); lua_rawseti(L, -2, i + 1); } diff --git a/src/modules/audio/wrap_RecordingDevice.cpp b/src/modules/audio/wrap_RecordingDevice.cpp index 256ef7e5a..d4d3cc534 100644 --- a/src/modules/audio/wrap_RecordingDevice.cpp +++ b/src/modules/audio/wrap_RecordingDevice.cpp @@ -128,13 +128,6 @@ int w_RecordingDevice_getName(lua_State *L) return 1; } -int w_RecordingDevice_getID(lua_State *L) -{ - RecordingDevice *d = luax_checkrecordingdevice(L, 1); - lua_pushnumber(L, d->getID()); - return 1; -} - int w_RecordingDevice_isRecording(lua_State *L) { RecordingDevice *d = luax_checkrecordingdevice(L, 1); @@ -152,7 +145,6 @@ static const luaL_Reg w_RecordingDevice_functions[] = { "getBitDepth", w_RecordingDevice_getBitDepth }, { "getChannels", w_RecordingDevice_getChannels }, { "getName", w_RecordingDevice_getName }, - { "getID", w_RecordingDevice_getID }, { "isRecording", w_RecordingDevice_isRecording }, { 0, 0 } }; From c7c253f4498dae46660ccccbfe62f20c037fcd77 Mon Sep 17 00:00:00 2001 From: Raidho Date: Thu, 3 Nov 2016 20:24:21 +0300 Subject: [PATCH 14/37] made getRecordingDevices keep already existing devices --HG-- branch : minor-mic-input --- src/modules/audio/null/RecordingDevice.cpp | 2 +- src/modules/audio/null/RecordingDevice.h | 2 +- src/modules/audio/openal/Audio.cpp | 30 +++++++++++++++++--- src/modules/audio/openal/RecordingDevice.cpp | 3 +- src/modules/audio/openal/RecordingDevice.h | 3 +- 5 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src/modules/audio/null/RecordingDevice.cpp b/src/modules/audio/null/RecordingDevice.cpp index 0d135fa95..876683c17 100644 --- a/src/modules/audio/null/RecordingDevice.cpp +++ b/src/modules/audio/null/RecordingDevice.cpp @@ -30,7 +30,7 @@ namespace null const char *RecordingDevice::name = "null"; -RecordingDevice::RecordingDevice(const char *, int) +RecordingDevice::RecordingDevice(const char *) { } diff --git a/src/modules/audio/null/RecordingDevice.h b/src/modules/audio/null/RecordingDevice.h index dd4ccc205..c679c9a92 100644 --- a/src/modules/audio/null/RecordingDevice.h +++ b/src/modules/audio/null/RecordingDevice.h @@ -34,7 +34,7 @@ namespace null class RecordingDevice : public love::audio::RecordingDevice { public: - RecordingDevice(const char *name, int id); + RecordingDevice(const char *name); virtual ~RecordingDevice(); virtual bool startRecording(); virtual bool startRecording(int samples, int sampleRate, int bitDepth, int channels); diff --git a/src/modules/audio/openal/Audio.cpp b/src/modules/audio/openal/Audio.cpp index 8658f5957..e8a0bd674 100644 --- a/src/modules/audio/openal/Audio.cpp +++ b/src/modules/audio/openal/Audio.cpp @@ -315,7 +315,8 @@ void Audio::setDistanceModel(DistanceModel distanceModel) const std::vector &Audio::getRecordingDevices() { - capture.clear(); + std::vector devnames; + std::vector devices; std::string defaultname(alcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER)); @@ -330,11 +331,15 @@ const std::vector &Audio::getRecordingDevices() alcCaptureCloseDevice(defaultdevice); } else - //failed to open default recording device - bail, return empty list + { + //failed to open default recording device - bail, return empty list + capture.clear(); return capture; + } } - capture.push_back(new RecordingDevice(defaultname.c_str(), 0)); + devnames.push_back(defaultname); + //find devices name list const ALCchar *devstr = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER); size_t offset = 0; while (true) @@ -343,10 +348,27 @@ const std::vector &Audio::getRecordingDevices() break; std::string str((ALCchar*)&devstr[offset]); if (str != defaultname) - capture.push_back(new RecordingDevice(str.c_str(), capture.size())); + devnames.push_back(str); offset += str.length() + 1; } + //build list of devices + devices.reserve(devnames.size()); + for (unsigned int i = 0; i < devnames.size(); i++) + { + devices[i] = nullptr; + for (auto c = capture.begin(); c != capture.end(); c++) + if (devnames[i] == (*c)->getName()) + devices[i] = *c; + + if (devices[i] == nullptr) + devices[i] = new RecordingDevice(devnames[i].c_str()); + } + + capture.clear(); + for (unsigned int i = 0; i < devnames.size(); i++) + capture.push_back(devices[i]); + return capture; } diff --git a/src/modules/audio/openal/RecordingDevice.cpp b/src/modules/audio/openal/RecordingDevice.cpp index 80bfc08ad..e978e6e48 100644 --- a/src/modules/audio/openal/RecordingDevice.cpp +++ b/src/modules/audio/openal/RecordingDevice.cpp @@ -42,9 +42,8 @@ public: }; -RecordingDevice::RecordingDevice(const char *name, int id) +RecordingDevice::RecordingDevice(const char *name) : name(name) - , id(id) { } diff --git a/src/modules/audio/openal/RecordingDevice.h b/src/modules/audio/openal/RecordingDevice.h index c6005601f..463ca3a38 100644 --- a/src/modules/audio/openal/RecordingDevice.h +++ b/src/modules/audio/openal/RecordingDevice.h @@ -47,7 +47,7 @@ namespace openal class RecordingDevice : public love::audio::RecordingDevice { public: - RecordingDevice(const char *name, int id); + RecordingDevice(const char *name); virtual ~RecordingDevice(); virtual bool startRecording(); virtual bool startRecording(int samples, int sampleRate, int bitDepth, int channels); @@ -66,7 +66,6 @@ private: int bitDepth = 16; int channels = 1; std::string name; - int id; ALCdevice *device = nullptr; }; //RecordingDevice From 422325588ecc26994725778c721e498add3b2687 Mon Sep 17 00:00:00 2001 From: Raidho Date: Fri, 4 Nov 2016 05:40:07 +0300 Subject: [PATCH 15/37] fixed potential memory leak, minor code imporovements --HG-- branch : minor-mic-input --- src/modules/audio/openal/Audio.cpp | 33 ++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/src/modules/audio/openal/Audio.cpp b/src/modules/audio/openal/Audio.cpp index e8a0bd674..8b6f20ead 100644 --- a/src/modules/audio/openal/Audio.cpp +++ b/src/modules/audio/openal/Audio.cpp @@ -120,8 +120,8 @@ Audio::Audio() alcMakeContextCurrent(nullptr); alcDestroyContext(context); alcCloseDevice(device); - for (unsigned int i = 0; i < capture.size(); i++) - delete capture[i]; + for (auto c : capture) + delete c; throw; } @@ -141,8 +141,8 @@ Audio::~Audio() alcMakeContextCurrent(nullptr); alcDestroyContext(context); alcCloseDevice(device); - for (unsigned int i = 0; i < capture.size(); i++) - delete capture[i]; + for (auto c : capture) + delete c; } @@ -337,6 +337,8 @@ const std::vector &Audio::getRecordingDevices() return capture; } } + + devnames.reserve(capture.size()); devnames.push_back(defaultname); //find devices name list @@ -352,20 +354,29 @@ const std::vector &Audio::getRecordingDevices() offset += str.length() + 1; } - //build list of devices devices.reserve(devnames.size()); + //build ordered list of devices for (unsigned int i = 0; i < devnames.size(); i++) { - devices[i] = nullptr; - for (auto c = capture.begin(); c != capture.end(); c++) - if (devnames[i] == (*c)->getName()) - devices[i] = *c; + devices.push_back(nullptr); + auto d = devices.end() - 1; - if (devices[i] == nullptr) - devices[i] = new RecordingDevice(devnames[i].c_str()); + for (auto c : capture) + if (devnames[i] == c->getName()) + *d = c; + + if (*d == nullptr) + *d = new RecordingDevice(devnames[i].c_str()); + else + (*d)->retain(); } + for (auto c : capture) + c->release(); capture.clear(); + capture.reserve(devices.size()); + + //this needs to be executed in specific order for (unsigned int i = 0; i < devnames.size(); i++) capture.push_back(devices[i]); From de855f30d527a9d30314c2109bdb709240305cbc Mon Sep 17 00:00:00 2001 From: xpol Date: Fri, 4 Nov 2016 11:37:38 +0800 Subject: [PATCH 16/37] Better love.arg.parse_options(): 1. use string.match for we are mean to match 2. better match pattern which now do not allow somthing like `not-a-valid--console` 3. use while loop which allows change loop var. --HG-- branch : better-parse-options --- src/scripts/boot.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/scripts/boot.lua b/src/scripts/boot.lua index 552827e1d..e3de9d4ed 100644 --- a/src/scripts/boot.lua +++ b/src/scripts/boot.lua @@ -134,15 +134,17 @@ function love.arg.parse_options() local game local argc = #arg - for i=1,argc do + local i = 1 + while i <= argc do -- Look for options. - local s, e, m = string.find(arg[i], "%-%-(.+)") + local m = string.match(arg[i], "^%-%-(.+)") if m and love.arg.options[m] then i = love.arg.parse_option(love.arg.options[m], i+1) elseif not game then game = i end + i = i + 1 end if not love.arg.options.game.set then From c28a6c51c49c5d1acba4645e3f29097d3fa010a3 Mon Sep 17 00:00:00 2001 From: Xpol Wan Date: Fri, 4 Nov 2016 22:27:49 +0800 Subject: [PATCH 17/37] love.arg.parse_option just return how many extra arg the current option consumes. --HG-- branch : better-parse-options --- src/scripts/boot.lua | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/scripts/boot.lua b/src/scripts/boot.lua index e3de9d4ed..50b31c6e6 100644 --- a/src/scripts/boot.lua +++ b/src/scripts/boot.lua @@ -122,11 +122,10 @@ function love.arg.parse_option(m, i) m.arg = {} for j=i,i+m.a-1 do table.insert(m.arg, arg[j]) - i = j end end - return i + return m.a end function love.arg.parse_options() @@ -140,7 +139,7 @@ function love.arg.parse_options() local m = string.match(arg[i], "^%-%-(.+)") if m and love.arg.options[m] then - i = love.arg.parse_option(love.arg.options[m], i+1) + i = i + love.arg.parse_option(love.arg.options[m], i+1) elseif not game then game = i end From c647317a749bc1872c41d503c7b177b2167a826c Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Sat, 5 Nov 2016 17:33:32 -0300 Subject: [PATCH 18/37] Re-generated boot.lua.h. --- src/scripts/boot.lua.h | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/scripts/boot.lua.h b/src/scripts/boot.lua.h index 90d13adf8..55c317952 100644 --- a/src/scripts/boot.lua.h +++ b/src/scripts/boot.lua.h @@ -218,30 +218,33 @@ const unsigned char boot_lua[] = 0x64, 0x6f, 0x0a, 0x09, 0x09, 0x09, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x28, 0x6d, 0x2e, 0x61, 0x72, 0x67, 0x2c, 0x20, 0x61, 0x72, 0x67, 0x5b, 0x6a, 0x5d, 0x29, 0x0a, - 0x09, 0x09, 0x09, 0x69, 0x20, 0x3d, 0x20, 0x6a, 0x0a, 0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a, 0x09, 0x65, 0x6e, 0x64, 0x0a, - 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x69, 0x0a, + 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6d, 0x2e, 0x61, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x61, 0x72, 0x67, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x28, 0x29, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x67, 0x61, 0x6d, 0x65, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x61, 0x72, 0x67, 0x63, 0x20, 0x3d, 0x20, 0x23, 0x61, 0x72, 0x67, 0x0a, - 0x09, 0x66, 0x6f, 0x72, 0x20, 0x69, 0x3d, 0x31, 0x2c, 0x61, 0x72, 0x67, 0x63, 0x20, 0x64, 0x6f, 0x0a, + 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x31, 0x0a, + 0x09, 0x77, 0x68, 0x69, 0x6c, 0x65, 0x20, 0x69, 0x20, 0x3c, 0x3d, 0x20, 0x61, 0x72, 0x67, 0x63, 0x20, 0x64, + 0x6f, 0x0a, 0x09, 0x09, 0x2d, 0x2d, 0x20, 0x4c, 0x6f, 0x6f, 0x6b, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x0a, - 0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x73, 0x2c, 0x20, 0x65, 0x2c, 0x20, 0x6d, 0x20, 0x3d, 0x20, - 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x66, 0x69, 0x6e, 0x64, 0x28, 0x61, 0x72, 0x67, 0x5b, 0x69, 0x5d, - 0x2c, 0x20, 0x22, 0x25, 0x2d, 0x25, 0x2d, 0x28, 0x2e, 0x2b, 0x29, 0x22, 0x29, 0x0a, + 0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6d, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, + 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x28, 0x61, 0x72, 0x67, 0x5b, 0x69, 0x5d, 0x2c, 0x20, 0x22, 0x5e, 0x25, + 0x2d, 0x25, 0x2d, 0x28, 0x2e, 0x2b, 0x29, 0x22, 0x29, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x20, 0x6d, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x61, 0x72, 0x67, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5b, 0x6d, 0x5d, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, - 0x09, 0x09, 0x09, 0x69, 0x20, 0x3d, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x61, 0x72, 0x67, 0x2e, 0x70, 0x61, - 0x72, 0x73, 0x65, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x61, 0x72, - 0x67, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5b, 0x6d, 0x5d, 0x2c, 0x20, 0x69, 0x2b, 0x31, 0x29, 0x0a, + 0x09, 0x09, 0x09, 0x69, 0x20, 0x3d, 0x20, 0x69, 0x20, 0x2b, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x61, 0x72, + 0x67, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6c, 0x6f, 0x76, + 0x65, 0x2e, 0x61, 0x72, 0x67, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5b, 0x6d, 0x5d, 0x2c, 0x20, + 0x69, 0x2b, 0x31, 0x29, 0x0a, 0x09, 0x09, 0x65, 0x6c, 0x73, 0x65, 0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x67, 0x61, 0x6d, 0x65, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x09, 0x09, 0x09, 0x67, 0x61, 0x6d, 0x65, 0x20, 0x3d, 0x20, 0x69, 0x0a, 0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a, + 0x09, 0x09, 0x69, 0x20, 0x3d, 0x20, 0x69, 0x20, 0x2b, 0x20, 0x31, 0x0a, 0x09, 0x65, 0x6e, 0x64, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x61, 0x72, 0x67, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x67, 0x61, 0x6d, 0x65, 0x2e, 0x73, 0x65, 0x74, 0x20, 0x74, 0x68, From b0849343111200cf3e67365643cec8ac2a4dbf79 Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Wed, 16 Nov 2016 17:37:43 +0000 Subject: [PATCH 19/37] Fix Shader:send and sendColor ignoring the last argument for shader uniform arrays. --- src/modules/graphics/opengl/wrap_Shader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/graphics/opengl/wrap_Shader.cpp b/src/modules/graphics/opengl/wrap_Shader.cpp index e13cc7926..0a3b5f149 100644 --- a/src/modules/graphics/opengl/wrap_Shader.cpp +++ b/src/modules/graphics/opengl/wrap_Shader.cpp @@ -48,7 +48,7 @@ int w_Shader_getWarnings(lua_State *L) static int _getCount(lua_State *L, int startidx, const Shader::UniformInfo *info) { - return std::min(std::max(lua_gettop(L) - startidx, 1), info->count); + return std::min(std::max(lua_gettop(L) - startidx + 1, 1), info->count); } template From 3e80ec2257860b5f9b82cf3123608b096fe9a0d7 Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Sat, 19 Nov 2016 19:13:26 -0400 Subject: [PATCH 20/37] Render pass API. Replaces love.graphics.setCanvas and friends. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Removed love.graphics.set/getCanvas, Canvas:renderTo, Canvas:newImageData, love.graphics.clear, love.graphics.discard, and love.graphics.newScreenshot. - Added love.graphics.beginPass and love.graphics.endPass. All rendering must happen within a pass. love.draw is now called while a render pass to the main screen is active. - Added love.graphics.renderPass, which is a wrapper for begin/endPass which calls the supplied function argument in between a begin/end. This is similar to the old Canvas:renderTo. - Added love.graphics.isPassActive, getPassCanvases, getPassWidth, getPassHeight, and getPassDimensions. - Added love.graphics.captureScreenshot. - Added a new love.run callback love.drawpasses, which is called after love.update and before love.draw. It is the place to render to Canvases using beginPass or renderPass, since love.draw now always draws to the main screen. - Renamed the ‘canvasswitches’ field in the table returned by love.graphics.getStats to ‘renderpasses’. love.graphics.beginPass has the following variants (and renderPass mirrors them with an additional function argument at the end): - love.graphics.beginPass(), begins rendering to the main screen without clearing it. - love.graphics.beginPass(r, g, b [, a]), begins rendering to the main screen and clears the screen to the specified color. - love.graphics.beginPass(canvas [, willstencil]), begins rendering to the specified Canvas without clearing it. love.graphics.stencil can only be used within the Canvas if ‘willstencil’ is true. - love.graphics.beginPass(canvas, r, g, b [, a] [, willstencil]), beings rendering to the specified Canvas and clears it to the specified color. love.graphics.stencil can only be used within the Canvas if ‘willstencil’ is true. - love.graphics.beginPass(info), where ‘info’ is a table in the following form: { {canvas [, r, g, b, a]}, — A canvas and an optional color to clear the Canvas to when the pass begins. {canvas2, [r, g, b, a]}, — Additional Canvases and optional clear colors for multi-canvas rendering. …, stencil = false, — Optional boolean field to specify whether love.graphics.stencil is allowed within this pass. False by default. } The pass info table can be created once up-front and used for multiple beginPass/renderPass calls. love.graphics.endPass can take an optional callback function argument, which causes endPass to capture the contents of the Canvas drawn to in the render pass to a new ImageData and passes it to the supplied function (this replaces Canvas:newImageData). This does not work on the main screen. love.graphics.captureScreenshot replaces love.graphics.newScreenshot. It takes a callback function argument which causes love.graphics.present to capture the contents of the screen to a new ImageData and passes it to the supplied function. captureScreenshot can only be called before drawing to the main screen begins in the current frame (e.g. in love.keypressed, love.update, etc.) --HG-- branch : minor --- CMakeLists.txt | 12 + .../xcode/liblove.xcodeproj/project.pbxproj | 18 + src/libraries/xxHash/xxhash.c | 889 ++++++++++++++++ src/libraries/xxHash/xxhash.h | 293 ++++++ src/modules/event/sdl/Event.cpp | 17 + src/modules/event/sdl/Event.h | 2 + src/modules/event/wrap_Event.cpp | 11 +- src/modules/graphics/Graphics.h | 14 +- src/modules/graphics/opengl/Canvas.cpp | 462 +-------- src/modules/graphics/opengl/Canvas.h | 52 +- src/modules/graphics/opengl/Graphics.cpp | 967 ++++++++++++------ src/modules/graphics/opengl/Graphics.h | 135 ++- src/modules/graphics/opengl/OpenGL.cpp | 96 +- src/modules/graphics/opengl/OpenGL.h | 19 +- src/modules/graphics/opengl/Shader.cpp | 21 +- src/modules/graphics/opengl/Shader.h | 5 +- src/modules/graphics/opengl/wrap_Canvas.cpp | 51 - src/modules/graphics/opengl/wrap_Graphics.cpp | 488 +++++---- src/modules/window/Window.h | 8 + src/modules/window/sdl/Window.cpp | 46 +- src/modules/window/sdl/Window.h | 4 + src/modules/window/wrap_Window.cpp | 16 +- src/scripts/boot.lua | 19 +- src/scripts/boot.lua.h | 40 +- 24 files changed, 2562 insertions(+), 1123 deletions(-) create mode 100755 src/libraries/xxHash/xxhash.c create mode 100755 src/libraries/xxHash/xxhash.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 2386770d7..ec1459325 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1418,6 +1418,17 @@ set(LOVE_SRC_3P_WUFF add_library(love_3p_wuff ${LOVE_SRC_3P_WUFF}) +# +# xxHash +# + +set(LOVE_SRC_3P_XXHASH + src/libraries/xxHash/xxhash.c + src/libraries/xxHash/xxhash.h +) + +add_library(love_3p_xxhash ${LOVE_SRC_3P_XXHASH}) + set(LOVE_3P love_3p_box2d love_3p_ddsparse @@ -1429,6 +1440,7 @@ set(LOVE_3P love_3p_lz4 love_3p_noise1234 love_3p_wuff + love_3p_xxhash ) love_disable_warnings(love_3p_box2d love_3p_enet love_3p_luasocket) diff --git a/platform/xcode/liblove.xcodeproj/project.pbxproj b/platform/xcode/liblove.xcodeproj/project.pbxproj index 70c7975b7..f16045158 100644 --- a/platform/xcode/liblove.xcodeproj/project.pbxproj +++ b/platform/xcode/liblove.xcodeproj/project.pbxproj @@ -880,6 +880,9 @@ FA4943551D9DDFFE00E8D38A /* HashFunction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA1DC2611C5D9555008F99A0 /* HashFunction.cpp */; }; FA4B66C91ABBCF1900558F15 /* Timer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA4B66C81ABBCF1900558F15 /* Timer.cpp */; }; FA4B66CA1ABBCF1900558F15 /* Timer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA4B66C81ABBCF1900558F15 /* Timer.cpp */; }; + FA4F2B791DE0125B00CA37D7 /* xxhash.c in Sources */ = {isa = PBXBuildFile; fileRef = FA4F2B771DE0125B00CA37D7 /* xxhash.c */; }; + FA4F2B7A1DE0125B00CA37D7 /* xxhash.h in Headers */ = {isa = PBXBuildFile; fileRef = FA4F2B781DE0125B00CA37D7 /* xxhash.h */; }; + FA4F2B7B1DE0181B00CA37D7 /* xxhash.c in Sources */ = {isa = PBXBuildFile; fileRef = FA4F2B771DE0125B00CA37D7 /* xxhash.c */; }; FA56D9BC1C208A0200D8D3C7 /* libmodplug.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FA56D9BA1C2089EE00D8D3C7 /* libmodplug.a */; }; FA577AB016C7507900860150 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA577A7916C71A1700860150 /* Cocoa.framework */; }; FA577AC216C7512D00860150 /* FreeType.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA577A6716C719D900860150 /* FreeType.framework */; }; @@ -1575,6 +1578,8 @@ FA41A3C61C0A1F950084430C /* ASTCHandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ASTCHandler.cpp; sourceTree = ""; }; FA41A3C71C0A1F950084430C /* ASTCHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASTCHandler.h; sourceTree = ""; }; FA4B66C81ABBCF1900558F15 /* Timer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Timer.cpp; sourceTree = ""; }; + FA4F2B771DE0125B00CA37D7 /* xxhash.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xxhash.c; sourceTree = ""; }; + FA4F2B781DE0125B00CA37D7 /* xxhash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xxhash.h; sourceTree = ""; }; FA56D9BA1C2089EE00D8D3C7 /* libmodplug.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libmodplug.a; sourceTree = ""; }; FA577A6716C719D900860150 /* FreeType.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FreeType.framework; path = /Library/Frameworks/FreeType.framework; sourceTree = ""; }; FA577A6D16C719EA00860150 /* Lua.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Lua.framework; path = /Library/Frameworks/Lua.framework; sourceTree = ""; }; @@ -1783,6 +1788,7 @@ FA1557BE1CE90A2C00AFF582 /* tinyexr */, FA0B7A191A958EA3000E1D17 /* utf8 */, FA0B7A1F1A958EA3000E1D17 /* Wuff */, + FA4F2B761DE0125B00CA37D7 /* xxHash */, ); name = libraries; path = ../../src/libraries; @@ -2890,6 +2896,15 @@ path = theora; sourceTree = ""; }; + FA4F2B761DE0125B00CA37D7 /* xxHash */ = { + isa = PBXGroup; + children = ( + FA4F2B771DE0125B00CA37D7 /* xxhash.c */, + FA4F2B781DE0125B00CA37D7 /* xxhash.h */, + ); + path = xxHash; + sourceTree = ""; + }; FA56D9B91C2089CE00D8D3C7 /* modplug */ = { isa = PBXGroup; children = ( @@ -3286,6 +3301,7 @@ FA0B7E5C1A95902C000E1D17 /* wrap_MotorJoint.h in Headers */, FA0B7A7C1A958EA3000E1D17 /* b2Contact.h in Headers */, FA0B7A881A958EA3000E1D17 /* b2PolygonAndCircleContact.h in Headers */, + FA4F2B7A1DE0125B00CA37D7 /* xxhash.h in Headers */, FA0B7DDE1A95902C000E1D17 /* wrap_BezierCurve.h in Headers */, FA0B7DED1A95902C000E1D17 /* Cursor.h in Headers */, FA0B7E501A95902C000E1D17 /* wrap_Fixture.h in Headers */, @@ -3567,6 +3583,7 @@ FA0B7CD41A95902C000E1D17 /* Source.cpp in Sources */, FAA3A9AF1B7D465A00CED060 /* android.cpp in Sources */, FA0B7CD11A95902C000E1D17 /* Audio.cpp in Sources */, + FA4F2B7B1DE0181B00CA37D7 /* xxhash.c in Sources */, FA0B7D131A95902C000E1D17 /* Font.cpp in Sources */, FA0B7EC91A95902C000E1D17 /* threads.cpp in Sources */, FA0B7A781A958EA3000E1D17 /* b2CircleContact.cpp in Sources */, @@ -3894,6 +3911,7 @@ FA0B7DA51A95902C000E1D17 /* PNGHandler.cpp in Sources */, FA0B7B371A958EA3000E1D17 /* wuff_internal.c in Sources */, FA0B7E971A95902C000E1D17 /* Sound.cpp in Sources */, + FA4F2B791DE0125B00CA37D7 /* xxhash.c in Sources */, FA0B7E361A95902C000E1D17 /* WheelJoint.cpp in Sources */, FA0B7A471A958EA3000E1D17 /* b2PolygonShape.cpp in Sources */, FA0B7D8D1A95902C000E1D17 /* ddsHandler.cpp in Sources */, diff --git a/src/libraries/xxHash/xxhash.c b/src/libraries/xxHash/xxhash.c new file mode 100755 index 000000000..f74880b0d --- /dev/null +++ b/src/libraries/xxHash/xxhash.c @@ -0,0 +1,889 @@ +/* +* xxHash - Fast Hash algorithm +* Copyright (C) 2012-2016, Yann Collet +* +* BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are +* met: +* +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above +* copyright notice, this list of conditions and the following disclaimer +* in the documentation and/or other materials provided with the +* distribution. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +* You can contact the author at : +* - xxHash homepage: http://www.xxhash.com +* - xxHash source repository : https://github.com/Cyan4973/xxHash +*/ + + +/* ************************************* +* Tuning parameters +***************************************/ +/*!XXH_FORCE_MEMORY_ACCESS : + * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable. + * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal. + * The below switch allow to select different access method for improved performance. + * Method 0 (default) : use `memcpy()`. Safe and portable. + * Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable). + * This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`. + * Method 2 : direct access. This method doesn't depend on compiler but violate C standard. + * It can generate buggy code on targets which do not support unaligned memory accesses. + * But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6) + * See http://stackoverflow.com/a/32095106/646947 for details. + * Prefer these methods in priority order (0 > 1 > 2) + */ +#ifndef XXH_FORCE_MEMORY_ACCESS /* can be defined externally, on command line for example */ +# if defined(__GNUC__) && ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) ) +# define XXH_FORCE_MEMORY_ACCESS 2 +# elif defined(__INTEL_COMPILER) || \ + (defined(__GNUC__) && ( defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7S__) )) +# define XXH_FORCE_MEMORY_ACCESS 1 +# endif +#endif + +/*!XXH_ACCEPT_NULL_INPUT_POINTER : + * If the input pointer is a null pointer, xxHash default behavior is to trigger a memory access error, since it is a bad pointer. + * When this option is enabled, xxHash output for null input pointers will be the same as a null-length input. + * By default, this option is disabled. To enable it, uncomment below define : + */ +/* #define XXH_ACCEPT_NULL_INPUT_POINTER 1 */ + +/*!XXH_FORCE_NATIVE_FORMAT : + * By default, xxHash library provides endian-independent Hash values, based on little-endian convention. + * Results are therefore identical for little-endian and big-endian CPU. + * This comes at a performance cost for big-endian CPU, since some swapping is required to emulate little-endian format. + * Should endian-independence be of no importance for your application, you may set the #define below to 1, + * to improve speed for Big-endian CPU. + * This option has no impact on Little_Endian CPU. + */ +#ifndef XXH_FORCE_NATIVE_FORMAT /* can be defined externally */ +# define XXH_FORCE_NATIVE_FORMAT 0 +#endif + +/*!XXH_FORCE_ALIGN_CHECK : + * This is a minor performance trick, only useful with lots of very small keys. + * It means : check for aligned/unaligned input. + * The check costs one initial branch per hash; set to 0 when the input data + * is guaranteed to be aligned. + */ +#ifndef XXH_FORCE_ALIGN_CHECK /* can be defined externally */ +# if defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64) +# define XXH_FORCE_ALIGN_CHECK 0 +# else +# define XXH_FORCE_ALIGN_CHECK 1 +# endif +#endif + + +/* ************************************* +* Includes & Memory related functions +***************************************/ +/*! Modify the local functions below should you wish to use some other memory routines +* for malloc(), free() */ +#include +static void* XXH_malloc(size_t s) { return malloc(s); } +static void XXH_free (void* p) { free(p); } +/*! and for memcpy() */ +#include +static void* XXH_memcpy(void* dest, const void* src, size_t size) { return memcpy(dest,src,size); } + +#define XXH_STATIC_LINKING_ONLY +#include "xxhash.h" + + +/* ************************************* +* Compiler Specific Options +***************************************/ +#ifdef _MSC_VER /* Visual Studio */ +# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ +# define FORCE_INLINE static __forceinline +#else +# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ +# ifdef __GNUC__ +# define FORCE_INLINE static inline __attribute__((always_inline)) +# else +# define FORCE_INLINE static inline +# endif +# else +# define FORCE_INLINE static +# endif /* __STDC_VERSION__ */ +#endif + + +/* ************************************* +* Basic Types +***************************************/ +#ifndef MEM_MODULE +# if !defined (__VMS) && (defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) +# include + typedef uint8_t BYTE; + typedef uint16_t U16; + typedef uint32_t U32; + typedef int32_t S32; +# else + typedef unsigned char BYTE; + typedef unsigned short U16; + typedef unsigned int U32; + typedef signed int S32; +# endif +#endif + +#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2)) + +/* Force direct memory access. Only works on CPU which support unaligned memory access in hardware */ +static U32 XXH_read32(const void* memPtr) { return *(const U32*) memPtr; } + +#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1)) + +/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */ +/* currently only defined for gcc and icc */ +typedef union { U32 u32; } __attribute__((packed)) unalign; +static U32 XXH_read32(const void* ptr) { return ((const unalign*)ptr)->u32; } + +#else + +/* portable and safe solution. Generally efficient. + * see : http://stackoverflow.com/a/32095106/646947 + */ +static U32 XXH_read32(const void* memPtr) +{ + U32 val; + memcpy(&val, memPtr, sizeof(val)); + return val; +} + +#endif /* XXH_FORCE_DIRECT_MEMORY_ACCESS */ + + +/* **************************************** +* Compiler-specific Functions and Macros +******************************************/ +#define XXH_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) + +/* Note : although _rotl exists for minGW (GCC under windows), performance seems poor */ +#if defined(_MSC_VER) +# define XXH_rotl32(x,r) _rotl(x,r) +# define XXH_rotl64(x,r) _rotl64(x,r) +#else +# define XXH_rotl32(x,r) ((x << r) | (x >> (32 - r))) +# define XXH_rotl64(x,r) ((x << r) | (x >> (64 - r))) +#endif + +#if defined(_MSC_VER) /* Visual Studio */ +# define XXH_swap32 _byteswap_ulong +#elif XXH_GCC_VERSION >= 403 +# define XXH_swap32 __builtin_bswap32 +#else +static U32 XXH_swap32 (U32 x) +{ + return ((x << 24) & 0xff000000 ) | + ((x << 8) & 0x00ff0000 ) | + ((x >> 8) & 0x0000ff00 ) | + ((x >> 24) & 0x000000ff ); +} +#endif + + +/* ************************************* +* Architecture Macros +***************************************/ +typedef enum { XXH_bigEndian=0, XXH_littleEndian=1 } XXH_endianess; + +/* XXH_CPU_LITTLE_ENDIAN can be defined externally, for example on the compiler command line */ +#ifndef XXH_CPU_LITTLE_ENDIAN + static const int g_one = 1; +# define XXH_CPU_LITTLE_ENDIAN (*(const char*)(&g_one)) +#endif + + +/* *************************** +* Memory reads +*****************************/ +typedef enum { XXH_aligned, XXH_unaligned } XXH_alignment; + +FORCE_INLINE U32 XXH_readLE32_align(const void* ptr, XXH_endianess endian, XXH_alignment align) +{ + if (align==XXH_unaligned) + return endian==XXH_littleEndian ? XXH_read32(ptr) : XXH_swap32(XXH_read32(ptr)); + else + return endian==XXH_littleEndian ? *(const U32*)ptr : XXH_swap32(*(const U32*)ptr); +} + +FORCE_INLINE U32 XXH_readLE32(const void* ptr, XXH_endianess endian) +{ + return XXH_readLE32_align(ptr, endian, XXH_unaligned); +} + +static U32 XXH_readBE32(const void* ptr) +{ + return XXH_CPU_LITTLE_ENDIAN ? XXH_swap32(XXH_read32(ptr)) : XXH_read32(ptr); +} + + +/* ************************************* +* Macros +***************************************/ +#define XXH_STATIC_ASSERT(c) { enum { XXH_static_assert = 1/(int)(!!(c)) }; } /* use only *after* variable declarations */ +XXH_PUBLIC_API unsigned XXH_versionNumber (void) { return XXH_VERSION_NUMBER; } + + +/* ******************************************************************* +* 32-bits hash functions +*********************************************************************/ +static const U32 PRIME32_1 = 2654435761U; +static const U32 PRIME32_2 = 2246822519U; +static const U32 PRIME32_3 = 3266489917U; +static const U32 PRIME32_4 = 668265263U; +static const U32 PRIME32_5 = 374761393U; + +static U32 XXH32_round(U32 seed, U32 input) +{ + seed += input * PRIME32_2; + seed = XXH_rotl32(seed, 13); + seed *= PRIME32_1; + return seed; +} + +FORCE_INLINE U32 XXH32_endian_align(const void* input, size_t len, U32 seed, XXH_endianess endian, XXH_alignment align) +{ + const BYTE* p = (const BYTE*)input; + const BYTE* bEnd = p + len; + U32 h32; +#define XXH_get32bits(p) XXH_readLE32_align(p, endian, align) + +#ifdef XXH_ACCEPT_NULL_INPUT_POINTER + if (p==NULL) { + len=0; + bEnd=p=(const BYTE*)(size_t)16; + } +#endif + + if (len>=16) { + const BYTE* const limit = bEnd - 16; + U32 v1 = seed + PRIME32_1 + PRIME32_2; + U32 v2 = seed + PRIME32_2; + U32 v3 = seed + 0; + U32 v4 = seed - PRIME32_1; + + do { + v1 = XXH32_round(v1, XXH_get32bits(p)); p+=4; + v2 = XXH32_round(v2, XXH_get32bits(p)); p+=4; + v3 = XXH32_round(v3, XXH_get32bits(p)); p+=4; + v4 = XXH32_round(v4, XXH_get32bits(p)); p+=4; + } while (p<=limit); + + h32 = XXH_rotl32(v1, 1) + XXH_rotl32(v2, 7) + XXH_rotl32(v3, 12) + XXH_rotl32(v4, 18); + } else { + h32 = seed + PRIME32_5; + } + + h32 += (U32) len; + + while (p+4<=bEnd) { + h32 += XXH_get32bits(p) * PRIME32_3; + h32 = XXH_rotl32(h32, 17) * PRIME32_4 ; + p+=4; + } + + while (p> 15; + h32 *= PRIME32_2; + h32 ^= h32 >> 13; + h32 *= PRIME32_3; + h32 ^= h32 >> 16; + + return h32; +} + + +XXH_PUBLIC_API unsigned int XXH32 (const void* input, size_t len, unsigned int seed) +{ +#if 0 + /* Simple version, good for code maintenance, but unfortunately slow for small inputs */ + XXH32_state_t state; + XXH32_reset(&state, seed); + XXH32_update(&state, input, len); + return XXH32_digest(&state); +#else + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if (XXH_FORCE_ALIGN_CHECK) { + if ((((size_t)input) & 3) == 0) { /* Input is 4-bytes aligned, leverage the speed benefit */ + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned); + else + return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned); + } } + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned); + else + return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned); +#endif +} + + + +/*====== Hash streaming ======*/ + +XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void) +{ + return (XXH32_state_t*)XXH_malloc(sizeof(XXH32_state_t)); +} +XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr) +{ + XXH_free(statePtr); + return XXH_OK; +} + +XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dstState, const XXH32_state_t* srcState) +{ + memcpy(dstState, srcState, sizeof(*dstState)); +} + +XXH_PUBLIC_API XXH_errorcode XXH32_reset(XXH32_state_t* statePtr, unsigned int seed) +{ + XXH32_state_t state; /* using a local state to memcpy() in order to avoid strict-aliasing warnings */ + memset(&state, 0, sizeof(state)-4); /* do not write into reserved, for future removal */ + state.v1 = seed + PRIME32_1 + PRIME32_2; + state.v2 = seed + PRIME32_2; + state.v3 = seed + 0; + state.v4 = seed - PRIME32_1; + memcpy(statePtr, &state, sizeof(state)); + return XXH_OK; +} + + +FORCE_INLINE XXH_errorcode XXH32_update_endian (XXH32_state_t* state, const void* input, size_t len, XXH_endianess endian) +{ + const BYTE* p = (const BYTE*)input; + const BYTE* const bEnd = p + len; + +#ifdef XXH_ACCEPT_NULL_INPUT_POINTER + if (input==NULL) return XXH_ERROR; +#endif + + state->total_len_32 += (unsigned)len; + state->large_len |= (len>=16) | (state->total_len_32>=16); + + if (state->memsize + len < 16) { /* fill in tmp buffer */ + XXH_memcpy((BYTE*)(state->mem32) + state->memsize, input, len); + state->memsize += (unsigned)len; + return XXH_OK; + } + + if (state->memsize) { /* some data left from previous update */ + XXH_memcpy((BYTE*)(state->mem32) + state->memsize, input, 16-state->memsize); + { const U32* p32 = state->mem32; + state->v1 = XXH32_round(state->v1, XXH_readLE32(p32, endian)); p32++; + state->v2 = XXH32_round(state->v2, XXH_readLE32(p32, endian)); p32++; + state->v3 = XXH32_round(state->v3, XXH_readLE32(p32, endian)); p32++; + state->v4 = XXH32_round(state->v4, XXH_readLE32(p32, endian)); p32++; + } + p += 16-state->memsize; + state->memsize = 0; + } + + if (p <= bEnd-16) { + const BYTE* const limit = bEnd - 16; + U32 v1 = state->v1; + U32 v2 = state->v2; + U32 v3 = state->v3; + U32 v4 = state->v4; + + do { + v1 = XXH32_round(v1, XXH_readLE32(p, endian)); p+=4; + v2 = XXH32_round(v2, XXH_readLE32(p, endian)); p+=4; + v3 = XXH32_round(v3, XXH_readLE32(p, endian)); p+=4; + v4 = XXH32_round(v4, XXH_readLE32(p, endian)); p+=4; + } while (p<=limit); + + state->v1 = v1; + state->v2 = v2; + state->v3 = v3; + state->v4 = v4; + } + + if (p < bEnd) { + XXH_memcpy(state->mem32, p, (size_t)(bEnd-p)); + state->memsize = (unsigned)(bEnd-p); + } + + return XXH_OK; +} + +XXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* state_in, const void* input, size_t len) +{ + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH32_update_endian(state_in, input, len, XXH_littleEndian); + else + return XXH32_update_endian(state_in, input, len, XXH_bigEndian); +} + + + +FORCE_INLINE U32 XXH32_digest_endian (const XXH32_state_t* state, XXH_endianess endian) +{ + const BYTE * p = (const BYTE*)state->mem32; + const BYTE* const bEnd = (const BYTE*)(state->mem32) + state->memsize; + U32 h32; + + if (state->large_len) { + h32 = XXH_rotl32(state->v1, 1) + XXH_rotl32(state->v2, 7) + XXH_rotl32(state->v3, 12) + XXH_rotl32(state->v4, 18); + } else { + h32 = state->v3 /* == seed */ + PRIME32_5; + } + + h32 += state->total_len_32; + + while (p+4<=bEnd) { + h32 += XXH_readLE32(p, endian) * PRIME32_3; + h32 = XXH_rotl32(h32, 17) * PRIME32_4; + p+=4; + } + + while (p> 15; + h32 *= PRIME32_2; + h32 ^= h32 >> 13; + h32 *= PRIME32_3; + h32 ^= h32 >> 16; + + return h32; +} + + +XXH_PUBLIC_API unsigned int XXH32_digest (const XXH32_state_t* state_in) +{ + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH32_digest_endian(state_in, XXH_littleEndian); + else + return XXH32_digest_endian(state_in, XXH_bigEndian); +} + + +/*====== Canonical representation ======*/ + +/*! Default XXH result types are basic unsigned 32 and 64 bits. +* The canonical representation follows human-readable write convention, aka big-endian (large digits first). +* These functions allow transformation of hash result into and from its canonical format. +* This way, hash values can be written into a file or buffer, and remain comparable across different systems and programs. +*/ + +XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash) +{ + XXH_STATIC_ASSERT(sizeof(XXH32_canonical_t) == sizeof(XXH32_hash_t)); + if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap32(hash); + memcpy(dst, &hash, sizeof(*dst)); +} + +XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src) +{ + return XXH_readBE32(src); +} + + +#ifndef XXH_NO_LONG_LONG + +/* ******************************************************************* +* 64-bits hash functions +*********************************************************************/ + +/*====== Memory access ======*/ + +#ifndef MEM_MODULE +# define MEM_MODULE +# if !defined (__VMS) && (defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) +# include + typedef uint64_t U64; +# else + typedef unsigned long long U64; /* if your compiler doesn't support unsigned long long, replace by another 64-bit type here. Note that xxhash.h will also need to be updated. */ +# endif +#endif + + +#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2)) + +/* Force direct memory access. Only works on CPU which support unaligned memory access in hardware */ +static U64 XXH_read64(const void* memPtr) { return *(const U64*) memPtr; } + +#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1)) + +/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */ +/* currently only defined for gcc and icc */ +typedef union { U32 u32; U64 u64; } __attribute__((packed)) unalign64; +static U64 XXH_read64(const void* ptr) { return ((const unalign64*)ptr)->u64; } + +#else + +/* portable and safe solution. Generally efficient. + * see : http://stackoverflow.com/a/32095106/646947 + */ + +static U64 XXH_read64(const void* memPtr) +{ + U64 val; + memcpy(&val, memPtr, sizeof(val)); + return val; +} + +#endif /* XXH_FORCE_DIRECT_MEMORY_ACCESS */ + +#if defined(_MSC_VER) /* Visual Studio */ +# define XXH_swap64 _byteswap_uint64 +#elif XXH_GCC_VERSION >= 403 +# define XXH_swap64 __builtin_bswap64 +#else +static U64 XXH_swap64 (U64 x) +{ + return ((x << 56) & 0xff00000000000000ULL) | + ((x << 40) & 0x00ff000000000000ULL) | + ((x << 24) & 0x0000ff0000000000ULL) | + ((x << 8) & 0x000000ff00000000ULL) | + ((x >> 8) & 0x00000000ff000000ULL) | + ((x >> 24) & 0x0000000000ff0000ULL) | + ((x >> 40) & 0x000000000000ff00ULL) | + ((x >> 56) & 0x00000000000000ffULL); +} +#endif + +FORCE_INLINE U64 XXH_readLE64_align(const void* ptr, XXH_endianess endian, XXH_alignment align) +{ + if (align==XXH_unaligned) + return endian==XXH_littleEndian ? XXH_read64(ptr) : XXH_swap64(XXH_read64(ptr)); + else + return endian==XXH_littleEndian ? *(const U64*)ptr : XXH_swap64(*(const U64*)ptr); +} + +FORCE_INLINE U64 XXH_readLE64(const void* ptr, XXH_endianess endian) +{ + return XXH_readLE64_align(ptr, endian, XXH_unaligned); +} + +static U64 XXH_readBE64(const void* ptr) +{ + return XXH_CPU_LITTLE_ENDIAN ? XXH_swap64(XXH_read64(ptr)) : XXH_read64(ptr); +} + + +/*====== xxh64 ======*/ + +static const U64 PRIME64_1 = 11400714785074694791ULL; +static const U64 PRIME64_2 = 14029467366897019727ULL; +static const U64 PRIME64_3 = 1609587929392839161ULL; +static const U64 PRIME64_4 = 9650029242287828579ULL; +static const U64 PRIME64_5 = 2870177450012600261ULL; + +static U64 XXH64_round(U64 acc, U64 input) +{ + acc += input * PRIME64_2; + acc = XXH_rotl64(acc, 31); + acc *= PRIME64_1; + return acc; +} + +static U64 XXH64_mergeRound(U64 acc, U64 val) +{ + val = XXH64_round(0, val); + acc ^= val; + acc = acc * PRIME64_1 + PRIME64_4; + return acc; +} + +FORCE_INLINE U64 XXH64_endian_align(const void* input, size_t len, U64 seed, XXH_endianess endian, XXH_alignment align) +{ + const BYTE* p = (const BYTE*)input; + const BYTE* bEnd = p + len; + U64 h64; +#define XXH_get64bits(p) XXH_readLE64_align(p, endian, align) + +#ifdef XXH_ACCEPT_NULL_INPUT_POINTER + if (p==NULL) { + len=0; + bEnd=p=(const BYTE*)(size_t)32; + } +#endif + + if (len>=32) { + const BYTE* const limit = bEnd - 32; + U64 v1 = seed + PRIME64_1 + PRIME64_2; + U64 v2 = seed + PRIME64_2; + U64 v3 = seed + 0; + U64 v4 = seed - PRIME64_1; + + do { + v1 = XXH64_round(v1, XXH_get64bits(p)); p+=8; + v2 = XXH64_round(v2, XXH_get64bits(p)); p+=8; + v3 = XXH64_round(v3, XXH_get64bits(p)); p+=8; + v4 = XXH64_round(v4, XXH_get64bits(p)); p+=8; + } while (p<=limit); + + h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18); + h64 = XXH64_mergeRound(h64, v1); + h64 = XXH64_mergeRound(h64, v2); + h64 = XXH64_mergeRound(h64, v3); + h64 = XXH64_mergeRound(h64, v4); + + } else { + h64 = seed + PRIME64_5; + } + + h64 += (U64) len; + + while (p+8<=bEnd) { + U64 const k1 = XXH64_round(0, XXH_get64bits(p)); + h64 ^= k1; + h64 = XXH_rotl64(h64,27) * PRIME64_1 + PRIME64_4; + p+=8; + } + + if (p+4<=bEnd) { + h64 ^= (U64)(XXH_get32bits(p)) * PRIME64_1; + h64 = XXH_rotl64(h64, 23) * PRIME64_2 + PRIME64_3; + p+=4; + } + + while (p> 33; + h64 *= PRIME64_2; + h64 ^= h64 >> 29; + h64 *= PRIME64_3; + h64 ^= h64 >> 32; + + return h64; +} + + +XXH_PUBLIC_API unsigned long long XXH64 (const void* input, size_t len, unsigned long long seed) +{ +#if 0 + /* Simple version, good for code maintenance, but unfortunately slow for small inputs */ + XXH64_state_t state; + XXH64_reset(&state, seed); + XXH64_update(&state, input, len); + return XXH64_digest(&state); +#else + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if (XXH_FORCE_ALIGN_CHECK) { + if ((((size_t)input) & 7)==0) { /* Input is aligned, let's leverage the speed advantage */ + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH64_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned); + else + return XXH64_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned); + } } + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH64_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned); + else + return XXH64_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned); +#endif +} + +/*====== Hash Streaming ======*/ + +XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void) +{ + return (XXH64_state_t*)XXH_malloc(sizeof(XXH64_state_t)); +} +XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr) +{ + XXH_free(statePtr); + return XXH_OK; +} + +XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* dstState, const XXH64_state_t* srcState) +{ + memcpy(dstState, srcState, sizeof(*dstState)); +} + +XXH_PUBLIC_API XXH_errorcode XXH64_reset(XXH64_state_t* statePtr, unsigned long long seed) +{ + XXH64_state_t state; /* using a local state to memcpy() in order to avoid strict-aliasing warnings */ + memset(&state, 0, sizeof(state)-8); /* do not write into reserved, for future removal */ + state.v1 = seed + PRIME64_1 + PRIME64_2; + state.v2 = seed + PRIME64_2; + state.v3 = seed + 0; + state.v4 = seed - PRIME64_1; + memcpy(statePtr, &state, sizeof(state)); + return XXH_OK; +} + +FORCE_INLINE XXH_errorcode XXH64_update_endian (XXH64_state_t* state, const void* input, size_t len, XXH_endianess endian) +{ + const BYTE* p = (const BYTE*)input; + const BYTE* const bEnd = p + len; + +#ifdef XXH_ACCEPT_NULL_INPUT_POINTER + if (input==NULL) return XXH_ERROR; +#endif + + state->total_len += len; + + if (state->memsize + len < 32) { /* fill in tmp buffer */ + XXH_memcpy(((BYTE*)state->mem64) + state->memsize, input, len); + state->memsize += (U32)len; + return XXH_OK; + } + + if (state->memsize) { /* tmp buffer is full */ + XXH_memcpy(((BYTE*)state->mem64) + state->memsize, input, 32-state->memsize); + state->v1 = XXH64_round(state->v1, XXH_readLE64(state->mem64+0, endian)); + state->v2 = XXH64_round(state->v2, XXH_readLE64(state->mem64+1, endian)); + state->v3 = XXH64_round(state->v3, XXH_readLE64(state->mem64+2, endian)); + state->v4 = XXH64_round(state->v4, XXH_readLE64(state->mem64+3, endian)); + p += 32-state->memsize; + state->memsize = 0; + } + + if (p+32 <= bEnd) { + const BYTE* const limit = bEnd - 32; + U64 v1 = state->v1; + U64 v2 = state->v2; + U64 v3 = state->v3; + U64 v4 = state->v4; + + do { + v1 = XXH64_round(v1, XXH_readLE64(p, endian)); p+=8; + v2 = XXH64_round(v2, XXH_readLE64(p, endian)); p+=8; + v3 = XXH64_round(v3, XXH_readLE64(p, endian)); p+=8; + v4 = XXH64_round(v4, XXH_readLE64(p, endian)); p+=8; + } while (p<=limit); + + state->v1 = v1; + state->v2 = v2; + state->v3 = v3; + state->v4 = v4; + } + + if (p < bEnd) { + XXH_memcpy(state->mem64, p, (size_t)(bEnd-p)); + state->memsize = (unsigned)(bEnd-p); + } + + return XXH_OK; +} + +XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH64_state_t* state_in, const void* input, size_t len) +{ + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH64_update_endian(state_in, input, len, XXH_littleEndian); + else + return XXH64_update_endian(state_in, input, len, XXH_bigEndian); +} + +FORCE_INLINE U64 XXH64_digest_endian (const XXH64_state_t* state, XXH_endianess endian) +{ + const BYTE * p = (const BYTE*)state->mem64; + const BYTE* const bEnd = (const BYTE*)state->mem64 + state->memsize; + U64 h64; + + if (state->total_len >= 32) { + U64 const v1 = state->v1; + U64 const v2 = state->v2; + U64 const v3 = state->v3; + U64 const v4 = state->v4; + + h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18); + h64 = XXH64_mergeRound(h64, v1); + h64 = XXH64_mergeRound(h64, v2); + h64 = XXH64_mergeRound(h64, v3); + h64 = XXH64_mergeRound(h64, v4); + } else { + h64 = state->v3 + PRIME64_5; + } + + h64 += (U64) state->total_len; + + while (p+8<=bEnd) { + U64 const k1 = XXH64_round(0, XXH_readLE64(p, endian)); + h64 ^= k1; + h64 = XXH_rotl64(h64,27) * PRIME64_1 + PRIME64_4; + p+=8; + } + + if (p+4<=bEnd) { + h64 ^= (U64)(XXH_readLE32(p, endian)) * PRIME64_1; + h64 = XXH_rotl64(h64, 23) * PRIME64_2 + PRIME64_3; + p+=4; + } + + while (p> 33; + h64 *= PRIME64_2; + h64 ^= h64 >> 29; + h64 *= PRIME64_3; + h64 ^= h64 >> 32; + + return h64; +} + +XXH_PUBLIC_API unsigned long long XXH64_digest (const XXH64_state_t* state_in) +{ + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH64_digest_endian(state_in, XXH_littleEndian); + else + return XXH64_digest_endian(state_in, XXH_bigEndian); +} + + +/*====== Canonical representation ======*/ + +XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash) +{ + XXH_STATIC_ASSERT(sizeof(XXH64_canonical_t) == sizeof(XXH64_hash_t)); + if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap64(hash); + memcpy(dst, &hash, sizeof(*dst)); +} + +XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src) +{ + return XXH_readBE64(src); +} + +#endif /* XXH_NO_LONG_LONG */ diff --git a/src/libraries/xxHash/xxhash.h b/src/libraries/xxHash/xxhash.h new file mode 100755 index 000000000..9d831e03b --- /dev/null +++ b/src/libraries/xxHash/xxhash.h @@ -0,0 +1,293 @@ +/* + xxHash - Extremely Fast Hash algorithm + Header File + Copyright (C) 2012-2016, Yann Collet. + + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + You can contact the author at : + - xxHash source repository : https://github.com/Cyan4973/xxHash +*/ + +/* Notice extracted from xxHash homepage : + +xxHash is an extremely fast Hash algorithm, running at RAM speed limits. +It also successfully passes all tests from the SMHasher suite. + +Comparison (single thread, Windows Seven 32 bits, using SMHasher on a Core 2 Duo @3GHz) + +Name Speed Q.Score Author +xxHash 5.4 GB/s 10 +CrapWow 3.2 GB/s 2 Andrew +MumurHash 3a 2.7 GB/s 10 Austin Appleby +SpookyHash 2.0 GB/s 10 Bob Jenkins +SBox 1.4 GB/s 9 Bret Mulvey +Lookup3 1.2 GB/s 9 Bob Jenkins +SuperFastHash 1.2 GB/s 1 Paul Hsieh +CityHash64 1.05 GB/s 10 Pike & Alakuijala +FNV 0.55 GB/s 5 Fowler, Noll, Vo +CRC32 0.43 GB/s 9 +MD5-32 0.33 GB/s 10 Ronald L. Rivest +SHA1-32 0.28 GB/s 10 + +Q.Score is a measure of quality of the hash function. +It depends on successfully passing SMHasher test set. +10 is a perfect score. + +A 64-bits version, named XXH64, is available since r35. +It offers much better speed, but for 64-bits applications only. +Name Speed on 64 bits Speed on 32 bits +XXH64 13.8 GB/s 1.9 GB/s +XXH32 6.8 GB/s 6.0 GB/s +*/ + +#ifndef XXHASH_H_5627135585666179 +#define XXHASH_H_5627135585666179 1 + +#if defined (__cplusplus) +extern "C" { +#endif + + +/* **************************** +* Definitions +******************************/ +#include /* size_t */ +typedef enum { XXH_OK=0, XXH_ERROR } XXH_errorcode; + + +/* **************************** +* API modifier +******************************/ +/** XXH_PRIVATE_API +* This is useful to include xxhash functions in `static` mode +* in order to inline them, and remove their symbol from the public list. +* Methodology : +* #define XXH_PRIVATE_API +* #include "xxhash.h" +* `xxhash.c` is automatically included. +* It's not useful to compile and link it as a separate module. +*/ +#ifdef XXH_PRIVATE_API +# ifndef XXH_STATIC_LINKING_ONLY +# define XXH_STATIC_LINKING_ONLY +# endif +# if defined(__GNUC__) +# define XXH_PUBLIC_API static __inline __attribute__((unused)) +# elif defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) +# define XXH_PUBLIC_API static inline +# elif defined(_MSC_VER) +# define XXH_PUBLIC_API static __inline +# else +# define XXH_PUBLIC_API static /* this version may generate warnings for unused static functions; disable the relevant warning */ +# endif +#else +# define XXH_PUBLIC_API /* do nothing */ +#endif /* XXH_PRIVATE_API */ + +/*!XXH_NAMESPACE, aka Namespace Emulation : + +If you want to include _and expose_ xxHash functions from within your own library, +but also want to avoid symbol collisions with other libraries which may also include xxHash, + +you can use XXH_NAMESPACE, to automatically prefix any public symbol from xxhash library +with the value of XXH_NAMESPACE (therefore, avoid NULL and numeric values). + +Note that no change is required within the calling program as long as it includes `xxhash.h` : +regular symbol name will be automatically translated by this header. +*/ +#ifdef XXH_NAMESPACE +# define XXH_CAT(A,B) A##B +# define XXH_NAME2(A,B) XXH_CAT(A,B) +# define XXH_versionNumber XXH_NAME2(XXH_NAMESPACE, XXH_versionNumber) +# define XXH32 XXH_NAME2(XXH_NAMESPACE, XXH32) +# define XXH32_createState XXH_NAME2(XXH_NAMESPACE, XXH32_createState) +# define XXH32_freeState XXH_NAME2(XXH_NAMESPACE, XXH32_freeState) +# define XXH32_reset XXH_NAME2(XXH_NAMESPACE, XXH32_reset) +# define XXH32_update XXH_NAME2(XXH_NAMESPACE, XXH32_update) +# define XXH32_digest XXH_NAME2(XXH_NAMESPACE, XXH32_digest) +# define XXH32_copyState XXH_NAME2(XXH_NAMESPACE, XXH32_copyState) +# define XXH32_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH32_canonicalFromHash) +# define XXH32_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH32_hashFromCanonical) +# define XXH64 XXH_NAME2(XXH_NAMESPACE, XXH64) +# define XXH64_createState XXH_NAME2(XXH_NAMESPACE, XXH64_createState) +# define XXH64_freeState XXH_NAME2(XXH_NAMESPACE, XXH64_freeState) +# define XXH64_reset XXH_NAME2(XXH_NAMESPACE, XXH64_reset) +# define XXH64_update XXH_NAME2(XXH_NAMESPACE, XXH64_update) +# define XXH64_digest XXH_NAME2(XXH_NAMESPACE, XXH64_digest) +# define XXH64_copyState XXH_NAME2(XXH_NAMESPACE, XXH64_copyState) +# define XXH64_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH64_canonicalFromHash) +# define XXH64_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH64_hashFromCanonical) +#endif + + +/* ************************************* +* Version +***************************************/ +#define XXH_VERSION_MAJOR 0 +#define XXH_VERSION_MINOR 6 +#define XXH_VERSION_RELEASE 2 +#define XXH_VERSION_NUMBER (XXH_VERSION_MAJOR *100*100 + XXH_VERSION_MINOR *100 + XXH_VERSION_RELEASE) +XXH_PUBLIC_API unsigned XXH_versionNumber (void); + + +/*-********************************************************************** +* 32-bits hash +************************************************************************/ +typedef unsigned int XXH32_hash_t; + +/*! XXH32() : + Calculate the 32-bits hash of sequence "length" bytes stored at memory address "input". + The memory between input & input+length must be valid (allocated and read-accessible). + "seed" can be used to alter the result predictably. + Speed on Core 2 Duo @ 3 GHz (single thread, SMHasher benchmark) : 5.4 GB/s */ +XXH_PUBLIC_API XXH32_hash_t XXH32 (const void* input, size_t length, unsigned int seed); + +/*====== Streaming ======*/ +typedef struct XXH32_state_s XXH32_state_t; /* incomplete type */ +XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void); +XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr); +XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dst_state, const XXH32_state_t* src_state); + +XXH_PUBLIC_API XXH_errorcode XXH32_reset (XXH32_state_t* statePtr, unsigned int seed); +XXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* statePtr, const void* input, size_t length); +XXH_PUBLIC_API XXH32_hash_t XXH32_digest (const XXH32_state_t* statePtr); + +/* +These functions generate the xxHash of an input provided in multiple segments. +Note that, for small input, they are slower than single-call functions, due to state management. +For small input, prefer `XXH32()` and `XXH64()` . + +XXH state must first be allocated, using XXH*_createState() . + +Start a new hash by initializing state with a seed, using XXH*_reset(). + +Then, feed the hash state by calling XXH*_update() as many times as necessary. +Obviously, input must be allocated and read accessible. +The function returns an error code, with 0 meaning OK, and any other value meaning there is an error. + +Finally, a hash value can be produced anytime, by using XXH*_digest(). +This function returns the nn-bits hash as an int or long long. + +It's still possible to continue inserting input into the hash state after a digest, +and generate some new hashes later on, by calling again XXH*_digest(). + +When done, free XXH state space if it was allocated dynamically. +*/ + +/*====== Canonical representation ======*/ + +typedef struct { unsigned char digest[4]; } XXH32_canonical_t; +XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash); +XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src); + +/* Default result type for XXH functions are primitive unsigned 32 and 64 bits. +* The canonical representation uses human-readable write convention, aka big-endian (large digits first). +* These functions allow transformation of hash result into and from its canonical format. +* This way, hash values can be written into a file / memory, and remain comparable on different systems and programs. +*/ + + +#ifndef XXH_NO_LONG_LONG +/*-********************************************************************** +* 64-bits hash +************************************************************************/ +typedef unsigned long long XXH64_hash_t; + +/*! XXH64() : + Calculate the 64-bits hash of sequence of length "len" stored at memory address "input". + "seed" can be used to alter the result predictably. + This function runs faster on 64-bits systems, but slower on 32-bits systems (see benchmark). +*/ +XXH_PUBLIC_API XXH64_hash_t XXH64 (const void* input, size_t length, unsigned long long seed); + +/*====== Streaming ======*/ +typedef struct XXH64_state_s XXH64_state_t; /* incomplete type */ +XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void); +XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr); +XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* dst_state, const XXH64_state_t* src_state); + +XXH_PUBLIC_API XXH_errorcode XXH64_reset (XXH64_state_t* statePtr, unsigned long long seed); +XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH64_state_t* statePtr, const void* input, size_t length); +XXH_PUBLIC_API XXH64_hash_t XXH64_digest (const XXH64_state_t* statePtr); + +/*====== Canonical representation ======*/ +typedef struct { unsigned char digest[8]; } XXH64_canonical_t; +XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash); +XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src); +#endif /* XXH_NO_LONG_LONG */ + + +#ifdef XXH_STATIC_LINKING_ONLY + +/* ================================================================================================ + This section contains definitions which are not guaranteed to remain stable. + They may change in future versions, becoming incompatible with a different version of the library. + They shall only be used with static linking. + Never use these definitions in association with dynamic linking ! +=================================================================================================== */ + +/* These definitions are only meant to make possible + static allocation of XXH state, on stack or in a struct for example. + Never use members directly. */ + +struct XXH32_state_s { + unsigned total_len_32; + unsigned large_len; + unsigned v1; + unsigned v2; + unsigned v3; + unsigned v4; + unsigned mem32[4]; /* buffer defined as U32 for alignment */ + unsigned memsize; + unsigned reserved; /* never read nor write, will be removed in a future version */ +}; /* typedef'd to XXH32_state_t */ + +#ifndef XXH_NO_LONG_LONG /* remove 64-bits support */ +struct XXH64_state_s { + unsigned long long total_len; + unsigned long long v1; + unsigned long long v2; + unsigned long long v3; + unsigned long long v4; + unsigned long long mem64[4]; /* buffer defined as U64 for alignment */ + unsigned memsize; + unsigned reserved[2]; /* never read nor write, will be removed in a future version */ +}; /* typedef'd to XXH64_state_t */ +#endif + +#ifdef XXH_PRIVATE_API +# include "xxhash.c" /* include xxhash function bodies as `static`, for inlining */ +#endif + +#endif /* XXH_STATIC_LINKING_ONLY */ + + +#if defined (__cplusplus) +} +#endif + +#endif /* XXHASH_H_5627135585666179 */ diff --git a/src/modules/event/sdl/Event.cpp b/src/modules/event/sdl/Event.cpp index 50de0a7a4..7d38818cc 100644 --- a/src/modules/event/sdl/Event.cpp +++ b/src/modules/event/sdl/Event.cpp @@ -112,6 +112,8 @@ Event::~Event() void Event::pump() { + exceptionIfInRenderPass(); + SDL_Event e; while (SDL_PollEvent(&e)) @@ -127,6 +129,8 @@ void Event::pump() Message *Event::wait() { + exceptionIfInRenderPass(); + SDL_Event e; if (SDL_WaitEvent(&e) != 1) @@ -137,6 +141,8 @@ Message *Event::wait() void Event::clear() { + exceptionIfInRenderPass(); + SDL_Event e; while (SDL_PollEvent(&e)) @@ -147,6 +153,17 @@ void Event::clear() love::event::Event::clear(); } +void Event::exceptionIfInRenderPass() +{ + // Some core OS graphics functionality (e.g. swap buffers on some platforms) + // happens inside SDL_PumpEvents - which is called by SDL_PollEvent and + // friends. It's probably a bad idea to call those functions while we're in + // a render pass. + auto gfx = Module::getInstance(Module::M_GRAPHICS); + if (gfx != nullptr && gfx->isPassActive()) + throw love::Exception("Cannot call this function while a render pass is active in love.graphics."); +} + Message *Event::convert(const SDL_Event &e) const { Message *msg = nullptr; diff --git a/src/modules/event/sdl/Event.h b/src/modules/event/sdl/Event.h index 9ab44174e..978dd4566 100644 --- a/src/modules/event/sdl/Event.h +++ b/src/modules/event/sdl/Event.h @@ -68,6 +68,8 @@ public: private: + void exceptionIfInRenderPass(); + Message *convert(const SDL_Event &e) const; Message *convertJoystickEvent(const SDL_Event &e) const; Message *convertWindowEvent(const SDL_Event &e) const; diff --git a/src/modules/event/wrap_Event.cpp b/src/modules/event/wrap_Event.cpp index 06b9686f6..908000032 100644 --- a/src/modules/event/wrap_Event.cpp +++ b/src/modules/event/wrap_Event.cpp @@ -53,15 +53,16 @@ int w_poll(lua_State *L) return 1; } -int w_pump(lua_State *) +int w_pump(lua_State *L) { - instance()->pump(); + luax_catchexcept(L, [&]() { instance()->pump(); }); return 0; } int w_wait(lua_State *L) { - Message *m = instance()->wait(); + Message *m = nullptr; + luax_catchexcept(L, [&]() { m = instance()->wait(); }); if (m) { int args = m->toLua(L); @@ -85,9 +86,9 @@ int w_push(lua_State *L) return 1; } -int w_clear(lua_State *) +int w_clear(lua_State *L) { - instance()->clear(); + luax_catchexcept(L, [&]() { instance()->clear(); }); return 0; } diff --git a/src/modules/graphics/Graphics.h b/src/modules/graphics/Graphics.h index 06e55db5e..cc713e2e4 100644 --- a/src/modules/graphics/Graphics.h +++ b/src/modules/graphics/Graphics.h @@ -34,6 +34,8 @@ namespace love namespace graphics { +const int MAX_COLOR_RENDER_TARGETS = 16; + /** * Globally sets whether gamma correction is enabled. Ideally this should be set * prior to using any Graphics module function. @@ -59,6 +61,14 @@ void gammaCorrectColor(Colorf &c); **/ void unGammaCorrectColor(Colorf &c); +class RenderOutsidePassException : public love::Exception +{ +public: + RenderOutsidePassException() + : Exception("Cannot draw outside of a render pass!") + {} +}; + class Graphics : public Module { public: @@ -178,7 +188,7 @@ public: struct Stats { int drawCalls; - int canvasSwitches; + int renderPasses; int shaderSwitches; int canvases; int images; @@ -257,6 +267,8 @@ public: **/ virtual bool isActive() const = 0; + virtual bool isPassActive() const = 0; + static bool getConstant(const char *in, DrawMode &out); static bool getConstant(DrawMode in, const char *&out); diff --git a/src/modules/graphics/opengl/Canvas.cpp b/src/modules/graphics/opengl/Canvas.cpp index 51d80bbc3..ffafa4182 100644 --- a/src/modules/graphics/opengl/Canvas.cpp +++ b/src/modules/graphics/opengl/Canvas.cpp @@ -36,11 +36,10 @@ namespace opengl static GLenum createFBO(GLuint &framebuffer, GLuint texture) { // get currently bound fbo to reset to it later - GLint current_fbo; - glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, ¤t_fbo); + GLuint current_fbo = gl.getFramebuffer(OpenGL::FRAMEBUFFER_ALL); glGenFramebuffers(1, &framebuffer); - gl.bindFramebuffer(GL_FRAMEBUFFER, framebuffer); + gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, framebuffer); if (texture != 0) { @@ -53,14 +52,19 @@ static GLenum createFBO(GLuint &framebuffer, GLuint texture) GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - // unbind framebuffer - gl.bindFramebuffer(GL_FRAMEBUFFER, (GLuint) current_fbo); - + gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, current_fbo); return status; } -static GLenum createMSAABuffer(int width, int height, int &samples, GLenum iformat, GLuint &buffer) +static bool createMSAABuffer(int width, int height, int &samples, GLenum iformat, GLuint &buffer) { + GLuint current_fbo = gl.getFramebuffer(OpenGL::FRAMEBUFFER_ALL); + + // Temporary FBO used to clear the renderbuffer. + GLuint fbo = 0; + glGenFramebuffers(1, &fbo); + gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, fbo); + glGenRenderbuffers(1, &buffer); glBindRenderbuffer(GL_RENDERBUFFER, buffer); @@ -73,7 +77,7 @@ static GLenum createMSAABuffer(int width, int height, int &samples, GLenum iform GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - if (status == GL_FRAMEBUFFER_COMPLETE) + if (status == GL_FRAMEBUFFER_COMPLETE && samples > 1) { // Initialize the buffer to transparent black. glClearColor(0.0f, 0.0f, 0.0f, 0.0f); @@ -83,22 +87,21 @@ static GLenum createMSAABuffer(int width, int height, int &samples, GLenum iform { glDeleteRenderbuffers(1, &buffer); buffer = 0; + samples = 0; } - return status; + gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, current_fbo); + gl.deleteFramebuffer(fbo); + + return status == GL_FRAMEBUFFER_COMPLETE && samples > 1; } -Canvas *Canvas::current = nullptr; -OpenGL::Viewport Canvas::systemViewport = OpenGL::Viewport(); -bool Canvas::screenHasSRGB = false; int Canvas::canvasCount = 0; Canvas::Canvas(int width, int height, Format format, int msaa) : fbo(0) - , resolve_fbo(0) , texture(0) , msaa_buffer(0) - , depth_stencil(0) , format(format) , requested_samples(msaa) , actual_samples(0) @@ -143,65 +146,15 @@ Canvas::Canvas(int width, int height, Format format, int msaa) Canvas::~Canvas() { --canvasCount; - - // reset framebuffer if still using this one - if (current == this) - stopGrab(); - unloadVolatile(); } -bool Canvas::createMSAAFBO(GLenum internalformat) -{ - actual_samples = requested_samples; - - if (actual_samples <= 1) - { - actual_samples = 0; - return false; - } - - // Create our FBO without a texture. - status = createFBO(fbo, 0); - - GLuint previous = gl.getDefaultFBO(); - if (current != this) - { - if (current != nullptr) - previous = current->fbo; - - gl.bindFramebuffer(GL_FRAMEBUFFER, fbo); - } - - // Create and attach the MSAA buffer for our FBO. - status = createMSAABuffer(width, height, actual_samples, internalformat, msaa_buffer); - - // Create the FBO used for the MSAA resolve, and attach the texture. - if (status == GL_FRAMEBUFFER_COMPLETE) - status = createFBO(resolve_fbo, texture); - - if (status != GL_FRAMEBUFFER_COMPLETE) - { - // Clean up. - glDeleteFramebuffers(1, &fbo); - glDeleteFramebuffers(1, &resolve_fbo); - glDeleteRenderbuffers(1, &msaa_buffer); - fbo = msaa_buffer = resolve_fbo = 0; - actual_samples = 0; - } - - if (current != this) - gl.bindFramebuffer(GL_FRAMEBUFFER, previous); - - return status == GL_FRAMEBUFFER_COMPLETE; -} - bool Canvas::loadVolatile() { OpenGL::TempDebugGroup debuggroup("Canvas load"); - fbo = depth_stencil = texture = 0; - resolve_fbo = msaa_buffer = 0; + fbo = texture = 0; + msaa_buffer = 0; status = GL_FRAMEBUFFER_COMPLETE; // glTexImage2D is guaranteed to error in this case. @@ -239,8 +192,8 @@ bool Canvas::loadVolatile() while (glGetError() != GL_NO_ERROR) /* Clear the error buffer. */; - glTexImage2D(GL_TEXTURE_2D, 0, iformat, width, height, 0, - externalformat, textype, nullptr); + glTexImage2D(GL_TEXTURE_2D, 0, iformat, width, height, 0, externalformat, + textype, nullptr); if (glGetError() != GL_NO_ERROR) { @@ -250,24 +203,27 @@ bool Canvas::loadVolatile() return false; } - // Try to create a MSAA FBO if requested. On failure (or no requested MSAA), - // fall back to a regular FBO. - if (!createMSAAFBO(internalformat)) - status = createFBO(fbo, texture); + // Create a canvas-local FBO used for glReadPixels as well as MSAA blitting. + status = createFBO(fbo, texture); if (status != GL_FRAMEBUFFER_COMPLETE) - { - if (fbo != 0) - { - glDeleteFramebuffers(1, &fbo); - fbo = 0; - } + { + if (fbo != 0) + { + gl.deleteFramebuffer(fbo); + fbo = 0; + } return false; - } + } + + actual_samples = requested_samples == 1 ? 0 : requested_samples; + + if (actual_samples > 0 && !createMSAABuffer(width, height, actual_samples, internalformat, msaa_buffer)) + actual_samples = 0; size_t prevmemsize = texture_memory; - texture_memory = (getFormatBitsPerPixel(format) * width * height) / 8; + texture_memory = ((getFormatBitsPerPixel(format) * width) / 8) * height; if (msaa_buffer != 0) texture_memory += (texture_memory * actual_samples); @@ -278,33 +234,35 @@ bool Canvas::loadVolatile() void Canvas::unloadVolatile() { - glDeleteFramebuffers(1, &fbo); - glDeleteFramebuffers(1, &resolve_fbo); + if (fbo != 0) + gl.deleteFramebuffer(fbo); - glDeleteRenderbuffers(1, &depth_stencil); - glDeleteRenderbuffers(1, &msaa_buffer); + if (msaa_buffer != 0) + glDeleteRenderbuffers(1, &msaa_buffer); - gl.deleteTexture(texture); + if (texture != 0) + gl.deleteTexture(texture); fbo = 0; - resolve_fbo = 0; - depth_stencil = 0; msaa_buffer = 0; texture = 0; - attachedCanvases.clear(); - gl.updateTextureMemorySize(texture_memory, 0); texture_memory = 0; } void Canvas::drawv(const Matrix4 &t, const Vertex *v) { - // FIXME: This doesn't handle cases where the Canvas is used as a texture - // in a SpriteBatch, Mesh, or ParticleSystem, or when the Canvas is used in - // a shader as a non-default texture. - if (Canvas::current == this) - throw love::Exception("Cannot draw a Canvas to itself."); + auto gfx = Module::getInstance(Module::M_GRAPHICS); + if (gfx != nullptr) + { + const PassInfo &info = gfx->getActivePass(); + for (const auto &attachment : info.colorAttachments) + { + if (attachment.canvas == this) + throw love::Exception("Cannot render a Canvas to itself!"); + } + } OpenGL::TempDebugGroup debuggroup("Canvas draw"); @@ -377,320 +335,6 @@ const void *Canvas::getHandle() const return &texture; } -void Canvas::setupGrab() -{ - // already grabbing - if (current == this) - return; - - // cleanup after previous Canvas - if (current != nullptr) - { - systemViewport = current->systemViewport; - current->stopGrab(true); - } - else - systemViewport = gl.getViewport(); - - // indicate we are using this Canvas. - current = this; - - // bind the framebuffer object. - gl.bindFramebuffer(GL_FRAMEBUFFER, fbo); - gl.setViewport({0, 0, width, height}); - - // Set up the projection matrix - gl.matrices.projection.push_back(Matrix4::ortho(0.0, (float) width, 0.0, (float) height)); -} - -void Canvas::startGrab(const std::vector &canvases) -{ - // Whether the new canvas list is different from the old one. - // A more thorough check is done below. - bool canvaseschanged = canvases.size() != attachedCanvases.size(); - bool hasSRGBcanvas = getSizedFormat(format) == FORMAT_SRGB; - - if (canvases.size() > 0) - { - if ((int) canvases.size() + 1 > gl.getMaxRenderTargets()) - throw love::Exception("This system can't simultaneously render to %d canvases.", canvases.size()+1); - - if (actual_samples != 0) - throw love::Exception("Multi-canvas rendering is not supported with MSAA."); - } - - bool multiformatsupported = isMultiFormatMultiCanvasSupported(); - - for (size_t i = 0; i < canvases.size(); i++) - { - if (canvases[i]->getWidth() != width || canvases[i]->getHeight() != height) - throw love::Exception("All canvases must have the same dimensions."); - - Format otherformat = canvases[i]->getTextureFormat(); - - if (otherformat != format && !multiformatsupported) - throw love::Exception("This system doesn't support multi-canvas rendering with different canvas formats."); - - if (canvases[i]->getMSAA() != 0) - throw love::Exception("Multi-canvas rendering is not supported with MSAA."); - - if (!canvaseschanged && canvases[i] != attachedCanvases[i]) - canvaseschanged = true; - - if (getSizedFormat(otherformat) == FORMAT_SRGB) - hasSRGBcanvas = true; - } - - OpenGL::TempDebugGroup debuggroup("Canvas set"); - - setupGrab(); - - // Make sure the correct sRGB setting is used when drawing to the canvases. - if (GLAD_VERSION_1_0 || GLAD_EXT_sRGB_write_control) - { - if (hasSRGBcanvas && !gl.hasFramebufferSRGB()) - gl.setFramebufferSRGB(true); - else if (!hasSRGBcanvas && gl.hasFramebufferSRGB()) - gl.setFramebufferSRGB(false); - } - - // Don't attach anything if there's nothing to change. - if (!canvaseschanged) - return; - - // Attach the canvas textures to the active FBO and set up MRTs. - std::vector drawbuffers; - drawbuffers.reserve(canvases.size() + 1); - - drawbuffers.push_back(GL_COLOR_ATTACHMENT0); - - // Attach the canvas textures to the currently bound framebuffer. - for (int i = 0; i < (int) canvases.size(); i++) - { - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1 + i, - GL_TEXTURE_2D, *(GLuint *) canvases[i]->getHandle(), 0); - - drawbuffers.push_back(GL_COLOR_ATTACHMENT1 + i); - } - - // set up multiple render targets - glDrawBuffers((int) drawbuffers.size(), &drawbuffers[0]); - - // We want to avoid reference cycles, so we don't retain the attached - // Canvases here. The code in Graphics::setCanvas retains them. - - attachedCanvases = canvases; -} - -void Canvas::startGrab() -{ - OpenGL::TempDebugGroup debuggroup("Canvas set"); - - setupGrab(); - - // Make sure the correct sRGB setting is used when drawing to the canvas. - if (GLAD_VERSION_1_0 || GLAD_EXT_sRGB_write_control) - { - bool isSRGB = getSizedFormat(format) == FORMAT_SRGB; - if (isSRGB && !gl.hasFramebufferSRGB()) - gl.setFramebufferSRGB(true); - else if (!isSRGB && gl.hasFramebufferSRGB()) - gl.setFramebufferSRGB(false); - } - - if (attachedCanvases.size() > 0) - { - // Make sure the FBO is only using a single draw buffer. - // GLES3 only has glDrawBuffers, so we avoid using glDrawBuffer. - const GLenum buffers[] = {GL_COLOR_ATTACHMENT0}; - glDrawBuffers(1, buffers); - - attachedCanvases.clear(); - } -} - -void Canvas::stopGrab(bool switchingToOtherCanvas) -{ - // i am not grabbing. leave me alone - if (current != this) - return; - - OpenGL::TempDebugGroup debuggroup("Canvas un-set"); - - // Make sure the canvas texture is up to date if we're using MSAA. - resolveMSAA(false); - - if (gl.matrices.projection.size() > 1) - gl.matrices.projection.pop_back(); - - if (!switchingToOtherCanvas) - { - // bind system framebuffer. - gl.bindFramebuffer(GL_FRAMEBUFFER, gl.getDefaultFBO()); - current = nullptr; - gl.setViewport(systemViewport); - - if (GLAD_VERSION_1_0 || GLAD_EXT_sRGB_write_control) - { - if (screenHasSRGB && !gl.hasFramebufferSRGB()) - gl.setFramebufferSRGB(true); - else if (!screenHasSRGB && gl.hasFramebufferSRGB()) - gl.setFramebufferSRGB(false); - } - } -} - -bool Canvas::checkCreateStencil() -{ - // Do nothing if we've already created the stencil buffer. - if (depth_stencil != 0) - return true; - - OpenGL::TempDebugGroup debuggroup("Canvas create stencil"); - - if (current != this) - gl.bindFramebuffer(GL_FRAMEBUFFER, fbo); - - GLenum format = GL_STENCIL_INDEX8; - std::vector attachments = {GL_STENCIL_ATTACHMENT}; - - // Prefer a combined depth/stencil buffer. - if (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object) - { - format = GL_DEPTH24_STENCIL8; - attachments = {GL_DEPTH_STENCIL_ATTACHMENT}; - } - else if (GLAD_EXT_packed_depth_stencil || GLAD_OES_packed_depth_stencil) - { - format = GL_DEPTH24_STENCIL8; - attachments = {GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT}; - } - - glGenRenderbuffers(1, &depth_stencil); - glBindRenderbuffer(GL_RENDERBUFFER, depth_stencil); - - if (requested_samples > 1) - glRenderbufferStorageMultisample(GL_RENDERBUFFER, requested_samples, format, width, height); - else - glRenderbufferStorage(GL_RENDERBUFFER, format, width, height); - - // Attach the buffer to the framebuffer object. - for (GLenum attachment : attachments) - glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, depth_stencil); - - glBindRenderbuffer(GL_RENDERBUFFER, 0); - - bool success = glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE; - - // We don't want the stencil buffer filled with garbage. - if (success) - glClear(GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - else - { - glDeleteRenderbuffers(1, &depth_stencil); - depth_stencil = 0; - } - - if (current && current != this) - gl.bindFramebuffer(GL_FRAMEBUFFER, current->fbo); - else if (!current) - gl.bindFramebuffer(GL_FRAMEBUFFER, gl.getDefaultFBO()); - - return success; -} - -love::image::ImageData *Canvas::newImageData(love::image::Image *image, int x, int y, int w, int h) -{ - if (x < 0 || y < 0 || w <= 0 || h <= 0 || (x + w) > width || (y + h) > height) - throw love::Exception("Invalid ImageData rectangle dimensions."); - - GLenum datatype = GL_UNSIGNED_BYTE; - image::ImageData::Format imageformat = image::ImageData::FORMAT_RGBA8; - - switch (getSizedFormat(format)) - { - case FORMAT_RGB10A2: // FIXME: Conversions aren't supported in GLES - datatype = GL_UNSIGNED_SHORT; - imageformat = image::ImageData::FORMAT_RGBA16; - break; - case FORMAT_R16F: - case FORMAT_RG16F: - case FORMAT_RGBA16F: - case FORMAT_RG11B10F: // FIXME: Conversions aren't supported in GLES - datatype = GL_HALF_FLOAT; - imageformat = image::ImageData::FORMAT_RGBA16F; - break; - case FORMAT_R32F: - case FORMAT_RG32F: - case FORMAT_RGBA32F: - datatype = GL_FLOAT; - imageformat = image::ImageData::FORMAT_RGBA32F; - break; - default: - break; - } - - size_t size = w * h * image::ImageData::getPixelSize(imageformat); - uint8 *pixels = nullptr; - try - { - pixels = new uint8[size]; - } - catch (std::bad_alloc &) - { - throw love::Exception("Out of memory."); - } - - // Make sure the canvas texture is up to date if we're using MSAA. - if (current == this) - resolveMSAA(false); - - // Our texture is attached to 'resolve_fbo' when we use MSAA. - if (resolve_fbo != 0) - gl.bindFramebuffer(GL_READ_FRAMEBUFFER, resolve_fbo); - else - gl.bindFramebuffer(GL_FRAMEBUFFER, fbo); - - glReadPixels(x, y, w, h, GL_RGBA, datatype, pixels); - - GLuint prevfbo = current ? current->fbo : gl.getDefaultFBO(); - gl.bindFramebuffer(GL_FRAMEBUFFER, prevfbo); - - // The new ImageData now owns the pixel data, so we don't delete it here. - return image->newImageData(w, h, imageformat, pixels, true); -} - -bool Canvas::resolveMSAA(bool restoreprev) -{ - if (resolve_fbo == 0 || msaa_buffer == 0) - return false; - - OpenGL::TempDebugGroup debuggroup("Canvas MSAA resolve"); - - GLint w = width; - GLint h = height; - - // Do the MSAA resolve by blitting the MSAA renderbuffer to the texture. - // For many of the MSAA extensions that add suffixes to the functions, we - // assign function pointers in OpenGL.cpp so we can call the core functions. - - gl.bindFramebuffer(GL_READ_FRAMEBUFFER, fbo); - gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, resolve_fbo); - - if (GLAD_APPLE_framebuffer_multisample) - glResolveMultisampleFramebufferAPPLE(); - else - glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST); - - if (restoreprev) - { - GLuint fbo = current ? current->fbo : gl.getDefaultFBO(); - gl.bindFramebuffer(GL_FRAMEBUFFER, fbo); - } - - return true; -} - Canvas::Format Canvas::getSizedFormat(Canvas::Format format) { switch (format) @@ -949,7 +593,7 @@ bool Canvas::isFormatSupported(Canvas::Format format) GLuint fbo = 0; supported = (createFBO(fbo, texture) == GL_FRAMEBUFFER_COMPLETE); - glDeleteFramebuffers(1, &fbo); + gl.deleteFramebuffer(fbo); gl.deleteTexture(texture); diff --git a/src/modules/graphics/opengl/Canvas.h b/src/modules/graphics/opengl/Canvas.h index 03d829bd9..e67800c62 100644 --- a/src/modules/graphics/opengl/Canvas.h +++ b/src/modules/graphics/opengl/Canvas.h @@ -82,26 +82,6 @@ public: bool setWrap(const Texture::Wrap &w) override; const void *getHandle() const override; - /** - * @param canvases A list of other canvases to temporarily attach to this one, - * to allow drawing to multiple canvases at once. - **/ - void startGrab(const std::vector &canvases); - void startGrab(); - void stopGrab(bool switchingToOtherCanvas = false); - - /** - * Create and attach a stencil buffer to this Canvas' framebuffer, if necessary. - **/ - bool checkCreateStencil(); - - love::image::ImageData *newImageData(love::image::Image *image, int x, int y, int w, int h); - - inline const std::vector &getAttachedCanvases() const - { - return attachedCanvases; - } - inline GLenum getStatus() const { return status; @@ -117,19 +97,26 @@ public: return actual_samples; } + inline int getRequestedMSAA() const + { + return requested_samples; + } + + inline ptrdiff_t getMSAAHandle() const + { + return msaa_buffer; + } + + inline GLuint getFBO() const + { + return fbo; + } + static Format getSizedFormat(Format format); static bool isSupported(); static bool isMultiFormatMultiCanvasSupported(); static bool isFormatSupported(Format format); - static Canvas *current; - - // The viewport dimensions of the system (default) framebuffer. - static OpenGL::Viewport systemViewport; - - // Whether the main screen should have linear -> sRGB conversions enabled. - static bool screenHasSRGB; - static int canvasCount; static bool getConstant(const char *in, Format &out); @@ -137,29 +124,20 @@ public: private: - void setupGrab(); - - bool createMSAAFBO(GLenum internalformat); - bool resolveMSAA(bool restoreprev); - void drawv(const Matrix4 &t, const Vertex *v); static void convertFormat(Format format, GLenum &internalformat, GLenum &externalformat, GLenum &type); static size_t getFormatBitsPerPixel(Format format); GLuint fbo; - GLuint resolve_fbo; GLuint texture; GLuint msaa_buffer; - GLuint depth_stencil; Format format; GLenum status; - std::vector attachedCanvases; - int requested_samples; int actual_samples; diff --git a/src/modules/graphics/opengl/Graphics.cpp b/src/modules/graphics/opengl/Graphics.cpp index 7635853cf..fcc5f1694 100644 --- a/src/modules/graphics/opengl/Graphics.cpp +++ b/src/modules/graphics/opengl/Graphics.cpp @@ -27,6 +27,9 @@ #include "font/Font.h" #include "Polyline.h" #include "math/MathModule.h" +#include "window/Window.h" + +#include "libraries/xxHash/xxhash.h" // C++ #include @@ -50,28 +53,34 @@ namespace opengl { Graphics::Graphics() - : currentWindow(Module::getInstance(Module::M_WINDOW)) - , quadIndices(nullptr) + : quadIndices(nullptr) , width(0) , height(0) , created(false) , active(true) + , canCaptureScreenshot(true) + , currentPass() , writingToStencil(false) + , renderPassCount(0) { gl = OpenGL(); states.reserve(10); states.push_back(DisplayState()); - if (currentWindow.get()) + auto window = getInstance(M_WINDOW); + + if (window != nullptr) { - int w, h; - love::window::WindowSettings wsettings; + window->setGraphics(this); - currentWindow->getWindow(w, h, wsettings); + if (window->isOpen()) + { + int w = 0, h = 0; + window->getPixelDimensions(w, h); - if (currentWindow->isOpen()) setMode(w, h); + } } } @@ -123,7 +132,6 @@ void Graphics::restoreState(const DisplayState &s) setFont(s.font.get()); setShader(s.shader.get()); - setCanvas(s.canvases); setColorMask(s.colorMask); setWireframe(s.wireframe); @@ -166,20 +174,6 @@ void Graphics::restoreStateChecked(const DisplayState &s) setFont(s.font.get()); setShader(s.shader.get()); - bool canvaseschanged = s.canvases.size() != cur.canvases.size(); - - for (size_t i = 0; i < s.canvases.size() && i < cur.canvases.size(); i++) - { - if (s.canvases[i].get() != cur.canvases[i].get()) - { - canvaseschanged = true; - break; - } - } - - if (canvaseschanged) - setCanvas(s.canvases); - if (s.colorMask != cur.colorMask) setColorMask(s.colorMask); @@ -217,32 +211,18 @@ void Graphics::setViewportSize(int width, int height) this->width = width; this->height = height; - if (!isCreated()) - return; + if (currentPass.active && currentPass.info.colorAttachmentCount == 0) + { + // Set the viewport to top-left corner. + gl.setViewport({0, 0, width, height}, false); - // We want to affect the main screen, not any Canvas that's currently active - // (not that any *should* be active when this is called.) - std::vector> canvases = states.back().canvases; - setCanvas(); - - // Set the viewport to top-left corner. - gl.setViewport({0, 0, width, height}); - - // If a canvas was bound before this function was called, it needs to be - // made aware of the new system viewport size. - Canvas::systemViewport = gl.getViewport(); - - // Set up the projection matrix - gl.matrices.projection.back() = Matrix4::ortho(0.0, (float) width, (float) height, 0.0); - - // Restore the previously active Canvas. - setCanvas(canvases); + // Set up the projection matrix + gl.matrices.projection.back() = Matrix4::ortho(0.0, (float) width, (float) height, 0.0); + } } bool Graphics::setMode(int width, int height) { - currentWindow.set(Module::getInstance(Module::M_WINDOW)); - this->width = width; this->height = height; @@ -282,8 +262,6 @@ bool Graphics::setMode(int width, int height) else setGammaCorrect(false); - Canvas::screenHasSRGB = isGammaCorrect(); - bool enabledebug = false; if (GLAD_VERSION_3_0) @@ -350,6 +328,15 @@ void Graphics::unSetMode() // mode change. Volatile::unloadAll(); + for (const auto &pair : framebufferObjects) + gl.deleteFramebuffer(pair.second); + + for (const CachedRenderbuffer &rb : stencilBuffers) + glDeleteRenderbuffers(1, &rb.renderbuffer); + + framebufferObjects.clear(); + stencilBuffers.clear(); + gl.deInitContext(); created = false; @@ -369,7 +356,8 @@ bool Graphics::isActive() const { // The graphics module is only completely 'active' if there's a window, a // context, and the active variable is set. - return active && isCreated() && currentWindow.get() && currentWindow->isOpen(); + auto window = getInstance(M_WINDOW); + return active && isCreated() && window != nullptr && window->isOpen(); } static void APIENTRY debugCB(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei /*len*/, const GLchar *msg, const GLvoid* /*usr*/) @@ -439,91 +427,181 @@ void Graphics::reset() origin(); } -void Graphics::clear(Colorf c) +void Graphics::beginPass(PassInfo::BeginAction beginAction, Colorf clearColor) { - gammaCorrectColor(c); + if (currentPass.active) + throw love::Exception("Cannot call beginPass while another render pass is active!"); - glClearColor(c.r, c.g, c.b, c.a); - glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + currentPass.active = true; - if (gl.bugs.clearRequiresDriverTextureStateUpdate && Shader::current) + OpenGL::TempDebugGroup debuggroup("Render Pass begin"); + + gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, gl.getDefaultFBO()); + + gl.setViewport({0, 0, width, height}, false); + + // The projection matrix is flipped compared to rendering to a canvas, due + // to OpenGL considering (0,0) bottom-left instead of top-left. + gl.matrices.projection.back() = Matrix4::ortho(0.0, (float) width, (float) height, 0.0); + + if (GLAD_VERSION_1_0 || GLAD_EXT_sRGB_write_control) { - // This seems to be enough to fix the bug for me. Other methods I've - // tried (e.g. dummy draws) don't work in all cases. - gl.useProgram(0); - gl.useProgram(Shader::current->getProgram()); + if (isGammaCorrect() && !gl.hasFramebufferSRGB()) + gl.setFramebufferSRGB(true); + else if (!isGammaCorrect() && gl.hasFramebufferSRGB()) + gl.setFramebufferSRGB(false); } + + // Always clear the stencil buffer, for now. + GLbitfield clearflags = GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT; + + if (beginAction == PassInfo::BEGIN_CLEAR) + { + Colorf c = clearColor; + gammaCorrectColor(c); + + glClearColor(c.r, c.g, c.b, c.a); + clearflags |= GL_COLOR_BUFFER_BIT; + } + + if (clearflags != 0) + glClear(clearflags); + + PassInfo info; + info.colorAttachmentCount = 0; + info.stencil = true; + + currentPass.info = info; + + renderPassCount++; + + canCaptureScreenshot = false; } -void Graphics::clear(const std::vector &colors) +void Graphics::beginPass(const PassInfo &info) { - if (colors.size() == 0) - return; + if (info.colorAttachmentCount == 0) + throw love::Exception("At least one Canvas must be specified for an off-screen render pass."); - size_t numcanvases = states.back().canvases.size(); + if (currentPass.active) + throw love::Exception("Cannot call beginPass while another render pass is active!"); - if (numcanvases > 0 && colors.size() != numcanvases) - throw love::Exception("Number of clear colors must match the number of active canvases (%ld)", states.back().canvases.size()); + if (info.colorAttachmentCount > gl.getMaxRenderTargets()) + throw love::Exception("This system can't simultaneously render to %d canvases.", info.colorAttachmentCount); - // We want to take the single-color codepath if there's no active Canvas, or - // if there's only one active Canvas. The multi-color codepath (in the loop - // below) assumes MRT functions are available, and also may call more - // expensive GL functions which are unnecessary if only one Canvas is active. - if (numcanvases <= 1) + Canvas *firstcanvas = info.colorAttachments[0].canvas; + + bool multiformatsupported = isSupported(Feature::FEATURE_MULTI_CANVAS_FORMATS); + Canvas::Format firstformat = firstcanvas->getTextureFormat(); + + bool hasSRGBcanvas = Canvas::getSizedFormat(firstformat) == Canvas::FORMAT_SRGB; + + for (int i = 1; i < info.colorAttachmentCount; i++) { - if (colors[0].enabled) - clear(colors[0].toColor()); + Canvas *c = info.colorAttachments[i].canvas; - return; + if (c->getWidth() != firstcanvas->getWidth() || c->getHeight() != firstcanvas->getHeight()) + throw love::Exception("All canvases in a render pass must have the same dimensions."); + + if (!multiformatsupported && c->getTextureFormat() != firstformat) + throw love::Exception("This system doesn't support multi-canvas rendering with different canvas formats."); + + if (c->getRequestedMSAA() != firstcanvas->getRequestedMSAA()) + throw love::Exception("All Canvases in a render pass must have the same requested MSAA value."); + + if (Canvas::getSizedFormat(c->getTextureFormat()) == Canvas::FORMAT_SRGB) + hasSRGBcanvas = true; } - bool drawbuffermodified = false; + OpenGL::TempDebugGroup debuggroup("Render Pass begin"); - for (int i = 0; i < (int) colors.size(); i++) + bindCachedFBOForPass(info); + + currentPass.active = true; + + int w = firstcanvas->getWidth(); + int h = firstcanvas->getHeight(); + + gl.setViewport({0, 0, w, h}, true); + gl.matrices.projection.back() = Matrix4::ortho(0.0, (float) w, 0.0, (float) h); + + // Make sure the correct sRGB setting is used when drawing to the canvases. + if (GLAD_VERSION_1_0 || GLAD_EXT_sRGB_write_control) { - if (!colors[i].enabled) - continue; - - GLfloat c[] = {colors[i].r, colors[i].g, colors[i].b, colors[i].a}; - - // TODO: Investigate a potential bug on AMD drivers in Windows/Linux - // which apparently causes the clear color to be incorrect when mixed - // sRGB and linear render targets are active. - if (isGammaCorrect()) - { - for (int i = 0; i < 3; i++) - c[i] = math::gammaToLinear(c[i]); - } - - if (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0) - glClearBufferfv(GL_COLOR, i, c); - else - { - glDrawBuffer(GL_COLOR_ATTACHMENT0 + i); - glClearColor(c[0], c[1], c[2], c[3]); - glClear(GL_COLOR_BUFFER_BIT); - - drawbuffermodified = true; - } + if (hasSRGBcanvas && !gl.hasFramebufferSRGB()) + gl.setFramebufferSRGB(true); + else if (!hasSRGBcanvas && gl.hasFramebufferSRGB()) + gl.setFramebufferSRGB(false); } - glClear(GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + GLbitfield clearflags = 0; - // Revert to the expected draw buffers once we're done, if glClearBuffer - // wasn't supported. - if (drawbuffermodified) + // Take a single-color clear codepath if there's only one specified Canvas. + // The multi-color codepath (in the loop below) assumes MRT functions are + // available, and also may call more expensive GL functions which are + // unnecessary if only one Canvas is used. + if (info.colorAttachmentCount <= 1) { - std::vector bufs; + if (info.colorAttachmentCount > 0 && info.colorAttachments[0].beginAction == PassInfo::BEGIN_CLEAR) + { + clearflags |= GL_COLOR_BUFFER_BIT; + Colorf c = info.colorAttachments[0].clearColor; + gammaCorrectColor(c); + glClearColor(c.r, c.g, c.b, c.a); + } + } + else + { + bool drawbuffermodified = false; - for (int i = 0; i < (int) states.back().canvases.size(); i++) - bufs.push_back(GL_COLOR_ATTACHMENT0 + i); + for (int i = 0; i < info.colorAttachmentCount; i++) + { + if (info.colorAttachments[i].beginAction == PassInfo::BEGIN_CLEAR) + { + Colorf c = info.colorAttachments[i].clearColor; + gammaCorrectColor(c); + + if (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0) + { + const GLfloat carray[] = {c.r, c.g, c.b, c.a}; + glClearBufferfv(GL_COLOR, i, carray); + } + else + { + glDrawBuffer(GL_COLOR_ATTACHMENT0 + i); + glClearColor(c.r, c.g, c.b, c.a); + glClear(GL_COLOR_BUFFER_BIT); + + drawbuffermodified = true; + } + } + } + + // Revert to the expected draw buffers once we're done, if glClearBuffer + // wasn't supported. + if (drawbuffermodified) + { + std::vector bufs; + + for (int i = 0; i < info.colorAttachmentCount; i++) + bufs.push_back(GL_COLOR_ATTACHMENT0 + i); - if (bufs.size() > 1) glDrawBuffers((int) bufs.size(), &bufs[0]); - else - glDrawBuffer(GL_COLOR_ATTACHMENT0); + } } + if (info.stencil) + clearflags |= GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT; + + if (clearflags != 0) + glClear(clearflags); + + for (int i = 0; i < info.colorAttachmentCount; i++) + info.colorAttachments[i].canvas->retain(); + + currentPass.info = info; + renderPassCount++; + if (gl.bugs.clearRequiresDriverTextureStateUpdate && Shader::current) { // This seems to be enough to fix the bug for me. Other methods I've @@ -533,21 +611,146 @@ void Graphics::clear(const std::vector &colors) } } -void Graphics::discard(const std::vector &colorbuffers, bool stencil) +void Graphics::endPass() +{ + endPass(0, 0, 0, 0, nullptr, nullptr); +} + +void Graphics::endPass(int sX, int sY, int sW, int sH, const ScreenshotInfo *info, void *screenshotCallbackData) +{ + if (!currentPass.active) + return; // Should this error instead? + + StrongRef imagedata; + + auto &attachments = currentPass.info.colorAttachments; + int attachmentcount = currentPass.info.colorAttachmentCount; + + if (info != nullptr) + { + if (attachmentcount == 0) + throw love::Exception("Use captureScreenshot to capture the main screen's contents."); + + if (sX < 0 || sY < 0 || sW <= 0 || sH <= 0 || (sX + sW) > getPassWidth() || (sY + sH) > getPassHeight()) + throw love::Exception("Invalid rectangle dimensions."); + + auto imagemodule = Module::getInstance(M_IMAGE); + + if (imagemodule == nullptr) + throw love::Exception("The love.image module must be loaded to capture a Canvas' contents."); + + image::ImageData::Format format; + switch (Canvas::getSizedFormat(attachments[0].canvas->getTextureFormat())) + { + case Canvas::FORMAT_RGB10A2: // FIXME: Conversions aren't supported in GLES + format = image::ImageData::FORMAT_RGBA16; + break; + case Canvas::FORMAT_R16F: + case Canvas::FORMAT_RG16F: + case Canvas::FORMAT_RGBA16F: + case Canvas::FORMAT_RG11B10F: // FIXME: Conversions aren't supported in GLES + format = image::ImageData::FORMAT_RGBA16F; + break; + case Canvas::FORMAT_R32F: + case Canvas::FORMAT_RG32F: + case Canvas::FORMAT_RGBA32F: + format = image::ImageData::FORMAT_RGBA32F; + break; + default: + format = image::ImageData::FORMAT_RGBA8; + break; + } + + imagedata.set(imagemodule->newImageData(sW, sH, format), Acquire::NORETAIN); + } + + if (currentPass.info.stencil) + discard(OpenGL::FRAMEBUFFER_ALL, {}, true); + + if (attachmentcount > 0 && attachments[0].canvas->getMSAA() > 1) + { + int w = attachments[0].canvas->getWidth(); + int h = attachments[0].canvas->getHeight(); + + for (int i = 0; i < attachmentcount; i++) + { + glReadBuffer(GL_COLOR_ATTACHMENT0 + i); + + gl.bindFramebuffer(OpenGL::FRAMEBUFFER_DRAW, attachments[i].canvas->getFBO()); + + if (GLAD_APPLE_framebuffer_multisample) + glResolveMultisampleFramebufferAPPLE(); + else + glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST); + } + } + + if (info != nullptr) + { + if (attachments[0].canvas->getMSAA() > 1) + gl.bindFramebuffer(OpenGL::FRAMEBUFFER_READ, attachments[0].canvas->getFBO()); + else if (attachmentcount > 1) + glReadBuffer(GL_COLOR_ATTACHMENT0); + + GLenum datatype; + switch (imagedata->getFormat()) + { + case image::ImageData::FORMAT_RGBA16: + datatype = GL_UNSIGNED_SHORT; + break; + case image::ImageData::FORMAT_RGBA16F: + datatype = GL_HALF_FLOAT; + break; + case image::ImageData::FORMAT_RGBA32F: + datatype = GL_FLOAT; + break; + default: + datatype = GL_UNSIGNED_BYTE; + break; + } + + glReadPixels(sX, sY, sW, sH, GL_RGBA, datatype, imagedata->getData()); + + info->callback(imagedata, info->ref, screenshotCallbackData); + } + + for (int i = 0; i < currentPass.info.colorAttachmentCount; i++) + currentPass.info.colorAttachments[i].canvas->release(); + + currentPass.active = false; +} + +const PassInfo &Graphics::getActivePass() const +{ + return currentPass.info; +} + +bool Graphics::isPassActive() const +{ + return currentPass.active; +} + +void Graphics::discard(OpenGL::FramebufferTarget target, const std::vector &colorbuffers, bool depthstencil) { if (!(GLAD_VERSION_4_3 || GLAD_ARB_invalidate_subdata || GLAD_ES_VERSION_3_0 || GLAD_EXT_discard_framebuffer)) return; + GLenum gltarget = GL_FRAMEBUFFER; + if (target == OpenGL::FRAMEBUFFER_READ) + gltarget = GL_READ_FRAMEBUFFER; + else if (target == OpenGL::FRAMEBUFFER_DRAW) + gltarget = GL_DRAW_FRAMEBUFFER; + std::vector attachments; attachments.reserve(colorbuffers.size()); // glDiscardFramebuffer uses different attachment enums for the default FBO. - if (!Canvas::current && gl.getDefaultFBO() == 0) + if (currentPass.info.colorAttachmentCount == 0 && gl.getDefaultFBO() == 0) { if (colorbuffers.size() > 0 && colorbuffers[0]) attachments.push_back(GL_COLOR); - if (stencil) + if (depthstencil) { attachments.push_back(GL_STENCIL); attachments.push_back(GL_DEPTH); @@ -555,9 +758,7 @@ void Graphics::discard(const std::vector &colorbuffers, bool stencil) } else { - int rendertargetcount = 1; - if (Canvas::current) - rendertargetcount = (int) states.back().canvases.size(); + int rendertargetcount = std::max(currentPass.info.colorAttachmentCount, 1); for (int i = 0; i < (int) colorbuffers.size(); i++) { @@ -565,7 +766,7 @@ void Graphics::discard(const std::vector &colorbuffers, bool stencil) attachments.push_back(GL_COLOR_ATTACHMENT0 + i); } - if (stencil) + if (depthstencil) { attachments.push_back(GL_STENCIL_ATTACHMENT); attachments.push_back(GL_DEPTH_ATTACHMENT); @@ -574,22 +775,264 @@ void Graphics::discard(const std::vector &colorbuffers, bool stencil) // Hint for the driver that it doesn't need to save these buffers. if (GLAD_VERSION_4_3 || GLAD_ARB_invalidate_subdata || GLAD_ES_VERSION_3_0) - glInvalidateFramebuffer(GL_FRAMEBUFFER, (GLint) attachments.size(), &attachments[0]); + glInvalidateFramebuffer(gltarget, (GLint) attachments.size(), &attachments[0]); else if (GLAD_EXT_discard_framebuffer) - glDiscardFramebufferEXT(GL_FRAMEBUFFER, (GLint) attachments.size(), &attachments[0]); + glDiscardFramebufferEXT(gltarget, (GLint) attachments.size(), &attachments[0]); } -void Graphics::present() +void Graphics::bindCachedFBOForPass(const PassInfo &pass) +{ + PassBufferInfo info; + memset(&info, 0, sizeof(PassBufferInfo)); + + info.stencil = pass.stencil; + + int ncanvases = pass.colorAttachmentCount; + + for (int i = 0; i < ncanvases; i++) + info.canvases[i] = pass.colorAttachments[i].canvas; + + uint32 hash = XXH32(&info, offsetof(PassBufferInfo, canvases) + ncanvases * sizeof(Canvas *), 0); + + GLuint fbo = framebufferObjects[hash]; + + if (fbo != 0) + { + gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, fbo); + } + else + { + int w = info.canvases[0]->getWidth(); + int h = info.canvases[0]->getHeight(); + int msaa = std::max(info.canvases[0]->getMSAA(), 1); + + glGenFramebuffers(1, &fbo); + gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, fbo); + + std::vector drawbuffers; + drawbuffers.reserve(ncanvases); + + for (int i = 0; i < ncanvases; i++) + { + drawbuffers.push_back(GL_COLOR_ATTACHMENT0 + i); + + if (msaa > 1) + { + GLuint rbo = (GLuint) info.canvases[i]->getMSAAHandle(); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, drawbuffers[i], GL_RENDERBUFFER, rbo); + } + else + { + GLuint tex = *(GLuint *) info.canvases[i]->getHandle(); + glFramebufferTexture2D(GL_FRAMEBUFFER, drawbuffers[i], GL_TEXTURE_2D, tex, 0); + } + } + + if (drawbuffers.size() > 1) + glDrawBuffers(1, &drawbuffers[0]); + + GLuint stencil = attachCachedStencilBuffer(w, h, msaa); + + if (stencil == 0) + { + gl.deleteFramebuffer(fbo); + gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, gl.getDefaultFBO()); + throw love::Exception("Could not create stencil buffer!"); + } + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + + if (status != GL_FRAMEBUFFER_COMPLETE) + { + gl.deleteFramebuffer(fbo); + const char *sstr = OpenGL::framebufferStatusString(status); + throw love::Exception("Could not create Framebuffer Object! %s", sstr); + } + + framebufferObjects[hash] = fbo; + } +} + +GLuint Graphics::attachCachedStencilBuffer(int w, int h, int samples) +{ + samples = samples == 1 ? 0 : samples; + + for (const CachedRenderbuffer &rb : stencilBuffers) + { + if (rb.w == w && rb.h == h && rb.samples == samples) + { + // Attach the buffer to the framebuffer object. + for (GLenum attachment : rb.attachments) + { + if (attachment != GL_NONE) + glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, rb.renderbuffer); + } + + return rb.renderbuffer; + } + } + + OpenGL::TempDebugGroup debuggroup("Created cached stencil buffer"); + + CachedRenderbuffer rb; + rb.w = w; + rb.h = h; + rb.samples = samples; + + rb.attachments[0] = GL_STENCIL_ATTACHMENT; + rb.attachments[1] = GL_NONE; + + GLenum format = GL_STENCIL_INDEX8; + + // Prefer a combined depth/stencil buffer. + if (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object) + { + format = GL_DEPTH24_STENCIL8; + rb.attachments[0] = GL_DEPTH_STENCIL_ATTACHMENT; + } + else if (GLAD_EXT_packed_depth_stencil || GLAD_OES_packed_depth_stencil) + { + format = GL_DEPTH24_STENCIL8; + rb.attachments[0] = GL_DEPTH_ATTACHMENT; + rb.attachments[1] = GL_STENCIL_ATTACHMENT; + } + + glGenRenderbuffers(1, &rb.renderbuffer); + glBindRenderbuffer(GL_RENDERBUFFER, rb.renderbuffer); + + if (rb.samples > 1) + glRenderbufferStorageMultisample(GL_RENDERBUFFER, rb.samples, format, rb.w, rb.h); + else + glRenderbufferStorage(GL_RENDERBUFFER, format, rb.w, rb.h); + + // Attach the buffer to the framebuffer object. + for (GLenum attachment : rb.attachments) + { + if (attachment != GL_NONE) + glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, rb.renderbuffer); + } + + glBindRenderbuffer(GL_RENDERBUFFER, 0); + + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + { + glDeleteRenderbuffers(1, &rb.renderbuffer); + rb.renderbuffer = 0; + } + + if (rb.renderbuffer != 0) + stencilBuffers.push_back(rb); + + return rb.renderbuffer; +} + +void Graphics::captureScreenshot(const ScreenshotInfo &info) +{ + if (!canCaptureScreenshot) + throw love::Exception("captureScreenshot cannot be called once rendering to the main screen has begun."); + + pendingScreenshotCallbacks.push_back(info); +} + +void Graphics::present(void *screenshotCallbackData) { if (!isActive()) return; - // Make sure we don't have a canvas active. - std::vector> canvases = states.back().canvases; - setCanvas(); + if (currentPass.active) + throw love::Exception("present cannot be called while a render pass is active."); - // Discard the stencil buffer before swapping. - discard({}, true); + gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, gl.getDefaultFBO()); + + if (!pendingScreenshotCallbacks.empty()) + { + int w = getWidth(); + int h = getHeight(); + + size_t row = 4 * w; + size_t size = row * h; + + GLubyte *pixels = nullptr; + GLubyte *screenshot = nullptr; + + try + { + pixels = new GLubyte[size]; + screenshot = new GLubyte[size]; + } + catch (std::exception &) + { + delete[] pixels; + delete[] screenshot; + throw love::Exception("Out of memory."); + } + +#ifdef LOVE_IOS + SDL_SysWMinfo info = {}; + SDL_VERSION(&info.version); + SDL_GetWindowWMInfo(SDL_GL_GetCurrentWindow(), &info); + + if (info.info.uikit.resolveFramebuffer != 0) + { + gl.bindFramebuffer(OpenGL::FRAMEBUFFER_DRAW, info.info.uikit.resolveFramebuffer); + + // We need to do an explicit MSAA resolve on iOS, because it uses GLES + // FBOs rather than a system framebuffer. + if (GLAD_ES_VERSION_3_0) + glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST); + else if (GLAD_APPLE_framebuffer_multisample) + glResolveMultisampleFramebufferAPPLE(); + + gl.bindFramebuffer(OpenGL::FRAMEBUFFER_READ, info.info.uikit.resolveFramebuffer); + } +#endif + + glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + + // Replace alpha values with full opacity. + for (size_t i = 3; i < size; i += 4) + pixels[i] = 255; + + // OpenGL sucks and reads pixels from the lower-left. Let's fix that. + GLubyte *src = pixels - row; + GLubyte *dst = screenshot + size; + + for (int i = 0; i < h; ++i) + memcpy(dst-=row, src+=row, row); + + delete[] pixels; + + auto imagemodule = Module::getInstance(M_IMAGE); + + for (int i = 0; i < (int) pendingScreenshotCallbacks.size(); i++) + { + const auto &info = pendingScreenshotCallbacks[i]; + image::ImageData *img = nullptr; + + try + { + img = imagemodule->newImageData(w, h, image::ImageData::FORMAT_RGBA8, screenshot); + } + catch (love::Exception &) + { + delete[] screenshot; + info.callback(nullptr, info.ref, nullptr); + for (int j = i + 1; j < (int) pendingScreenshotCallbacks.size(); j++) + { + const auto &ninfo = pendingScreenshotCallbacks[j]; + ninfo.callback(nullptr, ninfo.ref, nullptr); + } + pendingScreenshotCallbacks.clear(); + throw; + } + + info.callback(img, info.ref, screenshotCallbackData); + img->release(); + } + + delete[] screenshot; + pendingScreenshotCallbacks.clear(); + } #ifdef LOVE_IOS // Hack: SDL's color renderbuffer must be bound when swapBuffers is called. @@ -599,16 +1042,16 @@ void Graphics::present() glBindRenderbuffer(GL_RENDERBUFFER, info.info.uikit.colorbuffer); #endif - if (currentWindow.get()) - currentWindow->swapBuffers(); - - // Restore the currently active canvas, if there is one. - setCanvas(canvases); + auto window = getInstance(M_WINDOW); + if (window != nullptr) + window->swapBuffers(); // Reset the per-frame stat counts. gl.stats.drawCalls = 0; - gl.stats.framebufferBinds = 0; gl.stats.shaderSwitches = 0; + renderPassCount = 0; + + canCaptureScreenshot = true; } int Graphics::getWidth() const @@ -621,6 +1064,22 @@ int Graphics::getHeight() const return height; } +int Graphics::getPassWidth() const +{ + if (currentPass.active && currentPass.info.colorAttachmentCount > 0) + return currentPass.info.colorAttachments[0].canvas->getWidth(); + else + return width; +} + +int Graphics::getPassHeight() const +{ + if (currentPass.active && currentPass.info.colorAttachmentCount > 0) + return currentPass.info.colorAttachments[0].canvas->getHeight(); + else + return height; +} + bool Graphics::isCreated() const { return created; @@ -632,7 +1091,7 @@ void Graphics::setScissor(int x, int y, int width, int height) glEnable(GL_SCISSOR_TEST); // OpenGL's reversed y-coordinate is compensated for in OpenGL::setScissor. - gl.setScissor({rect.x, rect.y, rect.w, rect.h}); + gl.setScissor({rect.x, rect.y, rect.w, rect.h}, currentPass.info.colorAttachmentCount > 0); states.back().scissor = true; states.back().scissorRect = rect; @@ -679,11 +1138,10 @@ bool Graphics::getScissor(int &x, int &y, int &width, int &height) const void Graphics::drawToStencilBuffer(StencilAction action, int value) { - writingToStencil = true; + if (!currentPass.active || !currentPass.info.stencil) + throw love::Exception("Stenciling must be enabled in the active render pass."); - // Make sure the active canvas has a stencil buffer. - if (Canvas::current) - Canvas::current->checkCreateStencil(); + writingToStencil = true; // Disable color writes but don't save the state for it. glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); @@ -737,6 +1195,9 @@ void Graphics::stopDrawToStencilBuffer() void Graphics::setStencilTest(CompareMode compare, int value) { + if (!currentPass.info.stencil && compare != COMPARE_ALWAYS) + throw love::Exception("Stenciling must be enabled in the active render pass."); + DisplayState &state = states.back(); state.stencilCompare = compare; state.stencilTestValue = value; @@ -750,10 +1211,6 @@ void Graphics::setStencilTest(CompareMode compare, int value) return; } - // Make sure the active canvas has a stencil buffer. - if (Canvas::current) - Canvas::current->checkCreateStencil(); - GLenum glcompare = GL_EQUAL; /** @@ -863,9 +1320,6 @@ Canvas *Graphics::newCanvas(int width, int height, Canvas::Format format, int ms else if (height > gl.getMaxTextureSize()) throw Exception("Cannot create canvas: height of %d pixels is too large for this system.", height); - while (GL_NO_ERROR != glGetError()) - /* clear opengl error flag */; - Canvas *canvas = new Canvas(width, height, format, msaa); GLenum err = canvas->getStatus(); @@ -873,40 +1327,8 @@ Canvas *Graphics::newCanvas(int width, int height, Canvas::Format format, int ms if (err == GL_FRAMEBUFFER_COMPLETE) return canvas; - // create error message - std::stringstream error_string; - error_string << "Cannot create canvas: "; - switch (err) - { - case GL_FRAMEBUFFER_UNSUPPORTED: - error_string << "Not supported by your OpenGL drivers."; - break; - case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: - error_string << "Texture format cannot be rendered to on this system."; - break; - // remaining error codes are highly unlikely: - case GL_FRAMEBUFFER_UNDEFINED: - case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: - case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: - case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: - case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: - error_string << "Error in graphics driver."; - break; - default: - // my intel hda card wrongly returns 0 to glCheckFramebufferStatus() but sets - // no error flag. I think it meant to return GL_FRAMEBUFFER_UNSUPPORTED, but who - // knows. - if (glGetError() == GL_NO_ERROR) - error_string << "May not be supported by your OpenGL drivers."; - // the remaining error is an indication of a serious fuckup since it should - // only be returned if glCheckFramebufferStatus() was called with the wrong - // arguments. - else - error_string << "Cannot create canvas: Aliens did it (OpenGL error code: " << glGetError() << ")"; - } - canvas->release(); - throw Exception("%s", error_string.str().c_str()); + throw love::Exception("Cannot create Canvas: %s", OpenGL::framebufferStatusString(err)); return nullptr; // never reached } @@ -1016,74 +1438,6 @@ Shader *Graphics::getShader() const return states.back().shader.get(); } -void Graphics::setCanvas(Canvas *canvas) -{ - if (canvas == nullptr) - return setCanvas(); - - DisplayState &state = states.back(); - - canvas->startGrab(); - - std::vector> canvasref; - canvasref.push_back(canvas); - - std::swap(state.canvases, canvasref); -} - -void Graphics::setCanvas(const std::vector &canvases) -{ - if (canvases.size() == 0) - return setCanvas(); - else if (canvases.size() == 1) - return setCanvas(canvases[0]); - - DisplayState &state = states.back(); - - auto attachments = std::vector(canvases.begin() + 1, canvases.end()); - canvases[0]->startGrab(attachments); - - std::vector> canvasrefs; - canvasrefs.reserve(canvases.size()); - - for (Canvas *c : canvases) - canvasrefs.push_back(c); - - std::swap(state.canvases, canvasrefs); -} - -void Graphics::setCanvas(const std::vector> &canvases) -{ - std::vector canvaslist; - canvaslist.reserve(canvases.size()); - - for (const StrongRef &c : canvases) - canvaslist.push_back(c.get()); - - return setCanvas(canvaslist); -} - -void Graphics::setCanvas() -{ - DisplayState &state = states.back(); - - if (Canvas::current != nullptr) - Canvas::current->stopGrab(); - - state.canvases.clear(); -} - -std::vector Graphics::getCanvas() const -{ - std::vector canvases; - canvases.reserve(states.back().canvases.size()); - - for (const StrongRef &c : states.back().canvases) - canvases.push_back(c.get()); - - return canvases; -} - void Graphics::setColorMask(ColorMask mask) { glColorMask(mask.r, mask.g, mask.b, mask.a); @@ -1258,8 +1612,27 @@ bool Graphics::isWireframe() const return states.back().wireframe; } +void Graphics::draw(Drawable *drawable, const Matrix4 &m) +{ + if (!currentPass.active) + throw RenderOutsidePassException(); + + drawable->draw(m); +} + +void Graphics::drawq(Texture *texture, Quad *quad, const Matrix4 &m) +{ + if (!currentPass.active) + throw RenderOutsidePassException(); + + texture->drawq(quad, m); +} + void Graphics::print(const std::vector &str, const Matrix4 &m) { + if (!currentPass.active) + throw RenderOutsidePassException(); + checkSetDefaultFont(); DisplayState &state = states.back(); @@ -1270,6 +1643,9 @@ void Graphics::print(const std::vector &str, const Matrix4 void Graphics::printf(const std::vector &str, float wrap, Font::AlignMode align, const Matrix4 &m) { + if (!currentPass.active) + throw RenderOutsidePassException(); + checkSetDefaultFont(); DisplayState &state = states.back(); @@ -1284,6 +1660,9 @@ void Graphics::printf(const std::vector &str, float wrap, F void Graphics::points(const float *coords, const uint8 *colors, size_t numpoints) { + if (!currentPass.active) + throw RenderOutsidePassException(); + OpenGL::TempDebugGroup debuggroup("Graphics points draw"); gl.prepareDraw(); @@ -1305,6 +1684,9 @@ void Graphics::points(const float *coords, const uint8 *colors, size_t numpoints void Graphics::polyline(const float *coords, size_t count) { + if (!currentPass.active) + throw RenderOutsidePassException(); + const DisplayState &state = states.back(); if (state.lineJoin == LINE_JOIN_NONE) @@ -1335,6 +1717,9 @@ void Graphics::rectangle(DrawMode mode, float x, float y, float w, float h) void Graphics::rectangle(DrawMode mode, float x, float y, float w, float h, float rx, float ry, int points) { + if (!currentPass.active) + throw RenderOutsidePassException(); + if (rx == 0 || ry == 0) { rectangle(mode, x, y, w, h); @@ -1419,6 +1804,9 @@ void Graphics::circle(DrawMode mode, float x, float y, float radius) void Graphics::ellipse(DrawMode mode, float x, float y, float a, float b, int points) { + if (!currentPass.active) + throw RenderOutsidePassException(); + float two_pi = static_cast(LOVE_M_PI * 2); if (points <= 0) points = 1; float angle_shift = (two_pi / points); @@ -1446,6 +1834,9 @@ void Graphics::ellipse(DrawMode mode, float x, float y, float a, float b) void Graphics::arc(DrawMode drawmode, ArcMode arcmode, float x, float y, float radius, float angle1, float angle2, int points) { + if (!currentPass.active) + throw RenderOutsidePassException(); + // Nothing to display with no points or equal angles. (Or is there with line mode?) if (points <= 0 || angle1 == angle2) return; @@ -1539,6 +1930,9 @@ void Graphics::arc(DrawMode drawmode, ArcMode arcmode, float x, float y, float r /// @param count the number of coordinates/size of the array void Graphics::polygon(DrawMode mode, const float *coords, size_t count) { + if (!currentPass.active) + throw RenderOutsidePassException(); + // coords is an array of a closed loop of vertices, i.e. // coords[count-2] = coords[0], coords[count-1] = coords[1] if (mode == DRAW_LINE) @@ -1558,99 +1952,6 @@ void Graphics::polygon(DrawMode mode, const float *coords, size_t count) } } -love::image::ImageData *Graphics::newScreenshot(love::image::Image *image, bool copyAlpha) -{ - // Temporarily unbind the currently active canvas (glReadPixels reads the - // active framebuffer, not the main one.) - std::vector> canvases = states.back().canvases; - setCanvas(); - - int w = getWidth(); - int h = getHeight(); - - int row = 4 * w; - int size = row * h; - - GLubyte *pixels = nullptr; - GLubyte *screenshot = nullptr; - - try - { - pixels = new GLubyte[size]; - screenshot = new GLubyte[size]; - } - catch (std::exception &) - { - delete[] pixels; - delete[] screenshot; - setCanvas(canvases); - throw love::Exception("Out of memory."); - } - -#ifdef LOVE_IOS - SDL_SysWMinfo info = {}; - SDL_VERSION(&info.version); - SDL_GetWindowWMInfo(SDL_GL_GetCurrentWindow(), &info); - - if (info.info.uikit.resolveFramebuffer != 0) - { - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, info.info.uikit.resolveFramebuffer); - - // We need to do an explicit MSAA resolve on iOS, because it uses GLES - // FBOs rather than a system framebuffer. - if (GLAD_ES_VERSION_3_0) - glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST); - else if (GLAD_APPLE_framebuffer_multisample) - glResolveMultisampleFramebufferAPPLE(); - - glBindFramebuffer(GL_READ_FRAMEBUFFER, info.info.uikit.resolveFramebuffer); - } -#endif - - glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixels); - -#ifdef LOVE_IOS - // Restore the previous binding for the main framebuffer. - if (info.info.uikit.resolveFramebuffer != 0) - glBindFramebuffer(GL_FRAMEBUFFER, gl.getDefaultFBO()); -#endif - - if (!copyAlpha) - { - // Replace alpha values with full opacity. - for (int i = 3; i < size; i += 4) - pixels[i] = 255; - } - - // OpenGL sucks and reads pixels from the lower-left. Let's fix that. - GLubyte *src = pixels - row; - GLubyte *dst = screenshot + size; - - for (int i = 0; i < h; ++i) - memcpy(dst-=row, src+=row, row); - - delete[] pixels; - - love::image::ImageData *img = nullptr; - try - { - // Tell the new ImageData that it owns the screenshot data, so we don't - // need to delete it here. - img = image->newImageData(w, h, image::ImageData::FORMAT_RGBA8, screenshot, true); - } - catch (love::Exception &) - { - delete[] screenshot; - setCanvas(canvases); - throw; - } - - // Re-bind the active canvas, if necessary. - setCanvas(canvases); - - return img; -} - Graphics::RendererInfo Graphics::getRendererInfo() const { RendererInfo info; @@ -1686,7 +1987,7 @@ Graphics::Stats Graphics::getStats() const Stats stats; stats.drawCalls = gl.stats.drawCalls; - stats.canvasSwitches = gl.stats.framebufferBinds; + stats.renderPasses = renderPassCount; stats.shaderSwitches = gl.stats.shaderSwitches; stats.canvases = Canvas::canvasCount; stats.images = Image::imageCount; diff --git a/src/modules/graphics/opengl/Graphics.h b/src/modules/graphics/opengl/Graphics.h index 138858103..785df2606 100644 --- a/src/modules/graphics/opengl/Graphics.h +++ b/src/modules/graphics/opengl/Graphics.h @@ -24,6 +24,7 @@ // STD #include #include +#include // OpenGL #include "OpenGL.h" @@ -35,8 +36,6 @@ #include "image/Image.h" #include "image/ImageData.h" -#include "window/Window.h" - #include "video/VideoStream.h" #include "Font.h" @@ -53,21 +52,63 @@ namespace love { + +class Reference; + namespace graphics { namespace opengl { +struct PassInfo +{ + enum BeginAction + { + BEGIN_LOAD, + BEGIN_CLEAR, + BEGIN_DISCARD, + }; + + enum EndAction + { + END_STORE, + END_DISCARD, + }; + + struct ColorAttachment + { + Canvas *canvas = nullptr; + Colorf clearColor = Colorf(0.0f, 0.0f, 0.0f, 0.0f); + BeginAction beginAction = BEGIN_LOAD; + }; + + ColorAttachment colorAttachments[MAX_COLOR_RENDER_TARGETS]; + int colorAttachmentCount = 0; + + bool stencil = false; + + bool addColorAttachment(const ColorAttachment &attachment) + { + if (colorAttachmentCount + 1 < MAX_COLOR_RENDER_TARGETS) + { + colorAttachments[colorAttachmentCount++] = attachment; + return true; + } + + return false; + } +}; + class Graphics : public love::graphics::Graphics { public: - struct OptionalColorf - { - float r, g, b, a; - bool enabled; + typedef void (*ScreenshotCallback)(love::image::ImageData *i, Reference *ref, void *ud); - Colorf toColor() const { return Colorf(r, g, b, a); } + struct ScreenshotInfo + { + ScreenshotCallback callback; + Reference *ref; }; Graphics(); @@ -92,25 +133,19 @@ public: **/ void reset(); - /** - * Clears the screen to a specific color. - **/ - void clear(Colorf c); + void beginPass(PassInfo::BeginAction beginAction, Colorf clearColor); + void beginPass(const PassInfo &info); - /** - * Clears each active canvas to a different color. - **/ - void clear(const std::vector &colors); + void endPass(); + void endPass(int sX, int sY, int sW, int sH, const ScreenshotInfo *info, void *screenshotCallbackData); - /** - * Discards the contents of the screen. - **/ - void discard(const std::vector &colorbuffers, bool stencil); + const PassInfo &getActivePass() const; + virtual bool isPassActive() const; /** * Flips buffers. (Rendered geometry is presented on screen). **/ - void present(); + void present(void *screenshotCallbackData); /** * Gets the width of the current graphics viewport. @@ -122,6 +157,9 @@ public: **/ int getHeight() const; + int getPassWidth() const; + int getPassHeight() const; + /** * True if a graphics viewport is set. **/ @@ -231,13 +269,6 @@ public: Shader *getShader() const; - void setCanvas(Canvas *canvas); - void setCanvas(const std::vector &canvases); - void setCanvas(const std::vector> &canvases); - void setCanvas(); - - std::vector getCanvas() const; - /** * Sets the enabled color components when rendering. **/ @@ -330,6 +361,9 @@ public: **/ bool isWireframe() const; + void draw(Drawable *drawable, const Matrix4 &m); + void drawq(Texture *texture, Quad *quad, const Matrix4 &m); + /** * Draws text at the specified coordinates **/ @@ -422,12 +456,7 @@ public: **/ void polygon(DrawMode mode, const float *coords, size_t count); - /** - * Creates a screenshot of the view and saves it to the default folder. - * @param image The love.image module. - * @param copyAlpha If the alpha channel should be copied or set to full opacity (1.0). - **/ - love::image::ImageData *newScreenshot(love::image::Image *image, bool copyAlpha = true); + void captureScreenshot(const ScreenshotInfo &info); /** * Returns system-dependent renderer information. @@ -488,8 +517,6 @@ private: StrongRef font; StrongRef shader; - std::vector> canvases; - ColorMask colorMask = ColorMask(true, true, true, true); bool wireframe = false; @@ -500,19 +527,47 @@ private: float defaultMipmapSharpness = 0.0f; }; + struct CurrentPass + { + PassInfo info; + bool active; + }; + + struct PassBufferInfo + { + bool stencil; + Canvas *canvases[MAX_COLOR_RENDER_TARGETS]; + }; + + struct CachedRenderbuffer + { + int w; + int h; + int samples; + GLenum attachments[2]; + GLuint renderbuffer; + }; + void restoreState(const DisplayState &s); void restoreStateChecked(const DisplayState &s); + void bindCachedFBOForPass(const PassInfo &pass); + void discard(OpenGL::FramebufferTarget target, const std::vector &colorbuffers, bool depthstencil); + GLuint attachCachedStencilBuffer(int w, int h, int samples); + void checkSetDefaultFont(); int calculateEllipsePoints(float rx, float ry) const; - StrongRef currentWindow; - StrongRef defaultFont; std::vector pixelSizeStack; // stores current size of a pixel (needed for line drawing) + std::vector pendingScreenshotCallbacks; + + std::unordered_map framebufferObjects; + std::vector stencilBuffers; + QuadIndices *quadIndices; int width; @@ -520,11 +575,17 @@ private: bool created; bool active; + bool canCaptureScreenshot; + + CurrentPass currentPass; + bool writingToStencil; std::vector states; std::vector stackTypes; // Keeps track of the pushed stack types. + int renderPassCount; + static const size_t MAX_USER_STACK_DEPTH = 64; }; // Graphics diff --git a/src/modules/graphics/opengl/OpenGL.cpp b/src/modules/graphics/opengl/OpenGL.cpp index 50400c8ba..37615272d 100644 --- a/src/modules/graphics/opengl/OpenGL.cpp +++ b/src/modules/graphics/opengl/OpenGL.cpp @@ -23,7 +23,6 @@ #include "OpenGL.h" #include "Shader.h" -#include "Canvas.h" #include "common/Exception.h" // C++ @@ -135,6 +134,10 @@ void OpenGL::setupContext() else state.pointSize = 1.0f; + for (int i = 0; i < 2; i++) + state.boundFramebuffers[i] = std::numeric_limits::max(); + bindFramebuffer(FRAMEBUFFER_ALL, getDefaultFBO()); + if (GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_sRGB || GLAD_EXT_framebuffer_sRGB || GLAD_EXT_sRGB_write_control) { @@ -283,7 +286,7 @@ void OpenGL::initMaxValues() glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxdrawbuffers); } - maxRenderTargets = std::min(maxattachments, maxdrawbuffers); + maxRenderTargets = std::max(std::min(maxattachments, maxdrawbuffers), 1); if (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object || GLAD_EXT_framebuffer_multisample || GLAD_APPLE_framebuffer_multisample @@ -463,7 +466,7 @@ void OpenGL::useVertexAttribArrays(uint32 arraybits) glVertexAttrib4f(ATTRIB_COLOR, 1.0f, 1.0f, 1.0f, 1.0f); } -void OpenGL::setViewport(const OpenGL::Viewport &v) +void OpenGL::setViewport(const OpenGL::Viewport &v, bool canvasActive) { glViewport(v.x, v.y, v.w, v.h); state.viewport = v; @@ -471,7 +474,7 @@ void OpenGL::setViewport(const OpenGL::Viewport &v) // glScissor starts from the lower left, so we compensate when setting the // scissor. When the viewport is changed, we need to manually update the // scissor again. - setScissor(state.scissor); + setScissor(state.scissor, canvasActive); } OpenGL::Viewport OpenGL::getViewport() const @@ -479,9 +482,9 @@ OpenGL::Viewport OpenGL::getViewport() const return state.viewport; } -void OpenGL::setScissor(const OpenGL::Viewport &v) +void OpenGL::setScissor(const OpenGL::Viewport &v, bool canvasActive) { - if (Canvas::current) + if (canvasActive) glScissor(v.x, v.y, v.w, v.h); else { @@ -526,12 +529,53 @@ bool OpenGL::hasFramebufferSRGB() const return state.framebufferSRGBEnabled; } -void OpenGL::bindFramebuffer(GLenum target, GLuint framebuffer) +void OpenGL::bindFramebuffer(FramebufferTarget target, GLuint framebuffer) { - glBindFramebuffer(target, framebuffer); + bool bindingmodified = false; - if (target == GL_FRAMEBUFFER) - ++stats.framebufferBinds; + if ((target & FRAMEBUFFER_DRAW) && state.boundFramebuffers[0] != framebuffer) + { + bindingmodified = true; + state.boundFramebuffers[0] = framebuffer; + } + + if ((target & FRAMEBUFFER_READ) && state.boundFramebuffers[1] != framebuffer) + { + bindingmodified = true; + state.boundFramebuffers[1] = framebuffer; + } + + if (bindingmodified) + { + GLenum gltarget = GL_FRAMEBUFFER; + if (target == FRAMEBUFFER_DRAW) + gltarget = GL_DRAW_FRAMEBUFFER; + else if (target == FRAMEBUFFER_READ) + gltarget = GL_READ_FRAMEBUFFER; + + glBindFramebuffer(gltarget, framebuffer); + } +} + +GLenum OpenGL::getFramebuffer(FramebufferTarget target) const +{ + if (target & FRAMEBUFFER_DRAW) + return state.boundFramebuffers[0]; + else if (target & FRAMEBUFFER_READ) + return state.boundFramebuffers[1]; + else + return 0; +} + +void OpenGL::deleteFramebuffer(GLuint framebuffer) +{ + glDeleteFramebuffers(1, &framebuffer); + + for (int i = 0; i < 2; i++) + { + if (state.boundFramebuffers[i] == framebuffer) + state.boundFramebuffers[i] = 0; + } } void OpenGL::useProgram(GLuint program) @@ -680,7 +724,7 @@ int OpenGL::getMaxTextureSize() const int OpenGL::getMaxRenderTargets() const { - return maxRenderTargets; + return std::min(maxRenderTargets, MAX_COLOR_RENDER_TARGETS); } int OpenGL::getMaxRenderbufferSamples() const @@ -739,6 +783,36 @@ const char *OpenGL::errorString(GLenum errorcode) return text; } +const char *OpenGL::framebufferStatusString(GLenum status) +{ + switch (status) + { + case GL_FRAMEBUFFER_COMPLETE: + return "complete (success)"; + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: + return "Texture format cannot be rendered to on this system."; + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: + return "Error in graphics driver (missing render texture attachment)"; + case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: + return "Error in graphics driver (incomplete draw buffer)"; + case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: + return "Error in graphics driver (incomplete read buffer)"; + case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: + return "Canvas with the specified MSAA count cannot be rendered to on this system."; + case GL_FRAMEBUFFER_UNSUPPORTED: + return "Renderable textures are unsupported"; + default: + break; + } + + static char text[64] = {}; + + memset(text, 0, sizeof(text)); + sprintf(text, "0x%x", status); + + return text; +} + const char *OpenGL::debugSeverityString(GLenum severity) { switch (severity) diff --git a/src/modules/graphics/opengl/OpenGL.h b/src/modules/graphics/opengl/OpenGL.h index 64b4ff6c8..ce186e607 100644 --- a/src/modules/graphics/opengl/OpenGL.h +++ b/src/modules/graphics/opengl/OpenGL.h @@ -104,6 +104,13 @@ public: VENDOR_UNKNOWN }; + enum FramebufferTarget + { + FRAMEBUFFER_READ = (1 << 1), + FRAMEBUFFER_DRAW = (1 << 2), + FRAMEBUFFER_ALL = (FRAMEBUFFER_READ | FRAMEBUFFER_DRAW), + }; + // A rectangle representing an OpenGL viewport or a scissor box. struct Viewport { @@ -173,7 +180,6 @@ public: { size_t textureMemory; int drawCalls; - int framebufferBinds; int shaderSwitches; } stats; @@ -282,7 +288,7 @@ public: * Sets the OpenGL rendering viewport to the specified rectangle. * The y-coordinate starts at the top. **/ - void setViewport(const Viewport &v); + void setViewport(const Viewport &v, bool canvasActive); /** * Gets the current OpenGL rendering viewport rectangle. @@ -293,7 +299,7 @@ public: * Sets the scissor box to the specified rectangle. * The y-coordinate starts at the top and is flipped internally. **/ - void setScissor(const Viewport &v); + void setScissor(const Viewport &v, bool canvasActive); /** * Gets the current scissor box (regardless of whether scissoring is enabled.) @@ -323,7 +329,9 @@ public: /** * Binds a Framebuffer Object to the specified target. **/ - void bindFramebuffer(GLenum target, GLuint framebuffer); + void bindFramebuffer(FramebufferTarget target, GLuint framebuffer); + GLuint getFramebuffer(FramebufferTarget target) const; + void deleteFramebuffer(GLuint framebuffer); /** * Calls glUseProgram. @@ -413,6 +421,7 @@ public: static GLint getGLWrapMode(Texture::WrapMode wmode); static const char *errorString(GLenum errorcode); + static const char *framebufferStatusString(GLenum status); // Get human-readable strings for debug info. static const char *debugSeverityString(GLenum severity); @@ -456,6 +465,8 @@ private: float pointSize; + GLuint boundFramebuffers[2]; + bool framebufferSRGBEnabled; GLuint defaultTexture; diff --git a/src/modules/graphics/opengl/Shader.cpp b/src/modules/graphics/opengl/Shader.cpp index 8f5b8ddc9..6bdff33e1 100644 --- a/src/modules/graphics/opengl/Shader.cpp +++ b/src/modules/graphics/opengl/Shader.cpp @@ -22,7 +22,7 @@ #include "common/config.h" #include "Shader.h" -#include "Canvas.h" +#include "Graphics.h" // C++ #include @@ -76,7 +76,7 @@ Shader::Shader(const ShaderSource &source) , program(0) , builtinUniforms() , builtinAttributes() - , lastCanvas((Canvas *) -1) + , canvasWasActive(false) , lastViewport() , lastPointSize(0.0f) , videoTextureUnits() @@ -237,7 +237,7 @@ bool Shader::loadVolatile() OpenGL::TempDebugGroup debuggroup("Shader load"); // Recreating the shader program will invalidate uniforms that rely on these. - lastCanvas = (Canvas *) -1; + canvasWasActive = false; lastViewport = OpenGL::Viewport(); lastPointSize = -1.0f; @@ -339,11 +339,11 @@ bool Shader::loadVolatile() void Shader::unloadVolatile() { - if (current == this) - gl.useProgram(0); - if (program != 0) { + if (current == this) + gl.useProgram(0); + glDeleteProgram(program); program = 0; } @@ -686,7 +686,10 @@ void Shader::checkSetScreenParams() { OpenGL::Viewport view = gl.getViewport(); - if (view == lastViewport && lastCanvas == Canvas::current) + auto gfx = Module::getInstance(Module::M_GRAPHICS); + bool canvasActive = gfx->getActivePass().colorAttachmentCount > 0; + + if (view == lastViewport && canvasWasActive == canvasActive) return; // In the shader, we do pixcoord.y = gl_FragCoord.y * params.z + params.w. @@ -697,7 +700,7 @@ void Shader::checkSetScreenParams() 0.0f, 0.0f, }; - if (Canvas::current != nullptr) + if (canvasActive) { // No flipping: pixcoord.y = gl_FragCoord.y * 1.0 + 0.0. params[2] = 1.0f; @@ -719,7 +722,7 @@ void Shader::checkSetScreenParams() glUniform4fv(location, 1, params); } - lastCanvas = Canvas::current; + canvasWasActive = canvasActive; lastViewport = view; } diff --git a/src/modules/graphics/opengl/Shader.h b/src/modules/graphics/opengl/Shader.h index 61915061f..8561f3b1e 100644 --- a/src/modules/graphics/opengl/Shader.h +++ b/src/modules/graphics/opengl/Shader.h @@ -41,8 +41,6 @@ namespace graphics namespace opengl { -class Canvas; - // A GLSL shader class Shader : public Object, public Volatile { @@ -245,8 +243,7 @@ private: // Uniform name to retainable objects std::map boundRetainables; - // Pointer to the active Canvas when the screen params were last checked. - Canvas *lastCanvas; + bool canvasWasActive; OpenGL::Viewport lastViewport; float lastPointSize; diff --git a/src/modules/graphics/opengl/wrap_Canvas.cpp b/src/modules/graphics/opengl/wrap_Canvas.cpp index d1bdd5f3a..50ad2d752 100644 --- a/src/modules/graphics/opengl/wrap_Canvas.cpp +++ b/src/modules/graphics/opengl/wrap_Canvas.cpp @@ -33,55 +33,6 @@ Canvas *luax_checkcanvas(lua_State *L, int idx) return luax_checktype(L, idx, GRAPHICS_CANVAS_ID); } -int w_Canvas_renderTo(lua_State *L) -{ - Canvas *canvas = luax_checkcanvas(L, 1); - luaL_checktype(L, 2, LUA_TFUNCTION); - - auto graphics = Module::getInstance(Module::M_GRAPHICS); - - if (graphics) - { - // Save the current Canvas so we can restore it when we're done. - std::vector oldcanvases = graphics->getCanvas(); - - for (Canvas *c : oldcanvases) - c->retain(); - - luax_catchexcept(L, [&](){ graphics->setCanvas(canvas); }); - - lua_settop(L, 2); // make sure the function is on top of the stack - int status = lua_pcall(L, 0, 0, 0); - - graphics->setCanvas(oldcanvases); - - for (Canvas *c : oldcanvases) - c->release(); - - if (status != 0) - return lua_error(L); - } - - return 0; -} - -int w_Canvas_newImageData(lua_State *L) -{ - Canvas *canvas = luax_checkcanvas(L, 1); - love::image::Image *image = luax_getmodule(L, MODULE_IMAGE_ID); - int x = (int) luaL_optnumber(L, 2, 0); - int y = (int) luaL_optnumber(L, 3, 0); - int w = (int) luaL_optnumber(L, 4, canvas->getWidth()); - int h = (int) luaL_optnumber(L, 5, canvas->getHeight()); - - love::image::ImageData *img = nullptr; - luax_catchexcept(L, [&](){ img = canvas->newImageData(image, x, y, w, h); }); - - luax_pushtype(L, IMAGE_IMAGE_DATA_ID, img); - img->release(); - return 1; -} - int w_Canvas_getFormat(lua_State *L) { Canvas *canvas = luax_checkcanvas(L, 1); @@ -103,8 +54,6 @@ int w_Canvas_getMSAA(lua_State *L) static const luaL_Reg w_Canvas_functions[] = { - { "renderTo", w_Canvas_renderTo }, - { "newImageData", w_Canvas_newImageData }, { "getFormat", w_Canvas_getFormat }, { "getMSAA", w_Canvas_getMSAA }, { 0, 0 } diff --git a/src/modules/graphics/opengl/wrap_Graphics.cpp b/src/modules/graphics/opengl/wrap_Graphics.cpp index 99ee0e37b..7abed19cd 100644 --- a/src/modules/graphics/opengl/wrap_Graphics.cpp +++ b/src/modules/graphics/opengl/wrap_Graphics.cpp @@ -27,6 +27,7 @@ #include "filesystem/wrap_Filesystem.h" #include "video/VideoStream.h" #include "image/wrap_Image.h" +#include "common/Reference.h" #include #include @@ -60,79 +61,9 @@ int w_reset(lua_State *) return 0; } -int w_clear(lua_State *L) +int w_present(lua_State *L) { - Colorf color; - - if (lua_isnoneornil(L, 1)) - color.set(0.0, 0.0, 0.0, 0.0); - else if (lua_istable(L, 1)) - { - std::vector colors((size_t) lua_gettop(L)); - - for (int i = 0; i < lua_gettop(L); i++) - { - if (lua_isnoneornil(L, i + 1) || luax_objlen(L, i + 1) == 0) - { - colors[i].enabled = false; - continue; - } - - for (int j = 1; j <= 4; j++) - lua_rawgeti(L, i + 1, j); - - colors[i].enabled = true; - colors[i].r = (float) luaL_checknumber(L, -4); - colors[i].g = (float) luaL_checknumber(L, -3); - colors[i].b = (float) luaL_checknumber(L, -2); - colors[i].a = (float) luaL_optnumber(L, -1, 1.0); - - lua_pop(L, 4); - } - - luax_catchexcept(L, [&]() { instance()->clear(colors); }); - return 0; - } - else - { - color.r = (float) luaL_checknumber(L, 1); - color.g = (float) luaL_checknumber(L, 2); - color.b = (float) luaL_checknumber(L, 3); - color.a = (float) luaL_optnumber(L, 4, 1.0); - } - - luax_catchexcept(L, [&]() { instance()->clear(color); }); - return 0; -} - -int w_discard(lua_State *L) -{ - std::vector colorbuffers; - - if (lua_istable(L, 1)) - { - for (size_t i = 1; i <= luax_objlen(L, 1); i++) - { - lua_rawgeti(L, 1, i); - colorbuffers.push_back(luax_optboolean(L, -1, true)); - lua_pop(L, 1); - } - } - else - { - bool discardcolor = luax_optboolean(L, 1, true); - size_t numbuffers = std::max((size_t) 1, instance()->getCanvas().size()); - colorbuffers = std::vector(numbuffers, discardcolor); - } - - bool stencil = luax_optboolean(L, 2, true); - instance()->discard(colorbuffers, stencil); - return 0; -} - -int w_present(lua_State *) -{ - instance()->present(); + luax_catchexcept(L, [&]() { instance()->present(L); }); return 0; } @@ -173,6 +104,274 @@ int w_getDimensions(lua_State *L) return 2; } +int w_getPassWidth(lua_State *L) +{ + lua_pushinteger(L, instance()->getPassWidth()); + return 1; +} + +int w_getPassHeight(lua_State *L) +{ + lua_pushinteger(L, instance()->getPassHeight()); + return 1; +} + +int w_getPassDimensions(lua_State *L) +{ + lua_pushinteger(L, instance()->getPassWidth()); + lua_pushinteger(L, instance()->getPassHeight()); + return 2; +} + +static int w__beginPass(lua_State *L) +{ + int nextstartidx = 1; + + if (lua_isnoneornil(L, 1)) + { + luax_catchexcept(L, [&]() { instance()->beginPass(PassInfo::BEGIN_LOAD, Colorf()); }); + nextstartidx = 1; + } + else if (lua_isnumber(L, 1)) + { + Colorf c; + c.r = (float) luaL_checknumber(L, 1); + c.g = (float) luaL_checknumber(L, 2); + c.b = (float) luaL_checknumber(L, 3); + + if (lua_isnumber(L, 4)) + { + c.a = (float) lua_tonumber(L, 4); + nextstartidx = 5; + } + else + { + c.a = 1.0f; + nextstartidx = 4; + } + + luax_catchexcept(L, [&]() { instance()->beginPass(PassInfo::BEGIN_CLEAR, c); }); + } + else if (luax_istype(L, 1, GRAPHICS_CANVAS_ID)) + { + PassInfo::ColorAttachment attachment; + attachment.canvas = luax_checkcanvas(L, 1); + attachment.beginAction = PassInfo::BEGIN_LOAD; + + if (lua_isnumber(L, 2)) + { + attachment.beginAction = PassInfo::BEGIN_CLEAR; + attachment.clearColor.r = (float) luaL_checknumber(L, 2); + attachment.clearColor.g = (float) luaL_checknumber(L, 3); + attachment.clearColor.b = (float) luaL_checknumber(L, 4); + + if (lua_isnumber(L, 5)) + { + attachment.clearColor.a = (float) lua_tonumber(L, 5); + nextstartidx = 6; + } + else + { + attachment.clearColor.a = 1.0f; + nextstartidx = 5; + } + } + else + nextstartidx = 2; + + PassInfo info; + info.addColorAttachment(attachment); + + if (lua_isboolean(L, nextstartidx)) + { + info.stencil = luax_toboolean(L, nextstartidx); + nextstartidx++; + } + else + info.stencil = false; + + luax_catchexcept(L, [&]() { instance()->beginPass(info); }); + } + else + { + luaL_checktype(L, 1, LUA_TTABLE); + + int nattachments = std::max((int) luax_objlen(L, 1), 1); + + if (nattachments > MAX_COLOR_RENDER_TARGETS) + return luaL_error(L, "Cannot render to %d Canvases at once!", nattachments); + + PassInfo info; + + for (int i = 1; i <= nattachments; i++) + { + lua_rawgeti(L, 1, i); + luaL_checktype(L, -1, LUA_TTABLE); + + PassInfo::ColorAttachment attachment; + attachment.beginAction = PassInfo::BEGIN_LOAD; + + lua_rawgeti(L, -1, 1); + attachment.canvas = luax_checkcanvas(L, -1); + + lua_rawgeti(L, -2, 2); + if (!lua_isnoneornil(L, -1)) + { + attachment.beginAction = PassInfo::BEGIN_CLEAR; + + for (int j = 3; j < 6; j++) + lua_rawgeti(L, -j, j); + + attachment.clearColor.r = (float) luaL_checknumber(L, -4); + attachment.clearColor.g = (float) luaL_checknumber(L, -3); + attachment.clearColor.b = (float) luaL_checknumber(L, -2); + attachment.clearColor.a = (float) luaL_optnumber(L, -1, 1.0); + } + + lua_pop(L, 2 + (attachment.beginAction == PassInfo::BEGIN_CLEAR ? 4 : 1)); + + info.addColorAttachment(attachment); + } + + info.stencil = luax_boolflag(L, 1, "stencil", false); + + luax_catchexcept(L, [&]() { instance()->beginPass(info); }); + + nextstartidx = 2; + } + + return nextstartidx; +} + +int w_beginPass(lua_State *L) +{ + w__beginPass(L); + return 0; +} + +static void screenshotCallback(love::image::ImageData *i, Reference *ref, void *gd) +{ + if (i != nullptr) + { + lua_State *L = (lua_State *) gd; + ref->push(L); + delete ref; + luax_pushtype(L, IMAGE_IMAGE_DATA_ID, i); + lua_call(L, 1, 0); + } + else + delete ref; +} + +static int w__endPass(lua_State *L, int startidx) +{ + if (lua_isnoneornil(L, startidx)) + { + luax_catchexcept(L, []() { instance()->endPass(); }); + } + else + { + int x, y, w, h; + + if (lua_isnumber(L, startidx)) + { + x = (int) luaL_checknumber(L, 1); + y = (int) luaL_checknumber(L, 2); + w = (int) luaL_checknumber(L, 3); + h = (int) luaL_checknumber(L, 4); + startidx += 4; + } + else + { + x = 0; + y = 0; + w = instance()->getPassWidth(); + h = instance()->getPassHeight(); + } + + luaL_checktype(L, startidx, LUA_TFUNCTION); + + Graphics::ScreenshotInfo info; + info.callback = screenshotCallback; + + lua_pushvalue(L, startidx); + info.ref = luax_refif(L, LUA_TFUNCTION); + lua_pop(L, 1); + + luax_catchexcept(L, + [&]() { instance()->endPass(x, y, w, h, &info, L); }, + [&](bool except) { if (except) delete info.ref; } + ); + } + + return 0; +} + +int w_endPass(lua_State *L) +{ + return w__endPass(L, 1); +} + +int w_renderPass(lua_State *L) +{ + int startidx = w__beginPass(L); + + if (lua_type(L, startidx) != LUA_TFUNCTION) + { + w__endPass(L, startidx + 1); + luaL_checktype(L, startidx, LUA_TFUNCTION); + return 0; + } + + int nargs = lua_gettop(L) - startidx; + int status = lua_pcall(L, nargs, 0, 0); + + w__endPass(L, startidx + 1); + + if (status != 0) + return lua_error(L); + + return 0; +} + +int w_isPassActive(lua_State *L) +{ + luax_pushboolean(L, instance()->isPassActive()); + return 1; +} + +int w_getPassCanvases(lua_State *L) +{ + if (!instance()->isPassActive()) + return 0; + + const PassInfo &info = instance()->getActivePass(); + + for (const auto &attachment : info.colorAttachments) + luax_pushtype(L, GRAPHICS_CANVAS_ID, attachment.canvas); + + return info.colorAttachmentCount; +} + +int w_captureScreenshot(lua_State *L) +{ + luaL_checktype(L, 1, LUA_TFUNCTION); + + Graphics::ScreenshotInfo info; + info.callback = screenshotCallback; + + lua_pushvalue(L, 1); + info.ref = luax_refif(L, LUA_TFUNCTION); + lua_pop(L, 1); + + luax_catchexcept(L, + [&]() { instance()->captureScreenshot(info); }, + [&](bool except) { if (except) delete info.ref; } + ); + + return 0; +} + int w_setScissor(lua_State *L) { int nargs = lua_gettop(L); @@ -243,13 +442,13 @@ int w_stencil(lua_State *L) if (lua_toboolean(L, 4) == 0) instance()->clearStencil(); - instance()->drawToStencilBuffer(action, stencilvalue); + luax_catchexcept(L, [&](){ instance()->drawToStencilBuffer(action, stencilvalue); }); // Call stencilfunc() lua_pushvalue(L, 1); lua_call(L, 0, 0); - instance()->stopDrawToStencilBuffer(); + luax_catchexcept(L, [&](){ instance()->stopDrawToStencilBuffer(); }); return 0; } @@ -268,7 +467,7 @@ int w_setStencilTest(lua_State *L) comparevalue = (int) luaL_checknumber(L, 2); } - instance()->setStencilTest(compare, comparevalue); + luax_catchexcept(L, [&](){ instance()->setStencilTest(compare, comparevalue); }); return 0; } @@ -1246,79 +1445,6 @@ int w_isWireframe(lua_State *L) return 1; } -int w_newScreenshot(lua_State *L) -{ - love::image::Image *image = luax_getmodule(L, MODULE_IMAGE_ID); - bool copyAlpha = luax_optboolean(L, 1, false); - love::image::ImageData *i = 0; - - luax_catchexcept(L, [&](){ i = instance()->newScreenshot(image, copyAlpha); }); - - luax_pushtype(L, IMAGE_IMAGE_DATA_ID, i); - i->release(); - return 1; -} - -int w_setCanvas(lua_State *L) -{ - // Disable stencil writes. - instance()->stopDrawToStencilBuffer(); - - // called with none -> reset to default buffer - if (lua_isnoneornil(L, 1)) - { - instance()->setCanvas(); - return 0; - } - - bool is_table = lua_istable(L, 1); - std::vector canvases; - - if (is_table) - { - for (int i = 1; i <= (int) luax_objlen(L, 1); i++) - { - lua_rawgeti(L, 1, i); - canvases.push_back(luax_checkcanvas(L, -1)); - lua_pop(L, 1); - } - } - else - { - for (int i = 1; i <= lua_gettop(L); i++) - canvases.push_back(luax_checkcanvas(L, i)); - } - - luax_catchexcept(L, [&]() { - if (canvases.size() > 0) - instance()->setCanvas(canvases); - else - instance()->setCanvas(); - }); - - return 0; -} - -int w_getCanvas(lua_State *L) -{ - const std::vector canvases = instance()->getCanvas(); - int n = 0; - - for (Canvas *c : canvases) - { - luax_pushtype(L, GRAPHICS_CANVAS_ID, c); - n++; - } - - if (n == 0) - { - lua_pushnil(L); - n = 1; - } - - return n; -} - int w_setShader(lua_State *L) { if (lua_isnoneornil(L,1)) @@ -1497,8 +1623,8 @@ int w_getStats(lua_State *L) lua_pushinteger(L, stats.drawCalls); lua_setfield(L, -2, "drawcalls"); - lua_pushinteger(L, stats.canvasSwitches); - lua_setfield(L, -2, "canvasswitches"); + lua_pushinteger(L, stats.renderPasses); + lua_setfield(L, -2, "renderpasses"); lua_pushinteger(L, stats.shaderSwitches); lua_setfield(L, -2, "shaderswitches"); @@ -1555,9 +1681,9 @@ int w_draw(lua_State *L) luax_catchexcept(L, [&]() { if (texture && quad) - texture->drawq(quad, m); + instance()->drawq(texture, quad, m); else if (drawable) - drawable->draw(m); + instance()->draw(drawable, m); }); return 0; @@ -1698,11 +1824,14 @@ int w_points(lua_State *L) coords[i] = luax_tofloat(L, i + 1); } - instance()->points(coords, colors, numpoints); - - delete[] coords; - if (colors) - delete[] colors; + luax_catchexcept(L, + [&](){ instance()->points(coords, colors, numpoints); }, + [&](bool) { + delete[] coords; + if (colors) + delete[] colors; + } + ); return 0; } @@ -1738,9 +1867,11 @@ int w_line(lua_State *L) coords[i] = luax_tofloat(L, i + 1); } - instance()->polyline(coords, args); + luax_catchexcept(L, + [&](){ instance()->polyline(coords, args); }, + [&](bool) { delete[] coords; } + ); - delete[] coords; return 0; } @@ -1766,11 +1897,11 @@ int w_rectangle(lua_State *L) float ry = (float)luaL_optnumber(L, 7, rx); if (lua_isnoneornil(L, 8)) - instance()->rectangle(mode, x, y, w, h, rx, ry); + luax_catchexcept(L, [&](){ instance()->rectangle(mode, x, y, w, h, rx, ry); }); else { int points = (int) luaL_checknumber(L, 8); - instance()->rectangle(mode, x, y, w, h, rx, ry, points); + luax_catchexcept(L, [&](){ instance()->rectangle(mode, x, y, w, h, rx, ry, points); }); } return 0; @@ -1788,11 +1919,11 @@ int w_circle(lua_State *L) float radius = (float)luaL_checknumber(L, 4); if (lua_isnoneornil(L, 5)) - instance()->circle(mode, x, y, radius); + luax_catchexcept(L, [&](){ instance()->circle(mode, x, y, radius); }); else { int points = (int) luaL_checknumber(L, 5); - instance()->circle(mode, x, y, radius, points); + luax_catchexcept(L, [&](){ instance()->circle(mode, x, y, radius, points); }); } return 0; @@ -1811,11 +1942,11 @@ int w_ellipse(lua_State *L) float b = (float)luaL_optnumber(L, 5, a); if (lua_isnoneornil(L, 6)) - instance()->ellipse(mode, x, y, a, b); + luax_catchexcept(L, [&](){ instance()->ellipse(mode, x, y, a, b); }); else { int points = (int) luaL_checknumber(L, 6); - instance()->ellipse(mode, x, y, a, b, points); + luax_catchexcept(L, [&](){ instance()->ellipse(mode, x, y, a, b, points); }); } return 0; @@ -1848,11 +1979,11 @@ int w_arc(lua_State *L) float angle2 = (float) luaL_checknumber(L, startidx + 4); if (lua_isnoneornil(L, startidx + 5)) - instance()->arc(drawmode, arcmode, x, y, radius, angle1, angle2); + luax_catchexcept(L, [&](){ instance()->arc(drawmode, arcmode, x, y, radius, angle1, angle2); }); else { int points = (int) luaL_checknumber(L, startidx + 5); - instance()->arc(drawmode, arcmode, x, y, radius, angle1, angle2, points); + luax_catchexcept(L, [&](){ instance()->arc(drawmode, arcmode, x, y, radius, angle1, angle2, points); }); } return 0; @@ -1900,8 +2031,11 @@ int w_polygon(lua_State *L) // make a closed loop coords[args] = coords[0]; coords[args+1] = coords[1]; - instance()->polygon(mode, coords, args+2); - delete[] coords; + + luax_catchexcept(L, + [&](){ instance()->polygon(mode, coords, args+2); }, + [&](bool) { delete[] coords; } + ); return 0; } @@ -1987,8 +2121,6 @@ int w_inverseTransformPoint(lua_State *L) static const luaL_Reg functions[] = { { "reset", w_reset }, - { "clear", w_clear }, - { "discard", w_discard }, { "present", w_present }, { "newImage", w_newImage }, @@ -2030,9 +2162,6 @@ static const luaL_Reg functions[] = { "getPointSize", w_getPointSize }, { "setWireframe", w_setWireframe }, { "isWireframe", w_isWireframe }, - { "newScreenshot", w_newScreenshot }, - { "setCanvas", w_setCanvas }, - { "getCanvas", w_getCanvas }, { "setShader", w_setShader }, { "getShader", w_getShader }, @@ -2046,6 +2175,14 @@ static const luaL_Reg functions[] = { "getSystemLimits", w_getSystemLimits }, { "getStats", w_getStats }, + { "beginPass", w_beginPass }, + { "endPass", w_endPass }, + { "renderPass", w_renderPass }, + { "isPassActive", w_isPassActive }, + { "getPassCanvases", w_getPassCanvases }, + + { "captureScreenshot", w_captureScreenshot }, + { "draw", w_draw }, { "print", w_print }, @@ -2057,6 +2194,9 @@ static const luaL_Reg functions[] = { "getWidth", w_getWidth }, { "getHeight", w_getHeight }, { "getDimensions", w_getDimensions }, + { "getPassWidth", w_getPassWidth }, + { "getPassHeight", w_getPassHeight }, + { "getPassDimensions", w_getPassDimensions }, { "setScissor", w_setScissor }, { "intersectScissor", w_intersectScissor }, diff --git a/src/modules/window/Window.h b/src/modules/window/Window.h index c15d734c7..3549d384d 100644 --- a/src/modules/window/Window.h +++ b/src/modules/window/Window.h @@ -32,6 +32,12 @@ namespace love { + +namespace graphics +{ +class Graphics; +} + namespace window { @@ -108,6 +114,8 @@ public: // Implements Module. virtual ModuleType getModuleType() const { return M_WINDOW; } + virtual void setGraphics(graphics::Graphics *graphics) = 0; + virtual bool setWindow(int width = 800, int height = 600, WindowSettings *settings = nullptr) = 0; virtual void getWindow(int &width, int &height, WindowSettings &settings) = 0; diff --git a/src/modules/window/sdl/Window.cpp b/src/modules/window/sdl/Window.cpp index 81423f9c4..12c3ebe19 100644 --- a/src/modules/window/sdl/Window.cpp +++ b/src/modules/window/sdl/Window.cpp @@ -79,9 +79,16 @@ Window::~Window() { close(); + graphics.set(nullptr); + SDL_QuitSubSystem(SDL_INIT_VIDEO); } +void Window::setGraphics(graphics::Graphics *graphics) +{ + this->graphics.set(graphics); +} + void Window::setGLFramebufferAttributes(int msaa, bool sRGB) { // Set GL window / framebuffer attributes. @@ -401,6 +408,12 @@ bool Window::createWindowAndContext(int x, int y, int w, int h, Uint32 windowfla bool Window::setWindow(int width, int height, WindowSettings *settings) { + if (!graphics.get()) + graphics.set(Module::getInstance(Module::M_GRAPHICS)); + + if (graphics.get() && graphics->isPassActive()) + throw love::Exception("setMode cannot be called while a render pass is active in love.graphics."); + WindowSettings f; if (settings) @@ -507,9 +520,8 @@ bool Window::setWindow(int width, int height, WindowSettings *settings) updateSettings(f, false); - auto gfx = Module::getInstance(Module::M_GRAPHICS); - if (gfx != nullptr) - gfx->setMode(pixelWidth, pixelHeight); + if (graphics.get()) + graphics->setMode(pixelWidth, pixelHeight); #ifdef LOVE_ANDROID love::android::setImmersive(f.fullscreen); @@ -528,9 +540,8 @@ bool Window::onSizeChanged(int width, int height) SDL_GL_GetDrawableSize(window, &pixelWidth, &pixelHeight); - auto gfx = Module::getInstance(Module::M_GRAPHICS); - if (gfx != nullptr) - gfx->setViewportSize(pixelWidth, pixelHeight); + if (graphics.get()) + graphics->setViewportSize(pixelWidth, pixelHeight); return true; } @@ -596,13 +607,9 @@ void Window::updateSettings(const WindowSettings &newsettings, bool updateGraphi // May be 0 if the refresh rate can't be determined. settings.refreshrate = (double) dmode.refresh_rate; - if (updateGraphicsViewport) - { - // Update the viewport size now instead of waiting for event polling. - auto gfx = Module::getInstance(Module::M_GRAPHICS); - if (gfx != nullptr) - gfx->setViewportSize(pixelWidth, pixelHeight); - } + // Update the viewport size now instead of waiting for event polling. + if (updateGraphicsViewport && graphics.get()) + graphics->setViewportSize(pixelWidth, pixelHeight); } void Window::getWindow(int &width, int &height, WindowSettings &newsettings) @@ -618,9 +625,13 @@ void Window::getWindow(int &width, int &height, WindowSettings &newsettings) void Window::close() { - auto gfx = Module::getInstance(Module::M_GRAPHICS); - if (gfx != nullptr) - gfx->unSetMode(); + if (graphics.get()) + { + if (graphics->isPassActive()) + throw love::Exception("close cannot be called while a render pass is active in love.graphics."); + + graphics->unSetMode(); + } if (context) { @@ -646,6 +657,9 @@ bool Window::setFullscreen(bool fullscreen, Window::FullscreenType fstype) if (!window) return false; + if (graphics.get() && graphics->isPassActive()) + throw love::Exception("setFullscreen cannot be called while a render pass is active in love.graphics."); + WindowSettings newsettings = settings; newsettings.fullscreen = fullscreen; newsettings.fstype = fstype; diff --git a/src/modules/window/sdl/Window.h b/src/modules/window/sdl/Window.h index ade2d6f59..91e68e04f 100644 --- a/src/modules/window/sdl/Window.h +++ b/src/modules/window/sdl/Window.h @@ -41,6 +41,8 @@ public: Window(); ~Window(); + void setGraphics(graphics::Graphics *graphics); + bool setWindow(int width = 800, int height = 600, WindowSettings *settings = nullptr); void getWindow(int &width, int &height, WindowSettings &settings); @@ -149,6 +151,8 @@ private: bool hasSDL203orEarlier; ContextAttribs contextAttribs; + StrongRef graphics; + }; // Window } // sdl diff --git a/src/modules/window/wrap_Window.cpp b/src/modules/window/wrap_Window.cpp index f9a406eb4..bb439f3ef 100644 --- a/src/modules/window/wrap_Window.cpp +++ b/src/modules/window/wrap_Window.cpp @@ -119,7 +119,7 @@ int w_setMode(lua_State *L) if (lua_isnoneornil(L, 3)) { - luax_pushboolean(L, instance()->setWindow(w, h, nullptr)); + luax_catchexcept(L, [&](){ luax_pushboolean(L, instance()->setWindow(w, h, nullptr)); }); return 1; } @@ -266,10 +266,12 @@ int w_setFullscreen(lua_State *L) return luaL_error(L, "Invalid fullscreen type: %s", typestr); bool success = false; - if (fstype == Window::FULLSCREEN_MAX_ENUM) - success = instance()->setFullscreen(fullscreen); - else - success = instance()->setFullscreen(fullscreen, fstype); + luax_catchexcept(L, [&]() { + if (fstype == Window::FULLSCREEN_MAX_ENUM) + success = instance()->setFullscreen(fullscreen); + else + success = instance()->setFullscreen(fullscreen, fstype); + }); luax_pushboolean(L, success); return 1; @@ -296,9 +298,9 @@ int w_isOpen(lua_State *L) return 1; } -int w_close(lua_State * /*L*/) +int w_close(lua_State *L) { - instance()->close(); + luax_catchexcept(L, [&]() { instance()->close(); }); return 0; } diff --git a/src/scripts/boot.lua b/src/scripts/boot.lua index 98c2c9129..210643f86 100644 --- a/src/scripts/boot.lua +++ b/src/scripts/boot.lua @@ -545,9 +545,14 @@ function love.run() if love.update then love.update(dt) end -- will pass 0 if love.timer is disabled if love.graphics and love.graphics.isActive() then - love.graphics.clear(love.graphics.getBackgroundColor()) love.graphics.origin() + + if love.drawpasses then love.drawpasses() end + + love.graphics.beginPass(love.graphics.getBackgroundColor()) if love.draw then love.draw() end + love.graphics.endPass() + love.graphics.present() end @@ -598,15 +603,18 @@ function love.errhand(msg) end end if love.audio then love.audio.stop() end + love.graphics.reset() + if love.graphics.isPassActive() then + love.graphics.endPass() + end + local font = love.graphics.setNewFont(math.floor(love.window.toPixels(14))) - love.graphics.setBackgroundColor(89/255, 157/255, 220/255) love.graphics.setColor(1, 1, 1, 1) local trace = debug.traceback() - love.graphics.clear(love.graphics.getBackgroundColor()) love.graphics.origin() local err = {} @@ -628,9 +636,7 @@ function love.errhand(msg) local function draw() local pos = love.window.toPixels(70) - love.graphics.clear(love.graphics.getBackgroundColor()) love.graphics.printf(p, pos, pos, love.graphics.getWidth() - pos) - love.graphics.present() end while true do @@ -652,7 +658,8 @@ function love.errhand(msg) end end - draw() + love.graphics.renderPass(89/255, 157/255, 220/255, draw) + love.graphics.present() if love.timer then love.timer.sleep(0.1) diff --git a/src/scripts/boot.lua.h b/src/scripts/boot.lua.h index 8f499c12a..cb0762d0a 100644 --- a/src/scripts/boot.lua.h +++ b/src/scripts/boot.lua.h @@ -1005,14 +1005,19 @@ const unsigned char boot_lua[] = 0x09, 0x09, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x69, 0x73, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x28, 0x29, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, - 0x09, 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x63, - 0x6c, 0x65, 0x61, 0x72, 0x28, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, - 0x2e, 0x67, 0x65, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x43, 0x6f, 0x6c, 0x6f, - 0x72, 0x28, 0x29, 0x29, 0x0a, 0x09, 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x28, 0x29, 0x0a, + 0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x64, 0x72, 0x61, 0x77, 0x70, 0x61, 0x73, + 0x73, 0x65, 0x73, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x64, 0x72, 0x61, 0x77, + 0x70, 0x61, 0x73, 0x73, 0x65, 0x73, 0x28, 0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a, + 0x09, 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x62, + 0x65, 0x67, 0x69, 0x6e, 0x50, 0x61, 0x73, 0x73, 0x28, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, + 0x68, 0x69, 0x63, 0x73, 0x2e, 0x67, 0x65, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, + 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x28, 0x29, 0x29, 0x0a, 0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x64, 0x72, 0x61, 0x77, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x64, 0x72, 0x61, 0x77, 0x28, 0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a, + 0x09, 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x65, + 0x6e, 0x64, 0x50, 0x61, 0x73, 0x73, 0x28, 0x29, 0x0a, 0x09, 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x28, 0x29, 0x0a, 0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a, @@ -1098,23 +1103,21 @@ const unsigned char boot_lua[] = 0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x72, 0x65, 0x73, 0x65, 0x74, 0x28, 0x29, 0x0a, + 0x09, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, + 0x69, 0x73, 0x50, 0x61, 0x73, 0x73, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x28, 0x29, 0x20, 0x74, 0x68, 0x65, + 0x6e, 0x0a, + 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x65, 0x6e, + 0x64, 0x50, 0x61, 0x73, 0x73, 0x28, 0x29, 0x0a, + 0x09, 0x65, 0x6e, 0x64, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x6f, 0x6e, 0x74, 0x20, 0x3d, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x73, 0x65, 0x74, 0x4e, 0x65, 0x77, 0x46, 0x6f, 0x6e, 0x74, 0x28, 0x6d, 0x61, 0x74, 0x68, 0x2e, 0x66, 0x6c, 0x6f, 0x6f, 0x72, 0x28, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x74, 0x6f, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x28, 0x31, 0x34, 0x29, 0x29, 0x29, 0x0a, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x73, 0x65, 0x74, - 0x42, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x28, 0x38, 0x39, - 0x2f, 0x32, 0x35, 0x35, 0x2c, 0x20, 0x31, 0x35, 0x37, 0x2f, 0x32, 0x35, 0x35, 0x2c, 0x20, 0x32, 0x32, 0x30, - 0x2f, 0x32, 0x35, 0x35, 0x29, 0x0a, - 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x73, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x28, 0x31, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x31, 0x29, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x74, 0x72, 0x61, 0x63, 0x65, 0x20, 0x3d, 0x20, 0x64, 0x65, 0x62, 0x75, 0x67, 0x2e, 0x74, 0x72, 0x61, 0x63, 0x65, 0x62, 0x61, 0x63, 0x6b, 0x28, 0x29, 0x0a, - 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x63, 0x6c, 0x65, - 0x61, 0x72, 0x28, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x67, - 0x65, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x28, - 0x29, 0x29, 0x0a, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x28, 0x29, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x65, 0x72, 0x72, 0x20, 0x3d, 0x20, 0x7b, 0x7d, 0x0a, @@ -1148,16 +1151,10 @@ const unsigned char boot_lua[] = 0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x70, 0x6f, 0x73, 0x20, 0x3d, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x74, 0x6f, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x73, 0x28, 0x37, 0x30, 0x29, 0x0a, - 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x63, 0x6c, - 0x65, 0x61, 0x72, 0x28, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, - 0x67, 0x65, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x43, 0x6f, 0x6c, 0x6f, 0x72, - 0x28, 0x29, 0x29, 0x0a, 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x70, 0x2c, 0x20, 0x70, 0x6f, 0x73, 0x2c, 0x20, 0x70, 0x6f, 0x73, 0x2c, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x67, 0x65, 0x74, 0x57, 0x69, 0x64, 0x74, 0x68, 0x28, 0x29, 0x20, 0x2d, 0x20, 0x70, 0x6f, 0x73, 0x29, 0x0a, - 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x70, 0x72, - 0x65, 0x73, 0x65, 0x6e, 0x74, 0x28, 0x29, 0x0a, 0x09, 0x65, 0x6e, 0x64, 0x0a, 0x09, 0x77, 0x68, 0x69, 0x6c, 0x65, 0x20, 0x74, 0x72, 0x75, 0x65, 0x20, 0x64, 0x6f, 0x0a, 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x75, 0x6d, 0x70, 0x28, @@ -1194,7 +1191,12 @@ const unsigned char boot_lua[] = 0x09, 0x09, 0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a, 0x09, 0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a, 0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a, - 0x09, 0x09, 0x64, 0x72, 0x61, 0x77, 0x28, 0x29, 0x0a, + 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x72, 0x65, + 0x6e, 0x64, 0x65, 0x72, 0x50, 0x61, 0x73, 0x73, 0x28, 0x38, 0x39, 0x2f, 0x32, 0x35, 0x35, 0x2c, 0x20, 0x31, + 0x35, 0x37, 0x2f, 0x32, 0x35, 0x35, 0x2c, 0x20, 0x32, 0x32, 0x30, 0x2f, 0x32, 0x35, 0x35, 0x2c, 0x20, 0x64, + 0x72, 0x61, 0x77, 0x29, 0x0a, + 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x70, 0x72, + 0x65, 0x73, 0x65, 0x6e, 0x74, 0x28, 0x29, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x72, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x09, 0x09, 0x09, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x72, 0x2e, 0x73, 0x6c, 0x65, 0x65, From bed9bc1baec5f1700b8afc961d3d111deefd9aa1 Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Sat, 19 Nov 2016 20:12:12 -0400 Subject: [PATCH 21/37] =?UTF-8?q?We=20don=E2=80=99t=20need=20a=20list=20of?= =?UTF-8?q?=20projection=20matrices..?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --HG-- branch : minor --- src/modules/graphics/opengl/Graphics.cpp | 6 +++--- src/modules/graphics/opengl/OpenGL.cpp | 8 +++----- src/modules/graphics/opengl/OpenGL.h | 2 +- src/modules/graphics/opengl/Shader.cpp | 2 +- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/modules/graphics/opengl/Graphics.cpp b/src/modules/graphics/opengl/Graphics.cpp index fcc5f1694..ad08ebe04 100644 --- a/src/modules/graphics/opengl/Graphics.cpp +++ b/src/modules/graphics/opengl/Graphics.cpp @@ -217,7 +217,7 @@ void Graphics::setViewportSize(int width, int height) gl.setViewport({0, 0, width, height}, false); // Set up the projection matrix - gl.matrices.projection.back() = Matrix4::ortho(0.0, (float) width, (float) height, 0.0); + gl.matrices.projection = Matrix4::ortho(0.0, (float) width, (float) height, 0.0); } } @@ -442,7 +442,7 @@ void Graphics::beginPass(PassInfo::BeginAction beginAction, Colorf clearColor) // The projection matrix is flipped compared to rendering to a canvas, due // to OpenGL considering (0,0) bottom-left instead of top-left. - gl.matrices.projection.back() = Matrix4::ortho(0.0, (float) width, (float) height, 0.0); + gl.matrices.projection = Matrix4::ortho(0.0, (float) width, (float) height, 0.0); if (GLAD_VERSION_1_0 || GLAD_EXT_sRGB_write_control) { @@ -523,7 +523,7 @@ void Graphics::beginPass(const PassInfo &info) int h = firstcanvas->getHeight(); gl.setViewport({0, 0, w, h}, true); - gl.matrices.projection.back() = Matrix4::ortho(0.0, (float) w, 0.0, (float) h); + gl.matrices.projection = Matrix4::ortho(0.0, (float) w, 0.0, (float) h); // Make sure the correct sRGB setting is used when drawing to the canvases. if (GLAD_VERSION_1_0 || GLAD_EXT_sRGB_write_control) diff --git a/src/modules/graphics/opengl/OpenGL.cpp b/src/modules/graphics/opengl/OpenGL.cpp index 37615272d..3749829af 100644 --- a/src/modules/graphics/opengl/OpenGL.cpp +++ b/src/modules/graphics/opengl/OpenGL.cpp @@ -74,7 +74,6 @@ OpenGL::OpenGL() , state() { matrices.transform.reserve(10); - matrices.projection.reserve(2); } bool OpenGL::initContext() @@ -307,10 +306,9 @@ void OpenGL::initMaxValues() void OpenGL::initMatrices() { matrices.transform.clear(); - matrices.projection.clear(); matrices.transform.push_back(Matrix4()); - matrices.projection.push_back(Matrix4()); + matrices.projection = Matrix4(); } void OpenGL::createDefaultTexture() @@ -364,7 +362,7 @@ void OpenGL::prepareDraw() // because uniform uploads can be significantly slower than glLoadMatrix. if (GLAD_VERSION_1_0) { - const Matrix4 &curproj = matrices.projection.back(); + const Matrix4 &curproj = matrices.projection; const Matrix4 &curxform = matrices.transform.back(); const Matrix4 &lastproj = state.lastProjectionMatrix; @@ -377,7 +375,7 @@ void OpenGL::prepareDraw() glLoadMatrixf(curproj.getElements()); glMatrixMode(GL_MODELVIEW); - state.lastProjectionMatrix = matrices.projection.back(); + state.lastProjectionMatrix = matrices.projection; } // Same with the transform matrix. diff --git a/src/modules/graphics/opengl/OpenGL.h b/src/modules/graphics/opengl/OpenGL.h index ce186e607..c740c35e6 100644 --- a/src/modules/graphics/opengl/OpenGL.h +++ b/src/modules/graphics/opengl/OpenGL.h @@ -126,7 +126,7 @@ public: struct { std::vector transform; - std::vector projection; + Matrix4 projection; } matrices; class TempTransform diff --git a/src/modules/graphics/opengl/Shader.cpp b/src/modules/graphics/opengl/Shader.cpp index 6bdff33e1..0900a94f9 100644 --- a/src/modules/graphics/opengl/Shader.cpp +++ b/src/modules/graphics/opengl/Shader.cpp @@ -753,7 +753,7 @@ void Shader::checkSetBuiltinUniforms() checkSetPointSize(gl.getPointSize()); const Matrix4 &curxform = gl.matrices.transform.back(); - const Matrix4 &curproj = gl.matrices.projection.back(); + const Matrix4 &curproj = gl.matrices.projection; TemporaryAttacher attacher(this); From f118befeba80d56d4285dca1ac4b941490b6872d Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Sat, 19 Nov 2016 23:47:33 -0400 Subject: [PATCH 22/37] Potentially fix a random error in setMode --HG-- branch : minor --- src/modules/graphics/opengl/Graphics.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/graphics/opengl/Graphics.h b/src/modules/graphics/opengl/Graphics.h index 785df2606..4bef0968f 100644 --- a/src/modules/graphics/opengl/Graphics.h +++ b/src/modules/graphics/opengl/Graphics.h @@ -530,7 +530,7 @@ private: struct CurrentPass { PassInfo info; - bool active; + bool active = false; }; struct PassBufferInfo From a245819ae154ca6430e589d2e26fb6241b313b75 Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Sun, 20 Nov 2016 10:04:08 -0400 Subject: [PATCH 23/37] Fix compilation on macOS and iOS. Minor cleanup. --HG-- branch : minor --- .../xcode/liblove.xcodeproj/project.pbxproj | 40 +++++++++++++++++++ src/modules/audio/openal/Audio.cpp | 2 +- src/modules/audio/wrap_RecordingDevice.cpp | 24 +++++------ 3 files changed, 51 insertions(+), 15 deletions(-) diff --git a/platform/xcode/liblove.xcodeproj/project.pbxproj b/platform/xcode/liblove.xcodeproj/project.pbxproj index f16045158..a5e757144 100644 --- a/platform/xcode/liblove.xcodeproj/project.pbxproj +++ b/platform/xcode/liblove.xcodeproj/project.pbxproj @@ -883,6 +883,18 @@ FA4F2B791DE0125B00CA37D7 /* xxhash.c in Sources */ = {isa = PBXBuildFile; fileRef = FA4F2B771DE0125B00CA37D7 /* xxhash.c */; }; FA4F2B7A1DE0125B00CA37D7 /* xxhash.h in Headers */ = {isa = PBXBuildFile; fileRef = FA4F2B781DE0125B00CA37D7 /* xxhash.h */; }; FA4F2B7B1DE0181B00CA37D7 /* xxhash.c in Sources */ = {isa = PBXBuildFile; fileRef = FA4F2B771DE0125B00CA37D7 /* xxhash.c */; }; + FA4F2BA61DE1E36400CA37D7 /* RecordingDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA4F2BA21DE1E36400CA37D7 /* RecordingDevice.cpp */; }; + FA4F2BA71DE1E36400CA37D7 /* RecordingDevice.h in Headers */ = {isa = PBXBuildFile; fileRef = FA4F2BA31DE1E36400CA37D7 /* RecordingDevice.h */; }; + FA4F2BA81DE1E36400CA37D7 /* wrap_RecordingDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA4F2BA41DE1E36400CA37D7 /* wrap_RecordingDevice.cpp */; }; + FA4F2BA91DE1E36400CA37D7 /* wrap_RecordingDevice.h in Headers */ = {isa = PBXBuildFile; fileRef = FA4F2BA51DE1E36400CA37D7 /* wrap_RecordingDevice.h */; }; + FA4F2BAC1DE1E37000CA37D7 /* RecordingDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA4F2BAA1DE1E37000CA37D7 /* RecordingDevice.cpp */; }; + FA4F2BAD1DE1E37000CA37D7 /* RecordingDevice.h in Headers */ = {isa = PBXBuildFile; fileRef = FA4F2BAB1DE1E37000CA37D7 /* RecordingDevice.h */; }; + FA4F2BB01DE1E37B00CA37D7 /* RecordingDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA4F2BAE1DE1E37B00CA37D7 /* RecordingDevice.cpp */; }; + FA4F2BB11DE1E37B00CA37D7 /* RecordingDevice.h in Headers */ = {isa = PBXBuildFile; fileRef = FA4F2BAF1DE1E37B00CA37D7 /* RecordingDevice.h */; }; + FA4F2BB21DE1E4B400CA37D7 /* RecordingDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA4F2BAE1DE1E37B00CA37D7 /* RecordingDevice.cpp */; }; + FA4F2BB31DE1E4B800CA37D7 /* RecordingDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA4F2BA21DE1E36400CA37D7 /* RecordingDevice.cpp */; }; + FA4F2BB41DE1E4BD00CA37D7 /* RecordingDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA4F2BAA1DE1E37000CA37D7 /* RecordingDevice.cpp */; }; + FA4F2BB51DE1E4C300CA37D7 /* wrap_RecordingDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA4F2BA41DE1E36400CA37D7 /* wrap_RecordingDevice.cpp */; }; FA56D9BC1C208A0200D8D3C7 /* libmodplug.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FA56D9BA1C2089EE00D8D3C7 /* libmodplug.a */; }; FA577AB016C7507900860150 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA577A7916C71A1700860150 /* Cocoa.framework */; }; FA577AC216C7512D00860150 /* FreeType.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA577A6716C719D900860150 /* FreeType.framework */; }; @@ -1580,6 +1592,14 @@ FA4B66C81ABBCF1900558F15 /* Timer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Timer.cpp; sourceTree = ""; }; FA4F2B771DE0125B00CA37D7 /* xxhash.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xxhash.c; sourceTree = ""; }; FA4F2B781DE0125B00CA37D7 /* xxhash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xxhash.h; sourceTree = ""; }; + FA4F2BA21DE1E36400CA37D7 /* RecordingDevice.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RecordingDevice.cpp; sourceTree = ""; }; + FA4F2BA31DE1E36400CA37D7 /* RecordingDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RecordingDevice.h; sourceTree = ""; }; + FA4F2BA41DE1E36400CA37D7 /* wrap_RecordingDevice.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_RecordingDevice.cpp; sourceTree = ""; }; + FA4F2BA51DE1E36400CA37D7 /* wrap_RecordingDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_RecordingDevice.h; sourceTree = ""; }; + FA4F2BAA1DE1E37000CA37D7 /* RecordingDevice.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RecordingDevice.cpp; sourceTree = ""; }; + FA4F2BAB1DE1E37000CA37D7 /* RecordingDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RecordingDevice.h; sourceTree = ""; }; + FA4F2BAE1DE1E37B00CA37D7 /* RecordingDevice.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RecordingDevice.cpp; sourceTree = ""; }; + FA4F2BAF1DE1E37B00CA37D7 /* RecordingDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RecordingDevice.h; sourceTree = ""; }; FA56D9BA1C2089EE00D8D3C7 /* libmodplug.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libmodplug.a; sourceTree = ""; }; FA577A6716C719D900860150 /* FreeType.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FreeType.framework; path = /Library/Frameworks/FreeType.framework; sourceTree = ""; }; FA577A6D16C719EA00860150 /* Lua.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Lua.framework; path = /Library/Frameworks/Lua.framework; sourceTree = ""; }; @@ -2201,10 +2221,14 @@ FA0B7B3F1A95902C000E1D17 /* Audio.h */, FA0B7B401A95902C000E1D17 /* null */, FA0B7B451A95902C000E1D17 /* openal */, + FA4F2BA21DE1E36400CA37D7 /* RecordingDevice.cpp */, + FA4F2BA31DE1E36400CA37D7 /* RecordingDevice.h */, FA0B7B4C1A95902C000E1D17 /* Source.cpp */, FA0B7B4D1A95902C000E1D17 /* Source.h */, FA0B7B4E1A95902C000E1D17 /* wrap_Audio.cpp */, FA0B7B4F1A95902C000E1D17 /* wrap_Audio.h */, + FA4F2BA41DE1E36400CA37D7 /* wrap_RecordingDevice.cpp */, + FA4F2BA51DE1E36400CA37D7 /* wrap_RecordingDevice.h */, FA0B7B501A95902C000E1D17 /* wrap_Source.cpp */, FA0B7B511A95902C000E1D17 /* wrap_Source.h */, ); @@ -2216,6 +2240,8 @@ children = ( FA0B7B411A95902C000E1D17 /* Audio.cpp */, FA0B7B421A95902C000E1D17 /* Audio.h */, + FA4F2BAA1DE1E37000CA37D7 /* RecordingDevice.cpp */, + FA4F2BAB1DE1E37000CA37D7 /* RecordingDevice.h */, FA0B7B431A95902C000E1D17 /* Source.cpp */, FA0B7B441A95902C000E1D17 /* Source.h */, ); @@ -2229,6 +2255,8 @@ FA0B7B471A95902C000E1D17 /* Audio.h */, FA0B7B481A95902C000E1D17 /* Pool.cpp */, FA0B7B491A95902C000E1D17 /* Pool.h */, + FA4F2BAE1DE1E37B00CA37D7 /* RecordingDevice.cpp */, + FA4F2BAF1DE1E37B00CA37D7 /* RecordingDevice.h */, FA0B7B4A1A95902C000E1D17 /* Source.cpp */, FA0B7B4B1A95902C000E1D17 /* Source.h */, ); @@ -3038,6 +3066,7 @@ FA0B7D381A95902C000E1D17 /* Font.h in Headers */, FA0B7EC31A95902C000E1D17 /* threads.h in Headers */, FA0B7AC21A958EA3000E1D17 /* enet.h in Headers */, + FA4F2BA71DE1E36400CA37D7 /* RecordingDevice.h in Headers */, FA0B7E201A95902C000E1D17 /* Physics.h in Headers */, 217DFBEA1D9F6D490055D849 /* io.h in Headers */, FA0B79221A958E3B000E1D17 /* delay.h in Headers */, @@ -3189,6 +3218,7 @@ FA0B7A341A958EA3000E1D17 /* b2Collision.h in Headers */, 217DFBDA1D9F6D490055D849 /* auxiliar.h in Headers */, FA0B7EC71A95902C000E1D17 /* ThreadModule.h in Headers */, + FA4F2BAD1DE1E37000CA37D7 /* RecordingDevice.h in Headers */, FA0B792B1A958E3B000E1D17 /* Matrix.h in Headers */, FA0B7DA41A95902C000E1D17 /* PKMHandler.h in Headers */, FA0B7AC81A958EA3000E1D17 /* utility.h in Headers */, @@ -3257,6 +3287,7 @@ FA0B7D0B1A95902C000E1D17 /* wrap_FileData.h in Headers */, FA0B7DF91A95902C000E1D17 /* Body.h in Headers */, FA0B7DB91A95902C000E1D17 /* Joystick.h in Headers */, + FA4F2BB11DE1E37B00CA37D7 /* RecordingDevice.h in Headers */, FA0B7E2C1A95902C000E1D17 /* RevoluteJoint.h in Headers */, FA8951A41AA2EDF300EC385A /* wrap_Event.h in Headers */, FA0B7B2A1A958EA3000E1D17 /* simplexnoise1234.h in Headers */, @@ -3299,6 +3330,7 @@ FA0B79451A958E3B000E1D17 /* Variant.h in Headers */, FA0B7D691A95902C000E1D17 /* wrap_ParticleSystem.h in Headers */, FA0B7E5C1A95902C000E1D17 /* wrap_MotorJoint.h in Headers */, + FA4F2BA91DE1E36400CA37D7 /* wrap_RecordingDevice.h in Headers */, FA0B7A7C1A958EA3000E1D17 /* b2Contact.h in Headers */, FA0B7A881A958EA3000E1D17 /* b2PolygonAndCircleContact.h in Headers */, FA4F2B7A1DE0125B00CA37D7 /* xxhash.h in Headers */, @@ -3519,6 +3551,7 @@ FA0B7A3F1A958EA3000E1D17 /* b2ChainShape.cpp in Sources */, FA0B7E921A95902C000E1D17 /* ModPlugDecoder.cpp in Sources */, FA0B7E521A95902C000E1D17 /* wrap_FrictionJoint.cpp in Sources */, + FA4F2BB51DE1E4C300CA37D7 /* wrap_RecordingDevice.cpp in Sources */, FA0B7A311A958EA3000E1D17 /* b2CollidePolygon.cpp in Sources */, FA0B7A931A958EA3000E1D17 /* b2GearJoint.cpp in Sources */, FA0B7E0D1A95902C000E1D17 /* Fixture.cpp in Sources */, @@ -3576,6 +3609,7 @@ FA0B7A871A958EA3000E1D17 /* b2PolygonAndCircleContact.cpp in Sources */, FA0B7EF21A959D2C000E1D17 /* ios.mm in Sources */, FA0B7D3A1A95902C000E1D17 /* Graphics.cpp in Sources */, + FA4F2BB21DE1E4B400CA37D7 /* RecordingDevice.cpp in Sources */, FA0B7A2D1A958EA3000E1D17 /* b2CollideCircle.cpp in Sources */, FA59A2D31C06481400328DBA /* ParticleSystem.cpp in Sources */, FA0B7E131A95902C000E1D17 /* GearJoint.cpp in Sources */, @@ -3615,6 +3649,7 @@ FA0B7E2B1A95902C000E1D17 /* RevoluteJoint.cpp in Sources */, FA0B7B291A958EA3000E1D17 /* simplexnoise1234.cpp in Sources */, FA0B7D261A95902C000E1D17 /* wrap_Font.cpp in Sources */, + FA4F2BB31DE1E4B800CA37D7 /* RecordingDevice.cpp in Sources */, FA0B7E0A1A95902C000E1D17 /* EdgeShape.cpp in Sources */, FAB17BF11ABFB37500F9BA27 /* wrap_CompressedData.cpp in Sources */, FA0B7CF81A95902C000E1D17 /* FileData.cpp in Sources */, @@ -3646,6 +3681,7 @@ FA0B7CE31A95902C000E1D17 /* wrap_Audio.cpp in Sources */, FA0B7B381A958EA3000E1D17 /* wuff_internal.c in Sources */, FA0B7DF81A95902C000E1D17 /* Body.cpp in Sources */, + FA4F2BB41DE1E4BD00CA37D7 /* RecordingDevice.cpp in Sources */, FA0B7DF51A95902C000E1D17 /* wrap_Mouse.cpp in Sources */, FA0B7D6E1A95902C000E1D17 /* wrap_Shader.cpp in Sources */, FA0B7E861A95902C000E1D17 /* CoreAudioDecoder.cpp in Sources */, @@ -3767,7 +3803,9 @@ FA0B7E631A95902C000E1D17 /* wrap_PolygonShape.cpp in Sources */, FA0B7E721A95902C000E1D17 /* wrap_Shape.cpp in Sources */, FA0B7CFD1A95902C000E1D17 /* File.cpp in Sources */, + FA4F2BB01DE1E37B00CA37D7 /* RecordingDevice.cpp in Sources */, FA27B39D1B498151008A9DCE /* Video.cpp in Sources */, + FA4F2BAC1DE1E37000CA37D7 /* RecordingDevice.cpp in Sources */, FA0B7A2E1A958EA3000E1D17 /* b2CollideEdge.cpp in Sources */, FA0B7E691A95902C000E1D17 /* wrap_PulleyJoint.cpp in Sources */, FA0B7DB71A95902C000E1D17 /* Joystick.cpp in Sources */, @@ -3924,6 +3962,7 @@ FA0B7A981A958EA3000E1D17 /* b2MotorJoint.cpp in Sources */, FA0B7E061A95902C000E1D17 /* DistanceJoint.cpp in Sources */, FA620A321AA2F8DB005DB4C2 /* wrap_Quad.cpp in Sources */, + FA4F2BA61DE1E36400CA37D7 /* RecordingDevice.cpp in Sources */, FA0B793B1A958E3B000E1D17 /* runtime.cpp in Sources */, FA0B7E5D1A95902C000E1D17 /* wrap_MouseJoint.cpp in Sources */, FA0B7A741A958EA3000E1D17 /* b2ChainAndPolygonContact.cpp in Sources */, @@ -4016,6 +4055,7 @@ FA0B7A891A958EA3000E1D17 /* b2PolygonContact.cpp in Sources */, FA0B7E1E1A95902C000E1D17 /* Physics.cpp in Sources */, FA0B7E811A95902C000E1D17 /* Shape.cpp in Sources */, + FA4F2BA81DE1E36400CA37D7 /* wrap_RecordingDevice.cpp in Sources */, FA0B7B251A958EA3000E1D17 /* lutf8lib.c in Sources */, FA0B7ED41A95902D000E1D17 /* Timer.cpp in Sources */, FA19C4C51B4B0BD50059B0B3 /* wrap_Video.cpp in Sources */, diff --git a/src/modules/audio/openal/Audio.cpp b/src/modules/audio/openal/Audio.cpp index 8b6f20ead..920197d05 100644 --- a/src/modules/audio/openal/Audio.cpp +++ b/src/modules/audio/openal/Audio.cpp @@ -356,7 +356,7 @@ const std::vector &Audio::getRecordingDevices() devices.reserve(devnames.size()); //build ordered list of devices - for (unsigned int i = 0; i < devnames.size(); i++) + for (int i = 0; i < (int) devnames.size(); i++) { devices.push_back(nullptr); auto d = devices.end() - 1; diff --git a/src/modules/audio/wrap_RecordingDevice.cpp b/src/modules/audio/wrap_RecordingDevice.cpp index d4d3cc534..47d62a4f4 100644 --- a/src/modules/audio/wrap_RecordingDevice.cpp +++ b/src/modules/audio/wrap_RecordingDevice.cpp @@ -37,18 +37,17 @@ int w_RecordingDevice_startRecording(lua_State *L) RecordingDevice *d = luax_checkrecordingdevice(L, 1); if (lua_gettop(L) > 1) { - int samples = luaL_checkinteger(L, 2); - int sampleRate = luaL_checkinteger(L, 3); - int bitDepth = luaL_checkinteger(L, 4); - int channels = luaL_checkinteger(L, 5); + int samples = (int) luaL_checkinteger(L, 2); + int sampleRate = (int) luaL_checkinteger(L, 3); + int bitDepth = (int) luaL_checkinteger(L, 4); + int channels = (int) luaL_checkinteger(L, 5); luax_catchexcept(L, [&](){ lua_pushboolean(L, d->startRecording(samples, sampleRate, bitDepth, channels)); }); } else - luax_catchexcept(L, [&](){ - lua_pushboolean(L, d->startRecording()); - }); + luax_catchexcept(L, [&](){ lua_pushboolean(L, d->startRecording()); }); + return 1; } @@ -57,10 +56,9 @@ int w_RecordingDevice_stopRecording(lua_State *L) RecordingDevice *d = luax_checkrecordingdevice(L, 1); love::sound::SoundData *s = nullptr; - luax_catchexcept(L, [&](){ - s = d->getData(); - d->stopRecording(); - }); + luax_catchexcept(L, [&](){ s = d->getData(); }); + + d->stopRecording(); if (s != nullptr) { @@ -78,9 +76,7 @@ int w_RecordingDevice_getData(lua_State *L) RecordingDevice *d = luax_checkrecordingdevice(L, 1); love::sound::SoundData *s = nullptr; - luax_catchexcept(L, [&](){ - s = d->getData(); - }); + luax_catchexcept(L, [&](){ s = d->getData(); }); if (s != nullptr) { From 639c229bd73910e5c5e2dede1702a0a037553803 Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Sun, 20 Nov 2016 10:08:07 -0400 Subject: [PATCH 24/37] Rename RecordingDevice:startRecording and RecordingDevice:stopRecording to RecordingDevice:start and RecordingDevice:stop. --HG-- branch : minor --- src/modules/audio/RecordingDevice.h | 6 +++--- src/modules/audio/null/RecordingDevice.cpp | 6 +++--- src/modules/audio/null/RecordingDevice.h | 6 +++--- src/modules/audio/openal/RecordingDevice.cpp | 8 ++++---- src/modules/audio/openal/RecordingDevice.h | 6 +++--- src/modules/audio/wrap_RecordingDevice.cpp | 14 +++++++------- 6 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/modules/audio/RecordingDevice.h b/src/modules/audio/RecordingDevice.h index 48c7d41cd..0714c0e9f 100644 --- a/src/modules/audio/RecordingDevice.h +++ b/src/modules/audio/RecordingDevice.h @@ -41,7 +41,7 @@ public: * Begins audio input recording process. using default (previous) parameters. * @return True if recording started successfully. **/ - virtual bool startRecording() = 0; + virtual bool start() = 0; /** * Begins audio input recording process. @@ -51,12 +51,12 @@ public: * @param channels Desired number of channels. * @return True if recording started successfully. **/ - virtual bool startRecording(int samples, int sampleRate, int bitDepth, int channels) = 0; + virtual bool start(int samples, int sampleRate, int bitDepth, int channels) = 0; /** * Stops audio input recording. **/ - virtual void stopRecording() = 0; + virtual void stop() = 0; /** * Retreives recorded data. diff --git a/src/modules/audio/null/RecordingDevice.cpp b/src/modules/audio/null/RecordingDevice.cpp index 876683c17..94af141e7 100644 --- a/src/modules/audio/null/RecordingDevice.cpp +++ b/src/modules/audio/null/RecordingDevice.cpp @@ -38,17 +38,17 @@ RecordingDevice::~RecordingDevice() { } -bool RecordingDevice::startRecording() +bool RecordingDevice::start() { return false; } -bool RecordingDevice::startRecording(int, int, int, int) +bool RecordingDevice::start(int, int, int, int) { return false; } -void RecordingDevice::stopRecording() +void RecordingDevice::stop() { } diff --git a/src/modules/audio/null/RecordingDevice.h b/src/modules/audio/null/RecordingDevice.h index c679c9a92..11294d75c 100644 --- a/src/modules/audio/null/RecordingDevice.h +++ b/src/modules/audio/null/RecordingDevice.h @@ -36,9 +36,9 @@ class RecordingDevice : public love::audio::RecordingDevice public: RecordingDevice(const char *name); virtual ~RecordingDevice(); - virtual bool startRecording(); - virtual bool startRecording(int samples, int sampleRate, int bitDepth, int channels); - virtual void stopRecording(); + virtual bool start(); + virtual bool start(int samples, int sampleRate, int bitDepth, int channels); + virtual void stop(); virtual love::sound::SoundData *getData(); virtual const char *getName() const; virtual int getSampleCount() const; diff --git a/src/modules/audio/openal/RecordingDevice.cpp b/src/modules/audio/openal/RecordingDevice.cpp index e978e6e48..db59f4621 100644 --- a/src/modules/audio/openal/RecordingDevice.cpp +++ b/src/modules/audio/openal/RecordingDevice.cpp @@ -56,12 +56,12 @@ RecordingDevice::~RecordingDevice() alcCaptureCloseDevice(device); } -bool RecordingDevice::startRecording() +bool RecordingDevice::start() { - return startRecording(samples, sampleRate, bitDepth, channels); + return start(samples, sampleRate, bitDepth, channels); } -bool RecordingDevice::startRecording(int samples, int sampleRate, int bitDepth, int channels) +bool RecordingDevice::start(int samples, int sampleRate, int bitDepth, int channels) { if (isRecording()) { @@ -91,7 +91,7 @@ bool RecordingDevice::startRecording(int samples, int sampleRate, int bitDepth, return true; } -void RecordingDevice::stopRecording() +void RecordingDevice::stop() { if (!isRecording()) return; diff --git a/src/modules/audio/openal/RecordingDevice.h b/src/modules/audio/openal/RecordingDevice.h index 463ca3a38..2ccaea240 100644 --- a/src/modules/audio/openal/RecordingDevice.h +++ b/src/modules/audio/openal/RecordingDevice.h @@ -49,9 +49,9 @@ class RecordingDevice : public love::audio::RecordingDevice public: RecordingDevice(const char *name); virtual ~RecordingDevice(); - virtual bool startRecording(); - virtual bool startRecording(int samples, int sampleRate, int bitDepth, int channels); - virtual void stopRecording(); + virtual bool start(); + virtual bool start(int samples, int sampleRate, int bitDepth, int channels); + virtual void stop(); virtual love::sound::SoundData *getData(); virtual const char *getName() const; virtual int getSampleCount() const; diff --git a/src/modules/audio/wrap_RecordingDevice.cpp b/src/modules/audio/wrap_RecordingDevice.cpp index 47d62a4f4..49e93ad4a 100644 --- a/src/modules/audio/wrap_RecordingDevice.cpp +++ b/src/modules/audio/wrap_RecordingDevice.cpp @@ -32,7 +32,7 @@ RecordingDevice *luax_checkrecordingdevice(lua_State *L, int idx) return luax_checktype(L, idx, AUDIO_RECORDING_DEVICE_ID); } -int w_RecordingDevice_startRecording(lua_State *L) +int w_RecordingDevice_start(lua_State *L) { RecordingDevice *d = luax_checkrecordingdevice(L, 1); if (lua_gettop(L) > 1) @@ -42,23 +42,23 @@ int w_RecordingDevice_startRecording(lua_State *L) int bitDepth = (int) luaL_checkinteger(L, 4); int channels = (int) luaL_checkinteger(L, 5); luax_catchexcept(L, [&](){ - lua_pushboolean(L, d->startRecording(samples, sampleRate, bitDepth, channels)); + lua_pushboolean(L, d->start(samples, sampleRate, bitDepth, channels)); }); } else - luax_catchexcept(L, [&](){ lua_pushboolean(L, d->startRecording()); }); + luax_catchexcept(L, [&](){ lua_pushboolean(L, d->start()); }); return 1; } -int w_RecordingDevice_stopRecording(lua_State *L) +int w_RecordingDevice_stop(lua_State *L) { RecordingDevice *d = luax_checkrecordingdevice(L, 1); love::sound::SoundData *s = nullptr; luax_catchexcept(L, [&](){ s = d->getData(); }); - d->stopRecording(); + d->stop(); if (s != nullptr) { @@ -133,8 +133,8 @@ int w_RecordingDevice_isRecording(lua_State *L) static const luaL_Reg w_RecordingDevice_functions[] = { - { "startRecording", w_RecordingDevice_startRecording }, - { "stopRecording", w_RecordingDevice_stopRecording }, + { "start", w_RecordingDevice_start }, + { "stop", w_RecordingDevice_stop }, { "getData", w_RecordingDevice_getData }, { "getSampleCount", w_RecordingDevice_getSampleCount }, { "getSampleRate", w_RecordingDevice_getSampleRate }, From 9d2ee6d909c8429dc1acc1829890c8d6089ae601 Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Mon, 21 Nov 2016 18:28:16 -0400 Subject: [PATCH 25/37] Shader uniforms keep their values after love.window.setMode (resolves issue #1174). Also allow arrays of images/samplers in shaders, although driver support is iffy and you have to index into the array using a compile-time constant (which might exclude loop indexing). --HG-- branch : minor --- src/modules/graphics/opengl/Canvas.cpp | 3 + src/modules/graphics/opengl/Image.cpp | 3 + src/modules/graphics/opengl/OpenGL.h | 4 +- src/modules/graphics/opengl/Shader.cpp | 487 ++++++++++++-------- src/modules/graphics/opengl/Shader.h | 51 +- src/modules/graphics/opengl/wrap_Shader.cpp | 45 +- 6 files changed, 351 insertions(+), 242 deletions(-) diff --git a/src/modules/graphics/opengl/Canvas.cpp b/src/modules/graphics/opengl/Canvas.cpp index ffafa4182..f5cc027ca 100644 --- a/src/modules/graphics/opengl/Canvas.cpp +++ b/src/modules/graphics/opengl/Canvas.cpp @@ -151,6 +151,9 @@ Canvas::~Canvas() bool Canvas::loadVolatile() { + if (texture != 0) + return true; + OpenGL::TempDebugGroup debuggroup("Canvas load"); fbo = texture = 0; diff --git a/src/modules/graphics/opengl/Image.cpp b/src/modules/graphics/opengl/Image.cpp index 73b6fae81..0d0e8c7d3 100644 --- a/src/modules/graphics/opengl/Image.cpp +++ b/src/modules/graphics/opengl/Image.cpp @@ -281,6 +281,9 @@ void Image::loadFromImageData() bool Image::loadVolatile() { + if (texture != 0) + return true; + OpenGL::TempDebugGroup debuggroup("Image load"); if (isCompressed() && !hasCompressedTextureSupport(cdata[0]->getFormat(), sRGB)) diff --git a/src/modules/graphics/opengl/OpenGL.h b/src/modules/graphics/opengl/OpenGL.h index c740c35e6..f0905de10 100644 --- a/src/modules/graphics/opengl/OpenGL.h +++ b/src/modules/graphics/opengl/OpenGL.h @@ -106,8 +106,8 @@ public: enum FramebufferTarget { - FRAMEBUFFER_READ = (1 << 1), - FRAMEBUFFER_DRAW = (1 << 2), + FRAMEBUFFER_READ = (1 << 0), + FRAMEBUFFER_DRAW = (1 << 1), FRAMEBUFFER_ALL = (FRAMEBUFFER_READ | FRAMEBUFFER_DRAW), }; diff --git a/src/modules/graphics/opengl/Shader.cpp b/src/modules/graphics/opengl/Shader.cpp index 0900a94f9..6ad34bcea 100644 --- a/src/modules/graphics/opengl/Shader.cpp +++ b/src/modules/graphics/opengl/Shader.cpp @@ -41,11 +41,12 @@ namespace // reattaches the originally active program when destroyed struct TemporaryAttacher { - TemporaryAttacher(Shader *shader) + TemporaryAttacher(Shader *shader, bool attachNow) : curShader(shader) , prevShader(Shader::current) { - curShader->attach(true); + if (attachNow) + attach(); } ~TemporaryAttacher() @@ -56,6 +57,11 @@ namespace curShader->detach(); } + void attach() + { + curShader->attach(true); + } + Shader *curShader; Shader *prevShader; }; @@ -69,8 +75,6 @@ Shader *Shader::defaultVideoShader = nullptr; Shader::ShaderSource Shader::defaultCode[Graphics::RENDERER_MAX_ENUM][2]; Shader::ShaderSource Shader::defaultVideoCode[Graphics::RENDERER_MAX_ENUM][2]; -std::vector Shader::textureCounters; - Shader::Shader(const ShaderSource &source) : shaderSource(source) , program(0) @@ -84,10 +88,6 @@ Shader::Shader(const ShaderSource &source) if (source.vertex.empty() && source.pixel.empty()) throw love::Exception("Cannot create shader: no source code!"); - // initialize global texture id counters if needed - if ((int) textureCounters.size() < gl.getMaxTextureUnits() - 1) - textureCounters.resize(gl.getMaxTextureUnits() - 1, 0); - // load shader source and create program object loadVolatile(); } @@ -97,12 +97,25 @@ Shader::~Shader() if (current == this) detach(); - for (const auto &retainable : boundRetainables) - retainable.second->release(); - - boundRetainables.clear(); - unloadVolatile(); + + for (const auto &p : uniforms) + { + // Allocated with malloc(). + if (p.second.data != nullptr) + free(p.second.data); + + if (p.second.baseType == UNIFORM_SAMPLER) + { + for (int i = 0; i < p.second.count; i++) + { + if (p.second.textures[i] != nullptr) + p.second.textures[i]->release(); + } + + delete[] p.second.textures; + } + } } GLuint Shader::compileCode(ShaderStage stage, const std::string &code) @@ -176,8 +189,6 @@ void Shader::mapActiveUniforms() for (int i = 0; i < int(BUILTIN_MAX_ENUM); i++) builtinUniforms[i] = -1; - uniforms.clear(); - GLint activeprogram = 0; glGetIntegerv(GL_CURRENT_PROGRAM, &activeprogram); @@ -189,6 +200,9 @@ void Shader::mapActiveUniforms() GLchar cname[256]; const GLint bufsize = (GLint) (sizeof(cname) / sizeof(GLchar)); + std::map olduniforms = uniforms; + uniforms.clear(); + for (int i = 0; i < numuniforms; i++) { GLsizei namelen = 0; @@ -206,12 +220,6 @@ void Shader::mapActiveUniforms() else u.components = getUniformTypeComponents(gltype); - // Initialize all samplers to 0. Both GLSL and GLSL ES are supposed to - // do this themselves, but some Android devices (galaxy tab 3 and 4) - // don't seem to do it... - if (u.baseType == UNIFORM_SAMPLER) - glUniform1i(u.location, 0); - // glGetActiveUniform appends "[0]" to the end of array uniform names... if (u.name.length() > 3) { @@ -225,8 +233,135 @@ void Shader::mapActiveUniforms() if (builtinNames.find(u.name.c_str(), builtin)) builtinUniforms[int(builtin)] = u.location; - if (u.location != -1) - uniforms[u.name] = u; + if (u.location == -1) + continue; + + // Make sure previously set uniform data is preserved, and shader- + // initialized values are retrieved. + auto oldu = olduniforms.find(u.name); + if (oldu != olduniforms.end()) + { + u.data = oldu->second.data; + u.textures = oldu->second.textures; + + updateUniform(&u, u.count, true); + + if (u.baseType == UNIFORM_SAMPLER) + { + // Make sure all stored textures have their Volatiles loaded + // before the sendTextures call, since it calls getHandle(). + for (int i = 0; i < u.count; i++) + { + if (u.textures[i] == nullptr) + continue; + + Volatile *v = dynamic_cast(u.textures[i]); + if (v != nullptr) + v->loadVolatile(); + } + + sendTextures(&u, u.textures, u.count, true); + } + } + else + { + size_t datasize = 0; + + switch (u.baseType) + { + case UNIFORM_FLOAT: + datasize = sizeof(float) * u.components * u.count; + u.data = malloc(datasize); + break; + case UNIFORM_INT: + case UNIFORM_BOOL: + case UNIFORM_SAMPLER: + datasize = sizeof(int) * u.components * u.count; + u.data = malloc(datasize); + break; + case UNIFORM_MATRIX: + datasize = sizeof(float) * (u.matrix.rows * u.matrix.columns) * u.count; + u.data = malloc(datasize); + break; + default: + break; + } + + if (datasize > 0) + { + memset(u.data, 0, datasize); + + if (u.baseType == UNIFORM_SAMPLER) + { + // Initialize all samplers to 0. Both GLSL and GLSL ES are + // supposed to do this themselves, but some Android devices + // (galaxy tab 3 and 4) don't seem to do it... + glUniform1iv(u.location, u.count, u.ints); + + u.textures = new Texture*[u.count]; + memset(u.textures, 0, sizeof(Texture *) * u.count); + } + } + + size_t offset = 0; + + // Store any shader-initialized values in our own memory. + for (int i = 0; i < u.count; i++) + { + GLint location = u.location; + + if (u.count > 1) + { + std::string indexname = u.name + "[" + std::to_string(i) + "]"; + location = glGetUniformLocation(program, indexname.c_str()); + } + + if (location == -1) + continue; + + switch (u.baseType) + { + case UNIFORM_FLOAT: + glGetUniformfv(program, location, &u.floats[offset]); + offset += u.components; + break; + case UNIFORM_INT: + case UNIFORM_BOOL: + glGetUniformiv(program, location, &u.ints[offset]); + offset += u.components; + break; + case UNIFORM_MATRIX: + glGetUniformfv(program, location, &u.floats[offset]); + offset += u.matrix.rows * u.matrix.columns; + break; + default: + break; + } + } + } + + uniforms[u.name] = u; + } + + // Make sure uniforms that existed before but don't exist anymore are + // cleaned up. This theoretically shouldn't happen, but... + for (const auto &p : olduniforms) + { + if (uniforms.find(p.first) == uniforms.end()) + { + free(p.second.data); + + if (p.second.baseType != UNIFORM_SAMPLER) + continue; + + for (int i = 0; i < p.second.count; i++) + { + if (p.second.textures[i] != nullptr) + p.second.textures[i]->release(); + } + + delete[] p.second.textures; + } } gl.useProgram(activeprogram); @@ -251,8 +386,8 @@ bool Shader::loadVolatile() videoTextureUnits[i] = 0; // zero out active texture list - activeTexUnits.clear(); - activeTexUnits.insert(activeTexUnits.begin(), gl.getMaxTextureUnits() - 1, 0); + textureUnits.clear(); + textureUnits.resize(gl.getMaxTextureUnits(), TextureUnit()); std::vector shaderids; @@ -348,22 +483,12 @@ void Shader::unloadVolatile() program = 0; } - // decrement global texture id counters for texture units which had textures bound from this shader - for (size_t i = 0; i < activeTexUnits.size(); ++i) - { - if (activeTexUnits[i] > 0) - textureCounters[i] = std::max(textureCounters[i] - 1, 0); - } - // active texture list is probably invalid, clear it - activeTexUnits.clear(); - activeTexUnits.resize(gl.getMaxTextureUnits() - 1, 0); + textureUnits.clear(); + textureUnits.resize(gl.getMaxTextureUnits(), TextureUnit()); attributes.clear(); - // same with uniform location list - uniforms.clear(); - // And the locations of any built-in uniform variables. for (int i = 0; i < int(BUILTIN_MAX_ENUM); i++) builtinUniforms[i] = -1; @@ -420,10 +545,10 @@ void Shader::attach(bool temporary) { // make sure all sent textures are properly bound to their respective texture units // note: list potentially contains texture ids of deleted/invalid textures! - for (int i = 0; i < (int) activeTexUnits.size(); ++i) + for (int i = 1; i < (int) textureUnits.size(); ++i) { - if (activeTexUnits[i] > 0) - gl.bindTextureToUnit(activeTexUnits[i], i + 1, false); + if (textureUnits[i].active) + gl.bindTextureToUnit(textureUnits[i].texture, i, false); } } } @@ -455,160 +580,148 @@ const Shader::UniformInfo *Shader::getUniformInfo(const std::string &name) const return &(it->second); } -void Shader::sendInts(const UniformInfo *info, const int *vec, int count) +void Shader::updateUniform(const UniformInfo *info, int count, bool internalUpdate) { - if (info->baseType != UNIFORM_INT && info->baseType != UNIFORM_BOOL) - return; - - TemporaryAttacher attacher(this); + TemporaryAttacher attacher(this, !internalUpdate); int location = info->location; + UniformType type = info->baseType; - switch (info->components) + if (type == UNIFORM_FLOAT) { - case 4: - glUniform4iv(location, count, vec); - break; - case 3: - glUniform3iv(location, count, vec); - break; - case 2: - glUniform2iv(location, count, vec); - break; - case 1: - default: - glUniform1iv(location, count, vec); - break; + switch (info->components) + { + case 1: + glUniform1fv(location, count, info->floats); + break; + case 2: + glUniform2fv(location, count, info->floats); + break; + case 3: + glUniform3fv(location, count, info->floats); + break; + case 4: + glUniform4fv(location, count, info->floats); + break; + } + } + else if (type == UNIFORM_INT || type == UNIFORM_BOOL || type == UNIFORM_SAMPLER) + { + switch (info->components) + { + case 1: + glUniform1iv(location, count, info->ints); + break; + case 2: + glUniform2iv(location, count, info->ints); + break; + case 3: + glUniform3iv(location, count, info->ints); + break; + case 4: + glUniform4iv(location, count, info->ints); + break; + } + } + else if (type == UNIFORM_MATRIX) + { + int columns = info->matrix.columns; + int rows = info->matrix.rows; + + if (columns == 2 && rows == 2) + glUniformMatrix2fv(location, count, GL_FALSE, info->floats); + else if (columns == 3 && rows == 3) + glUniformMatrix3fv(location, count, GL_FALSE, info->floats); + else if (columns == 4 && rows == 4) + glUniformMatrix4fv(location, count, GL_FALSE, info->floats); + else if (columns == 2 && rows == 3) + glUniformMatrix2x3fv(location, count, GL_FALSE, info->floats); + else if (columns == 2 && rows == 4) + glUniformMatrix2x4fv(location, count, GL_FALSE, info->floats); + else if (columns == 3 && rows == 2) + glUniformMatrix3x2fv(location, count, GL_FALSE, info->floats); + else if (columns == 3 && rows == 4) + glUniformMatrix3x4fv(location, count, GL_FALSE, info->floats); + else if (columns == 4 && rows == 2) + glUniformMatrix4x2fv(location, count, GL_FALSE, info->floats); + else if (columns == 4 && rows == 3) + glUniformMatrix4x3fv(location, count, GL_FALSE, info->floats); } } -void Shader::sendFloats(const UniformInfo *info, const float *vec, int count) +int Shader::getFreeTextureUnits(int count) { - if (info->baseType != UNIFORM_FLOAT && info->baseType != UNIFORM_BOOL) - return; + int startunit = -1; - TemporaryAttacher attacher(this); - - int location = info->location; - - switch (info->components) + // Ignore the first texture unit for Shader-local texture bindings. + for (int i = 1; i < (int) textureUnits.size(); i++) { - case 4: - glUniform4fv(location, count, vec); - break; - case 3: - glUniform3fv(location, count, vec); - break; - case 2: - glUniform2fv(location, count, vec); - break; - case 1: - default: - glUniform1fv(location, count, vec); - break; + if (!textureUnits[i].active && i + count <= (int) textureUnits.size()) + { + startunit = i; + break; + } } + + if (startunit == -1) + throw love::Exception("No more texture units available for shader."); + + return startunit; } -void Shader::sendMatrices(const UniformInfo *info, const float *m, int count) -{ - if (info->baseType != UNIFORM_MATRIX) - return; - - TemporaryAttacher attacher(this); - - int location = info->location; - - int columns = info->matrix.columns; - int rows = info->matrix.rows; - - if (columns == 2 && rows == 2) - glUniformMatrix2fv(location, count, GL_FALSE, m); - else if (columns == 3 && rows == 3) - glUniformMatrix3fv(location, count, GL_FALSE, m); - else if (columns == 4 && rows == 4) - glUniformMatrix4fv(location, count, GL_FALSE, m); - else if (columns == 2 && rows == 3) - glUniformMatrix2x3fv(location, count, GL_FALSE, m); - else if (columns == 2 && rows == 4) - glUniformMatrix2x4fv(location, count, GL_FALSE, m); - else if (columns == 3 && rows == 2) - glUniformMatrix3x2fv(location, count, GL_FALSE, m); - else if (columns == 3 && rows == 4) - glUniformMatrix3x4fv(location, count, GL_FALSE, m); - else if (columns == 4 && rows == 2) - glUniformMatrix4x2fv(location, count, GL_FALSE, m); - else if (columns == 4 && rows == 3) - glUniformMatrix4x3fv(location, count, GL_FALSE, m); -} - -void Shader::sendTexture(const UniformInfo *info, Texture *texture) +void Shader::sendTextures(const UniformInfo *info, Texture **textures, int count, bool internalUpdate) { if (info->baseType != UNIFORM_SAMPLER) return; - GLuint gltex = *(GLuint *) texture->getHandle(); + count = std::min(count, info->count); + bool updateuniform = false; - TemporaryAttacher attacher(this); - - int texunit = getTextureUnit(info->name); - - // bind texture to assigned texture unit and send uniform to shader program - gl.bindTextureToUnit(gltex, texunit, false); - - glUniform1i(info->location, texunit); - - // increment global shader texture id counter for this texture unit, if we haven't already - if (activeTexUnits[texunit-1] == 0) - ++textureCounters[texunit-1]; - - // store texture id so it can be re-bound to the proper texture unit later - activeTexUnits[texunit-1] = gltex; - - retainObject(info->name, texture); -} - -void Shader::retainObject(const std::string &name, Object *object) -{ - object->retain(); - - auto it = boundRetainables.find(name); - if (it != boundRetainables.end()) - it->second->release(); - - boundRetainables[name] = object; -} - -int Shader::getTextureUnit(const std::string &name) -{ - auto it = texUnitPool.find(name); - - if (it != texUnitPool.end()) - return it->second; - - int texunit = 1; - - // prefer texture units which are unused by all other shaders - auto freeunit_it = std::find(textureCounters.begin(), textureCounters.end(), 0); - - if (freeunit_it != textureCounters.end()) + // Make sure the shader's samplers are associated with texture units. + for (int i = 0; i < count; i++) { - // we don't want to use unit 0 - texunit = (int) std::distance(textureCounters.begin(), freeunit_it) + 1; - } - else - { - // no completely unused texture units exist, try to use next free slot in our own list - auto nextunit_it = std::find(activeTexUnits.begin(), activeTexUnits.end(), 0); + if (info->ints[i] == 0 && textures[i] != nullptr) + { + int texunit = getFreeTextureUnits(1); + textureUnits[texunit].active = true; - if (nextunit_it == activeTexUnits.end()) - throw love::Exception("No more texture units available for shader."); - - // we don't want to use unit 0 - texunit = (int) std::distance(activeTexUnits.begin(), nextunit_it) + 1; + info->ints[i] = texunit; + updateuniform = true; + } } - texUnitPool[name] = texunit; - return texunit; + if (updateuniform) + updateUniform(info, count, internalUpdate); + + // Bind the textures to the texture units. + for (int i = 0; i < count; i++) + { + if (textures[i] != nullptr) + textures[i]->retain(); + + if (info->textures[i] != nullptr) + info->textures[i]->release(); + + info->textures[i] = textures[i]; + + int texunit = info->ints[i]; + + if (textures[i] != nullptr) + { + GLuint gltex = *(GLuint *) textures[i]->getHandle(); + + gl.bindTextureToUnit(gltex, texunit, false); + + // store texture id so it can be re-bound to the proper texture unit later + textureUnits[texunit].texture = gltex; + } + else + { + gl.bindTextureToUnit(0, texunit, false); + textureUnits[texunit].texture = 0; + textureUnits[texunit].active = false; + } + } } bool Shader::hasUniform(const std::string &name) const @@ -635,12 +748,12 @@ bool Shader::hasVertexAttrib(VertexAttribID attrib) const void Shader::setVideoTextures(GLuint ytexture, GLuint cbtexture, GLuint crtexture) { - TemporaryAttacher attacher(this); - // Set up the texture units that will be used by the shader to sample from // the textures, if they haven't been set up yet. if (videoTextureUnits[0] == 0) { + TemporaryAttacher attacher(this, true); + const GLint locs[3] = { builtinUniforms[BUILTIN_VIDEO_Y_CHANNEL], builtinUniforms[BUILTIN_VIDEO_CB_CHANNEL], @@ -656,14 +769,15 @@ void Shader::setVideoTextures(GLuint ytexture, GLuint cbtexture, GLuint crtextur { if (locs[i] >= 0 && names[i] != nullptr) { - videoTextureUnits[i] = getTextureUnit(names[i]); + const UniformInfo *info = getUniformInfo(names[i]); + if (info != nullptr) + { + videoTextureUnits[i] = getFreeTextureUnits(1); + textureUnits[videoTextureUnits[i]].active = true; - // Increment global shader texture id counter for this texture - // unit, if we haven't already. - if (activeTexUnits[videoTextureUnits[i] - 1] == 0) - ++textureCounters[videoTextureUnits[i] - 1]; - - glUniform1i(locs[i], videoTextureUnits[i]); + info->ints[0] = videoTextureUnits[i]; + updateUniform(info, 1); + } } } } @@ -676,7 +790,7 @@ void Shader::setVideoTextures(GLuint ytexture, GLuint cbtexture, GLuint crtextur if (videoTextureUnits[i] != 0) { // Store texture id so it can be re-bound later. - activeTexUnits[videoTextureUnits[i] - 1] = textures[i]; + textureUnits[videoTextureUnits[i]].texture = textures[i]; gl.bindTextureToUnit(textures[i], videoTextureUnits[i], false); } } @@ -718,7 +832,7 @@ void Shader::checkSetScreenParams() if (location >= 0) { - TemporaryAttacher attacher(this); + TemporaryAttacher attacher(this, true); glUniform4fv(location, 1, params); } @@ -735,7 +849,7 @@ void Shader::checkSetPointSize(float size) if (location >= 0) { - TemporaryAttacher attacher(this); + TemporaryAttacher attacher(this, true); glUniform1f(location, size); } @@ -755,7 +869,7 @@ void Shader::checkSetBuiltinUniforms() const Matrix4 &curxform = gl.matrices.transform.back(); const Matrix4 &curproj = gl.matrices.projection; - TemporaryAttacher attacher(this); + TemporaryAttacher attacher(this, true); bool tpmatrixneedsupdate = false; @@ -802,11 +916,6 @@ void Shader::checkSetBuiltinUniforms() } } -const std::map &Shader::getBoundRetainables() const -{ - return boundRetainables; -} - std::string Shader::getGLSLVersion() { const char *tmp = (const char *) glGetString(GL_SHADING_LANGUAGE_VERSION); diff --git a/src/modules/graphics/opengl/Shader.h b/src/modules/graphics/opengl/Shader.h index 8561f3b1e..2ead90da2 100644 --- a/src/modules/graphics/opengl/Shader.h +++ b/src/modules/graphics/opengl/Shader.h @@ -96,13 +96,24 @@ public: { int location; int count; + union { int components; MatrixSize matrix; }; + UniformType baseType; std::string name; + + union + { + void *data; + float *floats; + int *ints; + }; + + Texture **textures; }; // Pointer to currently active Shader. @@ -147,11 +158,9 @@ public: std::string getWarnings() const; const UniformInfo *getUniformInfo(const std::string &name) const; + void updateUniform(const UniformInfo *info, int count, bool internalUpdate = false); - void sendInts(const UniformInfo *info, const int *vec, int count); - void sendFloats(const UniformInfo *info, const float *vec, int count); - void sendMatrices(const UniformInfo *info, const float *m, int count); - void sendTexture(const UniformInfo *info, Texture *texture); + void sendTextures(const UniformInfo *info, Texture **textures, int count, bool internalUpdate = false); /** * Gets whether a uniform with the specified name exists and is actively @@ -171,24 +180,11 @@ public: void checkSetPointSize(float size); void checkSetBuiltinUniforms(); - const std::map &getBoundRetainables() const; - GLuint getProgram() const { return program; } - template - T *getScratchBuffer(size_t count) - { - size_t bytes = sizeof(T) * count; - - if (scratchBuffer.size() < bytes) - scratchBuffer.resize(bytes); - - return (T *) scratchBuffer.data(); - } - static std::string getGLSLVersion(); static bool isSupported(); @@ -200,6 +196,12 @@ public: private: + struct TextureUnit + { + GLuint texture = 0; + bool active = false; + }; + // Map active uniform names to their locations. void mapActiveUniforms(); @@ -209,9 +211,7 @@ private: GLuint compileCode(ShaderStage stage, const std::string &code); - int getTextureUnit(const std::string &name); - - void retainObject(const std::string &name, Object *object); + int getFreeTextureUnits(int count); // Get any warnings or errors generated only by the shader program object. std::string getProgramWarnings() const; @@ -237,11 +237,7 @@ private: std::map uniforms; // Texture unit pool for setting images - std::map texUnitPool; // texUnitPool[name] = textureunit - std::vector activeTexUnits; // activeTexUnits[textureunit-1] = textureid - - // Uniform name to retainable objects - std::map boundRetainables; + std::vector textureUnits; bool canvasWasActive; OpenGL::Viewport lastViewport; @@ -253,11 +249,6 @@ private: GLuint videoTextureUnits[3]; - std::vector scratchBuffer; - - // Counts total number of textures bound to each texture unit in all shaders - static std::vector textureCounters; - static StringMap::Entry stageNameEntries[]; static StringMap stageNames; diff --git a/src/modules/graphics/opengl/wrap_Shader.cpp b/src/modules/graphics/opengl/wrap_Shader.cpp index 3e0dfa191..25701c9e0 100644 --- a/src/modules/graphics/opengl/wrap_Shader.cpp +++ b/src/modules/graphics/opengl/wrap_Shader.cpp @@ -52,10 +52,8 @@ static int _getCount(lua_State *L, int startidx, const Shader::UniformInfo *info } template -static T *_getNumbers(lua_State *L, int startidx, Shader *shader, int components, int count) +static void _updateNumbers(lua_State *L, int startidx, T *values, int components, int count) { - T *values = shader->getScratchBuffer(components * count); - if (components == 1) { for (int i = 0; i < count; ++i) @@ -76,16 +74,15 @@ static T *_getNumbers(lua_State *L, int startidx, Shader *shader, int components lua_pop(L, components); } } - - return values; } int w_Shader_sendFloats(lua_State *L, int startidx, Shader *shader, const Shader::UniformInfo *info, bool colors) { int count = _getCount(L, startidx, info); int components = info->components; + float *values = info->floats; - float *values = _getNumbers(L, startidx, shader, components, count); + _updateNumbers(L, startidx, values, components, count); if (colors && graphics::isGammaCorrect()) { @@ -99,15 +96,15 @@ int w_Shader_sendFloats(lua_State *L, int startidx, Shader *shader, const Shader } } - luax_catchexcept(L, [&]() { shader->sendFloats(info, values, count); }); + luax_catchexcept(L, [&]() { shader->updateUniform(info, count); }); return 0; } int w_Shader_sendInts(lua_State *L, int startidx, Shader *shader, const Shader::UniformInfo *info) { int count = _getCount(L, startidx, info); - int *values = _getNumbers(L, startidx, shader, info->components, count); - luax_catchexcept(L, [&]() { shader->sendInts(info, values, count); }); + _updateNumbers(L, startidx, info->ints, info->components, count); + luax_catchexcept(L, [&]() { shader->updateUniform(info, count); }); return 0; } @@ -116,15 +113,15 @@ int w_Shader_sendBooleans(lua_State *L, int startidx, Shader *shader, const Shad int count = _getCount(L, startidx, info); int components = info->components; - // We have to send booleans as ints or floats. - float *values = shader->getScratchBuffer(components * count); + // We have to send booleans as ints. + int *values = info->ints; if (components == 1) { for (int i = 0; i < count; i++) { luaL_checktype(L, startidx + i, LUA_TBOOLEAN); - values[i] = (float) lua_toboolean(L, startidx + i); + values[i] = (int) lua_toboolean(L, startidx + i); } } else @@ -137,14 +134,14 @@ int w_Shader_sendBooleans(lua_State *L, int startidx, Shader *shader, const Shad { lua_rawgeti(L, startidx + i, k); luaL_checktype(L, -1, LUA_TBOOLEAN); - values[i * components + k - 1] = (float) lua_toboolean(L, -1); + values[i * components + k - 1] = (int) lua_toboolean(L, -1); } lua_pop(L, components); } } - luax_catchexcept(L, [&]() { shader->sendFloats(info, values, count); }); + luax_catchexcept(L, [&]() { shader->updateUniform(info, count); }); return 0; } @@ -163,7 +160,7 @@ int w_Shader_sendMatrices(lua_State *L, int startidx, Shader *shader, const Shad int rows = info->matrix.rows; int elements = columns * rows; - float *values = shader->getScratchBuffer(elements * count); + float *values = info->floats; for (int i = 0; i < count; i++) { @@ -243,15 +240,21 @@ int w_Shader_sendMatrices(lua_State *L, int startidx, Shader *shader, const Shad } } - shader->sendMatrices(info, values, count); + shader->updateUniform(info, count); return 0; } -int w_Shader_sendTexture(lua_State *L, int startidx, Shader *shader, const Shader::UniformInfo *info) +int w_Shader_sendTextures(lua_State *L, int startidx, Shader *shader, const Shader::UniformInfo *info) { - // We don't support arrays of textures (yet). - Texture *texture = luax_checktexture(L, startidx); - luax_catchexcept(L, [&]() { shader->sendTexture(info, texture); }); + int count = _getCount(L, startidx, info); + + std::vector textures; + textures.reserve(count); + + for (int i = 0; i < count; i++) + textures.push_back(luax_checktexture(L, startidx + i)); + + luax_catchexcept(L, [&]() { shader->sendTextures(info, textures.data(), count); }); return 0; } @@ -277,7 +280,7 @@ int w_Shader_send(lua_State *L) case Shader::UNIFORM_BOOL: return w_Shader_sendBooleans(L, startidx, shader, info); case Shader::UNIFORM_SAMPLER: - return w_Shader_sendTexture(L, startidx, shader, info); + return w_Shader_sendTextures(L, startidx, shader, info); default: return luaL_error(L, "Unknown variable type for shader uniform '%s", name); } From 14aa6965b3f0577060bd15039bc54b0cebf7c5af Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Thu, 24 Nov 2016 21:30:45 -0400 Subject: [PATCH 26/37] Added Transform objects to love.math (resolves issue #1228). - Added love.math.newTransform. - Added love.graphics.applyTransform and love.graphics.replaceTransform. - love.graphics.draw/print/printf, Text:add/addf, and SpriteBatch:add can accept a Transform argument. - love.graphics.push can accept an optional Transform as a second argument, which will apply the Transform after pushing the matrix stack, - Shader:send can accept Transform objects when sending to a mat4 uniform variable. --HG-- branch : minor --- CMakeLists.txt | 4 + .../xcode/liblove.xcodeproj/project.pbxproj | 20 ++ src/common/Matrix.cpp | 6 + src/common/Matrix.h | 9 + src/common/types.cpp | 3 +- src/common/types.h | 1 + src/modules/graphics/opengl/Graphics.cpp | 10 + src/modules/graphics/opengl/Graphics.h | 6 + src/modules/graphics/opengl/wrap_Graphics.cpp | 132 +++++--- src/modules/graphics/opengl/wrap_Shader.cpp | 8 + .../graphics/opengl/wrap_SpriteBatch.cpp | 46 ++- src/modules/graphics/opengl/wrap_Text.cpp | 71 ++-- src/modules/math/MathModule.cpp | 11 + src/modules/math/MathModule.h | 4 + src/modules/math/Transform.cpp | 131 ++++++++ src/modules/math/Transform.h | 83 +++++ src/modules/math/wrap_Math.cpp | 29 ++ src/modules/math/wrap_Transform.cpp | 305 ++++++++++++++++++ src/modules/math/wrap_Transform.h | 35 ++ 19 files changed, 820 insertions(+), 94 deletions(-) create mode 100644 src/modules/math/Transform.cpp create mode 100644 src/modules/math/Transform.h create mode 100644 src/modules/math/wrap_Transform.cpp create mode 100644 src/modules/math/wrap_Transform.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 14cdc3178..b1d3a3f39 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -655,6 +655,8 @@ set(LOVE_SRC_MODULE_MATH src/modules/math/MathModule.h src/modules/math/RandomGenerator.cpp src/modules/math/RandomGenerator.h + src/modules/math/Transform.cpp + src/modules/math/Transform.h src/modules/math/wrap_BezierCurve.cpp src/modules/math/wrap_BezierCurve.h src/modules/math/wrap_CompressedData.cpp @@ -663,6 +665,8 @@ set(LOVE_SRC_MODULE_MATH src/modules/math/wrap_Math.h src/modules/math/wrap_RandomGenerator.cpp src/modules/math/wrap_RandomGenerator.h + src/modules/math/wrap_Transform.cpp + src/modules/math/wrap_Transform.h ) source_group("modules\\math" FILES ${LOVE_SRC_MODULE_MATH}) diff --git a/platform/xcode/liblove.xcodeproj/project.pbxproj b/platform/xcode/liblove.xcodeproj/project.pbxproj index a5e757144..8426b1588 100644 --- a/platform/xcode/liblove.xcodeproj/project.pbxproj +++ b/platform/xcode/liblove.xcodeproj/project.pbxproj @@ -895,6 +895,12 @@ FA4F2BB31DE1E4B800CA37D7 /* RecordingDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA4F2BA21DE1E36400CA37D7 /* RecordingDevice.cpp */; }; FA4F2BB41DE1E4BD00CA37D7 /* RecordingDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA4F2BAA1DE1E37000CA37D7 /* RecordingDevice.cpp */; }; FA4F2BB51DE1E4C300CA37D7 /* wrap_RecordingDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA4F2BA41DE1E36400CA37D7 /* wrap_RecordingDevice.cpp */; }; + FA4F2BE31DE6650600CA37D7 /* Transform.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA4F2BDF1DE6650600CA37D7 /* Transform.cpp */; }; + FA4F2BE41DE6650600CA37D7 /* Transform.h in Headers */ = {isa = PBXBuildFile; fileRef = FA4F2BE01DE6650600CA37D7 /* Transform.h */; }; + FA4F2BE51DE6650600CA37D7 /* wrap_Transform.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA4F2BE11DE6650600CA37D7 /* wrap_Transform.cpp */; }; + FA4F2BE61DE6650600CA37D7 /* wrap_Transform.h in Headers */ = {isa = PBXBuildFile; fileRef = FA4F2BE21DE6650600CA37D7 /* wrap_Transform.h */; }; + FA4F2BE71DE6650D00CA37D7 /* Transform.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA4F2BDF1DE6650600CA37D7 /* Transform.cpp */; }; + FA4F2BE81DE6651000CA37D7 /* wrap_Transform.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA4F2BE11DE6650600CA37D7 /* wrap_Transform.cpp */; }; FA56D9BC1C208A0200D8D3C7 /* libmodplug.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FA56D9BA1C2089EE00D8D3C7 /* libmodplug.a */; }; FA577AB016C7507900860150 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA577A7916C71A1700860150 /* Cocoa.framework */; }; FA577AC216C7512D00860150 /* FreeType.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA577A6716C719D900860150 /* FreeType.framework */; }; @@ -1600,6 +1606,10 @@ FA4F2BAB1DE1E37000CA37D7 /* RecordingDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RecordingDevice.h; sourceTree = ""; }; FA4F2BAE1DE1E37B00CA37D7 /* RecordingDevice.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RecordingDevice.cpp; sourceTree = ""; }; FA4F2BAF1DE1E37B00CA37D7 /* RecordingDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RecordingDevice.h; sourceTree = ""; }; + FA4F2BDF1DE6650600CA37D7 /* Transform.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Transform.cpp; sourceTree = ""; }; + FA4F2BE01DE6650600CA37D7 /* Transform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Transform.h; sourceTree = ""; }; + FA4F2BE11DE6650600CA37D7 /* wrap_Transform.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_Transform.cpp; sourceTree = ""; }; + FA4F2BE21DE6650600CA37D7 /* wrap_Transform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_Transform.h; sourceTree = ""; }; FA56D9BA1C2089EE00D8D3C7 /* libmodplug.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libmodplug.a; sourceTree = ""; }; FA577A6716C719D900860150 /* FreeType.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FreeType.framework; path = /Library/Frameworks/FreeType.framework; sourceTree = ""; }; FA577A6D16C719EA00860150 /* Lua.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Lua.framework; path = /Library/Frameworks/Lua.framework; sourceTree = ""; }; @@ -2559,6 +2569,8 @@ FA0B7C041A95902C000E1D17 /* MathModule.h */, FA0B7C051A95902C000E1D17 /* RandomGenerator.cpp */, FA0B7C061A95902C000E1D17 /* RandomGenerator.h */, + FA4F2BDF1DE6650600CA37D7 /* Transform.cpp */, + FA4F2BE01DE6650600CA37D7 /* Transform.h */, FA0B7C071A95902C000E1D17 /* wrap_BezierCurve.cpp */, FA0B7C081A95902C000E1D17 /* wrap_BezierCurve.h */, FAB17BEE1ABFB37500F9BA27 /* wrap_CompressedData.cpp */, @@ -2569,6 +2581,8 @@ FA0B7C0B1A95902C000E1D17 /* wrap_RandomGenerator.cpp */, FA0B7C0C1A95902C000E1D17 /* wrap_RandomGenerator.h */, FA2E9BFE1C19E00C0004A1EE /* wrap_RandomGenerator.lua */, + FA4F2BE11DE6650600CA37D7 /* wrap_Transform.cpp */, + FA4F2BE21DE6650600CA37D7 /* wrap_Transform.h */, ); path = math; sourceTree = ""; @@ -3191,6 +3205,7 @@ FA0B7E6B1A95902C000E1D17 /* wrap_PulleyJoint.h in Headers */, FA0B7E051A95902C000E1D17 /* Contact.h in Headers */, FA0B7A691A958EA3000E1D17 /* b2Island.h in Headers */, + FA4F2BE41DE6650600CA37D7 /* Transform.h in Headers */, FA0B7E0E1A95902C000E1D17 /* Fixture.h in Headers */, FA0B7A401A958EA3000E1D17 /* b2ChainShape.h in Headers */, 217DFBE81D9F6D490055D849 /* inet.h in Headers */, @@ -3266,6 +3281,7 @@ 217DFBF71D9F6D490055D849 /* options.h in Headers */, FA0B7E3B1A95902C000E1D17 /* World.h in Headers */, FA0B7DC31A95902C000E1D17 /* wrap_Joystick.h in Headers */, + FA4F2BE61DE6650600CA37D7 /* wrap_Transform.h in Headers */, FA0B7EE71A95902D000E1D17 /* Window.h in Headers */, FA0B7E651A95902C000E1D17 /* wrap_PolygonShape.h in Headers */, FA0B7AC91A958EA3000E1D17 /* win32.h in Headers */, @@ -3501,6 +3517,7 @@ FA0B7AAB1A958EA3000E1D17 /* b2WeldJoint.cpp in Sources */, FA0B7EC21A95902C000E1D17 /* threads.cpp in Sources */, FA0B79371A958E3B000E1D17 /* macosx.mm in Sources */, + FA4F2BE81DE6651000CA37D7 /* wrap_Transform.cpp in Sources */, FA0B7D011A95902C000E1D17 /* Filesystem.cpp in Sources */, FA0B7ED91A95902D000E1D17 /* wrap_Timer.cpp in Sources */, FA0B7DD11A95902C000E1D17 /* love.cpp in Sources */, @@ -3583,6 +3600,7 @@ FA0B7D681A95902C000E1D17 /* wrap_ParticleSystem.cpp in Sources */, FA0B7AD51A958EA3000E1D17 /* unix.c in Sources */, FAB17BE71ABFAA9000F9BA27 /* lz4.c in Sources */, + FA4F2BE71DE6650D00CA37D7 /* Transform.cpp in Sources */, FA0B7D651A95902C000E1D17 /* wrap_Mesh.cpp in Sources */, FA0B7A531A958EA3000E1D17 /* b2Math.cpp in Sources */, FA0B7CDA1A95902C000E1D17 /* Pool.cpp in Sources */, @@ -3861,6 +3879,7 @@ FA0B7EDF1A95902D000E1D17 /* wrap_Touch.cpp in Sources */, FA0B794A1A958E3B000E1D17 /* wrap_Data.cpp in Sources */, 217DFBFB1D9F6D490055D849 /* serial.c in Sources */, + FA4F2BE51DE6650600CA37D7 /* wrap_Transform.cpp in Sources */, 217DFC0D1D9F6D490055D849 /* unixudp.c in Sources */, FA0B7CDC1A95902C000E1D17 /* Source.cpp in Sources */, FA0B7DC41A95902C000E1D17 /* wrap_JoystickModule.cpp in Sources */, @@ -4045,6 +4064,7 @@ FA0B7CDF1A95902C000E1D17 /* Source.cpp in Sources */, FA0B7ECE1A95902C000E1D17 /* wrap_LuaThread.cpp in Sources */, FA0B79431A958E3B000E1D17 /* Variant.cpp in Sources */, + FA4F2BE31DE6650600CA37D7 /* Transform.cpp in Sources */, FA0B7EA01A95902C000E1D17 /* Sound.cpp in Sources */, FA0B7DE51A95902C000E1D17 /* Cursor.cpp in Sources */, FA0B7EDB1A95902D000E1D17 /* Touch.cpp in Sources */, diff --git a/src/common/Matrix.cpp b/src/common/Matrix.cpp index 9e6077fa9..f6f6b4dcd 100644 --- a/src/common/Matrix.cpp +++ b/src/common/Matrix.cpp @@ -73,6 +73,12 @@ Matrix4::Matrix4() { setIdentity(); } + + +Matrix4::Matrix4(const float elements[16]) +{ + memcpy(e, elements, sizeof(float) * 16); +} Matrix4::Matrix4(float t00, float t10, float t01, float t11, float x, float y) { diff --git a/src/common/Matrix.h b/src/common/Matrix.h index 98945adc8..d5fd49a2b 100644 --- a/src/common/Matrix.h +++ b/src/common/Matrix.h @@ -52,6 +52,12 @@ public: **/ Matrix4(float t00, float t10, float t01, float t11, float x, float y); + /** + * Creates a new matrix from the specified elements. Be sure to pass + * exactly 16 elements in! + **/ + Matrix4(const float elements[16]); + /** * Creates a new matrix set to a transformation. **/ @@ -182,6 +188,9 @@ public: template void transform(V *dst, const V *src, int size) const; + /** + * Computes and returns the inverse of the matrix. + **/ Matrix4 inverse() const; /** diff --git a/src/common/types.cpp b/src/common/types.cpp index 2385315c9..80667c7a3 100644 --- a/src/common/types.cpp +++ b/src/common/types.cpp @@ -68,7 +68,8 @@ static const TypeBits *createTypeFlags() // Math. b[MATH_RANDOM_GENERATOR_ID] = (one << MATH_RANDOM_GENERATOR_ID) | b[OBJECT_ID]; b[MATH_BEZIER_CURVE_ID] = (one << MATH_BEZIER_CURVE_ID) | b[OBJECT_ID]; - b[MATH_COMPRESSED_DATA_ID] = (one <getMatrix(); +} + +void Graphics::replaceTransform(love::math::Transform *transform) +{ + gl.getTransform() = transform->getMatrix(); +} + Vector Graphics::transformPoint(Vector point) { Vector p; diff --git a/src/modules/graphics/opengl/Graphics.h b/src/modules/graphics/opengl/Graphics.h index 4bef0968f..9fbb5fdac 100644 --- a/src/modules/graphics/opengl/Graphics.h +++ b/src/modules/graphics/opengl/Graphics.h @@ -38,6 +38,8 @@ #include "video/VideoStream.h" +#include "math/Transform.h" + #include "Font.h" #include "Image.h" #include "graphics/Quad.h" @@ -488,6 +490,10 @@ public: void translate(float x, float y); void shear(float kx, float ky); void origin(); + + void applyTransform(love::math::Transform *transform); + void replaceTransform(love::math::Transform *transform); + Vector transformPoint(Vector point); Vector inverseTransformPoint(Vector point); diff --git a/src/modules/graphics/opengl/wrap_Graphics.cpp b/src/modules/graphics/opengl/wrap_Graphics.cpp index 7abed19cd..9833b2b05 100644 --- a/src/modules/graphics/opengl/wrap_Graphics.cpp +++ b/src/modules/graphics/opengl/wrap_Graphics.cpp @@ -1667,24 +1667,37 @@ int w_draw(lua_State *L) startidx = 2; } - float x = (float) luaL_optnumber(L, startidx + 0, 0.0); - float y = (float) luaL_optnumber(L, startidx + 1, 0.0); - float a = (float) luaL_optnumber(L, startidx + 2, 0.0); - float sx = (float) luaL_optnumber(L, startidx + 3, 1.0); - float sy = (float) luaL_optnumber(L, startidx + 4, sx); - float ox = (float) luaL_optnumber(L, startidx + 5, 0.0); - float oy = (float) luaL_optnumber(L, startidx + 6, 0.0); - float kx = (float) luaL_optnumber(L, startidx + 7, 0.0); - float ky = (float) luaL_optnumber(L, startidx + 8, 0.0); + if (luax_istype(L, startidx, MATH_TRANSFORM_ID)) + { + math::Transform *tf = luax_totype(L, startidx, MATH_TRANSFORM_ID); + luax_catchexcept(L, [&]() { + if (texture && quad) + instance()->drawq(texture, quad, tf->getMatrix()); + else + instance()->draw(drawable, tf->getMatrix()); + }); + } + else + { + float x = (float) luaL_optnumber(L, startidx + 0, 0.0); + float y = (float) luaL_optnumber(L, startidx + 1, 0.0); + float a = (float) luaL_optnumber(L, startidx + 2, 0.0); + float sx = (float) luaL_optnumber(L, startidx + 3, 1.0); + float sy = (float) luaL_optnumber(L, startidx + 4, sx); + float ox = (float) luaL_optnumber(L, startidx + 5, 0.0); + float oy = (float) luaL_optnumber(L, startidx + 6, 0.0); + float kx = (float) luaL_optnumber(L, startidx + 7, 0.0); + float ky = (float) luaL_optnumber(L, startidx + 8, 0.0); - Matrix4 m(x, y, a, sx, sy, ox, oy, kx, ky); + Matrix4 m(x, y, a, sx, sy, ox, oy, kx, ky); - luax_catchexcept(L, [&]() { - if (texture && quad) - instance()->drawq(texture, quad, m); - else if (drawable) - instance()->draw(drawable, m); - }); + luax_catchexcept(L, [&]() { + if (texture && quad) + instance()->drawq(texture, quad, m); + else if (drawable) + instance()->draw(drawable, m); + }); + } return 0; } @@ -1694,19 +1707,27 @@ int w_print(lua_State *L) std::vector str; luax_checkcoloredstring(L, 1, str); - float x = (float)luaL_optnumber(L, 2, 0.0); - float y = (float)luaL_optnumber(L, 3, 0.0); - float angle = (float)luaL_optnumber(L, 4, 0.0f); - float sx = (float)luaL_optnumber(L, 5, 1.0f); - float sy = (float)luaL_optnumber(L, 6, sx); - float ox = (float)luaL_optnumber(L, 7, 0.0f); - float oy = (float)luaL_optnumber(L, 8, 0.0f); - float kx = (float)luaL_optnumber(L, 9, 0.0f); - float ky = (float)luaL_optnumber(L, 10, 0.0f); + if (luax_istype(L, 2, MATH_TRANSFORM_ID)) + { + math::Transform *tf = luax_totype(L, 2, MATH_TRANSFORM_ID); + luax_catchexcept(L, [&](){ instance()->print(str, tf->getMatrix()); }); + } + else + { + float x = (float)luaL_optnumber(L, 2, 0.0); + float y = (float)luaL_optnumber(L, 3, 0.0); + float angle = (float)luaL_optnumber(L, 4, 0.0f); + float sx = (float)luaL_optnumber(L, 5, 1.0f); + float sy = (float)luaL_optnumber(L, 6, sx); + float ox = (float)luaL_optnumber(L, 7, 0.0f); + float oy = (float)luaL_optnumber(L, 8, 0.0f); + float kx = (float)luaL_optnumber(L, 9, 0.0f); + float ky = (float)luaL_optnumber(L, 10, 0.0f); - Matrix4 m(x, y, angle, sx, sy, ox, oy, kx, ky); + Matrix4 m(x, y, angle, sx, sy, ox, oy, kx, ky); - luax_catchexcept(L, [&](){ instance()->print(str, m); }); + luax_catchexcept(L, [&](){ instance()->print(str, m); }); + } return 0; } @@ -1715,36 +1736,38 @@ int w_printf(lua_State *L) std::vector str; luax_checkcoloredstring(L, 1, str); - float x = (float)luaL_checknumber(L, 2); - float y = (float)luaL_checknumber(L, 3); - float wrap = (float)luaL_checknumber(L, 4); - - float angle = 0.0f; - float sx = 1.0f, sy = 1.0f; - float ox = 0.0f, oy = 0.0f; - float kx = 0.0f, ky = 0.0f; - Font::AlignMode align = Font::ALIGN_LEFT; + Matrix4 m; - if (lua_gettop(L) >= 5) + int formatidx = 4; + + if (luax_istype(L, 2, MATH_TRANSFORM_ID)) { - if (!lua_isnil(L, 5)) - { - const char *str = luaL_checkstring(L, 5); - if (!Font::getConstant(str, align)) - return luaL_error(L, "Incorrect alignment: %s", str); - } + math::Transform *tf = luax_totype(L, 2, MATH_TRANSFORM_ID); + m = tf->getMatrix(); + formatidx = 3; + } + else + { + float x = (float)luaL_checknumber(L, 2); + float y = (float)luaL_checknumber(L, 3); - angle = (float) luaL_optnumber(L, 6, 0.0f); - sx = (float) luaL_optnumber(L, 7, 1.0f); - sy = (float) luaL_optnumber(L, 8, sx); - ox = (float) luaL_optnumber(L, 9, 0.0f); - oy = (float) luaL_optnumber(L, 10, 0.0f); - kx = (float) luaL_optnumber(L, 11, 0.0f); - ky = (float) luaL_optnumber(L, 12, 0.0f); + float angle = (float) luaL_optnumber(L, 6, 0.0f); + float sx = (float) luaL_optnumber(L, 7, 1.0f); + float sy = (float) luaL_optnumber(L, 8, sx); + float ox = (float) luaL_optnumber(L, 9, 0.0f); + float oy = (float) luaL_optnumber(L, 10, 0.0f); + float kx = (float) luaL_optnumber(L, 11, 0.0f); + float ky = (float) luaL_optnumber(L, 12, 0.0f); + + m = Matrix4(x, y, angle, sx, sy, ox, oy, kx, ky); } - Matrix4 m(x, y, angle, sx, sy, ox, oy, kx, ky); + float wrap = (float)luaL_checknumber(L, formatidx); + + const char *astr = lua_isnoneornil(L, formatidx + 1) ? nullptr : luaL_checkstring(L, formatidx + 1); + if (astr != nullptr && !Font::getConstant(astr, align)) + return luaL_error(L, "Incorrect alignment: %s", astr); luax_catchexcept(L, [&](){ instance()->printf(str, wrap, align, m); }); return 0; @@ -2048,6 +2071,13 @@ int w_push(lua_State *L) return luaL_error(L, "Invalid graphics stack type: %s", sname); luax_catchexcept(L, [&](){ instance()->push(stype); }); + + if (luax_istype(L, 2, MATH_TRANSFORM_ID)) + { + math::Transform *t = luax_totype(L, 2, MATH_TRANSFORM_ID); + instance()->applyTransform(t); + } + return 0; } diff --git a/src/modules/graphics/opengl/wrap_Shader.cpp b/src/modules/graphics/opengl/wrap_Shader.cpp index 25701c9e0..8a25c5ab8 100644 --- a/src/modules/graphics/opengl/wrap_Shader.cpp +++ b/src/modules/graphics/opengl/wrap_Shader.cpp @@ -21,6 +21,7 @@ #include "wrap_Shader.h" #include "graphics/wrap_Texture.h" #include "math/MathModule.h" +#include "math/Transform.h" #include #include @@ -164,6 +165,13 @@ int w_Shader_sendMatrices(lua_State *L, int startidx, Shader *shader, const Shad for (int i = 0; i < count; i++) { + if (columns == 4 && rows == 4 && luax_istype(L, startidx + i, MATH_TRANSFORM_ID)) + { + math::Transform *t = luax_totype(L, startidx + i, MATH_TRANSFORM_ID); + memcpy(&values[i * 16], t->getMatrix().getElements(), sizeof(float) * 16); + continue; + } + luaL_checktype(L, startidx + i, LUA_TTABLE); lua_rawgeti(L, startidx + i, 1); diff --git a/src/modules/graphics/opengl/wrap_SpriteBatch.cpp b/src/modules/graphics/opengl/wrap_SpriteBatch.cpp index 06b0312da..0bfbc4f58 100644 --- a/src/modules/graphics/opengl/wrap_SpriteBatch.cpp +++ b/src/modules/graphics/opengl/wrap_SpriteBatch.cpp @@ -23,6 +23,7 @@ #include "Image.h" #include "Canvas.h" #include "graphics/wrap_Texture.h" +#include "math/wrap_Transform.h" // C++ #include @@ -51,24 +52,37 @@ static inline int w_SpriteBatch_add_or_set(lua_State *L, SpriteBatch *t, int sta else if (lua_isnil(L, startidx) && !lua_isnoneornil(L, startidx + 1)) return luax_typerror(L, startidx, "Quad"); - float x = (float) luaL_optnumber(L, startidx + 0, 0.0); - float y = (float) luaL_optnumber(L, startidx + 1, 0.0); - float a = (float) luaL_optnumber(L, startidx + 2, 0.0); - float sx = (float) luaL_optnumber(L, startidx + 3, 1.0); - float sy = (float) luaL_optnumber(L, startidx + 4, sx); - float ox = (float) luaL_optnumber(L, startidx + 5, 0.0); - float oy = (float) luaL_optnumber(L, startidx + 6, 0.0); - float kx = (float) luaL_optnumber(L, startidx + 7, 0.0); - float ky = (float) luaL_optnumber(L, startidx + 8, 0.0); + if (luax_istype(L, startidx, MATH_TRANSFORM_ID)) + { + math::Transform *tf = luax_totype(L, startidx, MATH_TRANSFORM_ID); + luax_catchexcept(L, [&]() { + if (quad) + index = t->addq(quad, tf->getMatrix(), index); + else + index = t->add(tf->getMatrix(), index); + }); + } + else + { + float x = (float) luaL_optnumber(L, startidx + 0, 0.0); + float y = (float) luaL_optnumber(L, startidx + 1, 0.0); + float a = (float) luaL_optnumber(L, startidx + 2, 0.0); + float sx = (float) luaL_optnumber(L, startidx + 3, 1.0); + float sy = (float) luaL_optnumber(L, startidx + 4, sx); + float ox = (float) luaL_optnumber(L, startidx + 5, 0.0); + float oy = (float) luaL_optnumber(L, startidx + 6, 0.0); + float kx = (float) luaL_optnumber(L, startidx + 7, 0.0); + float ky = (float) luaL_optnumber(L, startidx + 8, 0.0); - Matrix4 m(x, y, a, sx, sy, ox, oy, kx, ky); + Matrix4 m(x, y, a, sx, sy, ox, oy, kx, ky); - luax_catchexcept(L, [&]() { - if (quad) - index = t->addq(quad, m, index); - else - index = t->add(m, index); - }); + luax_catchexcept(L, [&]() { + if (quad) + index = t->addq(quad, m, index); + else + index = t->add(m, index); + }); + } return index; } diff --git a/src/modules/graphics/opengl/wrap_Text.cpp b/src/modules/graphics/opengl/wrap_Text.cpp index 793b7ad11..dd8f7594b 100644 --- a/src/modules/graphics/opengl/wrap_Text.cpp +++ b/src/modules/graphics/opengl/wrap_Text.cpp @@ -19,6 +19,7 @@ **/ #include "wrap_Text.h" +#include "math/wrap_Transform.h" namespace love { @@ -126,24 +127,33 @@ int w_Text_add(lua_State *L) { Text *t = luax_checktext(L, 1); + int index = 0; + std::vector text; luax_checkcoloredstring(L, 2, text); - float x = (float) luaL_optnumber(L, 3, 0.0); - float y = (float) luaL_optnumber(L, 4, 0.0); - float a = (float) luaL_optnumber(L, 5, 0.0); - float sx = (float) luaL_optnumber(L, 6, 1.0); - float sy = (float) luaL_optnumber(L, 7, sx); - float ox = (float) luaL_optnumber(L, 8, 0.0); - float oy = (float) luaL_optnumber(L, 9, 0.0); - float kx = (float) luaL_optnumber(L, 10, 0.0); - float ky = (float) luaL_optnumber(L, 11, 0.0); + if (luax_istype(L, 3, MATH_TRANSFORM_ID)) + { + math::Transform *tf = luax_totype(L, 3, MATH_TRANSFORM_ID); + luax_catchexcept(L, [&](){ index = t->add(text, tf->getMatrix()); }); + } + else + { + float x = (float) luaL_optnumber(L, 3, 0.0); + float y = (float) luaL_optnumber(L, 4, 0.0); + float a = (float) luaL_optnumber(L, 5, 0.0); + float sx = (float) luaL_optnumber(L, 6, 1.0); + float sy = (float) luaL_optnumber(L, 7, sx); + float ox = (float) luaL_optnumber(L, 8, 0.0); + float oy = (float) luaL_optnumber(L, 9, 0.0); + float kx = (float) luaL_optnumber(L, 10, 0.0); + float ky = (float) luaL_optnumber(L, 11, 0.0); + + Matrix4 m(x, y, a, sx, sy, ox, oy, kx, ky); + luax_catchexcept(L, [&](){ index = t->add(text, m); }); + } - Matrix4 m(x, y, a, sx, sy, ox, oy, kx, ky); - int index = 0; - luax_catchexcept(L, [&](){ index = t->add(text, m); }); lua_pushnumber(L, index + 1); - return 1; } @@ -151,6 +161,8 @@ int w_Text_addf(lua_State *L) { Text *t = luax_checktext(L, 1); + int index = 0; + std::vector text; luax_checkcoloredstring(L, 2, text); @@ -162,21 +174,28 @@ int w_Text_addf(lua_State *L) if (!Font::getConstant(alignstr, align)) return luaL_error(L, "Invalid align mode: %s", alignstr); - float x = (float) luaL_optnumber(L, 5, 0.0); - float y = (float) luaL_optnumber(L, 6, 0.0); - float a = (float) luaL_optnumber(L, 7, 0.0); - float sx = (float) luaL_optnumber(L, 8, 1.0); - float sy = (float) luaL_optnumber(L, 9, sx); - float ox = (float) luaL_optnumber(L, 10, 0.0); - float oy = (float) luaL_optnumber(L, 11, 0.0); - float kx = (float) luaL_optnumber(L, 12, 0.0); - float ky = (float) luaL_optnumber(L, 13, 0.0); + if (luax_istype(L, 5, MATH_TRANSFORM_ID)) + { + math::Transform *tf = luax_totype(L, 5, MATH_TRANSFORM_ID); + luax_catchexcept(L, [&](){ index = t->addf(text, wrap, align, tf->getMatrix()); }); + } + else + { + float x = (float) luaL_optnumber(L, 5, 0.0); + float y = (float) luaL_optnumber(L, 6, 0.0); + float a = (float) luaL_optnumber(L, 7, 0.0); + float sx = (float) luaL_optnumber(L, 8, 1.0); + float sy = (float) luaL_optnumber(L, 9, sx); + float ox = (float) luaL_optnumber(L, 10, 0.0); + float oy = (float) luaL_optnumber(L, 11, 0.0); + float kx = (float) luaL_optnumber(L, 12, 0.0); + float ky = (float) luaL_optnumber(L, 13, 0.0); + + Matrix4 m(x, y, a, sx, sy, ox, oy, kx, ky); + luax_catchexcept(L, [&](){ index = t->addf(text, wrap, align, m); }); + } - Matrix4 m(x, y, a, sx, sy, ox, oy, kx, ky); - int index = 0; - luax_catchexcept(L, [&](){ index = t->addf(text, wrap, align, m); }); lua_pushnumber(L, index + 1); - return 1; } diff --git a/src/modules/math/MathModule.cpp b/src/modules/math/MathModule.cpp index 02250e569..34dc04b8d 100644 --- a/src/modules/math/MathModule.cpp +++ b/src/modules/math/MathModule.cpp @@ -25,6 +25,7 @@ #include "common/int.h" #include "common/StringMap.h" #include "BezierCurve.h" +#include "Transform.h" // STL #include @@ -374,6 +375,16 @@ BezierCurve *Math::newBezierCurve(const std::vector &points) return new BezierCurve(points); } +Transform *Math::newTransform() +{ + return new Transform(); +} + +Transform *Math::newTransform(float x, float y, float a, float sx, float sy, float ox, float oy, float kx, float ky) +{ + return new Transform(x, y, a, sx, sy, ox, oy, kx, ky); +} + std::string hash(HashFunction::Function function, Data *input) { return hash(function, (const char*) input->getData(), input->getSize()); diff --git a/src/modules/math/MathModule.h b/src/modules/math/MathModule.h index 8937308b4..027848010 100644 --- a/src/modules/math/MathModule.h +++ b/src/modules/math/MathModule.h @@ -45,6 +45,7 @@ namespace math { class BezierCurve; +class Transform; struct Triangle { @@ -176,6 +177,9 @@ public: **/ BezierCurve *newBezierCurve(const std::vector &points); + Transform *newTransform(); + Transform *newTransform(float x, float y, float a, float sx, float sy, float ox, float oy, float kx, float ky); + // Implements Module. virtual ModuleType getModuleType() const { diff --git a/src/modules/math/Transform.cpp b/src/modules/math/Transform.cpp new file mode 100644 index 000000000..e650692db --- /dev/null +++ b/src/modules/math/Transform.cpp @@ -0,0 +1,131 @@ +/** + * Copyright (c) 2006-2016 LOVE Development Team + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + **/ + +#include "Transform.h" + +namespace love +{ +namespace math +{ + +Transform::Transform() + : matrix() + , inverseDirty(true) + , inverseMatrix() +{ +} + +Transform::Transform(const Matrix4 &m) + : matrix(m) + , inverseDirty(true) + , inverseMatrix() +{ +} + +Transform::Transform(float x, float y, float a, float sx, float sy, float ox, float oy, float kx, float ky) + : matrix(x, y, a, sx, sy, ox, oy, kx, ky) + , inverseDirty(true) + , inverseMatrix() +{ +} + +Transform::~Transform() +{ +} + +Transform *Transform::clone() +{ + return new Transform(*this); +} + +Transform *Transform::inverse() +{ + return new Transform(getInverseMatrix()); +} + +void Transform::apply(Transform *other) +{ + matrix *= other->getMatrix(); + inverseDirty = true; +} + +void Transform::translate(float x, float y) +{ + matrix.translate(x, y); + inverseDirty = true; +} + +void Transform::rotate(float angle) +{ + matrix.rotate(angle); + inverseDirty = true; +} + +void Transform::scale(float x, float y) +{ + matrix.scale(x, y); + inverseDirty = true; +} + +void Transform::shear(float x, float y) +{ + matrix.shear(x, y); + inverseDirty = true; +} + +void Transform::reset() +{ + matrix.setIdentity(); + inverseDirty = true; +} + +void Transform::setTransformation(float x, float y, float a, float sx, float sy, float ox, float oy, float kx, float ky) +{ + matrix.setTransformation(x, y, a, sx, sy, ox, oy, kx, ky); + inverseDirty = true; +} + +love::Vector Transform::transformPoint(love::Vector p) const +{ + love::Vector result; + matrix.transform(&result, &p, 1); + return result; +} + +love::Vector Transform::inverseTransformPoint(love::Vector p) +{ + love::Vector result; + getInverseMatrix().transform(&result, &p, 1); + return result; +} + +const Matrix4 &Transform::getMatrix() const +{ + return matrix; +} + +void Transform::setMatrix(const Matrix4 &m) +{ + matrix = m; + inverseDirty = true; +} + +} // math +} // love diff --git a/src/modules/math/Transform.h b/src/modules/math/Transform.h new file mode 100644 index 000000000..b70e62d60 --- /dev/null +++ b/src/modules/math/Transform.h @@ -0,0 +1,83 @@ +/** + * Copyright (c) 2006-2016 LOVE Development Team + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + **/ + +#pragma once + +// LOVE +#include "common/Object.h" +#include "common/Matrix.h" +#include "common/Vector.h" + +namespace love +{ +namespace math +{ + +class Transform : public Object +{ +public: + + Transform(); + Transform(const Matrix4 &m); + Transform(float x, float y, float a, float sx, float sy, float ox, float oy, float kx, float ky); + + virtual ~Transform(); + + Transform *clone(); + Transform *inverse(); + + void apply(Transform *other); + + void translate(float x, float y); + void rotate(float angle); + void scale(float x, float y); + void shear(float x, float y); + + void reset(); + void setTransformation(float x, float y, float a, float sx, float sy, float ox, float oy, float kx, float ky); + + love::Vector transformPoint(love::Vector p) const; + love::Vector inverseTransformPoint(love::Vector p); + + const Matrix4 &getMatrix() const; + void setMatrix(const Matrix4 &m); + +private: + + inline const Matrix4 &getInverseMatrix() + { + if (inverseDirty) + { + inverseDirty = false; + inverseMatrix = matrix.inverse(); + } + + return inverseMatrix; + } + + Matrix4 matrix; + bool inverseDirty; + Matrix4 inverseMatrix; + +}; // Transform + + +} // math +} // love diff --git a/src/modules/math/wrap_Math.cpp b/src/modules/math/wrap_Math.cpp index c53531c96..341a30342 100644 --- a/src/modules/math/wrap_Math.cpp +++ b/src/modules/math/wrap_Math.cpp @@ -22,8 +22,10 @@ #include "wrap_RandomGenerator.h" #include "wrap_BezierCurve.h" #include "wrap_CompressedData.h" +#include "wrap_Transform.h" #include "MathModule.h" #include "BezierCurve.h" +#include "Transform.h" #include "common/b64.h" #include @@ -118,6 +120,31 @@ int w_newBezierCurve(lua_State *L) return 1; } +int w_newTransform(lua_State *L) +{ + Transform *t = nullptr; + + if (lua_isnoneornil(L, 1)) + t = Math::instance.newTransform(); + else + { + float x = (float) luaL_checknumber(L, 1); + float y = (float) luaL_checknumber(L, 2); + float a = (float) luaL_optnumber(L, 3, 0.0); + float sx = (float) luaL_optnumber(L, 4, 1.0); + float sy = (float) luaL_optnumber(L, 5, sx); + float ox = (float) luaL_optnumber(L, 6, 0.0); + float oy = (float) luaL_optnumber(L, 7, 0.0); + float kx = (float) luaL_optnumber(L, 8, 0.0); + float ky = (float) luaL_optnumber(L, 9, 0.0); + t = Math::instance.newTransform(x, y, a, sx, sy, ox, oy, kx, ky); + } + + luax_pushtype(L, MATH_TRANSFORM_ID, t); + t->release(); + return 1; +} + int w_triangulate(lua_State *L) { std::vector vertices; @@ -507,6 +534,7 @@ static const luaL_Reg functions[] = { "_getRandomGenerator", w__getRandomGenerator }, { "newRandomGenerator", w_newRandomGenerator }, { "newBezierCurve", w_newBezierCurve }, + { "newTransform", w_newTransform }, { "triangulate", w_triangulate }, { "isConvex", w_isConvex }, { "gammaToLinear", w_gammaToLinear }, @@ -525,6 +553,7 @@ static const lua_CFunction types[] = luaopen_randomgenerator, luaopen_beziercurve, luaopen_compresseddata, + luaopen_transform, 0 }; diff --git a/src/modules/math/wrap_Transform.cpp b/src/modules/math/wrap_Transform.cpp new file mode 100644 index 000000000..e9e96781b --- /dev/null +++ b/src/modules/math/wrap_Transform.cpp @@ -0,0 +1,305 @@ +/** + * Copyright (c) 2006-2016 LOVE Development Team + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + **/ + +#include "wrap_Transform.h" + +namespace love +{ +namespace math +{ + +Transform *luax_checktransform(lua_State *L, int idx) +{ + return luax_checktype(L, idx, MATH_TRANSFORM_ID); +} + +int w_Transform_clone(lua_State *L) +{ + Transform *t = luax_checktransform(L, 1); + Transform *newtransform = t->clone(); + luax_pushtype(L, MATH_TRANSFORM_ID, newtransform); + newtransform->release(); + return 1; +} + +int w_Transform_inverse(lua_State *L) +{ + Transform *t = luax_checktransform(L, 1); + Transform *inverse = t->inverse(); + luax_pushtype(L, MATH_TRANSFORM_ID, inverse); + inverse->release(); + return 1; +} + +int w_Transform_apply(lua_State *L) +{ + Transform *t = luax_checktransform(L, 1); + Transform *other = luax_checktransform(L, 2); + t->apply(other); + lua_pushvalue(L, 1); + return 1; +} + +int w_Transform_translate(lua_State *L) +{ + Transform *t = luax_checktransform(L, 1); + float x = (float) luaL_checknumber(L, 2); + float y = (float) luaL_checknumber(L, 3); + t->translate(x, y); + lua_pushvalue(L, 1); + return 1; +} + +int w_Transform_rotate(lua_State *L) +{ + Transform *t = luax_checktransform(L, 1); + float angle = (float) luaL_checknumber(L, 2); + t->rotate(angle); + lua_pushvalue(L, 1); + return 1; +} + +int w_Transform_scale(lua_State *L) +{ + Transform *t = luax_checktransform(L, 1); + float sx = (float) luaL_checknumber(L, 2); + float sy = (float) luaL_optnumber(L, 3, sx); + t->scale(sx, sy); + lua_pushvalue(L, 1); + return 1; +} + +int w_Transform_shear(lua_State *L) +{ + Transform *t = luax_checktransform(L, 1); + float kx = (float) luaL_checknumber(L, 2); + float ky = (float) luaL_checknumber(L, 3); + t->shear(kx, ky); + lua_pushvalue(L, 1); + return 1; +} + +int w_Transform_reset(lua_State *L) +{ + Transform *t = luax_checktransform(L, 1); + t->reset(); + lua_pushvalue(L, 1); + return 1; +} + +int w_Transform_setTransformation(lua_State *L) +{ + Transform *t = luax_checktransform(L, 1); + float x = (float) luaL_optnumber(L, 2, 0.0); + float y = (float) luaL_optnumber(L, 3, 0.0); + float a = (float) luaL_optnumber(L, 4, 0.0); + float sx = (float) luaL_optnumber(L, 5, 1.0); + float sy = (float) luaL_optnumber(L, 6, sx); + float ox = (float) luaL_optnumber(L, 7, 0.0); + float oy = (float) luaL_optnumber(L, 8, 0.0); + float kx = (float) luaL_optnumber(L, 9, 0.0); + float ky = (float) luaL_optnumber(L, 10, 0.0); + t->setTransformation(x, y, a, sx, sy, ox, oy, kx, ky); + lua_pushvalue(L, 1); + return 1; +} + +int w_Transform_setMatrix(lua_State *L) +{ + Transform *t = luax_checktransform(L, 1); + + bool columnmajor = false; + + int idx = 2; + if (lua_isboolean(L, idx)) + { + columnmajor = lua_toboolean(L, idx); + idx++; + } + + float elements[16]; + + if (lua_istable(L, idx)) + { + lua_rawgeti(L, idx, 1); + bool tableoftables = lua_istable(L, -1); + lua_pop(L, 1); + + if (tableoftables) + { + if (columnmajor) + { + for (int column = 0; column < 4; column++) + { + lua_rawgeti(L, idx, column + 1); + + for (int row = 0; row < 4; row++) + { + lua_rawgeti(L, -(row + 1), row + 1); + elements[column * 4 + row] = (float) luaL_checknumber(L, -1); + } + + lua_pop(L, 4 + 1); + } + } + else + { + for (int row = 0; row < 4; row++) + { + lua_rawgeti(L, idx, row + 1); + + for (int column = 0; column < 4; column++) + { + // The table has the matrix elements laid out in row-major + // order, but we need to store them column-major in memory. + lua_rawgeti(L, -(column + 1), column + 1); + elements[column * 4 + row] = (float) luaL_checknumber(L, -1); + } + + lua_pop(L, 4 + 1); + } + } + } + else + { + if (columnmajor) + { + for (int column = 0; column < 4; column++) + { + for (int row = 0; row < 4; row++) + { + lua_rawgeti(L, idx, column * 4 + row + 1); + elements[column * 4 + row] = (float) luaL_checknumber(L, -1); + } + } + } + else + { + for (int column = 0; column < 4; column++) + { + for (int row = 0; row < 4; row++) + { + // The table has the matrix elements laid out in row-major + // order, but we need to store them column-major in memory. + lua_rawgeti(L, idx, row * 4 + column + 1); + elements[column * 4 + row] = (float) luaL_checknumber(L, -1); + } + } + } + + lua_pop(L, 16); + } + } + else + { + if (columnmajor) + { + for (int i = 0; i < 16; i++) + elements[i] = (float) luaL_checknumber(L, idx + i); + } + else + { + for (int column = 0; column < 4; column++) + { + for (int row = 0; row < 4; row++) + elements[column * 4 + row] = (float) luaL_checknumber(L, row * 4 + column + idx); + } + } + } + + t->setMatrix(Matrix4(elements)); + lua_pushvalue(L, 1); + return 1; +} + +int w_Transform_getMatrix(lua_State *L) +{ + Transform *t = luax_checktransform(L, 1); + const float *elements = t->getMatrix().getElements(); + + // We want to push elements in row-major order, but they're stored column- + // major. + for (int row = 0; row < 4; row++) + { + for (int column = 0; column < 4; column++) + lua_pushnumber(L, elements[column * 4 + row]); + } + + return 16; +} + +int w_Transform_transformPoint(lua_State *L) +{ + Transform *t = luax_checktransform(L, 1); + love::Vector p; + p.x = (float) luaL_checknumber(L, 2); + p.y = (float) luaL_checknumber(L, 3); + p = t->transformPoint(p); + lua_pushnumber(L, p.x); + lua_pushnumber(L, p.y); + return 2; +} + +int w_Transform_inverseTransformPoint(lua_State *L) +{ + Transform *t = luax_checktransform(L, 1); + love::Vector p; + p.x = (float) luaL_checknumber(L, 2); + p.y = (float) luaL_checknumber(L, 3); + p = t->inverseTransformPoint(p); + lua_pushnumber(L, p.x); + lua_pushnumber(L, p.y); + return 2; +} + +int w_Transform__mul(lua_State *L) +{ + Transform *t1 = luax_checktransform(L, 1); + Transform *t2 = luax_checktransform(L, 2); + Transform *t3 = new Transform(t1->getMatrix() * t2->getMatrix()); + luax_pushtype(L, MATH_TRANSFORM_ID, t3); + t3->release(); + return 1; +} + +static const luaL_Reg functions[] = +{ + { "clone", w_Transform_clone }, + { "inverse", w_Transform_inverse }, + { "apply", w_Transform_apply }, + { "translate", w_Transform_translate }, + { "rotate", w_Transform_rotate }, + { "scale", w_Transform_scale }, + { "shear", w_Transform_shear }, + { "reset", w_Transform_reset }, + { "setTransformation", w_Transform_setTransformation }, + { "transformPoint", w_Transform_transformPoint }, + { "inverseTransformPoint", w_Transform_inverseTransformPoint }, + { "__mul", w_Transform__mul }, + { 0, 0 } +}; + +extern "C" int luaopen_transform(lua_State *L) +{ + return luax_register_type(L, MATH_TRANSFORM_ID, "Transform", functions, nullptr); +} + +} // math +} // love diff --git a/src/modules/math/wrap_Transform.h b/src/modules/math/wrap_Transform.h new file mode 100644 index 000000000..1ad732c34 --- /dev/null +++ b/src/modules/math/wrap_Transform.h @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2006-2016 LOVE Development Team + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + **/ + +#pragma once + +// LOVE +#include "Transform.h" +#include "common/runtime.h" + +namespace love +{ +namespace math +{ + +extern "C" int luaopen_transform(lua_State *L); + +} // math +} // love From 5e5b247fc73e4a9150c9b213abd097595ab62735 Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Thu, 24 Nov 2016 21:37:13 -0400 Subject: [PATCH 27/37] Work around buggy C++11 standard libraries (mingw) --HG-- branch : minor --- src/modules/graphics/opengl/Shader.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/modules/graphics/opengl/Shader.cpp b/src/modules/graphics/opengl/Shader.cpp index 6ad34bcea..df6e848a5 100644 --- a/src/modules/graphics/opengl/Shader.cpp +++ b/src/modules/graphics/opengl/Shader.cpp @@ -27,6 +27,7 @@ // C++ #include #include +#include namespace love { @@ -312,7 +313,10 @@ void Shader::mapActiveUniforms() if (u.count > 1) { - std::string indexname = u.name + "[" + std::to_string(i) + "]"; + std::ostringstream ss; + ss << i; + + std::string indexname = u.name + "[" + ss.str() + "]"; location = glGetUniformLocation(program, indexname.c_str()); } From 5d2b0a98e51eb70e96f04bcb196fd319ca5cb16f Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Thu, 24 Nov 2016 21:54:19 -0400 Subject: [PATCH 28/37] =?UTF-8?q?Add=20missing=20applyTransform=20and=20re?= =?UTF-8?q?placeTransform=20functions=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --HG-- branch : minor --- src/modules/graphics/opengl/wrap_Graphics.cpp | 17 +++++++++++++++++ src/modules/math/wrap_Transform.h | 1 + 2 files changed, 18 insertions(+) diff --git a/src/modules/graphics/opengl/wrap_Graphics.cpp b/src/modules/graphics/opengl/wrap_Graphics.cpp index 9833b2b05..8bcdd7c82 100644 --- a/src/modules/graphics/opengl/wrap_Graphics.cpp +++ b/src/modules/graphics/opengl/wrap_Graphics.cpp @@ -28,6 +28,7 @@ #include "video/VideoStream.h" #include "image/wrap_Image.h" #include "common/Reference.h" +#include "math/wrap_Transform.h" #include #include @@ -2124,6 +2125,20 @@ int w_origin(lua_State * /*L*/) return 0; } +int w_applyTransform(lua_State *L) +{ + math::Transform *t = math::luax_checktransform(L, 1); + instance()->applyTransform(t); + return 0; +} + +int w_replaceTransform(lua_State *L) +{ + math::Transform *t = math::luax_checktransform(L, 1); + instance()->replaceTransform(t); + return 0; +} + int w_transformPoint(lua_State *L) { Vector p; @@ -2252,6 +2267,8 @@ static const luaL_Reg functions[] = { "translate", w_translate }, { "shear", w_shear }, { "origin", w_origin }, + { "applyTransform", w_applyTransform }, + { "replaceTransform", w_replaceTransform }, { "transformPoint", w_transformPoint }, { "inverseTransformPoint", w_inverseTransformPoint }, diff --git a/src/modules/math/wrap_Transform.h b/src/modules/math/wrap_Transform.h index 1ad732c34..f3791dbc5 100644 --- a/src/modules/math/wrap_Transform.h +++ b/src/modules/math/wrap_Transform.h @@ -29,6 +29,7 @@ namespace love namespace math { +Transform *luax_checktransform(lua_State *L, int idx); extern "C" int luaopen_transform(lua_State *L); } // math From f85ee6b504c70fc0ad440daf08a5801c51456ec3 Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Fri, 25 Nov 2016 19:35:07 -0400 Subject: [PATCH 29/37] Add some more graphics feature and limit queries (resolves issue #1234). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit love.graphics.getSupported(): - Added “fullnpot” field, is true everywhere except on some older mobile devices. If false, mipmapping and wrap modes other than “clamp” can’t be used on non-power-of-two sized textures. - Added “pixelshaderhighp” field, is true everywhere except some older mobile devices. If false, “highp” precision can’t be used in pixel shaders and love will default to “mediump” instead. love.graphics.getSystemLimits(): - Added “anisotropy” field, gets the maximum amount of anisotropy you can specify in Texture:setFilter. love will clamp anisotropy arguments greater than this value. --HG-- branch : minor --- src/modules/graphics/Graphics.cpp | 3 +++ src/modules/graphics/Graphics.h | 3 +++ src/modules/graphics/opengl/Graphics.cpp | 6 ++++++ src/modules/graphics/opengl/OpenGL.cpp | 21 +++++++++++++++++++++ src/modules/graphics/opengl/OpenGL.h | 8 ++++++++ 5 files changed, 41 insertions(+) diff --git a/src/modules/graphics/Graphics.cpp b/src/modules/graphics/Graphics.cpp index 7280ef200..7406c915e 100644 --- a/src/modules/graphics/Graphics.cpp +++ b/src/modules/graphics/Graphics.cpp @@ -258,6 +258,8 @@ StringMap::Entry Graphics::featur { "multicanvasformats", FEATURE_MULTI_CANVAS_FORMATS }, { "clampzero", FEATURE_CLAMP_ZERO }, { "lighten", FEATURE_LIGHTEN }, + { "fullnpot", FEATURE_FULL_NPOT }, + { "pixelshaderhighp", FEATURE_PIXEL_SHADER_HIGHP }, }; StringMap Graphics::features(Graphics::featureEntries, sizeof(Graphics::featureEntries)); @@ -268,6 +270,7 @@ StringMap::Entry Graphics::syst { "texturesize", LIMIT_TEXTURE_SIZE }, { "multicanvas", LIMIT_MULTI_CANVAS }, { "canvasmsaa", LIMIT_CANVAS_MSAA }, + { "anisotropy", LIMIT_ANISOTROPY }, }; StringMap Graphics::systemLimits(Graphics::systemLimitEntries, sizeof(Graphics::systemLimitEntries)); diff --git a/src/modules/graphics/Graphics.h b/src/modules/graphics/Graphics.h index cc713e2e4..cbacb38cc 100644 --- a/src/modules/graphics/Graphics.h +++ b/src/modules/graphics/Graphics.h @@ -151,6 +151,8 @@ public: FEATURE_MULTI_CANVAS_FORMATS, FEATURE_CLAMP_ZERO, FEATURE_LIGHTEN, + FEATURE_FULL_NPOT, + FEATURE_PIXEL_SHADER_HIGHP, FEATURE_MAX_ENUM }; @@ -167,6 +169,7 @@ public: LIMIT_TEXTURE_SIZE, LIMIT_MULTI_CANVAS, LIMIT_CANVAS_MSAA, + LIMIT_ANISOTROPY, LIMIT_MAX_ENUM }; diff --git a/src/modules/graphics/opengl/Graphics.cpp b/src/modules/graphics/opengl/Graphics.cpp index 8e4feace5..2c43332f5 100644 --- a/src/modules/graphics/opengl/Graphics.cpp +++ b/src/modules/graphics/opengl/Graphics.cpp @@ -2009,6 +2009,8 @@ double Graphics::getSystemLimit(SystemLimit limittype) const return (double) gl.getMaxRenderTargets(); case Graphics::LIMIT_CANVAS_MSAA: return (double) gl.getMaxRenderbufferSamples(); + case Graphics::LIMIT_ANISOTROPY: + return (double) gl.getMaxAnisotropy(); default: return 0.0; } @@ -2024,6 +2026,10 @@ bool Graphics::isSupported(Feature feature) const return gl.isClampZeroTextureWrapSupported(); case FEATURE_LIGHTEN: return GLAD_VERSION_1_4 || GLAD_ES_VERSION_3_0 || GLAD_EXT_blend_minmax; + case FEATURE_FULL_NPOT: + return GLAD_VERSION_2_0 || GLAD_ES_VERSION_3_0 || GLAD_OES_texture_npot; + case FEATURE_PIXEL_SHADER_HIGHP: + return gl.isPixelShaderHighpSupported(); default: return false; } diff --git a/src/modules/graphics/opengl/OpenGL.cpp b/src/modules/graphics/opengl/OpenGL.cpp index 3749829af..f5a44775c 100644 --- a/src/modules/graphics/opengl/OpenGL.cpp +++ b/src/modules/graphics/opengl/OpenGL.cpp @@ -65,6 +65,7 @@ static void *LOVEGetProcAddress(const char *name) OpenGL::OpenGL() : stats() , contextInitialized(false) + , pixelShaderHighpSupported(false) , maxAnisotropy(1.0f) , maxTextureSize(0) , maxRenderTargets(1) @@ -268,6 +269,16 @@ void OpenGL::initOpenGLFunctions() void OpenGL::initMaxValues() { + if (GLAD_ES_VERSION_2_0 && !GLAD_ES_VERSION_3_0) + { + GLint range = 0; + GLint precision = 0; + glGetShaderPrecisionFormat(GL_FRAGMENT_SHADER, GL_HIGH_FLOAT, &range, &precision); + pixelShaderHighpSupported = range > 0; + } + else + pixelShaderHighpSupported = true; + // We'll need this value to clamp anisotropy. if (GLAD_EXT_texture_filter_anisotropic) glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnisotropy); @@ -715,6 +726,11 @@ bool OpenGL::isClampZeroTextureWrapSupported() const return GLAD_VERSION_1_3 || GLAD_EXT_texture_border_clamp || GLAD_NV_texture_border_clamp; } +bool OpenGL::isPixelShaderHighpSupported() const +{ + return pixelShaderHighpSupported; +} + int OpenGL::getMaxTextureSize() const { return maxTextureSize; @@ -740,6 +756,11 @@ float OpenGL::getMaxPointSize() const return maxPointSize; } +float OpenGL::getMaxAnisotropy() const +{ + return maxAnisotropy; +} + void OpenGL::updateTextureMemorySize(size_t oldsize, size_t newsize) { int64 memsize = (int64) stats.textureMemory + ((int64) newsize - (int64) oldsize); diff --git a/src/modules/graphics/opengl/OpenGL.h b/src/modules/graphics/opengl/OpenGL.h index f0905de10..099ed07f9 100644 --- a/src/modules/graphics/opengl/OpenGL.h +++ b/src/modules/graphics/opengl/OpenGL.h @@ -383,6 +383,7 @@ public: void setTextureWrap(const graphics::Texture::Wrap &w); bool isClampZeroTextureWrapSupported() const; + bool isPixelShaderHighpSupported() const; /** * Returns the maximum supported width or height of a texture. @@ -409,6 +410,12 @@ public: **/ float getMaxPointSize() const; + /** + * Returns the maximum anisotropic filtering value that can be used for + * Texture filtering. + **/ + float getMaxAnisotropy() const; + void updateTextureMemorySize(size_t oldsize, size_t newsize); @@ -438,6 +445,7 @@ private: bool contextInitialized; + bool pixelShaderHighpSupported; float maxAnisotropy; int maxTextureSize; int maxRenderTargets; From 92774b759b8100de3f0a9ddf4958db6ce2eed126 Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Fri, 25 Nov 2016 19:59:48 -0400 Subject: [PATCH 30/37] Fix line and shape segment counts and smooth line antialiasing when love.graphics.applyTransform and replaceTransform are used. --HG-- branch : minor --- src/common/Matrix.cpp | 6 ++++ src/common/Matrix.h | 6 ++++ src/modules/graphics/opengl/Graphics.cpp | 38 +++++++++++++++--------- src/modules/graphics/opengl/Graphics.h | 2 +- 4 files changed, 37 insertions(+), 15 deletions(-) diff --git a/src/common/Matrix.cpp b/src/common/Matrix.cpp index f6f6b4dcd..3cb9b3013 100644 --- a/src/common/Matrix.cpp +++ b/src/common/Matrix.cpp @@ -149,6 +149,12 @@ void Matrix4::setShear(float kx, float ky) e[1] = ky; e[4] = kx; } + +void Matrix4::getApproximateScale(float &sx, float &sy) const +{ + sx = sqrtf(e[0] * e[0] + e[4] * e[4]); + sy = sqrtf(e[1] * e[1] + e[5] * e[5]); +} void Matrix4::setRawTransformation(float t00, float t10, float t01, float t11, float x, float y) { diff --git a/src/common/Matrix.h b/src/common/Matrix.h index d5fd49a2b..2e62b12b8 100644 --- a/src/common/Matrix.h +++ b/src/common/Matrix.h @@ -118,6 +118,12 @@ public: * @param ky Shear along y-axis. **/ void setShear(float kx, float ky); + + /** + * Calculates the scale factors for a 2D affine transform. The output values + * are absolute (not signed). + **/ + void getApproximateScale(float &sx, float &sy) const; /** * Sets a transformation's values directly. Useful if you want to modify them inplace, diff --git a/src/modules/graphics/opengl/Graphics.cpp b/src/modules/graphics/opengl/Graphics.cpp index 2c43332f5..e47aff39f 100644 --- a/src/modules/graphics/opengl/Graphics.cpp +++ b/src/modules/graphics/opengl/Graphics.cpp @@ -291,9 +291,9 @@ bool Graphics::setMode(int width, int height) // Restore the graphics state. restoreState(states.back()); - pixelSizeStack.clear(); - pixelSizeStack.reserve(5); - pixelSizeStack.push_back(1); + pixelScaleStack.clear(); + pixelScaleStack.reserve(5); + pixelScaleStack.push_back(1); int gammacorrect = isGammaCorrect() ? 1 : 0; @@ -1688,23 +1688,24 @@ void Graphics::polyline(const float *coords, size_t count) throw RenderOutsidePassException(); const DisplayState &state = states.back(); + float pixelsize = std::max((float) pixelScaleStack.back(), 0.000001f); if (state.lineJoin == LINE_JOIN_NONE) { NoneJoinPolyline line; - line.render(coords, count, state.lineWidth * .5f, float(pixelSizeStack.back()), state.lineStyle == LINE_SMOOTH); + line.render(coords, count, state.lineWidth * .5f, pixelsize, state.lineStyle == LINE_SMOOTH); line.draw(); } else if (state.lineJoin == LINE_JOIN_BEVEL) { BevelJoinPolyline line; - line.render(coords, count, state.lineWidth * .5f, float(pixelSizeStack.back()), state.lineStyle == LINE_SMOOTH); + line.render(coords, count, state.lineWidth * .5f, pixelsize, state.lineStyle == LINE_SMOOTH); line.draw(); } else // LINE_JOIN_MITER { MiterJoinPolyline line; - line.render(coords, count, state.lineWidth * .5f, float(pixelSizeStack.back()), state.lineStyle == LINE_SMOOTH); + line.render(coords, count, state.lineWidth * .5f, pixelsize, state.lineStyle == LINE_SMOOTH); line.draw(); } } @@ -1782,8 +1783,7 @@ void Graphics::rectangle(DrawMode mode, float x, float y, float w, float h, floa int Graphics::calculateEllipsePoints(float rx, float ry) const { - float pixelScale = 1.0f / std::max((float) pixelSizeStack.back(), 0.00001f); - int points = (int) sqrtf(((rx + ry) / 2.0f) * 20.0f * pixelScale); + int points = (int) sqrtf(((rx + ry) / 2.0f) * 20.0f * (float) pixelScaleStack.back()); return std::max(points, 8); } @@ -2042,7 +2042,7 @@ void Graphics::push(StackType type) gl.pushTransform(); - pixelSizeStack.push_back(pixelSizeStack.back()); + pixelScaleStack.push_back(pixelScaleStack.back()); if (type == STACK_ALL) states.push_back(states.back()); @@ -2056,7 +2056,7 @@ void Graphics::pop() throw Exception("Minimum stack depth reached (more pops than pushes?)"); gl.popTransform(); - pixelSizeStack.pop_back(); + pixelScaleStack.pop_back(); if (stackTypes.back() == STACK_ALL) { @@ -2079,7 +2079,7 @@ void Graphics::rotate(float r) void Graphics::scale(float x, float y) { gl.getTransform().scale(x, y); - pixelSizeStack.back() *= 2. / (fabs(x) + fabs(y)); + pixelScaleStack.back() *= (fabs(x) + fabs(y)) / 2.0; } void Graphics::translate(float x, float y) @@ -2095,17 +2095,27 @@ void Graphics::shear(float kx, float ky) void Graphics::origin() { gl.getTransform().setIdentity(); - pixelSizeStack.back() = 1; + pixelScaleStack.back() = 1; } void Graphics::applyTransform(love::math::Transform *transform) { - gl.getTransform() *= transform->getMatrix(); + Matrix4 &m = gl.getTransform(); + m *= transform->getMatrix(); + + float sx, sy; + m.getApproximateScale(sx, sy); + pixelScaleStack.back() = (sx + sy) / 2.0; } void Graphics::replaceTransform(love::math::Transform *transform) { - gl.getTransform() = transform->getMatrix(); + const Matrix4 &m = transform->getMatrix(); + gl.getTransform() = m; + + float sx, sy; + m.getApproximateScale(sx, sy); + pixelScaleStack.back() = (sx + sy) / 2.0; } Vector Graphics::transformPoint(Vector point) diff --git a/src/modules/graphics/opengl/Graphics.h b/src/modules/graphics/opengl/Graphics.h index 9fbb5fdac..265b4db7d 100644 --- a/src/modules/graphics/opengl/Graphics.h +++ b/src/modules/graphics/opengl/Graphics.h @@ -567,7 +567,7 @@ private: StrongRef defaultFont; - std::vector pixelSizeStack; // stores current size of a pixel (needed for line drawing) + std::vector pixelScaleStack; std::vector pendingScreenshotCallbacks; From 050ca413be3f5ea8b5a2ab8d370f3f0444e9e089 Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Fri, 25 Nov 2016 20:07:30 -0400 Subject: [PATCH 31/37] Fix a missing reciprocal.. --HG-- branch : minor --- src/modules/graphics/opengl/Graphics.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/graphics/opengl/Graphics.cpp b/src/modules/graphics/opengl/Graphics.cpp index e47aff39f..1408e3373 100644 --- a/src/modules/graphics/opengl/Graphics.cpp +++ b/src/modules/graphics/opengl/Graphics.cpp @@ -1688,7 +1688,7 @@ void Graphics::polyline(const float *coords, size_t count) throw RenderOutsidePassException(); const DisplayState &state = states.back(); - float pixelsize = std::max((float) pixelScaleStack.back(), 0.000001f); + float pixelsize = 1.0f / std::max((float) pixelScaleStack.back(), 0.000001f); if (state.lineJoin == LINE_JOIN_NONE) { From 2c28eef74a5f71aa34ee994af07d54bc274f459e Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Fri, 25 Nov 2016 22:51:31 -0400 Subject: [PATCH 32/37] Added 2 new variants of Mesh:attachAttribute (resolves issue #1233). Thanks xenthral! - Added Mesh:attachAttribute(name, mesh, nameInMesh) which retargets the attribute name in the specified Mesh (3rd argument) to a new name for this mesh (1st argument). - Added Mesh:attachAttribute(name) which detaches any attached attributes from other meshes for that attribute name. --HG-- branch : minor --- src/modules/graphics/opengl/Mesh.cpp | 31 +++++++++++++++-------- src/modules/graphics/opengl/Mesh.h | 2 +- src/modules/graphics/opengl/wrap_Mesh.cpp | 10 ++++++-- 3 files changed, 30 insertions(+), 13 deletions(-) diff --git a/src/modules/graphics/opengl/Mesh.cpp b/src/modules/graphics/opengl/Mesh.cpp index 8d7e7e87b..7d7c011aa 100644 --- a/src/modules/graphics/opengl/Mesh.cpp +++ b/src/modules/graphics/opengl/Mesh.cpp @@ -309,9 +309,26 @@ bool Mesh::isAttributeEnabled(const std::string &name) const return it->second.enabled; } -void Mesh::attachAttribute(const std::string &name, Mesh *mesh) +void Mesh::attachAttribute(const std::string &name, Mesh *mesh, const std::string &attachname) { - if (mesh != this) + AttachedAttribute oldattrib = {}; + + auto it = attachedAttributes.find(name); + if (it != attachedAttributes.end()) + oldattrib = it->second; + + if (mesh == nullptr) + { + if (it != attachedAttributes.end() && oldattrib.mesh != this) + { + oldattrib.mesh->release(); + attachedAttributes.erase(it); + if (getAttributeIndex(name) != -1) + attachAttribute(name, this, name); + return; + } + } + else if (mesh != this) { for (const auto &it : mesh->attachedAttributes) { @@ -322,19 +339,13 @@ void Mesh::attachAttribute(const std::string &name, Mesh *mesh) } } - AttachedAttribute oldattrib = {}; AttachedAttribute newattrib = {}; - - auto it = attachedAttributes.find(name); - if (it != attachedAttributes.end()) - oldattrib = it->second; - newattrib.mesh = mesh; newattrib.enabled = oldattrib.mesh ? oldattrib.enabled : true; - newattrib.index = mesh->getAttributeIndex(name); + newattrib.index = mesh->getAttributeIndex(attachname); if (newattrib.index < 0) - throw love::Exception("The specified mesh does not have a vertex attribute named '%s'", name.c_str()); + throw love::Exception("The specified mesh does not have a vertex attribute named '%s'", attachname.c_str()); if (newattrib.mesh != this) newattrib.mesh->retain(); diff --git a/src/modules/graphics/opengl/Mesh.h b/src/modules/graphics/opengl/Mesh.h index 19ba61097..c34032d3b 100644 --- a/src/modules/graphics/opengl/Mesh.h +++ b/src/modules/graphics/opengl/Mesh.h @@ -138,7 +138,7 @@ public: * Attaches a vertex attribute from another Mesh to this one. The attribute * will be used when drawing this Mesh. **/ - void attachAttribute(const std::string &name, Mesh *mesh); + void attachAttribute(const std::string &name, Mesh *mesh, const std::string &attachname); void *mapVertexData(); void unmapVertexData(size_t modifiedoffset = 0, size_t modifiedsize = -1); diff --git a/src/modules/graphics/opengl/wrap_Mesh.cpp b/src/modules/graphics/opengl/wrap_Mesh.cpp index 3f730554a..f0bde6fa0 100644 --- a/src/modules/graphics/opengl/wrap_Mesh.cpp +++ b/src/modules/graphics/opengl/wrap_Mesh.cpp @@ -337,8 +337,14 @@ int w_Mesh_attachAttribute(lua_State *L) { Mesh *t = luax_checkmesh(L, 1); const char *name = luaL_checkstring(L, 2); - Mesh *mesh = luax_checkmesh(L, 3); - luax_catchexcept(L, [&](){ t->attachAttribute(name, mesh); }); + const char *attachname = name; + Mesh *mesh = nullptr; + if (lua_isnoneornil(L, 3)) + { + mesh = luax_checkmesh(L, 3); + attachname = luaL_optstring(L, 4, attachname); + } + luax_catchexcept(L, [&](){ t->attachAttribute(name, mesh, attachname); }); return 0; } From 9930b81f940b46b53c6291db57574ecda86f082b Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Fri, 25 Nov 2016 23:10:29 -0400 Subject: [PATCH 33/37] The Xcode project now recognizes frameworks placed in love/platform/Xcode/macosx/Frameworks/ when building for macOS (closes issue #1230). --HG-- branch : minor --- platform/xcode/liblove.xcodeproj/project.pbxproj | 12 ++++++++++++ platform/xcode/love.xcodeproj/project.pbxproj | 9 +++++++++ 2 files changed, 21 insertions(+) diff --git a/platform/xcode/liblove.xcodeproj/project.pbxproj b/platform/xcode/liblove.xcodeproj/project.pbxproj index 8426b1588..15f44a8d2 100644 --- a/platform/xcode/liblove.xcodeproj/project.pbxproj +++ b/platform/xcode/liblove.xcodeproj/project.pbxproj @@ -4401,6 +4401,7 @@ DYLIB_CURRENT_VERSION = 9.0; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", + "$(PROJECT_DIR)/macosx/Frameworks", /Library/Frameworks, "$(LOCAL_LIBRARY_DIR)/Frameworks", ); @@ -4408,6 +4409,9 @@ GCC_ENABLE_OBJC_EXCEPTIONS = YES; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(PROJECT_DIR)/macosx/Frameworks/FreeType.framework/Headers", + "$(PROJECT_DIR)/macosx/Frameworks/Lua.framework/Headers", + "$(PROJECT_DIR)/macosx/Frameworks/SDL2.framework/Headers", /Library/Frameworks/FreeType.framework/Headers, /Library/Frameworks/Lua.framework/Headers, /Library/Frameworks/SDL2.framework/Headers, @@ -4435,6 +4439,7 @@ DYLIB_CURRENT_VERSION = 9.0; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", + "$(PROJECT_DIR)/macosx/Frameworks", /Library/Frameworks, "$(LOCAL_LIBRARY_DIR)/Frameworks", ); @@ -4442,6 +4447,9 @@ GCC_ENABLE_OBJC_EXCEPTIONS = YES; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(PROJECT_DIR)/macosx/Frameworks/FreeType.framework/Headers", + "$(PROJECT_DIR)/macosx/Frameworks/Lua.framework/Headers", + "$(PROJECT_DIR)/macosx/Frameworks/SDL2.framework/Headers", /Library/Frameworks/FreeType.framework/Headers, /Library/Frameworks/Lua.framework/Headers, /Library/Frameworks/SDL2.framework/Headers, @@ -4470,6 +4478,7 @@ DYLIB_CURRENT_VERSION = 9.0; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", + "$(PROJECT_DIR)/macosx/Frameworks", /Library/Frameworks, "$(LOCAL_LIBRARY_DIR)/Frameworks", ); @@ -4477,6 +4486,9 @@ GCC_ENABLE_OBJC_EXCEPTIONS = YES; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(PROJECT_DIR)/macosx/Frameworks/FreeType.framework/Headers", + "$(PROJECT_DIR)/macosx/Frameworks/Lua.framework/Headers", + "$(PROJECT_DIR)/macosx/Frameworks/SDL2.framework/Headers", /Library/Frameworks/FreeType.framework/Headers, /Library/Frameworks/Lua.framework/Headers, /Library/Frameworks/SDL2.framework/Headers, diff --git a/platform/xcode/love.xcodeproj/project.pbxproj b/platform/xcode/love.xcodeproj/project.pbxproj index 496c34afe..bc38e00b6 100644 --- a/platform/xcode/love.xcodeproj/project.pbxproj +++ b/platform/xcode/love.xcodeproj/project.pbxproj @@ -430,6 +430,7 @@ COMBINE_HIDPI_IMAGES = YES; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", + "$(PROJECT_DIR)/macosx/Frameworks", /Library/Frameworks, "\"$(SRCROOT)/build/Release\"", "\"$(SRCROOT)/build/Debug\"", @@ -439,6 +440,8 @@ GCC_OPTIMIZATION_LEVEL = 0; HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(PROJECT_DIR)/macosx/Frameworks/Lua.framework/Headers", + "$(PROJECT_DIR)/macosx/Frameworks/SDL2.framework/Headers", /Library/Frameworks/Lua.framework/Headers, /Library/Frameworks/SDL2.framework/Headers, ); @@ -458,6 +461,7 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", + "$(PROJECT_DIR)/macosx/Frameworks", /Library/Frameworks, "\"$(SRCROOT)/build/Release\"", "\"$(SRCROOT)/build/Debug\"", @@ -465,6 +469,8 @@ ); HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(PROJECT_DIR)/macosx/Frameworks/Lua.framework/Headers", + "$(PROJECT_DIR)/macosx/Frameworks/SDL2.framework/Headers", /Library/Frameworks/Lua.framework/Headers, /Library/Frameworks/SDL2.framework/Headers, ); @@ -841,6 +847,7 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", + "$(PROJECT_DIR)/macosx/Frameworks", /Library/Frameworks, "\"$(SRCROOT)/build/Release\"", "\"$(SRCROOT)/build/Debug\"", @@ -848,6 +855,8 @@ ); HEADER_SEARCH_PATHS = ( "$(inherited)", + "$(PROJECT_DIR)/macosx/Frameworks/Lua.framework/Headers", + "$(PROJECT_DIR)/macosx/Frameworks/SDL2.framework/Headers", /Library/Frameworks/Lua.framework/Headers, /Library/Frameworks/SDL2.framework/Headers, ); From 5bf25007da3eb68c5088dfd4db9f256266af1077 Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Fri, 25 Nov 2016 23:21:08 -0400 Subject: [PATCH 34/37] Fix building for iOS --HG-- branch : minor --- .../xcode/liblove.xcodeproj/project.pbxproj | 36 +++++++++++++++++++ src/modules/audio/openal/RecordingDevice.h | 2 ++ 2 files changed, 38 insertions(+) diff --git a/platform/xcode/liblove.xcodeproj/project.pbxproj b/platform/xcode/liblove.xcodeproj/project.pbxproj index 15f44a8d2..914319054 100644 --- a/platform/xcode/liblove.xcodeproj/project.pbxproj +++ b/platform/xcode/liblove.xcodeproj/project.pbxproj @@ -901,6 +901,24 @@ FA4F2BE61DE6650600CA37D7 /* wrap_Transform.h in Headers */ = {isa = PBXBuildFile; fileRef = FA4F2BE21DE6650600CA37D7 /* wrap_Transform.h */; }; FA4F2BE71DE6650D00CA37D7 /* Transform.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA4F2BDF1DE6650600CA37D7 /* Transform.cpp */; }; FA4F2BE81DE6651000CA37D7 /* wrap_Transform.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA4F2BE11DE6650600CA37D7 /* wrap_Transform.cpp */; }; + FA4F2C031DE936C200CA37D7 /* auxiliar.c in Sources */ = {isa = PBXBuildFile; fileRef = 217DFB9D1D9F6D490055D849 /* auxiliar.c */; }; + FA4F2C041DE936C600CA37D7 /* buffer.c in Sources */ = {isa = PBXBuildFile; fileRef = 217DFB9F1D9F6D490055D849 /* buffer.c */; }; + FA4F2C051DE936C900CA37D7 /* compat.c in Sources */ = {isa = PBXBuildFile; fileRef = 217DFBA11D9F6D490055D849 /* compat.c */; }; + FA4F2C061DE936CD00CA37D7 /* except.c in Sources */ = {isa = PBXBuildFile; fileRef = 217DFBA31D9F6D490055D849 /* except.c */; }; + FA4F2C071DE936DA00CA37D7 /* inet.c in Sources */ = {isa = PBXBuildFile; fileRef = 217DFBAB1D9F6D490055D849 /* inet.c */; }; + FA4F2C081DE936DD00CA37D7 /* io.c in Sources */ = {isa = PBXBuildFile; fileRef = 217DFBAD1D9F6D490055D849 /* io.c */; }; + FA4F2C091DE936E200CA37D7 /* luasocket.c in Sources */ = {isa = PBXBuildFile; fileRef = 217DFBB11D9F6D490055D849 /* luasocket.c */; }; + FA4F2C0A1DE936E600CA37D7 /* mime.c in Sources */ = {isa = PBXBuildFile; fileRef = 217DFBB61D9F6D490055D849 /* mime.c */; }; + FA4F2C0B1DE936EA00CA37D7 /* options.c in Sources */ = {isa = PBXBuildFile; fileRef = 217DFBBA1D9F6D490055D849 /* options.c */; }; + FA4F2C0C1DE936ED00CA37D7 /* select.c in Sources */ = {isa = PBXBuildFile; fileRef = 217DFBBD1D9F6D490055D849 /* select.c */; }; + FA4F2C0D1DE936F100CA37D7 /* serial.c in Sources */ = {isa = PBXBuildFile; fileRef = 217DFBBF1D9F6D490055D849 /* serial.c */; }; + FA4F2C0E1DE936FE00CA37D7 /* tcp.c in Sources */ = {isa = PBXBuildFile; fileRef = 217DFBC51D9F6D490055D849 /* tcp.c */; }; + FA4F2C0F1DE936FE00CA37D7 /* timeout.c in Sources */ = {isa = PBXBuildFile; fileRef = 217DFBC71D9F6D490055D849 /* timeout.c */; }; + FA4F2C101DE936FE00CA37D7 /* udp.c in Sources */ = {isa = PBXBuildFile; fileRef = 217DFBCB1D9F6D490055D849 /* udp.c */; }; + FA4F2C111DE936FE00CA37D7 /* unix.c in Sources */ = {isa = PBXBuildFile; fileRef = 217DFBCD1D9F6D490055D849 /* unix.c */; }; + FA4F2C121DE936FE00CA37D7 /* unixtcp.c in Sources */ = {isa = PBXBuildFile; fileRef = 217DFBCF1D9F6D490055D849 /* unixtcp.c */; }; + FA4F2C131DE936FE00CA37D7 /* unixudp.c in Sources */ = {isa = PBXBuildFile; fileRef = 217DFBD11D9F6D490055D849 /* unixudp.c */; }; + FA4F2C141DE936FE00CA37D7 /* usocket.c in Sources */ = {isa = PBXBuildFile; fileRef = 217DFBD51D9F6D490055D849 /* usocket.c */; }; FA56D9BC1C208A0200D8D3C7 /* libmodplug.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FA56D9BA1C2089EE00D8D3C7 /* libmodplug.a */; }; FA577AB016C7507900860150 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA577A7916C71A1700860150 /* Cocoa.framework */; }; FA577AC216C7512D00860150 /* FreeType.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA577A6716C719D900860150 /* FreeType.framework */; }; @@ -3516,6 +3534,7 @@ FA0B7EE31A95902D000E1D17 /* Window.cpp in Sources */, FA0B7AAB1A958EA3000E1D17 /* b2WeldJoint.cpp in Sources */, FA0B7EC21A95902C000E1D17 /* threads.cpp in Sources */, + FA4F2C051DE936C900CA37D7 /* compat.c in Sources */, FA0B79371A958E3B000E1D17 /* macosx.mm in Sources */, FA4F2BE81DE6651000CA37D7 /* wrap_Transform.cpp in Sources */, FA0B7D011A95902C000E1D17 /* Filesystem.cpp in Sources */, @@ -3529,6 +3548,7 @@ FA0B7E6D1A95902C000E1D17 /* wrap_RevoluteJoint.cpp in Sources */, FA0B7A5F1A958EA3000E1D17 /* b2Body.cpp in Sources */, FA0B7E641A95902C000E1D17 /* wrap_PolygonShape.cpp in Sources */, + FA4F2C031DE936C200CA37D7 /* auxiliar.c in Sources */, FA0B7E731A95902C000E1D17 /* wrap_Shape.cpp in Sources */, FA0B7CFE1A95902C000E1D17 /* File.cpp in Sources */, FA27B39E1B498151008A9DCE /* Video.cpp in Sources */, @@ -3546,6 +3566,7 @@ FA0B7D0A1A95902C000E1D17 /* wrap_FileData.cpp in Sources */, FA0B7ABE1A958EA3000E1D17 /* compress.c in Sources */, FA0B7CF21A95902C000E1D17 /* DroppedFile.cpp in Sources */, + FA4F2C141DE936FE00CA37D7 /* usocket.c in Sources */, FA0B7D8A1A95902C000E1D17 /* CompressedImageData.cpp in Sources */, FA0B7AD21A958EA3000E1D17 /* protocol.c in Sources */, FA0B7D1F1A95902C000E1D17 /* ImageRasterizer.cpp in Sources */, @@ -3563,6 +3584,7 @@ FA0B7E191A95902C000E1D17 /* MotorJoint.cpp in Sources */, FA0B7E4F1A95902C000E1D17 /* wrap_Fixture.cpp in Sources */, FA0B7EBF1A95902C000E1D17 /* Thread.cpp in Sources */, + FA4F2C121DE936FE00CA37D7 /* unixtcp.c in Sources */, FA0B7EB91A95902C000E1D17 /* Channel.cpp in Sources */, FA4B66CA1ABBCF1900558F15 /* Timer.cpp in Sources */, FA0B7A3F1A958EA3000E1D17 /* b2ChainShape.cpp in Sources */, @@ -3570,6 +3592,7 @@ FA0B7E521A95902C000E1D17 /* wrap_FrictionJoint.cpp in Sources */, FA4F2BB51DE1E4C300CA37D7 /* wrap_RecordingDevice.cpp in Sources */, FA0B7A311A958EA3000E1D17 /* b2CollidePolygon.cpp in Sources */, + FA4F2C111DE936FE00CA37D7 /* unix.c in Sources */, FA0B7A931A958EA3000E1D17 /* b2GearJoint.cpp in Sources */, FA0B7E0D1A95902C000E1D17 /* Fixture.cpp in Sources */, FA0B7D191A95902C000E1D17 /* TrueTypeRasterizer.cpp in Sources */, @@ -3586,6 +3609,7 @@ FA0B7ED21A95902C000E1D17 /* wrap_ThreadModule.cpp in Sources */, FA0B7EE01A95902D000E1D17 /* wrap_Touch.cpp in Sources */, FA0B7A3C1A958EA3000E1D17 /* b2TimeOfImpact.cpp in Sources */, + FA4F2C081DE936DD00CA37D7 /* io.c in Sources */, FA0B7CDD1A95902C000E1D17 /* Source.cpp in Sources */, FA0B7DC51A95902C000E1D17 /* wrap_JoystickModule.cpp in Sources */, FA0B7E701A95902C000E1D17 /* wrap_RopeJoint.cpp in Sources */, @@ -3629,6 +3653,7 @@ FA0B7D3A1A95902C000E1D17 /* Graphics.cpp in Sources */, FA4F2BB21DE1E4B400CA37D7 /* RecordingDevice.cpp in Sources */, FA0B7A2D1A958EA3000E1D17 /* b2CollideCircle.cpp in Sources */, + FA4F2C0F1DE936FE00CA37D7 /* timeout.c in Sources */, FA59A2D31C06481400328DBA /* ParticleSystem.cpp in Sources */, FA0B7E131A95902C000E1D17 /* GearJoint.cpp in Sources */, FA0B7DC21A95902C000E1D17 /* wrap_Joystick.cpp in Sources */, @@ -3660,14 +3685,19 @@ FA0B7A7E1A958EA3000E1D17 /* b2ContactSolver.cpp in Sources */, FA0B7D971A95902C000E1D17 /* ImageData.cpp in Sources */, FA0B7E581A95902C000E1D17 /* wrap_Joint.cpp in Sources */, + FA4F2C0A1DE936E600CA37D7 /* mime.c in Sources */, FA0B7E311A95902C000E1D17 /* Shape.cpp in Sources */, FA0B7E491A95902C000E1D17 /* wrap_DistanceJoint.cpp in Sources */, FA0B7A391A958EA3000E1D17 /* b2DynamicTree.cpp in Sources */, + FA4F2C101DE936FE00CA37D7 /* udp.c in Sources */, FA0B7A681A958EA3000E1D17 /* b2Island.cpp in Sources */, FA0B7E2B1A95902C000E1D17 /* RevoluteJoint.cpp in Sources */, FA0B7B291A958EA3000E1D17 /* simplexnoise1234.cpp in Sources */, FA0B7D261A95902C000E1D17 /* wrap_Font.cpp in Sources */, FA4F2BB31DE1E4B800CA37D7 /* RecordingDevice.cpp in Sources */, + FA4F2C071DE936DA00CA37D7 /* inet.c in Sources */, + FA4F2C0B1DE936EA00CA37D7 /* options.c in Sources */, + FA4F2C0D1DE936F100CA37D7 /* serial.c in Sources */, FA0B7E0A1A95902C000E1D17 /* EdgeShape.cpp in Sources */, FAB17BF11ABFB37500F9BA27 /* wrap_CompressedData.cpp in Sources */, FA0B7CF81A95902C000E1D17 /* FileData.cpp in Sources */, @@ -3675,6 +3705,7 @@ FA0B7E981A95902C000E1D17 /* Sound.cpp in Sources */, FA0B7E371A95902C000E1D17 /* WheelJoint.cpp in Sources */, FA0B7A8A1A958EA3000E1D17 /* b2PolygonContact.cpp in Sources */, + FA4F2C0C1DE936ED00CA37D7 /* select.c in Sources */, FA0B7D8E1A95902C000E1D17 /* ddsHandler.cpp in Sources */, FA0B7DFE1A95902C000E1D17 /* ChainShape.cpp in Sources */, FA0B7A451A958EA3000E1D17 /* b2EdgeShape.cpp in Sources */, @@ -3718,12 +3749,14 @@ FA0B7E1C1A95902C000E1D17 /* MouseJoint.cpp in Sources */, FA0B7CF51A95902C000E1D17 /* File.cpp in Sources */, FA0B7E341A95902C000E1D17 /* WeldJoint.cpp in Sources */, + FA4F2C091DE936E200CA37D7 /* luasocket.c in Sources */, FA0B7D5C1A95902C000E1D17 /* wrap_Font.cpp in Sources */, FA0B7B221A958EA3000E1D17 /* luasocket.cpp in Sources */, FA0B7D311A95902C000E1D17 /* Graphics.cpp in Sources */, FA0B7E9E1A95902C000E1D17 /* WaveDecoder.cpp in Sources */, FA0B7EB31A95902C000E1D17 /* System.cpp in Sources */, FA0B7D1C1A95902C000E1D17 /* GlyphData.cpp in Sources */, + FA4F2C061DE936CD00CA37D7 /* except.c in Sources */, FA0B7AAE1A958EA3000E1D17 /* b2WheelJoint.cpp in Sources */, FA0B7E671A95902C000E1D17 /* wrap_PrismaticJoint.cpp in Sources */, FA0B7DCE1A95902C000E1D17 /* wrap_Keyboard.cpp in Sources */, @@ -3739,6 +3772,7 @@ FA620A3B1AA305F6005DB4C2 /* types.cpp in Sources */, FA0B7DD41A95902C000E1D17 /* BezierCurve.cpp in Sources */, FA0B7E7C1A95902C000E1D17 /* wrap_World.cpp in Sources */, + FA4F2C0E1DE936FE00CA37D7 /* tcp.c in Sources */, FA0B7D911A95902C000E1D17 /* FormatHandler.cpp in Sources */, FA0B7D431A95902C000E1D17 /* OpenGL.cpp in Sources */, FA0B7DBF1A95902C000E1D17 /* JoystickModule.cpp in Sources */, @@ -3755,6 +3789,7 @@ FA0B7E251A95902C000E1D17 /* PrismaticJoint.cpp in Sources */, FA0B7A561A958EA3000E1D17 /* b2Settings.cpp in Sources */, FA0B7E611A95902C000E1D17 /* wrap_Physics.cpp in Sources */, + FA4F2C041DE936C600CA37D7 /* buffer.c in Sources */, FA0B7DC81A95902C000E1D17 /* Keyboard.cpp in Sources */, FA4943551D9DDFFE00E8D38A /* HashFunction.cpp in Sources */, FA0B7EAD1A95902C000E1D17 /* wrap_SoundData.cpp in Sources */, @@ -3766,6 +3801,7 @@ FA0B7DE61A95902C000E1D17 /* Cursor.cpp in Sources */, FA0B7EDC1A95902D000E1D17 /* Touch.cpp in Sources */, FA0B7CE91A95902C000E1D17 /* Event.cpp in Sources */, + FA4F2C131DE936FE00CA37D7 /* unixudp.c in Sources */, FA0B7A961A958EA3000E1D17 /* b2Joint.cpp in Sources */, FA0B7A621A958EA3000E1D17 /* b2ContactManager.cpp in Sources */, FA0B7A2F1A958EA3000E1D17 /* b2CollideEdge.cpp in Sources */, diff --git a/src/modules/audio/openal/RecordingDevice.h b/src/modules/audio/openal/RecordingDevice.h index 2ccaea240..4092d1f21 100644 --- a/src/modules/audio/openal/RecordingDevice.h +++ b/src/modules/audio/openal/RecordingDevice.h @@ -21,6 +21,8 @@ #ifndef LOVE_AUDIO_OPENAL_RECORDING_DEVICE_H #define LOVE_AUDIO_OPENAL_RECORDING_DEVICE_H +#include "common/config.h" + #ifdef LOVE_APPLE_USE_FRAMEWORKS #ifdef LOVE_IOS #include From 82f66e30266b35887b5d061b2934285d082915ab Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Fri, 25 Nov 2016 23:50:37 -0400 Subject: [PATCH 35/37] Hopefully fix a couple potential issues with restoring Shader texture units after a setMode. --HG-- branch : minor --- src/modules/graphics/opengl/Shader.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/modules/graphics/opengl/Shader.cpp b/src/modules/graphics/opengl/Shader.cpp index df6e848a5..362b50668 100644 --- a/src/modules/graphics/opengl/Shader.cpp +++ b/src/modules/graphics/opengl/Shader.cpp @@ -237,6 +237,15 @@ void Shader::mapActiveUniforms() if (u.location == -1) continue; + if (u.baseType == UNIFORM_SAMPLER) + { + // Initialize all samplers to 0. Both GLSL and GLSL ES are supposed + // to do this themselves, but some Android devices (galaxy tab 3 and + // 4) don't seem to do it... + // NOTE: We also restore previously set texture units below. + glUniform1iv(u.location, u.count, u.ints); + } + // Make sure previously set uniform data is preserved, and shader- // initialized values are retrieved. auto oldu = olduniforms.find(u.name); @@ -294,11 +303,6 @@ void Shader::mapActiveUniforms() if (u.baseType == UNIFORM_SAMPLER) { - // Initialize all samplers to 0. Both GLSL and GLSL ES are - // supposed to do this themselves, but some Android devices - // (galaxy tab 3 and 4) don't seem to do it... - glUniform1iv(u.location, u.count, u.ints); - u.textures = new Texture*[u.count]; memset(u.textures, 0, sizeof(Texture *) * u.count); } @@ -679,7 +683,7 @@ void Shader::sendTextures(const UniformInfo *info, Texture **textures, int count return; count = std::min(count, info->count); - bool updateuniform = false; + bool updateuniform = internalUpdate; // Make sure the shader's samplers are associated with texture units. for (int i = 0; i < count; i++) From 100404c917409f2e1c8a4aea2f768654f8026e8c Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Sat, 26 Nov 2016 00:36:59 -0400 Subject: [PATCH 36/37] Revert a dumb change that caused a crash due to reading from invalid memory --HG-- branch : minor --- src/modules/graphics/opengl/Shader.cpp | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/modules/graphics/opengl/Shader.cpp b/src/modules/graphics/opengl/Shader.cpp index 362b50668..0eb8ce9ae 100644 --- a/src/modules/graphics/opengl/Shader.cpp +++ b/src/modules/graphics/opengl/Shader.cpp @@ -237,15 +237,6 @@ void Shader::mapActiveUniforms() if (u.location == -1) continue; - if (u.baseType == UNIFORM_SAMPLER) - { - // Initialize all samplers to 0. Both GLSL and GLSL ES are supposed - // to do this themselves, but some Android devices (galaxy tab 3 and - // 4) don't seem to do it... - // NOTE: We also restore previously set texture units below. - glUniform1iv(u.location, u.count, u.ints); - } - // Make sure previously set uniform data is preserved, and shader- // initialized values are retrieved. auto oldu = olduniforms.find(u.name); @@ -303,6 +294,11 @@ void Shader::mapActiveUniforms() if (u.baseType == UNIFORM_SAMPLER) { + // Initialize all samplers to 0. Both GLSL and GLSL ES are + // supposed to do this themselves, but some Android devices + // (galaxy tab 3 and 4) don't seem to do it... + glUniform1iv(u.location, u.count, u.ints); + u.textures = new Texture*[u.count]; memset(u.textures, 0, sizeof(Texture *) * u.count); } From cbba8c40a62c334aba03e5ba957b85d699767418 Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Sat, 26 Nov 2016 01:50:36 -0400 Subject: [PATCH 37/37] Split Mesh:attachAttribute(name, nil) out into Mesh:detachAttribute. Fix attachAttribute. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Don’t let me push commits on a friday night… --HG-- branch : minor --- src/modules/graphics/opengl/Mesh.cpp | 43 +++++++++++++---------- src/modules/graphics/opengl/Mesh.h | 1 + src/modules/graphics/opengl/Shader.cpp | 2 +- src/modules/graphics/opengl/wrap_Mesh.cpp | 20 +++++++---- 4 files changed, 40 insertions(+), 26 deletions(-) diff --git a/src/modules/graphics/opengl/Mesh.cpp b/src/modules/graphics/opengl/Mesh.cpp index 7d7c011aa..23eb8411d 100644 --- a/src/modules/graphics/opengl/Mesh.cpp +++ b/src/modules/graphics/opengl/Mesh.cpp @@ -311,24 +311,7 @@ bool Mesh::isAttributeEnabled(const std::string &name) const void Mesh::attachAttribute(const std::string &name, Mesh *mesh, const std::string &attachname) { - AttachedAttribute oldattrib = {}; - - auto it = attachedAttributes.find(name); - if (it != attachedAttributes.end()) - oldattrib = it->second; - - if (mesh == nullptr) - { - if (it != attachedAttributes.end() && oldattrib.mesh != this) - { - oldattrib.mesh->release(); - attachedAttributes.erase(it); - if (getAttributeIndex(name) != -1) - attachAttribute(name, this, name); - return; - } - } - else if (mesh != this) + if (mesh != this) { for (const auto &it : mesh->attachedAttributes) { @@ -339,7 +322,13 @@ void Mesh::attachAttribute(const std::string &name, Mesh *mesh, const std::strin } } + AttachedAttribute oldattrib = {}; AttachedAttribute newattrib = {}; + + auto it = attachedAttributes.find(name); + if (it != attachedAttributes.end()) + oldattrib = it->second; + newattrib.mesh = mesh; newattrib.enabled = oldattrib.mesh ? oldattrib.enabled : true; newattrib.index = mesh->getAttributeIndex(attachname); @@ -356,6 +345,24 @@ void Mesh::attachAttribute(const std::string &name, Mesh *mesh, const std::strin oldattrib.mesh->release(); } +bool Mesh::detachAttribute(const std::string &name) +{ + auto it = attachedAttributes.find(name); + + if (it != attachedAttributes.end() && it->second.mesh != this) + { + it->second.mesh->release(); + attachedAttributes.erase(it); + + if (getAttributeIndex(name) != -1) + attachAttribute(name, this, name); + + return true; + } + + return false; +} + void *Mesh::mapVertexData() { return vbo->map(); diff --git a/src/modules/graphics/opengl/Mesh.h b/src/modules/graphics/opengl/Mesh.h index c34032d3b..701feb89a 100644 --- a/src/modules/graphics/opengl/Mesh.h +++ b/src/modules/graphics/opengl/Mesh.h @@ -139,6 +139,7 @@ public: * will be used when drawing this Mesh. **/ void attachAttribute(const std::string &name, Mesh *mesh, const std::string &attachname); + bool detachAttribute(const std::string &name); void *mapVertexData(); void unmapVertexData(size_t modifiedoffset = 0, size_t modifiedsize = -1); diff --git a/src/modules/graphics/opengl/Shader.cpp b/src/modules/graphics/opengl/Shader.cpp index 0eb8ce9ae..df6e848a5 100644 --- a/src/modules/graphics/opengl/Shader.cpp +++ b/src/modules/graphics/opengl/Shader.cpp @@ -679,7 +679,7 @@ void Shader::sendTextures(const UniformInfo *info, Texture **textures, int count return; count = std::min(count, info->count); - bool updateuniform = internalUpdate; + bool updateuniform = false; // Make sure the shader's samplers are associated with texture units. for (int i = 0; i < count; i++) diff --git a/src/modules/graphics/opengl/wrap_Mesh.cpp b/src/modules/graphics/opengl/wrap_Mesh.cpp index f0bde6fa0..004340ef7 100644 --- a/src/modules/graphics/opengl/wrap_Mesh.cpp +++ b/src/modules/graphics/opengl/wrap_Mesh.cpp @@ -337,17 +337,22 @@ int w_Mesh_attachAttribute(lua_State *L) { Mesh *t = luax_checkmesh(L, 1); const char *name = luaL_checkstring(L, 2); - const char *attachname = name; - Mesh *mesh = nullptr; - if (lua_isnoneornil(L, 3)) - { - mesh = luax_checkmesh(L, 3); - attachname = luaL_optstring(L, 4, attachname); - } + Mesh *mesh = luax_checkmesh(L, 3); + const char *attachname = luaL_optstring(L, 4, name); luax_catchexcept(L, [&](){ t->attachAttribute(name, mesh, attachname); }); return 0; } +int w_Mesh_detachAttribute(lua_State *L) +{ + Mesh *t = luax_checkmesh(L, 1); + const char *name = luaL_checkstring(L, 2); + bool success = false; + luax_catchexcept(L, [&](){ success = t->detachAttribute(name); }); + luax_pushboolean(L, success); + return 1; +} + int w_Mesh_flush(lua_State *L) { Mesh *t = luax_checkmesh(L, 1); @@ -520,6 +525,7 @@ static const luaL_Reg w_Mesh_functions[] = { "setAttributeEnabled", w_Mesh_setAttributeEnabled }, { "isAttributeEnabled", w_Mesh_isAttributeEnabled }, { "attachAttribute", w_Mesh_attachAttribute }, + { "detachAttribute", w_Mesh_detachAttribute }, { "flush", w_Mesh_flush }, { "setVertexMap", w_Mesh_setVertexMap }, { "getVertexMap", w_Mesh_getVertexMap },