Browse Source

Update Renderer to support drawing in a separate thread and various updates/fixes

* Imagine: Add Gfx::RendererCommands to hold the renderer's draw state
* Imagine: Add Gfx::RendererTask to control the renderer's draw context and thread
* Imagine: Add Gfx::SyncFence to keep resources in sync with draw thread
* Imagine: Add View::prepareDraw() for loading image resources before drawing
* Imagine: Remove Gfx::RenderTarget, obsoleted by setting render to texture in RendererCommands
* Imagine: Use a dedicated thread to manage the main OpenGL context
* Imagine: Optionally use a separate thread and context when drawing with RendererTask
* Imagine: Add support for OpenGL share contexts
* Imagine: Add Gfx::Text::makeGlyphs()
* Imagine: Make Gfx::Texture movable
* Imagine: Make Base::Pipe more robust and movable
* Imagine: Make Semaphore movable
* Imagine: Add EventLoop::stop()
* Imagine: Fix GlibEventLoop with multiple threads using a thread local GMainContext
* Imagine: Add SimpleFrameTimer for use with systems with no OS-level frame time events
* Imagine: Use SimpleFrameTimer on Android versions below 4.1 and remove obsolete eventfd frame timer
* Imagine: Use a thread local variable to get & store the JNIEnv pointer
* Imagine: Use .aar output file instead of .jar and update Gradle scripts for Android
* Imagine: Increase Android x86 minimum SDK level to 16 for NDK 18
* Imagine: Don't manage the framebuffer object in EAGLView since it must be created on the draw thread
* Imagine: Properly disconnect the CAEAGLLayer when deleting the renderbuffer
* Imagine: Update docs to list Xcode 10 requirement due to C++17 features
* Imagine: Remove deprecated X11 GLX code and always use EGL
* Imagine: Update bundled X11 libraries for Pandora build
* Imagine: Add colored logger output for stderr
* Imagine: Add size template parameter to DelegateFunc as DelegateFunc2
* Imagine: Add DelegateFuncSet to easier support multiple delegates with priorities
* EmuFramework: Don't run emulation in draw handler since it now runs on a separate thread
* EmuFramework: Remove dither video option and control it automatically based on the active video effect
* EmuFramework: Add Renderer multithreading option
* EmuFramework: Fix build without CONFIG_VCONTROLS_GAMEPAD
* EmuFramework: Add option to control multithreading in renderer
pull/126/head
Robert Broglia 1 year ago
parent
commit
b589b71c89
100 changed files with 921 additions and 628 deletions
  1. 1
    1
      C64.emu/src/main/Main.cc
  2. 2
    2
      EmuFramework/include/emuframework/ButtonConfigView.hh
  3. 2
    1
      EmuFramework/include/emuframework/CreditsView.hh
  4. 1
    1
      EmuFramework/include/emuframework/EmuInputView.hh
  5. 1
    1
      EmuFramework/include/emuframework/EmuLoadProgressView.hh
  6. 2
    3
      EmuFramework/include/emuframework/EmuSystem.hh
  7. 3
    4
      EmuFramework/include/emuframework/EmuVideo.hh
  8. 1
    1
      EmuFramework/include/emuframework/EmuVideoLayer.hh
  9. 2
    1
      EmuFramework/include/emuframework/EmuView.hh
  10. 1
    1
      EmuFramework/include/emuframework/InputManagerView.hh
  11. 2
    1
      EmuFramework/include/emuframework/MsgPopup.hh
  12. 2
    1
      EmuFramework/include/emuframework/OptionView.hh
  13. 1
    1
      EmuFramework/include/emuframework/TouchConfigView.hh
  14. 5
    5
      EmuFramework/include/emuframework/VController.hh
  15. 3
    4
      EmuFramework/include/emuframework/VideoImageEffect.hh
  16. 1
    1
      EmuFramework/include/emuframework/VideoImageOverlay.hh
  17. 1
    0
      EmuFramework/linux-armv7-pandora.mk
  18. 17
    18
      EmuFramework/src/ButtonConfigView.cc
  19. 2
    4
      EmuFramework/src/ConfigFile.cc
  20. 11
    10
      EmuFramework/src/CreditsView.cc
  21. 122
    95
      EmuFramework/src/EmuApp.cc
  22. 4
    4
      EmuFramework/src/EmuInput.cc
  23. 2
    2
      EmuFramework/src/EmuInputView.cc
  24. 9
    10
      EmuFramework/src/EmuLoadProgressView.cc
  25. 3
    8
      EmuFramework/src/EmuOptions.cc
  26. 2
    3
      EmuFramework/src/EmuOptions.hh
  27. 14
    10
      EmuFramework/src/EmuSystem.cc
  28. 16
    21
      EmuFramework/src/EmuVideo.cc
  29. 20
    19
      EmuFramework/src/EmuVideoLayer.cc
  30. 18
    12
      EmuFramework/src/EmuView.cc
  31. 8
    9
      EmuFramework/src/InputManagerView.cc
  32. 14
    9
      EmuFramework/src/MsgPopup.cc
  33. 43
    20
      EmuFramework/src/OptionView.cc
  34. 2
    2
      EmuFramework/src/Screenshot.cc
  35. 20
    25
      EmuFramework/src/TouchConfigView.cc
  36. 56
    54
      EmuFramework/src/VController.cc
  37. 18
    18
      EmuFramework/src/VideoImageEffect.cc
  38. 7
    7
      EmuFramework/src/VideoImageOverlay.cc
  39. 3
    2
      EmuFramework/src/private.hh
  40. 1
    1
      MSX.emu/src/main/Main.cc
  41. 1
    1
      NEO.emu/src/main/Main.cc
  42. 1
    1
      NGP.emu/src/main/Main.cc
  43. 1
    1
      PCE.emu/src/main/Main.cc
  44. 1
    1
      Saturn.emu/src/main/Main.cc
  45. 1
    1
      Snes9x/src/main/Cheats.cc
  46. 1
    1
      Snes9x/src/main/Main.cc
  47. 4
    4
      imagine/android-java.mk
  48. 2
    2
      imagine/bundle/all/src/fixesproto/common.mk
  49. 2
    2
      imagine/bundle/all/src/inputproto/common.mk
  50. 6
    4
      imagine/bundle/all/src/libX11/common.mk
  51. BIN
      imagine/bundle/all/src/libX11/libX11-1.6.2.tar.bz2
  52. BIN
      imagine/bundle/all/src/libX11/libX11-1.6.5.tar.bz2
  53. 5
    3
      imagine/bundle/all/src/libXext/common.mk
  54. BIN
      imagine/bundle/all/src/libXext/libXext-1.3.2.tar.bz2
  55. BIN
      imagine/bundle/all/src/libXext/libXext-1.3.3.tar.bz2
  56. 3
    3
      imagine/bundle/all/src/libXfixes/common.mk
  57. BIN
      imagine/bundle/all/src/libXfixes/libXfixes-5.0.1.tar.bz2
  58. BIN
      imagine/bundle/all/src/libXfixes/libXfixes-5.0.3.tar.bz2
  59. 5
    3
      imagine/bundle/all/src/libXi/common.mk
  60. BIN
      imagine/bundle/all/src/libXi/libXi-1.7.2.tar.bz2
  61. BIN
      imagine/bundle/all/src/libXi/libXi-1.7.9.tar.bz2
  62. 3
    3
      imagine/bundle/all/src/libxcb/common.mk
  63. BIN
      imagine/bundle/all/src/libxcb/libxcb-1.13.tar.bz2
  64. BIN
      imagine/bundle/all/src/libxcb/libxcb-1.9.1.tar.bz2
  65. 3
    3
      imagine/bundle/all/src/xextproto/common.mk
  66. BIN
      imagine/bundle/all/src/xextproto/xextproto-7.2.1.tar.bz2
  67. BIN
      imagine/bundle/all/src/xextproto/xextproto-7.3.0.tar.bz2
  68. 3
    3
      imagine/bundle/all/src/xproto/common.mk
  69. BIN
      imagine/bundle/all/src/xproto/xproto-7.0.24.tar.bz2
  70. BIN
      imagine/bundle/all/src/xproto/xproto-7.0.31.tar.bz2
  71. 1
    1
      imagine/doc/INSTALL
  72. 2
    2
      imagine/doc/INSTALL-Android
  73. 8
    6
      imagine/include/imagine/base/Base.hh
  74. 14
    10
      imagine/include/imagine/base/BaseWindow.hh
  75. 41
    0
      imagine/include/imagine/base/CustomEvent.hh
  76. 2
    1
      imagine/include/imagine/base/EGLContextBase.hh
  77. 2
    0
      imagine/include/imagine/base/EventLoop.hh
  78. 5
    1
      imagine/include/imagine/base/GLContext.hh
  79. 43
    7
      imagine/include/imagine/base/Pipe.hh
  80. 4
    9
      imagine/include/imagine/base/Screen.hh
  81. 3
    1
      imagine/include/imagine/base/Window.hh
  82. 0
    1
      imagine/include/imagine/base/android/AndroidScreen.hh
  83. 15
    0
      imagine/include/imagine/base/baseDefs.hh
  84. 2
    1
      imagine/include/imagine/base/eventLoopDefs.hh
  85. 42
    0
      imagine/include/imagine/base/eventloop/CFCustomEvent.hh
  86. 48
    0
      imagine/include/imagine/base/eventloop/FDCustomEvent.hh
  87. 1
    3
      imagine/include/imagine/base/eventloop/GlibEventLoop.hh
  88. 4
    7
      imagine/include/imagine/base/iphone/EAGLView.hh
  89. 7
    0
      imagine/include/imagine/base/iphone/IOSGL.hh
  90. 2
    2
      imagine/include/imagine/base/timer/CFTimer.hh
  91. 0
    64
      imagine/include/imagine/base/x11/XGL.hh
  92. 0
    1
      imagine/include/imagine/base/x11/XWindow.hh
  93. 0
    10
      imagine/include/imagine/base/x11/glxIncludes.h
  94. 10
    10
      imagine/include/imagine/gfx/GeomQuad.hh
  95. 1
    1
      imagine/include/imagine/gfx/GeomQuadMesh.hh
  96. 169
    53
      imagine/include/imagine/gfx/Gfx.hh
  97. 1
    1
      imagine/include/imagine/gfx/GfxLGradient.hh
  98. 5
    5
      imagine/include/imagine/gfx/GfxSprite.hh
  99. 4
    3
      imagine/include/imagine/gfx/GfxText.hh
  100. 0
    0
      imagine/include/imagine/gfx/GlyphTextureSet.hh

+ 1
- 1
C64.emu/src/main/Main.cc View File

@@ -518,7 +518,7 @@ void EmuSystem::runFrame(EmuVideo *video, bool renderAudio)
if(video)
{
video->setFormat(canvasSrcPix);
video->writeFrame(canvasSrcPix);
video->startFrame(canvasSrcPix);
}
runningFrame = 0;
}

+ 2
- 2
EmuFramework/include/emuframework/ButtonConfigView.hh View File

@@ -52,7 +52,7 @@ public:
IG::WindowRect &viewRect() final { return viewFrame; }
void place() final;
bool inputEvent(Input::Event e) final;
void draw() final;
void draw(Gfx::RendererCommands &cmds) final;
void onAddedToController(Input::Event e) final;
};

