Merge remote-tracking branch 'origin/master' into navigation-features

This commit is contained in:
Wojtek Figat
2020-12-22 12:13:26 +01:00
68 changed files with 1006 additions and 2106 deletions

View File

@@ -1,3 +1,4 @@
# Redirect to our own Git LFS server
[lfs]
url="https://gitlab.flaxengine.com/flax/flaxengine.git/info/lfs"
locksverify = false

BIN
Content/Shaders/AtmospherePreCompute.flax (Stored with Git LFS)

Binary file not shown.

BIN
Content/Shaders/ColorGrading.flax (Stored with Git LFS)

Binary file not shown.

BIN
Content/Shaders/Fog.flax (Stored with Git LFS)

Binary file not shown.

BIN
Content/Shaders/Histogram.flax (Stored with Git LFS)

Binary file not shown.

BIN
Content/Shaders/MotionBlur.flax (Stored with Git LFS)

Binary file not shown.

BIN
Content/Shaders/PostProcessing.flax (Stored with Git LFS)

Binary file not shown.

BIN
Content/Shaders/ProbesFilter.flax (Stored with Git LFS)

Binary file not shown.

BIN
Content/Shaders/VolumetricFog.flax (Stored with Git LFS)

Binary file not shown.

View File

@@ -2,93 +2,56 @@
rem Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
rem Make sure the batch file exists in the root folder.
if not exist "Development\Scripts\Windows\GetMSBuildPath.bat" goto Error_BatchFileInWrongLocation
if not exist "Development\Scripts\Windows\GetMSBuildPath.bat" goto Error_InvalidLocation
rem Get the path to MSBuild executable.
call "Development\Scripts\Windows\GetMSBuildPath.bat"
if errorlevel 1 goto Error_NoVisualStudioEnvironment
rem If using VS2017, check that NuGet package manager is installed.
if not exist "%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" goto NoVsWhere
set MSBUILD_15_EXE=
if not exist "%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" goto Compile
for /f "delims=" %%i in ('"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere" -latest -products * -requires Microsoft.Component.MSBuild -property installationPath') do (
for %%j in (15.0, Current) do (
if exist "%%i\MSBuild\%%j\Bin\MSBuild.exe" (
set MSBUILD_15_EXE="%%i\MSBuild\%%j\Bin\MSBuild.exe"
goto FoundMsBuild15
set MSBUILD_PATH="%%i\MSBuild\%%j\Bin\MSBuild.exe"
goto Compile
)
)
)
:FoundMsBuild15
set MSBUILD_15_EXE_WITH_NUGET=
for /f "delims=" %%i in ('"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere" -latest -products * -requires Microsoft.Component.MSBuild -requires Microsoft.VisualStudio.Component.NuGet -property installationPath') do (
if exist "%%i\MSBuild\15.0\Bin\MSBuild.exe" (
set MSBUILD_15_EXE_WITH_NUGET="%%i\MSBuild\15.0\Bin\MSBuild.exe"
goto FoundMsBuild15WithNuget
)
)
:FoundMsBuild15WithNuget
if not [%MSBUILD_15_EXE%] == [] (
set MSBUILD_EXE=%MSBUILD_15_EXE%
goto NoVsWhere
)
if not [%MSBUILD_EXE%] == [%MSBUILD_15_EXE_WITH_NUGET%] goto Error_RequireNugetPackageManager
:NoVsWhere
rem Check to see if the build tool source files have changed. Some can be included conditionally based on NDA access.
:Compile
md Cache\Intermediate >nul 2>nul
dir /s /b Source\Tools\Flax.Build\*.cs >Cache\Intermediate\Flax.Build.Files.txt
fc /b Cache\Intermediate\Build\Flax.Build.Files.txt Cache\Intermediate\Build\Flax.Build.PrevFiles.txt >nul 2>nul
if not errorlevel 1 goto SkipClean
copy /y Cache\Intermediate\Build\Flax.Build.Files.txt Cache\Intermediate\Build\Flax.Build.PrevFiles.txt >nul
%MSBUILD_EXE% /nologo /verbosity:quiet Source\Tools\Flax.Build\Flax.Build.csproj /property:Configuration=Release /property:Platform=AnyCPU /target:Clean
:SkipClean
%MSBUILD_EXE% /nologo /verbosity:quiet Source\Tools\Flax.Build\Flax.Build.csproj /property:Configuration=Release /property:Platform=AnyCPU /target:Build
if errorlevel 1 goto Error_CompileFailed
rem Run the build tool using the provided arguments.
copy /y Cache\Intermediate\Build\Flax.Build.Files.txt Cache\Intermediate\Build\Flax.Build.PrevFiles.txt >nul
%MSBUILD_PATH% /nologo /verbosity:quiet Source\Tools\Flax.Build\Flax.Build.csproj /property:Configuration=Release /property:Platform=AnyCPU /target:Clean
:SkipClean
%MSBUILD_PATH% /nologo /verbosity:quiet Source\Tools\Flax.Build\Flax.Build.csproj /property:Configuration=Release /property:Platform=AnyCPU /target:Build
if errorlevel 1 goto Error_CompilationFailed
Binaries\Tools\Flax.Build.exe %*
if errorlevel 1 goto Error_FlaxBuildFailed
rem Done.
exit /B 0
:Error_BatchFileInWrongLocation
:Error_InvalidLocation
echo.
echo CallBuildTool ERROR: The batch file does not appear to be located in the root directory. This script must be run from within that directory.
echo CallBuildTool ERROR: The script is in invalid directory.
echo.
goto Exit
:Error_NoVisualStudioEnvironment
echo.
echo CallBuildTool ERROR: We couldn't find a valid installation of Visual Studio. This program requires Visual Studio 2015. Please check that you have Visual Studio installed, then verify that the HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\14.0\InstallDir (or HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\14.0\InstallDir on 32-bit machines) registry value is set. Visual Studio configures this value when it is installed, and this program expects it to be set to the '\Common7\IDE\' sub-folder under a valid Visual Studio installation directory.
echo CallBuildTool ERROR: Missing Visual Studio 2015 or newer.
echo.
goto Exit
:Error_RequireNugetPackageManager
echo.
echo CallBuildTool ERROR: NuGet Package Manager is requried to be installed to use %MSBUILD_EXE%. Please run the Visual Studio Installer and add it from the individual components list (in the 'Code Tools' category).
echo.
goto Exit
:Error_CompileFailed
:Error_CompilationFailed
echo.
echo CallBuildTool ERROR: Failed to compile Flax.Build project.
echo.
goto Exit
:Error_FlaxBuildFailed
echo.
echo CallBuildTool ERROR: Flax.Build tool failed.
echo.
goto Exit
:Exit
rem Exit with error.
exit /B 1

View File

@@ -2,72 +2,57 @@
rem Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
set MSBUILD_EXE=
set MSBUILD_PATH=
rem Try to get the MSBuild 15 path using vswhere (see https://github.com/Microsoft/vswhere).
if not exist "%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" goto no_vswhere
if not exist "%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" goto VsWhereNotFound
for /f "delims=" %%i in ('"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere" -latest -products * -requires Microsoft.Component.MSBuild -property installationPath') do (
if exist "%%i\MSBuild\15.0\Bin\MSBuild.exe" (
set MSBUILD_EXE="%%i\MSBuild\15.0\Bin\MSBuild.exe"
goto Succeeded
set MSBUILD_PATH="%%i\MSBuild\15.0\Bin\MSBuild.exe"
goto End
)
)
:no_vswhere
:VsWhereNotFound
rem Check for MSBuild 15. This is installed alongside Visual Studio 2017, so we get the path relative to that.
call :ReadInstallPath Microsoft\VisualStudio\SxS\VS7 15.0 MSBuild\15.0\bin\MSBuild.exe
if not errorlevel 1 goto Succeeded
rem Try to get the MSBuild 14.0 path directly (see https://msdn.microsoft.com/en-us/library/hh162058(v=vs.120).aspx).
if exist "%ProgramFiles(x86)%\MSBuild\14.0\bin\MSBuild.exe" (
set MSBUILD_EXE="%ProgramFiles(x86)%\MSBuild\14.0\bin\MSBuild.exe"
goto Succeeded
set MSBUILD_PATH="%ProgramFiles(x86)%\MSBuild\14.0\bin\MSBuild.exe"
goto End
)
rem Try to get the MSBuild 14.0 path from the registry.
call :ReadInstallPath Microsoft\MSBuild\ToolsVersions\14.0 MSBuildToolsPath MSBuild.exe
if not errorlevel 1 goto Succeeded
call :GetInstallPath Microsoft\VisualStudio\SxS\VS7 15.0 MSBuild\15.0\bin\MSBuild.exe
if not errorlevel 1 goto End
call :GetInstallPath Microsoft\MSBuild\ToolsVersions\14.0 MSBuildToolsPath MSBuild.exe
if not errorlevel 1 goto End
call :GetInstallPath Microsoft\MSBuild\ToolsVersions\12.0 MSBuildToolsPath MSBuild.exe
if not errorlevel 1 goto End
call :GetInstallPath Microsoft\MSBuild\ToolsVersions\4.0 MSBuildToolsPath MSBuild.exe
if not errorlevel 1 goto End
rem Check for older versions of MSBuild. These are registered as separate versions in the registry.
call :ReadInstallPath Microsoft\MSBuild\ToolsVersions\12.0 MSBuildToolsPath MSBuild.exe
if not errorlevel 1 goto Succeeded
call :ReadInstallPath Microsoft\MSBuild\ToolsVersions\4.0 MSBuildToolsPath MSBuild.exe
if not errorlevel 1 goto Succeeded
rem Searching failed.
exit /B 1
rem Searching done!
:Succeeded
:End
exit /B 0
rem Subroutine to query the registry under HKCU/HKLM Win32/Wow64 software registry keys for a certain install directory.
rem Arguments:
rem %1 = Registry path under the 'SOFTWARE' registry key
rem %2 = Value name
rem %3 = Relative path under this directory to look for an installed executable.
:ReadInstallPath
:GetInstallPath
for /f "tokens=2,*" %%A in ('REG.exe query HKCU\SOFTWARE\%1 /v %2 2^>Nul') do (
if exist "%%B%%3" (
set MSBUILD_EXE="%%B%3"
set MSBUILD_PATH="%%B%3"
exit /B 0
)
)
for /f "tokens=2,*" %%A in ('REG.exe query HKLM\SOFTWARE\%1 /v %2 2^>Nul') do (
if exist "%%B%3" (
set MSBUILD_EXE="%%B%3"
set MSBUILD_PATH="%%B%3"
exit /B 0
)
)
for /f "tokens=2,*" %%A in ('REG.exe query HKCU\SOFTWARE\Wow6432Node\%1 /v %2 2^>Nul') do (
if exist "%%B%%3" (
set MSBUILD_EXE="%%B%3"
set MSBUILD_PATH="%%B%3"
exit /B 0
)
)
for /f "tokens=2,*" %%A in ('REG.exe query HKLM\SOFTWARE\Wow6432Node\%1 /v %2 2^>Nul') do (
if exist "%%B%3" (
set MSBUILD_EXE="%%B%3"
set MSBUILD_PATH="%%B%3"
exit /B 0
)
)

View File

@@ -3,7 +3,7 @@
"Version": {
"Major": 1,
"Minor": 0,
"Build": 6213
"Build": 6214
},
"Company": "Flax",
"Copyright": "Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.",

View File

@@ -3,7 +3,7 @@
rem Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
setlocal
pushd
pushd %~dp0
echo Registering Flax Engine project files...
rem Check the current versions config
@@ -20,7 +20,7 @@ goto Done
rem Register the location (append to the end)
:notfound
echo Location '%EngineLocation%' is not registered. Adding it to the lsit of engine versions.
echo Location '%EngineLocation%' is not registered. Adding it to the list of engine versions.
echo %EngineLocation%>>"%appdata%\Flax\Versions.txt"
goto Done

View File

@@ -122,6 +122,25 @@ namespace FlaxEditor.GUI.ContextMenu
/// </summary>
public Panel ItemsContainer => _panel;
/// <summary>
/// The auto sort.
/// </summary>
private bool _autosort;
/// <summary>
/// The auto sort property.
/// </summary>
public bool AutoSort
{
get => _autosort;
set
{
_autosort = value;
if (_autosort)
SortButtons();
}
}
/// <summary>
/// Initializes a new instance of the <see cref="ContextMenu"/> class.
/// </summary>
@@ -137,6 +156,24 @@ namespace FlaxEditor.GUI.ContextMenu
};
}
/// <summary>
/// Sorts all <see cref="ContextMenuButton"/> alphabetically.
/// </summary>
/// <param name="force">Overrides <see cref="AutoSort"/> property.</param>
public void SortButtons(bool force = false)
{
if (!_autosort && !force)
return;
_panel.Children.Sort((control, control1) =>
{
if (control is ContextMenuButton cmb && control1 is ContextMenuButton cmb1)
return string.Compare(cmb.Text, cmb1.Text, StringComparison.OrdinalIgnoreCase);
if (!(control is ContextMenuButton))
return 1;
return -1;
});
}
/// <summary>
/// Removes all the added items (buttons, separators, etc.).
/// </summary>
@@ -158,6 +195,7 @@ namespace FlaxEditor.GUI.ContextMenu
{
var item = new ContextMenuButton(this, text);
item.Parent = _panel;
SortButtons();
return item;
}
@@ -171,6 +209,7 @@ namespace FlaxEditor.GUI.ContextMenu
{
var item = new ContextMenuButton(this, text, shortKeys);
item.Parent = _panel;
SortButtons();
return item;
}
@@ -185,6 +224,7 @@ namespace FlaxEditor.GUI.ContextMenu
var item = new ContextMenuButton(this, text);
item.Parent = _panel;
item.Clicked += clicked;
SortButtons();
return item;
}
@@ -199,6 +239,7 @@ namespace FlaxEditor.GUI.ContextMenu
var item = new ContextMenuButton(this, text);
item.Parent = _panel;
item.ButtonClicked += clicked;
SortButtons();
return item;
}
@@ -214,6 +255,7 @@ namespace FlaxEditor.GUI.ContextMenu
var item = new ContextMenuButton(this, text, shortKeys);
item.Parent = _panel;
item.Clicked += clicked;
SortButtons();
return item;
}

View File

@@ -136,6 +136,10 @@ namespace FlaxEditor.GUI.ContextMenu
// Direction: up
isUp = true;
locationSS.Y -= dpiSize.Y;
// Offset to fix sub-menu location
if (parent is ContextMenu menu && menu._childCM != null)
locationSS.Y += 30.0f * dpiScale;
}
if (monitorBounds.Right < rightBottomLocationSS.X)
{

View File

@@ -193,41 +193,64 @@ namespace FlaxEditor.GUI
if (!_window.IsMaximized)
{
var winSize = RootWindow.Size * Platform.DpiScale;
if (pos.Y > winSize.Y - 5 && pos.X < 5)
/*
* Distance from which the mouse is considered to be on the border/corner
* You can change the distance by modifying the x value in : (int)(x * Platform.DpiScale)
*/
int distance = (int)(10 * Platform.DpiScale);
if (pos.Y > winSize.Y - distance && pos.X < distance)
return WindowHitCodes.BottomLeft;
if (pos.X > winSize.X - 5 && pos.Y > winSize.Y - 5)
if (pos.X > winSize.X - distance && pos.Y > winSize.Y - distance)
return WindowHitCodes.BottomRight;
if (pos.Y < 5 && pos.X < 5)
if (pos.Y < distance && pos.X < distance)
return WindowHitCodes.TopLeft;
if (pos.Y < 5 && pos.X > winSize.X - 5)
if (pos.Y < distance && pos.X > winSize.X - distance)
return WindowHitCodes.TopRight;
if (pos.X > winSize.X - 5)
if (pos.X > winSize.X - distance)
return WindowHitCodes.Right;
if (pos.X < 5)
if (pos.X < distance)
return WindowHitCodes.Left;
if (pos.Y < 5)
if (pos.Y < distance)
return WindowHitCodes.Top;
if (pos.Y > winSize.Y - 5)
if (pos.Y > winSize.Y - distance)
return WindowHitCodes.Bottom;
}
var menuPos = PointFromWindow(pos);
var controlUnderMouse = GetChildAt(menuPos);
var isMouseOverSth = controlUnderMouse != null && controlUnderMouse != _title;
if (new Rectangle(Vector2.Zero, Size).Contains(ref menuPos) && !isMouseOverSth)
var rb = GetRightButton();
if (rb != null && _minimizeButton != null && new Rectangle(rb.UpperRight * Platform.DpiScale, (_minimizeButton.BottomLeft - rb.UpperRight) * Platform.DpiScale).Contains(ref menuPos) && !isMouseOverSth)
return WindowHitCodes.Caption;
return WindowHitCodes.Client;
}
/// <summary>
/// Return the rightmost button.
/// </summary>
/// <returns>Rightmost button, null if there is no <see cref="MainMenuButton"/></returns>
private MainMenuButton GetRightButton()
{
MainMenuButton b = null;
foreach (var control in Children)
{
if (b == null && control is MainMenuButton)
b = (MainMenuButton)control;
if (control is MainMenuButton && control.Right > b.Right)
b = (MainMenuButton)control;
}
return b;
}
/// <summary>
/// Adds the button.
/// </summary>

View File

@@ -98,7 +98,7 @@ void OnFinishBake(const ProbesRenderer::Entry& e)
void OnBrushModified(CSG::Brush* brush)
{
if (Editor::Managed->CanAutoBuildCSG())
if (brush && Editor::Managed && Editor::Managed->CanAutoBuildCSG())
{
CSG::Builder::Build(brush->GetBrushScene(), ManagedEditor::ManagedEditorOptions.AutoRebuildCSGTimeoutMs);
}

View File

@@ -9,7 +9,7 @@ namespace FlaxEditor.Utilities
{
public static readonly string DiscordUrl = "https://flaxengine.com/discord";
public static readonly string DocsUrl = "https://docs.flaxengine.com/";
public static readonly string BugTrackerUrl = "https://github.com/FlaxEngine/FlaxAPI/issues";
public static readonly string BugTrackerUrl = "https://github.com/FlaxEngine/FlaxEngine/issues";
public static readonly string WebsiteUrl = "https://flaxengine.com";
public static readonly string FacebookUrl = "https://facebook.com/FlaxEngine";
public static readonly string YoutubeUrl = "https://www.youtube.com/channel/UChdER2A3n19rJWIMOZJClhw";

View File

@@ -160,8 +160,8 @@ namespace FlaxEditor.Viewport.Cameras
if (IsAnimatingMove)
return;
EditorViewport.Input input;
Viewport.GetInput(out input);
Viewport.GetInput(out var input);
Viewport.GetPrevInput(out var prevInput);
var mainViewport = Viewport as MainEditorGizmoViewport;
bool isUsingGizmo = mainViewport != null && mainViewport.TransformGizmo.ActiveAxis != TransformGizmo.Axis.None;
@@ -202,7 +202,7 @@ namespace FlaxEditor.Viewport.Cameras
}
// Rotate or orbit
if (input.IsRotating || (input.IsOrbiting && !isUsingGizmo))
if (input.IsRotating || (input.IsOrbiting && !isUsingGizmo && prevInput.IsOrbiting))
{
yaw += mouseDelta.X;
pitch += mouseDelta.Y;

View File

@@ -699,6 +699,15 @@ namespace FlaxEditor.Viewport
input = _input;
}
/// <summary>
/// Gets the input state data (from the previous update).
/// </summary>
/// <param name="input">The input.</param>
public void GetPrevInput(out Input input)
{
input = _prevInput;
}
/// <summary>
/// Creates the projection matrix.
/// </summary>

View File

@@ -377,13 +377,21 @@ namespace FlaxEditor.Windows.Assets
{
MarkAsEdited();
UpdateToolstrip();
_propertiesEditor1.BuildLayoutOnUpdate();
_propertiesEditor2.BuildLayoutOnUpdate();
if (!_isEditingInstancedParameterValue)
{
_propertiesEditor1.BuildLayoutOnUpdate();
_propertiesEditor2.BuildLayoutOnUpdate();
}
}
private void OnTimelineModified()
{
_tmpParticleSystemIsDirty = true;
if (!_isEditingInstancedParameterValue)
{
_tmpParticleSystemIsDirty = true;
}
MarkAsEdited();
}

View File

@@ -139,6 +139,7 @@ namespace FlaxEditor.Windows
c = cm.AddChildMenu("New");
c.ContextMenu.Tag = item;
c.ContextMenu.AutoSort = true;
int newItems = 0;
for (int i = 0; i < Editor.ContentDatabase.Proxy.Count; i++)
{

View File

@@ -7,15 +7,13 @@
#include "QueueVulkan.h"
#include "GPUContextVulkan.h"
#include "GPUTimerQueryVulkan.h"
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
#include "DescriptorSetVulkan.h"
#endif
void CmdBufferVulkan::AddWaitSemaphore(VkPipelineStageFlags waitFlags, SemaphoreVulkan* waitSemaphore)
{
WaitFlags.Add(waitFlags);
ASSERT(!WaitSemaphores.Contains(waitSemaphore));
WaitSemaphores.Add(waitSemaphore);
_waitFlags.Add(waitFlags);
ASSERT(!_waitSemaphores.Contains(waitSemaphore));
_waitSemaphores.Add(waitSemaphore);
}
void CmdBufferVulkan::Begin()
@@ -25,11 +23,11 @@ void CmdBufferVulkan::Begin()
VkCommandBufferBeginInfo beginInfo;
RenderToolsVulkan::ZeroStruct(beginInfo, VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO);
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
VALIDATE_VULKAN_RESULT(vkBeginCommandBuffer(CommandBufferHandle, &beginInfo));
VALIDATE_VULKAN_RESULT(vkBeginCommandBuffer(_commandBufferHandle, &beginInfo));
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
// Acquire a descriptor pool set on
if (CurrentDescriptorPoolSetContainer == nullptr)
if (_descriptorPoolSetContainer == nullptr)
{
AcquirePoolSet();
}
@@ -72,27 +70,23 @@ void CmdBufferVulkan::BeginRenderPass(RenderPassVulkan* renderPass, FramebufferV
info.clearValueCount = clearValueCount;
info.pClearValues = clearValues;
vkCmdBeginRenderPass(CommandBufferHandle, &info, VK_SUBPASS_CONTENTS_INLINE);
vkCmdBeginRenderPass(_commandBufferHandle, &info, VK_SUBPASS_CONTENTS_INLINE);
_state = State::IsInsideRenderPass;
}
void CmdBufferVulkan::EndRenderPass()
{
ASSERT(IsInsideRenderPass());
vkCmdEndRenderPass(CommandBufferHandle);
vkCmdEndRenderPass(_commandBufferHandle);
_state = State::IsInsideBegin;
}
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
void CmdBufferVulkan::AcquirePoolSet()
{
ASSERT(!CurrentDescriptorPoolSetContainer);
CurrentDescriptorPoolSetContainer = &_device->DescriptorPoolsManager->AcquirePoolSetContainer();
ASSERT(!_descriptorPoolSetContainer);
_descriptorPoolSetContainer = &_device->DescriptorPoolsManager->AcquirePoolSetContainer();
}
#endif
#if GPU_ALLOW_PROFILE_EVENTS
void CmdBufferVulkan::BeginEvent(const Char* name)
@@ -138,19 +132,17 @@ void CmdBufferVulkan::RefreshFenceStatus()
{
_state = State::ReadyForBegin;
SubmittedWaitSemaphores.Clear();
_submittedWaitSemaphores.Clear();
vkResetCommandBuffer(CommandBufferHandle, VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT);
vkResetCommandBuffer(_commandBufferHandle, VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT);
_fence->GetOwner()->ResetFence(_fence);
FenceSignaledCounter++;
_fenceSignaledCounter++;
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
if (CurrentDescriptorPoolSetContainer)
if (_descriptorPoolSetContainer)
{
_device->DescriptorPoolsManager->ReleasePoolSet(*CurrentDescriptorPoolSetContainer);
CurrentDescriptorPoolSetContainer = nullptr;
_device->DescriptorPoolsManager->ReleasePoolSet(*_descriptorPoolSetContainer);
_descriptorPoolSetContainer = nullptr;
}
#endif
}
}
else
@@ -161,20 +153,20 @@ void CmdBufferVulkan::RefreshFenceStatus()
CmdBufferVulkan::CmdBufferVulkan(GPUDeviceVulkan* device, CmdBufferPoolVulkan* pool)
: _device(device)
, CommandBufferHandle(VK_NULL_HANDLE)
, _commandBufferHandle(VK_NULL_HANDLE)
, _state(State::ReadyForBegin)
, _fence(nullptr)
, FenceSignaledCounter(0)
, SubmittedFenceCounter(0)
, CommandBufferPool(pool)
, _fenceSignaledCounter(0)
, _submittedFenceCounter(0)
, _commandBufferPool(pool)
{
VkCommandBufferAllocateInfo createCmdBufInfo;
RenderToolsVulkan::ZeroStruct(createCmdBufInfo, VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO);
createCmdBufInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
createCmdBufInfo.commandBufferCount = 1;
createCmdBufInfo.commandPool = CommandBufferPool->GetHandle();
createCmdBufInfo.commandPool = _commandBufferPool->GetHandle();
VALIDATE_VULKAN_RESULT(vkAllocateCommandBuffers(_device->Device, &createCmdBufInfo, &CommandBufferHandle));
VALIDATE_VULKAN_RESULT(vkAllocateCommandBuffers(_device->Device, &createCmdBufInfo, &_commandBufferHandle));
_fence = _device->FenceManager.AllocateFence();
}
@@ -193,7 +185,7 @@ CmdBufferVulkan::~CmdBufferVulkan()
fenceManager.ReleaseFence(_fence);
}
vkFreeCommandBuffers(_device->Device, CommandBufferPool->GetHandle(), 1, &CommandBufferHandle);
vkFreeCommandBuffers(_device->Device, _commandBufferPool->GetHandle(), 1, &_commandBufferHandle);
}
CmdBufferVulkan* CmdBufferPoolVulkan::Create()

View File

@@ -11,9 +11,7 @@
class GPUDeviceVulkan;
class CmdBufferPoolVulkan;
class QueueVulkan;
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
class DescriptorPoolSetContainerVulkan;
#endif
/// <summary>
/// Implementation of the command buffer for the Vulkan backend.
@@ -36,20 +34,20 @@ public:
private:
GPUDeviceVulkan* _device;
VkCommandBuffer CommandBufferHandle;
VkCommandBuffer _commandBufferHandle;
State _state;
Array<VkPipelineStageFlags> WaitFlags;
Array<SemaphoreVulkan*> WaitSemaphores;
Array<SemaphoreVulkan*> SubmittedWaitSemaphores;
Array<VkPipelineStageFlags> _waitFlags;
Array<SemaphoreVulkan*> _waitSemaphores;
Array<SemaphoreVulkan*> _submittedWaitSemaphores;
void MarkSemaphoresAsSubmitted()
{
WaitFlags.Clear();
_waitFlags.Clear();
// Move to pending delete list
SubmittedWaitSemaphores = WaitSemaphores;
WaitSemaphores.Clear();
_submittedWaitSemaphores = _waitSemaphores;
_waitSemaphores.Clear();
}
FenceVulkan* _fence;
@@ -58,12 +56,14 @@ private:
#endif
// Last value passed after the fence got signaled
volatile uint64 FenceSignaledCounter;
volatile uint64 _fenceSignaledCounter;
// Last value when we submitted the cmd buffer; useful to track down if something waiting for the fence has actually been submitted
volatile uint64 SubmittedFenceCounter;
volatile uint64 _submittedFenceCounter;
CmdBufferPoolVulkan* CommandBufferPool;
CmdBufferPoolVulkan* _commandBufferPool;
DescriptorPoolSetContainerVulkan* _descriptorPoolSetContainer = nullptr;
public:
@@ -75,7 +75,7 @@ public:
CmdBufferPoolVulkan* GetOwner()
{
return CommandBufferPool;
return _commandBufferPool;
}
State GetState()
@@ -115,17 +115,17 @@ public:
inline VkCommandBuffer GetHandle() const
{
return CommandBufferHandle;
return _commandBufferHandle;
}
inline volatile uint64 GetFenceSignaledCounter() const
{
return FenceSignaledCounter;
return _fenceSignaledCounter;
}
inline volatile uint64 GetSubmittedFenceCounter() const
{
return SubmittedFenceCounter;
return _submittedFenceCounter;
}
public:
@@ -138,11 +138,12 @@ public:
void BeginRenderPass(RenderPassVulkan* renderPass, FramebufferVulkan* framebuffer, uint32 clearValueCount, VkClearValue* clearValues);
void EndRenderPass();
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
DescriptorPoolSetContainerVulkan* CurrentDescriptorPoolSetContainer = nullptr;
DescriptorPoolSetContainerVulkan* GetDescriptorPoolSet() const
{
return _descriptorPoolSetContainer;
}
void AcquirePoolSet();
#endif
#if GPU_ALLOW_PROFILE_EVENTS
void BeginEvent(const Char* name);

View File

