mirror of
https://github.com/Droid-Deck/DroidDeck.git
synced 2026-11-11 19:11:08 +00:00
* fix: upscale game buffers before native-resolution composition * fix: drop an added code comment from the effects source draw * fix: effects source keeps pillarboxed games and near-full source rectangles * fix: a larger opaque draw above keeps the full composition path --------- Co-authored-by: maxjivi05 <maxjividen@gmail.com>
567 lines
28 KiB
Python
567 lines
28 KiB
Python
"""Exercise the renderer's source selection and both presentation paths without a GPU."""
|
|
import os
|
|
from pathlib import Path
|
|
import re
|
|
import shutil
|
|
import subprocess
|
|
import tempfile
|
|
import unittest
|
|
|
|
|
|
REPO = Path(__file__).resolve().parents[2]
|
|
|
|
|
|
def function(source, signature):
|
|
start = source.rindex(signature)
|
|
return source[start:source.index('\n}\n', start) + 3]
|
|
|
|
|
|
class EffectsSourceResolutionTest(unittest.TestCase):
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
compiler = shutil.which(os.environ.get('CC', 'cc'))
|
|
if not compiler:
|
|
raise unittest.SkipTest('A host C compiler is required')
|
|
source = (REPO / 'app/src/main/cpp/waylandcomp/src/vk_present.c').read_text()
|
|
header = (REPO / 'app/src/main/cpp/waylandcomp/src/vk_present.h').read_text()
|
|
effects = (REPO / 'app/src/main/cpp/waylandcomp/src/effects_chain.c').read_text()
|
|
functions = '\n'.join(function(source, signature) for signature in (
|
|
'static int map_draw(', 'static int draw_to_blit(',
|
|
'static int draw_to_scene_blit(', 'static int effects_source_draw(',
|
|
'static void effects_source_note(',
|
|
'static void effects_overlay_draws(',
|
|
'static int render_impl(', 'static int pass_begin_impl('))
|
|
functions = (re.search(r'static int spatial_mode\([^\n]+', effects).group() + '\n' +
|
|
function(effects, 'int vkp_effects_upscaling(') +
|
|
function(effects, 'int vkp_effects_postprocessing(') + functions)
|
|
image = source[source.index('struct vkp_image {'):source.index('\n};', source.index('struct vkp_image {')) + 3]
|
|
draw = header[header.index('struct vkp_draw {'):header.index('\n};', header.index('struct vkp_draw {')) + 3]
|
|
hdr = header[header.index('struct vkp_hdr_frame {'):header.index('\n};', header.index('struct vkp_hdr_frame {')) + 3]
|
|
constants = sorted(set(re.findall(r'\bVK_[A-Z0-9_]+\b', functions + image)) -
|
|
{'VK_FORMAT_R8G8B8A8_UNORM'})
|
|
definitions = '\n'.join(f'#define {name} {0 if name in ("VK_SUCCESS", "VK_NULL_HANDLE") else i + 1}'
|
|
for i, name in enumerate(constants))
|
|
code = TYPES + definitions + '\n' + image + '\n' + draw + '\n' + hdr + STUBS + functions + CASES
|
|
cls.temp = tempfile.TemporaryDirectory(prefix='droiddeck-effects-resolution-')
|
|
cls.addClassCleanup(cls.temp.cleanup)
|
|
folder = Path(cls.temp.name)
|
|
(folder / 'resolution.c').write_text(code)
|
|
cls.binary = folder / 'resolution'
|
|
build = subprocess.run([compiler, '-std=c11', '-Wall', '-Wextra',
|
|
'-Wno-unused-parameter', '-Wno-unused-function',
|
|
str(folder / 'resolution.c'), '-o', str(cls.binary)],
|
|
capture_output=True, text=True)
|
|
if build.returncode:
|
|
raise RuntimeError(build.stderr)
|
|
|
|
def check_case(self, case):
|
|
result = subprocess.run([str(self.binary), case], capture_output=True, text=True, timeout=10)
|
|
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
|
|
self.assertEqual(result.stdout.strip(), 'PASS')
|
|
|
|
def test_low_resolution_viewport_is_copied_at_native_buffer_size(self):
|
|
self.check_case('helper')
|
|
|
|
def test_a_source_within_a_pixel_of_the_buffer_is_still_upscaled(self):
|
|
self.check_case('near-full')
|
|
|
|
def test_ineligible_compositions_keep_the_logical_scene(self):
|
|
self.check_case('rejections')
|
|
|
|
def test_swapchain_path_uses_the_game_buffer_as_effects_input(self):
|
|
self.check_case('render')
|
|
|
|
def test_offscreen_pass_uses_the_game_buffer_as_effects_input(self):
|
|
self.check_case('pass')
|
|
|
|
def test_hdr_and_non_upscaling_effects_keep_the_logical_scene(self):
|
|
self.check_case('gates')
|
|
|
|
def test_each_scaling_mode_selects_the_correct_input(self):
|
|
self.check_case('modes')
|
|
|
|
def test_fullscreen_source_occludes_unrelated_background_draws(self):
|
|
self.check_case('occlusion')
|
|
|
|
def test_foreground_overlays_keep_their_geometry_after_upscaling(self):
|
|
self.check_case('overlay')
|
|
|
|
def test_failed_intermediate_allocation_draws_all_original_layers(self):
|
|
self.check_case('allocation')
|
|
|
|
def test_effects_failure_maps_overlays_to_the_returned_image_size(self):
|
|
self.check_case('effect-failure')
|
|
|
|
def test_offscreen_pass_acquires_foreground_sources_and_restores_result_layout(self):
|
|
self.check_case('overlay-barriers')
|
|
|
|
def test_postprocessing_keeps_foreground_inside_the_effect_chain(self):
|
|
self.check_case('postprocessing')
|
|
|
|
|
|
TYPES = r'''
|
|
#include <assert.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
typedef int VkImage, VkDeviceMemory, VkFormat, VkCommandBuffer, VkResult, VkFilter;
|
|
typedef int VkImageLayout, VkPipelineStageFlags, VkImageUsageFlags;
|
|
typedef uint64_t VkDeviceSize;
|
|
typedef struct { unsigned width, height; } VkExtent2D;
|
|
typedef struct { int x, y, z; } VkOffset3D;
|
|
typedef struct { unsigned aspectMask, mipLevel, baseArrayLayer, layerCount; } VkImageSubresourceLayers;
|
|
typedef struct { unsigned aspectMask, baseMipLevel, levelCount, baseArrayLayer, layerCount; } VkImageSubresourceRange;
|
|
typedef struct {
|
|
VkImageSubresourceLayers srcSubresource;
|
|
VkOffset3D srcOffsets[2];
|
|
VkImageSubresourceLayers dstSubresource;
|
|
VkOffset3D dstOffsets[2];
|
|
} VkImageBlit;
|
|
typedef struct { int sType, flags; } VkCommandBufferBeginInfo;
|
|
typedef struct {
|
|
int sType, oldLayout, newLayout, srcQueueFamilyIndex, dstQueueFamilyIndex;
|
|
VkImage image;
|
|
VkImageSubresourceRange subresourceRange;
|
|
unsigned srcAccessMask, dstAccessMask;
|
|
} VkImageMemoryBarrier;
|
|
typedef struct { int sType; unsigned srcAccessMask, dstAccessMask; } VkMemoryBarrier;
|
|
typedef union { float float32[4]; } VkClearColorValue;
|
|
typedef struct {
|
|
int sType;
|
|
unsigned waitSemaphoreCount, commandBufferCount, signalSemaphoreCount;
|
|
const int *pWaitSemaphores, *pWaitDstStageMask, *pCommandBuffers, *pSignalSemaphores;
|
|
} VkSubmitInfo;
|
|
typedef struct {
|
|
int sType;
|
|
unsigned waitSemaphoreCount, swapchainCount;
|
|
const int *pWaitSemaphores, *pSwapchains;
|
|
const unsigned *pImageIndices;
|
|
} VkPresentInfoKHR;
|
|
#define SCENE_FMT 100
|
|
#define HDR_FMT 101
|
|
#define FG_HDR_FMT 102
|
|
#define VK_FORMAT_R8G8B8A8_UNORM SCENE_FMT
|
|
#define VKP_FG_MAX_GENERATIONS 2
|
|
#define HDRC_OUT_PQ 1
|
|
#define HDRC_OUT_SDR 2
|
|
#define droiddeck_log(...) ((void)0)
|
|
#define LOGE(...) ((void)0)
|
|
struct droiddeck_color { uint32_t identity; };
|
|
'''
|
|
|
|
|
|
STUBS = r'''
|
|
static int noop(int handle, ...) { return VK_SUCCESS; }
|
|
static VkImageMemoryBarrier recorded_barriers[64];
|
|
static int barrier_count;
|
|
static int note_barriers(int cmd, int src_stage, int dst_stage, int flags,
|
|
unsigned memory_count, const VkMemoryBarrier *memory,
|
|
unsigned buffer_count, const void *buffers,
|
|
unsigned image_count, const VkImageMemoryBarrier *images) {
|
|
for (unsigned i = 0; i < image_count; i++) {
|
|
assert(barrier_count < 64);
|
|
recorded_barriers[barrier_count++] = images[i];
|
|
}
|
|
return VK_SUCCESS;
|
|
}
|
|
static struct {
|
|
int (*ResetCommandBuffer)(int, ...), (*BeginCommandBuffer)(int, ...);
|
|
int (*CmdPipelineBarrier)(int, int, int, int, unsigned, const VkMemoryBarrier *,
|
|
unsigned, const void *, unsigned, const VkImageMemoryBarrier *);
|
|
int (*CmdClearColorImage)(int, ...);
|
|
int (*EndCommandBuffer)(int, ...), (*ResetFences)(int, ...);
|
|
int (*QueueSubmit)(int, ...), (*QueuePresentKHR)(int, ...);
|
|
} g_vk = {noop, noop, note_barriers, noop, noop, noop, noop, noop};
|
|
static int g_dev_state, g_window = 1, g_swapchain, g_swap_extra;
|
|
static int g_colorspace_ext, g_swap_hdr_unavailable, g_swap_is_hdr, g_swap_hdr_want;
|
|
static int g_plain_frame, g_first_frame_done, g_base_black;
|
|
static int g_qfam, g_queue, g_dev, g_fence, g_swap_fmt = SCENE_FMT;
|
|
static int g_cmds[3], g_acqs[3], g_rnds[3], images[1] = {2}, *g_images = images;
|
|
static uint32_t g_swap_md_identity;
|
|
static VkExtent2D g_extent = {1920, 1080};
|
|
static struct { float kx, ky, off_x, off_y; int rx, ry, rw, rh; } g_map;
|
|
static struct { VkImage img; int w, h; } g_scene;
|
|
struct vkp_img_slot { VkImage img; int w, h; };
|
|
static struct vkp_img_slot g_hdrscene, g_fgscene;
|
|
static struct { int active, rw, rh; VkImage result; } g_pass;
|
|
static struct { int64_t present_ns, present_max_ns; unsigned presents; } g_perf;
|
|
static int effects_active = 1, framegen, cardboard;
|
|
static struct { int scaling; } g_cur = {4};
|
|
static int copy_count, effects_count, screen_count, hdr_count, fail_scene, fail_effects, postprocessing;
|
|
static int copied_w, copied_h, target_w, target_h;
|
|
static int effects_w, effects_h, output_w, output_h, logical_w, logical_h;
|
|
static VkImageBlit copied;
|
|
static struct vkp_draw recorded_draws[16];
|
|
static VkImageBlit recorded_blits[16];
|
|
static VkImage recorded_targets[16];
|
|
static int recorded_after_effects[16];
|
|
static void vkp_apply_window_request(void) {}
|
|
static void sc_layer_hide_base(void) {}
|
|
static void check_surface_changed(void) {}
|
|
static int dev_init(void) { return 0; }
|
|
static int vkp_framegen_extra_images(void) { return 0; }
|
|
static void destroy_swapchain(void) {}
|
|
static int acquire_image(int k, int wait, uint32_t *image, VkResult *result) {
|
|
*image = 0; *result = VK_SUCCESS; return 0;
|
|
}
|
|
static void update_map(int w, int h) {
|
|
g_map.kx = (float)g_extent.width / w; g_map.ky = (float)g_extent.height / h;
|
|
g_map.rw = g_extent.width; g_map.rh = g_extent.height;
|
|
}
|
|
static int vkp_update_map(int w, int h) { update_map(w, h); return 0; }
|
|
static void send_hdr_metadata(const struct droiddeck_color *color) {}
|
|
static int vkp_effects_active(void) { return effects_active; }
|
|
static int vkp_effects_sync(void) { return 0; }
|
|
static int effect_count(const void *settings) { return postprocessing; }
|
|
static int vkp_framegen_active(void) { return framegen; }
|
|
static int vkp_cardboard_active(void) { return cardboard; }
|
|
static void vkp_cardboard_begin_frame(void) {}
|
|
static int vkp_framegen_format_ok(VkFormat format) { return 0; }
|
|
static int ensure_img(struct vkp_img_slot *slot, int w, int h, int format, int usage, const char *name) {
|
|
slot->img = 3; slot->w = w; slot->h = h; return 0;
|
|
}
|
|
static void vk_format_short(int format, char *out, size_t size) { *out = 0; }
|
|
static int ensure_scene_image(int w, int h) {
|
|
if (fail_scene) return -1;
|
|
g_scene.img = 1; g_scene.w = w; g_scene.h = h; return 0;
|
|
}
|
|
static VkFilter vkp_effects_blit_filter(void) { return 0; }
|
|
static void blendp_begin_frame(void) {}
|
|
static int record_bars_clear(int cmd, int target, const VkImageBlit *blit) { return 1; }
|
|
static int compose_hdr(int cmd, const struct vkp_draw *draws, int n, const struct vkp_hdr_frame *hdr,
|
|
int w, int h, int target, int format, int output) {
|
|
hdr_count++; return 0;
|
|
}
|
|
static void record_draw(int cmd, const struct vkp_draw *draw, const VkImageBlit *blit, int target,
|
|
int format, int w, int h, int filter) {
|
|
assert(copy_count < 16);
|
|
recorded_draws[copy_count] = *draw; recorded_blits[copy_count] = *blit;
|
|
recorded_targets[copy_count] = target; recorded_after_effects[copy_count] = effects_count > 0;
|
|
copy_count++; copied = *blit;
|
|
copied_w = draw->dw; copied_h = draw->dh; target_w = w; target_h = h;
|
|
}
|
|
static void vkp_effects_set_formats(int input, int output) {}
|
|
static VkImage vkp_effects_run(int cmd, int source, int w, int h, int ow, int oh, int *rw, int *rh) {
|
|
effects_count++; effects_w = w; effects_h = h; output_w = ow; output_h = oh;
|
|
if (!hdr_count) assert(g_scene.w == w && g_scene.h == h);
|
|
if (fail_effects) { *rw = w; *rh = h; return source; }
|
|
*rw = ow; *rh = oh; return 4;
|
|
}
|
|
static int vkp_framegen_run(int cmd, int result, int depth, int w, int h, int format, int *generated) { return 0; }
|
|
static void record_screen_blit(int cmd, int result, int w, int h, int sw, int sh, int image, int filter, int cb) {
|
|
screen_count++; logical_w = sw; logical_h = sh;
|
|
}
|
|
static void device_lost(const char *reason) {}
|
|
static int64_t perf_now(void) { return 0; }
|
|
static void perf_add(int64_t *sum, int64_t *max, unsigned *count, int64_t duration) {}
|
|
static VkResult timed_wait(int fence, uint64_t timeout) { return VK_SUCCESS; }
|
|
static void vkp_framegen_presented(int frames) {}
|
|
static const char *vk_result_name(int result) { return "success"; }
|
|
static void droiddeck_on_first_frame(void) {}
|
|
static void vkp_pass_abort(void) { g_pass.active = 0; }
|
|
'''
|
|
|
|
|
|
CASES = r'''
|
|
static struct vkp_image image = {.image = 10, .w = 1280, .h = 720, .sampled = 1};
|
|
static struct vkp_draw fullscreen(void) {
|
|
return (struct vkp_draw){.img = &image, .sw = image.w, .sh = image.h, .dw = 1920, .dh = 1080};
|
|
}
|
|
static void reset(void) {
|
|
copy_count = effects_count = screen_count = hdr_count = barrier_count = 0;
|
|
effects_active = 1; g_cur.scaling = 4;
|
|
framegen = cardboard = fail_scene = fail_effects = postprocessing = g_plain_frame = 0;
|
|
g_pass.active = 0; g_extent = (VkExtent2D){1920, 1080};
|
|
image.w = 1280; image.h = 720;
|
|
}
|
|
static void check_helper(void) {
|
|
struct vkp_draw original = fullscreen(), before = original, selected;
|
|
struct vkp_image image_before = image;
|
|
assert(effects_source_draw(1920, 1080, &original, 1, &selected));
|
|
assert(memcmp(&original, &before, sizeof(original)) == 0);
|
|
assert(memcmp(&image, &image_before, sizeof(image)) == 0);
|
|
assert(selected.img == original.img && selected.sw == 1280 && selected.sh == 720);
|
|
assert(selected.dw == 1280 && selected.dh == 720);
|
|
VkImageBlit blit;
|
|
assert(draw_to_scene_blit(&selected, 1280, 720, &blit));
|
|
assert(blit.srcOffsets[0].x == 0 && blit.srcOffsets[0].y == 0);
|
|
assert(blit.dstOffsets[0].x == 0 && blit.dstOffsets[0].y == 0);
|
|
assert(blit.srcOffsets[1].x == 1280 && blit.srcOffsets[1].y == 720);
|
|
assert(blit.dstOffsets[1].x == 1280 && blit.dstOffsets[1].y == 720);
|
|
image.w = 1920; image.h = 720;
|
|
original = fullscreen();
|
|
assert(effects_source_draw(1920, 1080, &original, 1, &selected));
|
|
assert(selected.dw == 1920 && selected.dh == 720);
|
|
}
|
|
static void rejected(struct vkp_draw draw, int n) {
|
|
struct vkp_draw selected = {0}, before = selected;
|
|
assert(!effects_source_draw(1920, 1080, &draw, n, &selected));
|
|
assert(memcmp(&selected, &before, sizeof(selected)) == 0);
|
|
}
|
|
static void check_rejections(void) {
|
|
struct vkp_draw draw = fullscreen(), unused;
|
|
assert(!effects_source_draw(1920, 1080, NULL, 1, &unused));
|
|
rejected(draw, 0); rejected(draw, -1);
|
|
draw.img = NULL; rejected(draw, 1);
|
|
draw = fullscreen(); draw.blend = 1; rejected(draw, 1);
|
|
draw = fullscreen(); draw.dx = 1; rejected(draw, 1);
|
|
draw = fullscreen(); draw.dy = -1; rejected(draw, 1);
|
|
draw = fullscreen(); draw.dw--; rejected(draw, 1);
|
|
draw = fullscreen(); draw.dh++; rejected(draw, 1);
|
|
draw = fullscreen(); draw.sx = 1; rejected(draw, 1);
|
|
draw = fullscreen(); draw.sy = 0.5f; rejected(draw, 1);
|
|
draw = fullscreen(); draw.sw--; rejected(draw, 1);
|
|
draw = fullscreen(); draw.sh--; rejected(draw, 1);
|
|
const int sizes[][2] = {{1920, 1080}, {1921, 720}, {1280, 1081}, {0, 720}, {1280, 0}, {-1, 720}};
|
|
for (unsigned i = 0; i < sizeof(sizes) / sizeof(sizes[0]); i++) {
|
|
image.w = sizes[i][0]; image.h = sizes[i][1]; rejected(fullscreen(), 1);
|
|
}
|
|
reset();
|
|
struct vkp_image native = {.image = 13, .w = 1920, .h = 1080};
|
|
struct vkp_draw layers[] = {fullscreen(), fullscreen()};
|
|
layers[1].img = &native; layers[1].sw = 1920; layers[1].sh = 1080;
|
|
assert(!effects_source_draw(1920, 1080, layers, 2, &unused));
|
|
layers[1] = fullscreen(); layers[1].sx = 1; layers[1].sw--;
|
|
assert(!effects_source_draw(1920, 1080, layers, 2, &unused));
|
|
struct vkp_image pixel = {.image = 14, .w = 1, .h = 1};
|
|
layers[0] = fullscreen(); layers[0].img = &pixel; layers[0].sw = 1; layers[0].sh = 1;
|
|
layers[1] = fullscreen(); layers[1].dx = 240; layers[1].dw = 1440;
|
|
assert(!effects_source_draw(1920, 1080, layers, 2, &unused));
|
|
layers[1] = fullscreen(); layers[1].dx = 320; layers[1].dy = 180; layers[1].dw = 1280; layers[1].dh = 720;
|
|
assert(!effects_source_draw(1920, 1080, layers, 2, &unused));
|
|
layers[0] = fullscreen();
|
|
layers[1] = fullscreen(); layers[1].dx = 10; layers[1].dy = 10; layers[1].dw = 24; layers[1].dh = 24;
|
|
assert(effects_source_draw(1920, 1080, layers, 2, &unused) == 1);
|
|
}
|
|
static void check_near_full_source(void) {
|
|
struct vkp_draw draw = fullscreen(), selected;
|
|
draw.sw = 1279.5f;
|
|
assert(effects_source_draw(1920, 1080, &draw, 1, &selected));
|
|
assert(selected.dw == 1280 && selected.dh == 720 && selected.sw == 1279.5f);
|
|
draw = fullscreen(); draw.sh = 720.5f;
|
|
assert(effects_source_draw(1920, 1080, &draw, 1, &selected));
|
|
}
|
|
static void run_path(int offscreen, const struct vkp_draw *draw, int n, const struct vkp_hdr_frame *hdr) {
|
|
struct vkp_draw before[4];
|
|
assert(n > 0 && n <= 4);
|
|
memcpy(before, draw, n * sizeof(*draw));
|
|
int rw = 0, rh = 0;
|
|
assert((offscreen ? pass_begin_impl(1920, 1080, draw, n, hdr, &rw, &rh)
|
|
: render_impl(1920, 1080, draw, n, hdr, NULL)) == 0);
|
|
assert(memcmp(draw, before, n * sizeof(*draw)) == 0);
|
|
if (offscreen) assert(rw == (fail_effects ? effects_w : (int)g_extent.width) &&
|
|
rh == (fail_effects ? effects_h : (int)g_extent.height));
|
|
}
|
|
static void check_input(int w, int h) {
|
|
assert(effects_count == 1 && effects_w == w && effects_h == h);
|
|
assert(output_w == (int)g_extent.width && output_h == (int)g_extent.height);
|
|
}
|
|
static void check_path(int offscreen) {
|
|
struct vkp_draw draw = fullscreen();
|
|
run_path(offscreen, &draw, 1, NULL);
|
|
check_input(1280, 720);
|
|
assert(copy_count == 1 && copied_w == 1280 && copied_h == 720);
|
|
assert(target_w == 1280 && target_h == 720);
|
|
assert(copied.srcOffsets[1].x == copied.dstOffsets[1].x);
|
|
assert(copied.srcOffsets[1].y == copied.dstOffsets[1].y);
|
|
if (!offscreen) assert(screen_count == 1 && logical_w == 1920 && logical_h == 1080);
|
|
reset(); image.w = 1920; image.h = 1080; draw = fullscreen();
|
|
run_path(offscreen, &draw, 1, NULL); check_input(1920, 1080);
|
|
reset(); g_extent = (VkExtent2D){2560, 1440}; draw = fullscreen();
|
|
run_path(offscreen, &draw, 1, NULL); check_input(1280, 720);
|
|
reset(); draw = fullscreen(); draw.dw = 1280; draw.dh = 720;
|
|
int rw, rh;
|
|
assert((offscreen ? pass_begin_impl(1280, 720, &draw, 1, NULL, &rw, &rh)
|
|
: render_impl(1280, 720, &draw, 1, NULL, NULL)) == 0);
|
|
check_input(1280, 720);
|
|
}
|
|
static void check_gates(void) {
|
|
for (int offscreen = 0; offscreen < 2; offscreen++) {
|
|
reset(); g_cur.scaling = 0;
|
|
struct vkp_draw draw = fullscreen();
|
|
run_path(offscreen, &draw, 1, NULL); check_input(1920, 1080);
|
|
assert(target_w == 1920 && target_h == 1080);
|
|
reset();
|
|
struct vkp_hdr_frame hdr = {0};
|
|
run_path(offscreen, &draw, 1, &hdr); check_input(1920, 1080);
|
|
assert(hdr_count == 1 && copy_count == 0);
|
|
reset();
|
|
struct vkp_draw desktop[] = {fullscreen(), fullscreen()};
|
|
desktop[0].dw = 960;
|
|
desktop[1].dx = 40; desktop[1].dy = 30; desktop[1].dw = 640; desktop[1].dh = 480;
|
|
run_path(offscreen, desktop, 2, NULL); check_input(1920, 1080);
|
|
reset(); draw = fullscreen(); draw.blend = 1;
|
|
run_path(offscreen, &draw, 1, NULL); check_input(1920, 1080);
|
|
}
|
|
reset(); effects_active = 0; framegen = 1;
|
|
struct vkp_draw draw = fullscreen();
|
|
run_path(0, &draw, 1, NULL);
|
|
assert(effects_count == 0 && target_w == 1920 && target_h == 1080);
|
|
reset(); fail_scene = 1;
|
|
run_path(0, &draw, 1, NULL);
|
|
assert(effects_count == 0 && copied.dstOffsets[1].x == 1920 && copied.dstOffsets[1].y == 1080);
|
|
}
|
|
static void check_modes(void) {
|
|
for (int mode = 0; mode <= 8; mode++) {
|
|
for (int offscreen = 0; offscreen < 2; offscreen++) {
|
|
reset(); g_cur.scaling = mode;
|
|
int upscale = mode == 3 || mode == 4 || mode == 5 || mode == 7 || mode == 8;
|
|
assert(vkp_effects_upscaling() == upscale);
|
|
struct vkp_draw draw = fullscreen();
|
|
run_path(offscreen, &draw, 1, NULL);
|
|
check_input(upscale ? 1280 : 1920, upscale ? 720 : 1080);
|
|
}
|
|
}
|
|
}
|
|
static struct vkp_image background = {.image = 11, .w = 320, .h = 240, .sampled = 1};
|
|
static struct vkp_image cursor = {.image = 12, .w = 24, .h = 24, .dmabuf = 1, .sampled = 1};
|
|
static struct vkp_draw background_draw(void) {
|
|
return (struct vkp_draw){.img = &background, .sw = 320, .sh = 240,
|
|
.dx = 40, .dy = 30, .dw = 640, .dh = 480, .blend = 1};
|
|
}
|
|
static struct vkp_draw cursor_draw(void) {
|
|
return (struct vkp_draw){.img = &cursor, .sw = 24, .sh = 24,
|
|
.dx = 480, .dy = 270, .dw = 24, .dh = 24, .blend = 1};
|
|
}
|
|
static void check_occlusion(void) {
|
|
struct vkp_draw layers[] = {background_draw(), fullscreen()}, selected;
|
|
struct vkp_draw before[2];
|
|
memcpy(before, layers, sizeof(layers));
|
|
assert(effects_source_draw(1920, 1080, layers, 2, &selected) == 2);
|
|
assert(selected.img == &image && selected.dw == 1280 && selected.dh == 720);
|
|
assert(memcmp(layers, before, sizeof(layers)) == 0);
|
|
for (int offscreen = 0; offscreen < 2; offscreen++) {
|
|
reset();
|
|
run_path(offscreen, layers, 2, NULL); check_input(1280, 720);
|
|
assert(copy_count == 1 && recorded_draws[0].img == &image);
|
|
assert(recorded_draws[0].dw == 1280 && recorded_draws[0].dh == 720);
|
|
assert(target_w == 1280 && target_h == 720);
|
|
assert(copied.srcOffsets[1].x == 1280 && copied.dstOffsets[1].x == 1280);
|
|
assert(copied.srcOffsets[1].y == 720 && copied.dstOffsets[1].y == 720);
|
|
if (!offscreen) assert(logical_w == 1920 && logical_h == 1080);
|
|
reset(); g_cur.scaling = 6;
|
|
run_path(offscreen, layers, 2, NULL); check_input(1920, 1080);
|
|
assert(copy_count == 2);
|
|
reset();
|
|
struct vkp_hdr_frame hdr = {0};
|
|
run_path(offscreen, layers, 2, &hdr); check_input(1920, 1080);
|
|
assert(hdr_count == 1 && copy_count == 0);
|
|
}
|
|
}
|
|
static void check_overlay(void) {
|
|
for (int offscreen = 0; offscreen < 2; offscreen++) {
|
|
for (int larger_output = 0; larger_output <= 1; larger_output++) {
|
|
reset();
|
|
if (larger_output) g_extent = (VkExtent2D){2560, 1440};
|
|
struct vkp_draw layers[] = {background_draw(), fullscreen(), cursor_draw()}, selected;
|
|
assert(effects_source_draw(1920, 1080, layers, 3, &selected) == 2);
|
|
run_path(offscreen, layers, 3, NULL); check_input(1280, 720);
|
|
assert(copy_count == 2 && recorded_draws[0].img == &image);
|
|
assert(!recorded_after_effects[0] && recorded_targets[0] == g_scene.img);
|
|
assert(recorded_draws[1].img == &cursor && recorded_draws[1].blend);
|
|
assert(recorded_after_effects[1] && recorded_targets[1] == 4);
|
|
assert(recorded_blits[1].dstOffsets[0].x == (larger_output ? 640 : 480));
|
|
assert(recorded_blits[1].dstOffsets[0].y == (larger_output ? 360 : 270));
|
|
assert(recorded_blits[1].dstOffsets[1].x - recorded_blits[1].dstOffsets[0].x ==
|
|
(larger_output ? 32 : 24));
|
|
assert(recorded_blits[1].dstOffsets[1].y - recorded_blits[1].dstOffsets[0].y ==
|
|
(larger_output ? 32 : 24));
|
|
}
|
|
for (int blend = 0; blend <= 1; blend++) {
|
|
reset();
|
|
struct vkp_draw layers[] = {background_draw(), fullscreen(), background_draw(), cursor_draw()};
|
|
layers[2].blend = blend;
|
|
layers[3].sx = 4; layers[3].sy = 2; layers[3].sw = 12; layers[3].sh = 18;
|
|
run_path(offscreen, layers, 4, NULL); check_input(1280, 720);
|
|
assert(copy_count == 3 && recorded_draws[0].img == &image);
|
|
assert(recorded_draws[1].img == &background);
|
|
assert(recorded_draws[1].blend == blend && recorded_after_effects[1]);
|
|
assert(recorded_draws[2].img == &cursor && recorded_after_effects[2]);
|
|
assert(recorded_blits[1].dstOffsets[0].x == 40);
|
|
assert(recorded_blits[1].dstOffsets[0].y == 30);
|
|
assert(recorded_blits[2].srcOffsets[0].x == 4 && recorded_blits[2].srcOffsets[0].y == 2);
|
|
assert(recorded_blits[2].srcOffsets[1].x == 16 && recorded_blits[2].srcOffsets[1].y == 20);
|
|
assert(recorded_targets[1] == 4 && recorded_targets[2] == 4);
|
|
assert(target_w == 1920 && target_h == 1080);
|
|
}
|
|
}
|
|
}
|
|
static void check_effect_failure(void) {
|
|
for (int offscreen = 0; offscreen < 2; offscreen++) {
|
|
reset(); fail_effects = 1;
|
|
struct vkp_draw layers[] = {background_draw(), fullscreen(), cursor_draw()};
|
|
run_path(offscreen, layers, 3, NULL); check_input(1280, 720);
|
|
assert(copy_count == 2 && recorded_targets[1] == g_scene.img && recorded_after_effects[1]);
|
|
assert(target_w == 1280 && target_h == 720);
|
|
assert(recorded_blits[1].dstOffsets[0].x == 320 && recorded_blits[1].dstOffsets[0].y == 180);
|
|
assert(recorded_blits[1].dstOffsets[1].x == 336 && recorded_blits[1].dstOffsets[1].y == 196);
|
|
}
|
|
}
|
|
static int find_barrier(VkImage image, int from, int to) {
|
|
for (int i = 0; i < barrier_count; i++)
|
|
if (recorded_barriers[i].image == image && recorded_barriers[i].oldLayout == from &&
|
|
recorded_barriers[i].newLayout == to) return i;
|
|
return -1;
|
|
}
|
|
static void check_overlay_barriers(void) {
|
|
for (int offscreen = 0; offscreen < 2; offscreen++) {
|
|
reset();
|
|
struct vkp_draw layers[] = {background_draw(), fullscreen(), cursor_draw()};
|
|
run_path(offscreen, layers, 3, NULL); check_input(1280, 720);
|
|
int acquired = find_barrier(cursor.image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
|
|
assert(acquired >= 0 && recorded_barriers[acquired].srcQueueFamilyIndex == VK_QUEUE_FAMILY_FOREIGN_EXT);
|
|
int writable = find_barrier(4, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
|
int readable = find_barrier(4, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
|
|
assert(writable > acquired && readable > writable);
|
|
}
|
|
}
|
|
static void check_postprocessing(void) {
|
|
for (int offscreen = 0; offscreen < 2; offscreen++) {
|
|
reset(); postprocessing = 1;
|
|
struct vkp_draw layers[] = {background_draw(), fullscreen(), cursor_draw()}, selected;
|
|
assert(vkp_effects_postprocessing());
|
|
assert(effects_source_draw(1920, 1080, layers, 2, &selected) == 2);
|
|
assert(!effects_source_draw(1920, 1080, layers, 3, &selected));
|
|
run_path(offscreen, layers, 3, NULL); check_input(1920, 1080);
|
|
assert(copy_count == 3);
|
|
for (int i = 0; i < copy_count; i++)
|
|
assert(!recorded_after_effects[i] && recorded_targets[i] == g_scene.img);
|
|
}
|
|
}
|
|
static void check_allocation(void) {
|
|
fail_scene = 1;
|
|
struct vkp_draw layers[] = {background_draw(), fullscreen()};
|
|
run_path(0, layers, 2, NULL);
|
|
assert(effects_count == 0 && copy_count == 2);
|
|
assert(recorded_draws[0].img == &background && recorded_draws[1].img == &image);
|
|
assert(recorded_draws[0].dw == 640 && recorded_draws[0].dh == 480);
|
|
assert(recorded_draws[1].dw == 1920 && recorded_draws[1].dh == 1080);
|
|
assert(recorded_blits[0].dstOffsets[0].x == 40 && recorded_blits[0].dstOffsets[0].y == 30);
|
|
assert(recorded_blits[1].dstOffsets[1].x == 1920 && recorded_blits[1].dstOffsets[1].y == 1080);
|
|
assert(target_w == 1920 && target_h == 1080);
|
|
}
|
|
int main(int argc, char **argv) {
|
|
assert(argc == 2); reset();
|
|
if (!strcmp(argv[1], "helper")) check_helper();
|
|
else if (!strcmp(argv[1], "near-full")) check_near_full_source();
|
|
else if (!strcmp(argv[1], "rejections")) check_rejections();
|
|
else if (!strcmp(argv[1], "render")) check_path(0);
|
|
else if (!strcmp(argv[1], "pass")) check_path(1);
|
|
else if (!strcmp(argv[1], "gates")) check_gates();
|
|
else if (!strcmp(argv[1], "modes")) check_modes();
|
|
else if (!strcmp(argv[1], "occlusion")) check_occlusion();
|
|
else if (!strcmp(argv[1], "overlay")) check_overlay();
|
|
else if (!strcmp(argv[1], "allocation")) check_allocation();
|
|
else if (!strcmp(argv[1], "effect-failure")) check_effect_failure();
|
|
else if (!strcmp(argv[1], "overlay-barriers")) check_overlay_barriers();
|
|
else if (!strcmp(argv[1], "postprocessing")) check_postprocessing();
|
|
else assert(0);
|
|
puts("PASS");
|
|
}
|
|
'''
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|