diff --git a/.gitmodules b/.gitmodules index 8cd01ae..2fb3c51 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "external/filesystem"] path = external/filesystem url = https://github.com/gulrak/filesystem +[submodule "external/simde"] + path = external/simde + url = https://github.com/simd-everywhere/simde diff --git a/AudioStream.cpp b/AudioStream.cpp index a5a629e..56ed4b2 100644 --- a/AudioStream.cpp +++ b/AudioStream.cpp @@ -111,7 +111,7 @@ namespace cpl janitorThreads(); - CPL_RUNTIME_ASSERTION(threads.size() == 0); + CPL_RUNTIME_ASSERTION(threads.size() == 0); // TODO: This can happen and hang the process. } private: diff --git a/AudioStream.h b/AudioStream.h index a1b5023..c0340bd 100644 --- a/AudioStream.h +++ b/AudioStream.h @@ -537,7 +537,7 @@ namespace cpl protected: std::shared_ptr stream; Reference() = default; - Reference(Reference&& ref) + Reference(Reference&& ref) noexcept : stream(std::move(ref.stream)) { } @@ -545,32 +545,6 @@ namespace cpl Handle getHandle() const noexcept { return { stream.get() }; } }; - struct ExclusiveDebugScope - { - ExclusiveDebugScope(std::atomic_flag& flag) - : flag(flag) - { - if (flag.test_and_set(std::memory_order_release)) - { - flag.clear(); - CPL_RUNTIME_EXCEPTION("Re-entrancy / concurrency detected in audio stream producer"); - } - } - - ~ExclusiveDebugScope() noexcept(false) - { - // TODO: use test() in C++20 - if (!flag.test_and_set(std::memory_order_release)) - { - CPL_RUNTIME_EXCEPTION("Re-entrancy / concurrency detected in audio stream producer"); - } - - flag.clear(); - } - - std::atomic_flag& flag; - }; - class Output final : public Reference { public: @@ -704,8 +678,33 @@ namespace cpl struct FrameBatch { + FrameBatch(std::shared_ptr&& out) + : output(std::move(out)), stream(nullptr) + { + if (output) + output->beginFrameProcessing(); + } + + bool submitFrame(ProducerFrame&& frame) + { + if (output) + output->handleFrame(std::move(frame)); + else if(stream) + return stream->publishFrame(std::move(frame)); + + return true; + } + + ~FrameBatch() + { + if (output) + output->endFrameProcessing(); + } + + protected: + template - bool hasContents(const std::weak_ptr& w) + static bool hasContents(const std::weak_ptr& w) { return w.owner_before(std::weak_ptr{}) || std::weak_ptr{}.owner_before(w); } @@ -724,36 +723,54 @@ namespace cpl } } - FrameBatch(std::shared_ptr&& out) - : output(std::move(out)), stream(nullptr) + private: + + AudioStream* stream; + std::shared_ptr output; + }; + + struct ExclusiveDebugScope + { + ExclusiveDebugScope(std::atomic_flag& flag) + : flag(flag) { - if (output) - output->beginFrameProcessing(); + if (flag.test_and_set(std::memory_order_release)) + { + flag.clear(); + CPL_RUNTIME_EXCEPTION("Re-entrancy / concurrency detected in audio stream producer"); + } } - bool submitFrame(ProducerFrame&& frame) + ~ExclusiveDebugScope() noexcept(false) { - if (output) - output->handleFrame(std::move(frame)); - else if(stream) - return stream->publishFrame(std::move(frame)); + // TODO: use test() in C++20 + if (!flag.test_and_set(std::memory_order_release)) + { + CPL_RUNTIME_EXCEPTION("Re-entrancy / concurrency detected in audio stream producer"); + } - return true; + flag.clear(); } - ~FrameBatch() - { - if (output) - output->endFrameProcessing(); - } + std::atomic_flag& flag; + }; - AudioStream* stream; - std::shared_ptr output; + class Input; + + struct InputFrameBatch : public FrameBatch + { + InputFrameBatch(Input& input); + + private: + + ExclusiveDebugScope debugScope; }; class Input final : public Reference { friend class AudioStream; + friend struct InputFrameBatch; + public: #ifdef CPL_JUCE void processIncomingRTAudio(const T* const * buffer, std::size_t numChannels, std::size_t numSamples, juce::AudioPlayHead& ph) @@ -766,15 +783,15 @@ namespace cpl void processIncomingRTAudio(const T* const * buffer, std::size_t numChannels, std::size_t numSamples, const Playhead& ph); /// - /// Returns the playhead for the system. - /// Only valid to call and read, while you're inside a - /// real time callback. + /// This must be called at least once, before streaming starts. + /// It is not safe to call this function concurrently + /// - decide on one thread, controlling it. /// - const Playhead& getPlayhead() const noexcept + template + void initializeInfo(ModifierFunc&& func, InputFrameBatch& batch) { - ExclusiveDebugScope scope(reentrancy); - - return playhead; + func(internalInfo); + batch.submitFrame(ProducerFrame(internalInfo)); } /// @@ -785,24 +802,24 @@ namespace cpl template void initializeInfo(ModifierFunc&& func) { - ExclusiveDebugScope scope(reentrancy); - - func(internalInfo); - FrameBatch batch(*this->stream); - batch.submitFrame(ProducerFrame(internalInfo)); + InputFrameBatch batch(*this); + initializeInfo(std::move(func), batch); } - void enqueueChannelName(std::size_t index, std::string&& name) + void enqueueChannelName(std::size_t index, std::string&& name, InputFrameBatch& batch) { - ExclusiveDebugScope scope(reentrancy); - ProducerFrame frame; frame.template emplace(ChannelNameData{ index, std::move(name) }); - FrameBatch batch(*this->stream); batch.submitFrame(std::move(frame)); } + void enqueueChannelName(std::size_t index, std::string&& name) + { + InputFrameBatch batch(*this); + enqueueChannelName(index, std::move(name), batch); + } + /// /// Checks to see if there currently is anyone listening to the output. /// If not, you're free to skip calling @@ -955,9 +972,7 @@ namespace cpl input.stream = stream; output->stream = std::move(stream); - auto ret = std::make_tuple(std::move(input), std::move(output)); - - return std::move(ret); + return std::make_tuple(std::move(input), std::move(output)); } private: diff --git a/AudioStream.inl b/AudioStream.inl index 07ee44d..a9a7fb5 100644 --- a/AudioStream.inl +++ b/AudioStream.inl @@ -329,16 +329,21 @@ namespace cpl } template - inline void AudioStream::Input::processIncomingRTAudio(const T* const* buffer, std::size_t numChannels, std::size_t numSamples, const AudioStream::Playhead& ph) + inline AudioStream::InputFrameBatch::InputFrameBatch(Input& input) + : FrameBatch(*input.stream), debugScope(input.reentrancy) { - ExclusiveDebugScope scope(reentrancy); + } + + template + inline void AudioStream::Input::processIncomingRTAudio(const T* const* buffer, std::size_t numChannels, std::size_t numSamples, const AudioStream::Playhead& ph) + { if (internalInfo.isSuspended) return; CPL_RUNTIME_ASSERTION(numChannels == internalInfo.channels); - FrameBatch batch(*this->stream); + InputFrameBatch batch(*this); cpl::CProcessorTimer overhead, all; overhead.start(); all.start(); diff --git a/CPLSource.cpp b/CPLSource.cpp index ae957c8..33ba22e 100755 --- a/CPLSource.cpp +++ b/CPLSource.cpp @@ -31,10 +31,11 @@ #include "MacroConstants.h" #include "LibraryOptions.h" -#include "Common.h" +#include "PlatformSpecific.h" #include "simd/simd_consts.cpp" #ifdef CPL_JUCE +#include "rendering/CDisplaySetup.cpp" #include "Resources.cpp" //#include "fonts/tahoma.cpp" @@ -43,7 +44,6 @@ #include "gui/GUI.cpp" // rendering #include "rendering/CSubpixelSoftwareGraphics.cpp" -#include "rendering/CDisplaySetup.cpp" // io and stuff @@ -80,4 +80,4 @@ #if !defined(CPL_LEAN) #include "CPLTests.cpp" #endif -*/ \ No newline at end of file +*/ diff --git a/CPresetManager.cpp b/CPresetManager.cpp index 30ab775..626ae9c 100755 --- a/CPresetManager.cpp +++ b/CPresetManager.cpp @@ -29,6 +29,8 @@ #include "CPresetManager.h" #include "Misc.h" #include +#include +#include "PlatformMisc.h" namespace cpl { @@ -47,164 +49,131 @@ namespace cpl return ::cpl::presetDirectory(); } - bool CPresetManager::savePresetAs(const ISerializerSystem & archive, juce::File & location, const std::string & uniqueExt) + CPresetManager::DialogState CPresetManager::savePresetAs(const CCheckedSerializer& archive, FileSavedCallback callback) { // should we really save empty files? if (archive.isEmpty()) - return false; + return nullptr; - std::string extension = uniqueExt.length() ? uniqueExt + "." + programInfo.programAbbr : programInfo.programAbbr; + std::string extension = !archive.getName().empty() ? archive.getName() + "." + programInfo.programAbbr : programInfo.programAbbr; - juce::FileChooser fileChooser(programInfo.name + ": Save preset to a file...", - juce::File(presetDirectory()), - #ifdef CPL_UNIXC - // native dialogs hangs programs on the distros I've tried - "*." + extension, false); - #else - "*." + extension); + auto fileChooser = std::make_unique(programInfo.name + ": Save preset to a file...", + juce::File(presetDirectory()), + #ifdef CPL_UNIXC + // native dialogs hangs programs on the distros I've tried + "*." + extension, false); + #else + "*." + extension); #endif - if (fileChooser.browseForFileToSave(true)) - { - auto result = fileChooser.getResult(); - - juce::String dotExt = "." + extension; - - bool hasExt = false; - - juce::String tempString = result.getFullPathName(); - juce::String finalString = tempString; - - // OS X handles multiple endings by duplicating them.. litterally. - // how nice. - while (tempString.endsWith(dotExt)) + fileChooser->launchAsync( + juce::FileBrowserComponent::saveMode, + [this, archive, callback, extension](const juce::FileChooser& chooser) { - hasExt = true; - finalString = tempString; - tempString = tempString.dropLastCharacters(dotExt.length()); - } - - std::string path = - hasExt ? - finalString.toStdString() : - result.withFileExtension(extension.c_str()).getFullPathName().toStdString(); + auto result = chooser.getResult(); + if (result.existsAsFile() || result.getParentDirectory().exists()) - if (!savePreset(path, archive, location)) - { - auto userAnswer = Misc::MsgBox("Error opening file:\n" + path + "\nTry another location?", - programInfo.name + "Error saving preset to file...", Misc::MsgStyle::sYesNoCancel | Misc::MsgIcon::iWarning); - if (userAnswer == Misc::MsgButton::bYes) - { - return savePresetAs(archive, location, uniqueExt); - } - else { - return false; + juce::String dotExt = "." + extension; + bool hasExt = false; + juce::String tempString = result.getFullPathName(); + juce::String finalString = tempString; + + // OS X handles multiple endings by duplicating them.. litterally. + // how nice. + while (tempString.endsWith(dotExt)) + { + hasExt = true; + finalString = tempString; + tempString = tempString.dropLastCharacters(dotExt.length()); + } + + std::string path = + hasExt ? + finalString.toStdString() : + result.withFileExtension(extension.c_str()).getFullPathName().toStdString(); + + if (savePreset(path, archive)) + { + if (callback) + callback(result); + } } } - else - { - return true; - } - } + ); - return false; + return std::move(fileChooser); } - bool CPresetManager::loadPresetAs(ISerializerSystem & builder, juce::File & location, const std::string & uniqueExt) - { - std::string extension = uniqueExt.length() ? uniqueExt + "." + programInfo.programAbbr : programInfo.programAbbr; + CPresetManager::DialogState CPresetManager::loadPresetAs(CCheckedSerializer builder, FileLoadedCallback whenDone) + { + std::string extension = !builder.getName().empty() ? builder.getName() + "." + programInfo.programAbbr : programInfo.programAbbr; - juce::FileChooser fileChooser(programInfo.name + ": Load preset from a file...", + auto fileChooser = std::make_unique(programInfo.name + ": Load preset from a file...", juce::File(presetDirectory()), - #ifdef CPL_MAC - "*." + programInfo.programAbbr); // it just doesn't work.. - #elif defined(CPL_UNIXC) - // native dialogs hangs programs on the distros I've tried + #ifdef CPL_MAC + "*." + extension, false); // native file selector on macos can only filter for one extension + #elif defined(CPL_UNIXC) + // native dialogs hangs programs on the distros I've tried "*." + extension, false); #else "*." + extension); #endif - if (fileChooser.browseForFileToOpen()) - { - auto result = fileChooser.getResult(); - // the file chooser can only choose file names with the correct extension, - // no need to check. - std::string path = result.getFullPathName().toStdString(); - if (!result.existsAsFile()) + fileChooser->launchAsync( + juce::FileBrowserComponent::openMode | juce::FileBrowserComponent::canSelectFiles, + [this, whenDone, extension, builder = std::move(builder)] (const juce::FileChooser& chooser) mutable { - auto userAnswer = Misc::MsgBox("Error opening file:\n" + path + "\nTry another location?", - programInfo.name + "Error loading preset from file...", Misc::MsgStyle::sYesNo | Misc::MsgIcon::iQuestion); - if (userAnswer == Misc::MsgButton::bYes) - { - return loadPresetAs(builder, location, uniqueExt); - } - else - { - return false; - } - } - else if (!result.getFileName().contains(extension.c_str())) - { - auto userAnswer = Misc::MsgBox("Warning: The selected file:\n" + result.getFileName().toStdString() + "\nDoes not have the verifiable extension " + extension + - "\nDo you want to load another file (Yes), proceed with the current (No) or discard the loading query (Cancel)?", - programInfo.name + ": Query about loading preset from file...", - Misc::MsgStyle::sYesNoCancel | Misc::MsgIcon::iWarning); - if (userAnswer == Misc::MsgButton::bYes) - { - return loadPresetAs(builder, location, uniqueExt); - } - else if (userAnswer == Misc::MsgButton::bNo) - { - return loadPreset(path, builder, location); - } - else + auto result = chooser.getResult(); + if (result.existsAsFile()) { - return false; + // the file chooser can only choose file names with the correct extension, + // no need to check. + std::string path = result.getFullPathName().toStdString(); + + if (!result.getFileName().contains(extension.c_str())) + { + // Warning about extension mismatch could be handled here + } + + if (loadPreset(path, builder)) + { + if (whenDone) + whenDone(result, builder); + } } } - else - { - return loadPreset(path, builder, location); - } - - } + ); - return false; + return std::move(fileChooser); } // these functions saves/loads directly - bool CPresetManager::savePreset(const std::string & path, const ISerializerSystem & archive, juce::File & location) + bool CPresetManager::savePreset(cpl::string_ref path, const ISerializerSystem & archive) { CExclusiveFile file; - if (!file.open(path, file.writeMode)) + if (!file.open(path.c_str(), file.writeMode)) return false; // clear existing file.. file.remove(); - if (!file.open(path)) + if (!file.open(path.c_str())) return false; auto content = archive.compile(true); - if (file.write(content.getBlock(), (std::int64_t)content.getSize())) - { - location = path; - return true; - } - - return false; + return file.write(content.getBlock(), (std::int64_t)content.getSize()); } - bool CPresetManager::loadPreset(const std::string & path, ISerializerSystem & builder, juce::File & location) + + bool CPresetManager::loadPreset(cpl::string_ref path, ISerializerSystem & builder) { try { CExclusiveFile file; - if (!file.open(path, file.readMode)) + if (!file.open(path.c_str(), file.readMode)) return false; std::vector data; @@ -215,18 +184,16 @@ namespace cpl return false; builder.clear(); - if (builder.build(WeakContentWrapper(data.data(), size))) - { - location = path; - return true; - } + return builder.build(WeakContentWrapper(data.data(), size)); } catch (const std::exception & e) { - Misc::MsgBox("Exception loading preset at " + path + ":\n" + e.what(), programInfo.name, Misc::MsgIcon::iStop); + Misc::MsgBox("Exception loading preset at " + path.string() + ":\n" + e.what(), programInfo.name, Misc::MsgIcon::iStop); } + return false; } + const std::vector & CPresetManager::getPresets() { currentPresets.clear(); @@ -235,41 +202,45 @@ namespace cpl while (iter.next()) { currentPresets.push_back(iter.getFile()); - } return currentPresets; } - bool CPresetManager::saveDefaultPreset(const ISerializerSystem & archive, juce::File & location) + + bool CPresetManager::saveDefaultPreset(const ISerializerSystem & archive) { - return savePreset(presetDirectory() + "default." + programInfo.programAbbr, archive, location); + return savePreset(presetDirectory() + "default." + programInfo.programAbbr, archive); } - bool CPresetManager::loadDefaultPreset(ISerializerSystem & builder, juce::File & location) + CPresetManager::DialogState CPresetManager::loadDefaultPreset(FileLoadedCallback whenDone) { auto path = presetDirectory() + "default." + programInfo.programAbbr; - if (!loadPreset(path, builder, location)) + + CCheckedSerializer builder("default"); + + if (loadPreset(path, builder)) { - auto answer = cpl::Misc::MsgBox( - "Error loading default preset at:\n" + path + "\n" + GetLastOSErrorMessage() + - "\nLoad a different preset?", - programInfo.name + ": Error loading preset...", - Misc::MsgIcon::iQuestion | Misc::MsgStyle::sYesNoCancel); - if (answer == Misc::MsgButton::bYes) - { - return loadPresetAs(builder, location); - } - else - { - return false; - } + whenDone({ path }, builder); + return {}; + } + auto answer = cpl::Misc::MsgBox( + "Error loading default preset at:\n" + path + "\n" + GetLastOSErrorMessage() + + "\nLoad a different preset?", + programInfo.name + ": Error loading preset...", + Misc::MsgIcon::iQuestion | Misc::MsgStyle::sYesNoCancel); + if (answer == Misc::MsgButton::bYes) + { + // Since loadPresetAs is now async, we can't easily use it here + // This would need a different approach or just return false + return loadPresetAs(builder, whenDone); } - return true; + + return {}; } CPresetManager::CPresetManager() {} CPresetManager::~CPresetManager() {} -}; +}; diff --git a/CPresetManager.h b/CPresetManager.h index ff7d6bb..d6d974d 100755 --- a/CPresetManager.h +++ b/CPresetManager.h @@ -34,6 +34,8 @@ #include "CExclusiveFile.h" #include #include +#include +#include namespace cpl { @@ -41,20 +43,24 @@ namespace cpl { public: + typedef std::unique_ptr DialogState; + static CPresetManager & instance(); + typedef std::function FileSavedCallback; + typedef std::function FileLoadedCallback; + // these functions pops up file selectors - bool savePresetAs(const ISerializerSystem & serializer, juce::File & location, const std::string & uniqueExt = ""); - bool loadPresetAs(ISerializerSystem & serializer, juce::File & location, const std::string & uniqueExt = ""); + DialogState savePresetAs(const CCheckedSerializer& archive, FileSavedCallback callback); + DialogState loadPresetAs(CCheckedSerializer builder, FileLoadedCallback whenDone); // these functions saves/loads directly - bool savePreset(const std::string & name, const ISerializerSystem & serializer, juce::File & location); - bool loadPreset(const std::string & name, ISerializerSystem & serializer, juce::File & location); + bool savePreset(cpl::string_ref path, const ISerializerSystem & serializer); + bool loadPreset(cpl::string_ref path, ISerializerSystem & serializer); const std::vector & getPresets(); - bool saveDefaultPreset(const ISerializerSystem & serializer, juce::File & location); - bool loadDefaultPreset(ISerializerSystem & serializer, juce::File & location); + bool saveDefaultPreset(const ISerializerSystem & serializer); + DialogState loadDefaultPreset(FileLoadedCallback whenDone); std::string getPresetDirectory() const noexcept; - juce::File getCurrentPreset(); private: std::vector currentPresets; diff --git a/CProcessorTimer.h b/CProcessorTimer.h index 5d855d3..1ed5bbe 100644 --- a/CProcessorTimer.h +++ b/CProcessorTimer.h @@ -30,15 +30,17 @@ #ifndef CPL_CPROCESSORTIMER_H #define CPL_CPROCESSORTIMER_H -#include "Utility.h" +#include "MacroConstants.h" #include "Misc.h" #include "Mathext.h" #include -#include -#include #include -#include + +#ifdef CPL_MAC +#define _CPL_USE_HCLOCK +#else #include "system/SysStats.h" +#endif namespace cpl { @@ -50,14 +52,15 @@ namespace cpl /// class CProcessorTimer { - private: - - public: - +#ifdef _CPL_USE_HCLOCK + typedef decltype(cpl::Misc::TimeCounter()) cclock_t; +#else typedef decltype(cpl::Misc::ClockCounter()) cclock_t; - +#endif + CProcessorTimer() + : deltaT(), startT() { } @@ -89,7 +92,7 @@ namespace cpl /// /// Returns the number of clocks passed by since start() (excluding whatever happened between any pause/resumes) /// - cclock_t getTime() + cclock_t getTime() const noexcept { return getClocks() - startT; } @@ -101,21 +104,34 @@ namespace cpl { startT = 0; deltaT = 0; } - + + double coreUsage() const noexcept + { + return clocksToCoreUsage(getTime()); + } + /// /// Returns a fraction, that represents how much of the core's capability was used /// (ie. clocks_used / core_clocks_per_sec) /// static double clocksToCoreUsage(cclock_t clocks) { +#ifdef _CPL_USE_HCLOCK + return cpl::Misc::TimeToSeconds(clocks); +#else return (0.001 * clocks) / (cpl::system::CProcessor::getMHz() * 1000); +#endif } private: static cclock_t getClocks() { +#ifdef _CPL_USE_HCLOCK + return cpl::Misc::TimeCounter(); +#else return cpl::Misc::ClockCounter(); +#endif } cclock_t deltaT, startT; diff --git a/CThread.h b/CThread.h index c2fbb15..6b651b1 100755 --- a/CThread.h +++ b/CThread.h @@ -93,7 +93,7 @@ namespace cpl } int join() - { + { // TODO: This is broken, but yeah. Ditch this and use std::thread anyway. void * retval; if (!thread) @@ -186,4 +186,4 @@ namespace cpl } }; } -#endif +#endif diff --git a/CTimer.h b/CTimer.h index 202e1c9..2b6b39b 100755 --- a/CTimer.h +++ b/CTimer.h @@ -280,4 +280,4 @@ namespace cpl }; // cpl -#endif +#endif diff --git a/ConcurrentServices.h b/ConcurrentServices.h index 5f84de9..add26ad 100644 --- a/ConcurrentServices.h +++ b/ConcurrentServices.h @@ -32,14 +32,11 @@ #ifndef CPL_CONCURRENTSERVICES_H #define CPL_CONCURRENTSERVICES_H -#include "Common.h" #include -#include "Utility.h" #include #include #include -#include "Misc.h" - +#include "Exceptions.h" namespace cpl { @@ -257,4 +254,4 @@ namespace cpl }; }; -#endif \ No newline at end of file +#endif diff --git a/Core.h b/Core.h index a65b8be..5b59219 100644 --- a/Core.h +++ b/Core.h @@ -44,7 +44,7 @@ namespace cpl string ret; ret.resize(size); snprintf(ret.data(), size, format.data(), std::forward(args)...); - return move(ret); + return ret; } template diff --git a/Exceptions.h b/Exceptions.h index 717be92..c4cee1c 100644 --- a/Exceptions.h +++ b/Exceptions.h @@ -40,7 +40,6 @@ #include "MacroConstants.h" #include "ProgramInfo.h" #include "Core.h" -#include "PlatformSpecific.h" namespace cpl { @@ -52,6 +51,13 @@ namespace cpl void CheckPruneExceptionLogFile(); void LogException(const string_ref errorMessage); void CrashIfUserDoesntDebug(const string_ref errorMessage); + + namespace Misc + { + bool IsBeingDebugged(); + void OutputToDebugger(const string_ref message); + }; + bool IsDebuggerAttached(); class CPLRuntimeException : public std::runtime_error @@ -84,7 +90,7 @@ namespace cpl { \ std::string message = std::string("Runtime exception (" #exceptionT ") in ") + ::cpl::programInfo.name + " (" + ::cpl::programInfo.version.toString() + "): \"" + msg + "\" in " + file + ":" + ::std::to_string(line) + " -> " + funcname; \ auto e = exceptionExpression;\ - CPL_DEBUGOUT((message + "\n").c_str()); \ + cpl::Misc::OutputToDebugger(message + "\n"); \ cpl::LogException(message); \ if(cpl::IsDebuggerAttached()) DBG_BREAK(); \ bool doAbort = isassert; \ diff --git a/JobSystem.h b/JobSystem.h index dec2d18..718bda8 100644 --- a/JobSystem.h +++ b/JobSystem.h @@ -208,7 +208,7 @@ namespace cpl for (std::size_t i = 0; i < workers; ++i) { - threads.emplace_back(&JobSystem::entry, this, i); + threads.emplace_back(&JobSystem::entry, this, static_cast(i)); } cv.notify_all(); diff --git a/LibraryOptions.h b/LibraryOptions.h index c436403..58d7772 100755 --- a/LibraryOptions.h +++ b/LibraryOptions.h @@ -49,7 +49,7 @@ #define JUCE_ENABLE_LIVE_CONSTANT_EDITOR 1 #endif //#define CPL_THROW_ON_NO_RESOURCE -#define CPL_TRACEGUARD_ENTRYPOINTS + #ifdef CPL_WINDOWS #define CPL_MINIMUM_WINDOWS_SUPPORT NTDDI_WINXP #else diff --git a/MacSupport.mm b/MacSupport.mm index 64c4951..f8ff512 100755 --- a/MacSupport.mm +++ b/MacSupport.mm @@ -27,23 +27,18 @@ *************************************************************************************/ -// i dont know why. i seriously dont. -// but all hell breaks loose if this is not here. -//#define Point CarbonDummyPointName - +// Include JUCE headers first to avoid OpenGL conflicts #import #import #import #import #import #import +#include -#include "MacSupport.h" -#include "Misc.h" +#include "PlatformMisc.h" #include "Exceptions.h" -#include -#include "Common.h" /********************************************************************************************* Spawns a messagebox using NSRunAlertPanel diff --git a/MacroConstants.h b/MacroConstants.h index af887aa..65a2061 100755 --- a/MacroConstants.h +++ b/MacroConstants.h @@ -29,7 +29,7 @@ *************************************************************************************/ -#ifndef CPL_MACROCONSTANTS_H +#if defined(__cplusplus) && !defined(CPL_MACROCONSTANTS_H) #define CPL_MACROCONSTANTS_H #include @@ -43,12 +43,21 @@ #endif #endif - #if defined(_WIN64) || defined(__x86_64__) || defined(__x86_64) + #if defined(__ARM_ARCH) || defined(__aarch64__) || defined(__arm64__) || defined(__arm__) + #define CPL_ARCH "ARM" + #define CPL_M_ARM 1 + #else + #define CPL_ARCH "x86" + #define CPL_M_X86 1 + #endif + + #if defined(_WIN64) || defined(__x86_64__) || defined(__aarch64__) || defined(__arm64__) + typedef std::uint64_t XWORD; #define CPL_M_64BIT 1 #define CPL_M_64BIT_ CPL_M_64BIT - #define CPL_ARCH_STRING "64-bit" + #define CPL_ARCH_STRING "64-bit" CPL_ARCH #else #define __M_32BIT_ #define CPL_M_32BIT 1 @@ -56,7 +65,7 @@ #define CPL_M_32BIT_ CPL_M_32BIT typedef std::uint32_t XWORD; - #define CPL_ARCH_STRING "32-bit" + #define CPL_ARCH_STRING "32-bit" CPL_ARCH #endif #if defined(_WIN32) || defined (_WIN64) @@ -64,7 +73,7 @@ #define CPL_WINDOWS #define CPL_PROG_EXTENSION ".dll" #define CPL_DIR_SEP '\\' - #elif defined (__MACH__) && (__APPLE__) + #elif defined (__MACH__) || (__APPLE__) #define CPL_DIR_SEP '/' #define CPL_MAC #include @@ -99,7 +108,10 @@ // gcc or msvc assembly syntax? #ifdef _MSC_VER #define CPL_INTEL_ASSEMBLY - #define DBG_BREAK() DebugBreak(); + #define DBG_BREAK() __debugbreak(); + #elif defined(__aarch64__) || defined(__arm64__) + #define CPL_ATT_ASSEMBLY + #define DBG_BREAK() __builtin_debugtrap() #else #define CPL_ATT_ASSEMBLY #define DBG_BREAK() __asm__("int $0x3") @@ -129,6 +141,7 @@ #define CPL_NOEXCEPT_IF_RELEASE #else #define CPL_NOEXCEPT_IF_RELEASE noexcept + #define CPL_RELEASE #endif /* @@ -216,6 +229,8 @@ #elif defined(__llvm__) && defined(__clang__) + //#define CPL_COMPILER_MULTIPLE_STATICS_SUPPORTED + // cross-platform size_t specifier for printf-families #define CPL_FMT_SZT "%zu" @@ -254,36 +269,39 @@ #define CPL_VECTOR_TARGET #endif #define CPL_COMPILER_SUPPORTS_AVX + + // Enable inclusion of all simd headers. + #ifndef __SSE__ + #define __SSE__ + #endif + #ifndef __SSE2__ + #define __SSE2__ + #endif + #ifndef __SSE3__ + #define __SSE3__ + #endif + #ifndef __SSSE3__ + #define __SSSE3__ + #endif + #ifndef __SSE4_2__ + #define __SSE4_2__ + #endif + #ifndef __SSE4_1__ + #define __SSE4_1__ + #endif + #ifndef __AVX__ + #define __AVX__ + #endif + #ifndef __AVX2__ + #define __AVX2__ + #endif + #else #define CPL_VECTOR_TARGET - #warning "Your compiler is out of date. Support for AVX codepaths is partially disabled." - #endif - - // Enable inclusion of all simd headers. - #ifndef __SSE__ - #define __SSE__ - #endif - #ifndef __SSE2__ - #define __SSE2__ - #endif - #ifndef __SSE3__ - #define __SSE3__ - #endif - #ifndef __SSSE3__ - #define __SSSE3__ - #endif - #ifndef __SSE4_2__ - #define __SSE4_2__ - #endif - #ifndef __SSE4_1__ - #define __SSE4_1__ - #endif - #ifndef __AVX__ - #define __AVX__ - #endif - #ifndef __AVX2__ - #define __AVX2__ - #endif + #ifndef CPL_M_ARM + #warning "Your compiler is out of date. Support for AVX codepaths is partially disabled." + #endif + #endif #define cwarn(exp) ("warning: " exp) diff --git a/Mathext.h b/Mathext.h index ea9f48b..ebb6ab3 100755 --- a/Mathext.h +++ b/Mathext.h @@ -70,9 +70,10 @@ namespace cpl CBoxFilter() : buf(), ptr(0) {}; - void setNext(scalar input) + template + void setNext(TInput input) { - buf[ptr] = input; + buf[ptr] = static_cast(input); ptr++; ptr %= size; } @@ -569,19 +570,19 @@ namespace cpl } // floors to next integer, down to zero infinity - template - inline typename std::enable_if::value, T2>::type - floorToNInf(T input) + template + inline typename std::enable_if::value, TOut>::type + floorToNInf(TIn input) { - return static_cast(std::floor(input)); + return static_cast(std::floor(input)); } // this is a no-op for integers - template - inline typename std::enable_if::value, T2>::type - floorToNInf(T input) + template + inline typename std::enable_if::value, TOut>::type + floorToNInf(TIn input) { - return static_cast(input); + return static_cast(input); } diff --git a/Misc.cpp b/Misc.cpp index 5cd7fc2..68c5a2c 100755 --- a/Misc.cpp +++ b/Misc.cpp @@ -28,6 +28,7 @@ *************************************************************************************/ #include "MacroConstants.h" +#include "PlatformMisc.h" #include "Misc.h" #include #include "PlatformSpecific.h" @@ -62,7 +63,6 @@ namespace cpl int addHandlers(); static std::string GetDirectoryPath(); - static int GetInstanceCounter(); static int __unusedInitialization = addHandlers(); static std::atomic oldTerminate; @@ -368,6 +368,11 @@ namespace cpl return false; } + void OutputToDebugger(const string_ref message) + { + CPL_DEBUGOUT(message.data()); + } + const char * GetImageBase() { @@ -411,20 +416,26 @@ namespace cpl *********************************************************************************************/ #ifndef CPL_MSVC - #ifdef CPL_M_64BIT_ - __inline__ uint64_t __rdtsc() { - uint64_t a, d; - __asm__ volatile ("rdtsc" : "=a" (a), "=d" (d)); - return (d << 32) | a; - } - #else __inline__ uint64_t __rdtsc() { - uint64_t x; - __asm__ volatile ("rdtsc" : "=A" (x)); - return x; + #if defined(__x86_64__) || defined(__i386__) + #ifdef CPL_M_64BIT_ + uint64_t a, d; + __asm__ volatile ("rdtsc" : "=a" (a), "=d" (d)); + return (d << 32) | a; + #else + uint64_t x; + __asm__ volatile ("rdtsc" : "=A" (x)); + return x; + #endif + #elif defined(__aarch64__) || defined(__arm64__) + // ARM64: Use the system counter + uint64_t val; + __asm__ volatile("mrs %0, cntvct_el0" : "=r" (val)); + return val; + #else + #error "Implement rdtsc for your platform" + #endif } - #endif - #endif /********************************************************************************************* @@ -447,6 +458,8 @@ namespace cpl #ifdef _WINDOWS_ ::QueryPerformanceCounter((LARGE_INTEGER*)&t); + #elif defined(CPL_MAC) && defined(CPL_JUCE) + t = juce::Time::getHighResolutionTicks(); #elif defined(CPL_MAC) auto t1 = mach_absolute_time(); *(decltype(t1)*)&t = t1; @@ -458,43 +471,56 @@ namespace cpl return t; } + double TimeDifference(long long time) { return TimeToMilisecs(TimeCounter() - time); } + + double TimeDifferenceSeconds(long long time) + { + return TimeToSeconds(TimeCounter() - time); + } - double TimeToMilisecs(long long time) - { - double ret = 0.0; + double TimeToSeconds(long long time) + { + double ret = 0.0; - #ifdef _WINDOWS_ - long long f; + #ifdef _WINDOWS_ + long long f; - ::QueryPerformanceFrequency((LARGE_INTEGER*)&f); + ::QueryPerformanceFrequency((LARGE_INTEGER*)&f); - //long long t = TimeCounter(); - ret = (time) * (1000.0 / f); - #elif defined(CPL_MAC) - auto t1 = *(decltype(mach_absolute_time())*)&time; + //long long t = TimeCounter(); + ret = (time) * (1.0 / f); + #elif defined(CPL_MAC) && defined(CPL_JUCE) + return time / (double)juce::Time::getHighResolutionTicksPerSecond(); + #elif defined(CPL_MAC) + auto t1 = *(decltype(mach_absolute_time())*)&time; - struct mach_timebase_info tinfo; - if (mach_timebase_info(&tinfo) == KERN_SUCCESS) - { - double hTime2nsFactor = (double)tinfo.numer / tinfo.denom; - ret = (((t1)* hTime2nsFactor) / 1000.0) / 1000.0; - } + struct mach_timebase_info tinfo; + if (mach_timebase_info(&tinfo) == KERN_SUCCESS) + { + double hTime2sFactor = tinfo.numer / (tinfo.denom * 1000.0 * 1000.0 * 1000.0); + ret = (((t1)* hTime2sFactor)); + } - #elif defined(__CPP11__) - using namespace std::chrono; + #elif defined(__CPP11__) + using namespace std::chrono; - high_resolution_clock::rep t1; - t1 = *(high_resolution_clock::rep *)&time; - milliseconds elapsed(t1); - ret = elapsed.count(); - #endif + high_resolution_clock::rep t1; + t1 = *(high_resolution_clock::rep *)&time; + seconds elapsed(t1); + ret = elapsed.count(); + #endif + + return ret; - return ret; + } + double TimeToMilisecs(long long time) + { + return TimeToSeconds(time) * 1000; } /********************************************************************************************* @@ -516,13 +542,7 @@ namespace cpl ctime = gmtime(&timeObj); #endif char buffer[100]; - // not cross platform. - #ifdef CPL_MSVC - - sprintf_s(buffer, "%01d:%01d:%01d", ctime->tm_hour, ctime->tm_min, ctime->tm_sec); - #else - sprintf(buffer, "%01d:%01d:%01d", ctime->tm_hour, ctime->tm_min, ctime->tm_sec); - #endif + cpl::sprintfs(buffer, "%01d:%01d:%01d", ctime->tm_hour, ctime->tm_min, ctime->tm_sec); return buffer; } @@ -540,15 +560,83 @@ namespace cpl ctime = gmtime(&timeObj); #endif char buffer[100]; - // not cross platform. - #ifdef CPL_MSVC - sprintf_s(buffer, "%d/%d/%d", ctime->tm_mday, ctime->tm_mon + 1, ctime->tm_year + 1900); - #else - sprintf(buffer, "%d/%d/%d", ctime->tm_mday, ctime->tm_mon + 1, ctime->tm_year + 1900); - #endif + cpl::sprintfs(buffer, "%d/%d/%d", ctime->tm_mday, ctime->tm_mon + 1, ctime->tm_year + 1900); return buffer; } + void _internalAlignedFree(void* obj) + { + #ifdef CPL_WINDOWS + _aligned_free(obj); + #else + if (obj) + { + free(((void**)obj)[-1]); + } + #endif + } + + void* alignedBytesMalloc(std::size_t size, std::size_t alignment) + { + void * ptr = nullptr; + #ifdef CPL_WINDOWS + ptr = _aligned_malloc(size, alignment); + #else + // : http://stackoverflow.com/questions/196329/osx-lacks-memalign + void *mem = malloc(size + (alignment - 1) + sizeof(void*)); + + char *amem = ((char*)mem) + sizeof(void*); + amem += ((alignment - ((uintptr_t)amem & (alignment - 1))) & (alignment - 1)); + + ((void**)amem)[-1] = mem; + ptr = amem;; + #endif + + return ptr; + } + + void* _internalAlignedRealloc(void* ptr, std::size_t elementSize, std::size_t numObjects, std::size_t alignment) + { + #ifdef CPL_WINDOWS + return _aligned_realloc(ptr, numObjects * elementSize, alignment); + #else + #ifdef CPL_MAC + // all allocations on OS X are aligned to 16-byte boundaries + // NOTE: removed, as alignedFree doesn't account for this + //if(alignment <= 16) + // return reinterpret_cast(std::realloc(ptr, numObjects * sizeof(Type))); + #endif + // https://github.com/numpy/numpy/issues/5312 + void *p1, **p2, *base; + std::size_t + old_offs, + offs = alignment - 1 + sizeof(void*), + n = elementSize * numObjects; + + if (ptr != nullptr) + { + base = *(((void**)ptr) - 1); + if ((p1 = std::realloc(base, n + offs)) == nullptr) + return nullptr; + if (p1 == base) + return ptr; + p2 = (void**)(((std::uintptr_t)(p1) + offs) & ~(alignment - 1)); + old_offs = (size_t)((std::uintptr_t)ptr - (std::uintptr_t)base); + std::memmove(p2, (char*)p1 + old_offs, n); + } + else + { + if ((p1 = std::malloc(n + offs)) == nullptr) + return nullptr; + p2 = (void**)(((std::uintptr_t)(p1) + offs) & ~(alignment - 1)); + } + *(p2 - 1) = p1; + return p2; + + #endif + } + + /********************************************************************************************* 'private' function, initializes the global DirectoryPath diff --git a/Misc.h b/Misc.h index 9fb2756..440b007 100755 --- a/Misc.h +++ b/Misc.h @@ -39,10 +39,10 @@ #include #include "Common.h" -#include "PlatformSpecific.h" #include "Types.h" #include "Core.h" #include "filesystem.h" +#include "Exceptions.h" namespace cpl { @@ -100,7 +100,6 @@ namespace cpl /// Releases a ID previously acquired. /// void ReleaseUniqueInstanceID(std::int32_t ID); - bool IsBeingDebugged(); /// /// Returns a pointer to the base of the current image (DLL/DYLIB/SO) @@ -127,7 +126,9 @@ namespace cpl std::uint64_t ClockCounter(); long long TimeCounter(); double TimeDifference(long long); + double TimeDifferenceSeconds(long long); double TimeToMilisecs(long long); + double TimeToSeconds(long long); /// /// Consumes any key from the console, without requiring enter to be hit. @@ -138,6 +139,10 @@ namespace cpl /// bool PromptAnyKey(); + void _internalAlignedFree(void*); + void* _internalAlignedRealloc(void* ptr, std::size_t elementSize, std::size_t numObjects, std::size_t alignment); + void* alignedBytesMalloc(std::size_t size, std::size_t alignment); + /// /// Returns uninitialized memory aligned to alignment boundary. /// Has same behaviour as std::malloc @@ -145,30 +150,7 @@ namespace cpl template Type * alignedMalloc(std::size_t numObjects) { - void * ptr = nullptr; - std::size_t size = sizeof(Type) * numObjects; - #ifdef CPL_WINDOWS - ptr = _aligned_malloc(size, alignment); - #else - // : http://stackoverflow.com/questions/196329/osx-lacks-memalign - - #ifdef CPL_MAC - // all allocations on OS X are aligned to 16-byte boundaries - // NOTE: removed, as alignedFree doesn't account for this - // if(alignment <= 16) - // return reinterpret_cast(std::malloc(numObjects * sizeof(Type))); - #endif - - void *mem = malloc(size + (alignment - 1) + sizeof(void*)); - - char *amem = ((char*)mem) + sizeof(void*); - amem += (alignment - ((uintptr_t)amem & (alignment - 1)) & (alignment - 1)); - - ((void**)amem)[-1] = mem; - ptr = amem; - #endif - - return reinterpret_cast(ptr); + return reinterpret_cast(alignedBytesMalloc(sizeof(Type) * numObjects, alignment)); } /// @@ -180,253 +162,15 @@ namespace cpl inline Type * alignedRealloc(Type * ptr, std::size_t numObjects) { static_assert(std::is_trivially_copyable::value, "Reallocations may need to trivially copy buffer"); - #ifdef CPL_WINDOWS - return reinterpret_cast(_aligned_realloc(ptr, numObjects * sizeof(Type), alignment)); - #else - #ifdef CPL_MAC - // all allocations on OS X are aligned to 16-byte boundaries - // NOTE: removed, as alignedFree doesn't account for this - //if(alignment <= 16) - // return reinterpret_cast(std::realloc(ptr, numObjects * sizeof(Type))); - #endif - // https://github.com/numpy/numpy/issues/5312 - void *p1, **p2, *base; - std::size_t - old_offs, - offs = alignment - 1 + sizeof(void*), - n = sizeof(Type) * numObjects; - - if (ptr != nullptr) - { - base = *(((void**)ptr) - 1); - if ((p1 = std::realloc(base, n + offs)) == nullptr) - return nullptr; - if (p1 == base) - return ptr; - p2 = (void**)(((std::uintptr_t)(p1) + offs) & ~(alignment - 1)); - old_offs = (size_t)((std::uintptr_t)ptr - (std::uintptr_t)base); - std::memmove(p2, (char*)p1 + old_offs, n); - } - else - { - if ((p1 = std::malloc(n + offs)) == nullptr) - return nullptr; - p2 = (void**)(((std::uintptr_t)(p1) + offs) & ~(alignment - 1)); - } - *(p2 - 1) = p1; - return reinterpret_cast(p2); - - #endif - } - - /// - /// Returns uninitialized memory aligned to alignment boundary. - /// Has same behaviour as std::malloc - /// - inline void * alignedBytesMalloc(std::size_t size, std::size_t alignment) - { - - void * ptr = nullptr; - #ifdef CPL_WINDOWS - ptr = _aligned_malloc(size, alignment); - #else - // : http://stackoverflow.com/questions/196329/osx-lacks-memalign - void *mem = malloc(size + (alignment - 1) + sizeof(void*)); - - char *amem = ((char*)mem) + sizeof(void*); - amem += ((alignment - ((uintptr_t)amem & (alignment - 1))) & (alignment - 1)); - - ((void**)amem)[-1] = mem; - ptr = amem;; - #endif - - return reinterpret_cast(ptr); + return reinterpret_cast(_internalAlignedRealloc(ptr, sizeof(Type), numObjects, alignment)); } template void alignedFree(Type & obj) { - #ifdef CPL_WINDOWS - _aligned_free(obj); - #else - if (obj) - { - free(((void**)obj)[-1]); - } - #endif + _internalAlignedFree(obj); obj = nullptr; } - - enum MsgButton : int - { - #ifdef CPL_WINDOWS - bYes = IDYES, - bNo = IDNO, - bRetry = IDRETRY, - bTryAgain = IDTRYAGAIN, - bContinue = IDCONTINUE, - bCancel = IDCANCEL, - bError = -1 - #elif defined(CPL_MAC) - bError = -1, - bYes = 6, - bNo = 7, - bRetry = 4, - bTryAgain = 10, - bContinue = 11, - bCancel = 2, - bOk = bYes - #else - bError = -1, - bYes = 1, - bNo = 2, - bRetry = 3, - bTryAgain = 4, - bContinue = 5, - bCancel = 6, - bOk = bYes - #endif - }; - enum MsgStyle : int - { - #ifdef CPL_WINDOWS - sOk = MB_OK, - sYesNo = MB_YESNO, - sYesNoCancel = MB_YESNOCANCEL, - sConTryCancel = MB_CANCELTRYCONTINUE - #elif defined(CPL_MAC) - sOk = 0, - sYesNo = 3, - sYesNoCancel = 6, - sConTryCancel = 9 - #else - sOk = 0, - sYesNo = 1, - sYesNoCancel = 2, - sConTryCancel = 3 - #endif - }; - enum MsgIcon : int - { - #ifdef CPL_WINDOWS - iStop = MB_ICONSTOP, - iQuestion = MB_ICONQUESTION, - iInfo = MB_ICONINFORMATION, - iWarning = MB_ICONWARNING - #elif defined(APE_IPLUG) - iStop = MB_ICONSTOP, - iQuestion = MB_ICONINFORMATION, - iInfo = MB_ICONINFORMATION, - iWarning = MB_ICONSTOP - #elif defined(CPL_MAC) - iStop = 0x10, - iWarning = iStop, - iInfo = 0x40, - iQuestion = 0x20 - #else - iInfo = 0 << 8, - iWarning = 1 << 8, - iStop = 2 << 8, - iQuestion = 3 << 8 - #endif - }; - - int MsgBox(const string_ref text, - const string_ref title = "", - int nStyle = MsgStyle::sOk, - void * parent = NULL, - const bool bBlocking = true); - - - /// - /// Waits on some boolean flag to become false. Be careful with using this in case of deadlocks. - /// - /// - /// - /// - template - bool SpinLock(unsigned int ms, T & bVal) { - unsigned start; - int ret; - loop: - start = QuickTime(); - while (!!bVal) { - if ((QuickTime() - start) > ms) - goto time_out; - Delay(0); - } - // normal exitpoint - return true; - // deadlock occurs - - time_out: - // TODO: refactor to separate file.. so we access the name of the program without cyclic dependencies. - ret = MsgBox("Deadlock detected in spinlock: Protected resource is not released after max interval. " - "Wait again (try again), release resource (continue) - can create async issues - or exit (cancel)?", - "cpl sync error!", - sConTryCancel | iStop); - switch (ret) - { - case MsgButton::bTryAgain: - goto loop; - case MsgButton::bContinue: - bVal = !bVal; // flipping val, and it's a reference so should release resource. - return false; - case MsgButton::bCancel: - //TODO: Maybe find a better way to exit? - exit(-1); - } - // not needed (except for warns) - return false; - } - - /// - /// Waits on the functor to return true for at least ms miliseconds. - /// If condition has not returned true yet, it prompts the user to - /// either continue anyway, wait some more, or exit the application. - /// - /// How many miliseconds to wait - /// Functor returning true if condition is met - /// Optional delay between invocations (0, default, will yield the thread) - /// True if functor returned true, false if user chose to continue anyway. - template - bool WaitOnCondition(unsigned int ms, Condition cond, unsigned int delay = 0) - { - unsigned start; - int ret; - loop: - start = QuickTime(); - while (!cond()) { - if ((QuickTime() - start) > ms) - goto time_out; - Delay(delay); - } - // normal exitpoint - return true; - // deadlock occurs - - time_out: - if (!presentUserOption) - { - return false; - } - ret = MsgBox("Deadlock detected in conditional wait: Protected resource is not released after max interval. " - "Wait again (try again, breaks if debugged), continue anyway (continue) - can create async issues - or exit (cancel)?", - "cpl conditional wait error!", - sConTryCancel | iStop); - switch (ret) - { - case MsgButton::bTryAgain: - CPL_BREAKIFDEBUGGED(); - goto loop; - case MsgButton::bCancel: - // TODO: exit another way? - exit(-1); - } - // not needed (except for warns) - return false; - } - }; // Misc }; // APE #endif diff --git a/PlatformMisc.h b/PlatformMisc.h new file mode 100755 index 0000000..cbc2ddc --- /dev/null +++ b/PlatformMisc.h @@ -0,0 +1,215 @@ +/************************************************************************************* + + cpl - cross-platform library - v. 0.1.0. + + Copyright (C) 2026 Janus Lynggaard Thorborg (www.jthorborg.com) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + See \licenses\ for additional details on licenses associated with this program. + +************************************************************************************** + + file:PlatformMisc.h + + Whatever doesn't fit other places, depending on system headers. + +*************************************************************************************/ + +#ifndef CPL_PLATFORMMISC_H +#define CPL_PLATFORMMISC_H + +#include "PlatformSpecific.h" +#include "lib/string_ref.h" +#include "Exceptions.h" + +namespace cpl +{ + namespace Misc + { + enum MsgButton : int + { + #ifdef CPL_WINDOWS + bYes = IDYES, + bNo = IDNO, + bRetry = IDRETRY, + bTryAgain = IDTRYAGAIN, + bContinue = IDCONTINUE, + bCancel = IDCANCEL, + bError = -1 + #elif defined(CPL_MAC) + bError = -1, + bYes = 6, + bNo = 7, + bRetry = 4, + bTryAgain = 10, + bContinue = 11, + bCancel = 2, + bOk = bYes + #else + bError = -1, + bYes = 1, + bNo = 2, + bRetry = 3, + bTryAgain = 4, + bContinue = 5, + bCancel = 6, + bOk = bYes + #endif + }; + enum MsgStyle : int + { + #ifdef CPL_WINDOWS + sOk = MB_OK, + sYesNo = MB_YESNO, + sYesNoCancel = MB_YESNOCANCEL, + sConTryCancel = MB_CANCELTRYCONTINUE + #elif defined(CPL_MAC) + sOk = 0, + sYesNo = 3, + sYesNoCancel = 6, + sConTryCancel = 9 + #else + sOk = 0, + sYesNo = 1, + sYesNoCancel = 2, + sConTryCancel = 3 + #endif + }; + enum MsgIcon : int + { + #ifdef CPL_WINDOWS + iStop = MB_ICONSTOP, + iQuestion = MB_ICONQUESTION, + iInfo = MB_ICONINFORMATION, + iWarning = MB_ICONWARNING + #elif defined(APE_IPLUG) + iStop = MB_ICONSTOP, + iQuestion = MB_ICONINFORMATION, + iInfo = MB_ICONINFORMATION, + iWarning = MB_ICONSTOP + #elif defined(CPL_MAC) + iStop = 0x10, + iWarning = iStop, + iInfo = 0x40, + iQuestion = 0x20 + #else + iInfo = 0 << 8, + iWarning = 1 << 8, + iStop = 2 << 8, + iQuestion = 3 << 8 + #endif + }; + + int MsgBox(const string_ref text, + const string_ref title = "", + int nStyle = MsgStyle::sOk, + void * parent = NULL, + const bool bBlocking = true); + + long Delay(int ms); + unsigned int QuickTime(); + + /// + /// Waits on some boolean flag to become false. Be careful with using this in case of deadlocks. + /// + /// + /// + /// + template + bool SpinLock(unsigned int ms, T & bVal) { + unsigned start; + int ret; + loop: + start = QuickTime(); + while (!!bVal) { + if ((QuickTime() - start) > ms) + goto time_out; + Delay(0); + } + // normal exitpoint + return true; + // deadlock occurs + + time_out: + // TODO: refactor to separate file.. so we access the name of the program without cyclic dependencies. + ret = MsgBox("Deadlock detected in spinlock: Protected resource is not released after max interval. " + "Wait again (try again), release resource (continue) - can create async issues - or exit (cancel)?", + "cpl sync error!", + sConTryCancel | iStop); + switch (ret) + { + case MsgButton::bTryAgain: + goto loop; + case MsgButton::bContinue: + bVal = !bVal; // flipping val, and it's a reference so should release resource. + return false; + case MsgButton::bCancel: + //TODO: Maybe find a better way to exit? + exit(-1); + } + // not needed (except for warns) + return false; + } + + /// + /// Waits on the functor to return true for at least ms miliseconds. + /// If condition has not returned true yet, it prompts the user to + /// either continue anyway, wait some more, or exit the application. + /// + /// How many miliseconds to wait + /// Functor returning true if condition is met + /// Optional delay between invocations (0, default, will yield the thread) + /// True if functor returned true, false if user chose to continue anyway. + template + bool WaitOnCondition(unsigned int ms, Condition cond, unsigned int delay = 0) + { + unsigned start; + int ret; + loop: + start = QuickTime(); + while (!cond()) { + if ((QuickTime() - start) > ms) + goto time_out; + Delay(delay); + } + // normal exitpoint + return true; + // deadlock occurs + + time_out: + if (!presentUserOption) + { + return false; + } + ret = MsgBox("Deadlock detected in conditional wait: Protected resource is not released after max interval. " + "Wait again (try again, breaks if debugged), continue anyway (continue) - can create async issues - or exit (cancel)?", + "cpl conditional wait error!", + sConTryCancel | iStop); + switch (ret) + { + case MsgButton::bTryAgain: + CPL_BREAKIFDEBUGGED(); + goto loop; + case MsgButton::bCancel: + // TODO: exit another way? + exit(-1); + } + // not needed (except for warns) + return false; + } + + }; // Misc +}; // APE +#endif diff --git a/PlatformSpecific.h b/PlatformSpecific.h index 7151bd9..09601c6 100755 --- a/PlatformSpecific.h +++ b/PlatformSpecific.h @@ -41,25 +41,45 @@ #include #include +#ifdef CPL_JUCE + +#include "../JuceLibraryCode/JuceHeader.h" +#include + +namespace cpl +{ + using namespace juce::gl; +} + +#else + +#include +#include + +#endif + #elif defined(CPL_UNIXC) #include #include #include #include -#include #include #include -#include +#include #include #ifdef CPL_MAC + #include #include #include "MacSupport.h" -#include +#include + +#ifndef CPL_JUCE #include #include +#endif #endif @@ -70,7 +90,12 @@ #ifndef CPL_MSVC #include // find similar header (set fpoint mask) for non-mscv on windows +#ifdef __x86_64__ #include +#elif defined(__aarch64__) || defined(__arm64__) +#define SIMDE_ENABLE_NATIVE_ALIASES +#include "external/simde/simde/x86/sse.h" +#endif #endif -#endif +#endif diff --git a/Process.h b/Process.h index 266fea8..195fa00 100755 --- a/Process.h +++ b/Process.h @@ -106,12 +106,7 @@ #include "process/Env.h" #include "process/ProcessUtil.h" #include -// TODO: Fix to detect GCC C++17 support -#if !__has_include() -#include -#else #include -#endif namespace cpl { diff --git a/ProgramVersion.h b/ProgramVersion.h index 4a72e1b..c3ddc80 100644 --- a/ProgramVersion.h +++ b/ProgramVersion.h @@ -38,6 +38,7 @@ #include #include #include +#include #ifdef _MSC_VER #pragma warning(push) @@ -83,11 +84,12 @@ namespace cpl Version ret; int parts[3]; - std::sscanf(version.c_str(), "%d.%d.%d", &parts[0], &parts[1], &parts[2]); - - ret.parts.major = (std::uint16_t) std::clamp(parts[0], 0, std::numeric_limits::max()); - ret.parts.minor = (std::uint16_t) std::clamp(parts[1], 0, std::numeric_limits::max()); - ret.parts.build = (std::uint32_t) std::clamp(parts[2], 0, std::numeric_limits::max()); + if (std::sscanf(version.c_str(), "%d.%d.%d", &parts[0], &parts[1], &parts[2]) == 3) + { + ret.parts.major = (std::uint16_t)std::clamp(parts[0], 0, std::numeric_limits::max()); + ret.parts.minor = (std::uint16_t)std::clamp(parts[1], 0, std::numeric_limits::max()); + ret.parts.build = (std::uint32_t)std::clamp(parts[2], 0, std::numeric_limits::max()); + } return ret; } diff --git a/Protected.cpp b/Protected.cpp index 1b042b3..5d3e541 100644 --- a/Protected.cpp +++ b/Protected.cpp @@ -28,6 +28,7 @@ *************************************************************************************/ #include "Protected.h" +#include "PlatformSpecific.h" #include "lib/StackBuffer.h" #include #include @@ -86,6 +87,56 @@ namespace cpl return buf; } +#ifndef CPL_WINDOWS + XWORD StatusCodeToXWORD(CProtected::CSystemException::Status code) + { + return (XWORD)code; + } +#else + XWORD StatusCodeToXWORD(CProtected::CSystemException::Status code) + { + switch (code) + { + case CProtected::CSystemException::Status::access_violation: return EXCEPTION_ACCESS_VIOLATION; + case CProtected::CSystemException::Status::intdiv_zero: return EXCEPTION_INT_DIVIDE_BY_ZERO; + case CProtected::CSystemException::Status::fdiv_zero: return EXCEPTION_FLT_DIVIDE_BY_ZERO; + case CProtected::CSystemException::Status::finvalid: return EXCEPTION_FLT_INVALID_OPERATION; + case CProtected::CSystemException::Status::fdenormal: return EXCEPTION_FLT_DENORMAL_OPERAND; + case CProtected::CSystemException::Status::finexact: return EXCEPTION_FLT_INEXACT_RESULT; + case CProtected::CSystemException::Status::foverflow: return EXCEPTION_FLT_OVERFLOW; + case CProtected::CSystemException::Status::funderflow: return EXCEPTION_FLT_UNDERFLOW; + case CProtected::CSystemException::Status::intoverflow: return EXCEPTION_INT_OVERFLOW; + case CProtected::CSystemException::Status::undefined_behaviour: return EXCEPTION_ILLEGAL_INSTRUCTION; + } + + CPL_UNREACHABLE(); + + return -1; + } + + CProtected::CSystemException::Status XWORDExceptionToStatusCode(XWORD code) + { + switch (code) + { + case EXCEPTION_ACCESS_VIOLATION: return CProtected::CSystemException::Status::access_violation; + case EXCEPTION_INT_DIVIDE_BY_ZERO: return CProtected::CSystemException::Status::intdiv_zero; + case EXCEPTION_FLT_DIVIDE_BY_ZERO: return CProtected::CSystemException::Status::fdiv_zero; + case EXCEPTION_FLT_INVALID_OPERATION: return CProtected::CSystemException::Status::finvalid; + case EXCEPTION_FLT_DENORMAL_OPERAND: return CProtected::CSystemException::Status::fdenormal; + case EXCEPTION_FLT_INEXACT_RESULT: return CProtected::CSystemException::Status::finexact; + case EXCEPTION_FLT_OVERFLOW: return CProtected::CSystemException::Status::foverflow; + case EXCEPTION_FLT_UNDERFLOW: return CProtected::CSystemException::Status::funderflow; + case EXCEPTION_INT_OVERFLOW: return CProtected::CSystemException::Status::intoverflow; + case EXCEPTION_ILLEGAL_INSTRUCTION: return CProtected::CSystemException::Status::undefined_behaviour; + } + + CPL_UNREACHABLE(); + + return CProtected::CSystemException::Status::access_violation; + } + +#endif + /********************************************************************************************* Formats a string and returns it. It explains what went wrong. @@ -99,7 +150,7 @@ namespace cpl base << "Non-software exception at 0x" << std::hex << e.data.faultAddr << " (at image base " + formatDifferenceAddress(imageBase, e.data.faultAddr) + ")" << newl; - base << "Exception code: " << e.data.exceptCode + base << "Exception code: " << StatusCodeToXWORD(e.data.exceptCode) << ", actual code: " << e.data.actualCode << ", extra info: " << e.data.extraInfoCode << newl; @@ -107,25 +158,25 @@ namespace cpl switch (e.data.exceptCode) { - case CSystemException::status::intdiv_zero: + case CSystemException::Status::intdiv_zero: return base.str() + "An integral division-by-zero was performed"; - case CSystemException::status::funderflow: + case CSystemException::Status::funderflow: return base.str() + "A floating point operation resulted in underflow"; - case CSystemException::status::foverflow: + case CSystemException::Status::foverflow: return base.str() + "A floating point operation resulted in overflow"; - case CSystemException::status::finexact: + case CSystemException::Status::finexact: return base.str() + "A floating point operation's result cannot be accurately expressed"; - case CSystemException::status::finvalid: + case CSystemException::Status::finvalid: return base.str() + "One of the operands for a floating point operation was invalid (typically negative numbers for sqrt, exp, log)"; - case CSystemException::status::fdiv_zero: + case CSystemException::Status::fdiv_zero: return base.str() + "A floating point division-by-zero was performed"; - case CSystemException::status::fdenormal: + case CSystemException::Status::fdenormal: return base.str() + "One of the operands for a floating point operation was denormal (too small to be represented)"; - case CSystemException::status::nullptr_from_plugin: + case CSystemException::Status::nullptr_from_plugin: return base.str() + "An API function was called with 'this' as an null pointer."; - case CSystemException::status::undefined_behaviour: + case CSystemException::Status::undefined_behaviour: return base.str() + "Undefined behaviour was executed (unreachable code inserted by compiler)"; - case CSystemException::status::access_violation: + case CSystemException::Status::access_violation: { std::stringstream fmt; #ifndef CPL_MSVC @@ -242,11 +293,6 @@ namespace cpl exceptionAddress = exp->ExceptionRecord->ExceptionAddress; switch (_code) { - case OSCustomRaiseCode: - { - e = CSystemException::Storage::create(OSCustomRaiseCode, true, exceptionAddress, nullptr, 0xDEAD); - break; - } case EXCEPTION_ILLEGAL_INSTRUCTION: { if (exceptionAddress) @@ -255,7 +301,7 @@ namespace cpl if (*assembly == 0x0B0F) // ud2 - #UD undefined behaviour, compiler trigger. { - e = CSystemException::Storage::create(exceptCode, false, exceptionAddress, nullptr, additionalCode); + e = CSystemException::Storage::create(XWORDExceptionToStatusCode(_code), false, exceptionAddress, nullptr, additionalCode); return EXCEPTION_EXECUTE_HANDLER; } } @@ -273,7 +319,7 @@ namespace cpl additionalCode = static_cast(exp->ExceptionRecord->ExceptionInformation[0]); } - e = CSystemException::Storage::create(exceptCode, safeToContinue, exceptionAddress, (const void *)addr, additionalCode); + e = CSystemException::Storage::create(XWORDExceptionToStatusCode(_code), safeToContinue, exceptionAddress, (const void *)addr, additionalCode); return EXCEPTION_EXECUTE_HANDLER; } @@ -289,7 +335,7 @@ namespace cpl case EXCEPTION_FLT_DENORMAL_OPERAND: _clearfp(); safeToContinue = true; - e = CSystemException::Storage::create(exceptCode, safeToContinue, exceptionAddress); + e = CSystemException::Storage::create(XWORDExceptionToStatusCode(_code), safeToContinue, exceptionAddress); return EXCEPTION_EXECUTE_HANDLER; @@ -373,6 +419,16 @@ namespace cpl } #endif + + void CProtected::CSystemException::reraise() const + { +#ifdef CPL_WINDOWS + RaiseException(static_cast(data.exceptCode), 0, 0, nullptr); +#else + raise(static_cast(data.exceptCode)); +#endif + } + XWORD CProtected::structuredExceptionHandlerTraceInterceptor(CProtected::PreembeddedFormatter & output, XWORD code, CSystemException::Storage & e, void * systemInformation) { @@ -483,7 +539,7 @@ namespace cpl if (*assembly == 0x0B0F) // ud2 - #UD undefined behaviour, compiler trigger. { threadData.currentExceptionData = CSystemException::Storage::create( - CSystemException::undefined_behaviour, + CSystemException::Status::undefined_behaviour, safeToContinue, nullptr, fault_address, @@ -510,7 +566,7 @@ namespace cpl { threadData.currentExceptionData = CSystemException::Storage::create( - CSystemException::access_violation, + CSystemException::Status::access_violation, safeToContinue, nullptr, fault_address, @@ -535,32 +591,32 @@ namespace cpl // exceptions that happened are still set in the status flags - always clear these, // or the exception might throw again std::feclearexcept(FE_ALL_EXCEPT); - CSystemException::status code_status; + CSystemException::Status code_status; switch (ecode) { case FPE_FLTDIV: - code_status = CSystemException::status::fdiv_zero; + code_status = CSystemException::Status::fdiv_zero; break; case FPE_FLTOVF: - code_status = CSystemException::status::foverflow; + code_status = CSystemException::Status::foverflow; break; case FPE_FLTUND: - code_status = CSystemException::status::funderflow; + code_status = CSystemException::Status::funderflow; break; case FPE_FLTRES: - code_status = CSystemException::status::finexact; + code_status = CSystemException::Status::finexact; break; case FPE_FLTINV: - code_status = CSystemException::status::finvalid; + code_status = CSystemException::Status::finvalid; break; case FPE_FLTSUB: - code_status = CSystemException::status::intsubscript; + code_status = CSystemException::Status::intsubscript; break; case FPE_INTDIV: - code_status = CSystemException::status::intdiv_zero; + code_status = CSystemException::Status::intdiv_zero; break; case FPE_INTOVF: - code_status = CSystemException::status::intoverflow; + code_status = CSystemException::Status::intoverflow; break; } diff --git a/Protected.h b/Protected.h index 2b87068..1d69434 100644 --- a/Protected.h +++ b/Protected.h @@ -26,17 +26,20 @@ Defines a wrapper call that catches system-level exceptions through a platform independant interface. - options: - #define CPL_UNSAFE_USE_SIGACTION - uses sigaction() instead of signal() on unix systems. Highly recommended - *************************************************************************************/ #ifndef CPL_PROTECTED_H #define CPL_PROTECTED_H +// Avoid PlatformSpecific.h, as that'll pull in JUCE if available. +#include "MacroConstants.h" +#ifdef CPL_WINDOWS +#include +#else +#include +#endif + #include "LibraryOptions.h" -#include "PlatformSpecific.h" #include #include #include @@ -45,7 +48,6 @@ #include "Utility.h" #include #include "Exceptions.h" -#include "Misc.h" #define CPL_TRACEGUARD_START \ cpl::CProtected::instance().topLevelTraceGuardedCode([&]() { @@ -59,8 +61,6 @@ namespace cpl class CProtected final : Utility::CNoncopyable { - static constexpr int OSCustomRaiseCode = 0xBEEF; - struct Throwable { virtual ~Throwable() {}; @@ -176,7 +176,6 @@ namespace cpl Utility::LazyStackPointer stream; }; - static void useFPUExceptions(bool b); static std::string formatExceptionMessage(const CSystemException &); static CProtected & instance(); @@ -327,17 +326,29 @@ namespace cpl struct CSystemException : public std::exception { public: - // this is kinda ugly, but needed to create a simple interface, abstract to seh and signals - // the retarted create() pattern is used because clang STILL doesn't support thread_local, - // thus we must use __thread, which DOESN'T support non-trivial destruction (implied by use of constructors). - // TODO: fix the upper messages. + enum class Status + { + nullptr_from_plugin = 1, + access_violation = SIGSEGV, + intdiv_zero, + fdiv_zero, + finvalid, + fdenormal, + finexact, + foverflow, + funderflow, + intsubscript, + intoverflow, + undefined_behaviour + }; + struct Storage { const void * faultAddr; // the address the exception occured const void * attemptedAddr; // if exception is a memory violation, this is the attempted address - XWORD exceptCode; // the exception code - int extraInfoCode; // addition, exception-specific code + Status exceptCode; // the exception code + int extraInfoCode; // additional, exception-specific code int actualCode; // what signal it was union { @@ -346,10 +357,10 @@ namespace cpl bool aVInProtectedMemory; // whether an access violation happened in our protected memory }; - static Storage create(XWORD exp, bool resolved = true, const void * faultAddress = nullptr, + static Storage create(Status code, bool resolved = true, const void * faultAddress = nullptr, const void * attemptedAddress = nullptr, int extraCode = 0, int actualCode = 0) { - return {faultAddress, attemptedAddress, exp, extraCode, actualCode, resolved}; + return {faultAddress, attemptedAddress, code, extraCode, actualCode, resolved}; } static Storage create() @@ -358,36 +369,8 @@ namespace cpl } } data; - enum status : XWORD { - nullptr_from_plugin = 1, - #ifdef CPL_WINDOWS - // this is not good: these are not crossplatform constants. - access_violation = EXCEPTION_ACCESS_VIOLATION, - undefined_behaviour = EXCEPTION_ILLEGAL_INSTRUCTION, - intdiv_zero = EXCEPTION_INT_DIVIDE_BY_ZERO, - fdiv_zero = EXCEPTION_FLT_DIVIDE_BY_ZERO, - finvalid = EXCEPTION_FLT_INVALID_OPERATION, - fdenormal = EXCEPTION_FLT_DENORMAL_OPERAND, - finexact = EXCEPTION_FLT_INEXACT_RESULT, - foverflow = EXCEPTION_FLT_OVERFLOW, - funderflow = EXCEPTION_FLT_UNDERFLOW - #elif defined(CPL_MAC) || defined(CPL_UNIXC) - access_violation = SIGSEGV, - intdiv_zero, - fdiv_zero, - finvalid, - fdenormal, - finexact, - foverflow, - funderflow, - intsubscript, - intoverflow, - undefined_behaviour - - #endif - }; - CSystemException() + : data(Storage::create()) { } @@ -397,18 +380,11 @@ namespace cpl data = eData; } - void reraise() const - { - #ifdef CPL_WINDOWS - RaiseException(static_cast(data.exceptCode), 0, 0, nullptr); - #else - raise(data.exceptCode); - #endif - } + void reraise() const; - CSystemException(XWORD exp, bool resolved = true, const void * faultAddress = nullptr, const void * attemptedAddress = nullptr, int extraCode = 0, int actualCode = 0) + CSystemException(Status code, bool resolved = true, const void * faultAddress = nullptr, const void * attemptedAddress = nullptr, int extraCode = 0, int actualCode = 0) { - data = Storage::create(exp, resolved, faultAddress, attemptedAddress, extraCode, actualCode); + data = Storage::create(code, resolved, faultAddress, attemptedAddress, extraCode, actualCode); } const char * what() const noexcept override @@ -426,7 +402,7 @@ namespace cpl else { threadData.pendingException.reset(new ThrowableException(Exception(args...))); - siglongjmp(threadData.threadJumpBuffer, OSCustomRaiseCode); + siglongjmp(threadData.threadJumpBuffer, 0xBEEF); } #else throw Exception(args...); diff --git a/Resources.cpp b/Resources.cpp index 3a4afd7..b1b4ed1 100644 --- a/Resources.cpp +++ b/Resources.cpp @@ -29,6 +29,7 @@ #include "Resources.h" #include "Misc.h" +#include "PlatformMisc.h" namespace cpl { @@ -63,18 +64,16 @@ namespace cpl // handle scalable vector graphics if (f.getFileExtension() == ".svg") { - internalImage = juce::Image::null; - juce::ScopedPointer element = juce::XmlDocument::parse(f); - if (element.get()) + internalImage = {}; + + if (auto element = juce::XmlDocument::parse(f); element) { drawableImage = juce::Drawable::createFromSVG(*element); return true; } else { - auto drawable = new juce::DrawableImage(); - drawable->setImage(juce::Image::null); - drawableImage = drawable; + drawableImage.reset(new juce::DrawableImage()); } } else @@ -82,18 +81,13 @@ namespace cpl internalImage = juce::ImageFileFormat::loadFrom(f); if (internalImage.isValid()) { - auto drawable = new juce::DrawableImage(); - drawable->setImage(internalImage); - drawableImage = drawable; + drawableImage.reset(new juce::DrawableImage(internalImage)); return true; } else { // set a default image? - - auto drawable = new juce::DrawableImage(); - drawable->setImage(juce::Image::null); - drawableImage = drawable; + drawableImage.reset(new juce::DrawableImage()); } } return false; @@ -126,19 +120,18 @@ namespace cpl { return &it->second; } - - + std::string dir = Misc::DirectoryPath() + "/resources/"; - std::string key { name }; auto & image = resources[key]; std::string path = (dir + key); image.setPath(path); + if (!image.load()) { Misc::MsgBox( - "Error loading resource " + path + ":" + newl + GetLastOSErrorMessage() + newl + + "Error loading resource " + path + ":\n" + GetLastOSErrorMessage() + "\n" + "Perhaps you didn't include the folder the plugin arrived in?", programInfo.name + " error!", Misc::MsgIcon::iStop diff --git a/Resources.h b/Resources.h index 341ac8f..ab2e564 100644 --- a/Resources.h +++ b/Resources.h @@ -51,7 +51,7 @@ namespace cpl { std::string path; juce::Image internalImage; - juce::ScopedPointer drawableImage; + std::unique_ptr drawableImage; public: CImage(std::string inPath); @@ -153,7 +153,7 @@ namespace cpl resource->drawWithin(g, size.withPosition(0, 0).toFloat(), juce::RectanglePlacement::centred, opacity); return image; } - return juce::Image::null; + return {}; } juce::Image & getImage() { return image; } diff --git a/Types.h b/Types.h index f93c388..bb2ed42 100755 --- a/Types.h +++ b/Types.h @@ -30,15 +30,28 @@ #ifndef CPL_TYPES_H #define CPL_TYPES_H +#include "MacroConstants.h" #include #include #include #include -#include "PlatformSpecific.h" + +#ifdef CPL_M_X86 #include #include #include -#include +#else +// Use SIMDE for cross-platform SIMD compatibility +// Enable native type aliases for SIMDE on ARM64 +#define SIMDE_ENABLE_NATIVE_ALIASES +#include "external/simde/simde/x86/mmx.h" +#include "external/simde/simde/x86/sse.h" +#include "external/simde/simde/x86/sse2.h" +#include "external/simde/simde/x86/avx.h" +#include "external/simde/simde/x86/avx2.h" +#include "external/simde/simde/x86/fma.h" + +#endif namespace cpl { @@ -77,7 +90,7 @@ namespace cpl typedef __m256i v256si; #ifdef CPL_WINDOWS - typedef DWORD OSError; + typedef std::uint32_t OSError; // DWORD #else typedef int OSError; #endif diff --git a/Utility.h b/Utility.h index 3c0599d..ddd11f6 100755 --- a/Utility.h +++ b/Utility.h @@ -220,16 +220,11 @@ namespace cpl move constructor - c++11 delete ? */ private: - #ifdef __CPP11__ CNoncopyable(const CNoncopyable & other) = delete; CNoncopyable & operator=(const CNoncopyable & other) = delete; CNoncopyable & operator=(CNoncopyable && other) = delete; CNoncopyable(CNoncopyable && other) = delete; - #else - CNoncopyable(const CNoncopyable & other); - CNoncopyable & operator=(const CNoncopyable & other); - #endif }; class CPubliclyNoncopyable @@ -238,16 +233,11 @@ namespace cpl protected: CPubliclyNoncopyable() {} ~CPubliclyNoncopyable() {} - #ifdef __CPP11__ CPubliclyNoncopyable(const CPubliclyNoncopyable & other) = default; CPubliclyNoncopyable & operator=(const CPubliclyNoncopyable & other) = default; CPubliclyNoncopyable & operator=(CPubliclyNoncopyable && other) = default; CPubliclyNoncopyable(CPubliclyNoncopyable && other) = default; - #else - CPubliclyNoncopyable(const CPubliclyNoncopyable & other); - CPubliclyNoncopyable & operator=(const CPubliclyNoncopyable & other); - #endif }; class COnlyPubliclyMovable @@ -256,15 +246,14 @@ namespace cpl protected: COnlyPubliclyMovable() {} ~COnlyPubliclyMovable() {} - #ifdef __CPP11__ + COnlyPubliclyMovable(const COnlyPubliclyMovable & other) = default; COnlyPubliclyMovable & operator=(const COnlyPubliclyMovable & other) = default; - #else - COnlyPubliclyMovable(const COnlyPubliclyMovable & other); - COnlyPubliclyMovable & operator=(const COnlyPubliclyMovable & other); - #endif }; +#ifdef _MSC_VER +#pragma warning(disable:26495) +#endif template struct LazyStackPointer : CNoncopyable @@ -316,6 +305,9 @@ namespace cpl typename std::aligned_storage::type storage; }; +#ifdef _MSC_VER +#pragma warning(default:26495) +#endif template struct OnScopeExit diff --git a/dsp.h b/dsp.h index 5183fc1..1795f25 100755 --- a/dsp.h +++ b/dsp.h @@ -64,8 +64,8 @@ namespace cpl DualComplex ret; - ret.val[0] = std::complex((x1 + x2) * 0.5, (y1 - y2) * 0.5); - ret.val[1] = std::complex((y1 + y2) * 0.5, -(x1 - x2) * 0.5); + ret.val[0] = std::complex((x1 + x2) * Ty(0.5), (y1 - y2) * Ty(0.5)); + ret.val[1] = std::complex((y1 + y2) * Ty(0.5), -(x1 - x2) * Ty(0.5)); return ret; } @@ -232,52 +232,42 @@ namespace cpl typename std::enable_if::type lzresponse(double x, int size) { - return x ? (size * sin(M_PI * x) * sin(M_PI * x / size)) / (M_PI * M_PI * x * x) : 1; + return x ? (size * sin(simd::consts::pi * x) * sin(simd::consts::pi * x / size)) / (simd::consts::pi_squared * x * x) : 1; } template typename std::enable_if::type lzresponse(float x, int size) { - return x ? (size * sinf(M_PI * x) * sinf(M_PI * x / size)) / (M_PI * M_PI * x * x) : 1; + return x ? (size * sinf(simd::consts::pi * x) * sinf(simd::consts::pi * x / size)) / (simd::consts::pi_squared * x * x) : 1; } template typename std::enable_if::type scresponse(double x) { - return x ? (sin(M_PI * x)) / (M_PI * x) : 1; + return x ? (sin(simd::consts::pi * x)) / (simd::consts::pi * x) : 1; + } + + template + typename std::enable_if::type + scresponse(float x) + { + return x ? (std::sin(simd::consts::pi * x)) / (simd::consts::pi * x) : 1; } template typename std::enable_if::type lzresponse(double x, int size) { - return x ? (size * cpl::Math::fastsine(M_PI * x) * cpl::Math::fastsine(M_PI * x / size)) / (TAU * x * x) : 1; + return x ? (size * cpl::Math::fastsine(simd::consts::pi * x) * cpl::Math::fastsine(simd::consts::pi * x / size)) / (simd::consts::pi_squared * x * x) : 1; } template typename std::enable_if::type lzresponse(float x, int size) { - return x ? (size * cpl::Math::fastsine(M_PI * x) * cpl::Math::fastsine(M_PI * x / size)) / (TAU * x * x) : 1; - } - - template - auto lfilter(T & vec, std::size_t asize, double x, signed int wsize) -> typename std::remove_reference::type - { - R resonance = 0; - signed start = static_cast(floor(x)); - for (signed int i = start - wsize + 1; i < start + wsize; ++i) - { - if (i >= 0 && i < asize) - { - auto impulse = vec[i]; - auto response = lzresponse(x - i, wsize); - resonance += impulse * response; - } - } - return resonance; + return x ? (size * cpl::Math::fastsine(simd::consts::pi * x) * cpl::Math::fastsine(simd::consts::pi * x / size)) / (simd::consts::pi_squared * x * x) : 1; } @@ -304,28 +294,14 @@ namespace cpl } - template - auto lfilter(T* vec, std::size_t asize, T x, signed int wsize) -> typename std::remove_reference::type + template + inline auto lanczosFilter(TInput* vec, Types::fsint_t asize, TFraction x, Types::fsint_t wsize) { - R resonance = 0; - signed start = static_cast(floor(x)); - for (signed int i = start - wsize + 1; i < (start + wsize + 1); ++i) - { - if (i >= 0 && i < asize) - { - auto impulse = vec[i]; - auto response = lzresponse(x - i, wsize); - resonance += impulse * response; - } - } - return resonance; - } + typedef typename std::remove_reference::type TRet; + + TResonance resonance = 0; + const auto start = cpl::Math::floorToNInf(x); - template - inline auto lanczosFilter(T* vec, Types::fsint_t asize, Y x, Types::fsint_t wsize) -> typename std::remove_reference::type - { - R resonance = 0; - Types::fsint_t start = cpl::Math::floorToNInf(x); for (Types::fsint_t i = start - wsize + 1; i < (start + wsize + 1); ++i) { if (i >= 0 && i < asize) @@ -335,12 +311,15 @@ namespace cpl resonance += impulse * response; } } - return resonance; + + return static_cast(resonance); } template inline R lanczosFilter(uarray vec, Y x, Types::fsint_t wsize) { + typedef typename std::remove_reference::type TRet; + R resonance = 0; Types::fsint_t start = cpl::Math::floorToNInf(x); Types::fsint_t asize = static_cast(vec.size()); @@ -351,33 +330,38 @@ namespace cpl { auto impulse = vec[i]; auto response = lzresponse(x - i, wsize); - resonance += impulse * response; + resonance += static_cast(impulse * response); } } - return resonance; + + return static_cast(resonance); } template - inline auto sincFilter(T* vec, std::size_t asize, Y x, Types::fsint_t wsize) -> typename std::remove_reference::type + inline auto sincFilter(T* vec, std::size_t asize, Y x, Types::fsint_t wsize) { + typedef typename std::remove_reference::type TRet; + R resonance = 0; Types::fsint_t start = cpl::Math::floorToNInf(x); + for (Types::fsint_t i = start - wsize + 1; i < (start + wsize + 1); ++i) { if (i >= 0 && i < asize) { auto impulse = vec[i]; auto response = scresponse(x - i); - resonance += impulse * response; + resonance += static_cast(impulse * response); } } - return resonance; + + return static_cast(resonance); } template inline auto linearFilter(T* vec, Types::fsint_t asize, Y x) -> typename std::remove_reference::type { - Types::fsint_t x1 = cpl::Math::floorToNInf(static_cast(x)); + Types::fsint_t x1 = cpl::Math::floorToNInf(x); Types::fsint_t x2 = std::min(asize - 1, x1 + 1); //if (x2 == asize - 1) @@ -391,7 +375,7 @@ namespace cpl template inline T linearFilter(cpl::uarray vec, Y x) { - Types::fsint_t x1 = cpl::Math::floorToNInf(static_cast(x)); + Types::fsint_t x1 = cpl::Math::floorToNInf(x); Types::fsint_t x2 = std::min(static_cast(vec.size()) - 1, x1 + 1); //if (x2 == asize - 1) diff --git a/dsp/CComplexResonator.h b/dsp/CComplexResonator.h index 0e7f0a5..56be4d3 100755 --- a/dsp/CComplexResonator.h +++ b/dsp/CComplexResonator.h @@ -98,6 +98,9 @@ namespace cpl { // 7 == state size of all members using namespace cpl; + using namespace cpl::simd; + + CPL_RUNTIME_ASSERTION(sampleRate > 0); const auto minWindowSize = std::min(minNSize, maxNSize); const auto maxWindowSize = std::max(minNSize, maxNSize); @@ -139,18 +142,18 @@ namespace cpl // 3 dB law bandwidth of complex resonator // see jos' paper - auto const r = exp(-M_PI * hDiff / sampleRate); + auto const r = exp(-consts::pi * hDiff / sampleRate); - N[k] = 1.0 / (1 - r); + N[k] = consts::one / (1 - r); for (std::size_t v = 0; v < numVectors; ++v) { // so basically, for doing frequency-domain windowing using DFT-coefficients of the windows, we need filters that are linearly // spaced around the frequency like the FFT. DFT bins are spaced linearly like 0.5 / N. - auto const omega = (2 * M_PI * (mappedHz[k] + Math::mapAroundZero(v, numVectors) * hDiff * 0.5)) / sampleRate; + auto const omega = (consts::tau * (mappedHz[k] + Math::mapAroundZero(v, numVectors) * hDiff * consts::half)) / sampleRate; - auto const realPart = r * cos(omega); - auto const imagPart = r * sin(omega); + auto const realPart = r * std::cos(omega); + auto const imagPart = r * std::sin(omega); coeff[v * vC + k + nR * real] = realPart; // coeffs.c[0].real() coeff[v * vC + k + nR * imag] = imagPart; // coeffs.c[0].imag() @@ -435,7 +438,7 @@ namespace cpl { for (std::size_t k = 0; k < maxResonators; k++) { - Scalar gainCoeff = scale / (constant.N[k] * 0.5); // 2^-3 (3 vectors) + Scalar gainCoeff = scale / (constant.N[k] * T(0.5)); // 2^-3 (3 vectors) Scalar realPart(0), imagPart(0); diff --git a/dsp/CPeakFilter.h b/dsp/CPeakFilter.h index 49364f7..d08a1b3 100755 --- a/dsp/CPeakFilter.h +++ b/dsp/CPeakFilter.h @@ -36,7 +36,6 @@ #include "../Mathext.h" #include "../Utility.h" -#include namespace cpl { diff --git a/dsp/DSPWindows.h b/dsp/DSPWindows.h index b00005e..18aaf37 100755 --- a/dsp/DSPWindows.h +++ b/dsp/DSPWindows.h @@ -181,40 +181,44 @@ namespace cpl void generalizedCosineSequence(InOutVector & v, LengthType N, Shape symmetry, T a0, T a1) { T K = T(symmetry == Shape::Periodic ? N : N - 1); - T offset = symmetry == Shape::DFTEven ? T(M_PI / K) : 0; + const auto piK = T(M_PI / K); + T offset = symmetry == Shape::DFTEven ? piK : 0; T scale = 1 / (a0 + a1); for (std::size_t n = 0; n < N; ++n) - v[n] = scale * (a0 - a1 * std::cos(offset + n * 2 * M_PI / K)); + v[n] = scale * (a0 - a1 * std::cos(offset + n * 2 * piK)); } template void generalizedCosineSequence(InOutVector & v, LengthType N, Shape symmetry, T a0, T a1, T a2) { T K = T(symmetry == Shape::Periodic ? N : N - 1); - T offset = symmetry == Shape::DFTEven ? T(M_PI / K) : 0; + const auto piK = T(M_PI / K); + T offset = symmetry == Shape::DFTEven ? piK : 0; T scale = 1 / (a0 + a1 + a2); for (std::size_t n = 0; n < N; ++n) - v[n] = scale * (a0 - a1 * std::cos(offset + n * 2 * M_PI / K) + a2 * std::cos(offset + n * 4 * M_PI / K)); + v[n] = scale * (a0 - a1 * std::cos(offset + n * 2 * piK) + a2 * std::cos(offset + n * 4 * piK)); } template void generalizedCosineSequence(InOutVector & v, LengthType N, Shape symmetry, T a0, T a1, T a2, T a3) { T K = T(symmetry == Shape::Periodic ? N : N - 1); - T offset = symmetry == Shape::DFTEven ? T(M_PI / K) : 0; + const auto piK = T(M_PI / K); + T offset = symmetry == Shape::DFTEven ? piK : 0; T scale = 1 / (a0 + a1 + a2 + a3); for (std::size_t n = 0; n < N; ++n) - v[n] = scale * (a0 - a1 * std::cos(offset + n * 2 * M_PI / K) + a2 * std::cos(offset + n * 4 * M_PI / K) - a3 * std::cos(offset + n * 6 * M_PI / K)); + v[n] = scale * (a0 - a1 * std::cos(offset + n * 2 * piK) + a2 * std::cos(offset + n * 4 * piK) - a3 * std::cos(offset + n * 6 * piK)); } template void generalizedCosineSequence(InOutVector & v, LengthType N, Shape symmetry, T a0, T a1, T a2, T a3, T a4) { T K = T(symmetry == Shape::Periodic ? N : N - 1); - T offset = symmetry == Shape::DFTEven ? T(M_PI / K) : 0; + const auto piK = T(M_PI / K); + T offset = symmetry == Shape::DFTEven ? piK : 0; T scale = 1 / (a0 + a1 + a2 + a3 + a4); for (std::size_t n = 0; n < N; ++n) - v[n] = scale * (a0 - a1 * std::cos(offset + n * 2 * M_PI / K) + a2 * std::cos(offset + n * 4 * M_PI / K) - a3 * std::cos(offset + n * 6 * M_PI / K) + a4 * std::cos(offset + n * 8 * M_PI / K)); + v[n] = scale * (a0 - a1 * std::cos(offset + n * 2 * piK) + a2 * std::cos(offset + n * 4 * piK) - a3 * std::cos(offset + n * 6 * piK) + a4 * std::cos(offset + n * 8 * piK)); } template<> @@ -626,11 +630,11 @@ namespace cpl { if (N == 0) return; - beta = std::max(std::min(beta, (T)0.5), std::numeric_limits::min()); + beta = std::max(std::min(beta, (T)0.5), (T)0.001); T K = T(symmetry == Shape::Periodic ? N : N - 1); T offset = symmetry == Shape::DFTEven ? (T)(0.5 / K) : 0; for (std::size_t n = 0; n < N; ++n) - w[n] = std::exp(-0.5 * cpl::Math::square((((n + offset) - K * 0.5) / (beta * N * 0.5)))); + w[n] = std::exp(T(-0.5) * cpl::Math::square((((n + offset) - K * T(0.5)) / (beta * N * T(0.5))))); } template @@ -715,7 +719,7 @@ namespace cpl T rtau = 1 / (T(N * 0.5) * (T)8.69 / alpha); for (std::size_t n = 0; n < N; ++n) - w[n] = std::exp(-rtau * std::abs((n + offset) - K * 0.5)); + w[n] = std::exp(-rtau * std::abs((n + offset) - K * T(0.5))); } template @@ -734,14 +738,15 @@ namespace cpl template static void generate(InOutVector & w, std::size_t N, Shape symmetry = Shape::Symmetric, T alpha = T(), T beta = T()) { + using consts = simd::consts; if (N == 0) return; T K = T(symmetry == Shape::Periodic ? N : N - 1); - T offset = symmetry == Shape::DFTEven ? (T)(0.5 / K) : 0; - T rtau = 1 / (T(N * 0.5) * (T)8.69 / alpha); + T offset = symmetry == Shape::DFTEven ? consts::half / K : 0; + T rtau = 1 / (N * consts::half * (T)8.69 / alpha); for (std::size_t n = 0; n < N; ++n) - w[n] = ((T)0.5 - (T)0.5 * std::cos(offset + n * 2 * M_PI / K)) * std::exp(-rtau * std::abs((n + offset) - K * (T)0.5)); + w[n] = (consts::half - consts::half * std::cos(offset + n * consts::tau / K)) * std::exp(-rtau * std::abs((n + offset) - K * consts::half)); } template @@ -760,13 +765,15 @@ namespace cpl template static void generate(InOutVector & w, std::size_t N, Shape symmetry = Shape::Symmetric, T alpha = T(), T beta = T()) { + using consts = simd::consts; + if (N == 0) return; T K = T(symmetry == Shape::Periodic ? N : N - 1); - T offset = symmetry == Shape::DFTEven ? (T)(0.5 / K) : 0; + T offset = symmetry == Shape::DFTEven ? consts::half / K : 0; for (std::size_t n = 0; n < N; ++n) - w[n] = scresponse((offset + (T)2 * n / K) - 1); + w[n] = scresponse((offset + consts::two * n / K) - 1); } template diff --git a/external/simde b/external/simde new file mode 160000 index 0000000..71fd833 --- /dev/null +++ b/external/simde @@ -0,0 +1 @@ +Subproject commit 71fd833d9666141edcd1d3c109a80e228303d8d7 diff --git a/ffts/dustfft.cpp b/ffts/dustfft.cpp index f512232..c23b677 100755 --- a/ffts/dustfft.cpp +++ b/ffts/dustfft.cpp @@ -19,7 +19,7 @@ #endif /* _MSC_VER */ #include /* sin() */ -#include +#include "../Types.h" #ifdef WIN32 # define ALIGN16 __declspec(align(16)) #else diff --git a/ffts/unifft.h b/ffts/unifft.h index 547f44a..825fc48 100644 --- a/ffts/unifft.h +++ b/ffts/unifft.h @@ -145,7 +145,7 @@ namespace cpl Scalar scale = Scalar(1) / input.size(); auto cout = output.template reinterpret(); - for (std::size_t i = 0; i < size() / 4; i += 4) + for (std::size_t i = 0; i < size / 4; i += 4) { cout[i + 0] = input[i + 0] * scale; cout[i + 1] = input[i + 1] * scale; diff --git a/gui/CCtrlEditSpace.cpp b/gui/CCtrlEditSpace.cpp index c70c64b..4cf5a74 100644 --- a/gui/CCtrlEditSpace.cpp +++ b/gui/CCtrlEditSpace.cpp @@ -56,15 +56,16 @@ namespace cpl } CCtrlEditSpace::CCtrlEditSpace(cpl::CBaseControl * parent) - : parentControl(parent), hasBeenInitialized(false), exitAfterAnimation(false), inputValueWasValid(false), - toolTip("Control Edit Space: Interface for editing the values of controls precisely."), - expanderButton(new CTriangleButton()), - compactWidth(120), - compactHeight(25), - fullWidth(200), - fullHeight(120), - compactMode(true) - + : parentControl(parent) + , exitAfterAnimation(false) + , inputValueWasValid(false) + , toolTip("Control Edit Space: Interface for editing the values of controls precisely.") + , expanderButton(new CTriangleButton()) + , compactWidth(120) + , compactHeight(25) + , fullWidth(200) + , fullHeight(120) + , compactMode(true) { if (!parent) { @@ -108,7 +109,6 @@ namespace cpl exportedControlName = parentControl->bGetExportedName(); - setBounds(0, 0, compactWidth, compactHeight); addChildComponent(intValueLabel); addChildComponent(switchWithOld.get()); addAndMakeVisible(iconSucces); @@ -116,6 +116,11 @@ namespace cpl addAndMakeVisible(fmtValueLabel); addAndMakeVisible(errorVisualizer); addAndMakeVisible(expanderButton.get()); + setBounds(0, 0, compactWidth, compactHeight); + + // JUCE 8 asserts if you attempt to grab keyboard focus without fully existing yet, + // so have this happen after message queue processing in a bit. + cpl::GUIUtils::MainEvent(*this, [this]() { createSimpleViewEditor(); }); } @@ -124,20 +129,8 @@ namespace cpl return toolTip; } - inline int getBorder(int size, int maxSize) - { - return 0; - } - void CCtrlEditSpace::paint(juce::Graphics & g) { - - if (!hasBeenInitialized) - { - hasBeenInitialized = true; - createSimpleViewEditor(); - } - g.fillAll(cpl::GetColour(cpl::ColourEntry::Deactivated)); g.setColour(cpl::GetColour(cpl::ColourEntry::Separator)); g.drawVerticalLine(getWidth() - (compactHeight - 1), 0.f, (float)getHeight() - 1); @@ -216,7 +209,6 @@ namespace cpl } errorVisualizer.setBounds(getBounds().withPosition(0, 0)); - fmtValueLabel.grabKeyboardFocus(); } void CCtrlEditSpace::createSimpleViewEditor() @@ -228,10 +220,6 @@ namespace cpl } void CCtrlEditSpace::editorShown(Label * curLabel, TextEditor & editor) { - if (curLabel == &fmtValueLabel) - editor.addListener(this); - if (curLabel == &intValueLabel) - editor.addListener(this); editor.setScrollToShowCursor(false); } @@ -302,28 +290,28 @@ namespace cpl } } - void CCtrlEditSpace::textEditorReturnKeyPressed(TextEditor & editor) + void CCtrlEditSpace::labelTextChanged(juce::Label* labelThatHasChanged) { - if (&editor == fmtValueLabel.getCurrentTextEditor()) + if (labelThatHasChanged == &fmtValueLabel) { // this is where we try to interpret a formatted value // to an internal range. // note: we use the value from the editor, because the label may not // have been updated yet. - if ((inputValueWasValid = interpretAndSet(editor.getText().toStdString()))) + if ((inputValueWasValid = interpretAndSet(fmtValueLabel.getText().toStdString()))) { animateSucces(&fmtValueLabel); } else animateError(&fmtValueLabel); } - else if (&editor == intValueLabel.getCurrentTextEditor()) + else if (labelThatHasChanged == &intValueLabel) { // here we try to map an input string to [0, 1] range. // the cbasecontrol provides a static method for this iCtrlPrec_t val(0); - auto succes = CBaseControl::bMapStringToInternal(editor.getText().toStdString(), val); + auto succes = CBaseControl::bMapStringToInternal(fmtValueLabel.getText().toStdString(), val); if (succes) @@ -383,7 +371,7 @@ namespace cpl void CCtrlEditSpace::visibilityChanged() { - fmtValueLabel.showEditor(); + //fmtValueLabel.showEditor(); resetToControl(); @@ -441,7 +429,7 @@ namespace cpl { return "What the internal value represents."; } - return juce::String::empty; + return {}; } void CCtrlEditSpace::onObjectDestruction(const CBaseControl::ObjectProxy & ctrl) { @@ -469,16 +457,6 @@ namespace cpl { return parentControl; } - void CCtrlEditSpace::labelTextChanged(juce::Label *labelThatHasChanged) - { - if (labelThatHasChanged == &fmtValueLabel) - { - auto const & editText = fmtValueLabel.getText(); - if (editText.length()) - interpretAndSet(editText.toStdString()); - - } - } void CCtrlEditSpace::changeListenerCallback(ChangeBroadcaster *source) { diff --git a/gui/CCtrlEditSpace.h b/gui/CCtrlEditSpace.h index 57572a4..6459eac 100644 --- a/gui/CCtrlEditSpace.h +++ b/gui/CCtrlEditSpace.h @@ -51,10 +51,9 @@ namespace cpl public juce::Component, public juce::Label::Listener, public CBaseControl::Listener, - public juce::TextEditor::Listener, public Utility::DestructionServer, public juce::ChangeListener, - public juce::ButtonListener, + public juce::Button::Listener, public CToolTipClient, public DestructionNotifier { @@ -97,7 +96,6 @@ namespace cpl virtual void labelTextChanged(juce::Label *labelThatHasChanged) override; virtual void resized() override; virtual void visibilityChanged() override; - virtual void textEditorReturnKeyPressed(TextEditor &) override; virtual void changeListenerCallback(ChangeBroadcaster *source) override; virtual void editorShown(Label *, TextEditor &) override; virtual void valueChanged(const CBaseControl * ctrl) override; @@ -136,7 +134,6 @@ namespace cpl bool compactMode; volatile bool inputValueWasValid; volatile bool exitAfterAnimation; - bool hasBeenInitialized; juce::Label fmtValueLabel; }; diff --git a/gui/CEditSpaceSpawner.cpp b/gui/CEditSpaceSpawner.cpp index 8f0a91a..0d55e29 100644 --- a/gui/CEditSpaceSpawner.cpp +++ b/gui/CEditSpaceSpawner.cpp @@ -38,9 +38,9 @@ namespace cpl parent.addMouseListener(this, true); dialog.setName(programInfo.name + " edit space"); dialog.setOpaque(true); - dialog.addToDesktop(juce::ComponentPeer::StyleFlags::windowHasDropShadow); + // JUCE 8 rounds corners of dropshadow peers... + dialog.addToDesktop(/*juce::ComponentPeer::StyleFlags::windowHasDropShadow | */ juce::ComponentPeer::StyleFlags::windowIsTemporary); dialog.setVisible(false); - } CEditSpaceSpawner::~CEditSpaceSpawner() diff --git a/gui/CEditSpaceSpawner.h b/gui/CEditSpaceSpawner.h index c15acce..119bf00 100644 --- a/gui/CEditSpaceSpawner.h +++ b/gui/CEditSpaceSpawner.h @@ -51,7 +51,6 @@ namespace cpl public juce::ComponentListener { public: - // http://stackoverflow.com/questions/281818/unmangling-the-result-of-stdtype-infoname CEditSpaceSpawner(juce::Component & parentToControl); ~CEditSpaceSpawner(); @@ -65,7 +64,6 @@ namespace cpl virtual void mouseDown(const juce::MouseEvent & e) override; - bool isEditSpacesOn; bool recursionEdit; juce::Component & parent; std::unique_ptr currentEditSpace; diff --git a/gui/CToolTip.cpp b/gui/CToolTip.cpp index 1fd1a30..99fcf7a 100644 --- a/gui/CToolTip.cpp +++ b/gui/CToolTip.cpp @@ -77,7 +77,7 @@ namespace cpl hideTip(); } - void CToolTipWindow::updatePosition(const String& tip, Point pos, const juce::Rectangle& parentArea) + void CToolTipWindow::updatePosition(const String& tip, juce::Point pos, const juce::Rectangle& parentArea) { int w, h; #if JUCE_MAJOR_VERSION >= 4 @@ -96,7 +96,7 @@ namespace cpl setVisible(true); } - void CToolTipWindow::displayTip(Point screenPos, const String& tip) + void CToolTipWindow::displayTip(juce::Point screenPos, const String& tip) { jassert(tip.isNotEmpty()); if (tipShowing != tip) @@ -165,7 +165,7 @@ namespace cpl } while ((parent = parent->getParentComponent())); } - return String::empty; + return {}; } void CToolTipWindow::hideTip() @@ -193,7 +193,7 @@ namespace cpl mouseClicks = clickCount; mouseWheelMoves = wheelCount; - const Point mousePos(mouseSource.getScreenPosition().roundToInt()); + const juce::Point mousePos(mouseSource.getScreenPosition().roundToInt()); const bool mouseMovedQuickly = mousePos.getDistanceFrom(lastMousePos) > 12; lastMousePos = mousePos; diff --git a/gui/CToolTip.h b/gui/CToolTip.h index 739cede..90d8a13 100644 --- a/gui/CToolTip.h +++ b/gui/CToolTip.h @@ -62,7 +62,7 @@ namespace cpl /** Returns the string that this object wants to show as its tooltip. */ virtual String bGetToolTip() const = 0; - virtual String bGetToolTipForChild(const Component *) const { return juce::String::empty; } + virtual String bGetToolTipForChild(const Component *) const { return {}; } }; class CToolTipWindow : public Component, @@ -100,7 +100,7 @@ namespace cpl void setMillisecondsBeforeTipAppears(int newTimeMs = 700) noexcept; /** Can be called to manually force a tip to be shown at a particular location. */ - void displayTip(Point screenPosition, const String& text); + void displayTip(juce::Point screenPosition, const String& text); /** Can be called to manually hide the tip if it's showing. */ void hideTip(); @@ -135,7 +135,7 @@ namespace cpl private: //============================================================================== int millisecondsBeforeTipAppears; - Point lastMousePos; + juce::Point lastMousePos; int mouseClicks, mouseWheelMoves; unsigned int lastCompChangeTime, lastHideTime; Component* lastComponentUnderMouse; @@ -144,7 +144,7 @@ namespace cpl void paint(Graphics&) override; void mouseEnter(const MouseEvent&) override; void timerCallback() override; - void updatePosition(const String&, Point, const juce::Rectangle&); + void updatePosition(const String&, juce::Point, const juce::Rectangle&); static String getTipFor(Component*); diff --git a/gui/CViews.h b/gui/CViews.h index 9851f8e..6883423 100644 --- a/gui/CViews.h +++ b/gui/CViews.h @@ -31,6 +31,7 @@ #ifndef CPL_CVIEWS_H #define CPL_CVIEWS_H + #include #include "../Common.h" #include "CToolTip.h" diff --git a/gui/DesignBase.cpp b/gui/DesignBase.cpp index 944dace..914cb2e 100755 --- a/gui/DesignBase.cpp +++ b/gui/DesignBase.cpp @@ -315,7 +315,7 @@ namespace cpl d1 + xO + 0, d1 + yO + triangleSize * 0.5f ); - triangleVertices.applyTransform(AffineTransform::identity.rotated(float(isPopped * -M_PI * 0.5), d1 + xO + triangleSize * 0.5f, d1 + yO + triangleSize * 0.5f)); + triangleVertices.applyTransform(AffineTransform().rotated(float(isPopped * -M_PI * 0.5), d1 + xO + triangleSize * 0.5f, d1 + yO + triangleSize * 0.5f)); g.setColour(cpl::GetColour(cpl::ColourEntry::Activated).brighter( c.isMouseOverOrDragging() * 0.1f + 0.2f + 0.2f * isPopped) ); @@ -343,23 +343,24 @@ namespace cpl } /* virtual juce::LowLevelGraphicsContext * LookAndFeel::createGraphicsContextAdvanced( - const Image & buffer, const Point origin, const RectangleList clip, bool isScreenContext, const Point componentPosition) + const Image & buffer, const juce::Point origin, const RectangleList clip, bool isScreenContext, const juce::Point componentPosition) { // default implementation just forwards: return createGraphicsContext(buffer, origin, clip); }*/ - juce::LowLevelGraphicsContext * CLookAndFeel_CPL::createGraphicsContext( + std::unique_ptr CLookAndFeel_CPL::createGraphicsContext( const Image &imageToRenderOn, - const Point< int > &origin, + juce::Point origin, const RectangleList< int > &initialClip) { +#ifdef CPL_ENABLE_CSUBPIXELGRAPHICS if (tryToRenderSubpixel/* && imageToRenderOn.getFormat() == imageToRenderOn.RGB*/) { - return new rendering::CSubpixelSoftwareGraphics(imageToRenderOn, origin, initialClip); + return std::make_unique(imageToRenderOn, origin, initialClip); } - - return new juce::LowLevelGraphicsSoftwareRenderer(imageToRenderOn, origin, initialClip); +#endif + return juce::LookAndFeel_V3::createGraphicsContext(imageToRenderOn, origin, initialClip); } @@ -405,7 +406,7 @@ namespace cpl else #endif { - return LookAndFeel::getTypefaceForFont(font); + return juce::LookAndFeel_V3::getTypefaceForFont(font); } } diff --git a/gui/DesignBase.h b/gui/DesignBase.h index 4693bbf..8cefb0b 100755 --- a/gui/DesignBase.h +++ b/gui/DesignBase.h @@ -34,12 +34,13 @@ #include "../Common.h" #include #include +#include namespace cpl { #ifdef CPL_JUCE typedef juce::Colour CColour; - typedef juce::Colours CColours; + namespace CColours = juce::Colours; typedef juce::Point CPoint; typedef juce::Rectangle CRect; typedef juce::Component GraphicComponent; @@ -116,10 +117,11 @@ namespace cpl static CLookAndFeel_CPL & defaultLook(); // overrides juce::Typeface::Ptr getTypefaceForFont(juce::Font const& font) override; - virtual juce::LowLevelGraphicsContext * createGraphicsContext( - const Image &imageToRenderOn, - const Point< int > &origin, - const RectangleList< int > &initialClip) override; + std::unique_ptr createGraphicsContext( + const Image& imageToRenderOn, + juce::Point origin, + const RectangleList& initialClip) override; + juce::Font getPopupMenuFont() override; juce::Font getComboBoxFont(ComboBox &) override; void drawComboBox( diff --git a/gui/NewStuffAndLook.h b/gui/NewStuffAndLook.h index b826d4a..0460140 100644 --- a/gui/NewStuffAndLook.h +++ b/gui/NewStuffAndLook.h @@ -534,8 +534,8 @@ namespace cpl { if (panelIsClosed) { - triangleVertices.applyTransform(AffineTransform::identity.rotated((float)M_PI / 2, originCenter.getX(), originCenter.getY())); - //triangleVertices.applyTransform(AffineTransform::identity.rotated(rotation, originCenter.getX(), originCenter.getY())); + triangleVertices.applyTransform(AffineTransform().rotated((float)M_PI / 2, originCenter.getX(), originCenter.getY())); + //triangleVertices.applyTransform(AffineTransform().rotated(rotation, originCenter.getX(), originCenter.getY())); } else { @@ -546,15 +546,15 @@ namespace cpl { if (panelIsClosed) { - triangleVertices.applyTransform(AffineTransform::identity.rotated((float)M_PI / 2, originCenter.getX(), originCenter.getY())); + triangleVertices.applyTransform(AffineTransform().rotated((float)M_PI / 2, originCenter.getX(), originCenter.getY())); } else { - triangleVertices.applyTransform(AffineTransform::identity.rotated((float)-M_PI, originCenter.getX(), originCenter.getY())); + triangleVertices.applyTransform(AffineTransform().rotated((float)-M_PI, originCenter.getX(), originCenter.getY())); } } - //triangleVertices.applyTransform(AffineTransform::identity.rotated(rotation, originCenter.getX(), originCenter.getY())); + //triangleVertices.applyTransform(AffineTransform().rotated(rotation, originCenter.getX(), originCenter.getY())); } int getMouseHoverButton() @@ -648,7 +648,7 @@ namespace cpl ); triangleVertices.applyTransform( - AffineTransform::identity.rotated(dirs[getToggleState()] * float(M_PI * 0.5), getWidth() * 0.5f, getHeight() * 0.5f) + AffineTransform().rotated(dirs[getToggleState()] * float(M_PI * 0.5), getWidth() * 0.5f, getHeight() * 0.5f) ); } @@ -664,7 +664,7 @@ namespace cpl { public: - static const size_t iconOffset = 3; + static const int iconOffset = 3; virtual void paint(juce::Graphics & g) override { diff --git a/gui/Tools.h b/gui/Tools.h index 511a785..d962eaf 100644 --- a/gui/Tools.h +++ b/gui/Tools.h @@ -204,7 +204,7 @@ namespace cpl { std::async(std::launch::async, [=]() { - cpl::Misc::Delay(msToDelay); + cpl::Misc::Delay(static_cast(msToDelay)); const juce::MessageManagerLock lock; f(); }); diff --git a/gui/controls/CColourControl.cpp b/gui/controls/CColourControl.cpp index d48495f..b75ba06 100644 --- a/gui/controls/CColourControl.cpp +++ b/gui/controls/CColourControl.cpp @@ -54,7 +54,9 @@ namespace cpl public juce::ColourSelector { public: - CustomColourSelector(int flags = (showSliders | showAlphaChannel | showColourAtTop | showColourspace), + static constexpr int TheNormalFlags = showSliders | showAlphaChannel | showColourAtTop | showColourspace | editableColour; + + CustomColourSelector(int flags = TheNormalFlags, int edgeGap = 4, int gapAroundColourSpaceComponent = 7) : juce::ColourSelector(flags, edgeGap, gapAroundColourSpaceComponent) @@ -109,7 +111,7 @@ namespace cpl public: ColourEditor(CColourControl * parentControl) - : CKnobSliderEditor(parentControl), parent(parentControl), selector(15, 5, 5), + : CKnobSliderEditor(parentControl), parent(parentControl), selector(CustomColourSelector::TheNormalFlags, 5, 5), recursionFlagWeChanged(false), recursionFlagTheyChanged(false) { oldHeight = fullHeight; diff --git a/gui/controls/CComboBox.cpp b/gui/controls/CComboBox.cpp index 155e1d6..b4da7fe 100644 --- a/gui/controls/CComboBox.cpp +++ b/gui/controls/CComboBox.cpp @@ -176,7 +176,7 @@ namespace cpl for (const auto & str : values) { counter++; - if (currentIndex == str) + if (currentIndex == juce::CharPointer_UTF8(str.c_str())) newIndex = counter; arr.add(str); } diff --git a/gui/controls/CValueComboBox.cpp b/gui/controls/CValueComboBox.cpp index e61efbd..41687c0 100644 --- a/gui/controls/CValueComboBox.cpp +++ b/gui/controls/CValueComboBox.cpp @@ -128,7 +128,7 @@ namespace cpl for (const auto & str : values) { counter++; - if (currentIndex == str) + if (currentIndex == juce::CharPointer_UTF8(str.c_str())) newIndex = counter; arr.add(str); } diff --git a/gui/widgets/CDSPWindowWidget.cpp b/gui/widgets/CDSPWindowWidget.cpp index 88ccc73..7032d13 100644 --- a/gui/widgets/CDSPWindowWidget.cpp +++ b/gui/widgets/CDSPWindowWidget.cpp @@ -94,7 +94,7 @@ namespace cpl // scale to graphics size y1 = Math::UnityScale::linear(y1, bot, top); - if (!std::isnormal(y1) && y1 != 0.0) + if (std::fpclassify(y1) != FP_NORMAL) { y1 = bot; } @@ -107,7 +107,7 @@ namespace cpl y2 = Math::UnityScale::Inv::exp(y2, fftMin, fftMax); // scale to graphics size - y2 = Math::UnityScale::linear(y2, bot, top); + y2 = Math::UnityScale::linear(y2, bot - 0.5f, top + 0.5f); if (!std::isnormal(y2)) { @@ -144,7 +144,7 @@ namespace cpl // normalize to [-1, 1] y2 = Math::UnityScale::Inv::linear(y2, minW, maxW); // scale to graphics size - y2 = Math::UnityScale::linear(y2, bot, top); + y2 = Math::UnityScale::linear(y2, bot - 0.5f, top + 0.5f); g.drawLine({(float)n - 1, (float)y1, float(n), (float)y2}); diff --git a/gui/widgets/CPresetWidget.cpp b/gui/widgets/CPresetWidget.cpp index 61b5686..58b24b7 100644 --- a/gui/widgets/CPresetWidget.cpp +++ b/gui/widgets/CPresetWidget.cpp @@ -73,53 +73,51 @@ namespace cpl SerializerType serializer(name); serializer.getArchiver().setMasterVersion(version); parent->serializeObject(serializer.getArchiver(), serializer.getArchiver().getLocalVersion()); - juce::File location; - bool result = CPresetManager::instance().savePresetAs(serializer, location, name); - // update list anyway; user may delete files in dialog etc. - updatePresetList(); - if (result) - { - setDisplayedPreset(location); - } - + dialog = CPresetManager::instance().savePresetAs(serializer, + [this](const juce::File& savedFile) { + // update list anyway; user may delete files in dialog etc. + updatePresetList(); + setDisplayedPreset(savedFile); + } + ); } else if (c == &kloadPreset) { - SerializerType serializer(name); - juce::File location; - bool result = CPresetManager::instance().loadPresetAs(serializer, location, name); - updatePresetList(); - if (result) - { - parent->deserializeObject(serializer.getBuilder(), serializer.getBuilder().getLocalVersion()); - setDisplayedPreset(location); - } + dialog = CPresetManager::instance().loadPresetAs(name, + [this](const juce::File& loadedFile, CCheckedSerializer& loadedData) { + updatePresetList(); + parent->deserializeObject(loadedData.getBuilder(), loadedData.getBuilder().getLocalVersion()); + setDisplayedPreset(loadedFile); + } + ); } else if (c == &ksaveDefault) { SerializerType serializer(name); serializer.getArchiver().setMasterVersion(version); parent->serializeObject(serializer.getArchiver(), serializer.getArchiver().getLocalVersion()); - juce::File location; - bool result = CPresetManager::instance().savePreset(fullPathToPreset("default"), serializer, location); + auto path = fullPathToPreset("default"); + juce::File file(path); + bool result = CPresetManager::instance().savePreset(path, serializer); // update list anyway; user may delete files in dialog etc. updatePresetList(); if (result) { - setDisplayedPreset(location); + setDisplayedPreset(file); } } else if (c == &kloadDefault) { SerializerType serializer(name); - juce::File location; - bool result = CPresetManager::instance().loadPreset(fullPathToPreset("default"), serializer, location); + auto path = fullPathToPreset("default"); + juce::File file(path); + bool result = CPresetManager::instance().loadPreset(path, serializer); updatePresetList(); if (result) { parent->deserializeObject(serializer.getBuilder(), serializer.getBuilder().getLocalVersion()); - setDisplayedPreset(location); + setDisplayedPreset(file); } } else if (c == &kpresetList) @@ -128,16 +126,15 @@ namespace cpl if (presetName.size()) { - juce::File location; + auto path = fullPathToPreset(presetName); + juce::File file(path); SerializerType serializer(name); - if (CPresetManager::instance().loadPreset(fullPathToPreset(presetName), serializer, location)) + if (CPresetManager::instance().loadPreset(fullPathToPreset(presetName), serializer)) { parent->deserializeObject(serializer.getBuilder(), serializer.getBuilder().getLocalVersion()); - setSelectedPreset(location); + setSelectedPreset(file); } } - - } } diff --git a/gui/widgets/CPresetWidget.h b/gui/widgets/CPresetWidget.h index b39a0f8..db716db 100644 --- a/gui/widgets/CPresetWidget.h +++ b/gui/widgets/CPresetWidget.h @@ -36,6 +36,7 @@ #include "../controls/Controls.h" #include "WidgetBase.h" #include "../../state/Serialization.h" +#include "../../CPresetManager.h" namespace cpl { @@ -96,15 +97,12 @@ namespace cpl void setEmulatedVersion(cpl::Version newVersion); protected: + std::string presetWithoutExtension(juce::File preset); std::string fullPathToPreset(const std::string_view); void setDisplayedPreset(juce::File location); - - void initControls(); - // data - CButton kloadPreset, ksavePreset, kloadDefault, ksaveDefault; CComboBox kpresetList; MatrixSection layout; @@ -113,6 +111,7 @@ namespace cpl std::string ext; Setup layoutSetup; Version version; + CPresetManager::DialogState dialog; }; }; diff --git a/gui/widgets/CTransformWidget.cpp b/gui/widgets/CTransformWidget.cpp index c139d93..f67cee4 100644 --- a/gui/widgets/CTransformWidget.cpp +++ b/gui/widgets/CTransformWidget.cpp @@ -140,7 +140,7 @@ namespace cpl } } } - return juce::String::empty; + return {}; } void CTransformWidget::inputCommand(int x, int y, const String & data) @@ -308,7 +308,7 @@ namespace cpl { for (unsigned x = 0; x < 3; ++x) { - g.drawText(texts[x], x * (elementWidth + 15), elementHeight + y * (elementHeight * 2), 10, elementHeight, juce::Justification::centred); + g.drawText(texts[x], x * (elementWidth + 15), elementHeight + y * (elementHeight * 2), 12, elementHeight, juce::Justification::centredLeft); } } } diff --git a/infrastructure/parameters/CustomFormatters.h b/infrastructure/parameters/CustomFormatters.h index 7096680..2ea2e5f 100644 --- a/infrastructure/parameters/CustomFormatters.h +++ b/infrastructure/parameters/CustomFormatters.h @@ -9,12 +9,33 @@ namespace cpl { + enum class FormattingFlags : std::int32_t + { + none, + includeUnit = 1 << 0, + defaultFlags = includeUnit + }; + + inline FormattingFlags operator ~(FormattingFlags flags) + { + return static_cast(~static_cast(flags)); + } + + inline FormattingFlags operator & (FormattingFlags a, FormattingFlags b) + { + return static_cast(static_cast(a) & static_cast(b)); + } + + inline FormattingFlags operator | (FormattingFlags a, FormattingFlags b) + { + return static_cast(static_cast(a) | static_cast(b)); + } template typename std::enable_if::value, std::string>::type printer(const T & val, int precision = 2) { char buf[100]; - std::sprintf(buf, "%.*f", precision, (double)val); + cpl::sprintfs(buf, "%.*f", precision, (double)val); return buf; } @@ -28,8 +49,10 @@ namespace cpl class VirtualFormatter { public: - virtual bool format(const T & val, std::string & buf) = 0; + + virtual bool format(const T & val, std::string & buf, FormattingFlags flags = FormattingFlags::defaultFlags) = 0; virtual bool interpret(const string_ref buf, T & val) = 0; + virtual std::string_view getUnit() const { return {}; } virtual ~VirtualFormatter() {} }; @@ -37,7 +60,7 @@ namespace cpl class BasicFormatter : public VirtualFormatter { public: - virtual bool format(const T & val, std::string & buf) override + virtual bool format(const T & val, std::string & buf, FormattingFlags flags) override { buf = printer(val, 2); return true; @@ -53,7 +76,7 @@ namespace cpl class IntegerFormatter : public VirtualFormatter { public: - virtual bool format(const T & val, std::string & buf) override + virtual bool format(const T & val, std::string & buf, FormattingFlags flags) override { auto intValue = static_cast(std::round(val)); buf = printer(intValue, 2); @@ -65,7 +88,7 @@ namespace cpl std::int64_t ret; if (cpl::lexicalConversion(buf, ret)) { - val = ret; + val = static_cast(ret); return true; } @@ -77,10 +100,10 @@ namespace cpl class HexFormatter : public VirtualFormatter { public: - virtual bool format(const T & val, std::string & buf) override + virtual bool format(const T & val, std::string & buf, FormattingFlags flags) override { char buffer[100]; - std::sprintf(buffer, "0x%X", (int)val); + cpl::sprintfs(buffer, "0x%X", (int)val); buf = buffer; return true; } @@ -95,7 +118,7 @@ namespace cpl class BooleanFormatter : public VirtualFormatter { public: - virtual bool format(const T & val, std::string & buf) override + virtual bool format(const T & val, std::string & buf, FormattingFlags flags) override { if (val >= (T)0.5) buf = "true"; @@ -124,14 +147,19 @@ namespace cpl using BasicFormatter::interpret; - virtual bool format(const T & val, std::string & buf) override + std::string_view getUnit() const override { return this->unit; } + + virtual bool format(const T & val, std::string & buf, FormattingFlags flags) override { - BasicFormatter::format(val, buf); - buf += unit; + BasicFormatter::format(val, buf, flags); + + if ((flags & FormattingFlags::includeUnit) != FormattingFlags::none) + buf += " " + unit; + return true; } - void setUnit(const std::string_view unit) { this->unit = std::string(" "); this->unit += unit; } + void setUnit(const std::string_view unit) { this->unit = unit; } private: std::string unit; @@ -144,9 +172,9 @@ namespace cpl DBFormatter() : UnitFormatter("dB") {} - virtual bool format(const T & val, std::string & buf) override + virtual bool format(const T & val, std::string & buf, FormattingFlags flags) override { - return UnitFormatter::format(20 * std::log10(val), buf); + return UnitFormatter::format(20 * std::log10(val), buf, flags); } virtual bool interpret(const string_ref buf, T & val) override @@ -169,9 +197,9 @@ namespace cpl PercentageFormatter() : UnitFormatter("%") {} - virtual bool format(const T & val, std::string & buf) override + virtual bool format(const T & val, std::string & buf, FormattingFlags flags) override { - return UnitFormatter::format(std::round(val * 100), buf); + return UnitFormatter::format(std::round(val * 100), buf, flags); } virtual bool interpret(const string_ref buf, T & val) override @@ -201,7 +229,7 @@ namespace cpl } const std::vector & getValues() const noexcept { return values; } - virtual bool format(const T & val, std::string & buf) override + virtual bool format(const T & val, std::string & buf, FormattingFlags flags) override { if (values.size() == 0) { @@ -210,7 +238,7 @@ namespace cpl auto index = static_cast( std::min(values.size() - 1, static_cast(std::max(std::round(val), (T)0))) - ); + ); buf = values[index]; return true; @@ -236,4 +264,4 @@ namespace cpl }; }; -#endif \ No newline at end of file +#endif diff --git a/infrastructure/parameters/CustomTransforms.h b/infrastructure/parameters/CustomTransforms.h index 04a1ee5..6d8f71f 100644 --- a/infrastructure/parameters/CustomTransforms.h +++ b/infrastructure/parameters/CustomTransforms.h @@ -106,6 +106,9 @@ namespace cpl class BooleanRange : public VirtualTransformer { public: + + int getQuantization() override { return 2; } + T normalize(T val) const noexcept override { return val >= 0.5 ? 1 : 0; @@ -151,6 +154,8 @@ namespace cpl IntegerLinearRange(T minimum, T maximum) : RangedVirtualTransformerBase(minimum, maximum) {} IntegerLinearRange() {} + int getQuantization() override { return max - min; } + T normalize(T val) const noexcept override { return static_cast(Math::UnityScale::Inv::linear(std::round(val), min, max)); diff --git a/infrastructure/parameters/JuceAudioParameterBridge.h b/infrastructure/parameters/JuceAudioParameterBridge.h new file mode 100644 index 0000000..f0a7bf3 --- /dev/null +++ b/infrastructure/parameters/JuceAudioParameterBridge.h @@ -0,0 +1,130 @@ +#ifndef CPL_JUCEAUDIOPARAMETERBRIDGE_H +#define CPL_JUCEAUDIOPARAMETERBRIDGE_H + +#include "ParameterSystem.h" +#include "../../Common.h" // Brings in JUCE + +namespace cpl +{ + template + class JuceAudioParameter : public juce::AudioProcessorParameter + { + public: + + typedef typename cpl::ParameterGroup::ParameterView View; + typedef typename cpl::ParameterGroup::Formatter Formatter; + typedef T ValueType; + + JuceAudioParameter(View& baseParameter) + : juce::AudioProcessorParameter() + , view(baseParameter) + { + } + + private: + + View& view; + + float getValue() const override // Normalized + { + return view.template getValueNormalized(); + } + + void setValue(float newValue) override + { + view.updateFromHostNormalized(static_cast(newValue)); + } + + float getDefaultValue() const override + { + return 0; + } + + juce::String getName(int maximumStringLength) const override + { + return view.getExportedName(); + } + + juce::String getLabel() const override + { + auto unit = view.getFormatter().getUnit(); + + return juce::String { unit.data(), unit.size()}; + } + + int getNumSteps() const override + { + return view.getTransformer().getQuantization(); + } + + bool isDiscrete() const override + { + return getNumSteps() != -1; + } + + bool isBoolean() const override + { + if (auto boolFormatter = dynamic_cast*>(&view.getFormatter())) + { + return true; + } + + return false; + } + + juce::String getText(float normalisedValue, int /*maximumStringLength*/) const override + { + auto& formatter = view.getFormatter(); + auto& transformer = view.getTransformer(); + + std::string output; + + FormattingFlags flags = FormattingFlags::defaultFlags & ~FormattingFlags::includeUnit; + + if (formatter.format(transformer.transform(static_cast(normalisedValue)), output, flags)) + return output; + + return ""; + } + + float getValueForText(const String& text) const override + { + auto& formatter = view.getFormatter(); + auto& transformer = view.getTransformer(); + + ValueType interpretedValue; + if (formatter.interpret(text.toStdString(), interpretedValue)) + { + return static_cast(transformer.normalize(interpretedValue)); + } + + return -1; + } + + bool isAutomatable() const override + { + return view.isParameterAutomated(); + } + + bool isMetaParameter() const override + { + return view.canParameterChangeOthers(); + } + + juce::AudioProcessorParameter::Category getCategory() const override + { + return juce::AudioProcessorParameter::Category::genericParameter; + } + }; + + template + inline void bridgeJuceAudioProcessorParameters(juce::AudioProcessor& processor, ParameterGroup& group) + { + for (auto& param : group) + { + processor.addParameter(new JuceAudioParameter(param)); + } + } +}; + +#endif diff --git a/infrastructure/parameters/ParameterSystem.h b/infrastructure/parameters/ParameterSystem.h index 47b85b1..4b188b1 100644 --- a/infrastructure/parameters/ParameterSystem.h +++ b/infrastructure/parameters/ParameterSystem.h @@ -17,7 +17,7 @@ namespace cpl class ZeroOneClamper { public: - T operator()(T arg) { return std::max((T)0, std::min((T)1, arg)); } + T operator()(T arg) { return std::clamp(arg, (T)0, (T)1); } }; @@ -36,7 +36,7 @@ namespace cpl } - ThreadedParameter(ThreadedParameter && other) + ThreadedParameter(ThreadedParameter && other) noexcept : value(other.value.load(LoadOrdering)), transformer(other.transformer), name(std::move(other.name)) { @@ -179,11 +179,6 @@ namespace cpl }; - - - - - template class ParameterGroup : public CSerializer::Serializable @@ -196,6 +191,7 @@ namespace cpl typedef typename BaseParameter::Formatter Formatter; typedef InternalFrameworkType FrameworkType; + typedef ParameterGroup QualifiedGroup; static const Parameters::Handle InvalidHandle = -1; @@ -221,7 +217,7 @@ namespace cpl class UIListener { public: - virtual void parameterChangedUI(Parameters::Handle localHandle, Parameters::Handle globalHandle, ParameterView * parameter) = 0; + virtual void parameterChangedUI(Parameters::Handle localHandle, Parameters::Handle globalHandle, ParameterView* parameter) = 0; virtual ~UIListener() {} }; @@ -232,7 +228,7 @@ namespace cpl class RTListener { public: - virtual void parameterChangedRT(Parameters::Handle localHandle, Parameters::Handle globalHandle, BaseParameter * param) = 0; + virtual void parameterChangedRT(Parameters::Handle localHandle, Parameters::Handle globalHandle, BaseParameter* param) = 0; virtual ~RTListener() {} }; @@ -244,8 +240,8 @@ namespace cpl typedef T ValueType; typedef BaseParameter ParameterType; ParameterView( - QualifiedGroup * parentToRef, - BaseParameter * parameterToRef, + QualifiedGroup* parentToRef, + BaseParameter* parameterToRef, Parameters::Handle handleOfThis, bool paramIsAutomatable = true, bool paramCanChangeOthers = false, @@ -261,7 +257,7 @@ namespace cpl } - ParameterView(ParameterView && other) + ParameterView(ParameterView&& other) : parent(other.parent) , handle(other.handle) , parameter(other.parameter) @@ -273,14 +269,14 @@ namespace cpl } - ParameterType * getParameter() noexcept { return parameter; } - const std::string & getNameContext() const { return nameContext; } + ParameterType* getParameter() noexcept { return parameter; } + const std::string& getNameContext() const { return nameContext; } std::string getExportedName() { return parent->prefix + nameContext + parameter->getName(); } const std::string& getLocalName() { return parameter->getName(); } const std::string& getParentPrefix() const { return parent->getExportPrefix(); } Parameters::Handle getHandle() { return handle; } - void addListener(UIListener * listener) { parent->addUIListener(handle, listener); } - void removeListener(UIListener * listener) { parent->removeUIListener(handle, listener); } + void addListener(UIListener* listener) { parent->addUIListener(handle, listener); } + void removeListener(UIListener* listener) { parent->removeUIListener(handle, listener); } void updateFromUINormalized(ValueType value, Parameters::UpdateFlagsT flags = Parameters::UpdateFlags::All) { @@ -330,27 +326,30 @@ namespace cpl { std::string buf; parameter->getFormatter().format(parameter->getTransformer().transform(parameter->getValue()), buf); - return std::move(buf); + return buf; } - Formatter & getFormatter() { return parameter->getFormatter(); } - Transformer & getTransformer() { return parameter->getTransformer(); } + Formatter& getFormatter() { return parameter->getFormatter(); } + Transformer& getTransformer() { return parameter->getTransformer(); } + + bool isParameterAutomated() const { return isAutomatable; } + bool canParameterChangeOthers() const { return canChangeOthers; } private: std::string nameContext; Parameters::Handle handle; - BaseParameter * parameter; + BaseParameter* parameter; bool isAutomatable; bool canChangeOthers; ABoolFlag changedFromProcessor; - std::set uiListeners; - QualifiedGroup * parent; + std::set uiListeners; + QualifiedGroup* parent; }; - ParameterGroup(std::string name, std::string exportPrefix, AutomatedProcessor & processorToAutomate, int parameterOffset = 0) + ParameterGroup(std::string name, std::string exportPrefix, AutomatedProcessor& processorToAutomate, int parameterOffset = 0) : processor(processorToAutomate), offset(parameterOffset), groupName(std::move(name)), prefix(std::move(exportPrefix)), isSealed(false) { bundleInstalledReferences = std::make_unique>(); @@ -359,7 +358,7 @@ namespace cpl int getOffset() const noexcept { return offset; } - void serialize(CSerializer::Archiver & archive, Version v) override + void serialize(CSerializer::Archiver& archive, Version v) override { if (!isSealed) CPL_RUNTIME_EXCEPTION("Parameter system being serialized without being sealed"); @@ -373,7 +372,7 @@ namespace cpl } } - void deserialize(CSerializer::Builder & builder, Version v) override + void deserialize(CSerializer::Builder& builder, Version v) override { if (!isSealed) CPL_RUNTIME_EXCEPTION("Parameter system being deserialized without being sealed"); @@ -395,7 +394,7 @@ namespace cpl /// You can for instance store all your parameters, formatters and transformers in a /// class deriving from the Parameters::UserContent. /// - void setUserData(Parameters::UserContent * content, bool releaseOnDestruction = true) + void setUserData(Parameters::UserContent* content, bool releaseOnDestruction = true) { userContent.reset(content); userContent.get_deleter().doDelete = !!releaseOnDestruction; @@ -404,7 +403,7 @@ namespace cpl /// /// Returns anything previously set with setUserData() - as of such, may be null. /// - Parameters::UserContent * getUserContent() noexcept + Parameters::UserContent* getUserContent() noexcept { return userContent.get(); } @@ -413,7 +412,7 @@ namespace cpl /// This function must only be called during initialization, ie. before any audio callbacks are done. /// Additionally, it only makes sense to call it on the UI thread. /// - Parameters::Handle registerParameter(BaseParameter * param, bool shouldBeAutomatable = true, bool canChangeOthers = false, std::string nameContext = "") + Parameters::Handle registerParameter(BaseParameter* param, bool shouldBeAutomatable = true, bool canChangeOthers = false, std::string nameContext = "") { if (isSealed) CPL_RUNTIME_EXCEPTION("Parameters registered to the system while it's sealed"); @@ -423,21 +422,21 @@ namespace cpl } - void registerParameterBundle(Parameters::BundleUpdate * bundle, std::string contextStack = "") + void registerParameterBundle(Parameters::BundleUpdate* bundle, std::string contextStack = "") { contextStack += bundle->getBundleContext(); bundle->generateInfo(); - auto & parameters = bundle->queryParameters(); - for (auto & parameter : parameters) + auto& parameters = bundle->queryParameters(); + for (auto& parameter : parameters) { parameter.handle = registerParameter(parameter.parameter, parameter.shouldBeAutomatable, parameter.canChangeOthers, contextStack); } - bundleInstalledReferences.get()->push_back({bundle, ¶meters}); + bundleInstalledReferences.get()->push_back({ bundle, ¶meters }); if (auto bundleChilds = bundle->getNestedChilds()) { - for (auto & childBundle : *bundleChilds) + for (auto& childBundle : *bundleChilds) { registerParameterBundle(childBundle, contextStack); } @@ -445,7 +444,7 @@ namespace cpl } - void registerSingleParameter(Parameters::SingleUpdate * singleRef) + void registerSingleParameter(Parameters::SingleUpdate* singleRef) { singleRef->generateInfo(); singleRef->parameterQuery->handle = registerParameter( @@ -453,15 +452,15 @@ namespace cpl singleRef->parameterQuery->shouldBeAutomatable, singleRef->parameterQuery->canChangeOthers ); - singleInstalledReferences.get()->push_back({singleRef, singleRef->parameterQuery}); + singleInstalledReferences.get()->push_back({ singleRef, singleRef->parameterQuery }); } void seal() { isSealed = true; - for (auto & ref : *bundleInstalledReferences.get()) + for (auto& ref : *bundleInstalledReferences.get()) { - for (auto & parameter : *ref.records) + for (auto& parameter : *ref.records) { parameter.uiParameterView = findParameter(parameter.handle); } @@ -469,7 +468,7 @@ namespace cpl ref.parent->parametersInstalled(); } - for (auto & ref : *singleInstalledReferences.get()) + for (auto& ref : *singleInstalledReferences.get()) { ref.record->uiParameterView = findParameter(ref.record->handle); ref.parent->parametersInstalled(); @@ -483,7 +482,7 @@ namespace cpl /// /// Only safe to call on the UI thread. /// - void addUIListener(Parameters::Handle globalHandle, UIListener * listener) + void addUIListener(Parameters::Handle globalHandle, UIListener* listener) { containedParameters.at(globalHandle - offset).uiListeners.insert(listener); } @@ -491,7 +490,7 @@ namespace cpl /// /// Only safe to call on the UI thread. /// - void removeUIListener(Parameters::Handle globalHandle, UIListener * listener) + void removeUIListener(Parameters::Handle globalHandle, UIListener* listener) { containedParameters.at(globalHandle - offset).uiListeners.erase(listener); } @@ -501,9 +500,9 @@ namespace cpl /// If spin is set, the function will always succeed but may spin. May allocate memory. /// If not, the return value indicates whether the operation succeeded. /// - bool addRTListener(RTListener * listener, bool spin = true) + bool addRTListener(RTListener* listener, bool spin = true) { - for (auto & slot : realtimeListeners) + for (auto& slot : realtimeListeners) { auto slotListener = slot.listener.load(std::memory_order_acquire); if (slotListener == listener) @@ -542,9 +541,9 @@ namespace cpl /// If spin is set, the function will always succeed but may spin. /// If not, the return value indicates whether the operation succeeded. /// - bool removeRTListener(RTListener * listener, bool spin = true) + bool removeRTListener(RTListener* listener, bool spin = true) { - for (auto & slot : realtimeListeners) + for (auto& slot : realtimeListeners) { auto slotListener = slot.listener.load(std::memory_order_acquire); @@ -582,7 +581,7 @@ namespace cpl /// void updateFromProcessorNormalized(Parameters::Handle globalHandle, T value, Parameters::UpdateFlagsT flags = Parameters::UpdateFlags::All) { - ParameterView & p = containedParameters.at(globalHandle - offset); + ParameterView& p = containedParameters.at(globalHandle - offset); p.parameter->setValue(value); value = p.parameter->getValue(); @@ -606,7 +605,7 @@ namespace cpl /// void updateFromHostNormalized(Parameters::Handle globalHandle, T value, Parameters::UpdateFlagsT flags = Parameters::UpdateFlags::All) { - ParameterView & p = containedParameters.at(globalHandle - offset); + ParameterView& p = containedParameters.at(globalHandle - offset); p.parameter->setValue(value); @@ -625,7 +624,7 @@ namespace cpl /// void updateFromUINormalized(Parameters::Handle globalHandle, T value, Parameters::UpdateFlagsT flags = Parameters::UpdateFlags::All) { - ParameterView & p = containedParameters.at(globalHandle - offset); + ParameterView& p = containedParameters.at(globalHandle - offset); p.parameter->setValue(value); value = p.parameter->getValue(); @@ -719,7 +718,7 @@ namespace cpl return containedParameters.size(); } - ParameterView * findParameter(Parameters::Handle globalHandle) + ParameterView* findParameter(Parameters::Handle globalHandle) { if (!isSealed) CPL_RUNTIME_EXCEPTION("ParameterView being acquired while the system isn't sealed"); @@ -732,15 +731,18 @@ namespace cpl return nullptr; } - ParameterView * findParameter(const std::string_view name) noexcept + ParameterView* findParameter(const std::string_view name) noexcept { if (!isSealed) CPL_RUNTIME_EXCEPTION("ParameterView being acquired while the system isn't sealed"); return findParameter(mapName(name)); } - const std::string & getName() const noexcept { return groupName; } - const std::string & getExportPrefix() const noexcept { return prefix; } + const std::string& getName() const noexcept { return groupName; } + const std::string& getExportPrefix() const noexcept { return prefix; } + + typename std::vector::iterator begin() { return containedParameters.begin(); } + typename std::vector::iterator end() { return containedParameters.end(); } private: diff --git a/infrastructure/values/WindowDesignValue.h b/infrastructure/values/WindowDesignValue.h index 2ce7634..1884b9d 100644 --- a/infrastructure/values/WindowDesignValue.h +++ b/infrastructure/values/WindowDesignValue.h @@ -37,7 +37,7 @@ namespace cpl this->setQuantization(enum_cast(cpl::dsp::WindowTypes::End) - 1); } - virtual bool format(const ValueType & val, std::string & buf) override + virtual bool format(const ValueType & val, std::string & buf, FormattingFlags flags) override { auto wtype = enum_cast(val); buf = dsp::Windows::stringFromEnum(wtype); @@ -70,12 +70,15 @@ namespace cpl { public: - virtual bool format(const ValueType & val, std::string & buf) override + virtual bool format(const ValueType & val, std::string & buf, FormattingFlags flags) override { char buffer[1000]; - sprintfs(buffer, u8"%d dB (%.1f\u03B1)", int(std::round(val)), val / 20); - buf = buffer; - return true; + if (sprintfs(buffer, u8"%d dB (%.1f\u03B1)", int(std::round(val)), val / 20) > 0) + { + buf = buffer; + return true; + } + return false; } }; diff --git a/lib/CDataBuffer.h b/lib/CDataBuffer.h index 106ddec..1be721f 100644 --- a/lib/CDataBuffer.h +++ b/lib/CDataBuffer.h @@ -36,7 +36,9 @@ #ifndef _CDATABUFFER_H #define _CDATABUFFER_H + #include "../MacroConstants.h" +#include "../Misc.h" #include #include #include @@ -98,8 +100,8 @@ namespace cpl CDataBuffer(const CDataBuffer & other) : buffer(nullptr), bufSize(0) { - resize(other.size); - std::memcpy(buffer, other.buffer, other.size * sizeof(T)); + resize(other.size()); + std::memcpy(buffer, other.buffer, other.size() * sizeof(T)); } CDataBuffer(CDataBuffer && other) @@ -113,8 +115,8 @@ namespace cpl CDataBuffer & operator = (const CDataBuffer & other) { clear(); - resize(other.size); - std::memcpy(buffer, other.buffer, other.size * sizeof(T)); + resize(other.size()); + std::memcpy(buffer, other.buffer, other.size() * sizeof(T)); return *this; } @@ -251,4 +253,4 @@ namespace cpl }; }; -#endif \ No newline at end of file +#endif diff --git a/octave/signal/octave_signal_ultrawin.cpp b/octave/signal/octave_signal_ultrawin.cpp index 93adfa9..e7c8bae 100644 --- a/octave/signal/octave_signal_ultrawin.cpp +++ b/octave/signal/octave_signal_ultrawin.cpp @@ -58,7 +58,7 @@ namespace octave T c = 1 - 1 / (xmu * xmu), t, u, v[64], vp, s; for (i = 0; i < (int)(sizeof(v) / sizeof(v[0])); v[i++] = 0); if (n > 1) for (i = 0; i < m; l = j - (j <= i++)) { - vp = *v, s = *v = i? (*v + v[1]) * mu * (divs[i] = (T)1/i) : 1; + vp = *v, s = *v = i? (*v + v[1]) * mu * (divs[i] = (TOut)1/i) : 1; for (met = 0, j = 1, u = 1; ; ++l, v[l] = vp * (i - l) / (mu + l - 1)) { #define _ t = v[j], v[j] += vp, vp = t, t = s, s += \ v[j] * (u *= c * (n - i - j) * divs[j]), met = s && s == t, ++j, @@ -67,11 +67,11 @@ namespace octave #undef _ if (met || !(j <= i)) break; } - w[i] = s / (n - i - 1); + w[i] = static_cast(s / (n - i - 1)); } else w[0] = 1; u = 1 / w[i = m - 1], w[i] = 1; - for (--i ; i >= 0; u *= (n - 2 - i + mu) / (n - 2 - i), w[i] *= u, --i); + for (--i ; i >= 0; u *= (n - 2 - i + mu) / (n - 2 - i), w[i] = static_cast(w[i] * u), --i); for (i = 0; i < m; w[n - 1 - i] = w[i], ++i); } } @@ -238,7 +238,7 @@ namespace octave T t = 0, s = -1; x = x? (T)M_PI/2 - std::acos(x/xmu) : 0; for (; i >= 0; t += w[i] * (T)1. / (j + 1) * (s=-s) * (x?cos(j*x):1), --i, j += 2); - for (t = (T)M_PI/4 / t, i = 0; t < 1 && i < n; w[i] *= t, ++i); + for (t = (T)M_PI/4 / t, i = 0; t < 1 && i < n; w[i] = TOut(w[i] * t), ++i); #if DEBUG_ULTRWIN fprintf(stderr, "%snorm DFT(w.sinc πx) @ %g %.16g\n", t<1? "":"NO ", 2*x,t); #endif diff --git a/process/ProcessUtil.h b/process/ProcessUtil.h index b5abdea..6250417 100755 --- a/process/ProcessUtil.h +++ b/process/ProcessUtil.h @@ -52,7 +52,7 @@ namespace cpl NullableHandle(T h) : handle(h) {} NullableHandle(std::nullptr_t) : handle(null()) {} - operator T() { return handle; } + operator T() const noexcept { return handle; } bool operator ==(const NullableHandle &other) const { return handle == other.handle; } bool operator !=(const NullableHandle &other) const { return handle != other.handle; } diff --git a/rendering/CDisplaySetup.cpp b/rendering/CDisplaySetup.cpp index 01c18ac..70b7eca 100755 --- a/rendering/CDisplaySetup.cpp +++ b/rendering/CDisplaySetup.cpp @@ -27,16 +27,13 @@ *************************************************************************************/ -#include "CDisplaySetup.h" #include "../MacroConstants.h" -#include "../gui/Tools.h" - - #ifdef CPL_WINDOWS #include "DisplayOrientationWindows.cpp" #elif defined(CPL_MAC) -#include -#elif defined(CPL_UNIXC) +#include +#include +#elif defined(CPL_UNIXC) #include "DisplayOrientationLinux.cpp" #else // add linux, osx, android, ios here. @@ -45,6 +42,9 @@ #include "../PlatformSpecific.h" #include "../MacSupport.h" +#include "CDisplaySetup.h" +#include "../gui/Tools.h" + namespace cpl { namespace rendering @@ -70,7 +70,7 @@ namespace cpl } } return getMainDisplay(); - } + } const CDisplaySetup::DisplayData & CDisplaySetup::displayFromIndex(std::size_t index) const { @@ -360,4 +360,4 @@ namespace cpl }; // {} rendering -}; // {} cpl +}; // {} cpl diff --git a/rendering/CDisplaySetup.h b/rendering/CDisplaySetup.h index 139412a..c6100a0 100755 --- a/rendering/CDisplaySetup.h +++ b/rendering/CDisplaySetup.h @@ -31,8 +31,9 @@ #ifndef _CDISPLAYSETUP_H #define _CDISPLAYSETUP_H -#include "../Common.h" + #include "SubpixelRendering.h" +#include "../gui/Tools.h" #include #include @@ -164,4 +165,4 @@ namespace cpl }; // {} rendering }; // {} cpl -#endif \ No newline at end of file +#endif diff --git a/rendering/COpenGLImage.h b/rendering/COpenGLImage.h index b366cff..1bf47ba 100644 --- a/rendering/COpenGLImage.h +++ b/rendering/COpenGLImage.h @@ -60,12 +60,12 @@ namespace cpl glPushMatrix(); glTranslatef(-1, -1, 0); - glScalef(2.0 / (image.width), 2.0 / (image.height), 1.0); + glScalef(2.0f / (image.width), 2.0f / (image.height), 1.0f); glGetIntegerv(GL_MATRIX_MODE, &matrixMode); glMatrixMode(GL_TEXTURE); glPushMatrix(); - glScalef(1.0 / image.textureWidth, 1.0 / image.textureHeight, 1.0); + glScalef(1.0f / image.textureWidth, 1.0f / image.textureHeight, 1.0f); img.bind(); @@ -172,7 +172,7 @@ namespace cpl void createEmptyImage() { - if (fillColour.getPixelARGB().getInRGBAMemoryOrder() == 0) // background colour is black. + if (fillColour.getPixelARGB().getInARGBMemoryOrder() == 0) // background colour is black. { juce::Image newContents(juceFormat, static_cast(textureWidth), static_cast(textureHeight), true); loadImageInternal(newContents); @@ -196,7 +196,7 @@ namespace cpl void load() { loadImageInternal(currentContents); - currentContents = juce::Image::null; + currentContents = juce::Image(); } /// @@ -224,7 +224,7 @@ namespace cpl /// void release() { - currentContents = juce::Image::null; + currentContents = juce::Image(); releaseTexture(); } @@ -296,7 +296,7 @@ namespace cpl auto sourceHeight = sourceBot - sourceTop; - auto r = [](double in) { return cpl::Math::round(in); }; + auto r = [](auto in) { return cpl::Math::round(static_cast(in)); }; juce::Image upload(juceFormat, static_cast(width), static_cast(height), false); { juce::Graphics g(upload); diff --git a/rendering/CSubpixelScanlineRenderer.h b/rendering/CSubpixelScanlineRenderer.h index 1cf43dc..89d0c95 100755 --- a/rendering/CSubpixelScanlineRenderer.h +++ b/rendering/CSubpixelScanlineRenderer.h @@ -88,7 +88,7 @@ namespace cpl : data(data), y(0), - origin((int)cpl::Math::floorToNInf(where.x), cpl::Math::round(where.y)), + origin(cpl::Math::floorToNInf(where.x), cpl::Math::round(where.y)), alphaMap(), alphaPos(-1), subXOff(cpl::Math::round(cpl::Math::frac(where.x) * 3)), diff --git a/rendering/CSubpixelSoftwareGraphics.cpp b/rendering/CSubpixelSoftwareGraphics.cpp index af8f03f..0be29ef 100755 --- a/rendering/CSubpixelSoftwareGraphics.cpp +++ b/rendering/CSubpixelSoftwareGraphics.cpp @@ -27,6 +27,8 @@ *************************************************************************************/ +#ifdef CPL_ENABLE_CSUBPIXELGRAPHICS + #include "CSubpixelSoftwareGraphics.h" #include "SubpixelRendering.h" #include "CSubpixelScanlineRenderer.h" @@ -113,12 +115,11 @@ namespace cpl const juce::RectangleList & initialClip, bool allowAlphaDrawing ) - : - buffer(imageToRenderOn), - origin(origin), - startingClip(initialClip), - displayInfo(CDisplaySetup::instance()), - LowLevelGraphicsSoftwareRenderer(imageToRenderOn, origin, initialClip) + : LowLevelGraphicsSoftwareRenderer(imageToRenderOn, origin, initialClip) + , buffer(imageToRenderOn) + , origin(origin) + , startingClip(initialClip) + , displayInfo(CDisplaySetup::instance()) { // throw if we're given an alpha image. // note that technically it doesn't make sense to draw on an ARGB image @@ -133,22 +134,26 @@ namespace cpl } } - - void CSubpixelSoftwareGraphics::drawGlyph(int glyphNumber, const AffineTransform & z) + void CSubpixelSoftwareGraphics::drawGlyphs( + juce::Span glyphs, + juce::Span> positions, + const juce::AffineTransform& t) { - if (!tryToDrawGlyph(glyphNumber, z)) - return juce::LowLevelGraphicsSoftwareRenderer::drawGlyph(glyphNumber, z); + using namespace juce; + + for (std::size_t i = 0; i < glyphs.size(); ++i) + { + auto affine = AffineTransform::translation(positions[i]).followedBy(t); + if (!tryToDrawGlyph(glyphs[i], affine)) + LowLevelGraphicsSoftwareRenderer::drawGlyph(glyphs[i], affine); + } } - bool CSubpixelSoftwareGraphics::tryToDrawGlyph(int glyphNumber, const AffineTransform & z) + bool CSubpixelSoftwareGraphics::tryToDrawGlyph(int glyphNumber, const juce::AffineTransform & z) { - using namespace juce::RenderingHelpers; - typedef CachedGlyphEdgeTable GlyphType; - typedef GlyphCache GlyphCacheType; - // only colours supported now if (!stack->fillType.isColour()) return false; @@ -161,7 +166,7 @@ namespace cpl // obtain transform auto & transform = stack->transform; // starting point - Point pos(z.getTranslationX(), z.getTranslationY()); + juce::Point pos(z.getTranslationX(), z.getTranslationY()); // find what display our glyph resides on. // Note this is wrong - need to somehow figure out the global position @@ -174,13 +179,24 @@ namespace cpl if (glyphSpansMultipleMonitors || !currentMonitor.isApplicableForSubpixels || !currentMonitor.isDuplicatesCompatible) return false; - // get the glyph outlines - std::unique_ptr> outlines; + // lcd ordering of pixels + auto matrixOrder = currentMonitor.displayMatrixOrder; + // here we handle the current rotation of this monitor. + // if it is RGB but rotated PI radians, we simply invert the pixel + // matrix orientation to BGR! If it is rotated inbetween, well, + // we wouldn't have reached this point! Then + // currentMonitor.isApplicableForSubpixels will be false. + if (currentMonitor.screenRotation == 180) + { + matrixOrder = InvertedMatrixOrientation(matrixOrder); + } + + // get the glyph outlines if (z.isOnlyTranslation() && !transform.isRotated) { // get the glyph from the cache - GlyphCacheType& cache = GlyphCacheType::getInstance(); + auto& cache = GlyphCache::getInstance(); // get the current font and scale it 3 times horizontally. font.setHorizontalScale(font.getHorizontalScale() * 3); @@ -201,27 +217,31 @@ namespace cpl pos += transform.offset.toFloat(); } - if (juce::ReferenceCountedObjectPtr glyphRef = cache.findOrCreateGlyph(font, glyphNumber)) + for (auto& layer : cache.get(font, glyphNumber)) { - // increase cache locality - glyphRef->lastAccessCount++; + if (auto* colourLayer = std::get_if(&layer.layer)) + { + const auto initialFill = stack->fillType; + const ScopeGuard scope{ [&] { this->stack->setFillType(initialFill); } }; - outlines.reset(glyphRef->edgeTable); - // dont delete this instance - manged by the glyphcache - outlines.get_deleter().shared = true; - } - else - { - // maybe create a new? but this indicates something is wrong.. - return false; - } + if (auto fill = colourLayer->colour) + stack->setFillType(*fill); + if (!RenderEdgeLayer(matrixOrder, pos, colourLayer->clip)) + return false; + } + else + { + // contains emojis or something + return false; + } + } } else // here we have to create a new edgetable - it is rotated/skewed. { // fix this code. looks funny. return false; - + /* auto font = getFont(); font.setHorizontalScale(font.getHorizontalScale() * 3); const float fontHeight = font.getHeight(); @@ -234,31 +254,20 @@ namespace cpl // delete it later. outlines.get_deleter().shared = false; + */ } - // hmm? - if (!outlines.get()) - return false; + return true; + } + bool CSubpixelSoftwareGraphics::RenderEdgeLayer(LCDMatrixOrientation matrixOrder, const juce::Point& pos, const juce::EdgeTable& outlines) + { // the colour to draw with - auto & colour = stack->fillType.colour; + auto& colour = stack->fillType.colour; // obtain bitmap buffer. Image::BitmapData destData(buffer, Image::BitmapData::readWrite); - // lcd ordering of pixels - auto matrixOrder = currentMonitor.displayMatrixOrder; - - // here we handle the current rotation of this monitor. - // if it is RGB but rotated PI radians, we simply invert the pixel - // matrix orientation to BGR! If it is rotated inbetween, well, - // we wouldn't have reached this point! Then - // currentMonitor.isApplicableForSubpixels will be false. - if (currentMonitor.screenRotation == 180) - { - matrixOrder = InvertedMatrixOrientation(matrixOrder); - } - // we support a custom gamma scale for each monitor (see currentMonitor.gammaScale), but since in practice // it isn't really used (and the code doesn't work yet) we just create a simple // gamma correction here - the same used in JUCE, albeit adjusts itself to the system setting... @@ -272,68 +281,68 @@ namespace cpl // create the correct renderer switch (matrixOrder) { - case LCDMatrixOrientation::RGB: + case LCDMatrixOrientation::RGB: + { + // create renderer, + switch (buffer.getFormat()) { - // create renderer, - switch (buffer.getFormat()) - { - case juce::Image::RGB: - { - - Renderer - subpixelRenderer(destData, colour, pos, startingClip, gammaScale); - - outlines->iterate(subpixelRenderer); - break; - } - case juce::Image::ARGB: - { - - Renderer - subpixelRenderer(destData, colour, pos, startingClip, gammaScale); - - outlines->iterate(subpixelRenderer); - break; - } - // unknown image type - default: - return false; - } + case juce::Image::RGB: + { + + Renderer + subpixelRenderer(destData, colour, pos, startingClip, gammaScale); + + outlines.iterate(subpixelRenderer); break; + } + case juce::Image::ARGB: + { + + Renderer + subpixelRenderer(destData, colour, pos, startingClip, gammaScale); + outlines.iterate(subpixelRenderer); + break; } - case LCDMatrixOrientation::BGR: + // unknown image type + default: + return false; + } + break; + + } + case LCDMatrixOrientation::BGR: + { + // create renderer, + switch (buffer.getFormat()) { - // create renderer, - switch (buffer.getFormat()) - { - case juce::Image::RGB: - { - - Renderer - subpixelRenderer(destData, colour, pos, startingClip, gammaScale); - - outlines->iterate(subpixelRenderer); - break; - } - case juce::Image::ARGB: - { - - Renderer - subpixelRenderer(destData, colour, pos, startingClip, gammaScale); - - outlines->iterate(subpixelRenderer); - break; - } - // unknown image type - default: - return false; - } + case juce::Image::RGB: + { + + Renderer + subpixelRenderer(destData, colour, pos, startingClip, gammaScale); + + outlines.iterate(subpixelRenderer); + break; + } + case juce::Image::ARGB: + { + + Renderer + subpixelRenderer(destData, colour, pos, startingClip, gammaScale); + + outlines.iterate(subpixelRenderer); break; } - // unknown pixel matrix of screen + // unknown image type default: return false; + } + break; + } + // unknown pixel matrix of screen + default: + return false; } // succesfully rendered something. return true; @@ -341,3 +350,5 @@ namespace cpl }; // {} rendering }; // {} cpl + +#endif \ No newline at end of file diff --git a/rendering/CSubpixelSoftwareGraphics.h b/rendering/CSubpixelSoftwareGraphics.h index fe018d6..bb1d3d5 100755 --- a/rendering/CSubpixelSoftwareGraphics.h +++ b/rendering/CSubpixelSoftwareGraphics.h @@ -30,6 +30,11 @@ #ifndef _CSUBPIXELSOFTWAREGRAPHICS_H #define _CSUBPIXELSOFTWAREGRAPHICS_H + +// #define CPL_ENABLE_CSUBPIXELGRAPHICS 1 + +#ifdef CPL_ENABLE_CSUBPIXELGRAPHICS + #include "../Common.h" #include "SubpixelRendering.h" @@ -41,8 +46,7 @@ namespace cpl class CDisplaySetup; class CSubpixelSoftwareGraphics - : - public juce::LowLevelGraphicsSoftwareRenderer + : public juce::LowLevelGraphicsSoftwareRenderer { public: @@ -53,21 +57,28 @@ namespace cpl virtual ~CSubpixelSoftwareGraphics() {}; // overrides - virtual void drawGlyph(int glyphNumber, const AffineTransform & z) override; + + void drawGlyphs(juce::Span glyphs, + juce::Span> positions, + const juce::AffineTransform& t) override; // the height in points where to stop drawing subpixel aa-glyphs static void setAntialiasingTransition(float heightToStopSubpixels); private: - bool tryToDrawGlyph(int glyphNumber, const AffineTransform & z); + bool tryToDrawGlyph(int glyphNumber, const juce::AffineTransform & z); + bool RenderEdgeLayer(LCDMatrixOrientation monitorMatrix, const juce::Point& pos, const juce::EdgeTable& outlines); static float maxHeight; - Point origin; + juce::Point origin; const juce::Image & buffer; - const RectangleList & startingClip; + const juce::RectangleList & startingClip; CDisplaySetup & displayInfo; }; }; // {} rendering }; // {} cpl + +#endif + #endif \ No newline at end of file diff --git a/rendering/DisplayOrientationMac.cpp b/rendering/DisplayOrientationMac.cpp old mode 100755 new mode 100644 diff --git a/rendering/Graphics.h b/rendering/Graphics.h index 0a0c208..57e4016 100644 --- a/rendering/Graphics.h +++ b/rendering/Graphics.h @@ -31,8 +31,25 @@ #define CPL_GRAPHICS_H #include +#include +#include +#include #include "../PlatformSpecific.h" +#ifdef CPL_JUCE + #include + using namespace juce::gl; +#else + #ifdef __APPLE__ + #define GL_SILENCE_DEPRECATION + #include + #include + #else + #include + #include + #endif +#endif + namespace cpl { // it should be called Graphics, but has n-dimensions appended to avoid @@ -442,10 +459,10 @@ namespace cpl { UPixel ret(*this); - ret.a = std::uint8_t(scale * ret.a); - ret.r = std::uint8_t(scale * ret.r); - ret.g = std::uint8_t(scale * ret.g); - ret.b = std::uint8_t(scale * ret.b); + ret.pixel.a = static_cast(scale * ret.pixel.a); + ret.pixel.r = static_cast(scale * ret.pixel.r); + ret.pixel.g = static_cast(scale * ret.pixel.g); + ret.pixel.b = static_cast(scale * ret.pixel.b); return ret; } diff --git a/rendering/OpenGLRendering.h b/rendering/OpenGLRendering.h index def85f5..5b29f79 100644 --- a/rendering/OpenGLRendering.h +++ b/rendering/OpenGLRendering.h @@ -246,7 +246,11 @@ namespace cpl friend class Rasterizer; COpenGLStack() - : ras(nullptr), blenderWasAltered(false) + : ras(nullptr) + , blenderWasAltered(false) + , oldDestinationBlend() + , oldSourceBlend() + { CPL_DEBUGCHECKGL(); diff --git a/rendering/SubpixelRendering.h b/rendering/SubpixelRendering.h index cd99948..37a79ba 100755 --- a/rendering/SubpixelRendering.h +++ b/rendering/SubpixelRendering.h @@ -32,6 +32,7 @@ #include #include "../Types.h" #include "DisplayOrientation.h" +#include namespace cpl { @@ -353,7 +354,7 @@ namespace cpl typedef std::uint8_t Pixel; // intermediate type for multiplications typedef std::uint16_t IntPixel; - static const std::size_t PixelMax = UCHAR_MAX; + static const std::size_t PixelMax = std::numeric_limits::max(); static const std::size_t size = PixelMax >> 4; typedef std::uint_fast16_t WType; diff --git a/simd/simd_consts.cpp b/simd/simd_consts.cpp old mode 100755 new mode 100644 index 1d1d823..315ee76 --- a/simd/simd_consts.cpp +++ b/simd/simd_consts.cpp @@ -28,150 +28,7 @@ *************************************************************************************/ +#ifndef CPL_COMPILER_MULTIPLE_STATICS_SUPPORTED #include "simd_consts.h" - -namespace cpl -{ - namespace simd - { - - - #ifndef _CPL_COMPILER_MULTIPLE_STATICS_SUPPORTED - #define SQRT_TWO 1.4142135623730950488016887242097 - #define SQRT_HALF_TWO 0.70710678118654752440084436210485 - - #define FILL_8(val, ty) {static_cast(val), static_cast(val), static_cast(val), static_cast(val), \ - static_cast(val), static_cast(val), static_cast(val), static_cast(val)} - #define FILL_4(val, ty) {static_cast(val), static_cast(val), static_cast(val), static_cast(val)} - #define FILL_2(val, ty) {static_cast(val), static_cast(val)} - - - #define DECLARE_SIMD_CONSTS(name, value) \ - template<> const v4sf consts::name = FILL_4(value, float); \ - template<> const v8sf consts::name = FILL_8(value, float); \ - template<> const v2sd consts::name = FILL_2(value, double); \ - template<> const v4sd consts::name = FILL_4(value, double); \ - template<> const float consts::name = (float)(value); \ - template<> const double consts::name = value; - //template<> const v128si consts::name = FILL_4(value, std::int32_t); - - - - - #ifdef CPL_MSVC - struct bit_helper - { - static const std::uint64_t bits = 0xFFFFFFFFFFFFFFFFULL; - // 32-bit IEEE floating point sign mask - static const std::uint32_t fsm = 0x7FFFFFFFU; - // 64-bit IEEE floating point sign mask - static const std::uint64_t dsm = 0x7FFFFFFFFFFFFFFFULL; - #else - namespace bit_helper - { - static const std::uint64_t bits = 0xFFFFFFFFFFFFFFFFULL; - // 32-bit IEEE floating point sign mask - static const std::uint32_t fsm = 0x7FFFFFFFU; - // 64-bit IEEE floating point sign mask - static const std::uint64_t dsm = 0x7FFFFFFFFFFFFFFFULL; - - #endif - }; - - // standards - DECLARE_SIMD_CONSTS(pi, M_PI); - DECLARE_SIMD_CONSTS(e, M_E); - DECLARE_SIMD_CONSTS(tau, M_PI * 2); - DECLARE_SIMD_CONSTS(pi_half, M_PI / 2); - DECLARE_SIMD_CONSTS(pi_quarter, M_PI / 4); - DECLARE_SIMD_CONSTS(four_over_pi, 4 / M_PI); - DECLARE_SIMD_CONSTS(zero, 0.0); - DECLARE_SIMD_CONSTS(two, 2.0); - DECLARE_SIMD_CONSTS(four, 4.0); - DECLARE_SIMD_CONSTS(one, 1.0); - DECLARE_SIMD_CONSTS(minus_one, -1.0); - DECLARE_SIMD_CONSTS(minus_two, -2.0); - DECLARE_SIMD_CONSTS(half, 0.5); - DECLARE_SIMD_CONSTS(quarter, 0.25); - DECLARE_SIMD_CONSTS(sqrt_two, SQRT_TWO); - DECLARE_SIMD_CONSTS(sqrt_half_two, SQRT_HALF_TWO); - DECLARE_SIMD_CONSTS(sqrt_half_two_minus, -SQRT_HALF_TWO); - DECLARE_SIMD_CONSTS(sign_bit, -0.0); - - // cephes magic numbers - DECLARE_SIMD_CONSTS(cephes_e__4, 1.0e-4); - DECLARE_SIMD_CONSTS(cephes_small, 1.0e-35); - DECLARE_SIMD_CONSTS(cephes_2414, 2.414213562373095); - DECLARE_SIMD_CONSTS(cephes_0414, 0.4142135623730950); - DECLARE_SIMD_CONSTS(cephes_8053, 8.05374449538e-2); - DECLARE_SIMD_CONSTS(cephes_1387, 1.38776856032E-1); - DECLARE_SIMD_CONSTS(cephes_1997, 1.99777106478E-1); - DECLARE_SIMD_CONSTS(cephes_3333, 3.33329491539E-1); - // extended precision arithmetic - DECLARE_SIMD_CONSTS(cephes_mdp1, -0.78515625); - DECLARE_SIMD_CONSTS(cephes_mdp2, -2.4187564849853515625e-4); - DECLARE_SIMD_CONSTS(cephes_mdp3, -3.77489497744594108e-8); - // sine/cosine calculation coefficients - DECLARE_SIMD_CONSTS(cephes_sin_p0, -1.9515295891E-4); - DECLARE_SIMD_CONSTS(cephes_sin_p1, 8.3321608736E-3); - DECLARE_SIMD_CONSTS(cephes_sin_p2, -1.6666654611E-1); - DECLARE_SIMD_CONSTS(cephes_cos_p0, 2.443315711809948E-005); - DECLARE_SIMD_CONSTS(cephes_cos_p1, -1.388731625493765E-003); - DECLARE_SIMD_CONSTS(cephes_cos_p2, 4.166664568298827E-002); - - // epsilon - template<> const v4sf consts::epsilon = FILL_4(FLT_EPSILON, float); - template<> const v8sf consts::epsilon = FILL_8(FLT_EPSILON, float); - template<> const v2sd consts::epsilon = FILL_2(DBL_EPSILON, double); - template<> const v4sd consts::epsilon = FILL_4(DBL_EPSILON, double); - // min - template<> const v4sf consts::min = FILL_4(FLT_MIN, float); - template<> const v8sf consts::min = FILL_8(FLT_MIN, float); - template<> const v2sd consts::min = FILL_2(DBL_MIN, double); - template<> const v4sd consts::min = FILL_4(DBL_MIN, double); - // min - template<> const v4sf consts::max = FILL_4(FLT_MAX, float); - template<> const v8sf consts::max = FILL_8(FLT_MAX, float); - template<> const v2sd consts::max = FILL_2(DBL_MAX, double); - template<> const v4sd consts::max = FILL_4(DBL_MAX, double); - - - // all_bits - need a better solution here, that doesn't involve intrinsics. - - template<> const float consts::all_bits = *(float*)&bit_helper::bits; - template<> const double consts::all_bits = *(double*)&bit_helper::bits; - - template<> const v4sf consts::all_bits = {*(float*)&bit_helper::bits, *(float*)&bit_helper::bits, - *(float*)&bit_helper::bits, *(float*)&bit_helper::bits}; - template<> const v8sf consts::all_bits = {*(float*)&bit_helper::bits, *(float*)&bit_helper::bits, - *(float*)&bit_helper::bits, *(float*)&bit_helper::bits, - *(float*)&bit_helper::bits, *(float*)&bit_helper::bits, - *(float*)&bit_helper::bits, *(float*)&bit_helper::bits}; - template<> const v2sd consts::all_bits = {*(double*)&bit_helper::bits, *(double*)&bit_helper::bits}; - template<> const v4sd consts::all_bits = {*(double*)&bit_helper::bits, *(double*)&bit_helper::bits, - *(double*)&bit_helper::bits, *(double*)&bit_helper::bits}; - - // sign mask - need a better solution here, that doesn't involve intrinsics. - template<> const float consts::sign_mask = *(float*)&bit_helper::fsm; - template<> const double consts::sign_mask = *(double*)&bit_helper::dsm; - - template<> const v4sf consts::sign_mask = {*(float*)&bit_helper::fsm, *(float*)&bit_helper::fsm, - *(float*)&bit_helper::fsm, *(float*)&bit_helper::fsm}; - template<> const v8sf consts::sign_mask = {*(float*)&bit_helper::fsm, *(float*)&bit_helper::fsm, - *(float*)&bit_helper::fsm, *(float*)&bit_helper::fsm, - *(float*)&bit_helper::fsm, *(float*)&bit_helper::fsm, - *(float*)&bit_helper::fsm, *(float*)&bit_helper::fsm}; - template<> const v2sd consts::sign_mask = {*(double*)&bit_helper::dsm, *(double*)&bit_helper::dsm}; - template<> const v4sd consts::sign_mask = {*(double*)&bit_helper::dsm, *(double*)&bit_helper::dsm, - *(double*)&bit_helper::dsm, *(double*)&bit_helper::dsm}; - - #undef FILL_8 - #undef FILL_4 - #undef FILL_2 - #undef SQRT_TWO - #undef SQRT_HALF - #undef DECLARE_SIMD_CONSTS - #endif - }; // simd - }; // cpl - +#include "simd_data.inl" +#endif diff --git a/simd/simd_consts.h b/simd/simd_consts.h index a3c373f..2b0cc01 100644 --- a/simd/simd_consts.h +++ b/simd/simd_consts.h @@ -2,7 +2,7 @@ cpl - cross-platform library - v. 0.1.0. - Copyright (C) 2016 Janus Lynggaard Thorborg (www.jthorborg.com) + Copyright (C) 2026 Janus Lynggaard Thorborg (www.jthorborg.com) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -21,10 +21,9 @@ ************************************************************************************** - file:simdmath.h + file:simd_consts.h - Standard math library routines like trigonometry and stuff. - Most is derived from the old cephes library. + Declarations of common simd constants in constant memory. *************************************************************************************/ @@ -52,6 +51,7 @@ namespace cpl static const V tau; static const V pi_half; static const V pi_quarter; + static const V pi_squared; static const V four_over_pi; static const V one; static const V minus_one; @@ -92,136 +92,12 @@ namespace cpl static const V cephes_cos_p1; static const V cephes_cos_p2; }; - - - #ifdef _CPL_COMPILER_MULTIPLE_STATICS_SUPPORTED - #define SQRT_TWO 1.4142135623730950488016887242097 - #define SQRT_HALF_TWO 0.70710678118654752440084436210485 - - #define FILL_8(val, ty) {static_cast(val), static_cast(val), static_cast(val), static_cast(val), \ - static_cast(val), static_cast(val), static_cast(val), static_cast(val)} - #define FILL_4(val, ty) {static_cast(val), static_cast(val), static_cast(val), static_cast(val)} - #define FILL_2(val, ty) {static_cast(val), static_cast(val)} - - - #define DECLARE_SIMD_CONSTS(name, value) \ - template<> const v4sf consts::name = FILL_4(value, float); \ - template<> const v8sf consts::name = FILL_8(value, float); \ - template<> const v2sd consts::name = FILL_2(value, double); \ - template<> const v4sd consts::name = FILL_4(value, double); \ - //template<> const v128si consts::name = FILL_4(value, std::int32_t); - - - - - #ifdef CPL_MSVC - struct bit_helper - { - static const std::uint64_t bits = 0xFFFFFFFFFFFFFFFFULL; - // 32-bit IEEE floating point sign mask - static const std::uint32_t fsm = 0x7FFFFFFFU; - // 64-bit IEEE floating point sign mask - static const std::uint64_t dsm = 0x7FFFFFFFFFFFFFFFULL; - #else - namespace bit_helper - { - static const std::uint64_t bits = 0xFFFFFFFFFFFFFFFFULL; - // 32-bit IEEE floating point sign mask - static const std::uint32_t fsm = 0x7FFFFFFFU; - // 64-bit IEEE floating point sign mask - static const std::uint64_t dsm = 0x7FFFFFFFFFFFFFFFULL; - - #endif - }; - - // standards - DECLARE_SIMD_CONSTS(pi, M_PI); - DECLARE_SIMD_CONSTS(e, M_E); - DECLARE_SIMD_CONSTS(tau, M_PI * 2); - DECLARE_SIMD_CONSTS(pi_half, M_PI / 2); - DECLARE_SIMD_CONSTS(pi_quarter, M_PI / 4); - DECLARE_SIMD_CONSTS(four_over_pi, 4 / M_PI); - DECLARE_SIMD_CONSTS(zero, 0.0); - DECLARE_SIMD_CONSTS(two, 2.0); - DECLARE_SIMD_CONSTS(four, 4.0); - DECLARE_SIMD_CONSTS(one, 1.0); - DECLARE_SIMD_CONSTS(minus_one, -1.0); - DECLARE_SIMD_CONSTS(minus_two, -1.0); - DECLARE_SIMD_CONSTS(half, 0.5); - DECLARE_SIMD_CONSTS(quarter, 0.25); - DECLARE_SIMD_CONSTS(sqrt_two, SQRT_TWO); - DECLARE_SIMD_CONSTS(sqrt_half_two, SQRT_HALF_TWO); - DECLARE_SIMD_CONSTS(sqrt_half_two_minus, -SQRT_HALF_TWO); - DECLARE_SIMD_CONSTS(sign_bit, -0.0); - - // cephes magic numbers - DECLARE_SIMD_CONSTS(cephes_e__4, 1.0e-4); - DECLARE_SIMD_CONSTS(cephes_small, 1.0e-35); - DECLARE_SIMD_CONSTS(cephes_2414, 2.414213562373095); - DECLARE_SIMD_CONSTS(cephes_0414, 0.4142135623730950); - DECLARE_SIMD_CONSTS(cephes_8053, 8.05374449538e-2); - DECLARE_SIMD_CONSTS(cephes_1387, 1.38776856032E-1); - DECLARE_SIMD_CONSTS(cephes_1997, 1.99777106478E-1); - DECLARE_SIMD_CONSTS(cephes_3333, 3.33329491539E-1); - // extended precision arithmetic - DECLARE_SIMD_CONSTS(cephes_mdp1, -0.78515625); - DECLARE_SIMD_CONSTS(cephes_mdp2, -2.4187564849853515625e-4); - DECLARE_SIMD_CONSTS(cephes_mdp3, -3.77489497744594108e-8); - // sine/cosine calculation coefficients - DECLARE_SIMD_CONSTS(cephes_sin_p0, -1.9515295891E-4); - DECLARE_SIMD_CONSTS(cephes_sin_p1, 8.3321608736E-3); - DECLARE_SIMD_CONSTS(cephes_sin_p2, -1.6666654611E-1); - DECLARE_SIMD_CONSTS(cephes_cos_p0, 2.443315711809948E-005); - DECLARE_SIMD_CONSTS(cephes_cos_p1, -1.388731625493765E-003); - DECLARE_SIMD_CONSTS(cephes_cos_p2, 4.166664568298827E-002); - - // epsilon - template<> const v4sf consts::epsilon = FILL_4(FLT_EPSILON, float); - template<> const v8sf consts::epsilon = FILL_8(FLT_EPSILON, float); - template<> const v2sd consts::epsilon = FILL_2(DBL_EPSILON, double); - template<> const v4sd consts::epsilon = FILL_4(DBL_EPSILON, double); - // min - template<> const v4sf consts::min = FILL_4(FLT_MIN, float); - template<> const v8sf consts::min = FILL_8(FLT_MIN, float); - template<> const v2sd consts::min = FILL_2(DBL_MIN, double); - template<> const v4sd consts::min = FILL_4(DBL_MIN, double); - // min - template<> const v4sf consts::max = FILL_4(FLT_MAX, float); - template<> const v8sf consts::max = FILL_8(FLT_MAX, float); - template<> const v2sd consts::max = FILL_2(DBL_MAX, double); - template<> const v4sd consts::max = FILL_4(DBL_MAX, double); - - - // all_bits - need a better solution here, that doesn't involve intrinsics. - template<> const v4sf consts::all_bits = {*(float*)&bit_helper::bits, *(float*)&bit_helper::bits, - *(float*)&bit_helper::bits, *(float*)&bit_helper::bits}; - template<> const v8sf consts::all_bits = {*(float*)&bit_helper::bits, *(float*)&bit_helper::bits, - *(float*)&bit_helper::bits, *(float*)&bit_helper::bits, - *(float*)&bit_helper::bits, *(float*)&bit_helper::bits, - *(float*)&bit_helper::bits, *(float*)&bit_helper::bits}; - template<> const v2sd consts::all_bits = {*(double*)&bit_helper::bits, *(double*)&bit_helper::bits}; - template<> const v4sd consts::all_bits = {*(double*)&bit_helper::bits, *(double*)&bit_helper::bits, - *(double*)&bit_helper::bits, *(double*)&bit_helper::bits}; - - // sign mask - need a better solution here, that doesn't involve intrinsics. - template<> const v4sf consts::sign_mask = {*(float*)&bit_helper::fsm, *(float*)&bit_helper::fsm, - *(float*)&bit_helper::fsm, *(float*)&bit_helper::fsm}; - template<> const v8sf consts::sign_mask = {*(float*)&bit_helper::fsm, *(float*)&bit_helper::fsm, - *(float*)&bit_helper::fsm, *(float*)&bit_helper::fsm, - *(float*)&bit_helper::fsm, *(float*)&bit_helper::fsm, - *(float*)&bit_helper::fsm, *(float*)&bit_helper::fsm}; - template<> const v2sd consts::sign_mask = {*(double*)&bit_helper::dsm, *(double*)&bit_helper::dsm}; - template<> const v4sd consts::sign_mask = {*(double*)&bit_helper::dsm, *(double*)&bit_helper::dsm, - *(double*)&bit_helper::dsm, *(double*)&bit_helper::dsm}; - - #undef FILL_8 - #undef FILL_4 - #undef FILL_2 - #undef SQRT_TWO - #undef SQRT_HALF - #undef DECLARE_SIMD_CONSTS - #endif }; // simd }; // cpl + +#ifdef CPL_COMPILER_MULTIPLE_STATICS_SUPPORTED +#include "simd_data.inl" +#endif + #endif diff --git a/simd/simd_data.inl b/simd/simd_data.inl new file mode 100644 index 0000000..5853632 --- /dev/null +++ b/simd/simd_data.inl @@ -0,0 +1,167 @@ +/************************************************************************************* + + cpl - cross-platform library - v. 0.1.0. + + Copyright (C) 2026 Janus Lynggaard Thorborg (www.jthorborg.com) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + See \licenses\ for additional details on licenses associated with this program. + +************************************************************************************** + + file:simd_data.inl + + Declaration for constants in for simd_consts.h + +*************************************************************************************/ + +namespace cpl +{ + namespace simd + { + #define SQRT_TWO 1.4142135623730950488016887242097 + #define SQRT_HALF_TWO 0.70710678118654752440084436210485 + + #define FILL_8(val, ty) {static_cast(val), static_cast(val), static_cast(val), static_cast(val), \ + static_cast(val), static_cast(val), static_cast(val), static_cast(val)} + #define FILL_4(val, ty) {static_cast(val), static_cast(val), static_cast(val), static_cast(val)} + #define FILL_2(val, ty) {static_cast(val), static_cast(val)} + + + #define DECLARE_SIMD_CONSTS(name, value) \ + template<> const v4sf consts::name = FILL_4(value, float); \ + template<> const v8sf consts::name = FILL_8(value, float); \ + template<> const v2sd consts::name = FILL_2(value, double); \ + template<> const v4sd consts::name = FILL_4(value, double); \ + template<> const float consts::name = (float)(value); \ + template<> const double consts::name = value; + + #ifdef CPL_MSVC + struct bit_helper + { + static const std::uint64_t bits = 0xFFFFFFFFFFFFFFFFULL; + // 32-bit IEEE floating point sign mask + static const std::uint32_t fsm = 0x7FFFFFFFU; + // 64-bit IEEE floating point sign mask + static const std::uint64_t dsm = 0x7FFFFFFFFFFFFFFFULL; + #else + namespace bit_helper + { + static const std::uint64_t bits = 0xFFFFFFFFFFFFFFFFULL; + // 32-bit IEEE floating point sign mask + static const std::uint32_t fsm = 0x7FFFFFFFU; + // 64-bit IEEE floating point sign mask + static const std::uint64_t dsm = 0x7FFFFFFFFFFFFFFFULL; + + #endif + }; + + // standards + DECLARE_SIMD_CONSTS(pi, M_PI); + DECLARE_SIMD_CONSTS(e, M_E); + DECLARE_SIMD_CONSTS(tau, M_PI * 2); + DECLARE_SIMD_CONSTS(pi_half, M_PI / 2); + DECLARE_SIMD_CONSTS(pi_squared, M_PI* M_PI); + DECLARE_SIMD_CONSTS(pi_quarter, M_PI / 4); + DECLARE_SIMD_CONSTS(four_over_pi, 4 / M_PI); + DECLARE_SIMD_CONSTS(zero, 0.0); + DECLARE_SIMD_CONSTS(two, 2.0); + DECLARE_SIMD_CONSTS(four, 4.0); + DECLARE_SIMD_CONSTS(one, 1.0); + DECLARE_SIMD_CONSTS(minus_one, -1.0); + DECLARE_SIMD_CONSTS(minus_two, -2.0); + DECLARE_SIMD_CONSTS(half, 0.5); + DECLARE_SIMD_CONSTS(quarter, 0.25); + DECLARE_SIMD_CONSTS(sqrt_two, SQRT_TWO); + DECLARE_SIMD_CONSTS(sqrt_half_two, SQRT_HALF_TWO); + DECLARE_SIMD_CONSTS(sqrt_half_two_minus, -SQRT_HALF_TWO); + DECLARE_SIMD_CONSTS(sign_bit, -0.0); + + // cephes magic numbers + DECLARE_SIMD_CONSTS(cephes_e__4, 1.0e-4); + DECLARE_SIMD_CONSTS(cephes_small, 1.0e-35); + DECLARE_SIMD_CONSTS(cephes_2414, 2.414213562373095); + DECLARE_SIMD_CONSTS(cephes_0414, 0.4142135623730950); + DECLARE_SIMD_CONSTS(cephes_8053, 8.05374449538e-2); + DECLARE_SIMD_CONSTS(cephes_1387, 1.38776856032E-1); + DECLARE_SIMD_CONSTS(cephes_1997, 1.99777106478E-1); + DECLARE_SIMD_CONSTS(cephes_3333, 3.33329491539E-1); + // extended precision arithmetic + DECLARE_SIMD_CONSTS(cephes_mdp1, -0.78515625); + DECLARE_SIMD_CONSTS(cephes_mdp2, -2.4187564849853515625e-4); + DECLARE_SIMD_CONSTS(cephes_mdp3, -3.77489497744594108e-8); + // sine/cosine calculation coefficients + DECLARE_SIMD_CONSTS(cephes_sin_p0, -1.9515295891E-4); + DECLARE_SIMD_CONSTS(cephes_sin_p1, 8.3321608736E-3); + DECLARE_SIMD_CONSTS(cephes_sin_p2, -1.6666654611E-1); + DECLARE_SIMD_CONSTS(cephes_cos_p0, 2.443315711809948E-005); + DECLARE_SIMD_CONSTS(cephes_cos_p1, -1.388731625493765E-003); + DECLARE_SIMD_CONSTS(cephes_cos_p2, 4.166664568298827E-002); + + // epsilon + template<> const v4sf consts::epsilon = FILL_4(FLT_EPSILON, float); + template<> const v8sf consts::epsilon = FILL_8(FLT_EPSILON, float); + template<> const v2sd consts::epsilon = FILL_2(DBL_EPSILON, double); + template<> const v4sd consts::epsilon = FILL_4(DBL_EPSILON, double); + // min + template<> const v4sf consts::min = FILL_4(FLT_MIN, float); + template<> const v8sf consts::min = FILL_8(FLT_MIN, float); + template<> const v2sd consts::min = FILL_2(DBL_MIN, double); + template<> const v4sd consts::min = FILL_4(DBL_MIN, double); + // min + template<> const v4sf consts::max = FILL_4(FLT_MAX, float); + template<> const v8sf consts::max = FILL_8(FLT_MAX, float); + template<> const v2sd consts::max = FILL_2(DBL_MAX, double); + template<> const v4sd consts::max = FILL_4(DBL_MAX, double); + + + // all_bits - need a better solution here, that doesn't involve intrinsics. + + template<> const float consts::all_bits = *(const float*)&bit_helper::bits; + template<> const double consts::all_bits = *(const double*)&bit_helper::bits; + + template<> const v4sf consts::all_bits = {*(const float*)&bit_helper::bits, *(const float*)&bit_helper::bits, + *(const float*)&bit_helper::bits, *(const float*)&bit_helper::bits}; + template<> const v8sf consts::all_bits = {*(const float*)&bit_helper::bits, *(const float*)&bit_helper::bits, + *(const float*)&bit_helper::bits, *(const float*)&bit_helper::bits, + *(const float*)&bit_helper::bits, *(const float*)&bit_helper::bits, + *(const float*)&bit_helper::bits, *(const float*)&bit_helper::bits}; + template<> const v2sd consts::all_bits = {*(const double*)&bit_helper::bits, *(const double*)&bit_helper::bits}; + template<> const v4sd consts::all_bits = {*(const double*)&bit_helper::bits, *(const double*)&bit_helper::bits, + *(const double*)&bit_helper::bits, *(const double*)&bit_helper::bits}; + + // sign mask - need a better solution here, that doesn't involve intrinsics. + template<> const float consts::sign_mask = *(const float*)&bit_helper::fsm; + template<> const double consts::sign_mask = *(const double*)&bit_helper::dsm; + + template<> const v4sf consts::sign_mask = {*(const float*)&bit_helper::fsm, *(const float*)&bit_helper::fsm, + *(const float*)&bit_helper::fsm, *(const float*)&bit_helper::fsm}; + template<> const v8sf consts::sign_mask = {*(const float*)&bit_helper::fsm, *(const float*)&bit_helper::fsm, + *(const float*)&bit_helper::fsm, *(const float*)&bit_helper::fsm, + *(const float*)&bit_helper::fsm, *(const float*)&bit_helper::fsm, + *(const float*)&bit_helper::fsm, *(const float*)&bit_helper::fsm}; + template<> const v2sd consts::sign_mask = {*(const double*)&bit_helper::dsm, *(const double*)&bit_helper::dsm}; + template<> const v4sd consts::sign_mask = {*(const double*)&bit_helper::dsm, *(const double*)&bit_helper::dsm, + *(const double*)&bit_helper::dsm, *(const double*)&bit_helper::dsm}; + + #undef FILL_8 + #undef FILL_4 + #undef FILL_2 + #undef SQRT_TWO + #undef SQRT_HALF + #undef DECLARE_SIMD_CONSTS + }; // simd +}; // cpl + diff --git a/simd/simd_math.h b/simd/simd_math.h index 2017bc9..a03ee70 100644 --- a/simd/simd_math.h +++ b/simd/simd_math.h @@ -751,14 +751,15 @@ namespace cpl CPL_SIMD_FUNC typename std::enable_if::type, float>::value, V>::type sin(V x) { // any x - V y; - - using VConsts = cpl::simd::consts; auto const elements = elements_of::value; - typedef typename to_integer::type VInt; + using VConsts = cpl::simd::consts; + using VIConsts = cpl::simd::consts; + + V y; + /* take the absolute value and extract the sign bit */ V sign_bit = simd::sign(x); x = vxor(x, sign_bit); @@ -784,7 +785,7 @@ namespace cpl // get the swap sign flag // swap_sign_sin = input & 4 (swap sign each M_PI multiple) // - auto has_fourth_bit = vand(j_as_float, four_as_int) == four_as_int; + V has_fourth_bit = vand(j_as_float, four_as_int) == four_as_int; V swap_sign_bit = vand(has_fourth_bit, VConsts::sign_bit); /* get the polynom selection mask @@ -870,10 +871,12 @@ namespace cpl // store the integer part of y in mm0 VInt j = vdouble_cvt_int32(y + VConsts::one); - +#ifdef CPL_M_X86 // jump out of avx here. - if (std::is_same::value) + if (std::is_same::value) { _mm256_zeroupper(); + } +#endif // j=(j+1) & (~1) (see the cephes sources) // add one and make it even @@ -1099,9 +1102,12 @@ namespace cpl // store the integer part of y in mm0 VInt j = vdouble_cvt_int32(y + VConsts::one); +#ifdef CPL_M_X86 // jump out of avx here. - if (std::is_same::value) + if (std::is_same::value) { _mm256_zeroupper(); + } +#endif // j=(j+1) & (~1) (see the cephes sources) // add one and make it even diff --git a/state/CSerializer.h b/state/CSerializer.h index b7575f7..a173a3d 100644 --- a/state/CSerializer.h +++ b/state/CSerializer.h @@ -118,7 +118,6 @@ #include #include #include "../stdext.h" -#include "../PlatformSpecific.h" #include "../Misc.h" #include "../ProgramVersion.h" #include "../Exceptions.h" @@ -297,14 +296,15 @@ namespace cpl } - const char * getBlock() const { return static_cast(contents.get()); } - std::size_t getSize() const { return contentSize; } - ContentWrapper(ContentWrapper && cw) + ContentWrapper(ContentWrapper&& cw) noexcept : contents(cw.contents.release()), contentSize(cw.contentSize) { } + const char * getBlock() const { return static_cast(contents.get()); } + std::size_t getSize() const { return contentSize; } + private: std::unique_ptr contents; std::size_t contentSize; @@ -531,7 +531,7 @@ namespace cpl class Key { public: - explicit Key(const KeyHeader * kh); + explicit Key(const KeyHeader* kh) = delete; Key(const std::string & s) : isString(true), intKey(0), stringKey(s) {}; Key(const char * s) : isString(true), intKey(0), stringKey(s) {}; Key(long long ID) : isString(false), intKey(ID) {}; @@ -782,37 +782,37 @@ namespace cpl template CSerializer & operator << (const std::unique_ptr & object) { - static_assert(delayed_error::value, "Serialization of std::unique_ptr is disabled (it is most likely NOT what you want; otherwise use .get())"); + static_assert(delayed_error::value, "Serialization of std::unique_ptr is disabled (it is most likely NOT what you want; otherwise use .get())"); return *this; } template CSerializer & operator >> (std::unique_ptr & object) { - static_assert(delayed_error::value, "Deserialization of std::unique_ptr is disabled (it is most likely NOT what you want; otherwise use .get())"); + static_assert(delayed_error::value, "Deserialization of std::unique_ptr is disabled (it is most likely NOT what you want; otherwise use .get())"); return *this; } template CSerializer& operator << (const std::shared_ptr& object) { - static_assert(delayed_error::value, "Serialization of std::shared_ptr is disabled (it is most likely NOT what you want; otherwise use .get())"); + static_assert(delayed_error::value, "Serialization of std::shared_ptr is disabled (it is most likely NOT what you want; otherwise use .get())"); return *this; } template CSerializer& operator >> (std::shared_ptr& object) { - static_assert(delayed_error::value, "Deserialization of std::shared_ptr is disabled (it is most likely NOT what you want; otherwise use .get())"); + static_assert(delayed_error::value, "Deserialization of std::shared_ptr is disabled (it is most likely NOT what you want; otherwise use .get())"); return *this; } template CSerializer& operator >> (std::optional& object) { - static_assert(delayed_error::value, "Serialization of std::optional is disabled (don't count on it being binary stable)"); + static_assert(delayed_error::value, "Serialization of std::optional is disabled (don't count on it being binary stable)"); return *this; } template CSerializer& operator << (std::optional& object) { - static_assert(delayed_error::value, "Deserialization of std::optional is disabled (don't count on it being binary stable)"); + static_assert(delayed_error::value, "Deserialization of std::optional is disabled (don't count on it being binary stable)"); return *this; } /// @@ -1038,6 +1038,8 @@ namespace cpl return internalSerializer.getContent("Content"); } + const std::string& getName() const { return nameReference; } + private: CSerializer internalSerializer; diff --git a/stdext.h b/stdext.h index 6ee04b3..7b6c997 100755 --- a/stdext.h +++ b/stdext.h @@ -35,6 +35,7 @@ #include #include #include +#include namespace cpl { diff --git a/system/InstructionSet.cpp b/system/InstructionSet.cpp index 16b3235..18f0026 100644 --- a/system/InstructionSet.cpp +++ b/system/InstructionSet.cpp @@ -1,51 +1,74 @@ #include "InstructionSet.h" +#if CPL_M_X86 namespace cpl { #ifndef CPL_WINDOWS // GCC Inline Assembly // creds: http://stackoverflow.com/questions/6121792/how-to-check-if-a-cpu-supports-the-sse3-instruction-set - void cpuid(int CPUInfo[4], int InfoType) + void __cpuid(int CPUInfo[4], int InfoType) { - __asm__ __volatile__( - "cpuid": - "=a" (CPUInfo[0]), - "=b" (CPUInfo[1]), - "=c" (CPUInfo[2]), - "=d" (CPUInfo[3]) : - "a" (InfoType), "c" (0) - ); + #if defined(__x86_64__) || defined(__i386__) + __asm__ __volatile__( + "cpuid": + "=a" (CPUInfo[0]), + "=b" (CPUInfo[1]), + "=c" (CPUInfo[2]), + "=d" (CPUInfo[3]) : + "a" (InfoType), "c" (0) + ); + #elif defined(__aarch64__) || defined(__arm64__) + // ARM64 doesn't have cpuid, return dummy values + // InfoType 0 returns max function and vendor string + if (InfoType == 0) { + CPUInfo[0] = 0; // Max function number + CPUInfo[1] = 0x41524D20; // "ARM " + CPUInfo[2] = 0x41524D20; // "ARM " + CPUInfo[3] = 0x41524D20; // "ARM " + } else { + // Return zeros for other queries + CPUInfo[0] = CPUInfo[1] = CPUInfo[2] = CPUInfo[3] = 0; + } + #else + #error "Unknown platform for CPUID" + #endif } - void cpuidex(int CPUInfo[4], int InfoType, int SubFunctionID) + void __cpuidex(int CPUInfo[4], int InfoType, int SubFunctionID) { - __asm__ __volatile__("movl $0, %%ecx" : "=g" (SubFunctionID)); - __asm__ __volatile__( - "cpuid": - "=a" (CPUInfo[0]), - "=b" (CPUInfo[1]), - "=c" (CPUInfo[2]), - "=d" (CPUInfo[3]) : - "a" (InfoType), "c" (0) - ); + #if defined(__x86_64__) || defined(__i386__) + __asm__ __volatile__("movl $0, %%ecx" : "=g" (SubFunctionID)); + __asm__ __volatile__( + "cpuid": + "=a" (CPUInfo[0]), + "=b" (CPUInfo[1]), + "=c" (CPUInfo[2]), + "=d" (CPUInfo[3]) : + "a" (InfoType), "c" (0) + ); + #else + // For non-x86 architectures, just call cpuid + (void)SubFunctionID; + cpuid(CPUInfo, InfoType); + #endif } - #else - - void cpuid(int CPUInfo[4], int InfoType) - { - return __cpuid(CPUInfo, InfoType); - }; - - void cpuidex(int CPUInfo[4], int InfoType, int SubFunctionID) - { - return __cpuidex(CPUInfo, InfoType, SubFunctionID); - }; - #endif + namespace msdn { const InstructionSet::InstructionSet_Internal InstructionSet::CPU_Rep; + void InstructionSet::cpuid(int CPUInfo[4], int InfoType) + { + return __cpuid(CPUInfo, InfoType); + }; + + void InstructionSet::cpuidex(int CPUInfo[4], int InfoType, int SubFunctionID) + { + return __cpuidex(CPUInfo, InfoType, SubFunctionID); + }; }; -} \ No newline at end of file +} + +#endif diff --git a/system/InstructionSet.h b/system/InstructionSet.h index 8dcc64b..16a58ef 100755 --- a/system/InstructionSet.h +++ b/system/InstructionSet.h @@ -5,13 +5,12 @@ #include #include #include +#include #include "../PlatformSpecific.h" +#include namespace cpl { - void cpuid(int *, int); - void cpuidex(int *, int, int); - namespace msdn { // Source: msdn @@ -19,74 +18,145 @@ namespace cpl class InstructionSet { +#if CPL_M_ARM + public: + // getters + static constexpr std::string_view Vendor() { return "ARM"; } + static constexpr std::string_view Brand() { return "ARM64 Processor"; } + + static constexpr bool isIntel() { return false; } + static constexpr bool isAMD() { return false; } + static constexpr bool isARM() { return true; } + + static constexpr bool SSE3() { return true; } + static constexpr bool PCLMULQDQ() { return false; } + static constexpr bool MONITOR() { return false; } + static constexpr bool SSSE3() { return true; } + static constexpr bool FMA() { return true; } + static constexpr bool CMPXCHG16B() { return false; } + static constexpr bool SSE41() { return true; } + static constexpr bool SSE42() { return true; } + static constexpr bool MOVBE() { return false; } + static constexpr bool POPCNT() { return true; } + static constexpr bool AES() { return false; } + static constexpr bool XSAVE() { return false; } + static constexpr bool OSXSAVE() { return false; } + static constexpr bool AVX() { return false; } + static constexpr bool F16C() { return false; } + static constexpr bool RDRAND() { return false; } + + static constexpr bool MSR() { return false; } + static constexpr bool CX8() { return false; } + static constexpr bool SEP() { return false; } + static constexpr bool CMOV() { return true; } + static constexpr bool CLFSH() { return false; } + static constexpr bool MMX() { return true; } + static constexpr bool FXSR() { return false; } + static constexpr bool SSE() { return true; } + static constexpr bool SSE2() { return true; } + + static constexpr bool FSGSBASE() { return false; } + static constexpr bool BMI1() { return false; } + static constexpr bool HLE() { return false; } + static constexpr bool AVX2() { return false; } + static constexpr bool BMI2() { return false; } + static constexpr bool ERMS() { return false; } + static constexpr bool INVPCID() { return false; } + static constexpr bool RTM() { return false; } + static constexpr bool AVX512F() { return false; } + static constexpr bool RDSEED() { return false; } + static constexpr bool ADX() { return false; } + static constexpr bool AVX512PF() { return false; } + static constexpr bool AVX512ER() { return false; } + static constexpr bool AVX512CD() { return false; } + static constexpr bool SHA() { return false; } + + static constexpr bool PREFETCHWT1() { return true; } + + static constexpr bool LAHF() { return false; } + static constexpr bool LZCNT() { return true; } + static constexpr bool ABM() { return false; } + static constexpr bool SSE4a() { return false; } + static constexpr bool XOP() { return false; } + static constexpr bool TBM() { return false; } + + static constexpr bool SYSCALL() { return false; } + static constexpr bool MMXEXT() { return true; } + static constexpr bool RDTSCP() { return true; } + static constexpr bool _3DNOWEXT() { return false; } + static constexpr bool _3DNOW() { return false; } + +#else // forward declarations class InstructionSet_Internal; - + static void cpuid(int CPUInfo[4], int InfoType); + static void cpuidex(int CPUInfo[4], int InfoType, int SubFunctionID); public: // getters - static std::string Vendor(void) { return CPU_Rep.vendor_; } - static std::string Brand(void) { return CPU_Rep.brand_; } - - static bool isIntel(void) { return CPU_Rep.isIntel_; } - static bool isAMD(void) { return CPU_Rep.isAMD_; } - - static bool SSE3(void) { return CPU_Rep.f_1_ECX_[0]; } - static bool PCLMULQDQ(void) { return CPU_Rep.f_1_ECX_[1]; } - static bool MONITOR(void) { return CPU_Rep.f_1_ECX_[3]; } - static bool SSSE3(void) { return CPU_Rep.f_1_ECX_[9]; } - static bool FMA(void) { return CPU_Rep.f_1_ECX_[12]; } - static bool CMPXCHG16B(void) { return CPU_Rep.f_1_ECX_[13]; } - static bool SSE41(void) { return CPU_Rep.f_1_ECX_[19]; } - static bool SSE42(void) { return CPU_Rep.f_1_ECX_[20]; } - static bool MOVBE(void) { return CPU_Rep.f_1_ECX_[22]; } - static bool POPCNT(void) { return CPU_Rep.f_1_ECX_[23]; } - static bool AES(void) { return CPU_Rep.f_1_ECX_[25]; } - static bool XSAVE(void) { return CPU_Rep.f_1_ECX_[26]; } - static bool OSXSAVE(void) { return CPU_Rep.f_1_ECX_[27]; } - static bool AVX(void) { return CPU_Rep.f_1_ECX_[28]; } - static bool F16C(void) { return CPU_Rep.f_1_ECX_[29]; } - static bool RDRAND(void) { return CPU_Rep.f_1_ECX_[30]; } - - static bool MSR(void) { return CPU_Rep.f_1_EDX_[5]; } - static bool CX8(void) { return CPU_Rep.f_1_EDX_[8]; } - static bool SEP(void) { return CPU_Rep.f_1_EDX_[11]; } - static bool CMOV(void) { return CPU_Rep.f_1_EDX_[15]; } - static bool CLFSH(void) { return CPU_Rep.f_1_EDX_[19]; } - static bool MMX(void) { return CPU_Rep.f_1_EDX_[23]; } - static bool FXSR(void) { return CPU_Rep.f_1_EDX_[24]; } - static bool SSE(void) { return CPU_Rep.f_1_EDX_[25]; } - static bool SSE2(void) { return CPU_Rep.f_1_EDX_[26]; } - - static bool FSGSBASE(void) { return CPU_Rep.f_7_EBX_[0]; } - static bool BMI1(void) { return CPU_Rep.f_7_EBX_[3]; } - static bool HLE(void) { return CPU_Rep.isIntel_ && CPU_Rep.f_7_EBX_[4]; } - static bool AVX2(void) { return CPU_Rep.f_7_EBX_[5]; } - static bool BMI2(void) { return CPU_Rep.f_7_EBX_[8]; } - static bool ERMS(void) { return CPU_Rep.f_7_EBX_[9]; } - static bool INVPCID(void) { return CPU_Rep.f_7_EBX_[10]; } - static bool RTM(void) { return CPU_Rep.isIntel_ && CPU_Rep.f_7_EBX_[11]; } - static bool AVX512F(void) { return CPU_Rep.f_7_EBX_[16]; } - static bool RDSEED(void) { return CPU_Rep.f_7_EBX_[18]; } - static bool ADX(void) { return CPU_Rep.f_7_EBX_[19]; } - static bool AVX512PF(void) { return CPU_Rep.f_7_EBX_[26]; } - static bool AVX512ER(void) { return CPU_Rep.f_7_EBX_[27]; } - static bool AVX512CD(void) { return CPU_Rep.f_7_EBX_[28]; } - static bool SHA(void) { return CPU_Rep.f_7_EBX_[29]; } - - static bool PREFETCHWT1(void) { return CPU_Rep.f_7_ECX_[0]; } - - static bool LAHF(void) { return CPU_Rep.f_81_ECX_[0]; } - static bool LZCNT(void) { return CPU_Rep.isIntel_ && CPU_Rep.f_81_ECX_[5]; } - static bool ABM(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_ECX_[5]; } - static bool SSE4a(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_ECX_[6]; } - static bool XOP(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_ECX_[11]; } - static bool TBM(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_ECX_[21]; } - - static bool SYSCALL(void) { return CPU_Rep.isIntel_ && CPU_Rep.f_81_EDX_[11]; } - static bool MMXEXT(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_EDX_[22]; } - static bool RDTSCP(void) { return CPU_Rep.isIntel_ && CPU_Rep.f_81_EDX_[27]; } - static bool _3DNOWEXT(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_EDX_[30]; } - static bool _3DNOW(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_EDX_[31]; } + static std::string Vendor() { return CPU_Rep.vendor_; } + static std::string Brand() { return CPU_Rep.brand_; } + + static bool isIntel() { return CPU_Rep.isIntel_; } + static bool isAMD() { return CPU_Rep.isAMD_; } + static constexpr bool isARM() { return false; } + + static bool SSE3() { return CPU_Rep.f_1_ECX_[0]; } + static bool PCLMULQDQ() { return CPU_Rep.f_1_ECX_[1]; } + static bool MONITOR() { return CPU_Rep.f_1_ECX_[3]; } + static bool SSSE3() { return CPU_Rep.f_1_ECX_[9]; } + static bool FMA() { return CPU_Rep.f_1_ECX_[12]; } + static bool CMPXCHG16B() { return CPU_Rep.f_1_ECX_[13]; } + static bool SSE41() { return CPU_Rep.f_1_ECX_[19]; } + static bool SSE42() { return CPU_Rep.f_1_ECX_[20]; } + static bool MOVBE() { return CPU_Rep.f_1_ECX_[22]; } + static bool POPCNT() { return CPU_Rep.f_1_ECX_[23]; } + static bool AES() { return CPU_Rep.f_1_ECX_[25]; } + static bool XSAVE() { return CPU_Rep.f_1_ECX_[26]; } + static bool OSXSAVE() { return CPU_Rep.f_1_ECX_[27]; } + static bool AVX() { return CPU_Rep.f_1_ECX_[28]; } + static bool F16C() { return CPU_Rep.f_1_ECX_[29]; } + static bool RDRAND() { return CPU_Rep.f_1_ECX_[30]; } + + static bool MSR() { return CPU_Rep.f_1_EDX_[5]; } + static bool CX8() { return CPU_Rep.f_1_EDX_[8]; } + static bool SEP() { return CPU_Rep.f_1_EDX_[11]; } + static bool CMOV() { return CPU_Rep.f_1_EDX_[15]; } + static bool CLFSH() { return CPU_Rep.f_1_EDX_[19]; } + static bool MMX() { return CPU_Rep.f_1_EDX_[23]; } + static bool FXSR() { return CPU_Rep.f_1_EDX_[24]; } + static bool SSE() { return CPU_Rep.f_1_EDX_[25]; } + static bool SSE2() { return CPU_Rep.f_1_EDX_[26]; } + + static bool FSGSBASE() { return CPU_Rep.f_7_EBX_[0]; } + static bool BMI1() { return CPU_Rep.f_7_EBX_[3]; } + static bool HLE() { return CPU_Rep.isIntel_ && CPU_Rep.f_7_EBX_[4]; } + static bool AVX2() { return CPU_Rep.f_7_EBX_[5]; } + static bool BMI2() { return CPU_Rep.f_7_EBX_[8]; } + static bool ERMS() { return CPU_Rep.f_7_EBX_[9]; } + static bool INVPCID() { return CPU_Rep.f_7_EBX_[10]; } + static bool RTM() { return CPU_Rep.isIntel_ && CPU_Rep.f_7_EBX_[11]; } + static bool AVX512F() { return CPU_Rep.f_7_EBX_[16]; } + static bool RDSEED() { return CPU_Rep.f_7_EBX_[18]; } + static bool ADX() { return CPU_Rep.f_7_EBX_[19]; } + static bool AVX512PF() { return CPU_Rep.f_7_EBX_[26]; } + static bool AVX512ER() { return CPU_Rep.f_7_EBX_[27]; } + static bool AVX512CD() { return CPU_Rep.f_7_EBX_[28]; } + static bool SHA() { return CPU_Rep.f_7_EBX_[29]; } + + static bool PREFETCHWT1() { return CPU_Rep.f_7_ECX_[0]; } + + static bool LAHF() { return CPU_Rep.f_81_ECX_[0]; } + static bool LZCNT() { return CPU_Rep.isIntel_ && CPU_Rep.f_81_ECX_[5]; } + static bool ABM() { return CPU_Rep.isAMD_ && CPU_Rep.f_81_ECX_[5]; } + static bool SSE4a() { return CPU_Rep.isAMD_ && CPU_Rep.f_81_ECX_[6]; } + static bool XOP() { return CPU_Rep.isAMD_ && CPU_Rep.f_81_ECX_[11]; } + static bool TBM() { return CPU_Rep.isAMD_ && CPU_Rep.f_81_ECX_[21]; } + + static bool SYSCALL() { return CPU_Rep.isIntel_ && CPU_Rep.f_81_EDX_[11]; } + static bool MMXEXT() { return CPU_Rep.isAMD_ && CPU_Rep.f_81_EDX_[22]; } + static bool RDTSCP() { return CPU_Rep.isIntel_ && CPU_Rep.f_81_EDX_[27]; } + static bool _3DNOWEXT() { return CPU_Rep.isAMD_ && CPU_Rep.f_81_EDX_[30]; } + static bool _3DNOW() { return CPU_Rep.isAMD_ && CPU_Rep.f_81_EDX_[31]; } private: static const InstructionSet_Internal CPU_Rep; @@ -108,17 +178,37 @@ namespace cpl data_ {}, extdata_ {} { + #if defined(__aarch64__) || defined(__arm64__) + // ARM64 fast path - skip expensive x86 CPU feature detection + vendor_ = "ARM"; + brand_ = "ARM64 Processor"; + // Reserve minimal space to avoid reallocations + data_.reserve(2); + extdata_.reserve(2); + // Set basic ARM64/NEON capabilities + // Enable SSE/SSE2 flags as these map to NEON via SIMDE + f_1_EDX_[25] = 1; // SSE maps to NEON + f_1_EDX_[26] = 1; // SSE2 maps to NEON + // ARM64 always has FPU + f_1_EDX_[24] = 1; // FXSR + return; + #endif + //int cpuInfo[4] = {-1}; std::array cpui; + // Reserve memory upfront to avoid reallocations + data_.reserve(32); + extdata_.reserve(16); + // Calling __cpuid with 0x0 as the function_id argument // gets the number of the highest valid function ID. - cpl::cpuid(cpui.data(), 0); + cpuid(cpui.data(), 0); nIds_ = cpui[0]; for (int i = 0; i <= nIds_; ++i) { - cpl::cpuidex(cpui.data(), i, 0); + cpuidex(cpui.data(), i, 0); data_.push_back(cpui); } @@ -154,7 +244,7 @@ namespace cpl // Calling __cpuid with 0x80000000 as the function_id argument // gets the number of the highest valid extended ID. - cpl::cpuid(cpui.data(), (int)0x80000000); + cpuid(cpui.data(), (int)0x80000000); nExIds_ = cpui[0]; char brand[0x40]; @@ -162,7 +252,7 @@ namespace cpl for (int i = (int)0x80000000; i <= nExIds_; ++i) { - cpl::cpuidex(cpui.data(), i, 0); + cpuidex(cpui.data(), i, 0); extdata_.push_back(cpui); } @@ -179,6 +269,7 @@ namespace cpl memcpy(brand, extdata_[2].data(), sizeof(cpui)); memcpy(brand + 16, extdata_[3].data(), sizeof(cpui)); memcpy(brand + 32, extdata_[4].data(), sizeof(cpui)); + brand[sizeof(brand) - 1] = '\0'; brand_ = brand; } }; @@ -198,6 +289,7 @@ namespace cpl std::vector> data_; std::vector> extdata_; }; +#endif }; }; }; diff --git a/system/SysStats.h b/system/SysStats.h index 0a45040..8c3e39a 100644 --- a/system/SysStats.h +++ b/system/SysStats.h @@ -108,7 +108,7 @@ namespace cpl static const std::string getName() { - return msdn::InstructionSet::Vendor() + msdn::InstructionSet::Brand(); + return std::string(msdn::InstructionSet::Vendor()) + "/" + std::string(msdn::InstructionSet::Brand()); } static bool test(Archs arch)