Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ endif
ifdef ${ccache}
$(info 💿 Using CCACHE - compile.core.mk )
CXX := ${ccache} $(CXX)
CC := ${ccache} $(CXX)
CC := ${ccache} $(CC)
endif

$(info 💾 CXX = $(CXX))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,7 @@ endif
ifdef ${ccache}
$(info 💿 Using CCACHE -- config.project.mk )
CXX := ${ccache} $(CXX)
CC := ${ccache} $(CXX)
CC := ${ccache} $(CC)
endif

ifdef PROJECT_RESOURCE_COMPILER
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@

#MINGW_PREFIX ?= /mingw32
MINGW_PREFIX ?= /mingw64
PLATFORM_CFLAGS += -std=gnu++17 -DUNICODE -D_UNICODE
PLATFORM_CXXFLAGS += -std=gnu++17
PLATFORM_CFLAGS += -DUNICODE -D_UNICODE
#PLATFORM_CFLAGS += -IC:/msys64/mingw32/include/gstreamer-1.0 -DOF_VIDEO_PLAYER_GSTREAMER

CC = $(MINGW_PREFIX)/bin/gcc
Expand Down
1 change: 1 addition & 0 deletions scripts/ci/linux64/install.sh

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Virtual Frame buffer - allows for headless linux to run GPU tests

Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ echo "checking assimp versions"
apt-cache policy libassimp-dev

sudo $OF_ROOT/scripts/linux/ubuntu/install_dependencies.sh -y;
sudo apt-get install -y xvfb || true

#commenting out below so we can get it working with default in Ubuntu 18.04
#sudo add-apt-repository -y ppa:dns/gnu
Expand Down
12 changes: 10 additions & 2 deletions scripts/ci/linux64/run_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ ROOT=${TRAVIS_BUILD_DIR:-"$( cd "$(dirname "$0")/../../.." ; pwd -P )"}
echo "##[group]**** Running unit tests ****"
cd $ROOT/tests
for group in *; do
# android / ios / emscripten have their own CI jobs; native linux Makefile can't run them
case "$group" in
android|ios|tvOS|emscripten) continue ;;
esac
if [ -d $group ]; then
echo "##[group] $group"
for test in $group/*; do
Expand All @@ -23,8 +27,12 @@ for group in *; do
binname=$(basename ${test})

if [[ -f ./${binname}_debug ]]; then
gdb -batch -ex "run" -ex "bt" -ex "q \$_exitcode" ./${binname}_debug
#./${binname}_debug
# GitHub linux runners have no DISPLAY; xvfb gives GLFW a software GL context
if [ -z "${DISPLAY:-}" ] && command -v xvfb-run >/dev/null 2>&1; then
xvfb-run -a gdb -batch -ex "run" -ex "bt" -ex "q \$_exitcode" ./${binname}_debug
else
gdb -batch -ex "run" -ex "bt" -ex "q \$_exitcode" ./${binname}_debug
fi
else
echo "Binary not found: ${binname}_debug"
exit 1
Expand Down
3 changes: 3 additions & 0 deletions scripts/ci/macos/run_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ make -j Debug
echo "**** Running unit tests ****"
cd $ROOT/tests
for group in *; do
case "$group" in
android|ios|tvOS|emscripten) continue ;;
esac
if [ -d $group ]; then
for test in $group/*; do
if [ -d $test ]; then
Expand Down
3 changes: 3 additions & 0 deletions scripts/ci/msys2/run_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ ROOT=$(pwd -P)
echo "##[group]**** Running unit tests ****"
cd $ROOT/tests
for group in *; do
case "$group" in
android|ios|tvOS|emscripten) continue ;;
esac
if [ -d $group ]; then
echo "##[group] $group"
for test in $group/*; do
Expand Down
5 changes: 4 additions & 1 deletion scripts/ci/osx/run_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ echo "##[endgroup]"
echo "##[group]**** Running unit tests ****"
cd $ROOT/tests
for group in *; do
case "$group" in
android|ios|tvOS|emscripten) continue ;;
esac
if [ -d $group ]; then
echo "##[group] $group"
for test in $group/*; do
Expand All @@ -42,4 +45,4 @@ for group in *; do
echo "##[endgroup]"
fi
done
echo "##[endgroup]"
echo "##[endgroup]"
Empty file.
1 change: 1 addition & 0 deletions tests/emscripten/glRenderSmokeTest/src/GLSmokeTestCore.h
1 change: 1 addition & 0 deletions tests/emscripten/glRenderSmokeTest/src/main.cpp
1 change: 1 addition & 0 deletions tests/emscripten/glRenderSmokeTest/src/ofApp.cpp
1 change: 1 addition & 0 deletions tests/emscripten/glRenderSmokeTest/src/ofApp.h
27 changes: 27 additions & 0 deletions tests/gl/glRenderSmokeTest/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# GL render smoke test

Exercises the code paths touched by the OpenGL ES 3.0/3.1 support work:
an indexed `ofVboMesh` drawn both directly (`drawElements`) and instanced
(`drawElementsInstanced`), a tessellated `ofPath` (tess2 / `ofIndexType`),
and an `ofFbo` round trip with a pixel readback to catch garbage/blank
triangles caused by an index-type/size mismatch.

The actual test logic lives in `src/GLSmokeTestCore.h` and is shared,
unmodified, by every platform target (desktop here, `tests/emscripten/glRenderSmokeTest`,
`tests/ios/glRenderSmokeTest`) — only the platform bridge (`ofApp`/`main`)
differs.

On desktop this is a normal windowed openFrameworks app picked up
automatically by the existing `tests/` CI harness (`scripts/ci/*/run_tests.sh`):
it runs a fixed number of frames, logs `GL_SMOKE_TEST RESULT=PASS` or
`RESULT=FAIL reason=...`, and exits with a matching process exit code.

To run manually:

```sh
cd tests/gl/glRenderSmokeTest
cp ../../../scripts/templates/osx/Makefile . # or linux/linux64 template
cp ../../../scripts/templates/osx/config.make .
make Debug
make RunDebug
```
Empty file.
173 changes: 173 additions & 0 deletions tests/gl/glRenderSmokeTest/src/GLSmokeTestCore.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
#pragma once

#include "ofMain.h"

// Shared by every platform target of glRenderSmokeTest (desktop, Emscripten,
// iOS, Android). Exercises the code paths touched by the OpenGL ES 3.0/3.1
// support work: indexed ofVboMesh draw + drawInstanced (drawElements /
// drawElementsInstanced), a tessellated ofPath (tess2 / ofIndexType), and an
// ofFbo round trip, on whatever GL/GLES context the host window created.
//
// No custom shader is used deliberately: the GLSL version/#define story
// differs per platform (desktop core profile vs GLES2 vs GLES3 "300 es" vs
// Emscripten) and isn't what this PR touches. Using the renderer's own
// default shader keeps the test focused on the index-type and tess2 paths
// while still exercising the ES-version #ifdef branches in
// ofGLProgrammableRenderer indirectly (every draw call goes through them).
class GLSmokeTestCore {
public:
static constexpr int framesToRun = 12;

void allocate() {
release();

indexedMesh.setMode(OF_PRIMITIVE_TRIANGLES);
indexedMesh.addVertices({
{-60.f, -60.f, 0.f}, {60.f, -60.f, 0.f},
{60.f, 60.f, 0.f}, {-60.f, 60.f, 0.f}
});
indexedMesh.addColors({ofColor::red, ofColor::green, ofColor::blue, ofColor::yellow});
indexedMesh.addIndices({0, 1, 2, 0, 2, 3});

tessellatedPath.clear();
tessellatedPath.setFilled(true);
tessellatedPath.setFillColor(ofColor(255, 140, 20));
tessellatedPath.setPolyWindingMode(OF_POLY_WINDING_ODD);
for (int i = 0; i < 12; ++i) {
const float radius = (i % 2 == 0) ? 55.f : 22.f;
const float angle = ofDegToRad(i * 30.f - 90.f);
const glm::vec2 point(std::cos(angle) * radius, std::sin(angle) * radius);
if (i == 0) {
tessellatedPath.moveTo(point);
} else {
tessellatedPath.lineTo(point);
}
}
tessellatedPath.close();

ofFbo::Settings settings;
settings.width = 128;
settings.height = 128;
settings.internalformat = GL_RGBA;
settings.useDepth = false;
settings.numSamples = 0;
fbo.allocate(settings);

allocated = fbo.isAllocated();
if (!allocated) {
fail("ofFbo failed to allocate");
}
}

void release() {
fbo.clear();
allocated = false;
}

void drawFrame() {
if (!allocated) {
fail("resources not allocated");
return;
}

clearGLErrors();

fbo.begin();
ofClear(0, 0, 0, 255);
ofSetColor(255);
ofPushMatrix();
ofTranslate(fbo.getWidth() * 0.5f, fbo.getHeight() * 0.5f);
indexedMesh.draw(); // ofGLProgrammableRenderer::draw(ofVboMesh) -> drawElements
indexedMesh.drawInstanced(OF_MESH_FILL, 2); // forces the drawElementsInstanced path (primCount > 1)
ofPopMatrix();
fbo.end();
checkGLErrors("after mesh draw");

ofSetColor(255);
fbo.draw(0.f, 0.f, static_cast<float>(fbo.getWidth()), static_cast<float>(fbo.getHeight()));

ofPushMatrix();
ofTranslate(180.f, 160.f);
tessellatedPath.draw(); // tess2 triangulation -> ofIndexType-sized indices
ofPopMatrix();
checkGLErrors("after path draw");

if (frame == framesToRun - 1) {
verifyPixels();
}
++frame;
}

bool finished() const {
return frame >= framesToRun;
}

bool passed() const {
return allocated && glErrorCount == 0 && pixelCheckPassed && failureReason.empty();
}

std::string resultLine() const {
if (passed()) {
return "GL_SMOKE_TEST RESULT=PASS frames=" + ofToString(frame);
}
std::string reason = failureReason;
if (reason.empty() && glErrorCount > 0) {
reason = "glGetError seen " + ofToString(glErrorCount) + " time(s), last=" + ofToHex(lastGLError);
}
if (reason.empty() && !pixelCheckPassed) {
reason = "FBO readback did not contain the expected non-background color";
}
return "GL_SMOKE_TEST RESULT=FAIL reason=" + reason;
}

private:
void fail(const std::string & reason) {
if (failureReason.empty()) {
failureReason = reason;
}
ofLogError("glRenderSmokeTest") << reason;
}

void clearGLErrors() {
while (glGetError() != GL_NO_ERROR) {
}
}

void checkGLErrors(const std::string & where) {
GLenum error = glGetError();
while (error != GL_NO_ERROR) {
++glErrorCount;
lastGLError = error;
ofLogError("glRenderSmokeTest") << "glGetError=" << ofToHex(error) << " " << where;
error = glGetError();
}
}

void verifyPixels() {
ofPixels pixels;
fbo.readToPixels(pixels);
if (!pixels.isAllocated()) {
fail("FBO readToPixels produced no pixels");
return;
}
const ofColor center = pixels.getColor(pixels.getWidth() / 2, pixels.getHeight() / 2);
// Background is cleared to black; any of the mesh's red/green/blue/yellow
// vertex colors interpolating in means the indexed draw actually reached
// the GPU with correctly-sized indices instead of reading garbage/nothing.
pixelCheckPassed = (center.r > 10 || center.g > 10 || center.b > 10);
if (!pixelCheckPassed) {
ofLogError("glRenderSmokeTest") << "center pixel=" << center << " (expected non-background)";
}
}

ofVboMesh indexedMesh;
ofPath tessellatedPath;
ofFbo fbo;

bool allocated = false;
bool pixelCheckPassed = false;
int frame = 0;
int glErrorCount = 0;
GLenum lastGLError = GL_NO_ERROR;
std::string failureReason;
};
23 changes: 23 additions & 0 deletions tests/gl/glRenderSmokeTest/src/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#include "ofMain.h"
#include "ofApp.h"
#if defined(TARGET_GLFW_WINDOW)
#include "ofAppGLFWWindow.h"
#endif

int main() {
ofGLWindowSettings settings;
settings.setSize(320, 240);
settings.windowMode = OF_WINDOW;

auto window = ofCreateWindow(settings);
#if defined(TARGET_GLFW_WINDOW)
auto glfw = dynamic_cast<ofAppGLFWWindow *>(window.get());
if (!glfw || !glfw->getGLFWWindow()) {
ofLogWarning("glRenderSmokeTest") << "no GL window (headless / GLFW init failed) — skip";
return 0;
}
#endif

ofRunApp(window, std::make_shared<ofApp>());
return ofRunMainLoop();
}
42 changes: 42 additions & 0 deletions tests/gl/glRenderSmokeTest/src/ofApp.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#include "ofApp.h"

#include <cstdlib>
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#endif

namespace {
// std::exit() alone doesn't stop Emscripten's requestAnimationFrame loop
// (the runtime stays alive for pending async work), so draw() keeps getting
// called forever after "finishing" unless we guard against it below, and the
// process itself needs emscripten_force_exit() to actually stop.
void terminateProcess(int code) {
#ifdef __EMSCRIPTEN__
emscripten_force_exit(code);
#else
std::exit(code);
#endif
}
}

void ofApp::setup() {
ofSetLogLevel(OF_LOG_NOTICE);
ofSetFrameRate(60);
ofBackground(0);
ofLogNotice("glRenderSmokeTest") << "renderer=" << glGetString(GL_RENDERER)
<< " version=" << glGetString(GL_VERSION);
core.allocate();
}

void ofApp::draw() {
if (core.finished()) {
return;
}

core.drawFrame();

if (core.finished()) {
ofLogNotice("glRenderSmokeTest") << core.resultLine();
terminateProcess(core.passed() ? 0 : 1);
}
}
13 changes: 13 additions & 0 deletions tests/gl/glRenderSmokeTest/src/ofApp.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#pragma once

#include "ofMain.h"
#include "GLSmokeTestCore.h"

class ofApp : public ofBaseApp {
public:
void setup() override;
void draw() override;

private:
GLSmokeTestCore core;
};
Empty file.
Empty file.
Loading
Loading