From fc8179d319046f45346bcbcc5aaeabebdf151f03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Ol=C5=A1=C3=A1k?= <marek.olsak@amd.com> Date: Sun, 5 Jan 2025 00:09:45 -0500 Subject: [PATCH] perf/pixel-rate: new pixel throughput microbenchmark This is an exhaustive microbenchmark to evaluate how many pixels hw can process under different circumstances with as little shader code as possible. Part-of: <https://gitlab.freedesktop.org/mesa/piglit/-/merge_requests/979> --- tests/perf/CMakeLists.gl.txt | 1 + tests/perf/pixel-rate.c | 279 +++++++++++++++++++++++++++++++++++ 2 files changed, 280 insertions(+) create mode 100644 tests/perf/pixel-rate.c diff --git a/tests/perf/CMakeLists.gl.txt b/tests/perf/CMakeLists.gl.txt index ad98e6295a..6481230f77 100644 --- a/tests/perf/CMakeLists.gl.txt +++ b/tests/perf/CMakeLists.gl.txt @@ -16,6 +16,7 @@ piglit_add_executable (fbobind fbobind.c common.c) piglit_add_executable (fill fill.c common.c) piglit_add_executable (genmipmap genmipmap.c common.c) piglit_add_executable (pbobench pbobench.c common.c) +piglit_add_executable (pixel-rate pixel-rate.c common.c) piglit_add_executable (readpixels readpixels.c common.c) piglit_add_executable (shader-io-rate shader-io-rate.c common.c) piglit_add_executable (teximage teximage.c common.c) diff --git a/tests/perf/pixel-rate.c b/tests/perf/pixel-rate.c new file mode 100644 index 0000000000..a44e101578 --- /dev/null +++ b/tests/perf/pixel-rate.c @@ -0,0 +1,279 @@ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + * 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 + * VMWARE 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. + */ + +/** + * Measure pixel throughput with different numbers of fragment shader inputs, + * qualifiers, and system values. + */ + +#include "common.h" +#include <stdbool.h> +#undef NDEBUG +#include <assert.h> +#include "piglit-util-gl.h" + +/* this must be a power of two to prevent precision issues */ +#define WINDOW_SIZE 1024 + +PIGLIT_GL_TEST_CONFIG_BEGIN + + config.supports_gl_compat_version = 10; + config.window_width = WINDOW_SIZE; + config.window_height = WINDOW_SIZE; + config.window_visual = PIGLIT_GL_VISUAL_RGBA | PIGLIT_GL_VISUAL_DOUBLE; + +PIGLIT_GL_TEST_CONFIG_END + +static unsigned gpu_freq_mhz; + +typedef struct { + const char *name; + bool sample_shading; + const char *vs; + const char *fs; + GLuint prog; +} prog_info; + +#define VS_SET_POSITION " gl_Position = vec4(gl_VertexID % 2 == 1 ? 1.0 : -1.0, gl_VertexID / 2 == 1 ? 1.0 : -1.0, 0.0, 1.0);\n" + +#define VS_POS \ + "#version 400\n" \ + "void main() {\n" \ + VS_SET_POSITION \ + "}\n" + +#define FS_OUT(v) \ + "#version 400\n" \ + "void main() {\n" \ + " gl_FragColor = vec4(" v ");\n" \ + "}" + +#define INPUTS_PROG(n, sample_shading, qual, qual_code, linear, linear_code, sysval, sysval_code) \ + {#n " inputs " qual linear sysval, \ + sample_shading, \ + \ + "#version 400\n" \ + "noperspective out vec4 varnp;\n" \ + qual_code " out vec4 var["#n"];\n" \ + "void main() {\n" \ + " float id = float(gl_VertexID);\n" \ + " varnp = vec4(id, id*id, id*id*id, id*id*id*id);\n" \ + " var[0].x = id;\n" \ + " for (int i = 1; i < ("#n" - (" linear_code " ? 1 : 0)) * 4; i++) var[i / 4][i % 4] = var[(i - 1) / 4][(i - 1) % 4] * id;\n" \ + VS_SET_POSITION \ + "}\n", \ + \ + "#version 400\n" \ + "noperspective in vec4 varnp;\n" \ + qual_code " in vec4 var["#n"];\n" /* the last one is unused, it will be eliminated by the GLSL compiler */ \ + "void main() {\n" \ + " vec4 v = " linear_code " ? varnp : vec4(1.0);\n" \ + " for (int i = 0; i < ("#n" - (" linear_code " ? 1 : 0)); i++) v *= var[i];\n" \ + " gl_FragColor = v + " sysval_code ";\n" \ + "}\n"} + +#define INPUTS_Q(n, sample_shading, qual, qual_code, linear, linear_code) \ + INPUTS_PROG(n, sample_shading, qual, qual_code, linear, linear_code,, "vec4(0.0)"), \ + INPUTS_PROG(n, sample_shading, qual, qual_code, linear, linear_code, "+Face", "vec4(gl_FrontFacing)"), \ + INPUTS_PROG(n, sample_shading, qual, qual_code, linear, linear_code, "+SampleMask", "vec4(gl_SampleMaskIn[0])"), \ + INPUTS_PROG(n, sample_shading, qual, qual_code, linear, linear_code, "+FragPos.x", "gl_FragCoord.xxxx"), \ + INPUTS_PROG(n, sample_shading, qual, qual_code, linear, linear_code, "+FragPos.y", "gl_FragCoord.yyyy"), \ + INPUTS_PROG(n, sample_shading, qual, qual_code, linear, linear_code, "+FragPos.z", "gl_FragCoord.zzzz"), \ + INPUTS_PROG(n, sample_shading, qual, qual_code, linear, linear_code, "+FragPos.w", "gl_FragCoord.wwww"), \ + INPUTS_PROG(n, sample_shading, qual, qual_code, linear, linear_code, "+FragPos.xy", "gl_FragCoord.xyyy"), \ + INPUTS_PROG(n, sample_shading, qual, qual_code, linear, linear_code, "+FragPos.zw", "gl_FragCoord.zwww"), \ + INPUTS_PROG(n, sample_shading, qual, qual_code, linear, linear_code, "+FragPos.xyz", "gl_FragCoord.xyzz"), \ + INPUTS_PROG(n, sample_shading, qual, qual_code, linear, linear_code, "+FragPos.xyw", "gl_FragCoord.xyww"), \ + INPUTS_PROG(n, sample_shading, qual, qual_code, linear, linear_code, "+FragPos.xyzw", "gl_FragCoord"), \ + INPUTS_PROG(n, sample_shading, qual, qual_code, linear, linear_code, "+FragPos.xy+Face", "gl_FragCoord.xyyy + vec4(gl_FrontFacing)"), \ + INPUTS_PROG(n, sample_shading, qual, qual_code, linear, linear_code, "+FragPos.xy+SampleMask", "gl_FragCoord.xyyy + vec4(gl_SampleMaskIn[0])"), \ + INPUTS_PROG(n, sample_shading, qual, qual_code, linear, linear_code, "+FragPos.xy+Face+SampleMask", "gl_FragCoord.xyyy + vec4(gl_FrontFacing) + vec4(gl_SampleMaskIn[0])"), \ + INPUTS_PROG(n, sample_shading, qual, qual_code, linear, linear_code, "+FragPos.xyzw+Face+SampleMask", "gl_FragCoord + vec4(gl_FrontFacing) + vec4(gl_SampleMaskIn[0])"), \ + INPUTS_PROG(n, true, qual, qual_code, linear, linear_code, "+SampleID", "vec4(gl_SampleID)"), \ + INPUTS_PROG(n, true, qual, qual_code, linear, linear_code, "+SampleID+FragPos.xy", "vec4(gl_SampleID) + gl_FragCoord.xyyy"), \ + INPUTS_PROG(n, true, qual, qual_code, linear, linear_code, "+SampleID+SampleMask", "vec4(gl_SampleID) + vec4(gl_SampleMaskIn[0])"), \ + INPUTS_PROG(n, true, qual, qual_code, linear, linear_code, "+SampleID+FragPos.xyzw+Face+SampleMask", "vec4(gl_SampleID) + gl_FragCoord + vec4(gl_FrontFacing) + vec4(gl_SampleMaskIn[0])") + +#define INPUT1(n) \ + INPUTS_Q(n, false, "flat", "flat", "", "false"), \ + INPUTS_Q(n, false, "persp",, "", "false"), \ + INPUTS_Q(n, true, "sample", "sample", "", "false") + +#define INPUTS(n) \ + INPUTS_Q(n, false, "persp",, "", "false"), \ + INPUTS_Q(n, false, "persp",, "+linear", "true"), \ + INPUTS_Q(n, true, "sample", "sample", "", "false"), \ + INPUTS_Q(n, true, "sample", "sample", "+linear", "true") + +static prog_info progs[] = { + {"Empty", false, VS_POS, + "#version 400\n" + "void main() {\n" + "}"}, + + {"Empty+discard", false, VS_POS, + "#version 400\n" + "void main() {\n" + " discard;\n" + "}"}, + + {"Const fill", false, VS_POS, FS_OUT("0.5, 0.4, 0.3, 0.2")}, + + {"Face", false, VS_POS, FS_OUT("gl_FrontFacing")}, + {"SampleMask", false, VS_POS, FS_OUT("gl_SampleMaskIn[0]")}, + {"FragPos.x", false, VS_POS, FS_OUT("gl_FragCoord.x")}, + {"FragPos.y", false, VS_POS, FS_OUT("gl_FragCoord.y")}, + {"FragPos.z", false, VS_POS, FS_OUT("gl_FragCoord.z")}, + {"FragPos.w", false, VS_POS, FS_OUT("gl_FragCoord.w")}, + {"FragPos.xy", false, VS_POS, FS_OUT("gl_FragCoord.xyyy")}, + {"FragPos.zw", false, VS_POS, FS_OUT("gl_FragCoord.zwww")}, + {"FragPos.xyz", false, VS_POS, FS_OUT("gl_FragCoord.xyzz")}, + {"FragPos.xyw", false, VS_POS, FS_OUT("gl_FragCoord.xyww")}, + {"FragPos.xyzw", false, VS_POS, FS_OUT("gl_FragCoord")}, + {"FragPos.xy+Face", false, VS_POS, FS_OUT("gl_FragCoord.xyyy + vec4(gl_FrontFacing)")}, + {"FragPos.xy+SampleMask", false, VS_POS, FS_OUT("gl_FragCoord.xyyy + vec4(gl_SampleMaskIn[0])")}, + {"FragPos.xy+Face+SampleMask", false, VS_POS, FS_OUT("gl_FragCoord.xyyy + vec4(gl_FrontFacing) + vec4(gl_SampleMaskIn[0])")}, + {"FragPos.xyzw+Face+SampleMask", false, VS_POS, FS_OUT("gl_FragCoord + vec4(gl_FrontFacing) + vec4(gl_SampleMaskIn[0])")}, + {"SampleID", true, VS_POS, FS_OUT("vec4(gl_SampleID)")}, + {"SamplePos", true, VS_POS, FS_OUT("gl_SamplePosition.xyyy")}, + {"SampleID+SamplePos", true, VS_POS, FS_OUT("vec4(gl_SampleID) + gl_SamplePosition.xyyy")}, + {"SampleID+SampleMask", true, VS_POS, FS_OUT("vec4(gl_SampleID) + vec4(gl_SampleMaskIn[0])")}, + {"SampleID+SamplePos+SampleMask", true, VS_POS, FS_OUT("vec4(gl_SampleID) + gl_SamplePosition.xyyy + vec4(gl_SampleMaskIn[0])")}, + {"SampleID+FragPos.xy", true, VS_POS, FS_OUT("vec4(gl_SampleID) + gl_FragCoord.xyyy")}, + {"SampleID+FragPos.z", true, VS_POS, FS_OUT("vec4(gl_SampleID) + gl_FragCoord.zzzz")}, + {"SampleID+FragPos.xyzw", true, VS_POS, FS_OUT("vec4(gl_SampleID) + gl_FragCoord")}, + {"SampleID+SamplePos+SampleMask+FragPos.xyzw+Face", true, VS_POS, FS_OUT("vec4(gl_SampleID) + gl_SamplePosition.xyyy + vec4(gl_SampleMaskIn[0]) + gl_FragCoord.xyzw + vec4(gl_FrontFacing)")}, + + INPUT1(1), + INPUTS(2), + INPUTS(3), + INPUTS(4), + INPUTS(5), + INPUTS(6), + INPUTS(7), + INPUTS(8), +}; + +typedef struct { + const char *name; + GLenum format; + unsigned num_samples; + GLuint fbo; +} fb_info; + +static fb_info fbs[] = { + {"RGBA8 1s", GL_RGBA8, 0}, + {"RGBA8 4s", GL_RGBA8, 4}, + {"RGBA16F 1s", GL_RGBA16F, 0}, + {"RGBA16F 4s", GL_RGBA16F, 4}, +}; + +void +piglit_init(int argc, char **argv) +{ + for (unsigned i = 1; i < argc; i++) { + if (strncmp(argv[i], "-freq=", 6) == 0) + sscanf(argv[i] + 6, "%u", &gpu_freq_mhz); + } + + piglit_require_gl_version(40); + + for (unsigned i = 0; i < ARRAY_SIZE(progs); i++) + progs[i].prog = piglit_build_simple_program(progs[i].vs, progs[i].fs); + + for (unsigned i = 0; i < ARRAY_SIZE(fbs); i++) { + GLuint rb; + glGenRenderbuffers(1, &rb); + glBindRenderbuffer(GL_RENDERBUFFER, rb); + glRenderbufferStorageMultisample(GL_RENDERBUFFER, fbs[i].num_samples, + fbs[i].format, 1024, 1024); + + glGenFramebuffers(1, &fbs[i].fbo); + glBindFramebuffer(GL_FRAMEBUFFER, fbs[i].fbo); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_RENDERBUFFER, rb); + bool status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + assert(status); + } +} + +static void +run_draw(unsigned iterations) +{ + for (unsigned i = 0; i < iterations; i++) + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); +} + +enum piglit_result +piglit_display(void) +{ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + if (getenv("TEST")) { + glUseProgram(progs[1].prog); + run_draw(1); + piglit_swap_buffers(); + return PIGLIT_PASS; + } + + printf(" %-60s, %s\n", "Fragment shader", gpu_freq_mhz ? "Samples/clock," : "GSamples/second"); + + printf("%*s", 62, ""); + for (unsigned j = 0; j < ARRAY_SIZE(fbs); j++) { + printf(",%10s", fbs[j].name); + } + puts(""); + + /* Warm up. */ + glUseProgram(progs[0].prog); + perf_measure_gpu_rate(run_draw, 0.01); + + for (unsigned i = 0; i < ARRAY_SIZE(progs); i++) { + printf(" %-60s", progs[i].name); + glUseProgram(progs[i].prog); + + for (unsigned j = 0; j < ARRAY_SIZE(fbs); j++) { + if (progs[i].sample_shading && fbs[j].num_samples <= 1) { + printf(", n/a"); + fflush(stdout); + continue; + } + glBindFramebuffer(GL_FRAMEBUFFER, fbs[j].fbo); + + double rate = perf_measure_gpu_rate(run_draw, 0.01); + rate *= (double)piglit_width * piglit_height * + (progs[i].sample_shading ? fbs[j].num_samples : 1); + + if (gpu_freq_mhz) { + rate /= gpu_freq_mhz * 1000000.0; + printf(",%10.2f", rate); + } else { + printf(",%10.2f", rate / 1000000000); + } + fflush(stdout); + } + puts(""); + } + + exit(0); + return PIGLIT_SKIP; +} -- GitLab