@@ -62,7 +62,7 @@ private:
struct BtnConfigMenuItem : public DualTextMenuItem
{
using DualTextMenuItem::DualTextMenuItem;
void draw(Gfx::Renderer &r, Gfx::GC xPos, Gfx::GC yPos, Gfx::GC xSize, Gfx::GC ySize, _2DOrigin align, const Gfx::ProjectionPlane &projP) const final;
void draw(Gfx::RendererCommands &cmds, Gfx::GC xPos, Gfx::GC yPos, Gfx::GC xSize, Gfx::GC ySize, _2DOrigin align, const Gfx::ProjectionPlane &projP) const final;
};

InputManagerView &rootIMView;

+ 2
- 1
EmuFramework/include/emuframework/CreditsView.hh View File

@@ -32,7 +32,8 @@ public:
CreditsView(const char *str, ViewAttachParams attach);
~CreditsView();
IG::WindowRect &viewRect() final { return rect; }
void draw() final;
void prepareDraw() final;
void draw(Gfx::RendererCommands &cmds) final;
void place() final;
bool inputEvent(Input::Event e) final;
void onAddedToController(Input::Event e) final {}

+ 1
- 1
EmuFramework/include/emuframework/EmuInputView.hh View File

@@ -24,7 +24,7 @@ public:
EmuInputView(ViewAttachParams attach): View(attach) {}
IG::WindowRect &viewRect() final { return rect; }
void place() final;
void draw() final;
void draw(Gfx::RendererCommands &cmds) final;
bool inputEvent(Input::Event e) final;
void onAddedToController(Input::Event e) final {}
void resetInput();

+ 1
- 1
EmuFramework/include/emuframework/EmuLoadProgressView.hh View File

@@ -37,7 +37,7 @@ public:
void setLabel(const char *str);
void place() final;
bool inputEvent(Input::Event e) final;
void draw() final;
void draw(Gfx::RendererCommands &cmds) final;
void onAddedToController(Input::Event e) final {}

// load context vars

+ 2
- 3
EmuFramework/include/emuframework/EmuSystem.hh View File

@@ -24,8 +24,8 @@
#include <imagine/gui/View.hh>
#include <imagine/util/audio/PcmFormat.hh>
#include <imagine/util/string.h>
#include <optional>
#include <stdexcept>
#include <experimental/optional>
#include <emuframework/EmuVideo.hh>

#ifdef ENV_NOTE
@@ -100,7 +100,7 @@ public:

using OnLoadProgressDelegate = DelegateFunc<bool(int pos, int max, const char *label)>;

using Error = std::experimental::optional<std::runtime_error>;
using Error = std::optional<std::runtime_error>;
using NameFilterFunc = bool(*)(const char *name);
static State state;
static FS::PathString savePath_;
@@ -109,7 +109,6 @@ public:
static Base::FrameTimeBase startFrameTime;
static Base::FrameTimeBase timePerVideoFrame;
static uint emuFrameNow;
static bool runFrameOnDraw;
static Audio::PcmFormat pcmFormat;
static uint audioFramesPerVideoFrame;
static uint aspectRatioX, aspectRatioY;

+ 3
- 4
EmuFramework/include/emuframework/EmuVideo.hh View File

@@ -57,10 +57,10 @@ public:
void setFormat(IG::PixmapDesc desc);
void resetImage();
EmuVideoImage startFrame();
void writeFrame(Gfx::LockedTextureBuffer texBuff);
void writeFrame(IG::Pixmap pix);
void startFrame(IG::Pixmap pix);
void finishFrame(Gfx::LockedTextureBuffer texBuff);
void finishFrame(IG::Pixmap pix);
void takeGameScreenshot();
void renderNextFrameToApp();
bool isExternalTexture();
Gfx::PixmapTexture &image();
Gfx::Renderer &renderer() { return r; }
@@ -71,7 +71,6 @@ protected:
Gfx::PixmapTexture vidImg{};
IG::MemPixmap memPix{};
bool screenshotNextFrame = false;
bool renderNextFrame = false;

void doScreenshot(IG::Pixmap pix);
};

+ 1
- 1
EmuFramework/include/emuframework/EmuVideoLayer.hh View File

@@ -27,7 +27,7 @@ class EmuVideoLayer
public:
EmuVideoLayer(EmuVideo &video);
void place(const IG::WindowRect &viewportRect, const Gfx::ProjectionPlane &projP, bool onScreenControlsOverlay);
void draw(const Gfx::ProjectionPlane &projP);
void draw(Gfx::RendererCommands &cmds, const Gfx::ProjectionPlane &projP);
void setOverlay(uint effect);
void setOverlayIntensity(Gfx::GC intensity);
void placeOverlay();

+ 2
- 1
EmuFramework/include/emuframework/EmuView.hh View File

@@ -26,7 +26,8 @@ public:
EmuView(ViewAttachParams attach, EmuVideoLayer *layer, EmuInputView *inputView);
IG::WindowRect &viewRect() final { return rect; }
void place() final;
void draw() final;
void prepareDraw() final;
void draw(Gfx::RendererCommands &cmds) final;
bool inputEvent(Input::Event e) final;
void onAddedToController(Input::Event e) final {}
bool hasLayer() const { return layer; }

+ 1
- 1
EmuFramework/include/emuframework/InputManagerView.hh View File

@@ -43,7 +43,7 @@ public:
IG::WindowRect &viewRect() final { return viewFrame; }
void place() final;
bool inputEvent(Input::Event e) final;
void draw() final;
void draw(Gfx::RendererCommands &cmds) final;
void onAddedToController(Input::Event e) final {}
};


+ 2
- 1
EmuFramework/include/emuframework/MsgPopup.hh View File

@@ -45,7 +45,8 @@ public:
void postError(const char *msg, int secs = 3);
void post(const char *prefix, const std::system_error &err, int secs = 3);
void post(const char *prefix, std::error_code ec, int secs = 3);
void draw();
void prepareDraw();
void draw(Gfx::RendererCommands &cmds);

[[gnu::format(printf, 4, 5)]]
void printf(uint secs, bool error, const char *format, ...);

+ 2
- 1
EmuFramework/include/emuframework/OptionView.hh View File

@@ -80,7 +80,8 @@ protected:
#if defined CONFIG_BASE_MULTI_WINDOW && defined CONFIG_BASE_MULTI_SCREEN
BoolMenuItem showOnSecondScreen;
#endif
BoolMenuItem dither;
TextMenuItem gpuMultithreadingItem[3];
MultiChoiceMenuItem gpuMultithreading;
TextHeadingMenuItem visualsHeading;
TextHeadingMenuItem screenShapeHeading;
TextHeadingMenuItem advancedHeading;

+ 1
- 1
EmuFramework/include/emuframework/TouchConfigView.hh View File

@@ -85,5 +85,5 @@ public:
public:
TouchConfigView(ViewAttachParams attach, const char *faceBtnName, const char *centerBtnName);
void place() final;
void draw() final;
void draw(Gfx::RendererCommands &cmds) final;
};

+ 5
- 5
EmuFramework/include/emuframework/VController.hh View File

@@ -27,7 +27,7 @@ class VControllerDPad
public:
constexpr VControllerDPad() {}
void setImg(Gfx::Renderer &r, Gfx::PixmapTexture &dpadR, Gfx::GTexC texHeight);
void draw(Gfx::Renderer &r) const;
void draw(Gfx::RendererCommands &cmds) const;
void setBoundingAreaVisible(Gfx::Renderer &r, bool on);
int getInput(IG::WP c) const;
IG::WindowRect bounds() const;
@@ -66,7 +66,7 @@ public:
void updateImg(Gfx::Renderer &r);
void setImg(Gfx::Renderer &r, Gfx::PixmapTexture *img);
void place(Gfx::GC btnSize, Gfx::GC yOffset);
void draw(Gfx::Renderer &r, const Gfx::ProjectionPlane &projP) const;
void draw(Gfx::RendererCommands &cmds, const Gfx::ProjectionPlane &projP) const;
int getInput(IG::WP c) const;
int translateInput(uint idx) const;
bool keyInput(VController &v, Gfx::Renderer &r, Input::Event e);
@@ -113,7 +113,7 @@ public:
void setFaceBtnPos(IG::Point2D<int> pos);
std::array<int, 2> getCenterBtnInput(IG::WP pos) const;
std::array<int, 2> getBtnInput(IG::WP pos) const;
void draw(Gfx::Renderer &r, bool showHidden) const;
void draw(Gfx::RendererCommands &cmds, bool showHidden) const;
VControllerDPad &dPad() { return dp; }
const VControllerDPad &dPad() const { return dp; }
Gfx::GC spacing() const { return btnSpace; }
@@ -193,8 +193,8 @@ public:
void toggleKeyboard();
void applyInput(Input::Event e);
bool keyInput(Input::Event e);
void draw(bool emuSystemControls, bool activeFF, bool showHidden = false);
void draw(bool emuSystemControls, bool activeFF, bool showHidden, float alpha);
void draw(Gfx::RendererCommands &cmds, bool emuSystemControls, bool activeFF, bool showHidden = false);
void draw(Gfx::RendererCommands &cmds, bool emuSystemControls, bool activeFF, bool showHidden, float alpha);
int numElements() const;
IG::WindowRect bounds(int elemIdx) const;
void setPos(int elemIdx, IG::Point2D<int> pos);

+ 3
- 4
EmuFramework/include/emuframework/VideoImageEffect.hh View File

@@ -17,7 +17,6 @@

#include <imagine/gfx/Gfx.hh>
#include <imagine/gfx/Texture.hh>
#include <imagine/gfx/RenderTarget.hh>
#include <imagine/pixmap/Pixmap.hh>
#include <system_error>

@@ -39,7 +38,7 @@ private:
int srcTexelHalfDeltaU{};
int srcPixelsU{};
uint effect_ = NO_EFFECT;
Gfx::RenderTarget renderTarget_{};
Gfx::Texture renderTarget_{};
IG::WP renderTargetScale{};
IG::WP renderTargetImgSize{};
IG::WP inputImgSize{1, 1};
@@ -68,7 +67,7 @@ public:
void setImageSize(Gfx::Renderer &r, IG::WP size);
void setBitDepth(Gfx::Renderer &r, uint bitDepth);
Gfx::Program &program();
Gfx::RenderTarget &renderTarget();
void drawRenderTarget(Gfx::Renderer &r, Gfx::PixmapTexture &img);
Gfx::Texture &renderTarget();
void drawRenderTarget(Gfx::RendererCommands &cmds, Gfx::PixmapTexture &img);
void deinit(Gfx::Renderer &r);
};

+ 1
- 1
EmuFramework/include/emuframework/VideoImageOverlay.hh View File

@@ -39,5 +39,5 @@ public:
constexpr VideoImageOverlay() {}
void setEffect(Gfx::Renderer &r, uint effect);
void place(const Gfx::Sprite &disp, uint lines);
void draw(Gfx::Renderer &r);
void draw(Gfx::RendererCommands &cmds);
};

+ 1
- 0
EmuFramework/linux-armv7-pandora.mk View File

@@ -0,0 +1 @@
../imagine/make/shortcut/common-builds/linux-armv7-pandora.mk

+ 17
- 18
EmuFramework/src/ButtonConfigView.cc View File

@@ -160,33 +160,32 @@ bool ButtonConfigSetView::inputEvent(Input::Event e)
return false;
}

