1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
|
/**
* SDL_BlitSurface bit-perfect rendering test suite written by Isaac Aronson
*/
/* Suppress C4996 VS compiler warnings for unlink() */
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_DEPRECATE)
#define _CRT_SECURE_NO_DEPRECATE
#endif
#if defined(_MSC_VER) && !defined(_CRT_NONSTDC_NO_DEPRECATE)
#define _CRT_NONSTDC_NO_DEPRECATE
#endif
#include <stdio.h>
#ifndef _MSC_VER
#include <unistd.h>
#else
/* Suppress uint64 to uint32 conversion warning within the PRNG engine */
#pragma warning( disable : 4244 )
#endif
#include <sys/stat.h>
#include <SDL3/SDL.h>
#include <SDL3/SDL_test.h>
#include "testautomation_images.h"
/* ====== xoroshiro128+ PRNG engine for deterministic blit input ===== */
Uint64 rotl(Uint64 x, int k) { return (x << k) | (x >> (-k & 63)); }
Uint64 next(Uint64 state[2]) {
Uint64 s0 = state[0], s1 = state[1];
Uint64 result = rotl((s0 + s1) * 9, 29) + s0;
state[0] = s0 ^ rotl(s1, 29);
state[1] = s0 ^ s1 << 9;
return result;
}
static Uint64 rngState[2] = {1, 2};
Uint32 getRandomUint32(void) {
return (Uint32)next(rngState);
}
/* ================= Test Case Helper Functions ================== */
/*
* Resets PRNG state to initialize tests using PRNG
*/
void SDLCALL blitSetUp(void **arg) {
rngState[0] = 1;
rngState[1] = 2;
}
/*
* Fill buffer with stream of PRNG pixel data given size
*/
static Uint32 *fillNextRandomBuffer(Uint32 *buf, const int width, const int height) {
int i;
for (i = 0; i < width * height; i++) {
buf[i] = getRandomUint32();
}
return buf;
}
/*
* Generates a stream of PRNG pixel data given length
*/
static Uint32 *getNextRandomBuffer(const int width, const int height) {
Uint32* buf = SDL_malloc(sizeof(Uint32) * width * height);
fillNextRandomBuffer(buf, width, height);
return buf;
}
/*
* Generates a 800 x 600 surface of PRNG pixel data
*/
SDL_Surface* getRandomSVGASurface(Uint32 *pixels, SDL_PixelFormat format) {
return SDL_CreateSurfaceFrom(800, 600, format, pixels, 800 * 4);
}
/*
* Calculates the FNV-1a hash of input pixel data
*/
Uint32 FNVHash(Uint32* buf, int length) {
const Uint32 fnv_prime = 0x811C9DC5;
Uint32 hash = 0;
int i;
for (i = 0; i < length; buf++, i++)
{
hash *= fnv_prime;
hash ^= (*buf);
}
return hash;
}
/*
* Wraps the FNV-1a hash for an input surface's pixels
*/
Uint32 hashSurfacePixels(SDL_Surface * surface) {
Uint64 buffer_size = surface->w * surface->h;
return FNVHash(surface->pixels, buffer_size);
}
/* ================= Test Case Implementation ================== */
/**
* Tests rendering a rainbow gradient background onto a blank surface, then rendering a sprite with complex geometry and
* transparency on top of said surface, and comparing the result to known accurate renders with a hash.
*/
static int SDLCALL blit_testExampleApplicationRender(void *arg) {
const int width = 32;
const int height = 32;
const Uint32 correct_hash = 0xe345d7a7;
SDL_Surface* dest_surface = SDL_CreateSurface(width, height, SDL_PIXELFORMAT_ARGB8888);
SDL_Surface* rainbow_background = SDLTest_ImageBlendingBackground();
SDL_Surface* gearbrain_sprite = SDLTest_ImageBlendingSprite();
// Blit background into "screen"
SDL_BlitSurface(rainbow_background, NULL, dest_surface, NULL);
// Blit example game sprite onto "screen"
SDL_BlitSurface(gearbrain_sprite, NULL, dest_surface, NULL);
// Check result
const Uint32 hash = hashSurfacePixels(dest_surface);
SDLTest_AssertCheck(hash == correct_hash,
"Should render identically, expected hash 0x%" SDL_PRIx32 ", got 0x%" SDL_PRIx32,
correct_hash, hash);
// Clean up
SDL_DestroySurface(rainbow_background);
SDL_DestroySurface(gearbrain_sprite);
SDL_DestroySurface(dest_surface);
return TEST_COMPLETED;
}
/**
* Tests rendering PRNG noise onto a surface of PRNG noise, while also testing color shift operations between the
* different source and destination pixel formats, without an alpha shuffle, at SVGA resolution. Compares to known
* accurate renders with a hash.
*/
static int SDLCALL blit_testRandomToRandomSVGA(void *arg) {
const int width = 800;
const int height = 600;
const Uint32 correct_hash = 0x42140c5f;
// Allocate random buffers
Uint32 *dest_pixels = getNextRandomBuffer(width, height);
Uint32 *src_pixels = getNextRandomBuffer(width, height);
// Create surfaces of different pixel formats
SDL_Surface* dest_surface = getRandomSVGASurface(dest_pixels, SDL_PIXELFORMAT_BGRA8888);
SDL_Surface* src_surface = getRandomSVGASurface(src_pixels, SDL_PIXELFORMAT_RGBA8888);
// Blit surfaces
SDL_BlitSurface(src_surface, NULL, dest_surface, NULL);
// Check result
const Uint32 hash = hashSurfacePixels(dest_surface);
SDLTest_AssertCheck(hash == correct_hash,
"Should render identically, expected hash 0x%" SDL_PRIx32 ", got 0x%" SDL_PRIx32,
correct_hash, hash);
// Clean up
SDL_DestroySurface(dest_surface);
SDL_DestroySurface(src_surface);
SDL_free(dest_pixels);
SDL_free(src_pixels);
return TEST_COMPLETED;
}
/**
* Tests rendering small chunks of 15 by 15px PRNG noise onto an initially blank SVGA surface, while also testing color
* shift operations between the different source and destination pixel formats, including an alpha shuffle. Compares to
* known accurate renders with a hash.
*/
static int SDLCALL blit_testRandomToRandomSVGAMultipleIterations(void *arg) {
const int width = 800;
const int height = 600;
const int blit_width = 15;
const int blit_height = 15;
int i;
const Uint32 correct_hash = 0x5d26be78;
Uint32 *buf = SDL_malloc(blit_width * blit_height * sizeof(Uint32));
// Create blank source surface
SDL_Surface *sourceSurface = SDL_CreateSurface(blit_width, blit_height, SDL_PIXELFORMAT_RGBA8888);
// Create blank destination surface
SDL_Surface* dest_surface = SDL_CreateSurface(width, height, SDL_PIXELFORMAT_ABGR8888);
// Perform 250k random blits into random areas of the blank surface
for (i = 0; i < 250000; i++) {
fillNextRandomBuffer(buf, blit_width, blit_height);
SDL_LockSurface(sourceSurface);
SDL_memcpy(sourceSurface->pixels, buf, blit_width * blit_height * sizeof(Uint32));
SDL_UnlockSurface(sourceSurface);
SDL_Rect dest_rect;
int location = (int)getRandomUint32();
dest_rect.x = location % (width - 15 - 1);
dest_rect.y = location % (height - 15 - 1);
SDL_BlitSurface(sourceSurface, NULL, dest_surface, &dest_rect);
}
// Check result
const Uint32 hash = hashSurfacePixels(dest_surface);
// Clean up
SDL_DestroySurface(dest_surface);
SDLTest_AssertCheck(hash == correct_hash,
"Should render identically, expected hash 0x%" SDL_PRIx32 ", got 0x%" SDL_PRIx32,
correct_hash, hash);
SDL_DestroySurface(sourceSurface);
SDL_free(buf);
return TEST_COMPLETED;
}
static const SDLTest_TestCaseReference blitTest1 = {
blit_testExampleApplicationRender, "blit_testExampleApplicationRender",
"Test example application render.", TEST_ENABLED
};
static const SDLTest_TestCaseReference blitTest2 = {
blit_testRandomToRandomSVGA, "blit_testRandomToRandomSVGA",
"Test SVGA noise render.", TEST_ENABLED
};
static const SDLTest_TestCaseReference blitTest3 = {
blit_testRandomToRandomSVGAMultipleIterations, "blit_testRandomToRandomSVGAMultipleIterations",
"Test SVGA noise render (250k iterations).", TEST_ENABLED
};
static const SDLTest_TestCaseReference *blitTests[] = {
&blitTest1, &blitTest2, &blitTest3, NULL
};
SDLTest_TestSuiteReference blitTestSuite = {
"Blending",
blitSetUp,
blitTests,
NULL
};
|