Blender V5.0
shader_test.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: Apache-2.0 */
4
5#include "testing/testing.h"
6
7#include "BLI_math_base.h"
9#include "GPU_batch.hh"
10#include "GPU_batch_presets.hh"
11#include "GPU_capabilities.hh"
12#include "GPU_compute.hh"
13#include "GPU_context.hh"
14#include "GPU_framebuffer.hh"
15#include "GPU_index_buffer.hh"
16#include "GPU_shader.hh"
17#include "GPU_shader_shared.hh"
18#include "GPU_state.hh"
19#include "GPU_texture.hh"
20#include "GPU_vertex_buffer.hh"
21#include "GPU_vertex_format.hh"
22
23#include "MEM_guardedalloc.h"
24
28#include "gpu_testing.hh"
29
30namespace blender::gpu::tests {
31
32using namespace blender::gpu::shader;
33
35{
36
37 static constexpr uint SIZE = 512;
38
39 /* Build compute shader. */
40 gpu::Shader *shader = GPU_shader_create_from_info_name("gpu_compute_2d_test");
41 EXPECT_NE(shader, nullptr);
42
43 /* Create texture to store result and attach to shader. */
44 blender::gpu::Texture *texture = GPU_texture_create_2d("gpu_shader_compute_2d",
45 SIZE,
46 SIZE,
47 1,
48 TextureFormat::SFLOAT_32_32_32_32,
50 nullptr);
51 EXPECT_NE(texture, nullptr);
52
55
56 /* Dispatch compute task. */
58
59 /* Check if compute has been done. */
61 float *data = static_cast<float *>(GPU_texture_read(texture, GPU_DATA_FLOAT, 0));
62 EXPECT_NE(data, nullptr);
63 for (int index = 0; index < SIZE * SIZE; index++) {
64 EXPECT_FLOAT_EQ(data[index * 4 + 0], 1.0f);
65 EXPECT_FLOAT_EQ(data[index * 4 + 1], 0.5f);
66 EXPECT_FLOAT_EQ(data[index * 4 + 2], 0.2f);
67 EXPECT_FLOAT_EQ(data[index * 4 + 3], 1.0f);
68 }
70
71 /* Cleanup. */
76}
77GPU_TEST(shader_compute_2d)
78
80{
81 static constexpr uint SIZE = 10;
82
83 /* Build compute shader. */
84 gpu::Shader *shader = GPU_shader_create_from_info_name("gpu_compute_1d_test");
85 EXPECT_NE(shader, nullptr);
86
87 /* Construct Texture. */
88 blender::gpu::Texture *texture = GPU_texture_create_1d("gpu_shader_compute_1d",
89 SIZE,
90 1,
91 TextureFormat::SFLOAT_32_32_32_32,
93 nullptr);
94 EXPECT_NE(texture, nullptr);
95
98
99 /* Dispatch compute task. */
101
102 /* Check if compute has been done. */
104
105 /* Create texture to load back result. */
106 float *data = static_cast<float *>(GPU_texture_read(texture, GPU_DATA_FLOAT, 0));
107 EXPECT_NE(data, nullptr);
108 for (int index = 0; index < SIZE; index++) {
109 float expected_value = index;
110 EXPECT_FLOAT_EQ(data[index * 4 + 0], expected_value);
111 EXPECT_FLOAT_EQ(data[index * 4 + 1], expected_value);
112 EXPECT_FLOAT_EQ(data[index * 4 + 2], expected_value);
113 EXPECT_FLOAT_EQ(data[index * 4 + 3], expected_value);
114 }
116
117 /* Cleanup. */
122}
123GPU_TEST(shader_compute_1d)
124
126{
127 static constexpr uint SIZE = 128;
128
129 /* Build compute shader. */
130 gpu::Shader *shader = GPU_shader_create_from_info_name("gpu_compute_vbo_test");
131 EXPECT_NE(shader, nullptr);
133
134 /* Construct VBO. */
135 GPUVertFormat format = {0};
136 GPU_vertformat_attr_add(&format, "pos", gpu::VertAttrType::SFLOAT_32_32_32_32);
140
141 /* Dispatch compute task. */
143
144 /* Check if compute has been done. */
146
147 /* Download the vertex buffer. */
148 float data[SIZE * 4];
150 for (int index = 0; index < SIZE; index++) {
151 float expected_value = index;
152 EXPECT_FLOAT_EQ(data[index * 4 + 0], expected_value);
153 EXPECT_FLOAT_EQ(data[index * 4 + 1], expected_value);
154 EXPECT_FLOAT_EQ(data[index * 4 + 2], expected_value);
155 EXPECT_FLOAT_EQ(data[index * 4 + 3], expected_value);
156 }
157
158 /* Cleanup. */
162}
163GPU_TEST(shader_compute_vbo)
164
166{
167 static constexpr uint SIZE = 128;
168
169 /* Build compute shader. */
170 gpu::Shader *shader = GPU_shader_create_from_info_name("gpu_compute_ibo_test");
171 EXPECT_NE(shader, nullptr);
173
174 /* Construct IBO. */
177
178 /* Dispatch compute task. */
180
181 /* Check if compute has been done. */
183
184 /* Download the index buffer. */
185 uint32_t data[SIZE];
187 for (int index = 0; index < SIZE; index++) {
188 uint32_t expected = index;
189 EXPECT_EQ(data[index], expected);
190 }
191
192 /* Cleanup. */
196}
197GPU_TEST(shader_compute_ibo)
198
200{
201 static constexpr uint SIZE = 128;
202
203 /* Build compute shader. */
204 gpu::Shader *shader = GPU_shader_create_from_info_name("gpu_compute_ssbo_test");
205 EXPECT_NE(shader, nullptr);
207
208 /* Construct SSBO. */
210 SIZE * sizeof(uint32_t), nullptr, GPU_USAGE_DEVICE_ONLY, __func__);
212
213 /* Dispatch compute task. */
215
216 /* Check if compute has been done. */
218
219 /* Download the storage buffer. */
220 uint32_t data[SIZE];
222 for (int index = 0; index < SIZE; index++) {
223 uint32_t expected = index * 4;
224 EXPECT_EQ(data[index], expected);
225 }
226
227 /* Cleanup. */
231}
232GPU_TEST(shader_compute_ssbo)
233
235{
236 /* Build compute shader. */
237 gpu::Shader *shader = GPU_shader_create_from_info_name("gpu_compute_ssbo_binding_test");
238 EXPECT_NE(shader, nullptr);
239
240 /* Perform tests. */
243
244 /* Cleanup. */
246}
247GPU_TEST(shader_ssbo_binding)
248
249static std::string print_test_data(const TestOutputRawData &raw, TestType type)
250{
251 std::stringstream ss;
252 switch (type) {
253 case TEST_TYPE_BOOL:
254 case TEST_TYPE_UINT:
255 ss << *reinterpret_cast<const uint *>(&raw);
256 break;
257 case TEST_TYPE_INT:
258 ss << *reinterpret_cast<const int *>(&raw);
259 break;
260 case TEST_TYPE_FLOAT:
261 ss << *reinterpret_cast<const float *>(&raw);
262 break;
263 case TEST_TYPE_IVEC2:
264 ss << *reinterpret_cast<const int2 *>(&raw);
265 break;
266 case TEST_TYPE_IVEC3:
267 ss << *reinterpret_cast<const int3 *>(&raw);
268 break;
269 case TEST_TYPE_IVEC4:
270 ss << *reinterpret_cast<const int4 *>(&raw);
271 break;
272 case TEST_TYPE_UVEC2:
273 ss << *reinterpret_cast<const uint2 *>(&raw);
274 break;
275 case TEST_TYPE_UVEC3:
276 ss << *reinterpret_cast<const uint3 *>(&raw);
277 break;
278 case TEST_TYPE_UVEC4:
279 ss << *reinterpret_cast<const uint4 *>(&raw);
280 break;
281 case TEST_TYPE_VEC2:
282 ss << *reinterpret_cast<const float2 *>(&raw);
283 break;
284 case TEST_TYPE_VEC3:
285 ss << *reinterpret_cast<const float3 *>(&raw);
286 break;
287 case TEST_TYPE_VEC4:
288 ss << *reinterpret_cast<const float4 *>(&raw);
289 break;
290 case TEST_TYPE_MAT2X2:
291 ss << *reinterpret_cast<const float2x2 *>(&raw);
292 break;
293 case TEST_TYPE_MAT2X3:
294 ss << *reinterpret_cast<const float2x3 *>(&raw);
295 break;
296 case TEST_TYPE_MAT2X4:
297 ss << *reinterpret_cast<const float2x4 *>(&raw);
298 break;
299 case TEST_TYPE_MAT3X2:
300 ss << *reinterpret_cast<const float3x2 *>(&raw);
301 break;
302 case TEST_TYPE_MAT3X3:
303 ss << *reinterpret_cast<const float3x3 *>(&raw);
304 break;
305 case TEST_TYPE_MAT3X4:
306 ss << *reinterpret_cast<const float3x4 *>(&raw);
307 break;
308 case TEST_TYPE_MAT4X2:
309 ss << *reinterpret_cast<const float4x2 *>(&raw);
310 break;
311 case TEST_TYPE_MAT4X3:
312 ss << *reinterpret_cast<const float4x3 *>(&raw);
313 break;
314 case TEST_TYPE_MAT4X4:
315 ss << *reinterpret_cast<const float4x4 *>(&raw);
316 break;
317 default:
318 ss << *reinterpret_cast<const MatBase<uint, 4, 4> *>(&raw);
319 break;
320 }
321 return ss.str();
322}
323
325{
326 /* Start at line one like the line report scheme.
327 * However, our preprocessor adds a line directive at the top of the file. Skip it. */
328 int64_t line = 1 - 1;
329 int64_t last_pos = 0;
330 int64_t pos = 0;
331 while ((pos = test_src.find('\n', pos)) != std::string::npos) {
332 if (line == test_line) {
333 return test_src.substr(last_pos, pos - last_pos);
334 }
335 pos += 1; /* Skip newline */
336 last_pos = pos;
337 line++;
338 }
339 return "";
340}
341
342static void gpu_shader_lib_test(const char *test_src_name, const char *additional_info = nullptr)
343{
344 using namespace shader;
345
347
348 ShaderCreateInfo create_info(test_src_name);
349 create_info.fragment_source(test_src_name);
350 create_info.additional_info("gpu_shader_test");
351 if (additional_info) {
352 create_info.additional_info(additional_info);
353 }
354
355 StringRefNull test_src = gpu_shader_dependency_get_source(test_src_name);
356
358 reinterpret_cast<GPUShaderCreateInfo *>(&create_info));
359
360 int test_count = 0;
361 /* Count tests. */
362 int64_t pos = 0;
363 StringRefNull target = "EXPECT_";
364 while ((pos = test_src.find(target, pos)) != std::string::npos) {
365 test_count++;
366 pos += sizeof("EXPECT_");
367 }
368
369 int test_output_px_len = divide_ceil_u(sizeof(TestOutput), 4 * 4);
370
373 "tx", test_output_px_len, test_count, 1, TextureFormat::UINT_32_32_32_32, usage, nullptr);
377
379
382
384
385 GPU_finish();
386
388 Span<TestOutput> tests(test_data, test_count);
389
390 for (const TestOutput &test : tests) {
391 if (ELEM(test.status, TEST_STATUS_NONE, TEST_STATUS_PASSED)) {
392 continue;
393 }
394 if (test.status == TEST_STATUS_FAILED) {
395 ADD_FAILURE_AT(test_src_name, test.line)
396 << "Value of: " << print_test_line(test_src, test.line) << "\n"
397 << " Actual: " << print_test_data(test.expect, TestType(test.type)) << "\n"
398 << "Expected: " << print_test_data(test.result, TestType(test.type)) << "\n";
399 }
400 else {
402 }
403 }
404
406
407 /* Cleanup. */
411 GPU_texture_free(tex);
412
414}
415
416static void test_math_lib()
417{
418 gpu_shader_lib_test("gpu_math_test.glsl");
419}
420GPU_TEST(math_lib)
421
422static void test_eevee_lib()
423{
424 /* TODO(fclem): Not passing currently. Need to be updated. */
425 // gpu_shader_lib_test("eevee_shadow_test.glsl", "eevee_tests_data");
426 gpu_shader_lib_test("eevee_occupancy_test.glsl");
427 gpu_shader_lib_test("eevee_horizon_scan_test.glsl");
428#ifndef __APPLE__ /* PSOs fail to compile on Mac. Try to port them to compute shader to see if it \
429 * fixes the issue. */
430 gpu_shader_lib_test("eevee_gbuffer_normal_test.glsl", "eevee_tests_data");
431 gpu_shader_lib_test("eevee_gbuffer_closure_test.glsl", "eevee_tests_data");
432#endif
433}
434GPU_TEST(eevee_lib)
435
436} // namespace blender::gpu::tests
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
EXPECT_EQ(BLI_expr_pylike_eval(expr, nullptr, 0, &result), EXPR_PYLIKE_INVALID)
MINLINE uint divide_ceil_u(uint a, uint b)
unsigned int uint
#define ELEM(...)
void GPU_batch_discard(blender::gpu::Batch *batch)
void GPU_batch_draw(blender::gpu::Batch *batch)
blender::gpu::Batch * GPU_batch_create_procedural(GPUPrimType primitive_type, int32_t vertex_count)
Definition gpu_batch.cc:83
void GPU_batch_set_shader(blender::gpu::Batch *batch, blender::gpu::Shader *shader, const blender::gpu::shader::SpecializationConstants *constants_state=nullptr)
void GPU_compute_dispatch(blender::gpu::Shader *shader, uint groups_x_len, uint groups_y_len, uint groups_z_len, const blender::gpu::shader::SpecializationConstants *constants_state=nullptr)
void GPU_render_end()
void GPU_render_begin()
blender::gpu::FrameBuffer * GPU_framebuffer_create(const char *name)
#define GPU_ATTACHMENT_TEXTURE(_texture)
#define GPU_ATTACHMENT_NONE
void GPU_framebuffer_free(blender::gpu::FrameBuffer *fb)
#define GPU_framebuffer_ensure_config(_fb,...)
void GPU_framebuffer_bind(blender::gpu::FrameBuffer *fb)
void GPU_indexbuf_discard(blender::gpu::IndexBuf *elem)
blender::gpu::IndexBuf * GPU_indexbuf_build_on_device(uint index_len)
void GPU_indexbuf_bind_as_ssbo(blender::gpu::IndexBuf *elem, int binding)
void GPU_indexbuf_read(blender::gpu::IndexBuf *elem, uint32_t *data)
@ GPU_PRIM_TRIS
void GPU_shader_free(blender::gpu::Shader *shader)
blender::gpu::Shader * GPU_shader_create_from_info(const GPUShaderCreateInfo *_info)
int GPU_shader_get_sampler_binding(blender::gpu::Shader *shader, const char *name)
void GPU_shader_bind(blender::gpu::Shader *shader, const blender::gpu::shader::SpecializationConstants *constants_state=nullptr)
int GPU_shader_get_ssbo_binding(blender::gpu::Shader *shader, const char *name)
blender::gpu::Shader * GPU_shader_create_from_info_name(const char *info_name)
void GPU_shader_unbind()
@ TEST_TYPE_IVEC2
@ TEST_TYPE_UINT
@ TEST_TYPE_INT
@ TEST_TYPE_IVEC4
@ TEST_TYPE_MAT2X2
@ TEST_TYPE_VEC2
@ TEST_TYPE_FLOAT
@ TEST_TYPE_VEC3
@ TEST_TYPE_BOOL
@ TEST_TYPE_IVEC3
@ TEST_TYPE_UVEC3
@ TEST_TYPE_MAT3X3
@ TEST_TYPE_UVEC2
@ TEST_TYPE_MAT4X4
@ TEST_TYPE_MAT2X3
@ TEST_TYPE_MAT4X3
@ TEST_TYPE_MAT3X2
@ TEST_TYPE_MAT4X2
@ TEST_TYPE_UVEC4
@ TEST_TYPE_MAT2X4
@ TEST_TYPE_VEC4
@ TEST_TYPE_MAT3X4
@ TEST_STATUS_NONE
@ TEST_STATUS_PASSED
@ TEST_STATUS_FAILED
@ GPU_BARRIER_BUFFER_UPDATE
Definition GPU_state.hh:56
@ GPU_BARRIER_TEXTURE_UPDATE
Definition GPU_state.hh:39
void GPU_finish()
Definition gpu_state.cc:310
void GPU_memory_barrier(GPUBarrier barrier)
Definition gpu_state.cc:326
void GPU_storagebuf_free(blender::gpu::StorageBuf *ssbo)
blender::gpu::StorageBuf * GPU_storagebuf_create_ex(size_t size, const void *data, GPUUsageType usage, const char *name)
void GPU_storagebuf_bind(blender::gpu::StorageBuf *ssbo, int slot)
void GPU_storagebuf_read(blender::gpu::StorageBuf *ssbo, void *data)
void GPU_texture_unbind(blender::gpu::Texture *texture)
@ GPU_DATA_UINT
@ GPU_DATA_FLOAT
eGPUTextureUsage
@ GPU_TEXTURE_USAGE_HOST_READ
@ GPU_TEXTURE_USAGE_ATTACHMENT
@ GPU_TEXTURE_USAGE_GENERAL
void * GPU_texture_read(blender::gpu::Texture *texture, eGPUDataFormat data_format, int mip_level)
void GPU_texture_image_bind(blender::gpu::Texture *texture, int unit)
blender::gpu::Texture * GPU_texture_create_2d(const char *name, int width, int height, int mip_len, blender::gpu::TextureFormat format, eGPUTextureUsage usage, const float *data)
void GPU_texture_free(blender::gpu::Texture *texture)
blender::gpu::Texture * GPU_texture_create_1d(const char *name, int width, int mip_len, blender::gpu::TextureFormat format, eGPUTextureUsage usage, const float *data)
blender::gpu::VertBuf * GPU_vertbuf_create_with_format_ex(const GPUVertFormat &format, GPUUsageType usage)
void GPU_vertbuf_read(const blender::gpu::VertBuf *verts, void *data)
void GPU_vertbuf_data_alloc(blender::gpu::VertBuf &verts, uint v_len)
void GPU_vertbuf_bind_as_ssbo(blender::gpu::VertBuf *verts, int binding)
void GPU_vertbuf_discard(blender::gpu::VertBuf *)
@ GPU_USAGE_DEVICE_ONLY
uint GPU_vertformat_attr_add(GPUVertFormat *format, blender::StringRef name, blender::gpu::VertAttrType type)
Read Guarded memory(de)allocation.
BMesh const char void * data
long long int int64_t
constexpr int64_t find(char c, int64_t pos=0) const
constexpr StringRef substr(int64_t start, int64_t size) const
struct @021025263243242147216143265077100330027142264337::@225245033123204053237120173316075113304004012000 batch
uint pos
TEX_TEMPLATE DataVec texture(T, FltCoord, float=0.0f) RET
#define GPU_TEST(test_name)
BLI_INLINE float fb(float length, float L)
format
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
StringRefNull gpu_shader_dependency_get_source(const StringRefNull shader_source_name)
static void test_shader_compute_2d()
static void test_shader_compute_ssbo()
static std::string print_test_data(const TestOutputRawData &raw, TestType type)
static Vector< int32_t > test_data()
static void test_eevee_lib()
static void test_shader_compute_vbo()
static void gpu_shader_lib_test(const char *test_src_name, const char *additional_info=nullptr)
static void test_math_lib()
static void test_shader_ssbo_binding()
static void test_shader_compute_1d()
static StringRef print_test_line(StringRefNull test_src, int64_t test_line)
static void test_shader_compute_ibo()
Describe inputs & outputs, stage interfaces, resources and sources of a shader. If all data is correc...
Self & fragment_source(StringRefNull filename)
Self & additional_info(StringRefNull info_name)