Skip to content

Conversation

@ford442
Copy link
Owner

@ford442 ford442 commented Nov 27, 2025

bring jules branch up to master

@gemini-code-assist
Copy link

Summary of Changes

Hello @ford442, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request represents a substantial upgrade to the projectM visualization engine, primarily focused on enhancing its WebAssembly (WASM) capabilities and overall performance. It introduces a sophisticated AudioWorklet-based audio processing system for web builds, allowing for more efficient and robust audio analysis. Concurrently, the codebase has been optimized through the adoption of C++17, strategic use of OpenMP for parallel processing, and fine-tuning of OpenGL buffer management for dynamic rendering data. These changes collectively aim to deliver a more performant, stable, and visually rich experience, particularly for web-based deployments.

Highlights

  • Enhanced WebAssembly (WASM) Support: Significant updates to CMakeLists.txt and the introduction of new files (projectM_emscripten.cpp, projectm_audio_processor.js) to bolster projectM's WebAssembly build. This includes upgrading to C++17, enabling advanced WASM features like SIMD, shared memory, WebGL2, and implementing an AudioWorklet-based audio processing pipeline for efficient background audio.
  • OpenGL Buffer Usage Optimization: Numerous C++ files (BlurTexture.cpp, Border.cpp, CustomShape.cpp, CustomWaveform.cpp, DarkenCenter.cpp, Filters.cpp, FinalComposite.cpp, MotionVectors.cpp, PerPixelMesh.cpp, Waveform.cpp, CopyTexture.cpp, PresetTransition.cpp) have transitioned glBufferData calls from GL_STATIC_DRAW or GL_STREAM_DRAW to GL_DYNAMIC_DRAW. This optimization improves performance for frequently updated vertex buffer data, crucial for real-time visualizers.
  • OpenMP Parallelization for Performance: Several core C++ files (CustomShape.cpp, CustomWaveform.cpp, MilkdropPreset.cpp, MilkdropShader.cpp, PerPixelMesh.cpp) now incorporate OpenMP directives (#pragma omp parallel, #pragma omp parallel for, #pragma omp simd). This introduces parallel computation to key rendering and calculation loops, aiming for significant performance gains, especially beneficial in multi-threaded WASM environments.
  • Shader Precision Upgrade and GLSL Modernization: GLSL shader files and related C++ code (src/libprojectM/MilkdropPreset/Shaders/*.frag, *.vert, CopyTexture.cpp, TransitionShaderManager.cpp, MilkdropSprite.cpp) have been updated to use highp precision for floats and integers, and sampler2D/sampler3D where applicable. This ensures higher rendering quality and better compatibility across various OpenGL ES and WebGL implementations.
  • Modernized Event Callback System: The internal event handling for preset switching and failures in ProjectMCWrapper and PlaylistCWrapper has been refactored to utilize std::function. This modernizes the C++ codebase, offering greater flexibility and type safety for callback mechanisms.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request brings the jules branch up to date with master, introducing a wide range of significant changes. These include upgrading to C++17, overhauling Emscripten build flags for performance with features like OpenMP and SIMD, and adding a new projectM_emscripten.cpp for web-specific logic and bindings. The audio processing has been modernized with an AudioWorkletProcessor. Many parts of the C++ codebase have been optimized with OpenMP pragmas, and OpenGL usage has been tweaked for performance. While many of these changes are beneficial, my review has identified several critical issues, including memory leaks, potential data races from incorrect OpenMP usage, and other bugs in the new Emscripten integration code. There are also some high and medium severity concerns regarding maintainability and correctness that should be addressed.

Comment on lines +324 to +333
char *str = (char*)EM_ASM_PTR({
const randIndex = Math.floor(Math.random()*100);
var jsString = '/presets/preset_'+randIndex+'.milk';
var lengthBytes = lengthBytesUTF8(jsString)+1;
return stringToNewUTF8(jsString);
});
AppData* app_datas = (AppData*)user_data;
projectm_playlist_add_preset(app_datas->playlist,str,false);
uint32_t indx = projectm_playlist_play_next(app_data.playlist,true);
return;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The memory allocated by EM_ASM_PTR for str is not being freed, leading to a memory leak. The stringToNewUTF8 function in Emscripten allocates memory on the heap that the C++ side is responsible for freeing. You should call free(str) after it's no longer needed, for example, before the return statement.

Comment on lines +631 to +643
eglGetConfigAttrib(display,eglconfig,EGL_SAMPLES,&numSamples);
eglGetConfigAttrib(display,eglconfig,EGL_COVERAGE_BUFFERS_NV,&numSamplesNV);
eglGetConfigAttrib(display,eglconfig,EGL_SAMPLE_BUFFERS,&numMBuffers);
eglGetConfigAttrib(display,eglconfig,EGL_RED_SIZE,&numRed);
eglGetConfigAttrib(display,eglconfig,EGL_GREEN_SIZE,&numGreen);
eglGetConfigAttrib(display,eglconfig,EGL_BLUE_SIZE,&numBlue);
eglGetConfigAttrib(display,eglconfig,EGL_ALPHA_SIZE,&numAlpha);
eglGetConfigAttrib(display,eglconfig,EGL_DEPTH_SIZE,&numDepth);
eglGetConfigAttrib(display,eglconfig,EGL_STENCIL_SIZE,&numStencil);
eglGetConfigAttrib(display,eglconfig,EGL_BUFFER_SIZE,&numBuffer);
eglGetConfigAttrib(display,eglconfig,EGL_COVERAGE_BUFFERS_NV,&numBuffersNV);
eglGetConfigAttrib(display,eglconfig,EGL_GL_COLORSPACE,&colorSpace);
eglGetConfigAttrib(display,eglconfig,EGL_COLOR_FORMAT_HI,&colorFormat);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

These eglGetConfigAttrib calls will not work as intended because eglconfig is NULL at this point. eglconfig is only assigned a value later in the eglChooseConfig call on line 700. You should move these attribute queries to after eglChooseConfig if you need these values, or remove them if they are not used.

m_finalComposite.CompileCompositeShader(m_state);
}

#pragma omp parallel

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

Placing #pragma omp parallel on a function definition is a compiler-specific extension (e.g., in GCC) and is not standard OpenMP. It causes the function to be executed by a team of threads when called. This function modifies member state such as m_isFirstFrame and swaps m_currentFrameBuffer and m_previousFrameBuffer, which will lead to data races if executed by multiple threads. This pragma should be removed. If parallelization is intended, it should be applied to specific, thread-safe blocks of code within the function.

}
}

#pragma omp parallel

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

Placing #pragma omp parallel on a function definition is a compiler-specific extension and not standard OpenMP. It causes the function to be executed by a team of threads when called. This function calls InitializeMesh, which modifies member variables like m_gridSizeX and m_gridSizeY, leading to data races if executed by multiple threads. This pragma should be removed. The parallelization inside InitializeMesh using #pragma omp parallel for is the correct approach for that part of the code.


int init() {

EM_ASM({

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This EM_ASM block contains a very large amount of JavaScript code (over 200 lines). This makes the C++ file difficult to read, maintain, and debug. It's highly recommended to move this JavaScript code into a separate .js file and link it with the Emscripten build using the --js-library flag. This will improve code organization and separation of concerns.

return;
});

int init() {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The init() function is very long and handles multiple responsibilities, including JavaScript initialization, EGL setup, WebGL context creation, and projectM engine initialization. For better readability and maintainability, consider refactoring this function into several smaller, more focused functions (e.g., init_javascript(), init_graphics(), init_projectm()).

Comment on lines +114 to +115
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using GL_LINEAR_MIPMAP_LINEAR for GL_TEXTURE_MIN_FILTER requires mipmaps to be generated for the texture. The current code does not generate mipmaps after calling glTexImage2D. This can lead to undefined behavior or incorrect rendering. You should either generate mipmaps by calling glGenerateMipmap(GL_TEXTURE_2D) after glTexImage2D, or change the minification filter to GL_LINEAR if mipmaps are not intended.

Suggested change
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants