diff --git a/.rive_head b/.rive_head index b3a54860..d985c4e7 100644 --- a/.rive_head +++ b/.rive_head @@ -1 +1 @@ -f9715435dd9605d0c9636088d69ef684b819ac81 +1cc5f2b6f6cf02d15bcb3654343e4f3322a95402 diff --git a/build/premake5.lua b/build/premake5.lua index 66c7d20b..444763b7 100644 --- a/build/premake5.lua +++ b/build/premake5.lua @@ -40,7 +40,6 @@ project('rive') do kind('StaticLib') language('C++') - cppdialect('C++11') targetdir('%{cfg.system}/bin/%{cfg.buildcfg}') objdir('%{cfg.system}/obj/%{cfg.buildcfg}') includedirs({ @@ -57,6 +56,17 @@ do flags({ 'FatalCompileWarnings' }) + filter({ 'options:for_unreal' }) + do + cppdialect('C++17') + defines({ '_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR' }) + end + + filter({ 'options:not for_unreal' }) + do + cppdialect('C++11') + end + filter({ 'system:macosx' }) do buildoptions({ diff --git a/build/rive_build_config.lua b/build/rive_build_config.lua index a4a07e13..bb8ee8bb 100644 --- a/build/rive_build_config.lua +++ b/build/rive_build_config.lua @@ -102,6 +102,11 @@ newoption({ description = 'Don\'t build with link time optimizations.', }) +newoption({ + trigger = 'for_unreal', + description = 'compile for unreal engine', +}) + location(RIVE_BUILD_OUT) targetdir(RIVE_BUILD_OUT) objdir(RIVE_BUILD_OUT .. '/obj') @@ -165,39 +170,49 @@ newoption({ -- This is just to match our old windows config. Rive Native specifically sets -- static/dynamic and maybe we should do the same elsewhere. -filter({ 'system:windows', 'options:windows_runtime=default' }) +filter({ 'system:windows', 'options:windows_runtime=default', 'options:not for_unreal' }) do staticruntime('on') -- Match Skia's /MT flag for link compatibility runtime('Release') end -filter({ 'system:windows', 'options:windows_runtime=static' }) +filter({ 'system:windows', 'options:windows_runtime=static', 'options:not for_unreal' }) do staticruntime('on') -- Match Skia's /MT flag for link compatibility end -filter({ 'system:windows', 'options:windows_runtime=dynamic' }) +filter({ 'system:windows', 'options:windows_runtime=dynamic', 'options:not for_unreal' }) do staticruntime('off') end -filter({ 'system:windows', 'options:not windows_runtime=default', 'options:config=debug' }) +filter({ + 'system:windows', + 'options:not windows_runtime=default', + 'options:config=debug', + 'options:not for_unreal', +}) do runtime('Debug') end -filter({ 'system:windows', 'options:not windows_runtime=default', 'options:config=release' }) +filter({ + 'system:windows', + 'options:not windows_runtime=default', + 'options:config=release', + 'options:not for_unreal', +}) do runtime('Release') end -filter({ 'system:windows', 'options:windows_runtime=dynamic_debug' }) +filter({ 'system:windows', 'options:windows_runtime=dynamic_debug', 'options:not for_unreal' }) do staticruntime('off') runtime('Debug') end -filter({ 'system:windows', 'options:windows_runtime=dynamic_release' }) +filter({ 'system:windows', 'options:windows_runtime=dynamic_release', 'options:not for_unreal' }) do staticruntime('off') runtime('Release') @@ -209,6 +224,12 @@ do defines({ '_USE_MATH_DEFINES', 'NOMINMAX' }) end +filter({ 'system:windows', 'options:for_unreal' }) +do + staticruntime('off') + runtime('Release') +end + filter({ 'system:windows', 'options:toolset=clang' }) do buildoptions({ @@ -361,31 +382,62 @@ if _OPTIONS['os'] == 'android' then '-static-libstdc++', }) - filter('options:arch=x86') + filter({ 'options:arch=x86', 'options:not for_unreal' }) do architecture('x86') buildoptions({ '--target=i686-none-linux-android21' }) linkoptions({ '--target=i686-none-linux-android21' }) end - filter('options:arch=x64') + + filter({ 'options:arch=x86', 'options:for_unreal' }) + do + architecture('x86') + buildoptions({ '--target=i686-none-linux-android31' }) + linkoptions({ '--target=i686-none-linux-android31' }) + end + + filter({ 'options:arch=x64', 'options:not for_unreal' }) do architecture('x64') buildoptions({ '--target=x86_64-none-linux-android21' }) linkoptions({ '--target=x86_64-none-linux-android21' }) end - filter('options:arch=arm') + + filter({ 'options:arch=x64', 'options:for_unreal' }) + do + architecture('x64') + buildoptions({ '--target=x86_64-none-linux-androi31' }) + linkoptions({ '--target=x86_64-none-linux-android31' }) + end + + filter({ 'options:arch=arm', 'options:not for_unreal' }) do architecture('arm') buildoptions({ '--target=armv7a-none-linux-android21' }) linkoptions({ '--target=armv7a-none-linux-android21' }) end - filter('options:arch=arm64') + + filter({ 'options:arch=arm', 'options:for_unreal' }) + do + architecture('arm') + buildoptions({ '--target=armv7a-none-linux-android31' }) + linkoptions({ '--target=armv7a-none-linux-android31' }) + end + + filter({ 'options:arch=arm64', 'options:not for_unreal' }) do architecture('arm64') buildoptions({ '--target=aarch64-none-linux-android21' }) linkoptions({ '--target=aarch64-none-linux-android21' }) end + filter({ 'options:arch=arm64', 'options:for_unreal' }) + do + architecture('arm64') + buildoptions({ '--target=aarch64-none-linux-android31' }) + linkoptions({ '--target=aarch64-none-linux-android31' }) + end + filter({}) end diff --git a/build/setup_compiler.lua b/build/setup_compiler.lua index 88f54630..3931fd09 100644 --- a/build/setup_compiler.lua +++ b/build/setup_compiler.lua @@ -209,30 +209,61 @@ if _OPTIONS['os'] == 'android' then }) pic('on') -- Position-independent code is required for NDK libraries. - filter('options:arch=x86') + filter({ 'options:arch=x86', 'options:not for_unreal' }) do architecture('x86') buildoptions({ '--target=i686-none-linux-android21' }) linkoptions({ '--target=i686-none-linux-android21' }) end - filter('options:arch=x64') + + filter({ 'options:arch=x86', 'options:for_unreal' }) + do + architecture('x86') + buildoptions({ '--target=i686-none-linux-android31' }) + linkoptions({ '--target=i686-none-linux-android31' }) + end + + filter({ 'options:arch=x64', 'options:not for_unreal' }) do architecture('x64') buildoptions({ '--target=x86_64-none-linux-android21' }) linkoptions({ '--target=x86_64-none-linux-android21' }) end - filter('options:arch=arm') + + filter({ 'options:arch=x64', 'options:for_unreal' }) + do + architecture('x64') + buildoptions({ '--target=x86_64-none-linux-androi31' }) + linkoptions({ '--target=x86_64-none-linux-android31' }) + end + + filter({ 'options:arch=arm', 'options:not for_unreal' }) do architecture('arm') buildoptions({ '--target=armv7a-none-linux-android21' }) linkoptions({ '--target=armv7a-none-linux-android21' }) end - filter('options:arch=arm64') + + filter({ 'options:arch=arm', 'options:for_unreal' }) + do + architecture('arm') + buildoptions({ '--target=armv7a-none-linux-android31' }) + linkoptions({ '--target=armv7a-none-linux-android31' }) + end + + filter({ 'options:arch=arm64', 'options:not for_unreal' }) do architecture('arm64') buildoptions({ '--target=aarch64-none-linux-android21' }) linkoptions({ '--target=aarch64-none-linux-android21' }) end + + filter({ 'options:arch=arm64', 'options:for_unreal' }) + do + architecture('arm64') + buildoptions({ '--target=aarch64-none-linux-android31' }) + linkoptions({ '--target=aarch64-none-linux-android31' }) + end filter({}) end diff --git a/dependencies/premake5_harfbuzz_v2.lua b/dependencies/premake5_harfbuzz_v2.lua index c26c1d28..d92ad955 100644 --- a/dependencies/premake5_harfbuzz_v2.lua +++ b/dependencies/premake5_harfbuzz_v2.lua @@ -264,7 +264,7 @@ do }) end - filter('options:config=release') + filter({ 'options:config=release', 'options:not for_unreal' }) do optimize('Size') end diff --git a/dependencies/premake5_sheenbidi_v2.lua b/dependencies/premake5_sheenbidi_v2.lua index 147f983d..ceb47c09 100644 --- a/dependencies/premake5_sheenbidi_v2.lua +++ b/dependencies/premake5_sheenbidi_v2.lua @@ -56,6 +56,10 @@ do filter('options:config=release') do defines({ 'SB_CONFIG_UNITY' }) + end + + filter({ 'options:config=release', 'options:not for_unreal' }) + do optimize('Size') end diff --git a/premake5_v2.lua b/premake5_v2.lua index 35acb878..8ad0d211 100644 --- a/premake5_v2.lua +++ b/premake5_v2.lua @@ -37,7 +37,6 @@ dofile(path.join(dependencies, 'premake5_yoga_v2.lua')) project('rive') do kind('StaticLib') - cppdialect('C++11') includedirs({ 'include', harfbuzz .. '/src', @@ -58,7 +57,17 @@ do files({ 'src/**.cpp' }) - flags({ 'FatalCompileWarnings' }) + filter('options:not for_unreal') + do + cppdialect('C++11') + flags({ 'FatalCompileWarnings' }) + end + + filter({ 'options:for_unreal' }) + do + cppdialect('C++17') + defines({ '_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR' }) + end filter({ 'options:with_rive_text', 'options:not no-harfbuzz-renames' }) do diff --git a/renderer/premake5_pls_renderer.lua b/renderer/premake5_pls_renderer.lua index df2e77b3..b9c1dd8b 100644 --- a/renderer/premake5_pls_renderer.lua +++ b/renderer/premake5_pls_renderer.lua @@ -94,28 +94,31 @@ nproc = nproc:gsub('%s+', '') -- remove whitespace local python_ply = dependency.github('dabeaz/ply', '5c4dc94d4c6d059ec127ee1493c735963a5d2645') local pls_generated_headers = RIVE_BUILD_OUT .. '/include' local pls_shaders_absolute_dir = path.getabsolute(pls_generated_headers .. '/generated/shaders') -local makecommand -if os.host() == 'windows' then - makecommand = '"set PYTHONPATH=' .. python_ply .. '/src" && ' -else - makecommand = 'PYTHONPATH="' .. python_ply .. '/src" ' -end -makecommand = makecommand - .. 'make -C ' +local makecommand = 'make -C ' .. path.getabsolute('src/shaders') .. ' -j' .. nproc .. ' OUT=' .. pls_shaders_absolute_dir +local minify_flags = '-p ' .. python_ply .. '/src' newoption({ trigger = 'raw_shaders', description = 'don\'t rename shader variables, or remove whitespace or comments', }) if _OPTIONS['raw_shaders'] then - makecommand = makecommand .. ' FLAGS=--human-readable' + minify_flags = minify_flags .. ' --human-readable' end +local minified_extension = 'glsl' + +if _OPTIONS['for_unreal'] then + minified_extension = 'ush' +end + +minify_flags = minify_flags .. ' --minified-extension=' .. minified_extension +makecommand = makecommand .. ' FLAGS="' .. minify_flags .. '"' + if os.host() == 'macosx' then if _OPTIONS['os'] == 'ios' and _OPTIONS['variant'] == 'system' then makecommand = makecommand .. ' rive_pls_ios_metallib' diff --git a/renderer/src/shaders/color_ramp.glsl b/renderer/src/shaders/color_ramp.glsl index 1cd3c649..d86c8903 100644 --- a/renderer/src/shaders/color_ramp.glsl +++ b/renderer/src/shaders/color_ramp.glsl @@ -7,7 +7,14 @@ #ifdef @VERTEX ATTR_BLOCK_BEGIN(Attrs) +#ifdef SPLIT_UINT4_ATTRIBUTES +ATTR(0, uint, @a_spanX); +ATTR(1, uint, @a_y); +ATTR(2, uint, @a_color0); +ATTR(3, uint, @a_color1); +#else ATTR(0, uint4, @a_span); // [spanX, y, color0, color1] +#endif ATTR_BLOCK_END #endif @@ -30,8 +37,15 @@ half4 unpackColorInt(uint color) VERTEX_MAIN(@colorRampVertexMain, Attrs, attrs, _vertexID, _instanceID) { +#ifdef SPLIT_UINT4_ATTRIBUTES + ATTR_UNPACK(_instanceID, attrs, @a_spanX, uint); + ATTR_UNPACK(_instanceID, attrs, @a_y, uint); + ATTR_UNPACK(_instanceID, attrs, @a_color0, uint); + ATTR_UNPACK(_instanceID, attrs, @a_color1, uint); + uint4 @a_span = uint4(@a_spanX, @a_y, @a_color0, @a_color1); +#else ATTR_UNPACK(_instanceID, attrs, @a_span, uint4); - +#endif VARYING_INIT(v_rampColor, half4); float x = float((_vertexID & 1) == 0 ? @a_span.x & 0xffffu : @a_span.x >> 16) / 65536.; diff --git a/renderer/src/shaders/common.glsl b/renderer/src/shaders/common.glsl index 0b95b71a..a2bc875e 100644 --- a/renderer/src/shaders/common.glsl +++ b/renderer/src/shaders/common.glsl @@ -3,7 +3,6 @@ */ // Common functions shared by multiple shaders. - #define PI float(3.141592653589793238) #ifndef @USING_DEPTH_STENCIL @@ -138,6 +137,8 @@ INLINE half min_value(half4 min4) INLINE float manhattan_width(float2 x) { return abs(x.x) + abs(x.y); } #ifdef @VERTEX + +#ifndef $UNIFORM_DEFINITIONS_AUTO_GENERATED UNIFORM_BLOCK_BEGIN(FLUSH_UNIFORM_BUFFER_IDX, @FlushUniforms) float gradInverseViewportY; float tessInverseViewportY; @@ -151,6 +152,7 @@ int4 renderTargetUpdateBounds; // drawBounds, or renderTargetBounds if there is uint pathIDGranularity; // Spacing between adjacent path IDs (1 if IEEE compliant). float vertexDiscardValue; UNIFORM_BLOCK_END(uniforms) +#endif #define RENDER_TARGET_COORD_TO_CLIP_COORD(COORD) \ float4((COORD).x* uniforms.renderTargetInverseViewportX - 1., \ @@ -218,6 +220,7 @@ INLINE void set_clip_rect_plane_distances(float2x2 clipRectInverseMatrix, #endif // VERTEX #ifdef @DRAW_IMAGE +#ifndef $UNIFORM_DEFINITIONS_AUTO_GENERATED UNIFORM_BLOCK_BEGIN(IMAGE_DRAW_UNIFORM_BUFFER_IDX, @ImageDrawUniforms) float4 viewMatrix; float2 translate; @@ -232,3 +235,4 @@ uint blendMode; uint zIndex; UNIFORM_BLOCK_END(imageDrawUniforms) #endif +#endif diff --git a/renderer/src/shaders/minify.py b/renderer/src/shaders/minify.py index 4ddaee60..01ab7d6f 100644 --- a/renderer/src/shaders/minify.py +++ b/renderer/src/shaders/minify.py @@ -1,7 +1,6 @@ import argparse import glob import os -import ply.lex as lex import re import sys from collections import defaultdict @@ -36,8 +35,16 @@ parser.add_argument("-o", "--outdir", required=True, help="OUTPUT directory to store the header files") parser.add_argument("-H", "--human-readable", action='store_true', help="don't rename or strip out comments or whitespace") +parser.add_argument("-e", "--minified-extension", type=str, default="glsl", + help="use this extension for minified files") +parser.add_argument("-p", "--ply-path", required=True, type=str, help="path to ply module") + args = parser.parse_args() +sys.path.append(args.ply_path) + +import ply.lex as lex + # tokens used by PLY to run lexical analysis on our glsl files. tokens = [ "DEFINE", @@ -539,6 +546,7 @@ class Minifier: out.write('#pragma once\n\n') for exp in sorted(self.exports): out.write('#define GLSL_%s "%s"\n' % (exp[1:], new_names[exp])) + out.write('#define GLSL_%s_raw %s\n' % (exp[1:], new_names[exp])) out.close() @@ -568,7 +576,7 @@ class Minifier: def write_offline_glsl(self, outdir): - output_path = os.path.join(outdir, os.path.splitext(self.basename)[0] + ".minified.glsl") + output_path = os.path.join(outdir, os.path.splitext(self.basename)[0] + ".minified." + args.minified_extension) print("Minifying %s <- %s" % (output_path, self.basename)) out = open(output_path, "w", newline='\n') self.emit_tokens_to_rewritten_glsl(out, preserve_exported_switches=True) diff --git a/renderer/src/shaders/rhi.glsl b/renderer/src/shaders/rhi.glsl new file mode 100644 index 00000000..8f7dd96d --- /dev/null +++ b/renderer/src/shaders/rhi.glsl @@ -0,0 +1,355 @@ +/* + * Copyright 2023 Rive + */ + +// This header provides GLSL-specific #defines and declarations that enable our shaders to be +// compiled on MSL and GLSL both. + +// HLSL warns that it will unroll the loops through r,g,b values in advanced_blend.glsl, but +// unrolling these loops is exactly what we want. +#pragma $warning($disable : 3550) + +// Don't warn about uninitialized variables. If we leave one uninitialized it's because we know what +// we're doing and don't want to pay the cost of initializing it. +#pragma $warning($disable : 4000) + +// #define native hlsl types if their names are being rewritten. +#define _ARE_TOKEN_NAMES_PRESERVED +#ifndef $_ARE_TOKEN_NAMES_PRESERVED +#define half $half +#define half2 $half2 +#define half3 $half3 +#define half4 $half4 +#define short $short +#define short2 $short2 +#define short3 $short3 +#define short4 $short4 +#define ushort $ushort +#define ushort2 $ushort2 +#define ushort3 $ushort3 +#define ushort4 $ushort4 +#define float2 $float2 +#define float3 $float3 +#define float4 $float4 +#define bool2 $bool2 +#define bool3 $bool3 +#define bool4 $bool4 +#define uint2 $uint2 +#define uint3 $uint3 +#define uint4 $uint4 +#define int2 $int2 +#define int3 $int3 +#define int4 $int4 +#define float4x2 $float4x2 +#define ushort $ushort +#define float2x2 $float2x2 +#define half3x4 $half3x4 +#endif + +$typedef float3 packed_float3; + +#ifdef @ENABLE_MIN_16_PRECISION + +$typedef $min16uint ushort; + +#else + +$typedef $uint ushort; + +#endif + +#define SPLAT(A, B) A##B + +#define INLINE $inline +#define OUT(ARG_TYPE) out ARG_TYPE + +#define ATTR_BLOCK_BEGIN(NAME) \ + struct NAME \ + { +#define ATTR(IDX, TYPE, NAME) TYPE NAME : SPLAT(ATTRIBUTE, IDX) +#define ATTR_BLOCK_END \ + } \ + ; +#define ATTR_LOAD(T, A, N, I) +#define ATTR_UNPACK(ID, attrs, NAME, TYPE) TYPE NAME = attrs.NAME + +#define UNIFORM_BUFFER_REGISTER(IDX) $register(SPLAT($b, IDX)) + +#define UNIFORM_BLOCK_BEGIN(IDX, NAME) \ + $cbuffer NAME : UNIFORM_BUFFER_REGISTER(IDX) \ + { \ + struct \ + { + +#define UNIFORM_BLOCK_END(NAME) \ + } \ + NAME; \ + } + +#define VARYING_BLOCK_BEGIN \ + struct Varyings \ + { + +#define NO_PERSPECTIVE $noperspective +#define @OPTIONALLY_FLAT $nointerpolation +#define FLAT $nointerpolation +#define VARYING(IDX, TYPE, NAME) TYPE NAME : SPLAT($TEXCOORD, IDX) + +#define VARYING_BLOCK_END \ + float4 _pos : $SV_Position; \ + } \ + ; + +#define VARYING_INIT(NAME, TYPE) TYPE NAME +#define VARYING_PACK(NAME) _varyings.NAME = NAME +#define VARYING_UNPACK(NAME, TYPE) TYPE NAME = _varyings.NAME + +#ifdef @VERTEX +#define VERTEX_TEXTURE_BLOCK_BEGIN +#define VERTEX_TEXTURE_BLOCK_END +#endif + +#ifdef @FRAGMENT +#define FRAG_TEXTURE_BLOCK_BEGIN +#define FRAG_TEXTURE_BLOCK_END +#endif + +#define TEXTURE_RGBA32UI(SET, IDX, NAME) uniform $Texture2D NAME : $register(SPLAT($t, IDX)) +#define TEXTURE_RGBA32F(SET, IDX, NAME) uniform $Texture2D NAME : $register(SPLAT($t, IDX)) +#define TEXTURE_RGBA8(SET, IDX, NAME) \ + uniform $Texture2D<$unorm float4> NAME : $register(SPLAT($t, IDX)) + +// SAMPLER_LINEAR and SAMPLER_MIPMAP are the same because in d3d11, sampler parameters are defined +// at the API level. +#define SAMPLER(TEXTURE_IDX, NAME) $SamplerState NAME : $register(SPLAT($s, TEXTURE_IDX)); +#define SAMPLER_LINEAR SAMPLER +#define SAMPLER_MIPMAP SAMPLER + +#define TEXEL_FETCH(NAME, COORD) NAME[COORD] +#define TEXTURE_SAMPLE(NAME, SAMPLER_NAME, COORD) NAME.$Sample(SAMPLER_NAME, COORD) +#define TEXTURE_SAMPLE_LOD(NAME, SAMPLER_NAME, COORD, LOD) \ + NAME.$SampleLevel(SAMPLER_NAME, COORD, LOD) +#define TEXTURE_SAMPLE_GRAD(NAME, SAMPLER_NAME, COORD, DDX, DDY) \ + NAME.$SampleGrad(SAMPLER_NAME, COORD, DDX, DDY) + +#define PLS_INTERLOCK_BEGIN +#define PLS_INTERLOCK_END + +#ifdef @ENABLE_RASTERIZER_ORDERED_VIEWS +#define PLS_TEX2D $RasterizerOrderedTexture2D +#else +#define PLS_TEX2D $RWTexture2D +#endif + +#define PLS_BLOCK_BEGIN +#ifdef @ENABLE_TYPED_UAV_LOAD_STORE +#define PLS_DECL4F(IDX, NAME) uniform PLS_TEX2D<$unorm half4> NAME : $register($SPLAT(u, IDX)) +#else +#define PLS_DECL4F(IDX, NAME) uniform PLS_TEX2D NAME : $register(SPLAT($u, IDX)) +#endif +#define PLS_DECLUI(IDX, NAME) uniform PLS_TEX2D NAME : $register(SPLAT($u, IDX)) +#define PLS_DECLUI_ATOMIC PLS_DECLUI +#define PLS_LOADUI_ATOMIC PLS_LOADUI +#define PLS_STOREUI_ATOMIC PLS_STOREUI +#define PLS_BLOCK_END + +#ifdef @ENABLE_TYPED_UAV_LOAD_STORE +#define PLS_LOAD4F(PLANE) PLANE[_plsCoord] +#else +#define PLS_LOAD4F(PLANE) unpackUnorm4x8(PLANE[_plsCoord]) +#endif +#define PLS_LOADUI(PLANE) PLANE[_plsCoord] +#ifdef @ENABLE_TYPED_UAV_LOAD_STORE +#define PLS_STORE4F(PLANE, VALUE) PLANE[_plsCoord] = (VALUE) +#else +#define PLS_STORE4F(PLANE, VALUE) PLANE[_plsCoord] = packUnorm4x8(VALUE) +#endif +#define PLS_STOREUI(PLANE, VALUE) PLANE[_plsCoord] = (VALUE) + +INLINE uint pls_atomic_max(PLS_TEX2D plane, int2 _plsCoord, uint x) +{ + uint originalValue; + $InterlockedMax(plane[_plsCoord], x, originalValue); + return originalValue; +} + +#define PLS_ATOMIC_MAX(PLANE, X) pls_atomic_max(PLANE, _plsCoord, X) + +INLINE uint pls_atomic_add(PLS_TEX2D plane, int2 _plsCoord, uint x) +{ + uint originalValue; + $InterlockedAdd(plane[_plsCoord], x, originalValue); + return originalValue; +} + +#define PLS_ATOMIC_ADD(PLANE, X) pls_atomic_add(PLANE, _plsCoord, X) + +#define PLS_PRESERVE_4F(PLANE) +#define PLS_PRESERVE_UI(PLANE) + +#define VERTEX_CONTEXT_DECL +#define VERTEX_CONTEXT_UNPACK + +#define VERTEX_MAIN(NAME, Attrs, attrs, _vertexID, _instanceID) \ + \ + uint $baseInstance; \ + \ + Varyings NAME(Attrs attrs, uint _vertexID \ + : $SV_VertexID, uint _instanceIDWithoutBase \ + : $SV_InstanceID) \ + { \ + uint _instanceID = _instanceIDWithoutBase + $baseInstance; \ + Varyings _varyings; + +#define IMAGE_RECT_VERTEX_MAIN(NAME, Attrs, attrs, _vertexID, _instanceID) \ + Varyings NAME(Attrs attrs, uint _vertexID : $SV_VertexID) \ + { \ + Varyings _varyings; \ + float4 _pos; + +#define IMAGE_MESH_VERTEX_MAIN(NAME, PositionAttr, position, UVAttr, uv, _vertexID) \ + Varyings NAME(PositionAttr position, UVAttr uv, uint _vertexID : $SV_VertexID) \ + { \ + Varyings _varyings; \ + float4 _pos; + +#define EMIT_VERTEX(POSITION) \ + _varyings._pos = POSITION; \ + } \ + return _varyings; + +#define FRAG_DATA_MAIN(DATA_TYPE, NAME) \ + DATA_TYPE NAME(Varyings _varyings) : $SV_Target \ + { + +#define EMIT_FRAG_DATA(VALUE) \ + return VALUE; \ + } + +#define FRAGMENT_CONTEXT_DECL , float2 _fragCoord +#define FRAGMENT_CONTEXT_UNPACK , _fragCoord + +#define PLS_CONTEXT_DECL , int2 _plsCoord +#define PLS_CONTEXT_UNPACK , _plsCoord + +#define PLS_MAIN(NAME) [$earlydepthstencil] void NAME(Varyings _varyings) { \ + float2 _fragCoord = _varyings._pos.xy;\ + int2 _plsCoord = int2(floor(_fragCoord)); + +#define PLS_MAIN_WITH_IMAGE_UNIFORMS(NAME) PLS_MAIN(NAME) + +#define EMIT_PLS } + +#define PLS_FRAG_COLOR_MAIN(NAME) \ + [$earlydepthstencil] half4 NAME(Varyings _varyings) : $SV_Target \ + { \ + float2 _fragCoord = _varyings._pos.xy; \ + int2 _plsCoord = int2(floor(_fragCoord)); \ + half4 _fragColor; + +#define PLS_FRAG_COLOR_MAIN_WITH_IMAGE_UNIFORMS(NAME) PLS_FRAG_COLOR_MAIN(NAME) + +#define EMIT_PLS_AND_FRAG_COLOR \ + } \ + return _fragColor; + +#define uintBitsToFloat $asfloat +#define intBitsToFloat $asfloat +#define floatBitsToInt $asint +#define floatBitsToUint $asuint +#define inversesqrt $rsqrt +#define notEqual(A, B) ((A) != (B)) +#define lessThanEqual(A, B) ((A) <= (B)) +#define greaterThanEqual(A, B) ((A) >= (B)) + +// HLSL matrices are stored in row-major order, and therefore transposed from their counterparts +// in GLSL and Metal. We can work around this entirely by reversing the arguments to mul(). +#define MUL(A, B) $mul(B, A) + +#define VERTEX_STORAGE_BUFFER_BLOCK_BEGIN +#define VERTEX_STORAGE_BUFFER_BLOCK_END + +#define FRAG_STORAGE_BUFFER_BLOCK_BEGIN +#define FRAG_STORAGE_BUFFER_BLOCK_END + +#define STORAGE_BUFFER_U32x2(IDX, GLSL_STRUCT_NAME, NAME) \ + $StructuredBuffer NAME : $register(SPLAT($t, IDX)) +#define STORAGE_BUFFER_U32x4(IDX, GLSL_STRUCT_NAME, NAME) \ + $StructuredBuffer NAME : $register(SPLAT($t, IDX)) +#define STORAGE_BUFFER_F32x4(IDX, GLSL_STRUCT_NAME, NAME) \ + $StructuredBuffer NAME : $register(SPLAT($t, IDX)) + +#define STORAGE_BUFFER_LOAD4(NAME, I) NAME[I] +#define STORAGE_BUFFER_LOAD2(NAME, I) NAME[I] + +INLINE half2 unpackHalf2x16(uint u) +{ + uint y = (u >> 16); + uint x = u & 0xffffu; + return half2($f16tof32(x), $f16tof32(y)); +} + +INLINE uint packHalf2x16(float2 v) +{ + uint x = $f32tof16(v.x); + uint y = $f32tof16(v.y); + return (y << 16) | x; +} + +INLINE half4 unpackUnorm4x8(uint u) +{ + uint4 vals = uint4(u & 0xffu, (u >> 8) & 0xffu, (u >> 16) & 0xffu, u >> 24); + return half4(vals) * (1. / 255.); +} + +INLINE uint packUnorm4x8(half4 color) +{ + uint4 vals = (uint4(color * 255.) & 0xff) << uint4(0, 8, 16, 24); + vals.rg |= vals.ba; + vals.r |= vals.g; + return vals.r; +} + +INLINE float atan(float y, float x) { return $atan2(y, x); } + +INLINE float2x2 inverse(float2x2 m) +{ + float2x2 adjoint = float2x2(m[1][1], -m[0][1], -m[1][0], m[0][0]); + return adjoint * (1. / determinant(m)); +} + +// Redirects for intrinsics that have different names in HLSL + +INLINE float mix(float x, float y, float s) { return $lerp(x, y, s); } +INLINE float2 mix(float2 x, float2 y, float2 s) { return $lerp(x, y, s); } +INLINE float3 mix(float3 x, float3 y, float3 s) { return $lerp(x, y, s); } +INLINE float4 mix(float4 x, float4 y, float4 s) { return $lerp(x, y, s); } + +INLINE float fract(float x) { return $frac(x); } +INLINE float2 fract(float2 x) { return $frac(x); } +INLINE float3 fract(float3 x) { return $frac(x); } +INLINE float4 fract(float4 x) { return $frac(x); } + +// Reimplement intrinsics for half types. +// This shadows the intrinsic function for floats, so we also have to declare that overload. + +INLINE float rive_sign(float x) { return sign(x); } +INLINE float2 rive_sign(float2 x) { return sign(x); } +INLINE float3 rive_sign(float3 x) { return sign(x); } +INLINE float4 rive_sign(float4 x) { return sign(x); } + +#define sign rive_sign + +INLINE float rive_abs(float x) { return abs(x); } +INLINE float2 rive_abs(float2 x) { return abs(x); } +INLINE float3 rive_abs(float3 x) { return abs(x); } +INLINE float4 rive_abs(float4 x) { return abs(x); } + +#define abs rive_abs + +INLINE float rive_sqrt(float x) { return sqrt(x); } +INLINE float2 rive_sqrt(float2 x) { return sqrt(x); } +INLINE float3 rive_sqrt(float3 x) { return sqrt(x); } +INLINE float4 rive_sqrt(float4 x) { return sqrt(x); } + +#define sqrt rive_sqrt diff --git a/renderer/src/shaders/tessellate.glsl b/renderer/src/shaders/tessellate.glsl index efbe0a8b..c7f31495 100644 --- a/renderer/src/shaders/tessellate.glsl +++ b/renderer/src/shaders/tessellate.glsl @@ -16,7 +16,14 @@ ATTR_BLOCK_BEGIN(Attrs) ATTR(0, float4, @a_p0p1_); // End in '_' because D3D interprets the '1' as a semantic index. ATTR(1, float4, @a_p2p3_); ATTR(2, float4, @a_joinTan_and_ys); // [joinTangent, y, reflectionY] -ATTR(3, uint4, @a_args); // [x0x1, reflectionX0X1, segmentCounts, contourIDWithFlags] +#ifdef SPLIT_UINT4_ATTRIBUTES +ATTR(3, uint, @a_x0x1); +ATTR(4, uint, @a_reflectionX0X1); +ATTR(5, uint, @a_segmentCounts); +ATTR(6, uint, @a_contourIDWithFlags); +#else +ATTR(3, uint4, @a_args); // [x0x1, reflectionX0X1, segmentCounts, contourIDWithFlags] +#endif ATTR_BLOCK_END #endif @@ -61,7 +68,16 @@ VERTEX_MAIN(@tessellateVertexMain, Attrs, attrs, _vertexID, _instanceID) ATTR_UNPACK(_instanceID, attrs, @a_p0p1_, float4); ATTR_UNPACK(_instanceID, attrs, @a_p2p3_, float4); ATTR_UNPACK(_instanceID, attrs, @a_joinTan_and_ys, float4); +#ifdef SPLIT_UINT4_ATTRIBUTES + ATTR_UNPACK(_instanceID, attrs, @a_x0x1, uint); + ATTR_UNPACK(_instanceID, attrs, @a_reflectionX0X1, uint); + ATTR_UNPACK(_instanceID, attrs, @a_segmentCounts, uint); + ATTR_UNPACK(_instanceID, attrs, @a_contourIDWithFlags, uint); + + uint4 @a_args = uint4(@a_x0x1, @a_reflectionX0X1, @a_segmentCounts, @a_contourIDWithFlags); +#else ATTR_UNPACK(_instanceID, attrs, @a_args, uint4); +#endif VARYING_INIT(v_p0p1, float4); VARYING_INIT(v_p2p3, float4); diff --git a/renderer/src/shaders/unreal/atomic_base.ush b/renderer/src/shaders/unreal/atomic_base.ush new file mode 100644 index 00000000..4cede29e --- /dev/null +++ b/renderer/src/shaders/unreal/atomic_base.ush @@ -0,0 +1,14 @@ +#pragma once +#define USING_PLS_STORAGE_TEXTURES 1 +#define ENABLE_TYPED_UAV_LOAD_STORE 1 +#define UNIFORM_DEFINITIONS_AUTO_GENERATED 1 +#define OPTIONALLY_FLAT flat + +#include "/Engine/Generated/GeneratedUniformBuffers.ush" +#include "parse_environment.ush" +#include "Generated/rhi.minified.ush" +#include "Generated/constants.minified.ush" +#include "Generated/common.minified.ush" +#include "Generated/advanced_blend.minified.ush" +#include "Generated/draw_path_common.minified.ush" +#include "Generated/atomic_draw.minified.ush" diff --git a/renderer/src/shaders/unreal/atomic_draw_image_mesh.usf b/renderer/src/shaders/unreal/atomic_draw_image_mesh.usf new file mode 100644 index 00000000..8eb85eda --- /dev/null +++ b/renderer/src/shaders/unreal/atomic_draw_image_mesh.usf @@ -0,0 +1,4 @@ +#define DRAW_IMAGE +#define DRAW_IMAGE_MESH +#include "/Engine/Public/Platform.ush" +#include "atomic_base.ush" diff --git a/renderer/src/shaders/unreal/atomic_draw_image_rect.usf b/renderer/src/shaders/unreal/atomic_draw_image_rect.usf new file mode 100644 index 00000000..36fb1673 --- /dev/null +++ b/renderer/src/shaders/unreal/atomic_draw_image_rect.usf @@ -0,0 +1,4 @@ +#define DRAW_IMAGE +#define DRAW_IMAGE_RECT +#include "/Engine/Public/Platform.ush" +#include "atomic_base.ush" diff --git a/renderer/src/shaders/unreal/atomic_draw_interior_triangles.usf b/renderer/src/shaders/unreal/atomic_draw_interior_triangles.usf new file mode 100644 index 00000000..c883d718 --- /dev/null +++ b/renderer/src/shaders/unreal/atomic_draw_interior_triangles.usf @@ -0,0 +1,3 @@ +#define DRAW_INTERIOR_TRIANGLES +#include "/Engine/Public/Platform.ush" +#include "atomic_base.ush" diff --git a/renderer/src/shaders/unreal/atomic_draw_path.usf b/renderer/src/shaders/unreal/atomic_draw_path.usf new file mode 100644 index 00000000..b0c74527 --- /dev/null +++ b/renderer/src/shaders/unreal/atomic_draw_path.usf @@ -0,0 +1,3 @@ +#define DRAW_PATH +#include "/Engine/Public/Platform.ush" +#include "atomic_base.ush" diff --git a/renderer/src/shaders/unreal/atomic_resolve_pls.usf b/renderer/src/shaders/unreal/atomic_resolve_pls.usf new file mode 100644 index 00000000..62da1f34 --- /dev/null +++ b/renderer/src/shaders/unreal/atomic_resolve_pls.usf @@ -0,0 +1,4 @@ +#define DRAW_RENDER_TARGET_UPDATE_BOUNDS 1 +#define RESOLVE_PLS 1 +#include "/Engine/Public/Platform.ush" +#include "atomic_base.ush" \ No newline at end of file diff --git a/renderer/src/shaders/unreal/color_ramp.usf b/renderer/src/shaders/unreal/color_ramp.usf new file mode 100644 index 00000000..550b94ab --- /dev/null +++ b/renderer/src/shaders/unreal/color_ramp.usf @@ -0,0 +1,10 @@ +#include "/Engine/Public/Platform.ush" +#define UNIFORM_DEFINITIONS_AUTO_GENERATED 1 +#define SPLIT_UINT4_ATTRIBUTES 1 + +#include "/Engine/Generated/GeneratedUniformBuffers.ush" +#include "parse_environment.ush" +#include "Generated/rhi.minified.ush" +#include "Generated/constants.minified.ush" +#include "Generated/common.minified.ush" +#include "Generated/color_ramp.minified.ush" diff --git a/renderer/src/shaders/unreal/draw_image_mesh.usf b/renderer/src/shaders/unreal/draw_image_mesh.usf new file mode 100644 index 00000000..438d81d8 --- /dev/null +++ b/renderer/src/shaders/unreal/draw_image_mesh.usf @@ -0,0 +1,15 @@ +#define PLS_IMPL_SUBPASS_LOAD +#define OPTIONALLY_FLAT flat +#define DRAW_IMAGE +#define DRAW_IMAGE_MESH + +#include "/Engine/Public/Platform.ush" +#include "/Engine/Generated/GeneratedUniformBuffers.ush" + +#include "parse_environment.ush" +#include "Generated/rhi.minified.ush" +#include "Generated/constants.minified.ush" +#include "Generated/specialization.minified.ush" +#include "Generated/common.minified.ush" +#include "Generated/advanced_blend.minified.ush" +#include "Generated/draw_image_mesh.minified.ush" diff --git a/renderer/src/shaders/unreal/draw_interior_triangles.usf b/renderer/src/shaders/unreal/draw_interior_triangles.usf new file mode 100644 index 00000000..b01eeb39 --- /dev/null +++ b/renderer/src/shaders/unreal/draw_interior_triangles.usf @@ -0,0 +1,14 @@ +#define PLS_IMPL_SUBPASS_LOAD +#define OPTIONALLY_FLAT flat +#define DRAW_INTERIOR_TRIANGLES + +#include "/Engine/Public/Platform.ush" +#include "/Engine/Generated/GeneratedUniformBuffers.ush" + +#include "parse_environment.ush" +#include "Generated/rhi.minified.ush" +#include "Generated/constants.minified.ush" +#include "Generated/common.minified.ush" +#include "Generated/draw_path_common.minified.ush" +#include "Generated/advanced_blend.minified.ush" +#include "Generated/draw_path.minified.ush" diff --git a/renderer/src/shaders/unreal/draw_path.usf b/renderer/src/shaders/unreal/draw_path.usf new file mode 100644 index 00000000..f6e8c1d2 --- /dev/null +++ b/renderer/src/shaders/unreal/draw_path.usf @@ -0,0 +1,15 @@ +#define ENABLE_INSTANCE_INDEX +#define PLS_IMPL_SUBPASS_LOAD +#define OPTIONALLY_FLAT flat +#define DRAW_PATH + +#include "/Engine/Public/Platform.ush" +#include "/Engine/Generated/GeneratedUniformBuffers.ush" + +#include "parse_environment.ush" +#include "Generated/rhi.minified.ush" +#include "Generated/constants.minified.ush" +#include "Generated/common.minified.ush" +#include "Generated/draw_path_common.minified.ush" +#include "Generated/advanced_blend.minified.ush" +#include "Generated/draw_path.minified.ush" diff --git a/renderer/src/shaders/unreal/parse_environment.ush b/renderer/src/shaders/unreal/parse_environment.ush new file mode 100644 index 00000000..349a5729 --- /dev/null +++ b/renderer/src/shaders/unreal/parse_environment.ush @@ -0,0 +1,29 @@ +// unreal rhi always defines the permutation values, however, we expect them to either exist or not. so +// here we check if its set to false and undef if it is +#if !ENABLE_CLIPPING +#undef ENABLE_CLIPPING +#endif + +#if !ENABLE_CLIP_RECT +#undef ENABLE_CLIP_RECT +#endif + +#if !ENABLE_ADVANCED_BLEND +#undef ENABLE_ADVANCED_BLEND +#endif + +#if !FIXED_FUNCTION_COLOR_BLEND +#undef FIXED_FUNCTION_COLOR_BLEND +#endif + +#if !ENABLE_HSL_BLEND_MODES +#undef ENABLE_HSL_BLEND_MODES +#endif + +#if !ENABLE_NESTED_CLIPPING +#undef ENABLE_NESTED_CLIPPING +#endif + +#if !ENABLE_EVEN_ODD +#undef ENABLE_EVEN_ODD +#endif diff --git a/renderer/src/shaders/unreal/tessellate.usf b/renderer/src/shaders/unreal/tessellate.usf new file mode 100644 index 00000000..dac886b9 --- /dev/null +++ b/renderer/src/shaders/unreal/tessellate.usf @@ -0,0 +1,10 @@ +#include "/Engine/Public/Platform.ush" +#define UNIFORM_DEFINITIONS_AUTO_GENERATED 1 +#define SPLIT_UINT4_ATTRIBUTES 1 + +#include "/Engine/Generated/GeneratedUniformBuffers.ush" +#include "parse_environment.ush" +#include "Generated/rhi.minified.ush" +#include "Generated/constants.minified.ush" +#include "Generated/common.minified.ush" +#include "Generated/tessellate.minified.ush" diff --git a/tests/check_golds.sh b/tests/check_golds.sh index f71cbc94..c0f854d5 100755 --- a/tests/check_golds.sh +++ b/tests/check_golds.sh @@ -12,43 +12,48 @@ ARGS= while :; do case $1 in + -u) + TARGET="unreal" + DEFAULT_BACKEND=rhi + shift + ;; -i) TARGET="ios" DEFAULT_BACKEND=metal UDID="$(idevice_id -l)" # brew install ideviceinstaller shift - ;; + ;; -s) TARGET="iossim" DEFAULT_BACKEND=metal UDID="$(xcrun simctl list devices | grep '(Booted)' | sed 's/^[^(]*(\([A-Z0-9\-]*\)) (Booted).*$/\1/')" shift - ;; + ;; -a) TARGET="android" DEFAULT_BACKEND=gl SERIAL="$(adb get-serialno)" shift - ;; + ;; -R) REBASELINE=true shift - ;; + ;; -r) ARGS="$ARGS --remote" shift - ;; + ;; -v) ARGS="$ARGS --verbose" shift - ;; + ;; -n) ARGS="$ARGS --no-rebuild --no-install" shift - ;; + ;; *) break - ;; + ;; esac done @@ -79,14 +84,14 @@ do elif [[ "$TARGET" == "android" ]]; then ID="android_$SERIAL/$BACKEND" fi - + NUMBER_OF_PROCESSORS="${NUMBER_OF_PROCESSORS:-$(nproc 2>/dev/null || sysctl -n hw.physicalcpu)}" if [[ $NUMBER_OF_PROCESSORS > 20 ]]; then GOLDEN_JOBS=6 else GOLDEN_JOBS=4 fi - + if [ "$REBASELINE" == true ]; then echo echo "Rebaselining $ID..." @@ -97,13 +102,13 @@ do echo "Checking $ID..." rm -fr .gold/candidates/$ID python3 deploy_tests.py gms goldens -j$GOLDEN_JOBS $ARGS --target=$TARGET --outdir=.gold/candidates/$ID --backend=$BACKEND $NO_REBUILD - + echo echo "Checking $ID..." rm -fr .gold/diffs/$ID && mkdir -p .gold/diffs/$ID python3 diff.py -g .gold/$ID -c .gold/candidates/$ID -j$NUMBER_OF_PROCESSORS -o .gold/diffs/$ID \ || open_file .gold/diffs/$ID/index.html fi - + NO_REBUILD="--no-rebuild --no-install" done diff --git a/tests/common/test_harness.cpp b/tests/common/test_harness.cpp index 8437c901..8ba7fe97 100644 --- a/tests/common/test_harness.cpp +++ b/tests/common/test_harness.cpp @@ -417,9 +417,15 @@ void TestHarness::onApplicationCrash(const char* message) { if (m_primaryTCPClient != nullptr) { - // Buy monitorStdIOThread() some time to finish pumping any messages - // related to this abort. +// Buy monitorStdIOThread() some time to finish pumping any messages +// related to this abort. + +// std::this_thread::sleep_for causes weird link issues in unreal. just use sleep instead +#if defined(RIVE_UNREAL) && defined(_WIN32) + Sleep(100); +#else std::this_thread::sleep_for(std::chrono::milliseconds(100)); +#endif shutdownStdioThread(); shutdownInputPumpThread(); m_primaryTCPClient->send4(REQUEST_TYPE_APPLICATION_CRASH); diff --git a/tests/common/test_harness.hpp b/tests/common/test_harness.hpp index 74098f30..ed8205ae 100644 --- a/tests/common/test_harness.hpp +++ b/tests/common/test_harness.hpp @@ -101,8 +101,8 @@ private: std::filesystem::path m_outputDir; // Forwarding stdout and stderr to the server. - int m_savedStdout; - int m_savedStderr; + int m_savedStdout = 0; + int m_savedStderr = 0; std::array m_stdioPipe = {0, 0}; std::thread m_stdioThread; diff --git a/tests/common/testing_window.cpp b/tests/common/testing_window.cpp index 0ed4e55d..bffc497a 100644 --- a/tests/common/testing_window.cpp +++ b/tests/common/testing_window.cpp @@ -12,10 +12,8 @@ #include #endif -// Don't explicitly delete this object. Calling eglDestroyContext during app -// teardown causes a crash on Pixel 4. The OS will clean this up for us -// automatically when we exit. -std::unique_ptr s_TestingWindow = nullptr; +// Call TestingWindow::Destroy if you want to delete the window singleton +TestingWindow* s_TestingWindow = nullptr; const char* TestingWindow::BackendName(Backend backend) { @@ -55,6 +53,8 @@ const char* TestingWindow::BackendName(Backend backend) return "dawn"; case TestingWindow::Backend::coregraphics: return "coregraphics"; + case Backend::rhi: + return "rhi"; } RIVE_UNREACHABLE(); } @@ -116,6 +116,8 @@ TestingWindow::Backend TestingWindow::ParseBackend(const char* name, std::string return Backend::dawn; if (nameStr == "coregraphics") return Backend::coregraphics; + if (nameStr == "rhi") + return Backend::rhi; fprintf(stderr, "'%s': invalid TestingWindow::Backend\n", name); abort(); } @@ -142,8 +144,14 @@ TestingWindow* TestingWindow::Init(Backend backend, const std::string& gpuNameFilterStr, void* platformWindow) { - const char* gpuNameFilter = !gpuNameFilterStr.empty() ? gpuNameFilterStr.c_str() : nullptr; - assert(!s_TestingWindow); + + const char* gpuNameFilter RIVE_MAYBE_UNUSED = + !gpuNameFilterStr.empty() ? gpuNameFilterStr.c_str() : nullptr; + + if (backend == Backend::rhi) + assert(s_TestingWindow); + else + assert(!s_TestingWindow); switch (backend) { case Backend::gl: @@ -236,29 +244,33 @@ TestingWindow* TestingWindow::Init(Backend backend, s_TestingWindow = MakeCoreGraphics(); #endif break; + case Backend::rhi: + break; } if (!s_TestingWindow) { fprintf(stderr, "Failed to create testing window for Backend::%s\n", BackendName(backend)); abort(); } - return s_TestingWindow.get(); + + return s_TestingWindow; } TestingWindow* TestingWindow::Get() { assert(s_TestingWindow); // Call Init() first! - return s_TestingWindow.get(); + return s_TestingWindow; +} + +void TestingWindow::Set(TestingWindow* inWindow) +{ + assert(inWindow); + s_TestingWindow = inWindow; } void TestingWindow::Destroy() { assert(s_TestingWindow); + delete s_TestingWindow; s_TestingWindow = nullptr; } - -char TestingWindow::getKey() -{ - fprintf(stderr, "TestingWindow::getKey not implemented."); - abort(); -} diff --git a/tests/common/testing_window.hpp b/tests/common/testing_window.hpp index 86d44590..10bdb0de 100644 --- a/tests/common/testing_window.hpp +++ b/tests/common/testing_window.hpp @@ -30,6 +30,7 @@ class RenderTarget; // renderer = TestingWindow::Get()->reset(width, height); // ... // + class TestingWindow { public: @@ -62,6 +63,8 @@ public: anglemsaa, dawn, coregraphics, + + rhi, }; constexpr static bool IsGL(Backend backend) @@ -86,6 +89,7 @@ public: case Backend::swiftshadercore: case Backend::dawn: case Backend::coregraphics: + case Backend::rhi: return false; } RIVE_UNREACHABLE(); @@ -113,6 +117,7 @@ public: case Backend::swiftshadercore: case Backend::dawn: case Backend::coregraphics: + case Backend::rhi: return false; } RIVE_UNREACHABLE(); @@ -140,6 +145,7 @@ public: case Backend::coregraphics: case Backend::angle: case Backend::anglemsaa: + case Backend::rhi: return false; } RIVE_UNREACHABLE(); @@ -152,6 +158,7 @@ public: case Backend::glatomic: case Backend::d3datomic: case Backend::metalatomic: + case Backend::rhi: case Backend::vkcore: case Backend::moltenvkcore: case Backend::swiftshadercore: @@ -194,6 +201,7 @@ public: case Backend::anglemsaa: case Backend::dawn: case Backend::coregraphics: + case Backend::rhi: return false; } RIVE_UNREACHABLE(); @@ -221,6 +229,7 @@ public: case Backend::angle: case Backend::dawn: case Backend::coregraphics: + case Backend::rhi: return false; } RIVE_UNREACHABLE(); @@ -247,6 +256,7 @@ public: const std::string& gpuNameFilter, void* platformWindow = nullptr); static TestingWindow* Get(); + static void Set(TestingWindow* inWindow); static void Destroy(); uint32_t width() const { return m_width; } @@ -273,7 +283,11 @@ public: // Blocks until a key is pressed. virtual bool peekKey(char& key) { return false; } - virtual char getKey(); + virtual char getKey() + { + fprintf(stderr, "TestingWindow::getKey not implemented."); + abort(); + } virtual bool shouldQuit() const { return false; } virtual ~TestingWindow() {} @@ -283,24 +297,23 @@ protected: uint32_t m_height = 0; private: - static std::unique_ptr MakeGLFW(Backend, Visibility); - static std::unique_ptr MakeEGL(Backend, void* platformWindow); + static TestingWindow* MakeGLFW(Backend, Visibility); + static TestingWindow* MakeEGL(Backend, void* platformWindow); #ifdef _WIN32 - static std::unique_ptr MakeD3D(Visibility); + static TestingWindow* MakeD3D(Visibility); #endif #ifdef __APPLE__ - static std::unique_ptr MakeMetalTexture(); + static TestingWindow* MakeMetalTexture(); #endif #ifdef RIVE_MACOSX - static std::unique_ptr MakeCoreGraphics(); + static TestingWindow* MakeCoreGraphics(); #endif - static std::unique_ptr MakeFiddleContext(Backend, - Visibility, - const char* gpuNameFilter, - void* platformWindow); - static std::unique_ptr MakeVulkanTexture(bool coreFeaturesOnly, - const char* gpuNameFilter); - static std::unique_ptr MakeAndroidVulkan(void* platformWindow); + static TestingWindow* MakeFiddleContext(Backend, + Visibility, + const char* gpuNameFilter, + void* platformWindow); + static TestingWindow* MakeVulkanTexture(bool coreFeaturesOnly, const char* gpuNameFilter); + static TestingWindow* MakeAndroidVulkan(void* platformWindow); }; RIVE_MAKE_ENUM_BITSET(TestingWindow::RendererFlags); diff --git a/tests/common/testing_window_android_vulkan.cpp b/tests/common/testing_window_android_vulkan.cpp index aacacee1..57501992 100644 --- a/tests/common/testing_window_android_vulkan.cpp +++ b/tests/common/testing_window_android_vulkan.cpp @@ -6,10 +6,7 @@ #if !defined(RIVE_ANDROID) -std::unique_ptr TestingWindow::MakeAndroidVulkan(void* platformWindow) -{ - return nullptr; -} +TestingWindow* TestingWindow::MakeAndroidVulkan(void* platformWindow) { return nullptr; } #else @@ -283,10 +280,9 @@ private: rcp m_renderTarget; }; -std::unique_ptr TestingWindow::MakeAndroidVulkan(void* platformWindow) +TestingWindow* TestingWindow::MakeAndroidVulkan(void* platformWindow) { - return std::make_unique( - reinterpret_cast(platformWindow)); + return new TestingWindowAndroidVulkan(reinterpret_cast(platformWindow)); } #endif diff --git a/tests/common/testing_window_coregraphics.cpp b/tests/common/testing_window_coregraphics.cpp index 272c1efd..fc0676a6 100644 --- a/tests/common/testing_window_coregraphics.cpp +++ b/tests/common/testing_window_coregraphics.cpp @@ -71,9 +71,6 @@ private: AutoCF m_space; }; -std::unique_ptr TestingWindow::MakeCoreGraphics() -{ - return std::make_unique(); -} +TestingWindow* TestingWindow::MakeCoreGraphics() { return new TestingWindowCoreGraphics; } #endif diff --git a/tests/common/testing_window_egl.cpp b/tests/common/testing_window_egl.cpp index 15ee36e3..12839e81 100644 --- a/tests/common/testing_window_egl.cpp +++ b/tests/common/testing_window_egl.cpp @@ -6,10 +6,7 @@ #ifdef RIVE_TOOLS_NO_GL -std::unique_ptr TestingWindow::MakeEGL(Backend backend, void* platformWindow) -{ - return nullptr; -} +TestingWindow* TestingWindow::MakeEGL(Backend backend, void* platformWindow) { return nullptr; } #else @@ -512,7 +509,7 @@ private: glutils::Texture m_headlessRenderTexture = glutils::Texture::Zero(); }; -std::unique_ptr TestingWindow::MakeEGL(Backend backend, void* platformWindow) +TestingWindow* TestingWindow::MakeEGL(Backend backend, void* platformWindow) { auto rendererFlags = RendererFlags::none; EGLint angleBackend = EGL_NONE; @@ -556,14 +553,15 @@ std::unique_ptr TestingWindow::MakeEGL(Backend backend, void* pla case Backend::swiftshadercore: case Backend::dawn: case Backend::coregraphics: + case Backend::rhi: printf("Invalid backend for TestingWindow::MakeEGLPbuffer."); abort(); break; } - return std::make_unique(angleBackend, - samples, - TestingGLRenderer::Make(rendererFlags), - platformWindow); + return new TestingWindowEGL(angleBackend, + samples, + TestingGLRenderer::Make(rendererFlags), + platformWindow); } #endif diff --git a/tests/common/testing_window_fiddle_context.cpp b/tests/common/testing_window_fiddle_context.cpp index 65908963..cc47ca1c 100644 --- a/tests/common/testing_window_fiddle_context.cpp +++ b/tests/common/testing_window_fiddle_context.cpp @@ -5,11 +5,10 @@ #include "testing_window.hpp" #if defined(TESTING) || defined(RIVE_TOOLS_NO_GLFW) - -std::unique_ptr TestingWindow::MakeFiddleContext(Backend, - Visibility, - const char*, - void* platformWindow) +TestingWindow* TestingWindow::MakeFiddleContext(Backend, + Visibility, + const char*, + void* platformWindow) { return nullptr; } @@ -190,6 +189,7 @@ public: switch (backend) { + case Backend::rhi: case Backend::coregraphics: break; case Backend::gl: @@ -328,15 +328,12 @@ private: std::unique_ptr m_fiddleContext; }; -std::unique_ptr TestingWindow::MakeFiddleContext(Backend backend, - Visibility visibility, - const char* gpuNameFilter, - void* platformWindow) +TestingWindow* TestingWindow::MakeFiddleContext(Backend backend, + Visibility visibility, + const char* gpuNameFilter, + void* platformWindow) { - return std::make_unique(backend, - visibility, - gpuNameFilter, - platformWindow); + return new TestingWindowFiddleContext(backend, visibility, gpuNameFilter, platformWindow); } #endif diff --git a/tests/common/testing_window_metal_texture.mm b/tests/common/testing_window_metal_texture.mm index cfefaff0..c8a16855 100644 --- a/tests/common/testing_window_metal_texture.mm +++ b/tests/common/testing_window_metal_texture.mm @@ -132,9 +132,9 @@ private: }; }; // namespace rive::gpu -std::unique_ptr TestingWindow::MakeMetalTexture() +TestingWindow* TestingWindow::MakeMetalTexture() { - return std::make_unique(); + return new rive::gpu::TestingWindowMetalTexture(); } #endif diff --git a/tests/common/testing_window_vulkan_texture.cpp b/tests/common/testing_window_vulkan_texture.cpp index 874de340..27106c10 100644 --- a/tests/common/testing_window_vulkan_texture.cpp +++ b/tests/common/testing_window_vulkan_texture.cpp @@ -6,8 +6,7 @@ #ifndef RIVE_VULKAN -std::unique_ptr TestingWindow::MakeVulkanTexture(bool coreFeaturesOnly, - const char* gpuNameFilter) +TestingWindow* TestingWindow::MakeVulkanTexture(bool coreFeaturesOnly, const char* gpuNameFilter) { return nullptr; } @@ -261,10 +260,9 @@ private: }; }; // namespace rive::gpu -std::unique_ptr TestingWindow::MakeVulkanTexture(bool coreFeaturesOnly, - const char* gpuNameFilter) +TestingWindow* TestingWindow::MakeVulkanTexture(bool coreFeaturesOnly, const char* gpuNameFilter) { - return std::make_unique(coreFeaturesOnly, gpuNameFilter); + return new rive::gpu::TestingWindowVulkanTexture(coreFeaturesOnly, gpuNameFilter); } #endif diff --git a/tests/deploy_tests.py b/tests/deploy_tests.py index 5a4c7044..cc32564a 100644 --- a/tests/deploy_tests.py +++ b/tests/deploy_tests.py @@ -65,7 +65,7 @@ parser.add_argument("-m", "--match", help="`match` patter for gms") parser.add_argument("-t", "--target", default="host", - choices=["host", "android", "ios", "iossim"], + choices=["host", "android", "ios", "iossim", "unreal"], help="which platform to run on") parser.add_argument("-u", "--ios_udid", type=str, @@ -338,6 +338,10 @@ def update_cmd_to_deploy_on_target(cmd): dirname = os.path.dirname(cmd[0]) toolname = os.path.basename(cmd[0]) + if args.target == "unreal": + unreal_exe_path = os.path.join(dirname, "Windows", "rive_unreal.exe") + return [unreal_exe_path, "/Game/maps/" + toolname, "-ResX=1280", "-ResY=720", "-WINDOWED"] + cmd[1:] + if args.target == "android": sharedlib = os.path.join(dirname, "lib%s.so" % toolname) print("\nDeploying %s on android..." % sharedlib) @@ -491,6 +495,13 @@ def main(): args.remote = True # Since we can't do port forwarding in iOS, it always has to be remote. if not args.ios_udid: args.ios_udid = "booted" + elif args.target == 'unreal': + # currently, unreal needs to run only one job at a time for goldens and gms to work + args.jobs_per_tool = 1 + if args.builddir == None: + args.builddir = "out/debug" + # unreal is currently always rhi, we may have seperate rhi types in the future like rhi_metal etc.. + args.backend = 'rhi' else: assert(args.target == "host") if args.builddir == None: @@ -596,7 +607,7 @@ def main(): flush=True) # On mobile we can't launch >1 instance of the app at a time. - serial_deploy = not args.server_only and ("ios" in args.target or args.target == "android") + serial_deploy = not args.server_only and ("ios" in args.target or args.target == "android" or args.target == "unreal") procs = [] def keyboard_interrupt_handler(signal, frame): diff --git a/tests/gm/gmmain.cpp b/tests/gm/gmmain.cpp index 0ea43ffd..6817f5cc 100644 --- a/tests/gm/gmmain.cpp +++ b/tests/gm/gmmain.cpp @@ -81,7 +81,80 @@ static bool is_arg(const char arg[], const char target[], const char alt[] = nul return !strcmp(arg, target) || (arg && !strcmp(arg, alt)); } -#if defined(RIVE_IOS) || defined(RIVE_IOS_SIMULATOR) +#if defined(RIVE_UNREAL) + +typedef const void* REGISTRY_HANDLE; + +REGISTRY_HANDLE gms_get_registry_head() { return rivegm::GMRegistry::head(); } + +REGISTRY_HANDLE gms_registry_get_next(REGISTRY_HANDLE position_handle) +{ + const GMRegistry* position = reinterpret_cast(position_handle); + if (position == nullptr) + return nullptr; + return position->next(); +} + +bool gms_run_gm(REGISTRY_HANDLE gm_handle) +{ + const GMRegistry* position = reinterpret_cast(gm_handle); + if (position == nullptr) + return false; + + auto gm = position->get()(); + if (!gm) + { + return false; + } + + gm->onceBeforeDraw(); + + uint32_t width = gm->width(); + uint32_t height = gm->height(); + TestingWindow::Get()->resize(width, height); + gm->run(nullptr); + + return true; +} + +bool gms_registry_get_name(REGISTRY_HANDLE position_handle, std::string& name) +{ + const GMRegistry* position = reinterpret_cast(position_handle); + if (position == nullptr) + return false; + + auto gm = position->get()(); + if (!gm) + { + return false; + } + + name = gm->name(); + return true; +} + +bool gms_registry_get_size(REGISTRY_HANDLE position_handle, size_t& width, size_t& height) +{ + const GMRegistry* position = reinterpret_cast(position_handle); + if (position == nullptr) + return false; + + width = 0; + height = 0; + + auto gm = position->get()(); + if (!gm) + { + return false; + } + + width = gm->width(); + height = gm->height(); + + return true; +} +int gms_main(int argc, const char* argv[]) +#elif defined(RIVE_IOS) || defined(RIVE_IOS_SIMULATOR) int gms_ios_main(int argc, const char* argv[]) #elif defined(RIVE_ANDROID) int rive_android_main(int argc, const char* const* argv, struct android_app*) @@ -157,6 +230,7 @@ int main(int argc, const char* argv[]) dumpGMs(std::string(match), interactive); TestingWindow::Destroy(); // Exercise our PLS teardown process now that we're done. + TestHarness::Instance().shutdown(); return 0; } diff --git a/tests/goldens/goldens.cpp b/tests/goldens/goldens.cpp index 34abec2a..60ecb225 100644 --- a/tests/goldens/goldens.cpp +++ b/tests/goldens/goldens.cpp @@ -19,7 +19,6 @@ #include #include #include - constexpr static int kWindowTargetSize = 1600; GoldensArguments s_args; @@ -165,6 +164,8 @@ private: int goldens_ios_main(int argc, const char* argv[]) #elif defined(RIVE_ANDROID) int rive_android_main(int argc, const char* const* argv, struct android_app*) +#elif defined(RIVE_UNREAL) +int goldens_main(int argc, const char* argv[]) #else int main(int argc, const char* argv[]) #endif diff --git a/tests/goldens/goldens_arguments.hpp b/tests/goldens/goldens_arguments.hpp index 8f676bc6..19497bf1 100644 --- a/tests/goldens/goldens_arguments.hpp +++ b/tests/goldens/goldens_arguments.hpp @@ -22,21 +22,19 @@ public: args::Group required(*m_parser, "required arguments:", args::Group::Validators::All); args::Group optional(*m_parser, "optional arguments:", args::Group::Validators::DontCare); - args::ValueFlag testHarness(optional, - "test_harness", - "TCP server address of python test harness", - {"test_harness"}, - "/dev/null"); args::ValueFlag src(optional, "src", "source src filename (ignored if --test_harness)", {'s', "src"}); + args::ValueFlag testHarness(optional, + "test_harness", + "TCP server address of python test harness", + {"test_harness"}); + // no default for windows because /dev/null isnt a thing args::ValueFlag output(optional, "output", "output png directory (ignored if --test_harness)", - {'o', "output"}, - "/dev/null"); - + {'o', "output"}); args::ValueFlag artboard(optional, "artboard", "artboard to draw from (only when src != '-')", @@ -49,7 +47,7 @@ public: args::ValueFlag backend(optional, "backend", "backend type: [gl, metal, angle_gl, angle_d3d, " - "angle_vk, angle_mtl, coregraphics, skia_raster]", + "angle_vk, angle_mtl, coregraphics, skia_raster, rhi]", {'b', "backend"}); args::Flag headless(optional, "headless", diff --git a/tests/premake5.lua b/tests/premake5.lua index 2ea0efb8..e6a84516 100644 --- a/tests/premake5.lua +++ b/tests/premake5.lua @@ -1,6 +1,5 @@ dofile('rive_tools_project.lua') - project('imagediff') do kind('ConsoleApp') @@ -32,6 +31,10 @@ do do links({ 'opengl32' }) end + filter({ 'system:windows', 'options:for_unreal' }) + do + kind('None') + end filter('system:linux') do links({ 'GL' }) @@ -42,23 +45,32 @@ do end end -rive_tools_project('bench', _OPTIONS['os'] == 'ios' and 'StaticLib' or 'ConsoleApp') -do - files({ 'bench/*.cpp' }) +if not _OPTIONS['for_unreal'] then + rive_tools_project('bench', _OPTIONS['os'] == 'ios' and 'StaticLib' or 'ConsoleApp') + do + files({ 'bench/*.cpp' }) + end end rive_tools_project('gms', 'RiveTool') do files({ 'gm/*.cpp' }) + filter({ 'options:for_unreal' }) + do + defines({ 'RIVE_UNREAL' }) + end end rive_tools_project('goldens', 'RiveTool') do exceptionhandling('On') files({ 'goldens/goldens.cpp' }) + filter({ 'options:for_unreal' }) + do + defines({ 'RIVE_UNREAL' }) + end end - rive_tools_project('player', 'RiveTool') do files({ 'player/player.cpp' }) diff --git a/tests/rive_tools_project.lua b/tests/rive_tools_project.lua index 266579ed..6fbc8826 100644 --- a/tests/rive_tools_project.lua +++ b/tests/rive_tools_project.lua @@ -18,6 +18,7 @@ function rive_tools_project(name, project_kind) kind( _OPTIONS['os'] == 'android' and 'SharedLib' or _OPTIONS['os'] == 'ios' and 'StaticLib' + or _OPTIONS['for_unreal'] and 'StaticLib' or 'ConsoleApp' ) else @@ -129,13 +130,8 @@ function rive_tools_project(name, project_kind) }) end - filter({}) - - if - project_kind == 'ConsoleApp' - or project_kind == 'SharedLib' - or project_kind == 'RiveTool' - then + filter({ 'kind:ConsoleApp or SharedLib or WindowedApp' }) + do libdirs({ RIVE_RUNTIME_DIR .. '/build/%{cfg.system}/bin/' .. RIVE_BUILD_CONFIG }) links({ @@ -150,22 +146,24 @@ function rive_tools_project(name, project_kind) 'libwebp', 'rive_yoga', 'rive_harfbuzz', - 'rive_sheenbidi' + 'rive_sheenbidi', }) if ndk then relative_ndk = ndk if string.sub(ndk, 1, 1) == '/' then -- An absolute file path wasn't working with premake. - local current_path = string.gmatch(path.getabsolute('.'), "([^\\/]+)") + local current_path = string.gmatch(path.getabsolute('.'), '([^\\/]+)') for dir in current_path do relative_ndk = '../' .. relative_ndk end end - files({ relative_ndk .. '/sources/android/native_app_glue/android_native_app_glue.c' }) + files({ + relative_ndk .. '/sources/android/native_app_glue/android_native_app_glue.c', + }) end - filter({ 'system:windows' }) + filter({ 'kind:ConsoleApp or SharedLib or WindowedApp', 'system:windows' }) do libdirs({ RIVE_RUNTIME_DIR .. '/skia/dependencies/glfw_build/src/Release', @@ -173,7 +171,7 @@ function rive_tools_project(name, project_kind) links({ 'glfw3', 'opengl32', 'd3d11', 'dxgi', 'd3dcompiler', 'ws2_32' }) end - filter('system:macosx') + filter({ 'kind:ConsoleApp or SharedLib or WindowedApp', 'system:macosx' }) do libdirs({ RIVE_RUNTIME_DIR .. '/skia/dependencies/glfw_build/src' }) links({ @@ -195,18 +193,18 @@ function rive_tools_project(name, project_kind) }) end - filter('system:linux') + filter({ 'kind:ConsoleApp or SharedLib or WindowedApp', 'system:linux' }) do libdirs({ RIVE_RUNTIME_DIR .. '/skia/dependencies/glfw_build/src' }) links({ 'glfw3', 'm', 'z', 'dl', 'pthread', 'GL' }) end - filter('system:android') + filter({ 'kind:ConsoleApp or SharedLib or WindowedApp', 'system:android' }) do links({ 'EGL', 'GLESv3', 'log' }) end - filter('options:with-dawn') + filter({ 'kind:ConsoleApp or SharedLib or WindowedApp', 'options:with-dawn' }) do links({ 'dawn_native_static', @@ -216,20 +214,27 @@ function rive_tools_project(name, project_kind) }) end - filter({ 'options:with-dawn', 'system:windows' }) + filter({ + 'kind:ConsoleApp or SharedLib or WindowedApp', + 'options:with-dawn', + 'system:windows', + }) do links({ 'dxguid' }) end - filter({ 'options:with-dawn', 'system:macosx' }) + filter({ + 'kind:ConsoleApp or SharedLib or WindowedApp', + 'options:with-dawn', + 'system:macosx', + }) do links({ 'IOSurface.framework' }) end - - filter({}) end -end + filter({}) +end rive_tools_project('tools_common', 'StaticLib') do @@ -242,6 +247,12 @@ do RIVE_PLS_DIR .. '/path_fiddle/fiddle_context_dawn.cpp', }) + filter({ 'options:for_unreal' }) + do + defines({ 'RIVE_UNREAL', 'RIVE_TOOLS_NO_GLFW', 'RIVE_TOOLS_NO_GL' }) + cppdialect('C++20') + end + filter({ 'toolset:not msc' }) do buildoptions({ '-Wshorten-64-to-32' }) diff --git a/tests/unit_tests/premake5.lua b/tests/unit_tests/premake5.lua index bff1f6e3..bc7e7a71 100644 --- a/tests/unit_tests/premake5.lua +++ b/tests/unit_tests/premake5.lua @@ -49,7 +49,7 @@ do 'runtime/**.cpp', -- the runtime tests 'renderer/**.cpp', -- the renderer tests '../../utils/**.cpp', -- no_op utils - '../common/render_context_null.cpp', + '../common/render_context_null.cpp', }) filter('system:linux') @@ -81,12 +81,11 @@ do 'CoreText.framework', }) end - + filter({ 'toolset:not msc' }) do buildoptions({ '-Wshorten-64-to-32' }) end filter({}) - end diff --git a/tests/unreal/.vscode/launch.json b/tests/unreal/.vscode/launch.json new file mode 100644 index 00000000..f8228f95 --- /dev/null +++ b/tests/unreal/.vscode/launch.json @@ -0,0 +1,18 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Python Debugger: Current File with Arguments", + "type": "debugpy", + "request": "launch", + "program": "${file}", + "console": "integratedTerminal", + "args": [ + "C:/Git/rive/packages/runtime/tests" + ] + } + ] +} \ No newline at end of file diff --git a/tests/unreal/Config/DefaultEditor.ini b/tests/unreal/Config/DefaultEditor.ini new file mode 100644 index 00000000..e69de29b diff --git a/tests/unreal/Config/DefaultEngine.ini b/tests/unreal/Config/DefaultEngine.ini new file mode 100644 index 00000000..1d9b7c01 --- /dev/null +++ b/tests/unreal/Config/DefaultEngine.ini @@ -0,0 +1,100 @@ + + +[/Script/EngineSettings.GameMapsSettings] +GameDefaultMap=/Game/maps/TestMap.TestMap +EditorStartupMap=/Game/maps/TestMap.TestMap + +[/Script/WindowsTargetPlatform.WindowsTargetSettings] +DefaultGraphicsRHI=DefaultGraphicsRHI_Vulkan +-D3D12TargetedShaderFormats=PCD3D_SM5 ++D3D12TargetedShaderFormats=PCD3D_SM6 +-D3D11TargetedShaderFormats=PCD3D_SM5 ++D3D11TargetedShaderFormats=PCD3D_SM5 ++VulkanTargetedShaderFormats=SF_VULKAN_SM6 +Compiler=Default +AudioSampleRate=48000 +AudioCallbackBufferFrameSize=1024 +AudioNumBuffersToEnqueue=1 +AudioMaxChannels=0 +AudioNumSourceWorkers=4 +SpatializationPlugin= +SourceDataOverridePlugin= +ReverbPlugin= +OcclusionPlugin= +CompressionOverrides=(bOverrideCompressionTimes=False,DurationThreshold=5.000000,MaxNumRandomBranches=0,SoundCueQualityIndex=0) +CacheSizeKB=65536 +MaxChunkSizeOverrideKB=0 +bResampleForDevice=False +MaxSampleRate=48000.000000 +HighSampleRate=32000.000000 +MedSampleRate=24000.000000 +LowSampleRate=12000.000000 +MinSampleRate=8000.000000 +CompressionQualityModifier=1.000000 +AutoStreamingThreshold=0.000000 +SoundCueCookQualityIndex=-1 + +[/Script/Engine.RendererSettings] +r.Mobile.EnableNoPrecomputedLightingCSMShader=True + +r.GenerateMeshDistanceFields=True + +r.DynamicGlobalIlluminationMethod=1 + +r.ReflectionMethod=1 + +r.Shadow.Virtual.Enable=1 + +r.DefaultFeature.AutoExposure.ExtendDefaultLuminanceRange=True + +r.DefaultFeature.LocalExposure.HighlightContrastScale=0.8 + +r.DefaultFeature.LocalExposure.ShadowContrastScale=0.8 +r.GPUCrashDebugging=True + +[/Script/LinuxTargetPlatform.LinuxTargetSettings] +SpatializationPlugin= +SourceDataOverridePlugin= +ReverbPlugin= +OcclusionPlugin= +SoundCueCookQualityIndex=-1 +-TargetedRHIs=SF_VULKAN_SM5 ++TargetedRHIs=SF_VULKAN_SM5 ++TargetedRHIs=SF_VULKAN_SM6 ++TargetedRHIs=SF_VULKAN_ES31 + +[/Script/HardwareTargeting.HardwareTargetingSettings] +TargetedHardwareClass=Desktop +AppliedTargetedHardwareClass=Desktop +DefaultGraphicsPerformance=Maximum +AppliedDefaultGraphicsPerformance=Maximum + +[/Script/WorldPartitionEditor.WorldPartitionEditorSettings] +CommandletClass=Class'/Script/UnrealEd.WorldPartitionConvertCommandlet' + +[/Script/Engine.UserInterfaceSettings] +bAuthorizeAutomaticWidgetVariableCreation=False +FontDPIPreset=Standard +FontDPI=72 + +[/Script/Engine.Engine] ++ActiveGameNameRedirects=(OldGameName="TP_Blank",NewGameName="/Script/rive_unreal") ++ActiveGameNameRedirects=(OldGameName="/Script/TP_Blank",NewGameName="/Script/rive_unreal") + +[/Script/AndroidFileServerEditor.AndroidFileServerRuntimeSettings] +bEnablePlugin=True +bAllowNetworkConnection=True +SecurityToken=6B3C9F5B4A0292AFFA62F192840FB8EC +bIncludeInShipping=False +bAllowExternalStartInShipping=False +bCompileAFSProject=False +bUseCompression=False +bLogFiles=False +bReportStats=False +ConnectionType=USBOnly +bUseManualIPAddress=False +ManualIPAddress= + +[/Script/RiveRenderer.RiveRendererSettings] +bEnableRHITechPreview=True + diff --git a/tests/unreal/Config/DefaultGame.ini b/tests/unreal/Config/DefaultGame.ini new file mode 100644 index 00000000..a7289a6a --- /dev/null +++ b/tests/unreal/Config/DefaultGame.ini @@ -0,0 +1,100 @@ + + +[/Script/EngineSettings.GeneralProjectSettings] +ProjectID=0E9694AD44863D7CA6FDE5839ADA271C + +[/Script/UnrealEd.ProjectPackagingSettings] +Build=IfProjectHasCode +BuildConfiguration=PPBC_Development +BuildTarget= +FullRebuild=False +ForDistribution=False +IncludeDebugFiles=False +BlueprintNativizationMethod=Disabled +bIncludeNativizedAssetsInProjectGeneration=False +bExcludeMonolithicEngineHeadersInNativizedCode=False +UsePakFile=True +bUseIoStore=True +bUseZenStore=False +bMakeBinaryConfig=False +bGenerateChunks=False +bGenerateNoChunks=False +bChunkHardReferencesOnly=False +bForceOneChunkPerFile=False +MaxChunkSize=0 +bBuildHttpChunkInstallData=False +HttpChunkInstallDataDirectory=(Path="") +WriteBackMetadataToAssetRegistry=Disabled +bWritePluginSizeSummaryJsons=False +bCompressed=True +PackageCompressionFormat=Oodle +bForceUseProjectCompressionFormatIgnoreHardwareOverride=False +PackageAdditionalCompressionOptions= +PackageCompressionMethod=Kraken +PackageCompressionLevel_DebugDevelopment=4 +PackageCompressionLevel_TestShipping=4 +PackageCompressionLevel_Distribution=7 +PackageCompressionMinBytesSaved=1024 +PackageCompressionMinPercentSaved=5 +bPackageCompressionEnableDDC=False +PackageCompressionMinSizeToConsiderDDC=0 +HttpChunkInstallDataVersion= +IncludePrerequisites=True +IncludeAppLocalPrerequisites=False +bShareMaterialShaderCode=True +bDeterministicShaderCodeOrder=False +bSharedMaterialNativeLibraries=True +ApplocalPrerequisitesDirectory=(Path="") +IncludeCrashReporter=False +InternationalizationPreset=English +-CulturesToStage=en ++CulturesToStage=en +LocalizationTargetCatchAllChunkId=0 +bCookAll=False +bCookMapsOnly=False +bSkipEditorContent=False +bSkipMovies=False +-IniKeyDenylist=KeyStorePassword +-IniKeyDenylist=KeyPassword +-IniKeyDenylist=rsa.privateexp +-IniKeyDenylist=rsa.modulus +-IniKeyDenylist=rsa.publicexp +-IniKeyDenylist=aes.key +-IniKeyDenylist=SigningPublicExponent +-IniKeyDenylist=SigningModulus +-IniKeyDenylist=SigningPrivateExponent +-IniKeyDenylist=EncryptionKey +-IniKeyDenylist=DevCenterUsername +-IniKeyDenylist=DevCenterPassword +-IniKeyDenylist=IOSTeamID +-IniKeyDenylist=SigningCertificate +-IniKeyDenylist=MobileProvision +-IniKeyDenylist=IniKeyDenylist +-IniKeyDenylist=IniSectionDenylist ++IniKeyDenylist=KeyStorePassword ++IniKeyDenylist=KeyPassword ++IniKeyDenylist=rsa.privateexp ++IniKeyDenylist=rsa.modulus ++IniKeyDenylist=rsa.publicexp ++IniKeyDenylist=aes.key ++IniKeyDenylist=SigningPublicExponent ++IniKeyDenylist=SigningModulus ++IniKeyDenylist=SigningPrivateExponent ++IniKeyDenylist=EncryptionKey ++IniKeyDenylist=DevCenterUsername ++IniKeyDenylist=DevCenterPassword ++IniKeyDenylist=IOSTeamID ++IniKeyDenylist=SigningCertificate ++IniKeyDenylist=MobileProvision ++IniKeyDenylist=IniKeyDenylist ++IniKeyDenylist=IniSectionDenylist +-IniSectionDenylist=HordeStorageServers +-IniSectionDenylist=StorageServers ++IniSectionDenylist=HordeStorageServers ++IniSectionDenylist=StorageServers ++MapsToCook=(FilePath="/Game/maps/gms") ++MapsToCook=(FilePath="/Game/maps/goldens") ++DirectoriesToAlwaysCook=(Path="/Game") +bRetainStagedDirectory=False +CustomStageCopyHandler= + diff --git a/tests/unreal/Config/DefaultInput.ini b/tests/unreal/Config/DefaultInput.ini new file mode 100644 index 00000000..2d93b8dc --- /dev/null +++ b/tests/unreal/Config/DefaultInput.ini @@ -0,0 +1,467 @@ +[/Script/Engine.InputSettings] +-AxisConfig=(AxisKeyName="Gamepad_LeftX",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f)) +-AxisConfig=(AxisKeyName="Gamepad_LeftY",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f)) +-AxisConfig=(AxisKeyName="Gamepad_RightX",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f)) +-AxisConfig=(AxisKeyName="Gamepad_RightY",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f)) +-AxisConfig=(AxisKeyName="MouseX",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f)) +-AxisConfig=(AxisKeyName="MouseY",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f)) +-AxisConfig=(AxisKeyName="Mouse2D",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f)) ++AxisConfig=(AxisKeyName="Gamepad_LeftX",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="Gamepad_LeftY",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="Gamepad_RightX",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="Gamepad_RightY",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="MouseX",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="MouseY",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="Mouse2D",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="MouseWheelAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="Gamepad_LeftTriggerAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="Gamepad_RightTriggerAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="Gamepad_Special_Left_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="Gamepad_Special_Left_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="Vive_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="Vive_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="Vive_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="Vive_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="Vive_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="Vive_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="MixedReality_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="MixedReality_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="MixedReality_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="MixedReality_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="MixedReality_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="MixedReality_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="MixedReality_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="MixedReality_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="MixedReality_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="MixedReality_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="OculusTouch_Left_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="OculusTouch_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="OculusTouch_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="OculusTouch_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="OculusTouch_Right_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="OculusTouch_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="OculusTouch_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="OculusTouch_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="ValveIndex_Left_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="ValveIndex_Left_Grip_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="ValveIndex_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="ValveIndex_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="ValveIndex_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="ValveIndex_Right_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="ValveIndex_Right_Grip_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="ValveIndex_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="ValveIndex_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="ValveIndex_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +bAltEnterTogglesFullscreen=True +bF11TogglesFullscreen=True +bUseMouseForTouch=False +bEnableMouseSmoothing=True +bEnableFOVScaling=True +bCaptureMouseOnLaunch=True +bEnableLegacyInputScales=True +bEnableMotionControls=True +bFilterInputByPlatformUser=False +bShouldFlushPressedKeysOnViewportFocusLost=True +bAlwaysShowTouchInterface=False +bShowConsoleOnFourFingerTap=True +bEnableGestureRecognizer=False +bUseAutocorrect=False +DefaultViewportMouseCaptureMode=CapturePermanently_IncludingInitialMouseDown +DefaultViewportMouseLockMode=LockOnCapture +FOVScale=0.011110 +DoubleClickTime=0.200000 +DefaultPlayerInputClass=/Script/EnhancedInput.EnhancedPlayerInput +DefaultInputComponentClass=/Script/EnhancedInput.EnhancedInputComponent +DefaultTouchInterface=/Engine/MobileResources/HUD/DefaultVirtualJoysticks.DefaultVirtualJoysticks +-ConsoleKeys=Tilde ++ConsoleKeys=Tilde ++ConsoleKeys=Caret + +[/Script/EngineSettings.ConsoleSettings] +MaxScrollbackSize=1024 +-ManualAutoCompleteList=(Command="Exit",Desc="Exits the game") +-ManualAutoCompleteList=(Command="DebugCreatePlayer 1",Desc=) +-ManualAutoCompleteList=(Command="ToggleDrawEvents",Desc="Toggles annotations for shader debugging with Pix, Razor or similar GPU capture tools") +-ManualAutoCompleteList=(Command="Shot",Desc="Make a screenshot") +-ManualAutoCompleteList=(Command="RecompileShaders changed",Desc="Recompile shaders that have any changes on their source files") +-ManualAutoCompleteList=(Command="RecompileShaders global",Desc="Recompile global shaders that have any changes on their source files") +-ManualAutoCompleteList=(Command="RecompileShaders material ",Desc="Recompile shaders for a specific material if it's source files have changed") +-ManualAutoCompleteList=(Command="RecompileShaders all",Desc="Recompile all shaders that have any changes on their source files") +-ManualAutoCompleteList=(Command="Debug Crash",Desc="Simulates a game thread crash for debugging") +-ManualAutoCompleteList=(Command="Debug RenderCrash",Desc="Simulates a render thread crash for debugging") +-ManualAutoCompleteList=(Command="DumpMaterialStats",Desc="Dump material information") +-ManualAutoCompleteList=(Command="DumpShaderStats",Desc="Dump shader information") +-ManualAutoCompleteList=(Command="DumpShaderPipelineStats",Desc="Dump shader pipeline information") +-ManualAutoCompleteList=(Command="DumpShaderCompileStats",Desc="Dump shader compilation information") +-ManualAutoCompleteList=(Command="DumpGPU -upload",Desc="Dump the GPU's intermediary resources and upload to network") +-ManualAutoCompleteList=(Command="StartFPSChart",Desc="after that use StopFPSChart") +-ManualAutoCompleteList=(Command="StopFPSChart",Desc="after that look for the output in Saved/Profiling/FPSChartStats") +-ManualAutoCompleteList=(Command="FreezeAt",Desc="Locks the player view and rendering time.") +-ManualAutoCompleteList=(Command="Open",Desc=" Opens the specified map, doesn't pass previously set options") +-ManualAutoCompleteList=(Command="Travel",Desc=" Travels to the specified map, passes along previously set options") +-ManualAutoCompleteList=(Command="ServerTravel",Desc=" Travels to the specified map and brings clients along, passes along previously set options") +-ManualAutoCompleteList=(Command="DisplayAll",Desc=" Display property values for instances of classname") +-ManualAutoCompleteList=(Command="DisplayAllLocation",Desc=" Display location for all instances of classname") +-ManualAutoCompleteList=(Command="DisplayAllRotation",Desc=" Display rotation for all instances of classname") +-ManualAutoCompleteList=(Command="DisplayClear",Desc="Clears previous DisplayAll entries") +-ManualAutoCompleteList=(Command="FlushPersistentDebugLines",Desc="Clears persistent debug line cache") +-ManualAutoCompleteList=(Command="GetAll ",Desc=" Log property values of all instances of classname") +-ManualAutoCompleteList=(Command="GetAllLocation",Desc=" Log location for all instances of classname") +-ManualAutoCompleteList=(Command="GetAllRotation",Desc=" Log rotation for all instances of classname") +-ManualAutoCompleteList=(Command="Obj List ",Desc=" ") +-ManualAutoCompleteList=(Command="Obj ListContentRefs",Desc=" ") +-ManualAutoCompleteList=(Command="Obj Classes",Desc="Shows all classes") +-ManualAutoCompleteList=(Command="Obj Refs",Desc="Name= [Shortest] [Longest] [All] [External] [Direct] [Full] [Minimal] [GCOnly] [History=] Lists referencers of the specified object") +-ManualAutoCompleteList=(Command="Obj GC",Desc="Runs the UObject Garbage Collector and resets the GC timer.") +-ManualAutoCompleteList=(Command="Obj Dump ",Desc=" [Recurse or Hide/Show=\"category1,...\"] or [Recurse or Hide/Show=\"category1,...\"] Prints the value of all variables for the specified object") +-ManualAutoCompleteList=(Command="EditActor",Desc=" or or TRACE") +-ManualAutoCompleteList=(Command="EditDefault",Desc="") +-ManualAutoCompleteList=(Command="EditObject",Desc=" or or ") +-ManualAutoCompleteList=(Command="ReloadCfg ",Desc=" Reloads config variables for the specified object/class") +-ManualAutoCompleteList=(Command="ReloadLoc ",Desc=" Reloads localized variables for the specified object/class") +-ManualAutoCompleteList=(Command="Set ",Desc=" Sets property to value on objectname") +-ManualAutoCompleteList=(Command="SetNoPEC",Desc=" Sets property to value on objectname without Pre/Post Edit Change notifications") +-ManualAutoCompleteList=(Command="Stat FPS",Desc="Shows FPS counter") +-ManualAutoCompleteList=(Command="Stat UNIT",Desc="Shows hardware unit framerate") +-ManualAutoCompleteList=(Command="Stat DrawCount",Desc="Shows draw counts broken down by category") +-ManualAutoCompleteList=(Command="Stat UnitGraph",Desc="Draws simple unit time graph") +-ManualAutoCompleteList=(Command="Stat NamedEvents",Desc="Stat NamedEvents (Enables named events for external profilers)") +-ManualAutoCompleteList=(Command="Stat VerboseNamedEvents",Desc="Stat VerboseNamedEvents (Enables verbose named events for external profilers)") +-ManualAutoCompleteList=(Command="Stat StartFile",Desc="Stat StartFile (starts a stats capture, creating a new file in the Profiling directory; stop with stat StopFile to close the file)") +-ManualAutoCompleteList=(Command="Stat StopFile",Desc="Stat StopFile (finishes a stats capture started by stat StartFile)") +-ManualAutoCompleteList=(Command="Stat CPULoad",Desc="Stat CPULoad (Shows CPU Utilization)") +-ManualAutoCompleteList=(Command="Stat DUMPHITCHES",Desc="executes dumpstats on hitches - see log") +-ManualAutoCompleteList=(Command="Stat D3D11RHI",Desc="Shows Direct3D 11 stats") +-ManualAutoCompleteList=(Command="Stat LEVELS",Desc="Displays level streaming info") +-ManualAutoCompleteList=(Command="Stat GAME",Desc="Displays game performance stats") +-ManualAutoCompleteList=(Command="Stat MEMORY",Desc="Displays memory stats") +-ManualAutoCompleteList=(Command="Stat PHYSICS",Desc="Displays physics performance stats") +-ManualAutoCompleteList=(Command="Stat STREAMING",Desc="Displays basic texture streaming stats") +-ManualAutoCompleteList=(Command="Stat STREAMINGDETAILS",Desc="Displays detailed texture streaming stats") +-ManualAutoCompleteList=(Command="Stat GPU",Desc="Displays GPU stats for the frame") +-ManualAutoCompleteList=(Command="Stat COLLISION",Desc=) +-ManualAutoCompleteList=(Command="Stat PARTICLES",Desc=) +-ManualAutoCompleteList=(Command="Stat SCRIPT",Desc=) +-ManualAutoCompleteList=(Command="Stat AUDIO",Desc=) +-ManualAutoCompleteList=(Command="Stat ANIM",Desc=) +-ManualAutoCompleteList=(Command="Stat NET",Desc=) +-ManualAutoCompleteList=(Command="Stat LIST",Desc=" List groups of stats, saved sets, or specific stats within a specified group") +-ManualAutoCompleteList=(Command="Stat splitscreen",Desc=) +-ManualAutoCompleteList=(Command="MemReport",Desc="Outputs memory stats to a profile file. -Full gives more data, -Log outputs to the log") +-ManualAutoCompleteList=(Command="ListTextures",Desc="[Streaming|NonStreaming|Forced] [-Alphasort] [-csv] Lists all loaded textures and their current memory footprint") +-ManualAutoCompleteList=(Command="ListStreamingTextures",Desc="Lists info for all streaming textures") +-ManualAutoCompleteList=(Command="ListAnims",Desc="Lists info for all animations") +-ManualAutoCompleteList=(Command="ListSkeletalMeshes",Desc="Lists info for all skeletal meshes") +-ManualAutoCompleteList=(Command="ListStaticMeshes",Desc="Lists info for all static meshes") +-ManualAutoCompleteList=(Command="InvestigateTexture",Desc="Shows streaming info about the specified texture") +-ManualAutoCompleteList=(Command="DumpTextureStreamingStats",Desc="Dump current streaming stats (e.g. pool capacity) to the log") +-ManualAutoCompleteList=(Command="RestartLevel",Desc="Restarts the level") +-ManualAutoCompleteList=(Command="Module List",Desc="Lists all known modules") +-ManualAutoCompleteList=(Command="Module Load",Desc="Attempts to load the specified module name") +-ManualAutoCompleteList=(Command="Module Unload",Desc="Unloads the specified module name") +-ManualAutoCompleteList=(Command="Module Reload",Desc="Reloads the specified module name, unloading it first if needed") +-ManualAutoCompleteList=(Command="Module Recompile",Desc="Attempts to recompile a module, first unloading it if needed") +-ManualAutoCompleteList=(Command="HotReload",Desc=" Attempts to recompile a UObject DLL and reload it on the fly") +-ManualAutoCompleteList=(Command="au.debug.AudioDebugSound",Desc=" Rejects new USoundBase requests where the sound name does not contain the provided filter") +-ManualAutoCompleteList=(Command="au.debug.AudioGetDynamicSoundVolume",Desc="Gets volume for given sound type ('Class', 'Cue' or 'Wave') with provided name") +-ManualAutoCompleteList=(Command="au.debug.AudioMemReport",Desc="Lists info for audio memory") +-ManualAutoCompleteList=(Command="au.debug.AudioMixerDebugSound",Desc=" With new mixer enabled, rejects new USoundBase requests where the sound name does not contain the provided filter") +-ManualAutoCompleteList=(Command="au.debug.AudioResetAllDynamicSoundVolumes",Desc="Resets all dynamic volumes to unity") +-ManualAutoCompleteList=(Command="au.debug.AudioResetDynamicSoundVolume",Desc="Resets volume for given sound type ('Class', 'Cue' or 'Wave') with provided name to unity") +-ManualAutoCompleteList=(Command="au.debug.AudioSetDynamicSoundVolume",Desc="Name= Type= Vol= Sets volume for given sound type ('Class', 'Cue' or 'Wave') with provided name") +-ManualAutoCompleteList=(Command="au.debug.AudioSoloSoundClass",Desc=" [nonexclusive] Solos sounds using this USoundClass. If nonexclusive, existing solos will persist") +-ManualAutoCompleteList=(Command="au.debug.AudioSoloSoundCue",Desc=" [nonexclusive] Solos any type of USoundBase. If nonexclusive, existing solos will persist") +-ManualAutoCompleteList=(Command="au.debug.AudioSoloSoundWave",Desc=" [nonexclusive] Solos USoundWave. If nonexclusive, existing solos will persist") +-ManualAutoCompleteList=(Command="au.debug.ClearSoloAudio",Desc="Clears solo'ed object") +-ManualAutoCompleteList=(Command="au.debug.DisableLPF",Desc="Disables low-pass filter") +-ManualAutoCompleteList=(Command="au.debug.DisableEQFilter",Desc="Disables EQ") +-ManualAutoCompleteList=(Command="au.debug.DisableRadio",Desc="Disables radio effect") +-ManualAutoCompleteList=(Command="au.debug.DumpSoundInfo",Desc="Dumps sound information to log") +-ManualAutoCompleteList=(Command="au.debug.EnableRadio",Desc="Enables radio effect") +-ManualAutoCompleteList=(Command="au.debug.IsolateDryAudio",Desc="Isolates dry audio") +-ManualAutoCompleteList=(Command="au.debug.IsolateReverb",Desc="Isolates reverb") +-ManualAutoCompleteList=(Command="au.debug.ListAudioComponents",Desc="Dumps a detailed list of all AudioComponent objects") +-ManualAutoCompleteList=(Command="au.debug.ListSoundClasses",Desc="Lists a summary of loaded sound collated by class") +-ManualAutoCompleteList=(Command="au.debug.ListSoundClassVolumes",Desc="Lists all sound class volumes") +-ManualAutoCompleteList=(Command="au.debug.ListSoundDurations",Desc="Lists durations of all active sounds") +-ManualAutoCompleteList=(Command="au.debug.ListWaves",Desc="List the WaveInstances and whether they have a source") +-ManualAutoCompleteList=(Command="au.debug.PlayAllPIEAudio",Desc="Toggls whether or not all devices should play their audio") +-ManualAutoCompleteList=(Command="au.debug.PlaySoundCue",Desc="Plays the given soundcue") +-ManualAutoCompleteList=(Command="au.debug.PlaySoundWave",Desc=" Plays the given soundwave") +-ManualAutoCompleteList=(Command="au.debug.ResetSoundState",Desc="Resets volumes to default and removes test filters") +-ManualAutoCompleteList=(Command="au.debug.SetBaseSoundMix",Desc=" Sets the base sound mix") +-ManualAutoCompleteList=(Command="au.debug.ShowSoundClassHierarchy",Desc="") +-ManualAutoCompleteList=(Command="au.debug.SoloAudio",Desc="Solos the audio device associated with the parent world") +-ManualAutoCompleteList=(Command="au.debug.SoundClassFixup",Desc="Deprecated") +-ManualAutoCompleteList=(Command="au.debug.TestLFEBleed",Desc="Sets LPF to max for all sources") +-ManualAutoCompleteList=(Command="au.debug.TestLPF",Desc="Sets LPF to max for all sources") +-ManualAutoCompleteList=(Command="au.debug.TestStereoBleed",Desc="Test bleeding stereo sounds fully to the rear speakers") +-ManualAutoCompleteList=(Command="au.debug.ToggleHRTFForAll",Desc="Toggles whether or not HRTF spatialization is enabled for all") +-ManualAutoCompleteList=(Command="au.debug.ToggleSpatExt",Desc="Toggles enablement of the Spatial Audio Extension") +-ManualAutoCompleteList=(Command="DisableAllScreenMessages",Desc="Disables all on-screen warnings/messages") +-ManualAutoCompleteList=(Command="EnableAllScreenMessages",Desc="Enables all on-screen warnings/messages") +-ManualAutoCompleteList=(Command="ToggleAllScreenMessages",Desc="Toggles display state of all on-screen warnings/messages") +-ManualAutoCompleteList=(Command="ToggleAsyncCompute",Desc="Toggles AsyncCompute for platforms that have it") +-ManualAutoCompleteList=(Command="ToggleRenderingThread",Desc="Toggles the rendering thread for platforms that have it") +-ManualAutoCompleteList=(Command="CaptureMode",Desc="Toggles display state of all on-screen warnings/messages") +-ManualAutoCompleteList=(Command="ShowDebug None",Desc="Toggles ShowDebug w/ current debug type selection") +-ManualAutoCompleteList=(Command="ShowDebug Reset",Desc="Turns off ShowDebug, and clears debug type selection") +-ManualAutoCompleteList=(Command="ShowDebug NET",Desc=) +-ManualAutoCompleteList=(Command="ShowDebug PHYSICS",Desc=) +-ManualAutoCompleteList=(Command="ShowDebug COLLISION",Desc=) +-ManualAutoCompleteList=(Command="ShowDebug AI",Desc=) +-ManualAutoCompleteList=(Command="ShowDebug CAMERA",Desc=) +-ManualAutoCompleteList=(Command="ShowDebug WEAPON",Desc=) +-ManualAutoCompleteList=(Command="ShowDebug ANIMATION",Desc="Toggles display state of animation debug data") +-ManualAutoCompleteList=(Command="ShowDebug BONES",Desc="Toggles display of skeletalmesh bones") +-ManualAutoCompleteList=(Command="ShowDebug INPUT",Desc=) +-ManualAutoCompleteList=(Command="ShowDebug FORCEFEEDBACK",Desc="Toggles display of current force feedback values and what is contributing to that calculation") +-ManualAutoCompleteList=(Command="ShowDebugToggleSubCategory 3DBONES",Desc="With ShowDebug Bones: Toggles bone rendering between single lines and a more complex 3D model per bone") +-ManualAutoCompleteList=(Command="ShowDebugToggleSubCategory SYNCGROUPS",Desc="With ShowDebug Animation: Toggles display of sync group data") +-ManualAutoCompleteList=(Command="ShowDebugToggleSubCategory MONTAGES",Desc="With ShowDebug Animation: Toggles display of montage data") +-ManualAutoCompleteList=(Command="ShowDebugToggleSubCategory GRAPH",Desc="With ShowDebug Animation: Toggles display of animation blueprint graph") +-ManualAutoCompleteList=(Command="ShowDebugToggleSubCategory CURVES",Desc="With ShowDebug Animation: Toggles display of curve data") +-ManualAutoCompleteList=(Command="ShowDebugToggleSubCategory NOTIFIES",Desc="With ShowDebug Animation: Toggles display of notify data") +-ManualAutoCompleteList=(Command="ShowDebugToggleSubCategory FULLGRAPH",Desc="With ShowDebug Animation: Toggles graph display between active nodes only and all nodes") +-ManualAutoCompleteList=(Command="ShowDebugToggleSubCategory FULLBLENDSPACEDISPLAY",Desc="With ShowDebug Animation: Toggles display of sample blend weights on blendspaces") +-ManualAutoCompleteList=(Command="ShowDebugForReticleTargetToggle ",Desc=" Toggles 'ShowDebug' from showing debug info between reticle target actor (of subclass ) and camera view target") +-ManualAutoCompleteList=(Command="Stat SoundCues",Desc="Deprecated (Use au.debug.SoundCues): Shows active SoundCues") +-ManualAutoCompleteList=(Command="Stat SoundMixes",Desc="Deprecated (Use au.debug.SoundMixes): Shows active SoundMixes") +-ManualAutoCompleteList=(Command="Stat SoundModulators",Desc="Deprecated (Use au.debug.SoundModulators): Shows modulator debug info as provided by active audio modulation plugin") +-ManualAutoCompleteList=(Command="Stat SoundModulatorsHelp",Desc="Deprecated (Use au.debug.SoundModulatorsHelp):Shows modulator debug help provided by active audio modulation plugin") +-ManualAutoCompleteList=(Command="Stat SoundReverb",Desc="Deprecated (Use au.debug.SoundReverb): Shows active SoundReverb") +-ManualAutoCompleteList=(Command="Stat SoundWaves",Desc="Deprecated (Use au.debug.SoundWaves): Shows active SoundWaves") +-ManualAutoCompleteList=(Command="Stat Sounds",Desc="Deprecated (Use au.debug.Sounds): <-debug> Shows all active sounds. Displays value sorted by when sort is set") +-ManualAutoCompleteList=(Command="ScriptAudit LongestFunctions",Desc="List functions that contain the most bytecode - optionally include # of entries to list") +-ManualAutoCompleteList=(Command="ScriptAudit FrequentFunctionsCalled",Desc="List functions that are most frequently called from bytecode - optionally include # of entries to list") +-ManualAutoCompleteList=(Command="ScriptAudit FrequentInstructions",Desc="List most frequently used instructions - optionally include # of entries to list") +-ManualAutoCompleteList=(Command="ScriptAudit TotalBytecodeSize",Desc="Gather total size of bytecode in bytes of currently loaded functions") +-ManualAutoCompleteList=(Command="Audio3dVisualize",Desc="Shows locations of sound sources playing (white text) and their left and right channel locations respectively (red and green). Virtualized loops (if enabled) display in blue.") +-ManualAutoCompleteList=(Command="StartMovieCapture",Desc=) +-ManualAutoCompleteList=(Command="StopMovieCapture",Desc=) +-ManualAutoCompleteList=(Command="TraceTag",Desc="Draw traces that use the specified tag") +-ManualAutoCompleteList=(Command="TraceTagAll",Desc="Draw all scene queries regardless of the trace tag") +-ManualAutoCompleteList=(Command="VisLog",Desc="Launches Log Visualizer tool") +-ManualAutoCompleteList=(Command="CycleNavDrawn",Desc="Cycles through navigation data (navmeshes for example) to draw one at a time") +-ManualAutoCompleteList=(Command="Log ",Desc=" Change verbosity level for a log category") +-ManualAutoCompleteList=(Command="Log list",Desc=" Search for log categories") +-ManualAutoCompleteList=(Command="Log reset",Desc="Undo any changes to log verbosity") +-ManualAutoCompleteList=(Command="RecordAnimation ActorName AnimName",Desc="Record animation for a specified actor to the specified file") +-ManualAutoCompleteList=(Command="StopRecordingAnimation All",Desc="Stop all recording animations") +-ManualAutoCompleteList=(Command="RecordSequence Filter ActorOrClassName",Desc="Record a level sequence from gameplay. Filter=") +-ManualAutoCompleteList=(Command="StopRecordingSequence",Desc="Stop recording the current sequence. Only one sequence recording can be active at one time.") +-ManualAutoCompleteList=(Command="RecordTake Filter ActorOrClassName",Desc="Record a level sequence from gameplay. Filter=") +-ManualAutoCompleteList=(Command="StopRecordingTake",Desc="Stop recording the current sequence. Only one sequence recording can be active at one time.") +-ManualAutoCompleteList=(Command="FreezeRendering",Desc="Toggle freezing of most aspects of rendering (such as visibility calculations), useful in conjunction with ToggleDebugCamera to fly around and see how frustum and occlusion culling is working") +-ManualAutoCompleteList=(Command="ProfileGPU",Desc="Profile one frame of rendering commands sent to the GPU") +-ManualAutoCompleteList=(Command="ProfileGPUHitches",Desc="Toggle profiling of GPU hitches.") +-ManualAutoCompleteList=(Command="DumpGPU",Desc="Dump one frame of rendering intermediary resources to disk.") +-ManualAutoCompleteList=(Command="Automation",Desc="Run an automation command (e.g., Automation RunTests TestName)") +-ManualAutoCompleteList=(Command="CsvProfile Start",Desc="Start CSV profiling.") +-ManualAutoCompleteList=(Command="CsvProfile Stop",Desc="Stop CSV profiling.") +-ManualAutoCompleteList=(Command="NetProfile Enable",Desc="Start network profiling.") +-ManualAutoCompleteList=(Command="NetProfile Disable",Desc="Stop network profiling.") ++ManualAutoCompleteList=(Command="Exit",Desc="Exits the game") ++ManualAutoCompleteList=(Command="DebugCreatePlayer 1",Desc="") ++ManualAutoCompleteList=(Command="ToggleDrawEvents",Desc="Toggles annotations for shader debugging with Pix, Razor or similar GPU capture tools") ++ManualAutoCompleteList=(Command="Shot",Desc="Make a screenshot") ++ManualAutoCompleteList=(Command="RecompileShaders changed",Desc="Recompile shaders that have any changes on their source files") ++ManualAutoCompleteList=(Command="RecompileShaders global",Desc="Recompile global shaders that have any changes on their source files") ++ManualAutoCompleteList=(Command="RecompileShaders material ",Desc="Recompile shaders for a specific material if it\'s source files have changed") ++ManualAutoCompleteList=(Command="RecompileShaders all",Desc="Recompile all shaders that have any changes on their source files") ++ManualAutoCompleteList=(Command="Debug Crash",Desc="Simulates a game thread crash for debugging") ++ManualAutoCompleteList=(Command="Debug RenderCrash",Desc="Simulates a render thread crash for debugging") ++ManualAutoCompleteList=(Command="DumpMaterialStats",Desc="Dump material information") ++ManualAutoCompleteList=(Command="DumpShaderStats",Desc="Dump shader information") ++ManualAutoCompleteList=(Command="DumpShaderPipelineStats",Desc="Dump shader pipeline information") ++ManualAutoCompleteList=(Command="DumpShaderCompileStats",Desc="Dump shader compilation information") ++ManualAutoCompleteList=(Command="DumpGPU -upload",Desc="Dump the GPU\'s intermediary resources and upload to network") ++ManualAutoCompleteList=(Command="StartFPSChart",Desc="after that use StopFPSChart") ++ManualAutoCompleteList=(Command="StopFPSChart",Desc="after that look for the output in Saved/Profiling/FPSChartStats") ++ManualAutoCompleteList=(Command="FreezeAt",Desc="Locks the player view and rendering time.") ++ManualAutoCompleteList=(Command="Open",Desc=" Opens the specified map, doesn\'t pass previously set options") ++ManualAutoCompleteList=(Command="Travel",Desc=" Travels to the specified map, passes along previously set options") ++ManualAutoCompleteList=(Command="ServerTravel",Desc=" Travels to the specified map and brings clients along, passes along previously set options") ++ManualAutoCompleteList=(Command="DisplayAll",Desc=" Display property values for instances of classname") ++ManualAutoCompleteList=(Command="DisplayAllLocation",Desc=" Display location for all instances of classname") ++ManualAutoCompleteList=(Command="DisplayAllRotation",Desc=" Display rotation for all instances of classname") ++ManualAutoCompleteList=(Command="DisplayClear",Desc="Clears previous DisplayAll entries") ++ManualAutoCompleteList=(Command="FlushPersistentDebugLines",Desc="Clears persistent debug line cache") ++ManualAutoCompleteList=(Command="GetAll ",Desc=" Log property values of all instances of classname") ++ManualAutoCompleteList=(Command="GetAllLocation",Desc=" Log location for all instances of classname") ++ManualAutoCompleteList=(Command="GetAllRotation",Desc=" Log rotation for all instances of classname") ++ManualAutoCompleteList=(Command="Obj List ",Desc=" ") ++ManualAutoCompleteList=(Command="Obj ListContentRefs",Desc=" ") ++ManualAutoCompleteList=(Command="Obj Classes",Desc="Shows all classes") ++ManualAutoCompleteList=(Command="Obj Refs",Desc="Name= [Shortest] [Longest] [All] [External] [Direct] [Full] [Minimal] [GCOnly] [History=] Lists referencers of the specified object") ++ManualAutoCompleteList=(Command="Obj GC",Desc="Runs the UObject Garbage Collector and resets the GC timer.") ++ManualAutoCompleteList=(Command="Obj Dump ",Desc=" [Recurse or Hide/Show=\"category1,...\"] or [Recurse or Hide/Show=\"category1,...\"] Prints the value of all variables for the specified object") ++ManualAutoCompleteList=(Command="EditActor",Desc=" or or TRACE") ++ManualAutoCompleteList=(Command="EditDefault",Desc="") ++ManualAutoCompleteList=(Command="EditObject",Desc=" or or ") ++ManualAutoCompleteList=(Command="ReloadCfg ",Desc=" Reloads config variables for the specified object/class") ++ManualAutoCompleteList=(Command="ReloadLoc ",Desc=" Reloads localized variables for the specified object/class") ++ManualAutoCompleteList=(Command="Set ",Desc=" Sets property to value on objectname") ++ManualAutoCompleteList=(Command="SetNoPEC",Desc=" Sets property to value on objectname without Pre/Post Edit Change notifications") ++ManualAutoCompleteList=(Command="Stat FPS",Desc="Shows FPS counter") ++ManualAutoCompleteList=(Command="Stat UNIT",Desc="Shows hardware unit framerate") ++ManualAutoCompleteList=(Command="Stat DrawCount",Desc="Shows draw counts broken down by category") ++ManualAutoCompleteList=(Command="Stat UnitGraph",Desc="Draws simple unit time graph") ++ManualAutoCompleteList=(Command="Stat NamedEvents",Desc="Stat NamedEvents (Enables named events for external profilers)") ++ManualAutoCompleteList=(Command="Stat VerboseNamedEvents",Desc="Stat VerboseNamedEvents (Enables verbose named events for external profilers)") ++ManualAutoCompleteList=(Command="Stat StartFile",Desc="Stat StartFile (starts a stats capture, creating a new file in the Profiling directory; stop with stat StopFile to close the file)") ++ManualAutoCompleteList=(Command="Stat StopFile",Desc="Stat StopFile (finishes a stats capture started by stat StartFile)") ++ManualAutoCompleteList=(Command="Stat CPULoad",Desc="Stat CPULoad (Shows CPU Utilization)") ++ManualAutoCompleteList=(Command="Stat DUMPHITCHES",Desc="executes dumpstats on hitches - see log") ++ManualAutoCompleteList=(Command="stat D3D11 RHI",Desc="Shows Direct3D 11 stats") ++ManualAutoCompleteList=(Command="Stat LEVELS",Desc="Displays level streaming info") ++ManualAutoCompleteList=(Command="Stat GAME",Desc="Displays game performance stats") ++ManualAutoCompleteList=(Command="Stat MEMORY",Desc="Displays memory stats") ++ManualAutoCompleteList=(Command="Stat PHYSICS",Desc="Displays physics performance stats") ++ManualAutoCompleteList=(Command="Stat STREAMING",Desc="Displays basic texture streaming stats") ++ManualAutoCompleteList=(Command="Stat STREAMINGDETAILS",Desc="Displays detailed texture streaming stats") ++ManualAutoCompleteList=(Command="Stat GPU",Desc="Displays GPU stats for the frame") ++ManualAutoCompleteList=(Command="Stat COLLISION",Desc="") ++ManualAutoCompleteList=(Command="Stat PARTICLES",Desc="") ++ManualAutoCompleteList=(Command="Stat SCRIPT",Desc="") ++ManualAutoCompleteList=(Command="Stat AUDIO",Desc="") ++ManualAutoCompleteList=(Command="Stat ANIM",Desc="") ++ManualAutoCompleteList=(Command="Stat NET",Desc="") ++ManualAutoCompleteList=(Command="Stat LIST",Desc=" List groups of stats, saved sets, or specific stats within a specified group") ++ManualAutoCompleteList=(Command="Stat splitscreen",Desc="") ++ManualAutoCompleteList=(Command="MemReport",Desc="Outputs memory stats to a profile file. -Full gives more data, -Log outputs to the log") ++ManualAutoCompleteList=(Command="ListTextures",Desc="[Streaming|NonStreaming|Forced] [-Alphasort] [-csv] Lists all loaded textures and their current memory footprint") ++ManualAutoCompleteList=(Command="ListStreamingTextures",Desc="Lists info for all streaming textures") ++ManualAutoCompleteList=(Command="ListAnims",Desc="Lists info for all animations") ++ManualAutoCompleteList=(Command="ListSkeletalMeshes",Desc="Lists info for all skeletal meshes") ++ManualAutoCompleteList=(Command="ListStaticMeshes",Desc="Lists info for all static meshes") ++ManualAutoCompleteList=(Command="InvestigateTexture",Desc="Shows streaming info about the specified texture") ++ManualAutoCompleteList=(Command="DumpTextureStreamingStats",Desc="Dump current streaming stats (e.g. pool capacity) to the log") ++ManualAutoCompleteList=(Command="RestartLevel",Desc="Restarts the level") ++ManualAutoCompleteList=(Command="Module List",Desc="Lists all known modules") ++ManualAutoCompleteList=(Command="Module Load",Desc="Attempts to load the specified module name") ++ManualAutoCompleteList=(Command="Module Unload",Desc="Unloads the specified module name") ++ManualAutoCompleteList=(Command="Module Reload",Desc="Reloads the specified module name, unloading it first if needed") ++ManualAutoCompleteList=(Command="Module Recompile",Desc="Attempts to recompile a module, first unloading it if needed") ++ManualAutoCompleteList=(Command="HotReload",Desc=" Attempts to recompile a UObject DLL and reload it on the fly") ++ManualAutoCompleteList=(Command="au.debug.AudioDebugSound",Desc=" Rejects new USoundBase requests where the sound name does not contain the provided filter") ++ManualAutoCompleteList=(Command="au.debug.AudioGetDynamicSoundVolume",Desc="Gets volume for given sound type (\'Class\', \'Cue\' or \'Wave\') with provided name") ++ManualAutoCompleteList=(Command="au.debug.AudioMemReport",Desc="Lists info for audio memory") ++ManualAutoCompleteList=(Command="au.debug.AudioMixerDebugSound",Desc=" With new mixer enabled, rejects new USoundBase requests where the sound name does not contain the provided filter") ++ManualAutoCompleteList=(Command="au.debug.AudioResetAllDynamicSoundVolumes",Desc="Resets all dynamic volumes to unity") ++ManualAutoCompleteList=(Command="au.debug.AudioResetDynamicSoundVolume",Desc="Resets volume for given sound type (\'Class\', \'Cue\' or \'Wave\') with provided name to unity") ++ManualAutoCompleteList=(Command="au.debug.AudioSetDynamicSoundVolume",Desc="Name= Type= Vol= Sets volume for given sound type (\'Class\', \'Cue\' or \'Wave\') with provided name") ++ManualAutoCompleteList=(Command="au.debug.AudioSoloSoundClass",Desc=" [nonexclusive] Solos sounds using this USoundClass. If nonexclusive, existing solos will persist") ++ManualAutoCompleteList=(Command="au.debug.AudioSoloSoundCue",Desc=" [nonexclusive] Solos any type of USoundBase. If nonexclusive, existing solos will persist") ++ManualAutoCompleteList=(Command="au.debug.AudioSoloSoundWave",Desc=" [nonexclusive] Solos USoundWave. If nonexclusive, existing solos will persist") ++ManualAutoCompleteList=(Command="au.debug.ClearSoloAudio",Desc="Clears solo\'ed object") ++ManualAutoCompleteList=(Command="au.debug.DisableLPF",Desc="Disables low-pass filter") ++ManualAutoCompleteList=(Command="au.debug.DisableEQFilter",Desc="Disables EQ") ++ManualAutoCompleteList=(Command="au.debug.DisableRadio",Desc="Disables radio effect") ++ManualAutoCompleteList=(Command="au.debug.DumpSoundInfo",Desc="Dumps sound information to log") ++ManualAutoCompleteList=(Command="au.debug.EnableRadio",Desc="Enables radio effect") ++ManualAutoCompleteList=(Command="au.debug.IsolateDryAudio",Desc="Isolates dry audio") ++ManualAutoCompleteList=(Command="au.debug.IsolateReverb",Desc="Isolates reverb") ++ManualAutoCompleteList=(Command="au.debug.ListAudioComponents",Desc="Dumps a detailed list of all AudioComponent objects") ++ManualAutoCompleteList=(Command="au.debug.ListSoundClasses",Desc="Lists a summary of loaded sound collated by class") ++ManualAutoCompleteList=(Command="au.debug.ListSoundClassVolumes",Desc="Lists all sound class volumes") ++ManualAutoCompleteList=(Command="au.debug.ListSoundDurations",Desc="Lists durations of all active sounds") ++ManualAutoCompleteList=(Command="au.debug.ListWaves",Desc="List the WaveInstances and whether they have a source") ++ManualAutoCompleteList=(Command="au.debug.PlayAllPIEAudio",Desc="Toggls whether or not all devices should play their audio") ++ManualAutoCompleteList=(Command="au.debug.PlaySoundCue",Desc="Plays the given soundcue") ++ManualAutoCompleteList=(Command="au.debug.PlaySoundWave",Desc=" Plays the given soundwave") ++ManualAutoCompleteList=(Command="au.debug.ResetSoundState",Desc="Resets volumes to default and removes test filters") ++ManualAutoCompleteList=(Command="au.debug.SetBaseSoundMix",Desc=" Sets the base sound mix") ++ManualAutoCompleteList=(Command="au.debug.ShowSoundClassHierarchy",Desc="") ++ManualAutoCompleteList=(Command="au.debug.SoloAudio",Desc="Solos the audio device associated with the parent world") ++ManualAutoCompleteList=(Command="au.debug.SoundClassFixup",Desc="Deprecated") ++ManualAutoCompleteList=(Command="au.debug.TestLFEBleed",Desc="Sets LPF to max for all sources") ++ManualAutoCompleteList=(Command="au.debug.TestLPF",Desc="Sets LPF to max for all sources") ++ManualAutoCompleteList=(Command="au.debug.TestStereoBleed",Desc="Test bleeding stereo sounds fully to the rear speakers") ++ManualAutoCompleteList=(Command="au.debug.ToggleHRTFForAll",Desc="Toggles whether or not HRTF spatialization is enabled for all") ++ManualAutoCompleteList=(Command="au.debug.ToggleSpatExt",Desc="Toggles enablement of the Spatial Audio Extension") ++ManualAutoCompleteList=(Command="DisableAllScreenMessages",Desc="Disables all on-screen warnings/messages") ++ManualAutoCompleteList=(Command="EnableAllScreenMessages",Desc="Enables all on-screen warnings/messages") ++ManualAutoCompleteList=(Command="ToggleAllScreenMessages",Desc="Toggles display state of all on-screen warnings/messages") ++ManualAutoCompleteList=(Command="ToggleAsyncCompute",Desc="Toggles AsyncCompute for platforms that have it") ++ManualAutoCompleteList=(Command="ToggleRenderingThread",Desc="Toggles the rendering thread for platforms that have it") ++ManualAutoCompleteList=(Command="CaptureMode",Desc="Toggles display state of all on-screen warnings/messages") ++ManualAutoCompleteList=(Command="ShowDebug None",Desc="Toggles ShowDebug w/ current debug type selection") ++ManualAutoCompleteList=(Command="ShowDebug Reset",Desc="Turns off ShowDebug, and clears debug type selection") ++ManualAutoCompleteList=(Command="ShowDebug NET",Desc="") ++ManualAutoCompleteList=(Command="ShowDebug PHYSICS",Desc="") ++ManualAutoCompleteList=(Command="ShowDebug COLLISION",Desc="") ++ManualAutoCompleteList=(Command="ShowDebug AI",Desc="") ++ManualAutoCompleteList=(Command="ShowDebug CAMERA",Desc="") ++ManualAutoCompleteList=(Command="ShowDebug WEAPON",Desc="") ++ManualAutoCompleteList=(Command="ShowDebug ANIMATION",Desc="Toggles display state of animation debug data") ++ManualAutoCompleteList=(Command="ShowDebug BONES",Desc="Toggles display of skeletalmesh bones") ++ManualAutoCompleteList=(Command="ShowDebug INPUT",Desc="") ++ManualAutoCompleteList=(Command="ShowDebug FORCEFEEDBACK",Desc="Toggles display of current force feedback values and what is contributing to that calculation") ++ManualAutoCompleteList=(Command="ShowDebugToggleSubCategory 3DBONES",Desc="With ShowDebug Bones: Toggles bone rendering between single lines and a more complex 3D model per bone") ++ManualAutoCompleteList=(Command="ShowDebugToggleSubCategory SYNCGROUPS",Desc="With ShowDebug Animation: Toggles display of sync group data") ++ManualAutoCompleteList=(Command="ShowDebugToggleSubCategory MONTAGES",Desc="With ShowDebug Animation: Toggles display of montage data") ++ManualAutoCompleteList=(Command="ShowDebugToggleSubCategory GRAPH",Desc="With ShowDebug Animation: Toggles display of animation blueprint graph") ++ManualAutoCompleteList=(Command="ShowDebugToggleSubCategory CURVES",Desc="With ShowDebug Animation: Toggles display of curve data") ++ManualAutoCompleteList=(Command="ShowDebugToggleSubCategory NOTIFIES",Desc="With ShowDebug Animation: Toggles display of notify data") ++ManualAutoCompleteList=(Command="ShowDebugToggleSubCategory FULLGRAPH",Desc="With ShowDebug Animation: Toggles graph display between active nodes only and all nodes") ++ManualAutoCompleteList=(Command="ShowDebugToggleSubCategory FULLBLENDSPACEDISPLAY",Desc="With ShowDebug Animation: Toggles display of sample blend weights on blendspaces") ++ManualAutoCompleteList=(Command="ShowDebugForReticleTargetToggle ",Desc=" Toggles \'ShowDebug\' from showing debug info between reticle target actor (of subclass ) and camera view target") ++ManualAutoCompleteList=(Command="Stat SoundCues",Desc="Deprecated (Use au.debug.SoundCues): Shows active SoundCues") ++ManualAutoCompleteList=(Command="Stat SoundMixes",Desc="Deprecated (Use au.debug.SoundMixes): Shows active SoundMixes") ++ManualAutoCompleteList=(Command="Stat SoundModulators",Desc="Deprecated (Use au.debug.SoundModulators): Shows modulator debug info as provided by active audio modulation plugin") ++ManualAutoCompleteList=(Command="Stat SoundModulatorsHelp",Desc="Deprecated (Use au.debug.SoundModulatorsHelp):Shows modulator debug help provided by active audio modulation plugin") ++ManualAutoCompleteList=(Command="Stat SoundReverb",Desc="Deprecated (Use au.debug.SoundReverb): Shows active SoundReverb") ++ManualAutoCompleteList=(Command="Stat SoundWaves",Desc="Deprecated (Use au.debug.SoundWaves): Shows active SoundWaves") ++ManualAutoCompleteList=(Command="Stat Sounds",Desc="Deprecated (Use au.debug.Sounds): <-debug> Shows all active sounds. Displays value sorted by when sort is set") ++ManualAutoCompleteList=(Command="ScriptAudit LongestFunctions",Desc="List functions that contain the most bytecode - optionally include # of entries to list") ++ManualAutoCompleteList=(Command="ScriptAudit FrequentFunctionsCalled",Desc="List functions that are most frequently called from bytecode - optionally include # of entries to list") ++ManualAutoCompleteList=(Command="ScriptAudit FrequentInstructions",Desc="List most frequently used instructions - optionally include # of entries to list") ++ManualAutoCompleteList=(Command="ScriptAudit TotalBytecodeSize",Desc="Gather total size of bytecode in bytes of currently loaded functions") ++ManualAutoCompleteList=(Command="Audio3dVisualize",Desc="Shows locations of sound sources playing (white text) and their left and right channel locations respectively (red and green). Virtualized loops (if enabled) display in blue.") ++ManualAutoCompleteList=(Command="StartMovieCapture",Desc="") ++ManualAutoCompleteList=(Command="StopMovieCapture",Desc="") ++ManualAutoCompleteList=(Command="TraceTag",Desc="Draw traces that use the specified tag") ++ManualAutoCompleteList=(Command="TraceTagAll",Desc="Draw all scene queries regardless of the trace tag") ++ManualAutoCompleteList=(Command="VisLog",Desc="Launches Log Visualizer tool") ++ManualAutoCompleteList=(Command="CycleNavDrawn",Desc="Cycles through navigation data (navmeshes for example) to draw one at a time") ++ManualAutoCompleteList=(Command="Log ",Desc=" Change verbosity level for a log category") ++ManualAutoCompleteList=(Command="Log list",Desc=" Search for log categories") ++ManualAutoCompleteList=(Command="Log reset",Desc="Undo any changes to log verbosity") ++ManualAutoCompleteList=(Command="RecordAnimation ActorName AnimName",Desc="Record animation for a specified actor to the specified file") ++ManualAutoCompleteList=(Command="StopRecordingAnimation All",Desc="Stop all recording animations") ++ManualAutoCompleteList=(Command="RecordSequence Filter ActorOrClassName",Desc="Record a level sequence from gameplay. Filter=") ++ManualAutoCompleteList=(Command="StopRecordingSequence",Desc="Stop recording the current sequence. Only one sequence recording can be active at one time.") ++ManualAutoCompleteList=(Command="RecordTake Filter ActorOrClassName",Desc="Record a level sequence from gameplay. Filter=") ++ManualAutoCompleteList=(Command="StopRecordingTake",Desc="Stop recording the current sequence. Only one sequence recording can be active at one time.") ++ManualAutoCompleteList=(Command="FreezeRendering",Desc="Toggle freezing of most aspects of rendering (such as visibility calculations), useful in conjunction with ToggleDebugCamera to fly around and see how frustum and occlusion culling is working") ++ManualAutoCompleteList=(Command="ProfileGPU",Desc="Profile one frame of rendering commands sent to the GPU") ++ManualAutoCompleteList=(Command="ProfileGPUHitches",Desc="Toggle profiling of GPU hitches.") ++ManualAutoCompleteList=(Command="DumpGPU",Desc="Dump one frame of rendering intermediary resources to disk.") ++ManualAutoCompleteList=(Command="Automation",Desc="Run an automation command (e.g., Automation RunTests TestName)") ++ManualAutoCompleteList=(Command="CsvProfile Start",Desc="Start CSV profiling.") ++ManualAutoCompleteList=(Command="CsvProfile Stop",Desc="Stop CSV profiling.") ++ManualAutoCompleteList=(Command="NetProfile Enable",Desc="Start network profiling.") ++ManualAutoCompleteList=(Command="NetProfile Disable",Desc="Stop network profiling.") ++ManualAutoCompleteList=(Command="ShowDebug EnhancedInput",Desc="Displays debug information about the current state of any Enhanced Input Mapping Contexts") ++ManualAutoCompleteList=(Command="ShowDebug WorldSubsystemInput",Desc="Displays debug information about the current state of any Enhanced Input Mapping Contexts applied to the Enhanced Input world subsystem.") ++ManualAutoCompleteList=(Command="ShowDebug InputSettings",Desc="Displays debug information any user input settings, such as player mappable keys.") ++ManualAutoCompleteList=(Command="ShowDebug DeviceProperty",Desc="Display debug information about currently active Input Device Properties") ++ManualAutoCompleteList=(Command="ShowDebug Devices",Desc="Displays all current Platform Users and their Input Devices.") +-AutoCompleteMapPaths=Content/Maps ++AutoCompleteMapPaths=Content/Maps +BackgroundOpacityPercentage=85.000000 +bOrderTopToBottom=False +bDisplayHelpInAutoComplete=True +InputColor=(B=230,G=230,R=230,A=255) +HistoryColor=(B=180,G=180,R=180,A=255) +AutoCompleteCommandColor=(B=185,G=109,R=144,A=255) +AutoCompleteCVarColor=(B=86,G=158,R=86,A=255) +AutoCompleteFadedColor=(B=100,G=100,R=100,A=255) + diff --git a/tests/unreal/Content/BP_GMTesting.uasset b/tests/unreal/Content/BP_GMTesting.uasset new file mode 100644 index 00000000..fbfb4000 Binary files /dev/null and b/tests/unreal/Content/BP_GMTesting.uasset differ diff --git a/tests/unreal/Content/Input/IA_Exit.uasset b/tests/unreal/Content/Input/IA_Exit.uasset new file mode 100644 index 00000000..f2731185 Binary files /dev/null and b/tests/unreal/Content/Input/IA_Exit.uasset differ diff --git a/tests/unreal/Content/Input/IA_ToggleMenu.uasset b/tests/unreal/Content/Input/IA_ToggleMenu.uasset new file mode 100644 index 00000000..d9a01491 Binary files /dev/null and b/tests/unreal/Content/Input/IA_ToggleMenu.uasset differ diff --git a/tests/unreal/Content/Input/IMC_Testing.uasset b/tests/unreal/Content/Input/IMC_Testing.uasset new file mode 100644 index 00000000..350572ec Binary files /dev/null and b/tests/unreal/Content/Input/IMC_Testing.uasset differ diff --git a/tests/unreal/Content/PlayerControllers/TestingPlayerController.uasset b/tests/unreal/Content/PlayerControllers/TestingPlayerController.uasset new file mode 100644 index 00000000..94bba105 Binary files /dev/null and b/tests/unreal/Content/PlayerControllers/TestingPlayerController.uasset differ diff --git a/tests/unreal/Content/RiveMaterial.uasset b/tests/unreal/Content/RiveMaterial.uasset new file mode 100644 index 00000000..430cd76f Binary files /dev/null and b/tests/unreal/Content/RiveMaterial.uasset differ diff --git a/tests/unreal/Content/RiveTest.uasset b/tests/unreal/Content/RiveTest.uasset new file mode 100644 index 00000000..3c6ea750 Binary files /dev/null and b/tests/unreal/Content/RiveTest.uasset differ diff --git a/tests/unreal/Content/Rivs/Rope.uasset b/tests/unreal/Content/Rivs/Rope.uasset new file mode 100644 index 00000000..6ba0b405 Binary files /dev/null and b/tests/unreal/Content/Rivs/Rope.uasset differ diff --git a/tests/unreal/Content/Rivs/Rope_Texture.uasset b/tests/unreal/Content/Rivs/Rope_Texture.uasset new file mode 100644 index 00000000..df661dd8 Binary files /dev/null and b/tests/unreal/Content/Rivs/Rope_Texture.uasset differ diff --git a/tests/unreal/Content/Rivs/Rope_Widget.uasset b/tests/unreal/Content/Rivs/Rope_Widget.uasset new file mode 100644 index 00000000..27a44dfb Binary files /dev/null and b/tests/unreal/Content/Rivs/Rope_Widget.uasset differ diff --git a/tests/unreal/Content/Rivs/cute_robot.uasset b/tests/unreal/Content/Rivs/cute_robot.uasset new file mode 100644 index 00000000..56e33f4a Binary files /dev/null and b/tests/unreal/Content/Rivs/cute_robot.uasset differ diff --git a/tests/unreal/Content/Rivs/cute_robot_Texture.uasset b/tests/unreal/Content/Rivs/cute_robot_Texture.uasset new file mode 100644 index 00000000..4a2b1f94 Binary files /dev/null and b/tests/unreal/Content/Rivs/cute_robot_Texture.uasset differ diff --git a/tests/unreal/Content/Rivs/cute_robot_Widget.uasset b/tests/unreal/Content/Rivs/cute_robot_Widget.uasset new file mode 100644 index 00000000..e283361a Binary files /dev/null and b/tests/unreal/Content/Rivs/cute_robot_Widget.uasset differ diff --git a/tests/unreal/Content/Rivs/falling.uasset b/tests/unreal/Content/Rivs/falling.uasset new file mode 100644 index 00000000..debdb625 Binary files /dev/null and b/tests/unreal/Content/Rivs/falling.uasset differ diff --git a/tests/unreal/Content/Rivs/falling_Texture.uasset b/tests/unreal/Content/Rivs/falling_Texture.uasset new file mode 100644 index 00000000..0a83450a Binary files /dev/null and b/tests/unreal/Content/Rivs/falling_Texture.uasset differ diff --git a/tests/unreal/Content/Rivs/falling_Widget.uasset b/tests/unreal/Content/Rivs/falling_Widget.uasset new file mode 100644 index 00000000..78bbfac9 Binary files /dev/null and b/tests/unreal/Content/Rivs/falling_Widget.uasset differ diff --git a/tests/unreal/Content/Rivs/runner.uasset b/tests/unreal/Content/Rivs/runner.uasset new file mode 100644 index 00000000..42629e3b Binary files /dev/null and b/tests/unreal/Content/Rivs/runner.uasset differ diff --git a/tests/unreal/Content/Rivs/runner_Texture.uasset b/tests/unreal/Content/Rivs/runner_Texture.uasset new file mode 100644 index 00000000..6376b943 Binary files /dev/null and b/tests/unreal/Content/Rivs/runner_Texture.uasset differ diff --git a/tests/unreal/Content/Rivs/runner_Widget.uasset b/tests/unreal/Content/Rivs/runner_Widget.uasset new file mode 100644 index 00000000..2ba9a912 Binary files /dev/null and b/tests/unreal/Content/Rivs/runner_Widget.uasset differ diff --git a/tests/unreal/Content/Rivs/stopwatch.uasset b/tests/unreal/Content/Rivs/stopwatch.uasset new file mode 100644 index 00000000..16eeb126 Binary files /dev/null and b/tests/unreal/Content/Rivs/stopwatch.uasset differ diff --git a/tests/unreal/Content/Rivs/stopwatch_Widget.uasset b/tests/unreal/Content/Rivs/stopwatch_Widget.uasset new file mode 100644 index 00000000..ea45e5ba Binary files /dev/null and b/tests/unreal/Content/Rivs/stopwatch_Widget.uasset differ diff --git a/tests/unreal/Content/Rivs/tape.uasset b/tests/unreal/Content/Rivs/tape.uasset new file mode 100644 index 00000000..aebaa56c Binary files /dev/null and b/tests/unreal/Content/Rivs/tape.uasset differ diff --git a/tests/unreal/Content/Rivs/tape_Widget.uasset b/tests/unreal/Content/Rivs/tape_Widget.uasset new file mode 100644 index 00000000..9e10368f Binary files /dev/null and b/tests/unreal/Content/Rivs/tape_Widget.uasset differ diff --git a/tests/unreal/Content/Rivs/tiger.uasset b/tests/unreal/Content/Rivs/tiger.uasset new file mode 100644 index 00000000..df84ce59 Binary files /dev/null and b/tests/unreal/Content/Rivs/tiger.uasset differ diff --git a/tests/unreal/Content/Rivs/tiger_Widget.uasset b/tests/unreal/Content/Rivs/tiger_Widget.uasset new file mode 100644 index 00000000..ef1bbb73 Binary files /dev/null and b/tests/unreal/Content/Rivs/tiger_Widget.uasset differ diff --git a/tests/unreal/Content/Widgets/BPW_GMView.uasset b/tests/unreal/Content/Widgets/BPW_GMView.uasset new file mode 100644 index 00000000..10670c45 Binary files /dev/null and b/tests/unreal/Content/Widgets/BPW_GMView.uasset differ diff --git a/tests/unreal/Content/Widgets/BPW_GM_Name.uasset b/tests/unreal/Content/Widgets/BPW_GM_Name.uasset new file mode 100644 index 00000000..d568f0a4 Binary files /dev/null and b/tests/unreal/Content/Widgets/BPW_GM_Name.uasset differ diff --git a/tests/unreal/Content/Widgets/BPW_Golden_Name.uasset b/tests/unreal/Content/Widgets/BPW_Golden_Name.uasset new file mode 100644 index 00000000..89bdc18d Binary files /dev/null and b/tests/unreal/Content/Widgets/BPW_Golden_Name.uasset differ diff --git a/tests/unreal/Content/Widgets/BPW_TestingMain.uasset b/tests/unreal/Content/Widgets/BPW_TestingMain.uasset new file mode 100644 index 00000000..5f759036 Binary files /dev/null and b/tests/unreal/Content/Widgets/BPW_TestingMain.uasset differ diff --git a/tests/unreal/Content/Widgets/GM_Testing.uasset b/tests/unreal/Content/Widgets/GM_Testing.uasset new file mode 100644 index 00000000..c13bfda5 Binary files /dev/null and b/tests/unreal/Content/Widgets/GM_Testing.uasset differ diff --git a/tests/unreal/Content/Widgets/GM_Widget.uasset b/tests/unreal/Content/Widgets/GM_Widget.uasset new file mode 100644 index 00000000..776c2404 Binary files /dev/null and b/tests/unreal/Content/Widgets/GM_Widget.uasset differ diff --git a/tests/unreal/Content/Widgets/TestingPlayerController.uasset b/tests/unreal/Content/Widgets/TestingPlayerController.uasset new file mode 100644 index 00000000..318e4a87 Binary files /dev/null and b/tests/unreal/Content/Widgets/TestingPlayerController.uasset differ diff --git a/tests/unreal/Content/__ExternalActors__/maps/TestMap/0/2T/Y00052K48AUG3EY3VYT6FF.uasset b/tests/unreal/Content/__ExternalActors__/maps/TestMap/0/2T/Y00052K48AUG3EY3VYT6FF.uasset new file mode 100644 index 00000000..81fede82 Binary files /dev/null and b/tests/unreal/Content/__ExternalActors__/maps/TestMap/0/2T/Y00052K48AUG3EY3VYT6FF.uasset differ diff --git a/tests/unreal/Content/__ExternalActors__/maps/TestMap/3/R6/3X6HMINOJZUXS43JV8VF1C.uasset b/tests/unreal/Content/__ExternalActors__/maps/TestMap/3/R6/3X6HMINOJZUXS43JV8VF1C.uasset new file mode 100644 index 00000000..907acb77 Binary files /dev/null and b/tests/unreal/Content/__ExternalActors__/maps/TestMap/3/R6/3X6HMINOJZUXS43JV8VF1C.uasset differ diff --git a/tests/unreal/Content/__ExternalActors__/maps/TestMap/7/ZM/968UFKEC2VXYU2DGZG1EVU.uasset b/tests/unreal/Content/__ExternalActors__/maps/TestMap/7/ZM/968UFKEC2VXYU2DGZG1EVU.uasset new file mode 100644 index 00000000..5ba4e728 Binary files /dev/null and b/tests/unreal/Content/__ExternalActors__/maps/TestMap/7/ZM/968UFKEC2VXYU2DGZG1EVU.uasset differ diff --git a/tests/unreal/Content/__ExternalActors__/maps/TestMap/C/XS/M8C4ZBTE6XD6B7UYFFX7JQ.uasset b/tests/unreal/Content/__ExternalActors__/maps/TestMap/C/XS/M8C4ZBTE6XD6B7UYFFX7JQ.uasset new file mode 100644 index 00000000..652bb8a3 Binary files /dev/null and b/tests/unreal/Content/__ExternalActors__/maps/TestMap/C/XS/M8C4ZBTE6XD6B7UYFFX7JQ.uasset differ diff --git a/tests/unreal/Content/maps/TestMap.umap b/tests/unreal/Content/maps/TestMap.umap new file mode 100644 index 00000000..30ebf5c2 Binary files /dev/null and b/tests/unreal/Content/maps/TestMap.umap differ diff --git a/tests/unreal/Content/maps/TestMap_HLOD0_Instancing.uasset b/tests/unreal/Content/maps/TestMap_HLOD0_Instancing.uasset new file mode 100644 index 00000000..d646f188 Binary files /dev/null and b/tests/unreal/Content/maps/TestMap_HLOD0_Instancing.uasset differ diff --git a/tests/unreal/Content/maps/gm_gamemode.uasset b/tests/unreal/Content/maps/gm_gamemode.uasset new file mode 100644 index 00000000..39467f65 Binary files /dev/null and b/tests/unreal/Content/maps/gm_gamemode.uasset differ diff --git a/tests/unreal/Content/maps/gms.umap b/tests/unreal/Content/maps/gms.umap new file mode 100644 index 00000000..a620644e Binary files /dev/null and b/tests/unreal/Content/maps/gms.umap differ diff --git a/tests/unreal/Content/maps/goldens.umap b/tests/unreal/Content/maps/goldens.umap new file mode 100644 index 00000000..75488aac Binary files /dev/null and b/tests/unreal/Content/maps/goldens.umap differ diff --git a/tests/unreal/Plugins/GM/GM.uplugin b/tests/unreal/Plugins/GM/GM.uplugin new file mode 100644 index 00000000..d7ac9623 --- /dev/null +++ b/tests/unreal/Plugins/GM/GM.uplugin @@ -0,0 +1,30 @@ +{ + "FileVersion": 3, + "Version": 1, + "VersionName": "1.0", + "FriendlyName": "GM", + "Description": "Rive GM tests for unreal", + "Category": "Other", + "CreatedBy": "Rive Inc", + "CreatedByURL": "", + "DocsURL": "", + "MarketplaceURL": "", + "SupportURL": "", + "CanContainContent": true, + "IsBetaVersion": false, + "IsExperimentalVersion": false, + "Installed": false, + "Modules": [ + { + "Name": "GM", + "Type": "Runtime", + "LoadingPhase": "Default" + } + ], + "Plugins": [ + { + "Name": "Rive", + "Enabled": true + } + ] +} \ No newline at end of file diff --git a/tests/unreal/Plugins/GM/Resources/Icon128.png b/tests/unreal/Plugins/GM/Resources/Icon128.png new file mode 100644 index 00000000..1231d4aa Binary files /dev/null and b/tests/unreal/Plugins/GM/Resources/Icon128.png differ diff --git a/tests/unreal/Plugins/GM/Source/GM/GM.Build.cs b/tests/unreal/Plugins/GM/Source/GM/GM.Build.cs new file mode 100644 index 00000000..8ce45d81 --- /dev/null +++ b/tests/unreal/Plugins/GM/Source/GM/GM.Build.cs @@ -0,0 +1,66 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +using System.IO; +using UnrealBuildTool; + +public class GM : ModuleRules +{ + public GM(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; + + PublicIncludePaths.AddRange( + new string[] { + // ... add public include paths required here ... + } + ); + + var fullPluginPath = Path.GetFullPath(Path.Combine(PluginDirectory, "../")); + + PrivateIncludePaths.AddRange( + new string[] { + // ... add other private include paths required here ... + Path.Combine(fullPluginPath, "Rive", "Source", "RiveRenderer", "Private") + }); + + + PublicDependencyModuleNames.AddRange( + new string[] + { + "Core", + "CoreUObject", + "InputCore", + "Projects", + "RHI", + "RenderCore", + "Rive", + "RiveLibrary", + "RiveRenderer", + "Engine", + "GMLibrary", + // ... add other public dependencies that you statically link with here ... + } + ); + + #if UE_UE_5_0_OR_LATER + PublicDependencyModuleNames.Add("RHICore"); + #else + #endif + + + PrivateDependencyModuleNames.AddRange( + new string[] + { + // ... add private dependencies that you statically link with here ... + } + ); + + + DynamicallyLoadedModuleNames.AddRange( + new string[] + { + // ... add any modules that your module loads dynamically here ... + } + ); + } +} diff --git a/tests/unreal/Plugins/GM/Source/GM/Private/GM.cpp b/tests/unreal/Plugins/GM/Source/GM/Private/GM.cpp new file mode 100644 index 00000000..fec3a11b --- /dev/null +++ b/tests/unreal/Plugins/GM/Source/GM/Private/GM.cpp @@ -0,0 +1,22 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "GM.h" +#include "Misc/MessageDialog.h" +#include "Modules/ModuleManager.h" +#include "Interfaces/IPluginManager.h" +#include "Misc/Paths.h" +#include "HAL/PlatformProcess.h" +#include "gms.hpp" +#include "goldens.hpp" + +#define LOCTEXT_NAMESPACE "FGMModule" + +void FGMModule::StartupModule() {} + +void FGMModule::ShutdownModule() {} + +#undef LOCTEXT_NAMESPACE + +IMPLEMENT_MODULE(FGMModule, GM) + +DEFINE_LOG_CATEGORY(GM_Log); \ No newline at end of file diff --git a/tests/unreal/Plugins/GM/Source/GM/Private/GMTestingManager.cpp b/tests/unreal/Plugins/GM/Source/GM/Private/GMTestingManager.cpp new file mode 100644 index 00000000..1f3a8d45 --- /dev/null +++ b/tests/unreal/Plugins/GM/Source/GM/Private/GMTestingManager.cpp @@ -0,0 +1,527 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#include "GMTestingManager.h" + +#include "IRiveRenderer.h" +#include "IRiveRendererModule.h" +#include "Rive/RiveTextureObject.h" +#include "GM.h" +#include "goldens.hpp" +#if WITH_EDITOR +#include "Kismet/GameplayStatics.h" +#include "Kismet/KismetSystemLibrary.h" +#endif +#include "Platform/RenderContextRHIImpl.hpp" + +AGMTestingManager::AGMTestingManager() +{ + StaticMeshComponent = CreateDefaultSubobject(TEXT("StaticMesh")); + RootComponent = StaticMeshComponent; +} + +void AGMTestingManager::StartTesting() +{ + ENQUEUE_RENDER_COMMAND(GMGoldenMainCommand) + ([this](FRHICommandListImmediate& RHICmdList) { + FString CmdLine = FCommandLine::GetOriginal(); + UE_LOG(GM_Log, + Display, + TEXT("AGMTestingManager::StartTesting() Start testing with command line " + "and %s cwd %s"), + *CmdLine, + *FPaths::LaunchDir()); + + TArray CommandLineOptions; + auto Parsed = CmdLine.ParseIntoArray(CommandLineOptions, TEXT(" ")); + + UE_LOG(GM_Log, + Display, + TEXT("AGMTestingManager::StartTesting() Processing %i arguments"), + Parsed); + + int argc = 0; + // all possible values + some padding + // we ignore -p since we are always single threaded + const char* argv[16]; + if (CommandLineOptions.Num() > 1) + { + argv[argc++] = TCHAR_TO_ANSI(*CommandLineOptions[0]); + } + else + { + argv[argc++] = TCHAR_TO_ANSI(*FPaths::LaunchDir()); + } + + for (size_t i = 1; i < CommandLineOptions.Num(); ++i) + { + UE_LOG(GM_Log, + Display, + TEXT("AGMTestingManager::StartTesting() Processing {%s}"), + *CommandLineOptions[i]); + + if (CommandLineOptions[i] == TEXT("--test_harness")) + { + argv[argc++] = "--test_harness"; + argv[argc++] = TCHAR_TO_ANSI(*CommandLineOptions[++i]); + } + else if (CommandLineOptions[i] == TEXT("-o")) + { + FString output = FPaths::Combine(FPaths::LaunchDir(), CommandLineOptions[++i]); + output = IFileManager::Get().ConvertToAbsolutePathForExternalAppForRead(*output); + argv[argc++] = "--output"; + argv[argc++] = TCHAR_TO_ANSI(*output); + } + else if (CommandLineOptions[i] == TEXT("--output")) + { + FString output = FPaths::Combine(FPaths::LaunchDir(), CommandLineOptions[++i]); + output = IFileManager::Get().ConvertToAbsolutePathForExternalAppForRead(*output); + argv[argc++] = "--output"; + argv[argc++] = TCHAR_TO_ANSI(*output); + } + else if (CommandLineOptions[i] == TEXT("-s")) + { + FString src = IFileManager::Get().ConvertToAbsolutePathForExternalAppForRead( + *CommandLineOptions[++i]); + argv[argc++] = "--src"; + argv[argc++] = TCHAR_TO_ANSI(*src); + } + else if (CommandLineOptions[i] == TEXT("--src")) + { + FString src = IFileManager::Get().ConvertToAbsolutePathForExternalAppForRead( + *CommandLineOptions[++i]); + argv[argc++] = "--src"; + argv[argc++] = TCHAR_TO_ANSI(*src); + } + else if (CommandLineOptions[i] == TEXT("--match")) + { + argv[argc++] = "--match"; + argv[argc++] = TCHAR_TO_ANSI(*CommandLineOptions[++i]); + } + else if (CommandLineOptions[i] == TEXT("-m")) + { + argv[argc++] = "--match"; + argv[argc++] = TCHAR_TO_ANSI(*CommandLineOptions[++i]); + } + else if (CommandLineOptions[i] == TEXT("--fast-png")) + { + argv[argc++] = "--fast-png"; + } + else if (CommandLineOptions[i] == TEXT("-f")) + { + argv[argc++] = "--fast-png"; + } + else if (CommandLineOptions[i] == TEXT("--interactive")) + { + argv[argc++] = "--interactive"; + } + else if (CommandLineOptions[i] == TEXT("-i")) + { + argv[argc++] = "--interactive"; + } + else if (CommandLineOptions[i] == TEXT("--backend")) + { + ++i; + UE_LOG(GM_Log, + Warning, + TEXT("Ingoring backend command because it is hardcoded to rhi")); + } + else if (CommandLineOptions[i] == TEXT("-b")) + { + ++i; + UE_LOG(GM_Log, + Warning, + TEXT("Ingoring backend command because it is hardcoded to rhi")); + } + else if (CommandLineOptions[i] == TEXT("--headless")) + { + argv[argc++] = "--headless"; + } + else if (CommandLineOptions[i] == TEXT("-h")) + { + argv[argc++] = "--headless"; + } + else if (CommandLineOptions[i] == TEXT("--verbose")) + { + argv[argc++] = "--verbose"; + } + else if (CommandLineOptions[i] == TEXT("-v")) + { + argv[argc++] = "--verbose"; + } + } + + argv[argc++] = "--backend"; + argv[argc++] = "rhi"; + + if (bShouldAddTestArguments) + { + UE_LOG(GM_Log, + Display, + TEXT("AGMTestingManager::InitTesting() gms_main adding test params")); + + argv[argc++] = "--verbose"; + + FString ProjectDir = FPaths::ProjectDir(); + ProjectDir = + IFileManager::Get().ConvertToAbsolutePathForExternalAppForRead(*ProjectDir); + + argv[argc++] = "--output"; + argv[argc++] = TCHAR_TO_ANSI(*FPaths::Combine(ProjectDir, "output")); + + if (TestingType == EGMTestingType::Golden) + { + FString RivePath = + FPaths::Combine(FPaths::ProjectDir(), "../../../../gold/rivs/Bear.riv"); + RivePath = + IFileManager::Get().ConvertToAbsolutePathForExternalAppForRead(*RivePath); + + argv[argc++] = "--src"; + argv[argc++] = TCHAR_TO_ANSI(*RivePath); + } + } + + FString ArgLog; + for (size_t i = 0; i < argc; i++) + { + ArgLog += argv[i] + FString(" "); + } + + UE_LOG(GM_Log, + Display, + TEXT("AGMTestingManager::StartTesting() starting gms with argc %i argv %s"), + argc, + *ArgLog); + + switch (TestingType) + { + case EGMTestingType::GM: + RunGMs(argc, argv); + break; + case EGMTestingType::Golden: + RunGoldens(argc, argv); + break; + case EGMTestingType::Both: + RunGMs(argc, argv); + RunGoldens(argc, argv); + } + // we have to reset this here because both gms and goldens delete the window + TestingWindow = nullptr; + + AsyncTask(ENamedThreads::GameThread, [this]() { + if (bShouldExitOnFinish) + { +#if WITH_EDITOR + UKismetSystemLibrary::QuitGame(GetWorld(), + UGameplayStatics::GetPlayerController(GetWorld(), 0), + EQuitPreference::Type::Quit, + false); +#else + FGenericPlatformMisc::RequestExit(false); +#endif + } + else + { + OnGmTestingFinished.Broadcast(); + } + }); + }); +} + +void AGMTestingManager::DeInitTesting() {} + +void AGMTestingManager::SetDMTexture(UTexture2DDynamic* Texture, bool MeshIsVisible) +{ + check(DM); + DM->SetTextureParameterValue("texture", Texture); + StaticMeshComponent->SetHiddenInGame(!MeshIsVisible, true); +} + +void AGMTestingManager::RunGM(UPARAM(ref) FGMData& GM, bool ShouldGenerateDisplayTexture) +{ + TestingWindow->isGoldens = false; + if (GM.registry_position == nullptr) + { + + UE_LOG(GM_Log, Error, TEXT("Failed to find gm of name %s"), *GM.Name); + return; + } + + if (ShouldGenerateDisplayTexture) + { + UE_LOG(GM_Log, Display, TEXT("Generating display texture for gm named %s"), *GM.Name); + if (GM.Width == 0 || GM.Height == 0) + { + size_t width, height; + if (gms_registry_get_size(GM.registry_position, width, height)) + { + GM.Width = width; + GM.Height = height; + } + else + { + UE_LOG(GM_Log, Error, TEXT("Failed to get size of gm named %s"), *GM.Name); + return; + } + } + + GM.RiveTexture = NewObject(GetOuter()); + GM.RiveTexture->OnResourceInitializedOnRenderThread.AddUObject( + this, + &AGMTestingManager::RunSpecificGM_RenderThread); + + TestingWindow->RenderTexture = GM.RiveTexture; + GMToRun = &GM; + + GM.RiveTexture->ResizeRenderTargets(FIntPoint(GM.Width, GM.Height)); + } +} + +void AGMTestingManager::RunGolden(FGoldenData& Golden, bool ShouldGenerateDisplayTexture) +{ + TestingWindow->isGoldens = true; +} + +void AGMTestingManager::BeginPlay() +{ + DM = StaticMeshComponent->CreateDynamicMaterialInstance(0); + + CustomTestTexture = NewObject(GetOuter()); + + auto& RenderModule = IRiveRendererModule::Get(); + auto InRenderer = RenderModule.GetRenderer(); + InRenderer->CallOrRegister_OnInitialized( + IRiveRenderer::FOnRendererInitialized::FDelegate::CreateUObject( + this, + &AGMTestingManager::RiveReady)); +} + +void AGMTestingManager::RiveReady(IRiveRenderer* InRiveRenderer) +{ + Renderer = InRiveRenderer; + bRiveReady = true; + + // just in case we didnt run gms yet + if (TestingWindow) + delete TestingWindow; + + TestingWindow = new UnrealTestingWindow(this, InRiveRenderer); + ::TestingWindow::Set(TestingWindow); + + if (bShouldGenerateGMList) + { + // populate GMList with info + REGISTRY_HANDLE registery_handle = rivegm::GMRegistry::head(); + + while (registery_handle) + { + FGMData GMData; + std::string GMName; + if (gms_registry_get_name(registery_handle, GMName)) + { + GMData.Name.AppendChars(GMName.c_str(), GMName.size()); + } + + size_t width, height; + if (gms_registry_get_size(registery_handle, width, height)) + { + GMData.Width = width; + GMData.Height = height; + } + + GMData.registry_position = registery_handle; + + GMList.Add(GMData); + registery_handle = gms_registry_get_next(registery_handle); + } + + UE_LOG(GM_Log, Display, TEXT("GMs ready for testing with %i GMs"), GMList.Num()); + } + + UE_LOG(GM_Log, Display, TEXT("GMs ready for testing with")); + + OnGMTestingReady.Broadcast(); +} + +void AGMTestingManager::RunGMs(int argc, const char* argv[]) +{ + TestingWindow->isGoldens = false; + int error = gms_main(argc, argv); + + if (error != 0) + { + UE_LOG(GM_Log, + Error, + TEXT("AGMTestingManager::StartTesting() gms_main failed with error code %i"), + error); + } + else + { + UE_LOG(GM_Log, + Display, + TEXT("AGMTestingManager::StartTesting() gms_main finished succefully")); + } +} + +void AGMTestingManager::RunGoldens(int argc, const char* argv[]) +{ + TestingWindow->isGoldens = true; + int error = goldens_main(argc, argv); + + if (error != 0) + { + UE_LOG(GM_Log, + Error, + TEXT("AGMTestingManager::StartTesting() goldens_main failed with error code %i"), + error); + } + else + { + UE_LOG(GM_Log, + Display, + TEXT("AGMTestingManager::StartTesting() goldens_main finished succefully")); + } +} + +void AGMTestingManager::RunCustomTestClear() +{ + check(CustomTestTexture); + DM->SetTextureParameterValue(TEXT("texture"), CustomTestTexture); + CustomTestTexture->OnResourceInitializedOnRenderThread.AddUObject( + this, + &AGMTestingManager::RunCustomTestClear_RenderThread); + CustomTestTexture->ResizeRenderTargets(FIntPoint(256, 256)); +} + +void AGMTestingManager::RunCustomTestManyPaths(int32 NumberOfPathsToRun) +{ + auto Size = sqrt(NumberOfPathsToRun); + check(CustomTestTexture); + check(Size); + Size = FMath::Clamp(Size, RIVE_MIN_TEX_RESOLUTION, RIVE_MAX_TEX_RESOLUTION); + DM->SetTextureParameterValue(TEXT("texture"), CustomTestTexture); + CustomTestTexture->OnResourceInitializedOnRenderThread.AddUObject( + this, + &AGMTestingManager::RunCustomTestManyPaths_RenderThread); + CustomTestTexture->ResizeRenderTargets(FIntPoint(Size, Size)); +} + +void AGMTestingManager::RunCustomTestClear_RenderThread(FRHICommandListImmediate& RHICmdList, + FTextureRHIRef& NewResource) +{ + auto context = Renderer->GetRenderContext(); + auto impl = context->static_impl_cast(); + auto renderTarget = impl->makeRenderTarget(RHICmdList, NewResource); + + context->beginFrame({.renderTargetWidth = NewResource->GetSizeX(), + .renderTargetHeight = NewResource->GetSizeY(), + .loadAction = rive::gpu::LoadAction::clear, + .clearColor = 0xFFFFFF00}); + + context->flush({.renderTarget = renderTarget.get()}); +} + +void AGMTestingManager::RunCustomTestManyPaths_RenderThread(FRHICommandListImmediate& RHICmdList, + FTextureRHIRef& NewResource) +{ + auto context = Renderer->GetRenderContext(); + auto impl = context->static_impl_cast(); + auto renderTarget = impl->makeRenderTarget(RHICmdList, NewResource); + + auto width = NewResource->GetSizeX(); + auto height = NewResource->GetSizeY(); + + context->beginFrame({.renderTargetWidth = width, + .renderTargetHeight = height, + .loadAction = rive::gpu::LoadAction::clear, + .clearColor = 0xFF000000}); + + auto renderer = std::make_unique(context); + + rive::ColorInt Color = 0; + + const int ColorStep = 0xFFFFFF / (width * height); + + for (size_t x = 0; x < width; x++) + { + for (size_t y = 0; y < height; y++) + { + Color += ColorStep; + auto strokePaint = context->makeRenderPaint(); + strokePaint->style(rive::RenderPaintStyle::fill); + strokePaint->color(rive::colorWithAlpha(Color, 0xFF)); + strokePaint->blendMode(rive::BlendMode::srcOver); + rive::RawPath path; + path.addRect({static_cast(x), + static_cast(y), + static_cast(x + 1), + static_cast(y + 1)}); + auto renderPath = context->makeRenderPath(path, rive::FillRule::nonZero); + renderer->drawPath(renderPath.get(), strokePaint.get()); + } + } + + context->flush({.renderTarget = renderTarget.get()}); +} + +void AGMTestingManager::RunSpecificGM_RenderThread(FRHICommandListImmediate& RHICmdList, + FTextureRHIRef& NewResource) +{ + if (GMToRun == nullptr) + { + UE_LOG(GM_Log, Error, TEXT("No GM To Run!")); + return; + } + + // we are already on the render thread so we don't need to enque a new command + if (!gms_run_gm(GMToRun->registry_position)) + { + UE_LOG(GM_Log, Error, TEXT("Failed to run gm named %s"), *GMToRun->Name); + } + + AsyncTask(ENamedThreads::GameThread, [this, GM = GMToRun]() { SetDMTexture(GM->RiveTexture); }); + + // null it out since we already ran it + GMToRun = nullptr; +} + +void UGMDataStatics::ResetGmTexture(FGMData& Data) { Data.RiveTexture = nullptr; } + +void UGMDataStatics::ResetGoldenTexture(FGoldenData& Data) { Data.RiveTexture = nullptr; } + +void UGMDataStatics::MakeGoldenData(FString PathToRiv, FGoldenData& OutGoldenData, bool& Successful) +{ + auto& FileManager = IFileManager::Get(); + if (!FileManager.FileExists(*PathToRiv)) + { + Successful = false; + return; + } + + OutGoldenData.Name = FPaths::GetBaseFilename(PathToRiv); + OutGoldenData.FilePath = PathToRiv; + Successful = true; +} + +void UGMDataStatics::GenerateGoldenForDefaultLocation(TArray& OutGoldens) +{ + auto& FileManager = IFileManager::Get(); + + const auto ProjectFile = FPaths::GetProjectFilePath(); + auto RelativePath = FPaths::Combine(ProjectFile, "../../../../../gold/rivs"); + auto RivDir = FileManager.ConvertToAbsolutePathForExternalAppForRead(*RelativePath); + UE_LOG(GM_Log, Display, TEXT("Generating Default Golden List for %s"), *RivDir); + FileManager.IterateDirectoryStat( + *RivDir, + [&OutGoldens](const TCHAR* Path, const FFileStatData& Stat) -> bool { + FGoldenData Data; + bool success = false; + UGMDataStatics::MakeGoldenData(Path, Data, success); + + if (success) + { + OutGoldens.Add(Data); + } + + return true; + }); +} diff --git a/tests/unreal/Plugins/GM/Source/GM/Private/UnrealTestingWindow.cpp b/tests/unreal/Plugins/GM/Source/GM/Private/UnrealTestingWindow.cpp new file mode 100644 index 00000000..8b72ab6b --- /dev/null +++ b/tests/unreal/Plugins/GM/Source/GM/Private/UnrealTestingWindow.cpp @@ -0,0 +1,156 @@ +#include "UnrealTestingWindow.h" + +#include "Kismet/GameplayStatics.h" +#include "Kismet/KismetInputLibrary.h" +#include "RiveRenderer/Public/IRiveRenderer.h" +#include "Platform/RenderContextRHIImpl.hpp" +#include "Rive/RiveTexture.h" +#include "GMTestingManager.h" +#include + +#include "Misc/EngineVersionComparison.h" + +#if UE_VERSION_OLDER_THAN(5, 4, 0) +#define CREATE_TEXTURE(RHICmdList, Desc) RHICreateTexture(Desc) +#else // UE_VERSION_OLDER_THAN (5, 4,0) +#define CREATE_TEXTURE(RHICmdList, Desc) RHICmdList.CreateTexture(Desc) +#endif + +UnrealTestingWindow::UnrealTestingWindow(UObject* WorldContextObject, IRiveRenderer* RiveRenderer) : + RiveRenderer(RiveRenderer), + RenderContext(RiveRenderer->GetRenderContext()), + WorldContextObject(WorldContextObject) +{} + +void UnrealTestingWindow::resize(int width, int height) +{ + TestingWindow::resize(width, height); + + auto impl = RenderContext->static_impl_cast(); + auto& RHICmdList = GRHICommandList.GetImmediateCommandList(); + + if (RenderTexture) + { + // use the render texture created external for visibility + check(RenderTexture->Size.X == width); + check(RenderTexture->Size.Y == height); + auto Texture = RenderTexture->GetResource()->GetTexture2DRHI(); + RenderTarget = impl->makeRenderTarget(RHICmdList, Texture); + } + else + { + FRHITextureCreateDesc Desc = + FRHITextureCreateDesc::Create2D(TEXT("RiveTestingWindowRenderTarget"), + width, + height, + EPixelFormat::PF_R8G8B8A8); + Desc.SetFlags(ETextureCreateFlags::UAV | ETextureCreateFlags::Dynamic | + ETextureCreateFlags::ShaderResource | ETextureCreateFlags::RenderTargetable); + FTexture2DRHIRef Texture = CREATE_TEXTURE(RHICmdList, Desc); + RenderTarget = impl->makeRenderTarget(RHICmdList, Texture); + } + + FRHITextureCreateDesc CopyDesc = + FRHITextureCreateDesc::Create2D(TEXT("RiveTestingWindowCopyDest"), + width, + height, + EPixelFormat::PF_R8G8B8A8); + CopyDesc.SetFlags(ETextureCreateFlags::CPUReadback); + CopyDestTexture = CREATE_TEXTURE(RHICmdList, CopyDesc); +} + +rive::Factory* UnrealTestingWindow::factory() { return RenderContext; } + +std::unique_ptr UnrealTestingWindow::beginFrame(uint32_t clearColor, bool doClear) +{ + rive::gpu::RenderContext::FrameDescriptor FrameDescriptor; + FrameDescriptor.renderTargetWidth = width(); + FrameDescriptor.renderTargetHeight = height(); + FrameDescriptor.loadAction = + doClear ? rive::gpu::LoadAction::clear : rive::gpu::LoadAction::preserveRenderTarget; + FrameDescriptor.clearColor = clearColor; + FrameDescriptor.wireframe = false; + FrameDescriptor.fillsDisabled = false; + FrameDescriptor.strokesDisabled = false; + + RenderContext->beginFrame(std::move(FrameDescriptor)); + return std::make_unique(RenderContext); +} + +void UnrealTestingWindow::endFrame(std::vector* pixelData) +{ + RenderContext->flush({RenderTarget.get()}); + + if (pixelData == nullptr) + return; + + const size_t size = m_height * m_width * 4; + + pixelData->resize(size); + + check(pixelData->size() == size); + + auto& RHICmdList = GRHICommandList.GetImmediateCommandList(); + + auto RHIRenderTarget = static_cast(RenderTarget.get()); + auto RenderTargetTexture = RHIRenderTarget->texture(); + + auto Fence = GDynamicRHI->RHICreateGPUFence(TEXT("GM_FLUSH_FENCE")); + TransitionAndCopyTexture(RHICmdList, + RenderTargetTexture, + CopyDestTexture, + FRHICopyTextureInfo()); + + RHICmdList.Transition( + FRHITransitionInfo(CopyDestTexture, ERHIAccess::Unknown, ERHIAccess::CPURead)); + + RHICmdList.WriteGPUFence(Fence); + + void* Data = nullptr; + int32 Width; + int32 Height; + RHICmdList.MapStagingSurface(CopyDestTexture, Fence.GetReference(), Data, Width, Height); + + check(Width >= static_cast(m_width)); + check(Height >= static_cast(m_height)); + + uint32 DestStride = Width * 4; + for (uint32_t y = 0; y < m_height; ++y) + { + uint8_t* src = static_cast(Data) + (DestStride * y); + uint8_t* dst = pixelData->data() + ((m_height - y - 1) * m_width * 4); + + memcpy(dst, src, m_width * 4); + } + + RHICmdList.UnmapStagingSurface(CopyDestTexture); +} + +rive::gpu::RenderContext* UnrealTestingWindow::renderContext() const { return RenderContext; } + +rive::gpu::RenderTarget* UnrealTestingWindow::renderTarget() const { return RenderTarget.get(); } + +void UnrealTestingWindow::flushPLSContext() { RenderContext->flush({RenderTarget.get()}); } + +bool UnrealTestingWindow::peekKey(char& key) +{ + auto controller = UGameplayStatics::GetPlayerController(WorldContextObject, 0); + return controller->GetInputAnalogKeyState(EKeys::SpaceBar) != 0; +} + +char UnrealTestingWindow::getKey() +{ + char key = ' '; + while (!peekKey(key)) + { + if (shouldQuit()) + { + printf("Window terminated by user.\n"); + exit(0); + } + } + + return key; +} + +bool UnrealTestingWindow::shouldQuit() const { return false; } \ No newline at end of file diff --git a/tests/unreal/Plugins/GM/Source/GM/Public/GM.h b/tests/unreal/Plugins/GM/Source/GM/Public/GM.h new file mode 100644 index 00000000..f1deccbe --- /dev/null +++ b/tests/unreal/Plugins/GM/Source/GM/Public/GM.h @@ -0,0 +1,15 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "Modules/ModuleManager.h" + +class FGMModule : public IModuleInterface +{ +public: + /** IModuleInterface implementation */ + virtual void StartupModule() override; + virtual void ShutdownModule() override; +}; + +DECLARE_LOG_CATEGORY_EXTERN(GM_Log, Display, All); diff --git a/tests/unreal/Plugins/GM/Source/GM/Public/GMTestingManager.h b/tests/unreal/Plugins/GM/Source/GM/Public/GMTestingManager.h new file mode 100644 index 00000000..a7b9fa05 --- /dev/null +++ b/tests/unreal/Plugins/GM/Source/GM/Public/GMTestingManager.h @@ -0,0 +1,183 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Rive/RiveTexture.h" +#include "UObject/Object.h" +#include "UnrealTestingWindow.h" +#include "GMTestingManager.generated.h" + +UENUM(Blueprintable, BlueprintType) +enum class EGMTestingType : uint8 +{ + GM, + Golden, + Both +}; + +USTRUCT(Blueprintable, BlueprintType) +struct GM_API FGMData +{ + GENERATED_BODY() + // Width of the GM gets populate when GM list is created + UPROPERTY(BlueprintReadOnly, VisibleAnywhere) + int32 Width = 0; + // Height of the GM gets populate when GM list is created + UPROPERTY(BlueprintReadOnly, VisibleAnywhere) + int32 Height = 0; + // transient texture that gets deleted after this gm is removed from view to save memory + UPROPERTY(BlueprintReadOnly, VisibleAnywhere) + URiveTexture* RiveTexture = nullptr; + // the name of the golden + UPROPERTY(BlueprintReadOnly, VisibleAnywhere) + FString Name; + // opaque handle to GM registry u accessed and provided by gms.dll + const void* registry_position = nullptr; +}; + +USTRUCT(Blueprintable, BlueprintType) +struct GM_API FGoldenData +{ + GENERATED_BODY() + + // this is only populated after the first time this golden has been drawn + UPROPERTY(BlueprintReadOnly, VisibleAnywhere) + int32 Width = 0; + + // this is only populated after the first time this golden has been drawn + UPROPERTY(BlueprintReadOnly, VisibleAnywhere) + int32 Height = 0; + + // transient texture that gets deleted after this golden is removed from view to save memory + UPROPERTY(BlueprintReadOnly, VisibleAnywhere) + URiveTexture* RiveTexture = nullptr; + + // base file name for then golden + UPROPERTY(BlueprintReadOnly, VisibleAnywhere) + FString Name; + + // full absolute path to golden + UPROPERTY(BlueprintReadOnly, VisibleAnywhere) + FString FilePath; +}; + +UCLASS() +class GM_API UGMDataStatics : public UObject +{ + GENERATED_BODY() +public: + // Resets the RiveTexture to nullptr to release the memory + UFUNCTION(BlueprintCallable) + static void ResetGmTexture(UPARAM(ref) FGMData& Data); + + // Resets the RiveTexture to nullptr to release the memory + UFUNCTION(BlueprintCallable) + static void ResetGoldenTexture(UPARAM(ref) FGoldenData& Data); + // Create a new golden with minimal info, verifying the path exists + UFUNCTION(BlueprintCallable) + static void MakeGoldenData(FString PathToRiv, FGoldenData& OutGoldenData, bool& Successful); + // Create Goldens using assumed launch location is the Unreal Project root directory, like + // running from editor + UFUNCTION(BlueprintCallable) + static void GenerateGoldenForDefaultLocation(TArray& OutGoldens); +}; + +DECLARE_DYNAMIC_MULTICAST_DELEGATE(FGMTestingReady); +DECLARE_DYNAMIC_MULTICAST_DELEGATE(FGMTestingFinished); +/** + * + */ +UCLASS() +class GM_API AGMTestingManager : public AActor +{ + GENERATED_BODY() +public: + AGMTestingManager(); + + UFUNCTION(BlueprintCallable) + void StartTesting(); + UFUNCTION(BlueprintCallable) + void DeInitTesting(); + + UFUNCTION(BlueprintCallable) + void SetDMTexture(UTexture2DDynamic* Texture, bool MeshIsVisible = true); + + /* + * Runs the given GM through gms.dll + * If @ShouldGenerateDisplayTexture is true, a new RiveTexture will be created and stored in GM + * That will be rendered to assuming it will be used to display the finished GM + */ + UFUNCTION(BlueprintCallable) + void RunGM(UPARAM(ref) FGMData& GM, bool ShouldGenerateDisplayTexture = true); + + /* + * Runs the given Golden through goldens_main + * If @ShouldGenerateDisplayTexture is true, a new RiveTexture will be created and stored in + * Golden That will be rendered to assuming it will be used to display the finished Golden + */ + UFUNCTION(BlueprintCallable) + void RunGolden(UPARAM(ref) FGoldenData& Golden, bool ShouldGenerateDisplayTexture = true); + + UFUNCTION(BlueprintCallable) + void RunCustomTestClear(); + + UFUNCTION(BlueprintCallable) + void RunCustomTestManyPaths(int32 NumberOfPathsToRun); + + UPROPERTY(BlueprintReadWrite, EditAnywhere) + bool bShouldAddTestArguments; + + UPROPERTY(BlueprintReadWrite, EditAnywhere) + bool bShouldExitOnFinish; + + UPROPERTY(BlueprintReadWrite, EditAnywhere) + bool bShouldGenerateGMList; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "GM") + FGMTestingReady OnGMTestingReady; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "GM") + FGMTestingFinished OnGmTestingFinished; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "GM") + EGMTestingType TestingType = EGMTestingType::GM; + +protected: + UPROPERTY(BlueprintReadOnly, EditDefaultsOnly) + UStaticMeshComponent* StaticMeshComponent; + + UPROPERTY(BlueprintReadOnly, VisibleAnywhere) + TArray GMList; + +protected: + virtual void BeginPlay() override; + void RiveReady(IRiveRenderer* InRiveRenderer); + +private: + void RunGMs(int argc, const char* argv[]); + void RunGoldens(int argc, const char* argv[]); + + void RunCustomTestClear_RenderThread(FRHICommandListImmediate& RHICmdList, + FTextureRHIRef& NewResource); + void RunCustomTestManyPaths_RenderThread(FRHICommandListImmediate& RHICmdList, + FTextureRHIRef& NewResource); + void RunSpecificGM_RenderThread(FRHICommandListImmediate& RHICmdList, + FTextureRHIRef& NewResource); + // GMRan in RunSpecificGM_RenderThread and set by RunGM + FGMData* GMToRun = nullptr; + + // this is managed by the GM lib. so we just reset this to null after running gms_main + UnrealTestingWindow* TestingWindow; + + UPROPERTY() + URiveTexture* CustomTestTexture; + + UPROPERTY() + UMaterialInstanceDynamic* DM; + + IRiveRenderer* Renderer; + + bool bRiveReady; + bool bResourceReady; +}; diff --git a/tests/unreal/Plugins/GM/Source/GM/Public/UnrealTestingWindow.h b/tests/unreal/Plugins/GM/Source/GM/Public/UnrealTestingWindow.h new file mode 100644 index 00000000..b12124a5 --- /dev/null +++ b/tests/unreal/Plugins/GM/Source/GM/Public/UnrealTestingWindow.h @@ -0,0 +1,54 @@ +#pragma once +#include "rive/refcnt.hpp" +#include "Rive/RiveTexture.h" +struct FGMData; +class IRiveRenderer; +class IRiveRenderTarget; +class URiveTexture; +THIRD_PARTY_INCLUDES_START +#include "gms.hpp" +THIRD_PARTY_INCLUDES_END + +/* + * UnrealTestingWindow is the testing window implementation for rhi intended to be used with gms + * unit testing. This is assumed to be on the Render thread. If used on the Game thread it will + * cause unexpected behaviour. + */ + +class UnrealTestingWindow : public TestingWindow +{ +public: + UnrealTestingWindow(UObject* WorldContextObject, IRiveRenderer* RenderTarget); + + virtual void resize(int width, int height) override; + + virtual rive::Factory* factory() override; + virtual std::unique_ptr beginFrame(uint32_t clearColor, + bool doClear = true) override; + virtual void endFrame(std::vector* pixelData = nullptr) override; + + virtual rive::gpu::RenderContext* renderContext() const override; + virtual rive::gpu::RenderTarget* renderTarget() const override; + virtual void flushPLSContext() override; + + virtual bool peekKey(char& key) override; + virtual char getKey() override; + virtual bool shouldQuit() const override; + + bool isGoldens = false; + + // texture used for rendering GM + URiveTexture* RenderTexture = nullptr; + +private: + // we don't reference count this here because a UObject owns this class and the render target + IRiveRenderer* RiveRenderer; + + rive::gpu::RenderContext* RenderContext; + rive::rcp RenderTarget; + // I am not allowed to both render to and read from the same texture, so i'm going to copy to + // this one and read from here + FTexture2DRHIRef CopyDestTexture; + + UObject* WorldContextObject; +}; diff --git a/tests/unreal/Plugins/GM/Source/ThirdParty/GMLibrary/ExampleLibrary.cpp b/tests/unreal/Plugins/GM/Source/ThirdParty/GMLibrary/ExampleLibrary.cpp new file mode 100644 index 00000000..6c1aaeba --- /dev/null +++ b/tests/unreal/Plugins/GM/Source/ThirdParty/GMLibrary/ExampleLibrary.cpp @@ -0,0 +1,23 @@ +#if defined _WIN32 || defined _WIN64 +#include + +#define EXAMPLELIBRARY_EXPORT __declspec(dllexport) +#else +#include +#endif + +#ifndef EXAMPLELIBRARY_EXPORT +#define EXAMPLELIBRARY_EXPORT +#endif + +EXAMPLELIBRARY_EXPORT void ExampleLibraryFunction() +{ +#if defined _WIN32 || defined _WIN64 + MessageBox(NULL, + TEXT("Loaded ExampleLibrary.dll from Third Party Plugin sample."), + TEXT("Third Party Plugin"), + MB_OK); +#else + printf("Loaded ExampleLibrary from Third Party Plugin sample"); +#endif +} \ No newline at end of file diff --git a/tests/unreal/Plugins/GM/Source/ThirdParty/GMLibrary/ExampleLibrary.sln b/tests/unreal/Plugins/GM/Source/ThirdParty/GMLibrary/ExampleLibrary.sln new file mode 100644 index 00000000..a19ae4bb --- /dev/null +++ b/tests/unreal/Plugins/GM/Source/ThirdParty/GMLibrary/ExampleLibrary.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.31101.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ExampleLibrary", "ExampleLibrary.vcxproj", "{9B50F1F8-0116-442C-A071-F5C3A120A5CB}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {9B50F1F8-0116-442C-A071-F5C3A120A5CB}.Debug|x64.ActiveCfg = Debug|x64 + {9B50F1F8-0116-442C-A071-F5C3A120A5CB}.Debug|x64.Build.0 = Debug|x64 + {9B50F1F8-0116-442C-A071-F5C3A120A5CB}.Release|x64.ActiveCfg = Release|x64 + {9B50F1F8-0116-442C-A071-F5C3A120A5CB}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/tests/unreal/Plugins/GM/Source/ThirdParty/GMLibrary/ExampleLibrary.vcxproj b/tests/unreal/Plugins/GM/Source/ThirdParty/GMLibrary/ExampleLibrary.vcxproj new file mode 100644 index 00000000..bef28b1a --- /dev/null +++ b/tests/unreal/Plugins/GM/Source/ThirdParty/GMLibrary/ExampleLibrary.vcxproj @@ -0,0 +1,158 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {9B50F1F8-0116-442C-A071-F5C3A120A5CB} + Win32Proj + ExampleLibrary + + + + DynamicLibrary + true + v143 + Unicode + + + DynamicLibrary + true + v143 + Unicode + + + DynamicLibrary + false + v143 + true + Unicode + + + DynamicLibrary + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + $(SolutionDir)$(Platform)\$(Configuration)\ + + + + + + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;EXAMPLELIBRARY_EXPORTS;%(PreprocessorDefinitions) + true + + + Windows + true + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;EXAMPLELIBRARY_EXPORTS;%(PreprocessorDefinitions) + true + + + Windows + true + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;EXAMPLELIBRARY_EXPORTS;%(PreprocessorDefinitions) + true + + + Windows + true + true + true + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;EXAMPLELIBRARY_EXPORTS;%(PreprocessorDefinitions) + true + + + Windows + true + true + true + + + copy "$(TargetPath)" "$(SolutionDir)..\..\..\Binaries\ThirdParty\GMLibrary\Win64\" + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/unreal/Plugins/GM/Source/ThirdParty/GMLibrary/ExampleLibrary.vcxproj.filters b/tests/unreal/Plugins/GM/Source/ThirdParty/GMLibrary/ExampleLibrary.vcxproj.filters new file mode 100644 index 00000000..29d3ce73 --- /dev/null +++ b/tests/unreal/Plugins/GM/Source/ThirdParty/GMLibrary/ExampleLibrary.vcxproj.filters @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/tests/unreal/Plugins/GM/Source/ThirdParty/GMLibrary/GMLibrary.Build.cs b/tests/unreal/Plugins/GM/Source/ThirdParty/GMLibrary/GMLibrary.Build.cs new file mode 100644 index 00000000..aa563761 --- /dev/null +++ b/tests/unreal/Plugins/GM/Source/ThirdParty/GMLibrary/GMLibrary.Build.cs @@ -0,0 +1,41 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +using System.IO; +using UnrealBuildTool; + +public class GMLibrary : ModuleRules +{ + public GMLibrary(ReadOnlyTargetRules Target) : base(Target) + { + Type = ModuleType.External; + PublicSystemIncludePaths.Add("$(ModuleDir)/Public"); + + PublicDependencyModuleNames.Add("RiveLibrary"); + + PublicDefinitions.Add("RIVE_UNREAL"); + PublicDefinitions.Add("RIVE_UNREAL_IMPORT"); + + if (Target.Platform == UnrealTargetPlatform.Win64) + { + // Add the import library + PublicAdditionalLibraries.Add("/WHOLEARCHIVE:" + Path.Combine(ModuleDirectory, "x64", "Release", "gms.lib")); + PublicAdditionalLibraries.Add(Path.Combine(ModuleDirectory, "x64", "Release", "goldens.lib")); + PublicAdditionalLibraries.Add(Path.Combine(ModuleDirectory, "x64", "Release", "tools_common.lib")); + } + else if (Target.Platform == UnrealTargetPlatform.Mac) + { + PublicDelayLoadDLLs.Add(Path.Combine(ModuleDirectory, "Mac", "Release", "gms.dylib")); + RuntimeDependencies.Add("$(PluginDir)/Source/ThirdParty/GMLibrary/Mac/Release/gms.dylib"); + + PublicDelayLoadDLLs.Add(Path.Combine(ModuleDirectory, "Mac", "Release", "goldens.dylib")); + RuntimeDependencies.Add("$(PluginDir)/Source/ThirdParty/GMLibrary/Mac/Release/goldens.dylib"); + } + else if (Target.Platform == UnrealTargetPlatform.Linux) + { + string ExampleSoPath = Path.Combine("$(PluginDir)", "Binaries", "ThirdParty", "GMLibrary", "Linux", "x86_64-unknown-linux-gnu", "gms.so"); + PublicAdditionalLibraries.Add(ExampleSoPath); + PublicDelayLoadDLLs.Add(ExampleSoPath); + RuntimeDependencies.Add(ExampleSoPath); + } + } +} diff --git a/tests/unreal/Plugins/GM/Source/ThirdParty/GMLibrary/Public/gms.hpp b/tests/unreal/Plugins/GM/Source/ThirdParty/GMLibrary/Public/gms.hpp new file mode 100644 index 00000000..5cb2a23b --- /dev/null +++ b/tests/unreal/Plugins/GM/Source/ThirdParty/GMLibrary/Public/gms.hpp @@ -0,0 +1,378 @@ +/* + * Copyright 2022 Rive + */ + +#pragma once + +#include "rive/rive_types.hpp" +#include "rive/enum_bitset.hpp" +#include +#include +#include + +#include "rive/renderer.hpp" +#include +#include +#include +#include + +namespace rivegm +{ + +class GM +{ + std::string m_Name; + const int m_Width, m_Height; + +public: + GM(int width, int height, const char name[]) : + m_Name(name, strlen(name)), m_Width(width), m_Height(height) + {} + virtual ~GM() {} + + int width() const { return m_Width; } + int height() const { return m_Height; } + std::string name() const { return m_Name; } + + void onceBeforeDraw() { this->onOnceBeforeDraw(); } + + // Calls clearColor(), TestingWindow::beginFrame(), draw(), TestingWindow::flush(). + // (Most GMs just need to override onDraw() instead of overriding this method.) + virtual void run(std::vector* pixels); + + virtual rive::ColorInt clearColor() const { return 0xffffffff; } + + void draw(rive::Renderer*); + +protected: + virtual void onOnceBeforeDraw() {} + virtual void onDraw(rive::Renderer*) = 0; +}; + +template class Registry +{ + static Registry* s_Head; + static Registry* s_Tail; + + T m_Value; + Registry* m_Next = nullptr; + +public: + Registry(T value, bool isSlow) : m_Value(value) + { + if (s_Head == nullptr) + { + s_Tail = s_Head = this; + } + else if (isSlow) + { + m_Next = s_Head; + s_Head = this; + } + else + { + s_Tail->m_Next = this; + s_Tail = this; + } + } + + static const Registry* head() { return s_Head; } + const T& get() const { return m_Value; } + const Registry* next() const { return m_Next; } +}; + +template Registry* Registry::s_Head = nullptr; +template Registry* Registry::s_Tail = nullptr; + +using GMFactory = std::unique_ptr (*)(); +using GMRegistry = Registry; + +} // namespace rivegm + +namespace rive +{ +class Renderer; +class Factory; +namespace gpu +{ +class RenderContext; +class RenderContextGLImpl; +class RenderTarget; +} // namespace gpu +}; // namespace rive + +// Wraps a factory for rive::Renderer and a singleton target for it to render into (GL window, HTML +// canvas, software buffer, etc.): +// +// TestingWindow::Init(type); +// renderer = TestingWindow::Get()->reset(width, height); +// ... +// + +class TestingWindow +{ +public: + enum class Backend + { + gl, + glatomic, + glmsaa, + d3d, + d3datomic, + metal, + metalatomic, + + // System default Vulkan driver. + vulkan, + vulkanatomic, + + // Vulkan on Metal, aka MoltenVK. + // (defaults to /usr/local/share/vulkan/icd.d/MoltenVK_icd.json if VK_ICD_FILENAMES is not + // set.) + moltenvk, + moltenvkatomic, + + // Swiftshader, Google's CPU implementation of Vulkan. + // (defaults to ./vk_swiftshader_icd.json if VK_ICD_FILENAMES is not set.) + swiftshader, + swiftshaderatomic, + + angle, + anglemsaa, + dawn, + coregraphics, + + rhi, + }; + + constexpr static bool IsGL(Backend backend) + { + switch (backend) + { + case Backend::gl: + case Backend::glatomic: + case Backend::glmsaa: + case Backend::angle: + case Backend::anglemsaa: + return true; + case Backend::d3d: + case Backend::d3datomic: + case Backend::metal: + case Backend::metalatomic: + case Backend::vulkan: + case Backend::vulkanatomic: + case Backend::moltenvk: + case Backend::moltenvkatomic: + case Backend::swiftshader: + case Backend::swiftshaderatomic: + case Backend::dawn: + case Backend::coregraphics: + case Backend::rhi: + return false; + } + RIVE_UNREACHABLE(); + } + + constexpr static bool IsANGLE(Backend backend) + { + switch (backend) + { + case Backend::angle: + case Backend::anglemsaa: + return true; + case Backend::gl: + case Backend::glatomic: + case Backend::glmsaa: + case Backend::d3d: + case Backend::d3datomic: + case Backend::metal: + case Backend::metalatomic: + case Backend::vulkan: + case Backend::vulkanatomic: + case Backend::moltenvk: + case Backend::moltenvkatomic: + case Backend::swiftshader: + case Backend::swiftshaderatomic: + case Backend::dawn: + case Backend::coregraphics: + case Backend::rhi: + return false; + } + RIVE_UNREACHABLE(); + } + + constexpr static bool IsVulkan(Backend backend) + { + switch (backend) + { + case Backend::vulkan: + case Backend::vulkanatomic: + case Backend::moltenvk: + case Backend::moltenvkatomic: + case Backend::swiftshader: + case Backend::swiftshaderatomic: + return true; + case Backend::gl: + case Backend::glatomic: + case Backend::glmsaa: + case Backend::d3d: + case Backend::d3datomic: + case Backend::metal: + case Backend::metalatomic: + case Backend::dawn: + case Backend::coregraphics: + case Backend::angle: + case Backend::anglemsaa: + case Backend::rhi: + return false; + } + RIVE_UNREACHABLE(); + } + + constexpr static bool IsAtomic(Backend backend) + { + switch (backend) + { + case Backend::glatomic: + case Backend::d3datomic: + case Backend::metalatomic: + case Backend::vulkanatomic: + case Backend::moltenvkatomic: + case Backend::swiftshaderatomic: + case Backend::rhi: + return true; + case Backend::gl: + case Backend::glmsaa: + case Backend::d3d: + case Backend::metal: + case Backend::vulkan: + case Backend::moltenvk: + case Backend::swiftshader: + case Backend::angle: + case Backend::anglemsaa: + case Backend::dawn: + case Backend::coregraphics: + return false; + } + RIVE_UNREACHABLE(); + } + + constexpr static bool IsMSAA(Backend backend) + { + switch (backend) + { + case Backend::glmsaa: + case Backend::anglemsaa: + return true; + case Backend::glatomic: + case Backend::d3datomic: + case Backend::metalatomic: + case Backend::vulkanatomic: + case Backend::moltenvkatomic: + case Backend::swiftshaderatomic: + case Backend::gl: + case Backend::d3d: + case Backend::metal: + case Backend::vulkan: + case Backend::moltenvk: + case Backend::swiftshader: + case Backend::angle: + case Backend::dawn: + case Backend::coregraphics: + case Backend::rhi: + return false; + } + RIVE_UNREACHABLE(); + } + + enum class RendererFlags + { + none = 0, + useMSAA = 1 << 0, + disableRasterOrdering = 1 << 1, + }; + + enum class Visibility + { + headless, + window, + fullscreen, + }; + + static const char* BackendName(Backend); + static Backend ParseBackend(const char* name, std::string* gpuNameFilter); + static TestingWindow* Init(Backend, + Visibility, + const std::string& gpuNameFilter, + void* platformWindow = nullptr); + static TestingWindow* Get(); + static void Set(TestingWindow* inWindow); + static void Destroy(); + + uint32_t width() const { return m_width; } + uint32_t height() const { return m_height; } + + virtual rive::Factory* factory() = 0; + virtual void resize(int width, int height) + { + m_width = width; + m_height = height; + } + virtual std::unique_ptr beginFrame(uint32_t clearColor, + bool doClear = true) = 0; + virtual void endFrame(std::vector* pixelData = nullptr) = 0; + + // For testing directly on RenderContext. + virtual rive::gpu::RenderContext* renderContext() const { return nullptr; } + virtual rive::gpu::RenderContextGLImpl* renderContextGLImpl() const { return nullptr; } + virtual rive::gpu::RenderTarget* renderTarget() const { return nullptr; } + + // For testing render pass breaks. Caller must call renderContext()->beginFrame() again. + virtual void flushPLSContext() {} + + // Blocks until a key is pressed. + virtual bool peekKey(char& key) { return false; } + virtual char getKey() + { + fprintf(stderr, "TestingWindow::getKey not implemented."); + abort(); + } + virtual bool shouldQuit() const { return false; } + + virtual ~TestingWindow() {} + +protected: + uint32_t m_width = 0; + uint32_t m_height = 0; + +private: + static std::unique_ptr MakeGLFW(Backend, Visibility); + static std::unique_ptr MakeEGL(Backend, void* platformWindow); +#ifdef _WIN32 + static std::unique_ptr MakeD3D(Visibility); +#endif +#ifdef __APPLE__ + static std::unique_ptr MakeMetalTexture(); +#endif +#ifdef RIVE_MACOSX + static std::unique_ptr MakeCoreGraphics(); +#endif + static std::unique_ptr MakeFiddleContext(Backend, + Visibility, + const char* gpuNameFilter, + void* platformWindow); + static std::unique_ptr MakeVulkanTexture(const char* gpuNameFilter); + static std::unique_ptr MakeAndroidVulkan(void* platformWindow); +}; + +RIVE_MAKE_ENUM_BITSET(TestingWindow::RendererFlags); + +typedef const void* REGISTRY_HANDLE; + +int gms_main(int argc, const char* argv[]); +void gms_memcpy(void* dst, void* src, size_t size); +bool gms_registry_get_size(REGISTRY_HANDLE position_handle, size_t& width, size_t& height); +bool gms_registry_get_name(REGISTRY_HANDLE position_handle, std::string& name); +REGISTRY_HANDLE gms_get_registry_head(); +REGISTRY_HANDLE gms_registry_get_next(REGISTRY_HANDLE position_handle); +bool gms_run_gm(REGISTRY_HANDLE gm_handle); diff --git a/tests/unreal/Plugins/GM/Source/ThirdParty/GMLibrary/Public/goldens.hpp b/tests/unreal/Plugins/GM/Source/ThirdParty/GMLibrary/Public/goldens.hpp new file mode 100644 index 00000000..a26e1663 --- /dev/null +++ b/tests/unreal/Plugins/GM/Source/ThirdParty/GMLibrary/Public/goldens.hpp @@ -0,0 +1,8 @@ +#pragma once +#include +class TestingWindow; + +extern int /*__declspec(dllimport)*/ goldens_main(int argc, const char* argv[]); +/*void __declspec(dllimport) goldens_resize_pixel_data(std::vector* pixels, size_t size); +void __declspec(dllimport) goldens_init(TestingWindow* InTestingWindow); +void __declspec(dllimport) goldens_memcpy(void* dst, void* src, size_t size);*/ diff --git a/tests/unreal/Plugins/GM/Source/ThirdParty/GMLibrary/x64/Release/gms.lib b/tests/unreal/Plugins/GM/Source/ThirdParty/GMLibrary/x64/Release/gms.lib new file mode 100644 index 00000000..9adc0e8c Binary files /dev/null and b/tests/unreal/Plugins/GM/Source/ThirdParty/GMLibrary/x64/Release/gms.lib differ diff --git a/tests/unreal/Plugins/GM/Source/ThirdParty/GMLibrary/x64/Release/goldens.lib b/tests/unreal/Plugins/GM/Source/ThirdParty/GMLibrary/x64/Release/goldens.lib new file mode 100644 index 00000000..252762b2 Binary files /dev/null and b/tests/unreal/Plugins/GM/Source/ThirdParty/GMLibrary/x64/Release/goldens.lib differ diff --git a/tests/unreal/Plugins/GM/Source/ThirdParty/GMLibrary/x64/Release/tools_common.lib b/tests/unreal/Plugins/GM/Source/ThirdParty/GMLibrary/x64/Release/tools_common.lib new file mode 100644 index 00000000..9bf76485 Binary files /dev/null and b/tests/unreal/Plugins/GM/Source/ThirdParty/GMLibrary/x64/Release/tools_common.lib differ diff --git a/tests/unreal/Scripts/CopyGMs.py b/tests/unreal/Scripts/CopyGMs.py new file mode 100644 index 00000000..aa8ac6be --- /dev/null +++ b/tests/unreal/Scripts/CopyGMs.py @@ -0,0 +1,153 @@ +from argparse import ArgumentParser +from pathlib import Path + +# premake command for GMs +''' +premake5 --scripts="./premake5.lua" --with_rive_text --with_rive_audio=external --for_unreal --config=release --os=windows --out="out\release" vs2022 +''' +UNREAL_INSERTS_START = \ +'''THIRD_PARTY_INCLUDES_START +#undef PI +#pragma warning( push ) +#pragma warning( disable : 4611 ) +''' + +UNREAL_INSERTS_END = \ +'''THIRD_PARTY_INCLUDES_END +#define PI (3.1415926535897932f) +#pragma warning( pop ) +''' + +ProgramDescription = 'Copy Gms from Rive test folder to Unreal source modifything to work for unreal' +ScriptPath = Path(__file__).parent.parent.resolve() + +''' +List of files to skip when processing src GM files +''' + +ExcludeGMList = \ +[ + 'gmmain.cpp', +] + +''' +List of files to include from common folder +''' + +IncludeCommmonList = \ +[ + 'write_png_file.hpp', + 'write_png_file.cpp', + 'testing_window.hpp', + 'testing_window.cpp', + 'queue.hpp', + 'rand.hpp', + 'test_harness.hpp', + 'test_harness.cpp', + 'tcp_client.hpp', + 'tcp_client.cpp', + 'rive_mgr.hpp', + 'rive_mgr.cpp', +] + +''' +updates @file_data to work with unreal build system +We only update .cpp files though, that way we dont need to redefine things at then end + +First it tries to insert UNREAL_INSERTS_START before the first #include line +if there are no #include lines it will attempt to insert at the first empty line +if there are no empty lines it will just insert at the very front + +UNREAL_INSERTS_END always insert at the end +''' + +def update_file_data(file_data : str, file_path: Path): + + if file_path.suffix != '.cpp': + return file_data + + first_include = file_data.find('#include') + if first_include != -1: + if first_include != 0: + return file_data[:first_include-1] + UNREAL_INSERTS_START + file_data[first_include-1:] + UNREAL_INSERTS_END + else: + return UNREAL_INSERTS_START + file_data + UNREAL_INSERTS_END + + first_empty_line = file_data.find('#include') + if first_include != -1: + if first_include != 0: + return file_data[:first_empty_line-1] + UNREAL_INSERTS_START + file_data[first_empty_line-1:] + UNREAL_INSERTS_END + + return UNREAL_INSERTS_START + file_data + UNREAL_INSERTS_END + +''' +Iterates through @src_path collecting all files with extension .cpp|c.h|.hpp +Then prepends BEGIN_THRID_PARTY__INCLUDE and appends END_THRID_PARTY_INCLUDE constants +Then writes the file out to dst_path with the same name as the input +@excludes is removed from the generated list of src files before iteratation begins +''' + +def copy_from_path_excluding(src_path, dst_path, excludes=[]): + src_files = [] + src_files.extend(src_path.glob('**/*.cpp')) + src_files.extend(src_path.glob('**/*.h')) + src_files.extend(src_path.glob('**/*.hpp')) + + for file in excludes: + full_file = src_path.joinpath(file) + if full_file in src_files: + src_files.remove(full_file) + + for src in src_files: + print(f'processing {src.name}') + src_data = None + with open(src, 'r') as src_file: + src_data = update_file_data(src_file.read(), src) + + if src_data is None: + print(f'Failed to read {src}, skipping') + continue + + with open(dst_path.joinpath(src.name), 'wt') as dst_file: + dst_file.write(src_data) + +''' +Iterates through @includes appending each value like this "src_path + include_value" +and reading the contents of the file with that path +Then prepends BEGIN_THRID_PARTY__INCLUDE and appends END_THRID_PARTY_INCLUDE constants +Then writes the file out to dst_path with the same name as the input +''' + +def copy_from_path_including(src_path, dst_path, includes=[]): + for src_name in includes: + src = src_path.joinpath(src_name) + + print(f'processing {src.name}') + src_data = None + with open(src, 'r') as src_file: + src_data = update_file_data(src_file.read(), src) + + if src_data is None: + print(f'Failed to read {src}, skipping') + continue + + with open(dst_path.joinpath(src.name), 'wt') as dst_file: + dst_file.write(src_data) + +if __name__ == '__main__': + parser = ArgumentParser(prog='CopyGMs', + description=ProgramDescription) + + parser.add_argument('rive_tests_path') + parser.add_argument('-u','--unreal_path', default=ScriptPath.joinpath("Source/rive_unreal")) + + args = parser.parse_args() + + gm_src_path = Path(args.rive_tests_path).joinpath("gm") + gm_dst_path = Path(args.unreal_path).joinpath("gm") + + common_src_path = Path(args.rive_tests_path).joinpath("common") + common_dst_path = Path(args.unreal_path).joinpath("common") + + copy_from_path_excluding(gm_src_path, gm_dst_path, excludes=ExcludeGMList) + copy_from_path_including(common_src_path, common_dst_path, includes=IncludeCommmonList) diff --git a/tests/unreal/Source/rive_unreal.Target.cs b/tests/unreal/Source/rive_unreal.Target.cs new file mode 100644 index 00000000..10fc376a --- /dev/null +++ b/tests/unreal/Source/rive_unreal.Target.cs @@ -0,0 +1,17 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +using UnrealBuildTool; +using System.Collections.Generic; + +public class rive_unrealTarget : TargetRules +{ + public rive_unrealTarget(TargetInfo Target) : base(Target) + { + Type = TargetType.Game; + DefaultBuildSettings = BuildSettingsVersion.Latest; +#if UE_5_0_OR_LATER + IncludeOrderVersion = EngineIncludeOrderVersion.Latest; +#endif + ExtraModuleNames.Add("rive_unreal"); + } +} diff --git a/tests/unreal/Source/rive_unreal/rive_unreal.Build.cs b/tests/unreal/Source/rive_unreal/rive_unreal.Build.cs new file mode 100644 index 00000000..ff1552fe --- /dev/null +++ b/tests/unreal/Source/rive_unreal/rive_unreal.Build.cs @@ -0,0 +1,22 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +using UnrealBuildTool; + +public class rive_unreal : ModuleRules +{ + public rive_unreal(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; + PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "EnhancedInput", }); + + PrivateDependencyModuleNames.AddRange(new string[] { "Rive", "UElibPNG", "zlib" }); + + // Uncomment if you are using Slate UI + // PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" }); + + // Uncomment if you are using online features + // PrivateDependencyModuleNames.Add("OnlineSubsystem"); + + // To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true + } +} diff --git a/tests/unreal/Source/rive_unreal/rive_unreal.cpp b/tests/unreal/Source/rive_unreal/rive_unreal.cpp new file mode 100644 index 00000000..7d23b219 --- /dev/null +++ b/tests/unreal/Source/rive_unreal/rive_unreal.cpp @@ -0,0 +1,6 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "rive_unreal.h" +#include "Modules/ModuleManager.h" + +IMPLEMENT_PRIMARY_GAME_MODULE(FDefaultGameModuleImpl, rive_unreal, "rive_unreal"); diff --git a/tests/unreal/Source/rive_unreal/rive_unreal.h b/tests/unreal/Source/rive_unreal/rive_unreal.h new file mode 100644 index 00000000..ddbf2e22 --- /dev/null +++ b/tests/unreal/Source/rive_unreal/rive_unreal.h @@ -0,0 +1,5 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" diff --git a/tests/unreal/Source/rive_unrealEditor.Target.cs b/tests/unreal/Source/rive_unrealEditor.Target.cs new file mode 100644 index 00000000..a7b3abf8 --- /dev/null +++ b/tests/unreal/Source/rive_unrealEditor.Target.cs @@ -0,0 +1,17 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +using UnrealBuildTool; +using System.Collections.Generic; + +public class rive_unrealEditorTarget : TargetRules +{ + public rive_unrealEditorTarget(TargetInfo Target) : base(Target) + { + Type = TargetType.Editor; + DefaultBuildSettings = BuildSettingsVersion.Latest; +#if UE_5_0_OR_LATER + IncludeOrderVersion = EngineIncludeOrderVersion.Latest; +#endif + ExtraModuleNames.Add("rive_unreal"); + } +} diff --git a/tests/unreal/premake5.lua b/tests/unreal/premake5.lua new file mode 100644 index 00000000..e69de29b diff --git a/tests/unreal/rive_unreal.uproject b/tests/unreal/rive_unreal.uproject new file mode 100644 index 00000000..237c3297 --- /dev/null +++ b/tests/unreal/rive_unreal.uproject @@ -0,0 +1,30 @@ +{ + "FileVersion": 3, + "EngineAssociation": "5.4", + "Category": "", + "Description": "", + "Modules": [ + { + "Name": "rive_unreal", + "Type": "Runtime", + "LoadingPhase": "Default" + } + ], + "Plugins": [ + { + "Name": "ModelingToolsEditorMode", + "Enabled": true, + "TargetAllowList": [ + "Editor" + ] + }, + { + "Name": "VisualStudioTools", + "Enabled": true, + "MarketplaceURL": "com.epicgames.launcher://ue/marketplace/product/362651520df94e4fa65492dbcba44ae2", + "SupportedTargetPlatforms": [ + "Win64" + ] + } + ] +} \ No newline at end of file