From: José Fonseca Date: Mon, 28 Jan 2013 19:48:41 +0000 (+0000) Subject: Merge branch 'trim-auto' X-Git-Url: https://git.cworth.org/git?p=apitrace-tests;a=commitdiff_plain;h=5efeef24814e1744e3c4f3109f82e700576439ab;hp=0ba9c4e22bc917a666b8762b734884e72c142ccf Merge branch 'trim-auto' --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 47c38cb..2dff857 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,6 +47,19 @@ elseif (PKG_CONFIG_FOUND) pkg_check_modules (GLESV2 glesv2) endif () +# Check for the presence of several python packages, which are needed to build +# generated tests. +execute_process( + COMMAND ${python} -c "import PIL" + OUTPUT_QUIET + ERROR_QUIET + RESULT_VARIABLE IMPORT_PIL_RESULT) +if (IMPORT_PIL_RESULT EQUAL 0) + set (PIL_FOUND 1) +else () + message (STATUS "python PIL module not found") +endif () + if (UNIX) link_libraries(m) endif (UNIX) @@ -90,3 +103,18 @@ enable_testing() add_subdirectory (apps) add_subdirectory (traces) +# FIXME: The tests in the cli directory are intended to be high-level +# tests of the apitrace command-line interface which would ideally be +# portable across all platforms. However, these tests all rely on +# doing image comparisons and the current implementation of the +# "apitrace dump-images" command relies on direct invocation of the +# glretrace command. +# +# Someday, we should have more unified commands for replaying traces, +# dumping images, etc. At that point these cli tests should be usable +# with all targets so that we can drop the "if (OPENGL_FOUND)" +# condition here. +if (OPENGL_FOUND AND PIL_FOUND) + add_subdirectory (cli) + add_subdirectory (trim_stress) +endif () diff --git a/apps/README.markdown b/apps/README.markdown new file mode 100644 index 0000000..3820630 --- /dev/null +++ b/apps/README.markdown @@ -0,0 +1,34 @@ +These directories contain many drawing-api-specific test applications. + +Within each directory's CMakeLists.txt file are you will see +definitions of tests such as the following: + + add_app_test ( + NAME "gl_default_sb" + TARGET gl_tri + ARGS -sb + REF default_sb.ref.txt + ) + +This block specifies a test with NAME "gl_default_sb" that involves +executing the TARGET program "gl_tri" with ARGS of "-sb". The REF +script contains a reference trance that should result from tracing the +given program, along with specifications for additional checks to be +peformed. + +The actual execution of the test (and parsing of the REF script) is +performed by the python program in ../app_driver.py. This driver +program runs the application with the given arguments, performs a +trace of the application, checks the trace against the reference +script, and then run a "retrace" of the application (replaying the +commands in the trace). + +In addition to a dump of the expected trace content, the reference +file can contain directives to cause the driver program to perform +additional checking. Some of the available directives are: + + #image: Dump the current framebuffer image and compare against the + given file. + + #state: Dump the current state in JSON format and compare against + the given file. diff --git a/cli/.gitignore b/cli/.gitignore new file mode 100644 index 0000000..cc6da65 --- /dev/null +++ b/cli/.gitignore @@ -0,0 +1,7 @@ +!*.trace +tri-trim.trace +tri-out +glxsimple-ref +glxsimple-out +glxsimple-trim.trace +glxsimple-trim-unused-textures.trace diff --git a/cli/CMakeLists.txt b/cli/CMakeLists.txt new file mode 100644 index 0000000..9d7ce55 --- /dev/null +++ b/cli/CMakeLists.txt @@ -0,0 +1,29 @@ +function (ADD_CLI_TEST) + cmake_parse_arguments( + TEST + # Options + "" + # One value args + "NAME" + # Multi value args + "" + ${ARGN} + ) + + if (APITRACE_EXECUTABLE) + add_test( + NAME ${TEST_NAME} + COMMAND + python ${CMAKE_SOURCE_DIR}/cli_driver.py + --apitrace ${APITRACE_EXECUTABLE} + --apitrace-source ${APITRACE_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/${TEST_NAME} + ) + endif () +endfunction () + +add_cli_test(NAME "cli-diff-images.script") +add_cli_test(NAME "cli-diff-images-mismatch.script") +add_cli_test(NAME "cli-trim-single-swapbuffers.script") +add_cli_test(NAME "cli-trim-unused-textures.script") +add_cli_test(NAME "cli-trim-unused-shaders.script") diff --git a/cli/README.markdown b/cli/README.markdown new file mode 100644 index 0000000..4f49822 --- /dev/null +++ b/cli/README.markdown @@ -0,0 +1,39 @@ +This directory tests high-level functionality of apitrace command-line +interface (cli). See also the neighboring "traces" directory which +also tests the cli, but in ways that can only be verified by doing +comparisons of resulting dump files. + +For writing new tests, if the functionality can be exercised by a +sequence of apitrace command invocations, and then a final comparison +of "apitrace dump" output, then it's likely simplest to write a new +test in the traces directory. Otherwise, a new test program can be +written in this directory. + +The tests in this directory are found in files with names matching +*.script by convention. The scripts must be listed explicitly in the +CMakeLists.txt file. Each script consists of simple line-based +commands with the following meanings (based on the first word of each +line): + + apitrace: Execute the current apitrace executable being tested + with the given arguments. If apitrace returns a + non-zero status, the test will fail. + + expect: Compare the results of the previously-executed command + with the given (json-quoted) string. If the strings + are not identical, the test will fail. + + rm_and_mkdir: Remove any existing directory of the given name and + then create it. The directory name is always + interpreted locally. If this fails for any reason + other than "file does not exist" the test will fail. + +Note: Blank lines and lines beginning with '#' are ignored. + +Commands can be prefixed with "EXPECT_FAILURE:" to indicate that a +command is expected to return a non-zero value. In this case, a return +value of zero from the command will cause the test to fail. + +If none of the commands in the script cause the test to fail (as +described above), then the test will pass. + diff --git a/cli/cli-diff-images-mismatch.script b/cli/cli-diff-images-mismatch.script new file mode 100644 index 0000000..15446ad --- /dev/null +++ b/cli/cli-diff-images-mismatch.script @@ -0,0 +1,7 @@ +EXPECT_FAILURE: apitrace diff-images --verbose images/white-1x1/ images/black-1x1/ + +# In addition to getting the return value indicating an error, let's +# also require that "apitrace diff-images" gave us the output we +# expect. + +expect "Comparing images/white-1x1/0.png and images/black-1x1/0.png ... MISMATCH\n" diff --git a/cli/cli-diff-images.script b/cli/cli-diff-images.script new file mode 100644 index 0000000..27327d4 --- /dev/null +++ b/cli/cli-diff-images.script @@ -0,0 +1,8 @@ +apitrace diff-images --verbose images/white-1x1/ images/white-1x1/ + +# Ensure that the "apitrace diff-images" actually did something. This +# is important since if it couldn't find images in one directory or +# the other then it would just silently return 0 and this test would +# incorrectly pass. + +expect "Comparing images/white-1x1/0.png and images/white-1x1/0.png ... MATCH\n" diff --git a/cli/cli-trim-single-swapbuffers.script b/cli/cli-trim-single-swapbuffers.script new file mode 100644 index 0000000..e653d49 --- /dev/null +++ b/cli/cli-trim-single-swapbuffers.script @@ -0,0 +1,15 @@ +# First, trim the trace to the final glxSwapBuffers call + +apitrace trim --auto --calls=27 tri.trace + +# Then dump the image and compare to our reference + +rm_and_mkdir ./tri-out +apitrace dump-images --call-nos=no -o ./tri-out/tri tri-trim.trace +apitrace diff-images -v ./tri-ref ./tri-out + +# In addition to getting the return value indicating no error, let's +# also require that "apitrace diff-images" gave us the output we +# expect. + +expect "Comparing ./tri-ref/tri0000000000.png and ./tri-out/tri0000000000.png ... MATCH\n" diff --git a/cli/cli-trim-unused-shaders.script b/cli/cli-trim-unused-shaders.script new file mode 100644 index 0000000..8d461b0 --- /dev/null +++ b/cli/cli-trim-unused-shaders.script @@ -0,0 +1,120 @@ +# Generate reference images from unmodified trace. + +# We carefully generate images only for desired frames (dropping the +# first frame that draws using shaders). + +rm_and_mkdir glxsimple-ref +apitrace dump-images --calls=10,47,50,71,87,88 --call-nos=no -o ./glxsimple-ref/ glxsimple.trace + +# Trim to the same callset used to generate reference images + +apitrace trim --auto --calls=10,47,50,71,87,88 glxsimple.trace + +# Verify that we actually trimmed what we wanted to + +apitrace diff --diff=python glxsimple.trace glxsimple-trim.trace +expect r""" glXChooseVisual(37134976, 0, (GLX_RGBA, GLX_RED_SIZE, GLX_RED_SIZE, GLX_GREEN_SIZE, GLX_RED_SIZE, GLX_BLUE_SIZE, GLX_RED_SIZE, GLX_ALPHA_SIZE, GLX_RED_SIZE, GLX_DOUBLEBUFFER, GLX_DEPTH_SIZE, 24, GLX_STENCIL_SIZE, GLX_RED_SIZE, GLX_X_VISUAL_TYPE, GLX_DIRECT_COLOR, 0)) = ([37179128, 34, 0, 24, 5, 16711680, 65280, 255, 256, 8]) + glXCreateContext(37134976, ([37179128, 34, 0, 24, 5, 16711680, 65280, 255, 256, 8]), 0, True) = 37241648 + glXMakeCurrent(37134976, 41943041, 37241648) = True + glViewport(0, 0, 64, 64) + glMatrixMode(GL_PROJECTION) + glLoadIdentity() + glOrtho(0.0, 64.0, 64.0, 0.0, 0.0, 1.0) + glMatrixMode(GL_MODELVIEW) + glClearColor(0.0, 0.0, 1.0, 1.0) + glClear((GL_COLOR_BUFFER_BIT)) + glXSwapBuffers(37134976, 41943041) +- glCreateShader(GL_VERTEX_SHADER) = 7 +- glShaderSource(7, 1, ('void main()\n{\n gl_Position = ftransform();\n}\n'), 0) +- glCompileShader(7) +- glCreateShader(GL_FRAGMENT_SHADER) = 8 +- glShaderSource(8, 1, ('#version 120\nuniform vec4 color;\nvoid main()\n{\n gl_FragColor = color;\n}\n'), 0) +- glCompileShader(8) +- glCreateProgram() = 9 +- glAttachShader(9, 7) +- glAttachShader(9, 8) +- glLinkProgram(9) +- glUseProgram(9) +- glGetUniformLocation(9, color) = 0 +- glUniform4f(0, 0.0, 1.0, 0.0, 1.0) +- glBegin(GL_QUADS) +- glVertex2f(0.0, 0.0) +- glVertex2f(64.0, 0.0) +- glVertex2f(64.0, 64.0) +- glVertex2f(0.0, 64.0) +- glEnd() + glUseProgram(0) +- glXSwapBuffers(37134976, 41943041) + glGenTextures(1, (1)) + glBindTexture(GL_TEXTURE_2D, 1) + glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, blob(3)) + glEnable(GL_TEXTURE_2D) + glBegin(GL_QUADS) + glTexCoord2f(0.0, 0.0) + glVertex2f(0.0, 0.0) + glTexCoord2f(1.0, 0.0) + glVertex2f(64.0, 0.0) + glTexCoord2f(1.0, 1.0) + glVertex2f(64.0, 64.0) + glTexCoord2f(0.0, 1.0) + glVertex2f(0.0, 64.0) + glEnd() + glDisable(GL_TEXTURE_2D) + glXSwapBuffers(37134976, 41943041) + glClearColor(1.0, 0.0, 0.0, 1.0) + glClear((GL_COLOR_BUFFER_BIT)) + glXSwapBuffers(37134976, 41943041) + glCreateShader(GL_VERTEX_SHADER) = 10 + glShaderSource(10, 1, ('void main()\n{\n gl_Position = ftransform();\n}\n'), 0) + glCompileShader(10) + glCreateShader(GL_FRAGMENT_SHADER) = 11 + glShaderSource(11, 1, ('#version 120\nuniform vec4 color;\nvoid main()\n{\n gl_FragColor = color;\n}\n'), 0) + glCompileShader(11) + glCreateProgram() = 12 + glAttachShader(12, 10) + glAttachShader(12, 11) + glLinkProgram(12) + glUseProgram(12) + glGetUniformLocation(12, color) = 0 + glUniform4f(0, 1.0, 0.0, 1.0, 1.0) + glBegin(GL_QUADS) + glVertex2f(0.0, 0.0) + glVertex2f(64.0, 0.0) + glVertex2f(64.0, 64.0) + glVertex2f(0.0, 64.0) + glEnd() + glUseProgram(0) + glXSwapBuffers(37134976, 41943041) + glGenTextures(1, (2)) + glBindTexture(GL_TEXTURE_2D, 2) + glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, blob(3)) + glEnable(GL_TEXTURE_2D) + glBegin(GL_QUADS) + glTexCoord2f(0.0, 0.0) + glVertex2f(0.0, 0.0) + glTexCoord2f(1.0, 0.0) + glVertex2f(64.0, 0.0) + glTexCoord2f(1.0, 1.0) + glVertex2f(64.0, 64.0) + glTexCoord2f(0.0, 1.0) + glVertex2f(0.0, 64.0) + glEnd() + glDisable(GL_TEXTURE_2D) + glXSwapBuffers(37134976, 41943041) + glXDestroyContext(37134976, 37241648) +""" + +# Generate output images for all frames in the trimmed trace + +rm_and_mkdir glxsimple-out +apitrace dump-images --call-nos=no -o ./glxsimple-out/ glxsimple-trim.trace + +# Compare output to reference images + +apitrace diff-images -v ./glxsimple-ref ./glxsimple-out +expect r"""Comparing ./glxsimple-ref/0000000000.png and ./glxsimple-out/0000000000.png ... MATCH +Comparing ./glxsimple-ref/0000000001.png and ./glxsimple-out/0000000001.png ... MATCH +Comparing ./glxsimple-ref/0000000002.png and ./glxsimple-out/0000000002.png ... MATCH +Comparing ./glxsimple-ref/0000000003.png and ./glxsimple-out/0000000003.png ... MATCH +Comparing ./glxsimple-ref/0000000004.png and ./glxsimple-out/0000000004.png ... MATCH +""" diff --git a/cli/cli-trim-unused-textures.script b/cli/cli-trim-unused-textures.script new file mode 100644 index 0000000..96ab853 --- /dev/null +++ b/cli/cli-trim-unused-textures.script @@ -0,0 +1,120 @@ +# Generate reference images from unmodified trace. + +# We carefully generate images only for desired frames (dropping the +# first frame that draws using textures). + +rm_and_mkdir glxsimple-ref +apitrace dump-images --calls=10,31,50,71,87,88 --call-nos=no -o ./glxsimple-ref/ glxsimple.trace + +# Trim to the same callset used to generate reference images + +apitrace trim --auto -o glxsimple-trim-unused-textures.trace --calls=10,31,50,71,87,88 glxsimple.trace + +# Verify that we actually trimmed what we wanted to + +apitrace diff --diff=python glxsimple.trace glxsimple-trim-unused-textures.trace +expect r""" glXChooseVisual(37134976, 0, (GLX_RGBA, GLX_RED_SIZE, GLX_RED_SIZE, GLX_GREEN_SIZE, GLX_RED_SIZE, GLX_BLUE_SIZE, GLX_RED_SIZE, GLX_ALPHA_SIZE, GLX_RED_SIZE, GLX_DOUBLEBUFFER, GLX_DEPTH_SIZE, 24, GLX_STENCIL_SIZE, GLX_RED_SIZE, GLX_X_VISUAL_TYPE, GLX_DIRECT_COLOR, 0)) = ([37179128, 34, 0, 24, 5, 16711680, 65280, 255, 256, 8]) + glXCreateContext(37134976, ([37179128, 34, 0, 24, 5, 16711680, 65280, 255, 256, 8]), 0, True) = 37241648 + glXMakeCurrent(37134976, 41943041, 37241648) = True + glViewport(0, 0, 64, 64) + glMatrixMode(GL_PROJECTION) + glLoadIdentity() + glOrtho(0.0, 64.0, 64.0, 0.0, 0.0, 1.0) + glMatrixMode(GL_MODELVIEW) + glClearColor(0.0, 0.0, 1.0, 1.0) + glClear((GL_COLOR_BUFFER_BIT)) + glXSwapBuffers(37134976, 41943041) + glCreateShader(GL_VERTEX_SHADER) = 7 + glShaderSource(7, 1, ('void main()\n{\n gl_Position = ftransform();\n}\n'), 0) + glCompileShader(7) + glCreateShader(GL_FRAGMENT_SHADER) = 8 + glShaderSource(8, 1, ('#version 120\nuniform vec4 color;\nvoid main()\n{\n gl_FragColor = color;\n}\n'), 0) + glCompileShader(8) + glCreateProgram() = 9 + glAttachShader(9, 7) + glAttachShader(9, 8) + glLinkProgram(9) + glUseProgram(9) + glGetUniformLocation(9, color) = 0 + glUniform4f(0, 0.0, 1.0, 0.0, 1.0) + glBegin(GL_QUADS) + glVertex2f(0.0, 0.0) + glVertex2f(64.0, 0.0) + glVertex2f(64.0, 64.0) + glVertex2f(0.0, 64.0) + glEnd() + glUseProgram(0) + glXSwapBuffers(37134976, 41943041) +- glGenTextures(1, (1)) + glBindTexture(GL_TEXTURE_2D, 1) +- glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, blob(3)) + glEnable(GL_TEXTURE_2D) +- glBegin(GL_QUADS) +- glTexCoord2f(0.0, 0.0) +- glVertex2f(0.0, 0.0) +- glTexCoord2f(1.0, 0.0) +- glVertex2f(64.0, 0.0) +- glTexCoord2f(1.0, 1.0) +- glVertex2f(64.0, 64.0) +- glTexCoord2f(0.0, 1.0) +- glVertex2f(0.0, 64.0) +- glEnd() + glDisable(GL_TEXTURE_2D) +- glXSwapBuffers(37134976, 41943041) + glClearColor(1.0, 0.0, 0.0, 1.0) + glClear((GL_COLOR_BUFFER_BIT)) + glXSwapBuffers(37134976, 41943041) + glCreateShader(GL_VERTEX_SHADER) = 10 + glShaderSource(10, 1, ('void main()\n{\n gl_Position = ftransform();\n}\n'), 0) + glCompileShader(10) + glCreateShader(GL_FRAGMENT_SHADER) = 11 + glShaderSource(11, 1, ('#version 120\nuniform vec4 color;\nvoid main()\n{\n gl_FragColor = color;\n}\n'), 0) + glCompileShader(11) + glCreateProgram() = 12 + glAttachShader(12, 10) + glAttachShader(12, 11) + glLinkProgram(12) + glUseProgram(12) + glGetUniformLocation(12, color) = 0 + glUniform4f(0, 1.0, 0.0, 1.0, 1.0) + glBegin(GL_QUADS) + glVertex2f(0.0, 0.0) + glVertex2f(64.0, 0.0) + glVertex2f(64.0, 64.0) + glVertex2f(0.0, 64.0) + glEnd() + glUseProgram(0) + glXSwapBuffers(37134976, 41943041) + glGenTextures(1, (2)) + glBindTexture(GL_TEXTURE_2D, 2) + glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, blob(3)) + glEnable(GL_TEXTURE_2D) + glBegin(GL_QUADS) + glTexCoord2f(0.0, 0.0) + glVertex2f(0.0, 0.0) + glTexCoord2f(1.0, 0.0) + glVertex2f(64.0, 0.0) + glTexCoord2f(1.0, 1.0) + glVertex2f(64.0, 64.0) + glTexCoord2f(0.0, 1.0) + glVertex2f(0.0, 64.0) + glEnd() + glDisable(GL_TEXTURE_2D) + glXSwapBuffers(37134976, 41943041) + glXDestroyContext(37134976, 37241648) +""" + +# Generate output images for all frames in the trimmed trace + +rm_and_mkdir glxsimple-out +apitrace dump-images --call-nos=no -o ./glxsimple-out/ glxsimple-trim-unused-textures.trace + +# Compare output to reference images + +apitrace diff-images -v ./glxsimple-ref ./glxsimple-out +expect r"""Comparing ./glxsimple-ref/0000000000.png and ./glxsimple-out/0000000000.png ... MATCH +Comparing ./glxsimple-ref/0000000001.png and ./glxsimple-out/0000000001.png ... MATCH +Comparing ./glxsimple-ref/0000000002.png and ./glxsimple-out/0000000002.png ... MATCH +Comparing ./glxsimple-ref/0000000003.png and ./glxsimple-out/0000000003.png ... MATCH +Comparing ./glxsimple-ref/0000000004.png and ./glxsimple-out/0000000004.png ... MATCH +""" diff --git a/cli/glxsimple.trace b/cli/glxsimple.trace new file mode 100644 index 0000000..9c22592 Binary files /dev/null and b/cli/glxsimple.trace differ diff --git a/cli/images/black-1x1/0.png b/cli/images/black-1x1/0.png new file mode 100644 index 0000000..12b0fd3 Binary files /dev/null and b/cli/images/black-1x1/0.png differ diff --git a/cli/images/white-1x1/0.png b/cli/images/white-1x1/0.png new file mode 100644 index 0000000..9c2dfc7 Binary files /dev/null and b/cli/images/white-1x1/0.png differ diff --git a/cli/src/.gitignore b/cli/src/.gitignore new file mode 100644 index 0000000..db85a30 --- /dev/null +++ b/cli/src/.gitignore @@ -0,0 +1,2 @@ +*~ +glxsimple diff --git a/cli/src/Makefile b/cli/src/Makefile new file mode 100644 index 0000000..87102fc --- /dev/null +++ b/cli/src/Makefile @@ -0,0 +1,9 @@ +PROGS=glxsimple + +all: $(PROGS) + +%:%.c + $(CC) -Wall -Wextra -g -o $@ $^ -lGL -lX11 -lGLEW + +clean: + rm -f *.o $(PROGS) diff --git a/cli/src/glxsimple.c b/cli/src/glxsimple.c new file mode 100644 index 0000000..a4c9560 --- /dev/null +++ b/cli/src/glxsimple.c @@ -0,0 +1,285 @@ +/************************************************************************** + * Copyright 2012 Intel corporation + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +int width = 64; +int height = 64; + +static void +set_2d_projection (void) +{ + glMatrixMode (GL_PROJECTION); + glLoadIdentity (); + glOrtho (0, width, height, 0, 0, 1); + glMatrixMode (GL_MODELVIEW); +} + +static void +draw_fullscreen_quad (void) +{ + glBegin (GL_QUADS); + glVertex2f (0, 0); + glVertex2f (width, 0); + glVertex2f (width, height); + glVertex2f (0, height); + glEnd (); +} + +static void +draw_fullscreen_textured_quad (void) +{ + glBegin (GL_QUADS); + glTexCoord2f(0, 0); glVertex2f (0, 0); + glTexCoord2f(1, 0); glVertex2f (width, 0); + glTexCoord2f(1, 1); glVertex2f (width, height); + glTexCoord2f(0, 1); glVertex2f (0, height); + glEnd (); +} + +static void +paint_rgb_using_clear (double r, double g, double b) +{ + glClearColor(r, g, b, 1.0); + glClear(GL_COLOR_BUFFER_BIT); +} + +static void +paint_rgb_using_glsl (double r, double g, double b) +{ + const char * vs_source = + "void main()\n" + "{\n" + " gl_Position = ftransform();\n" + "}\n"; + const char * fs_source = + "#version 120\n" + "uniform vec4 color;\n" + "void main()\n" + "{\n" + " gl_FragColor = color;\n" + "}\n"; + + GLuint vs, fs, program; + GLint color; + + vs = glCreateShader (GL_VERTEX_SHADER); + glShaderSource (vs, 1, &vs_source, NULL); + glCompileShader (vs); + + fs = glCreateShader (GL_FRAGMENT_SHADER); + glShaderSource (fs, 1, &fs_source, NULL); + glCompileShader (fs); + + program = glCreateProgram (); + glAttachShader (program, vs); + glAttachShader (program, fs); + + glLinkProgram (program); + glUseProgram (program); + + color = glGetUniformLocation (program, "color"); + + glUniform4f (color, r, g, b, 1.0); + + draw_fullscreen_quad (); + + glUseProgram (0); +} + +static GLuint +create_rgb_texture (double r, double g, double b) +{ + uint8_t data[3]; + GLuint texture = 0; + + data[0] = (uint8_t) (255.0 * r); + data[1] = (uint8_t) (255.0 * g); + data[2] = (uint8_t) (255.0 * b); + + glGenTextures (1, &texture); + + glBindTexture (GL_TEXTURE_2D, texture); + + glTexImage2D (GL_TEXTURE_2D, + 0, GL_COMPRESSED_RGBA, + 1, 1, 0, + GL_RGB, GL_UNSIGNED_BYTE, data); + + return texture; +} + +static void +paint_using_texture (GLuint texture) +{ + glBindTexture (GL_TEXTURE_2D, texture); + + glEnable (GL_TEXTURE_2D); + + draw_fullscreen_textured_quad (); + + glDisable (GL_TEXTURE_2D); +} + +static void +draw (Display *dpy, Window window, int width, int height) +{ +#define PASSES 2 + int i; + GLenum glew_err; + GLuint texture[PASSES]; + + int visual_attr[] = { + GLX_RGBA, + GLX_RED_SIZE, 8, + GLX_GREEN_SIZE, 8, + GLX_BLUE_SIZE, 8, + GLX_ALPHA_SIZE, 8, + GLX_DOUBLEBUFFER, + GLX_DEPTH_SIZE, 24, + GLX_STENCIL_SIZE, 8, + GLX_X_VISUAL_TYPE, GLX_DIRECT_COLOR, + None + }; + + /* Window and context setup. */ + XVisualInfo *visual_info = glXChooseVisual(dpy, 0, visual_attr); + GLXContext ctx = glXCreateContext(dpy, visual_info, NULL, True); + glXMakeCurrent(dpy, window, ctx); + + glew_err = glewInit(); + if (glew_err != GLEW_OK) + { + fprintf (stderr, "glewInit failed: %s\n", + glewGetErrorString(glew_err)); + exit (1); + } + + glViewport(0, 0, width, height); + + set_2d_projection (); + +/* Simply count through some colors, frame by frame. */ +#define RGB(frame) (((frame+1)/4) % 2), (((frame+1)/2) % 2), ((frame+1) % 2) + + int frame = 0; + for (i = 0; i < PASSES; i++) { + + /* Frame: Draw a solid frame using glClear. */ + paint_rgb_using_clear (RGB(frame)); + glXSwapBuffers (dpy, window); + frame++; + + /* Frame: Draw a solid frame using GLSL. */ + paint_rgb_using_glsl (RGB(frame)); + glXSwapBuffers (dpy, window); + frame++; + + /* Frame: Draw a solid frame using a texture. */ + texture[i] = create_rgb_texture (RGB(frame)); + paint_using_texture (texture[i]); + glXSwapBuffers (dpy, window); + frame++; + } + + /* Draw another frame with a re-used texture. */ + paint_using_texture (texture[0]); + glXSwapBuffers (dpy, window); + frame++; + + /* Cleanup */ + glXDestroyContext (dpy, ctx); +} + +static void +handle_events(Display *dpy, Window window, int width, int height) +{ + XEvent xev; + KeyCode quit_code = XKeysymToKeycode (dpy, XStringToKeysym("Q")); + + XNextEvent (dpy, &xev); + + while (1) { + XNextEvent (dpy, &xev); + switch (xev.type) { + case KeyPress: + if (xev.xkey.keycode == quit_code) { + return; + } + break; + case ConfigureNotify: + width = xev.xconfigure.width; + height = xev.xconfigure.height; + break; + case Expose: + if (xev.xexpose.count == 0) { + draw (dpy, window, width, height); + return; + } + break; + } + } +} + +int +main (void) +{ + Display *dpy; + Window window; + + dpy = XOpenDisplay (NULL); + + if (dpy == NULL) { + fprintf(stderr, "Failed to open display %s\n", + XDisplayName(NULL)); + return 1; + } + + window = XCreateSimpleWindow(dpy, DefaultRootWindow (dpy), + 0, 0, width, height, 0, + BlackPixel (dpy, DefaultScreen (dpy)), + BlackPixel (dpy, DefaultScreen (dpy))); + + XSelectInput(dpy, window, + KeyPressMask | StructureNotifyMask | ExposureMask); + + XMapWindow (dpy, window); + + handle_events (dpy, window, width, height); + + XDestroyWindow (dpy, window); + XCloseDisplay (dpy); + + return 0; +} diff --git a/cli/tri-ref-mismatch/tri0000000000.png b/cli/tri-ref-mismatch/tri0000000000.png new file mode 100644 index 0000000..e682610 Binary files /dev/null and b/cli/tri-ref-mismatch/tri0000000000.png differ diff --git a/cli/tri-ref/tri0000000000.png b/cli/tri-ref/tri0000000000.png new file mode 100644 index 0000000..b7840bd Binary files /dev/null and b/cli/tri-ref/tri0000000000.png differ diff --git a/cli/tri.trace b/cli/tri.trace new file mode 100644 index 0000000..f900d52 Binary files /dev/null and b/cli/tri.trace differ diff --git a/cli_driver.py b/cli_driver.py new file mode 100644 index 0000000..6c1bfd0 --- /dev/null +++ b/cli_driver.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python + +# Copyright 2012 Intel Corporation +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +'''Test driver for scripts in the cli directory.''' + +import json +import errno +import shutil +import subprocess +import difflib + +from base_driver import * + +class CliDriver(Driver): + + def do_apitrace(self, args): + cmd = [self.options.apitrace] + args.split() + + print " ".join(cmd) + proc = subprocess.Popen(cmd, stdout = subprocess.PIPE) + self.output = proc.communicate()[0] + + proc.wait() + + if (self.expect_failure): + if (proc.returncode == 0): + fail("Command unexpectedly passed when expecting failure:\n " + " ".join(cmd)) + else: + if (proc.returncode != 0): + fail("Command failed (returned non-zero):\n " + " ".join(cmd)) + + def do_expect(self, args): + expected = eval(args) + if (self.output != expected): + differ = difflib.Differ() + diff = differ.compare(expected.splitlines(1), self.output.splitlines(1)) + diff = ''.join(diff) + fail("Unexpected output:\n%s\n" % diff) + + def do_rm_and_mkdir(self, args): + + args = args.split() + + # Operate only on local directories + dir = './' + args[0] + + # Failing to delete a directory that doesn't exist is no failure + def rmtree_onerror(function, path, excinfo): + if excinfo[0] == OSError and excinfo[1].errno != errno.ENOENT: + raise + + shutil.rmtree(dir, onerror = rmtree_onerror) + os.makedirs(dir) + + def unknown_command(self, args): + fail('Broken test script: Unknown command: %s' % (args)) + + def run_script(self, cli_script): + "Execute the commands in the given cli script." + + commands = { + 'apitrace': self.do_apitrace, + 'expect': self.do_expect, + 'rm_and_mkdir': self.do_rm_and_mkdir + } + + script = open(cli_script, 'rt') + + while True: + + self.expect_failure = False + + line = script.readline() + + # Exit loop on EOF + if (line == ''): + break + + line = line.rstrip() + + if " " in line: + (cmd, args) = line.split(None,1) + if args.startswith('r"""'): + while not line.endswith('"""'): + line = script.readline() + line = line.rstrip() + args += '\n' + line + else: + cmd = line + args = '' + + # Ignore blank lines and comments + if (len(cmd) == 0 or cmd == '\n' or cmd[0] == '#'): + continue + + if (cmd == 'EXPECT_FAILURE:'): + self.expect_failure = True + if " " in args: + (cmd, args) = args.split(None, 1) + else: + cmd = args + args = '' + + commands.get(cmd, self.unknown_command)(args) + + def run(self): + self.parseOptions() + + self.run_script(self.args[0]) + + pass_() + +if __name__ == '__main__': + CliDriver().run() diff --git a/traces/.gitignore b/traces/.gitignore index a0f8a41..4f72b30 100644 --- a/traces/.gitignore +++ b/traces/.gitignore @@ -1,2 +1,3 @@ !*.trace *.src.trace +*-trim.trace diff --git a/traces/README.markdown b/traces/README.markdown index 2fd26af..805ab98 100644 --- a/traces/README.markdown +++ b/traces/README.markdown @@ -1,3 +1,21 @@ +This directory tests various operations of the apitrace command-line +interface (cli) operating on existings. + +All files with a .script extension specify a simple script for +testing. The test driver (in ../tool_driver.py) will parse the script +file line-by-line, interpreting each line as arguments to the apitrace +cli interface and invoking the cli with the given arguments. This +continues until the first line of the script beginning with the "dump" +command. + +After a "dump" command in the script, the remainder of the script file +provides the expected output of the given dump command. The tool +driver will report a test failure if the actual dump output differs +from that given in the. + +Here are descriptions of some of the trace files contained here which +are used by the test scripts: + * zlib-no-eof.trace: is a short, zlib compressed trace, with an unexpected end of file because the application terminated abnormally (which is actual very normal). @@ -6,3 +24,6 @@ any data from it. * incomplete-call.trace: trace with an incomplete call, with missing arguments + +* glxsimple.trace: trace from a simple program showing drawing with + glClear, with GLSL shader, and with texture. See ../cli/src \ No newline at end of file diff --git a/traces/glxsimple.trace b/traces/glxsimple.trace new file mode 100644 index 0000000..8726260 Binary files /dev/null and b/traces/glxsimple.trace differ diff --git a/traces/trim-frame-set.script b/traces/trim-frame-set.script new file mode 100644 index 0000000..33293c3 --- /dev/null +++ b/traces/trim-frame-set.script @@ -0,0 +1,20 @@ +trim --frames=0-5/draw,0-5/frame glxsimple.trace +dump --verbose glxsimple-trim.trace +0 glClear(mask = GL_COLOR_BUFFER_BIT) +1 glXSwapBuffers(dpy = 0x236a280, drawable = 41943041) + +2 glEnd() +3 glXSwapBuffers(dpy = 0x236a280, drawable = 41943041) + +4 glEnd() +5 glXSwapBuffers(dpy = 0x236a280, drawable = 41943041) + +6 glClear(mask = GL_COLOR_BUFFER_BIT) +7 glXSwapBuffers(dpy = 0x236a280, drawable = 41943041) + +8 glEnd() +9 glXSwapBuffers(dpy = 0x236a280, drawable = 41943041) + +10 glEnd() +11 glXSwapBuffers(dpy = 0x236a280, drawable = 41943041) + diff --git a/traces/trim-head-by-frame.script b/traces/trim-head-by-frame.script new file mode 100644 index 0000000..64b7eb3 --- /dev/null +++ b/traces/trim-head-by-frame.script @@ -0,0 +1,14 @@ +trim -o trim-head-by-frame.src.trace --auto --frames=0 glxsimple.trace +dump --verbose trim-head-by-frame.src.trace +0 glXChooseVisual(dpy = 0x236a280, screen = 0, attribList = {GLX_RGBA, GLX_RED_SIZE, GLX_RED_SIZE, GLX_GREEN_SIZE, GLX_RED_SIZE, GLX_BLUE_SIZE, GLX_RED_SIZE, GLX_ALPHA_SIZE, GLX_RED_SIZE, GLX_DOUBLEBUFFER, GLX_DEPTH_SIZE, 24, GLX_STENCIL_SIZE, GLX_RED_SIZE, GLX_X_VISUAL_TYPE, GLX_DIRECT_COLOR, 0}) = &{visual = 0x2374ef8, visualid = 34, screen = 0, depth = 24, c_class = 5, red_mask = 16711680, green_mask = 65280, blue_mask = 255, colormap_size = 256, bits_per_rgb = 8} +1 glXCreateContext(dpy = 0x236a280, vis = &{visual = 0x2374ef8, visualid = 34, screen = 0, depth = 24, c_class = 5, red_mask = 16711680, green_mask = 65280, blue_mask = 255, colormap_size = 256, bits_per_rgb = 8}, shareList = NULL, direct = True) = 0x2384330 +2 glXMakeCurrent(dpy = 0x236a280, drawable = 41943041, ctx = 0x2384330) = True +3 glViewport(x = 0, y = 0, width = 64, height = 64) +4 glMatrixMode(mode = GL_PROJECTION) +5 glLoadIdentity() +6 glOrtho(left = 0, right = 64, bottom = 64, top = 0, zNear = 0, zFar = 1) +7 glMatrixMode(mode = GL_MODELVIEW) +8 glClearColor(red = 0, green = 0, blue = 1, alpha = 1) +9 glClear(mask = GL_COLOR_BUFFER_BIT) +10 glXSwapBuffers(dpy = 0x236a280, drawable = 41943041) + diff --git a/traces/trim-head.script b/traces/trim-head.script new file mode 100644 index 0000000..897e4e9 --- /dev/null +++ b/traces/trim-head.script @@ -0,0 +1,14 @@ +trim -o trim-head.src.trace --auto --calls=0-843 glxsimple.trace +dump --verbose trim-head.src.trace +0 glXChooseVisual(dpy = 0x236a280, screen = 0, attribList = {GLX_RGBA, GLX_RED_SIZE, GLX_RED_SIZE, GLX_GREEN_SIZE, GLX_RED_SIZE, GLX_BLUE_SIZE, GLX_RED_SIZE, GLX_ALPHA_SIZE, GLX_RED_SIZE, GLX_DOUBLEBUFFER, GLX_DEPTH_SIZE, 24, GLX_STENCIL_SIZE, GLX_RED_SIZE, GLX_X_VISUAL_TYPE, GLX_DIRECT_COLOR, 0}) = &{visual = 0x2374ef8, visualid = 34, screen = 0, depth = 24, c_class = 5, red_mask = 16711680, green_mask = 65280, blue_mask = 255, colormap_size = 256, bits_per_rgb = 8} +1 glXCreateContext(dpy = 0x236a280, vis = &{visual = 0x2374ef8, visualid = 34, screen = 0, depth = 24, c_class = 5, red_mask = 16711680, green_mask = 65280, blue_mask = 255, colormap_size = 256, bits_per_rgb = 8}, shareList = NULL, direct = True) = 0x2384330 +2 glXMakeCurrent(dpy = 0x236a280, drawable = 41943041, ctx = 0x2384330) = True +3 glViewport(x = 0, y = 0, width = 64, height = 64) +4 glMatrixMode(mode = GL_PROJECTION) +5 glLoadIdentity() +6 glOrtho(left = 0, right = 64, bottom = 64, top = 0, zNear = 0, zFar = 1) +7 glMatrixMode(mode = GL_MODELVIEW) +8 glClearColor(red = 0, green = 0, blue = 1, alpha = 1) +9 glClear(mask = GL_COLOR_BUFFER_BIT) +10 glXSwapBuffers(dpy = 0x236a280, drawable = 41943041) + diff --git a/trim_stress/.gitignore b/trim_stress/.gitignore new file mode 100644 index 0000000..2c3a0d6 --- /dev/null +++ b/trim_stress/.gitignore @@ -0,0 +1,5 @@ +!*.trace +*-ref/ +*-out +*-index.html +*.trace.trim diff --git a/trim_stress/CMakeLists.txt b/trim_stress/CMakeLists.txt new file mode 100644 index 0000000..652590d --- /dev/null +++ b/trim_stress/CMakeLists.txt @@ -0,0 +1,16 @@ +file (GLOB traces RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.trace) + +list (SORT traces) + +foreach (trace ${traces}) + if (APITRACE_EXECUTABLE AND APITRACE_SOURCE_DIR) + add_test( + NAME trim-stress-${trace} + COMMAND + python ${CMAKE_SOURCE_DIR}/trim_stress_driver.py + --apitrace ${APITRACE_EXECUTABLE} + --apitrace-source ${APITRACE_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/${trace} + ) + endif () +endforeach (trace) diff --git a/trim_stress/README.markdown b/trim_stress/README.markdown new file mode 100644 index 0000000..adab8dc --- /dev/null +++ b/trim_stress/README.markdown @@ -0,0 +1,19 @@ +This directory performs stress testing of "apitrace trim". + +For each trace file in this directory the trim_stress.py test driver +will perform the following operations: + +Given .trace: + + 1. Generate snapshots of original trace in ./-ref/ + + 2. For each frame of the trace: + + 1. Use "apitrace trim" to trim .trace to a + single-frame trace in -trim.trace + + 2. Generate a snapshot of the trimmed trace to + ./-out/.png + + 3. Use "apitrace diff-images" to compare all snapshots in + ./-out with those in ./-ref diff --git a/trim_stress/glxsimple.trace b/trim_stress/glxsimple.trace new file mode 100644 index 0000000..162de4d Binary files /dev/null and b/trim_stress/glxsimple.trace differ diff --git a/trim_stress_driver.py b/trim_stress_driver.py new file mode 100644 index 0000000..64863f8 --- /dev/null +++ b/trim_stress_driver.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python + +# Copyright 2012 Intel Corporation +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +'''Stress test driver for apitrace trim.''' + +import os, errno, shutil, subprocess + +from base_driver import * + +def rm_and_mkdir(dir): + + # Operate only on local directories + dir = './' + dir + + # Failing to delete a directory that doesn't exist is no failure + def rmtree_onerror(function, path, excinfo): + if excinfo[0] == OSError and excinfo[1].errno != errno.ENOENT: + raise + + shutil.rmtree(dir, onerror = rmtree_onerror) + os.makedirs(dir) + +class TrimStressDriver(Driver): + + def trim_stress(self, trace_file): + "Execute an apitrace-trim stress test for the given trace." + + (dir, file_name) = os.path.split(trace_file) + (name, extension) = file_name.rsplit('.', 1) + + ref_dir = name + '-ref/' + out_dir = name + '-out/' + trim_file = name + '.trace.trim' + + rm_and_mkdir(ref_dir) + rm_and_mkdir(out_dir) + + subprocess.check_call([self.options.apitrace, "dump-images", "--call-nos=no", "--output=" + ref_dir, trace_file]); + + # Count the number of frame snapshots generated + frames = 0 + for dirpath, dirs, files in os.walk(ref_dir): + # Don't descend into any sub-directories (not that there + # should be any) + del dirs[:] + frames = len(files) + + for frame in range(frames): + try: + subprocess.check_call([self.options.apitrace, "trim", "--auto", "--frame=%d" % (frame), "--output=" + trim_file, trace_file]) + except: + print "An error occurred while trimming frame %d from %s" % (frame, trace_file) + fail() + try: + subprocess.check_call([self.options.apitrace, "dump-images", "--call-nos=no", "--output=" + out_dir + "frame", trim_file]) + except: + print "An error occurred replaying %s to generate a frame snapshot" % (trim_file) + fail() + os.rename("%s/frame0000000000.png" % (out_dir), "%s/%010d.png" % (out_dir, frame)) + + try: + subprocess.check_call([self.options.apitrace, "diff-images", "-v", ref_dir, out_dir]) + except: + print "Trimmed frames did not match reference images. See " + name + "-index.html" + fail() + finally: + os.rename("index.html", name + "-index.html") + + def run(self): + self.parseOptions() + + self.trim_stress(self.args[0]) + + pass_() + +if __name__ == '__main__': + TrimStressDriver().run()