@@ -11,11 +11,7 @@
#include "GPUAdapterVulkan.h"
#include "CmdBufferVulkan.h"
#include "Engine/Threading/Threading.h"
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
#include "Engine/Engine/Engine.h"
#endif
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
void DescriptorSetLayoutInfoVulkan::CacheTypesUsageID()
{
@@ -23,7 +19,7 @@ void DescriptorSetLayoutInfoVulkan::CacheTypesUsageID()
static uint32 uniqueID = 1;
static Dictionary<uint32, uint32> typesUsageHashMap;
const uint32 typesUsageHash = Crc::MemCrc32(LayoutTypes, sizeof(LayoutTypes));
const uint32 typesUsageHash = Crc::MemCrc32(_layoutTypes, sizeof(_layoutTypes));
ScopeLock lock(locker);
uint32 id;
if (!typesUsageHashMap.TryGet(typesUsageHash, id))
@@ -34,19 +30,17 @@ void DescriptorSetLayoutInfoVulkan::CacheTypesUsageID()
_typesUsageID = id;
}
#endif
void DescriptorSetLayoutInfoVulkan::AddDescriptor(int32 descriptorSetIndex, const VkDescriptorSetLayoutBinding& descriptor)
{
// Increment type usage
LayoutTypes[descriptor.descriptorType]++;
_layoutTypes[descriptor.descriptorType]++;
if (descriptorSetIndex >= SetLayouts.Count())
if (descriptorSetIndex >= _setLayouts.Count())
{
SetLayouts.Resize(descriptorSetIndex + 1);
_setLayouts.Resize(descriptorSetIndex + 1);
}
SetLayout& descSetLayout = SetLayouts[descriptorSetIndex];
SetLayout& descSetLayout = _setLayouts[descriptorSetIndex];
descSetLayout.LayoutBindings.Add(descriptor);
// TODO: manual hash update method?
@@ -92,51 +86,51 @@ void DescriptorSetLayoutVulkan::Compile()
const VkPhysicalDeviceLimits& limits = _device->PhysicalDeviceLimits;
// Check for maxDescriptorSetSamplers
ASSERT(LayoutTypes[VK_DESCRIPTOR_TYPE_SAMPLER]
+ LayoutTypes[VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER]
ASSERT(_layoutTypes[VK_DESCRIPTOR_TYPE_SAMPLER]
+ _layoutTypes[VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER]
< limits.maxDescriptorSetSamplers);
// Check for maxDescriptorSetUniformBuffers
ASSERT(LayoutTypes[VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER]
+ LayoutTypes[VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC]
ASSERT(_layoutTypes[VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER]
+ _layoutTypes[VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC]
< limits.maxDescriptorSetUniformBuffers);
// Check for maxDescriptorSetUniformBuffersDynamic
if (!_device->Adapter->IsAMD())
{
ASSERT(LayoutTypes[VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC]
ASSERT(_layoutTypes[VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC]
< limits.maxDescriptorSetUniformBuffersDynamic);
}
// Check for maxDescriptorSetStorageBuffers
ASSERT(LayoutTypes[VK_DESCRIPTOR_TYPE_STORAGE_BUFFER]
+ LayoutTypes[VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC]
ASSERT(_layoutTypes[VK_DESCRIPTOR_TYPE_STORAGE_BUFFER]
+ _layoutTypes[VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC]
< limits.maxDescriptorSetStorageBuffers);
// Check for maxDescriptorSetStorageBuffersDynamic
if (LayoutTypes[VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC] > limits.maxDescriptorSetUniformBuffersDynamic)
if (_layoutTypes[VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC] > limits.maxDescriptorSetUniformBuffersDynamic)
{
// TODO: Downgrade to non-dynamic?
}
ASSERT(LayoutTypes[VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC]
ASSERT(_layoutTypes[VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC]
< limits.maxDescriptorSetStorageBuffersDynamic);
// Check for maxDescriptorSetSampledImages
ASSERT(LayoutTypes[VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER]
+ LayoutTypes[VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE]
+ LayoutTypes[VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER]
ASSERT(_layoutTypes[VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER]
+ _layoutTypes[VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE]
+ _layoutTypes[VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER]
< limits.maxDescriptorSetSampledImages);
// Check for maxDescriptorSetStorageImages
ASSERT(LayoutTypes[VK_DESCRIPTOR_TYPE_STORAGE_IMAGE]
+ LayoutTypes[VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER]
ASSERT(_layoutTypes[VK_DESCRIPTOR_TYPE_STORAGE_IMAGE]
+ _layoutTypes[VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER]
< limits.maxDescriptorSetStorageImages);
_handles.Resize(SetLayouts.Count());
_handles.Resize(_setLayouts.Count());
for (int32 i = 0; i < SetLayouts.Count(); i++)
for (int32 i = 0; i < _setLayouts.Count(); i++)
{
auto& layout = SetLayouts[i];
auto& layout = _setLayouts[i];
VkDescriptorSetLayoutCreateInfo layoutInfo;
RenderToolsVulkan::ZeroStruct(layoutInfo, VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO);
@@ -146,7 +140,6 @@ void DescriptorSetLayoutVulkan::Compile()
VALIDATE_VULKAN_RESULT(vkCreateDescriptorSetLayout(_device->Device, &layoutInfo, nullptr, &_handles[i]));
}
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
if (_typesUsageID == ~0)
{
CacheTypesUsageID();
@@ -155,35 +148,27 @@ void DescriptorSetLayoutVulkan::Compile()
RenderToolsVulkan::ZeroStruct(_allocateInfo, VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO);
_allocateInfo.descriptorSetCount = _handles.Count();
_allocateInfo.pSetLayouts = _handles.Get();
#endif
}
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
DescriptorPoolVulkan::DescriptorPoolVulkan(GPUDeviceVulkan* device, const DescriptorSetLayoutVulkan& layout)
#else
DescriptorPoolVulkan::DescriptorPoolVulkan(GPUDeviceVulkan* device)
#endif
: _device(device)
, _handle(VK_NULL_HANDLE)
, MaxDescriptorSets(0)
, NumAllocatedDescriptorSets(0)
, PeakAllocatedDescriptorSets(0)
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
, DescriptorSetsMax(0)
, AllocatedDescriptorSetsCount(0)
, AllocatedDescriptorSetsCountMax(0)
, Layout(layout)
#endif
{
Array<VkDescriptorPoolSize, FixedAllocation<VK_DESCRIPTOR_TYPE_RANGE_SIZE>> types;
Array<VkDescriptorPoolSize, FixedAllocation<VULKAN_DESCRIPTOR_TYPE_END + 1>> types;
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
// Max number of descriptor sets layout allocations
// The maximum amount of descriptor sets layout allocations to hold
const uint32 MaxSetsAllocations = 256;
// Descriptor sets number required to allocate the max number of descriptor sets layout.
// When we're hashing pools with types usage ID the descriptor pool can be used for different layouts so the initial layout does not make much sense.
// In the latter case we'll be probably overallocating the descriptor types but given the relatively small number of max allocations this should not have
// In the latter case we'll be probably over-allocating the descriptor types but given the relatively small number of max allocations this should not have
// a serious impact.
MaxDescriptorSets = MaxSetsAllocations * (VULKAN_HASH_POOLS_WITH_TYPES_USAGE_ID ? 1 : Layout.GetLayouts().Count());
for (uint32 typeIndex = VK_DESCRIPTOR_TYPE_BEGIN_RANGE; typeIndex < VK_DESCRIPTOR_TYPE_END_RANGE; ++typeIndex)
DescriptorSetsMax = MaxSetsAllocations * (VULKAN_HASH_POOLS_WITH_TYPES_USAGE_ID ? 1 : Layout.GetLayouts().Count());
for (uint32 typeIndex = VULKAN_DESCRIPTOR_TYPE_BEGIN; typeIndex <= VULKAN_DESCRIPTOR_TYPE_END; typeIndex++)
{
const VkDescriptorType descriptorType = (VkDescriptorType)typeIndex;
const uint32 typesUsed = Layout.GetTypesUsed(descriptorType);
@@ -195,55 +180,13 @@ DescriptorPoolVulkan::DescriptorPoolVulkan(GPUDeviceVulkan* device)
type.descriptorCount = typesUsed * MaxSetsAllocations;
}
}
#else
MaxDescriptorSets = 16384;
Platform::MemoryClear(MaxAllocatedTypes, sizeof(MaxAllocatedTypes));
Platform::MemoryClear(NumAllocatedTypes, sizeof(NumAllocatedTypes));
Platform::MemoryClear(PeakAllocatedTypes, sizeof(PeakAllocatedTypes));
VkDescriptorPoolSize type;
type.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
type.descriptorCount = 2048;
types.Add(type);
type.type = VK_DESCRIPTOR_TYPE_SAMPLER;
type.descriptorCount = 1024;
types.Add(type);
type.type = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
type.descriptorCount = 512;
types.Add(type);
type.type = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER;
type.descriptorCount = 512;
types.Add(type);
type.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
type.descriptorCount = 512;
types.Add(type);
type.type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
type.descriptorCount = 512;
types.Add(type);
type.type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
type.descriptorCount = 2048;
types.Add(type);
for (const VkDescriptorPoolSize& poolSize : types)
{
MaxAllocatedTypes[poolSize.type] = poolSize.descriptorCount;
}
#endif
VkDescriptorPoolCreateInfo createInfo;
RenderToolsVulkan::ZeroStruct(createInfo, VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO);
createInfo.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
createInfo.poolSizeCount = types.Count();
createInfo.pPoolSizes = types.Get();
createInfo.maxSets = MaxDescriptorSets;
createInfo.maxSets = DescriptorSetsMax;
VALIDATE_VULKAN_RESULT(vkCreateDescriptorPool(_device->Device, &createInfo, nullptr, &_handle));
}
@@ -258,45 +201,33 @@ DescriptorPoolVulkan::~DescriptorPoolVulkan()
void DescriptorPoolVulkan::TrackAddUsage(const DescriptorSetLayoutVulkan& layout)
{
// Check and increment our current type usage
for (uint32 typeIndex = VK_DESCRIPTOR_TYPE_BEGIN_RANGE; typeIndex < VK_DESCRIPTOR_TYPE_END_RANGE; typeIndex++)
for (uint32 typeIndex = VULKAN_DESCRIPTOR_TYPE_BEGIN; typeIndex <= VULKAN_DESCRIPTOR_TYPE_END; typeIndex++)
{
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
ASSERT(Layout.GetTypesUsed((VkDescriptorType)typeIndex) == layout.GetTypesUsed((VkDescriptorType)typeIndex));
#else
NumAllocatedTypes[typeIndex] += (int32)layout.GetTypesUsed((VkDescriptorType)typeIndex);
PeakAllocatedTypes[typeIndex] = Math::Max(PeakAllocatedTypes[typeIndex], NumAllocatedTypes[typeIndex]);
#endif
}
NumAllocatedDescriptorSets += layout.GetLayouts().Count();
PeakAllocatedDescriptorSets = Math::Max(NumAllocatedDescriptorSets, PeakAllocatedDescriptorSets);
AllocatedDescriptorSetsCount += layout.GetLayouts().Count();
AllocatedDescriptorSetsCountMax = Math::Max(AllocatedDescriptorSetsCount, AllocatedDescriptorSetsCountMax);
}
void DescriptorPoolVulkan::TrackRemoveUsage(const DescriptorSetLayoutVulkan& layout)
{
// Check and increment our current type usage
for (uint32 typeIndex = VK_DESCRIPTOR_TYPE_BEGIN_RANGE; typeIndex < VK_DESCRIPTOR_TYPE_END_RANGE; typeIndex++)
for (uint32 typeIndex = VULKAN_DESCRIPTOR_TYPE_BEGIN; typeIndex <= VULKAN_DESCRIPTOR_TYPE_END; typeIndex++)
{
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
ASSERT(Layout.GetTypesUsed((VkDescriptorType)typeIndex) == layout.GetTypesUsed((VkDescriptorType)typeIndex));
#else
NumAllocatedTypes[typeIndex] -= (int32)layout.GetTypesUsed((VkDescriptorType)typeIndex);
ASSERT(NumAllocatedTypes[typeIndex] >= 0);
#endif
}
NumAllocatedDescriptorSets -= layout.GetLayouts().Count();
AllocatedDescriptorSetsCount -= layout.GetLayouts().Count();
}
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
void DescriptorPoolVulkan::Reset()
{
if (_handle != VK_NULL_HANDLE)
{
VALIDATE_VULKAN_RESULT(vkResetDescriptorPool(_device->Device, _handle, 0));
}
NumAllocatedDescriptorSets = 0;
AllocatedDescriptorSetsCount = 0;
}
bool DescriptorPoolVulkan::AllocateDescriptorSets(const VkDescriptorSetAllocateInfo& descriptorSetAllocateInfo, VkDescriptorSet* result)
@@ -370,10 +301,6 @@ void TypedDescriptorPoolSetVulkan::Reset()
_poolListCurrent = _poolListHead;
}
#endif
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
DescriptorPoolSetContainerVulkan::DescriptorPoolSetContainerVulkan(GPUDeviceVulkan* device)
: _device(device)
, _lastFrameUsed(Engine::FrameCount)
@@ -464,8 +391,6 @@ void DescriptorPoolsManagerVulkan::GC()
}
}
#endif
PipelineLayoutVulkan::PipelineLayoutVulkan(GPUDeviceVulkan* device, const DescriptorSetLayoutInfoVulkan& layout)
: _device(device)
, _handle(VK_NULL_HANDLE)
@@ -491,84 +416,10 @@ PipelineLayoutVulkan::~PipelineLayoutVulkan()
}
}
#if !VULKAN_USE_DESCRIPTOR_POOL_MANAGER
DescriptorSetsVulkan::DescriptorSetsVulkan(GPUDeviceVulkan* device, const DescriptorSetLayoutVulkan& layout, GPUContextVulkan* context)
: _device(device)
, _pool(nullptr)
, _layout(&layout)
{
const auto& layoutHandles = layout.GetHandles();
if (layoutHandles.HasItems())
{
VkDescriptorSetAllocateInfo allocateInfo;
RenderToolsVulkan::ZeroStruct(allocateInfo, VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO);
allocateInfo.descriptorSetCount = layoutHandles.Count();
allocateInfo.pSetLayouts = layoutHandles.Get();
_sets.AddZeroed(layoutHandles.Count());
_pool = context->AllocateDescriptorSets(allocateInfo, layout, _sets.Get());
_pool->TrackAddUsage(layout);
}
}
DescriptorSetsVulkan::~DescriptorSetsVulkan()
{
}
DescriptorSetRingBufferVulkan::DescriptorSetRingBufferVulkan(GPUDeviceVulkan* device)
: _device(device)
, _currDescriptorSets(nullptr)
{
}
DescriptorSetsVulkan* DescriptorSetRingBufferVulkan::RequestDescriptorSets(GPUContextVulkan* context, CmdBufferVulkan* cmdBuffer, const PipelineLayoutVulkan* layout)
{
DescriptorSetsEntry* foundEntry = nullptr;
for (DescriptorSetsEntry* descriptorSetsEntry : DescriptorSetsEntries)
{
if (descriptorSetsEntry->CmdBuffer == cmdBuffer)
{
foundEntry = descriptorSetsEntry;
}
}
if (!foundEntry)
{
if (!layout->HasDescriptors())
{
return nullptr;
}
foundEntry = New<DescriptorSetsEntry>(cmdBuffer);
DescriptorSetsEntries.Add(foundEntry);
}
const uint64 cmdBufferFenceSignaledCounter = cmdBuffer->GetFenceSignaledCounter();
for (int32 i = 0; i < foundEntry->Pairs.Count(); i++)
{
DescriptorSetsPair& entry = foundEntry->Pairs[i];
if (entry.FenceCounter < cmdBufferFenceSignaledCounter)
{
entry.FenceCounter = cmdBufferFenceSignaledCounter;
return entry.DescriptorSets;
}
}
DescriptorSetsPair newEntry;
newEntry.DescriptorSets = New<DescriptorSetsVulkan>(_device, layout->GetDescriptorSetLayout(), context);
newEntry.FenceCounter = cmdBufferFenceSignaledCounter;
foundEntry->Pairs.Add(newEntry);
return newEntry.DescriptorSets;
}
#endif
uint32 DescriptorSetWriterVulkan::SetupDescriptorWrites(const SpirvShaderDescriptorInfo& info, VkWriteDescriptorSet* writeDescriptors, VkDescriptorImageInfo* imageInfo, VkDescriptorBufferInfo* bufferInfo, uint8* bindingToDynamicOffsetMap)
{
WriteDescriptors = writeDescriptors;
NumWrites = info.DescriptorTypesCount;
WritesCount = info.DescriptorTypesCount;
ASSERT(info.DescriptorTypesCount <= 64 && TEXT("Out of bits for Dirty Mask! More than 64 resources in one descriptor set!"));
BindingToDynamicOffsetMap = bindingToDynamicOffsetMap;

View File

@@ -9,12 +9,13 @@
#include "IncludeVulkanHeaders.h"
#include "Types.h"
#include "Config.h"
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
#include "Engine/Platform/CriticalSection.h"
#endif
#if GRAPHICS_API_VULKAN
#define VULKAN_DESCRIPTOR_TYPE_BEGIN VK_DESCRIPTOR_TYPE_SAMPLER
#define VULKAN_DESCRIPTOR_TYPE_END VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT
class GPUDeviceVulkan;
class CmdBufferVulkan;
class GPUContextVulkan;
@@ -39,7 +40,7 @@ namespace DescriptorSet
Domain = 4,
// Graphics pipeline stages count
NumGfxStages = 5,
GraphicsStagesCount = 5,
// Compute pipeline slot
Compute = 0,
@@ -110,48 +111,42 @@ public:
protected:
uint32 LayoutTypes[VK_DESCRIPTOR_TYPE_RANGE_SIZE];
Array<SetLayout> SetLayouts;
uint32 _layoutTypes[VULKAN_DESCRIPTOR_TYPE_END];
Array<SetLayout> _setLayouts;
uint32 _hash = 0;
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
uint32 _typesUsageID = ~0;
void CacheTypesUsageID();
#endif
void AddDescriptor(int32 descriptorSetIndex, const VkDescriptorSetLayoutBinding& descriptor);
public:
DescriptorSetLayoutInfoVulkan()
{
Platform::MemoryClear(LayoutTypes, sizeof(LayoutTypes));
Platform::MemoryClear(_layoutTypes, sizeof(_layoutTypes));
}
public:
inline uint32 GetTypesUsed(VkDescriptorType type) const
{
return LayoutTypes[type];
return _layoutTypes[type];
}
const Array<SetLayout>& GetLayouts() const
{
return SetLayouts;
return _setLayouts;
}
inline const uint32* GetLayoutTypes() const
{
return LayoutTypes;
return _layoutTypes;
}
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
inline uint32 GetTypesUsageID() const
{
return _typesUsageID;
}
#endif
public:
@@ -159,40 +154,27 @@ public:
void CopyFrom(const DescriptorSetLayoutInfoVulkan& info)
{
Platform::MemoryCopy(LayoutTypes, info.LayoutTypes, sizeof(LayoutTypes));
Platform::MemoryCopy(_layoutTypes, info._layoutTypes, sizeof(_layoutTypes));
_hash = info._hash;
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
_typesUsageID = info._typesUsageID;
#endif
SetLayouts = info.SetLayouts;
_setLayouts = info._setLayouts;
}
inline bool operator ==(const DescriptorSetLayoutInfoVulkan& other) const
{
if (other.SetLayouts.Count() != SetLayouts.Count())
{
if (other._setLayouts.Count() != _setLayouts.Count())
return false;
}
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
if (other._typesUsageID != _typesUsageID)
{
return false;
}
#endif
for (int32 index = 0; index < other.SetLayouts.Count(); index++)
for (int32 index = 0; index < other._setLayouts.Count(); index++)
{
const int32 numBindings = SetLayouts[index].LayoutBindings.Count();
if (other.SetLayouts[index].LayoutBindings.Count() != numBindings)
{
const int32 bindingsCount = _setLayouts[index].LayoutBindings.Count();
if (other._setLayouts[index].LayoutBindings.Count() != bindingsCount)
return false;
}
if (numBindings != 0 && Platform::MemoryCompare(other.SetLayouts[index].LayoutBindings.Get(), SetLayouts[index].LayoutBindings.Get(), numBindings * sizeof(VkDescriptorSetLayoutBinding)))
{
if (bindingsCount != 0 && Platform::MemoryCompare(other._setLayouts[index].LayoutBindings.Get(), _setLayouts[index].LayoutBindings.Get(), bindingsCount * sizeof(VkDescriptorSetLayoutBinding)))
return false;
}
}
return true;
@@ -214,9 +196,7 @@ private:
GPUDeviceVulkan* _device;
DescriptorSetLayoutHandlesArray _handles;
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
VkDescriptorSetAllocateInfo _allocateInfo;
#endif
public:
@@ -225,24 +205,22 @@ public:
public:
void Compile();
inline const DescriptorSetLayoutHandlesArray& GetHandles() const
{
return _handles;
}
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
inline const VkDescriptorSetAllocateInfo& GetAllocateInfo() const
{
return _allocateInfo;
}
#endif
friend inline uint32 GetHash(const DescriptorSetLayoutVulkan& key)
{
return key._hash;
}
void Compile();
};
class DescriptorPoolVulkan
@@ -252,25 +230,15 @@ private:
GPUDeviceVulkan* _device;
VkDescriptorPool _handle;
uint32 MaxDescriptorSets;
uint32 NumAllocatedDescriptorSets;
uint32 PeakAllocatedDescriptorSets;
uint32 DescriptorSetsMax;
uint32 AllocatedDescriptorSetsCount;
uint32 AllocatedDescriptorSetsCountMax;
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
const DescriptorSetLayoutVulkan& Layout;
#else
int32 MaxAllocatedTypes[VK_DESCRIPTOR_TYPE_RANGE_SIZE];
int32 NumAllocatedTypes[VK_DESCRIPTOR_TYPE_RANGE_SIZE];
int32 PeakAllocatedTypes[VK_DESCRIPTOR_TYPE_RANGE_SIZE];
#endif
public:
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
DescriptorPoolVulkan(GPUDeviceVulkan* device, const DescriptorSetLayoutVulkan& layout);
#else
DescriptorPoolVulkan(GPUDeviceVulkan* device);
#endif
~DescriptorPoolVulkan();
@@ -283,43 +251,28 @@ public:
inline bool IsEmpty() const
{
return NumAllocatedDescriptorSets == 0;
return AllocatedDescriptorSetsCount == 0;
}
inline bool CanAllocate(const DescriptorSetLayoutVulkan& layout) const
{
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
return MaxDescriptorSets > NumAllocatedDescriptorSets + layout.GetLayouts().Count();
#else
for (uint32 typeIndex = VK_DESCRIPTOR_TYPE_BEGIN_RANGE; typeIndex < VK_DESCRIPTOR_TYPE_END_RANGE; typeIndex++)
{
if (NumAllocatedTypes[typeIndex] + (int32)layout.GetTypesUsed((VkDescriptorType)typeIndex) > MaxAllocatedTypes[typeIndex])
{
return false;
}
}
return true;
#endif
return DescriptorSetsMax > AllocatedDescriptorSetsCount + layout.GetLayouts().Count();
}
inline uint32 GetAllocatedDescriptorSetsCount() const
{
return AllocatedDescriptorSetsCount;
}
void TrackAddUsage(const DescriptorSetLayoutVulkan& layout);
void TrackRemoveUsage(const DescriptorSetLayoutVulkan& layout);
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
void Reset();
bool AllocateDescriptorSets(const VkDescriptorSetAllocateInfo& descriptorSetAllocateInfo, VkDescriptorSet* result);
inline uint32 GetNumAllocatedDescriptorSets() const
{
return NumAllocatedDescriptorSets;
}
#endif
};
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
class DescriptorPoolSetContainerVulkan;
class TypedDescriptorPoolSetVulkan
@@ -425,8 +378,6 @@ public:
void GC();
};
#endif
class PipelineLayoutVulkan
{
private:
@@ -476,115 +427,6 @@ struct DescriptorSetWriteContainerVulkan
}
};
#if !VULKAN_USE_DESCRIPTOR_POOL_MANAGER
class DescriptorSetsVulkan
{
public:
typedef Array<VkDescriptorSet, FixedAllocation<DescriptorSet::NumGfxStages>> DescriptorSetArray;
private:
GPUDeviceVulkan* _device;
DescriptorPoolVulkan* _pool;
const DescriptorSetLayoutVulkan* _layout;
DescriptorSetArray _sets;
public:
DescriptorSetsVulkan(GPUDeviceVulkan* device, const DescriptorSetLayoutVulkan& layout, GPUContextVulkan* context);
~DescriptorSetsVulkan();
public:
inline const DescriptorSetArray& GetHandles() const
{
return _sets;
}
inline void Bind(VkCommandBuffer cmdBuffer, VkPipelineLayout pipelineLayout, VkPipelineBindPoint bindPoint, const Array<uint32>& dynamicOffsets) const
{
vkCmdBindDescriptorSets(cmdBuffer, bindPoint, pipelineLayout, 0, _sets.Count(), _sets.Get(), dynamicOffsets.Count(), dynamicOffsets.Get());
}
};
class DescriptorSetRingBufferVulkan
{
private:
GPUDeviceVulkan* _device;
DescriptorSetsVulkan* _currDescriptorSets;
struct DescriptorSetsPair
{
uint64 FenceCounter;
DescriptorSetsVulkan* DescriptorSets;
DescriptorSetsPair()
: FenceCounter(0)
, DescriptorSets(nullptr)
{
}
};
struct DescriptorSetsEntry
{
CmdBufferVulkan* CmdBuffer;
Array<DescriptorSetsPair> Pairs;
DescriptorSetsEntry(CmdBufferVulkan* cmdBuffer)
: CmdBuffer(cmdBuffer)
{
}
~DescriptorSetsEntry()
{
for (auto& pair : Pairs)
{
Delete(pair.DescriptorSets);
}
}
};
Array<DescriptorSetsEntry*> DescriptorSetsEntries;
public:
DescriptorSetRingBufferVulkan(GPUDeviceVulkan* device);
virtual ~DescriptorSetRingBufferVulkan()
{
}
public:
void Reset()
{
_currDescriptorSets = nullptr;
}
void Release()
{
DescriptorSetsEntries.ClearDelete();
}
void Set(DescriptorSetsVulkan* newDescriptorSets)
{
_currDescriptorSets = newDescriptorSets;
}
inline void Bind(VkCommandBuffer cmdBuffer, VkPipelineLayout pipelineLayout, VkPipelineBindPoint bindPoint, const Array<uint32>& dynamicOffsets)
{
ASSERT(_currDescriptorSets);
_currDescriptorSets->Bind(cmdBuffer, pipelineLayout, bindPoint, dynamicOffsets);
}
DescriptorSetsVulkan* RequestDescriptorSets(GPUContextVulkan* context, CmdBufferVulkan* cmdBuffer, const PipelineLayoutVulkan* layout);
};
#endif
class DescriptorSetWriterVulkan
{
public:
@@ -592,7 +434,7 @@ public:
VkWriteDescriptorSet* WriteDescriptors;
uint8* BindingToDynamicOffsetMap;
uint32* DynamicOffsets;
uint32 NumWrites;
uint32 WritesCount;
public:
@@ -600,7 +442,7 @@ public:
: WriteDescriptors(nullptr)
, BindingToDynamicOffsetMap(nullptr)
, DynamicOffsets(nullptr)
, NumWrites(0)
, WritesCount(0)
{
}
@@ -610,7 +452,7 @@ public:
bool WriteUniformBuffer(uint32 descriptorIndex, VkBuffer buffer, VkDeviceSize offset, VkDeviceSize range) const
{
ASSERT(descriptorIndex < NumWrites);
ASSERT(descriptorIndex < WritesCount);
ASSERT(WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
VkDescriptorBufferInfo* bufferInfo = const_cast<VkDescriptorBufferInfo*>(WriteDescriptors[descriptorIndex].pBufferInfo);
ASSERT(bufferInfo);
@@ -622,7 +464,7 @@ public:
bool WriteDynamicUniformBuffer(uint32 descriptorIndex, VkBuffer buffer, VkDeviceSize offset, VkDeviceSize range, uint32 dynamicOffset) const
{
ASSERT(descriptorIndex < NumWrites);
ASSERT(descriptorIndex < WritesCount);
ASSERT(WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC);
VkDescriptorBufferInfo* bufferInfo = const_cast<VkDescriptorBufferInfo*>(WriteDescriptors[descriptorIndex].pBufferInfo);
ASSERT(bufferInfo);
@@ -636,7 +478,7 @@ public:
bool WriteSampler(uint32 descriptorIndex, VkSampler sampler) const
{
ASSERT(descriptorIndex < NumWrites);
ASSERT(descriptorIndex < WritesCount);
ASSERT(WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER || WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
VkDescriptorImageInfo* imageInfo = const_cast<VkDescriptorImageInfo*>(WriteDescriptors[descriptorIndex].pImageInfo);
ASSERT(imageInfo);
@@ -646,7 +488,7 @@ public:
bool WriteImage(uint32 descriptorIndex, VkImageView imageView, VkImageLayout layout) const
{
ASSERT(descriptorIndex < NumWrites);
ASSERT(descriptorIndex < WritesCount);
ASSERT(WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE || WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
VkDescriptorImageInfo* imageInfo = const_cast<VkDescriptorImageInfo*>(WriteDescriptors[descriptorIndex].pImageInfo);
ASSERT(imageInfo);
@@ -657,7 +499,7 @@ public:
bool WriteStorageImage(uint32 descriptorIndex, VkImageView imageView, VkImageLayout layout) const
{
ASSERT(descriptorIndex < NumWrites);
ASSERT(descriptorIndex < WritesCount);
ASSERT(WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE);
VkDescriptorImageInfo* imageInfo = const_cast<VkDescriptorImageInfo*>(WriteDescriptors[descriptorIndex].pImageInfo);
ASSERT(imageInfo);
@@ -668,7 +510,7 @@ public:
bool WriteStorageTexelBuffer(uint32 descriptorIndex, const VkBufferView* bufferView) const
{
ASSERT(descriptorIndex < NumWrites);
ASSERT(descriptorIndex < WritesCount);
ASSERT(WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER);
WriteDescriptors[descriptorIndex].pTexelBufferView = bufferView;
return true;
@@ -676,7 +518,7 @@ public:
bool WriteStorageBuffer(uint32 descriptorIndex, VkBuffer buffer, VkDeviceSize offset, VkDeviceSize range) const
{
ASSERT(descriptorIndex < NumWrites);
ASSERT(descriptorIndex < WritesCount);
ASSERT(WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER || WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC);
VkDescriptorBufferInfo* bufferInfo = const_cast<VkDescriptorBufferInfo*>(WriteDescriptors[descriptorIndex].pBufferInfo);
ASSERT(bufferInfo);
@@ -688,14 +530,14 @@ public:
bool WriteUniformTexelBuffer(uint32 descriptorIndex, const VkBufferView* view) const
{
ASSERT(descriptorIndex < NumWrites);
ASSERT(descriptorIndex < WritesCount);
ASSERT(WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER);
return DescriptorSet::CopyAndReturnNotEqual(WriteDescriptors[descriptorIndex].pTexelBufferView, view);
}
void SetDescriptorSet(VkDescriptorSet descriptorSet) const
{
for (uint32 i = 0; i < NumWrites; i++)
for (uint32 i = 0; i < WritesCount; i++)
{
WriteDescriptors[i].dstSet = descriptorSet;
}

View File

@@ -102,15 +102,11 @@ GPUContextVulkan::GPUContextVulkan(GPUDeviceVulkan* device, QueueVulkan* queue)
GPUContextVulkan::~GPUContextVulkan()
{
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
for (int32 i = 0; i < _descriptorPools.Count(); i++)
{
_descriptorPools[i].ClearDelete();
}
_descriptorPools.Clear();
#else
_descriptorPools.ClearDelete();
#endif
Delete(_cmdBufferManager);
}
@@ -297,7 +293,6 @@ DescriptorPoolVulkan* GPUContextVulkan::AllocateDescriptorSets(const VkDescripto
{
VkResult result = VK_ERROR_OUT_OF_DEVICE_MEMORY;
VkDescriptorSetAllocateInfo allocateInfo = descriptorSetAllocateInfo;
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
DescriptorPoolVulkan* pool = nullptr;
const uint32 hash = VULKAN_HASH_POOLS_WITH_TYPES_USAGE_ID ? layout.GetTypesUsageID() : GetHash(layout);
@@ -317,15 +312,6 @@ DescriptorPoolVulkan* GPUContextVulkan::AllocateDescriptorSets(const VkDescripto
{
typedDescriptorPools = &_descriptorPools.Add(hash, DescriptorPoolArray())->Value;
}
#else
DescriptorPoolVulkan* pool = _descriptorPools.HasItems() ? _descriptorPools.Last() : nullptr;
if (pool && pool->CanAllocate(layout))
{
allocateInfo.descriptorPool = pool->GetHandle();
result = vkAllocateDescriptorSets(_device->Device, &allocateInfo, outSets);
}
#endif
if (result < VK_SUCCESS)
{
@@ -336,13 +322,8 @@ DescriptorPoolVulkan* GPUContextVulkan::AllocateDescriptorSets(const VkDescripto
else
{
// Spec says any negative value could be due to fragmentation, so create a new Pool. If it fails here then we really are out of memory!
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
pool = New<DescriptorPoolVulkan>(_device, layout);
typedDescriptorPools->Add(pool);
#else
pool = New<DescriptorPoolVulkan>(_device);
_descriptorPools.Add(pool);
#endif
allocateInfo.descriptorPool = pool->GetHandle();
VALIDATE_VULKAN_RESULT(vkAllocateDescriptorSets(_device->Device, &allocateInfo, outSets));
}
@@ -540,23 +521,13 @@ void GPUContextVulkan::UpdateDescriptorSets(GPUPipelineStateVulkan* pipelineStat
ASSERT(pipelineLayout);
bool needsWrite = false;
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
// No current descriptor pools set - acquire one and reset
bool newDescriptorPool = pipelineState->AcquirePoolSet(cmdBuffer);
const bool newDescriptorPool = pipelineState->AcquirePoolSet(cmdBuffer);
needsWrite |= newDescriptorPool;
#else
const auto newDescriptorSets = pipelineState->DSRingBuffer.RequestDescriptorSets(this, cmdBuffer, pipelineLayout);
pipelineState->DSRingBuffer.Set(newDescriptorSets);
if (!newDescriptorSets)
{
return;
}
const auto& descriptorSetHandles = newDescriptorSets->GetHandles();
#endif
// Update descriptors for every used shader stage
uint32 remainingHasDescriptorsPerStageMask = pipelineState->HasDescriptorsPerStageMask;
for (int32 stage = 0; stage < DescriptorSet::NumGfxStages && remainingHasDescriptorsPerStageMask; stage++)
for (int32 stage = 0; stage < DescriptorSet::GraphicsStagesCount && remainingHasDescriptorsPerStageMask; stage++)
{
// Only process stages that exist in this pipeline and use descriptors
if (remainingHasDescriptorsPerStageMask & 1)
@@ -568,27 +539,19 @@ void GPUContextVulkan::UpdateDescriptorSets(GPUPipelineStateVulkan* pipelineStat
}
// Allocate sets based on what changed
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
//if (needsWrite) // TODO: write on change only?
#endif
{
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
if (!pipelineState->AllocateDescriptorSets())
{
return;
}
#endif
uint32 remainingStagesMask = pipelineState->HasDescriptorsPerStageMask;
uint32 stage = 0;
while (remainingStagesMask)
{
if (remainingStagesMask & 1)
{
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
const VkDescriptorSet descriptorSet = pipelineState->DescriptorSetHandles[stage];
#else
const VkDescriptorSet descriptorSet = descriptorSetHandles[stage];
#endif
pipelineState->DSWriter[stage].SetDescriptorSet(descriptorSet);
}
@@ -608,39 +571,21 @@ void GPUContextVulkan::UpdateDescriptorSets(ComputePipelineStateVulkan* pipeline
bool needsWrite = false;
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
// No current descriptor pools set - acquire one and reset
bool newDescriptorPool = pipelineState->AcquirePoolSet(cmdBuffer);
const bool newDescriptorPool = pipelineState->AcquirePoolSet(cmdBuffer);
needsWrite |= newDescriptorPool;
#else
const auto newDescriptorSets = pipelineState->DSRingBuffer.RequestDescriptorSets(this, cmdBuffer, pipelineLayout);
pipelineState->DSRingBuffer.Set(newDescriptorSets);
if (!newDescriptorSets)
{
return;
}
const auto& descriptorSetHandles = newDescriptorSets->GetHandles();
#endif
// Update descriptors
UpdateDescriptorSets(*pipelineState->DescriptorInfo, pipelineState->DSWriter, needsWrite);
// Allocate sets based on what changed
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
//if (needsWrite) // TODO: write on change only?
#endif
//if (needsWrite) // TODO: write on change only?f
{
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
if (!pipelineState->AllocateDescriptorSets())
{
return;
}
#endif
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
const VkDescriptorSet descriptorSet = pipelineState->DescriptorSetHandles[DescriptorSet::Compute];
#else
const VkDescriptorSet descriptorSet = descriptorSetHandles[DescriptorSet::Compute];
#endif
pipelineState->DSWriter.SetDescriptorSet(descriptorSet);
vkUpdateDescriptorSets(_device->Device, pipelineState->DSWriteContainer.DescriptorWrites.Count(), pipelineState->DSWriteContainer.DescriptorWrites.Get(), 0, nullptr);
@@ -675,8 +620,6 @@ void GPUContextVulkan::OnDrawCall()
if (_rtDirtyFlag && cmdBuffer->IsInsideRenderPass())
EndRenderPass();
_currentState->Reset();
if (pipelineState->HasDescriptorsPerStageMask)
{
UpdateDescriptorSets(pipelineState);

View File

@@ -114,12 +114,8 @@ private:
DescriptorOwnerResourceVulkan* _uaHandles[GPU_MAX_UA_BINDED];
DescriptorOwnerResourceVulkan** _handles[(int32)SpirvShaderResourceBindingType::MAX];
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
typedef Array<DescriptorPoolVulkan*> DescriptorPoolArray;
Dictionary<uint32, DescriptorPoolArray> _descriptorPools;
#else
Array<DescriptorPoolVulkan*> _descriptorPools;
#endif
public:

View File

@@ -1096,9 +1096,7 @@ GPUDeviceVulkan::GPUDeviceVulkan(ShaderProfile shaderProfile, GPUAdapterVulkan*
, ValidationCache(VK_NULL_HANDLE)
#endif
, UniformBufferUploader(nullptr)
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
, DescriptorPoolsManager(nullptr)
#endif
{
}
@@ -1871,9 +1869,7 @@ bool GPUDeviceVulkan::Init()
// Prepare stuff
FenceManager.Init(this);
UniformBufferUploader = New<UniformBufferUploaderVulkan>(this);
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
DescriptorPoolsManager = New<DescriptorPoolsManagerVulkan>(this);
#endif
MainContext = New<GPUContextVulkan>(this, GraphicsQueue);
// TODO: create and load PipelineCache
#if VULKAN_SUPPORTS_VALIDATION_CACHE
@@ -1895,9 +1891,7 @@ void GPUDeviceVulkan::DrawBegin()
// Flush resources
DeferredDeletionQueue.ReleaseResources();
StagingManager.ProcessPendingFree();
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
DescriptorPoolsManager->GC();
#endif
}
void GPUDeviceVulkan::Dispose()
@@ -1925,9 +1919,7 @@ void GPUDeviceVulkan::Dispose()
StagingManager.Dispose();
TimestampQueryPools.ClearDelete();
SAFE_DELETE_GPU_RESOURCE(UniformBufferUploader);
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
Delete(DescriptorPoolsManager);
#endif
SAFE_DELETE(MainContext);
SAFE_DELETE(GraphicsQueue);
SAFE_DELETE(ComputeQueue);

View File

@@ -26,9 +26,7 @@ class RenderPassVulkan;
class FenceManagerVulkan;
class GPUDeviceVulkan;
class UniformBufferUploaderVulkan;
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
class DescriptorPoolsManagerVulkan;
#endif
class SemaphoreVulkan
{
@@ -637,9 +635,10 @@ public:
/// </summary>
UniformBufferUploaderVulkan* UniformBufferUploader;
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
/// <summary>
/// The descriptor pools manager.
/// </summary>
DescriptorPoolsManagerVulkan* DescriptorPoolsManager;
#endif
/// <summary>
/// The physical device limits.

View File

@@ -47,11 +47,8 @@ ComputePipelineStateVulkan* GPUShaderProgramCSVulkan::GetOrCreateState()
_pipelineState = New<ComputePipelineStateVulkan>(_device, pipeline, layout);
_pipelineState->DescriptorInfo = &DescriptorInfo;
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
_pipelineState->DescriptorSetsLayout = &layout->GetDescriptorSetLayout();
_pipelineState->DescriptorSetHandles.AddZeroed(_pipelineState->DescriptorSetsLayout->GetHandles().Count());
#endif
uint32 totalNumDynamicOffsets = 0;
@@ -89,13 +86,9 @@ ComputePipelineStateVulkan::ComputePipelineStateVulkan(GPUDeviceVulkan* device,
ComputePipelineStateVulkan::~ComputePipelineStateVulkan()
{
DSWriteContainer.Release();
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
CurrentTypedDescriptorPoolSet = nullptr;
DescriptorSetsLayout = nullptr;
DescriptorSetHandles.Resize(0);
#else
DSRingBuffer.Release();
#endif
DynamicOffsets.Resize(0);
_device->DeferredDeletionQueue.EnqueueResource(DeferredDeletionQueueVulkan::Type::Pipeline, _handle);
_layout = nullptr;
@@ -105,9 +98,6 @@ GPUPipelineStateVulkan::GPUPipelineStateVulkan(GPUDeviceVulkan* device)
: GPUResourceVulkan<GPUPipelineState>(device, StringView::Empty)
, _pipelines(16)
, _layout(nullptr)
#if !VULKAN_USE_DESCRIPTOR_POOL_MANAGER
, DSRingBuffer(device)
#endif
{
}
@@ -132,10 +122,8 @@ PipelineLayoutVulkan* GPUPipelineStateVulkan::GetLayout()
_layout = _device->GetOrCreateLayout(descriptorSetLayoutInfo);
ASSERT(_layout);
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
DescriptorSetsLayout = &_layout->GetDescriptorSetLayout();
DescriptorSetHandles.AddZeroed(DescriptorSetsLayout->GetHandles().Count());
#endif
return _layout;
}
@@ -192,13 +180,9 @@ VkPipeline GPUPipelineStateVulkan::GetState(RenderPassVulkan* renderPass)
void GPUPipelineStateVulkan::OnReleaseGPU()
{
DSWriteContainer.Release();
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
CurrentTypedDescriptorPoolSet = nullptr;
DescriptorSetsLayout = nullptr;
DescriptorSetHandles.Resize(0);
#else
DSRingBuffer.Release();
#endif
DynamicOffsets.Resize(0);
for (auto i = _pipelines.Begin(); i.IsNotEnd(); ++i)
{
@@ -290,6 +274,7 @@ bool GPUPipelineStateVulkan::Init(const Description& desc)
_dynamicStates[_descDynamic.dynamicStateCount++] = VK_DYNAMIC_STATE_VIEWPORT;
_dynamicStates[_descDynamic.dynamicStateCount++] = VK_DYNAMIC_STATE_SCISSOR;
_dynamicStates[_descDynamic.dynamicStateCount++] = VK_DYNAMIC_STATE_STENCIL_REFERENCE;
static_assert(ARRAY_COUNT(_dynamicStates) <= 3, "Invalid dynamic states array.");
_desc.pDynamicState = &_descDynamic;
// Multisample
@@ -350,7 +335,7 @@ bool GPUPipelineStateVulkan::Init(const Description& desc)
_desc.pColorBlendState = &_descColorBlend;
ASSERT(DSWriteContainer.DescriptorWrites.IsEmpty());
for (int32 stage = 0; stage < DescriptorSet::NumGfxStages; stage++)
for (int32 stage = 0; stage < DescriptorSet::GraphicsStagesCount; stage++)
{
const auto descriptor = DescriptorInfoPerStage[stage];
if (descriptor == nullptr || descriptor->DescriptorTypesCount == 0)
@@ -369,9 +354,9 @@ bool GPUPipelineStateVulkan::Init(const Description& desc)
VkDescriptorImageInfo* currentImageInfo = DSWriteContainer.DescriptorImageInfo.Get();
VkDescriptorBufferInfo* currentBufferInfo = DSWriteContainer.DescriptorBufferInfo.Get();
uint8* currentBindingToDynamicOffsetMap = DSWriteContainer.BindingToDynamicOffsetMap.Get();
uint32 dynamicOffsetsStart[DescriptorSet::NumGfxStages];
uint32 dynamicOffsetsStart[DescriptorSet::GraphicsStagesCount];
uint32 totalNumDynamicOffsets = 0;
for (int32 stage = 0; stage < DescriptorSet::NumGfxStages; stage++)
for (int32 stage = 0; stage < DescriptorSet::GraphicsStagesCount; stage++)
{
dynamicOffsetsStart[stage] = totalNumDynamicOffsets;
@@ -389,7 +374,7 @@ bool GPUPipelineStateVulkan::Init(const Description& desc)
}
DynamicOffsets.AddZeroed(totalNumDynamicOffsets);
for (int32 stage = 0; stage < DescriptorSet::NumGfxStages; stage++)
for (int32 stage = 0; stage < DescriptorSet::GraphicsStagesCount; stage++)
{
DSWriter[stage].DynamicOffsets = dynamicOffsetsStart[stage] + DynamicOffsets.Get();
}

View File

@@ -44,8 +44,6 @@ public:
DescriptorSetWriteContainerVulkan DSWriteContainer;
DescriptorSetWriterVulkan DSWriter;
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
const DescriptorSetLayoutVulkan* DescriptorSetsLayout = nullptr;
TypedDescriptorPoolSetVulkan* CurrentTypedDescriptorPoolSet = nullptr;
Array<VkDescriptorSet> DescriptorSetHandles;
@@ -53,7 +51,7 @@ public:
inline bool AcquirePoolSet(CmdBufferVulkan* cmdBuffer)
{
// Pipeline state has no current descriptor pools set or set owner is not current - acquire a new pool set
DescriptorPoolSetContainerVulkan* cmdBufferPoolSet = cmdBuffer->CurrentDescriptorPoolSetContainer;
DescriptorPoolSetContainerVulkan* cmdBufferPoolSet = cmdBuffer->GetDescriptorPoolSet();
if (CurrentTypedDescriptorPoolSet == nullptr || CurrentTypedDescriptorPoolSet->GetOwner() != cmdBufferPoolSet)
{
ASSERT(cmdBufferPoolSet);
@@ -70,26 +68,12 @@ public:
return CurrentTypedDescriptorPoolSet->AllocateDescriptorSets(*DescriptorSetsLayout, DescriptorSetHandles.Get());
}
#else
DescriptorSetRingBufferVulkan DSRingBuffer;
#endif
Array<uint32> DynamicOffsets;
public:
void Reset()
{
#if !VULKAN_USE_DESCRIPTOR_POOL_MANAGER
DSRingBuffer.Reset();
#endif
}
void Bind(CmdBufferVulkan* cmdBuffer)
{
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
vkCmdBindDescriptorSets(
cmdBuffer->GetHandle(),
VK_PIPELINE_BIND_POINT_COMPUTE,
@@ -99,9 +83,6 @@ public:
DescriptorSetHandles.Get(),
DynamicOffsets.Count(),
DynamicOffsets.Get());
#else
DSRingBuffer.Bind(cmdBuffer->GetHandle(), GetLayout()->GetHandle(), VK_PIPELINE_BIND_POINT_COMPUTE, DynamicOffsets);
#endif
}
public:
@@ -131,7 +112,7 @@ private:
VkPipelineTessellationStateCreateInfo _descTessellation;
VkPipelineViewportStateCreateInfo _descViewport;
VkPipelineDynamicStateCreateInfo _descDynamic;
VkDynamicState _dynamicStates[VK_DYNAMIC_STATE_RANGE_SIZE];
VkDynamicState _dynamicStates[3];
VkPipelineMultisampleStateCreateInfo _descMultisample;
VkPipelineDepthStencilStateCreateInfo _descDepthStencil;
VkPipelineRasterizationStateCreateInfo _descRasterization;
@@ -162,17 +143,15 @@ public:
/// <summary>
/// The cached shader bindings per stage.
/// </summary>
const ShaderBindings* ShaderBindingsPerStage[DescriptorSet::NumGfxStages];
const ShaderBindings* ShaderBindingsPerStage[DescriptorSet::GraphicsStagesCount];
/// <summary>
/// The cached shader descriptor infos per stage.
/// </summary>
const SpirvShaderDescriptorInfo* DescriptorInfoPerStage[DescriptorSet::NumGfxStages];
const SpirvShaderDescriptorInfo* DescriptorInfoPerStage[DescriptorSet::GraphicsStagesCount];
DescriptorSetWriteContainerVulkan DSWriteContainer;
DescriptorSetWriterVulkan DSWriter[DescriptorSet::NumGfxStages];
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
DescriptorSetWriterVulkan DSWriter[DescriptorSet::GraphicsStagesCount];
const DescriptorSetLayoutVulkan* DescriptorSetsLayout = nullptr;
TypedDescriptorPoolSetVulkan* CurrentTypedDescriptorPoolSet = nullptr;
@@ -181,7 +160,7 @@ public:
inline bool AcquirePoolSet(CmdBufferVulkan* cmdBuffer)
{
// Pipeline state has no current descriptor pools set or set owner is not current - acquire a new pool set
DescriptorPoolSetContainerVulkan* cmdBufferPoolSet = cmdBuffer->CurrentDescriptorPoolSetContainer;
DescriptorPoolSetContainerVulkan* cmdBufferPoolSet = cmdBuffer->GetDescriptorPoolSet();
if (CurrentTypedDescriptorPoolSet == nullptr || CurrentTypedDescriptorPoolSet->GetOwner() != cmdBufferPoolSet)
{
ASSERT(cmdBufferPoolSet);
@@ -198,26 +177,12 @@ public:
return CurrentTypedDescriptorPoolSet->AllocateDescriptorSets(*DescriptorSetsLayout, DescriptorSetHandles.Get());
}
#else
DescriptorSetRingBufferVulkan DSRingBuffer;
#endif
Array<uint32> DynamicOffsets;
public:
void Reset()
{
#if !VULKAN_USE_DESCRIPTOR_POOL_MANAGER
DSRingBuffer.Reset();
#endif
}
void Bind(CmdBufferVulkan* cmdBuffer)
{
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
vkCmdBindDescriptorSets(
cmdBuffer->GetHandle(),
VK_PIPELINE_BIND_POINT_GRAPHICS,
@@ -227,9 +192,6 @@ public:
DescriptorSetHandles.Get(),
DynamicOffsets.Count(),
DynamicOffsets.Get());
#else
DSRingBuffer.Bind(cmdBuffer->GetHandle(), GetLayout()->GetHandle(), VK_PIPELINE_BIND_POINT_GRAPHICS, DynamicOffsets);
#endif
}
/// <summary>

View File

@@ -36,23 +36,23 @@ void QueueVulkan::Submit(CmdBufferVulkan* cmdBuffer, uint32 numSignalSemaphores,
submitInfo.pSignalSemaphores = signalSemaphores;
Array<VkSemaphore> waitSemaphores;
if (cmdBuffer->WaitSemaphores.HasItems())
if (cmdBuffer->_waitSemaphores.HasItems())
{
waitSemaphores.EnsureCapacity((uint32)cmdBuffer->WaitSemaphores.Count());
for (auto semaphore : cmdBuffer->WaitSemaphores)
waitSemaphores.EnsureCapacity((uint32)cmdBuffer->_waitSemaphores.Count());
for (auto semaphore : cmdBuffer->_waitSemaphores)
{
waitSemaphores.Add(semaphore->GetHandle());
}
submitInfo.waitSemaphoreCount = (uint32)cmdBuffer->WaitSemaphores.Count();
submitInfo.waitSemaphoreCount = (uint32)cmdBuffer->_waitSemaphores.Count();
submitInfo.pWaitSemaphores = waitSemaphores.Get();
submitInfo.pWaitDstStageMask = cmdBuffer->WaitFlags.Get();
submitInfo.pWaitDstStageMask = cmdBuffer->_waitFlags.Get();
}
VALIDATE_VULKAN_RESULT(vkQueueSubmit(_queue, 1, &submitInfo, fence->GetHandle()));
cmdBuffer->_state = CmdBufferVulkan::State::Submitted;
cmdBuffer->MarkSemaphoresAsSubmitted();
cmdBuffer->SubmittedFenceCounter = cmdBuffer->FenceSignaledCounter;
cmdBuffer->_submittedFenceCounter = cmdBuffer->_fenceSignaledCounter;
#if 0
// Wait for the GPU to be idle on every submit (useful for tracking GPU hangs)

View File

@@ -244,7 +244,9 @@ String RenderToolsVulkan::GetVkErrorString(VkResult result)
VKERR(VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT);
VKERR(VK_ERROR_FRAGMENTATION_EXT);
VKERR(VK_ERROR_NOT_PERMITTED_EXT);
#if VK_HEADER_VERSION < 140
VKERR(VK_RESULT_RANGE_SIZE);
#endif
default:
sb.AppendFormat(TEXT("0x{0:x}"), static_cast<uint32>(result));
break;

View File

@@ -443,6 +443,23 @@ void PlatformBase::TrackAllocation(uint64 size)
#endif
void PlatformBase::GetEnvironmentVariables(Dictionary<String, String>& result)
{
// Not supported
}
bool PlatformBase::GetEnvironmentVariable(const String& name, String& value)
{
// Not supported
return true;
}
bool PlatformBase::SetEnvironmentVariable(const String& name, const String& value)
{
// Not supported
return true;
}
int32 PlatformBase::StartProcess(const StringView& filename, const StringView& args, const StringView& workingDir, bool hiddenWindow, bool waitForEnd)
{
// Not supported

View File

@@ -61,11 +61,11 @@ protected:
PACK_STRUCT(struct Data
{
float FirstOrder;
float First;
float AtmosphereR;
int AtmosphereLayer;
float Dummy0;
Vector4 DhdH;
Vector4 dhdh;
});
namespace AtmospherePreComputeImpl
@@ -367,17 +367,17 @@ void AtmospherePreComputeService::Dispose()
release();
}
void GetLayerValue(int32 layer, float& atmosphereR, Vector4& DhdH)
void GetLayerValue(int32 layer, float& atmosphereR, Vector4& dhdh)
{
float r = layer / Math::Max<float>(InscatterAltitudeSampleNum - 1.0f, 1.0f);
r = r * r;
r = Math::Sqrt(RadiusGround * RadiusGround + r * (RadiusAtmosphere * RadiusAtmosphere - RadiusGround * RadiusGround)) + (layer == 0 ? 0.01f : (layer == InscatterAltitudeSampleNum - 1 ? -0.001f : 0.0f));
float dMin = RadiusAtmosphere - r;
float dMax = Math::Sqrt(r * r - RadiusGround * RadiusGround) + Math::Sqrt(RadiusAtmosphere * RadiusAtmosphere - RadiusGround * RadiusGround);
float dMinP = r - RadiusGround;
float dMaxP = Math::Sqrt(r * r - RadiusGround * RadiusGround);
const float dMin = RadiusAtmosphere - r;
const float dMax = Math::Sqrt(r * r - RadiusGround * RadiusGround) + Math::Sqrt(RadiusAtmosphere * RadiusAtmosphere - RadiusGround * RadiusGround);
const float dMinP = r - RadiusGround;
const float dMaxP = Math::Sqrt(r * r - RadiusGround * RadiusGround);
atmosphereR = r;
DhdH = Vector4(dMin, dMax, dMinP, dMaxP);
dhdh = Vector4(dMin, dMax, dMinP, dMaxP);
}
void AtmospherePreComputeImpl::onRender(RenderTask* task, GPUContext* context)
@@ -390,8 +390,8 @@ void AtmospherePreComputeImpl::onRender(RenderTask* task, GPUContext* context)
}
ASSERT(_isUpdatePending && _updateFrameNumber == 0);
auto shader = _shader->GetShader();
auto cb = shader->GetCB(0);
const auto shader = _shader->GetShader();
const auto cb = shader->GetCB(0);
Data data;
// Compute transmittance texture T (line 1 in algorithm 4.1)
@@ -415,7 +415,7 @@ void AtmospherePreComputeImpl::onRender(RenderTask* task, GPUContext* context)
context->SetState(_psInscatter1_A);
for (int32 layer = 0; layer < InscatterAltitudeSampleNum; layer++)
{
GetLayerValue(layer, data.AtmosphereR, data.DhdH);
GetLayerValue(layer, data.AtmosphereR, data.dhdh);
data.AtmosphereLayer = layer;
context->UpdateCB(cb, &data);
context->BindCB(0, cb);
@@ -426,7 +426,7 @@ void AtmospherePreComputeImpl::onRender(RenderTask* task, GPUContext* context)
context->SetState(_psInscatter1_B);
for (int32 layer = 0; layer < InscatterAltitudeSampleNum; layer++)
{
GetLayerValue(layer, data.AtmosphereR, data.DhdH);
GetLayerValue(layer, data.AtmosphereR, data.dhdh);
data.AtmosphereLayer = layer;
context->UpdateCB(cb, &data);
context->BindCB(0, cb);
@@ -439,7 +439,7 @@ void AtmospherePreComputeImpl::onRender(RenderTask* task, GPUContext* context)
//// old way to render Inscatter1 to DeltaSR and DeltaSM at once but didn't work well :/ (no time to find out why)
//for (int32 layer = 0; layer < InscatterAltitudeSampleNum; layer++)
//{
// GetLayerValue(layer, data.AtmosphereR, data.DhdH);
// GetLayerValue(layer, data.AtmosphereR, data.dhdh);
// data.AtmosphereLayer = layer;
// cb->SetData(&data);
// context->Bind(0, cb);
@@ -472,7 +472,7 @@ void AtmospherePreComputeImpl::onRender(RenderTask* task, GPUContext* context)
context->BindSR(5, AtmosphereDeltaSM->ViewVolume());
for (int32 layer = 0; layer < InscatterAltitudeSampleNum; layer++)
{
GetLayerValue(layer, data.AtmosphereR, data.DhdH);
GetLayerValue(layer, data.AtmosphereR, data.dhdh);
data.AtmosphereLayer = layer;
context->UpdateCB(cb, &data);
context->BindCB(0, cb);
@@ -489,14 +489,14 @@ void AtmospherePreComputeImpl::onRender(RenderTask* task, GPUContext* context)
context->UnBindSR(6);
context->SetViewportAndScissors((float)InscatterWidth, (float)InscatterHeight);
context->SetState(_psInscatterS);
data.FirstOrder = order == 2 ? 1.0f : 0.0f;
data.First = order == 2 ? 1.0f : 0.0f;
context->BindSR(0, AtmosphereTransmittance);
context->BindSR(3, AtmosphereDeltaE);
context->BindSR(4, AtmosphereDeltaSR->ViewVolume());
context->BindSR(5, AtmosphereDeltaSM->ViewVolume());
for (int32 layer = 0; layer < InscatterAltitudeSampleNum; layer++)
{
GetLayerValue(layer, data.AtmosphereR, data.DhdH);
GetLayerValue(layer, data.AtmosphereR, data.dhdh);
data.AtmosphereLayer = layer;
context->UpdateCB(cb, &data);
context->BindCB(0, cb);
@@ -523,7 +523,7 @@ void AtmospherePreComputeImpl::onRender(RenderTask* task, GPUContext* context)
context->BindSR(6, AtmosphereDeltaJ->ViewVolume());
for (int32 layer = 0; layer < InscatterAltitudeSampleNum; layer++)
{
GetLayerValue(layer, data.AtmosphereR, data.DhdH);
GetLayerValue(layer, data.AtmosphereR, data.dhdh);
data.AtmosphereLayer = layer;
context->UpdateCB(cb, &data);
context->BindCB(0, cb);
@@ -545,7 +545,7 @@ void AtmospherePreComputeImpl::onRender(RenderTask* task, GPUContext* context)
context->BindSR(4, AtmosphereDeltaSR->ViewVolume());
for (int32 layer = 0; layer < InscatterAltitudeSampleNum; layer++)
{
GetLayerValue(layer, data.AtmosphereR, data.DhdH);
GetLayerValue(layer, data.AtmosphereR, data.dhdh);
data.AtmosphereLayer = layer;
context->UpdateCB(cb, &data);
context->BindCB(0, cb);

View File

@@ -16,7 +16,6 @@
#include "Engine/ContentImporters/AssetsImportingManager.h"
#include "Engine/ContentImporters/CreateMaterial.h"
#include "ThirdParty/meshoptimizer/meshoptimizer.h"
#include <regex>
void RemoveNamespace(String& name)
{
@@ -1193,27 +1192,15 @@ bool ModelTool::ImportModel(const String& path, ModelData& meshData, Options opt
int32 ModelTool::DetectLodIndex(const String& nodeName)
{
// Try detect mesh lod index
int32 index;
String result;
std::match_results<const Char*> match;
String reversed = nodeName;
reversed.Reverse();
// Find '<name>LOD<index>' case
const std::wregex regex2(TEXT("^(\\d+)DOL"));
if (regex_search(*reversed, match, regex2) && match.size() == 2)
const int32 index = nodeName.FindLast(TEXT("LOD"));
if (index != -1)
{
// Get result
String num(match[1].str().c_str());
num.Reverse();
// Parse value
if (!StringUtils::Parse(*num, num.Length(), &index))
int32 num;
if (!StringUtils::Parse(nodeName.Get() + index + 3, &num))
{
if (index >= 0 && index < MODEL_MAX_LODS)
if (num >= 0 && num < MODEL_MAX_LODS)
{
return index;
return num;
}
LOG(Warning, "Invalid mesh level of detail index at node \'{0}\'. Maximum supported amount of LODs is {1}.", nodeName, MODEL_MAX_LODS);

View File

@@ -635,7 +635,7 @@ bool TextureTool::ImportTextureDirectXTex(ImageType type, const StringView& path
{
sourceDxgiFormat = ToDxgiFormat(PixelFormatExtensions::ToNonsRGB(ToPixelFormat(sourceDxgiFormat)));
((DirectX::TexMetadata&)currentImage->GetMetadata()).format = sourceDxgiFormat;
for (int32 i = 0; i < currentImage->GetImageCount(); i++)
for (size_t i = 0; i < currentImage->GetImageCount(); i++)
((DirectX::Image*)currentImage->GetImages())[i].format = sourceDxgiFormat;
}

View File

@@ -194,7 +194,7 @@ namespace FlaxEngine.GUI
{
// Auto hide if mouse leaves control area
Vector2 mousePos = Input.MouseScreenPosition;
Vector2 location = _showTarget.ScreenToClient(mousePos / Platform.DpiScale);
Vector2 location = _showTarget.ScreenToClient(mousePos);
if (!_showTarget.OnTestTooltipOverControl(ref location))
{
// Mouse left or sth

View File

@@ -234,7 +234,7 @@ namespace FlaxEngine.GUI
/// <inheritdoc />
public override Vector2 ScreenToClient(Vector2 location)
{
return _window.ScreenToClient(location);
return _window.ScreenToClient(location) / Platform.DpiScale;
}
/// <inheritdoc />

View File

@@ -13,5 +13,5 @@ using System.Runtime.InteropServices;
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
[assembly: Guid("095aaaed-cc57-6182-57cc-8261bcf06644")]
[assembly: AssemblyVersion("1.0.6213")]
[assembly: AssemblyFileVersion("1.0.6213")]
[assembly: AssemblyVersion("1.0.6214")]
[assembly: AssemblyFileVersion("1.0.6214")]

View File

@@ -5,11 +5,11 @@
#include "Engine/Core/Compiler.h"
#define FLAXENGINE_NAME "FlaxEngine"
#define FLAXENGINE_VERSION Version(1, 0, 6213)
#define FLAXENGINE_VERSION_TEXT "1.0.6213"
#define FLAXENGINE_VERSION Version(1, 0, 6214)
#define FLAXENGINE_VERSION_TEXT "1.0.6214"
#define FLAXENGINE_VERSION_MAJOR 1
#define FLAXENGINE_VERSION_MINOR 0
#define FLAXENGINE_VERSION_BUILD 6213
#define FLAXENGINE_VERSION_BUILD 6214
#define FLAXENGINE_COMPANY "Flax"
#define FLAXENGINE_COPYRIGHT "Copyright (c) 2012-2020 Wojciech Figat. All rights reserved."

View File

@@ -57,9 +57,8 @@ const static int InscatterNuNum = 8;
const static int AtmosphericFogInscatterAltitudeSampleNum = 4;
// Configuration
#define TRANSMITTANCE_NON_LINEAR 1
#define INSCATTER_NON_LINEAR 1
#define ATMOSPHERIC_TEXTURE_SAMPLE_FIX 1
#define TRANSMITTANCE_NON_LINEAR 1
#define INSCATTER_NON_LINEAR 1
#ifndef ATMOSPHERIC_NO_SUN_DISK
#define ATMOSPHERIC_NO_SUN_DISK 0
@@ -90,15 +89,15 @@ Texture3D AtmosphereInscatterTexture : register(t2);
float2 GetTransmittanceUV(float radius, float Mu)
{
float U, V;
float u, v;
#if TRANSMITTANCE_NON_LINEAR
V = sqrt((radius - RadiusGround) / (RadiusAtmosphere - RadiusGround));
U = atan((Mu + 0.15) / (1.0 + 0.15) * tan(1.5)) / 1.5;
v = sqrt((radius - RadiusGround) / (RadiusAtmosphere - RadiusGround));
u = atan((Mu + 0.15) / (1.0 + 0.15) * tan(1.5)) / 1.5;
#else
V = (radius - RadiusGround) / (RadiusAtmosphere - RadiusGround);
U = (Mu + 0.15) / (1.0 + 0.15);
v = (radius - RadiusGround) / (RadiusAtmosphere - RadiusGround);
u = (Mu + 0.15) / (1.0 + 0.15);
#endif
return float2(U, V);
return float2(u, v);
}
void GetTransmittanceRMuS(float2 uv, out float radius, out float MuS)
@@ -116,9 +115,9 @@ void GetTransmittanceRMuS(float2 uv, out float radius, out float MuS)
float2 GetIrradianceUV(float radius, float MuS)
{
float V = (radius - RadiusGround) / (RadiusAtmosphere - RadiusGround);
float U = (MuS + 0.2) / (1.0 + 0.2);
return float2(U, V);
float v = (radius - RadiusGround) / (RadiusAtmosphere - RadiusGround);
float u = (MuS + 0.2) / (1.0 + 0.2);
return float2(u, v);
}
void GetIrradianceRMuS(float2 uv, out float radius, out float MuS)
@@ -137,9 +136,6 @@ float4 Texture4DSample(Texture3D tex, float radius, float Mu, float MuS, float N
float4 TexOffset = RMu < 0.0 && Delta > 0.0 ? float4(1.0, 0.0, 0.0, 0.5 - 0.5 / float(InscatterMuNum)) : float4(-1.0, H * H, H, 0.5 + 0.5 / float(InscatterMuNum));
float MuR = 0.5 / float(AtmosphericFogInscatterAltitudeSampleNum) + Rho / H * (1.0 - 1.0 / float(AtmosphericFogInscatterAltitudeSampleNum));
float MuMu = TexOffset.w + (RMu * TexOffset.x + sqrt(Delta + TexOffset.y)) / (Rho + TexOffset.z) * (0.5 - 1.0 / float(InscatterMuNum));
// paper formula
//float MuMuS = 0.5 / float(InscatterMuSNum) + max((1.0 - exp(-3.0 * MuS - 0.6)) / (1.0 - exp(-3.6)), 0.0) * (1.0 - 1.0 / float(InscatterMuSNum));
// better formula
float MuMuS = 0.5 / float(InscatterMuSNum) + (atan(max(MuS, -0.1975) * tan(1.26 * 1.1)) / 1.1 + (1.0 - 0.26)) * 0.5 * (1.0 - 1.0 / float(InscatterMuSNum));
#else
float MuR = 0.5 / float(AtmosphericFogInscatterAltitudeSampleNum) + Rho / H * (1.0 - 1.0 / float(AtmosphericFogInscatterAltitudeSampleNum));
@@ -154,35 +150,32 @@ float4 Texture4DSample(Texture3D tex, float radius, float Mu, float MuS, float N
+ tex.SampleLevel(SamplerLinearClamp, float3((MuNu + MuMuS + 1.0) / float(InscatterNuNum), MuMu, MuR), 0) * LerpValue;
}
float Mod(float X, float Y)
float Mod(float x, float y)
{
return X - Y * floor(X/Y);
return x - y * floor(x / y);
}
void GetMuMuSNu(float2 uv, float radius, float4 DhdH, out float Mu, out float MuS, out float Nu)
{
float X = uv.x * float(InscatterMuSNum * InscatterNuNum) - 0.5;
float Y = uv.y * float(InscatterMuNum) - 0.5;
float x = uv.x * float(InscatterMuSNum * InscatterNuNum) - 0.5;
float y = uv.y * float(InscatterMuNum) - 0.5;
#if INSCATTER_NON_LINEAR
if (Y < float(InscatterMuNum) * 0.5f)
if (y < float(InscatterMuNum) * 0.5f)
{
float D = 1.0 - Y / (float(InscatterMuNum) * 0.5f - 1.0);
D = min(max(DhdH.z, D * DhdH.w), DhdH.w * 0.999);
Mu = (RadiusGround * RadiusGround - radius * radius - D * D) / (2.0 * radius * D);
float d = 1.0 - y / (float(InscatterMuNum) * 0.5f - 1.0);
d = min(max(DhdH.z, d * DhdH.w), DhdH.w * 0.999);
Mu = (RadiusGround * RadiusGround - radius * radius - d * d) / (2.0 * radius * d);
Mu = min(Mu, -sqrt(1.0 - (RadiusGround / radius) * (RadiusGround / radius)) - 0.001);
}
else
{
float D = (Y - float(InscatterMuNum) * 0.5f) / (float(InscatterMuNum) * 0.5f - 1.0);
D = min(max(DhdH.x, D * DhdH.y), DhdH.y * 0.999);
Mu = (RadiusAtmosphere * RadiusAtmosphere - radius * radius - D * D) / (2.0 * radius * D);
float d = (y - float(InscatterMuNum) * 0.5f) / (float(InscatterMuNum) * 0.5f - 1.0);
d = min(max(DhdH.x, d * DhdH.y), DhdH.y * 0.999);
Mu = (RadiusAtmosphere * RadiusAtmosphere - radius * radius - d * d) / (2.0 * radius * d);
}
MuS = Mod(X, float(InscatterMuSNum)) / (float(InscatterMuSNum) - 1.0);
// paper formula
//MuS = -(0.6 + log(1.0 - MuS * (1.0 - exp(-3.6)))) / 3.0;
// better formula
MuS = Mod(x, float(InscatterMuSNum)) / (float(InscatterMuSNum) - 1.0);
MuS = tan((2.0 * MuS - 1.0 + 0.26) * 1.1) / tan(1.26 * 1.1);
Nu = -1.0 + floor(X / float(InscatterMuSNum)) / (float(InscatterNuNum) - 1.0) * 2.0;
Nu = -1.0 + floor(x / float(InscatterMuSNum)) / (float(InscatterNuNum) - 1.0) * 2.0;
#else
Mu = -1.0 + 2.0 * Y / (float(InscatterMuNum) - 1.0);
MuS = Mod(X, float(InscatterMuSNum)) / (float(InscatterMuSNum) - 1.0);
@@ -191,10 +184,7 @@ void GetMuMuSNu(float2 uv, float radius, float4 DhdH, out float Mu, out float Mu
#endif
}
/**
* Nearest intersection of ray r,mu with ground or top atmosphere boundary
* mu=cos(ray zenith angle at ray origin)
*/
// Nearest intersection of ray r,mu with ground or top atmosphere boundary, mu=cos(ray zenith angle at ray origin)
float Limit(float radius, float Mu)
{
float Dout = -radius * Mu + sqrt(radius * radius * (Mu * Mu - 1.0) + RadiusLimit * RadiusLimit);
@@ -210,32 +200,20 @@ float Limit(float radius, float Mu)
return Dout;
}
/**
* Transmittance(=transparency) of atmosphere for infinite ray (r,mu)
* (mu=cos(view zenith angle)), intersections with ground ignored
*/
// Transmittance(=transparency) of atmosphere for infinite ray (r,mu) (mu=cos(view zenith angle)), intersections with ground ignored
float3 Transmittance(float radius, float Mu)
{
float2 uv = GetTransmittanceUV(radius, Mu);
return AtmosphereTransmittanceTexture.SampleLevel(SamplerLinearClamp, uv, 0).rgb;
}
/**
* Transmittance(=transparency) of atmosphere for infinite ray (r,mu)
* (mu=cos(view zenith angle)), or zero if ray intersects ground
*/
// Transmittance(=transparency) of atmosphere for infinite ray (r,mu) (mu=cos(view zenith angle)), or zero if ray intersects ground
float3 TransmittanceWithShadow(float radius, float Mu)
{
// Need to correct calculation based on shadow feature, currently don't consider
//return Mu < -sqrt(1.0 - (RadiusGround / radius) * (RadiusGround / radius)) ? float3(0.f, 0.f, 0.f) : Transmittance(Radius, Mu);
return Transmittance(radius, Mu);
}
/**
* Transmittance(=transparency) of atmosphere between x and x0
* Assume segment x,x0 not intersecting ground
* D = Distance between x and x0, mu=cos(zenith angle of [x,x0) ray at x)
*/
//Transmittance(=transparency) of atmosphere between x and x0. Assume segment x,x0 not intersecting ground. D = Distance between x and x0, mu=cos(zenith angle of [x,x0) ray at x)
float3 TransmittanceWithDistance(float radius, float Mu, float D)
{
float3 result;
@@ -252,42 +230,36 @@ float3 TransmittanceWithDistance(float radius, float Mu, float D)
return result;
}
/**
* Transmittance(=transparency) of atmosphere between x and x0
* Assume segment x,x0 not intersecting ground
* radius=||x||, Mu=cos(zenith angle of [x,x0) ray at x), v=unit direction vector of [x,x0) ray
*/
// Transmittance(=transparency) of atmosphere between x and x0. Assume segment x,x0 not intersecting ground radius=||x||, Mu=cos(zenith angle of [x,x0) ray at x), v=unit direction vector of [x,x0) ray
float3 TransmittanceWithDistance(float radius, float Mu, float3 V, float3 X0)
{
float3 result;
float R1 = length(X0);
float d1 = length(X0);
float Mu1 = dot(X0, V) / radius;
if (Mu > 0.0)
{
result = min(Transmittance(radius, Mu) / Transmittance(R1, Mu1), 1.0);
}
result = min(Transmittance(radius, Mu) / Transmittance(d1, Mu1), 1.0);
else
{
result = min(Transmittance(R1, -Mu1) / Transmittance(radius, -Mu), 1.0);
}
result = min(Transmittance(d1, -Mu1) / Transmittance(radius, -Mu), 1.0);
return result;
}
/**
* Optical depth for ray (r,mu) of length d, using analytic formula
* (mu=cos(view zenith angle)), intersections with ground ignored
* H=height scale of exponential density function
*/
// Optical depth for ray (r,mu) of length d, using analytic formula (mu=cos(view zenith angle)), intersections with ground ignored H=height scale of exponential density function
float OpticalDepthWithDistance(float H, float radius, float Mu, float D)
{
float particleDensity = 6.2831; // REK 04, Table 2
float A = sqrt((0.5/H)*radius);
float2 A01 = A * float2(Mu, Mu + D / radius);
float a = sqrt(0.5 / H * radius);
float2 A01 = a * float2(Mu, Mu + D / radius);
float2 A01Sign = sign(A01);
float2 A01Squared = A01*A01;
float X = A01Sign.y > A01Sign.x ? exp(A01Squared.x) : 0.0;
float2 Y = A01Sign / (2.3193 * abs(A01) + sqrt(1.52 * A01Squared + 4.0)) * float2(1.0, exp(-D / H*(D / (2.0 * radius) + Mu)));
return sqrt((particleDensity * H)*radius) * exp((RadiusGround - radius) / H) * (X + dot(Y, float2(1.0, -1.0)));
float x = A01Sign.y > A01Sign.x ? exp(A01Squared.x) : 0.0;
float2 y = A01Sign / (2.3193 * abs(A01) + sqrt(1.52 * A01Squared + 4.0)) * float2(1.0, exp(-D / H*(D / (2.0 * radius) + Mu)));
return sqrt((particleDensity * H)*radius) * exp((RadiusGround - radius) / H) * (x + dot(y, float2(1.0, -1.0)));
}
// Transmittance(=transparency) of atmosphere for ray (r,mu) of length d (mu=cos(view zenith angle)), intersections with ground ignored uses analytic formula instead of transmittance texture, REK 04, Atmospheric Transparency
float3 AnalyticTransmittance(float R, float Mu, float D)
{
return exp(- BetaRayleighScattering * OpticalDepthWithDistance(HeightScaleRayleigh, R, Mu, D) - BetaMieExtinction * OpticalDepthWithDistance(HeightScaleMie, R, Mu, D));
}
float3 Irradiance(Texture2D tex, float r, float muS)
@@ -296,32 +268,23 @@ float3 Irradiance(Texture2D tex, float r, float muS)
return tex.SampleLevel(SamplerLinearClamp, uv, 0).rgb;
}
/** Rayleigh phase function */
// Rayleigh phase function
float PhaseFunctionR(float Mu)
{
return (3.0 / (16.0 * PI)) * (1.0 + Mu * Mu);
}
/** Mie phase function */
// Mie phase function
float PhaseFunctionM(float Mu)
{
return 1.5 * 1.0 / (4.0 * PI) * (1.0 - MieG * MieG) * pow(1.0 + (MieG * MieG) - 2.0 * MieG * Mu, -3.0/2.0) * (1.0 + Mu * Mu) / (2.0 + MieG * MieG);
}
/** Approximated single Mie scattering (cf. approximate Cm in paragraph "Angular precision") */
// Approximated single Mie scattering (cf. approximate Cm in paragraph "Angular precision")
float3 GetMie(float4 RayMie)
{
// RayMie.rgb=C*, RayMie.w=Cm,r
return RayMie.rgb * RayMie.w / max(RayMie.r, 1e-4) * (BetaRayleighScattering.rrr / BetaRayleighScattering.rgb);
}
/** Transmittance(=transparency) of atmosphere for ray (r,mu) of length d
* (mu=cos(view zenith angle)), intersections with ground ignored
* uses analytic formula instead of transmittance texture, REK 04, Atmospheric Transparency
*/
float3 AnalyticTransmittance(float R, float Mu, float D)
{
return exp(- BetaRayleighScattering * OpticalDepthWithDistance(HeightScaleRayleigh, R, Mu, D) - BetaMieExtinction * OpticalDepthWithDistance(HeightScaleMie, R, Mu, D));
}
#endif

View File

@@ -24,39 +24,38 @@
static const float HeightOffset = 0.01f;
/** inscattered light along ray x+tv, when sun in direction s (=S[L]-T(x,x0)S[L]|x0) */
float3 GetInscatterColor(float fogDepth, float3 X, float T, float3 V, float3 S, float radius, float Mu, out float3 attenuation, bool isSceneGeometry)
// inscattered light along ray x+tv, when sun in direction s (=S[L]-T(x,x0)S[L]|x0)
float3 GetInscatterColor(float fogDepth, float3 X, float T, float3 V, float3 S, float radius, float Mu, out float3 attenuation, bool isSceneGeometry)
{
float3 result = float3(0.f, 0.f, 0.f); // X in space and ray looking in space, intialize
attenuation = float3(1.f, 1.f, 1.f);
float3 result = float3(0.0f, 0.0f, 0.0f);
attenuation = float3(1.0f, 1.0f, 1.0f);
float D = -radius * Mu - sqrt(radius * radius * (Mu * Mu - 1.0) + RadiusAtmosphere * RadiusAtmosphere);
if (D > 0.0)
{
// if X in space and ray intersects atmosphere
// move X to nearest intersection of ray with top atmosphere boundary
X += D * V;
T -= D;
Mu = (radius * Mu + D) / RadiusAtmosphere;
radius = RadiusAtmosphere;
}
float Epsilon = 0.005f;//maybe 0.004?
if (radius < RadiusGround + HeightOffset + Epsilon)
float d = -radius * Mu - sqrt(radius * radius * (Mu * Mu - 1.0) + RadiusAtmosphere * RadiusAtmosphere);
if (d > 0.0f)
{
float Diff = (RadiusGround + HeightOffset + Epsilon) - radius;
X -= Diff * V;
T -= Diff;
radius = RadiusGround + HeightOffset + Epsilon;
// if X in space and ray intersects atmosphere
// move X to nearest intersection of ray with top atmosphere boundary
X += d * V;
T -= d;
Mu = (radius * Mu + d) / RadiusAtmosphere;
radius = RadiusAtmosphere;
}
float epsilon = 0.005f;
if (radius < RadiusGround + HeightOffset + epsilon)
{
float diff = (RadiusGround + HeightOffset + epsilon) - radius;
X -= diff * V;
T -= diff;
radius = RadiusGround + HeightOffset + epsilon;
Mu = dot(X, V) / radius;
}
if (radius <= RadiusAtmosphere && fogDepth > 0.f)
{
if (radius <= RadiusAtmosphere && fogDepth > 0.0f)
{
float3 X0 = X + T * V;
float R0 = length(X0);
// if ray intersects atmosphere
float Nu = dot(V, S);
float MuS = dot(X, S) / radius;
@@ -64,11 +63,11 @@ float3 GetInscatterColor(float fogDepth, float3 X, float T, float3 V, float3 S,
if (isSceneGeometry)
{
Mu = max(Mu, MuHorizon + Epsilon + 0.15);
Mu = max(Mu, MuHorizon + epsilon + 0.15f);
}
else
{
Mu = max(Mu, MuHorizon + Epsilon);
{
Mu = max(Mu, MuHorizon + epsilon);
}
float MuOriginal = Mu;
@@ -81,9 +80,8 @@ float3 GetInscatterColor(float fogDepth, float3 X, float T, float3 V, float3 S,
{
V.z = max(V.z, 0.15);
V = normalize(V);
float3 X1 = X + T * V;
float R1 = length(X1);
Mu = dot(X1, V) / R1;
float3 x1 = X + T * V;
Mu = dot(x1, V) / length(x1);
}
}
@@ -91,264 +89,186 @@ float3 GetInscatterColor(float fogDepth, float3 X, float T, float3 V, float3 S,
float phaseM = PhaseFunctionM(Nu);
float4 inscatter = max(Texture4DSample(AtmosphereInscatterTexture, radius, Mu, MuS, Nu), 0.0);
if (T > 0.0)
if (T > 0.0)
{
#if ATMOSPHERIC_TEXTURE_SAMPLE_FIX
// Avoids imprecision problems in transmittance computations based on textures
attenuation = AnalyticTransmittance(radius, Mu, T);
#else
attenuation = TransmittanceWithDistance(radius, Mu, V, X0);
#endif
float Mu0 = dot(X0, V) / R0;
float MuS0 = dot(X0, S) / R0;
if (isSceneGeometry)
{
R0 = max(R0, radius);
}
if (R0 > RadiusGround + HeightOffset)
{
if (blendRatio < 1.0)
{
inscatter = max(inscatter - attenuation.rgbr * Texture4DSample(AtmosphereInscatterTexture, R0, Mu0, MuS0, Nu), 0.0);
#if ATMOSPHERIC_TEXTURE_SAMPLE_FIX
// avoids imprecision problems near horizon by interpolating between two points above and below horizon
if (!isSceneGeometry )
if (!isSceneGeometry)
{
if (abs(Mu - MuHorizon) < Epsilon)
if (abs(Mu - MuHorizon) < epsilon)
{
float Alpha = ((Mu - MuHorizon) + Epsilon) * 0.5f / Epsilon;
Mu = MuHorizon - Epsilon;
Mu = MuHorizon - epsilon;
R0 = sqrt(radius * radius + T * T + 2.0 * radius * T * Mu);
Mu0 = (radius * Mu + T) / R0;
Mu0 = max(MuHorizon + Epsilon, Mu0);
float4 Inscatter0 = Texture4DSample(AtmosphereInscatterTexture, radius, Mu, MuS, Nu);
float4 Inscatter1 = Texture4DSample(AtmosphereInscatterTexture, R0, Mu0, MuS0, Nu);
float4 InscatterA = max(Inscatter0 - attenuation.rgbr * Inscatter1, 0.0);
Mu0 = max(MuHorizon + epsilon, Mu0);
float4 inscatter0 = Texture4DSample(AtmosphereInscatterTexture, radius, Mu, MuS, Nu);
float4 inscatter1 = Texture4DSample(AtmosphereInscatterTexture, R0, Mu0, MuS0, Nu);
float4 inscatterA = max(inscatter0 - attenuation.rgbr * inscatter1, 0.0);
Mu = MuHorizon + Epsilon;
Mu = MuHorizon + epsilon;
R0 = sqrt(radius * radius + T * T + 2.0 * radius * T * Mu);
Mu0 = (radius * Mu + T) / R0;
Mu0 = max(MuHorizon + Epsilon, Mu0);
Inscatter0 = Texture4DSample(AtmosphereInscatterTexture, radius, Mu, MuS, Nu);
Inscatter1 = Texture4DSample(AtmosphereInscatterTexture, R0, Mu0, MuS0, Nu);
float4 InscatterB = max(Inscatter0 - attenuation.rgbr * Inscatter1, 0.0);
Mu0 = max(MuHorizon + epsilon, Mu0);
inscatter0 = Texture4DSample(AtmosphereInscatterTexture, radius, Mu, MuS, Nu);
inscatter1 = Texture4DSample(AtmosphereInscatterTexture, R0, Mu0, MuS0, Nu);
float4 inscatterB = max(inscatter1 - attenuation.rgbr * inscatter1, 0.0);
inscatter = lerp(InscatterA, InscatterB, Alpha);
float alpha = ((Mu - MuHorizon) + epsilon) * 0.5f / epsilon;
inscatter = lerp(inscatterA, inscatterB, alpha);
}
}
else if (blendRatio > 0.0)
{
inscatter = lerp(inscatter, (1.0 - attenuation.rgbr) * max(Texture4DSample(AtmosphereInscatterTexture, radius, MuOriginal, MuS, Nu), 0.0), blendRatio);
inscatter = lerp(inscatter, (1.0 - attenuation.rgbr) * max(Texture4DSample(AtmosphereInscatterTexture, radius, MuOriginal, MuS, Nu), 0.0), blendRatio);
}
#endif
}
else
{
inscatter = (1.0 - attenuation.rgbr) * inscatter;
inscatter = (1.0 - attenuation.rgbr) * inscatter;
}
}
}
#if ATMOSPHERIC_TEXTURE_SAMPLE_FIX
// Avoids imprecision problems in Mie scattering when sun is below horizon
inscatter.w *= smoothstep(0.00, 0.02, MuS);
#endif
result = max(inscatter.rgb * phaseR + GetMie(inscatter) * phaseM, 0.0);
}
inscatter.w *= smoothstep(0.00, 0.02, MuS);
result = max(inscatter.rgb * phaseR + GetMie(inscatter) * phaseM, 0.0);
}
return result;
}
// Ground radiance at end of ray x+tv, when sun in direction s attenuated bewteen ground and viewer (=R[L0]+R[L*])
// Ground radiance at end of ray x+tv, when sun in direction s attenuated between ground and viewer (=R[L0]+R[L*])
float3 GetGroundColor(float4 sceneColor, float3 X, float T, float3 V, float3 S, float radius, float3 attenuation, bool isSceneGeometry)
{
float3 result = float3(0.f, 0.f, 0.f); // ray looking at the sky (for intial value)
if (T > 0.0)
{
float3 result = float3(0.0f, 0.0f, 0.0f);
if (T > 0.0f)
{
// if ray hits ground surface
// ground Reflectance at end of ray, X0
float3 X0 = X + T * V;
float R0 = length(X0);
float3 N = X0 / R0;
N = X0 / R0;
// ground Reflectance at end of ray, X0
float3 X0 = X + T * V;
float R0 = length(X0);
float3 N = X0 / R0;
sceneColor.xyz = saturate(sceneColor.xyz + 0.05);
float4 reflectance = sceneColor * float4(0.2, 0.2, 0.2, 1.0);
float4 reflectance = sceneColor * float4(0.2, 0.2, 0.2, 1.0);
// direct sun light (radiance) reaching X0
float MuS = dot(N, S);
float3 SunLight = isSceneGeometry ? float3(0.f, 0.f, 0.f) : TransmittanceWithShadow(R0, MuS);
// direct sun light (radiance) reaching X0
float MuS = dot(N, S);
float3 sunLight = isSceneGeometry ? float3(0.f, 0.f, 0.f) : TransmittanceWithShadow(R0, MuS);
// precomputed sky light (irradiance) (=E[L*]) at X0
float3 GroundSkyLight = Irradiance(AtmosphereIrradianceTexture, R0, MuS);
// precomputed sky light (irradiance) (=E[L*]) at X0
float3 groundSkyLight = Irradiance(AtmosphereIrradianceTexture, R0, MuS);
// light reflected at X0 (=(R[L0]+R[L*])/T(X,X0))
float3 groundColor = (reflectance.rgb * (max(MuS, 0.0) * SunLight + GroundSkyLight)) / PI;
// light reflected at X0 (=(R[L0]+R[L*])/T(X,X0))
float3 groundColor = reflectance.rgb * ((max(MuS, 0.0) * sunLight + groundSkyLight) / PI);
// water specular color due to SunLight
if (!isSceneGeometry && reflectance.w > 0.0)
// water specular color due to SunLight
if (!isSceneGeometry && reflectance.w > 0.0)
{
float3 H = normalize(S - V);
float fresnel = 0.02 + 0.98 * pow(1.0 - dot(-V, H), 5.0);
float waterBrdf = fresnel * pow(max(dot(H, N), 0.0), 150.0);
groundColor += reflectance.w * max(waterBrdf, 0.0) * SunLight;
}
float3 H = normalize(S - V);
float fresnel = 0.02 + 0.98 * pow(1.0 - dot(-V, H), 5.0);
float waterBrdf = fresnel * pow(max(dot(H, N), 0.0), 150.0);
groundColor += reflectance.w * max(waterBrdf, 0.0) * sunLight;
}
result = attenuation * groundColor; //=R[L0]+R[L*]
}
return result;
}
return result;
}
// Direct sun light for ray x+tv, when sun in direction s (=L0)
float3 GetSunColor(AtmosphericFogData atmosphericFog, float3 X, float T, float3 V, float3 S, float radius, float Mu)
float3 GetSunColor(AtmosphericFogData atmosphericFog, float3 X, float T, float3 V, float3 S, float radius, float Mu)
{
if (T > 0.0)
{
return float3(0.0f, 0.0f, 0.0f);
}
else
{
float3 transmittance = radius <= RadiusAtmosphere ? TransmittanceWithShadow(radius, Mu) : float3(1.0, 1.0, 1.0); // T(X,xo)
float sunIntensity = step(cos(PI * atmosphericFog.AtmosphericFogSunDiscScale / 180.0), dot(V, S)); // Lsun
return transmittance * sunIntensity; // Eq (9)
}
float3 transmittance = radius <= RadiusAtmosphere ? TransmittanceWithShadow(radius, Mu) : float3(1.0, 1.0, 1.0); // T(X,xo)
float sunIntensity = step(cos(PI * atmosphericFog.AtmosphericFogSunDiscScale / 180.0), dot(V, S)); // Lsun
return transmittance * sunIntensity; // Eq (9)
}
float3 inscatter(inout float3 x, inout float t, float3 v, float3 s, out float r, out float mu, out float3 attenuation)
{
float3 result = 0;
r = length(x);
mu = dot(x, v) / r;
float d = -r * mu - sqrt(r * r * (mu * mu - 1.0) + RadiusAtmosphere * RadiusAtmosphere);
float3 result = 0;
r = length(x);
mu = dot(x, v) / r;
float d = -r * mu - sqrt(r * r * (mu * mu - 1.0) + RadiusAtmosphere * RadiusAtmosphere);
// if x in space and ray intersects atmosphere
if (d > 0.0)
if (d > 0.0)
{
// move x to nearest intersection of ray with top atmosphere boundary
x += d * v;
t -= d;
mu = (r * mu + d) / RadiusAtmosphere;
r = RadiusAtmosphere;
}
// move x to nearest intersection of ray with top atmosphere boundary
x += d * v;
t -= d;
mu = (r * mu + d) / RadiusAtmosphere;
r = RadiusAtmosphere;
}
float epsilon = 0.0045f;
// if ray intersects atmosphere
if (r <= RadiusAtmosphere)
if (r <= RadiusAtmosphere)
{
float nu = dot(v, s);
float muS = dot(x, s) / r;
float phaseR = PhaseFunctionR(nu);
float phaseM = PhaseFunctionM(nu);
float4 inscatter = max(Texture4DSample(AtmosphereInscatterTexture, r, mu, muS, nu), 0.0);
float nu = dot(v, s);
float muS = dot(x, s) / r;
float phaseR = PhaseFunctionR(nu);
float phaseM = PhaseFunctionM(nu);
float4 inscatter = max(Texture4DSample(AtmosphereInscatterTexture, r, mu, muS, nu), 0.0);
if (t > 0.0)
if (t > 0.0)
{
float3 x0 = x + t * v;
float r0 = length(x0);
float rMu0 = dot(x0, v);
float mu0 = rMu0 / r0;
float muS0 = dot(x0, s) / r0;
#ifdef ATMOSPHERIC_TEXTURE_SAMPLE_FIX
// avoids imprecision problems in transmittance computations based on textures
float3 x0 = x + t * v;
float r0 = length(x0);
float rMu0 = dot(x0, v);
float mu0 = rMu0 / r0;
float muS0 = dot(x0, s) / r0;
attenuation = AnalyticTransmittance(r, mu, t);
#else
attenuation = TransmittanceWithDistance(r, mu, v, x0);
#endif
if (r0 > RadiusGround + 0.01)
if (r0 > RadiusGround + 0.01)
{
// computes S[L]-T(x,x0)S[L]|x0
inscatter = max(inscatter - attenuation.rgbr * Texture4DSample(AtmosphereInscatterTexture, r0, mu0, muS0, nu), 0.0);
#ifdef ATMOSPHERIC_TEXTURE_SAMPLE_FIX
// avoids imprecision problems near horizon by interpolating between two points above and below horizon
const float EPS = 0.004;
// computes S[L]-T(x,x0)S[L]|x0
inscatter = max(inscatter - attenuation.rgbr * Texture4DSample(AtmosphereInscatterTexture, r0, mu0, muS0, nu), 0.0);
float muHoriz = -sqrt(1.0 - (RadiusGround / r) * (RadiusGround / r));
if (abs(mu - muHoriz) < EPS)
if (abs(mu - muHoriz) < epsilon)
{
float a = ((mu - muHoriz) + EPS) / (2.0 * EPS);
mu = muHoriz - EPS;
mu = muHoriz - epsilon;
r0 = sqrt(r * r + t * t + 2.0 * r * t * mu);
mu0 = (r * mu + t) / r0;
float4 inScatter0 = Texture4DSample(AtmosphereInscatterTexture, r, mu, muS, nu);
float4 inScatter1 = Texture4DSample(AtmosphereInscatterTexture, r0, mu0, muS0, nu);
float4 inScatterA = max(inScatter0 - attenuation.rgbr * inScatter1, 0.0);
mu = muHoriz + EPS;
mu = muHoriz + epsilon;
r0 = sqrt(r * r + t * t + 2.0 * r * t * mu);
mu0 = (r * mu + t) / r0;
inScatter0 = Texture4DSample(AtmosphereInscatterTexture, r, mu, muS, nu);
inScatter1 = Texture4DSample(AtmosphereInscatterTexture, r0, mu0, muS0, nu);
float4 inScatterB = max(inScatter0 - attenuation.rgbr * inScatter1, 0.0);
inscatter = lerp(inScatterA, inScatterB, a);
float alpha = ((mu - muHoriz) + epsilon) / (2.0 * epsilon);
inscatter = lerp(inScatterA, inScatterB, alpha);
}
#endif
}
}
#ifdef ATMOSPHERIC_TEXTURE_SAMPLE_FIX
// avoids imprecision problems in Mie scattering when sun is below horizon
}
}
inscatter.w *= smoothstep(0.00, 0.02, muS);
#endif
result = max(inscatter.rgb * phaseR + GetMie(inscatter) * phaseM, 0.0);
}
result = max(inscatter.rgb * phaseR + GetMie(inscatter) * phaseM, 0.0);
}
return result;
return result;
}
/*
//ground radiance at end of ray x+tv, when sun in direction s
//attenuated bewteen ground and viewer (=R[L0]+R[L*])
float3 groundColor(float3 x, float t, float3 v, float3 s, float r, float mu, float3 attenuation)
{
float3 result;
if (t > 0.0)
{
// if ray hits ground surface
// ground reflectance at end of ray, x0
float3 x0 = x + t * v;
float r0 = length(x0);
float3 n = x0 / r0;
float4 SceneColor = 0;
SceneColor.xyz = saturate(SceneColor.xyz + 0.05);
float4 Reflectance = SceneColor * float4(0.2, 0.2, 0.2, 1.0);
if (r0 > RadiusGround + 0.01)
{
reflectance = float4(0.4, 0.4, 0.4, 0.0);
}
// direct sun light (radiance) reaching x0
float muS = dot(n, s);
float3 sunLight = transmittanceWithShadow(r0, muS);
// precomputed sky light (irradiance) (=E[L*]) at x0
float3 groundSkyLight = Irradiance(AtmosphereIrradianceTexture, r0, muS);
// light reflected at x0 (=(R[L0]+R[L*])/T(x,x0))
float3 groundColor = reflectance.rgb * (max(muS, 0.0) * sunLight + groundSkyLight) * ISun / M_PI;
// water specular color due to sunLight
if (reflectance.w > 0.0)
{
float3 h = normalize(s - v);
float fresnel = 0.02 + 0.98 * pow(1.0 - dot(-v, h), 5.0);
float waterBrdf = fresnel * pow(max(dot(h, n), 0.0), 150.0);
groundColor += reflectance.w * max(waterBrdf, 0.0) * sunLight * ISun;
}
result = attenuation * groundColor; //=R[L0]+R[L*]
} else { // ray looking at the sky
result = 0.0;
}
return result;
}
*/
static const float EPSILON_ATMOSPHERE = 0.002f;
static const float EPSILON_INSCATTER = 0.004f;
@@ -359,19 +279,19 @@ static const float EPSILON_INSCATTER = 0.004f;
// output - maxPathLength: distance traversed within atmosphere
// output - return value: intersection occurred true/false
bool intersectAtmosphere(in float3 viewPosition, in float3 d, out float offset, out float maxPathLength)
{
{
offset = 0.0f;
maxPathLength = 0.0f;
// vector from ray origin to center of the sphere
float3 l = -viewPosition;
float l2 = dot(l,l);
float s = dot(l,d);
float l2 = dot(l, l);
float s = dot(l, d);
// adjust top atmosphere boundary by small epsilon to prevent artifacts
float r = Rt - EPSILON_ATMOSPHERE;
float r2 = r*r;
if(l2 <= r2)
float r2 = r * r;
if (l2 <= r2)
{
// ray origin inside sphere, hit is ensured
float m2 = l2 - (s * s);
@@ -379,11 +299,11 @@ bool intersectAtmosphere(in float3 viewPosition, in float3 d, out float offset,
maxPathLength = s + q;
return true;
}
else if(s >= 0)
else if (s >= 0)
{
// ray starts outside in front of sphere, hit is possible
float m2 = l2 - (s * s);
if(m2 <= r2)
if (m2 <= r2)
{
// ray hits atmosphere definitely
float q = sqrt(r2 - m2);
@@ -392,7 +312,7 @@ bool intersectAtmosphere(in float3 viewPosition, in float3 d, out float offset,
return true;
}
}
return false;
}
@@ -409,7 +329,7 @@ float3 GetInscatteredLight(AtmosphericFogData atmosphericFog, in float3 viewPosi
if (intersectAtmosphere(viewPosition, viewDir, offset, maxPathLength))
{
return float3(offset / 10,0,0);
return float3(offset / 10, 0, 0);
float pathLength = distance(viewPosition, surfacePos);
//return pathLength.xxx;
@@ -418,10 +338,11 @@ float3 GetInscatteredLight(AtmosphericFogData atmosphericFog, in float3 viewPosi
if (pathLength > offset)
{
//return float3(1,0,0);
// offsetting camera
float3 startPos = viewPosition + offset * viewDir;
float startPosHeight = length(startPos); pathLength -= offset;
float startPosHeight = length(startPos);
pathLength -= offset;
// starting position of path is now ensured to be inside atmosphere
// was either originally there or has been moved to top boundary
@@ -508,9 +429,9 @@ float4 GetAtmosphericFog(AtmosphericFogData atmosphericFog, float viewFar, float
viewPosition.y = (viewPosition.y - atmosphericFog.AtmosphericFogGroundOffset) * atmosphericFog.AtmosphericFogAltitudeScale;
//viewPosition *= atmosphericFog.AtmosphericFogDistanceScale;
//viewPosition *= scale;
//viewPosition *= scale;
//if(length(worldPosition) > Rg)
// return float4(0, 0,0 ,0);
@@ -531,27 +452,27 @@ float4 GetAtmosphericFog(AtmosphericFogData atmosphericFog, float viewFar, float
return float4(0, 0, 0, 1);
#endif
#if 1
// TODO: scale viewPosition from cm to km !!!!!!!
// TODO: scale viewPosition from cm to km
float scale = 0.0001f * atmosphericFog.AtmosphericFogDistanceScale;
viewPosition.y = (viewPosition.y - atmosphericFog.AtmosphericFogGroundOffset) * atmosphericFog.AtmosphericFogAltitudeScale;
//viewPosition *= atmosphericFog.AtmosphericFogDistanceScale;
viewPosition *= scale;
//viewPosition.xz *= 0.00001f;
//viewPosition *= scale;
viewPosition.y += RadiusGround + HeightOffset;
//viewPosition.y -= atmosphericFog.AtmosphericFogGroundOffset;
//worldPosition
float Radius = length(viewPosition);
float3 V = normalize(viewVector);
float Mu = dot(viewPosition, V) / Radius;
float T = -Radius * Mu - sqrt(Radius * Radius * (Mu * Mu - 1.0) + RadiusGround * RadiusGround);
//-Radius * Mu > sqrt(Radius * Radius * (Mu * Mu - 1.0) + RadiusGround * RadiusGround)
/*
float3 g = viewPosition - float3(0.0, 0.0, RadiusGround + 10.0);

View File

@@ -3,6 +3,23 @@
#include "./Flax/Common.hlsl"
#include "./Flax/Atmosphere.hlsl"
// Provides functions for atmospheric scattering and aerial perspective.
//
// Explanations:
// Scale Height = the altitude (height above ground) at which the average
// atmospheric density is found.
// Optical Depth = also called optical length, airmass, etc.
//
// References:
// [GPUGems2] GPU Gems 2: Accurate Atmospheric Scattering by Sean O'Neil.
// [GPUPro3] An Approximation to the Chapman Grazing-Incidence Function for
// Atmospheric Scattering, GPU Pro3, pp. 105.
// Papers bei Bruneton, Nishita, etc.
//
// This code contains embedded portions of free sample source code from
// http://www-evasion.imag.fr/Membres/Eric.Bruneton/PrecomputedAtmosphericScattering2.zip, Author: Eric Bruneton,
// 08/16/2011, Copyright (c) 2008 INRIA, All Rights Reserved, which have been altered from their original version.
const static int TransmittanceIntegralSamples = 500;
const static int InscatterIntegralSamples = 50;
const static int IrradianceIntegralSamples = 32;
@@ -14,11 +31,11 @@ const static float InscatterDeltaPhi = PI / float(InscatterSphericalIntegralSamp
const static float InscatterDeltaTheta = PI / float(InscatterSphericalIntegralSamples);
META_CB_BEGIN(0, Data)
float FirstOrder;
float First;
float AtmosphereR;
int AtmosphereLayer;
float Dummy0;
float4 DhdH;
float4 dhdh;
META_CB_END
Texture2D AtmosphereDeltaETexture : register(t3);
@@ -26,117 +43,72 @@ Texture3D AtmosphereDeltaSRTexture : register(t4);
Texture3D AtmosphereDeltaSMTexture : register(t5);
Texture3D AtmosphereDeltaJTexture : register(t6);
struct AtmosphereGSOutput
float GetOpticalDepth(float h, float radius, float mu)
{
float4 Position : SV_Position;
float2 TexCoord : TEXCOORD0;
//uint LayerIndex : SV_RenderTargetArrayIndex;
};
/*
META_VS(true, FEATURE_LEVEL_ES2)
META_VS_IN_ELEMENT(POSITION, 0, R32G32_FLOAT, 0, ALIGN, PER_VERTEX, 0, true)
META_VS_IN_ELEMENT(TEXCOORD, 0, R32G32_FLOAT, 0, ALIGN, PER_VERTEX, 0, true)
Quad_VS2PS VS(float2 Position : POSITION0, float2 TexCoord : TEXCOORD0)
{
Quad_VS2PS output;
output.Position = float4(Position, 0, 1);
output.TexCoord = TexCoord;
return output;
}
META_GS(true, FEATURE_LEVEL_SM4)
[maxvertexcount(3)]
void GS_Atmosphere(triangle Quad_VS2PS input[3], inout TriangleStream<AtmosphereGSOutput> output)
{
AtmosphereGSOutput vertex;
for(int i = 0; i < 3; i++)
{
vertex.Position = input[i].Position;
vertex.TexCoord = input[i].TexCoord;
vertex.LayerIndex = AtmosphereLayer;
output.Append(vertex);
}
}
*/
float OpticalDepth(float H, float radius, float Mu)
{
float result = 0.0;
float Dx = Limit(radius, Mu) / float(TransmittanceIntegralSamples);
float Xi = 0.0;
float Yi = exp(-(radius - RadiusGround) / H);
float result = 0.0f;
float ti = Limit(radius, mu) / float(TransmittanceIntegralSamples);
float xi = 0.0f;
float yi = exp(-(radius - RadiusGround) / h);
LOOP
for (int i = 1; i <= TransmittanceIntegralSamples; i++)
{
float Xj = float(i) * Dx;
float Yj = exp(-(sqrt(radius * radius + Xj * Xj + 2.0 * Xj * radius * Mu) - RadiusGround) / H);
result += (Yi + Yj) / 2.0 * Dx;
Xi = Xj;
Yi = Yj;
float xj = float(i) * ti;
float yj = exp(-(sqrt(radius * radius + xj * xj + 2.0f * xj * radius * mu) - RadiusGround) / h);
result += (yi + yj) * 0.5f * ti;
xi = xj;
yi = yj;
}
return Mu < -sqrt(1.0 - (RadiusGround / radius) * (RadiusGround / radius)) ? 1e9 : result;
return mu < -sqrt(1.0f - (RadiusGround / radius) * (RadiusGround / radius)) ? 1e9 : result;
}
META_PS(true, FEATURE_LEVEL_ES2)
float4 PS_Transmittance(Quad_VS2PS input) : SV_Target0
{
float radius, MuS;
GetTransmittanceRMuS(input.TexCoord, radius, MuS);
float3 depth = BetaRayleighScattering * OpticalDepth(HeightScaleRayleigh, radius, MuS) + BetaMieExtinction * OpticalDepth(HeightScaleMie, radius, MuS);
return float4(exp(-depth), 0.0f); // Eq (5)
float radius, mus;
GetTransmittanceRMuS(input.TexCoord, radius, mus);
float3 depth = BetaRayleighScattering * GetOpticalDepth(HeightScaleRayleigh, radius, mus) + BetaMieExtinction * GetOpticalDepth(HeightScaleMie, radius, mus);
return float4(exp(-depth), 0.0f);
}
META_PS(true, FEATURE_LEVEL_ES2)
float4 PS_Irradiance1(Quad_VS2PS input) : SV_Target0
{
float radius, MuS;
GetIrradianceRMuS(input.TexCoord, radius, MuS);
return float4(Transmittance(radius, MuS) * max(MuS, 0.0), 0.0);
float radius, mus;
GetIrradianceRMuS(input.TexCoord, radius, mus);
return float4(Transmittance(radius, mus) * max(mus, 0.0f), 0.0f);
}
META_PS(true, FEATURE_LEVEL_ES2)
float4 PS_IrradianceN(Quad_VS2PS input) : SV_Target0
{
float radius, MuS;
GetIrradianceRMuS(input.TexCoord, radius, MuS);
float3 S = float3(sqrt(max(1.0 - MuS * MuS, 0.0)), 0.0, MuS);
float3 result = float3(0.0f, 0.0f, 0.0f);
// Integral over 2.PI around x with two nested loops over W directions (theta, phi) -- Eq (15)
float radius, mus;
GetIrradianceRMuS(input.TexCoord, radius, mus);
float3 s = float3(sqrt(max(1.0f - mus * mus, 0.0f)), 0.0f, mus);
float3 result = float3(0, 0, 0);
for (int iPhi = 0; iPhi < 4 * IrradianceIntegralSamplesHalf; iPhi++)
{
float phi = (float(iPhi) + 0.5) * IrradianceDeltaPhi;
float phi = (float(iPhi) + 0.5f) * IrradianceDeltaPhi;
for (int iTheta = 0; iTheta < IrradianceIntegralSamplesHalf; iTheta++)
{
float theta = (float(iTheta) + 0.5) * IrradianceDeltaTheta;
float Dw = IrradianceDeltaTheta * IrradianceDeltaPhi * sin(theta);
float3 W = float3(cos(phi) * sin(theta), sin(phi) * sin(theta), cos(theta));
float Nu = dot(S, W);
float theta = (float(iTheta) + 0.5f) * IrradianceDeltaTheta;
float dw = IrradianceDeltaTheta * IrradianceDeltaPhi * sin(theta);
float3 w = float3(cos(phi) * sin(theta), sin(phi) * sin(theta), cos(theta));
float nu = dot(s, w);
if (FirstOrder == 1.0)
if (First == 1.0f)
{
// First iteration is special because Rayleigh and Mie were stored separately,
// without the phase functions factors; they must be reintroduced here
float Pr1 = PhaseFunctionR(Nu);
float Pm1 = PhaseFunctionM(Nu);
float3 Ray1 = Texture4DSample(AtmosphereDeltaSRTexture, radius, W.z, MuS, Nu).rgb;
float3 Mie1 = Texture4DSample(AtmosphereDeltaSMTexture, radius, W.z, MuS, Nu).rgb;
result += (Ray1 * Pr1 + Mie1 * Pm1) * W.z * Dw;
float pr1 = PhaseFunctionR(nu);
float pm1 = PhaseFunctionM(nu);
float3 ray1 = Texture4DSample(AtmosphereDeltaSRTexture, radius, w.z, mus, nu).xyz;
float3 mie1 = Texture4DSample(AtmosphereDeltaSMTexture, radius, w.z, mus, nu).xyz;
result += (ray1 * pr1 + mie1 * pm1) * w.z * dw;
}
else
{
result += Texture4DSample(AtmosphereDeltaSRTexture, radius, W.z, MuS, Nu).rgb * W.z * Dw;
result += Texture4DSample(AtmosphereDeltaSRTexture, radius, w.z, mus, nu).xyz * w.z * dw;
}
}
}
return float4(result, 0.0);
}
@@ -146,219 +118,185 @@ float4 PS_CopyIrradiance1(Quad_VS2PS input) : SV_Target0
return AtmosphereDeltaETexture.Sample(SamplerLinearClamp, input.TexCoord);
}
void Integrand(float radius, float Mu, float MuS, float Nu, float T, out float3 Ray, out float3 Mie)
void Integrand(float radius, float mu, float mus, float nu, float t, out float3 ray, out float3 mie)
{
Ray = float3(0, 0, 0);
Mie = float3(0, 0, 0);
float Ri = sqrt(radius * radius + T * T + 2.0 * radius * Mu * T);
float MuSi = (Nu * T + MuS * radius) / Ri;
Ri = max(RadiusGround, Ri);
if (MuSi >= -sqrt(1.0 - RadiusGround * RadiusGround / (Ri * Ri)) )
ray = float3(0, 0, 0);
mie = float3(0, 0, 0);
float ri = sqrt(radius * radius + t * t + 2.0f * radius * mu * t);
float musi = (nu * t + mus * radius) / ri;
ri = max(RadiusGround, ri);
if (musi >= -sqrt(1.0 - RadiusGround * RadiusGround / (ri * ri)) )
{
float3 Ti = TransmittanceWithDistance(radius, Mu, T) * Transmittance(Ri, MuSi);
Ray = exp(-(Ri - RadiusGround) / HeightScaleRayleigh) * Ti;
Mie = exp(-(Ri - RadiusGround) / HeightScaleMie) * Ti;
float3 ti = TransmittanceWithDistance(radius, mu, t) * Transmittance(ri, musi);
ray = exp(-(ri - RadiusGround) / HeightScaleRayleigh) * ti;
mie = exp(-(ri - RadiusGround) / HeightScaleMie) * ti;
}
}
// For Inscatter 1
void Inscatter(float radius, float Mu, float MuS, float Nu, out float3 Ray, out float3 Mie)
void Inscatter(float radius, float mu, float mus, float nu, out float3 ray, out float3 mie)
{
Ray = float3(0, 0, 0);
Mie = float3(0, 0, 0);
float Dx = Limit(radius, Mu) / float(InscatterIntegralSamples);
float Xi = 0.0;
float3 Rayi;
float3 Miei;
Integrand(radius, Mu, MuS, Nu, 0.0, Rayi, Miei);
ray = float3(0, 0, 0);
mie = float3(0, 0, 0);
float dx = Limit(radius, mu) / float(InscatterIntegralSamples);
float xi = 0.0f;
float3 rayi;
float3 miei;
Integrand(radius, mu, mus, nu, 0.0f, rayi, miei);
for (int i = 1; i <= InscatterIntegralSamples; i++)
{
float Xj = float(i) * Dx;
float xj = float(i) * dx;
float3 Rayj;
float3 Miej;
Integrand(radius, Mu, MuS, Nu, Xj, Rayj, Miej);
Ray += (Rayi + Rayj) / 2.0 * Dx;
Mie += (Miei + Miej) / 2.0 * Dx;
Xi = Xj;
Rayi = Rayj;
Miei = Miej;
Integrand(radius, mu, mus, nu, xj, Rayj, Miej);
ray += (rayi + Rayj) * 0.5f * dx;
mie += (miei + Miej) * 0.5f * dx;
xi = xj;
rayi = Rayj;
miei = Miej;
}
Ray *= BetaRayleighScattering;
Mie *= BetaMieScattering;
}
struct Inscatter1Output
{
float4 DeltaSR : SV_Target0;
float4 DeltaSM : SV_Target1;
};
META_PS(true, FEATURE_LEVEL_ES2)
float4 PS_Inscatter1_A(AtmosphereGSOutput input) : SV_Target
{
float3 Ray;
float3 Mie;
float Mu, MuS, Nu;
GetMuMuSNu(input.TexCoord, AtmosphereR, DhdH, Mu, MuS, Nu);
Inscatter(AtmosphereR, Mu, MuS, Nu, Ray, Mie);
// Store separately Rayleigh and Mie contributions, WITHOUT the phase function factor (cf "Angular precision")
return float4(Ray, 1);
ray *= BetaRayleighScattering;
mie *= BetaMieScattering;
}
META_PS(true, FEATURE_LEVEL_ES2)
float4 PS_CopyInscatter1(AtmosphereGSOutput input) : SV_Target0
float4 PS_Inscatter1_A(Quad_VS2PS input) : SV_Target
{
float3 UVW = float3(input.TexCoord, (float(AtmosphereLayer) + 0.5f) / float(AtmosphericFogInscatterAltitudeSampleNum));
float4 Ray = AtmosphereDeltaSRTexture.Sample(SamplerLinearClamp, UVW);
float4 Mie = AtmosphereDeltaSRTexture.Sample(SamplerLinearClamp, UVW);
return float4(Ray.rgb, Mie.r);
float3 ray;
float3 mie;
float mu, mus, nu;
GetMuMuSNu(input.TexCoord, AtmosphereR, dhdh, mu, mus, nu);
Inscatter(AtmosphereR, mu, mus, nu, ray, mie);
return float4(ray, 1);
}
// For Inscatter S
void Inscatter(float Radius, float Mu, float MuS, float Nu, out float3 RayMie)
META_PS(true, FEATURE_LEVEL_ES2)
float4 PS_CopyInscatter1(Quad_VS2PS input) : SV_Target0
{
Radius = clamp(Radius, RadiusGround, RadiusAtmosphere);
Mu = clamp(Mu, -1.0, 1.0);
MuS = clamp(MuS, -1.0, 1.0);
float Variation = sqrt(1.0 - Mu * Mu) * sqrt(1.0 - MuS * MuS);
Nu = clamp(Nu, MuS * Mu - Variation, MuS * Mu + Variation);
float3 uvw = float3(input.TexCoord, (float(AtmosphereLayer) + 0.5f) / float(AtmosphericFogInscatterAltitudeSampleNum));
float4 ray = AtmosphereDeltaSRTexture.Sample(SamplerLinearClamp, uvw);
float4 mie = AtmosphereDeltaSRTexture.Sample(SamplerLinearClamp, uvw);
return float4(ray.xyz, mie.x);
}
float cThetaMin = -sqrt(1.0 - (RadiusGround / Radius) * (RadiusGround / Radius));
void Inscatter(float radius, float mu, float mus, float nu, out float3 rayMie)
{
radius = clamp(radius, RadiusGround, RadiusAtmosphere);
mu = clamp(mu, -1.0f, 1.0f);
mus = clamp(mus, -1.0f, 1.0f);
float variation = sqrt(1.0f - mu * mu) * sqrt(1.0f - mus * mus);
nu = clamp(nu, mus * mu - variation, mus * mu + variation);
float cThetaMin = -sqrt(1.0f - (RadiusGround / radius) * (RadiusGround / radius));
float3 v = float3(sqrt(1.0f - mu * mu), 0.0f, mu);
float sx = v.x == 0.0f ? 0.0f : (nu - mus * mu) / v.x;
float3 s = float3(sx, sqrt(max(0.0f, 1.0f - sx * sx - mus * mus)), mus);
rayMie = float3(0, 0, 0);
float3 V = float3(sqrt(1.0 - Mu * Mu), 0.0, Mu);
float Sx = V.x == 0.0 ? 0.0 : (Nu - MuS * Mu) / V.x;
float3 S = float3(Sx, sqrt(max(0.0, 1.0 - Sx * Sx - MuS * MuS)), MuS);
RayMie = float3(0.f, 0.f, 0.f);
// Integral over 4.PI around x with two nested loops over W directions (theta, phi) - Eq (7)
for (int iTheta = 0; iTheta < InscatterSphericalIntegralSamples; iTheta++)
{
float theta = (float(iTheta) + 0.5) * InscatterDeltaTheta;
float theta = (float(iTheta) + 0.5f) * InscatterDeltaTheta;
float cTheta = cos(theta);
float GReflectance = 0.0;
float DGround = 0.0;
float3 GTransmittance = float3(0.f, 0.f, 0.f);
float ground = 0.0f;
float3 transmittance = float3(0, 0, 0);
float reflectance = 0.0f;
if (cTheta < cThetaMin)
{
// If ground visible in direction W, Compute transparency GTransmittance between x and ground
GReflectance = AverageGroundRelectance / PI;
DGround = -Radius * cTheta - sqrt(Radius * Radius * (cTheta * cTheta - 1.0) + RadiusGround * RadiusGround);
GTransmittance = TransmittanceWithDistance(RadiusGround, -(Radius * cTheta + DGround) / RadiusGround, DGround);
{
ground = -radius * cTheta - sqrt(radius * radius * (cTheta * cTheta - 1.0f) + RadiusGround * RadiusGround);
transmittance = TransmittanceWithDistance(RadiusGround, -(radius * cTheta + ground) / RadiusGround, ground);
reflectance = AverageGroundRelectance / PI;
}
for (int iPhi = 0; iPhi < 2 * InscatterSphericalIntegralSamples; iPhi++)
{
float phi = (float(iPhi) + 0.5) * InscatterDeltaPhi;
float Dw = InscatterDeltaTheta * InscatterDeltaPhi * sin(theta);
float3 W = float3(cos(phi) * sin(theta), sin(phi) * sin(theta), cTheta);
float dw = InscatterDeltaTheta * InscatterDeltaPhi * sin(theta);
float3 w = float3(cos(phi) * sin(theta), sin(phi) * sin(theta), cTheta);
float nu1 = dot(s, w);
float nu2 = dot(v, w);
float pr2 = PhaseFunctionR(nu2);
float pm2 = PhaseFunctionM(nu2);
float3 normal = (float3(0.0f, 0.0f, radius) + ground * w) / RadiusGround;
float3 irradiance = Irradiance(AtmosphereDeltaETexture, RadiusGround, dot(normal, s));
float3 rayMie1 = reflectance * irradiance * transmittance;
float Nu1 = dot(S, W);
float Nu2 = dot(V, W);
float Pr2 = PhaseFunctionR(Nu2);
float Pm2 = PhaseFunctionM(Nu2);
// Compute irradiance received at ground in direction W (if ground visible) =deltaE
float3 GNormal = (float3(0.0, 0.0, Radius) + DGround * W) / RadiusGround;
float3 GIrradiance = Irradiance(AtmosphereDeltaETexture, RadiusGround, dot(GNormal, S));
float3 RayMie1; // light arriving at x from direction W
// First term = light reflected from the ground and attenuated before reaching x, =T.alpha/PI.deltaE
RayMie1 = GReflectance * GIrradiance * GTransmittance;
// Second term = inscattered light, =deltaS
if (FirstOrder == 1.0)
if (First == 1.0f)
{
// First iteration is special because Rayleigh and Mie were stored separately,
// without the phase functions factors; they must be reintroduced here
float Pr1 = PhaseFunctionR(Nu1);
float Pm1 = PhaseFunctionM(Nu1);
float3 Ray1 = Texture4DSample(AtmosphereDeltaSRTexture, Radius, W.z, MuS, Nu1).rgb;
float3 Mie1 = Texture4DSample(AtmosphereDeltaSMTexture, Radius, W.z, MuS, Nu1).rgb;
RayMie1 += Ray1 * Pr1 + Mie1 * Pm1;
}
else
float pr1 = PhaseFunctionR(nu1);
float pm1 = PhaseFunctionM(nu1);
float3 ray1 = Texture4DSample(AtmosphereDeltaSRTexture, radius, w.z, mus, nu1).xyz;
float3 mie1 = Texture4DSample(AtmosphereDeltaSMTexture, radius, w.z, mus, nu1).xyz;
rayMie1 += ray1 * pr1 + mie1 * pm1;
}
else
{
RayMie1 += Texture4DSample(AtmosphereDeltaSRTexture, Radius, W.z, MuS, Nu1).rgb;
rayMie1 += Texture4DSample(AtmosphereDeltaSRTexture, radius, w.z, mus, nu1).xyz;
}
// Light coming from direction W and scattered in direction V
// = light arriving at x from direction W (RayMie1) * SUM(scattering coefficient * phaseFunction) - Eq (7)
RayMie += RayMie1 * (BetaRayleighScattering * exp(-(Radius - RadiusGround) / HeightScaleRayleigh) * Pr2 + BetaMieScattering * exp(-(Radius - RadiusGround) / HeightScaleMie) * Pm2) * Dw;
rayMie += rayMie1 * (BetaRayleighScattering * exp(-(radius - RadiusGround) / HeightScaleRayleigh) * pr2 + BetaMieScattering * exp(-(radius - RadiusGround) / HeightScaleMie) * pm2) * dw;
}
}
// output RayMie = J[T.alpha/PI.deltaE + deltaS] (line 7 in algorithm 4.1)
}
META_PS(true, FEATURE_LEVEL_ES2)
float4 PS_InscatterS(AtmosphereGSOutput input) : SV_Target0
float4 PS_InscatterS(Quad_VS2PS input) : SV_Target0
{
float3 RayMie;
float Mu, MuS, Nu;
GetMuMuSNu(input.TexCoord, AtmosphereR, DhdH, Mu, MuS, Nu);
Inscatter(AtmosphereR, Mu, MuS, Nu, RayMie);
return float4(RayMie, 0);
float3 rayMie;
float mu, mus, nu;
GetMuMuSNu(input.TexCoord, AtmosphereR, dhdh, mu, mus, nu);
Inscatter(AtmosphereR, mu, mus, nu, rayMie);
return float4(rayMie, 0);
}
float3 Integrand(float Radius, float Mu, float MuS, float Nu, float T)
float3 Integrand(float radius, float mu, float mus, float nu, float t)
{
float Ri = sqrt(Radius * Radius + T * T + 2.0 * Radius * Mu * T);
float Mui = (Radius * Mu + T) / Ri;
float MuSi = (Nu * T + MuS * Radius) / Ri;
return Texture4DSample(AtmosphereDeltaJTexture, Ri, Mui, MuSi, Nu).rgb * TransmittanceWithDistance(Radius, Mu, T);
float ri = sqrt(radius * radius + t * t + 2.0 * radius * mu * t);
float mui = (radius * mu + t) / ri;
float musi = (nu * t + mus * radius) / ri;
return Texture4DSample(AtmosphereDeltaJTexture, ri, mui, musi, nu).xyz * TransmittanceWithDistance(radius, mu, t);
}
// InscatterN
float3 Inscatter(float Radius, float Mu, float MuS, float Nu)
float3 Inscatter(float radius, float mu, float mus, float nu)
{
float3 RayMie = float3(0.f, 0.f, 0.f);
float Dx = Limit(Radius, Mu) / float(InscatterIntegralSamples);
float Xi = 0.0;
float3 RayMiei = Integrand(Radius, Mu, MuS, Nu, 0.0);
float3 rayMie = float3(0, 0, 0);
float dx = Limit(radius, mu) / float(InscatterIntegralSamples);
float xi = 0.0f;
float3 raymiei = Integrand(radius, mu, mus, nu, 0.0f);
for (int i = 1; i <= InscatterIntegralSamples; i++)
{
float Xj = float(i) * Dx;
float3 RayMiej = Integrand(Radius, Mu, MuS, Nu, Xj);
RayMie += (RayMiei + RayMiej) / 2.0 * Dx;
Xi = Xj;
RayMiei = RayMiej;
float xj = float(i) * dx;
float3 RayMiej = Integrand(radius, mu, mus, nu, xj);
rayMie += (raymiei + RayMiej) * 0.5f * dx;
xi = xj;
raymiei = RayMiej;
}
return RayMie;
return rayMie;
}
META_PS(true, FEATURE_LEVEL_ES2)
float4 PS_InscatterN(AtmosphereGSOutput input) : SV_Target0
float4 PS_InscatterN(Quad_VS2PS input) : SV_Target0
{
float Mu, MuS, Nu;
GetMuMuSNu(input.TexCoord, AtmosphereR, DhdH, Mu, MuS, Nu);
return float4(Inscatter(AtmosphereR, Mu, MuS, Nu), 0);
float mu, mus, nu;
GetMuMuSNu(input.TexCoord, AtmosphereR, dhdh, mu, mus, nu);
return float4(Inscatter(AtmosphereR, mu, mus, nu), 0);
}
META_PS(true, FEATURE_LEVEL_ES2)
float4 PS_CopyInscatterN(AtmosphereGSOutput input) : SV_Target0
float4 PS_CopyInscatterN(Quad_VS2PS input) : SV_Target0
{
float Mu, MuS, Nu;
GetMuMuSNu(input.TexCoord, AtmosphereR, DhdH, Mu, MuS, Nu);
float3 UVW = float3(input.TexCoord, (float(AtmosphereLayer) + 0.5f) / float(AtmosphericFogInscatterAltitudeSampleNum));
float4 Ray = AtmosphereDeltaSRTexture.Sample(SamplerLinearClamp, UVW) / PhaseFunctionR(Nu);
return float4(Ray.rgb, 0);
float mu, mus, nu;
GetMuMuSNu(input.TexCoord, AtmosphereR, dhdh, mu, mus, nu);
float3 uvw = float3(input.TexCoord, (float(AtmosphereLayer) + 0.5f) / float(AtmosphericFogInscatterAltitudeSampleNum));
float4 ray = AtmosphereDeltaSRTexture.Sample(SamplerLinearClamp, uvw) / PhaseFunctionR(nu);
return float4(ray.xyz, 0);
}
META_PS(true, FEATURE_LEVEL_ES2)
float4 PS_Inscatter1_B(AtmosphereGSOutput input) : SV_Target
float4 PS_Inscatter1_B(Quad_VS2PS input) : SV_Target
{
float3 Ray;
float3 Mie;
float Mu, MuS, Nu;
GetMuMuSNu(input.TexCoord, AtmosphereR, DhdH, Mu, MuS, Nu);
Inscatter(AtmosphereR, Mu, MuS, Nu, Ray, Mie);
// Store separately Rayleigh and Mie contributions, WITHOUT the phase function factor (cf "Angular precision")
return float4(Mie, 1);
float3 ray;
float3 mie;
float mu, mus, nu;
GetMuMuSNu(input.TexCoord, AtmosphereR, dhdh, mu, mus, nu);
Inscatter(AtmosphereR, mu, mus, nu, ray, mie);
return float4(mie, 1);
}

View File

@@ -5,61 +5,11 @@
#include "./Flax/Math.hlsl"
// Bidirectional reflectance distribution functions
// Physically based shading model:
// Microfacet specular = D*G*F / (4*NoL*NoV) = D*Vis*F
// Vis = G / (4*NoL*NoV)
float3 Diffuse_Lambert(float3 diffuseColor)
{
return diffuseColor * (1 / PI);
}
// [Burley 2012, "Physically-Based Shading at Disney"]
float3 Diffuse_Burley(float3 diffuseColor, float roughness, float NoV, float NoL, float VoH)
{
float FD90 = 0.5 + 2 * VoH * VoH * roughness;
float FdV = 1 + (FD90 - 1) * Pow5(1 - NoV);
float FdL = 1 + (FD90 - 1) * Pow5(1 - NoL);
return diffuseColor * ((1 / PI) * FdV * FdL);
}
// [Gotanda 2012, "Beyond a Simple Physically Based Blinn-Phong Model in Real-Time"]
float3 Diffuse_OrenNayar(float3 diffuseColor, float roughness, float NoV, float NoL, float VoH)
{
float a = roughness * roughness;
float s = a;
float s2 = s * s;
float VoL = 2 * VoH * VoH - 1;
float Cosri = VoL - NoV * NoL;
float C1 = 1 - 0.5 * s2 / (s2 + 0.33);
float C2 = 0.45 * s2 / (s2 + 0.09) * Cosri * (Cosri >= 0 ? rcp( max(NoL, NoV)) : 1);
return diffuseColor / PI * (C1 + C2) * (1 + roughness * 0.5);
}
float PhongShadingPow(float x, float y)
{
return ClampedPow(x, y);
}
// [Blinn 1977, "Models of light reflection for computer synthesized pictures"]
float D_Blinn(float roughness, float NoH)
{
float a = roughness * roughness;
float a2 = a * a;
float n = 2 / a2 - 2;
return (n + 2) / (2 * PI) * PhongShadingPow(NoH, n);
}
// [Beckmann 1963, "The scattering of electromagnetic waves from rough surfaces"]
float D_Beckmann(float roughness, float NoH)
{
float a = roughness * roughness;
float a2 = a * a;
float NoH2 = NoH * NoH;
return exp((NoH2 - 1) / (a2 * NoH2)) / (PI * a2 * NoH2 * NoH2);
}
// GGX / Trowbridge-Reitz
// [Walter et al. 2007, "Microfacet models for refraction through rough surfaces"]
float D_GGX(float roughness, float NoH)
@@ -70,36 +20,6 @@ float D_GGX(float roughness, float NoH)
return a2 / (PI * d * d);
}
// Anisotropic GGX
// [Burley 2012, "Physically-Based Shading at Disney"]
float D_GGXaniso(float roughnessX, float roughnessY, float NoH, float3 H, float3 X, float3 Y)
{
float ax = roughnessX * roughnessX;
float ay = roughnessY * roughnessY;
float XoH = dot(X, H);
float YoH = dot(Y, H);
float d = XoH * XoH / (ax * ax) + YoH * YoH / (ay * ay) + NoH * NoH;
return 1 / (PI * ax * ay * d * d);
}
float Vis_Implicit()
{
return 0.25;
}
// [Neumann et al. 1999, "Compact metallic reflectance models"]
float Vis_Neumann(float NoV, float NoL)
{
return 1 / (4 * max(NoL, NoV));
}
// [Kelemen 2001, "A microfacet based coupled specular-matte brdf model with importance sampling"]
float Vis_Kelemen(float VoH)
{
// constant to prevent NaN
return rcp(4 * VoH * VoH + 1e-5);
}
// Tuned to match behavior of Vis_Smith
// [Schlick 1994, "An Inexpensive BRDF Model for Physically-Based Rendering"]
float Vis_Schlick(float roughness, float NoV, float NoL)
@@ -116,8 +36,8 @@ float Vis_Smith(float roughness, float NoV, float NoL)
{
float a = Square(roughness);
float a2 = a * a;
float vis_SmithV = NoV + sqrt( NoV * (NoV - NoV * a2) + a2);
float vis_SmithL = NoL + sqrt( NoL * (NoL - NoL * a2) + a2);
float vis_SmithV = NoV + sqrt(NoV * (NoV - NoV * a2) + a2);
float vis_SmithL = NoL + sqrt(NoL * (NoL - NoL * a2) + a2);
return rcp(vis_SmithV * vis_SmithL);
}
@@ -126,14 +46,9 @@ float Vis_Smith(float roughness, float NoV, float NoL)
float Vis_SmithJointApprox(float roughness, float NoV, float NoL)
{
float a = Square(roughness);
float vis_SmithV = NoL * (NoV * (1 - a) + a);
float vis_SmithL = NoV * (NoL * (1 - a) + a);
return 0.5 * rcp(vis_SmithV + vis_SmithL);
}
float3 F_None(float3 specularColor)
{
return specularColor;
float visSmithV = NoL * (NoV * (1 - a) + a);
float visSmithL = NoV * (NoL * (1 - a) + a);
return 0.5 * rcp(visSmithV + visSmithL);
}
// [Schlick 1994, "An Inexpensive BRDF Model for Physically-Based Rendering"]
@@ -143,86 +58,43 @@ float3 F_Schlick(float3 specularColor, float VoH)
return saturate(50.0 * specularColor.g) * fc + (1 - fc) * specularColor;
}
float3 F_Fresnel(float3 specularColor, float VoH)
{
float3 specularColorSqrt = sqrt(clamp(float3(0, 0, 0), float3(0.99, 0.99, 0.99), specularColor));
float3 n = (1 + specularColorSqrt ) / (1 - specularColorSqrt);
float3 g = sqrt(n * n + VoH * VoH - 1);
return 0.5 * Square((g - VoH) / (g + VoH)) * (1 + Square(((g + VoH) * VoH - 1) / ((g - VoH) * VoH + 1)));
}
float D_InvBlinn(float roughness, float NoH)
{
float m = roughness * roughness;
float m2 = m * m;
float A = 4;
float cos2h = NoH * NoH;
return rcp( PI * (1 + A * m2)) * (1 + A * exp(-cos2h / m2));
}
float D_InvBeckmann(float roughness, float NoH)
{
float m = roughness * roughness;
float m2 = m * m;
float A = 4;
float cos2h = NoH * NoH;
float sin2h = 1 - cos2h;
float sin4h = sin2h * sin2h;
return rcp(PI * (1 + A * m2) * sin4h) * (sin4h + A * exp(-cos2h / (m2 * sin2h)));
}
float D_InvGGX(float roughness, float NoH)
{
float a = roughness * roughness;
float a2 = a * a;
float A = 4;
float d = (NoH - a2 * NoH) * NoH + a2;
return rcp(PI * (1 + A * a2)) * (1 + 4 * a2 * a2 / (d * d));
}
float Vis_Cloth(float NoV, float NoL)
{
return rcp(4 * (NoL + NoV - NoL * NoV));
}
#define REFLECTION_CAPTURE_NUM_MIPS 7
#define REFLECTION_CAPTURE_ROUGHEST_MIP 1
#define REFLECTION_CAPTURE_ROUGHNESS_MIP_SCALE 1.2
half ProbeMipFromRoughness(half roughness)
{
half levelFrom1x1 = REFLECTION_CAPTURE_ROUGHEST_MIP - REFLECTION_CAPTURE_ROUGHNESS_MIP_SCALE * log2(roughness);
return REFLECTION_CAPTURE_NUM_MIPS - 1 - levelFrom1x1;
half mip1px = REFLECTION_CAPTURE_ROUGHEST_MIP - REFLECTION_CAPTURE_ROUGHNESS_MIP_SCALE * log2(roughness);
return REFLECTION_CAPTURE_NUM_MIPS - 1 - mip1px;
}
half SSRMipFromRoughness(half roughness)
{
half levelFrom1x1 = 4 - REFLECTION_CAPTURE_ROUGHNESS_MIP_SCALE * log2(roughness);
return max(1, 10 - levelFrom1x1);
half mip1px = 4 - REFLECTION_CAPTURE_ROUGHNESS_MIP_SCALE * log2(roughness);
return max(1, 10 - mip1px);
}
float ComputeReflectionCaptureRoughnessFromMip(float mip)
{
float levelFrom1x1 = REFLECTION_CAPTURE_NUM_MIPS - 1 - mip;
return exp2((REFLECTION_CAPTURE_ROUGHEST_MIP - levelFrom1x1) / REFLECTION_CAPTURE_ROUGHNESS_MIP_SCALE);
float mip1px = REFLECTION_CAPTURE_NUM_MIPS - 1 - mip;
return exp2((REFLECTION_CAPTURE_ROUGHEST_MIP - mip1px) / REFLECTION_CAPTURE_ROUGHNESS_MIP_SCALE);
}
// [Lazarov 2013, "Getting More Physical in Call of Duty: Black Ops II"]
float3 EnvBRDFApprox(float3 specularColor, float roughness, float NoV)
{
// Approximate version, base for pre integrated version
const half4 c0 = {-1, -0.0275, -0.572, 0.022};
const half4 c1 = {1, 0.0425, 1.04, -0.04};
const half4 c0 = { -1, -0.0275, -0.572, 0.022 };
const half4 c1 = { 1, 0.0425, 1.04, -0.04 };
half4 r = roughness * c0 + c1;
half a004 = min(r.x * r.x, exp2(-9.28 * NoV)) * r.x + r.y;
half2 ab = half2(-1.04, 1.04) * a004 + r.zw;
return specularColor * ab.x + saturate(50.0 * specularColor.g) * ab.y;
}
// Pre integrated environment GF
// Importance sampled preintegrated G * F
float3 EnvBRDF(Texture2D preIntegratedGF, float3 specularColor, float roughness, float NoV)
{
// Importance sampled preintegrated G * F
float2 ab = preIntegratedGF.SampleLevel(SamplerLinearClamp, float2(NoV, roughness), 0).rg;
return specularColor * ab.x + saturate(50.0 * specularColor.g) * ab.y;
}
@@ -231,29 +103,7 @@ float3 EnvBRDF(Texture2D preIntegratedGF, float3 specularColor, float roughness,
float RoughnessToSpecularPower(float roughness)
{
// TODO: use path tracer as a reference and calculate valid params for this conversion
return pow(2, 13 * (1 - roughness));
#if 0
float coeff = pow(4, roughness);
coeff = max(coeff, 2.0f / (MAX_SPECULAR_POWER + 2.0f));
return 2.0f / coeff - 2.0f;
//const float Log2Of1OnLn2_Plus1 = 1.52876637294; // log2(1 / ln(2)) + 1
//return exp2(10 * roughness + Log2Of1OnLn2_Plus1);
//return pow(2, 2 * (1 - roughness));
return pow(2, 13 * (1 - roughness));
//return exp2(10 * roughness + 1);
#endif
}
/*
float SpecularPowerToRoughness(float specularPower)
{
return pow(-0.25f, specularPower * 0.5f + 1.0f);
}
*/
#endif

View File

@@ -18,67 +18,4 @@ bool RayHitRect(float3 r, float3 rectCenter, float3 rectX, float3 rectY, float3
return inExtentX && inExtentY;
}
// Computes where a ray hits a sphere (which is centered at the origin).
// \param[in] rayOrigin The start position of the ray.
// \param[in] rayDirection The normalized direction of the ray.
// \param[in] radius The radius of the sphere.
// \param[out] enter The ray parameter where the ray enters the sphere.
// 0 if the ray is already in the sphere.
// \param[out] exit The ray parameter where the ray exits the sphere.
// \return 0 or a positive value if the ray hits the sphere. A negative value
// if the ray does not touch the sphere.
float HitSphere(float3 rayOrigin, float3 rayDirection, float radius, out float enter, out float exit)
{
// Solve the equation: ||rayOrigin + distance * rayDirection|| = r
//
// This is a straight-forward quadratic equation:
// ||O + d * D|| = r
// => (O + d * D)2 = r2 where V2 means V.V
// => d2 * D2 + 2 * d * (O.D) + O2 - r2 = 0
// D2 is 1 because the rayDirection is normalized.
// => d = -O.D + sqrt((O.D)2 - O2 + r2)
float OD = dot(rayOrigin, rayDirection);
float OO = dot(rayOrigin, rayOrigin);
float radicand = OD * OD - OO + radius * radius;
enter = max(0, -OD - sqrt(radicand));
exit = -OD + sqrt(radicand);
// If radicand is negative then we do not have a result - no hit.
return radicand;
}
// Clips a ray to an AABB. Does not handle rays parallel to any of the planes.
//
// @param rayOrigin - The origin of the ray in world space.
// @param rayEnd - The end of the ray in world space.
// @param boxMin - The minimum extrema of the box.
// @param boxMax - The maximum extrema of the box.
// @return - Returns the closest intersection along the ray in x, and furthest in y.
// If the ray did not intersect the box, then the furthest intersection <= the closest intersection.
// The intersections will always be in the range [0,1], which corresponds to [rayOrigin, rayEnd] in worldspace.
// To find the world space position of either intersection, simply plug it back into the ray equation:
// WorldPos = RayOrigin + (rayEnd - RayOrigin) * Intersection;
float2 LineBoxIntersect(float3 rayOrigin, float3 rayEnd, float3 boxMin, float3 boxMax)
{
float3 invRayDir = 1.0f / (rayEnd - rayOrigin);
//find the ray intersection with each of the 3 planes defined by the minimum extrema.
float3 planeIntersections1 = (boxMin - rayOrigin) * invRayDir;
//find the ray intersection with each of the 3 planes defined by the maximum extrema.
float3 planeIntersections2 = (boxMax - rayOrigin) * invRayDir;
//get the closest of these intersections along the ray
float3 closestPlaneIntersections = min(planeIntersections1, planeIntersections2);
//get the furthest of these intersections along the ray
float3 furthestPlaneIntersections = max(planeIntersections1, planeIntersections2);
float2 boxIntersections;
//find the furthest near intersection
boxIntersections.x = max(closestPlaneIntersections.x, max(closestPlaneIntersections.y, closestPlaneIntersections.z));
//find the closest far intersection
boxIntersections.y = min(furthestPlaneIntersections.x, min(furthestPlaneIntersections.y, furthestPlaneIntersections.z));
//clamp the intersections to be between rayOrigin and RayEnd on the ray
return saturate(boxIntersections);
}
#endif

View File

@@ -45,31 +45,20 @@ Texture2D LutTexture : register(t0);
// [Krystek 1985, "An algorithm to calculate correlated colour temperature"]
float2 PlanckianLocusChromaticity(float temp)
{
float u = ( 0.860117757f + 1.54118254e-4f * temp + 1.28641212e-7f * temp*temp ) / ( 1.0f + 8.42420235e-4f * temp + 7.08145163e-7f * temp*temp );
float v = ( 0.317398726f + 4.22806245e-5f * temp + 4.20481691e-8f * temp*temp ) / ( 1.0f - 2.89741816e-5f * temp + 1.61456053e-7f * temp*temp );
float x = 3*u / ( 2*u - 8*v + 4 );
float y = 2*v / ( 2*u - 8*v + 4 );
return float2(x,y);
float u = (0.860117757f + 1.54118254e-4f * temp + 1.28641212e-7f * temp * temp) / (1.0f + 8.42420235e-4f * temp + 7.08145163e-7f * temp * temp);
float v = (0.317398726f + 4.22806245e-5f * temp + 4.20481691e-8f * temp * temp) / (1.0f - 2.89741816e-5f * temp + 1.61456053e-7f * temp * temp);
float x = 3 * u / (2 * u - 8 * v + 4);
float y = 2 * v / (2 * u - 8 * v + 4);
return float2(x, y);
}
// Accurate for 4000K < temp < 25000K
// in: correlated color temperature
// out: CIE 1931 chromaticity
float2 D_IlluminantChromaticity(float temp)
// Calculates chromaticity from temperature
float2 IlluminantChromaticity(float temp)
{
// Correct for revision of Plank's law
// This makes 6500 == D65
temp *= 1.4388 / 1.438;
float x = temp <= 7000 ?
0.244063 + ( 0.09911e3 + ( 2.9678e6 - 4.6070e9 / temp ) / temp ) / temp :
0.237040 + ( 0.24748e3 + ( 1.9018e6 - 2.0064e9 / temp ) / temp ) / temp;
float y = -3 * x*x + 2.87 * x - 0.275;
return float2(x,y);
float x = temp <= 7000 ? 0.244063 + (0.09911e3 + (2.9678e6 - 4.6070e9 / temp) / temp) / temp : 0.237040 + (0.24748e3 + (1.9018e6 - 2.0064e9 / temp) / temp) / temp;
float y = -3 * x * x + 2.87 * x - 0.275;
return float2(x, y);
}
// Find closest color temperature to chromaticity
@@ -77,38 +66,31 @@ float2 D_IlluminantChromaticity(float temp)
float CorrelatedColortemperature(float x, float y)
{
float n = (x - 0.3320) / (0.1858 - y);
return -449 * n*n*n + 3525 * n*n - 6823.3 * n + 5520.33;
return -449 * n * n * n + 3525 * n * n - 6823.3 * n + 5520.33;
}
// [McCamy 1992, "Correlated color temperature as an explicit function of chromaticity coordinates"]
float2 PlanckianIsothermal(float temp, float tint)
{
float u = ( 0.860117757f + 1.54118254e-4f * temp + 1.28641212e-7f * temp*temp ) / ( 1.0f + 8.42420235e-4f * temp + 7.08145163e-7f * temp*temp );
float v = ( 0.317398726f + 4.22806245e-5f * temp + 4.20481691e-8f * temp*temp ) / ( 1.0f - 2.89741816e-5f * temp + 1.61456053e-7f * temp*temp );
float ud = ( -1.13758118e9f - 1.91615621e6f * temp - 1.53177f * temp*temp ) / Square( 1.41213984e6f + 1189.62f * temp + temp*temp );
float vd = ( 1.97471536e9f - 705674.0f * temp - 308.607f * temp*temp ) / Square( 6.19363586e6f - 179.456f * temp + temp*temp );
float2 uvd = normalize( float2( u, v ) );
// Correlated color temperature is meaningful within +/- 0.05
float u = (0.860117757f + 1.54118254e-4f * temp + 1.28641212e-7f * temp * temp) / (1.0f + 8.42420235e-4f * temp + 7.08145163e-7f * temp * temp);
float v = (0.317398726f + 4.22806245e-5f * temp + 4.20481691e-8f * temp * temp) / (1.0f - 2.89741816e-5f * temp + 1.61456053e-7f * temp * temp);
float ud = (-1.13758118e9f - 1.91615621e6f * temp - 1.53177f * temp * temp) / Square(1.41213984e6f + 1189.62f * temp + temp * temp);
float vd = (1.97471536e9f - 705674.0f * temp - 308.607f * temp * temp) / Square(6.19363586e6f - 179.456f * temp + temp * temp);
float2 uvd = normalize(float2(u, v));
u += -uvd.y * tint * 0.05;
v += uvd.x * tint * 0.05;
float x = 3*u / ( 2*u - 8*v + 4 );
float y = 2*v / ( 2*u - 8*v + 4 );
float x = 3 * u / (2 * u - 8 * v + 4);
float y = 2 * v / (2 * u - 8 * v + 4);
return float2(x,y);
}
float3 WhiteBalance(float3 linearColor)
{
float2 srcWhiteDaylight = D_IlluminantChromaticity(WhiteTemp);
float2 srcWhiteDaylight = IlluminantChromaticity(WhiteTemp);
float2 srcWhitePlankian = PlanckianLocusChromaticity(WhiteTemp);
float2 srcWhite = WhiteTemp < 4000 ? srcWhitePlankian : srcWhiteDaylight;
float2 d65White = float2(0.31270, 0.32900);
// Offset along isotherm
float2 d65White = float2(0.31270f, 0.32900f);
float2 isothermal = PlanckianIsothermal(WhiteTemp, WhiteTint) - srcWhitePlankian;
srcWhite += isothermal;
@@ -121,8 +103,8 @@ float3 WhiteBalance(float3 linearColor)
float3 ColorCorrect(float3 color, float luma, float4 saturation, float4 contrast, float4 gamma, float4 gain, float4 offset)
{
color = max(0, lerp(luma.xxx, color, saturation.xyz * saturation.w));
color = pow(color * (1.0 / 0.18), contrast.xyz * contrast.w) * 0.18;
color = pow(color, 1.0 / (gamma.xyz * gamma.w));
color = pow(color * (1.0f / 0.18f), contrast.xyz * contrast.w) * 0.18f;
color = pow(color, 1.0f / (gamma.xyz * gamma.w));
color = color * (gain.xyz * gain.w) + (offset.xyz + offset.w);
return color;
}
@@ -130,35 +112,12 @@ float3 ColorCorrect(float3 color, float luma, float4 saturation, float4 contrast
float3 ColorCorrectAll(float3 color)
{
float luma = dot(color, AP1_RGB2Y);
// Shadow CC
float3 ccColorShadows = ColorCorrect(color, luma,
ColorSaturationShadows,
ColorContrastShadows,
ColorGammaShadows,
ColorGainShadows,
ColorOffsetShadows);
float3 ccColorShadows = ColorCorrect(color, luma, ColorSaturationShadows, ColorContrastShadows, ColorGammaShadows, ColorGainShadows, ColorOffsetShadows);
float ccWeightShadows = 1 - smoothstep(0, ColorCorrectionShadowsMax, luma);
// Highlight CC
float3 ccColorHighlights = ColorCorrect(color, luma,
ColorSaturationHighlights,
ColorContrastHighlights,
ColorGammaHighlights,
ColorGainHighlights,
ColorOffsetHighlights);
float3 ccColorHighlights = ColorCorrect(color, luma,ColorSaturationHighlights, ColorContrastHighlights, ColorGammaHighlights, ColorGainHighlights, ColorOffsetHighlights);
float ccWeightHighlights = smoothstep(ColorCorrectionHighlightsMin, 1, luma);
// Midtone CC
float3 ccColorMidtones = ColorCorrect(color, luma,
ColorSaturationMidtones,
ColorContrastMidtones,
ColorGammaMidtones,
ColorGainMidtones,
ColorOffsetMidtones);
float3 ccColorMidtones = ColorCorrect(color, luma, ColorSaturationMidtones, ColorContrastMidtones, ColorGammaMidtones, ColorGainMidtones, ColorOffsetMidtones);
float ccWeightMidtones = 1 - ccWeightShadows - ccWeightHighlights;
// Blend shadow, midtone and highlight CCs
return ccColorShadows * ccWeightShadows + ccColorMidtones * ccWeightMidtones + ccColorHighlights * ccWeightHighlights;
}
@@ -207,8 +166,7 @@ float3 TonemapNeutral(float3 linearColor)
float3 TonemapACES(float3 linearColor)
{
// The code in this file was originally written by Stephen Hill (@self_shadow), who deserves all
// credit for coming up with this fit and implementing it. Buy him a beer next time you see him. :)
// The code was originally written by Stephen Hill (@self_shadow).
// sRGB => XYZ => D65_2_D60 => AP1 => RRT_SAT
static const float3x3 ACESInputMat =
@@ -280,7 +238,6 @@ float4 CombineLUTs(float2 uv, uint layerIndex)
// Move from encoded LUT color space to linear color
//float3 linearColor = encodedColor.rgb; // Default
//float3 linearColor = LogCToLinear(encodedColor.rgb); // LogC
float3 linearColor = LogToLinear(encodedColor.rgb) - LogToLinear(0); // Log
// Apply white balance

View File

@@ -196,25 +196,6 @@ struct Quad_GS2PS
uint LayerIndex : SV_RenderTargetArrayIndex;
};
float VisualizeTextureGrid(float2 texCoord, float2 textureSize, float gridSize)
{
if (gridSize > 0)
{
float2 t = frac(texCoord * textureSize);
return (t.x < gridSize || t.x > 1 - gridSize) || (t.y < gridSize || t.y > 1 - gridSize);
}
else
{
int2 t = int2(texCoord * textureSize);
return (t.x % 2 == 0 && t.y % 2 == 0) || (t.x % 2 == 1 && t.y % 2 == 1);
}
}
float BiasedNDotL(float NoL)
{
return saturate(NoL * 1.08f - 0.08f);
}
float Luminance(float3 color)
{
return dot(color, float3(0.299f, 0.587f, 0.114f));

View File

@@ -38,22 +38,14 @@ float4 CalculateCombinedFog(float3 worldPosition, float sceneDepth, float3 volum
float excludeDistance = 0;
#if VOLUMETRIC_FOG
// Volumetric fog covers up to MaxDistance along ViewZ, exclude analytical fog from this range
excludeDistance = max(ExponentialHeightFog.VolumetricFogMaxDistance - 100, 0);
#endif
float4 fog = GetExponentialHeightFog(ExponentialHeightFog, worldPosition, GBuffer.ViewPos, excludeDistance);
#if VOLUMETRIC_FOG
// Sample volumetric fog lookup table
float4 volumetricFog = IntegratedLightScattering.SampleLevel(SamplerLinearClamp, volumeUV, 0);
// Mix volumetric and analytical fog
fog = float4(volumetricFog.rgb + fog.rgb * volumetricFog.a, volumetricFog.a * fog.a);
#endif
return fog;

View File

@@ -9,73 +9,50 @@
// http://gpuopen.com/optimized-reversible-tonemapper-for-resolve/
float3 FastTonemap(float3 c)
{
return c * rcp(max(max(c.r, c.g), c.b) + 1.0);
return c * rcp(max(max(c.r, c.g), c.b) + 1.0);
}
float4 FastTonemap(float4 c)
{
return float4(FastTonemap(c.rgb), c.a);
return float4(FastTonemap(c.rgb), c.a);
}
float3 FastTonemap(float3 c, float w)
{
return c * (w * rcp(max(max(c.r, c.g), c.b) + 1.0));
return c * (w * rcp(max(max(c.r, c.g), c.b) + 1.0));
}
float4 FastTonemap(float4 c, float w)
{
return float4(FastTonemap(c.rgb, w), c.a);
return float4(FastTonemap(c.rgb, w), c.a);
}
float3 FastTonemapInvert(float3 c)
{
return c * rcp(1.0 - max(max(c.r, c.g), c.b));
return c * rcp(1.0 - max(max(c.r, c.g), c.b));
}
float4 FastTonemapInvert(float4 c)
{
return float4(FastTonemapInvert(c.rgb), c.a);
return float4(FastTonemapInvert(c.rgb), c.a);
}
half3 LinearTo709Branchless(half3 linearColor)
{
linearColor = max(6.10352e-5, linearColor);
return min(linearColor * 4.5, pow(max(linearColor, 0.018), 0.45) * 1.099 - 0.099);
}
half3 LinearToSrgbBranchless(half3 linearColor)
{
linearColor = max(6.10352e-5, linearColor);
return min(linearColor * 12.92, pow(max(linearColor, 0.00313067), 1.0/2.4) * 1.055 - 0.055);
}
half LinearToSrgbBranchingChannel(half linearColor)
half LinearToSrgbChannel(half linearColor)
{
if (linearColor < 0.00313067)
return linearColor * 12.92;
return pow(linearColor, (1.0/2.4)) * 1.055 - 0.055;
return pow(linearColor, (1.0 / 2.4)) * 1.055 - 0.055;
}
half3 LinearToSrgbBranching(half3 linearColor)
half3 LinearToSrgb(half3 linearColor)
{
return half3(
LinearToSrgbBranchingChannel(linearColor.r),
LinearToSrgbBranchingChannel(linearColor.g),
LinearToSrgbBranchingChannel(linearColor.b));
LinearToSrgbChannel(linearColor.r),
LinearToSrgbChannel(linearColor.g),
LinearToSrgbChannel(linearColor.b));
}
half3 LinearToSrgb(half3 linearColor)
{
#if FEATURE_LEVEL >= FEATURE_LEVEL_SM4
// Branching is faster than branchless on PC
return LinearToSrgbBranching(linearColor);
#else
// Use branchless on mobile
return LinearToSrgbBranchless(linearColor);
#endif
}
half3 sRGBToLinear(half3 color)
half3 sRGBToLinear(half3 color)
{
color = max(6.10352e-5, color);
return color > 0.04045 ? pow(color * (1.0 / 1.055) + 0.0521327, 2.4) : color * (1.0 / 12.92);
@@ -103,7 +80,7 @@ float3 LinearToLog(float3 linearColor)
const float exposureGrey = 444;
// Using stripped down, 'pure log', formula. Parameterized by grey points and dynamic range covered.
float3 logColor = log2(linearColor) / linearRange - log2(linearGrey) / linearRange + exposureGrey / 1023.0; // scalar: 3log2 3mad
float3 logColor = log2(linearColor) / linearRange - log2(linearGrey) / linearRange + exposureGrey / 1023.0; // scalar: 3log2 3mad
//float3 logColor = (log2(linearColor) - log2(linearGrey)) / linearRange + exposureGrey / 1023.0;
//float3 logColor = log2(linearColor / linearGrey) / linearRange + exposureGrey / 1023.0;
//float3 logColor = (0.432699 * log10(0.5 * linearColor + 0.037584) + 0.616596) + 0.03; // SLog
@@ -113,74 +90,4 @@ float3 LinearToLog(float3 linearColor)
return logColor;
}
// Alexa LogC converters (El 1000)
// See http://www.vocas.nl/webfm_send/964
// Max range is ~58.85666
struct ParamsLogC
{
float cut;
float a, b, c, d, e, f;
};
static const ParamsLogC LogC =
{
0.011361, // cut
5.555556, // a
0.047996, // b
0.244161, // c
0.386036, // d
5.301883, // e
0.092819 // f
};
float3 LinearToLogC(float3 linearColor)
{
return LogC.c * log10(LogC.a * linearColor + LogC.b) + LogC.d;
}
float3 LogCToLinear(float3 logcColor)
{
return (pow(10.0, (logcColor - LogC.d) / LogC.c) - LogC.b) / LogC.a;
}
// Dolby PQ transforms
float3 ST2084ToLinear(float3 pq)
{
const float m1 = 0.1593017578125; // = 2610. / 4096. * .25;
const float m2 = 78.84375; // = 2523. / 4096. * 128;
const float c1 = 0.8359375; // = 2392. / 4096. * 32 - 2413./4096.*32 + 1;
const float c2 = 18.8515625; // = 2413. / 4096. * 32;
const float c3 = 18.6875; // = 2392. / 4096. * 32;
const float C = 10000.;
float3 Np = pow(pq, 1.0 / m2);
float3 L = Np - c1;
L = max(0.0, L);
L = L / (c2 - c3 * Np);
L = pow(L, 1.0 / m1);
float3 P = L * C;
return P;
}
float3 LinearToST2084(float3 lin)
{
const float m1 = 0.1593017578125; // = 2610. / 4096. * .25;
const float m2 = 78.84375; // = 2523. / 4096. * 128;
const float c1 = 0.8359375; // = 2392. / 4096. * 32 - 2413./4096.*32 + 1;
const float c2 = 18.8515625; // = 2413. / 4096. * 32;
const float c3 = 18.6875; // = 2392. / 4096. * 32;
const float C = 10000.0;
float3 L = lin / C;
float3 Lm = pow(L, m1);
float3 N1 = (c1 + c2 * Lm);
float3 N2 = (1.0 + c3 * Lm);
float3 N = N1 * rcp(N2);
float3 P = pow(N, m2);
return P;
}
#endif

View File

@@ -60,18 +60,6 @@ void CS_GenerateHistogram(uint3 groupId : SV_GroupID, uint3 dispatchThreadId : S
if (dispatchThreadId.x < InputSize.x && dispatchThreadId.y < InputSize.y)
{
uint weight = 1u;
// Vignette weighting to add more focus on the center of the screen
#if 0
{
float2 uv = float2(dispatchThreadId.xy) / float2(InputSize.x, InputSize.y);
float2 d = abs(uv - (0.5).xx);
float factor = saturate(1.0 - dot(d, d));
factor *= factor;
weight = (uint)(64.0 * factor);
}
#endif
float3 color = Input[dispatchThreadId.xy].xyz;
float luminance = Luminance(color);
float logLuminance = ComputeHistogramPositionFromLuminance(luminance);

View File

@@ -3,19 +3,13 @@
#ifndef __IES_PROFILE__
#define __IES_PROFILE__
// Apply 1D IES light profile texture
// Calculate IES light profile from 1D texture
float ComputeLightProfileMultiplier(Texture2D tex, float3 worldPosition, float3 lightPosition, float3 lightDirection)
{
float3 negLightDirection = normalize(worldPosition - lightPosition);
// -1..1
float dotProd = dot(negLightDirection, lightDirection);
// -PI..PI (this distortion could be put into the texture but not without quality loss or more memory)
float angle = asin(dotProd);
// 0..1
float normAngle = angle / PI + 0.5f;
return tex.SampleLevel(SamplerLinearClamp, float2(normAngle, 0), 0).r;
float3 l = normalize(worldPosition - lightPosition);
float d = dot(lightPosition, lightDirection);
float angle = asin(d) / PI + 0.5f;
return tex.SampleLevel(SamplerLinearClamp, float2(angle, 0), 0).r;
}
#endif

View File

@@ -28,7 +28,6 @@ LightingData StandardShading(GBufferSample gBuffer, float energy, float3 L, floa
float NoH = saturate(dot(N, H));
float VoH = saturate(dot(V, H));
// Generalized microfacet specular
float D = D_GGX(gBuffer.Roughness, NoH) * energy;
float Vis = Vis_SmithJointApprox(gBuffer.Roughness, NoV, NoL);
float3 F = F_Schlick(specularColor, VoH);
@@ -113,7 +112,6 @@ float4 GetLighting(float3 viewPos, LightData lightData, GBufferSample gBuffer, f
{
float4 result = 0;
float3 V = normalize(viewPos - gBuffer.WorldPos);
float3 ToLight = lightData.Direction;
float3 N = gBuffer.Normal;
float3 L = lightData.Direction; // no need to normalize
float NoL = saturate(dot(N, L));
@@ -124,7 +122,7 @@ float4 GetLighting(float3 viewPos, LightData lightData, GBufferSample gBuffer, f
// Calculate attenuation
if (isRadial)
{
GetRadialLightAttenuation(lightData, isSpotLight, gBuffer.WorldPos, N, 1, ToLight, L, NoL, distanceAttenuation, lightRadiusMask, spotAttenuation);
GetRadialLightAttenuation(lightData, isSpotLight, gBuffer.WorldPos, N, 1, lightData.Direction, L, NoL, distanceAttenuation, lightRadiusMask, spotAttenuation);
}
float attenuation = distanceAttenuation * lightRadiusMask * spotAttenuation;
@@ -132,13 +130,13 @@ float4 GetLighting(float3 viewPos, LightData lightData, GBufferSample gBuffer, f
ShadowData shadow = GetShadow(lightData, gBuffer, shadowMask);
// Reduce shadow mapping artifacts
shadow.SurfaceShadow *= saturate(NoL * 6 - 0.2);
shadow.SurfaceShadow *= saturate(NoL * 6.0f - 0.2f);
BRANCH
if (shadow.SurfaceShadow + shadow.TransmissionShadow > 0)
{
gBuffer.Roughness = max(gBuffer.Roughness, lightData.MinRoughness);
float energy = AreaLightSpecular(lightData, gBuffer.Roughness, ToLight, L, V, N);
float energy = AreaLightSpecular(lightData, gBuffer.Roughness, lightData.Direction, L, V, N);
// Calculate direct lighting
LightingData lighting = SurfaceShading(gBuffer, energy, L, V, N);

View File

@@ -47,8 +47,8 @@ struct LightingData
// WorldLightVector is the vector from the position being shaded to the light, divided by the radius of the light.
float RadialAttenuation(float3 worldLightVector, half falloffExponent)
{
float normalizeDistanceSquared = dot(worldLightVector, worldLightVector);
return pow(1.0f - saturate(normalizeDistanceSquared), falloffExponent);
float t = dot(worldLightVector, worldLightVector);
return pow(1.0f - saturate(t), falloffExponent);
}
// Calculates attenuation for a spot light. Where L normalize vector to light.
@@ -127,30 +127,21 @@ float AreaLightSpecular(LightData lightData, float roughness, inout float3 toLig
BRANCH
if (lightData.SourceLength > 0)
{
// Energy conservation
float lineAngle = saturate(lightData.SourceLength * invDistToLight);
energy *= m / saturate(m + 0.5 * lineAngle);
// Closest point on line segment to ray
float3 l01 = lightData.Direction * lightData.SourceLength;
float3 l0 = toLight - 0.5 * l01;
float3 l1 = toLight + 0.5 * l01;
float a = Square(lightData.SourceLength);
float b = dot(r, l01);
float t = saturate(dot(l0, b * r - l01) / (a - b * b));
toLight = l0 + t * l01;
}
BRANCH
if (lightData.SourceRadius > 0)
{
// Energy conservation
float sphereAngle = saturate(lightData.SourceRadius * invDistToLight);
energy *= Square(m / saturate(m + 0.5 * sphereAngle));
// Closest point on sphere to ray
float3 closestPointOnRay = dot(toLight, r) * r;
float3 centerToRay = closestPointOnRay - toLight;
float3 closestPointOnSphere = toLight + centerToRay * saturate(lightData.SourceRadius * rsqrt(dot(centerToRay, centerToRay)));

View File

@@ -5,8 +5,8 @@
uint NextPow2(uint value)
{
uint mask = (1 << firstbithigh(value)) - 1;
return (value + mask) & ~mask;
uint mask = (1 << firstbithigh(value)) - 1;
return (value + mask) & ~mask;
}
float3 SafeNormalize(float3 v)
@@ -17,7 +17,6 @@ float3 SafeNormalize(float3 v)
float3 ExtractLargestComponent(float3 v)
{
float3 a = abs(v);
if (a.x > a.y)
{
if (a.x > a.z)
@@ -32,161 +31,160 @@ float3 ExtractLargestComponent(float3 v)
return float3(0, v.y > 0 ? 1 : -1, 0);
}
}
return float3(0, 0, v.z > 0 ? 1 : -1);
}
float Square(float x)
{
return x*x;
return x * x;
}
float2 Square(float2 x)
{
return x*x;
return x * x;
}
float3 Square(float3 x)
{
return x*x;
return x * x;
}
float4 Square(float4 x)
{
return x*x;
return x * x;
}
float Pow2(float x)
{
return x*x;
return x * x;
}
float2 Pow2(float2 x)
{
return x*x;
return x * x;
}
float3 Pow2(float3 x)
{
return x*x;
return x * x;
}
float4 Pow2(float4 x)
{
return x*x;
return x * x;
}
float Pow3(float x)
{
return x*x*x;
return x * x * x;
}
float2 Pow3(float2 x)
{
return x*x*x;
return x * x * x;
}
float3 Pow3(float3 x)
{
return x*x*x;
return x * x * x;
}
float4 Pow3(float4 x)
{
return x*x*x;
return x * x * x;
}
float Pow4(float x)
{
float xx = x*x;
float xx = x * x;
return xx * xx;
}
float2 Pow4(float2 x)
{
float2 xx = x*x;
float2 xx = x * x;
return xx * xx;
}
float3 Pow4(float3 x)
{
float3 xx = x*x;
float3 xx = x * x;
return xx * xx;
}
float4 Pow4(float4 x)
{
float4 xx = x*x;
float4 xx = x * x;
return xx * xx;
}
float Pow5(float x)
{
float xx = x*x;
float xx = x * x;
return xx * xx * x;
}
float2 Pow5(float2 x)
{
float2 xx = x*x;
float2 xx = x * x;
return xx * xx * x;
}
float3 Pow5(float3 x)
{
float3 xx = x*x;
float3 xx = x * x;
return xx * xx * x;
}
float4 Pow5(float4 x)
{
float4 xx = x*x;
float4 xx = x * x;
return xx * xx * x;
}
float Pow6(float x)
{
float xx = x*x;
float xx = x * x;
return xx * xx * xx;
}
float2 Pow6(float2 x)
{
float2 xx = x*x;
float2 xx = x * x;
return xx * xx * xx;
}
float3 Pow6(float3 x)
{
float3 xx = x*x;
float3 xx = x * x;
return xx * xx * xx;
}
float4 Pow6(float4 x)
{
float4 xx = x*x;
float4 xx = x * x;
return xx * xx * xx;
}
float ClampedPow(float x,float y)
float ClampedPow(float x, float y)
{
return pow(max(abs(x), 0.000001f),y);
return pow(max(abs(x), 0.000001f), y);
}
float2 ClampedPow(float2 x,float2 y)
float2 ClampedPow(float2 x, float2 y)
{
return pow(max(abs(x), float2(0.000001f, 0.000001f)), y);
}
float3 ClampedPow(float3 x,float3 y)
float3 ClampedPow(float3 x, float3 y)
{
return pow(max(abs(x), float3(0.000001f, 0.000001f, 0.000001f)), y);
}
}
float4 ClampedPow(float4 x,float4 y)
float4 ClampedPow(float4 x, float4 y)
{
return pow(max(abs(x), float4(0.000001f, 0.000001f, 0.000001f, 0.000001f)), y);
}
}
float4 FindQuatBetween(float3 from, float3 to)
{
@@ -208,8 +206,8 @@ float4 FindQuatBetween(float3 from, float3 to)
{
w = 0.f;
result = abs(from.x) > abs(from.y)
? float4(-from.z, 0.f, from.x, w)
: float4(0.f, -from.z, from.y, w);
? float4(-from.z, 0.f, from.x, w)
: float4(0.f, -from.z, from.y, w);
}
return normalize(result);
@@ -218,23 +216,13 @@ float4 FindQuatBetween(float3 from, float3 to)
// Rotates Position about the given axis by the given angle, in radians, and returns the offset to position
float3 RotateAboutAxis(float4 normalizedRotationAxisAndAngle, float3 positionOnAxis, float3 position)
{
// Project position onto the rotation axis and find the closest point on the axis to Position
float3 closestPointOnAxis = positionOnAxis + normalizedRotationAxisAndAngle.xyz * dot(normalizedRotationAxisAndAngle.xyz, position - positionOnAxis);
// Construct orthogonal axes in the plane of the rotation
float3 axisU = position - closestPointOnAxis;
float3 axisV = cross(normalizedRotationAxisAndAngle.xyz, axisU);
float cosAngle, sinAngle;
sincos(normalizedRotationAxisAndAngle.w, sinAngle, cosAngle);
// Rotate using the orthogonal axes
float3 rotation = axisU * cosAngle + axisV * sinAngle;
// Reconstruct the rotated world space position
float3 rotatedPosition = closestPointOnAxis + rotation;
// Convert from position to a position offset
return rotatedPosition - position;
float3 pointOnAxis = positionOnAxis + normalizedRotationAxisAndAngle.xyz * dot(normalizedRotationAxisAndAngle.xyz, position - positionOnAxis);
float3 axisU = position - pointOnAxis;
float3 axisV = cross(normalizedRotationAxisAndAngle.xyz, axisU);
float cosAngle, sinAngle;
sincos(normalizedRotationAxisAndAngle.w, sinAngle, cosAngle);
float3 rotation = axisU * cosAngle + axisV * sinAngle;
return pointOnAxis + rotation - position;
}
#endif

View File

@@ -133,10 +133,10 @@ float4 ImportanceSampleGGX(float2 e, float roughness)
// Multiple importance sampling power heuristic of two functions with a power of two.
// [Veach 1997, "Robust Monte Carlo Methods for Light Transport Simulation"]
float MISWeight(uint number, float pdf, uint otherNumber, float otherpdf)
float MISWeight(uint number, float PDF, uint otherNumber, float otherPDF)
{
float weight = number * pdf;
float otherWeight = otherNumber * otherpdf;
float weight = number * PDF;
float otherWeight = otherNumber * otherPDF;
return weight * weight / (weight * weight + otherWeight * otherWeight);
}

View File

@@ -43,16 +43,16 @@ Texture2D Input0 : register(t0);
Texture2D Input1 : register(t1);
Texture2D Input2 : register(t2);
// Converts a motion vector into RGBA color.
float4 VectorToColor(float2 mv)
// Calculates the color for the a motion vector debugging
float4 VectorToColor(float2 motionVector)
{
float phi = atan2(mv.x, mv.y);
float phi = atan2(motionVector.x, motionVector.y);
float hue = (phi / PI + 1) * 0.5;
float r = abs(hue * 6 - 3) - 1;
float g = 2 - abs(hue * 6 - 2);
float b = 2 - abs(hue * 6 - 4);
float a = length(mv);
float a = length(motionVector);
return saturate(float4(r, g, b, a));
}
@@ -61,18 +61,15 @@ float4 VectorToColor(float2 mv)
META_PS(true, FEATURE_LEVEL_ES2)
float4 PS_MotionVectorsDebug(Quad_VS2PS input) : SV_Target
{
float4 src = SAMPLE_RT(Input0, input.TexCoord);
float4 color = SAMPLE_RT(Input0, input.TexCoord);
float2 motionVector = SAMPLE_RT(Input1, input.TexCoord).rg * (DebugAmplitude * 5.0f);
float4 motionColor = VectorToColor(motionVector);
float2 mv = SAMPLE_RT(Input1, input.TexCoord).rg * (DebugAmplitude * 5.0f);
float4 mc = VectorToColor(mv);
float colorRation = saturate(2 - DebugBlend * 2);
float motionColorRatio = saturate(DebugBlend * 2);
color.rgb = lerp(color.rgb * colorRation, motionColor.rgb, motionColor.a * motionColorRatio);
float3 rgb = mc.rgb;
float src_ratio = saturate(2 - DebugBlend * 2);
float mc_ratio = saturate(DebugBlend * 2);
rgb = lerp(src.rgb * src_ratio, rgb, mc.a * mc_ratio);
return float4(rgb, src.a);
return color;
}
// Motion vector arrow data from VS to PS
@@ -88,60 +85,51 @@ ArrowVaryings VS_DebugArrow(uint VertexId : SV_VertexID)
{
// Screen aspect ratio
float aspect = GBuffer.ScreenSize.x * GBuffer.ScreenSize.w;
float inv_aspect = GBuffer.ScreenSize.y * GBuffer.ScreenSize.z;
float aspectInv = GBuffer.ScreenSize.y * GBuffer.ScreenSize.z;
// Vertex IDs
uint arrow_id = VertexId / 6;
uint point_id = VertexId - arrow_id * 6;
uint arrowId = VertexId / 6;
uint pointId = VertexId - arrowId * 6;
// Column/Row number of the arrow
uint row = arrow_id / DebugColumnCount;
uint col = arrow_id - row * DebugColumnCount;
// Column and row number of the arrow
uint row = arrowId / DebugColumnCount;
uint col = arrowId - row * DebugColumnCount;
// Texture coordinate of the reference point
// Get the motion vector
float2 uv = float2((col + 0.5) / DebugColumnCount, (row + 0.5) / DebugRowCount);
// Retrieve the motion vector
float2 mv = SAMPLE_RT(Input1, uv).rg * DebugAmplitude;
float2 motionVector = SAMPLE_RT(Input1, uv).rg * DebugAmplitude;
// Arrow color
float4 color = VectorToColor(mv);
float4 color = VectorToColor(motionVector);
// Arrow vertex position parameter (0 = origin, 1 = head)
float arrow_l = point_id > 0;
// Rotation matrix for the arrow head
float2 head_dir = normalize(mv * float2(aspect, 1));
float2x2 head_rot = float2x2(head_dir.y, head_dir.x, -head_dir.x, head_dir.y);
// Offset for arrow head vertices
float head_x = point_id == 3 ? -1 : (point_id == 5 ? 1 : 0);
head_x *= arrow_l * 0.3 * saturate(length(mv) * DebugRowCount);
float2 head_offs = float2(head_x, -abs(head_x));
head_offs = mul(head_rot, head_offs) * float2(inv_aspect, 1);
// Arrow transformation
float isEnd = pointId > 0;
float2 direction = normalize(motionVector * float2(aspect, 1));
float2x2 rotation = float2x2(direction.y, direction.x, -direction.x, direction.y);
float offsetStart = pointId == 3 ? -1 : (pointId == 5 ? 1 : 0);
offsetStart *= isEnd * 0.3f * saturate(length(motionVector) * DebugRowCount);
float2 offset = float2(offsetStart, -abs(offsetStart));
offset = mul(rotation, offset) * float2(aspectInv, 1);
// Vertex position in the clip space
float2 vp = mv * arrow_l + head_offs * 2 / DebugRowCount + uv * 2 - 1;
float2 pos = motionVector * isEnd + offset * 2 / DebugRowCount + uv * 2.0f - 1.0f;
// Convert to the screen coordinates
float2 scoord = (vp + 1) * 0.5 * GBuffer.ScreenSize.xy;
// Snap to a pixel-perfect position.
scoord = round(scoord);
float2 posSS = (pos + 1) * 0.5f * GBuffer.ScreenSize.xy;
posSS = round(posSS);
// Bring back to the clip space
vp = (scoord + 0.5) * GBuffer.ScreenSize.zw * 2 - 1;
vp.y *= -1;
pos = (posSS + 0.5f) * GBuffer.ScreenSize.zw * 2.0f - 1.0f;
pos.y *= -1;
// Color tweaks
color.rgb = lerp(color.rgb, 1, 0.5);
color.rgb = lerp(color.rgb, 1, 0.5f);
color.a = DebugBlend;
// Output
ArrowVaryings output;
output.Position = float4(vp, 0, 1);
output.ScreenUV = scoord;
output.Position = float4(pos, 0, 1);
output.ScreenUV = posSS;
output.Color = color;
return output;
}
@@ -150,8 +138,8 @@ META_PS(true, FEATURE_LEVEL_ES2)
float4 PS_DebugArrow(ArrowVaryings input) : SV_Target
{
// Pseudo anti-aliasing
float aa = length(frac(input.ScreenUV) - 0.5) / 0.707;
aa *= (aa * (aa * 0.305306011 + 0.682171111) + 0.012522878); // gamma
float aa = length(frac(input.ScreenUV) - 0.5f) / 0.707f;
aa *= (aa * (aa * 0.305306011f + 0.682171111f) + 0.012522878f);
return float4(input.Color.rgb, input.Color.a * aa);
}
@@ -190,10 +178,10 @@ float4 PS_VelocitySetup(Quad_VS2PS input) : SV_Target
float2 v = SAMPLE_RT(Input0, input.TexCoord).rg;
// Apply the exposure time and convert to the pixel space
v *= (VelocityScale * 0.5) * GBuffer.ScreenSize.xy;
v *= (VelocityScale * 0.5f) * GBuffer.ScreenSize.xy;
// Clamp the vector with the maximum blur radius
v /= max(1.0, length(v) * RcpMaxBlurRadius);
v /= max(1.0f, length(v) * RcpMaxBlurRadius);
// Sample the depth of the pixel
float depth = SAMPLE_RT(Input1, input.TexCoord).r;
@@ -201,7 +189,7 @@ float4 PS_VelocitySetup(Quad_VS2PS input) : SV_Target
depth = LinearizeZ(gBufferData, depth);
// Pack into 10/10/10/2 format
return float4((v * RcpMaxBlurRadius + 1.0) * 0.5, depth, 0.0);
return float4((v * RcpMaxBlurRadius + 1.0f) * 0.5f, depth, 0.0f);
}
float2 MaxV(float2 v1, float2 v2)
@@ -220,40 +208,40 @@ float4 PS_TileMax1(Quad_VS2PS input) : SV_Target
float2 v3 = SAMPLE_RT(Input0, input.TexCoord + d.xw).rg;
float2 v4 = SAMPLE_RT(Input0, input.TexCoord + d.zw).rg;
v1 = (v1 * 2.0 - 1.0) * MaxBlurRadius;
v2 = (v2 * 2.0 - 1.0) * MaxBlurRadius;
v3 = (v3 * 2.0 - 1.0) * MaxBlurRadius;
v4 = (v4 * 2.0 - 1.0) * MaxBlurRadius;
v1 = (v1 * 2.0f - 1.0f) * MaxBlurRadius;
v2 = (v2 * 2.0f - 1.0f) * MaxBlurRadius;
v3 = (v3 * 2.0f - 1.0f) * MaxBlurRadius;
v4 = (v4 * 2.0f - 1.0f) * MaxBlurRadius;
return float4(MaxV(MaxV(MaxV(v1, v2), v3), v4), 0.0, 0.0);
return float4(MaxV(MaxV(MaxV(v1, v2), v3), v4), 0.0f, 0.0f);
}
// Pixel Shader for TileMax filter (2 pixel width)
META_PS(true, FEATURE_LEVEL_ES2)
float4 PS_TileMax2(Quad_VS2PS input) : SV_Target
{
float4 d = TexelSize2.xyxy * float4(-0.5, -0.5, 0.5, 0.5);
float4 d = TexelSize2.xyxy * float4(-0.5f, -0.5f, 0.5f, 0.5f);
float2 v1 = SAMPLE_RT(Input0, input.TexCoord + d.xy).rg;
float2 v2 = SAMPLE_RT(Input0, input.TexCoord + d.zy).rg;
float2 v3 = SAMPLE_RT(Input0, input.TexCoord + d.xw).rg;
float2 v4 = SAMPLE_RT(Input0, input.TexCoord + d.zw).rg;
return float4(MaxV(MaxV(MaxV(v1, v2), v3), v4), 0.0, 0.0);
return float4(MaxV(MaxV(MaxV(v1, v2), v3), v4), 0.0f, 0.0f);
}
// Pixel Shader for TileMax filter (2 pixel width)
META_PS(true, FEATURE_LEVEL_ES2)
float4 PS_TileMax4(Quad_VS2PS input) : SV_Target
{
float4 d = TexelSize4.xyxy * float4(-0.5, -0.5, 0.5, 0.5);
float4 d = TexelSize4.xyxy * float4(-0.5f, -0.5f, 0.5f, 0.5f);
float2 v1 = SAMPLE_RT(Input0, input.TexCoord + d.xy).rg;
float2 v2 = SAMPLE_RT(Input0, input.TexCoord + d.zy).rg;
float2 v3 = SAMPLE_RT(Input0, input.TexCoord + d.xw).rg;
float2 v4 = SAMPLE_RT(Input0, input.TexCoord + d.zw).rg;
return float4(MaxV(MaxV(MaxV(v1, v2), v3), v4), 0.0, 0.0);
return float4(MaxV(MaxV(MaxV(v1, v2), v3), v4), 0.0f, 0.0f);
}
// Pixel Shader for TileMax filter (variable width)
@@ -306,7 +294,7 @@ float4 PS_NeighborMax(Quad_VS2PS input) : SV_Target
float2 vb = MaxV(v4, MaxV(v5, v6));
float2 vc = MaxV(v7, MaxV(v8, v9));
return float4(MaxV(va, MaxV(vb, vc)) * (1.0 / cw), 0.0, 0.0);
return float4(MaxV(va, MaxV(vb, vc)) * (1.0f / cw), 0.0f, 0.0f);
}
// Interleaved gradient function from Jimenez 2014
@@ -314,8 +302,8 @@ float4 PS_NeighborMax(Quad_VS2PS input) : SV_Target
float GradientNoise(float2 uv)
{
uv = floor(uv * GBuffer.ScreenSize.xy);
float f = dot(float2(0.06711056, 0.00583715), uv);
return frac(52.9829189 * frac(f));
float f = dot(float2(0.06711056f, 0.00583715f), uv);
return frac(52.9829189f * frac(f));
}
// Returns true or false with a given interval
@@ -328,103 +316,77 @@ bool Interval(float phase, float interval)
float2 JitterTile(float2 uv)
{
float rx, ry;
sincos(GradientNoise(uv + float2(2.0, 0.0)) * (2.0f * PI), ry, rx);
return float2(rx, ry) * TexelSizeNM.xy * 0.25;
sincos(GradientNoise(uv + float2(2.0f, 0.0f)) * (2.0f * PI), ry, rx);
return float2(rx, ry) * TexelSizeNM.xy * 0.25f;
}
// Velocity sampling function
float3 SampleVelocity(float2 uv)
{
float3 v = SAMPLE_RT(Input1, uv).xyz;
return float3((v.xy * 2.0 - 1.0) * MaxBlurRadius, v.z);
return float3((v.xy * 2.0f - 1.0f) * MaxBlurRadius, v.z);
}
// Pixel Shader for reconstruction filter (applies the motion blur to the frame)
META_PS(true, FEATURE_LEVEL_ES2)
float4 PS_Reconstruction(Quad_VS2PS input) : SV_Target
{
// Color sample at the center point
const float4 c_p = SAMPLE_RT(Input0, input.TexCoord);
// Sample at the current location
const float4 color = SAMPLE_RT(Input0, input.TexCoord);
const float3 velocity = SampleVelocity(input.TexCoord);
const float velocityLen = max(length(velocity.xy), 0.5);
const float depthInv = 1.0 / velocity.z;
// Velocity/Depth sample at the center point
const float3 vd_p = SampleVelocity(input.TexCoord);
const float l_v_p = max(length(vd_p.xy), 0.5);
const float rcp_d_p = 1.0 / vd_p.z;
const float2 velocityMax = SAMPLE_RT(Input2, input.TexCoord + JitterTile(input.TexCoord)).xy;
const float velocityMaxLength = length(velocityMax);
if (velocityMaxLength < 2.0f)
return color;
const float2 velocityWeighted = (velocityLen * 2.0f > velocityMaxLength) ? velocity.xy * (velocityMaxLength / velocityLen) : velocityMax;
// NeighborMax vector sample at the center point
const float2 v_max = SAMPLE_RT(Input2, input.TexCoord + JitterTile(input.TexCoord)).xy;
const float l_v_max = length(v_max);
const float rcp_l_v_max = 1.0 / l_v_max;
// Calculate the amount of samples
const float sc = floor(min(LoopCount, velocityMaxLength * 0.5f));
// Escape early if the NeighborMax vector is small enough
if (l_v_max < 2.0)
return c_p;
// Use V_p as a secondary sampling direction except when it's too small
// compared to V_max. This vector is rescaled to be the length of V_max.
const float2 v_alt = (l_v_p * 2.0 > l_v_max) ? vd_p.xy * (l_v_max / l_v_p) : v_max;
// Determine the sample count.
const float sc = floor(min(LoopCount, l_v_max * 0.5));
// Loop variables (starts from the outermost sample)
const float dt = 1.0 / sc;
const float t_offs = (GradientNoise(input.TexCoord) - 0.5) * dt;
float t = 1.0 - dt * 0.5;
float count = 0.0;
// Background velocity
// This is used for tracking the maximum velocity in the background layer
float l_v_bg = max(l_v_p, 1.0);
// Color accumlation
float4 acc = 0.0;
// Accumlation loop
float backgroudVelocity = max(velocityLen, 1.0f);
const float dt = 1.0f / sc;
const float offsetNoise = (GradientNoise(input.TexCoord) - 0.5f) * dt;
float t = 1.0f - dt * 0.5f;
float count = 0.0f;
float4 sum = 0.0f;
LOOP
while (t > dt * 0.25)
{
// Sampling direction (switched per every two samples)
const float2 v_s = Interval(count, 4.0) ? v_alt : v_max;
const float2 sampleVelocity = Interval(count, 4.0) ? velocityWeighted : velocityMax;
// Sample position (inverted per every sample)
const float t_s = (Interval(count, 2.0) ? -t : t) + t_offs;
const float samplePosition = (Interval(count, 2.0) ? -t : t) + offsetNoise;
// Distance to the sample position
const float l_t = l_v_max * abs(t_s);
// Calculate UVs for the sample position
const float2 sampleUV = input.TexCoord + sampleVelocity * samplePosition * GBuffer.ScreenSize.zw;
// UVs for the sample position
const float2 uv0 = input.TexCoord + v_s * t_s * GBuffer.ScreenSize.zw;
//const float2 uv1 = input.TexCoord + v_s * t_s * MotionVectorsTexelSize.xy;
const float2 uv1 = uv0;
// Color sample
const float3 c = SAMPLE_RT(Input0, uv0).rgb;
// Velocity/Depth sample
const float3 vd = SampleVelocity(uv1);
// Background/Foreground separation
const float fg = saturate((vd_p.z - vd.z) * 20.0 * rcp_d_p);
// Sample color and velocity with depth
const float3 c = SAMPLE_RT(Input0, sampleUV).rgb;
const float3 velocityDepth = SampleVelocity(sampleUV);
// Length of the velocity vector
const float l_v = lerp(l_v_bg, length(vd.xy), fg);
const float foreground = saturate((velocity.z - velocityDepth.z) * 20.0f * depthInv);
const float sampleVelocityLength = lerp(backgroudVelocity, length(velocityDepth.xy), foreground);
// Sample weight
// (Distance test) * (Spreading out by motion) * (Triangular window)
const float w = saturate(l_v - l_t) / l_v * (1.2 - t);
// Apply color accumulation
float weight = saturate(sampleVelocityLength - (velocityMaxLength * abs(samplePosition))) / sampleVelocityLength * (1.2f - t);
sum += float4(c, 1.0) * weight;
// Color accumulation
acc += float4(c, 1.0) * w;
// Calculate the background velocity
backgroudVelocity = max(backgroudVelocity, sampleVelocityLength);
// Update the background velocity.
l_v_bg = max(l_v_bg, l_v);
// Advance to the next sample.
t = Interval(count, 2.0) ? t - dt : t;
count += 1.0;
// Move to the next sample
t = Interval(count, 2.0f) ? t - dt : t;
count += 1.0f;
}
// Add the center sample
acc += float4(c_p.rgb, 1.0) * (1.2 / (l_v_bg * sc * 2.0));
sum += float4(color.rgb, 1.0f) * (1.2f / (backgroudVelocity * sc * 2.0f));
return float4(acc.rgb / acc.a, c_p.a);
return float4(sum.rgb / sum.a, color.a);
}

View File

@@ -15,6 +15,10 @@
//
// Perlin noise shader by toneburst:
// http://machinesdontcare.wordpress.com/2009/06/25/3d-perlin-noise-sphere-vertex-shader-sourcecode/
//
// Lens flares by John Chapman:
//https://john-chapman.github.io/2017/11/05/pseudo-lens-flare.html
//
#include "./Flax/Common.hlsl"
#include "./Flax/Random.hlsl"
@@ -107,7 +111,6 @@ half3 ColorLookupTable(half3 linearColor)
{
// Move from linear color to encoded LUT color space
//float3 encodedColor = linearColor; // Default
//float3 encodedColor = saturate(LinearToLogC(linearColor)); // LogC
float3 encodedColor = LinearToLog(linearColor + LogToLinear(0)); // Log
float3 uvw = encodedColor * ((LUTSize - 1) / LUTSize) + (0.5f / LUTSize);

View File

@@ -25,46 +25,44 @@ float4 SampleCubemap(float3 uv)
return Cube.SampleLevel(SamplerLinearClamp, uv, SourceMipIndex);
}
float3 GetCubemapVector(float2 scaledUVs)
float3 UvToCubeMapUv(float2 uv)
{
float3 cubeCoordinates;
float3 coords;
if (CubeFace == 0)
{
cubeCoordinates = float3(1, -scaledUVs.y, -scaledUVs.x);
coords = float3(1, -uv.y, -uv.x);
}
else if (CubeFace == 1)
{
cubeCoordinates = float3(-1, -scaledUVs.y, scaledUVs.x);
coords = float3(-1, -uv.y, uv.x);
}
else if (CubeFace == 2)
{
cubeCoordinates = float3(scaledUVs.x, 1, scaledUVs.y);
coords = float3(uv.x, 1, uv.y);
}
else if (CubeFace == 3)
{
cubeCoordinates = float3(scaledUVs.x, -1, -scaledUVs.y);
coords = float3(uv.x, -1, -uv.y);
}
else if (CubeFace == 4)
{
cubeCoordinates = float3(scaledUVs.x, -scaledUVs.y, 1);
coords = float3(uv.x, -uv.y, 1);
}
else
{
cubeCoordinates = float3(-scaledUVs.x, -scaledUVs.y, -1);
coords = float3(-uv.x, -uv.y, -1);
}
return cubeCoordinates;
return coords;
}
// Pixel Shader for filtring probe mip levels
META_PS(true, FEATURE_LEVEL_ES2)
float4 PS_FilterFace(Quad_VS2PS input) : SV_Target
{
float2 scaledUVs = input.TexCoord * 2 - 1;
float3 cubeCoordinates = GetCubemapVector(scaledUVs);
float2 uv = input.TexCoord * 2 - 1;
float3 cubeCoordinates = UvToCubeMapUv(uv);
#define NUM_FILTER_SAMPLES 512
#define NUM_FILTER_SAMPLES 512
float3 N = normalize(cubeCoordinates);
float roughness = ComputeReflectionCaptureRoughnessFromMip(SourceMipIndex);
@@ -102,19 +100,15 @@ float4 PS_CopyFace(Quad_VS2PS input) : SV_Target
META_PS(true, FEATURE_LEVEL_ES2)
float4 PS_CalcDiffuseIrradiance(Quad_VS2PS input) : SV_Target
{
float2 scaledUVs = input.TexCoord * 2 - 1;
float3 cubeCoordinates = normalize(GetCubemapVector(scaledUVs));
float squaredUVs = 1 + dot(scaledUVs, scaledUVs);
// Dividing by NumSamples here to keep the sum in the range of fp16, once we get down to the 1x1 mip
float2 uv = input.TexCoord * 2 - 1;
float3 cubeCoordinates = normalize(UvToCubeMapUv(uv));
float squaredUVs = 1 + dot(uv, uv);
float weight = 4 / (sqrt(squaredUVs) * squaredUVs);
ThreeBandSHVector shCoefficients = SHBasisFunction3(cubeCoordinates);
float currentSHCoefficient = dot(shCoefficients.V0, CoefficientMask0) + dot(shCoefficients.V1, CoefficientMask1) + shCoefficients.V2 * CoefficientMask2;
float3 radiance = SampleCubemap(cubeCoordinates).rgb;
return float4(radiance * currentSHCoefficient * weight, weight);
}
@@ -124,23 +118,23 @@ float4 PS_AccDiffuseIrradiance(Quad_VS2PS input) : SV_Target
{
float4 result = 0;
{
float2 scaledUVs = saturate(input.TexCoord + Sample01.xy) * 2 - 1;
float3 cubeCoordinates = GetCubemapVector(scaledUVs);
float2 uv = saturate(input.TexCoord + Sample01.xy) * 2 - 1;
float3 cubeCoordinates = UvToCubeMapUv(uv);
result += SampleCubemap(cubeCoordinates);
}
{
float2 scaledUVs = saturate(input.TexCoord + Sample01.zw) * 2 - 1;
float3 cubeCoordinates = GetCubemapVector(scaledUVs);
float2 uv = saturate(input.TexCoord + Sample01.zw) * 2 - 1;
float3 cubeCoordinates = UvToCubeMapUv(uv);
result += SampleCubemap(cubeCoordinates);
}
{
float2 scaledUVs = saturate(input.TexCoord + Sample23.xy) * 2 - 1;
float3 cubeCoordinates = GetCubemapVector(scaledUVs);
float2 uv = saturate(input.TexCoord + Sample23.xy) * 2 - 1;
float3 cubeCoordinates = UvToCubeMapUv(uv);
result += SampleCubemap(cubeCoordinates);
}
{
float2 scaledUVs = saturate(input.TexCoord + Sample23.zw) * 2 - 1;
float3 cubeCoordinates = GetCubemapVector(scaledUVs);
float2 uv = saturate(input.TexCoord + Sample23.zw) * 2 - 1;
float3 cubeCoordinates = UvToCubeMapUv(uv);
result += SampleCubemap(cubeCoordinates);
}
return result / 4.0f;

View File

@@ -3,104 +3,38 @@
#ifndef __RANDOM__
#define __RANDOM__
// @param xy should be a integer position (e.g. pixel position on the screen), repeats each 128x128 pixels similar to a texture lookup but is only ALU
float PseudoRandom(float2 xy)
{
float2 pos = frac(xy / 128.0f) * 128.0f + float2(-64.340622f, -72.465622f);
return frac(dot(pos.xyx * pos.xyy, float3(20.390625f, 60.703125f, 2.4281209f)));
float2 p = frac(xy / 128.0f) * 128.0f + float2(-64.340622f, -72.465622f);
return frac(dot(p.xyx * p.xyy, float3(20.390625f, 60.703125f, 2.4281209f)));
}
// Find good arbitrary axis vectors to represent U and V axes of a plane, given just the normal
void FindBestAxisVectors(float3 input, out float3 axis1, out float3 axis2)
{
const float3 N = abs(input);
// Find best basis vectors
if( N.z > N.x && N.z > N.y )
{
const float3 a = abs(input);
if (a.z > a.x && a.z > a.y)
axis1 = float3(1, 0, 0);
}
else
{
axis1 = float3(0, 0, 1);
}
axis1 = normalize(axis1 - input * dot(axis1, input));
axis2 = cross(axis1, input);
}
// References for noise:
//
// Improved Perlin noise
// http://mrl.nyu.edu/~perlin/noise/
// http://http.developer.nvidia.com/GPUGems/gpugems_ch05.html
// Modified Noise for Evaluation on Graphics Hardware
// http://www.csee.umbc.edu/~olano/papers/mNoise.pdf
// Perlin Noise
// http://mrl.nyu.edu/~perlin/doc/oscar.html
// Fast Gradient Noise
// http://prettyprocs.wordpress.com/2012/10/20/fast-perlin-noise
// -------- ALU based method ---------
/*
* Pseudo random number generator, based on "TEA, a tiny Encrytion Algorithm"
* http://citeseer.ist.psu.edu/viewdoc/download?doi=10.1.1.45.281&rep=rep1&type=pdf
* @param v - old seed (full 32bit range)
* @param iterationCount - >=1, bigger numbers cost more performance but improve quality
* @return new seed
*/
uint2 ScrambleTEA(uint2 v, uint iterationCount = 3)
{
// Start with some random data (numbers can be arbitrary but those have been used by others and seem to work well)
uint k[4] ={ 0xA341316Cu , 0xC8013EA4u , 0xAD90777Du , 0x7E95761Eu };
uint y = v[0];
uint z = v[1];
uint sum = 0;
UNROLL
for (uint i = 0; i < iterationCount; i++)
{
sum += 0x9e3779b9;
y += (z << 4u) + k[0] ^ z + sum ^ (z >> 5u) + k[1];
z += (y << 4u) + k[2] ^ y + sum ^ (y >> 5u) + k[3];
}
return uint2(y, z);
}
// Computes a pseudo random number for a given integer 2D position
// @param v - old seed (full 32bit range)
// @return random number in the range -1 .. 1
float ComputeRandomFrom2DPosition(uint2 v)
{
return (ScrambleTEA(v).x & 0xffff ) / (float)(0xffff) * 2 - 1;
}
// Computes a pseudo random number for a given integer 2D position
// @param v - old seed (full 32bit range)
// @return random number in the range -1 .. 1
float ComputeRandomFrom3DPosition(int3 v)
{
// numbers found by experimentation
return ComputeRandomFrom2DPosition(v.xy ^ (uint2(0x123456, 0x23446) * v.zx) );
}
// Evaluate polynomial to get smooth transitions for Perlin noise (2 add, 5 mul)
float PerlinRamp(in float t)
{
return t * t * t * (t * (t * 6 - 15) + 10);
}
float2 PerlinRamp(in float2 t)
{
return t * t * t * (t * (t * 6 - 15) + 10);
}
float3 PerlinRamp(in float3 t)
{
return t * t * t * (t * (t * 6 - 15) + 10);
}
float4 PerlinRamp(in float4 t)
{
return t * t * t * (t * (t * 6 - 15) + 10);

View File

@@ -11,21 +11,20 @@ struct ThreeBandSHVector
half V2;
};
ThreeBandSHVector SHBasisFunction3(half3 inputVector)
ThreeBandSHVector SHBasisFunction3(half3 v)
{
ThreeBandSHVector result;
result.V0.x = 0.282095f;
result.V0.y = -0.488603f * inputVector.y;
result.V0.z = 0.488603f * inputVector.z;
result.V0.w = -0.488603f * inputVector.x;
result.V0.y = -0.488603f * v.y;
result.V0.z = 0.488603f * v.z;
result.V0.w = -0.488603f * v.x;
half3 vectorSquared = inputVector * inputVector;
result.V1.x = 1.092548f * inputVector.x * inputVector.y;
result.V1.y = -1.092548f * inputVector.y * inputVector.z;
result.V1.z = 0.315392f * (3.0f * inputVector.z - 1.0f);
result.V1.w = -1.092548f * inputVector.x * inputVector.z;
result.V2 = 0.546274f * (inputVector.x - inputVector.y);
result.V1.x = 1.092548f * v.x * v.y;
result.V1.y = -1.092548f * v.y * v.z;
result.V1.z = 0.315392f * (3.0f * v.z - 1.0f);
result.V1.w = -1.092548f * v.x * v.z;
result.V2 = 0.546274f * (v.x - v.y);
return result;
}

View File

@@ -39,25 +39,8 @@ struct LightShadowData
#define DECLARE_LIGHTSHADOWDATA_ACCESS(uniformName) LightShadowData Get##uniformName##Data() { return uniformName; }
#endif
// Gets the cube texture face index to use for shadow map sampling for the given view-to-light direction vector
// Where: direction = normalize(worldPosition - lightPosition)
int GetCubeFaceIndex(float3 direction)
{
int cubeFaceIndex;
float3 absDirection = abs(direction);
float maxDirection = max(absDirection.x, max(absDirection.y, absDirection.z));
if (maxDirection == absDirection.x)
cubeFaceIndex = absDirection.x == direction.x ? 0 : 1;
else if (maxDirection == absDirection.y)
cubeFaceIndex = absDirection.y == direction.y ? 2 : 3;
else
cubeFaceIndex = absDirection.z == direction.z ? 4 : 5;
return cubeFaceIndex;
}
float3 GetShadowPositionOffset(float offsetScale, float NoL, float3 normal)
{
// Note: offsetScale should be multiplied by 2*ShadowMapTextureTexelSize on CPU
float normalOffsetScale = saturate(1.0f - NoL);
return normal * (offsetScale * normalOffsetScale);
}
@@ -65,8 +48,6 @@ float3 GetShadowPositionOffset(float offsetScale, float NoL, float3 normal)
float CalculateSubsurfaceOcclusion(float opacity, float sceneDepth, float shadowMapDepth)
{
float thickness = max(sceneDepth - shadowMapDepth, 0);
//float density = -0.05f * log(1 - min(opacity, 0.999f));
//float occlusion = saturate(exp(-thickness * density));
float occlusion = 1 - thickness * lerp(1.0f, 100.0f, opacity);
return shadowMapDepth > 0.99f ? 1 : occlusion;
}

View File

@@ -42,6 +42,22 @@
#include "./Flax/PCFKernels.hlsl"
#endif
// Gets the cube texture face index to use for shadow map sampling for the given view-to-light direction vector
// Where: direction = normalize(worldPosition - lightPosition)
int GetCubeFaceIndex(float3 direction)
{
int cubeFaceIndex;
float3 absDirection = abs(direction);
float maxDirection = max(absDirection.x, max(absDirection.y, absDirection.z));
if (maxDirection == absDirection.x)
cubeFaceIndex = absDirection.x == direction.x ? 0 : 1;
else if (maxDirection == absDirection.y)
cubeFaceIndex = absDirection.y == direction.y ? 2 : 3;
else
cubeFaceIndex = absDirection.z == direction.z ? 4 : 5;
return cubeFaceIndex;
}
// Samples the shadow map with a fixed-size PCF kernel optimized with GatherCmpRed.
// Uses code from "Fast Conventional Shadow Filtering" by Holger Gruen, in GPU Pro.
float SampleShadowMapFixedSizePCF(Texture2DArray shadowMap, float2 shadowMapSize, float sceneDepth, float2 shadowPos, uint cascadeIndex)

View File

@@ -103,30 +103,12 @@ float3 ComputeVolumeUV(float3 worldPosition, float4x4 worldToClip)
return float3(ndcPosition.xy * float2(0.5f, -0.5f) + 0.5f, ComputeNormalizedZSliceFromDepth(ndcPosition.w));
}
float IsotropicPhase()
{
return 1 / (4 * PI);
}
float HenyeyGreensteinPhase(float g, float cosTheta)
{
return (1 - g * g) / (4 * PI * pow(1 + g * g + 2 * g * cosTheta, 1.5f));
}
float SchlickPhase(float k, float cosTheta)
{
float t = (1 + k * cosTheta);
return (1 - k * k) / (4 * PI * t * t);
}
float RaleighPhase(float cosTheta)
{
return 3.0f * (1.0f + cosTheta * cosTheta) / (16.0f * PI);
}
// Positive g = forward scattering
// Zero g = isotropic
// Negative g = backward scattering
// +g = forward scattering, 0=g = isotropic, -g = backward scattering
float PhaseFunction(float g, float cosTheta)
{
return HenyeyGreensteinPhase(g, cosTheta);

View File

@@ -476,6 +476,25 @@ namespace Flax.Build
"System.Core",
};
SetupProjectConfigurations(project, rootProject);
if (project.Configurations.Count == 0)
{
// Hardcoded dummy configuration even if platform tools are missing for this platform
var platform = Platform.BuildPlatform;
var architecture = TargetArchitecture.x64;
var configuration = TargetConfiguration.Debug;
project.Configurations.Add(new Project.ConfigurationData
{
Platform = platform.Target,
PlatformName = platform.Target.ToString(),
Architecture = architecture,
ArchitectureName = architecture.ToString(),
Configuration = configuration,
ConfigurationName = configuration.ToString(),
Target = target,
TargetBuildOptions = GetBuildOptions(target, platform, null, architecture, configuration, project.WorkspaceRootPath),
Modules = new Dictionary<Module, BuildOptions>(),
});
}
var c = project.Configurations[0];
c.Name = "Debug|AnyCPU";
c.Text = "Debug";

View File

@@ -946,7 +946,7 @@ namespace Flax.Build
{
using (new ProfileEventScope(reference.Project.Name))
{
if (Configuration.BuildBindingsOnly || reference.Project.IsCSharpOnlyProject)
if (Configuration.BuildBindingsOnly || reference.Project.IsCSharpOnlyProject || !platform.HasRequiredSDKsInstalled)
{
BuildTargetReferenceNativeCppBindingsOnly(buildContext, buildData, reference);
}

View File

@@ -37,7 +37,7 @@ namespace Flax.Build.Platforms
if (string.IsNullOrEmpty(sdkPath))
{
// Look for ndk installed side-by-side with an sdk
if (AndroidSdk.Instance.IsValid)
if (AndroidSdk.Instance.IsValid && Directory.Exists(Path.Combine(AndroidSdk.Instance.RootPath, "ndk")))
{
var subdirs = Directory.GetDirectories(Path.Combine(AndroidSdk.Instance.RootPath, "ndk"));
if (subdirs.Length != 0)