New code style refactor

This commit is contained in:
Jack Andersen 2018-12-07 19:17:51 -10:00
parent 2c2c72bfd1
commit 058ea23a00
113 changed files with 23305 additions and 27650 deletions

View File

@ -5,17 +5,13 @@
#include "boo/inputdev/XInputPad.hpp" #include "boo/inputdev/XInputPad.hpp"
#include "boo/inputdev/NintendoPowerA.hpp" #include "boo/inputdev/NintendoPowerA.hpp"
namespace boo namespace boo {
{
const DeviceSignature BOO_DEVICE_SIGS[] = const DeviceSignature BOO_DEVICE_SIGS[] = {DEVICE_SIG(DolphinSmashAdapter, 0x57e, 0x337, DeviceType::USB),
{ DEVICE_SIG(DualshockPad, 0x54c, 0x268, DeviceType::HID),
DEVICE_SIG(DolphinSmashAdapter, 0x57e, 0x337, DeviceType::USB), DEVICE_SIG(GenericPad, 0, 0, DeviceType::HID),
DEVICE_SIG(DualshockPad, 0x54c, 0x268, DeviceType::HID), DEVICE_SIG(NintendoPowerA, 0x20D6, 0xA711, DeviceType::USB),
DEVICE_SIG(GenericPad, 0, 0, DeviceType::HID), DEVICE_SIG(XInputPad, 0, 0, DeviceType::XInput),
DEVICE_SIG(NintendoPowerA, 0x20D6, 0xA711, DeviceType::USB), DEVICE_SIG_SENTINEL()};
DEVICE_SIG(XInputPad, 0, 0, DeviceType::XInput),
DEVICE_SIG_SENTINEL()
};
} }

View File

@ -4,50 +4,80 @@
#include <mutex> #include <mutex>
#include "nxstl/mutex" #include "nxstl/mutex"
namespace boo namespace boo {
{
class IObj {
std::atomic_int m_refCount = {0};
class IObj
{
std::atomic_int m_refCount = {0};
protected: protected:
virtual ~IObj() = default; virtual ~IObj() = default;
public: public:
virtual std::unique_lock<std::recursive_mutex> destructorLock()=0; virtual std::unique_lock<std::recursive_mutex> destructorLock() = 0;
void increment() { m_refCount++; } void increment() { m_refCount++; }
void decrement() void decrement() {
{ if (m_refCount.fetch_sub(1) == 1) {
if (m_refCount.fetch_sub(1) == 1) auto lk = destructorLock();
{ delete this;
auto lk = destructorLock();
delete this;
}
} }
}
}; };
template<class SubCls> template <class SubCls>
class ObjToken class ObjToken {
{ SubCls* m_obj = nullptr;
SubCls* m_obj = nullptr;
public: public:
ObjToken() = default; ObjToken() = default;
ObjToken(SubCls* obj) : m_obj(obj) { if (m_obj) m_obj->increment(); } ObjToken(SubCls* obj) : m_obj(obj) {
ObjToken(const ObjToken& other) : m_obj(other.m_obj) { if (m_obj) m_obj->increment(); } if (m_obj)
ObjToken(ObjToken&& other) : m_obj(other.m_obj) { other.m_obj = nullptr; } m_obj->increment();
ObjToken& operator=(SubCls* obj) }
{ if (m_obj) m_obj->decrement(); m_obj = obj; if (m_obj) m_obj->increment(); return *this; } ObjToken(const ObjToken& other) : m_obj(other.m_obj) {
ObjToken& operator=(const ObjToken& other) if (m_obj)
{ if (m_obj) m_obj->decrement(); m_obj = other.m_obj; if (m_obj) m_obj->increment(); return *this; } m_obj->increment();
ObjToken& operator=(ObjToken&& other) }
{ if (m_obj) m_obj->decrement(); m_obj = other.m_obj; other.m_obj = nullptr; return *this; } ObjToken(ObjToken&& other) : m_obj(other.m_obj) { other.m_obj = nullptr; }
~ObjToken() { if (m_obj) m_obj->decrement(); } ObjToken& operator=(SubCls* obj) {
SubCls* get() const { return m_obj; } if (m_obj)
SubCls* operator->() const { return m_obj; } m_obj->decrement();
SubCls& operator*() const { return *m_obj; } m_obj = obj;
template<class T> T* cast() const { return static_cast<T*>(m_obj); } if (m_obj)
operator bool() const { return m_obj != nullptr; } m_obj->increment();
void reset() { if (m_obj) m_obj->decrement(); m_obj = nullptr; } return *this;
}
ObjToken& operator=(const ObjToken& other) {
if (m_obj)
m_obj->decrement();
m_obj = other.m_obj;
if (m_obj)
m_obj->increment();
return *this;
}
ObjToken& operator=(ObjToken&& other) {
if (m_obj)
m_obj->decrement();
m_obj = other.m_obj;
other.m_obj = nullptr;
return *this;
}
~ObjToken() {
if (m_obj)
m_obj->decrement();
}
SubCls* get() const { return m_obj; }
SubCls* operator->() const { return m_obj; }
SubCls& operator*() const { return *m_obj; }
template <class T>
T* cast() const {
return static_cast<T*>(m_obj);
}
operator bool() const { return m_obj != nullptr; }
void reset() {
if (m_obj)
m_obj->decrement();
m_obj = nullptr;
}
}; };
} } // namespace boo

View File

@ -5,269 +5,242 @@
#include <condition_variable> #include <condition_variable>
#include "nxstl/condition_variable" #include "nxstl/condition_variable"
namespace boo namespace boo {
{
template <class Receiver> template <class Receiver>
struct DeferredWindowEvents : public IWindowCallback struct DeferredWindowEvents : public IWindowCallback {
{ Receiver& m_rec;
Receiver& m_rec; std::mutex m_mt;
std::mutex m_mt; std::condition_variable m_resizeCv;
std::condition_variable m_resizeCv; DeferredWindowEvents(Receiver& rec) : m_rec(rec) {}
DeferredWindowEvents(Receiver& rec) : m_rec(rec) {}
bool m_destroyed = false; bool m_destroyed = false;
void destroyed() void destroyed() { m_destroyed = true; }
{
m_destroyed = true; bool m_hasResize = false;
SWindowRect m_latestResize;
void resized(const SWindowRect& rect, bool sync) {
std::unique_lock<std::mutex> lk(m_mt);
m_latestResize = rect;
m_hasResize = true;
if (sync)
m_resizeCv.wait_for(lk, std::chrono::milliseconds(500));
}
struct Command {
enum class Type {
MouseDown,
MouseUp,
MouseMove,
MouseEnter,
MouseLeave,
Scroll,
TouchDown,
TouchUp,
TouchMove,
CharKeyDown,
CharKeyUp,
SpecialKeyDown,
SpecialKeyUp,
ModKeyDown,
ModKeyUp
} m_type;
SWindowCoord m_coord;
EMouseButton m_button;
EModifierKey m_mods;
SScrollDelta m_scroll;
STouchCoord m_tCoord;
uintptr_t m_tid;
unsigned long m_charcode;
ESpecialKey m_special;
bool m_isRepeat;
void dispatch(Receiver& rec) const {
switch (m_type) {
case Type::MouseDown:
rec.mouseDown(m_coord, m_button, m_mods);
break;
case Type::MouseUp:
rec.mouseUp(m_coord, m_button, m_mods);
break;
case Type::MouseMove:
rec.mouseMove(m_coord);
break;
case Type::MouseEnter:
rec.mouseEnter(m_coord);
break;
case Type::MouseLeave:
rec.mouseLeave(m_coord);
break;
case Type::Scroll:
rec.scroll(m_coord, m_scroll);
break;
case Type::TouchDown:
rec.touchDown(m_tCoord, m_tid);
break;
case Type::TouchUp:
rec.touchUp(m_tCoord, m_tid);
break;
case Type::TouchMove:
rec.touchMove(m_tCoord, m_tid);
break;
case Type::CharKeyDown:
rec.charKeyDown(m_charcode, m_mods, m_isRepeat);
break;
case Type::CharKeyUp:
rec.charKeyUp(m_charcode, m_mods);
break;
case Type::SpecialKeyDown:
rec.specialKeyDown(m_special, m_mods, m_isRepeat);
break;
case Type::SpecialKeyUp:
rec.specialKeyUp(m_special, m_mods);
break;
case Type::ModKeyDown:
rec.modKeyDown(m_mods, m_isRepeat);
break;
case Type::ModKeyUp:
rec.modKeyUp(m_mods);
break;
default:
break;
}
} }
bool m_hasResize = false; Command(Type tp) : m_type(tp) {}
SWindowRect m_latestResize; };
void resized(const SWindowRect& rect, bool sync) std::vector<Command> m_cmds;
{
std::unique_lock<std::mutex> lk(m_mt); void mouseDown(const SWindowCoord& coord, EMouseButton button, EModifierKey mods) {
m_latestResize = rect; std::unique_lock<std::mutex> lk(m_mt);
m_hasResize = true; m_cmds.emplace_back(Command::Type::MouseDown);
if (sync) m_cmds.back().m_coord = coord;
m_resizeCv.wait_for(lk, std::chrono::milliseconds(500)); m_cmds.back().m_button = button;
m_cmds.back().m_mods = mods;
}
void mouseUp(const SWindowCoord& coord, EMouseButton button, EModifierKey mods) {
std::unique_lock<std::mutex> lk(m_mt);
m_cmds.emplace_back(Command::Type::MouseUp);
m_cmds.back().m_coord = coord;
m_cmds.back().m_button = button;
m_cmds.back().m_mods = mods;
}
void mouseMove(const SWindowCoord& coord) {
std::unique_lock<std::mutex> lk(m_mt);
m_cmds.emplace_back(Command::Type::MouseMove);
m_cmds.back().m_coord = coord;
}
void mouseEnter(const SWindowCoord& coord) {
std::unique_lock<std::mutex> lk(m_mt);
m_cmds.emplace_back(Command::Type::MouseEnter);
m_cmds.back().m_coord = coord;
}
void mouseLeave(const SWindowCoord& coord) {
std::unique_lock<std::mutex> lk(m_mt);
m_cmds.emplace_back(Command::Type::MouseLeave);
m_cmds.back().m_coord = coord;
}
void scroll(const SWindowCoord& coord, const SScrollDelta& scroll) {
std::unique_lock<std::mutex> lk(m_mt);
m_cmds.emplace_back(Command::Type::Scroll);
m_cmds.back().m_coord = coord;
m_cmds.back().m_scroll = scroll;
}
void touchDown(const STouchCoord& coord, uintptr_t tid) {
std::unique_lock<std::mutex> lk(m_mt);
m_cmds.emplace_back(Command::Type::TouchDown);
m_cmds.back().m_tCoord = coord;
m_cmds.back().m_tid = tid;
}
void touchUp(const STouchCoord& coord, uintptr_t tid) {
std::unique_lock<std::mutex> lk(m_mt);
m_cmds.emplace_back(Command::Type::TouchUp);
m_cmds.back().m_tCoord = coord;
m_cmds.back().m_tid = tid;
}
void touchMove(const STouchCoord& coord, uintptr_t tid) {
std::unique_lock<std::mutex> lk(m_mt);
m_cmds.emplace_back(Command::Type::TouchMove);
m_cmds.back().m_tCoord = coord;
m_cmds.back().m_tid = tid;
}
void charKeyDown(unsigned long charCode, EModifierKey mods, bool isRepeat) {
std::unique_lock<std::mutex> lk(m_mt);
m_cmds.emplace_back(Command::Type::CharKeyDown);
m_cmds.back().m_charcode = charCode;
m_cmds.back().m_mods = mods;
m_cmds.back().m_isRepeat = isRepeat;
}
void charKeyUp(unsigned long charCode, EModifierKey mods) {
std::unique_lock<std::mutex> lk(m_mt);
m_cmds.emplace_back(Command::Type::CharKeyUp);
m_cmds.back().m_charcode = charCode;
m_cmds.back().m_mods = mods;
}
void specialKeyDown(ESpecialKey key, EModifierKey mods, bool isRepeat) {
std::unique_lock<std::mutex> lk(m_mt);
m_cmds.emplace_back(Command::Type::SpecialKeyDown);
m_cmds.back().m_special = key;
m_cmds.back().m_mods = mods;
m_cmds.back().m_isRepeat = isRepeat;
}
void specialKeyUp(ESpecialKey key, EModifierKey mods) {
std::unique_lock<std::mutex> lk(m_mt);
m_cmds.emplace_back(Command::Type::SpecialKeyUp);
m_cmds.back().m_special = key;
m_cmds.back().m_mods = mods;
}
void modKeyDown(EModifierKey mod, bool isRepeat) {
std::unique_lock<std::mutex> lk(m_mt);
m_cmds.emplace_back(Command::Type::ModKeyDown);
m_cmds.back().m_mods = mod;
m_cmds.back().m_isRepeat = isRepeat;
}
void modKeyUp(EModifierKey mod) {
std::unique_lock<std::mutex> lk(m_mt);
m_cmds.emplace_back(Command::Type::ModKeyUp);
m_cmds.back().m_mods = mod;
}
ITextInputCallback* getTextInputCallback() { return m_rec.getTextInputCallback(); }
void dispatchEvents() {
std::unique_lock<std::mutex> lk(m_mt);
bool destroyed = m_destroyed;
bool hasResize = m_hasResize;
if (hasResize)
m_hasResize = false;
SWindowRect latestResize = m_latestResize;
std::vector<Command> cmds;
m_cmds.swap(cmds);
lk.unlock();
if (destroyed) {
m_rec.destroyed();
return;
} }
struct Command if (hasResize)
{ m_rec.resized(latestResize, false);
enum class Type
{
MouseDown,
MouseUp,
MouseMove,
MouseEnter,
MouseLeave,
Scroll,
TouchDown,
TouchUp,
TouchMove,
CharKeyDown,
CharKeyUp,
SpecialKeyDown,
SpecialKeyUp,
ModKeyDown,
ModKeyUp
} m_type;
SWindowCoord m_coord; for (const Command& cmd : cmds)
EMouseButton m_button; cmd.dispatch(m_rec);
EModifierKey m_mods; }
SScrollDelta m_scroll;
STouchCoord m_tCoord;
uintptr_t m_tid;
unsigned long m_charcode;
ESpecialKey m_special;
bool m_isRepeat;
void dispatch(Receiver& rec) const
{
switch (m_type)
{
case Type::MouseDown:
rec.mouseDown(m_coord, m_button, m_mods);
break;
case Type::MouseUp:
rec.mouseUp(m_coord, m_button, m_mods);
break;
case Type::MouseMove:
rec.mouseMove(m_coord);
break;
case Type::MouseEnter:
rec.mouseEnter(m_coord);
break;
case Type::MouseLeave:
rec.mouseLeave(m_coord);
break;
case Type::Scroll:
rec.scroll(m_coord, m_scroll);
break;
case Type::TouchDown:
rec.touchDown(m_tCoord, m_tid);
break;
case Type::TouchUp:
rec.touchUp(m_tCoord, m_tid);
break;
case Type::TouchMove:
rec.touchMove(m_tCoord, m_tid);
break;
case Type::CharKeyDown:
rec.charKeyDown(m_charcode, m_mods, m_isRepeat);
break;
case Type::CharKeyUp:
rec.charKeyUp(m_charcode, m_mods);
break;
case Type::SpecialKeyDown:
rec.specialKeyDown(m_special, m_mods, m_isRepeat);
break;
case Type::SpecialKeyUp:
rec.specialKeyUp(m_special, m_mods);
break;
case Type::ModKeyDown:
rec.modKeyDown(m_mods, m_isRepeat);
break;
case Type::ModKeyUp:
rec.modKeyUp(m_mods);
break;
default: break;
}
}
Command(Type tp) : m_type(tp) {}
};
std::vector<Command> m_cmds;
void mouseDown(const SWindowCoord& coord, EMouseButton button, EModifierKey mods)
{
std::unique_lock<std::mutex> lk(m_mt);
m_cmds.emplace_back(Command::Type::MouseDown);
m_cmds.back().m_coord = coord;
m_cmds.back().m_button = button;
m_cmds.back().m_mods = mods;
}
void mouseUp(const SWindowCoord& coord, EMouseButton button, EModifierKey mods)
{
std::unique_lock<std::mutex> lk(m_mt);
m_cmds.emplace_back(Command::Type::MouseUp);
m_cmds.back().m_coord = coord;
m_cmds.back().m_button = button;
m_cmds.back().m_mods = mods;
}
void mouseMove(const SWindowCoord& coord)
{
std::unique_lock<std::mutex> lk(m_mt);
m_cmds.emplace_back(Command::Type::MouseMove);
m_cmds.back().m_coord = coord;
}
void mouseEnter(const SWindowCoord& coord)
{
std::unique_lock<std::mutex> lk(m_mt);
m_cmds.emplace_back(Command::Type::MouseEnter);
m_cmds.back().m_coord = coord;
}
void mouseLeave(const SWindowCoord& coord)
{
std::unique_lock<std::mutex> lk(m_mt);
m_cmds.emplace_back(Command::Type::MouseLeave);
m_cmds.back().m_coord = coord;
}
void scroll(const SWindowCoord& coord, const SScrollDelta& scroll)
{
std::unique_lock<std::mutex> lk(m_mt);
m_cmds.emplace_back(Command::Type::Scroll);
m_cmds.back().m_coord = coord;
m_cmds.back().m_scroll = scroll;
}
void touchDown(const STouchCoord& coord, uintptr_t tid)
{
std::unique_lock<std::mutex> lk(m_mt);
m_cmds.emplace_back(Command::Type::TouchDown);
m_cmds.back().m_tCoord = coord;
m_cmds.back().m_tid = tid;
}
void touchUp(const STouchCoord& coord, uintptr_t tid)
{
std::unique_lock<std::mutex> lk(m_mt);
m_cmds.emplace_back(Command::Type::TouchUp);
m_cmds.back().m_tCoord = coord;
m_cmds.back().m_tid = tid;
}
void touchMove(const STouchCoord& coord, uintptr_t tid)
{
std::unique_lock<std::mutex> lk(m_mt);
m_cmds.emplace_back(Command::Type::TouchMove);
m_cmds.back().m_tCoord = coord;
m_cmds.back().m_tid = tid;
}
void charKeyDown(unsigned long charCode, EModifierKey mods, bool isRepeat)
{
std::unique_lock<std::mutex> lk(m_mt);
m_cmds.emplace_back(Command::Type::CharKeyDown);
m_cmds.back().m_charcode = charCode;
m_cmds.back().m_mods = mods;
m_cmds.back().m_isRepeat = isRepeat;
}
void charKeyUp(unsigned long charCode, EModifierKey mods)
{
std::unique_lock<std::mutex> lk(m_mt);
m_cmds.emplace_back(Command::Type::CharKeyUp);
m_cmds.back().m_charcode = charCode;
m_cmds.back().m_mods = mods;
}
void specialKeyDown(ESpecialKey key, EModifierKey mods, bool isRepeat)
{
std::unique_lock<std::mutex> lk(m_mt);
m_cmds.emplace_back(Command::Type::SpecialKeyDown);
m_cmds.back().m_special = key;
m_cmds.back().m_mods = mods;
m_cmds.back().m_isRepeat = isRepeat;
}
void specialKeyUp(ESpecialKey key, EModifierKey mods)
{
std::unique_lock<std::mutex> lk(m_mt);
m_cmds.emplace_back(Command::Type::SpecialKeyUp);
m_cmds.back().m_special = key;
m_cmds.back().m_mods = mods;
}
void modKeyDown(EModifierKey mod, bool isRepeat)
{
std::unique_lock<std::mutex> lk(m_mt);
m_cmds.emplace_back(Command::Type::ModKeyDown);
m_cmds.back().m_mods = mod;
m_cmds.back().m_isRepeat = isRepeat;
}
void modKeyUp(EModifierKey mod)
{
std::unique_lock<std::mutex> lk(m_mt);
m_cmds.emplace_back(Command::Type::ModKeyUp);
m_cmds.back().m_mods = mod;
}
ITextInputCallback* getTextInputCallback() { return m_rec.getTextInputCallback(); }
void dispatchEvents()
{
std::unique_lock<std::mutex> lk(m_mt);
bool destroyed = m_destroyed;
bool hasResize = m_hasResize;
if (hasResize)
m_hasResize = false;
SWindowRect latestResize = m_latestResize;
std::vector<Command> cmds;
m_cmds.swap(cmds);
lk.unlock();
if (destroyed)
{
m_rec.destroyed();
return;
}
if (hasResize)
m_rec.resized(latestResize, false);
for (const Command& cmd : cmds)
cmd.dispatch(m_rec);
}
}; };
} } // namespace boo

View File

@ -7,88 +7,68 @@
#include "IWindow.hpp" #include "IWindow.hpp"
#include "inputdev/DeviceFinder.hpp" #include "inputdev/DeviceFinder.hpp"
namespace boo namespace boo {
{
class IApplication; class IApplication;
struct IApplicationCallback struct IApplicationCallback {
{ virtual int appMain(IApplication*) = 0;
virtual int appMain(IApplication*)=0; virtual void appQuitting(IApplication*) = 0;
virtual void appQuitting(IApplication*)=0; virtual void appFilesOpen(IApplication*, const std::vector<SystemString>&) {}
virtual void appFilesOpen(IApplication*, const std::vector<SystemString>&) {}
}; };
class IApplication class IApplication {
{ friend class WindowCocoa;
friend class WindowCocoa; friend class WindowWayland;
friend class WindowWayland; friend class WindowXlib;
friend class WindowXlib; friend class WindowWin32;
friend class WindowWin32; virtual void _deletedWindow(IWindow* window) = 0;
virtual void _deletedWindow(IWindow* window)=0;
public: public:
virtual ~IApplication() = default; virtual ~IApplication() = default;
enum class EPlatformType enum class EPlatformType {
{ Auto = 0,
Auto = 0, Wayland = 1,
Wayland = 1, Xlib = 2,
Xlib = 2, Android = 3,
Android = 3, Cocoa = 4,
Cocoa = 4, CocoaTouch = 5,
CocoaTouch = 5, Win32 = 6,
Win32 = 6, UWP = 7,
UWP = 7, Revolution = 8,
Revolution = 8, Cafe = 9,
Cafe = 9, NX = 10,
NX = 10, Qt = 11
Qt = 11 };
}; virtual EPlatformType getPlatformType() const = 0;
virtual EPlatformType getPlatformType() const=0;
virtual int run() = 0;
virtual int run()=0; virtual SystemStringView getUniqueName() const = 0;
virtual SystemStringView getUniqueName() const=0; virtual SystemStringView getFriendlyName() const = 0;
virtual SystemStringView getFriendlyName() const=0; virtual SystemStringView getProcessName() const = 0;
virtual SystemStringView getProcessName() const=0; virtual const std::vector<SystemString>& getArgs() const = 0;
virtual const std::vector<SystemString>& getArgs() const=0;
/* Constructors/initializers for sub-objects */
/* Constructors/initializers for sub-objects */ virtual std::shared_ptr<IWindow> newWindow(SystemStringView title) = 0;
virtual std::shared_ptr<IWindow> newWindow(SystemStringView title)=0;
}; };
int int ApplicationRun(IApplication::EPlatformType platform, IApplicationCallback& cb, SystemStringView uniqueName,
ApplicationRun(IApplication::EPlatformType platform, SystemStringView friendlyName, SystemStringView pname, const std::vector<SystemString>& args,
IApplicationCallback& cb, std::string_view gfxApi = {}, uint32_t samples = 1, uint32_t anisotropy = 1, bool deepColor = false,
SystemStringView uniqueName, bool singleInstance = true);
SystemStringView friendlyName,
SystemStringView pname,
const std::vector<SystemString>& args,
std::string_view gfxApi = {},
uint32_t samples = 1,
uint32_t anisotropy = 1,
bool deepColor = false,
bool singleInstance=true);
extern IApplication* APP; extern IApplication* APP;
static inline int static inline int ApplicationRun(IApplication::EPlatformType platform, IApplicationCallback& cb,
ApplicationRun(IApplication::EPlatformType platform, SystemStringView uniqueName, SystemStringView friendlyName, int argc,
IApplicationCallback& cb, const SystemChar** argv, std::string_view gfxApi = {}, uint32_t samples = 1,
SystemStringView uniqueName, uint32_t anisotropy = 1, bool deepColor = false, bool singleInstance = true) {
SystemStringView friendlyName, if (APP)
int argc, const SystemChar** argv, return 1;
std::string_view gfxApi = {}, std::vector<SystemString> args;
uint32_t samples = 1, for (int i = 1; i < argc; ++i)
uint32_t anisotropy = 1, args.push_back(argv[i]);
bool deepColor = false, return ApplicationRun(platform, cb, uniqueName, friendlyName, argv[0], args, gfxApi, samples, anisotropy, deepColor,
bool singleInstance=true) singleInstance);
{
if (APP)
return 1;
std::vector<SystemString> args;
for (int i=1 ; i<argc ; ++i)
args.push_back(argv[i]);
return ApplicationRun(platform, cb, uniqueName, friendlyName, argv[0], args,
gfxApi, samples, anisotropy, deepColor, singleInstance);
}
} }
} // namespace boo

View File

@ -3,61 +3,55 @@
#include <memory> #include <memory>
#include <cstdint> #include <cstdint>
namespace boo namespace boo {
{
struct IGraphicsCommandQueue; struct IGraphicsCommandQueue;
struct IGraphicsDataFactory; struct IGraphicsDataFactory;
class IGraphicsContext class IGraphicsContext {
{ friend class WindowCocoa;
friend class WindowCocoa; friend class WindowXCB;
friend class WindowXCB; virtual void _setCallback(class IWindowCallback* cb) { (void)cb; }
virtual void _setCallback(class IWindowCallback* cb) {(void)cb;}
public: public:
enum class EGraphicsAPI {
enum class EGraphicsAPI None = 0,
{ OpenGL3_3 = 1,
None = 0, OpenGL4_2 = 2,
OpenGL3_3 = 1, Vulkan = 3,
OpenGL4_2 = 2, D3D11 = 4,
Vulkan = 3, Metal = 6,
D3D11 = 4, GX = 7,
Metal = 6, GX2 = 8,
GX = 7, NX = 9
GX2 = 8, };
NX = 9
};
enum class EPixelFormat
{
None = 0,
RGBA8 = 1, /* Default */
RGBA16 = 2,
RGBA8_Z24 = 3,
RGBAF32 = 4,
RGBAF32_Z24 = 5
};
virtual ~IGraphicsContext() = default;
virtual EGraphicsAPI getAPI() const=0;
virtual EPixelFormat getPixelFormat() const=0;
virtual void setPixelFormat(EPixelFormat pf)=0;
virtual bool initializeContext(void* handle)=0;
virtual void makeCurrent()=0;
virtual void postInit()=0;
virtual void present()=0;
virtual IGraphicsCommandQueue* getCommandQueue()=0; enum class EPixelFormat {
virtual IGraphicsDataFactory* getDataFactory()=0; None = 0,
RGBA8 = 1, /* Default */
RGBA16 = 2,
RGBA8_Z24 = 3,
RGBAF32 = 4,
RGBAF32_Z24 = 5
};
/* Creates a new context on current thread!! Call from main client thread */ virtual ~IGraphicsContext() = default;
virtual IGraphicsDataFactory* getMainContextDataFactory()=0;
/* Creates a new context on current thread!! Call from client loading thread */ virtual EGraphicsAPI getAPI() const = 0;
virtual IGraphicsDataFactory* getLoadContextDataFactory()=0; virtual EPixelFormat getPixelFormat() const = 0;
virtual void setPixelFormat(EPixelFormat pf) = 0;
virtual bool initializeContext(void* handle) = 0;
virtual void makeCurrent() = 0;
virtual void postInit() = 0;
virtual void present() = 0;
virtual IGraphicsCommandQueue* getCommandQueue() = 0;
virtual IGraphicsDataFactory* getDataFactory() = 0;
/* Creates a new context on current thread!! Call from main client thread */
virtual IGraphicsDataFactory* getMainContextDataFactory() = 0;
/* Creates a new context on current thread!! Call from client loading thread */
virtual IGraphicsDataFactory* getLoadContextDataFactory() = 0;
}; };
}
} // namespace boo

View File

@ -8,315 +8,286 @@
#undef min #undef min
#undef max #undef max
namespace boo namespace boo {
{
struct IGraphicsCommandQueue; struct IGraphicsCommandQueue;
struct IGraphicsDataFactory; struct IGraphicsDataFactory;
struct IAudioVoiceEngine; struct IAudioVoiceEngine;
enum class EMouseButton enum class EMouseButton { None = 0, Primary = 1, Secondary = 2, Middle = 3, Aux1 = 4, Aux2 = 5 };
{
None = 0, struct SWindowCoord {
Primary = 1, int pixel[2];
Secondary = 2, int virtualPixel[2];
Middle = 3, float norm[2];
Aux1 = 4,
Aux2 = 5
}; };
struct SWindowCoord struct SWindowRect {
{ int location[2];
int pixel[2]; int size[2];
int virtualPixel[2];
float norm[2]; SWindowRect() { std::memset(this, 0, sizeof(SWindowRect)); }
SWindowRect(int x, int y, int w, int h) {
location[0] = x;
location[1] = y;
size[0] = w;
size[1] = h;
}
bool operator!=(const SWindowRect& other) const {
return location[0] != other.location[0] || location[1] != other.location[1] || size[0] != other.size[0] ||
size[1] != other.size[1];
}
bool operator==(const SWindowRect& other) const { return !(*this != other); }
bool coordInRect(const SWindowCoord& coord) const {
return coord.pixel[0] >= location[0] && coord.pixel[0] < location[0] + size[0] && coord.pixel[1] >= location[1] &&
coord.pixel[1] < location[1] + size[1];
}
SWindowRect intersect(const SWindowRect& other) const {
if (location[0] < other.location[0] + other.size[0] && location[0] + size[0] > other.location[0] &&
location[1] < other.location[1] + other.size[1] && location[1] + size[1] > other.location[1]) {
SWindowRect ret;
ret.location[0] = std::max(location[0], other.location[0]);
ret.location[1] = std::max(location[1], other.location[1]);
ret.size[0] = std::min(location[0] + size[0], other.location[0] + other.size[0]) - ret.location[0];
ret.size[1] = std::min(location[1] + size[1], other.location[1] + other.size[1]) - ret.location[1];
return ret;
}
return {};
}
}; };
struct SWindowRect struct STouchCoord {
{ double coord[2];
int location[2];
int size[2];
SWindowRect() {std::memset(this, 0, sizeof(SWindowRect));}
SWindowRect(int x, int y, int w, int h)
{
location[0] = x;
location[1] = y;
size[0] = w;
size[1] = h;
}
bool operator!=(const SWindowRect& other) const
{
return location[0] != other.location[0] ||
location[1] != other.location[1] ||
size[0] != other.size[0] ||
size[1] != other.size[1];
}
bool operator==(const SWindowRect& other) const {return !(*this != other);}
bool coordInRect(const SWindowCoord& coord) const
{
return coord.pixel[0] >= location[0] && coord.pixel[0] < location[0] + size[0] &&
coord.pixel[1] >= location[1] && coord.pixel[1] < location[1] + size[1];
}
SWindowRect intersect(const SWindowRect& other) const
{
if (location[0] < other.location[0] + other.size[0] &&
location[0] + size[0] > other.location[0] &&
location[1] < other.location[1] + other.size[1] &&
location[1] + size[1] > other.location[1])
{
SWindowRect ret;
ret.location[0] = std::max(location[0], other.location[0]);
ret.location[1] = std::max(location[1], other.location[1]);
ret.size[0] = std::min(location[0] + size[0], other.location[0] + other.size[0]) - ret.location[0];
ret.size[1] = std::min(location[1] + size[1], other.location[1] + other.size[1]) - ret.location[1];
return ret;
}
return {};
}
}; };
struct STouchCoord struct SScrollDelta {
{ double delta[2];
double coord[2]; bool isFine; /* Use system-scale fine-scroll (for scrollable-trackpads) */
bool isAccelerated = false; /* System performs acceleration computation */
SScrollDelta operator+(const SScrollDelta& other) {
SScrollDelta ret;
ret.delta[0] = delta[0] + other.delta[0];
ret.delta[1] = delta[1] + other.delta[1];
ret.isFine = isFine || other.isFine;
ret.isAccelerated = isAccelerated || other.isAccelerated;
return ret;
}
SScrollDelta& operator+=(const SScrollDelta& other) {
delta[0] += other.delta[0];
delta[1] += other.delta[1];
isFine |= other.isFine;
isAccelerated |= other.isAccelerated;
return *this;
}
void zeroOut() {
delta[0] = 0.0;
delta[1] = 0.0;
}
}; };
struct SScrollDelta enum class ESpecialKey {
{ None = 0,
double delta[2]; F1 = 1,
bool isFine; /* Use system-scale fine-scroll (for scrollable-trackpads) */ F2 = 2,
bool isAccelerated = false; /* System performs acceleration computation */ F3 = 3,
F4 = 4,
SScrollDelta operator+(const SScrollDelta& other) F5 = 5,
{ F6 = 6,
SScrollDelta ret; F7 = 7,
ret.delta[0] = delta[0] + other.delta[0]; F8 = 8,
ret.delta[1] = delta[1] + other.delta[1]; F9 = 9,
ret.isFine = isFine || other.isFine; F10 = 10,
ret.isAccelerated = isAccelerated || other.isAccelerated; F11 = 11,
return ret; F12 = 12,
} Esc = 13,
SScrollDelta& operator+=(const SScrollDelta& other) Enter = 14,
{ Backspace = 15,
delta[0] += other.delta[0]; Insert = 16,
delta[1] += other.delta[1]; Delete = 17,
isFine |= other.isFine; Home = 18,
isAccelerated |= other.isAccelerated; End = 19,
return *this; PgUp = 20,
} PgDown = 21,
void zeroOut() {delta[0] = 0.0; delta[1] = 0.0;} Left = 22,
Right = 23,
Up = 24,
Down = 25
}; };
enum class ESpecialKey enum class EModifierKey {
{ None = 0,
None = 0, Ctrl = 1 << 0,
F1 = 1, Alt = 1 << 2,
F2 = 2, Shift = 1 << 3,
F3 = 3, Command = 1 << 4,
F4 = 4, CtrlCommand = EModifierKey::Ctrl | EModifierKey::Command
F5 = 5,
F6 = 6,
F7 = 7,
F8 = 8,
F9 = 9,
F10 = 10,
F11 = 11,
F12 = 12,
Esc = 13,
Enter = 14,
Backspace = 15,
Insert = 16,
Delete = 17,
Home = 18,
End = 19,
PgUp = 20,
PgDown = 21,
Left = 22,
Right = 23,
Up = 24,
Down = 25
};
enum class EModifierKey
{
None = 0,
Ctrl = 1<<0,
Alt = 1<<2,
Shift = 1<<3,
Command = 1<<4,
CtrlCommand = EModifierKey::Ctrl | EModifierKey::Command
}; };
ENABLE_BITWISE_ENUM(EModifierKey) ENABLE_BITWISE_ENUM(EModifierKey)
struct ITextInputCallback struct ITextInputCallback {
{ virtual bool hasMarkedText() const = 0;
virtual bool hasMarkedText() const=0; virtual std::pair<int, int> markedRange() const = 0;
virtual std::pair<int,int> markedRange() const=0; virtual std::pair<int, int> selectedRange() const = 0;
virtual std::pair<int,int> selectedRange() const=0; virtual void setMarkedText(std::string_view str, const std::pair<int, int>& selectedRange,
virtual void setMarkedText(std::string_view str, const std::pair<int, int>& replacementRange) = 0;
const std::pair<int,int>& selectedRange, virtual void unmarkText() = 0;
const std::pair<int,int>& replacementRange)=0;
virtual void unmarkText()=0; virtual std::string substringForRange(const std::pair<int, int>& range, std::pair<int, int>& actualRange) const = 0;
virtual void insertText(std::string_view str, const std::pair<int, int>& range = {-1, 0}) = 0;
virtual std::string substringForRange(const std::pair<int,int>& range, virtual int characterIndexAtPoint(const SWindowCoord& point) const = 0;
std::pair<int,int>& actualRange) const=0; virtual SWindowRect rectForCharacterRange(const std::pair<int, int>& range,
virtual void insertText(std::string_view str, const std::pair<int,int>& range={-1,0})=0; std::pair<int, int>& actualRange) const = 0;
virtual int characterIndexAtPoint(const SWindowCoord& point) const=0;
virtual SWindowRect rectForCharacterRange(const std::pair<int,int>& range,
std::pair<int,int>& actualRange) const=0;
}; };
class IWindowCallback class IWindowCallback {
{
public: public:
virtual void resized(const SWindowRect& rect, bool sync) virtual void resized(const SWindowRect& rect, bool sync) { (void)rect; }
{(void)rect;} virtual void mouseDown(const SWindowCoord& coord, EMouseButton button, EModifierKey mods) {
virtual void mouseDown(const SWindowCoord& coord, EMouseButton button, EModifierKey mods) (void)coord;
{(void)coord;(void)button;(void)mods;} (void)button;
virtual void mouseUp(const SWindowCoord& coord, EMouseButton button, EModifierKey mods) (void)mods;
{(void)coord;(void)button;(void)mods;} }
virtual void mouseMove(const SWindowCoord& coord) virtual void mouseUp(const SWindowCoord& coord, EMouseButton button, EModifierKey mods) {
{(void)coord;} (void)coord;
virtual void mouseEnter(const SWindowCoord& coord) (void)button;
{(void)coord;} (void)mods;
virtual void mouseLeave(const SWindowCoord& coord) }
{(void)coord;} virtual void mouseMove(const SWindowCoord& coord) { (void)coord; }
virtual void scroll(const SWindowCoord& coord, const SScrollDelta& scroll) virtual void mouseEnter(const SWindowCoord& coord) { (void)coord; }
{(void)coord;(void)scroll;} virtual void mouseLeave(const SWindowCoord& coord) { (void)coord; }
virtual void scroll(const SWindowCoord& coord, const SScrollDelta& scroll) {
(void)coord;
(void)scroll;
}
virtual void touchDown(const STouchCoord& coord, uintptr_t tid) virtual void touchDown(const STouchCoord& coord, uintptr_t tid) {
{(void)coord;(void)tid;} (void)coord;
virtual void touchUp(const STouchCoord& coord, uintptr_t tid) (void)tid;
{(void)coord;(void)tid;} }
virtual void touchMove(const STouchCoord& coord, uintptr_t tid) virtual void touchUp(const STouchCoord& coord, uintptr_t tid) {
{(void)coord;(void)tid;} (void)coord;
(void)tid;
}
virtual void touchMove(const STouchCoord& coord, uintptr_t tid) {
(void)coord;
(void)tid;
}
virtual void charKeyDown(unsigned long charCode, EModifierKey mods, bool isRepeat) virtual void charKeyDown(unsigned long charCode, EModifierKey mods, bool isRepeat) {
{(void)charCode;(void)mods;(void)isRepeat;} (void)charCode;
virtual void charKeyUp(unsigned long charCode, EModifierKey mods) (void)mods;
{(void)charCode;(void)mods;} (void)isRepeat;
virtual void specialKeyDown(ESpecialKey key, EModifierKey mods, bool isRepeat) }
{(void)key;(void)mods;(void)isRepeat;} virtual void charKeyUp(unsigned long charCode, EModifierKey mods) {
virtual void specialKeyUp(ESpecialKey key, EModifierKey mods) (void)charCode;
{(void)key;(void)mods;} (void)mods;
virtual void modKeyDown(EModifierKey mod, bool isRepeat) }
{(void)mod;(void)isRepeat;} virtual void specialKeyDown(ESpecialKey key, EModifierKey mods, bool isRepeat) {
virtual void modKeyUp(EModifierKey mod) {(void)mod;} (void)key;
(void)mods;
virtual ITextInputCallback* getTextInputCallback() {return nullptr;} (void)isRepeat;
}
virtual void focusLost() {} virtual void specialKeyUp(ESpecialKey key, EModifierKey mods) {
virtual void focusGained() {} (void)key;
virtual void windowMoved(const SWindowRect& rect) (void)mods;
{ (void)rect; } }
virtual void modKeyDown(EModifierKey mod, bool isRepeat) {
(void)mod;
(void)isRepeat;
}
virtual void modKeyUp(EModifierKey mod) { (void)mod; }
virtual void destroyed() virtual ITextInputCallback* getTextInputCallback() { return nullptr; }
{}
virtual void focusLost() {}
virtual void focusGained() {}
virtual void windowMoved(const SWindowRect& rect) { (void)rect; }
virtual void destroyed() {}
}; };
enum class ETouchType enum class ETouchType { None = 0, Display = 1, Trackpad = 2 };
{
None = 0,
Display = 1,
Trackpad = 2
};
enum class EWindowStyle enum class EWindowStyle {
{ None = 0,
None = 0, Titlebar = 1 << 0,
Titlebar = 1<<0, Resize = 1 << 1,
Resize = 1<<1, Close = 1 << 2,
Close = 1<<2,
Default = Titlebar | Resize | Close Default = Titlebar | Resize | Close
}; };
ENABLE_BITWISE_ENUM(EWindowStyle) ENABLE_BITWISE_ENUM(EWindowStyle)
enum class EMouseCursor enum class EMouseCursor { None = 0, Pointer = 1, HorizontalArrow = 2, VerticalArrow = 3, IBeam = 4, Crosshairs = 5 };
{
None = 0,
Pointer = 1,
HorizontalArrow = 2,
VerticalArrow = 3,
IBeam = 4,
Crosshairs = 5
};
enum class EClipboardType enum class EClipboardType { None = 0, String = 1, UTF8String = 2, PNGImage = 3 };
{
None = 0,
String = 1,
UTF8String = 2,
PNGImage = 3
};
class IWindow : public std::enable_shared_from_this<IWindow> class IWindow : public std::enable_shared_from_this<IWindow> {
{
public: public:
virtual ~IWindow() = default;
virtual ~IWindow() = default;
virtual void setCallback(IWindowCallback* cb)=0;
virtual void closeWindow()=0; virtual void setCallback(IWindowCallback* cb) = 0;
virtual void showWindow()=0;
virtual void hideWindow()=0;
virtual SystemString getTitle()=0;
virtual void setTitle(SystemStringView title)=0;
virtual void setCursor(EMouseCursor cursor)=0; virtual void closeWindow() = 0;
virtual void setWaitCursor(bool wait)=0; virtual void showWindow() = 0;
virtual void hideWindow() = 0;
virtual void setWindowFrameDefault()=0; virtual SystemString getTitle() = 0;
virtual void getWindowFrame(float& xOut, float& yOut, float& wOut, float& hOut) const=0; virtual void setTitle(SystemStringView title) = 0;
virtual void getWindowFrame(int& xOut, int& yOut, int& wOut, int& hOut) const=0;
virtual SWindowRect getWindowFrame() const
{
SWindowRect retval;
getWindowFrame(retval.location[0], retval.location[1], retval.size[0], retval.size[1]);
return retval;
}
virtual void setWindowFrame(float x, float y, float w, float h)=0;
virtual void setWindowFrame(int x, int y, int w, int h)=0;
virtual void setWindowFrame(const SWindowRect& rect)
{
setWindowFrame(rect.location[0], rect.location[1], rect.size[0], rect.size[1]);
}
virtual float getVirtualPixelFactor() const=0;
virtual bool isFullscreen() const=0;
virtual void setFullscreen(bool fs)=0;
virtual void claimKeyboardFocus(const int coord[2])=0; virtual void setCursor(EMouseCursor cursor) = 0;
virtual bool clipboardCopy(EClipboardType type, const uint8_t* data, size_t sz)=0; virtual void setWaitCursor(bool wait) = 0;
virtual std::unique_ptr<uint8_t[]> clipboardPaste(EClipboardType type, size_t& sz)=0;
virtual void waitForRetrace()=0; virtual void setWindowFrameDefault() = 0;
virtual void getWindowFrame(float& xOut, float& yOut, float& wOut, float& hOut) const = 0;
virtual uintptr_t getPlatformHandle() const=0; virtual void getWindowFrame(int& xOut, int& yOut, int& wOut, int& hOut) const = 0;
virtual bool _incomingEvent(void* event) {(void)event; return false;} virtual SWindowRect getWindowFrame() const {
virtual void _cleanup() {} SWindowRect retval;
getWindowFrame(retval.location[0], retval.location[1], retval.size[0], retval.size[1]);
return retval;
}
virtual void setWindowFrame(float x, float y, float w, float h) = 0;
virtual void setWindowFrame(int x, int y, int w, int h) = 0;
virtual void setWindowFrame(const SWindowRect& rect) {
setWindowFrame(rect.location[0], rect.location[1], rect.size[0], rect.size[1]);
}
virtual float getVirtualPixelFactor() const = 0;
virtual ETouchType getTouchType() const=0; virtual bool isFullscreen() const = 0;
virtual void setFullscreen(bool fs) = 0;
virtual void setStyle(EWindowStyle style)=0; virtual void claimKeyboardFocus(const int coord[2]) = 0;
virtual EWindowStyle getStyle() const=0; virtual bool clipboardCopy(EClipboardType type, const uint8_t* data, size_t sz) = 0;
virtual std::unique_ptr<uint8_t[]> clipboardPaste(EClipboardType type, size_t& sz) = 0;
virtual void setTouchBarProvider(void*) {} virtual void waitForRetrace() = 0;
virtual IGraphicsCommandQueue* getCommandQueue()=0; virtual uintptr_t getPlatformHandle() const = 0;
virtual IGraphicsDataFactory* getDataFactory()=0; virtual bool _incomingEvent(void* event) {
(void)event;
return false;
}
virtual void _cleanup() {}
/* Creates a new context on current thread!! Call from main client thread */ virtual ETouchType getTouchType() const = 0;
virtual IGraphicsDataFactory* getMainContextDataFactory()=0;
/* Creates a new context on current thread!! Call from client loading thread */ virtual void setStyle(EWindowStyle style) = 0;
virtual IGraphicsDataFactory* getLoadContextDataFactory()=0; virtual EWindowStyle getStyle() const = 0;
virtual void setTouchBarProvider(void*) {}
virtual IGraphicsCommandQueue* getCommandQueue() = 0;
virtual IGraphicsDataFactory* getDataFactory() = 0;
/* Creates a new context on current thread!! Call from main client thread */
virtual IGraphicsDataFactory* getMainContextDataFactory() = 0;
/* Creates a new context on current thread!! Call from client loading thread */
virtual IGraphicsDataFactory* getLoadContextDataFactory() = 0;
}; };
}
} // namespace boo

View File

@ -14,68 +14,65 @@
template <class T> template <class T>
using ComPtr = Microsoft::WRL::ComPtr<T>; using ComPtr = Microsoft::WRL::ComPtr<T>;
template <class T> template <class T>
static inline ComPtr<T>* ReferenceComPtr(ComPtr<T>& ptr) static inline ComPtr<T>* ReferenceComPtr(ComPtr<T>& ptr) {
{ return reinterpret_cast<ComPtr<T>*>(ptr.GetAddressOf()); } return reinterpret_cast<ComPtr<T>*>(ptr.GetAddressOf());
}
#endif #endif
#include <string> #include <string>
#include <string_view> #include <string_view>
#ifndef ENABLE_BITWISE_ENUM #ifndef ENABLE_BITWISE_ENUM
#define ENABLE_BITWISE_ENUM(type)\ #define ENABLE_BITWISE_ENUM(type) \
constexpr type operator|(type a, type b)\ constexpr type operator|(type a, type b) { \
{\ using T = std::underlying_type_t<type>; \
using T = std::underlying_type_t<type>;\ return type(static_cast<T>(a) | static_cast<T>(b)); \
return type(static_cast<T>(a) | static_cast<T>(b));\ } \
}\ constexpr type operator&(type a, type b) { \
constexpr type operator&(type a, type b)\ using T = std::underlying_type_t<type>; \
{\ return type(static_cast<T>(a) & static_cast<T>(b)); \
using T = std::underlying_type_t<type>;\ } \
return type(static_cast<T>(a) & static_cast<T>(b));\ inline type& operator|=(type& a, const type& b) { \
}\ using T = std::underlying_type_t<type>; \
inline type& operator|=(type& a, const type& b)\ a = type(static_cast<T>(a) | static_cast<T>(b)); \
{\ return a; \
using T = std::underlying_type_t<type>;\ } \
a = type(static_cast<T>(a) | static_cast<T>(b));\ inline type& operator&=(type& a, const type& b) { \
return a;\ using T = std::underlying_type_t<type>; \
}\ a = type(static_cast<T>(a) & static_cast<T>(b)); \
inline type& operator&=(type& a, const type& b)\ return a; \
{\ } \
using T = std::underlying_type_t<type>;\ inline type operator~(const type& key) { \
a = type(static_cast<T>(a) & static_cast<T>(b));\ using T = std::underlying_type_t<type>; \
return a;\ return type(~static_cast<T>(key)); \
}\ }
inline type operator~(const type& key)\
{\
using T = std::underlying_type_t<type>;\
return type(~static_cast<T>(key));\
}
#endif #endif
namespace boo namespace boo {
{
#ifdef _WIN32 #ifdef _WIN32
using SystemString = std::wstring; using SystemString = std::wstring;
using SystemStringView = std::wstring_view; using SystemStringView = std::wstring_view;
using SystemChar = wchar_t; using SystemChar = wchar_t;
# ifndef _SYS_STR #ifndef _SYS_STR
# define _SYS_STR(val) L ## val #define _SYS_STR(val) L##val
# endif #endif
#else #else
using SystemString = std::string; using SystemString = std::string;
using SystemStringView = std::string_view; using SystemStringView = std::string_view;
using SystemChar = char; using SystemChar = char;
# ifndef _SYS_STR #ifndef _SYS_STR
# define _SYS_STR(val) val #define _SYS_STR(val) val
# endif #endif
#endif #endif
#ifndef NDEBUG #ifndef NDEBUG
#define __BooTraceArgs , const char* file, int line #define __BooTraceArgs , const char *file, int line
#define __BooTraceArgsUse , file, line #define __BooTraceArgsUse , file, line
#define __BooTraceInitializer , m_file(file), m_line(line) #define __BooTraceInitializer , m_file(file), m_line(line)
#define __BooTraceFields const char* m_file; int m_line; #define __BooTraceFields \
const char* m_file; \
int m_line;
#define BooTrace , __FILE__, __LINE__ #define BooTrace , __FILE__, __LINE__
#else #else
#define __BooTraceArgs #define __BooTraceArgs
@ -85,5 +82,4 @@ namespace boo
#define BooTrace #define BooTrace
#endif #endif
} } // namespace boo

View File

@ -9,25 +9,25 @@
/** Multiplatform TLS-pointer wrapper (for compilers without proper thread_local support) */ /** Multiplatform TLS-pointer wrapper (for compilers without proper thread_local support) */
template <class T> template <class T>
class ThreadLocalPtr class ThreadLocalPtr {
{
#if _WIN32 #if _WIN32
DWORD m_key; DWORD m_key;
public: public:
ThreadLocalPtr() {m_key = TlsAlloc();} ThreadLocalPtr() { m_key = TlsAlloc(); }
~ThreadLocalPtr() {TlsFree(m_key);} ~ThreadLocalPtr() { TlsFree(m_key); }
T* get() const {return static_cast<T*>(TlsGetValue(m_key));} T* get() const { return static_cast<T*>(TlsGetValue(m_key)); }
void reset(T* v=nullptr) {TlsSetValue(m_key, LPVOID(v));} void reset(T* v = nullptr) { TlsSetValue(m_key, LPVOID(v)); }
#else #else
pthread_key_t m_key; pthread_key_t m_key;
public: public:
ThreadLocalPtr() {pthread_key_create(&m_key, nullptr);} ThreadLocalPtr() { pthread_key_create(&m_key, nullptr); }
~ThreadLocalPtr() {pthread_key_delete(m_key);} ~ThreadLocalPtr() { pthread_key_delete(m_key); }
T* get() const {return static_cast<T*>(pthread_getspecific(m_key));} T* get() const { return static_cast<T*>(pthread_getspecific(m_key)); }
void reset(T* v=nullptr) {pthread_setspecific(m_key, v);} void reset(T* v = nullptr) { pthread_setspecific(m_key, v); }
#endif #endif
T* operator->() {return get();} T* operator->() { return get(); }
}; };
#endif #endif

View File

@ -2,43 +2,37 @@
#include "IApplication.hpp" #include "IApplication.hpp"
namespace boo namespace boo {
{
#if WINDOWS_STORE #if WINDOWS_STORE
using namespace Windows::ApplicationModel::Core; using namespace Windows::ApplicationModel::Core;
ref struct ViewProvider sealed : IFrameworkViewSource ref struct ViewProvider sealed : IFrameworkViewSource {
{ internal : ViewProvider(boo::IApplicationCallback& appCb, SystemStringView uniqueName, SystemStringView friendlyName,
internal: SystemStringView pname, Platform::Array<Platform::String ^> ^ params, bool singleInstance)
ViewProvider(boo::IApplicationCallback& appCb, : m_appCb(appCb)
SystemStringView uniqueName, , m_uniqueName(uniqueName)
SystemStringView friendlyName, , m_friendlyName(friendlyName)
SystemStringView pname, , m_pname(pname)
Platform::Array<Platform::String^>^ params, , m_singleInstance(singleInstance) {
bool singleInstance) SystemChar selfPath[1024];
: m_appCb(appCb), m_uniqueName(uniqueName), m_friendlyName(friendlyName), GetModuleFileNameW(nullptr, selfPath, 1024);
m_pname(pname), m_singleInstance(singleInstance) m_args.reserve(params->Length + 1);
{ m_args.emplace_back(selfPath);
SystemChar selfPath[1024]; for (Platform::String ^ str : params)
GetModuleFileNameW(nullptr, selfPath, 1024); m_args.emplace_back(str->Data());
m_args.reserve(params->Length + 1); }
m_args.emplace_back(selfPath);
for (Platform::String^ str : params)
m_args.emplace_back(str->Data());
}
public:
virtual IFrameworkView^ CreateView();
internal: public:
boo::IApplicationCallback& m_appCb; virtual IFrameworkView ^ CreateView();
SystemString m_uniqueName;
SystemString m_friendlyName; internal : boo::IApplicationCallback& m_appCb;
SystemString m_pname; SystemString m_uniqueName;
std::vector<SystemString> m_args; SystemString m_friendlyName;
bool m_singleInstance; SystemString m_pname;
std::vector<SystemString> m_args;
bool m_singleInstance;
}; };
#endif #endif
}
} // namespace boo

View File

@ -5,51 +5,39 @@
#include <memory> #include <memory>
#include "boo/BooObject.hpp" #include "boo/BooObject.hpp"
namespace boo namespace boo {
{
struct IAudioVoice; struct IAudioVoice;
struct IAudioVoiceCallback; struct IAudioVoiceCallback;
struct ChannelMap; struct ChannelMap;
struct IAudioSubmixCallback; struct IAudioSubmixCallback;
enum class SubmixFormat enum class SubmixFormat { Int16, Int32, Float };
{
Int16, struct IAudioSubmix : IObj {
Int32, /** Reset channel-levels to silence; unbind all submixes */
Float virtual void resetSendLevels() = 0;
/** Set channel-levels for target submix (AudioChannel enum for array index) */
virtual void setSendLevel(IAudioSubmix* submix, float level, bool slew) = 0;
/** Gets fixed sample rate of submix this way */
virtual double getSampleRate() const = 0;
/** Gets fixed sample format of submix this way */
virtual SubmixFormat getSampleFormat() const = 0;
}; };
struct IAudioSubmix : IObj struct IAudioSubmixCallback {
{ /** Client-provided claim to implement / is ready to call applyEffect() */
/** Reset channel-levels to silence; unbind all submixes */ virtual bool canApplyEffect() const = 0;
virtual void resetSendLevels()=0;
/** Set channel-levels for target submix (AudioChannel enum for array index) */ /** Client-provided effect solution for interleaved, master sample-rate audio */
virtual void setSendLevel(IAudioSubmix* submix, float level, bool slew)=0; virtual void applyEffect(int16_t* audio, size_t frameCount, const ChannelMap& chanMap, double sampleRate) const = 0;
virtual void applyEffect(int32_t* audio, size_t frameCount, const ChannelMap& chanMap, double sampleRate) const = 0;
virtual void applyEffect(float* audio, size_t frameCount, const ChannelMap& chanMap, double sampleRate) const = 0;
/** Gets fixed sample rate of submix this way */ /** Notify of output sample rate changes (for instance, changing the default audio device on Windows) */
virtual double getSampleRate() const=0; virtual void resetOutputSampleRate(double sampleRate) = 0;
/** Gets fixed sample format of submix this way */
virtual SubmixFormat getSampleFormat() const=0;
}; };
struct IAudioSubmixCallback } // namespace boo
{
/** Client-provided claim to implement / is ready to call applyEffect() */
virtual bool canApplyEffect() const=0;
/** Client-provided effect solution for interleaved, master sample-rate audio */
virtual void applyEffect(int16_t* audio, size_t frameCount,
const ChannelMap& chanMap, double sampleRate) const=0;
virtual void applyEffect(int32_t* audio, size_t frameCount,
const ChannelMap& chanMap, double sampleRate) const=0;
virtual void applyEffect(float* audio, size_t frameCount,
const ChannelMap& chanMap, double sampleRate) const=0;
/** Notify of output sample rate changes (for instance, changing the default audio device on Windows) */
virtual void resetOutputSampleRate(double sampleRate)=0;
};
}

View File

@ -5,106 +5,89 @@
#include <cstring> #include <cstring>
#include "boo/BooObject.hpp" #include "boo/BooObject.hpp"
namespace boo namespace boo {
{
struct IAudioSubmix; struct IAudioSubmix;
enum class AudioChannelSet enum class AudioChannelSet { Stereo, Quad, Surround51, Surround71, Unknown = 0xff };
{
Stereo, enum class AudioChannel {
Quad, FrontLeft,
Surround51, FrontRight,
Surround71, RearLeft,
Unknown = 0xff RearRight,
FrontCenter,
LFE,
SideLeft,
SideRight,
Unknown = 0xff
}; };
enum class AudioChannel struct ChannelMap {
{ unsigned m_channelCount = 0;
FrontLeft, AudioChannel m_channels[8] = {};
FrontRight,
RearLeft,
RearRight,
FrontCenter,
LFE,
SideLeft,
SideRight,
Unknown = 0xff
}; };
struct ChannelMap static inline unsigned ChannelCount(AudioChannelSet layout) {
{ switch (layout) {
unsigned m_channelCount = 0; case AudioChannelSet::Stereo:
AudioChannel m_channels[8] = {}; return 2;
}; case AudioChannelSet::Quad:
return 4;
static inline unsigned ChannelCount(AudioChannelSet layout) case AudioChannelSet::Surround51:
{ return 6;
switch (layout) case AudioChannelSet::Surround71:
{ return 8;
case AudioChannelSet::Stereo: default:
return 2; break;
case AudioChannelSet::Quad: }
return 4; return 0;
case AudioChannelSet::Surround51:
return 6;
case AudioChannelSet::Surround71:
return 8;
default: break;
}
return 0;
} }
struct IAudioVoice : IObj struct IAudioVoice : IObj {
{ /** Set sample rate into voice (may result in audio discontinuities) */
/** Set sample rate into voice (may result in audio discontinuities) */ virtual void resetSampleRate(double sampleRate) = 0;
virtual void resetSampleRate(double sampleRate)=0;
/** Reset channel-levels to silence; unbind all submixes */ /** Reset channel-levels to silence; unbind all submixes */
virtual void resetChannelLevels()=0; virtual void resetChannelLevels() = 0;
/** Set channel-levels for mono audio source (AudioChannel enum for array index) */ /** Set channel-levels for mono audio source (AudioChannel enum for array index) */
virtual void setMonoChannelLevels(IAudioSubmix* submix, const float coefs[8], bool slew)=0; virtual void setMonoChannelLevels(IAudioSubmix* submix, const float coefs[8], bool slew) = 0;
/** Set channel-levels for stereo audio source (AudioChannel enum for array index) */ /** Set channel-levels for stereo audio source (AudioChannel enum for array index) */
virtual void setStereoChannelLevels(IAudioSubmix* submix, const float coefs[8][2], bool slew)=0; virtual void setStereoChannelLevels(IAudioSubmix* submix, const float coefs[8][2], bool slew) = 0;
/** Called by client to dynamically adjust the pitch of voices with dynamic pitch enabled */ /** Called by client to dynamically adjust the pitch of voices with dynamic pitch enabled */
virtual void setPitchRatio(double ratio, bool slew)=0; virtual void setPitchRatio(double ratio, bool slew) = 0;
/** Instructs platform to begin consuming sample data; invoking callback as needed */ /** Instructs platform to begin consuming sample data; invoking callback as needed */
virtual void start()=0; virtual void start() = 0;
/** Instructs platform to stop consuming sample data */ /** Instructs platform to stop consuming sample data */
virtual void stop()=0; virtual void stop() = 0;
}; };
struct IAudioVoiceCallback struct IAudioVoiceCallback {
{ /** boo calls this on behalf of the audio platform to proactively invoke potential
/** boo calls this on behalf of the audio platform to proactively invoke potential * pitch or panning changes before processing samples */
* pitch or panning changes before processing samples */ virtual void preSupplyAudio(boo::IAudioVoice& voice, double dt) = 0;
virtual void preSupplyAudio(boo::IAudioVoice& voice, double dt)=0;
/** boo calls this on behalf of the audio platform to request more audio /** boo calls this on behalf of the audio platform to request more audio
* frames from the client */ * frames from the client */
virtual size_t supplyAudio(IAudioVoice& voice, size_t frames, int16_t* data)=0; virtual size_t supplyAudio(IAudioVoice& voice, size_t frames, int16_t* data) = 0;
/** after resampling, boo calls this for each submix that this voice targets; /** after resampling, boo calls this for each submix that this voice targets;
* client performs volume processing and bus-routing this way */ * client performs volume processing and bus-routing this way */
virtual void routeAudio(size_t frames, size_t channels, double dt, int busId, int16_t* in, int16_t* out) virtual void routeAudio(size_t frames, size_t channels, double dt, int busId, int16_t* in, int16_t* out) {
{ memmove(out, in, frames * channels * 2);
memmove(out, in, frames * channels * 2); }
}
virtual void routeAudio(size_t frames, size_t channels, double dt, int busId, int32_t* in, int32_t* out) virtual void routeAudio(size_t frames, size_t channels, double dt, int busId, int32_t* in, int32_t* out) {
{ memmove(out, in, frames * channels * 4);
memmove(out, in, frames * channels * 4); }
}
virtual void routeAudio(size_t frames, size_t channels, double dt, int busId, float* in, float* out) virtual void routeAudio(size_t frames, size_t channels, double dt, int busId, float* in, float* out) {
{ memmove(out, in, frames * channels * 4);
memmove(out, in, frames * channels * 4); }
}
}; };
} } // namespace boo

View File

@ -7,100 +7,95 @@
#include <memory> #include <memory>
#include <vector> #include <vector>
namespace boo namespace boo {
{
struct IAudioVoiceEngine; struct IAudioVoiceEngine;
/** Time-sensitive event callback for synchronizing the client with rendered audio waveform */ /** Time-sensitive event callback for synchronizing the client with rendered audio waveform */
struct IAudioVoiceEngineCallback struct IAudioVoiceEngineCallback {
{ /** All mixing occurs in virtual 5ms intervals;
/** All mixing occurs in virtual 5ms intervals; * this is called at the start of each interval for all mixable entities */
* this is called at the start of each interval for all mixable entities */ virtual void on5MsInterval(IAudioVoiceEngine& engine, double dt) {}
virtual void on5MsInterval(IAudioVoiceEngine& engine, double dt) {}
/** When a pumping cycle is complete this is called to allow the client to /** When a pumping cycle is complete this is called to allow the client to
* perform periodic cleanup tasks */ * perform periodic cleanup tasks */
virtual void onPumpCycleComplete(IAudioVoiceEngine& engine) {} virtual void onPumpCycleComplete(IAudioVoiceEngine& engine) {}
}; };
/** Mixing and sample-rate-conversion system. Allocates voices and mixes them /** Mixing and sample-rate-conversion system. Allocates voices and mixes them
* before sending the final samples to an OS-supplied audio-queue */ * before sending the final samples to an OS-supplied audio-queue */
struct IAudioVoiceEngine struct IAudioVoiceEngine {
{ virtual ~IAudioVoiceEngine() = default;
virtual ~IAudioVoiceEngine() = default;
/** Client calls this to request allocation of new mixer-voice. /** Client calls this to request allocation of new mixer-voice.
* Returns empty unique_ptr if necessary resources aren't available. * Returns empty unique_ptr if necessary resources aren't available.
* ChannelLayout automatically reduces to maximum-supported layout by HW. * ChannelLayout automatically reduces to maximum-supported layout by HW.
* *
* Client must be prepared to supply audio frames via the callback when this is called; * Client must be prepared to supply audio frames via the callback when this is called;
* the backing audio-buffers are primed with initial data for low-latency playback start * the backing audio-buffers are primed with initial data for low-latency playback start
*/ */
virtual ObjToken<IAudioVoice> allocateNewMonoVoice(double sampleRate, virtual ObjToken<IAudioVoice> allocateNewMonoVoice(double sampleRate, IAudioVoiceCallback* cb,
IAudioVoiceCallback* cb, bool dynamicPitch = false) = 0;
bool dynamicPitch=false)=0;
/** Same as allocateNewMonoVoice, but source audio is stereo-interleaved */ /** Same as allocateNewMonoVoice, but source audio is stereo-interleaved */
virtual ObjToken<IAudioVoice> allocateNewStereoVoice(double sampleRate, virtual ObjToken<IAudioVoice> allocateNewStereoVoice(double sampleRate, IAudioVoiceCallback* cb,
IAudioVoiceCallback* cb, bool dynamicPitch = false) = 0;
bool dynamicPitch=false)=0;
/** Client calls this to allocate a Submix for gathering audio together for effects processing */ /** Client calls this to allocate a Submix for gathering audio together for effects processing */
virtual ObjToken<IAudioSubmix> allocateNewSubmix(bool mainOut, IAudioSubmixCallback* cb, int busId)=0; virtual ObjToken<IAudioSubmix> allocateNewSubmix(bool mainOut, IAudioSubmixCallback* cb, int busId) = 0;
/** Client can register for key callback events from the mixing engine this way */ /** Client can register for key callback events from the mixing engine this way */
virtual void setCallbackInterface(IAudioVoiceEngineCallback* cb)=0; virtual void setCallbackInterface(IAudioVoiceEngineCallback* cb) = 0;
/** Client may use this to determine current speaker-setup */ /** Client may use this to determine current speaker-setup */
virtual AudioChannelSet getAvailableSet()=0; virtual AudioChannelSet getAvailableSet() = 0;
/** Ensure backing platform buffer is filled as much as possible with mixed samples */ /** Ensure backing platform buffer is filled as much as possible with mixed samples */
virtual void pumpAndMixVoices()=0; virtual void pumpAndMixVoices() = 0;
/** Set total volume of engine */ /** Set total volume of engine */
virtual void setVolume(float vol)=0; virtual void setVolume(float vol) = 0;
/** Enable or disable Lt/Rt surround encoding. If successful, getAvailableSet() will return Surround51 */ /** Enable or disable Lt/Rt surround encoding. If successful, getAvailableSet() will return Surround51 */
virtual bool enableLtRt(bool enable)=0; virtual bool enableLtRt(bool enable) = 0;
/** Get current Audio output in use */ /** Get current Audio output in use */
virtual std::string getCurrentAudioOutput() const=0; virtual std::string getCurrentAudioOutput() const = 0;
/** Set current Audio output to use */ /** Set current Audio output to use */
virtual bool setCurrentAudioOutput(const char* name)=0; virtual bool setCurrentAudioOutput(const char* name) = 0;
/** Get list of Audio output devices found on system */ /** Get list of Audio output devices found on system */
virtual std::vector<std::pair<std::string, std::string>> enumerateAudioOutputs() const=0; virtual std::vector<std::pair<std::string, std::string>> enumerateAudioOutputs() const = 0;
/** Get list of MIDI input devices found on system */ /** Get list of MIDI input devices found on system */
virtual std::vector<std::pair<std::string, std::string>> enumerateMIDIInputs() const=0; virtual std::vector<std::pair<std::string, std::string>> enumerateMIDIInputs() const = 0;
/** Query if system supports creating a virtual MIDI input */ /** Query if system supports creating a virtual MIDI input */
virtual bool supportsVirtualMIDIIn() const=0; virtual bool supportsVirtualMIDIIn() const = 0;
/** Create ad-hoc MIDI in port and register with system */ /** Create ad-hoc MIDI in port and register with system */
virtual std::unique_ptr<IMIDIIn> newVirtualMIDIIn(ReceiveFunctor&& receiver)=0; virtual std::unique_ptr<IMIDIIn> newVirtualMIDIIn(ReceiveFunctor&& receiver) = 0;
/** Create ad-hoc MIDI out port and register with system */ /** Create ad-hoc MIDI out port and register with system */
virtual std::unique_ptr<IMIDIOut> newVirtualMIDIOut()=0; virtual std::unique_ptr<IMIDIOut> newVirtualMIDIOut() = 0;
/** Create ad-hoc MIDI in/out port and register with system */ /** Create ad-hoc MIDI in/out port and register with system */
virtual std::unique_ptr<IMIDIInOut> newVirtualMIDIInOut(ReceiveFunctor&& receiver)=0; virtual std::unique_ptr<IMIDIInOut> newVirtualMIDIInOut(ReceiveFunctor&& receiver) = 0;
/** Open named MIDI in port, name format depends on OS */ /** Open named MIDI in port, name format depends on OS */
virtual std::unique_ptr<IMIDIIn> newRealMIDIIn(const char* name, ReceiveFunctor&& receiver)=0; virtual std::unique_ptr<IMIDIIn> newRealMIDIIn(const char* name, ReceiveFunctor&& receiver) = 0;
/** Open named MIDI out port, name format depends on OS */ /** Open named MIDI out port, name format depends on OS */
virtual std::unique_ptr<IMIDIOut> newRealMIDIOut(const char* name)=0; virtual std::unique_ptr<IMIDIOut> newRealMIDIOut(const char* name) = 0;
/** Open named MIDI in/out port, name format depends on OS */ /** Open named MIDI in/out port, name format depends on OS */
virtual std::unique_ptr<IMIDIInOut> newRealMIDIInOut(const char* name, ReceiveFunctor&& receiver)=0; virtual std::unique_ptr<IMIDIInOut> newRealMIDIInOut(const char* name, ReceiveFunctor&& receiver) = 0;
/** If this returns true, MIDI callbacks are assumed to be *not* thread-safe; need protection via mutex */ /** If this returns true, MIDI callbacks are assumed to be *not* thread-safe; need protection via mutex */
virtual bool useMIDILock() const=0; virtual bool useMIDILock() const = 0;
/** Get canonical count of frames for each 5ms output block */ /** Get canonical count of frames for each 5ms output block */
virtual size_t get5MsFrames() const=0; virtual size_t get5MsFrames() const = 0;
}; };
/** Construct host platform's voice engine */ /** Construct host platform's voice engine */
@ -112,5 +107,4 @@ std::unique_ptr<IAudioVoiceEngine> NewWAVAudioVoiceEngine(const char* path, doub
std::unique_ptr<IAudioVoiceEngine> NewWAVAudioVoiceEngine(const wchar_t* path, double sampleRate, int numChans); std::unique_ptr<IAudioVoiceEngine> NewWAVAudioVoiceEngine(const wchar_t* path, double sampleRate, int numChans);
#endif #endif
} } // namespace boo

View File

@ -5,58 +5,56 @@
#include <vector> #include <vector>
#include <cstdint> #include <cstdint>
namespace boo namespace boo {
{
struct IAudioVoiceEngine; struct IAudioVoiceEngine;
using ReceiveFunctor = std::function<void(std::vector<uint8_t>&&, double time)>; using ReceiveFunctor = std::function<void(std::vector<uint8_t>&&, double time)>;
class IMIDIPort class IMIDIPort {
{ bool m_virtual;
bool m_virtual;
protected: protected:
IAudioVoiceEngine* m_parent; IAudioVoiceEngine* m_parent;
IMIDIPort(IAudioVoiceEngine* parent, bool virt) : m_virtual(virt), m_parent(parent) {} IMIDIPort(IAudioVoiceEngine* parent, bool virt) : m_virtual(virt), m_parent(parent) {}
public: public:
virtual ~IMIDIPort(); virtual ~IMIDIPort();
bool isVirtual() const {return m_virtual;} bool isVirtual() const { return m_virtual; }
virtual std::string description() const=0; virtual std::string description() const = 0;
void _disown() { m_parent = nullptr; } void _disown() { m_parent = nullptr; }
}; };
class IMIDIReceiver class IMIDIReceiver {
{
public: public:
ReceiveFunctor m_receiver; ReceiveFunctor m_receiver;
IMIDIReceiver(ReceiveFunctor&& receiver) : m_receiver(std::move(receiver)) {} IMIDIReceiver(ReceiveFunctor&& receiver) : m_receiver(std::move(receiver)) {}
}; };
class IMIDIIn : public IMIDIPort, public IMIDIReceiver class IMIDIIn : public IMIDIPort, public IMIDIReceiver {
{
protected: protected:
IMIDIIn(IAudioVoiceEngine* parent, bool virt, ReceiveFunctor&& receiver) IMIDIIn(IAudioVoiceEngine* parent, bool virt, ReceiveFunctor&& receiver)
: IMIDIPort(parent, virt), IMIDIReceiver(std::move(receiver)) {} : IMIDIPort(parent, virt), IMIDIReceiver(std::move(receiver)) {}
public: public:
virtual ~IMIDIIn(); virtual ~IMIDIIn();
}; };
class IMIDIOut : public IMIDIPort class IMIDIOut : public IMIDIPort {
{
protected: protected:
IMIDIOut(IAudioVoiceEngine* parent, bool virt) : IMIDIPort(parent, virt) {} IMIDIOut(IAudioVoiceEngine* parent, bool virt) : IMIDIPort(parent, virt) {}
public: public:
virtual ~IMIDIOut(); virtual ~IMIDIOut();
virtual size_t send(const void* buf, size_t len) const=0; virtual size_t send(const void* buf, size_t len) const = 0;
}; };
class IMIDIInOut : public IMIDIPort, public IMIDIReceiver class IMIDIInOut : public IMIDIPort, public IMIDIReceiver {
{
protected: protected:
IMIDIInOut(IAudioVoiceEngine* parent, bool virt, ReceiveFunctor&& receiver) IMIDIInOut(IAudioVoiceEngine* parent, bool virt, ReceiveFunctor&& receiver)
: IMIDIPort(parent, virt), IMIDIReceiver(std::move(receiver)) {} : IMIDIPort(parent, virt), IMIDIReceiver(std::move(receiver)) {}
public: public:
virtual ~IMIDIInOut(); virtual ~IMIDIInOut();
virtual size_t send(const void* buf, size_t len) const=0; virtual size_t send(const void* buf, size_t len) const = 0;
}; };
} } // namespace boo

View File

@ -3,39 +3,36 @@
#include <cstdlib> #include <cstdlib>
#include <cstdint> #include <cstdint>
namespace boo namespace boo {
{
class IMIDIReader class IMIDIReader {
{
public: public:
virtual void noteOff(uint8_t chan, uint8_t key, uint8_t velocity)=0; virtual void noteOff(uint8_t chan, uint8_t key, uint8_t velocity) = 0;
virtual void noteOn(uint8_t chan, uint8_t key, uint8_t velocity)=0; virtual void noteOn(uint8_t chan, uint8_t key, uint8_t velocity) = 0;
virtual void notePressure(uint8_t chan, uint8_t key, uint8_t pressure)=0; virtual void notePressure(uint8_t chan, uint8_t key, uint8_t pressure) = 0;
virtual void controlChange(uint8_t chan, uint8_t control, uint8_t value)=0; virtual void controlChange(uint8_t chan, uint8_t control, uint8_t value) = 0;
virtual void programChange(uint8_t chan, uint8_t program)=0; virtual void programChange(uint8_t chan, uint8_t program) = 0;
virtual void channelPressure(uint8_t chan, uint8_t pressure)=0; virtual void channelPressure(uint8_t chan, uint8_t pressure) = 0;
virtual void pitchBend(uint8_t chan, int16_t pitch)=0; virtual void pitchBend(uint8_t chan, int16_t pitch) = 0;
virtual void allSoundOff(uint8_t chan)=0; virtual void allSoundOff(uint8_t chan) = 0;
virtual void resetAllControllers(uint8_t chan)=0; virtual void resetAllControllers(uint8_t chan) = 0;
virtual void localControl(uint8_t chan, bool on)=0; virtual void localControl(uint8_t chan, bool on) = 0;
virtual void allNotesOff(uint8_t chan)=0; virtual void allNotesOff(uint8_t chan) = 0;
virtual void omniMode(uint8_t chan, bool on)=0; virtual void omniMode(uint8_t chan, bool on) = 0;
virtual void polyMode(uint8_t chan, bool on)=0; virtual void polyMode(uint8_t chan, bool on) = 0;
virtual void sysex(const void* data, size_t len)=0; virtual void sysex(const void* data, size_t len) = 0;
virtual void timeCodeQuarterFrame(uint8_t message, uint8_t value)=0; virtual void timeCodeQuarterFrame(uint8_t message, uint8_t value) = 0;
virtual void songPositionPointer(uint16_t pointer)=0; virtual void songPositionPointer(uint16_t pointer) = 0;
virtual void songSelect(uint8_t song)=0; virtual void songSelect(uint8_t song) = 0;
virtual void tuneRequest()=0; virtual void tuneRequest() = 0;
virtual void startSeq()=0; virtual void startSeq() = 0;
virtual void continueSeq()=0; virtual void continueSeq() = 0;
virtual void stopSeq()=0; virtual void stopSeq() = 0;
virtual void reset()=0; virtual void reset() = 0;
}; };
} } // namespace boo

View File

@ -5,22 +5,18 @@
#include <functional> #include <functional>
#include <vector> #include <vector>
namespace boo namespace boo {
{
class MIDIDecoder {
IMIDIReader& m_out;
uint8_t m_status = 0;
bool _readContinuedValue(std::vector<uint8_t>::const_iterator& it, std::vector<uint8_t>::const_iterator end,
uint32_t& valOut);
class MIDIDecoder
{
IMIDIReader& m_out;
uint8_t m_status = 0;
bool _readContinuedValue(std::vector<uint8_t>::const_iterator& it,
std::vector<uint8_t>::const_iterator end,
uint32_t& valOut);
public: public:
MIDIDecoder(IMIDIReader& out) : m_out(out) {} MIDIDecoder(IMIDIReader& out) : m_out(out) {}
std::vector<uint8_t>::const_iterator std::vector<uint8_t>::const_iterator receiveBytes(std::vector<uint8_t>::const_iterator begin,
receiveBytes(std::vector<uint8_t>::const_iterator begin, std::vector<uint8_t>::const_iterator end);
std::vector<uint8_t>::const_iterator end);
}; };
} } // namespace boo

View File

@ -3,46 +3,44 @@
#include "boo/audiodev/IMIDIReader.hpp" #include "boo/audiodev/IMIDIReader.hpp"
#include "boo/audiodev/IMIDIPort.hpp" #include "boo/audiodev/IMIDIPort.hpp"
namespace boo namespace boo {
{
template <class Sender> template <class Sender>
class MIDIEncoder : public IMIDIReader class MIDIEncoder : public IMIDIReader {
{ Sender& m_sender;
Sender& m_sender; uint8_t m_status = 0;
uint8_t m_status = 0; void _sendMessage(const uint8_t* data, size_t len);
void _sendMessage(const uint8_t* data, size_t len); void _sendContinuedValue(uint32_t val);
void _sendContinuedValue(uint32_t val);
public: public:
MIDIEncoder(Sender& sender) : m_sender(sender) {} MIDIEncoder(Sender& sender) : m_sender(sender) {}
void noteOff(uint8_t chan, uint8_t key, uint8_t velocity); void noteOff(uint8_t chan, uint8_t key, uint8_t velocity);
void noteOn(uint8_t chan, uint8_t key, uint8_t velocity); void noteOn(uint8_t chan, uint8_t key, uint8_t velocity);
void notePressure(uint8_t chan, uint8_t key, uint8_t pressure); void notePressure(uint8_t chan, uint8_t key, uint8_t pressure);
void controlChange(uint8_t chan, uint8_t control, uint8_t value); void controlChange(uint8_t chan, uint8_t control, uint8_t value);
void programChange(uint8_t chan, uint8_t program); void programChange(uint8_t chan, uint8_t program);
void channelPressure(uint8_t chan, uint8_t pressure); void channelPressure(uint8_t chan, uint8_t pressure);
void pitchBend(uint8_t chan, int16_t pitch); void pitchBend(uint8_t chan, int16_t pitch);
void allSoundOff(uint8_t chan); void allSoundOff(uint8_t chan);
void resetAllControllers(uint8_t chan); void resetAllControllers(uint8_t chan);
void localControl(uint8_t chan, bool on); void localControl(uint8_t chan, bool on);
void allNotesOff(uint8_t chan); void allNotesOff(uint8_t chan);
void omniMode(uint8_t chan, bool on); void omniMode(uint8_t chan, bool on);
void polyMode(uint8_t chan, bool on); void polyMode(uint8_t chan, bool on);
void sysex(const void* data, size_t len); void sysex(const void* data, size_t len);
void timeCodeQuarterFrame(uint8_t message, uint8_t value); void timeCodeQuarterFrame(uint8_t message, uint8_t value);
void songPositionPointer(uint16_t pointer); void songPositionPointer(uint16_t pointer);
void songSelect(uint8_t song); void songSelect(uint8_t song);
void tuneRequest(); void tuneRequest();
void startSeq(); void startSeq();
void continueSeq(); void continueSeq();
void stopSeq(); void stopSeq();
void reset(); void reset();
}; };
} } // namespace boo

View File

@ -10,4 +10,3 @@
#include "graphicsdev/IGraphicsCommandQueue.hpp" #include "graphicsdev/IGraphicsCommandQueue.hpp"
#include "graphicsdev/IGraphicsDataFactory.hpp" #include "graphicsdev/IGraphicsDataFactory.hpp"
#include "DeferredWindowEvents.hpp" #include "DeferredWindowEvents.hpp"

View File

@ -9,70 +9,61 @@
#include <vector> #include <vector>
#include <unordered_set> #include <unordered_set>
typedef HRESULT (WINAPI *pD3DCreateBlob) typedef HRESULT(WINAPI* pD3DCreateBlob)(SIZE_T Size, ID3DBlob** ppBlob);
(SIZE_T Size,
ID3DBlob** ppBlob);
extern pD3DCreateBlob D3DCreateBlobPROC; extern pD3DCreateBlob D3DCreateBlobPROC;
namespace boo namespace boo {
{
struct BaseGraphicsData; struct BaseGraphicsData;
class D3D11DataFactory : public IGraphicsDataFactory class D3D11DataFactory : public IGraphicsDataFactory {
{
public: public:
virtual ~D3D11DataFactory() = default; virtual ~D3D11DataFactory() = default;
Platform platform() const {return Platform::D3D11;} Platform platform() const { return Platform::D3D11; }
const SystemChar* platformName() const {return _SYS_STR("D3D11");} const SystemChar* platformName() const { return _SYS_STR("D3D11"); }
class Context final : public IGraphicsDataFactory::Context class Context final : public IGraphicsDataFactory::Context {
{ friend class D3D11DataFactoryImpl;
friend class D3D11DataFactoryImpl; D3D11DataFactory& m_parent;
D3D11DataFactory& m_parent; boo::ObjToken<BaseGraphicsData> m_data;
boo::ObjToken<BaseGraphicsData> m_data; Context(D3D11DataFactory& parent __BooTraceArgs);
Context(D3D11DataFactory& parent __BooTraceArgs); ~Context();
~Context();
public:
Platform platform() const {return Platform::D3D11;}
const SystemChar* platformName() const {return _SYS_STR("D3D11");}
boo::ObjToken<IGraphicsBufferS> newStaticBuffer(BufferUse use, const void* data, size_t stride, size_t count); public:
boo::ObjToken<IGraphicsBufferD> newDynamicBuffer(BufferUse use, size_t stride, size_t count); Platform platform() const { return Platform::D3D11; }
const SystemChar* platformName() const { return _SYS_STR("D3D11"); }
boo::ObjToken<ITextureS> newStaticTexture(size_t width, size_t height, size_t mips, TextureFormat fmt, boo::ObjToken<IGraphicsBufferS> newStaticBuffer(BufferUse use, const void* data, size_t stride, size_t count);
TextureClampMode clampMode, const void* data, size_t sz); boo::ObjToken<IGraphicsBufferD> newDynamicBuffer(BufferUse use, size_t stride, size_t count);
boo::ObjToken<ITextureSA> newStaticArrayTexture(size_t width, size_t height, size_t layers, size_t mips,
TextureFormat fmt, TextureClampMode clampMode,
const void* data, size_t sz);
boo::ObjToken<ITextureD> newDynamicTexture(size_t width, size_t height, TextureFormat fmt, TextureClampMode clampMode);
boo::ObjToken<ITextureR> newRenderTexture(size_t width, size_t height, TextureClampMode clampMode,
size_t colorBindCount, size_t depthBindCount);
ObjToken<IShaderStage> boo::ObjToken<ITextureS> newStaticTexture(size_t width, size_t height, size_t mips, TextureFormat fmt,
newShaderStage(const uint8_t* data, size_t size, PipelineStage stage); TextureClampMode clampMode, const void* data, size_t sz);
boo::ObjToken<ITextureSA> newStaticArrayTexture(size_t width, size_t height, size_t layers, size_t mips,
TextureFormat fmt, TextureClampMode clampMode, const void* data,
size_t sz);
boo::ObjToken<ITextureD> newDynamicTexture(size_t width, size_t height, TextureFormat fmt,
TextureClampMode clampMode);
boo::ObjToken<ITextureR> newRenderTexture(size_t width, size_t height, TextureClampMode clampMode,
size_t colorBindCount, size_t depthBindCount);
ObjToken<IShaderPipeline> ObjToken<IShaderStage> newShaderStage(const uint8_t* data, size_t size, PipelineStage stage);
newShaderPipeline(ObjToken<IShaderStage> vertex, ObjToken<IShaderStage> fragment,
ObjToken<IShaderStage> geometry, ObjToken<IShaderStage> control,
ObjToken<IShaderStage> evaluation, const VertexFormatInfo& vtxFmt,
const AdditionalPipelineInfo& additionalInfo);
boo::ObjToken<IShaderDataBinding> ObjToken<IShaderPipeline> newShaderPipeline(ObjToken<IShaderStage> vertex, ObjToken<IShaderStage> fragment,
newShaderDataBinding(const boo::ObjToken<IShaderPipeline>& pipeline, ObjToken<IShaderStage> geometry, ObjToken<IShaderStage> control,
const boo::ObjToken<IGraphicsBuffer>& vbo, ObjToken<IShaderStage> evaluation, const VertexFormatInfo& vtxFmt,
const boo::ObjToken<IGraphicsBuffer>& instVbo, const AdditionalPipelineInfo& additionalInfo);
const boo::ObjToken<IGraphicsBuffer>& ibo,
size_t ubufCount, const boo::ObjToken<IGraphicsBuffer>* ubufs, const PipelineStage* ubufStages,
const size_t* ubufOffs, const size_t* ubufSizes,
size_t texCount, const boo::ObjToken<ITexture>* texs,
const int* bindIdxs, const bool* bindDepth,
size_t baseVert = 0, size_t baseInst = 0);
};
static std::vector<uint8_t> CompileHLSL(const char* source, PipelineStage stage); boo::ObjToken<IShaderDataBinding> newShaderDataBinding(
const boo::ObjToken<IShaderPipeline>& pipeline, const boo::ObjToken<IGraphicsBuffer>& vbo,
const boo::ObjToken<IGraphicsBuffer>& instVbo, const boo::ObjToken<IGraphicsBuffer>& ibo, size_t ubufCount,
const boo::ObjToken<IGraphicsBuffer>* ubufs, const PipelineStage* ubufStages, const size_t* ubufOffs,
const size_t* ubufSizes, size_t texCount, const boo::ObjToken<ITexture>* texs, const int* bindIdxs,
const bool* bindDepth, size_t baseVert = 0, size_t baseInst = 0);
};
static std::vector<uint8_t> CompileHLSL(const char* source, PipelineStage stage);
}; };
} } // namespace boo
#endif // _WIN32 #endif // _WIN32

View File

@ -6,64 +6,56 @@
#include "boo/IGraphicsContext.hpp" #include "boo/IGraphicsContext.hpp"
#include "GLSLMacros.hpp" #include "GLSLMacros.hpp"
namespace boo namespace boo {
{
struct BaseGraphicsData; struct BaseGraphicsData;
struct GLContext struct GLContext {
{ uint32_t m_sampleCount = 1;
uint32_t m_sampleCount = 1; uint32_t m_anisotropy = 1;
uint32_t m_anisotropy = 1; bool m_deepColor = false;
bool m_deepColor = false;
}; };
class GLDataFactory : public IGraphicsDataFactory class GLDataFactory : public IGraphicsDataFactory {
{
public: public:
class Context final : public IGraphicsDataFactory::Context class Context final : public IGraphicsDataFactory::Context {
{ friend class GLDataFactoryImpl;
friend class GLDataFactoryImpl; GLDataFactory& m_parent;
GLDataFactory& m_parent; ObjToken<BaseGraphicsData> m_data;
ObjToken<BaseGraphicsData> m_data; Context(GLDataFactory& parent __BooTraceArgs);
Context(GLDataFactory& parent __BooTraceArgs); ~Context();
~Context();
public:
Platform platform() const { return Platform::OpenGL; }
const SystemChar* platformName() const { return _SYS_STR("OpenGL"); }
ObjToken<IGraphicsBufferS> newStaticBuffer(BufferUse use, const void* data, size_t stride, size_t count); public:
ObjToken<IGraphicsBufferD> newDynamicBuffer(BufferUse use, size_t stride, size_t count); Platform platform() const { return Platform::OpenGL; }
const SystemChar* platformName() const { return _SYS_STR("OpenGL"); }
ObjToken<ITextureS> newStaticTexture(size_t width, size_t height, size_t mips, TextureFormat fmt, ObjToken<IGraphicsBufferS> newStaticBuffer(BufferUse use, const void* data, size_t stride, size_t count);
TextureClampMode clampMode, const void* data, size_t sz); ObjToken<IGraphicsBufferD> newDynamicBuffer(BufferUse use, size_t stride, size_t count);
ObjToken<ITextureSA> newStaticArrayTexture(size_t width, size_t height, size_t layers, size_t mips,
TextureFormat fmt, TextureClampMode clampMode, const void* data, size_t sz);
ObjToken<ITextureD> newDynamicTexture(size_t width, size_t height, TextureFormat fmt, TextureClampMode clampMode);
ObjToken<ITextureR> newRenderTexture(size_t width, size_t height, TextureClampMode clampMode,
size_t colorBindingCount, size_t depthBindingCount);
ObjToken<IShaderStage> ObjToken<ITextureS> newStaticTexture(size_t width, size_t height, size_t mips, TextureFormat fmt,
newShaderStage(const uint8_t* data, size_t size, PipelineStage stage); TextureClampMode clampMode, const void* data, size_t sz);
ObjToken<ITextureSA> newStaticArrayTexture(size_t width, size_t height, size_t layers, size_t mips,
TextureFormat fmt, TextureClampMode clampMode, const void* data,
size_t sz);
ObjToken<ITextureD> newDynamicTexture(size_t width, size_t height, TextureFormat fmt, TextureClampMode clampMode);
ObjToken<ITextureR> newRenderTexture(size_t width, size_t height, TextureClampMode clampMode,
size_t colorBindingCount, size_t depthBindingCount);
ObjToken<IShaderPipeline> ObjToken<IShaderStage> newShaderStage(const uint8_t* data, size_t size, PipelineStage stage);
newShaderPipeline(ObjToken<IShaderStage> vertex, ObjToken<IShaderStage> fragment,
ObjToken<IShaderStage> geometry, ObjToken<IShaderStage> control,
ObjToken<IShaderStage> evaluation, const VertexFormatInfo& vtxFmt,
const AdditionalPipelineInfo& additionalInfo);
ObjToken<IShaderDataBinding> ObjToken<IShaderPipeline> newShaderPipeline(ObjToken<IShaderStage> vertex, ObjToken<IShaderStage> fragment,
newShaderDataBinding(const ObjToken<IShaderPipeline>& pipeline, ObjToken<IShaderStage> geometry, ObjToken<IShaderStage> control,
const ObjToken<IGraphicsBuffer>& vbo, ObjToken<IShaderStage> evaluation, const VertexFormatInfo& vtxFmt,
const ObjToken<IGraphicsBuffer>& instVbo, const AdditionalPipelineInfo& additionalInfo);
const ObjToken<IGraphicsBuffer>& ibo,
size_t ubufCount, const ObjToken<IGraphicsBuffer>* ubufs, const PipelineStage* ubufStages, ObjToken<IShaderDataBinding> newShaderDataBinding(
const size_t* ubufOffs, const size_t* ubufSizes, const ObjToken<IShaderPipeline>& pipeline, const ObjToken<IGraphicsBuffer>& vbo,
size_t texCount, const ObjToken<ITexture>* texs, const ObjToken<IGraphicsBuffer>& instVbo, const ObjToken<IGraphicsBuffer>& ibo, size_t ubufCount,
const int* texBindIdx, const bool* depthBind, const ObjToken<IGraphicsBuffer>* ubufs, const PipelineStage* ubufStages, const size_t* ubufOffs,
size_t baseVert = 0, size_t baseInst = 0); const size_t* ubufSizes, size_t texCount, const ObjToken<ITexture>* texs, const int* texBindIdx,
}; const bool* depthBind, size_t baseVert = 0, size_t baseInst = 0);
};
}; };
} } // namespace boo
#endif #endif

View File

@ -3,48 +3,47 @@
#define BOO_GLSL_MAX_UNIFORM_COUNT 8 #define BOO_GLSL_MAX_UNIFORM_COUNT 8
#define BOO_GLSL_MAX_TEXTURE_COUNT 8 #define BOO_GLSL_MAX_TEXTURE_COUNT 8
#define BOO_GLSL_BINDING_HEAD \ #define BOO_GLSL_BINDING_HEAD \
"#ifdef VULKAN\n" \ "#ifdef VULKAN\n" \
"#define gl_VertexID gl_VertexIndex\n" \ "#define gl_VertexID gl_VertexIndex\n" \
"#extension GL_ARB_separate_shader_objects: enable\n" \ "#extension GL_ARB_separate_shader_objects: enable\n" \
"#define SBINDING(idx) layout(location=idx)\n" \ "#define SBINDING(idx) layout(location=idx)\n" \
"#else\n" \ "#else\n" \
"#define SBINDING(idx)\n" \ "#define SBINDING(idx)\n" \
"#endif\n" \ "#endif\n" \
"#extension GL_ARB_shading_language_420pack: enable\n" \ "#extension GL_ARB_shading_language_420pack: enable\n" \
"#ifdef GL_ARB_shading_language_420pack\n" \ "#ifdef GL_ARB_shading_language_420pack\n" \
"#define UBINDING0 layout(binding=0)\n" \ "#define UBINDING0 layout(binding=0)\n" \
"#define UBINDING1 layout(binding=1)\n" \ "#define UBINDING1 layout(binding=1)\n" \
"#define UBINDING2 layout(binding=2)\n" \ "#define UBINDING2 layout(binding=2)\n" \
"#define UBINDING3 layout(binding=3)\n" \ "#define UBINDING3 layout(binding=3)\n" \
"#define UBINDING4 layout(binding=4)\n" \ "#define UBINDING4 layout(binding=4)\n" \
"#define UBINDING5 layout(binding=5)\n" \ "#define UBINDING5 layout(binding=5)\n" \
"#define UBINDING6 layout(binding=6)\n" \ "#define UBINDING6 layout(binding=6)\n" \
"#define UBINDING7 layout(binding=7)\n" \ "#define UBINDING7 layout(binding=7)\n" \
"#define TBINDING0 layout(binding=8)\n" \ "#define TBINDING0 layout(binding=8)\n" \
"#define TBINDING1 layout(binding=9)\n" \ "#define TBINDING1 layout(binding=9)\n" \
"#define TBINDING2 layout(binding=10)\n" \ "#define TBINDING2 layout(binding=10)\n" \
"#define TBINDING3 layout(binding=11)\n" \ "#define TBINDING3 layout(binding=11)\n" \
"#define TBINDING4 layout(binding=12)\n" \ "#define TBINDING4 layout(binding=12)\n" \
"#define TBINDING5 layout(binding=13)\n" \ "#define TBINDING5 layout(binding=13)\n" \
"#define TBINDING6 layout(binding=14)\n" \ "#define TBINDING6 layout(binding=14)\n" \
"#define TBINDING7 layout(binding=15)\n" \ "#define TBINDING7 layout(binding=15)\n" \
"#else\n" \ "#else\n" \
"#define UBINDING0\n" \ "#define UBINDING0\n" \
"#define UBINDING1\n" \ "#define UBINDING1\n" \
"#define UBINDING2\n" \ "#define UBINDING2\n" \
"#define UBINDING3\n" \ "#define UBINDING3\n" \
"#define UBINDING4\n" \ "#define UBINDING4\n" \
"#define UBINDING5\n" \ "#define UBINDING5\n" \
"#define UBINDING6\n" \ "#define UBINDING6\n" \
"#define UBINDING7\n" \ "#define UBINDING7\n" \
"#define TBINDING0\n" \ "#define TBINDING0\n" \
"#define TBINDING1\n" \ "#define TBINDING1\n" \
"#define TBINDING2\n" \ "#define TBINDING2\n" \
"#define TBINDING3\n" \ "#define TBINDING3\n" \
"#define TBINDING4\n" \ "#define TBINDING4\n" \
"#define TBINDING5\n" \ "#define TBINDING5\n" \
"#define TBINDING6\n" \ "#define TBINDING6\n" \
"#define TBINDING7\n" \ "#define TBINDING7\n" \
"#endif\n" "#endif\n"

View File

@ -4,41 +4,38 @@
#include "boo/IWindow.hpp" #include "boo/IWindow.hpp"
#include <functional> #include <functional>
namespace boo namespace boo {
{
struct IGraphicsCommandQueue struct IGraphicsCommandQueue {
{ virtual ~IGraphicsCommandQueue() = default;
virtual ~IGraphicsCommandQueue() = default;
using Platform = IGraphicsDataFactory::Platform; using Platform = IGraphicsDataFactory::Platform;
virtual Platform platform() const=0; virtual Platform platform() const = 0;
virtual const SystemChar* platformName() const=0; virtual const SystemChar* platformName() const = 0;
virtual void setShaderDataBinding(const ObjToken<IShaderDataBinding>& binding)=0; virtual void setShaderDataBinding(const ObjToken<IShaderDataBinding>& binding) = 0;
virtual void setRenderTarget(const ObjToken<ITextureR>& target)=0; virtual void setRenderTarget(const ObjToken<ITextureR>& target) = 0;
virtual void setViewport(const SWindowRect& rect, float znear=0.f, float zfar=1.f)=0; virtual void setViewport(const SWindowRect& rect, float znear = 0.f, float zfar = 1.f) = 0;
virtual void setScissor(const SWindowRect& rect)=0; virtual void setScissor(const SWindowRect& rect) = 0;
virtual void resizeRenderTexture(const ObjToken<ITextureR>& tex, size_t width, size_t height)=0; virtual void resizeRenderTexture(const ObjToken<ITextureR>& tex, size_t width, size_t height) = 0;
virtual void schedulePostFrameHandler(std::function<void(void)>&& func)=0; virtual void schedulePostFrameHandler(std::function<void(void)>&& func) = 0;
virtual void setClearColor(const float rgba[4])=0; virtual void setClearColor(const float rgba[4]) = 0;
virtual void clearTarget(bool render=true, bool depth=true)=0; virtual void clearTarget(bool render = true, bool depth = true) = 0;
virtual void draw(size_t start, size_t count)=0; virtual void draw(size_t start, size_t count) = 0;
virtual void drawIndexed(size_t start, size_t count)=0; virtual void drawIndexed(size_t start, size_t count) = 0;
virtual void drawInstances(size_t start, size_t count, size_t instCount, size_t startInst=0)=0; virtual void drawInstances(size_t start, size_t count, size_t instCount, size_t startInst = 0) = 0;
virtual void drawInstancesIndexed(size_t start, size_t count, size_t instCount, size_t startInst=0)=0; virtual void drawInstancesIndexed(size_t start, size_t count, size_t instCount, size_t startInst = 0) = 0;
virtual void resolveBindTexture(const ObjToken<ITextureR>& texture, const SWindowRect& rect, virtual void resolveBindTexture(const ObjToken<ITextureR>& texture, const SWindowRect& rect, bool tlOrigin,
bool tlOrigin, int bindIdx, bool color, bool depth, bool clearDepth=false)=0; int bindIdx, bool color, bool depth, bool clearDepth = false) = 0;
virtual void resolveDisplay(const ObjToken<ITextureR>& source)=0; virtual void resolveDisplay(const ObjToken<ITextureR>& source) = 0;
virtual void execute()=0; virtual void execute() = 0;
virtual void startRenderer()=0; virtual void startRenderer() = 0;
virtual void stopRenderer()=0; virtual void stopRenderer() = 0;
}; };
} } // namespace boo

View File

@ -12,165 +12,126 @@
#include <ctype.h> #include <ctype.h>
#endif #endif
namespace boo namespace boo {
{
struct IGraphicsCommandQueue; struct IGraphicsCommandQueue;
/** Supported buffer uses */ /** Supported buffer uses */
enum class BufferUse enum class BufferUse { Null, Vertex, Index, Uniform };
{
Null,
Vertex,
Index,
Uniform
};
/** Typeless graphics buffer */ /** Typeless graphics buffer */
struct IGraphicsBuffer : IObj struct IGraphicsBuffer : IObj {
{ bool dynamic() const { return m_dynamic; }
bool dynamic() const { return m_dynamic; }
protected: protected:
bool m_dynamic; bool m_dynamic;
explicit IGraphicsBuffer(bool dynamic) : m_dynamic(dynamic) {} explicit IGraphicsBuffer(bool dynamic) : m_dynamic(dynamic) {}
}; };
/** Static resource buffer for verts, indices, uniform constants */ /** Static resource buffer for verts, indices, uniform constants */
struct IGraphicsBufferS : IGraphicsBuffer struct IGraphicsBufferS : IGraphicsBuffer {
{
protected: protected:
IGraphicsBufferS() : IGraphicsBuffer(false) {} IGraphicsBufferS() : IGraphicsBuffer(false) {}
}; };
/** Dynamic resource buffer for verts, indices, uniform constants */ /** Dynamic resource buffer for verts, indices, uniform constants */
struct IGraphicsBufferD : IGraphicsBuffer struct IGraphicsBufferD : IGraphicsBuffer {
{ virtual void load(const void* data, size_t sz) = 0;
virtual void load(const void* data, size_t sz)=0; virtual void* map(size_t sz) = 0;
virtual void* map(size_t sz)=0; virtual void unmap() = 0;
virtual void unmap()=0;
protected: protected:
IGraphicsBufferD() : IGraphicsBuffer(true) {} IGraphicsBufferD() : IGraphicsBuffer(true) {}
}; };
/** Texture access types */ /** Texture access types */
enum class TextureType enum class TextureType { Static, StaticArray, Dynamic, Render };
{
Static,
StaticArray,
Dynamic,
Render
};
/** Supported texture formats */ /** Supported texture formats */
enum class TextureFormat enum class TextureFormat { RGBA8, I8, I16, DXT1, PVRTC4 };
{
RGBA8,
I8,
I16,
DXT1,
PVRTC4
};
/** Supported texture clamp modes */ /** Supported texture clamp modes */
enum class TextureClampMode enum class TextureClampMode { Invalid = -1, Repeat, ClampToWhite, ClampToBlack, ClampToEdge, ClampToEdgeNearest };
{
Invalid = -1,
Repeat,
ClampToWhite,
ClampToBlack,
ClampToEdge,
ClampToEdgeNearest
};
/** Typeless texture */ /** Typeless texture */
struct ITexture : IObj struct ITexture : IObj {
{ TextureType type() const { return m_type; }
TextureType type() const { return m_type; }
/* Only applies on GL and Vulkan. Use shader semantics on other platforms */ /* Only applies on GL and Vulkan. Use shader semantics on other platforms */
virtual void setClampMode(TextureClampMode mode) {} virtual void setClampMode(TextureClampMode mode) {}
protected: protected:
TextureType m_type; TextureType m_type;
explicit ITexture(TextureType type) : m_type(type) {} explicit ITexture(TextureType type) : m_type(type) {}
}; };
/** Static resource buffer for textures */ /** Static resource buffer for textures */
struct ITextureS : ITexture struct ITextureS : ITexture {
{
protected: protected:
ITextureS() : ITexture(TextureType::Static) {} ITextureS() : ITexture(TextureType::Static) {}
}; };
/** Static-array resource buffer for array textures */ /** Static-array resource buffer for array textures */
struct ITextureSA : ITexture struct ITextureSA : ITexture {
{
protected: protected:
ITextureSA() : ITexture(TextureType::StaticArray) {} ITextureSA() : ITexture(TextureType::StaticArray) {}
}; };
/** Dynamic resource buffer for textures */ /** Dynamic resource buffer for textures */
struct ITextureD : ITexture struct ITextureD : ITexture {
{ virtual void load(const void* data, size_t sz) = 0;
virtual void load(const void* data, size_t sz)=0; virtual void* map(size_t sz) = 0;
virtual void* map(size_t sz)=0; virtual void unmap() = 0;
virtual void unmap()=0;
protected: protected:
ITextureD() : ITexture(TextureType::Dynamic) {} ITextureD() : ITexture(TextureType::Dynamic) {}
}; };
/** Resource buffer for render-target textures */ /** Resource buffer for render-target textures */
struct ITextureR : ITexture struct ITextureR : ITexture {
{
protected: protected:
ITextureR() : ITexture(TextureType::Render) {} ITextureR() : ITexture(TextureType::Render) {}
}; };
/** Types of vertex attributes */ /** Types of vertex attributes */
enum class VertexSemantic enum class VertexSemantic {
{ None = 0,
None = 0, Position3,
Position3, Position4,
Position4, Normal3,
Normal3, Normal4,
Normal4, Color,
Color, ColorUNorm,
ColorUNorm, UV2,
UV2, UV4,
UV4, Weight,
Weight, ModelView,
ModelView, SemanticMask = 0xf,
SemanticMask = 0xf, Instanced = 0x10
Instanced = 0x10
}; };
ENABLE_BITWISE_ENUM(VertexSemantic) ENABLE_BITWISE_ENUM(VertexSemantic)
/** Used to create IVertexFormat */ /** Used to create IVertexFormat */
struct VertexElementDescriptor struct VertexElementDescriptor {
{ VertexSemantic semantic;
VertexSemantic semantic; int semanticIdx = 0;
int semanticIdx = 0; VertexElementDescriptor() = default;
VertexElementDescriptor() = default; VertexElementDescriptor(VertexSemantic s, int idx = 0) : semantic(s), semanticIdx(idx) {}
VertexElementDescriptor(VertexSemantic s, int idx=0)
: semantic(s), semanticIdx(idx) {}
}; };
/** Structure for passing vertex format info for pipeline construction */ /** Structure for passing vertex format info for pipeline construction */
struct VertexFormatInfo struct VertexFormatInfo {
{ size_t elementCount = 0;
size_t elementCount = 0; const VertexElementDescriptor* elements = nullptr;
const VertexElementDescriptor* elements = nullptr;
VertexFormatInfo() = default; VertexFormatInfo() = default;
VertexFormatInfo(size_t sz, const VertexElementDescriptor* elem) VertexFormatInfo(size_t sz, const VertexElementDescriptor* elem) : elementCount(sz), elements(elem) {}
: elementCount(sz), elements(elem) {}
template<typename T> template <typename T>
VertexFormatInfo(const T& tp) VertexFormatInfo(const T& tp) : elementCount(std::extent_v<T>), elements(tp) {}
: elementCount(std::extent_v<T>), elements(tp) {}
VertexFormatInfo(const std::initializer_list<VertexElementDescriptor>& l) VertexFormatInfo(const std::initializer_list<VertexElementDescriptor>& l)
: elementCount(l.size()), elements(l.begin()) {} : elementCount(l.size()), elements(l.begin()) {}
}; };
/** Opaque token for referencing a shader stage usable in a graphics pipeline */ /** Opaque token for referencing a shader stage usable in a graphics pipeline */
@ -186,176 +147,128 @@ struct IShaderPipeline : IObj {};
struct IShaderDataBinding : IObj {}; struct IShaderDataBinding : IObj {};
/** Used wherever distinction of pipeline stages is needed */ /** Used wherever distinction of pipeline stages is needed */
enum class PipelineStage enum class PipelineStage { Null, Vertex, Fragment, Geometry, Control, Evaluation };
{
Null, /** Used by platform shader pipeline constructors */
Vertex, enum class Primitive { Triangles, TriStrips, Patches };
Fragment,
Geometry, /** Used by platform shader pipeline constructors */
Control, enum class CullMode { None, Backface, Frontface };
Evaluation
/** Used by platform shader pipeline constructors */
enum class ZTest {
None,
LEqual, /* Flipped on Vulkan, D3D, Metal */
Greater,
GEqual,
Equal
}; };
/** Used by platform shader pipeline constructors */ /** Used by platform shader pipeline constructors */
enum class Primitive enum class BlendFactor {
{ Zero,
Triangles, One,
TriStrips, SrcColor,
Patches InvSrcColor,
}; DstColor,
InvDstColor,
SrcAlpha,
InvSrcAlpha,
DstAlpha,
InvDstAlpha,
SrcColor1,
InvSrcColor1,
/** Used by platform shader pipeline constructors */ /* Special value that activates DstColor - SrcColor blending */
enum class CullMode Subtract
{
None,
Backface,
Frontface
};
/** Used by platform shader pipeline constructors */
enum class ZTest
{
None,
LEqual, /* Flipped on Vulkan, D3D, Metal */
Greater,
GEqual,
Equal
};
/** Used by platform shader pipeline constructors */
enum class BlendFactor
{
Zero,
One,
SrcColor,
InvSrcColor,
DstColor,
InvDstColor,
SrcAlpha,
InvSrcAlpha,
DstAlpha,
InvDstAlpha,
SrcColor1,
InvSrcColor1,
/* Special value that activates DstColor - SrcColor blending */
Subtract
}; };
/** Structure for passing additional pipeline construction information */ /** Structure for passing additional pipeline construction information */
struct AdditionalPipelineInfo struct AdditionalPipelineInfo {
{ BlendFactor srcFac = BlendFactor::One;
BlendFactor srcFac = BlendFactor::One; BlendFactor dstFac = BlendFactor::Zero;
BlendFactor dstFac = BlendFactor::Zero; Primitive prim = Primitive::TriStrips;
Primitive prim = Primitive::TriStrips; ZTest depthTest = ZTest::LEqual;
ZTest depthTest = ZTest::LEqual; bool depthWrite = true;
bool depthWrite = true; bool colorWrite = true;
bool colorWrite = true; bool alphaWrite = false;
bool alphaWrite = false; CullMode culling = CullMode::Backface;
CullMode culling = CullMode::Backface; uint32_t patchSize = 0;
uint32_t patchSize = 0; bool overwriteAlpha = true;
bool overwriteAlpha = true; bool depthAttachment = true;
bool depthAttachment = true;
}; };
/** Factory object for creating batches of resources as an IGraphicsData token */ /** Factory object for creating batches of resources as an IGraphicsData token */
struct IGraphicsDataFactory struct IGraphicsDataFactory {
{ virtual ~IGraphicsDataFactory() = default;
virtual ~IGraphicsDataFactory() = default;
enum class Platform enum class Platform { Null, OpenGL, D3D11, Metal, Vulkan, GX, NX };
{ virtual Platform platform() const = 0;
Null, virtual const SystemChar* platformName() const = 0;
OpenGL,
D3D11,
Metal,
Vulkan,
GX,
NX
};
virtual Platform platform() const=0;
virtual const SystemChar* platformName() const=0;
struct Context struct Context {
{ virtual Platform platform() const = 0;
virtual Platform platform() const=0; virtual const SystemChar* platformName() const = 0;
virtual const SystemChar* platformName() const=0;
virtual ObjToken<IGraphicsBufferS> virtual ObjToken<IGraphicsBufferS> newStaticBuffer(BufferUse use, const void* data, size_t stride,
newStaticBuffer(BufferUse use, const void* data, size_t stride, size_t count)=0; size_t count) = 0;
virtual ObjToken<IGraphicsBufferD> virtual ObjToken<IGraphicsBufferD> newDynamicBuffer(BufferUse use, size_t stride, size_t count) = 0;
newDynamicBuffer(BufferUse use, size_t stride, size_t count)=0;
virtual ObjToken<ITextureS> virtual ObjToken<ITextureS> newStaticTexture(size_t width, size_t height, size_t mips, TextureFormat fmt,
newStaticTexture(size_t width, size_t height, size_t mips, TextureFormat fmt, TextureClampMode clampMode, const void* data, size_t sz) = 0;
TextureClampMode clampMode, const void* data, size_t sz)=0; virtual ObjToken<ITextureSA> newStaticArrayTexture(size_t width, size_t height, size_t layers, size_t mips,
virtual ObjToken<ITextureSA> TextureFormat fmt, TextureClampMode clampMode, const void* data,
newStaticArrayTexture(size_t width, size_t height, size_t layers, size_t mips, size_t sz) = 0;
TextureFormat fmt, TextureClampMode clampMode, const void* data, size_t sz)=0; virtual ObjToken<ITextureD> newDynamicTexture(size_t width, size_t height, TextureFormat fmt,
virtual ObjToken<ITextureD> TextureClampMode clampMode) = 0;
newDynamicTexture(size_t width, size_t height, TextureFormat fmt, TextureClampMode clampMode)=0; virtual ObjToken<ITextureR> newRenderTexture(size_t width, size_t height, TextureClampMode clampMode,
virtual ObjToken<ITextureR> size_t colorBindingCount, size_t depthBindingCount) = 0;
newRenderTexture(size_t width, size_t height, TextureClampMode clampMode,
size_t colorBindingCount, size_t depthBindingCount)=0;
virtual ObjToken<IShaderStage> virtual ObjToken<IShaderStage> newShaderStage(const uint8_t* data, size_t size, PipelineStage stage) = 0;
newShaderStage(const uint8_t* data, size_t size, PipelineStage stage)=0;
ObjToken<IShaderStage> ObjToken<IShaderStage> newShaderStage(const std::vector<uint8_t>& data, PipelineStage stage) {
newShaderStage(const std::vector<uint8_t>& data, PipelineStage stage) return newShaderStage(data.data(), data.size(), stage);
{ }
return newShaderStage(data.data(), data.size(), stage);
}
virtual ObjToken<IShaderPipeline> virtual ObjToken<IShaderPipeline> newShaderPipeline(ObjToken<IShaderStage> vertex, ObjToken<IShaderStage> fragment,
newShaderPipeline(ObjToken<IShaderStage> vertex, ObjToken<IShaderStage> fragment, ObjToken<IShaderStage> geometry, ObjToken<IShaderStage> control,
ObjToken<IShaderStage> geometry, ObjToken<IShaderStage> control, ObjToken<IShaderStage> evaluation,
ObjToken<IShaderStage> evaluation, const VertexFormatInfo& vtxFmt, const VertexFormatInfo& vtxFmt,
const AdditionalPipelineInfo& additionalInfo)=0; const AdditionalPipelineInfo& additionalInfo) = 0;
ObjToken<IShaderPipeline> ObjToken<IShaderPipeline> newShaderPipeline(ObjToken<IShaderStage> vertex, ObjToken<IShaderStage> fragment,
newShaderPipeline(ObjToken<IShaderStage> vertex, ObjToken<IShaderStage> fragment, const VertexFormatInfo& vtxFmt,
const VertexFormatInfo& vtxFmt, const AdditionalPipelineInfo& additionalInfo) const AdditionalPipelineInfo& additionalInfo) {
{ return newShaderPipeline(vertex, fragment, {}, {}, {}, vtxFmt, additionalInfo);
return newShaderPipeline(vertex, fragment, {}, {}, {}, vtxFmt, additionalInfo); }
}
virtual ObjToken<IShaderDataBinding> virtual ObjToken<IShaderDataBinding> newShaderDataBinding(
newShaderDataBinding(const ObjToken<IShaderPipeline>& pipeline, const ObjToken<IShaderPipeline>& pipeline, const ObjToken<IGraphicsBuffer>& vbo,
const ObjToken<IGraphicsBuffer>& vbo, const ObjToken<IGraphicsBuffer>& instVbo, const ObjToken<IGraphicsBuffer>& ibo, size_t ubufCount,
const ObjToken<IGraphicsBuffer>& instVbo, const ObjToken<IGraphicsBuffer>* ubufs, const PipelineStage* ubufStages, const size_t* ubufOffs,
const ObjToken<IGraphicsBuffer>& ibo, const size_t* ubufSizes, size_t texCount, const ObjToken<ITexture>* texs, const int* texBindIdx,
size_t ubufCount, const ObjToken<IGraphicsBuffer>* ubufs, const PipelineStage* ubufStages, const bool* depthBind, size_t baseVert = 0, size_t baseInst = 0) = 0;
const size_t* ubufOffs, const size_t* ubufSizes,
size_t texCount, const ObjToken<ITexture>* texs,
const int* texBindIdx, const bool* depthBind,
size_t baseVert = 0, size_t baseInst = 0)=0;
ObjToken<IShaderDataBinding> ObjToken<IShaderDataBinding> newShaderDataBinding(const ObjToken<IShaderPipeline>& pipeline,
newShaderDataBinding(const ObjToken<IShaderPipeline>& pipeline, const ObjToken<IGraphicsBuffer>& vbo,
const ObjToken<IGraphicsBuffer>& vbo, const ObjToken<IGraphicsBuffer>& instVbo,
const ObjToken<IGraphicsBuffer>& instVbo, const ObjToken<IGraphicsBuffer>& ibo, size_t ubufCount,
const ObjToken<IGraphicsBuffer>& ibo, const ObjToken<IGraphicsBuffer>* ubufs,
size_t ubufCount, const ObjToken<IGraphicsBuffer>* ubufs, const PipelineStage* ubufStages, const PipelineStage* ubufStages, size_t texCount,
size_t texCount, const ObjToken<ITexture>* texs, const ObjToken<ITexture>* texs, const int* texBindIdx,
const int* texBindIdx, const bool* depthBind, const bool* depthBind, size_t baseVert = 0, size_t baseInst = 0) {
size_t baseVert = 0, size_t baseInst = 0) return newShaderDataBinding(pipeline, vbo, instVbo, ibo, ubufCount, ubufs, ubufStages, nullptr, nullptr, texCount,
{ texs, texBindIdx, depthBind, baseVert, baseInst);
return newShaderDataBinding(pipeline, vbo, instVbo, ibo, }
ubufCount, ubufs, ubufStages, nullptr, };
nullptr, texCount, texs, texBindIdx, depthBind,
baseVert, baseInst);
}
};
virtual void commitTransaction(const std::function<bool(Context& ctx)>& __BooTraceArgs)=0; virtual void commitTransaction(const std::function<bool(Context& ctx)>& __BooTraceArgs) = 0;
virtual ObjToken<IGraphicsBufferD> newPoolBuffer(BufferUse use, size_t stride, size_t count __BooTraceArgs)=0; virtual ObjToken<IGraphicsBufferD> newPoolBuffer(BufferUse use, size_t stride, size_t count __BooTraceArgs) = 0;
virtual void setDisplayGamma(float gamma)=0; virtual void setDisplayGamma(float gamma) = 0;
virtual bool isTessellationSupported(uint32_t& maxPatchSizeOut)=0; virtual bool isTessellationSupported(uint32_t& maxPatchSizeOut) = 0;
}; };
using GraphicsDataFactoryContext = IGraphicsDataFactory::Context; using GraphicsDataFactoryContext = IGraphicsDataFactory::Context;
using FactoryCommitFunc = std::function<bool(GraphicsDataFactoryContext& ctx)>; using FactoryCommitFunc = std::function<bool(GraphicsDataFactoryContext& ctx)>;
} } // namespace boo

View File

@ -6,60 +6,53 @@
#include "IGraphicsCommandQueue.hpp" #include "IGraphicsCommandQueue.hpp"
#include "boo/IGraphicsContext.hpp" #include "boo/IGraphicsContext.hpp"
namespace boo namespace boo {
{
struct BaseGraphicsData; struct BaseGraphicsData;
class MetalDataFactory : public IGraphicsDataFactory class MetalDataFactory : public IGraphicsDataFactory {
{
public: public:
class Context final : public IGraphicsDataFactory::Context class Context final : public IGraphicsDataFactory::Context {
{ friend class MetalDataFactoryImpl;
friend class MetalDataFactoryImpl; MetalDataFactory& m_parent;
MetalDataFactory& m_parent; ObjToken<BaseGraphicsData> m_data;
ObjToken<BaseGraphicsData> m_data; Context(MetalDataFactory& parent __BooTraceArgs);
Context(MetalDataFactory& parent __BooTraceArgs); ~Context();
~Context();
public:
Platform platform() const { return Platform::Metal; }
const SystemChar* platformName() const { return _SYS_STR("Metal"); }
ObjToken<IGraphicsBufferS> newStaticBuffer(BufferUse use, const void* data, size_t stride, size_t count); public:
ObjToken<IGraphicsBufferD> newDynamicBuffer(BufferUse use, size_t stride, size_t count); Platform platform() const { return Platform::Metal; }
const SystemChar* platformName() const { return _SYS_STR("Metal"); }
ObjToken<ITextureS> newStaticTexture(size_t width, size_t height, size_t mips, TextureFormat fmt, ObjToken<IGraphicsBufferS> newStaticBuffer(BufferUse use, const void* data, size_t stride, size_t count);
TextureClampMode clampMode, const void* data, size_t sz); ObjToken<IGraphicsBufferD> newDynamicBuffer(BufferUse use, size_t stride, size_t count);
ObjToken<ITextureSA> newStaticArrayTexture(size_t width, size_t height, size_t layers, size_t mips,
TextureFormat fmt, TextureClampMode clampMode, const void* data,
size_t sz);
ObjToken<ITextureD> newDynamicTexture(size_t width, size_t height, TextureFormat fmt,
TextureClampMode clampMode);
ObjToken<ITextureR> newRenderTexture(size_t width, size_t height, TextureClampMode clampMode,
size_t colorBindCount, size_t depthBindCount);
ObjToken<IShaderStage> newShaderStage(const uint8_t* data, size_t size, PipelineStage stage); ObjToken<ITextureS> newStaticTexture(size_t width, size_t height, size_t mips, TextureFormat fmt,
TextureClampMode clampMode, const void* data, size_t sz);
ObjToken<ITextureSA> newStaticArrayTexture(size_t width, size_t height, size_t layers, size_t mips,
TextureFormat fmt, TextureClampMode clampMode, const void* data,
size_t sz);
ObjToken<ITextureD> newDynamicTexture(size_t width, size_t height, TextureFormat fmt, TextureClampMode clampMode);
ObjToken<ITextureR> newRenderTexture(size_t width, size_t height, TextureClampMode clampMode, size_t colorBindCount,
size_t depthBindCount);
ObjToken<IShaderPipeline> newShaderPipeline(ObjToken<IShaderStage> vertex, ObjToken<IShaderStage> fragment, ObjToken<IShaderStage> newShaderStage(const uint8_t* data, size_t size, PipelineStage stage);
ObjToken<IShaderStage> geometry, ObjToken<IShaderStage> control,
ObjToken<IShaderStage> evaluation, const VertexFormatInfo& vtxFmt,
const AdditionalPipelineInfo& additionalInfo);
ObjToken<IShaderDataBinding> ObjToken<IShaderPipeline> newShaderPipeline(ObjToken<IShaderStage> vertex, ObjToken<IShaderStage> fragment,
newShaderDataBinding(const ObjToken<IShaderPipeline>& pipeline, ObjToken<IShaderStage> geometry, ObjToken<IShaderStage> control,
const ObjToken<IGraphicsBuffer>& vbo, ObjToken<IShaderStage> evaluation, const VertexFormatInfo& vtxFmt,
const ObjToken<IGraphicsBuffer>& instVbo, const AdditionalPipelineInfo& additionalInfo);
const ObjToken<IGraphicsBuffer>& ibo,
size_t ubufCount, const ObjToken<IGraphicsBuffer>* ubufs, const PipelineStage* ubufStages,
const size_t* ubufOffs, const size_t* ubufSizes,
size_t texCount, const ObjToken<ITexture>* texs,
const int* texBindIdxs, const bool* depthBind,
size_t baseVert = 0, size_t baseInst = 0);
};
static std::vector<uint8_t> CompileMetal(const char* source, PipelineStage stage); ObjToken<IShaderDataBinding> newShaderDataBinding(
const ObjToken<IShaderPipeline>& pipeline, const ObjToken<IGraphicsBuffer>& vbo,
const ObjToken<IGraphicsBuffer>& instVbo, const ObjToken<IGraphicsBuffer>& ibo, size_t ubufCount,
const ObjToken<IGraphicsBuffer>* ubufs, const PipelineStage* ubufStages, const size_t* ubufOffs,
const size_t* ubufSizes, size_t texCount, const ObjToken<ITexture>* texs, const int* texBindIdxs,
const bool* depthBind, size_t baseVert = 0, size_t baseInst = 0);
};
static std::vector<uint8_t> CompileMetal(const char* source, PipelineStage stage);
}; };
} } // namespace boo
#endif #endif
#endif // __APPLE__ #endif // __APPLE__

View File

@ -12,82 +12,74 @@ struct pipe_context;
struct st_context; struct st_context;
struct pipe_surface; struct pipe_surface;
namespace boo namespace boo {
{
struct BaseGraphicsData; struct BaseGraphicsData;
struct NXContext struct NXContext {
{ struct pipe_surface* m_windowSurfaces[2];
struct pipe_surface* m_windowSurfaces[2]; NvFence m_fences[2];
NvFence m_fences[2]; bool m_fence_swap;
bool m_fence_swap;
bool initialize(); bool initialize();
bool terminate(); bool terminate();
bool _resizeWindowSurfaces(); bool _resizeWindowSurfaces();
unsigned m_sampleCount = 1; unsigned m_sampleCount = 1;
struct pipe_screen* m_screen = nullptr; struct pipe_screen* m_screen = nullptr;
struct pipe_context* m_pctx = nullptr; struct pipe_context* m_pctx = nullptr;
struct st_context* m_st = nullptr; struct st_context* m_st = nullptr;
nx_compiler m_compiler; nx_compiler m_compiler;
std::unordered_map<uint32_t, void*> m_samplers; std::unordered_map<uint32_t, void*> m_samplers;
std::unordered_map<uint32_t, void*> m_blendStates; std::unordered_map<uint32_t, void*> m_blendStates;
std::unordered_map<uint32_t, void*> m_rasStates; std::unordered_map<uint32_t, void*> m_rasStates;
std::unordered_map<uint32_t, void*> m_dsStates; std::unordered_map<uint32_t, void*> m_dsStates;
std::unordered_map<uint64_t, void*> m_vtxElemStates; std::unordered_map<uint64_t, void*> m_vtxElemStates;
}; };
class NXDataFactory : public IGraphicsDataFactory class NXDataFactory : public IGraphicsDataFactory {
{
public: public:
class Context final : public IGraphicsDataFactory::Context class Context final : public IGraphicsDataFactory::Context {
{ friend class NXDataFactoryImpl;
friend class NXDataFactoryImpl; NXDataFactory& m_parent;
NXDataFactory& m_parent; boo::ObjToken<BaseGraphicsData> m_data;
boo::ObjToken<BaseGraphicsData> m_data; Context(NXDataFactory& parent __BooTraceArgs);
Context(NXDataFactory& parent __BooTraceArgs); ~Context();
~Context();
public:
Platform platform() const {return Platform::NX;}
const SystemChar* platformName() const {return _SYS_STR("NX");}
boo::ObjToken<IGraphicsBufferS> newStaticBuffer(BufferUse use, const void* data, size_t stride, size_t count); public:
boo::ObjToken<IGraphicsBufferD> newDynamicBuffer(BufferUse use, size_t stride, size_t count); Platform platform() const { return Platform::NX; }
const SystemChar* platformName() const { return _SYS_STR("NX"); }
boo::ObjToken<ITextureS> newStaticTexture(size_t width, size_t height, size_t mips, TextureFormat fmt, boo::ObjToken<IGraphicsBufferS> newStaticBuffer(BufferUse use, const void* data, size_t stride, size_t count);
TextureClampMode clampMode, const void* data, size_t sz); boo::ObjToken<IGraphicsBufferD> newDynamicBuffer(BufferUse use, size_t stride, size_t count);
boo::ObjToken<ITextureSA> newStaticArrayTexture(size_t width, size_t height, size_t layers, size_t mips,
TextureFormat fmt, TextureClampMode clampMode,
const void* data, size_t sz);
boo::ObjToken<ITextureD> newDynamicTexture(size_t width, size_t height, TextureFormat fmt, TextureClampMode clampMode);
boo::ObjToken<ITextureR> newRenderTexture(size_t width, size_t height, TextureClampMode clampMode,
size_t colorBindCount, size_t depthBindCount);
ObjToken<IShaderStage> boo::ObjToken<ITextureS> newStaticTexture(size_t width, size_t height, size_t mips, TextureFormat fmt,
newShaderStage(const uint8_t* data, size_t size, PipelineStage stage); TextureClampMode clampMode, const void* data, size_t sz);
boo::ObjToken<ITextureSA> newStaticArrayTexture(size_t width, size_t height, size_t layers, size_t mips,
TextureFormat fmt, TextureClampMode clampMode, const void* data,
size_t sz);
boo::ObjToken<ITextureD> newDynamicTexture(size_t width, size_t height, TextureFormat fmt,
TextureClampMode clampMode);
boo::ObjToken<ITextureR> newRenderTexture(size_t width, size_t height, TextureClampMode clampMode,
size_t colorBindCount, size_t depthBindCount);
ObjToken<IShaderPipeline> ObjToken<IShaderStage> newShaderStage(const uint8_t* data, size_t size, PipelineStage stage);
newShaderPipeline(ObjToken<IShaderStage> vertex, ObjToken<IShaderStage> fragment,
ObjToken<IShaderStage> geometry, ObjToken<IShaderStage> control,
ObjToken<IShaderStage> evaluation, const VertexFormatInfo& vtxFmt,
const AdditionalPipelineInfo& additionalInfo);
boo::ObjToken<IShaderDataBinding> ObjToken<IShaderPipeline> newShaderPipeline(ObjToken<IShaderStage> vertex, ObjToken<IShaderStage> fragment,
newShaderDataBinding(const boo::ObjToken<IShaderPipeline>& pipeline, ObjToken<IShaderStage> geometry, ObjToken<IShaderStage> control,
const boo::ObjToken<IGraphicsBuffer>& vbo, ObjToken<IShaderStage> evaluation, const VertexFormatInfo& vtxFmt,
const boo::ObjToken<IGraphicsBuffer>& instVbo, const AdditionalPipelineInfo& additionalInfo);
const boo::ObjToken<IGraphicsBuffer>& ibo,
size_t ubufCount, const boo::ObjToken<IGraphicsBuffer>* ubufs, const PipelineStage* ubufStages, boo::ObjToken<IShaderDataBinding> newShaderDataBinding(
const size_t* ubufOffs, const size_t* ubufSizes, const boo::ObjToken<IShaderPipeline>& pipeline, const boo::ObjToken<IGraphicsBuffer>& vbo,
size_t texCount, const boo::ObjToken<ITexture>* texs, const boo::ObjToken<IGraphicsBuffer>& instVbo, const boo::ObjToken<IGraphicsBuffer>& ibo, size_t ubufCount,
const int* bindIdxs, const bool* bindDepth, const boo::ObjToken<IGraphicsBuffer>* ubufs, const PipelineStage* ubufStages, const size_t* ubufOffs,
size_t baseVert = 0, size_t baseInst = 0); const size_t* ubufSizes, size_t texCount, const boo::ObjToken<ITexture>* texs, const int* bindIdxs,
}; const bool* bindDepth, size_t baseVert = 0, size_t baseInst = 0);
};
}; };
} } // namespace boo
#endif #endif

View File

@ -15,171 +15,153 @@
/* Forward-declare handle type for Vulkan Memory Allocator */ /* Forward-declare handle type for Vulkan Memory Allocator */
struct VmaAllocator_T; struct VmaAllocator_T;
namespace boo namespace boo {
{
struct BaseGraphicsData; struct BaseGraphicsData;
struct VulkanContext struct VulkanContext {
{ struct LayerProperties {
struct LayerProperties VkLayerProperties properties;
{ std::vector<VkExtensionProperties> extensions;
VkLayerProperties properties; };
std::vector<VkExtensionProperties> extensions;
};
std::vector<LayerProperties> m_instanceLayerProperties; std::vector<LayerProperties> m_instanceLayerProperties;
std::vector<const char*> m_layerNames; std::vector<const char*> m_layerNames;
std::vector<const char*> m_instanceExtensionNames; std::vector<const char*> m_instanceExtensionNames;
VkInstance m_instance = VK_NULL_HANDLE; VkInstance m_instance = VK_NULL_HANDLE;
std::vector<const char*> m_deviceExtensionNames; std::vector<const char*> m_deviceExtensionNames;
std::vector<VkPhysicalDevice> m_gpus; std::vector<VkPhysicalDevice> m_gpus;
VkPhysicalDeviceFeatures m_features; VkPhysicalDeviceFeatures m_features;
VkPhysicalDeviceProperties m_gpuProps; VkPhysicalDeviceProperties m_gpuProps;
VkPhysicalDeviceMemoryProperties m_memoryProperties; VkPhysicalDeviceMemoryProperties m_memoryProperties;
VkDevice m_dev = VK_NULL_HANDLE; VkDevice m_dev = VK_NULL_HANDLE;
VmaAllocator_T* m_allocator = VK_NULL_HANDLE; VmaAllocator_T* m_allocator = VK_NULL_HANDLE;
uint32_t m_queueCount; uint32_t m_queueCount;
uint32_t m_graphicsQueueFamilyIndex = UINT32_MAX; uint32_t m_graphicsQueueFamilyIndex = UINT32_MAX;
std::vector<VkQueueFamilyProperties> m_queueProps; std::vector<VkQueueFamilyProperties> m_queueProps;
VkQueue m_queue = VK_NULL_HANDLE; VkQueue m_queue = VK_NULL_HANDLE;
std::mutex m_queueLock; std::mutex m_queueLock;
VkDescriptorSetLayout m_descSetLayout = VK_NULL_HANDLE; VkDescriptorSetLayout m_descSetLayout = VK_NULL_HANDLE;
VkPipelineLayout m_pipelinelayout = VK_NULL_HANDLE; VkPipelineLayout m_pipelinelayout = VK_NULL_HANDLE;
VkRenderPass m_pass = VK_NULL_HANDLE; VkRenderPass m_pass = VK_NULL_HANDLE;
VkRenderPass m_passColorOnly = VK_NULL_HANDLE; VkRenderPass m_passColorOnly = VK_NULL_HANDLE;
VkCommandPool m_loadPool = VK_NULL_HANDLE; VkCommandPool m_loadPool = VK_NULL_HANDLE;
VkCommandBuffer m_loadCmdBuf = VK_NULL_HANDLE; VkCommandBuffer m_loadCmdBuf = VK_NULL_HANDLE;
VkFormat m_displayFormat; VkFormat m_displayFormat;
VkFormat m_internalFormat; VkFormat m_internalFormat;
struct Window struct Window {
{ struct SwapChain {
struct SwapChain VkFormat m_format = VK_FORMAT_UNDEFINED;
{ VkSwapchainKHR m_swapChain = VK_NULL_HANDLE;
VkFormat m_format = VK_FORMAT_UNDEFINED; struct Buffer {
VkSwapchainKHR m_swapChain = VK_NULL_HANDLE; VkImage m_image = VK_NULL_HANDLE;
struct Buffer VkImageView m_colorView = VK_NULL_HANDLE;
{ VkFramebuffer m_framebuffer = VK_NULL_HANDLE;
VkImage m_image = VK_NULL_HANDLE; VkRenderPassBeginInfo m_passBeginInfo = {};
VkImageView m_colorView = VK_NULL_HANDLE; void setImage(VulkanContext* ctx, VkImage image, uint32_t width, uint32_t height);
VkFramebuffer m_framebuffer = VK_NULL_HANDLE; void destroy(VkDevice dev);
VkRenderPassBeginInfo m_passBeginInfo = {}; };
void setImage(VulkanContext* ctx, VkImage image, uint32_t width, uint32_t height); std::vector<Buffer> m_bufs;
void destroy(VkDevice dev); uint32_t m_backBuf = 0;
}; void destroy(VkDevice dev) {
std::vector<Buffer> m_bufs; for (Buffer& buf : m_bufs)
uint32_t m_backBuf = 0; buf.destroy(dev);
void destroy(VkDevice dev) m_bufs.clear();
{ if (m_swapChain) {
for (Buffer& buf : m_bufs) vk::DestroySwapchainKHR(dev, m_swapChain, nullptr);
buf.destroy(dev); m_swapChain = VK_NULL_HANDLE;
m_bufs.clear(); }
if (m_swapChain) m_backBuf = 0;
{ }
vk::DestroySwapchainKHR(dev, m_swapChain, nullptr); } m_swapChains[2];
m_swapChain = VK_NULL_HANDLE; uint32_t m_activeSwapChain = 0;
}
m_backBuf = 0;
}
} m_swapChains[2];
uint32_t m_activeSwapChain = 0;
#if _WIN32 #if _WIN32
HWND m_hwnd = 0; HWND m_hwnd = 0;
bool m_fs = false; bool m_fs = false;
LONG m_fsStyle; LONG m_fsStyle;
LONG m_fsExStyle; LONG m_fsExStyle;
RECT m_fsRect; RECT m_fsRect;
int m_fsCountDown = 0; int m_fsCountDown = 0;
#endif #endif
}; };
std::unordered_map<const boo::IWindow*, std::unique_ptr<Window>> m_windows; std::unordered_map<const boo::IWindow*, std::unique_ptr<Window>> m_windows;
VkSampleCountFlags m_sampleCountColor = VK_SAMPLE_COUNT_1_BIT; VkSampleCountFlags m_sampleCountColor = VK_SAMPLE_COUNT_1_BIT;
VkSampleCountFlags m_sampleCountDepth = VK_SAMPLE_COUNT_1_BIT; VkSampleCountFlags m_sampleCountDepth = VK_SAMPLE_COUNT_1_BIT;
float m_anisotropy = 1.f; float m_anisotropy = 1.f;
bool m_deepColor = false; bool m_deepColor = false;
std::unordered_map<uint32_t, VkSampler> m_samplers; std::unordered_map<uint32_t, VkSampler> m_samplers;
bool initVulkan(std::string_view appName, PFN_vkGetInstanceProcAddr getVkProc); bool initVulkan(std::string_view appName, PFN_vkGetInstanceProcAddr getVkProc);
bool enumerateDevices(); bool enumerateDevices();
void initDevice(); void initDevice();
void destroyDevice(); void destroyDevice();
void initSwapChain(Window& windowCtx, VkSurfaceKHR surface, VkFormat format, VkColorSpaceKHR colorspace); void initSwapChain(Window& windowCtx, VkSurfaceKHR surface, VkFormat format, VkColorSpaceKHR colorspace);
struct SwapChainResize struct SwapChainResize {
{ Window& m_windowCtx;
Window& m_windowCtx; VkSurfaceKHR m_surface;
VkSurfaceKHR m_surface; VkFormat m_format;
VkFormat m_format; VkColorSpaceKHR m_colorspace;
VkColorSpaceKHR m_colorspace; SWindowRect m_rect;
SWindowRect m_rect; SwapChainResize(Window& windowCtx, VkSurfaceKHR surface, VkFormat format, VkColorSpaceKHR colorspace,
SwapChainResize(Window& windowCtx, VkSurfaceKHR surface, const SWindowRect& rect)
VkFormat format, VkColorSpaceKHR colorspace, : m_windowCtx(windowCtx), m_surface(surface), m_format(format), m_colorspace(colorspace), m_rect(rect) {}
const SWindowRect& rect) };
: m_windowCtx(windowCtx), m_surface(surface), std::queue<SwapChainResize> m_deferredResizes;
m_format(format), m_colorspace(colorspace), m_rect(rect) {} std::mutex m_resizeLock;
}; void resizeSwapChain(Window& windowCtx, VkSurfaceKHR surface, VkFormat format, VkColorSpaceKHR colorspace,
std::queue<SwapChainResize> m_deferredResizes; const SWindowRect& rect);
std::mutex m_resizeLock; bool _resizeSwapChains();
void resizeSwapChain(Window& windowCtx, VkSurfaceKHR surface,
VkFormat format, VkColorSpaceKHR colorspace,
const SWindowRect& rect);
bool _resizeSwapChains();
}; };
extern VulkanContext g_VulkanContext; extern VulkanContext g_VulkanContext;
class VulkanDataFactory : public IGraphicsDataFactory class VulkanDataFactory : public IGraphicsDataFactory {
{
public: public:
class Context final : public IGraphicsDataFactory::Context class Context final : public IGraphicsDataFactory::Context {
{ friend class VulkanDataFactoryImpl;
friend class VulkanDataFactoryImpl; VulkanDataFactory& m_parent;
VulkanDataFactory& m_parent; boo::ObjToken<BaseGraphicsData> m_data;
boo::ObjToken<BaseGraphicsData> m_data; Context(VulkanDataFactory& parent __BooTraceArgs);
Context(VulkanDataFactory& parent __BooTraceArgs); ~Context();
~Context();
public:
Platform platform() const {return Platform::Vulkan;}
const SystemChar* platformName() const {return _SYS_STR("Vulkan");}
boo::ObjToken<IGraphicsBufferS> newStaticBuffer(BufferUse use, const void* data, size_t stride, size_t count); public:
boo::ObjToken<IGraphicsBufferD> newDynamicBuffer(BufferUse use, size_t stride, size_t count); Platform platform() const { return Platform::Vulkan; }
const SystemChar* platformName() const { return _SYS_STR("Vulkan"); }
boo::ObjToken<ITextureS> newStaticTexture(size_t width, size_t height, size_t mips, TextureFormat fmt, boo::ObjToken<IGraphicsBufferS> newStaticBuffer(BufferUse use, const void* data, size_t stride, size_t count);
TextureClampMode clampMode, const void* data, size_t sz); boo::ObjToken<IGraphicsBufferD> newDynamicBuffer(BufferUse use, size_t stride, size_t count);
boo::ObjToken<ITextureSA> newStaticArrayTexture(size_t width, size_t height, size_t layers, size_t mips,
TextureFormat fmt, TextureClampMode clampMode,
const void* data, size_t sz);
boo::ObjToken<ITextureD> newDynamicTexture(size_t width, size_t height, TextureFormat fmt, TextureClampMode clampMode);
boo::ObjToken<ITextureR> newRenderTexture(size_t width, size_t height, TextureClampMode clampMode,
size_t colorBindCount, size_t depthBindCount);
ObjToken<IShaderStage> boo::ObjToken<ITextureS> newStaticTexture(size_t width, size_t height, size_t mips, TextureFormat fmt,
newShaderStage(const uint8_t* data, size_t size, PipelineStage stage); TextureClampMode clampMode, const void* data, size_t sz);
boo::ObjToken<ITextureSA> newStaticArrayTexture(size_t width, size_t height, size_t layers, size_t mips,
TextureFormat fmt, TextureClampMode clampMode, const void* data,
size_t sz);
boo::ObjToken<ITextureD> newDynamicTexture(size_t width, size_t height, TextureFormat fmt,
TextureClampMode clampMode);
boo::ObjToken<ITextureR> newRenderTexture(size_t width, size_t height, TextureClampMode clampMode,
size_t colorBindCount, size_t depthBindCount);
ObjToken<IShaderPipeline> ObjToken<IShaderStage> newShaderStage(const uint8_t* data, size_t size, PipelineStage stage);
newShaderPipeline(ObjToken<IShaderStage> vertex, ObjToken<IShaderStage> fragment,
ObjToken<IShaderStage> geometry, ObjToken<IShaderStage> control,
ObjToken<IShaderStage> evaluation, const VertexFormatInfo& vtxFmt,
const AdditionalPipelineInfo& additionalInfo);
boo::ObjToken<IShaderDataBinding> ObjToken<IShaderPipeline> newShaderPipeline(ObjToken<IShaderStage> vertex, ObjToken<IShaderStage> fragment,
newShaderDataBinding(const boo::ObjToken<IShaderPipeline>& pipeline, ObjToken<IShaderStage> geometry, ObjToken<IShaderStage> control,
const boo::ObjToken<IGraphicsBuffer>& vbo, ObjToken<IShaderStage> evaluation, const VertexFormatInfo& vtxFmt,
const boo::ObjToken<IGraphicsBuffer>& instVbo, const AdditionalPipelineInfo& additionalInfo);
const boo::ObjToken<IGraphicsBuffer>& ibo,
size_t ubufCount, const boo::ObjToken<IGraphicsBuffer>* ubufs, const PipelineStage* ubufStages,
const size_t* ubufOffs, const size_t* ubufSizes,
size_t texCount, const boo::ObjToken<ITexture>* texs,
const int* bindIdxs, const bool* bindDepth,
size_t baseVert = 0, size_t baseInst = 0);
};
static std::vector<uint8_t> CompileGLSL(const char* source, PipelineStage stage); boo::ObjToken<IShaderDataBinding> newShaderDataBinding(
const boo::ObjToken<IShaderPipeline>& pipeline, const boo::ObjToken<IGraphicsBuffer>& vbo,
const boo::ObjToken<IGraphicsBuffer>& instVbo, const boo::ObjToken<IGraphicsBuffer>& ibo, size_t ubufCount,
const boo::ObjToken<IGraphicsBuffer>* ubufs, const PipelineStage* ubufStages, const size_t* ubufOffs,
const size_t* ubufSizes, size_t texCount, const boo::ObjToken<ITexture>* texs, const int* bindIdxs,
const bool* bindDepth, size_t baseVert = 0, size_t baseInst = 0);
};
static std::vector<uint8_t> CompileGLSL(const char* source, PipelineStage stage);
}; };
} } // namespace boo
#endif #endif

View File

@ -214,4 +214,3 @@ void init_dispatch_table_middle(VkInstance instance, bool include_bottom);
void init_dispatch_table_bottom(VkInstance instance, VkDevice dev); void init_dispatch_table_bottom(VkInstance instance, VkDevice dev);
} // namespace vk } // namespace vk

View File

@ -3,79 +3,76 @@
#include <string> #include <string>
/* These match mesa's internal stages */ /* These match mesa's internal stages */
enum class nx_shader_stage enum class nx_shader_stage {
{ NONE = -1,
NONE = -1, VERTEX = 0,
VERTEX = 0, TESS_CTRL = 1,
TESS_CTRL = 1, TESS_EVAL = 2,
TESS_EVAL = 2, GEOMETRY = 3,
GEOMETRY = 3, FRAGMENT = 4,
FRAGMENT = 4, COMPUTE = 5,
COMPUTE = 5,
}; };
struct standalone_options struct standalone_options {
{ int glsl_version;
int glsl_version; int dump_ast;
int dump_ast; int dump_hir;
int dump_hir; int dump_lir;
int dump_lir; int dump_builder;
int dump_builder; int do_link;
int do_link; int just_log;
int just_log;
}; };
class nx_compiler; class nx_compiler;
class nx_shader_stage_object class nx_shader_stage_object {
{ friend class nx_compiler;
friend class nx_compiler; nx_compiler* m_parent = nullptr;
nx_compiler* m_parent = nullptr; struct gl_shader* m_shader = nullptr;
struct gl_shader *m_shader = nullptr; nx_shader_stage_object(nx_compiler& parent) : m_parent(&parent) {}
nx_shader_stage_object(nx_compiler& parent) : m_parent(&parent) {}
public: public:
nx_shader_stage_object() = default; nx_shader_stage_object() = default;
nx_shader_stage_object(const nx_shader_stage_object&); nx_shader_stage_object(const nx_shader_stage_object&);
nx_shader_stage_object& operator=(const nx_shader_stage_object&); nx_shader_stage_object& operator=(const nx_shader_stage_object&);
~nx_shader_stage_object() { reset(); } ~nx_shader_stage_object() { reset(); }
void reset(); void reset();
operator bool() const; operator bool() const;
nx_shader_stage stage() const; nx_shader_stage stage() const;
const char* info_log() const; const char* info_log() const;
}; };
class nx_linked_shader class nx_linked_shader {
{ friend class nx_compiler;
friend class nx_compiler; nx_compiler* m_parent = nullptr;
nx_compiler* m_parent = nullptr; struct gl_shader_program* m_program = nullptr;
struct gl_shader_program* m_program = nullptr; nx_linked_shader(nx_compiler& parent) : m_parent(&parent) {}
nx_linked_shader(nx_compiler& parent) : m_parent(&parent) {}
public: public:
nx_linked_shader() = default; nx_linked_shader() = default;
nx_linked_shader(const nx_linked_shader&); nx_linked_shader(const nx_linked_shader&);
nx_linked_shader& operator=(const nx_linked_shader&); nx_linked_shader& operator=(const nx_linked_shader&);
~nx_linked_shader() { reset(); } ~nx_linked_shader() { reset(); }
void reset(); void reset();
operator bool() const { return m_program != nullptr; } operator bool() const { return m_program != nullptr; }
const struct gl_shader_program* program() const { return m_program; } const struct gl_shader_program* program() const { return m_program; }
}; };
class nx_compiler class nx_compiler {
{ friend class nx_shader_stage_object;
friend class nx_shader_stage_object; friend class nx_linked_shader;
friend class nx_linked_shader; struct pipe_screen* m_screen = nullptr;
struct pipe_screen *m_screen = nullptr; struct st_context* m_st = nullptr;
struct st_context *m_st = nullptr; struct standalone_options m_options = {};
struct standalone_options m_options = {}; bool m_ownsCtx = false;
bool m_ownsCtx = false; void compile_shader(struct gl_context* ctx, struct gl_shader* shader);
void compile_shader(struct gl_context *ctx, struct gl_shader *shader);
public: public:
nx_compiler(); nx_compiler();
~nx_compiler(); ~nx_compiler();
bool initialize(struct pipe_screen *screen, struct st_context *st, bool initialize(struct pipe_screen* screen, struct st_context* st, const struct standalone_options* o = nullptr);
const struct standalone_options *o = nullptr); bool initialize(const struct standalone_options* o = nullptr);
bool initialize(const struct standalone_options *o = nullptr); nx_shader_stage_object compile(nx_shader_stage type, const char* source);
nx_shader_stage_object compile(nx_shader_stage type, const char *source); nx_linked_shader link(unsigned num_stages, const nx_shader_stage_object** stages, std::string* infoLog = nullptr);
nx_linked_shader link(unsigned num_stages, const nx_shader_stage_object **stages, std::string* infoLog = nullptr); std::pair<std::shared_ptr<uint8_t[]>, size_t> offline_link(unsigned num_stages, const nx_shader_stage_object** stages,
std::pair<std::shared_ptr<uint8_t[]>, size_t> std::string* infoLog = nullptr);
offline_link(unsigned num_stages, const nx_shader_stage_object **stages, std::string* infoLog = nullptr);
}; };

View File

@ -1,7 +1,3 @@
#pragma once #pragma once
namespace boo namespace boo {}
{
}

View File

@ -13,80 +13,73 @@
#include <hidsdi.h> #include <hidsdi.h>
#endif #endif
namespace boo namespace boo {
{
class DeviceToken; class DeviceToken;
class IHIDDevice; class IHIDDevice;
enum class HIDReportType enum class HIDReportType { Input, Output, Feature };
{
Input,
Output,
Feature
};
class DeviceBase : public std::enable_shared_from_this<DeviceBase> class DeviceBase : public std::enable_shared_from_this<DeviceBase> {
{ friend class DeviceToken;
friend class DeviceToken; friend struct DeviceSignature;
friend struct DeviceSignature; friend class HIDDeviceIOKit;
friend class HIDDeviceIOKit;
uint64_t m_typeHash; uint64_t m_typeHash;
class DeviceToken* m_token; class DeviceToken* m_token;
std::shared_ptr<IHIDDevice> m_hidDev; std::shared_ptr<IHIDDevice> m_hidDev;
void _deviceDisconnected(); void _deviceDisconnected();
public: public:
DeviceBase(uint64_t typeHash, DeviceToken* token); DeviceBase(uint64_t typeHash, DeviceToken* token);
virtual ~DeviceBase() = default; virtual ~DeviceBase() = default;
uint64_t getTypeHash() const { return m_typeHash; } uint64_t getTypeHash() const { return m_typeHash; }
void closeDevice(); void closeDevice();
/* Callbacks */
virtual void deviceDisconnected()=0;
virtual void deviceError(const char* error, ...);
virtual void initialCycle() {}
virtual void transferCycle() {}
virtual void finalCycle() {}
/* Low-Level API */ /* Callbacks */
bool sendUSBInterruptTransfer(const uint8_t* data, size_t length); virtual void deviceDisconnected() = 0;
size_t receiveUSBInterruptTransfer(uint8_t* data, size_t length); virtual void deviceError(const char* error, ...);
virtual void initialCycle() {}
virtual void transferCycle() {}
virtual void finalCycle() {}
inline unsigned getVendorId(); /* Low-Level API */
inline unsigned getProductId(); bool sendUSBInterruptTransfer(const uint8_t* data, size_t length);
inline std::string_view getVendorName(); size_t receiveUSBInterruptTransfer(uint8_t* data, size_t length);
inline std::string_view getProductName();
/* High-Level API */ inline unsigned getVendorId();
inline unsigned getProductId();
inline std::string_view getVendorName();
inline std::string_view getProductName();
/* High-Level API */
#if _WIN32 #if _WIN32
#if !WINDOWS_STORE #if !WINDOWS_STORE
const PHIDP_PREPARSED_DATA getReportDescriptor(); const PHIDP_PREPARSED_DATA getReportDescriptor();
#endif #endif
#else #else
std::vector<uint8_t> getReportDescriptor(); std::vector<uint8_t> getReportDescriptor();
#endif #endif
bool sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message=0); bool sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message = 0);
size_t receiveHIDReport(uint8_t* data, size_t length, HIDReportType tp, uint32_t message=0); // Prefer callback version size_t receiveHIDReport(uint8_t* data, size_t length, HIDReportType tp,
virtual void receivedHIDReport(const uint8_t* /*data*/, size_t /*length*/, HIDReportType /*tp*/, uint32_t /*message*/) {} uint32_t message = 0); // Prefer callback version
virtual void receivedHIDReport(const uint8_t* /*data*/, size_t /*length*/, HIDReportType /*tp*/,
uint32_t /*message*/) {}
}; };
template <class CB> template <class CB>
class TDeviceBase : public DeviceBase class TDeviceBase : public DeviceBase {
{
protected: protected:
std::mutex m_callbackLock; std::mutex m_callbackLock;
CB* m_callback = nullptr; CB* m_callback = nullptr;
public: public:
TDeviceBase(uint64_t typeHash, DeviceToken* token) : DeviceBase(typeHash, token) {} TDeviceBase(uint64_t typeHash, DeviceToken* token) : DeviceBase(typeHash, token) {}
void setCallback(CB* cb) void setCallback(CB* cb) {
{ std::lock_guard<std::mutex> lk(m_callbackLock);
std::lock_guard<std::mutex> lk(m_callbackLock); m_callback = cb;
m_callback = cb; }
}
}; };
} } // namespace boo

View File

@ -16,151 +16,131 @@
#include <windows.h> #include <windows.h>
#endif #endif
namespace boo namespace boo {
{
class DeviceFinder class DeviceFinder {
{
public: public:
friend class HIDListenerIOKit; friend class HIDListenerIOKit;
friend class HIDListenerUdev; friend class HIDListenerUdev;
friend class HIDListenerWinUSB; friend class HIDListenerWinUSB;
static inline DeviceFinder* instance() {return skDevFinder;} static inline DeviceFinder* instance() { return skDevFinder; }
private: private:
static class DeviceFinder* skDevFinder; static class DeviceFinder* skDevFinder;
/* Types this finder is interested in (immutable) */ /* Types this finder is interested in (immutable) */
DeviceSignature::TDeviceSignatureSet m_types; DeviceSignature::TDeviceSignatureSet m_types;
/* Platform-specific USB event registration /* Platform-specific USB event registration
* (for auto-scanning, NULL if not registered) */ * (for auto-scanning, NULL if not registered) */
std::unique_ptr<IHIDListener> m_listener; std::unique_ptr<IHIDListener> m_listener;
/* Set of presently-connected device tokens */ /* Set of presently-connected device tokens */
TDeviceTokens m_tokens; TDeviceTokens m_tokens;
std::mutex m_tokensLock; std::mutex m_tokensLock;
/* Friend methods for platform-listener to find/insert/remove /* Friend methods for platform-listener to find/insert/remove
* tokens with type-filtering */ * tokens with type-filtering */
inline bool _hasToken(const std::string& path) inline bool _hasToken(const std::string& path) {
{ auto preCheck = m_tokens.find(path);
auto preCheck = m_tokens.find(path); if (preCheck != m_tokens.end())
if (preCheck != m_tokens.end()) return true;
return true; return false;
return false; }
inline bool _insertToken(std::unique_ptr<DeviceToken>&& token) {
if (DeviceSignature::DeviceMatchToken(*token, m_types)) {
m_tokensLock.lock();
TInsertedDeviceToken inseredTok = m_tokens.insert(std::make_pair(token->getDevicePath(), std::move(token)));
m_tokensLock.unlock();
deviceConnected(*inseredTok.first->second);
return true;
} }
inline bool _insertToken(std::unique_ptr<DeviceToken>&& token) return false;
{ }
if (DeviceSignature::DeviceMatchToken(*token, m_types)) inline void _removeToken(const std::string& path) {
{ auto preCheck = m_tokens.find(path);
m_tokensLock.lock(); if (preCheck != m_tokens.end()) {
TInsertedDeviceToken inseredTok = DeviceToken& tok = *preCheck->second;
m_tokens.insert(std::make_pair(token->getDevicePath(), std::move(token))); std::shared_ptr<DeviceBase> dev = tok.m_connectedDev;
m_tokensLock.unlock(); tok._deviceClose();
deviceConnected(*inseredTok.first->second); deviceDisconnected(tok, dev.get());
return true; m_tokensLock.lock();
} m_tokens.erase(preCheck);
return false; m_tokensLock.unlock();
}
inline void _removeToken(const std::string& path)
{
auto preCheck = m_tokens.find(path);
if (preCheck != m_tokens.end())
{
DeviceToken& tok = *preCheck->second;
std::shared_ptr<DeviceBase> dev = tok.m_connectedDev;
tok._deviceClose();
deviceDisconnected(tok, dev.get());
m_tokensLock.lock();
m_tokens.erase(preCheck);
m_tokensLock.unlock();
}
} }
}
public: public:
class CDeviceTokensHandle {
DeviceFinder& m_finder;
class CDeviceTokensHandle public:
{ inline CDeviceTokensHandle(DeviceFinder& finder) : m_finder(finder) { m_finder.m_tokensLock.lock(); }
DeviceFinder& m_finder; inline ~CDeviceTokensHandle() { m_finder.m_tokensLock.unlock(); }
public: inline TDeviceTokens::iterator begin() { return m_finder.m_tokens.begin(); }
inline CDeviceTokensHandle(DeviceFinder& finder) : m_finder(finder) inline TDeviceTokens::iterator end() { return m_finder.m_tokens.end(); }
{m_finder.m_tokensLock.lock();} };
inline ~CDeviceTokensHandle() {m_finder.m_tokensLock.unlock();}
inline TDeviceTokens::iterator begin() {return m_finder.m_tokens.begin();}
inline TDeviceTokens::iterator end() {return m_finder.m_tokens.end();}
};
/* Application must specify its interested device-types */ /* Application must specify its interested device-types */
DeviceFinder(std::unordered_set<uint64_t> types) DeviceFinder(std::unordered_set<uint64_t> types) {
{ if (skDevFinder) {
if (skDevFinder) fprintf(stderr, "only one instance of CDeviceFinder may be constructed");
{ abort();
fprintf(stderr, "only one instance of CDeviceFinder may be constructed");
abort();
}
skDevFinder = this;
for (const uint64_t& typeHash : types)
{
const DeviceSignature* sigIter = BOO_DEVICE_SIGS;
while (sigIter->m_name)
{
if (sigIter->m_typeHash == typeHash)
m_types.push_back(sigIter);
++sigIter;
}
}
} }
virtual ~DeviceFinder() skDevFinder = this;
{ for (const uint64_t& typeHash : types) {
if (m_listener) const DeviceSignature* sigIter = BOO_DEVICE_SIGS;
m_listener->stopScanning(); while (sigIter->m_name) {
skDevFinder = NULL; if (sigIter->m_typeHash == typeHash)
m_types.push_back(sigIter);
++sigIter;
}
} }
}
virtual ~DeviceFinder() {
if (m_listener)
m_listener->stopScanning();
skDevFinder = NULL;
}
/* Get interested device-type mask */ /* Get interested device-type mask */
inline const DeviceSignature::TDeviceSignatureSet& getTypes() const {return m_types;} inline const DeviceSignature::TDeviceSignatureSet& getTypes() const { return m_types; }
/* Iterable set of tokens */ /* Iterable set of tokens */
inline CDeviceTokensHandle getTokens() {return CDeviceTokensHandle(*this);} inline CDeviceTokensHandle getTokens() { return CDeviceTokensHandle(*this); }
/* Automatic device scanning */ /* Automatic device scanning */
inline bool startScanning() inline bool startScanning() {
{ if (!m_listener)
if (!m_listener) m_listener = IHIDListenerNew(*this);
m_listener = IHIDListenerNew(*this); if (m_listener)
if (m_listener) return m_listener->startScanning();
return m_listener->startScanning(); return false;
return false; }
} inline bool stopScanning() {
inline bool stopScanning() if (!m_listener)
{ m_listener = IHIDListenerNew(*this);
if (!m_listener) if (m_listener)
m_listener = IHIDListenerNew(*this); return m_listener->stopScanning();
if (m_listener) return false;
return m_listener->stopScanning(); }
return false;
}
/* Manual device scanning */ /* Manual device scanning */
inline bool scanNow() inline bool scanNow() {
{ if (!m_listener)
if (!m_listener) m_listener = IHIDListenerNew(*this);
m_listener = IHIDListenerNew(*this); if (m_listener)
if (m_listener) return m_listener->scanNow();
return m_listener->scanNow(); return false;
return false; }
}
virtual void deviceConnected(DeviceToken&) {} virtual void deviceConnected(DeviceToken&) {}
virtual void deviceDisconnected(DeviceToken&, DeviceBase*) {} virtual void deviceDisconnected(DeviceToken&, DeviceBase*) {}
#if _WIN32 #if _WIN32
/* Windows-specific WM_DEVICECHANGED handler */ /* Windows-specific WM_DEVICECHANGED handler */
static LRESULT winDevChangedHandler(WPARAM wParam, LPARAM lParam); static LRESULT winDevChangedHandler(WPARAM wParam, LPARAM lParam);
#endif #endif
}; };
} } // namespace boo

View File

@ -6,48 +6,36 @@
#include <memory> #include <memory>
#include <string> #include <string>
namespace boo namespace boo {
{
enum class DeviceType enum class DeviceType { None = 0, USB = 1, Bluetooth = 2, HID = 3, XInput = 4 };
{
None = 0,
USB = 1,
Bluetooth = 2,
HID = 3,
XInput = 4
};
class DeviceToken; class DeviceToken;
class DeviceBase; class DeviceBase;
#define dev_typeid(type) std::hash<std::string>()(#type) #define dev_typeid(type) std::hash<std::string>()(#type)
struct DeviceSignature struct DeviceSignature {
{ typedef std::vector<const DeviceSignature*> TDeviceSignatureSet;
typedef std::vector<const DeviceSignature*> TDeviceSignatureSet; typedef std::function<std::shared_ptr<DeviceBase>(DeviceToken*)> TFactoryLambda;
typedef std::function<std::shared_ptr<DeviceBase>(DeviceToken*)> TFactoryLambda; const char* m_name;
const char* m_name; uint64_t m_typeHash;
uint64_t m_typeHash; unsigned m_vid, m_pid;
unsigned m_vid, m_pid; TFactoryLambda m_factory;
TFactoryLambda m_factory; DeviceType m_type;
DeviceType m_type; DeviceSignature() : m_name(NULL), m_typeHash(dev_typeid(DeviceSignature)) {} /* Sentinel constructor */
DeviceSignature() : m_name(NULL), m_typeHash(dev_typeid(DeviceSignature)) {} /* Sentinel constructor */ DeviceSignature(const char* name, uint64_t typeHash, unsigned vid, unsigned pid, TFactoryLambda&& factory,
DeviceSignature(const char* name, uint64_t typeHash, unsigned vid, unsigned pid, DeviceType type = DeviceType::None)
TFactoryLambda&& factory, DeviceType type=DeviceType::None) : m_name(name), m_typeHash(typeHash), m_vid(vid), m_pid(pid), m_factory(factory), m_type(type) {}
: m_name(name), m_typeHash(typeHash), m_vid(vid), m_pid(pid), static bool DeviceMatchToken(const DeviceToken& token, const TDeviceSignatureSet& sigSet);
m_factory(factory), m_type(type) {} static std::shared_ptr<DeviceBase> DeviceNew(DeviceToken& token);
static bool DeviceMatchToken(const DeviceToken& token, const TDeviceSignatureSet& sigSet);
static std::shared_ptr<DeviceBase> DeviceNew(DeviceToken& token);
}; };
#define DEVICE_SIG(name, vid, pid, type) \ #define DEVICE_SIG(name, vid, pid, type) \
DeviceSignature(#name, dev_typeid(name), vid, pid,\ DeviceSignature(#name, dev_typeid(name), vid, pid, \
[](DeviceToken* tok) -> std::shared_ptr<DeviceBase> {return std::make_shared<name>(tok);}, type) [](DeviceToken* tok) -> std::shared_ptr<DeviceBase> { return std::make_shared<name>(tok); }, type)
#define DEVICE_SIG_SENTINEL() DeviceSignature() #define DEVICE_SIG_SENTINEL() DeviceSignature()
extern const DeviceSignature BOO_DEVICE_SIGS[]; extern const DeviceSignature BOO_DEVICE_SIGS[];
} } // namespace boo

View File

@ -4,75 +4,62 @@
#include "DeviceBase.hpp" #include "DeviceBase.hpp"
#include "DeviceSignature.hpp" #include "DeviceSignature.hpp"
namespace boo namespace boo {
{
class DeviceToken class DeviceToken {
{ friend struct DeviceSignature;
friend struct DeviceSignature; friend class HIDListenerWinUSB;
friend class HIDListenerWinUSB; DeviceType m_devType;
DeviceType m_devType; unsigned m_vendorId;
unsigned m_vendorId; unsigned m_productId;
unsigned m_productId; std::string m_vendorName;
std::string m_vendorName; std::string m_productName;
std::string m_productName; std::string m_devPath;
std::string m_devPath;
friend class DeviceBase;
friend class DeviceBase; std::shared_ptr<DeviceBase> m_connectedDev;
std::shared_ptr<DeviceBase> m_connectedDev;
friend class DeviceFinder;
friend class DeviceFinder; inline void _deviceClose() {
inline void _deviceClose() if (m_connectedDev)
{ m_connectedDev->_deviceDisconnected();
if (m_connectedDev) m_connectedDev = NULL;
m_connectedDev->_deviceDisconnected(); }
m_connectedDev = NULL;
}
public: public:
DeviceToken(const DeviceToken&) = delete;
DeviceToken(const DeviceToken&& other)
: m_devType(other.m_devType)
, m_vendorId(other.m_vendorId)
, m_productId(other.m_productId)
, m_vendorName(other.m_vendorName)
, m_productName(other.m_productName)
, m_devPath(other.m_devPath)
, m_connectedDev(other.m_connectedDev) {}
inline DeviceToken(DeviceType devType, unsigned vid, unsigned pid, const char* vname, const char* pname,
const char* path)
: m_devType(devType), m_vendorId(vid), m_productId(pid), m_devPath(path), m_connectedDev(NULL) {
if (vname)
m_vendorName = vname;
if (pname)
m_productName = pname;
}
DeviceToken(const DeviceToken&) = delete; inline DeviceType getDeviceType() const { return m_devType; }
DeviceToken(const DeviceToken&& other) inline unsigned getVendorId() const { return m_vendorId; }
: m_devType(other.m_devType), inline unsigned getProductId() const { return m_productId; }
m_vendorId(other.m_vendorId), inline std::string_view getVendorName() const { return m_vendorName; }
m_productId(other.m_productId), inline std::string_view getProductName() const { return m_productName; }
m_vendorName(other.m_vendorName), inline std::string_view getDevicePath() const { return m_devPath; }
m_productName(other.m_productName), inline bool isDeviceOpen() const { return (m_connectedDev != NULL); }
m_devPath(other.m_devPath), inline std::shared_ptr<DeviceBase> openAndGetDevice() {
m_connectedDev(other.m_connectedDev) if (!m_connectedDev)
{} m_connectedDev = DeviceSignature::DeviceNew(*this);
inline DeviceToken(DeviceType devType, unsigned vid, unsigned pid, const char* vname, const char* pname, const char* path) return m_connectedDev;
: m_devType(devType), }
m_vendorId(vid),
m_productId(pid), inline bool operator==(const DeviceToken& rhs) const { return m_devPath == rhs.m_devPath; }
m_devPath(path), inline bool operator<(const DeviceToken& rhs) const { return m_devPath < rhs.m_devPath; }
m_connectedDev(NULL)
{
if (vname)
m_vendorName = vname;
if (pname)
m_productName = pname;
}
inline DeviceType getDeviceType() const {return m_devType;}
inline unsigned getVendorId() const {return m_vendorId;}
inline unsigned getProductId() const {return m_productId;}
inline std::string_view getVendorName() const {return m_vendorName;}
inline std::string_view getProductName() const {return m_productName;}
inline std::string_view getDevicePath() const {return m_devPath;}
inline bool isDeviceOpen() const {return (m_connectedDev != NULL);}
inline std::shared_ptr<DeviceBase> openAndGetDevice()
{
if (!m_connectedDev)
m_connectedDev = DeviceSignature::DeviceNew(*this);
return m_connectedDev;
}
inline bool operator ==(const DeviceToken& rhs) const
{return m_devPath == rhs.m_devPath;}
inline bool operator <(const DeviceToken& rhs) const
{return m_devPath < rhs.m_devPath;}
}; };
} } // namespace boo

View File

@ -4,88 +4,93 @@
#include "DeviceBase.hpp" #include "DeviceBase.hpp"
#include "../System.hpp" #include "../System.hpp"
namespace boo namespace boo {
{
enum class EDolphinControllerType enum class EDolphinControllerType {
{ None = 0,
None = 0, Normal = 0x10,
Normal = 0x10, Wavebird = 0x20,
Wavebird = 0x20,
}; };
ENABLE_BITWISE_ENUM(EDolphinControllerType) ENABLE_BITWISE_ENUM(EDolphinControllerType)
enum class EDolphinControllerButtons enum class EDolphinControllerButtons {
{ Start = 1 << 0,
Start = 1<<0, Z = 1 << 1,
Z = 1<<1, R = 1 << 2,
R = 1<<2, L = 1 << 3,
L = 1<<3, A = 1 << 8,
A = 1<<8, B = 1 << 9,
B = 1<<9, X = 1 << 10,
X = 1<<10, Y = 1 << 11,
Y = 1<<11, Left = 1 << 12,
Left = 1<<12, Right = 1 << 13,
Right = 1<<13, Down = 1 << 14,
Down = 1<<14, Up = 1 << 15
Up = 1<<15
}; };
ENABLE_BITWISE_ENUM(EDolphinControllerButtons) ENABLE_BITWISE_ENUM(EDolphinControllerButtons)
struct DolphinControllerState struct DolphinControllerState {
{ int16_t m_leftStick[2] = {0};
int16_t m_leftStick[2] = {0}; int16_t m_rightStick[2] = {0};
int16_t m_rightStick[2] = {0}; int16_t m_analogTriggers[2] = {0};
int16_t m_analogTriggers[2] = {0}; uint16_t m_btns = 0;
uint16_t m_btns = 0; void reset() {
void reset() m_leftStick[0] = 0;
{ m_leftStick[1] = 0;
m_leftStick[0] = 0; m_rightStick[0] = 0;
m_leftStick[1] = 0; m_rightStick[1] = 0;
m_rightStick[0] = 0; m_analogTriggers[0] = 0;
m_rightStick[1] = 0; m_analogTriggers[1] = 0;
m_analogTriggers[0] = 0; m_btns = 0;
m_analogTriggers[1] = 0; }
m_btns = 0; void clamp();
}
void clamp();
}; };
struct IDolphinSmashAdapterCallback struct IDolphinSmashAdapterCallback {
{ virtual void controllerConnected(unsigned idx, EDolphinControllerType type) {
virtual void controllerConnected(unsigned idx, EDolphinControllerType type) {(void)idx;(void)type;} (void)idx;
virtual void controllerDisconnected(unsigned idx) {(void)idx;} (void)type;
virtual void controllerUpdate(unsigned idx, EDolphinControllerType type, }
const DolphinControllerState& state) {(void)idx;(void)type;(void)state;} virtual void controllerDisconnected(unsigned idx) { (void)idx; }
virtual void controllerUpdate(unsigned idx, EDolphinControllerType type, const DolphinControllerState& state) {
(void)idx;
(void)type;
(void)state;
}
}; };
class DolphinSmashAdapter final : public TDeviceBase<IDolphinSmashAdapterCallback> class DolphinSmashAdapter final : public TDeviceBase<IDolphinSmashAdapterCallback> {
{ int16_t m_leftStickCal[2] = {0x7f};
int16_t m_leftStickCal[2] = {0x7f}; int16_t m_rightStickCal[2] = {0x7f};
int16_t m_rightStickCal[2] = {0x7f}; int16_t m_triggersCal[2] = {0x0};
int16_t m_triggersCal[2] = {0x0}; uint8_t m_knownControllers = 0;
uint8_t m_knownControllers = 0; uint8_t m_rumbleRequest = 0;
uint8_t m_rumbleRequest = 0; bool m_hardStop[4] = {false};
bool m_hardStop[4] = {false}; uint8_t m_rumbleState = 0xf; /* Force initial send of stop-rumble command */
uint8_t m_rumbleState = 0xf; /* Force initial send of stop-rumble command */ void deviceDisconnected();
void deviceDisconnected(); void initialCycle();
void initialCycle(); void transferCycle();
void transferCycle(); void finalCycle();
void finalCycle();
public: public:
DolphinSmashAdapter(DeviceToken* token); DolphinSmashAdapter(DeviceToken* token);
~DolphinSmashAdapter(); ~DolphinSmashAdapter();
void setCallback(IDolphinSmashAdapterCallback* cb) void setCallback(IDolphinSmashAdapterCallback* cb) {
{ TDeviceBase<IDolphinSmashAdapterCallback>::setCallback(cb);
TDeviceBase<IDolphinSmashAdapterCallback>::setCallback(cb); m_knownControllers = 0;
m_knownControllers = 0; }
} void startRumble(unsigned idx) {
void startRumble(unsigned idx) if (idx >= 4)
{if (idx >= 4) return; m_rumbleRequest |= 1<<idx;} return;
void stopRumble(unsigned idx, bool hard=false) m_rumbleRequest |= 1 << idx;
{if (idx >= 4) return; m_rumbleRequest &= ~(1<<idx); m_hardStop[idx] = hard;} }
void stopRumble(unsigned idx, bool hard = false) {
if (idx >= 4)
return;
m_rumbleRequest &= ~(1 << idx);
m_hardStop[idx] = hard;
}
}; };
} } // namespace boo

View File

@ -4,181 +4,153 @@
#include "DeviceBase.hpp" #include "DeviceBase.hpp"
#include "boo/System.hpp" #include "boo/System.hpp"
namespace boo namespace boo {
{
struct DualshockLED struct DualshockLED {
{ uint8_t timeEnabled;
uint8_t timeEnabled; uint8_t dutyLength;
uint8_t dutyLength; uint8_t enabled;
uint8_t enabled; uint8_t dutyOff;
uint8_t dutyOff; uint8_t dutyOn;
uint8_t dutyOn;
}; };
struct DualshockRumble struct DualshockRumble {
{ uint8_t padding;
uint8_t padding; uint8_t rightDuration;
uint8_t rightDuration; bool rightOn;
bool rightOn; uint8_t leftDuration;
uint8_t leftDuration; uint8_t leftForce;
uint8_t leftForce;
}; };
union DualshockOutReport union DualshockOutReport {
{ struct {
struct uint8_t reportId;
{ DualshockRumble rumble;
uint8_t reportId; uint8_t gyro1;
DualshockRumble rumble; uint8_t gyro2;
uint8_t gyro1; uint8_t padding[2];
uint8_t gyro2; uint8_t leds;
uint8_t padding[2]; DualshockLED led[4];
uint8_t leds; DualshockLED reserved;
DualshockLED led[4]; };
DualshockLED reserved; uint8_t buf[49];
};
uint8_t buf[49];
}; };
enum class EDualshockPadButtons enum class EDualshockPadButtons {
{ Select = 1 << 0,
Select = 1<< 0, L3 = 1 << 1,
L3 = 1<< 1, R3 = 1 << 2,
R3 = 1<< 2, Start = 1 << 3,
Start = 1<< 3, Up = 1 << 4,
Up = 1<< 4, Right = 1 << 5,
Right = 1<< 5, Down = 1 << 6,
Down = 1<< 6, Left = 1 << 7,
Left = 1<< 7, L2 = 1 << 8,
L2 = 1<< 8, R2 = 1 << 9,
R2 = 1<< 9, L1 = 1 << 10,
L1 = 1<<10, R1 = 1 << 11,
R1 = 1<<11, Triangle = 1 << 12,
Triangle = 1<<12, Circle = 1 << 13,
Circle = 1<<13, Cross = 1 << 14,
Cross = 1<<14, Square = 1 << 15
Square = 1<<15
}; };
enum class EDualshockMotor : uint8_t enum class EDualshockMotor : uint8_t {
{ None = 0,
None = 0, Right = 1 << 0,
Right = 1<<0, Left = 1 << 1,
Left = 1<<1,
}; };
ENABLE_BITWISE_ENUM(EDualshockMotor) ENABLE_BITWISE_ENUM(EDualshockMotor)
enum class EDualshockLED enum class EDualshockLED { LED_OFF = 0, LED_1 = 1 << 1, LED_2 = 1 << 2, LED_3 = 1 << 3, LED_4 = 1 << 4 };
{
LED_OFF = 0,
LED_1 = 1<<1,
LED_2 = 1<<2,
LED_3 = 1<<3,
LED_4 = 1<<4
};
ENABLE_BITWISE_ENUM(EDualshockLED) ENABLE_BITWISE_ENUM(EDualshockLED)
struct DualshockPadState struct DualshockPadState {
{ uint8_t m_reportType;
uint8_t m_reportType; uint8_t m_reserved1;
uint8_t m_reserved1; uint16_t m_buttonState;
uint16_t m_buttonState; uint8_t m_psButtonState;
uint8_t m_psButtonState; uint8_t m_reserved2;
uint8_t m_reserved2; uint8_t m_leftStick[2];
uint8_t m_leftStick[2]; uint8_t m_rightStick[2];
uint8_t m_rightStick[2]; uint8_t m_reserved3[4];
uint8_t m_reserved3[4]; uint8_t m_pressureUp;
uint8_t m_pressureUp; uint8_t m_pressureRight;
uint8_t m_pressureRight; uint8_t m_pressureDown;
uint8_t m_pressureDown; uint8_t m_pressureLeft;
uint8_t m_pressureLeft; uint8_t m_pressureL2;
uint8_t m_pressureL2; uint8_t m_pressureR2;
uint8_t m_pressureR2; uint8_t m_pressureL1;
uint8_t m_pressureL1; uint8_t m_pressureR1;
uint8_t m_pressureR1; uint8_t m_pressureTriangle;
uint8_t m_pressureTriangle; uint8_t m_pressureCircle;
uint8_t m_pressureCircle; uint8_t m_pressureCross;
uint8_t m_pressureCross; uint8_t m_pressureSquare;
uint8_t m_pressureSquare; uint8_t m_reserved4[3];
uint8_t m_reserved4[3]; uint8_t m_charge;
uint8_t m_charge; uint8_t m_power;
uint8_t m_power; uint8_t m_connection;
uint8_t m_connection; uint8_t m_reserved5[9];
uint8_t m_reserved5[9]; uint16_t m_accelerometer[3];
uint16_t m_accelerometer[3]; uint16_t m_gyrometerZ;
uint16_t m_gyrometerZ; // INTERNAL, set by libBoo, do not modify directly!
// INTERNAL, set by libBoo, do not modify directly! float accPitch;
float accPitch; float accYaw;
float accYaw; float gyroZ;
float gyroZ;
}; };
class DualshockPad; class DualshockPad;
struct IDualshockPadCallback struct IDualshockPadCallback {
{ virtual void controllerDisconnected() {}
virtual void controllerDisconnected() {} virtual void controllerUpdate(DualshockPad&, const DualshockPadState&) {}
virtual void controllerUpdate(DualshockPad&, const DualshockPadState&) {}
}; };
class DualshockPad final : public TDeviceBase<IDualshockPadCallback> class DualshockPad final : public TDeviceBase<IDualshockPadCallback> {
{ EDualshockMotor m_rumbleRequest;
EDualshockMotor m_rumbleRequest; EDualshockMotor m_rumbleState;
EDualshockMotor m_rumbleState; uint8_t m_rumbleDuration[2];
uint8_t m_rumbleDuration[2]; uint8_t m_rumbleIntensity[2];
uint8_t m_rumbleIntensity[2]; EDualshockLED m_led;
EDualshockLED m_led; DualshockOutReport m_report;
DualshockOutReport m_report; void deviceDisconnected();
void deviceDisconnected(); void initialCycle();
void initialCycle(); void transferCycle();
void transferCycle(); void finalCycle();
void finalCycle(); void receivedHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message);
void receivedHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message);
public: public:
DualshockPad(DeviceToken* token); DualshockPad(DeviceToken* token);
~DualshockPad(); ~DualshockPad();
void startRumble(EDualshockMotor motor, uint8_t duration = 254, uint8_t intensity=255) void startRumble(EDualshockMotor motor, uint8_t duration = 254, uint8_t intensity = 255) {
{ m_rumbleRequest |= motor;
m_rumbleRequest |= motor; if ((EDualshockMotor(motor) & EDualshockMotor::Left) != EDualshockMotor::None) {
if ((EDualshockMotor(motor) & EDualshockMotor::Left) != EDualshockMotor::None) m_rumbleDuration[0] = duration;
{ m_rumbleIntensity[0] = intensity;
m_rumbleDuration[0] = duration;
m_rumbleIntensity[0] = intensity;
}
if ((EDualshockMotor(motor) & EDualshockMotor::Right) != EDualshockMotor::None)
{
m_rumbleDuration[1] = duration;
m_rumbleIntensity[1] = intensity;
}
} }
if ((EDualshockMotor(motor) & EDualshockMotor::Right) != EDualshockMotor::None) {
void stopRumble(int motor) m_rumbleDuration[1] = duration;
{ m_rumbleIntensity[1] = intensity;
m_rumbleRequest &= ~EDualshockMotor(motor);
} }
}
EDualshockLED getLED() void stopRumble(int motor) { m_rumbleRequest &= ~EDualshockMotor(motor); }
{
return m_led;
}
void setLED(EDualshockLED led, bool on = true) EDualshockLED getLED() { return m_led; }
{
if (on)
m_led |= led;
else
m_led &= ~led;
setRawLED(int(led)); void setLED(EDualshockLED led, bool on = true) {
} if (on)
m_led |= led;
else
m_led &= ~led;
void setRawLED(int led) setRawLED(int(led));
{ }
m_report.leds = led;
sendHIDReport(m_report.buf, sizeof(m_report), HIDReportType::Output, 0x01); void setRawLED(int led) {
} m_report.leds = led;
sendHIDReport(m_report.buf, sizeof(m_report), HIDReportType::Output, 0x01);
}
}; };
} } // namespace boo

View File

@ -5,29 +5,26 @@
#include <map> #include <map>
#include <mutex> #include <mutex>
namespace boo namespace boo {
{
struct IGenericPadCallback struct IGenericPadCallback {
{ virtual void controllerConnected() {}
virtual void controllerConnected() {} virtual void controllerDisconnected() {}
virtual void controllerDisconnected() {} virtual void valueUpdate(const HIDMainItem& item, int32_t value) {}
virtual void valueUpdate(const HIDMainItem& item, int32_t value) {}
}; };
class GenericPad final : public TDeviceBase<IGenericPadCallback> class GenericPad final : public TDeviceBase<IGenericPadCallback> {
{ HIDParser m_parser;
HIDParser m_parser;
public: public:
GenericPad(DeviceToken* token); GenericPad(DeviceToken* token);
~GenericPad(); ~GenericPad();
void deviceDisconnected(); void deviceDisconnected();
void initialCycle(); void initialCycle();
void receivedHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message); void receivedHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message);
void enumerateValues(const std::function<bool(const HIDMainItem& item)>& valueCB) const; void enumerateValues(const std::function<bool(const HIDMainItem& item)>& valueCB) const;
}; };
} } // namespace boo

View File

@ -10,211 +10,197 @@
#include <hidsdi.h> #include <hidsdi.h>
#endif #endif
namespace boo namespace boo {
{
struct HIDItemState; struct HIDItemState;
struct HIDCollectionItem; struct HIDCollectionItem;
struct HIDReports; struct HIDReports;
enum class HIDUsagePage : uint8_t enum class HIDUsagePage : uint8_t {
{ Undefined = 0,
Undefined = 0, GenericDesktop = 1,
GenericDesktop = 1, Simulation = 2,
Simulation = 2, VR = 3,
VR = 3, Sport = 4,
Sport = 4, Game = 5,
Game = 5, GenericDevice = 6,
GenericDevice = 6, Keyboard = 7,
Keyboard = 7, LEDs = 8,
LEDs = 8, Button = 9,
Button = 9, Ordinal = 10,
Ordinal = 10, Telephony = 11,
Telephony = 11, Consumer = 12,
Consumer = 12, Digitizer = 13
Digitizer = 13
}; };
enum class HIDUsage : uint8_t enum class HIDUsage : uint8_t {
{ Undefined = 0,
Undefined = 0,
/* Generic Desktop */ /* Generic Desktop */
Pointer = 1, Pointer = 1,
Mouse = 2, Mouse = 2,
Reserved = 3, Reserved = 3,
Joystick = 4, Joystick = 4,
GamePad = 5, GamePad = 5,
Keyboard = 6, Keyboard = 6,
Keypad = 7, Keypad = 7,
MultiAxis = 8, MultiAxis = 8,
TabletPC = 9, TabletPC = 9,
X = 0x30, X = 0x30,
Y = 0x31, Y = 0x31,
Z = 0x32, Z = 0x32,
Rx = 0x33, Rx = 0x33,
Ry = 0x34, Ry = 0x34,
Rz = 0x35, Rz = 0x35,
Slider = 0x36, Slider = 0x36,
Dial = 0x37, Dial = 0x37,
Wheel = 0x38, Wheel = 0x38,
HatSwitch = 0x39, HatSwitch = 0x39,
CountedBuffer = 0x3a, CountedBuffer = 0x3a,
ByteCount = 0x3b, ByteCount = 0x3b,
MotionWakeup = 0x3c, MotionWakeup = 0x3c,
Start = 0x3d, Start = 0x3d,
Select = 0x3e, Select = 0x3e,
Vx = 0x40, Vx = 0x40,
Vy = 0x41, Vy = 0x41,
Vz = 0x42, Vz = 0x42,
Vbrx = 0x43, Vbrx = 0x43,
Vbry = 0x44, Vbry = 0x44,
Vbrz = 0x45, Vbrz = 0x45,
Vno = 0x46, Vno = 0x46,
FeatureNotification = 0x47, FeatureNotification = 0x47,
ResolutionMultiplier = 0x48, ResolutionMultiplier = 0x48,
SystemControl = 0x80, SystemControl = 0x80,
SystemPowerDown = 0x81, SystemPowerDown = 0x81,
SystemSleep = 0x82, SystemSleep = 0x82,
SystemWakeUp = 0x83, SystemWakeUp = 0x83,
SystemContextMenu = 0x84, SystemContextMenu = 0x84,
SystemMainMenu = 0x85, SystemMainMenu = 0x85,
SystemAppMenu = 0x86, SystemAppMenu = 0x86,
SystemMenuHelp = 0x87, SystemMenuHelp = 0x87,
SystemMenuExit = 0x88, SystemMenuExit = 0x88,
SystemMenuSelect = 0x89, SystemMenuSelect = 0x89,
SystemMenuRight = 0x8a, SystemMenuRight = 0x8a,
SystemMenuLeft = 0x8b, SystemMenuLeft = 0x8b,
SystemMenuUp = 0x8c, SystemMenuUp = 0x8c,
SystemMenuDown = 0x8d, SystemMenuDown = 0x8d,
SystemColdRestart = 0x8e, SystemColdRestart = 0x8e,
SystemWarmRestart = 0x8f, SystemWarmRestart = 0x8f,
DPadUp = 0x90, DPadUp = 0x90,
DPadDown = 0x91, DPadDown = 0x91,
DPadRight = 0x92, DPadRight = 0x92,
DPadLeft = 0x93, DPadLeft = 0x93,
SystemDock = 0xa0, SystemDock = 0xa0,
SystemUndock = 0xa1, SystemUndock = 0xa1,
SystemSetup = 0xa2, SystemSetup = 0xa2,
SystemBreak = 0xa3, SystemBreak = 0xa3,
SystemDebuggerBreak = 0xa4, SystemDebuggerBreak = 0xa4,
ApplicationBreak = 0xa5, ApplicationBreak = 0xa5,
ApplicationDebuggerBreak = 0xa6, ApplicationDebuggerBreak = 0xa6,
SystemSpeakerMute = 0xa7, SystemSpeakerMute = 0xa7,
SystemHibernate = 0xa8, SystemHibernate = 0xa8,
SystemDisplayInvert = 0xb0, SystemDisplayInvert = 0xb0,
SystemDisplayInternal = 0xb1, SystemDisplayInternal = 0xb1,
SystemDisplayExternal = 0xb2, SystemDisplayExternal = 0xb2,
SystemDisplayBoth = 0xb3, SystemDisplayBoth = 0xb3,
SystemDisplayDual = 0xb4, SystemDisplayDual = 0xb4,
SystemDisplayToggleIntExt = 0xb5, SystemDisplayToggleIntExt = 0xb5,
/* Game Controls */ /* Game Controls */
_3DGameController = 0x1, _3DGameController = 0x1,
PinballDevice = 0x2, PinballDevice = 0x2,
GunDevice = 0x3, GunDevice = 0x3,
PointOfView = 0x20, PointOfView = 0x20,
TurnLeftRight = 0x21, TurnLeftRight = 0x21,
PitchForwardBackward = 0x22, PitchForwardBackward = 0x22,
RollRightLeft = 0x23, RollRightLeft = 0x23,
MoveRightLeft = 0x24, MoveRightLeft = 0x24,
MoveForwardBackward = 0x25, MoveForwardBackward = 0x25,
MoveUpDown = 0x26, MoveUpDown = 0x26,
LeanLeftRight = 0x27, LeanLeftRight = 0x27,
LeanForwardBackward = 0x28, LeanForwardBackward = 0x28,
HeightOfPOV = 0x29, HeightOfPOV = 0x29,
Flipper = 0x2a, Flipper = 0x2a,
SecondaryFlipper = 0x2b, SecondaryFlipper = 0x2b,
Bump = 0x2c, Bump = 0x2c,
NewGame = 0x2d, NewGame = 0x2d,
ShootBall = 0x2e, ShootBall = 0x2e,
Player = 0x2f, Player = 0x2f,
GunBolt = 0x30, GunBolt = 0x30,
GunClip = 0x31, GunClip = 0x31,
GunSelector = 0x32, GunSelector = 0x32,
GunSingleShot = 0x33, GunSingleShot = 0x33,
GunBurst = 0x34, GunBurst = 0x34,
GunAutomatic = 0x35, GunAutomatic = 0x35,
GunSafety = 0x36, GunSafety = 0x36,
GamepadFireJump = 0x37, GamepadFireJump = 0x37,
GamepadTrigger = 0x39, GamepadTrigger = 0x39,
}; };
using HIDRange = std::pair<int32_t, int32_t>; using HIDRange = std::pair<int32_t, int32_t>;
/* [6.2.2.5] Input, Output, and Feature Items */ /* [6.2.2.5] Input, Output, and Feature Items */
struct HIDMainItem struct HIDMainItem {
{ uint16_t m_flags;
uint16_t m_flags; HIDUsagePage m_usagePage;
HIDUsagePage m_usagePage; HIDUsage m_usage;
HIDUsage m_usage; HIDRange m_logicalRange;
HIDRange m_logicalRange; int32_t m_reportSize;
int32_t m_reportSize; bool IsConstant() const { return (m_flags & 0x1) != 0; }
bool IsConstant() const { return (m_flags & 0x1) != 0; } bool IsVariable() const { return (m_flags & 0x2) != 0; }
bool IsVariable() const { return (m_flags & 0x2) != 0; } bool IsRelative() const { return (m_flags & 0x4) != 0; }
bool IsRelative() const { return (m_flags & 0x4) != 0; } bool IsWrap() const { return (m_flags & 0x8) != 0; }
bool IsWrap() const { return (m_flags & 0x8) != 0; } bool IsNonlinear() const { return (m_flags & 0x10) != 0; }
bool IsNonlinear() const { return (m_flags & 0x10) != 0; } bool IsNoPreferred() const { return (m_flags & 0x20) != 0; }
bool IsNoPreferred() const { return (m_flags & 0x20) != 0; } bool IsNullState() const { return (m_flags & 0x40) != 0; }
bool IsNullState() const { return (m_flags & 0x40) != 0; } bool IsVolatile() const { return (m_flags & 0x80) != 0; }
bool IsVolatile() const { return (m_flags & 0x80) != 0; } bool IsBufferedBytes() const { return (m_flags & 0x100) != 0; }
bool IsBufferedBytes() const { return (m_flags & 0x100) != 0; }
HIDMainItem() = default; HIDMainItem() = default;
HIDMainItem(uint32_t flags, const HIDItemState& state, uint32_t reportIdx); HIDMainItem(uint32_t flags, const HIDItemState& state, uint32_t reportIdx);
HIDMainItem(uint32_t flags, HIDUsagePage usagePage, HIDUsage usage, HIDMainItem(uint32_t flags, HIDUsagePage usagePage, HIDUsage usage, HIDRange logicalRange, int32_t reportSize);
HIDRange logicalRange, int32_t reportSize); const char* GetUsagePageName() const;
const char* GetUsagePageName() const; const char* GetUsageName() const;
const char* GetUsageName() const;
}; };
class HIDParser class HIDParser {
{
public: public:
enum class ParserStatus enum class ParserStatus { OK, Done, Error };
{
OK,
Done,
Error
};
private: private:
ParserStatus m_status = ParserStatus::OK;
ParserStatus m_status = ParserStatus::OK;
#if _WIN32 #if _WIN32
#if !WINDOWS_STORE #if !WINDOWS_STORE
std::vector<HIDMainItem> m_itemPool; std::vector<HIDMainItem> m_itemPool;
mutable std::vector<HIDP_DATA> m_dataList; mutable std::vector<HIDP_DATA> m_dataList;
PHIDP_PREPARSED_DATA m_descriptorData = nullptr; PHIDP_PREPARSED_DATA m_descriptorData = nullptr;
#endif #endif
#else #else
std::unique_ptr<HIDMainItem[]> m_itemPool; std::unique_ptr<HIDMainItem[]> m_itemPool;
using Report = std::pair<uint32_t, std::pair<uint32_t, uint32_t>>; using Report = std::pair<uint32_t, std::pair<uint32_t, uint32_t>>;
std::unique_ptr<Report[]> m_reportPool; std::unique_ptr<Report[]> m_reportPool;
std::pair<uint32_t, uint32_t> m_inputReports = {}; std::pair<uint32_t, uint32_t> m_inputReports = {};
std::pair<uint32_t, uint32_t> m_outputReports = {}; std::pair<uint32_t, uint32_t> m_outputReports = {};
std::pair<uint32_t, uint32_t> m_featureReports = {}; std::pair<uint32_t, uint32_t> m_featureReports = {};
bool m_multipleReports = false; bool m_multipleReports = false;
static ParserStatus ParseItem(HIDReports& reportsOut, static ParserStatus ParseItem(HIDReports& reportsOut, std::stack<HIDItemState>& stateStack,
std::stack<HIDItemState>& stateStack, std::stack<HIDCollectionItem>& collectionStack, const uint8_t*& it, const uint8_t* end,
std::stack<HIDCollectionItem>& collectionStack, bool& multipleReports);
const uint8_t*& it, const uint8_t* end,
bool& multipleReports);
#endif #endif
public: public:
#if _WIN32 #if _WIN32
#if !WINDOWS_STORE #if !WINDOWS_STORE
ParserStatus Parse(const PHIDP_PREPARSED_DATA descriptorData); ParserStatus Parse(const PHIDP_PREPARSED_DATA descriptorData);
#endif #endif
#else #else
ParserStatus Parse(const uint8_t* descriptorData, size_t len); ParserStatus Parse(const uint8_t* descriptorData, size_t len);
static size_t CalculateMaxInputReportSize(const uint8_t* descriptorData, size_t len); static size_t CalculateMaxInputReportSize(const uint8_t* descriptorData, size_t len);
static std::pair<HIDUsagePage, HIDUsage> GetApplicationUsage(const uint8_t* descriptorData, size_t len); static std::pair<HIDUsagePage, HIDUsage> GetApplicationUsage(const uint8_t* descriptorData, size_t len);
#endif #endif
operator bool() const { return m_status == ParserStatus::Done; } operator bool() const { return m_status == ParserStatus::Done; }
void EnumerateValues(const std::function<bool(const HIDMainItem& item)>& valueCB) const; void EnumerateValues(const std::function<bool(const HIDMainItem& item)>& valueCB) const;
void ScanValues(const std::function<bool(const HIDMainItem& item, int32_t value)>& valueCB, void ScanValues(const std::function<bool(const HIDMainItem& item, int32_t value)>& valueCB, const uint8_t* data,
const uint8_t* data, size_t len) const; size_t len) const;
}; };
} } // namespace boo

View File

@ -4,35 +4,31 @@
#include <mutex> #include <mutex>
#include "DeviceToken.hpp" #include "DeviceToken.hpp"
namespace boo namespace boo {
{
typedef std::unordered_map<std::string, std::unique_ptr<DeviceToken>> TDeviceTokens; typedef std::unordered_map<std::string, std::unique_ptr<DeviceToken>> TDeviceTokens;
typedef std::pair<TDeviceTokens::iterator, bool> TInsertedDeviceToken; typedef std::pair<TDeviceTokens::iterator, bool> TInsertedDeviceToken;
class DeviceFinder; class DeviceFinder;
class IHIDListener class IHIDListener {
{
public: public:
virtual ~IHIDListener() = default; virtual ~IHIDListener() = default;
/* Automatic device scanning */ /* Automatic device scanning */
virtual bool startScanning()=0; virtual bool startScanning() = 0;
virtual bool stopScanning()=0; virtual bool stopScanning() = 0;
/* Manual device scanning */ /* Manual device scanning */
virtual bool scanNow()=0; virtual bool scanNow() = 0;
#if _WIN32 && !WINDOWS_STORE #if _WIN32 && !WINDOWS_STORE
/* External listener implementation (for Windows) */ /* External listener implementation (for Windows) */
virtual bool _extDevConnect(const char* path)=0; virtual bool _extDevConnect(const char* path) = 0;
virtual bool _extDevDisconnect(const char* path)=0; virtual bool _extDevDisconnect(const char* path) = 0;
#endif #endif
}; };
/* Platform-specific constructor */ /* Platform-specific constructor */
std::unique_ptr<IHIDListener> IHIDListenerNew(DeviceFinder& finder); std::unique_ptr<IHIDListener> IHIDListenerNew(DeviceFinder& finder);
} } // namespace boo

View File

@ -2,51 +2,47 @@
#include "DeviceBase.hpp" #include "DeviceBase.hpp"
#include "boo/System.hpp" #include "boo/System.hpp"
namespace boo namespace boo {
{ struct NintendoPowerAState {
struct NintendoPowerAState uint8_t y : 1;
{ uint8_t b : 1;
uint8_t y : 1; uint8_t a : 1;
uint8_t b : 1; uint8_t x : 1;
uint8_t a : 1; uint8_t l : 1;
uint8_t x : 1; uint8_t r : 1;
uint8_t l : 1; uint8_t zl : 1;
uint8_t r : 1; uint8_t zr : 1;
uint8_t zl : 1; uint8_t minus : 1;
uint8_t zr : 1; uint8_t plus : 1;
uint8_t minus : 1; uint8_t stickL : 1;
uint8_t plus : 1; uint8_t stickR : 1;
uint8_t stickL : 1; uint8_t home : 1;
uint8_t stickR : 1; uint8_t capture : 1;
uint8_t home : 1; uint8_t dPad;
uint8_t capture : 1; uint8_t leftX;
uint8_t dPad; uint8_t leftY;
uint8_t leftX; uint8_t rightX;
uint8_t leftY; uint8_t rightY;
uint8_t rightX; bool operator==(const NintendoPowerAState& other);
uint8_t rightY; bool operator!=(const NintendoPowerAState& other);
bool operator==(const NintendoPowerAState& other);
bool operator!=(const NintendoPowerAState& other);
}; };
class NintendoPowerA; class NintendoPowerA;
struct INintendoPowerACallback struct INintendoPowerACallback {
{ virtual void controllerDisconnected() {}
virtual void controllerDisconnected() {} virtual void controllerUpdate(const NintendoPowerAState& state) {}
virtual void controllerUpdate(const NintendoPowerAState& state) {}
}; };
class NintendoPowerA final : public TDeviceBase<INintendoPowerACallback> class NintendoPowerA final : public TDeviceBase<INintendoPowerACallback> {
{ NintendoPowerAState m_last;
NintendoPowerAState m_last; void deviceDisconnected();
void deviceDisconnected(); void initialCycle();
void initialCycle(); void transferCycle();
void transferCycle(); void finalCycle();
void finalCycle(); void receivedHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message);
void receivedHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message);
public: public:
NintendoPowerA(DeviceToken*); NintendoPowerA(DeviceToken*);
~NintendoPowerA(); ~NintendoPowerA();
}; };
} } // namespace boo

View File

@ -1,7 +1,3 @@
#pragma once #pragma once
namespace boo namespace boo {}
{
}

View File

@ -4,63 +4,55 @@
#include "DeviceSignature.hpp" #include "DeviceSignature.hpp"
#include "boo/System.hpp" #include "boo/System.hpp"
namespace boo namespace boo {
{
struct XInputPadState struct XInputPadState {
{ uint16_t wButtons;
uint16_t wButtons; uint8_t bLeftTrigger;
uint8_t bLeftTrigger; uint8_t bRightTrigger;
uint8_t bRightTrigger; int16_t sThumbLX;
int16_t sThumbLX; int16_t sThumbLY;
int16_t sThumbLY; int16_t sThumbRX;
int16_t sThumbRX; int16_t sThumbRY;
int16_t sThumbRY;
}; };
enum class EXInputMotor : uint8_t enum class EXInputMotor : uint8_t {
{ None = 0,
None = 0, Right = 1 << 0,
Right = 1<<0, Left = 1 << 1,
Left = 1<<1,
}; };
ENABLE_BITWISE_ENUM(EXInputMotor) ENABLE_BITWISE_ENUM(EXInputMotor)
class XInputPad; class XInputPad;
struct IXInputPadCallback struct IXInputPadCallback {
{ virtual void controllerDisconnected() {}
virtual void controllerDisconnected() {} virtual void controllerUpdate(XInputPad& pad, const XInputPadState&) {}
virtual void controllerUpdate(XInputPad& pad, const XInputPadState&) {}
}; };
class XInputPad final : public TDeviceBase<IXInputPadCallback> class XInputPad final : public TDeviceBase<IXInputPadCallback> {
{ friend class HIDListenerWinUSB;
friend class HIDListenerWinUSB; uint16_t m_rumbleRequest[2] = {};
uint16_t m_rumbleRequest[2] = {}; uint16_t m_rumbleState[2] = {};
uint16_t m_rumbleState[2] = {};
public: public:
XInputPad(DeviceToken* token) : TDeviceBase<IXInputPadCallback>(dev_typeid(XInputPad), token) {} XInputPad(DeviceToken* token) : TDeviceBase<IXInputPadCallback>(dev_typeid(XInputPad), token) {}
void deviceDisconnected() void deviceDisconnected() {
{ std::lock_guard<std::mutex> lk(m_callbackLock);
std::lock_guard<std::mutex> lk(m_callbackLock); if (m_callback)
if (m_callback) m_callback->controllerDisconnected();
m_callback->controllerDisconnected(); }
} void startRumble(EXInputMotor motors, uint16_t intensity) {
void startRumble(EXInputMotor motors, uint16_t intensity) if ((motors & EXInputMotor::Left) != EXInputMotor::None)
{ m_rumbleRequest[0] = intensity;
if ((motors & EXInputMotor::Left) != EXInputMotor::None) if ((motors & EXInputMotor::Right) != EXInputMotor::None)
m_rumbleRequest[0] = intensity; m_rumbleRequest[1] = intensity;
if ((motors & EXInputMotor::Right) != EXInputMotor::None) }
m_rumbleRequest[1] = intensity; void stopRumble(EXInputMotor motors) {
} if ((motors & EXInputMotor::Left) != EXInputMotor::None)
void stopRumble(EXInputMotor motors) m_rumbleRequest[0] = 0;
{ if ((motors & EXInputMotor::Right) != EXInputMotor::None)
if ((motors & EXInputMotor::Left) != EXInputMotor::None) m_rumbleRequest[1] = 0;
m_rumbleRequest[0] = 0; }
if ((motors & EXInputMotor::Right) != EXInputMotor::None)
m_rumbleRequest[1] = 0;
}
}; };
} } // namespace boo

View File

@ -5,151 +5,127 @@
#include <utility> #include <utility>
/// A smart pointer that can manage the lifecycle of Core Foundation objects. /// A smart pointer that can manage the lifecycle of Core Foundation objects.
template<typename T> template <typename T>
class CFPointer { class CFPointer {
public: public:
CFPointer() : storage(nullptr) { } CFPointer() : storage(nullptr) {}
CFPointer(T pointer) : storage(toStorageType(pointer)) { CFPointer(T pointer) : storage(toStorageType(pointer)) {
if (storage) { if (storage) {
CFRetain(storage); CFRetain(storage);
}
} }
}
CFPointer(const CFPointer & other) : storage(other.storage) { CFPointer(const CFPointer& other) : storage(other.storage) {
if (CFTypeRef ptr = storage) { if (CFTypeRef ptr = storage) {
CFRetain(ptr); CFRetain(ptr);
}
} }
}
CFPointer(CFPointer && other) : storage(std::exchange(other.storage, nullptr)) { } CFPointer(CFPointer&& other) : storage(std::exchange(other.storage, nullptr)) {}
~CFPointer() { ~CFPointer() {
if (CFTypeRef pointer = storage) { if (CFTypeRef pointer = storage) {
CFRelease(pointer); CFRelease(pointer);
}
} }
}
static inline CFPointer<T> adopt(T CF_RELEASES_ARGUMENT ptr) { static inline CFPointer<T> adopt(T CF_RELEASES_ARGUMENT ptr) { return CFPointer<T>(ptr, CFPointer<T>::Adopt); }
return CFPointer<T>(ptr, CFPointer<T>::Adopt);
}
T get() const { T get() const { return fromStorageType(storage); }
return fromStorageType(storage);
}
CFPointer &operator=(CFPointer other) { CFPointer& operator=(CFPointer other) {
swap(other); swap(other);
return *this; return *this;
} }
T* operator&() T* operator&() {
{ if (CFTypeRef pointer = storage) {
if (CFTypeRef pointer = storage) { CFRelease(pointer);
CFRelease(pointer);
}
return (T*)&storage;
} }
operator bool() const { return storage != nullptr; } return (T*)&storage;
}
operator bool() const { return storage != nullptr; }
void reset() void reset() {
{ if (storage) {
if (storage) CFRelease(storage);
{ storage = nullptr;
CFRelease(storage);
storage = nullptr;
}
} }
}
private: private:
CFTypeRef storage; CFTypeRef storage;
enum AdoptTag { Adopt }; enum AdoptTag { Adopt };
CFPointer(T ptr, AdoptTag) : storage(toStorageType(ptr)) { } CFPointer(T ptr, AdoptTag) : storage(toStorageType(ptr)) {}
inline CFTypeRef toStorageType(CFTypeRef ptr) const { inline CFTypeRef toStorageType(CFTypeRef ptr) const { return (CFTypeRef)ptr; }
return (CFTypeRef)ptr;
}
inline T fromStorageType(CFTypeRef pointer) const { inline T fromStorageType(CFTypeRef pointer) const { return (T)pointer; }
return (T)pointer;
}
void swap(CFPointer &other) { void swap(CFPointer& other) { std::swap(storage, other.storage); }
std::swap(storage, other.storage);
}
}; };
/// A smart pointer that can manage the lifecycle of CoreFoundation IUnknown objects. /// A smart pointer that can manage the lifecycle of CoreFoundation IUnknown objects.
template<typename T> template <typename T>
class IUnknownPointer { class IUnknownPointer {
public: public:
IUnknownPointer() : _storage(nullptr) { } IUnknownPointer() : _storage(nullptr) {}
IUnknownPointer(T** pointer) : _storage(toStorageType(pointer)) { IUnknownPointer(T** pointer) : _storage(toStorageType(pointer)) {
if (_storage) { if (_storage) {
(*pointer)->AddRef(pointer); (*pointer)->AddRef(pointer);
}
} }
}
IUnknownPointer(const IUnknownPointer & other) : _storage(other._storage) { IUnknownPointer(const IUnknownPointer& other) : _storage(other._storage) {
if (IUnknownVTbl** ptr = _storage) { if (IUnknownVTbl** ptr = _storage) {
(*ptr)->AddRef(ptr); (*ptr)->AddRef(ptr);
}
} }
IUnknownPointer& operator=(const IUnknownPointer & other) { }
if (IUnknownVTbl** pointer = _storage) { IUnknownPointer& operator=(const IUnknownPointer& other) {
(*pointer)->Release(pointer); if (IUnknownVTbl** pointer = _storage) {
} (*pointer)->Release(pointer);
_storage = other._storage;
if (IUnknownVTbl** ptr = _storage) {
(*ptr)->AddRef(ptr);
}
return *this;
} }
_storage = other._storage;
if (IUnknownVTbl** ptr = _storage) {
(*ptr)->AddRef(ptr);
}
return *this;
}
IUnknownPointer(IUnknownPointer && other) : _storage(std::exchange(other._storage, nullptr)) { } IUnknownPointer(IUnknownPointer&& other) : _storage(std::exchange(other._storage, nullptr)) {}
~IUnknownPointer() { ~IUnknownPointer() {
if (IUnknownVTbl** pointer = _storage) { if (IUnknownVTbl** pointer = _storage) {
(*pointer)->Release(pointer); (*pointer)->Release(pointer);
}
} }
}
static inline IUnknownPointer<T> adopt(T** ptr) { static inline IUnknownPointer<T> adopt(T** ptr) { return IUnknownPointer<T>(ptr, IUnknownPointer<T>::Adopt); }
return IUnknownPointer<T>(ptr, IUnknownPointer<T>::Adopt);
}
T* get() const { T* get() const { return fromStorageType(_storage); }
return fromStorageType(_storage);
}
T* operator->() const { return get(); } T* operator->() const { return get(); }
T** storage() const { return (T**)_storage; } T** storage() const { return (T**)_storage; }
LPVOID* operator&() { LPVOID* operator&() {
if (IUnknownVTbl** pointer = _storage) { if (IUnknownVTbl** pointer = _storage) {
printf("%p RELEASE %d\n", pointer, (*pointer)->Release(pointer)); printf("%p RELEASE %d\n", pointer, (*pointer)->Release(pointer));
}
return (LPVOID*)&_storage;
} }
operator bool() const { return _storage != nullptr; } return (LPVOID*)&_storage;
}
operator bool() const { return _storage != nullptr; }
private: private:
IUnknownVTbl** _storage; IUnknownVTbl** _storage;
enum AdoptTag { Adopt }; enum AdoptTag { Adopt };
IUnknownPointer(T** ptr, AdoptTag) : _storage(toStorageType(ptr)) { } IUnknownPointer(T** ptr, AdoptTag) : _storage(toStorageType(ptr)) {}
inline IUnknownVTbl** toStorageType(T** ptr) const { inline IUnknownVTbl** toStorageType(T** ptr) const { return (IUnknownVTbl**)ptr; }
return (IUnknownVTbl**)ptr;
}
inline T* fromStorageType(IUnknownVTbl** pointer) const { inline T* fromStorageType(IUnknownVTbl** pointer) const { return *(T**)pointer; }
return *(T**)pointer;
}
void swap(IUnknownPointer &other) { void swap(IUnknownPointer& other) { std::swap(_storage, other._storage); }
std::swap(_storage, other._storage);
}
}; };

View File

@ -3,26 +3,31 @@
#include "boo/BooObject.hpp" #include "boo/BooObject.hpp"
#include <iterator> #include <iterator>
namespace boo namespace boo {
{
/** Linked-list iterator shareable by ListNode types. */ /** Linked-list iterator shareable by ListNode types. */
template <class T> template <class T>
class ListIterator class ListIterator {
{ T* m_node;
T* m_node;
public:
using iterator_category = std::bidirectional_iterator_tag;
using value_type = T;
using difference_type = std::ptrdiff_t;
using pointer = T*;
using reference = T&;
explicit ListIterator(T* node) : m_node(node) {} public:
T& operator*() const { return *m_node; } using iterator_category = std::bidirectional_iterator_tag;
bool operator!=(const ListIterator& other) const { return m_node != other.m_node; } using value_type = T;
ListIterator& operator++() { m_node = m_node->m_next; return *this; } using difference_type = std::ptrdiff_t;
ListIterator& operator--() { m_node = m_node->m_prev; return *this; } using pointer = T*;
using reference = T&;
explicit ListIterator(T* node) : m_node(node) {}
T& operator*() const { return *m_node; }
bool operator!=(const ListIterator& other) const { return m_node != other.m_node; }
ListIterator& operator++() {
m_node = m_node->m_next;
return *this;
}
ListIterator& operator--() {
m_node = m_node->m_prev;
return *this;
}
}; };
/** Linked-list IObj node made part of objects participating in list. /** Linked-list IObj node made part of objects participating in list.
@ -30,50 +35,43 @@ public:
* to support the common list-management functionality. * to support the common list-management functionality.
*/ */
template <class N, class H, class P = IObj> template <class N, class H, class P = IObj>
struct ListNode : P struct ListNode : P {
{ using iterator = ListIterator<N>;
using iterator = ListIterator<N>; iterator begin() { return iterator(static_cast<N*>(this)); }
iterator begin() { return iterator(static_cast<N*>(this)); } iterator end() { return iterator(nullptr); }
iterator end() { return iterator(nullptr); }
H m_head;
N* m_next;
N* m_prev = nullptr;
ListNode(H head) : m_head(head) {
auto lk = N::_getHeadLock(head);
m_next = N::_getHeadPtr(head);
if (m_next)
m_next->m_prev = static_cast<N*>(this);
N::_getHeadPtr(head) = static_cast<N*>(this);
}
H m_head;
N* m_next;
N* m_prev = nullptr;
ListNode(H head) : m_head(head)
{
auto lk = N::_getHeadLock(head);
m_next = N::_getHeadPtr(head);
if (m_next)
m_next->m_prev = static_cast<N*>(this);
N::_getHeadPtr(head) = static_cast<N*>(this);
}
protected: protected:
~ListNode() ~ListNode() {
{ if (m_prev) {
if (m_prev) if (m_next)
{ m_next->m_prev = m_prev;
if (m_next) m_prev->m_next = m_next;
m_next->m_prev = m_prev; } else {
m_prev->m_next = m_next; if (m_next)
} m_next->m_prev = nullptr;
else N::_getHeadPtr(m_head) = m_next;
{
if (m_next)
m_next->m_prev = nullptr;
N::_getHeadPtr(m_head) = m_next;
}
} }
}
}; };
static inline uint32_t flp2(uint32_t x) static inline uint32_t flp2(uint32_t x) {
{ x = x | (x >> 1);
x = x | (x >> 1); x = x | (x >> 2);
x = x | (x >> 2); x = x | (x >> 4);
x = x | (x >> 4); x = x | (x >> 8);
x = x | (x >> 8); x = x | (x >> 16);
x = x | (x >> 16); return x - (x >> 1);
return x - (x >> 1);
}
} }
} // namespace boo

File diff suppressed because it is too large Load Diff

View File

@ -2,295 +2,228 @@
#include "AudioVoiceEngine.hpp" #include "AudioVoiceEngine.hpp"
#include <cstring> #include <cstring>
namespace boo namespace boo {
{
void AudioMatrixMono::setDefaultMatrixCoefficients(AudioChannelSet acSet) void AudioMatrixMono::setDefaultMatrixCoefficients(AudioChannelSet acSet) {
{ m_curSlewFrame = 0;
m_curSlewFrame = 0; m_slewFrames = 0;
m_slewFrames = 0; memset(&m_coefs, 0, sizeof(m_coefs));
memset(&m_coefs, 0, sizeof(m_coefs)); switch (acSet) {
switch (acSet) case AudioChannelSet::Stereo:
{ case AudioChannelSet::Quad:
case AudioChannelSet::Stereo: m_coefs.v[int(AudioChannel::FrontLeft)] = 1.0;
case AudioChannelSet::Quad: m_coefs.v[int(AudioChannel::FrontRight)] = 1.0;
m_coefs.v[int(AudioChannel::FrontLeft)] = 1.0; break;
m_coefs.v[int(AudioChannel::FrontRight)] = 1.0; case AudioChannelSet::Surround51:
break; case AudioChannelSet::Surround71:
case AudioChannelSet::Surround51: m_coefs.v[int(AudioChannel::FrontCenter)] = 1.0;
case AudioChannelSet::Surround71: break;
m_coefs.v[int(AudioChannel::FrontCenter)] = 1.0; default:
break; break;
default: break; }
}
int16_t* AudioMatrixMono::mixMonoSampleData(const AudioVoiceEngineMixInfo& info, const int16_t* dataIn,
int16_t* dataOut, size_t samples) {
const ChannelMap& chmap = info.m_channelMap;
for (size_t s = 0; s < samples; ++s, ++dataIn) {
if (m_slewFrames && m_curSlewFrame < m_slewFrames) {
double t = m_curSlewFrame / double(m_slewFrames);
double omt = 1.0 - t;
for (unsigned c = 0; c < chmap.m_channelCount; ++c) {
AudioChannel ch = chmap.m_channels[c];
if (ch != AudioChannel::Unknown) {
*dataOut = Clamp16(*dataOut + *dataIn * (m_coefs.v[int(ch)] * t + m_oldCoefs.v[int(ch)] * omt));
++dataOut;
}
}
++m_curSlewFrame;
} else {
for (unsigned c = 0; c < chmap.m_channelCount; ++c) {
AudioChannel ch = chmap.m_channels[c];
if (ch != AudioChannel::Unknown) {
*dataOut = Clamp16(*dataOut + *dataIn * m_coefs.v[int(ch)]);
++dataOut;
}
}
} }
}
return dataOut;
} }
int16_t* AudioMatrixMono::mixMonoSampleData(const AudioVoiceEngineMixInfo& info, int32_t* AudioMatrixMono::mixMonoSampleData(const AudioVoiceEngineMixInfo& info, const int32_t* dataIn,
const int16_t* dataIn, int16_t* dataOut, size_t samples) int32_t* dataOut, size_t samples) {
{ const ChannelMap& chmap = info.m_channelMap;
const ChannelMap& chmap = info.m_channelMap; for (size_t s = 0; s < samples; ++s, ++dataIn) {
for (size_t s=0 ; s<samples ; ++s, ++dataIn) if (m_slewFrames && m_curSlewFrame < m_slewFrames) {
{ double t = m_curSlewFrame / double(m_slewFrames);
if (m_slewFrames && m_curSlewFrame < m_slewFrames) double omt = 1.0 - t;
{
double t = m_curSlewFrame / double(m_slewFrames);
double omt = 1.0 - t;
for (unsigned c=0 ; c<chmap.m_channelCount ; ++c) for (unsigned c = 0; c < chmap.m_channelCount; ++c) {
{ AudioChannel ch = chmap.m_channels[c];
AudioChannel ch = chmap.m_channels[c]; if (ch != AudioChannel::Unknown) {
if (ch != AudioChannel::Unknown) *dataOut = Clamp32(*dataOut + *dataIn * (m_coefs.v[int(ch)] * t + m_oldCoefs.v[int(ch)] * omt));
{ ++dataOut;
*dataOut = Clamp16(*dataOut + *dataIn * (m_coefs.v[int(ch)] * t + m_oldCoefs.v[int(ch)] * omt)); }
++dataOut; }
}
}
++m_curSlewFrame; ++m_curSlewFrame;
} } else {
else for (unsigned c = 0; c < chmap.m_channelCount; ++c) {
{ AudioChannel ch = chmap.m_channels[c];
for (unsigned c=0 ; c<chmap.m_channelCount ; ++c) if (ch != AudioChannel::Unknown) {
{ *dataOut = Clamp32(*dataOut + *dataIn * m_coefs.v[int(ch)]);
AudioChannel ch = chmap.m_channels[c]; ++dataOut;
if (ch != AudioChannel::Unknown)
{
*dataOut = Clamp16(*dataOut + *dataIn * m_coefs.v[int(ch)]);
++dataOut;
}
}
} }
}
} }
return dataOut; }
return dataOut;
} }
int32_t* AudioMatrixMono::mixMonoSampleData(const AudioVoiceEngineMixInfo& info, float* AudioMatrixMono::mixMonoSampleData(const AudioVoiceEngineMixInfo& info, const float* dataIn, float* dataOut,
const int32_t* dataIn, int32_t* dataOut, size_t samples) size_t samples) {
{ const ChannelMap& chmap = info.m_channelMap;
const ChannelMap& chmap = info.m_channelMap; for (size_t s = 0; s < samples; ++s, ++dataIn) {
for (size_t s=0 ; s<samples ; ++s, ++dataIn) if (m_slewFrames && m_curSlewFrame < m_slewFrames) {
{ double t = m_curSlewFrame / double(m_slewFrames);
if (m_slewFrames && m_curSlewFrame < m_slewFrames) double omt = 1.0 - t;
{
double t = m_curSlewFrame / double(m_slewFrames);
double omt = 1.0 - t;
for (unsigned c=0 ; c<chmap.m_channelCount ; ++c) for (unsigned c = 0; c < chmap.m_channelCount; ++c) {
{ AudioChannel ch = chmap.m_channels[c];
AudioChannel ch = chmap.m_channels[c]; if (ch != AudioChannel::Unknown) {
if (ch != AudioChannel::Unknown) *dataOut = *dataOut + *dataIn * (m_coefs.v[int(ch)] * t + m_oldCoefs.v[int(ch)] * omt);
{ ++dataOut;
*dataOut = Clamp32(*dataOut + *dataIn * (m_coefs.v[int(ch)] * t + m_oldCoefs.v[int(ch)] * omt)); }
++dataOut; }
}
}
++m_curSlewFrame; ++m_curSlewFrame;
} } else {
else for (unsigned c = 0; c < chmap.m_channelCount; ++c) {
{ AudioChannel ch = chmap.m_channels[c];
for (unsigned c=0 ; c<chmap.m_channelCount ; ++c) if (ch != AudioChannel::Unknown) {
{ *dataOut = *dataOut + *dataIn * m_coefs.v[int(ch)];
AudioChannel ch = chmap.m_channels[c]; ++dataOut;
if (ch != AudioChannel::Unknown)
{
*dataOut = Clamp32(*dataOut + *dataIn * m_coefs.v[int(ch)]);
++dataOut;
}
}
} }
}
} }
return dataOut; }
return dataOut;
} }
float* AudioMatrixMono::mixMonoSampleData(const AudioVoiceEngineMixInfo& info, void AudioMatrixStereo::setDefaultMatrixCoefficients(AudioChannelSet acSet) {
const float* dataIn, float* dataOut, size_t samples) m_curSlewFrame = 0;
{ m_slewFrames = 0;
const ChannelMap& chmap = info.m_channelMap; memset(&m_coefs, 0, sizeof(m_coefs));
for (size_t s=0 ; s<samples ; ++s, ++dataIn) switch (acSet) {
{ case AudioChannelSet::Stereo:
if (m_slewFrames && m_curSlewFrame < m_slewFrames) case AudioChannelSet::Quad:
{ m_coefs.v[int(AudioChannel::FrontLeft)][0] = 1.0;
double t = m_curSlewFrame / double(m_slewFrames); m_coefs.v[int(AudioChannel::FrontRight)][1] = 1.0;
double omt = 1.0 - t; break;
case AudioChannelSet::Surround51:
case AudioChannelSet::Surround71:
m_coefs.v[int(AudioChannel::FrontLeft)][0] = 1.0;
m_coefs.v[int(AudioChannel::FrontRight)][1] = 1.0;
break;
default:
break;
}
}
for (unsigned c=0 ; c<chmap.m_channelCount ; ++c) int16_t* AudioMatrixStereo::mixStereoSampleData(const AudioVoiceEngineMixInfo& info, const int16_t* dataIn,
{ int16_t* dataOut, size_t frames) {
AudioChannel ch = chmap.m_channels[c]; const ChannelMap& chmap = info.m_channelMap;
if (ch != AudioChannel::Unknown) for (size_t f = 0; f < frames; ++f, dataIn += 2) {
{ if (m_slewFrames && m_curSlewFrame < m_slewFrames) {
*dataOut = *dataOut + *dataIn * (m_coefs.v[int(ch)] * t + m_oldCoefs.v[int(ch)] * omt); double t = m_curSlewFrame / double(m_slewFrames);
++dataOut; double omt = 1.0 - t;
}
}
++m_curSlewFrame; for (unsigned c = 0; c < chmap.m_channelCount; ++c) {
AudioChannel ch = chmap.m_channels[c];
if (ch != AudioChannel::Unknown) {
*dataOut = Clamp16(*dataOut + *dataIn * (m_coefs.v[int(ch)][0] * t + m_oldCoefs.v[int(ch)][0] * omt) +
*dataIn * (m_coefs.v[int(ch)][1] * t + m_oldCoefs.v[int(ch)][1] * omt));
++dataOut;
} }
else }
{
for (unsigned c=0 ; c<chmap.m_channelCount ; ++c) ++m_curSlewFrame;
{ } else {
AudioChannel ch = chmap.m_channels[c]; for (unsigned c = 0; c < chmap.m_channelCount; ++c) {
if (ch != AudioChannel::Unknown) AudioChannel ch = chmap.m_channels[c];
{ if (ch != AudioChannel::Unknown) {
*dataOut = *dataOut + *dataIn * m_coefs.v[int(ch)]; *dataOut = Clamp16(*dataOut + dataIn[0] * m_coefs.v[int(ch)][0] + dataIn[1] * m_coefs.v[int(ch)][1]);
++dataOut; ++dataOut;
}
}
} }
}
} }
return dataOut; }
return dataOut;
} }
void AudioMatrixStereo::setDefaultMatrixCoefficients(AudioChannelSet acSet) int32_t* AudioMatrixStereo::mixStereoSampleData(const AudioVoiceEngineMixInfo& info, const int32_t* dataIn,
{ int32_t* dataOut, size_t frames) {
m_curSlewFrame = 0; const ChannelMap& chmap = info.m_channelMap;
m_slewFrames = 0; for (size_t f = 0; f < frames; ++f, dataIn += 2) {
memset(&m_coefs, 0, sizeof(m_coefs)); if (m_slewFrames && m_curSlewFrame < m_slewFrames) {
switch (acSet) double t = m_curSlewFrame / double(m_slewFrames);
{ double omt = 1.0 - t;
case AudioChannelSet::Stereo:
case AudioChannelSet::Quad: for (unsigned c = 0; c < chmap.m_channelCount; ++c) {
m_coefs.v[int(AudioChannel::FrontLeft)][0] = 1.0; AudioChannel ch = chmap.m_channels[c];
m_coefs.v[int(AudioChannel::FrontRight)][1] = 1.0; if (ch != AudioChannel::Unknown) {
break; *dataOut = Clamp32(*dataOut + *dataIn * (m_coefs.v[int(ch)][0] * t + m_oldCoefs.v[int(ch)][0] * omt) +
case AudioChannelSet::Surround51: *dataIn * (m_coefs.v[int(ch)][1] * t + m_oldCoefs.v[int(ch)][1] * omt));
case AudioChannelSet::Surround71: ++dataOut;
m_coefs.v[int(AudioChannel::FrontLeft)][0] = 1.0; }
m_coefs.v[int(AudioChannel::FrontRight)][1] = 1.0; }
break;
default: break; ++m_curSlewFrame;
} else {
for (unsigned c = 0; c < chmap.m_channelCount; ++c) {
AudioChannel ch = chmap.m_channels[c];
if (ch != AudioChannel::Unknown) {
*dataOut = Clamp32(*dataOut + dataIn[0] * m_coefs.v[int(ch)][0] + dataIn[1] * m_coefs.v[int(ch)][1]);
++dataOut;
}
}
} }
}
return dataOut;
} }
int16_t* AudioMatrixStereo::mixStereoSampleData(const AudioVoiceEngineMixInfo& info, float* AudioMatrixStereo::mixStereoSampleData(const AudioVoiceEngineMixInfo& info, const float* dataIn, float* dataOut,
const int16_t* dataIn, int16_t* dataOut, size_t frames) size_t frames) {
{ const ChannelMap& chmap = info.m_channelMap;
const ChannelMap& chmap = info.m_channelMap; for (size_t f = 0; f < frames; ++f, dataIn += 2) {
for (size_t f=0 ; f<frames ; ++f, dataIn += 2) if (m_slewFrames && m_curSlewFrame < m_slewFrames) {
{ double t = m_curSlewFrame / double(m_slewFrames);
if (m_slewFrames && m_curSlewFrame < m_slewFrames) double omt = 1.0 - t;
{
double t = m_curSlewFrame / double(m_slewFrames);
double omt = 1.0 - t;
for (unsigned c=0 ; c<chmap.m_channelCount ; ++c) for (unsigned c = 0; c < chmap.m_channelCount; ++c) {
{ AudioChannel ch = chmap.m_channels[c];
AudioChannel ch = chmap.m_channels[c]; if (ch != AudioChannel::Unknown) {
if (ch != AudioChannel::Unknown) *dataOut = *dataOut + *dataIn * (m_coefs.v[int(ch)][0] * t + m_oldCoefs.v[int(ch)][0] * omt) +
{ *dataIn * (m_coefs.v[int(ch)][1] * t + m_oldCoefs.v[int(ch)][1] * omt);
*dataOut = Clamp16(*dataOut + ++dataOut;
*dataIn * (m_coefs.v[int(ch)][0] * t + m_oldCoefs.v[int(ch)][0] * omt) + }
*dataIn * (m_coefs.v[int(ch)][1] * t + m_oldCoefs.v[int(ch)][1] * omt)); }
++dataOut;
}
}
++m_curSlewFrame; ++m_curSlewFrame;
} } else {
else for (unsigned c = 0; c < chmap.m_channelCount; ++c) {
{ AudioChannel ch = chmap.m_channels[c];
for (unsigned c=0 ; c<chmap.m_channelCount ; ++c) if (ch != AudioChannel::Unknown) {
{ *dataOut = *dataOut + dataIn[0] * m_coefs.v[int(ch)][0] + dataIn[1] * m_coefs.v[int(ch)][1];
AudioChannel ch = chmap.m_channels[c]; ++dataOut;
if (ch != AudioChannel::Unknown)
{
*dataOut = Clamp16(*dataOut +
dataIn[0] * m_coefs.v[int(ch)][0] +
dataIn[1] * m_coefs.v[int(ch)][1]);
++dataOut;
}
}
} }
}
} }
return dataOut; }
} return dataOut;
int32_t* AudioMatrixStereo::mixStereoSampleData(const AudioVoiceEngineMixInfo& info,
const int32_t* dataIn, int32_t* dataOut, size_t frames)
{
const ChannelMap& chmap = info.m_channelMap;
for (size_t f=0 ; f<frames ; ++f, dataIn += 2)
{
if (m_slewFrames && m_curSlewFrame < m_slewFrames)
{
double t = m_curSlewFrame / double(m_slewFrames);
double omt = 1.0 - t;
for (unsigned c=0 ; c<chmap.m_channelCount ; ++c)
{
AudioChannel ch = chmap.m_channels[c];
if (ch != AudioChannel::Unknown)
{
*dataOut = Clamp32(*dataOut +
*dataIn * (m_coefs.v[int(ch)][0] * t + m_oldCoefs.v[int(ch)][0] * omt) +
*dataIn * (m_coefs.v[int(ch)][1] * t + m_oldCoefs.v[int(ch)][1] * omt));
++dataOut;
}
}
++m_curSlewFrame;
}
else
{
for (unsigned c=0 ; c<chmap.m_channelCount ; ++c)
{
AudioChannel ch = chmap.m_channels[c];
if (ch != AudioChannel::Unknown)
{
*dataOut = Clamp32(*dataOut +
dataIn[0] * m_coefs.v[int(ch)][0] +
dataIn[1] * m_coefs.v[int(ch)][1]);
++dataOut;
}
}
}
}
return dataOut;
}
float* AudioMatrixStereo::mixStereoSampleData(const AudioVoiceEngineMixInfo& info,
const float* dataIn, float* dataOut, size_t frames)
{
const ChannelMap& chmap = info.m_channelMap;
for (size_t f=0 ; f<frames ; ++f, dataIn += 2)
{
if (m_slewFrames && m_curSlewFrame < m_slewFrames)
{
double t = m_curSlewFrame / double(m_slewFrames);
double omt = 1.0 - t;
for (unsigned c=0 ; c<chmap.m_channelCount ; ++c)
{
AudioChannel ch = chmap.m_channels[c];
if (ch != AudioChannel::Unknown)
{
*dataOut = *dataOut +
*dataIn * (m_coefs.v[int(ch)][0] * t + m_oldCoefs.v[int(ch)][0] * omt) +
*dataIn * (m_coefs.v[int(ch)][1] * t + m_oldCoefs.v[int(ch)][1] * omt);
++dataOut;
}
}
++m_curSlewFrame;
}
else
{
for (unsigned c=0 ; c<chmap.m_channelCount ; ++c)
{
AudioChannel ch = chmap.m_channels[c];
if (ch != AudioChannel::Unknown)
{
*dataOut = *dataOut +
dataIn[0] * m_coefs.v[int(ch)][0] +
dataIn[1] * m_coefs.v[int(ch)][1];
++dataOut;
}
}
}
}
return dataOut;
}
} }
} // namespace boo

View File

@ -10,155 +10,138 @@
#include <immintrin.h> #include <immintrin.h>
#endif #endif
namespace boo namespace boo {
{
struct AudioVoiceEngineMixInfo; struct AudioVoiceEngineMixInfo;
static inline int16_t Clamp16(float in) static inline int16_t Clamp16(float in) {
{ if (in < SHRT_MIN)
if (in < SHRT_MIN) return SHRT_MIN;
return SHRT_MIN; else if (in > SHRT_MAX)
else if (in > SHRT_MAX) return SHRT_MAX;
return SHRT_MAX; return in;
return in;
} }
static inline int32_t Clamp32(float in) static inline int32_t Clamp32(float in) {
{ if (in < INT_MIN)
if (in < INT_MIN) return INT_MIN;
return INT_MIN; else if (in > INT_MAX)
else if (in > INT_MAX) return INT_MAX;
return INT_MAX; return in;
return in;
} }
class AudioMatrixMono class AudioMatrixMono {
{ union Coefs {
union Coefs float v[8];
{
float v[8];
#if __SSE__ #if __SSE__
__m128 q[2]; __m128 q[2];
__m64 d[4]; __m64 d[4];
#endif #endif
}; };
Coefs m_coefs = {}; Coefs m_coefs = {};
Coefs m_oldCoefs = {}; Coefs m_oldCoefs = {};
size_t m_slewFrames = 0; size_t m_slewFrames = 0;
size_t m_curSlewFrame = ~size_t(0); size_t m_curSlewFrame = ~size_t(0);
public: public:
AudioMatrixMono() {setDefaultMatrixCoefficients(AudioChannelSet::Stereo);} AudioMatrixMono() { setDefaultMatrixCoefficients(AudioChannelSet::Stereo); }
void setDefaultMatrixCoefficients(AudioChannelSet acSet); void setDefaultMatrixCoefficients(AudioChannelSet acSet);
void setMatrixCoefficients(const float coefs[8], size_t slewFrames=0) void setMatrixCoefficients(const float coefs[8], size_t slewFrames = 0) {
{ m_slewFrames = slewFrames;
m_slewFrames = slewFrames;
#if __SSE__ #if __SSE__
if (m_curSlewFrame != 0) if (m_curSlewFrame != 0) {
{ m_oldCoefs.q[0] = m_coefs.q[0];
m_oldCoefs.q[0] = m_coefs.q[0]; m_oldCoefs.q[1] = m_coefs.q[1];
m_oldCoefs.q[1] = m_coefs.q[1]; }
} m_coefs.q[0] = _mm_loadu_ps(coefs);
m_coefs.q[0] = _mm_loadu_ps(coefs); m_coefs.q[1] = _mm_loadu_ps(&coefs[4]);
m_coefs.q[1] = _mm_loadu_ps(&coefs[4]);
#else #else
for (int i=0 ; i<8 ; ++i) for (int i = 0; i < 8; ++i) {
{ if (m_curSlewFrame != 0)
if (m_curSlewFrame != 0) m_oldCoefs.v[i] = m_coefs.v[i];
m_oldCoefs.v[i] = m_coefs.v[i]; m_coefs.v[i] = coefs[i];
m_coefs.v[i] = coefs[i]; }
}
#endif #endif
m_curSlewFrame = 0; m_curSlewFrame = 0;
} }
int16_t* mixMonoSampleData(const AudioVoiceEngineMixInfo& info, int16_t* mixMonoSampleData(const AudioVoiceEngineMixInfo& info, const int16_t* dataIn, int16_t* dataOut,
const int16_t* dataIn, int16_t* dataOut, size_t samples); size_t samples);
int32_t* mixMonoSampleData(const AudioVoiceEngineMixInfo& info, int32_t* mixMonoSampleData(const AudioVoiceEngineMixInfo& info, const int32_t* dataIn, int32_t* dataOut,
const int32_t* dataIn, int32_t* dataOut, size_t samples); size_t samples);
float* mixMonoSampleData(const AudioVoiceEngineMixInfo& info, float* mixMonoSampleData(const AudioVoiceEngineMixInfo& info, const float* dataIn, float* dataOut, size_t samples);
const float* dataIn, float* dataOut, size_t samples);
bool isSilent() const bool isSilent() const {
{ if (m_curSlewFrame < m_slewFrames)
if (m_curSlewFrame < m_slewFrames) for (int i = 0; i < 8; ++i)
for (int i=0 ; i<8 ; ++i) if (m_oldCoefs.v[i] > FLT_EPSILON)
if (m_oldCoefs.v[i] > FLT_EPSILON) return false;
return false; for (int i = 0; i < 8; ++i)
for (int i=0 ; i<8 ; ++i) if (m_coefs.v[i] > FLT_EPSILON)
if (m_coefs.v[i] > FLT_EPSILON) return false;
return false; return true;
return true; }
}
}; };
class AudioMatrixStereo class AudioMatrixStereo {
{ union Coefs {
union Coefs float v[8][2];
{
float v[8][2];
#if __SSE__ #if __SSE__
__m128 q[4]; __m128 q[4];
__m64 d[8]; __m64 d[8];
#endif #endif
}; };
Coefs m_coefs = {}; Coefs m_coefs = {};
Coefs m_oldCoefs = {}; Coefs m_oldCoefs = {};
size_t m_slewFrames = 0; size_t m_slewFrames = 0;
size_t m_curSlewFrame = ~size_t(0); size_t m_curSlewFrame = ~size_t(0);
public: public:
AudioMatrixStereo() {setDefaultMatrixCoefficients(AudioChannelSet::Stereo);} AudioMatrixStereo() { setDefaultMatrixCoefficients(AudioChannelSet::Stereo); }
void setDefaultMatrixCoefficients(AudioChannelSet acSet); void setDefaultMatrixCoefficients(AudioChannelSet acSet);
void setMatrixCoefficients(const float coefs[8][2], size_t slewFrames=0) void setMatrixCoefficients(const float coefs[8][2], size_t slewFrames = 0) {
{ m_slewFrames = slewFrames;
m_slewFrames = slewFrames;
#if __SSE__ #if __SSE__
if (m_curSlewFrame != 0) if (m_curSlewFrame != 0) {
{ m_oldCoefs.q[0] = m_coefs.q[0];
m_oldCoefs.q[0] = m_coefs.q[0]; m_oldCoefs.q[1] = m_coefs.q[1];
m_oldCoefs.q[1] = m_coefs.q[1]; m_oldCoefs.q[2] = m_coefs.q[2];
m_oldCoefs.q[2] = m_coefs.q[2]; m_oldCoefs.q[3] = m_coefs.q[3];
m_oldCoefs.q[3] = m_coefs.q[3]; }
} m_coefs.q[0] = _mm_loadu_ps(coefs[0]);
m_coefs.q[0] = _mm_loadu_ps(coefs[0]); m_coefs.q[1] = _mm_loadu_ps(coefs[2]);
m_coefs.q[1] = _mm_loadu_ps(coefs[2]); m_coefs.q[2] = _mm_loadu_ps(coefs[4]);
m_coefs.q[2] = _mm_loadu_ps(coefs[4]); m_coefs.q[3] = _mm_loadu_ps(coefs[6]);
m_coefs.q[3] = _mm_loadu_ps(coefs[6]);
#else #else
for (int i=0 ; i<8 ; ++i) for (int i = 0; i < 8; ++i) {
{ if (m_curSlewFrame != 0) {
if (m_curSlewFrame != 0) m_oldCoefs.v[i][0] = m_coefs.v[i][0];
{ m_oldCoefs.v[i][1] = m_coefs.v[i][1];
m_oldCoefs.v[i][0] = m_coefs.v[i][0]; }
m_oldCoefs.v[i][1] = m_coefs.v[i][1]; m_coefs.v[i][0] = coefs[i][0];
} m_coefs.v[i][1] = coefs[i][1];
m_coefs.v[i][0] = coefs[i][0]; }
m_coefs.v[i][1] = coefs[i][1];
}
#endif #endif
m_curSlewFrame = 0; m_curSlewFrame = 0;
} }
int16_t* mixStereoSampleData(const AudioVoiceEngineMixInfo& info, int16_t* mixStereoSampleData(const AudioVoiceEngineMixInfo& info, const int16_t* dataIn, int16_t* dataOut,
const int16_t* dataIn, int16_t* dataOut, size_t frames); size_t frames);
int32_t* mixStereoSampleData(const AudioVoiceEngineMixInfo& info, int32_t* mixStereoSampleData(const AudioVoiceEngineMixInfo& info, const int32_t* dataIn, int32_t* dataOut,
const int32_t* dataIn, int32_t* dataOut, size_t frames); size_t frames);
float* mixStereoSampleData(const AudioVoiceEngineMixInfo& info, float* mixStereoSampleData(const AudioVoiceEngineMixInfo& info, const float* dataIn, float* dataOut, size_t frames);
const float* dataIn, float* dataOut, size_t frames);
bool isSilent() const bool isSilent() const {
{ if (m_curSlewFrame < m_slewFrames)
if (m_curSlewFrame < m_slewFrames) for (int i = 0; i < 8; ++i)
for (int i=0 ; i<8 ; ++i) if (m_oldCoefs.v[i][0] > FLT_EPSILON || m_oldCoefs.v[i][1] > FLT_EPSILON)
if (m_oldCoefs.v[i][0] > FLT_EPSILON || m_oldCoefs.v[i][1] > FLT_EPSILON) return false;
return false; for (int i = 0; i < 8; ++i)
for (int i=0 ; i<8 ; ++i) if (m_coefs.v[i][0] > FLT_EPSILON || m_coefs.v[i][1] > FLT_EPSILON)
if (m_coefs.v[i][0] > FLT_EPSILON || m_coefs.v[i][1] > FLT_EPSILON) return false;
return false; return true;
return true; }
}
}; };
} } // namespace boo

View File

@ -4,531 +4,440 @@
#include <immintrin.h> #include <immintrin.h>
namespace boo namespace boo {
{
typedef union typedef union {
{ float v[4];
float v[4];
#if __SSE__ #if __SSE__
__m128 q; __m128 q;
__m64 d[2]; __m64 d[2];
#endif #endif
} TVectorUnion; } TVectorUnion;
static constexpr TVectorUnion Min32Vec = {{INT32_MIN, INT32_MIN, INT32_MIN, INT32_MIN}}; static constexpr TVectorUnion Min32Vec = {{INT32_MIN, INT32_MIN, INT32_MIN, INT32_MIN}};
static constexpr TVectorUnion Max32Vec = {{INT32_MAX, INT32_MAX, INT32_MAX, INT32_MAX}}; static constexpr TVectorUnion Max32Vec = {{INT32_MAX, INT32_MAX, INT32_MAX, INT32_MAX}};
void AudioMatrixMono::setDefaultMatrixCoefficients(AudioChannelSet acSet) void AudioMatrixMono::setDefaultMatrixCoefficients(AudioChannelSet acSet) {
{ m_curSlewFrame = 0;
m_curSlewFrame = 0; m_slewFrames = 0;
m_slewFrames = 0; m_coefs.q[0] = _mm_xor_ps(m_coefs.q[0], m_coefs.q[0]);
m_coefs.q[0] = _mm_xor_ps(m_coefs.q[0], m_coefs.q[0]); m_coefs.q[1] = _mm_xor_ps(m_coefs.q[1], m_coefs.q[1]);
m_coefs.q[1] = _mm_xor_ps(m_coefs.q[1], m_coefs.q[1]); switch (acSet) {
switch (acSet) case AudioChannelSet::Stereo:
{ case AudioChannelSet::Quad:
case AudioChannelSet::Stereo: m_coefs.v[int(AudioChannel::FrontLeft)] = 1.0;
case AudioChannelSet::Quad: m_coefs.v[int(AudioChannel::FrontRight)] = 1.0;
m_coefs.v[int(AudioChannel::FrontLeft)] = 1.0; break;
m_coefs.v[int(AudioChannel::FrontRight)] = 1.0; case AudioChannelSet::Surround51:
case AudioChannelSet::Surround71:
m_coefs.v[int(AudioChannel::FrontCenter)] = 1.0;
break;
default:
break;
}
}
int16_t* AudioMatrixMono::mixMonoSampleData(const AudioVoiceEngineMixInfo& info, const int16_t* dataIn,
int16_t* dataOut, size_t samples) {
const ChannelMap& chmap = info.m_channelMap;
for (size_t s = 0; s < samples; ++s, ++dataIn) {
if (m_slewFrames && m_curSlewFrame < m_slewFrames) {
double t = m_curSlewFrame / double(m_slewFrames);
double omt = 1.0 - t;
for (unsigned c = 0; c < chmap.m_channelCount; ++c) {
AudioChannel ch = chmap.m_channels[c];
if (ch != AudioChannel::Unknown) {
*dataOut = Clamp16(*dataOut + *dataIn * (m_coefs.v[int(ch)] * t + m_oldCoefs.v[int(ch)] * omt));
++dataOut;
}
}
++m_curSlewFrame;
} else {
for (unsigned c = 0; c < chmap.m_channelCount; ++c) {
AudioChannel ch = chmap.m_channels[c];
if (ch != AudioChannel::Unknown) {
*dataOut = Clamp16(*dataOut + *dataIn * m_coefs.v[int(ch)]);
++dataOut;
}
}
}
}
return dataOut;
}
int32_t* AudioMatrixMono::mixMonoSampleData(const AudioVoiceEngineMixInfo& info, const int32_t* dataIn,
int32_t* dataOut, size_t samples) {
const ChannelMap& chmap = info.m_channelMap;
for (size_t s = 0; s < samples; ++s, ++dataIn) {
if (m_slewFrames && m_curSlewFrame < m_slewFrames) {
float t = m_curSlewFrame / float(m_slewFrames);
float omt = 1.f - t;
switch (chmap.m_channelCount) {
case 2: {
++m_curSlewFrame;
float t2 = m_curSlewFrame / float(m_slewFrames);
float omt2 = 1.f - t2;
TVectorUnion coefs, samps;
coefs.q = _mm_add_ps(
_mm_mul_ps(_mm_shuffle_ps(m_coefs.q[0], m_coefs.q[0], _MM_SHUFFLE(1, 0, 1, 0)), _mm_set_ps(t, t, t2, t2)),
_mm_mul_ps(_mm_shuffle_ps(m_oldCoefs.q[0], m_oldCoefs.q[0], _MM_SHUFFLE(1, 0, 1, 0)),
_mm_set_ps(omt, omt, omt2, omt2)));
samps.q = _mm_cvtepi32_ps(_mm_set_epi32(dataIn[1], dataIn[0], dataIn[1], dataIn[0]));
__m128i* out = reinterpret_cast<__m128i*>(dataOut);
__m128 pre = _mm_add_ps(_mm_cvtepi32_ps(_mm_loadu_si128(out)), _mm_mul_ps(coefs.q, samps.q));
_mm_storeu_si128(out, _mm_cvttps_epi32(_mm_min_ps(_mm_max_ps(pre, Min32Vec.q), Max32Vec.q)));
dataOut += 4;
++s;
++dataIn;
break; break;
case AudioChannelSet::Surround51: }
case AudioChannelSet::Surround71: case 4: {
m_coefs.v[int(AudioChannel::FrontCenter)] = 1.0; TVectorUnion coefs, samps;
coefs.q = _mm_add_ps(_mm_mul_ps(m_coefs.q[0], _mm_set1_ps(t)), _mm_mul_ps(m_oldCoefs.q[0], _mm_set1_ps(omt)));
samps.q = _mm_cvtepi32_ps(_mm_loadu_si128(reinterpret_cast<const __m128i*>(dataIn)));
__m128i* out = reinterpret_cast<__m128i*>(dataOut);
__m128 pre = _mm_add_ps(_mm_cvtepi32_ps(_mm_loadu_si128(out)), _mm_mul_ps(coefs.q, samps.q));
_mm_storeu_si128(out, _mm_cvttps_epi32(_mm_min_ps(_mm_max_ps(pre, Min32Vec.q), Max32Vec.q)));
dataOut += 4;
break; break;
default: break; }
} case 6: {
} TVectorUnion coefs, samps;
coefs.q = _mm_add_ps(_mm_mul_ps(m_coefs.q[0], _mm_set1_ps(t)), _mm_mul_ps(m_oldCoefs.q[0], _mm_set1_ps(omt)));
samps.q = _mm_cvtepi32_ps(_mm_loadu_si128(reinterpret_cast<const __m128i*>(dataIn)));
int16_t* AudioMatrixMono::mixMonoSampleData(const AudioVoiceEngineMixInfo& info, __m128i* out = reinterpret_cast<__m128i*>(dataOut);
const int16_t* dataIn, int16_t* dataOut, size_t samples) __m128 pre = _mm_add_ps(_mm_cvtepi32_ps(_mm_loadu_si128(out)), _mm_mul_ps(coefs.q, samps.q));
{ _mm_storeu_si128(out, _mm_cvttps_epi32(_mm_min_ps(_mm_max_ps(pre, Min32Vec.q), Max32Vec.q)));
const ChannelMap& chmap = info.m_channelMap;
for (size_t s=0 ; s<samples ; ++s, ++dataIn)
{
if (m_slewFrames && m_curSlewFrame < m_slewFrames)
{
double t = m_curSlewFrame / double(m_slewFrames);
double omt = 1.0 - t;
for (unsigned c=0 ; c<chmap.m_channelCount ; ++c) dataOut += 4;
{
AudioChannel ch = chmap.m_channels[c];
if (ch != AudioChannel::Unknown)
{
*dataOut = Clamp16(*dataOut + *dataIn * (m_coefs.v[int(ch)] * t + m_oldCoefs.v[int(ch)] * omt));
++dataOut;
}
}
++m_curSlewFrame; coefs.q = _mm_add_ps(_mm_mul_ps(m_coefs.q[1], _mm_set1_ps(t)), _mm_mul_ps(m_oldCoefs.q[1], _mm_set1_ps(omt)));
} samps.q = _mm_cvtepi32_ps(_mm_loadu_si128(reinterpret_cast<const __m128i*>(dataIn)));
else
{
for (unsigned c=0 ; c<chmap.m_channelCount ; ++c)
{
AudioChannel ch = chmap.m_channels[c];
if (ch != AudioChannel::Unknown)
{
*dataOut = Clamp16(*dataOut + *dataIn * m_coefs.v[int(ch)]);
++dataOut;
}
}
}
}
return dataOut;
}
int32_t* AudioMatrixMono::mixMonoSampleData(const AudioVoiceEngineMixInfo& info, out = reinterpret_cast<__m128i*>(dataOut);
const int32_t* dataIn, int32_t* dataOut, size_t samples) __m128i loadOut = _mm_loadu_si128(out);
{ pre = _mm_add_ps(_mm_cvtepi32_ps(loadOut), _mm_mul_ps(coefs.q, samps.q));
const ChannelMap& chmap = info.m_channelMap; _mm_storel_epi64(out, _mm_cvttps_epi32(_mm_min_ps(_mm_max_ps(pre, Min32Vec.q), Max32Vec.q)));
for (size_t s=0 ; s<samples ; ++s, ++dataIn)
{
if (m_slewFrames && m_curSlewFrame < m_slewFrames)
{
float t = m_curSlewFrame / float(m_slewFrames);
float omt = 1.f - t;
switch (chmap.m_channelCount) dataOut += 2;
{
case 2:
{
++m_curSlewFrame;
float t2 = m_curSlewFrame / float(m_slewFrames);
float omt2 = 1.f - t2;
TVectorUnion coefs, samps;
coefs.q = _mm_add_ps(_mm_mul_ps(_mm_shuffle_ps(m_coefs.q[0], m_coefs.q[0], _MM_SHUFFLE(1, 0, 1, 0)),
_mm_set_ps(t, t, t2, t2)),
_mm_mul_ps(_mm_shuffle_ps(m_oldCoefs.q[0], m_oldCoefs.q[0], _MM_SHUFFLE(1, 0, 1, 0)),
_mm_set_ps(omt, omt, omt2, omt2)));
samps.q = _mm_cvtepi32_ps(_mm_set_epi32(dataIn[1], dataIn[0], dataIn[1], dataIn[0]));
__m128i* out = reinterpret_cast<__m128i*>(dataOut);
__m128 pre = _mm_add_ps(_mm_cvtepi32_ps(_mm_loadu_si128(out)), _mm_mul_ps(coefs.q, samps.q));
_mm_storeu_si128(out, _mm_cvttps_epi32(_mm_min_ps(_mm_max_ps(pre, Min32Vec.q), Max32Vec.q)));
dataOut += 4;
++s;
++dataIn;
break;
}
case 4:
{
TVectorUnion coefs, samps;
coefs.q = _mm_add_ps(_mm_mul_ps(m_coefs.q[0], _mm_set1_ps(t)),
_mm_mul_ps(m_oldCoefs.q[0], _mm_set1_ps(omt)));
samps.q = _mm_cvtepi32_ps(_mm_loadu_si128(reinterpret_cast<const __m128i*>(dataIn)));
__m128i* out = reinterpret_cast<__m128i*>(dataOut);
__m128 pre = _mm_add_ps(_mm_cvtepi32_ps(_mm_loadu_si128(out)), _mm_mul_ps(coefs.q, samps.q));
_mm_storeu_si128(out, _mm_cvttps_epi32(_mm_min_ps(_mm_max_ps(pre, Min32Vec.q), Max32Vec.q)));
dataOut += 4;
break;
}
case 6:
{
TVectorUnion coefs, samps;
coefs.q = _mm_add_ps(_mm_mul_ps(m_coefs.q[0], _mm_set1_ps(t)),
_mm_mul_ps(m_oldCoefs.q[0], _mm_set1_ps(omt)));
samps.q = _mm_cvtepi32_ps(_mm_loadu_si128(reinterpret_cast<const __m128i*>(dataIn)));
__m128i* out = reinterpret_cast<__m128i*>(dataOut);
__m128 pre = _mm_add_ps(_mm_cvtepi32_ps(_mm_loadu_si128(out)), _mm_mul_ps(coefs.q, samps.q));
_mm_storeu_si128(out, _mm_cvttps_epi32(_mm_min_ps(_mm_max_ps(pre, Min32Vec.q), Max32Vec.q)));
dataOut += 4;
coefs.q = _mm_add_ps(_mm_mul_ps(m_coefs.q[1], _mm_set1_ps(t)),
_mm_mul_ps(m_oldCoefs.q[1], _mm_set1_ps(omt)));
samps.q = _mm_cvtepi32_ps(_mm_loadu_si128(reinterpret_cast<const __m128i*>(dataIn)));
out = reinterpret_cast<__m128i*>(dataOut);
__m128i loadOut = _mm_loadu_si128(out);
pre = _mm_add_ps(_mm_cvtepi32_ps(loadOut), _mm_mul_ps(coefs.q, samps.q));
_mm_storel_epi64(out, _mm_cvttps_epi32(_mm_min_ps(_mm_max_ps(pre, Min32Vec.q), Max32Vec.q)));
dataOut += 2;
break;
}
case 8:
{
TVectorUnion coefs, samps;
coefs.q = _mm_add_ps(_mm_mul_ps(m_coefs.q[0], _mm_set1_ps(t)),
_mm_mul_ps(m_oldCoefs.q[0], _mm_set1_ps(omt)));
samps.q = _mm_cvtepi32_ps(_mm_loadu_si128(reinterpret_cast<const __m128i*>(dataIn)));
__m128i* out = reinterpret_cast<__m128i*>(dataOut);
__m128 pre = _mm_add_ps(_mm_cvtepi32_ps(_mm_loadu_si128(out)), _mm_mul_ps(coefs.q, samps.q));
_mm_storeu_si128(out, _mm_cvttps_epi32(_mm_min_ps(_mm_max_ps(pre, Min32Vec.q), Max32Vec.q)));
dataOut += 4;
coefs.q = _mm_add_ps(_mm_mul_ps(m_coefs.q[1], _mm_set1_ps(t)),
_mm_mul_ps(m_oldCoefs.q[1], _mm_set1_ps(omt)));
samps.q = _mm_cvtepi32_ps(_mm_loadu_si128(reinterpret_cast<const __m128i*>(dataIn)));
out = reinterpret_cast<__m128i*>(dataOut);
pre = _mm_add_ps(_mm_cvtepi32_ps(_mm_loadu_si128(out)), _mm_mul_ps(coefs.q, samps.q));
_mm_storeu_si128(out, _mm_cvttps_epi32(_mm_min_ps(_mm_max_ps(pre, Min32Vec.q), Max32Vec.q)));
dataOut += 4;
break;
}
default:
{
for (unsigned c=0 ; c<chmap.m_channelCount ; ++c)
{
AudioChannel ch = chmap.m_channels[c];
if (ch != AudioChannel::Unknown)
{
*dataOut = Clamp32(*dataOut + *dataIn * (m_coefs.v[int(ch)] * t + m_oldCoefs.v[int(ch)] * omt));
++dataOut;
}
}
break;
}
}
++m_curSlewFrame;
}
else
{
switch (chmap.m_channelCount)
{
case 2:
{
TVectorUnion coefs, samps;
coefs.q = _mm_shuffle_ps(m_coefs.q[0], m_coefs.q[0], _MM_SHUFFLE(1, 0, 1, 0));
samps.q = _mm_cvtepi32_ps(_mm_set_epi32(dataIn[1], dataIn[0], dataIn[1], dataIn[0]));
__m128i* out = reinterpret_cast<__m128i*>(dataOut);
__m128i huh2 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(out));
__m128 huh3 = _mm_cvtepi32_ps(huh2);
__m128 pre = _mm_add_ps(huh3, _mm_mul_ps(coefs.q, samps.q));
_mm_storeu_si128(out, _mm_cvttps_epi32(_mm_min_ps(_mm_max_ps(pre, Min32Vec.q), Max32Vec.q)));
dataOut += 4;
++s;
++dataIn;
break;
}
case 4:
{
TVectorUnion samps;
samps.q = _mm_cvtepi32_ps(_mm_loadu_si128(reinterpret_cast<const __m128i*>(dataIn)));
__m128i* out = reinterpret_cast<__m128i*>(dataOut);
__m128 pre = _mm_add_ps(_mm_cvtepi32_ps(_mm_loadu_si128(out)), _mm_mul_ps(m_coefs.q[0], samps.q));
_mm_storeu_si128(out, _mm_cvttps_epi32(_mm_min_ps(_mm_max_ps(pre, Min32Vec.q), Max32Vec.q)));
dataOut += 4;
break;
}
case 6:
{
TVectorUnion samps;
samps.q = _mm_cvtepi32_ps(_mm_loadu_si128(reinterpret_cast<const __m128i*>(dataIn)));
__m128i* out = reinterpret_cast<__m128i*>(dataOut);
__m128 pre = _mm_add_ps(_mm_cvtepi32_ps(_mm_loadu_si128(out)), _mm_mul_ps(m_coefs.q[0], samps.q));
_mm_storeu_si128(out, _mm_cvttps_epi32(_mm_min_ps(_mm_max_ps(pre, Min32Vec.q), Max32Vec.q)));
dataOut += 4;
samps.q = _mm_cvtepi32_ps(_mm_loadu_si128(reinterpret_cast<const __m128i*>(dataIn)));
out = reinterpret_cast<__m128i*>(dataOut);
__m128i loadOut = _mm_loadu_si128(out);
pre = _mm_add_ps(_mm_cvtepi32_ps(loadOut), _mm_mul_ps(m_coefs.q[1], samps.q));
_mm_storel_epi64(out, _mm_cvttps_epi32(_mm_min_ps(_mm_max_ps(pre, Min32Vec.q), Max32Vec.q)));
dataOut += 2;
break;
}
case 8:
{
TVectorUnion samps;
samps.q = _mm_cvtepi32_ps(_mm_loadu_si128(reinterpret_cast<const __m128i*>(dataIn)));
__m128i* out = reinterpret_cast<__m128i*>(dataOut);
__m128 pre = _mm_add_ps(_mm_cvtepi32_ps(_mm_loadu_si128(out)), _mm_mul_ps(m_coefs.q[0], samps.q));
_mm_storeu_si128(out, _mm_cvttps_epi32(_mm_min_ps(_mm_max_ps(pre, Min32Vec.q), Max32Vec.q)));
dataOut += 4;
samps.q = _mm_cvtepi32_ps(_mm_loadu_si128(reinterpret_cast<const __m128i*>(dataIn)));
out = reinterpret_cast<__m128i*>(dataOut);
pre = _mm_add_ps(_mm_cvtepi32_ps(_mm_loadu_si128(out)), _mm_mul_ps(m_coefs.q[1], samps.q));
_mm_storeu_si128(out, _mm_cvttps_epi32(_mm_min_ps(_mm_max_ps(pre, Min32Vec.q), Max32Vec.q)));
dataOut += 4;
break;
}
default:
{
for (unsigned c=0 ; c<chmap.m_channelCount ; ++c)
{
AudioChannel ch = chmap.m_channels[c];
if (ch != AudioChannel::Unknown)
{
*dataOut = Clamp32(*dataOut + *dataIn * m_coefs.v[int(ch)]);
++dataOut;
}
}
break;
}
}
}
}
return dataOut;
}
float* AudioMatrixMono::mixMonoSampleData(const AudioVoiceEngineMixInfo& info,
const float* dataIn, float* dataOut, size_t samples)
{
const ChannelMap& chmap = info.m_channelMap;
for (size_t s=0 ; s<samples ; ++s, ++dataIn)
{
if (m_slewFrames && m_curSlewFrame < m_slewFrames)
{
float t = m_curSlewFrame / float(m_slewFrames);
float omt = 1.f - t;
switch (chmap.m_channelCount)
{
case 2:
{
++m_curSlewFrame;
float t2 = m_curSlewFrame / float(m_slewFrames);
float omt2 = 1.f - t2;
TVectorUnion coefs, samps;
coefs.q = _mm_add_ps(_mm_mul_ps(_mm_shuffle_ps(m_coefs.q[0], m_coefs.q[0], _MM_SHUFFLE(1, 0, 1, 0)),
_mm_set_ps(t, t, t2, t2)),
_mm_mul_ps(_mm_shuffle_ps(m_oldCoefs.q[0], m_oldCoefs.q[0], _MM_SHUFFLE(1, 0, 1, 0)),
_mm_set_ps(omt, omt, omt2, omt2)));
samps.q = _mm_loadu_ps(dataIn);
samps.q = _mm_shuffle_ps(samps.q, samps.q, _MM_SHUFFLE(1, 0, 1, 0));
__m128 pre = _mm_add_ps(_mm_loadu_ps(dataOut), _mm_mul_ps(coefs.q, samps.q));
_mm_storeu_ps(dataOut, pre);
dataOut += 4;
++s;
++dataIn;
break;
}
default:
{
for (unsigned c=0 ; c<chmap.m_channelCount ; ++c)
{
AudioChannel ch = chmap.m_channels[c];
if (ch != AudioChannel::Unknown)
{
*dataOut = *dataOut + *dataIn * (m_coefs.v[int(ch)] * t + m_oldCoefs.v[int(ch)] * omt);
++dataOut;
}
}
break;
}
}
++m_curSlewFrame;
}
else
{
switch (chmap.m_channelCount)
{
case 2:
{
TVectorUnion coefs, samps;
coefs.q = _mm_shuffle_ps(m_coefs.q[0], m_coefs.q[0], _MM_SHUFFLE(1, 0, 1, 0));
samps.q = _mm_loadu_ps(dataIn);
samps.q = _mm_shuffle_ps(samps.q, samps.q, _MM_SHUFFLE(1, 0, 1, 0));
__m128 pre = _mm_add_ps(_mm_loadu_ps(dataOut), _mm_mul_ps(coefs.q, samps.q));
_mm_storeu_ps(dataOut, pre);
dataOut += 4;
++s;
++dataIn;
break;
}
default:
{
for (unsigned c=0 ; c<chmap.m_channelCount ; ++c)
{
AudioChannel ch = chmap.m_channels[c];
if (ch != AudioChannel::Unknown)
{
*dataOut = *dataOut + *dataIn * m_coefs.v[int(ch)];
++dataOut;
}
}
break;
}
}
}
}
return dataOut;
}
void AudioMatrixStereo::setDefaultMatrixCoefficients(AudioChannelSet acSet)
{
m_curSlewFrame = 0;
m_slewFrames = 0;
m_coefs.q[0] = _mm_xor_ps(m_coefs.q[0], m_coefs.q[0]);
m_coefs.q[1] = _mm_xor_ps(m_coefs.q[1], m_coefs.q[1]);
m_coefs.q[2] = _mm_xor_ps(m_coefs.q[2], m_coefs.q[2]);
m_coefs.q[3] = _mm_xor_ps(m_coefs.q[3], m_coefs.q[3]);
switch (acSet)
{
case AudioChannelSet::Stereo:
case AudioChannelSet::Quad:
m_coefs.v[int(AudioChannel::FrontLeft)][0] = 1.0;
m_coefs.v[int(AudioChannel::FrontRight)][1] = 1.0;
break; break;
case AudioChannelSet::Surround51: }
case AudioChannelSet::Surround71: case 8: {
m_coefs.v[int(AudioChannel::FrontLeft)][0] = 1.0; TVectorUnion coefs, samps;
m_coefs.v[int(AudioChannel::FrontRight)][1] = 1.0; coefs.q = _mm_add_ps(_mm_mul_ps(m_coefs.q[0], _mm_set1_ps(t)), _mm_mul_ps(m_oldCoefs.q[0], _mm_set1_ps(omt)));
samps.q = _mm_cvtepi32_ps(_mm_loadu_si128(reinterpret_cast<const __m128i*>(dataIn)));
__m128i* out = reinterpret_cast<__m128i*>(dataOut);
__m128 pre = _mm_add_ps(_mm_cvtepi32_ps(_mm_loadu_si128(out)), _mm_mul_ps(coefs.q, samps.q));
_mm_storeu_si128(out, _mm_cvttps_epi32(_mm_min_ps(_mm_max_ps(pre, Min32Vec.q), Max32Vec.q)));
dataOut += 4;
coefs.q = _mm_add_ps(_mm_mul_ps(m_coefs.q[1], _mm_set1_ps(t)), _mm_mul_ps(m_oldCoefs.q[1], _mm_set1_ps(omt)));
samps.q = _mm_cvtepi32_ps(_mm_loadu_si128(reinterpret_cast<const __m128i*>(dataIn)));
out = reinterpret_cast<__m128i*>(dataOut);
pre = _mm_add_ps(_mm_cvtepi32_ps(_mm_loadu_si128(out)), _mm_mul_ps(coefs.q, samps.q));
_mm_storeu_si128(out, _mm_cvttps_epi32(_mm_min_ps(_mm_max_ps(pre, Min32Vec.q), Max32Vec.q)));
dataOut += 4;
break; break;
default: break; }
default: {
for (unsigned c = 0; c < chmap.m_channelCount; ++c) {
AudioChannel ch = chmap.m_channels[c];
if (ch != AudioChannel::Unknown) {
*dataOut = Clamp32(*dataOut + *dataIn * (m_coefs.v[int(ch)] * t + m_oldCoefs.v[int(ch)] * omt));
++dataOut;
}
}
break;
}
}
++m_curSlewFrame;
} else {
switch (chmap.m_channelCount) {
case 2: {
TVectorUnion coefs, samps;
coefs.q = _mm_shuffle_ps(m_coefs.q[0], m_coefs.q[0], _MM_SHUFFLE(1, 0, 1, 0));
samps.q = _mm_cvtepi32_ps(_mm_set_epi32(dataIn[1], dataIn[0], dataIn[1], dataIn[0]));
__m128i* out = reinterpret_cast<__m128i*>(dataOut);
__m128i huh2 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(out));
__m128 huh3 = _mm_cvtepi32_ps(huh2);
__m128 pre = _mm_add_ps(huh3, _mm_mul_ps(coefs.q, samps.q));
_mm_storeu_si128(out, _mm_cvttps_epi32(_mm_min_ps(_mm_max_ps(pre, Min32Vec.q), Max32Vec.q)));
dataOut += 4;
++s;
++dataIn;
break;
}
case 4: {
TVectorUnion samps;
samps.q = _mm_cvtepi32_ps(_mm_loadu_si128(reinterpret_cast<const __m128i*>(dataIn)));
__m128i* out = reinterpret_cast<__m128i*>(dataOut);
__m128 pre = _mm_add_ps(_mm_cvtepi32_ps(_mm_loadu_si128(out)), _mm_mul_ps(m_coefs.q[0], samps.q));
_mm_storeu_si128(out, _mm_cvttps_epi32(_mm_min_ps(_mm_max_ps(pre, Min32Vec.q), Max32Vec.q)));
dataOut += 4;
break;
}
case 6: {
TVectorUnion samps;
samps.q = _mm_cvtepi32_ps(_mm_loadu_si128(reinterpret_cast<const __m128i*>(dataIn)));
__m128i* out = reinterpret_cast<__m128i*>(dataOut);
__m128 pre = _mm_add_ps(_mm_cvtepi32_ps(_mm_loadu_si128(out)), _mm_mul_ps(m_coefs.q[0], samps.q));
_mm_storeu_si128(out, _mm_cvttps_epi32(_mm_min_ps(_mm_max_ps(pre, Min32Vec.q), Max32Vec.q)));
dataOut += 4;
samps.q = _mm_cvtepi32_ps(_mm_loadu_si128(reinterpret_cast<const __m128i*>(dataIn)));
out = reinterpret_cast<__m128i*>(dataOut);
__m128i loadOut = _mm_loadu_si128(out);
pre = _mm_add_ps(_mm_cvtepi32_ps(loadOut), _mm_mul_ps(m_coefs.q[1], samps.q));
_mm_storel_epi64(out, _mm_cvttps_epi32(_mm_min_ps(_mm_max_ps(pre, Min32Vec.q), Max32Vec.q)));
dataOut += 2;
break;
}
case 8: {
TVectorUnion samps;
samps.q = _mm_cvtepi32_ps(_mm_loadu_si128(reinterpret_cast<const __m128i*>(dataIn)));
__m128i* out = reinterpret_cast<__m128i*>(dataOut);
__m128 pre = _mm_add_ps(_mm_cvtepi32_ps(_mm_loadu_si128(out)), _mm_mul_ps(m_coefs.q[0], samps.q));
_mm_storeu_si128(out, _mm_cvttps_epi32(_mm_min_ps(_mm_max_ps(pre, Min32Vec.q), Max32Vec.q)));
dataOut += 4;
samps.q = _mm_cvtepi32_ps(_mm_loadu_si128(reinterpret_cast<const __m128i*>(dataIn)));
out = reinterpret_cast<__m128i*>(dataOut);
pre = _mm_add_ps(_mm_cvtepi32_ps(_mm_loadu_si128(out)), _mm_mul_ps(m_coefs.q[1], samps.q));
_mm_storeu_si128(out, _mm_cvttps_epi32(_mm_min_ps(_mm_max_ps(pre, Min32Vec.q), Max32Vec.q)));
dataOut += 4;
break;
}
default: {
for (unsigned c = 0; c < chmap.m_channelCount; ++c) {
AudioChannel ch = chmap.m_channels[c];
if (ch != AudioChannel::Unknown) {
*dataOut = Clamp32(*dataOut + *dataIn * m_coefs.v[int(ch)]);
++dataOut;
}
}
break;
}
}
} }
}
return dataOut;
} }
int16_t* AudioMatrixStereo::mixStereoSampleData(const AudioVoiceEngineMixInfo& info, float* AudioMatrixMono::mixMonoSampleData(const AudioVoiceEngineMixInfo& info, const float* dataIn, float* dataOut,
const int16_t* dataIn, int16_t* dataOut, size_t frames) size_t samples) {
{ const ChannelMap& chmap = info.m_channelMap;
const ChannelMap& chmap = info.m_channelMap; for (size_t s = 0; s < samples; ++s, ++dataIn) {
for (size_t f=0 ; f<frames ; ++f, dataIn += 2) if (m_slewFrames && m_curSlewFrame < m_slewFrames) {
{ float t = m_curSlewFrame / float(m_slewFrames);
if (m_slewFrames && m_curSlewFrame < m_slewFrames) float omt = 1.f - t;
{
double t = m_curSlewFrame / double(m_slewFrames);
double omt = 1.0 - t;
for (unsigned c=0 ; c<chmap.m_channelCount ; ++c) switch (chmap.m_channelCount) {
{ case 2: {
AudioChannel ch = chmap.m_channels[c]; ++m_curSlewFrame;
if (ch != AudioChannel::Unknown) float t2 = m_curSlewFrame / float(m_slewFrames);
{ float omt2 = 1.f - t2;
*dataOut = Clamp16(*dataOut +
*dataIn * (m_coefs.v[int(ch)][0] * t + m_oldCoefs.v[int(ch)][0] * omt) +
*dataIn * (m_coefs.v[int(ch)][1] * t + m_oldCoefs.v[int(ch)][1] * omt));
++dataOut;
}
}
++m_curSlewFrame; TVectorUnion coefs, samps;
coefs.q = _mm_add_ps(
_mm_mul_ps(_mm_shuffle_ps(m_coefs.q[0], m_coefs.q[0], _MM_SHUFFLE(1, 0, 1, 0)), _mm_set_ps(t, t, t2, t2)),
_mm_mul_ps(_mm_shuffle_ps(m_oldCoefs.q[0], m_oldCoefs.q[0], _MM_SHUFFLE(1, 0, 1, 0)),
_mm_set_ps(omt, omt, omt2, omt2)));
samps.q = _mm_loadu_ps(dataIn);
samps.q = _mm_shuffle_ps(samps.q, samps.q, _MM_SHUFFLE(1, 0, 1, 0));
__m128 pre = _mm_add_ps(_mm_loadu_ps(dataOut), _mm_mul_ps(coefs.q, samps.q));
_mm_storeu_ps(dataOut, pre);
dataOut += 4;
++s;
++dataIn;
break;
}
default: {
for (unsigned c = 0; c < chmap.m_channelCount; ++c) {
AudioChannel ch = chmap.m_channels[c];
if (ch != AudioChannel::Unknown) {
*dataOut = *dataOut + *dataIn * (m_coefs.v[int(ch)] * t + m_oldCoefs.v[int(ch)] * omt);
++dataOut;
}
} }
else break;
{ }
for (unsigned c=0 ; c<chmap.m_channelCount ; ++c) }
{
AudioChannel ch = chmap.m_channels[c]; ++m_curSlewFrame;
if (ch != AudioChannel::Unknown) } else {
{ switch (chmap.m_channelCount) {
*dataOut = Clamp16(*dataOut + case 2: {
dataIn[0] * m_coefs.v[int(ch)][0] + TVectorUnion coefs, samps;
dataIn[1] * m_coefs.v[int(ch)][1]); coefs.q = _mm_shuffle_ps(m_coefs.q[0], m_coefs.q[0], _MM_SHUFFLE(1, 0, 1, 0));
++dataOut; samps.q = _mm_loadu_ps(dataIn);
} samps.q = _mm_shuffle_ps(samps.q, samps.q, _MM_SHUFFLE(1, 0, 1, 0));
}
__m128 pre = _mm_add_ps(_mm_loadu_ps(dataOut), _mm_mul_ps(coefs.q, samps.q));
_mm_storeu_ps(dataOut, pre);
dataOut += 4;
++s;
++dataIn;
break;
}
default: {
for (unsigned c = 0; c < chmap.m_channelCount; ++c) {
AudioChannel ch = chmap.m_channels[c];
if (ch != AudioChannel::Unknown) {
*dataOut = *dataOut + *dataIn * m_coefs.v[int(ch)];
++dataOut;
}
} }
break;
}
}
} }
return dataOut; }
return dataOut;
} }
int32_t* AudioMatrixStereo::mixStereoSampleData(const AudioVoiceEngineMixInfo& info, void AudioMatrixStereo::setDefaultMatrixCoefficients(AudioChannelSet acSet) {
const int32_t* dataIn, int32_t* dataOut, size_t frames) m_curSlewFrame = 0;
{ m_slewFrames = 0;
const ChannelMap& chmap = info.m_channelMap; m_coefs.q[0] = _mm_xor_ps(m_coefs.q[0], m_coefs.q[0]);
for (size_t f=0 ; f<frames ; ++f, dataIn += 2) m_coefs.q[1] = _mm_xor_ps(m_coefs.q[1], m_coefs.q[1]);
{ m_coefs.q[2] = _mm_xor_ps(m_coefs.q[2], m_coefs.q[2]);
if (m_slewFrames && m_curSlewFrame < m_slewFrames) m_coefs.q[3] = _mm_xor_ps(m_coefs.q[3], m_coefs.q[3]);
{ switch (acSet) {
double t = m_curSlewFrame / double(m_slewFrames); case AudioChannelSet::Stereo:
double omt = 1.0 - t; case AudioChannelSet::Quad:
m_coefs.v[int(AudioChannel::FrontLeft)][0] = 1.0;
m_coefs.v[int(AudioChannel::FrontRight)][1] = 1.0;
break;
case AudioChannelSet::Surround51:
case AudioChannelSet::Surround71:
m_coefs.v[int(AudioChannel::FrontLeft)][0] = 1.0;
m_coefs.v[int(AudioChannel::FrontRight)][1] = 1.0;
break;
default:
break;
}
}
for (unsigned c=0 ; c<chmap.m_channelCount ; ++c) int16_t* AudioMatrixStereo::mixStereoSampleData(const AudioVoiceEngineMixInfo& info, const int16_t* dataIn,
{ int16_t* dataOut, size_t frames) {
AudioChannel ch = chmap.m_channels[c]; const ChannelMap& chmap = info.m_channelMap;
if (ch != AudioChannel::Unknown) for (size_t f = 0; f < frames; ++f, dataIn += 2) {
{ if (m_slewFrames && m_curSlewFrame < m_slewFrames) {
*dataOut = Clamp32(*dataOut + double t = m_curSlewFrame / double(m_slewFrames);
*dataIn * (m_coefs.v[int(ch)][0] * t + m_oldCoefs.v[int(ch)][0] * omt) + double omt = 1.0 - t;
*dataIn * (m_coefs.v[int(ch)][1] * t + m_oldCoefs.v[int(ch)][1] * omt));
++dataOut;
}
}
++m_curSlewFrame; for (unsigned c = 0; c < chmap.m_channelCount; ++c) {
AudioChannel ch = chmap.m_channels[c];
if (ch != AudioChannel::Unknown) {
*dataOut = Clamp16(*dataOut + *dataIn * (m_coefs.v[int(ch)][0] * t + m_oldCoefs.v[int(ch)][0] * omt) +
*dataIn * (m_coefs.v[int(ch)][1] * t + m_oldCoefs.v[int(ch)][1] * omt));
++dataOut;
} }
else }
{
for (unsigned c=0 ; c<chmap.m_channelCount ; ++c) ++m_curSlewFrame;
{ } else {
AudioChannel ch = chmap.m_channels[c]; for (unsigned c = 0; c < chmap.m_channelCount; ++c) {
if (ch != AudioChannel::Unknown) AudioChannel ch = chmap.m_channels[c];
{ if (ch != AudioChannel::Unknown) {
*dataOut = Clamp32(*dataOut + *dataOut = Clamp16(*dataOut + dataIn[0] * m_coefs.v[int(ch)][0] + dataIn[1] * m_coefs.v[int(ch)][1]);
dataIn[0] * m_coefs.v[int(ch)][0] + ++dataOut;
dataIn[1] * m_coefs.v[int(ch)][1]);
++dataOut;
}
}
} }
}
} }
return dataOut; }
return dataOut;
} }
float* AudioMatrixStereo::mixStereoSampleData(const AudioVoiceEngineMixInfo& info, int32_t* AudioMatrixStereo::mixStereoSampleData(const AudioVoiceEngineMixInfo& info, const int32_t* dataIn,
const float* dataIn, float* dataOut, size_t frames) int32_t* dataOut, size_t frames) {
{ const ChannelMap& chmap = info.m_channelMap;
const ChannelMap& chmap = info.m_channelMap; for (size_t f = 0; f < frames; ++f, dataIn += 2) {
for (size_t f=0 ; f<frames ; ++f, dataIn += 2) if (m_slewFrames && m_curSlewFrame < m_slewFrames) {
{ double t = m_curSlewFrame / double(m_slewFrames);
if (m_slewFrames && m_curSlewFrame < m_slewFrames) double omt = 1.0 - t;
{
double t = m_curSlewFrame / double(m_slewFrames);
double omt = 1.0 - t;
for (unsigned c=0 ; c<chmap.m_channelCount ; ++c) for (unsigned c = 0; c < chmap.m_channelCount; ++c) {
{ AudioChannel ch = chmap.m_channels[c];
AudioChannel ch = chmap.m_channels[c]; if (ch != AudioChannel::Unknown) {
if (ch != AudioChannel::Unknown) *dataOut = Clamp32(*dataOut + *dataIn * (m_coefs.v[int(ch)][0] * t + m_oldCoefs.v[int(ch)][0] * omt) +
{ *dataIn * (m_coefs.v[int(ch)][1] * t + m_oldCoefs.v[int(ch)][1] * omt));
*dataOut = *dataOut + ++dataOut;
*dataIn * (m_coefs.v[int(ch)][0] * t + m_oldCoefs.v[int(ch)][0] * omt) + }
*dataIn * (m_coefs.v[int(ch)][1] * t + m_oldCoefs.v[int(ch)][1] * omt); }
++dataOut;
}
}
++m_curSlewFrame; ++m_curSlewFrame;
} } else {
else for (unsigned c = 0; c < chmap.m_channelCount; ++c) {
{ AudioChannel ch = chmap.m_channels[c];
for (unsigned c=0 ; c<chmap.m_channelCount ; ++c) if (ch != AudioChannel::Unknown) {
{ *dataOut = Clamp32(*dataOut + dataIn[0] * m_coefs.v[int(ch)][0] + dataIn[1] * m_coefs.v[int(ch)][1]);
AudioChannel ch = chmap.m_channels[c]; ++dataOut;
if (ch != AudioChannel::Unknown)
{
*dataOut = *dataOut +
dataIn[0] * m_coefs.v[int(ch)][0] +
dataIn[1] * m_coefs.v[int(ch)][1];
++dataOut;
}
}
} }
}
} }
return dataOut; }
return dataOut;
} }
float* AudioMatrixStereo::mixStereoSampleData(const AudioVoiceEngineMixInfo& info, const float* dataIn, float* dataOut,
size_t frames) {
const ChannelMap& chmap = info.m_channelMap;
for (size_t f = 0; f < frames; ++f, dataIn += 2) {
if (m_slewFrames && m_curSlewFrame < m_slewFrames) {
double t = m_curSlewFrame / double(m_slewFrames);
double omt = 1.0 - t;
for (unsigned c = 0; c < chmap.m_channelCount; ++c) {
AudioChannel ch = chmap.m_channels[c];
if (ch != AudioChannel::Unknown) {
*dataOut = *dataOut + *dataIn * (m_coefs.v[int(ch)][0] * t + m_oldCoefs.v[int(ch)][0] * omt) +
*dataIn * (m_coefs.v[int(ch)][1] * t + m_oldCoefs.v[int(ch)][1] * omt);
++dataOut;
}
}
++m_curSlewFrame;
} else {
for (unsigned c = 0; c < chmap.m_channelCount; ++c) {
AudioChannel ch = chmap.m_channels[c];
if (ch != AudioChannel::Unknown) {
*dataOut = *dataOut + dataIn[0] * m_coefs.v[int(ch)][0] + dataIn[1] * m_coefs.v[int(ch)][1];
++dataOut;
}
}
}
}
return dataOut;
} }
} // namespace boo

View File

@ -7,86 +7,71 @@
#undef min #undef min
#undef max #undef max
namespace boo namespace boo {
{
AudioSubmix::AudioSubmix(BaseAudioVoiceEngine& root, IAudioSubmixCallback* cb, int busId, bool mainOut) AudioSubmix::AudioSubmix(BaseAudioVoiceEngine& root, IAudioSubmixCallback* cb, int busId, bool mainOut)
: ListNode<AudioSubmix, BaseAudioVoiceEngine*, IAudioSubmix>(&root), m_busId(busId), m_mainOut(mainOut), m_cb(cb) : ListNode<AudioSubmix, BaseAudioVoiceEngine*, IAudioSubmix>(&root), m_busId(busId), m_mainOut(mainOut), m_cb(cb) {
{ if (mainOut)
if (mainOut) setSendLevel(m_head->m_mainSubmix.get(), 1.f, false);
setSendLevel(m_head->m_mainSubmix.get(), 1.f, false);
} }
AudioSubmix::~AudioSubmix() AudioSubmix::~AudioSubmix() { m_head->m_submixesDirty = true; }
{
m_head->m_submixesDirty = true;
}
AudioSubmix*& AudioSubmix::_getHeadPtr(BaseAudioVoiceEngine* head) { return head->m_submixHead; } AudioSubmix*& AudioSubmix::_getHeadPtr(BaseAudioVoiceEngine* head) { return head->m_submixHead; }
std::unique_lock<std::recursive_mutex> AudioSubmix::_getHeadLock(BaseAudioVoiceEngine* head) std::unique_lock<std::recursive_mutex> AudioSubmix::_getHeadLock(BaseAudioVoiceEngine* head) {
{ return std::unique_lock<std::recursive_mutex>{head->m_dataMutex}; } return std::unique_lock<std::recursive_mutex>{head->m_dataMutex};
std::unique_lock<std::recursive_mutex> AudioSubmix::destructorLock() }
{ return std::unique_lock<std::recursive_mutex>{m_head->m_dataMutex}; } std::unique_lock<std::recursive_mutex> AudioSubmix::destructorLock() {
return std::unique_lock<std::recursive_mutex>{m_head->m_dataMutex};
bool AudioSubmix::_isDirectDependencyOf(AudioSubmix* send)
{
return m_sendGains.find(send) != m_sendGains.cend();
} }
bool AudioSubmix::_mergeC3(std::list<AudioSubmix*>& output, bool AudioSubmix::_isDirectDependencyOf(AudioSubmix* send) { return m_sendGains.find(send) != m_sendGains.cend(); }
std::vector<std::list<AudioSubmix*>>& lists)
{ bool AudioSubmix::_mergeC3(std::list<AudioSubmix*>& output, std::vector<std::list<AudioSubmix*>>& lists) {
for (auto outerIt = lists.begin() ; outerIt != lists.cend() ; ++outerIt) for (auto outerIt = lists.begin(); outerIt != lists.cend(); ++outerIt) {
{ if (outerIt->empty())
if (outerIt->empty()) continue;
continue; AudioSubmix* smx = outerIt->front();
AudioSubmix* smx = outerIt->front(); bool found = false;
bool found = false; for (auto innerIt = lists.begin(); innerIt != lists.cend(); ++innerIt) {
for (auto innerIt = lists.begin() ; innerIt != lists.cend() ; ++innerIt) if (innerIt->empty() || outerIt == innerIt)
{ continue;
if (innerIt->empty() || outerIt == innerIt) if (smx == innerIt->front()) {
continue; innerIt->pop_front();
if (smx == innerIt->front()) found = true;
{ }
innerIt->pop_front();
found = true;
}
}
if (found)
{
outerIt->pop_front();
output.push_back(smx);
return true;
}
} }
return false; if (found) {
outerIt->pop_front();
output.push_back(smx);
return true;
}
}
return false;
} }
std::list<AudioSubmix*> AudioSubmix::_linearizeC3() std::list<AudioSubmix*> AudioSubmix::_linearizeC3() {
{ std::vector<std::list<AudioSubmix*>> lists = {{}};
std::vector<std::list<AudioSubmix*>> lists = {{}}; if (m_head->m_submixHead)
if (m_head->m_submixHead) for (AudioSubmix& smx : *m_head->m_submixHead) {
for (AudioSubmix& smx : *m_head->m_submixHead) if (&smx == this)
{ continue;
if (&smx == this) if (smx._isDirectDependencyOf(this))
continue; lists[0].push_back(&smx);
if (smx._isDirectDependencyOf(this)) }
lists[0].push_back(&smx); lists.reserve(lists[0].size() + 1);
} for (AudioSubmix* smx : lists[0])
lists.reserve(lists[0].size() + 1); lists.push_back(smx->_linearizeC3());
for (AudioSubmix* smx : lists[0])
lists.push_back(smx->_linearizeC3());
std::list<AudioSubmix*> ret = {this}; std::list<AudioSubmix*> ret = {this};
while (_mergeC3(ret, lists)) {} while (_mergeC3(ret, lists)) {}
return ret; return ret;
} }
template <typename T> template <typename T>
void AudioSubmix::_zeroFill() void AudioSubmix::_zeroFill() {
{ if (_getScratch<T>().size())
if (_getScratch<T>().size()) std::fill(_getScratch<T>().begin(), _getScratch<T>().end(), 0);
std::fill(_getScratch<T>().begin(), _getScratch<T>().end(), 0);
} }
template void AudioSubmix::_zeroFill<int16_t>(); template void AudioSubmix::_zeroFill<int16_t>();
@ -94,16 +79,15 @@ template void AudioSubmix::_zeroFill<int32_t>();
template void AudioSubmix::_zeroFill<float>(); template void AudioSubmix::_zeroFill<float>();
template <typename T> template <typename T>
T* AudioSubmix::_getMergeBuf(size_t frames) T* AudioSubmix::_getMergeBuf(size_t frames) {
{ if (_getRedirect<T>())
if (_getRedirect<T>()) return _getRedirect<T>();
return _getRedirect<T>();
size_t sampleCount = frames * m_head->clientMixInfo().m_channelMap.m_channelCount; size_t sampleCount = frames * m_head->clientMixInfo().m_channelMap.m_channelCount;
if (_getScratch<T>().size() < sampleCount) if (_getScratch<T>().size() < sampleCount)
_getScratch<T>().resize(sampleCount); _getScratch<T>().resize(sampleCount);
return _getScratch<T>().data(); return _getScratch<T>().data();
} }
template int16_t* AudioSubmix::_getMergeBuf<int16_t>(size_t frames); template int16_t* AudioSubmix::_getMergeBuf<int16_t>(size_t frames);
@ -111,143 +95,116 @@ template int32_t* AudioSubmix::_getMergeBuf<int32_t>(size_t frames);
template float* AudioSubmix::_getMergeBuf<float>(size_t frames); template float* AudioSubmix::_getMergeBuf<float>(size_t frames);
template <typename T> template <typename T>
static inline T ClampInt(float in) static inline T ClampInt(float in) {
{ if (std::is_floating_point<T>()) {
if (std::is_floating_point<T>()) return in; // Allow subsequent mixing stages to work with over-saturated values
{ } else {
return in; // Allow subsequent mixing stages to work with over-saturated values constexpr T MAX = std::numeric_limits<T>::max();
} constexpr T MIN = std::numeric_limits<T>::min();
else
{
constexpr T MAX = std::numeric_limits<T>::max();
constexpr T MIN = std::numeric_limits<T>::min();
if (in < MIN) if (in < MIN)
return MIN; return MIN;
else if (in > MAX) else if (in > MAX)
return MAX; return MAX;
else else
return in; return in;
} }
} }
template <typename T> template <typename T>
size_t AudioSubmix::_pumpAndMix(size_t frames) size_t AudioSubmix::_pumpAndMix(size_t frames) {
{ const ChannelMap& chMap = m_head->clientMixInfo().m_channelMap;
const ChannelMap& chMap = m_head->clientMixInfo().m_channelMap; size_t chanCount = chMap.m_channelCount;
size_t chanCount = chMap.m_channelCount;
if (_getRedirect<T>()) if (_getRedirect<T>()) {
{ if (m_cb && m_cb->canApplyEffect())
if (m_cb && m_cb->canApplyEffect()) m_cb->applyEffect(_getRedirect<T>(), frames, chMap, m_head->mixInfo().m_sampleRate);
m_cb->applyEffect(_getRedirect<T>(), frames, chMap, m_head->mixInfo().m_sampleRate); _getRedirect<T>() += chanCount * frames;
_getRedirect<T>() += chanCount * frames; } else {
} size_t sampleCount = frames * chanCount;
else if (_getScratch<T>().size() < sampleCount)
{ _getScratch<T>().resize(sampleCount);
size_t sampleCount = frames * chanCount; if (m_cb && m_cb->canApplyEffect())
if (_getScratch<T>().size() < sampleCount) m_cb->applyEffect(_getScratch<T>().data(), frames, chMap, m_head->mixInfo().m_sampleRate);
_getScratch<T>().resize(sampleCount);
if (m_cb && m_cb->canApplyEffect())
m_cb->applyEffect(_getScratch<T>().data(), frames, chMap, m_head->mixInfo().m_sampleRate);
size_t curSlewFrame = m_slewFrames; size_t curSlewFrame = m_slewFrames;
for (auto& smx : m_sendGains) for (auto& smx : m_sendGains) {
{ curSlewFrame = m_curSlewFrame;
curSlewFrame = m_curSlewFrame; AudioSubmix& sm = *reinterpret_cast<AudioSubmix*>(smx.first);
AudioSubmix& sm = *reinterpret_cast<AudioSubmix*>(smx.first); auto it = _getScratch<T>().begin();
auto it = _getScratch<T>().begin(); T* dataOut = sm._getMergeBuf<T>(frames);
T* dataOut = sm._getMergeBuf<T>(frames);
for (size_t f=0 ; f<frames ; ++f) for (size_t f = 0; f < frames; ++f) {
{ if (m_slewFrames && curSlewFrame < m_slewFrames) {
if (m_slewFrames && curSlewFrame < m_slewFrames) double t = curSlewFrame / double(m_slewFrames);
{ double omt = 1.0 - t;
double t = curSlewFrame / double(m_slewFrames);
double omt = 1.0 - t;
for (unsigned c=0 ; c<chanCount ; ++c) for (unsigned c = 0; c < chanCount; ++c) {
{ *dataOut = ClampInt<T>(*dataOut + *it * (smx.second[1] * t + smx.second[0] * omt));
*dataOut = ClampInt<T>(*dataOut + *it * (smx.second[1] * t + smx.second[0] * omt)); ++it;
++it; ++dataOut;
++dataOut; }
}
++curSlewFrame; ++curSlewFrame;
} } else {
else for (unsigned c = 0; c < chanCount; ++c) {
{ *dataOut = ClampInt<T>(*dataOut + *it * smx.second[1]);
for (unsigned c=0 ; c<chanCount ; ++c) ++it;
{ ++dataOut;
*dataOut = ClampInt<T>(*dataOut + *it * smx.second[1]); }
++it;
++dataOut;
}
}
}
} }
m_curSlewFrame += curSlewFrame; }
} }
m_curSlewFrame += curSlewFrame;
}
return frames; return frames;
} }
template size_t AudioSubmix::_pumpAndMix<int16_t>(size_t frames); template size_t AudioSubmix::_pumpAndMix<int16_t>(size_t frames);
template size_t AudioSubmix::_pumpAndMix<int32_t>(size_t frames); template size_t AudioSubmix::_pumpAndMix<int32_t>(size_t frames);
template size_t AudioSubmix::_pumpAndMix<float>(size_t frames); template size_t AudioSubmix::_pumpAndMix<float>(size_t frames);
void AudioSubmix::_resetOutputSampleRate() void AudioSubmix::_resetOutputSampleRate() {
{ if (m_cb)
if (m_cb) m_cb->resetOutputSampleRate(m_head->mixInfo().m_sampleRate);
m_cb->resetOutputSampleRate(m_head->mixInfo().m_sampleRate);
} }
void AudioSubmix::resetSendLevels() void AudioSubmix::resetSendLevels() {
{ if (m_sendGains.empty())
if (m_sendGains.empty()) return;
return; m_sendGains.clear();
m_sendGains.clear(); m_head->m_submixesDirty = true;
}
void AudioSubmix::setSendLevel(IAudioSubmix* submix, float level, bool slew) {
auto search = m_sendGains.find(submix);
if (search == m_sendGains.cend()) {
search = m_sendGains.emplace(submix, std::array<float, 2>{1.f, 1.f}).first;
m_head->m_submixesDirty = true; m_head->m_submixesDirty = true;
}
m_slewFrames = slew ? m_head->m_5msFrames : 0;
m_curSlewFrame = 0;
search->second[0] = search->second[1];
search->second[1] = level;
} }
void AudioSubmix::setSendLevel(IAudioSubmix* submix, float level, bool slew) const AudioVoiceEngineMixInfo& AudioSubmix::mixInfo() const { return m_head->mixInfo(); }
{
auto search = m_sendGains.find(submix);
if (search == m_sendGains.cend())
{
search = m_sendGains.emplace(submix, std::array<float, 2>{1.f, 1.f}).first;
m_head->m_submixesDirty = true;
}
m_slewFrames = slew ? m_head->m_5msFrames : 0; double AudioSubmix::getSampleRate() const { return mixInfo().m_sampleRate; }
m_curSlewFrame = 0;
search->second[0] = search->second[1]; SubmixFormat AudioSubmix::getSampleFormat() const {
search->second[1] = level; switch (mixInfo().m_sampleFormat) {
case SOXR_INT16_I:
default:
return SubmixFormat::Int16;
case SOXR_INT32_I:
return SubmixFormat::Int32;
case SOXR_FLOAT32_I:
return SubmixFormat::Float;
}
} }
const AudioVoiceEngineMixInfo& AudioSubmix::mixInfo() const } // namespace boo
{
return m_head->mixInfo();
}
double AudioSubmix::getSampleRate() const
{
return mixInfo().m_sampleRate;
}
SubmixFormat AudioSubmix::getSampleFormat() const
{
switch (mixInfo().m_sampleFormat)
{
case SOXR_INT16_I:
default:
return SubmixFormat::Int16;
case SOXR_INT32_I:
return SubmixFormat::Int32;
case SOXR_FLOAT32_I:
return SubmixFormat::Float;
}
}
}

View File

@ -3,7 +3,7 @@
#include "boo/audiodev/IAudioSubmix.hpp" #include "boo/audiodev/IAudioSubmix.hpp"
#include <list> #include <list>
#include <vector> #include <vector>
#include <array> #include <array>
#include <unordered_map> #include <unordered_map>
#include "Common.hpp" #include "Common.hpp"
@ -15,88 +15,107 @@ struct AudioUnitVoiceEngine;
struct VSTVoiceEngine; struct VSTVoiceEngine;
struct WAVOutVoiceEngine; struct WAVOutVoiceEngine;
namespace boo namespace boo {
{
class BaseAudioVoiceEngine; class BaseAudioVoiceEngine;
class AudioVoice; class AudioVoice;
struct AudioVoiceEngineMixInfo; struct AudioVoiceEngineMixInfo;
/* Output gains for each mix-send/channel */ /* Output gains for each mix-send/channel */
class AudioSubmix : public ListNode<AudioSubmix, BaseAudioVoiceEngine*, IAudioSubmix> class AudioSubmix : public ListNode<AudioSubmix, BaseAudioVoiceEngine*, IAudioSubmix> {
{ friend class BaseAudioVoiceEngine;
friend class BaseAudioVoiceEngine; friend class AudioVoiceMono;
friend class AudioVoiceMono; friend class AudioVoiceStereo;
friend class AudioVoiceStereo; friend struct WASAPIAudioVoiceEngine;
friend struct WASAPIAudioVoiceEngine; friend struct ::AudioUnitVoiceEngine;
friend struct ::AudioUnitVoiceEngine; friend struct ::VSTVoiceEngine;
friend struct ::VSTVoiceEngine; friend struct ::WAVOutVoiceEngine;
friend struct ::WAVOutVoiceEngine;
/* Mixer-engine relationships */ /* Mixer-engine relationships */
int m_busId; int m_busId;
bool m_mainOut; bool m_mainOut;
/* Callback (effect source, optional) */ /* Callback (effect source, optional) */
IAudioSubmixCallback* m_cb; IAudioSubmixCallback* m_cb;
/* Slew state for output gains */ /* Slew state for output gains */
size_t m_slewFrames = 0; size_t m_slewFrames = 0;
size_t m_curSlewFrame = 0; size_t m_curSlewFrame = 0;
/* Output gains for each mix-send/channel */ /* Output gains for each mix-send/channel */
std::unordered_map<IAudioSubmix*, std::array<float, 2>> m_sendGains; std::unordered_map<IAudioSubmix*, std::array<float, 2>> m_sendGains;
/* Temporary scratch buffers for accumulating submix audio */ /* Temporary scratch buffers for accumulating submix audio */
std::vector<int16_t> m_scratch16; std::vector<int16_t> m_scratch16;
std::vector<int32_t> m_scratch32; std::vector<int32_t> m_scratch32;
std::vector<float> m_scratchFlt; std::vector<float> m_scratchFlt;
template <typename T> std::vector<T>& _getScratch(); template <typename T>
std::vector<T>& _getScratch();
/* Override scratch buffers with alternate destination */ /* Override scratch buffers with alternate destination */
int16_t* m_redirect16 = nullptr; int16_t* m_redirect16 = nullptr;
int32_t* m_redirect32 = nullptr; int32_t* m_redirect32 = nullptr;
float* m_redirectFlt = nullptr; float* m_redirectFlt = nullptr;
template <typename T> T*& _getRedirect(); template <typename T>
T*& _getRedirect();
/* C3-linearization support (to mitigate a potential diamond problem on 'clever' submix routes) */ /* C3-linearization support (to mitigate a potential diamond problem on 'clever' submix routes) */
bool _isDirectDependencyOf(AudioSubmix* send); bool _isDirectDependencyOf(AudioSubmix* send);
std::list<AudioSubmix*> _linearizeC3(); std::list<AudioSubmix*> _linearizeC3();
static bool _mergeC3(std::list<AudioSubmix*>& output, static bool _mergeC3(std::list<AudioSubmix*>& output, std::vector<std::list<AudioSubmix*>>& lists);
std::vector<std::list<AudioSubmix*>>& lists);
/* Fill scratch buffers with silence for new mix cycle */ /* Fill scratch buffers with silence for new mix cycle */
template <typename T> void _zeroFill(); template <typename T>
void _zeroFill();
/* Receive audio from a single voice / submix */ /* Receive audio from a single voice / submix */
template <typename T> T* _getMergeBuf(size_t frames); template <typename T>
T* _getMergeBuf(size_t frames);
/* Mix scratch buffers into sends */ /* Mix scratch buffers into sends */
template <typename T> size_t _pumpAndMix(size_t frames); template <typename T>
size_t _pumpAndMix(size_t frames);
void _resetOutputSampleRate(); void _resetOutputSampleRate();
public: public:
static AudioSubmix*& _getHeadPtr(BaseAudioVoiceEngine* head); static AudioSubmix*& _getHeadPtr(BaseAudioVoiceEngine* head);
static std::unique_lock<std::recursive_mutex> _getHeadLock(BaseAudioVoiceEngine* head); static std::unique_lock<std::recursive_mutex> _getHeadLock(BaseAudioVoiceEngine* head);
std::unique_lock<std::recursive_mutex> destructorLock(); std::unique_lock<std::recursive_mutex> destructorLock();
AudioSubmix(BaseAudioVoiceEngine& root, IAudioSubmixCallback* cb, int busId, bool mainOut); AudioSubmix(BaseAudioVoiceEngine& root, IAudioSubmixCallback* cb, int busId, bool mainOut);
~AudioSubmix(); ~AudioSubmix();
void resetSendLevels(); void resetSendLevels();
void setSendLevel(IAudioSubmix* submix, float level, bool slew); void setSendLevel(IAudioSubmix* submix, float level, bool slew);
const AudioVoiceEngineMixInfo& mixInfo() const; const AudioVoiceEngineMixInfo& mixInfo() const;
double getSampleRate() const; double getSampleRate() const;
SubmixFormat getSampleFormat() const; SubmixFormat getSampleFormat() const;
}; };
template <> inline std::vector<int16_t>& AudioSubmix::_getScratch() { return m_scratch16; } template <>
template <> inline std::vector<int32_t>& AudioSubmix::_getScratch() { return m_scratch32; } inline std::vector<int16_t>& AudioSubmix::_getScratch() {
template <> inline std::vector<float>& AudioSubmix::_getScratch() { return m_scratchFlt; } return m_scratch16;
}
template <> inline int16_t*& AudioSubmix::_getRedirect<int16_t>() { return m_redirect16; } template <>
template <> inline int32_t*& AudioSubmix::_getRedirect<int32_t>() { return m_redirect32; } inline std::vector<int32_t>& AudioSubmix::_getScratch() {
template <> inline float*& AudioSubmix::_getRedirect<float>() { return m_redirectFlt; } return m_scratch32;
}
template <>
inline std::vector<float>& AudioSubmix::_getScratch() {
return m_scratchFlt;
} }
template <>
inline int16_t*& AudioSubmix::_getRedirect<int16_t>() {
return m_redirect16;
}
template <>
inline int32_t*& AudioSubmix::_getRedirect<int32_t>() {
return m_redirect32;
}
template <>
inline float*& AudioSubmix::_getRedirect<float>() {
return m_redirectFlt;
}
} // namespace boo

View File

@ -3,383 +3,303 @@
#include "logvisor/logvisor.hpp" #include "logvisor/logvisor.hpp"
#include <cmath> #include <cmath>
namespace boo namespace boo {
{
static logvisor::Module Log("boo::AudioVoice"); static logvisor::Module Log("boo::AudioVoice");
static AudioMatrixMono DefaultMonoMtx; static AudioMatrixMono DefaultMonoMtx;
static AudioMatrixStereo DefaultStereoMtx; static AudioMatrixStereo DefaultStereoMtx;
AudioVoice::AudioVoice(BaseAudioVoiceEngine& root, AudioVoice::AudioVoice(BaseAudioVoiceEngine& root, IAudioVoiceCallback* cb, bool dynamicRate)
IAudioVoiceCallback* cb, bool dynamicRate) : ListNode<AudioVoice, BaseAudioVoiceEngine*, IAudioVoice>(&root), m_cb(cb), m_dynamicRate(dynamicRate) {}
: ListNode<AudioVoice, BaseAudioVoiceEngine*, IAudioVoice>(&root), m_cb(cb), m_dynamicRate(dynamicRate)
{}
AudioVoice::~AudioVoice() AudioVoice::~AudioVoice() { soxr_delete(m_src); }
{
soxr_delete(m_src);
}
AudioVoice*& AudioVoice::_getHeadPtr(BaseAudioVoiceEngine* head) { return head->m_voiceHead; } AudioVoice*& AudioVoice::_getHeadPtr(BaseAudioVoiceEngine* head) { return head->m_voiceHead; }
std::unique_lock<std::recursive_mutex> AudioVoice::_getHeadLock(BaseAudioVoiceEngine* head) std::unique_lock<std::recursive_mutex> AudioVoice::_getHeadLock(BaseAudioVoiceEngine* head) {
{ return std::unique_lock<std::recursive_mutex>{head->m_dataMutex}; } return std::unique_lock<std::recursive_mutex>{head->m_dataMutex};
std::unique_lock<std::recursive_mutex> AudioVoice::destructorLock() }
{ return std::unique_lock<std::recursive_mutex>{m_head->m_dataMutex}; } std::unique_lock<std::recursive_mutex> AudioVoice::destructorLock() {
return std::unique_lock<std::recursive_mutex>{m_head->m_dataMutex};
}
void AudioVoice::_setPitchRatio(double ratio, bool slew) void AudioVoice::_setPitchRatio(double ratio, bool slew) {
{ if (m_dynamicRate) {
if (m_dynamicRate) m_sampleRatio = ratio * m_sampleRateIn / m_sampleRateOut;
{ soxr_error_t err = soxr_set_io_ratio(m_src, m_sampleRatio, slew ? m_head->m_5msFrames : 0);
m_sampleRatio = ratio * m_sampleRateIn / m_sampleRateOut; if (err) {
soxr_error_t err = soxr_set_io_ratio(m_src, m_sampleRatio, slew ? m_head->m_5msFrames : 0); Log.report(logvisor::Fatal, "unable to set resampler rate: %s", soxr_strerror(err));
if (err) m_setPitchRatio = false;
{ return;
Log.report(logvisor::Fatal, "unable to set resampler rate: %s", soxr_strerror(err));
m_setPitchRatio = false;
return;
}
} }
m_setPitchRatio = false; }
m_setPitchRatio = false;
} }
void AudioVoice::_midUpdate() void AudioVoice::_midUpdate() {
{ if (m_resetSampleRate)
if (m_resetSampleRate) _resetSampleRate(m_deferredSampleRate);
_resetSampleRate(m_deferredSampleRate); if (m_setPitchRatio)
if (m_setPitchRatio) _setPitchRatio(m_pitchRatio, m_slew);
_setPitchRatio(m_pitchRatio, m_slew);
} }
void AudioVoice::setPitchRatio(double ratio, bool slew) void AudioVoice::setPitchRatio(double ratio, bool slew) {
{ m_setPitchRatio = true;
m_setPitchRatio = true; m_pitchRatio = ratio;
m_pitchRatio = ratio; m_slew = slew;
m_slew = slew;
} }
void AudioVoice::resetSampleRate(double sampleRate) void AudioVoice::resetSampleRate(double sampleRate) {
{ m_resetSampleRate = true;
m_resetSampleRate = true; m_deferredSampleRate = sampleRate;
m_deferredSampleRate = sampleRate;
} }
void AudioVoice::start() void AudioVoice::start() { m_running = true; }
{
m_running = true; void AudioVoice::stop() { m_running = false; }
AudioVoiceMono::AudioVoiceMono(BaseAudioVoiceEngine& root, IAudioVoiceCallback* cb, double sampleRate, bool dynamicRate)
: AudioVoice(root, cb, dynamicRate) {
_resetSampleRate(sampleRate);
} }
void AudioVoice::stop() void AudioVoiceMono::_resetSampleRate(double sampleRate) {
{ soxr_delete(m_src);
m_running = false;
}
AudioVoiceMono::AudioVoiceMono(BaseAudioVoiceEngine& root, IAudioVoiceCallback* cb, double rateOut = m_head->mixInfo().m_sampleRate;
double sampleRate, bool dynamicRate) soxr_datatype_t formatOut = m_head->mixInfo().m_sampleFormat;
: AudioVoice(root, cb, dynamicRate) soxr_io_spec_t ioSpec = soxr_io_spec(SOXR_INT16_I, formatOut);
{ soxr_quality_spec_t qSpec = soxr_quality_spec(SOXR_20_BITQ, m_dynamicRate ? SOXR_VR : 0);
_resetSampleRate(sampleRate);
}
void AudioVoiceMono::_resetSampleRate(double sampleRate) soxr_error_t err;
{ m_src = soxr_create(sampleRate, rateOut, 1, &err, &ioSpec, &qSpec, nullptr);
soxr_delete(m_src);
double rateOut = m_head->mixInfo().m_sampleRate; if (err) {
soxr_datatype_t formatOut = m_head->mixInfo().m_sampleFormat; Log.report(logvisor::Fatal, "unable to create soxr resampler: %s", soxr_strerror(err));
soxr_io_spec_t ioSpec = soxr_io_spec(SOXR_INT16_I, formatOut);
soxr_quality_spec_t qSpec = soxr_quality_spec(SOXR_20_BITQ, m_dynamicRate ? SOXR_VR : 0);
soxr_error_t err;
m_src = soxr_create(sampleRate, rateOut, 1,
&err, &ioSpec, &qSpec, nullptr);
if (err)
{
Log.report(logvisor::Fatal, "unable to create soxr resampler: %s", soxr_strerror(err));
m_resetSampleRate = false;
return;
}
m_sampleRateIn = sampleRate;
m_sampleRateOut = rateOut;
m_sampleRatio = m_sampleRateIn / m_sampleRateOut;
soxr_set_input_fn(m_src, soxr_input_fn_t(SRCCallback), this, 0);
_setPitchRatio(m_pitchRatio, false);
m_resetSampleRate = false; m_resetSampleRate = false;
return;
}
m_sampleRateIn = sampleRate;
m_sampleRateOut = rateOut;
m_sampleRatio = m_sampleRateIn / m_sampleRateOut;
soxr_set_input_fn(m_src, soxr_input_fn_t(SRCCallback), this, 0);
_setPitchRatio(m_pitchRatio, false);
m_resetSampleRate = false;
} }
size_t AudioVoiceMono::SRCCallback(AudioVoiceMono* ctx, int16_t** data, size_t frames) size_t AudioVoiceMono::SRCCallback(AudioVoiceMono* ctx, int16_t** data, size_t frames) {
{ std::vector<int16_t>& scratchIn = ctx->m_head->m_scratchIn;
std::vector<int16_t>& scratchIn = ctx->m_head->m_scratchIn; if (scratchIn.size() < frames)
if (scratchIn.size() < frames) scratchIn.resize(frames);
scratchIn.resize(frames); *data = scratchIn.data();
*data = scratchIn.data(); if (ctx->m_silentOut) {
if (ctx->m_silentOut) memset(scratchIn.data(), 0, frames * 2);
{ return frames;
memset(scratchIn.data(), 0, frames * 2); } else
return frames; return ctx->m_cb->supplyAudio(*ctx, frames, scratchIn.data());
}
else
return ctx->m_cb->supplyAudio(*ctx, frames, scratchIn.data());
} }
bool AudioVoiceMono::isSilent() const bool AudioVoiceMono::isSilent() const {
{ if (m_sendMatrices.size()) {
if (m_sendMatrices.size()) for (auto& mtx : m_sendMatrices)
{ if (!mtx.second.isSilent())
for (auto& mtx : m_sendMatrices) return false;
if (!mtx.second.isSilent()) return true;
return false; } else {
return true; return DefaultMonoMtx.isSilent();
} }
else
{
return DefaultMonoMtx.isSilent();
}
} }
template <typename T> template <typename T>
size_t AudioVoiceMono::_pumpAndMix(size_t frames) size_t AudioVoiceMono::_pumpAndMix(size_t frames) {
{ auto& scratchPre = m_head->_getScratchPre<T>();
auto& scratchPre = m_head->_getScratchPre<T>(); if (scratchPre.size() < frames)
if (scratchPre.size() < frames) scratchPre.resize(frames + 2);
scratchPre.resize(frames + 2);
auto& scratchPost = m_head->_getScratchPost<T>(); auto& scratchPost = m_head->_getScratchPost<T>();
if (scratchPost.size() < frames) if (scratchPost.size() < frames)
scratchPost.resize(frames + 2); scratchPost.resize(frames + 2);
double dt = frames / m_sampleRateOut; double dt = frames / m_sampleRateOut;
m_cb->preSupplyAudio(*this, dt); m_cb->preSupplyAudio(*this, dt);
_midUpdate(); _midUpdate();
if (isSilent()) if (isSilent()) {
{ int16_t* dummy;
int16_t* dummy; SRCCallback(this, &dummy, size_t(std::ceil(frames * m_sampleRatio)));
SRCCallback(this, &dummy, size_t(std::ceil(frames * m_sampleRatio))); return 0;
return 0; }
size_t oDone = soxr_output(m_src, scratchPre.data(), frames);
if (oDone) {
if (m_sendMatrices.size()) {
for (auto& mtx : m_sendMatrices) {
AudioSubmix& smx = *reinterpret_cast<AudioSubmix*>(mtx.first);
m_cb->routeAudio(oDone, 1, dt, smx.m_busId, scratchPre.data(), scratchPost.data());
mtx.second.mixMonoSampleData(m_head->clientMixInfo(), scratchPost.data(), smx._getMergeBuf<T>(oDone), oDone);
}
} else {
AudioSubmix& smx = *m_head->m_mainSubmix;
m_cb->routeAudio(oDone, 1, dt, m_head->m_mainSubmix->m_busId, scratchPre.data(), scratchPost.data());
DefaultMonoMtx.mixMonoSampleData(m_head->clientMixInfo(), scratchPost.data(), smx._getMergeBuf<T>(oDone), oDone);
} }
}
size_t oDone = soxr_output(m_src, scratchPre.data(), frames); return oDone;
if (oDone)
{
if (m_sendMatrices.size())
{
for (auto& mtx : m_sendMatrices)
{
AudioSubmix& smx = *reinterpret_cast<AudioSubmix*>(mtx.first);
m_cb->routeAudio(oDone, 1, dt, smx.m_busId, scratchPre.data(), scratchPost.data());
mtx.second.mixMonoSampleData(m_head->clientMixInfo(), scratchPost.data(),
smx._getMergeBuf<T>(oDone), oDone);
}
}
else
{
AudioSubmix& smx = *m_head->m_mainSubmix;
m_cb->routeAudio(oDone, 1, dt, m_head->m_mainSubmix->m_busId, scratchPre.data(), scratchPost.data());
DefaultMonoMtx.mixMonoSampleData(m_head->clientMixInfo(), scratchPost.data(),
smx._getMergeBuf<T>(oDone), oDone);
}
}
return oDone;
} }
void AudioVoiceMono::resetChannelLevels() void AudioVoiceMono::resetChannelLevels() {
{ m_head->m_submixesDirty = true;
m_head->m_submixesDirty = true; m_sendMatrices.clear();
m_sendMatrices.clear();
} }
void AudioVoiceMono::setMonoChannelLevels(IAudioSubmix* submix, const float coefs[8], bool slew) void AudioVoiceMono::setMonoChannelLevels(IAudioSubmix* submix, const float coefs[8], bool slew) {
{ if (!submix)
if (!submix) submix = m_head->m_mainSubmix.get();
submix = m_head->m_mainSubmix.get();
auto search = m_sendMatrices.find(submix); auto search = m_sendMatrices.find(submix);
if (search == m_sendMatrices.cend()) if (search == m_sendMatrices.cend())
search = m_sendMatrices.emplace(submix, AudioMatrixMono{}).first; search = m_sendMatrices.emplace(submix, AudioMatrixMono{}).first;
search->second.setMatrixCoefficients(coefs, slew ? m_head->m_5msFrames : 0); search->second.setMatrixCoefficients(coefs, slew ? m_head->m_5msFrames : 0);
} }
void AudioVoiceMono::setStereoChannelLevels(IAudioSubmix* submix, const float coefs[8][2], bool slew) void AudioVoiceMono::setStereoChannelLevels(IAudioSubmix* submix, const float coefs[8][2], bool slew) {
{ float newCoefs[8] = {coefs[0][0], coefs[1][0], coefs[2][0], coefs[3][0],
float newCoefs[8] = coefs[4][0], coefs[5][0], coefs[6][0], coefs[7][0]};
{
coefs[0][0],
coefs[1][0],
coefs[2][0],
coefs[3][0],
coefs[4][0],
coefs[5][0],
coefs[6][0],
coefs[7][0]
};
if (!submix) if (!submix)
submix = m_head->m_mainSubmix.get(); submix = m_head->m_mainSubmix.get();
auto search = m_sendMatrices.find(submix); auto search = m_sendMatrices.find(submix);
if (search == m_sendMatrices.cend()) if (search == m_sendMatrices.cend())
search = m_sendMatrices.emplace(submix, AudioMatrixMono{}).first; search = m_sendMatrices.emplace(submix, AudioMatrixMono{}).first;
search->second.setMatrixCoefficients(newCoefs, slew ? m_head->m_5msFrames : 0); search->second.setMatrixCoefficients(newCoefs, slew ? m_head->m_5msFrames : 0);
} }
AudioVoiceStereo::AudioVoiceStereo(BaseAudioVoiceEngine& root, IAudioVoiceCallback* cb, AudioVoiceStereo::AudioVoiceStereo(BaseAudioVoiceEngine& root, IAudioVoiceCallback* cb, double sampleRate,
double sampleRate, bool dynamicRate) bool dynamicRate)
: AudioVoice(root, cb, dynamicRate) : AudioVoice(root, cb, dynamicRate) {
{ _resetSampleRate(sampleRate);
_resetSampleRate(sampleRate);
} }
void AudioVoiceStereo::_resetSampleRate(double sampleRate) void AudioVoiceStereo::_resetSampleRate(double sampleRate) {
{ soxr_delete(m_src);
soxr_delete(m_src);
double rateOut = m_head->mixInfo().m_sampleRate; double rateOut = m_head->mixInfo().m_sampleRate;
soxr_datatype_t formatOut = m_head->mixInfo().m_sampleFormat; soxr_datatype_t formatOut = m_head->mixInfo().m_sampleFormat;
soxr_io_spec_t ioSpec = soxr_io_spec(SOXR_INT16_I, formatOut); soxr_io_spec_t ioSpec = soxr_io_spec(SOXR_INT16_I, formatOut);
soxr_quality_spec_t qSpec = soxr_quality_spec(SOXR_20_BITQ, m_dynamicRate ? SOXR_VR : 0); soxr_quality_spec_t qSpec = soxr_quality_spec(SOXR_20_BITQ, m_dynamicRate ? SOXR_VR : 0);
soxr_error_t err; soxr_error_t err;
m_src = soxr_create(sampleRate, rateOut, 2, m_src = soxr_create(sampleRate, rateOut, 2, &err, &ioSpec, &qSpec, nullptr);
&err, &ioSpec, &qSpec, nullptr);
if (!m_src) if (!m_src) {
{ Log.report(logvisor::Fatal, "unable to create soxr resampler: %s", soxr_strerror(err));
Log.report(logvisor::Fatal, "unable to create soxr resampler: %s", soxr_strerror(err));
m_resetSampleRate = false;
return;
}
m_sampleRateIn = sampleRate;
m_sampleRateOut = rateOut;
m_sampleRatio = m_sampleRateIn / m_sampleRateOut;
soxr_set_input_fn(m_src, soxr_input_fn_t(SRCCallback), this, 0);
_setPitchRatio(m_pitchRatio, false);
m_resetSampleRate = false; m_resetSampleRate = false;
return;
}
m_sampleRateIn = sampleRate;
m_sampleRateOut = rateOut;
m_sampleRatio = m_sampleRateIn / m_sampleRateOut;
soxr_set_input_fn(m_src, soxr_input_fn_t(SRCCallback), this, 0);
_setPitchRatio(m_pitchRatio, false);
m_resetSampleRate = false;
} }
size_t AudioVoiceStereo::SRCCallback(AudioVoiceStereo* ctx, int16_t** data, size_t frames) size_t AudioVoiceStereo::SRCCallback(AudioVoiceStereo* ctx, int16_t** data, size_t frames) {
{ std::vector<int16_t>& scratchIn = ctx->m_head->m_scratchIn;
std::vector<int16_t>& scratchIn = ctx->m_head->m_scratchIn; size_t samples = frames * 2;
size_t samples = frames * 2; if (scratchIn.size() < samples)
if (scratchIn.size() < samples) scratchIn.resize(samples);
scratchIn.resize(samples); *data = scratchIn.data();
*data = scratchIn.data(); if (ctx->m_silentOut) {
if (ctx->m_silentOut) memset(scratchIn.data(), 0, samples * 2);
{ return frames;
memset(scratchIn.data(), 0, samples * 2); } else
return frames; return ctx->m_cb->supplyAudio(*ctx, frames, scratchIn.data());
}
else
return ctx->m_cb->supplyAudio(*ctx, frames, scratchIn.data());
} }
bool AudioVoiceStereo::isSilent() const bool AudioVoiceStereo::isSilent() const {
{ if (m_sendMatrices.size()) {
if (m_sendMatrices.size()) for (auto& mtx : m_sendMatrices)
{ if (!mtx.second.isSilent())
for (auto& mtx : m_sendMatrices) return false;
if (!mtx.second.isSilent()) return true;
return false; } else {
return true; return DefaultStereoMtx.isSilent();
} }
else
{
return DefaultStereoMtx.isSilent();
}
} }
template <typename T> template <typename T>
size_t AudioVoiceStereo::_pumpAndMix(size_t frames) size_t AudioVoiceStereo::_pumpAndMix(size_t frames) {
{ size_t samples = frames * 2;
size_t samples = frames * 2;
auto& scratchPre = m_head->_getScratchPre<T>(); auto& scratchPre = m_head->_getScratchPre<T>();
if (scratchPre.size() < samples) if (scratchPre.size() < samples)
scratchPre.resize(samples + 4); scratchPre.resize(samples + 4);
auto& scratchPost = m_head->_getScratchPost<T>(); auto& scratchPost = m_head->_getScratchPost<T>();
if (scratchPost.size() < samples) if (scratchPost.size() < samples)
scratchPost.resize(samples + 4); scratchPost.resize(samples + 4);
double dt = frames / m_sampleRateOut; double dt = frames / m_sampleRateOut;
m_cb->preSupplyAudio(*this, dt); m_cb->preSupplyAudio(*this, dt);
_midUpdate(); _midUpdate();
if (isSilent()) if (isSilent()) {
{ int16_t* dummy;
int16_t* dummy; SRCCallback(this, &dummy, size_t(std::ceil(frames * m_sampleRatio)));
SRCCallback(this, &dummy, size_t(std::ceil(frames * m_sampleRatio))); return 0;
return 0; }
size_t oDone = soxr_output(m_src, scratchPre.data(), frames);
if (oDone) {
if (m_sendMatrices.size()) {
for (auto& mtx : m_sendMatrices) {
AudioSubmix& smx = *reinterpret_cast<AudioSubmix*>(mtx.first);
m_cb->routeAudio(oDone, 2, dt, smx.m_busId, scratchPre.data(), scratchPost.data());
mtx.second.mixStereoSampleData(m_head->clientMixInfo(), scratchPost.data(), smx._getMergeBuf<T>(oDone), oDone);
}
} else {
AudioSubmix& smx = *m_head->m_mainSubmix;
m_cb->routeAudio(oDone, 2, dt, m_head->m_mainSubmix->m_busId, scratchPre.data(), scratchPost.data());
DefaultStereoMtx.mixStereoSampleData(m_head->clientMixInfo(), scratchPost.data(), smx._getMergeBuf<T>(oDone),
oDone);
} }
}
size_t oDone = soxr_output(m_src, scratchPre.data(), frames);
if (oDone) return oDone;
{
if (m_sendMatrices.size())
{
for (auto& mtx : m_sendMatrices)
{
AudioSubmix& smx = *reinterpret_cast<AudioSubmix*>(mtx.first);
m_cb->routeAudio(oDone, 2, dt, smx.m_busId, scratchPre.data(), scratchPost.data());
mtx.second.mixStereoSampleData(m_head->clientMixInfo(), scratchPost.data(),
smx._getMergeBuf<T>(oDone), oDone);
}
}
else
{
AudioSubmix& smx = *m_head->m_mainSubmix;
m_cb->routeAudio(oDone, 2, dt, m_head->m_mainSubmix->m_busId, scratchPre.data(), scratchPost.data());
DefaultStereoMtx.mixStereoSampleData(m_head->clientMixInfo(), scratchPost.data(),
smx._getMergeBuf<T>(oDone), oDone);
}
}
return oDone;
} }
void AudioVoiceStereo::resetChannelLevels() void AudioVoiceStereo::resetChannelLevels() {
{ m_head->m_submixesDirty = true;
m_head->m_submixesDirty = true; m_sendMatrices.clear();
m_sendMatrices.clear();
} }
void AudioVoiceStereo::setMonoChannelLevels(IAudioSubmix* submix, const float coefs[8], bool slew) void AudioVoiceStereo::setMonoChannelLevels(IAudioSubmix* submix, const float coefs[8], bool slew) {
{ float newCoefs[8][2] = {{coefs[0], coefs[0]}, {coefs[1], coefs[1]}, {coefs[2], coefs[2]}, {coefs[3], coefs[3]},
float newCoefs[8][2] = {coefs[4], coefs[4]}, {coefs[5], coefs[5]}, {coefs[6], coefs[6]}, {coefs[7], coefs[7]}};
{
{coefs[0], coefs[0]},
{coefs[1], coefs[1]},
{coefs[2], coefs[2]},
{coefs[3], coefs[3]},
{coefs[4], coefs[4]},
{coefs[5], coefs[5]},
{coefs[6], coefs[6]},
{coefs[7], coefs[7]}
};
if (!submix) if (!submix)
submix = m_head->m_mainSubmix.get(); submix = m_head->m_mainSubmix.get();
auto search = m_sendMatrices.find(submix); auto search = m_sendMatrices.find(submix);
if (search == m_sendMatrices.cend()) if (search == m_sendMatrices.cend())
search = m_sendMatrices.emplace(submix, AudioMatrixStereo{}).first; search = m_sendMatrices.emplace(submix, AudioMatrixStereo{}).first;
search->second.setMatrixCoefficients(newCoefs, slew ? m_head->m_5msFrames : 0); search->second.setMatrixCoefficients(newCoefs, slew ? m_head->m_5msFrames : 0);
} }
void AudioVoiceStereo::setStereoChannelLevels(IAudioSubmix* submix, const float coefs[8][2], bool slew) void AudioVoiceStereo::setStereoChannelLevels(IAudioSubmix* submix, const float coefs[8][2], bool slew) {
{ if (!submix)
if (!submix) submix = m_head->m_mainSubmix.get();
submix = m_head->m_mainSubmix.get();
auto search = m_sendMatrices.find(submix); auto search = m_sendMatrices.find(submix);
if (search == m_sendMatrices.cend()) if (search == m_sendMatrices.cend())
search = m_sendMatrices.emplace(submix, AudioMatrixStereo{}).first; search = m_sendMatrices.emplace(submix, AudioMatrixStereo{}).first;
search->second.setMatrixCoefficients(coefs, slew ? m_head->m_5msFrames : 0); search->second.setMatrixCoefficients(coefs, slew ? m_head->m_5msFrames : 0);
} }
} } // namespace boo

View File

@ -12,121 +12,124 @@ struct AudioUnitVoiceEngine;
struct VSTVoiceEngine; struct VSTVoiceEngine;
struct WAVOutVoiceEngine; struct WAVOutVoiceEngine;
namespace boo namespace boo {
{
class BaseAudioVoiceEngine; class BaseAudioVoiceEngine;
struct AudioVoiceEngineMixInfo; struct AudioVoiceEngineMixInfo;
struct IAudioSubmix; struct IAudioSubmix;
class AudioVoice : public ListNode<AudioVoice, BaseAudioVoiceEngine*, IAudioVoice> class AudioVoice : public ListNode<AudioVoice, BaseAudioVoiceEngine*, IAudioVoice> {
{ friend class BaseAudioVoiceEngine;
friend class BaseAudioVoiceEngine; friend class AudioSubmix;
friend class AudioSubmix; friend struct WASAPIAudioVoiceEngine;
friend struct WASAPIAudioVoiceEngine; friend struct ::AudioUnitVoiceEngine;
friend struct ::AudioUnitVoiceEngine; friend struct ::VSTVoiceEngine;
friend struct ::VSTVoiceEngine; friend struct ::WAVOutVoiceEngine;
friend struct ::WAVOutVoiceEngine;
protected: protected:
/* Callback (audio source) */ /* Callback (audio source) */
IAudioVoiceCallback* m_cb; IAudioVoiceCallback* m_cb;
/* Sample-rate converter */ /* Sample-rate converter */
soxr_t m_src = nullptr; soxr_t m_src = nullptr;
double m_sampleRateIn; double m_sampleRateIn;
double m_sampleRateOut; double m_sampleRateOut;
bool m_dynamicRate; bool m_dynamicRate;
/* Running bool */ /* Running bool */
bool m_running = false; bool m_running = false;
/* Deferred sample-rate reset */ /* Deferred sample-rate reset */
bool m_resetSampleRate = false; bool m_resetSampleRate = false;
double m_deferredSampleRate; double m_deferredSampleRate;
virtual void _resetSampleRate(double sampleRate)=0; virtual void _resetSampleRate(double sampleRate) = 0;
/* Deferred pitch ratio set */ /* Deferred pitch ratio set */
bool m_setPitchRatio = false; bool m_setPitchRatio = false;
double m_pitchRatio = 1.0; double m_pitchRatio = 1.0;
double m_sampleRatio = 1.0; double m_sampleRatio = 1.0;
bool m_slew = false; bool m_slew = false;
void _setPitchRatio(double ratio, bool slew); void _setPitchRatio(double ratio, bool slew);
/* Mid-pump update */ /* Mid-pump update */
void _midUpdate(); void _midUpdate();
virtual size_t pumpAndMix16(size_t frames)=0; virtual size_t pumpAndMix16(size_t frames) = 0;
virtual size_t pumpAndMix32(size_t frames)=0; virtual size_t pumpAndMix32(size_t frames) = 0;
virtual size_t pumpAndMixFlt(size_t frames)=0; virtual size_t pumpAndMixFlt(size_t frames) = 0;
template <typename T> size_t pumpAndMix(size_t frames); template <typename T>
size_t pumpAndMix(size_t frames);
AudioVoice(BaseAudioVoiceEngine& root, IAudioVoiceCallback* cb, bool dynamicRate); AudioVoice(BaseAudioVoiceEngine& root, IAudioVoiceCallback* cb, bool dynamicRate);
public: public:
static AudioVoice*& _getHeadPtr(BaseAudioVoiceEngine* head); static AudioVoice*& _getHeadPtr(BaseAudioVoiceEngine* head);
static std::unique_lock<std::recursive_mutex> _getHeadLock(BaseAudioVoiceEngine* head); static std::unique_lock<std::recursive_mutex> _getHeadLock(BaseAudioVoiceEngine* head);
std::unique_lock<std::recursive_mutex> destructorLock(); std::unique_lock<std::recursive_mutex> destructorLock();
~AudioVoice(); ~AudioVoice();
void resetSampleRate(double sampleRate); void resetSampleRate(double sampleRate);
void setPitchRatio(double ratio, bool slew); void setPitchRatio(double ratio, bool slew);
void start(); void start();
void stop(); void stop();
double getSampleRateIn() const {return m_sampleRateIn;} double getSampleRateIn() const { return m_sampleRateIn; }
double getSampleRateOut() const {return m_sampleRateOut;} double getSampleRateOut() const { return m_sampleRateOut; }
};
template <> inline size_t AudioVoice::pumpAndMix<int16_t>(size_t frames) { return pumpAndMix16(frames); }
template <> inline size_t AudioVoice::pumpAndMix<int32_t>(size_t frames) { return pumpAndMix32(frames); }
template <> inline size_t AudioVoice::pumpAndMix<float>(size_t frames) { return pumpAndMixFlt(frames); }
class AudioVoiceMono : public AudioVoice
{
std::unordered_map<IAudioSubmix*, AudioMatrixMono> m_sendMatrices;
bool m_silentOut = false;
void _resetSampleRate(double sampleRate);
static size_t SRCCallback(AudioVoiceMono* ctx,
int16_t** data, size_t requestedLen);
bool isSilent() const;
template <typename T> size_t _pumpAndMix(size_t frames);
size_t pumpAndMix16(size_t frames) { return _pumpAndMix<int16_t>(frames); }
size_t pumpAndMix32(size_t frames) { return _pumpAndMix<int32_t>(frames); }
size_t pumpAndMixFlt(size_t frames) { return _pumpAndMix<float>(frames); }
public:
AudioVoiceMono(BaseAudioVoiceEngine& root, IAudioVoiceCallback* cb,
double sampleRate, bool dynamicRate);
void resetChannelLevels();
void setMonoChannelLevels(IAudioSubmix* submix, const float coefs[8], bool slew);
void setStereoChannelLevels(IAudioSubmix* submix, const float coefs[8][2], bool slew);
};
class AudioVoiceStereo : public AudioVoice
{
std::unordered_map<IAudioSubmix*, AudioMatrixStereo> m_sendMatrices;
bool m_silentOut = false;
void _resetSampleRate(double sampleRate);
static size_t SRCCallback(AudioVoiceStereo* ctx,
int16_t** data, size_t requestedLen);
bool isSilent() const;
template <typename T> size_t _pumpAndMix(size_t frames);
size_t pumpAndMix16(size_t frames) { return _pumpAndMix<int16_t>(frames); }
size_t pumpAndMix32(size_t frames) { return _pumpAndMix<int32_t>(frames); }
size_t pumpAndMixFlt(size_t frames) { return _pumpAndMix<float>(frames); }
public:
AudioVoiceStereo(BaseAudioVoiceEngine& root, IAudioVoiceCallback* cb,
double sampleRate, bool dynamicRate);
void resetChannelLevels();
void setMonoChannelLevels(IAudioSubmix* submix, const float coefs[8], bool slew);
void setStereoChannelLevels(IAudioSubmix* submix, const float coefs[8][2], bool slew);
}; };
template <>
inline size_t AudioVoice::pumpAndMix<int16_t>(size_t frames) {
return pumpAndMix16(frames);
}
template <>
inline size_t AudioVoice::pumpAndMix<int32_t>(size_t frames) {
return pumpAndMix32(frames);
}
template <>
inline size_t AudioVoice::pumpAndMix<float>(size_t frames) {
return pumpAndMixFlt(frames);
} }
class AudioVoiceMono : public AudioVoice {
std::unordered_map<IAudioSubmix*, AudioMatrixMono> m_sendMatrices;
bool m_silentOut = false;
void _resetSampleRate(double sampleRate);
static size_t SRCCallback(AudioVoiceMono* ctx, int16_t** data, size_t requestedLen);
bool isSilent() const;
template <typename T>
size_t _pumpAndMix(size_t frames);
size_t pumpAndMix16(size_t frames) { return _pumpAndMix<int16_t>(frames); }
size_t pumpAndMix32(size_t frames) { return _pumpAndMix<int32_t>(frames); }
size_t pumpAndMixFlt(size_t frames) { return _pumpAndMix<float>(frames); }
public:
AudioVoiceMono(BaseAudioVoiceEngine& root, IAudioVoiceCallback* cb, double sampleRate, bool dynamicRate);
void resetChannelLevels();
void setMonoChannelLevels(IAudioSubmix* submix, const float coefs[8], bool slew);
void setStereoChannelLevels(IAudioSubmix* submix, const float coefs[8][2], bool slew);
};
class AudioVoiceStereo : public AudioVoice {
std::unordered_map<IAudioSubmix*, AudioMatrixStereo> m_sendMatrices;
bool m_silentOut = false;
void _resetSampleRate(double sampleRate);
static size_t SRCCallback(AudioVoiceStereo* ctx, int16_t** data, size_t requestedLen);
bool isSilent() const;
template <typename T>
size_t _pumpAndMix(size_t frames);
size_t pumpAndMix16(size_t frames) { return _pumpAndMix<int16_t>(frames); }
size_t pumpAndMix32(size_t frames) { return _pumpAndMix<int32_t>(frames); }
size_t pumpAndMixFlt(size_t frames) { return _pumpAndMix<float>(frames); }
public:
AudioVoiceStereo(BaseAudioVoiceEngine& root, IAudioVoiceCallback* cb, double sampleRate, bool dynamicRate);
void resetChannelLevels();
void setMonoChannelLevels(IAudioSubmix* submix, const float coefs[8], bool slew);
void setStereoChannelLevels(IAudioSubmix* submix, const float coefs[8][2], bool slew);
};
} // namespace boo

View File

@ -1,156 +1,123 @@
#include "AudioVoiceEngine.hpp" #include "AudioVoiceEngine.hpp"
#include <cassert> #include <cassert>
namespace boo namespace boo {
{
BaseAudioVoiceEngine::~BaseAudioVoiceEngine() BaseAudioVoiceEngine::~BaseAudioVoiceEngine() {
{ m_mainSubmix.reset();
m_mainSubmix.reset(); assert(m_voiceHead == nullptr && "Dangling voices detected");
assert(m_voiceHead == nullptr && "Dangling voices detected"); assert(m_submixHead == nullptr && "Dangling submixes detected");
assert(m_submixHead == nullptr && "Dangling submixes detected");
} }
template <typename T> template <typename T>
void BaseAudioVoiceEngine::_pumpAndMixVoices(size_t frames, T* dataOut) void BaseAudioVoiceEngine::_pumpAndMixVoices(size_t frames, T* dataOut) {
{ if (dataOut)
if (dataOut) memset(dataOut, 0, sizeof(T) * frames * m_mixInfo.m_channelMap.m_channelCount);
memset(dataOut, 0, sizeof(T) * frames * m_mixInfo.m_channelMap.m_channelCount);
if (m_ltRtProcessing) {
size_t sampleCount = m_5msFrames * 5;
if (_getLtRtIn<T>().size() < sampleCount)
_getLtRtIn<T>().resize(sampleCount);
m_mainSubmix->_getRedirect<T>() = _getLtRtIn<T>().data();
} else {
m_mainSubmix->_getRedirect<T>() = dataOut;
}
if (m_submixesDirty) {
m_linearizedSubmixes = m_mainSubmix->_linearizeC3();
m_submixesDirty = false;
}
size_t remFrames = frames;
while (remFrames) {
size_t thisFrames;
if (remFrames < m_5msFrames) {
thisFrames = remFrames;
if (m_engineCallback)
m_engineCallback->on5MsInterval(*this, thisFrames / double(m_5msFrames) * 5.0 / 1000.0);
} else {
thisFrames = m_5msFrames;
if (m_engineCallback)
m_engineCallback->on5MsInterval(*this, 5.0 / 1000.0);
}
if (m_ltRtProcessing) if (m_ltRtProcessing)
{ std::fill(_getLtRtIn<T>().begin(), _getLtRtIn<T>().end(), 0.f);
size_t sampleCount = m_5msFrames * 5;
if (_getLtRtIn<T>().size() < sampleCount) for (auto it = m_linearizedSubmixes.rbegin(); it != m_linearizedSubmixes.rend(); ++it)
_getLtRtIn<T>().resize(sampleCount); (*it)->_zeroFill<T>();
m_mainSubmix->_getRedirect<T>() = _getLtRtIn<T>().data();
} if (m_voiceHead)
else for (AudioVoice& vox : *m_voiceHead)
{ if (vox.m_running)
m_mainSubmix->_getRedirect<T>() = dataOut; vox.pumpAndMix<T>(thisFrames);
for (auto it = m_linearizedSubmixes.rbegin(); it != m_linearizedSubmixes.rend(); ++it)
(*it)->_pumpAndMix<T>(thisFrames);
remFrames -= thisFrames;
if (!dataOut)
continue;
if (m_ltRtProcessing) {
m_ltRtProcessing->Process(_getLtRtIn<T>().data(), dataOut, int(thisFrames));
m_mainSubmix->_getRedirect<T>() = _getLtRtIn<T>().data();
} }
if (m_submixesDirty) size_t sampleCount = thisFrames * m_mixInfo.m_channelMap.m_channelCount;
{ for (size_t i = 0; i < sampleCount; ++i)
m_linearizedSubmixes = m_mainSubmix->_linearizeC3(); dataOut[i] *= m_totalVol;
m_submixesDirty = false;
}
size_t remFrames = frames; dataOut += sampleCount;
while (remFrames) }
{
size_t thisFrames;
if (remFrames < m_5msFrames)
{
thisFrames = remFrames;
if (m_engineCallback)
m_engineCallback->on5MsInterval(*this, thisFrames / double(m_5msFrames) * 5.0 / 1000.0);
}
else
{
thisFrames = m_5msFrames;
if (m_engineCallback)
m_engineCallback->on5MsInterval(*this, 5.0 / 1000.0);
}
if (m_ltRtProcessing) if (m_engineCallback)
std::fill(_getLtRtIn<T>().begin(), _getLtRtIn<T>().end(), 0.f); m_engineCallback->onPumpCycleComplete(*this);
for (auto it = m_linearizedSubmixes.rbegin() ; it != m_linearizedSubmixes.rend() ; ++it)
(*it)->_zeroFill<T>();
if (m_voiceHead)
for (AudioVoice& vox : *m_voiceHead)
if (vox.m_running)
vox.pumpAndMix<T>(thisFrames);
for (auto it = m_linearizedSubmixes.rbegin() ; it != m_linearizedSubmixes.rend() ; ++it)
(*it)->_pumpAndMix<T>(thisFrames);
remFrames -= thisFrames;
if (!dataOut)
continue;
if (m_ltRtProcessing)
{
m_ltRtProcessing->Process(_getLtRtIn<T>().data(), dataOut, int(thisFrames));
m_mainSubmix->_getRedirect<T>() = _getLtRtIn<T>().data();
}
size_t sampleCount = thisFrames * m_mixInfo.m_channelMap.m_channelCount;
for (size_t i=0 ; i<sampleCount ; ++i)
dataOut[i] *= m_totalVol;
dataOut += sampleCount;
}
if (m_engineCallback)
m_engineCallback->onPumpCycleComplete(*this);
} }
template void BaseAudioVoiceEngine::_pumpAndMixVoices<int16_t>(size_t frames, int16_t* dataOut); template void BaseAudioVoiceEngine::_pumpAndMixVoices<int16_t>(size_t frames, int16_t* dataOut);
template void BaseAudioVoiceEngine::_pumpAndMixVoices<int32_t>(size_t frames, int32_t* dataOut); template void BaseAudioVoiceEngine::_pumpAndMixVoices<int32_t>(size_t frames, int32_t* dataOut);
template void BaseAudioVoiceEngine::_pumpAndMixVoices<float>(size_t frames, float* dataOut); template void BaseAudioVoiceEngine::_pumpAndMixVoices<float>(size_t frames, float* dataOut);
void BaseAudioVoiceEngine::_resetSampleRate() void BaseAudioVoiceEngine::_resetSampleRate() {
{ if (m_voiceHead)
if (m_voiceHead) for (boo::AudioVoice& vox : *m_voiceHead)
for (boo::AudioVoice& vox : *m_voiceHead) vox._resetSampleRate(vox.m_sampleRateIn);
vox._resetSampleRate(vox.m_sampleRateIn); if (m_submixHead)
if (m_submixHead) for (boo::AudioSubmix& smx : *m_submixHead)
for (boo::AudioSubmix& smx : *m_submixHead) smx._resetOutputSampleRate();
smx._resetOutputSampleRate();
} }
ObjToken<IAudioVoice> ObjToken<IAudioVoice> BaseAudioVoiceEngine::allocateNewMonoVoice(double sampleRate, IAudioVoiceCallback* cb,
BaseAudioVoiceEngine::allocateNewMonoVoice(double sampleRate, bool dynamicPitch) {
IAudioVoiceCallback* cb, return {new AudioVoiceMono(*this, cb, sampleRate, dynamicPitch)};
bool dynamicPitch)
{
return {new AudioVoiceMono(*this, cb, sampleRate, dynamicPitch)};
} }
ObjToken<IAudioVoice> ObjToken<IAudioVoice> BaseAudioVoiceEngine::allocateNewStereoVoice(double sampleRate, IAudioVoiceCallback* cb,
BaseAudioVoiceEngine::allocateNewStereoVoice(double sampleRate, bool dynamicPitch) {
IAudioVoiceCallback* cb, return {new AudioVoiceStereo(*this, cb, sampleRate, dynamicPitch)};
bool dynamicPitch)
{
return {new AudioVoiceStereo(*this, cb, sampleRate, dynamicPitch)};
} }
ObjToken<IAudioSubmix> ObjToken<IAudioSubmix> BaseAudioVoiceEngine::allocateNewSubmix(bool mainOut, IAudioSubmixCallback* cb, int busId) {
BaseAudioVoiceEngine::allocateNewSubmix(bool mainOut, IAudioSubmixCallback* cb, int busId) return {new AudioSubmix(*this, cb, busId, mainOut)};
{
return {new AudioSubmix(*this, cb, busId, mainOut)};
} }
void BaseAudioVoiceEngine::setCallbackInterface(IAudioVoiceEngineCallback* cb) void BaseAudioVoiceEngine::setCallbackInterface(IAudioVoiceEngineCallback* cb) { m_engineCallback = cb; }
{
m_engineCallback = cb; void BaseAudioVoiceEngine::setVolume(float vol) { m_totalVol = vol; }
bool BaseAudioVoiceEngine::enableLtRt(bool enable) {
if (enable && m_mixInfo.m_channelMap.m_channelCount == 2 && m_mixInfo.m_channels == AudioChannelSet::Stereo)
m_ltRtProcessing = std::make_unique<LtRtProcessing>(m_5msFrames, m_mixInfo);
else
m_ltRtProcessing.reset();
return m_ltRtProcessing.operator bool();
} }
void BaseAudioVoiceEngine::setVolume(float vol) const AudioVoiceEngineMixInfo& BaseAudioVoiceEngine::mixInfo() const { return m_mixInfo; }
{
m_totalVol = vol; const AudioVoiceEngineMixInfo& BaseAudioVoiceEngine::clientMixInfo() const {
return m_ltRtProcessing ? m_ltRtProcessing->inMixInfo() : m_mixInfo;
} }
bool BaseAudioVoiceEngine::enableLtRt(bool enable) } // namespace boo
{
if (enable && m_mixInfo.m_channelMap.m_channelCount == 2 &&
m_mixInfo.m_channels == AudioChannelSet::Stereo)
m_ltRtProcessing = std::make_unique<LtRtProcessing>(m_5msFrames, m_mixInfo);
else
m_ltRtProcessing.reset();
return m_ltRtProcessing.operator bool();
}
const AudioVoiceEngineMixInfo& BaseAudioVoiceEngine::mixInfo() const
{
return m_mixInfo;
}
const AudioVoiceEngineMixInfo& BaseAudioVoiceEngine::clientMixInfo() const
{
return m_ltRtProcessing ? m_ltRtProcessing->inMixInfo() : m_mixInfo;
}
}

View File

@ -8,87 +8,110 @@
#include <functional> #include <functional>
#include <mutex> #include <mutex>
namespace boo namespace boo {
{
/** Base class for managing mixing and sample-rate-conversion amongst active voices */ /** Base class for managing mixing and sample-rate-conversion amongst active voices */
class BaseAudioVoiceEngine : public IAudioVoiceEngine class BaseAudioVoiceEngine : public IAudioVoiceEngine {
{
protected: protected:
friend class AudioVoice; friend class AudioVoice;
friend class AudioSubmix; friend class AudioSubmix;
friend class AudioVoiceMono; friend class AudioVoiceMono;
friend class AudioVoiceStereo; friend class AudioVoiceStereo;
float m_totalVol = 1.f; float m_totalVol = 1.f;
AudioVoiceEngineMixInfo m_mixInfo; AudioVoiceEngineMixInfo m_mixInfo;
std::recursive_mutex m_dataMutex; std::recursive_mutex m_dataMutex;
AudioVoice* m_voiceHead = nullptr; AudioVoice* m_voiceHead = nullptr;
AudioSubmix* m_submixHead = nullptr; AudioSubmix* m_submixHead = nullptr;
size_t m_5msFrames = 0; size_t m_5msFrames = 0;
IAudioVoiceEngineCallback* m_engineCallback = nullptr; IAudioVoiceEngineCallback* m_engineCallback = nullptr;
/* Shared scratch buffers for accumulating audio data for resampling */ /* Shared scratch buffers for accumulating audio data for resampling */
std::vector<int16_t> m_scratchIn; std::vector<int16_t> m_scratchIn;
std::vector<int16_t> m_scratch16Pre; std::vector<int16_t> m_scratch16Pre;
std::vector<int32_t> m_scratch32Pre; std::vector<int32_t> m_scratch32Pre;
std::vector<float> m_scratchFltPre; std::vector<float> m_scratchFltPre;
template <typename T> std::vector<T>& _getScratchPre(); template <typename T>
std::vector<int16_t> m_scratch16Post; std::vector<T>& _getScratchPre();
std::vector<int32_t> m_scratch32Post; std::vector<int16_t> m_scratch16Post;
std::vector<float> m_scratchFltPost; std::vector<int32_t> m_scratch32Post;
template <typename T> std::vector<T>& _getScratchPost(); std::vector<float> m_scratchFltPost;
template <typename T>
std::vector<T>& _getScratchPost();
/* LtRt processing if enabled */ /* LtRt processing if enabled */
std::unique_ptr<LtRtProcessing> m_ltRtProcessing; std::unique_ptr<LtRtProcessing> m_ltRtProcessing;
std::vector<int16_t> m_ltRtIn16; std::vector<int16_t> m_ltRtIn16;
std::vector<int32_t> m_ltRtIn32; std::vector<int32_t> m_ltRtIn32;
std::vector<float> m_ltRtInFlt; std::vector<float> m_ltRtInFlt;
template <typename T> std::vector<T>& _getLtRtIn(); template <typename T>
std::vector<T>& _getLtRtIn();
std::unique_ptr<AudioSubmix> m_mainSubmix; std::unique_ptr<AudioSubmix> m_mainSubmix;
std::list<AudioSubmix*> m_linearizedSubmixes; std::list<AudioSubmix*> m_linearizedSubmixes;
bool m_submixesDirty = true; bool m_submixesDirty = true;
template <typename T> template <typename T>
void _pumpAndMixVoices(size_t frames, T* dataOut); void _pumpAndMixVoices(size_t frames, T* dataOut);
void _resetSampleRate(); void _resetSampleRate();
public: public:
BaseAudioVoiceEngine() : m_mainSubmix(std::make_unique<AudioSubmix>(*this, nullptr, -1, false)) {} BaseAudioVoiceEngine() : m_mainSubmix(std::make_unique<AudioSubmix>(*this, nullptr, -1, false)) {}
~BaseAudioVoiceEngine(); ~BaseAudioVoiceEngine();
ObjToken<IAudioVoice> allocateNewMonoVoice(double sampleRate, ObjToken<IAudioVoice> allocateNewMonoVoice(double sampleRate, IAudioVoiceCallback* cb, bool dynamicPitch = false);
IAudioVoiceCallback* cb,
bool dynamicPitch=false);
ObjToken<IAudioVoice> allocateNewStereoVoice(double sampleRate, ObjToken<IAudioVoice> allocateNewStereoVoice(double sampleRate, IAudioVoiceCallback* cb, bool dynamicPitch = false);
IAudioVoiceCallback* cb,
bool dynamicPitch=false);
ObjToken<IAudioSubmix> allocateNewSubmix(bool mainOut, IAudioSubmixCallback* cb, int busId); ObjToken<IAudioSubmix> allocateNewSubmix(bool mainOut, IAudioSubmixCallback* cb, int busId);
void setCallbackInterface(IAudioVoiceEngineCallback* cb); void setCallbackInterface(IAudioVoiceEngineCallback* cb);
void setVolume(float vol); void setVolume(float vol);
bool enableLtRt(bool enable); bool enableLtRt(bool enable);
const AudioVoiceEngineMixInfo& mixInfo() const; const AudioVoiceEngineMixInfo& mixInfo() const;
const AudioVoiceEngineMixInfo& clientMixInfo() const; const AudioVoiceEngineMixInfo& clientMixInfo() const;
AudioChannelSet getAvailableSet() {return clientMixInfo().m_channels;} AudioChannelSet getAvailableSet() { return clientMixInfo().m_channels; }
void pumpAndMixVoices() {} void pumpAndMixVoices() {}
size_t get5MsFrames() const {return m_5msFrames;} size_t get5MsFrames() const { return m_5msFrames; }
}; };
template <> inline std::vector<int16_t>& BaseAudioVoiceEngine::_getScratchPre<int16_t>() { return m_scratch16Pre; } template <>
template <> inline std::vector<int32_t>& BaseAudioVoiceEngine::_getScratchPre<int32_t>() { return m_scratch32Pre; } inline std::vector<int16_t>& BaseAudioVoiceEngine::_getScratchPre<int16_t>() {
template <> inline std::vector<float>& BaseAudioVoiceEngine::_getScratchPre<float>() { return m_scratchFltPre; } return m_scratch16Pre;
}
template <> inline std::vector<int16_t>& BaseAudioVoiceEngine::_getScratchPost<int16_t>() { return m_scratch16Post; } template <>
template <> inline std::vector<int32_t>& BaseAudioVoiceEngine::_getScratchPost<int32_t>() { return m_scratch32Post; } inline std::vector<int32_t>& BaseAudioVoiceEngine::_getScratchPre<int32_t>() {
template <> inline std::vector<float>& BaseAudioVoiceEngine::_getScratchPost<float>() { return m_scratchFltPost; } return m_scratch32Pre;
}
template <> inline std::vector<int16_t>& BaseAudioVoiceEngine::_getLtRtIn<int16_t>() { return m_ltRtIn16; } template <>
template <> inline std::vector<int32_t>& BaseAudioVoiceEngine::_getLtRtIn<int32_t>() { return m_ltRtIn32; } inline std::vector<float>& BaseAudioVoiceEngine::_getScratchPre<float>() {
template <> inline std::vector<float>& BaseAudioVoiceEngine::_getLtRtIn<float>() { return m_ltRtInFlt; } return m_scratchFltPre;
} }
template <>
inline std::vector<int16_t>& BaseAudioVoiceEngine::_getScratchPost<int16_t>() {
return m_scratch16Post;
}
template <>
inline std::vector<int32_t>& BaseAudioVoiceEngine::_getScratchPost<int32_t>() {
return m_scratch32Post;
}
template <>
inline std::vector<float>& BaseAudioVoiceEngine::_getScratchPost<float>() {
return m_scratchFltPost;
}
template <>
inline std::vector<int16_t>& BaseAudioVoiceEngine::_getLtRtIn<int16_t>() {
return m_ltRtIn16;
}
template <>
inline std::vector<int32_t>& BaseAudioVoiceEngine::_getLtRtIn<int32_t>() {
return m_ltRtIn32;
}
template <>
inline std::vector<float>& BaseAudioVoiceEngine::_getLtRtIn<float>() {
return m_ltRtInFlt;
}
} // namespace boo

View File

@ -4,19 +4,16 @@
#include "../Common.hpp" #include "../Common.hpp"
#include "boo/audiodev/IAudioVoice.hpp" #include "boo/audiodev/IAudioVoice.hpp"
namespace boo namespace boo {
{
/** Pertinent information from audio backend about optimal mixed-audio representation */ /** Pertinent information from audio backend about optimal mixed-audio representation */
struct AudioVoiceEngineMixInfo struct AudioVoiceEngineMixInfo {
{ double m_sampleRate = 32000.0;
double m_sampleRate = 32000.0; soxr_datatype_t m_sampleFormat = SOXR_FLOAT32_I;
soxr_datatype_t m_sampleFormat = SOXR_FLOAT32_I; unsigned m_bitsPerSample = 32;
unsigned m_bitsPerSample = 32; AudioChannelSet m_channels = AudioChannelSet::Stereo;
AudioChannelSet m_channels = AudioChannelSet::Stereo; ChannelMap m_channelMap = {2, {AudioChannel::FrontLeft, AudioChannel::FrontRight}};
ChannelMap m_channelMap = {2, {AudioChannel::FrontLeft, AudioChannel::FrontRight}}; size_t m_periodFrames = 160;
size_t m_periodFrames = 160;
}; };
} } // namespace boo

View File

@ -7,304 +7,259 @@
#include <alsa/asoundlib.h> #include <alsa/asoundlib.h>
#include <signal.h> #include <signal.h>
namespace boo namespace boo {
{
extern logvisor::Module ALSALog; extern logvisor::Module ALSALog;
static inline double TimespecToDouble(struct timespec& ts) static inline double TimespecToDouble(struct timespec& ts) { return ts.tv_sec + ts.tv_nsec / 1.0e9; }
{
return ts.tv_sec + ts.tv_nsec / 1.0e9;
}
struct LinuxMidi : BaseAudioVoiceEngine struct LinuxMidi : BaseAudioVoiceEngine {
{ std::unordered_map<std::string, IMIDIPort*> m_openHandles;
std::unordered_map<std::string, IMIDIPort*> m_openHandles; void _addOpenHandle(const char* name, IMIDIPort* port) { m_openHandles[name] = port; }
void _addOpenHandle(const char* name, IMIDIPort* port) void _removeOpenHandle(IMIDIPort* port) {
{ for (auto it = m_openHandles.begin(); it != m_openHandles.end();) {
m_openHandles[name] = port; if (it->second == port) {
it = m_openHandles.erase(it);
continue;
}
++it;
} }
void _removeOpenHandle(IMIDIPort* port) }
{
for (auto it = m_openHandles.begin(); it != m_openHandles.end();) ~LinuxMidi() {
{ for (auto& p : m_openHandles)
if (it->second == port) p.second->_disown();
{ }
it = m_openHandles.erase(it);
continue; std::vector<std::pair<std::string, std::string>> enumerateMIDIInputs() const {
} std::vector<std::pair<std::string, std::string>> ret;
++it; int status;
int card = -1; /* use -1 to prime the pump of iterating through card list */
if ((status = snd_card_next(&card)) < 0)
return {};
if (card < 0)
return {};
snd_rawmidi_info_t* info;
snd_rawmidi_info_malloc(&info);
while (card >= 0) {
snd_ctl_t* ctl;
char name[32];
int device = -1;
int status;
sprintf(name, "hw:%d", card);
if ((status = snd_ctl_open(&ctl, name, 0)) < 0)
continue;
do {
status = snd_ctl_rawmidi_next_device(ctl, &device);
if (status < 0)
break;
if (device >= 0) {
sprintf(name + strlen(name), ",%d", device);
auto search = m_openHandles.find(name);
if (search != m_openHandles.cend()) {
ret.push_back(std::make_pair(name, search->second->description()));
continue;
}
snd_rawmidi_t* midi;
if (!snd_rawmidi_open(&midi, nullptr, name, SND_RAWMIDI_NONBLOCK)) {
snd_rawmidi_info(midi, info);
ret.push_back(std::make_pair(name, snd_rawmidi_info_get_name(info)));
snd_rawmidi_close(midi);
}
} }
} while (device >= 0);
snd_ctl_close(ctl);
if ((status = snd_card_next(&card)) < 0)
break;
} }
~LinuxMidi() snd_rawmidi_info_free(info);
{
for (auto& p : m_openHandles)
p.second->_disown();
}
std::vector<std::pair<std::string, std::string>> enumerateMIDIInputs() const return ret;
{ }
std::vector<std::pair<std::string, std::string>> ret;
int status;
int card = -1; /* use -1 to prime the pump of iterating through card list */
if ((status = snd_card_next(&card)) < 0) bool supportsVirtualMIDIIn() const { return true; }
return {};
if (card < 0)
return {};
snd_rawmidi_info_t* info; static void MIDIFreeProc(void* midiStatus) { snd_rawmidi_status_free((snd_rawmidi_status_t*)midiStatus); }
snd_rawmidi_info_malloc(&info);
while (card >= 0) static void MIDIReceiveProc(snd_rawmidi_t* midi, const ReceiveFunctor& receiver) {
{ logvisor::RegisterThreadName("Boo MIDI");
snd_ctl_t *ctl; snd_rawmidi_status_t* midiStatus;
char name[32]; snd_rawmidi_status_malloc(&midiStatus);
int device = -1; pthread_cleanup_push(MIDIFreeProc, midiStatus);
int status;
sprintf(name, "hw:%d", card);
if ((status = snd_ctl_open(&ctl, name, 0)) < 0)
continue;
do { uint8_t buf[512];
status = snd_ctl_rawmidi_next_device(ctl, &device); while (true) {
if (status < 0) snd_htimestamp_t ts;
break; snd_rawmidi_status(midi, midiStatus);
if (device >= 0) snd_rawmidi_status_get_tstamp(midiStatus, &ts);
{ int rdBytes = snd_rawmidi_read(midi, buf, 512);
sprintf(name + strlen(name), ",%d", device); if (rdBytes < 0) {
auto search = m_openHandles.find(name); if (rdBytes != -EINTR) {
if (search != m_openHandles.cend()) ALSALog.report(logvisor::Error, "MIDI connection lost");
{ break;
ret.push_back(std::make_pair(name, search->second->description()));
continue;
}
snd_rawmidi_t* midi;
if (!snd_rawmidi_open(&midi, nullptr, name, SND_RAWMIDI_NONBLOCK))
{
snd_rawmidi_info(midi, info);
ret.push_back(std::make_pair(name, snd_rawmidi_info_get_name(info)));
snd_rawmidi_close(midi);
}
}
} while (device >= 0);
snd_ctl_close(ctl);
if ((status = snd_card_next(&card)) < 0)
break;
} }
continue;
}
snd_rawmidi_info_free(info); int oldtype;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldtype);
return ret; receiver(std::vector<uint8_t>(std::cbegin(buf), std::cbegin(buf) + rdBytes), TimespecToDouble(ts));
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldtype);
pthread_testcancel();
} }
bool supportsVirtualMIDIIn() const pthread_cleanup_pop(1);
{ }
return true;
struct MIDIIn : public IMIDIIn {
snd_rawmidi_t* m_midi;
std::thread m_midiThread;
MIDIIn(LinuxMidi* parent, snd_rawmidi_t* midi, bool virt, ReceiveFunctor&& receiver)
: IMIDIIn(parent, virt, std::move(receiver))
, m_midi(midi)
, m_midiThread(std::bind(MIDIReceiveProc, m_midi, m_receiver)) {}
~MIDIIn() {
if (m_parent)
static_cast<LinuxMidi*>(m_parent)->_removeOpenHandle(this);
pthread_cancel(m_midiThread.native_handle());
if (m_midiThread.joinable())
m_midiThread.join();
snd_rawmidi_close(m_midi);
} }
static void MIDIFreeProc(void* midiStatus) std::string description() const {
{ snd_rawmidi_info_t* info;
snd_rawmidi_status_free((snd_rawmidi_status_t*)midiStatus); snd_rawmidi_info_alloca(&info);
snd_rawmidi_info(m_midi, info);
std::string ret = snd_rawmidi_info_get_name(info);
return ret;
}
};
struct MIDIOut : public IMIDIOut {
snd_rawmidi_t* m_midi;
MIDIOut(LinuxMidi* parent, snd_rawmidi_t* midi, bool virt) : IMIDIOut(parent, virt), m_midi(midi) {}
~MIDIOut() {
if (m_parent)
static_cast<LinuxMidi*>(m_parent)->_removeOpenHandle(this);
snd_rawmidi_close(m_midi);
} }
static void MIDIReceiveProc(snd_rawmidi_t* midi, const ReceiveFunctor& receiver) std::string description() const {
{ snd_rawmidi_info_t* info;
logvisor::RegisterThreadName("Boo MIDI"); snd_rawmidi_info_alloca(&info);
snd_rawmidi_status_t* midiStatus; snd_rawmidi_info(m_midi, info);
snd_rawmidi_status_malloc(&midiStatus); std::string ret = snd_rawmidi_info_get_name(info);
pthread_cleanup_push(MIDIFreeProc, midiStatus); return ret;
uint8_t buf[512];
while (true)
{
snd_htimestamp_t ts;
snd_rawmidi_status(midi, midiStatus);
snd_rawmidi_status_get_tstamp(midiStatus, &ts);
int rdBytes = snd_rawmidi_read(midi, buf, 512);
if (rdBytes < 0)
{
if (rdBytes != -EINTR)
{
ALSALog.report(logvisor::Error, "MIDI connection lost");
break;
}
continue;
}
int oldtype;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldtype);
receiver(std::vector<uint8_t>(std::cbegin(buf), std::cbegin(buf) + rdBytes), TimespecToDouble(ts));
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldtype);
pthread_testcancel();
}
pthread_cleanup_pop(1);
} }
struct MIDIIn : public IMIDIIn size_t send(const void* buf, size_t len) const { return size_t(std::max(0l, snd_rawmidi_write(m_midi, buf, len))); }
{ };
snd_rawmidi_t* m_midi;
std::thread m_midiThread;
MIDIIn(LinuxMidi* parent, snd_rawmidi_t* midi, bool virt, ReceiveFunctor&& receiver) struct MIDIInOut : public IMIDIInOut {
: IMIDIIn(parent, virt, std::move(receiver)), m_midi(midi), snd_rawmidi_t* m_midiIn;
m_midiThread(std::bind(MIDIReceiveProc, m_midi, m_receiver)) {} snd_rawmidi_t* m_midiOut;
std::thread m_midiThread;
~MIDIIn() MIDIInOut(LinuxMidi* parent, snd_rawmidi_t* midiIn, snd_rawmidi_t* midiOut, bool virt, ReceiveFunctor&& receiver)
{ : IMIDIInOut(parent, virt, std::move(receiver))
if (m_parent) , m_midiIn(midiIn)
static_cast<LinuxMidi*>(m_parent)->_removeOpenHandle(this); , m_midiOut(midiOut)
pthread_cancel(m_midiThread.native_handle()); , m_midiThread(std::bind(MIDIReceiveProc, m_midiIn, m_receiver)) {}
if (m_midiThread.joinable())
m_midiThread.join();
snd_rawmidi_close(m_midi);
}
std::string description() const ~MIDIInOut() {
{ if (m_parent)
snd_rawmidi_info_t* info; static_cast<LinuxMidi*>(m_parent)->_removeOpenHandle(this);
snd_rawmidi_info_alloca(&info); pthread_cancel(m_midiThread.native_handle());
snd_rawmidi_info(m_midi, info); if (m_midiThread.joinable())
std::string ret = snd_rawmidi_info_get_name(info); m_midiThread.join();
return ret; snd_rawmidi_close(m_midiIn);
} snd_rawmidi_close(m_midiOut);
};
struct MIDIOut : public IMIDIOut
{
snd_rawmidi_t* m_midi;
MIDIOut(LinuxMidi* parent, snd_rawmidi_t* midi, bool virt)
: IMIDIOut(parent, virt), m_midi(midi) {}
~MIDIOut()
{
if (m_parent)
static_cast<LinuxMidi*>(m_parent)->_removeOpenHandle(this);
snd_rawmidi_close(m_midi);
}
std::string description() const
{
snd_rawmidi_info_t* info;
snd_rawmidi_info_alloca(&info);
snd_rawmidi_info(m_midi, info);
std::string ret = snd_rawmidi_info_get_name(info);
return ret;
}
size_t send(const void* buf, size_t len) const
{
return size_t(std::max(0l, snd_rawmidi_write(m_midi, buf, len)));
}
};
struct MIDIInOut : public IMIDIInOut
{
snd_rawmidi_t* m_midiIn;
snd_rawmidi_t* m_midiOut;
std::thread m_midiThread;
MIDIInOut(LinuxMidi* parent, snd_rawmidi_t* midiIn, snd_rawmidi_t* midiOut, bool virt, ReceiveFunctor&& receiver)
: IMIDIInOut(parent, virt, std::move(receiver)), m_midiIn(midiIn), m_midiOut(midiOut),
m_midiThread(std::bind(MIDIReceiveProc, m_midiIn, m_receiver)) {}
~MIDIInOut()
{
if (m_parent)
static_cast<LinuxMidi*>(m_parent)->_removeOpenHandle(this);
pthread_cancel(m_midiThread.native_handle());
if (m_midiThread.joinable())
m_midiThread.join();
snd_rawmidi_close(m_midiIn);
snd_rawmidi_close(m_midiOut);
}
std::string description() const
{
snd_rawmidi_info_t* info;
snd_rawmidi_info_alloca(&info);
snd_rawmidi_info(m_midiIn, info);
std::string ret = snd_rawmidi_info_get_name(info);
return ret;
}
size_t send(const void* buf, size_t len) const
{
return size_t(std::max(0l, snd_rawmidi_write(m_midiOut, buf, len)));
}
};
std::unique_ptr<IMIDIIn> newVirtualMIDIIn(ReceiveFunctor&& receiver)
{
int status;
snd_rawmidi_t* midi;
status = snd_rawmidi_open(&midi, nullptr, "virtual", 0);
if (status)
return {};
return std::make_unique<MIDIIn>(nullptr, midi, true, std::move(receiver));
} }
std::unique_ptr<IMIDIOut> newVirtualMIDIOut() std::string description() const {
{ snd_rawmidi_info_t* info;
int status; snd_rawmidi_info_alloca(&info);
snd_rawmidi_t* midi; snd_rawmidi_info(m_midiIn, info);
status = snd_rawmidi_open(nullptr, &midi, "virtual", 0); std::string ret = snd_rawmidi_info_get_name(info);
if (status) return ret;
return {};
return std::make_unique<MIDIOut>(nullptr, midi, true);
} }
std::unique_ptr<IMIDIInOut> newVirtualMIDIInOut(ReceiveFunctor&& receiver) size_t send(const void* buf, size_t len) const {
{ return size_t(std::max(0l, snd_rawmidi_write(m_midiOut, buf, len)));
int status;
snd_rawmidi_t* midiIn;
snd_rawmidi_t* midiOut;
status = snd_rawmidi_open(&midiIn, &midiOut, "virtual", 0);
if (status)
return {};
return std::make_unique<MIDIInOut>(nullptr, midiIn, midiOut, true, std::move(receiver));
} }
};
std::unique_ptr<IMIDIIn> newRealMIDIIn(const char* name, ReceiveFunctor&& receiver) std::unique_ptr<IMIDIIn> newVirtualMIDIIn(ReceiveFunctor&& receiver) {
{ int status;
snd_rawmidi_t* midi; snd_rawmidi_t* midi;
int status = snd_rawmidi_open(&midi, nullptr, name, 0); status = snd_rawmidi_open(&midi, nullptr, "virtual", 0);
if (status) if (status)
return {}; return {};
auto ret = std::make_unique<MIDIIn>(this, midi, true, std::move(receiver)); return std::make_unique<MIDIIn>(nullptr, midi, true, std::move(receiver));
_addOpenHandle(name, ret.get()); }
return ret;
}
std::unique_ptr<IMIDIOut> newRealMIDIOut(const char* name) std::unique_ptr<IMIDIOut> newVirtualMIDIOut() {
{ int status;
snd_rawmidi_t* midi; snd_rawmidi_t* midi;
int status = snd_rawmidi_open(nullptr, &midi, name, 0); status = snd_rawmidi_open(nullptr, &midi, "virtual", 0);
if (status) if (status)
return {}; return {};
auto ret = std::make_unique<MIDIOut>(this, midi, true); return std::make_unique<MIDIOut>(nullptr, midi, true);
_addOpenHandle(name, ret.get()); }
return ret;
}
std::unique_ptr<IMIDIInOut> newRealMIDIInOut(const char* name, ReceiveFunctor&& receiver) std::unique_ptr<IMIDIInOut> newVirtualMIDIInOut(ReceiveFunctor&& receiver) {
{ int status;
snd_rawmidi_t* midiIn; snd_rawmidi_t* midiIn;
snd_rawmidi_t* midiOut; snd_rawmidi_t* midiOut;
int status = snd_rawmidi_open(&midiIn, &midiOut, name, 0); status = snd_rawmidi_open(&midiIn, &midiOut, "virtual", 0);
if (status) if (status)
return {}; return {};
auto ret = std::make_unique<MIDIInOut>(this, midiIn, midiOut, true, std::move(receiver)); return std::make_unique<MIDIInOut>(nullptr, midiIn, midiOut, true, std::move(receiver));
_addOpenHandle(name, ret.get()); }
return ret;
}
bool useMIDILock() const {return true;} std::unique_ptr<IMIDIIn> newRealMIDIIn(const char* name, ReceiveFunctor&& receiver) {
snd_rawmidi_t* midi;
int status = snd_rawmidi_open(&midi, nullptr, name, 0);
if (status)
return {};
auto ret = std::make_unique<MIDIIn>(this, midi, true, std::move(receiver));
_addOpenHandle(name, ret.get());
return ret;
}
std::unique_ptr<IMIDIOut> newRealMIDIOut(const char* name) {
snd_rawmidi_t* midi;
int status = snd_rawmidi_open(nullptr, &midi, name, 0);
if (status)
return {};
auto ret = std::make_unique<MIDIOut>(this, midi, true);
_addOpenHandle(name, ret.get());
return ret;
}
std::unique_ptr<IMIDIInOut> newRealMIDIInOut(const char* name, ReceiveFunctor&& receiver) {
snd_rawmidi_t* midiIn;
snd_rawmidi_t* midiOut;
int status = snd_rawmidi_open(&midiIn, &midiOut, name, 0);
if (status)
return {};
auto ret = std::make_unique<MIDIInOut>(this, midiIn, midiOut, true, std::move(receiver));
_addOpenHandle(name, ret.get());
return ret;
}
bool useMIDILock() const { return true; }
}; };
} } // namespace boo

View File

@ -5,28 +5,23 @@
#undef min #undef min
#undef max #undef max
namespace boo namespace boo {
{
template <typename T> template <typename T>
inline T ClampFull(float in) inline T ClampFull(float in) {
{ if (std::is_floating_point<T>()) {
if(std::is_floating_point<T>()) return std::min<T>(std::max<T>(in, -1.f), 1.f);
{ } else {
return std::min<T>(std::max<T>(in, -1.f), 1.f); constexpr T MAX = std::numeric_limits<T>::max();
} constexpr T MIN = std::numeric_limits<T>::min();
else
{
constexpr T MAX = std::numeric_limits<T>::max();
constexpr T MIN = std::numeric_limits<T>::min();
if (in < MIN) if (in < MIN)
return MIN; return MIN;
else if (in > MAX) else if (in > MAX)
return MAX; return MAX;
else else
return in; return in;
} }
} }
#if INTEL_IPP #if INTEL_IPP
@ -38,148 +33,130 @@ inline T ClampFull(float in)
#if USE_LPF #if USE_LPF
static constexpr int FirTaps = 27; static constexpr int FirTaps = 27;
FIRFilter12k::FIRFilter12k(int windowFrames, double sampleRate) FIRFilter12k::FIRFilter12k(int windowFrames, double sampleRate) {
{ Ipp64f* taps = ippsMalloc_64f(FirTaps);
Ipp64f* taps = ippsMalloc_64f(FirTaps); Ipp32f* taps32 = ippsMalloc_32f(FirTaps);
Ipp32f* taps32 = ippsMalloc_32f(FirTaps); int sizeSpec, sizeBuf;
int sizeSpec, sizeBuf;
ippsFIRGenGetBufferSize(FirTaps, &sizeBuf); ippsFIRGenGetBufferSize(FirTaps, &sizeBuf);
m_firBuffer = ippsMalloc_8u(sizeBuf); m_firBuffer = ippsMalloc_8u(sizeBuf);
ippsFIRGenLowpass_64f(12000.0 / sampleRate, taps, FirTaps, ippWinBartlett, ippTrue, m_firBuffer); ippsFIRGenLowpass_64f(12000.0 / sampleRate, taps, FirTaps, ippWinBartlett, ippTrue, m_firBuffer);
ippsConvert_64f32f(taps, taps32, FirTaps); ippsConvert_64f32f(taps, taps32, FirTaps);
ippsFree(taps); ippsFree(taps);
ippsFree(m_firBuffer); ippsFree(m_firBuffer);
m_dlySrc = ippsMalloc_32f(FirTaps); m_dlySrc = ippsMalloc_32f(FirTaps);
ippsFIRSRGetSize(FirTaps, ipp32f, &sizeSpec, &sizeBuf); ippsFIRSRGetSize(FirTaps, ipp32f, &sizeSpec, &sizeBuf);
m_firSpec = (IppsFIRSpec_32f*)ippsMalloc_8u(sizeSpec); m_firSpec = (IppsFIRSpec_32f*)ippsMalloc_8u(sizeSpec);
m_firBuffer = ippsMalloc_8u(sizeBuf); m_firBuffer = ippsMalloc_8u(sizeBuf);
ippsFIRSRInit_32f(taps32, FirTaps, ippAlgDirect, m_firSpec); ippsFIRSRInit_32f(taps32, FirTaps, ippAlgDirect, m_firSpec);
ippsFree(taps32); ippsFree(taps32);
m_inBuf = ippsMalloc_32f(windowFrames); m_inBuf = ippsMalloc_32f(windowFrames);
} }
FIRFilter12k::~FIRFilter12k() FIRFilter12k::~FIRFilter12k() {
{ ippsFree(m_firSpec);
ippsFree(m_firSpec); ippsFree(m_firBuffer);
ippsFree(m_firBuffer); ippsFree(m_dlySrc);
ippsFree(m_dlySrc); ippsFree(m_inBuf);
ippsFree(m_inBuf);
} }
void FIRFilter12k::Process(Ipp32f* buf, int windowFrames) void FIRFilter12k::Process(Ipp32f* buf, int windowFrames) {
{ ippsZero_32f(m_dlySrc, FirTaps);
ippsZero_32f(m_dlySrc, FirTaps); ippsMove_32f(buf, m_inBuf, windowFrames);
ippsMove_32f(buf, m_inBuf, windowFrames); ippsFIRSR_32f(m_inBuf, buf, windowFrames, m_firSpec, m_dlySrc, nullptr, m_firBuffer);
ippsFIRSR_32f(m_inBuf, buf, windowFrames, m_firSpec, m_dlySrc, nullptr, m_firBuffer);
} }
#endif #endif
WindowedHilbert::WindowedHilbert(int windowFrames, double sampleRate) : WindowedHilbert::WindowedHilbert(int windowFrames, double sampleRate)
:
#if USE_LPF #if USE_LPF
m_fir(windowFrames, sampleRate), m_fir(windowFrames, sampleRate)
,
#endif #endif
m_windowFrames(windowFrames), m_windowFrames(windowFrames)
m_halfFrames(windowFrames / 2), , m_halfFrames(windowFrames / 2)
m_inputBuf(ippsMalloc_32f(m_windowFrames * 2 + m_halfFrames)), , m_inputBuf(ippsMalloc_32f(m_windowFrames * 2 + m_halfFrames))
m_outputBuf(ippsMalloc_32fc(m_windowFrames * 4)), , m_outputBuf(ippsMalloc_32fc(m_windowFrames * 4))
m_hammingTable(ippsMalloc_32f(m_halfFrames)) , m_hammingTable(ippsMalloc_32f(m_halfFrames)) {
{ ippsZero_32f(m_inputBuf, m_windowFrames * 2 + m_halfFrames);
ippsZero_32f(m_inputBuf, m_windowFrames * 2 + m_halfFrames); ippsZero_32fc(m_outputBuf, m_windowFrames * 4);
ippsZero_32fc(m_outputBuf, m_windowFrames * 4); m_output[0] = m_outputBuf;
m_output[0] = m_outputBuf; m_output[1] = m_output[0] + m_windowFrames;
m_output[1] = m_output[0] + m_windowFrames; m_output[2] = m_output[1] + m_windowFrames;
m_output[2] = m_output[1] + m_windowFrames; m_output[3] = m_output[2] + m_windowFrames;
m_output[3] = m_output[2] + m_windowFrames; int sizeSpec, sizeBuf;
int sizeSpec, sizeBuf; ippsHilbertGetSize_32f32fc(m_windowFrames, ippAlgHintFast, &sizeSpec, &sizeBuf);
ippsHilbertGetSize_32f32fc(m_windowFrames, ippAlgHintFast, &sizeSpec, &sizeBuf); m_spec = (IppsHilbertSpec*)ippMalloc(sizeSpec);
m_spec = (IppsHilbertSpec*)ippMalloc(sizeSpec); m_buffer = (Ipp8u*)ippMalloc(sizeBuf);
m_buffer = (Ipp8u*)ippMalloc(sizeBuf); ippsHilbertInit_32f32fc(m_windowFrames, ippAlgHintFast, m_spec, m_buffer);
ippsHilbertInit_32f32fc(m_windowFrames, ippAlgHintFast, m_spec, m_buffer);
for (int i=0 ; i<m_halfFrames ; ++i) for (int i = 0; i < m_halfFrames; ++i)
m_hammingTable[i] = Ipp32f(std::cos(M_PI * (i / double(m_halfFrames) + 1.0)) * 0.5 + 0.5); m_hammingTable[i] = Ipp32f(std::cos(M_PI * (i / double(m_halfFrames) + 1.0)) * 0.5 + 0.5);
} }
WindowedHilbert::~WindowedHilbert() WindowedHilbert::~WindowedHilbert() {
{ ippFree(m_spec);
ippFree(m_spec); ippFree(m_buffer);
ippFree(m_buffer); ippsFree(m_inputBuf);
ippsFree(m_inputBuf); ippsFree(m_outputBuf);
ippsFree(m_outputBuf); ippsFree(m_hammingTable);
ippsFree(m_hammingTable);
} }
void WindowedHilbert::_AddWindow() void WindowedHilbert::_AddWindow() {
{
#if USE_LPF #if USE_LPF
Ipp32f* inBufBase = &m_inputBuf[m_windowFrames * m_bufIdx + m_halfFrames]; Ipp32f* inBufBase = &m_inputBuf[m_windowFrames * m_bufIdx + m_halfFrames];
m_fir.Process(inBufBase, m_windowFrames); m_fir.Process(inBufBase, m_windowFrames);
#endif #endif
if (m_bufIdx) if (m_bufIdx) {
{ /* Mirror last half of samples to start of input buffer */
/* Mirror last half of samples to start of input buffer */ Ipp32f* bufBase = &m_inputBuf[m_windowFrames * 2];
Ipp32f* bufBase = &m_inputBuf[m_windowFrames * 2]; ippsCopy_32f(bufBase, m_inputBuf, m_halfFrames);
ippsCopy_32f(bufBase, m_inputBuf, m_halfFrames); ippsHilbert_32f32fc(&m_inputBuf[m_windowFrames], m_output[2], m_spec, m_buffer);
ippsHilbert_32f32fc(&m_inputBuf[m_windowFrames], ippsHilbert_32f32fc(&m_inputBuf[m_windowFrames + m_halfFrames], m_output[3], m_spec, m_buffer);
m_output[2], m_spec, m_buffer); } else {
ippsHilbert_32f32fc(&m_inputBuf[m_windowFrames + m_halfFrames], ippsHilbert_32f32fc(&m_inputBuf[0], m_output[0], m_spec, m_buffer);
m_output[3], m_spec, m_buffer); ippsHilbert_32f32fc(&m_inputBuf[m_halfFrames], m_output[1], m_spec, m_buffer);
} }
else m_bufIdx ^= 1;
{
ippsHilbert_32f32fc(&m_inputBuf[0],
m_output[0], m_spec, m_buffer);
ippsHilbert_32f32fc(&m_inputBuf[m_halfFrames],
m_output[1], m_spec, m_buffer);
}
m_bufIdx ^= 1;
} }
void WindowedHilbert::AddWindow(const float* input, int stride) void WindowedHilbert::AddWindow(const float* input, int stride) {
{ Ipp32f* bufBase = &m_inputBuf[m_windowFrames * m_bufIdx + m_halfFrames];
Ipp32f* bufBase = &m_inputBuf[m_windowFrames * m_bufIdx + m_halfFrames]; for (int i = 0; i < m_windowFrames; ++i)
for (int i=0 ; i<m_windowFrames ; ++i) bufBase[i] = input[i * stride];
bufBase[i] = input[i * stride]; _AddWindow();
_AddWindow();
} }
void WindowedHilbert::AddWindow(const int32_t* input, int stride) void WindowedHilbert::AddWindow(const int32_t* input, int stride) {
{ Ipp32f* bufBase = &m_inputBuf[m_windowFrames * m_bufIdx + m_halfFrames];
Ipp32f* bufBase = &m_inputBuf[m_windowFrames * m_bufIdx + m_halfFrames]; for (int i = 0; i < m_windowFrames; ++i)
for (int i=0 ; i<m_windowFrames ; ++i) bufBase[i] = input[i * stride] / (float(INT32_MAX) + 1.f);
bufBase[i] = input[i * stride] / (float(INT32_MAX) + 1.f); _AddWindow();
_AddWindow();
} }
void WindowedHilbert::AddWindow(const int16_t* input, int stride) void WindowedHilbert::AddWindow(const int16_t* input, int stride) {
{ Ipp32f* bufBase = &m_inputBuf[m_windowFrames * m_bufIdx + m_halfFrames];
Ipp32f* bufBase = &m_inputBuf[m_windowFrames * m_bufIdx + m_halfFrames]; for (int i = 0; i < m_windowFrames; ++i)
for (int i=0 ; i<m_windowFrames ; ++i) bufBase[i] = input[i * stride] / (float(INT16_MAX) + 1.f);
bufBase[i] = input[i * stride] / (float(INT16_MAX) + 1.f); _AddWindow();
_AddWindow();
} }
template <typename T> template <typename T>
void WindowedHilbert::Output(T* output, float lCoef, float rCoef) const void WindowedHilbert::Output(T* output, float lCoef, float rCoef) const {
{ int first, middle, last;
int first, middle, last; if (m_bufIdx) {
if (m_bufIdx) first = 3;
{ middle = 0;
first = 3; last = 1;
middle = 0; } else {
last = 1; first = 1;
} middle = 2;
else last = 3;
{ }
first = 1;
middle = 2;
last = 3;
}
#if 0 #if 0
for (int i=0 ; i<m_windowFrames ; ++i) for (int i=0 ; i<m_windowFrames ; ++i)
@ -191,27 +168,23 @@ void WindowedHilbert::Output(T* output, float lCoef, float rCoef) const
return; return;
#endif #endif
int i, t; int i, t;
for (i=0, t=0 ; i<m_halfFrames ; ++i, ++t) for (i = 0, t = 0; i < m_halfFrames; ++i, ++t) {
{ float tmp =
float tmp = m_output[first][m_halfFrames + i].im * (1.f - m_hammingTable[t]) + m_output[first][m_halfFrames + i].im * (1.f - m_hammingTable[t]) + m_output[middle][i].im * m_hammingTable[t];
m_output[middle][i].im * m_hammingTable[t]; output[i * 2] = ClampFull<T>(output[i * 2] + tmp * lCoef);
output[i*2] = ClampFull<T>(output[i*2] + tmp * lCoef); output[i * 2 + 1] = ClampFull<T>(output[i * 2 + 1] + tmp * rCoef);
output[i*2+1] = ClampFull<T>(output[i*2+1] + tmp * rCoef); }
} for (; i < m_windowFrames - m_halfFrames; ++i) {
for (; i<m_windowFrames-m_halfFrames ; ++i) float tmp = m_output[middle][i].im;
{ output[i * 2] = ClampFull<T>(output[i * 2] + tmp * lCoef);
float tmp = m_output[middle][i].im; output[i * 2 + 1] = ClampFull<T>(output[i * 2 + 1] + tmp * rCoef);
output[i*2] = ClampFull<T>(output[i*2] + tmp * lCoef); }
output[i*2+1] = ClampFull<T>(output[i*2+1] + tmp * rCoef); for (t = 0; i < m_windowFrames; ++i, ++t) {
} float tmp = m_output[middle][i].im * (1.f - m_hammingTable[t]) + m_output[last][t].im * m_hammingTable[t];
for (t=0 ; i<m_windowFrames ; ++i, ++t) output[i * 2] = ClampFull<T>(output[i * 2] + tmp * lCoef);
{ output[i * 2 + 1] = ClampFull<T>(output[i * 2 + 1] + tmp * rCoef);
float tmp = m_output[middle][i].im * (1.f - m_hammingTable[t]) + }
m_output[last][t].im * m_hammingTable[t];
output[i*2] = ClampFull<T>(output[i*2] + tmp * lCoef);
output[i*2+1] = ClampFull<T>(output[i*2+1] + tmp * rCoef);
}
} }
template void WindowedHilbert::Output<int16_t>(int16_t* output, float lCoef, float rCoef) const; template void WindowedHilbert::Output<int16_t>(int16_t* output, float lCoef, float rCoef) const;
@ -220,53 +193,71 @@ template void WindowedHilbert::Output<float>(float* output, float lCoef, float r
#endif #endif
template <> int16_t* LtRtProcessing::_getInBuf<int16_t>() { return m_16Buffer.get(); } template <>
template <> int32_t* LtRtProcessing::_getInBuf<int32_t>() { return m_32Buffer.get(); } int16_t* LtRtProcessing::_getInBuf<int16_t>() {
template <> float* LtRtProcessing::_getInBuf<float>() { return m_fltBuffer.get(); } return m_16Buffer.get();
}
template <>
int32_t* LtRtProcessing::_getInBuf<int32_t>() {
return m_32Buffer.get();
}
template <>
float* LtRtProcessing::_getInBuf<float>() {
return m_fltBuffer.get();
}
template <> int16_t* LtRtProcessing::_getOutBuf<int16_t>() { return m_16Buffer.get() + m_outputOffset; } template <>
template <> int32_t* LtRtProcessing::_getOutBuf<int32_t>() { return m_32Buffer.get() + m_outputOffset; } int16_t* LtRtProcessing::_getOutBuf<int16_t>() {
template <> float* LtRtProcessing::_getOutBuf<float>() { return m_fltBuffer.get() + m_outputOffset; } return m_16Buffer.get() + m_outputOffset;
}
template <>
int32_t* LtRtProcessing::_getOutBuf<int32_t>() {
return m_32Buffer.get() + m_outputOffset;
}
template <>
float* LtRtProcessing::_getOutBuf<float>() {
return m_fltBuffer.get() + m_outputOffset;
}
LtRtProcessing::LtRtProcessing(int _5msFrames, const AudioVoiceEngineMixInfo& mixInfo) LtRtProcessing::LtRtProcessing(int _5msFrames, const AudioVoiceEngineMixInfo& mixInfo)
: m_inMixInfo(mixInfo), m_windowFrames(_5msFrames * 4), m_halfFrames(m_windowFrames / 2), : m_inMixInfo(mixInfo)
m_outputOffset(m_windowFrames * 5 * 2) , m_windowFrames(_5msFrames * 4)
, m_halfFrames(m_windowFrames / 2)
, m_outputOffset(m_windowFrames * 5 * 2)
#if INTEL_IPP #if INTEL_IPP
, m_hilbertSL(m_windowFrames, mixInfo.m_sampleRate), , m_hilbertSL(m_windowFrames, mixInfo.m_sampleRate)
m_hilbertSR(m_windowFrames, mixInfo.m_sampleRate) , m_hilbertSR(m_windowFrames, mixInfo.m_sampleRate)
#endif #endif
{ {
m_inMixInfo.m_channels = AudioChannelSet::Surround51; m_inMixInfo.m_channels = AudioChannelSet::Surround51;
m_inMixInfo.m_channelMap.m_channelCount = 5; m_inMixInfo.m_channelMap.m_channelCount = 5;
m_inMixInfo.m_channelMap.m_channels[0] = AudioChannel::FrontLeft; m_inMixInfo.m_channelMap.m_channels[0] = AudioChannel::FrontLeft;
m_inMixInfo.m_channelMap.m_channels[1] = AudioChannel::FrontRight; m_inMixInfo.m_channelMap.m_channels[1] = AudioChannel::FrontRight;
m_inMixInfo.m_channelMap.m_channels[2] = AudioChannel::FrontCenter; m_inMixInfo.m_channelMap.m_channels[2] = AudioChannel::FrontCenter;
m_inMixInfo.m_channelMap.m_channels[3] = AudioChannel::RearLeft; m_inMixInfo.m_channelMap.m_channels[3] = AudioChannel::RearLeft;
m_inMixInfo.m_channelMap.m_channels[4] = AudioChannel::RearRight; m_inMixInfo.m_channelMap.m_channels[4] = AudioChannel::RearRight;
int samples = m_windowFrames * (5 * 2 + 2 * 2); int samples = m_windowFrames * (5 * 2 + 2 * 2);
switch (mixInfo.m_sampleFormat) switch (mixInfo.m_sampleFormat) {
{ case SOXR_INT16_I:
case SOXR_INT16_I: m_16Buffer.reset(new int16_t[samples]);
m_16Buffer.reset(new int16_t[samples]); memset(m_16Buffer.get(), 0, sizeof(int16_t) * samples);
memset(m_16Buffer.get(), 0, sizeof(int16_t) * samples); break;
break; case SOXR_INT32_I:
case SOXR_INT32_I: m_32Buffer.reset(new int32_t[samples]);
m_32Buffer.reset(new int32_t[samples]); memset(m_32Buffer.get(), 0, sizeof(int32_t) * samples);
memset(m_32Buffer.get(), 0, sizeof(int32_t) * samples); break;
break; case SOXR_FLOAT32_I:
case SOXR_FLOAT32_I: m_fltBuffer.reset(new float[samples]);
m_fltBuffer.reset(new float[samples]); memset(m_fltBuffer.get(), 0, sizeof(float) * samples);
memset(m_fltBuffer.get(), 0, sizeof(float) * samples); break;
break; default:
default: break;
break; }
}
} }
template <typename T> template <typename T>
void LtRtProcessing::Process(const T* input, T* output, int frameCount) void LtRtProcessing::Process(const T* input, T* output, int frameCount) {
{
#if 0 #if 0
for (int i=0 ; i<frameCount ; ++i) for (int i=0 ; i<frameCount ; ++i)
{ {
@ -276,90 +267,81 @@ void LtRtProcessing::Process(const T* input, T* output, int frameCount)
return; return;
#endif #endif
int outFramesRem = frameCount; int outFramesRem = frameCount;
T* inBuf = _getInBuf<T>(); T* inBuf = _getInBuf<T>();
T* outBuf = _getOutBuf<T>(); T* outBuf = _getOutBuf<T>();
int tail = std::min(m_windowFrames * 2, m_bufferTail + frameCount); int tail = std::min(m_windowFrames * 2, m_bufferTail + frameCount);
int samples = (tail - m_bufferTail) * 5; int samples = (tail - m_bufferTail) * 5;
memmove(&inBuf[m_bufferTail * 5], input, samples * sizeof(T)); memmove(&inBuf[m_bufferTail * 5], input, samples * sizeof(T));
//printf("input %d to %d\n", tail - m_bufferTail, m_bufferTail); // printf("input %d to %d\n", tail - m_bufferTail, m_bufferTail);
input += samples; input += samples;
frameCount -= tail - m_bufferTail; frameCount -= tail - m_bufferTail;
int head = std::min(m_windowFrames * 2, m_bufferHead + outFramesRem); int head = std::min(m_windowFrames * 2, m_bufferHead + outFramesRem);
samples = (head - m_bufferHead) * 2; samples = (head - m_bufferHead) * 2;
memmove(output, outBuf + m_bufferHead * 2, samples * sizeof(T)); memmove(output, outBuf + m_bufferHead * 2, samples * sizeof(T));
//printf("output %d from %d\n", head - m_bufferHead, m_bufferHead); // printf("output %d from %d\n", head - m_bufferHead, m_bufferHead);
output += samples; output += samples;
outFramesRem -= head - m_bufferHead; outFramesRem -= head - m_bufferHead;
int bufIdx = m_bufferTail / m_windowFrames; int bufIdx = m_bufferTail / m_windowFrames;
if (tail / m_windowFrames > bufIdx) if (tail / m_windowFrames > bufIdx) {
{ T* in = &inBuf[bufIdx * m_windowFrames * 5];
T* in = &inBuf[bufIdx * m_windowFrames * 5]; T* out = &outBuf[bufIdx * m_windowFrames * 2];
T* out = &outBuf[bufIdx * m_windowFrames * 2];
#if INTEL_IPP #if INTEL_IPP
m_hilbertSL.AddWindow(in + 3, 5); m_hilbertSL.AddWindow(in + 3, 5);
m_hilbertSR.AddWindow(in + 4, 5); m_hilbertSR.AddWindow(in + 4, 5);
#endif #endif
// x(:,1) + sqrt(.5)*x(:,3) + sqrt(19/25)*x(:,4) + sqrt(6/25)*x(:,5) // x(:,1) + sqrt(.5)*x(:,3) + sqrt(19/25)*x(:,4) + sqrt(6/25)*x(:,5)
// x(:,2) + sqrt(.5)*x(:,3) - sqrt(6/25)*x(:,4) - sqrt(19/25)*x(:,5) // x(:,2) + sqrt(.5)*x(:,3) - sqrt(6/25)*x(:,4) - sqrt(19/25)*x(:,5)
if (bufIdx) if (bufIdx) {
{ int delayI = -m_halfFrames;
int delayI = -m_halfFrames; for (int i = 0; i < m_windowFrames; ++i, ++delayI) {
for (int i=0 ; i<m_windowFrames ; ++i, ++delayI) out[i * 2] = ClampFull<T>(in[delayI * 5] + 0.7071068f * in[delayI * 5 + 2]);
{ out[i * 2 + 1] = ClampFull<T>(in[delayI * 5 + 1] + 0.7071068f * in[delayI * 5 + 2]);
out[i * 2] = ClampFull<T>(in[delayI * 5] + 0.7071068f * in[delayI * 5 + 2]); // printf("in %d out %d\n", bufIdx * m_5msFrames + delayI, bufIdx * m_5msFrames + i);
out[i * 2 + 1] = ClampFull<T>(in[delayI * 5 + 1] + 0.7071068f * in[delayI * 5 + 2]); }
//printf("in %d out %d\n", bufIdx * m_5msFrames + delayI, bufIdx * m_5msFrames + i); } else {
} int delayI = m_windowFrames * 2 - m_halfFrames;
} int i;
else for (i = 0; i < m_halfFrames; ++i, ++delayI) {
{ out[i * 2] = ClampFull<T>(in[delayI * 5] + 0.7071068f * in[delayI * 5 + 2]);
int delayI = m_windowFrames * 2 - m_halfFrames; out[i * 2 + 1] = ClampFull<T>(in[delayI * 5 + 1] + 0.7071068f * in[delayI * 5 + 2]);
int i; // printf("in %d out %d\n", bufIdx * m_5msFrames + delayI, bufIdx * m_5msFrames + i);
for (i=0 ; i<m_halfFrames ; ++i, ++delayI) }
{ delayI = 0;
out[i * 2] = ClampFull<T>(in[delayI * 5] + 0.7071068f * in[delayI * 5 + 2]); for (; i < m_windowFrames; ++i, ++delayI) {
out[i * 2 + 1] = ClampFull<T>(in[delayI * 5 + 1] + 0.7071068f * in[delayI * 5 + 2]); out[i * 2] = ClampFull<T>(in[delayI * 5] + 0.7071068f * in[delayI * 5 + 2]);
//printf("in %d out %d\n", bufIdx * m_5msFrames + delayI, bufIdx * m_5msFrames + i); out[i * 2 + 1] = ClampFull<T>(in[delayI * 5 + 1] + 0.7071068f * in[delayI * 5 + 2]);
} // printf("in %d out %d\n", bufIdx * m_5msFrames + delayI, bufIdx * m_5msFrames + i);
delayI = 0; }
for (; i<m_windowFrames ; ++i, ++delayI) }
{
out[i * 2] = ClampFull<T>(in[delayI * 5] + 0.7071068f * in[delayI * 5 + 2]);
out[i * 2 + 1] = ClampFull<T>(in[delayI * 5 + 1] + 0.7071068f * in[delayI * 5 + 2]);
//printf("in %d out %d\n", bufIdx * m_5msFrames + delayI, bufIdx * m_5msFrames + i);
}
}
#if INTEL_IPP #if INTEL_IPP
m_hilbertSL.Output(out, 0.8717798f, 0.4898979f); m_hilbertSL.Output(out, 0.8717798f, 0.4898979f);
m_hilbertSR.Output(out, -0.4898979f, -0.8717798f); m_hilbertSR.Output(out, -0.4898979f, -0.8717798f);
#endif #endif
} }
m_bufferTail = (tail == m_windowFrames * 2) ? 0 : tail; m_bufferTail = (tail == m_windowFrames * 2) ? 0 : tail;
m_bufferHead = (head == m_windowFrames * 2) ? 0 : head; m_bufferHead = (head == m_windowFrames * 2) ? 0 : head;
if (frameCount) if (frameCount) {
{ samples = frameCount * 5;
samples = frameCount * 5; memmove(inBuf, input, samples * sizeof(T));
memmove(inBuf, input, samples * sizeof(T)); // printf("input %d to %d\n", frameCount, 0);
//printf("input %d to %d\n", frameCount, 0); m_bufferTail = frameCount;
m_bufferTail = frameCount; }
}
if (outFramesRem) if (outFramesRem) {
{ samples = outFramesRem * 2;
samples = outFramesRem * 2; memmove(output, outBuf, samples * sizeof(T));
memmove(output, outBuf, samples * sizeof(T)); // printf("output %d from %d\n", outFramesRem, 0);
//printf("output %d from %d\n", outFramesRem, 0); m_bufferHead = outFramesRem;
m_bufferHead = outFramesRem; }
}
} }
template void LtRtProcessing::Process<int16_t>(const int16_t* input, int16_t* output, int frameCount); template void LtRtProcessing::Process<int16_t>(const int16_t* input, int16_t* output, int frameCount);
template void LtRtProcessing::Process<int32_t>(const int32_t* input, int32_t* output, int frameCount); template void LtRtProcessing::Process<int32_t>(const int32_t* input, int32_t* output, int frameCount);
template void LtRtProcessing::Process<float>(const float* input, float* output, int frameCount); template void LtRtProcessing::Process<float>(const float* input, float* output, int frameCount);
} } // namespace boo

View File

@ -9,73 +9,73 @@
#include "ipp.h" #include "ipp.h"
#endif #endif
namespace boo namespace boo {
{
#if INTEL_IPP #if INTEL_IPP
#define USE_LPF 0 #define USE_LPF 0
#if USE_LPF #if USE_LPF
class FIRFilter12k class FIRFilter12k {
{ IppsFIRSpec_32f* m_firSpec;
IppsFIRSpec_32f* m_firSpec; Ipp8u* m_firBuffer;
Ipp8u* m_firBuffer; Ipp32f* m_dlySrc;
Ipp32f* m_dlySrc; Ipp32f* m_inBuf;
Ipp32f* m_inBuf;
public: public:
explicit FIRFilter12k(int windowFrames, double sampleRate); explicit FIRFilter12k(int windowFrames, double sampleRate);
~FIRFilter12k(); ~FIRFilter12k();
void Process(Ipp32f* buf, int windowFrames); void Process(Ipp32f* buf, int windowFrames);
}; };
#endif #endif
class WindowedHilbert class WindowedHilbert {
{
#if USE_LPF #if USE_LPF
FIRFilter12k m_fir; FIRFilter12k m_fir;
#endif #endif
IppsHilbertSpec* m_spec; IppsHilbertSpec* m_spec;
Ipp8u* m_buffer; Ipp8u* m_buffer;
int m_windowFrames, m_halfFrames; int m_windowFrames, m_halfFrames;
int m_bufIdx = 0; int m_bufIdx = 0;
Ipp32f* m_inputBuf; Ipp32f* m_inputBuf;
Ipp32fc* m_outputBuf; Ipp32fc* m_outputBuf;
Ipp32fc* m_output[4]; Ipp32fc* m_output[4];
Ipp32f* m_hammingTable; Ipp32f* m_hammingTable;
void _AddWindow(); void _AddWindow();
public: public:
explicit WindowedHilbert(int windowFrames, double sampleRate); explicit WindowedHilbert(int windowFrames, double sampleRate);
~WindowedHilbert(); ~WindowedHilbert();
void AddWindow(const float* input, int stride); void AddWindow(const float* input, int stride);
void AddWindow(const int32_t* input, int stride); void AddWindow(const int32_t* input, int stride);
void AddWindow(const int16_t* input, int stride); void AddWindow(const int16_t* input, int stride);
template <typename T> template <typename T>
void Output(T* output, float lCoef, float rCoef) const; void Output(T* output, float lCoef, float rCoef) const;
}; };
#endif #endif
class LtRtProcessing class LtRtProcessing {
{ AudioVoiceEngineMixInfo m_inMixInfo;
AudioVoiceEngineMixInfo m_inMixInfo; int m_windowFrames;
int m_windowFrames; int m_halfFrames;
int m_halfFrames; int m_outputOffset;
int m_outputOffset; int m_bufferTail = 0;
int m_bufferTail = 0; int m_bufferHead = 0;
int m_bufferHead = 0; std::unique_ptr<int16_t[]> m_16Buffer;
std::unique_ptr<int16_t[]> m_16Buffer; std::unique_ptr<int32_t[]> m_32Buffer;
std::unique_ptr<int32_t[]> m_32Buffer; std::unique_ptr<float[]> m_fltBuffer;
std::unique_ptr<float[]> m_fltBuffer;
#if INTEL_IPP #if INTEL_IPP
WindowedHilbert m_hilbertSL, m_hilbertSR; WindowedHilbert m_hilbertSL, m_hilbertSR;
#endif #endif
template <typename T> T* _getInBuf(); template <typename T>
template <typename T> T* _getOutBuf(); T* _getInBuf();
template <typename T>
T* _getOutBuf();
public: public:
LtRtProcessing(int _5msFrames, const AudioVoiceEngineMixInfo& mixInfo); LtRtProcessing(int _5msFrames, const AudioVoiceEngineMixInfo& mixInfo);
template <typename T> template <typename T>
void Process(const T* input, T* output, int frameCount); void Process(const T* input, T* output, int frameCount);
const AudioVoiceEngineMixInfo& inMixInfo() const { return m_inMixInfo; } const AudioVoiceEngineMixInfo& inMixInfo() const { return m_inMixInfo; }
}; };
} } // namespace boo

View File

@ -1,12 +1,11 @@
#include "MIDICommon.hpp" #include "MIDICommon.hpp"
#include "boo/audiodev/IMIDIPort.hpp" #include "boo/audiodev/IMIDIPort.hpp"
namespace boo namespace boo {
{
IMIDIPort::~IMIDIPort() {} IMIDIPort::~IMIDIPort() {}
IMIDIIn::~IMIDIIn() {} IMIDIIn::~IMIDIIn() {}
IMIDIOut::~IMIDIOut() {} IMIDIOut::~IMIDIOut() {}
IMIDIInOut::~IMIDIInOut() {} IMIDIInOut::~IMIDIInOut() {}
} } // namespace boo

View File

@ -1,30 +1,27 @@
#pragma once #pragma once
namespace boo namespace boo {
{
enum class Status enum class Status {
{ NoteOff = 0x80,
NoteOff = 0x80, NoteOn = 0x90,
NoteOn = 0x90, NotePressure = 0xA0,
NotePressure = 0xA0, ControlChange = 0xB0,
ControlChange = 0xB0, ProgramChange = 0xC0,
ProgramChange = 0xC0, ChannelPressure = 0xD0,
ChannelPressure = 0xD0, PitchBend = 0xE0,
PitchBend = 0xE0, SysEx = 0xF0,
SysEx = 0xF0, TimecodeQuarterFrame = 0xF1,
TimecodeQuarterFrame = 0xF1, SongPositionPointer = 0xF2,
SongPositionPointer = 0xF2, SongSelect = 0xF3,
SongSelect = 0xF3, TuneRequest = 0xF6,
TuneRequest = 0xF6, SysExTerm = 0xF7,
SysExTerm = 0xF7, TimingClock = 0xF8,
TimingClock = 0xF8, Start = 0xFA,
Start = 0xFA, Continue = 0xFB,
Continue = 0xFB, Stop = 0xFC,
Stop = 0xFC, ActiveSensing = 0xFE,
ActiveSensing = 0xFE, Reset = 0xFF,
Reset = 0xFF,
}; };
} }

View File

@ -3,207 +3,183 @@
#include <memory> #include <memory>
#include <algorithm> #include <algorithm>
namespace boo namespace boo {
{
static inline uint8_t clamp7(uint8_t val) {return std::max(0, std::min(127, int(val)));} static inline uint8_t clamp7(uint8_t val) { return std::max(0, std::min(127, int(val))); }
bool MIDIDecoder::_readContinuedValue(std::vector<uint8_t>::const_iterator& it, bool MIDIDecoder::_readContinuedValue(std::vector<uint8_t>::const_iterator& it,
std::vector<uint8_t>::const_iterator end, std::vector<uint8_t>::const_iterator end, uint32_t& valOut) {
uint32_t& valOut) uint8_t a = *it++;
{ valOut = a & 0x7f;
if (a & 0x80) {
if (it == end)
return false;
valOut <<= 7;
a = *it++;
valOut |= a & 0x7f;
if (a & 0x80) {
if (it == end)
return false;
valOut <<= 7;
a = *it++;
valOut |= a & 0x7f;
}
}
return true;
}
std::vector<uint8_t>::const_iterator MIDIDecoder::receiveBytes(std::vector<uint8_t>::const_iterator begin,
std::vector<uint8_t>::const_iterator end) {
std::vector<uint8_t>::const_iterator it = begin;
while (it != end) {
uint8_t a = *it++; uint8_t a = *it++;
valOut = a & 0x7f; uint8_t b;
if (a & 0x80) if (a & 0x80)
{ m_status = a;
else
it--;
if (m_status == 0xff) {
/* Meta events (ignored for now) */
if (it == end)
return begin;
a = *it++;
uint32_t length;
_readContinuedValue(it, end, length);
it += length;
} else {
uint8_t chan = m_status & 0xf;
switch (Status(m_status & 0xf0)) {
case Status::NoteOff: {
if (it == end) if (it == end)
return false; return begin;
valOut <<= 7;
a = *it++; a = *it++;
valOut |= a & 0x7f; if (it == end)
return begin;
if (a & 0x80) b = *it++;
{ m_out.noteOff(chan, clamp7(a), clamp7(b));
if (it == end) break;
return false; }
valOut <<= 7; case Status::NoteOn: {
a = *it++; if (it == end)
valOut |= a & 0x7f; return begin;
a = *it++;
if (it == end)
return begin;
b = *it++;
m_out.noteOn(chan, clamp7(a), clamp7(b));
break;
}
case Status::NotePressure: {
if (it == end)
return begin;
a = *it++;
if (it == end)
return begin;
b = *it++;
m_out.notePressure(chan, clamp7(a), clamp7(b));
break;
}
case Status::ControlChange: {
if (it == end)
return begin;
a = *it++;
if (it == end)
return begin;
b = *it++;
m_out.controlChange(chan, clamp7(a), clamp7(b));
break;
}
case Status::ProgramChange: {
if (it == end)
return begin;
a = *it++;
m_out.programChange(chan, clamp7(a));
break;
}
case Status::ChannelPressure: {
if (it == end)
return begin;
a = *it++;
m_out.channelPressure(chan, clamp7(a));
break;
}
case Status::PitchBend: {
if (it == end)
return begin;
a = *it++;
if (it == end)
return begin;
b = *it++;
m_out.pitchBend(chan, clamp7(b) * 128 + clamp7(a));
break;
}
case Status::SysEx: {
switch (Status(m_status & 0xff)) {
case Status::SysEx: {
uint32_t len;
if (!_readContinuedValue(it, end, len) || end - it < len)
return begin;
m_out.sysex(&*it, len);
break;
} }
} case Status::TimecodeQuarterFrame: {
if (it == end)
return true; return begin;
} a = *it++;
m_out.timeCodeQuarterFrame(a >> 4 & 0x7, a & 0xf);
std::vector<uint8_t>::const_iterator break;
MIDIDecoder::receiveBytes(std::vector<uint8_t>::const_iterator begin,
std::vector<uint8_t>::const_iterator end)
{
std::vector<uint8_t>::const_iterator it = begin;
while (it != end)
{
uint8_t a = *it++;
uint8_t b;
if (a & 0x80)
m_status = a;
else
it--;
if (m_status == 0xff)
{
/* Meta events (ignored for now) */
if (it == end)
return begin;
a = *it++;
uint32_t length;
_readContinuedValue(it, end, length);
it += length;
} else
{
uint8_t chan = m_status & 0xf;
switch (Status(m_status & 0xf0))
{
case Status::NoteOff:
{
if (it == end)
return begin;
a = *it++;
if (it == end)
return begin;
b = *it++;
m_out.noteOff(chan, clamp7(a), clamp7(b));
break;
}
case Status::NoteOn:
{
if (it == end)
return begin;
a = *it++;
if (it == end)
return begin;
b = *it++;
m_out.noteOn(chan, clamp7(a), clamp7(b));
break;
}
case Status::NotePressure:
{
if (it == end)
return begin;
a = *it++;
if (it == end)
return begin;
b = *it++;
m_out.notePressure(chan, clamp7(a), clamp7(b));
break;
}
case Status::ControlChange:
{
if (it == end)
return begin;
a = *it++;
if (it == end)
return begin;
b = *it++;
m_out.controlChange(chan, clamp7(a), clamp7(b));
break;
}
case Status::ProgramChange:
{
if (it == end)
return begin;
a = *it++;
m_out.programChange(chan, clamp7(a));
break;
}
case Status::ChannelPressure:
{
if (it == end)
return begin;
a = *it++;
m_out.channelPressure(chan, clamp7(a));
break;
}
case Status::PitchBend:
{
if (it == end)
return begin;
a = *it++;
if (it == end)
return begin;
b = *it++;
m_out.pitchBend(chan, clamp7(b) * 128 + clamp7(a));
break;
}
case Status::SysEx:
{
switch (Status(m_status & 0xff))
{
case Status::SysEx:
{
uint32_t len;
if (!_readContinuedValue(it, end, len) || end - it < len)
return begin;
m_out.sysex(&*it, len);
break;
}
case Status::TimecodeQuarterFrame:
{
if (it == end)
return begin;
a = *it++;
m_out.timeCodeQuarterFrame(a >> 4 & 0x7, a & 0xf);
break;
}
case Status::SongPositionPointer:
{
if (it == end)
return begin;
a = *it++;
if (it == end)
return begin;
b = *it++;
m_out.songPositionPointer(clamp7(b) * 128 + clamp7(a));
break;
}
case Status::SongSelect:
{
if (it == end)
return begin;
a = *it++;
m_out.songSelect(clamp7(a));
break;
}
case Status::TuneRequest:
m_out.tuneRequest();
break;
case Status::Start:
m_out.startSeq();
break;
case Status::Continue:
m_out.continueSeq();
break;
case Status::Stop:
m_out.stopSeq();
break;
case Status::Reset:
m_out.reset();
break;
case Status::SysExTerm:
case Status::TimingClock:
case Status::ActiveSensing:
default:
break;
}
break;
}
default:
break;
}
} }
case Status::SongPositionPointer: {
if (it == end)
return begin;
a = *it++;
if (it == end)
return begin;
b = *it++;
m_out.songPositionPointer(clamp7(b) * 128 + clamp7(a));
break;
}
case Status::SongSelect: {
if (it == end)
return begin;
a = *it++;
m_out.songSelect(clamp7(a));
break;
}
case Status::TuneRequest:
m_out.tuneRequest();
break;
case Status::Start:
m_out.startSeq();
break;
case Status::Continue:
m_out.continueSeq();
break;
case Status::Stop:
m_out.stopSeq();
break;
case Status::Reset:
m_out.reset();
break;
case Status::SysExTerm:
case Status::TimingClock:
case Status::ActiveSensing:
default:
break;
}
break;
}
default:
break;
}
} }
return it; }
return it;
} }
} } // namespace boo

View File

@ -1,228 +1,182 @@
#include "boo/audiodev/MIDIEncoder.hpp" #include "boo/audiodev/MIDIEncoder.hpp"
#include "MIDICommon.hpp" #include "MIDICommon.hpp"
namespace boo namespace boo {
{
template <class Sender> template <class Sender>
void MIDIEncoder<Sender>::_sendMessage(const uint8_t* data, size_t len) void MIDIEncoder<Sender>::_sendMessage(const uint8_t* data, size_t len) {
{ if (data[0] == m_status)
if (data[0] == m_status) m_sender.send(data + 1, len - 1);
m_sender.send(data + 1, len - 1); else {
else if (data[0] & 0x80)
{ m_status = data[0];
if (data[0] & 0x80)
m_status = data[0];
m_sender.send(data, len);
}
}
template <class Sender>
void MIDIEncoder<Sender>::_sendContinuedValue(uint32_t val)
{
uint8_t send[3] = {};
uint8_t* ptr = nullptr;
if (val >= 0x4000)
{
ptr = &send[0];
send[0] = 0x80 | ((val / 0x4000) & 0x7f);
send[1] = 0x80;
val &= 0x3fff;
}
if (val >= 0x80)
{
if (!ptr)
ptr = &send[1];
send[1] = 0x80 | ((val / 0x80) & 0x7f);
}
if (!ptr)
ptr = &send[2];
send[2] = val & 0x7f;
m_sender.send(ptr, 3 - (ptr - send));
}
template <class Sender>
void MIDIEncoder<Sender>::noteOff(uint8_t chan, uint8_t key, uint8_t velocity)
{
uint8_t cmd[3] = {uint8_t(int(Status::NoteOff) | (chan & 0xf)),
uint8_t(key & 0x7f), uint8_t(velocity & 0x7f)};
_sendMessage(cmd, 3);
}
template <class Sender>
void MIDIEncoder<Sender>::noteOn(uint8_t chan, uint8_t key, uint8_t velocity)
{
uint8_t cmd[3] = {uint8_t(int(Status::NoteOn) | (chan & 0xf)),
uint8_t(key & 0x7f), uint8_t(velocity & 0x7f)};
_sendMessage(cmd, 3);
}
template <class Sender>
void MIDIEncoder<Sender>::notePressure(uint8_t chan, uint8_t key, uint8_t pressure)
{
uint8_t cmd[3] = {uint8_t(int(Status::NotePressure) | (chan & 0xf)),
uint8_t(key & 0x7f), uint8_t(pressure & 0x7f)};
_sendMessage(cmd, 3);
}
template <class Sender>
void MIDIEncoder<Sender>::controlChange(uint8_t chan, uint8_t control, uint8_t value)
{
uint8_t cmd[3] = {uint8_t(int(Status::ControlChange) | (chan & 0xf)),
uint8_t(control & 0x7f), uint8_t(value & 0x7f)};
_sendMessage(cmd, 3);
}
template <class Sender>
void MIDIEncoder<Sender>::programChange(uint8_t chan, uint8_t program)
{
uint8_t cmd[2] = {uint8_t(int(Status::ProgramChange) | (chan & 0xf)),
uint8_t(program & 0x7f)};
_sendMessage(cmd, 2);
}
template <class Sender>
void MIDIEncoder<Sender>::channelPressure(uint8_t chan, uint8_t pressure)
{
uint8_t cmd[2] = {uint8_t(int(Status::ChannelPressure) | (chan & 0xf)),
uint8_t(pressure & 0x7f)};
_sendMessage(cmd, 2);
}
template <class Sender>
void MIDIEncoder<Sender>::pitchBend(uint8_t chan, int16_t pitch)
{
uint8_t cmd[3] = {uint8_t(int(Status::PitchBend) | (chan & 0xf)),
uint8_t((pitch % 128) & 0x7f), uint8_t((pitch / 128) & 0x7f)};
_sendMessage(cmd, 3);
}
template <class Sender>
void MIDIEncoder<Sender>::allSoundOff(uint8_t chan)
{
uint8_t cmd[3] = {uint8_t(int(Status::ControlChange) | (chan & 0xf)),
120, 0};
_sendMessage(cmd, 3);
}
template <class Sender>
void MIDIEncoder<Sender>::resetAllControllers(uint8_t chan)
{
uint8_t cmd[3] = {uint8_t(int(Status::ControlChange) | (chan & 0xf)),
121, 0};
_sendMessage(cmd, 3);
}
template <class Sender>
void MIDIEncoder<Sender>::localControl(uint8_t chan, bool on)
{
uint8_t cmd[3] = {uint8_t(int(Status::ControlChange) | (chan & 0xf)),
122, uint8_t(on ? 127 : 0)};
_sendMessage(cmd, 3);
}
template <class Sender>
void MIDIEncoder<Sender>::allNotesOff(uint8_t chan)
{
uint8_t cmd[3] = {uint8_t(int(Status::ControlChange) | (chan & 0xf)),
123, 0};
_sendMessage(cmd, 3);
}
template <class Sender>
void MIDIEncoder<Sender>::omniMode(uint8_t chan, bool on)
{
uint8_t cmd[3] = {uint8_t(int(Status::ControlChange) | (chan & 0xf)),
uint8_t(on ? 125 : 124), 0};
_sendMessage(cmd, 3);
}
template <class Sender>
void MIDIEncoder<Sender>::polyMode(uint8_t chan, bool on)
{
uint8_t cmd[3] = {uint8_t(int(Status::ControlChange) | (chan & 0xf)),
uint8_t(on ? 127 : 126), 0};
_sendMessage(cmd, 3);
}
template <class Sender>
void MIDIEncoder<Sender>::sysex(const void* data, size_t len)
{
uint8_t cmd = uint8_t(Status::SysEx);
_sendMessage(&cmd, 1);
_sendContinuedValue(len);
m_sender.send(data, len); m_sender.send(data, len);
cmd = uint8_t(Status::SysExTerm); }
_sendMessage(&cmd, 1);
} }
template <class Sender> template <class Sender>
void MIDIEncoder<Sender>::timeCodeQuarterFrame(uint8_t message, uint8_t value) void MIDIEncoder<Sender>::_sendContinuedValue(uint32_t val) {
{ uint8_t send[3] = {};
uint8_t cmd[2] = {uint8_t(int(Status::TimecodeQuarterFrame)), uint8_t* ptr = nullptr;
uint8_t((message & 0x7 << 4) | (value & 0xf))}; if (val >= 0x4000) {
_sendMessage(cmd, 2); ptr = &send[0];
send[0] = 0x80 | ((val / 0x4000) & 0x7f);
send[1] = 0x80;
val &= 0x3fff;
}
if (val >= 0x80) {
if (!ptr)
ptr = &send[1];
send[1] = 0x80 | ((val / 0x80) & 0x7f);
}
if (!ptr)
ptr = &send[2];
send[2] = val & 0x7f;
m_sender.send(ptr, 3 - (ptr - send));
} }
template <class Sender> template <class Sender>
void MIDIEncoder<Sender>::songPositionPointer(uint16_t pointer) void MIDIEncoder<Sender>::noteOff(uint8_t chan, uint8_t key, uint8_t velocity) {
{ uint8_t cmd[3] = {uint8_t(int(Status::NoteOff) | (chan & 0xf)), uint8_t(key & 0x7f), uint8_t(velocity & 0x7f)};
uint8_t cmd[3] = {uint8_t(int(Status::SongPositionPointer)), _sendMessage(cmd, 3);
uint8_t((pointer % 128) & 0x7f), uint8_t((pointer / 128) & 0x7f)};
_sendMessage(cmd, 3);
} }
template <class Sender> template <class Sender>
void MIDIEncoder<Sender>::songSelect(uint8_t song) void MIDIEncoder<Sender>::noteOn(uint8_t chan, uint8_t key, uint8_t velocity) {
{ uint8_t cmd[3] = {uint8_t(int(Status::NoteOn) | (chan & 0xf)), uint8_t(key & 0x7f), uint8_t(velocity & 0x7f)};
uint8_t cmd[2] = {uint8_t(int(Status::TimecodeQuarterFrame)), _sendMessage(cmd, 3);
uint8_t(song & 0x7f)};
_sendMessage(cmd, 2);
} }
template <class Sender> template <class Sender>
void MIDIEncoder<Sender>::tuneRequest() void MIDIEncoder<Sender>::notePressure(uint8_t chan, uint8_t key, uint8_t pressure) {
{ uint8_t cmd[3] = {uint8_t(int(Status::NotePressure) | (chan & 0xf)), uint8_t(key & 0x7f), uint8_t(pressure & 0x7f)};
uint8_t cmd = uint8_t(Status::TuneRequest); _sendMessage(cmd, 3);
_sendMessage(&cmd, 1);
}
template <class Sender>
void MIDIEncoder<Sender>::startSeq()
{
uint8_t cmd = uint8_t(Status::Start);
_sendMessage(&cmd, 1);
} }
template <class Sender> template <class Sender>
void MIDIEncoder<Sender>::continueSeq() void MIDIEncoder<Sender>::controlChange(uint8_t chan, uint8_t control, uint8_t value) {
{ uint8_t cmd[3] = {uint8_t(int(Status::ControlChange) | (chan & 0xf)), uint8_t(control & 0x7f), uint8_t(value & 0x7f)};
uint8_t cmd = uint8_t(Status::Continue); _sendMessage(cmd, 3);
_sendMessage(&cmd, 1);
} }
template <class Sender> template <class Sender>
void MIDIEncoder<Sender>::stopSeq() void MIDIEncoder<Sender>::programChange(uint8_t chan, uint8_t program) {
{ uint8_t cmd[2] = {uint8_t(int(Status::ProgramChange) | (chan & 0xf)), uint8_t(program & 0x7f)};
uint8_t cmd = uint8_t(Status::Stop); _sendMessage(cmd, 2);
_sendMessage(&cmd, 1);
} }
template <class Sender>
void MIDIEncoder<Sender>::channelPressure(uint8_t chan, uint8_t pressure) {
uint8_t cmd[2] = {uint8_t(int(Status::ChannelPressure) | (chan & 0xf)), uint8_t(pressure & 0x7f)};
_sendMessage(cmd, 2);
}
template <class Sender> template <class Sender>
void MIDIEncoder<Sender>::reset() void MIDIEncoder<Sender>::pitchBend(uint8_t chan, int16_t pitch) {
{ uint8_t cmd[3] = {uint8_t(int(Status::PitchBend) | (chan & 0xf)), uint8_t((pitch % 128) & 0x7f),
uint8_t cmd = uint8_t(Status::Reset); uint8_t((pitch / 128) & 0x7f)};
_sendMessage(&cmd, 1); _sendMessage(cmd, 3);
}
template <class Sender>
void MIDIEncoder<Sender>::allSoundOff(uint8_t chan) {
uint8_t cmd[3] = {uint8_t(int(Status::ControlChange) | (chan & 0xf)), 120, 0};
_sendMessage(cmd, 3);
}
template <class Sender>
void MIDIEncoder<Sender>::resetAllControllers(uint8_t chan) {
uint8_t cmd[3] = {uint8_t(int(Status::ControlChange) | (chan & 0xf)), 121, 0};
_sendMessage(cmd, 3);
}
template <class Sender>
void MIDIEncoder<Sender>::localControl(uint8_t chan, bool on) {
uint8_t cmd[3] = {uint8_t(int(Status::ControlChange) | (chan & 0xf)), 122, uint8_t(on ? 127 : 0)};
_sendMessage(cmd, 3);
}
template <class Sender>
void MIDIEncoder<Sender>::allNotesOff(uint8_t chan) {
uint8_t cmd[3] = {uint8_t(int(Status::ControlChange) | (chan & 0xf)), 123, 0};
_sendMessage(cmd, 3);
}
template <class Sender>
void MIDIEncoder<Sender>::omniMode(uint8_t chan, bool on) {
uint8_t cmd[3] = {uint8_t(int(Status::ControlChange) | (chan & 0xf)), uint8_t(on ? 125 : 124), 0};
_sendMessage(cmd, 3);
}
template <class Sender>
void MIDIEncoder<Sender>::polyMode(uint8_t chan, bool on) {
uint8_t cmd[3] = {uint8_t(int(Status::ControlChange) | (chan & 0xf)), uint8_t(on ? 127 : 126), 0};
_sendMessage(cmd, 3);
}
template <class Sender>
void MIDIEncoder<Sender>::sysex(const void* data, size_t len) {
uint8_t cmd = uint8_t(Status::SysEx);
_sendMessage(&cmd, 1);
_sendContinuedValue(len);
m_sender.send(data, len);
cmd = uint8_t(Status::SysExTerm);
_sendMessage(&cmd, 1);
}
template <class Sender>
void MIDIEncoder<Sender>::timeCodeQuarterFrame(uint8_t message, uint8_t value) {
uint8_t cmd[2] = {uint8_t(int(Status::TimecodeQuarterFrame)), uint8_t((message & 0x7 << 4) | (value & 0xf))};
_sendMessage(cmd, 2);
}
template <class Sender>
void MIDIEncoder<Sender>::songPositionPointer(uint16_t pointer) {
uint8_t cmd[3] = {uint8_t(int(Status::SongPositionPointer)), uint8_t((pointer % 128) & 0x7f),
uint8_t((pointer / 128) & 0x7f)};
_sendMessage(cmd, 3);
}
template <class Sender>
void MIDIEncoder<Sender>::songSelect(uint8_t song) {
uint8_t cmd[2] = {uint8_t(int(Status::TimecodeQuarterFrame)), uint8_t(song & 0x7f)};
_sendMessage(cmd, 2);
}
template <class Sender>
void MIDIEncoder<Sender>::tuneRequest() {
uint8_t cmd = uint8_t(Status::TuneRequest);
_sendMessage(&cmd, 1);
}
template <class Sender>
void MIDIEncoder<Sender>::startSeq() {
uint8_t cmd = uint8_t(Status::Start);
_sendMessage(&cmd, 1);
}
template <class Sender>
void MIDIEncoder<Sender>::continueSeq() {
uint8_t cmd = uint8_t(Status::Continue);
_sendMessage(&cmd, 1);
}
template <class Sender>
void MIDIEncoder<Sender>::stopSeq() {
uint8_t cmd = uint8_t(Status::Stop);
_sendMessage(&cmd, 1);
}
template <class Sender>
void MIDIEncoder<Sender>::reset() {
uint8_t cmd = uint8_t(Status::Reset);
_sendMessage(&cmd, 1);
} }
template class MIDIEncoder<IMIDIOut>; template class MIDIEncoder<IMIDIOut>;
template class MIDIEncoder<IMIDIInOut>; template class MIDIEncoder<IMIDIInOut>;
} } // namespace boo

View File

@ -6,420 +6,357 @@
#include <pulse/pulseaudio.h> #include <pulse/pulseaudio.h>
#include <unistd.h> #include <unistd.h>
namespace boo namespace boo {
{
static logvisor::Module Log("boo::PulseAudio"); static logvisor::Module Log("boo::PulseAudio");
logvisor::Module ALSALog("boo::ALSA"); logvisor::Module ALSALog("boo::ALSA");
static const uint64_t StereoChans = (1 << PA_CHANNEL_POSITION_FRONT_LEFT) | static const uint64_t StereoChans = (1 << PA_CHANNEL_POSITION_FRONT_LEFT) | (1 << PA_CHANNEL_POSITION_FRONT_RIGHT);
(1 << PA_CHANNEL_POSITION_FRONT_RIGHT);
static const uint64_t QuadChans = (1 << PA_CHANNEL_POSITION_FRONT_LEFT) | static const uint64_t QuadChans = (1 << PA_CHANNEL_POSITION_FRONT_LEFT) | (1 << PA_CHANNEL_POSITION_FRONT_RIGHT) |
(1 << PA_CHANNEL_POSITION_FRONT_RIGHT) | (1 << PA_CHANNEL_POSITION_REAR_LEFT) | (1 << PA_CHANNEL_POSITION_REAR_RIGHT);
(1 << PA_CHANNEL_POSITION_REAR_LEFT) |
(1 << PA_CHANNEL_POSITION_REAR_RIGHT);
static const uint64_t S51Chans = (1 << PA_CHANNEL_POSITION_FRONT_LEFT) | static const uint64_t S51Chans = (1 << PA_CHANNEL_POSITION_FRONT_LEFT) | (1 << PA_CHANNEL_POSITION_FRONT_RIGHT) |
(1 << PA_CHANNEL_POSITION_FRONT_RIGHT) | (1 << PA_CHANNEL_POSITION_REAR_LEFT) | (1 << PA_CHANNEL_POSITION_REAR_RIGHT) |
(1 << PA_CHANNEL_POSITION_REAR_LEFT) | (1 << PA_CHANNEL_POSITION_FRONT_CENTER) | (1 << PA_CHANNEL_POSITION_LFE);
(1 << PA_CHANNEL_POSITION_REAR_RIGHT) |
(1 << PA_CHANNEL_POSITION_FRONT_CENTER) |
(1 << PA_CHANNEL_POSITION_LFE);
static const uint64_t S71Chans = (1 << PA_CHANNEL_POSITION_FRONT_LEFT) | static const uint64_t S71Chans = (1 << PA_CHANNEL_POSITION_FRONT_LEFT) | (1 << PA_CHANNEL_POSITION_FRONT_RIGHT) |
(1 << PA_CHANNEL_POSITION_FRONT_RIGHT) | (1 << PA_CHANNEL_POSITION_REAR_LEFT) | (1 << PA_CHANNEL_POSITION_REAR_RIGHT) |
(1 << PA_CHANNEL_POSITION_REAR_LEFT) | (1 << PA_CHANNEL_POSITION_FRONT_CENTER) | (1 << PA_CHANNEL_POSITION_LFE) |
(1 << PA_CHANNEL_POSITION_REAR_RIGHT) | (1 << PA_CHANNEL_POSITION_SIDE_LEFT) | (1 << PA_CHANNEL_POSITION_SIDE_RIGHT);
(1 << PA_CHANNEL_POSITION_FRONT_CENTER) |
(1 << PA_CHANNEL_POSITION_LFE) |
(1 << PA_CHANNEL_POSITION_SIDE_LEFT) |
(1 << PA_CHANNEL_POSITION_SIDE_RIGHT);
struct PulseAudioVoiceEngine : LinuxMidi struct PulseAudioVoiceEngine : LinuxMidi {
{ pa_mainloop* m_mainloop = nullptr;
pa_mainloop* m_mainloop = nullptr; pa_context* m_ctx = nullptr;
pa_context* m_ctx = nullptr; pa_stream* m_stream = nullptr;
pa_stream* m_stream = nullptr; std::string m_sinkName;
std::string m_sinkName; bool m_handleMove = false;
bool m_handleMove = false; pa_sample_spec m_sampleSpec = {};
pa_sample_spec m_sampleSpec = {}; pa_channel_map m_chanMap = {};
pa_channel_map m_chanMap = {};
int _paWaitReady() int _paWaitReady() {
{ int retval = 0;
int retval = 0; while (pa_context_get_state(m_ctx) < PA_CONTEXT_READY)
while (pa_context_get_state(m_ctx) < PA_CONTEXT_READY) pa_mainloop_iterate(m_mainloop, 1, &retval);
pa_mainloop_iterate(m_mainloop, 1, &retval); return retval;
return retval; }
int _paStreamWaitReady() {
int retval = 0;
while (pa_stream_get_state(m_stream) < PA_STREAM_READY)
pa_mainloop_iterate(m_mainloop, 1, &retval);
return retval;
}
int _paIterate(pa_operation* op) const {
int retval = 0;
while (pa_operation_get_state(op) == PA_OPERATION_RUNNING)
pa_mainloop_iterate(m_mainloop, 1, &retval);
return retval;
}
bool _setupSink() {
if (m_stream) {
pa_stream_disconnect(m_stream);
pa_stream_unref(m_stream);
m_stream = nullptr;
} }
int _paStreamWaitReady() pa_operation* op;
{ m_sampleSpec.format = PA_SAMPLE_INVALID;
int retval = 0; op = pa_context_get_sink_info_by_name(m_ctx, m_sinkName.c_str(), pa_sink_info_cb_t(_getSinkInfoReply), this);
while (pa_stream_get_state(m_stream) < PA_STREAM_READY) _paIterate(op);
pa_mainloop_iterate(m_mainloop, 1, &retval); pa_operation_unref(op);
return retval;
if (m_sampleSpec.format == PA_SAMPLE_INVALID) {
Log.report(logvisor::Error, "Unable to setup audio stream");
goto err;
} }
int _paIterate(pa_operation* op) const m_5msFrames = m_sampleSpec.rate * 5 / 1000;
{
int retval = 0; m_mixInfo.m_sampleRate = m_sampleSpec.rate;
while (pa_operation_get_state(op) == PA_OPERATION_RUNNING) m_mixInfo.m_sampleFormat = SOXR_FLOAT32;
pa_mainloop_iterate(m_mainloop, 1, &retval); m_mixInfo.m_bitsPerSample = 32;
return retval; m_mixInfo.m_periodFrames = m_5msFrames;
if (!(m_stream = pa_stream_new(m_ctx, "master", &m_sampleSpec, &m_chanMap))) {
Log.report(logvisor::Error, "Unable to pa_stream_new(): %s", pa_strerror(pa_context_errno(m_ctx)));
goto err;
} }
bool _setupSink() pa_buffer_attr bufAttr;
{ bufAttr.minreq = uint32_t(m_5msFrames * m_sampleSpec.channels * sizeof(float));
if (m_stream) bufAttr.maxlength = bufAttr.minreq * 24;
{ bufAttr.tlength = bufAttr.maxlength;
pa_stream_disconnect(m_stream); bufAttr.prebuf = UINT32_MAX;
pa_stream_unref(m_stream); bufAttr.fragsize = UINT32_MAX;
m_stream = nullptr;
}
pa_operation* op; if (pa_stream_connect_playback(m_stream, m_sinkName.c_str(), &bufAttr,
m_sampleSpec.format = PA_SAMPLE_INVALID; pa_stream_flags_t(PA_STREAM_START_UNMUTED | PA_STREAM_EARLY_REQUESTS), nullptr,
op = pa_context_get_sink_info_by_name(m_ctx, m_sinkName.c_str(), pa_sink_info_cb_t(_getSinkInfoReply), this); nullptr)) {
_paIterate(op); Log.report(logvisor::Error, "Unable to pa_stream_connect_playback()");
pa_operation_unref(op); goto err;
if (m_sampleSpec.format == PA_SAMPLE_INVALID)
{
Log.report(logvisor::Error, "Unable to setup audio stream");
goto err;
}
m_5msFrames = m_sampleSpec.rate * 5 / 1000;
m_mixInfo.m_sampleRate = m_sampleSpec.rate;
m_mixInfo.m_sampleFormat = SOXR_FLOAT32;
m_mixInfo.m_bitsPerSample = 32;
m_mixInfo.m_periodFrames = m_5msFrames;
if (!(m_stream = pa_stream_new(m_ctx, "master", &m_sampleSpec, &m_chanMap)))
{
Log.report(logvisor::Error, "Unable to pa_stream_new(): %s", pa_strerror(pa_context_errno(m_ctx)));
goto err;
}
pa_buffer_attr bufAttr;
bufAttr.minreq = uint32_t(m_5msFrames * m_sampleSpec.channels * sizeof(float));
bufAttr.maxlength = bufAttr.minreq * 24;
bufAttr.tlength = bufAttr.maxlength;
bufAttr.prebuf = UINT32_MAX;
bufAttr.fragsize = UINT32_MAX;
if (pa_stream_connect_playback(m_stream, m_sinkName.c_str(), &bufAttr,
pa_stream_flags_t(PA_STREAM_START_UNMUTED | PA_STREAM_EARLY_REQUESTS),
nullptr, nullptr))
{
Log.report(logvisor::Error, "Unable to pa_stream_connect_playback()");
goto err;
}
pa_stream_set_moved_callback(m_stream, pa_stream_notify_cb_t(_streamMoved), this);
_paStreamWaitReady();
_resetSampleRate();
return true;
err:
if (m_stream)
{
pa_stream_disconnect(m_stream);
pa_stream_unref(m_stream);
m_stream = nullptr;
}
return false;
} }
PulseAudioVoiceEngine() pa_stream_set_moved_callback(m_stream, pa_stream_notify_cb_t(_streamMoved), this);
{
if (!(m_mainloop = pa_mainloop_new()))
{
Log.report(logvisor::Error, "Unable to pa_mainloop_new()");
return;
}
pa_mainloop_api* mlApi = pa_mainloop_get_api(m_mainloop); _paStreamWaitReady();
pa_proplist* propList = pa_proplist_new();
pa_proplist_sets(propList, PA_PROP_APPLICATION_ICON_NAME, APP->getUniqueName().data());
char pidStr[16];
snprintf(pidStr, 16, "%d", int(getpid()));
pa_proplist_sets(propList, PA_PROP_APPLICATION_PROCESS_ID, pidStr);
if (!(m_ctx = pa_context_new_with_proplist(mlApi, APP->getFriendlyName().data(), propList)))
{
Log.report(logvisor::Error, "Unable to pa_context_new_with_proplist()");
pa_mainloop_free(m_mainloop);
m_mainloop = nullptr;
return;
}
pa_operation* op; _resetSampleRate();
return true;
err:
if (m_stream) {
pa_stream_disconnect(m_stream);
pa_stream_unref(m_stream);
m_stream = nullptr;
}
return false;
}
if (pa_context_connect(m_ctx, nullptr, PA_CONTEXT_NOFLAGS, nullptr)) PulseAudioVoiceEngine() {
{ if (!(m_mainloop = pa_mainloop_new())) {
Log.report(logvisor::Error, "Unable to pa_context_connect()"); Log.report(logvisor::Error, "Unable to pa_mainloop_new()");
goto err; return;
}
_paWaitReady();
op = pa_context_get_server_info(m_ctx, pa_server_info_cb_t(_getServerInfoReply), this);
_paIterate(op);
pa_operation_unref(op);
if (!_setupSink())
goto err;
return;
err:
pa_context_disconnect(m_ctx);
pa_context_unref(m_ctx);
m_ctx = nullptr;
pa_mainloop_free(m_mainloop);
m_mainloop = nullptr;
} }
~PulseAudioVoiceEngine() pa_mainloop_api* mlApi = pa_mainloop_get_api(m_mainloop);
{ pa_proplist* propList = pa_proplist_new();
if (m_stream) pa_proplist_sets(propList, PA_PROP_APPLICATION_ICON_NAME, APP->getUniqueName().data());
{ char pidStr[16];
pa_stream_disconnect(m_stream); snprintf(pidStr, 16, "%d", int(getpid()));
pa_stream_unref(m_stream); pa_proplist_sets(propList, PA_PROP_APPLICATION_PROCESS_ID, pidStr);
if (!(m_ctx = pa_context_new_with_proplist(mlApi, APP->getFriendlyName().data(), propList))) {
Log.report(logvisor::Error, "Unable to pa_context_new_with_proplist()");
pa_mainloop_free(m_mainloop);
m_mainloop = nullptr;
return;
}
pa_operation* op;
if (pa_context_connect(m_ctx, nullptr, PA_CONTEXT_NOFLAGS, nullptr)) {
Log.report(logvisor::Error, "Unable to pa_context_connect()");
goto err;
}
_paWaitReady();
op = pa_context_get_server_info(m_ctx, pa_server_info_cb_t(_getServerInfoReply), this);
_paIterate(op);
pa_operation_unref(op);
if (!_setupSink())
goto err;
return;
err:
pa_context_disconnect(m_ctx);
pa_context_unref(m_ctx);
m_ctx = nullptr;
pa_mainloop_free(m_mainloop);
m_mainloop = nullptr;
}
~PulseAudioVoiceEngine() {
if (m_stream) {
pa_stream_disconnect(m_stream);
pa_stream_unref(m_stream);
}
if (m_ctx) {
pa_context_disconnect(m_ctx);
pa_context_unref(m_ctx);
}
if (m_mainloop) {
pa_mainloop_free(m_mainloop);
}
}
static void _streamMoved(pa_stream* p, PulseAudioVoiceEngine* userdata) {
userdata->m_sinkName = pa_stream_get_device_name(p);
userdata->m_handleMove = true;
}
static void _getServerInfoReply(pa_context* c, const pa_server_info* i, PulseAudioVoiceEngine* userdata) {
userdata->m_sinkName = i->default_sink_name;
}
void _parseAudioChannelSet(const pa_channel_map* chm) {
m_chanMap = *chm;
ChannelMap& chmapOut = m_mixInfo.m_channelMap;
m_mixInfo.m_channels = AudioChannelSet::Unknown;
uint64_t chBits = 0;
chmapOut.m_channelCount = chm->channels;
for (unsigned c = 0; c < chm->channels; ++c) {
chBits |= 1 << chm->map[c];
switch (chm->map[c]) {
case PA_CHANNEL_POSITION_FRONT_LEFT:
chmapOut.m_channels[c] = AudioChannel::FrontLeft;
break;
case PA_CHANNEL_POSITION_FRONT_RIGHT:
chmapOut.m_channels[c] = AudioChannel::FrontRight;
break;
case PA_CHANNEL_POSITION_REAR_LEFT:
chmapOut.m_channels[c] = AudioChannel::RearLeft;
break;
case PA_CHANNEL_POSITION_REAR_RIGHT:
chmapOut.m_channels[c] = AudioChannel::RearRight;
break;
case PA_CHANNEL_POSITION_FRONT_CENTER:
chmapOut.m_channels[c] = AudioChannel::FrontCenter;
break;
case PA_CHANNEL_POSITION_LFE:
chmapOut.m_channels[c] = AudioChannel::LFE;
break;
case PA_CHANNEL_POSITION_SIDE_LEFT:
chmapOut.m_channels[c] = AudioChannel::SideLeft;
break;
case PA_CHANNEL_POSITION_SIDE_RIGHT:
chmapOut.m_channels[c] = AudioChannel::SideRight;
break;
default:
chmapOut.m_channels[c] = AudioChannel::Unknown;
break;
}
}
static const std::array<AudioChannelSet, 4> testSets = {
{AudioChannelSet::Surround71, AudioChannelSet::Surround51, AudioChannelSet::Quad, AudioChannelSet::Stereo}};
for (AudioChannelSet set : testSets) {
switch (set) {
case AudioChannelSet::Stereo: {
if ((chBits & StereoChans) == StereoChans) {
m_mixInfo.m_channels = AudioChannelSet::Stereo;
return;
} }
if (m_ctx) break;
{ }
pa_context_disconnect(m_ctx); case AudioChannelSet::Quad: {
pa_context_unref(m_ctx); if ((chBits & QuadChans) == QuadChans) {
m_mixInfo.m_channels = AudioChannelSet::Quad;
return;
} }
if (m_mainloop) break;
{ }
pa_mainloop_free(m_mainloop); case AudioChannelSet::Surround51: {
if ((chBits & S51Chans) == S51Chans) {
m_mixInfo.m_channels = AudioChannelSet::Surround51;
return;
} }
} break;
}
static void _streamMoved(pa_stream* p, PulseAudioVoiceEngine* userdata) case AudioChannelSet::Surround71: {
{ if ((chBits & S71Chans) == S71Chans) {
userdata->m_sinkName = pa_stream_get_device_name(p); m_mixInfo.m_channels = AudioChannelSet::Surround71;
userdata->m_handleMove = true; return;
}
static void _getServerInfoReply(pa_context* c, const pa_server_info* i, PulseAudioVoiceEngine* userdata)
{
userdata->m_sinkName = i->default_sink_name;
}
void _parseAudioChannelSet(const pa_channel_map* chm)
{
m_chanMap = *chm;
ChannelMap& chmapOut = m_mixInfo.m_channelMap;
m_mixInfo.m_channels = AudioChannelSet::Unknown;
uint64_t chBits = 0;
chmapOut.m_channelCount = chm->channels;
for (unsigned c=0 ; c<chm->channels ; ++c)
{
chBits |= 1 << chm->map[c];
switch (chm->map[c])
{
case PA_CHANNEL_POSITION_FRONT_LEFT:
chmapOut.m_channels[c] = AudioChannel::FrontLeft;
break;
case PA_CHANNEL_POSITION_FRONT_RIGHT:
chmapOut.m_channels[c] = AudioChannel::FrontRight;
break;
case PA_CHANNEL_POSITION_REAR_LEFT:
chmapOut.m_channels[c] = AudioChannel::RearLeft;
break;
case PA_CHANNEL_POSITION_REAR_RIGHT:
chmapOut.m_channels[c] = AudioChannel::RearRight;
break;
case PA_CHANNEL_POSITION_FRONT_CENTER:
chmapOut.m_channels[c] = AudioChannel::FrontCenter;
break;
case PA_CHANNEL_POSITION_LFE:
chmapOut.m_channels[c] = AudioChannel::LFE;
break;
case PA_CHANNEL_POSITION_SIDE_LEFT:
chmapOut.m_channels[c] = AudioChannel::SideLeft;
break;
case PA_CHANNEL_POSITION_SIDE_RIGHT:
chmapOut.m_channels[c] = AudioChannel::SideRight;
break;
default:
chmapOut.m_channels[c] = AudioChannel::Unknown;
break;
}
} }
break;
}
default:
break;
}
}
}
static const std::array<AudioChannelSet, 4> testSets = static void _getSinkInfoReply(pa_context* c, const pa_sink_info* i, int eol, PulseAudioVoiceEngine* userdata) {
{{AudioChannelSet::Surround71, AudioChannelSet::Surround51, if (!i)
AudioChannelSet::Quad, AudioChannelSet::Stereo}}; return;
for (AudioChannelSet set : testSets) userdata->m_sampleSpec.format = PA_SAMPLE_FLOAT32;
{ userdata->m_sampleSpec.rate = i->sample_spec.rate;
switch (set) userdata->m_sampleSpec.channels = i->sample_spec.channels;
{ userdata->_parseAudioChannelSet(&i->channel_map);
case AudioChannelSet::Stereo: }
{
if ((chBits & StereoChans) == StereoChans) mutable std::vector<std::pair<std::string, std::string>> m_sinks;
{ static void _getSinkInfoListReply(pa_context* c, const pa_sink_info* i, int eol, PulseAudioVoiceEngine* userdata) {
m_mixInfo.m_channels = AudioChannelSet::Stereo; if (i)
return; userdata->m_sinks.push_back(std::make_pair(i->name, i->description));
} }
break; std::vector<std::pair<std::string, std::string>> enumerateAudioOutputs() const {
} pa_operation* op = pa_context_get_sink_info_list(m_ctx, pa_sink_info_cb_t(_getSinkInfoListReply), (void*)this);
case AudioChannelSet::Quad: _paIterate(op);
{ pa_operation_unref(op);
if ((chBits & QuadChans) == QuadChans) std::vector<std::pair<std::string, std::string>> ret;
{ ret.swap(m_sinks);
m_mixInfo.m_channels = AudioChannelSet::Quad; return ret;
return; }
}
break; std::string getCurrentAudioOutput() const { return m_sinkName; }
}
case AudioChannelSet::Surround51: bool m_sinkOk = false;
{ static void _checkAudioSinkReply(pa_context* c, const pa_sink_info* i, int eol, PulseAudioVoiceEngine* userdata) {
if ((chBits & S51Chans) == S51Chans) if (i)
{ userdata->m_sinkOk = true;
m_mixInfo.m_channels = AudioChannelSet::Surround51; }
return; bool setCurrentAudioOutput(const char* name) {
} m_sinkOk = false;
break; pa_operation* op;
} op = pa_context_get_sink_info_by_name(m_ctx, name, pa_sink_info_cb_t(_checkAudioSinkReply), this);
case AudioChannelSet::Surround71: _paIterate(op);
{ pa_operation_unref(op);
if ((chBits & S71Chans) == S71Chans) if (m_sinkOk) {
{ m_sinkName = name;
m_mixInfo.m_channels = AudioChannelSet::Surround71; return _setupSink();
return; }
} return false;
break; }
}
default: break; void _doIterate() {
} int retval;
} pa_mainloop_iterate(m_mainloop, 1, &retval);
if (m_handleMove) {
m_handleMove = false;
_setupSink();
}
}
void pumpAndMixVoices() {
if (!m_stream) {
/* Dummy pump mode - use failsafe defaults for 1/60sec of samples */
m_mixInfo.m_sampleRate = 32000.0;
m_mixInfo.m_sampleFormat = SOXR_FLOAT32_I;
m_mixInfo.m_bitsPerSample = 32;
m_5msFrames = 32000 / 60;
m_mixInfo.m_periodFrames = m_5msFrames;
m_mixInfo.m_channels = AudioChannelSet::Stereo;
m_mixInfo.m_channelMap.m_channelCount = 2;
m_mixInfo.m_channelMap.m_channels[0] = AudioChannel::FrontLeft;
m_mixInfo.m_channelMap.m_channels[1] = AudioChannel::FrontRight;
_pumpAndMixVoices(m_5msFrames, (float*)nullptr);
return;
} }
static void _getSinkInfoReply(pa_context* c, const pa_sink_info* i, int eol, PulseAudioVoiceEngine* userdata) size_t writableSz = pa_stream_writable_size(m_stream);
{ size_t frameSz = m_mixInfo.m_channelMap.m_channelCount * sizeof(float);
if (!i) size_t writableFrames = writableSz / frameSz;
return; size_t writablePeriods = writableFrames / m_mixInfo.m_periodFrames;
userdata->m_sampleSpec.format = PA_SAMPLE_FLOAT32;
userdata->m_sampleSpec.rate = i->sample_spec.rate; if (!writablePeriods) {
userdata->m_sampleSpec.channels = i->sample_spec.channels; _doIterate();
userdata->_parseAudioChannelSet(&i->channel_map); return;
} }
mutable std::vector<std::pair<std::string, std::string>> m_sinks; void* data;
static void _getSinkInfoListReply(pa_context* c, const pa_sink_info* i, int eol, PulseAudioVoiceEngine* userdata) size_t periodSz = m_mixInfo.m_periodFrames * frameSz;
{ size_t nbytes = writablePeriods * periodSz;
if (i) if (pa_stream_begin_write(m_stream, &data, &nbytes)) {
userdata->m_sinks.push_back(std::make_pair(i->name, i->description)); pa_stream_state_t st = pa_stream_get_state(m_stream);
} Log.report(logvisor::Error, "Unable to pa_stream_begin_write(): %s %d", pa_strerror(pa_context_errno(m_ctx)), st);
std::vector<std::pair<std::string, std::string>> enumerateAudioOutputs() const _doIterate();
{ return;
pa_operation* op = pa_context_get_sink_info_list(m_ctx, pa_sink_info_cb_t(_getSinkInfoListReply), (void*)this);
_paIterate(op);
pa_operation_unref(op);
std::vector<std::pair<std::string, std::string>> ret;
ret.swap(m_sinks);
return ret;
} }
std::string getCurrentAudioOutput() const writablePeriods = nbytes / periodSz;
{ size_t periodSamples = m_mixInfo.m_periodFrames * m_mixInfo.m_channelMap.m_channelCount;
return m_sinkName; _pumpAndMixVoices(m_mixInfo.m_periodFrames * writablePeriods, reinterpret_cast<float*>(data));
}
bool m_sinkOk = false; if (pa_stream_write(m_stream, data, nbytes, nullptr, 0, PA_SEEK_RELATIVE))
static void _checkAudioSinkReply(pa_context* c, const pa_sink_info* i, int eol, PulseAudioVoiceEngine* userdata) Log.report(logvisor::Error, "Unable to pa_stream_write()");
{
if (i)
userdata->m_sinkOk = true;
}
bool setCurrentAudioOutput(const char* name)
{
m_sinkOk = false;
pa_operation* op;
op = pa_context_get_sink_info_by_name(m_ctx, name, pa_sink_info_cb_t(_checkAudioSinkReply), this);
_paIterate(op);
pa_operation_unref(op);
if (m_sinkOk)
{
m_sinkName = name;
return _setupSink();
}
return false;
}
void _doIterate() _doIterate();
{ }
int retval;
pa_mainloop_iterate(m_mainloop, 1, &retval);
if (m_handleMove)
{
m_handleMove = false;
_setupSink();
}
}
void pumpAndMixVoices()
{
if (!m_stream)
{
/* Dummy pump mode - use failsafe defaults for 1/60sec of samples */
m_mixInfo.m_sampleRate = 32000.0;
m_mixInfo.m_sampleFormat = SOXR_FLOAT32_I;
m_mixInfo.m_bitsPerSample = 32;
m_5msFrames = 32000 / 60;
m_mixInfo.m_periodFrames = m_5msFrames;
m_mixInfo.m_channels = AudioChannelSet::Stereo;
m_mixInfo.m_channelMap.m_channelCount = 2;
m_mixInfo.m_channelMap.m_channels[0] = AudioChannel::FrontLeft;
m_mixInfo.m_channelMap.m_channels[1] = AudioChannel::FrontRight;
_pumpAndMixVoices(m_5msFrames, (float*)nullptr);
return;
}
size_t writableSz = pa_stream_writable_size(m_stream);
size_t frameSz = m_mixInfo.m_channelMap.m_channelCount * sizeof(float);
size_t writableFrames = writableSz / frameSz;
size_t writablePeriods = writableFrames / m_mixInfo.m_periodFrames;
if (!writablePeriods)
{
_doIterate();
return;
}
void* data;
size_t periodSz = m_mixInfo.m_periodFrames * frameSz;
size_t nbytes = writablePeriods * periodSz;
if (pa_stream_begin_write(m_stream, &data, &nbytes))
{
pa_stream_state_t st = pa_stream_get_state(m_stream);
Log.report(logvisor::Error, "Unable to pa_stream_begin_write(): %s %d",
pa_strerror(pa_context_errno(m_ctx)), st);
_doIterate();
return;
}
writablePeriods = nbytes / periodSz;
size_t periodSamples = m_mixInfo.m_periodFrames * m_mixInfo.m_channelMap.m_channelCount;
_pumpAndMixVoices(m_mixInfo.m_periodFrames * writablePeriods, reinterpret_cast<float*>(data));
if (pa_stream_write(m_stream, data, nbytes, nullptr, 0, PA_SEEK_RELATIVE))
Log.report(logvisor::Error, "Unable to pa_stream_write()");
_doIterate();
}
}; };
std::unique_ptr<IAudioVoiceEngine> NewAudioVoiceEngine() std::unique_ptr<IAudioVoiceEngine> NewAudioVoiceEngine() { return std::make_unique<PulseAudioVoiceEngine>(); }
{
return std::make_unique<PulseAudioVoiceEngine>();
}
} } // namespace boo

File diff suppressed because it is too large Load Diff

View File

@ -2,305 +2,247 @@
#include "logvisor/logvisor.hpp" #include "logvisor/logvisor.hpp"
#include "boo/audiodev/IAudioVoiceEngine.hpp" #include "boo/audiodev/IAudioVoiceEngine.hpp"
namespace boo namespace boo {
{
static logvisor::Module Log("boo::WAVOut"); static logvisor::Module Log("boo::WAVOut");
struct WAVOutVoiceEngine : BaseAudioVoiceEngine struct WAVOutVoiceEngine : BaseAudioVoiceEngine {
{ std::vector<float> m_interleavedBuf;
std::vector<float> m_interleavedBuf;
AudioChannelSet _getAvailableSet() AudioChannelSet _getAvailableSet() { return AudioChannelSet::Stereo; }
{
return AudioChannelSet::Stereo; std::string getCurrentAudioOutput() const { return "wavout"; }
bool setCurrentAudioOutput(const char* name) { return false; }
std::vector<std::pair<std::string, std::string>> enumerateAudioOutputs() const { return {{"wavout", "WAVOut"}}; }
std::vector<std::pair<std::string, std::string>> enumerateMIDIInputs() const { return {}; }
bool supportsVirtualMIDIIn() const { return false; }
ReceiveFunctor* m_midiReceiver = nullptr;
struct MIDIIn : public IMIDIIn {
MIDIIn(WAVOutVoiceEngine* parent, bool virt, ReceiveFunctor&& receiver)
: IMIDIIn(parent, virt, std::move(receiver)) {}
std::string description() const { return "WAVOut MIDI"; }
};
std::unique_ptr<IMIDIIn> newVirtualMIDIIn(ReceiveFunctor&& receiver) {
std::unique_ptr<IMIDIIn> ret = std::make_unique<MIDIIn>(nullptr, true, std::move(receiver));
m_midiReceiver = &ret->m_receiver;
return ret;
}
std::unique_ptr<IMIDIOut> newVirtualMIDIOut() { return {}; }
std::unique_ptr<IMIDIInOut> newVirtualMIDIInOut(ReceiveFunctor&& receiver) { return {}; }
std::unique_ptr<IMIDIIn> newRealMIDIIn(const char* name, ReceiveFunctor&& receiver) { return {}; }
std::unique_ptr<IMIDIOut> newRealMIDIOut(const char* name) { return {}; }
std::unique_ptr<IMIDIInOut> newRealMIDIInOut(const char* name, ReceiveFunctor&& receiver) { return {}; }
bool useMIDILock() const { return false; }
FILE* m_fp = nullptr;
size_t m_bytesWritten = 0;
void prepareWAV(double sampleRate, int numChans) {
uint32_t speakerMask = 0;
switch (numChans) {
default:
case 2:
numChans = 2;
m_mixInfo.m_channels = AudioChannelSet::Stereo;
m_mixInfo.m_channelMap.m_channelCount = 2;
m_mixInfo.m_channelMap.m_channels[0] = AudioChannel::FrontLeft;
m_mixInfo.m_channelMap.m_channels[1] = AudioChannel::FrontRight;
speakerMask = 0x00000001 | 0x00000002;
break;
case 4:
numChans = 4;
m_mixInfo.m_channels = AudioChannelSet::Quad;
m_mixInfo.m_channelMap.m_channelCount = 4;
m_mixInfo.m_channelMap.m_channels[0] = AudioChannel::FrontLeft;
m_mixInfo.m_channelMap.m_channels[1] = AudioChannel::FrontRight;
m_mixInfo.m_channelMap.m_channels[2] = AudioChannel::RearLeft;
m_mixInfo.m_channelMap.m_channels[3] = AudioChannel::RearRight;
speakerMask = 0x00000001 | 0x00000002 | 0x00000010 | 0x00000020;
break;
case 6:
numChans = 6;
m_mixInfo.m_channels = AudioChannelSet::Surround51;
m_mixInfo.m_channelMap.m_channelCount = 6;
m_mixInfo.m_channelMap.m_channels[0] = AudioChannel::FrontLeft;
m_mixInfo.m_channelMap.m_channels[1] = AudioChannel::FrontRight;
m_mixInfo.m_channelMap.m_channels[2] = AudioChannel::FrontCenter;
m_mixInfo.m_channelMap.m_channels[3] = AudioChannel::LFE;
m_mixInfo.m_channelMap.m_channels[4] = AudioChannel::RearLeft;
m_mixInfo.m_channelMap.m_channels[5] = AudioChannel::RearRight;
speakerMask = 0x00000001 | 0x00000002 | 0x00000004 | 0x00000008 | 0x00000010 | 0x00000020;
break;
case 8:
numChans = 8;
m_mixInfo.m_channels = AudioChannelSet::Surround71;
m_mixInfo.m_channelMap.m_channelCount = 8;
m_mixInfo.m_channelMap.m_channels[0] = AudioChannel::FrontLeft;
m_mixInfo.m_channelMap.m_channels[1] = AudioChannel::FrontRight;
m_mixInfo.m_channelMap.m_channels[2] = AudioChannel::FrontCenter;
m_mixInfo.m_channelMap.m_channels[3] = AudioChannel::LFE;
m_mixInfo.m_channelMap.m_channels[4] = AudioChannel::RearLeft;
m_mixInfo.m_channelMap.m_channels[5] = AudioChannel::RearRight;
m_mixInfo.m_channelMap.m_channels[6] = AudioChannel::SideLeft;
m_mixInfo.m_channelMap.m_channels[7] = AudioChannel::SideRight;
speakerMask =
0x00000001 | 0x00000002 | 0x00000004 | 0x00000008 | 0x00000010 | 0x00000020 | 0x00000200 | 0x00000400;
break;
} }
std::string getCurrentAudioOutput() const if (numChans == 2) {
{ fwrite("RIFF", 1, 4, m_fp);
return "wavout"; uint32_t dataSize = 0;
uint32_t chunkSize = 36 + dataSize;
fwrite(&chunkSize, 1, 4, m_fp);
fwrite("WAVE", 1, 4, m_fp);
fwrite("fmt ", 1, 4, m_fp);
uint32_t sixteen = 16;
fwrite(&sixteen, 1, 4, m_fp);
uint16_t audioFmt = 3;
fwrite(&audioFmt, 1, 2, m_fp);
uint16_t chCount = numChans;
fwrite(&chCount, 1, 2, m_fp);
uint32_t sampRate = sampleRate;
fwrite(&sampRate, 1, 4, m_fp);
uint16_t blockAlign = 4 * numChans;
uint32_t byteRate = sampRate * blockAlign;
fwrite(&byteRate, 1, 4, m_fp);
fwrite(&blockAlign, 1, 2, m_fp);
uint16_t bps = 32;
fwrite(&bps, 1, 2, m_fp);
fwrite("data", 1, 4, m_fp);
fwrite(&dataSize, 1, 4, m_fp);
} else {
fwrite("RIFF", 1, 4, m_fp);
uint32_t dataSize = 0;
uint32_t chunkSize = 60 + dataSize;
fwrite(&chunkSize, 1, 4, m_fp);
fwrite("WAVE", 1, 4, m_fp);
fwrite("fmt ", 1, 4, m_fp);
uint32_t forty = 40;
fwrite(&forty, 1, 4, m_fp);
uint16_t audioFmt = 0xFFFE;
fwrite(&audioFmt, 1, 2, m_fp);
uint16_t chCount = numChans;
fwrite(&chCount, 1, 2, m_fp);
uint32_t sampRate = sampleRate;
fwrite(&sampRate, 1, 4, m_fp);
uint16_t blockAlign = 4 * numChans;
uint32_t byteRate = sampRate * blockAlign;
fwrite(&byteRate, 1, 4, m_fp);
fwrite(&blockAlign, 1, 2, m_fp);
uint16_t bps = 32;
fwrite(&bps, 1, 2, m_fp);
uint16_t extSize = 22;
fwrite(&extSize, 1, 2, m_fp);
fwrite(&bps, 1, 2, m_fp);
fwrite(&speakerMask, 1, 4, m_fp);
fwrite("\x03\x00\x00\x00\x00\x00\x10\x00\x80\x00\x00\xaa\x00\x38\x9b\x71", 1, 16, m_fp);
fwrite("data", 1, 4, m_fp);
fwrite(&dataSize, 1, 4, m_fp);
} }
bool setCurrentAudioOutput(const char* name) m_mixInfo.m_periodFrames = 512;
{ m_mixInfo.m_sampleRate = sampleRate;
return false; m_mixInfo.m_sampleFormat = SOXR_FLOAT32_I;
} m_mixInfo.m_bitsPerSample = 32;
_buildAudioRenderClient();
}
std::vector<std::pair<std::string, std::string>> enumerateAudioOutputs() const WAVOutVoiceEngine(const char* path, double sampleRate, int numChans) {
{ m_fp = fopen(path, "wb");
return {{"wavout", "WAVOut"}}; if (!m_fp)
} return;
prepareWAV(sampleRate, numChans);
std::vector<std::pair<std::string, std::string>> enumerateMIDIInputs() const }
{
return {};
}
bool supportsVirtualMIDIIn() const
{
return false;
}
ReceiveFunctor* m_midiReceiver = nullptr;
struct MIDIIn : public IMIDIIn
{
MIDIIn(WAVOutVoiceEngine* parent, bool virt, ReceiveFunctor&& receiver)
: IMIDIIn(parent, virt, std::move(receiver)) {}
std::string description() const
{
return "WAVOut MIDI";
}
};
std::unique_ptr<IMIDIIn> newVirtualMIDIIn(ReceiveFunctor&& receiver)
{
std::unique_ptr<IMIDIIn> ret = std::make_unique<MIDIIn>(nullptr, true, std::move(receiver));
m_midiReceiver = &ret->m_receiver;
return ret;
}
std::unique_ptr<IMIDIOut> newVirtualMIDIOut()
{
return {};
}
std::unique_ptr<IMIDIInOut> newVirtualMIDIInOut(ReceiveFunctor&& receiver)
{
return {};
}
std::unique_ptr<IMIDIIn> newRealMIDIIn(const char* name, ReceiveFunctor&& receiver)
{
return {};
}
std::unique_ptr<IMIDIOut> newRealMIDIOut(const char* name)
{
return {};
}
std::unique_ptr<IMIDIInOut> newRealMIDIInOut(const char* name, ReceiveFunctor&& receiver)
{
return {};
}
bool useMIDILock() const {return false;}
FILE* m_fp = nullptr;
size_t m_bytesWritten = 0;
void prepareWAV(double sampleRate, int numChans)
{
uint32_t speakerMask = 0;
switch (numChans)
{
default:
case 2:
numChans = 2;
m_mixInfo.m_channels = AudioChannelSet::Stereo;
m_mixInfo.m_channelMap.m_channelCount = 2;
m_mixInfo.m_channelMap.m_channels[0] = AudioChannel::FrontLeft;
m_mixInfo.m_channelMap.m_channels[1] = AudioChannel::FrontRight;
speakerMask = 0x00000001 | 0x00000002;
break;
case 4:
numChans = 4;
m_mixInfo.m_channels = AudioChannelSet::Quad;
m_mixInfo.m_channelMap.m_channelCount = 4;
m_mixInfo.m_channelMap.m_channels[0] = AudioChannel::FrontLeft;
m_mixInfo.m_channelMap.m_channels[1] = AudioChannel::FrontRight;
m_mixInfo.m_channelMap.m_channels[2] = AudioChannel::RearLeft;
m_mixInfo.m_channelMap.m_channels[3] = AudioChannel::RearRight;
speakerMask = 0x00000001 | 0x00000002 | 0x00000010 | 0x00000020;
break;
case 6:
numChans = 6;
m_mixInfo.m_channels = AudioChannelSet::Surround51;
m_mixInfo.m_channelMap.m_channelCount = 6;
m_mixInfo.m_channelMap.m_channels[0] = AudioChannel::FrontLeft;
m_mixInfo.m_channelMap.m_channels[1] = AudioChannel::FrontRight;
m_mixInfo.m_channelMap.m_channels[2] = AudioChannel::FrontCenter;
m_mixInfo.m_channelMap.m_channels[3] = AudioChannel::LFE;
m_mixInfo.m_channelMap.m_channels[4] = AudioChannel::RearLeft;
m_mixInfo.m_channelMap.m_channels[5] = AudioChannel::RearRight;
speakerMask = 0x00000001 | 0x00000002 | 0x00000004 | 0x00000008 | 0x00000010 | 0x00000020;
break;
case 8:
numChans = 8;
m_mixInfo.m_channels = AudioChannelSet::Surround71;
m_mixInfo.m_channelMap.m_channelCount = 8;
m_mixInfo.m_channelMap.m_channels[0] = AudioChannel::FrontLeft;
m_mixInfo.m_channelMap.m_channels[1] = AudioChannel::FrontRight;
m_mixInfo.m_channelMap.m_channels[2] = AudioChannel::FrontCenter;
m_mixInfo.m_channelMap.m_channels[3] = AudioChannel::LFE;
m_mixInfo.m_channelMap.m_channels[4] = AudioChannel::RearLeft;
m_mixInfo.m_channelMap.m_channels[5] = AudioChannel::RearRight;
m_mixInfo.m_channelMap.m_channels[6] = AudioChannel::SideLeft;
m_mixInfo.m_channelMap.m_channels[7] = AudioChannel::SideRight;
speakerMask = 0x00000001 | 0x00000002 | 0x00000004 | 0x00000008 | 0x00000010 | 0x00000020 | 0x00000200 | 0x00000400;
break;
}
if (numChans == 2)
{
fwrite("RIFF", 1, 4, m_fp);
uint32_t dataSize = 0;
uint32_t chunkSize = 36 + dataSize;
fwrite(&chunkSize, 1, 4, m_fp);
fwrite("WAVE", 1, 4, m_fp);
fwrite("fmt ", 1, 4, m_fp);
uint32_t sixteen = 16;
fwrite(&sixteen, 1, 4, m_fp);
uint16_t audioFmt = 3;
fwrite(&audioFmt, 1, 2, m_fp);
uint16_t chCount = numChans;
fwrite(&chCount, 1, 2, m_fp);
uint32_t sampRate = sampleRate;
fwrite(&sampRate, 1, 4, m_fp);
uint16_t blockAlign = 4 * numChans;
uint32_t byteRate = sampRate * blockAlign;
fwrite(&byteRate, 1, 4, m_fp);
fwrite(&blockAlign, 1, 2, m_fp);
uint16_t bps = 32;
fwrite(&bps, 1, 2, m_fp);
fwrite("data", 1, 4, m_fp);
fwrite(&dataSize, 1, 4, m_fp);
}
else
{
fwrite("RIFF", 1, 4, m_fp);
uint32_t dataSize = 0;
uint32_t chunkSize = 60 + dataSize;
fwrite(&chunkSize, 1, 4, m_fp);
fwrite("WAVE", 1, 4, m_fp);
fwrite("fmt ", 1, 4, m_fp);
uint32_t forty = 40;
fwrite(&forty, 1, 4, m_fp);
uint16_t audioFmt = 0xFFFE;
fwrite(&audioFmt, 1, 2, m_fp);
uint16_t chCount = numChans;
fwrite(&chCount, 1, 2, m_fp);
uint32_t sampRate = sampleRate;
fwrite(&sampRate, 1, 4, m_fp);
uint16_t blockAlign = 4 * numChans;
uint32_t byteRate = sampRate * blockAlign;
fwrite(&byteRate, 1, 4, m_fp);
fwrite(&blockAlign, 1, 2, m_fp);
uint16_t bps = 32;
fwrite(&bps, 1, 2, m_fp);
uint16_t extSize = 22;
fwrite(&extSize, 1, 2, m_fp);
fwrite(&bps, 1, 2, m_fp);
fwrite(&speakerMask, 1, 4, m_fp);
fwrite("\x03\x00\x00\x00\x00\x00\x10\x00\x80\x00\x00\xaa\x00\x38\x9b\x71", 1, 16, m_fp);
fwrite("data", 1, 4, m_fp);
fwrite(&dataSize, 1, 4, m_fp);
}
m_mixInfo.m_periodFrames = 512;
m_mixInfo.m_sampleRate = sampleRate;
m_mixInfo.m_sampleFormat = SOXR_FLOAT32_I;
m_mixInfo.m_bitsPerSample = 32;
_buildAudioRenderClient();
}
WAVOutVoiceEngine(const char* path, double sampleRate, int numChans)
{
m_fp = fopen(path, "wb");
if (!m_fp)
return;
prepareWAV(sampleRate, numChans);
}
#if _WIN32 #if _WIN32
WAVOutVoiceEngine(const wchar_t* path, double sampleRate, int numChans) WAVOutVoiceEngine(const wchar_t* path, double sampleRate, int numChans) {
{ m_fp = _wfopen(path, L"wb");
m_fp = _wfopen(path, L"wb"); if (!m_fp)
if (!m_fp) return;
return; prepareWAV(sampleRate, numChans);
prepareWAV(sampleRate, numChans); }
}
#endif #endif
void finishWav() void finishWav() {
{ uint32_t dataSize = m_bytesWritten;
uint32_t dataSize = m_bytesWritten;
if (m_mixInfo.m_channelMap.m_channelCount == 2) if (m_mixInfo.m_channelMap.m_channelCount == 2) {
{ fseek(m_fp, 4, SEEK_SET);
fseek(m_fp, 4, SEEK_SET); uint32_t chunkSize = 36 + dataSize;
uint32_t chunkSize = 36 + dataSize; fwrite(&chunkSize, 1, 4, m_fp);
fwrite(&chunkSize, 1, 4, m_fp);
fseek(m_fp, 40, SEEK_SET); fseek(m_fp, 40, SEEK_SET);
fwrite(&dataSize, 1, 4, m_fp); fwrite(&dataSize, 1, 4, m_fp);
} } else {
else fseek(m_fp, 4, SEEK_SET);
{ uint32_t chunkSize = 60 + dataSize;
fseek(m_fp, 4, SEEK_SET); fwrite(&chunkSize, 1, 4, m_fp);
uint32_t chunkSize = 60 + dataSize;
fwrite(&chunkSize, 1, 4, m_fp);
fseek(m_fp, 64, SEEK_SET); fseek(m_fp, 64, SEEK_SET);
fwrite(&dataSize, 1, 4, m_fp); fwrite(&dataSize, 1, 4, m_fp);
}
fclose(m_fp);
} }
~WAVOutVoiceEngine() fclose(m_fp);
{ }
finishWav();
}
void _buildAudioRenderClient() ~WAVOutVoiceEngine() { finishWav(); }
{
m_5msFrames = m_mixInfo.m_sampleRate * 5 / 1000;
m_interleavedBuf.resize(m_mixInfo.m_channelMap.m_channelCount * m_5msFrames);
}
void _rebuildAudioRenderClient(double sampleRate, size_t periodFrames) void _buildAudioRenderClient() {
{ m_5msFrames = m_mixInfo.m_sampleRate * 5 / 1000;
m_mixInfo.m_periodFrames = periodFrames; m_interleavedBuf.resize(m_mixInfo.m_channelMap.m_channelCount * m_5msFrames);
m_mixInfo.m_sampleRate = sampleRate; }
_buildAudioRenderClient();
_resetSampleRate();
}
void pumpAndMixVoices() void _rebuildAudioRenderClient(double sampleRate, size_t periodFrames) {
{ m_mixInfo.m_periodFrames = periodFrames;
size_t frameSz = 4 * m_mixInfo.m_channelMap.m_channelCount; m_mixInfo.m_sampleRate = sampleRate;
_pumpAndMixVoices(m_5msFrames, m_interleavedBuf.data()); _buildAudioRenderClient();
fwrite(m_interleavedBuf.data(), 1, m_5msFrames * frameSz, m_fp); _resetSampleRate();
m_bytesWritten += m_5msFrames * frameSz; }
}
void pumpAndMixVoices() {
size_t frameSz = 4 * m_mixInfo.m_channelMap.m_channelCount;
_pumpAndMixVoices(m_5msFrames, m_interleavedBuf.data());
fwrite(m_interleavedBuf.data(), 1, m_5msFrames * frameSz, m_fp);
m_bytesWritten += m_5msFrames * frameSz;
}
}; };
std::unique_ptr<IAudioVoiceEngine> NewWAVAudioVoiceEngine(const char* path, double sampleRate, int numChans) std::unique_ptr<IAudioVoiceEngine> NewWAVAudioVoiceEngine(const char* path, double sampleRate, int numChans) {
{ std::unique_ptr<IAudioVoiceEngine> ret = std::make_unique<WAVOutVoiceEngine>(path, sampleRate, numChans);
std::unique_ptr<IAudioVoiceEngine> ret = std::make_unique<WAVOutVoiceEngine>(path, sampleRate, numChans); if (!static_cast<WAVOutVoiceEngine&>(*ret).m_fp)
if (!static_cast<WAVOutVoiceEngine&>(*ret).m_fp) return {};
return {}; return ret;
return ret;
} }
#if _WIN32 #if _WIN32
std::unique_ptr<IAudioVoiceEngine> NewWAVAudioVoiceEngine(const wchar_t* path, double sampleRate, int numChans) std::unique_ptr<IAudioVoiceEngine> NewWAVAudioVoiceEngine(const wchar_t* path, double sampleRate, int numChans) {
{ std::unique_ptr<IAudioVoiceEngine> ret = std::make_unique<WAVOutVoiceEngine>(path, sampleRate, numChans);
std::unique_ptr<IAudioVoiceEngine> ret = std::make_unique<WAVOutVoiceEngine>(path, sampleRate, numChans); if (!static_cast<WAVOutVoiceEngine&>(*ret).m_fp)
if (!static_cast<WAVOutVoiceEngine&>(*ret).m_fp) return {};
return {}; return ret;
return ret;
} }
#endif #endif
} } // namespace boo

View File

@ -1,18 +1,15 @@
#include "Common.hpp" #include "Common.hpp"
#include <cmath> #include <cmath>
namespace boo namespace boo {
{
void UpdateGammaLUT(ITextureD* tex, float gamma) void UpdateGammaLUT(ITextureD* tex, float gamma) {
{ void* data = tex->map(65536 * 2);
void* data = tex->map(65536 * 2); for (int i = 0; i < 65536; ++i) {
for (int i=0 ; i<65536 ; ++i) float level = std::pow(i / 65535.f, gamma);
{ reinterpret_cast<uint16_t*>(data)[i] = level * 65535.f;
float level = std::pow(i / 65535.f, gamma); }
reinterpret_cast<uint16_t*>(data)[i] = level * 65535.f; tex->unmap();
}
tex->unmap();
} }
} } // namespace boo

View File

@ -10,155 +10,185 @@
#include "boo/graphicsdev/IGraphicsDataFactory.hpp" #include "boo/graphicsdev/IGraphicsDataFactory.hpp"
#include "../Common.hpp" #include "../Common.hpp"
namespace boo namespace boo {
{
struct BaseGraphicsData; struct BaseGraphicsData;
struct BaseGraphicsPool; struct BaseGraphicsPool;
template<class NodeCls, class DataCls = BaseGraphicsData> template <class NodeCls, class DataCls = BaseGraphicsData>
struct GraphicsDataNode; struct GraphicsDataNode;
/** Inherited by data factory implementations to track the head data and pool nodes */ /** Inherited by data factory implementations to track the head data and pool nodes */
struct GraphicsDataFactoryHead struct GraphicsDataFactoryHead {
{ std::recursive_mutex m_dataMutex;
std::recursive_mutex m_dataMutex; BaseGraphicsData* m_dataHead = nullptr;
BaseGraphicsData* m_dataHead = nullptr; BaseGraphicsPool* m_poolHead = nullptr;
BaseGraphicsPool* m_poolHead = nullptr;
~GraphicsDataFactoryHead() ~GraphicsDataFactoryHead() {
{ assert(m_dataHead == nullptr && "Dangling graphics data pools detected");
assert(m_dataHead == nullptr && "Dangling graphics data pools detected"); assert(m_poolHead == nullptr && "Dangling graphics data pools detected");
assert(m_poolHead == nullptr && "Dangling graphics data pools detected"); }
}
}; };
/** Private generalized data container class. /** Private generalized data container class.
* Keeps head pointers to all graphics objects by type * Keeps head pointers to all graphics objects by type
*/ */
struct BaseGraphicsData : ListNode<BaseGraphicsData, GraphicsDataFactoryHead*> struct BaseGraphicsData : ListNode<BaseGraphicsData, GraphicsDataFactoryHead*> {
{ static BaseGraphicsData*& _getHeadPtr(GraphicsDataFactoryHead* head) { return head->m_dataHead; }
static BaseGraphicsData*& _getHeadPtr(GraphicsDataFactoryHead* head) { return head->m_dataHead; } static std::unique_lock<std::recursive_mutex> _getHeadLock(GraphicsDataFactoryHead* head) {
static std::unique_lock<std::recursive_mutex> _getHeadLock(GraphicsDataFactoryHead* head) return std::unique_lock<std::recursive_mutex>{head->m_dataMutex};
{ return std::unique_lock<std::recursive_mutex>{head->m_dataMutex}; } }
__BooTraceFields __BooTraceFields
GraphicsDataNode<IShaderStage, BaseGraphicsData>* m_Ss = nullptr; GraphicsDataNode<IShaderStage, BaseGraphicsData>* m_Ss = nullptr;
GraphicsDataNode<IShaderPipeline, BaseGraphicsData>* m_SPs = nullptr; GraphicsDataNode<IShaderPipeline, BaseGraphicsData>* m_SPs = nullptr;
GraphicsDataNode<IShaderDataBinding, BaseGraphicsData>* m_SBinds = nullptr; GraphicsDataNode<IShaderDataBinding, BaseGraphicsData>* m_SBinds = nullptr;
GraphicsDataNode<IGraphicsBufferS, BaseGraphicsData>* m_SBufs = nullptr; GraphicsDataNode<IGraphicsBufferS, BaseGraphicsData>* m_SBufs = nullptr;
GraphicsDataNode<IGraphicsBufferD, BaseGraphicsData>* m_DBufs = nullptr; GraphicsDataNode<IGraphicsBufferD, BaseGraphicsData>* m_DBufs = nullptr;
GraphicsDataNode<ITextureS, BaseGraphicsData>* m_STexs = nullptr; GraphicsDataNode<ITextureS, BaseGraphicsData>* m_STexs = nullptr;
GraphicsDataNode<ITextureSA, BaseGraphicsData>* m_SATexs = nullptr; GraphicsDataNode<ITextureSA, BaseGraphicsData>* m_SATexs = nullptr;
GraphicsDataNode<ITextureD, BaseGraphicsData>* m_DTexs = nullptr; GraphicsDataNode<ITextureD, BaseGraphicsData>* m_DTexs = nullptr;
GraphicsDataNode<ITextureR, BaseGraphicsData>* m_RTexs = nullptr; GraphicsDataNode<ITextureR, BaseGraphicsData>* m_RTexs = nullptr;
template<class T> GraphicsDataNode<T, BaseGraphicsData>*& getHead(); template <class T>
template<class T> size_t countForward() GraphicsDataNode<T, BaseGraphicsData>*& getHead();
{ auto* head = getHead<T>(); return head ? head->countForward() : 0; } template <class T>
std::unique_lock<std::recursive_mutex> destructorLock() override size_t countForward() {
{ return std::unique_lock<std::recursive_mutex>{m_head->m_dataMutex}; } auto* head = getHead<T>();
return head ? head->countForward() : 0;
}
std::unique_lock<std::recursive_mutex> destructorLock() override {
return std::unique_lock<std::recursive_mutex>{m_head->m_dataMutex};
}
explicit BaseGraphicsData(GraphicsDataFactoryHead& head __BooTraceArgs) explicit BaseGraphicsData(GraphicsDataFactoryHead& head __BooTraceArgs)
: ListNode<BaseGraphicsData, GraphicsDataFactoryHead*>(&head) __BooTraceInitializer : ListNode<BaseGraphicsData, GraphicsDataFactoryHead*>(&head) __BooTraceInitializer {}
{}
}; };
template <> inline GraphicsDataNode<IShaderStage, BaseGraphicsData>*& template <>
BaseGraphicsData::getHead<IShaderStage>() { return m_Ss; } inline GraphicsDataNode<IShaderStage, BaseGraphicsData>*& BaseGraphicsData::getHead<IShaderStage>() {
template <> inline GraphicsDataNode<IShaderPipeline, BaseGraphicsData>*& return m_Ss;
BaseGraphicsData::getHead<IShaderPipeline>() { return m_SPs; } }
template <> inline GraphicsDataNode<IShaderDataBinding, BaseGraphicsData>*& template <>
BaseGraphicsData::getHead<IShaderDataBinding>() { return m_SBinds; } inline GraphicsDataNode<IShaderPipeline, BaseGraphicsData>*& BaseGraphicsData::getHead<IShaderPipeline>() {
template <> inline GraphicsDataNode<IGraphicsBufferS, BaseGraphicsData>*& return m_SPs;
BaseGraphicsData::getHead<IGraphicsBufferS>() { return m_SBufs; } }
template <> inline GraphicsDataNode<IGraphicsBufferD, BaseGraphicsData>*& template <>
BaseGraphicsData::getHead<IGraphicsBufferD>() { return m_DBufs; } inline GraphicsDataNode<IShaderDataBinding, BaseGraphicsData>*& BaseGraphicsData::getHead<IShaderDataBinding>() {
template <> inline GraphicsDataNode<ITextureS, BaseGraphicsData>*& return m_SBinds;
BaseGraphicsData::getHead<ITextureS>() { return m_STexs; } }
template <> inline GraphicsDataNode<ITextureSA, BaseGraphicsData>*& template <>
BaseGraphicsData::getHead<ITextureSA>() { return m_SATexs; } inline GraphicsDataNode<IGraphicsBufferS, BaseGraphicsData>*& BaseGraphicsData::getHead<IGraphicsBufferS>() {
template <> inline GraphicsDataNode<ITextureD, BaseGraphicsData>*& return m_SBufs;
BaseGraphicsData::getHead<ITextureD>() { return m_DTexs; } }
template <> inline GraphicsDataNode<ITextureR, BaseGraphicsData>*& template <>
BaseGraphicsData::getHead<ITextureR>() { return m_RTexs; } inline GraphicsDataNode<IGraphicsBufferD, BaseGraphicsData>*& BaseGraphicsData::getHead<IGraphicsBufferD>() {
return m_DBufs;
}
template <>
inline GraphicsDataNode<ITextureS, BaseGraphicsData>*& BaseGraphicsData::getHead<ITextureS>() {
return m_STexs;
}
template <>
inline GraphicsDataNode<ITextureSA, BaseGraphicsData>*& BaseGraphicsData::getHead<ITextureSA>() {
return m_SATexs;
}
template <>
inline GraphicsDataNode<ITextureD, BaseGraphicsData>*& BaseGraphicsData::getHead<ITextureD>() {
return m_DTexs;
}
template <>
inline GraphicsDataNode<ITextureR, BaseGraphicsData>*& BaseGraphicsData::getHead<ITextureR>() {
return m_RTexs;
}
/** Private generalized pool container class. /** Private generalized pool container class.
* Keeps head pointer to exactly one dynamic buffer while otherwise conforming to BaseGraphicsData * Keeps head pointer to exactly one dynamic buffer while otherwise conforming to BaseGraphicsData
*/ */
struct BaseGraphicsPool : ListNode<BaseGraphicsPool, GraphicsDataFactoryHead*> struct BaseGraphicsPool : ListNode<BaseGraphicsPool, GraphicsDataFactoryHead*> {
{ static BaseGraphicsPool*& _getHeadPtr(GraphicsDataFactoryHead* head) { return head->m_poolHead; }
static BaseGraphicsPool*& _getHeadPtr(GraphicsDataFactoryHead* head) { return head->m_poolHead; } static std::unique_lock<std::recursive_mutex> _getHeadLock(GraphicsDataFactoryHead* head) {
static std::unique_lock<std::recursive_mutex> _getHeadLock(GraphicsDataFactoryHead* head) return std::unique_lock<std::recursive_mutex>{head->m_dataMutex};
{ return std::unique_lock<std::recursive_mutex>{head->m_dataMutex}; } }
__BooTraceFields __BooTraceFields
GraphicsDataNode<IGraphicsBufferD, BaseGraphicsPool>* m_DBufs = nullptr; GraphicsDataNode<IGraphicsBufferD, BaseGraphicsPool>* m_DBufs = nullptr;
template<class T> GraphicsDataNode<T, BaseGraphicsPool>*& getHead(); template <class T>
template<class T> size_t countForward() GraphicsDataNode<T, BaseGraphicsPool>*& getHead();
{ auto* head = getHead<T>(); return head ? head->countForward() : 0; } template <class T>
std::unique_lock<std::recursive_mutex> destructorLock() override size_t countForward() {
{ return std::unique_lock<std::recursive_mutex>{m_head->m_dataMutex}; } auto* head = getHead<T>();
return head ? head->countForward() : 0;
}
std::unique_lock<std::recursive_mutex> destructorLock() override {
return std::unique_lock<std::recursive_mutex>{m_head->m_dataMutex};
}
explicit BaseGraphicsPool(GraphicsDataFactoryHead& head __BooTraceArgs) explicit BaseGraphicsPool(GraphicsDataFactoryHead& head __BooTraceArgs)
: ListNode<BaseGraphicsPool, GraphicsDataFactoryHead*>(&head) __BooTraceInitializer : ListNode<BaseGraphicsPool, GraphicsDataFactoryHead*>(&head) __BooTraceInitializer {}
{}
}; };
template <> inline GraphicsDataNode<IGraphicsBufferD, BaseGraphicsPool>*& template <>
BaseGraphicsPool::getHead<IGraphicsBufferD>() { return m_DBufs; } inline GraphicsDataNode<IGraphicsBufferD, BaseGraphicsPool>*& BaseGraphicsPool::getHead<IGraphicsBufferD>() {
return m_DBufs;
}
/** Private generalised graphics object node. /** Private generalised graphics object node.
* Keeps a strong reference to the data pool that it's a member of; * Keeps a strong reference to the data pool that it's a member of;
* as well as doubly-linked pointers to same-type sibling objects * as well as doubly-linked pointers to same-type sibling objects
*/ */
template<class NodeCls, class DataCls> template <class NodeCls, class DataCls>
struct GraphicsDataNode : ListNode<GraphicsDataNode<NodeCls, DataCls>, ObjToken<DataCls>, NodeCls> struct GraphicsDataNode : ListNode<GraphicsDataNode<NodeCls, DataCls>, ObjToken<DataCls>, NodeCls> {
{ using base = ListNode<GraphicsDataNode<NodeCls, DataCls>, ObjToken<DataCls>, NodeCls>;
using base = ListNode<GraphicsDataNode<NodeCls, DataCls>, ObjToken<DataCls>, NodeCls>; static GraphicsDataNode<NodeCls, DataCls>*& _getHeadPtr(ObjToken<DataCls>& head) {
static GraphicsDataNode<NodeCls, DataCls>*& _getHeadPtr(ObjToken<DataCls>& head) return head->template getHead<NodeCls>();
{ return head->template getHead<NodeCls>(); } }
static std::unique_lock<std::recursive_mutex> _getHeadLock(ObjToken<DataCls>& head) static std::unique_lock<std::recursive_mutex> _getHeadLock(ObjToken<DataCls>& head) {
{ return std::unique_lock<std::recursive_mutex>{head->m_head->m_dataMutex}; } return std::unique_lock<std::recursive_mutex>{head->m_head->m_dataMutex};
}
std::unique_lock<std::recursive_mutex> destructorLock() override std::unique_lock<std::recursive_mutex> destructorLock() override {
{ return std::unique_lock<std::recursive_mutex>{base::m_head->m_head->m_dataMutex}; } return std::unique_lock<std::recursive_mutex>{base::m_head->m_head->m_dataMutex};
}
explicit GraphicsDataNode(const ObjToken<DataCls>& data) explicit GraphicsDataNode(const ObjToken<DataCls>& data)
: ListNode<GraphicsDataNode<NodeCls, DataCls>, ObjToken<DataCls>, NodeCls>(data) : ListNode<GraphicsDataNode<NodeCls, DataCls>, ObjToken<DataCls>, NodeCls>(data) {}
{}
class iterator class iterator {
{ GraphicsDataNode<NodeCls, DataCls>* m_node;
GraphicsDataNode<NodeCls, DataCls>* m_node;
public:
using iterator_category = std::bidirectional_iterator_tag;
using value_type = NodeCls;
using difference_type = std::ptrdiff_t;
using pointer = NodeCls*;
using reference = NodeCls&;
explicit iterator(GraphicsDataNode<NodeCls, DataCls>* node) : m_node(node) {} public:
NodeCls& operator*() const { return *m_node; } using iterator_category = std::bidirectional_iterator_tag;
bool operator!=(const iterator& other) const { return m_node != other.m_node; } using value_type = NodeCls;
iterator& operator++() { m_node = m_node->m_next; return *this; } using difference_type = std::ptrdiff_t;
iterator& operator--() { m_node = m_node->m_prev; return *this; } using pointer = NodeCls*;
}; using reference = NodeCls&;
iterator begin() { return iterator(this); } explicit iterator(GraphicsDataNode<NodeCls, DataCls>* node) : m_node(node) {}
iterator end() { return iterator(nullptr); } NodeCls& operator*() const { return *m_node; }
bool operator!=(const iterator& other) const { return m_node != other.m_node; }
size_t countForward() iterator& operator++() {
{ m_node = m_node->m_next;
size_t ret = 0; return *this;
for (auto& n : *this)
++ret;
return ret;
} }
iterator& operator--() {
m_node = m_node->m_prev;
return *this;
}
};
iterator begin() { return iterator(this); }
iterator end() { return iterator(nullptr); }
size_t countForward() {
size_t ret = 0;
for (auto& n : *this)
++ret;
return ret;
}
}; };
void UpdateGammaLUT(ITextureD* tex, float gamma); void UpdateGammaLUT(ITextureD* tex, float gamma);
} } // namespace boo

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,26 +1,23 @@
#include "boo/graphicsdev/glxew.h" #include "boo/graphicsdev/glxew.h"
#include "logvisor/logvisor.hpp" #include "logvisor/logvisor.hpp"
namespace boo namespace boo {
{
static logvisor::Module Log("boo::GLX"); static logvisor::Module Log("boo::GLX");
void GLXExtensionCheck() void GLXExtensionCheck() {
{ if (!GLXEW_SGI_video_sync)
if (!GLXEW_SGI_video_sync) Log.report(logvisor::Fatal, "GLX_SGI_video_sync not available");
Log.report(logvisor::Fatal, "GLX_SGI_video_sync not available"); if (!GLXEW_EXT_swap_control && !GLXEW_MESA_swap_control && !GLXEW_SGI_swap_control)
if (!GLXEW_EXT_swap_control && !GLXEW_MESA_swap_control && !GLXEW_SGI_swap_control) Log.report(logvisor::Fatal, "swap_control not available");
Log.report(logvisor::Fatal, "swap_control not available");
} }
void GLXEnableVSync(Display* disp, GLXWindow drawable) void GLXEnableVSync(Display* disp, GLXWindow drawable) {
{ if (GLXEW_EXT_swap_control)
if (GLXEW_EXT_swap_control) glXSwapIntervalEXT(disp, drawable, 1);
glXSwapIntervalEXT(disp, drawable, 1); else if (GLXEW_MESA_swap_control)
else if (GLXEW_MESA_swap_control) glXSwapIntervalMESA(1);
glXSwapIntervalMESA(1); else if (GLXEW_SGI_swap_control)
else if (GLXEW_SGI_swap_control) glXSwapIntervalSGI(1);
glXSwapIntervalSGI(1);
} }
} } // namespace boo

File diff suppressed because it is too large Load Diff

View File

@ -184,342 +184,458 @@ PFN_vkCreateDebugReportCallbackEXT CreateDebugReportCallbackEXT;
PFN_vkDestroyDebugReportCallbackEXT DestroyDebugReportCallbackEXT; PFN_vkDestroyDebugReportCallbackEXT DestroyDebugReportCallbackEXT;
PFN_vkDebugReportMessageEXT DebugReportMessageEXT; PFN_vkDebugReportMessageEXT DebugReportMessageEXT;
void init_dispatch_table_top(PFN_vkGetInstanceProcAddr get_instance_proc_addr) void init_dispatch_table_top(PFN_vkGetInstanceProcAddr get_instance_proc_addr) {
{ GetInstanceProcAddr = get_instance_proc_addr;
GetInstanceProcAddr = get_instance_proc_addr;
CreateInstance = reinterpret_cast<PFN_vkCreateInstance>(GetInstanceProcAddr(VK_NULL_HANDLE, "vkCreateInstance")); CreateInstance = reinterpret_cast<PFN_vkCreateInstance>(GetInstanceProcAddr(VK_NULL_HANDLE, "vkCreateInstance"));
EnumerateInstanceExtensionProperties = reinterpret_cast<PFN_vkEnumerateInstanceExtensionProperties>(GetInstanceProcAddr(VK_NULL_HANDLE, "vkEnumerateInstanceExtensionProperties")); EnumerateInstanceExtensionProperties = reinterpret_cast<PFN_vkEnumerateInstanceExtensionProperties>(
EnumerateInstanceLayerProperties = reinterpret_cast<PFN_vkEnumerateInstanceLayerProperties>(GetInstanceProcAddr(VK_NULL_HANDLE, "vkEnumerateInstanceLayerProperties")); GetInstanceProcAddr(VK_NULL_HANDLE, "vkEnumerateInstanceExtensionProperties"));
EnumerateInstanceLayerProperties = reinterpret_cast<PFN_vkEnumerateInstanceLayerProperties>(
GetInstanceProcAddr(VK_NULL_HANDLE, "vkEnumerateInstanceLayerProperties"));
} }
void init_dispatch_table_middle(VkInstance instance, bool include_bottom) void init_dispatch_table_middle(VkInstance instance, bool include_bottom) {
{ GetInstanceProcAddr =
GetInstanceProcAddr = reinterpret_cast<PFN_vkGetInstanceProcAddr>(GetInstanceProcAddr(instance, "vkGetInstanceProcAddr")); reinterpret_cast<PFN_vkGetInstanceProcAddr>(GetInstanceProcAddr(instance, "vkGetInstanceProcAddr"));
DestroyInstance = reinterpret_cast<PFN_vkDestroyInstance>(GetInstanceProcAddr(instance, "vkDestroyInstance")); DestroyInstance = reinterpret_cast<PFN_vkDestroyInstance>(GetInstanceProcAddr(instance, "vkDestroyInstance"));
EnumeratePhysicalDevices = reinterpret_cast<PFN_vkEnumeratePhysicalDevices>(GetInstanceProcAddr(instance, "vkEnumeratePhysicalDevices")); EnumeratePhysicalDevices =
GetPhysicalDeviceFeatures = reinterpret_cast<PFN_vkGetPhysicalDeviceFeatures>(GetInstanceProcAddr(instance, "vkGetPhysicalDeviceFeatures")); reinterpret_cast<PFN_vkEnumeratePhysicalDevices>(GetInstanceProcAddr(instance, "vkEnumeratePhysicalDevices"));
GetPhysicalDeviceFormatProperties = reinterpret_cast<PFN_vkGetPhysicalDeviceFormatProperties>(GetInstanceProcAddr(instance, "vkGetPhysicalDeviceFormatProperties")); GetPhysicalDeviceFeatures =
GetPhysicalDeviceImageFormatProperties = reinterpret_cast<PFN_vkGetPhysicalDeviceImageFormatProperties>(GetInstanceProcAddr(instance, "vkGetPhysicalDeviceImageFormatProperties")); reinterpret_cast<PFN_vkGetPhysicalDeviceFeatures>(GetInstanceProcAddr(instance, "vkGetPhysicalDeviceFeatures"));
GetPhysicalDeviceProperties = reinterpret_cast<PFN_vkGetPhysicalDeviceProperties>(GetInstanceProcAddr(instance, "vkGetPhysicalDeviceProperties")); GetPhysicalDeviceFormatProperties = reinterpret_cast<PFN_vkGetPhysicalDeviceFormatProperties>(
GetPhysicalDeviceQueueFamilyProperties = reinterpret_cast<PFN_vkGetPhysicalDeviceQueueFamilyProperties>(GetInstanceProcAddr(instance, "vkGetPhysicalDeviceQueueFamilyProperties")); GetInstanceProcAddr(instance, "vkGetPhysicalDeviceFormatProperties"));
GetPhysicalDeviceMemoryProperties = reinterpret_cast<PFN_vkGetPhysicalDeviceMemoryProperties>(GetInstanceProcAddr(instance, "vkGetPhysicalDeviceMemoryProperties")); GetPhysicalDeviceImageFormatProperties = reinterpret_cast<PFN_vkGetPhysicalDeviceImageFormatProperties>(
CreateDevice = reinterpret_cast<PFN_vkCreateDevice>(GetInstanceProcAddr(instance, "vkCreateDevice")); GetInstanceProcAddr(instance, "vkGetPhysicalDeviceImageFormatProperties"));
EnumerateDeviceExtensionProperties = reinterpret_cast<PFN_vkEnumerateDeviceExtensionProperties>(GetInstanceProcAddr(instance, "vkEnumerateDeviceExtensionProperties")); GetPhysicalDeviceProperties = reinterpret_cast<PFN_vkGetPhysicalDeviceProperties>(
GetPhysicalDeviceSparseImageFormatProperties = reinterpret_cast<PFN_vkGetPhysicalDeviceSparseImageFormatProperties>(GetInstanceProcAddr(instance, "vkGetPhysicalDeviceSparseImageFormatProperties")); GetInstanceProcAddr(instance, "vkGetPhysicalDeviceProperties"));
DestroySurfaceKHR = reinterpret_cast<PFN_vkDestroySurfaceKHR>(GetInstanceProcAddr(instance, "vkDestroySurfaceKHR")); GetPhysicalDeviceQueueFamilyProperties = reinterpret_cast<PFN_vkGetPhysicalDeviceQueueFamilyProperties>(
GetPhysicalDeviceSurfaceSupportKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceSupportKHR>(GetInstanceProcAddr(instance, "vkGetPhysicalDeviceSurfaceSupportKHR")); GetInstanceProcAddr(instance, "vkGetPhysicalDeviceQueueFamilyProperties"));
GetPhysicalDeviceSurfaceCapabilitiesKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR>(GetInstanceProcAddr(instance, "vkGetPhysicalDeviceSurfaceCapabilitiesKHR")); GetPhysicalDeviceMemoryProperties = reinterpret_cast<PFN_vkGetPhysicalDeviceMemoryProperties>(
GetPhysicalDeviceSurfaceFormatsKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceFormatsKHR>(GetInstanceProcAddr(instance, "vkGetPhysicalDeviceSurfaceFormatsKHR")); GetInstanceProcAddr(instance, "vkGetPhysicalDeviceMemoryProperties"));
GetPhysicalDeviceSurfacePresentModesKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfacePresentModesKHR>(GetInstanceProcAddr(instance, "vkGetPhysicalDeviceSurfacePresentModesKHR")); CreateDevice = reinterpret_cast<PFN_vkCreateDevice>(GetInstanceProcAddr(instance, "vkCreateDevice"));
GetPhysicalDeviceDisplayPropertiesKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceDisplayPropertiesKHR>(GetInstanceProcAddr(instance, "vkGetPhysicalDeviceDisplayPropertiesKHR")); EnumerateDeviceExtensionProperties = reinterpret_cast<PFN_vkEnumerateDeviceExtensionProperties>(
GetPhysicalDeviceDisplayPlanePropertiesKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceDisplayPlanePropertiesKHR>(GetInstanceProcAddr(instance, "vkGetPhysicalDeviceDisplayPlanePropertiesKHR")); GetInstanceProcAddr(instance, "vkEnumerateDeviceExtensionProperties"));
GetDisplayPlaneSupportedDisplaysKHR = reinterpret_cast<PFN_vkGetDisplayPlaneSupportedDisplaysKHR>(GetInstanceProcAddr(instance, "vkGetDisplayPlaneSupportedDisplaysKHR")); GetPhysicalDeviceSparseImageFormatProperties = reinterpret_cast<PFN_vkGetPhysicalDeviceSparseImageFormatProperties>(
GetDisplayModePropertiesKHR = reinterpret_cast<PFN_vkGetDisplayModePropertiesKHR>(GetInstanceProcAddr(instance, "vkGetDisplayModePropertiesKHR")); GetInstanceProcAddr(instance, "vkGetPhysicalDeviceSparseImageFormatProperties"));
CreateDisplayModeKHR = reinterpret_cast<PFN_vkCreateDisplayModeKHR>(GetInstanceProcAddr(instance, "vkCreateDisplayModeKHR")); DestroySurfaceKHR = reinterpret_cast<PFN_vkDestroySurfaceKHR>(GetInstanceProcAddr(instance, "vkDestroySurfaceKHR"));
GetDisplayPlaneCapabilitiesKHR = reinterpret_cast<PFN_vkGetDisplayPlaneCapabilitiesKHR>(GetInstanceProcAddr(instance, "vkGetDisplayPlaneCapabilitiesKHR")); GetPhysicalDeviceSurfaceSupportKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceSupportKHR>(
CreateDisplayPlaneSurfaceKHR = reinterpret_cast<PFN_vkCreateDisplayPlaneSurfaceKHR>(GetInstanceProcAddr(instance, "vkCreateDisplayPlaneSurfaceKHR")); GetInstanceProcAddr(instance, "vkGetPhysicalDeviceSurfaceSupportKHR"));
GetPhysicalDeviceSurfaceCapabilitiesKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR>(
GetInstanceProcAddr(instance, "vkGetPhysicalDeviceSurfaceCapabilitiesKHR"));
GetPhysicalDeviceSurfaceFormatsKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceFormatsKHR>(
GetInstanceProcAddr(instance, "vkGetPhysicalDeviceSurfaceFormatsKHR"));
GetPhysicalDeviceSurfacePresentModesKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfacePresentModesKHR>(
GetInstanceProcAddr(instance, "vkGetPhysicalDeviceSurfacePresentModesKHR"));
GetPhysicalDeviceDisplayPropertiesKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceDisplayPropertiesKHR>(
GetInstanceProcAddr(instance, "vkGetPhysicalDeviceDisplayPropertiesKHR"));
GetPhysicalDeviceDisplayPlanePropertiesKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceDisplayPlanePropertiesKHR>(
GetInstanceProcAddr(instance, "vkGetPhysicalDeviceDisplayPlanePropertiesKHR"));
GetDisplayPlaneSupportedDisplaysKHR = reinterpret_cast<PFN_vkGetDisplayPlaneSupportedDisplaysKHR>(
GetInstanceProcAddr(instance, "vkGetDisplayPlaneSupportedDisplaysKHR"));
GetDisplayModePropertiesKHR = reinterpret_cast<PFN_vkGetDisplayModePropertiesKHR>(
GetInstanceProcAddr(instance, "vkGetDisplayModePropertiesKHR"));
CreateDisplayModeKHR =
reinterpret_cast<PFN_vkCreateDisplayModeKHR>(GetInstanceProcAddr(instance, "vkCreateDisplayModeKHR"));
GetDisplayPlaneCapabilitiesKHR = reinterpret_cast<PFN_vkGetDisplayPlaneCapabilitiesKHR>(
GetInstanceProcAddr(instance, "vkGetDisplayPlaneCapabilitiesKHR"));
CreateDisplayPlaneSurfaceKHR = reinterpret_cast<PFN_vkCreateDisplayPlaneSurfaceKHR>(
GetInstanceProcAddr(instance, "vkCreateDisplayPlaneSurfaceKHR"));
#ifdef VK_USE_PLATFORM_XLIB_KHR #ifdef VK_USE_PLATFORM_XLIB_KHR
CreateXlibSurfaceKHR = reinterpret_cast<PFN_vkCreateXlibSurfaceKHR>(GetInstanceProcAddr(instance, "vkCreateXlibSurfaceKHR")); CreateXlibSurfaceKHR =
reinterpret_cast<PFN_vkCreateXlibSurfaceKHR>(GetInstanceProcAddr(instance, "vkCreateXlibSurfaceKHR"));
#endif #endif
#ifdef VK_USE_PLATFORM_XLIB_KHR #ifdef VK_USE_PLATFORM_XLIB_KHR
GetPhysicalDeviceXlibPresentationSupportKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR>(GetInstanceProcAddr(instance, "vkGetPhysicalDeviceXlibPresentationSupportKHR")); GetPhysicalDeviceXlibPresentationSupportKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR>(
GetInstanceProcAddr(instance, "vkGetPhysicalDeviceXlibPresentationSupportKHR"));
#endif #endif
#ifdef VK_USE_PLATFORM_XCB_KHR #ifdef VK_USE_PLATFORM_XCB_KHR
CreateXcbSurfaceKHR = reinterpret_cast<PFN_vkCreateXcbSurfaceKHR>(GetInstanceProcAddr(instance, "vkCreateXcbSurfaceKHR")); CreateXcbSurfaceKHR =
reinterpret_cast<PFN_vkCreateXcbSurfaceKHR>(GetInstanceProcAddr(instance, "vkCreateXcbSurfaceKHR"));
#endif #endif
#ifdef VK_USE_PLATFORM_XCB_KHR #ifdef VK_USE_PLATFORM_XCB_KHR
GetPhysicalDeviceXcbPresentationSupportKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR>(GetInstanceProcAddr(instance, "vkGetPhysicalDeviceXcbPresentationSupportKHR")); GetPhysicalDeviceXcbPresentationSupportKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR>(
GetInstanceProcAddr(instance, "vkGetPhysicalDeviceXcbPresentationSupportKHR"));
#endif #endif
#ifdef VK_USE_PLATFORM_WAYLAND_KHR #ifdef VK_USE_PLATFORM_WAYLAND_KHR
CreateWaylandSurfaceKHR = reinterpret_cast<PFN_vkCreateWaylandSurfaceKHR>(GetInstanceProcAddr(instance, "vkCreateWaylandSurfaceKHR")); CreateWaylandSurfaceKHR =
reinterpret_cast<PFN_vkCreateWaylandSurfaceKHR>(GetInstanceProcAddr(instance, "vkCreateWaylandSurfaceKHR"));
#endif #endif
#ifdef VK_USE_PLATFORM_WAYLAND_KHR #ifdef VK_USE_PLATFORM_WAYLAND_KHR
GetPhysicalDeviceWaylandPresentationSupportKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR>(GetInstanceProcAddr(instance, "vkGetPhysicalDeviceWaylandPresentationSupportKHR")); GetPhysicalDeviceWaylandPresentationSupportKHR =
reinterpret_cast<PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR>(
GetInstanceProcAddr(instance, "vkGetPhysicalDeviceWaylandPresentationSupportKHR"));
#endif #endif
#ifdef VK_USE_PLATFORM_MIR_KHR #ifdef VK_USE_PLATFORM_MIR_KHR
CreateMirSurfaceKHR = reinterpret_cast<PFN_vkCreateMirSurfaceKHR>(GetInstanceProcAddr(instance, "vkCreateMirSurfaceKHR")); CreateMirSurfaceKHR =
reinterpret_cast<PFN_vkCreateMirSurfaceKHR>(GetInstanceProcAddr(instance, "vkCreateMirSurfaceKHR"));
#endif #endif
#ifdef VK_USE_PLATFORM_MIR_KHR #ifdef VK_USE_PLATFORM_MIR_KHR
GetPhysicalDeviceMirPresentationSupportKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceMirPresentationSupportKHR>(GetInstanceProcAddr(instance, "vkGetPhysicalDeviceMirPresentationSupportKHR")); GetPhysicalDeviceMirPresentationSupportKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceMirPresentationSupportKHR>(
GetInstanceProcAddr(instance, "vkGetPhysicalDeviceMirPresentationSupportKHR"));
#endif #endif
#ifdef VK_USE_PLATFORM_ANDROID_KHR #ifdef VK_USE_PLATFORM_ANDROID_KHR
CreateAndroidSurfaceKHR = reinterpret_cast<PFN_vkCreateAndroidSurfaceKHR>(GetInstanceProcAddr(instance, "vkCreateAndroidSurfaceKHR")); CreateAndroidSurfaceKHR =
reinterpret_cast<PFN_vkCreateAndroidSurfaceKHR>(GetInstanceProcAddr(instance, "vkCreateAndroidSurfaceKHR"));
#endif #endif
#ifdef VK_USE_PLATFORM_WIN32_KHR #ifdef VK_USE_PLATFORM_WIN32_KHR
CreateWin32SurfaceKHR = reinterpret_cast<PFN_vkCreateWin32SurfaceKHR>(GetInstanceProcAddr(instance, "vkCreateWin32SurfaceKHR")); CreateWin32SurfaceKHR =
reinterpret_cast<PFN_vkCreateWin32SurfaceKHR>(GetInstanceProcAddr(instance, "vkCreateWin32SurfaceKHR"));
#endif #endif
#ifdef VK_USE_PLATFORM_WIN32_KHR #ifdef VK_USE_PLATFORM_WIN32_KHR
GetPhysicalDeviceWin32PresentationSupportKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR>(GetInstanceProcAddr(instance, "vkGetPhysicalDeviceWin32PresentationSupportKHR")); GetPhysicalDeviceWin32PresentationSupportKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR>(
GetInstanceProcAddr(instance, "vkGetPhysicalDeviceWin32PresentationSupportKHR"));
#endif #endif
CreateDebugReportCallbackEXT = reinterpret_cast<PFN_vkCreateDebugReportCallbackEXT>(GetInstanceProcAddr(instance, "vkCreateDebugReportCallbackEXT")); CreateDebugReportCallbackEXT = reinterpret_cast<PFN_vkCreateDebugReportCallbackEXT>(
DestroyDebugReportCallbackEXT = reinterpret_cast<PFN_vkDestroyDebugReportCallbackEXT>(GetInstanceProcAddr(instance, "vkDestroyDebugReportCallbackEXT")); GetInstanceProcAddr(instance, "vkCreateDebugReportCallbackEXT"));
DebugReportMessageEXT = reinterpret_cast<PFN_vkDebugReportMessageEXT>(GetInstanceProcAddr(instance, "vkDebugReportMessageEXT")); DestroyDebugReportCallbackEXT = reinterpret_cast<PFN_vkDestroyDebugReportCallbackEXT>(
GetInstanceProcAddr(instance, "vkDestroyDebugReportCallbackEXT"));
DebugReportMessageEXT =
reinterpret_cast<PFN_vkDebugReportMessageEXT>(GetInstanceProcAddr(instance, "vkDebugReportMessageEXT"));
if (!include_bottom) if (!include_bottom)
return; return;
GetDeviceProcAddr = reinterpret_cast<PFN_vkGetDeviceProcAddr>(GetInstanceProcAddr(instance, "vkGetDeviceProcAddr")); GetDeviceProcAddr = reinterpret_cast<PFN_vkGetDeviceProcAddr>(GetInstanceProcAddr(instance, "vkGetDeviceProcAddr"));
DestroyDevice = reinterpret_cast<PFN_vkDestroyDevice>(GetInstanceProcAddr(instance, "vkDestroyDevice")); DestroyDevice = reinterpret_cast<PFN_vkDestroyDevice>(GetInstanceProcAddr(instance, "vkDestroyDevice"));
GetDeviceQueue = reinterpret_cast<PFN_vkGetDeviceQueue>(GetInstanceProcAddr(instance, "vkGetDeviceQueue")); GetDeviceQueue = reinterpret_cast<PFN_vkGetDeviceQueue>(GetInstanceProcAddr(instance, "vkGetDeviceQueue"));
QueueSubmit = reinterpret_cast<PFN_vkQueueSubmit>(GetInstanceProcAddr(instance, "vkQueueSubmit")); QueueSubmit = reinterpret_cast<PFN_vkQueueSubmit>(GetInstanceProcAddr(instance, "vkQueueSubmit"));
QueueWaitIdle = reinterpret_cast<PFN_vkQueueWaitIdle>(GetInstanceProcAddr(instance, "vkQueueWaitIdle")); QueueWaitIdle = reinterpret_cast<PFN_vkQueueWaitIdle>(GetInstanceProcAddr(instance, "vkQueueWaitIdle"));
DeviceWaitIdle = reinterpret_cast<PFN_vkDeviceWaitIdle>(GetInstanceProcAddr(instance, "vkDeviceWaitIdle")); DeviceWaitIdle = reinterpret_cast<PFN_vkDeviceWaitIdle>(GetInstanceProcAddr(instance, "vkDeviceWaitIdle"));
AllocateMemory = reinterpret_cast<PFN_vkAllocateMemory>(GetInstanceProcAddr(instance, "vkAllocateMemory")); AllocateMemory = reinterpret_cast<PFN_vkAllocateMemory>(GetInstanceProcAddr(instance, "vkAllocateMemory"));
FreeMemory = reinterpret_cast<PFN_vkFreeMemory>(GetInstanceProcAddr(instance, "vkFreeMemory")); FreeMemory = reinterpret_cast<PFN_vkFreeMemory>(GetInstanceProcAddr(instance, "vkFreeMemory"));
MapMemory = reinterpret_cast<PFN_vkMapMemory>(GetInstanceProcAddr(instance, "vkMapMemory")); MapMemory = reinterpret_cast<PFN_vkMapMemory>(GetInstanceProcAddr(instance, "vkMapMemory"));
UnmapMemory = reinterpret_cast<PFN_vkUnmapMemory>(GetInstanceProcAddr(instance, "vkUnmapMemory")); UnmapMemory = reinterpret_cast<PFN_vkUnmapMemory>(GetInstanceProcAddr(instance, "vkUnmapMemory"));
FlushMappedMemoryRanges = reinterpret_cast<PFN_vkFlushMappedMemoryRanges>(GetInstanceProcAddr(instance, "vkFlushMappedMemoryRanges")); FlushMappedMemoryRanges =
InvalidateMappedMemoryRanges = reinterpret_cast<PFN_vkInvalidateMappedMemoryRanges>(GetInstanceProcAddr(instance, "vkInvalidateMappedMemoryRanges")); reinterpret_cast<PFN_vkFlushMappedMemoryRanges>(GetInstanceProcAddr(instance, "vkFlushMappedMemoryRanges"));
GetDeviceMemoryCommitment = reinterpret_cast<PFN_vkGetDeviceMemoryCommitment>(GetInstanceProcAddr(instance, "vkGetDeviceMemoryCommitment")); InvalidateMappedMemoryRanges = reinterpret_cast<PFN_vkInvalidateMappedMemoryRanges>(
BindBufferMemory = reinterpret_cast<PFN_vkBindBufferMemory>(GetInstanceProcAddr(instance, "vkBindBufferMemory")); GetInstanceProcAddr(instance, "vkInvalidateMappedMemoryRanges"));
BindImageMemory = reinterpret_cast<PFN_vkBindImageMemory>(GetInstanceProcAddr(instance, "vkBindImageMemory")); GetDeviceMemoryCommitment =
GetBufferMemoryRequirements = reinterpret_cast<PFN_vkGetBufferMemoryRequirements>(GetInstanceProcAddr(instance, "vkGetBufferMemoryRequirements")); reinterpret_cast<PFN_vkGetDeviceMemoryCommitment>(GetInstanceProcAddr(instance, "vkGetDeviceMemoryCommitment"));
GetImageMemoryRequirements = reinterpret_cast<PFN_vkGetImageMemoryRequirements>(GetInstanceProcAddr(instance, "vkGetImageMemoryRequirements")); BindBufferMemory = reinterpret_cast<PFN_vkBindBufferMemory>(GetInstanceProcAddr(instance, "vkBindBufferMemory"));
GetImageSparseMemoryRequirements = reinterpret_cast<PFN_vkGetImageSparseMemoryRequirements>(GetInstanceProcAddr(instance, "vkGetImageSparseMemoryRequirements")); BindImageMemory = reinterpret_cast<PFN_vkBindImageMemory>(GetInstanceProcAddr(instance, "vkBindImageMemory"));
QueueBindSparse = reinterpret_cast<PFN_vkQueueBindSparse>(GetInstanceProcAddr(instance, "vkQueueBindSparse")); GetBufferMemoryRequirements = reinterpret_cast<PFN_vkGetBufferMemoryRequirements>(
CreateFence = reinterpret_cast<PFN_vkCreateFence>(GetInstanceProcAddr(instance, "vkCreateFence")); GetInstanceProcAddr(instance, "vkGetBufferMemoryRequirements"));
DestroyFence = reinterpret_cast<PFN_vkDestroyFence>(GetInstanceProcAddr(instance, "vkDestroyFence")); GetImageMemoryRequirements =
ResetFences = reinterpret_cast<PFN_vkResetFences>(GetInstanceProcAddr(instance, "vkResetFences")); reinterpret_cast<PFN_vkGetImageMemoryRequirements>(GetInstanceProcAddr(instance, "vkGetImageMemoryRequirements"));
GetFenceStatus = reinterpret_cast<PFN_vkGetFenceStatus>(GetInstanceProcAddr(instance, "vkGetFenceStatus")); GetImageSparseMemoryRequirements = reinterpret_cast<PFN_vkGetImageSparseMemoryRequirements>(
WaitForFences = reinterpret_cast<PFN_vkWaitForFences>(GetInstanceProcAddr(instance, "vkWaitForFences")); GetInstanceProcAddr(instance, "vkGetImageSparseMemoryRequirements"));
CreateSemaphore = reinterpret_cast<PFN_vkCreateSemaphore>(GetInstanceProcAddr(instance, "vkCreateSemaphore")); QueueBindSparse = reinterpret_cast<PFN_vkQueueBindSparse>(GetInstanceProcAddr(instance, "vkQueueBindSparse"));
DestroySemaphore = reinterpret_cast<PFN_vkDestroySemaphore>(GetInstanceProcAddr(instance, "vkDestroySemaphore")); CreateFence = reinterpret_cast<PFN_vkCreateFence>(GetInstanceProcAddr(instance, "vkCreateFence"));
CreateEvent = reinterpret_cast<PFN_vkCreateEvent>(GetInstanceProcAddr(instance, "vkCreateEvent")); DestroyFence = reinterpret_cast<PFN_vkDestroyFence>(GetInstanceProcAddr(instance, "vkDestroyFence"));
DestroyEvent = reinterpret_cast<PFN_vkDestroyEvent>(GetInstanceProcAddr(instance, "vkDestroyEvent")); ResetFences = reinterpret_cast<PFN_vkResetFences>(GetInstanceProcAddr(instance, "vkResetFences"));
GetEventStatus = reinterpret_cast<PFN_vkGetEventStatus>(GetInstanceProcAddr(instance, "vkGetEventStatus")); GetFenceStatus = reinterpret_cast<PFN_vkGetFenceStatus>(GetInstanceProcAddr(instance, "vkGetFenceStatus"));
SetEvent = reinterpret_cast<PFN_vkSetEvent>(GetInstanceProcAddr(instance, "vkSetEvent")); WaitForFences = reinterpret_cast<PFN_vkWaitForFences>(GetInstanceProcAddr(instance, "vkWaitForFences"));
ResetEvent = reinterpret_cast<PFN_vkResetEvent>(GetInstanceProcAddr(instance, "vkResetEvent")); CreateSemaphore = reinterpret_cast<PFN_vkCreateSemaphore>(GetInstanceProcAddr(instance, "vkCreateSemaphore"));
CreateQueryPool = reinterpret_cast<PFN_vkCreateQueryPool>(GetInstanceProcAddr(instance, "vkCreateQueryPool")); DestroySemaphore = reinterpret_cast<PFN_vkDestroySemaphore>(GetInstanceProcAddr(instance, "vkDestroySemaphore"));
DestroyQueryPool = reinterpret_cast<PFN_vkDestroyQueryPool>(GetInstanceProcAddr(instance, "vkDestroyQueryPool")); CreateEvent = reinterpret_cast<PFN_vkCreateEvent>(GetInstanceProcAddr(instance, "vkCreateEvent"));
GetQueryPoolResults = reinterpret_cast<PFN_vkGetQueryPoolResults>(GetInstanceProcAddr(instance, "vkGetQueryPoolResults")); DestroyEvent = reinterpret_cast<PFN_vkDestroyEvent>(GetInstanceProcAddr(instance, "vkDestroyEvent"));
CreateBuffer = reinterpret_cast<PFN_vkCreateBuffer>(GetInstanceProcAddr(instance, "vkCreateBuffer")); GetEventStatus = reinterpret_cast<PFN_vkGetEventStatus>(GetInstanceProcAddr(instance, "vkGetEventStatus"));
DestroyBuffer = reinterpret_cast<PFN_vkDestroyBuffer>(GetInstanceProcAddr(instance, "vkDestroyBuffer")); SetEvent = reinterpret_cast<PFN_vkSetEvent>(GetInstanceProcAddr(instance, "vkSetEvent"));
CreateBufferView = reinterpret_cast<PFN_vkCreateBufferView>(GetInstanceProcAddr(instance, "vkCreateBufferView")); ResetEvent = reinterpret_cast<PFN_vkResetEvent>(GetInstanceProcAddr(instance, "vkResetEvent"));
DestroyBufferView = reinterpret_cast<PFN_vkDestroyBufferView>(GetInstanceProcAddr(instance, "vkDestroyBufferView")); CreateQueryPool = reinterpret_cast<PFN_vkCreateQueryPool>(GetInstanceProcAddr(instance, "vkCreateQueryPool"));
CreateImage = reinterpret_cast<PFN_vkCreateImage>(GetInstanceProcAddr(instance, "vkCreateImage")); DestroyQueryPool = reinterpret_cast<PFN_vkDestroyQueryPool>(GetInstanceProcAddr(instance, "vkDestroyQueryPool"));
DestroyImage = reinterpret_cast<PFN_vkDestroyImage>(GetInstanceProcAddr(instance, "vkDestroyImage")); GetQueryPoolResults =
GetImageSubresourceLayout = reinterpret_cast<PFN_vkGetImageSubresourceLayout>(GetInstanceProcAddr(instance, "vkGetImageSubresourceLayout")); reinterpret_cast<PFN_vkGetQueryPoolResults>(GetInstanceProcAddr(instance, "vkGetQueryPoolResults"));
CreateImageView = reinterpret_cast<PFN_vkCreateImageView>(GetInstanceProcAddr(instance, "vkCreateImageView")); CreateBuffer = reinterpret_cast<PFN_vkCreateBuffer>(GetInstanceProcAddr(instance, "vkCreateBuffer"));
DestroyImageView = reinterpret_cast<PFN_vkDestroyImageView>(GetInstanceProcAddr(instance, "vkDestroyImageView")); DestroyBuffer = reinterpret_cast<PFN_vkDestroyBuffer>(GetInstanceProcAddr(instance, "vkDestroyBuffer"));
CreateShaderModule = reinterpret_cast<PFN_vkCreateShaderModule>(GetInstanceProcAddr(instance, "vkCreateShaderModule")); CreateBufferView = reinterpret_cast<PFN_vkCreateBufferView>(GetInstanceProcAddr(instance, "vkCreateBufferView"));
DestroyShaderModule = reinterpret_cast<PFN_vkDestroyShaderModule>(GetInstanceProcAddr(instance, "vkDestroyShaderModule")); DestroyBufferView = reinterpret_cast<PFN_vkDestroyBufferView>(GetInstanceProcAddr(instance, "vkDestroyBufferView"));
CreatePipelineCache = reinterpret_cast<PFN_vkCreatePipelineCache>(GetInstanceProcAddr(instance, "vkCreatePipelineCache")); CreateImage = reinterpret_cast<PFN_vkCreateImage>(GetInstanceProcAddr(instance, "vkCreateImage"));
DestroyPipelineCache = reinterpret_cast<PFN_vkDestroyPipelineCache>(GetInstanceProcAddr(instance, "vkDestroyPipelineCache")); DestroyImage = reinterpret_cast<PFN_vkDestroyImage>(GetInstanceProcAddr(instance, "vkDestroyImage"));
GetPipelineCacheData = reinterpret_cast<PFN_vkGetPipelineCacheData>(GetInstanceProcAddr(instance, "vkGetPipelineCacheData")); GetImageSubresourceLayout =
MergePipelineCaches = reinterpret_cast<PFN_vkMergePipelineCaches>(GetInstanceProcAddr(instance, "vkMergePipelineCaches")); reinterpret_cast<PFN_vkGetImageSubresourceLayout>(GetInstanceProcAddr(instance, "vkGetImageSubresourceLayout"));
CreateGraphicsPipelines = reinterpret_cast<PFN_vkCreateGraphicsPipelines>(GetInstanceProcAddr(instance, "vkCreateGraphicsPipelines")); CreateImageView = reinterpret_cast<PFN_vkCreateImageView>(GetInstanceProcAddr(instance, "vkCreateImageView"));
CreateComputePipelines = reinterpret_cast<PFN_vkCreateComputePipelines>(GetInstanceProcAddr(instance, "vkCreateComputePipelines")); DestroyImageView = reinterpret_cast<PFN_vkDestroyImageView>(GetInstanceProcAddr(instance, "vkDestroyImageView"));
DestroyPipeline = reinterpret_cast<PFN_vkDestroyPipeline>(GetInstanceProcAddr(instance, "vkDestroyPipeline")); CreateShaderModule =
CreatePipelineLayout = reinterpret_cast<PFN_vkCreatePipelineLayout>(GetInstanceProcAddr(instance, "vkCreatePipelineLayout")); reinterpret_cast<PFN_vkCreateShaderModule>(GetInstanceProcAddr(instance, "vkCreateShaderModule"));
DestroyPipelineLayout = reinterpret_cast<PFN_vkDestroyPipelineLayout>(GetInstanceProcAddr(instance, "vkDestroyPipelineLayout")); DestroyShaderModule =
CreateSampler = reinterpret_cast<PFN_vkCreateSampler>(GetInstanceProcAddr(instance, "vkCreateSampler")); reinterpret_cast<PFN_vkDestroyShaderModule>(GetInstanceProcAddr(instance, "vkDestroyShaderModule"));
DestroySampler = reinterpret_cast<PFN_vkDestroySampler>(GetInstanceProcAddr(instance, "vkDestroySampler")); CreatePipelineCache =
CreateDescriptorSetLayout = reinterpret_cast<PFN_vkCreateDescriptorSetLayout>(GetInstanceProcAddr(instance, "vkCreateDescriptorSetLayout")); reinterpret_cast<PFN_vkCreatePipelineCache>(GetInstanceProcAddr(instance, "vkCreatePipelineCache"));
DestroyDescriptorSetLayout = reinterpret_cast<PFN_vkDestroyDescriptorSetLayout>(GetInstanceProcAddr(instance, "vkDestroyDescriptorSetLayout")); DestroyPipelineCache =
CreateDescriptorPool = reinterpret_cast<PFN_vkCreateDescriptorPool>(GetInstanceProcAddr(instance, "vkCreateDescriptorPool")); reinterpret_cast<PFN_vkDestroyPipelineCache>(GetInstanceProcAddr(instance, "vkDestroyPipelineCache"));
DestroyDescriptorPool = reinterpret_cast<PFN_vkDestroyDescriptorPool>(GetInstanceProcAddr(instance, "vkDestroyDescriptorPool")); GetPipelineCacheData =
ResetDescriptorPool = reinterpret_cast<PFN_vkResetDescriptorPool>(GetInstanceProcAddr(instance, "vkResetDescriptorPool")); reinterpret_cast<PFN_vkGetPipelineCacheData>(GetInstanceProcAddr(instance, "vkGetPipelineCacheData"));
AllocateDescriptorSets = reinterpret_cast<PFN_vkAllocateDescriptorSets>(GetInstanceProcAddr(instance, "vkAllocateDescriptorSets")); MergePipelineCaches =
FreeDescriptorSets = reinterpret_cast<PFN_vkFreeDescriptorSets>(GetInstanceProcAddr(instance, "vkFreeDescriptorSets")); reinterpret_cast<PFN_vkMergePipelineCaches>(GetInstanceProcAddr(instance, "vkMergePipelineCaches"));
UpdateDescriptorSets = reinterpret_cast<PFN_vkUpdateDescriptorSets>(GetInstanceProcAddr(instance, "vkUpdateDescriptorSets")); CreateGraphicsPipelines =
CreateFramebuffer = reinterpret_cast<PFN_vkCreateFramebuffer>(GetInstanceProcAddr(instance, "vkCreateFramebuffer")); reinterpret_cast<PFN_vkCreateGraphicsPipelines>(GetInstanceProcAddr(instance, "vkCreateGraphicsPipelines"));
DestroyFramebuffer = reinterpret_cast<PFN_vkDestroyFramebuffer>(GetInstanceProcAddr(instance, "vkDestroyFramebuffer")); CreateComputePipelines =
CreateRenderPass = reinterpret_cast<PFN_vkCreateRenderPass>(GetInstanceProcAddr(instance, "vkCreateRenderPass")); reinterpret_cast<PFN_vkCreateComputePipelines>(GetInstanceProcAddr(instance, "vkCreateComputePipelines"));
DestroyRenderPass = reinterpret_cast<PFN_vkDestroyRenderPass>(GetInstanceProcAddr(instance, "vkDestroyRenderPass")); DestroyPipeline = reinterpret_cast<PFN_vkDestroyPipeline>(GetInstanceProcAddr(instance, "vkDestroyPipeline"));
GetRenderAreaGranularity = reinterpret_cast<PFN_vkGetRenderAreaGranularity>(GetInstanceProcAddr(instance, "vkGetRenderAreaGranularity")); CreatePipelineLayout =
CreateCommandPool = reinterpret_cast<PFN_vkCreateCommandPool>(GetInstanceProcAddr(instance, "vkCreateCommandPool")); reinterpret_cast<PFN_vkCreatePipelineLayout>(GetInstanceProcAddr(instance, "vkCreatePipelineLayout"));
DestroyCommandPool = reinterpret_cast<PFN_vkDestroyCommandPool>(GetInstanceProcAddr(instance, "vkDestroyCommandPool")); DestroyPipelineLayout =
ResetCommandPool = reinterpret_cast<PFN_vkResetCommandPool>(GetInstanceProcAddr(instance, "vkResetCommandPool")); reinterpret_cast<PFN_vkDestroyPipelineLayout>(GetInstanceProcAddr(instance, "vkDestroyPipelineLayout"));
AllocateCommandBuffers = reinterpret_cast<PFN_vkAllocateCommandBuffers>(GetInstanceProcAddr(instance, "vkAllocateCommandBuffers")); CreateSampler = reinterpret_cast<PFN_vkCreateSampler>(GetInstanceProcAddr(instance, "vkCreateSampler"));
FreeCommandBuffers = reinterpret_cast<PFN_vkFreeCommandBuffers>(GetInstanceProcAddr(instance, "vkFreeCommandBuffers")); DestroySampler = reinterpret_cast<PFN_vkDestroySampler>(GetInstanceProcAddr(instance, "vkDestroySampler"));
BeginCommandBuffer = reinterpret_cast<PFN_vkBeginCommandBuffer>(GetInstanceProcAddr(instance, "vkBeginCommandBuffer")); CreateDescriptorSetLayout =
EndCommandBuffer = reinterpret_cast<PFN_vkEndCommandBuffer>(GetInstanceProcAddr(instance, "vkEndCommandBuffer")); reinterpret_cast<PFN_vkCreateDescriptorSetLayout>(GetInstanceProcAddr(instance, "vkCreateDescriptorSetLayout"));
ResetCommandBuffer = reinterpret_cast<PFN_vkResetCommandBuffer>(GetInstanceProcAddr(instance, "vkResetCommandBuffer")); DestroyDescriptorSetLayout =
CmdBindPipeline = reinterpret_cast<PFN_vkCmdBindPipeline>(GetInstanceProcAddr(instance, "vkCmdBindPipeline")); reinterpret_cast<PFN_vkDestroyDescriptorSetLayout>(GetInstanceProcAddr(instance, "vkDestroyDescriptorSetLayout"));
CmdSetViewport = reinterpret_cast<PFN_vkCmdSetViewport>(GetInstanceProcAddr(instance, "vkCmdSetViewport")); CreateDescriptorPool =
CmdSetScissor = reinterpret_cast<PFN_vkCmdSetScissor>(GetInstanceProcAddr(instance, "vkCmdSetScissor")); reinterpret_cast<PFN_vkCreateDescriptorPool>(GetInstanceProcAddr(instance, "vkCreateDescriptorPool"));
CmdSetLineWidth = reinterpret_cast<PFN_vkCmdSetLineWidth>(GetInstanceProcAddr(instance, "vkCmdSetLineWidth")); DestroyDescriptorPool =
CmdSetDepthBias = reinterpret_cast<PFN_vkCmdSetDepthBias>(GetInstanceProcAddr(instance, "vkCmdSetDepthBias")); reinterpret_cast<PFN_vkDestroyDescriptorPool>(GetInstanceProcAddr(instance, "vkDestroyDescriptorPool"));
CmdSetBlendConstants = reinterpret_cast<PFN_vkCmdSetBlendConstants>(GetInstanceProcAddr(instance, "vkCmdSetBlendConstants")); ResetDescriptorPool =
CmdSetDepthBounds = reinterpret_cast<PFN_vkCmdSetDepthBounds>(GetInstanceProcAddr(instance, "vkCmdSetDepthBounds")); reinterpret_cast<PFN_vkResetDescriptorPool>(GetInstanceProcAddr(instance, "vkResetDescriptorPool"));
CmdSetStencilCompareMask = reinterpret_cast<PFN_vkCmdSetStencilCompareMask>(GetInstanceProcAddr(instance, "vkCmdSetStencilCompareMask")); AllocateDescriptorSets =
CmdSetStencilWriteMask = reinterpret_cast<PFN_vkCmdSetStencilWriteMask>(GetInstanceProcAddr(instance, "vkCmdSetStencilWriteMask")); reinterpret_cast<PFN_vkAllocateDescriptorSets>(GetInstanceProcAddr(instance, "vkAllocateDescriptorSets"));
CmdSetStencilReference = reinterpret_cast<PFN_vkCmdSetStencilReference>(GetInstanceProcAddr(instance, "vkCmdSetStencilReference")); FreeDescriptorSets =
CmdBindDescriptorSets = reinterpret_cast<PFN_vkCmdBindDescriptorSets>(GetInstanceProcAddr(instance, "vkCmdBindDescriptorSets")); reinterpret_cast<PFN_vkFreeDescriptorSets>(GetInstanceProcAddr(instance, "vkFreeDescriptorSets"));
CmdBindIndexBuffer = reinterpret_cast<PFN_vkCmdBindIndexBuffer>(GetInstanceProcAddr(instance, "vkCmdBindIndexBuffer")); UpdateDescriptorSets =
CmdBindVertexBuffers = reinterpret_cast<PFN_vkCmdBindVertexBuffers>(GetInstanceProcAddr(instance, "vkCmdBindVertexBuffers")); reinterpret_cast<PFN_vkUpdateDescriptorSets>(GetInstanceProcAddr(instance, "vkUpdateDescriptorSets"));
CmdDraw = reinterpret_cast<PFN_vkCmdDraw>(GetInstanceProcAddr(instance, "vkCmdDraw")); CreateFramebuffer = reinterpret_cast<PFN_vkCreateFramebuffer>(GetInstanceProcAddr(instance, "vkCreateFramebuffer"));
CmdDrawIndexed = reinterpret_cast<PFN_vkCmdDrawIndexed>(GetInstanceProcAddr(instance, "vkCmdDrawIndexed")); DestroyFramebuffer =
CmdDrawIndirect = reinterpret_cast<PFN_vkCmdDrawIndirect>(GetInstanceProcAddr(instance, "vkCmdDrawIndirect")); reinterpret_cast<PFN_vkDestroyFramebuffer>(GetInstanceProcAddr(instance, "vkDestroyFramebuffer"));
CmdDrawIndexedIndirect = reinterpret_cast<PFN_vkCmdDrawIndexedIndirect>(GetInstanceProcAddr(instance, "vkCmdDrawIndexedIndirect")); CreateRenderPass = reinterpret_cast<PFN_vkCreateRenderPass>(GetInstanceProcAddr(instance, "vkCreateRenderPass"));
CmdDispatch = reinterpret_cast<PFN_vkCmdDispatch>(GetInstanceProcAddr(instance, "vkCmdDispatch")); DestroyRenderPass = reinterpret_cast<PFN_vkDestroyRenderPass>(GetInstanceProcAddr(instance, "vkDestroyRenderPass"));
CmdDispatchIndirect = reinterpret_cast<PFN_vkCmdDispatchIndirect>(GetInstanceProcAddr(instance, "vkCmdDispatchIndirect")); GetRenderAreaGranularity =
CmdCopyBuffer = reinterpret_cast<PFN_vkCmdCopyBuffer>(GetInstanceProcAddr(instance, "vkCmdCopyBuffer")); reinterpret_cast<PFN_vkGetRenderAreaGranularity>(GetInstanceProcAddr(instance, "vkGetRenderAreaGranularity"));
CmdCopyImage = reinterpret_cast<PFN_vkCmdCopyImage>(GetInstanceProcAddr(instance, "vkCmdCopyImage")); CreateCommandPool = reinterpret_cast<PFN_vkCreateCommandPool>(GetInstanceProcAddr(instance, "vkCreateCommandPool"));
CmdBlitImage = reinterpret_cast<PFN_vkCmdBlitImage>(GetInstanceProcAddr(instance, "vkCmdBlitImage")); DestroyCommandPool =
CmdCopyBufferToImage = reinterpret_cast<PFN_vkCmdCopyBufferToImage>(GetInstanceProcAddr(instance, "vkCmdCopyBufferToImage")); reinterpret_cast<PFN_vkDestroyCommandPool>(GetInstanceProcAddr(instance, "vkDestroyCommandPool"));
CmdCopyImageToBuffer = reinterpret_cast<PFN_vkCmdCopyImageToBuffer>(GetInstanceProcAddr(instance, "vkCmdCopyImageToBuffer")); ResetCommandPool = reinterpret_cast<PFN_vkResetCommandPool>(GetInstanceProcAddr(instance, "vkResetCommandPool"));
CmdUpdateBuffer = reinterpret_cast<PFN_vkCmdUpdateBuffer>(GetInstanceProcAddr(instance, "vkCmdUpdateBuffer")); AllocateCommandBuffers =
CmdFillBuffer = reinterpret_cast<PFN_vkCmdFillBuffer>(GetInstanceProcAddr(instance, "vkCmdFillBuffer")); reinterpret_cast<PFN_vkAllocateCommandBuffers>(GetInstanceProcAddr(instance, "vkAllocateCommandBuffers"));
CmdClearColorImage = reinterpret_cast<PFN_vkCmdClearColorImage>(GetInstanceProcAddr(instance, "vkCmdClearColorImage")); FreeCommandBuffers =
CmdClearDepthStencilImage = reinterpret_cast<PFN_vkCmdClearDepthStencilImage>(GetInstanceProcAddr(instance, "vkCmdClearDepthStencilImage")); reinterpret_cast<PFN_vkFreeCommandBuffers>(GetInstanceProcAddr(instance, "vkFreeCommandBuffers"));
CmdClearAttachments = reinterpret_cast<PFN_vkCmdClearAttachments>(GetInstanceProcAddr(instance, "vkCmdClearAttachments")); BeginCommandBuffer =
CmdResolveImage = reinterpret_cast<PFN_vkCmdResolveImage>(GetInstanceProcAddr(instance, "vkCmdResolveImage")); reinterpret_cast<PFN_vkBeginCommandBuffer>(GetInstanceProcAddr(instance, "vkBeginCommandBuffer"));
CmdSetEvent = reinterpret_cast<PFN_vkCmdSetEvent>(GetInstanceProcAddr(instance, "vkCmdSetEvent")); EndCommandBuffer = reinterpret_cast<PFN_vkEndCommandBuffer>(GetInstanceProcAddr(instance, "vkEndCommandBuffer"));
CmdResetEvent = reinterpret_cast<PFN_vkCmdResetEvent>(GetInstanceProcAddr(instance, "vkCmdResetEvent")); ResetCommandBuffer =
CmdWaitEvents = reinterpret_cast<PFN_vkCmdWaitEvents>(GetInstanceProcAddr(instance, "vkCmdWaitEvents")); reinterpret_cast<PFN_vkResetCommandBuffer>(GetInstanceProcAddr(instance, "vkResetCommandBuffer"));
CmdPipelineBarrier = reinterpret_cast<PFN_vkCmdPipelineBarrier>(GetInstanceProcAddr(instance, "vkCmdPipelineBarrier")); CmdBindPipeline = reinterpret_cast<PFN_vkCmdBindPipeline>(GetInstanceProcAddr(instance, "vkCmdBindPipeline"));
CmdBeginQuery = reinterpret_cast<PFN_vkCmdBeginQuery>(GetInstanceProcAddr(instance, "vkCmdBeginQuery")); CmdSetViewport = reinterpret_cast<PFN_vkCmdSetViewport>(GetInstanceProcAddr(instance, "vkCmdSetViewport"));
CmdEndQuery = reinterpret_cast<PFN_vkCmdEndQuery>(GetInstanceProcAddr(instance, "vkCmdEndQuery")); CmdSetScissor = reinterpret_cast<PFN_vkCmdSetScissor>(GetInstanceProcAddr(instance, "vkCmdSetScissor"));
CmdResetQueryPool = reinterpret_cast<PFN_vkCmdResetQueryPool>(GetInstanceProcAddr(instance, "vkCmdResetQueryPool")); CmdSetLineWidth = reinterpret_cast<PFN_vkCmdSetLineWidth>(GetInstanceProcAddr(instance, "vkCmdSetLineWidth"));
CmdWriteTimestamp = reinterpret_cast<PFN_vkCmdWriteTimestamp>(GetInstanceProcAddr(instance, "vkCmdWriteTimestamp")); CmdSetDepthBias = reinterpret_cast<PFN_vkCmdSetDepthBias>(GetInstanceProcAddr(instance, "vkCmdSetDepthBias"));
CmdCopyQueryPoolResults = reinterpret_cast<PFN_vkCmdCopyQueryPoolResults>(GetInstanceProcAddr(instance, "vkCmdCopyQueryPoolResults")); CmdSetBlendConstants =
CmdPushConstants = reinterpret_cast<PFN_vkCmdPushConstants>(GetInstanceProcAddr(instance, "vkCmdPushConstants")); reinterpret_cast<PFN_vkCmdSetBlendConstants>(GetInstanceProcAddr(instance, "vkCmdSetBlendConstants"));
CmdBeginRenderPass = reinterpret_cast<PFN_vkCmdBeginRenderPass>(GetInstanceProcAddr(instance, "vkCmdBeginRenderPass")); CmdSetDepthBounds = reinterpret_cast<PFN_vkCmdSetDepthBounds>(GetInstanceProcAddr(instance, "vkCmdSetDepthBounds"));
CmdNextSubpass = reinterpret_cast<PFN_vkCmdNextSubpass>(GetInstanceProcAddr(instance, "vkCmdNextSubpass")); CmdSetStencilCompareMask =
CmdEndRenderPass = reinterpret_cast<PFN_vkCmdEndRenderPass>(GetInstanceProcAddr(instance, "vkCmdEndRenderPass")); reinterpret_cast<PFN_vkCmdSetStencilCompareMask>(GetInstanceProcAddr(instance, "vkCmdSetStencilCompareMask"));
CmdExecuteCommands = reinterpret_cast<PFN_vkCmdExecuteCommands>(GetInstanceProcAddr(instance, "vkCmdExecuteCommands")); CmdSetStencilWriteMask =
CreateSwapchainKHR = reinterpret_cast<PFN_vkCreateSwapchainKHR>(GetInstanceProcAddr(instance, "vkCreateSwapchainKHR")); reinterpret_cast<PFN_vkCmdSetStencilWriteMask>(GetInstanceProcAddr(instance, "vkCmdSetStencilWriteMask"));
DestroySwapchainKHR = reinterpret_cast<PFN_vkDestroySwapchainKHR>(GetInstanceProcAddr(instance, "vkDestroySwapchainKHR")); CmdSetStencilReference =
GetSwapchainImagesKHR = reinterpret_cast<PFN_vkGetSwapchainImagesKHR>(GetInstanceProcAddr(instance, "vkGetSwapchainImagesKHR")); reinterpret_cast<PFN_vkCmdSetStencilReference>(GetInstanceProcAddr(instance, "vkCmdSetStencilReference"));
AcquireNextImageKHR = reinterpret_cast<PFN_vkAcquireNextImageKHR>(GetInstanceProcAddr(instance, "vkAcquireNextImageKHR")); CmdBindDescriptorSets =
QueuePresentKHR = reinterpret_cast<PFN_vkQueuePresentKHR>(GetInstanceProcAddr(instance, "vkQueuePresentKHR")); reinterpret_cast<PFN_vkCmdBindDescriptorSets>(GetInstanceProcAddr(instance, "vkCmdBindDescriptorSets"));
CreateSharedSwapchainsKHR = reinterpret_cast<PFN_vkCreateSharedSwapchainsKHR>(GetInstanceProcAddr(instance, "vkCreateSharedSwapchainsKHR")); CmdBindIndexBuffer =
reinterpret_cast<PFN_vkCmdBindIndexBuffer>(GetInstanceProcAddr(instance, "vkCmdBindIndexBuffer"));
CmdBindVertexBuffers =
reinterpret_cast<PFN_vkCmdBindVertexBuffers>(GetInstanceProcAddr(instance, "vkCmdBindVertexBuffers"));
CmdDraw = reinterpret_cast<PFN_vkCmdDraw>(GetInstanceProcAddr(instance, "vkCmdDraw"));
CmdDrawIndexed = reinterpret_cast<PFN_vkCmdDrawIndexed>(GetInstanceProcAddr(instance, "vkCmdDrawIndexed"));
CmdDrawIndirect = reinterpret_cast<PFN_vkCmdDrawIndirect>(GetInstanceProcAddr(instance, "vkCmdDrawIndirect"));
CmdDrawIndexedIndirect =
reinterpret_cast<PFN_vkCmdDrawIndexedIndirect>(GetInstanceProcAddr(instance, "vkCmdDrawIndexedIndirect"));
CmdDispatch = reinterpret_cast<PFN_vkCmdDispatch>(GetInstanceProcAddr(instance, "vkCmdDispatch"));
CmdDispatchIndirect =
reinterpret_cast<PFN_vkCmdDispatchIndirect>(GetInstanceProcAddr(instance, "vkCmdDispatchIndirect"));
CmdCopyBuffer = reinterpret_cast<PFN_vkCmdCopyBuffer>(GetInstanceProcAddr(instance, "vkCmdCopyBuffer"));
CmdCopyImage = reinterpret_cast<PFN_vkCmdCopyImage>(GetInstanceProcAddr(instance, "vkCmdCopyImage"));
CmdBlitImage = reinterpret_cast<PFN_vkCmdBlitImage>(GetInstanceProcAddr(instance, "vkCmdBlitImage"));
CmdCopyBufferToImage =
reinterpret_cast<PFN_vkCmdCopyBufferToImage>(GetInstanceProcAddr(instance, "vkCmdCopyBufferToImage"));
CmdCopyImageToBuffer =
reinterpret_cast<PFN_vkCmdCopyImageToBuffer>(GetInstanceProcAddr(instance, "vkCmdCopyImageToBuffer"));
CmdUpdateBuffer = reinterpret_cast<PFN_vkCmdUpdateBuffer>(GetInstanceProcAddr(instance, "vkCmdUpdateBuffer"));
CmdFillBuffer = reinterpret_cast<PFN_vkCmdFillBuffer>(GetInstanceProcAddr(instance, "vkCmdFillBuffer"));
CmdClearColorImage =
reinterpret_cast<PFN_vkCmdClearColorImage>(GetInstanceProcAddr(instance, "vkCmdClearColorImage"));
CmdClearDepthStencilImage =
reinterpret_cast<PFN_vkCmdClearDepthStencilImage>(GetInstanceProcAddr(instance, "vkCmdClearDepthStencilImage"));
CmdClearAttachments =
reinterpret_cast<PFN_vkCmdClearAttachments>(GetInstanceProcAddr(instance, "vkCmdClearAttachments"));
CmdResolveImage = reinterpret_cast<PFN_vkCmdResolveImage>(GetInstanceProcAddr(instance, "vkCmdResolveImage"));
CmdSetEvent = reinterpret_cast<PFN_vkCmdSetEvent>(GetInstanceProcAddr(instance, "vkCmdSetEvent"));
CmdResetEvent = reinterpret_cast<PFN_vkCmdResetEvent>(GetInstanceProcAddr(instance, "vkCmdResetEvent"));
CmdWaitEvents = reinterpret_cast<PFN_vkCmdWaitEvents>(GetInstanceProcAddr(instance, "vkCmdWaitEvents"));
CmdPipelineBarrier =
reinterpret_cast<PFN_vkCmdPipelineBarrier>(GetInstanceProcAddr(instance, "vkCmdPipelineBarrier"));
CmdBeginQuery = reinterpret_cast<PFN_vkCmdBeginQuery>(GetInstanceProcAddr(instance, "vkCmdBeginQuery"));
CmdEndQuery = reinterpret_cast<PFN_vkCmdEndQuery>(GetInstanceProcAddr(instance, "vkCmdEndQuery"));
CmdResetQueryPool = reinterpret_cast<PFN_vkCmdResetQueryPool>(GetInstanceProcAddr(instance, "vkCmdResetQueryPool"));
CmdWriteTimestamp = reinterpret_cast<PFN_vkCmdWriteTimestamp>(GetInstanceProcAddr(instance, "vkCmdWriteTimestamp"));
CmdCopyQueryPoolResults =
reinterpret_cast<PFN_vkCmdCopyQueryPoolResults>(GetInstanceProcAddr(instance, "vkCmdCopyQueryPoolResults"));
CmdPushConstants = reinterpret_cast<PFN_vkCmdPushConstants>(GetInstanceProcAddr(instance, "vkCmdPushConstants"));
CmdBeginRenderPass =
reinterpret_cast<PFN_vkCmdBeginRenderPass>(GetInstanceProcAddr(instance, "vkCmdBeginRenderPass"));
CmdNextSubpass = reinterpret_cast<PFN_vkCmdNextSubpass>(GetInstanceProcAddr(instance, "vkCmdNextSubpass"));
CmdEndRenderPass = reinterpret_cast<PFN_vkCmdEndRenderPass>(GetInstanceProcAddr(instance, "vkCmdEndRenderPass"));
CmdExecuteCommands =
reinterpret_cast<PFN_vkCmdExecuteCommands>(GetInstanceProcAddr(instance, "vkCmdExecuteCommands"));
CreateSwapchainKHR =
reinterpret_cast<PFN_vkCreateSwapchainKHR>(GetInstanceProcAddr(instance, "vkCreateSwapchainKHR"));
DestroySwapchainKHR =
reinterpret_cast<PFN_vkDestroySwapchainKHR>(GetInstanceProcAddr(instance, "vkDestroySwapchainKHR"));
GetSwapchainImagesKHR =
reinterpret_cast<PFN_vkGetSwapchainImagesKHR>(GetInstanceProcAddr(instance, "vkGetSwapchainImagesKHR"));
AcquireNextImageKHR =
reinterpret_cast<PFN_vkAcquireNextImageKHR>(GetInstanceProcAddr(instance, "vkAcquireNextImageKHR"));
QueuePresentKHR = reinterpret_cast<PFN_vkQueuePresentKHR>(GetInstanceProcAddr(instance, "vkQueuePresentKHR"));
CreateSharedSwapchainsKHR =
reinterpret_cast<PFN_vkCreateSharedSwapchainsKHR>(GetInstanceProcAddr(instance, "vkCreateSharedSwapchainsKHR"));
} }
void init_dispatch_table_bottom(VkInstance instance, VkDevice dev) void init_dispatch_table_bottom(VkInstance instance, VkDevice dev) {
{ GetDeviceProcAddr = reinterpret_cast<PFN_vkGetDeviceProcAddr>(GetInstanceProcAddr(instance, "vkGetDeviceProcAddr"));
GetDeviceProcAddr = reinterpret_cast<PFN_vkGetDeviceProcAddr>(GetInstanceProcAddr(instance, "vkGetDeviceProcAddr")); GetDeviceProcAddr = reinterpret_cast<PFN_vkGetDeviceProcAddr>(GetDeviceProcAddr(dev, "vkGetDeviceProcAddr"));
GetDeviceProcAddr = reinterpret_cast<PFN_vkGetDeviceProcAddr>(GetDeviceProcAddr(dev, "vkGetDeviceProcAddr"));
DestroyDevice = reinterpret_cast<PFN_vkDestroyDevice>(GetDeviceProcAddr(dev, "vkDestroyDevice")); DestroyDevice = reinterpret_cast<PFN_vkDestroyDevice>(GetDeviceProcAddr(dev, "vkDestroyDevice"));
GetDeviceQueue = reinterpret_cast<PFN_vkGetDeviceQueue>(GetDeviceProcAddr(dev, "vkGetDeviceQueue")); GetDeviceQueue = reinterpret_cast<PFN_vkGetDeviceQueue>(GetDeviceProcAddr(dev, "vkGetDeviceQueue"));
QueueSubmit = reinterpret_cast<PFN_vkQueueSubmit>(GetDeviceProcAddr(dev, "vkQueueSubmit")); QueueSubmit = reinterpret_cast<PFN_vkQueueSubmit>(GetDeviceProcAddr(dev, "vkQueueSubmit"));
QueueWaitIdle = reinterpret_cast<PFN_vkQueueWaitIdle>(GetDeviceProcAddr(dev, "vkQueueWaitIdle")); QueueWaitIdle = reinterpret_cast<PFN_vkQueueWaitIdle>(GetDeviceProcAddr(dev, "vkQueueWaitIdle"));
DeviceWaitIdle = reinterpret_cast<PFN_vkDeviceWaitIdle>(GetDeviceProcAddr(dev, "vkDeviceWaitIdle")); DeviceWaitIdle = reinterpret_cast<PFN_vkDeviceWaitIdle>(GetDeviceProcAddr(dev, "vkDeviceWaitIdle"));
AllocateMemory = reinterpret_cast<PFN_vkAllocateMemory>(GetDeviceProcAddr(dev, "vkAllocateMemory")); AllocateMemory = reinterpret_cast<PFN_vkAllocateMemory>(GetDeviceProcAddr(dev, "vkAllocateMemory"));
FreeMemory = reinterpret_cast<PFN_vkFreeMemory>(GetDeviceProcAddr(dev, "vkFreeMemory")); FreeMemory = reinterpret_cast<PFN_vkFreeMemory>(GetDeviceProcAddr(dev, "vkFreeMemory"));
MapMemory = reinterpret_cast<PFN_vkMapMemory>(GetDeviceProcAddr(dev, "vkMapMemory")); MapMemory = reinterpret_cast<PFN_vkMapMemory>(GetDeviceProcAddr(dev, "vkMapMemory"));
UnmapMemory = reinterpret_cast<PFN_vkUnmapMemory>(GetDeviceProcAddr(dev, "vkUnmapMemory")); UnmapMemory = reinterpret_cast<PFN_vkUnmapMemory>(GetDeviceProcAddr(dev, "vkUnmapMemory"));
FlushMappedMemoryRanges = reinterpret_cast<PFN_vkFlushMappedMemoryRanges>(GetDeviceProcAddr(dev, "vkFlushMappedMemoryRanges")); FlushMappedMemoryRanges =
InvalidateMappedMemoryRanges = reinterpret_cast<PFN_vkInvalidateMappedMemoryRanges>(GetDeviceProcAddr(dev, "vkInvalidateMappedMemoryRanges")); reinterpret_cast<PFN_vkFlushMappedMemoryRanges>(GetDeviceProcAddr(dev, "vkFlushMappedMemoryRanges"));
GetDeviceMemoryCommitment = reinterpret_cast<PFN_vkGetDeviceMemoryCommitment>(GetDeviceProcAddr(dev, "vkGetDeviceMemoryCommitment")); InvalidateMappedMemoryRanges =
BindBufferMemory = reinterpret_cast<PFN_vkBindBufferMemory>(GetDeviceProcAddr(dev, "vkBindBufferMemory")); reinterpret_cast<PFN_vkInvalidateMappedMemoryRanges>(GetDeviceProcAddr(dev, "vkInvalidateMappedMemoryRanges"));
BindImageMemory = reinterpret_cast<PFN_vkBindImageMemory>(GetDeviceProcAddr(dev, "vkBindImageMemory")); GetDeviceMemoryCommitment =
GetBufferMemoryRequirements = reinterpret_cast<PFN_vkGetBufferMemoryRequirements>(GetDeviceProcAddr(dev, "vkGetBufferMemoryRequirements")); reinterpret_cast<PFN_vkGetDeviceMemoryCommitment>(GetDeviceProcAddr(dev, "vkGetDeviceMemoryCommitment"));
GetImageMemoryRequirements = reinterpret_cast<PFN_vkGetImageMemoryRequirements>(GetDeviceProcAddr(dev, "vkGetImageMemoryRequirements")); BindBufferMemory = reinterpret_cast<PFN_vkBindBufferMemory>(GetDeviceProcAddr(dev, "vkBindBufferMemory"));
GetImageSparseMemoryRequirements = reinterpret_cast<PFN_vkGetImageSparseMemoryRequirements>(GetDeviceProcAddr(dev, "vkGetImageSparseMemoryRequirements")); BindImageMemory = reinterpret_cast<PFN_vkBindImageMemory>(GetDeviceProcAddr(dev, "vkBindImageMemory"));
QueueBindSparse = reinterpret_cast<PFN_vkQueueBindSparse>(GetDeviceProcAddr(dev, "vkQueueBindSparse")); GetBufferMemoryRequirements =
CreateFence = reinterpret_cast<PFN_vkCreateFence>(GetDeviceProcAddr(dev, "vkCreateFence")); reinterpret_cast<PFN_vkGetBufferMemoryRequirements>(GetDeviceProcAddr(dev, "vkGetBufferMemoryRequirements"));
DestroyFence = reinterpret_cast<PFN_vkDestroyFence>(GetDeviceProcAddr(dev, "vkDestroyFence")); GetImageMemoryRequirements =
ResetFences = reinterpret_cast<PFN_vkResetFences>(GetDeviceProcAddr(dev, "vkResetFences")); reinterpret_cast<PFN_vkGetImageMemoryRequirements>(GetDeviceProcAddr(dev, "vkGetImageMemoryRequirements"));
GetFenceStatus = reinterpret_cast<PFN_vkGetFenceStatus>(GetDeviceProcAddr(dev, "vkGetFenceStatus")); GetImageSparseMemoryRequirements = reinterpret_cast<PFN_vkGetImageSparseMemoryRequirements>(
WaitForFences = reinterpret_cast<PFN_vkWaitForFences>(GetDeviceProcAddr(dev, "vkWaitForFences")); GetDeviceProcAddr(dev, "vkGetImageSparseMemoryRequirements"));
CreateSemaphore = reinterpret_cast<PFN_vkCreateSemaphore>(GetDeviceProcAddr(dev, "vkCreateSemaphore")); QueueBindSparse = reinterpret_cast<PFN_vkQueueBindSparse>(GetDeviceProcAddr(dev, "vkQueueBindSparse"));
DestroySemaphore = reinterpret_cast<PFN_vkDestroySemaphore>(GetDeviceProcAddr(dev, "vkDestroySemaphore")); CreateFence = reinterpret_cast<PFN_vkCreateFence>(GetDeviceProcAddr(dev, "vkCreateFence"));
CreateEvent = reinterpret_cast<PFN_vkCreateEvent>(GetDeviceProcAddr(dev, "vkCreateEvent")); DestroyFence = reinterpret_cast<PFN_vkDestroyFence>(GetDeviceProcAddr(dev, "vkDestroyFence"));
DestroyEvent = reinterpret_cast<PFN_vkDestroyEvent>(GetDeviceProcAddr(dev, "vkDestroyEvent")); ResetFences = reinterpret_cast<PFN_vkResetFences>(GetDeviceProcAddr(dev, "vkResetFences"));
GetEventStatus = reinterpret_cast<PFN_vkGetEventStatus>(GetDeviceProcAddr(dev, "vkGetEventStatus")); GetFenceStatus = reinterpret_cast<PFN_vkGetFenceStatus>(GetDeviceProcAddr(dev, "vkGetFenceStatus"));
SetEvent = reinterpret_cast<PFN_vkSetEvent>(GetDeviceProcAddr(dev, "vkSetEvent")); WaitForFences = reinterpret_cast<PFN_vkWaitForFences>(GetDeviceProcAddr(dev, "vkWaitForFences"));
ResetEvent = reinterpret_cast<PFN_vkResetEvent>(GetDeviceProcAddr(dev, "vkResetEvent")); CreateSemaphore = reinterpret_cast<PFN_vkCreateSemaphore>(GetDeviceProcAddr(dev, "vkCreateSemaphore"));
CreateQueryPool = reinterpret_cast<PFN_vkCreateQueryPool>(GetDeviceProcAddr(dev, "vkCreateQueryPool")); DestroySemaphore = reinterpret_cast<PFN_vkDestroySemaphore>(GetDeviceProcAddr(dev, "vkDestroySemaphore"));
DestroyQueryPool = reinterpret_cast<PFN_vkDestroyQueryPool>(GetDeviceProcAddr(dev, "vkDestroyQueryPool")); CreateEvent = reinterpret_cast<PFN_vkCreateEvent>(GetDeviceProcAddr(dev, "vkCreateEvent"));
GetQueryPoolResults = reinterpret_cast<PFN_vkGetQueryPoolResults>(GetDeviceProcAddr(dev, "vkGetQueryPoolResults")); DestroyEvent = reinterpret_cast<PFN_vkDestroyEvent>(GetDeviceProcAddr(dev, "vkDestroyEvent"));
CreateBuffer = reinterpret_cast<PFN_vkCreateBuffer>(GetDeviceProcAddr(dev, "vkCreateBuffer")); GetEventStatus = reinterpret_cast<PFN_vkGetEventStatus>(GetDeviceProcAddr(dev, "vkGetEventStatus"));
DestroyBuffer = reinterpret_cast<PFN_vkDestroyBuffer>(GetDeviceProcAddr(dev, "vkDestroyBuffer")); SetEvent = reinterpret_cast<PFN_vkSetEvent>(GetDeviceProcAddr(dev, "vkSetEvent"));
CreateBufferView = reinterpret_cast<PFN_vkCreateBufferView>(GetDeviceProcAddr(dev, "vkCreateBufferView")); ResetEvent = reinterpret_cast<PFN_vkResetEvent>(GetDeviceProcAddr(dev, "vkResetEvent"));
DestroyBufferView = reinterpret_cast<PFN_vkDestroyBufferView>(GetDeviceProcAddr(dev, "vkDestroyBufferView")); CreateQueryPool = reinterpret_cast<PFN_vkCreateQueryPool>(GetDeviceProcAddr(dev, "vkCreateQueryPool"));
CreateImage = reinterpret_cast<PFN_vkCreateImage>(GetDeviceProcAddr(dev, "vkCreateImage")); DestroyQueryPool = reinterpret_cast<PFN_vkDestroyQueryPool>(GetDeviceProcAddr(dev, "vkDestroyQueryPool"));
DestroyImage = reinterpret_cast<PFN_vkDestroyImage>(GetDeviceProcAddr(dev, "vkDestroyImage")); GetQueryPoolResults = reinterpret_cast<PFN_vkGetQueryPoolResults>(GetDeviceProcAddr(dev, "vkGetQueryPoolResults"));
GetImageSubresourceLayout = reinterpret_cast<PFN_vkGetImageSubresourceLayout>(GetDeviceProcAddr(dev, "vkGetImageSubresourceLayout")); CreateBuffer = reinterpret_cast<PFN_vkCreateBuffer>(GetDeviceProcAddr(dev, "vkCreateBuffer"));
CreateImageView = reinterpret_cast<PFN_vkCreateImageView>(GetDeviceProcAddr(dev, "vkCreateImageView")); DestroyBuffer = reinterpret_cast<PFN_vkDestroyBuffer>(GetDeviceProcAddr(dev, "vkDestroyBuffer"));
DestroyImageView = reinterpret_cast<PFN_vkDestroyImageView>(GetDeviceProcAddr(dev, "vkDestroyImageView")); CreateBufferView = reinterpret_cast<PFN_vkCreateBufferView>(GetDeviceProcAddr(dev, "vkCreateBufferView"));
CreateShaderModule = reinterpret_cast<PFN_vkCreateShaderModule>(GetDeviceProcAddr(dev, "vkCreateShaderModule")); DestroyBufferView = reinterpret_cast<PFN_vkDestroyBufferView>(GetDeviceProcAddr(dev, "vkDestroyBufferView"));
DestroyShaderModule = reinterpret_cast<PFN_vkDestroyShaderModule>(GetDeviceProcAddr(dev, "vkDestroyShaderModule")); CreateImage = reinterpret_cast<PFN_vkCreateImage>(GetDeviceProcAddr(dev, "vkCreateImage"));
CreatePipelineCache = reinterpret_cast<PFN_vkCreatePipelineCache>(GetDeviceProcAddr(dev, "vkCreatePipelineCache")); DestroyImage = reinterpret_cast<PFN_vkDestroyImage>(GetDeviceProcAddr(dev, "vkDestroyImage"));
DestroyPipelineCache = reinterpret_cast<PFN_vkDestroyPipelineCache>(GetDeviceProcAddr(dev, "vkDestroyPipelineCache")); GetImageSubresourceLayout =
GetPipelineCacheData = reinterpret_cast<PFN_vkGetPipelineCacheData>(GetDeviceProcAddr(dev, "vkGetPipelineCacheData")); reinterpret_cast<PFN_vkGetImageSubresourceLayout>(GetDeviceProcAddr(dev, "vkGetImageSubresourceLayout"));
MergePipelineCaches = reinterpret_cast<PFN_vkMergePipelineCaches>(GetDeviceProcAddr(dev, "vkMergePipelineCaches")); CreateImageView = reinterpret_cast<PFN_vkCreateImageView>(GetDeviceProcAddr(dev, "vkCreateImageView"));
CreateGraphicsPipelines = reinterpret_cast<PFN_vkCreateGraphicsPipelines>(GetDeviceProcAddr(dev, "vkCreateGraphicsPipelines")); DestroyImageView = reinterpret_cast<PFN_vkDestroyImageView>(GetDeviceProcAddr(dev, "vkDestroyImageView"));
CreateComputePipelines = reinterpret_cast<PFN_vkCreateComputePipelines>(GetDeviceProcAddr(dev, "vkCreateComputePipelines")); CreateShaderModule = reinterpret_cast<PFN_vkCreateShaderModule>(GetDeviceProcAddr(dev, "vkCreateShaderModule"));
DestroyPipeline = reinterpret_cast<PFN_vkDestroyPipeline>(GetDeviceProcAddr(dev, "vkDestroyPipeline")); DestroyShaderModule = reinterpret_cast<PFN_vkDestroyShaderModule>(GetDeviceProcAddr(dev, "vkDestroyShaderModule"));
CreatePipelineLayout = reinterpret_cast<PFN_vkCreatePipelineLayout>(GetDeviceProcAddr(dev, "vkCreatePipelineLayout")); CreatePipelineCache = reinterpret_cast<PFN_vkCreatePipelineCache>(GetDeviceProcAddr(dev, "vkCreatePipelineCache"));
DestroyPipelineLayout = reinterpret_cast<PFN_vkDestroyPipelineLayout>(GetDeviceProcAddr(dev, "vkDestroyPipelineLayout")); DestroyPipelineCache = reinterpret_cast<PFN_vkDestroyPipelineCache>(GetDeviceProcAddr(dev, "vkDestroyPipelineCache"));
CreateSampler = reinterpret_cast<PFN_vkCreateSampler>(GetDeviceProcAddr(dev, "vkCreateSampler")); GetPipelineCacheData = reinterpret_cast<PFN_vkGetPipelineCacheData>(GetDeviceProcAddr(dev, "vkGetPipelineCacheData"));
DestroySampler = reinterpret_cast<PFN_vkDestroySampler>(GetDeviceProcAddr(dev, "vkDestroySampler")); MergePipelineCaches = reinterpret_cast<PFN_vkMergePipelineCaches>(GetDeviceProcAddr(dev, "vkMergePipelineCaches"));
CreateDescriptorSetLayout = reinterpret_cast<PFN_vkCreateDescriptorSetLayout>(GetDeviceProcAddr(dev, "vkCreateDescriptorSetLayout")); CreateGraphicsPipelines =
DestroyDescriptorSetLayout = reinterpret_cast<PFN_vkDestroyDescriptorSetLayout>(GetDeviceProcAddr(dev, "vkDestroyDescriptorSetLayout")); reinterpret_cast<PFN_vkCreateGraphicsPipelines>(GetDeviceProcAddr(dev, "vkCreateGraphicsPipelines"));
CreateDescriptorPool = reinterpret_cast<PFN_vkCreateDescriptorPool>(GetDeviceProcAddr(dev, "vkCreateDescriptorPool")); CreateComputePipelines =
DestroyDescriptorPool = reinterpret_cast<PFN_vkDestroyDescriptorPool>(GetDeviceProcAddr(dev, "vkDestroyDescriptorPool")); reinterpret_cast<PFN_vkCreateComputePipelines>(GetDeviceProcAddr(dev, "vkCreateComputePipelines"));
ResetDescriptorPool = reinterpret_cast<PFN_vkResetDescriptorPool>(GetDeviceProcAddr(dev, "vkResetDescriptorPool")); DestroyPipeline = reinterpret_cast<PFN_vkDestroyPipeline>(GetDeviceProcAddr(dev, "vkDestroyPipeline"));
AllocateDescriptorSets = reinterpret_cast<PFN_vkAllocateDescriptorSets>(GetDeviceProcAddr(dev, "vkAllocateDescriptorSets")); CreatePipelineLayout = reinterpret_cast<PFN_vkCreatePipelineLayout>(GetDeviceProcAddr(dev, "vkCreatePipelineLayout"));
FreeDescriptorSets = reinterpret_cast<PFN_vkFreeDescriptorSets>(GetDeviceProcAddr(dev, "vkFreeDescriptorSets")); DestroyPipelineLayout =
UpdateDescriptorSets = reinterpret_cast<PFN_vkUpdateDescriptorSets>(GetDeviceProcAddr(dev, "vkUpdateDescriptorSets")); reinterpret_cast<PFN_vkDestroyPipelineLayout>(GetDeviceProcAddr(dev, "vkDestroyPipelineLayout"));
CreateFramebuffer = reinterpret_cast<PFN_vkCreateFramebuffer>(GetDeviceProcAddr(dev, "vkCreateFramebuffer")); CreateSampler = reinterpret_cast<PFN_vkCreateSampler>(GetDeviceProcAddr(dev, "vkCreateSampler"));
DestroyFramebuffer = reinterpret_cast<PFN_vkDestroyFramebuffer>(GetDeviceProcAddr(dev, "vkDestroyFramebuffer")); DestroySampler = reinterpret_cast<PFN_vkDestroySampler>(GetDeviceProcAddr(dev, "vkDestroySampler"));
CreateRenderPass = reinterpret_cast<PFN_vkCreateRenderPass>(GetDeviceProcAddr(dev, "vkCreateRenderPass")); CreateDescriptorSetLayout =
DestroyRenderPass = reinterpret_cast<PFN_vkDestroyRenderPass>(GetDeviceProcAddr(dev, "vkDestroyRenderPass")); reinterpret_cast<PFN_vkCreateDescriptorSetLayout>(GetDeviceProcAddr(dev, "vkCreateDescriptorSetLayout"));
GetRenderAreaGranularity = reinterpret_cast<PFN_vkGetRenderAreaGranularity>(GetDeviceProcAddr(dev, "vkGetRenderAreaGranularity")); DestroyDescriptorSetLayout =
CreateCommandPool = reinterpret_cast<PFN_vkCreateCommandPool>(GetDeviceProcAddr(dev, "vkCreateCommandPool")); reinterpret_cast<PFN_vkDestroyDescriptorSetLayout>(GetDeviceProcAddr(dev, "vkDestroyDescriptorSetLayout"));
DestroyCommandPool = reinterpret_cast<PFN_vkDestroyCommandPool>(GetDeviceProcAddr(dev, "vkDestroyCommandPool")); CreateDescriptorPool = reinterpret_cast<PFN_vkCreateDescriptorPool>(GetDeviceProcAddr(dev, "vkCreateDescriptorPool"));
ResetCommandPool = reinterpret_cast<PFN_vkResetCommandPool>(GetDeviceProcAddr(dev, "vkResetCommandPool")); DestroyDescriptorPool =
AllocateCommandBuffers = reinterpret_cast<PFN_vkAllocateCommandBuffers>(GetDeviceProcAddr(dev, "vkAllocateCommandBuffers")); reinterpret_cast<PFN_vkDestroyDescriptorPool>(GetDeviceProcAddr(dev, "vkDestroyDescriptorPool"));
FreeCommandBuffers = reinterpret_cast<PFN_vkFreeCommandBuffers>(GetDeviceProcAddr(dev, "vkFreeCommandBuffers")); ResetDescriptorPool = reinterpret_cast<PFN_vkResetDescriptorPool>(GetDeviceProcAddr(dev, "vkResetDescriptorPool"));
BeginCommandBuffer = reinterpret_cast<PFN_vkBeginCommandBuffer>(GetDeviceProcAddr(dev, "vkBeginCommandBuffer")); AllocateDescriptorSets =
EndCommandBuffer = reinterpret_cast<PFN_vkEndCommandBuffer>(GetDeviceProcAddr(dev, "vkEndCommandBuffer")); reinterpret_cast<PFN_vkAllocateDescriptorSets>(GetDeviceProcAddr(dev, "vkAllocateDescriptorSets"));
ResetCommandBuffer = reinterpret_cast<PFN_vkResetCommandBuffer>(GetDeviceProcAddr(dev, "vkResetCommandBuffer")); FreeDescriptorSets = reinterpret_cast<PFN_vkFreeDescriptorSets>(GetDeviceProcAddr(dev, "vkFreeDescriptorSets"));
CmdBindPipeline = reinterpret_cast<PFN_vkCmdBindPipeline>(GetDeviceProcAddr(dev, "vkCmdBindPipeline")); UpdateDescriptorSets = reinterpret_cast<PFN_vkUpdateDescriptorSets>(GetDeviceProcAddr(dev, "vkUpdateDescriptorSets"));
CmdSetViewport = reinterpret_cast<PFN_vkCmdSetViewport>(GetDeviceProcAddr(dev, "vkCmdSetViewport")); CreateFramebuffer = reinterpret_cast<PFN_vkCreateFramebuffer>(GetDeviceProcAddr(dev, "vkCreateFramebuffer"));
CmdSetScissor = reinterpret_cast<PFN_vkCmdSetScissor>(GetDeviceProcAddr(dev, "vkCmdSetScissor")); DestroyFramebuffer = reinterpret_cast<PFN_vkDestroyFramebuffer>(GetDeviceProcAddr(dev, "vkDestroyFramebuffer"));
CmdSetLineWidth = reinterpret_cast<PFN_vkCmdSetLineWidth>(GetDeviceProcAddr(dev, "vkCmdSetLineWidth")); CreateRenderPass = reinterpret_cast<PFN_vkCreateRenderPass>(GetDeviceProcAddr(dev, "vkCreateRenderPass"));
CmdSetDepthBias = reinterpret_cast<PFN_vkCmdSetDepthBias>(GetDeviceProcAddr(dev, "vkCmdSetDepthBias")); DestroyRenderPass = reinterpret_cast<PFN_vkDestroyRenderPass>(GetDeviceProcAddr(dev, "vkDestroyRenderPass"));
CmdSetBlendConstants = reinterpret_cast<PFN_vkCmdSetBlendConstants>(GetDeviceProcAddr(dev, "vkCmdSetBlendConstants")); GetRenderAreaGranularity =
CmdSetDepthBounds = reinterpret_cast<PFN_vkCmdSetDepthBounds>(GetDeviceProcAddr(dev, "vkCmdSetDepthBounds")); reinterpret_cast<PFN_vkGetRenderAreaGranularity>(GetDeviceProcAddr(dev, "vkGetRenderAreaGranularity"));
CmdSetStencilCompareMask = reinterpret_cast<PFN_vkCmdSetStencilCompareMask>(GetDeviceProcAddr(dev, "vkCmdSetStencilCompareMask")); CreateCommandPool = reinterpret_cast<PFN_vkCreateCommandPool>(GetDeviceProcAddr(dev, "vkCreateCommandPool"));
CmdSetStencilWriteMask = reinterpret_cast<PFN_vkCmdSetStencilWriteMask>(GetDeviceProcAddr(dev, "vkCmdSetStencilWriteMask")); DestroyCommandPool = reinterpret_cast<PFN_vkDestroyCommandPool>(GetDeviceProcAddr(dev, "vkDestroyCommandPool"));
CmdSetStencilReference = reinterpret_cast<PFN_vkCmdSetStencilReference>(GetDeviceProcAddr(dev, "vkCmdSetStencilReference")); ResetCommandPool = reinterpret_cast<PFN_vkResetCommandPool>(GetDeviceProcAddr(dev, "vkResetCommandPool"));
CmdBindDescriptorSets = reinterpret_cast<PFN_vkCmdBindDescriptorSets>(GetDeviceProcAddr(dev, "vkCmdBindDescriptorSets")); AllocateCommandBuffers =
CmdBindIndexBuffer = reinterpret_cast<PFN_vkCmdBindIndexBuffer>(GetDeviceProcAddr(dev, "vkCmdBindIndexBuffer")); reinterpret_cast<PFN_vkAllocateCommandBuffers>(GetDeviceProcAddr(dev, "vkAllocateCommandBuffers"));
CmdBindVertexBuffers = reinterpret_cast<PFN_vkCmdBindVertexBuffers>(GetDeviceProcAddr(dev, "vkCmdBindVertexBuffers")); FreeCommandBuffers = reinterpret_cast<PFN_vkFreeCommandBuffers>(GetDeviceProcAddr(dev, "vkFreeCommandBuffers"));
CmdDraw = reinterpret_cast<PFN_vkCmdDraw>(GetDeviceProcAddr(dev, "vkCmdDraw")); BeginCommandBuffer = reinterpret_cast<PFN_vkBeginCommandBuffer>(GetDeviceProcAddr(dev, "vkBeginCommandBuffer"));
CmdDrawIndexed = reinterpret_cast<PFN_vkCmdDrawIndexed>(GetDeviceProcAddr(dev, "vkCmdDrawIndexed")); EndCommandBuffer = reinterpret_cast<PFN_vkEndCommandBuffer>(GetDeviceProcAddr(dev, "vkEndCommandBuffer"));
CmdDrawIndirect = reinterpret_cast<PFN_vkCmdDrawIndirect>(GetDeviceProcAddr(dev, "vkCmdDrawIndirect")); ResetCommandBuffer = reinterpret_cast<PFN_vkResetCommandBuffer>(GetDeviceProcAddr(dev, "vkResetCommandBuffer"));
CmdDrawIndexedIndirect = reinterpret_cast<PFN_vkCmdDrawIndexedIndirect>(GetDeviceProcAddr(dev, "vkCmdDrawIndexedIndirect")); CmdBindPipeline = reinterpret_cast<PFN_vkCmdBindPipeline>(GetDeviceProcAddr(dev, "vkCmdBindPipeline"));
CmdDispatch = reinterpret_cast<PFN_vkCmdDispatch>(GetDeviceProcAddr(dev, "vkCmdDispatch")); CmdSetViewport = reinterpret_cast<PFN_vkCmdSetViewport>(GetDeviceProcAddr(dev, "vkCmdSetViewport"));
CmdDispatchIndirect = reinterpret_cast<PFN_vkCmdDispatchIndirect>(GetDeviceProcAddr(dev, "vkCmdDispatchIndirect")); CmdSetScissor = reinterpret_cast<PFN_vkCmdSetScissor>(GetDeviceProcAddr(dev, "vkCmdSetScissor"));
CmdCopyBuffer = reinterpret_cast<PFN_vkCmdCopyBuffer>(GetDeviceProcAddr(dev, "vkCmdCopyBuffer")); CmdSetLineWidth = reinterpret_cast<PFN_vkCmdSetLineWidth>(GetDeviceProcAddr(dev, "vkCmdSetLineWidth"));
CmdCopyImage = reinterpret_cast<PFN_vkCmdCopyImage>(GetDeviceProcAddr(dev, "vkCmdCopyImage")); CmdSetDepthBias = reinterpret_cast<PFN_vkCmdSetDepthBias>(GetDeviceProcAddr(dev, "vkCmdSetDepthBias"));
CmdBlitImage = reinterpret_cast<PFN_vkCmdBlitImage>(GetDeviceProcAddr(dev, "vkCmdBlitImage")); CmdSetBlendConstants = reinterpret_cast<PFN_vkCmdSetBlendConstants>(GetDeviceProcAddr(dev, "vkCmdSetBlendConstants"));
CmdCopyBufferToImage = reinterpret_cast<PFN_vkCmdCopyBufferToImage>(GetDeviceProcAddr(dev, "vkCmdCopyBufferToImage")); CmdSetDepthBounds = reinterpret_cast<PFN_vkCmdSetDepthBounds>(GetDeviceProcAddr(dev, "vkCmdSetDepthBounds"));
CmdCopyImageToBuffer = reinterpret_cast<PFN_vkCmdCopyImageToBuffer>(GetDeviceProcAddr(dev, "vkCmdCopyImageToBuffer")); CmdSetStencilCompareMask =
CmdUpdateBuffer = reinterpret_cast<PFN_vkCmdUpdateBuffer>(GetDeviceProcAddr(dev, "vkCmdUpdateBuffer")); reinterpret_cast<PFN_vkCmdSetStencilCompareMask>(GetDeviceProcAddr(dev, "vkCmdSetStencilCompareMask"));
CmdFillBuffer = reinterpret_cast<PFN_vkCmdFillBuffer>(GetDeviceProcAddr(dev, "vkCmdFillBuffer")); CmdSetStencilWriteMask =
CmdClearColorImage = reinterpret_cast<PFN_vkCmdClearColorImage>(GetDeviceProcAddr(dev, "vkCmdClearColorImage")); reinterpret_cast<PFN_vkCmdSetStencilWriteMask>(GetDeviceProcAddr(dev, "vkCmdSetStencilWriteMask"));
CmdClearDepthStencilImage = reinterpret_cast<PFN_vkCmdClearDepthStencilImage>(GetDeviceProcAddr(dev, "vkCmdClearDepthStencilImage")); CmdSetStencilReference =
CmdClearAttachments = reinterpret_cast<PFN_vkCmdClearAttachments>(GetDeviceProcAddr(dev, "vkCmdClearAttachments")); reinterpret_cast<PFN_vkCmdSetStencilReference>(GetDeviceProcAddr(dev, "vkCmdSetStencilReference"));
CmdResolveImage = reinterpret_cast<PFN_vkCmdResolveImage>(GetDeviceProcAddr(dev, "vkCmdResolveImage")); CmdBindDescriptorSets =
CmdSetEvent = reinterpret_cast<PFN_vkCmdSetEvent>(GetDeviceProcAddr(dev, "vkCmdSetEvent")); reinterpret_cast<PFN_vkCmdBindDescriptorSets>(GetDeviceProcAddr(dev, "vkCmdBindDescriptorSets"));
CmdResetEvent = reinterpret_cast<PFN_vkCmdResetEvent>(GetDeviceProcAddr(dev, "vkCmdResetEvent")); CmdBindIndexBuffer = reinterpret_cast<PFN_vkCmdBindIndexBuffer>(GetDeviceProcAddr(dev, "vkCmdBindIndexBuffer"));
CmdWaitEvents = reinterpret_cast<PFN_vkCmdWaitEvents>(GetDeviceProcAddr(dev, "vkCmdWaitEvents")); CmdBindVertexBuffers = reinterpret_cast<PFN_vkCmdBindVertexBuffers>(GetDeviceProcAddr(dev, "vkCmdBindVertexBuffers"));
CmdPipelineBarrier = reinterpret_cast<PFN_vkCmdPipelineBarrier>(GetDeviceProcAddr(dev, "vkCmdPipelineBarrier")); CmdDraw = reinterpret_cast<PFN_vkCmdDraw>(GetDeviceProcAddr(dev, "vkCmdDraw"));
CmdBeginQuery = reinterpret_cast<PFN_vkCmdBeginQuery>(GetDeviceProcAddr(dev, "vkCmdBeginQuery")); CmdDrawIndexed = reinterpret_cast<PFN_vkCmdDrawIndexed>(GetDeviceProcAddr(dev, "vkCmdDrawIndexed"));
CmdEndQuery = reinterpret_cast<PFN_vkCmdEndQuery>(GetDeviceProcAddr(dev, "vkCmdEndQuery")); CmdDrawIndirect = reinterpret_cast<PFN_vkCmdDrawIndirect>(GetDeviceProcAddr(dev, "vkCmdDrawIndirect"));
CmdResetQueryPool = reinterpret_cast<PFN_vkCmdResetQueryPool>(GetDeviceProcAddr(dev, "vkCmdResetQueryPool")); CmdDrawIndexedIndirect =
CmdWriteTimestamp = reinterpret_cast<PFN_vkCmdWriteTimestamp>(GetDeviceProcAddr(dev, "vkCmdWriteTimestamp")); reinterpret_cast<PFN_vkCmdDrawIndexedIndirect>(GetDeviceProcAddr(dev, "vkCmdDrawIndexedIndirect"));
CmdCopyQueryPoolResults = reinterpret_cast<PFN_vkCmdCopyQueryPoolResults>(GetDeviceProcAddr(dev, "vkCmdCopyQueryPoolResults")); CmdDispatch = reinterpret_cast<PFN_vkCmdDispatch>(GetDeviceProcAddr(dev, "vkCmdDispatch"));
CmdPushConstants = reinterpret_cast<PFN_vkCmdPushConstants>(GetDeviceProcAddr(dev, "vkCmdPushConstants")); CmdDispatchIndirect = reinterpret_cast<PFN_vkCmdDispatchIndirect>(GetDeviceProcAddr(dev, "vkCmdDispatchIndirect"));
CmdBeginRenderPass = reinterpret_cast<PFN_vkCmdBeginRenderPass>(GetDeviceProcAddr(dev, "vkCmdBeginRenderPass")); CmdCopyBuffer = reinterpret_cast<PFN_vkCmdCopyBuffer>(GetDeviceProcAddr(dev, "vkCmdCopyBuffer"));
CmdNextSubpass = reinterpret_cast<PFN_vkCmdNextSubpass>(GetDeviceProcAddr(dev, "vkCmdNextSubpass")); CmdCopyImage = reinterpret_cast<PFN_vkCmdCopyImage>(GetDeviceProcAddr(dev, "vkCmdCopyImage"));
CmdEndRenderPass = reinterpret_cast<PFN_vkCmdEndRenderPass>(GetDeviceProcAddr(dev, "vkCmdEndRenderPass")); CmdBlitImage = reinterpret_cast<PFN_vkCmdBlitImage>(GetDeviceProcAddr(dev, "vkCmdBlitImage"));
CmdExecuteCommands = reinterpret_cast<PFN_vkCmdExecuteCommands>(GetDeviceProcAddr(dev, "vkCmdExecuteCommands")); CmdCopyBufferToImage = reinterpret_cast<PFN_vkCmdCopyBufferToImage>(GetDeviceProcAddr(dev, "vkCmdCopyBufferToImage"));
CreateSwapchainKHR = reinterpret_cast<PFN_vkCreateSwapchainKHR>(GetDeviceProcAddr(dev, "vkCreateSwapchainKHR")); CmdCopyImageToBuffer = reinterpret_cast<PFN_vkCmdCopyImageToBuffer>(GetDeviceProcAddr(dev, "vkCmdCopyImageToBuffer"));
DestroySwapchainKHR = reinterpret_cast<PFN_vkDestroySwapchainKHR>(GetDeviceProcAddr(dev, "vkDestroySwapchainKHR")); CmdUpdateBuffer = reinterpret_cast<PFN_vkCmdUpdateBuffer>(GetDeviceProcAddr(dev, "vkCmdUpdateBuffer"));
GetSwapchainImagesKHR = reinterpret_cast<PFN_vkGetSwapchainImagesKHR>(GetDeviceProcAddr(dev, "vkGetSwapchainImagesKHR")); CmdFillBuffer = reinterpret_cast<PFN_vkCmdFillBuffer>(GetDeviceProcAddr(dev, "vkCmdFillBuffer"));
AcquireNextImageKHR = reinterpret_cast<PFN_vkAcquireNextImageKHR>(GetDeviceProcAddr(dev, "vkAcquireNextImageKHR")); CmdClearColorImage = reinterpret_cast<PFN_vkCmdClearColorImage>(GetDeviceProcAddr(dev, "vkCmdClearColorImage"));
QueuePresentKHR = reinterpret_cast<PFN_vkQueuePresentKHR>(GetDeviceProcAddr(dev, "vkQueuePresentKHR")); CmdClearDepthStencilImage =
CreateSharedSwapchainsKHR = reinterpret_cast<PFN_vkCreateSharedSwapchainsKHR>(GetDeviceProcAddr(dev, "vkCreateSharedSwapchainsKHR")); reinterpret_cast<PFN_vkCmdClearDepthStencilImage>(GetDeviceProcAddr(dev, "vkCmdClearDepthStencilImage"));
CmdClearAttachments = reinterpret_cast<PFN_vkCmdClearAttachments>(GetDeviceProcAddr(dev, "vkCmdClearAttachments"));
CmdResolveImage = reinterpret_cast<PFN_vkCmdResolveImage>(GetDeviceProcAddr(dev, "vkCmdResolveImage"));
CmdSetEvent = reinterpret_cast<PFN_vkCmdSetEvent>(GetDeviceProcAddr(dev, "vkCmdSetEvent"));
CmdResetEvent = reinterpret_cast<PFN_vkCmdResetEvent>(GetDeviceProcAddr(dev, "vkCmdResetEvent"));
CmdWaitEvents = reinterpret_cast<PFN_vkCmdWaitEvents>(GetDeviceProcAddr(dev, "vkCmdWaitEvents"));
CmdPipelineBarrier = reinterpret_cast<PFN_vkCmdPipelineBarrier>(GetDeviceProcAddr(dev, "vkCmdPipelineBarrier"));
CmdBeginQuery = reinterpret_cast<PFN_vkCmdBeginQuery>(GetDeviceProcAddr(dev, "vkCmdBeginQuery"));
CmdEndQuery = reinterpret_cast<PFN_vkCmdEndQuery>(GetDeviceProcAddr(dev, "vkCmdEndQuery"));
CmdResetQueryPool = reinterpret_cast<PFN_vkCmdResetQueryPool>(GetDeviceProcAddr(dev, "vkCmdResetQueryPool"));
CmdWriteTimestamp = reinterpret_cast<PFN_vkCmdWriteTimestamp>(GetDeviceProcAddr(dev, "vkCmdWriteTimestamp"));
CmdCopyQueryPoolResults =
reinterpret_cast<PFN_vkCmdCopyQueryPoolResults>(GetDeviceProcAddr(dev, "vkCmdCopyQueryPoolResults"));
CmdPushConstants = reinterpret_cast<PFN_vkCmdPushConstants>(GetDeviceProcAddr(dev, "vkCmdPushConstants"));
CmdBeginRenderPass = reinterpret_cast<PFN_vkCmdBeginRenderPass>(GetDeviceProcAddr(dev, "vkCmdBeginRenderPass"));
CmdNextSubpass = reinterpret_cast<PFN_vkCmdNextSubpass>(GetDeviceProcAddr(dev, "vkCmdNextSubpass"));
CmdEndRenderPass = reinterpret_cast<PFN_vkCmdEndRenderPass>(GetDeviceProcAddr(dev, "vkCmdEndRenderPass"));
CmdExecuteCommands = reinterpret_cast<PFN_vkCmdExecuteCommands>(GetDeviceProcAddr(dev, "vkCmdExecuteCommands"));
CreateSwapchainKHR = reinterpret_cast<PFN_vkCreateSwapchainKHR>(GetDeviceProcAddr(dev, "vkCreateSwapchainKHR"));
DestroySwapchainKHR = reinterpret_cast<PFN_vkDestroySwapchainKHR>(GetDeviceProcAddr(dev, "vkDestroySwapchainKHR"));
GetSwapchainImagesKHR =
reinterpret_cast<PFN_vkGetSwapchainImagesKHR>(GetDeviceProcAddr(dev, "vkGetSwapchainImagesKHR"));
AcquireNextImageKHR = reinterpret_cast<PFN_vkAcquireNextImageKHR>(GetDeviceProcAddr(dev, "vkAcquireNextImageKHR"));
QueuePresentKHR = reinterpret_cast<PFN_vkQueuePresentKHR>(GetDeviceProcAddr(dev, "vkQueuePresentKHR"));
CreateSharedSwapchainsKHR =
reinterpret_cast<PFN_vkCreateSharedSwapchainsKHR>(GetDeviceProcAddr(dev, "vkCreateSharedSwapchainsKHR"));
} }
} // namespace vk } // namespace vk

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,40 +1,36 @@
#include "boo/graphicsdev/nx_compiler.hpp" #include "boo/graphicsdev/nx_compiler.hpp"
int main(int argc, char** argv) int main(int argc, char** argv) {
{ nx_compiler c;
nx_compiler c; c.initialize();
c.initialize();
nx_shader_stage_object objs[] = nx_shader_stage_object objs[] = {c.compile(nx_shader_stage::VERTEX,
{ "#version 330\n"
c.compile(nx_shader_stage::VERTEX, "#extension GL_ARB_separate_shader_objects: enable\n"
"#version 330\n" "#extension GL_ARB_shading_language_420pack: enable\n"
"#extension GL_ARB_separate_shader_objects: enable\n" "layout(location=0) in vec3 in_pos;\n"
"#extension GL_ARB_shading_language_420pack: enable\n" "layout(location=1) in vec3 in_norm;\n"
"layout(location=0) in vec3 in_pos;\n" "layout(location=2) in vec2 in_uv;\n"
"layout(location=1) in vec3 in_norm;\n" "layout(location=0) out vec2 out_uv;\n"
"layout(location=2) in vec2 in_uv;\n" "void main()\n"
"layout(location=0) out vec2 out_uv;\n" "{\n"
"void main()\n" " gl_Position = vec4(in_pos, 1.0).zyxx;\n"
"{\n" " out_uv = in_uv;\n"
" gl_Position = vec4(in_pos, 1.0).zyxx;\n" "}"),
" out_uv = in_uv;\n" c.compile(nx_shader_stage::FRAGMENT,
"}"), "#version 330\n"
c.compile(nx_shader_stage::FRAGMENT, "#extension GL_ARB_separate_shader_objects: enable\n"
"#version 330\n" "#extension GL_ARB_shading_language_420pack: enable\n"
"#extension GL_ARB_separate_shader_objects: enable\n" "layout(binding=8) uniform sampler2D texs[2];\n"
"#extension GL_ARB_shading_language_420pack: enable\n" "layout(location=0) out vec4 out_frag;\n"
"layout(binding=8) uniform sampler2D texs[2];\n" "layout(location=0) in vec2 out_uv;\n"
"layout(location=0) out vec4 out_frag;\n" "void main()\n"
"layout(location=0) in vec2 out_uv;\n" "{\n"
"void main()\n" " out_frag = texture(texs[0], out_uv) + texture(texs[1], out_uv);\n"
"{\n" "}")};
" out_frag = texture(texs[0], out_uv) + texture(texs[1], out_uv);\n"
"}")
};
std::string log; std::string log;
auto linkData = c.link(2, objs, &log); auto linkData = c.link(2, objs, &log);
return 0; return 0;
} }

View File

@ -3,111 +3,93 @@
#include "IHIDDevice.hpp" #include "IHIDDevice.hpp"
#include <cstdarg> #include <cstdarg>
namespace boo namespace boo {
{
DeviceBase::DeviceBase(uint64_t typeHash, DeviceToken* token) DeviceBase::DeviceBase(uint64_t typeHash, DeviceToken* token) : m_typeHash(typeHash), m_token(token) {}
: m_typeHash(typeHash), m_token(token)
{ void DeviceBase::_deviceDisconnected() {
deviceDisconnected();
m_token = nullptr;
if (m_hidDev) {
m_hidDev->_deviceDisconnected();
m_hidDev.reset();
}
} }
void DeviceBase::_deviceDisconnected() void DeviceBase::closeDevice() {
{ if (m_token)
deviceDisconnected(); m_token->_deviceClose();
m_token = nullptr;
if (m_hidDev)
{
m_hidDev->_deviceDisconnected();
m_hidDev.reset();
}
} }
void DeviceBase::closeDevice() void DeviceBase::deviceError(const char* error, ...) {
{ va_list vl;
if (m_token) va_start(vl, error);
m_token->_deviceClose(); vfprintf(stderr, error, vl);
va_end(vl);
} }
void DeviceBase::deviceError(const char* error, ...) bool DeviceBase::sendUSBInterruptTransfer(const uint8_t* data, size_t length) {
{ if (m_hidDev)
va_list vl; return m_hidDev->_sendUSBInterruptTransfer(data, length);
va_start(vl, error); return false;
vfprintf(stderr, error, vl);
va_end(vl);
} }
bool DeviceBase::sendUSBInterruptTransfer(const uint8_t* data, size_t length) size_t DeviceBase::receiveUSBInterruptTransfer(uint8_t* data, size_t length) {
{ if (m_hidDev)
if (m_hidDev) return m_hidDev->_receiveUSBInterruptTransfer(data, length);
return m_hidDev->_sendUSBInterruptTransfer(data, length); return false;
return false;
} }
size_t DeviceBase::receiveUSBInterruptTransfer(uint8_t* data, size_t length) unsigned DeviceBase::getVendorId() {
{ if (m_token)
if (m_hidDev) return m_token->getVendorId();
return m_hidDev->_receiveUSBInterruptTransfer(data, length); return -1;
return false;
} }
unsigned DeviceBase::getVendorId() unsigned DeviceBase::getProductId() {
{ if (m_token)
if (m_token) return m_token->getProductId();
return m_token->getVendorId(); return -1;
return -1;
} }
unsigned DeviceBase::getProductId() std::string_view DeviceBase::getVendorName() {
{ if (m_token)
if (m_token) return m_token->getVendorName();
return m_token->getProductId(); return {};
return -1;
} }
std::string_view DeviceBase::getVendorName() std::string_view DeviceBase::getProductName() {
{ if (m_token)
if (m_token) return m_token->getProductName();
return m_token->getVendorName(); return {};
return {};
}
std::string_view DeviceBase::getProductName()
{
if (m_token)
return m_token->getProductName();
return {};
} }
#if _WIN32 #if _WIN32
#if !WINDOWS_STORE #if !WINDOWS_STORE
const PHIDP_PREPARSED_DATA DeviceBase::getReportDescriptor() const PHIDP_PREPARSED_DATA DeviceBase::getReportDescriptor() {
{ if (m_hidDev)
if (m_hidDev) return m_hidDev->_getReportDescriptor();
return m_hidDev->_getReportDescriptor(); return {};
return {};
} }
#endif #endif
#else #else
std::vector<uint8_t> DeviceBase::getReportDescriptor() std::vector<uint8_t> DeviceBase::getReportDescriptor() {
{ if (m_hidDev)
if (m_hidDev) return m_hidDev->_getReportDescriptor();
return m_hidDev->_getReportDescriptor(); return {};
return {};
} }
#endif #endif
bool DeviceBase::sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message) bool DeviceBase::sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message) {
{ if (m_hidDev)
if (m_hidDev) return m_hidDev->_sendHIDReport(data, length, tp, message);
return m_hidDev->_sendHIDReport(data, length, tp, message); return false;
return false;
} }
size_t DeviceBase::receiveHIDReport(uint8_t* data, size_t length, HIDReportType tp, uint32_t message) size_t DeviceBase::receiveHIDReport(uint8_t* data, size_t length, HIDReportType tp, uint32_t message) {
{ if (m_hidDev)
if (m_hidDev) return m_hidDev->_receiveHIDReport(data, length, tp, message);
return m_hidDev->_receiveHIDReport(data, length, tp, message); return 0;
return 0;
} }
} } // namespace boo

View File

@ -6,68 +6,59 @@
#include <usbiodef.h> #include <usbiodef.h>
#endif #endif
namespace boo namespace boo {
{
DeviceFinder* DeviceFinder::skDevFinder = nullptr; DeviceFinder* DeviceFinder::skDevFinder = nullptr;
#if _WIN32 && !WINDOWS_STORE #if _WIN32 && !WINDOWS_STORE
/* Windows-specific WM_DEVICECHANGED handler */ /* Windows-specific WM_DEVICECHANGED handler */
LRESULT DeviceFinder::winDevChangedHandler(WPARAM wParam, LPARAM lParam) LRESULT DeviceFinder::winDevChangedHandler(WPARAM wParam, LPARAM lParam) {
{ PDEV_BROADCAST_HDR dbh = (PDEV_BROADCAST_HDR)lParam;
PDEV_BROADCAST_HDR dbh = (PDEV_BROADCAST_HDR)lParam; PDEV_BROADCAST_DEVICEINTERFACE dbhi = (PDEV_BROADCAST_DEVICEINTERFACE)lParam;
PDEV_BROADCAST_DEVICEINTERFACE dbhi = (PDEV_BROADCAST_DEVICEINTERFACE)lParam; DeviceFinder* finder = instance();
DeviceFinder* finder = instance(); if (!finder)
if (!finder)
return 0;
if (wParam == DBT_DEVICEARRIVAL)
{
if (dbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
{
DeviceType type = DeviceType::None;
if (dbhi->dbcc_classguid == GUID_DEVINTERFACE_USB_DEVICE)
type = DeviceType::USB;
else if (dbhi->dbcc_classguid == GUID_DEVINTERFACE_HID)
type = DeviceType::HID;
if (type != DeviceType::None)
{
#ifdef UNICODE
char devPath[1024];
wcstombs(devPath, dbhi->dbcc_name, 1024);
finder->m_listener->_extDevConnect(devPath);
#else
finder->m_listener->_extDevConnect(dbhi->dbcc_name);
#endif
}
}
}
else if (wParam == DBT_DEVICEREMOVECOMPLETE)
{
if (dbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
{
DeviceType type = DeviceType::None;
if (dbhi->dbcc_classguid == GUID_DEVINTERFACE_USB_DEVICE)
type = DeviceType::USB;
else if (dbhi->dbcc_classguid == GUID_DEVINTERFACE_HID)
type = DeviceType::HID;
if (type != DeviceType::None)
{
#ifdef UNICODE
char devPath[1024];
wcstombs(devPath, dbhi->dbcc_name, 1024);
finder->m_listener->_extDevDisconnect(devPath);
#else
finder->m_listener->_extDevDisconnect(dbhi->dbcc_name);
#endif
}
}
}
return 0; return 0;
if (wParam == DBT_DEVICEARRIVAL) {
if (dbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
DeviceType type = DeviceType::None;
if (dbhi->dbcc_classguid == GUID_DEVINTERFACE_USB_DEVICE)
type = DeviceType::USB;
else if (dbhi->dbcc_classguid == GUID_DEVINTERFACE_HID)
type = DeviceType::HID;
if (type != DeviceType::None) {
#ifdef UNICODE
char devPath[1024];
wcstombs(devPath, dbhi->dbcc_name, 1024);
finder->m_listener->_extDevConnect(devPath);
#else
finder->m_listener->_extDevConnect(dbhi->dbcc_name);
#endif
}
}
} else if (wParam == DBT_DEVICEREMOVECOMPLETE) {
if (dbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
DeviceType type = DeviceType::None;
if (dbhi->dbcc_classguid == GUID_DEVINTERFACE_USB_DEVICE)
type = DeviceType::USB;
else if (dbhi->dbcc_classguid == GUID_DEVINTERFACE_HID)
type = DeviceType::HID;
if (type != DeviceType::None) {
#ifdef UNICODE
char devPath[1024];
wcstombs(devPath, dbhi->dbcc_name, 1024);
finder->m_listener->_extDevDisconnect(devPath);
#else
finder->m_listener->_extDevDisconnect(dbhi->dbcc_name);
#endif
}
}
}
return 0;
} }
#endif #endif
} } // namespace boo

View File

@ -3,87 +3,75 @@
#include "boo/inputdev/GenericPad.hpp" #include "boo/inputdev/GenericPad.hpp"
#include "IHIDDevice.hpp" #include "IHIDDevice.hpp"
namespace boo namespace boo {
{
extern const DeviceSignature BOO_DEVICE_SIGS[]; extern const DeviceSignature BOO_DEVICE_SIGS[];
bool DeviceSignature::DeviceMatchToken(const DeviceToken& token, const TDeviceSignatureSet& sigSet) {
bool DeviceSignature::DeviceMatchToken(const DeviceToken& token, const TDeviceSignatureSet& sigSet) if (token.getDeviceType() == DeviceType::HID) {
{ uint64_t genPadHash = dev_typeid(GenericPad);
if (token.getDeviceType() == DeviceType::HID) bool hasGenericPad = false;
{ for (const DeviceSignature* sig : sigSet) {
uint64_t genPadHash = dev_typeid(GenericPad); if (sig->m_vid == token.getVendorId() && sig->m_pid == token.getProductId() && sig->m_type != DeviceType::HID)
bool hasGenericPad = false; return false;
for (const DeviceSignature* sig : sigSet) if (sig->m_typeHash == genPadHash)
{ hasGenericPad = true;
if (sig->m_vid == token.getVendorId() && sig->m_pid == token.getProductId() &&
sig->m_type != DeviceType::HID)
return false;
if (sig->m_typeHash == genPadHash)
hasGenericPad = true;
}
return hasGenericPad;
} }
for (const DeviceSignature* sig : sigSet) return hasGenericPad;
{ }
if (sig->m_vid == token.getVendorId() && sig->m_pid == token.getProductId()) for (const DeviceSignature* sig : sigSet) {
return true; if (sig->m_vid == token.getVendorId() && sig->m_pid == token.getProductId())
} return true;
return false; }
return false;
} }
std::shared_ptr<IHIDDevice> IHIDDeviceNew(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp); std::shared_ptr<IHIDDevice> IHIDDeviceNew(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp);
std::shared_ptr<DeviceBase> DeviceSignature::DeviceNew(DeviceToken& token) std::shared_ptr<DeviceBase> DeviceSignature::DeviceNew(DeviceToken& token) {
{ std::shared_ptr<DeviceBase> retval;
std::shared_ptr<DeviceBase> retval;
/* Perform signature-matching to find the appropriate device-factory */ /* Perform signature-matching to find the appropriate device-factory */
const DeviceSignature* foundSig = nullptr; const DeviceSignature* foundSig = nullptr;
const DeviceSignature* sigIter = BOO_DEVICE_SIGS; const DeviceSignature* sigIter = BOO_DEVICE_SIGS;
unsigned targetVid = token.getVendorId(); unsigned targetVid = token.getVendorId();
unsigned targetPid = token.getProductId(); unsigned targetPid = token.getProductId();
while (sigIter->m_name) while (sigIter->m_name) {
{ if (sigIter->m_vid == targetVid && sigIter->m_pid == targetPid) {
if (sigIter->m_vid == targetVid && sigIter->m_pid == targetPid) foundSig = sigIter;
{ break;
foundSig = sigIter;
break;
}
++sigIter;
} }
if (!foundSig) ++sigIter;
{ }
/* Try Generic HID devices */ if (!foundSig) {
if (token.getDeviceType() == DeviceType::HID) /* Try Generic HID devices */
{ if (token.getDeviceType() == DeviceType::HID) {
retval = std::make_shared<GenericPad>(&token); retval = std::make_shared<GenericPad>(&token);
if (!retval) if (!retval)
return nullptr;
retval->m_hidDev = IHIDDeviceNew(token, retval);
if (!retval->m_hidDev)
return nullptr;
retval->m_hidDev->_startThread();
return retval;
}
return nullptr; return nullptr;
retval->m_hidDev = IHIDDeviceNew(token, retval);
if (!retval->m_hidDev)
return nullptr;
retval->m_hidDev->_startThread();
return retval;
} }
if (foundSig->m_type != DeviceType::None && foundSig->m_type != token.getDeviceType())
return nullptr;
retval = foundSig->m_factory(&token); return nullptr;
if (!retval) }
return nullptr; if (foundSig->m_type != DeviceType::None && foundSig->m_type != token.getDeviceType())
return nullptr;
retval->m_hidDev = IHIDDeviceNew(token, retval);
if (!retval->m_hidDev) retval = foundSig->m_factory(&token);
return nullptr; if (!retval)
retval->m_hidDev->_startThread(); return nullptr;
return retval; retval->m_hidDev = IHIDDeviceNew(token, retval);
if (!retval->m_hidDev)
return nullptr;
retval->m_hidDev->_startThread();
return retval;
} }
} } // namespace boo

View File

@ -3,8 +3,7 @@
#include <cstdio> #include <cstdio>
#include <cstring> #include <cstring>
namespace boo namespace boo {
{
/* /*
* Reference: https://github.com/ToadKing/wii-u-gc-adapter/blob/master/wii-u-gc-adapter.c * Reference: https://github.com/ToadKing/wii-u-gc-adapter/blob/master/wii-u-gc-adapter.c
*/ */
@ -14,134 +13,118 @@ DolphinSmashAdapter::DolphinSmashAdapter(DeviceToken* token)
DolphinSmashAdapter::~DolphinSmashAdapter() {} DolphinSmashAdapter::~DolphinSmashAdapter() {}
static inline EDolphinControllerType parseType(unsigned char status) static inline EDolphinControllerType parseType(unsigned char status) {
{ EDolphinControllerType type =
EDolphinControllerType type = EDolphinControllerType(status) & (EDolphinControllerType::Normal | EDolphinControllerType::Wavebird);
EDolphinControllerType(status) & (EDolphinControllerType::Normal | EDolphinControllerType::Wavebird); switch (type) {
switch (type) case EDolphinControllerType::Normal:
{ case EDolphinControllerType::Wavebird:
case EDolphinControllerType::Normal:
case EDolphinControllerType::Wavebird:
return type;
default:
return EDolphinControllerType::None;
}
}
static inline EDolphinControllerType parseState(DolphinControllerState* stateOut, uint8_t* payload, bool& rumble)
{
memset(stateOut, 0, sizeof(DolphinControllerState));
unsigned char status = payload[0];
EDolphinControllerType type = parseType(status);
rumble = ((status & 0x04) != 0) ? true : false;
stateOut->m_btns = (uint16_t)payload[1] << 8 | (uint16_t)payload[2];
stateOut->m_leftStick[0] = payload[3];
stateOut->m_leftStick[1] = payload[4];
stateOut->m_rightStick[0] = payload[5];
stateOut->m_rightStick[1] = payload[6];
stateOut->m_analogTriggers[0] = payload[7];
stateOut->m_analogTriggers[1] = payload[8];
return type; return type;
default:
return EDolphinControllerType::None;
}
} }
void DolphinSmashAdapter::initialCycle() static inline EDolphinControllerType parseState(DolphinControllerState* stateOut, uint8_t* payload, bool& rumble) {
{ memset(stateOut, 0, sizeof(DolphinControllerState));
uint8_t handshakePayload[] = {0x13}; unsigned char status = payload[0];
sendUSBInterruptTransfer(handshakePayload, sizeof(handshakePayload)); EDolphinControllerType type = parseType(status);
rumble = ((status & 0x04) != 0) ? true : false;
stateOut->m_btns = (uint16_t)payload[1] << 8 | (uint16_t)payload[2];
stateOut->m_leftStick[0] = payload[3];
stateOut->m_leftStick[1] = payload[4];
stateOut->m_rightStick[0] = payload[5];
stateOut->m_rightStick[1] = payload[6];
stateOut->m_analogTriggers[0] = payload[7];
stateOut->m_analogTriggers[1] = payload[8];
return type;
} }
void DolphinSmashAdapter::transferCycle() void DolphinSmashAdapter::initialCycle() {
{ uint8_t handshakePayload[] = {0x13};
uint8_t payload[37]; sendUSBInterruptTransfer(handshakePayload, sizeof(handshakePayload));
size_t recvSz = receiveUSBInterruptTransfer(payload, sizeof(payload)); }
if (recvSz != 37 || payload[0] != 0x21)
return;
// printf("RECEIVED DATA %zu %02X\n", recvSz, payload[0]); void DolphinSmashAdapter::transferCycle() {
uint8_t payload[37];
size_t recvSz = receiveUSBInterruptTransfer(payload, sizeof(payload));
if (recvSz != 37 || payload[0] != 0x21)
return;
std::lock_guard<std::mutex> lk(m_callbackLock); // printf("RECEIVED DATA %zu %02X\n", recvSz, payload[0]);
if (!m_callback)
return;
/* Parse controller states */ std::lock_guard<std::mutex> lk(m_callbackLock);
uint8_t* controller = &payload[1]; if (!m_callback)
uint8_t rumbleMask = 0; return;
for (uint32_t i = 0; i < 4; i++, controller += 9)
{ /* Parse controller states */
DolphinControllerState state; uint8_t* controller = &payload[1];
bool rumble = false; uint8_t rumbleMask = 0;
EDolphinControllerType type = parseState(&state, controller, rumble); for (uint32_t i = 0; i < 4; i++, controller += 9) {
if (type != EDolphinControllerType::None && !(m_knownControllers & 1 << i)) DolphinControllerState state;
{ bool rumble = false;
m_leftStickCal[0] = state.m_leftStick[0]; EDolphinControllerType type = parseState(&state, controller, rumble);
m_leftStickCal[1] = state.m_leftStick[1]; if (type != EDolphinControllerType::None && !(m_knownControllers & 1 << i)) {
m_rightStickCal[0] = state.m_rightStick[0]; m_leftStickCal[0] = state.m_leftStick[0];
m_rightStickCal[1] = state.m_rightStick[1]; m_leftStickCal[1] = state.m_leftStick[1];
m_triggersCal[0] = state.m_analogTriggers[0]; m_rightStickCal[0] = state.m_rightStick[0];
m_triggersCal[1] = state.m_analogTriggers[1]; m_rightStickCal[1] = state.m_rightStick[1];
m_knownControllers |= 1 << i; m_triggersCal[0] = state.m_analogTriggers[0];
m_callback->controllerConnected(i, type); m_triggersCal[1] = state.m_analogTriggers[1];
} m_knownControllers |= 1 << i;
else if (type == EDolphinControllerType::None && (m_knownControllers & 1 << i)) m_callback->controllerConnected(i, type);
{ } else if (type == EDolphinControllerType::None && (m_knownControllers & 1 << i)) {
m_knownControllers &= ~(1 << i); m_knownControllers &= ~(1 << i);
m_callback->controllerDisconnected(i); m_callback->controllerDisconnected(i);
} }
if (m_knownControllers & 1 << i) if (m_knownControllers & 1 << i) {
{ state.m_leftStick[0] = state.m_leftStick[0] - m_leftStickCal[0];
state.m_leftStick[0] = state.m_leftStick[0] - m_leftStickCal[0]; state.m_leftStick[1] = state.m_leftStick[1] - m_leftStickCal[1];
state.m_leftStick[1] = state.m_leftStick[1] - m_leftStickCal[1]; state.m_rightStick[0] = state.m_rightStick[0] - m_rightStickCal[0];
state.m_rightStick[0] = state.m_rightStick[0] - m_rightStickCal[0]; state.m_rightStick[1] = state.m_rightStick[1] - m_rightStickCal[1];
state.m_rightStick[1] = state.m_rightStick[1] - m_rightStickCal[1]; state.m_analogTriggers[0] = state.m_analogTriggers[0] - m_triggersCal[0];
state.m_analogTriggers[0] = state.m_analogTriggers[0] - m_triggersCal[0]; state.m_analogTriggers[1] = state.m_analogTriggers[1] - m_triggersCal[1];
state.m_analogTriggers[1] = state.m_analogTriggers[1] - m_triggersCal[1]; m_callback->controllerUpdate(i, type, state);
m_callback->controllerUpdate(i, type, state); }
} rumbleMask |= rumble ? 1 << i : 0;
rumbleMask |= rumble ? 1 << i : 0; }
/* Send rumble message (if needed) */
uint8_t rumbleReq = m_rumbleRequest & rumbleMask;
if (rumbleReq != m_rumbleState) {
uint8_t rumbleMessage[5] = {0x11};
for (int i = 0; i < 4; ++i) {
if (rumbleReq & 1 << i)
rumbleMessage[i + 1] = 1;
else if (m_hardStop[i])
rumbleMessage[i + 1] = 2;
else
rumbleMessage[i + 1] = 0;
} }
/* Send rumble message (if needed) */
uint8_t rumbleReq = m_rumbleRequest & rumbleMask;
if (rumbleReq != m_rumbleState)
{
uint8_t rumbleMessage[5] = {0x11};
for (int i = 0; i < 4; ++i)
{
if (rumbleReq & 1 << i)
rumbleMessage[i + 1] = 1;
else if (m_hardStop[i])
rumbleMessage[i + 1] = 2;
else
rumbleMessage[i + 1] = 0;
}
sendUSBInterruptTransfer(rumbleMessage, sizeof(rumbleMessage));
m_rumbleState = rumbleReq;
}
}
void DolphinSmashAdapter::finalCycle()
{
uint8_t rumbleMessage[5] = {0x11, 0, 0, 0, 0};
sendUSBInterruptTransfer(rumbleMessage, sizeof(rumbleMessage)); sendUSBInterruptTransfer(rumbleMessage, sizeof(rumbleMessage));
m_rumbleState = rumbleReq;
}
} }
void DolphinSmashAdapter::deviceDisconnected() void DolphinSmashAdapter::finalCycle() {
{ uint8_t rumbleMessage[5] = {0x11, 0, 0, 0, 0};
for (uint32_t i = 0; i < 4; i++) sendUSBInterruptTransfer(rumbleMessage, sizeof(rumbleMessage));
{ }
if (m_knownControllers & 1 << i)
{ void DolphinSmashAdapter::deviceDisconnected() {
m_knownControllers &= ~(1 << i); for (uint32_t i = 0; i < 4; i++) {
std::lock_guard<std::mutex> lk(m_callbackLock); if (m_knownControllers & 1 << i) {
if (m_callback) m_knownControllers &= ~(1 << i);
m_callback->controllerDisconnected(i); std::lock_guard<std::mutex> lk(m_callbackLock);
} if (m_callback)
m_callback->controllerDisconnected(i);
} }
}
} }
/* The following code is derived from pad.c in libogc /* The following code is derived from pad.c in libogc
@ -170,98 +153,79 @@ void DolphinSmashAdapter::deviceDisconnected()
static int16_t pad_clampregion[8] = {30, 180, 15, 72, 40, 15, 59, 31}; static int16_t pad_clampregion[8] = {30, 180, 15, 72, 40, 15, 59, 31};
static void pad_clampstick(int16_t& px, int16_t& py, int16_t max, int16_t xy, int16_t min) static void pad_clampstick(int16_t& px, int16_t& py, int16_t max, int16_t xy, int16_t min) {
{ int x = px;
int x = px; int y = py;
int y = py; int signX;
int signX; int signY;
int signY; int d;
int d;
if (x > 0) if (x > 0) {
{ signX = 1;
signX = 1; } else {
} signX = -1;
else x = -x;
{ }
signX = -1;
x = -x;
}
if (y > 0) if (y > 0) {
{ signY = 1;
signY = 1; } else {
} signY = -1;
else y = -y;
{ }
signY = -1;
y = -y;
}
if (x <= min) if (x <= min)
x = 0; x = 0;
else else
x -= min; x -= min;
if (y <= min) if (y <= min) {
{ y = 0;
y = 0; } else {
} y -= min;
else }
{
y -= min;
}
if (x == 0 && y == 0) if (x == 0 && y == 0) {
{ px = py = 0;
px = py = 0; return;
return; }
}
if (xy * y <= xy * x) if (xy * y <= xy * x) {
{ d = xy * x + (max - xy) * y;
d = xy * x + (max - xy) * y; if (xy * max < d) {
if (xy * max < d) x = int16_t(xy * max * x / d);
{ y = int16_t(xy * max * y / d);
x = int16_t(xy * max * x / d);
y = int16_t(xy * max * y / d);
}
} }
else } else {
{ d = xy * y + (max - xy) * x;
d = xy * y + (max - xy) * x; if (xy * max < d) {
if (xy * max < d) x = int16_t(xy * max * x / d);
{ y = int16_t(xy * max * y / d);
x = int16_t(xy * max * x / d);
y = int16_t(xy * max * y / d);
}
} }
}
px = int16_t(signX * x); px = int16_t(signX * x);
py = int16_t(signY * y); py = int16_t(signY * y);
} }
static void pad_clamptrigger(int16_t& trigger) static void pad_clamptrigger(int16_t& trigger) {
{ int16_t min, max;
int16_t min, max;
min = pad_clampregion[0]; min = pad_clampregion[0];
max = pad_clampregion[1]; max = pad_clampregion[1];
if (min > trigger) if (min > trigger)
trigger = 0; trigger = 0;
else else {
{ if (max < trigger)
if (max < trigger) trigger = max;
trigger = max; trigger -= min;
trigger -= min; }
}
} }
void DolphinControllerState::clamp() void DolphinControllerState::clamp() {
{ pad_clampstick(m_leftStick[0], m_leftStick[1], pad_clampregion[3], pad_clampregion[4], pad_clampregion[2]);
pad_clampstick(m_leftStick[0], m_leftStick[1], pad_clampregion[3], pad_clampregion[4], pad_clampregion[2]); pad_clampstick(m_rightStick[0], m_rightStick[1], pad_clampregion[6], pad_clampregion[7], pad_clampregion[5]);
pad_clampstick(m_rightStick[0], m_rightStick[1], pad_clampregion[6], pad_clampregion[7], pad_clampregion[5]); pad_clamptrigger(m_analogTriggers[0]);
pad_clamptrigger(m_analogTriggers[0]); pad_clamptrigger(m_analogTriggers[1]);
pad_clamptrigger(m_analogTriggers[1]);
}
} }
} // namespace boo

View File

@ -7,54 +7,40 @@
#include <memory.h> #include <memory.h>
#ifdef _WIN32 #ifdef _WIN32
static inline uint16_t bswap16(uint16_t val) {return _byteswap_ushort(val);} static inline uint16_t bswap16(uint16_t val) { return _byteswap_ushort(val); }
#elif __GNUC__ && !defined(__FreeBSD__) #elif __GNUC__ && !defined(__FreeBSD__)
static inline uint16_t bswap16(uint16_t val) {return __builtin_bswap16(val); } static inline uint16_t bswap16(uint16_t val) { return __builtin_bswap16(val); }
#elif !defined(__FreeBSD__) #elif !defined(__FreeBSD__)
static inline uint16_t bswap16(uint16_t val) {return __builtin_byteswap(val);} static inline uint16_t bswap16(uint16_t val) { return __builtin_byteswap(val); }
#endif #endif
#ifndef M_PIF #ifndef M_PIF
#define M_PIF 3.14159265358979323846f /* pi */ #define M_PIF 3.14159265358979323846f /* pi */
#endif #endif
#define RAD_TO_DEG (180.f/M_PIF) #define RAD_TO_DEG (180.f / M_PIF)
namespace boo namespace boo {
{ static const uint8_t defaultReport[49] = {0x01, 0x01, 0xff, 0x00, 0xff, 0x00, 0xff, 0x80, 0x00, 0x00, 0x00, 0xff,
static const uint8_t defaultReport[49] = { 0x27, 0x10, 0x00, 0x32, 0xff, 0x27, 0x10, 0x00, 0x32, 0xff, 0x27, 0x10,
0x01, 0x00, 0x32, 0xff, 0x27, 0x10, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00};
0x01, 0xff, 0x00, 0xff, 0x00,
0xff, 0x80, 0x00, 0x00, 0x00,
0xff, 0x27, 0x10, 0x00, 0x32,
0xff, 0x27, 0x10, 0x00, 0x32,
0xff, 0x27, 0x10, 0x00, 0x32,
0xff, 0x27, 0x10, 0x00, 0x32,
0x00, 0x00, 0x00, 0x00, 0x00
};
DualshockPad::DualshockPad(DeviceToken* token) DualshockPad::DualshockPad(DeviceToken* token)
: TDeviceBase<IDualshockPadCallback>(dev_typeid(DualshockPad), token), : TDeviceBase<IDualshockPadCallback>(dev_typeid(DualshockPad), token)
m_rumbleRequest(EDualshockMotor::None), , m_rumbleRequest(EDualshockMotor::None)
m_rumbleState(EDualshockMotor::None) , m_rumbleState(EDualshockMotor::None) {
{ memcpy(m_report.buf, defaultReport, 49);
memcpy(m_report.buf, defaultReport, 49);
} }
DualshockPad::~DualshockPad() DualshockPad::~DualshockPad() {}
{
void DualshockPad::deviceDisconnected() {
std::lock_guard<std::mutex> lk(m_callbackLock);
if (m_callback)
m_callback->controllerDisconnected();
} }
void DualshockPad::deviceDisconnected() void DualshockPad::initialCycle() {
{
std::lock_guard<std::mutex> lk(m_callbackLock);
if (m_callback)
m_callback->controllerDisconnected();
}
void DualshockPad::initialCycle()
{
#if 0 #if 0
uint8_t setupCommand[5] = {0xF4, 0x42, 0x0c, 0x00, 0x00}; //Tells controller to start sending changes on in pipe uint8_t setupCommand[5] = {0xF4, 0x42, 0x0c, 0x00, 0x00}; //Tells controller to start sending changes on in pipe
if (!sendHIDReport(setupCommand, 5, HIDReportType::Feature, 0xF4)) if (!sendHIDReport(setupCommand, 5, HIDReportType::Feature, 0xF4))
@ -69,77 +55,64 @@ void DualshockPad::initialCycle()
#endif #endif
} }
void DualshockPad::transferCycle() void DualshockPad::transferCycle() {}
{
void DualshockPad::finalCycle() {
m_report.rumble.leftDuration = 0;
m_report.rumble.leftForce = 0;
m_report.rumble.rightDuration = 0;
m_report.rumble.rightOn = false;
sendHIDReport(m_report.buf, sizeof(m_report), HIDReportType::Output, 0x01);
} }
void DualshockPad::finalCycle() void DualshockPad::receivedHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message) {
{ if (message != 1 || length != 49 || tp != HIDReportType::Input)
m_report.rumble.leftDuration = 0; return;
m_report.rumble.leftForce = 0; DualshockPadState state = *reinterpret_cast<const DualshockPadState*>(data);
m_report.rumble.rightDuration = 0;
m_report.rumble.rightOn = false; for (int i = 0; i < 3; i++)
state.m_accelerometer[i] = bswap16(state.m_accelerometer[i]);
state.m_gyrometerZ = bswap16(state.m_gyrometerZ);
const double zeroG = 511.5; // 1.65/3.3*1023 (1,65V);
float accXval = -((double)state.m_accelerometer[0] - zeroG);
float accYval = -((double)state.m_accelerometer[1] - zeroG);
float accZval = -((double)state.m_accelerometer[2] - zeroG);
state.accPitch = (atan2(accYval, accZval) + M_PIF) * RAD_TO_DEG;
state.accYaw = (atan2(accXval, accZval) + M_PIF) * RAD_TO_DEG;
state.gyroZ = (state.m_gyrometerZ / 1023.f);
{
std::lock_guard<std::mutex> lk(m_callbackLock);
if (m_callback)
m_callback->controllerUpdate(*this, state);
}
if (m_rumbleRequest != m_rumbleState) {
if ((m_rumbleRequest & EDualshockMotor::Left) != EDualshockMotor::None) {
m_report.rumble.leftDuration = m_rumbleDuration[0];
m_report.rumble.leftForce = m_rumbleIntensity[0];
} else {
m_report.rumble.leftDuration = 0;
m_report.rumble.leftForce = 0;
}
if ((m_rumbleRequest & EDualshockMotor::Right) != EDualshockMotor::None) {
m_report.rumble.rightDuration = m_rumbleDuration[1];
m_report.rumble.rightOn = m_rumbleIntensity[1] > 0;
} else {
m_report.rumble.rightDuration = 0;
m_report.rumble.rightOn = false;
}
sendHIDReport(m_report.buf, sizeof(m_report), HIDReportType::Output, 0x01); sendHIDReport(m_report.buf, sizeof(m_report), HIDReportType::Output, 0x01);
m_rumbleState = m_rumbleRequest;
} else {
if (state.m_reserved5[8] & 0x80)
m_rumbleRequest &= ~EDualshockMotor::Right;
if (state.m_reserved5[7] & 0x01)
m_rumbleRequest &= ~EDualshockMotor::Left;
m_rumbleState = m_rumbleRequest;
}
} }
void DualshockPad::receivedHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message) } // namespace boo
{
if (message != 1 || length != 49 || tp != HIDReportType::Input)
return;
DualshockPadState state = *reinterpret_cast<const DualshockPadState*>(data);
for (int i = 0; i < 3; i++)
state.m_accelerometer[i] = bswap16(state.m_accelerometer[i]);
state.m_gyrometerZ = bswap16(state.m_gyrometerZ);
const double zeroG = 511.5; // 1.65/3.3*1023 (1,65V);
float accXval = -((double)state.m_accelerometer[0] - zeroG);
float accYval = -((double)state.m_accelerometer[1] - zeroG);
float accZval = -((double)state.m_accelerometer[2] - zeroG);
state.accPitch = (atan2(accYval, accZval) + M_PIF) * RAD_TO_DEG;
state.accYaw = (atan2(accXval, accZval) + M_PIF) * RAD_TO_DEG;
state.gyroZ = (state.m_gyrometerZ / 1023.f);
{
std::lock_guard<std::mutex> lk(m_callbackLock);
if (m_callback)
m_callback->controllerUpdate(*this, state);
}
if (m_rumbleRequest != m_rumbleState)
{
if ((m_rumbleRequest & EDualshockMotor::Left) != EDualshockMotor::None)
{
m_report.rumble.leftDuration = m_rumbleDuration[0];
m_report.rumble.leftForce = m_rumbleIntensity[0];
}
else
{
m_report.rumble.leftDuration = 0;
m_report.rumble.leftForce = 0;
}
if ((m_rumbleRequest & EDualshockMotor::Right) != EDualshockMotor::None)
{
m_report.rumble.rightDuration = m_rumbleDuration[1];
m_report.rumble.rightOn = m_rumbleIntensity[1] > 0;
}
else
{
m_report.rumble.rightDuration = 0;
m_report.rumble.rightOn = false;
}
sendHIDReport(m_report.buf, sizeof(m_report), HIDReportType::Output, 0x01);
m_rumbleState = m_rumbleRequest;
}
else
{
if (state.m_reserved5[8] & 0x80)
m_rumbleRequest &= ~EDualshockMotor::Right;
if (state.m_reserved5[7] & 0x01)
m_rumbleRequest &= ~EDualshockMotor::Left;
m_rumbleState = m_rumbleRequest;
}
}
} // boo

View File

@ -1,57 +1,46 @@
#include "boo/inputdev/GenericPad.hpp" #include "boo/inputdev/GenericPad.hpp"
#include "boo/inputdev/DeviceToken.hpp" #include "boo/inputdev/DeviceToken.hpp"
namespace boo namespace boo {
{
GenericPad::GenericPad(DeviceToken* token) GenericPad::GenericPad(DeviceToken* token) : TDeviceBase<IGenericPadCallback>(dev_typeid(GenericPad), token) {}
: TDeviceBase<IGenericPadCallback>(dev_typeid(GenericPad), token)
{
}
GenericPad::~GenericPad() {} GenericPad::~GenericPad() {}
void GenericPad::deviceDisconnected() void GenericPad::deviceDisconnected() {
{ std::lock_guard<std::mutex> lk(m_callbackLock);
std::lock_guard<std::mutex> lk(m_callbackLock); if (m_callback)
if (m_callback) m_callback->controllerDisconnected();
m_callback->controllerDisconnected();
} }
void GenericPad::initialCycle() void GenericPad::initialCycle() {
{
#if _WIN32 #if _WIN32
#if !WINDOWS_STORE #if !WINDOWS_STORE
const PHIDP_PREPARSED_DATA reportDesc = getReportDescriptor(); const PHIDP_PREPARSED_DATA reportDesc = getReportDescriptor();
m_parser.Parse(reportDesc); m_parser.Parse(reportDesc);
#endif #endif
#else #else
std::vector<uint8_t> reportDesc = getReportDescriptor(); std::vector<uint8_t> reportDesc = getReportDescriptor();
m_parser.Parse(reportDesc.data(), reportDesc.size()); m_parser.Parse(reportDesc.data(), reportDesc.size());
#endif #endif
std::lock_guard<std::mutex> lk(m_callbackLock); std::lock_guard<std::mutex> lk(m_callbackLock);
if (m_callback) if (m_callback)
m_callback->controllerConnected(); m_callback->controllerConnected();
} }
void GenericPad::receivedHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message) void GenericPad::receivedHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message) {
{ std::lock_guard<std::mutex> lk(m_callbackLock);
std::lock_guard<std::mutex> lk(m_callbackLock); if (length == 0 || tp != HIDReportType::Input || !m_callback)
if (length == 0 || tp != HIDReportType::Input || !m_callback) return;
return; std::function<bool(const HIDMainItem&, int32_t)> func = [this](const HIDMainItem& item, int32_t value) {
std::function<bool(const HIDMainItem&, int32_t)> func = m_callback->valueUpdate(item, value);
[this](const HIDMainItem& item, int32_t value) return true;
{ };
m_callback->valueUpdate(item, value); m_parser.ScanValues(func, data, length);
return true;
};
m_parser.ScanValues(func, data, length);
} }
void GenericPad::enumerateValues(const std::function<bool(const HIDMainItem& item)>& valueCB) const void GenericPad::enumerateValues(const std::function<bool(const HIDMainItem& item)>& valueCB) const {
{ m_parser.EnumerateValues(valueCB);
m_parser.EnumerateValues(valueCB);
} }
} } // namespace boo

View File

@ -1,31 +1,21 @@
#include "boo/inputdev/IHIDListener.hpp" #include "boo/inputdev/IHIDListener.hpp"
#include "boo/inputdev/DeviceFinder.hpp" #include "boo/inputdev/DeviceFinder.hpp"
namespace boo {
namespace boo class HIDListenerBSD final : public IHIDListener {
{ DeviceFinder& m_finder;
class HIDListenerBSD final : public IHIDListener
{
DeviceFinder& m_finder;
public: public:
HIDListenerBSD(DeviceFinder& finder) HIDListenerBSD(DeviceFinder& finder) : m_finder(finder) {}
: m_finder(finder)
{
}
~HIDListenerBSD() ~HIDListenerBSD() {}
{
}
bool startScanning() { return false; } bool startScanning() { return false; }
bool stopScanning() { return false; } bool stopScanning() { return false; }
bool scanNow() { return false; } bool scanNow() { return false; }
}; };
IHIDListener* IHIDListenerNew(DeviceFinder &finder) IHIDListener* IHIDListenerNew(DeviceFinder& finder) { return new HIDListenerBSD(finder); }
{ } // namespace boo
return new HIDListenerBSD(finder);
}
}

View File

@ -5,380 +5,320 @@
#include "IOKitPointer.hpp" #include "IOKitPointer.hpp"
#include <thread> #include <thread>
namespace boo namespace boo {
{
class HIDDeviceIOKit : public IHIDDevice class HIDDeviceIOKit : public IHIDDevice {
{ DeviceToken& m_token;
DeviceToken& m_token; std::shared_ptr<DeviceBase> m_devImp;
std::shared_ptr<DeviceBase> m_devImp;
IUnknownPointer<IOUSBInterfaceInterface> m_usbIntf; IUnknownPointer<IOUSBInterfaceInterface> m_usbIntf;
uint8_t m_usbIntfInPipe = 0; uint8_t m_usbIntfInPipe = 0;
uint8_t m_usbIntfOutPipe = 0; uint8_t m_usbIntfOutPipe = 0;
CFPointer<IOHIDDeviceRef> m_hidIntf; CFPointer<IOHIDDeviceRef> m_hidIntf;
bool m_runningTransferLoop = false; bool m_runningTransferLoop = false;
bool m_isBt = false; bool m_isBt = false;
std::string_view m_devPath; std::string_view m_devPath;
std::mutex m_initMutex; std::mutex m_initMutex;
std::condition_variable m_initCond; std::condition_variable m_initCond;
std::thread m_thread; std::thread m_thread;
bool _sendUSBInterruptTransfer(const uint8_t* data, size_t length) bool _sendUSBInterruptTransfer(const uint8_t* data, size_t length) {
{ if (m_usbIntf) {
if (m_usbIntf) IOReturn res = m_usbIntf->WritePipe(m_usbIntf.storage(), m_usbIntfOutPipe, (void*)data, length);
{ return res == kIOReturnSuccess;
IOReturn res = m_usbIntf->WritePipe(m_usbIntf.storage(), m_usbIntfOutPipe, (void*)data, length);
return res == kIOReturnSuccess;
}
return false;
} }
return false;
}
size_t _receiveUSBInterruptTransfer(uint8_t* data, size_t length) size_t _receiveUSBInterruptTransfer(uint8_t* data, size_t length) {
{ if (m_usbIntf) {
if (m_usbIntf) UInt32 readSize = length;
{ IOReturn res = m_usbIntf->ReadPipe(m_usbIntf.storage(), m_usbIntfInPipe, data, &readSize);
UInt32 readSize = length; if (res != kIOReturnSuccess)
IOReturn res = m_usbIntf->ReadPipe(m_usbIntf.storage(), m_usbIntfInPipe, data, &readSize);
if (res != kIOReturnSuccess)
return 0;
return readSize;
}
return 0; return 0;
return readSize;
} }
return 0;
}
std::vector<uint8_t> _getReportDescriptor() std::vector<uint8_t> _getReportDescriptor() {
{ if (m_hidIntf) {
if (m_hidIntf) if (CFTypeRef desc = IOHIDDeviceGetProperty(m_hidIntf.get(), CFSTR(kIOHIDReportDescriptorKey))) {
{ CFIndex len = CFDataGetLength(CFDataRef(desc));
if (CFTypeRef desc = IOHIDDeviceGetProperty(m_hidIntf.get(), CFSTR(kIOHIDReportDescriptorKey))) std::vector<uint8_t> ret(len, '\0');
{ CFDataGetBytes(CFDataRef(desc), CFRangeMake(0, len), &ret[0]);
CFIndex len = CFDataGetLength(CFDataRef(desc)); return ret;
std::vector<uint8_t> ret(len, '\0'); }
CFDataGetBytes(CFDataRef(desc), CFRangeMake(0, len), &ret[0]);
return ret;
}
}
return {};
} }
return {};
}
bool _sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message) bool _sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message) {
{ /* HACK: A bug in IOBluetoothGamepadHIDDriver prevents raw output report transmission
/* HACK: A bug in IOBluetoothGamepadHIDDriver prevents raw output report transmission * USB driver appears to work correctly */
* USB driver appears to work correctly */ if (m_hidIntf && !m_isBt) {
if (m_hidIntf && !m_isBt) IOReturn res = IOHIDDeviceSetReport(m_hidIntf.get(), IOHIDReportType(tp), message, data, length);
{ return res == kIOReturnSuccess;
IOReturn res = IOHIDDeviceSetReport(m_hidIntf.get(), IOHIDReportType(tp), message, data, length);
return res == kIOReturnSuccess;
}
return false;
} }
return false;
}
size_t _receiveHIDReport(uint8_t* data, size_t length, HIDReportType tp, uint32_t message) size_t _receiveHIDReport(uint8_t* data, size_t length, HIDReportType tp, uint32_t message) {
{ if (m_hidIntf) {
if (m_hidIntf) CFIndex readSize = length;
{ IOReturn res = IOHIDDeviceGetReport(m_hidIntf.get(), IOHIDReportType(tp), message, data, &readSize);
CFIndex readSize = length; if (res != kIOReturnSuccess)
IOReturn res = IOHIDDeviceGetReport(m_hidIntf.get(), IOHIDReportType(tp), message, data, &readSize);
if (res != kIOReturnSuccess)
return 0;
return readSize;
}
return 0; return 0;
return readSize;
}
return 0;
}
static void _threadProcUSBLL(std::shared_ptr<HIDDeviceIOKit> device) {
char thrName[128];
snprintf(thrName, 128, "%s Transfer Thread", device->m_token.getProductName().data());
pthread_setname_np(thrName);
char errStr[256];
std::unique_lock<std::mutex> lk(device->m_initMutex);
/* Get the HID element's parent (USB interrupt transfer-interface) */
IOObjectPointer<io_iterator_t> devIter;
IOObjectPointer<io_registry_entry_t> devEntry =
IORegistryEntryFromPath(kIOMasterPortDefault, device->m_devPath.data());
IOObjectPointer<io_object_t> interfaceEntry;
IORegistryEntryGetChildIterator(devEntry.get(), kIOServicePlane, &devIter);
while (IOObjectPointer<io_service_t> obj = IOIteratorNext(devIter.get())) {
if (IOObjectConformsTo(obj.get(), kIOUSBInterfaceClassName)) {
interfaceEntry = obj;
break;
}
}
if (!interfaceEntry) {
snprintf(errStr, 256, "Unable to find interface for %s@%s\n", device->m_token.getProductName().data(),
device->m_devPath.data());
device->m_devImp->deviceError(errStr);
lk.unlock();
device->m_initCond.notify_one();
return;
} }
static void _threadProcUSBLL(std::shared_ptr<HIDDeviceIOKit> device) /* IOKit Plugin COM interface (WTF Apple???) */
{ IOCFPluginPointer iodev;
char thrName[128]; SInt32 score;
snprintf(thrName, 128, "%s Transfer Thread", device->m_token.getProductName().data()); IOReturn err;
pthread_setname_np(thrName); err = IOCreatePlugInInterfaceForService(interfaceEntry.get(), kIOUSBInterfaceUserClientTypeID,
char errStr[256]; kIOCFPlugInInterfaceID, &iodev, &score);
std::unique_lock<std::mutex> lk(device->m_initMutex); if (err) {
snprintf(errStr, 256, "Unable to open %s@%s\n", device->m_token.getProductName().data(),
/* Get the HID element's parent (USB interrupt transfer-interface) */ device->m_devPath.data());
IOObjectPointer<io_iterator_t> devIter; device->m_devImp->deviceError(errStr);
IOObjectPointer<io_registry_entry_t> devEntry = IORegistryEntryFromPath(kIOMasterPortDefault, lk.unlock();
device->m_devPath.data()); device->m_initCond.notify_one();
IOObjectPointer<io_object_t> interfaceEntry; return;
IORegistryEntryGetChildIterator(devEntry.get(), kIOServicePlane, &devIter);
while (IOObjectPointer<io_service_t> obj = IOIteratorNext(devIter.get()))
{
if (IOObjectConformsTo(obj.get(), kIOUSBInterfaceClassName))
{
interfaceEntry = obj;
break;
}
}
if (!interfaceEntry)
{
snprintf(errStr, 256, "Unable to find interface for %s@%s\n",
device->m_token.getProductName().data(),
device->m_devPath.data());
device->m_devImp->deviceError(errStr);
lk.unlock();
device->m_initCond.notify_one();
return;
}
/* IOKit Plugin COM interface (WTF Apple???) */
IOCFPluginPointer iodev;
SInt32 score;
IOReturn err;
err = IOCreatePlugInInterfaceForService(interfaceEntry.get(),
kIOUSBInterfaceUserClientTypeID,
kIOCFPlugInInterfaceID,
&iodev,
&score);
if (err)
{
snprintf(errStr, 256, "Unable to open %s@%s\n",
device->m_token.getProductName().data(), device->m_devPath.data());
device->m_devImp->deviceError(errStr);
lk.unlock();
device->m_initCond.notify_one();
return;
}
/* USB interface function-pointer table */
IUnknownPointer<IOUSBInterfaceInterface> intf;
err = iodev.As(&intf, kIOUSBInterfaceInterfaceID);
if (err)
{
snprintf(errStr, 256, "Unable to open %s@%s\n",
device->m_token.getProductName().data(), device->m_devPath.data());
device->m_devImp->deviceError(errStr);
lk.unlock();
device->m_initCond.notify_one();
return;
}
/* Obtain exclusive lock on device */
device->m_usbIntf = intf;
err = intf->USBInterfaceOpen(intf.storage());
if (err != kIOReturnSuccess)
{
if (err == kIOReturnExclusiveAccess)
{
snprintf(errStr, 256, "Unable to open %s@%s: someone else using it\n",
device->m_token.getProductName().data(), device->m_devPath.data());
device->m_devImp->deviceError(errStr);
}
else
{
snprintf(errStr, 256, "Unable to open %s@%s\n",
device->m_token.getProductName().data(), device->m_devPath.data());
device->m_devImp->deviceError(errStr);
}
lk.unlock();
device->m_initCond.notify_one();
return;
}
/* Determine pipe indices for interrupt I/O */
UInt8 numEndpoints = 0;
err = intf->GetNumEndpoints(intf.storage(), &numEndpoints);
for (int i=1 ; i<numEndpoints+1 ; ++i)
{
UInt8 dir, num, tType, interval;
UInt16 mPacketSz;
err = intf->GetPipeProperties(intf.storage(), i, &dir, &num, &tType, &mPacketSz, &interval);
if (tType == kUSBInterrupt)
{
if (dir == kUSBIn)
device->m_usbIntfInPipe = num;
else if (dir == kUSBOut)
device->m_usbIntfOutPipe = num;
}
}
/* Return control to main thread */
device->m_runningTransferLoop = true;
lk.unlock();
device->m_initCond.notify_one();
/* Start transfer loop */
device->m_devImp->initialCycle();
while (device->m_runningTransferLoop)
device->m_devImp->transferCycle();
device->m_devImp->finalCycle();
/* Cleanup */
intf->USBInterfaceClose(intf.storage());
device->m_usbIntf = nullptr;
} }
static void _threadProcBTLL(std::shared_ptr<HIDDeviceIOKit> device) /* USB interface function-pointer table */
{ IUnknownPointer<IOUSBInterfaceInterface> intf;
std::unique_lock<std::mutex> lk(device->m_initMutex); err = iodev.As(&intf, kIOUSBInterfaceInterfaceID);
if (err) {
/* Return control to main thread */ snprintf(errStr, 256, "Unable to open %s@%s\n", device->m_token.getProductName().data(),
device->m_runningTransferLoop = true; device->m_devPath.data());
lk.unlock(); device->m_devImp->deviceError(errStr);
device->m_initCond.notify_one(); lk.unlock();
device->m_initCond.notify_one();
/* Start transfer loop */ return;
device->m_devImp->initialCycle();
while (device->m_runningTransferLoop)
device->m_devImp->transferCycle();
device->m_devImp->finalCycle();
} }
static void _hidRemoveCb(void * _Nullable context, /* Obtain exclusive lock on device */
IOReturn result, device->m_usbIntf = intf;
void * _Nullable sender) err = intf->USBInterfaceOpen(intf.storage());
{ if (err != kIOReturnSuccess) {
reinterpret_cast<HIDDeviceIOKit*>(context)->m_runningTransferLoop = false; if (err == kIOReturnExclusiveAccess) {
snprintf(errStr, 256, "Unable to open %s@%s: someone else using it\n", device->m_token.getProductName().data(),
device->m_devPath.data());
device->m_devImp->deviceError(errStr);
} else {
snprintf(errStr, 256, "Unable to open %s@%s\n", device->m_token.getProductName().data(),
device->m_devPath.data());
device->m_devImp->deviceError(errStr);
}
lk.unlock();
device->m_initCond.notify_one();
return;
} }
static void _hidReportCb(void * _Nullable context, /* Determine pipe indices for interrupt I/O */
IOReturn, UInt8 numEndpoints = 0;
void * _Nullable, err = intf->GetNumEndpoints(intf.storage(), &numEndpoints);
IOHIDReportType type, for (int i = 1; i < numEndpoints + 1; ++i) {
uint32_t reportID, UInt8 dir, num, tType, interval;
uint8_t * report, UInt16 mPacketSz;
CFIndex reportLength) err = intf->GetPipeProperties(intf.storage(), i, &dir, &num, &tType, &mPacketSz, &interval);
{ if (tType == kUSBInterrupt) {
reinterpret_cast<DeviceBase*>(context)->receivedHIDReport(report, reportLength, HIDReportType(type), reportID); if (dir == kUSBIn)
device->m_usbIntfInPipe = num;
else if (dir == kUSBOut)
device->m_usbIntfOutPipe = num;
}
} }
static void _threadProcHID(std::shared_ptr<HIDDeviceIOKit> device) /* Return control to main thread */
{ device->m_runningTransferLoop = true;
char thrName[128]; lk.unlock();
snprintf(thrName, 128, "%s Transfer Thread", device->m_token.getProductName().data()); device->m_initCond.notify_one();
pthread_setname_np(thrName);
char errStr[256];
std::unique_lock<std::mutex> lk(device->m_initMutex);
/* Get the HID element's object (HID device interface) */ /* Start transfer loop */
IOObjectPointer<io_service_t> interfaceEntry = device->m_devImp->initialCycle();
IORegistryEntryFromPath(kIOMasterPortDefault, device->m_devPath.data()); while (device->m_runningTransferLoop)
if (!IOObjectConformsTo(interfaceEntry.get(), "IOHIDDevice")) device->m_devImp->transferCycle();
{ device->m_devImp->finalCycle();
snprintf(errStr, 256, "Unable to find interface for %s@%s\n",
device->m_token.getProductName().data(),
device->m_devPath.data());
device->m_devImp->deviceError(errStr);
lk.unlock();
device->m_initCond.notify_one();
return;
}
device->m_hidIntf = IOHIDDeviceCreate(nullptr, interfaceEntry.get()); /* Cleanup */
if (!device->m_hidIntf) intf->USBInterfaceClose(intf.storage());
{ device->m_usbIntf = nullptr;
snprintf(errStr, 256, "Unable to open %s@%s\n", }
device->m_token.getProductName().data(), device->m_devPath.data());
device->m_devImp->deviceError(errStr);
lk.unlock();
device->m_initCond.notify_one();
return;
}
/* Open device */ static void _threadProcBTLL(std::shared_ptr<HIDDeviceIOKit> device) {
IOReturn err = IOHIDDeviceOpen(device->m_hidIntf.get(), kIOHIDOptionsTypeNone); std::unique_lock<std::mutex> lk(device->m_initMutex);
if (err != kIOReturnSuccess)
{
if (err == kIOReturnExclusiveAccess)
{
snprintf(errStr, 256, "Unable to open %s@%s: someone else using it\n",
device->m_token.getProductName().data(), device->m_devPath.data());
device->m_devImp->deviceError(errStr);
}
else
{
snprintf(errStr, 256, "Unable to open %s@%s\n",
device->m_token.getProductName().data(), device->m_devPath.data());
device->m_devImp->deviceError(errStr);
}
lk.unlock();
device->m_initCond.notify_one();
return;
}
/* Register removal callback */ /* Return control to main thread */
IOHIDDeviceRegisterRemovalCallback(device->m_hidIntf.get(), _hidRemoveCb, device.get()); device->m_runningTransferLoop = true;
lk.unlock();
device->m_initCond.notify_one();
/* Make note if device uses bluetooth driver */ /* Start transfer loop */
if (CFTypeRef transport = IOHIDDeviceGetProperty(device->m_hidIntf.get(), CFSTR(kIOHIDTransportKey))) device->m_devImp->initialCycle();
device->m_isBt = CFStringCompare(CFStringRef(transport), CFSTR(kIOHIDTransportBluetoothValue), 0) == kCFCompareEqualTo; while (device->m_runningTransferLoop)
device->m_devImp->transferCycle();
device->m_devImp->finalCycle();
}
/* Register input buffer */ static void _hidRemoveCb(void* _Nullable context, IOReturn result, void* _Nullable sender) {
std::unique_ptr<uint8_t[]> buffer; reinterpret_cast<HIDDeviceIOKit*>(context)->m_runningTransferLoop = false;
int bufSize = 0; }
if (CFTypeRef maxSize = IOHIDDeviceGetProperty(device->m_hidIntf.get(), CFSTR(kIOHIDMaxInputReportSizeKey)))
CFNumberGetValue(CFNumberRef(maxSize), kCFNumberIntType, &bufSize);
if (bufSize)
{
buffer = std::unique_ptr<uint8_t[]>(new uint8_t[bufSize]);
IOHIDDeviceRegisterInputReportCallback(device->m_hidIntf.get(), buffer.get(), bufSize,
_hidReportCb, device->m_devImp.get());
IOHIDDeviceScheduleWithRunLoop(device->m_hidIntf.get(), CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
}
/* Return control to main thread */ static void _hidReportCb(void* _Nullable context, IOReturn, void* _Nullable, IOHIDReportType type, uint32_t reportID,
device->m_runningTransferLoop = true; uint8_t* report, CFIndex reportLength) {
lk.unlock(); reinterpret_cast<DeviceBase*>(context)->receivedHIDReport(report, reportLength, HIDReportType(type), reportID);
device->m_initCond.notify_one(); }
/* Start transfer loop */ static void _threadProcHID(std::shared_ptr<HIDDeviceIOKit> device) {
device->m_devImp->initialCycle(); char thrName[128];
while (device->m_runningTransferLoop) snprintf(thrName, 128, "%s Transfer Thread", device->m_token.getProductName().data());
{ pthread_setname_np(thrName);
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.010, true); char errStr[256];
if (device->m_runningTransferLoop) std::unique_lock<std::mutex> lk(device->m_initMutex);
device->m_devImp->transferCycle();
}
device->m_devImp->finalCycle();
/* Cleanup */ /* Get the HID element's object (HID device interface) */
IOHIDDeviceClose(device->m_hidIntf.get(), kIOHIDOptionsTypeNone); IOObjectPointer<io_service_t> interfaceEntry =
device->m_hidIntf.reset(); IORegistryEntryFromPath(kIOMasterPortDefault, device->m_devPath.data());
if (!IOObjectConformsTo(interfaceEntry.get(), "IOHIDDevice")) {
snprintf(errStr, 256, "Unable to find interface for %s@%s\n", device->m_token.getProductName().data(),
device->m_devPath.data());
device->m_devImp->deviceError(errStr);
lk.unlock();
device->m_initCond.notify_one();
return;
} }
void _deviceDisconnected() device->m_hidIntf = IOHIDDeviceCreate(nullptr, interfaceEntry.get());
{ if (!device->m_hidIntf) {
m_runningTransferLoop = false; snprintf(errStr, 256, "Unable to open %s@%s\n", device->m_token.getProductName().data(),
device->m_devPath.data());
device->m_devImp->deviceError(errStr);
lk.unlock();
device->m_initCond.notify_one();
return;
} }
/* Open device */
IOReturn err = IOHIDDeviceOpen(device->m_hidIntf.get(), kIOHIDOptionsTypeNone);
if (err != kIOReturnSuccess) {
if (err == kIOReturnExclusiveAccess) {
snprintf(errStr, 256, "Unable to open %s@%s: someone else using it\n", device->m_token.getProductName().data(),
device->m_devPath.data());
device->m_devImp->deviceError(errStr);
} else {
snprintf(errStr, 256, "Unable to open %s@%s\n", device->m_token.getProductName().data(),
device->m_devPath.data());
device->m_devImp->deviceError(errStr);
}
lk.unlock();
device->m_initCond.notify_one();
return;
}
/* Register removal callback */
IOHIDDeviceRegisterRemovalCallback(device->m_hidIntf.get(), _hidRemoveCb, device.get());
/* Make note if device uses bluetooth driver */
if (CFTypeRef transport = IOHIDDeviceGetProperty(device->m_hidIntf.get(), CFSTR(kIOHIDTransportKey)))
device->m_isBt =
CFStringCompare(CFStringRef(transport), CFSTR(kIOHIDTransportBluetoothValue), 0) == kCFCompareEqualTo;
/* Register input buffer */
std::unique_ptr<uint8_t[]> buffer;
int bufSize = 0;
if (CFTypeRef maxSize = IOHIDDeviceGetProperty(device->m_hidIntf.get(), CFSTR(kIOHIDMaxInputReportSizeKey)))
CFNumberGetValue(CFNumberRef(maxSize), kCFNumberIntType, &bufSize);
if (bufSize) {
buffer = std::unique_ptr<uint8_t[]>(new uint8_t[bufSize]);
IOHIDDeviceRegisterInputReportCallback(device->m_hidIntf.get(), buffer.get(), bufSize, _hidReportCb,
device->m_devImp.get());
IOHIDDeviceScheduleWithRunLoop(device->m_hidIntf.get(), CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
}
/* Return control to main thread */
device->m_runningTransferLoop = true;
lk.unlock();
device->m_initCond.notify_one();
/* Start transfer loop */
device->m_devImp->initialCycle();
while (device->m_runningTransferLoop) {
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.010, true);
if (device->m_runningTransferLoop)
device->m_devImp->transferCycle();
}
device->m_devImp->finalCycle();
/* Cleanup */
IOHIDDeviceClose(device->m_hidIntf.get(), kIOHIDOptionsTypeNone);
device->m_hidIntf.reset();
}
void _deviceDisconnected() { m_runningTransferLoop = false; }
public: public:
HIDDeviceIOKit(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp)
: m_token(token), m_devImp(devImp), m_devPath(token.getDevicePath()) {}
HIDDeviceIOKit(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp) void _startThread() {
: m_token(token), std::unique_lock<std::mutex> lk(m_initMutex);
m_devImp(devImp), DeviceType dType = m_token.getDeviceType();
m_devPath(token.getDevicePath()) if (dType == DeviceType::USB)
{ m_thread = std::thread(_threadProcUSBLL, std::static_pointer_cast<HIDDeviceIOKit>(shared_from_this()));
else if (dType == DeviceType::Bluetooth)
m_thread = std::thread(_threadProcBTLL, std::static_pointer_cast<HIDDeviceIOKit>(shared_from_this()));
else if (dType == DeviceType::HID)
m_thread = std::thread(_threadProcHID, std::static_pointer_cast<HIDDeviceIOKit>(shared_from_this()));
else {
fprintf(stderr, "invalid token supplied to device constructor\n");
return;
} }
m_initCond.wait(lk);
}
void _startThread() ~HIDDeviceIOKit() {
{ m_runningTransferLoop = false;
std::unique_lock<std::mutex> lk(m_initMutex); if (m_thread.joinable())
DeviceType dType = m_token.getDeviceType(); m_thread.detach();
if (dType == DeviceType::USB) }
m_thread = std::thread(_threadProcUSBLL, std::static_pointer_cast<HIDDeviceIOKit>(shared_from_this()));
else if (dType == DeviceType::Bluetooth)
m_thread = std::thread(_threadProcBTLL, std::static_pointer_cast<HIDDeviceIOKit>(shared_from_this()));
else if (dType == DeviceType::HID)
m_thread = std::thread(_threadProcHID, std::static_pointer_cast<HIDDeviceIOKit>(shared_from_this()));
else
{
fprintf(stderr, "invalid token supplied to device constructor\n");
return;
}
m_initCond.wait(lk);
}
~HIDDeviceIOKit()
{
m_runningTransferLoop = false;
if (m_thread.joinable())
m_thread.detach();
}
}; };
std::shared_ptr<IHIDDevice> IHIDDeviceNew(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp) std::shared_ptr<IHIDDevice> IHIDDeviceNew(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp) {
{ return std::make_shared<HIDDeviceIOKit>(token, devImp);
return std::make_shared<HIDDeviceIOKit>(token, devImp);
} }
} } // namespace boo

View File

@ -1,34 +1,27 @@
#include "IHIDDevice.hpp" #include "IHIDDevice.hpp"
namespace boo namespace boo {
{
class HIDDeviceNX : public IHIDDevice class HIDDeviceNX : public IHIDDevice {
{ DeviceToken& m_token;
DeviceToken& m_token; std::shared_ptr<DeviceBase> m_devImp;
std::shared_ptr<DeviceBase> m_devImp; std::string_view m_devPath;
std::string_view m_devPath;
public: public:
HIDDeviceNX(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp) HIDDeviceNX(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp)
: m_token(token), : m_token(token), m_devImp(devImp), m_devPath(token.getDevicePath()) {}
m_devImp(devImp),
m_devPath(token.getDevicePath())
{
}
void _deviceDisconnected() {} void _deviceDisconnected() {}
bool _sendUSBInterruptTransfer(const uint8_t* data, size_t length) { return false; } bool _sendUSBInterruptTransfer(const uint8_t* data, size_t length) { return false; }
size_t _receiveUSBInterruptTransfer(uint8_t* data, size_t length) { return 0; } size_t _receiveUSBInterruptTransfer(uint8_t* data, size_t length) { return 0; }
std::vector<uint8_t> _getReportDescriptor() { return {}; } std::vector<uint8_t> _getReportDescriptor() { return {}; }
bool _sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message) { return false; } bool _sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message) { return false; }
size_t _receiveHIDReport(uint8_t* data, size_t length, HIDReportType tp, uint32_t message) { return 0; } size_t _receiveHIDReport(uint8_t* data, size_t length, HIDReportType tp, uint32_t message) { return 0; }
void _startThread() {} void _startThread() {}
}; };
std::shared_ptr<IHIDDevice> IHIDDeviceNew(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp) std::shared_ptr<IHIDDevice> IHIDDeviceNew(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp) {
{ return std::make_shared<HIDDeviceNX>(token, devImp);
return std::make_shared<HIDDeviceNX>(token, devImp);
} }
} } // namespace boo

View File

@ -3,28 +3,22 @@
#include "boo/inputdev/DeviceToken.hpp" #include "boo/inputdev/DeviceToken.hpp"
#include "boo/inputdev/DeviceBase.hpp" #include "boo/inputdev/DeviceBase.hpp"
namespace boo namespace boo {
{
class HIDDeviceUWP : public IHIDDevice class HIDDeviceUWP : public IHIDDevice {
{
public: public:
HIDDeviceUWP(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp) HIDDeviceUWP(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp) {}
{
} void _deviceDisconnected() {}
bool _sendUSBInterruptTransfer(const uint8_t* data, size_t length) { return false; }
void _deviceDisconnected() {} size_t _receiveUSBInterruptTransfer(uint8_t* data, size_t length) { return 0; }
bool _sendUSBInterruptTransfer(const uint8_t* data, size_t length) { return false; } bool _sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message) { return false; }
size_t _receiveUSBInterruptTransfer(uint8_t* data, size_t length) { return 0; } size_t _receiveHIDReport(uint8_t* data, size_t length, HIDReportType tp, uint32_t message) { return false; }
bool _sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message) { return false; } void _startThread() {}
size_t _receiveHIDReport(uint8_t* data, size_t length, HIDReportType tp, uint32_t message) { return false; }
void _startThread() {}
}; };
std::shared_ptr<IHIDDevice> IHIDDeviceNew(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp) std::shared_ptr<IHIDDevice> IHIDDeviceNew(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp) {
{ return std::make_shared<HIDDeviceUWP>(token, devImp);
return std::make_shared<HIDDeviceUWP>(token, devImp);
} }
} } // namespace boo

View File

@ -18,8 +18,7 @@
#include <cstring> #include <cstring>
#include "boo/inputdev/HIDParser.hpp" #include "boo/inputdev/HIDParser.hpp"
namespace boo namespace boo {
{
udev* GetUdev(); udev* GetUdev();
@ -27,331 +26,270 @@ udev* GetUdev();
* Reference: http://tali.admingilde.org/linux-docbook/usb/ch07s06.html * Reference: http://tali.admingilde.org/linux-docbook/usb/ch07s06.html
*/ */
class HIDDeviceUdev final : public IHIDDevice class HIDDeviceUdev final : public IHIDDevice {
{ DeviceToken& m_token;
DeviceToken& m_token; std::shared_ptr<DeviceBase> m_devImp;
std::shared_ptr<DeviceBase> m_devImp;
int m_devFd = 0; int m_devFd = 0;
unsigned m_usbIntfInPipe = 0; unsigned m_usbIntfInPipe = 0;
unsigned m_usbIntfOutPipe = 0; unsigned m_usbIntfOutPipe = 0;
bool m_runningTransferLoop = false; bool m_runningTransferLoop = false;
std::string_view m_devPath; std::string_view m_devPath;
std::mutex m_initMutex; std::mutex m_initMutex;
std::condition_variable m_initCond; std::condition_variable m_initCond;
std::thread m_thread; std::thread m_thread;
bool _sendUSBInterruptTransfer(const uint8_t* data, size_t length) bool _sendUSBInterruptTransfer(const uint8_t* data, size_t length) {
{ if (m_devFd) {
if (m_devFd) usbdevfs_bulktransfer xfer = {m_usbIntfOutPipe | USB_DIR_OUT, (unsigned)length, 30, (void*)data};
{ int ret = ioctl(m_devFd, USBDEVFS_BULK, &xfer);
usbdevfs_bulktransfer xfer = if (ret != (int)length)
{
m_usbIntfOutPipe | USB_DIR_OUT,
(unsigned)length,
30,
(void*)data
};
int ret = ioctl(m_devFd, USBDEVFS_BULK, &xfer);
if (ret != (int)length)
return false;
return true;
}
return false; return false;
return true;
}
return false;
}
size_t _receiveUSBInterruptTransfer(uint8_t* data, size_t length) {
if (m_devFd) {
usbdevfs_bulktransfer xfer = {m_usbIntfInPipe | USB_DIR_IN, (unsigned)length, 30, data};
return ioctl(m_devFd, USBDEVFS_BULK, &xfer);
}
return 0;
}
static void _threadProcUSBLL(std::shared_ptr<HIDDeviceUdev> device) {
int i;
char errStr[256];
std::unique_lock<std::mutex> lk(device->m_initMutex);
udev_device* udevDev = udev_device_new_from_syspath(GetUdev(), device->m_devPath.data());
/* Get device file */
const char* dp = udev_device_get_devnode(udevDev);
int fd = open(dp, O_RDWR);
if (fd < 0) {
snprintf(errStr, 256, "Unable to open %s@%s: %s\n", device->m_token.getProductName().data(), dp, strerror(errno));
device->m_devImp->deviceError(errStr);
lk.unlock();
device->m_initCond.notify_one();
udev_device_unref(udevDev);
return;
}
device->m_devFd = fd;
usb_device_descriptor devDesc = {};
read(fd, &devDesc, 1);
read(fd, &devDesc.bDescriptorType, devDesc.bLength - 1);
if (devDesc.bNumConfigurations) {
usb_config_descriptor confDesc = {};
read(fd, &confDesc, 1);
read(fd, &confDesc.bDescriptorType, confDesc.bLength - 1);
if (confDesc.bNumInterfaces) {
usb_interface_descriptor intfDesc = {};
read(fd, &intfDesc, 1);
read(fd, &intfDesc.bDescriptorType, intfDesc.bLength - 1);
for (i = 0; i < intfDesc.bNumEndpoints + 1; ++i) {
usb_endpoint_descriptor endpDesc = {};
read(fd, &endpDesc, 1);
read(fd, &endpDesc.bDescriptorType, endpDesc.bLength - 1);
if ((endpDesc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) {
if ((endpDesc.bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN)
device->m_usbIntfInPipe = endpDesc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
else if ((endpDesc.bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT)
device->m_usbIntfOutPipe = endpDesc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
}
}
}
} }
size_t _receiveUSBInterruptTransfer(uint8_t* data, size_t length) /* Request that kernel disconnects existing driver */
{ usbdevfs_ioctl disconnectReq = {0, USBDEVFS_DISCONNECT, NULL};
if (m_devFd) ioctl(fd, USBDEVFS_IOCTL, &disconnectReq);
{
usbdevfs_bulktransfer xfer = /* Return control to main thread */
{ device->m_runningTransferLoop = true;
m_usbIntfInPipe | USB_DIR_IN, lk.unlock();
(unsigned)length, device->m_initCond.notify_one();
30,
data /* Start transfer loop */
}; device->m_devImp->initialCycle();
return ioctl(m_devFd, USBDEVFS_BULK, &xfer); while (device->m_runningTransferLoop)
} device->m_devImp->transferCycle();
return 0; device->m_devImp->finalCycle();
/* Cleanup */
close(fd);
device->m_devFd = 0;
udev_device_unref(udevDev);
}
static void _threadProcBTLL(std::shared_ptr<HIDDeviceUdev> device) {
std::unique_lock<std::mutex> lk(device->m_initMutex);
udev_device* udevDev = udev_device_new_from_syspath(GetUdev(), device->m_devPath.data());
/* Return control to main thread */
device->m_runningTransferLoop = true;
lk.unlock();
device->m_initCond.notify_one();
/* Start transfer loop */
device->m_devImp->initialCycle();
while (device->m_runningTransferLoop)
device->m_devImp->transferCycle();
device->m_devImp->finalCycle();
udev_device_unref(udevDev);
}
static void _threadProcHID(std::shared_ptr<HIDDeviceUdev> device) {
char errStr[256];
std::unique_lock<std::mutex> lk(device->m_initMutex);
udev_device* udevDev = udev_device_new_from_syspath(GetUdev(), device->m_devPath.data());
/* Get device file */
const char* dp = udev_device_get_devnode(udevDev);
int fd = open(dp, O_RDWR | O_NONBLOCK);
if (fd < 0) {
snprintf(errStr, 256, "Unable to open %s@%s: %s\n", device->m_token.getProductName().data(), dp, strerror(errno));
device->m_devImp->deviceError(errStr);
lk.unlock();
device->m_initCond.notify_one();
udev_device_unref(udevDev);
return;
}
device->m_devFd = fd;
/* Return control to main thread */
device->m_runningTransferLoop = true;
lk.unlock();
device->m_initCond.notify_one();
/* Report descriptor size */
int reportDescSize;
if (ioctl(fd, HIDIOCGRDESCSIZE, &reportDescSize) == -1) {
snprintf(errStr, 256, "Unable to ioctl(HIDIOCGRDESCSIZE) %s@%s: %s\n", device->m_token.getProductName().data(),
dp, strerror(errno));
device->m_devImp->deviceError(errStr);
close(fd);
return;
} }
static void _threadProcUSBLL(std::shared_ptr<HIDDeviceUdev> device) /* Get report descriptor */
{ hidraw_report_descriptor reportDesc;
int i; reportDesc.size = reportDescSize;
char errStr[256]; if (ioctl(fd, HIDIOCGRDESC, &reportDesc) == -1) {
std::unique_lock<std::mutex> lk(device->m_initMutex); snprintf(errStr, 256, "Unable to ioctl(HIDIOCGRDESC) %s@%s: %s\n", device->m_token.getProductName().data(), dp,
udev_device* udevDev = udev_device_new_from_syspath(GetUdev(), device->m_devPath.data()); strerror(errno));
device->m_devImp->deviceError(errStr);
/* Get device file */ close(fd);
const char* dp = udev_device_get_devnode(udevDev); return;
int fd = open(dp, O_RDWR);
if (fd < 0)
{
snprintf(errStr, 256, "Unable to open %s@%s: %s\n",
device->m_token.getProductName().data(), dp, strerror(errno));
device->m_devImp->deviceError(errStr);
lk.unlock();
device->m_initCond.notify_one();
udev_device_unref(udevDev);
return;
}
device->m_devFd = fd;
usb_device_descriptor devDesc = {};
read(fd, &devDesc, 1);
read(fd, &devDesc.bDescriptorType, devDesc.bLength-1);
if (devDesc.bNumConfigurations)
{
usb_config_descriptor confDesc = {};
read(fd, &confDesc, 1);
read(fd, &confDesc.bDescriptorType, confDesc.bLength-1);
if (confDesc.bNumInterfaces)
{
usb_interface_descriptor intfDesc = {};
read(fd, &intfDesc, 1);
read(fd, &intfDesc.bDescriptorType, intfDesc.bLength-1);
for (i=0 ; i<intfDesc.bNumEndpoints+1 ; ++i)
{
usb_endpoint_descriptor endpDesc = {};
read(fd, &endpDesc, 1);
read(fd, &endpDesc.bDescriptorType, endpDesc.bLength-1);
if ((endpDesc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)
{
if ((endpDesc.bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN)
device->m_usbIntfInPipe = endpDesc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
else if ((endpDesc.bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT)
device->m_usbIntfOutPipe = endpDesc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
}
}
}
}
/* Request that kernel disconnects existing driver */
usbdevfs_ioctl disconnectReq = {
0,
USBDEVFS_DISCONNECT,
NULL
};
ioctl(fd, USBDEVFS_IOCTL, &disconnectReq);
/* Return control to main thread */
device->m_runningTransferLoop = true;
lk.unlock();
device->m_initCond.notify_one();
/* Start transfer loop */
device->m_devImp->initialCycle();
while (device->m_runningTransferLoop)
device->m_devImp->transferCycle();
device->m_devImp->finalCycle();
/* Cleanup */
close(fd);
device->m_devFd = 0;
udev_device_unref(udevDev);
} }
size_t readSz = HIDParser::CalculateMaxInputReportSize(reportDesc.value, reportDesc.size);
std::unique_ptr<uint8_t[]> readBuf(new uint8_t[readSz]);
static void _threadProcBTLL(std::shared_ptr<HIDDeviceUdev> device) /* Start transfer loop */
{ device->m_devImp->initialCycle();
std::unique_lock<std::mutex> lk(device->m_initMutex); while (device->m_runningTransferLoop) {
udev_device* udevDev = udev_device_new_from_syspath(GetUdev(), device->m_devPath.data()); fd_set readset;
FD_ZERO(&readset);
/* Return control to main thread */ FD_SET(fd, &readset);
device->m_runningTransferLoop = true; struct timeval timeout = {0, 10000};
lk.unlock(); if (select(fd + 1, &readset, nullptr, nullptr, &timeout) > 0) {
device->m_initCond.notify_one(); while (true) {
ssize_t sz = read(fd, readBuf.get(), readSz);
/* Start transfer loop */ if (sz < 0)
device->m_devImp->initialCycle(); break;
while (device->m_runningTransferLoop) device->m_devImp->receivedHIDReport(readBuf.get(), sz, HIDReportType::Input, readBuf[0]);
device->m_devImp->transferCycle();
device->m_devImp->finalCycle();
udev_device_unref(udevDev);
}
static void _threadProcHID(std::shared_ptr<HIDDeviceUdev> device)
{
char errStr[256];
std::unique_lock<std::mutex> lk(device->m_initMutex);
udev_device* udevDev = udev_device_new_from_syspath(GetUdev(), device->m_devPath.data());
/* Get device file */
const char* dp = udev_device_get_devnode(udevDev);
int fd = open(dp, O_RDWR | O_NONBLOCK);
if (fd < 0)
{
snprintf(errStr, 256, "Unable to open %s@%s: %s\n",
device->m_token.getProductName().data(), dp, strerror(errno));
device->m_devImp->deviceError(errStr);
lk.unlock();
device->m_initCond.notify_one();
udev_device_unref(udevDev);
return;
} }
device->m_devFd = fd; }
if (device->m_runningTransferLoop)
/* Return control to main thread */ device->m_devImp->transferCycle();
device->m_runningTransferLoop = true;
lk.unlock();
device->m_initCond.notify_one();
/* Report descriptor size */
int reportDescSize;
if (ioctl(fd, HIDIOCGRDESCSIZE, &reportDescSize) == -1)
{
snprintf(errStr, 256, "Unable to ioctl(HIDIOCGRDESCSIZE) %s@%s: %s\n",
device->m_token.getProductName().data(), dp, strerror(errno));
device->m_devImp->deviceError(errStr);
close(fd);
return;
}
/* Get report descriptor */
hidraw_report_descriptor reportDesc;
reportDesc.size = reportDescSize;
if (ioctl(fd, HIDIOCGRDESC, &reportDesc) == -1)
{
snprintf(errStr, 256, "Unable to ioctl(HIDIOCGRDESC) %s@%s: %s\n",
device->m_token.getProductName().data(), dp, strerror(errno));
device->m_devImp->deviceError(errStr);
close(fd);
return;
}
size_t readSz = HIDParser::CalculateMaxInputReportSize(reportDesc.value, reportDesc.size);
std::unique_ptr<uint8_t[]> readBuf(new uint8_t[readSz]);
/* Start transfer loop */
device->m_devImp->initialCycle();
while (device->m_runningTransferLoop)
{
fd_set readset;
FD_ZERO(&readset);
FD_SET(fd, &readset);
struct timeval timeout = {0, 10000};
if (select(fd + 1, &readset, nullptr, nullptr, &timeout) > 0)
{
while (true)
{
ssize_t sz = read(fd, readBuf.get(), readSz);
if (sz < 0)
break;
device->m_devImp->receivedHIDReport(readBuf.get(), sz,
HIDReportType::Input, readBuf[0]);
}
}
if (device->m_runningTransferLoop)
device->m_devImp->transferCycle();
}
device->m_devImp->finalCycle();
/* Cleanup */
close(fd);
device->m_devFd = 0;
udev_device_unref(udevDev);
} }
device->m_devImp->finalCycle();
void _deviceDisconnected() /* Cleanup */
{ close(fd);
m_runningTransferLoop = false; device->m_devFd = 0;
udev_device_unref(udevDev);
}
void _deviceDisconnected() { m_runningTransferLoop = false; }
std::vector<uint8_t> _getReportDescriptor() {
/* Report descriptor size */
int reportDescSize;
if (ioctl(m_devFd, HIDIOCGRDESCSIZE, &reportDescSize) == -1)
return {};
/* Get report descriptor */
hidraw_report_descriptor reportDesc;
reportDesc.size = reportDescSize;
if (ioctl(m_devFd, HIDIOCGRDESC, &reportDesc) == -1)
return {};
std::vector<uint8_t> ret(reportDesc.size, '\0');
memmove(ret.data(), reportDesc.value, reportDesc.size);
return ret;
}
bool _sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message) {
if (m_devFd) {
if (tp == HIDReportType::Feature) {
int ret = ioctl(m_devFd, HIDIOCSFEATURE(length), data);
if (ret < 0)
return false;
return true;
} else if (tp == HIDReportType::Output) {
ssize_t ret = write(m_devFd, data, length);
if (ret < 0)
return false;
return true;
}
} }
return false;
}
std::vector<uint8_t> _getReportDescriptor() size_t _receiveHIDReport(uint8_t* data, size_t length, HIDReportType tp, uint32_t message) {
{ if (m_devFd) {
/* Report descriptor size */ if (tp == HIDReportType::Feature) {
int reportDescSize; data[0] = message;
if (ioctl(m_devFd, HIDIOCGRDESCSIZE, &reportDescSize) == -1) int ret = ioctl(m_devFd, HIDIOCGFEATURE(length), data);
return {}; if (ret < 0)
return 0;
/* Get report descriptor */ return length;
hidraw_report_descriptor reportDesc; }
reportDesc.size = reportDescSize;
if (ioctl(m_devFd, HIDIOCGRDESC, &reportDesc) == -1)
return {};
std::vector<uint8_t> ret(reportDesc.size, '\0');
memmove(ret.data(), reportDesc.value, reportDesc.size);
return ret;
}
bool _sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message)
{
if (m_devFd)
{
if (tp == HIDReportType::Feature)
{
int ret = ioctl(m_devFd, HIDIOCSFEATURE(length), data);
if (ret < 0)
return false;
return true;
}
else if (tp == HIDReportType::Output)
{
ssize_t ret = write(m_devFd, data, length);
if (ret < 0)
return false;
return true;
}
}
return false;
}
size_t _receiveHIDReport(uint8_t *data, size_t length, HIDReportType tp, uint32_t message)
{
if (m_devFd)
{
if (tp == HIDReportType::Feature)
{
data[0] = message;
int ret = ioctl(m_devFd, HIDIOCGFEATURE(length), data);
if (ret < 0)
return 0;
return length;
}
}
return 0;
} }
return 0;
}
public: public:
HIDDeviceUdev(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp)
: m_token(token), m_devImp(devImp), m_devPath(token.getDevicePath()) {}
HIDDeviceUdev(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp) void _startThread() {
: m_token(token), std::unique_lock<std::mutex> lk(m_initMutex);
m_devImp(devImp), DeviceType dType = m_token.getDeviceType();
m_devPath(token.getDevicePath()) if (dType == DeviceType::USB)
{ m_thread = std::thread(_threadProcUSBLL, std::static_pointer_cast<HIDDeviceUdev>(shared_from_this()));
else if (dType == DeviceType::Bluetooth)
m_thread = std::thread(_threadProcBTLL, std::static_pointer_cast<HIDDeviceUdev>(shared_from_this()));
else if (dType == DeviceType::HID)
m_thread = std::thread(_threadProcHID, std::static_pointer_cast<HIDDeviceUdev>(shared_from_this()));
else {
fprintf(stderr, "invalid token supplied to device constructor");
abort();
} }
m_initCond.wait(lk);
}
void _startThread() ~HIDDeviceUdev() {
{ m_runningTransferLoop = false;
std::unique_lock<std::mutex> lk(m_initMutex); if (m_thread.joinable())
DeviceType dType = m_token.getDeviceType(); m_thread.detach();
if (dType == DeviceType::USB) }
m_thread = std::thread(_threadProcUSBLL, std::static_pointer_cast<HIDDeviceUdev>(shared_from_this()));
else if (dType == DeviceType::Bluetooth)
m_thread = std::thread(_threadProcBTLL, std::static_pointer_cast<HIDDeviceUdev>(shared_from_this()));
else if (dType == DeviceType::HID)
m_thread = std::thread(_threadProcHID, std::static_pointer_cast<HIDDeviceUdev>(shared_from_this()));
else
{
fprintf(stderr, "invalid token supplied to device constructor");
abort();
}
m_initCond.wait(lk);
}
~HIDDeviceUdev()
{
m_runningTransferLoop = false;
if (m_thread.joinable())
m_thread.detach();
}
}; };
std::shared_ptr<IHIDDevice> IHIDDeviceNew(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp) std::shared_ptr<IHIDDevice> IHIDDeviceNew(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp) {
{ return std::make_shared<HIDDeviceUdev>(token, devImp);
return std::make_shared<HIDDeviceUdev>(token, devImp);
} }
} } // namespace boo

View File

@ -21,378 +21,311 @@
#undef min #undef min
#undef max #undef max
namespace boo namespace boo {
{
class HIDDeviceWinUSB final : public IHIDDevice class HIDDeviceWinUSB final : public IHIDDevice {
{ DeviceToken& m_token;
DeviceToken& m_token; std::shared_ptr<DeviceBase> m_devImp;
std::shared_ptr<DeviceBase> m_devImp;
HANDLE m_devHandle = 0; HANDLE m_devHandle = 0;
HANDLE m_hidHandle = 0; HANDLE m_hidHandle = 0;
WINUSB_INTERFACE_HANDLE m_usbHandle = nullptr; WINUSB_INTERFACE_HANDLE m_usbHandle = nullptr;
unsigned m_usbIntfInPipe = 0; unsigned m_usbIntfInPipe = 0;
unsigned m_usbIntfOutPipe = 0; unsigned m_usbIntfOutPipe = 0;
bool m_runningTransferLoop = false; bool m_runningTransferLoop = false;
std::string_view m_devPath; std::string_view m_devPath;
std::mutex m_initMutex; std::mutex m_initMutex;
std::condition_variable m_initCond; std::condition_variable m_initCond;
std::thread m_thread; std::thread m_thread;
bool _sendUSBInterruptTransfer(const uint8_t* data, size_t length) bool _sendUSBInterruptTransfer(const uint8_t* data, size_t length) {
{ if (m_usbHandle) {
if (m_usbHandle) ULONG lengthTransferred = 0;
{ if (!WinUsb_WritePipe(m_usbHandle, m_usbIntfOutPipe, (PUCHAR)data, (ULONG)length, &lengthTransferred, NULL) ||
ULONG lengthTransferred = 0; lengthTransferred != length)
if (!WinUsb_WritePipe(m_usbHandle, m_usbIntfOutPipe, (PUCHAR)data,
(ULONG)length, &lengthTransferred, NULL) ||
lengthTransferred != length)
return false;
return true;
}
return false; return false;
return true;
}
return false;
}
size_t _receiveUSBInterruptTransfer(uint8_t* data, size_t length) {
if (m_usbHandle) {
ULONG lengthTransferred = 0;
if (!WinUsb_ReadPipe(m_usbHandle, m_usbIntfInPipe, (PUCHAR)data, (ULONG)length, &lengthTransferred, NULL))
return 0;
return lengthTransferred;
}
return 0;
}
static void _threadProcUSBLL(std::shared_ptr<HIDDeviceWinUSB> device) {
unsigned i;
char errStr[256];
std::unique_lock<std::mutex> lk(device->m_initMutex);
/* POSIX.. who needs it?? -MS */
device->m_devHandle =
CreateFileA(device->m_devPath.data(), GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
if (INVALID_HANDLE_VALUE == device->m_devHandle) {
_snprintf(errStr, 256, "Unable to open %s@%s: %d\n", device->m_token.getProductName().data(),
device->m_devPath.data(), GetLastError());
device->m_devImp->deviceError(errStr);
lk.unlock();
device->m_initCond.notify_one();
return;
} }
size_t _receiveUSBInterruptTransfer(uint8_t* data, size_t length) if (!WinUsb_Initialize(device->m_devHandle, &device->m_usbHandle)) {
{ _snprintf(errStr, 256, "Unable to open %s@%s: %d\n", device->m_token.getProductName().data(),
if (m_usbHandle) device->m_devPath.data(), GetLastError());
{ device->m_devImp->deviceError(errStr);
ULONG lengthTransferred = 0; lk.unlock();
if (!WinUsb_ReadPipe(m_usbHandle, m_usbIntfInPipe, (PUCHAR)data, device->m_initCond.notify_one();
(ULONG)length, &lengthTransferred, NULL)) CloseHandle(device->m_devHandle);
return 0; return;
return lengthTransferred; }
/* Enumerate device pipes */
USB_INTERFACE_DESCRIPTOR ifDesc = {0};
if (!WinUsb_QueryInterfaceSettings(device->m_usbHandle, 0, &ifDesc)) {
_snprintf(errStr, 256, "Unable to open %s@%s: %d\n", device->m_token.getProductName().data(),
device->m_devPath.data(), GetLastError());
device->m_devImp->deviceError(errStr);
lk.unlock();
device->m_initCond.notify_one();
CloseHandle(device->m_devHandle);
return;
}
for (i = 0; i < ifDesc.bNumEndpoints; ++i) {
WINUSB_PIPE_INFORMATION pipeDesc;
WinUsb_QueryPipe(device->m_usbHandle, 0, i, &pipeDesc);
if (pipeDesc.PipeType == UsbdPipeTypeInterrupt) {
if (USB_ENDPOINT_DIRECTION_IN(pipeDesc.PipeId))
device->m_usbIntfInPipe = pipeDesc.PipeId;
else
device->m_usbIntfOutPipe = pipeDesc.PipeId;
}
}
/* Return control to main thread */
device->m_runningTransferLoop = true;
lk.unlock();
device->m_initCond.notify_one();
/* Start transfer loop */
device->m_devImp->initialCycle();
while (device->m_runningTransferLoop)
device->m_devImp->transferCycle();
device->m_devImp->finalCycle();
/* Cleanup */
WinUsb_Free(device->m_usbHandle);
CloseHandle(device->m_devHandle);
device->m_devHandle = 0;
}
static void _threadProcBTLL(std::shared_ptr<HIDDeviceWinUSB> device) {
std::unique_lock<std::mutex> lk(device->m_initMutex);
/* Return control to main thread */
device->m_runningTransferLoop = true;
lk.unlock();
device->m_initCond.notify_one();
/* Start transfer loop */
device->m_devImp->initialCycle();
while (device->m_runningTransferLoop)
device->m_devImp->transferCycle();
device->m_devImp->finalCycle();
}
size_t m_minFeatureSz = 0;
size_t m_minInputSz = 0;
size_t m_minOutputSz = 0;
PHIDP_PREPARSED_DATA m_preparsedData = nullptr;
static void _threadProcHID(std::shared_ptr<HIDDeviceWinUSB> device) {
char errStr[256];
std::unique_lock<std::mutex> lk(device->m_initMutex);
/* POSIX.. who needs it?? -MS */
device->m_overlapped.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
device->m_hidHandle =
CreateFileA(device->m_devPath.data(), GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
if (INVALID_HANDLE_VALUE == device->m_hidHandle) {
_snprintf(errStr, 256, "Unable to open %s@%s: %d\n", device->m_token.getProductName().data(),
device->m_devPath.data(), GetLastError());
device->m_devImp->deviceError(errStr);
lk.unlock();
device->m_initCond.notify_one();
return;
}
if (!HidD_GetPreparsedData(device->m_hidHandle, &device->m_preparsedData)) {
_snprintf(errStr, 256, "Unable get preparsed data of %s@%s: %d\n", device->m_token.getProductName().data(),
device->m_devPath.data(), GetLastError());
device->m_devImp->deviceError(errStr);
lk.unlock();
device->m_initCond.notify_one();
return;
}
HIDP_CAPS caps;
HidP_GetCaps(device->m_preparsedData, &caps);
device->m_minFeatureSz = caps.FeatureReportByteLength;
device->m_minInputSz = caps.InputReportByteLength;
device->m_minOutputSz = caps.OutputReportByteLength;
/* Return control to main thread */
device->m_runningTransferLoop = true;
lk.unlock();
device->m_initCond.notify_one();
/* Allocate read buffer */
size_t inBufferSz = device->m_minInputSz;
std::unique_ptr<uint8_t[]> readBuf(new uint8_t[inBufferSz]);
/* Start transfer loop */
device->m_devImp->initialCycle();
while (device->m_runningTransferLoop) {
device->ReadCycle(readBuf.get(), inBufferSz);
if (device->m_runningTransferLoop)
device->m_devImp->transferCycle();
}
device->m_devImp->finalCycle();
/* Cleanup */
CloseHandle(device->m_overlapped.hEvent);
CloseHandle(device->m_hidHandle);
HidD_FreePreparsedData(device->m_preparsedData);
device->m_hidHandle = nullptr;
}
void _deviceDisconnected() { m_runningTransferLoop = false; }
std::vector<uint8_t> m_sendBuf;
std::vector<uint8_t> m_recvBuf;
const PHIDP_PREPARSED_DATA _getReportDescriptor() { return m_preparsedData; }
bool _sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message) {
size_t maxOut = std::max(m_minFeatureSz, std::max(m_minOutputSz, length));
if (m_sendBuf.size() < maxOut)
m_sendBuf.resize(maxOut);
if (maxOut > length)
memset(m_sendBuf.data() + length, 0, maxOut - length);
memmove(m_sendBuf.data(), data, length);
if (tp == HIDReportType::Output) {
DWORD useLength = DWORD(std::max(length, m_minOutputSz));
DWORD BytesWritten;
OVERLAPPED Overlapped;
ZeroMemory(&Overlapped, sizeof(Overlapped));
BOOL Result = WriteFile(m_hidHandle, m_sendBuf.data(), useLength, &BytesWritten, &Overlapped);
if (!Result) {
DWORD Error = GetLastError();
if (Error == ERROR_INVALID_USER_BUFFER) {
// std::cout << "Falling back to SetOutputReport" << std::endl;
if (!HidD_SetOutputReport(m_hidHandle, (PVOID)m_sendBuf.data(), useLength))
return false;
} }
if (Error != ERROR_IO_PENDING) {
fprintf(stderr, "Write Failed %08X\n", int(Error));
return false;
}
}
if (!GetOverlappedResult(m_hidHandle, &Overlapped, &BytesWritten, TRUE)) {
DWORD Error = GetLastError();
fprintf(stderr, "Write Failed %08X\n", int(Error));
return false;
}
} else if (tp == HIDReportType::Feature) {
DWORD useLength = DWORD(std::max(length, m_minFeatureSz));
if (!HidD_SetFeature(m_hidHandle, (PVOID)m_sendBuf.data(), useLength)) {
// int error = GetLastError();
return false;
}
}
return true;
}
size_t _receiveHIDReport(uint8_t* data, size_t length, HIDReportType tp, uint32_t message) {
size_t maxIn = std::max(m_minFeatureSz, std::max(m_minInputSz, length));
if (m_recvBuf.size() < maxIn)
m_recvBuf.resize(maxIn);
memset(m_recvBuf.data(), 0, length);
m_recvBuf[0] = message;
if (tp == HIDReportType::Input) {
if (!HidD_GetInputReport(m_hidHandle, m_recvBuf.data(), ULONG(std::max(m_minInputSz, length))))
return 0;
} else if (tp == HIDReportType::Feature) {
if (!HidD_GetFeature(m_hidHandle, m_recvBuf.data(), ULONG(std::max(m_minFeatureSz, length))))
return 0; return 0;
} }
static void _threadProcUSBLL(std::shared_ptr<HIDDeviceWinUSB> device) memmove(data, m_recvBuf.data(), length);
{ return length;
unsigned i; }
char errStr[256];
std::unique_lock<std::mutex> lk(device->m_initMutex);
/* POSIX.. who needs it?? -MS */
device->m_devHandle = CreateFileA(device->m_devPath.data(),
GENERIC_WRITE | GENERIC_READ,
FILE_SHARE_WRITE | FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
NULL);
if (INVALID_HANDLE_VALUE == device->m_devHandle)
{
_snprintf(errStr, 256, "Unable to open %s@%s: %d\n",
device->m_token.getProductName().data(),
device->m_devPath.data(), GetLastError());
device->m_devImp->deviceError(errStr);
lk.unlock();
device->m_initCond.notify_one();
return;
}
if (!WinUsb_Initialize(device->m_devHandle, &device->m_usbHandle))
{
_snprintf(errStr, 256, "Unable to open %s@%s: %d\n",
device->m_token.getProductName().data(),
device->m_devPath.data(), GetLastError());
device->m_devImp->deviceError(errStr);
lk.unlock();
device->m_initCond.notify_one();
CloseHandle(device->m_devHandle);
return;
}
/* Enumerate device pipes */
USB_INTERFACE_DESCRIPTOR ifDesc = {0};
if (!WinUsb_QueryInterfaceSettings(device->m_usbHandle, 0, &ifDesc))
{
_snprintf(errStr, 256, "Unable to open %s@%s: %d\n",
device->m_token.getProductName().data(),
device->m_devPath.data(), GetLastError());
device->m_devImp->deviceError(errStr);
lk.unlock();
device->m_initCond.notify_one();
CloseHandle(device->m_devHandle);
return;
}
for (i=0 ; i<ifDesc.bNumEndpoints ; ++i)
{
WINUSB_PIPE_INFORMATION pipeDesc;
WinUsb_QueryPipe(device->m_usbHandle, 0, i, &pipeDesc);
if (pipeDesc.PipeType == UsbdPipeTypeInterrupt)
{
if (USB_ENDPOINT_DIRECTION_IN(pipeDesc.PipeId))
device->m_usbIntfInPipe = pipeDesc.PipeId;
else
device->m_usbIntfOutPipe = pipeDesc.PipeId;
}
}
/* Return control to main thread */
device->m_runningTransferLoop = true;
lk.unlock();
device->m_initCond.notify_one();
/* Start transfer loop */
device->m_devImp->initialCycle();
while (device->m_runningTransferLoop)
device->m_devImp->transferCycle();
device->m_devImp->finalCycle();
/* Cleanup */
WinUsb_Free(device->m_usbHandle);
CloseHandle(device->m_devHandle);
device->m_devHandle = 0;
}
static void _threadProcBTLL(std::shared_ptr<HIDDeviceWinUSB> device)
{
std::unique_lock<std::mutex> lk(device->m_initMutex);
/* Return control to main thread */
device->m_runningTransferLoop = true;
lk.unlock();
device->m_initCond.notify_one();
/* Start transfer loop */
device->m_devImp->initialCycle();
while (device->m_runningTransferLoop)
device->m_devImp->transferCycle();
device->m_devImp->finalCycle();
}
size_t m_minFeatureSz = 0;
size_t m_minInputSz = 0;
size_t m_minOutputSz = 0;
PHIDP_PREPARSED_DATA m_preparsedData = nullptr;
static void _threadProcHID(std::shared_ptr<HIDDeviceWinUSB> device)
{
char errStr[256];
std::unique_lock<std::mutex> lk(device->m_initMutex);
/* POSIX.. who needs it?? -MS */
device->m_overlapped.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
device->m_hidHandle = CreateFileA(device->m_devPath.data(),
GENERIC_WRITE | GENERIC_READ,
FILE_SHARE_WRITE | FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
NULL);
if (INVALID_HANDLE_VALUE == device->m_hidHandle)
{
_snprintf(errStr, 256, "Unable to open %s@%s: %d\n",
device->m_token.getProductName().data(),
device->m_devPath.data(), GetLastError());
device->m_devImp->deviceError(errStr);
lk.unlock();
device->m_initCond.notify_one();
return;
}
if (!HidD_GetPreparsedData(device->m_hidHandle, &device->m_preparsedData))
{
_snprintf(errStr, 256, "Unable get preparsed data of %s@%s: %d\n",
device->m_token.getProductName().data(),
device->m_devPath.data(), GetLastError());
device->m_devImp->deviceError(errStr);
lk.unlock();
device->m_initCond.notify_one();
return;
}
HIDP_CAPS caps;
HidP_GetCaps(device->m_preparsedData, &caps);
device->m_minFeatureSz = caps.FeatureReportByteLength;
device->m_minInputSz = caps.InputReportByteLength;
device->m_minOutputSz = caps.OutputReportByteLength;
/* Return control to main thread */
device->m_runningTransferLoop = true;
lk.unlock();
device->m_initCond.notify_one();
/* Allocate read buffer */
size_t inBufferSz = device->m_minInputSz;
std::unique_ptr<uint8_t[]> readBuf(new uint8_t[inBufferSz]);
/* Start transfer loop */
device->m_devImp->initialCycle();
while (device->m_runningTransferLoop)
{
device->ReadCycle(readBuf.get(), inBufferSz);
if (device->m_runningTransferLoop)
device->m_devImp->transferCycle();
}
device->m_devImp->finalCycle();
/* Cleanup */
CloseHandle(device->m_overlapped.hEvent);
CloseHandle(device->m_hidHandle);
HidD_FreePreparsedData(device->m_preparsedData);
device->m_hidHandle = nullptr;
}
void _deviceDisconnected()
{
m_runningTransferLoop = false;
}
std::vector<uint8_t> m_sendBuf;
std::vector<uint8_t> m_recvBuf;
const PHIDP_PREPARSED_DATA _getReportDescriptor()
{
return m_preparsedData;
}
bool _sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message)
{
size_t maxOut = std::max(m_minFeatureSz, std::max(m_minOutputSz, length));
if (m_sendBuf.size() < maxOut)
m_sendBuf.resize(maxOut);
if (maxOut > length)
memset(m_sendBuf.data() + length, 0, maxOut - length);
memmove(m_sendBuf.data(), data, length);
if (tp == HIDReportType::Output)
{
DWORD useLength = DWORD(std::max(length, m_minOutputSz));
DWORD BytesWritten;
OVERLAPPED Overlapped;
ZeroMemory(&Overlapped, sizeof(Overlapped));
BOOL Result = WriteFile(m_hidHandle, m_sendBuf.data(), useLength, &BytesWritten, &Overlapped);
if (!Result)
{
DWORD Error = GetLastError();
if (Error == ERROR_INVALID_USER_BUFFER)
{
//std::cout << "Falling back to SetOutputReport" << std::endl;
if (!HidD_SetOutputReport(m_hidHandle, (PVOID)m_sendBuf.data(), useLength))
return false;
}
if (Error != ERROR_IO_PENDING)
{
fprintf(stderr, "Write Failed %08X\n", int(Error));
return false;
}
}
if (!GetOverlappedResult(m_hidHandle, &Overlapped, &BytesWritten, TRUE))
{
DWORD Error = GetLastError();
fprintf(stderr, "Write Failed %08X\n", int(Error));
return false;
}
}
else if (tp == HIDReportType::Feature)
{
DWORD useLength = DWORD(std::max(length, m_minFeatureSz));
if (!HidD_SetFeature(m_hidHandle, (PVOID)m_sendBuf.data(), useLength))
{
//int error = GetLastError();
return false;
}
}
return true;
}
size_t _receiveHIDReport(uint8_t* data, size_t length, HIDReportType tp, uint32_t message)
{
size_t maxIn = std::max(m_minFeatureSz, std::max(m_minInputSz, length));
if (m_recvBuf.size() < maxIn)
m_recvBuf.resize(maxIn);
memset(m_recvBuf.data(), 0, length);
m_recvBuf[0] = message;
if (tp == HIDReportType::Input)
{
if (!HidD_GetInputReport(m_hidHandle, m_recvBuf.data(), ULONG(std::max(m_minInputSz, length))))
return 0;
}
else if (tp == HIDReportType::Feature)
{
if (!HidD_GetFeature(m_hidHandle, m_recvBuf.data(), ULONG(std::max(m_minFeatureSz, length))))
return 0;
}
memmove(data, m_recvBuf.data(), length);
return length;
}
public: public:
HIDDeviceWinUSB(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp)
: m_token(token), m_devImp(devImp), m_devPath(token.getDevicePath()) {}
HIDDeviceWinUSB(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp) void _startThread() {
: m_token(token), std::unique_lock<std::mutex> lk(m_initMutex);
m_devImp(devImp), DeviceType dType = m_token.getDeviceType();
m_devPath(token.getDevicePath()) if (dType == DeviceType::USB)
{ m_thread = std::thread(_threadProcUSBLL, std::static_pointer_cast<HIDDeviceWinUSB>(shared_from_this()));
} else if (dType == DeviceType::Bluetooth)
m_thread = std::thread(_threadProcBTLL, std::static_pointer_cast<HIDDeviceWinUSB>(shared_from_this()));
else if (dType == DeviceType::HID)
m_thread = std::thread(_threadProcHID, std::static_pointer_cast<HIDDeviceWinUSB>(shared_from_this()));
else
throw std::runtime_error("invalid token supplied to device constructor");
m_initCond.wait(lk);
}
void _startThread() ~HIDDeviceWinUSB() {
{ m_runningTransferLoop = false;
std::unique_lock<std::mutex> lk(m_initMutex); if (m_thread.joinable())
DeviceType dType = m_token.getDeviceType(); m_thread.detach();
if (dType == DeviceType::USB) }
m_thread = std::thread(_threadProcUSBLL, std::static_pointer_cast<HIDDeviceWinUSB>(shared_from_this()));
else if (dType == DeviceType::Bluetooth)
m_thread = std::thread(_threadProcBTLL, std::static_pointer_cast<HIDDeviceWinUSB>(shared_from_this()));
else if (dType == DeviceType::HID)
m_thread = std::thread(_threadProcHID, std::static_pointer_cast<HIDDeviceWinUSB>(shared_from_this()));
else
throw std::runtime_error("invalid token supplied to device constructor");
m_initCond.wait(lk);
}
~HIDDeviceWinUSB() OVERLAPPED m_overlapped = {};
{
void ReadCycle(uint8_t* inBuffer, size_t inBufferSz) {
ResetEvent(m_overlapped.hEvent);
ZeroMemory(inBuffer, inBufferSz);
DWORD BytesRead = 0;
BOOL Result = ReadFile(m_hidHandle, inBuffer, DWORD(inBufferSz), &BytesRead, &m_overlapped);
if (!Result) {
DWORD Error = GetLastError();
if (Error == ERROR_DEVICE_NOT_CONNECTED) {
m_runningTransferLoop = false; m_runningTransferLoop = false;
if (m_thread.joinable()) return;
m_thread.detach(); } else if (Error != ERROR_IO_PENDING) {
fprintf(stderr, "Read Failed: %08X\n", int(Error));
return;
} else if (!GetOverlappedResultEx(m_hidHandle, &m_overlapped, &BytesRead, 10, TRUE)) {
return;
}
} }
OVERLAPPED m_overlapped = {}; m_devImp->receivedHIDReport(inBuffer, BytesRead, HIDReportType::Input, inBuffer[0]);
}
void ReadCycle(uint8_t* inBuffer, size_t inBufferSz)
{
ResetEvent(m_overlapped.hEvent);
ZeroMemory(inBuffer, inBufferSz);
DWORD BytesRead = 0;
BOOL Result = ReadFile(m_hidHandle, inBuffer, DWORD(inBufferSz), &BytesRead, &m_overlapped);
if (!Result)
{
DWORD Error = GetLastError();
if (Error == ERROR_DEVICE_NOT_CONNECTED)
{
m_runningTransferLoop = false;
return;
}
else if (Error != ERROR_IO_PENDING)
{
fprintf(stderr, "Read Failed: %08X\n", int(Error));
return;
}
else if (!GetOverlappedResultEx(m_hidHandle, &m_overlapped, &BytesRead, 10, TRUE))
{
return;
}
}
m_devImp->receivedHIDReport(inBuffer, BytesRead, HIDReportType::Input, inBuffer[0]);
}
}; };
std::shared_ptr<IHIDDevice> IHIDDeviceNew(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp) std::shared_ptr<IHIDDevice> IHIDDeviceNew(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp) {
{ return std::make_shared<HIDDeviceWinUSB>(token, devImp);
return std::make_shared<HIDDeviceWinUSB>(token, devImp);
} }
} } // namespace boo

View File

@ -2,33 +2,25 @@
#include "boo/inputdev/DeviceToken.hpp" #include "boo/inputdev/DeviceToken.hpp"
#include "boo/inputdev/DeviceBase.hpp" #include "boo/inputdev/DeviceBase.hpp"
namespace boo namespace boo {
{
class HIDDeviceBSD final : public IHIDDevice class HIDDeviceBSD final : public IHIDDevice {
{ DeviceToken& m_token;
DeviceToken& m_token; DeviceBase& m_devImp;
DeviceBase& m_devImp;
void _deviceDisconnected() {}
bool _sendUSBInterruptTransfer(const uint8_t* data, size_t length) { return false; }
size_t _receiveUSBInterruptTransfer(uint8_t* data, size_t length) { return 0; }
bool _sendHIDReport(const uint8_t* data, size_t length, uint16_t message) { return false; }
size_t _recieveReport(const uint8_t* data, size_t length, uint16_t message) { return 0; }
void _deviceDisconnected() {}
bool _sendUSBInterruptTransfer(const uint8_t* data, size_t length) { return false; }
size_t _receiveUSBInterruptTransfer(uint8_t* data, size_t length) { return 0; }
bool _sendHIDReport(const uint8_t* data, size_t length, uint16_t message) { return false; }
size_t _recieveReport(const uint8_t* data, size_t length, uint16_t message) {return 0; }
public: public:
HIDDeviceBSD(DeviceToken& token, DeviceBase& devImp) HIDDeviceBSD(DeviceToken& token, DeviceBase& devImp) : m_token(token), m_devImp(devImp) {}
: m_token(token),
m_devImp(devImp)
{
}
~HIDDeviceBSD() ~HIDDeviceBSD() {}
{
}
}; };
std::shared_ptr<IHIDDevice> IHIDDeviceNew(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp) std::shared_ptr<IHIDDevice> IHIDDeviceNew(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp) {
{ return std::make_shared<HIDDeviceBSD>(token, devImp);
return std::make_shared<HIDDeviceBSD>(token, devImp);
}
} }
} // namespace boo

View File

@ -11,326 +11,285 @@
#include "IOKitPointer.hpp" #include "IOKitPointer.hpp"
#include "../CFPointer.hpp" #include "../CFPointer.hpp"
namespace boo namespace boo {
{
/* /*
* Reference: http://oroboro.com/usb-serial-number-osx/ * Reference: http://oroboro.com/usb-serial-number-osx/
*/ */
static bool getUSBStringDescriptor(const IUnknownPointer<IOUSBDeviceInterface182>& usbDevice, UInt8 idx, char* out) static bool getUSBStringDescriptor(const IUnknownPointer<IOUSBDeviceInterface182>& usbDevice, UInt8 idx, char* out) {
{ UInt16 buffer[128];
UInt16 buffer[128];
// wow... we're actually forced to make hard coded bus requests. Its like
// hard disk programming in the 80's!
IOUSBDevRequest request;
request.bmRequestType = USBmakebmRequestType(kUSBIn,
kUSBStandard,
kUSBDevice);
request.bRequest = kUSBRqGetDescriptor;
request.wValue = (kUSBStringDesc << 8) | idx;
request.wIndex = 0x409; // english
request.wLength = sizeof(buffer);
request.pData = buffer;
kern_return_t err = usbDevice->DeviceRequest(usbDevice.storage(), &request);
if (err != 0)
{
// the request failed... fairly uncommon for the USB disk driver, but not
// so uncommon for other devices. This can also be less reliable if your
// disk is mounted through an external USB hub. At this level we actually
// have to worry about hardware issues like this.
return false;
}
// we're mallocing this string just as an example. But you probably will want
// to do something smarter, like pre-allocated buffers in the info class, or
// use a string class.
if (request.wLenDone == 0)
return false;
unsigned count = (request.wLenDone - 1) / 2; // wow... we're actually forced to make hard coded bus requests. Its like
unsigned i; // hard disk programming in the 80's!
for (i=0 ; i<count ; ++i) IOUSBDevRequest request;
out[i] = buffer[i+1];
out[i] = '\0'; request.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
request.bRequest = kUSBRqGetDescriptor;
return true; request.wValue = (kUSBStringDesc << 8) | idx;
request.wIndex = 0x409; // english
request.wLength = sizeof(buffer);
request.pData = buffer;
kern_return_t err = usbDevice->DeviceRequest(usbDevice.storage(), &request);
if (err != 0) {
// the request failed... fairly uncommon for the USB disk driver, but not
// so uncommon for other devices. This can also be less reliable if your
// disk is mounted through an external USB hub. At this level we actually
// have to worry about hardware issues like this.
return false;
}
// we're mallocing this string just as an example. But you probably will want
// to do something smarter, like pre-allocated buffers in the info class, or
// use a string class.
if (request.wLenDone == 0)
return false;
unsigned count = (request.wLenDone - 1) / 2;
unsigned i;
for (i = 0; i < count; ++i)
out[i] = buffer[i + 1];
out[i] = '\0';
return true;
} }
class HIDListenerIOKit : public IHIDListener class HIDListenerIOKit : public IHIDListener {
{ DeviceFinder& m_finder;
DeviceFinder& m_finder;
CFRunLoopRef m_listenerRunLoop;
IONotificationPortRef m_llPort;
IOObjectPointer<io_iterator_t> m_llAddNotif, m_llRemoveNotif;
IOObjectPointer<io_iterator_t> m_hidAddNotif, m_hidRemoveNotif;
const char* m_usbClass;
bool m_scanningEnabled;
static void devicesConnectedUSBLL(HIDListenerIOKit* listener,
io_iterator_t iterator)
{
while (IOObjectPointer<io_service_t> obj = IOIteratorNext(iterator))
{
io_string_t devPath;
if (IORegistryEntryGetPath(obj.get(), kIOServicePlane, devPath) != 0)
continue;
if (!listener->m_scanningEnabled || CFRunLoopRef m_listenerRunLoop;
listener->m_finder._hasToken(devPath)) IONotificationPortRef m_llPort;
continue; IOObjectPointer<io_iterator_t> m_llAddNotif, m_llRemoveNotif;
IOObjectPointer<io_iterator_t> m_hidAddNotif, m_hidRemoveNotif;
UInt16 vid, pid; const char* m_usbClass;
char vstr[128] = {0}; bool m_scanningEnabled;
char pstr[128] = {0};
{
IOCFPluginPointer devServ;
SInt32 score;
IOReturn err;
err = IOCreatePlugInInterfaceForService(obj.get(), kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &devServ, &score);
if (err != kIOReturnSuccess)
{
fprintf(stderr, "unable to open IOKit plugin interface\n");
continue;
}
IUnknownPointer<IOUSBDeviceInterface182> dev; static void devicesConnectedUSBLL(HIDListenerIOKit* listener, io_iterator_t iterator) {
err = devServ.As(&dev, kIOUSBDeviceInterfaceID182); while (IOObjectPointer<io_service_t> obj = IOIteratorNext(iterator)) {
if (err != kIOReturnSuccess) io_string_t devPath;
{ if (IORegistryEntryGetPath(obj.get(), kIOServicePlane, devPath) != 0)
fprintf(stderr, "unable to open IOKit device interface\n"); continue;
continue;
}
dev->GetDeviceVendor(dev.storage(), &vid); if (!listener->m_scanningEnabled || listener->m_finder._hasToken(devPath))
dev->GetDeviceProduct(dev.storage(), &pid); continue;
UInt8 vstridx, pstridx; UInt16 vid, pid;
dev->USBGetManufacturerStringIndex(dev.storage(), &vstridx); char vstr[128] = {0};
dev->USBGetProductStringIndex(dev.storage(), &pstridx); char pstr[128] = {0};
{
getUSBStringDescriptor(dev, vstridx, vstr); IOCFPluginPointer devServ;
getUSBStringDescriptor(dev, pstridx, pstr); SInt32 score;
} IOReturn err;
err = IOCreatePlugInInterfaceForService(obj.get(), kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID,
listener->m_finder._insertToken(std::make_unique<DeviceToken>(DeviceType::USB, &devServ, &score);
vid, pid, vstr, pstr, devPath)); if (err != kIOReturnSuccess) {
fprintf(stderr, "unable to open IOKit plugin interface\n");
//printf("ADDED %08X %s\n", obj.get(), devPath); continue;
} }
IUnknownPointer<IOUSBDeviceInterface182> dev;
err = devServ.As(&dev, kIOUSBDeviceInterfaceID182);
if (err != kIOReturnSuccess) {
fprintf(stderr, "unable to open IOKit device interface\n");
continue;
}
dev->GetDeviceVendor(dev.storage(), &vid);
dev->GetDeviceProduct(dev.storage(), &pid);
UInt8 vstridx, pstridx;
dev->USBGetManufacturerStringIndex(dev.storage(), &vstridx);
dev->USBGetProductStringIndex(dev.storage(), &pstridx);
getUSBStringDescriptor(dev, vstridx, vstr);
getUSBStringDescriptor(dev, pstridx, pstr);
}
listener->m_finder._insertToken(std::make_unique<DeviceToken>(DeviceType::USB, vid, pid, vstr, pstr, devPath));
// printf("ADDED %08X %s\n", obj.get(), devPath);
} }
}
static void devicesDisconnectedUSBLL(HIDListenerIOKit* listener,
io_iterator_t iterator) static void devicesDisconnectedUSBLL(HIDListenerIOKit* listener, io_iterator_t iterator) {
{ if (CFRunLoopGetCurrent() != listener->m_listenerRunLoop) {
if (CFRunLoopGetCurrent() != listener->m_listenerRunLoop) CFRunLoopPerformBlock(listener->m_listenerRunLoop, kCFRunLoopDefaultMode,
{ ^{ devicesDisconnectedUSBLL(listener, iterator); });
CFRunLoopPerformBlock(listener->m_listenerRunLoop, kCFRunLoopDefaultMode, ^{ CFRunLoopWakeUp(listener->m_listenerRunLoop);
devicesDisconnectedUSBLL(listener, iterator); return;
});
CFRunLoopWakeUp(listener->m_listenerRunLoop);
return;
}
while (IOObjectPointer<io_service_t> obj = IOIteratorNext(iterator))
{
io_string_t devPath;
if (IORegistryEntryGetPath(obj.get(), kIOServicePlane, devPath) != 0)
continue;
listener->m_finder._removeToken(devPath);
//printf("REMOVED %08X %s\n", obj.get(), devPath);
}
} }
while (IOObjectPointer<io_service_t> obj = IOIteratorNext(iterator)) {
static void devicesConnectedHID(HIDListenerIOKit* listener, io_string_t devPath;
io_iterator_t iterator) if (IORegistryEntryGetPath(obj.get(), kIOServicePlane, devPath) != 0)
{ continue;
while (IOObjectPointer<io_service_t> obj = IOIteratorNext(iterator)) listener->m_finder._removeToken(devPath);
{ // printf("REMOVED %08X %s\n", obj.get(), devPath);
io_string_t devPath;
if (IORegistryEntryGetPath(obj.get(), kIOServicePlane, devPath) != 0)
continue;
if (!listener->m_scanningEnabled ||
listener->m_finder._hasToken(devPath))
continue;
unsigned vidv, pidv;
char vstr[128] = {0};
char pstr[128] = {0};
{
IOCFPluginPointer devServ;
SInt32 score;
IOReturn err;
err = IOCreatePlugInInterfaceForService(obj.get(), kIOHIDDeviceTypeID, kIOCFPlugInInterfaceID, &devServ, &score);
if (err != kIOReturnSuccess)
{
fprintf(stderr, "unable to open IOKit plugin interface\n");
continue;
}
IUnknownPointer<IOHIDDeviceDeviceInterface> dev;
err = devServ.As(&dev, kIOHIDDeviceDeviceInterfaceID);
if (err != kIOReturnSuccess)
{
fprintf(stderr, "unable to open IOKit device interface\n");
continue;
}
/* Game controllers only */
CFPointer<CFNumberRef> usagePage;
dev->getProperty(dev.storage(), CFSTR(kIOHIDPrimaryUsagePageKey), (CFTypeRef*)&usagePage);
CFPointer<CFNumberRef> usage;
dev->getProperty(dev.storage(), CFSTR(kIOHIDPrimaryUsageKey), (CFTypeRef*)&usage);
int usagePageV, usageV;
CFNumberGetValue(usagePage.get(), kCFNumberIntType, &usagePageV);
CFNumberGetValue(usage.get(), kCFNumberIntType, &usageV);
if (usagePageV == kHIDPage_GenericDesktop)
{
if (usageV != kHIDUsage_GD_Joystick && usageV != kHIDUsage_GD_GamePad)
continue;
}
else
{
continue;
}
CFPointer<CFNumberRef> vid, pid;
dev->getProperty(dev.storage(), CFSTR(kIOHIDVendorIDKey), (CFTypeRef*)&vid);
dev->getProperty(dev.storage(), CFSTR(kIOHIDProductIDKey), (CFTypeRef*)&pid);
CFNumberGetValue(vid.get(), kCFNumberIntType, &vidv);
CFNumberGetValue(pid.get(), kCFNumberIntType, &pidv);
CFPointer<CFStringRef> vstridx, pstridx;
dev->getProperty(dev.storage(), CFSTR(kIOHIDManufacturerKey), (CFTypeRef*)&vstridx);
dev->getProperty(dev.storage(), CFSTR(kIOHIDProductKey), (CFTypeRef*)&pstridx);
if (vstridx)
CFStringGetCString(vstridx.get(), vstr, 128, kCFStringEncodingUTF8);
if (pstridx)
CFStringGetCString(pstridx.get(), pstr, 128, kCFStringEncodingUTF8);
}
listener->m_finder._insertToken(std::make_unique<DeviceToken>(DeviceType::HID,
vidv, pidv, vstr, pstr, devPath));
//printf("ADDED %08X %s\n", obj, devPath);
}
} }
}
static void devicesDisconnectedHID(HIDListenerIOKit* listener, static void devicesConnectedHID(HIDListenerIOKit* listener, io_iterator_t iterator) {
io_iterator_t iterator) while (IOObjectPointer<io_service_t> obj = IOIteratorNext(iterator)) {
{ io_string_t devPath;
if (CFRunLoopGetCurrent() != listener->m_listenerRunLoop) if (IORegistryEntryGetPath(obj.get(), kIOServicePlane, devPath) != 0)
{ continue;
CFRunLoopPerformBlock(listener->m_listenerRunLoop, kCFRunLoopDefaultMode, ^{
devicesDisconnectedHID(listener, iterator); if (!listener->m_scanningEnabled || listener->m_finder._hasToken(devPath))
}); continue;
CFRunLoopWakeUp(listener->m_listenerRunLoop);
return; unsigned vidv, pidv;
char vstr[128] = {0};
char pstr[128] = {0};
{
IOCFPluginPointer devServ;
SInt32 score;
IOReturn err;
err =
IOCreatePlugInInterfaceForService(obj.get(), kIOHIDDeviceTypeID, kIOCFPlugInInterfaceID, &devServ, &score);
if (err != kIOReturnSuccess) {
fprintf(stderr, "unable to open IOKit plugin interface\n");
continue;
} }
while (IOObjectPointer<io_service_t> obj = IOIteratorNext(iterator))
{ IUnknownPointer<IOHIDDeviceDeviceInterface> dev;
io_string_t devPath; err = devServ.As(&dev, kIOHIDDeviceDeviceInterfaceID);
if (IORegistryEntryGetPath(obj.get(), kIOServicePlane, devPath) != 0) if (err != kIOReturnSuccess) {
continue; fprintf(stderr, "unable to open IOKit device interface\n");
listener->m_finder._removeToken(devPath); continue;
//printf("REMOVED %08X %s\n", obj, devPath);
} }
/* Game controllers only */
CFPointer<CFNumberRef> usagePage;
dev->getProperty(dev.storage(), CFSTR(kIOHIDPrimaryUsagePageKey), (CFTypeRef*)&usagePage);
CFPointer<CFNumberRef> usage;
dev->getProperty(dev.storage(), CFSTR(kIOHIDPrimaryUsageKey), (CFTypeRef*)&usage);
int usagePageV, usageV;
CFNumberGetValue(usagePage.get(), kCFNumberIntType, &usagePageV);
CFNumberGetValue(usage.get(), kCFNumberIntType, &usageV);
if (usagePageV == kHIDPage_GenericDesktop) {
if (usageV != kHIDUsage_GD_Joystick && usageV != kHIDUsage_GD_GamePad)
continue;
} else {
continue;
}
CFPointer<CFNumberRef> vid, pid;
dev->getProperty(dev.storage(), CFSTR(kIOHIDVendorIDKey), (CFTypeRef*)&vid);
dev->getProperty(dev.storage(), CFSTR(kIOHIDProductIDKey), (CFTypeRef*)&pid);
CFNumberGetValue(vid.get(), kCFNumberIntType, &vidv);
CFNumberGetValue(pid.get(), kCFNumberIntType, &pidv);
CFPointer<CFStringRef> vstridx, pstridx;
dev->getProperty(dev.storage(), CFSTR(kIOHIDManufacturerKey), (CFTypeRef*)&vstridx);
dev->getProperty(dev.storage(), CFSTR(kIOHIDProductKey), (CFTypeRef*)&pstridx);
if (vstridx)
CFStringGetCString(vstridx.get(), vstr, 128, kCFStringEncodingUTF8);
if (pstridx)
CFStringGetCString(pstridx.get(), pstr, 128, kCFStringEncodingUTF8);
}
listener->m_finder._insertToken(std::make_unique<DeviceToken>(DeviceType::HID, vidv, pidv, vstr, pstr, devPath));
// printf("ADDED %08X %s\n", obj, devPath);
} }
}
static void devicesDisconnectedHID(HIDListenerIOKit* listener, io_iterator_t iterator) {
if (CFRunLoopGetCurrent() != listener->m_listenerRunLoop) {
CFRunLoopPerformBlock(listener->m_listenerRunLoop, kCFRunLoopDefaultMode,
^{ devicesDisconnectedHID(listener, iterator); });
CFRunLoopWakeUp(listener->m_listenerRunLoop);
return;
}
while (IOObjectPointer<io_service_t> obj = IOIteratorNext(iterator)) {
io_string_t devPath;
if (IORegistryEntryGetPath(obj.get(), kIOServicePlane, devPath) != 0)
continue;
listener->m_finder._removeToken(devPath);
// printf("REMOVED %08X %s\n", obj, devPath);
}
}
public: public:
HIDListenerIOKit(DeviceFinder& finder) HIDListenerIOKit(DeviceFinder& finder) : m_finder(finder) {
: m_finder(finder) struct utsname kernInfo;
uname(&kernInfo);
int release = atoi(kernInfo.release);
m_usbClass = release >= 15 ? "IOUSBHostDevice" : kIOUSBDeviceClassName;
m_listenerRunLoop = CFRunLoopGetMain();
m_llPort = IONotificationPortCreate(kIOMasterPortDefault);
CFRunLoopSourceRef rlSrc = IONotificationPortGetRunLoopSource(m_llPort);
CFRunLoopAddSource(m_listenerRunLoop, rlSrc, kCFRunLoopDefaultMode);
m_scanningEnabled = true;
/* Register HID Matcher */
{ {
struct utsname kernInfo; CFMutableDictionaryRef matchDict = IOServiceMatching("IOHIDDevice");
uname(&kernInfo); CFRetain(matchDict);
int release = atoi(kernInfo.release);
m_usbClass = release >= 15 ? "IOUSBHostDevice" : kIOUSBDeviceClassName;
m_listenerRunLoop = CFRunLoopGetMain(); kern_return_t hidRet =
m_llPort = IONotificationPortCreate(kIOMasterPortDefault); IOServiceAddMatchingNotification(m_llPort, kIOMatchedNotification, matchDict,
CFRunLoopSourceRef rlSrc = IONotificationPortGetRunLoopSource(m_llPort); (IOServiceMatchingCallback)devicesConnectedHID, this, &m_hidAddNotif);
CFRunLoopAddSource(m_listenerRunLoop, rlSrc, kCFRunLoopDefaultMode); if (hidRet == kIOReturnSuccess)
m_scanningEnabled = true; devicesConnectedHID(this, m_hidAddNotif.get());
/* Register HID Matcher */ hidRet =
{ IOServiceAddMatchingNotification(m_llPort, kIOTerminatedNotification, matchDict,
CFMutableDictionaryRef matchDict = IOServiceMatching("IOHIDDevice"); (IOServiceMatchingCallback)devicesDisconnectedHID, this, &m_hidRemoveNotif);
CFRetain(matchDict); if (hidRet == kIOReturnSuccess)
devicesDisconnectedHID(this, m_hidRemoveNotif.get());
kern_return_t hidRet =
IOServiceAddMatchingNotification(m_llPort, kIOMatchedNotification, matchDict,
(IOServiceMatchingCallback)devicesConnectedHID, this, &m_hidAddNotif);
if (hidRet == kIOReturnSuccess)
devicesConnectedHID(this, m_hidAddNotif.get());
hidRet =
IOServiceAddMatchingNotification(m_llPort, kIOTerminatedNotification, matchDict,
(IOServiceMatchingCallback)devicesDisconnectedHID, this, &m_hidRemoveNotif);
if (hidRet == kIOReturnSuccess)
devicesDisconnectedHID(this, m_hidRemoveNotif.get());
}
/* Register Low-Level USB Matcher */
{
CFMutableDictionaryRef matchDict = IOServiceMatching(m_usbClass);
CFRetain(matchDict);
kern_return_t llRet =
IOServiceAddMatchingNotification(m_llPort, kIOMatchedNotification, matchDict,
(IOServiceMatchingCallback)devicesConnectedUSBLL, this, &m_llAddNotif);
if (llRet == kIOReturnSuccess)
devicesConnectedUSBLL(this, m_llAddNotif.get());
llRet =
IOServiceAddMatchingNotification(m_llPort, kIOTerminatedNotification, matchDict,
(IOServiceMatchingCallback)devicesDisconnectedUSBLL, this, &m_llRemoveNotif);
if (llRet == kIOReturnSuccess)
devicesDisconnectedUSBLL(this, m_llRemoveNotif.get());
}
m_scanningEnabled = false;
} }
~HIDListenerIOKit() /* Register Low-Level USB Matcher */
{ {
//CFRunLoopRemoveSource(m_listenerRunLoop, IONotificationPortGetRunLoopSource(m_llPort), kCFRunLoopDefaultMode); CFMutableDictionaryRef matchDict = IOServiceMatching(m_usbClass);
IONotificationPortDestroy(m_llPort); CFRetain(matchDict);
kern_return_t llRet =
IOServiceAddMatchingNotification(m_llPort, kIOMatchedNotification, matchDict,
(IOServiceMatchingCallback)devicesConnectedUSBLL, this, &m_llAddNotif);
if (llRet == kIOReturnSuccess)
devicesConnectedUSBLL(this, m_llAddNotif.get());
llRet =
IOServiceAddMatchingNotification(m_llPort, kIOTerminatedNotification, matchDict,
(IOServiceMatchingCallback)devicesDisconnectedUSBLL, this, &m_llRemoveNotif);
if (llRet == kIOReturnSuccess)
devicesDisconnectedUSBLL(this, m_llRemoveNotif.get());
} }
/* Automatic device scanning */ m_scanningEnabled = false;
bool startScanning() }
{
m_scanningEnabled = true; ~HIDListenerIOKit() {
return true; // CFRunLoopRemoveSource(m_listenerRunLoop, IONotificationPortGetRunLoopSource(m_llPort), kCFRunLoopDefaultMode);
IONotificationPortDestroy(m_llPort);
}
/* Automatic device scanning */
bool startScanning() {
m_scanningEnabled = true;
return true;
}
bool stopScanning() {
m_scanningEnabled = false;
return true;
}
/* Manual device scanning */
bool scanNow() {
IOObjectPointer<io_iterator_t> iter;
if (IOServiceGetMatchingServices(kIOMasterPortDefault, IOServiceMatching(m_usbClass), &iter) == kIOReturnSuccess) {
devicesConnectedUSBLL(this, iter.get());
} }
bool stopScanning() return true;
{ }
m_scanningEnabled = false;
return true;
}
/* Manual device scanning */
bool scanNow()
{
IOObjectPointer<io_iterator_t> iter;
if (IOServiceGetMatchingServices(kIOMasterPortDefault,
IOServiceMatching(m_usbClass), &iter) == kIOReturnSuccess)
{
devicesConnectedUSBLL(this, iter.get());
}
return true;
}
}; };
std::unique_ptr<IHIDListener> IHIDListenerNew(DeviceFinder& finder) std::unique_ptr<IHIDListener> IHIDListenerNew(DeviceFinder& finder) {
{ return std::make_unique<HIDListenerIOKit>(finder);
return std::make_unique<HIDListenerIOKit>(finder);
} }
} } // namespace boo

View File

@ -1,25 +1,18 @@
#include "boo/inputdev/IHIDListener.hpp" #include "boo/inputdev/IHIDListener.hpp"
namespace boo namespace boo {
{
class HIDListenerNX : public IHIDListener class HIDListenerNX : public IHIDListener {
{ DeviceFinder& m_finder;
DeviceFinder& m_finder;
public: public:
HIDListenerNX(DeviceFinder& finder) HIDListenerNX(DeviceFinder& finder) : m_finder(finder) {}
: m_finder(finder)
{}
bool startScanning() { return false; } bool startScanning() { return false; }
bool stopScanning() { return false; } bool stopScanning() { return false; }
bool scanNow() { return false; } bool scanNow() { return false; }
}; };
std::unique_ptr<IHIDListener> IHIDListenerNew(DeviceFinder& finder) std::unique_ptr<IHIDListener> IHIDListenerNew(DeviceFinder& finder) { return std::make_unique<HIDListenerNX>(finder); }
{
return std::make_unique<HIDListenerNX>(finder);
}
} } // namespace boo

View File

@ -2,25 +2,20 @@
#include "boo/inputdev/IHIDListener.hpp" #include "boo/inputdev/IHIDListener.hpp"
#include "boo/inputdev/DeviceFinder.hpp" #include "boo/inputdev/DeviceFinder.hpp"
namespace boo namespace boo {
{
class HIDListenerUWP : public IHIDListener class HIDListenerUWP : public IHIDListener {
{
public: public:
HIDListenerUWP(DeviceFinder& finder) {} HIDListenerUWP(DeviceFinder& finder) {}
/* Automatic device scanning */ /* Automatic device scanning */
bool startScanning() { return false; } bool startScanning() { return false; }
bool stopScanning() { return false; } bool stopScanning() { return false; }
/* Manual device scanning */ /* Manual device scanning */
bool scanNow() { return false; } bool scanNow() { return false; }
}; };
std::unique_ptr<IHIDListener> IHIDListenerNew(DeviceFinder& finder) std::unique_ptr<IHIDListener> IHIDListenerNew(DeviceFinder& finder) { return std::make_unique<HIDListenerUWP>(finder); }
{
return std::make_unique<HIDListenerUWP>(finder);
}
} } // namespace boo

View File

@ -12,122 +12,110 @@
#include <unistd.h> #include <unistd.h>
#include <thread> #include <thread>
namespace boo namespace boo {
{
static udev* UDEV_INST = nullptr; static udev* UDEV_INST = nullptr;
udev* GetUdev() udev* GetUdev() {
{ if (!UDEV_INST)
if (!UDEV_INST) UDEV_INST = udev_new();
UDEV_INST = udev_new(); return UDEV_INST;
return UDEV_INST;
} }
class HIDListenerUdev final : public IHIDListener class HIDListenerUdev final : public IHIDListener {
{ DeviceFinder& m_finder;
DeviceFinder& m_finder;
udev_monitor* m_udevMon; udev_monitor* m_udevMon;
std::thread m_udevThread; std::thread m_udevThread;
bool m_scanningEnabled; bool m_scanningEnabled;
void deviceConnected(udev_device* device) void deviceConnected(udev_device* device) {
{ if (!m_scanningEnabled)
if (!m_scanningEnabled) return;
return;
/* Prevent redundant registration */ /* Prevent redundant registration */
const char* devPath = udev_device_get_syspath(device); const char* devPath = udev_device_get_syspath(device);
if (m_finder._hasToken(devPath)) if (m_finder._hasToken(devPath))
return; return;
/* Filter to USB/BT */ /* Filter to USB/BT */
const char* dt = udev_device_get_devtype(device); const char* dt = udev_device_get_devtype(device);
DeviceType type; DeviceType type;
int vid = 0, pid = 0; int vid = 0, pid = 0;
const char* manuf = nullptr; const char* manuf = nullptr;
const char* product = nullptr; const char* product = nullptr;
if (dt) if (dt) {
{ if (!strcmp(dt, "usb_device"))
if (!strcmp(dt, "usb_device")) type = DeviceType::USB;
type = DeviceType::USB; else if (!strcmp(dt, "bluetooth_device"))
else if (!strcmp(dt, "bluetooth_device")) type = DeviceType::Bluetooth;
type = DeviceType::Bluetooth; else
else return;
return;
udev_list_entry* attrs = udev_device_get_properties_list_entry(device); udev_list_entry* attrs = udev_device_get_properties_list_entry(device);
udev_list_entry* vide = udev_list_entry_get_by_name(attrs, "ID_VENDOR_ID"); udev_list_entry* vide = udev_list_entry_get_by_name(attrs, "ID_VENDOR_ID");
if (vide) if (vide)
vid = strtol(udev_list_entry_get_value(vide), nullptr, 16); vid = strtol(udev_list_entry_get_value(vide), nullptr, 16);
udev_list_entry* pide = udev_list_entry_get_by_name(attrs, "ID_MODEL_ID"); udev_list_entry* pide = udev_list_entry_get_by_name(attrs, "ID_MODEL_ID");
if (pide) if (pide)
pid = strtol(udev_list_entry_get_value(pide), nullptr, 16); pid = strtol(udev_list_entry_get_value(pide), nullptr, 16);
udev_list_entry* manufe = udev_list_entry_get_by_name(attrs, "ID_VENDOR"); udev_list_entry* manufe = udev_list_entry_get_by_name(attrs, "ID_VENDOR");
if (manufe) if (manufe)
manuf = udev_list_entry_get_value(manufe); manuf = udev_list_entry_get_value(manufe);
udev_list_entry* producte = udev_list_entry_get_by_name(attrs, "ID_MODEL"); udev_list_entry* producte = udev_list_entry_get_by_name(attrs, "ID_MODEL");
if (producte) if (producte)
product = udev_list_entry_get_value(producte); product = udev_list_entry_get_value(producte);
} } else if (!strcmp(udev_device_get_subsystem(device), "hidraw")) {
else if (!strcmp(udev_device_get_subsystem(device), "hidraw")) type = DeviceType::HID;
{ udev_device* parent = udev_device_get_parent(device);
type = DeviceType::HID; udev_list_entry* attrs = udev_device_get_properties_list_entry(parent);
udev_device* parent = udev_device_get_parent(device);
udev_list_entry* attrs = udev_device_get_properties_list_entry(parent);
udev_list_entry* hidide = udev_list_entry_get_by_name(attrs, "HID_ID"); udev_list_entry* hidide = udev_list_entry_get_by_name(attrs, "HID_ID");
if (hidide) if (hidide) {
{ const char* hidid = udev_list_entry_get_value(hidide);
const char* hidid = udev_list_entry_get_value(hidide); const char* vids = strchr(hidid, ':') + 1;
const char* vids = strchr(hidid, ':') + 1; const char* pids = strchr(vids, ':') + 1;
const char* pids = strchr(vids, ':') + 1; vid = strtol(vids, nullptr, 16);
vid = strtol(vids, nullptr, 16); pid = strtol(pids, nullptr, 16);
pid = strtol(pids, nullptr, 16); }
}
udev_list_entry* hidnamee = udev_list_entry_get_by_name(attrs, "HID_NAME"); udev_list_entry* hidnamee = udev_list_entry_get_by_name(attrs, "HID_NAME");
if (hidnamee) if (hidnamee) {
{ product = udev_list_entry_get_value(hidnamee);
product = udev_list_entry_get_value(hidnamee); manuf = product;
manuf = product; }
}
/* Get device file */ /* Get device file */
const char* dp = udev_device_get_devnode(device); const char* dp = udev_device_get_devnode(device);
int fd = open(dp, O_RDWR); int fd = open(dp, O_RDWR);
if (fd < 0) if (fd < 0)
return; return;
/* Report descriptor size */ /* Report descriptor size */
int reportDescSize; int reportDescSize;
if (ioctl(fd, HIDIOCGRDESCSIZE, &reportDescSize) == -1) if (ioctl(fd, HIDIOCGRDESCSIZE, &reportDescSize) == -1) {
{ // const char* err = strerror(errno);
//const char* err = strerror(errno); close(fd);
close(fd); return;
return; }
}
/* Get report descriptor */ /* Get report descriptor */
hidraw_report_descriptor reportDesc; hidraw_report_descriptor reportDesc;
reportDesc.size = reportDescSize; reportDesc.size = reportDescSize;
if (ioctl(fd, HIDIOCGRDESC, &reportDesc) == -1) if (ioctl(fd, HIDIOCGRDESC, &reportDesc) == -1) {
{ // const char* err = strerror(errno);
//const char* err = strerror(errno); close(fd);
close(fd); return;
return; }
} close(fd);
close(fd);
std::pair<HIDUsagePage, HIDUsage> usage = std::pair<HIDUsagePage, HIDUsage> usage = HIDParser::GetApplicationUsage(reportDesc.value, reportDesc.size);
HIDParser::GetApplicationUsage(reportDesc.value, reportDesc.size); if (usage.first != HIDUsagePage::GenericDesktop ||
if (usage.first != HIDUsagePage::GenericDesktop || (usage.second != HIDUsage::Joystick && usage.second != HIDUsage::GamePad))
(usage.second != HIDUsage::Joystick && usage.second != HIDUsage::GamePad)) return;
return; }
}
#if 0 #if 0
udev_list_entry* att = nullptr; udev_list_entry* att = nullptr;
@ -140,120 +128,105 @@ class HIDListenerUdev final : public IHIDListener
fprintf(stderr, "\n\n"); fprintf(stderr, "\n\n");
#endif #endif
m_finder._insertToken(std::make_unique<DeviceToken>(type, vid, pid, manuf, product, devPath)); m_finder._insertToken(std::make_unique<DeviceToken>(type, vid, pid, manuf, product, devPath));
} }
void deviceDisconnected(udev_device* device) void deviceDisconnected(udev_device* device) {
{ const char* devPath = udev_device_get_syspath(device);
const char* devPath = udev_device_get_syspath(device); m_finder._removeToken(devPath);
m_finder._removeToken(devPath); }
}
void _udevProc() void _udevProc() {
{ logvisor::RegisterThreadName("Boo udev");
logvisor::RegisterThreadName("Boo udev"); udev_monitor_enable_receiving(m_udevMon);
udev_monitor_enable_receiving(m_udevMon); int fd = udev_monitor_get_fd(m_udevMon);
int fd = udev_monitor_get_fd(m_udevMon); while (true) {
while (true) fd_set fds;
{ FD_ZERO(&fds);
fd_set fds; FD_SET(fd, &fds);
FD_ZERO(&fds); if (pselect(fd + 1, &fds, nullptr, nullptr, nullptr, nullptr) < 0) {
FD_SET(fd, &fds); /* SIGTERM handled here */
if (pselect(fd+1, &fds, nullptr, nullptr, nullptr, nullptr) < 0) if (errno == EINTR)
{ break;
/* SIGTERM handled here */ }
if (errno == EINTR) int oldtype;
break; pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldtype);
} udev_device* dev = udev_monitor_receive_device(m_udevMon);
int oldtype; if (dev) {
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldtype); const char* action = udev_device_get_action(dev);
udev_device* dev = udev_monitor_receive_device(m_udevMon); if (!strcmp(action, "add"))
if (dev) deviceConnected(dev);
{ else if (!strcmp(action, "remove"))
const char* action = udev_device_get_action(dev); deviceDisconnected(dev);
if (!strcmp(action, "add")) udev_device_unref(dev);
deviceConnected(dev); }
else if (!strcmp(action, "remove")) pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldtype);
deviceDisconnected(dev); pthread_testcancel();
udev_device_unref(dev);
}
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldtype);
pthread_testcancel();
}
} }
}
public: public:
HIDListenerUdev(DeviceFinder& finder) HIDListenerUdev(DeviceFinder& finder) : m_finder(finder) {
: m_finder(finder) /* Setup hotplug events */
{ m_udevMon = udev_monitor_new_from_netlink(GetUdev(), "udev");
/* Setup hotplug events */ if (!m_udevMon) {
m_udevMon = udev_monitor_new_from_netlink(GetUdev(), "udev"); fprintf(stderr, "unable to init udev_monitor");
if (!m_udevMon) abort();
{
fprintf(stderr, "unable to init udev_monitor");
abort();
}
udev_monitor_filter_add_match_subsystem_devtype(m_udevMon, "usb", "usb_device");
udev_monitor_filter_add_match_subsystem_devtype(m_udevMon, "bluetooth", "bluetooth_device");
udev_monitor_filter_add_match_subsystem_devtype(m_udevMon, "hidraw", nullptr);
udev_monitor_filter_update(m_udevMon);
/* Initial HID Device Add */
m_scanningEnabled = true;
scanNow();
m_scanningEnabled = false;
/* Start hotplug thread */
m_udevThread = std::thread(std::bind(&HIDListenerUdev::_udevProc, this), this);
} }
udev_monitor_filter_add_match_subsystem_devtype(m_udevMon, "usb", "usb_device");
udev_monitor_filter_add_match_subsystem_devtype(m_udevMon, "bluetooth", "bluetooth_device");
udev_monitor_filter_add_match_subsystem_devtype(m_udevMon, "hidraw", nullptr);
udev_monitor_filter_update(m_udevMon);
~HIDListenerUdev() /* Initial HID Device Add */
{ m_scanningEnabled = true;
pthread_cancel(m_udevThread.native_handle()); scanNow();
if (m_udevThread.joinable()) m_scanningEnabled = false;
m_udevThread.join();
udev_monitor_unref(m_udevMon);
}
/* Automatic device scanning */ /* Start hotplug thread */
bool startScanning() m_udevThread = std::thread(std::bind(&HIDListenerUdev::_udevProc, this), this);
{ }
m_scanningEnabled = true;
return true;
}
bool stopScanning()
{
m_scanningEnabled = false;
return true;
}
/* Manual device scanning */ ~HIDListenerUdev() {
bool scanNow() pthread_cancel(m_udevThread.native_handle());
{ if (m_udevThread.joinable())
udev_enumerate* uenum = udev_enumerate_new(GetUdev()); m_udevThread.join();
udev_enumerate_add_match_subsystem(uenum, "usb"); udev_monitor_unref(m_udevMon);
udev_enumerate_add_match_subsystem(uenum, "bluetooth"); }
udev_enumerate_add_match_subsystem(uenum, "hidraw");
udev_enumerate_scan_devices(uenum);
udev_list_entry* uenumList = udev_enumerate_get_list_entry(uenum);
udev_list_entry* uenumItem;
udev_list_entry_foreach(uenumItem, uenumList)
{
const char* devPath = udev_list_entry_get_name(uenumItem);
udev_device* dev = udev_device_new_from_syspath(UDEV_INST, devPath);
if (dev)
deviceConnected(dev);
udev_device_unref(dev);
}
udev_enumerate_unref(uenum);
return true;
}
/* Automatic device scanning */
bool startScanning() {
m_scanningEnabled = true;
return true;
}
bool stopScanning() {
m_scanningEnabled = false;
return true;
}
/* Manual device scanning */
bool scanNow() {
udev_enumerate* uenum = udev_enumerate_new(GetUdev());
udev_enumerate_add_match_subsystem(uenum, "usb");
udev_enumerate_add_match_subsystem(uenum, "bluetooth");
udev_enumerate_add_match_subsystem(uenum, "hidraw");
udev_enumerate_scan_devices(uenum);
udev_list_entry* uenumList = udev_enumerate_get_list_entry(uenum);
udev_list_entry* uenumItem;
udev_list_entry_foreach(uenumItem, uenumList) {
const char* devPath = udev_list_entry_get_name(uenumItem);
udev_device* dev = udev_device_new_from_syspath(UDEV_INST, devPath);
if (dev)
deviceConnected(dev);
udev_device_unref(dev);
}
udev_enumerate_unref(uenum);
return true;
}
}; };
std::unique_ptr<IHIDListener> IHIDListenerNew(DeviceFinder& finder) std::unique_ptr<IHIDListener> IHIDListenerNew(DeviceFinder& finder) {
{ return std::make_unique<HIDListenerUdev>(finder);
return std::make_unique<HIDListenerUdev>(finder);
} }
} } // namespace boo

View File

@ -18,306 +18,257 @@
#include <hidclass.h> #include <hidclass.h>
#include <Xinput.h> #include <Xinput.h>
namespace boo namespace boo {
{
class HIDListenerWinUSB final : public IHIDListener class HIDListenerWinUSB final : public IHIDListener {
{ DeviceFinder& m_finder;
DeviceFinder& m_finder;
bool m_scanningEnabled; bool m_scanningEnabled;
/* /*
* Reference: https://github.com/pbatard/libwdi/blob/master/libwdi/libwdi.c * Reference: https://github.com/pbatard/libwdi/blob/master/libwdi/libwdi.c
*/ */
void _enumerate(DeviceType type, CONST GUID* TypeGUID, const char* pathFilter) void _enumerate(DeviceType type, CONST GUID* TypeGUID, const char* pathFilter) {
{ /* Don't ask */
/* Don't ask */ static const LPCSTR arPrefix[3] = {"VID_", "PID_", "MI_"};
static const LPCSTR arPrefix[3] = {"VID_", "PID_", "MI_"}; unsigned i, j;
unsigned i, j; CONFIGRET r;
CONFIGRET r; ULONG devpropType;
ULONG devpropType; DWORD reg_type;
DWORD reg_type; HDEVINFO hDevInfo = 0;
HDEVINFO hDevInfo = 0; SP_DEVINFO_DATA DeviceInfoData = {0};
SP_DEVINFO_DATA DeviceInfoData = {0}; DeviceInfoData.cbSize = sizeof(DeviceInfoData);
DeviceInfoData.cbSize = sizeof(DeviceInfoData); SP_DEVICE_INTERFACE_DATA DeviceInterfaceData = {0};
SP_DEVICE_INTERFACE_DATA DeviceInterfaceData = {0}; DeviceInterfaceData.cbSize = sizeof(DeviceInterfaceData);
DeviceInterfaceData.cbSize = sizeof(DeviceInterfaceData); union {
union { SP_DEVICE_INTERFACE_DETAIL_DATA_A wtf;
SP_DEVICE_INTERFACE_DETAIL_DATA_A wtf; CHAR alloc[2048];
CHAR alloc[2048]; } DeviceInterfaceDetailData; /* Stack allocation should be fine for this */
} DeviceInterfaceDetailData; /* Stack allocation should be fine for this */ DeviceInterfaceDetailData.wtf.cbSize = sizeof(DeviceInterfaceDetailData);
DeviceInterfaceDetailData.wtf.cbSize = sizeof(DeviceInterfaceDetailData); CHAR szDeviceInstanceID[MAX_DEVICE_ID_LEN];
CHAR szDeviceInstanceID[MAX_DEVICE_ID_LEN]; LPSTR pszToken, pszNextToken;
LPSTR pszToken, pszNextToken; CHAR szVid[MAX_DEVICE_ID_LEN], szPid[MAX_DEVICE_ID_LEN], szMi[MAX_DEVICE_ID_LEN];
CHAR szVid[MAX_DEVICE_ID_LEN], szPid[MAX_DEVICE_ID_LEN], szMi[MAX_DEVICE_ID_LEN];
/* List all connected HID devices */ /* List all connected HID devices */
hDevInfo = SetupDiGetClassDevs(NULL, 0, 0, DIGCF_PRESENT | DIGCF_ALLCLASSES | DIGCF_DEVICEINTERFACE); hDevInfo = SetupDiGetClassDevs(NULL, 0, 0, DIGCF_PRESENT | DIGCF_ALLCLASSES | DIGCF_DEVICEINTERFACE);
if (hDevInfo == INVALID_HANDLE_VALUE) if (hDevInfo == INVALID_HANDLE_VALUE)
return; return;
for (i=0 ; ; ++i) for (i = 0;; ++i) {
{ if (!SetupDiEnumDeviceInterfaces(hDevInfo, NULL, TypeGUID, i, &DeviceInterfaceData))
if (!SetupDiEnumDeviceInterfaces(hDevInfo, break;
NULL,
TypeGUID,
i,
&DeviceInterfaceData))
break;
DeviceInterfaceDetailData.wtf.cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); DeviceInterfaceDetailData.wtf.cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
if (!SetupDiGetDeviceInterfaceDetailA(hDevInfo, if (!SetupDiGetDeviceInterfaceDetailA(hDevInfo, &DeviceInterfaceData, &DeviceInterfaceDetailData.wtf,
&DeviceInterfaceData, sizeof(DeviceInterfaceDetailData), NULL, &DeviceInfoData))
&DeviceInterfaceDetailData.wtf, continue;
sizeof(DeviceInterfaceDetailData),
NULL,
&DeviceInfoData))
continue;
r = CM_Get_Device_IDA(DeviceInfoData.DevInst, szDeviceInstanceID, MAX_PATH, 0); r = CM_Get_Device_IDA(DeviceInfoData.DevInst, szDeviceInstanceID, MAX_PATH, 0);
if (r != CR_SUCCESS) if (r != CR_SUCCESS)
continue; continue;
/* Retreive the device description as reported by the device itself */ /* Retreive the device description as reported by the device itself */
pszToken = strtok_s(szDeviceInstanceID , "\\#&", &pszNextToken); pszToken = strtok_s(szDeviceInstanceID, "\\#&", &pszNextToken);
szVid[0] = '\0'; szVid[0] = '\0';
szPid[0] = '\0'; szPid[0] = '\0';
szMi[0] = '\0'; szMi[0] = '\0';
while (pszToken != NULL) while (pszToken != NULL) {
{ for (j = 0; j < 3; ++j) {
for (j=0 ; j<3 ; ++j) if (strncmp(pszToken, arPrefix[j], 4) == 0) {
{ switch (j) {
if (strncmp(pszToken, arPrefix[j], 4) == 0) case 0:
{ strcpy_s(szVid, MAX_DEVICE_ID_LEN, pszToken);
switch (j) break;
{ case 1:
case 0: strcpy_s(szPid, MAX_DEVICE_ID_LEN, pszToken);
strcpy_s(szVid, MAX_DEVICE_ID_LEN, pszToken); break;
break; case 2:
case 1: strcpy_s(szMi, MAX_DEVICE_ID_LEN, pszToken);
strcpy_s(szPid, MAX_DEVICE_ID_LEN, pszToken); break;
break; default:
case 2: break;
strcpy_s(szMi, MAX_DEVICE_ID_LEN, pszToken);
break;
default:
break;
}
}
}
pszToken = strtok_s(NULL, "\\#&", &pszNextToken);
} }
}
if (!szVid[0] || !szPid[0])
continue;
unsigned vid = strtol(szVid+4, NULL, 16);
unsigned pid = strtol(szPid+4, NULL, 16);
CHAR productW[1024] = {0};
//CHAR product[1024] = {0};
DWORD productSz = 0;
if (!SetupDiGetDevicePropertyW(hDevInfo, &DeviceInfoData, &DEVPKEY_Device_BusReportedDeviceDesc,
&devpropType, (BYTE*)productW, 1024, &productSz, 0)) {
/* fallback to SPDRP_DEVICEDESC (USB hubs still use it) */
SetupDiGetDeviceRegistryPropertyA(hDevInfo, &DeviceInfoData, SPDRP_DEVICEDESC,
&reg_type, (BYTE*)productW, 1024, &productSz);
}
/* DAFUQ??? Why isn't this really WCHAR??? */
//WideCharToMultiByte(CP_UTF8, 0, productW, -1, product, 1024, nullptr, nullptr);
WCHAR manufW[1024] = L"Someone"; /* Windows Vista and earlier will use this as the vendor */
CHAR manuf[1024] = {0};
DWORD manufSz = 0;
SetupDiGetDevicePropertyW(hDevInfo, &DeviceInfoData, &DEVPKEY_Device_Manufacturer,
&devpropType, (BYTE*)manufW, 1024, &manufSz, 0);
WideCharToMultiByte(CP_UTF8, 0, manufW, -1, manuf, 1024, nullptr, nullptr);
if (type == DeviceType::HID)
{
HANDLE devHnd = CreateFileA(DeviceInterfaceDetailData.wtf.DevicePath,
GENERIC_WRITE | GENERIC_READ,
FILE_SHARE_WRITE | FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
NULL);
if (INVALID_HANDLE_VALUE == devHnd)
continue;
PHIDP_PREPARSED_DATA preparsedData;
if (!HidD_GetPreparsedData(devHnd, &preparsedData))
{
CloseHandle(devHnd);
continue;
}
HIDP_CAPS caps;
HidP_GetCaps(preparsedData, &caps);
HidD_FreePreparsedData(preparsedData);
CloseHandle(devHnd);
/* Filter non joysticks and gamepads */
if (caps.UsagePage != 1 || (caps.Usage != 4 && caps.Usage != 5))
continue;
}
/* Store as a shouting string (to keep hash-lookups consistent) */
CharUpperA(DeviceInterfaceDetailData.wtf.DevicePath);
/* Filter to specific device (provided by hotplug event) */
if (pathFilter && strcmp(pathFilter, DeviceInterfaceDetailData.wtf.DevicePath))
continue;
if (!m_scanningEnabled || m_finder._hasToken(DeviceInterfaceDetailData.wtf.DevicePath))
continue;
/* Whew!! that's a single device enumerated!! */
m_finder._insertToken(std::make_unique<DeviceToken>(
type, vid, pid, manuf, productW,
DeviceInterfaceDetailData.wtf.DevicePath));
} }
pszToken = strtok_s(NULL, "\\#&", &pszNextToken);
}
SetupDiDestroyDeviceInfoList(hDevInfo); if (!szVid[0] || !szPid[0])
} continue;
void _pollDevices(const char* pathFilter) unsigned vid = strtol(szVid + 4, NULL, 16);
{ unsigned pid = strtol(szPid + 4, NULL, 16);
_enumerate(DeviceType::HID, &GUID_DEVINTERFACE_HID, pathFilter);
_enumerate(DeviceType::USB, &GUID_DEVINTERFACE_USB_DEVICE, pathFilter);
}
static XInputPadState ConvertXInputState(const XINPUT_GAMEPAD& pad) CHAR productW[1024] = {0};
{ // CHAR product[1024] = {0};
return {pad.wButtons, pad.bLeftTrigger, pad.bRightTrigger, DWORD productSz = 0;
pad.sThumbLX, pad.sThumbLY, pad.sThumbLY, pad.sThumbRY}; if (!SetupDiGetDevicePropertyW(hDevInfo, &DeviceInfoData, &DEVPKEY_Device_BusReportedDeviceDesc, &devpropType,
} (BYTE*)productW, 1024, &productSz, 0)) {
/* fallback to SPDRP_DEVICEDESC (USB hubs still use it) */
SetupDiGetDeviceRegistryPropertyA(hDevInfo, &DeviceInfoData, SPDRP_DEVICEDESC, &reg_type, (BYTE*)productW, 1024,
&productSz);
}
/* DAFUQ??? Why isn't this really WCHAR??? */
// WideCharToMultiByte(CP_UTF8, 0, productW, -1, product, 1024, nullptr, nullptr);
std::thread m_xinputThread; WCHAR manufW[1024] = L"Someone"; /* Windows Vista and earlier will use this as the vendor */
bool m_xinputRunning = true; CHAR manuf[1024] = {0};
DWORD m_xinputPackets[4] = {DWORD(-1), DWORD(-1), DWORD(-1), DWORD(-1)}; DWORD manufSz = 0;
std::vector<DeviceToken> m_xinputTokens; SetupDiGetDevicePropertyW(hDevInfo, &DeviceInfoData, &DEVPKEY_Device_Manufacturer, &devpropType, (BYTE*)manufW,
void _xinputProc() 1024, &manufSz, 0);
{ WideCharToMultiByte(CP_UTF8, 0, manufW, -1, manuf, 1024, nullptr, nullptr);
m_xinputTokens.reserve(4);
for (int i=0 ; i<4 ; ++i)
m_xinputTokens.emplace_back(DeviceType::XInput, 0, i, "", "", "");
while (m_xinputRunning) if (type == DeviceType::HID) {
{ HANDLE devHnd = CreateFileA(DeviceInterfaceDetailData.wtf.DevicePath, GENERIC_WRITE | GENERIC_READ,
for (int i=0 ; i<4 ; ++i) FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_EXISTING,
{ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
DeviceToken& tok = m_xinputTokens[i]; if (INVALID_HANDLE_VALUE == devHnd)
XINPUT_STATE state; continue;
if (XInputGetState(i, &state) == ERROR_SUCCESS) PHIDP_PREPARSED_DATA preparsedData;
{ if (!HidD_GetPreparsedData(devHnd, &preparsedData)) {
if (state.dwPacketNumber != m_xinputPackets[i]) CloseHandle(devHnd);
{ continue;
if (m_xinputPackets[i] == -1)
m_finder.deviceConnected(tok);
m_xinputPackets[i] = state.dwPacketNumber;
if (tok.m_connectedDev)
{
XInputPad& pad = static_cast<XInputPad&>(*tok.m_connectedDev);
std::lock_guard<std::mutex> lk(pad.m_callbackLock);
if (pad.m_callback)
pad.m_callback->controllerUpdate(pad, ConvertXInputState(state.Gamepad));
}
}
if (tok.m_connectedDev)
{
XInputPad& pad = static_cast<XInputPad&>(*tok.m_connectedDev);
if (pad.m_rumbleRequest[0] != pad.m_rumbleState[0] ||
pad.m_rumbleRequest[1] != pad.m_rumbleState[1])
{
pad.m_rumbleState[0] = pad.m_rumbleRequest[0];
pad.m_rumbleState[1] = pad.m_rumbleRequest[1];
XINPUT_VIBRATION vibe = {pad.m_rumbleRequest[0], pad.m_rumbleRequest[1]};
XInputSetState(i, &vibe);
}
}
}
else if (m_xinputPackets[i] != -1)
{
m_xinputPackets[i] = -1;
if (tok.m_connectedDev)
{
XInputPad& pad = static_cast<XInputPad&>(*tok.m_connectedDev);
pad.deviceDisconnected();
}
m_finder.deviceDisconnected(tok, tok.m_connectedDev.get());
}
}
Sleep(10);
} }
HIDP_CAPS caps;
HidP_GetCaps(preparsedData, &caps);
HidD_FreePreparsedData(preparsedData);
CloseHandle(devHnd);
/* Filter non joysticks and gamepads */
if (caps.UsagePage != 1 || (caps.Usage != 4 && caps.Usage != 5))
continue;
}
/* Store as a shouting string (to keep hash-lookups consistent) */
CharUpperA(DeviceInterfaceDetailData.wtf.DevicePath);
/* Filter to specific device (provided by hotplug event) */
if (pathFilter && strcmp(pathFilter, DeviceInterfaceDetailData.wtf.DevicePath))
continue;
if (!m_scanningEnabled || m_finder._hasToken(DeviceInterfaceDetailData.wtf.DevicePath))
continue;
/* Whew!! that's a single device enumerated!! */
m_finder._insertToken(
std::make_unique<DeviceToken>(type, vid, pid, manuf, productW, DeviceInterfaceDetailData.wtf.DevicePath));
} }
SetupDiDestroyDeviceInfoList(hDevInfo);
}
void _pollDevices(const char* pathFilter) {
_enumerate(DeviceType::HID, &GUID_DEVINTERFACE_HID, pathFilter);
_enumerate(DeviceType::USB, &GUID_DEVINTERFACE_USB_DEVICE, pathFilter);
}
static XInputPadState ConvertXInputState(const XINPUT_GAMEPAD& pad) {
return {pad.wButtons, pad.bLeftTrigger, pad.bRightTrigger, pad.sThumbLX, pad.sThumbLY, pad.sThumbLY, pad.sThumbRY};
}
std::thread m_xinputThread;
bool m_xinputRunning = true;
DWORD m_xinputPackets[4] = {DWORD(-1), DWORD(-1), DWORD(-1), DWORD(-1)};
std::vector<DeviceToken> m_xinputTokens;
void _xinputProc() {
m_xinputTokens.reserve(4);
for (int i = 0; i < 4; ++i)
m_xinputTokens.emplace_back(DeviceType::XInput, 0, i, "", "", "");
while (m_xinputRunning) {
for (int i = 0; i < 4; ++i) {
DeviceToken& tok = m_xinputTokens[i];
XINPUT_STATE state;
if (XInputGetState(i, &state) == ERROR_SUCCESS) {
if (state.dwPacketNumber != m_xinputPackets[i]) {
if (m_xinputPackets[i] == -1)
m_finder.deviceConnected(tok);
m_xinputPackets[i] = state.dwPacketNumber;
if (tok.m_connectedDev) {
XInputPad& pad = static_cast<XInputPad&>(*tok.m_connectedDev);
std::lock_guard<std::mutex> lk(pad.m_callbackLock);
if (pad.m_callback)
pad.m_callback->controllerUpdate(pad, ConvertXInputState(state.Gamepad));
}
}
if (tok.m_connectedDev) {
XInputPad& pad = static_cast<XInputPad&>(*tok.m_connectedDev);
if (pad.m_rumbleRequest[0] != pad.m_rumbleState[0] || pad.m_rumbleRequest[1] != pad.m_rumbleState[1]) {
pad.m_rumbleState[0] = pad.m_rumbleRequest[0];
pad.m_rumbleState[1] = pad.m_rumbleRequest[1];
XINPUT_VIBRATION vibe = {pad.m_rumbleRequest[0], pad.m_rumbleRequest[1]};
XInputSetState(i, &vibe);
}
}
} else if (m_xinputPackets[i] != -1) {
m_xinputPackets[i] = -1;
if (tok.m_connectedDev) {
XInputPad& pad = static_cast<XInputPad&>(*tok.m_connectedDev);
pad.deviceDisconnected();
}
m_finder.deviceDisconnected(tok, tok.m_connectedDev.get());
}
}
Sleep(10);
}
}
public: public:
HIDListenerWinUSB(DeviceFinder& finder) HIDListenerWinUSB(DeviceFinder& finder) : m_finder(finder) {
: m_finder(finder) /* Initial HID Device Add */
{ _pollDevices(nullptr);
/* Initial HID Device Add */
_pollDevices(nullptr);
/* XInput arbitration thread */ /* XInput arbitration thread */
for (const DeviceSignature* sig : m_finder.getTypes()) for (const DeviceSignature* sig : m_finder.getTypes()) {
{ if (sig->m_type == DeviceType::XInput) {
if (sig->m_type == DeviceType::XInput) m_xinputThread = std::thread(std::bind(&HIDListenerWinUSB::_xinputProc, this));
{ break;
m_xinputThread = std::thread(std::bind(&HIDListenerWinUSB::_xinputProc, this)); }
break;
}
}
} }
}
~HIDListenerWinUSB() ~HIDListenerWinUSB() {
{ m_xinputRunning = false;
m_xinputRunning = false; if (m_xinputThread.joinable())
if (m_xinputThread.joinable()) m_xinputThread.join();
m_xinputThread.join(); }
}
/* Automatic device scanning */ /* Automatic device scanning */
bool startScanning() bool startScanning() {
{ m_scanningEnabled = true;
m_scanningEnabled = true; return true;
return true; }
} bool stopScanning() {
bool stopScanning() m_scanningEnabled = false;
{ return true;
m_scanningEnabled = false; }
return true;
}
/* Manual device scanning */ /* Manual device scanning */
bool scanNow() bool scanNow() {
{ _pollDevices(nullptr);
_pollDevices(nullptr); return true;
return true; }
}
bool _extDevConnect(const char* path) bool _extDevConnect(const char* path) {
{ char upperPath[1024];
char upperPath[1024]; strcpy_s(upperPath, 1024, path);
strcpy_s(upperPath, 1024, path); CharUpperA(upperPath);
CharUpperA(upperPath); if (m_scanningEnabled && !m_finder._hasToken(upperPath))
if (m_scanningEnabled && !m_finder._hasToken(upperPath)) _pollDevices(upperPath);
_pollDevices(upperPath); return true;
return true; }
}
bool _extDevDisconnect(const char* path) bool _extDevDisconnect(const char* path) {
{ char upperPath[1024];
char upperPath[1024]; strcpy_s(upperPath, 1024, path);
strcpy_s(upperPath, 1024, path); CharUpperA(upperPath);
CharUpperA(upperPath); m_finder._removeToken(upperPath);
m_finder._removeToken(upperPath); return true;
return true; }
}
}; };
std::unique_ptr<IHIDListener> IHIDListenerNew(DeviceFinder& finder) std::unique_ptr<IHIDListener> IHIDListenerNew(DeviceFinder& finder) {
{ return std::make_unique<HIDListenerWinUSB>(finder);
return std::make_unique<HIDListenerWinUSB>(finder);
} }
} } // namespace boo

File diff suppressed because it is too large Load Diff

View File

@ -8,29 +8,27 @@
#include <hidsdi.h> #include <hidsdi.h>
#endif #endif
namespace boo namespace boo {
{
class IHIDDevice : public std::enable_shared_from_this<IHIDDevice> class IHIDDevice : public std::enable_shared_from_this<IHIDDevice> {
{ friend class DeviceBase;
friend class DeviceBase; friend struct DeviceSignature;
friend struct DeviceSignature; virtual void _deviceDisconnected() = 0;
virtual void _deviceDisconnected()=0; virtual bool _sendUSBInterruptTransfer(const uint8_t* data, size_t length) = 0;
virtual bool _sendUSBInterruptTransfer(const uint8_t* data, size_t length)=0; virtual size_t _receiveUSBInterruptTransfer(uint8_t* data, size_t length) = 0;
virtual size_t _receiveUSBInterruptTransfer(uint8_t* data, size_t length)=0;
#if _WIN32 #if _WIN32
#if !WINDOWS_STORE #if !WINDOWS_STORE
virtual const PHIDP_PREPARSED_DATA _getReportDescriptor()=0; virtual const PHIDP_PREPARSED_DATA _getReportDescriptor() = 0;
#endif #endif
#else #else
virtual std::vector<uint8_t> _getReportDescriptor()=0; virtual std::vector<uint8_t> _getReportDescriptor() = 0;
#endif #endif
virtual bool _sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message)=0; virtual bool _sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message) = 0;
virtual size_t _receiveHIDReport(uint8_t* data, size_t length, HIDReportType tp, uint32_t message)=0; virtual size_t _receiveHIDReport(uint8_t* data, size_t length, HIDReportType tp, uint32_t message) = 0;
virtual void _startThread()=0; virtual void _startThread() = 0;
public: public:
virtual ~IHIDDevice() = default; virtual ~IHIDDevice() = default;
}; };
} } // namespace boo

View File

@ -7,113 +7,97 @@
#include <utility> #include <utility>
/// A smart pointer that can manage the lifecycle of IOKit objects. /// A smart pointer that can manage the lifecycle of IOKit objects.
template<typename T> template <typename T>
class IOObjectPointer { class IOObjectPointer {
public: public:
IOObjectPointer() : storage(0) { } IOObjectPointer() : storage(0) {}
IOObjectPointer(T pointer) : storage(toStorageType(pointer)) { IOObjectPointer(T pointer) : storage(toStorageType(pointer)) {
if (storage) { if (storage) {
IOObjectRetain(storage); IOObjectRetain(storage);
}
} }
}
IOObjectPointer(const IOObjectPointer & other) : storage(other.storage) { IOObjectPointer(const IOObjectPointer& other) : storage(other.storage) {
if (io_object_t ptr = storage) { if (io_object_t ptr = storage) {
IOObjectRetain(ptr); IOObjectRetain(ptr);
}
} }
IOObjectPointer& operator=(const IOObjectPointer & other) { }
if (io_object_t pointer = storage) { IOObjectPointer& operator=(const IOObjectPointer& other) {
IOObjectRelease(pointer); if (io_object_t pointer = storage) {
} IOObjectRelease(pointer);
storage = other.storage;
if (io_object_t ptr = storage) {
IOObjectRetain(ptr);
}
return *this;
} }
storage = other.storage;
if (io_object_t ptr = storage) {
IOObjectRetain(ptr);
}
return *this;
}
IOObjectPointer(IOObjectPointer && other) : storage(std::exchange(other.storage, 0)) { } IOObjectPointer(IOObjectPointer&& other) : storage(std::exchange(other.storage, 0)) {}
~IOObjectPointer() { ~IOObjectPointer() {
if (io_object_t pointer = storage) { if (io_object_t pointer = storage) {
IOObjectRelease(pointer); IOObjectRelease(pointer);
}
} }
}
static inline IOObjectPointer<T> adopt(T ptr) { static inline IOObjectPointer<T> adopt(T ptr) { return IOObjectPointer<T>(ptr, IOObjectPointer<T>::Adopt); }
return IOObjectPointer<T>(ptr, IOObjectPointer<T>::Adopt);
}
T get() const { T get() const { return fromStorageType(storage); }
return fromStorageType(storage); io_object_t* operator&() {
if (io_object_t pointer = storage) {
IOObjectRelease(pointer);
} }
io_object_t* operator&() return &storage;
{ }
if (io_object_t pointer = storage) { operator bool() const { return storage != 0; }
IOObjectRelease(pointer);
}
return &storage;
}
operator bool() const { return storage != 0; }
private: private:
io_object_t storage; io_object_t storage;
enum AdoptTag { Adopt }; enum AdoptTag { Adopt };
IOObjectPointer(T ptr, AdoptTag) : storage(toStorageType(ptr)) { } IOObjectPointer(T ptr, AdoptTag) : storage(toStorageType(ptr)) {}
inline io_object_t toStorageType(io_object_t ptr) const { inline io_object_t toStorageType(io_object_t ptr) const { return (io_object_t)ptr; }
return (io_object_t)ptr;
}
inline T fromStorageType(io_object_t pointer) const { inline T fromStorageType(io_object_t pointer) const { return (T)pointer; }
return (T)pointer;
}
void swap(IOObjectPointer &other) { void swap(IOObjectPointer& other) { std::swap(storage, other.storage); }
std::swap(storage, other.storage);
}
}; };
/// A smart pointer that can manage the lifecycle of IOKit plugin objects. /// A smart pointer that can manage the lifecycle of IOKit plugin objects.
class IOCFPluginPointer { class IOCFPluginPointer {
public: public:
IOCFPluginPointer() : _storage(nullptr) { } IOCFPluginPointer() : _storage(nullptr) {}
IOCFPluginPointer(const IOCFPluginPointer & other) = delete; IOCFPluginPointer(const IOCFPluginPointer& other) = delete;
IOCFPluginPointer(IOCFPluginPointer && other) : _storage(std::exchange(other._storage, nullptr)) { } IOCFPluginPointer(IOCFPluginPointer&& other) : _storage(std::exchange(other._storage, nullptr)) {}
~IOCFPluginPointer() { ~IOCFPluginPointer() {
if (IOCFPlugInInterface** pointer = _storage) { if (IOCFPlugInInterface** pointer = _storage) {
IODestroyPlugInInterface(pointer); IODestroyPlugInInterface(pointer);
}
} }
}
IOCFPlugInInterface*** operator&() IOCFPlugInInterface*** operator&() {
{ if (IOCFPlugInInterface** pointer = _storage) {
if (IOCFPlugInInterface** pointer = _storage) { IODestroyPlugInInterface(pointer);
IODestroyPlugInInterface(pointer);
}
return &_storage;
} }
return &_storage;
}
HRESULT As(LPVOID* p, CFUUIDRef uuid) const HRESULT As(LPVOID* p, CFUUIDRef uuid) const {
{ (*_storage)->AddRef(_storage); // Needed for some reason
(*_storage)->AddRef(_storage); // Needed for some reason return (*_storage)->QueryInterface(_storage, CFUUIDGetUUIDBytes(uuid), p);
return (*_storage)->QueryInterface(_storage, CFUUIDGetUUIDBytes(uuid), p); }
}
operator bool() const { return _storage != nullptr; } operator bool() const { return _storage != nullptr; }
IOCFPlugInInterface** storage() const { return _storage; } IOCFPlugInInterface** storage() const { return _storage; }
private: private:
IOCFPlugInInterface** _storage; IOCFPlugInInterface** _storage;
void swap(IOCFPluginPointer &other) { void swap(IOCFPluginPointer& other) { std::swap(_storage, other._storage); }
std::swap(_storage, other._storage);
}
}; };

View File

@ -1,56 +1,44 @@
#include "boo/inputdev/NintendoPowerA.hpp" #include "boo/inputdev/NintendoPowerA.hpp"
#include "boo/inputdev/DeviceSignature.hpp" #include "boo/inputdev/DeviceSignature.hpp"
#include <memory.h> #include <memory.h>
namespace boo namespace boo {
{
NintendoPowerA::NintendoPowerA(DeviceToken* token) NintendoPowerA::NintendoPowerA(DeviceToken* token)
: TDeviceBase<INintendoPowerACallback>(dev_typeid(NintendoPowerA), token) : TDeviceBase<INintendoPowerACallback>(dev_typeid(NintendoPowerA), token) {}
{
} NintendoPowerA::~NintendoPowerA() {}
NintendoPowerA::~NintendoPowerA() void NintendoPowerA::deviceDisconnected() {
{ std::lock_guard<std::mutex> lk(m_callbackLock);
} if (m_callback)
m_callback->controllerDisconnected();
void NintendoPowerA::deviceDisconnected()
{
std::lock_guard<std::mutex> lk(m_callbackLock);
if (m_callback)
m_callback->controllerDisconnected();
} }
void NintendoPowerA::initialCycle() {} void NintendoPowerA::initialCycle() {}
void NintendoPowerA::transferCycle() void NintendoPowerA::transferCycle() {
{ uint8_t payload[8];
uint8_t payload[8]; size_t recvSz = receiveUSBInterruptTransfer(payload, sizeof(payload));
size_t recvSz = receiveUSBInterruptTransfer(payload, sizeof(payload)); if (recvSz != 8)
if (recvSz != 8) return;
return;
NintendoPowerAState state = *reinterpret_cast<NintendoPowerAState*>(&payload); NintendoPowerAState state = *reinterpret_cast<NintendoPowerAState*>(&payload);
std::lock_guard<std::mutex> lk(m_callbackLock); std::lock_guard<std::mutex> lk(m_callbackLock);
if (state != m_last && m_callback) if (state != m_last && m_callback)
m_callback->controllerUpdate(state); m_callback->controllerUpdate(state);
m_last = state; m_last = state;
} }
void NintendoPowerA::finalCycle() {} void NintendoPowerA::finalCycle() {}
void NintendoPowerA::receivedHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message) void NintendoPowerA::receivedHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message) {}
{
bool NintendoPowerAState::operator==(const NintendoPowerAState& other) {
return !memcmp(this, &other, sizeof(NintendoPowerAState));
} }
bool NintendoPowerAState::operator==(const NintendoPowerAState &other) bool NintendoPowerAState::operator!=(const NintendoPowerAState& other) {
{ return memcmp(this, &other, sizeof(NintendoPowerAState));
return !memcmp(this, &other, sizeof(NintendoPowerAState));
} }
bool NintendoPowerAState::operator!=(const NintendoPowerAState &other) } // namespace boo
{
return memcmp(this, &other, sizeof(NintendoPowerAState));
}
}

View File

@ -12,32 +12,28 @@
#include <unordered_map> #include <unordered_map>
#include <mutex> #include <mutex>
namespace boo namespace boo {
{
class IWindow; class IWindow;
struct MetalContext struct MetalContext {
{ id<MTLDevice> m_dev = nullptr;
id<MTLDevice> m_dev = nullptr; id<MTLCommandQueue> m_q = nullptr;
id<MTLCommandQueue> m_q = nullptr; struct Window {
struct Window CAMetalLayer* m_metalLayer = nullptr;
{ std::mutex m_resizeLock;
CAMetalLayer* m_metalLayer = nullptr; bool m_needsResize;
std::mutex m_resizeLock; CGSize m_size;
bool m_needsResize; };
CGSize m_size; std::unordered_map<IWindow*, Window> m_windows;
}; uint32_t m_sampleCount = 1;
std::unordered_map<IWindow*, Window> m_windows; uint32_t m_anisotropy = 1;
uint32_t m_sampleCount = 1; MTLPixelFormat m_pixelFormat = MTLPixelFormatBGRA8Unorm;
uint32_t m_anisotropy = 1;
MTLPixelFormat m_pixelFormat = MTLPixelFormatBGRA8Unorm;
}; };
} } // namespace boo
#else #else
namespace boo namespace boo {
{ struct MetalContext {};
struct MetalContext {}; } // namespace boo
}
#endif #endif
#endif // __APPLE__ #endif // __APPLE__

View File

@ -7,133 +7,95 @@
#include <switch.h> #include <switch.h>
namespace boo namespace boo {
{
static logvisor::Module Log("boo::NXApplication"); static logvisor::Module Log("boo::NXApplication");
std::shared_ptr<IWindow> _WindowNXNew(std::string_view title, NXContext* nxCtx); std::shared_ptr<IWindow> _WindowNXNew(std::string_view title, NXContext* nxCtx);
class ApplicationNX : public IApplication class ApplicationNX : public IApplication {
{ IApplicationCallback& m_callback;
IApplicationCallback& m_callback; const std::string m_uniqueName;
const std::string m_uniqueName; const std::string m_friendlyName;
const std::string m_friendlyName; const std::string m_pname;
const std::string m_pname; const std::vector<std::string> m_args;
const std::vector<std::string> m_args;
NXContext m_nxCtx; NXContext m_nxCtx;
void _deletedWindow(IWindow* window) {} void _deletedWindow(IWindow* window) {}
public: public:
ApplicationNX(IApplicationCallback& callback, ApplicationNX(IApplicationCallback& callback, std::string_view uniqueName, std::string_view friendlyName,
std::string_view uniqueName, std::string_view pname, const std::vector<std::string>& args, std::string_view gfxApi, uint32_t samples,
std::string_view friendlyName, uint32_t anisotropy, bool deepColor, bool singleInstance)
std::string_view pname, : m_callback(callback), m_uniqueName(uniqueName), m_friendlyName(friendlyName), m_pname(pname), m_args(args) {}
const std::vector<std::string>& args,
std::string_view gfxApi,
uint32_t samples,
uint32_t anisotropy,
bool deepColor,
bool singleInstance)
: m_callback(callback),
m_uniqueName(uniqueName),
m_friendlyName(friendlyName),
m_pname(pname),
m_args(args)
{}
EPlatformType getPlatformType() const { return EPlatformType::NX; } EPlatformType getPlatformType() const { return EPlatformType::NX; }
int run() int run() {
{ /* Spawn client thread */
/* Spawn client thread */ int clientReturn = INT_MIN;
int clientReturn = INT_MIN; std::mutex initmt;
std::mutex initmt; std::condition_variable initcv;
std::condition_variable initcv; std::unique_lock<std::mutex> outerLk(initmt);
std::unique_lock<std::mutex> outerLk(initmt); std::thread clientThread([&]() {
std::thread clientThread([&]() std::unique_lock<std::mutex> innerLk(initmt);
{ innerLk.unlock();
std::unique_lock<std::mutex> innerLk(initmt); initcv.notify_one();
innerLk.unlock(); std::string thrName = std::string(getFriendlyName()) + " Client";
initcv.notify_one(); logvisor::RegisterThreadName(thrName.c_str());
std::string thrName = std::string(getFriendlyName()) + " Client"; clientReturn = m_callback.appMain(this);
logvisor::RegisterThreadName(thrName.c_str()); });
clientReturn = m_callback.appMain(this); initcv.wait(outerLk);
});
initcv.wait(outerLk);
// Main graphics loop // Main graphics loop
while (clientReturn == INT_MIN && appletMainLoop()) while (clientReturn == INT_MIN && appletMainLoop()) {
{ // Get and process input
// Get and process input hidScanInput();
hidScanInput(); u32 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
u32 kDown = hidKeysDown(CONTROLLER_P1_AUTO); if (kDown & KEY_PLUS)
if (kDown & KEY_PLUS) break;
break;
}
m_callback.appQuitting(this);
if (clientThread.joinable())
clientThread.join();
return 0;
} }
std::string_view getUniqueName() const m_callback.appQuitting(this);
{ if (clientThread.joinable())
return m_uniqueName; clientThread.join();
}
std::string_view getFriendlyName() const return 0;
{ }
return m_friendlyName;
}
std::string_view getProcessName() const std::string_view getUniqueName() const { return m_uniqueName; }
{
return m_pname;
}
const std::vector<std::string>& getArgs() const std::string_view getFriendlyName() const { return m_friendlyName; }
{
return m_args;
}
std::shared_ptr<IWindow> m_window; std::string_view getProcessName() const { return m_pname; }
std::shared_ptr<IWindow> newWindow(std::string_view title)
{ const std::vector<std::string>& getArgs() const { return m_args; }
if (m_window)
Log.report(logvisor::Fatal, "Only 1 window allowed on NX"); std::shared_ptr<IWindow> m_window;
m_window = _WindowNXNew(title, &m_nxCtx); std::shared_ptr<IWindow> newWindow(std::string_view title) {
return m_window; if (m_window)
} Log.report(logvisor::Fatal, "Only 1 window allowed on NX");
m_window = _WindowNXNew(title, &m_nxCtx);
return m_window;
}
}; };
IApplication* APP = nullptr; IApplication* APP = nullptr;
int ApplicationRun(IApplication::EPlatformType platform, int ApplicationRun(IApplication::EPlatformType platform, IApplicationCallback& cb, SystemStringView uniqueName,
IApplicationCallback& cb, SystemStringView friendlyName, SystemStringView pname, const std::vector<SystemString>& args,
SystemStringView uniqueName, std::string_view gfxApi, uint32_t samples, uint32_t anisotropy, bool deepColor,
SystemStringView friendlyName, bool singleInstance) {
SystemStringView pname, std::string thrName = std::string(friendlyName) + " Main Thread";
const std::vector<SystemString>& args, logvisor::RegisterThreadName(thrName.c_str());
std::string_view gfxApi,
uint32_t samples,
uint32_t anisotropy,
bool deepColor,
bool singleInstance)
{
std::string thrName = std::string(friendlyName) + " Main Thread";
logvisor::RegisterThreadName(thrName.c_str());
if (APP) if (APP)
return 1; return 1;
APP = new ApplicationNX(cb, uniqueName, friendlyName, pname, args, gfxApi, APP = new ApplicationNX(cb, uniqueName, friendlyName, pname, args, gfxApi, samples, anisotropy, deepColor,
samples, anisotropy, deepColor, singleInstance); singleInstance);
int ret = APP->run(); int ret = APP->run();
delete APP; delete APP;
APP = nullptr; APP = nullptr;
return ret; return ret;
} }
} } // namespace boo

View File

@ -5,116 +5,111 @@
#include <switch.h> #include <switch.h>
namespace boo namespace boo {
{
std::unique_ptr<IGraphicsCommandQueue> _NewNXCommandQueue(NXContext* ctx, IGraphicsContext* parent); std::unique_ptr<IGraphicsCommandQueue> _NewNXCommandQueue(NXContext* ctx, IGraphicsContext* parent);
std::unique_ptr<IGraphicsDataFactory> _NewNXDataFactory(IGraphicsContext* parent, NXContext* ctx); std::unique_ptr<IGraphicsDataFactory> _NewNXDataFactory(IGraphicsContext* parent, NXContext* ctx);
struct GraphicsContextNX : IGraphicsContext struct GraphicsContextNX : IGraphicsContext {
{ NXContext* m_nxCtx;
NXContext* m_nxCtx; std::unique_ptr<IGraphicsDataFactory> m_dataFactory;
std::unique_ptr<IGraphicsDataFactory> m_dataFactory; std::unique_ptr<IGraphicsCommandQueue> m_commandQueue;
std::unique_ptr<IGraphicsCommandQueue> m_commandQueue;
public: public:
explicit GraphicsContextNX(NXContext* nxCtx) explicit GraphicsContextNX(NXContext* nxCtx) : m_nxCtx(nxCtx) {
: m_nxCtx(nxCtx) m_dataFactory = _NewNXDataFactory(this, nxCtx);
{ m_commandQueue = _NewNXCommandQueue(nxCtx, this);
m_dataFactory = _NewNXDataFactory(this, nxCtx); }
m_commandQueue = _NewNXCommandQueue(nxCtx, this);
}
EGraphicsAPI getAPI() const { return EGraphicsAPI::NX; } EGraphicsAPI getAPI() const { return EGraphicsAPI::NX; }
EPixelFormat getPixelFormat() const { return EPixelFormat::RGBA8; } EPixelFormat getPixelFormat() const { return EPixelFormat::RGBA8; }
void setPixelFormat(EPixelFormat pf) {} void setPixelFormat(EPixelFormat pf) {}
bool initializeContext(void* handle) { return m_nxCtx->initialize(); } bool initializeContext(void* handle) { return m_nxCtx->initialize(); }
void makeCurrent() {} void makeCurrent() {}
void postInit() {} void postInit() {}
void present() {} void present() {}
IGraphicsCommandQueue* getCommandQueue() { return m_commandQueue.get(); } IGraphicsCommandQueue* getCommandQueue() { return m_commandQueue.get(); }
IGraphicsDataFactory* getDataFactory() { return m_dataFactory.get(); } IGraphicsDataFactory* getDataFactory() { return m_dataFactory.get(); }
IGraphicsDataFactory* getMainContextDataFactory() { return m_dataFactory.get(); } IGraphicsDataFactory* getMainContextDataFactory() { return m_dataFactory.get(); }
IGraphicsDataFactory* getLoadContextDataFactory() { return m_dataFactory.get(); } IGraphicsDataFactory* getLoadContextDataFactory() { return m_dataFactory.get(); }
}; };
class WindowNX : public IWindow class WindowNX : public IWindow {
{ std::string m_title;
std::string m_title; std::unique_ptr<GraphicsContextNX> m_gfxCtx;
std::unique_ptr<GraphicsContextNX> m_gfxCtx; IWindowCallback* m_callback = nullptr;
IWindowCallback* m_callback = nullptr;
public: public:
WindowNX(std::string_view title, NXContext* nxCtx) WindowNX(std::string_view title, NXContext* nxCtx) : m_title(title), m_gfxCtx(new GraphicsContextNX(nxCtx)) {
: m_title(title), m_gfxCtx(new GraphicsContextNX(nxCtx)) m_gfxCtx->initializeContext(nullptr);
{ }
m_gfxCtx->initializeContext(nullptr);
}
void setCallback(IWindowCallback* cb) { m_callback = cb; } void setCallback(IWindowCallback* cb) { m_callback = cb; }
void closeWindow() {} void closeWindow() {}
void showWindow() {} void showWindow() {}
void hideWindow() {} void hideWindow() {}
SystemString getTitle() { return m_title; } SystemString getTitle() { return m_title; }
void setTitle(SystemStringView title) { m_title = title; } void setTitle(SystemStringView title) { m_title = title; }
void setCursor(EMouseCursor cursor) {} void setCursor(EMouseCursor cursor) {}
void setWaitCursor(bool wait) {} void setWaitCursor(bool wait) {}
void setWindowFrameDefault() {} void setWindowFrameDefault() {}
void getWindowFrame(float& xOut, float& yOut, float& wOut, float& hOut) const void getWindowFrame(float& xOut, float& yOut, float& wOut, float& hOut) const {
{ u32 width, height;
u32 width, height; gfxGetFramebufferResolution(&width, &height);
gfxGetFramebufferResolution(&width, &height); xOut = 0;
xOut = 0; yOut = 0;
yOut = 0; wOut = width;
wOut = width; hOut = height;
hOut = height; }
} void getWindowFrame(int& xOut, int& yOut, int& wOut, int& hOut) const {
void getWindowFrame(int& xOut, int& yOut, int& wOut, int& hOut) const u32 width, height;
{ gfxGetFramebufferResolution(&width, &height);
u32 width, height; xOut = 0;
gfxGetFramebufferResolution(&width, &height); yOut = 0;
xOut = 0; wOut = width;
yOut = 0; hOut = height;
wOut = width; }
hOut = height; void setWindowFrame(float x, float y, float w, float h) {}
} void setWindowFrame(int x, int y, int w, int h) {}
void setWindowFrame(float x, float y, float w, float h) {} float getVirtualPixelFactor() const { return 1.f; }
void setWindowFrame(int x, int y, int w, int h) {}
float getVirtualPixelFactor() const { return 1.f; }
bool isFullscreen() const { return true; } bool isFullscreen() const { return true; }
void setFullscreen(bool fs) {} void setFullscreen(bool fs) {}
void claimKeyboardFocus(const int coord[2]) {} void claimKeyboardFocus(const int coord[2]) {}
bool clipboardCopy(EClipboardType type, const uint8_t* data, size_t sz) { return false; } bool clipboardCopy(EClipboardType type, const uint8_t* data, size_t sz) { return false; }
std::unique_ptr<uint8_t[]> clipboardPaste(EClipboardType type, size_t& sz) { return {}; } std::unique_ptr<uint8_t[]> clipboardPaste(EClipboardType type, size_t& sz) { return {}; }
void waitForRetrace() {} void waitForRetrace() {}
uintptr_t getPlatformHandle() const { return 0; } uintptr_t getPlatformHandle() const { return 0; }
bool _incomingEvent(void* event) {(void)event; return false;} bool _incomingEvent(void* event) {
void _cleanup() {} (void)event;
return false;
}
void _cleanup() {}
ETouchType getTouchType() const { return ETouchType::Display; } ETouchType getTouchType() const { return ETouchType::Display; }
void setStyle(EWindowStyle style) {} void setStyle(EWindowStyle style) {}
EWindowStyle getStyle() const { return EWindowStyle::None; } EWindowStyle getStyle() const { return EWindowStyle::None; }
void setTouchBarProvider(void*) {} void setTouchBarProvider(void*) {}
IGraphicsCommandQueue* getCommandQueue() { return m_gfxCtx->getCommandQueue(); } IGraphicsCommandQueue* getCommandQueue() { return m_gfxCtx->getCommandQueue(); }
IGraphicsDataFactory* getDataFactory() { return m_gfxCtx->getDataFactory(); } IGraphicsDataFactory* getDataFactory() { return m_gfxCtx->getDataFactory(); }
IGraphicsDataFactory* getMainContextDataFactory() { return m_gfxCtx->getMainContextDataFactory(); } IGraphicsDataFactory* getMainContextDataFactory() { return m_gfxCtx->getMainContextDataFactory(); }
IGraphicsDataFactory* getLoadContextDataFactory() { return m_gfxCtx->getLoadContextDataFactory(); } IGraphicsDataFactory* getLoadContextDataFactory() { return m_gfxCtx->getLoadContextDataFactory(); }
}; };
std::shared_ptr<IWindow> _WindowNXNew(std::string_view title, NXContext* nxCtx) std::shared_ptr<IWindow> _WindowNXNew(std::string_view title, NXContext* nxCtx) {
{ std::shared_ptr<IWindow> ret = std::make_shared<WindowNX>(title, nxCtx);
std::shared_ptr<IWindow> ret = std::make_shared<WindowNX>(title, nxCtx); return ret;
return ret;
} }
} } // namespace boo

View File

@ -25,294 +25,234 @@ PFN_D3D12_SERIALIZE_ROOT_SIGNATURE D3D12SerializeRootSignaturePROC = nullptr;
pD3DCompile D3DCompilePROC = nullptr; pD3DCompile D3DCompilePROC = nullptr;
pD3DCreateBlob D3DCreateBlobPROC = nullptr; pD3DCreateBlob D3DCreateBlobPROC = nullptr;
static bool FindBestD3DCompile() static bool FindBestD3DCompile() {
{ D3DCompilePROC = D3DCompile;
D3DCompilePROC = D3DCompile; D3DCreateBlobPROC = D3DCreateBlob;
D3DCreateBlobPROC = D3DCreateBlob; return D3DCompilePROC != nullptr && D3DCreateBlobPROC != nullptr;
return D3DCompilePROC != nullptr && D3DCreateBlobPROC != nullptr;
} }
namespace boo namespace boo {
{
static logvisor::Module Log("boo::ApplicationUWP"); static logvisor::Module Log("boo::ApplicationUWP");
std::shared_ptr<IWindow> _WindowUWPNew(SystemStringView title, Boo3DAppContextUWP& d3dCtx); std::shared_ptr<IWindow> _WindowUWPNew(SystemStringView title, Boo3DAppContextUWP& d3dCtx);
class ApplicationUWP final : public IApplication class ApplicationUWP final : public IApplication {
{ friend ref class AppView;
friend ref class AppView; IApplicationCallback& m_callback;
IApplicationCallback& m_callback; const SystemString m_uniqueName;
const SystemString m_uniqueName; const SystemString m_friendlyName;
const SystemString m_friendlyName; const SystemString m_pname;
const SystemString m_pname; const std::vector<SystemString> m_args;
const std::vector<SystemString> m_args; std::shared_ptr<IWindow> m_window;
std::shared_ptr<IWindow> m_window; bool m_singleInstance;
bool m_singleInstance; bool m_issuedWindow = false;
bool m_issuedWindow = false;
Boo3DAppContextUWP m_3dCtx; Boo3DAppContextUWP m_3dCtx;
void _deletedWindow(IWindow* window) void _deletedWindow(IWindow* window) {}
{
}
public: public:
ApplicationUWP(IApplicationCallback& callback, SystemStringView uniqueName, SystemStringView friendlyName,
SystemStringView pname, const std::vector<SystemString>& args, bool singleInstance)
: m_callback(callback)
, m_uniqueName(uniqueName)
, m_friendlyName(friendlyName)
, m_pname(pname)
, m_args(args)
, m_singleInstance(singleInstance) {
typedef HRESULT(WINAPI * CreateDXGIFactory1PROC)(REFIID riid, _COM_Outptr_ void** ppFactory);
CreateDXGIFactory1PROC MyCreateDXGIFactory1 = CreateDXGIFactory1;
ApplicationUWP(IApplicationCallback& callback, bool no12 = true;
SystemStringView uniqueName, for (const SystemString& arg : args)
SystemStringView friendlyName, if (!arg.compare(L"--d3d12"))
SystemStringView pname, no12 = false;
const std::vector<SystemString>& args,
bool singleInstance)
: m_callback(callback),
m_uniqueName(uniqueName),
m_friendlyName(friendlyName),
m_pname(pname),
m_args(args),
m_singleInstance(singleInstance)
{
typedef HRESULT(WINAPI*CreateDXGIFactory1PROC)(REFIID riid, _COM_Outptr_ void **ppFactory);
CreateDXGIFactory1PROC MyCreateDXGIFactory1 = CreateDXGIFactory1;
bool no12 = true;
for (const SystemString& arg : args)
if (!arg.compare(L"--d3d12"))
no12 = false;
#if _WIN32_WINNT_WIN10 #if _WIN32_WINNT_WIN10
if (!no12) if (!no12) {
{ if (!FindBestD3DCompile())
if (!FindBestD3DCompile()) Log.report(logvisor::Fatal, "unable to find D3DCompile_[43-47].dll");
Log.report(logvisor::Fatal, "unable to find D3DCompile_[43-47].dll");
D3D12SerializeRootSignaturePROC = D3D12SerializeRootSignature; D3D12SerializeRootSignaturePROC = D3D12SerializeRootSignature;
/* Create device */ /* Create device */
PFN_D3D12_CREATE_DEVICE MyD3D12CreateDevice = D3D12CreateDevice; PFN_D3D12_CREATE_DEVICE MyD3D12CreateDevice = D3D12CreateDevice;
/* Obtain DXGI Factory */ /* Obtain DXGI Factory */
HRESULT hr = MyCreateDXGIFactory1(__uuidof(IDXGIFactory2), &m_3dCtx.m_ctx12.m_dxFactory); HRESULT hr = MyCreateDXGIFactory1(__uuidof(IDXGIFactory2), &m_3dCtx.m_ctx12.m_dxFactory);
if (FAILED(hr)) if (FAILED(hr))
Log.report(logvisor::Fatal, "unable to create DXGI factory"); Log.report(logvisor::Fatal, "unable to create DXGI factory");
/* Adapter */ /* Adapter */
ComPtr<IDXGIAdapter1> ppAdapter; ComPtr<IDXGIAdapter1> ppAdapter;
for (UINT adapterIndex = 0; ; ++adapterIndex) for (UINT adapterIndex = 0;; ++adapterIndex) {
{ ComPtr<IDXGIAdapter1> pAdapter;
ComPtr<IDXGIAdapter1> pAdapter; if (DXGI_ERROR_NOT_FOUND == m_3dCtx.m_ctx12.m_dxFactory->EnumAdapters1(adapterIndex, &pAdapter))
if (DXGI_ERROR_NOT_FOUND == m_3dCtx.m_ctx12.m_dxFactory->EnumAdapters1(adapterIndex, &pAdapter)) break;
break;
// Check to see if the adapter supports Direct3D 12, but don't create the // Check to see if the adapter supports Direct3D 12, but don't create the
// actual device yet. // actual device yet.
if (SUCCEEDED(MyD3D12CreateDevice(pAdapter.Get(), D3D_FEATURE_LEVEL_11_0, _uuidof(ID3D12Device), nullptr))) if (SUCCEEDED(MyD3D12CreateDevice(pAdapter.Get(), D3D_FEATURE_LEVEL_11_0, _uuidof(ID3D12Device), nullptr))) {
{ ppAdapter = std::move(pAdapter);
ppAdapter = std::move(pAdapter); break;
break;
}
}
/* Create device */
hr = ppAdapter ? MyD3D12CreateDevice(ppAdapter.Get(), D3D_FEATURE_LEVEL_11_0, __uuidof(ID3D12Device), &m_3dCtx.m_ctx12.m_dev) : E_FAIL;
if (!FAILED(hr))
{
/* Establish loader objects */
if (FAILED(m_3dCtx.m_ctx12.m_dev->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT,
__uuidof(ID3D12CommandAllocator), &m_3dCtx.m_ctx12.m_loadqalloc)))
Log.report(logvisor::Fatal, "unable to create loader allocator");
D3D12_COMMAND_QUEUE_DESC desc =
{
D3D12_COMMAND_LIST_TYPE_DIRECT,
D3D12_COMMAND_QUEUE_PRIORITY_NORMAL,
D3D12_COMMAND_QUEUE_FLAG_NONE
};
if (FAILED(m_3dCtx.m_ctx12.m_dev->CreateCommandQueue(&desc, __uuidof(ID3D12CommandQueue), &m_3dCtx.m_ctx12.m_loadq)))
Log.report(logvisor::Fatal, "unable to create loader queue");
if (FAILED(m_3dCtx.m_ctx12.m_dev->CreateFence(0, D3D12_FENCE_FLAG_NONE, __uuidof(ID3D12Fence), &m_3dCtx.m_ctx12.m_loadfence)))
Log.report(logvisor::Fatal, "unable to create loader fence");
m_3dCtx.m_ctx12.m_loadfencehandle = CreateEvent(nullptr, FALSE, FALSE, nullptr);
if (FAILED(m_3dCtx.m_ctx12.m_dev->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, m_3dCtx.m_ctx12.m_loadqalloc.Get(),
nullptr, __uuidof(ID3D12GraphicsCommandList), &m_3dCtx.m_ctx12.m_loadlist)))
Log.report(logvisor::Fatal, "unable to create loader list");
Log.report(logvisor::Info, "initialized D3D12 renderer");
return;
}
else
{
/* Some Win10 client HW doesn't support D3D12 (despite being supposedly HW-agnostic) */
m_3dCtx.m_ctx12.m_dev.Reset();
m_3dCtx.m_ctx12.m_dxFactory.Reset();
}
} }
}
/* Create device */
hr = ppAdapter ? MyD3D12CreateDevice(ppAdapter.Get(), D3D_FEATURE_LEVEL_11_0, __uuidof(ID3D12Device),
&m_3dCtx.m_ctx12.m_dev)
: E_FAIL;
if (!FAILED(hr)) {
/* Establish loader objects */
if (FAILED(m_3dCtx.m_ctx12.m_dev->CreateCommandAllocator(
D3D12_COMMAND_LIST_TYPE_DIRECT, __uuidof(ID3D12CommandAllocator), &m_3dCtx.m_ctx12.m_loadqalloc)))
Log.report(logvisor::Fatal, "unable to create loader allocator");
D3D12_COMMAND_QUEUE_DESC desc = {D3D12_COMMAND_LIST_TYPE_DIRECT, D3D12_COMMAND_QUEUE_PRIORITY_NORMAL,
D3D12_COMMAND_QUEUE_FLAG_NONE};
if (FAILED(m_3dCtx.m_ctx12.m_dev->CreateCommandQueue(&desc, __uuidof(ID3D12CommandQueue),
&m_3dCtx.m_ctx12.m_loadq)))
Log.report(logvisor::Fatal, "unable to create loader queue");
if (FAILED(m_3dCtx.m_ctx12.m_dev->CreateFence(0, D3D12_FENCE_FLAG_NONE, __uuidof(ID3D12Fence),
&m_3dCtx.m_ctx12.m_loadfence)))
Log.report(logvisor::Fatal, "unable to create loader fence");
m_3dCtx.m_ctx12.m_loadfencehandle = CreateEvent(nullptr, FALSE, FALSE, nullptr);
if (FAILED(m_3dCtx.m_ctx12.m_dev->CreateCommandList(
0, D3D12_COMMAND_LIST_TYPE_DIRECT, m_3dCtx.m_ctx12.m_loadqalloc.Get(), nullptr,
__uuidof(ID3D12GraphicsCommandList), &m_3dCtx.m_ctx12.m_loadlist)))
Log.report(logvisor::Fatal, "unable to create loader list");
Log.report(logvisor::Info, "initialized D3D12 renderer");
return;
} else {
/* Some Win10 client HW doesn't support D3D12 (despite being supposedly HW-agnostic) */
m_3dCtx.m_ctx12.m_dev.Reset();
m_3dCtx.m_ctx12.m_dxFactory.Reset();
}
}
#endif #endif
{
if (!FindBestD3DCompile())
Log.report(logvisor::Fatal, "unable to find D3DCompile_[43-47].dll");
/* Create device proc */
PFN_D3D11_CREATE_DEVICE MyD3D11CreateDevice = D3D11CreateDevice;
/* Create device */
D3D_FEATURE_LEVEL level = D3D_FEATURE_LEVEL_11_0;
ComPtr<ID3D11Device> tempDev;
ComPtr<ID3D11DeviceContext> tempCtx;
if (FAILED(MyD3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, D3D11_CREATE_DEVICE_FLAGS, &level,
1, D3D11_SDK_VERSION, &tempDev, nullptr, &tempCtx)))
Log.report(logvisor::Fatal, "unable to create D3D11 device");
ComPtr<IDXGIDevice2> device;
if (FAILED(tempDev.As<ID3D11Device1>(&m_3dCtx.m_ctx11.m_dev)) || !m_3dCtx.m_ctx11.m_dev ||
FAILED(tempCtx.As<ID3D11DeviceContext1>(&m_3dCtx.m_ctx11.m_devCtx)) || !m_3dCtx.m_ctx11.m_devCtx ||
FAILED(m_3dCtx.m_ctx11.m_dev.As<IDXGIDevice2>(&device)) || !device)
{
exit(1);
}
/* Obtain DXGI Factory */
ComPtr<IDXGIAdapter> adapter;
device->GetParent(__uuidof(IDXGIAdapter), &adapter);
adapter->GetParent(__uuidof(IDXGIFactory2), &m_3dCtx.m_ctx11.m_dxFactory);
/* Build default sampler here */
CD3D11_SAMPLER_DESC sampDesc(D3D11_DEFAULT);
sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
m_3dCtx.m_ctx11.m_dev->CreateSamplerState(&sampDesc, &m_3dCtx.m_ctx11.m_ss[0]);
sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_BORDER;
sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_BORDER;
sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_BORDER;
m_3dCtx.m_ctx11.m_dev->CreateSamplerState(&sampDesc, &m_3dCtx.m_ctx11.m_ss[1]);
Log.report(logvisor::Info, "initialized D3D11 renderer");
return;
}
Log.report(logvisor::Fatal, "system doesn't support D3D11 or D3D12");
}
EPlatformType getPlatformType() const
{ {
return EPlatformType::UWP; if (!FindBestD3DCompile())
Log.report(logvisor::Fatal, "unable to find D3DCompile_[43-47].dll");
/* Create device proc */
PFN_D3D11_CREATE_DEVICE MyD3D11CreateDevice = D3D11CreateDevice;
/* Create device */
D3D_FEATURE_LEVEL level = D3D_FEATURE_LEVEL_11_0;
ComPtr<ID3D11Device> tempDev;
ComPtr<ID3D11DeviceContext> tempCtx;
if (FAILED(MyD3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, D3D11_CREATE_DEVICE_FLAGS, &level, 1,
D3D11_SDK_VERSION, &tempDev, nullptr, &tempCtx)))
Log.report(logvisor::Fatal, "unable to create D3D11 device");
ComPtr<IDXGIDevice2> device;
if (FAILED(tempDev.As<ID3D11Device1>(&m_3dCtx.m_ctx11.m_dev)) || !m_3dCtx.m_ctx11.m_dev ||
FAILED(tempCtx.As<ID3D11DeviceContext1>(&m_3dCtx.m_ctx11.m_devCtx)) || !m_3dCtx.m_ctx11.m_devCtx ||
FAILED(m_3dCtx.m_ctx11.m_dev.As<IDXGIDevice2>(&device)) || !device) {
exit(1);
}
/* Obtain DXGI Factory */
ComPtr<IDXGIAdapter> adapter;
device->GetParent(__uuidof(IDXGIAdapter), &adapter);
adapter->GetParent(__uuidof(IDXGIFactory2), &m_3dCtx.m_ctx11.m_dxFactory);
/* Build default sampler here */
CD3D11_SAMPLER_DESC sampDesc(D3D11_DEFAULT);
sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
m_3dCtx.m_ctx11.m_dev->CreateSamplerState(&sampDesc, &m_3dCtx.m_ctx11.m_ss[0]);
sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_BORDER;
sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_BORDER;
sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_BORDER;
m_3dCtx.m_ctx11.m_dev->CreateSamplerState(&sampDesc, &m_3dCtx.m_ctx11.m_ss[1]);
Log.report(logvisor::Info, "initialized D3D11 renderer");
return;
} }
Log.report(logvisor::Fatal, "system doesn't support D3D11 or D3D12");
}
std::thread m_clientThread; EPlatformType getPlatformType() const { return EPlatformType::UWP; }
int run()
{
/* Spawn client thread */
int clientReturn = 0;
m_clientThread = std::thread([&]()
{
std::string thrName = WCSTMBS(getFriendlyName().data()) + " Client Thread";
logvisor::RegisterThreadName(thrName.c_str());
clientReturn = m_callback.appMain(this);
});
CoreWindow::GetForCurrentThread()->Activate(); std::thread m_clientThread;
CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessUntilQuit); int run() {
return 0; /* Spawn client thread */
int clientReturn = 0;
m_clientThread = std::thread([&]() {
std::string thrName = WCSTMBS(getFriendlyName().data()) + " Client Thread";
logvisor::RegisterThreadName(thrName.c_str());
clientReturn = m_callback.appMain(this);
});
CoreWindow::GetForCurrentThread()->Activate();
CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessUntilQuit);
return 0;
}
void quit() {
m_callback.appQuitting(this);
if (m_clientThread.joinable())
m_clientThread.join();
}
SystemStringView getUniqueName() const { return m_uniqueName; }
SystemStringView getFriendlyName() const { return m_friendlyName; }
SystemStringView getProcessName() const { return m_pname; }
const std::vector<SystemString>& getArgs() const { return m_args; }
std::shared_ptr<IWindow> newWindow(SystemStringView title, uint32_t sampleCount) {
if (!m_issuedWindow) {
m_issuedWindow = true;
return m_window;
} }
return {};
}
void quit() void _setWindow(CoreWindow ^ window) { m_window = _WindowUWPNew(m_friendlyName, m_3dCtx); }
{
m_callback.appQuitting(this);
if (m_clientThread.joinable())
m_clientThread.join();
}
SystemStringView getUniqueName() const
{
return m_uniqueName;
}
SystemStringView getFriendlyName() const
{
return m_friendlyName;
}
SystemStringView getProcessName() const
{
return m_pname;
}
const std::vector<SystemString>& getArgs() const
{
return m_args;
}
std::shared_ptr<IWindow> newWindow(SystemStringView title, uint32_t sampleCount)
{
if (!m_issuedWindow)
{
m_issuedWindow = true;
return m_window;
}
return {};
}
void _setWindow(CoreWindow^ window)
{
m_window = _WindowUWPNew(m_friendlyName, m_3dCtx);
}
}; };
IApplication* APP = NULL; IApplication* APP = NULL;
ref class AppView sealed : public IFrameworkView ref class AppView sealed : public IFrameworkView {
{ ApplicationUWP m_app;
ApplicationUWP m_app;
internal: internal : AppView(IApplicationCallback& callback, SystemStringView uniqueName, SystemStringView friendlyName,
AppView(IApplicationCallback& callback, SystemStringView pname, const std::vector<SystemString>& args, bool singleInstance)
SystemStringView uniqueName, : m_app(callback, uniqueName, friendlyName, pname, args, singleInstance) {
SystemStringView friendlyName, APP = &m_app;
SystemStringView pname, }
const std::vector<SystemString>& args,
bool singleInstance)
: m_app(callback, uniqueName, friendlyName, pname, args, singleInstance) { APP = &m_app; }
public: public:
virtual void Initialize(CoreApplicationView^ applicationView) virtual void Initialize(CoreApplicationView ^ applicationView) {
{ applicationView->Activated +=
applicationView->Activated += ref new TypedEventHandler<CoreApplicationView^, IActivatedEventArgs^>(this, &AppView::OnActivated); ref new TypedEventHandler<CoreApplicationView ^, IActivatedEventArgs ^>(this, &AppView::OnActivated);
} }
virtual void SetWindow(CoreWindow^ window) virtual void SetWindow(CoreWindow ^ window) { m_app._setWindow(window); }
{
m_app._setWindow(window);
}
virtual void Load(String^ entryPoint) virtual void Load(String ^ entryPoint) {}
{
} virtual void Run() { m_app.run(); }
virtual void Run() virtual void Uninitialize() { m_app.quit(); }
{
m_app.run();
}
virtual void Uninitialize() void OnActivated(CoreApplicationView ^ applicationView, IActivatedEventArgs ^ args) {
{ CoreWindow::GetForCurrentThread()->Activate();
m_app.quit(); }
}
void OnActivated(CoreApplicationView^ applicationView, IActivatedEventArgs^ args)
{
CoreWindow::GetForCurrentThread()->Activate();
}
}; };
IFrameworkView^ ViewProvider::CreateView() IFrameworkView ^ ViewProvider::CreateView() {
{ return ref new AppView(m_appCb, m_uniqueName, m_friendlyName, m_pname, m_args, m_singleInstance);
return ref new AppView(m_appCb, m_uniqueName, m_friendlyName, m_pname, m_args, m_singleInstance);
}
} }
} // namespace boo

View File

@ -33,565 +33,467 @@ static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM l
pD3DCompile D3DCompilePROC = nullptr; pD3DCompile D3DCompilePROC = nullptr;
pD3DCreateBlob D3DCreateBlobPROC = nullptr; pD3DCreateBlob D3DCreateBlobPROC = nullptr;
static bool FindBestD3DCompile() static bool FindBestD3DCompile() {
{ HMODULE d3dCompilelib = LoadLibraryW(L"D3DCompiler_47.dll");
HMODULE d3dCompilelib = LoadLibraryW(L"D3DCompiler_47.dll"); if (!d3dCompilelib) {
if (!d3dCompilelib) d3dCompilelib = LoadLibraryW(L"D3DCompiler_46.dll");
{ if (!d3dCompilelib) {
d3dCompilelib = LoadLibraryW(L"D3DCompiler_46.dll"); d3dCompilelib = LoadLibraryW(L"D3DCompiler_45.dll");
if (!d3dCompilelib) if (!d3dCompilelib) {
{ d3dCompilelib = LoadLibraryW(L"D3DCompiler_44.dll");
d3dCompilelib = LoadLibraryW(L"D3DCompiler_45.dll"); if (!d3dCompilelib) {
if (!d3dCompilelib) d3dCompilelib = LoadLibraryW(L"D3DCompiler_43.dll");
{
d3dCompilelib = LoadLibraryW(L"D3DCompiler_44.dll");
if (!d3dCompilelib)
{
d3dCompilelib = LoadLibraryW(L"D3DCompiler_43.dll");
}
}
} }
}
} }
if (d3dCompilelib) }
{ if (d3dCompilelib) {
D3DCompilePROC = (pD3DCompile)GetProcAddress(d3dCompilelib, "D3DCompile"); D3DCompilePROC = (pD3DCompile)GetProcAddress(d3dCompilelib, "D3DCompile");
D3DCreateBlobPROC = (pD3DCreateBlob)GetProcAddress(d3dCompilelib, "D3DCreateBlob"); D3DCreateBlobPROC = (pD3DCreateBlob)GetProcAddress(d3dCompilelib, "D3DCreateBlob");
return D3DCompilePROC != nullptr && D3DCreateBlobPROC != nullptr; return D3DCompilePROC != nullptr && D3DCreateBlobPROC != nullptr;
} }
return false; return false;
} }
namespace boo namespace boo {
{
static logvisor::Module Log("boo::ApplicationWin32"); static logvisor::Module Log("boo::ApplicationWin32");
Win32Cursors WIN32_CURSORS; Win32Cursors WIN32_CURSORS;
std::shared_ptr<IWindow> _WindowWin32New(SystemStringView title, Boo3DAppContextWin32& d3dCtx); std::shared_ptr<IWindow> _WindowWin32New(SystemStringView title, Boo3DAppContextWin32& d3dCtx);
class ApplicationWin32 final : public IApplication class ApplicationWin32 final : public IApplication {
{ IApplicationCallback& m_callback;
IApplicationCallback& m_callback; const SystemString m_uniqueName;
const SystemString m_uniqueName; const SystemString m_friendlyName;
const SystemString m_friendlyName; const SystemString m_pname;
const SystemString m_pname; const std::vector<SystemString> m_args;
const std::vector<SystemString> m_args; std::unordered_map<HWND, std::weak_ptr<IWindow>> m_allWindows;
std::unordered_map<HWND, std::weak_ptr<IWindow>> m_allWindows;
Boo3DAppContextWin32 m_3dCtx; Boo3DAppContextWin32 m_3dCtx;
#if BOO_HAS_VULKAN #if BOO_HAS_VULKAN
PFN_vkGetInstanceProcAddr m_getVkProc = nullptr; PFN_vkGetInstanceProcAddr m_getVkProc = nullptr;
#endif #endif
void _deletedWindow(IWindow* window) void _deletedWindow(IWindow* window) { m_allWindows.erase(HWND(window->getPlatformHandle())); }
{
m_allWindows.erase(HWND(window->getPlatformHandle()));
}
public: public:
ApplicationWin32(IApplicationCallback& callback, SystemStringView uniqueName, SystemStringView friendlyName,
ApplicationWin32(IApplicationCallback& callback, SystemStringView pname, const std::vector<SystemString>& args, std::string_view gfxApi,
SystemStringView uniqueName, uint32_t samples, uint32_t anisotropy, bool deepColor, bool singleInstance)
SystemStringView friendlyName, : m_callback(callback), m_uniqueName(uniqueName), m_friendlyName(friendlyName), m_pname(pname), m_args(args) {
SystemStringView pname, m_3dCtx.m_ctx11.m_sampleCount = samples;
const std::vector<SystemString>& args, m_3dCtx.m_ctx11.m_anisotropy = anisotropy;
std::string_view gfxApi, m_3dCtx.m_ctx11.m_fbFormat = deepColor ? DXGI_FORMAT_R16G16B16A16_FLOAT : DXGI_FORMAT_R8G8B8A8_UNORM;
uint32_t samples, m_3dCtx.m_ctxOgl.m_glCtx.m_sampleCount = samples;
uint32_t anisotropy, m_3dCtx.m_ctxOgl.m_glCtx.m_anisotropy = anisotropy;
bool deepColor, m_3dCtx.m_ctxOgl.m_glCtx.m_deepColor = deepColor;
bool singleInstance)
: m_callback(callback),
m_uniqueName(uniqueName),
m_friendlyName(friendlyName),
m_pname(pname),
m_args(args)
{
m_3dCtx.m_ctx11.m_sampleCount = samples;
m_3dCtx.m_ctx11.m_anisotropy = anisotropy;
m_3dCtx.m_ctx11.m_fbFormat = deepColor ? DXGI_FORMAT_R16G16B16A16_FLOAT : DXGI_FORMAT_R8G8B8A8_UNORM;
m_3dCtx.m_ctxOgl.m_glCtx.m_sampleCount = samples;
m_3dCtx.m_ctxOgl.m_glCtx.m_anisotropy = anisotropy;
m_3dCtx.m_ctxOgl.m_glCtx.m_deepColor = deepColor;
#if BOO_HAS_VULKAN #if BOO_HAS_VULKAN
g_VulkanContext.m_sampleCountColor = samples; g_VulkanContext.m_sampleCountColor = samples;
g_VulkanContext.m_sampleCountDepth = samples; g_VulkanContext.m_sampleCountDepth = samples;
g_VulkanContext.m_anisotropy = anisotropy; g_VulkanContext.m_anisotropy = anisotropy;
g_VulkanContext.m_deepColor = deepColor; g_VulkanContext.m_deepColor = deepColor;
#endif #endif
HMODULE dxgilib = LoadLibraryW(L"dxgi.dll"); HMODULE dxgilib = LoadLibraryW(L"dxgi.dll");
if (!dxgilib) if (!dxgilib)
Log.report(logvisor::Fatal, "unable to load dxgi.dll"); Log.report(logvisor::Fatal, "unable to load dxgi.dll");
typedef HRESULT(WINAPI*CreateDXGIFactory1PROC)(REFIID riid, _COM_Outptr_ void **ppFactory); typedef HRESULT(WINAPI * CreateDXGIFactory1PROC)(REFIID riid, _COM_Outptr_ void** ppFactory);
CreateDXGIFactory1PROC MyCreateDXGIFactory1 = (CreateDXGIFactory1PROC)GetProcAddress(dxgilib, "CreateDXGIFactory1"); CreateDXGIFactory1PROC MyCreateDXGIFactory1 = (CreateDXGIFactory1PROC)GetProcAddress(dxgilib, "CreateDXGIFactory1");
if (!MyCreateDXGIFactory1) if (!MyCreateDXGIFactory1)
Log.report(logvisor::Fatal, "unable to find CreateDXGIFactory1 in DXGI.dll\n"); Log.report(logvisor::Fatal, "unable to find CreateDXGIFactory1 in DXGI.dll\n");
bool noD3d = false; bool noD3d = false;
#if BOO_HAS_VULKAN #if BOO_HAS_VULKAN
bool useVulkan = false; bool useVulkan = false;
#endif #endif
if (!gfxApi.empty()) if (!gfxApi.empty()) {
{
#if BOO_HAS_VULKAN #if BOO_HAS_VULKAN
if (!gfxApi.compare("Vulkan")) if (!gfxApi.compare("Vulkan")) {
{ noD3d = true;
noD3d = true; useVulkan = true;
useVulkan = true; }
} if (!gfxApi.compare("OpenGL")) {
if (!gfxApi.compare("OpenGL")) noD3d = true;
{ useVulkan = false;
noD3d = true; }
useVulkan = false;
}
#else #else
if (!gfxApi.compare("OpenGL")) if (!gfxApi.compare("OpenGL"))
noD3d = true; noD3d = true;
#endif #endif
} }
for (const SystemString& arg : args) for (const SystemString& arg : args) {
{
#if BOO_HAS_VULKAN #if BOO_HAS_VULKAN
if (!arg.compare(L"--d3d11")) if (!arg.compare(L"--d3d11")) {
{ useVulkan = false;
useVulkan = false; noD3d = false;
noD3d = false; }
} if (!arg.compare(L"--vulkan")) {
if (!arg.compare(L"--vulkan")) noD3d = true;
{ useVulkan = true;
noD3d = true; }
useVulkan = true; if (!arg.compare(L"--gl")) {
} noD3d = true;
if (!arg.compare(L"--gl")) useVulkan = false;
{ }
noD3d = true;
useVulkan = false;
}
#else #else
if (!arg.compare(L"--d3d11")) if (!arg.compare(L"--d3d11"))
noD3d = false; noD3d = false;
if (!arg.compare(L"--gl")) if (!arg.compare(L"--gl"))
noD3d = true; noD3d = true;
#endif #endif
} }
HMODULE d3d11lib = nullptr; HMODULE d3d11lib = nullptr;
if (!noD3d) if (!noD3d)
d3d11lib = LoadLibraryW(L"D3D11.dll"); d3d11lib = LoadLibraryW(L"D3D11.dll");
if (d3d11lib) if (d3d11lib) {
{ if (!FindBestD3DCompile())
if (!FindBestD3DCompile()) Log.report(logvisor::Fatal, "unable to find D3DCompile_[43-47].dll");
Log.report(logvisor::Fatal, "unable to find D3DCompile_[43-47].dll");
/* Create device proc */ /* Create device proc */
PFN_D3D11_CREATE_DEVICE MyD3D11CreateDevice = (PFN_D3D11_CREATE_DEVICE)GetProcAddress(d3d11lib, "D3D11CreateDevice"); PFN_D3D11_CREATE_DEVICE MyD3D11CreateDevice =
if (!MyD3D11CreateDevice) (PFN_D3D11_CREATE_DEVICE)GetProcAddress(d3d11lib, "D3D11CreateDevice");
Log.report(logvisor::Fatal, "unable to find D3D11CreateDevice in D3D11.dll"); if (!MyD3D11CreateDevice)
Log.report(logvisor::Fatal, "unable to find D3D11CreateDevice in D3D11.dll");
/* Create device */ /* Create device */
D3D_FEATURE_LEVEL level = D3D_FEATURE_LEVEL_11_0; D3D_FEATURE_LEVEL level = D3D_FEATURE_LEVEL_11_0;
ComPtr<ID3D11Device> tempDev; ComPtr<ID3D11Device> tempDev;
ComPtr<ID3D11DeviceContext> tempCtx; ComPtr<ID3D11DeviceContext> tempCtx;
if (FAILED(MyD3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, D3D11_CREATE_DEVICE_FLAGS, &level, if (FAILED(MyD3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, D3D11_CREATE_DEVICE_FLAGS, &level, 1,
1, D3D11_SDK_VERSION, &tempDev, nullptr, &tempCtx))) D3D11_SDK_VERSION, &tempDev, nullptr, &tempCtx)))
Log.report(logvisor::Fatal, "unable to create D3D11 device"); Log.report(logvisor::Fatal, "unable to create D3D11 device");
ComPtr<IDXGIDevice2> device; ComPtr<IDXGIDevice2> device;
if (FAILED(tempDev.As<ID3D11Device1>(&m_3dCtx.m_ctx11.m_dev)) || !m_3dCtx.m_ctx11.m_dev || if (FAILED(tempDev.As<ID3D11Device1>(&m_3dCtx.m_ctx11.m_dev)) || !m_3dCtx.m_ctx11.m_dev ||
FAILED(tempCtx.As<ID3D11DeviceContext1>(&m_3dCtx.m_ctx11.m_devCtx)) || !m_3dCtx.m_ctx11.m_devCtx || FAILED(tempCtx.As<ID3D11DeviceContext1>(&m_3dCtx.m_ctx11.m_devCtx)) || !m_3dCtx.m_ctx11.m_devCtx ||
FAILED(m_3dCtx.m_ctx11.m_dev.As<IDXGIDevice2>(&device)) || !device) FAILED(m_3dCtx.m_ctx11.m_dev.As<IDXGIDevice2>(&device)) || !device) {
{ MessageBoxW(nullptr,
MessageBoxW(nullptr, L"Windows 7 users should install 'Platform Update for Windows 7':\n" L"Windows 7 users should install 'Platform Update for Windows 7':\n"
L"https://www.microsoft.com/en-us/download/details.aspx?id=36805", L"https://www.microsoft.com/en-us/download/details.aspx?id=36805",
L"IDXGIDevice2 interface error", MB_OK | MB_ICONERROR); L"IDXGIDevice2 interface error", MB_OK | MB_ICONERROR);
exit(1); exit(1);
} }
/* Obtain DXGI Factory */ /* Obtain DXGI Factory */
ComPtr<IDXGIAdapter> adapter; ComPtr<IDXGIAdapter> adapter;
device->GetParent(__uuidof(IDXGIAdapter), &adapter); device->GetParent(__uuidof(IDXGIAdapter), &adapter);
adapter->GetParent(__uuidof(IDXGIFactory2), &m_3dCtx.m_ctx11.m_dxFactory); adapter->GetParent(__uuidof(IDXGIFactory2), &m_3dCtx.m_ctx11.m_dxFactory);
m_3dCtx.m_ctx11.m_anisotropy = std::min(m_3dCtx.m_ctx11.m_anisotropy, uint32_t(16)); m_3dCtx.m_ctx11.m_anisotropy = std::min(m_3dCtx.m_ctx11.m_anisotropy, uint32_t(16));
/* Build default sampler here */ /* Build default sampler here */
CD3D11_SAMPLER_DESC sampDesc(D3D11_DEFAULT); CD3D11_SAMPLER_DESC sampDesc(D3D11_DEFAULT);
sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP; sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP; sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
sampDesc.Filter = D3D11_FILTER_ANISOTROPIC; sampDesc.Filter = D3D11_FILTER_ANISOTROPIC;
sampDesc.MaxAnisotropy = m_3dCtx.m_ctx11.m_anisotropy; sampDesc.MaxAnisotropy = m_3dCtx.m_ctx11.m_anisotropy;
m_3dCtx.m_ctx11.m_dev->CreateSamplerState(&sampDesc, &m_3dCtx.m_ctx11.m_ss[0]); m_3dCtx.m_ctx11.m_dev->CreateSamplerState(&sampDesc, &m_3dCtx.m_ctx11.m_ss[0]);
sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_BORDER; sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_BORDER;
sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_BORDER; sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_BORDER;
sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_BORDER; sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_BORDER;
m_3dCtx.m_ctx11.m_dev->CreateSamplerState(&sampDesc, &m_3dCtx.m_ctx11.m_ss[1]); m_3dCtx.m_ctx11.m_dev->CreateSamplerState(&sampDesc, &m_3dCtx.m_ctx11.m_ss[1]);
sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_BORDER; sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_BORDER;
sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_BORDER; sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_BORDER;
sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_BORDER; sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_BORDER;
std::fill(std::begin(sampDesc.BorderColor), std::end(sampDesc.BorderColor), 0.f); std::fill(std::begin(sampDesc.BorderColor), std::end(sampDesc.BorderColor), 0.f);
m_3dCtx.m_ctx11.m_dev->CreateSamplerState(&sampDesc, &m_3dCtx.m_ctx11.m_ss[2]); m_3dCtx.m_ctx11.m_dev->CreateSamplerState(&sampDesc, &m_3dCtx.m_ctx11.m_ss[2]);
sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP; sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP; sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP; sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
m_3dCtx.m_ctx11.m_dev->CreateSamplerState(&sampDesc, &m_3dCtx.m_ctx11.m_ss[3]); m_3dCtx.m_ctx11.m_dev->CreateSamplerState(&sampDesc, &m_3dCtx.m_ctx11.m_ss[3]);
sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP; sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP; sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP; sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
sampDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT; sampDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
m_3dCtx.m_ctx11.m_dev->CreateSamplerState(&sampDesc, &m_3dCtx.m_ctx11.m_ss[4]); m_3dCtx.m_ctx11.m_dev->CreateSamplerState(&sampDesc, &m_3dCtx.m_ctx11.m_ss[4]);
Log.report(logvisor::Info, "initialized D3D11 renderer"); Log.report(logvisor::Info, "initialized D3D11 renderer");
return; return;
} }
#if BOO_HAS_VULKAN #if BOO_HAS_VULKAN
if (useVulkan) if (useVulkan) {
{ HMODULE vulkanLib = LoadLibraryW(L"vulkan-1.dll");
HMODULE vulkanLib = LoadLibraryW(L"vulkan-1.dll"); if (vulkanLib) {
if (vulkanLib) m_getVkProc = (PFN_vkGetInstanceProcAddr)GetProcAddress(vulkanLib, "vkGetInstanceProcAddr");
{ if (m_getVkProc) {
m_getVkProc = (PFN_vkGetInstanceProcAddr)GetProcAddress(vulkanLib, "vkGetInstanceProcAddr"); /* Check device support for vulkan */
if (m_getVkProc) if (g_VulkanContext.m_instance == VK_NULL_HANDLE) {
{ auto appName = getUniqueName();
/* Check device support for vulkan */ if (g_VulkanContext.initVulkan(WCSTMBS(appName.data()).c_str(), m_getVkProc)) {
if (g_VulkanContext.m_instance == VK_NULL_HANDLE) if (g_VulkanContext.enumerateDevices()) {
{ /* Obtain DXGI Factory */
auto appName = getUniqueName(); HRESULT hr = MyCreateDXGIFactory1(__uuidof(IDXGIFactory1), &m_3dCtx.m_vulkanDxFactory);
if (g_VulkanContext.initVulkan(WCSTMBS(appName.data()).c_str(), m_getVkProc)) if (FAILED(hr))
{ Log.report(logvisor::Fatal, "unable to create DXGI factory");
if (g_VulkanContext.enumerateDevices())
{
/* Obtain DXGI Factory */
HRESULT hr = MyCreateDXGIFactory1(__uuidof(IDXGIFactory1), &m_3dCtx.m_vulkanDxFactory);
if (FAILED(hr))
Log.report(logvisor::Fatal, "unable to create DXGI factory");
Log.report(logvisor::Info, "initialized Vulkan renderer"); Log.report(logvisor::Info, "initialized Vulkan renderer");
return; return;
} }
}
}
}
} }
}
} }
}
}
#endif #endif
/* Finally try OpenGL */ /* Finally try OpenGL */
{ {
/* Obtain DXGI Factory */ /* Obtain DXGI Factory */
HRESULT hr = MyCreateDXGIFactory1(__uuidof(IDXGIFactory1), &m_3dCtx.m_ctxOgl.m_dxFactory); HRESULT hr = MyCreateDXGIFactory1(__uuidof(IDXGIFactory1), &m_3dCtx.m_ctxOgl.m_dxFactory);
if (FAILED(hr)) if (FAILED(hr))
Log.report(logvisor::Fatal, "unable to create DXGI factory"); Log.report(logvisor::Fatal, "unable to create DXGI factory");
Log.report(logvisor::Info, "initialized OpenGL renderer"); Log.report(logvisor::Info, "initialized OpenGL renderer");
return; return;
}
Log.report(logvisor::Fatal, "system doesn't support Vulkan, D3D11, or OpenGL");
} }
EPlatformType getPlatformType() const Log.report(logvisor::Fatal, "system doesn't support Vulkan, D3D11, or OpenGL");
{ }
return EPlatformType::Win32;
EPlatformType getPlatformType() const { return EPlatformType::Win32; }
LRESULT winHwndHandler(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
/* Lookup boo window instance */
auto search = m_allWindows.find(hwnd);
if (search == m_allWindows.end())
return DefWindowProc(hwnd, uMsg, wParam, lParam);
;
std::shared_ptr<IWindow> window = search->second.lock();
if (!window)
return DefWindowProc(hwnd, uMsg, wParam, lParam);
switch (uMsg) {
case WM_CREATE:
return 0;
case WM_DEVICECHANGE:
return DeviceFinder::winDevChangedHandler(wParam, lParam);
case WM_CLOSE:
case WM_SIZE:
case WM_MOVING:
case WM_SYSKEYDOWN:
case WM_KEYDOWN:
case WM_SYSKEYUP:
case WM_KEYUP:
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_RBUTTONDOWN:
case WM_RBUTTONUP:
case WM_MBUTTONDOWN:
case WM_MBUTTONUP:
case WM_XBUTTONDOWN:
case WM_XBUTTONUP:
case WM_MOUSEMOVE:
case WM_MOUSELEAVE:
case WM_NCMOUSELEAVE:
case WM_MOUSEHOVER:
case WM_NCMOUSEHOVER:
case WM_MOUSEWHEEL:
case WM_MOUSEHWHEEL:
case WM_CHAR:
case WM_UNICHAR: {
HWNDEvent eventData(uMsg, wParam, lParam);
window->_incomingEvent(&eventData);
} }
LRESULT winHwndHandler(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) default:
{ return DefWindowProc(hwnd, uMsg, wParam, lParam);
/* Lookup boo window instance */
auto search = m_allWindows.find(hwnd);
if (search == m_allWindows.end())
return DefWindowProc(hwnd, uMsg, wParam, lParam);;
std::shared_ptr<IWindow> window = search->second.lock();
if (!window)
return DefWindowProc(hwnd, uMsg, wParam, lParam);
switch (uMsg)
{
case WM_CREATE:
return 0;
case WM_DEVICECHANGE:
return DeviceFinder::winDevChangedHandler(wParam, lParam);
case WM_CLOSE:
case WM_SIZE:
case WM_MOVING:
case WM_SYSKEYDOWN:
case WM_KEYDOWN:
case WM_SYSKEYUP:
case WM_KEYUP:
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_RBUTTONDOWN:
case WM_RBUTTONUP:
case WM_MBUTTONDOWN:
case WM_MBUTTONUP:
case WM_XBUTTONDOWN:
case WM_XBUTTONUP:
case WM_MOUSEMOVE:
case WM_MOUSELEAVE:
case WM_NCMOUSELEAVE:
case WM_MOUSEHOVER:
case WM_NCMOUSEHOVER:
case WM_MOUSEWHEEL:
case WM_MOUSEHWHEEL:
case WM_CHAR:
case WM_UNICHAR:
{
HWNDEvent eventData(uMsg, wParam, lParam);
window->_incomingEvent(&eventData);
}
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
} }
}
template <class W> template <class W>
static void DoSetFullscreen(W& win, bool fs) static void DoSetFullscreen(W& win, bool fs) {
{ std::lock_guard<std::mutex> lk(g_nwmt);
std::lock_guard<std::mutex> lk(g_nwmt); if (fs) {
if (fs) win.m_fsStyle = GetWindowLong(win.m_hwnd, GWL_STYLE);
{ win.m_fsExStyle = GetWindowLong(win.m_hwnd, GWL_EXSTYLE);
win.m_fsStyle = GetWindowLong(win.m_hwnd, GWL_STYLE); GetWindowRect(win.m_hwnd, &win.m_fsRect);
win.m_fsExStyle = GetWindowLong(win.m_hwnd, GWL_EXSTYLE);
GetWindowRect(win.m_hwnd, &win.m_fsRect);
SetWindowLong(win.m_hwnd, GWL_STYLE, SetWindowLong(win.m_hwnd, GWL_STYLE, win.m_fsStyle & ~(WS_CAPTION | WS_THICKFRAME));
win.m_fsStyle & ~(WS_CAPTION | WS_THICKFRAME)); SetWindowLong(win.m_hwnd, GWL_EXSTYLE,
SetWindowLong(win.m_hwnd, GWL_EXSTYLE, win.m_fsExStyle & ~(WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE));
win.m_fsExStyle & ~(WS_EX_DLGMODALFRAME |
WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE));
MONITORINFO monitor_info; MONITORINFO monitor_info;
monitor_info.cbSize = sizeof(monitor_info); monitor_info.cbSize = sizeof(monitor_info);
GetMonitorInfo(MonitorFromWindow(win.m_hwnd, MONITOR_DEFAULTTONEAREST), GetMonitorInfo(MonitorFromWindow(win.m_hwnd, MONITOR_DEFAULTTONEAREST), &monitor_info);
&monitor_info); SetWindowPos(win.m_hwnd, NULL, monitor_info.rcMonitor.left, monitor_info.rcMonitor.top,
SetWindowPos(win.m_hwnd, NULL, monitor_info.rcMonitor.left, monitor_info.rcMonitor.top, monitor_info.rcMonitor.right - monitor_info.rcMonitor.left,
monitor_info.rcMonitor.right - monitor_info.rcMonitor.left, monitor_info.rcMonitor.bottom - monitor_info.rcMonitor.top,
monitor_info.rcMonitor.bottom - monitor_info.rcMonitor.top, SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
win.m_fs = true; win.m_fs = true;
} } else {
else SetWindowLong(win.m_hwnd, GWL_STYLE, win.m_fsStyle);
{ SetWindowLong(win.m_hwnd, GWL_EXSTYLE, win.m_fsExStyle);
SetWindowLong(win.m_hwnd, GWL_STYLE, win.m_fsStyle);
SetWindowLong(win.m_hwnd, GWL_EXSTYLE, win.m_fsExStyle);
SetWindowPos(win.m_hwnd, NULL, win.m_fsRect.left, win.m_fsRect.top, SetWindowPos(win.m_hwnd, NULL, win.m_fsRect.left, win.m_fsRect.top, win.m_fsRect.right - win.m_fsRect.left,
win.m_fsRect.right - win.m_fsRect.left, win.m_fsRect.bottom - win.m_fsRect.top, win.m_fsRect.bottom - win.m_fsRect.top, SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
win.m_fs = false; win.m_fs = false;
}
g_nwcv.notify_one();
} }
g_nwcv.notify_one();
}
int run() int run() {
{ g_mainThreadId = GetCurrentThreadId();
g_mainThreadId = GetCurrentThreadId();
/* Spawn client thread */ /* Spawn client thread */
int clientReturn = 0; int clientReturn = 0;
std::thread clientThread([&]() std::thread clientThread([&]() {
{ std::string thrName = WCSTMBS(getFriendlyName().data()) + " Client Thread";
std::string thrName = WCSTMBS(getFriendlyName().data()) + " Client Thread"; logvisor::RegisterThreadName(thrName.c_str());
logvisor::RegisterThreadName(thrName.c_str()); CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE);
CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE); clientReturn = m_callback.appMain(this);
clientReturn = m_callback.appMain(this); PostThreadMessageW(g_mainThreadId, WM_USER + 1, 0, 0);
PostThreadMessageW(g_mainThreadId, WM_USER+1, 0, 0); });
});
/* Pump messages */ /* Pump messages */
MSG msg = {0}; MSG msg = {0};
while (GetMessage(&msg, NULL, 0, 0)) while (GetMessage(&msg, NULL, 0, 0)) {
{ if (!msg.hwnd) {
if (!msg.hwnd) /* PostThreadMessage events */
{ switch (msg.message) {
/* PostThreadMessage events */ case WM_USER: {
switch (msg.message) /* New-window message (coalesced onto main thread) */
{ std::lock_guard<std::mutex> lk(g_nwmt);
case WM_USER: SystemStringView* title = reinterpret_cast<SystemStringView*>(msg.wParam);
{ m_mwret = newWindow(*title);
/* New-window message (coalesced onto main thread) */ g_nwcv.notify_one();
std::lock_guard<std::mutex> lk(g_nwmt); continue;
SystemStringView* title = reinterpret_cast<SystemStringView*>(msg.wParam); }
m_mwret = newWindow(*title); case WM_USER + 1:
g_nwcv.notify_one(); /* Quit message from client thread */
continue; PostQuitMessage(0);
} continue;
case WM_USER+1: case WM_USER + 2:
/* Quit message from client thread */ /* SetCursor call from client thread */
PostQuitMessage(0); SetCursor(HCURSOR(msg.wParam));
continue; continue;
case WM_USER+2: case WM_USER + 3:
/* SetCursor call from client thread */ /* ImmSetOpenStatus call from client thread */
SetCursor(HCURSOR(msg.wParam)); ImmSetOpenStatus(HIMC(msg.wParam), BOOL(msg.lParam));
continue; continue;
case WM_USER+3: case WM_USER + 4:
/* ImmSetOpenStatus call from client thread */ /* ImmSetCompositionWindow call from client thread */
ImmSetOpenStatus(HIMC(msg.wParam), BOOL(msg.lParam)); ImmSetCompositionWindow(HIMC(msg.wParam), LPCOMPOSITIONFORM(msg.lParam));
continue; continue;
case WM_USER+4: case WM_USER + 5:
/* ImmSetCompositionWindow call from client thread */ /* SetFullscreen call for OpenGL window */
ImmSetCompositionWindow(HIMC(msg.wParam), LPCOMPOSITIONFORM(msg.lParam)); DoSetFullscreen(*reinterpret_cast<OGLContext::Window*>(msg.wParam), msg.lParam);
continue; continue;
case WM_USER+5:
/* SetFullscreen call for OpenGL window */
DoSetFullscreen(*reinterpret_cast<OGLContext::Window*>(msg.wParam), msg.lParam);
continue;
#if BOO_HAS_VULKAN #if BOO_HAS_VULKAN
case WM_USER+6: case WM_USER + 6:
/* SetFullscreen call for Vulkan window */ /* SetFullscreen call for Vulkan window */
DoSetFullscreen(*reinterpret_cast<boo::VulkanContext::Window*>(msg.wParam), msg.lParam); DoSetFullscreen(*reinterpret_cast<boo::VulkanContext::Window*>(msg.wParam), msg.lParam);
continue; continue;
#endif #endif
default: break; default:
} break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
} }
}
m_callback.appQuitting(this); TranslateMessage(&msg);
clientThread.join(); DispatchMessage(&msg);
return clientReturn;
} }
~ApplicationWin32() m_callback.appQuitting(this);
{ clientThread.join();
for (auto& p : m_allWindows) return clientReturn;
if (auto w = p.second.lock()) }
w->_cleanup();
~ApplicationWin32() {
for (auto& p : m_allWindows)
if (auto w = p.second.lock())
w->_cleanup();
}
SystemStringView getUniqueName() const { return m_uniqueName; }
SystemStringView getFriendlyName() const { return m_friendlyName; }
SystemStringView getProcessName() const { return m_pname; }
const std::vector<SystemString>& getArgs() const { return m_args; }
std::shared_ptr<IWindow> m_mwret;
std::shared_ptr<IWindow> newWindow(SystemStringView title) {
if (GetCurrentThreadId() != g_mainThreadId) {
std::unique_lock<std::mutex> lk(g_nwmt);
if (!PostThreadMessageW(g_mainThreadId, WM_USER, WPARAM(&title), 0))
Log.report(logvisor::Fatal, "PostThreadMessage error");
g_nwcv.wait(lk);
std::shared_ptr<IWindow> ret = std::move(m_mwret);
m_mwret.reset();
return ret;
} }
SystemStringView getUniqueName() const std::shared_ptr<IWindow> window = _WindowWin32New(title, m_3dCtx);
{ HWND hwnd = HWND(window->getPlatformHandle());
return m_uniqueName; m_allWindows[hwnd] = window;
} return window;
}
SystemStringView getFriendlyName() const
{
return m_friendlyName;
}
SystemStringView getProcessName() const
{
return m_pname;
}
const std::vector<SystemString>& getArgs() const
{
return m_args;
}
std::shared_ptr<IWindow> m_mwret;
std::shared_ptr<IWindow> newWindow(SystemStringView title)
{
if (GetCurrentThreadId() != g_mainThreadId)
{
std::unique_lock<std::mutex> lk(g_nwmt);
if (!PostThreadMessageW(g_mainThreadId, WM_USER, WPARAM(&title), 0))
Log.report(logvisor::Fatal, "PostThreadMessage error");
g_nwcv.wait(lk);
std::shared_ptr<IWindow> ret = std::move(m_mwret);
m_mwret.reset();
return ret;
}
std::shared_ptr<IWindow> window = _WindowWin32New(title, m_3dCtx);
HWND hwnd = HWND(window->getPlatformHandle());
m_allWindows[hwnd] = window;
return window;
}
}; };
IApplication* APP = NULL; IApplication* APP = NULL;
int ApplicationRun(IApplication::EPlatformType platform, int ApplicationRun(IApplication::EPlatformType platform, IApplicationCallback& cb, SystemStringView uniqueName,
IApplicationCallback& cb, SystemStringView friendlyName, SystemStringView pname, const std::vector<SystemString>& args,
SystemStringView uniqueName, std::string_view gfxApi, uint32_t samples, uint32_t anisotropy, bool deepColor,
SystemStringView friendlyName, bool singleInstance) {
SystemStringView pname, std::string thrName = WCSTMBS(friendlyName.data()) + " Main Thread";
const std::vector<SystemString>& args, logvisor::RegisterThreadName(thrName.c_str());
std::string_view gfxApi, if (APP)
uint32_t samples, return 1;
uint32_t anisotropy, if (platform != IApplication::EPlatformType::Win32 && platform != IApplication::EPlatformType::Auto)
bool deepColor, return 1;
bool singleInstance)
{
std::string thrName = WCSTMBS(friendlyName.data()) + " Main Thread";
logvisor::RegisterThreadName(thrName.c_str());
if (APP)
return 1;
if (platform != IApplication::EPlatformType::Win32 &&
platform != IApplication::EPlatformType::Auto)
return 1;
#if _WIN32_WINNT_WINBLUE #if _WIN32_WINNT_WINBLUE
/* HI-DPI support */ /* HI-DPI support */
HMODULE shcoreLib = LoadLibraryW(L"Shcore.dll"); HMODULE shcoreLib = LoadLibraryW(L"Shcore.dll");
if (shcoreLib) if (shcoreLib)
MyGetScaleFactorForMonitor = MyGetScaleFactorForMonitor = (PFN_GetScaleFactorForMonitor)GetProcAddress(shcoreLib, "GetScaleFactorForMonitor");
(PFN_GetScaleFactorForMonitor)GetProcAddress(shcoreLib, "GetScaleFactorForMonitor");
#endif #endif
WIN32_CURSORS.m_arrow = LoadCursor(nullptr, IDC_ARROW); WIN32_CURSORS.m_arrow = LoadCursor(nullptr, IDC_ARROW);
WIN32_CURSORS.m_hResize = LoadCursor(nullptr, IDC_SIZEWE); WIN32_CURSORS.m_hResize = LoadCursor(nullptr, IDC_SIZEWE);
WIN32_CURSORS.m_vResize = LoadCursor(nullptr, IDC_SIZENS); WIN32_CURSORS.m_vResize = LoadCursor(nullptr, IDC_SIZENS);
WIN32_CURSORS.m_ibeam = LoadCursor(nullptr, IDC_IBEAM); WIN32_CURSORS.m_ibeam = LoadCursor(nullptr, IDC_IBEAM);
WIN32_CURSORS.m_crosshairs = LoadCursor(nullptr, IDC_CROSS); WIN32_CURSORS.m_crosshairs = LoadCursor(nullptr, IDC_CROSS);
WIN32_CURSORS.m_wait = LoadCursor(nullptr, IDC_WAIT); WIN32_CURSORS.m_wait = LoadCursor(nullptr, IDC_WAIT);
/* One class for *all* boo windows */ /* One class for *all* boo windows */
WNDCLASS wndClass = WNDCLASS wndClass = {0, WindowProc, 0, 0, GetModuleHandle(nullptr), 0, 0, 0, 0, L"BooWindow"};
{ wndClass.hIcon = LoadIconW(wndClass.hInstance, MAKEINTRESOURCEW(101));
0, wndClass.hCursor = WIN32_CURSORS.m_arrow;
WindowProc, RegisterClassW(&wndClass);
0,
0,
GetModuleHandle(nullptr),
0,
0,
0,
0,
L"BooWindow"
};
wndClass.hIcon = LoadIconW(wndClass.hInstance, MAKEINTRESOURCEW(101));
wndClass.hCursor = WIN32_CURSORS.m_arrow;
RegisterClassW(&wndClass);
APP = new ApplicationWin32(cb, uniqueName, friendlyName, pname, args, APP = new ApplicationWin32(cb, uniqueName, friendlyName, pname, args, gfxApi, samples, anisotropy, deepColor,
gfxApi, samples, anisotropy, deepColor, singleInstance); singleInstance);
int ret = APP->run(); int ret = APP->run();
delete APP; delete APP;
APP = nullptr; APP = nullptr;
return ret; return ret;
} }
} } // namespace boo
static const DEV_BROADCAST_DEVICEINTERFACE HOTPLUG_CONF = static const DEV_BROADCAST_DEVICEINTERFACE HOTPLUG_CONF = {sizeof(DEV_BROADCAST_DEVICEINTERFACE),
{ DBT_DEVTYP_DEVICEINTERFACE};
sizeof(DEV_BROADCAST_DEVICEINTERFACE),
DBT_DEVTYP_DEVICEINTERFACE
};
static bool HOTPLUG_REGISTERED = false; static bool HOTPLUG_REGISTERED = false;
static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
{ if (!HOTPLUG_REGISTERED && uMsg == WM_CREATE) {
if (!HOTPLUG_REGISTERED && uMsg == WM_CREATE) /* Register hotplug notification with windows */
{ RegisterDeviceNotification(hwnd, (LPVOID)&HOTPLUG_CONF,
/* Register hotplug notification with windows */ DEVICE_NOTIFY_WINDOW_HANDLE | DEVICE_NOTIFY_ALL_INTERFACE_CLASSES);
RegisterDeviceNotification(hwnd, (LPVOID)&HOTPLUG_CONF, HOTPLUG_REGISTERED = true;
DEVICE_NOTIFY_WINDOW_HANDLE | DEVICE_NOTIFY_ALL_INTERFACE_CLASSES); }
HOTPLUG_REGISTERED = true; return static_cast<boo::ApplicationWin32*>(boo::APP)->winHwndHandler(hwnd, uMsg, wParam, lParam);
}
return static_cast<boo::ApplicationWin32*>(boo::APP)->winHwndHandler(hwnd, uMsg, wParam, lParam);
} }

Some files were not shown because too many files have changed in this diff Show More