mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-05-06 01:50:38 +00:00
Drop the requirement for PIL in the tests, use pypng drop-in replacement
* This means we no longer need to import PIL from somewhere else if we run via renderdoccmd test functional
This commit is contained in:
+2
-2
@@ -22,8 +22,6 @@ Running the tests requires the same python version as was used to build the vers
|
||||
|
||||
**NOTE:** For windows users you also need to match the bitness, so a 64-bit python install will be needed to test a 64-bit build of RenderDoc, and similarly for 32-bit.
|
||||
|
||||
You'll need to install `psutil` and `pillow` python modules via pip. The script will check for this and error if you don't have them available.
|
||||
|
||||
Then running the tests means invoking `run_tests.py` with any options you need:
|
||||
|
||||
* `--renderdoc` and `--pyrenderdoc` are common parameters, used to modify the OS library search path and the python module path respectively to locate the right libraries. E.g. on windows `--pyrenderdoc /path/to/renderdoc/x64/Development/pymodules --renderdoc /path/to/renderdoc/x64/Development`.
|
||||
@@ -54,4 +52,6 @@ RenderDoc is released under the MIT license, see [the main github repository](ht
|
||||
|
||||
The tests use [GLAD](https://github.com/Dav1dde/glad) for extension loading, which is MIT licensed. [LZ4](https://github.com/lz4/lz4) for compression, which is BSD licensed. [volk](https://github.com/zeux/volk) for vulkan loading, which is MIT licensed. [nuklear](https://github.com/vurtun/nuklear) for the launcher UI, which is MIT licensed. [shaderc](https://github.com/google/shaderc) for building SPIR-V shaders, which is Apache-2.0 licensed.
|
||||
|
||||
The python tests use [pypng](https://github.com/drj11/pypng) as a pure dependency-less python png load/save library, which is MIT licensed.
|
||||
|
||||
A short clip from [Caminandes](http://www.caminandes.com/), available under the [Creative Commons Attribution 3.0 license (CC) caminandes.com](http://www.caminandes.com/sharing/), is used as a demo video.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -296,7 +296,7 @@ class TestCase:
|
||||
|
||||
self.controller.SaveTexture(save_data, img_path)
|
||||
|
||||
if not util.image_compare(img_path, ref_path):
|
||||
if not util.png_compare(img_path, ref_path):
|
||||
raise TestFailureException("Reference and output backbuffer image differ", ref_path, img_path)
|
||||
|
||||
log.success("Backbuffer is identical to reference")
|
||||
|
||||
+41
-22
@@ -4,7 +4,8 @@ import re
|
||||
import time
|
||||
import hashlib
|
||||
import zipfile
|
||||
from PIL import Image, ImageChops, ImageStat
|
||||
from typing import Tuple, List
|
||||
from . import png
|
||||
|
||||
|
||||
def _timestr():
|
||||
@@ -104,28 +105,50 @@ def sanitise_filename(name: str):
|
||||
return re.sub('^/', '', name)
|
||||
|
||||
|
||||
def image_compare(test_img: str, ref_img: str, tolerance: int = 2):
|
||||
def png_save(out_path: str, rows: List[bytes], dimensions: Tuple[int, int]):
|
||||
try:
|
||||
out = Image.open(test_img)
|
||||
f = open(out_path, 'wb')
|
||||
except Exception as ex:
|
||||
raise FileNotFoundError("Can't open {}".format(sanitise_filename(test_img)))
|
||||
raise FileNotFoundError("Can't open {} for write".format(sanitise_filename(out_path)))
|
||||
|
||||
try:
|
||||
ref = Image.open(ref_img)
|
||||
except Exception as ex:
|
||||
raise FileNotFoundError("Can't open {}".format(sanitise_filename(ref_img)))
|
||||
writer = png.Writer(dimensions[0], dimensions[1], alpha=True, greyscale=False, compression=7)
|
||||
writer.write(f, rows)
|
||||
f.close()
|
||||
|
||||
if out.mode != ref.mode or out.size != ref.size:
|
||||
|
||||
def png_load_data(in_path: str):
|
||||
reader = png.Reader(filename=in_path)
|
||||
return list(reader.read()[2])
|
||||
|
||||
|
||||
def png_load_dimensions(in_path: str):
|
||||
reader = png.Reader(filename=in_path)
|
||||
info = reader.read()
|
||||
return (info[0], info[1])
|
||||
|
||||
|
||||
def png_compare(test_img: str, ref_img: str, tolerance: int = 2):
|
||||
test_reader = png.Reader(filename=test_img)
|
||||
ref_reader = png.Reader(filename=ref_img)
|
||||
|
||||
test_w, test_h, test_data, _ = test_reader.read()
|
||||
ref_w, ref_h, ref_data, _ = ref_reader.read()
|
||||
|
||||
if test_w != ref_w or test_h != test_h:
|
||||
return False
|
||||
|
||||
# Generate the difference
|
||||
diff = ImageChops.difference(out, ref)
|
||||
is_same = True
|
||||
diff_data = []
|
||||
|
||||
# Subtract N from the difference, to allow for off-by-N errors that can be caused by rounding.
|
||||
# For example clearing to 0.5, 0.5, 0.5 has two valid representations: 127,127,127 and 128,128,128
|
||||
# which are equally far from true 0.5.
|
||||
diff = ImageChops.subtract(diff, Image.new(diff.mode, (diff.width, diff.height),
|
||||
(tolerance, tolerance, tolerance, tolerance)))
|
||||
for test_row, ref_row in zip(test_data, ref_data):
|
||||
diff = [min(255, abs(test_row[i] - ref_row[i])*4) for i in range(0, test_w*4)]
|
||||
|
||||
is_same = is_same and not any([d > tolerance*4 for d in diff])
|
||||
|
||||
diff_data.append([255 if i % 4 == 3 else d for i, d in enumerate(diff)])
|
||||
|
||||
if is_same:
|
||||
return True
|
||||
|
||||
# If the diff fails, dump the difference to a file
|
||||
diff_file = get_tmp_path('diff.png')
|
||||
@@ -133,13 +156,9 @@ def image_compare(test_img: str, ref_img: str, tolerance: int = 2):
|
||||
if os.path.exists(diff_file):
|
||||
os.remove(diff_file)
|
||||
|
||||
if sum(ImageStat.Stat(diff).sum) > 0:
|
||||
# this does (img1 + img2) / scale, so scale=0.5 means we multiply the image by 2/0.5 = 4
|
||||
diff = ImageChops.add(diff, diff, scale=0.5)
|
||||
diff.convert("RGB").save(diff_file)
|
||||
return False
|
||||
png_save(diff_file, diff_data, (test_w, test_h))
|
||||
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def md5_compare(test_file: str, ref_file: str):
|
||||
|
||||
@@ -2,13 +2,6 @@ import argparse
|
||||
import os
|
||||
import sys
|
||||
|
||||
try:
|
||||
import PIL
|
||||
del PIL
|
||||
except ImportError as e:
|
||||
print("Missing dependency: {}".format(e))
|
||||
sys.exit(1)
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('-r', '--renderdoc',
|
||||
help="The location of the renderdoc library to use", type=str)
|
||||
|
||||
@@ -64,7 +64,7 @@ class D3D11_Overlay_Test(rdtest.TestCase):
|
||||
|
||||
self.controller.SaveTexture(save_data, overlay_path)
|
||||
|
||||
if not rdtest.image_compare(overlay_path, ref_path, tolerance):
|
||||
if not rdtest.png_compare(overlay_path, ref_path, tolerance):
|
||||
raise rdtest.TestFailureException("Reference and output image differ for overlay {}".format(str(overlay)), overlay_path, ref_path)
|
||||
|
||||
rdtest.log.success("Reference and output image are identical for {}".format(str(overlay)))
|
||||
@@ -79,7 +79,7 @@ class D3D11_Overlay_Test(rdtest.TestCase):
|
||||
|
||||
self.controller.SaveTexture(save_data, tmp_path)
|
||||
|
||||
if not rdtest.image_compare(tmp_path, ref_path):
|
||||
if not rdtest.png_compare(tmp_path, ref_path):
|
||||
raise rdtest.TestFailureException("Reference and output image differ for depth {}", tmp_path, ref_path)
|
||||
|
||||
rdtest.log.success("Reference and output image are identical for depth")
|
||||
@@ -91,7 +91,7 @@ class D3D11_Overlay_Test(rdtest.TestCase):
|
||||
|
||||
self.controller.SaveTexture(save_data, tmp_path)
|
||||
|
||||
if not rdtest.image_compare(tmp_path, ref_path):
|
||||
if not rdtest.png_compare(tmp_path, ref_path):
|
||||
raise rdtest.TestFailureException("Reference and output image differ for stencil {}", tmp_path, ref_path)
|
||||
|
||||
rdtest.log.success("Reference and output image are identical for stencil")
|
||||
|
||||
@@ -65,7 +65,7 @@ class D3D12_Overlay_Test(rdtest.TestCase):
|
||||
|
||||
self.controller.SaveTexture(save_data, overlay_path)
|
||||
|
||||
if not rdtest.image_compare(overlay_path, ref_path, tolerance):
|
||||
if not rdtest.png_compare(overlay_path, ref_path, tolerance):
|
||||
raise rdtest.TestFailureException("Reference and output image differ for overlay {}".format(str(overlay)), overlay_path, ref_path)
|
||||
|
||||
rdtest.log.success("Reference and output image are identical for {}".format(str(overlay)))
|
||||
@@ -80,7 +80,7 @@ class D3D12_Overlay_Test(rdtest.TestCase):
|
||||
|
||||
self.controller.SaveTexture(save_data, tmp_path)
|
||||
|
||||
if not rdtest.image_compare(tmp_path, ref_path):
|
||||
if not rdtest.png_compare(tmp_path, ref_path):
|
||||
raise rdtest.TestFailureException("Reference and output image differ for depth {}", tmp_path, ref_path)
|
||||
|
||||
rdtest.log.success("Reference and output image are identical for depth")
|
||||
@@ -92,7 +92,7 @@ class D3D12_Overlay_Test(rdtest.TestCase):
|
||||
|
||||
self.controller.SaveTexture(save_data, tmp_path)
|
||||
|
||||
if not rdtest.image_compare(tmp_path, ref_path):
|
||||
if not rdtest.png_compare(tmp_path, ref_path):
|
||||
raise rdtest.TestFailureException("Reference and output image differ for stencil {}", tmp_path, ref_path)
|
||||
|
||||
rdtest.log.success("Reference and output image are identical for stencil")
|
||||
|
||||
@@ -62,7 +62,7 @@ class GL_Overlay_Test(rdtest.TestCase):
|
||||
|
||||
self.controller.SaveTexture(save_data, overlay_path)
|
||||
|
||||
if not rdtest.image_compare(overlay_path, ref_path, tolerance):
|
||||
if not rdtest.png_compare(overlay_path, ref_path, tolerance):
|
||||
raise rdtest.TestFailureException("Reference and output image differ for overlay {}".format(str(overlay)), overlay_path, ref_path)
|
||||
|
||||
rdtest.log.success("Reference and output image are identical for {}".format(str(overlay)))
|
||||
@@ -77,7 +77,7 @@ class GL_Overlay_Test(rdtest.TestCase):
|
||||
|
||||
self.controller.SaveTexture(save_data, tmp_path)
|
||||
|
||||
if not rdtest.image_compare(tmp_path, ref_path):
|
||||
if not rdtest.png_compare(tmp_path, ref_path):
|
||||
raise rdtest.TestFailureException("Reference and output image differ for depth {}", tmp_path, ref_path)
|
||||
|
||||
rdtest.log.success("Reference and output image are identical for depth")
|
||||
@@ -89,7 +89,7 @@ class GL_Overlay_Test(rdtest.TestCase):
|
||||
|
||||
self.controller.SaveTexture(save_data, tmp_path)
|
||||
|
||||
if not rdtest.image_compare(tmp_path, ref_path):
|
||||
if not rdtest.png_compare(tmp_path, ref_path):
|
||||
raise rdtest.TestFailureException("Reference and output image differ for stencil {}", tmp_path, ref_path)
|
||||
|
||||
rdtest.log.success("Reference and output image are identical for stencil")
|
||||
|
||||
@@ -22,7 +22,7 @@ class VK_Indirect(rdtest.TestCase):
|
||||
|
||||
self.controller.SaveTexture(save_data, overlay_path)
|
||||
|
||||
if not rdtest.image_compare(overlay_path, ref_path):
|
||||
if not rdtest.png_compare(overlay_path, ref_path):
|
||||
raise rdtest.TestFailureException("Reference and output image differ @ EID {}".format(str(eventId)),
|
||||
ref_path, overlay_path)
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ class VK_Overlay_Test(rdtest.TestCase):
|
||||
|
||||
self.controller.SaveTexture(save_data, overlay_path)
|
||||
|
||||
if not rdtest.image_compare(overlay_path, ref_path, tolerance):
|
||||
if not rdtest.png_compare(overlay_path, ref_path, tolerance):
|
||||
raise rdtest.TestFailureException("Reference and output image differ for overlay {}".format(str(overlay)), overlay_path, ref_path)
|
||||
|
||||
rdtest.log.success("Reference and output image are identical for {}".format(str(overlay)))
|
||||
@@ -77,7 +77,7 @@ class VK_Overlay_Test(rdtest.TestCase):
|
||||
|
||||
self.controller.SaveTexture(save_data, tmp_path)
|
||||
|
||||
if not rdtest.image_compare(tmp_path, ref_path):
|
||||
if not rdtest.png_compare(tmp_path, ref_path):
|
||||
raise rdtest.TestFailureException("Reference and output image differ for depth {}", tmp_path, ref_path)
|
||||
|
||||
rdtest.log.success("Reference and output image are identical for depth")
|
||||
@@ -89,7 +89,7 @@ class VK_Overlay_Test(rdtest.TestCase):
|
||||
|
||||
self.controller.SaveTexture(save_data, tmp_path)
|
||||
|
||||
if not rdtest.image_compare(tmp_path, ref_path):
|
||||
if not rdtest.png_compare(tmp_path, ref_path):
|
||||
raise rdtest.TestFailureException("Reference and output image differ for stencil {}", tmp_path, ref_path)
|
||||
|
||||
rdtest.log.success("Reference and output image are identical for stencil")
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import renderdoc as rd
|
||||
import rdtest
|
||||
from PIL import Image
|
||||
|
||||
|
||||
class VK_Sample_Locations(rdtest.TestCase):
|
||||
@@ -72,14 +71,24 @@ class VK_Sample_Locations(rdtest.TestCase):
|
||||
save_data.sample.mapToArray = False
|
||||
|
||||
dim = (0, 0)
|
||||
fmt: rd.ResourceFormat = None
|
||||
texs = self.controller.GetTextures()
|
||||
for tex in texs:
|
||||
tex: rd.TextureDescription
|
||||
if tex.resourceId == save_data.resourceId:
|
||||
dim = (tex.width, tex.height)
|
||||
fmt = tex.format
|
||||
|
||||
if dim == (0,0):
|
||||
raise rdtest.TestFailureException("Couldn't get dimensions of texture")
|
||||
|
||||
halfdim = (dim[0] >> 1, dim[1])
|
||||
|
||||
if (fmt.type != rd.ResourceFormatType.Regular or fmt.compByteWidth != 1 or fmt.compCount != 4):
|
||||
raise rdtest.TestFailureException("Texture is not RGBA8 as expected: {}".format(fmt.Name()))
|
||||
|
||||
stride = fmt.compByteWidth * fmt.compCount * dim[0]
|
||||
|
||||
last_draw: rd.DrawcallDescription = self.get_last_draw()
|
||||
|
||||
self.controller.SetFrameEvent(last_draw.eventId, True)
|
||||
@@ -104,24 +113,32 @@ class VK_Sample_Locations(rdtest.TestCase):
|
||||
save_data.sample.sampleIndex = sample
|
||||
self.controller.SaveTexture(save_data, tmp_path)
|
||||
|
||||
try:
|
||||
img = Image.open(tmp_path)
|
||||
except Exception as ex:
|
||||
raise FileNotFoundError("Can't open {}".format(rdtest.sanitise_filename(tmp_path)))
|
||||
combined_data = rdtest.png_load_data(tmp_path)
|
||||
|
||||
img.crop((0, 0, dim[0]/2, dim[1])).save(degenerate_path)
|
||||
img.crop((dim[0]/2, 0, dim[0], dim[1])).save(rotated_path)
|
||||
# crop left for degenerate, and crop right for rotated
|
||||
degenerate = []
|
||||
rotated = []
|
||||
for row in range(0, dim[1]):
|
||||
srcstart = row * stride
|
||||
|
||||
len = halfdim[0] * fmt.compCount
|
||||
|
||||
degenerate.append(combined_data[row][0:len])
|
||||
rotated.append(combined_data[row][len:])
|
||||
|
||||
rdtest.png_save(degenerate_path, degenerate, halfdim)
|
||||
rdtest.png_save(rotated_path, rotated, halfdim)
|
||||
|
||||
# first two degenerate images should be identical, as should the last two, and they should be different.
|
||||
if not rdtest.image_compare(degenerate_paths[0], degenerate_paths[1], 0):
|
||||
if not rdtest.png_compare(degenerate_paths[0], degenerate_paths[1], 0):
|
||||
raise rdtest.TestFailureException("Degenerate grid sample 0 and 1 are different",
|
||||
degenerate_paths[0], degenerate_paths[1])
|
||||
|
||||
if not rdtest.image_compare(degenerate_paths[2], degenerate_paths[3], 0):
|
||||
if not rdtest.png_compare(degenerate_paths[2], degenerate_paths[3], 0):
|
||||
raise rdtest.TestFailureException("Degenerate grid sample 2 and 3 are different",
|
||||
degenerate_paths[2], degenerate_paths[3])
|
||||
|
||||
if rdtest.image_compare(degenerate_paths[1], degenerate_paths[2], 0):
|
||||
if rdtest.png_compare(degenerate_paths[1], degenerate_paths[2], 0):
|
||||
raise rdtest.TestFailureException("Degenerate grid sample 1 and 2 are identical",
|
||||
degenerate_paths[1], degenerate_paths[2])
|
||||
|
||||
@@ -130,7 +147,7 @@ class VK_Sample_Locations(rdtest.TestCase):
|
||||
# all rotated images should be different
|
||||
for A in range(0, 4):
|
||||
for B in range(A+1, 4):
|
||||
if rdtest.image_compare(rotated_paths[A], rotated_paths[B], 0):
|
||||
if rdtest.png_compare(rotated_paths[A], rotated_paths[B], 0):
|
||||
raise rdtest.TestFailureException("Rotated grid sample {} and {} are identical".format(A, B),
|
||||
rotated_paths[A], rotated_paths[B])
|
||||
|
||||
|
||||
Reference in New Issue
Block a user