From 7174e52c486ab15bacda96dc7a0eb47ee2ad943d Mon Sep 17 00:00:00 2001 From: apoorv569 Date: Sun, 17 Oct 2021 13:38:50 +0530 Subject: [PATCH] Trying out OpenGL to render waveform. --- meson.build | 13 ++-- src/GL.cpp | 97 +++++++++++++++++++++++++++ src/GL.hpp | 9 +++ src/WaveformViewer.cpp | 144 +++++++++++++++++++++++++++++++---------- src/WaveformViewer.hpp | 14 ++++ 5 files changed, 239 insertions(+), 38 deletions(-) create mode 100644 src/GL.cpp create mode 100644 src/GL.hpp diff --git a/meson.build b/meson.build index 53763bf..5bbc419 100755 --- a/meson.build +++ b/meson.build @@ -121,6 +121,7 @@ src = [ 'src/Serialize.cpp', 'src/Tags.cpp', 'src/WaveformViewer.cpp', + 'src/GL.cpp', 'src/SH_Event.cpp', ] @@ -137,12 +138,14 @@ if not wx.found() wx_base = wx_subproject.dependency('wxbase') wx_core = wx_subproject.dependency('wxcore') wx_media = wx_subproject.dependency('wxmedia') - wx = [wx_core, wx_base, wx_media] + wx_gl = wx_subproject.dependency('wxgl') + + wx = [wx_core, wx_base, wx_media, wx_gl] else wx_found = true - wxconfig = find_program(['wx-config-gtk3', 'wx-config']) - wx_modules = ['media', 'std'] + wxconfig = find_program(['wx-config', 'wx-config-gtk3', 'wx-config-3.1']) + wx_modules = ['gl', 'media', 'std'] wx_cxx_flags = [] wx_libs = [] @@ -183,6 +186,8 @@ if not snd.found() snd = snd_subproject.dependency('sndfile') endif +glew = dependency('glew') + # Create samplehive-config.h based on configuration config_h = configure_file(output: 'SampleHiveConfig.hpp', configuration: config_data,) @@ -203,7 +208,7 @@ if wx_found else executable('SampleHive', sources: src, - dependencies: [wx, taglib, sqlite3, yaml, snd], + dependencies: [wx, taglib, sqlite3, yaml, snd, glew], install: true, install_rpath: prefix / 'lib') endif diff --git a/src/GL.cpp b/src/GL.cpp new file mode 100644 index 0000000..19fad5d --- /dev/null +++ b/src/GL.cpp @@ -0,0 +1,97 @@ +#include +#include +#include + +#include "GL.hpp" + +#include + +#include + +static std::string ParseShader(const std::string& filepath) +{ + std::string source; + + std::ifstream stream(filepath); + std::stringstream shader_stream; + + stream.exceptions(std::ifstream::failbit | std::ifstream::badbit); + + try + { + shader_stream << stream.rdbuf(); + stream.close(); + + source = shader_stream.str(); + } + catch(std::ifstream::failure e) + { + std::cout << "Error! Could not parse shader file." << e.code() << e.what() << std::endl; + } + + return source; +} + +static unsigned int CompileShader(unsigned int type, const std::string& source) +{ + unsigned int id = glCreateShader(type); + const char* src = source.c_str(); + glShaderSource(id, 1, &src , nullptr); + glCompileShader(id); + + int result; + glGetShaderiv(id, GL_COMPILE_STATUS, &result); + + if (result == GL_FALSE) + { + int length; + glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length); + char message[length]; + glGetShaderInfoLog(id, length, &length, message); + + std::cout << "Failed to compile " + << (type == GL_VERTEX_SHADER ? "vertex" : "fragment") + << "shader" << std::endl; + + std::cout << message << std::endl; + + glDeleteShader(id); + + return 0; + } + + return id; +} + +static unsigned int CreateShader(unsigned int type, const std::string& source) +{ + unsigned int program = glCreateProgram(); + + unsigned int shader = CompileShader(type, source); + + glAttachShader(program, shader); + glLinkProgram(program); + glValidateProgram(program); + + glDeleteShader(shader); + + return program; +} + +bool GLHelper::InitGlew() +{ + GLenum initStatus = glewInit(); + + return initStatus == GLEW_OK; +} + +void GLHelper::Render() +{ + glClearColor(0.2f, 0.3f, 0.3f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); +} + +void GLHelper::SetSize(int width, int height) +{ + glViewport(0, 0, width, height); +} diff --git a/src/GL.hpp b/src/GL.hpp new file mode 100644 index 0000000..46e4e05 --- /dev/null +++ b/src/GL.hpp @@ -0,0 +1,9 @@ +#pragma once + +class GLHelper +{ + public: + bool InitGlew(); + void Render(); + void SetSize(int w, int h); +}; diff --git a/src/WaveformViewer.cpp b/src/WaveformViewer.cpp index d1b02d5..f704030 100644 --- a/src/WaveformViewer.cpp +++ b/src/WaveformViewer.cpp @@ -45,9 +45,50 @@ WaveformViewer::WaveformViewer(wxWindow* parentFrame, wxWindow* window, wxDataVi m_ParentFrame(parentFrame), m_Window(window), m_Database(database), m_Library(library), m_MediaCtrl(mediaCtrl), m_ConfigFilepath(configFilepath), m_DatabaseFilepath(databaseFilepath) { + // Create the canvas and context. + #if wxCHECK_VERSION(3, 1, 0) + // These settings should work with any GPU from the last 10 years. + wxGLAttributes display_attributes; + display_attributes.PlatformDefaults().RGBA().DoubleBuffer().EndList(); + + wxGLContextAttrs context_attributes; + context_attributes.PlatformDefaults().CoreProfile().OGLVersion(3, 3).EndList(); + + m_Canvas = new wxGLCanvas(this, display_attributes); + m_Context = new wxGLContext(m_Canvas, NULL, &context_attributes); + + if (!m_Context->IsOK()) + { + wxLogDebug("Failed to create context."); + return; + } + #else + int display_attributes[] = + { WX_GL_RGBA, + WX_GL_DOUBLEBUFFER, + WX_GL_CORE_PROFILE, + WX_GL_MAJOR_VERSION ,3, + WX_GL_MINOR_VERSION, 3, + 0 }; + + m_Canvas = new wxGLCanvas(this, wxID_ANY, display_attributes); + m_Context = new wxGLContext(m_Canvas, NULL); + + // Unfortunately, there doesn't seem to be any way to check if the + // context is ok prior to wxWidgets 3.1.0. + #endif // wxCHECK_VERSION + + // On Linux, we must delay delay initialization until the canvas has + // been fully created. On windows, we can finish now. + #ifdef __WXMSW__ + InitGL(); + #elif defined(__WXGTK__) + m_Canvas->Bind(wxEVT_CREATE, [this](wxWindowCreateEvent&){ InitGL(); }); + #endif // defined + this->SetDoubleBuffered(true); - Bind(wxEVT_PAINT, &WaveformViewer::OnPaint, this); + // Bind(wxEVT_PAINT, &WaveformViewer::OnPaint, this); Bind(wxEVT_MOTION, &WaveformViewer::OnMouseMotion, this); Bind(wxEVT_LEFT_DOWN, &WaveformViewer::OnMouseLeftButtonDown, this); Bind(wxEVT_LEFT_UP, &WaveformViewer::OnMouseLeftButtonUp, this); @@ -57,55 +98,90 @@ WaveformViewer::WaveformViewer(wxWindow* parentFrame, wxWindow* window, wxDataVi WaveformViewer::~WaveformViewer() { + delete m_Context; +} +void WaveformViewer::InitGL() +{ + // First call SetCurrent or GL initialization will fail. + m_Context->SetCurrent(*m_Canvas); + + // Initialize GLEW. + bool glewInialized = m_Helper.InitGlew(); + + if (!glewInialized) + { + wxLogDebug("Failed to initialize GLEW."); + return; + } + + wxLogDebug("Context and GLEW initialized."); + + // Bind event handlers for the canvas. Binding was delayed until OpenGL was + // initialized because these handlers will need to call OpenGL functions. + m_Canvas->Bind(wxEVT_SIZE, &WaveformViewer::OnSize, this); + m_Canvas->Bind(wxEVT_PAINT, &WaveformViewer::OnPaint, this); +} + +void WaveformViewer::OnSize(wxSizeEvent &event) +{ + wxSize sz = event.GetSize(); + m_Helper.SetSize(sz.GetWidth(), sz.GetHeight()); + + event.Skip(); } void WaveformViewer::OnPaint(wxPaintEvent& event) { - wxPaintDC dc(this); + wxPaintDC dc(m_Canvas); - const wxSize& size = m_Window->GetClientSize(); + m_Helper.Render(); + m_Canvas->SwapBuffers(); - if (!m_WaveformBitmap.IsOk() - || m_WaveformBitmap.GetWidth() != size.x - || m_WaveformBitmap.GetHeight() != size.y - || bBitmapDirty) - { - wxLogDebug("Updating waveform bitmap.."); + // wxPaintDC dc(this); - m_WaveformBitmap = wxBitmap(wxImage(size.x, size.y), 32); + // const wxSize& size = m_Window->GetClientSize(); - UpdateWaveformBitmap(); + // if (!m_WaveformBitmap.IsOk() + // || m_WaveformBitmap.GetWidth() != size.x + // || m_WaveformBitmap.GetHeight() != size.y + // || bBitmapDirty) + // { + // wxLogDebug("Updating waveform bitmap.."); - bBitmapDirty = false; - } + // m_WaveformBitmap = wxBitmap(wxImage(size.x, size.y), 32); - dc.DrawBitmap(m_WaveformBitmap, 0, 0, false); + // UpdateWaveformBitmap(); - RenderPlayhead(dc); + // bBitmapDirty = false; + // } - // Draw selection range - if (bSelectRange) - { - wxRect rect(m_CurrentPoint, m_AnchorPoint); + // dc.DrawBitmap(m_WaveformBitmap, 0, 0, false); - dc.SetPen(wxPen(wxColour(200, 200, 200), 2, wxPENSTYLE_SOLID)); - dc.SetBrush(wxBrush(wxColour(200, 200, 200, 80), wxBRUSHSTYLE_SOLID)); - dc.DrawRectangle(rect); - } + // RenderPlayhead(dc); - // Draw selected area - if (!bSelectRange && bDrawSelectedArea && !bBitmapDirty) - { - dc.SetPen(wxPen(wxColour(200, 200, 200, 255), 4, wxPENSTYLE_SOLID)); - dc.SetBrush(wxBrush(wxColour(200, 200, 200, 80), wxBRUSHSTYLE_SOLID)); - dc.DrawRectangle(wxRect(m_AnchorPoint.x, -2, m_CurrentPoint.x - m_AnchorPoint.x, this->GetSize().GetHeight() + 5)); + // // Draw selection range + // if (bSelectRange) + // { + // wxRect rect(m_CurrentPoint, m_AnchorPoint); - bAreaSelected = true; - SendLoopPoints(); - } - else - bAreaSelected = false; + // dc.SetPen(wxPen(wxColour(200, 200, 200), 2, wxPENSTYLE_SOLID)); + // dc.SetBrush(wxBrush(wxColour(200, 200, 200, 80), wxBRUSHSTYLE_SOLID)); + // dc.DrawRectangle(rect); + // } + + // // Draw selected area + // if (!bSelectRange && bDrawSelectedArea && !bBitmapDirty) + // { + // dc.SetPen(wxPen(wxColour(200, 200, 200, 255), 4, wxPENSTYLE_SOLID)); + // dc.SetBrush(wxBrush(wxColour(200, 200, 200, 80), wxBRUSHSTYLE_SOLID)); + // dc.DrawRectangle(wxRect(m_AnchorPoint.x, -2, m_CurrentPoint.x - m_AnchorPoint.x, this->GetSize().GetHeight() + 5)); + + // bAreaSelected = true; + // SendLoopPoints(); + // } + // else + // bAreaSelected = false; } void WaveformViewer::RenderPlayhead(wxDC& dc) diff --git a/src/WaveformViewer.hpp b/src/WaveformViewer.hpp index e073d65..e70071e 100644 --- a/src/WaveformViewer.hpp +++ b/src/WaveformViewer.hpp @@ -21,12 +21,14 @@ #pragma once #include "Database.hpp" +#include "GL.hpp" #include #include #include #include #include +#include #include #include #include @@ -48,10 +50,12 @@ class WaveformViewer : public wxPanel wxWindow* m_ParentFrame; wxWindow* m_Window; + // ------------------------------------------------------------------- Database& m_Database; wxDataViewListCtrl& m_Library; wxMediaCtrl& m_MediaCtrl; + // ------------------------------------------------------------------- const std::string& m_ConfigFilepath; const std::string& m_DatabaseFilepath; @@ -66,6 +70,12 @@ class WaveformViewer : public wxPanel wxPoint m_AnchorPoint; wxPoint m_CurrentPoint; + private: + // ------------------------------------------------------------------- + wxGLCanvas* m_Canvas; + wxGLContext* m_Context; + GLHelper m_Helper; + private: // ------------------------------------------------------------------- bool bBitmapDirty = false; @@ -75,6 +85,10 @@ class WaveformViewer : public wxPanel private: // ------------------------------------------------------------------- + void InitGL(); + + // ------------------------------------------------------------------- + void OnSize(wxSizeEvent& event); void OnPaint(wxPaintEvent& event); void RenderPlayhead(wxDC& dc); void UpdateWaveformBitmap();