void ButtonConfigSetView::draw()
void ButtonConfigSetView::draw(Gfx::RendererCommands &cmds)
{
using namespace Gfx;
auto &r = renderer();
r.setBlendMode(0);
r.noTexProgram.use(r, projP.makeTranslate());
r.setColor(.4, .4, .4, 1.);
GeomRect::draw(r, viewFrame, projP);
cmds.setBlendMode(0);
cmds.setCommonProgram(CommonProgram::NO_TEX, projP.makeTranslate());
cmds.setColor(.4, .4, .4, 1.);
GeomRect::draw(cmds, viewFrame, projP);
#ifdef CONFIG_INPUT_POINTING_DEVICES
if(pointerUIIsInit())
{
r.setColor(.2, .2, .2, 1.);
GeomRect::draw(r, unbindB, projP);
GeomRect::draw(r, cancelB, projP);
cmds.setColor(.2, .2, .2, 1.);
GeomRect::draw(cmds, unbindB, projP);
GeomRect::draw(cmds, cancelB, projP);
}
#endif

r.setColor(COLOR_WHITE);
r.texAlphaProgram.use(r);
cmds.setColor(COLOR_WHITE);
cmds.setCommonProgram(CommonProgram::TEX_ALPHA);
#ifdef CONFIG_INPUT_POINTING_DEVICES
if(pointerUIIsInit())
{
unbind.draw(r, projP.unProjectRect(unbindB).pos(C2DO), C2DO, projP);
cancel.draw(r, projP.unProjectRect(cancelB).pos(C2DO), C2DO, projP);
unbind.draw(cmds, projP.unProjectRect(unbindB).pos(C2DO), C2DO, projP);
cancel.draw(cmds, projP.unProjectRect(cancelB).pos(C2DO), C2DO, projP);
}
#endif
text.draw(r, 0, 0, C2DO, projP);
text.draw(cmds, 0, 0, C2DO, projP);
}

void ButtonConfigSetView::onAddedToController(Input::Event e)
@@ -203,12 +202,12 @@ void ButtonConfigSetView::onAddedToController(Input::Event e)
#endif
}

void ButtonConfigView::BtnConfigMenuItem::draw(Gfx::Renderer &r, Gfx::GC xPos, Gfx::GC yPos, Gfx::GC xSize, Gfx::GC ySize, _2DOrigin align, const Gfx::ProjectionPlane &projP) const
void ButtonConfigView::BtnConfigMenuItem::draw(Gfx::RendererCommands &cmds, Gfx::GC xPos, Gfx::GC yPos, Gfx::GC xSize, Gfx::GC ySize, _2DOrigin align, const Gfx::ProjectionPlane &projP) const
{
using namespace Gfx;
BaseTextMenuItem::draw(r, xPos, yPos, xSize, ySize, align, projP);
r.setColor(1., 1., 0.); // yellow
DualTextMenuItem::draw2ndText(r, xPos, yPos, xSize, ySize, align, projP);
BaseTextMenuItem::draw(cmds, xPos, yPos, xSize, ySize, align, projP);
cmds.setColor(1., 1., 0.); // yellow
DualTextMenuItem::draw2ndText(cmds, xPos, yPos, xSize, ySize, align, projP);
}

static std::pair<const KeyCategory *, uint> findCategoryAndKeyInConfig(Input::Key key, InputDeviceConfig &devConf, const KeyCategory *skipCat, int skipIdx_)

+ 2
- 4
EmuFramework/src/ConfigFile.cc View File

@@ -133,6 +133,7 @@ static OptionBase *cfgFileOption[] =
&optionImgEffect,
&optionImageEffectPixelFormat,
#endif
&optionGPUMultiThreading,
&optionOverlayEffect,
&optionOverlayEffectLevel,
#ifdef CONFIG_INPUT_RELATIVE_MOTION_DEVICES
@@ -189,7 +190,6 @@ static OptionBase *cfgFileOption[] =
#if defined __ANDROID__
&optionLowProfileOSNav,
&optionHideOSNav,
&optionDitherImage,
&optionAndroidTextureStorage,
&optionProcessPriority,
&optionSustainedPerformanceMode,
@@ -451,9 +451,6 @@ void loadConfigFile()
bcase CFGKEY_SKIP_LATE_FRAMES: optionSkipLateFrames.readFromIO(io, size);
bcase CFGKEY_FRAME_RATE: optionFrameRate.readFromIO(io, size);
bcase CFGKEY_FRAME_RATE_PAL: optionFrameRatePAL.readFromIO(io, size);
#if defined(CONFIG_BASE_ANDROID)
bcase CFGKEY_DITHER_IMAGE: optionDitherImage.readFromIO(io, size);
#endif
bcase CFGKEY_LAST_DIR: optionLastLoadPath.readFromIO(io, size);
bcase CFGKEY_FONT_Y_SIZE: optionFontSize.readFromIO(io, size);
bcase CFGKEY_GAME_ORIENTATION: optionGameOrientation.readFromIO(io, size);
@@ -469,6 +466,7 @@ void loadConfigFile()
bcase CFGKEY_IMAGE_EFFECT: optionImgEffect.readFromIO(io, size);
bcase CFGKEY_IMAGE_EFFECT_PIXEL_FORMAT: optionImageEffectPixelFormat.readFromIO(io, size);
#endif
bcase CFGKEY_GPU_MULTITHREADING: optionGPUMultiThreading.readFromIO(io, size);
bcase CFGKEY_OVERLAY_EFFECT: optionOverlayEffect.readFromIO(io, size);
bcase CFGKEY_OVERLAY_EFFECT_LEVEL: optionOverlayEffectLevel.readFromIO(io, size);
bcase CFGKEY_TOUCH_CONTROL_VIRBRATE: optionVibrateOnPush.readFromIO(io, size);

+ 11
- 10
EmuFramework/src/CreditsView.cc View File

@@ -27,26 +27,27 @@ CreditsView::CreditsView(const char *str, ViewAttachParams attach):
animate =
[this](Base::Screen::FrameParams params)
{
window().postDraw();
if(fade.update(1))
{
params.readdOnFrame();
}
postDraw();
return fade.update(1);
};
screen()->addOnFrame(animate);
place();
}

void CreditsView::draw()
void CreditsView::prepareDraw()
{
text.makeGlyphs(renderer());
}

void CreditsView::draw(Gfx::RendererCommands &cmds)
{
using namespace Gfx;
auto &r = renderer();
r.setColor(1., 1., 1., fade.now());
r.texAlphaProgram.use(r, projP.makeTranslate());
cmds.setColor(1., 1., 1., fade.now());
cmds.setCommonProgram(CommonProgram::TEX_ALPHA, projP.makeTranslate());
auto textRect = rect;
if(IG::isOdd(textRect.ySize()))
textRect.y2--;
text.draw(r, projP.unProjectRect(textRect).pos(C2DO), C2DO, projP);
text.draw(cmds, projP.unProjectRect(textRect).pos(C2DO), C2DO, projP);
}

void CreditsView::place()

+ 122
- 95
EmuFramework/src/EmuApp.cc View File

@@ -151,6 +151,7 @@ bool EmuModalViewStack::inputEvent(Input::Event e)
}

static Gfx::Renderer renderer;
Gfx::RendererTask rendererTask{renderer};
AppWindowData mainWin{}, extraWin{};
bool menuViewIsActive = true;
EmuVideo emuVideo{renderer};
@@ -215,7 +216,7 @@ Gfx::PixmapTexture &getAsset(Gfx::Renderer &r, AssetID assetID)
{
logErr("couldn't load %s", assetFilename[assetID]);
}
res.init(r, png);
res = r.makePixmapTexture(png);
}
return res;
}
@@ -230,20 +231,20 @@ void postDrawToEmuWindows()
emuWin->win.postDraw();
}

static void drawEmuVideo(Gfx::Renderer &r)
static void drawEmuVideo(Gfx::RendererCommands &cmds)
{
if(emuView.hasLayer())
emuView.draw();
emuView.draw(cmds);
else if(emuView2.hasLayer())
emuView2.draw();
popup.draw();
r.setClipRect(false);
r.presentDrawable(emuWin->drawable);
emuView2.draw(cmds);
popup.draw(cmds);
cmds.setClipTest(false);
cmds.present();
}

void updateAndDrawEmuVideo()
void updateAndDrawEmuVideo(Gfx::RendererCommands &cmds)
{
drawEmuVideo(renderer);
drawEmuVideo(cmds);
}

void startViewportAnimation(AppWindowData &winData)
@@ -286,11 +287,33 @@ static void applyFrameRates()
EmuSystem::configFrameTime();
}

static void addInitialOnFrame(Base::Screen &screen, uint delay)
{
screen.addOnFrame(
[delay](Base::Screen::FrameParams params)
{
if(!EmuSystem::isActive())
return false;
postDrawToEmuWindows();
// delay for timestamps to stabilize
if(delay)
{
addInitialOnFrame(params.screen(), delay - 1);
}
else
{
params.screen().addOnFrame(onFrameUpdate);
}
return false;
});
}

static void startEmulation()
{
setCPUNeedsLowLatency(true);
EmuSystem::start();
emuWin->win.screen()->addOnFrameOnce(onFrameUpdate);
auto &screen = *emuWin->win.screen();
addInitialOnFrame(screen, screen.frameRate() / 5);
}

static void pauseEmulation()
@@ -325,19 +348,9 @@ void EmuApp::exitGame(bool allowAutosaveState)
popMenuToRoot();
}

static void drawEmuFrame(Gfx::Renderer &r)
static void drawEmuFrame(Gfx::RendererCommands &cmds)
{
if(EmuSystem::runFrameOnDraw)
{
bool renderAudio = optionSound;
emuVideo.renderNextFrameToApp();
EmuSystem::runFrame(&emuVideo, renderAudio);
EmuSystem::runFrameOnDraw = false;
}
else
{
drawEmuVideo(r);
}
drawEmuVideo(cmds);
}

static bool allWindowsAreFocused()
@@ -381,33 +394,42 @@ void setEmuViewOnExtraWindow(bool on)
winConf.setOnSurfaceChange(
[](Base::Window &win, Base::Window::SurfaceChange change)
{
rendererTask.updateDrawableForSurfaceChange(extraWin.drawableHolder, change);
if(change.resized())
{
logMsg("view resize for extra window");
renderer.restoreBind();
updateProjection(extraWin, makeViewport(win));
emuView2.setViewRect(extraWin.viewport().bounds(), extraWin.projectionPlane);
emuView2.place();
}
renderer.updateDrawableForSurfaceChange(extraWin.drawable, change);
});

winConf.setOnDraw(
[](Base::Window &win, Base::Window::DrawParams params)
{
renderer.updateCurrentDrawable(extraWin.drawable, win, params, extraWin.viewport(), extraWin.projectionMat);
renderer.clear();
if(EmuSystem::isActive())
{
drawEmuFrame(renderer);
}
else
{
emuView2.draw();
renderer.setClipRect(false);
renderer.presentDrawable(extraWin.drawable);
popup.prepareDraw();
}
renderer.finishPresentDrawable(extraWin.drawable);
emuView2.prepareDraw();
auto fence = renderer.addResourceSyncFence();
rendererTask.draw(extraWin.drawableHolder, win, params,
[fence](Gfx::Drawable &drawable, const Base::Window &win, Gfx::RendererDrawTask task)
{
auto cmds = task.makeRendererCommands(drawable, extraWin.viewport(), extraWin.projectionMat);
cmds.clear();
task.waitSync(fence);
if(EmuSystem::isActive())
{
drawEmuFrame(cmds);
}
else
{
emuView2.draw(cmds);
cmds.setClipTest(false);
cmds.present();
}
}, 1);
});

