Skip to content

Commit d15b0a5

Browse files
committed
[primitives,benchmark] add benchmark tool for primitives
1 parent e2cdcd8 commit d15b0a5

File tree

5 files changed

+246
-22
lines changed

5 files changed

+246
-22
lines changed

cmake/ConfigOptions.cmake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ if(WIN32 AND NOT UWP)
5454
option(WITH_WIN8 "Use Windows 8 libraries" OFF)
5555
endif()
5656

57+
option(BUILD_BENCHMARK "Build benchmark tools (for debugging and development only)" OFF)
5758
option(BUILD_TESTING "Build unit tests (compatible with packaging)" OFF)
5859
cmake_dependent_option(
5960
BUILD_TESTING_INTERNAL "Build unit tests (CI only, not for packaging!)" OFF "NOT BUILD_TESTING" OFF

libfreerdp/primitives/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,10 @@ endif()
9191

9292
freerdp_object_library_add(freerdp-primitives)
9393

94+
if(BUILD_BENCHMARK)
95+
add_subdirectory(benchmark)
96+
endif()
97+
9498
if(BUILD_TESTING_INTERNAL)
9599
add_subdirectory(test)
96100
endif()
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# FreeRDP: A Remote Desktop Protocol Implementation
2+
# FreeRDP cmake build script
3+
#
4+
# Copyright 2025 Armin Novak <[email protected]>
5+
# Copyright 2025 Thincast Technologies GmbH
6+
#
7+
# Licensed under the Apache License, Version 2.0 (the "License");
8+
# you may not use this file except in compliance with the License.
9+
# You may obtain a copy of the License at
10+
#
11+
# http://www.apache.org/licenses/LICENSE-2.0
12+
#
13+
# Unless required by applicable law or agreed to in writing, software
14+
# distributed under the License is distributed on an "AS IS" BASIS,
15+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
# See the License for the specific language governing permissions and
17+
# limitations under the License.
18+
19+
20+
add_executable(primitives-benchmark
21+
benchmark.c
22+
)
23+
target_link_libraries(primitives-benchmark PRIVATE winpr freerdp)
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
/**
2+
* FreeRDP: A Remote Desktop Protocol Implementation
3+
* primitives benchmarking tool
4+
*
5+
* Copyright 2025 Armin Novak <[email protected]>
6+
* Copyright 2025 Thincast Technologies GmbH
7+
*
8+
* Licensed under the Apache License, Version 2.0 (the "License");
9+
* you may not use this file except in compliance with the License.
10+
* You may obtain a copy of the License at
11+
*
12+
* http://www.apache.org/licenses/LICENSE-2.0
13+
*
14+
* Unless required by applicable law or agreed to in writing, software
15+
* distributed under the License is distributed on an "AS IS" BASIS,
16+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
* See the License for the specific language governing permissions and
18+
* limitations under the License.
19+
*/
20+
21+
#include <stdio.h>
22+
23+
#include <winpr/crypto.h>
24+
#include <winpr/sysinfo.h>
25+
#include <freerdp/primitives.h>
26+
27+
typedef struct
28+
{
29+
BYTE* channels[3];
30+
UINT32 steps[3];
31+
prim_size_t roi;
32+
BYTE* outputBuffer;
33+
UINT32 outputStride;
34+
UINT32 testedFormat;
35+
} primitives_YUV_benchmark;
36+
37+
static void primitives_YUV_benchmark_free(primitives_YUV_benchmark* bench)
38+
{
39+
if (!bench)
40+
return;
41+
42+
free(bench->outputBuffer);
43+
44+
for (size_t i = 0; i < 3; i++)
45+
free(bench->channels[i]);
46+
47+
const primitives_YUV_benchmark empty = { 0 };
48+
*bench = empty;
49+
}
50+
51+
static primitives_YUV_benchmark primitives_YUV_benchmark_init(void)
52+
{
53+
primitives_YUV_benchmark ret = { 0 };
54+
ret.roi.width = 3840 * 4;
55+
ret.roi.height = 2160 * 4;
56+
ret.outputStride = ret.roi.width * 4;
57+
ret.testedFormat = PIXEL_FORMAT_BGRA32;
58+
59+
ret.outputBuffer = calloc(ret.outputStride, ret.roi.height);
60+
if (!ret.outputBuffer)
61+
goto fail;
62+
63+
for (size_t i = 0; i < 3; i++)
64+
{
65+
BYTE* buf = ret.channels[i] = calloc(ret.roi.width, ret.roi.height);
66+
if (!buf)
67+
goto fail;
68+
69+
winpr_RAND(buf, 1ull * ret.roi.width * ret.roi.height);
70+
ret.steps[i] = ret.roi.width;
71+
}
72+
73+
return ret;
74+
75+
fail:
76+
primitives_YUV_benchmark_free(&ret);
77+
return ret;
78+
}
79+
80+
static const char* print_time(UINT64 t, char* buffer, size_t size)
81+
{
82+
(void)_snprintf(buffer, size, "%u.%03u.%03u.%03u", (unsigned)(t / 1000000000ull),
83+
(unsigned)((t / 1000000ull) % 1000), (unsigned)((t / 1000ull) % 1000),
84+
(unsigned)((t) % 1000));
85+
return buffer;
86+
}
87+
88+
static BOOL primitives_YUV420_benchmark_run(primitives_YUV_benchmark* bench, primitives_t* prims)
89+
{
90+
const BYTE* channels[3] = { 0 };
91+
92+
for (size_t i = 0; i < 3; i++)
93+
channels[i] = bench->channels[i];
94+
95+
for (size_t x = 0; x < 10; x++)
96+
{
97+
const UINT64 start = winpr_GetTickCount64NS();
98+
pstatus_t status =
99+
prims->YUV420ToRGB_8u_P3AC4R(channels, bench->steps, bench->outputBuffer,
100+
bench->outputStride, bench->testedFormat, &bench->roi);
101+
const UINT64 end = winpr_GetTickCount64NS();
102+
if (status != PRIMITIVES_SUCCESS)
103+
{
104+
(void)fprintf(stderr, "Running YUV420ToRGB_8u_P3AC4R failed\n");
105+
return FALSE;
106+
}
107+
const UINT64 diff = end - start;
108+
char buffer[32] = { 0 };
109+
printf("[%" PRIuz "] YUV420ToRGB_8u_P3AC4R %" PRIu32 "x%" PRIu32 " took %sns\n", x,
110+
bench->roi.width, bench->roi.height, print_time(diff, buffer, sizeof(buffer)));
111+
}
112+
113+
return TRUE;
114+
}
115+
116+
static BOOL primitives_YUV444_benchmark_run(primitives_YUV_benchmark* bench, primitives_t* prims)
117+
{
118+
const BYTE* channels[3] = { 0 };
119+
120+
for (size_t i = 0; i < 3; i++)
121+
channels[i] = bench->channels[i];
122+
123+
for (size_t x = 0; x < 10; x++)
124+
{
125+
const UINT64 start = winpr_GetTickCount64NS();
126+
pstatus_t status =
127+
prims->YUV444ToRGB_8u_P3AC4R(channels, bench->steps, bench->outputBuffer,
128+
bench->outputStride, bench->testedFormat, &bench->roi);
129+
const UINT64 end = winpr_GetTickCount64NS();
130+
if (status != PRIMITIVES_SUCCESS)
131+
{
132+
(void)fprintf(stderr, "Running YUV444ToRGB_8u_P3AC4R failed\n");
133+
return FALSE;
134+
}
135+
const UINT64 diff = end - start;
136+
char buffer[32] = { 0 };
137+
printf("[%" PRIuz "] YUV444ToRGB_8u_P3AC4R %" PRIu32 "x%" PRIu32 " took %sns\n", x,
138+
bench->roi.width, bench->roi.height, print_time(diff, buffer, sizeof(buffer)));
139+
}
140+
141+
return TRUE;
142+
}
143+
144+
int main(int argc, char* argv[])
145+
{
146+
WINPR_UNUSED(argc);
147+
WINPR_UNUSED(argv);
148+
primitives_YUV_benchmark bench = primitives_YUV_benchmark_init();
149+
primitives_t* opt = primitives_get();
150+
primitives_t* generic = primitives_get_generic();
151+
if (!generic || !opt)
152+
{
153+
(void)fprintf(stderr, "failed to get primitives: generic=%p, opt=%p\n", (void*)generic,
154+
(void*)opt);
155+
goto fail;
156+
}
157+
158+
printf("Running YUV420 -> RGB benchmark on generic implementation:\n");
159+
if (!primitives_YUV420_benchmark_run(&bench, generic))
160+
{
161+
(void)fprintf(stderr, "YUV420 -> RGB benchmark failed\n");
162+
goto fail;
163+
}
164+
printf("\n");
165+
166+
printf("Running YUV420 benchmark on optimized implementation:\n");
167+
if (!primitives_YUV420_benchmark_run(&bench, opt))
168+
{
169+
(void)fprintf(stderr, "YUV420 benchmark failed\n");
170+
goto fail;
171+
}
172+
printf("\n");
173+
174+
printf("Running YUV444 -> RGB benchmark on generic implementation:\n");
175+
if (!primitives_YUV444_benchmark_run(&bench, generic))
176+
{
177+
(void)fprintf(stderr, "YUV444 -> RGB benchmark failed\n");
178+
goto fail;
179+
}
180+
printf("\n");
181+
182+
printf("Running YUV444 -> RGB benchmark on optimized implementation:\n");
183+
if (!primitives_YUV444_benchmark_run(&bench, opt))
184+
{
185+
(void)fprintf(stderr, "YUV444 -> RGB benchmark failed\n");
186+
goto fail;
187+
}
188+
printf("\n");
189+
fail:
190+
primitives_YUV_benchmark_free(&bench);
191+
return 0;
192+
}

libfreerdp/primitives/prim_YUV.c

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,11 @@
3333
#include "prim_internal.h"
3434
#include "prim_YUV.h"
3535

36-
static pstatus_t general_LumaToYUV444(const BYTE* WINPR_RESTRICT pSrcRaw[3],
37-
const UINT32 srcStep[3], BYTE* WINPR_RESTRICT pDstRaw[3],
38-
const UINT32 dstStep[3],
39-
const RECTANGLE_16* WINPR_RESTRICT roi)
36+
static inline pstatus_t general_LumaToYUV444(const BYTE* WINPR_RESTRICT pSrcRaw[3],
37+
const UINT32 srcStep[3],
38+
BYTE* WINPR_RESTRICT pDstRaw[3],
39+
const UINT32 dstStep[3],
40+
const RECTANGLE_16* WINPR_RESTRICT roi)
4041
{
4142
const UINT32 nWidth = roi->right - roi->left;
4243
const UINT32 nHeight = roi->bottom - roi->top;
@@ -93,10 +94,11 @@ static pstatus_t general_LumaToYUV444(const BYTE* WINPR_RESTRICT pSrcRaw[3],
9394
return PRIMITIVES_SUCCESS;
9495
}
9596

96-
static pstatus_t general_ChromaV1ToYUV444(const BYTE* WINPR_RESTRICT pSrcRaw[3],
97-
const UINT32 srcStep[3], BYTE* WINPR_RESTRICT pDstRaw[3],
98-
const UINT32 dstStep[3],
99-
const RECTANGLE_16* WINPR_RESTRICT roi)
97+
static inline pstatus_t general_ChromaV1ToYUV444(const BYTE* WINPR_RESTRICT pSrcRaw[3],
98+
const UINT32 srcStep[3],
99+
BYTE* WINPR_RESTRICT pDstRaw[3],
100+
const UINT32 dstStep[3],
101+
const RECTANGLE_16* WINPR_RESTRICT roi)
100102
{
101103
const UINT32 mod = 16;
102104
UINT32 uY = 0;
@@ -167,11 +169,11 @@ static pstatus_t general_ChromaV1ToYUV444(const BYTE* WINPR_RESTRICT pSrcRaw[3],
167169
return PRIMITIVES_SUCCESS;
168170
}
169171

170-
static pstatus_t general_ChromaV2ToYUV444(const BYTE* WINPR_RESTRICT pSrc[3],
171-
const UINT32 srcStep[3], UINT32 nTotalWidth,
172-
UINT32 nTotalHeight, BYTE* WINPR_RESTRICT pDst[3],
173-
const UINT32 dstStep[3],
174-
const RECTANGLE_16* WINPR_RESTRICT roi)
172+
static inline pstatus_t general_ChromaV2ToYUV444(const BYTE* WINPR_RESTRICT pSrc[3],
173+
const UINT32 srcStep[3], UINT32 nTotalWidth,
174+
UINT32 nTotalHeight, BYTE* WINPR_RESTRICT pDst[3],
175+
const UINT32 dstStep[3],
176+
const RECTANGLE_16* WINPR_RESTRICT roi)
175177
{
176178
const UINT32 nWidth = roi->right - roi->left;
177179
const UINT32 nHeight = roi->bottom - roi->top;
@@ -333,10 +335,10 @@ general_YUV444SplitToYUV420(const BYTE* WINPR_RESTRICT pSrc[3], const UINT32 src
333335
return PRIMITIVES_SUCCESS;
334336
}
335337

336-
static pstatus_t general_YUV444ToRGB_DOUBLE_ROW(BYTE* WINPR_RESTRICT pRGB[2], UINT32 DstFormat,
337-
const BYTE* WINPR_RESTRICT pY[2],
338-
const BYTE* WINPR_RESTRICT pU[2],
339-
const BYTE* WINPR_RESTRICT pV[2], size_t nWidth)
338+
static inline pstatus_t
339+
general_YUV444ToRGB_DOUBLE_ROW(BYTE* WINPR_RESTRICT pRGB[2], UINT32 DstFormat,
340+
const BYTE* WINPR_RESTRICT pY[2], const BYTE* WINPR_RESTRICT pU[2],
341+
const BYTE* WINPR_RESTRICT pV[2], size_t nWidth)
340342
{
341343
const DWORD formatSize = FreeRDPGetBytesPerPixel(DstFormat);
342344
fkt_writePixel writePixel = getPixelWriteFunction(DstFormat, FALSE);
@@ -1053,11 +1055,13 @@ static pstatus_t general_RGBToYUV420_8u_P3AC4R(const BYTE* WINPR_RESTRICT pSrc,
10531055
}
10541056
}
10551057

1056-
INLINE void general_RGBToAVC444YUV_BGRX_DOUBLE_ROW(
1057-
size_t offset, const BYTE* WINPR_RESTRICT pSrcEven, const BYTE* WINPR_RESTRICT pSrcOdd,
1058-
BYTE* WINPR_RESTRICT b1Even, BYTE* WINPR_RESTRICT b1Odd, BYTE* WINPR_RESTRICT b2,
1059-
BYTE* WINPR_RESTRICT b3, BYTE* WINPR_RESTRICT b4, BYTE* WINPR_RESTRICT b5,
1060-
BYTE* WINPR_RESTRICT b6, BYTE* WINPR_RESTRICT b7, UINT32 width)
1058+
void general_RGBToAVC444YUV_BGRX_DOUBLE_ROW(size_t offset, const BYTE* WINPR_RESTRICT pSrcEven,
1059+
const BYTE* WINPR_RESTRICT pSrcOdd,
1060+
BYTE* WINPR_RESTRICT b1Even, BYTE* WINPR_RESTRICT b1Odd,
1061+
BYTE* WINPR_RESTRICT b2, BYTE* WINPR_RESTRICT b3,
1062+
BYTE* WINPR_RESTRICT b4, BYTE* WINPR_RESTRICT b5,
1063+
BYTE* WINPR_RESTRICT b6, BYTE* WINPR_RESTRICT b7,
1064+
UINT32 width)
10611065
{
10621066
for (UINT32 x = offset; x < width; x += 2)
10631067
{

0 commit comments

Comments
 (0)