EEVEE-Next: Tests support

Enable tests for EEVEE Next.

As a workaround for allowing the use of EEVEE Next (still an
experimental feature) with `--factory-startup`, `arg_handle_engine_set`
enables the feature when `-E BLENDER_EEVEE_NEXT` is used.
In addition, EEVEE Next is always registered, so it's available when
calling `WM_init`.
If it's actually disabled, it will be immediately unregistered after that.

Notes:
- `get_gpu_device_type` always fails with error:
  > GPU API is not available in background mode
- Setup and tests are the same as EEVEE. There are many tests that
  only make sense for Cycles, and many EEVEE Next features that are
  not actually tested.

Pull Request: https://projects.blender.org/blender/blender/pulls/112161
This commit is contained in:
Miguel Pozo 2023-10-05 16:02:49 +02:00
parent 361d4abbfc
commit b4de568cc4
6 changed files with 196 additions and 5 deletions

@ -3042,14 +3042,38 @@ void DRW_engine_register(DrawEngineType *draw_engine_type)
void DRW_engines_register_experimental()
{
if (U.experimental.enable_eevee_next) {
RE_engines_register(&DRW_engine_viewport_eevee_next_type);
if (!U.experimental.enable_eevee_next) {
/** Since EEVEE Next is always registered in `DRW_engines_register`,
* Here we just have to unregister if it's not actually enabled. */
for (auto *type = static_cast<RenderEngineType *>(R_engines.first); type; type = type->next) {
if (type == &DRW_engine_viewport_eevee_next_type) {
BLI_remlink(&R_engines, type);
break;
}
}
for (auto *type = static_cast<DRWRegisteredDrawEngine *>(g_registered_engines.engines.first);
type != nullptr;
type = static_cast<DRWRegisteredDrawEngine *>(type->next))
{
if (type->draw_engine == DRW_engine_viewport_eevee_next_type.draw_engine) {
BLI_remlink(&g_registered_engines.engines, type);
type->draw_engine->engine_free();
g_registered_engines.len--;
MEM_freeN(type);
break;
}
}
}
}
void DRW_engines_register()
{
RE_engines_register(&DRW_engine_viewport_eevee_type);
/* Always register EEVEE Next so it can be used in background mode with `--factory-startup`.
* (Needed for tests). */
RE_engines_register(&DRW_engine_viewport_eevee_next_type);
RE_engines_register(&DRW_engine_viewport_workbench_type);
DRW_engine_register(&draw_engine_gpencil_type);

@ -2246,6 +2246,7 @@ enum {
/** #RenderData::engine (scene.cc) */
extern const char *RE_engine_id_BLENDER_EEVEE;
extern const char *RE_engine_id_BLENDER_EEVEE_NEXT;
extern const char *RE_engine_id_BLENDER_WORKBENCH;
extern const char *RE_engine_id_CYCLES;

@ -536,9 +536,6 @@ int main(int argc,
WM_init(C, argc, (const char **)argv);
/* Need to be after WM init so that userpref are loaded. */
RE_engines_init_experimental();
#ifndef WITH_PYTHON
printf(
"\n* WARNING * - Blender compiled without Python!\n"
@ -557,6 +554,10 @@ int main(int argc,
BLI_args_parse(ba, ARG_PASS_FINAL, main_args_handle_load_file, C);
#endif
/* Need to be after WM init so that userpref are loaded,
* and after ars_parse, in case and experimental engine is enabled there. */
RE_engines_init_experimental();
/* Explicitly free data allocated for argument parsing:
* - 'ba'
* - 'argv' on WIN32.

@ -1614,6 +1614,12 @@ static int arg_handle_engine_set(int argc, const char **argv, void *data)
exit(0);
}
else {
if (strcmp(argv[1], RE_engine_id_BLENDER_EEVEE_NEXT) == 0) {
/** NOTE: Temp workaround to support EEVEE Next tests.
* This ensures the engine is not unregistered in `DRW_engines_register_experimental`
* when using --factory-startup. */
U.experimental.enable_eevee_next = true;
}
Scene *scene = CTX_data_scene(C);
if (scene) {
if (BLI_findstring(&R_engines, argv[1], offsetof(RenderEngineType, idname))) {

@ -679,6 +679,18 @@ if(WITH_CYCLES OR WITH_OPENGL_RENDER_TESTS)
)
endforeach()
# Eevee Next
foreach(render_test ${render_tests})
add_python_test(
eevee_next_${render_test}_test
${CMAKE_CURRENT_LIST_DIR}/eevee_next_render_tests.py
-blender "${TEST_BLENDER_EXE}"
-testdir "${TEST_SRC_DIR}/render/${render_test}"
-idiff "${OPENIMAGEIO_IDIFF}"
-outdir "${TEST_OUT_DIR}/eevee_next"
)
endforeach()
foreach(render_test ${render_tests})
# Workbench
add_python_test(

@ -0,0 +1,147 @@
#!/usr/bin/env python3
# SPDX-FileCopyrightText: 2015-2022 Blender Authors
#
# SPDX-License-Identifier: Apache-2.0
import argparse
import os
import pathlib
import subprocess
import sys
from pathlib import Path
def setup():
import bpy
for scene in bpy.data.scenes:
scene.render.engine = 'BLENDER_EEVEE_NEXT'
# Enable Eevee features
scene = bpy.context.scene
eevee = scene.eevee
eevee.gtao_distance = 1
eevee.use_volumetric_shadows = True
eevee.volumetric_tile_size = '2'
eevee.use_motion_blur = True
# Does not work in edit mode
try:
# Simple probe setup
bpy.ops.object.lightprobe_add(type='CUBEMAP', location=(0.5, 0, 1.5))
cubemap = bpy.context.selected_objects[0]
cubemap.scale = (2.5, 2.5, 1.0)
cubemap.data.falloff = 0
cubemap.data.clip_start = 2.4
bpy.ops.object.lightprobe_add(type='GRID', location=(0, 0, 0.25))
grid = bpy.context.selected_objects[0]
grid.scale = (1.735, 1.735, 1.735)
grid.data.bake_samples = 256
except:
pass
# Only include the plane in probes
for ob in scene.objects:
if ob.name != 'Plane' and ob.type != 'LIGHT':
ob.hide_probe_volume = True
ob.hide_probe_cubemap = True
bpy.ops.scene.light_cache_bake()
# When run from inside Blender, render and exit.
try:
import bpy
inside_blender = True
except ImportError:
inside_blender = False
if inside_blender:
try:
setup()
except Exception as e:
print(e)
sys.exit(1)
def get_gpu_device_type(blender):
#TODO: This always fails.
command = [
blender,
"-noaudio",
"--background"
"--factory-startup"
"--python",
str(pathlib.Path(__file__).parent / "gpu_info.py")
]
try:
completed_process = subprocess.run(command, stdout=subprocess.PIPE)
for line in completed_process.stdout.read_text():
if line.startswith("GPU_DEVICE_TYPE:"):
vendor = line.split(':')[1]
return vendor
except BaseException as e:
return None
return None
def get_arguments(filepath, output_filepath):
return [
"--background",
"-noaudio",
"--factory-startup",
"--enable-autoexec",
"--debug-memory",
"--debug-exit-on-error",
filepath,
"-E", "BLENDER_EEVEE_NEXT",
"-P",
os.path.realpath(__file__),
"-o", output_filepath,
"-F", "PNG",
"-f", "1"]
def create_argparse():
parser = argparse.ArgumentParser()
parser.add_argument("-blender", nargs="+")
parser.add_argument("-testdir", nargs=1)
parser.add_argument("-outdir", nargs=1)
parser.add_argument("-idiff", nargs=1)
return parser
def main():
parser = create_argparse()
args = parser.parse_args()
blender = args.blender[0]
test_dir = args.testdir[0]
idiff = args.idiff[0]
output_dir = args.outdir[0]
gpu_device_type = get_gpu_device_type(blender)
reference_override_dir = None
if gpu_device_type == "AMD":
reference_override_dir = "eevee_next_renders/amd"
from modules import render_report
report = render_report.Report("Eevee Next", output_dir, idiff)
report.set_pixelated(True)
report.set_reference_dir("eevee_next_renders")
report.set_reference_override_dir(reference_override_dir)
report.set_compare_engine('cycles', 'CPU')
test_dir_name = Path(test_dir).name
if test_dir_name.startswith('image'):
report.set_fail_threshold(0.051)
ok = report.run(test_dir, blender, get_arguments, batch=True)
sys.exit(not ok)
if not inside_blender and __name__ == "__main__":
main()