winConf.setOnInputEvent(
@@ -415,7 +437,6 @@ void setEmuViewOnExtraWindow(bool on)
{
if(!e.isPointer())
{
renderer.restoreBind();
return handleInputEvent(win, e);
}
return false;
@@ -424,7 +445,6 @@ void setEmuViewOnExtraWindow(bool on)
winConf.setOnFocusChange(
[](Base::Window &win, uint in)
{
renderer.restoreBind();
extraWin.focused = in;
onFocusChange(in);
});
@@ -438,7 +458,6 @@ void setEmuViewOnExtraWindow(bool on)
winConf.setOnDismiss(
[](Base::Window &win)
{
renderer.setCurrentDrawable({});
EmuSystem::resetFrameTime();
logMsg("setting emu view on main window");
emuWin = &mainWin;
@@ -505,9 +524,8 @@ void startGameFromMenu()
Input::setKeyRepeat(false);
emuWin->win.screen()->setFrameRate(1. / EmuSystem::frameTime());
startEmulation();
mainWin.win.postDraw();
if(extraWin.win)
extraWin.win.postDraw();
mainWin.win.postDraw(); // main window draws the virtual controls
emuView.place();
emuView2.place();
}
@@ -579,7 +597,6 @@ void mainInitCommon(int argc, char** argv)
[](const char *filename)
{
logMsg("got IPC: %s", filename);
renderer.restoreBind();
handleOpenFileCommand(filename);
});
initOptions();
@@ -600,16 +617,18 @@ void mainInitCommon(int argc, char** argv)

{
Gfx::Error err{};
auto threadMode = (Gfx::Renderer::ThreadMode)optionGPUMultiThreading.val;
#ifdef EMU_FRAMEWORK_WINDOW_PIXEL_FORMAT_OPTION
renderer = Gfx::Renderer::makeConfiguredRenderer((IG::PixelFormatID)optionWindowPixelFormat.val, err);
renderer = Gfx::Renderer::makeConfiguredRenderer(threadMode, (IG::PixelFormatID)optionWindowPixelFormat.val, err);
#else
renderer = Gfx::Renderer::makeConfiguredRenderer(err);
renderer = Gfx::Renderer::makeConfiguredRenderer(threadMode, err);
#endif
if(err)
{
Base::exitWithErrorMessagePrintf(-1, "Error creating renderer: %s", err->what());
return;
}
rendererTask.start(2);
}

auto compiled = renderer.texAlphaProgram.compile(renderer);
@@ -617,10 +636,6 @@ void mainInitCommon(int argc, char** argv)
compiled |= View::compileGfxPrograms(renderer);
if(compiled)
renderer.autoReleaseShaderCompiler();
if(!optionDitherImage.isConst)
{
renderer.setDither(optionDitherImage);
}

#ifdef __ANDROID__
if((int8)optionProcessPriority != 0)
@@ -654,11 +669,10 @@ void mainInitCommon(int argc, char** argv)
emuVideoLayer.setEffectBitDepth((IG::PixelFormatID)optionImageEffectPixelFormat.val == IG::PIXEL_RGBA8888 ? 32 : 16);
#endif

Base::setOnResume(
Base::addOnResume(
[](bool focused)
{
AudioManager::startSession();
renderer.restoreBind();
if(!menuViewIsActive && focused && EmuSystem::isPaused())
{
logMsg("resuming emulation due to app resume");
@@ -668,24 +682,21 @@ void mainInitCommon(int argc, char** argv)
startEmulation();
postDrawToEmuWindows();
}
return true;
});

Base::setOnFreeCaches(
[]()
{
renderer.restoreBind();
if(View::defaultFace)
View::defaultFace.freeCaches();
if(View::defaultBoldFace)
View::defaultBoldFace.freeCaches();
View::defaultFace.freeCaches();
View::defaultBoldFace.freeCaches();
});

Base::setOnExit(
Base::addOnExit(
[](bool backgrounded)
{
EmuSystem::closeSound();
AudioManager::endSession();
renderer.restoreBind();
if(backgrounded)
{
EmuApp::restoreMenuFromGame();
@@ -709,14 +720,15 @@ void mainInitCommon(int argc, char** argv)
Bluetooth::closeBT(bta);
#endif

mainWin.drawable.freeCaches();
extraWin.drawable.freeCaches();
renderer.finish();
View::defaultFace.freeCaches();
View::defaultBoldFace.freeCaches();

#ifdef CONFIG_BASE_IOS
//if(backgrounded)
// FsSys::remove("/private/var/mobile/Library/Caches/" CONFIG_APP_ID "/com.apple.opengl/shaders.maps");
#endif

return true;
});

Base::Screen::setOnChange(
@@ -740,7 +752,6 @@ void mainInitCommon(int argc, char** argv)
[]()
{
logMsg("input devs enumerated");
renderer.restoreBind();
updateInputDevices();
EmuControls::updateAutoOnScreenControlVisible();
});
@@ -750,7 +761,6 @@ void mainInitCommon(int argc, char** argv)
{
logMsg("got input dev change");

renderer.restoreBind();
updateInputDevices();
EmuControls::updateAutoOnScreenControlVisible();

@@ -774,9 +784,10 @@ void mainInitCommon(int argc, char** argv)
onFrameUpdate = [](Base::Screen::FrameParams params)
{
commonUpdateInput();
bool doFrame = false;
if(unlikely(fastForwardActive || EmuSystem::shouldFastForward()))
{
EmuSystem::runFrameOnDraw = true;
doFrame = true;
postDrawToEmuWindows();
if(fastForwardActive)
{
@@ -798,10 +809,10 @@ void mainInitCommon(int argc, char** argv)
else
{
uint frames = EmuSystem::advanceFramesWithTime(params.timestamp());
//logDMsg("%d frames elapsed (%fs)", frames, Base::frameTimeBaseToSecsDec(params.frameTimeDiff()));
//logDMsg("%d frames elapsed (%fs)", frames, Base::frameTimeBaseToSecsDec(params.timestampDiff()));
if(frames)
{
EmuSystem::runFrameOnDraw = true;
doFrame = true;
postDrawToEmuWindows();
constexpr uint maxLateFrameSkip = 6;
uint maxFrameSkip = optionSkipLateFrames ? maxLateFrameSkip : 0;
@@ -812,6 +823,7 @@ void mainInitCommon(int argc, char** argv)
assumeExpr(maxFrameSkip <= maxLateFrameSkip);
if(frames > 1 && maxFrameSkip)
{
//logDMsg("running %d frames", frames);
uint framesToSkip = frames - 1;
framesToSkip = std::min(framesToSkip, maxFrameSkip);
bool renderAudio = optionSound;
@@ -822,7 +834,12 @@ void mainInitCommon(int argc, char** argv)
}
}
}
params.readdOnFrame();
if(doFrame)
{
bool renderAudio = optionSound;
EmuSystem::runFrame(&emuVideo, renderAudio);
}
return true;
};

Base::WindowConfig winConf;
@@ -830,14 +847,12 @@ void mainInitCommon(int argc, char** argv)
winConf.setOnInputEvent(
[](Base::Window &win, Input::Event e)
{
renderer.restoreBind();
return handleInputEvent(win, e);
});

winConf.setOnFocusChange(
[](Base::Window &win, uint in)
{
renderer.restoreBind();
mainWin.focused = in;
onFocusChange(in);
});
@@ -846,51 +861,61 @@ void mainInitCommon(int argc, char** argv)
[](Base::Window &win, const char *filename)
{
logMsg("got DnD: %s", filename);
renderer.restoreBind();
handleOpenFileCommand(filename);
});

winConf.setOnSurfaceChange(
[](Base::Window &win, Base::Window::SurfaceChange change)
{
rendererTask.updateDrawableForSurfaceChange(mainWin.drawableHolder, change);
if(change.resized())
{
renderer.restoreBind();
updateWindowViewport(mainWin, change);
emuView.setViewRect(mainWin.viewport().bounds(), mainWin.projectionPlane);
placeElements();
}
renderer.updateDrawableForSurfaceChange(mainWin.drawable, change);
});

winConf.setOnDraw(
[](Base::Window &win, Base::Window::DrawParams params)
{
renderer.updateCurrentDrawable(mainWin.drawable, win, params, mainWin.viewport(), mainWin.projectionMat);
renderer.clear();
if(EmuSystem::isActive())
{
if(emuView.hasLayer())
drawEmuFrame(renderer);
else
{
emuView.draw();
renderer.setClipRect(false);
renderer.presentDrawable(mainWin.drawable);
}
}
else
popup.prepareDraw();
emuView.prepareDraw();
if(!EmuSystem::isActive())
{
emuView.draw();
if(modalViewController.size())
modalViewController.draw();
else if(menuViewIsActive)
viewStack.draw();
popup.draw();
renderer.setClipRect(false);
renderer.presentDrawable(mainWin.drawable);
modalViewController.prepareDraw();
viewStack.prepareDraw();
}
renderer.finishPresentDrawable(mainWin.drawable);
auto fence = renderer.addResourceSyncFence();
rendererTask.draw(mainWin.drawableHolder, win, params,
[fence](Gfx::Drawable &drawable, const Base::Window &win, Gfx::RendererDrawTask task)
{
auto cmds = task.makeRendererCommands(drawable, mainWin.viewport(), mainWin.projectionMat);
cmds.clear();
task.waitSync(fence);
if(EmuSystem::isActive())
{
if(emuView.hasLayer())
drawEmuFrame(cmds);
else
{
emuView.draw(cmds);
cmds.setClipTest(false);
cmds.present();
}
}
else
{
emuView.draw(cmds);
if(modalViewController.size())
modalViewController.draw(cmds);
else if(menuViewIsActive)
viewStack.draw(cmds);
popup.draw(cmds);
cmds.setClipTest(false);
cmds.present();
}
}, 0);
});

if(Base::usesPermission(Base::Permission::WRITE_EXT_STORAGE) &&
@@ -927,6 +952,7 @@ void mainInitCommon(int argc, char** argv)
viewStack.setShowNavViewBackButton(View::needsBackControl);
EmuApp::onCustomizeNavView(*viewNav);
viewStack.setNavView(std::move(viewNav));
viewStack.setRendererTask(&rendererTask);
}
{
auto viewNav = std::make_unique<BasicNavView>
@@ -945,6 +971,7 @@ void mainInitCommon(int argc, char** argv)
modalViewController.setShowNavViewBackButton(View::needsBackControl);
EmuApp::onCustomizeNavView(*viewNav);
modalViewController.setNavView(std::move(viewNav));
modalViewController.setRendererTask(&rendererTask);
}

mainInitWindowCommon(mainWin.win);
@@ -1282,7 +1309,7 @@ void EmuApp::createSystemWithMedia(GenericIO io, const char *path, const char *n
}
auto loadProgressView = new EmuLoadProgressView{emuViewAttachParams(), e, onComplete};
pushAndShowModalView(*loadProgressView, e);
loadProgressView->pipe.init({},
loadProgressView->pipe.addToEventLoop({},
[loadProgressView](Base::Pipe &pipe)
{
while(pipe.hasData())
@@ -1298,14 +1325,14 @@ void EmuApp::createSystemWithMedia(GenericIO io, const char *path, const char *n
char errorStr[len + 1];
pipe.read(errorStr, len);
errorStr[len] = 0;
pipe.deinit();
pipe.removeFromEventLoop();
popModalViews();
popup.postError(errorStr, 4);
return 0;
}
case EmuSystem::LoadProgress::OK:
{
pipe.deinit();
pipe.removeFromEventLoop();
auto onComplete = loadProgressView->onComplete;
auto originalEvent = loadProgressView->originalEvent;
popModalViews();

+ 4
- 4
EmuFramework/src/EmuInput.cc View File

@@ -63,7 +63,7 @@ void initVControls(Gfx::Renderer &r)
vController.setBoundingAreaVisible(optionTouchCtrlBoundingBoxes);
vController.init((int)optionTouchCtrlAlpha / 255.0, vControllerPixelSize(), View::defaultFace.nominalHeight()*1.75, mainWin.projectionPlane);
#else
vController.init((int)optionTouchCtrlAlpha / 255.0, IG::makeEvenRoundedUp(vController.xMMSizeToPixel(mainWin.win, 8.5)), View::defaultFace->nominalHeight()*1.75, mainWin.projectionPlane);
vController.init((int)optionTouchCtrlAlpha / 255.0, IG::makeEvenRoundedUp(vController.xMMSizeToPixel(mainWin.win, 8.5)), View::defaultFace.nominalHeight()*1.75, mainWin.projectionPlane);
#endif

if(!vControllerLayoutPosChanged) // setup default positions if not provided in config file
@@ -726,7 +726,7 @@ void setupVControllerVars()
vController.setBaseBtnSize(vControllerPixelSize(), View::defaultFace.nominalHeight()*1.75, mainWin.projectionPlane);
vController.setBoundingAreaVisible(optionTouchCtrlBoundingBoxes);
#else
vController.init((int)optionTouchCtrlAlpha / 255.0, IG::makeEvenRoundedUp(vController.xMMSizeToPixel(mainWin.win, 8.5)), View::defaultFace->nominalHeight()*1.75, mainWin.projectionPlane);
vController.init((int)optionTouchCtrlAlpha / 255.0, IG::makeEvenRoundedUp(vController.xMMSizeToPixel(mainWin.win, 8.5)), View::defaultFace.nominalHeight()*1.75, mainWin.projectionPlane);
#endif

auto &layoutPos = vControllerLayoutPos[mainWin.viewport().isPortrait() ? 1 : 0];
@@ -776,7 +776,7 @@ void updateVControlImg()
{
logErr("couldn't load overlay png");
}
overlayImg.init(r, png);
overlayImg = r.makePixmapTexture(png);
vController.setImg(overlayImg);
}
#endif
@@ -789,7 +789,7 @@ void updateVControlImg()
{
logErr("couldn't load kb overlay png");
}
kbOverlayImg.init(r, png);
kbOverlayImg = r.makePixmapTexture(png);
vController.setKeyboardImage(kbOverlayImg);
}
}

+ 2
- 2
EmuFramework/src/EmuInputView.cc View File

@@ -24,9 +24,9 @@

extern bool touchControlsAreOn;

void EmuInputView::draw()
void EmuInputView::draw(Gfx::RendererCommands &cmds)
{
vController.draw(touchControlsAreOn && EmuSystem::touchControlsApplicable(), ffKeyPushed || ffToggleActive);
vController.draw(cmds, touchControlsAreOn && EmuSystem::touchControlsApplicable(), ffKeyPushed || ffToggleActive);
}

void EmuInputView::place()

+ 9
- 10
EmuFramework/src/EmuLoadProgressView.cc View File

@@ -45,24 +45,23 @@ bool EmuLoadProgressView::inputEvent(Input::Event e)
return true;
}

void EmuLoadProgressView::draw()
void EmuLoadProgressView::draw(Gfx::RendererCommands &cmds)
{
if(!strlen(str.data()))
return;
using namespace Gfx;
auto &r = renderer();
projP.resetTransforms(r);
r.setBlendMode(0);
projP.resetTransforms(cmds);
cmds.setBlendMode(0);
if(max)
{
r.noTexProgram.use(r);
r.setColor(.0, .0, .75);
cmds.setCommonProgram(CommonProgram::NO_TEX);
cmds.setColor(.0, .0, .75);
Gfx::GC barHeight = text.ySize*1.5;
auto bar = makeGCRectRel(projP.bounds().pos(LC2DO) - GP{0_gc, barHeight/2_gc},
{IG::scalePointRange((Gfx::GC)pos, 0_gc, (Gfx::GC)max, 0_gc, projP.w), barHeight});
GeomRect::draw(r, bar);
GeomRect::draw(cmds, bar);
}
r.texAlphaProgram.use(r);
r.setColor(COLOR_WHITE);
text.draw(r, 0, 0, C2DO, projP);
cmds.setCommonProgram(CommonProgram::TEX_ALPHA);
cmds.setColor(COLOR_WHITE);
text.draw(cmds, 0, 0, C2DO, projP);
}

+ 3
- 8
EmuFramework/src/EmuOptions.cc View File

@@ -128,6 +128,9 @@ bool imageEffectPixelFormatIsValid(uint8 val)
Byte1Option optionImageEffectPixelFormat(CFGKEY_IMAGE_EFFECT_PIXEL_FORMAT, IG::PIXEL_NONE, 0, imageEffectPixelFormatIsValid);
#endif

Byte1Option optionGPUMultiThreading{CFGKEY_GPU_MULTITHREADING, 0, 0,
optionIsValidWithMax<(int)Gfx::Renderer::ThreadMode::MULTI>};

#ifdef CONFIG_INPUT_RELATIVE_MOTION_DEVICES
Byte4Option optionRelPointerDecel(CFGKEY_REL_POINTER_DECEL, optionRelPointerDecelMed,
!Config::envIsAndroid, optionIsValidWithMax<optionRelPointerDecelHigh>);
@@ -224,8 +227,6 @@ SByte1Option optionProcessPriority{CFGKEY_PROCESS_PRIORITY, -6, 0, optionIsValid
Byte1Option optionSustainedPerformanceMode{CFGKEY_SUSTAINED_PERFORMANCE_MODE, 1, 0};
#endif

Byte1Option optionDitherImage(CFGKEY_DITHER_IMAGE, 1, !Config::envIsAndroid);

#ifdef EMU_FRAMEWORK_WINDOW_PIXEL_FORMAT_OPTION
bool windowPixelFormatIsValid(uint8 val)
{
@@ -289,12 +290,6 @@ void initOptions()
if(Base::androidSDK() >= 11)
{
optionNotificationIcon.initDefault(0);
// don't change dither setting on Android 3.0+
optionDitherImage.isConst = 1;
}
else
{
optionDitherImage.initDefault(0);
}
if(Base::androidSDK() < 12)
{

+ 2
- 3
EmuFramework/src/EmuOptions.hh View File

@@ -72,7 +72,7 @@ enum { CFGKEY_SOUND = 0, CFGKEY_TOUCH_CONTROL_DISPLAY = 1,
CFGKEY_SKIP_LATE_FRAMES = 76, CFGKEY_FRAME_RATE = 77,
CFGKEY_FRAME_RATE_PAL = 78, CFGKEY_TIME_FRAMES_WITH_SCREEN_REFRESH = 79,
CFGKEY_SUSTAINED_PERFORMANCE_MODE = 80, CFGKEY_SHOW_BLUETOOTH_SCAN = 81,
CFGKEY_ADD_SOUND_BUFFERS_ON_UNDERRUN = 82
CFGKEY_ADD_SOUND_BUFFERS_ON_UNDERRUN = 82, CFGKEY_GPU_MULTITHREADING = 83
// 256+ is reserved
};

@@ -236,11 +236,10 @@ extern SByte1Option optionProcessPriority;
extern Byte1Option optionSustainedPerformanceMode;
#endif

extern Byte1Option optionDitherImage;

#ifdef EMU_FRAMEWORK_WINDOW_PIXEL_FORMAT_OPTION
extern Byte1Option optionWindowPixelFormat;
#endif
extern Byte1Option optionGPUMultiThreading;

static const char *optionSavePathDefaultToken = ":DEFAULT:";
extern PathOption optionSavePath;

+ 14
- 10
EmuFramework/src/EmuSystem.cc View File

@@ -57,7 +57,6 @@ FS::FileString EmuSystem::originalGameName_{};
Base::FrameTimeBase EmuSystem::startFrameTime = 0;
Base::FrameTimeBase EmuSystem::timePerVideoFrame = 0;
uint EmuSystem::emuFrameNow = 0;
bool EmuSystem::runFrameOnDraw = false;
int EmuSystem::saveStateSlot = 0;
Audio::PcmFormat EmuSystem::pcmFormat = {44100, Audio::SampleFormats::s16, 2};
uint EmuSystem::audioFramesPerVideoFrame = 0;
@@ -125,10 +124,10 @@ static uint audioFramesWritten()

static bool shouldStartAudioWrites()
{
// audio starts when at least 1 video frame worth of data is written
// audio starts when at least 90% of a video frame worth of data is written
// and there are <= 2 video frames worth of free space left in buffer
return audioFramesFree() <= EmuSystem::audioFramesPerVideoFrame * 2
&& audioFramesWritten() >= EmuSystem::audioFramesPerVideoFrame;
&& audioFramesWritten() >= EmuSystem::audioFramesPerVideoFrame * 0.9;
}

template<typename T>
@@ -180,8 +179,12 @@ void EmuSystem::startSound()
if(!audioStream->isOpen())
{
uint wantedLatency = std::round(optionSoundBuffers * (1000000. * frameTime()));
rBuff.init(pcmFormat.uSecsToBytes(wantedLatency));
logMsg("created audio buffer with %d frames (%uus)", pcmFormat.bytesToFrames(rBuff.freeSpace()), wantedLatency);
auto buffSize = pcmFormat.uSecsToBytes(wantedLatency);
if(buffSize != rBuff.capacity())
{
rBuff.init(buffSize);
logMsg("created audio buffer with %d frames (%uus)", pcmFormat.bytesToFrames(rBuff.freeSpace()), wantedLatency);
}
audioWriteState = AudioWriteState::BUFFER;
Audio::OutputStreamConfig outputConf
{
@@ -228,6 +231,7 @@ void EmuSystem::startSound()
startAudioStats();
if(shouldStartAudioWrites())
{
logMsg("resuming audio writes with buffer fill %u/%u bytes", rBuff.size(), rBuff.capacity());
audioWriteState = AudioWriteState::ACTIVE;
}
else
@@ -244,17 +248,16 @@ void EmuSystem::stopSound()
if(optionSound)
{
stopAudioStats();
audioWriteState = AudioWriteState::BUFFER;
if(audioStream)
audioStream->pause();
audioStream->close();
rBuff.reset();
}
}

void EmuSystem::closeSound()
{
stopAudioStats();
audioWriteState = AudioWriteState::BUFFER;
if(audioStream)
audioStream->close();
stopSound();
rBuff.deinit();
}

@@ -266,6 +269,7 @@ void EmuSystem::flushSound()
audioWriteState = AudioWriteState::BUFFER;
if(audioStream)
audioStream->flush();
rBuff.reset();
}
}


+ 16
- 21
EmuFramework/src/EmuVideo.cc View File

@@ -38,10 +38,11 @@ void EmuVideo::setFormat(IG::PixmapDesc desc)
{
Gfx::TextureConfig conf{desc};
conf.setWillWriteOften(true);
vidImg.init(r, conf);
vidImg = r.makePixmapTexture(conf);
}
else
{
rendererTask.haltDrawing();
vidImg.setFormat(desc, 1);
}
logMsg("resized to:%dx%d", desc.w(), desc.h());
@@ -57,6 +58,8 @@ void EmuVideo::setFormat(IG::PixmapDesc desc)

EmuVideoImage EmuVideo::startFrame()
{
if(vidImg.needsExclusiveLock())
rendererTask.haltDrawing();
auto lockedTex = vidImg.lock(0);
if(!lockedTex)
{
@@ -70,32 +73,29 @@ EmuVideoImage EmuVideo::startFrame()
return {*this, lockedTex};
}

void EmuVideo::writeFrame(Gfx::LockedTextureBuffer texBuff)
void EmuVideo::startFrame(IG::Pixmap pix)
{
if(vidImg.needsExclusiveLock())
rendererTask.haltDrawing();
finishFrame(pix);
}

void EmuVideo::finishFrame(Gfx::LockedTextureBuffer texBuff)
{
if(unlikely(screenshotNextFrame))
{
doScreenshot(texBuff.pixmap());
}
vidImg.unlock(texBuff);
if(renderNextFrame)
{
renderNextFrame = false;
updateAndDrawEmuVideo();
}
}

void EmuVideo::writeFrame(IG::Pixmap pix)
void EmuVideo::finishFrame(IG::Pixmap pix)
{
if(screenshotNextFrame)
if(unlikely(screenshotNextFrame))
{
doScreenshot(pix);
}
vidImg.write(0, pix, {}, vidImg.bestAlignment(pix));
if(renderNextFrame)
{
renderNextFrame = false;
updateAndDrawEmuVideo();
}
}

void EmuVideo::takeGameScreenshot()
@@ -103,11 +103,6 @@ void EmuVideo::takeGameScreenshot()
screenshotNextFrame = true;
}

void EmuVideo::renderNextFrameToApp()
{
renderNextFrame = true;
}

void EmuVideo::doScreenshot(IG::Pixmap pix)
{
screenshotNextFrame = false;
@@ -148,11 +143,11 @@ void EmuVideoImage::endFrame()
{
if(texBuff)
{
emuVideo->writeFrame(texBuff);
emuVideo->finishFrame(texBuff);
}
else if(pix)
{
emuVideo->writeFrame(pix);
emuVideo->finishFrame(pix);
}
}


+ 20
- 19
EmuFramework/src/EmuVideoLayer.cc View File

@@ -38,7 +38,7 @@ void EmuVideoLayer::resetImage()
if(vidImgEffect.renderTarget())
{
logMsg("drawing video via render target");
disp.setImg(&vidImgEffect.renderTarget().texture());
disp.setImg(&vidImgEffect.renderTarget());
}
else
#endif
@@ -225,44 +225,45 @@ void EmuVideoLayer::place(const IG::WindowRect &viewportRect, const Gfx::Project
placeEffect();
}

void EmuVideoLayer::draw(const Gfx::ProjectionPlane &projP)
void EmuVideoLayer::draw(Gfx::RendererCommands &cmds, const Gfx::ProjectionPlane &projP)
{
using namespace Gfx;
auto &r = video.renderer();
if(EmuSystem::isStarted())
{
bool videoActive = true;
if(unlikely(!EmuSystem::isActive()))
{
r.setColor(.25, .25, .25);
cmds.setColor(.25, .25, .25);
videoActive = false;
}

r.setBlendMode(0);
cmds.setBlendMode(0);
#ifdef CONFIG_GFX_OPENGL_SHADER_PIPELINE
if(vidImgEffect.program())
{
auto prevViewport = r.viewport();
r.setClipRect(false);
r.setProgram(vidImgEffect.program());
r.setRenderTarget(vidImgEffect.renderTarget());
r.clear();
vidImgEffect.drawRenderTarget(r, video.image());
r.setRenderTarget({});
r.setViewport(prevViewport);
disp.useDefaultProgram(videoActive ? IMG_MODE_REPLACE : IMG_MODE_MODULATE, projP.makeTranslate());
auto prevViewport = cmds.viewport();
cmds.setClipTest(false);
cmds.setProgram(vidImgEffect.program());
cmds.setRenderTarget(vidImgEffect.renderTarget());
cmds.setDither(false);
cmds.clear();
vidImgEffect.drawRenderTarget(cmds, video.image());
cmds.setDefaultRenderTarget();
cmds.setDither(true);
cmds.setViewport(prevViewport);
disp.setCommonProgram(cmds, videoActive ? IMG_MODE_REPLACE : IMG_MODE_MODULATE, projP.makeTranslate());
}
else
#endif
{
disp.useDefaultProgram(videoActive ? IMG_MODE_REPLACE : IMG_MODE_MODULATE, projP.makeTranslate());
disp.setCommonProgram(cmds, videoActive ? IMG_MODE_REPLACE : IMG_MODE_MODULATE, projP.makeTranslate());
}
if(useLinearFilter)
Gfx::TextureSampler::bindDefaultNoMipClampSampler(r);
cmds.setCommonTextureSampler(Gfx::CommonTextureSampler::NO_MIP_CLAMP);
else
Gfx::TextureSampler::bindDefaultNoLinearNoMipClampSampler(r);
disp.draw(r);
vidImgOverlay.draw(r);
cmds.setCommonTextureSampler(Gfx::CommonTextureSampler::NO_LINEAR_NO_MIP_CLAMP);
disp.draw(cmds);
vidImgOverlay.draw(cmds);
}
}


+ 18
- 12
EmuFramework/src/EmuView.cc View File

@@ -23,29 +23,35 @@ EmuView::EmuView(ViewAttachParams attach, EmuVideoLayer *layer, EmuInputView *in
inputView{inputView}
{}

void EmuView::draw()
void EmuView::prepareDraw()
{
#ifdef CONFIG_EMUFRAMEWORK_AUDIO_STATS
audioStatsText.makeGlyphs(renderer());
#endif
}

void EmuView::draw(Gfx::RendererCommands &cmds)
{
using namespace Gfx;
if(layer)
{
layer->draw(projP);
layer->draw(cmds, projP);
}
if(EmuSystem::isActive() && inputView)
{
renderer().loadTransform(projP.makeTranslate());
inputView->draw();
cmds.loadTransform(projP.makeTranslate());
inputView->draw(cmds);
}
#ifdef CONFIG_EMUFRAMEWORK_AUDIO_STATS
if(strlen(audioStatsStr.data()))
{
auto &r = renderer();
r.noTexProgram.use(r);
r.setBlendMode(BLEND_MODE_ALPHA);
r.setColor(0., 0., 0., .7);
GeomRect::draw(r, audioStatsRect);
r.setColor(1., 1., 1., 1.);
r.texAlphaProgram.use(r);
audioStatsText.draw(r, projP.alignXToPixel(audioStatsRect.x + TableView::globalXIndent),
cmds.setCommonProgram(CommonProgram::NO_TEX);
cmds.setBlendMode(BLEND_MODE_ALPHA);
cmds.setColor(0., 0., 0., .7);
GeomRect::draw(cmds, audioStatsRect);
cmds.setColor(1., 1., 1., 1.);
cmds.setCommonProgram(CommonProgram::TEX_ALPHA);
audioStatsText.draw(cmds, projP.alignXToPixel(audioStatsRect.x + TableView::globalXIndent),
projP.alignYToPixel(audioStatsRect.yCenter()), LC2DO, projP);
}
#endif

+ 8
- 9
EmuFramework/src/InputManagerView.cc View File

@@ -61,17 +61,16 @@ bool IdentInputDeviceView::inputEvent(Input::Event e)
return false;
}

void IdentInputDeviceView::draw()
void IdentInputDeviceView::draw(Gfx::RendererCommands &cmds)
{
using namespace Gfx;
auto &r = renderer();
r.setBlendMode(0);
r.noTexProgram.use(r, projP.makeTranslate());
r.setColor(.4, .4, .4, 1.);
GeomRect::draw(r, viewFrame, projP);
r.setColor(COLOR_WHITE);
r.texAlphaProgram.use(r);
text.draw(r, 0, 0, C2DO, projP);
cmds.setBlendMode(0);
cmds.setCommonProgram(CommonProgram::NO_TEX, projP.makeTranslate());
cmds.setColor(.4, .4, .4, 1.);
GeomRect::draw(cmds, viewFrame, projP);
cmds.setColor(COLOR_WHITE);
cmds.setCommonProgram(CommonProgram::TEX_ALPHA);
text.draw(cmds, 0, 0, C2DO, projP);
}

static void removeKeyConfFromAllDevices(const KeyConfig *conf)

+ 14
- 9
EmuFramework/src/MsgPopup.cc View File

@@ -103,17 +103,22 @@ void MsgPopup::vprintf(uint secs, bool error, const char *format, va_list args)
postContent(secs, error);
}

void MsgPopup::draw()
void MsgPopup::prepareDraw()
{
text.makeGlyphs(r);
}

void MsgPopup::draw(Gfx::RendererCommands &cmds)
{
using namespace Gfx;
if(strlen(str.data()))
{
r.noTexProgram.use(r, projP.makeTranslate());
r.setBlendMode(BLEND_MODE_ALPHA);
cmds.setCommonProgram(CommonProgram::NO_TEX, projP.makeTranslate());
cmds.setBlendMode(BLEND_MODE_ALPHA);
if(error)
r.setColor(1., 0, 0, .7);
cmds.setColor(1., 0, 0, .7);
else
r.setColor(0, 0, 1., .7);
cmds.setColor(0, 0, 1., .7);
Gfx::GCRect rect(-projP.wHalf(), -projP.hHalf(),
projP.wHalf(), -projP.hHalf() + (text.ySize * 1.5));
#if CONFIG_ENV_WEBOS_OS >= 3
@@ -124,9 +129,9 @@ void MsgPopup::draw()
rect.y2 = projP.hHalf;
}
#endif
GeomRect::draw(r, rect);
r.setColor(1., 1., 1., 1.);
r.texAlphaProgram.use(r);
text.draw(r, 0, projP.alignYToPixel(rect.y + (text.ySize * 1.5)/2.), C2DO, projP);
GeomRect::draw(cmds, rect);
cmds.setColor(1., 1., 1., 1.);
cmds.setCommonProgram(CommonProgram::TEX_ALPHA);
text.draw(cmds, 0, projP.alignYToPixel(rect.y + (text.ySize * 1.5)/2.), C2DO, projP);
}
}

+ 43
- 20
EmuFramework/src/OptionView.cc View File

@@ -241,6 +241,12 @@ static void setWindowPixelFormat(PixelFormatID format)
}
#endif

static void setGPUMultiThreading(Gfx::Renderer::ThreadMode mode)
{
popup.post("Restart app for option to take effect");
optionGPUMultiThreading = (int)mode;
}

static void setFontSize(uint val)
{
optionFontSize = val;
@@ -311,13 +317,12 @@ public:
return false;
}

void draw() final
void draw(Gfx::RendererCommands &cmds) final
{
using namespace Gfx;
auto &r = renderer();
r.setColor(1., 1., 1., 1.);
r.texAlphaProgram.use(r, projP.makeTranslate());
fpsText.draw(r, projP.alignXToPixel(projP.bounds().xCenter()),
cmds.setColor(1., 1., 1., 1.);
cmds.setCommonProgram(CommonProgram::TEX_ALPHA, projP.makeTranslate());
fpsText.draw(cmds, projP.alignXToPixel(projP.bounds().xCenter()),
projP.alignYToPixel(projP.bounds().yCenter()), C2DO, projP);
}

@@ -353,7 +358,7 @@ public:
avgFrameTimeDiff, totalFrameTimeSecs(), avgFrameTimeSecs, totalFrames);
onDetectFrameTime(avgFrameTimeSecs);
popAndShow();
return;
return false;
}
else
lastAverageFrameTimeSecs = averageFrameTimeSecs();
@@ -371,17 +376,19 @@ public:
totalFrameTimeSecs(), averageFrameTimeSecs(), totalFrames);
onDetectFrameTime(0);
popAndShow();
return false;
}
else
{
params.readdOnFrame();
return true;
}
};
params.screen().addOnFrame(detectFrameRate);
return false;
}
else
{
params.readdOnFrame();
return true;
}
};
emuWin->win.screen()->addOnFrame(detectFrameRate);
@@ -425,10 +432,6 @@ void VideoOptionView::loadStockItems()
#endif
item.emplace_back(&overlayEffect);
item.emplace_back(&overlayEffectLevel);
if(!optionDitherImage.isConst)
{
item.emplace_back(&dither);
}
item.emplace_back(&screenShapeHeading);
item.emplace_back(&zoom);
item.emplace_back(&viewportZoom);
@@ -446,6 +449,7 @@ void VideoOptionView::loadStockItems()
#ifdef EMU_FRAMEWORK_WINDOW_PIXEL_FORMAT_OPTION
item.emplace_back(&windowPixelFormat);
#endif
item.emplace_back(&gpuMultithreading);
#if defined CONFIG_BASE_MULTI_WINDOW && defined CONFIG_BASE_X11
item.emplace_back(&secondDisplay);
#endif
@@ -600,7 +604,7 @@ VideoOptionView::VideoOptionView(ViewAttachParams attach, bool customMenu):
}
return true;
};
if(!Gfx::Texture::isAndroidGraphicBufferStorageWhitelisted())
if(!Gfx::Texture::isAndroidGraphicBufferStorageWhitelisted(renderer()))
{
auto &ynAlertView = *new YesNoAlertView{view.attachParams(),
"Setting Graphic Buffer improves performance but may hang or crash "
@@ -951,15 +955,34 @@ VideoOptionView::VideoOptionView(ViewAttachParams attach, bool customMenu):
}
},
#endif
dither
gpuMultithreadingItem
{
"Dither Image",
(bool)optionDitherImage,
[this](BoolMenuItem &item, View &, Input::Event e)
{"Auto", [this]() { setGPUMultiThreading(Gfx::Renderer::ThreadMode::AUTO); }},
{"Off", [this]() { setGPUMultiThreading(Gfx::Renderer::ThreadMode::SINGLE); }},
{"On", [this]() { setGPUMultiThreading(Gfx::Renderer::ThreadMode::MULTI); }},
},
gpuMultithreading
{
"Render Multithreading",
[this](int idx) -> const char*
{
optionDitherImage = item.flipBoolValue(*this);
renderer().setDither(optionDitherImage);
}
if(idx == 0)
{
return renderer().threadMode() == Gfx::Renderer::ThreadMode::MULTI ? "On" : "Off";
}
else
return nullptr;
},
[]()
{
switch(optionGPUMultiThreading.val)
{
default: return 0;
case (int)Gfx::Renderer::ThreadMode::SINGLE: return 1;
case (int)Gfx::Renderer::ThreadMode::MULTI: return 2;
}
}(),
gpuMultithreadingItem
},
visualsHeading{"Visuals"},
screenShapeHeading{"Screen Shape"},

