FidelityFX FSR v2.0.1

pull/1/head v2.0.1
Rys Sommefeldt 2022-06-20 20:58:16 +07:00
commit c8fc17d281
164 changed files with 50572 additions and 0 deletions

4
.gitignore vendored

@ -0,0 +1,4 @@
bin/
/.vs/fidelityfx-fsr2/v16
/.vs
out/

@ -0,0 +1,65 @@
variables:
SampleName: FSR2_Sample
CMakeConfig: -G "Visual Studio 16 2019" -A x64
GIT_SUBMODULE_STRATEGY: normal
FF_USE_FASTZIP: "true"
ARTIFACT_COMPRESSION_LEVEL: "fast"
stages:
- build
- deploy
build_dx12:
tags:
- windows
- amd64
stage: build
artifacts:
paths:
- bin/
script:
- 'cmake -A x64 -S . -B build/DX12 -DGFX_API=DX12'
- 'cmake --build build/DX12 --config Release --parallel 4 -- /p:CL_MPcount=16'
build_vk:
tags:
- windows
- amd64
stage: build
artifacts:
paths:
- bin/
script:
- 'cmake -A x64 -S . -B build/VK -DGFX_API=VK'
- 'cmake --build build/VK --config Release --parallel 4 -- /p:CL_MPcount=16'
package_sample:
tags:
- windows
- amd64
stage: deploy
dependencies:
- build_dx12
- build_vk
script:
- echo "Packaging build"
- echo cd .\bin\ > %SampleName%_DX12.bat
- echo start %SampleName%_DX12.exe >> %SampleName%_DX12.bat
- copy %VULKAN_SDK%\Bin\glslc.exe bin
- echo cd .\bin\ > %SampleName%_VK.bat
- echo start %SampleName%_VK.exe >> %SampleName%_VK.bat
artifacts:
name: "%SampleName%-%CI_COMMIT_TAG%-%CI_COMMIT_REF_NAME%-%CI_COMMIT_SHORT_SHA%"
paths:
- "bin/"
- "media/cauldron-media/AbandonedWarehouse/"
- "media/cauldron-media/Sponza-New/"
- "media/cauldron-media/envmaps/"
- "media/cauldron-media/noise/"
- "media/cauldron-media/color_ramp_bt2020_dcip3/"
- "media/cauldron-media/readme.md"
- "media/cauldron-media/screenshot.png"
- "README.md"
- "LICENSE.txt"
- "%SampleName%_DX12.bat"
- "%SampleName%_VK.bat"

6
.gitmodules vendored

@ -0,0 +1,6 @@
[submodule "media/cauldron-media"]
path = media/cauldron-media
url = ../../GPUOpen-LibrariesAndSDKs/Cauldron-Media.git
[submodule "libs/cauldron"]
path = libs/cauldron
url = ../../GPUOpen-LibrariesAndSDKs/Cauldron.git

@ -0,0 +1,90 @@
# This file is part of the FidelityFX SDK.
#
# Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
cmake_minimum_required(VERSION 3.12.1)
option (GFX_API_DX12 "Build with DX12" ON)
if(NOT DEFINED GFX_API)
project (FSR2_Sample)
else()
project (FSR2_Sample_${GFX_API})
set_property(DIRECTORY ${CMAKE_PROJECT_DIR} PROPERTY VS_STARTUP_PROJECT ${PROJECT_NAME})
if(GFX_API STREQUAL DX12)
set(GFX_API_DX12 ON)
set(GFX_API_VK OFF)
elseif(GFX_API STREQUAL VK)
set(GFX_API_DX12 OFF)
set(GFX_API_VK ON)
else()
message(STATUS "----------------------------------------------------------------------------------------")
message(STATUS "")
message(STATUS "** Almost there!!")
message(STATUS "")
message(STATUS " This framework supports DX12 and VULKAN, you need to invoke cmake in one of these ways:")
message(STATUS "")
message(STATUS " Examples:")
message(STATUS " Generate selected one:")
message(STATUS " cmake <project_root_dir> -DGFX_API=DX12")
message(STATUS " cmake <project_root_dir> -DGFX_API=VK")
message(STATUS " Generate with switches (Default is ON):")
message(STATUS " cmake <project_root_dir> [-DGFX_API_DX12=ON|OFF] [-DGFX_API_VK=ON|OFF]")
message(STATUS "")
message(STATUS "----------------------------------------------------------------------------------------")
message(FATAL_ERROR "")
endif()
endif()
# Check MSVC toolset version, Visual Studio 2019 required
if(MSVC_TOOLSET_VERSION VERSION_LESS 142)
message(FATAL_ERROR "Cannot find MSVC toolset version 142 or greater. Please make sure Visual Studio 2019 or newer installed")
endif()
# ouput exe to bin directory
SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_HOME_DIRECTORY}/bin)
foreach( OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES} )
string( TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG )
set( CMAKE_RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${CMAKE_HOME_DIRECTORY}/bin )
endforeach( OUTPUTCONFIG CMAKE_CONFIGURATION_TYPES )
add_compile_options(/MP)
add_compile_definitions($<$<CONFIG:RelWithDebInfo>:USE_PIX>)
# override build options in ffx-fsr2-api cmake
option (FFX_FSR2_API_DX12 "Build FSR 2.0 DX12 backend" ${GFX_API_DX12})
option (FFX_FSR2_API_VK "Build FSR 2.0 Vulkan backend" ${GFX_API_VK})
# reference libs used by both backends
add_subdirectory(libs/cauldron)
add_subdirectory(src/Common)
add_subdirectory(src/ffx-fsr2-api)
if(GFX_API_VK)
find_package(Vulkan REQUIRED)
add_subdirectory(src/VK)
endif()
if(GFX_API_DX12)
add_subdirectory(src/DX12)
endif()

@ -0,0 +1,19 @@
Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

@ -0,0 +1,822 @@
# FidelityFX Super Resolution 2.0.1 (FSR 2.0)
Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
![Screenshot](screenshot.png)
AMD FidelityFX Super Resolution 2.0 (FSR 2) is an open source, high-quality solution for producing high resolution frames from lower resolution inputs.
You can find the binaries for FidelityFX FSR in the release section on GitHub.
# Super Resolution 2.0
### Table of contents
- [Introduction](#introduction)
- [Shading language requirements](#shading-language-requirements)
- [Quick start checklist](#quick-start-checklist)
- [Integration guidelines](#integration-guidelines)
- [Quality modes](#quality-modes)
- [Performance](#performance)
- [Memory requirements](#memory-requirements)
- [Input resources](#input-resources)
- [Depth buffer configurations](#depth-buffer-configurations)
- [Providing motion vectors](#providing-motion-vectors)
- [Reactive mask](#reactive-mask)
- [Transparency & composition mask](#transparency-and-composition-mask)
- [Automatically generating reactivity](#automatically-generating-reactivity)
- [Placement in the frame](#placement-in-the-frame)
- [Host API](#host-api)
- [Modular backend](#modular-backend)
- [Memory management](#memory-management)
- [Temporal antialiasing](#temporal-antialiasing)
- [Camera jitter](#camera-jitter)
- [Camera jump cuts](#camera-jump-cuts)
- [Mipmap biasing](#mipmap-biasing)
- [HDR support](#hdr-support)
- [Falling back to 32bit floating point](#falling-back-to-32bit-floating-point)
- [64-wide wavefronts](#64-wide-wavefronts)
- [The technique](#the-technique)
- [Algorithm structure](#algorithm-structure)
- [Compute luminance pyramid](#compute-luminance-pyramid)
- [Adjust input color](#adjust-input-color)
- [Reconstruct & dilate](#reconstruct-and-dilate)
- [Depth clip](#depth-clip)
- [Create locks](#create-locks)
- [Reproject & accumulate](#reproject-accumulate)
- [Robust Contrast Adaptive Sharpening (RCAS)](#robust-contrast-adaptive-sharpening-rcas)
- [Building the sample](#building-the-sample)
- [Version history](#version-history)
- [Limitations](#limitations)
- [References](#references)
# Introduction
**FidelityFX Super Resolution 2.0** (or **FSR2** for short) is a cutting-edge upscaling technique developed from the ground up to produce high resolution frames from lower resolution inputs.
![alt text](docs/media/super-resolution-temporal/overview.svg "A diagram showing the input resources to the super resolution (temporal) algorithm.")
FSR2 uses temporal feedback to reconstruct high-resolution images while maintaining and even improving image quality compared to native rendering.
FSR2 can enable “practical performance” for costly render operations, such as hardware ray tracing.
## Shading language requirements
`HLSL` `CS_6_2` `CS_6_6*`
\* - CS_6_6 is used on some hardware which supports 64-wide wavefronts.
# Quick start checklist
To use FSR2 you should follow the steps below:
1. Double click [`GenerateSolutions.bat`](build/GenerateSolutions.bat) in the [`build`](build) directory.
2. Open the solution matching your API, and build the solution.
3. Copy the API library from `bin/ffx_fsr2_api` into the folder containing a folder in your project which contains third-party libraries.
4. Copy the library matching the FSR2 backend you want to use, e.g.: `bin/ffx_fsr2_api/ffx_fsr2_api_dx12_x64.lib` for DirectX12.
5. Copy the following core API header files from src/ffx-fsr2-api into your project: `ffx_fsr2.h`, `ffx_types.h`, `ffx_error.h`, `ffx_fsr2_interface.h`, `ffx_util.h`, `shaders/ffx_fsr2_common.h`, and `shaders/ffx_fsr2_resources.h`. Care should be taken to maintain the relative directory structure at the destination of the file copying.
6. Copy the header files for the API backend of your choice, e.g. for DirectX12 you would copy `dx12/ffx_fsr2_dx12.h` and `dx12/shaders/ffx_fsr2_shaders_dx12.h`. Care should be taken to maintain the relative directory structure at the destination of the file copying.
7. Include the `ffx_fsr2.h` header file in your codebase where you wish to interact with FSR2.
8. Create a backend for your target API. E.g. for DirectX12 you should call [`ffxFsr2GetInterfaceDX12`](../src/ffx-fsr2-api/ffx_fsr2.h#L204). A scratch buffer should be allocated of the size returned by calling [`ffxFsr2GetScratchMemorySizeDX12`](../src/ffx-fsr2-api/dx12/ffx_fsr2_dx12.h#L40) and the pointer to that buffer passed to [`ffxFsr2GetInterfaceDX12`](../src/ffx-fsr2-api/dx12/ffx_fsr2_dx12.h#L55).
9. Create a FSR2 context by calling [`ffxFsr2ContextCreate`](../src/ffx-fsr2-api/ffx_fsr2.h#L204). The parameters structure should be filled out matching the configuration of your application. See the API reference documentation for more details.
10. Each frame you should call [`ffxFsr2ContextDispatch`](../src/ffx-fsr2-api/ffx_fsr2.h#L254) to launch FSR2 workloads. The parameters structure should be filled out matching the configuration of your application. See the API reference documentation for more details.
11. When your application is terminating (or you wish to destroy the context for another reason) you should call [`ffxFsr2ContextDestroy`](../src/ffx-fsr2-api/ffx_fsr2.h#L268). The GPU should be idle before calling this function.
12. Sub-pixel jittering should be applied to your application's projection matrix. This should be done when performing the main rendering of your application. You should use the [`ffxFsr2GetJitterOffset`](../src/ffx-fsr2-api/ffx_fsr2.h#L268) function to compute the precise jitter offsets. See [Camera jitter](#camera-jitter) section for more details.
13. For the best upscaling quality it is strongly advised that you populate the [Reactive mask](#reactive-mask) and [Transparency & composition mask](#transparency-and-composition-mask) according to our guidelines. You can also use [`ffxFsr2ContextGenerateReactiveMask`](../src/ffx-fsr2-api/ffx_fsr2.h#L265) as a starting point.
14. Applications should expose [scaling modes](#scaling-modes), in their user interface in the following order: Quality, Balanced, Performance, and (optionally) Ultra Performance.
15. Applications should also expose a sharpening slider to allow end users to acheive additional quality.
# Integration guidelines
## Scaling modes
For the convenience of end users, the FSR2 API provides a number of preset scaling ratios which are named.
| Quality | Per-dimension scaling factor |
|-------------------|------------------------------|
| Quality | 1.5x |
| Balanced | 1.7x |
| Performance | 2.0x |
| Ultra performance | 3.0x |
We strongly recommend that applications adopt consistent naming and scaling ratios in their user interface. This is to ensure that user experience is consistent for your application's users which may have experience of other applications using FSR2.
## Performance
Depending on your target hardware and operating configuration FSR2 will operate at different performance levels.
The table below summarizes the measured performance of FSR2 on a variety of hardware.
| Target resolution | Quality | RX 6950 XT | RX 6900 XT | RX 6800 XT | RX 6800 | RX 6700 XT | RX 6600 XT | RX 5700 XT | RX Vega 56 | RX 590 |
|-------------------|------------------|------------|------------|------------|---------|------------|------------|------------|------------|--------|
| 3840x2160 | Quality (1.5x) | 1.1ms | 1.2ms | 1.3ms | 1.6ms | 1.8ms | 3.0ms | 2.4ms | 3.7ms | 5.6ms |
| | Balanced (1.7x) | 1.0ms | 1.1ms | 1.1ms | 1.4ms | 1.7ms | 2.7ms | 2.2ms | 3.3ms | 5.3ms |
| | Performance (2x) | 0.9ms | 1.0ms | 1.0ms | 1.4ms | 1.5ms | 2.3ms | 2.0ms | 3.1ms | 4.9ms |
| | Ultra perf. (3x) | 0.8ms | 0.9ms | 0.9ms | 1.2ms | 1.4ms | 1.8ms | 1.7ms | 2.7ms | 4.3ms |
| 2560x1440 | Quality (1.5x) | 0.5ms | 0.5ms | 0.5ms | 0.7ms | 0.8ms | 1.2ms | 1.0ms | 1.6ms | 2.5ms |
| | Balanced (1.7x) | 0.4ms | 0.5ms | 0.5ms | 0.6ms | 0.8ms | 1.0ms | 1.0ms | 1.5ms | 2.4ms |
| | Performance (2x) | 0.4ms | 0.4ms | 0.4ms | 0.5ms | 0.7ms | 0.9ms | 0.9ms | 1.4ms | 2.2ms |
| | Ultra perf. (3x) | 0.3ms | 0.4ms | 0.4ms | 0.5ms | 0.6ms | 0.8ms | 0.7ms | 1.2ms | 1.9ms |
| 1920x1080 | Quality (1.5x) | 0.3ms | 0.3ms | 0.3ms | 0.3ms | 0.5ms | 0.6ms | 0.6ms | 0.9ms | 1.4ms |
| | Balanced (1.7x) | 0.2ms | 0.2ms | 0.3ms | 0.3ms | 0.4ms | 0.6ms | 0.5ms | 0.8ms | 1.3ms |
| | Performance (2x) | 0.2ms | 0.2ms | 0.2ms | 0.3ms | 0.4ms | 0.5ms | 0.5ms | 0.8ms | 1.3ms |
| | Ultra perf. (3x) | 0.2ms | 0.2ms | 0.2ms | 0.3ms | 0.4ms | 0.4ms | 0.4ms | 0.7ms | 1.1ms |
Figures are rounded to the nearest 0.1ms and are without [`enableSharpening`](../src/ffx-fsr2-api/ffx_fsr2.h#L127) set.
## Memory requirements
Using FSR2 requires some additional GPU local memory to be allocated for consumption by the GPU. When using the FSR2 API, this memory is allocated when the FSR2 context is created, and is done so via the series of callbacks which comprise the backend interface. This memory is used to store intermediate surfaces which are computed by the FSR2 algorithm as well as surfaces which are persistent across many frames of the application. The table below includes the amount of memory used by FSR2 under various operating conditions. The "Working set" column indicates the total amount of memory used by FSR2 as the algorithm is executing on the GPU; this is the amount of memory FSR2 will require to run. The "Persistent memory" column indicates how much of the "Working set" column is required to be left intact for subsequent frames of the application; this memory stores the temporal data consumed by FSR2. The "Aliasable memory" column indicates how much of the "Working set" column may be aliased by surfaces or other resources used by the application outside of the operating boundaries of FSR2.
You can take control of resource creation in FSR2 by overriding the resource creation and destruction parts of the FSR2 backend interface. This means that for a perfect integration of FSR2, additional memory which is equal to the "Persistent memory" column of the table below is required depending on your operating conditions.
| Resolution | Quality | Working set (MB) | Persistent memory (MB) | Aliasable memory (MB) |
| -----------|------------------------|------------------|------------------------|-------------------------|
| 3840x2160 | Quality (1.5x) | 293.53MB | 94.92MB | 198.61MB |
| | Balanced (1.7x) | 274.03MB | 94.92MB | 179.11MB |
| | Performance (2x) | 255.68MB | 94.92MB | 160.76MB |
| | Ultra performance (3x) | 227.11MB | 94.92MB | 132.19MB |
| 2560x1440 | Quality (1.5x) | 136.41MB | 84.37MB | 52.04MB |
| | Balanced (1.7x) | 126.97MB | 84.37MB | 42.60MB |
| | Performance (2x) | 117.53MB | 84.37MB | 33.16MB |
| | Ultra performance (3x) | 104.95MB | 84.37MB | 20.58MB |
| 1920x1080 | Quality (1.5x) | 76.46MB | 47.46MB | 29.18MB |
| | Balanced (1.7x) | 71.75MB | 47.46MB | 23.68MB |
| | Performance (2x) | 67.81MB | 47.46MB | 20.79MB |
| | Ultra performance (3x) | 58.38MB | 47.46MB | 11.09MB |
For details on how to manage FSR2's memory requirements please refer to the section of this document dealing with [Memory management](#memory-management).
## Input resources
FSR2 is a temporal algorithm, and therefore requires access to data from both the current and previous frame. The following table enumerates all external inputs required by FSR2.
> The resolution column indicates if the data should be at 'rendered' resolution or 'presentation' resolution. 'Rendered' resolution indicates that the resource should match the resolution at which the application is performing its rendering. Conversely, 'presentation' indicates that the resolution of the target should match that which is to be presented to the user. All resources are from the current rendered frame, for DirectX(R)12 and Vulkan(R) applications all input resources should be transitioned to [`D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE`](https://docs.microsoft.com/en-us/windows/win32/api/d3d12/ne-d3d12-d3d12_resource_states) and [`VK_ACCESS_SHADER_READ_BIT`](https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkAccessFlagBits.html) respectively before calling [`ffxFsr2ContextDispatch`](../src/ffx-fsr2-api/ffx_fsr2.h#L254).
| Name | Resolution | Format | Type | Notes |
| ----------------|------------------------------|------------------------------------|-----------|------------------------------------------------|
| Color buffer | Render | `APPLICATION SPECIFIED` | Texture | The render resolution color buffer for the current frame provided by the application. If the contents of the color buffer are in high dynamic range (HDR), then the [`FFX_FSR2_ENABLE_HIGH_DYNAMIC_RANGE`](../src/ffx-fsr2-api/ffx_fsr2.h#L87) flag should be set in the [`flags`](../src/ffx-fsr2-api/ffx_fsr2.h#L103) field of the [`FfxFsr2ContextDescription`](../src/ffx-fsr2-api/ffx_fsr2.h#L101) structure. |
| Depth buffer | Render | `APPLICATION SPECIFIED (1x FLOAT)` | Texture | The render resolution depth buffer for the current frame provided by the application. The data should be provided as a single floating point value, the precision of which is under the application's control. The configuration of the depth should be communicated to FSR2 via the [`flags`](../src/ffx-fsr2-api/ffx_fsr2.h#L103) field of the [`FfxFsr2ContextDescription`](../src/ffx-fsr2-api/ffx_fsr2.h#L101) structure when creating the [`FfxFsr2Context`](../src/ffx-fsr2-api/ffx_fsr2.h#L164). You should set the [`FFX_FSR2_ENABLE_DEPTH_INVERTED`](../src/ffx-fsr2-api/ffx_fsr2.h#L90) flag if your depth buffer is inverted (that is [1..0] range), and you should set the [`FFX_FSR2_ENABLE_DEPTH_INFINITE`](../src/ffx-fsr2-api/ffx_fsr2.h#L91) flag if your depth buffer has an infinite far plane. If the application provides the depth buffer in `D32S8` format, then FSR2 will ignore the stencil component of the buffer, and create an `R32_FLOAT` resource to address the depth buffer. On GCN and RDNA hardware, depth buffers are stored separately from stencil buffers. |
| Motion vectors | Render or presentation | `APPLICATION SPECIFIED (2x FLOAT)` | Texture | The 2D motion vectors for the current frame provided by the application in [**(<-width, -height>**..**<width, height>**] range. If your application renders motion vectors with a different range, you may use the [`motionVectorScale`](../src/ffx-fsr2-api/ffx_fsr2.h#L125) field of the [`FfxFsr2DispatchDescription`](../src/ffx-fsr2-api/ffx_fsr2.h#L114) structure to adjust them to match the expected range for FSR2. Internally, FSR2 uses 16-bit quantities to represent motion vectors in many cases, which means that while motion vectors with greater precision can be provided, FSR2 will not benefit from the increased precision. The resolution of the motion vector buffer should be equal to the render resolution, unless the [`FFX_FSR2_ENABLE_DISPLAY_RESOLUTION_MOTION_VECTORS`](../src/ffx-fsr2-api/ffx_fsr2.h#L88) flag is set in the [`flags`](../src/ffx-fsr2-api/ffx_fsr2.h#L103) field of the [`FfxFsr2ContextDescription`](../src/ffx-fsr2-api/ffx_fsr2.h#L101) structure when creating the [`FfxFsr2Context`](../src/ffx-fsr2-api/ffx_fsr2.h#L164), in which case it should be equal to the presentation resolution. |
| Reactive mask | Render | `R8_UNORM` | Texture | As some areas of a rendered image do not leave a footprint in the depth buffer or include motion vectors, FSR2 provides support for a reactive mask texture which can be used to indicate to FSR2 where such areas are. Good examples of these are particles, or alpha-blended objects which do not write depth or motion vectors. If this resource is not set, then FSR2's shading change detection logic will handle these cases as best it can, but for optimal results, this resource should be set. For more information on the reactive mask please refer to the [Reactive mask](#reactive-mask) section. |
| Exposure | 1x1 | `R32_FLOAT` | Texture | A 1x1 texture containing the exposure value computed for the current frame. This resource is optional, and may be omitted if the [`FFX_FSR2_ENABLE_AUTO_EXPOSURE`](../src/ffx-fsr2-api/ffx_fsr2.h#L92) flag is set in the [`flags`](../src/ffx-fsr2-api/ffx_fsr2.h#L103) field of the [`FfxFsr2ContextDescription`](../src/ffx-fsr2-api/ffx_fsr2.h#L101) structure when creating the [`FfxFsr2Context`](../src/ffx-fsr2-api/ffx_fsr2.h#L164). |
## Depth buffer configurations
It is strongly recommended that an inverted, infinite depth buffer is used with FSR2. However, alternative depth buffer configurations are supported. An application should inform the FSR2 API of its depth buffer configuration by setting the appropriate flags during the creation of the [`FfxFsr2Context`](../src/ffx-fsr2-api/ffx_fsr2.h#L164). The table below contains the appropriate flags.
| FSR2 flag | Note |
|----------------------------------|--------------------------------------------------------------------------------------------|
| [`FFX_FSR2_ENABLE_DEPTH_INVERTED`](../src/ffx-fsr2-api/ffx_fsr2.h#L90) | A bit indicating that the input depth buffer data provided is inverted [max..0]. |
| [`FFX_FSR2_ENABLE_DEPTH_INFINITE`](../src/ffx-fsr2-api/ffx_fsr2.h#L91) | A bit indicating that the input depth buffer data provided is using an infinite far plane. |
## Providing motion vectors
### Space
A key part of a temporal algorithm (be it antialiasing or upscaling) is the provision of motion vectors. FSR2 accepts motion vectors in 2D which encode the motion from a pixel in the current frame to the position of that same pixel in the previous frame. FSR2 expects that motion vectors are provided by the application in [**<-width, -height>**..**<width, height>**] range; this matches screenspace. For example, a motion vector for a pixel in the upper-left corner of the screen with a value of <width, height> would represent a motion that traversed the full width and height of the input surfaces, originating from the bottom-right corner.
![alt text](docs/media/super-resolution-temporal/motion-vectors.svg "A diagram showing a 2D motion vector.")
If your application computes motion vectors in another space - for example normalized device coordinate space - then you may use the [`motionVectorScale`](../src/ffx-fsr2-api/ffx_fsr2.h#L125) field of the [`FfxFsr2DispatchDescription`](../src/ffx-fsr2-api/ffx_fsr2.h#L114) structure to instruct FSR2 to adjust them to match the expected range for FSR2. The code examples below illustrate how motion vectors may be scaled to screen space. The example HLSL and C++ code below illustrates how NDC-space motion vectors can be scaled using the FSR2 host API.
```HLSL
// GPU: Example of application NDC motion vector computation
float2 motionVector = (currentPosition.xy / currentPosition.w) - (previousPosition.xy / previousPosition.w);
// CPU: Matching FSR 2.0 motionVectorScale configuration
dispatchParameters.motionVectorScale.x = (float)renderWidth;
dispatchParameters.motionVectorScale.y = (float)renderHeight;
```
### Precision & resolution
Internally, FSR2 uses 16bit quantities to represent motion vectors in many cases, which means that while motion vectors with greater precision can be provided, FSR2 will not currently benefit from the increased precision. The resolution of the motion vector buffer should be equal to the render resolution, unless the [`FFX_FSR2_ENABLE_DISPLAY_RESOLUTION_MOTION_VECTORS`](../src/ffx-fsr2-api/ffx_fsr2.h#L88) flag is set in the [`flags`](../src/ffx-fsr2-api/ffx_fsr2.h#L103) field of the [`FfxFsr2ContextDescription`](../src/ffx-fsr2-api/ffx_fsr2.h#L114) structure when creating the [`FfxFsr2Context`](../src/ffx-fsr2-api/ffx_fsr2.h#L164), in which case it should be equal to the presentation resolution.
### Coverage
FSR2 will perform better quality upscaling when more objects provide their motion vectors. It is therefore advised that all opaque, alpha-tested and alpha-blended objects should write their motion vectors for all covered pixels. If vertex shader effects are applied - such as scrolling UVs - these calculations should also be factored into the calculation of motion for the best results. For alpha-blended objects it is also strongly advised that the alpha value of each covered pixel is stored to the corresponding pixel in the [reactive mask](#reactive-mask). This will allow FSR2 to perform better handling of alpha-blended objects during upscaling. The reactive mask is especially important for alpha-blended objects where writing motion vectors might be prohibitive, such as particles.
## Reactive mask
In the context of FSR2, the term "reactivity" means how much influence the samples rendered for the current frame have over the production of the final upscaled image. Typically, samples rendered for the current frame contribute a relatively modest amount to the result computed by FSR2; however, there are exceptions. To produce the best results for fast moving, alpha-blended objects, FSR2 requires the [Reproject & accumulate](#reproject-accumulate) stage to become more reactive for such pixels. As there is no good way to determine from either color, depth or motion vectors which pixels have been rendered using alpha blending, FSR2 performs best when applications explicity mark such areas.
Therefore, it is strongly encouraged that applications provide a reactive mask to FSR2. The reactive mask guides FSR2 on where it should reduce its reliance on historical information when compositing the current pixel, and instead allow the current frame's samples to contribute more to the final result. The reactive mask allows the application to provide a value from [0..1] where 0 indicates that the pixel is not at all reactive (and should use the default FSR2 composition strategy), and a value of 1 indicates the pixel should be fully reactive.
While there are other applications for the reactive mask, the primary application for the reactive mask is producing better results of upscaling images which include alpha-blended objects. A good proxy for reactiveness is actually the alpha value used when compositing an alpha-blended object into the scene, therefore, applications should write `alpha` to the reactive mask. It should be noted that it is unlikely that a reactive value of close to 1 will ever produce good results. Therefore, we recommend clamping the maximum reactive value to around 0.9.
If a [Reactive mask](#reactive-mask) is not provided to FSR2 (by setting the [`reactive`](../src/ffx-fsr2-api/ffx_fsr2.h#L121) field of [`FfxFsr2DispatchDescription`](../src/ffx-fsr2-api/ffx_fsr2.h#L114) to `NULL`) then an internally generated 1x1 texture with a cleared reactive value will be used.
## Transparency & composition mask
In addition to the [Reactive mask](#reactive-mask), FSR2 provides for the application to denote areas of other specialist rendering which should be accounted for during the upscaling process. Examples of such special rendering include areas of raytraced reflections or animated textures.
While the [Reactive mask](#reactive-mask) adjusts the accumulation balance, the [Transparency & composition mask](#transparency-and-composition-mask) adjusts the pixel locks created by FSR2. A pixel with a value of 0 in the [Transparency & composition mask](#ttransparency-and-composition-mask) does not perform any additional modification to the lock for that pixel. Conversely, a value of 1 denotes that the lock for that pixel should be completely removed.
If a [Transparency & composition mask](#transparency-and-composition-mask) is not provided to FSR2 (by setting the [`transparencyAndComposition`](#../src/ffx-fsr2-api/ffx_fsr2.h#L122) field of [`FfxFsr2DispatchDescription`](../src/ffx-fsr2-api/ffx_fsr2.h#L114) to `NULL`) then an internally generated 1x1 texture with a cleared transparency and composition value will be used.
## Automatically generating reactivity
To help applications generate the [Reactive mask](#reactive-mask) and the [Transparency & composition mask](#transparency-and-composition-mask), FSR2 provides an optional helper API. Under the hood, the API launches a compute shader which computes these values for each pixel using a luminance-based heuristic.
Applications wishing to do this can call the [`ffxFsr2ContextGenerateReactiveMask`](../src/ffx-fsr2-api/ffx_fsr2.h#L265) function and should pass two versions of the color buffer, one containing opaque only geometry, and the other containing both opaque and alpha-blended objects.
## Exposure
FSR2 provides two values which control the exposure used when performing upscaling. They are as follows:
1. **Pre-exposure** a value by which we divide the input signal to get back to the original signal produced by the game before any packing into lower precision render targets.
2. **Expsoure** a value which is multiplied against the result of the pre-exposed color value.
The exposure value should match that which the application uses during any subsequent tonemapping passes performed by the application. This means FSR2 will operate consistently with what is likely to be visible in the final tonemapped image.
> In various stages of the FSR2 algorithm described in this document, FSR2 will compute its own exposure value for internal use. It is worth noting that all outputs from FSR2 will have this internal tonemapping reversed before the final output is written. Meaning that FSR2 returns results in the same domain as the original input signal.
Poorly selected exposure values can have a drastic impact on the final quality of FSR2's upscaling. Therefore, it is recommended that [`FFX_FSR2_ENABLE_AUTO_EXPOSURE`](../src/ffx-fsr2-api/ffx_fsr2.h#L92) is used by the application, unless there is a particular reason not to. When [`FFX_FSR2_ENABLE_AUTO_EXPOSURE`](../src/ffx-fsr2-api/ffx_fsr2.h#L92) is set in the [`flags`](../src/ffx-fsr2-api/ffx_fsr2.h#L103) field of the [`FfxFsr2ContextDescription`](../src/ffx-fsr2-api/ffx_fsr2.h#L101) structure, the exposure calculation shown in the HLSL code below is used to compute the exposure value, this matches the exposure response of ISO 100 film stock.
```HLSL
float ComputeAutoExposureFromAverageLog(float averageLogLuminance)
{
const float averageLuminance = exp(averageLogLuminance);
const float S = 100.0f; // ISO arithmetic speed
const float K = 12.5f;
const float exposureIso100 = log2((averageLuminance * S) / K);
const float q = 0.65f;
const float luminanceMax = (78.0f / (q * S)) * pow(2.0f, exposureIso100);
return 1 / luminanceMax;
}
```
## Placement in the frame
The primary goal of FSR2 is to improve application rendering performance by using a temporal upscaling algorithm relying on a number of inputs. Therefore, its placement in the pipeline is key to ensuring the right balance between the highest quality visual quality and great performance.
![alt text](docs/media/super-resolution-temporal/pipeline-placement.svg "A diagram showing the placement of FidelityFX Super Resolution (Temporal) in the wider rendering pipeline.")
With any image upscaling approach is it important to understand how to place other image-space algorithms with respect to the upscaling algorithm. Placing these other image-space effects before the upscaling has the advantage that they run at a lower resolution, which of course confers a performance advantage onto the application. However, it may not be appropriate for some classes of image-space techniques. For example, many applications may introduce noise or grain into the final image, perhaps to simulate a physical camera. Doing so before an upscaler might cause the upscaler to amplify the noise, causing undesirable artifacts in the resulting upscaled image. The following table divides common real-time image-space techniques into two columns. 'Post processing A' contains all the techniques which typically would run before FSR2's upscaling, meaning they would all run at render resolution. Conversely, the 'Post processing B' column contains all the techniques which are recommend to run after FSR2, meaning they would run at the larger, presentation resolution.
| Post processing A | Post processing B |
|--------------------------------|----------------------|
| Screenspace reflections | Film grain |
| Screenspace ambient occlusion | Chromatic abberation |
| Denoisers (shadow, reflections)| Vignette |
| Exposure (optional) | Tonemapping |
| | Bloom |
| | Depth of field |
| | Motion blur |
Please note that the recommendations here are for guidance purposes only and depend on the precise characteristics of your application's implementation.
## Host API
While it is possible to generate the appropriate intermediate resources, compile the shader code, set the bindings, and submit the dispatches, it is much easier to use the FSR2 host API which is provided.
To use to the API, you should link the FSR2 libraries (more on which ones shortly) and include the `ffx_fsr2.h` header file, which in turn has the following header dependencies:
```
ffx_assert.h
ffx_error.h
ffx_fsr2_interface.h
ffx_types.h
ffx_util.h
```
To use the FSR2 API, you should link `ffx_fsr2_api_x64.lib` which will provide the symbols for the application-facing APIs. However, FSR2's API has a modular backend, which means that different graphics APIs and platforms may be targetted through the use of a matching backend. Therefore, you should further include the backend lib matching your requirements, referencing the table below.
| Target | Library name |
|---------------------|-------------------------|
| DirectX(R)12 | `ffx_fsr2_dx12_x64.lib` |
| Vulkan(R) | `ffx_fsr2_vk_x64.lib` |
> Please note the modular architecture of the FSR2 API allows for custom backends to be implemented. See the [Modular backend](#modular-backend) section for more details.
To begin using the API, the application should first create a [`FfxFsr2Context`](../src/ffx-fsr2-api/ffx_fsr2.h#L164) structure. This structure should be located somewhere with a lifetime approximately matching that of your backbuffer; somewhere on the application's heap is usually a good choice. By calling [`ffxFsr2ContextCreate`](../src/ffx-fsr2-api/ffx_fsr2.h#L204) the [`FfxFsr2Context`](../src/ffx-fsr2-api/ffx_fsr2.h#L164) structure will be populated with the data it requires. Moreover, a number of calls will be made from [`ffxFsr2ContextCreate`](../src/ffx-fsr2-api/ffx_fsr2.h#L204) to the backend which is provided to [`FfxFsr2Context`](../src/ffx-fsr2-api/ffx_fsr2.h#L164) as part of the [`FfxFsr2ContextDescription`](../src/ffx-fsr2-api/ffx_fsr2.h#L101) structure. These calls will perform such tasks as creating intermediate resources required by FSR2 and setting up shaders and their associated pipeline state. The FSR2 API does not perform any dynamic memory allocation.
Each frame of your application where upscaling is required, you should call [`ffxFsr2ContextDispatch`](../src/ffx-fsr2-api/ffx_fsr2.h#L254). This function accepts the [`FfxFsr2Context`](../src/ffx-fsr2-api/ffx_fsr2.h#L164) structure that was created earlier in the application's lifetime as well as a description of precisely how upscaling should be performed and on which data. This description is provided by the application filling out a [`FfxFsr2DispatchDescription`](../src/ffx-fsr2-api/ffx_fsr2.h#L114) structure.
Destroying the context is performed by calling [`ffxFsr2ContextDestroy`](../src/ffx-fsr2-api/ffx_fsr2.h#L268). Please note, that the GPU should be idle before attempting to call [`ffxFsr2ContextDestroy`](../src/ffx-fsr2-api/ffx_fsr2.h#L268), and the function does not perform implicit synchronization to ensure that resources being accessed by FSR2 are not currently in flight. The reason for this choice is to avoid FSR2 introducing additional GPU flushes for applications who already perform adequate synchronization at the point where they might wish to destroy the [`FfxFsr2Context`](../src/ffx-fsr2-api/ffx_fsr2.h#L164), this allows an application to perform the most efficient possible creation and teardown of the FSR2 API when required.
There are additional helper functions which are provided as part of the FSR2 API. These helper functions perform tasks like the computation of sub-pixel jittering offsets, as well as the calculation of rendering resolutions based on dispatch resolutions and the default [scaling modes](#scaling-modes) provided by FSR2.
For more exhaustive documentation of the FSR2 API, you can refer to the API reference documentation provided.
## Modular backend
The design of the FSR2 API means that the core implementation of the FSR2 algorithm is unaware upon which rendering API it sits. Instead, FSR2 calls functions provided to it through an interface, allowing different backends to be used with FSR2. This design also allows for applications integrating FSR2 to provide their own backend implementation, meaning that platforms which FSR2 does not currently support may be targeted by implementing a handful of functions. Moreover, applications which have their own rendering abstractions can also implement their own backend, taking control of all aspects of FSR2's underlying function, including memory management, resource creation, shader compilation, shader resource bindings, and the submission of FSR2 workloads to the graphics device.
![alt text](docs/media/super-resolution-temporal/api-architecture.svg "A diagram showing the high-level architecture of the FSR2 API.")
Out of the box, the FSR2 API will compile into multiple libraries following the separation already outlined between the core API and the backends. This means if you wish to use the backends provided with FSR2 you should link both the core FSR2 API lib as well the backend matching your requirements.
> The public release of FSR2 comes with DirectX(R)12 and Vulkan(R) backends, but other backends are available upon request. Talk with your AMD Developer Technology representative for more information.
## Memory management
If the FSR2 API is used with one of the supplied backends (e.g: DirectX(R)12 or Vulkan(R)) then all the resources required by FSR2 are created as committed resources directly using the graphics device provided by the host application. However, by overriding the create and destroy family of functions present in the backend interface it is possible for an application to more precisely control the memory management of FSR2.
To do this, you can either provide a full custom backend to FSR2 via the [`FfxFsr2ContextDescription`](../src/ffx-fsr2-api/ffx_fsr2.h#L101) structure passed to [`ffxFsr2ContextCreate`](../src/ffx-fsr2-api/ffx_fsr2.h#L204) function, or you can retrieve the backend for your desired API and override the resource creation and destruction functions to handle them yourself. To do this, simply overwrite the [`fpCreateResource`](../src/ffx-fsr2-api/ffx_fsr2_interface.h#L403) and [`fpDestroyResource`](../src/ffx-fsr2-api/ffx_fsr2_interface.h#L399) function pointers.
``` CPP
// Setup DX12 interface.
const size_t scratchBufferSize = ffxFsr2GetScratchMemorySizeDX12();
void* scratchBuffer = malloc(scratchBufferSize);
FfxErrorCode errorCode = ffxFsr2GetInterfaceDX12(&contextDescription.callbacks, m_pDevice->GetDevice(), scratchBuffer, scratchBufferSize);
FFX_ASSERT(errorCode == FFX_OK);
// Override the resource creation and destruction.
contextDescription.callbacks.createResource = myCreateResource;
contextDescription.callbacks.destroyResource = myDestroyResource;
// Set up the context description.
contextDescription.device = ffxGetDeviceDX12(m_pDevice->GetDevice());
contextDescription.maxRenderSize.width = renderWidth;
contextDescription.maxRenderSize.height = renderHeight;
contextDescription.displaySize.width = displayWidth;
contextDescription.displaySize.height = displayHeight;
contextDescription.flags = FFX_FSR2_ENABLE_HIGH_DYNAMIC_RANGE
| FFX_FSR2_ENABLE_DEPTH_INVERTED
| FFX_FSR2_ENABLE_AUTO_EXPOSURE;
// Create the FSR2 context.
errorCode = ffxFsr2ContextCreate(&context, &contextDescription);
FFX_ASSERT(errorCode == FFX_OK);
```
One interesting advantage to an application taking control of the memory management required for FSR2 is that resource aliasing maybe performed, which can yield a memory saving. The table present in [Memory requirements](#memory-requirements) demonstrates the savings available through using this technique. In order to realise the savings shown in this table, an appropriate area of memory - the contents of which are not required to survive across a call to the FSR2 dispatches - should be found to share with the aliasable resources required for FSR2. Each [`FfxFsr2CreateResourceFunc`](../src/ffx-fsr2-api/ffx_fsr2_interface.h#L197) call made by FSR2's core API through the FSR2 backend interface will contains a set of flags as part of the [`FfxCreateResourceDescription`](../src/ffx-fsr2-api/ffx_types.h#L251) structure. If the [`FFX_RESOURCE_FLAGS_ALIASABLE`](../src/ffx-fsr2-api/ffx_types.h#L101) is set in the [`flags`](../src/ffx-fsr2-api/ffx_types.h#L208) field this indicates that the resource may be safely aliased with other resources in the rendering frame.
## Temporal Antialiasing
Temporal antialiasing (TAA) is a technique which uses the output of previous frames to construct a higher quality output from the current frame. As FSR2 has a similar goal - albeit with the additional goal of also increasing the resolution of the rendered image - there is no longer any need to include a separate TAA pass in your application.
## Camera jitter
FSR2 relies on the application to apply sub-pixel jittering while rendering - this is typically included in the projection matrix of the camera. To make the application of camera jitter simple, the FSR2 API provides a small set of utility function which computes the sub-pixel jitter offset for a particular frame within a sequence of separate jitter offsets.
``` CPP
int32_t ffxFsr2GetJitterPhaseCount(int32_t renderWidth, int32_t displayWidth);
FfxErrorCode ffxFsr2GetJitterOffset(float* outX, float* outY, int32_t jitterPhase, int32_t sequenceLength);
```
Internally, these function implement a Halton[2,3] sequence [[Halton](#references)]. The goal of the Halton sequence is to provide spatially separated points, which cover the available space.
![alt text](docs/media/super-resolution-temporal/jitter-space.svg "A diagram showing how to map sub-pixel jitter offsets to projection offsets.")
It is important to understand that the values returned from the [`ffxFsr2GetJitterOffset`](../src/ffx-fsr2-api/ffx_fsr2.h#L268) are in unit pixel space, and in order to composite this correctly into a projection matrix we must convert them into projection offsets. The diagram above shows a single pixel in unit pixel space, and in projection space. The code listing below shows how to correctly composite the sub-pixel jitter offset value into a projection matrix.
``` CPP
const int32_t jitterPhaseCount = ffxFsr2GetJitterPhaseCount(renderWidth, displayWidth);
float jitterX = 0;
float jitterY = 0;
ffxFsr2GetJitterOffset(&jitterX, &jitterY, index, jitterPhaseCount);
// Calculate the jittered projection matrix.
const float jitterX = 2.0f * jitterX / (float)renderWidth;
const float jitterY = -2.0f * jitterY / (float)renderHeight;
const Matrix4 jitterTranslationMatrix = translateMatrix(Matrix3::identity, Vector3(jitterX, jitterY, 0));
const Matrix4 jitteredProjectionMatrix = jitterTranslationMatrix * projectionMatrix;
```
Jitter should be applied to *all* rendering. This includes opaque, alpha transparent, and raytraced objects. For rasterized objects, the sub-pixel jittering values calculated by the [`ffxFsr2GetJitterOffset`](../src/ffx-fsr2-api/ffx_fsr2.h#L268) function can be applied to the camera projection matrix which is ultimately used to perform transformations during vertex shading. For raytraced rendering, the sub-pixel jitter should be applied to the ray's origin - often the camera's position.
Whether you elect to use the recommended [`ffxFsr2GetJitterOffset`](../src/ffx-fsr2-api/ffx_fsr2.h#L268) function or your own sequence generator, you must set the [`jitterOffset`](../src/ffx-fsr2-api/ffx_fsr2.h#L124) field of the [`FfxFsr2DispatchDescription`](../src/ffx-fsr2-api/ffx_fsr2.h#L114) structure to inform FSR2 of the jitter offset that has been applied in order to render each frame. Moreover, if not using the recommended [`ffxFsr2GetJitterOffset`](../src/ffx-fsr2-api/ffx_fsr2.h#L268) function, care should be taken that your jitter sequence never generates a null vector; that is value of 0 in both the X and Y dimensions.
The table below shows the jitter sequence length for each of the default quality modes.
| Quality mode | Scaling factor | Sequence length |
|-------------------|-------------------------|-----------------|
| Quality | 1.5x (per dimension) | 18 |
| Balanced | 1.7x (per dimension) | 23 |
| Performance | 2.0x (per dimension) | 32 |
| Ultra performance | 3.0x (per dimension) | 72 |
| Custom | [1..n]x (per dimension) | `ceil(8 * n^2)` |
## Camera jump cuts
Most applications with real-time rendering have a large degree of temporal consistency between any two consecutive frames. However, there are cases where a change to a camera's transformation might cause an abrupt change in what is rendered. In such cases, FSR2 is unlikely to be able to reuse any data it has accumulated from previous frames, and should clear this data such to exclude it from consideration in the compositing process. In order to indicate to FSR2 that a jump cut has occurred with the camera you should set the [`reset`](../src/ffx-fsr2-api/ffx_fsr2.h#L131) field of the [`FfxFsr2DispatchDescription`](../src/ffx-fsr2-api/ffx_fsr2.h#L114) structure to `true` for the first frame of the discontinuous camera transformation.
Rendering performance may be slightly less than typical frame-to-frame operation when using the reset flag, as FSR2 will clear some additional internal resources.
## Mipmap biasing
Applying a negative mipmap biasing will typically generate an upscaled image with better texture detail. We recommend applying the following formula to your Mipmap bias:
``` CPP
mipBias = log2(renderResolution/displayResolution) - 1.0;
```
It is suggested that applications adjust the MIP bias for specific high-frequency texture content which is susceptible to showing temporal aliasing issues.
The following table illustrates the mipmap biasing factor which results from evaluating the above pseudocode for the scaling ratios matching the suggested quality modes that applications should expose to end users.
| Quality mode | Scaling factor | Mipmap bias |
|-------------------|-----------------------|-------------|
| Quality | 1.5X (per dimension) | -1.58 |
| Balanced | 1.7X (per dimension) | -1.76 |
| Performance | 2.0X (per dimension) | -2.0 |
| Ultra performance | 3.0X (per dimension) | -2.58 |
## HDR support
High dynamic range images are supported in FSR2. To enable this, you should set the [`FFX_FSR2_ENABLE_HIGH_DYNAMIC_RANGE`](../src/ffx-fsr2-api/ffx_fsr2.h#L87) bit in the [`flags`](../src/ffx-fsr2-api/ffx_fsr2.h#L103) field of the [`FfxFsr2ContextDescription`](../src/ffx-fsr2-api/ffx_fsr2.h#L101) structure. Images should be provided to FSR2 in linear color space.
> Support for additional color spaces might be provided in a future revision of FSR2.
## Falling back to 32-bit floating point
FSR2 was designed to take advantage of half precision (FP16) hardware acceleration to achieve the highest possible performance. However, to provide the maximum level of compatibility and flexibility for applications, FSR2 also includes the ability to compile the shaders using full precision (FP32) operations.
It is recommended to use the FP16 version of FSR2 on all hardware which supports it. You can query your graphics card's level of support for FP16 by querying the [`D3D12_FEATURE_DATA_SHADER_MIN_PRECISION_SUPPORT`](https://docs.microsoft.com/en-us/windows/win32/api/d3d12/ne-d3d12-d3d12_shader_min_precision_support) capability in DirectX(R)12 - you should check that the `D3D[11/12]_SHADER_MIN_PRECISION_16_BIT` is set, and if it is not, fallback to the FP32 version of FSR2. For Vulkan, if [`VkPhysicalDeviceFloat16Int8FeaturesKHR::shaderFloat16`](https://khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkPhysicalDeviceShaderFloat16Int8FeaturesKHR.html) is not set, then you should fallback to the FP32 version of FSR2. Similarly, if [`VkPhysicalDevice16BitStorageFeatures::storageBuffer16BitAccess`](https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkPhysicalDevice16BitStorageFeatures.html) is not set, you should also fallback to the FP32 version of FSR2.
To enable the FP32 path in the FSR2 shader source code, you should define `FFX_HALF` to be `1`. In order to share the majority of the algorithm's source code between both FP16 and FP32 (ensuring a high level of code sharing to support ongoing maintenance), you will notice that the FSR2 shader source code uses a set of type macros which facilitate easy switching between 16-bit and 32-bit base types in the shader source.
| FidelityFX type | FP32 | FP16 |
|-----------------|-------------|-----------------|
| `FFX_MIN16_F` | `float` | `min16float` |
| `FFX_MIN16_F2` | `float2` | `min16float2` |
| `FFX_MIN16_F3` | `float3` | `min16float3` |
| `FFX_MIN16_F4` | `float4` | `min16float4` |
The table above enumerates the mappings between the abstract FidelityFX SDK types, and the underlaying intrinsic type which will be substituted depending on the configuration of the shader source during compilation.
## 64-wide wavefronts
Modern GPUs execute collections of threads - called wavefronts - together in a SIMT fashion. The precise number of threads which constitute a single wavefront is a hardware-specific quantity. Some hardware, such as AMD's GCN and RDNA-based GPUs support collecting 64 threads together into a single wavefront. Depending on the precise characteristics of an algorithm's execution, it may be more or less advantageous to prefer a specific wavefront width. With the introduction of Shader Model 6.6, Microsoft added the ability to specific the width of a wavefront via HLSL. For hardware, such as RDNA which supports both 32 and 64 wide wavefront widths, this is a very useful tool for optimization purposes, as it provides a clean and portable way to ask the driver software stack to execute a wavefront with a specific width.
For DirectX(R)12 based applications which are running on RDNA and RDNA2-based GPUs and using the Microsoft Agility SDK, the FSR2 host API will select a 64-wide wavefront width.
# The technique
## Algorithm structure
The FSR2 algorithm is implemented in a series of stages, which are as follows:
1. Compute luminance pyramid
2. Adjust input color
3. Reconstruct & dilate
4. Depth clip
5. Create locks
6. Reproject & accumulate
7. Robust Contrast Adaptive Sharpening (RCAS)
Each pass stage of the algorithm is laid out in the sections following this one, but the data flow for the complete FSR2 algorithm is shown in the diagram below.
![alt text](docs/media/super-resolution-temporal/algorithm-structure.svg "A diagram showing all passes in the FSR2 algorithm.")
## Compute luminance pyramid
The compute luminance pyramid stage has two responsibilities:
1. To produce a lower resolution version of the input color's luminance. This is used by shading change detection in the accumulation pass.
2. To produce a 1x1 exposure texture which is optionally used by the exposure calculations of the [Adjust input color](#adjust-input-color) stage to apply tonemapping, and the [Reproject & Accumulate](#project-and-accumulate) stage for reversing local tonemapping ahead of producing an ouput from FSR2.
### Resource inputs
The following table contains all resources consumed by the [Compute luminance pyramid](#compute-luminance-pyramid) stage.
> The temporal layer indicates which frame the data should be sourced from. 'Current frame' means that the data should be sourced from resources created for the frame that is to be presented next. 'Previous frame' indicates that the data should be sourced from resources which were created for the frame that has just presented. The resolution column indicates if the data should be at 'rendered' resolution or 'presentation' resolution. 'Rendered' resolution indicates that the resource should match the resolution at which the application is performing its rendering. Conversely, 'presentation' indicates that the resolution of the target should match that which is to be presented to the user.
| Name | Temporal layer | Resolution | Format | Type | Notes |
| ----------------|-----------------|--------------|-------------------------|-----------|----------------------------------------------|
| Color buffer | Current frame | Render | `APPLICATION SPECIFIED` | Texture | The render resolution color buffer for the current frame provided by the application. If the contents of the color buffer are in high dynamic range (HDR), then the [`FFX_FSR2_ENABLE_HIGH_DYNAMIC_RANGE`](../src/ffx-fsr2-api/ffx_fsr2.h#L87) flag should be set in the [`flags`](../src/ffx-fsr2-api/ffx_fsr2.h#L103) field of the [`FfxFsr2ContextDescription`](../src/ffx-fsr2-api/ffx_fsr2.h#L101) structure. |
### Resource outputs
The following table contains all resources produced or modified by the [Compute luminance pyramid](#compute-luminance-pyramid) stage.
> The temporal layer indicates which frame the data should be sourced from. 'Current frame' means that the data should be sourced from resources created for the frame that is to be presented next. 'Previous frame' indicates that the data should be sourced from resources which were created for the frame that has just presented. The resolution column indicates if the data should be at 'rendered' resolution or 'presentation' resolution. 'Rendered' resolution indicates that the resource should match the resolution at which the application is performing its rendering. Conversely, 'presentation' indicates that the resolution of the target should match that which is to be presented to the user.
| Name | Temporal layer | Resolution | Format | Type | Notes |
| ----------------------------|-----------------|------------------|-------------------------|-----------|----------------------------------------------|
| Exposure | Current frame | 1x1 | `R32_FLOAT` | Texture | A 1x1 texture containing the exposure value computed for the current frame. This resource is optional, and may be omitted if the [`FFX_FSR2_ENABLE_AUTO_EXPOSURE`](../src/ffx-fsr2-api/ffx_fsr2.h#L92) flag is set in the [`flags`](../src/ffx-fsr2-api/ffx_fsr2.h#L103) field of the [`FfxFsr2ContextDescription`](../src/ffx-fsr2-api/ffx_fsr2.h#L101) structure when creating the [`FfxFsr2Context`](../src/ffx-fsr2-api/ffx_fsr2.h#L164). |
| Current luminance | Current frame | `Render * 0.5` | `R16_FLOAT` | Texture | A texture at 50% of render resolution texture which contains the luminance of the current frame. |
### Description
The [Compute luminance pyramid](#compute-luminance-pyramid) stage is implemented using FidelityFX [Single Pass Downsampler](single-pass-downsampler.md), an optimized technique for producing mipmap chains using a single compute shader dispatch. Instead of the conventional (full) pyramidal approach, SPD provides a mechanism to produce a specific set of mipmap levels for an arbitrary input texture, as well as performing arbitrary calculations on that data as we store it to the target location in memory. In FSR2, we are interested in producing in upto two intermediate resources depending on the configuration of the [`FfxFsr2Context`](../src/ffx-fsr2-api/ffx_fsr2.h#L164). The first resource is a low-resolution representation of the current luminance, this is used later in FSR2 to attempt to detect shading changes. The second is the exposure value, and while it is always computed, it is only used by subsequent stages if the [`FFX_FSR2_ENABLE_AUTO_EXPOSURE`](../src/ffx-fsr2-api/ffx_fsr2.h#L92) flag is set in the [`flags`](../src/ffx-fsr2-api/ffx_fsr2.h#L103) field of the [`FfxFsr2ContextDescription`](../src/ffx-fsr2-api/ffx_fsr2.h#L101) structure upon context creation. The exposure value - either from the application, or the [Compute luminance pyramid](#compute-luminance-pyramid) stage - is used in the [Adjust input color](#adjust-input-color) stage of FSR2, as well as by the [Reproject & Accumulate](#project-and-accumulate) stage.
![alt text](docs/media/super-resolution-temporal/auto-exposure.svg "A diagram showing the mipmap levels written by auto-exposure.")
As used by FSR2, SPD is configured to write only to the 2nd (half resolution) and last (1x1) mipmap level. Moreover, different calculations are applied at each of these levels to calculate the quantities required by subsequent stages of the FSR2 algorithm. This means the rest of the mipmap chain is not required to be backed by GPU local memory (or indeed any type of memory).
The 2nd mipmap level contains current luminance, the value of which is computed during the downsampling of the color buffer using the following HLSL:
``` HLSL
float3 rgb = LoadInputColor(tex);
float3 rgb2y = float3(0.2126, 0.7152, 0.0722);
float logLuma = log(max(FSR2_EPSILON, dot(rgb2y, rgb)));
```
The last mipmap level is computed using the following HLSL:
``` HLSL
float ComputeAutoExposureFromAverageLog(float averageLogLuminance)
{
const float averageLuminance = exp(averageLogLuminance);
const float S = 100.0f; // ISO arithmetic speed
const float K = 12.5f;
const float exposureIso100 = log2((averageLuminance * S) / K);
const float q = 0.65f;
const float luminanceMax = (78.0f / (q * S)) * pow(2.0f, exposureIso100);
return 1 / luminanceMax;
}
```
## Adjust input color
There are several types of adjustments which FSR2 performs on the input colors, these are as follows:
1. The input color is divided by the pre-exposure value.
2. The input color is multiplied by the exposure value.
3. The exposed color is then converted to the YCoCg color space [[**YCoCg**](#references)].
Please note that manipulations to the color values provided by the application are strictly internal to FSR2, meaning that the results produced by FSR2 are always converted by into the requested color space (typically linear).
### Resource inputs
The following table contains all resources consumed by the [Adjust input color](#Adjust-input-color) stage.
> The temporal layer indicates which frame the data should be sourced from. 'Current frame' means that the data should be sourced from resources created for the frame that is to be presented next. 'Previous frame' indicates that the data should be sourced from resources which were created for the frame that has just presented. The resolution column indicates if the data should be at 'rendered' resolution or 'presentation' resolution. 'Rendered' resolution indicates that the resource should match the resolution at which the application is performing its rendering. Conversely, 'presentation' indicates that the resolution of the target should match that which is to be presented to the user.
| Name | Temporal layer | Resolution | Format | Type | Notes |
| ----------------|-----------------|--------------|---------------------------|-----------|----------------------------------------------|
| Color buffer | Current frame | Render | `APPLICATION SPECIFIED` | Texture | The render resolution color buffer for the current frame provided by the application. If the contents of the color buffer are in high dynamic range (HDR), then the [`FFX_FSR2_ENABLE_HIGH_DYNAMIC_RANGE`](../src/ffx-fsr2-api/ffx_fsr2.h#L87) flag should be set in the [`flags`](../src/ffx-fsr2-api/ffx_fsr2.h#L103) field of the [`FfxFsr2ContextDescription`](../src/ffx-fsr2-api/ffx_fsr2.h#L101) structure. |
| Exposure | Current frame | 1x1 | ``R32_FLOAT`` | Texture | A 1x1 texture containing the exposure value computed for the current frame. This resource can be supplied by the application, or computed by the [Compute luminance pyramid](#compute-luminance-pyramid) stage of FSR2 if the [`FFX_FSR2_ENABLE_AUTO_EXPOSURE`](../src/ffx-fsr2-api/ffx_fsr2.h#L92) flag is set in the [`flags`](../src/ffx-fsr2-api/ffx_fsr2.h#L103) field of the [`FfxFsr2ContextDescription`](../src/ffx-fsr2-api/ffx_fsr2.h#L101) structure. |
### Resource outputs
The following table contains all resources produced or modified by the [Adjust input color](#Adjust-input-color) stage.
> The temporal layer indicates which frame the data should be sourced from. 'Current frame' means that the data should be sourced from resources created for the frame that is to be presented next. 'Previous frame' indicates that the data should be sourced from resources which were created for the frame that has just presented. The resolution column indicates if the data should be at 'rendered' resolution or 'presentation' resolution. 'Rendered' resolution indicates that the resource should match the resolution at which the application is performing its rendering. Conversely, 'presentation' indicates that the resolution of the target should match that which is to be presented to the user.
| Name | Temporal layer | Resolution | Format | Type | Notes |
| ----------------------------|-----------------|--------------|-------------------------|-----------|----------------------------------------------|
| Adjusted color buffer | Current frame | Render | `R16G16B16A16_FLOAT` | Texture | A texture containing the adjusted version of the application's color buffer. The tonemapping operator may not be the same as any tonemapping operator included in the application, and is instead a local, reversible operator used throughout FSR2. This buffer is stored in YCoCg format. |
| Luminance history | Many frames | Render | `R8G8B8A8_UNORM` | Texture | A texture containing three frames of luminance history, as well as a stability factor encoded in the alpha channel. |
| Previous depth buffer | Current frame | Render | `R32_UNORM` | Texture | A texture containing a reconstructed and dilated depth values. This surface is cleared by the [Adjust input color](#adjust-input-color) stage. Please note: When viewing this texture in a capture tool (such as [RenderDoc](https://renderdoc.org)) it may not display correctly. This is because the format of this texture is ``R32_UNORM`` and contains IEEE754 floating point values, which have been written after performing a bitcast using the [`asuint`](https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-asuint) intrinsic function. See the note in [Adjust input color](#adjust-input-color) for more details on the specifics of how this works. |
### Description
The majority of the FSR2 algorithm operates in YCoCg color space. In order to avoid repeatedly calculating conversions from the color space used by the application, FSR2 implements a dedicated stage which applies all adjustments to the color once, with the results then being cached to an adjusted color texture which other passes may then read. As part of the adjustment process, FSR2 also calculates a luminance history buffer.
As the luminance buffer is persistent (it is not available for aliasing, or cleared each frame), we have access to four frames of history during the [Adjust input color](#Adjust-input-color) stage on any one frame. However, at the end of the [Adjust input color](#Adjust-input-color) stage, the luminance history values are shifted down, meaning that subsequent stages of FSR2 have access to the three most recent frames of luminance (the current frame, and the two frames before it). Therefore, if we denote the current frame as n, then the values stored in the luminance history buffer are as follows.
| Channel | Frame index (Start of adjust input color stage) | Frame index (End of adjust input color stage) |
|---------|-------------------------------------------------|-----------------------------------------------|
| Red | n-1 | n |
| Green | n-2 | n - 1 |
| Blue | n-3 | n - 2 |
The alpha channel of the luminance history buffer contains a measure of the stability of the luminance over the currrent frame, and the three frames that came before it. This is computed in the following way:
``` HLSL
float stabilityValue = 1.0f;
for (int i = 0; i < 3; i++) {
stabilityValue = min(stabilityValue, MinDividedByMax(currentFrameLuma, currentFrameLumaHistory[i]));
}
```
In additional to its color adjustment responsibilities already outlined, this stage also has the responsibility for clearing the reprojected depth buffer to a known value, ready for the [Reconstruct & dilate](#reconstruct-and-dilate) stage on the next frame of the application. The buffer must be cleared, as [Reconstruct & dilate](#reconstruct-and-dilate) will populate it using atomic operations. Depending on the configuration of the depth buffer, an appropriate clearing value is selected.
The format of the previous depth buffer is `R32_UINT` which allows the use of [`InterlockedMax`](https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/interlockedmax) and [`InterlockedMin`](https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/interlockedmin) operations to be performed from the [Reconstruct & dilate](#reconstruct-and-dilate) stage of FSR2. This is done with the resulting integer values returned by converting depth values using the [`asint`](https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-asint) functions. This works because depth values are always greater than 0, meaning that the monotonicity of IEEE754 floating point values when interpreted as integers is guaranteed.
## Reconstruct and dilate
The reconstruct & dilate stage consumes the applications depth buffer and motion vectors, and produces a reconstructed and dilated depth buffer for the previous frame, together with a dilated set of motion vectors in UV space. The stage runs at render resolution.
![alt text](docs/media/super-resolution-temporal/vector-dilation.svg "A diagram showing how a motion vector is dilated based on the depth value.")
### Resource inputs
The following table contains all of the resources which are required by the reconstruct & dilate stage.
> The temporal layer indicates which frame the data should be sourced from. 'Current frame' means that the data should be sourced from resources created for the frame that is to be presented next. 'Previous frame' indicates that the data should be sourced from resources which were created for the frame that has just presented. The resolution column indicates if the data should be at 'rendered' resolution or 'presentation' resolution. 'Rendered' resolution indicates that the resource should match the resolution at which the application is performing its rendering. Conversely, 'presentation' indicates that the resolution of the target should match that which is to be presented to the user.
| Name | Temporal layer | Resolution | Format | Type | Notes |
| ----------------------------|-----------------|------------|------------------------------------|-----------|------------------------------------------------|
| Depth buffer | Current frame | Render | `APPLICATION SPECIFIED (1x FLOAT)` | Texture | The render resolution depth buffer for the current frame provided by the application. The data should be provided as a single floating point value, the precision of which is under the application's control. The configuration of the depth should be communicated to FSR2 via the [`flags`](../src/ffx-fsr2-api/ffx_fsr2.h#L103) field of the [`FfxFsr2ContextDescription`](../src/ffx-fsr2-api/ffx_fsr2.h#L101) structure when creating the [`FfxFsr2Context`](../src/ffx-fsr2-api/ffx_fsr2.h#L164). You should set the [`FFX_FSR2_ENABLE_DEPTH_INVERTED`](../src/ffx-fsr2-api/ffx_fsr2.h#L90) flag if your depth buffer is inverted (that is [1..0] range), and you should set the flag if your depth buffer has as infinite far plane. If the application provides the depth buffer in `D32S8` format, then FSR2 will ignore the stencil component of the buffer, and create an `R32_FLOAT` resource to address the depth buffer. On GCN and RDNA hardware, depth buffers are stored separately from stencil buffers. |
| Motion vectors | Current fraame | Render or presentation | `APPLICATION SPECIFIED (2x FLOAT)` | Texture | The 2D motion vectors for the current frame provided by the application in [*(<-width, -height>*..*<width, height>*] range. If your application renders motion vectors with a different range, you may use the [`motionVectorScale`](../src/ffx-fsr2-api/ffx_fsr2.h#L125) field of the [`FfxFsr2DispatchDescription`](../src/ffx-fsr2-api/ffx_fsr2.h#L114) structure to adjust them to match the expected range for FSR2. Internally, FSR2 uses 16bit quantities to represent motion vectors in many cases, which means that while motion vectors with greater precision can be provided, FSR2 will not benefit from the increased precision. The resolution of the motion vector buffer should be equal to the render resolution, unless the [`FFX_FSR2_ENABLE_DISPLAY_RESOLUTION_MOTION_VECTORS`](../src/ffx-fsr2-api/ffx_fsr2.h#L88) flag is set in the [`flags`](../src/ffx-fsr2-api/ffx_fsr2.h#L103) field of the [`FfxFsr2ContextDescription`](../src/ffx-fsr2-api/ffx_fsr2.h#L101) structure when creating the [`FfxFsr2Context`](../src/ffx-fsr2-api/ffx_fsr2.h#L164), in which case it should be equal to the presentation resolution. |
### Resource outputs
The following table contains all of the resources which are produced by the reconstruct & dilate stage.
> The temporal layer indicates which frame the data should be sourced from. 'Current frame' means that the data should be sourced from resources created for the frame that is to be presented next. 'Previous frame' indicates that the data should be sourced from resources which were created for the frame that has just presented. The resolution column indicates if the data should be at 'rendered' resolution or 'presentation' resolution. 'Rendered' resolution indicates that the resource should match the resolution at which the application is performing its rendering. Conversely, 'presentation' indicates that the resolution of the target should match that which is to be presented to the user.
| Name | Temporal layer | Resolution | Format | Type | Notes |
| ------------------------------------|-----------------|------------|------------------------|-----------|------------------------------------------------|
| Previous depth buffer | Current frame | Render | `R32_UNORM` | Texture | A texture containing the reconstructed previous frame depth values. This surface should first be cleared, see the [Adjust input color](#adjust-input-color) stage for details. Please note: When viewing this texture in a capture tool (such as [RenderDoc](https://renderdoc.org)) it may not display correctly. This is because the format of this texture is ``R32_UNORM`` and contains IEEE754 floating point values, which have been written after performing a bitcast using the ``asuint`` intrinsic function. See the note in [Reproject & accumulate](#reproject-accumulate) for more details on the specifics of how this works. |
| Dilated depth | Current frame | Render | `R16_UINT` | Texture | A texture containing dilated depth values computed from the application's depth buffer. |
| Dilated motion vectors | Current frame | Render | `R16G16_FLOAT` | Texture | A texture containing dilated 2D motion vectors computed from the application's 2D motion vector buffer. The red and green channel contains the two-dimensional motion vectors in NDC space. |
### Description
The first step of the [Reconstruct & dilate](#reconstruct-and-dilate) stage is to compute the dilated depth values and motion vectors from the application's depth values and motion vectors for the current frame. Dilated depth values and motion vectors emphasise the edges of geometry which has been rendered into the depth buffer. This is because the edges of geometry will often introduce discontinuities into a contiguous series of depth values, meaning that as depth values and motion vectors are dilated, they will naturally follow the contours of the geometric edges present in the depth buffer. In order to compute the dilated depth values and motion vectors, FSR2 looks at the depth values for a 3x3 neighbourhood for each pixel and then selects the depth values and motion vectors in that neighbourhood where the depth value is nearest to the camera. In the diagram below, you can see how the central pixel of the 3x3 kernel is updated with the depth value and motion vectors from the pixel with the largest depth value - the pixel on the central, right hand side.
As this stage is the first time that motion vectors are consumed by FSR2, this is where motion vector scaling is applied if using the FSR2 host API. Motion vector scaling factors provided via the [`motionVectorScale`](../src/ffx-fsr2-api/ffx_fsr2.h#L125) field of the [`FfxFsr2DispatchDescription`](../src/ffx-fsr2-api/ffx_fsr2.h#L114) structure and allows you to transform non-screenspace motion vectors into screenspace motion vectors which FSR2 expects.
``` CPP
// An example of how to manipulate motion vector scaling factors using the FSR2 host API.
FfxFsr2DispatchParameters dispatchParams = { 0 };
dispatchParams.motionVectorScale.x = renderWidth;
dispatchParams.motionVectorScale.y = renderHeight;
```
With the dilated motion vectors, we can now move to the second part of the [Reconstruct & dilate](#reconstruct-and-dilate) stage, which is to estimate the position of each pixel in the current frame's depth buffer in the previous frame. This is done by applying the dilated motion vector computed for a pixel, to its depth buffer value. As it is possible for many pixels to reproject into the same pixel in the previous depth buffer, atomic operations are used in order to resolve the value of the nearest depth value for each pixel. This is done using the [`InterlockedMax`](https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/interlockedmax) or [`InterlockedMin`](https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/interlockedmin) operation (the choice depending on if the application's depth buffer is inverted or not). The use of cumulative operations to resolve the contents of the previous depth buffer implies that the reconstructed depth buffer resource must always be cleared to a known value, which is performed in the [Reproject & accumulate](#reproject-accumulate) stage. This is performed on frame N for frame N + 1.
![alt text](docs/media/super-resolution-temporal/reconstruct-previous-depth.svg "A diagram showing a dilated motion vector being applied to a depth value.")
When using the FSR2 API, the application's depth buffer and the application's velocity buffer must be specified as separate resources as per the [Resource inputs](#resource-inputs) table above. However, if you are undertaking a bespoke integration into your application, this constraint may be relaxed. Take care that the performance characteristics of this pass do not change if moving to a format for the motion vector texture which is more sparse, e.g.: as part of a packed g-buffer in a deferred renderer.
## Depth clip
The goal of the [Depth clip](#depth-clip) stage is to produce a mask which indicates disoccluded areas of the current frame.
This stage runs at render resolution.
### Resource inputs
The following table contains all the resources which are consumed by the [Depth clip](#depth-clip) stage.
> The temporal layer indicates which frame the data should be sourced from. 'Current frame' means that the data should be sourced from resources created for the frame that is to be presented next. 'Previous frame' indicates that the data should be sourced from resources which were created for the frame that has just presented. The resolution column indicates if the data should be at 'rendered' resolution or 'presentation' resolution. 'Rendered' resolution indicates that the resource should match the resolution at which the application is performing its rendering. Conversely, 'presentation' indicates that the resolution of the target should match that which is to be presented to the user.
| Name | Temporal layer | Resolution | Format | Type | Notes |
| ------------------------------------|-----------------|------------|------------------------|-----------|------------------------------------------------|
| Previous depth buffer | Current frame | Render | `R32_UNORM` | Texture | A texture containing the reconstructed previous frame depth values. This surface should first be cleared, see the [Reproject & accumulate](#reproject-accumulate) stage for details. Please note: When viewing this texture in a capture tool (such as [RenderDoc](https://renderdoc.org)) it may not display correctly. This is because the format of this texture is ``R32_UINT`` and contains IEEE754 floating point values, which have been written after performing a bitcast using the ``asuint`` intrinsic function. See the note in [Reproject & accumulate](#reproject-accumulate) for more details on the specifics of how this works. |
| Dilated depth | Current frame | Render | `R16_UINT` | Texture | A texture containing dilated depth values computed from the application's depth buffer. |
| Dilated motion vectors | Current frame | Render | `R16G16_FLOAT` | Texture | A texture containing dilated 2D motion vectors computed from the application's 2D motion vector buffer. The red and green channel contains the two-dimensional motion vectors in NDC space, and the alpha channel contains the depth value used by the [Depth clip](#depth-clip) stage. |
### Resource outputs
The following table contains all the resources which are produced by the [Depth clip](#depth-clip) stage.
> The temporal layer indicates which frame the data should be sourced from. 'Current frame' means that the data should be sourced from resources created for the frame that is to be presented next. 'Previous frame' indicates that the data should be sourced from resources which were created for the frame that has just presented. The resolution column indicates if the data should be at 'rendered' resolution or 'presentation' resolution. 'Rendered' resolution indicates that the resource should match the resolution at which the application is performing its rendering. Conversely, 'presentation' indicates that the resolution of the target should match that which is to be presented to the user.
| Name | Temporal layer | Resolution | Format | Type | Notes |
| ------------------------------------|-----------------|------------|------------------------|-----------|----------------------------------------|
| Disocclusion mask | Current frame | Render | `R8_FLOAT` | Texture | A texture containing a value indicating how much the pixel has been disoccluded. A value of 0 means that the pixel was entirely occluded in the previous frame, and values greater than zero mean that the pixel was visible to an extent proportional to the value. Therefore when examining the mask in a graphics debugging tool, the darker areas in the disocclusion mask indicate areas which are more disoccluded. |
### Description
To generate the disocclusion mask, the depth value must be computed for each pixel from the previous camera's position and the new camera's position. In the diagram below, you can see a camera moving from an initial position (labelled P0) to a new position (labelled P1). As it does so, the shaded area behind the sphere becomes disoccluded - that is it becomes visible from the camera at P1 and was previously occluded from the point of view of P0.
![alt text](docs/media/super-resolution-temporal/disocclusion.svg "A diagram showing a disoccluded area as a camera moves from position 0 to position 1.")
With both values depth values, we can compare the delta between them against the Akeley separation value [[Akeley-06](#references)]. Intuitively, the Akeley separation constant provides a minimum distance between two objects represented in a floating point depth buffer which allow you to say - with a high degree of certainty - that the objects were originally distinct from one another. In the diagram below you can see that the mid-grey and dark-grey objects have a delta which is larger than the kSep value which has been computed for the application's depth buffer configuration. However, the distance from the light-gray object to the mid-grey object does not exceed the computed kSep value, and therefore we are unable to conclude if this object is distinct.
![alt text](docs/media/super-resolution-temporal/k-sep.svg "A diagram showing the concept behind the constant of separation.")
The value stored in the disocclusion mask is in the range [0..1], where 1 maps to a value greater than or equal to the Akeley separation value.
## Create locks
This stage is responsible for creating new locks on pixels which are consumed in the [Reproject & Accumulate](#reproject-accumulate) stage. This stage runs at render resolution.
### Resource inputs
The following table contains all resources consumed by the [Create locks](#create-locks) stage.
> The temporal layer indicates which frame the data should be sourced from. 'Current frame' means that the data should be sourced from resources created for the frame that is to be presented next. 'Previous frame' indicates that the data should be sourced from resources which were created for the frame that has just presented. The resolution column indicates if the data should be at 'rendered' resolution or 'presentation' resolution. 'Rendered' resolution indicates that the resource should match the resolution at which the application is performing its rendering. Conversely, 'presentation' indicates that the resolution of the target should match that which is to be presented to the user.
| Name | Temporal layer | Resolution | Format | Type | Notes |
| ----------------------------|-----------------|--------------|-------------------------|-----------|----------------------------------------------|
| Adjusted color buffer | Current frame | Render | `R16G16B16A16_FLOAT` | Texture | A texture containing the adjusted version of the application's color buffer. The tonemapping operator may not be the same as any tonemapping operator included in the application, and is instead a local, reversible operator used throughout FSR2. This buffer is stored in YCoCg format. |
| Lock status | Current frame | Presentation | `R16G16_FLOAT` | Texture | A mask which indicates whether or not to perform color rectification on a pixel, can be thought of as a lock on the pixel to stop rectification from removing the detail. Please note: This texture is part of an array of two textures along with the Lock status texture which is used as an input to this stage. The selection of which texture in the array is used for input and output is swapped each frame. The red channel contains the time remaining on the pixel lock, and the Y channel contains the luminance of the pixel at the time when the lock was created. |
### Resource outputs
The following table contains all resources produced or modified by the [Create locks](#create-locks) stage.
> The temporal layer indicates which frame the data should be sourced from. 'Current frame' means that the data should be sourced from resources created for the frame that is to be presented next. 'Previous frame' indicates that the data should be sourced from resources which were created for the frame that has just presented. The resolution column indicates if the data should be at 'rendered' resolution or 'presentation' resolution. 'Rendered' resolution indicates that the resource should match the resolution at which the application is performing its rendering. Conversely, 'presentation' indicates that the resolution of the target should match that which is to be presented to the user.
| Name | Temporal layer | Resolution | Format | Type | Notes |
| ----------------------------|-----------------|--------------|-------------------------|-----------|----------------------------------------------|
| Lock status | Current frame | Presentation | `R16G16_FLOAT` | Texture | A mask which indicates whether or not to perform color rectification on a pixel, can be thought of as a lock on the pixel to stop rectification from removing the detail. Please note: This texture is part of an array of two textures along with the Lock status texture which is used as an input to this stage. The selection of which texture in the array is used for input and output is swapped each frame. The red channel contains the time remaining on the pixel lock, and the Y channel contains the luminance of the pixel at the time when the lock was created. The [Create locks](#create-locks) stage updates only a subset of this resource. |
### Description
Intuitively, a pixel lock is a mechanism to stop color rectification from being applied to a pixel. The net effect of this locking is that more of the previous frame's color data is used when computing the final, super resolution pixel color in the [Reproject & accumulate](#reproject-accumulate) stage. The lock status texture contains two values which together compose a pixel lock. The red channel of the lock status texture contains the remaining lifetime of a pixel lock. This value is decremented by the initial lock length divided by the total length of the jitter sequence. When a lock reaches zero, it is considered to be expired. The green channel of the lock status texture contains the luminance of the pixel at the time the lock was created, but it is only populated during the reprojection stage of [Reproject & accumulate](#reproject-accumulate) stage. The luminance value is ultimately used in the [Reproject & Accumulate](#reproject-accumulate) stage as part of the shading change detection, this allows FSR2 to unlock a pixel if there is discontinuous change to the pixel's appearance (e.g.: an abrupt change to the shading of the pixel).
When creating locks, the 3x3 neighbourhood of luminance values is compared against a threshold. The result of this comparison determines if a new lock should be created. The use of the neighbourhood allows us to detect thin features in the input image which should be locked in order to preserve details in the final super resolution image; such as wires, or chain linked fences.
## Reproject & accumulate
This stage undertakes the following steps:
1. The current frame's color buffer is upsampled using Lanczos filtering.
2. The previous frame's output color and lock status buffers are reprojected, as if they were viewed from the current camera's perspective.
3. Various cleanup steps to the historical color data.
4. The historical color data, and the upscaled color data from the current frame are accumulated.
5. The output is (optionally) tonemapped ready for RCAS sharpening.
This stage runs at presentation resolution.
### Resource inputs
The following table contain all resources required by the [Reproject & accumulate](#reproject-accumulate) stage.
> The temporal layer indicates which frame the data should be sourced from. 'Current frame' means that the data should be sourced from resources created for the frame that is to be presented next. 'Previous frame' indicates that the data should be sourced from resources which were created for the frame that has just presented. The resolution column indicates if the data should be at 'rendered' resolution or 'presentation' resolution. 'Rendered' resolution indicates that the resource should match the resolution at which the application is performing its rendering. Conversely, 'presentation' indicates that the resolution of the target should match that which is to be presented to the user.
| Name | Temporal layer | Resolution | Format | Type | Notes |
| ------------------------------------|-----------------|--------------|------------------------|-----------|----------------------------------------|
| Disocclusion mask | Current frame | Render | `R8_UNORM` | Texture | A texture containing a value indicating how much the pixel has been disoccluded. |
| Dilated motion vectors | Current frame | Render | `R16G16_FLOAT` | Texture | A texture containing dilated motion vectors computed from the application's velocity buffer. The red and green channel contains the two-dimensional motion vectors in UV space. |
| Reactive mask | Current frame | Render | `R8_UNORM` | Texture | As some areas of a rendered image do not leave a footprint in the depth buffer or include motion vectors, FSR2 provides support for a reactive mask texture which can be used to indicate to FSR2 where such areas are. Good examples of these are particles, or alpha-blended objects which do not write depth or motion vectors. If this resource is not set, then FSR2's shading change detection logic will handle these cases as best it can, but for optimal results, this resource should be set. For more information on the reactive mask please refer to the [Reactive mask](#reactive-mask) section. |
| Output buffer | Previous frame | Presentation | ``R16G16B16A16_FLOAT`` | Texture | The output buffer produced by the FSR2 algorithm running in the previous frame. Please note: This buffer is used internally by FSR2, and is distinct from the presentation buffer which is derived from the output buffer, and has [RCAS](#robust-contrast-adpative-sharpening-rcas) applied. Please note: This texture is part of an array of two textures along with the Output buffer texture which is produced by the [Reproject & accumulate](#reproject-accumulate) stage. The selection of which texture in the array is used for input and output is swapped each frame. |
| Current luminance | Current frame | `Render * 0.5` | `R16_FLOAT` | Texture | A texture at 50% of render resolution texture which contains the luminance of the current frame. |
| Luminance history | Many frames | Render | `R8G8B8A8_UNORM` | Texture | A texture containing three frames of luminance history, as well as a stability factor encoded in the alpha channel. |
| Adjusted color buffer | Current frame | Render | `R16G16B16A16_FLOAT` | Texture | A texture containing the adjusted version of the application's color buffer. The tonemapping operator may not be the same as any tonemapping operator included in the application, and is instead a local, reversible operator used throughout FSR2. This buffer is stored in YCoCg format. |
| Lock status | Previous frame | Presentation | `R16G16_FLOAT` | Texture | A mask which indicates not to perform color clipping on a pixel, can be thought of as a lock on the pixel to stop clipping removing the detail. For a more detailed description of the pixel locking mechanism please refer to the [Create locks](#create-locks) stage. Please note: This texture is part of an array of two textures along with the Lock status texture which is used as an output from this stage. The selection of which texture in the array is used for input and output is swapped each frame. |
### Resource outputs
This table contains the resources produced by the [Reproject & accumulate](#reproject-accumulate) stage.
> The temporal layer indicates which frame the data should be sourced from. 'Current frame' means that the data should be sourced from resources created for the frame that is to be presented next. 'Previous frame' indicates that the data should be sourced from resources which were created for the frame that has just presented. The resolution column indicates if the data should be at 'rendered' resolution or 'presentation' resolution. 'Rendered' resolution indicates that the resource should match the resolution at which the application is performing its rendering. Conversely, 'presentation' indicates that the resolution of the target should match that which is to be presented to the user.
| Name | Temporal layer | Resolution | Format | Type | Notes |
| ----------------------------|-----------------|--------------|-------------------------|-----------|----------------------------------------------|
| Output buffer | Current frame | Presentation | `R16G16B16A16_FLOAT` | Texture | The output buffer produced by the [Reproject & accumulate](#reproject-accumulate) stage for the current frame. Please note: This buffer is used internally by FSR2, and is distinct from the presentation buffer which is produced as an output from this stage after applying RCAS. Please note: This texture is part of an array of two textures along with the Output buffer texture which is consumed by the [Reproject & accumulate](#reproject-accumulate) stage. The selection of which texture in the array is used for input and output is swapped each frame. |
| Reprojected locks | Current frame | Render | `R16G16_FLOAT` | Texture | The reprojected lock status texture. |
### Description
The reproject & accumulate stage of FSR2 is the most complicated and expensive stage in the algorithm. It brings together the results from many of the previous algorithmic steps and accumulates the reprojected color data from the previous frame together with the upsampled color data from the current frame. Please note the description in this documentation is designed to give you an intuition for the steps involved in this stage and does not necessarily match the implementation precisely.
![alt text](docs/media/super-resolution-temporal/reproject-and-accumulate-structure.svg "A diagram showing all phases in the rerpoject & accumulate portion of the FSR2 algorithm.")
The first step of the [Reproject & accumulate](#reproject-accumulate) stage is to assess each pixel for changes in its shading. If we are in a locked area, the luminance at the time the lock was created is compared to FSR2's shading change threshold. In a non-locked area, both the current frame and historical luminance values are used to make this determination. Shading change determination is a key part of FSR2's [Reproject & accumulate](#reproject-accumulate) stage, and feeds into many of the other parts of this stage.
![alt text](docs/media/super-resolution-temporal/upsample-with-lanczos.svg "A diagram showing upsampling of the current frame's input using Lanczos.")
Next we must upsample the adjusted color. To perform upsampling, the adjusted color's pixel position serves as the center of a 5x5 Lanczos resampling kernel [[Lanczos]](#references). In the diagram above, you can see that the Lanczos functions are centered around the display resolution sample `S`. The point in each pixel - labelled `P` - denotes the render resolution jittered sample position for which we calculate the Lanczos weights. Looking above and to the right of the 5x5 pixel neighbourhood, you can see the `Lanczos(x, 2)` resampling kernel being applied to the render resolution samples in the 5x5 grid of pixels surrounding the pixel position. It is worth noting that while conceptually the neighbourhood is 5x5, in the implementation only a 4x4 is actually sampled, due to the zero weighted contributions of those pixels on the periphery of the neighbourhood. The implementation of the Lanczos kernel may vary by GPU product. On RDNA2-based products, we use a look-up-table (LUT) to encode the `sinc(x)` function. This helps to produce a more harmonious balance between ALU and memory in the [Reproject & accumulate](#reproject-accumulate) stage. As the upsample step has access to the 5x5 neighbourhood of pixels, it makes sense from an efficiency point of view to also calculate the YCoCg bounding box - which is used during color rectification - at this point. The diagram below shows a 2D YCo bounding box being constructed from a 3x3 neighbourhood around the current pixel, in reality the bounding box also has a third dimension for Cg.
![alt text](docs/media/super-resolution-temporal/calculate-bounding-box.svg "A diagram showing how a YCoCg bounding box is computed from the current frame's adjust color samples.")
Reprojection is another key part of the [Reproject & accumulate](#reproject-accumulate) stage. To perform reprojection, the dilated motion vectors produced by the [Reconstruct & dilate](#reconstruct-and-dilate) stage are sampled and then applied to the output buffer from the previous frame's execution of FSR2. The left of the diagram below shows two-dimensional motion vector **M** being applied to the current pixel position. On the right, you can see the `Lanczos(x, 2)` resampling kernel being applied to the 5x5 grid of pixels surrounding the translated pixel position. As with the upsampling step, the implementation of the Lanczos kernel may vary by GPU product. The result of the reprojection is a presentation resolution image which contains all the data from the previous frame that could be mapped into the current frame. However, it is not just the previous frame's output color that is reprojected. As FSR2 relies on a mechanism whereby each pixel may be locked to enhance its temporal stability, the locks must also be reprojected from the previous frame into the current frame. This is done in much the same way as the reprojection of the color data, but also combines the results of the shading change detection step we performed on the various luminance values, both current and historical.
![alt text](docs/media/super-resolution-temporal/reproject-mvs.svg "A diagram showing the 5x5 Lanczos sampling kernel applied to a pixel position determined by translating the current pixel position by the motion vectors.")
It is now time to update our locks. The first task for update locks is to look for locks which were created during this frame's [Create locks](#create-locks) stage that are not reprojected, and instead have the luminance value of the current frame written to the green channel of the reprojected locks texture. All that remains then is to discern which locks are trustworthy for the current frame and pass those on to the color rectification step. The truthworthiness determination is done by comparing the luminance values within a neighbourhood of pixels in the current luminance texture. If the luminance separation between these values is large, then we should not trust the lock.
With our lock updates applied and their trustworthiness determined, we can move on to color rectification which is the next crucial step of FSR2's [Reproject & accumulate](#reproject-accumulate) stage. During this stage, a final color is determined from the pixel's historical data which will then be blended with the current frame's upsampled color in order to form the final accumulated super-resolution color. The determination of the final historical color and its contribution is chiefly controlled by two things:
1. Reducing the influence of the historical samples for areas which are disoccluded. This is undertaken by modulating the color value by the disocclusion mask.
2. Reducing the influence of the historical samples (marked S<sub>h</sub> in the diagram below) are far from the current frame color's bounding box (computed during the upsampling phase of the [Reproject & accumulate](#reproject-accumulate) stage).
![alt text](docs/media/super-resolution-temporal/clamp-to-box.svg "A diagram showing a historical color sample being clamped to the YCoCg bounding box for the current frame.")
The final step of the [Reproject & accumulate](#reproject-accumulate) stage is to accumulate the current frame's upsampled color with the rectified historical color data. By default, FSR2 will typically blend the current frame with a relatively low linear interpolation factor - that is relatively little of the current frame will be included in the final output. However, this can be altered based on the contents of the application provided reactivity mask. See the [reactive mask](#reactive-mask) section for further details.
## Robust Contrast Adaptive Sharpening (RCAS)
Robust Contrast Adaptive Sharpening (RCAS) was originally introduced in FidelityFX Super Resolution 1.0 as an additional sharpening pass to help generate additional clarity and sharpeness in the final upscaled image. RCAS is a derivative of the popular Contrast Adaptive Sharpening (CAS) algorithm, but with some key differences which make it more suitable for upscaling. Whereas CAS uses a simplified mechanism to convert local contrast into a variable amount of sharpness, conversely RCAS uses a more exact mechanism, solving for the maximum local sharpness possible before clipping. Additionally, RCAS also has a built-in process to limit the sharpening of what it detects as possible noise. Support for some scaling (which was included in CAS) is not included in RCAS, therefore it should run at presentation resolution.
### Resource inputs
This table contains the resources consumed by the [Robust Contrast Adaptive Sharpening (RCAS)](#robust-contrast-adaptive-sharpening-rcas) stage.
> The temporal layer indicates which frame the data should be sourced from. 'Current frame' means that the data should be sourced from resources created for the frame that is to be presented next. 'Previous frame' indicates that the data should be sourced from resources which were created for the frame that has just presented. The resolution column indicates if the data should be at 'rendered' resolution or 'presentation' resolution. 'Rendered' resolution indicates that the resource should match the resolution at which the application is performing its rendering. Conversely, 'presentation' indicates that the resolution of the target should match that which is to be presented to the user.
| Name | Temporal layer | Resolution | Format | Type | Notes |
| ----------------------------|-----------------|--------------|-------------------------|-----------|----------------------------------------------|
| Output buffer | Previous frame | Presentation | `R16G16B16A16_FLOAT` | Texture | The output buffer produced by the [Reproject & Accumulate](#reproject-accumulate) stage for the current frame. Please note: This buffer is used internally by FSR2, and is distinct from the presentation buffer which is produced as an output from this stage after applying RCAS. Please note: This texture is part of an array of two textures along with the Output buffer texture which is consumed by the [Reproject & Accumulate](#reproject-accumulate) stage. The selection of which texture in the array is used for input and output is swapped each frame. |
### Resource outputs
> The temporal layer indicates which frame the data should be sourced from. 'Current frame' means that the data should be sourced from resources created for the frame that is to be presented next. 'Previous frame' indicates that the data should be sourced from resources which were created for the frame that has just presented. The resolution column indicates if the data should be at 'rendered' resolution or 'presentation' resolution. 'Rendered' resolution indicates that the resource should match the resolution at which the application is performing its rendering. Conversely, 'presentation' indicates that the resolution of the target should match that which is to be presented to the user.
| Name | Temporal layer | Resolution | Format | Type | Notes |
| -----------------------------|-----------------|--------------|-------------------------|-----------|----------------------------------------------|
| Presentation buffer | Previous frame | Presentation | Application specific | Texture | The presentation buffer produced by the completed FSR2 algorithm for the current frame. |
### Description
RCAS operates on data sampled using a 5-tap filter configured in a cross pattern. See the diagram below.
![alt text](docs/media/super-resolution-temporal/rcas-weights.svg "A diagram showing the weights RCAS applies to neighbourhood pixels.")
With the samples retreived, RCAS then chooses the 'w' which results in no clipping, limits 'w', and multiplies by the 'sharp' amount. The solution above has issues with MSAA input as the steps along the gradient cause edge detection issues. To help stabilize the results of RCAS, it uses 4x the maximum and 4x the minimum (depending on equation) in place of the individual taps, as well as switching from 'm' to either the minimum or maximum (depending on side), to help in energy conservation.
# Building the sample
## Prerequisites
To build the FSR2 sample, please follow the following instructions:
1) Install the following tools:
- [CMake 3.16](https://cmake.org/download/)
- Install the "Desktop Development with C++" workload
- [Visual Studio 2019](https://visualstudio.microsoft.com/downloads/)
- [Windows 10 SDK 10.0.18362.0](https://developer.microsoft.com/en-us/windows/downloads/windows-10-sdk)
- [Git 2.32.0](https://git-scm.com/downloads)
2) Generate the solutions:
```
> cd <installation path>\build
> GenerateSolutions.bat
```
3) Open the solutions in the DX12 or Vulkan directory (depending on your preference), compile and run.
# Version history
| Version | Date | Notes |
| ---------------|-------------------|--------------------------------------------------------------|
| **2.0.1** | 2022-06-22 | Initial release of FidelityFX Super Resolution 2.0. |
# References
[**Akeley-06**] Kurt Akeley and Jonathan Su, **"Minimum Triangle Separation for Correct Z-Buffer Occlusion"**,
[http://www.cs.cmu.edu/afs/cs/academic/class/15869-f11/www/readings/akeley06_triseparation.pdf](http://www.cs.cmu.edu/afs/cs/academic/class/15869-f11/www/readings/akeley06_triseparation.pdf)
[**Lanczos**] Lanczos resampling, **"Lanczos resampling"**, [https://en.wikipedia.org/wiki/Lanczos_resampling](https://en.wikipedia.org/wiki/Lanczos_resampling)
[**Halton**] Halton sequence, **"Halton sequence"**, [https://en.wikipedia.org/wiki/Halton_sequence](https://en.wikipedia.org/wiki/Halton_sequence)
[**YCoCg**] YCoCg Color Space, [https://en.wikipedia.org/wiki/YCoCg](https://en.wikipedia.org/wiki/YCoCg)

2
build/.gitignore vendored

@ -0,0 +1,2 @@
DX12/
VK/

@ -0,0 +1,58 @@
@echo off
setlocal enabledelayedexpansion
echo Checking pre-requisites...
:: Check if CMake is installed
cmake --version > nul 2>&1
if %errorlevel% NEQ 0 (
echo Cannot find path to cmake. Is CMake installed? Exiting...
exit /b -1
) else (
echo CMake - Ready.
)
:: Check if submodule is initialized (first time) to avoid CMake file not found errors
if not exist ..\libs\cauldron\common.cmake (
echo File: common.cmake doesn't exist in '.\libs\cauldron\' - Initializing submodule...
:: attempt to initialize submodule
cd ..
echo.
git submodule sync --recursive
git submodule update --init --recursive
cd build
:: check if submodule initialized properly
if not exist ..\libs\cauldron\common.cmake (
echo.
echo '..\libs\cauldron\common.cmake is still not there.'
echo Could not initialize submodule. Make sure all the submodules are initialized and updated.
echo Exiting...
echo.
exit /b -1
) else (
echo Cauldron - Ready.
)
) else (
echo Cauldron - Ready.
)
:: Check if VULKAN_SDK is installed but don't bail out
if "%VULKAN_SDK%"=="" (
echo Vulkan SDK is not installed -Environment variable VULKAN_SDK is not defined- : Please install the latest Vulkan SDK from LunarG.
) else (
echo Vulkan SDK - Ready : %VULKAN_SDK%
)
:: Call CMake
mkdir DX12
cd DX12
cmake -A x64 ..\.. -DGFX_API=DX12
cd ..
mkdir VK
cd VK
cmake -A x64 ..\.. -DGFX_API=VK
cd ..

@ -0,0 +1,58 @@
@echo off
setlocal enabledelayedexpansion
echo Checking pre-requisites...
:: Check if CMake is installed
cmake --version > nul 2>&1
if %errorlevel% NEQ 0 (
echo Cannot find path to cmake. Is CMake installed? Exiting...
exit /b -1
) else (
echo CMake - Ready.
)
:: Check if submodule is initialized (first time) to avoid CMake file not found errors
if not exist ..\libs\cauldron\common.cmake (
echo File: common.cmake doesn't exist in '.\libs\cauldron\' - Initializing submodule...
:: attempt to initialize submodule
cd ..
echo.
git submodule sync --recursive
git submodule update --init --recursive
cd build
:: check if submodule initialized properly
if not exist ..\libs\cauldron\common.cmake (
echo.
echo '..\libs\cauldron\common.cmake is still not there.'
echo Could not initialize submodule. Make sure all the submodules are initialized and updated.
echo Exiting...
echo.
exit /b -1
) else (
echo Cauldron - Ready.
)
) else (
echo Cauldron - Ready.
)
:: Check if VULKAN_SDK is installed but don't bail out
if "%VULKAN_SDK%"=="" (
echo Vulkan SDK is not installed -Environment variable VULKAN_SDK is not defined- : Please install the latest Vulkan SDK from LunarG.
) else (
echo Vulkan SDK - Ready : %VULKAN_SDK%
)
:: Call CMake
mkdir DX12
cd DX12
cmake -A x64 ..\.. -DGFX_API=DX12 -DFSR2_BUILD_AS_DLL=1
cd ..
mkdir VK
cd VK
cmake -A x64 ..\.. -DGFX_API=VK -DFSR2_BUILD_AS_DLL=1
cd ..

@ -0,0 +1,35 @@
# This file is part of the FidelityFX SDK.
#
# Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
Checks: 'clang-diagnostic-*,clang-analyzer-*,cppcoreguidelines-*,modernize-*,-modernize-use-trailing-return-type'
WarningsAsErrors: false
HeaderFilterRegex: ''
AnalyzeTemporaryDtors: false
FormatStyle: google
CheckOptions:
- { key: readability-identifier-naming.NamespaceCase, value: CamelCase }
- { key: readability-identifier-naming.ClassCase, value: CamelCase }
- { key: readability-identifier-naming.PrivateMemberPrefix, value: m_ }
- { key: readability-identifier-naming.StructCase, value: CamelCase }
- { key: readability-identifier-naming.FunctionCase, value: camelBase }
- { key: readability-identifier-naming.VariableCase, value: camelBase }
- { key: readability-identifier-naming.GlobalConstantCase, value: UPPER_CASE }

@ -0,0 +1,28 @@
#
# enables multithreading compilation
#
add_compile_options(/MP)
#
# includes cauldron's helper cmakes
#
include(${CMAKE_CURRENT_SOURCE_DIR}/../../libs/cauldron/common.cmake)
#
# Add manifest so the app uses the right DPI settings
#
function(addManifest PROJECT_NAME)
IF (MSVC)
IF (CMAKE_MAJOR_VERSION LESS 3)
MESSAGE(WARNING "CMake version 3.0 or newer is required use build variable TARGET_FILE")
ELSE()
ADD_CUSTOM_COMMAND(
TARGET ${PROJECT_NAME}
POST_BUILD
COMMAND "mt.exe" -manifest \"${CMAKE_CURRENT_SOURCE_DIR}\\dpiawarescaling.manifest\" -inputresource:\"$<TARGET_FILE:${PROJECT_NAME}>\"\;\#1 -outputresource:\"$<TARGET_FILE:${PROJECT_NAME}>\"\;\#1
COMMENT "Adding display aware manifest..."
)
ENDIF()
ENDIF(MSVC)
endfunction()

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 29 KiB

@ -0,0 +1,155 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="212.72499mm"
height="112.44791mm"
viewBox="0 0 212.72499 112.44791"
version="1.1"
id="svg157"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs154">
<clipPath
id="clip0">
<rect
x="291"
y="116"
width="804"
height="425"
id="rect161" />
</clipPath>
</defs>
<g
id="layer1"
transform="translate(94.795491,-94.827588)">
<g
clip-path="url(#clip0)"
transform="matrix(0.26458333,0,0,0.26458333,-171.78924,64.135922)"
id="g208">
<rect
x="584"
y="117"
width="221"
height="103"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#ffffff"
id="rect166" />
<text
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="20px"
transform="translate(648.646,175)"
id="text168">Application</text>
<rect
x="584"
y="271"
width="221"
height="103"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
id="rect170" />
<text
fill="#ffffff"
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="20px"
transform="translate(655.141,305)"
id="text176">FidelityFX <tspan
font-size="20px"
x="-44.619999"
y="24"
id="tspan172">Super Resolution 2.0</tspan><tspan
font-size="20px"
x="26"
y="48"
id="tspan174">API</tspan></text>
<path
d="m 698,220 v 36.266 h -6 V 220 Z m 6,33.266 -9,18 -9,-18 z"
id="path178" />
<rect
x="292"
y="437"
width="223"
height="103"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
id="rect180" />
<text
fill="#ffffff"
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="20px"
transform="translate(364.453,471)"
id="text186">FidelityFX <tspan
font-size="20px"
x="-29.629999"
y="24"
id="tspan182">Super Resolution</tspan><tspan
font-size="20px"
x="-42.5"
y="48"
id="tspan184">DirectX®12 Backend</tspan></text>
<rect
x="582"
y="437"
width="223"
height="103"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
id="rect188" />
<text
fill="#ffffff"
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="20px"
transform="translate(653.951,471)"
id="text194">FidelityFX <tspan
font-size="20px"
x="-29.629999"
y="24"
id="tspan190">Super Resolution</tspan><tspan
font-size="20px"
x="-30.129999"
y="48"
id="tspan192">Vulkan® Backend</tspan></text>
<rect
x="871"
y="437"
width="223"
height="103"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#ffffff"
id="rect196" />
<text
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="20px"
transform="translate(910.689,483)"
id="text200">Applications own <tspan
font-size="20px"
x="37.869999"
y="24"
id="tspan198">backend</tspan></text>
<path
d="m 697.693,374 v 34.293 H 404 l 3,-3 v 16.293 h -6 v -19.293 h 293.693 l -3,3 V 374 Z M 413,418.586 l -9,18 -9,-18 z"
id="path202" />
<path
d="m 0,-3 h 34.2932 v 291.293 l -3,-3 h 16.2929 v 6 H 28.2932 V 0 l 3,3 H 0 Z m 44.5861,282.293 18,9 -18,9 z"
transform="matrix(0,1,1,0,695,374)"
id="path204" />
<path
d="M 2.99945,-0.0575102 3.9119,47.5314 -2.087,47.6464 -2.99945,0.0575102 Z M 9.85328,44.4169 1.2,62.5861 -8.14341,44.762 Z"
transform="matrix(-1,0,0,1,694.2,374)"
id="path206" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.2 KiB

@ -0,0 +1,229 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="395.90704mm"
height="91.54583mm"
viewBox="0 0 395.90704 91.54583"
version="1.1"
id="svg5"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs2">
<clipPath
id="clip0">
<rect
x="270"
y="119"
width="1513"
height="358"
id="rect9" />
</clipPath>
</defs>
<g
id="layer1"
transform="translate(336.07723,-19.886877)">
<g
clip-path="url(#clip0)"
transform="matrix(0.26458333,0,0,0.26458333,-407.51473,-11.598539)"
id="g76">
<rect
x="271"
y="120"
width="409"
height="248"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#595959"
id="rect14" />
<text
fill="#ffffff"
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="48px"
transform="translate(358.816,222)"
id="text22">Color buffer<tspan
font-size="16px"
x="39.41"
y="28"
id="tspan16">APPLICATION SPECIFIED</tspan><tspan
font-size="16px"
x="70.660004"
y="66"
id="tspan18">Current frame</tspan><tspan
font-size="16px"
x="58.16"
y="85"
id="tspan20">Render resolution</tspan></text>
<rect
x="1023"
y="120"
width="408"
height="248"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect24" />
<rect
x="1279"
y="278"
width="213"
height="128"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#595959"
id="rect26" />
<text
fill="#ffffff"
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="20px"
transform="translate(1307.65,313)"
id="text34">Current Luminance<tspan
font-size="12px"
x="49.869999"
y="16"
id="tspan28">R16_FLOAT</tspan><tspan
font-size="12px"
x="43"
y="45"
id="tspan30">Current frame</tspan><tspan
font-size="12px"
x="25.870001"
y="59"
id="tspan32">Render resolution / 2</tspan></text>
<rect
x="1421"
y="374"
width="115"
height="64"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect36" />
<rect
x="1507"
y="422"
width="59"
height="31"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect38" />
<rect
x="1551"
y="446"
width="29"
height="15"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect40" />
<rect
x="1573"
y="457"
width="15"
height="7"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#595959"
id="rect42" />
<text
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="22px"
transform="translate(1680.61,380)"
id="text50">Exposure<tspan
font-size="16px"
x="3.8800001"
y="20"
id="tspan44">R32_FLOAT</tspan><tspan
font-size="16px"
x="-5.1199999"
y="59"
id="tspan46">Current frame</tspan><tspan
font-size="16px"
x="29.5"
y="78"
id="tspan48">1x1</tspan></text>
<path
d="M 0.258096,-0.428237 57.0346,33.7906 56.5184,34.6471 -0.258096,0.428237 Z M 58.1607,28.0477 65.3413,39.3808 51.9664,38.3253 Z"
transform="matrix(-1,0,0,1,1660.84,411.5)"
id="path52" />
<rect
x="271"
y="440"
width="21"
height="21"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#595959"
id="rect54" />
<text
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="21px"
transform="translate(302.565,458)"
id="text56">Memory allocated</text>
<rect
x="519"
y="440"
width="21"
height="21"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect58" />
<text
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="21px"
transform="translate(548.709,458)"
id="text64">No<tspan
font-size="21px"
x="29.25"
y="0"
id="tspan60">memory</tspan><tspan
font-size="21px"
x="105.75"
y="0"
id="tspan62">allocate</tspan>d</text>
<rect
x="762"
y="199"
width="179"
height="90"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
id="rect66" />
<text
fill="#ffffff"
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="18px"
transform="translate(796.991,240)"
id="text70">Auto-exposure <tspan
font-size="18px"
x="34.5"
y="21"
id="tspan68">(SPD</tspan>)</text>
<path
d="m 680,241 h 67.146 v 6 H 680 Z m 64.146,-6 18,9 -18,9 z"
id="path72" />
<path
d="m 941,241 h 67.15 v 6 H 941 Z m 64.15,-6 18,9 -18,9 z"
id="path74" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.6 KiB

@ -0,0 +1,397 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="153.31731mm"
height="95.659805mm"
viewBox="0 0 153.31731 95.659804"
version="1.1"
id="svg5"
inkscape:version="1.1.2 (b8e25be833, 2022-02-05)"
sodipodi:docname="calculate-bounding-box.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="0.77771465"
inkscape:cx="2170.4619"
inkscape:cy="970.79308"
inkscape:window-width="3699"
inkscape:window-height="2126"
inkscape:window-x="130"
inkscape:window-y="-11"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs2">
<clipPath
id="clip0">
<rect
x="91"
y="126"
width="818"
height="533"
id="rect9" />
</clipPath>
<clipPath
id="clip0-4">
<rect
x="90"
y="126"
width="591"
height="386"
id="rect578" />
</clipPath>
</defs>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(469.25724,108.45535)">
<g
clip-path="url(#clip0-4)"
transform="matrix(0.26458333,0,0,0.26458333,-493.33432,-144.57045)"
id="g667">
<rect
x="357"
y="173"
width="260"
height="258"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
stroke-dasharray="6, 2"
fill="#ffffff"
id="rect583" />
<rect
x="92"
y="266"
width="28"
height="28"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect585" />
<text
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="18px"
transform="translate(101.408,286)"
id="text587">1</text>
<rect
x="92"
y="294"
width="28"
height="28"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#262626"
id="rect589" />
<text
fill="#ffffff"
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="18px"
transform="translate(101.408,314)"
id="text591">4</text>
<rect
x="92"
y="322"
width="28"
height="28"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#7f7f7f"
id="rect593" />
<text
fill="#ffffff"
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="18px"
transform="translate(101.408,342)"
id="text595">7</text>
<rect
x="120"
y="266"
width="29"
height="28"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#bfbfbf"
id="rect597" />
<text
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="18px"
transform="translate(129.92,286)"
id="text599">2</text>
<rect
x="120"
y="294"
width="29"
height="28"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#7f7f7f"
id="rect601" />
<text
fill="#ffffff"
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="18px"
transform="translate(129.92,314)"
id="text603">5</text>
<rect
x="120"
y="322"
width="29"
height="28"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#404040"
id="rect605" />
<text
fill="#ffffff"
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="18px"
transform="translate(129.919,342)"
id="text607">8</text>
<rect
x="149"
y="266"
width="28"
height="28"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#a6a6a6"
id="rect609" />
<text
fill="#ffffff"
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="18px"
transform="translate(158.431,286)"
id="text611">3</text>
<rect
x="149"
y="294"
width="28"
height="28"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#f2f2f2"
id="rect613" />
<text
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="18px"
transform="translate(158.431,314)"
id="text615">6</text>
<rect
x="149"
y="322"
width="28"
height="28"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#262626"
id="rect617" />
<text
fill="#ffffff"
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="18px"
transform="translate(158.431,342)"
id="text619">9</text>
<rect
x="386"
y="225"
width="28"
height="28"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect621" />
<text
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="21px"
transform="translate(394.451,247)"
id="text623">1</text>
<rect
x="588"
y="373"
width="29"
height="28"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#262626"
id="rect625" />
<text
fill="#ffffff"
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="21px"
transform="translate(596.927,395)"
id="text627">9</text>
<rect
x="501"
y="229"
width="28"
height="28"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#7f7f7f"
id="rect629" />
<text
fill="#ffffff"
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="21px"
transform="translate(509.738,251)"
id="text631">5</text>
<rect
x="414"
y="173"
width="29"
height="28"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#bfbfbf"
id="rect633" />
<text
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="21px"
transform="translate(423.376,195)"
id="text635">2</text>
<rect
x="472"
y="288"
width="29"
height="28"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#7f7f7f"
id="rect637" />
<text
fill="#ffffff"
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="21px"
transform="translate(481.227,309)"
id="text639">7</text>
<rect
x="530"
y="403"
width="29"
height="27"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#404040"
id="rect641" />
<text
fill="#ffffff"
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="21px"
transform="translate(539.077,424)"
id="text643">8</text>
<rect
x="443"
y="316"
width="29"
height="28"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#a6a6a6"
id="rect645" />
<text
fill="#ffffff"
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="21px"
transform="translate(452.301,337)"
id="text647">3</text>
<rect
x="357"
y="342"
width="29"
height="28"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#f2f2f2"
id="rect649" />
<text
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="21px"
transform="translate(366.039,364)"
id="text651">6</text>
<rect
x="559"
y="243"
width="29"
height="28"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#262626"
id="rect653" />
<text
fill="#ffffff"
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="21px"
transform="translate(568.002,265)"
id="text655">4</text>
<path
d="m 301.5,474 h 367.978 v 1 H 301.5 Z m 356.227,-6.932 12.74,7.432 -12.74,7.432 c -0.239,0.139 -0.545,0.059 -0.684,-0.18 -0.139,-0.238 -0.058,-0.545 0.18,-0.684 l 12,-7 v 0.864 l -12,-7 c -0.238,-0.139 -0.319,-0.445 -0.18,-0.684 0.139,-0.238 0.445,-0.319 0.684,-0.18 z"
id="path657" />
<path
d="m 300.463,474.811 -1.459,-337.328 1,-0.004 1.459,337.328 z m -8.34,-325.541 7.377,-12.772 7.487,12.708 c 0.14,0.238 0.061,0.544 -0.177,0.684 -0.238,0.141 -0.545,0.061 -0.685,-0.177 l -7.052,-11.969 0.864,-0.004 -6.948,12.03 c -0.138,0.239 -0.444,0.321 -0.683,0.183 -0.239,-0.138 -0.321,-0.444 -0.183,-0.683 z"
id="path659" />
<text
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="16px"
transform="translate(482.727,498)"
id="text663">Y<tspan
font-size="16px"
x="-207.86301"
y="-184"
id="tspan661">Co</tspan></text>
<path
d="m 194,303 h 49.772 v 6 H 194 Z m 46.772,-6 18,9 -18,9 z"
id="path665" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

@ -0,0 +1,142 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="108.68822mm"
height="91.637817mm"
viewBox="0 0 108.68822 91.637818"
version="1.1"
id="svg201"
inkscape:version="1.1.2 (b8e25be833, 2022-02-05)"
sodipodi:docname="clamp-to-box.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview203"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="0.77771465"
inkscape:cx="1852.8647"
inkscape:cy="639.05187"
inkscape:window-width="3699"
inkscape:window-height="2126"
inkscape:window-x="130"
inkscape:window-y="-11"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs198">
<clipPath
id="clip0">
<rect
x="75"
y="244"
width="718"
height="568"
id="rect205" />
</clipPath>
<clipPath
id="clip0-4">
<rect
x="75"
y="210"
width="455"
height="376"
id="rect415" />
</clipPath>
</defs>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(385.15442,20.48774)">
<g
clip-path="url(#clip0-4)"
transform="matrix(0.26458333,0,0,0.26458333,-414.38962,-79.413341)"
id="g450">
<rect
x="148"
y="310.75"
width="206.25"
height="211.25"
fill="#ffffff"
id="rect420" />
<path
d="M 354.25,310.75 423,242 V 453.25 L 354.25,522 Z"
fill="#cdcdcd"
fill-rule="evenodd"
id="path422" />
<path
d="M 148,310.75 216.75,242 H 423 l -68.75,68.75 z"
fill="#ffffff"
fill-rule="evenodd"
id="path424" />
<path
d="M 148,310.75 216.75,242 H 423 V 453.25 L 354.25,522 H 148 Z m 0,0 H 354.25 L 423,242 m -68.75,68.75 V 522"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="none"
fill-rule="evenodd"
id="path426" />
<path
d="m 149.491,539 h 205.131 v 1 H 149.491 Z m 11.747,7.932 -12.74,-7.432 12.74,-7.432 c 0.239,-0.139 0.545,-0.058 0.684,0.18 0.139,0.239 0.058,0.545 -0.18,0.684 l -12,7 v -0.864 l 12,7 c 0.238,0.139 0.319,0.445 0.18,0.684 -0.139,0.238 -0.445,0.319 -0.684,0.18 z m 181.637,-14.864 12.741,7.432 -12.741,7.432 c -0.238,0.139 -0.545,0.059 -0.684,-0.18 -0.139,-0.238 -0.058,-0.545 0.18,-0.684 l 12,-7 v 0.864 l -12,-7 c -0.238,-0.139 -0.319,-0.445 -0.18,-0.684 0.139,-0.238 0.446,-0.319 0.684,-0.18 z"
id="path428" />
<path
d="m 136,310.486 v 211.739 h -1 V 310.486 Z m -7.932,11.752 7.432,-12.74 7.432,12.74 c 0.139,0.239 0.058,0.545 -0.18,0.684 -0.239,0.139 -0.545,0.058 -0.684,-0.18 l -7,-12 h 0.864 l -7,12 c -0.139,0.238 -0.445,0.319 -0.684,0.18 -0.238,-0.139 -0.319,-0.445 -0.18,-0.684 z m 14.864,188.24 -7.432,12.74 -7.432,-12.74 c -0.139,-0.239 -0.058,-0.545 0.18,-0.684 0.239,-0.139 0.545,-0.058 0.684,0.18 l 7,12 h -0.864 l 7,-12 c 0.139,-0.238 0.446,-0.319 0.684,-0.18 0.239,0.139 0.319,0.445 0.18,0.684 z"
id="path430" />
<path
d="M 1.04255,0.377307 66.9117,70.2796 66.1839,70.9654 0.314756,1.06311 Z M 3.32688,14.3674 -0.00158941,-0.00168668 14.1446,4.17387 c 0.2649,0.07818 0.4162,0.35625 0.338,0.6211 -0.0782,0.26484 -0.3562,0.41617 -0.6211,0.338 L 0.537392,1.20006 1.16604,0.607681 4.30109,14.1418 C 4.3634,14.4108 4.19584,14.6794 3.92682,14.7417 3.6578,14.804 3.3892,14.6365 3.32688,14.3674 Z M 63.9003,56.9762 67.2288,71.3453 53.0826,67.1698 c -0.2648,-0.0782 -0.4162,-0.3562 -0.338,-0.6211 0.0782,-0.2648 0.3563,-0.4162 0.6211,-0.338 l 13.3241,3.9329 -0.6286,0.5923 -3.1351,-13.534 c -0.0623,-0.269 0.1052,-0.5376 0.3743,-0.6 0.269,-0.0623 0.5376,0.1053 0.5999,0.3743 z"
transform="matrix(-1,0,0,1,436.727,459.5)"
id="path432" />
<text
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="20px"
transform="translate(246.842,569)"
id="text438">Y<tspan
font-size="20px"
x="158.099"
y="-52"
id="tspan434">Cg</tspan><tspan
font-size="20px"
x="-137.29401"
y="-139"
id="tspan436">Co</tspan></text>
<path
d="m 475,249.5 c 0,-4.142 3.358,-7.5 7.5,-7.5 4.142,0 7.5,3.358 7.5,7.5 0,4.142 -3.358,7.5 -7.5,7.5 -4.142,0 -7.5,-3.358 -7.5,-7.5 z"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#bfbfbf"
fill-rule="evenodd"
id="path440" />
<path
d="M 1.04793,0.36318 86.4139,88.4899 85.6956,89.1857 0.329665,1.05895 Z M 3.52476,14.3202 -0.0016124,-0.00166446 14.2008,3.97836 c 0.2659,0.07451 0.4211,0.35047 0.3466,0.61637 -0.0745,0.2659 -0.3505,0.42105 -0.6164,0.34654 L 0.553895,1.19253 1.17431,0.591538 4.49575,14.0811 C 4.56178,14.3492 4.39793,14.6201 4.1298,14.6861 3.86166,14.7522 3.59078,14.5883 3.52476,14.3202 Z M 83.2195,75.2292 86.7457,89.5511 72.5433,85.571 c -0.2659,-0.0745 -0.4211,-0.3505 -0.3465,-0.6164 0.0745,-0.2659 0.3504,-0.421 0.6163,-0.3465 l 13.3771,3.7488 -0.6204,0.601 -3.3213,-13.4896 c -0.0661,-0.2681 0.0978,-0.539 0.3659,-0.605 0.2682,-0.066 0.539,0.0978 0.6051,0.3659 z"
transform="matrix(-1,0,0,1,477.244,255.5)"
id="path442" />
<text
font-family="'Cambria Math', 'Cambria Math_MSFontService', sans-serif"
font-weight="400"
font-size="28px"
transform="translate(498.106,236)"
id="text448">𝑠<tspan
font-size="20px"
x="12.32"
y="6"
id="tspan444"></tspan><tspan
font-size="28px"
x="-56.324799"
y="76"
id="tspan446">𝑑</tspan></text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.0 KiB

@ -0,0 +1,188 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="161.14555mm"
height="118.31009mm"
viewBox="0 0 161.14555 118.31009"
version="1.1"
id="svg772"
inkscape:version="1.1.2 (b8e25be833, 2022-02-05)"
sodipodi:docname="disocclusion.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview774"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="0.77771465"
inkscape:cx="1031.2266"
inkscape:cy="322.74048"
inkscape:window-width="3699"
inkscape:window-height="2126"
inkscape:window-x="130"
inkscape:window-y="-11"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs769">
<clipPath
id="clip0">
<rect
x="240"
y="194"
width="629"
height="454"
id="rect776" />
</clipPath>
<image
width="64"
height="64"
xlink:href="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAkACQAAD/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wAARCABAAEADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD8qq/qoor+VegAr+qiiv5V6ACv6qKK/lXoAK/qoor+VegAr+qiiv5V6ACv6qKK/lXoAK/qooooA/lXr+qiiv5V6ACv6qKK/lXoAK/qoor+VegAr+qiiv5V6ACv6qKK/lXoAK/qooooA/lXr+qiiv5V6ACv6qKK/lXoAK/qoor+VegD/9k="
preserveAspectRatio="none"
id="img1" />
<pattern
width="64"
height="64"
patternUnits="userSpaceOnUse"
id="pattern2"
overflow="hidden"
patternTransform="matrix(0.1875 9.31323e-10 -9.31323e-10 0.1875 600 360)">
<use
width="100%"
height="100%"
xlink:href="#img1"
id="use780" />
</pattern>
</defs>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(167.66556,-62.978976)">
<g
clip-path="url(#clip0)"
transform="matrix(0.26458333,0,0,0.26458333,-231.41076,11.64981)"
id="g817">
<path
d="M 775,195 509.313,529 h -0.626 L 243,195 Z"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#f2f2f2"
fill-rule="evenodd"
fill-opacity="0.321569"
id="path785" />
<path
d="M 845,195 579.764,529 h -2.528 L 312,195 Z"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#f2f2f2"
fill-rule="evenodd"
fill-opacity="0.321569"
id="path787" />
<path
d="M 613.761,196.079 530.057,529.795 508.651,529.789 423.616,195.567 Z"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#0d0d0d"
fill-rule="evenodd"
fill-opacity="0.25098"
id="path789" />
<path
d="m 548.253,196.058 48.14,333.75 -26.424,-0.008 -224.12,-334.049 z"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#0d0d0d"
fill-rule="evenodd"
fill-opacity="0.25098"
id="path791" />
<path
d="M 613.853,195.785 571.389,359.67 549.14,196.23 Z"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="url(#pattern2)"
fill-rule="evenodd"
id="path793" />
<path
d="m 467.401,359.12 c -0.535,-30.66 22.029,-55.916 50.398,-56.411 28.369,-0.495 51.801,23.958 52.336,54.618 0.535,30.66 -22.029,55.916 -50.398,56.411 -28.369,0.495 -51.801,-23.958 -52.336,-54.618 z"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#404040"
fill-rule="evenodd"
id="path795" />
<text
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="20px"
transform="translate(760.019,555)"
id="text797">Near plane</text>
<path
d="m 532,546 -20,23 -20,-23 z"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill-rule="evenodd"
id="path799" />
<rect
x="492"
y="560"
width="40"
height="49"
id="rect801" />
<path
d="M 599,546 579.5,569 560,546 Z"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill-rule="evenodd"
id="path803" />
<rect
x="560"
y="560"
width="39"
height="49"
id="rect805" />
<path
d="m 269,528 580.96,4.393"
stroke="#000000"
stroke-width="5.5"
stroke-miterlimit="8"
fill="none"
fill-rule="evenodd"
id="path807" />
<text
font-family="'Cambria Math', 'Cambria Math_MSFontService', sans-serif"
font-weight="400"
font-size="28px"
transform="translate(505.891,635)"
id="text815">𝐏<tspan
font-size="20px"
x="15.45"
y="6"
id="tspan809">0</tspan><tspan
font-size="28px"
x="68.396202"
y="0"
id="tspan811">𝐏</tspan><tspan
font-size="20px"
x="83.196198"
y="5"
id="tspan813">1</tspan></text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.2 KiB

@ -0,0 +1,215 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="229.12453mm"
height="122.14461mm"
viewBox="0 0 229.12453 122.14461"
version="1.1"
id="svg5"
inkscape:version="1.1.2 (b8e25be833, 2022-02-05)"
sodipodi:docname="jitter-space.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="0.77771465"
inkscape:cx="2071.4539"
inkscape:cy="867.92759"
inkscape:window-width="3699"
inkscape:window-height="2126"
inkscape:window-x="130"
inkscape:window-y="-11"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs2">
<clipPath
id="clip0">
<rect
x="232"
y="262"
width="931"
height="465"
id="rect9" />
</clipPath>
<clipPath
id="clip0-5">
<rect
x="232"
y="251"
width="897"
height="476"
id="rect132" />
</clipPath>
<clipPath
id="clip0-0">
<rect
x="232"
y="251"
width="897"
height="476"
id="rect313" />
</clipPath>
</defs>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(442.89752,81.176205)">
<g
clip-path="url(#clip0-0)"
transform="matrix(0.26458333,0,0,0.26458333,-508.39182,-148.60245)"
id="g376">
<rect
x="301"
y="309"
width="304"
height="285"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#f2f2f2"
id="rect318" />
<path
d="M 454,310.491 V 592.85 h -1 V 310.491 Z m -7.932,11.747 7.432,-12.74 7.432,12.74 c 0.139,0.239 0.058,0.545 -0.18,0.684 -0.239,0.139 -0.545,0.058 -0.684,-0.18 l -7,-12 h 0.864 l -7,12 c -0.139,0.238 -0.445,0.319 -0.684,0.18 -0.238,-0.139 -0.319,-0.445 -0.18,-0.684 z m 14.864,258.865 -7.432,12.741 -7.432,-12.741 c -0.139,-0.238 -0.058,-0.545 0.18,-0.684 0.239,-0.139 0.545,-0.058 0.684,0.18 l 7,12 h -0.864 l 7,-12 c 0.139,-0.238 0.446,-0.319 0.684,-0.18 0.239,0.139 0.319,0.446 0.18,0.684 z"
id="path320" />
<path
d="M 0.984252,-0.499999 302.49,-0.499843 v 1 L 0.984252,0.500001 Z M 12.7381,7.4319 -0.00231745,0 12.7381,-7.43188 c 0.2385,-0.13914 0.5447,-0.05857 0.6838,0.17995 0.1391,0.23853 0.0586,0.54469 -0.18,0.68383 L 1.24194,0.43189 V -0.431889 L 13.2419,6.56812 c 0.2386,0.13914 0.3191,0.4453 0.18,0.68382 -0.1392,0.23853 -0.4453,0.3191 -0.6838,0.17996 z M 290.742,-7.43174 303.483,1.57377e-4 290.742,7.43204 c -0.238,0.13914 -0.544,0.05857 -0.683,-0.17995 -0.14,-0.23853 -0.059,-0.54469 0.18,-0.68383 l 12,-6.999993 v 0.863779 l -12,-7.000006 c -0.239,-0.13914 -0.32,-0.4453 -0.18,-0.68383 0.139,-0.23852 0.445,-0.31909 0.683,-0.17995 z"
transform="matrix(-1,0,0,1,604.98,452.5)"
id="path322" />
<text
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="21px"
transform="translate(379.835,665)"
id="text332">Unit pixel offset*<tspan
font-size="21px"
x="56.375099"
y="-376"
id="tspan324">-</tspan>0.5<tspan
font-size="21px"
x="62.906799"
y="-40"
id="tspan326">0.5</tspan><tspan
font-size="21px"
x="233.916"
y="-206"
id="tspan328">0.5</tspan><tspan
font-size="21px"
x="-133.005"
y="-206"
id="tspan330">-</tspan>0.5</text>
<rect
x="779"
y="309"
width="304"
height="285"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#f2f2f2"
id="rect334" />
<path
d="M 0.500001,0.984252 0.500157,283.003 h -1 L -0.499999,0.984252 Z M -7.43188,12.7381 0,-0.00231745 7.4319,12.7381 c 0.13914,0.2385 0.05857,0.5446 -0.17996,0.6838 -0.23852,0.1391 -0.54468,0.0586 -0.68382,-0.18 L -0.431889,1.24194 H 0.43189 L -6.5681,13.2419 c -0.13914,0.2386 -0.4453,0.3191 -0.68383,0.18 -0.23852,-0.1391 -0.31909,-0.4453 -0.17995,-0.6838 z M 7.43204,271.255 1.57342e-4,283.996 -7.43174,271.255 c -0.13914,-0.238 -0.05857,-0.544 0.17995,-0.683 0.23853,-0.139 0.54469,-0.059 0.68383,0.18 l 7.000006,12 h -0.863779 l 6.999993,-12 c 0.13914,-0.239 0.4453,-0.319 0.68383,-0.18 0.23852,0.139 0.31909,0.445 0.17995,0.683 z"
transform="matrix(1,0,0,-1,931.5,594.494)"
id="path336" />
<path
d="m 780.498,451.002 301.492,0.477 v 1 l -301.493,-0.477 z m 11.728,7.95 -12.728,-7.452 12.752,-7.412 c 0.239,-0.138 0.545,-0.057 0.683,0.181 0.139,0.239 0.058,0.545 -0.181,0.684 l -12.011,6.981 0.002,-0.864 11.989,7.019 c 0.238,0.14 0.318,0.446 0.178,0.684 -0.139,0.238 -0.445,0.319 -0.684,0.179 z m 278.024,-14.424 12.73,7.452 -12.75,7.412 c -0.24,0.139 -0.54,0.058 -0.68,-0.181 -0.14,-0.239 -0.06,-0.545 0.18,-0.684 l 12.01,-6.981 v 0.864 l -11.99,-7.019 c -0.24,-0.139 -0.32,-0.446 -0.18,-0.684 0.14,-0.238 0.45,-0.318 0.68,-0.179 z"
id="path338" />
<text
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="21px"
transform="translate(859.831,665)"
id="text352">Projection offset<tspan
font-size="14px"
x="-559.68903"
y="49"
id="tspan340">*</tspan><tspan
font-size="14px"
x="-549.43903"
y="49"
id="tspan342">-</tspan><tspan
font-size="14px"
x="-541.93903"
y="49"
id="tspan344">Y component is inverted to match UV space</tspan><tspan
font-family="'Cambria Math', 'Cambria Math_MSFontService', sans-serif"
font-size="21px"
x="-132.918"
y="-205"
id="tspan346"></tspan><tspan
font-family="'Cambria Math', 'Cambria Math_MSFontService', sans-serif"
font-size="21px"
x="-111.488"
y="-220"
id="tspan348">1</tspan><tspan
font-family="'Cambria Math', 'Cambria Math_MSFontService', sans-serif"
font-size="21px"
x="-113.668"
y="-191"
id="tspan350">𝑤</tspan></text>
<path
d="m 746.604,453.415 h 8 8 v 1 h -8 -8 z"
fill-rule="evenodd"
id="path354" />
<text
font-family="'Cambria Math', 'Cambria Math_MSFontService', sans-serif"
font-weight="400"
font-size="21px"
transform="translate(910.115,630)"
id="text360"><tspan
font-size="21px"
x="19.43"
y="-15"
id="tspan356">1</tspan><tspan
font-size="21px"
x="19.25"
y="14"
id="tspan358"></tspan></text>
<path
d="m 929.795,623.439 h 6 6 v 1 h -6 -6 z"
fill-rule="evenodd"
id="path362" />
<text
font-family="'Cambria Math', 'Cambria Math_MSFontService', sans-serif"
font-weight="400"
font-size="21px"
transform="translate(924.727,269)"
id="text366">1<tspan
font-size="21px"
x="-0.179993"
y="30"
id="tspan364"></tspan></text>
<path
d="m 924.607,277.953 h 6 6 v 1 h -6 -6 z"
fill-rule="evenodd"
id="path368" />
<text
font-family="'Cambria Math', 'Cambria Math_MSFontService', sans-serif"
font-weight="400"
font-size="21px"
transform="translate(1099.64,445)"
id="text372">1<tspan
font-size="21px"
x="-2.17993"
y="30"
id="tspan370">𝑤</tspan></text>
<path
d="m 1097.52,453.587 h 8 8 v 1 h -8 -8 z"
fill-rule="evenodd"
id="path374" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.7 KiB

@ -0,0 +1,111 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="114.56458mm"
height="93.759834mm"
viewBox="0 0 114.56458 93.759834"
version="1.1"
id="svg4107"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs4104">
<clipPath
id="clip0">
<rect
x="238"
y="119"
width="433"
height="364"
id="rect4111" />
</clipPath>
</defs>
<g
id="layer1"
transform="translate(162.74621,-52.46029)">
<g
clip-path="url(#clip0)"
transform="matrix(0.26458333,0,0,0.26458333,-225.71704,18.639099)"
id="g4138">
<path
d="M 0.501077,0.989431 0.570418,64.7178 -0.429581,64.7189 -0.498923,0.990519 Z M -7.41803,12.7461 -2.54736e-6,-0.00231738 7.44574,12.73 c 0.1394,0.2383 0.05917,0.5446 -0.17921,0.684 -0.23837,0.1394 -0.54462,0.0592 -0.68402,-0.1792 L -0.430538,1.2424 0.433241,1.24146 -6.5537,13.2491 c -0.13888,0.2387 -0.44495,0.3196 -0.68363,0.1807 -0.23867,-0.1389 -0.31958,-0.445 -0.1807,-0.6837 z M 7.48952,52.9624 0.0714987,65.7108 -7.37425,52.9785 c -0.1394,-0.2383 -0.05916,-0.5446 0.17921,-0.684 0.23837,-0.1394 0.54462,-0.0591 0.68402,0.1792 l 7.013054,11.9924 -0.863778,9e-4 6.986934,-12.0076 c 0.13888,-0.2386 0.44496,-0.3195 0.68363,-0.1807 0.23868,0.1389 0.31958,0.445 0.1807,0.6837 z"
transform="matrix(0,1,1,0,588.5,178.5)"
id="path4116" />
<text
font-family="'Cambria Math', 'Cambria Math_MSFontService', sans-serif"
font-weight="400"
font-size="36px"
transform="translate(587.798,153)"
id="text4120">𝒌<tspan
font-size="26px"
x="22"
y="7"
id="tspan4118">𝑠𝑒𝑝</tspan></text>
<rect
x="573"
y="278"
width="14"
height="77"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#bfbfbf"
id="rect4122" />
<rect
x="656"
y="362"
width="14"
height="77"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#404040"
id="rect4124" />
<path
d="m 344,362 -39,-37 39,-37 z"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill-rule="evenodd"
id="path4126" />
<rect
x="238"
y="288"
width="82"
height="74"
id="rect4128" />
<path
d="M 1.83093e-6,-2.25 182.275,-2.24985 v 4.5 L -1.83093e-6,2.25 Z M 180.026,-6.74985 193.526,1.5748e-4 180.026,6.75015 Z"
transform="matrix(1,0,0,-1,344.5,325.5)"
id="path4130" />
<rect
x="607"
y="201"
width="14"
height="77"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#f2f2f2"
id="rect4132" />
<path
d="M 589.5,178.5 V 482.196"
stroke="#000000"
stroke-miterlimit="8"
stroke-dasharray="4, 3"
fill="none"
fill-rule="evenodd"
id="path4134" />
<path
d="M 0,0 1.5748e-4,303.696"
stroke="#000000"
stroke-miterlimit="8"
stroke-dasharray="4, 3"
fill="none"
fill-rule="evenodd"
transform="matrix(1,0,0,-1,654.5,482.196)"
id="path4136" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

@ -0,0 +1,528 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="124.35416mm"
height="61.434879mm"
viewBox="0 0 124.35417 61.434879"
version="1.1"
id="svg777"
inkscape:version="1.1.2 (b8e25be833, 2022-02-05)"
sodipodi:docname="motion_vectors.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview779"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="0.77771465"
inkscape:cx="582.47585"
inkscape:cy="7.7149119"
inkscape:window-width="3699"
inkscape:window-height="2126"
inkscape:window-x="130"
inkscape:window-y="-11"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs774">
<clipPath
id="clip0">
<rect
x="872"
y="92"
width="470"
height="251"
id="rect781" />
</clipPath>
<clipPath
id="clip0-8">
<rect
x="724"
y="418"
width="470"
height="251"
id="rect428" />
</clipPath>
</defs>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(48.828399,-146.09089)">
<g
clip-path="url(#clip0-8)"
transform="matrix(0.26458333,0,0,0.26458333,-240.38673,35.49506)"
id="g567">
<rect
x="726"
y="571"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect433" />
<rect
x="777"
y="419"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect435" />
<rect
x="725"
y="419"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect437" />
<rect
x="725"
y="467"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect439" />
<rect
x="777"
y="519"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect441" />
<rect
x="725"
y="519"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect443" />
<path
d="m 743,443 c 0,-4.418 3.582,-8 8,-8 4.418,0 8,3.582 8,8 0,4.418 -3.582,8 -8,8 -4.418,0 -8,-3.582 -8,-8 z"
fill-rule="evenodd"
id="path445" />
<path
d="m 795,443 c 0,-4.418 3.582,-8 8,-8 4.418,0 8,3.582 8,8 0,4.418 -3.582,8 -8,8 -4.418,0 -8,-3.582 -8,-8 z"
fill-rule="evenodd"
id="path447" />
<path
d="m 795,545 c 0,-4.418 3.582,-8 8,-8 4.418,0 8,3.582 8,8 0,4.418 -3.582,8 -8,8 -4.418,0 -8,-3.582 -8,-8 z"
fill-rule="evenodd"
id="path449" />
<path
d="m 743,546 c 0,-4.418 3.358,-8 7.5,-8 4.142,0 7.5,3.582 7.5,8 0,4.418 -3.358,8 -7.5,8 -4.142,0 -7.5,-3.582 -7.5,-8 z"
fill-rule="evenodd"
id="path451" />
<rect
x="829"
y="419"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect453" />
<rect
x="829"
y="467"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect455" />
<rect
x="829"
y="519"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect457" />
<path
d="m 847,443 c 0,-4.418 3.582,-8 8,-8 4.418,0 8,3.582 8,8 0,4.418 -3.582,8 -8,8 -4.418,0 -8,-3.582 -8,-8 z"
fill-rule="evenodd"
id="path459" />
<path
d="m 847,495 c 0,-4.418 3.582,-8 8,-8 4.418,0 8,3.582 8,8 0,4.418 -3.582,8 -8,8 -4.418,0 -8,-3.582 -8,-8 z"
fill-rule="evenodd"
id="path461" />
<path
d="m 847,545 c 0,-4.418 3.582,-8 8,-8 4.418,0 8,3.582 8,8 0,4.418 -3.582,8 -8,8 -4.418,0 -8,-3.582 -8,-8 z"
fill-rule="evenodd"
id="path463" />
<rect
x="881"
y="419"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect465" />
<rect
x="881"
y="467"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect467" />
<rect
x="881"
y="519"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect469" />
<path
d="m 899,443 c 0,-4.418 3.582,-8 8,-8 4.418,0 8,3.582 8,8 0,4.418 -3.582,8 -8,8 -4.418,0 -8,-3.582 -8,-8 z"
fill-rule="evenodd"
id="path471" />
<path
d="m 899,495 c 0,-4.418 3.582,-8 8,-8 4.418,0 8,3.582 8,8 0,4.418 -3.582,8 -8,8 -4.418,0 -8,-3.582 -8,-8 z"
fill-rule="evenodd"
id="path473" />
<path
d="m 899,545 c 0,-4.418 3.582,-8 8,-8 4.418,0 8,3.582 8,8 0,4.418 -3.582,8 -8,8 -4.418,0 -8,-3.582 -8,-8 z"
fill-rule="evenodd"
id="path475" />
<rect
x="777"
y="571"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect477" />
<path
d="m 795,595.5 c 0,-4.142 3.582,-7.5 8,-7.5 4.418,0 8,3.358 8,7.5 0,4.142 -3.582,7.5 -8,7.5 -4.418,0 -8,-3.358 -8,-7.5 z"
fill-rule="evenodd"
id="path479" />
<rect
x="829"
y="571"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect481" />
<path
d="m 847,595.5 c 0,-4.142 3.582,-7.5 8,-7.5 4.418,0 8,3.358 8,7.5 0,4.142 -3.582,7.5 -8,7.5 -4.418,0 -8,-3.358 -8,-7.5 z"
fill-rule="evenodd"
id="path483" />
<rect
x="881"
y="571"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect485" />
<path
d="m 899,595.5 c 0,-4.142 3.582,-7.5 8,-7.5 4.418,0 8,3.358 8,7.5 0,4.142 -3.582,7.5 -8,7.5 -4.418,0 -8,-3.358 -8,-7.5 z"
fill-rule="evenodd"
id="path487" />
<rect
x="1037"
y="419"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect489" />
<rect
x="985"
y="419"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect491" />
<rect
x="1037"
y="467"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect493" />
<rect
x="985"
y="467"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect495" />
<rect
x="1037"
y="519"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect497" />
<rect
x="985"
y="519"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect499" />
<path
d="m 1003,493.5 c 0,-4.142 3.58,-7.5 8,-7.5 4.42,0 8,3.358 8,7.5 0,4.142 -3.58,7.5 -8,7.5 -4.42,0 -8,-3.358 -8,-7.5 z"
fill-rule="evenodd"
id="path501" />
<path
d="m 1003,443 c 0,-4.418 3.58,-8 8,-8 4.42,0 8,3.582 8,8 0,4.418 -3.58,8 -8,8 -4.42,0 -8,-3.582 -8,-8 z"
fill-rule="evenodd"
id="path503" />
<path
d="m 1055,443 c 0,-4.418 3.58,-8 8,-8 4.42,0 8,3.582 8,8 0,4.418 -3.58,8 -8,8 -4.42,0 -8,-3.582 -8,-8 z"
fill-rule="evenodd"
id="path505" />
<path
d="m 1055,495 c 0,-4.418 3.58,-8 8,-8 4.42,0 8,3.582 8,8 0,4.418 -3.58,8 -8,8 -4.42,0 -8,-3.582 -8,-8 z"
fill-rule="evenodd"
id="path507" />
<path
d="m 1055,545 c 0,-4.418 3.58,-8 8,-8 4.42,0 8,3.582 8,8 0,4.418 -3.58,8 -8,8 -4.42,0 -8,-3.582 -8,-8 z"
fill-rule="evenodd"
id="path509" />
<path
d="m 1003,546 c 0,-4.418 3.36,-8 7.5,-8 4.14,0 7.5,3.582 7.5,8 0,4.418 -3.36,8 -7.5,8 -4.14,0 -7.5,-3.582 -7.5,-8 z"
fill-rule="evenodd"
id="path511" />
<rect
x="1089"
y="419"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect513" />
<rect
x="1089"
y="467"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect515" />
<rect
x="1089"
y="519"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect517" />
<path
d="m 1107,443 c 0,-4.418 3.58,-8 8,-8 4.42,0 8,3.582 8,8 0,4.418 -3.58,8 -8,8 -4.42,0 -8,-3.582 -8,-8 z"
fill-rule="evenodd"
id="path519" />
<path
d="m 1107,495 c 0,-4.418 3.58,-8 8,-8 4.42,0 8,3.582 8,8 0,4.418 -3.58,8 -8,8 -4.42,0 -8,-3.582 -8,-8 z"
fill-rule="evenodd"
id="path521" />
<path
d="m 1107,545 c 0,-4.418 3.58,-8 8,-8 4.42,0 8,3.582 8,8 0,4.418 -3.58,8 -8,8 -4.42,0 -8,-3.582 -8,-8 z"
fill-rule="evenodd"
id="path523" />
<rect
x="1141"
y="419"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect525" />
<rect
x="1141"
y="467"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect527" />
<rect
x="1141"
y="519"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect529" />
<path
d="m 1159,443 c 0,-4.418 3.58,-8 8,-8 4.42,0 8,3.582 8,8 0,4.418 -3.58,8 -8,8 -4.42,0 -8,-3.582 -8,-8 z"
fill-rule="evenodd"
id="path531" />
<path
d="m 1159,495 c 0,-4.418 3.58,-8 8,-8 4.42,0 8,3.582 8,8 0,4.418 -3.58,8 -8,8 -4.42,0 -8,-3.582 -8,-8 z"
fill-rule="evenodd"
id="path533" />
<path
d="m 1159,545 c 0,-4.418 3.58,-8 8,-8 4.42,0 8,3.582 8,8 0,4.418 -3.58,8 -8,8 -4.42,0 -8,-3.582 -8,-8 z"
fill-rule="evenodd"
id="path535" />
<rect
x="985"
y="571"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect537" />
<path
d="m 1003,595.5 c 0,-4.142 3.58,-7.5 8,-7.5 4.42,0 8,3.358 8,7.5 0,4.142 -3.58,7.5 -8,7.5 -4.42,0 -8,-3.358 -8,-7.5 z"
fill-rule="evenodd"
id="path539" />
<rect
x="1089"
y="571"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect541" />
<path
d="m 1107,595.5 c 0,-4.142 3.58,-7.5 8,-7.5 4.42,0 8,3.358 8,7.5 0,4.142 -3.58,7.5 -8,7.5 -4.42,0 -8,-3.358 -8,-7.5 z"
fill-rule="evenodd"
id="path543" />
<rect
x="1141"
y="571"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect545" />
<path
d="m 1159,595.5 c 0,-4.142 3.58,-7.5 8,-7.5 4.42,0 8,3.358 8,7.5 0,4.142 -3.58,7.5 -8,7.5 -4.42,0 -8,-3.358 -8,-7.5 z"
fill-rule="evenodd"
id="path547" />
<rect
x="777"
y="467"
width="52"
height="52"
stroke="#000000"
stroke-width="6"
stroke-miterlimit="8"
fill="#7f7f7f"
id="rect549" />
<path
d="m 743,493.5 c 0,-4.142 3.582,-7.5 8,-7.5 4.418,0 8,3.358 8,7.5 0,4.142 -3.582,7.5 -8,7.5 -4.418,0 -8,-3.358 -8,-7.5 z"
fill-rule="evenodd"
id="path551" />
<path
d="m 795,495 c 0,-4.418 3.582,-8 8,-8 4.418,0 8,3.582 8,8 0,4.418 -3.582,8 -8,8 -4.418,0 -8,-3.582 -8,-8 z"
fill-rule="evenodd"
id="path553" />
<path
d="m 743,595.5 c 0,-4.142 3.582,-7.5 8,-7.5 4.418,0 8,3.358 8,7.5 0,4.142 -3.582,7.5 -8,7.5 -4.418,0 -8,-3.358 -8,-7.5 z"
fill-rule="evenodd"
id="path555" />
<rect
x="1037"
y="571"
width="52"
height="52"
stroke="#000000"
stroke-width="6"
stroke-miterlimit="8"
fill="#7f7f7f"
id="rect557" />
<path
d="m 1055,595.5 c 0,-4.142 3.58,-7.5 8,-7.5 4.42,0 8,3.358 8,7.5 0,4.142 -3.58,7.5 -8,7.5 -4.42,0 -8,-3.358 -8,-7.5 z"
fill-rule="evenodd"
id="path559" />
<path
d="m 1057.96,591.577 -243.323,-87.633 1.016,-2.822 243.327,87.633 z M 814.994,508.856 809.5,500.5 l 9.56,-2.934 z"
id="path561" />
<text
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="21px"
transform="translate(763.067,650)"
id="text565">Previous frame<tspan
font-size="21px"
x="264.793"
y="-2"
id="tspan563">Current frame</tspan></text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 15 KiB

@ -0,0 +1,230 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="294.74582mm"
height="90.487495mm"
viewBox="0 0 294.74582 90.487495"
version="1.1"
id="svg2500"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs2497">
<clipPath
id="clip0">
<rect
x="44"
y="95"
width="1114"
height="344"
id="rect2504" />
</clipPath>
</defs>
<g
id="layer1"
transform="translate(214.39353,-61.921199)">
<g
clip-path="url(#clip0)"
transform="matrix(0.26458333,0,0,0.26458333,-226.0352,36.785783)"
id="g2585">
<path
d="m 422.002,263 439.846,0.248 -0.003,6 L 421.998,269 Z m 436.85,-5.753 17.995,9.01 -18.005,8.99 z"
id="path2509" />
<rect
x="422"
y="96"
width="358"
height="340"
id="rect2511" />
<text
fill="#ffffff"
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="24px"
transform="translate(554.201,125)"
id="text2517">FidelityFX <tspan
font-size="24px"
x="-53.669998"
y="29"
id="tspan2513">Super Resolution</tspan><tspan
font-size="24px"
x="117.16"
y="29"
id="tspan2515">2.0</tspan></text>
<path
d="m 726,261 h 39 v 92.666 h -24 v -6 h 21 l -3,3 V 264 l 3,3 h -36 z m 18,98.666 -18,-9 18,-9 z"
fill="#ffffff"
id="path2519" />
<rect
x="877"
y="215"
width="280"
height="102"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#ffffff"
id="rect2521" />
<text
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="20px"
transform="translate(960.821,239)"
id="text2529">Output buffer<tspan
font-size="14px"
x="-11.6299"
y="18"
id="tspan2523">APPLICATION SPECIFIED</tspan><tspan
font-size="14px"
x="15.75"
y="52"
id="tspan2525">Current frame</tspan><tspan
font-size="14px"
x="-11"
y="69"
id="tspan2527">Presentation resolution</tspan></text>
<rect
x="45"
y="96"
width="280"
height="103"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#ffffff"
id="rect2531" />
<text
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="20px"
transform="translate(132.951,122)"
id="text2541">Depth buffer<tspan
font-size="14px"
x="-48"
y="18"
id="tspan2533">APPLICATION SPECIFIED (1x FLOAT)</tspan><tspan
font-size="14px"
x="11.63"
y="52"
id="tspan2535">Current frame</tspan><tspan
font-size="14px"
x="1"
y="69"
id="tspan2537">Render</tspan><tspan
font-size="14px"
x="45.25"
y="69"
id="tspan2539">resolution</tspan></text>
<rect
x="45"
y="214"
width="280"
height="103"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#ffffff"
id="rect2543" />
<text
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="20px"
transform="translate(125.451,239)"
id="text2551">Velocity buffer<tspan
font-size="14px"
x="-40.5"
y="18"
id="tspan2545">APPLICATION SPECIFIED (2x FLOAT)</tspan><tspan
font-size="14px"
x="19.129999"
y="52"
id="tspan2547">Current frame</tspan><tspan
font-size="14px"
x="8.5000095"
y="69"
id="tspan2549">Render resolution</tspan></text>
<path
d="m 325,145 h 51.277 v 121.258 l -3,-3 h 33.277 v 6 H 370.277 V 148 l 3,3 H 325 Z m 78.554,112.258 18,9 -18,9 z"
id="path2553" />
<rect
x="45"
y="332"
width="280"
height="104"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#ffffff"
id="rect2555" />
<text
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="20px"
transform="translate(136.331,357)"
id="text2563">Color buffer<tspan
font-size="14px"
x="-19.129999"
y="18"
id="tspan2557">APPLICATION SPECIFIED</tspan><tspan
font-size="14px"
x="8.25"
y="52"
id="tspan2559">Current frame</tspan><tspan
font-size="14px"
x="-2.3800001"
y="69"
id="tspan2561">Render resolution</tspan></text>
<path
d="m 0,-3 h 51.2771 v 121.296 l -3,-3 H 81.554 v 6 H 45.2771 V 0 l 3,3 H 0 Z m 78.554,112.296 18,9 -18,9 z"
transform="matrix(1,0,0,-1,325,384.296)"
id="path2565" />
<rect
x="475"
y="302"
width="251"
height="98"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#ffffff"
id="rect2567" />
<text
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="20px"
transform="translate(543.899,324)"
id="text2577">Output<tspan
font-size="20px"
x="62.75"
y="0"
id="tspan2569">buffer</tspan><tspan
font-size="14px"
x="-9.25"
y="18"
id="tspan2571">R16G16B16A16_FLOAT</tspan><tspan
font-size="14px"
x="13.12"
y="52"
id="tspan2573">Previous frame</tspan><tspan
font-size="14px"
x="-11"
y="69"
id="tspan2575">Presentation resolution</tspan></text>
<path
d="m 325,263 h 81.554 v 6 H 325 Z m 78.554,-6 18,9 -18,9 z"
id="path2579" />
<rect
x="475"
y="244"
width="251"
height="40"
fill="#ffffff"
id="rect2581" />
<path
d="M 477,353.666 H 438 V 261 h 22 v 6 h -19 l 3,-3 v 86.666 l -3,-3 h 36 z M 457,255 l 18,9 -18,9 z"
fill="#ffffff"
id="path2583" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.0 KiB

@ -0,0 +1,162 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="306.12292mm"
height="47.95702mm"
viewBox="0 0 306.12292 47.95702"
version="1.1"
id="svg2384"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs2381">
<clipPath
id="clip0">
<rect
x="97"
y="180"
width="1157"
height="195"
id="rect2388" />
</clipPath>
</defs>
<g
id="layer1"
transform="translate(323.50476,-98.835922)">
<g
clip-path="url(#clip0)"
transform="matrix(0.26458333,0,0,0.26458333,-349.16934,47.574194)"
id="g2437">
<rect
x="341"
y="271"
width="182"
height="103"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#ffffff"
id="rect2393" />
<text
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="20px"
transform="translate(369.29,317)"
id="text2397">Post Processing<tspan
font-size="20px"
x="57.25"
y="24"
id="tspan2395">A</tspan></text>
<rect
x="98"
y="271"
width="182"
height="103"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#ffffff"
id="rect2399" />
<text
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="20px"
transform="translate(147.588,329)"
id="text2401">Rendering</text>
<rect
x="584"
y="271"
width="182"
height="103"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
id="rect2403" />
<text
fill="#ffffff"
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="20px"
transform="translate(635.986,305)"
id="text2409">FidelityFX <tspan
font-size="20px"
x="-29.620001"
y="24"
id="tspan2405">Super Resolution</tspan><tspan
font-size="20px"
x="-5.5"
y="48"
id="tspan2407">(Temporal)</tspan></text>
<path
d="m 766.019,319 46.042,0.291 -0.038,5.999 -46.042,-0.29 z m 43.08,-5.728 17.943,9.113 -18.057,8.886 z"
id="path2411" />
<path
d="m 523,319 h 46.042 v 6 H 523 Z m 43.042,-6 18,9 -18,9 z"
id="path2413" />
<rect
x="1071"
y="271"
width="182"
height="103"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#ffffff"
id="rect2415" />
<text
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="20px"
transform="translate(1142.77,329)"
id="text2417">HUD</text>
<path
d="M 0.0189386,-2.99994 46.0608,-2.70928 46.0229,3.2906 -0.0189386,2.99994 Z M 43.0988,-8.7281 61.0416,0.385354 42.9851,9.27154 Z"
transform="matrix(1,0,0,-1,1009,322.385)"
id="path2419" />
<text
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="22px"
transform="translate(440.437,208)"
id="text2421">Linear</text>
<path
d="m 468.82,221.116 84.672,70.715 -0.641,0.768 -84.671,-70.715 z m 86.663,65.212 5.364,12.297 -13.056,-3.087 z"
id="path2423" />
<text
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="22px"
transform="translate(768.995,208)"
id="text2425">Linear</text>
<path
d="M 0.498061,-0.0439896 6.43008,67.1199 5.43396,67.2078 -0.498061,0.0439896 Z M 11.7328,64.6437 6.81181,77.125 -0.220673,65.6994 Z"
transform="matrix(-1,0,0,1,797.312,221.5)"
id="path2427" />
<path
d="M 7.73966e-6,-3 46.0416,-2.99988 v 6 L -7.73966e-6,3 Z M 43.0416,-8.99989 61.0416,1.5748e-4 43.0415,9.00011 Z"
transform="matrix(1,0,0,-1,280,322)"
id="path2429" />
<rect
x="827"
y="271"
width="182"
height="103"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#ffffff"
id="rect2431" />
<text
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="20px"
transform="translate(855.442,317)"
id="text2435">Post Processing<tspan
font-size="20px"
x="57.5"
y="24"
id="tspan2433">B</tspan></text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.7 KiB

@ -0,0 +1,407 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="166.44464mm"
height="87.921509mm"
viewBox="0 0 166.44464 87.921509"
version="1.1"
id="svg1301"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1298">
<clipPath
id="clip0">
<rect
x="331"
y="274"
width="649"
height="356"
id="rect1305" />
</clipPath>
</defs>
<g
id="layer1"
transform="translate(229.51097,-41.090793)">
<g
clip-path="url(#clip0)"
transform="matrix(0.26458333,0,0,0.26458333,-319.71746,-35.880218)"
id="g1456">
<rect
x="445"
y="380"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#7f7f7f"
id="rect1310" />
<rect
x="393"
y="380"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect1312" />
<rect
x="445"
y="429"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#7f7f7f"
id="rect1314" />
<rect
x="393"
y="429"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#7f7f7f"
id="rect1316" />
<rect
x="445"
y="481"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#7f7f7f"
id="rect1318" />
<rect
x="393"
y="481"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect1320" />
<path
d="m 411,455.5 c 0,-4.142 3.582,-7.5 8,-7.5 4.418,0 8,3.358 8,7.5 0,4.142 -3.582,7.5 -8,7.5 -4.418,0 -8,-3.358 -8,-7.5 z"
fill-rule="evenodd"
id="path1322" />
<path
d="m 411,404.5 c 0,-4.142 3.582,-7.5 8,-7.5 4.418,0 8,3.358 8,7.5 0,4.142 -3.582,7.5 -8,7.5 -4.418,0 -8,-3.358 -8,-7.5 z"
fill-rule="evenodd"
id="path1324" />
<path
d="m 463,404.5 c 0,-4.142 3.582,-7.5 8,-7.5 4.418,0 8,3.358 8,7.5 0,4.142 -3.582,7.5 -8,7.5 -4.418,0 -8,-3.358 -8,-7.5 z"
fill-rule="evenodd"
id="path1326" />
<path
d="m 463,456.5 c 0,-4.142 3.582,-7.5 8,-7.5 4.418,0 8,3.358 8,7.5 0,4.142 -3.582,7.5 -8,7.5 -4.418,0 -8,-3.358 -8,-7.5 z"
fill-rule="evenodd"
id="path1328" />
<path
d="m 463,507 c 0,-4.418 3.582,-8 8,-8 4.418,0 8,3.582 8,8 0,4.418 -3.582,8 -8,8 -4.418,0 -8,-3.582 -8,-8 z"
fill-rule="evenodd"
id="path1330" />
<path
d="m 411,507.5 c 0,-4.142 3.358,-7.5 7.5,-7.5 4.142,0 7.5,3.358 7.5,7.5 0,4.142 -3.358,7.5 -7.5,7.5 -4.142,0 -7.5,-3.358 -7.5,-7.5 z"
fill-rule="evenodd"
id="path1332" />
<rect
x="497"
y="380"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect1334" />
<rect
x="497"
y="429"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#7f7f7f"
id="rect1336" />
<rect
x="497"
y="481"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect1338" />
<path
d="m 515,404.5 c 0,-4.142 3.582,-7.5 8,-7.5 4.418,0 8,3.358 8,7.5 0,4.142 -3.582,7.5 -8,7.5 -4.418,0 -8,-3.358 -8,-7.5 z"
fill-rule="evenodd"
id="path1340" />
<path
d="m 515,456.5 c 0,-4.142 3.582,-7.5 8,-7.5 4.418,0 8,3.358 8,7.5 0,4.142 -3.582,7.5 -8,7.5 -4.418,0 -8,-3.358 -8,-7.5 z"
fill-rule="evenodd"
id="path1342" />
<path
d="m 515,507 c 0,-4.418 3.582,-8 8,-8 4.418,0 8,3.582 8,8 0,4.418 -3.582,8 -8,8 -4.418,0 -8,-3.582 -8,-8 z"
fill-rule="evenodd"
id="path1344" />
<path
d="M 0,0 46.5346,78.166"
stroke="#000000"
stroke-miterlimit="8"
fill="none"
fill-rule="evenodd"
transform="matrix(-1,0,0,1,526.035,317.5)"
id="path1346" />
<path
d="M 0,0 46.5346,78.166"
stroke="#000000"
stroke-miterlimit="8"
fill="none"
fill-rule="evenodd"
transform="matrix(-1,0,0,1,578.035,370.5)"
id="path1348" />
<path
d="m 476.5,515.5 51.853,78.774"
stroke="#000000"
stroke-miterlimit="8"
fill="none"
fill-rule="evenodd"
id="path1350" />
<path
d="M 0,0 46.5346,78.166"
stroke="#000000"
stroke-miterlimit="8"
fill="none"
fill-rule="evenodd"
transform="matrix(-1,0,0,1,411.035,463.5)"
id="path1352" />
<text
font-family="'Cambria Math', 'Cambria Math_MSFontService', sans-serif"
font-weight="400"
font-size="36px"
transform="translate(527.385,308)"
id="text1372">𝑠<tspan
font-size="26px"
x="15.17"
y="7"
id="tspan1354">𝑛</tspan><tspan
font-size="36px"
x="58.447899"
y="52"
id="tspan1356">𝑠</tspan><tspan
font-size="26px"
x="73.617996"
y="60"
id="tspan1358">𝑤</tspan><tspan
font-size="36px"
x="4.1111498"
y="308"
id="tspan1360">𝑠</tspan><tspan
font-size="26px"
x="19.281099"
y="315"
id="tspan1362">𝑠</tspan><tspan
font-size="36px"
x="-187.326"
y="253"
id="tspan1364">𝑠</tspan><tspan
font-size="26px"
x="-172.15601"
y="261"
id="tspan1366">𝑒</tspan><tspan
font-size="36px"
x="-169.914"
y="36"
id="tspan1368">𝑠</tspan><tspan
font-size="26px"
x="-154.744"
y="43"
id="tspan1370">𝑚</tspan></text>
<path
d="m 406.5,353.5 56.458,95.419"
stroke="#000000"
stroke-miterlimit="8"
fill="none"
fill-rule="evenodd"
id="path1374" />
<text
font-family="'Cambria Math', 'Cambria Math_MSFontService', sans-serif"
font-weight="400"
font-size="28px"
transform="translate(648.911,399)"
id="text1384">𝑤<tspan
font-size="20px"
x="19.82"
y="6"
id="tspan1376">0</tspan><tspan
font-size="28px"
x="40.509998"
y="0"
id="tspan1378">=</tspan><tspan
font-size="28px"
x="156.74001"
y="-21"
id="tspan1380"></tspan>𝑠<tspan
font-size="20px"
x="189.50999"
y="-16"
id="tspan1382">𝑚</tspan></text>
<path
d="m 935.659,396.712 c 2.662,0.702 4.706,2.217 6.132,4.546 1.427,2.329 2.14,5.138 2.14,8.429 0,3.299 -0.711,6.109 -2.133,8.428 -1.422,2.32 -3.468,3.831 -6.139,4.533 l -0.328,-1.053 c 2.106,-0.702 3.671,-2.087 4.696,-4.156 1.026,-2.069 1.539,-4.699 1.539,-7.889 0,-3.081 -0.511,-5.64 -1.532,-7.677 -1.021,-2.037 -2.602,-3.406 -4.744,-4.108 z m -206.726,0 0.369,1.053 c -2.133,0.702 -3.712,2.071 -4.737,4.108 -1.026,2.037 -1.538,4.596 -1.538,7.677 0,3.19 0.512,5.82 1.538,7.889 1.025,2.069 2.591,3.454 4.696,4.156 l -0.328,1.053 c -2.671,-0.702 -4.717,-2.213 -6.139,-4.533 -1.422,-2.319 -2.132,-5.129 -2.132,-8.428 0,-3.291 0.713,-6.1 2.139,-8.429 1.427,-2.329 3.471,-3.844 6.132,-4.546 z"
fill-rule="evenodd"
id="path1386" />
<text
font-family="'Cambria Math', 'Cambria Math_MSFontService', sans-serif"
font-weight="400"
font-size="28px"
transform="translate(729.821,418)"
id="text1408">𝑠<tspan
font-size="20px"
x="11.77"
y="5"
id="tspan1388">𝑛</tspan><tspan
font-size="28px"
x="32.75"
y="0"
id="tspan1390">+</tspan><tspan
font-size="28px"
x="59.970001"
y="0"
id="tspan1392">𝑠</tspan><tspan
font-size="20px"
x="71.739998"
y="5"
id="tspan1394">𝑒</tspan><tspan
font-size="28px"
x="90.739998"
y="0"
id="tspan1396">+</tspan><tspan
font-size="28px"
x="117.96"
y="0"
id="tspan1398">𝑠</tspan><tspan
font-size="20px"
x="129.73"
y="5"
id="tspan1400">𝑠</tspan><tspan
font-size="28px"
x="147.53"
y="0"
id="tspan1402">+</tspan><tspan
font-size="28px"
x="174.75"
y="0"
id="tspan1404">𝑠</tspan><tspan
font-size="20px"
x="186.52"
y="5"
id="tspan1406">𝑤</tspan></text>
<path
d="m 718.401,389.948 h 76 76 76 v 2 h -76 -76 -76 z"
fill-rule="evenodd"
id="path1410" />
<text
font-family="'Cambria Math', 'Cambria Math_MSFontService', sans-serif"
font-weight="400"
font-size="28px"
transform="translate(624.76,507)"
id="text1424">𝑤<tspan
font-size="20px"
x="19.17"
y="6"
id="tspan1412">1</tspan><tspan
font-size="28px"
x="39.860001"
y="0"
id="tspan1414">=</tspan><tspan
font-size="28px"
x="166.59"
y="-21"
id="tspan1416">1</tspan><tspan
font-size="28px"
x="188.31"
y="-21"
id="tspan1418"></tspan><tspan
font-size="28px"
x="215.53"
y="-21"
id="tspan1420">𝑠</tspan><tspan
font-size="20px"
x="227.3"
y="-15"
id="tspan1422">𝑚</tspan></text>
<path
d="m 910.859,505.127 c 2.661,0.701 4.705,2.217 6.131,4.546 1.427,2.328 2.14,5.138 2.14,8.428 0,3.3 -0.711,6.109 -2.133,8.429 -1.422,2.32 -3.468,3.83 -6.138,4.532 l -0.329,-1.053 c 2.106,-0.701 3.671,-2.087 4.697,-4.156 1.025,-2.069 1.538,-4.698 1.538,-7.888 0,-3.081 -0.511,-5.64 -1.531,-7.677 -1.021,-2.037 -2.603,-3.407 -4.745,-4.109 z m -206.727,0 0.369,1.052 c -2.132,0.702 -3.712,2.072 -4.737,4.109 -1.025,2.037 -1.538,4.596 -1.538,7.677 0,3.19 0.513,5.819 1.538,7.888 1.025,2.069 2.591,3.455 4.696,4.156 l -0.328,1.053 c -2.67,-0.702 -4.717,-2.212 -6.138,-4.532 -1.422,-2.32 -2.133,-5.129 -2.133,-8.429 0,-3.29 0.713,-6.1 2.139,-8.428 1.427,-2.329 3.471,-3.845 6.132,-4.546 z"
fill-rule="evenodd"
id="path1426" />
<text
font-family="'Cambria Math', 'Cambria Math_MSFontService', sans-serif"
font-weight="400"
font-size="28px"
transform="translate(705.02,526)"
id="text1452">𝑠<tspan
font-size="20px"
x="11.77"
y="6"
id="tspan1428">𝑛</tspan><tspan
font-size="28px"
x="32.75"
y="0"
id="tspan1430">+</tspan><tspan
font-size="28px"
x="59.970001"
y="0"
id="tspan1432">𝑠</tspan><tspan
font-size="20px"
x="71.739998"
y="6"
id="tspan1434">𝑒</tspan><tspan
font-size="28px"
x="90.739998"
y="0"
id="tspan1436">+</tspan><tspan
font-size="28px"
x="117.96"
y="0"
id="tspan1438">𝑠</tspan><tspan
font-size="20px"
x="129.73"
y="6"
id="tspan1440">𝑠</tspan><tspan
font-size="28px"
x="147.53"
y="0"
id="tspan1442">+</tspan><tspan
font-size="28px"
x="174.75"
y="0"
id="tspan1444">𝑠</tspan><tspan
font-size="20px"
x="186.52"
y="6"
id="tspan1446">𝑤</tspan><tspan
font-size="28px"
x="222.78999"
y="0"
id="tspan1448"></tspan><tspan
font-size="28px"
x="250.00999"
y="0"
id="tspan1450">4</tspan></text>
<path
d="m 693.02,498.363 h 69.25 69.25 69.25 69.25 v 2 h -69.25 -69.25 -69.25 -69.25 z"
fill-rule="evenodd"
id="path1454" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 12 KiB

@ -0,0 +1,512 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="124.88333mm"
height="55.033333mm"
viewBox="0 0 124.88333 55.033333"
version="1.1"
id="svg3815"
inkscape:version="1.1.2 (b8e25be833, 2022-02-05)"
sodipodi:docname="reconstruct-previous-depth.png.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview3817"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="0.77771465"
inkscape:cx="1333.3939"
inkscape:cy="348.45686"
inkscape:window-width="3699"
inkscape:window-height="2126"
inkscape:window-x="130"
inkscape:window-y="-11"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs3812">
<clipPath
id="clip0">
<rect
x="1002"
y="95"
width="472"
height="208"
id="rect3819" />
</clipPath>
</defs>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(247.51383,-56.174055)">
<g
clip-path="url(#clip0)"
transform="matrix(0.26458333,0,0,0.26458333,-512.62633,31.038639)"
id="g3956">
<rect
x="1057"
y="96"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect3824" />
<rect
x="1005"
y="96"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect3826" />
<rect
x="1005"
y="145"
width="52"
height="51"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect3828" />
<rect
x="1057"
y="196"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect3830" />
<rect
x="1005"
y="196"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect3832" />
<path
d="m 1023,120 c 0,-4.418 3.36,-8 7.5,-8 4.14,0 7.5,3.582 7.5,8 0,4.418 -3.36,8 -7.5,8 -4.14,0 -7.5,-3.582 -7.5,-8 z"
fill-rule="evenodd"
id="path3834" />
<path
d="m 1075,120 c 0,-4.418 3.36,-8 7.5,-8 4.14,0 7.5,3.582 7.5,8 0,4.418 -3.36,8 -7.5,8 -4.14,0 -7.5,-3.582 -7.5,-8 z"
fill-rule="evenodd"
id="path3836" />
<path
d="m 1075,222.5 c 0,-4.142 3.36,-7.5 7.5,-7.5 4.14,0 7.5,3.358 7.5,7.5 0,4.142 -3.36,7.5 -7.5,7.5 -4.14,0 -7.5,-3.358 -7.5,-7.5 z"
fill-rule="evenodd"
id="path3838" />
<path
d="m 1022,223 c 0,-4.418 3.58,-8 8,-8 4.42,0 8,3.582 8,8 0,4.418 -3.58,8 -8,8 -4.42,0 -8,-3.582 -8,-8 z"
fill-rule="evenodd"
id="path3840" />
<rect
x="1109"
y="96"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect3842" />
<rect
x="1109"
y="145"
width="52"
height="51"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect3844" />
<rect
x="1109"
y="196"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect3846" />
<path
d="m 1127,120 c 0,-4.418 3.36,-8 7.5,-8 4.14,0 7.5,3.582 7.5,8 0,4.418 -3.36,8 -7.5,8 -4.14,0 -7.5,-3.582 -7.5,-8 z"
fill-rule="evenodd"
id="path3848" />
<path
d="m 1127,172 c 0,-4.418 3.58,-8 8,-8 4.42,0 8,3.582 8,8 0,4.418 -3.58,8 -8,8 -4.42,0 -8,-3.582 -8,-8 z"
fill-rule="evenodd"
id="path3850" />
<path
d="m 1127,222.5 c 0,-4.142 3.36,-7.5 7.5,-7.5 4.14,0 7.5,3.358 7.5,7.5 0,4.142 -3.36,7.5 -7.5,7.5 -4.14,0 -7.5,-3.358 -7.5,-7.5 z"
fill-rule="evenodd"
id="path3852" />
<rect
x="1161"
y="96"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect3854" />
<rect
x="1161"
y="145"
width="52"
height="51"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect3856" />
<rect
x="1161"
y="196"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect3858" />
<path
d="m 1179,120 c 0,-4.418 3.36,-8 7.5,-8 4.14,0 7.5,3.582 7.5,8 0,4.418 -3.36,8 -7.5,8 -4.14,0 -7.5,-3.582 -7.5,-8 z"
fill-rule="evenodd"
id="path3860" />
<path
d="m 1179,172 c 0,-4.418 3.58,-8 8,-8 4.42,0 8,3.582 8,8 0,4.418 -3.58,8 -8,8 -4.42,0 -8,-3.582 -8,-8 z"
fill-rule="evenodd"
id="path3862" />
<path
d="m 1179,222.5 c 0,-4.142 3.36,-7.5 7.5,-7.5 4.14,0 7.5,3.358 7.5,7.5 0,4.142 -3.36,7.5 -7.5,7.5 -4.14,0 -7.5,-3.358 -7.5,-7.5 z"
fill-rule="evenodd"
id="path3864" />
<rect
x="1057"
y="248"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect3866" />
<path
d="m 1075,272.5 c 0,-4.142 3.36,-7.5 7.5,-7.5 4.14,0 7.5,3.358 7.5,7.5 0,4.142 -3.36,7.5 -7.5,7.5 -4.14,0 -7.5,-3.358 -7.5,-7.5 z"
fill-rule="evenodd"
id="path3868" />
<rect
x="1109"
y="248"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect3870" />
<path
d="m 1127,272.5 c 0,-4.142 3.36,-7.5 7.5,-7.5 4.14,0 7.5,3.358 7.5,7.5 0,4.142 -3.36,7.5 -7.5,7.5 -4.14,0 -7.5,-3.358 -7.5,-7.5 z"
fill-rule="evenodd"
id="path3872" />
<rect
x="1161"
y="248"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect3874" />
<path
d="m 1179,272.5 c 0,-4.142 3.36,-7.5 7.5,-7.5 4.14,0 7.5,3.358 7.5,7.5 0,4.142 -3.36,7.5 -7.5,7.5 -4.14,0 -7.5,-3.358 -7.5,-7.5 z"
fill-rule="evenodd"
id="path3876" />
<rect
x="1317"
y="96"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect3878" />
<rect
x="1265"
y="96"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect3880" />
<rect
x="1317"
y="145"
width="52"
height="51"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect3882" />
<rect
x="1265"
y="145"
width="52"
height="51"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect3884" />
<rect
x="1317"
y="196"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect3886" />
<rect
x="1265"
y="196"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect3888" />
<path
d="m 1283,171 c 0,-4.418 3.36,-8 7.5,-8 4.14,0 7.5,3.582 7.5,8 0,4.418 -3.36,8 -7.5,8 -4.14,0 -7.5,-3.582 -7.5,-8 z"
fill-rule="evenodd"
id="path3890" />
<path
d="m 1283,120 c 0,-4.418 3.36,-8 7.5,-8 4.14,0 7.5,3.582 7.5,8 0,4.418 -3.36,8 -7.5,8 -4.14,0 -7.5,-3.582 -7.5,-8 z"
fill-rule="evenodd"
id="path3892" />
<path
d="m 1335,120 c 0,-4.418 3.36,-8 7.5,-8 4.14,0 7.5,3.582 7.5,8 0,4.418 -3.36,8 -7.5,8 -4.14,0 -7.5,-3.582 -7.5,-8 z"
fill-rule="evenodd"
id="path3894" />
<path
d="m 1335,172 c 0,-4.418 3.36,-8 7.5,-8 4.14,0 7.5,3.582 7.5,8 0,4.418 -3.36,8 -7.5,8 -4.14,0 -7.5,-3.582 -7.5,-8 z"
fill-rule="evenodd"
id="path3896" />
<path
d="m 1335,222.5 c 0,-4.142 3.36,-7.5 7.5,-7.5 4.14,0 7.5,3.358 7.5,7.5 0,4.142 -3.36,7.5 -7.5,7.5 -4.14,0 -7.5,-3.358 -7.5,-7.5 z"
fill-rule="evenodd"
id="path3898" />
<path
d="m 1282,223 c 0,-4.418 3.58,-8 8,-8 4.42,0 8,3.582 8,8 0,4.418 -3.58,8 -8,8 -4.42,0 -8,-3.582 -8,-8 z"
fill-rule="evenodd"
id="path3900" />
<rect
x="1369"
y="96"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect3902" />
<rect
x="1369"
y="145"
width="52"
height="51"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect3904" />
<rect
x="1369"
y="196"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect3906" />
<path
d="m 1387,120 c 0,-4.418 3.36,-8 7.5,-8 4.14,0 7.5,3.582 7.5,8 0,4.418 -3.36,8 -7.5,8 -4.14,0 -7.5,-3.582 -7.5,-8 z"
fill-rule="evenodd"
id="path3908" />
<path
d="m 1387,172 c 0,-4.418 3.36,-8 7.5,-8 4.14,0 7.5,3.582 7.5,8 0,4.418 -3.36,8 -7.5,8 -4.14,0 -7.5,-3.582 -7.5,-8 z"
fill-rule="evenodd"
id="path3910" />
<path
d="m 1387,222.5 c 0,-4.142 3.36,-7.5 7.5,-7.5 4.14,0 7.5,3.358 7.5,7.5 0,4.142 -3.36,7.5 -7.5,7.5 -4.14,0 -7.5,-3.358 -7.5,-7.5 z"
fill-rule="evenodd"
id="path3912" />
<rect
x="1421"
y="96"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect3914" />
<rect
x="1421"
y="145"
width="52"
height="51"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect3916" />
<rect
x="1421"
y="196"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect3918" />
<path
d="m 1439,120 c 0,-4.418 3.36,-8 7.5,-8 4.14,0 7.5,3.582 7.5,8 0,4.418 -3.36,8 -7.5,8 -4.14,0 -7.5,-3.582 -7.5,-8 z"
fill-rule="evenodd"
id="path3920" />
<path
d="m 1439,172 c 0,-4.418 3.36,-8 7.5,-8 4.14,0 7.5,3.582 7.5,8 0,4.418 -3.36,8 -7.5,8 -4.14,0 -7.5,-3.582 -7.5,-8 z"
fill-rule="evenodd"
id="path3922" />
<path
d="m 1439,222.5 c 0,-4.142 3.36,-7.5 7.5,-7.5 4.14,0 7.5,3.358 7.5,7.5 0,4.142 -3.36,7.5 -7.5,7.5 -4.14,0 -7.5,-3.358 -7.5,-7.5 z"
fill-rule="evenodd"
id="path3924" />
<rect
x="1265"
y="248"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect3926" />
<path
d="m 1283,272.5 c 0,-4.142 3.36,-7.5 7.5,-7.5 4.14,0 7.5,3.358 7.5,7.5 0,4.142 -3.36,7.5 -7.5,7.5 -4.14,0 -7.5,-3.358 -7.5,-7.5 z"
fill-rule="evenodd"
id="path3928" />
<rect
x="1369"
y="248"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect3930" />
<path
d="m 1387,272.5 c 0,-4.142 3.36,-7.5 7.5,-7.5 4.14,0 7.5,3.358 7.5,7.5 0,4.142 -3.36,7.5 -7.5,7.5 -4.14,0 -7.5,-3.358 -7.5,-7.5 z"
fill-rule="evenodd"
id="path3932" />
<rect
x="1421"
y="248"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect3934" />
<path
d="m 1439,272.5 c 0,-4.142 3.36,-7.5 7.5,-7.5 4.14,0 7.5,3.358 7.5,7.5 0,4.142 -3.36,7.5 -7.5,7.5 -4.14,0 -7.5,-3.358 -7.5,-7.5 z"
fill-rule="evenodd"
id="path3936" />
<rect
x="1057"
y="145"
width="52"
height="51"
stroke="#000000"
stroke-width="6"
stroke-miterlimit="8"
fill="#7f7f7f"
id="rect3938" />
<rect
x="1005"
y="248"
width="52"
height="52"
stroke="#000000"
stroke-width="6"
stroke-miterlimit="8"
fill="#7f7f7f"
id="rect3940" />
<path
d="m 1023,171 c 0,-4.418 3.36,-8 7.5,-8 4.14,0 7.5,3.582 7.5,8 0,4.418 -3.36,8 -7.5,8 -4.14,0 -7.5,-3.582 -7.5,-8 z"
fill-rule="evenodd"
id="path3942" />
<path
d="m 1075,172 c 0,-4.418 3.58,-8 8,-8 4.42,0 8,3.582 8,8 0,4.418 -3.58,8 -8,8 -4.42,0 -8,-3.582 -8,-8 z"
fill-rule="evenodd"
id="path3944" />
<path
d="m 1023,272.5 c 0,-4.142 3.36,-7.5 7.5,-7.5 4.14,0 7.5,3.358 7.5,7.5 0,4.142 -3.36,7.5 -7.5,7.5 -4.14,0 -7.5,-3.358 -7.5,-7.5 z"
fill-rule="evenodd"
id="path3946" />
<rect
x="1317"
y="248"
width="52"
height="52"
stroke="#000000"
stroke-width="6"
stroke-miterlimit="8"
fill="#7f7f7f"
id="rect3948" />
<path
d="m 1335,272.5 c 0,-4.142 3.36,-7.5 7.5,-7.5 4.14,0 7.5,3.358 7.5,7.5 0,4.142 -3.36,7.5 -7.5,7.5 -4.14,0 -7.5,-3.358 -7.5,-7.5 z"
fill-rule="evenodd"
id="path3950" />
<path
d="m 1076.01,171.09 256.5,93.074 -1.02,2.82 -256.5,-93.074 z m 256.15,88.162 5.48,8.369 -9.57,2.911 z"
id="path3952" />
<path
d="m 1023.5,272 h 305.94 v 3 H 1023.5 Z m 303.94,-4.5 8,6 -8,6 z"
id="path3954" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 15 KiB

@ -0,0 +1,677 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="438.67917mm"
height="228.07083mm"
viewBox="0 0 438.67917 228.07083"
version="1.1"
id="svg1944"
inkscape:version="1.1.2 (b8e25be833, 2022-02-05)"
sodipodi:docname="reproject-and-accumulate-structure.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1946"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="0.77771465"
inkscape:cx="2185.8917"
inkscape:cy="1044.0847"
inkscape:window-width="3699"
inkscape:window-height="2126"
inkscape:window-x="130"
inkscape:window-y="-11"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs1941">
<clipPath
id="clip0">
<rect
x="167"
y="101"
width="1658"
height="870"
id="rect1948" />
</clipPath>
</defs>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(473.13339,127.98387)">
<g
clip-path="url(#clip0)"
transform="matrix(0.26458333,0,0,0.26458333,-517.31881,-154.70679)"
id="g2159">
<rect
x="300"
y="479"
width="16"
height="19"
id="rect1953" />
<path
d="m 0,-3 h 159.594 v 61.457 h -6 V 0 l 3,3 H 0 Z m 165.594,58.457 -9,18 -9,-18 z"
transform="matrix(1,0,0,-1,316,444.457)"
id="path1955" />
<rect
x="300"
y="435"
width="16"
height="18"
id="rect1957" />
<rect
x="892"
y="560"
width="21"
height="20"
stroke="#2f528f"
stroke-width="2"
stroke-miterlimit="8"
fill="#4472c4"
id="rect1959" />
<rect
x="776"
y="560"
width="21"
height="21"
stroke="#2f528f"
stroke-width="2"
stroke-miterlimit="8"
fill="#4472c4"
id="rect1961" />
<rect
x="967"
y="611"
width="20"
height="20"
stroke="#2f528f"
stroke-width="2"
stroke-miterlimit="8"
fill="#4472c4"
id="rect1963" />
<rect
x="1075"
y="611"
width="20"
height="20"
stroke="#2f528f"
stroke-width="2"
stroke-miterlimit="8"
fill="#4472c4"
id="rect1965" />
<rect
x="965"
y="560"
width="20"
height="21"
stroke="#2f528f"
stroke-width="2"
stroke-miterlimit="8"
fill="#4472c4"
id="rect1967" />
<rect
x="1074"
y="560"
width="20"
height="20"
stroke="#2f528f"
stroke-width="2"
stroke-miterlimit="8"
fill="#4472c4"
id="rect1969" />
<rect
x="787"
y="560"
width="16"
height="18"
id="rect1971" />
<rect
x="1495"
y="835"
width="61"
height="31"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
id="rect1973" />
<text
fill="#ffffff"
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="16px"
transform="translate(1511.06,856)"
id="text1975">Pass</text>
<rect
x="1567"
y="835"
width="61"
height="31"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#ffffff"
id="rect1977" />
<text
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="12px"
transform="translate(1582.31,854)"
id="text1979">Buffer</text>
<rect
x="399"
y="299"
width="148"
height="72"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
id="rect1981" />
<text
fill="#ffffff"
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="20px"
transform="translate(440.758,318)"
id="text1987">Shading <tspan
font-size="20px"
x="2.8699999"
y="24"
id="tspan1983">change</tspan><tspan
font-size="20px"
x="-6.8800001"
y="48"
id="tspan1985">detection</tspan></text>
<rect
x="771"
y="560"
width="147"
height="71"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
id="rect1989" />
<text
fill="#ffffff"
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="20px"
transform="translate(790.107,603)"
id="text1991">Update Locks</text>
<rect
x="168"
y="430"
width="147"
height="72"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#ffffff"
id="rect1993" />
<text
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="20px"
transform="translate(196.474,473)"
id="text1995">Lock status</text>
<rect
x="168"
y="807"
width="147"
height="71"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#ffffff"
id="rect1997" />
<text
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="20px"
transform="translate(191.349,838)"
id="text2001">Disocclusion <tspan
font-size="20px"
x="29"
y="24"
id="tspan1999">mask</tspan></text>
<path
d="m 0,-3 h 532.183 v 199.427 h -6 V 0 l 3,3 H 0 Z m 538.183,196.427 -9,18 -9,-18 z"
transform="matrix(1,0,0,-1,315,842.427)"
id="path2003" />
<rect
x="956"
y="560"
width="148"
height="71"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
id="rect2005" />
<text
fill="#ffffff"
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="20px"
transform="translate(979.694,603)"
id="text2007">Rectify color</text>
<path
d="m 0,-3 h 772.861 v 199.801 h -6 V 0 l 3,3 H 0 Z m 778.861,196.801 -9,18 -9,-18 z"
transform="matrix(1,0,0,-1,315,842.801)"
id="path2009" />
<rect
x="1318"
y="560"
width="147"
height="72"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
id="rect2011" />
<text
fill="#ffffff"
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="20px"
transform="translate(1343.48,603)"
id="text2013">Accumulate</text>
<rect
x="1318"
y="429"
width="147"
height="71"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
id="rect2015" />
<text
fill="#ffffff"
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="20px"
transform="translate(1353.23,472)"
id="text2017">Tonemap</text>
<rect
x="1134"
y="560"
width="148"
height="71"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
id="rect2019" />
<text
fill="#ffffff"
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="20px"
transform="translate(1169.49,603)"
id="text2021">Tonemap</text>
<rect
x="1498"
y="560"
width="146"
height="72"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
id="rect2023" />
<text
fill="#ffffff"
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="20px"
transform="translate(1541.26,591)"
id="text2027">Inverse <tspan
font-size="20px"
x="-7.25"
y="24"
id="tspan2025">tonemap</tspan></text>
<path
d="m 918,593 h 23.061 v 6 H 918 Z m 20.061,-6 18,9 -18,9 z"
id="path2029" />
<path
d="m 1104,593 h 15.02 v 6 H 1104 Z m 12.02,-6 18,9 -18,9 z"
id="path2031" />
<path
d="M 1.44131e-5,-3 17.7786,-2.99991 v 6 L -1.44131e-5,3 Z M 14.7786,-8.99993 32.7786,1.5748e-4 14.7785,9.00007 Z"
transform="matrix(1,0,0,-1,1465,596)"
id="path2033" />
<path
d="m 1282.04,593 21.09,0.259 -0.08,6 -21.09,-0.259 z m 18.16,-5.777 17.89,9.22 -18.11,8.778 z"
id="path2035" />
<path
d="m 1395,500 v 44.932 h -6 V 500 Z m 6,41.932 -9,18 -9,-18 z"
id="path2037" />
<rect
x="1006"
y="195"
width="148"
height="72"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
id="rect2039" />
<text
fill="#ffffff"
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="20px"
transform="translate(1039.53,238)"
id="text2041">Upsample</text>
<rect
x="169"
y="254"
width="147"
height="71"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#ffffff"
id="rect2043" />
<text
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="20px"
transform="translate(189.971,296)"
id="text2045">Luma history</text>
<rect
x="168"
y="342"
width="147"
height="72"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#ffffff"
id="rect2047" />
<text
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="20px"
transform="translate(210.224,373)"
id="text2051">Current <tspan
font-size="20px"
x="-11"
y="24"
id="tspan2049">luminance</tspan></text>
<rect
x="168"
y="102"
width="147"
height="72"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#ffffff"
id="rect2053" />
<text
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="20px"
transform="translate(182.599,145)"
id="text2057">Adjusted <tspan
font-size="20px"
x="76.75"
y="0"
id="tspan2055">color</tspan></text>
<path
d="m 316,286 h 44.378 v 48.587 l -3,-3 h 26.377 v 6 H 354.378 V 289 l 3,3 H 316 Z m 64.755,39.587 18,9 -18,9 z"
id="path2059" />
<path
d="m 0,-3 h 44.8764 v 45.9532 l -3,-3 h 26.8764 v 6 H 38.8764 V 0 l 3,3 H 0 Z m 65.7528,36.9532 18,9 -18,9 z"
transform="matrix(1,0,0,-1,315,377.953)"
id="path2061" />
<rect
x="168"
y="685"
width="147"
height="72"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#ffffff"
id="rect2063" />
<text
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="20px"
transform="translate(185.099,728)"
id="text2065">Output buffer</text>
<path
d="m 1154,228 h 240.51 v 186.13 h -6 V 231 l 3,3 H 1154 Z m 246.51,183.13 -9,18 -9,-18 z"
id="path2067" />
<rect
x="168"
y="891"
width="147"
height="71"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#ffffff"
id="rect2069" />
<text
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="20px"
transform="translate(183.599,934)"
id="text2071">Reactive mask</text>
<path
d="m 0,-3 h 1079.31 v 282.927 h -6 V 0 l 3,3 H 0 Z m 1085.31,279.927 -9,18 -9,-18 z"
transform="matrix(1,0,0,-1,315,926.927)"
id="path2073" />
<rect
x="168"
y="559"
width="147"
height="71"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#ffffff"
id="rect2075" />
<text
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="20px"
transform="translate(212.474,578)"
id="text2081">Dilated <tspan
font-size="20px"
x="-0.369995"
y="24"
id="tspan2077">motion</tspan><tspan
font-size="20px"
x="-0.75"
y="48"
id="tspan2079">vectors</tspan></text>
<rect
x="399"
y="559"
width="148"
height="72"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
id="rect2083" />
<text
fill="#ffffff"
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="20px"
transform="translate(433.503,602)"
id="text2085">Reproject</text>
<path
d="M 470,299.432 V 228 h 521.572 v 6 H 473 l 3,-3 v 68.432 z M 988.572,222 l 17.998,9 -17.998,9 z"
id="path2087" />
<path
d="m 547,332 h 358.9 v 212.638 h -6 V 335 l 3,3 H 547 Z m 364.9,209.638 -9,18 -9,-18 z"
id="path2089" />
<path
d="m 315,135 h 767.98 v 45.19 h -6 V 138 l 3,3 H 315 Z m 773.98,42.19 -9,18 -9,-18 z"
id="path2091" />
<rect
x="581"
y="559"
width="147"
height="72"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#ffffff"
id="rect2093" />
<text
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="20px"
transform="translate(604.788,590)"
id="text2097">Reprojected <tspan
font-size="20px"
x="6.6199999"
y="24"
id="tspan2095">lock status</tspan></text>
<rect
x="1676"
y="560"
width="148"
height="72"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#ffffff"
id="rect2099" />
<text
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="20px"
transform="translate(1721.04,603)"
id="text2101">Output</text>
<path
d="M 1.44131e-5,-3 17.7786,-2.99991 v 6 L -1.44131e-5,3 Z M 14.7786,-8.99993 32.7786,1.5748e-4 14.7785,9.00007 Z"
transform="matrix(1,0,0,-1,1644,596)"
id="path2103" />
<text
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="20px"
transform="translate(326.589,130)"
id="text2105">5x5 Lanczos</text>
<path
d="m 547,332 h 431.611 v 213.338 h -6 V 335 l 3,3 H 547 Z m 437.611,210.338 -9,18 -9,-18 z"
id="path2107" />
<text
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="20px"
transform="translate(487.278,413)"
id="text2109">Y (Luminance)</text>
<path
d="m 547,592 h 18.884 v 6 H 547 Z m 15.884,-6 18,9 -18,9 z"
id="path2111" />
<text
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="20px"
transform="translate(484.471,284)"
id="text2117">1<tspan
font-size="20px"
x="75.349503"
y="30"
id="tspan2113">1</tspan><tspan
font-size="20px"
x="610.20502"
y="12"
id="tspan2115">Clamping box</tspan></text>
<path
d="m 0,-3 h 44.8764 v 128.778 l -3,-3 h 26.8764 v 6 H 38.8764 V 0 l 3,3 H 0 Z m 65.7528,119.778 18,9 -18,9 z"
transform="matrix(1,0,0,-1,315,720.778)"
id="path2119" />
<text
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="20px"
transform="translate(330.697,870)"
id="text2125">2x2<tspan
font-size="20px"
x="33.75"
y="0"
id="tspan2121">bilinea</tspan><tspan
font-size="20px"
x="87.75"
y="0"
id="tspan2123">r</tspan></text>
<path
d="m 316,485 h 159.594 v 58.919 h -6 V 488 l 3,3 H 316 Z m 165.594,55.919 -9,18 -9,-18 z"
id="path2127" />
<text
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="20px"
transform="translate(320.814,473)"
id="text2137">2x2<tspan
font-size="20px"
x="33.75"
y="0"
id="tspan2129">bilinea</tspan>r<tspan
font-size="20px"
x="5.4096098"
y="111"
id="tspan2131">1</tspan><tspan
font-size="20px"
x="4.4811101"
y="275"
id="tspan2133">5x5 Lanczos</tspan><tspan
font-size="20px"
x="417.24301"
y="114"
id="tspan2135">1</tspan></text>
<path
d="M 0,-3 H 18 V 3 H 0 Z m 24,0 h 15.6321 v 8.36787 h -6 V 0 l 3,3 H 24 Z m 15.6321,14.3679 v 18 h -6 v -18 z m 0,24 v 18 h -6 v -18 z m 0,24 v 18 h -6 v -18 z m 0,24 v 18.0001 h -6 V 83.3679 Z m 0,24.0001 v 18 h -6 v -18 z m 0,24 v 18 h -6 v -18 z m 0,24 v 18 h -6 v -18 z m 0,24 v 18 h -6 v -18 z m 0,24 v 18 h -6 v -18 z m 0,24 v 18 h -6 v -18 z m 0,24 v 18 h -6 v -18 z m 0,24 v 18 h -6 v -18 z m 0,24 v 18 h -6 v -18 z m 0,24 v 18 h -6 v -18 z m 0,24 v 18 h -6 v -18 z m 0,24 v 18 h -6 v -18 z m 0,24 v 18 h -6 v -18 z m 0,24 v 18 h -6 v -18 z m 0,24 v 18 h -6 v -18 z m 0,24 v 18 h -6 v -18 z m 0,24 v 15.495 h -8.5049 v -6 h 5.5049 l -3,3 v -12.495 z m -14.5049,15.495 h -9.4951 v -6 h 9.4951 z m -6.4951,6 -17.999974,-9 17.999974,-9 z"
transform="matrix(0,1,1,0,473,631)"
id="path2139" />
<text
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="20px"
transform="translate(328.288,280)"
id="text2149">2x2<tspan
font-size="20px"
x="33.75"
y="0"
id="tspan2141">bilinea</tspan>r<tspan
font-size="20px"
x="1.15607"
y="128"
id="tspan2143">2x2 bilinear</tspan><tspan
font-size="20px"
x="-0.508362"
y="674"
id="tspan2145">2x2</tspan><tspan
font-size="20px"
x="33.2416"
y="674"
id="tspan2147">bilinea</tspan>r</text>
<path
d="m 728.039,592 27.418,0.353 -0.077,6 L 727.961,598 Z m 24.496,-5.685 17.882,9.231 -18.114,8.767 z"
id="path2151" />
<path
d="m 1083,266.965 3.28,278.31 -6,0.071 -3.28,-278.311 z m 9.25,275.24 -8.79,18.104 -9.21,-17.892 z"
id="path2153" />
<path
d="m 315.011,592 68.753,0.258 -0.022,6 L 314.989,598 Z m 65.776,-5.754 17.966,9.068 -18.034,8.932 z"
id="path2155" />
<path
d="m 0,-3 h 40.0282 v 138.078 h -24 v -6 h 21 l -3,3 V 0 l 3,3 H 0 Z m 19.0282,144.078 -18.00001,-9 18.00001,-9 z"
transform="matrix(0,-1,-1,0,786.078,560.028)"
id="path2157" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 20 KiB

@ -0,0 +1,735 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="276.04974mm"
height="127.7922mm"
viewBox="0 0 276.04974 127.7922"
version="1.1"
id="svg5"
inkscape:version="1.1.2 (b8e25be833, 2022-02-05)"
sodipodi:docname="reproject-mvs.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="0.77771465"
inkscape:cx="1384.8267"
inkscape:cy="-437.17834"
inkscape:window-width="3699"
inkscape:window-height="2126"
inkscape:window-x="130"
inkscape:window-y="-11"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs2">
<clipPath
id="clip0">
<rect
x="26"
y="434"
width="1159"
height="556"
id="rect9" />
</clipPath>
<clipPath
id="clip1">
<rect
x="0"
y="9.3132302e-10"
width="1847850"
height="673100"
id="rect12" />
</clipPath>
<image
width="128"
height="133"
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACFCAYAAACT8/B4AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAtnSURBVHhe7d3rU1N3Ggdwq2AgF3IRkNsCuUCuhyQkkhhuFgiBQQmgsICiBVlB1lsFBGvb1FYXx6pdbbXi2mItu1a6687O7szO6AvfOM4443tf+sY3vvMv0H2OPO5YBxWSc02ezwxzft/oKHPOyfk9v3P5nVWEEJKytPBTg0uSYgaCweB3sGyyWq2nsrOz9yx+TJKe3W4fgMXuxfR/uzUazRC2SbKKxWKlfr//LMbfsNlsJ4qKiiwYSbJ58eLFBz6f72do6hc/+S34cz3+OUlGk5OT9ZFI5DjGJeXl5Y0cO3asDiNJJhaL5fKzZ8+W/Pa/AkeBdCgKr2AkyWJ6etqkUChOYHynsrKyL2ZmZkoxkmQAxd2n4+PjJRjfaWpqqrSqquo8RiJ3CwsLa2DM/wvGZYHu4gI2idxlZWVtgAU79l82p9O5c2RkpBMjkbOSkpIL7yv+3vTkyZN1brf7OkYiY2tAXIdzn8/3Ayw+WExElrZu3eqFxY7FtDJsNwDFYxVGIkcVFRUnDxw4UIxxpdbp9fpvsE3khj31C9/ihE7twmiAvWJI5KimpibHbDZ/gTEuKpVqfHZ2Nt4jCBGTw+H4qLCw0I0xLtFo1KNWq+leAZn6lu0GsB03GA3cwCaRi/r6+rTS0tKLGBMC/845WNBwUE7Y4Z/dbj+AMSFKpXJocHAwoa6ECMxmsx1qaWlxYEzI3r17TTAa+BgjkQO/3/9XbHIiFApx0p0QAcRisbUMw7CncTlDdYCMwNCvDPr/gxg5AXXAblCBkUhZdXX17qamJjtGToTDYU95eTnVAXJgtVpnsckpn8+3optKiAju3r2bBhX7HEZOBYPBs1ycWCI8Onz4cHZBQcGnGDml1WqP3L59uwAjkSLop6ObNm2qxsgp6AJq1Gp1N0YiRXl5eX+6devWOoycev78uQKww0EiVZFI5Eds8qKysvInbBKp2bNnj9Lj8fD6VE9DQ8PpWCy2GiORkp6eHjf00W8+9s0p9h4D+H8+xEikxGAw7G5ubrZh5EVjY6MXCs1DGImUmM1mQS7Y1NXV8VpnkDjl5OR8j01eFRQUnKMTQhITCoU0DMMI8kAnDDU/h4IzHSORAhj+bWpvbxfkWb5gMNjp9XrbMRIpgI1yaHh42IiRV319fWUWiyWGkUgBVOeXsSkIv9//KzaJ2KAgS9Pr9YLeslVZWXmGCkHpyCopKfkS24LIyMiYhEXaYiKigsNxO4zNt2AUxObNm5tra2ujGImY7Hb7J93d3YI+v3f+/Pkip9P5zinniEDgCDCPTUGFw2GaQURsUIit0Wq1ojzCzTDM11QIik9rNpu/wraglEolFYJiC4VCUSgARTkr19HR0dzQ0NCBkYghEAgcgIpclAkcYAcoN5lMn2EkYsCXPoimoqLiJjaJ0NgCTKPRfItRFAaD4TQVgiKJRqO6+vr6UxhFYbFY2AKULg2Loampqcbr9Yr6mhf4/7e2trZSISgGl8u1z+FwiPqKl/b2disUgrw8iUTeIz8/XxK3ZsGOSA+NisFoNEriVOxyX0RBONTW1qaHIZgkZvGMRCIzdI+gwMLhcLXH4+H1IZDl2rhxY29PT08QIxFCbW3tKAzBzBhFVV5eXmGz2fZhJELQarUnoQCUxIUY+D1W63S6SxiJEKAG+Bs2JQFGAn/BJuHb+Pi4iq95gOLl9/svUCEokMHBQcf69es5mQaWK1AD/KGvr4/eLiKE3NzcfqPRWI5REuD3YUwm0y6MhE8Gg2Hm3r17GoySAIXgGrVa/WeMhE8w7v4XNiWls7PzGjYJX8bGxtRSfblzKBQ6FYvF6B5BPg0MDBRDF7Afo6RkZWXtu3Tpkgkj4UNOTk43wzBWjJJSXFxcqdPp+jESPgSDwRMPHz7UYpSU+/fvG+AoQIUgnyKRSELvAeQbFKj/wCbh2t27dzPg8C/pc+7sJWoqBHkSjUZ/BwWgpM4AvkmlUv1xbm6uFCPhUmFhYXdvb6+k39phs9l8sBP0YSRcYgvAO3fuSLIAfIWdqBoIMltZymlpaZHF49hUCPLg8ePHGS6XSxY3XVAhyAOLxVKk0WgkXQC+olAoxubn50swEi6kp6f/vr29nZM3gfKtrq5uQ0ZGRi9GwgWj0Xj66tWrkroE/DbPnz9X5eXlUSHIpUAgcAubstDc3HwVmyRRUFCpy8rKZLVCPR7PNzBqUWAkiRgdHbXCCt2LURasViv82qNejCQRKpVqO6xQuZ1etbM7AbZJIjIzM9mHQNZilAX4fZWw49Ir57ng9/v/iU1ZqampEXX6mqTQ39+vz87OnsEoK1C3nAoGg5kYSTxCoVBzdXV1D0ZZ0Wg0W9ra2powkngwDHMEdgJZvrC5q6urxGQyncRI4gH9v6QeAl0po9FIzwrECyrpdKVSKetK2m6300Oj8RoYGCiHoZQknwFYLugCRq5du1aIkawEbPwdExMTHoyyVFRU5DKbzXswkpVwu90z7Dv7McoSe2UwPz9/DiNZCeg/ZV0AvuJ0Oq9APUPzCa9EJBLJdzgcpzHKms1mOwoL5WIiyxIIBHqT5SSKTqera2pqolvFV4JhmLOTk5OyuAPofXw+nxZGAxcwkuWAFfYTNpMCdAM/YpO8D1TN2cl2CrW0tPSzcDiswkjehZ16tbW1NYwxKUAdUE91wDLB+D9p+v9XHj16pHG5XJKY3FryQqHQv7GZVLxe79yLFy9WYyRLGR4eLsrKyjqGMak4nc5PYrFYHkaylIKCgiFYSZUYkwrUNq6cnBxZ3d0sOKiWv1tYWFiDMamwh3+Hw0HvGXyb69evqwwGQ1LfSWu328/ATiCrO5wFAxs/CAXgdoxJSa1WRzs6OuoxkjewF02Savj3ptHR0Vw4AtDLJd4E/eMaj8eTEv0jdAPs/QE0HHxdb2+vv7a2NiVewJienj505MgRJ0bCYhjm+MTEREqMka1Wa4HRaDyOkbCH/5qamv9gTAmww/9MdwmhgYEBv8/nm8KYEhQKxeDatWtlMeUN79LS0o5ZLJYsjClhx44dufn5+Ulxy1tCHj58qHQ6nUlx8+dKrV+/nj3pldoPjej1+tbNmze3YkwpVVVVbY2NjaKf+Op87cfU3d0t6GlKKP5u1tfXp+qkimnQDfyAbUGw23d6epp9q8mrbb6KDSZ2HA7LgzA8YV/IOAtj8gaoUnndMPDLsE/9TC6m1ORwOMZhYVtM/GC3Y2FhYWDjxo2X2cm2Vq9efRC391tfb5OxYcOG7Vqtdt7r9W7la0eIRCLXSkpKMjCmqizYMLw8OcRut5GRkW0VFRXz8IXeBV3typ9NYCc5YE/R7ty5k9Np2s+ePesOhUI0hQowm83H5ubmOD0K9PT0uGG7/QJdzMvDfKIyg8Hg5x0dHSe56q+h+Ll68+bNHIwpze1264qLizm5bTwWi62Fuiqm0+nY0+rcHl07OzsrTSbTQiAQSGjKNqgzqmDodwQjAXA0HN22bVtCl4krKyvNsG5vQLfK6/yEmdCfXIHaYDCeU5kw9lXhrB9JeddPAtJhvfx3165dcX1rm5ub++Gb/z00hampWlpatsOh6+KDBw+WfQGH3WEUCsXM1NSUGz8ir+nq6irDewWW/cWCkRQ7je5F2BY78CPhQGFodrlcv1ZVVXVAfOcvzW7848ePn4PuIyVP+ixXbm5ueP/+/V9B8707QV1dXZfNZpvfsmWLET8Sh91uHyoqKpodGxtb8hr306dP1dAvfT08PPwRfkTeAdZVS2Zm5hlYp0vOMTgxMVEBNdRl2Phj+JEksGPMj6FInLVYLNPQbmR/4Bt/GgqcG0ePHqUrXyvQ19dng2HcHMMwX0J8uS7ZdxPD0fbvKpXqMHvof/kXpWhoaIh9t2+I/YE+jR6ESACuv5frEtcrIVxZtep/D3+JQENSKSEAAAAASUVORK5CYII="
preserveAspectRatio="none"
id="img2" />
<clipPath
id="clip3">
<rect
x="0"
y="0"
width="647796"
height="673100"
id="rect16" />
</clipPath>
<clipPath
id="clip4">
<rect
x="0"
y="0"
width="1841500"
height="673100"
id="rect19" />
</clipPath>
<clipPath
id="clip5">
<rect
x="0"
y="0"
width="647796"
height="673100"
id="rect22" />
</clipPath>
<clipPath
id="clip0-9">
<rect
x="26"
y="434"
width="1059"
height="493"
id="rect481" />
</clipPath>
<clipPath
id="clip1-8">
<rect
x="0"
y="9.3132302e-10"
width="1847850"
height="673100"
id="rect484" />
</clipPath>
<image
width="128"
height="133"
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACFCAYAAACT8/B4AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAtnSURBVHhe7d3rU1N3Ggdwq2AgF3IRkNsCuUCuhyQkkhhuFgiBQQmgsICiBVlB1lsFBGvb1FYXx6pdbbXi2mItu1a6687O7szO6AvfOM4443tf+sY3vvMv0H2OPO5YBxWSc02ezwxzft/oKHPOyfk9v3P5nVWEEJKytPBTg0uSYgaCweB3sGyyWq2nsrOz9yx+TJKe3W4fgMXuxfR/uzUazRC2SbKKxWKlfr//LMbfsNlsJ4qKiiwYSbJ58eLFBz6f72do6hc/+S34cz3+OUlGk5OT9ZFI5DjGJeXl5Y0cO3asDiNJJhaL5fKzZ8+W/Pa/AkeBdCgKr2AkyWJ6etqkUChOYHynsrKyL2ZmZkoxkmQAxd2n4+PjJRjfaWpqqrSqquo8RiJ3CwsLa2DM/wvGZYHu4gI2idxlZWVtgAU79l82p9O5c2RkpBMjkbOSkpIL7yv+3vTkyZN1brf7OkYiY2tAXIdzn8/3Ayw+WExElrZu3eqFxY7FtDJsNwDFYxVGIkcVFRUnDxw4UIxxpdbp9fpvsE3khj31C9/ihE7twmiAvWJI5KimpibHbDZ/gTEuKpVqfHZ2Nt4jCBGTw+H4qLCw0I0xLtFo1KNWq+leAZn6lu0GsB03GA3cwCaRi/r6+rTS0tKLGBMC/845WNBwUE7Y4Z/dbj+AMSFKpXJocHAwoa6ECMxmsx1qaWlxYEzI3r17TTAa+BgjkQO/3/9XbHIiFApx0p0QAcRisbUMw7CncTlDdYCMwNCvDPr/gxg5AXXAblCBkUhZdXX17qamJjtGToTDYU95eTnVAXJgtVpnsckpn8+3optKiAju3r2bBhX7HEZOBYPBs1ycWCI8Onz4cHZBQcGnGDml1WqP3L59uwAjkSLop6ObNm2qxsgp6AJq1Gp1N0YiRXl5eX+6devWOoycev78uQKww0EiVZFI5Eds8qKysvInbBKp2bNnj9Lj8fD6VE9DQ8PpWCy2GiORkp6eHjf00W8+9s0p9h4D+H8+xEikxGAw7G5ubrZh5EVjY6MXCs1DGImUmM1mQS7Y1NXV8VpnkDjl5OR8j01eFRQUnKMTQhITCoU0DMMI8kAnDDU/h4IzHSORAhj+bWpvbxfkWb5gMNjp9XrbMRIpgI1yaHh42IiRV319fWUWiyWGkUgBVOeXsSkIv9//KzaJ2KAgS9Pr9YLeslVZWXmGCkHpyCopKfkS24LIyMiYhEXaYiKigsNxO4zNt2AUxObNm5tra2ujGImY7Hb7J93d3YI+v3f+/Pkip9P5zinniEDgCDCPTUGFw2GaQURsUIit0Wq1ojzCzTDM11QIik9rNpu/wraglEolFYJiC4VCUSgARTkr19HR0dzQ0NCBkYghEAgcgIpclAkcYAcoN5lMn2EkYsCXPoimoqLiJjaJ0NgCTKPRfItRFAaD4TQVgiKJRqO6+vr6UxhFYbFY2AKULg2Loampqcbr9Yr6mhf4/7e2trZSISgGl8u1z+FwiPqKl/b2disUgrw8iUTeIz8/XxK3ZsGOSA+NisFoNEriVOxyX0RBONTW1qaHIZgkZvGMRCIzdI+gwMLhcLXH4+H1IZDl2rhxY29PT08QIxFCbW3tKAzBzBhFVV5eXmGz2fZhJELQarUnoQCUxIUY+D1W63S6SxiJEKAG+Bs2JQFGAn/BJuHb+Pi4iq95gOLl9/svUCEokMHBQcf69es5mQaWK1AD/KGvr4/eLiKE3NzcfqPRWI5REuD3YUwm0y6MhE8Gg2Hm3r17GoySAIXgGrVa/WeMhE8w7v4XNiWls7PzGjYJX8bGxtRSfblzKBQ6FYvF6B5BPg0MDBRDF7Afo6RkZWXtu3Tpkgkj4UNOTk43wzBWjJJSXFxcqdPp+jESPgSDwRMPHz7UYpSU+/fvG+AoQIUgnyKRSELvAeQbFKj/wCbh2t27dzPg8C/pc+7sJWoqBHkSjUZ/BwWgpM4AvkmlUv1xbm6uFCPhUmFhYXdvb6+k39phs9l8sBP0YSRcYgvAO3fuSLIAfIWdqBoIMltZymlpaZHF49hUCPLg8ePHGS6XSxY3XVAhyAOLxVKk0WgkXQC+olAoxubn50swEi6kp6f/vr29nZM3gfKtrq5uQ0ZGRi9GwgWj0Xj66tWrkroE/DbPnz9X5eXlUSHIpUAgcAubstDc3HwVmyRRUFCpy8rKZLVCPR7PNzBqUWAkiRgdHbXCCt2LURasViv82qNejCQRKpVqO6xQuZ1etbM7AbZJIjIzM9mHQNZilAX4fZWw49Ir57ng9/v/iU1ZqampEXX6mqTQ39+vz87OnsEoK1C3nAoGg5kYSTxCoVBzdXV1D0ZZ0Wg0W9ra2powkngwDHMEdgJZvrC5q6urxGQyncRI4gH9v6QeAl0po9FIzwrECyrpdKVSKetK2m6300Oj8RoYGCiHoZQknwFYLugCRq5du1aIkawEbPwdExMTHoyyVFRU5DKbzXswkpVwu90z7Dv7McoSe2UwPz9/DiNZCeg/ZV0AvuJ0Oq9APUPzCa9EJBLJdzgcpzHKms1mOwoL5WIiyxIIBHqT5SSKTqera2pqolvFV4JhmLOTk5OyuAPofXw+nxZGAxcwkuWAFfYTNpMCdAM/YpO8D1TN2cl2CrW0tPSzcDiswkjehZ16tbW1NYwxKUAdUE91wDLB+D9p+v9XHj16pHG5XJKY3FryQqHQv7GZVLxe79yLFy9WYyRLGR4eLsrKyjqGMak4nc5PYrFYHkaylIKCgiFYSZUYkwrUNq6cnBxZ3d0sOKiWv1tYWFiDMamwh3+Hw0HvGXyb69evqwwGQ1LfSWu328/ATiCrO5wFAxs/CAXgdoxJSa1WRzs6OuoxkjewF02Savj3ptHR0Vw4AtDLJd4E/eMaj8eTEv0jdAPs/QE0HHxdb2+vv7a2NiVewJienj505MgRJ0bCYhjm+MTEREqMka1Wa4HRaDyOkbCH/5qamv9gTAmww/9MdwmhgYEBv8/nm8KYEhQKxeDatWtlMeUN79LS0o5ZLJYsjClhx44dufn5+Ulxy1tCHj58qHQ6nUlx8+dKrV+/nj3pldoPjej1+tbNmze3YkwpVVVVbY2NjaKf+Op87cfU3d0t6GlKKP5u1tfXp+qkimnQDfyAbUGw23d6epp9q8mrbb6KDSZ2HA7LgzA8YV/IOAtj8gaoUnndMPDLsE/9TC6m1ORwOMZhYVtM/GC3Y2FhYWDjxo2X2cm2Vq9efRC391tfb5OxYcOG7Vqtdt7r9W7la0eIRCLXSkpKMjCmqizYMLw8OcRut5GRkW0VFRXz8IXeBV3typ9NYCc5YE/R7ty5k9Np2s+ePesOhUI0hQowm83H5ubmOD0K9PT0uGG7/QJdzMvDfKIyg8Hg5x0dHSe56q+h+Ll68+bNHIwpze1264qLizm5bTwWi62Fuiqm0+nY0+rcHl07OzsrTSbTQiAQSGjKNqgzqmDodwQjAXA0HN22bVtCl4krKyvNsG5vQLfK6/yEmdCfXIHaYDCeU5kw9lXhrB9JeddPAtJhvfx3165dcX1rm5ub++Gb/z00hampWlpatsOh6+KDBw+WfQGH3WEUCsXM1NSUGz8ir+nq6irDewWW/cWCkRQ7je5F2BY78CPhQGFodrlcv1ZVVXVAfOcvzW7848ePn4PuIyVP+ixXbm5ueP/+/V9B8707QV1dXZfNZpvfsmWLET8Sh91uHyoqKpodGxtb8hr306dP1dAvfT08PPwRfkTeAdZVS2Zm5hlYp0vOMTgxMVEBNdRl2Phj+JEksGPMj6FInLVYLNPQbmR/4Bt/GgqcG0ePHqUrXyvQ19dng2HcHMMwX0J8uS7ZdxPD0fbvKpXqMHvof/kXpWhoaIh9t2+I/YE+jR6ESACuv5frEtcrIVxZtep/D3+JQENSKSEAAAAASUVORK5CYII="
preserveAspectRatio="none"
id="img2-3" />
<clipPath
id="clip3-8">
<rect
x="0"
y="0"
width="647796"
height="673100"
id="rect488" />
</clipPath>
<clipPath
id="clip4-5">
<rect
x="0"
y="0"
width="1841500"
height="673100"
id="rect491" />
</clipPath>
<clipPath
id="clip5-7">
<rect
x="0"
y="0"
width="647796"
height="673100"
id="rect494" />
</clipPath>
</defs>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(261.23083,-263.9559)">
<g
clip-path="url(#clip0-9)"
transform="matrix(0.26458333,0,0,0.26458333,-271.87593,146.47935)"
id="g667">
<rect
x="153"
y="529"
width="416"
height="279"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#f2f2f2"
id="rect499" />
<path
d="m 180,564.5 c 0,-4.142 3.582,-7.5 8,-7.5 4.418,0 8,3.358 8,7.5 0,4.142 -3.582,7.5 -8,7.5 -4.418,0 -8,-3.358 -8,-7.5 z"
fill-rule="evenodd"
id="path501" />
<path
d="m 183.788,559.091 50.645,35.695 -0.576,0.817 -50.645,-35.694 z m 52.178,30.047 6.352,11.817 -13.265,-2.009 z"
id="path503" />
<path
d="M 631,743 273,629.295 v -28.59 L 631,487 Z"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#595959"
fill-rule="evenodd"
fill-opacity="0.490196"
id="path505" />
<rect
x="242"
y="599"
width="31"
height="32"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect507" />
<rect
x="672"
y="487"
width="52"
height="51"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect509" />
<rect
x="620"
y="487"
width="52"
height="51"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect511" />
<rect
x="672"
y="535"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect513" />
<rect
x="620"
y="535"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect515" />
<rect
x="672"
y="587"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect517" />
<rect
x="620"
y="587"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect519" />
<rect
x="724"
y="487"
width="52"
height="51"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect521" />
<rect
x="724"
y="535"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect523" />
<rect
x="724"
y="587"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect525" />
<rect
x="776"
y="487"
width="52"
height="51"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect527" />
<rect
x="776"
y="535"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect529" />
<rect
x="776"
y="587"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect531" />
<rect
x="620"
y="639"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect533" />
<rect
x="724"
y="639"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect535" />
<rect
x="776"
y="639"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect537" />
<rect
x="672"
y="639"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect539" />
<rect
x="828"
y="487"
width="52"
height="51"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect541" />
<rect
x="828"
y="535"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect543" />
<rect
x="828"
y="587"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect545" />
<rect
x="828"
y="639"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect547" />
<rect
x="672"
y="691"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect549" />
<rect
x="620"
y="691"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect551" />
<rect
x="724"
y="691"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect553" />
<rect
x="776"
y="691"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect555" />
<rect
x="828"
y="691"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect557" />
<path
d="m 249,615 c 0,-4.418 3.582,-8 8,-8 4.418,0 8,3.582 8,8 0,4.418 -3.582,8 -8,8 -4.418,0 -8,-3.582 -8,-8 z"
fill-rule="evenodd"
id="path559" />
<path
d="M 0,0 1.5748e-4,112.248"
stroke="#000000"
stroke-miterlimit="8"
stroke-dasharray="4, 3"
fill="none"
fill-rule="evenodd"
transform="matrix(1,0,0,-1,257.5,599.748)"
id="path561" />
<path
d="M 188.5,557.788 V 487.5"
stroke="#000000"
stroke-miterlimit="8"
stroke-dasharray="4, 3"
fill="none"
fill-rule="evenodd"
id="path563" />
<path
d="M 0.989977,-0.499998 68.063,-0.499845 v 1 L 0.989974,0.500002 Z M 12.738,7.43192 -0.00231745,3.23596e-8 12.7381,-7.43186 c 0.2385,-0.13914 0.5447,-0.05857 0.6838,0.17995 0.1391,0.23853 0.0586,0.54469 -0.1799,0.68383 L 1.24193,0.431892 1.24194,-0.431887 13.2419,6.56814 c 0.2385,0.13914 0.3191,0.4453 0.18,0.68383 -0.1392,0.23852 -0.4453,0.31909 -0.6839,0.17995 z M 56.3145,-7.43176 69.0549,1.57414e-4 56.3145,7.43202 C 56.076,7.57116 55.7698,7.49059 55.6307,7.25206 55.4916,7.01354 55.5721,6.70738 55.8106,6.56824 l 12.0001,-6.999975 v 0.863779 l -12,-7.000024 c -0.2385,-0.13914 -0.3191,-0.4453 -0.18,-0.68383 0.1392,-0.23852 0.4453,-0.31909 0.6838,-0.17995 z"
transform="matrix(-1,0,0,1,257.553,487.5)"
id="path565" />
<path
d="M 102.5,615.5 H 242.348"
stroke="#000000"
stroke-miterlimit="8"
stroke-dasharray="4, 3"
fill="none"
fill-rule="evenodd"
id="path567" />
<path
d="M 0.501347,0.989393 0.570148,51.5371 -0.429851,51.5385 -0.498652,0.990754 Z M -7.41455,12.7482 -3.15554e-6,-0.00231738 7.44922,12.7279 C 7.58869,12.9663 7.50853,13.2725 7.2702,13.412 7.03186,13.5515 6.72559,13.4713 6.58613,13.233 L -0.430199,1.24252 0.433579,1.24135 -6.55008,13.2509 c -0.13882,0.2387 -0.44487,0.3197 -0.68358,0.1808 -0.23872,-0.1388 -0.3197,-0.4448 -0.18089,-0.6835 z M 7.48604,39.7794 0.0714992,52.5299 -7.37772,39.7996 c -0.13947,-0.2383 -0.05932,-0.5446 0.17902,-0.684 0.23833,-0.1395 0.5446,-0.0594 0.68407,0.179 L 0.501695,51.285 -0.362083,51.2862 6.62158,39.2767 c 0.13881,-0.2387 0.44486,-0.3197 0.68358,-0.1809 0.23871,0.1388 0.3197,0.4449 0.18088,0.6836 z"
transform="matrix(1,0,0,-1,98.5,615.028)"
id="path569" />
<path
d="M 0,0 78.7002,1.5748e-4"
stroke="#000000"
stroke-miterlimit="8"
stroke-dasharray="4, 3"
fill="none"
fill-rule="evenodd"
transform="matrix(-1,0,0,1,181.2,562.5)"
id="path571" />
<text
font-family="'Cambria Math', 'Cambria Math_MSFontService', sans-serif"
font-weight="400"
font-size="36px"
transform="translate(199.386,468)"
id="text579">𝐌<tspan
font-size="26px"
x="31"
y="7"
id="tspan573">𝑥</tspan><tspan
font-size="36px"
x="-161.05099"
y="128"
id="tspan575">𝐌</tspan><tspan
font-size="26px"
x="-130.052"
y="135"
id="tspan577">𝑦</tspan></text>
<g
clip-path="url(#clip1-8)"
transform="matrix(0,-1.5748e-4,1.5748e-4,0,912,761)"
id="g585">
<g
clip-path="url(#clip3-8)"
transform="scale(2.85252,1)"
id="g583">
<use
width="100%"
height="100%"
xlink:href="#img2-3"
transform="scale(5060.9)"
id="use581"
x="0"
y="0" />
</g>
</g>
<path
d="m 649.5,561.5 h 434.07"
stroke="#404040"
stroke-miterlimit="8"
stroke-dasharray="4, 3"
fill="none"
fill-rule="evenodd"
id="path587" />
<path
d="m 649.5,614.5 433.63,0.5"
stroke="#404040"
stroke-miterlimit="8"
stroke-dasharray="4, 3"
fill="none"
fill-rule="evenodd"
id="path589" />
<path
d="M 0,0 434.071,0.0927559"
stroke="#404040"
stroke-miterlimit="8"
stroke-dasharray="4, 3"
fill="none"
fill-rule="evenodd"
transform="matrix(1,0,0,-1,649.5,665.593)"
id="path591" />
<path
d="m 649.5,717.5 h 434.07"
stroke="#404040"
stroke-miterlimit="8"
stroke-dasharray="4, 3"
fill="none"
fill-rule="evenodd"
id="path593" />
<path
d="M 0,0 0.209764,406.475"
stroke="#404040"
stroke-miterlimit="8"
stroke-dasharray="4, 3"
fill="none"
fill-rule="evenodd"
transform="matrix(-1,0,0,1,851.71,505.5)"
id="path595" />
<path
d="m 646.5,512.5 0.546,407.066"
stroke="#404040"
stroke-miterlimit="8"
stroke-dasharray="4, 3"
fill="none"
fill-rule="evenodd"
id="path597" />
<path
d="M 0,0 0.209764,406.475"
stroke="#404040"
stroke-miterlimit="8"
stroke-dasharray="4, 3"
fill="none"
fill-rule="evenodd"
transform="matrix(-1,0,0,1,802.71,513.5)"
id="path599" />
<path
d="m 750.5,513.5 1.223,405.975"
stroke="#404040"
stroke-miterlimit="8"
stroke-dasharray="4, 3"
fill="none"
fill-rule="evenodd"
id="path601" />
<path
d="m 697.5,514.5 2.34,405.358"
stroke="#404040"
stroke-miterlimit="8"
stroke-dasharray="4, 3"
fill="none"
fill-rule="evenodd"
id="path603" />
<path
d="m 648.5,511.5 h 434.07"
stroke="#404040"
stroke-miterlimit="8"
stroke-dasharray="4, 3"
fill="none"
fill-rule="evenodd"
id="path605" />
<g
clip-path="url(#clip4-5)"
transform="matrix(1.5748e-4,0,0,1.5748e-4,604,772)"
id="g611">
<g
clip-path="url(#clip5-7)"
transform="scale(2.84272,1)"
id="g609">
<use
width="100%"
height="100%"
xlink:href="#img2-3"
transform="scale(5060.9)"
id="use607"
x="0"
y="0" />
</g>
</g>
<path
d="m 742,510 c 0,-4.418 3.358,-8 7.5,-8 4.142,0 7.5,3.582 7.5,8 0,4.418 -3.358,8 -7.5,8 -4.142,0 -7.5,-3.582 -7.5,-8 z"
fill-rule="evenodd"
id="path613" />
<path
d="m 794,510 c 0,-4.418 3.582,-8 8,-8 4.418,0 8,3.582 8,8 0,4.418 -3.582,8 -8,8 -4.418,0 -8,-3.582 -8,-8 z"
fill-rule="evenodd"
id="path615" />
<path
d="m 844,510 c 0,-4.418 3.582,-8 8,-8 4.418,0 8,3.582 8,8 0,4.418 -3.582,8 -8,8 -4.418,0 -8,-3.582 -8,-8 z"
fill-rule="evenodd"
id="path617" />
<path
d="m 689,510 c 0,-4.418 3.582,-8 8,-8 4.418,0 8,3.582 8,8 0,4.418 -3.582,8 -8,8 -4.418,0 -8,-3.582 -8,-8 z"
fill-rule="evenodd"
id="path619" />
<path
d="m 639,510 c 0,-4.418 3.358,-8 7.5,-8 4.142,0 7.5,3.582 7.5,8 0,4.418 -3.358,8 -7.5,8 -4.142,0 -7.5,-3.582 -7.5,-8 z"
fill-rule="evenodd"
id="path621" />
<path
d="m 743,717.5 c 0,-4.142 3.582,-7.5 8,-7.5 4.418,0 8,3.358 8,7.5 0,4.142 -3.582,7.5 -8,7.5 -4.418,0 -8,-3.358 -8,-7.5 z"
fill-rule="evenodd"
id="path623" />
<path
d="m 795,717.5 c 0,-4.142 3.582,-7.5 8,-7.5 4.418,0 8,3.358 8,7.5 0,4.142 -3.582,7.5 -8,7.5 -4.418,0 -8,-3.358 -8,-7.5 z"
fill-rule="evenodd"
id="path625" />
<path
d="m 845,717.5 c 0,-4.142 3.582,-7.5 8,-7.5 4.418,0 8,3.358 8,7.5 0,4.142 -3.582,7.5 -8,7.5 -4.418,0 -8,-3.358 -8,-7.5 z"
fill-rule="evenodd"
id="path627" />
<path
d="m 690,717.5 c 0,-4.142 3.582,-7.5 8,-7.5 4.418,0 8,3.358 8,7.5 0,4.142 -3.582,7.5 -8,7.5 -4.418,0 -8,-3.358 -8,-7.5 z"
fill-rule="evenodd"
id="path629" />
<path
d="m 640,717.5 c 0,-4.142 3.582,-7.5 8,-7.5 4.418,0 8,3.358 8,7.5 0,4.142 -3.582,7.5 -8,7.5 -4.418,0 -8,-3.358 -8,-7.5 z"
fill-rule="evenodd"
id="path631" />
<path
d="m 743,613.5 c 0,-4.142 3.582,-7.5 8,-7.5 4.418,0 8,3.358 8,7.5 0,4.142 -3.582,7.5 -8,7.5 -4.418,0 -8,-3.358 -8,-7.5 z"
fill-rule="evenodd"
id="path633" />
<path
d="m 743,664 c 0,-4.418 3.582,-8 8,-8 4.418,0 8,3.582 8,8 0,4.418 -3.582,8 -8,8 -4.418,0 -8,-3.582 -8,-8 z"
fill-rule="evenodd"
id="path635" />
<path
d="m 742,561 c 0,-4.418 3.582,-8 8,-8 4.418,0 8,3.582 8,8 0,4.418 -3.582,8 -8,8 -4.418,0 -8,-3.582 -8,-8 z"
fill-rule="evenodd"
id="path637" />
<path
d="m 795,613.5 c 0,-4.142 3.582,-7.5 8,-7.5 4.418,0 8,3.358 8,7.5 0,4.142 -3.582,7.5 -8,7.5 -4.418,0 -8,-3.358 -8,-7.5 z"
fill-rule="evenodd"
id="path639" />
<path
d="m 796,664 c 0,-4.418 3.358,-8 7.5,-8 4.142,0 7.5,3.582 7.5,8 0,4.418 -3.358,8 -7.5,8 -4.142,0 -7.5,-3.582 -7.5,-8 z"
fill-rule="evenodd"
id="path641" />
<path
d="m 795,561 c 0,-4.418 3.358,-8 7.5,-8 4.142,0 7.5,3.582 7.5,8 0,4.418 -3.358,8 -7.5,8 -4.142,0 -7.5,-3.582 -7.5,-8 z"
fill-rule="evenodd"
id="path643" />
<path
d="m 845,613.5 c 0,-4.142 3.582,-7.5 8,-7.5 4.418,0 8,3.358 8,7.5 0,4.142 -3.582,7.5 -8,7.5 -4.418,0 -8,-3.358 -8,-7.5 z"
fill-rule="evenodd"
id="path645" />
<path
d="m 846,664 c 0,-4.418 3.358,-8 7.5,-8 4.142,0 7.5,3.582 7.5,8 0,4.418 -3.358,8 -7.5,8 -4.142,0 -7.5,-3.582 -7.5,-8 z"
fill-rule="evenodd"
id="path647" />
<path
d="m 845,561 c 0,-4.418 3.358,-8 7.5,-8 4.142,0 7.5,3.582 7.5,8 0,4.418 -3.358,8 -7.5,8 -4.142,0 -7.5,-3.582 -7.5,-8 z"
fill-rule="evenodd"
id="path649" />
<path
d="m 690,613.5 c 0,-4.142 3.582,-7.5 8,-7.5 4.418,0 8,3.358 8,7.5 0,4.142 -3.582,7.5 -8,7.5 -4.418,0 -8,-3.358 -8,-7.5 z"
fill-rule="evenodd"
id="path651" />
<path
d="m 691,664 c 0,-4.418 3.358,-8 7.5,-8 4.142,0 7.5,3.582 7.5,8 0,4.418 -3.358,8 -7.5,8 -4.142,0 -7.5,-3.582 -7.5,-8 z"
fill-rule="evenodd"
id="path653" />
<path
d="m 690,561 c 0,-4.418 3.358,-8 7.5,-8 4.142,0 7.5,3.582 7.5,8 0,4.418 -3.358,8 -7.5,8 -4.142,0 -7.5,-3.582 -7.5,-8 z"
fill-rule="evenodd"
id="path655" />
<path
d="m 640,613.5 c 0,-4.142 3.582,-7.5 8,-7.5 4.418,0 8,3.358 8,7.5 0,4.142 -3.582,7.5 -8,7.5 -4.418,0 -8,-3.358 -8,-7.5 z"
fill-rule="evenodd"
id="path657" />
<path
d="m 640,664 c 0,-4.418 3.582,-8 8,-8 4.418,0 8,3.582 8,8 0,4.418 -3.582,8 -8,8 -4.418,0 -8,-3.582 -8,-8 z"
fill-rule="evenodd"
id="path659" />
<path
d="m 639,561 c 0,-4.418 3.582,-8 8,-8 4.418,0 8,3.582 8,8 0,4.418 -3.582,8 -8,8 -4.418,0 -8,-3.582 -8,-8 z"
fill-rule="evenodd"
id="path661" />
<rect
x="1031"
y="458"
width="51"
height="297"
fill="#ffffff"
id="rect663" />
<rect
x="609"
y="887"
width="297"
height="40"
fill="#ffffff"
id="rect665" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 29 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 46 KiB

@ -0,0 +1,897 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="295.9624mm"
height="86.851547mm"
viewBox="0 0 295.9624 86.851547"
version="1.1"
id="svg317"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs314">
<clipPath
id="clip0">
<rect
x="152"
y="465"
width="1133"
height="344"
id="rect321" />
</clipPath>
<clipPath
id="clip1">
<rect
x="857"
y="464"
width="427"
height="320"
id="rect324" />
</clipPath>
<clipPath
id="clip2">
<rect
x="857"
y="464"
width="427"
height="320"
id="rect327" />
</clipPath>
<clipPath
id="clip3">
<rect
x="857"
y="464"
width="427"
height="320"
id="rect330" />
</clipPath>
<clipPath
id="clip4">
<rect
x="857"
y="464"
width="427"
height="320"
id="rect333" />
</clipPath>
<clipPath
id="clip5">
<rect
x="857"
y="464"
width="427"
height="320"
id="rect336" />
</clipPath>
<clipPath
id="clip6">
<rect
x="895"
y="486"
width="329"
height="257"
id="rect339" />
</clipPath>
<clipPath
id="clip7">
<rect
x="857"
y="464"
width="427"
height="320"
id="rect342" />
</clipPath>
<clipPath
id="clip8">
<rect
x="857"
y="464"
width="427"
height="320"
id="rect345" />
</clipPath>
<clipPath
id="clip9">
<rect
x="857"
y="464"
width="427"
height="320"
id="rect348" />
</clipPath>
<clipPath
id="clip10">
<rect
x="857"
y="464"
width="427"
height="320"
id="rect351" />
</clipPath>
<clipPath
id="clip11">
<rect
x="857"
y="464"
width="427"
height="320"
id="rect354" />
</clipPath>
<clipPath
id="clip12">
<rect
x="857"
y="464"
width="427"
height="320"
id="rect357" />
</clipPath>
<clipPath
id="clip13">
<rect
x="857"
y="464"
width="427"
height="320"
id="rect360" />
</clipPath>
<clipPath
id="clip14">
<rect
x="857"
y="464"
width="427"
height="320"
id="rect363" />
</clipPath>
<clipPath
id="clip15">
<rect
x="857"
y="464"
width="427"
height="320"
id="rect366" />
</clipPath>
<clipPath
id="clip16">
<rect
x="857"
y="464"
width="427"
height="320"
id="rect369" />
</clipPath>
<clipPath
id="clip17">
<rect
x="857"
y="464"
width="427"
height="320"
id="rect372" />
</clipPath>
<clipPath
id="clip18">
<rect
x="857"
y="464"
width="427"
height="320"
id="rect375" />
</clipPath>
<clipPath
id="clip19">
<rect
x="857"
y="464"
width="427"
height="320"
id="rect378" />
</clipPath>
<clipPath
id="clip20">
<rect
x="857"
y="464"
width="427"
height="320"
id="rect381" />
</clipPath>
<clipPath
id="clip21">
<rect
x="857"
y="464"
width="427"
height="320"
id="rect384" />
</clipPath>
<clipPath
id="clip22">
<rect
x="857"
y="464"
width="427"
height="320"
id="rect387" />
</clipPath>
</defs>
<g
id="layer1"
transform="translate(64.630694,-80.409279)">
<g
clip-path="url(#clip0)"
transform="matrix(0.26458333,0,0,0.26458333,-104.84736,-46.78709)"
id="g592">
<rect
x="153"
y="529"
width="416"
height="279"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#f2f2f2"
id="rect392" />
<path
d="M 631,743 273,629.295 v -28.59 L 631,487 Z"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#595959"
fill-rule="evenodd"
fill-opacity="0.490196"
id="path394" />
<rect
x="242"
y="599"
width="31"
height="32"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect396" />
<rect
x="672"
y="487"
width="52"
height="51"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect398" />
<rect
x="620"
y="487"
width="52"
height="51"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect400" />
<rect
x="672"
y="535"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect402" />
<rect
x="620"
y="535"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect404" />
<rect
x="672"
y="587"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect406" />
<rect
x="620"
y="587"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect408" />
<rect
x="724"
y="487"
width="52"
height="51"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect410" />
<rect
x="724"
y="535"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect412" />
<rect
x="724"
y="587"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect414" />
<path
d="m 743,613.5 c 0,-4.142 3.582,-7.5 8,-7.5 4.418,0 8,3.358 8,7.5 0,4.142 -3.582,7.5 -8,7.5 -4.418,0 -8,-3.358 -8,-7.5 z"
fill-rule="evenodd"
id="path416" />
<rect
x="776"
y="487"
width="52"
height="51"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect418" />
<rect
x="776"
y="535"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect420" />
<rect
x="776"
y="587"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect422" />
<rect
x="620"
y="639"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect424" />
<rect
x="724"
y="639"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect426" />
<rect
x="776"
y="639"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect428" />
<rect
x="672"
y="639"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect430" />
<rect
x="828"
y="487"
width="52"
height="51"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect432" />
<rect
x="828"
y="535"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect434" />
<rect
x="828"
y="587"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect436" />
<rect
x="828"
y="639"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect438" />
<rect
x="672"
y="691"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect440" />
<rect
x="620"
y="691"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect442" />
<rect
x="724"
y="691"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect444" />
<rect
x="776"
y="691"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect446" />
<rect
x="828"
y="691"
width="52"
height="52"
stroke="#000000"
stroke-width="2"
stroke-miterlimit="8"
fill="#d9d9d9"
id="rect448" />
<path
d="m 249,615 c 0,-4.418 3.582,-8 8,-8 4.418,0 8,3.582 8,8 0,4.418 -3.582,8 -8,8 -4.418,0 -8,-3.582 -8,-8 z"
fill-rule="evenodd"
id="path450" />
<path
d="m 743,664 c 0,-4.418 3.582,-8 8,-8 4.418,0 8,3.582 8,8 0,4.418 -3.582,8 -8,8 -4.418,0 -8,-3.582 -8,-8 z"
fill="#bfbfbf"
fill-rule="evenodd"
id="path452" />
<path
d="m 743,717.5 c 0,-4.142 3.582,-7.5 8,-7.5 4.418,0 8,3.358 8,7.5 0,4.142 -3.582,7.5 -8,7.5 -4.418,0 -8,-3.358 -8,-7.5 z"
fill="#bfbfbf"
fill-rule="evenodd"
id="path454" />
<path
d="m 742,561 c 0,-4.418 3.582,-8 8,-8 4.418,0 8,3.582 8,8 0,4.418 -3.582,8 -8,8 -4.418,0 -8,-3.582 -8,-8 z"
fill="#bfbfbf"
fill-rule="evenodd"
id="path456" />
<path
d="m 742,510 c 0,-4.418 3.358,-8 7.5,-8 4.142,0 7.5,3.582 7.5,8 0,4.418 -3.358,8 -7.5,8 -4.142,0 -7.5,-3.582 -7.5,-8 z"
fill="#bfbfbf"
fill-rule="evenodd"
id="path458" />
<path
d="m 795,613.5 c 0,-4.142 3.582,-7.5 8,-7.5 4.418,0 8,3.358 8,7.5 0,4.142 -3.582,7.5 -8,7.5 -4.418,0 -8,-3.358 -8,-7.5 z"
fill="#bfbfbf"
fill-rule="evenodd"
id="path460" />
<path
d="m 796,664 c 0,-4.418 3.358,-8 7.5,-8 4.142,0 7.5,3.582 7.5,8 0,4.418 -3.358,8 -7.5,8 -4.142,0 -7.5,-3.582 -7.5,-8 z"
fill="#bfbfbf"
fill-rule="evenodd"
id="path462" />
<path
d="m 795,717.5 c 0,-4.142 3.582,-7.5 8,-7.5 4.418,0 8,3.358 8,7.5 0,4.142 -3.582,7.5 -8,7.5 -4.418,0 -8,-3.358 -8,-7.5 z"
fill="#bfbfbf"
fill-rule="evenodd"
id="path464" />
<path
d="m 795,561 c 0,-4.418 3.358,-8 7.5,-8 4.142,0 7.5,3.582 7.5,8 0,4.418 -3.358,8 -7.5,8 -4.142,0 -7.5,-3.582 -7.5,-8 z"
fill="#bfbfbf"
fill-rule="evenodd"
id="path466" />
<path
d="m 794,510 c 0,-4.418 3.582,-8 8,-8 4.418,0 8,3.582 8,8 0,4.418 -3.582,8 -8,8 -4.418,0 -8,-3.582 -8,-8 z"
fill="#bfbfbf"
fill-rule="evenodd"
id="path468" />
<path
d="m 845,613.5 c 0,-4.142 3.582,-7.5 8,-7.5 4.418,0 8,3.358 8,7.5 0,4.142 -3.582,7.5 -8,7.5 -4.418,0 -8,-3.358 -8,-7.5 z"
fill="#bfbfbf"
fill-rule="evenodd"
id="path470" />
<path
d="m 846,664 c 0,-4.418 3.358,-8 7.5,-8 4.142,0 7.5,3.582 7.5,8 0,4.418 -3.358,8 -7.5,8 -4.142,0 -7.5,-3.582 -7.5,-8 z"
fill="#bfbfbf"
fill-rule="evenodd"
id="path472" />
<path
d="m 845,717.5 c 0,-4.142 3.582,-7.5 8,-7.5 4.418,0 8,3.358 8,7.5 0,4.142 -3.582,7.5 -8,7.5 -4.418,0 -8,-3.358 -8,-7.5 z"
fill="#bfbfbf"
fill-rule="evenodd"
id="path474" />
<path
d="m 845,561 c 0,-4.418 3.358,-8 7.5,-8 4.142,0 7.5,3.582 7.5,8 0,4.418 -3.358,8 -7.5,8 -4.142,0 -7.5,-3.582 -7.5,-8 z"
fill="#bfbfbf"
fill-rule="evenodd"
id="path476" />
<path
d="m 844,510 c 0,-4.418 3.582,-8 8,-8 4.418,0 8,3.582 8,8 0,4.418 -3.582,8 -8,8 -4.418,0 -8,-3.582 -8,-8 z"
fill="#bfbfbf"
fill-rule="evenodd"
id="path478" />
<path
d="m 690,613.5 c 0,-4.142 3.582,-7.5 8,-7.5 4.418,0 8,3.358 8,7.5 0,4.142 -3.582,7.5 -8,7.5 -4.418,0 -8,-3.358 -8,-7.5 z"
fill="#bfbfbf"
fill-rule="evenodd"
id="path480" />
<path
d="m 691,664 c 0,-4.418 3.358,-8 7.5,-8 4.142,0 7.5,3.582 7.5,8 0,4.418 -3.358,8 -7.5,8 -4.142,0 -7.5,-3.582 -7.5,-8 z"
fill="#bfbfbf"
fill-rule="evenodd"
id="path482" />
<path
d="m 690,717.5 c 0,-4.142 3.582,-7.5 8,-7.5 4.418,0 8,3.358 8,7.5 0,4.142 -3.582,7.5 -8,7.5 -4.418,0 -8,-3.358 -8,-7.5 z"
fill="#bfbfbf"
fill-rule="evenodd"
id="path484" />
<path
d="m 690,561 c 0,-4.418 3.358,-8 7.5,-8 4.142,0 7.5,3.582 7.5,8 0,4.418 -3.358,8 -7.5,8 -4.142,0 -7.5,-3.582 -7.5,-8 z"
fill="#bfbfbf"
fill-rule="evenodd"
id="path486" />
<path
d="m 689,510 c 0,-4.418 3.582,-8 8,-8 4.418,0 8,3.582 8,8 0,4.418 -3.582,8 -8,8 -4.418,0 -8,-3.582 -8,-8 z"
fill="#bfbfbf"
fill-rule="evenodd"
id="path488" />
<path
d="m 640,613.5 c 0,-4.142 3.582,-7.5 8,-7.5 4.418,0 8,3.358 8,7.5 0,4.142 -3.582,7.5 -8,7.5 -4.418,0 -8,-3.358 -8,-7.5 z"
fill="#bfbfbf"
fill-rule="evenodd"
id="path490" />
<path
d="m 640,664 c 0,-4.418 3.582,-8 8,-8 4.418,0 8,3.582 8,8 0,4.418 -3.582,8 -8,8 -4.418,0 -8,-3.582 -8,-8 z"
fill="#bfbfbf"
fill-rule="evenodd"
id="path492" />
<path
d="m 640,717.5 c 0,-4.142 3.582,-7.5 8,-7.5 4.418,0 8,3.358 8,7.5 0,4.142 -3.582,7.5 -8,7.5 -4.418,0 -8,-3.358 -8,-7.5 z"
fill="#bfbfbf"
fill-rule="evenodd"
id="path494" />
<path
d="m 639,561 c 0,-4.418 3.582,-8 8,-8 4.418,0 8,3.582 8,8 0,4.418 -3.582,8 -8,8 -4.418,0 -8,-3.582 -8,-8 z"
fill="#bfbfbf"
fill-rule="evenodd"
id="path496" />
<path
d="m 639,510 c 0,-4.418 3.358,-8 7.5,-8 4.142,0 7.5,3.582 7.5,8 0,4.418 -3.358,8 -7.5,8 -4.142,0 -7.5,-3.582 -7.5,-8 z"
fill="#bfbfbf"
fill-rule="evenodd"
id="path498" />
<path
d="m 10,-0.499992 169.663,1.41e-4 v 1 L 10,0.500008 Z M 12,6.00001 0,0 12,-5.99999 Z m 165.663,-11.99986 12,6.00000748 -12,5.99999252 z"
transform="matrix(-1,0,0,1,846.163,614.5)"
id="path500" />
<path
d="m 751.062,528.497 1.063,171.925 -1,0.006 -1.063,-171.925 z m -6.488,2.04 5.926,-12.037 6.074,11.963 z m 13.039,167.851 -5.926,12.037 -6.074,-11.962 z"
id="path502" />
<g
clip-path="url(#clip1)"
id="g506">
<rect
x="896"
y="487"
width="327"
height="255"
fill="#d9d9d9"
id="rect504" />
</g>
<g
clip-path="url(#clip2)"
id="g510">
<path
d="m 896,742 h 327 M 896,657 h 327 M 896,614 h 327 M 896,572 h 327 M 896,529 h 327 M 896,487 h 327"
stroke="#000000"
stroke-width="1.5"
stroke-linejoin="round"
stroke-miterlimit="10"
fill="none"
id="path508" />
</g>
<g
clip-path="url(#clip3)"
id="g514">
<path
d="m 896,487 v 255 m 41,-255 v 255 m 41,-255 v 255 m 41,-255 v 255 m 41,-255 v 255 m 41,-255 v 255 m 41,-255 v 255 m 40,-255 v 255 m 41,-255 v 255"
stroke="#000000"
stroke-width="1.5"
stroke-linejoin="round"
stroke-miterlimit="10"
fill="none"
id="path512" />
</g>
<g
clip-path="url(#clip4)"
id="g518">
<rect
x="896"
y="487"
width="327"
height="255"
stroke="#000000"
stroke-width="1.5"
stroke-linejoin="round"
stroke-miterlimit="10"
fill="none"
id="rect516" />
</g>
<g
clip-path="url(#clip5)"
id="g522">
<path
d="m 896,699 h 327"
stroke="#d9d9d9"
stroke-width="1.5"
stroke-linejoin="round"
stroke-miterlimit="10"
fill="none"
fill-rule="evenodd"
id="path520" />
</g>
<g
clip-path="url(#clip6)"
id="g526">
<path
d="m 1223.5,699.757 c -106.28,0.155 -267.068,0.142 -318.847,0.465 -4.153,0.026 7.957,1.423 8.176,1.476 2.725,0.654 5.45,1.519 8.176,2.453 2.725,0.934 5.45,2.074 8.175,3.15 2.725,1.076 5.45,2.307 8.176,3.308 2.725,1 5.45,2.053 8.175,2.697 2.725,0.645 5.451,1.194 8.176,1.167 2.725,-0.027 5.45,-0.324 8.175,-1.329 2.725,-1.005 5.451,-2.464 8.176,-4.698 2.725,-2.235 5.45,-5.09 8.175,-8.709 2.726,-3.62 5.451,-7.982 8.176,-13.006 2.725,-5.024 5.45,-10.845 8.176,-17.138 2.725,-6.294 5.445,-13.353 8.175,-20.62 2.73,-7.267 5.45,-15.178 8.18,-22.982 2.72,-7.804 5.45,-16.042 8.17,-23.841 2.73,-7.799 5.45,-15.752 8.18,-22.952 2.72,-7.2 5.45,-14.231 8.17,-20.25 2.73,-6.018 5.45,-11.534 8.18,-15.863 2.72,-4.329 5.45,-7.848 8.17,-10.112 2.73,-2.264 5.45,-3.473 8.18,-3.473 2.72,0 5.45,1.209 8.17,3.473 2.73,2.264 5.45,5.783 8.18,10.112 2.73,4.329 5.45,9.845 8.18,15.863 2.72,6.019 5.45,13.05 8.17,20.25 2.73,7.2 5.45,15.153 8.18,22.952 2.72,7.799 5.45,16.037 8.17,23.841 2.73,7.804 5.45,15.715 8.18,22.982 2.72,7.267 5.45,14.326 8.17,20.62 2.73,6.293 5.45,12.114 8.18,17.138 2.72,5.024 5.45,9.386 8.17,13.006 2.73,3.619 5.45,6.474 8.18,8.709 2.73,2.234 5.45,3.693 8.18,4.698 2.72,1.005 5.45,1.302 8.17,1.329 2.73,0.027 5.45,-0.522 8.18,-1.167 2.72,-0.644 5.45,-1.697 8.17,-2.697 2.73,-1.001 5.45,-2.232 8.18,-3.308 2.72,-1.076 5.45,-2.216 8.17,-3.15 2.73,-0.934 5.45,-1.799 8.18,-2.453 2.72,-0.655 5.45,-1.152 8.17,-1.476 2.73,-0.323 5.45,-0.31 8.18,-0.465"
stroke="#000000"
stroke-width="4.5"
stroke-linecap="round"
stroke-linejoin="round"
stroke-miterlimit="10"
fill="none"
id="path524" />
</g>
<g
clip-path="url(#clip7)"
id="g530">
<text
fill="#595959"
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="16px"
transform="translate(1238.27,746)"
id="text528">-0.25</text>
</g>
<g
clip-path="url(#clip8)"
id="g534">
<text
fill="#595959"
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="16px"
transform="translate(1238.27,704)"
id="text532">0</text>
</g>
<g
clip-path="url(#clip9)"
id="g538">
<text
fill="#595959"
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="16px"
transform="translate(1238.27,661)"
id="text536">0.25</text>
</g>
<g
clip-path="url(#clip10)"
id="g542">
<text
fill="#595959"
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="16px"
transform="translate(1238.27,619)"
id="text540">0.5</text>
</g>
<g
clip-path="url(#clip11)"
id="g546">
<text
fill="#595959"
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="16px"
transform="translate(1238.27,576)"
id="text544">0.75</text>
</g>
<g
clip-path="url(#clip12)"
id="g550">
<text
fill="#595959"
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="16px"
transform="translate(1238.27,534)"
id="text548">1</text>
</g>
<g
clip-path="url(#clip13)"
id="g554">
<text
fill="#595959"
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="16px"
transform="translate(1238.27,491)"
id="text552">1.25</text>
</g>
<g
clip-path="url(#clip14)"
id="g558">
<text
fill="#595959"
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="16px"
transform="translate(879.36,767)"
id="text556">-2.00</text>
</g>
<g
clip-path="url(#clip15)"
id="g562">
<text
fill="#595959"
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="16px"
transform="translate(920.288,767)"
id="text560">-1.50</text>
</g>
<g
clip-path="url(#clip16)"
id="g566">
<text
fill="#595959"
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="16px"
transform="translate(961.217,767)"
id="text564">-1.00</text>
</g>
<g
clip-path="url(#clip17)"
id="g570">
<text
fill="#595959"
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="16px"
transform="translate(1002.15,767)"
id="text568">-0.50</text>
</g>
<g
clip-path="url(#clip18)"
id="g574">
<text
fill="#595959"
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="16px"
transform="translate(1045.52,767)"
id="text572">0.00</text>
</g>
<g
clip-path="url(#clip19)"
id="g578">
<text
fill="#595959"
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="16px"
transform="translate(1086.45,767)"
id="text576">0.50</text>
</g>
<g
clip-path="url(#clip20)"
id="g582">
<text
fill="#595959"
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="16px"
transform="translate(1127.38,767)"
id="text580">1.00</text>
</g>
<g
clip-path="url(#clip21)"
id="g586">
<text
fill="#595959"
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="16px"
transform="translate(1168.31,767)"
id="text584">1.50</text>
</g>
<g
clip-path="url(#clip22)"
id="g590">
<text
fill="#595959"
font-family="Calibri, Calibri_MSFontService, sans-serif"
font-weight="400"
font-size="16px"
transform="translate(1209.24,767)"
id="text588">2.00</text>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 24 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 32 KiB

@ -0,0 +1,11 @@
include(common)
add_library(winpixeventruntimelib SHARED IMPORTED GLOBAL)
set_property(TARGET winpixeventruntimelib PROPERTY IMPORTED_IMPLIB ${CMAKE_CURRENT_SOURCE_DIR}/WinPixEventRuntime.lib)
set(WINPIXEVENT_BIN
"${CMAKE_CURRENT_SOURCE_DIR}/WinPixEventRuntime.dll"
)
copyTargetCommand("${WINPIXEVENT_BIN}" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} copied_winpixevent_bin)
add_dependencies(winpixeventruntimelib copied_winpixevent_bin)

@ -0,0 +1,531 @@
/*==========================================================================;
*
* Copyright (C) Microsoft Corporation. All Rights Reserved.
*
* File: PIXEvents.h
* Content: PIX include file
* Don't include this file directly - use pix3.h
*
****************************************************************************/
#pragma once
#ifndef _PixEvents_H_
#define _PixEvents_H_
#ifndef _PIX3_H_
# error Do not include this file directly - use pix3.h
#endif
#include "PIXEventsCommon.h"
#if defined(XBOX) || defined(_XBOX_ONE) || defined(_DURANGO)
# define PIX_XBOX
#endif
#if _MSC_VER < 1800
# error This version of pix3.h is only supported on Visual Studio 2013 or higher
#elif _MSC_VER < 1900
# ifndef constexpr // Visual Studio 2013 doesn't support constexpr
# define constexpr
# define PIX3__DEFINED_CONSTEXPR
# endif
#endif
namespace PIXEventsDetail
{
template<typename... ARGS>
struct PIXEventTypeInferer
{
static constexpr PIXEventType Begin() { return PIXEvent_BeginEvent_VarArgs; }
static constexpr PIXEventType SetMarker() { return PIXEvent_SetMarker_VarArgs; }
static constexpr PIXEventType BeginOnContext() { return PIXEvent_BeginEvent_OnContext_VarArgs; }
static constexpr PIXEventType SetMarkerOnContext() { return PIXEvent_SetMarker_OnContext_VarArgs; }
// Xbox and Windows store different types of events for context events.
// On Xbox these include a context argument, while on Windows they do
// not. It is important not to change the event types used on the
// Windows version as there are OS components (eg debug layer & DRED)
// that decode event structs.
#ifdef PIX_XBOX
static constexpr PIXEventType GpuBeginOnContext() { return PIXEvent_BeginEvent_OnContext_VarArgs; }
static constexpr PIXEventType GpuSetMarkerOnContext() { return PIXEvent_SetMarker_OnContext_VarArgs; }
#else
static constexpr PIXEventType GpuBeginOnContext() { return PIXEvent_BeginEvent_VarArgs; }
static constexpr PIXEventType GpuSetMarkerOnContext() { return PIXEvent_SetMarker_VarArgs; }
#endif
};
template<>
struct PIXEventTypeInferer<void>
{
static constexpr PIXEventType Begin() { return PIXEvent_BeginEvent_NoArgs; }
static constexpr PIXEventType SetMarker() { return PIXEvent_SetMarker_NoArgs; }
static constexpr PIXEventType BeginOnContext() { return PIXEvent_BeginEvent_OnContext_NoArgs; }
static constexpr PIXEventType SetMarkerOnContext() { return PIXEvent_SetMarker_OnContext_NoArgs; }
#ifdef PIX_XBOX
static constexpr PIXEventType GpuBeginOnContext() { return PIXEvent_BeginEvent_OnContext_NoArgs; }
static constexpr PIXEventType GpuSetMarkerOnContext() { return PIXEvent_SetMarker_OnContext_NoArgs; }
#else
static constexpr PIXEventType GpuBeginOnContext() { return PIXEvent_BeginEvent_NoArgs; }
static constexpr PIXEventType GpuSetMarkerOnContext() { return PIXEvent_SetMarker_NoArgs; }
#endif
};
inline void PIXCopyEventArguments(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit)
{
// nothing
}
template<typename ARG, typename... ARGS>
void PIXCopyEventArguments(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, ARG const& arg, ARGS const&... args)
{
PIXCopyEventArgument(destination, limit, arg);
PIXCopyEventArguments(destination, limit, args...);
}
template<typename STR, typename... ARGS>
__declspec(noinline) void PIXBeginEventAllocate(PIXEventsThreadInfo* threadInfo, UINT64 color, STR formatString, ARGS... args)
{
UINT64 time = PIXEventsReplaceBlock(threadInfo, false);
if (!time)
return;
UINT64* destination = threadInfo->destination;
UINT64* limit = threadInfo->biasedLimit;
if (destination >= limit)
return;
limit += PIXEventsSafeFastCopySpaceQwords;
*destination++ = PIXEncodeEventInfo(time, PIXEventTypeInferer<ARGS...>::Begin());
*destination++ = color;
PIXCopyEventArguments(destination, limit, formatString, args...);
*destination = PIXEventsBlockEndMarker;
threadInfo->destination = destination;
}
template<typename STR, typename... ARGS>
void PIXBeginEvent(UINT64 color, STR formatString, ARGS... args)
{
PIXEventsThreadInfo* threadInfo = PIXGetThreadInfo();
UINT64* destination = threadInfo->destination;
UINT64* limit = threadInfo->biasedLimit;
if (destination < limit)
{
limit += PIXEventsSafeFastCopySpaceQwords;
UINT64 time = PIXGetTimestampCounter();
*destination++ = PIXEncodeEventInfo(time, PIXEventTypeInferer<ARGS...>::Begin());
*destination++ = color;
PIXCopyEventArguments(destination, limit, formatString, args...);
*destination = PIXEventsBlockEndMarker;
threadInfo->destination = destination;
}
else if (limit != nullptr)
{
PIXBeginEventAllocate(threadInfo, color, formatString);
}
}
template<typename STR, typename... ARGS>
__declspec(noinline) void PIXSetMarkerAllocate(PIXEventsThreadInfo* threadInfo, UINT64 color, STR formatString, ARGS... args)
{
UINT64 time = PIXEventsReplaceBlock(threadInfo, false);
if (!time)
return;
UINT64* destination = threadInfo->destination;
UINT64* limit = threadInfo->biasedLimit;
if (destination >= limit)
return;
limit += PIXEventsSafeFastCopySpaceQwords;
*destination++ = PIXEncodeEventInfo(time, PIXEventTypeInferer<ARGS...>::SetMarker());
*destination++ = color;
PIXCopyEventArguments(destination, limit, formatString, args...);
*destination = PIXEventsBlockEndMarker;
threadInfo->destination = destination;
}
template<typename STR, typename... ARGS>
void PIXSetMarker(UINT64 color, STR formatString, ARGS... args)
{
PIXEventsThreadInfo* threadInfo = PIXGetThreadInfo();
UINT64* destination = threadInfo->destination;
UINT64* limit = threadInfo->biasedLimit;
if (destination < limit)
{
limit += PIXEventsSafeFastCopySpaceQwords;
UINT64 time = PIXGetTimestampCounter();
*destination++ = PIXEncodeEventInfo(time, PIXEventTypeInferer<ARGS...>::SetMarker());
*destination++ = color;
PIXCopyEventArguments(destination, limit, formatString, args...);
*destination = PIXEventsBlockEndMarker;
threadInfo->destination = destination;
}
else if (limit != nullptr)
{
PIXSetMarkerAllocate(threadInfo, color, formatString, args...);
}
}
#if !PIX_XBOX
template<typename STR, typename... ARGS>
__declspec(noinline) void PIXBeginEventOnContextCpuAllocate(PIXEventsThreadInfo* threadInfo, void* context, UINT64 color, STR formatString, ARGS... args)
{
UINT64 time = PIXEventsReplaceBlock(threadInfo, false);
if (!time)
return;
UINT64* destination = threadInfo->destination;
UINT64* limit = threadInfo->biasedLimit;
if (destination >= limit)
return;
limit += PIXEventsSafeFastCopySpaceQwords;
*destination++ = PIXEncodeEventInfo(time, PIXEventTypeInferer<ARGS...>::BeginOnContext());
*destination++ = color;
PIXCopyEventArguments(destination, limit, context, formatString, args...);
*destination = PIXEventsBlockEndMarker;
threadInfo->destination = destination;
}
template<typename STR, typename... ARGS>
void PIXBeginEventOnContextCpu(void* context, UINT64 color, STR formatString, ARGS... args)
{
PIXEventsThreadInfo* threadInfo = PIXGetThreadInfo();
UINT64* destination = threadInfo->destination;
UINT64* limit = threadInfo->biasedLimit;
if (destination < limit)
{
limit += PIXEventsSafeFastCopySpaceQwords;
UINT64 time = PIXGetTimestampCounter();
*destination++ = PIXEncodeEventInfo(time, PIXEventTypeInferer<ARGS...>::BeginOnContext());
*destination++ = color;
PIXCopyEventArguments(destination, limit, context, formatString, args...);
*destination = PIXEventsBlockEndMarker;
threadInfo->destination = destination;
}
else if (limit != nullptr)
{
PIXBeginEventOnContextCpuAllocate(threadInfo, context, color, formatString, args...);
}
}
#endif
template<typename CONTEXT, typename STR, typename... ARGS>
void PIXBeginEvent(CONTEXT* context, UINT64 color, STR formatString, ARGS... args)
{
#if PIX_XBOX
PIXBeginEvent(color, formatString, args...);
#else
PIXBeginEventOnContextCpu(context, color, formatString, args...);
#endif
// TODO: we've already encoded this once for the CPU event - figure out way to avoid doing it again
UINT64 buffer[PIXEventsGraphicsRecordSpaceQwords];
UINT64* destination = buffer;
UINT64* limit = buffer + PIXEventsGraphicsRecordSpaceQwords - PIXEventsReservedTailSpaceQwords;
*destination++ = PIXEncodeEventInfo(0, PIXEventTypeInferer<ARGS...>::GpuBeginOnContext());
*destination++ = color;
PIXCopyEventArguments(destination, limit, formatString, args...);
*destination = 0ull;
PIXBeginGPUEventOnContext(context, static_cast<void*>(buffer), static_cast<UINT>(reinterpret_cast<BYTE*>(destination) - reinterpret_cast<BYTE*>(buffer)));
}
#if !PIX_XBOX
template<typename STR, typename... ARGS>
__declspec(noinline) void PIXSetMarkerOnContextCpuAllocate(PIXEventsThreadInfo* threadInfo, void* context, UINT64 color, STR formatString, ARGS... args)
{
UINT64 time = PIXEventsReplaceBlock(threadInfo, false);
if (!time)
return;
UINT64* destination = threadInfo->destination;
UINT64* limit = threadInfo->biasedLimit;
if (destination >= limit)
return;
limit += PIXEventsSafeFastCopySpaceQwords;
*destination++ = PIXEncodeEventInfo(time, PIXEventTypeInferer<ARGS...>::SetMarkerOnContext());
*destination++ = color;
PIXCopyEventArguments(destination, limit, context, formatString, args...);
*destination = PIXEventsBlockEndMarker;
threadInfo->destination = destination;
}
template<typename STR, typename... ARGS>
void PIXSetMarkerOnContextCpu(void* context, UINT64 color, STR formatString, ARGS... args)
{
PIXEventsThreadInfo* threadInfo = PIXGetThreadInfo();
UINT64* destination = threadInfo->destination;
UINT64* limit = threadInfo->biasedLimit;
if (destination < limit)
{
limit += PIXEventsSafeFastCopySpaceQwords;
UINT64 time = PIXGetTimestampCounter();
*destination++ = PIXEncodeEventInfo(time, PIXEventTypeInferer<ARGS...>::SetMarkerOnContext());
*destination++ = color;
PIXCopyEventArguments(destination, limit, context, formatString, args...);
*destination = PIXEventsBlockEndMarker;
threadInfo->destination = destination;
}
else if (limit != nullptr)
{
PIXSetMarkerOnContextCpuAllocate(threadInfo, context, color, formatString, args...);
}
}
#endif
template<typename CONTEXT, typename STR, typename... ARGS>
void PIXSetMarker(CONTEXT* context, UINT64 color, STR formatString, ARGS... args)
{
#if PIX_XBOX
PIXSetMarker(color, formatString, args...);
#else
PIXSetMarkerOnContextCpu(context, color, formatString, args...);
#endif
UINT64 buffer[PIXEventsGraphicsRecordSpaceQwords];
UINT64* destination = buffer;
UINT64* limit = buffer + PIXEventsGraphicsRecordSpaceQwords - PIXEventsReservedTailSpaceQwords;
*destination++ = PIXEncodeEventInfo(0, PIXEventTypeInferer<ARGS...>::GpuSetMarkerOnContext());
*destination++ = color;
PIXCopyEventArguments(destination, limit, formatString, args...);
*destination = 0ull;
PIXSetGPUMarkerOnContext(context, static_cast<void*>(buffer), static_cast<UINT>(reinterpret_cast<BYTE*>(destination) - reinterpret_cast<BYTE*>(buffer)));
}
__declspec(noinline) inline void PIXEndEventAllocate(PIXEventsThreadInfo* threadInfo)
{
UINT64 time = PIXEventsReplaceBlock(threadInfo, true);
if (!time)
return;
UINT64* destination = threadInfo->destination;
UINT64* limit = threadInfo->biasedLimit;
if (destination >= limit)
return;
limit += PIXEventsSafeFastCopySpaceQwords;
*destination++ = PIXEncodeEventInfo(time, PIXEvent_EndEvent);
*destination = PIXEventsBlockEndMarker;
threadInfo->destination = destination;
}
inline void PIXEndEvent()
{
PIXEventsThreadInfo* threadInfo = PIXGetThreadInfo();
UINT64* destination = threadInfo->destination;
UINT64* limit = threadInfo->biasedLimit;
if (destination < limit)
{
limit += PIXEventsSafeFastCopySpaceQwords;
UINT64 time = PIXGetTimestampCounter();
*destination++ = PIXEncodeEventInfo(time, PIXEvent_EndEvent);
*destination = PIXEventsBlockEndMarker;
threadInfo->destination = destination;
}
else if (limit != nullptr)
{
PIXEndEventAllocate(threadInfo);
}
}
#if !PIX_XBOX
__declspec(noinline) inline void PIXEndEventOnContextCpuAllocate(PIXEventsThreadInfo* threadInfo, void* context)
{
UINT64 time = PIXEventsReplaceBlock(threadInfo, true);
if (!time)
return;
UINT64* destination = threadInfo->destination;
UINT64* limit = threadInfo->biasedLimit;
if (destination >= limit)
return;
limit += PIXEventsSafeFastCopySpaceQwords;
*destination++ = PIXEncodeEventInfo(time, PIXEvent_EndEvent_OnContext);
PIXCopyEventArgument(destination, limit, context);
*destination = PIXEventsBlockEndMarker;
threadInfo->destination = destination;
}
inline void PIXEndEventOnContextCpu(void* context)
{
PIXEventsThreadInfo* threadInfo = PIXGetThreadInfo();
UINT64* destination = threadInfo->destination;
UINT64* limit = threadInfo->biasedLimit;
if (destination < limit)
{
limit += PIXEventsSafeFastCopySpaceQwords;
UINT64 time = PIXGetTimestampCounter();
*destination++ = PIXEncodeEventInfo(time, PIXEvent_EndEvent_OnContext);
PIXCopyEventArgument(destination, limit, context);
*destination = PIXEventsBlockEndMarker;
threadInfo->destination = destination;
}
else if (limit != nullptr)
{
PIXEndEventOnContextCpuAllocate(threadInfo, context);
}
}
#endif
template<typename CONTEXT>
void PIXEndEvent(CONTEXT* context)
{
#if PIX_XBOX
PIXEndEvent();
#else
PIXEndEventOnContextCpu(context);
#endif
PIXEndGPUEventOnContext(context);
}
}
template<typename... ARGS>
void PIXBeginEvent(UINT64 color, PCWSTR formatString, ARGS... args)
{
PIXEventsDetail::PIXBeginEvent(color, formatString, args...);
}
template<typename... ARGS>
void PIXBeginEvent(UINT64 color, PCSTR formatString, ARGS... args)
{
PIXEventsDetail::PIXBeginEvent(color, formatString, args...);
}
template<typename... ARGS>
void PIXSetMarker(UINT64 color, PCWSTR formatString, ARGS... args)
{
PIXEventsDetail::PIXSetMarker(color, formatString, args...);
}
template<typename... ARGS>
void PIXSetMarker(UINT64 color, PCSTR formatString, ARGS... args)
{
PIXEventsDetail::PIXSetMarker(color, formatString, args...);
}
template<typename CONTEXT, typename... ARGS>
void PIXBeginEvent(CONTEXT* context, UINT64 color, PCWSTR formatString, ARGS... args)
{
PIXEventsDetail::PIXBeginEvent(context, color, formatString, args...);
}
template<typename CONTEXT, typename... ARGS>
void PIXBeginEvent(CONTEXT* context, UINT64 color, PCSTR formatString, ARGS... args)
{
PIXEventsDetail::PIXBeginEvent(context, color, formatString, args...);
}
template<typename CONTEXT, typename... ARGS>
void PIXSetMarker(CONTEXT* context, UINT64 color, PCWSTR formatString, ARGS... args)
{
PIXEventsDetail::PIXSetMarker(context, color, formatString, args...);
}
template<typename CONTEXT, typename... ARGS>
void PIXSetMarker(CONTEXT* context, UINT64 color, PCSTR formatString, ARGS... args)
{
PIXEventsDetail::PIXSetMarker(context, color, formatString, args...);
}
inline void PIXEndEvent()
{
PIXEventsDetail::PIXEndEvent();
}
template<typename CONTEXT>
void PIXEndEvent(CONTEXT* context)
{
PIXEventsDetail::PIXEndEvent(context);
}
template<typename CONTEXT>
class PIXScopedEventObject
{
CONTEXT* m_context;
public:
template<typename... ARGS>
PIXScopedEventObject(CONTEXT* context, UINT64 color, PCWSTR formatString, ARGS... args)
: m_context(context)
{
PIXBeginEvent(context, color, formatString, args...);
}
template<typename... ARGS>
PIXScopedEventObject(CONTEXT* context, UINT64 color, PCSTR formatString, ARGS... args)
: m_context(context)
{
PIXBeginEvent(context, color, formatString, args...);
}
~PIXScopedEventObject()
{
PIXEndEvent(m_context);
}
};
template<>
class PIXScopedEventObject<void>
{
public:
template<typename... ARGS>
PIXScopedEventObject(UINT64 color, PCWSTR formatString, ARGS... args)
{
PIXBeginEvent(color, formatString, args...);
}
template<typename... ARGS>
PIXScopedEventObject(UINT64 color, PCSTR formatString, ARGS... args)
{
PIXBeginEvent(color, formatString, args...);
}
~PIXScopedEventObject()
{
PIXEndEvent();
}
};
#define PIXConcatenate(a, b) a ## b
#define PIXGetScopedEventVariableName(a, b) PIXConcatenate(a, b)
#define PIXScopedEvent(context, ...) PIXScopedEventObject<PIXInferScopedEventType<decltype(context)>::Type> PIXGetScopedEventVariableName(pixEvent, __LINE__)(context, __VA_ARGS__)
#ifdef PIX3__DEFINED_CONSTEXPR
#undef constexpr
#undef PIX3__DEFINED_CONSTEXPR
#endif
#endif // _PIXEvents_H__

@ -0,0 +1,587 @@
/*==========================================================================;
*
* Copyright (C) Microsoft Corporation. All Rights Reserved.
*
* File: PIXEventsCommon.h
* Content: PIX include file
* Don't include this file directly - use pix3.h
*
****************************************************************************/
#pragma once
#ifndef _PIXEventsCommon_H_
#define _PIXEventsCommon_H_
#include <cstdint>
#if defined(_M_X64) || defined(_M_IX86)
#include <emmintrin.h>
#endif
//
// The PIXBeginEvent and PIXSetMarker functions have an optimized path for
// copying strings that work by copying 128-bit or 64-bits at a time. In some
// circumstances this may result in PIX logging the remaining memory after the
// null terminator.
//
// By default this optimization is enabled unless Address Sanitizer is enabled,
// since this optimization can trigger a global-buffer-overflow when copying
// string literals.
//
// The PIX_ENABLE_BLOCK_ARGUMENT_COPY controls whether or not this optimization
// is enabled. Applications may also explicitly set this macro to 0 to disable
// the optimization if necessary.
//
#if defined(PIX_ENABLE_BLOCK_ARGUMENT_COPY)
// Previously set values override everything
# define PIX_ENABLE_BLOCK_ARGUMENT_COPY_SET 0
#elif defined(__has_feature)
# if __has_feature(address_sanitizer)
// Disable block argument copy when address sanitizer is enabled
# define PIX_ENABLE_BLOCK_ARGUMENT_COPY 0
# define PIX_ENABLE_BLOCK_ARGUMENT_COPY_SET 1
# endif
#endif
#if !defined(PIX_ENABLE_BLOCK_ARGUMENT_COPY)
// Default to enabled.
# define PIX_ENABLE_BLOCK_ARGUMENT_COPY 1
# define PIX_ENABLE_BLOCK_ARGUMENT_COPY_SET 1
#endif
struct PIXEventsBlockInfo;
struct PIXEventsThreadInfo
{
PIXEventsBlockInfo* block;
UINT64* biasedLimit;
UINT64* destination;
};
extern "C" UINT64 WINAPI PIXEventsReplaceBlock(PIXEventsThreadInfo* threadInfo, bool getEarliestTime) noexcept;
enum PIXEventType
{
PIXEvent_EndEvent = 0x000,
PIXEvent_BeginEvent_VarArgs = 0x001,
PIXEvent_BeginEvent_NoArgs = 0x002,
PIXEvent_SetMarker_VarArgs = 0x007,
PIXEvent_SetMarker_NoArgs = 0x008,
PIXEvent_EndEvent_OnContext = 0x010,
PIXEvent_BeginEvent_OnContext_VarArgs = 0x011,
PIXEvent_BeginEvent_OnContext_NoArgs = 0x012,
PIXEvent_SetMarker_OnContext_VarArgs = 0x017,
PIXEvent_SetMarker_OnContext_NoArgs = 0x018,
};
static const UINT64 PIXEventsReservedRecordSpaceQwords = 64;
//this is used to make sure SSE string copy always will end 16-byte write in the current block
//this way only a check if destination < limit can be performed, instead of destination < limit - 1
//since both these are UINT64* and SSE writes in 16 byte chunks, 8 bytes are kept in reserve
//so even if SSE overwrites 8 extra bytes, those will still belong to the correct block
//on next iteration check destination will be greater than limit
//this is used as well for fixed size UMD events and PIXEndEvent since these require less space
//than other variable length user events and do not need big reserved space
static const UINT64 PIXEventsReservedTailSpaceQwords = 2;
static const UINT64 PIXEventsSafeFastCopySpaceQwords = PIXEventsReservedRecordSpaceQwords - PIXEventsReservedTailSpaceQwords;
static const UINT64 PIXEventsGraphicsRecordSpaceQwords = 64;
//Bits 7-19 (13 bits)
static const UINT64 PIXEventsBlockEndMarker = 0x00000000000FFF80;
//Bits 10-19 (10 bits)
static const UINT64 PIXEventsTypeReadMask = 0x00000000000FFC00;
static const UINT64 PIXEventsTypeWriteMask = 0x00000000000003FF;
static const UINT64 PIXEventsTypeBitShift = 10;
//Bits 20-63 (44 bits)
static const UINT64 PIXEventsTimestampReadMask = 0xFFFFFFFFFFF00000;
static const UINT64 PIXEventsTimestampWriteMask = 0x00000FFFFFFFFFFF;
static const UINT64 PIXEventsTimestampBitShift = 20;
inline UINT64 PIXEncodeEventInfo(UINT64 timestamp, PIXEventType eventType)
{
return ((timestamp & PIXEventsTimestampWriteMask) << PIXEventsTimestampBitShift) |
(((UINT64)eventType & PIXEventsTypeWriteMask) << PIXEventsTypeBitShift);
}
//Bits 60-63 (4)
static const UINT64 PIXEventsStringAlignmentWriteMask = 0x000000000000000F;
static const UINT64 PIXEventsStringAlignmentReadMask = 0xF000000000000000;
static const UINT64 PIXEventsStringAlignmentBitShift = 60;
//Bits 55-59 (5)
static const UINT64 PIXEventsStringCopyChunkSizeWriteMask = 0x000000000000001F;
static const UINT64 PIXEventsStringCopyChunkSizeReadMask = 0x0F80000000000000;
static const UINT64 PIXEventsStringCopyChunkSizeBitShift = 55;
//Bit 54
static const UINT64 PIXEventsStringIsANSIWriteMask = 0x0000000000000001;
static const UINT64 PIXEventsStringIsANSIReadMask = 0x0040000000000000;
static const UINT64 PIXEventsStringIsANSIBitShift = 54;
//Bit 53
static const UINT64 PIXEventsStringIsShortcutWriteMask = 0x0000000000000001;
static const UINT64 PIXEventsStringIsShortcutReadMask = 0x0020000000000000;
static const UINT64 PIXEventsStringIsShortcutBitShift = 53;
inline UINT64 PIXEncodeStringInfo(UINT64 alignment, UINT64 copyChunkSize, BOOL isANSI, BOOL isShortcut)
{
return ((alignment & PIXEventsStringAlignmentWriteMask) << PIXEventsStringAlignmentBitShift) |
((copyChunkSize & PIXEventsStringCopyChunkSizeWriteMask) << PIXEventsStringCopyChunkSizeBitShift) |
(((UINT64)isANSI & PIXEventsStringIsANSIWriteMask) << PIXEventsStringIsANSIBitShift) |
(((UINT64)isShortcut & PIXEventsStringIsShortcutWriteMask) << PIXEventsStringIsShortcutBitShift);
}
template<UINT alignment, class T>
inline bool PIXIsPointerAligned(T* pointer)
{
return !(((UINT64)pointer) & (alignment - 1));
}
// Generic template version slower because of the additional clear write
template<class T>
inline void PIXCopyEventArgument(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, T argument)
{
if (destination < limit)
{
*destination = 0ull;
*((T*)destination) = argument;
++destination;
}
}
// int32 specialization to avoid slower double memory writes
template<>
inline void PIXCopyEventArgument<INT32>(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, INT32 argument)
{
if (destination < limit)
{
*reinterpret_cast<INT64*>(destination) = static_cast<INT64>(argument);
++destination;
}
}
// unsigned int32 specialization to avoid slower double memory writes
template<>
inline void PIXCopyEventArgument<UINT32>(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, UINT32 argument)
{
if (destination < limit)
{
*destination = static_cast<UINT64>(argument);
++destination;
}
}
// int64 specialization to avoid slower double memory writes
template<>
inline void PIXCopyEventArgument<INT64>(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, INT64 argument)
{
if (destination < limit)
{
*reinterpret_cast<INT64*>(destination) = argument;
++destination;
}
}
// unsigned int64 specialization to avoid slower double memory writes
template<>
inline void PIXCopyEventArgument<UINT64>(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, UINT64 argument)
{
if (destination < limit)
{
*destination = argument;
++destination;
}
}
//floats must be cast to double during writing the data to be properly printed later when reading the data
//this is needed because when float is passed to varargs function it's cast to double
template<>
inline void PIXCopyEventArgument<float>(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, float argument)
{
if (destination < limit)
{
*reinterpret_cast<double*>(destination) = static_cast<double>(argument);
++destination;
}
}
//char has to be cast to a longer signed integer type
//this is due to printf not ignoring correctly the upper bits of unsigned long long for a char format specifier
template<>
inline void PIXCopyEventArgument<char>(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, char argument)
{
if (destination < limit)
{
*reinterpret_cast<INT64*>(destination) = static_cast<INT64>(argument);
++destination;
}
}
//unsigned char has to be cast to a longer unsigned integer type
//this is due to printf not ignoring correctly the upper bits of unsigned long long for a char format specifier
template<>
inline void PIXCopyEventArgument<unsigned char>(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, unsigned char argument)
{
if (destination < limit)
{
*destination = static_cast<UINT64>(argument);
++destination;
}
}
//bool has to be cast to an integer since it's not explicitly supported by string format routines
//there's no format specifier for bool type, but it should work with integer format specifiers
template<>
inline void PIXCopyEventArgument<bool>(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, bool argument)
{
if (destination < limit)
{
*destination = static_cast<UINT64>(argument);
++destination;
}
}
inline void PIXCopyEventArgumentSlowest(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, _In_ PCSTR argument)
{
*destination++ = PIXEncodeStringInfo(0, 8, TRUE, FALSE);
while (destination < limit)
{
UINT64 c = static_cast<uint8_t>(argument[0]);
if (!c)
{
*destination++ = 0;
return;
}
UINT64 x = c;
c = static_cast<uint8_t>(argument[1]);
if (!c)
{
*destination++ = x;
return;
}
x |= c << 8;
c = static_cast<uint8_t>(argument[2]);
if (!c)
{
*destination++ = x;
return;
}
x |= c << 16;
c = static_cast<uint8_t>(argument[3]);
if (!c)
{
*destination++ = x;
return;
}
x |= c << 24;
c = static_cast<uint8_t>(argument[4]);
if (!c)
{
*destination++ = x;
return;
}
x |= c << 32;
c = static_cast<uint8_t>(argument[5]);
if (!c)
{
*destination++ = x;
return;
}
x |= c << 40;
c = static_cast<uint8_t>(argument[6]);
if (!c)
{
*destination++ = x;
return;
}
x |= c << 48;
c = static_cast<uint8_t>(argument[7]);
if (!c)
{
*destination++ = x;
return;
}
x |= c << 56;
*destination++ = x;
argument += 8;
}
}
inline void PIXCopyEventArgumentSlow(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, _In_ PCSTR argument)
{
#if PIX_ENABLE_BLOCK_ARGUMENT_COPY
if (PIXIsPointerAligned<8>(argument))
{
*destination++ = PIXEncodeStringInfo(0, 8, TRUE, FALSE);
UINT64* source = (UINT64*)argument;
while (destination < limit)
{
UINT64 qword = *source++;
*destination++ = qword;
//check if any of the characters is a terminating zero
if (!((qword & 0xFF00000000000000) &&
(qword & 0xFF000000000000) &&
(qword & 0xFF0000000000) &&
(qword & 0xFF00000000) &&
(qword & 0xFF000000) &&
(qword & 0xFF0000) &&
(qword & 0xFF00) &&
(qword & 0xFF)))
{
break;
}
}
}
else
#endif // PIX_ENABLE_BLOCK_ARGUMENT_COPY
{
PIXCopyEventArgumentSlowest(destination, limit, argument);
}
}
template<>
inline void PIXCopyEventArgument<PCSTR>(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, _In_ PCSTR argument)
{
if (destination < limit)
{
if (argument != nullptr)
{
#if (defined(_M_X64) || defined(_M_IX86)) && PIX_ENABLE_BLOCK_ARGUMENT_COPY
if (PIXIsPointerAligned<16>(argument))
{
*destination++ = PIXEncodeStringInfo(0, 16, TRUE, FALSE);
__m128i zero = _mm_setzero_si128();
if (PIXIsPointerAligned<16>(destination))
{
while (destination < limit)
{
__m128i mem = _mm_load_si128((__m128i*)argument);
_mm_store_si128((__m128i*)destination, mem);
//check if any of the characters is a terminating zero
__m128i res = _mm_cmpeq_epi8(mem, zero);
destination += 2;
if (_mm_movemask_epi8(res))
break;
argument += 16;
}
}
else
{
while (destination < limit)
{
__m128i mem = _mm_load_si128((__m128i*)argument);
_mm_storeu_si128((__m128i*)destination, mem);
//check if any of the characters is a terminating zero
__m128i res = _mm_cmpeq_epi8(mem, zero);
destination += 2;
if (_mm_movemask_epi8(res))
break;
argument += 16;
}
}
}
else
#endif // (defined(_M_X64) || defined(_M_IX86)) && PIX_ENABLE_BLOCK_ARGUMENT_COPY
{
PIXCopyEventArgumentSlow(destination, limit, argument);
}
}
else
{
*destination++ = 0ull;
}
}
}
template<>
inline void PIXCopyEventArgument<PSTR>(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, _In_ PSTR argument)
{
PIXCopyEventArgument(destination, limit, (PCSTR)argument);
}
inline void PIXCopyEventArgumentSlowest(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, _In_ PCWSTR argument)
{
*destination++ = PIXEncodeStringInfo(0, 8, FALSE, FALSE);
while (destination < limit)
{
UINT64 c = static_cast<uint16_t>(argument[0]);
if (!c)
{
*destination++ = 0;
return;
}
UINT64 x = c;
c = static_cast<uint16_t>(argument[1]);
if (!c)
{
*destination++ = x;
return;
}
x |= c << 16;
c = static_cast<uint16_t>(argument[2]);
if (!c)
{
*destination++ = x;
return;
}
x |= c << 32;
c = static_cast<uint16_t>(argument[3]);
if (!c)
{
*destination++ = x;
return;
}
x |= c << 48;
*destination++ = x;
argument += 4;
}
}
inline void PIXCopyEventArgumentSlow(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, _In_ PCWSTR argument)
{
#if PIX_ENABLE_BLOCK_ARGUMENT_COPY
if (PIXIsPointerAligned<8>(argument))
{
*destination++ = PIXEncodeStringInfo(0, 8, FALSE, FALSE);
UINT64* source = (UINT64*)argument;
while (destination < limit)
{
UINT64 qword = *source++;
*destination++ = qword;
//check if any of the characters is a terminating zero
//TODO: check if reversed condition is faster
if (!((qword & 0xFFFF000000000000) &&
(qword & 0xFFFF00000000) &&
(qword & 0xFFFF0000) &&
(qword & 0xFFFF)))
{
break;
}
}
}
else
#endif // PIX_ENABLE_BLOCK_ARGUMENT_COPY
{
PIXCopyEventArgumentSlowest(destination, limit, argument);
}
}
template<>
inline void PIXCopyEventArgument<PCWSTR>(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, _In_ PCWSTR argument)
{
if (destination < limit)
{
if (argument != nullptr)
{
#if (defined(_M_X64) || defined(_M_IX86)) && PIX_ENABLE_BLOCK_ARGUMENT_COPY
if (PIXIsPointerAligned<16>(argument))
{
*destination++ = PIXEncodeStringInfo(0, 16, FALSE, FALSE);
__m128i zero = _mm_setzero_si128();
if (PIXIsPointerAligned<16>(destination))
{
while (destination < limit)
{
__m128i mem = _mm_load_si128((__m128i*)argument);
_mm_store_si128((__m128i*)destination, mem);
//check if any of the characters is a terminating zero
__m128i res = _mm_cmpeq_epi16(mem, zero);
destination += 2;
if (_mm_movemask_epi8(res))
break;
argument += 8;
}
}
else
{
while (destination < limit)
{
__m128i mem = _mm_load_si128((__m128i*)argument);
_mm_storeu_si128((__m128i*)destination, mem);
//check if any of the characters is a terminating zero
__m128i res = _mm_cmpeq_epi16(mem, zero);
destination += 2;
if (_mm_movemask_epi8(res))
break;
argument += 8;
}
}
}
else
#endif // (defined(_M_X64) || defined(_M_IX86)) && PIX_ENABLE_BLOCK_ARGUMENT_COPY
{
PIXCopyEventArgumentSlow(destination, limit, argument);
}
}
else
{
*destination++ = 0ull;
}
}
}
template<>
inline void PIXCopyEventArgument<PWSTR>(_Out_writes_to_ptr_(limit) UINT64*& destination, _In_ const UINT64* limit, _In_ PWSTR argument)
{
PIXCopyEventArgument(destination, limit, (PCWSTR)argument);
};
#if defined(__d3d12_x_h__) || defined(__d3d12_h__)
inline void PIXSetGPUMarkerOnContext(_In_ ID3D12GraphicsCommandList* commandList, _In_reads_bytes_(size) void* data, UINT size)
{
commandList->SetMarker(D3D12_EVENT_METADATA, data, size);
}
inline void PIXSetGPUMarkerOnContext(_In_ ID3D12CommandQueue* commandQueue, _In_reads_bytes_(size) void* data, UINT size)
{
commandQueue->SetMarker(D3D12_EVENT_METADATA, data, size);
}
inline void PIXBeginGPUEventOnContext(_In_ ID3D12GraphicsCommandList* commandList, _In_reads_bytes_(size) void* data, UINT size)
{
commandList->BeginEvent(D3D12_EVENT_METADATA, data, size);
}
inline void PIXBeginGPUEventOnContext(_In_ ID3D12CommandQueue* commandQueue, _In_reads_bytes_(size) void* data, UINT size)
{
commandQueue->BeginEvent(D3D12_EVENT_METADATA, data, size);
}
inline void PIXEndGPUEventOnContext(_In_ ID3D12GraphicsCommandList* commandList)
{
commandList->EndEvent();
}
inline void PIXEndGPUEventOnContext(_In_ ID3D12CommandQueue* commandQueue)
{
commandQueue->EndEvent();
}
#endif //__d3d12_x_h__
template<class T> struct PIXInferScopedEventType { typedef T Type; };
template<class T> struct PIXInferScopedEventType<const T> { typedef T Type; };
template<class T> struct PIXInferScopedEventType<T*> { typedef T Type; };
template<class T> struct PIXInferScopedEventType<T* const> { typedef T Type; };
template<> struct PIXInferScopedEventType<UINT64> { typedef void Type; };
template<> struct PIXInferScopedEventType<const UINT64> { typedef void Type; };
template<> struct PIXInferScopedEventType<INT64> { typedef void Type; };
template<> struct PIXInferScopedEventType<const INT64> { typedef void Type; };
template<> struct PIXInferScopedEventType<UINT> { typedef void Type; };
template<> struct PIXInferScopedEventType<const UINT> { typedef void Type; };
template<> struct PIXInferScopedEventType<INT> { typedef void Type; };
template<> struct PIXInferScopedEventType<const INT> { typedef void Type; };
#if PIX_ENABLE_BLOCK_ARGUMENT_COPY_SET
#undef PIX_ENABLE_BLOCK_ARGUMENT_COPY
#endif
#undef PIX_ENABLE_BLOCK_ARGUMENT_COPY_SET
#endif //_PIXEventsCommon_H_

@ -0,0 +1,144 @@
/*==========================================================================;
*
* Copyright (C) Microsoft Corporation. All Rights Reserved.
*
* File: pix3.h
* Content: PIX include file
*
****************************************************************************/
#pragma once
#ifndef _PIX3_H_
#define _PIX3_H_
#include <sal.h>
#ifndef __cplusplus
#error "Only C++ files can include pix3.h. C is not supported."
#endif
#if !defined(USE_PIX_SUPPORTED_ARCHITECTURE)
#if defined(_M_X64) || defined(USE_PIX_ON_ALL_ARCHITECTURES) || defined(_M_ARM64)
#define USE_PIX_SUPPORTED_ARCHITECTURE
#endif
#endif
#if !defined(USE_PIX)
#if defined(USE_PIX_SUPPORTED_ARCHITECTURE) && (defined(_DEBUG) || DBG || defined(PROFILE) || defined(PROFILE_BUILD)) && !defined(_PREFAST_)
#define USE_PIX
#endif
#endif
#if defined(USE_PIX) && !defined(USE_PIX_SUPPORTED_ARCHITECTURE)
#pragma message("Warning: Pix markers are only supported on AMD64 and ARM64")
#endif
#if defined(XBOX) || defined(_XBOX_ONE) || defined(_DURANGO) || defined(_GAMING_XBOX)
#include "pix3_xbox.h"
#else
#include "pix3_win.h"
#endif
// These flags are used by both PIXBeginCapture and PIXGetCaptureState
#define PIX_CAPTURE_TIMING (1 << 0)
#define PIX_CAPTURE_GPU (1 << 1)
#define PIX_CAPTURE_FUNCTION_SUMMARY (1 << 2)
#define PIX_CAPTURE_FUNCTION_DETAILS (1 << 3)
#define PIX_CAPTURE_CALLGRAPH (1 << 4)
#define PIX_CAPTURE_INSTRUCTION_TRACE (1 << 5)
#define PIX_CAPTURE_SYSTEM_MONITOR_COUNTERS (1 << 6)
#define PIX_CAPTURE_VIDEO (1 << 7)
#define PIX_CAPTURE_AUDIO (1 << 8)
union PIXCaptureParameters
{
enum PIXCaptureStorage
{
Hybrid = 0,
Disk,
Memory,
};
struct GpuCaptureParameters
{
PVOID reserved;
} GpuCaptureParameters;
struct TimingCaptureParameters
{
PWSTR FileName;
UINT32 MaximumToolingMemorySizeMb;
PIXCaptureStorage CaptureStorage;
BOOL CaptureGpuTiming;
BOOL CaptureCallstacks;
BOOL CaptureCpuSamples;
UINT32 CpuSamplesPerSecond;
} TimingCaptureParameters;
};
typedef PIXCaptureParameters* PPIXCaptureParameters;
#if defined(USE_PIX) && defined(USE_PIX_SUPPORTED_ARCHITECTURE)
#define PIX_EVENTS_ARE_TURNED_ON
#include "PIXEventsCommon.h"
#include "PIXEvents.h"
// Starts a programmatically controlled capture.
// captureFlags uses the PIX_CAPTURE_* family of flags to specify the type of capture to take
extern "C" HRESULT WINAPI PIXBeginCapture1(DWORD captureFlags, _In_opt_ const PPIXCaptureParameters captureParameters);
inline HRESULT PIXBeginCapture(DWORD captureFlags, _In_opt_ const PPIXCaptureParameters captureParameters) { return PIXBeginCapture1(captureFlags, captureParameters); }
// Stops a programmatically controlled capture
// If discard == TRUE, the captured data is discarded
// If discard == FALSE, the captured data is saved
extern "C" HRESULT WINAPI PIXEndCapture(BOOL discard);
extern "C" DWORD WINAPI PIXGetCaptureState();
extern "C" void WINAPI PIXReportCounter(_In_ PCWSTR name, float value);
#else
// Eliminate these APIs when not using PIX
inline HRESULT PIXBeginCapture1(DWORD, _In_opt_ const PIXCaptureParameters*) { return S_OK; }
inline HRESULT PIXBeginCapture(DWORD, _In_opt_ const PIXCaptureParameters*) { return S_OK; }
inline HRESULT PIXEndCapture(BOOL) { return S_OK; }
inline DWORD PIXGetCaptureState() { return 0; }
inline void PIXReportCounter(_In_ PCWSTR, float) {}
inline void PIXNotifyWakeFromFenceSignal(_In_ HANDLE) {}
inline void PIXBeginEvent(UINT64, _In_ PCSTR, ...) {}
inline void PIXBeginEvent(UINT64, _In_ PCWSTR, ...) {}
inline void PIXBeginEvent(void*, UINT64, _In_ PCSTR, ...) {}
inline void PIXBeginEvent(void*, UINT64, _In_ PCWSTR, ...) {}
inline void PIXEndEvent() {}
inline void PIXEndEvent(void*) {}
inline void PIXSetMarker(UINT64, _In_ PCSTR, ...) {}
inline void PIXSetMarker(UINT64, _In_ PCWSTR, ...) {}
inline void PIXSetMarker(void*, UINT64, _In_ PCSTR, ...) {}
inline void PIXSetMarker(void*, UINT64, _In_ PCWSTR, ...) {}
inline void PIXScopedEvent(UINT64, _In_ PCSTR, ...) {}
inline void PIXScopedEvent(UINT64, _In_ PCWSTR, ...) {}
inline void PIXScopedEvent(void*, UINT64, _In_ PCSTR, ...) {}
inline void PIXScopedEvent(void*, UINT64, _In_ PCWSTR, ...) {}
// don't show warnings about expressions with no effect
#pragma warning(disable:4548)
#pragma warning(disable:4555)
#endif // USE_PIX
// Use these functions to specify colors to pass as metadata to a PIX event/marker API.
// Use PIX_COLOR() to specify a particular color for an event.
// Or, use PIX_COLOR_INDEX() to specify a set of unique event categories, and let PIX choose
// the colors to represent each category.
inline UINT PIX_COLOR(BYTE r, BYTE g, BYTE b) { return 0xff000000 | (r << 16) | (g << 8) | b; }
inline UINT PIX_COLOR_INDEX(BYTE i) { return i; }
const UINT PIX_COLOR_DEFAULT = PIX_COLOR_INDEX(0);
#endif // _PIX3_H_

@ -0,0 +1,48 @@
/*==========================================================================;
*
* Copyright (C) Microsoft Corporation. All Rights Reserved.
*
* File: PIX3_win.h
* Content: PIX include file
* Don't include this file directly - use pix3.h
*
****************************************************************************/
#pragma once
#ifndef _PIX3_H_
#error Don't include this file directly - use pix3.h
#endif
#ifndef _PIX3_WIN_H_
#define _PIX3_WIN_H_
// PIXEventsThreadInfo is defined in PIXEventsCommon.h
struct PIXEventsThreadInfo;
extern "C" PIXEventsThreadInfo* PIXGetThreadInfo() noexcept;
#if defined(USE_PIX) && defined(USE_PIX_SUPPORTED_ARCHITECTURE)
// Notifies PIX that an event handle was set as a result of a D3D12 fence being signaled.
// The event specified must have the same handle value as the handle
// used in ID3D12Fence::SetEventOnCompletion.
extern "C" void WINAPI PIXNotifyWakeFromFenceSignal(_In_ HANDLE event);
#endif
// The following defines denote the different metadata values that have been used
// by tools to denote how to parse pix marker event data. The first two values
// are legacy values.
#define WINPIX_EVENT_UNICODE_VERSION 0
#define WINPIX_EVENT_ANSI_VERSION 1
#define WINPIX_EVENT_PIX3BLOB_VERSION 2
#define D3D12_EVENT_METADATA WINPIX_EVENT_PIX3BLOB_VERSION
__forceinline UINT64 PIXGetTimestampCounter()
{
LARGE_INTEGER time = {};
QueryPerformanceCounter(&time);
return time.QuadPart;
}
#endif //_PIX3_WIN_H_

@ -0,0 +1 @@
Subproject commit 08e3881a04a0e207d65b4560d023c74c3775732e

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 MiB

Binary file not shown.

@ -0,0 +1 @@
Subproject commit de9ae2e9787a64e455d60996c35c043e1b8a537d

@ -0,0 +1,20 @@
FidelityFX Super Resolution 2.0.1
=================================
Features
--------
- The first release of FidelityFX Super Resolution 2.0.
Changes
-------
- First release.
Limitations
-----------
- The precise configuration and contents of the reactivity mask is subject to change in a future version of FSR2.
- Registered XBOX developers can refer to the GDK for an example of reactivity usage.
Known issues
------------
- The Vulkan version of the FSR2 sample application experiences a crash on Intel i9-10900K products.
- The Vulkan version of the FSR2 sample application experiences a crash when using the function keys to toggle between scaling modes on some NVIDIA hardware.

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 MiB

@ -0,0 +1,10 @@
include(${CMAKE_CURRENT_SOURCE_DIR}/../../common.cmake)
add_library(FSR2_Sample_Common INTERFACE)
set(config
${CMAKE_CURRENT_SOURCE_DIR}/../Common/FSR2_Sample.json
)
copyTargetCommand("${config}" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} copied_common_config)
add_dependencies(FSR2_Sample_Common copied_common_config)

@ -0,0 +1,74 @@
{
"globals": {
"CpuValidationLayerEnabled": false,
"GpuValidationLayerEnabled": false,
"presentationMode": 0,
"width": 1920,
"height": 1080,
"activeScene": 1,
"benchmark": false,
"vsync": false,
"stablePowerState": false,
"fontsize": 13,
"FreesyncHDROptionEnabled": true
},
"scenes": [
{
"name": "AbandonedWarehouse",
"directory": "..\\media\\cauldron-media\\AbandonedWarehouse\\",
"filename": "AbandonedWarehouse.gltf",
"TAA": true,
"toneMapper": 0,
"iblFactor": 0.6,
"emmisiveFactor": 1,
"intensity": 10,
"exposure": 1.4,
"camera": {
"defaultFrom": [ 0.45, -1.4, -6.0 ],
"defaultTo": [ -6.5, -1.4, -7 ]
}
},
{
"name": "Sponza",
"directory": "..\\media\\cauldron-media\\Sponza-New\\",
"filename": "scene.gltf",
"TAA": true,
"toneMapper": 0,
"iblFactor": 0.2,
"emmisiveFactor": 1,
"intensity": 7.5,
"exposure": 1.5,
"activeCamera": 1,
"camera": {
"defaultFrom": [ 3.0, 1.5, -0.2 ],
"defaultTo": [ 0.0, 1.5, -0.2 ]
},
"BenchmarkSettings": {
"timeStep": 1,
"timeStart": 0,
"timeEnd": 10000,
"exitWhenTimeEnds": true,
"resultsFilename": "Sponza.csv",
"warmUpFrames": 200,
"sequence": {
"timeStart": 0,
"timeEnd": 2000,
"keyFrames": [
{
"time": 0,
"from": [ 5.13694048, 1.89175785, -1.40289795 ],
"to": [ 0.703276634, 1.02280307, 0.218072295 ],
"screenShotName": "camera1.jpg"
},
{
"time": 1000,
"from": [ -5.13694048, 1.89175785, -1.40289795 ],
"to": [ 0.703276634, 1.02280307, 0.218072295 ],
"screenShotName": "camera2.jpg"
}
]
}
}
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

@ -0,0 +1 @@
IDI_ICON1 ICON DISCARDABLE "GPUOpenChip.ico"

@ -0,0 +1,123 @@
# This file is part of the FidelityFX SDK.
#
# Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
set(sources
UpscaleContext.cpp
UpscaleContext.h
UpscaleContext_Spatial.cpp
UpscaleContext_Spatial.h
UpscaleContext_FSR2_API.cpp
UpscaleContext_FSR2_API.h
GPUFrameRateLimiter.cpp
GPUFrameRateLimiter.h
ShaderCompiler.cpp
ShaderCompiler.h
FSR2Sample.cpp
FSR2Sample.h
Renderer.cpp
Renderer.h
stdafx.cpp
stdafx.h
UI.cpp
UI.h
dpiawarescaling.manifest)
set(fsr1_shaders_src
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_core.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_common_types.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_core_cpu.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_core_gpu_common.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_core_gpu_common_half.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_core_portability.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_core_hlsl.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_fsr1.h)
set(spd_shaders_src
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_core.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_common_types.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_core_cpu.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_core_gpu_common.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_core_gpu_common_half.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_core_portability.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_core_hlsl.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_spd.h)
set(fsr2_shaders_src
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_fsr2_accumulate.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_fsr2_callbacks_hlsl.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_fsr2_common.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_fsr2_compute_luminance_pyramid.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_fsr2_depth_clip.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_core.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_common_types.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_core_cpu.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_core_gpu_common.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_core_gpu_common_half.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_core_portability.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_core_hlsl.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_fsr1.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_spd.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_fsr2_common.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_fsr2_lock.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_fsr2_postprocess_lock_status.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_fsr2_prepare_input_color.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_fsr2_accumulate_pass.hlsl
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_fsr2_compute_luminance_pyramid_pass.hlsl
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_fsr2_depth_clip_pass.hlsl
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_fsr2_lock_pass.hlsl
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_fsr2_prepare_input_color_pass.hlsl
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_fsr2_reconstruct_dilated_velocity_and_previous_depth.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_fsr2_reconstruct_previous_depth_pass.hlsl
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_fsr2_reproject.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_fsr2_resources.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_fsr2_sample.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_fsr2_upsample.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_fsr2_rcas_pass.hlsl
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_fsr2_rcas.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_fsr2_autogen_reactive_pass.hlsl)
set(sample_shader_src
${CMAKE_CURRENT_SOURCE_DIR}/UpscaleSpatial.hlsl
${CMAKE_CURRENT_SOURCE_DIR}/FSRPass.hlsl
${CMAKE_CURRENT_SOURCE_DIR}/GPUFrameRateLimiter.hlsl
${CMAKE_CURRENT_SOURCE_DIR}/DebugBlit.hlsl)
set(APP_ICON_GPUOPEN "${CMAKE_CURRENT_SOURCE_DIR}/../common/GpuOpenIcon.rc")
source_group("sources" FILES ${sources})
source_group("shaders" FILES ${sample_shader_src})
source_group("spd_shaders" FILES ${spd_shaders_src})
source_group("fsr1_shaders" FILES ${fsr1_shaders_src})
source_group("fsr2_shaders" FILES ${fsr2_shaders_src})
copyCommand("${spd_shaders_src}" ${CMAKE_HOME_DIRECTORY}/bin/ShaderLibDX)
copyCommand("${fsr1_shaders_src}" ${CMAKE_HOME_DIRECTORY}/bin/ShaderLibDX)
copyCommand("${fsr2_shaders_src}" ${CMAKE_HOME_DIRECTORY}/bin/ShaderLibDX)
copyCommand("${sample_shader_src}" ${CMAKE_HOME_DIRECTORY}/bin/ShaderLibDX)
add_executable(FSR2_Sample_DX12 WIN32 ${sources} ${fsr1_shaders_src} ${spd_shaders_src} ${fsr2_shaders_src} ${sample_shader_src} ${common} ${APP_ICON_GPUOPEN})
target_compile_definitions(FSR2_Sample_DX12 PRIVATE USE_PIX=1 $<$<CONFIG:RelWithDebInfo>:FSR2_DEBUG_SHADERS=1>)
target_link_libraries(FSR2_Sample_DX12 LINK_PUBLIC FSR2_Sample_Common Cauldron_DX12 ImGUI amd_ags d3dcompiler D3D12 ffx_fsr2_api_x64 ffx_fsr2_api_dx12_x64)
target_include_directories(FSR2_Sample_DX12 PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api ${CMAKE_CURRENT_SOURCE_DIR}/../../libs)
target_link_directories(FSR2_Sample_DX12 PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../../libs)
set_target_properties(FSR2_Sample_DX12 PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_HOME_DIRECTORY}/bin" DEBUG_POSTFIX "d")
#set_source_files_properties(${sample_shaders_src} PROPERTIES VS_TOOL_OVERRIDE "Text")

@ -0,0 +1,51 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "FSR2_Common.hlsl"
#include "Common.hlsl"
Texture2D<float4> r_in : register(t0);
RWTexture2D<float4> rw_out : register(u0);
cbuffer cbBlit0 : register(b0)
{
int g_outChannelRed;
int g_outChannelGreen;
int g_outChannelBlue;
float2 outChannelRedMinMax;
float2 outChannelGreenMinMax;
float2 outChannelBlueMinMax;
};
[numthreads(8, 8, 1)]
void CS(uint3 globalID : SV_DispatchThreadID)
{
float4 srcColor = r_in[globalID.xy];
// remap channels
float4 dstColor = float4(srcColor[g_outChannelRed], srcColor[g_outChannelGreen], srcColor[g_outChannelBlue], 1.f);
// apply offset and scale
dstColor.rgb -= float3(outChannelRedMinMax.x, outChannelGreenMinMax.x, outChannelBlueMinMax.x);
dstColor.rgb /= float3(outChannelRedMinMax.y - outChannelRedMinMax.x, outChannelGreenMinMax.y - outChannelGreenMinMax.x, outChannelBlueMinMax.y - outChannelBlueMinMax.x);
rw_out[globalID.xy] = float4(dstColor);
}

@ -0,0 +1,785 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "stdafx.h"
#include <intrin.h>
#include "FSR2Sample.h"
#define USE_INVERTED_INFINITE_PROJECTION 1
static constexpr float MagnifierBorderColor_Locked[3] = { 0.002f, 0.72f, 0.0f };
static constexpr float MagnifierBorderColor_Free[3] = { 0.72f, 0.002f, 0.0f };
//static constexpr float AMD_PI = 3.1415926535897932384626433832795f;
//static constexpr float AMD_PI_OVER_2 = 1.5707963267948966192313216916398f;
//static constexpr float AMD_PI_OVER_4 = 0.78539816339744830961566084581988f;
FSR2Sample::FSR2Sample(LPCSTR name) : FrameworkWindows(name)
{
m_time = 0;
m_bPlay = true;
m_pGltfLoader = NULL;
}
//--------------------------------------------------------------------------------------
//
// OnParseCommandLine
//
//--------------------------------------------------------------------------------------
void FSR2Sample::OnParseCommandLine(LPSTR lpCmdLine, uint32_t* pWidth, uint32_t* pHeight)
{
// set some default values
*pWidth = 1920;
*pHeight = 1080;
m_activeScene = 0; //load the first one by default
m_VsyncEnabled = false;
m_bIsBenchmarking = false;
m_fontSize = 13.f; // default value overridden by a json file if available
m_isCpuValidationLayerEnabled = false;
m_isGpuValidationLayerEnabled = false;
m_activeCamera = 1;
m_stablePowerState = false;
//read globals
auto process = [&](json jData)
{
*pWidth = jData.value("width", *pWidth);
*pHeight = jData.value("height", *pHeight);
m_fullscreenMode = jData.value("presentationMode", m_fullscreenMode);
m_activeScene = jData.value("activeScene", m_activeScene);
m_activeCamera = jData.value("activeCamera", m_activeCamera);
m_isCpuValidationLayerEnabled = jData.value("CpuValidationLayerEnabled", m_isCpuValidationLayerEnabled);
m_isGpuValidationLayerEnabled = jData.value("GpuValidationLayerEnabled", m_isGpuValidationLayerEnabled);
m_VsyncEnabled = jData.value("vsync", m_VsyncEnabled);
m_FreesyncHDROptionEnabled = jData.value("FreesyncHDROptionEnabled", m_FreesyncHDROptionEnabled);
m_bIsBenchmarking = jData.value("benchmark", m_bIsBenchmarking);
m_stablePowerState = jData.value("stablePowerState", m_stablePowerState);
m_fontSize = jData.value("fontsize", m_fontSize);
};
// read config file
//
{
std::ifstream f("FSR2_Sample.json");
if (!f)
{
MessageBox(NULL, "Config file not found!\n", "Cauldron Panic!", MB_ICONERROR);
exit(0);
}
try
{
f >> m_jsonConfigFile;
}
catch (json::parse_error)
{
MessageBox(NULL, "Error parsing GLTFSample.json!\n", "Cauldron Panic!", MB_ICONERROR);
exit(0);
}
}
json globals = m_jsonConfigFile["globals"];
process(globals);
// read json globals from commandline (and override values from config file)
//
try
{
if (strlen(lpCmdLine) > 0)
{
auto j3 = json::parse(lpCmdLine);
process(j3);
}
}
catch (json::parse_error)
{
Trace("Error parsing commandline\n");
exit(0);
}
// get the list of scenes
for (const auto& scene : m_jsonConfigFile["scenes"])
m_sceneNames.push_back(scene["name"]);
}
//--------------------------------------------------------------------------------------
//
// OnCreate
//
//--------------------------------------------------------------------------------------
void FSR2Sample::OnCreate()
{
//init the shader compiler
InitDirectXCompiler();
CreateShaderCache();
// Create a instance of the renderer and initialize it, we need to do that for each GPU
m_pRenderer = new Renderer();
#if USE_INVERTED_INFINITE_PROJECTION
m_pRenderer->OnCreate(&m_device, &m_swapChain, m_fontSize, true);
#else
m_pRenderer->OnCreate(&m_device, &m_swapChain, m_fontSize, false);
#endif
// init GUI (non gfx stuff)
ImGUI_Init((void*)m_windowHwnd);
m_UIState.Initialize();
OnResize();
OnUpdateDisplay();
// Init Camera, looking at the origin
m_UIState.camera.LookAt(math::Vector4(0, 0, 5, 0), math::Vector4(0, 0, 0, 0));
}
//--------------------------------------------------------------------------------------
//
// OnDestroy
//
//--------------------------------------------------------------------------------------
void FSR2Sample::OnDestroy()
{
ImGUI_Shutdown();
m_device.GPUFlush();
m_pRenderer->UnloadScene();
m_pRenderer->OnDestroyWindowSizeDependentResources();
m_pRenderer->OnDestroy();
delete m_pRenderer;
//shut down the shader compiler
DestroyShaderCache(&m_device);
if (m_pGltfLoader)
{
delete m_pGltfLoader;
m_pGltfLoader = NULL;
}
}
//--------------------------------------------------------------------------------------
//
// OnEvent, win32 sends us events and we forward them to ImGUI
//
//--------------------------------------------------------------------------------------
bool FSR2Sample::OnEvent(MSG msg)
{
if (ImGUI_WndProcHandler(msg.hwnd, msg.message, msg.wParam, msg.lParam))
return true;
// handle function keys (F1, F2...) here, rest of the input is handled
// by imGUI later in HandleInput() function
const WPARAM& KeyPressed = msg.wParam;
switch (msg.message)
{
case WM_KEYUP:
case WM_SYSKEYUP:
/* WINDOW TOGGLES */
if (KeyPressed == VK_F11) m_UIState.bShowControlsWindow ^= 1;
if (KeyPressed == VK_F12) m_UIState.bShowProfilerWindow ^= 1;
if (KeyPressed == VK_F1) {
// Native
m_SavedUiValues[m_UIState.m_nUpscaleType].scaleMode = m_nUpscaleMode = UPSCALE_QUALITY_MODE_NONE;
m_SavedUiValues[m_UIState.m_nUpscaleType].upscaleRatio = m_fUpscaleRatio = 1.f;
m_SavedUiValues[m_UIState.m_nUpscaleType].mipBias = m_UIState.mipBias = mipBias[UPSCALE_QUALITY_MODE_NONE];
m_bUpscaleChanged = true;
}
if (KeyPressed == VK_F2) {
// Ultra Quality
m_SavedUiValues[m_UIState.m_nUpscaleType].scaleMode = m_nUpscaleMode = UPSCALE_QUALITY_MODE_ULTRA_QUALITY;
m_SavedUiValues[m_UIState.m_nUpscaleType].mipBias = m_UIState.mipBias = mipBias[UPSCALE_QUALITY_MODE_ULTRA_QUALITY];
m_bUpscaleChanged = true;
}
if (KeyPressed == VK_F3) {
// Quality
m_SavedUiValues[m_UIState.m_nUpscaleType].scaleMode = m_nUpscaleMode = UPSCALE_QUALITY_MODE_QUALITY;
m_SavedUiValues[m_UIState.m_nUpscaleType].mipBias = m_UIState.mipBias = mipBias[UPSCALE_QUALITY_MODE_QUALITY];
m_bUpscaleChanged = true;
}
if (KeyPressed == VK_F4) {
// Balanced
m_SavedUiValues[m_UIState.m_nUpscaleType].scaleMode = m_nUpscaleMode = UPSCALE_QUALITY_MODE_BALANCE;
m_SavedUiValues[m_UIState.m_nUpscaleType].mipBias = m_UIState.mipBias = mipBias[UPSCALE_QUALITY_MODE_BALANCE];
m_bUpscaleChanged = true;
}
if (KeyPressed == VK_F5) {
// Performance
m_SavedUiValues[m_UIState.m_nUpscaleType].scaleMode = m_nUpscaleMode = UPSCALE_QUALITY_MODE_PERFORMANCE;
m_SavedUiValues[m_UIState.m_nUpscaleType].mipBias = m_UIState.mipBias = mipBias[UPSCALE_QUALITY_MODE_PERFORMANCE];
m_bUpscaleChanged = true;
}
if (KeyPressed == VK_F6) {
// Ultra Performance
m_SavedUiValues[m_UIState.m_nUpscaleType].scaleMode = m_nUpscaleMode = UPSCALE_QUALITY_MODE_ULTRA_PERFORMANCE;
m_SavedUiValues[m_UIState.m_nUpscaleType].mipBias = m_UIState.mipBias = mipBias[UPSCALE_QUALITY_MODE_ULTRA_PERFORMANCE];
m_bUpscaleChanged = true;
}
break;
}
return true;
}
//--------------------------------------------------------------------------------------
//
// OnResize
//
//--------------------------------------------------------------------------------------
void FSR2Sample::OnResize(bool forceResizeRender)
{
bool resizeDisplay = forceResizeRender ||
(m_UIState.displayWidth != m_Width || m_UIState.displayHeight != m_Height);
m_UIState.displayWidth = m_Width;
m_UIState.displayHeight = m_Height;
RefreshRenderResolution();
bool resizeRender = forceResizeRender ||
(m_UIState.renderWidth != m_renderWidth || m_UIState.renderHeight != m_renderHeight);
m_renderWidth = m_UIState.renderWidth;
m_renderHeight = m_UIState.renderHeight;
if (resizeDisplay || resizeRender)
{
// Flush GPU
m_device.GPUFlush();
}
if (resizeDisplay || resizeRender)
{
if (m_pRenderer != NULL)
{
m_pRenderer->OnDestroyWindowSizeDependentResources();
}
// if resizing but not minimizing the recreate it with the new size
//
if (m_UIState.displayWidth > 0 && m_UIState.displayHeight > 0)
{
if (m_pRenderer != NULL)
{
m_UIState.bReset = true;
m_pRenderer->OnCreateWindowSizeDependentResources(&m_swapChain, &m_UIState);
}
}
}
float aspect = m_UIState.renderWidth * 1.f / m_UIState.renderHeight;
#if USE_INVERTED_INFINITE_PROJECTION
m_UIState.camera.SetFov(AMD_PI_OVER_2/aspect, aspect, 0.1f);
#else
m_UIState.camera.SetFov(AMD_PI_OVER_2 / aspect, aspect, 0.1f, 1000.0f);
#endif
}
//--------------------------------------------------------------------------------------
//
// UpdateDisplay
//
//--------------------------------------------------------------------------------------
void FSR2Sample::OnUpdateDisplay()
{
// Destroy resources (if we are not minimized)
if (m_pRenderer)
{
m_pRenderer->OnUpdateDisplayDependentResources(&m_swapChain);
}
}
//--------------------------------------------------------------------------------------
//
// LoadScene
//
//--------------------------------------------------------------------------------------
void FSR2Sample::LoadScene(int sceneIndex)
{
json scene = m_jsonConfigFile["scenes"][sceneIndex];
// release everything and load the GLTF, just the light json data, the rest (textures and geometry) will be done in the main loop
if (m_pGltfLoader != NULL)
{
m_pRenderer->UnloadScene();
m_pRenderer->OnDestroyWindowSizeDependentResources();
m_pRenderer->OnDestroy();
m_pGltfLoader->Unload();
// Make sure the actual resolution is updated no matter previous render size
RefreshRenderResolution();
#if USE_INVERTED_INFINITE_PROJECTION
m_pRenderer->OnCreate(&m_device, &m_swapChain, m_fontSize, true);
#else
m_pRenderer->OnCreate(&m_device, &m_swapChain, m_fontSize, false);
#endif
m_pRenderer->OnCreateWindowSizeDependentResources(&m_swapChain, &m_UIState);
}
delete(m_pGltfLoader);
m_pGltfLoader = new GLTFCommon();
if (m_pGltfLoader->Load(scene["directory"], scene["filename"]) == false)
{
MessageBox(NULL, "The selected model couldn't be found, please check the documentation", "Cauldron Panic!", MB_ICONERROR);
exit(0);
}
// Load the UI settings, and also some defaults cameras and lights, in case the GLTF has none
{
#define LOAD(j, key, val) val = j.value(key, val)
// global settings
LOAD(scene, "toneMapper", m_UIState.SelectedTonemapperIndex);
LOAD(scene, "skyDomeType", m_UIState.SelectedSkydomeTypeIndex);
LOAD(scene, "exposure", m_UIState.Exposure);
LOAD(scene, "iblFactor", m_UIState.IBLFactor);
LOAD(scene, "skyDomeType", m_UIState.SelectedSkydomeTypeIndex);
// Add a default light in case there are none
if (m_pGltfLoader->m_lights.size() == 0)
{
if(scene["name"]==std::string("Sponza"))
{
{
tfNode n;
n.m_transform.LookAt(math::Vector4(20, 100, -10, 0), math::Vector4(0, 0, 0, 0));
tfLight l;
l.m_type = tfLight::LIGHT_DIRECTIONAL;
l.m_intensity = scene.value("intensity", 1.0f);
l.m_color = math::Vector4(1.0f, 1.0f, 0.8f, 0.0f);
l.m_shadowResolution = 1024;
m_pGltfLoader->AddLight(n, l);
}
{
tfNode n;
n.m_transform.LookAt(math::Vector4(-2, 10, 1, 0), math::Vector4(0, 0, 0, 0));
tfLight l;
l.m_type = tfLight::LIGHT_SPOTLIGHT;
l.m_intensity = scene.value("intensity", 1.0f);
l.m_color = math::Vector4(1.0f, 1.0f, 1.0f, 0.0f);
l.m_range = 15;
l.m_outerConeAngle = AMD_PI_OVER_4;
l.m_innerConeAngle = AMD_PI_OVER_4 * 0.9f;
l.m_shadowResolution = 1024;
m_pGltfLoader->AddLight(n, l);
}
{
tfNode n;
n.m_transform.m_translation = math::Vector4(-13.f, 1.9f, 0.f, 0.f);
tfLight l;
l.m_type = tfLight::LIGHT_POINTLIGHT;
l.m_intensity = scene.value("intensity", 1.0f) * 0.25f;
l.m_color = math::Vector4(1.0f, 0.95f, 0.8f, 0.0f);
l.m_range = 4;
m_pGltfLoader->AddLight(n, l);
}
{
tfNode n;
n.m_transform.m_translation = math::Vector4(13.f, 1.9f, 0.f, 0.f);
tfLight l;
l.m_type = tfLight::LIGHT_POINTLIGHT;
l.m_intensity = scene.value("intensity", 1.0f) * 0.25f;
l.m_color = math::Vector4(1.0f, 0.95f, 0.8f, 0.0f);
l.m_range = 10;
m_pGltfLoader->AddLight(n, l);
}
}
else
{
tfNode n;
n.m_transform.LookAt(PolarToVector(AMD_PI_OVER_2, 0.58f) * 3.5f, math::Vector4(0, 0, 0, 0));
tfLight l;
l.m_type = tfLight::LIGHT_SPOTLIGHT;
l.m_intensity = scene.value("intensity", 1.0f);
l.m_color = math::Vector4(1.0f, 1.0f, 1.0f, 0.0f);
l.m_range = 15;
l.m_outerConeAngle = AMD_PI_OVER_4;
l.m_innerConeAngle = AMD_PI_OVER_4 * 0.9f;
l.m_shadowResolution = 1024;
m_pGltfLoader->AddLight(n, l);
}
}
// Allocate shadow information (if any)
m_pRenderer->AllocateShadowMaps(m_pGltfLoader);
// set default camera
json camera = scene["camera"];
m_activeCamera = scene.value("activeCamera", m_activeCamera);
math::Vector4 from = GetVector(GetElementJsonArray(camera, "defaultFrom", { 0.0, 0.0, 10.0 }));
math::Vector4 to = GetVector(GetElementJsonArray(camera, "defaultTo", { 0.0, 0.0, 0.0 }));
m_UIState.camera.LookAt(from, to);
// set benchmarking state if enabled
if (m_bIsBenchmarking)
{
std::string deviceName;
std::string driverVersion;
m_device.GetDeviceInfo(&deviceName, &driverVersion);
BenchmarkConfig(scene["BenchmarkSettings"], m_activeCamera, m_pGltfLoader, deviceName, driverVersion);
}
// indicate the mainloop we started loading a GLTF and it needs to load the rest (textures and geometry)
m_loadingScene = true;
}
}
//--------------------------------------------------------------------------------------
//
// OnUpdate
//
//--------------------------------------------------------------------------------------
void FSR2Sample::OnUpdate()
{
ImGuiIO& io = ImGui::GetIO();
//If the mouse was not used by the GUI then it's for the camera
if (io.WantCaptureMouse)
{
io.MouseDelta.x = 0;
io.MouseDelta.y = 0;
io.MouseWheel = 0;
}
// Update Camera
UpdateCamera(m_UIState.camera, io);
// Keyboard & Mouse
HandleInput(io);
// Animation Update
if (m_bPlay)
m_time += (float)m_deltaTime / 1000.0f; // animation time in seconds
if (m_pGltfLoader)
{
for( int i = 0; i<32; ++i)
m_pGltfLoader->SetAnimationTime(i, m_time);
if (m_UIState.m_balloonID >= 0) {
std::vector<tfNode>& pNodes = m_pGltfLoader->m_nodes;
math::Matrix4* pNodesMatrices = m_pGltfLoader->m_animatedMats.data();
if (!m_UIState.m_balloonInit) {
m_pGltfLoader->TransformScene(0, math::Matrix4::identity());
m_UIState.m_InitBaloonTransform= pNodesMatrices[m_UIState.m_balloonID];
m_UIState.m_CurBaloonTransform = pNodesMatrices[m_UIState.m_balloonID];
m_UIState.m_balloonInit = true;
}
float y = std::sin(m_time) * m_UIState.m_BalloonAmplitude;
if (m_UIState.m_bBalloonAttachToCamera) {
Vectormath::Vector3 eye = m_UIState.camera.GetPosition().getXYZ() + m_UIState.camera.GetDirection().getXYZ() * m_UIState.balloon_offset_z;
Vectormath::Vector3 look = m_UIState.camera.GetPosition().getXYZ() + m_UIState.camera.GetDirection().getXYZ() * -2.0f;
m_UIState.baloon_pos = m_UIState.baloon_pos + (eye - m_UIState.baloon_pos) * (1.0f - std::expf(50.0f * -(float)m_deltaTime / 1000.0f));
m_UIState.baloon_tip_pos = m_UIState.baloon_tip_pos + (look - m_UIState.baloon_tip_pos) * (1.0f - std::expf(50.0f * -(float)m_deltaTime / 1000.0f));
m_UIState.m_CurBaloonTransform = Vectormath::inverse(Vectormath::Matrix4::lookAt(Vectormath::Point3(m_UIState.baloon_pos), Vectormath::Point3(m_UIState.baloon_tip_pos), Vectormath::Vector3(0.0f, 1.0f, 0.0f))) *
Vectormath::Matrix4::translation(Vectormath::Vector3(m_UIState.balloon_offset_x, m_UIState.balloon_offset_y, 0.0f)) * //
Vectormath::Matrix4::rotation(-3.141592f / 2.0f, Vectormath::Vector3(1.0f, 0.0f, 0.0f)) * //
Vectormath::Matrix4::rotation(-3.141592f / 2.0f, Vectormath::Vector3(0.0f, 0.0f, 1.0f)) * //
Vectormath::Matrix4::scale(Vectormath::Vector3(m_UIState.balloon_offset_scale, m_UIState.balloon_offset_scale, m_UIState.balloon_offset_scale));
}
pNodesMatrices[m_UIState.m_balloonID] = Vectormath::Matrix4::translation(Vectormath::Vector3(0.0f, y, 0.0f)) * m_UIState.m_CurBaloonTransform;
}
m_pGltfLoader->TransformScene(0, math::Matrix4::identity());
}
m_UIState.deltaTime = m_deltaTime;
// frame rate limiter
if (m_UIState.bEnableFrameRateLimiter && !m_UIState.bUseGPUFrameRateLimiter)
{
static double lastFrameTime = MillisecondsNow();
double timeNow = MillisecondsNow();
double deltaTime = timeNow - lastFrameTime;
if (deltaTime < m_UIState.targetFrameTime)
{
Sleep(DWORD(m_UIState.targetFrameTime - deltaTime));
timeNow += m_UIState.targetFrameTime - deltaTime;
}
lastFrameTime = timeNow;
}
}
void FSR2Sample::HandleInput(const ImGuiIO& io)
{
auto fnIsKeyTriggered = [&io](char key) { return io.KeysDown[key] && io.KeysDownDuration[key] == 0.0f; };
// Handle Keyboard/Mouse input here
/* MAGNIFIER CONTROLS */
if (fnIsKeyTriggered('L')) m_UIState.ToggleMagnifierLock();
if (fnIsKeyTriggered('M') || io.MouseClicked[2]) m_UIState.bUseMagnifier ^= 1; // middle mouse / M key toggles magnifier
// save current state to saved
m_SavedUiValues[m_UIState.m_nUpscaleType].scaleMode = m_nUpscaleMode;
m_SavedUiValues[m_UIState.m_nUpscaleType].upscaleRatio = m_fUpscaleRatio;
m_SavedUiValues[m_UIState.m_nUpscaleType].mipBias = m_UIState.mipBias;
m_SavedUiValues[m_UIState.m_nUpscaleType].useSharpen = m_UIState.bUseRcas;
m_SavedUiValues[m_UIState.m_nUpscaleType].sharpness = m_UIState.sharpening;
m_SavedUiValues[m_UIState.m_nUpscaleType].useTAA = m_UIState.bUseTAA;
if (io.KeyCtrl == true)
{
if (fnIsKeyTriggered('1'))
{
m_bUpscaleChanged = true;
m_UIState.m_nUpscaleType = UPSCALE_TYPE_POINT;
m_UIState.bDynamicRes = false;
}
else if (fnIsKeyTriggered('2'))
{
m_bUpscaleChanged = true;
m_UIState.m_nUpscaleType = UPSCALE_TYPE_BILINEAR;
m_UIState.bDynamicRes = false;
}
else if (fnIsKeyTriggered('3'))
{
m_bUpscaleChanged = true;
m_UIState.m_nUpscaleType = UPSCALE_TYPE_BICUBIC;
m_UIState.bDynamicRes = false;
}
else if (fnIsKeyTriggered('4'))
{
m_bUpscaleChanged = true;
m_UIState.m_nUpscaleType = UPSCALE_TYPE_FSR_1_0;
m_UIState.bDynamicRes = false;
}
else if (fnIsKeyTriggered('5'))
{
m_bUpscaleChanged = true;
m_UIState.m_nUpscaleType = UPSCALE_TYPE_FSR_2_0;
}
else if (fnIsKeyTriggered('0'))
{
m_bUpscaleChanged = true;
m_UIState.m_nUpscaleType = UPSCALE_TYPE_NATIVE;
m_UIState.bDynamicRes = false;
// force reset values for native
m_SavedUiValues[m_UIState.m_nUpscaleType].scaleMode = UPSCALE_QUALITY_MODE_NONE;
m_SavedUiValues[m_UIState.m_nUpscaleType].upscaleRatio = 1.f;
m_SavedUiValues[m_UIState.m_nUpscaleType].mipBias = 0.f;
}
}
m_nUpscaleMode = m_SavedUiValues[m_UIState.m_nUpscaleType].scaleMode;
m_fUpscaleRatio = m_SavedUiValues[m_UIState.m_nUpscaleType].upscaleRatio;
m_UIState.mipBias = m_SavedUiValues[m_UIState.m_nUpscaleType].mipBias;
m_UIState.bUseRcas = m_SavedUiValues[m_UIState.m_nUpscaleType].useSharpen;
m_UIState.sharpening = m_SavedUiValues[m_UIState.m_nUpscaleType].sharpness;
m_UIState.bUseTAA = m_SavedUiValues[m_UIState.m_nUpscaleType].useTAA;
if (m_bUpscaleChanged)
{
OnResize(true);
}
if (io.MouseClicked[1] && m_UIState.bUseMagnifier) // right mouse click
m_UIState.ToggleMagnifierLock();
}
void FSR2Sample::UpdateCamera(Camera& cam, const ImGuiIO& io)
{
float yaw = cam.GetYaw();
float pitch = cam.GetPitch();
float distance = cam.GetDistance();
cam.UpdatePreviousMatrices(); // set previous view matrix
// Sets Camera based on UI selection (WASD, Orbit or any of the GLTF cameras)
if ((io.KeyCtrl == false) && (io.MouseDown[0] == true))
{
yaw -= io.MouseDelta.x / 100.f;
pitch += io.MouseDelta.y / 100.f;
}
// Choose camera movement depending on setting
if (m_activeCamera == 0)
{
// If nothing has changed, don't calculate an update (we are getting micro changes in view causing bugs)
if (!io.MouseWheel && (!io.MouseDown[0] || (!io.MouseDelta.x && !io.MouseDelta.y)))
return;
// Orbiting
distance -= (float)io.MouseWheel / 3.0f;
distance = std::max<float>(distance, 0.1f);
bool panning = (io.KeyCtrl == true) && (io.MouseDown[0] == true);
cam.UpdateCameraPolar(yaw, pitch,
panning ? -io.MouseDelta.x / 100.0f : 0.0f,
panning ? io.MouseDelta.y / 100.0f : 0.0f,
distance);
}
else if (m_activeCamera == 1)
{
// WASD
cam.SetSpeed(io.KeyCtrl ? 1.0f : 0.01f);
cam.UpdateCameraWASD(yaw, pitch, io.KeysDown, io.DeltaTime);
if (m_UIState.m_bHeadBobbing)
{
static math::Vector4 bob = math::Vector4(0.f);
math::Vector4 eyePos = cam.GetPosition();
// remove old headbob
eyePos -= bob;
// compute new headbob
math::Matrix4 view = cam.GetView();
math::Vector4 side = view.getRow(0) * 0.02f;
bob = side * sin(5 * m_time);
bob.setY(bob.getY() + 0.04f * sin(5* 2*m_time));
// apply new headbob
eyePos += bob;
view.setCol3(eyePos);
math::Vector4 viewDir = -cam.GetDirection();
view.setCol2(viewDir);
cam.SetMatrix(view);
}
}
else if (m_activeCamera > 1)
{
// Use a camera from the GLTF
m_pGltfLoader->GetCamera(m_activeCamera - 2, &cam);
}
}
//--------------------------------------------------------------------------------------
//
// OnRender, updates the state from the UI, animates, transforms and renders the scene
//
//--------------------------------------------------------------------------------------
void FSR2Sample::OnRender()
{
// Do any start of frame necessities
BeginFrame();
ImGUI_UpdateIO(m_UIState.displayWidth, m_UIState.displayHeight);
if (m_UIState.bDynamicRes)
{
// Quick dynamic resolution test: generate a resolution that fits in between the defined upscale factor
// and native resolution.
float sinTime = ((float)(sin(MillisecondsNow() / 1000.0)) + 1.0f) / 2.0f;
float thisFrameUpscaleRatio = (1.0f / m_fUpscaleRatio) + (1.0f - 1.0f / m_fUpscaleRatio) * sinTime;
m_UIState.renderWidth = (uint32_t)((float)m_Width * thisFrameUpscaleRatio);
m_UIState.renderHeight = (uint32_t)((float)m_Height * thisFrameUpscaleRatio);
m_UIState.mipBias = log2f(float(m_UIState.renderWidth) / m_UIState.displayWidth) - 1.0f;
}
else
{
float thisFrameUpscaleRatio = 1.f/m_SavedUiValues[m_UIState.m_nUpscaleType].upscaleRatio;
m_UIState.renderWidth = (uint32_t)((float)m_Width * thisFrameUpscaleRatio);
m_UIState.renderHeight = (uint32_t)((float)m_Height * thisFrameUpscaleRatio);
m_UIState.mipBias = log2f(float(m_UIState.renderWidth) / m_UIState.displayWidth) - 1.0f;
}
ImGui::NewFrame();
if (m_loadingScene)
{
// the scene loads in chunks, that way we can show a progress bar
static int loadingStage = 0;
loadingStage = m_pRenderer->LoadScene(m_pGltfLoader, loadingStage);
if (loadingStage == 0)
{
m_time = 0;
m_loadingScene = false;
const json& j3 = m_pGltfLoader->j3;
if (j3.find("meshes") != j3.end()) {
const json& nodes = j3["nodes"];
for (uint32_t i = 0; i < nodes.size(); i++) {
const json& node = nodes[i];
std::string name = GetElementString(node, "name", "unnamed");
if (name == "baloon") {
m_UIState.m_balloonID = i;
}
}
}
}
}
else if (m_pGltfLoader && m_bIsBenchmarking)
{
// Benchmarking takes control of the time, and exits the app when the animation is done
std::vector<TimeStamp> timeStamps = m_pRenderer->GetTimingValues();
m_time = BenchmarkLoop(timeStamps, &m_UIState.camera, m_pRenderer->GetScreenshotFileName());
}
else
{
BuildUI(); // UI logic. Note that the rendering of the UI happens later.
OnUpdate(); // Update camera, handle keyboard/mouse input
}
// Do Render frame using AFR
m_pRenderer->OnRender(&m_UIState, m_UIState.camera, &m_swapChain);
// Framework will handle Present and some other end of frame logic
EndFrame();
}
//--------------------------------------------------------------------------------------
//
// WinMain
//
//--------------------------------------------------------------------------------------
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
LPCSTR Name = "FidelityFX Super Resolution 2.0";
// create new DX sample
return RunFramework(hInstance, lpCmdLine, nCmdShow, new FSR2Sample(Name));
}
#if !defined(_DEBUG)
CAULDRON_APP_USE_DX12_AGILITY_SDK(600, u8".\\D3D12\\")
#endif

@ -0,0 +1,175 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#pragma once
#include "base/FrameworkWindows.h"
#include "Renderer.h"
#include "UI.h"
//
// This is the main class, it manages the state of the sample and does all the high level work without touching the GPU directly.
// This class uses the GPU via the the SampleRenderer class. We would have a SampleRenderer instance for each GPU.
//
// This class takes care of:
//
// - loading a scene (just the CPU data)
// - updating the camera
// - keeping track of time
// - handling the keyboard
// - updating the animation
// - building the UI (but do not renders it)
// - uses the SampleRenderer to update all the state to the GPU and do the rendering
//
class FSR2Sample : public FrameworkWindows
{
public:
FSR2Sample(LPCSTR name);
void OnParseCommandLine(LPSTR lpCmdLine, uint32_t* pWidth, uint32_t* pHeight) override;
void OnCreate() override;
void OnDestroy() override;
void OnRender() override;
bool OnEvent(MSG msg) override;
void OnResize(bool forceRecreateResources = false) override;
void OnUpdateDisplay() override;
void BuildUI();
void LoadScene(int sceneIndex);
void OnUpdate();
void HandleInput(const ImGuiIO& io);
void UpdateCamera(Camera& cam, const ImGuiIO& io);
private:
bool m_bIsBenchmarking;
GLTFCommon* m_pGltfLoader = NULL;
bool m_loadingScene = false;
Renderer* m_pRenderer = NULL;
UIState m_UIState;
float m_fontSize;
uint32_t m_renderWidth = 0;
uint32_t m_renderHeight = 0;
float m_time; // Time accumulator in seconds, used for animation.
// json config file
json m_jsonConfigFile;
std::vector<std::string> m_sceneNames;
int m_activeScene;
int m_activeCamera;
bool m_bPlay;
bool m_bDisableHalf = false;
bool m_bEnableHalf = false;
bool m_bDisplayHalf = false;
bool UseSlowFallback(bool deviceSupport)
{
assert(!m_bEnableHalf || !m_bDisableHalf);
if (m_bEnableHalf)
return false;
else
if (m_bDisableHalf)
return true;
else
return !deviceSupport;
}
private:
void RefreshRenderResolution()
{
switch (m_nUpscaleMode)
{
case UPSCALE_QUALITY_MODE_NONE:
m_fUpscaleRatio = 1.0f;
break;
case UPSCALE_QUALITY_MODE_ULTRA_QUALITY:
m_fUpscaleRatio = 1.3f;
break;
case UPSCALE_QUALITY_MODE_QUALITY:
m_fUpscaleRatio = 1.5f;
break;
case UPSCALE_QUALITY_MODE_BALANCE:
m_fUpscaleRatio = 1.7f;
break;
case UPSCALE_QUALITY_MODE_PERFORMANCE:
m_fUpscaleRatio = 2.0f;
break;
case UPSCALE_QUALITY_MODE_ULTRA_PERFORMANCE:
m_fUpscaleRatio = 3.0f;
break;
default: break;
}
if (m_UIState.bDynamicRes)
{
m_UIState.renderWidth = uint32_t(m_UIState.displayWidth);
m_UIState.renderHeight = uint32_t(m_UIState.displayHeight);
}
else
{
m_UIState.renderWidth = uint32_t(m_UIState.displayWidth / m_fUpscaleRatio);
m_UIState.renderHeight = uint32_t(m_UIState.displayHeight / m_fUpscaleRatio);
}
m_bUpscaleChanged = false;
}
bool m_bUpscaleChanged = true;
// A collection of mip bias levels for the various quality modes.
float mipBias[UPSCALE_QUALITY_MODE_COUNT] = {
0.f + log2(1.f / 1.0f) - 1.0f + FLT_EPSILON, // -1.0 - UPSCALE_QUALITY_MODE_NONE
0.f + log2(1.f / 1.3f) - 1.0f + FLT_EPSILON, // -1.38f - UPSCALE_QUALITY_MODE_ULTRA_QUALITY
0.f + log2(1.f / 1.5f) - 1.0f + FLT_EPSILON, // -1.58f - UPSCALE_QUALITY_MODE_QUALITY
0.f + log2(1.f / 1.7f) - 1.0f + FLT_EPSILON, // -1.76f - UPSCALE_QUALITY_MODE_BALANCED
0.f + log2(1.f / 2.0f) - 1.0f + FLT_EPSILON, // -2.0f - UPSCALE_QUALITY_MODE_PERFORMANCE
0.f + log2(1.f / 3.0f) - 1.0f + FLT_EPSILON, // -2.58f - UPSCALE_QUALITY_MODE_ULTRA_PERFORMANCE
0.0f // 0.0f - UPSCALE_QUALITY_MODE_CUSTOM
};
UpscaleQualityMode m_nUpscaleMode = UPSCALE_QUALITY_MODE_PERFORMANCE;
float m_fUpscaleRatio = 2.0f;
bool m_bDynamicRes = false;
float m_fDynamicResMinPercentage = 1.0f;
struct
{
UpscaleQualityMode scaleMode;
float upscaleRatio;
float mipBias;
bool useSharpen;
float sharpness;
bool useTAA;
bool dynamicRes;
float dynamicResMinPercent;
} m_SavedUiValues[UPSCALE_TYPE_COUNT] = {
{ UPSCALE_QUALITY_MODE_PERFORMANCE, 2.0f, -2.f, true, 1.f, true, false, 1.0f}, // UPSCALE_TYPE_POINT
{ UPSCALE_QUALITY_MODE_PERFORMANCE, 2.0f, -2.f, true, 1.f, true, false, 1.0f}, // UPSCALE_TYPE_BILINEAR
{ UPSCALE_QUALITY_MODE_PERFORMANCE, 2.0f, -2.f, true, 1.f, true, false, 1.0f}, // UPSCALE_TYPE_BICUBIC
{ UPSCALE_QUALITY_MODE_PERFORMANCE, 2.0f, -2.f, true, 1.f, true, false, 1.0f}, // UPSCALE_TYPE_FSR_1_0 (w/ TAA)
{ UPSCALE_QUALITY_MODE_PERFORMANCE, 2.0f, -2.f, false, 1.f, true, false, 1.0f}, // UPSCALE_TYPE_FSR_2_0
{ UPSCALE_QUALITY_MODE_NONE, 1.0f, 0.f, false, 1.f, true, false, 1.0f}, // UPSCALE_TYPE_NATIVE
};
};

@ -0,0 +1,118 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
cbuffer cb : register(b0)
{
uint4 Const0;
uint4 Const1;
uint4 Const2;
uint4 Const3;
uint4 Sample;
};
#define A_GPU 1
#define A_HLSL 1
SamplerState samLinearClamp : register(s0);
#if SAMPLE_SLOW_FALLBACK
#include "ffx_a.h"
Texture2D InputTexture : register(t0);
RWTexture2D<float4> OutputTexture : register(u0);
#if SAMPLE_EASU
#define FSR_EASU_F 1
AF4 FsrEasuRF(AF2 p) { AF4 res = InputTexture.GatherRed(samLinearClamp, p, int2(0, 0)); return res; }
AF4 FsrEasuGF(AF2 p) { AF4 res = InputTexture.GatherGreen(samLinearClamp, p, int2(0, 0)); return res; }
AF4 FsrEasuBF(AF2 p) { AF4 res = InputTexture.GatherBlue(samLinearClamp, p, int2(0, 0)); return res; }
#endif
#if SAMPLE_RCAS
#define FSR_RCAS_F
AF4 FsrRcasLoadF(ASU2 p) { return InputTexture.Load(int3(ASU2(p), 0)); }
void FsrRcasInputF(inout FfxFloat32 r, inout FfxFloat32 g, inout FfxFloat32 b) {}
#endif
#else
#define A_HALF
#include "ffx_a.h"
Texture2D<AH4> InputTexture : register(t0);
RWTexture2D<AH4> OutputTexture : register(u0);
#if SAMPLE_EASU
#define FSR_EASU_H 1
AH4 FsrEasuRH(AF2 p) { AH4 res = InputTexture.GatherRed(samLinearClamp, p, int2(0, 0)); return res; }
AH4 FsrEasuGH(AF2 p) { AH4 res = InputTexture.GatherGreen(samLinearClamp, p, int2(0, 0)); return res; }
AH4 FsrEasuBH(AF2 p) { AH4 res = InputTexture.GatherBlue(samLinearClamp, p, int2(0, 0)); return res; }
#endif
#if SAMPLE_RCAS
#define FSR_RCAS_H
AH4 FsrRcasLoadH(ASW2 p) { return InputTexture.Load(ASW3(ASW2(p), 0)); }
void FsrRcasInputH(inout AH1 r,inout AH1 g,inout AH1 b){}
#endif
#endif
#include "ffx_fsr1.h"
void CurrFilter(int2 pos)
{
#if SAMPLE_BILINEAR
AF2 pp = (AF2(pos) * AF2_AU2(Const0.xy) + AF2_AU2(Const0.zw)) * AF2_AU2(Const1.xy) + AF2(0.5, -0.5) * AF2_AU2(Const1.zw);
OutputTexture[pos] = InputTexture.SampleLevel(samLinearClamp, pp, 0.0);
#endif
#if SAMPLE_EASU
#if SAMPLE_SLOW_FALLBACK
AF3 c;
FsrEasuF(c, pos, Const0, Const1, Const2, Const3);
OutputTexture[pos] = float4(c, 1);
#else
AH3 c;
FsrEasuH(c, pos, Const0, Const1, Const2, Const3);
OutputTexture[pos] = AH4(c, 1);
#endif
#endif
#if SAMPLE_RCAS
#if SAMPLE_SLOW_FALLBACK
AF3 c;
FsrRcasF(c.r, c.g, c.b, pos, Const0);
if (Sample.x == 1)
c *= c;
OutputTexture[pos] = float4(c, 1);
#else
AH3 c;
FsrRcasH(c.r, c.g, c.b, pos, Const0);
if( Sample.x == 1 )
c *= c;
OutputTexture[pos] = AH4(c, 1);
#endif
#endif
}
[numthreads(WIDTH, HEIGHT, DEPTH)]
void mainCS(uint3 LocalThreadId : SV_GroupThreadID, uint3 WorkGroupId : SV_GroupID, uint3 Dtid : SV_DispatchThreadID)
{
// Do remapping of local xy in workgroup for a more PS-like swizzle pattern.
AU2 gxy = ARmp8x8(LocalThreadId.x) + AU2(WorkGroupId.x << 4u, WorkGroupId.y << 4u);
CurrFilter(gxy);
gxy.x += 8u;
CurrFilter(gxy);
gxy.y += 8u;
CurrFilter(gxy);
gxy.x -= 8u;
CurrFilter(gxy);
}

@ -0,0 +1,85 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "GPUFrameRateLimiter.h"
static const UINT BufferLength = 32768 * 32;
void GPUFrameRateLimiter::OnCreate(Device* pDevice, ResourceViewHeaps* pResourceViewHeaps)
{
m_pipeline.OnCreate(pDevice, pResourceViewHeaps, "GPUFrameRateLimiter.hlsl", "CSMain", 1, 0, 32, 1, 1);
const CD3DX12_RESOURCE_DESC bufDesc = CD3DX12_RESOURCE_DESC(
D3D12_RESOURCE_DIMENSION_BUFFER, 0,
BufferLength * sizeof(float), 1, 1, 1, DXGI_FORMAT_UNKNOWN,
1, 0, D3D12_TEXTURE_LAYOUT_ROW_MAJOR, D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS);
m_buffer.InitBuffer(pDevice, "FrameTimeLimiter", &bufDesc, sizeof(float), D3D12_RESOURCE_STATE_COMMON);
m_bufferState = D3D12_RESOURCE_STATE_COMMON;
pResourceViewHeaps->AllocCBV_SRV_UAVDescriptor(1, &m_UAVs);
m_buffer.CreateBufferUAV(0, nullptr, &m_UAVs);
m_frameTimeHistoryCount = 0;
m_frameTimeHistorySum = 0;
}
void GPUFrameRateLimiter::OnDestroy()
{
m_buffer.OnDestroy();
m_pipeline.OnDestroy();
}
void GPUFrameRateLimiter::Draw(ID3D12GraphicsCommandList* pCmdList, DynamicBufferRing* pDynamicBufferRing, uint64_t lastFrameTimeMicrosecs, uint64_t targetFrameTimeMicrosecs)
{
if (m_bufferState != D3D12_RESOURCE_STATE_UNORDERED_ACCESS)
{
pCmdList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_buffer.GetResource(), D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_UNORDERED_ACCESS));
m_bufferState = D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
}
constexpr double DampenFactor = 0.05;
constexpr double MaxTargetFrameTimeUs = 200'000.0; // 200ms 5fps to match CPU limiter UI.
constexpr double MinTargetFrameTimeUs = 50.0;
if (m_frameTimeHistoryCount >= _countof(m_frameTimeHistory))
{
m_frameTimeHistorySum -= m_frameTimeHistory[m_frameTimeHistoryCount % _countof(m_frameTimeHistory)];
}
m_frameTimeHistorySum += lastFrameTimeMicrosecs;
m_frameTimeHistory[m_frameTimeHistoryCount % _countof(m_frameTimeHistory)] = lastFrameTimeMicrosecs;
m_frameTimeHistoryCount++;
double recentFrameTimeAvg = double(m_frameTimeHistorySum) / min(m_frameTimeHistoryCount, _countof(m_frameTimeHistory));
double clampedTargetFrameTimeMs = max(min(double(targetFrameTimeMicrosecs), MaxTargetFrameTimeUs), MinTargetFrameTimeUs);
double deltaRatio = (recentFrameTimeAvg - clampedTargetFrameTimeMs) / clampedTargetFrameTimeMs;
m_overhead -= m_overhead * deltaRatio * DampenFactor;
m_overhead = min(max(1.0, m_overhead), 1000000.0);
uint32_t numLoops = uint32_t(m_overhead);
const D3D12_GPU_VIRTUAL_ADDRESS cbVa = pDynamicBufferRing->AllocConstantBuffer(sizeof(uint32_t), &numLoops);
m_pipeline.Draw(pCmdList, cbVa, &m_UAVs, nullptr, BufferLength / 32, 1, 1);
}

@ -0,0 +1,49 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#pragma once
#include "stdafx.h"
#include "base/Texture.h"
#include "PostProc/PostProcCS.h"
class GPUFrameRateLimiter
{
public:
void OnCreate(Device* pDevice, ResourceViewHeaps* pResourceViewHeaps);
void OnDestroy();
void Draw(ID3D12GraphicsCommandList* pCmdList, DynamicBufferRing* pDynamicBufferRing, uint64_t lastFrameTimeMs, uint64_t targetFrameTimeMs);
private:
PostProcCS m_pipeline;
Texture m_buffer;
D3D12_RESOURCE_STATES m_bufferState;
CBV_SRV_UAV m_UAVs;
double m_overhead = 1.0;
static const uint32_t FRAME_TIME_HISTORY_SAMPLES = 4;
uint64_t m_frameTimeHistory[FRAME_TIME_HISTORY_SAMPLES];
uint64_t m_frameTimeHistorySum = 0;
uint64_t m_frameTimeHistoryCount = 0;
};

@ -0,0 +1,38 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
RWStructuredBuffer<float> DataBuffer : register(u0);
cbuffer cb : register(b0)
{
uint NumLoops;
}
[numthreads(32,1,1)]
void CSMain(uint dtID : SV_DispatchThreadID)
{
float tmp = DataBuffer[dtID];
for (uint i = 0; i < NumLoops; i++)
{
tmp = sin(tmp) + 1.5f;
}
DataBuffer[dtID] = tmp;
}

@ -0,0 +1,997 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "Renderer.h"
#include "UI.h"
//--------------------------------------------------------------------------------------
//
// OnCreate
//
//--------------------------------------------------------------------------------------
void Renderer::OnCreate(Device* pDevice, SwapChain *pSwapChain, float FontSize, bool bInvertedDepth)
{
m_pDevice = pDevice;
m_bInvertedDepth = bInvertedDepth;
// Initialize helpers
// Create all the heaps for the resources views
const uint32_t cbvDescriptorCount = 4000;
const uint32_t srvDescriptorCount = 8000;
const uint32_t uavDescriptorCount = 100;
const uint32_t dsvDescriptorCount = 100;
const uint32_t rtvDescriptorCount = 100;
const uint32_t samplerDescriptorCount = 20;
m_ResourceViewHeaps.OnCreate(pDevice, cbvDescriptorCount, srvDescriptorCount, uavDescriptorCount, dsvDescriptorCount, rtvDescriptorCount, samplerDescriptorCount);
// Create a commandlist ring for the Direct queue
uint32_t commandListsPerBackBuffer = 8;
m_CommandListRing.OnCreate(pDevice, backBufferCount, commandListsPerBackBuffer, pDevice->GetGraphicsQueue()->GetDesc());
// Create a 'dynamic' constant buffer
const uint32_t constantBuffersMemSize = 200 * 1024 * 1024;
m_ConstantBufferRing.OnCreate(pDevice, backBufferCount, constantBuffersMemSize, &m_ResourceViewHeaps);
// Create a 'static' pool for vertices, indices and constant buffers
const uint32_t staticGeometryMemSize = (5 * 128) * 1024 * 1024;
m_VidMemBufferPool.OnCreate(pDevice, staticGeometryMemSize, true, "StaticGeom");
// initialize the GPU time stamps module
m_GPUTimer.OnCreate(pDevice, backBufferCount);
// Quick helper to upload resources, it has it's own commandList and uses suballocation.
const uint32_t uploadHeapMemSize = 1000 * 1024 * 1024;
m_UploadHeap.OnCreate(pDevice, uploadHeapMemSize); // initialize an upload heap (uses suballocation for faster results)
// Create GBuffer and render passes
{
{
m_GBuffer.OnCreate(
pDevice,
&m_ResourceViewHeaps,
{
{ GBUFFER_DEPTH, DXGI_FORMAT_D32_FLOAT},
{ GBUFFER_FORWARD, DXGI_FORMAT_R16G16B16A16_FLOAT},
{ GBUFFER_MOTION_VECTORS, DXGI_FORMAT_R16G16_FLOAT},
{ GBUFFER_UPSCALEREACTIVE, DXGI_FORMAT_R8_UNORM},
{ GBUFFER_UPSCALE_TRANSPARENCY_AND_COMPOSITION, DXGI_FORMAT_R8_UNORM},
},
1,
bInvertedDepth ? GBUFFER_INVERTED_DEPTH : GBUFFER_NONE
);
GBufferFlags fullGBuffer = GBUFFER_DEPTH | GBUFFER_FORWARD | GBUFFER_MOTION_VECTORS | GBUFFER_UPSCALEREACTIVE | GBUFFER_UPSCALE_TRANSPARENCY_AND_COMPOSITION;
if (bInvertedDepth) fullGBuffer |= GBUFFER_INVERTED_DEPTH;
m_RenderPassFullGBuffer.OnCreate(&m_GBuffer, fullGBuffer);
GBufferFlags depthAndHdr = GBUFFER_DEPTH | GBUFFER_FORWARD;
if (bInvertedDepth) depthAndHdr |= GBUFFER_INVERTED_DEPTH;
m_RenderPassJustDepthAndHdr.OnCreate(&m_GBuffer, depthAndHdr);
}
}
#if USE_SHADOWMASK
m_shadowResolve.OnCreate(m_pDevice, &m_ResourceViewHeaps, &m_ConstantBufferRing);
// Create the shadow mask descriptors
m_ResourceViewHeaps.AllocCBV_SRV_UAVDescriptor(1, &m_ShadowMaskUAV);
m_ResourceViewHeaps.AllocCBV_SRV_UAVDescriptor(1, &m_ShadowMaskSRV);
#endif
m_ResourceViewHeaps.AllocCBV_SRV_UAVDescriptor(1, &m_displayOutputSRV);
m_ResourceViewHeaps.AllocCBV_SRV_UAVDescriptor(1, &m_displayOutputUAV);
m_ResourceViewHeaps.AllocRTVDescriptor(1, &m_renderOutputRTV);
m_ResourceViewHeaps.AllocRTVDescriptor(1, &m_displayOutputRTV);
m_SkyDome.OnCreate(pDevice, &m_UploadHeap, &m_ResourceViewHeaps, &m_ConstantBufferRing, &m_VidMemBufferPool, "..\\media\\cauldron-media\\envmaps\\papermill\\diffuse.dds", "..\\media\\cauldron-media\\envmaps\\papermill\\specular.dds", DXGI_FORMAT_R16G16B16A16_FLOAT, 4, m_bInvertedDepth);
m_SkyDomeProc.OnCreate(pDevice, &m_ResourceViewHeaps, &m_ConstantBufferRing, &m_VidMemBufferPool, DXGI_FORMAT_R16G16B16A16_FLOAT, 1, m_bInvertedDepth);
m_Wireframe.OnCreate(pDevice, &m_ResourceViewHeaps, &m_ConstantBufferRing, &m_VidMemBufferPool, DXGI_FORMAT_R16G16B16A16_FLOAT, 1, m_bInvertedDepth);
m_WireframeBox.OnCreate(pDevice, &m_ResourceViewHeaps, &m_ConstantBufferRing, &m_VidMemBufferPool);
m_DownSample.OnCreate(pDevice, &m_ResourceViewHeaps, &m_ConstantBufferRing, &m_VidMemBufferPool, DXGI_FORMAT_R16G16B16A16_FLOAT);
m_Bloom.OnCreate(pDevice, &m_ResourceViewHeaps, &m_ConstantBufferRing, &m_VidMemBufferPool, DXGI_FORMAT_R16G16B16A16_FLOAT);
// Create tonemapping pass
m_ToneMappingPS.OnCreate(pDevice, &m_ResourceViewHeaps, &m_ConstantBufferRing, &m_VidMemBufferPool, pSwapChain->GetFormat());
m_ToneMappingCS.OnCreate(pDevice, &m_ResourceViewHeaps, &m_ConstantBufferRing);
m_ColorConversionPS.OnCreate(pDevice, &m_ResourceViewHeaps, &m_ConstantBufferRing, &m_VidMemBufferPool, pSwapChain->GetFormat());
// Initialize UI rendering resources
m_ImGUI.OnCreate(pDevice, &m_UploadHeap, &m_ResourceViewHeaps, &m_ConstantBufferRing, pSwapChain->GetFormat(), FontSize);
// Make sure upload heap has finished uploading before continuing
m_VidMemBufferPool.UploadData(m_UploadHeap.GetCommandList());
m_UploadHeap.FlushAndFinish();
// TAA
m_ResourceViewHeaps.AllocCBV_SRV_UAVDescriptor(3, &m_UpscaleSRVs);
m_GpuFrameRateLimiter.OnCreate(pDevice, &m_ResourceViewHeaps);
// needs to be completely reinitialized, as the format potentially changes
bool hdr = (pSwapChain->GetDisplayMode() != DISPLAYMODE_SDR);
DXGI_FORMAT mFormat = (hdr ? m_pGBufferHDRTexture->GetFormat() : DXGI_FORMAT_R8G8B8A8_UNORM);
m_MagnifierPS.OnCreate(m_pDevice, &m_ResourceViewHeaps, &m_ConstantBufferRing, &m_VidMemBufferPool, mFormat);
ResetScene();
}
//--------------------------------------------------------------------------------------
//
// OnDestroy
//
//--------------------------------------------------------------------------------------
void Renderer::OnDestroy()
{
m_GpuFrameRateLimiter.OnDestroy();
m_AsyncPool.Flush();
m_ImGUI.OnDestroy();
m_ColorConversionPS.OnDestroy();
m_ToneMappingCS.OnDestroy();
m_ToneMappingPS.OnDestroy();
m_Bloom.OnDestroy();
m_DownSample.OnDestroy();
m_WireframeBox.OnDestroy();
m_Wireframe.OnDestroy();
m_SkyDomeProc.OnDestroy();
m_SkyDome.OnDestroy();
#if USE_SHADOWMASK
m_shadowResolve.OnDestroy();
#endif
m_GBuffer.OnDestroy();
m_UploadHeap.OnDestroy();
m_GPUTimer.OnDestroy();
m_VidMemBufferPool.OnDestroy();
m_ConstantBufferRing.OnDestroy();
m_ResourceViewHeaps.OnDestroy();
m_CommandListRing.OnDestroy();
m_pUpscaleContext->OnDestroy();
delete m_pUpscaleContext;
m_pUpscaleContext = NULL;
m_MagnifierPS.OnDestroy();
}
//--------------------------------------------------------------------------------------
//
// OnCreateWindowSizeDependentResources
//
//--------------------------------------------------------------------------------------
void Renderer::OnCreateWindowSizeDependentResources(SwapChain *pSwapChain, UIState* pState)
{
m_Width = pState->renderWidth;
m_Height = pState->renderHeight;
// Set the viewport & scissors rect
m_Viewport = { 0.0f, 0.0f, static_cast<float>(m_Width), static_cast<float>(m_Height), 0.0f, 1.0f };
m_RectScissor = { 0, 0, (LONG)m_Width, (LONG)m_Height };
#if USE_SHADOWMASK
// Create shadow mask
//
m_ShadowMask.Init(m_pDevice, "shadowbuffer", &CD3DX12_RESOURCE_DESC::Tex2D(DXGI_FORMAT_R8G8B8A8_UNORM, Width, Height, 1, 1, 1, 0, D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS), D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, NULL);
m_ShadowMask.CreateUAV(0, &m_ShadowMaskUAV);
m_ShadowMask.CreateSRV(0, &m_ShadowMaskSRV);
#endif
// Create GBuffer
//
m_GBuffer.OnCreateWindowSizeDependentResources(pSwapChain, m_Width, m_Height);
m_RenderPassFullGBuffer.OnCreateWindowSizeDependentResources(m_Width, m_Height);
m_RenderPassJustDepthAndHdr.OnCreateWindowSizeDependentResources(m_Width, m_Height);
{
m_GBuffer.m_HDR.CreateSRV(0, &m_UpscaleSRVs);
m_GBuffer.m_DepthBuffer.CreateSRV(1, &m_UpscaleSRVs);
m_GBuffer.m_MotionVectors.CreateSRV(2, &m_UpscaleSRVs);
m_pGBufferHDRTexture = &m_GBuffer.m_HDR;
}
{
CD3DX12_RESOURCE_DESC desc = CD3DX12_RESOURCE_DESC::Tex2D(m_pGBufferHDRTexture->GetFormat(), m_pGBufferHDRTexture->GetWidth(), m_pGBufferHDRTexture->GetHeight(), 1, 1, 1, 0, D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS);
m_OpaqueTexture.Init(m_pDevice, "OpaqueBuffer", &desc, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, NULL);
}
// update bloom and downscaling effect
//
m_DownSample.OnCreateWindowSizeDependentResources(m_Width, m_Height, m_pGBufferHDRTexture, 5); //downsample the HDR texture 5 times
m_Bloom.OnCreateWindowSizeDependentResources(m_Width / 2, m_Height / 2, m_DownSample.GetTexture(), 5, m_pGBufferHDRTexture);
// update bloom and downscaling effect
//
bool renderNative = (pState->m_nUpscaleType == UPSCALE_TYPE_NATIVE);
bool hdr = (pSwapChain->GetDisplayMode() != DISPLAYMODE_SDR);
DXGI_FORMAT dFormat = (hdr ? m_pGBufferHDRTexture->GetFormat() : DXGI_FORMAT_R8G8B8A8_UNORM);
DXGI_FORMAT rFormat = (hdr ? m_pGBufferHDRTexture->GetFormat() : pSwapChain->GetFormat());
// Update pipelines in case the format of the RTs changed (this happens when going HDR)
m_ColorConversionPS.UpdatePipelines(pSwapChain->GetFormat(), pSwapChain->GetDisplayMode());
m_ToneMappingPS.UpdatePipelines(renderNative ? (hdr ? dFormat : DXGI_FORMAT_R8G8B8A8_UNORM_SRGB) : rFormat);
m_ImGUI.UpdatePipeline(rFormat);
FLOAT cc[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
m_renderOutput.InitRenderTarget(m_pDevice, "RenderOutput", &CD3DX12_RESOURCE_DESC::Tex2D(rFormat, pState->renderWidth, pState->renderHeight, 1, 1, 1, 0, D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET), D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, cc);
m_renderOutput.CreateRTV(0, &m_renderOutputRTV);
m_displayOutput.InitRenderTarget(m_pDevice, "DisplayOutput", &CD3DX12_RESOURCE_DESC::Tex2D(dFormat, pState->displayWidth, pState->displayHeight, 1, 1, 1, 0, D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS | D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET), D3D12_RESOURCE_STATE_RENDER_TARGET);
m_displayOutput.CreateSRV(0, &m_displayOutputSRV);
m_displayOutput.CreateUAV(0, &m_displayOutputUAV);
if (hdr)
m_displayOutput.CreateRTV(0, &m_displayOutputRTV, -1, -1, -1);
else
m_displayOutput.CreateRTV(0, &m_displayOutputRTV, -1, -1, -1);
m_MagnifierPS.OnCreateWindowSizeDependentResources(&m_displayOutput);
// Lazy Upscale context generation:
if ((m_pUpscaleContext == NULL) || (pState->m_nUpscaleType != m_pUpscaleContext->Type()))
{
if (m_pUpscaleContext)
{
m_pUpscaleContext->OnDestroy();
delete m_pUpscaleContext;
m_pUpscaleContext = NULL;
}
// create upscale context
UpscaleContext::FfxUpscaleInitParams upscaleParams = { pState->m_nUpscaleType, m_bInvertedDepth, m_pDevice, pSwapChain->GetFormat(), &m_UploadHeap, backBufferCount };
m_pUpscaleContext = UpscaleContext::CreateUpscaleContext(upscaleParams);
}
m_pUpscaleContext->OnCreateWindowSizeDependentResources(
m_renderOutput.GetResource(),
m_displayOutput.GetResource(),
pState->renderWidth,
pState->renderHeight,
pState->displayWidth,
pState->displayHeight,
hdr);
m_UploadHeap.FlushAndFinish();
}
//--------------------------------------------------------------------------------------
//
// OnDestroyWindowSizeDependentResources
//
//--------------------------------------------------------------------------------------
void Renderer::OnDestroyWindowSizeDependentResources()
{
m_displayOutput.OnDestroy();
m_renderOutput.OnDestroy();
m_OpaqueTexture.OnDestroy();
m_Bloom.OnDestroyWindowSizeDependentResources();
m_DownSample.OnDestroyWindowSizeDependentResources();
m_RenderPassJustDepthAndHdr.OnDestroyWindowSizeDependentResources();
m_RenderPassFullGBuffer.OnDestroyWindowSizeDependentResources();
m_GBuffer.OnDestroyWindowSizeDependentResources();
// destroy upscale context
if (m_pUpscaleContext)
{
m_pUpscaleContext->OnDestroyWindowSizeDependentResources();
}
m_MagnifierPS.OnDestroyWindowSizeDependentResources();
#if USE_SHADOWMASK
m_ShadowMask.OnDestroy();
#endif
}
void Renderer::OnUpdateDisplayDependentResources(SwapChain* pSwapChain)
{
// Update pipelines in case the format of the RTs changed (this happens when going HDR)
m_ColorConversionPS.UpdatePipelines(pSwapChain->GetFormat(), pSwapChain->GetDisplayMode());
m_ToneMappingPS.UpdatePipelines(pSwapChain->GetFormat());
m_ImGUI.UpdatePipeline((pSwapChain->GetDisplayMode() == DISPLAYMODE_SDR) ? pSwapChain->GetFormat() : m_pGBufferHDRTexture->GetFormat());
}
//--------------------------------------------------------------------------------------
//
// LoadScene
//
//--------------------------------------------------------------------------------------
int Renderer::LoadScene(GLTFCommon *pGLTFCommon, int Stage)
{
// show loading progress
//
ImGui::OpenPopup("Loading");
if (ImGui::BeginPopupModal("Loading", NULL, ImGuiWindowFlags_AlwaysAutoResize))
{
float progress = (float)Stage / 13.0f;
ImGui::ProgressBar(progress, ImVec2(0.f, 0.f), NULL);
ImGui::EndPopup();
}
// use multi threading
AsyncPool *pAsyncPool = &m_AsyncPool;
// Loading stages
//
if (Stage == 0)
{
}
else if (Stage == 5)
{
Profile p("m_pGltfLoader->Load");
m_pGLTFTexturesAndBuffers = new GLTFTexturesAndBuffers();
m_pGLTFTexturesAndBuffers->OnCreate(m_pDevice, pGLTFCommon, &m_UploadHeap, &m_VidMemBufferPool, &m_ConstantBufferRing);
}
else if (Stage == 6)
{
Profile p("LoadTextures");
// here we are loading onto the GPU all the textures and the inverse matrices
// this data will be used to create the PBR and Depth passes
m_pGLTFTexturesAndBuffers->LoadTextures(pAsyncPool);
}
else if (Stage == 7)
{
Profile p("m_GLTFDepth->OnCreate");
//create the glTF's textures, VBs, IBs, shaders and descriptors for this particular pass
m_GLTFDepth = new GltfDepthPass();
m_GLTFDepth->OnCreate(
m_pDevice,
&m_UploadHeap,
&m_ResourceViewHeaps,
&m_ConstantBufferRing,
&m_VidMemBufferPool,
m_pGLTFTexturesAndBuffers,
pAsyncPool,
DXGI_FORMAT_D32_FLOAT,
m_bInvertedDepth
);
}
else if (Stage == 9)
{
Profile p("m_GLTFPBR->OnCreate");
// same thing as above but for the PBR pass
m_GLTFPBR = new GltfPbrPass();
m_GLTFPBR->OnCreate(
m_pDevice,
&m_UploadHeap,
&m_ResourceViewHeaps,
&m_ConstantBufferRing,
m_pGLTFTexturesAndBuffers,
&m_SkyDome,
false, // use a SSAO mask
USE_SHADOWMASK,
&m_RenderPassFullGBuffer,
pAsyncPool,
m_bInvertedDepth
);
}
else if (Stage == 10)
{
Profile p("m_GLTFBBox->OnCreate");
// just a bounding box pass that will draw boundingboxes instead of the geometry itself
m_GLTFBBox = new GltfBBoxPass();
m_GLTFBBox->OnCreate(
m_pDevice,
&m_UploadHeap,
&m_ResourceViewHeaps,
&m_ConstantBufferRing,
&m_VidMemBufferPool,
m_pGLTFTexturesAndBuffers,
&m_Wireframe
);
// we are borrowing the upload heap command list for uploading to the GPU the IBs and VBs
m_VidMemBufferPool.UploadData(m_UploadHeap.GetCommandList());
}
else if (Stage == 11)
{
Profile p("Flush");
m_UploadHeap.FlushAndFinish();
//once everything is uploaded we dont need he upload heaps anymore
m_VidMemBufferPool.FreeUploadHeap();
// tell caller that we are done loading the map
return 0;
}
Stage++;
return Stage;
}
//--------------------------------------------------------------------------------------
//
// UnloadScene
//
//--------------------------------------------------------------------------------------
void Renderer::UnloadScene()
{
// wait for all the async loading operations to finish
m_AsyncPool.Flush();
m_pDevice->GPUFlush();
if (m_GLTFPBR)
{
m_GLTFPBR->OnDestroy();
delete m_GLTFPBR;
m_GLTFPBR = NULL;
}
if (m_GLTFDepth)
{
m_GLTFDepth->OnDestroy();
delete m_GLTFDepth;
m_GLTFDepth = NULL;
}
if (m_GLTFBBox)
{
m_GLTFBBox->OnDestroy();
delete m_GLTFBBox;
m_GLTFBBox = NULL;
}
if (m_pGLTFTexturesAndBuffers)
{
m_pGLTFTexturesAndBuffers->OnDestroy();
delete m_pGLTFTexturesAndBuffers;
m_pGLTFTexturesAndBuffers = NULL;
}
while (!m_shadowMapPool.empty())
{
m_shadowMapPool.back().ShadowMap.OnDestroy();
m_shadowMapPool.pop_back();
}
}
void Renderer::AllocateShadowMaps(GLTFCommon* pGLTFCommon)
{
// Go through the lights and allocate shadow information
uint32_t NumShadows = 0;
for (int i = 0; i < pGLTFCommon->m_lightInstances.size(); ++i)
{
const tfLight& lightData = pGLTFCommon->m_lights[pGLTFCommon->m_lightInstances[i].m_lightId];
if (lightData.m_shadowResolution)
{
SceneShadowInfo ShadowInfo;
ShadowInfo.ShadowResolution = lightData.m_shadowResolution;
ShadowInfo.ShadowIndex = NumShadows++;
ShadowInfo.LightIndex = i;
m_shadowMapPool.push_back(ShadowInfo);
}
}
if (NumShadows > MaxShadowInstances)
{
Trace("Number of shadows has exceeded maximum supported. Please grow value in gltfCommon.h/perFrameStruct.h");
throw;
}
// If we had shadow information, allocate all required maps and bindings
if (!m_shadowMapPool.empty())
{
m_ResourceViewHeaps.AllocDSVDescriptor((uint32_t)m_shadowMapPool.size(), &m_ShadowMapPoolDSV);
m_ResourceViewHeaps.AllocCBV_SRV_UAVDescriptor((uint32_t)m_shadowMapPool.size(), &m_ShadowMapPoolSRV);
std::vector<SceneShadowInfo>::iterator CurrentShadow = m_shadowMapPool.begin();
for( uint32_t i = 0; CurrentShadow < m_shadowMapPool.end(); ++i, ++CurrentShadow)
{
CurrentShadow->ShadowMap.InitDepthStencil(m_pDevice, "m_pShadowMap", &CD3DX12_RESOURCE_DESC::Tex2D(DXGI_FORMAT_D32_FLOAT, CurrentShadow->ShadowResolution, CurrentShadow->ShadowResolution, 1, 1, 1, 0, D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL), 0.0f);
CurrentShadow->ShadowMap.CreateDSV(CurrentShadow->ShadowIndex, &m_ShadowMapPoolDSV);
CurrentShadow->ShadowMap.CreateSRV(CurrentShadow->ShadowIndex, &m_ShadowMapPoolSRV);
}
}
}
//--------------------------------------------------------------------------------------
//
// OnRender
//
//--------------------------------------------------------------------------------------
void Renderer::OnRender(UIState* pState, const Camera& Cam, SwapChain* pSwapChain)
{
const bool bHDR = pSwapChain->GetDisplayMode() != DISPLAYMODE_SDR;
bool bUseUpscale = pState->m_nUpscaleType != UPSCALE_TYPE_NATIVE;
bool bUseTaaRcas = pState->bUseTAA || pState->bUseRcas;
GBuffer* pGBuffer = &m_GBuffer;
GBufferRenderPass* pRenderPassFullGBuffer = &m_RenderPassFullGBuffer;
GBufferRenderPass* pRenderPassJustDepthAndHdr = &m_RenderPassJustDepthAndHdr;
GltfPbrPass* pGltfPbr = m_GLTFPBR;
// Timing values
UINT64 gpuTicksPerSecond;
m_pDevice->GetGraphicsQueue()->GetTimestampFrequency(&gpuTicksPerSecond);
// Let our resource managers do some house keeping
m_CommandListRing.OnBeginFrame();
m_ConstantBufferRing.OnBeginFrame();
m_GPUTimer.OnBeginFrame(gpuTicksPerSecond, &m_TimeStamps);
// Render size viewport
D3D12_VIEWPORT vpr = { 0.0f, 0.0f, static_cast<float>(pState->renderWidth), static_cast<float>(pState->renderHeight), 0.0f, 1.0f };
D3D12_RECT srr = { 0, 0, (LONG)pState->renderWidth, (LONG)pState->renderHeight };
// Display size viewport
D3D12_VIEWPORT vpd = { 0.0f, 0.0f, static_cast<float>(pState->displayWidth), static_cast<float>(pState->displayHeight), 0.0f, 1.0f };
D3D12_RECT srd = { 0, 0, (LONG)pState->displayWidth, (LONG)pState->displayHeight };
m_pUpscaleContext->PreDraw(pState);
static float fLightModHelper = 2.f;
// Sets the perFrame data
per_frame *pPerFrame = NULL;
if (m_pGLTFTexturesAndBuffers)
{
// fill as much as possible using the GLTF (camera, lights, ...)
pPerFrame = m_pGLTFTexturesAndBuffers->m_pGLTFCommon->SetPerFrameData(Cam);
// Set some lighting factors
pPerFrame->iblFactor = pState->IBLFactor;
pPerFrame->emmisiveFactor = 1.0f;
pPerFrame->invScreenResolution[0] = 1.0f / ((float)pState->renderWidth);
pPerFrame->invScreenResolution[1] = 1.0f / ((float)pState->renderHeight);
pPerFrame->wireframeOptions.setX(pState->WireframeColor[0]);
pPerFrame->wireframeOptions.setY(pState->WireframeColor[1]);
pPerFrame->wireframeOptions.setZ(pState->WireframeColor[2]);
pPerFrame->wireframeOptions.setW(pState->WireframeMode == UIState::WireframeMode::WIREFRAME_MODE_SOLID_COLOR ? 1.0f : 0.0f);
pPerFrame->lodBias = pState->mipBias;
m_pGLTFTexturesAndBuffers->SetPerFrameConstants();
m_pGLTFTexturesAndBuffers->SetSkinningMatricesForSkeletons();
}
// command buffer calls
ID3D12GraphicsCommandList* pCmdLst1 = m_CommandListRing.GetNewCommandList();
m_GPUTimer.GetTimeStamp(pCmdLst1, "Begin Frame");
pCmdLst1->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(pSwapChain->GetCurrentBackBufferResource(), D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET));
// Render shadow maps
std::vector<CD3DX12_RESOURCE_BARRIER> ShadowReadBarriers;
std::vector<CD3DX12_RESOURCE_BARRIER> ShadowWriteBarriers;
if (m_GLTFDepth && pPerFrame != NULL)
{
std::vector<SceneShadowInfo>::iterator ShadowMap = m_shadowMapPool.begin();
while (ShadowMap < m_shadowMapPool.end())
{
pCmdLst1->ClearDepthStencilView(m_ShadowMapPoolDSV.GetCPU(ShadowMap->ShadowIndex), D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr);
++ShadowMap;
}
m_GPUTimer.GetTimeStamp(pCmdLst1, "Clear shadow maps");
// Render all shadows
ShadowMap = m_shadowMapPool.begin();
while (ShadowMap < m_shadowMapPool.end())
{
SetViewportAndScissor(pCmdLst1, 0, 0, ShadowMap->ShadowResolution, ShadowMap->ShadowResolution);
pCmdLst1->OMSetRenderTargets(0, NULL, false, &m_ShadowMapPoolDSV.GetCPU(ShadowMap->ShadowIndex));
per_frame* cbDepthPerFrame = m_GLTFDepth->SetPerFrameConstants();
cbDepthPerFrame->mCameraCurrViewProj = pPerFrame->lights[ShadowMap->LightIndex].mLightViewProj;
cbDepthPerFrame->lodBias = pState->mipBias;
m_GLTFDepth->Draw(pCmdLst1);
// Push a barrier
ShadowReadBarriers.push_back(CD3DX12_RESOURCE_BARRIER::Transition(ShadowMap->ShadowMap.GetResource(), D3D12_RESOURCE_STATE_DEPTH_WRITE, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE));
ShadowWriteBarriers.push_back(CD3DX12_RESOURCE_BARRIER::Transition(ShadowMap->ShadowMap.GetResource(), D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_DEPTH_WRITE));
m_GPUTimer.GetTimeStamp(pCmdLst1, "Shadow map");
++ShadowMap;
}
// Transition all shadow map barriers
pCmdLst1->ResourceBarrier((UINT)ShadowReadBarriers.size(), ShadowReadBarriers.data());
}
// Shadow resolve ---------------------------------------------------------------------------
#if USE_SHADOWMASK
if (pPerFrame != NULL)
{
const D3D12_RESOURCE_BARRIER preShadowResolve[] =
{
CD3DX12_RESOURCE_BARRIER::Transition(m_ShadowMask.GetResource(), D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS),
CD3DX12_RESOURCE_BARRIER::Transition(m_MotionVectorsDepthMap.GetResource(), D3D12_RESOURCE_STATE_DEPTH_WRITE, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE)
};
pCmdLst1->ResourceBarrier(ARRAYSIZE(preShadowResolve), preShadowResolve);
ShadowResolveFrame shadowResolveFrame;
shadowResolveFrame.m_Width = m_Width;
shadowResolveFrame.m_Height = m_Height;
shadowResolveFrame.m_ShadowMapSRV = m_ShadowMapSRV;
shadowResolveFrame.m_DepthBufferSRV = m_MotionVectorsDepthMapSRV;
shadowResolveFrame.m_ShadowBufferUAV = m_ShadowMaskUAV;
m_shadowResolve.Draw(pCmdLst1, m_pGLTFTexturesAndBuffers, &shadowResolveFrame);
const D3D12_RESOURCE_BARRIER postShadowResolve[] =
{
CD3DX12_RESOURCE_BARRIER::Transition(m_ShadowMask.GetResource(), D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE),
CD3DX12_RESOURCE_BARRIER::Transition(m_MotionVectorsDepthMap.GetResource(), D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_DEPTH_WRITE)
};
pCmdLst1->ResourceBarrier(ARRAYSIZE(postShadowResolve), postShadowResolve);
}
m_GPUTimer.GetTimeStamp(pCmdLst1, "Shadow resolve");
#endif
// Render Scene to the GBuffer ------------------------------------------------
if (pPerFrame != NULL)
{
pCmdLst1->RSSetViewports(1, &vpr);
pCmdLst1->RSSetScissorRects(1, &srr);
if (pGltfPbr)
{
const bool bWireframe = pState->WireframeMode != UIState::WireframeMode::WIREFRAME_MODE_OFF;
std::vector<GltfPbrPass::BatchList> opaque, transparent;
pGltfPbr->BuildBatchLists(&opaque, &transparent, bWireframe);
// Render opaque geometry
{
pRenderPassFullGBuffer->BeginPass(pCmdLst1, true);
#if USE_SHADOWMASK
pGltfPbr->DrawBatchList(pCmdLst1, &m_ShadowMaskSRV, &solid, bWireframe);
#else
pGltfPbr->DrawBatchList(pCmdLst1, &m_ShadowMapPoolSRV, &opaque, bWireframe);
#endif
m_GPUTimer.GetTimeStamp(pCmdLst1, "PBR Opaque");
pRenderPassFullGBuffer->EndPass();
}
// draw skydome
{
pRenderPassJustDepthAndHdr->BeginPass(pCmdLst1, false);
// Render skydome
if (pState->SelectedSkydomeTypeIndex == 1)
{
math::Matrix4 clipToView = math::inverse(pPerFrame->mCameraCurrViewProj);
m_SkyDome.Draw(pCmdLst1, clipToView);
m_GPUTimer.GetTimeStamp(pCmdLst1, "Skydome cube");
}
else if (pState->SelectedSkydomeTypeIndex == 0)
{
SkyDomeProc::Constants skyDomeConstants;
skyDomeConstants.invViewProj = math::inverse(pPerFrame->mCameraCurrViewProj);
skyDomeConstants.vSunDirection = math::Vector4(1.0f, 0.05f, 0.0f, 0.0f);
skyDomeConstants.turbidity = 10.0f;
skyDomeConstants.rayleigh = 2.0f;
skyDomeConstants.mieCoefficient = 0.005f;
skyDomeConstants.mieDirectionalG = 0.8f;
skyDomeConstants.luminance = 1.0f;
m_SkyDomeProc.Draw(pCmdLst1, skyDomeConstants);
m_GPUTimer.GetTimeStamp(pCmdLst1, "Skydome proc");
}
pRenderPassJustDepthAndHdr->EndPass();
}
{
// Copy resource before we render transparent stuff
pCmdLst1->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_OpaqueTexture.GetResource(), D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_COPY_DEST));
pCmdLst1->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_GBuffer.m_HDR.GetResource(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE));
pCmdLst1->CopyResource(m_OpaqueTexture.GetResource(), m_GBuffer.m_HDR.GetResource());
pCmdLst1->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_OpaqueTexture.GetResource(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE));
pCmdLst1->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_GBuffer.m_HDR.GetResource(), D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET));
}
// draw transparent geometry
{
pRenderPassFullGBuffer->BeginPass(pCmdLst1, false);
std::sort(transparent.begin(), transparent.end());
pGltfPbr->DrawBatchList(pCmdLst1, &m_ShadowMapPoolSRV, &transparent, bWireframe);
m_GPUTimer.GetTimeStamp(pCmdLst1, "PBR Transparent");
pRenderPassFullGBuffer->EndPass();
}
}
// draw object's bounding boxes
if (m_GLTFBBox && pPerFrame != NULL)
{
if (pState->bDrawBoundingBoxes)
{
m_GLTFBBox->Draw(pCmdLst1, pPerFrame->mCameraCurrViewProj);
m_GPUTimer.GetTimeStamp(pCmdLst1, "Bounding Box");
}
}
// draw light's frustums
if (pState->bDrawLightFrustum && pPerFrame != NULL)
{
UserMarker marker(pCmdLst1, "light frustrums");
math::Vector4 vCenter = math::Vector4(0.0f, 0.0f, 0.5f, 0.0f);
math::Vector4 vRadius = math::Vector4(1.0f, 1.0f, 0.5f, 0.0f);
math::Vector4 vColor = math::Vector4(1.0f, 1.0f, 1.0f, 1.0f);
for (uint32_t i = 0; i < pPerFrame->lightCount; i++)
{
math::Matrix4 spotlightMatrix = math::inverse(pPerFrame->lights[i].mLightViewProj);
math::Matrix4 worldMatrix = pPerFrame->mCameraCurrViewProj * spotlightMatrix;
m_WireframeBox.Draw(pCmdLst1, &m_Wireframe, worldMatrix, vCenter, vRadius, vColor);
}
m_GPUTimer.GetTimeStamp(pCmdLst1, "Light's frustum");
}
}
if (ShadowWriteBarriers.size())
pCmdLst1->ResourceBarrier((UINT)ShadowWriteBarriers.size(), ShadowWriteBarriers.data());
D3D12_RESOURCE_BARRIER preResolve[1] = {
CD3DX12_RESOURCE_BARRIER::Transition(pGBuffer->m_HDR.GetResource(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE),
};
pCmdLst1->ResourceBarrier(1, preResolve);
// Post proc---------------------------------------------------------------------------
// Bloom, takes HDR as input and applies bloom to it.
{
D3D12_CPU_DESCRIPTOR_HANDLE renderTargets[] = { pGBuffer->m_HDRRTV.GetCPU() };
pCmdLst1->OMSetRenderTargets(ARRAYSIZE(renderTargets), renderTargets, false, NULL);
m_DownSample.Draw(pCmdLst1);
m_GPUTimer.GetTimeStamp(pCmdLst1, "Downsample");
m_Bloom.Draw(pCmdLst1, &pGBuffer->m_HDR);
m_GPUTimer.GetTimeStamp(pCmdLst1, "Bloom");
}
// Start tracking input/output resources at this point to handle HDR and SDR render paths
ID3D12Resource* pRscCurrentInput = pGBuffer->m_HDR.GetResource();
CBV_SRV_UAV SRVCurrentInput = pGBuffer->m_HDRSRV;
D3D12_CPU_DESCRIPTOR_HANDLE RTVCurrentOutput = pGBuffer->m_HDRRTV.GetCPU();
CBV_SRV_UAV UAVCurrentOutput = pGBuffer->m_HDRUAV;
// Always use upscale context, if we don't want one, Spatial can skip upscale (but still do TAA and/or RCAS)
if(bUseUpscale || bUseTaaRcas)
{
pCmdLst1->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(pGBuffer->m_HDR.GetResource(), D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE| D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE));
pCmdLst1->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(pGBuffer->m_MotionVectors.GetResource(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE));
pCmdLst1->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(pGBuffer->m_UpscaleReactive.GetResource(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE));
pCmdLst1->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(pGBuffer->m_UpscaleTransparencyAndComposition.GetResource(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE));
pCmdLst1->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(pGBuffer->m_DepthBuffer.GetResource(), D3D12_RESOURCE_STATE_DEPTH_WRITE, D3D12_RESOURCE_STATE_DEPTH_READ | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE));
UpscaleContext::FfxUpscaleSetup upscaleSetup;
upscaleSetup.cameraSetup.vCameraPos = pState->camera.GetPosition();
upscaleSetup.cameraSetup.mCameraView = pState->camera.GetView();
upscaleSetup.cameraSetup.mCameraViewInv = math::inverse(pState->camera.GetView());
upscaleSetup.cameraSetup.mCameraProj = pState->camera.GetProjection();
upscaleSetup.opaqueOnlyColorResource = m_OpaqueTexture.GetResource();
upscaleSetup.unresolvedColorResource = m_GBuffer.m_HDR.GetResource();
upscaleSetup.motionvectorResource = m_GBuffer.m_MotionVectors.GetResource();
upscaleSetup.depthbufferResource = m_GBuffer.m_DepthBuffer.GetResource();
upscaleSetup.reactiveMapResource = m_GBuffer.m_UpscaleReactive.GetResource();
upscaleSetup.transparencyAndCompositionResource = m_GBuffer.m_UpscaleTransparencyAndComposition.GetResource();
upscaleSetup.resolvedColorResource = m_displayOutput.GetResource();
pCmdLst1->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_displayOutput.GetResource(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_UNORDERED_ACCESS));
m_pUpscaleContext->Draw(pCmdLst1, upscaleSetup, pState);
pCmdLst1->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_displayOutput.GetResource(), D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_RENDER_TARGET));
SRVCurrentInput = m_displayOutputSRV;
{
vpr = { 0.0f, 0.0f, static_cast<float>(pState->displayWidth), static_cast<float>(pState->displayHeight), 0.0f, 1.0f };
srr = { 0, 0, (LONG)pState->displayWidth, (LONG)pState->displayHeight };
}
pCmdLst1->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(pGBuffer->m_DepthBuffer.GetResource(), D3D12_RESOURCE_STATE_DEPTH_READ | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_DEPTH_WRITE));
pCmdLst1->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(pGBuffer->m_MotionVectors.GetResource(), D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET));
pCmdLst1->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(pGBuffer->m_UpscaleReactive.GetResource(), D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET));
pCmdLst1->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(pGBuffer->m_UpscaleTransparencyAndComposition.GetResource(), D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET));
pCmdLst1->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(pGBuffer->m_HDR.GetResource(), D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE));
if(bUseUpscale)
m_GPUTimer.GetTimeStamp(pCmdLst1, m_pUpscaleContext->Name().c_str());
else if(pState->bUseTAA && !pState->bUseRcas)
m_GPUTimer.GetTimeStamp(pCmdLst1, "TAA");
else if (!pState->bUseTAA && pState->bUseRcas)
m_GPUTimer.GetTimeStamp(pCmdLst1, "RCAS");
else if (pState->bUseTAA && pState->bUseRcas)
m_GPUTimer.GetTimeStamp(pCmdLst1, "TAA & RCAS");
pCmdLst1->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_displayOutput.GetResource(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE));
pCmdLst1->OMSetRenderTargets(1, &m_displayOutputRTV.GetCPU(), true, NULL);
pRscCurrentInput = m_displayOutput.GetResource();
SRVCurrentInput = m_displayOutputSRV;
RTVCurrentOutput = m_displayOutputRTV.GetCPU();
UAVCurrentOutput = m_displayOutputUAV;
}
// Upscale done, display size is now the main target
pCmdLst1->RSSetViewports(1, &vpd);
pCmdLst1->RSSetScissorRects(1, &srd);
// If using FreeSync HDR we need to do the tonemapping in-place and then apply the GUI, later we'll apply the color conversion into the swapchain
if (bHDR)
{
// Magnifier Pass: m_HDR as input, pass' own output
if (pState->bUseMagnifier)
{
// Note: assumes m_GBuffer.HDR is in D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE
m_MagnifierPS.Draw(pCmdLst1, pState->MagnifierParams, SRVCurrentInput);
m_GPUTimer.GetTimeStamp(pCmdLst1, "Magnifier");
pRscCurrentInput = m_MagnifierPS.GetPassOutputResource();
SRVCurrentInput = m_MagnifierPS.GetPassOutputSRV();
RTVCurrentOutput = m_MagnifierPS.GetPassOutputRTV().GetCPU();
UAVCurrentOutput = m_MagnifierPS.GetPassOutputUAV();
pCmdLst1->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_MagnifierPS.GetPassOutputResource(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE));
}
// In place Tonemapping ------------------------------------------------------------------------
{
D3D12_RESOURCE_BARRIER inputRscToUAV = CD3DX12_RESOURCE_BARRIER::Transition(pRscCurrentInput, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
pCmdLst1->ResourceBarrier(1, &inputRscToUAV);
m_ToneMappingCS.Draw(pCmdLst1, &UAVCurrentOutput, pState->ExposureHdr, pState->SelectedTonemapperIndex, pState->displayWidth, pState->displayHeight);
D3D12_RESOURCE_BARRIER inputRscToSRV = CD3DX12_RESOURCE_BARRIER::Transition(pRscCurrentInput, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_RENDER_TARGET);
pCmdLst1->ResourceBarrier(1, &inputRscToSRV);
}
// Render HUD ------------------------------------------------------------------------
{
pCmdLst1->OMSetRenderTargets(1, &RTVCurrentOutput, true, NULL);
pCmdLst1->RSSetViewports(1, &vpd);
pCmdLst1->RSSetScissorRects(1, &srd);
m_ImGUI.Draw(pCmdLst1);
m_GPUTimer.GetTimeStamp(pCmdLst1, "ImGUI Rendering");
// reset viewport/scissorRects after rendering UI
pCmdLst1->RSSetViewports(1, &vpd);
pCmdLst1->RSSetScissorRects(1, &srd);
D3D12_RESOURCE_BARRIER inputRscToSRV = CD3DX12_RESOURCE_BARRIER::Transition(pRscCurrentInput, D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
pCmdLst1->ResourceBarrier(1, &inputRscToSRV);
}
}
// Keep tracking input/output resource views
RTVCurrentOutput = *pSwapChain->GetCurrentBackBufferRTV();
UAVCurrentOutput = {}; // no BackBufferUAV.
ID3D12GraphicsCommandList* pCmdLst2 = pCmdLst1;
if (bHDR)
{
pCmdLst2->OMSetRenderTargets(1, pSwapChain->GetCurrentBackBufferRTV(), true, NULL);
// FS HDR mode! Apply color conversion now.
m_ColorConversionPS.Draw(pCmdLst2, &SRVCurrentInput);
m_GPUTimer.GetTimeStamp(pCmdLst2, "Color conversion");
}
else
{
// Magnifier Pass: m_HDR as input, pass' own output
if (pState->bUseMagnifier)
{
// Note: assumes m_GBuffer.HDR is in D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE
m_MagnifierPS.Draw(pCmdLst2, pState->MagnifierParams, SRVCurrentInput);
m_GPUTimer.GetTimeStamp(pCmdLst2, "Magnifier");
pRscCurrentInput = m_MagnifierPS.GetPassOutputResource();
SRVCurrentInput = m_MagnifierPS.GetPassOutputSRV();
RTVCurrentOutput = m_MagnifierPS.GetPassOutputRTV().GetCPU();
UAVCurrentOutput = m_MagnifierPS.GetPassOutputUAV();
pCmdLst2->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_MagnifierPS.GetPassOutputResource(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE));
}
// Tonemapping ------------------------------------------------------------------------
{
pCmdLst2->OMSetRenderTargets(1, pSwapChain->GetCurrentBackBufferRTV(), true, NULL);
pCmdLst2->RSSetScissorRects(1, &srd);
m_ToneMappingPS.Draw(pCmdLst2, &SRVCurrentInput, pState->Exposure, pState->SelectedTonemapperIndex);
m_GPUTimer.GetTimeStamp(pCmdLst2, "Tone mapping");
}
// Render HUD to swapchain directly ------------------------------------------------------------------------
{
m_ImGUI.Draw(pCmdLst2);
m_GPUTimer.GetTimeStamp(pCmdLst2, "ImGUI Rendering");
}
}
pCmdLst2->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(pGBuffer->m_HDR.GetResource(), D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET));
if (bUseUpscale || bUseTaaRcas)
pCmdLst2->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_displayOutput.GetResource(), D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET));
if (pState->bUseMagnifier)
pCmdLst2->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_MagnifierPS.GetPassOutputResource(), D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET));
if (!m_pScreenShotName.empty())
{
m_SaveTexture.CopyRenderTargetIntoStagingTexture(m_pDevice->GetDevice(), pCmdLst2, pSwapChain->GetCurrentBackBufferResource(), D3D12_RESOURCE_STATE_RENDER_TARGET);
}
// Transition swapchain into present mode
pCmdLst2->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(pSwapChain->GetCurrentBackBufferResource(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT));
if (pState->bEnableFrameRateLimiter &&
pState->bUseGPUFrameRateLimiter &&
!m_TimeStamps.empty())
{
m_GpuFrameRateLimiter.Draw(pCmdLst2, &m_ConstantBufferRing, uint64_t(m_TimeStamps.back().m_microseconds), uint64_t(pState->targetFrameTime * 1000));
m_GPUTimer.GetTimeStamp(pCmdLst2, "FrameRateLimiter");
}
m_GPUTimer.OnEndFrame();
m_GPUTimer.CollectTimings(pCmdLst2);
// Close & Submit the command list #2 -------------------------------------------------
ThrowIfFailed(pCmdLst2->Close());
ID3D12CommandList* CmdListList2[] = { pCmdLst2 };
m_pDevice->GetGraphicsQueue()->ExecuteCommandLists(1, CmdListList2);
// Handle screenshot request
if (!m_pScreenShotName.empty())
{
m_SaveTexture.SaveStagingTextureAsJpeg(m_pDevice->GetDevice(), m_pDevice->GetGraphicsQueue(), m_pScreenShotName.c_str());
m_pScreenShotName.clear();
}
// wait for swapchain: ensure timings in the sample are correct, not something you'd want to do in a title
pSwapChain->WaitForSwapChain();
}
void Renderer::ResetScene()
{
}
void Renderer::PopulateEmitters(float frameTime)
{
bool m_Paused = false;
}
void Renderer::BuildDevUI(UIState* pState)
{
if (m_pUpscaleContext)
{
m_pUpscaleContext->BuildDevUI(pState);
}
}

@ -0,0 +1,158 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#pragma once
#include "stdafx.h"
#include "base/GBuffer.h"
#include "PostProc/MagnifierPS.h"
#include "UpscaleContext.h"
#include "GPUFrameRateLimiter.h"
struct UIState;
// We are queuing (backBufferCount + 0.5) frames, so we need to triple buffer the resources that get modified each frame
static const int backBufferCount = 3;
#define USE_SHADOWMASK false
using namespace CAULDRON_DX12;
//
// Renderer class is responsible for rendering resources management and recording command buffers.
class Renderer
{
public:
void OnCreate(Device* pDevice, SwapChain *pSwapChain, float FontSize, bool bInvertedDepth = false);
void OnDestroy();
void OnCreateWindowSizeDependentResources(SwapChain *pSwapChain, UIState* pState);
void OnDestroyWindowSizeDependentResources();
void OnUpdateDisplayDependentResources(SwapChain *pSwapChain);
int LoadScene(GLTFCommon *pGLTFCommon, int Stage = 0);
void UnloadScene();
void AllocateShadowMaps(GLTFCommon* pGLTFCommon);
const std::vector<TimeStamp>& GetTimingValues() const { return m_TimeStamps; }
std::string& GetScreenshotFileName() { return m_pScreenShotName; }
void OnRender(UIState* pState, const Camera& Cam, SwapChain* pSwapChain);
void ResetScene();
void PopulateEmitters(float frameTime);
void BuildDevUI(UIState* pState);
private:
Device *m_pDevice;
uint32_t m_Width;
uint32_t m_Height;
D3D12_VIEWPORT m_Viewport;
D3D12_RECT m_RectScissor;
bool m_HasTAA = false;
bool m_bInvertedDepth;
// Initialize helper classes
ResourceViewHeaps m_ResourceViewHeaps;
UploadHeap m_UploadHeap;
DynamicBufferRing m_ConstantBufferRing;
StaticBufferPool m_VidMemBufferPool;
CommandListRing m_CommandListRing;
GPUTimestamps m_GPUTimer;
GPUFrameRateLimiter m_GpuFrameRateLimiter;
//gltf passes
GltfPbrPass *m_GLTFPBR;
GltfBBoxPass *m_GLTFBBox;
GltfDepthPass *m_GLTFDepth;
GLTFTexturesAndBuffers *m_pGLTFTexturesAndBuffers;
// effects
Bloom m_Bloom;
SkyDome m_SkyDome;
DownSamplePS m_DownSample;
SkyDomeProc m_SkyDomeProc;
ToneMapping m_ToneMappingPS;
ToneMappingCS m_ToneMappingCS;
ColorConversionPS m_ColorConversionPS;
MagnifierPS m_MagnifierPS;
// TAA
CBV_SRV_UAV m_UpscaleSRVs;
UpscaleContext* m_pUpscaleContext;
// GUI
ImGUI m_ImGUI;
// Temporary render targets
GBuffer m_GBuffer;
GBufferRenderPass m_RenderPassFullGBuffer;
GBufferRenderPass m_RenderPassJustDepthAndHdr;
Texture* m_pGBufferHDRTexture = NULL;
Texture m_OpaqueTexture;
Texture m_MotionVectorsDepthMap;
DSV m_MotionVectorsDepthMapDSV;
CBV_SRV_UAV m_MotionVectorsDepthMapSRV;
#if USE_SHADOWMASK
// shadow mask
Texture m_ShadowMask;
CBV_SRV_UAV m_ShadowMaskUAV;
CBV_SRV_UAV m_ShadowMaskSRV;
ShadowResolvePass m_shadowResolve;
#endif
// shadowmaps
typedef struct {
Texture ShadowMap;
uint32_t ShadowIndex;
uint32_t ShadowResolution;
uint32_t LightIndex;
} SceneShadowInfo;
std::vector<SceneShadowInfo> m_shadowMapPool;
DSV m_ShadowMapPoolDSV;
CBV_SRV_UAV m_ShadowMapPoolSRV;
Texture m_renderOutput;
RTV m_renderOutputRTV;
Texture m_displayOutput;
CBV_SRV_UAV m_displayOutputSRV;
RTV m_displayOutputRTV;
CBV_SRV_UAV m_displayOutputUAV;
// widgets
Wireframe m_Wireframe;
WireframeBox m_WireframeBox;
std::vector<TimeStamp> m_TimeStamps;
// screen shot
std::string m_pScreenShotName = "";
SaveTexture m_SaveTexture;
AsyncPool m_AsyncPool;
};

@ -0,0 +1,210 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "ShaderCompiler.h"
namespace FSR2
{
ShaderCompiler::ShaderCompiler()
{
}
ShaderCompiler::~ShaderCompiler()
{
SafeRelease(validator_);
SafeRelease(compiler_);
SafeRelease(library_);
SafeRelease(includeHandler_);
SafeRelease(linker_);
if (dll_) {
FreeLibrary(dll_);
}
}
HRESULT ShaderCompiler::init(const wchar_t* pathDXCompiler_dll, const wchar_t* pathDXIL_dll)
{
HRESULT status = S_OK;
HMODULE dllDXIL = LoadLibraryW(pathDXIL_dll);
dll_ = LoadLibraryW(pathDXCompiler_dll);
if (!dll_) {
//fmt::print(std::cerr, "Cannot load dxcompiler.dll\n");
return E_FAIL;
}
DxcCreateInstanceProc pfnDxcCreateInstance = DxcCreateInstanceProc(GetProcAddress(dll_, "DxcCreateInstance"));
if (pfnDxcCreateInstance == nullptr)
{
//fmt::print(std::cerr, "Failed to DxcCreateInstance address.\n");
return E_FAIL;
}
SafeRelease(validator_);
if (FAILED(pfnDxcCreateInstance(CLSID_DxcValidator, IID_PPV_ARGS(&validator_)))) {
//fmt::print(std::cerr, "Failed creating DxcValidator\n");
status = E_FAIL;
}
SafeRelease(compiler_);
if (FAILED(pfnDxcCreateInstance(CLSID_DxcCompiler, IID_PPV_ARGS(&compiler_)))) {
//fmt::print(std::cerr, "Failed create DxcCompiler instance.\n");
status = E_FAIL;
}
SafeRelease(library_);
if (FAILED(pfnDxcCreateInstance(CLSID_DxcLibrary, IID_PPV_ARGS(&library_)))) {
//fmt::print(std::cerr, "Failed creating DxcLibrary instance.\n");
status = E_FAIL;
}
SafeRelease(reflection_);
if (FAILED(pfnDxcCreateInstance(CLSID_DxcContainerReflection, IID_PPV_ARGS(&reflection_)))) {
//fmt::print(std::cerr, "Failed creating DxcLibrary instance.\n");
status = E_FAIL;
}
if (FAILED(library_->CreateIncludeHandler(&includeHandler_))) {
//fmt::print(std::cerr, "Failed creating Dxc include handler.\n");
status = E_FAIL;
}
SafeRelease(linker_);
if (FAILED(pfnDxcCreateInstance(CLSID_DxcLinker, IID_PPV_ARGS(&linker_)))) {
//fmt::print(std::cerr, "Failed creating DxcLinker instance.\n");
status = E_FAIL;
}
return status;
}
HRESULT ShaderCompiler::validate(IDxcBlob* input, IDxcOperationResult** ppResult, UINT32 flags)
{
return validator_->Validate(input, flags, ppResult);
}
IDxcContainerReflection* ShaderCompiler::dgbReflection()
{
return reflection_;
}
HRESULT ShaderCompiler::compile(LPCWSTR FilePath, IDxcBlobEncoding* source, ShaderCompilationDesc* desc, IDxcBlob** ppResult)
{
HRESULT hr = E_FAIL;
std::wstring s;
for (UINT d = 0; d < (UINT)desc->Defines.size(); ++d)
{
s += L"-D";
s += desc->Defines[d].Name;
s += L"=";
s += desc->Defines[d].Value;
s += L" ";
}
IDxcOperationResult* pResult = nullptr;
if (SUCCEEDED(hr = compiler_->Compile(
source, // program text
FilePath, // file name, mostly for error messages
desc->EntryPoint, // entry point function
desc->TargetProfile, // target profile
desc->CompileArguments.data(), // compilation arguments
(UINT)desc->CompileArguments.size(), // number of compilation arguments
desc->Defines.data(), // define arguments
(UINT)desc->Defines.size(), // number of define arguments
includeHandler_, // handler for #include directives
&pResult)))
{
HRESULT hrCompile = E_FAIL;
if (SUCCEEDED(hr = pResult->GetStatus(&hrCompile)))
{
if (SUCCEEDED(hrCompile))
{
if (ppResult)
{
pResult->GetResult(ppResult);
hr = S_OK;
}
else
{
hr = E_FAIL;
}
}
}
// Always try and get the error buffer to display possible warnings.
IDxcBlobEncoding* pPrintBlob = nullptr;
if (SUCCEEDED(pResult->GetErrorBuffer(&pPrintBlob)))
{
// We can use the library to get our preferred encoding.
IDxcBlobEncoding* pPrintBlob16 = nullptr;
library_->GetBlobAsUtf16(pPrintBlob, &pPrintBlob16);
OutputDebugStringW(static_cast<const wchar_t*>(pPrintBlob16->GetBufferPointer()));
//fmt::print(std::wcerr, L"{}", static_cast<const wchar_t*>(pPrintBlob16->GetBufferPointer()));
SafeRelease(pPrintBlob);
SafeRelease(pPrintBlob16);
}
SafeRelease(pResult);
}
return hr;
}
HRESULT ShaderCompiler::compileFromFile(LPCWSTR FilePath, ShaderCompilationDesc* desc, IDxcBlob** ppResult)
{
HRESULT hr = E_FAIL;
if (desc)
{
IDxcBlobEncoding* source = nullptr;
if (SUCCEEDED(hr = library_->CreateBlobFromFile(FilePath, nullptr, &source)))
{
if (SUCCEEDED(hr = compile(FilePath, source, desc, ppResult)))
{
SafeRelease(source);
}
}
}
return hr;
}
HRESULT ShaderCompiler::compileFromString(std::string code, ShaderCompilationDesc* desc, IDxcBlob** ppResult)
{
HRESULT hr = E_FAIL;
if (desc)
{
IDxcBlobEncoding* source = nullptr;
if (SUCCEEDED(hr = library_->CreateBlobWithEncodingFromPinned(code.data(), (UINT32)code.length(), CP_ACP, &source)))
{
if (SUCCEEDED(hr = compile(L"NOT_SET", source, desc, ppResult)))
{
SafeRelease(source);
}
}
}
return hr;
}
}

@ -0,0 +1,114 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#pragma once
#define WIN32_LEAN_AND_MEAN
#include "windows.h"
#include <dxgi.h>
#include <string>
#include <map>
#include <vector>
#include <dxcapi.h>
namespace FSR2
{
// Safe release for interfaces
template<class Interface>
inline void SafeRelease(Interface*& pInterfaceToRelease)
{
if (pInterfaceToRelease != nullptr)
{
pInterfaceToRelease->Release();
pInterfaceToRelease = nullptr;
}
}
template<class Interface>
inline void SafeDelete(Interface*& pInterfaceToDelete)
{
if (pInterfaceToDelete != nullptr)
{
delete pInterfaceToDelete;
pInterfaceToDelete = nullptr;
}
}
#define DXIL_FOURCC(ch0, ch1, ch2, ch3) ( \
(uint32_t)(uint8_t)(ch0) | (uint32_t)(uint8_t)(ch1) << 8 | \
(uint32_t)(uint8_t)(ch2) << 16 | (uint32_t)(uint8_t)(ch3) << 24 \
)
enum DxilFourCC {
DFCC_Container = DXIL_FOURCC('D', 'X', 'B', 'C'), // for back-compat with tools that look for DXBC containers
DFCC_ResourceDef = DXIL_FOURCC('R', 'D', 'E', 'F'),
DFCC_InputSignature = DXIL_FOURCC('I', 'S', 'G', '1'),
DFCC_OutputSignature = DXIL_FOURCC('O', 'S', 'G', '1'),
DFCC_PatchConstantSignature = DXIL_FOURCC('P', 'S', 'G', '1'),
DFCC_ShaderStatistics = DXIL_FOURCC('S', 'T', 'A', 'T'),
DFCC_ShaderDebugInfoDXIL = DXIL_FOURCC('I', 'L', 'D', 'B'),
DFCC_ShaderDebugName = DXIL_FOURCC('I', 'L', 'D', 'N'),
DFCC_FeatureInfo = DXIL_FOURCC('S', 'F', 'I', '0'),
DFCC_PrivateData = DXIL_FOURCC('P', 'R', 'I', 'V'),
DFCC_RootSignature = DXIL_FOURCC('R', 'T', 'S', '0'),
DFCC_DXIL = DXIL_FOURCC('D', 'X', 'I', 'L'),
DFCC_PipelineStateValidation = DXIL_FOURCC('P', 'S', 'V', '0'),
};
struct ShaderCompilationDesc
{
LPCWSTR EntryPoint;
LPCWSTR TargetProfile;
std::vector<LPCWSTR> CompileArguments;
std::vector<DxcDefine> Defines;
};
class ShaderCompiler
{
IDxcLibrary* library_ = nullptr;
IDxcCompiler* compiler_ = nullptr;
IDxcLinker* linker_ = nullptr;
IDxcIncludeHandler* includeHandler_ = nullptr;
IDxcValidator* validator_ = nullptr;
IDxcContainerReflection* reflection_ = nullptr;
HMODULE dll_ = 0;
HRESULT compile(LPCWSTR FilePath, IDxcBlobEncoding* source, ShaderCompilationDesc* desc, IDxcBlob** ppResult);
public:
ShaderCompiler();
~ShaderCompiler();
HRESULT init(const wchar_t* pathDXCompiler_dll, const wchar_t* pathDXIL_dll);
HRESULT compileFromFile(LPCWSTR FilePath, ShaderCompilationDesc* desc, IDxcBlob** ppResult);
HRESULT compileFromString(std::string code, ShaderCompilationDesc* desc, IDxcBlob** ppResult);
HRESULT validate(IDxcBlob* input, IDxcOperationResult** ppResult, UINT32 flags = DxcValidatorFlags_InPlaceEdit);
void disassemble(const void* pBuffer, size_t size);
IDxcContainerReflection* dgbReflection();
};
}

@ -0,0 +1,664 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "stdafx.h"
#include "UI.h"
#include "FSR2Sample.h"
#include "imgui.h"
#include "base/FrameworkWindows.h"
// To use the 'disabled UI state' functionality (ImGuiItemFlags_Disabled), include internal header
// https://github.com/ocornut/imgui/issues/211#issuecomment-339241929
#include "imgui_internal.h"
static void DisableUIStateBegin(const bool& bEnable)
{
if (!bEnable)
{
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
}
};
static void DisableUIStateEnd(const bool& bEnable)
{
if (!bEnable)
{
ImGui::PopItemFlag();
ImGui::PopStyleVar();
}
};
// Some constants and utility functions
static constexpr float MAGNIFICATION_AMOUNT_MIN = 1.0f;
static constexpr float MAGNIFICATION_AMOUNT_MAX = 32.0f;
static constexpr float MAGNIFIER_RADIUS_MIN = 0.01f;
static constexpr float MAGNIFIER_RADIUS_MAX = 0.85f;
static constexpr float MAGNIFIER_BORDER_COLOR__LOCKED[3] = { 0.002f, 0.72f, 0.0f }; // G
static constexpr float MAGNIFIER_BORDER_COLOR__FREE [3] = { 0.72f, 0.002f, 0.0f }; // R
template<class T> static T clamped(const T& v, const T& min, const T& max)
{
if (v < min) return min;
else if (v > max) return max;
else return v;
}
void FSR2Sample::BuildUI()
{
// save current values in case anything changes in the UI
m_SavedUiValues[m_UIState.m_nUpscaleType].scaleMode = m_nUpscaleMode;
m_SavedUiValues[m_UIState.m_nUpscaleType].upscaleRatio = m_fUpscaleRatio;
m_SavedUiValues[m_UIState.m_nUpscaleType].mipBias = m_UIState.mipBias;
m_SavedUiValues[m_UIState.m_nUpscaleType].useSharpen = m_UIState.bUseRcas;
m_SavedUiValues[m_UIState.m_nUpscaleType].sharpness = m_UIState.sharpening;
m_SavedUiValues[m_UIState.m_nUpscaleType].useTAA = m_UIState.bUseTAA;
// if we haven't initialized GLTFLoader yet, don't draw UI.
if (m_pGltfLoader == nullptr)
{
LoadScene(m_activeScene);
return;
}
ImGuiIO& io = ImGui::GetIO();
ImGuiStyle& style = ImGui::GetStyle();
style.FrameBorderSize = 1.0f;
const uint32_t W = this->GetWidth();
const uint32_t H = this->GetHeight();
const uint32_t PROFILER_WINDOW_PADDIG_X = 10;
const uint32_t PROFILER_WINDOW_PADDIG_Y = 10;
const uint32_t PROFILER_WINDOW_SIZE_X = 330;
const uint32_t PROFILER_WINDOW_SIZE_Y = 450;
const uint32_t PROFILER_WINDOW_POS_X = W - PROFILER_WINDOW_PADDIG_X - PROFILER_WINDOW_SIZE_X;
const uint32_t PROFILER_WINDOW_POS_Y = PROFILER_WINDOW_PADDIG_Y;
const uint32_t CONTROLS_WINDOW_POS_X = 10;
const uint32_t CONTROLS_WINDOW_POS_Y = 10;
const uint32_t CONTROLW_WINDOW_SIZE_X = 350;
const uint32_t CONTROLW_WINDOW_SIZE_Y = 780; // assuming > 720p
// Render CONTROLS window
//
ImGui::SetNextWindowPos(ImVec2(CONTROLS_WINDOW_POS_X, CONTROLS_WINDOW_POS_Y), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(ImVec2(CONTROLW_WINDOW_SIZE_X, CONTROLW_WINDOW_SIZE_Y), ImGuiCond_FirstUseEver);
if (m_UIState.bShowControlsWindow)
{
ImGui::Begin("CONTROLS (F11)", &m_UIState.bShowControlsWindow);
if (ImGui::CollapsingHeader("Animation", ImGuiTreeNodeFlags_DefaultOpen))
{
ImGui::Checkbox("Play", &m_bPlay);
ImGui::SliderFloat("Time", &m_time, 0, 30);
}
ImGui::Spacing();
ImGui::Spacing();
if (ImGui::CollapsingHeader("Scene", ImGuiTreeNodeFlags_DefaultOpen))
{
char* cameraControl[] = { "Orbit", "WASD", "cam #0", "cam #1", "cam #2", "cam #3" , "cam #4", "cam #5" };
if (m_activeCamera >= m_pGltfLoader->m_cameras.size() + 2)
m_activeCamera = 0;
ImGui::Combo("Camera", &m_activeCamera, cameraControl, min((int)(m_pGltfLoader->m_cameras.size() + 2), _countof(cameraControl)));
ImGui::Checkbox("Camera Headbobbing", &m_UIState.m_bHeadBobbing);
auto getterLambda = [](void* data, int idx, const char** out_str)->bool { *out_str = ((std::vector<std::string> *)data)->at(idx).c_str(); return true; };
if (ImGui::Combo("Model", &m_activeScene, getterLambda, &m_sceneNames, (int)m_sceneNames.size()))
{
// Note:
// probably queueing this as an event and handling it at the end/beginning
// of frame is a better idea rather than in the middle of drawing UI.
LoadScene(m_activeScene);
//bail out as we need to reload everything
ImGui::End();
ImGui::EndFrame();
ImGui::NewFrame();
return;
}
ImGui::SliderFloat("IBL Factor", &m_UIState.IBLFactor, 0.0f, 3.0f);
for (int i = 0; i < m_pGltfLoader->m_lights.size(); i++)
{
ImGui::SliderFloat(format("Light %i Intensity", i).c_str(), &m_pGltfLoader->m_lights[i].m_intensity, 0.0f, 50.0f);
}
if (ImGui::Button("Set Spot Light 0 to Camera's View"))
{
int idx = m_pGltfLoader->m_lightInstances[0].m_nodeIndex;
m_pGltfLoader->m_nodes[idx].m_transform.LookAt(m_UIState.camera.GetPosition(), m_UIState.camera.GetPosition() - m_UIState.camera.GetDirection());
m_pGltfLoader->m_animatedMats[idx] = m_pGltfLoader->m_nodes[idx].m_transform.GetWorldMat();
}
}
if (m_UIState.m_balloonID >= 0) {
if (ImGui::CollapsingHeader("Balloon Control", ImGuiTreeNodeFlags_DefaultOpen)) {
ImGui::Checkbox("Attach to camera", &m_UIState.m_bBalloonAttachToCamera);
ImGui::SliderFloat("Amplitude", &m_UIState.m_BalloonAmplitude, 0.0f, 1.0f);
ImGui::SliderFloat("Offset X", &m_UIState.balloon_offset_x, -1.0f, 1.0f);
ImGui::SliderFloat("Offset Y", &m_UIState.balloon_offset_y, -1.0f, 1.0f);
ImGui::SliderFloat("Offset Z", &m_UIState.balloon_offset_z, -1.0f, 1.0f);
ImGui::SliderFloat("Scale", &m_UIState.balloon_offset_scale, 0.0f, 0.1f);
}
}
ImGui::Spacing();
ImGui::Spacing();
if (ImGui::CollapsingHeader("Upscaling ", ImGuiTreeNodeFlags_DefaultOpen)) //blank space needed, otherwise the identically named 'combo' drop down below won't work
{
const char* modes[] = { "Point [Ctrl-1]", "Bilinear [Ctrl-2]", "Bicubic [Ctrl-3]", "FSR 1.0 [Ctrl-4]", "FSR 2.0 [Ctrl-5]", "Native [Ctrl-0]" };
if (ImGui::Combo("Upscaling", (int32_t*)&m_UIState.m_nUpscaleType, modes, _countof(modes)))
{
if (m_UIState.m_nUpscaleType == UPSCALE_TYPE_NATIVE)
{
// force reset values for native
m_SavedUiValues[m_UIState.m_nUpscaleType].scaleMode = UPSCALE_QUALITY_MODE_NONE;
m_SavedUiValues[m_UIState.m_nUpscaleType].upscaleRatio = 1.f;
m_SavedUiValues[m_UIState.m_nUpscaleType].mipBias = 0.f;
}
// update current values based on the last stored defaults
m_nUpscaleMode = m_SavedUiValues[m_UIState.m_nUpscaleType].scaleMode;
m_fUpscaleRatio = m_SavedUiValues[m_UIState.m_nUpscaleType].upscaleRatio;
m_UIState.mipBias = m_SavedUiValues[m_UIState.m_nUpscaleType].mipBias;
m_UIState.bUseRcas = m_SavedUiValues[m_UIState.m_nUpscaleType].useSharpen;
m_UIState.sharpening = m_SavedUiValues[m_UIState.m_nUpscaleType].sharpness;
m_UIState.bUseTAA = m_SavedUiValues[m_UIState.m_nUpscaleType].useTAA;
RefreshRenderResolution();
OnResize(true);
}
if (m_UIState.m_nUpscaleType == UPSCALE_TYPE_FSR_2_0)
{
// adjust to match the combo box options
int32_t upscaleQualityMode = m_nUpscaleMode - UPSCALE_QUALITY_MODE_QUALITY;
const char* ratios[] = { "Quality (1.5x)", "Balanced (1.7x)", "Performance (2x)", "Ultra Performance (3x)", "Custom" };
if (ImGui::Combo("Scale mode", &upscaleQualityMode, ratios, _countof(ratios)))
{
m_nUpscaleMode = (UpscaleQualityMode)(upscaleQualityMode + UPSCALE_QUALITY_MODE_QUALITY); // above combo box starts from quality mode.
m_UIState.mipBias = mipBias[m_nUpscaleMode];
RefreshRenderResolution();
OnResize();
}
else
{
m_nUpscaleMode = (UpscaleQualityMode)(upscaleQualityMode + UPSCALE_QUALITY_MODE_QUALITY); // above combo box starts from quality mode.
ImGui::SliderFloat("Mip LOD bias", &m_UIState.mipBias, -5.0f, 0.0f);
}
if ((m_nUpscaleMode == UPSCALE_QUALITY_MODE_CUSTOM) && ImGui::SliderFloat("Custom factor", &m_fUpscaleRatio, 1.0f, 3.0f))
{
RefreshRenderResolution();
OnResize();
}
if (m_UIState.m_nUpscaleType == UPSCALE_TYPE_FSR_2_0)
{
if (ImGui::Checkbox("Dynamic resolution", &m_UIState.bDynamicRes)) {
OnResize();
}
}
else
m_UIState.bDynamicRes = false;
}
else if (m_UIState.m_nUpscaleType <= UPSCALE_TYPE_FSR_1_0)
{
const char* ratios[] = { "Native", "Ultra Quality (1.3x)", "Quality (1.5x)", "Balanced (1.7x)", "Performance (2x)", "Ultra Performance (3x)", "Custom" };
if (ImGui::Combo("Scale mode", (int32_t*)&m_nUpscaleMode, ratios, _countof(ratios)))
{
m_UIState.mipBias = mipBias[m_nUpscaleMode];
RefreshRenderResolution();
OnResize();
}
else
{
ImGui::SliderFloat("Mip LOD bias", &m_UIState.mipBias, -5.0f, 0.0f);
}
if ((m_nUpscaleMode == UPSCALE_QUALITY_MODE_CUSTOM) && ImGui::SliderFloat("Custom factor", &m_fUpscaleRatio, 1.0f, 3.0f))
{
RefreshRenderResolution();
OnResize();
}
m_UIState.bDynamicRes = false;
}
else
{
m_UIState.mipBias = mipBias[UPSCALE_QUALITY_MODE_NONE];
}
ImGui::Checkbox("RCAS Sharpening", &m_UIState.bUseRcas);
if (m_UIState.m_nUpscaleType == UPSCALE_TYPE_FSR_2_0) {
if (m_UIState.bUseRcas) {
ImGui::SliderFloat("Sharpening", &m_UIState.sharpening, 0.0f, 1.0f);
}
} else {
if (m_UIState.bUseRcas) {
ImGui::SliderFloat("Sharpening attentuation", &m_UIState.sharpening, 0.0f, 10.0f);
}
}
if (m_UIState.m_nUpscaleType != UPSCALE_TYPE_FSR_2_0)
ImGui::Checkbox("TAA", &m_UIState.bUseTAA);
ImGui::Text("Render resolution: %dx%d", m_UIState.renderWidth, m_UIState.renderHeight);
ImGui::Text("Display resolution: %dx%d", m_UIState.displayWidth, m_UIState.displayHeight);
if (m_bDisplayHalf)
{
ImGui::Spacing();
ImGui::Spacing();
ImGui::Text(UseSlowFallback(m_device.IsFp16Supported()) ? "Slow fallback" : "Fast path");
}
}
ImGui::Spacing();
ImGui::Spacing();
if (ImGui::CollapsingHeader("PostProcessing", ImGuiTreeNodeFlags_DefaultOpen))
{
const char* tonemappers[] = { "AMD Tonemapper", "DX11DSK", "Reinhard", "Uncharted2Tonemap", "ACES", "No tonemapper" };
ImGui::Combo("Tonemapper", &m_UIState.SelectedTonemapperIndex, tonemappers, _countof(tonemappers));
bool isHdr = m_displayModesAvailable[m_currentDisplayModeNamesIndex] != DISPLAYMODE_SDR;
ImGui::SliderFloat("Exposure", isHdr ? &m_UIState.ExposureHdr : &m_UIState.Exposure, 0.0f, 4.0f);
}
ImGui::Spacing();
ImGui::Spacing();
if (ImGui::CollapsingHeader("Magnifier", ImGuiTreeNodeFlags_DefaultOpen))
{
// read in Magnifier pass parameters from the UI & app state
MagnifierPS::PassParameters& params = m_UIState.MagnifierParams;
params.uImageHeight = m_Height;
params.uImageWidth = m_Width;
params.iMousePos[0] = m_UIState.bLockMagnifierPosition ? m_UIState.LockedMagnifiedScreenPositionX : static_cast<int>(io.MousePos.x);
params.iMousePos[1] = m_UIState.bLockMagnifierPosition ? m_UIState.LockedMagnifiedScreenPositionY : static_cast<int>(io.MousePos.y);
ImGui::Checkbox("Show Magnifier (M)", &m_UIState.bUseMagnifier);
DisableUIStateBegin(m_UIState.bUseMagnifier);
{
// Use a local bool state here to track locked state through the UI widget,
// and then call ToggleMagnifierLockedState() to update the persistent state (m_UIstate).
// The keyboard input for toggling lock directly operates on the persistent state.
const bool bIsMagnifierCurrentlyLocked = m_UIState.bLockMagnifierPosition;
bool bMagnifierToggle = bIsMagnifierCurrentlyLocked;
ImGui::Checkbox("Lock Position (L)", &bMagnifierToggle);
if (bMagnifierToggle != bIsMagnifierCurrentlyLocked)
m_UIState.ToggleMagnifierLock();
ImGui::SliderFloat("Screen Size", &params.fMagnifierScreenRadius, MAGNIFIER_RADIUS_MIN, MAGNIFIER_RADIUS_MAX);
ImGui::SliderFloat("Magnification", &params.fMagnificationAmount, MAGNIFICATION_AMOUNT_MIN, MAGNIFICATION_AMOUNT_MAX);
ImGui::SliderInt("OffsetX", &params.iMagnifierOffset[0], -m_Width, m_Width);
ImGui::SliderInt("OffsetY", &params.iMagnifierOffset[1], -m_Height, m_Height);
}
DisableUIStateEnd(m_UIState.bUseMagnifier);
}
ImGui::Spacing();
ImGui::Spacing();
if (ImGui::CollapsingHeader("Debug", ImGuiTreeNodeFlags_DefaultOpen))
{
ImGui::Checkbox("Show Bounding Boxes", &m_UIState.bDrawBoundingBoxes);
ImGui::Checkbox("Show Light Frustum", &m_UIState.bDrawLightFrustum);
ImGui::Text("Wireframe");
ImGui::SameLine(); ImGui::RadioButton("Off", (int*)&m_UIState.WireframeMode, (int)UIState::WireframeMode::WIREFRAME_MODE_OFF);
ImGui::SameLine(); ImGui::RadioButton("Shaded", (int*)&m_UIState.WireframeMode, (int)UIState::WireframeMode::WIREFRAME_MODE_SHADED);
ImGui::SameLine(); ImGui::RadioButton("Solid color", (int*)&m_UIState.WireframeMode, (int)UIState::WireframeMode::WIREFRAME_MODE_SOLID_COLOR);
if (m_UIState.WireframeMode == UIState::WireframeMode::WIREFRAME_MODE_SOLID_COLOR)
ImGui::ColorEdit3("Wire solid color", m_UIState.WireframeColor, ImGuiColorEditFlags_NoAlpha);
}
ImGui::Spacing();
ImGui::Spacing();
if (ImGui::CollapsingHeader("Presentation Mode", ImGuiTreeNodeFlags_DefaultOpen))
{
const char* fullscreenModes[] = { "Windowed", "BorderlessFullscreen", "ExclusiveFulscreen" };
if (ImGui::Combo("Fullscreen Mode", (int*)&m_fullscreenMode, fullscreenModes, _countof(fullscreenModes)))
{
if (m_previousFullscreenMode != m_fullscreenMode)
{
HandleFullScreen();
m_previousFullscreenMode = m_fullscreenMode;
}
}
ImGui::Checkbox("Framerate Limiter", &m_UIState.bEnableFrameRateLimiter);
if (m_UIState.bEnableFrameRateLimiter)
{
ImGui::Checkbox("Use GPU Framerate Limiter", &m_UIState.bUseGPUFrameRateLimiter);
DWORD maxFramerate = 240;
DEVMODE lpDevMode = {};
lpDevMode.dmSize = sizeof(DEVMODE);
static int sTargetFPS = -1;
if (sTargetFPS < 0)
{
if (EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &lpDevMode))
{
sTargetFPS = lpDevMode.dmDisplayFrequency;
}
else
{
sTargetFPS = maxFramerate;
}
}
ImGui::SliderInt("Target FPS", &sTargetFPS, 5, maxFramerate);
m_UIState.targetFrameTime = 1000.f * (1.f / sTargetFPS);
}
}
ImGui::Spacing();
ImGui::Spacing();
if (m_FreesyncHDROptionEnabled && ImGui::CollapsingHeader("FreeSync HDR", ImGuiTreeNodeFlags_DefaultOpen))
{
static bool openWarning = false;
const char** displayModeNames = &m_displayModesNamesAvailable[0];
if (ImGui::Combo("Display Mode", (int*)&m_currentDisplayModeNamesIndex, displayModeNames, (int)m_displayModesNamesAvailable.size()))
{
if (m_fullscreenMode != PRESENTATIONMODE_WINDOWED)
{
UpdateDisplay(m_disableLocalDimming);
m_previousDisplayModeNamesIndex = m_currentDisplayModeNamesIndex;
}
else if (CheckIfWindowModeHdrOn() &&
(m_displayModesAvailable[m_currentDisplayModeNamesIndex] == DISPLAYMODE_SDR ||
m_displayModesAvailable[m_currentDisplayModeNamesIndex] == DISPLAYMODE_HDR10_2084 ||
m_displayModesAvailable[m_currentDisplayModeNamesIndex] == DISPLAYMODE_HDR10_SCRGB))
{
UpdateDisplay(m_disableLocalDimming);
m_previousDisplayModeNamesIndex = m_currentDisplayModeNamesIndex;
}
else
{
openWarning = true;
m_currentDisplayModeNamesIndex = m_previousDisplayModeNamesIndex;
}
OnResize(true);
}
if (openWarning)
{
ImGui::OpenPopup("Display Modes Warning");
ImGui::BeginPopupModal("Display Modes Warning", NULL, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::Text("\nChanging display modes is only available either using HDR toggle in windows display setting for HDR10 modes or in fullscreen for FS HDR modes\n\n");
if (ImGui::Button("Cancel", ImVec2(120, 0))) { openWarning = false; ImGui::CloseCurrentPopup(); }
ImGui::EndPopup();
}
if (m_displayModesAvailable[m_currentDisplayModeNamesIndex] == DisplayMode::DISPLAYMODE_FSHDR_Gamma22 ||
m_displayModesAvailable[m_currentDisplayModeNamesIndex] == DisplayMode::DISPLAYMODE_FSHDR_SCRGB)
{
static bool selectedDisableLocaldimmingSetting = false;
if (ImGui::Checkbox("Disable Local Dimming", &selectedDisableLocaldimmingSetting))
UpdateDisplay(selectedDisableLocaldimmingSetting);
}
}
ImGui::End(); // CONTROLS
}
// Render PROFILER window
//
if (m_UIState.bShowProfilerWindow)
{
constexpr size_t NUM_FRAMES = 128;
static float FRAME_TIME_ARRAY[NUM_FRAMES] = { 0 };
// track highest frame rate and determine the max value of the graph based on the measured highest value
static float RECENT_HIGHEST_FRAME_TIME = 0.0f;
constexpr int FRAME_TIME_GRAPH_MAX_FPS[] = { 800, 240, 120, 90, 65, 45, 30, 15, 10, 5, 4, 3, 2, 1 };
static float FRAME_TIME_GRAPH_MAX_VALUES[_countof(FRAME_TIME_GRAPH_MAX_FPS)] = { 0 }; // us
for (int i = 0; i < _countof(FRAME_TIME_GRAPH_MAX_FPS); ++i) { FRAME_TIME_GRAPH_MAX_VALUES[i] = 1000000.f / FRAME_TIME_GRAPH_MAX_FPS[i]; }
//scrolling data and average FPS computing
const std::vector<TimeStamp>& timeStamps = m_pRenderer->GetTimingValues();
const bool bTimeStampsAvailable = timeStamps.size() > 0;
if (bTimeStampsAvailable)
{
RECENT_HIGHEST_FRAME_TIME = 0;
FRAME_TIME_ARRAY[NUM_FRAMES - 1] = timeStamps.back().m_microseconds;
for (uint32_t i = 0; i < NUM_FRAMES - 1; i++)
{
FRAME_TIME_ARRAY[i] = FRAME_TIME_ARRAY[i + 1];
}
RECENT_HIGHEST_FRAME_TIME = max(RECENT_HIGHEST_FRAME_TIME, FRAME_TIME_ARRAY[NUM_FRAMES - 1]);
}
const float& frameTime_us = FRAME_TIME_ARRAY[NUM_FRAMES - 1];
const float frameTime_ms = frameTime_us * 0.001f;
const int fps = bTimeStampsAvailable ? static_cast<int>(1000000.0f / frameTime_us) : 0;
// UI
ImGui::SetNextWindowPos(ImVec2((float)PROFILER_WINDOW_POS_X, (float)PROFILER_WINDOW_POS_Y), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(ImVec2(PROFILER_WINDOW_SIZE_X, PROFILER_WINDOW_SIZE_Y), ImGuiCond_FirstUseEver);
ImGui::Begin("PROFILER (F12))", &m_UIState.bShowProfilerWindow);
ImGui::Text("Resolution : %ix%i", m_Width, m_Height);
ImGui::Text("API : %s", m_systemInfo.mGfxAPI.c_str());
ImGui::Text("GPU : %s", m_systemInfo.mGPUName.c_str());
ImGui::Text("CPU : %s", m_systemInfo.mCPUName.c_str());
ImGui::Text("FPS : %d (%.2f ms)", fps, frameTime_ms);
if (ImGui::CollapsingHeader("GPU Timings", ImGuiTreeNodeFlags_DefaultOpen))
{
std::string msOrUsButtonText = m_UIState.bShowMilliseconds ? "Switch to microseconds" : "Switch to milliseconds";
if (ImGui::Button(msOrUsButtonText.c_str())) {
m_UIState.bShowMilliseconds = !m_UIState.bShowMilliseconds;
}
ImGui::Checkbox("Show Avg/Min/Max", &m_UIState.bShowAverage);
ImGui::Spacing();
if (m_isCpuValidationLayerEnabled || m_isGpuValidationLayerEnabled)
{
ImGui::TextColored(ImVec4(1,1,0,1), "WARNING: Validation layer is switched on");
ImGui::Text("Performance numbers may be inaccurate!");
}
// find the index of the FrameTimeGraphMaxValue as the next higher-than-recent-highest-frame-time in the pre-determined value list
size_t iFrameTimeGraphMaxValue = 0;
for (int i = 0; i < _countof(FRAME_TIME_GRAPH_MAX_VALUES); ++i)
{
if (RECENT_HIGHEST_FRAME_TIME < FRAME_TIME_GRAPH_MAX_VALUES[i]) // FRAME_TIME_GRAPH_MAX_VALUES are in increasing order
{
iFrameTimeGraphMaxValue = min(_countof(FRAME_TIME_GRAPH_MAX_VALUES) - 1, i + 1);
break;
}
}
ImGui::PlotLines("", FRAME_TIME_ARRAY, NUM_FRAMES, 0, "GPU frame time (us)", 0.0f, FRAME_TIME_GRAPH_MAX_VALUES[iFrameTimeGraphMaxValue], ImVec2(0, 80));
constexpr uint32_t AvgTimeStampUpdateIntervalMs = 1000;
const double currTime = MillisecondsNow();
bool bResetAccumulatingState = false;
if ((m_UIState.accumulatingFrameCount > 1) &&
((currTime - m_UIState.lastTimeStampsResetTime) > AvgTimeStampUpdateIntervalMs))
{
std::swap(m_UIState.displayedTimeStamps, m_UIState.accumulatingTimeStamps);
for (uint32_t i = 0; i < m_UIState.displayedTimeStamps.size(); i++)
{
m_UIState.displayedTimeStamps[i].sum /= m_UIState.accumulatingFrameCount;
}
bResetAccumulatingState = true;
}
bResetAccumulatingState |= (m_UIState.accumulatingTimeStamps.size() != timeStamps.size());
if (bResetAccumulatingState)
{
m_UIState.accumulatingTimeStamps.resize(0);
m_UIState.accumulatingTimeStamps.resize(timeStamps.size());
m_UIState.lastTimeStampsResetTime = currTime;
m_UIState.accumulatingFrameCount = 0;
}
const float unitScaling = m_UIState.bShowMilliseconds ? (1.0f / 1000.0f) : 1.0f;
for (uint32_t i = 0; i < timeStamps.size(); i++)
{
float value = timeStamps[i].m_microseconds * unitScaling;
const char* pStrUnit = m_UIState.bShowMilliseconds ? "ms" : "us";
ImGui::Text("%-18s: %7.2f %s", timeStamps[i].m_label.c_str(), value, pStrUnit);
if (m_UIState.bShowAverage)
{
if (m_UIState.displayedTimeStamps.size() == timeStamps.size())
{
ImGui::SameLine();
ImGui::Text(" avg: %7.2f %s", m_UIState.displayedTimeStamps[i].sum * unitScaling, pStrUnit);
ImGui::SameLine();
ImGui::Text(" min: %7.2f %s", m_UIState.displayedTimeStamps[i].minimum * unitScaling, pStrUnit);
ImGui::SameLine();
ImGui::Text(" max: %7.2f %s", m_UIState.displayedTimeStamps[i].maximum * unitScaling, pStrUnit);
}
UIState::AccumulatedTimeStamp* pAccumulatingTimeStamp = &m_UIState.accumulatingTimeStamps[i];
pAccumulatingTimeStamp->sum += timeStamps[i].m_microseconds;
pAccumulatingTimeStamp->minimum = min(pAccumulatingTimeStamp->minimum, timeStamps[i].m_microseconds);
pAccumulatingTimeStamp->maximum = max(pAccumulatingTimeStamp->maximum, timeStamps[i].m_microseconds);
}
}
m_UIState.accumulatingFrameCount++;
}
ImGui::End(); // PROFILER
}
}
void UIState::Initialize()
{
// init magnifier params
for (int ch = 0; ch < 3; ++ch) this->MagnifierParams.fBorderColorRGB[ch] = MAGNIFIER_BORDER_COLOR__FREE[ch]; // start at 'free' state
// init GUI state
this->SelectedTonemapperIndex = 0;
this->bUseMagnifier = false;
this->bLockMagnifierPosition = this->bLockMagnifierPositionHistory = false;
this->SelectedSkydomeTypeIndex = 0;
this->Exposure = 1.0f;
this->IBLFactor = 2.0f;
this->bDrawLightFrustum = false;
this->bDrawBoundingBoxes = false;
this->WireframeMode = WireframeMode::WIREFRAME_MODE_OFF;
this->WireframeColor[0] = 0.0f;
this->WireframeColor[1] = 1.0f;
this->WireframeColor[2] = 0.0f;
this->bShowControlsWindow = true;
this->bShowProfilerWindow = true;
this->bShowMilliseconds = false;
this->bShowAverage = true;
this->lastTimeStampsResetTime = 0;
this->accumulatingFrameCount = 0;
}
//
// Magnifier UI Controls
//
void UIState::ToggleMagnifierLock()
{
if (this->bUseMagnifier)
{
this->bLockMagnifierPositionHistory = this->bLockMagnifierPosition; // record histroy
this->bLockMagnifierPosition = !this->bLockMagnifierPosition; // flip state
const bool bLockSwitchedOn = !this->bLockMagnifierPositionHistory && this->bLockMagnifierPosition;
const bool bLockSwitchedOff = this->bLockMagnifierPositionHistory && !this->bLockMagnifierPosition;
if (bLockSwitchedOn)
{
const ImGuiIO& io = ImGui::GetIO();
this->LockedMagnifiedScreenPositionX = static_cast<int>(io.MousePos.x);
this->LockedMagnifiedScreenPositionY = static_cast<int>(io.MousePos.y);
for (int ch = 0; ch < 3; ++ch) this->MagnifierParams.fBorderColorRGB[ch] = MAGNIFIER_BORDER_COLOR__LOCKED[ch];
}
else if (bLockSwitchedOff)
{
for (int ch = 0; ch < 3; ++ch) this->MagnifierParams.fBorderColorRGB[ch] = MAGNIFIER_BORDER_COLOR__FREE[ch];
}
}
}
// These are currently not bound to any mouse input and are here for convenience/reference.
// Mouse scroll is currently wired up to camera for panning and moving in the local Z direction.
// Any application that would prefer otherwise can utilize these for easily controlling the magnifier parameters through the desired input.
void UIState::AdjustMagnifierSize (float increment /*= 0.05f*/){ MagnifierParams.fMagnifierScreenRadius = clamped(MagnifierParams.fMagnifierScreenRadius + increment, MAGNIFIER_RADIUS_MIN, MAGNIFIER_RADIUS_MAX); }
void UIState::AdjustMagnifierMagnification(float increment /*= 1.00f*/){ MagnifierParams.fMagnificationAmount = clamped(MagnifierParams.fMagnificationAmount + increment, MAGNIFICATION_AMOUNT_MIN, MAGNIFICATION_AMOUNT_MAX); }
bool UIState::DevOption(bool* pBoolValue, const char* name)
{
return ImGui::Checkbox(name, pBoolValue);
}
bool UIState::DevOption(int* pIntValue, const char* name, int minVal, int maxVal)
{
return ImGui::SliderInt(name, pIntValue, minVal, maxVal);
}
bool UIState::DevOption(int* pEnumValue, const char* name, uint32_t count, const char* const* ppEnumNames)
{
return ImGui::Combo(name, pEnumValue, ppEnumNames, count);
}
bool UIState::DevOption(int* pActiveIndex, uint32_t count, const char* const* ppEnumNames)
{
bool pressed = false;
ImGui::BeginGroup();
for (uint32_t i = 0; i < count; i++)
{
pressed |= ImGui::RadioButton(ppEnumNames[i], pActiveIndex, int(i));
}
ImGui::EndGroup();
return pressed;
}
bool UIState::DevOption(float* pFloatValue, const char* name, float fMin, float fMax)
{
return ImGui::SliderFloat(name, pFloatValue, fMin, fMax);
}
void UIState::Text(const char* text)
{
ImGui::Text(text);
}

@ -0,0 +1,219 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#pragma once
#include "PostProc/MagnifierPS.h"
#include <string>
enum TAA_MODE
{
RM_DISABLED,
RM_FFX_TAA,
};
typedef enum UpscaleType {
UPSCALE_TYPE_POINT = 0,
UPSCALE_TYPE_BILINEAR = 1,
UPSCALE_TYPE_BICUBIC = 2,
UPSCALE_TYPE_FSR_1_0 = 3,
UPSCALE_TYPE_FSR_2_0 = 4,
UPSCALE_TYPE_NATIVE = 5,
// add above this.
UPSCALE_TYPE_COUNT
} UpscaleType;
typedef enum UpscaleQualityMode {
UPSCALE_QUALITY_MODE_NONE = 0,
UPSCALE_QUALITY_MODE_ULTRA_QUALITY = 1,
UPSCALE_QUALITY_MODE_QUALITY = 2,
UPSCALE_QUALITY_MODE_BALANCE = 3,
UPSCALE_QUALITY_MODE_PERFORMANCE = 4,
UPSCALE_QUALITY_MODE_ULTRA_PERFORMANCE = 5,
UPSCALE_QUALITY_MODE_CUSTOM = 6,
// add above this.
UPSCALE_QUALITY_MODE_COUNT
} UpscaleQualityMode;
struct UIState
{
Camera camera;
bool m_bHeadBobbing = false;
//
// WINDOW MANAGEMENT
//
bool bShowControlsWindow;
bool bShowProfilerWindow;
//
// POST PROCESS CONTROLS
//
int SelectedTonemapperIndex;
float Exposure;
float ExposureHdr = 1.f;
bool bReset = false;
bool bUseMagnifier;
bool bLockMagnifierPosition;
bool bLockMagnifierPositionHistory;
int LockedMagnifiedScreenPositionX;
int LockedMagnifiedScreenPositionY;
CAULDRON_DX12::MagnifierPS::PassParameters MagnifierParams;
const std::string* m_pScreenShotName = NULL;
// FSR
uint32_t displayWidth = 0;
uint32_t displayHeight = 0;
uint32_t renderWidth = 0;
uint32_t renderHeight = 0;
bool bVsyncOn = true;
// NOTE: These should always match the values in m_SavedUiValues for m_nUpscaleType.
UpscaleType m_nUpscaleType = UPSCALE_TYPE_FSR_2_0;
float mipBias = -1.58f;
bool bUseRcas = false;
bool bUseTAA = true;
float sharpening = 0.8f;
// Dynamic resolution
bool bDynamicRes = false;
// TAA
TAA_MODE taaMode = RM_FFX_TAA;
unsigned int closestVelocitySamplePattern = 0; // 5 samples
float Feedback = 15.f / 16.f;
// FSR2 auto reactive
bool bUseFsr2AutoReactive = false;
float fFsr2AutoReactiveScale = 1.f;
float fFsr2AutoReactiveThreshold = 0.01f;
bool bFsr2AutoReactiveTonemap = true;
bool bFsr2AutoReactiveInverseTonemap = false;
bool bFsr2AutoReactiveThreshold = true;
bool bFsr2AutoReactiveUseMax = true;
// FSR2 debug out
bool bUseDebugOut = false;
int nDebugBlitSurface = 6; // FFX_FSR2_RESOURCE_IDENTIFIER_INTERNAL_UPSCALED_COLOR
int nDebugOutMappingR = 0;
int nDebugOutMappingG = 1;
int nDebugOutMappingB = 2;
float v2DebugOutMappingR[2] = { 0.f, 1.f };
float v2DebugOutMappingG[2] = { 0.f, 1.f };
float v2DebugOutMappingB[2] = { 0.f, 1.f };
float v4DebugSliderValues[8] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
//
// APP/SCENE CONTROLS
//
float IBLFactor;
double deltaTime;
bool bEnableFrameRateLimiter = false;
bool bUseGPUFrameRateLimiter = false;
double targetFrameTime;
int SelectedSkydomeTypeIndex;
bool bDrawBoundingBoxes;
bool bDrawLightFrustum;
enum class WireframeMode : int
{
WIREFRAME_MODE_OFF = 0,
WIREFRAME_MODE_SHADED = 1,
WIREFRAME_MODE_SOLID_COLOR = 2,
};
WireframeMode WireframeMode;
float WireframeColor[3];
//
// PROFILER CONTROLS
//
bool bShowMilliseconds;
bool bShowAverage;
struct AccumulatedTimeStamp
{
float sum;
float minimum;
float maximum;
AccumulatedTimeStamp()
: sum(0.0f), minimum(FLT_MAX), maximum(0)
{
}
};
std::vector<AccumulatedTimeStamp> displayedTimeStamps;
std::vector<AccumulatedTimeStamp> accumulatingTimeStamps;
double lastTimeStampsResetTime;
uint32_t accumulatingFrameCount;
int32_t m_balloonID = -1;
bool m_balloonInit = false;
float m_BalloonAmplitude = 0.05f;
math::Matrix4 m_InitBaloonTransform = {};
math::Matrix4 m_CurBaloonTransform = {};
bool m_bBalloonAttachToCamera = true;
float balloon_offset_x = 0.05f;
float balloon_offset_y = -0.08f;
float balloon_offset_z = -0.17f;
float balloon_offset_scale = 0.05f;
Vectormath::Vector3 baloon_pos = { 0.0f, 0.0f, 0.0f };
Vectormath::Vector3 baloon_tip_pos = { 0.0f, 0.0f, 0.0f };
// -----------------------------------------------
void Initialize();
void ToggleMagnifierLock();
void AdjustMagnifierSize(float increment = 0.05f);
void AdjustMagnifierMagnification(float increment = 1.00f);
bool DevOption(bool* pBoolValue, const char* name);
bool DevOption(int* pIntValue, const char* name, int minVal, int maxVal);
bool DevOption(int* pEnumValue, const char* name, uint32_t count, const char* const* ppEnumNames);
bool DevOption(int* pActiveIndex, uint32_t count, const char* const* ppEnumNames);
bool DevOption(float* pFloatValue, const char* name, float fMin = 0.0f, float fMax = 1.0f);
void Text(const char* text);
template<typename T>
bool DevOption(T& flags, uint32_t bitIdx, const char* name)
{
static_assert(sizeof(T) <= sizeof(uint64_t));
bool bSet = uint64_t(flags) & (1ULL << bitIdx);
bool bResult = DevOption(&bSet, name);
if (bSet)
{
flags = T(uint64_t(flags) | (1ULL << bitIdx));
}
else
{
flags = T(uint64_t(flags) & ~(1ULL << bitIdx));
}
return bResult;
}
};

@ -0,0 +1,156 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "stdafx.h"
#include "UpscaleContext.h"
#include "UpscaleContext_Spatial.h"
#include "UpscaleContext_FSR2_API.h"
#include "../ffx-fsr2-api/ffx_fsr2.h"
// factory
UpscaleContext* UpscaleContext::CreateUpscaleContext(const FfxUpscaleInitParams& initParams)
{
UpscaleContext* pContext = NULL;
switch (initParams.nType)
{
default:
case UPSCALE_TYPE_NATIVE:
case UPSCALE_TYPE_POINT:
case UPSCALE_TYPE_BILINEAR:
case UPSCALE_TYPE_BICUBIC:
case UPSCALE_TYPE_FSR_1_0:
pContext = new UpscaleContext_Spatial(initParams.nType, "Spatial");
break;
case UPSCALE_TYPE_FSR_2_0:
pContext = new UpscaleContext_FSR2_API(initParams.nType, "FSR2 API");
break;
// Additional options:
// MSAA, MSAA resolve to higher resolution, then add TAA?
// Checkerboard?
}
pContext->OnCreate(initParams);
return pContext;
}
UpscaleContext::UpscaleContext(std::string name)
: m_Name(name)
{
}
//--------------------------------------------------------------------------------------
//
// OnCreate
//
//--------------------------------------------------------------------------------------
void UpscaleContext::OnCreate(const FfxUpscaleInitParams& initParams)
{
m_bInvertedDepth = initParams.bInvertedDepth;
m_pDevice = initParams.pDevice;
m_OutputFormat = initParams.outFormat;
m_Type = initParams.nType;
m_MaxQueuedFrames = initParams.maxQueuedFrames;
const uint32_t cbvDescriptorCount = 4000;
const uint32_t srvDescriptorCount = 8000;
const uint32_t uavDescriptorCount = 100;
const uint32_t dsvDescriptorCount = 100;
const uint32_t rtvDescriptorCount = 100;
const uint32_t samplerDescriptorCount = 20;
m_ResourceViewHeaps.OnCreate(m_pDevice, cbvDescriptorCount, srvDescriptorCount, uavDescriptorCount, dsvDescriptorCount, rtvDescriptorCount, samplerDescriptorCount);
// Create a 'dynamic' constant buffer
const uint32_t constantBuffersMemSize = 2 * 1024 * 1024;
m_ConstantBufferRing.OnCreate(m_pDevice, 2, constantBuffersMemSize, &m_ResourceViewHeaps);
}
//--------------------------------------------------------------------------------------
//
// OnDestroy
//
//--------------------------------------------------------------------------------------
void UpscaleContext::OnDestroy()
{
m_ConstantBufferRing.OnDestroy();
m_ResourceViewHeaps.OnDestroy();
}
//--------------------------------------------------------------------------------------
//
// OnCreateWindowSizeDependentResources
//
//--------------------------------------------------------------------------------------
void UpscaleContext::OnCreateWindowSizeDependentResources(ID3D12Resource* input, ID3D12Resource* output, uint32_t renderWidth, uint32_t renderHeight, uint32_t displayWidth, uint32_t displayHeight, bool hdr)
{
// ToDo: For FSR2 we'll have more than one input (and it may change every frame?)
m_input = input;
m_output = output;
m_renderWidth = renderWidth;
m_renderHeight = renderHeight;
m_displayWidth = displayWidth;
m_displayHeight = displayHeight;
m_index = 0;
m_frameIndex = 0;
m_bInit = true;
m_hdr = hdr;
}
//--------------------------------------------------------------------------------------
//
// OnDestroyWindowSizeDependentResources
//
//--------------------------------------------------------------------------------------
void UpscaleContext::OnDestroyWindowSizeDependentResources()
{
}
void UpscaleContext::PreDraw(UIState* pState)
{
m_bUseTaa = pState->bUseTAA;
m_ConstantBufferRing.OnBeginFrame();
m_frameIndex++;
if (m_bUseTaa)
{
m_index++;
// handle reset and jitter
const int32_t jitterPhaseCount = ffxFsr2GetJitterPhaseCount(pState->renderWidth, pState->displayWidth);
ffxFsr2GetJitterOffset(&m_JitterX, &m_JitterY, m_index, jitterPhaseCount);
pState->camera.SetProjectionJitter(2.0f * m_JitterX / (float)pState->renderWidth, -2.0f * m_JitterY / (float)pState->renderHeight);
}
else
{
pState->camera.SetProjectionJitter(0.f, 0.f);
}
}
void UpscaleContext::GenerateReactiveMask(ID3D12GraphicsCommandList* pCommandList, const FfxUpscaleSetup& cameraSetup, UIState* pState)
{
}

@ -0,0 +1,144 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#pragma once
#include "Base/Texture.h"
#include "Base/StaticBufferPool.h"
#include "Base/GBuffer.h"
#include "PostProc/PostProcCS.h"
#include "UI.h"
using namespace CAULDRON_DX12;
#define GROUP_SIZE 8
class UpscaleContext
{
public:
typedef struct
{
UpscaleType nType;
bool bInvertedDepth;
Device* pDevice;
DXGI_FORMAT outFormat;
UploadHeap* pUploadHeap;
uint32_t maxQueuedFrames;
}FfxUpscaleInitParams;
typedef struct
{
math::Matrix4 mCameraView;
math::Matrix4 mCameraProj;
math::Matrix4 mCameraViewInv;
math::Vector4 vCameraPos;
}FfxCameraSetup;
typedef struct
{
FfxCameraSetup cameraSetup;
ID3D12Resource* opaqueOnlyColorResource; // opaque only input
ID3D12Resource* unresolvedColorResource; // input0
ID3D12Resource* motionvectorResource; // input1
ID3D12Resource* depthbufferResource; // input2
ID3D12Resource* reactiveMapResource; // input3
ID3D12Resource* transparencyAndCompositionResource; // input4
ID3D12Resource* resolvedColorResource; // output
}FfxUpscaleSetup;
struct CBV_SRV_UAV_RING
{
bool Init(ResourceViewHeaps* pHeaps, uint32_t numDescriptorsPerFrame, uint32_t numBufferedFrames)
{
descriptorsPerFrame = numDescriptorsPerFrame;
numFrames = numBufferedFrames;
return pHeaps->AllocCBV_SRV_UAVDescriptor(numDescriptorsPerFrame * numBufferedFrames, &innerDescriptorTable);
}
CBV_SRV_UAV GetFrame(uint32_t frameIndex) const
{
CBV_SRV_UAV result = {};
const uint32_t tableOffset = descriptorsPerFrame * (frameIndex % numFrames);
result.SetResourceView(
innerDescriptorTable.GetSize(),
innerDescriptorTable.GetDescriptorSize(),
innerDescriptorTable.GetCPU(tableOffset),
innerDescriptorTable.GetGPU(tableOffset));
return result;
}
CBV_SRV_UAV innerDescriptorTable;
uint32_t descriptorsPerFrame = 0;
uint32_t numFrames = 1;
};
// factory
static UpscaleContext* CreateUpscaleContext(const FfxUpscaleInitParams& initParams);
UpscaleContext(std::string name);
virtual UpscaleType Type() { return m_Type; };
virtual std::string Name() { return "Upscale"; }
virtual void OnCreate(const FfxUpscaleInitParams& initParams);
virtual void OnDestroy();
virtual void OnCreateWindowSizeDependentResources(
ID3D12Resource* input,
ID3D12Resource* output,
uint32_t renderWidth,
uint32_t renderHeight,
uint32_t displayWidth,
uint32_t displayHeight,
bool hdr);
virtual void OnDestroyWindowSizeDependentResources();
virtual void BuildDevUI(UIState* pState) {}
virtual void PreDraw(UIState* pState);
virtual void GenerateReactiveMask(ID3D12GraphicsCommandList* pCommandList, const FfxUpscaleSetup& cameraSetup, UIState* pState);
virtual void Draw(ID3D12GraphicsCommandList* pCommandList, const FfxUpscaleSetup& cameraSetup, UIState* pState) = NULL;
protected:
Device* m_pDevice;
ResourceViewHeaps m_ResourceViewHeaps;
DynamicBufferRing m_ConstantBufferRing;
DXGI_FORMAT m_OutputFormat;
uint32_t m_MaxQueuedFrames;
UpscaleType m_Type;
std::string m_Name;
bool m_bInvertedDepth;
bool m_bUseTaa;
uint32_t m_renderWidth, m_renderHeight;
uint32_t m_displayWidth, m_displayHeight;
bool m_bInit;
uint32_t m_frameIndex;
uint32_t m_index;
float m_JitterX;
float m_JitterY;
ID3D12Resource* m_input;
ID3D12Resource* m_output;
bool m_hdr;
};

@ -0,0 +1,199 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "stdafx.h"
#include "UpscaleContext_FSR2_API.h"
#include <d3dcompiler.h>
#include <d3d12shader.h>
#include "ShaderCompiler.h"
#include "../ffx-fsr2-api/dx12/ffx_fsr2_dx12.h"
static bool isLuidsEqual(LUID luid1, LUID luid2)
{
return memcmp(&luid1, &luid2, sizeof(LUID)) == 0;
}
static uint64_t getMemoryUsageSnapshot(ID3D12Device* device)
{
uint64_t memoryUsage = -1;
IDXGIFactory* pFactory = nullptr;
if (SUCCEEDED(CreateDXGIFactory2(0, IID_PPV_ARGS(&pFactory)))) {
IDXGIAdapter* pAdapter = nullptr;
UINT i = 0;
while (pFactory->EnumAdapters(i++, &pAdapter) != DXGI_ERROR_NOT_FOUND)
{
DXGI_ADAPTER_DESC desc{};
if (SUCCEEDED(pAdapter->GetDesc(&desc))) {
if (isLuidsEqual(desc.AdapterLuid, device->GetAdapterLuid())) {
IDXGIAdapter4* pAdapter4 = nullptr;
if (SUCCEEDED(pAdapter->QueryInterface(IID_PPV_ARGS(&pAdapter4)))) {
DXGI_QUERY_VIDEO_MEMORY_INFO info{};
if (SUCCEEDED(pAdapter4->QueryVideoMemoryInfo(0, DXGI_MEMORY_SEGMENT_GROUP_LOCAL, &info))) {
memoryUsage = info.CurrentUsage;
}
pAdapter4->Release();
}
}
pAdapter->Release();
}
}
pFactory->Release();
}
return memoryUsage;
}
UpscaleContext_FSR2_API::UpscaleContext_FSR2_API(UpscaleType type, std::string name)
: UpscaleContext(name)
{
}
void UpscaleContext_FSR2_API::OnCreate(const FfxUpscaleInitParams& initParams)
{
UpscaleContext::OnCreate(initParams);
}
void UpscaleContext_FSR2_API::OnDestroy()
{
UpscaleContext::OnDestroy();
}
void UpscaleContext_FSR2_API::OnCreateWindowSizeDependentResources(
ID3D12Resource* input,
ID3D12Resource* output,
uint32_t renderWidth,
uint32_t renderHeight,
uint32_t displayWidth,
uint32_t displayHeight,
bool hdr)
{
UpscaleContext::OnCreateWindowSizeDependentResources(input, output, renderWidth, renderHeight, displayWidth, displayHeight, hdr);
// Setup DX12 interface.
const size_t scratchBufferSize = ffxFsr2GetScratchMemorySizeDX12();
void* scratchBuffer = malloc(scratchBufferSize);
FfxErrorCode errorCode = ffxFsr2GetInterfaceDX12(&initializationParameters.callbacks, m_pDevice->GetDevice(), scratchBuffer, scratchBufferSize);
FFX_ASSERT(errorCode == FFX_OK);
// This adds a ref to the device. The reference will get freed in ffxFsr2ContextDestroy
initializationParameters.device = ffxGetDeviceDX12(m_pDevice->GetDevice());
initializationParameters.maxRenderSize.width = renderWidth;
initializationParameters.maxRenderSize.height = renderHeight;
initializationParameters.displaySize.width = displayWidth;
initializationParameters.displaySize.height = displayHeight;
initializationParameters.flags = FFX_FSR2_ENABLE_DEPTH_INVERTED
| FFX_FSR2_ENABLE_AUTO_EXPOSURE;
if (hdr) {
initializationParameters.flags |= FFX_FSR2_ENABLE_HIGH_DYNAMIC_RANGE;
}
#if COMPILE_FROM_HLSL
// Override the shader creation so we can compile from HLSL source.
FfxFsr2Interface d3dInterface = {};
errorCode = ffxFsr2GetInterfaceDX12(&d3dInterface, m_pDevice->GetDevice(), scratchBuffer, scratchBufferSize);
initializationParameters.callbacks.fpCreateRenderPass = s_bUseHLSLShaders ? createRenderPassFromSource : d3dInterface.fpCreateRenderPass;
// copy for use by on-demand shader compile from HLSL
memcpy(&s_initializationParameters, &initializationParameters, sizeof(FfxFsr2ContextDescription));
#endif // #if COMPILE_FROM_HLSL
const uint64_t memoryUsageBefore = getMemoryUsageSnapshot(m_pDevice->GetDevice());
ffxFsr2ContextCreate(&context, &initializationParameters);
const uint64_t memoryUsageAfter = getMemoryUsageSnapshot(m_pDevice->GetDevice());
memoryUsageInMegabytes = (memoryUsageAfter - memoryUsageBefore) * 0.000001f;
}
void UpscaleContext_FSR2_API::OnDestroyWindowSizeDependentResources()
{
UpscaleContext::OnDestroyWindowSizeDependentResources();
ffxFsr2ContextDestroy(&context);
free(initializationParameters.callbacks.scratchBuffer);
}
void UpscaleContext_FSR2_API::BuildDevUI(UIState* pState)
{
}
void UpscaleContext_FSR2_API::ReloadPipelines()
{
m_pDevice->GPUFlush();
OnDestroyWindowSizeDependentResources();
OnCreateWindowSizeDependentResources(m_input, m_output, m_renderWidth, m_renderHeight, m_displayWidth, m_displayHeight, m_hdr);
}
void UpscaleContext_FSR2_API::GenerateReactiveMask(ID3D12GraphicsCommandList* pCommandList, const FfxUpscaleSetup& cameraSetup, UIState* pState)
{
FfxFsr2GenerateReactiveDescription generateReactiveParameters;
generateReactiveParameters.commandList = ffxGetCommandListDX12(pCommandList);
generateReactiveParameters.colorOpaqueOnly = ffxGetResourceDX12(&context, cameraSetup.opaqueOnlyColorResource);
generateReactiveParameters.colorPreUpscale = ffxGetResourceDX12(&context, cameraSetup.unresolvedColorResource);
generateReactiveParameters.outReactive = ffxGetResourceDX12(&context, cameraSetup.reactiveMapResource, L"FSR2_InputReactiveMap", FFX_RESOURCE_STATE_UNORDERED_ACCESS);
generateReactiveParameters.renderSize.width = pState->renderWidth;
generateReactiveParameters.renderSize.height = pState->renderHeight;
generateReactiveParameters.scale = pState->fFsr2AutoReactiveScale;
generateReactiveParameters.cutoffThreshold = pState->fFsr2AutoReactiveThreshold;
generateReactiveParameters.flags = (pState->bFsr2AutoReactiveTonemap ? FFX_FSR2_AUTOREACTIVEFLAGS_APPLY_TONEMAP :0) |
(pState->bFsr2AutoReactiveInverseTonemap ? FFX_FSR2_AUTOREACTIVEFLAGS_APPLY_INVERSETONEMAP : 0) |
(pState->bFsr2AutoReactiveThreshold ? FFX_FSR2_AUTOREACTIVEFLAGS_APPLY_THRESHOLD : 0) |
(pState->bFsr2AutoReactiveUseMax ? FFX_FSR2_AUTOREACTIVEFLAGS_USE_COMPONENTS_MAX : 0);
ffxFsr2ContextGenerateReactiveMask(&context, &generateReactiveParameters);
}
void UpscaleContext_FSR2_API::Draw(ID3D12GraphicsCommandList* pCommandList, const FfxUpscaleSetup& cameraSetup, UIState* pState)
{
FfxFsr2DispatchDescription dispatchParameters = {};
dispatchParameters.commandList = ffxGetCommandListDX12(pCommandList);
dispatchParameters.color = ffxGetResourceDX12(&context, cameraSetup.unresolvedColorResource, L"FSR2_InputColor");
dispatchParameters.depth = ffxGetResourceDX12(&context, cameraSetup.depthbufferResource, L"FSR2_InputDepth");
dispatchParameters.motionVectors = ffxGetResourceDX12(&context, cameraSetup.motionvectorResource, L"FSR2_InputMotionVectors");
dispatchParameters.exposure = ffxGetResourceDX12(&context, nullptr, L"FSR2_InputExposure");
dispatchParameters.reactive = ffxGetResourceDX12(&context, cameraSetup.reactiveMapResource, L"FSR2_InputReactiveMap");
dispatchParameters.transparencyAndComposition = ffxGetResourceDX12(&context, cameraSetup.transparencyAndCompositionResource, L"FSR2_TransparencyAndCompositionMap");
dispatchParameters.output = ffxGetResourceDX12(&context, cameraSetup.resolvedColorResource, L"FSR2_OutputUpscaledColor", FFX_RESOURCE_STATE_UNORDERED_ACCESS);
dispatchParameters.jitterOffset.x = m_JitterX;
dispatchParameters.jitterOffset.y = m_JitterY;
dispatchParameters.motionVectorScale.x = (float)pState->renderWidth;
dispatchParameters.motionVectorScale.y = (float)pState->renderHeight;
dispatchParameters.reset = false;
dispatchParameters.enableSharpening = pState->bUseRcas;
dispatchParameters.sharpness = pState->sharpening;
dispatchParameters.frameTimeDelta = (float)pState->deltaTime;
dispatchParameters.preExposure = 1.0f;
dispatchParameters.renderSize.width = pState->renderWidth;
dispatchParameters.renderSize.height = pState->renderHeight;
dispatchParameters.cameraFar = pState->camera.GetFarPlane();
dispatchParameters.cameraNear = pState->camera.GetNearPlane();
dispatchParameters.cameraFovAngleVertical = pState->camera.GetFovV();
pState->bReset = false;
FfxErrorCode errorCode = ffxFsr2ContextDispatch(&context, &dispatchParameters);
FFX_ASSERT(errorCode == FFX_OK);
}

@ -0,0 +1,64 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#pragma once
#include "Base/Texture.h"
#include "Base/StaticBufferPool.h"
#include "Base/GBuffer.h"
#include "PostProc/PostProcCS.h"
#include "UI.h"
#include "UpscaleContext.h"
#include "../ffx-fsr2-api/ffx_fsr2.h"
using namespace CAULDRON_DX12;
class UpscaleContext_FSR2_API : public UpscaleContext
{
public:
UpscaleContext_FSR2_API(UpscaleType type, std::string name);
virtual std::string Name() { return "FSR 2.0 API"; }
virtual void OnCreate(const FfxUpscaleInitParams& initParams);
virtual void OnDestroy();
virtual void OnCreateWindowSizeDependentResources(
ID3D12Resource* input,
ID3D12Resource* output,
uint32_t renderWidth,
uint32_t renderHeight,
uint32_t displayWidth,
uint32_t displayHeight,
bool hdr);
virtual void OnDestroyWindowSizeDependentResources();
virtual void BuildDevUI(UIState* pState) override;
virtual void GenerateReactiveMask(ID3D12GraphicsCommandList* pCommandList, const FfxUpscaleSetup& cameraSetup, UIState* pState);
virtual void Draw(ID3D12GraphicsCommandList* pCommandList, const FfxUpscaleSetup& cameraSetup, UIState* pState);
private:
void ReloadPipelines();
FfxFsr2ContextDescription initializationParameters = {};
FfxFsr2Context context;
float memoryUsageInMegabytes = 0;
};

@ -0,0 +1,363 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "stdafx.h"
#include "UpscaleContext_Spatial.h"
// CAS
#define FFX_CPU
#include "shaders/ffx_core.h"
#include "shaders/ffx_fsr1.h"
#include <DirectXMath.h>
using namespace DirectX;
struct FSRConstants
{
XMUINT4 Const0;
XMUINT4 Const1;
XMUINT4 Const2;
XMUINT4 Const3;
XMUINT4 Sample;
};
// Upscaler using up to 3 passes:
// 1) optionally TAA
// 2) Spatial upscaler (point, bilinear, bicubic or EASU)
// 3) optionally RCAS
UpscaleContext_Spatial::UpscaleContext_Spatial(UpscaleType type, std::string name)
: UpscaleContext(name)
{
m_bUseRcas = true;
m_bHdr = false;
}
//--------------------------------------------------------------------------------------
//
// OnCreate
//
//--------------------------------------------------------------------------------------
void UpscaleContext_Spatial::OnCreate(const FfxUpscaleInitParams& initParams)
{
UpscaleContext::OnCreate(initParams);
bool slowFallback = false;
// TAA descriptor tables
m_TaaTableSrv.Init(&m_ResourceViewHeaps, 4, m_MaxQueuedFrames);
m_TaaUav.Init(&m_ResourceViewHeaps, 1, m_MaxQueuedFrames);
m_FsrSrv.Init(&m_ResourceViewHeaps, 1, m_MaxQueuedFrames);
m_FsrUav.Init(&m_ResourceViewHeaps, 1, m_MaxQueuedFrames);
m_RCasSrv.Init(&m_ResourceViewHeaps, 1, m_MaxQueuedFrames);
m_RCasUav.Init(&m_ResourceViewHeaps, 1, m_MaxQueuedFrames);
CD3DX12_STATIC_SAMPLER_DESC sd[4] = {};
sd[0].Init(0, D3D12_FILTER_MIN_MAG_MIP_POINT, D3D12_TEXTURE_ADDRESS_MODE_CLAMP, D3D12_TEXTURE_ADDRESS_MODE_CLAMP);
sd[1].Init(1, D3D12_FILTER_MIN_MAG_MIP_POINT, D3D12_TEXTURE_ADDRESS_MODE_CLAMP, D3D12_TEXTURE_ADDRESS_MODE_CLAMP);
sd[2].Init(2, D3D12_FILTER_MIN_MAG_MIP_LINEAR, D3D12_TEXTURE_ADDRESS_MODE_CLAMP, D3D12_TEXTURE_ADDRESS_MODE_CLAMP);
sd[3].Init(3, D3D12_FILTER_MIN_MAG_MIP_POINT, D3D12_TEXTURE_ADDRESS_MODE_CLAMP, D3D12_TEXTURE_ADDRESS_MODE_CLAMP);
DefineList defines;
defines["UPSCALE_TYPE"] = std::to_string(UPSCALE_TYPE_NATIVE);
defines["SAMPLE_SLOW_FALLBACK"] = "1";
defines["SAMPLE_EASU"] = "0";
defines["SAMPLE_RCAS"] = "0";
defines["USE_WS_MOTIONVECTORS"] = "0";
m_taa.OnCreate(m_pDevice, &m_ResourceViewHeaps, "TAA.hlsl", "main", 1, 4, 16, 16, 1, &defines, 4, sd);
m_taaFirst.OnCreate(m_pDevice, &m_ResourceViewHeaps, "TAA.hlsl", "first", 1, 4, 16, 16, 1, &defines, 4, sd);
defines["SAMPLE_SLOW_FALLBACK"] = (slowFallback ? "1" : "0");
defines["UPSCALE_TYPE"] = std::to_string(UPSCALE_TYPE_FSR_1_0);
defines["SAMPLE_RCAS"] = "1";
defines["DO_INVREINHARD"] = "0";
m_rcas.OnCreate(m_pDevice, &m_ResourceViewHeaps, "UpscaleSpatial.hlsl", "upscalePassCS", 1, 1, 64, 1, 1, &defines, 2, sd);
defines["UPSCALE_TYPE"] = std::to_string(m_Type);
defines["SAMPLE_EASU"] = "1";
defines["SAMPLE_RCAS"] = "0";
m_upscale.OnCreate(m_pDevice, &m_ResourceViewHeaps, "UpscaleSpatial.hlsl", "upscalePassCS", 1, 1, 64, 1, 1, &defines, 2, sd);
defines["DO_INVREINHARD"] = "1";
m_upscaleTAA.OnCreate(m_pDevice, &m_ResourceViewHeaps, "UpscaleSpatial.hlsl", "upscalePassCS", 1, 1, 64, 1, 1, &defines, 2, sd);
}
//--------------------------------------------------------------------------------------
//
// OnDestroy
//
//--------------------------------------------------------------------------------------
void UpscaleContext_Spatial::OnDestroy()
{
m_taa.OnDestroy();
m_taaFirst.OnDestroy();
m_upscale.OnDestroy();
m_upscaleTAA.OnDestroy();
m_rcas.OnDestroy();
UpscaleContext::OnDestroy();
}
//--------------------------------------------------------------------------------------
//
// OnCreateWindowSizeDependentResources
//
//--------------------------------------------------------------------------------------
void UpscaleContext_Spatial::OnCreateWindowSizeDependentResources(ID3D12Resource* input, ID3D12Resource* output, uint32_t renderWidth, uint32_t renderHeight, uint32_t displayWidth, uint32_t displayHeight, bool hdr)
{
UpscaleContext::OnCreateWindowSizeDependentResources(input, output, renderWidth, renderHeight, displayWidth, displayHeight, hdr);
DXGI_FORMAT fmt = DXGI_FORMAT_R16G16B16A16_FLOAT;
m_TAAIntermediary[0].InitRenderTarget(m_pDevice, "TaaIntermediary0", &CD3DX12_RESOURCE_DESC::Tex2D(fmt, renderWidth, renderHeight, 1, 1, 1, 0, D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS), D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);
m_TAAIntermediary[1].InitRenderTarget(m_pDevice, "TaaIntermediary1", &CD3DX12_RESOURCE_DESC::Tex2D(fmt, renderWidth, renderHeight, 1, 1, 1, 0, D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS), D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);
fmt = (m_bHdr ? DXGI_FORMAT_R10G10B10A2_UNORM : DXGI_FORMAT_R8G8B8A8_UNORM);
m_FSRIntermediary.InitRenderTarget(m_pDevice, "FSRIntermediary", &CD3DX12_RESOURCE_DESC::Tex2D(fmt, displayWidth, displayHeight, 1, 1, 1, 0, D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS), D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
m_bResetTaa = true;
}
//--------------------------------------------------------------------------------------
//
// OnDestroyWindowSizeDependentResources
//
//--------------------------------------------------------------------------------------
void UpscaleContext_Spatial::OnDestroyWindowSizeDependentResources()
{
m_TAAIntermediary[0].OnDestroy();
m_TAAIntermediary[1].OnDestroy();
m_FSRIntermediary.OnDestroy();
}
//--------------------------------------------------------------------------------------
//
// Draw: executes the Spatial upscale (optionally with additional TAA and sharpening passes)
//
//--------------------------------------------------------------------------------------
void UpscaleContext_Spatial::Draw(ID3D12GraphicsCommandList* pCommandList, const FfxUpscaleSetup& cameraSetup, UIState* pState)
{
ID3D12DescriptorHeap* pDescriptorHeaps[] = { m_ResourceViewHeaps.GetCBV_SRV_UAVHeap(), m_ResourceViewHeaps.GetSamplerHeap() };
pCommandList->SetDescriptorHeaps(2, pDescriptorHeaps);
ID3D12Resource* curInput = cameraSetup.unresolvedColorResource;
ID3D12Resource* curOutput = cameraSetup.resolvedColorResource;
m_bUseRcas = pState->bUseRcas;
// TAA
if (m_bUseTaa)
{
UserMarker marker(pCommandList, "TAA");
pCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_TAAIntermediary[(m_index +1) & 1].GetResource(), D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS));
{
// set up constantbuffer
m_TaaConsts.g_PrevRenderResolution[0] = m_bInit ? m_renderWidth : m_TaaConsts.g_CurRenderResolution[0];
m_TaaConsts.g_PrevRenderResolution[1] = m_bInit ? m_renderHeight : m_TaaConsts.g_CurRenderResolution[1];
m_TaaConsts.g_PrevScreenResolution[0] = m_bInit ? m_displayWidth : m_TaaConsts.g_CurScreenResolution[0];
m_TaaConsts.g_PrevScreenResolution[1] = m_bInit ? m_displayHeight : m_TaaConsts.g_CurScreenResolution[1];
m_TaaConsts.g_CurRenderResolution[0] = pState->renderWidth;
m_TaaConsts.g_CurRenderResolution[1] = pState->renderHeight;
m_TaaConsts.g_CurScreenResolution[0] = m_displayWidth;
m_TaaConsts.g_CurScreenResolution[1] = m_displayHeight;
m_TaaConsts.g_ClosestVelocitySamplePattern = pState->closestVelocitySamplePattern;
m_TaaConsts.g_FeedbackDefault = pState->Feedback;
// set previous projection, remove jitter
m_TaaConsts.cameraPrev = m_TaaConsts.cameraCurr;
math::Vector4 projX = m_TaaConsts.cameraPrev.mCameraProj.getCol0();
math::Vector4 projY = m_TaaConsts.cameraPrev.mCameraProj.getCol1();
projX.setZ(0);
projY.setZ(0);
m_TaaConsts.cameraPrev.mCameraProj.setCol0(projX);
m_TaaConsts.cameraPrev.mCameraProj.setCol1(projY);
m_TaaConsts.cameraCurr.mCameraView = math::transpose(pState->camera.GetView());
m_TaaConsts.cameraCurr.mCameraProj = math::transpose(pState->camera.GetProjection());
m_TaaConsts.cameraCurr.mCameraViewInv = math::transpose(math::inverse(pState->camera.GetView()));
m_TaaConsts.cameraCurr.vCameraPos = pState->camera.GetPosition();
if (m_bInit) m_TaaConsts.cameraPrev = m_TaaConsts.cameraCurr;
}
D3D12_GPU_VIRTUAL_ADDRESS cbHandle = {};
uint32_t* pConstMem = 0;
m_ConstantBufferRing.AllocConstantBuffer(sizeof(FfxTaaCB), (void**)&pConstMem, &cbHandle);
memcpy(pConstMem, &m_TaaConsts, sizeof(FfxTaaCB));
// set up SRVs
D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
srvDesc.Texture2D.MipLevels = 1;
srvDesc.Format = cameraSetup.unresolvedColorResource->GetDesc().Format;
m_pDevice->GetDevice()->CreateShaderResourceView(cameraSetup.unresolvedColorResource, &srvDesc, m_TaaTableSrv.GetFrame(m_frameIndex).GetCPU(0));
// Depth: change typeless to FLOAT for depth
switch (cameraSetup.depthbufferResource->GetDesc().Format)
{
// Override TYPELESS resources to prevent device removed.
case DXGI_FORMAT_D32_FLOAT:
case DXGI_FORMAT_R32_TYPELESS: srvDesc.Format = DXGI_FORMAT_R32_FLOAT;
break;
case DXGI_FORMAT_R16_TYPELESS: srvDesc.Format = DXGI_FORMAT_R16_FLOAT;
break;
case DXGI_FORMAT_D16_UNORM: srvDesc.Format = DXGI_FORMAT_R16_UNORM;
break;
default:
srvDesc.Format = cameraSetup.depthbufferResource->GetDesc().Format;
break;
}
m_pDevice->GetDevice()->CreateShaderResourceView(cameraSetup.depthbufferResource, &srvDesc, m_TaaTableSrv.GetFrame(m_frameIndex).GetCPU(1));
srvDesc.Format = m_TAAIntermediary[m_index & 1].GetResource()->GetDesc().Format;
m_pDevice->GetDevice()->CreateShaderResourceView(m_TAAIntermediary[m_index & 1].GetResource(), &srvDesc, m_TaaTableSrv.GetFrame(m_frameIndex).GetCPU(2));
srvDesc.Format = cameraSetup.motionvectorResource->GetDesc().Format;
m_pDevice->GetDevice()->CreateShaderResourceView(cameraSetup.motionvectorResource, &srvDesc, m_TaaTableSrv.GetFrame(m_frameIndex).GetCPU(3));
D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc = {};
uavDesc.Format = m_OutputFormat;
uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D;
uavDesc.Format = m_TAAIntermediary[(m_index + 1) & 1].GetResource()->GetDesc().Format;
m_pDevice->GetDevice()->CreateUnorderedAccessView(m_TAAIntermediary[(m_index+1) & 1].GetResource(), 0, &uavDesc, m_TaaUav.GetFrame(m_frameIndex).GetCPU(0));
static const int threadGroupWorkRegionDim = 16;
int dispatchX = (pState->renderWidth + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
int dispatchY = (pState->renderHeight + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
CBV_SRV_UAV currTaaUavs = m_TaaUav.GetFrame(m_frameIndex);
CBV_SRV_UAV currTaaSrvs = m_TaaTableSrv.GetFrame(m_frameIndex);
if (m_bResetTaa)
{
m_taaFirst.Draw(pCommandList, cbHandle, &currTaaUavs, &currTaaSrvs, dispatchX, dispatchY, 1);
m_bResetTaa = false;
}
else
{
m_taa.Draw(pCommandList, cbHandle, &currTaaUavs, &currTaaSrvs, dispatchX, dispatchY, 1);
}
// set up taa target
curInput = m_TAAIntermediary[(m_index+1) & 1].GetResource();
pCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_TAAIntermediary[(m_index + 1) & 1].GetResource(), D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE));
}
// Upscale
{
UserMarker marker(pCommandList, "Upscale");
ID3D12Resource* pFsrInput = curInput;
ID3D12Resource* pFsrOutput = nullptr;
if (m_bUseRcas)
{
// write to intermediary surface
pFsrOutput = m_FSRIntermediary.GetResource();
}
else
{
// output to final
pFsrOutput = cameraSetup.resolvedColorResource;
}
D3D12_GPU_VIRTUAL_ADDRESS cbHandle = {};
{
FSRConstants consts = {};
ffxFsrPopulateEasuConstants(
reinterpret_cast<FfxUInt32*>(&consts.Const0),
reinterpret_cast<FfxUInt32*>(&consts.Const1),
reinterpret_cast<FfxUInt32*>(&consts.Const2),
reinterpret_cast<FfxUInt32*>(&consts.Const3),
static_cast<FfxFloat32>(pState->renderWidth), static_cast<FfxFloat32>(pState->renderHeight),
static_cast<FfxFloat32>(m_renderWidth), static_cast<FfxFloat32>(m_renderHeight),
static_cast<FfxFloat32>(pState->displayWidth), static_cast<FfxFloat32>(pState->displayHeight));
uint32_t* pConstMem = 0;
m_ConstantBufferRing.AllocConstantBuffer(sizeof(FSRConstants), (void**)&pConstMem, &cbHandle);
memcpy(pConstMem, &consts, sizeof(FSRConstants));
}
D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
srvDesc.Texture2D.MipLevels = 1;
srvDesc.Format = pFsrInput->GetDesc().Format;
m_pDevice->GetDevice()->CreateShaderResourceView(pFsrInput, &srvDesc, m_FsrSrv.GetFrame(m_frameIndex).GetCPU(0));
D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc = {};
uavDesc.Format = m_OutputFormat;
uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D;
uavDesc.Format = pFsrOutput->GetDesc().Format;
m_pDevice->GetDevice()->CreateUnorderedAccessView(pFsrOutput, 0, &uavDesc, m_FsrUav.GetFrame(m_frameIndex).GetCPU(0));
// This value is the image region dimension that each thread group of the FSR shader operates on
static const int threadGroupWorkRegionDim = 16;
int dispatchX = (pState->displayWidth + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
int dispatchY = (pState->displayHeight + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
CBV_SRV_UAV currFsrUavs = m_FsrUav.GetFrame(m_frameIndex);
CBV_SRV_UAV currFsrSrvs = m_FsrSrv.GetFrame(m_frameIndex);
if (m_bUseTaa)
m_upscaleTAA.Draw(pCommandList, cbHandle, &currFsrUavs, &currFsrSrvs, dispatchX, dispatchY, 1);
else
m_upscale.Draw(pCommandList, cbHandle, &currFsrUavs, &currFsrSrvs, dispatchX, dispatchY, 1);
curInput = pFsrOutput;
}
// RCAS
if (pState->bUseRcas)
{
pCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(curInput, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE));
UserMarker marker(pCommandList, "RCAS");
D3D12_GPU_VIRTUAL_ADDRESS cbHandle = {};
{
FSRConstants consts = {};
FsrRcasCon(reinterpret_cast<FfxUInt32*>(&consts.Const0), pState->sharpening);
consts.Sample.x = (m_bHdr ? 1 : 0);
uint32_t* pConstMem = 0;
m_ConstantBufferRing.AllocConstantBuffer(sizeof(FSRConstants), (void**)&pConstMem, &cbHandle);
memcpy(pConstMem, &consts, sizeof(FSRConstants));
}
D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
srvDesc.Texture2D.MipLevels = 1;
srvDesc.Format = curInput->GetDesc().Format;
m_pDevice->GetDevice()->CreateShaderResourceView(curInput, &srvDesc, m_RCasSrv.GetFrame(m_frameIndex).GetCPU(0));
D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc = {};
uavDesc.Format = m_OutputFormat;
uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D;
uavDesc.Format = curOutput->GetDesc().Format;
m_pDevice->GetDevice()->CreateUnorderedAccessView(curOutput, 0, &uavDesc, m_RCasUav.GetFrame(m_frameIndex).GetCPU(0));
static const int threadGroupWorkRegionDim = 16;
int dispatchX = (pState->displayWidth + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
int dispatchY = (pState->displayHeight + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
CBV_SRV_UAV currRCasUavs = m_RCasUav.GetFrame(m_frameIndex);
CBV_SRV_UAV currRCasSrvs = m_RCasSrv.GetFrame(m_frameIndex);
m_rcas.Draw(pCommandList, cbHandle, &currRCasUavs, &currRCasSrvs, dispatchX, dispatchY, 1);
pCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(curInput, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS));
}
}

@ -0,0 +1,84 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#pragma once
#include "Base/Texture.h"
#include "Base/StaticBufferPool.h"
#include "Base/GBuffer.h"
#include "PostProc/PostProcCS.h"
#include "UI.h"
#include "UpscaleContext.h"
using namespace CAULDRON_DX12;
class UpscaleContext_Spatial : public UpscaleContext
{
public:
UpscaleContext_Spatial(UpscaleType type, std::string name);
virtual std::string Name() { return "Spatial Upscale"; }
virtual void OnCreate(const FfxUpscaleInitParams& initParams);
virtual void OnDestroy();
virtual void OnCreateWindowSizeDependentResources(ID3D12Resource* input, ID3D12Resource* output, uint32_t renderWidth, uint32_t renderHeight, uint32_t displayWidth, uint32_t displayHeight, bool hdr);
virtual void OnDestroyWindowSizeDependentResources();
virtual void Draw(ID3D12GraphicsCommandList* pCommandList, const FfxUpscaleSetup& cameraSetup, UIState* pState);
protected:
typedef struct _FfxTaaCB_
{
int g_PrevRenderResolution[2];
int g_PrevScreenResolution[2];
int g_CurRenderResolution[2];
int g_CurScreenResolution[2];
int g_taaMode;
int g_ClosestVelocitySamplePattern;
float g_FeedbackDefault;
int _pad0;
FfxCameraSetup cameraCurr;
FfxCameraSetup cameraPrev;
}FfxTaaCB;
bool m_bUseRcas;
bool m_bHdr;
FfxTaaCB m_TaaConsts;
PostProcCS m_upscale;
PostProcCS m_upscaleTAA; // Cauldron TAA requires Inverse Reinhard after upscale path
PostProcCS m_rcas;
PostProcCS m_taa;
PostProcCS m_taaFirst;
Texture m_TAAIntermediary[2];
Texture m_FSRIntermediary;
CBV_SRV_UAV_RING m_TaaTableSrv;
CBV_SRV_UAV_RING m_TaaUav;
CBV_SRV_UAV_RING m_FsrSrv;
CBV_SRV_UAV_RING m_FsrUav;
CBV_SRV_UAV_RING m_RCasSrv;
CBV_SRV_UAV_RING m_RCasUav;
bool m_bResetTaa = true;
};

@ -0,0 +1,248 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#define GROUP_SIZE 8
// These must match UpscaleType
#define UPSCALE_TYPE_POINT 0
#define UPSCALE_TYPE_BILINEAR 1
#define UPSCALE_TYPE_BICUBIC 2
#define UPSCALE_TYPE_FSR1 3
#define UPSCALE_TYPE_NATIVE 5
#if SAMPLE_EASU || SAMPLE_RCAS
[[vk::binding(0)]] cbuffer cb : register(b0)
{
uint4 Const0;
uint4 Const1;
uint4 Const2;
uint4 Const3;
uint4 Sample;
};
#endif
#define FFX_GPU 1
#define FFX_HLSL 1
[[vk::binding(3)]] SamplerState PointSampler : register(s0);
[[vk::binding(4)]] SamplerState LinearSampler : register(s1);
#if SAMPLE_SLOW_FALLBACK
#include "ffx_core.h"
[[vk::binding(1)]] Texture2D ColorBuffer : register(t0);
[[vk::binding(2)]] RWTexture2D<float4> OutputTexture : register(u0);
#if SAMPLE_EASU
#define FFX_FSR_EASU_FLOAT
FfxFloat32x4 FsrEasuRF(FfxFloat32x2 p) { FfxFloat32x4 res = ColorBuffer.GatherRed(LinearSampler, p, int2(0, 0)); return res; }
FfxFloat32x4 FsrEasuGF(FfxFloat32x2 p) { FfxFloat32x4 res = ColorBuffer.GatherGreen(LinearSampler, p, int2(0, 0)); return res; }
FfxFloat32x4 FsrEasuBF(FfxFloat32x2 p) { FfxFloat32x4 res = ColorBuffer.GatherBlue(LinearSampler, p, int2(0, 0)); return res; }
#endif
#if SAMPLE_RCAS
#define FSR_RCAS_F
FfxFloat32x4 FsrRcasLoadF(ASU2 p) { return ColorBuffer.Load(int3(ASU2(p), 0)); }
void FsrRcasInputF(inout FfxFloat32 r, inout FfxFloat32 g, inout FfxFloat32 b) {}
#endif
#else
#define FFX_HALF 1
#include "ffx_core.h"
[[vk::binding(1)]] Texture2D<FfxFloat16x4> ColorBuffer : register(t0);
[[vk::binding(2)]] RWTexture2D<FfxFloat16x4> OutputTexture : register(u0);
#if SAMPLE_EASU
#define FFX_FSR_EASU_HALF
FfxFloat16x4 FsrEasuRH(FfxFloat32x2 p) { FfxFloat16x4 res = ColorBuffer.GatherRed(LinearSampler, p, int2(0, 0)); return res; }
FfxFloat16x4 FsrEasuGH(FfxFloat32x2 p) { FfxFloat16x4 res = ColorBuffer.GatherGreen(LinearSampler, p, int2(0, 0)); return res; }
FfxFloat16x4 FsrEasuBH(FfxFloat32x2 p) { FfxFloat16x4 res = ColorBuffer.GatherBlue(LinearSampler, p, int2(0, 0)); return res; }
#endif
#if SAMPLE_RCAS
#define FSR_RCAS_H
FfxFloat16x4 FsrRcasLoadH(FfxInt16x2 p) { return ColorBuffer.Load(FfxInt16x3(FfxInt16x2 (p), 0)); }
void FsrRcasInputH(inout FfxFloat16 r, inout FfxFloat16 g, inout FfxFloat16 b) {}
#endif
#endif
#include "ffx_fsr1.h"
/**********************************************************************
MIT License
Copyright(c) 2019 MJP
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
********************************************************************/
float4 SampleHistoryCatmullRom(in float2 uv, in float2 texelSize)
{
// Source: https://gist.github.com/TheRealMJP/c83b8c0f46b63f3a88a5986f4fa982b1
// License: https://gist.github.com/TheRealMJP/bc503b0b87b643d3505d41eab8b332ae
// We're going to sample a a 4x4 grid of texels surrounding the target UV coordinate. We'll do this by rounding
// down the sample location to get the exact center of our "starting" texel. The starting texel will be at
// location [1, 1] in the grid, where [0, 0] is the top left corner.
float2 samplePos = uv / texelSize;
float2 texPos1 = floor(samplePos - 0.5f) + 0.5f;
// Compute the fractional offset from our starting texel to our original sample location, which we'll
// feed into the Catmull-Rom spline function to get our filter weights.
float2 f = samplePos - texPos1;
// Compute the Catmull-Rom weights using the fractional offset that we calculated earlier.
// These equations are pre-expanded based on our knowledge of where the texels will be located,
// which lets us avoid having to evaluate a piece-wise function.
float2 w0 = f * (-0.5f + f * (1.0f - 0.5f * f));
float2 w1 = 1.0f + f * f * (-2.5f + 1.5f * f);
float2 w2 = f * (0.5f + f * (2.0f - 1.5f * f));
float2 w3 = f * f * (-0.5f + 0.5f * f);
// Work out weighting factors and sampling offsets that will let us use bilinear filtering to
// simultaneously evaluate the middle 2 samples from the 4x4 grid.
float2 w12 = w1 + w2;
float2 offset12 = w2 / (w1 + w2);
// Compute the final UV coordinates we'll use for sampling the texture
float2 texPos0 = texPos1 - 1.0f;
float2 texPos3 = texPos1 + 2.0f;
float2 texPos12 = texPos1 + offset12;
texPos0 *= texelSize;
texPos3 *= texelSize;
texPos12 *= texelSize;
float4 result = 0.f;
result += ColorBuffer.SampleLevel(LinearSampler, float2(texPos0.x, texPos0.y), 0.0f) * w0.x * w0.y;
result += ColorBuffer.SampleLevel(LinearSampler, float2(texPos12.x, texPos0.y), 0.0f) * w12.x * w0.y;
result += ColorBuffer.SampleLevel(LinearSampler, float2(texPos3.x, texPos0.y), 0.0f) * w3.x * w0.y;
result += ColorBuffer.SampleLevel(LinearSampler, float2(texPos0.x, texPos12.y), 0.0f) * w0.x * w12.y;
result += ColorBuffer.SampleLevel(LinearSampler, float2(texPos12.x, texPos12.y), 0.0f) * w12.x * w12.y;
result += ColorBuffer.SampleLevel(LinearSampler, float2(texPos3.x, texPos12.y), 0.0f) * w3.x * w12.y;
result += ColorBuffer.SampleLevel(LinearSampler, float2(texPos0.x, texPos3.y), 0.0f) * w0.x * w3.y;
result += ColorBuffer.SampleLevel(LinearSampler, float2(texPos12.x, texPos3.y), 0.0f) * w12.x * w3.y;
result += ColorBuffer.SampleLevel(LinearSampler, float2(texPos3.x, texPos3.y), 0.0f) * w3.x * w3.y;
return max(result, 0.0f);
}
#if SAMPLE_EASU || SAMPLE_RCAS
float4 ReinhardInverse(in float4 sdr)
{
return float4( sdr.xyz / max(1.0f - sdr.xyz, 1e-5f), sdr.w);
}
FfxFloat16x4 ReinhardInverse(in FfxFloat16x4 sdr)
{
return FfxFloat16x4( sdr.xyz / max(FfxFloat16 (1.0f) - sdr.xyz, FfxFloat16 (1e-5f)), sdr.w);
}
void CurrFilter(int2 pos)
{
const float2 texelSize = FfxFloat32x2(1.f, -1.f) * asfloat(Const1.zw);
FfxFloat32x2 uv = (FfxFloat32x2(pos) * asfloat(Const0.xy) + asfloat(Const0.zw)) * asfloat(Const1.xy) + FfxFloat32x2(0.5, -0.5) * asfloat(Const1.zw);
#if SAMPLE_SLOW_FALLBACK
FfxFloat32x4 finalColor = 0.f;
#else
FfxFloat32x4 finalColor = 0.f;
#endif
#if (UPSCALE_TYPE == UPSCALE_TYPE_POINT) || (UPSCALE_TYPE == UPSCALE_TYPE_NATIVE)
finalColor = ColorBuffer.SampleLevel(PointSampler, uv, 0.0);
#elif UPSCALE_TYPE == UPSCALE_TYPE_BILINEAR
finalColor = ColorBuffer.SampleLevel(LinearSampler, uv, 0.0);
#elif UPSCALE_TYPE == UPSCALE_TYPE_BICUBIC
finalColor = SampleHistoryCatmullRom(uv, texelSize);
#elif UPSCALE_TYPE == UPSCALE_TYPE_FSR1
#if SAMPLE_EASU
#if SAMPLE_SLOW_FALLBACK
FsrEasuF(finalColor.xyz, pos, Const0, Const1, Const2, Const3);
#else
FsrEasuH(finalColor.xyz, pos, Const0, Const1, Const2, Const3);
#endif
#endif
#if SAMPLE_RCAS
#if SAMPLE_SLOW_FALLBACK
FsrRcasF(finalColor.r, finalColor.g, finalColor.b, pos, Const0);
#else
FsrRcasH(finalColor.r, finalColor.g, finalColor.b, pos, Const0);
#endif
if (Sample.x == 1)
finalColor.rgb *= finalColor.rgb;
#endif
finalColor.a = 1;
#endif
#if DO_INVREINHARD
OutputTexture[pos] = ReinhardInverse(finalColor);
#else
OutputTexture[pos] = finalColor;
#endif
}
[numthreads(WIDTH, HEIGHT, DEPTH)]
void upscalePassCS(uint3 LocalThreadId : SV_GroupThreadID, uint3 WorkGroupId : SV_GroupID, uint3 Dtid : SV_DispatchThreadID)
{
// Do remapping of local xy in workgroup for a more PS-like swizzle pattern.
FfxUInt32x2 gxy = ffxRemapForQuad(LocalThreadId.x) + FfxUInt32x2(WorkGroupId.x << 4u, WorkGroupId.y << 4u);
CurrFilter(gxy);
gxy.x += 8u;
CurrFilter(gxy);
gxy.y += 8u;
CurrFilter(gxy);
gxy.x -= 8u;
CurrFilter(gxy);
}
#else
[numthreads(WIDTH, HEIGHT, DEPTH)]
void BlitCS(uint3 globalID : SV_DispatchThreadID)
{
float4 srcColor = ColorBuffer[globalID.xy];
// remap channels
float4 dstColor = float4(srcColor[g_outChannelRed], srcColor[g_outChannelGreen], srcColor[g_outChannelBlue], 1.f);
// apply offset and scale
dstColor.rgb -= float3(outChannelRedMinMax.x, outChannelGreenMinMax.x, outChannelBlueMinMax.x);
dstColor.rgb /= float3(outChannelRedMinMax.y - outChannelRedMinMax.x, outChannelGreenMinMax.y - outChannelGreenMinMax.x, outChannelBlueMinMax.y - outChannelBlueMinMax.x);
#if FFX_HALF == 1
OutputTexture[globalID.xy] = FfxFloat16x4(dstColor);
#else
OutputTexture[globalID.xy] = float4(dstColor);
#endif
}
#endif

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/PM</dpiAware>
</windowsSettings>
</application>
</assembly>

@ -0,0 +1,29 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// stdafx.cpp : source file that includes just the standard includes
// SampleD3D12.pch will be the pre-compiled header
// stdafx.obj will contain the pre-compiled type information
#include "stdafx.h"
// TODO: reference any additional headers you need in STDAFX.H
// and not in this file

@ -0,0 +1,91 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently, but
// are changed infrequently
//
#pragma once
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
// Windows Header Files:
#include <windows.h>
#include <windowsx.h>
// C RunTime Header Files
#include <malloc.h>
#include <map>
#include <mutex>
#include <vector>
#include <fstream>
#include "../../libs/d3d12x/d3dx12.h"
// Pull in math library
#include "../../libs/vectormath/vectormath.hpp"
// TODO: reference additional headers your program requires here
#include "Base/Imgui.h"
#include "Base/ImguiHelper.h"
#include "Base/Fence.h"
#include "Base/Helper.h"
#include "Base/Device.h"
#include "Base/Texture.h"
#include "Base/FrameworkWindows.h"
#include "Base/FreeSyncHDR.h"
#include "Base/SwapChain.h"
#include "Base/UploadHeap.h"
#include "Base/SaveTexture.h"
#include "Base/UserMarkers.h"
#include "Base/GPUTimestamps.h"
#include "Base/CommandListRing.h"
#include "Base/StaticBufferPool.h"
#include "Base/DynamicBufferRing.h"
#include "Base/ResourceViewHeaps.h"
#include "Base/ShaderCompilerCache.h"
#include "Base/ShaderCompilerHelper.h"
#include "Base/StaticConstantBufferPool.h"
#include "GLTF/GltfPbrPass.h"
#include "GLTF/GltfBBoxPass.h"
#include "GLTF/GltfDepthPass.h"
#include "GLTF/GltfMotionVectorsPass.h"
#include "Misc/Misc.h"
#include "Misc/Error.h"
#include "Misc/Camera.h"
#include "PostProc/TAA.h"
#include "PostProc/Bloom.h"
#include "PostProc/BlurPS.h"
#include "PostProc/SkyDome.h"
#include "PostProc/SkyDomeProc.h"
#include "PostProc/PostProcCS.h"
#include "PostProc/ToneMapping.h"
#include "PostProc/ToneMappingCS.h"
#include "PostProc/ColorConversionPS.h"
#include "PostProc/DownSamplePS.h"
#include "PostProc/ShadowResolvePass.h"
#include "Widgets/wireframe.h"
#define API_DX12
using namespace CAULDRON_DX12;

@ -0,0 +1,121 @@
# This file is part of the FidelityFX SDK.
#
# Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
set(sources
UpscaleContext.cpp
UpscaleContext.h
UpscaleContext_Spatial.cpp
UpscaleContext_Spatial.h
UpscaleContext_FSR2_API.cpp
UpscaleContext_FSR2_API.h
GPUFrameRateLimiter.cpp
GPUFrameRateLimiter.h
ShaderCompiler.cpp
ShaderCompiler.h
FSR2Sample.cpp
FSR2Sample.h
Renderer.cpp
Renderer.h
stdafx.cpp
stdafx.h
UI.cpp
UI.h
dpiawarescaling.manifest)
set(fsr1_shaders_src
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_core.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_common_types.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_core_cpu.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_core_gpu_common.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_core_gpu_common_half.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_core_portability.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_core_hlsl.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_fsr1.h)
set(spd_shaders_src
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_core.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_common_types.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_core_cpu.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_core_gpu_common.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_core_gpu_common_half.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_core_portability.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_core_hlsl.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_spd.h)
set(fsr2_shaders_src
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_fsr2_accumulate.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_fsr2_callbacks_glsl.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_fsr2_common.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_fsr2_compute_luminance_pyramid.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_fsr2_depth_clip.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_core.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_common_types.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_core_cpu.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_core_gpu_common.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_core_gpu_common_half.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_core_portability.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_core_glsl.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_fsr1.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_spd.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_fsr2_common.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_fsr2_lock.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_fsr2_postprocess_lock_status.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_fsr2_prepare_input_color.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_fsr2_reconstruct_dilated_velocity_and_previous_depth.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_fsr2_reproject.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_fsr2_sample.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_fsr2_upsample.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_fsr2_rcas.h
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_fsr2_autogen_reactive_pass.glsl
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_fsr2_accumulate_pass.glsl
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_fsr2_compute_luminance_pyramid_pass.glsl
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_fsr2_depth_clip_pass.glsl
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_fsr2_lock_pass.glsl
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_fsr2_prepare_input_color_pass.glsl
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_fsr2_reconstruct_previous_depth_pass.glsl
${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api/shaders/ffx_fsr2_rcas_pass.glsl)
set(particle_shaders_src
${CMAKE_CURRENT_SOURCE_DIR}/GPUFrameRateLimiter.hlsl
${CMAKE_CURRENT_SOURCE_DIR}/DebugBlit.hlsl
${CMAKE_CURRENT_SOURCE_DIR}/UpscaleSpatial.hlsl
${CMAKE_CURRENT_SOURCE_DIR}/FSRPass.hlsl)
set(APP_ICON_GPUOPEN "${CMAKE_CURRENT_SOURCE_DIR}/../common/GpuOpenIcon.rc")
source_group("sources" FILES ${sources})
source_group("spatial_shaders" FILES ${fsr1_shaders_src})
source_group("fsr2_shaders" FILES ${fsr2_shaders_src})
source_group("sample_shaders" FILES ${sample_shaders_src})
copyCommand("${spd_shaders_src}" ${CMAKE_HOME_DIRECTORY}/bin/ShaderLibVK)
copyCommand("${fsr1_shaders_src}" ${CMAKE_HOME_DIRECTORY}/bin/ShaderLibVK)
copyCommand("${fsr2_shaders_src}" ${CMAKE_HOME_DIRECTORY}/bin/ShaderLibVK)
copyCommand("${particle_shaders_src}" ${CMAKE_HOME_DIRECTORY}/bin/ShaderLibVK)
add_executable(FSR2_Sample_VK WIN32 ${sources} ${fsr2_src} ${sample_shaders_src} ${fsr1_shaders_src} ${fsr2_shaders_src} ${particle_shaders_src} ${spd_shaders_src} ${common} ${APP_ICON_GPUOPEN})
target_compile_definitions(FSR2_Sample_VK PRIVATE $<$<CONFIG:RelWithDebInfo>:FSR2_DEBUG_SHADERS=1>)
target_link_libraries(FSR2_Sample_VK LINK_PUBLIC FSR2_Sample_Common Cauldron_VK ImGUI d3dcompiler Vulkan::Vulkan ffx_fsr2_api_x64 ffx_fsr2_api_vk_x64) # ffx_fsr2_api_x64
target_include_directories(FSR2_Sample_VK PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../ffx-fsr2-api ${CMAKE_CURRENT_SOURCE_DIR}/../../libs)
target_link_directories(FSR2_Sample_VK PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../../libs)
set_target_properties(FSR2_Sample_VK PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_HOME_DIRECTORY}/bin" DEBUG_POSTFIX "d")
set_source_files_properties(${Shaders_src} PROPERTIES VS_TOOL_OVERRIDE "Text")

@ -0,0 +1,48 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
[[vk::binding(0)]] Texture2D<float4> r_in : register(t0);
[[vk::binding(1)]] RWTexture2D<float4> rw_out : register(u0);
[[vk::binding(2)]] cbuffer cbBlit0 : register(b0)
{
int g_outChannelRed;
int g_outChannelGreen;
int g_outChannelBlue;
float2 outChannelRedMinMax;
float2 outChannelGreenMinMax;
float2 outChannelBlueMinMax;
};
[numthreads(8, 8, 1)]
void CS(uint3 globalID : SV_DispatchThreadID)
{
float4 srcColor = r_in[globalID.xy];
// remap channels
float4 dstColor = float4(srcColor[g_outChannelRed], srcColor[g_outChannelGreen], srcColor[g_outChannelBlue], 1.f);
// apply offset and scale
dstColor.rgb -= float3(outChannelRedMinMax.x, outChannelGreenMinMax.x, outChannelBlueMinMax.x);
dstColor.rgb /= float3(outChannelRedMinMax.y - outChannelRedMinMax.x, outChannelGreenMinMax.y - outChannelGreenMinMax.x, outChannelBlueMinMax.y - outChannelBlueMinMax.x);
rw_out[globalID.xy] = float4(dstColor);
}

@ -0,0 +1,798 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "stdafx.h"
#include <intrin.h>
#include "FSR2Sample.h"
#define USE_INVERTED_INFINITE_PROJECTION 1
static constexpr float MagnifierBorderColor_Locked[3] = { 0.002f, 0.72f, 0.0f };
static constexpr float MagnifierBorderColor_Free[3] = { 0.72f, 0.002f, 0.0f };
//static constexpr float AMD_PI = 3.1415926535897932384626433832795f;
//static constexpr float AMD_PI_OVER_2 = 1.5707963267948966192313216916398f;
//static constexpr float AMD_PI_OVER_4 = 0.78539816339744830961566084581988f;
FSR2Sample::FSR2Sample(LPCSTR name) : FrameworkWindows(name)
{
m_time = 0;
m_bPlay = true;
m_pGltfLoader = NULL;
}
//--------------------------------------------------------------------------------------
//
// OnParseCommandLine
//
//--------------------------------------------------------------------------------------
void FSR2Sample::OnParseCommandLine(LPSTR lpCmdLine, uint32_t* pWidth, uint32_t* pHeight)
{
// set some default values
*pWidth = 1920;
*pHeight = 1080;
m_activeScene = 0; //load the first one by default
m_VsyncEnabled = false;
m_bIsBenchmarking = false;
m_fontSize = 13.f; // default value overridden by a json file if available
m_isCpuValidationLayerEnabled = false;
m_isGpuValidationLayerEnabled = false;
m_activeCamera = 1;
m_stablePowerState = false;
//read globals
auto process = [&](json jData)
{
*pWidth = jData.value("width", *pWidth);
*pHeight = jData.value("height", *pHeight);
m_fullscreenMode = jData.value("presentationMode", m_fullscreenMode);
m_activeScene = jData.value("activeScene", m_activeScene);
m_activeCamera = jData.value("activeCamera", m_activeCamera);
m_isCpuValidationLayerEnabled = jData.value("CpuValidationLayerEnabled", m_isCpuValidationLayerEnabled);
m_isGpuValidationLayerEnabled = jData.value("GpuValidationLayerEnabled", m_isGpuValidationLayerEnabled);
m_VsyncEnabled = jData.value("vsync", m_VsyncEnabled);
m_FreesyncHDROptionEnabled = jData.value("FreesyncHDROptionEnabled", m_FreesyncHDROptionEnabled);
m_bIsBenchmarking = jData.value("benchmark", m_bIsBenchmarking);
m_stablePowerState = jData.value("stablePowerState", m_stablePowerState);
m_fontSize = jData.value("fontsize", m_fontSize);
};
// read config file
//
{
std::ifstream f("FSR2_Sample.json");
if (!f)
{
MessageBox(NULL, "Config file not found!\n", "Cauldron Panic!", MB_ICONERROR);
exit(0);
}
try
{
f >> m_jsonConfigFile;
}
catch (json::parse_error)
{
MessageBox(NULL, "Error parsing GLTFSample.json!\n", "Cauldron Panic!", MB_ICONERROR);
exit(0);
}
}
json globals = m_jsonConfigFile["globals"];
process(globals);
// read json globals from commandline (and override values from config file)
//
try
{
if (strlen(lpCmdLine) > 0)
{
auto j3 = json::parse(lpCmdLine);
process(j3);
}
}
catch (json::parse_error)
{
Trace("Error parsing commandline\n");
exit(0);
}
// get the list of scenes
for (const auto& scene : m_jsonConfigFile["scenes"])
m_sceneNames.push_back(scene["name"]);
}
//--------------------------------------------------------------------------------------
//
// OnCreate
//
//--------------------------------------------------------------------------------------
void FSR2Sample::OnCreate()
{
//init the shader compiler
InitDirectXCompiler();
CreateShaderCache();
// Create a instance of the renderer and initialize it, we need to do that for each GPU
m_pRenderer = new Renderer();
#if USE_INVERTED_INFINITE_PROJECTION
m_pRenderer->OnCreate(&m_device, &m_swapChain, m_fontSize, true);
#else
m_pRenderer->OnCreate(&m_device, &m_swapChain, m_fontSize, false);
#endif
// init GUI (non gfx stuff)
ImGUI_Init((void*)m_windowHwnd);
m_UIState.Initialize();
OnResize();
OnUpdateDisplay();
// Init Camera, looking at the origin
m_UIState.camera.LookAt(math::Vector4(0, 0, 5, 0), math::Vector4(0, 0, 0, 0));
}
//--------------------------------------------------------------------------------------
//
// OnDestroy
//
//--------------------------------------------------------------------------------------
void FSR2Sample::OnDestroy()
{
ImGUI_Shutdown();
m_device.GPUFlush();
m_pRenderer->UnloadScene();
m_pRenderer->OnDestroyWindowSizeDependentResources();
m_pRenderer->OnDestroy();
delete m_pRenderer;
//shut down the shader compiler
DestroyShaderCache(&m_device);
if (m_pGltfLoader)
{
delete m_pGltfLoader;
m_pGltfLoader = NULL;
}
}
//--------------------------------------------------------------------------------------
//
// OnEvent, win32 sends us events and we forward them to ImGUI
//
//--------------------------------------------------------------------------------------
bool FSR2Sample::OnEvent(MSG msg)
{
if (ImGUI_WndProcHandler(msg.hwnd, msg.message, msg.wParam, msg.lParam))
return true;
// handle function keys (F1, F2...) here, rest of the input is handled
// by imGUI later in HandleInput() function
const WPARAM& KeyPressed = msg.wParam;
switch (msg.message)
{
case WM_KEYUP:
case WM_SYSKEYUP:
/* WINDOW TOGGLES */
if (KeyPressed == VK_F11) m_UIState.bShowControlsWindow ^= 1;
if (KeyPressed == VK_F12) m_UIState.bShowProfilerWindow ^= 1;
if (KeyPressed == VK_F1) {
// Native
m_SavedUiValues[m_UIState.m_nUpscaleType].scaleMode = m_nUpscaleMode = UPSCALE_QUALITY_MODE_NONE;
m_SavedUiValues[m_UIState.m_nUpscaleType].upscaleRatio = m_fUpscaleRatio = 1.f;
m_SavedUiValues[m_UIState.m_nUpscaleType].mipBias = m_UIState.mipBias = mipBias[UPSCALE_QUALITY_MODE_NONE];
m_bUpscaleChanged = true;
}
if (KeyPressed == VK_F2) {
// Ultra Quality
m_SavedUiValues[m_UIState.m_nUpscaleType].scaleMode = m_nUpscaleMode = UPSCALE_QUALITY_MODE_ULTRA_QUALITY;
m_SavedUiValues[m_UIState.m_nUpscaleType].mipBias = m_UIState.mipBias = mipBias[UPSCALE_QUALITY_MODE_ULTRA_QUALITY];
m_bUpscaleChanged = true;
}
if (KeyPressed == VK_F3) {
// Quality
m_SavedUiValues[m_UIState.m_nUpscaleType].scaleMode = m_nUpscaleMode = UPSCALE_QUALITY_MODE_QUALITY;
m_SavedUiValues[m_UIState.m_nUpscaleType].mipBias = m_UIState.mipBias = mipBias[UPSCALE_QUALITY_MODE_QUALITY];
m_bUpscaleChanged = true;
}
if (KeyPressed == VK_F4) {
// Balanced
m_SavedUiValues[m_UIState.m_nUpscaleType].scaleMode = m_nUpscaleMode = UPSCALE_QUALITY_MODE_BALANCE;
m_SavedUiValues[m_UIState.m_nUpscaleType].mipBias = m_UIState.mipBias = mipBias[UPSCALE_QUALITY_MODE_BALANCE];
m_bUpscaleChanged = true;
}
if (KeyPressed == VK_F5) {
// Performance
m_SavedUiValues[m_UIState.m_nUpscaleType].scaleMode = m_nUpscaleMode = UPSCALE_QUALITY_MODE_PERFORMANCE;
m_SavedUiValues[m_UIState.m_nUpscaleType].mipBias = m_UIState.mipBias = mipBias[UPSCALE_QUALITY_MODE_PERFORMANCE];
m_bUpscaleChanged = true;
}
if (KeyPressed == VK_F6) {
// Ultra Performance
m_SavedUiValues[m_UIState.m_nUpscaleType].scaleMode = m_nUpscaleMode = UPSCALE_QUALITY_MODE_ULTRA_PERFORMANCE;
m_SavedUiValues[m_UIState.m_nUpscaleType].mipBias = m_UIState.mipBias = mipBias[UPSCALE_QUALITY_MODE_ULTRA_PERFORMANCE];
m_bUpscaleChanged = true;
}
break;
}
return true;
}
//--------------------------------------------------------------------------------------
//
// OnResize
//
//--------------------------------------------------------------------------------------
void FSR2Sample::OnResize(bool forceResizeRender)
{
bool resizeDisplay = forceResizeRender ||
(m_UIState.displayWidth != m_Width || m_UIState.displayHeight != m_Height);
m_UIState.displayWidth = m_Width;
m_UIState.displayHeight = m_Height;
RefreshRenderResolution();
bool resizeRender = forceResizeRender ||
(m_UIState.renderWidth != m_renderWidth || m_UIState.renderHeight != m_renderHeight);
m_renderWidth = m_UIState.renderWidth;
m_renderHeight = m_UIState.renderHeight;
if (resizeDisplay || resizeRender)
{
// Flush GPU
m_device.GPUFlush();
}
if (resizeDisplay || resizeRender)
{
if (m_pRenderer != NULL)
{
m_pRenderer->OnDestroyWindowSizeDependentResources();
}
// if resizing but not minimizing the recreate it with the new size
//
if (m_UIState.displayWidth > 0 && m_UIState.displayHeight > 0)
{
if (m_pRenderer != NULL)
{
m_UIState.bReset = true;
m_pRenderer->OnCreateWindowSizeDependentResources(&m_swapChain, &m_UIState);
}
}
}
float aspect = m_UIState.renderWidth * 1.f / m_UIState.renderHeight;
#if USE_INVERTED_INFINITE_PROJECTION
m_UIState.camera.SetFov(AMD_PI_OVER_2 / aspect, aspect, 0.1f);
#else
m_UIState.camera.SetFov(AMD_PI_OVER_2 / aspect, aspect, 0.1f, 1000.0f);
#endif
}
//--------------------------------------------------------------------------------------
//
// UpdateDisplay
//
//--------------------------------------------------------------------------------------
void FSR2Sample::OnUpdateDisplay()
{
// Destroy resources (if we are not minimized)
if (m_pRenderer)
{
m_pRenderer->OnUpdateDisplayDependentResources(&m_swapChain);
}
}
//--------------------------------------------------------------------------------------
//
// LoadScene
//
//--------------------------------------------------------------------------------------
void FSR2Sample::LoadScene(int sceneIndex)
{
json scene = m_jsonConfigFile["scenes"][sceneIndex];
// release everything and load the GLTF, just the light json data, the rest (textures and geometry) will be done in the main loop
if (m_pGltfLoader != NULL)
{
m_pRenderer->UnloadScene();
m_pRenderer->OnDestroyWindowSizeDependentResources();
m_pRenderer->OnDestroy();
m_pGltfLoader->Unload();
// Make sure the actual resolution is updated no matter previous render size
RefreshRenderResolution();
#if USE_INVERTED_INFINITE_PROJECTION
m_pRenderer->OnCreate(&m_device, &m_swapChain, m_fontSize, true);
#else
m_pRenderer->OnCreate(&m_device, &m_swapChain, m_fontSize, false);
#endif
m_pRenderer->OnCreateWindowSizeDependentResources(&m_swapChain, &m_UIState);
}
delete(m_pGltfLoader);
m_pGltfLoader = new GLTFCommon();
if (m_pGltfLoader->Load(scene["directory"], scene["filename"]) == false)
{
MessageBox(NULL, "The selected model couldn't be found, please check the documentation", "Cauldron Panic!", MB_ICONERROR);
exit(0);
}
// Load the UI settings, and also some defaults cameras and lights, in case the GLTF has none
{
#define LOAD(j, key, val) val = j.value(key, val)
// global settings
LOAD(scene, "toneMapper", m_UIState.SelectedTonemapperIndex);
LOAD(scene, "skyDomeType", m_UIState.SelectedSkydomeTypeIndex);
LOAD(scene, "exposure", m_UIState.Exposure);
LOAD(scene, "iblFactor", m_UIState.IBLFactor);
LOAD(scene, "emmisiveFactor", m_UIState.EmissiveFactor);
LOAD(scene, "skyDomeType", m_UIState.SelectedSkydomeTypeIndex);
// Add a default light in case there are none
if (m_pGltfLoader->m_lights.size() == 0)
{
if (scene["name"] == std::string("Sponza"))
{
{
tfNode n;
n.m_transform.LookAt(math::Vector4(20, 100, -10, 0), math::Vector4(0, 0, 0, 0));
tfLight l;
l.m_type = tfLight::LIGHT_DIRECTIONAL;
l.m_intensity = scene.value("intensity", 1.0f);
l.m_color = math::Vector4(1.0f, 1.0f, 0.8f, 0.0f);
l.m_shadowResolution = 1024;
m_pGltfLoader->AddLight(n, l);
}
{
tfNode n;
n.m_transform.LookAt(math::Vector4(-2, 10, 1, 0), math::Vector4(0, 0, 0, 0));
tfLight l;
l.m_type = tfLight::LIGHT_SPOTLIGHT;
l.m_intensity = scene.value("intensity", 1.0f);
l.m_color = math::Vector4(1.0f, 1.0f, 1.0f, 0.0f);
l.m_range = 15;
l.m_outerConeAngle = AMD_PI_OVER_4;
l.m_innerConeAngle = AMD_PI_OVER_4 * 0.9f;
l.m_shadowResolution = 1024;
m_pGltfLoader->AddLight(n, l);
}
{
tfNode n;
n.m_transform.m_translation = math::Vector4(-13.f, 1.9f, 0.f, 0.f);
tfLight l;
l.m_type = tfLight::LIGHT_POINTLIGHT;
l.m_intensity = scene.value("intensity", 1.0f) * 0.25f;
l.m_color = math::Vector4(1.0f, 0.95f, 0.8f, 0.0f);
l.m_range = 4;
m_pGltfLoader->AddLight(n, l);
}
{
tfNode n;
n.m_transform.m_translation = math::Vector4(13.f, 1.9f, 0.f, 0.f);
tfLight l;
l.m_type = tfLight::LIGHT_POINTLIGHT;
l.m_intensity = scene.value("intensity", 1.0f) * 0.25f;
l.m_color = math::Vector4(1.0f, 0.95f, 0.8f, 0.0f);
l.m_range = 10;
m_pGltfLoader->AddLight(n, l);
}
}
else
{
tfNode n;
n.m_transform.LookAt(PolarToVector(AMD_PI_OVER_2, 0.58f) * 3.5f, math::Vector4(0, 0, 0, 0));
tfLight l;
l.m_type = tfLight::LIGHT_SPOTLIGHT;
l.m_intensity = scene.value("intensity", 1.0f);
l.m_color = math::Vector4(1.0f, 1.0f, 1.0f, 0.0f);
l.m_range = 15;
l.m_outerConeAngle = AMD_PI_OVER_4;
l.m_innerConeAngle = AMD_PI_OVER_4 * 0.9f;
l.m_shadowResolution = 1024;
m_pGltfLoader->AddLight(n, l);
}
}
// Allocate shadow information (if any)
m_pRenderer->AllocateShadowMaps(m_pGltfLoader);
// set default camera
json camera = scene["camera"];
m_activeCamera = scene.value("activeCamera", m_activeCamera);
math::Vector4 from = GetVector(GetElementJsonArray(camera, "defaultFrom", { 0.0, 0.0, 10.0 }));
math::Vector4 to = GetVector(GetElementJsonArray(camera, "defaultTo", { 0.0, 0.0, 0.0 }));
m_UIState.camera.LookAt(from, to);
// set benchmarking state if enabled
if (m_bIsBenchmarking)
{
std::string deviceName;
std::string driverVersion;
m_device.GetDeviceInfo(&deviceName, &driverVersion);
BenchmarkConfig(scene["BenchmarkSettings"], m_activeCamera, m_pGltfLoader, deviceName, driverVersion);
}
// indicate the mainloop we started loading a GLTF and it needs to load the rest (textures and geometry)
m_loadingScene = true;
}
}
//--------------------------------------------------------------------------------------
//
// OnUpdate
//
//--------------------------------------------------------------------------------------
void FSR2Sample::OnUpdate()
{
ImGuiIO& io = ImGui::GetIO();
//If the mouse was not used by the GUI then it's for the camera
if (io.WantCaptureMouse)
{
io.MouseDelta.x = 0;
io.MouseDelta.y = 0;
io.MouseWheel = 0;
}
// Update Camera
UpdateCamera(m_UIState.camera, io);
// Keyboard & Mouse
HandleInput(io);
// Animation Update
if (m_bPlay)
m_time += (float)m_deltaTime / 1000.0f; // animation time in seconds
if (m_pGltfLoader)
{
for (int i = 0; i < 32; ++i)
m_pGltfLoader->SetAnimationTime(i, m_time);
if (m_UIState.m_baloonID >= 0) {
std::vector<tfNode>& pNodes = m_pGltfLoader->m_nodes;
math::Matrix4* pNodesMatrices = m_pGltfLoader->m_animatedMats.data();
if (!m_UIState.m_baloonInit) {
m_pGltfLoader->TransformScene(0, math::Matrix4::identity());
m_UIState.m_InitBaloonTransform = pNodesMatrices[m_UIState.m_baloonID];
m_UIState.m_CurBaloonTransform = pNodesMatrices[m_UIState.m_baloonID];
m_UIState.m_baloonInit = true;
}
float y = std::sin(m_time) * m_UIState.m_BaloonAmplitude;
if (m_UIState.m_bBaloonAttachToCamera) {
Vectormath::Vector3 eye = m_UIState.camera.GetPosition().getXYZ() + m_UIState.camera.GetDirection().getXYZ() * m_UIState.baloon_offset_z;
Vectormath::Vector3 look = m_UIState.camera.GetPosition().getXYZ() + m_UIState.camera.GetDirection().getXYZ() * -2.0f;
m_UIState.baloon_pos = m_UIState.baloon_pos + (eye - m_UIState.baloon_pos) * (1.0f - std::expf(50.0f * -(float)m_deltaTime / 1000.0f));
m_UIState.baloon_tip_pos = m_UIState.baloon_tip_pos + (look - m_UIState.baloon_tip_pos) * (1.0f - std::expf(50.0f * -(float)m_deltaTime / 1000.0f));
m_UIState.m_CurBaloonTransform = Vectormath::inverse(Vectormath::Matrix4::lookAt(Vectormath::Point3(m_UIState.baloon_pos), Vectormath::Point3(m_UIState.baloon_tip_pos), Vectormath::Vector3(0.0f, 1.0f, 0.0f))) *
Vectormath::Matrix4::translation(Vectormath::Vector3(m_UIState.baloon_offset_x, m_UIState.baloon_offset_y, 0.0f)) * //
Vectormath::Matrix4::rotation(-3.141592f / 2.0f, Vectormath::Vector3(1.0f, 0.0f, 0.0f)) * //
Vectormath::Matrix4::rotation(-3.141592f / 2.0f, Vectormath::Vector3(0.0f, 0.0f, 1.0f)) * //
Vectormath::Matrix4::scale(Vectormath::Vector3(m_UIState.baloon_offset_scale, m_UIState.baloon_offset_scale, m_UIState.baloon_offset_scale));
}
pNodesMatrices[m_UIState.m_baloonID] = Vectormath::Matrix4::translation(Vectormath::Vector3(0.0f, y, 0.0f)) * m_UIState.m_CurBaloonTransform;
}
m_pGltfLoader->TransformScene(0, math::Matrix4::identity());
}
m_UIState.deltaTime = m_deltaTime;
// frame rate limiter
if (m_UIState.bEnableFrameRateLimiter && !m_UIState.bUseGPUFrameRateLimiter)
{
static double lastFrameTime = MillisecondsNow();
double timeNow = MillisecondsNow();
double deltaTime = timeNow - lastFrameTime;
if (deltaTime < m_UIState.targetFrameTime)
{
Sleep(DWORD(m_UIState.targetFrameTime - deltaTime));
timeNow += m_UIState.targetFrameTime - deltaTime;
}
lastFrameTime = timeNow;
}
}
void FSR2Sample::HandleInput(const ImGuiIO& io)
{
auto fnIsKeyTriggered = [&io](char key) { return io.KeysDown[key] && io.KeysDownDuration[key] == 0.0f; };
// Handle Keyboard/Mouse input here
/* MAGNIFIER CONTROLS */
if (fnIsKeyTriggered('L')) m_UIState.ToggleMagnifierLock();
if (fnIsKeyTriggered('M') || io.MouseClicked[2]) m_UIState.bUseMagnifier ^= 1; // middle mouse / M key toggles magnifier
// save current state to saved
m_SavedUiValues[m_UIState.m_nUpscaleType].scaleMode = m_nUpscaleMode;
m_SavedUiValues[m_UIState.m_nUpscaleType].upscaleRatio = m_fUpscaleRatio;
m_SavedUiValues[m_UIState.m_nUpscaleType].mipBias = m_UIState.mipBias;
m_SavedUiValues[m_UIState.m_nUpscaleType].useSharpen = m_UIState.bUseRcas;
m_SavedUiValues[m_UIState.m_nUpscaleType].sharpness = m_UIState.sharpening;
m_SavedUiValues[m_UIState.m_nUpscaleType].useTAA = m_UIState.bUseTAA;
if (io.KeyCtrl == true)
{
if (fnIsKeyTriggered('1'))
{
m_bUpscaleChanged = true;
m_UIState.m_nUpscaleType = UPSCALE_TYPE_POINT;
m_UIState.bDynamicRes = false;
}
else if (fnIsKeyTriggered('2'))
{
m_bUpscaleChanged = true;
m_UIState.m_nUpscaleType = UPSCALE_TYPE_BILINEAR;
m_UIState.bDynamicRes = false;
}
else if (fnIsKeyTriggered('3'))
{
m_bUpscaleChanged = true;
m_UIState.m_nUpscaleType = UPSCALE_TYPE_BICUBIC;
m_UIState.bDynamicRes = false;
}
else if (fnIsKeyTriggered('4'))
{
m_bUpscaleChanged = true;
m_UIState.m_nUpscaleType = UPSCALE_TYPE_FSR_1_0;
m_UIState.bDynamicRes = false;
}
else if (fnIsKeyTriggered('5'))
{
m_bUpscaleChanged = true;
m_UIState.m_nUpscaleType = UPSCALE_TYPE_FSR_2_0;
}
else if (fnIsKeyTriggered('0'))
{
m_bUpscaleChanged = true;
m_UIState.m_nUpscaleType = UPSCALE_TYPE_NATIVE;
m_UIState.bDynamicRes = false;
// force reset values for native
m_SavedUiValues[m_UIState.m_nUpscaleType].scaleMode = UPSCALE_QUALITY_MODE_NONE;
m_SavedUiValues[m_UIState.m_nUpscaleType].upscaleRatio = 1.f;
m_SavedUiValues[m_UIState.m_nUpscaleType].mipBias = 0.f;
}
}
m_nUpscaleMode = m_SavedUiValues[m_UIState.m_nUpscaleType].scaleMode;
m_fUpscaleRatio = m_SavedUiValues[m_UIState.m_nUpscaleType].upscaleRatio;
m_UIState.mipBias = m_SavedUiValues[m_UIState.m_nUpscaleType].mipBias;
m_UIState.bUseRcas = m_SavedUiValues[m_UIState.m_nUpscaleType].useSharpen;
m_UIState.sharpening = m_SavedUiValues[m_UIState.m_nUpscaleType].sharpness;
m_UIState.bUseTAA = m_SavedUiValues[m_UIState.m_nUpscaleType].useTAA;
if (m_bUpscaleChanged)
{
OnResize(true);
}
if (io.MouseClicked[1] && m_UIState.bUseMagnifier) // right mouse click
m_UIState.ToggleMagnifierLock();
}
void FSR2Sample::UpdateCamera(Camera& cam, const ImGuiIO& io)
{
float yaw = cam.GetYaw();
float pitch = cam.GetPitch();
float distance = cam.GetDistance();
cam.UpdatePreviousMatrices(); // set previous view matrix
// Sets Camera based on UI selection (WASD, Orbit or any of the GLTF cameras)
if ((io.KeyCtrl == false) && (io.MouseDown[0] == true))
{
yaw -= io.MouseDelta.x / 100.f;
pitch += io.MouseDelta.y / 100.f;
}
// Choose camera movement depending on setting
if (m_activeCamera == 0)
{
// If nothing has changed, don't calculate an update (we are getting micro changes in view causing bugs)
if (!io.MouseWheel && (!io.MouseDown[0] || (!io.MouseDelta.x && !io.MouseDelta.y)))
return;
// Orbiting
distance -= (float)io.MouseWheel / 3.0f;
distance = std::max<float>(distance, 0.1f);
bool panning = (io.KeyCtrl == true) && (io.MouseDown[0] == true);
cam.UpdateCameraPolar(yaw, pitch,
panning ? -io.MouseDelta.x / 100.0f : 0.0f,
panning ? io.MouseDelta.y / 100.0f : 0.0f,
distance);
}
else if (m_activeCamera == 1)
{
// WASD
cam.SetSpeed(io.KeyCtrl ? 1.0f : 0.01f);
// prevent camera inertia for more precise movement
if (!m_UIState.m_bCameraInertia)
{
bool notW = !io.KeysDown['W'];
bool notA = !io.KeysDown['A'];
bool notS = !io.KeysDown['S'];
bool notD = !io.KeysDown['D'];
bool notE = !io.KeysDown['E'];
bool notQ = !io.KeysDown['Q'];
if (notW && notA && notS && notD && notQ && notE)
cam.SetSpeed(0.0f);
}
cam.UpdateCameraWASD(yaw, pitch, io.KeysDown, io.DeltaTime);
if (m_UIState.m_bHeadBobbing)
{
static math::Vector4 bob = math::Vector4(0.f);
math::Vector4 eyePos = cam.GetPosition();
// remove old headbob
eyePos -= bob;
// compute new headbob
math::Matrix4 view = cam.GetView();
math::Vector4 side = view.getRow(0) * 0.02f;
bob = side * sin(5 * m_time);
bob.setY(bob.getY() + 0.04f * sin(5 * 2 * m_time));
// apply new headbob
eyePos += bob;
view.setCol3(eyePos);
math::Vector4 viewDir = -cam.GetDirection();
view.setCol2(viewDir);
cam.SetMatrix(view);
}
}
else if (m_activeCamera > 1)
{
// Use a camera from the GLTF
m_pGltfLoader->GetCamera(m_activeCamera - 2, &cam);
}
}
//--------------------------------------------------------------------------------------
//
// OnRender, updates the state from the UI, animates, transforms and renders the scene
//
//--------------------------------------------------------------------------------------
void FSR2Sample::OnRender()
{
// Do any start of frame necessities
BeginFrame();
ImGUI_UpdateIO(m_UIState.displayWidth, m_UIState.displayHeight);
if (m_UIState.bDynamicRes)
{
// Quick dynamic resolution test: generate a resolution that fits in between the defined upscale factor
// and native resolution.
float sinTime = float(sin(MillisecondsNow() / 1000.0) + 1.0f) / 2.0f;
float thisFrameUpscaleRatio = (1.0f / m_fUpscaleRatio) + (1.0f - 1.0f / m_fUpscaleRatio) * sinTime;
m_UIState.renderWidth = (uint32_t)((float)m_Width * thisFrameUpscaleRatio);
m_UIState.renderHeight = (uint32_t)((float)m_Height * thisFrameUpscaleRatio);
m_UIState.mipBias = log2f(float(m_UIState.renderWidth) / m_UIState.displayWidth) - 1.0f;
}
else
{
float thisFrameUpscaleRatio = 1.f / m_SavedUiValues[m_UIState.m_nUpscaleType].upscaleRatio;
m_UIState.renderWidth = (uint32_t)((float)m_Width * thisFrameUpscaleRatio);
m_UIState.renderHeight = (uint32_t)((float)m_Height * thisFrameUpscaleRatio);
m_UIState.mipBias = log2f(float(m_UIState.renderWidth) / m_UIState.displayWidth) - 1.0f;
}
ImGui::NewFrame();
if (m_loadingScene)
{
// the scene loads in chunks, that way we can show a progress bar
static int loadingStage = 0;
loadingStage = m_pRenderer->LoadScene(m_pGltfLoader, loadingStage);
if (loadingStage == 0)
{
m_time = 0;
m_loadingScene = false;
const json& j3 = m_pGltfLoader->j3;
if (j3.find("meshes") != j3.end()) {
const json& nodes = j3["nodes"];
for (uint32_t i = 0; i < nodes.size(); i++) {
const json& node = nodes[i];
std::string name = GetElementString(node, "name", "unnamed");
if (name == "baloon") {
m_UIState.m_baloonID = i;
}
}
}
}
}
else if (m_pGltfLoader && m_bIsBenchmarking)
{
// Benchmarking takes control of the time, and exits the app when the animation is done
std::vector<TimeStamp> timeStamps = m_pRenderer->GetTimingValues();
m_time = BenchmarkLoop(timeStamps, &m_UIState.camera, m_pRenderer->GetScreenshotFileName());
}
else
{
BuildUI(); // UI logic. Note that the rendering of the UI happens later.
OnUpdate(); // Update camera, handle keyboard/mouse input
}
// Do Render frame using AFR
m_pRenderer->OnRender(&m_UIState, m_UIState.camera, &m_swapChain);
// Framework will handle Present and some other end of frame logic
EndFrame();
}
//--------------------------------------------------------------------------------------
//
// WinMain
//
//--------------------------------------------------------------------------------------
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
LPCSTR Name = "FidelityFX Super Resolution 2.0";
// create new DX sample
return RunFramework(hInstance, lpCmdLine, nCmdShow, new FSR2Sample(Name));
}

@ -0,0 +1,174 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#pragma once
#include "base/FrameworkWindows.h"
#include "Renderer.h"
#include "UI.h"
//
// This is the main class, it manages the state of the sample and does all the high level work without touching the GPU directly.
// This class uses the GPU via the the SampleRenderer class. We would have a SampleRenderer instance for each GPU.
//
// This class takes care of:
//
// - loading a scene (just the CPU data)
// - updating the camera
// - keeping track of time
// - handling the keyboard
// - updating the animation
// - building the UI (but do not renders it)
// - uses the SampleRenderer to update all the state to the GPU and do the rendering
//
class FSR2Sample : public FrameworkWindows
{
public:
FSR2Sample(LPCSTR name);
void OnParseCommandLine(LPSTR lpCmdLine, uint32_t* pWidth, uint32_t* pHeight) override;
void OnCreate() override;
void OnDestroy() override;
void OnRender() override;
bool OnEvent(MSG msg) override;
void OnResize(bool forceRecreateResources = false) override;
void OnUpdateDisplay() override;
void BuildUI();
void LoadScene(int sceneIndex);
void OnUpdate();
void HandleInput(const ImGuiIO& io);
void UpdateCamera(Camera& cam, const ImGuiIO& io);
private:
bool m_bIsBenchmarking;
GLTFCommon* m_pGltfLoader = NULL;
bool m_loadingScene = false;
Renderer* m_pRenderer = NULL;
UIState m_UIState;
float m_fontSize;
uint32_t m_renderWidth = 0;
uint32_t m_renderHeight = 0;
float m_time; // Time accumulator in seconds, used for animation.
// json config file
json m_jsonConfigFile;
std::vector<std::string> m_sceneNames;
int m_activeScene;
int m_activeCamera;
bool m_bPlay;
bool m_bDisableHalf = false;
bool m_bEnableHalf = false;
bool m_bDisplayHalf = false;
bool UseSlowFallback(bool deviceSupport)
{
assert(!m_bEnableHalf || !m_bDisableHalf);
if (m_bEnableHalf)
return false;
else
if (m_bDisableHalf)
return true;
else
return !deviceSupport;
}
private:
void RefreshRenderResolution()
{
switch (m_nUpscaleMode)
{
case UPSCALE_QUALITY_MODE_NONE:
m_fUpscaleRatio = 1.0f;
break;
case UPSCALE_QUALITY_MODE_ULTRA_QUALITY:
m_fUpscaleRatio = 1.3f;
break;
case UPSCALE_QUALITY_MODE_QUALITY:
m_fUpscaleRatio = 1.5f;
break;
case UPSCALE_QUALITY_MODE_BALANCE:
m_fUpscaleRatio = 1.7f;
break;
case UPSCALE_QUALITY_MODE_PERFORMANCE:
m_fUpscaleRatio = 2.0f;
break;
case UPSCALE_QUALITY_MODE_ULTRA_PERFORMANCE:
m_fUpscaleRatio = 3.0f;
break;
default: break;
}
if (m_UIState.bDynamicRes)
{
m_UIState.renderWidth = uint32_t(m_UIState.displayWidth);
m_UIState.renderHeight = uint32_t(m_UIState.displayHeight);
}
else
{
m_UIState.renderWidth = uint32_t(m_UIState.displayWidth / m_fUpscaleRatio);
m_UIState.renderHeight = uint32_t(m_UIState.displayHeight / m_fUpscaleRatio);
}
m_bUpscaleChanged = false;
}
bool m_bUpscaleChanged = true;
float mipBias[UPSCALE_QUALITY_MODE_COUNT] = {
0.f + log2(1.f / 1.0f) - 1.0f + FLT_EPSILON, // -1.0 - UPSCALE_QUALITY_MODE_NONE
0.f + log2(1.f / 1.3f) - 1.0f + FLT_EPSILON, // -1.38f - UPSCALE_QUALITY_MODE_ULTRA_QUALITY
0.f + log2(1.f / 1.5f) - 1.0f + FLT_EPSILON, // -1.58f - UPSCALE_QUALITY_MODE_QUALITY
0.f + log2(1.f / 1.7f) - 1.0f + FLT_EPSILON, // -1.76f - UPSCALE_QUALITY_MODE_BALANCED
0.f + log2(1.f / 2.0f) - 1.0f + FLT_EPSILON, // -2.0f - UPSCALE_QUALITY_MODE_PERFORMANCE
0.f + log2(1.f / 3.0f) - 1.0f + FLT_EPSILON, // -2.58f - UPSCALE_QUALITY_MODE_ULTRA_PERFORMANCE
0.0f // 0.0f - UPSCALE_QUALITY_MODE_CUSTOM
};
UpscaleQualityMode m_nUpscaleMode = UPSCALE_QUALITY_MODE_PERFORMANCE;
float m_fUpscaleRatio = 2.0f;
bool m_bDynamicRes = false;
float m_fDynamicResMinPercentage = 1.0f;
struct
{
UpscaleQualityMode scaleMode;
float upscaleRatio;
float mipBias;
bool useSharpen;
float sharpness;
bool useTAA;
bool dynamicRes;
float dynamicResMinPercent;
} m_SavedUiValues[UPSCALE_TYPE_COUNT] = {
{ UPSCALE_QUALITY_MODE_PERFORMANCE, 2.0f, -2.f, true, 1.f, true, false, 1.0f}, // UPSCALE_TYPE_POINT
{ UPSCALE_QUALITY_MODE_PERFORMANCE, 2.0f, -2.f, true, 1.f, true, false, 1.0f}, // UPSCALE_TYPE_BILINEAR
{ UPSCALE_QUALITY_MODE_PERFORMANCE, 2.0f, -2.f, true, 1.f, true, false, 1.0f}, // UPSCALE_TYPE_BICUBIC
{ UPSCALE_QUALITY_MODE_PERFORMANCE, 2.0f, -2.f, true, 1.f, true, false, 1.0f}, // UPSCALE_TYPE_FSR_1_0 (w/ TAA)
{ UPSCALE_QUALITY_MODE_PERFORMANCE, 2.0f, -2.f, false, 1.f, true, false, 1.0f}, // UPSCALE_TYPE_FSR_2_0
{ UPSCALE_QUALITY_MODE_NONE, 1.0f, 0.f, false, 1.f, true, false, 1.0f}, // UPSCALE_TYPE_NATIVE
};
};

@ -0,0 +1,118 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
cbuffer cb : register(b0)
{
uint4 Const0;
uint4 Const1;
uint4 Const2;
uint4 Const3;
uint4 Sample;
};
#define A_GPU 1
#define A_HLSL 1
SamplerState samLinearClamp : register(s0);
#if SAMPLE_SLOW_FALLBACK
#include "ffx_a.h"
Texture2D InputTexture : register(t0);
RWTexture2D<float4> OutputTexture : register(u0);
#if SAMPLE_EASU
#define FSR_EASU_F 1
AF4 FsrEasuRF(AF2 p) { AF4 res = InputTexture.GatherRed(samLinearClamp, p, int2(0, 0)); return res; }
AF4 FsrEasuGF(AF2 p) { AF4 res = InputTexture.GatherGreen(samLinearClamp, p, int2(0, 0)); return res; }
AF4 FsrEasuBF(AF2 p) { AF4 res = InputTexture.GatherBlue(samLinearClamp, p, int2(0, 0)); return res; }
#endif
#if SAMPLE_RCAS
#define FSR_RCAS_F
AF4 FsrRcasLoadF(ASU2 p) { return InputTexture.Load(int3(ASU2(p), 0)); }
void FsrRcasInputF(inout FfxFloat32 r, inout FfxFloat32 g, inout FfxFloat32 b) {}
#endif
#else
#define A_HALF
#include "ffx_a.h"
Texture2D<AH4> InputTexture : register(t0);
RWTexture2D<AH4> OutputTexture : register(u0);
#if SAMPLE_EASU
#define FSR_EASU_H 1
AH4 FsrEasuRH(AF2 p) { AH4 res = InputTexture.GatherRed(samLinearClamp, p, int2(0, 0)); return res; }
AH4 FsrEasuGH(AF2 p) { AH4 res = InputTexture.GatherGreen(samLinearClamp, p, int2(0, 0)); return res; }
AH4 FsrEasuBH(AF2 p) { AH4 res = InputTexture.GatherBlue(samLinearClamp, p, int2(0, 0)); return res; }
#endif
#if SAMPLE_RCAS
#define FSR_RCAS_H
AH4 FsrRcasLoadH(ASW2 p) { return InputTexture.Load(ASW3(ASW2(p), 0)); }
void FsrRcasInputH(inout AH1 r,inout AH1 g,inout AH1 b){}
#endif
#endif
#include "ffx_fsr1.h"
void CurrFilter(int2 pos)
{
#if SAMPLE_BILINEAR
AF2 pp = (AF2(pos) * AF2_AU2(Const0.xy) + AF2_AU2(Const0.zw)) * AF2_AU2(Const1.xy) + AF2(0.5, -0.5) * AF2_AU2(Const1.zw);
OutputTexture[pos] = InputTexture.SampleLevel(samLinearClamp, pp, 0.0);
#endif
#if SAMPLE_EASU
#if SAMPLE_SLOW_FALLBACK
AF3 c;
FsrEasuF(c, pos, Const0, Const1, Const2, Const3);
OutputTexture[pos] = float4(c, 1);
#else
AH3 c;
FsrEasuH(c, pos, Const0, Const1, Const2, Const3);
OutputTexture[pos] = AH4(c, 1);
#endif
#endif
#if SAMPLE_RCAS
#if SAMPLE_SLOW_FALLBACK
AF3 c;
FsrRcasF(c.r, c.g, c.b, pos, Const0);
if (Sample.x == 1)
c *= c;
OutputTexture[pos] = float4(c, 1);
#else
AH3 c;
FsrRcasH(c.r, c.g, c.b, pos, Const0);
if( Sample.x == 1 )
c *= c;
OutputTexture[pos] = AH4(c, 1);
#endif
#endif
}
[numthreads(WIDTH, HEIGHT, DEPTH)]
void mainCS(uint3 LocalThreadId : SV_GroupThreadID, uint3 WorkGroupId : SV_GroupID, uint3 Dtid : SV_DispatchThreadID)
{
// Do remapping of local xy in workgroup for a more PS-like swizzle pattern.
AU2 gxy = ARmp8x8(LocalThreadId.x) + AU2(WorkGroupId.x << 4u, WorkGroupId.y << 4u);
CurrFilter(gxy);
gxy.x += 8u;
CurrFilter(gxy);
gxy.y += 8u;
CurrFilter(gxy);
gxy.x -= 8u;
CurrFilter(gxy);
}

@ -0,0 +1,173 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "GPUFrameRateLimiter.h"
static const UINT BufferLength = 32768 * 32;
void GPUFrameRateLimiter::OnCreate(Device* pDevice, DynamicBufferRing* pDynamicBufferRing, ResourceViewHeaps* pResourceViewHeaps)
{
m_pDevice = pDevice;
m_pResourceViewHeaps = pResourceViewHeaps;
{
std::vector<VkDescriptorSetLayoutBinding> layoutBindings(2);
layoutBindings[0].binding = 0;
layoutBindings[0].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
layoutBindings[0].descriptorCount = 1;
layoutBindings[0].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
layoutBindings[1].binding = 1;
layoutBindings[1].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
layoutBindings[1].descriptorCount = 1;
layoutBindings[1].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
pResourceViewHeaps->CreateDescriptorSetLayout(&layoutBindings, &m_dsLayout);
pResourceViewHeaps->AllocDescriptor(m_dsLayout, &m_ds);
}
m_pipeline.OnCreate(pDevice, "GPUFrameRateLimiter.hlsl", "CSMain", "-T cs_6_0", m_dsLayout, 32, 1, 1);
#ifdef USE_VMA
VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
bufferInfo.size = BufferLength * sizeof(float);
bufferInfo.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
VmaAllocationCreateInfo allocInfo = {};
allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
allocInfo.flags = VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT;
VkResult res = vmaCreateBuffer(pDevice->GetAllocator(), &bufferInfo, &allocInfo, &m_buffer, &m_bufferAlloc, nullptr);
assert(res == VK_SUCCESS);
SetResourceName(pDevice->GetDevice(), VK_OBJECT_TYPE_BUFFER, (uint64_t)m_buffer, "FrameTimeLimiter (sys mem)");
#else
// create the buffer, allocate it in SYSTEM memory, bind it and map it
VkBufferCreateInfo buf_info = {};
buf_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
buf_info.pNext = NULL;
buf_info.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
buf_info.size = BufferLength * sizeof(float);
buf_info.queueFamilyIndexCount = 0;
buf_info.pQueueFamilyIndices = NULL;
buf_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
buf_info.flags = 0;
res = vkCreateBuffer(m_pDevice->GetDevice(), &buf_info, NULL, &m_buffer);
assert(res == VK_SUCCESS);
// allocate the buffer in system memory
VkMemoryRequirements mem_reqs;
vkGetBufferMemoryRequirements(m_pDevice->GetDevice(), m_buffer, &mem_reqs);
VkMemoryAllocateInfo alloc_info = {};
alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
alloc_info.pNext = NULL;
alloc_info.memoryTypeIndex = VK_MEMORY_PROPERTY_DEVICE_LOCAL;
alloc_info.allocationSize = mem_reqs.size;
bool pass = memory_type_from_properties(m_pDevice->GetPhysicalDeviceMemoryProperties(), mem_reqs.memoryTypeBits,
VK_MEMORY_PROPERTY_DEVICE_LOCAL,
&alloc_info.memoryTypeIndex);
assert(pass && "No mappable, coherent memory");
res = vkAllocateMemory(m_pDevice->GetDevice(), &alloc_info, NULL, &m_deviceMemory);
assert(res == VK_SUCCESS);
// bind buffer
res = vkBindBufferMemory(m_pDevice->GetDevice(), m_buffer, m_deviceMemory, 0);
assert(res == VK_SUCCESS);
#endif
VkWriteDescriptorSet writeData;
VkDescriptorBufferInfo bufferDescriptorInfo;
bufferDescriptorInfo.buffer = m_buffer;
bufferDescriptorInfo.offset = 0;
bufferDescriptorInfo.range = BufferLength * sizeof(float);
writeData = {};
writeData.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writeData.dstSet = m_ds;
writeData.descriptorCount = 1;
writeData.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
writeData.pBufferInfo = &bufferDescriptorInfo;
writeData.dstBinding = 0;
writeData.dstArrayElement = 0;
vkUpdateDescriptorSets(pDevice->GetDevice(), 1, &writeData, 0, nullptr);
pDynamicBufferRing->SetDescriptorSet(1, sizeof(uint32_t), m_ds);
m_frameTimeHistoryCount = 0;
m_frameTimeHistorySum = 0;
}
void GPUFrameRateLimiter::OnDestroy()
{
m_pipeline.OnDestroy();
if (m_buffer != VK_NULL_HANDLE)
{
#ifdef USE_VMA
vmaDestroyBuffer(m_pDevice->GetAllocator(), m_buffer, m_bufferAlloc);
#else
vkFreeMemory(m_pDevice->GetDevice(), m_deviceMemory, NULL);
vkDestroyBuffer(m_pDevice->GetDevice(), m_buffer, NULL);
#endif
m_buffer = VK_NULL_HANDLE;
}
if (m_dsLayout != VK_NULL_HANDLE)
vkDestroyDescriptorSetLayout(m_pDevice->GetDevice(), m_dsLayout, nullptr);
}
void GPUFrameRateLimiter::Draw(VkCommandBuffer cmdBuf, DynamicBufferRing* pDynamicBufferRing, uint64_t lastFrameTimeMicrosecs, uint64_t targetFrameTimeMicrosecs)
{
constexpr double DampenFactor = 0.05;
constexpr double MaxTargetFrameTimeUs = 200'000.0; // 200ms 5fps to match CPU limiter UI.
constexpr double MinTargetFrameTimeUs = 50.0;
if (m_frameTimeHistoryCount >= _countof(m_frameTimeHistory))
{
m_frameTimeHistorySum -= m_frameTimeHistory[m_frameTimeHistoryCount % _countof(m_frameTimeHistory)];
}
m_frameTimeHistorySum += lastFrameTimeMicrosecs;
m_frameTimeHistory[m_frameTimeHistoryCount % _countof(m_frameTimeHistory)] = lastFrameTimeMicrosecs;
m_frameTimeHistoryCount++;
double recentFrameTimeAvg = double(m_frameTimeHistorySum) / min(m_frameTimeHistoryCount, _countof(m_frameTimeHistory));
double clampedTargetFrameTimeMs = max(min(double(targetFrameTimeMicrosecs), MaxTargetFrameTimeUs), MinTargetFrameTimeUs);
double deltaRatio = (recentFrameTimeAvg - clampedTargetFrameTimeMs) / clampedTargetFrameTimeMs;
m_overhead -= m_overhead * deltaRatio * DampenFactor;
m_overhead = min(max(1.0, m_overhead), 1000000.0);
uint32_t numLoops = uint32_t(m_overhead);
VkDescriptorBufferInfo dynamicUboInfo = pDynamicBufferRing->AllocConstantBuffer(sizeof(uint32_t), &numLoops);
m_pipeline.Draw(cmdBuf, &dynamicUboInfo, m_ds, BufferLength / 32, 1, 1);
}

@ -0,0 +1,58 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#pragma once
#include "stdafx.h"
#include "base/Texture.h"
#include "PostProc/PostProcCS.h"
class GPUFrameRateLimiter
{
public:
void OnCreate(Device* pDevice, DynamicBufferRing* pDynamicBufferRing, ResourceViewHeaps* pResourceViewHeaps);
void OnDestroy();
void Draw(VkCommandBuffer cmd, DynamicBufferRing* pDynamicBufferRing, uint64_t lastFrameTimeMs, uint64_t targetFrameTimeMs);
private:
Device* m_pDevice;
ResourceViewHeaps* m_pResourceViewHeaps;
PostProcCS m_pipeline;
VkBuffer m_buffer;
VkDescriptorSetLayout m_dsLayout;
VkDescriptorSet m_ds;
#ifdef USE_VMA
VmaAllocation m_bufferAlloc = VK_NULL_HANDLE;
VmaAllocation m_bufferAllocVid = VK_NULL_HANDLE;
#else
VkDeviceMemory m_deviceMemory = VK_NULL_HANDLE;;
VkDeviceMemory m_deviceMemoryVid = VK_NULL_HANDLE;;
#endif
double m_overhead = 1.0;
static const uint32_t FRAME_TIME_HISTORY_SAMPLES = 4;
uint64_t m_frameTimeHistory[FRAME_TIME_HISTORY_SAMPLES];
uint64_t m_frameTimeHistorySum = 0;
uint64_t m_frameTimeHistoryCount = 0;
};

@ -0,0 +1,39 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
[[vk::binding(0)]] RWStructuredBuffer<float> DataBuffer : register(u0);
[[vk::binding(1)]]
cbuffer cb : register(b0)
{
uint NumLoops;
}
[numthreads(32,1,1)]
void CSMain(uint dtID : SV_DispatchThreadID)
{
float tmp = DataBuffer[dtID];
for (uint i = 0; i < NumLoops; i++)
{
tmp = sin(tmp) + 1.5f;
}
DataBuffer[dtID] = tmp;
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,147 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#pragma once
#include "stdafx.h"
#include "base/GBuffer.h"
#include "PostProc/MagnifierPS.h"
#include "UpscaleContext.h"
#include "GPUFrameRateLimiter.h"
// We are queuing (backBufferCount + 0.5) frames, so we need to triple buffer the resources that get modified each frame
static const int backBufferCount = 3;
using namespace CAULDRON_VK;
struct UIState;
//
// Renderer class is responsible for rendering resources management and recording command buffers.
class Renderer
{
public:
void OnCreate(Device *pDevice, SwapChain *pSwapChain, float FontSize, bool bInvertedDepth = false);
void OnDestroy();
void OnCreateWindowSizeDependentResources(SwapChain *pSwapChain, UIState* pState);
void OnDestroyWindowSizeDependentResources();
void OnUpdateDisplayDependentResources(SwapChain *pSwapChain);
void OnUpdateLocalDimmingChangedResources(SwapChain *pSwapChain);
int LoadScene(GLTFCommon *pGLTFCommon, int Stage = 0);
void UnloadScene();
void AllocateShadowMaps(GLTFCommon* pGLTFCommon);
const std::vector<TimeStamp> &GetTimingValues() { return m_TimeStamps; }
std::string& GetScreenshotFileName() { return m_pScreenShotName; }
void OnRender(UIState* pState, const Camera& Cam, SwapChain* pSwapChain);
void BuildDevUI(UIState* pState);
private:
Device *m_pDevice;
uint32_t m_Width;
uint32_t m_Height;
bool m_HasTAA = false;
bool m_bInvertedDepth;
// Initialize helper classes
ResourceViewHeaps m_ResourceViewHeaps;
UploadHeap m_UploadHeap;
DynamicBufferRing m_ConstantBufferRing;
StaticBufferPool m_VidMemBufferPool;
StaticBufferPool m_SysMemBufferPool;
CommandListRing m_CommandListRing;
GPUTimestamps m_GPUTimer;
GPUFrameRateLimiter m_GpuFrameRateLimiter;
// GLTF passes
GltfPbrPass *m_GLTFPBR;
GltfBBoxPass *m_GLTFBBox;
GltfDepthPass *m_GLTFDepth;
GLTFTexturesAndBuffers *m_pGLTFTexturesAndBuffers;
// Effects
Bloom m_Bloom;
SkyDome m_SkyDome;
DownSamplePS m_DownSample;
SkyDomeProc m_SkyDomeProc;
ToneMapping m_ToneMappingPS;
ToneMappingCS m_ToneMappingCS;
ColorConversionPS m_ColorConversionPS;
MagnifierPS m_MagnifierPS;
bool m_bMagResourceReInit = false;
// Upscale
UpscaleContext* m_pUpscaleContext = nullptr;
VkRenderPass m_RenderPassDisplayOutput;
VkFramebuffer m_FramebufferDisplayOutput;
// GUI
ImGUI m_ImGUI;
// GBuffer and render passes
GBuffer m_GBuffer;
GBufferRenderPass m_RenderPassFullGBufferWithClear;
GBufferRenderPass m_RenderPassJustDepthAndHdr;
GBufferRenderPass m_RenderPassFullGBuffer;
Texture m_OpaqueTexture;
VkImageView m_OpaqueTextureSRV;
// Shadow maps
VkRenderPass m_Render_pass_shadow;
typedef struct {
Texture ShadowMap;
uint32_t ShadowIndex;
uint32_t ShadowResolution;
uint32_t LightIndex;
VkImageView ShadowDSV;
VkFramebuffer ShadowFrameBuffer;
} SceneShadowInfo;
std::vector<SceneShadowInfo> m_shadowMapPool;
std::vector< VkImageView> m_ShadowSRVPool;
Texture m_displayOutput;
VkImageView m_displayOutputSRV;
// Widgets
Wireframe m_Wireframe;
WireframeBox m_WireframeBox;
std::vector<TimeStamp> m_TimeStamps;
// Screen shot
std::string m_pScreenShotName = "";
// TODO: Implement!
//SaveTexture m_SaveTexture;
AsyncPool m_AsyncPool;
};

@ -0,0 +1,636 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "stdafx.h"
#include "UI.h"
#include "FSR2Sample.h"
#include "imgui.h"
#include "base/FrameworkWindows.h"
// To use the 'disabled UI state' functionality (ImGuiItemFlags_Disabled), include internal header
// https://github.com/ocornut/imgui/issues/211#issuecomment-339241929
#include "imgui_internal.h"
static void DisableUIStateBegin(const bool& bEnable)
{
if (!bEnable)
{
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
}
};
static void DisableUIStateEnd(const bool& bEnable)
{
if (!bEnable)
{
ImGui::PopItemFlag();
ImGui::PopStyleVar();
}
};
// Some constants and utility functions
static constexpr float MAGNIFICATION_AMOUNT_MIN = 1.0f;
static constexpr float MAGNIFICATION_AMOUNT_MAX = 32.0f;
static constexpr float MAGNIFIER_RADIUS_MIN = 0.01f;
static constexpr float MAGNIFIER_RADIUS_MAX = 0.85f;
static constexpr float MAGNIFIER_BORDER_COLOR__LOCKED[3] = { 0.002f, 0.72f, 0.0f }; // G
static constexpr float MAGNIFIER_BORDER_COLOR__FREE[3] = { 0.72f, 0.002f, 0.0f }; // R
template<class T> static T clamped(const T& v, const T& min, const T& max)
{
if (v < min) return min;
else if (v > max) return max;
else return v;
}
void FSR2Sample::BuildUI()
{
// save current values in case anything changes in the UI
m_SavedUiValues[m_UIState.m_nUpscaleType].scaleMode = m_nUpscaleMode;
m_SavedUiValues[m_UIState.m_nUpscaleType].upscaleRatio = m_fUpscaleRatio;
m_SavedUiValues[m_UIState.m_nUpscaleType].mipBias = m_UIState.mipBias;
m_SavedUiValues[m_UIState.m_nUpscaleType].useSharpen = m_UIState.bUseRcas;
m_SavedUiValues[m_UIState.m_nUpscaleType].sharpness = m_UIState.sharpening;
m_SavedUiValues[m_UIState.m_nUpscaleType].useTAA = m_UIState.bUseTAA;
// if we haven't initialized GLTFLoader yet, don't draw UI.
if (m_pGltfLoader == nullptr)
{
LoadScene(m_activeScene);
return;
}
ImGuiIO& io = ImGui::GetIO();
ImGuiStyle& style = ImGui::GetStyle();
style.FrameBorderSize = 1.0f;
const uint32_t W = this->GetWidth();
const uint32_t H = this->GetHeight();
const uint32_t PROFILER_WINDOW_PADDIG_X = 10;
const uint32_t PROFILER_WINDOW_PADDIG_Y = 10;
const uint32_t PROFILER_WINDOW_SIZE_X = 330;
const uint32_t PROFILER_WINDOW_SIZE_Y = 450;
const uint32_t PROFILER_WINDOW_POS_X = W - PROFILER_WINDOW_PADDIG_X - PROFILER_WINDOW_SIZE_X;
const uint32_t PROFILER_WINDOW_POS_Y = PROFILER_WINDOW_PADDIG_Y;
const uint32_t CONTROLS_WINDOW_POS_X = 10;
const uint32_t CONTROLS_WINDOW_POS_Y = 10;
const uint32_t CONTROLW_WINDOW_SIZE_X = 350;
const uint32_t CONTROLW_WINDOW_SIZE_Y = 780; // assuming > 720p
// Render CONTROLS window
//
ImGui::SetNextWindowPos(ImVec2(CONTROLS_WINDOW_POS_X, CONTROLS_WINDOW_POS_Y), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(ImVec2(CONTROLW_WINDOW_SIZE_X, CONTROLW_WINDOW_SIZE_Y), ImGuiCond_FirstUseEver);
if (m_UIState.bShowControlsWindow)
{
ImGui::Begin("CONTROLS (F11)", &m_UIState.bShowControlsWindow);
if (ImGui::CollapsingHeader("Animation", ImGuiTreeNodeFlags_DefaultOpen))
{
ImGui::Checkbox("Play", &m_bPlay);
ImGui::SliderFloat("Time", &m_time, 0, 30);
}
ImGui::Spacing();
ImGui::Spacing();
if (m_UIState.m_baloonID >= 0) {
if (ImGui::CollapsingHeader("Baloon Control", ImGuiTreeNodeFlags_DefaultOpen)) {
ImGui::Checkbox("Attach to Camera", &m_UIState.m_bBaloonAttachToCamera);
ImGui::SliderFloat("Amplitude", &m_UIState.m_BaloonAmplitude, 0.0f, 1.0f);
ImGui::SliderFloat("offset_X", &m_UIState.baloon_offset_x, -1.0f, 1.0f);
ImGui::SliderFloat("offset_Y", &m_UIState.baloon_offset_y, -1.0f, 1.0f);
ImGui::SliderFloat("offset_Z", &m_UIState.baloon_offset_z, -1.0f, 1.0f);
ImGui::SliderFloat("scale", &m_UIState.baloon_offset_scale, 0.0f, 0.1f);
}
}
if (ImGui::CollapsingHeader("Scene", ImGuiTreeNodeFlags_DefaultOpen))
{
char* cameraControl[] = { "Orbit", "WASD", "cam #0", "cam #1", "cam #2", "cam #3" , "cam #4", "cam #5" };
if (m_activeCamera >= m_pGltfLoader->m_cameras.size() + 2)
m_activeCamera = 0;
ImGui::Combo("Camera", &m_activeCamera, cameraControl, min((int)(m_pGltfLoader->m_cameras.size() + 2), _countof(cameraControl)));
ImGui::Checkbox("Camera Headbobbing", &m_UIState.m_bHeadBobbing);
auto getterLambda = [](void* data, int idx, const char** out_str)->bool { *out_str = ((std::vector<std::string> *)data)->at(idx).c_str(); return true; };
if (ImGui::Combo("Model", &m_activeScene, getterLambda, &m_sceneNames, (int)m_sceneNames.size()))
{
m_UIState.bRenderParticleSystem = (m_activeScene == 11);
// Note:
// probably queueing this as an event and handling it at the end/beginning
// of frame is a better idea rather than in the middle of drawing UI.
LoadScene(m_activeScene);
//bail out as we need to reload everything
ImGui::End();
ImGui::EndFrame();
ImGui::NewFrame();
return;
}
ImGui::SliderFloat("IBL Factor", &m_UIState.IBLFactor, 0.0f, 3.0f);
for (int i = 0; i < m_pGltfLoader->m_lights.size(); i++)
{
ImGui::SliderFloat(format("Light %i Intensity", i).c_str(), &m_pGltfLoader->m_lights[i].m_intensity, 0.0f, 50.0f);
}
if (ImGui::Button("Set Spot Light 0 to Camera's View"))
{
int idx = m_pGltfLoader->m_lightInstances[0].m_nodeIndex;
m_pGltfLoader->m_nodes[idx].m_transform.LookAt(m_UIState.camera.GetPosition(), m_UIState.camera.GetPosition() - m_UIState.camera.GetDirection());
m_pGltfLoader->m_animatedMats[idx] = m_pGltfLoader->m_nodes[idx].m_transform.GetWorldMat();
}
}
ImGui::Spacing();
ImGui::Spacing();
if (ImGui::CollapsingHeader("Upscaling ", ImGuiTreeNodeFlags_DefaultOpen)) //blank space needed, otherwise the identically named 'combo' drop down below won't work
{
const char* modes[] = { "Point [Ctrl-1]", "Bilinear[Ctrl-2]", "Bicubic[Ctrl-3]", "FSR 1.0[Ctrl-4]", "FSR 2.0 [Ctrl-5]", "Native [Ctrl-0]" };
if (ImGui::Combo("Upscaling", (int32_t*)&m_UIState.m_nUpscaleType, modes, _countof(modes)))
{
if (m_UIState.m_nUpscaleType == UPSCALE_TYPE_NATIVE)
{
// force reset values for native
m_SavedUiValues[m_UIState.m_nUpscaleType].scaleMode = UPSCALE_QUALITY_MODE_NONE;
m_SavedUiValues[m_UIState.m_nUpscaleType].upscaleRatio = 1.f;
m_SavedUiValues[m_UIState.m_nUpscaleType].mipBias = 0.f;
}
// update current values based on the last stored defaults
m_nUpscaleMode = m_SavedUiValues[m_UIState.m_nUpscaleType].scaleMode;
m_fUpscaleRatio = m_SavedUiValues[m_UIState.m_nUpscaleType].upscaleRatio;
m_UIState.mipBias = m_SavedUiValues[m_UIState.m_nUpscaleType].mipBias;
m_UIState.bUseRcas = m_SavedUiValues[m_UIState.m_nUpscaleType].useSharpen;
m_UIState.sharpening = m_SavedUiValues[m_UIState.m_nUpscaleType].sharpness;
m_UIState.bUseTAA = m_SavedUiValues[m_UIState.m_nUpscaleType].useTAA;
RefreshRenderResolution();
OnResize(true);
}
if (m_UIState.m_nUpscaleType <= UPSCALE_TYPE_FSR_2_0)
{
// adjust to match the combo box options
int32_t upscaleQualityMode = m_nUpscaleMode - UPSCALE_QUALITY_MODE_QUALITY;
const char* ratios[] = { "Quality (1.5x)", "Balanced (1.7x)", "Performance (2x)", "Ultra Performance (3x)", "Custom" };
if (ImGui::Combo("Scale mode", &upscaleQualityMode, ratios, _countof(ratios)))
{
m_nUpscaleMode = (UpscaleQualityMode)(upscaleQualityMode + UPSCALE_QUALITY_MODE_QUALITY); // above combo box starts from quality mode.
m_UIState.mipBias = mipBias[m_nUpscaleMode];
RefreshRenderResolution();
OnResize();
}
else
{
m_nUpscaleMode = (UpscaleQualityMode)(upscaleQualityMode + UPSCALE_QUALITY_MODE_QUALITY); // above combo box starts from quality mode.
ImGui::SliderFloat("Mip LOD bias", &m_UIState.mipBias, -5.0f, 0.0f);
}
if ((m_nUpscaleMode == UPSCALE_QUALITY_MODE_CUSTOM) && ImGui::SliderFloat("Custom factor", &m_fUpscaleRatio, 1.0f, 3.0f))
{
RefreshRenderResolution();
OnResize();
}
if (m_UIState.m_nUpscaleType == UPSCALE_TYPE_FSR_2_0)
{
if (ImGui::Checkbox("Dynamic resolution", &m_UIState.bDynamicRes)) {
OnResize();
}
}
else
m_UIState.bDynamicRes = false;
}
else
{
m_UIState.mipBias = mipBias[UPSCALE_TYPE_NATIVE];
}
ImGui::Checkbox("RCAS Sharpening", &m_UIState.bUseRcas);
if (m_UIState.m_nUpscaleType == UPSCALE_TYPE_FSR_2_0) {
if (m_UIState.bUseRcas) {
ImGui::SliderFloat("Sharpening", &m_UIState.sharpening, 0.0f, 1.0f);
}
} else {
if (m_UIState.bUseRcas) {
ImGui::SliderFloat("Sharpening attentuation", &m_UIState.sharpening, 0.0f, 10.0f);
}
}
if (m_UIState.m_nUpscaleType != UPSCALE_TYPE_FSR_2_0)
ImGui::Checkbox("TAA", &m_UIState.bUseTAA);
ImGui::Text("Render resolution: %dx%d", m_UIState.renderWidth, m_UIState.renderHeight);
ImGui::Text("Display resolution: %dx%d", m_UIState.displayWidth, m_UIState.displayHeight);
if (m_bDisplayHalf)
{
ImGui::Spacing();
ImGui::Spacing();
ImGui::Text(UseSlowFallback(m_device.IsFp16Supported()) ? "Slow fallback" : "Fast path");
}
}
ImGui::Spacing();
ImGui::Spacing();
if (ImGui::CollapsingHeader("PostProcessing", ImGuiTreeNodeFlags_DefaultOpen))
{
const char* tonemappers[] = { "AMD Tonemapper", "DX11DSK", "Reinhard", "Uncharted2Tonemap", "ACES", "No tonemapper" };
ImGui::Combo("Tonemapper", &m_UIState.SelectedTonemapperIndex, tonemappers, _countof(tonemappers));
bool isHdr = m_displayModesAvailable[m_currentDisplayModeNamesIndex] != DISPLAYMODE_SDR;
ImGui::SliderFloat("Exposure", isHdr ? &m_UIState.ExposureHdr : &m_UIState.Exposure, 0.0f, 4.0f);
}
ImGui::Spacing();
ImGui::Spacing();
if (ImGui::CollapsingHeader("Magnifier", ImGuiTreeNodeFlags_DefaultOpen))
{
// read in Magnifier pass parameters from the UI & app state
MagnifierPS::PassParameters& params = m_UIState.MagnifierParams;
params.uImageHeight = m_Height;
params.uImageWidth = m_Width;
params.iMousePos[0] = m_UIState.bLockMagnifierPosition ? m_UIState.LockedMagnifiedScreenPositionX : static_cast<int>(io.MousePos.x);
params.iMousePos[1] = m_UIState.bLockMagnifierPosition ? m_UIState.LockedMagnifiedScreenPositionY : static_cast<int>(io.MousePos.y);
ImGui::Checkbox("Show Magnifier (M)", &m_UIState.bUseMagnifier);
DisableUIStateBegin(m_UIState.bUseMagnifier);
{
// Use a local bool state here to track locked state through the UI widget,
// and then call ToggleMagnifierLockedState() to update the persistent state (m_UIstate).
// The keyboard input for toggling lock directly operates on the persistent state.
const bool bIsMagnifierCurrentlyLocked = m_UIState.bLockMagnifierPosition;
bool bMagnifierToggle = bIsMagnifierCurrentlyLocked;
ImGui::Checkbox("Lock Position (L)", &bMagnifierToggle);
if (bMagnifierToggle != bIsMagnifierCurrentlyLocked)
m_UIState.ToggleMagnifierLock();
ImGui::SliderFloat("Screen Size", &params.fMagnifierScreenRadius, MAGNIFIER_RADIUS_MIN, MAGNIFIER_RADIUS_MAX);
ImGui::SliderFloat("Magnification", &params.fMagnificationAmount, MAGNIFICATION_AMOUNT_MIN, MAGNIFICATION_AMOUNT_MAX);
ImGui::SliderInt("OffsetX", &params.iMagnifierOffset[0], -m_Width, m_Width);
ImGui::SliderInt("OffsetY", &params.iMagnifierOffset[1], -m_Height, m_Height);
}
DisableUIStateEnd(m_UIState.bUseMagnifier);
}
ImGui::Spacing();
ImGui::Spacing();
if (ImGui::CollapsingHeader("Debug", ImGuiTreeNodeFlags_DefaultOpen))
{
ImGui::Checkbox("Show Bounding Boxes", &m_UIState.bDrawBoundingBoxes);
ImGui::Checkbox("Show Light Frustum", &m_UIState.bDrawLightFrustum);
ImGui::Text("Wireframe");
ImGui::SameLine(); ImGui::RadioButton("Off", (int*)&m_UIState.WireframeMode, (int)UIState::WireframeMode::WIREFRAME_MODE_OFF);
ImGui::SameLine(); ImGui::RadioButton("Shaded", (int*)&m_UIState.WireframeMode, (int)UIState::WireframeMode::WIREFRAME_MODE_SHADED);
ImGui::SameLine(); ImGui::RadioButton("Solid color", (int*)&m_UIState.WireframeMode, (int)UIState::WireframeMode::WIREFRAME_MODE_SOLID_COLOR);
if (m_UIState.WireframeMode == UIState::WireframeMode::WIREFRAME_MODE_SOLID_COLOR)
ImGui::ColorEdit3("Wire solid color", m_UIState.WireframeColor, ImGuiColorEditFlags_NoAlpha);
}
ImGui::Spacing();
ImGui::Spacing();
if (ImGui::CollapsingHeader("Presentation Mode", ImGuiTreeNodeFlags_DefaultOpen))
{
const char* fullscreenModes[] = { "Windowed", "BorderlessFullscreen", "ExclusiveFulscreen" };
if (ImGui::Combo("Fullscreen Mode", (int*)&m_fullscreenMode, fullscreenModes, _countof(fullscreenModes)))
{
if (m_previousFullscreenMode != m_fullscreenMode)
{
HandleFullScreen();
m_previousFullscreenMode = m_fullscreenMode;
}
}
ImGui::Checkbox("Framerate Limiter", &m_UIState.bEnableFrameRateLimiter);
if (m_UIState.bEnableFrameRateLimiter)
{
ImGui::Checkbox("Use GPU Framerate Limiter", &m_UIState.bUseGPUFrameRateLimiter);
DWORD maxFramerate = 240;
DEVMODE lpDevMode = {};
lpDevMode.dmSize = sizeof(DEVMODE);
static int sTargetFPS = -1;
if (sTargetFPS < 0)
{
if (EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &lpDevMode))
{
sTargetFPS = lpDevMode.dmDisplayFrequency;
}
else
{
sTargetFPS = maxFramerate;
}
}
ImGui::SliderInt("Target FPS", &sTargetFPS, 5, maxFramerate);
m_UIState.targetFrameTime = 1000.f * (1.f / sTargetFPS);
}
}
ImGui::Spacing();
ImGui::Spacing();
if (m_FreesyncHDROptionEnabled && ImGui::CollapsingHeader("FreeSync HDR", ImGuiTreeNodeFlags_DefaultOpen))
{
static bool openWarning = false;
const char** displayModeNames = &m_displayModesNamesAvailable[0];
if (ImGui::Combo("Display Mode", (int*)&m_currentDisplayModeNamesIndex, displayModeNames, (int)m_displayModesNamesAvailable.size()))
{
if (m_fullscreenMode != PRESENTATIONMODE_WINDOWED)
{
UpdateDisplay(m_disableLocalDimming);
m_previousDisplayModeNamesIndex = m_currentDisplayModeNamesIndex;
}
else if (CheckIfWindowModeHdrOn() &&
(m_displayModesAvailable[m_currentDisplayModeNamesIndex] == DISPLAYMODE_SDR ||
m_displayModesAvailable[m_currentDisplayModeNamesIndex] == DISPLAYMODE_HDR10_2084 ||
m_displayModesAvailable[m_currentDisplayModeNamesIndex] == DISPLAYMODE_HDR10_SCRGB))
{
UpdateDisplay(m_disableLocalDimming);
m_previousDisplayModeNamesIndex = m_currentDisplayModeNamesIndex;
}
else
{
openWarning = true;
m_currentDisplayModeNamesIndex = m_previousDisplayModeNamesIndex;
}
OnResize(true);
}
if (openWarning)
{
ImGui::OpenPopup("Display Modes Warning");
ImGui::BeginPopupModal("Display Modes Warning", NULL, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::Text("\nChanging display modes is only available either using HDR toggle in windows display setting for HDR10 modes or in fullscreen for FS HDR modes\n\n");
if (ImGui::Button("Cancel", ImVec2(120, 0))) { openWarning = false; ImGui::CloseCurrentPopup(); }
ImGui::EndPopup();
}
if (m_displayModesAvailable[m_currentDisplayModeNamesIndex] == DisplayMode::DISPLAYMODE_FSHDR_Gamma22 ||
m_displayModesAvailable[m_currentDisplayModeNamesIndex] == DisplayMode::DISPLAYMODE_FSHDR_SCRGB)
{
static bool selectedDisableLocaldimmingSetting = false;
if (ImGui::Checkbox("Disable Local Dimming", &selectedDisableLocaldimmingSetting))
UpdateDisplay(selectedDisableLocaldimmingSetting);
}
}
ImGui::End(); // CONTROLS
}
// Render PROFILER window
//
if (m_UIState.bShowProfilerWindow)
{
constexpr size_t NUM_FRAMES = 128;
static float FRAME_TIME_ARRAY[NUM_FRAMES] = { 0 };
// track highest frame rate and determine the max value of the graph based on the measured highest value
static float RECENT_HIGHEST_FRAME_TIME = 0.0f;
constexpr int FRAME_TIME_GRAPH_MAX_FPS[] = { 800, 240, 120, 90, 65, 45, 30, 15, 10, 5, 4, 3, 2, 1 };
static float FRAME_TIME_GRAPH_MAX_VALUES[_countof(FRAME_TIME_GRAPH_MAX_FPS)] = { 0 }; // us
for (int i = 0; i < _countof(FRAME_TIME_GRAPH_MAX_FPS); ++i) { FRAME_TIME_GRAPH_MAX_VALUES[i] = 1000000.f / FRAME_TIME_GRAPH_MAX_FPS[i]; }
//scrolling data and average FPS computing
const std::vector<TimeStamp>& timeStamps = m_pRenderer->GetTimingValues();
const bool bTimeStampsAvailable = timeStamps.size() > 0;
if (bTimeStampsAvailable)
{
RECENT_HIGHEST_FRAME_TIME = 0;
FRAME_TIME_ARRAY[NUM_FRAMES - 1] = timeStamps.back().m_microseconds;
for (uint32_t i = 0; i < NUM_FRAMES - 1; i++)
{
FRAME_TIME_ARRAY[i] = FRAME_TIME_ARRAY[i + 1];
}
RECENT_HIGHEST_FRAME_TIME = max(RECENT_HIGHEST_FRAME_TIME, FRAME_TIME_ARRAY[NUM_FRAMES - 1]);
}
const float& frameTime_us = FRAME_TIME_ARRAY[NUM_FRAMES - 1];
const float frameTime_ms = frameTime_us * 0.001f;
const int fps = bTimeStampsAvailable ? static_cast<int>(1000000.0f / frameTime_us) : 0;
// UI
ImGui::SetNextWindowPos(ImVec2((float)PROFILER_WINDOW_POS_X, (float)PROFILER_WINDOW_POS_Y), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(ImVec2(PROFILER_WINDOW_SIZE_X, PROFILER_WINDOW_SIZE_Y), ImGuiCond_FirstUseEver);
ImGui::Begin("PROFILER (F12))", &m_UIState.bShowProfilerWindow);
ImGui::Text("Resolution : %ix%i", m_Width, m_Height);
ImGui::Text("API : %s", m_systemInfo.mGfxAPI.c_str());
ImGui::Text("GPU : %s", m_systemInfo.mGPUName.c_str());
ImGui::Text("CPU : %s", m_systemInfo.mCPUName.c_str());
ImGui::Text("FPS : %d (%.2f ms)", fps, frameTime_ms);
if (ImGui::CollapsingHeader("GPU Timings", ImGuiTreeNodeFlags_DefaultOpen))
{
std::string msOrUsButtonText = m_UIState.bShowMilliseconds ? "Switch to microseconds" : "Switch to milliseconds";
if (ImGui::Button(msOrUsButtonText.c_str())) {
m_UIState.bShowMilliseconds = !m_UIState.bShowMilliseconds;
}
ImGui::Checkbox("Show Avg/Min/Max", &m_UIState.bShowAverage);
ImGui::Spacing();
if (m_isCpuValidationLayerEnabled || m_isGpuValidationLayerEnabled)
{
ImGui::TextColored(ImVec4(1, 1, 0, 1), "WARNING: Validation layer is switched on");
ImGui::Text("Performance numbers may be inaccurate!");
}
// find the index of the FrameTimeGraphMaxValue as the next higher-than-recent-highest-frame-time in the pre-determined value list
size_t iFrameTimeGraphMaxValue = 0;
for (int i = 0; i < _countof(FRAME_TIME_GRAPH_MAX_VALUES); ++i)
{
if (RECENT_HIGHEST_FRAME_TIME < FRAME_TIME_GRAPH_MAX_VALUES[i]) // FRAME_TIME_GRAPH_MAX_VALUES are in increasing order
{
iFrameTimeGraphMaxValue = min(_countof(FRAME_TIME_GRAPH_MAX_VALUES) - 1, i + 1);
break;
}
}
ImGui::PlotLines("", FRAME_TIME_ARRAY, NUM_FRAMES, 0, "GPU frame time (us)", 0.0f, FRAME_TIME_GRAPH_MAX_VALUES[iFrameTimeGraphMaxValue], ImVec2(0, 80));
constexpr uint32_t AvgTimeStampUpdateIntervalMs = 1000;
const double currTime = MillisecondsNow();
bool bResetAccumulatingState = false;
if ((m_UIState.accumulatingFrameCount > 1) &&
((currTime - m_UIState.lastTimeStampsResetTime) > AvgTimeStampUpdateIntervalMs))
{
std::swap(m_UIState.displayedTimeStamps, m_UIState.accumulatingTimeStamps);
for (uint32_t i = 0; i < m_UIState.displayedTimeStamps.size(); i++)
{
m_UIState.displayedTimeStamps[i].sum /= m_UIState.accumulatingFrameCount;
}
bResetAccumulatingState = true;
}
bResetAccumulatingState |= (m_UIState.accumulatingTimeStamps.size() != timeStamps.size());
if (bResetAccumulatingState)
{
m_UIState.accumulatingTimeStamps.resize(0);
m_UIState.accumulatingTimeStamps.resize(timeStamps.size());
m_UIState.lastTimeStampsResetTime = currTime;
m_UIState.accumulatingFrameCount = 0;
}
const float unitScaling = m_UIState.bShowMilliseconds ? (1.0f / 1000.0f) : 1.0f;
for (uint32_t i = 0; i < timeStamps.size(); i++)
{
float value = timeStamps[i].m_microseconds * unitScaling;
const char* pStrUnit = m_UIState.bShowMilliseconds ? "ms" : "us";
ImGui::Text("%-18s: %7.2f %s", timeStamps[i].m_label.c_str(), value, pStrUnit);
if (m_UIState.bShowAverage)
{
if (m_UIState.displayedTimeStamps.size() == timeStamps.size())
{
ImGui::SameLine();
ImGui::Text(" avg: %7.2f %s", m_UIState.displayedTimeStamps[i].sum * unitScaling, pStrUnit);
ImGui::SameLine();
ImGui::Text(" min: %7.2f %s", m_UIState.displayedTimeStamps[i].minimum * unitScaling, pStrUnit);
ImGui::SameLine();
ImGui::Text(" max: %7.2f %s", m_UIState.displayedTimeStamps[i].maximum * unitScaling, pStrUnit);
}
UIState::AccumulatedTimeStamp* pAccumulatingTimeStamp = &m_UIState.accumulatingTimeStamps[i];
pAccumulatingTimeStamp->sum += timeStamps[i].m_microseconds;
pAccumulatingTimeStamp->minimum = min(pAccumulatingTimeStamp->minimum, timeStamps[i].m_microseconds);
pAccumulatingTimeStamp->maximum = max(pAccumulatingTimeStamp->maximum, timeStamps[i].m_microseconds);
}
}
m_UIState.accumulatingFrameCount++;
}
ImGui::End(); // PROFILER
}
}
void UIState::Initialize()
{
// init magnifier params
for (int ch = 0; ch < 3; ++ch) this->MagnifierParams.fBorderColorRGB[ch] = MAGNIFIER_BORDER_COLOR__FREE[ch]; // start at 'free' state
// init GUI state
this->SelectedTonemapperIndex = 0;
this->bUseMagnifier = false;
this->bLockMagnifierPosition = this->bLockMagnifierPositionHistory = false;
this->SelectedSkydomeTypeIndex = 0;
this->Exposure = 1.0f;
this->IBLFactor = 2.0f;
this->EmissiveFactor = 1.0f;
this->bDrawLightFrustum = false;
this->bDrawBoundingBoxes = false;
this->WireframeMode = WireframeMode::WIREFRAME_MODE_OFF;
this->WireframeColor[0] = 0.0f;
this->WireframeColor[1] = 1.0f;
this->WireframeColor[2] = 0.0f;
this->bShowControlsWindow = true;
this->bShowProfilerWindow = true;
this->bShowMilliseconds = false;
this->bShowAverage = true;
this->lastTimeStampsResetTime = 0;
this->accumulatingFrameCount = 0;
}
//
// Magnifier UI Controls
//
void UIState::ToggleMagnifierLock()
{
if (this->bUseMagnifier)
{
this->bLockMagnifierPositionHistory = this->bLockMagnifierPosition; // record histroy
this->bLockMagnifierPosition = !this->bLockMagnifierPosition; // flip state
const bool bLockSwitchedOn = !this->bLockMagnifierPositionHistory && this->bLockMagnifierPosition;
const bool bLockSwitchedOff = this->bLockMagnifierPositionHistory && !this->bLockMagnifierPosition;
if (bLockSwitchedOn)
{
const ImGuiIO& io = ImGui::GetIO();
this->LockedMagnifiedScreenPositionX = static_cast<int>(io.MousePos.x);
this->LockedMagnifiedScreenPositionY = static_cast<int>(io.MousePos.y);
for (int ch = 0; ch < 3; ++ch) this->MagnifierParams.fBorderColorRGB[ch] = MAGNIFIER_BORDER_COLOR__LOCKED[ch];
}
else if (bLockSwitchedOff)
{
for (int ch = 0; ch < 3; ++ch) this->MagnifierParams.fBorderColorRGB[ch] = MAGNIFIER_BORDER_COLOR__FREE[ch];
}
}
}
// These are currently not bound to any mouse input and are here for convenience/reference.
// Mouse scroll is currently wired up to camera for panning and moving in the local Z direction.
// Any application that would prefer otherwise can utilize these for easily controlling the magnifier parameters through the desired input.
void UIState::AdjustMagnifierSize(float increment /*= 0.05f*/) { MagnifierParams.fMagnifierScreenRadius = clamped(MagnifierParams.fMagnifierScreenRadius + increment, MAGNIFIER_RADIUS_MIN, MAGNIFIER_RADIUS_MAX); }
void UIState::AdjustMagnifierMagnification(float increment /*= 1.00f*/) { MagnifierParams.fMagnificationAmount = clamped(MagnifierParams.fMagnificationAmount + increment, MAGNIFICATION_AMOUNT_MIN, MAGNIFICATION_AMOUNT_MAX); }
bool UIState::DevOption(bool* pBoolValue, const char* name)
{
return ImGui::Checkbox(name, pBoolValue);
}
bool UIState::DevOption(int* pIntValue, const char* name, int minVal, int maxVal)
{
return ImGui::SliderInt(name, pIntValue, minVal, maxVal);
}
bool UIState::DevOption(int* pEnumValue, const char* name, uint32_t count, const char* const* ppEnumNames)
{
return ImGui::Combo(name, pEnumValue, ppEnumNames, count);
}
bool UIState::DevOption(int* pActiveIndex, uint32_t count, const char* const* ppEnumNames)
{
bool pressed = false;
ImGui::BeginGroup();
for (uint32_t i = 0; i < count; i++)
{
pressed |= ImGui::RadioButton(ppEnumNames[i], pActiveIndex, int(i));
}
ImGui::EndGroup();
return pressed;
}
bool UIState::DevOption(float* pFloatValue, const char* name, float fMin, float fMax)
{
return ImGui::SliderFloat(name, pFloatValue, fMin, fMax);
}
void UIState::Text(const char* text)
{
ImGui::Text(text);
}

@ -0,0 +1,212 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#pragma once
#include "PostProc/MagnifierPS.h"
#include <string>
enum TAA_MODE
{
RM_DISABLED,
RM_FFX_TAA,
};
typedef enum UpscaleType {
UPSCALE_TYPE_POINT = 0,
UPSCALE_TYPE_BILINEAR = 1,
UPSCALE_TYPE_BICUBIC = 2,
UPSCALE_TYPE_FSR_1_0 = 3,
UPSCALE_TYPE_FSR_2_0 = 4,
UPSCALE_TYPE_NATIVE = 5,
// add above this.
UPSCALE_TYPE_COUNT
} UpscaleType;
typedef enum UpscaleQualityMode {
UPSCALE_QUALITY_MODE_NONE = 0,
UPSCALE_QUALITY_MODE_ULTRA_QUALITY = 1,
UPSCALE_QUALITY_MODE_QUALITY = 2,
UPSCALE_QUALITY_MODE_BALANCE = 3,
UPSCALE_QUALITY_MODE_PERFORMANCE = 4,
UPSCALE_QUALITY_MODE_ULTRA_PERFORMANCE = 5,
UPSCALE_QUALITY_MODE_CUSTOM = 6,
// add above this.
UPSCALE_QUALITY_MODE_COUNT
} UpscaleQualityMode;
struct UIState
{
Camera camera;
bool m_bCameraInertia = false;
bool m_bHeadBobbing = false;
//
// WINDOW MANAGEMENT
//
bool bShowControlsWindow;
bool bShowProfilerWindow;
//
// POST PROCESS CONTROLS
//
int SelectedTonemapperIndex;
float Exposure;
float ExposureHdr = 1.f;
bool bReset = false;
bool bRenderParticleSystem = false;
bool bUseMagnifier;
bool bLockMagnifierPosition;
bool bLockMagnifierPositionHistory;
int LockedMagnifiedScreenPositionX;
int LockedMagnifiedScreenPositionY;
CAULDRON_VK::MagnifierPS::PassParameters MagnifierParams;
const std::string* m_pScreenShotName = NULL;
// FSR
uint32_t displayWidth = 0;
uint32_t displayHeight = 0;
uint32_t renderWidth = 0;
uint32_t renderHeight = 0;
bool bVsyncOn = true;
// NOTE: These should always match the values in m_SavedUiValues for m_nUpscaleType.
UpscaleType m_nUpscaleType = UPSCALE_TYPE_FSR_2_0;
float mipBias = -2.0f;
bool bUseRcas = false;
bool bUseTAA = true;
float sharpening = 0.8f;
// Dynamic resolution
bool bDynamicRes = false;
// TAA
TAA_MODE taaMode = RM_FFX_TAA;
unsigned int closestVelocitySamplePattern = 0; // 5 samples
float Feedback = 15.f / 16.f;
// FSR2 auto reactive
bool bUseFsr2AutoReactive = false;
float fFsr2AutoReactiveScale = 1.f;
float fFsr2AutoReactiveThreshold = 0.01f;
bool bFsr2AutoReactiveTonemap = true;
bool bFsr2AutoReactiveInverseTonemap = false;
bool bFsr2AutoReactiveThreshold = true;
bool bFsr2AutoReactiveUseMax = true;
//
// APP/SCENE CONTROLS
//
float IBLFactor;
float EmissiveFactor;
double deltaTime;
bool bEnableFrameRateLimiter = false;
bool bUseGPUFrameRateLimiter = false;
double targetFrameTime;
int SelectedSkydomeTypeIndex;
bool bDrawBoundingBoxes;
bool bDrawLightFrustum;
enum class WireframeMode : int
{
WIREFRAME_MODE_OFF = 0,
WIREFRAME_MODE_SHADED = 1,
WIREFRAME_MODE_SOLID_COLOR = 2,
};
WireframeMode WireframeMode;
float WireframeColor[3];
//
// PROFILER CONTROLS
//
bool bShowMilliseconds;
bool bShowAverage;
struct AccumulatedTimeStamp
{
float sum;
float minimum;
float maximum;
AccumulatedTimeStamp()
: sum(0.0f), minimum(FLT_MAX), maximum(0)
{
}
};
std::vector<AccumulatedTimeStamp> displayedTimeStamps;
std::vector<AccumulatedTimeStamp> accumulatingTimeStamps;
double lastTimeStampsResetTime;
uint32_t accumulatingFrameCount;
int32_t m_baloonID = -1;
bool m_baloonInit = false;
float m_BaloonAmplitude = 0.05f;
math::Matrix4 m_InitBaloonTransform = {};
math::Matrix4 m_CurBaloonTransform = {};
bool m_bBaloonAttachToCamera = true;
float baloon_offset_x = 0.05f;
float baloon_offset_y = -0.08f;
float baloon_offset_z = -0.17f;
float baloon_offset_scale = 0.05f;
Vectormath::Vector3 baloon_pos = { 0.0f, 0.0f, 0.0f };
Vectormath::Vector3 baloon_tip_pos = { 0.0f, 0.0f, 0.0f };
// -----------------------------------------------
void Initialize();
void ToggleMagnifierLock();
void AdjustMagnifierSize(float increment = 0.05f);
void AdjustMagnifierMagnification(float increment = 1.00f);
bool DevOption(bool* pBoolValue, const char* name);
bool DevOption(int* pIntValue, const char* name, int minVal, int maxVal);
bool DevOption(int* pEnumValue, const char* name, uint32_t count, const char* const* ppEnumNames);
bool DevOption(int* pActiveIndex, uint32_t count, const char* const* ppEnumNames);
bool DevOption(float* pFloatValue, const char* name, float fMin = 0.0f, float fMax = 1.0f);
void Text(const char* text);
template<typename T>
bool DevOption(T& flags, uint32_t bitIdx, const char* name)
{
static_assert(sizeof(T) <= sizeof(uint64_t));
bool bSet = uint64_t(flags) & (1ULL << bitIdx);
bool bResult = DevOption(&bSet, name);
if (bSet)
{
flags = T(uint64_t(flags) | (1ULL << bitIdx));
}
else
{
flags = T(uint64_t(flags) & ~(1ULL << bitIdx));
}
return bResult;
}
};

@ -0,0 +1,157 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "stdafx.h"
#include "UpscaleContext.h"
#include "UpscaleContext_Spatial.h"
#include "UpscaleContext_FSR2_API.h"
#include "../ffx-fsr2-api/ffx_fsr2.h"
// factory
UpscaleContext* UpscaleContext::CreateUpscaleContext(const FfxUpscaleInitParams& initParams)
{
UpscaleContext* pContext = NULL;
switch (initParams.nType)
{
default:
case UPSCALE_TYPE_NATIVE:
case UPSCALE_TYPE_POINT:
case UPSCALE_TYPE_BILINEAR:
case UPSCALE_TYPE_BICUBIC:
case UPSCALE_TYPE_FSR_1_0:
pContext = new UpscaleContext_Spatial(initParams.nType, "Spatial");
break;
case UPSCALE_TYPE_FSR_2_0:
pContext = new UpscaleContext_FSR2_API(initParams.nType, "FSR2 API");
break;
// Additional options:
// MSAA, MSAA resolve to higher resolution, then add TAA?
// Checkerboard?
}
pContext->OnCreate(initParams);
return pContext;
}
UpscaleContext::UpscaleContext(std::string name)
: m_Name(name)
{
}
//--------------------------------------------------------------------------------------
//
// OnCreate
//
//--------------------------------------------------------------------------------------
void UpscaleContext::OnCreate(const FfxUpscaleInitParams& initParams)
{
m_bInvertedDepth = initParams.bInvertedDepth;
m_pDevice = initParams.pDevice;
m_OutputFormat = initParams.outFormat;
m_Type = initParams.nType;
m_MaxQueuedFrames = initParams.maxQueuedFrames;
const uint32_t cbvDescriptorCount = 4000;
const uint32_t srvDescriptorCount = 8000;
const uint32_t uavDescriptorCount = 100;
const uint32_t samplerDescriptorCount = 20;
m_ResourceViewHeaps.OnCreate(m_pDevice, cbvDescriptorCount, srvDescriptorCount, uavDescriptorCount, samplerDescriptorCount);
// Create a 'dynamic' constant buffer
const uint32_t constantBuffersMemSize = 2 * 1024 * 1024;
VkResult result = m_ConstantBufferRing.OnCreate(m_pDevice, 2, constantBuffersMemSize, "UpscaleContext_ConstantBufferRing");
FFX_ASSERT(result == VK_SUCCESS);
}
//--------------------------------------------------------------------------------------
//
// OnDestroy
//
//--------------------------------------------------------------------------------------
void UpscaleContext::OnDestroy()
{
m_ConstantBufferRing.OnDestroy();
m_ResourceViewHeaps.OnDestroy();
}
//--------------------------------------------------------------------------------------
//
// OnCreateWindowSizeDependentResources
//
//--------------------------------------------------------------------------------------
void UpscaleContext::OnCreateWindowSizeDependentResources(VkImageView input, VkImageView output, uint32_t renderWidth, uint32_t renderHeight, uint32_t displayWidth, uint32_t displayHeight, bool hdr)
{
m_input = input;
m_output = output;
m_renderWidth = renderWidth;
m_renderHeight = renderHeight;
m_displayWidth = displayWidth;
m_displayHeight = displayHeight;
m_index = 0;
m_frameIndex = 0;
m_bInit = true;
m_hdr = hdr;
}
//--------------------------------------------------------------------------------------
//
// OnDestroyWindowSizeDependentResources
//
//--------------------------------------------------------------------------------------
void UpscaleContext::OnDestroyWindowSizeDependentResources()
{
}
//--------------------------------------------------------------------------------------
//
// PreDraw
//
//--------------------------------------------------------------------------------------
void UpscaleContext::PreDraw(UIState* pState)
{
m_bUseTaa = pState->bUseTAA;
m_ConstantBufferRing.OnBeginFrame();
m_frameIndex++;
if (m_bUseTaa)
{
m_index++;
// handle reset and jitter
const int32_t jitterPhaseCount = ffxFsr2GetJitterPhaseCount(pState->renderWidth, pState->displayWidth);
ffxFsr2GetJitterOffset(&m_JitterX, &m_JitterY, m_index, jitterPhaseCount);
pState->camera.SetProjectionJitter(2.0f * m_JitterX / (float)pState->renderWidth, -2.0f * m_JitterY / (float)pState->renderHeight);
}
else
{
pState->camera.SetProjectionJitter(0.f, 0.f);
}
}
void UpscaleContext::GenerateReactiveMask(VkCommandBuffer pCommandList, const FfxUpscaleSetup& cameraSetup, UIState* pState)
{
}

@ -0,0 +1,116 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#pragma once
#include "Base/Texture.h"
#include "Base/StaticBufferPool.h"
#include "Base/GBuffer.h"
#include "PostProc/PostProcCS.h"
#include "UI.h"
using namespace CAULDRON_VK;
#define GROUP_SIZE 8
class UpscaleContext
{
public:
typedef struct
{
UpscaleType nType;
bool bInvertedDepth;
Device* pDevice;
VkFormat outFormat;
UploadHeap* pUploadHeap;
uint32_t maxQueuedFrames;
}FfxUpscaleInitParams;
typedef struct
{
math::Matrix4 mCameraView;
math::Matrix4 mCameraProj;
math::Matrix4 mCameraViewInv;
math::Vector4 vCameraPos;
}FfxCameraSetup;
typedef struct
{
FfxCameraSetup cameraSetup;
Texture* unresolvedColorResource; // input0
Texture* motionvectorResource; // input1
Texture* depthbufferResource; // input2
Texture* reactiveMapResource; // input3
Texture* transparencyAndCompositionResource; // input4
Texture* opaqueOnlyColorResource; // input5
Texture* resolvedColorResource; // output
VkImageView unresolvedColorResourceView; // input0
VkImageView motionvectorResourceView; // input1
VkImageView depthbufferResourceView; // input2
VkImageView reactiveMapResourceView; // input3
VkImageView transparencyAndCompositionResourceView; // input4
VkImageView opaqueOnlyColorResourceView; // input5
VkImageView resolvedColorResourceView; // output
}FfxUpscaleSetup;
// factory
static UpscaleContext* CreateUpscaleContext(const FfxUpscaleInitParams& initParams);
UpscaleContext(std::string name);
virtual UpscaleType Type() { return m_Type; };
virtual std::string Name() { return "Upscale"; }
virtual void OnCreate(const FfxUpscaleInitParams& initParams);
virtual void OnDestroy();
virtual void OnCreateWindowSizeDependentResources(VkImageView input, VkImageView output, uint32_t renderWidth, uint32_t renderHeight, uint32_t displayWidth, uint32_t displayHeight, bool hdr);
virtual void OnDestroyWindowSizeDependentResources();
virtual void BuildDevUI(UIState* pState) {}
virtual void PreDraw(UIState* pState);
virtual void GenerateReactiveMask(VkCommandBuffer pCommandList, const FfxUpscaleSetup& cameraSetup, UIState* pState);
virtual void Draw(VkCommandBuffer commandBuffer, const FfxUpscaleSetup& cameraSetup, UIState* pState) = NULL;
protected:
Device* m_pDevice;
ResourceViewHeaps m_ResourceViewHeaps;
DynamicBufferRing m_ConstantBufferRing;
VkFormat m_OutputFormat;
uint32_t m_MaxQueuedFrames;
UpscaleType m_Type;
std::string m_Name;
bool m_bInvertedDepth;
bool m_bUseTaa;
uint32_t m_renderWidth, m_renderHeight;
uint32_t m_displayWidth, m_displayHeight;
bool m_bInit;
uint32_t m_frameIndex;
uint32_t m_index;
float m_JitterX;
float m_JitterY;
VkImageView m_input;
VkImageView m_output;
bool m_hdr;
};

@ -0,0 +1,184 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "stdafx.h"
#include "UpscaleContext_FSR2_API.h"
#include "../ffx-fsr2-api/vk/ffx_fsr2_vk.h"
static VkDeviceSize getMemoryUsageSnapshot(VkPhysicalDevice physicalDevice)
{
// check if VK_EXT_memory_budget is enabled
std::vector<VkExtensionProperties> extensionProperties;
// enumerate all the device extensions
uint32_t deviceExtensionCount = 0;
vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &deviceExtensionCount, nullptr);
extensionProperties.resize(deviceExtensionCount);
vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &deviceExtensionCount, extensionProperties.data());
bool extensionFound = false;
for (uint32_t i = 0; i < deviceExtensionCount; i++)
{
if (strcmp(extensionProperties[i].extensionName, VK_EXT_MEMORY_BUDGET_EXTENSION_NAME) == 0)
extensionFound = true;
}
if (!extensionFound)
return 0;
VkDeviceSize memoryUsage = 0;
VkPhysicalDeviceMemoryBudgetPropertiesEXT memoryBudgetProperties = {};
memoryBudgetProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT;
VkPhysicalDeviceMemoryProperties2 memoryProperties = {};
memoryProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2;
memoryProperties.pNext = &memoryBudgetProperties;
vkGetPhysicalDeviceMemoryProperties2(physicalDevice, &memoryProperties);
for (uint32_t i = 0; i < memoryProperties.memoryProperties.memoryTypeCount; i++)
memoryUsage += memoryBudgetProperties.heapUsage[i];
return memoryUsage;
}
UpscaleContext_FSR2_API::UpscaleContext_FSR2_API(UpscaleType type, std::string name)
: UpscaleContext(name)
{
}
void UpscaleContext_FSR2_API::OnCreate(const FfxUpscaleInitParams& initParams)
{
UpscaleContext::OnCreate(initParams);
}
void UpscaleContext_FSR2_API::OnDestroy()
{
UpscaleContext::OnDestroy();
}
void UpscaleContext_FSR2_API::OnCreateWindowSizeDependentResources(
VkImageView input,
VkImageView output,
uint32_t renderWidth,
uint32_t renderHeight,
uint32_t displayWidth,
uint32_t displayHeight,
bool hdr)
{
UpscaleContext::OnCreateWindowSizeDependentResources(input, output, renderWidth, renderHeight, displayWidth, displayHeight, hdr);
// Setup VK interface.
const size_t scratchBufferSize = ffxFsr2GetScratchMemorySizeVK(m_pDevice->GetPhysicalDevice());
void* scratchBuffer = malloc(scratchBufferSize);
FfxErrorCode errorCode = ffxFsr2GetInterfaceVK(&initializationParameters.callbacks, scratchBuffer, scratchBufferSize, m_pDevice->GetPhysicalDevice(), vkGetDeviceProcAddr);
FFX_ASSERT(errorCode == FFX_OK);
initializationParameters.device = ffxGetDeviceVK(m_pDevice->GetDevice());
initializationParameters.maxRenderSize.width = renderWidth;
initializationParameters.maxRenderSize.height = renderHeight;
initializationParameters.displaySize.width = displayWidth;
initializationParameters.displaySize.height = displayHeight;
initializationParameters.flags = FFX_FSR2_ENABLE_DEPTH_INVERTED
| FFX_FSR2_ENABLE_AUTO_EXPOSURE;
if (hdr) {
initializationParameters.flags |= FFX_FSR2_ENABLE_HIGH_DYNAMIC_RANGE;
}
const uint64_t memoryUsageBefore = getMemoryUsageSnapshot(m_pDevice->GetPhysicalDevice());
ffxFsr2ContextCreate(&context, &initializationParameters);
const uint64_t memoryUsageAfter = getMemoryUsageSnapshot(m_pDevice->GetPhysicalDevice());
memoryUsageInMegabytes = (memoryUsageAfter - memoryUsageBefore) * 0.000001f;
}
void UpscaleContext_FSR2_API::OnDestroyWindowSizeDependentResources()
{
UpscaleContext::OnDestroyWindowSizeDependentResources();
ffxFsr2ContextDestroy(&context);
}
void UpscaleContext_FSR2_API::BuildDevUI(UIState* pState)
{
if (memoryUsageInMegabytes > 0) {
char meminfo[256];
sprintf_s(meminfo, "FSR 2.0 GPU memory usage: %.2f MB", memoryUsageInMegabytes);
pState->Text(meminfo);
}
pState->bReset = ImGui::Button("Reset accumulation");
}
void UpscaleContext_FSR2_API::GenerateReactiveMask(VkCommandBuffer pCommandList, const FfxUpscaleSetup& cameraSetup, UIState* pState)
{
FfxFsr2GenerateReactiveDescription generateReactiveParameters;
generateReactiveParameters.commandList = ffxGetCommandListVK(pCommandList);
generateReactiveParameters.colorOpaqueOnly = ffxGetTextureResourceVK(&context, cameraSetup.opaqueOnlyColorResource->Resource(), cameraSetup.opaqueOnlyColorResourceView, cameraSetup.opaqueOnlyColorResource->GetWidth(), cameraSetup.opaqueOnlyColorResource->GetHeight(), cameraSetup.opaqueOnlyColorResource->GetFormat(), L"FSR2_OpaqueOnlyColorResource");
generateReactiveParameters.colorPreUpscale = ffxGetTextureResourceVK(&context, cameraSetup.unresolvedColorResource->Resource(), cameraSetup.unresolvedColorResourceView, cameraSetup.unresolvedColorResource->GetWidth(), cameraSetup.unresolvedColorResource->GetHeight(), cameraSetup.unresolvedColorResource->GetFormat(), L"FSR2_UnresolvedColorResource");
generateReactiveParameters.outReactive = ffxGetTextureResourceVK(&context, cameraSetup.reactiveMapResource->Resource(), cameraSetup.reactiveMapResourceView, cameraSetup.reactiveMapResource->GetWidth(), cameraSetup.reactiveMapResource->GetHeight(), cameraSetup.reactiveMapResource->GetFormat(), L"FSR2_InputReactiveMap", FFX_RESOURCE_STATE_GENERIC_READ);
generateReactiveParameters.renderSize.width = pState->renderWidth;
generateReactiveParameters.renderSize.height = pState->renderHeight;
generateReactiveParameters.scale = pState->fFsr2AutoReactiveScale;
generateReactiveParameters.cutoffThreshold = pState->fFsr2AutoReactiveThreshold;
generateReactiveParameters.flags = (pState->bFsr2AutoReactiveTonemap ? FFX_FSR2_AUTOREACTIVEFLAGS_APPLY_TONEMAP : 0) |
(pState->bFsr2AutoReactiveInverseTonemap ? FFX_FSR2_AUTOREACTIVEFLAGS_APPLY_INVERSETONEMAP : 0) |
(pState->bFsr2AutoReactiveThreshold ? FFX_FSR2_AUTOREACTIVEFLAGS_APPLY_THRESHOLD : 0) |
(pState->bFsr2AutoReactiveUseMax ? FFX_FSR2_AUTOREACTIVEFLAGS_USE_COMPONENTS_MAX : 0);
ffxFsr2ContextGenerateReactiveMask(&context, &generateReactiveParameters);
}
void UpscaleContext_FSR2_API::Draw(VkCommandBuffer commandBuffer, const FfxUpscaleSetup& cameraSetup, UIState* pState)
{
FfxFsr2DispatchDescription dispatchParameters = {};
dispatchParameters.commandList = ffxGetCommandListVK(commandBuffer);
dispatchParameters.color = ffxGetTextureResourceVK(&context, cameraSetup.unresolvedColorResource->Resource(), cameraSetup.unresolvedColorResourceView, cameraSetup.unresolvedColorResource->GetWidth(), cameraSetup.unresolvedColorResource->GetHeight(), cameraSetup.unresolvedColorResource->GetFormat(), L"FSR2_InputColor");
dispatchParameters.depth = ffxGetTextureResourceVK(&context, cameraSetup.depthbufferResource->Resource(), cameraSetup.depthbufferResourceView, cameraSetup.depthbufferResource->GetWidth(), cameraSetup.depthbufferResource->GetHeight(), cameraSetup.depthbufferResource->GetFormat(), L"FSR2_InputDepth");
dispatchParameters.motionVectors = ffxGetTextureResourceVK(&context, cameraSetup.motionvectorResource->Resource(), cameraSetup.motionvectorResourceView, cameraSetup.motionvectorResource->GetWidth(), cameraSetup.motionvectorResource->GetHeight(), cameraSetup.motionvectorResource->GetFormat(), L"FSR2_InputMotionVectors");
dispatchParameters.exposure = ffxGetTextureResourceVK(&context, nullptr, nullptr, 1, 1, VK_FORMAT_UNDEFINED, L"FSR2_InputExposure");
dispatchParameters.reactive = ffxGetTextureResourceVK(&context, cameraSetup.reactiveMapResource->Resource(), cameraSetup.reactiveMapResourceView, cameraSetup.reactiveMapResource->GetWidth(), cameraSetup.reactiveMapResource->GetHeight(), cameraSetup.reactiveMapResource->GetFormat(), L"FSR2_InputReactiveMap");
dispatchParameters.transparencyAndComposition = ffxGetTextureResourceVK(&context, cameraSetup.transparencyAndCompositionResource->Resource(), cameraSetup.transparencyAndCompositionResourceView, cameraSetup.transparencyAndCompositionResource->GetWidth(), cameraSetup.transparencyAndCompositionResource->GetHeight(), cameraSetup.transparencyAndCompositionResource->GetFormat(), L"FSR2_TransparencyAndCompositionMap");
dispatchParameters.output = ffxGetTextureResourceVK(&context, cameraSetup.resolvedColorResource->Resource(), cameraSetup.resolvedColorResourceView, cameraSetup.resolvedColorResource->GetWidth(), cameraSetup.resolvedColorResource->GetHeight(), cameraSetup.resolvedColorResource->GetFormat(), L"FSR2_OutputUpscaledColor", FFX_RESOURCE_STATE_UNORDERED_ACCESS);
dispatchParameters.jitterOffset.x = m_JitterX;
dispatchParameters.jitterOffset.y = m_JitterY;
dispatchParameters.motionVectorScale.x = (float)pState->renderWidth;
dispatchParameters.motionVectorScale.y = (float)pState->renderHeight;
dispatchParameters.reset = pState->bReset;
dispatchParameters.enableSharpening = pState->bUseRcas;
dispatchParameters.sharpness = pState->sharpening;
dispatchParameters.frameTimeDelta = (float)pState->deltaTime;
dispatchParameters.preExposure = 1.0f;
dispatchParameters.renderSize.width = pState->renderWidth;
dispatchParameters.renderSize.height = pState->renderHeight;
dispatchParameters.cameraFar = pState->camera.GetFarPlane();
dispatchParameters.cameraNear = pState->camera.GetNearPlane();
dispatchParameters.cameraFovAngleVertical = pState->camera.GetFovV();
pState->bReset = false;
FfxErrorCode errorCode = ffxFsr2ContextDispatch(&context, &dispatchParameters);
FFX_ASSERT(errorCode == FFX_OK);
}

@ -0,0 +1,55 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#pragma once
#include "Base/Texture.h"
#include "Base/StaticBufferPool.h"
#include "Base/GBuffer.h"
#include "PostProc/PostProcCS.h"
#include "UI.h"
#include "UpscaleContext.h"
#include "../ffx-fsr2-api/ffx_fsr2.h"
using namespace CAULDRON_VK;
class UpscaleContext_FSR2_API : public UpscaleContext
{
public:
UpscaleContext_FSR2_API(UpscaleType type, std::string name);
virtual std::string Name() { return "FSR 2.0 API"; }
virtual void OnCreate(const FfxUpscaleInitParams& initParams);
virtual void OnDestroy();
virtual void OnCreateWindowSizeDependentResources(VkImageView input, VkImageView output, uint32_t renderWidth, uint32_t renderHeight, uint32_t displayWidth, uint32_t displayHeight, bool hdr);
virtual void OnDestroyWindowSizeDependentResources();
virtual void BuildDevUI(UIState* pState) override;
virtual void GenerateReactiveMask(VkCommandBuffer pCommandList, const FfxUpscaleSetup& cameraSetup, UIState* pState);
virtual void Draw(VkCommandBuffer commandBuffer, const FfxUpscaleSetup& cameraSetup, UIState* pState);
private:
FfxFsr2ContextDescription initializationParameters = {};
FfxFsr2Context context;
float memoryUsageInMegabytes = 0;
};

@ -0,0 +1,541 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "stdafx.h"
#include "UpscaleContext_Spatial.h"
// CAS
#define FFX_CPU
#include "shaders/ffx_core.h"
#include "shaders/ffx_fsr1.h"
#include <DirectXMath.h>
using namespace DirectX;
struct FSRConstants
{
XMUINT4 Const0;
XMUINT4 Const1;
XMUINT4 Const2;
XMUINT4 Const3;
XMUINT4 Sample;
};
// Upscaler using up to 3 passes:
// 1) optionally TAA
// 2) Spatial upscaler (point, bilinear, bicubic or EASU)
// 3) optionally RCAS
UpscaleContext_Spatial::UpscaleContext_Spatial(UpscaleType type, std::string name)
: UpscaleContext(name)
{
m_bUseRcas = true;
m_bHdr = false;
}
//--------------------------------------------------------------------------------------
//
// OnCreate
//
//--------------------------------------------------------------------------------------
void UpscaleContext_Spatial::OnCreate(const FfxUpscaleInitParams& initParams)
{
UpscaleContext::OnCreate(initParams);
bool slowFallback = false;
VkResult res;
{
// Create samplers
VkSamplerCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
info.magFilter = VK_FILTER_NEAREST;
info.minFilter = VK_FILTER_NEAREST;
info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
info.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
info.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
info.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
info.minLod = -1000;
info.maxLod = 1000;
info.maxAnisotropy = 1.0f;
res = vkCreateSampler(m_pDevice->GetDevice(), &info, NULL, &m_samplers[0]);
assert(res == VK_SUCCESS);
res = vkCreateSampler(m_pDevice->GetDevice(), &info, NULL, &m_samplers[1]);
assert(res == VK_SUCCESS);
res = vkCreateSampler(m_pDevice->GetDevice(), &info, NULL, &m_samplers[3]);
assert(res == VK_SUCCESS);
info.magFilter = VK_FILTER_LINEAR;
info.minFilter = VK_FILTER_LINEAR;
res = vkCreateSampler(m_pDevice->GetDevice(), &info, NULL, &m_samplers[4]);
assert(res == VK_SUCCESS);
info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
res = vkCreateSampler(m_pDevice->GetDevice(), &info, NULL, &m_samplers[2]);
assert(res == VK_SUCCESS);
}
{
// Create VkDescriptor Set Layout Bindings
//
std::vector<VkDescriptorSetLayoutBinding> m_TAAInputs(4 + 1 + 4);
for (int i = 0; i < 4; i++)
{
m_TAAInputs[i].binding = i;
m_TAAInputs[i].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
m_TAAInputs[i].descriptorCount = 1;
m_TAAInputs[i].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
m_TAAInputs[i].pImmutableSamplers = NULL;
}
m_TAAInputs[4].binding = 4;
m_TAAInputs[4].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
m_TAAInputs[4].descriptorCount = 1;
m_TAAInputs[4].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
m_TAAInputs[4].pImmutableSamplers = NULL;
for (int i = 5; i < 4 + 5; i++)
{
m_TAAInputs[i].binding = i;
m_TAAInputs[i].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER;
m_TAAInputs[i].descriptorCount = 1;
m_TAAInputs[i].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
m_TAAInputs[i].pImmutableSamplers = &m_samplers[i - 5];
}
m_ResourceViewHeaps.CreateDescriptorSetLayout(&m_TAAInputs, &m_TaaDescriptorSetLayout);
m_ResourceViewHeaps.AllocDescriptor(m_TaaDescriptorSetLayout, &m_TaaDescriptorSet[0]);
m_ResourceViewHeaps.AllocDescriptor(m_TaaDescriptorSetLayout, &m_TaaDescriptorSet[1]);
m_ResourceViewHeaps.AllocDescriptor(m_TaaDescriptorSetLayout, &m_TaaDescriptorSet[2]);
DefineList defines;
defines["UPSCALE_TYPE"] = std::to_string(UPSCALE_TYPE_NATIVE);
defines["SAMPLE_SLOW_FALLBACK"] = "1";
defines["SAMPLE_EASU"] = "0";
defines["SAMPLE_RCAS"] = "0";
defines["USE_WS_MOTIONVECTORS"] = "0";
m_taa.OnCreate(m_pDevice, "TAA.hlsl", "main", "-T cs_6_0", m_TaaDescriptorSetLayout, 16, 16, 1, &defines);
m_taaFirst.OnCreate(m_pDevice, "TAA.hlsl", "first", "-T cs_6_0", m_TaaDescriptorSetLayout, 16, 16, 1, &defines);
}
{
std::vector<VkDescriptorSetLayoutBinding> layoutBindings(5);
layoutBindings[0].binding = 0;
layoutBindings[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
layoutBindings[0].descriptorCount = 1;
layoutBindings[0].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
layoutBindings[0].pImmutableSamplers = nullptr;
layoutBindings[1].binding = 1;
layoutBindings[1].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
layoutBindings[1].descriptorCount = 1;
layoutBindings[1].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
layoutBindings[1].pImmutableSamplers = nullptr;
layoutBindings[2].binding = 2;
layoutBindings[2].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
layoutBindings[2].descriptorCount = 1;
layoutBindings[2].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
layoutBindings[2].pImmutableSamplers = nullptr;
layoutBindings[3].binding = 3;
layoutBindings[3].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER;
layoutBindings[3].descriptorCount = 1;
layoutBindings[3].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
layoutBindings[3].pImmutableSamplers = &m_samplers[0]; // Point
layoutBindings[4].binding = 4;
layoutBindings[4].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER;
layoutBindings[4].descriptorCount = 1;
layoutBindings[4].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
layoutBindings[4].pImmutableSamplers = &m_samplers[4]; // Linear
m_ResourceViewHeaps.CreateDescriptorSetLayout(&layoutBindings, &m_UpscaleDescriptorSetLayout);
m_ResourceViewHeaps.AllocDescriptor(m_UpscaleDescriptorSetLayout, &m_UpscaleDescriptorSet[0]);
m_ResourceViewHeaps.AllocDescriptor(m_UpscaleDescriptorSetLayout, &m_UpscaleDescriptorSet[1]);
m_ResourceViewHeaps.AllocDescriptor(m_UpscaleDescriptorSetLayout, &m_UpscaleDescriptorSet[2]);
DefineList defines;
defines["SAMPLE_SLOW_FALLBACK"] = (slowFallback ? "1" : "0");
defines["UPSCALE_TYPE"] = std::to_string(UPSCALE_TYPE_FSR_1_0);
defines["SAMPLE_RCAS"] = "1";
defines["DO_INVREINHARD"] = "0";
m_rcas.OnCreate(m_pDevice, "UpscaleSpatial.hlsl", "upscalePassCS", "-T cs_6_0", m_UpscaleDescriptorSetLayout, 64, 1, 1, &defines);
m_ResourceViewHeaps.AllocDescriptor(m_UpscaleDescriptorSetLayout, &m_RCASDescriptorSet[0]);
m_ResourceViewHeaps.AllocDescriptor(m_UpscaleDescriptorSetLayout, &m_RCASDescriptorSet[1]);
m_ResourceViewHeaps.AllocDescriptor(m_UpscaleDescriptorSetLayout, &m_RCASDescriptorSet[2]);
defines["UPSCALE_TYPE"] = std::to_string(m_Type);
defines["SAMPLE_EASU"] = "1";
defines["SAMPLE_RCAS"] = "0";
m_upscale.OnCreate(m_pDevice, "UpscaleSpatial.hlsl", "upscalePassCS", "-T cs_6_0", m_UpscaleDescriptorSetLayout, 64, 1, 1, &defines);
defines["DO_INVREINHARD"] = "1";
m_upscaleTAA.OnCreate(m_pDevice, "UpscaleSpatial.hlsl", "upscalePassCS", "-T cs_6_0", m_UpscaleDescriptorSetLayout, 64, 1, 1, &defines);
}
}
//--------------------------------------------------------------------------------------
//
// OnDestroy
//
//--------------------------------------------------------------------------------------
void UpscaleContext_Spatial::OnDestroy()
{
vkDestroySampler(m_pDevice->GetDevice(), m_samplers[0], nullptr);
vkDestroySampler(m_pDevice->GetDevice(), m_samplers[1], nullptr);
vkDestroySampler(m_pDevice->GetDevice(), m_samplers[2], nullptr);
vkDestroySampler(m_pDevice->GetDevice(), m_samplers[3], nullptr);
vkDestroySampler(m_pDevice->GetDevice(), m_samplers[4], nullptr);
m_samplers[0] = nullptr;
m_samplers[1] = nullptr;
m_samplers[2] = nullptr;
m_samplers[3] = nullptr;
m_samplers[4] = nullptr;
for (int i = 0; i < 3; i++)
{
m_ResourceViewHeaps.FreeDescriptor(m_TaaDescriptorSet[i]);
m_ResourceViewHeaps.FreeDescriptor(m_UpscaleDescriptorSet[i]);
m_ResourceViewHeaps.FreeDescriptor(m_RCASDescriptorSet[i]);
m_TaaDescriptorSet[i] = nullptr;
m_UpscaleDescriptorSet[i] = nullptr;
m_RCASDescriptorSet[i] = nullptr;
}
vkDestroyDescriptorSetLayout(m_pDevice->GetDevice(), m_TaaDescriptorSetLayout, nullptr);
vkDestroyDescriptorSetLayout(m_pDevice->GetDevice(), m_UpscaleDescriptorSetLayout, nullptr);
m_TaaDescriptorSetLayout = nullptr;
m_UpscaleDescriptorSetLayout = nullptr;
m_taa.OnDestroy();
m_taaFirst.OnDestroy();
m_upscale.OnDestroy();
m_upscaleTAA.OnDestroy();
m_rcas.OnDestroy();
UpscaleContext::OnDestroy();
}
//--------------------------------------------------------------------------------------
//
// OnCreateWindowSizeDependentResources
//
//--------------------------------------------------------------------------------------
void UpscaleContext_Spatial::OnCreateWindowSizeDependentResources(VkImageView input, VkImageView output, uint32_t renderWidth, uint32_t renderHeight, uint32_t displayWidth, uint32_t displayHeight, bool hdr)
{
UpscaleContext::OnCreateWindowSizeDependentResources(input, output, renderWidth, renderHeight, displayWidth, displayHeight, hdr);
// TAA buffers
//
m_TAAIntermediary[0].InitRenderTarget(m_pDevice, renderWidth, renderHeight, VK_FORMAT_R16G16B16A16_SFLOAT, VK_SAMPLE_COUNT_1_BIT, (VkImageUsageFlags)(VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT), false, "TaaIntermediary0");
m_TAAIntermediary[0].CreateSRV(&m_TAAIntermediarySrv[0]);
m_TAAIntermediary[1].InitRenderTarget(m_pDevice, renderWidth, renderHeight, VK_FORMAT_R16G16B16A16_SFLOAT, VK_SAMPLE_COUNT_1_BIT, (VkImageUsageFlags)(VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT), false, "TaaIntermediary1");
m_TAAIntermediary[1].CreateSRV(&m_TAAIntermediarySrv[1]);
// FSR buffer
//
m_FSRIntermediary.InitRenderTarget(m_pDevice, displayWidth, displayHeight, m_bHdr ? VK_FORMAT_A2R10G10B10_UNORM_PACK32 : VK_FORMAT_R8G8B8A8_UNORM, VK_SAMPLE_COUNT_1_BIT, (VkImageUsageFlags)(VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT), false, "FSR Intermediary");
m_FSRIntermediary.CreateSRV(&m_FSRIntermediarySrv);
m_bResetTaa = true;
}
//--------------------------------------------------------------------------------------
//
// OnDestroyWindowSizeDependentResources
//
//--------------------------------------------------------------------------------------
void UpscaleContext_Spatial::OnDestroyWindowSizeDependentResources()
{
vkDestroyImageView(m_pDevice->GetDevice(), m_TAAIntermediarySrv[0], 0);
vkDestroyImageView(m_pDevice->GetDevice(), m_TAAIntermediarySrv[1], 0);
vkDestroyImageView(m_pDevice->GetDevice(), m_FSRIntermediarySrv, 0);
m_TAAIntermediarySrv[0] = nullptr;
m_TAAIntermediarySrv[1] = nullptr;
m_FSRIntermediarySrv = nullptr;
m_TAAIntermediary[0].OnDestroy();
m_TAAIntermediary[1].OnDestroy();
m_FSRIntermediary.OnDestroy();
}
//--------------------------------------------------------------------------------------
//
// Draw: executes the Spatial upscale (optionally with additional TAA and sharpening passes)
//
//--------------------------------------------------------------------------------------
void UpscaleContext_Spatial::Draw(VkCommandBuffer commandBuffer, const FfxUpscaleSetup& cameraSetup, UIState* pState)
{
VkImageView curInput = cameraSetup.unresolvedColorResourceView;
VkImageView curOutput = cameraSetup.resolvedColorResourceView;
m_bUseRcas = pState->bUseRcas;
uint32_t currentDescriptorIndex = m_DescriptorSetIndex % 3;
// TAA
if (m_bUseTaa)
{
SetPerfMarkerBegin(commandBuffer, "TAA");
{
VkImageMemoryBarrier barriers[2] = {};
barriers[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barriers[0].pNext = NULL;
barriers[0].srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
barriers[0].dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
barriers[0].oldLayout = m_bResetTaa ? VK_IMAGE_LAYOUT_UNDEFINED : VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
barriers[0].newLayout = VK_IMAGE_LAYOUT_GENERAL;
barriers[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barriers[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barriers[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
barriers[0].subresourceRange.baseMipLevel = 0;
barriers[0].subresourceRange.levelCount = 1;
barriers[0].subresourceRange.baseArrayLayer = 0;
barriers[0].subresourceRange.layerCount = 1;
barriers[0].image = m_TAAIntermediary[(m_index + 1) & 1].Resource();
if (m_bResetTaa)
{
barriers[1] = barriers[0];
barriers[1].srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
barriers[1].dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
barriers[1].oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
barriers[1].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
barriers[1].image = m_TAAIntermediary[m_index & 1].Resource();
}
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, m_bResetTaa ? 2 : 1, barriers);
}
{
// set up constantbuffer
m_TaaConsts.g_PrevRenderResolution[0] = m_bInit ? m_renderWidth : m_TaaConsts.g_CurRenderResolution[0];
m_TaaConsts.g_PrevRenderResolution[1] = m_bInit ? m_renderHeight : m_TaaConsts.g_CurRenderResolution[1];
m_TaaConsts.g_PrevScreenResolution[0] = m_bInit ? m_displayWidth : m_TaaConsts.g_CurScreenResolution[0];
m_TaaConsts.g_PrevScreenResolution[1] = m_bInit ? m_displayHeight : m_TaaConsts.g_CurScreenResolution[1];
m_TaaConsts.g_CurRenderResolution[0] = pState->renderWidth;
m_TaaConsts.g_CurRenderResolution[1] = pState->renderHeight;
m_TaaConsts.g_CurScreenResolution[0] = m_displayWidth;
m_TaaConsts.g_CurScreenResolution[1] = m_displayHeight;
m_TaaConsts.g_ClosestVelocitySamplePattern = pState->closestVelocitySamplePattern;
m_TaaConsts.g_FeedbackDefault = pState->Feedback;
// set previous projection, remove jitter
m_TaaConsts.cameraPrev = m_TaaConsts.cameraCurr;
math::Vector4 projX = m_TaaConsts.cameraPrev.mCameraProj.getCol0();
math::Vector4 projY = m_TaaConsts.cameraPrev.mCameraProj.getCol1();
projX.setZ(0);
projY.setZ(0);
m_TaaConsts.cameraPrev.mCameraProj.setCol0(projX);
m_TaaConsts.cameraPrev.mCameraProj.setCol1(projY);
m_TaaConsts.cameraCurr.mCameraView = math::transpose(pState->camera.GetView());
m_TaaConsts.cameraCurr.mCameraProj = math::transpose(pState->camera.GetProjection());
m_TaaConsts.cameraCurr.mCameraViewInv = math::transpose(math::inverse(pState->camera.GetView()));
m_TaaConsts.cameraCurr.vCameraPos = pState->camera.GetPosition();
if (m_bInit) m_TaaConsts.cameraPrev = m_TaaConsts.cameraCurr;
}
{
SetDescriptorSet(m_pDevice->GetDevice(), 0, cameraSetup.unresolvedColorResourceView, NULL, m_TaaDescriptorSet[currentDescriptorIndex]);
SetDescriptorSet(m_pDevice->GetDevice(), 1, cameraSetup.depthbufferResourceView, NULL, m_TaaDescriptorSet[currentDescriptorIndex]);
SetDescriptorSet(m_pDevice->GetDevice(), 2, m_TAAIntermediarySrv[m_index & 1], NULL, m_TaaDescriptorSet[currentDescriptorIndex]);
SetDescriptorSet(m_pDevice->GetDevice(), 3, cameraSetup.motionvectorResourceView, NULL, m_TaaDescriptorSet[currentDescriptorIndex]);
SetDescriptorSet(m_pDevice->GetDevice(), 4, m_TAAIntermediarySrv[(m_index + 1) & 1], m_TaaDescriptorSet[currentDescriptorIndex]);
}
static const int threadGroupWorkRegionDim = 16;
int dispatchX = (pState->renderWidth + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
int dispatchY = (pState->renderHeight + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
if (m_bResetTaa)
{
m_taaFirst.Draw(commandBuffer, nullptr, m_TaaDescriptorSet[currentDescriptorIndex], dispatchX, dispatchY, 1);
m_bResetTaa = false;
}
else
{
m_taa.Draw(commandBuffer, nullptr, m_TaaDescriptorSet[currentDescriptorIndex], dispatchX, dispatchY, 1);
}
// set up taa target
curInput = m_TAAIntermediarySrv[(m_index + 1) & 1];
{
VkImageMemoryBarrier barriers[1] = {};
barriers[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barriers[0].pNext = NULL;
barriers[0].srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
barriers[0].dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
barriers[0].oldLayout = VK_IMAGE_LAYOUT_GENERAL;
barriers[0].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
barriers[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barriers[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barriers[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
barriers[0].subresourceRange.baseMipLevel = 0;
barriers[0].subresourceRange.levelCount = 1;
barriers[0].subresourceRange.baseArrayLayer = 0;
barriers[0].subresourceRange.layerCount = 1;
barriers[0].image = m_TAAIntermediary[(m_index + 1) & 1].Resource();
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, barriers);
}
SetPerfMarkerEnd(commandBuffer);
}
// Upscale
{
SetPerfMarkerBegin(commandBuffer, "Upscale");
VkImageView pFsrInput = curInput;
VkImageView pFsrOutput = nullptr;
if (m_bUseRcas)
{
// write to intermediary surface
pFsrOutput = m_FSRIntermediarySrv;
VkImageMemoryBarrier barriers[1] = {};
barriers[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barriers[0].pNext = NULL;
barriers[0].srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
barriers[0].dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
barriers[0].oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
barriers[0].newLayout = VK_IMAGE_LAYOUT_GENERAL;
barriers[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barriers[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barriers[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
barriers[0].subresourceRange.baseMipLevel = 0;
barriers[0].subresourceRange.levelCount = 1;
barriers[0].subresourceRange.baseArrayLayer = 0;
barriers[0].subresourceRange.layerCount = 1;
barriers[0].image = m_FSRIntermediary.Resource();
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, barriers);
}
else
{
// output to final
pFsrOutput = cameraSetup.resolvedColorResourceView;
}
VkDescriptorBufferInfo cbHandle = {};
{
FSRConstants consts = {};
ffxFsrPopulateEasuConstants(
reinterpret_cast<FfxUInt32*>(&consts.Const0),
reinterpret_cast<FfxUInt32*>(&consts.Const1),
reinterpret_cast<FfxUInt32*>(&consts.Const2),
reinterpret_cast<FfxUInt32*>(&consts.Const3),
static_cast<FfxFloat32>(pState->renderWidth), static_cast<FfxFloat32>(pState->renderHeight),
static_cast<FfxFloat32>(m_renderWidth), static_cast<FfxFloat32>(m_renderHeight),
static_cast<FfxFloat32>(pState->displayWidth), static_cast<FfxFloat32>(pState->displayHeight));
uint32_t* pConstMem = 0;
m_ConstantBufferRing.AllocConstantBuffer(sizeof(FSRConstants), (void**)&pConstMem, &cbHandle);
memcpy(pConstMem, &consts, sizeof(FSRConstants));
}
{
m_ConstantBufferRing.SetDescriptorSet(0, sizeof(FSRConstants), m_UpscaleDescriptorSet[currentDescriptorIndex]);
SetDescriptorSet(m_pDevice->GetDevice(), 1, pFsrInput, NULL, m_UpscaleDescriptorSet[currentDescriptorIndex]);
SetDescriptorSet(m_pDevice->GetDevice(), 2, pFsrOutput, m_UpscaleDescriptorSet[currentDescriptorIndex]);
}
// This value is the image region dimension that each thread group of the FSR shader operates on
static const int threadGroupWorkRegionDim = 16;
int dispatchX = (pState->displayWidth + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
int dispatchY = (pState->displayHeight + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
if (m_bUseTaa)
m_upscaleTAA.Draw(commandBuffer, &cbHandle, m_UpscaleDescriptorSet[currentDescriptorIndex], dispatchX, dispatchY, 1);
else
m_upscale.Draw(commandBuffer, &cbHandle, m_UpscaleDescriptorSet[currentDescriptorIndex], dispatchX, dispatchY, 1);
if (m_bUseRcas)
{
VkImageMemoryBarrier barriers[1] = {};
barriers[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barriers[0].pNext = NULL;
barriers[0].srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
barriers[0].dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
barriers[0].oldLayout = VK_IMAGE_LAYOUT_GENERAL;
barriers[0].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
barriers[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barriers[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barriers[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
barriers[0].subresourceRange.baseMipLevel = 0;
barriers[0].subresourceRange.levelCount = 1;
barriers[0].subresourceRange.baseArrayLayer = 0;
barriers[0].subresourceRange.layerCount = 1;
barriers[0].image = m_FSRIntermediary.Resource();
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, barriers);
curInput = pFsrOutput;
}
SetPerfMarkerEnd(commandBuffer);
}
// RCAS
if (pState->bUseRcas)
{
SetPerfMarkerBegin(commandBuffer, "RCAS");
VkDescriptorBufferInfo cbHandle = {};
{
FSRConstants consts = {};
FsrRcasCon(reinterpret_cast<FfxUInt32*>(&consts.Const0), pState->sharpening);
consts.Sample.x = (m_bHdr ? 1 : 0);
uint32_t* pConstMem = 0;
m_ConstantBufferRing.AllocConstantBuffer(sizeof(FSRConstants), (void**)&pConstMem, &cbHandle);
memcpy(pConstMem, &consts, sizeof(FSRConstants));
}
{
m_ConstantBufferRing.SetDescriptorSet(0, sizeof(FSRConstants), m_RCASDescriptorSet[currentDescriptorIndex]);
SetDescriptorSet(m_pDevice->GetDevice(), 1, curInput, NULL, m_RCASDescriptorSet[currentDescriptorIndex]);
SetDescriptorSet(m_pDevice->GetDevice(), 2, curOutput, m_RCASDescriptorSet[currentDescriptorIndex]);
}
static const int threadGroupWorkRegionDim = 16;
int dispatchX = (pState->displayWidth + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
int dispatchY = (pState->displayHeight + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
m_rcas.Draw(commandBuffer, &cbHandle, m_RCASDescriptorSet[currentDescriptorIndex], dispatchX, dispatchY, 1);
SetPerfMarkerEnd(commandBuffer);
}
m_DescriptorSetIndex++;
}

@ -0,0 +1,90 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#pragma once
#include "Base/Texture.h"
#include "Base/StaticBufferPool.h"
#include "Base/GBuffer.h"
#include "PostProc/PostProcCS.h"
#include "UI.h"
#include "UpscaleContext.h"
using namespace CAULDRON_VK;
class UpscaleContext_Spatial : public UpscaleContext
{
public:
UpscaleContext_Spatial(UpscaleType type, std::string name);
virtual std::string Name() { return "Spatial Upscale"; }
virtual void OnCreate(const FfxUpscaleInitParams& initParams);
virtual void OnDestroy();
virtual void OnCreateWindowSizeDependentResources(VkImageView input, VkImageView output, uint32_t renderWidth, uint32_t renderHeight, uint32_t displayWidth, uint32_t displayHeight, bool hdr);
virtual void OnDestroyWindowSizeDependentResources();
virtual void Draw(VkCommandBuffer commandBuffer, const FfxUpscaleSetup& cameraSetup, UIState* pState);
protected:
typedef struct _FfxTaaCB_
{
int g_PrevRenderResolution[2];
int g_PrevScreenResolution[2];
int g_CurRenderResolution[2];
int g_CurScreenResolution[2];
int g_taaMode;
int g_ClosestVelocitySamplePattern;
float g_FeedbackDefault;
int _pad0;
FfxCameraSetup cameraCurr;
FfxCameraSetup cameraPrev;
}FfxTaaCB;
bool m_bUseRcas;
bool m_bHdr;
FfxTaaCB m_TaaConsts;
VkSampler m_samplers[5];
uint32_t m_DescriptorSetIndex = 0;
VkDescriptorSet m_TaaDescriptorSet[3];
VkDescriptorSetLayout m_TaaDescriptorSetLayout;
VkDescriptorSet m_RCASDescriptorSet[3];
VkDescriptorSet m_UpscaleDescriptorSet[3];
VkDescriptorSetLayout m_UpscaleDescriptorSetLayout;
PostProcCS m_upscale;
PostProcCS m_upscaleTAA; // Cauldron TAA requires Inverse Reinhard after upscale path
PostProcCS m_rcas;
PostProcCS m_taa;
PostProcCS m_taaFirst;
Texture m_TAAIntermediary[2];
VkImageView m_TAAIntermediarySrv[2];
Texture m_FSRIntermediary;
VkImageView m_FSRIntermediarySrv;
bool m_bResetTaa = true;
};

@ -0,0 +1,248 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#define GROUP_SIZE 8
// These must match UpscaleType
#define UPSCALE_TYPE_POINT 0
#define UPSCALE_TYPE_BILINEAR 1
#define UPSCALE_TYPE_BICUBIC 2
#define UPSCALE_TYPE_FSR1 3
#define UPSCALE_TYPE_NATIVE 5
#if SAMPLE_EASU || SAMPLE_RCAS
[[vk::binding(0)]] cbuffer cb : register(b0)
{
uint4 Const0;
uint4 Const1;
uint4 Const2;
uint4 Const3;
uint4 Sample;
};
#endif
#define FFX_GPU 1
#define FFX_HLSL 1
[[vk::binding(3)]] SamplerState PointSampler : register(s0);
[[vk::binding(4)]] SamplerState LinearSampler : register(s1);
#if SAMPLE_SLOW_FALLBACK
#include "ffx_core.h"
[[vk::binding(1)]] Texture2D ColorBuffer : register(t0);
[[vk::binding(2)]] RWTexture2D<float4> OutputTexture : register(u0);
#if SAMPLE_EASU
#define FFX_FSR_EASU_FLOAT
FfxFloat32x4 FsrEasuRF(FfxFloat32x2 p) { FfxFloat32x4 res = ColorBuffer.GatherRed(LinearSampler, p, int2(0, 0)); return res; }
FfxFloat32x4 FsrEasuGF(FfxFloat32x2 p) { FfxFloat32x4 res = ColorBuffer.GatherGreen(LinearSampler, p, int2(0, 0)); return res; }
FfxFloat32x4 FsrEasuBF(FfxFloat32x2 p) { FfxFloat32x4 res = ColorBuffer.GatherBlue(LinearSampler, p, int2(0, 0)); return res; }
#endif
#if SAMPLE_RCAS
#define FSR_RCAS_F
FfxFloat32x4 FsrRcasLoadF(ASU2 p) { return ColorBuffer.Load(int3(ASU2(p), 0)); }
void FsrRcasInputF(inout FfxFloat32 r, inout FfxFloat32 g, inout FfxFloat32 b) {}
#endif
#else
#define FFX_HALF 1
#include "ffx_core.h"
[[vk::binding(1)]] Texture2D<FfxFloat16x4> ColorBuffer : register(t0);
[[vk::binding(2)]] RWTexture2D<FfxFloat16x4> OutputTexture : register(u0);
#if SAMPLE_EASU
#define FFX_FSR_EASU_HALF
FfxFloat16x4 FsrEasuRH(FfxFloat32x2 p) { FfxFloat16x4 res = ColorBuffer.GatherRed(LinearSampler, p, int2(0, 0)); return res; }
FfxFloat16x4 FsrEasuGH(FfxFloat32x2 p) { FfxFloat16x4 res = ColorBuffer.GatherGreen(LinearSampler, p, int2(0, 0)); return res; }
FfxFloat16x4 FsrEasuBH(FfxFloat32x2 p) { FfxFloat16x4 res = ColorBuffer.GatherBlue(LinearSampler, p, int2(0, 0)); return res; }
#endif
#if SAMPLE_RCAS
#define FSR_RCAS_H
FfxFloat16x4 FsrRcasLoadH(FfxInt16x2 p) { return ColorBuffer.Load(FfxInt16x3(FfxInt16x2 (p), 0)); }
void FsrRcasInputH(inout FfxFloat16 r, inout FfxFloat16 g, inout FfxFloat16 b) {}
#endif
#endif
#include "ffx_fsr1.h"
/**********************************************************************
MIT License
Copyright(c) 2019 MJP
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
********************************************************************/
float4 SampleHistoryCatmullRom(in float2 uv, in float2 texelSize)
{
// Source: https://gist.github.com/TheRealMJP/c83b8c0f46b63f3a88a5986f4fa982b1
// License: https://gist.github.com/TheRealMJP/bc503b0b87b643d3505d41eab8b332ae
// We're going to sample a a 4x4 grid of texels surrounding the target UV coordinate. We'll do this by rounding
// down the sample location to get the exact center of our "starting" texel. The starting texel will be at
// location [1, 1] in the grid, where [0, 0] is the top left corner.
float2 samplePos = uv / texelSize;
float2 texPos1 = floor(samplePos - 0.5f) + 0.5f;
// Compute the fractional offset from our starting texel to our original sample location, which we'll
// feed into the Catmull-Rom spline function to get our filter weights.
float2 f = samplePos - texPos1;
// Compute the Catmull-Rom weights using the fractional offset that we calculated earlier.
// These equations are pre-expanded based on our knowledge of where the texels will be located,
// which lets us avoid having to evaluate a piece-wise function.
float2 w0 = f * (-0.5f + f * (1.0f - 0.5f * f));
float2 w1 = 1.0f + f * f * (-2.5f + 1.5f * f);
float2 w2 = f * (0.5f + f * (2.0f - 1.5f * f));
float2 w3 = f * f * (-0.5f + 0.5f * f);
// Work out weighting factors and sampling offsets that will let us use bilinear filtering to
// simultaneously evaluate the middle 2 samples from the 4x4 grid.
float2 w12 = w1 + w2;
float2 offset12 = w2 / (w1 + w2);
// Compute the final UV coordinates we'll use for sampling the texture
float2 texPos0 = texPos1 - 1.0f;
float2 texPos3 = texPos1 + 2.0f;
float2 texPos12 = texPos1 + offset12;
texPos0 *= texelSize;
texPos3 *= texelSize;
texPos12 *= texelSize;
float4 result = 0.f;
result += ColorBuffer.SampleLevel(LinearSampler, float2(texPos0.x, texPos0.y), 0.0f) * w0.x * w0.y;
result += ColorBuffer.SampleLevel(LinearSampler, float2(texPos12.x, texPos0.y), 0.0f) * w12.x * w0.y;
result += ColorBuffer.SampleLevel(LinearSampler, float2(texPos3.x, texPos0.y), 0.0f) * w3.x * w0.y;
result += ColorBuffer.SampleLevel(LinearSampler, float2(texPos0.x, texPos12.y), 0.0f) * w0.x * w12.y;
result += ColorBuffer.SampleLevel(LinearSampler, float2(texPos12.x, texPos12.y), 0.0f) * w12.x * w12.y;
result += ColorBuffer.SampleLevel(LinearSampler, float2(texPos3.x, texPos12.y), 0.0f) * w3.x * w12.y;
result += ColorBuffer.SampleLevel(LinearSampler, float2(texPos0.x, texPos3.y), 0.0f) * w0.x * w3.y;
result += ColorBuffer.SampleLevel(LinearSampler, float2(texPos12.x, texPos3.y), 0.0f) * w12.x * w3.y;
result += ColorBuffer.SampleLevel(LinearSampler, float2(texPos3.x, texPos3.y), 0.0f) * w3.x * w3.y;
return max(result, 0.0f);
}
#if SAMPLE_EASU || SAMPLE_RCAS
float4 ReinhardInverse(in float4 sdr)
{
return float4( sdr.xyz / max(1.0f - sdr.xyz, 1e-5f), sdr.w);
}
FfxFloat16x4 ReinhardInverse(in FfxFloat16x4 sdr)
{
return FfxFloat16x4( sdr.xyz / max(FfxFloat16 (1.0f) - sdr.xyz, FfxFloat16 (1e-5f)), sdr.w);
}
void CurrFilter(int2 pos)
{
const float2 texelSize = FfxFloat32x2(1.f, -1.f) * asfloat(Const1.zw);
FfxFloat32x2 uv = (FfxFloat32x2(pos) * asfloat(Const0.xy) + asfloat(Const0.zw)) * asfloat(Const1.xy) + FfxFloat32x2(0.5, -0.5) * asfloat(Const1.zw);
#if SAMPLE_SLOW_FALLBACK
FfxFloat32x4 finalColor = 0.f;
#else
FfxFloat32x4 finalColor = 0.f;
#endif
#if (UPSCALE_TYPE == UPSCALE_TYPE_POINT) || (UPSCALE_TYPE == UPSCALE_TYPE_NATIVE)
finalColor = ColorBuffer.SampleLevel(PointSampler, uv, 0.0);
#elif UPSCALE_TYPE == UPSCALE_TYPE_BILINEAR
finalColor = ColorBuffer.SampleLevel(LinearSampler, uv, 0.0);
#elif UPSCALE_TYPE == UPSCALE_TYPE_BICUBIC
finalColor = SampleHistoryCatmullRom(uv, texelSize);
#elif UPSCALE_TYPE == UPSCALE_TYPE_FSR1
#if SAMPLE_EASU
#if SAMPLE_SLOW_FALLBACK
FsrEasuF(finalColor.xyz, pos, Const0, Const1, Const2, Const3);
#else
FsrEasuH(finalColor.xyz, pos, Const0, Const1, Const2, Const3);
#endif
#endif
#if SAMPLE_RCAS
#if SAMPLE_SLOW_FALLBACK
FsrRcasF(finalColor.r, finalColor.g, finalColor.b, pos, Const0);
#else
FsrRcasH(finalColor.r, finalColor.g, finalColor.b, pos, Const0);
#endif
if (Sample.x == 1)
finalColor.rgb *= finalColor.rgb;
#endif
finalColor.a = 1;
#endif
#if DO_INVREINHARD
OutputTexture[pos] = ReinhardInverse(finalColor);
#else
OutputTexture[pos] = finalColor;
#endif
}
[numthreads(WIDTH, HEIGHT, DEPTH)]
void upscalePassCS(uint3 LocalThreadId : SV_GroupThreadID, uint3 WorkGroupId : SV_GroupID, uint3 Dtid : SV_DispatchThreadID)
{
// Do remapping of local xy in workgroup for a more PS-like swizzle pattern.
FfxUInt32x2 gxy = ffxRemapForQuad(LocalThreadId.x) + FfxUInt32x2(WorkGroupId.x << 4u, WorkGroupId.y << 4u);
CurrFilter(gxy);
gxy.x += 8u;
CurrFilter(gxy);
gxy.y += 8u;
CurrFilter(gxy);
gxy.x -= 8u;
CurrFilter(gxy);
}
#else
[numthreads(WIDTH, HEIGHT, DEPTH)]
void BlitCS(uint3 globalID : SV_DispatchThreadID)
{
float4 srcColor = ColorBuffer[globalID.xy];
// remap channels
float4 dstColor = float4(srcColor[g_outChannelRed], srcColor[g_outChannelGreen], srcColor[g_outChannelBlue], 1.f);
// apply offset and scale
dstColor.rgb -= float3(outChannelRedMinMax.x, outChannelGreenMinMax.x, outChannelBlueMinMax.x);
dstColor.rgb /= float3(outChannelRedMinMax.y - outChannelRedMinMax.x, outChannelGreenMinMax.y - outChannelGreenMinMax.x, outChannelBlueMinMax.y - outChannelBlueMinMax.x);
#if FFX_HALF == 1
OutputTexture[globalID.xy] = FfxFloat16x4(dstColor);
#else
OutputTexture[globalID.xy] = float4(dstColor);
#endif
}
#endif

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/PM</dpiAware>
</windowsSettings>
</application>
</assembly>

@ -0,0 +1,31 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// stdafx.cpp : source file that includes just the standard includes
// ObjRendererD3D12.pch will be the pre-compiled header
// stdafx.obj will contain the pre-compiled type information
#include "stdafx.h"
// TODO: reference any additional headers you need in STDAFX.H
// and not in this file

@ -0,0 +1,89 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently, but
// are changed infrequently
//
#pragma once
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
// Windows Header Files:
#include <windows.h>
#include <windowsx.h>
// C RunTime Header Files
#include <malloc.h>
#include <map>
#include <vector>
#include <mutex>
#include <fstream>
#include "vulkan/vulkan.h"
// Pull in math library
#include "../../libs/vectormath/vectormath.hpp"
// TODO: reference additional headers your program requires here
#include "Base/Imgui.h"
#include "Base/ImguiHelper.h"
#include "Base/Device.h"
#include "Base/Helper.h"
#include "Base/Texture.h"
#include "Base/FrameworkWindows.h"
#include "Base/FreeSyncHDR.h"
#include "Base/SwapChain.h"
#include "Base/UploadHeap.h"
#include "Base/GPUTimeStamps.h"
#include "Base/CommandListRing.h"
#include "Base/StaticBufferPool.h"
#include "Base/DynamicBufferRing.h"
#include "Base/ResourceViewHeaps.h"
#include "Base/ShaderCompilerCache.h"
#include "Base/ShaderCompilerHelper.h"
#include "GLTF/GltfPbrPass.h"
#include "GLTF/GltfBBoxPass.h"
#include "GLTF/GltfDepthPass.h"
#include "Misc/Misc.h"
#include "Misc/Camera.h"
#include "PostProc/TAA.h"
#include "PostProc/Bloom.h"
#include "PostProc/BlurPS.h"
#include "PostProc/SkyDome.h"
#include "PostProc/ToneMapping.h"
#include "PostProc/ToneMappingCS.h"
#include "PostProc/ColorConversionPS.h"
#include "PostProc/SkyDomeProc.h"
#include "PostProc/DownSamplePS.h"
#include "Widgets/Axis.h"
#include "Widgets/CheckerBoardFloor.h"
#include "Widgets/WireframeBox.h"
#include "Widgets/WireframeSphere.h"
using namespace CAULDRON_VK;

@ -0,0 +1,101 @@
# This file is part of the FidelityFX SDK.
#
# Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
cmake_minimum_required(VERSION 3.15)
#set(CMAKE_CONFIGURATION_TYPES Debug Release)
set(CMAKE_DEBUG_POSTFIX d)
option (FFX_FSR2_API_DX12 "Build FSR 2.0 DX12 backend" ON)
option (FFX_FSR2_API_VK "Build FSR 2.0 Vulkan backend" ON)
set(FSR2_AUTO_COMPILE_SHADERS ON CACHE BOOL "Compile shaders automatically as a prebuild step.")
if(CMAKE_GENERATOR STREQUAL "Ninja")
set(USE_DEPFILE TRUE)
else()
set(USE_DEPFILE FALSE)
endif()
if(CMAKE_GENERATOR STREQUAL "Visual Studio 16 2019")
set(FSR2_VS_VERSION 2019)
endif()
if(CMAKE_GENERATOR_PLATFORM STREQUAL "x64" OR CMAKE_EXE_LINKER_FLAGS STREQUAL "/machine:x64")
set(FSR2_PLATFORM_NAME x64)
elseif(CMAKE_GENERATOR_PLATFORM STREQUAL "Win32" OR CMAKE_EXE_LINKER_FLAGS STREQUAL "/machine:X86")
set(FSR2_PLATFORM_NAME x86)
else()
message(FATAL_ERROR "Unsupported target platform - only supporting x64 and Win32 currently")
endif()
# Embed PDBs in the debug versions of the libs
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Z7")
# Write both debug and release versions of the static libs to the /lib folder as they are uniquely named
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${CMAKE_HOME_DIRECTORY}/bin/ffx_fsr2_api/)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE ${CMAKE_HOME_DIRECTORY}/bin/ffx_fsr2_api/)
add_compile_definitions(_UNICODE)
add_compile_definitions(UNICODE)
if(FSR2_VS_VERSION STREQUAL 2015)
message(NOTICE "Forcing the SDK path for VS 2015")
set(CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION "10.0.18362.0")
endif()
set(FFX_SC_EXECUTABLE
${CMAKE_CURRENT_SOURCE_DIR}/../../tools/sc/FidelityFX_SC.exe)
set(FFX_SC_BASE_ARGS
-reflection -deps=gcc -DFFX_GPU=1 -DOPT_PRECOMPUTE_REACTIVE_MAX=1)
set(FFX_SC_PERMUTATION_ARGS
-DFFX_FSR2_OPTION_USE_LANCZOS_LUT={0,1}
-DFFX_FSR2_OPTION_HDR_COLOR_INPUT={0,1}
-DFFX_FSR2_OPTION_LOW_RESOLUTION_MOTION_VECTORS={0,1}
-DFFX_FSR2_OPTION_JITTERED_MOTION_VECTORS={0,1}
-DFFX_FSR2_OPTION_INVERTED_DEPTH={0,1}
-DFFX_FSR2_OPTION_APPLY_SHARPENING={0,1})
file(GLOB SOURCES
"${CMAKE_CURRENT_SOURCE_DIR}/*.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/*.h")
if (FSR2_BUILD_AS_DLL)
add_library(ffx_fsr2_api_${FSR2_PLATFORM_NAME} SHARED ${SOURCES})
else()
add_library(ffx_fsr2_api_${FSR2_PLATFORM_NAME} STATIC ${SOURCES})
endif()
# graphics api backends
if(FFX_FSR2_API_DX12)
message("Will build FSR2 library: DX12 backend")
add_subdirectory(dx12)
endif()
if(FFX_FSR2_API_VK)
message("Will build FSR2 library: Vulkan backend")
add_subdirectory(vk)
endif()
# api
source_group("source" FILES ${SOURCES})
set_source_files_properties(${SHADERS} PROPERTIES HEADER_FILE_ONLY TRUE)

@ -0,0 +1,169 @@
# This file is part of the FidelityFX SDK.
#
# Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
if(NOT ${FFX_FSR2_API_DX12})
return()
endif()
set(FFX_SC_DX12_BASE_ARGS
-E CS -Wno-for-redefinition -Wno-ambig-lit-shift -Wno-conversion -DFFX_HLSL=1 -DFFX_HLSL_6_2=1)
file(GLOB SHADERS
"${CMAKE_CURRENT_SOURCE_DIR}/../shaders/*.h"
"${CMAKE_CURRENT_SOURCE_DIR}/../shaders/*.hlsl")
set(PASS_SHADERS
${CMAKE_CURRENT_SOURCE_DIR}/../shaders/ffx_fsr2_autogen_reactive_pass.hlsl
${CMAKE_CURRENT_SOURCE_DIR}/../shaders/ffx_fsr2_accumulate_pass.hlsl
${CMAKE_CURRENT_SOURCE_DIR}/../shaders/ffx_fsr2_compute_luminance_pyramid_pass.hlsl
${CMAKE_CURRENT_SOURCE_DIR}/../shaders/ffx_fsr2_depth_clip_pass.hlsl
${CMAKE_CURRENT_SOURCE_DIR}/../shaders/ffx_fsr2_lock_pass.hlsl
${CMAKE_CURRENT_SOURCE_DIR}/../shaders/ffx_fsr2_prepare_input_color_pass.hlsl
${CMAKE_CURRENT_SOURCE_DIR}/../shaders/ffx_fsr2_reconstruct_previous_depth_pass.hlsl
${CMAKE_CURRENT_SOURCE_DIR}/../shaders/ffx_fsr2_rcas_pass.hlsl)
file(GLOB_RECURSE DX12
"${CMAKE_CURRENT_SOURCE_DIR}/../ffx_assert.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/*.h"
"${CMAKE_CURRENT_SOURCE_DIR}/*.cpp")
if (FSR2_BUILD_AS_DLL)
add_library(ffx_fsr2_api_dx12_${FSR2_PLATFORM_NAME} SHARED ${DX12})
else()
add_library(ffx_fsr2_api_dx12_${FSR2_PLATFORM_NAME} STATIC ${DX12})
endif()
target_include_directories(ffx_fsr2_api_dx12_${FSR2_PLATFORM_NAME} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/../shaders/dx12)
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../shaders/dx12)
if (FSR2_AUTO_COMPILE_SHADERS)
set(FFX_SC_DEPENDENT_TARGET ffx_fsr2_api_dx12_${FSR2_PLATFORM_NAME})
else()
set(FFX_SC_DEPENDENT_TARGET ffx_fsr2_api_dx12_shaders_${FSR2_PLATFORM_NAME})
add_custom_target(${FFX_SC_DEPENDENT_TARGET})
endif()
if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.20.0")
cmake_policy(SET CMP0116 OLD)
endif()
get_filename_component(PASS_SHADER_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}/../shaders/dx12 ABSOLUTE)
foreach(PASS_SHADER ${PASS_SHADERS})
get_filename_component(PASS_SHADER_FILENAME ${PASS_SHADER} NAME_WE)
get_filename_component(PASS_SHADER_TARGET ${PASS_SHADER} NAME_WLE)
set(WAVE32_PERMUTATION_HEADER ${PASS_SHADER_OUTPUT_PATH}/${PASS_SHADER_TARGET}_permutations.h)
set(WAVE64_PERMUTATION_HEADER ${PASS_SHADER_OUTPUT_PATH}/${PASS_SHADER_TARGET}_wave64_permutations.h)
set(WAVE32_16BIT_PERMUTATION_HEADER ${PASS_SHADER_OUTPUT_PATH}/${PASS_SHADER_TARGET}_16bit_permutations.h)
set(WAVE64_16BIT_PERMUTATION_HEADER ${PASS_SHADER_OUTPUT_PATH}/${PASS_SHADER_TARGET}_wave64_16bit_permutations.h)
# combine base and permutation args
set(FFX_SC_ARGS ${FFX_SC_BASE_ARGS} ${FFX_SC_DX12_BASE_ARGS} ${FFX_SC_PERMUTATION_ARGS})
if (USE_DEPFILE)
# Wave32
add_custom_command(
OUTPUT ${WAVE32_PERMUTATION_HEADER}
COMMAND ${FFX_SC_EXECUTABLE} ${FFX_SC_ARGS} -name=${PASS_SHADER_FILENAME} -DFFX_HALF=0 -T cs_6_2 -I ${CMAKE_CURRENT_SOURCE_DIR}/../shaders -output=${PASS_SHADER_OUTPUT_PATH} ${PASS_SHADER}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
DEPENDS ${PASS_SHADER}
DEPFILE ${WAVE32_PERMUTATION_HEADER}.d
)
list(APPEND PERMUTATION_OUTPUTS ${WAVE32_PERMUTATION_HEADER})
# Wave64
add_custom_command(
OUTPUT ${WAVE64_PERMUTATION_HEADER}
COMMAND ${FFX_SC_EXECUTABLE} ${FFX_SC_ARGS} -name=${PASS_SHADER_FILENAME}_wave64 "-DFFX_FSR2_PREFER_WAVE64=\"[WaveSize(64)]\"" -DFFX_HALF=0 -T cs_6_6 -I ${CMAKE_CURRENT_SOURCE_DIR}/../shaders -output=${PASS_SHADER_OUTPUT_PATH} ${PASS_SHADER}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
DEPENDS ${PASS_SHADER}
DEPFILE ${WAVE64_PERMUTATION_HEADER}.d
)
list(APPEND PERMUTATION_OUTPUTS ${WAVE64_PERMUTATION_HEADER})
# skip 16-bit permutations for the compute luminance pyramid pass
if (NOT ${PASS_SHADER_FILENAME} STREQUAL "ffx_fsr2_compute_luminance_pyramid_pass")
# Wave32 16-bit
add_custom_command(
OUTPUT ${WAVE32_16BIT_PERMUTATION_HEADER}
COMMAND ${FFX_SC_EXECUTABLE} ${FFX_SC_ARGS} -name=${PASS_SHADER_FILENAME}_16bit -DFFX_HALF=1 -enable-16bit-types -T cs_6_2 -I ${CMAKE_CURRENT_SOURCE_DIR}/../shaders -output=${PASS_SHADER_OUTPUT_PATH} ${PASS_SHADER}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
DEPENDS ${PASS_SHADER}
DEPFILE ${WAVE32_16BIT_PERMUTATION_HEADER}.d
)
list(APPEND PERMUTATION_OUTPUTS ${WAVE32_16BIT_PERMUTATION_HEADER})
# Wave64 16-bit
add_custom_command(
OUTPUT ${WAVE64_16BIT_PERMUTATION_HEADER}
COMMAND ${FFX_SC_EXECUTABLE} ${FFX_SC_ARGS} -name=${PASS_SHADER_FILENAME}_wave64_16bit "-DFFX_FSR2_PREFER_WAVE64=\"[WaveSize(64)]\"" -DFFX_HALF=1 -enable-16bit-types -T cs_6_6 -I ${CMAKE_CURRENT_SOURCE_DIR}/../shaders -output=${PASS_SHADER_OUTPUT_PATH} ${PASS_SHADER}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
DEPENDS ${PASS_SHADER}
DEPFILE ${WAVE64_16BIT_PERMUTATION_HEADER}.d
)
list(APPEND PERMUTATION_OUTPUTS ${WAVE64_16BIT_PERMUTATION_HEADER})
endif()
else()
# Wave32
add_custom_command(
OUTPUT ${WAVE32_PERMUTATION_HEADER}
COMMAND ${FFX_SC_EXECUTABLE} ${FFX_SC_ARGS} -name=${PASS_SHADER_FILENAME} -DFFX_HALF=0 -T cs_6_2 -I ${CMAKE_CURRENT_SOURCE_DIR}/../shaders -output=${PASS_SHADER_OUTPUT_PATH} ${PASS_SHADER}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
DEPENDS ${PASS_SHADER}
)
list(APPEND PERMUTATION_OUTPUTS ${WAVE32_PERMUTATION_HEADER})
# Wave64
add_custom_command(
OUTPUT ${WAVE64_PERMUTATION_HEADER}
COMMAND ${FFX_SC_EXECUTABLE} ${FFX_SC_ARGS} -name=${PASS_SHADER_FILENAME}_wave64 "-DFFX_FSR2_PREFER_WAVE64=\"[WaveSize(64)]\"" -DFFX_HALF=0 -T cs_6_6 -I ${CMAKE_CURRENT_SOURCE_DIR}/../shaders -output=${PASS_SHADER_OUTPUT_PATH} ${PASS_SHADER}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
DEPENDS ${PASS_SHADER}
)
list(APPEND PERMUTATION_OUTPUTS ${WAVE64_PERMUTATION_HEADER})
# Skip 16-bit permutations for the compute luminance pyramid pass
if (NOT ${PASS_SHADER_FILENAME} STREQUAL "ffx_fsr2_compute_luminance_pyramid_pass")
# Wave32 16-bit
add_custom_command(
OUTPUT ${WAVE32_16BIT_PERMUTATION_HEADER}
COMMAND ${FFX_SC_EXECUTABLE} ${FFX_SC_ARGS} -name=${PASS_SHADER_FILENAME}_16bit -DFFX_HALF=1 -enable-16bit-types -T cs_6_2 -I ${CMAKE_CURRENT_SOURCE_DIR}/../shaders -output=${PASS_SHADER_OUTPUT_PATH} ${PASS_SHADER}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
DEPENDS ${PASS_SHADER}
)
list(APPEND PERMUTATION_OUTPUTS ${WAVE32_16BIT_PERMUTATION_HEADER})
# Wave64 16-bit
add_custom_command(
OUTPUT ${WAVE64_16BIT_PERMUTATION_HEADER}
COMMAND ${FFX_SC_EXECUTABLE} ${FFX_SC_ARGS} -name=${PASS_SHADER_FILENAME}_wave64_16bit "-DFFX_FSR2_PREFER_WAVE64=\"[WaveSize(64)]\"" -DFFX_HALF=1 -enable-16bit-types -T cs_6_6 -I ${CMAKE_CURRENT_SOURCE_DIR}/../shaders -output=${PASS_SHADER_OUTPUT_PATH} ${PASS_SHADER}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
DEPENDS ${PASS_SHADER}
)
list(APPEND PERMUTATION_OUTPUTS ${WAVE64_16BIT_PERMUTATION_HEADER})
endif()
endif()
endforeach(PASS_SHADER)
add_custom_target(shader_permutations_dx12 DEPENDS ${PERMUTATION_OUTPUTS})
add_dependencies(${FFX_SC_DEPENDENT_TARGET} shader_permutations_dx12)
source_group("source" FILES ${DX12})
source_group("shaders" FILES ${SHADERS})

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -0,0 +1,114 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// This file contains function declarations to convert DX12 resources
// to and from API independent FFX resources.
// @defgroup DX12
#pragma once
#include <d3d12.h>
#include "../ffx_fsr2_interface.h"
#if defined(__cplusplus)
extern "C" {
#endif // #if defined(__cplusplus)
/// Query how much memory is required for the DirectX 12 backend's scratch buffer.
///
/// @returns
/// The size (in bytes) of the required scratch memory buffer for the DX12 backend.
FFX_API size_t ffxFsr2GetScratchMemorySizeDX12();
/// Populate an interface with pointers for the DX12 backend.
///
/// @param [out] fsr2Interface A pointer to a <c><i>FfxFsr2Interface</i></c> structure to populate with pointers.
/// @param [in] device A pointer to the DirectX12 device.
/// @param [in] scratchBuffer A pointer to a buffer of memory which can be used by the DirectX(R)12 backend.
/// @param [in] scratchBufferSize The size (in bytes) of the buffer pointed to by <c><i>scratchBuffer</i></c>.
///
/// @retval
/// FFX_OK The operation completed successfully.
/// @retval
/// FFX_ERROR_CODE_INVALID_POINTER The <c><i>interface</i></c> pointer was <c><i>NULL</i></c>.
///
/// @ingroup FSR2 DX12
FFX_API FfxErrorCode ffxFsr2GetInterfaceDX12(
FfxFsr2Interface* fsr2Interface,
ID3D12Device* device,
void* scratchBuffer,
size_t scratchBufferSize);
/// Create a <c><i>FfxFsr2Device</i></c> from a <c><i>ID3D12Device</i></c>.
///
/// @param [in] device A pointer to the DirectX12 device.
///
/// @returns
/// An abstract FidelityFX device.
///
/// @ingroup FSR2 DX12
FFX_API FfxDevice ffxGetDeviceDX12(ID3D12Device* device);
/// Create a <c><i>FfxCommandList</i></c> from a <c><i>ID3D12CommandList</i></c>.
///
/// @param [in] cmdList A pointer to the DirectX12 command list.
///
/// @returns
/// An abstract FidelityFX command list.
///
/// @ingroup FSR2 DX12
FFX_API FfxCommandList ffxGetCommandListDX12(ID3D12CommandList* cmdList);
/// Create a <c><i>FfxResource</i></c> from a <c><i>ID3D12Resource</i></c>.
///
/// @param [in] fsr2Interface A pointer to a <c><i>FfxFsr2Interface</i></c> structure.
/// @param [in] resDx12 A pointer to the DirectX12 resource.
/// @param [in] name (optional) A name string to identify the resource in debug mode.
/// @param [in] state The state the resource is currently in.
/// @param [in] shaderComponentMapping The shader component mapping.
///
/// @returns
/// An abstract FidelityFX resources.
///
/// @ingroup FSR2 DX12
FFX_API FfxResource ffxGetResourceDX12(
FfxFsr2Context* context,
ID3D12Resource* resDx12,
wchar_t* name = nullptr,
FfxResourceStates state = FFX_RESOURCE_STATE_COMPUTE_READ,
UINT shaderComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING);
/// Retrieve a <c><i>ID3D12Resource</i></c> pointer associated with a UAV RESOURCE_IDENTIFIER.
/// Used for debug purposes when blitting internal surfaces.
///
/// @param [in] context A pointer to a <c><i>FfxFsr2Context</i></c> structure.
/// @param [in] resId A resourceID.
///
/// @returns
/// A <c><i>ID3D12Resource</i> pointer</c>.
///
/// @ingroup FSR2 DX12
FFX_API ID3D12Resource* ffxGetDX12ResourcePtr(FfxFsr2Context* context, uint32_t resId);
#if defined(__cplusplus)
}
#endif // #if defined(__cplusplus)

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015 Microsoft
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

Some files were not shown because too many files have changed in this diff Show More