Integrate Dear ImGui with SDL to reduce code duplication.

Signed-off-by: Andrea Odetti <mariofutire@gmail.com>
This commit is contained in:
Andrea Odetti 2021-02-06 16:55:08 +00:00
parent 05b2d5b2e7
commit 989410c17d
12 changed files with 287 additions and 14 deletions

View file

@ -24,6 +24,7 @@ bool getEmulatorOptions(int argc, const char * argv [], const std::string & edit
("multi-threaded,m", "Multi threaded")
("loose-mutex,l", "Loose mutex")
("sdl-driver", po::value<int>()->default_value(options.sdlDriver), "SDL driver")
("imgui", "Render with Dear ImGui")
("timer-interval,i", po::value<int>()->default_value(options.timerInterval), "Timer interval in ms");
po::options_description configDesc("configuration");
@ -82,6 +83,7 @@ bool getEmulatorOptions(int argc, const char * argv [], const std::string & edit
options.looseMutex = vm.count("loose-mutex");
options.timerInterval = vm["timer-interval"].as<int>();
options.sdlDriver = vm["sdl-driver"].as<int>();
options.imgui = vm.count("imgui");
if (vm.count("config"))
{

View file

@ -36,6 +36,7 @@ struct EmulatorOptions
bool fixedSpeed = false; // default adaptive
int sdlDriver = -1; // default = -1 to let SDL choose
bool imgui = false; // use imgui renderer
std::vector<std::string> registryOptions;
};

View file

@ -1,5 +1,7 @@
include(FindPkgConfig)
add_executable(sa2)
set(SOURCE_FILES
main.cpp
emulator.cpp
@ -7,7 +9,7 @@ set(SOURCE_FILES
sdirectsound.cpp
utils.cpp
sdlframe.cpp
sdlrendererframe.cpp
renderer/sdlrendererframe.cpp
)
set(HEADER_FILES
@ -16,12 +18,7 @@ set(HEADER_FILES
sdirectsound.h
utils.h
sdlframe.h
sdlrendererframe.h
)
add_executable(sa2
${SOURCE_FILES}
${HEADER_FILES}
renderer/sdlrendererframe.h
)
find_package(SDL2 REQUIRED)
@ -41,5 +38,50 @@ target_link_libraries(sa2 PRIVATE
common2
)
target_sources(sa2 PRIVATE
${SOURCE_FILES}
${HEADER_FILES}
)
set(IMGUI_PATH NONE CACHE PATH "path to imgui")
if (EXISTS ${IMGUI_PATH}/imgui.h)
message("Using IMGUI_PATH=${IMGUI_PATH}")
pkg_search_module(GLES2 REQUIRED glesv2)
target_sources(sa2 PRIVATE
imgui/sdlimguiframe.cpp
imgui/image.cpp
imgui/gles.h
imgui/image.h
imgui/sdlimguiframe.h
${IMGUI_PATH}/imgui.cpp
${IMGUI_PATH}/imgui_demo.cpp
${IMGUI_PATH}/imgui_draw.cpp
${IMGUI_PATH}/imgui_tables.cpp
${IMGUI_PATH}/imgui_widgets.cpp
${IMGUI_PATH}/backends/imgui_impl_sdl.cpp
${IMGUI_PATH}/backends/imgui_impl_opengl3.cpp
)
target_include_directories(sa2 PRIVATE
${IMGUI_PATH}
${IMGUI_PATH}/backends
)
target_link_libraries(sa2 PRIVATE
${GLES2_LIBRARIES}
)
target_compile_definitions(sa2 PRIVATE
SA2_IMGUI
IMGUI_IMPL_OPENGL_ES2
)
else()
message("Bad IMGUI_PATH=${IMGUI_PATH}, skipping imgui code")
endif()
install(TARGETS sa2
DESTINATION bin)

View file

@ -0,0 +1,33 @@
#pragma once
#if defined(IMGUI_IMPL_OPENGL_ES2)
// Pi3 with Fake KMS
// "OpenGL ES 2.0 Mesa 19.3.2"
// "Supported versions are: 1.00 ES"
#include <GLES2/gl2.h>
#define SDL_CONTEXT_MAJOR 2
#elif defined(IMGUI_IMPL_OPENGL_ES3)
// Pi4 with Fake KMS
// "OpenGL ES 3.1 Mesa 19.3.2"
// "Supported versions are: 1.00 ES, 3.00 ES, and 3.10 ES"
// On my desktop
// "OpenGL ES 3.1 Mesa 20.2.6"
// "Supported versions are: 1.00 ES, 3.00 ES, and 3.10 ES"
#include <GLES3/gl3.h>
#define SDL_CONTEXT_MAJOR 3
// "310 es" is accepted on a Pi4, but the imgui shaders do not compile
#endif
// this is used in all cases for GL_BGRA_EXT
#include <GLES2/gl2ext.h>
#include "imgui.h"
#include "imgui_impl_sdl.h"
#include "imgui_impl_opengl3.h"

View file

@ -0,0 +1,43 @@
#include "frontends/sdl/imgui/image.h"
#ifdef GL_UNPACK_ROW_LENGTH
// GLES3: defined in gl3.h
#define UGL_UNPACK_LENGTH GL_UNPACK_ROW_LENGTH
#else
// GLES2: defined in gl2ext.h as an extension GL_EXT_unpack_subimage
#define UGL_UNPACK_LENGTH GL_UNPACK_ROW_LENGTH_EXT
#endif
/*
2 extensions are used here
ES2: GL_UNPACK_ROW_LENGTH_EXT and GL_BGRA_EXT
ES3: GL_BGRA_EXT
They both work on Pi3/4 and my Intel Haswell card.
What does SDL do about it?
GL_UNPACK_ROW_LENGTH_EXT: it uses instead texture coordinates
GL_BGRA_EXT: it uses a shader to swap the color bytes
For simplicity (and because it just works) here we are using these 2 extensions.
*/
void LoadTextureFromData(GLuint texture, const uint8_t * data, size_t width, size_t height, size_t pitch)
{
glBindTexture(GL_TEXTURE_2D, texture);
glPixelStorei(UGL_UNPACK_LENGTH, pitch); // in pixels
// Setup filtering parameters for display
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
const GLenum format = GL_BGRA_EXT; // this is defined in gl2ext.h and nowhere in gl3.h
const GLenum type = GL_UNSIGNED_BYTE;
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, data);
// reset to default state
glPixelStorei(UGL_UNPACK_LENGTH, 0);
}

View file

@ -0,0 +1,6 @@
#pragma once
#include <cstddef>
#include "frontends/sdl/imgui/gles.h"
void LoadTextureFromData(GLuint texture, const uint8_t * data, size_t width, size_t height, size_t pitch);

View file

@ -0,0 +1,104 @@
#include "StdAfx.h"
#include "frontends/sdl/imgui/sdlimguiframe.h"
#include "frontends/sdl/imgui/image.h"
#include "frontends/sdl/utils.h"
#include "Interface.h"
#include "Core.h"
#include <iostream>
SDLImGuiFrame::SDLImGuiFrame()
{
Video & video = GetVideo();
myBorderlessWidth = video.GetFrameBufferBorderlessWidth();
myBorderlessHeight = video.GetFrameBufferBorderlessHeight();
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, SDL_CONTEXT_MAJOR); // from local gles.h
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
// Create window with graphics context
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
SDL_WindowFlags windowFlags = (SDL_WindowFlags)(SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
myWindow.reset(SDL_CreateWindow(g_pAppTitle.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, myBorderlessWidth, myBorderlessHeight, windowFlags), SDL_DestroyWindow);
if (!myWindow)
{
throw std::runtime_error(SDL_GetError());
}
myGLContext = SDL_GL_CreateContext(myWindow.get());
if (!myGLContext)
{
throw std::runtime_error(SDL_GetError());
}
SDL_GL_MakeCurrent(myWindow.get(), myGLContext);
// Setup Platform/Renderer backends
std::cerr << "GL_VENDOR: " << glGetString(GL_VENDOR) << std::endl;
std::cerr << "GL_RENDERER: " << glGetString(GL_RENDERER) << std::endl;
std::cerr << "GL_VERSION: " << glGetString(GL_VERSION) << std::endl;
std::cerr << "GL_SHADING_LANGUAGE_VERSION: " << glGetString(GL_SHADING_LANGUAGE_VERSION) << std::endl;
// const char* runtime_gl_extensions = (const char*)glGetString(GL_EXTENSIONS);
// std::cerr << "GL_EXTENSIONS: " << runtime_gl_extensions << std::endl;
// Setup Dear ImGui context
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO(); (void)io;
//io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
//io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
ImGui::StyleColorsDark();
ImGui_ImplSDL2_InitForOpenGL(myWindow.get(), myGLContext);
ImGui_ImplOpenGL3_Init();
glGenTextures(1, &myTexture);
const int width = video.GetFrameBufferWidth();
const size_t borderWidth = video.GetFrameBufferBorderWidth();
const size_t borderHeight = video.GetFrameBufferBorderHeight();
myPitch = width;
myOffset = (width * borderHeight + borderWidth) * sizeof(bgra_t);
}
SDLImGuiFrame::~SDLImGuiFrame()
{
glDeleteTextures(1, &myTexture);
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplSDL2_Shutdown();
ImGui::DestroyContext();
SDL_GL_DeleteContext(myGLContext);
}
void SDLImGuiFrame::UpdateTexture()
{
LoadTextureFromData(myTexture, myFramebuffer.data() + myOffset, myBorderlessWidth, myBorderlessHeight, myPitch);
}
void SDLImGuiFrame::RenderPresent()
{
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplSDL2_NewFrame(myWindow.get());
ImGui::NewFrame();
// need to flip the texture vertically
const ImVec2 uv0(0, 1);
const ImVec2 uv1(1, 0);
const ImVec2 zero(0, 0);
// draw on the background
ImGui::GetBackgroundDrawList()->AddImage((void*)(intptr_t)myTexture, zero, ImGui::GetIO().DisplaySize, uv0, uv1);
ImGui::Render();
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
SDL_GL_SwapWindow(myWindow.get());
}

View file

@ -0,0 +1,24 @@
#pragma once
#include "frontends/sdl/sdlframe.h"
#include "frontends/sdl/imgui/gles.h"
class SDLImGuiFrame : public SDLFrame
{
public:
SDLImGuiFrame();
~SDLImGuiFrame() override;
void UpdateTexture() override;
void RenderPresent() override;
private:
size_t myPitch;
size_t myOffset;
size_t myBorderlessWidth;
size_t myBorderlessHeight;
SDL_GLContext myGLContext;
GLuint myTexture;
};

View file

@ -17,7 +17,12 @@
#include "frontends/sdl/gamepad.h"
#include "frontends/sdl/sdirectsound.h"
#include "frontends/sdl/utils.h"
#include "frontends/sdl/sdlrendererframe.h"
#include "frontends/sdl/renderer/sdlrendererframe.h"
#ifdef SA2_IMGUI
#include "frontends/sdl/imgui/sdlimguiframe.h"
#endif
#include "Core.h"
#include "Log.h"
@ -79,7 +84,21 @@ void run_sdl(int argc, const char * argv [])
if (!run)
return;
const std::shared_ptr<SDLFrame> frame(new SDLRendererFrame(options));
std::shared_ptr<SDLFrame> frame;
#ifdef SA2_IMGUI
if (options.imgui)
{
frame.reset(new SDLImGuiFrame());
}
else
{
frame.reset(new SDLRendererFrame(options));
}
#else
frame.reset(new SDLRendererFrame(options));
#endif
SetFrame(frame);
if (options.log)

View file

@ -1,5 +1,5 @@
#include "StdAfx.h"
#include "frontends/sdl/sdlrendererframe.h"
#include "frontends/sdl/renderer/sdlrendererframe.h"
#include "frontends/sdl/utils.h"
#include "frontends/common2/programoptions.h"
@ -20,8 +20,7 @@ SDLRendererFrame::SDLRendererFrame(const EmulatorOptions & options)
myWindow.reset(SDL_CreateWindow(g_pAppTitle.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, sw, sh, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE), SDL_DestroyWindow);
if (!myWindow)
{
std::cerr << "SDL_CreateWindow Error: " << SDL_GetError() << std::endl;
return;
throw std::runtime_error(SDL_GetError());
}
SetApplicationIcon();
@ -29,8 +28,7 @@ SDLRendererFrame::SDLRendererFrame(const EmulatorOptions & options)
myRenderer.reset(SDL_CreateRenderer(myWindow.get(), options.sdlDriver, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC), SDL_DestroyRenderer);
if (!myRenderer)
{
std::cerr << "SDL_CreateRenderer Error: " << SDL_GetError() << std::endl;
return;
throw std::runtime_error(SDL_GetError());
}
const Uint32 format = SDL_PIXELFORMAT_ARGB8888;

View file

@ -10,3 +10,4 @@ libncurses-dev
libevdev-dev
libsdl2-image-dev
libsdl2-dev
libgles-dev