+ 2
- 2
EmuFramework/src/Screenshot.cc View File

@@ -54,7 +54,7 @@ bool writeScreenshot(const IG::Pixmap &vidPix, const char *fname)
namespace Base
{

JNIEnv* jEnv(); // JNIEnv of main event thread
JNIEnv* jEnvForThread(); // JNIEnv of main event thread
extern jclass jBaseActivityCls;
extern jobject jBaseActivity;

@@ -65,7 +65,7 @@ bool writeScreenshot(const IG::Pixmap &vidPix, const char *fname)
static JavaInstMethod<jobject(jint, jint, jint)> jMakeBitmap;
static JavaInstMethod<jboolean(jobject, jobject)> jWritePNG;
using namespace Base;
auto env = jEnv();
auto env = jEnvForThread();
if(!jMakeBitmap)
{
jMakeBitmap.setup(env, jBaseActivityCls, "makeBitmap", "(III)Landroid/graphics/Bitmap;");

+ 20
- 25
EmuFramework/src/TouchConfigView.cc View File

@@ -149,7 +149,7 @@ public:
void init();
void place() final;
bool inputEvent(Input::Event e) final;
void draw() final;
void draw(Gfx::RendererCommands &cmds) final;
void onAddedToController(Input::Event e) final {}
};

@@ -161,12 +161,9 @@ void OnScreenInputPlaceView::init()
animate =
[this](Base::Screen::FrameParams params)
{
window().postDraw();
postDraw();
//logMsg("updating fade");
if(textFade.update(1))
{
params.readdOnFrame();
}
return textFade.update(1);
};
animationStartTimer.callbackAfterSec(
[this]()
@@ -268,29 +265,28 @@ bool OnScreenInputPlaceView::inputEvent(Input::Event e)
return true;
}

void OnScreenInputPlaceView::draw()
void OnScreenInputPlaceView::draw(Gfx::RendererCommands &cmds)
{
using namespace Gfx;
auto &r = renderer();
projP.resetTransforms(r);
vController.draw(true, false, true, .75);
r.setColor(.5, .5, .5);
r.noTexProgram.use(r, projP.makeTranslate());
projP.resetTransforms(cmds);
vController.draw(cmds, true, false, true, .75);
cmds.setColor(.5, .5, .5);
cmds.setCommonProgram(CommonProgram::NO_TEX, projP.makeTranslate());
Gfx::GC lineSize = projP.unprojectYSize(1);
GeomRect::draw(r, Gfx::GCRect{-projP.wHalf(), -lineSize/(Gfx::GC)2.,
GeomRect::draw(cmds, Gfx::GCRect{-projP.wHalf(), -lineSize/(Gfx::GC)2.,
projP.wHalf(), lineSize/(Gfx::GC)2.});
lineSize = projP.unprojectYSize(1);
GeomRect::draw(r, Gfx::GCRect{-lineSize/(Gfx::GC)2., -projP.hHalf(),
GeomRect::draw(cmds, Gfx::GCRect{-lineSize/(Gfx::GC)2., -projP.hHalf(),
lineSize/(Gfx::GC)2., projP.hHalf()});

if(textFade.now() != 0.)
{
r.setColor(0., 0., 0., textFade.now()/2.);
GeomRect::draw(r, Gfx::makeGCRectRel({-text.xSize/(Gfx::GC)2. - text.spaceSize, -text.ySize/(Gfx::GC)2. - text.spaceSize},
cmds.setColor(0., 0., 0., textFade.now()/2.);
GeomRect::draw(cmds, Gfx::makeGCRectRel({-text.xSize/(Gfx::GC)2. - text.spaceSize, -text.ySize/(Gfx::GC)2. - text.spaceSize},
{text.xSize + text.spaceSize*(Gfx::GC)2., text.ySize + text.spaceSize*(Gfx::GC)2.}));
r.setColor(1., 1., 1., textFade.now());
r.texAlphaProgram.use(r);
text.draw(r, projP.unProjectRect(viewFrame).pos(C2DO), C2DO, projP);
cmds.setColor(1., 1., 1., textFade.now());
cmds.setCommonProgram(CommonProgram::TEX_ALPHA);
text.draw(cmds, projP.unProjectRect(viewFrame).pos(C2DO), C2DO, projP);
}
}

@@ -376,6 +372,7 @@ static void setButtonStagger(uint val)
EmuControls::setupVControllerVars();
vController.place();
}
#endif

static void setButtonState(uint state, uint btnIdx)
{
@@ -383,7 +380,6 @@ static void setButtonState(uint state, uint btnIdx)
vControllerLayoutPosChanged = true;
EmuControls::setupVControllerVars();
}
#endif

static void setAlpha(uint val)
{
@@ -391,13 +387,12 @@ static void setAlpha(uint val)
vController.setAlpha((int)optionTouchCtrlAlpha / 255.0);
}

void TouchConfigView::draw()
void TouchConfigView::draw(Gfx::RendererCommands &cmds)
{
using namespace Gfx;
auto &r = renderer();
projP.resetTransforms(r);
vController.draw(true, false, true, .75);
TableView::draw();
projP.resetTransforms(cmds);
vController.draw(cmds, true, false, true, .75);
TableView::draw(cmds);
}

void TouchConfigView::place()

+ 56
- 54
EmuFramework/src/VController.cc View File

@@ -48,7 +48,7 @@ void VControllerDPad::updateBoundingAreaGfx(Gfx::Renderer &r)
: IG::isOdd(input) ? IG::PIXEL_DESC_RGB565.build(1., 1., 1., 1.)
: IG::PIXEL_DESC_RGB565.build(0., 1., 0., 1.);
}
mapImg.init(r, {mapPix});
mapImg = r.makePixmapTexture({mapPix});
mapImg.write(0, mapPix, {});
mapSpr.init({}, mapImg);
mapSpr.setPos(padArea, mainWin.projectionPlane);
@@ -129,16 +129,16 @@ void VControllerDPad::setBoundingAreaVisible(Gfx::Renderer &r, bool on)
}
}

void VControllerDPad::draw(Gfx::Renderer &r) const
void VControllerDPad::draw(Gfx::RendererCommands &cmds) const
{
Gfx::TextureSampler::bindDefaultNearestMipClampSampler(r);
spr.useDefaultProgram(Gfx::IMG_MODE_MODULATE);
spr.draw(r);
cmds.setCommonTextureSampler(Gfx::CommonTextureSampler::NEAREST_MIP_CLAMP);
spr.setCommonProgram(cmds, Gfx::IMG_MODE_MODULATE);
spr.draw(cmds);

if(visualizeBounds)
{
mapSpr.useDefaultProgram(Gfx::IMG_MODE_MODULATE);
mapSpr.draw(r);
mapSpr.setCommonProgram(cmds, Gfx::IMG_MODE_MODULATE);
mapSpr.draw(cmds);
}
}

@@ -207,24 +207,24 @@ void VControllerKeyboard::place(Gfx::GC btnSize, Gfx::GC yOffset)
logMsg("key size %dx%d", keyXSize, keyYSize);
}

void VControllerKeyboard::draw(Gfx::Renderer &r, const Gfx::ProjectionPlane &projP) const
void VControllerKeyboard::draw(Gfx::RendererCommands &cmds, const Gfx::ProjectionPlane &projP) const
{
if(spr.image()->levels() > 1)
Gfx::TextureSampler::bindDefaultNearestMipClampSampler(r);
cmds.setCommonTextureSampler(Gfx::CommonTextureSampler::NEAREST_MIP_CLAMP);
else
Gfx::TextureSampler::bindDefaultNoMipClampSampler(r);
spr.useDefaultProgram(Gfx::IMG_MODE_MODULATE);
spr.draw(r);
cmds.setCommonTextureSampler(Gfx::CommonTextureSampler::NO_MIP_CLAMP);
spr.setCommonProgram(cmds, Gfx::IMG_MODE_MODULATE);
spr.draw(cmds);
if(selected.x != -1)
{
r.setColor(.2, .71, .9, 1./3.);
r.noTexProgram.use(r, projP.makeTranslate());
cmds.setColor(.2, .71, .9, 1./3.);
cmds.setCommonProgram(Gfx::CommonProgram::NO_TEX, projP.makeTranslate());
IG::WindowRect rect{};
rect.x = bound.x + (selected.x * keyXSize);
rect.x2 = bound.x + ((selected.x2 + 1) * keyXSize);
rect.y = bound.y + (selected.y * keyYSize);
rect.y2 = rect.y + keyYSize;
Gfx::GeomRect::draw(r, rect, projP);
Gfx::GeomRect::draw(cmds, rect, projP);
}
}

@@ -786,76 +786,76 @@ std::array<int, 2> VControllerGamepad::getBtnInput(IG::WP pos) const
return btnOut;
}

void VControllerGamepad::draw(Gfx::Renderer &r, bool showHidden) const
void VControllerGamepad::draw(Gfx::RendererCommands &cmds, bool showHidden) const
{
using namespace Gfx;
if(dp.state() == 1 || (showHidden && dp.state()))
{
dp.draw(r);
dp.draw(cmds);
}

if(faceBtnsState == 1 || (showHidden && faceBtnsState))
{
TextureSampler::bindDefaultNearestMipClampSampler(r);
cmds.setCommonTextureSampler(Gfx::CommonTextureSampler::NEAREST_MIP_CLAMP);
if(showBoundingArea)
{
r.noTexProgram.use(r);
cmds.setCommonProgram(CommonProgram::NO_TEX);
iterateTimes((EmuSystem::inputHasTriggerBtns && !triggersInline_) ? EmuSystem::inputFaceBtns-2 : activeFaceBtns, i)
{
GeomRect::draw(r, faceBtnBound[i], mainWin.projectionPlane);
GeomRect::draw(cmds, faceBtnBound[i], mainWin.projectionPlane);
}
}
circleBtnSpr[0].useDefaultProgram(Gfx::IMG_MODE_MODULATE);
circleBtnSpr[0].setCommonProgram(cmds, Gfx::IMG_MODE_MODULATE);
//GeomRect::draw(faceBtnsBound);
iterateTimes((EmuSystem::inputHasTriggerBtns && !triggersInline_) ? EmuSystem::inputFaceBtns-2 : activeFaceBtns, i)
{
circleBtnSpr[i].draw(r);
circleBtnSpr[i].draw(cmds);
}
}

if(EmuSystem::inputHasTriggerBtns && !triggersInline_)
{
TextureSampler::bindDefaultNearestMipClampSampler(r);
cmds.setCommonTextureSampler(Gfx::CommonTextureSampler::NEAREST_MIP_CLAMP);
if(showBoundingArea)
{
if(lTriggerState_ == 1 || (showHidden && lTriggerState_))
{
r.noTexProgram.use(r);
GeomRect::draw(r, faceBtnBound[lTriggerIdx()], mainWin.projectionPlane);
cmds.setCommonProgram(CommonProgram::NO_TEX);
GeomRect::draw(cmds, faceBtnBound[lTriggerIdx()], mainWin.projectionPlane);
}
if(rTriggerState_ == 1 || (showHidden && rTriggerState_))
{
r.noTexProgram.use(r);
GeomRect::draw(r, faceBtnBound[rTriggerIdx()], mainWin.projectionPlane);
cmds.setCommonProgram(CommonProgram::NO_TEX);
GeomRect::draw(cmds, faceBtnBound[rTriggerIdx()], mainWin.projectionPlane);
}
}
if(lTriggerState_ == 1 || (showHidden && lTriggerState_))
{
circleBtnSpr[lTriggerIdx()].useDefaultProgram(Gfx::IMG_MODE_MODULATE);
circleBtnSpr[lTriggerIdx()].draw(r);
circleBtnSpr[lTriggerIdx()].setCommonProgram(cmds, Gfx::IMG_MODE_MODULATE);
circleBtnSpr[lTriggerIdx()].draw(cmds);
}
if(rTriggerState_ == 1 || (showHidden && rTriggerState_))
{
circleBtnSpr[rTriggerIdx()].useDefaultProgram(Gfx::IMG_MODE_MODULATE);
circleBtnSpr[rTriggerIdx()].draw(r);
circleBtnSpr[rTriggerIdx()].setCommonProgram(cmds, Gfx::IMG_MODE_MODULATE);
circleBtnSpr[rTriggerIdx()].draw(cmds);
}
}

if(centerBtnsState == 1 || (showHidden && centerBtnsState))
{
TextureSampler::bindDefaultNearestMipClampSampler(r);
cmds.setCommonTextureSampler(Gfx::CommonTextureSampler::NEAREST_MIP_CLAMP);
if(showBoundingArea)
{
r.noTexProgram.use(r);
cmds.setCommonProgram(CommonProgram::NO_TEX);
iterateTimes(EmuSystem::inputCenterBtns, i)
{
GeomRect::draw(r, centerBtnBound[i], mainWin.projectionPlane);
GeomRect::draw(cmds, centerBtnBound[i], mainWin.projectionPlane);
}
}
centerBtnSpr[0].useDefaultProgram(Gfx::IMG_MODE_MODULATE);
centerBtnSpr[0].setCommonProgram(cmds, Gfx::IMG_MODE_MODULATE);
iterateTimes(EmuSystem::inputCenterBtns, i)
{
centerBtnSpr[i].draw(r);
centerBtnSpr[i].draw(cmds);
}
}
}
@@ -1104,45 +1104,44 @@ bool VController::keyInput(Input::Event e)
return kb.keyInput(*this, renderer_, e);
}

void VController::draw(bool emuSystemControls, bool activeFF, bool showHidden)
void VController::draw(Gfx::RendererCommands &cmds, bool emuSystemControls, bool activeFF, bool showHidden)
{
draw(emuSystemControls, activeFF, showHidden, alpha);
draw(cmds, emuSystemControls, activeFF, showHidden, alpha);
}

void VController::draw(bool emuSystemControls, bool activeFF, bool showHidden, float alpha)
void VController::draw(Gfx::RendererCommands &cmds, bool emuSystemControls, bool activeFF, bool showHidden, float alpha)
{
using namespace Gfx;
if(unlikely(alpha == 0.))
return;
auto &r = renderer_;
r.setBlendMode(BLEND_MODE_ALPHA);
r.setColor(1., 1., 1., alpha);
cmds.setBlendMode(BLEND_MODE_ALPHA);
cmds.setColor(1., 1., 1., alpha);
#ifdef CONFIG_VCONTROLS_GAMEPAD
if(isInKeyboardMode())
kb.draw(r, mainWin.projectionPlane);
kb.draw(cmds, mainWin.projectionPlane);
else if(emuSystemControls)
gp.draw(r, showHidden);
gp.draw(cmds, showHidden);
#else
if(isInKeyboardMode())
kb.draw(r, mainWin.projectionPlane);
kb.draw(cmds, mainWin.projectionPlane);
#endif
//GeomRect::draw(menuBound);
//GeomRect::draw(ffBound);
if(menuBtnState == 1 || (showHidden && menuBtnState))
{
r.setColor(1., 1., 1., alpha);
TextureSampler::bindDefaultNearestMipClampSampler(r);
menuBtnSpr.useDefaultProgram(IMG_MODE_MODULATE);
menuBtnSpr.draw(r);
cmds.setColor(1., 1., 1., alpha);
cmds.setCommonTextureSampler(Gfx::CommonTextureSampler::NEAREST_MIP_CLAMP);
menuBtnSpr.setCommonProgram(cmds, IMG_MODE_MODULATE);
menuBtnSpr.draw(cmds);
}
if(ffBtnState == 1 || (showHidden && ffBtnState))
{
r.setColor(1., 1., 1., alpha);
TextureSampler::bindDefaultNearestMipClampSampler(r);
ffBtnSpr.useDefaultProgram(IMG_MODE_MODULATE);
cmds.setColor(1., 1., 1., alpha);
cmds.setCommonTextureSampler(Gfx::CommonTextureSampler::NEAREST_MIP_CLAMP);
ffBtnSpr.setCommonProgram(cmds, IMG_MODE_MODULATE);
if(activeFF)
r.setColor(1., 0., 0., alpha);
ffBtnSpr.draw(r);
cmds.setColor(1., 0., 0., alpha);
ffBtnSpr.draw(cmds);
}
}

@@ -1324,10 +1323,13 @@ void VController::setAlpha(float val)
alpha = val;
}

#ifdef CONFIG_VCONTROLS_GAMEPAD
VControllerGamepad &VController::gamePad()
{
return gp;
}
#endif

Gfx::Renderer &VController::renderer()
{
return renderer_;

+ 18
- 18
EmuFramework/src/VideoImageEffect.cc View File

@@ -98,7 +98,7 @@ void VideoImageEffect::deinit(Gfx::Renderer &r)

void VideoImageEffect::deinitProgram(Gfx::Renderer &r)
{
prog.deinit();
prog.deinit(r);
if(vShader)
{
r.deleteShader(vShader);
@@ -118,12 +118,15 @@ uint VideoImageEffect::effect()

void VideoImageEffect::initRenderTargetTexture(Gfx::Renderer &r)
{
if(!renderTarget_)
if(!renderTargetScale.x)
return;
renderTargetImgSize.x = inputImgSize.x * renderTargetScale.x;
renderTargetImgSize.y = inputImgSize.y * renderTargetScale.y;
IG::PixmapDesc renderPix{renderTargetImgSize, useRGB565RenderTarget ? IG::PIXEL_RGB565 : IG::PIXEL_RGBA8888};
renderTarget_.setFormat(r, renderPix);
if(!renderTarget_)
renderTarget_ = r.makeTexture({renderPix});
else
renderTarget_.setFormat(renderPix, 1);
Gfx::TextureSampler::initDefaultNoLinearNoMipClampSampler(r);
}

@@ -160,7 +163,6 @@ void VideoImageEffect::compile(Gfx::Renderer &r, bool isExternalTex)
}

renderTargetScale = desc->scale;
renderTarget_.init();
initRenderTargetTexture(r);
auto err = compileEffect(r, *desc, isExternalTex, false);
if(err.code())
@@ -236,9 +238,9 @@ std::system_error VideoImageEffect::compileEffect(Gfx::Renderer &r, EffectDesc d
r.autoReleaseShaderCompiler();
return {{EINVAL, std::system_category()}, "GPU rejected shader (link error)"};
}
srcTexelDeltaU = prog.uniformLocation("srcTexelDelta");
srcTexelHalfDeltaU = prog.uniformLocation("srcTexelHalfDelta");
srcPixelsU = prog.uniformLocation("srcPixels");
srcTexelDeltaU = prog.uniformLocation(r, "srcTexelDelta");
srcTexelHalfDeltaU = prog.uniformLocation(r, "srcTexelHalfDelta");
srcPixelsU = prog.uniformLocation(r, "srcPixels");
updateProgramUniforms(r);
r.autoReleaseShaderCompiler();
return {{}};
@@ -246,13 +248,12 @@ std::system_error VideoImageEffect::compileEffect(Gfx::Renderer &r, EffectDesc d

void VideoImageEffect::updateProgramUniforms(Gfx::Renderer &r)
{
r.setProgram(prog);
if(srcTexelDeltaU != -1)
r.uniformF(srcTexelDeltaU, 1.0f / (float)inputImgSize.x, 1.0f / (float)inputImgSize.y);
r.uniformF(prog, srcTexelDeltaU, 1.0f / (float)inputImgSize.x, 1.0f / (float)inputImgSize.y);
if(srcTexelHalfDeltaU != -1)
r.uniformF(srcTexelHalfDeltaU, 0.5f * (1.0f / (float)inputImgSize.x), 0.5f * (1.0f / (float)inputImgSize.y));
r.uniformF(prog, srcTexelHalfDeltaU, 0.5f * (1.0f / (float)inputImgSize.x), 0.5f * (1.0f / (float)inputImgSize.y));
if(srcPixelsU != -1)
r.uniformF(srcPixelsU, inputImgSize.x, inputImgSize.y);
r.uniformF(prog, srcPixelsU, inputImgSize.x, inputImgSize.y);
}

void VideoImageEffect::setImageSize(Gfx::Renderer &r, IG::WP size)
@@ -264,8 +265,7 @@ void VideoImageEffect::setImageSize(Gfx::Renderer &r, IG::WP size)
inputImgSize = size;
if(program())
updateProgramUniforms(r);
if(renderTarget_)
<