Some renaming

This commit is contained in:
2025-04-15 01:32:56 +02:00
parent 12d6a1578d
commit a39d972126
864 changed files with 29 additions and 34 deletions

View File

@@ -0,0 +1 @@
BasedOnStyle: Google

View File

@@ -0,0 +1,109 @@
set(LIBMANUL_NAME "betterRenderer")
set(LIBMANUL_WITH_DX12 OFF)
set(LIBMANUL_WITH_VULKAN OFF)
if (WIN32)
set(LIBMANUL_WITH_DX12 ON)
elseif (UNIX)
set(LIBMANUL_WITH_VULKAN ON)
endif ()
if (LIBMANUL_WITH_VULKAN)
find_package(Vulkan 1.3.230 REQUIRED)
include_directories(${Vulkan_INCLUDE_DIRS})
endif ()
# Third-party deps
add_subdirectory("thirdparty/nvrhi")
add_subdirectory("thirdparty/yaml-cpp")
add_subdirectory("thirdparty/fmt")
add_subdirectory("thirdparty/entt")
add_subdirectory("thirdparty/fsr2")
add_subdirectory("mashadercompiler")
add_subdirectory("shaders")
# Source code
file(GLOB_RECURSE src
"renderer/*.cpp"
"renderer/*.hpp"
"renderer/*.h")
add_library(${LIBMANUL_NAME} ${src})
target_include_directories(${LIBMANUL_NAME} PUBLIC
"renderer/include")
target_include_directories(${LIBMANUL_NAME} PRIVATE
"renderer/source"
"${GLOBAL_INCLUDE_DIR}"
"${IMGUI_DIR}"
"${GLM_INCLUDE_DIR}")
# Needs more refactoring to get rid of that unfortunately...
target_include_directories(${LIBMANUL_NAME} PRIVATE
"${GLOBAL_ROOT_DIR}")
# DynObj requires MOVER.h
target_include_directories(${LIBMANUL_NAME} PRIVATE
"${GLOBAL_ROOT_DIR}/McZapkie")
# OpenAL required by MOVER.h
target_include_directories(${LIBMANUL_NAME} PRIVATE
"${OPENAL_INCLUDE_DIR}")
# GLuint, GLint typedef'd here
target_include_directories(${LIBMANUL_NAME} PRIVATE
"${DEPS_DIR}/glad/include")
# Python.h
target_include_directories(${LIBMANUL_NAME} PRIVATE
"${PYTHON_INCLUDE_DIR}")
target_compile_definitions(${LIBMANUL_NAME} PRIVATE
GLM_ENABLE_EXPERIMENTAL
GLM_FORCE_SWIZZLE)
target_compile_definitions(${LIBMANUL_NAME} PRIVATE
_USE_MATH_DEFINES)
# windows headers included SOMEWHERE
target_compile_definitions(${LIBMANUL_NAME} PRIVATE
NOMINMAX)
# to avoid global_settings changing size...
target_compile_definitions(${LIBMANUL_NAME} PRIVATE
${DEFINITIONS})
# we need to include libserialport stuff to include uart because of
# the setting class having mismatched size across TUs otherwise
target_include_directories(${LIBMANUL_NAME} PRIVATE
${libserialport_INCLUDE_DIR} ${LUAJIT_INCLUDE_DIR})
if (LIBMANUL_WITH_DX12)
target_link_libraries(${LIBMANUL_NAME} nvrhi_d3d12 dxgi.lib)
target_compile_definitions(${LIBMANUL_NAME} PUBLIC "LIBMANUL_WITH_D3D12=1" "GLFW_EXPOSE_NATIVE_WIN32")
else ()
target_compile_definitions(${LIBMANUL_NAME} PUBLIC "LIBMANUL_WITH_D3D12=0")
endif ()
if (LIBMANUL_WITH_VULKAN)
target_link_libraries(${LIBMANUL_NAME} nvrhi_vk Vulkan::Headers)
target_compile_definitions(${LIBMANUL_NAME} PUBLIC "LIBMANUL_WITH_VULKAN=1" "GLFW_INCLUDE_VULKAN")
else ()
target_compile_definitions(${LIBMANUL_NAME} PUBLIC "LIBMANUL_WITH_VULKAN=0")
endif ()
## For double-precision vector goodness
#if (WIN32)
# target_compile_options(${LIBMANUL_NAME} PRIVATE "/arch:AVX2")
#elseif (UNIX)
# target_compile_options(${LIBMANUL_NAME} PRIVATE "-mavx2")
#endif ()
target_sources(${LIBMANUL_NAME} INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/eu07_source/register.cpp")
set_target_properties(nvrhi yaml-cpp fmt EnTT glfw yaml-cpp-parse yaml-cpp-read yaml-cpp-sandbox PROPERTIES FOLDER "libraries")
target_link_libraries(${LIBMANUL_NAME} ${LIBMANUL_NAME}_fsr2 nvrhi yaml-cpp fmt EnTT glfw)
add_dependencies(${LIBMANUL_NAME} ${LIBMANUL_NAME}_shaders)

View File

@@ -0,0 +1,31 @@
#include "Logs.h"
#include "nvrenderer/nvrenderer.h"
namespace {
std::unique_ptr<gfx_renderer> create_nvrenderer_for_d3d12() {
return std::make_unique<NvRenderer>(NvRenderer::Api::D3D12);
}
std::unique_ptr<gfx_renderer> create_nvrenderer_for_vulkan() {
return std::make_unique<NvRenderer>(NvRenderer::Api::Vulkan);
}
std::unique_ptr<gfx_renderer> create_nvrenderer_default() {
#if LIBMANUL_WITH_D3D12
return std::make_unique<NvRenderer>(NvRenderer::Api::D3D12);
#elif LIBMANUL_WITH_VULKAN
return std::make_unique<NvRenderer>(NvRenderer::Api::Vulkan);
#endif
ErrorLog("Failed to initialize any NVRHI Renderer");
return nullptr;
}
} // namespace
bool register_manul_d3d12 =
LIBMANUL_WITH_D3D12 &&
gfx_renderer_factory::get_instance()->register_backend(
"advanced_d3d12", &create_nvrenderer_for_d3d12);
bool register_manul_vulkan =
LIBMANUL_WITH_VULKAN &&
gfx_renderer_factory::get_instance()->register_backend(
"advanced_vk", &create_nvrenderer_for_vulkan);
bool register_manul = gfx_renderer_factory::get_instance()->register_backend(
"advanced", &create_nvrenderer_default);

View File

@@ -0,0 +1,25 @@
cmake_minimum_required(VERSION 3.24)
set(CMAKE_CXX_STANDARD 17)
project(MaSzynaShaderCompiler)
file(GLOB_RECURSE src "src/*.cpp" "src/*.hpp")
# Confirmed to work with DXC distributed via VCPKG. Your mileage may vary
# TODO Make sure that it works as well for people who do not use VCPKG
find_package(directx-dxc CONFIG REQUIRED)
add_executable(MaSzynaShaderCompiler ${src})
target_link_libraries(MaSzynaShaderCompiler nvrhi yaml-cpp Microsoft::DirectXShaderCompiler Microsoft::DXIL)
if (WIN32)
target_link_libraries(MaSzynaShaderCompiler delayimp)
target_link_options(MaSzynaShaderCompiler PRIVATE "/DELAYLOAD:dxcompiler.dll")
endif ()
# We later pass the DXIL.dll parent directory as first argument to MaSzynaShaderCompiler so that it can sign DX12 shaders
get_target_property(dxil_dll_path Microsoft::DXIL IMPORTED_LOCATION)
get_filename_component(dxil_dll_path ${dxil_dll_path} DIRECTORY)
set_target_properties(MaSzynaShaderCompiler PROPERTIES dxil_path "${dxil_dll_path}")

View File

@@ -0,0 +1,18 @@
#include <iostream>
#include "shader_compiler.hpp"
int main(int argc, const char** argv) {
if (argc != 4) {
return -1;
}
#ifdef WIN32
std::cout << "DXIL path: " << argv[1] << std::endl;
SetDllDirectoryA(argv[1]);
#endif
setlocale(LC_ALL, ".UTF8");
MaShaderCompiler compiler{};
compiler.m_project_path = argv[2];
compiler.m_output_path = argv[3];
return compiler.Run();
}

View File

@@ -0,0 +1,312 @@
#include "shader_compiler.hpp"
#include <fstream>
#include <iostream>
#include "utils.hpp"
int MaShaderCompiler::Run() {
ParseOptions();
Init();
m_shader_path = m_project_path.parent_path();
YAML::Node src = YAML::LoadFile((m_project_path).generic_string());
create_directories(m_output_path);
{
YAML::Node dest;
if (!CompileProject(dest, src["shaders"], src["templates"],
ShaderPlatform::D3D12)) {
return E_FAIL;
}
std::ofstream fout(m_output_path / "shaders_dxil.manul");
fout << dest;
}
{
YAML::Node dest;
if (!CompileProject(dest, src["shaders"], src["templates"],
ShaderPlatform::Vulkan)) {
return E_FAIL;
}
std::ofstream fout(m_output_path / "shaders_spirv.manul");
fout << dest;
}
{
std::ofstream fout(m_output_path / "project.manul");
fout << src;
}
return S_OK;
}
void MaShaderCompiler::ParseOptions() {}
void MaShaderCompiler::Init() {
const char *shader_path = getenv("shader_path");
if (shader_path) {
m_shader_path = shader_path;
std::cout << "shader path: " << m_shader_path.generic_string() << std::endl;
} else {
m_shader_path = "";
std::cout << "no custom shader path specified" << std::endl;
}
DxcCreateInstance(CLSID_DxcUtils, IID_PPV_ARGS(&m_dxc_utils));
DxcCreateInstance(CLSID_DxcCompiler, IID_PPV_ARGS(&m_dxc_compiler));
m_dxc_utils->CreateDefaultIncludeHandler(&m_dxc_include_handler);
}
YAML::Binary MaShaderCompiler::CompileShaderToBlob(
std::filesystem::path file_name, std::wstring entry_name,
std::vector<std::tuple<std::wstring, std::wstring>> defines,
std::string target, ShaderPlatform platform) {
file_name.replace_extension(".hlsl");
static std::unordered_map<std::string, std::wstring> targets{
{"compute", L"cs_6_0"}, {"vertex", L"vs_6_0"}, {"hull", L"hs_6_0"},
{"domain", L"ds_6_0"}, {"geometry", L"gs_6_0"}, {"pixel", L"ps_6_0"}};
const wchar_t *target_str;
if (auto target_found = targets.find(target); target_found != targets.end()) {
target_str = target_found->second.c_str();
} else {
return {};
}
RefCountPtr<IDxcBlobEncoding> source_blob;
if (FAILED(m_dxc_utils->LoadFile(file_name.generic_wstring().c_str(), nullptr,
&source_blob))) {
return {};
}
DxcBuffer Source;
Source.Ptr = source_blob->GetBufferPointer();
Source.Size = source_blob->GetBufferSize();
Source.Encoding = DXC_CP_ACP;
std::vector<std::wstring> include_paths{
m_shader_path.generic_wstring(),
file_name.parent_path().generic_wstring()};
std::vector<const wchar_t *> args;
args.emplace_back(L"-HV");
args.emplace_back(L"2021");
args.emplace_back(L"-Zpc");
for (const auto &include_path : include_paths) {
args.emplace_back(L"-I");
args.emplace_back(include_path.c_str());
}
// if (m_generate_debug) {
// args.emplace_back(L"-Od");
// args.emplace_back(L"-Zi");
// } else {
// //args.emplace_back(L"-O3");
// }
args.emplace_back(L"-Zi");
args.emplace_back(L"-Qembed_debug");
std::vector<std::wstring> reg_shifts;
// Gather SPIRV register shifts once
static const wchar_t *regShiftArgs[] = {
L"-fvk-s-shift",
L"-fvk-t-shift",
L"-fvk-b-shift",
L"-fvk-u-shift",
};
uint32_t regShifts[] = {128, 0, 256, 384};
switch (platform) {
case ShaderPlatform::D3D12:
args.emplace_back(L"-Gis");
break;
case ShaderPlatform::Vulkan:
args.emplace_back(L"-spirv");
args.emplace_back(L"-fspv-target-env=vulkan1.2");
args.emplace_back(L"-fvk-use-dx-layout");
defines.emplace_back(L"SPIRV", L"1");
for (uint32_t reg = 0; reg < 4; reg++) {
for (uint32_t space = 0; space < 8; space++) {
wchar_t buf[64];
reg_shifts.emplace_back(regShiftArgs[reg]);
swprintf(buf, std::size(buf), L"%u", regShifts[reg]);
reg_shifts.emplace_back(buf);
swprintf(buf, std::size(buf), L"%u", space);
reg_shifts.emplace_back(buf);
}
}
for (const std::wstring &arg : reg_shifts) {
args.emplace_back(arg.c_str());
}
break;
}
std::vector<DxcDefine> dxc_defines{};
dxc_defines.reserve(defines.size());
for (const auto &[name, definition] : defines) {
auto &define = dxc_defines.emplace_back();
define.Name = name.c_str();
define.Value = definition.c_str();
}
RefCountPtr<IDxcCompilerArgs> compiler_args;
m_dxc_utils->BuildArguments(file_name.stem().generic_wstring().c_str(),
entry_name.c_str(), target_str, args.data(),
args.size(), dxc_defines.data(),
dxc_defines.size(), &compiler_args);
std::vector<LPCWSTR> wargs(
compiler_args->GetArguments(),
compiler_args->GetArguments() + compiler_args->GetCount());
RefCountPtr<IDxcResult> result;
HRESULT hr = m_dxc_compiler->Compile(
&Source, // Source buffer.
compiler_args->GetArguments(), // Array of pointers to arguments.
compiler_args->GetCount(), // Number of arguments.
m_dxc_include_handler, // User-provided interface to handle
// #include
// directives (optional).
IID_PPV_ARGS(&result) // Compiler output status, buffer, and errors.
);
RefCountPtr<IDxcBlobUtf8> errors;
result->GetOutput(DXC_OUT_ERRORS, IID_PPV_ARGS(&errors), nullptr);
auto errors_length = errors->GetStringLength();
if (errors && errors_length) {
std::string message(errors->GetStringPointer());
std::cout << "Shader compile log: " << file_name.generic_string()
<< std::endl;
std::cout << message;
// WriteLog("Shader compile log: " + fileName.generic_string());
// WriteLog(message);
}
if (FAILED(hr)) {
std::cout << "Shader compile failed: " << file_name.generic_string()
<< std::endl;
return {};
}
RefCountPtr<IDxcBlob> binary_blob;
result->GetOutput(DXC_OUT_OBJECT, IID_PPV_ARGS(&binary_blob), nullptr);
auto buf_start =
static_cast<unsigned char *>(binary_blob->GetBufferPointer());
auto buf_end = buf_start + binary_blob->GetBufferSize();
std::vector<unsigned char> buf{buf_start, buf_end};
YAML::Binary binary{};
binary.swap(buf);
return binary;
}
bool MaShaderCompiler::CompileUtilityShader(YAML::Node &dest,
const YAML::Node &src,
const YAML::Node &templates,
ShaderPlatform platform) {
std::vector<std::tuple<std::wstring, std::wstring>> definitions{};
for (const auto definition :
TemplateOverride("definitions", src, templates)) {
auto &[name, define] = definitions.emplace_back();
name = ToWide(definition.first.as<std::string>());
define = ToWide(definition.second.as<std::string>());
}
const std::filesystem::path file_name =
m_shader_path /
TemplateOverride("source", src, templates).as<std::string>();
const auto entry_name =
TemplateOverride("entrypoint", src, templates).as<std::string>();
const auto target =
TemplateOverride("target", src, templates).as<std::string>();
const auto blob = CompileShaderToBlob(file_name, ToWide(entry_name),
definitions, target, platform);
if (!blob.size()) {
return false;
}
dest["binary"] = blob;
dest["entrypoint"] = entry_name;
return true;
}
bool MaShaderCompiler::CompileMaterial(YAML::Node &dest, const YAML::Node &src,
const YAML::Node &templates,
ShaderPlatform platform) {
std::vector<std::tuple<std::wstring, std::wstring>> definitions{};
for (const auto definition :
TemplateOverride("definitions", src, templates)) {
auto &[name, define] = definitions.emplace_back();
name = ToWide(definition.first.as<std::string>());
define = ToWide(definition.second.as<std::string>());
}
const std::filesystem::path file_name =
m_shader_path /
TemplateOverride("source", src, templates).as<std::string>();
const std::string entry_name = "main";
const std::string target = "pixel";
for (int i = 0; i < static_cast<int>(MaterialRenderPass::Count); ++i) {
auto pass = static_cast<MaterialRenderPass>(i);
const static std::unordered_map<MaterialRenderPass, std::string> pass_names{
{MaterialRenderPass::Deferred, "deferred"},
{MaterialRenderPass::Forward, "forward"},
{MaterialRenderPass::CubeMap, "cubemap"},
};
static std::unordered_map<MaterialRenderPass, std::wstring>
pass_definitions{
{MaterialRenderPass::Deferred, L"PASS_GBUFFER"},
{MaterialRenderPass::Forward, L"PASS_FORWARD"},
{MaterialRenderPass::CubeMap, L"PASS_CUBEMAP"},
};
std::vector<std::tuple<std::wstring, std::wstring>> local_definitions{};
local_definitions.emplace_back(L"PASS", pass_definitions.at(pass));
local_definitions.insert(local_definitions.end(), definitions.begin(),
definitions.end());
auto blob = CompileShaderToBlob(file_name, ToWide(entry_name),
local_definitions, target, platform);
if (!blob.size()) {
return false;
}
dest[pass_names.at(pass)]["binary"] = blob;
dest[pass_names.at(pass)]["entrypoint"] = entry_name;
}
return true;
}
bool MaShaderCompiler::CompileProject(YAML::Node &dest, const YAML::Node &src,
const YAML::Node &templates,
ShaderPlatform platform) {
{
YAML::Node dest_materials = dest["materials"];
for (const auto material : src["materials"]) {
YAML::Node dest_material;
if (!CompileMaterial(dest_material, material.second, templates, platform))
return false;
dest_materials[material.first] = dest_material;
}
}
{
for (const auto material : src["utility"]) {
YAML::Node dest_shader;
if (!CompileUtilityShader(dest_shader, material.second, templates,
platform))
return false;
dest["utility"][material.first] = dest_shader;
}
}
return true;
}

View File

@@ -0,0 +1,47 @@
#pragma once
#include <nvrhi/nvrhi.h>
#include <yaml-cpp/yaml.h>
#include <filesystem>
#ifndef _WIN32
#include <WinAdapter.h>
#else
#include <Windows.h>
#endif
#include <dxcapi.h>
template <typename T>
using RefCountPtr = nvrhi::RefCountPtr<T>;
enum class MaterialRenderPass : int { Deferred, Forward, CubeMap, Count };
enum class ShaderPlatform { D3D12, Vulkan };
class MaShaderCompiler {
public:
int Run();
std::filesystem::path m_project_path;
std::filesystem::path m_output_path;
private:
std::filesystem::path m_shader_path;
bool m_generate_debug = false;
void ParseOptions();
void Init();
YAML::Binary CompileShaderToBlob(
std::filesystem::path file_name, std::wstring entry_name,
std::vector<std::tuple<std::wstring, std::wstring>> defines,
std::string target, ShaderPlatform platform);
bool CompileUtilityShader(YAML::Node& dest, const YAML::Node& src,
const YAML::Node& templates,
ShaderPlatform platform);
bool CompileMaterial(YAML::Node& dest, const YAML::Node& src,
const YAML::Node& templates, ShaderPlatform platform);
bool CompileProject(YAML::Node& dest, const YAML::Node& src,
const YAML::Node& templates, ShaderPlatform platform);
RefCountPtr<IDxcUtils> m_dxc_utils;
RefCountPtr<IDxcCompiler3> m_dxc_compiler;
RefCountPtr<IDxcIncludeHandler> m_dxc_include_handler;
};

View File

@@ -0,0 +1,31 @@
#pragma once
#include <yaml-cpp/yaml.h>
#include <string>
inline std::wstring ToWide(const std::string& str) {
std::wstring wstr{};
wstr.resize(std::mbstowcs(nullptr, str.c_str(), static_cast<uint64_t>(-1)));
std::mbstowcs(wstr.data(), str.c_str(), wstr.size());
return wstr;
}
inline std::string ToNarrow(const std::wstring& wstr) {
std::string str{};
str.resize(std::wcstombs(nullptr, wstr.c_str(), static_cast<uint64_t>(-1)));
std::wcstombs(str.data(), wstr.c_str(), str.size());
return str;
}
template <typename KeyType>
YAML::Node TemplateOverride(const KeyType& key, const YAML::Node& container,
const YAML::Node& templates) {
YAML::Node local = container[key];
YAML::Node use_template = container["use_template"];
if (!local.IsDefined() && use_template.IsDefined() &&
templates[use_template.as<std::string_view>()].IsDefined()) {
return templates[use_template.as<std::string_view>()][key];
}
return local;
}

View File

@@ -0,0 +1,735 @@
#pragma once
//STL includes required by EU07 includes
#include <deque>
#include <stdexcept>
#include <memory>
#include <variant>
#include <math.h>
// OpenGL includes required by EU07 includes
#include <glad/glad.h> // for GLuint, GLint
// Div. includes required by EU07 includes
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <nvrhi/nvrhi.h>
#include <yaml-cpp/yaml.h>
#include <entt/container/dense_map.hpp>
#include <future>
#include <mutex>
// EU07 includes
#include <Classes.h>
#include <scene.h>
#include "quadtree.h"
#include "renderer.h"
#include "nvrenderer_enums.h"
#include "resource_registry.h"
#include "sky.h"
template <typename Renderer>
struct MaRendererConstants {
static constexpr size_t NumMaterialPasses() noexcept {
return static_cast<size_t>(Renderer::RenderPassType::Num);
}
static constexpr size_t NumDrawTypes() noexcept {
return static_cast<size_t>(Renderer::DrawType::Num);
}
static constexpr size_t NumShadowPipelines() noexcept {
return NumDrawTypes() << 1;
}
static constexpr size_t NumMaterialPipelines() noexcept {
return NumDrawTypes() * NumMaterialPasses();
}
static constexpr size_t GetPipelineIndex(
typename Renderer::RenderPassType pass_type,
typename Renderer::DrawType draw_type) {
return static_cast<size_t>(draw_type) * NumMaterialPasses() +
static_cast<size_t>(pass_type);
}
static constexpr size_t GetDefaultShadowPipelineIndex(
typename Renderer::DrawType type, const bool masked) {
return static_cast<size_t>(type) << 1 | !!masked & 1;
}
};
class NvRenderer : public gfx_renderer, public MaResourceRegistry {
public:
enum class Api { D3D11, D3D12, Vulkan };
using RenderPassType = RendererEnums::RenderPassType;
using DrawType = RendererEnums::DrawType;
public:
using Constants = MaRendererConstants<NvRenderer>;
// SZ renderer interface implementation
virtual ~NvRenderer() override {}
virtual bool Init(GLFWwindow *Window) override;
virtual bool AddViewport(
const global_settings::extraviewport_config &conf) override;
virtual void Shutdown() override;
virtual bool Render() override;
virtual void SwapBuffers() override;
virtual float Framerate() override;
virtual gfx::geometrybank_handle Create_Bank() override;
virtual gfx::geometry_handle Insert(gfx::index_array &Indices,
gfx::vertex_array &Vertices,
gfx::userdata_array &Userdata,
gfx::geometrybank_handle const &Geometry,
int const Type) override;
virtual gfx::geometry_handle Insert(gfx::vertex_array &Vertices,
gfx::userdata_array &Userdata,
gfx::geometrybank_handle const &Geometry,
int const Type) override;
virtual bool Replace(gfx::vertex_array &Vertices,
gfx::userdata_array &Userdata,
gfx::geometry_handle const &Geometry, int const Type,
std::size_t const Offset = 0) override;
virtual bool Append(gfx::vertex_array &Vertices,
gfx::userdata_array &Userdata,
gfx::geometry_handle const &Geometry,
int const Type) override;
virtual gfx::index_array const &Indices(
gfx::geometry_handle const &Geometry) const override;
virtual gfx::vertex_array const &Vertices(
gfx::geometry_handle const &Geometry) const override;
const gfx::userdata_array &UserData(
const gfx::geometry_handle &Geometry) const override;
virtual material_handle Fetch_Material(std::string const &Filename,
bool const Loadnow = true) override;
virtual void Bind_Material(material_handle const Material,
TSubModel const *sm = nullptr,
lighting_data const *lighting = nullptr) override;
virtual IMaterial const *Material(
material_handle const Material) const override;
virtual std::shared_ptr<gl::program> Fetch_Shader(
std::string const &name) override;
virtual texture_handle Fetch_Texture(
std::string const &Filename, bool const Loadnow = true,
GLint format_hint = GL_SRGB_ALPHA) override;
virtual void Bind_Texture(texture_handle const Texture) override;
virtual void Bind_Texture(std::size_t const Unit,
texture_handle const Texture) override;
virtual ITexture &Texture(texture_handle const Texture) override;
virtual ITexture const &Texture(texture_handle const Texture) const override;
virtual void Pick_Control_Callback(
std::function<void(TSubModel const *, const glm::vec2)> Callback)
override;
virtual void Pick_Node_Callback(
std::function<void(scene::basic_node *)> Callback) override;
virtual TSubModel const *Pick_Control() const override;
virtual scene::basic_node const *Pick_Node() const override;
virtual glm::dvec3 Mouse_Position() const override;
virtual void Update(double const Deltatime) override;
virtual void Update_Pick_Control() override;
virtual void Update_Pick_Node() override;
virtual glm::dvec3 Update_Mouse_Position() override;
virtual bool Debug_Ui_State(std::optional<bool>) override;
virtual std::string const &info_times() const override;
virtual std::string const &info_stats() const override;
virtual imgui_renderer *GetImguiRenderer() override;
virtual void MakeScreenshot() override;
NvRenderer(Api api) : m_api(api), MaResourceRegistry(nullptr) {}
static bool d3d11_renderer_register;
static bool d3d12_renderer_register;
static bool vk_renderer_register;
static bool renderer_register;
struct IFrustumTester {
virtual bool IntersectSphere(const glm::dvec3 &origin,
double radius) const = 0;
virtual bool IntersectBox(const glm::dvec3 &origin,
const glm::dvec3 &extent) const = 0;
virtual bool IntersectBox(const glm::dmat4x3 &transform,
const glm::dvec3 &origin,
const glm::dvec3 &extent) const = 0;
};
class NvRendererBackend *GetBackend() const { return m_backend.get(); }
// private:
friend class NvRendererBackend;
friend class NvRendererMessageCallback;
friend class NvImguiRenderer;
friend class NvTextureManager;
friend struct NvGbuffer;
friend struct NvSsao;
friend struct FullScreenPass;
friend struct NvTAA;
friend struct NvFSR;
friend struct MaEnvironment;
friend struct MaShadowMap;
Api m_api;
std::shared_ptr<class NvRendererBackend> m_backend;
std::shared_ptr<class NvRendererMessageCallback> m_message_callback;
std::shared_ptr<class NvImguiRenderer> m_imgui_renderer;
std::shared_ptr<struct NvGbuffer> m_gbuffer;
std::shared_ptr<struct NvGbuffer> m_gbuffer_cube;
std::shared_ptr<struct NvGbuffer> m_gbuffer_shadow;
std::shared_ptr<struct GbufferLighting> m_gbuffer_lighting;
std::shared_ptr<struct GbufferBlitPass> m_gbuffer_blit;
std::shared_ptr<struct NvSsao> m_ssao;
std::shared_ptr<struct MotionCache> m_motion_cache;
std::shared_ptr<struct InstanceBank> m_instance_cache;
std::shared_ptr<struct NvTAA> m_taa;
std::shared_ptr<struct TonemapPass> m_tonemap;
std::shared_ptr<struct NvFSR> m_fsr;
std::shared_ptr<struct MaEnvironment> m_environment;
std::shared_ptr<struct MaShadowMap> m_shadow_map;
std::shared_ptr<struct MaContactShadows> m_contact_shadows;
std::shared_ptr<struct Bloom> m_bloom;
std::shared_ptr<struct Sky> m_sky;
std::shared_ptr<struct MaAutoExposure> m_auto_exposure;
std::shared_ptr<struct MaConfig> m_config;
struct MaConfig *GetConfig() const { return m_config.get(); }
static struct MaConfig *Config();
struct GeometryBounds {
glm::dvec3 m_origin;
glm::dvec3 m_extent;
};
GeometryBounds GetGeometryBounds(gfx::geometry_handle const &handle) const;
size_t GetCurrentFrame() const;
void DebugUi();
glm::dmat4 m_previous_view_proj;
glm::dmat4 m_previous_view;
bool m_debug_ui_active;
nvrhi::CommandListHandle m_command_list;
nvrhi::EventQueryHandle m_upload_event_query;
std::mutex m_mtx_context_lock;
glm::dvec3 m_previous_env_position;
uint64_t m_previous_env_frame;
using section_sequence = std::vector<scene::basic_section *>;
using distancecell_pair = std::pair<double, scene::basic_cell *>;
using cell_sequence = std::vector<distancecell_pair>;
struct RenderPass {
RenderPassType m_type;
double m_draw_range;
uint64_t m_frame_index;
bool m_draw_shapes = true;
bool m_draw_lines = true;
bool m_draw_tanimobj = true;
bool m_draw_dynamic = true;
bool m_draw_track = true;
bool m_draw_instances = true;
bool m_sort_batches = true;
bool m_sort_transparents = true;
nvrhi::CommandListHandle m_command_list_draw;
nvrhi::CommandListHandle m_command_list_preparation;
nvrhi::FramebufferHandle m_framebuffer;
nvrhi::ViewportState m_viewport_state;
glm::dmat4 m_projection;
glm::dvec3 m_origin;
glm::dmat3 m_transform;
glm::dmat3 m_history_transform;
double ScreenRadius(const glm::dvec3 &origin, double radius) const {
glm::dvec4 ndc =
m_projection *
glm::dvec4(0., radius, -glm::length(origin - m_origin), 1.);
return glm::abs(ndc.y / ndc.w);
}
double ScreenRadius(double sqr_distance, double radius) const {
glm::dvec4 ndc =
m_projection * glm::dvec4(0., radius, -glm::sqrt(sqr_distance), 1.);
return glm::abs(ndc.y / ndc.w);
}
bool NeedsHistory() const { return m_type == RenderPassType::Deferred; }
bool SphereVisible(const glm::dvec3 &origin, double radius) const {
return !m_frustum_tester ||
m_frustum_tester->IntersectSphere(origin - m_origin, radius);
}
bool BoxVisible(const glm::dvec3 &origin, const glm::dvec3 &extent) const {
return !m_frustum_tester ||
m_frustum_tester->IntersectBox(origin - m_origin, extent);
}
bool BoxVisible(glm::dmat4x3 transform, const glm::dvec3 &origin,
const glm::dvec3 &extent) const {
transform[3] -= m_origin;
return !m_frustum_tester ||
m_frustum_tester->IntersectBox(transform, origin, extent);
}
bool Visible(scene::bounding_area const &area) const {
return SphereVisible(area.center, area.radius);
}
bool Visible(TDynamicObject *dynamic) const {
return SphereVisible(dynamic->GetPosition(), dynamic->radius() * 1.25);
}
const IFrustumTester *m_frustum_tester;
};
struct GeometryChunk {
std::vector<gfx::basic_vertex> m_vertices;
std::vector<gfx::basic_index> m_indices;
size_t m_vertex_offset;
size_t m_index_offset;
size_t m_vertex_count;
size_t m_index_count;
bool m_indexed;
bool m_is_uptodate = false;
uint64_t m_last_frame_requested = static_cast<uint64_t>(-1);
uint64_t m_last_frame_updated = static_cast<uint64_t>(-1);
uint64_t m_last_frame_uploaded = static_cast<uint64_t>(-1);
nvrhi::BufferHandle m_index_buffer;
nvrhi::BufferHandle m_vertex_buffer;
nvrhi::EventQueryHandle m_chunk_ready_handle;
std::future<void> m_chunk_upload;
glm::vec3 m_origin;
glm::vec3 m_extent;
glm::dvec3 m_last_position_requested;
std::function<bool(gfx::vertex_array &, int)> m_replace_impl;
void UpdateBounds();
};
struct GeometryBank {
nvrhi::BufferHandle m_index_buffer;
nvrhi::BufferHandle m_vertex_buffer;
size_t m_vertex_count;
size_t m_index_count;
std::vector<GeometryChunk> m_chunks;
bool m_is_uptodate = false;
nvrhi::EventQueryHandle m_uploaded_query;
uint64_t m_last_frame_requested = static_cast<uint64_t>(-1);
};
std::vector<GeometryBank> m_geometry_banks;
nvrhi::BufferHandle m_culling_view_data;
struct GeometryBatch {
struct CullingViewData {
glm::vec4 m_frustum_planes[6];
glm::mat4 m_projection;
};
struct CullingPushConstants {
glm::vec3 m_origin;
float m_instance_radius;
float m_min_radius;
float m_max_radius;
uint32_t m_num_instances;
};
struct CullingCommandBuffer {
uint32_t m_index_count_per_instance;
uint32_t m_instance_count;
uint32_t m_start_index_location;
int32_t m_base_vertex_location;
uint32_t m_start_instance_location;
};
gfx::geometry_handle m_geometry;
material_handle m_material;
std::vector<glm::dmat4> m_transforms;
glm::vec3 m_diffuse;
glm::dvec3 m_origin;
glm::dvec3 m_extent;
glm::dvec3 m_instance_origin;
nvrhi::BufferHandle m_static_instance_buffer;
nvrhi::BufferHandle m_instance_buffer;
nvrhi::BufferHandle m_static_command_buffer;
nvrhi::BufferHandle m_command_buffer;
nvrhi::BindingSetHandle m_culling_binding_set;
double m_sqr_distance_max;
double m_sqr_distance_min;
double m_instance_radius;
std::string m_name;
};
std::vector<GeometryBatch> m_geometry_batches;
QuadTree m_batch_quadtree;
struct TrackBatch {
std::string m_name;
glm::dvec3 m_origin;
glm::dvec3 m_extent;
glm::dvec3 m_geometry_origin;
glm::vec3 m_diffuse;
std::vector<nvrhi::DrawArguments> m_draw_commands;
gfx::geometry_handle m_geometry;
material_handle m_material;
nvrhi::BufferHandle m_command_buffer;
struct BufferRegion {
size_t m_start_vertex;
size_t m_vertex_count;
size_t m_start_index;
size_t m_index_count;
};
std::vector<BufferRegion> m_regions;
std::vector<bool> m_regions_need_update;
};
bool TrackBatchPartialUpdate(size_t batch_index, size_t track_index,
gfx::vertex_array &vertices);
std::vector<TrackBatch> m_track_batches;
QuadTree m_track_quadtree;
struct Shape {
std::string m_name;
glm::dvec3 m_origin;
glm::dvec3 m_extent;
glm::dvec3 m_geometry_origin;
gfx::geometry_handle m_geometry;
bool m_draw_in_opaque;
bool m_draw_in_transparent;
material_handle m_material;
glm::vec3 m_diffuse;
};
std::vector<Shape> m_shapes;
QuadTree m_shape_quadtree;
struct Line {
std::string m_name;
glm::dvec3 m_origin;
glm::dvec3 m_extent;
glm::dvec3 m_geometry_origin;
gfx::geometry_handle m_geometry;
float m_line_width;
glm::vec3 m_color;
float m_metalness;
float m_roughness;
};
std::vector<Line> m_lines;
QuadTree m_line_quadtree;
struct Renderable {
struct RenderableItem {
bool m_render_in_forward = false;
glm::dmat4x3 m_transform;
glm::dmat4x3 m_history_transform;
gfx::geometry_handle m_geometry;
material_handle m_material;
double m_sqr_distance_max;
double m_sqr_distance_min;
float m_opacity;
float m_selfillum;
glm::vec3 m_diffuse;
};
struct SpotLight {
glm::vec3 m_color;
glm::dvec3 m_origin;
glm::vec3 m_direction;
float m_radius;
float m_cos_inner_cone;
float m_cos_outer_cone;
};
std::vector<RenderableItem> m_items;
std::vector<SpotLight> m_spotlights;
bool m_render_in_deferred = false;
bool m_render_in_forward = false;
void Reset() {
m_items.clear();
m_spotlights.clear();
m_render_in_deferred = false;
m_render_in_forward = false;
}
};
struct AnimObj {
std::string m_name;
glm::dvec3 m_origin;
glm::dvec3 m_extent;
bool m_render_in_deferred;
bool m_render_in_forward;
bool m_animatable;
bool m_was_once_animated = false;
double m_sqr_distance_max;
double m_sqr_distance_min;
double m_distance;
TAnimModel *m_model;
Renderable m_renderable;
};
std::vector<AnimObj> m_animateds;
QuadTree m_animated_quadtree;
struct DynObj {
TDynamicObject *m_dynamic;
glm::dvec3 m_location;
double m_radius;
double m_distance;
Renderable m_renderable;
Renderable m_renderable_kabina;
};
uint32_t m_dynamic_with_kabina;
std::vector<DynObj> m_dynamics;
std::vector<uint32_t> m_renderable_dynamics;
std::vector<Renderable::SpotLight> m_renderable_spotlights;
struct Cell {
scene::basic_cell *m_cell;
glm::dvec3 m_origin;
glm::dvec3 m_extent;
};
std::vector<Cell> m_cells;
QuadTree m_cell_quadtree;
void GatherSpotLights(const RenderPass &pass);
void UpdateGeometry(gfx::geometry_handle handle, const RenderPass &pass,
bool *p_is_uploading = nullptr);
section_sequence m_sectionqueue;
cell_sequence m_cellqueue;
struct MaterialCache;
struct MaterialTemplate : public MaResourceRegistry {
struct TextureBinding {
std::string m_name;
std::string m_sampler_name;
int m_hint;
texture_handle m_default_texture;
};
nvrhi::static_vector<TextureBinding, 8> m_texture_bindings;
std::array<nvrhi::GraphicsPipelineHandle, Constants::NumMaterialPipelines()>
m_pipelines;
std::array<MaResourceMappingSet, Constants::NumMaterialPipelines()>
m_resource_mappings;
std::string m_name;
std::string m_masked_shadow_texture;
nvrhi::BindingLayoutHandle m_binding_layout;
virtual bool CreateBindingSet(size_t pipeline_index, MaterialCache &cache);
[[nodiscard]] nvrhi::IGraphicsPipeline *GetPipeline(
RenderPassType pass_type, DrawType draw_type, bool alpha_masked) const;
virtual void Init(const YAML::Node &conf);
MaterialTemplate(const std::string &name, NvRenderer *renderer)
: m_name(name), MaResourceRegistry(renderer), m_renderer(renderer) {}
NvRenderer *m_renderer;
};
struct MaterialCache : public IMaterial, public MaResourceRegistry {
explicit MaterialCache(MaterialTemplate *my_template)
: MaResourceRegistry(my_template), m_template(my_template) {}
void Init() { InitResourceRegistry(); }
std::array<uint64_t, Constants::NumMaterialPipelines()>
m_last_texture_updates{};
std::string m_name;
std::array<nvrhi::IGraphicsPipeline *, Constants::NumMaterialPipelines()>
m_pipelines;
std::array<nvrhi::BindingSetHandle, Constants::NumMaterialPipelines()>
m_binding_sets;
std::array<texture_handle, 8> m_texture_handles;
nvrhi::BindingSetHandle m_binding_set;
nvrhi::BindingSetHandle m_binding_set_cubemap;
nvrhi::BindingSetHandle m_binding_set_shadow;
glm::dvec3 m_last_position_requested;
MaterialTemplate *m_template;
bool m_masked_shadow;
int m_shadow_rank;
uint64_t m_last_frame_requested = 0;
std::optional<float> m_opacity;
glm::dvec2 m_size;
[[nodiscard]] bool ShouldRenderInPass(const RenderPass &pass) const;
void finalize(bool Loadnow) override {}
bool update() override { return false; }
[[nodiscard]] float get_or_guess_opacity() const override {
if (m_opacity.has_value()) return *m_opacity;
if (m_masked_shadow)
return 0.0f;
else
return 0.5f;
}
bool is_translucent() const override { return m_masked_shadow; }
glm::vec2 GetSize() const override { return m_size; }
std::string GetName() const override { return m_name; }
std::optional<float> GetSelfillum() const override { return std::nullopt; }
texture_handle GetTexture(int slot) const override { return m_texture_handles[slot]; };
int GetShadowRank() const override { return m_shadow_rank; }
};
std::unordered_map<std::string, material_handle> m_material_map;
std::vector<MaterialCache> m_material_cache;
bool m_scene_initialized = false;
bool m_envir_can_filter = false;
std::array<nvrhi::InputLayoutHandle, static_cast<size_t>(DrawType::Num)>
m_input_layouts;
nvrhi::InputLayoutHandle GetInputLayout(DrawType draw_type);
struct DrawConstants {
glm::mat4 m_jittered_projection;
glm::mat4 m_projection;
glm::mat4 m_projection_history;
};
struct CubeDrawConstants {
glm::mat4 m_face_projection;
};
struct PushConstantsDraw {
glm::mat3x4 m_modelview;
glm::mat3x4 m_modelview_history;
glm::vec3 m_diffuse;
float m_alpha_threshold;
float m_alpha_mult;
float m_selfillum;
};
typedef PushConstantsDraw PushConstantsCubemap;
struct PushConstantsLine {
glm::mat3x4 m_modelview;
glm::mat3x4 m_modelview_history;
glm::vec3 m_color;
float m_line_weight;
float m_metalness;
float m_roughness;
};
struct PushConstantsShadow {
glm::mat4 m_modelviewprojection;
float m_alpha_threshold;
};
bool InitMaterials();
std::unordered_map<std::string_view, std::shared_ptr<MaterialTemplate>>
m_material_templates;
nvrhi::FramebufferHandle m_framebuffer_forward;
nvrhi::BindingLayoutHandle m_binding_layout_drawconstants;
nvrhi::BindingLayoutHandle m_binding_layout_cubedrawconstants;
nvrhi::BindingLayoutHandle m_binding_layout_shadowdrawconstants;
nvrhi::BindingLayoutHandle m_binding_layout_forward;
nvrhi::BindingSetHandle m_binding_set_drawconstants;
nvrhi::BindingSetHandle m_binding_set_cubedrawconstants;
nvrhi::BindingSetHandle m_binding_set_shadowdrawconstants;
nvrhi::BindingSetHandle m_binding_set_line;
std::array<nvrhi::BindingSetHandle, 2> m_binding_set_forward;
nvrhi::BufferHandle m_cubedrawconstant_buffer;
nvrhi::BufferHandle m_drawconstant_buffer;
std::array<nvrhi::ShaderHandle, Constants::NumDrawTypes()> m_vertex_shader;
std::array<nvrhi::ShaderHandle, Constants::NumDrawTypes()>
m_vertex_shader_cubemap;
std::array<nvrhi::ShaderHandle, Constants::NumDrawTypes()>
m_vertex_shader_shadow;
nvrhi::BindingLayoutHandle m_binding_layout_shadow_masked;
nvrhi::ShaderHandle m_pixel_shader_shadow_masked;
std::array<nvrhi::InputLayoutHandle, Constants::NumDrawTypes()>
m_input_layout;
std::array<nvrhi::GraphicsPipelineHandle, Constants::NumShadowPipelines()>
m_pso_shadow;
nvrhi::GraphicsPipelineHandle m_pso_line;
std::condition_variable m_cv_next_frame;
glm::mat4 m_previous_projection;
struct DrawCallStat {
void Reset() {
m_drawcalls = 0;
m_triangles = 0;
}
int m_drawcalls = 0;
int m_triangles = 0;
void Draw(nvrhi::DrawArguments const &args) {
++m_drawcalls;
m_triangles += args.vertexCount * args.instanceCount / 3;
}
};
DrawCallStat m_drawcalls_shape{};
DrawCallStat m_drawcalls_line{};
DrawCallStat m_drawcalls_tanimobj{};
DrawCallStat m_drawcalls_dynamic{};
DrawCallStat m_drawcalls_track{};
DrawCallStat m_drawcalls_instances{};
DrawCallStat *m_drawcall_counter = nullptr;
bool m_draw_shapes = true;
bool m_draw_lines = true;
bool m_draw_tanimobj = true;
bool m_draw_dynamic = true;
bool m_draw_track = true;
bool m_draw_instances = true;
bool m_sort_batches = true;
bool m_sort_transparents = true;
bool m_pause_animations = false;
std::unordered_set<const void *> m_batched_instances;
static const void *CombinePointers(const void *a, const void *b) {
union {
struct {
uint32_t a;
uint32_t b;
};
const void *c;
} val{};
val.a = static_cast<uint32_t>(reinterpret_cast<uint64_t>(a));
val.b = static_cast<uint32_t>(reinterpret_cast<uint64_t>(b));
return val.c;
}
static double SqrDistToScreenSize(double sqr_dist, double radius) {
double dist = glm::sqrt(sqr_dist);
glm::dmat4 std_projection =
glm::perspectiveRH_ZO(glm::radians(45.), 1., .1, 2e5);
glm::dvec4 ndc = std_projection * glm::dvec4(0., radius, -dist, 1.);
return glm::abs(ndc.y / ndc.w);
};
void UpdateDrawData(const RenderPass &pass, const glm::dmat4 &transform,
const glm::dmat4 &history_transform,
float opacity_threshold, float opacity_mult,
float selfillum, const glm::vec3& diffuse);
void UpdateDrawDataLine(const RenderPass &pass, const glm::dmat4 &transform,
const glm::dmat4 &history_transform,
const Line &line);
void BindConstants(const RenderPass &pass, nvrhi::GraphicsState &gfx_state);
bool BindMaterial(material_handle handle, DrawType draw_type,
const RenderPass &pass, nvrhi::GraphicsState &gfx_state,
float &alpha_threshold);
bool BindLineMaterial(DrawType draw_type, const RenderPass &pass,
nvrhi::GraphicsState &gfx_state);
bool BindGeometry(gfx::geometry_handle handle, const RenderPass &pass,
nvrhi::GraphicsState &gfx_state, nvrhi::DrawArguments &args,
bool &indexed);
void GatherModelsForBatching();
void GatherTracksForBatching();
void GatherShapesForBatching();
void GatherAnimatedsForBatching();
void GatherCellsForAnimation();
void GatherDynamics();
void RenderBatches(const RenderPass &pass);
void RenderTracks(const RenderPass &pass);
void RenderShapes(const RenderPass &pass);
void RenderLines(const RenderPass &pass);
void RenderAnimateds(const RenderPass &pass);
void RenderKabina(const RenderPass &pass);
void Render(const Renderable &renderable, const RenderPass &pass,
const glm::dvec3 &history_origin, const double distance);
void Animate(const glm::dvec3 &origin, double radius, uint64_t frame);
void Render(TAnimModel *Instance, const RenderPass &pass);
bool Render(TDynamicObject *Dynamic, const RenderPass &pass);
bool Render(TModel3d *Model, material_data const *Material,
float const Squaredistance, const RenderPass &pass);
void Render(TSubModel *Submodel, const RenderPass &pass);
bool Render_cab(TDynamicObject const *Dynamic, const RenderPass &pass);
void Animate(AnimObj &instance, const glm::dvec3 &origin, uint64_t frame);
void Animate(DynObj &instance, const glm::dvec3 &origin, uint64_t frame);
void Animate(Renderable &renderable, TModel3d *Model,
const material_data *material, double distance,
const glm::dvec3 &position, const glm::vec3 &angle,
uint64_t frame);
void Animate(Renderable &renderable, TModel3d *Model,
const material_data *material, double distance,
const glm::dmat4 &transform, uint64_t frame);
void Animate(Renderable &renderable, TSubModel *Submodel,
const material_data *material, double distance,
const glm::dmat4 &transform, uint64_t frame);
#if LIBMANUL_WITH_D3D12
bool InitForD3D12(GLFWwindow *Window);
#endif
#if LIBMANUL_WITH_VULKAN
bool InitForVulkan(GLFWwindow *Window);
#endif
};

View File

@@ -0,0 +1,18 @@
#pragma once
namespace RendererEnums {
enum class RenderPassType {
Deferred,
Forward,
CubeMap,
ShadowMap,
Num,
RendererWarmUp
};
enum class DrawType : uint8_t { Model, InstancedModel, Num };
}

View File

@@ -0,0 +1,96 @@
#pragma once
#include <array>
#include <glm/glm.hpp>
#include <glm/gtx/component_wise.hpp>
#include <memory>
#include <vector>
struct QuadTreeLeave {
QuadTreeLeave(const glm::dvec3& origin, const glm::dvec3& extent,
uint32_t index)
: m_origin(origin), m_extent(extent), m_index(index) {}
glm::dvec3 m_origin;
glm::dvec3 m_extent;
uint32_t m_index;
};
struct QuadTreeBuilder {
struct Node {
std::array<std::shared_ptr<Node>, 4> m_children;
std::vector<QuadTreeLeave> m_leaves;
glm::dvec2 m_min;
glm::dvec2 m_max;
glm::dvec3 m_actual_min;
glm::dvec3 m_actual_max;
[[nodiscard]] int GetChildIndex(glm::dvec3 origin) const {
glm::dvec2 mid = .5 * (m_min + m_max);
return (origin.x < mid.x ? 0 : 1) | (origin.z < mid.y ? 0 : 2);
}
[[nodiscard]] glm::dvec4 GetChildBounds(int index) const {
glm::dvec2 mid = .5 * (m_min + m_max);
return glm::dvec4{
index & 1 ? mid.x : m_min.x, index & 2 ? mid.y : m_min.y,
index & 1 ? m_max.x : mid.x, index & 2 ? m_max.y : mid.y};
}
Node(const double min_x, const double min_z, const double max_x,
const double max_z)
: m_min(min_x, min_z),
m_max(max_x, max_z),
m_actual_min(std::numeric_limits<double>::max()),
m_actual_max(-std::numeric_limits<double>::max()) {}
explicit Node(const glm::dvec4& minmax_xz)
: m_min(minmax_xz.x, minmax_xz.y),
m_max(minmax_xz.z, minmax_xz.w),
m_actual_min(std::numeric_limits<double>::max()),
m_actual_max(-std::numeric_limits<double>::max()) {}
[[nodiscard]] int NumChildren() const {
int result = 0;
for (const auto& child : m_children) {
if (child) ++result;
}
return result;
}
[[nodiscard]] const Node* FirstChild() const {
for (const auto& child : m_children) {
if (child) return child.get();
}
return nullptr;
}
void Insert(uint32_t index, glm::dvec3 min_, glm::dvec3 max_, int level);
};
std::shared_ptr<Node> m_root;
glm::dvec3 m_min;
glm::dvec3 m_max;
int m_max_levels;
QuadTreeBuilder(double min_x, double min_z, double max_x, double max_z);
void Insert(uint32_t index, glm::dvec3 min, glm::dvec3 max) const;
};
struct QuadTree {
struct Node {
glm::dvec3 m_origin;
glm::dvec3 m_extent;
uint32_t m_first_child;
uint32_t m_child_count;
uint32_t m_first_leave;
uint32_t m_leave_count;
};
std::vector<Node> m_nodes;
std::vector<QuadTreeLeave> m_leaves;
void Build(const QuadTreeBuilder& builder);
void Add(Node& node, const QuadTreeBuilder::Node& item);
template <typename Tester, typename CallbackFn>
void ForEach(const Tester& tester, CallbackFn fn, size_t subtree_index = 0) {
const auto& subtree = m_nodes[subtree_index];
if (!tester.BoxVisible(subtree.m_origin, subtree.m_extent)) return;
for (int i = 0; i < subtree.m_leave_count; ++i) {
const auto& leave = m_leaves[subtree.m_first_leave + i];
if (!tester.BoxVisible(leave.m_origin, leave.m_extent)) continue;
fn(leave.m_index);
}
for (int i = 0; i < subtree.m_child_count; ++i) {
ForEach(tester, fn, subtree.m_first_child + i);
}
}
};

View File

@@ -0,0 +1,110 @@
#pragma once
#include <nvrhi/nvrhi.h>
#include <entt/entt.hpp>
#include <glm/glm.hpp>
class NvTextureManager;
class MaResourceRegistry;
struct MaResourceMapping {
int m_slot;
int m_size;
entt::hashed_string m_key;
nvrhi::ResourceType m_type;
#define MA_RESOURCE_MAPPING_INITIALIZER(type) \
template <typename KeyType> \
static MaResourceMapping type(int slot, const KeyType& key) { \
MaResourceMapping mapping{}; \
mapping.m_slot = slot; \
mapping.m_key = static_cast<entt::hashed_string>(key); \
mapping.m_type = nvrhi::ResourceType::type; \
return mapping; \
}
template <typename KeyType>
static MaResourceMapping Texture_SRV(int slot, const KeyType& key) {
MaResourceMapping mapping{};
mapping.m_slot = slot;
mapping.m_key = static_cast<entt::hashed_string>(key);
mapping.m_type = nvrhi::ResourceType::Texture_SRV;
return mapping;
}
MA_RESOURCE_MAPPING_INITIALIZER(Texture_UAV)
MA_RESOURCE_MAPPING_INITIALIZER(TypedBuffer_SRV)
MA_RESOURCE_MAPPING_INITIALIZER(TypedBuffer_UAV)
MA_RESOURCE_MAPPING_INITIALIZER(StructuredBuffer_SRV)
MA_RESOURCE_MAPPING_INITIALIZER(StructuredBuffer_UAV)
MA_RESOURCE_MAPPING_INITIALIZER(RawBuffer_SRV)
MA_RESOURCE_MAPPING_INITIALIZER(RawBuffer_UAV)
MA_RESOURCE_MAPPING_INITIALIZER(ConstantBuffer)
MA_RESOURCE_MAPPING_INITIALIZER(VolatileConstantBuffer)
MA_RESOURCE_MAPPING_INITIALIZER(Sampler)
MA_RESOURCE_MAPPING_INITIALIZER(RayTracingAccelStruct)
#undef MA_RESOURCE_MAPPING_INITIALIZER
static MaResourceMapping PushConstants(const int slot, const int size) {
MaResourceMapping mapping{};
mapping.m_slot = slot;
mapping.m_type = nvrhi::ResourceType::PushConstants;
mapping.m_size = size;
return mapping;
}
template <typename PushType>
static MaResourceMapping PushConstants(const int slot) {
MaResourceMapping mapping{};
mapping.m_slot = slot;
mapping.m_type = nvrhi::ResourceType::PushConstants;
mapping.m_size = sizeof(PushType);
return mapping;
}
};
struct MaResourceMappingSet {
nvrhi::static_vector<MaResourceMapping, 128> m_mappings;
MaResourceMappingSet& Add(const MaResourceMapping& mapping);
void ToBindingLayout(nvrhi::BindingLayoutDesc& desc) const;
bool ToBindingSet(nvrhi::BindingSetDesc& desc,
const MaResourceRegistry* registry,
const nvrhi::BindingLayoutDesc* reference_layout = nullptr) const;
};
struct MaResourceView {
nvrhi::IResource* m_resource = nullptr;
nvrhi::ResourceType m_type = nvrhi::ResourceType::None;
[[nodiscard]] bool IsValid() const {
return m_resource && m_type != nvrhi::ResourceType::None;
}
};
class MaResourceRegistry {
public:
[[nodiscard]] MaResourceView GetResource(
const entt::hashed_string& key, nvrhi::ResourceType requested_type) const;
[[nodiscard]] NvTextureManager* GetTextureManager() const;
[[nodiscard]] uint64_t GetLastStreamingTextureUpdate() const;
void UpdateLastStreamingTextureUse(const glm::dvec3& location) const;
void RegisterResource(bool global, const entt::hashed_string& key,
nvrhi::IResource* resource, nvrhi::ResourceType type);
void RegisterTexture(const entt::hashed_string& key, size_t handle);
void RegisterResource(bool global, const char* key,
nvrhi::IResource* resource, nvrhi::ResourceType type);
void RegisterTexture(const char* key, size_t handle);
virtual ~MaResourceRegistry() = default;
protected:
explicit MaResourceRegistry(MaResourceRegistry* parent);
void InitResourceRegistry();
private:
[[nodiscard]] MaResourceView GetResourceLocal(
const entt::hashed_string& key, nvrhi::ResourceType requested_type) const;
MaResourceRegistry* m_parent = nullptr;
std::shared_ptr<NvTextureManager> m_texture_manager = nullptr;
entt::dense_map<entt::id_type, MaResourceView> m_resource_views{};
entt::dense_map<entt::id_type, size_t> m_streaming_textures{};
};

View File

@@ -0,0 +1,142 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2016-2021, Intel Corporation
//
// SPDX-License-Identifier: MIT
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Author(s): Lukasz, Migas (Lukasz.Migas@intel.com)
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// See https://github.com/GameTechDev/TAA
#ifndef __INTEL_TAA_H__
#define __INTEL_TAA_H__
#define INTEL_TAA_NUM_THREADS_X 8
#define INTEL_TAA_NUM_THREADS_Y 8
#ifdef __cplusplus
#include <cmath>
namespace IntelTAA
{
// cpp<->hlsl mapping
typedef glm::mat4 Matrix4x4;
typedef glm::vec4 Vector4;
typedef glm::vec3 Vector3;
typedef glm::vec2 Vector2;
typedef glm::ivec2 Vector2i;
typedef glm::ivec4 Vector4i;
typedef glm::uvec4 Vector4ui;
typedef unsigned int uint32;
#else // #ifdef __cplusplus
// cpp<->hlsl mapping
#define Matrix4x4 float4x4
#define Vector4 float4
#define Vector3 float3
#define Vector2 float2
#define Vector2i int2
#define Vector4i int4
#define Vector4ui uint4
#define uint32 uint
#endif
// Constant buffer
struct FTAAResolve
{
Vector4 Resolution; //width, height, 1/width, 1/height
Vector2 Jitter;
uint32 FrameNumber;
uint32 DebugFlags; // AllowLongestVelocityVector | AllowNeighbourhoodSampling | AllowYCoCg | AllowVarianceClipping | AllowBicubicFilter | AllowDepthThreshold | MarkNoHistoryPixels
float LerpMul;
float LerpPow;
float VarClipGammaMin; // (MIN_VARIANCE_GAMMA)
float VarClipGammaMax; // (MAX_VARIANCE_GAMMA)
float PreExposureNewOverOld;
float Padding0;
float Padding1;
float Padding2;
};
#ifndef __cplusplus
typedef float fp32_t;
typedef float2 fp32_t2;
typedef float3 fp32_t3;
typedef float4 fp32_t4;
#if 0 == USE_FP16
typedef float fp16_t;
typedef float2 fp16_t2;
typedef float3 fp16_t3;
typedef float4 fp16_t4;
typedef int i16_t;
typedef int2 i16_t2;
typedef int3 i16_t3;
typedef int4 i16_t4;
typedef uint ui16_t;
typedef uint2 ui16_t2;
typedef uint3 ui16_t3;
typedef uint4 ui16_t4;
#else
typedef float16_t fp16_t;
typedef float16_t2 fp16_t2;
typedef float16_t3 fp16_t3;
typedef float16_t4 fp16_t4;
typedef int16_t i16_t;
typedef int16_t2 i16_t2;
typedef int16_t3 i16_t3;
typedef int16_t4 i16_t4;
typedef uint16_t ui16_t;
typedef uint16_t2 ui16_t2;
typedef uint16_t3 ui16_t3;
typedef uint16_t4 ui16_t4;
#endif
// this is all that the effect requires
struct TAAParams
{
FTAAResolve CBData;
// in current MiniEngine's format
Texture2D<fp16_t3> VelocityBuffer;
// Current colour buffer - rgb used
Texture2D<fp16_t3> ColourTexture;
// Stored temporal antialiased pixel - .a should be sufficient enough to handle weight stored as float [0.5f, 1.f)
Texture2D<fp32_t4> HistoryTexture;
// Current linear depth buffer - used only when USE_DEPTH_THRESHOLD is set
Texture2D<fp16_t> DepthBuffer;
// Previous linear frame depth buffer - used only when USE_DEPTH_THRESHOLD is set
Texture2D<fp16_t> PrvDepthBuffer;
// Antialiased colour buffer (used in a next frame as the HistoryTexture)
RWTexture2D<fp16_t4> OutTexture;
// Samplers
SamplerState MinMagLinearMipPointClamp;
SamplerState MinMagMipPointClamp;
};
#endif // #ifndef __cplusplus
#ifdef __cplusplus
} // namespace
#endif // #ifdef __cplusplus
#endif // #ifndef __INTEL_TAA_H__

View File

@@ -0,0 +1,263 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2016-2021, Intel Corporation
//
// SPDX-License-Identifier: MIT
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// XeGTAO is based on GTAO/GTSO "Jimenez et al. / Practical Real-Time Strategies for Accurate Indirect Occlusion",
// https://www.activision.com/cdn/research/Practical_Real_Time_Strategies_for_Accurate_Indirect_Occlusion_NEW%20VERSION_COLOR.pdf
//
// Implementation: Filip Strugar (filip.strugar@intel.com), Steve Mccalla <stephen.mccalla@intel.com> (\_/)
// Version: 1.02 (='.'=)
// Details: https://github.com/GameTechDev/XeGTAO (")_(")
//
// Version history:
// 1.00 (2021-08-09): Initial release
// 1.01 (2021-09-02): Fix for depth going to inf for 'far' depth buffer values that are out of fp16 range
// 1.02 (2021-09-03): More fast_acos use and made final horizon cos clamping optional (off by default): 3-4% perf boost
// 1.10 (2021-09-03): Added a couple of heuristics to combat over-darkening errors in certain scenarios
// 1.20 (2021-09-06): Optional normal from depth generation is now a standalone pass: no longer integrated into
// main XeGTAO pass to reduce complexity and allow reuse; also quality of generated normals improved
// 1.21 (2021-09-28): Replaced 'groupshared'-based denoiser with a slightly slower multi-pass one where a 2-pass new
// equals 1-pass old. However, 1-pass new is faster than the 1-pass old and enough when TAA enabled.
// 1.22 (2021-09-28): Added 'XeGTAO_' prefix to all local functions to avoid name clashes with various user codebases.
// 1.30 (2021-10-10): Added support for directional component (bent normals).
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifndef __XE_GTAO_TYPES_H__
#define __XE_GTAO_TYPES_H__
#ifdef __cplusplus
#include <cmath>
namespace XeGTAO
{
// cpp<->hlsl mapping
struct Matrix4x4 { float m[16]; };
struct Vector3 { float x,y,z; };
struct Vector2 { float x,y; };
struct Vector2i { int x,y; };
typedef unsigned int uint;
#else // #ifdef __cplusplus
// cpp<->hlsl mapping
#define Matrix4x4 float4x4
#define Vector3 float3
#define Vector2 float2
#define Vector2i int2
#endif
// Global consts that need to be visible from both shader and cpp side
#define XE_GTAO_DEPTH_MIP_LEVELS 5 // this one is hard-coded to 5 for now
#define XE_GTAO_NUMTHREADS_X 8 // these can be changed
#define XE_GTAO_NUMTHREADS_Y 8 // these can be changed
struct GTAOConstants
{
Vector2i ViewportSize;
Vector2 ViewportPixelSize; // .zw == 1.0 / ViewportSize.xy
Vector2 DepthUnpackConsts;
Vector2 CameraTanHalfFOV;
Vector2 NDCToViewMul;
Vector2 NDCToViewAdd;
Vector2 NDCToViewMul_x_PixelSize;
float EffectRadius; // world (viewspace) maximum size of the shadow
float EffectFalloffRange;
float RadiusMultiplier;
float Padding0;
float FinalValuePower;
float DenoiseBlurBeta;
float SampleDistributionPower;
float ThinOccluderCompensation;
float DepthMIPSamplingOffset;
int NoiseIndex; // frameIndex % 64 if using TAA or 0 otherwise
};
// This is used only for the development (ray traced ground truth).
struct ReferenceRTAOConstants
{
float TotalRaysLength ; // similar to Radius from GTAO
float Albedo ; // the assumption on the average material albedo
int MaxBounces ; // how many rays to recurse before stopping
int AccumulatedFrames ; // how many frames have we accumulated so far (after resetting/clearing). If 0 - this is the first.
int AccumulateFrameMax ; // how many frames are we aiming to accumulate; stop when we hit!
int Padding0;
int Padding1;
int Padding2;
#ifdef __cplusplus
ReferenceRTAOConstants( ) { TotalRaysLength = 1.0f; Albedo = 0.0f; MaxBounces = 1; AccumulatedFrames = 0; AccumulateFrameMax = 0; }
#endif
};
#ifndef XE_GTAO_USE_DEFAULT_CONSTANTS
#define XE_GTAO_USE_DEFAULT_CONSTANTS 1
#endif
// some constants reduce performance if provided as dynamic values; if these constants are not required to be dynamic and they match default values,
// set XE_GTAO_USE_DEFAULT_CONSTANTS and the code will compile into a more efficient shader
#define XE_GTAO_DEFAULT_RADIUS_MULTIPLIER (1.457f ) // allows us to use different value as compared to ground truth radius to counter inherent screen space biases
#define XE_GTAO_DEFAULT_FALLOFF_RANGE (0.615f ) // distant samples contribute less
#define XE_GTAO_DEFAULT_SAMPLE_DISTRIBUTION_POWER (2.0f ) // small crevices more important than big surfaces
#define XE_GTAO_DEFAULT_THIN_OCCLUDER_COMPENSATION (0.0f ) // the new 'thickness heuristic' approach
#define XE_GTAO_DEFAULT_FINAL_VALUE_POWER (2.2f ) // modifies the final ambient occlusion value using power function - this allows some of the above heuristics to do different things
#define XE_GTAO_DEFAULT_DEPTH_MIP_SAMPLING_OFFSET (3.30f ) // main trade-off between performance (memory bandwidth) and quality (temporal stability is the first affected, thin objects next)
#define XE_GTAO_OCCLUSION_TERM_SCALE (1.5f) // for packing in UNORM (because raw, pre-denoised occlusion term can overshoot 1 but will later average out to 1)
// From https://www.shadertoy.com/view/3tB3z3 - except we're using R2 here
#define XE_HILBERT_LEVEL 6U
#define XE_HILBERT_WIDTH ( (1U << XE_HILBERT_LEVEL) )
#define XE_HILBERT_AREA ( XE_HILBERT_WIDTH * XE_HILBERT_WIDTH )
inline uint HilbertIndex( uint posX, uint posY )
{
uint index = 0U;
for( uint curLevel = XE_HILBERT_WIDTH/2U; curLevel > 0U; curLevel /= 2U )
{
uint regionX = ( posX & curLevel ) > 0U;
uint regionY = ( posY & curLevel ) > 0U;
index += curLevel * curLevel * ( (3U * regionX) ^ regionY);
if( regionY == 0U )
{
if( regionX == 1U )
{
posX = uint( (XE_HILBERT_WIDTH - 1U) ) - posX;
posY = uint( (XE_HILBERT_WIDTH - 1U) ) - posY;
}
uint temp = posX;
posX = posY;
posY = temp;
}
}
return index;
}
#ifdef __cplusplus
struct GTAOSettings
{
int QualityLevel = 2; // 0: low; 1: medium; 2: high; 3: ultra
int DenoisePasses = 1; // 0: disabled; 1: sharp; 2: medium; 3: soft
float Radius = 0.5f; // [0.0, ~ ] World (view) space size of the occlusion sphere.
// auto-tune-d settings
float RadiusMultiplier = XE_GTAO_DEFAULT_RADIUS_MULTIPLIER;
float FalloffRange = XE_GTAO_DEFAULT_FALLOFF_RANGE;
float SampleDistributionPower = XE_GTAO_DEFAULT_SAMPLE_DISTRIBUTION_POWER;
float ThinOccluderCompensation = XE_GTAO_DEFAULT_THIN_OCCLUDER_COMPENSATION;
float FinalValuePower = XE_GTAO_DEFAULT_FINAL_VALUE_POWER;
float DepthMIPSamplingOffset = XE_GTAO_DEFAULT_DEPTH_MIP_SAMPLING_OFFSET;
};
template<class T> inline T clamp( T const & v, T const & min, T const & max ) { assert( max >= min ); if( v < min ) return min; if( v > max ) return max; return v; }
// If using TAA then set noiseIndex to frameIndex % 64 - otherwise use 0
inline void GTAOUpdateConstants( XeGTAO::GTAOConstants& consts, int viewportWidth, int viewportHeight, const XeGTAO::GTAOSettings & settings, const float projMatrix[16], bool rowMajor, unsigned int frameCounter )
{
consts.ViewportSize = { viewportWidth, viewportHeight };
consts.ViewportPixelSize = { 1.0f / (float)viewportWidth, 1.0f / (float)viewportHeight };
float depthLinearizeMul = (rowMajor)?(-projMatrix[3 * 4 + 2]):(-projMatrix[3 + 2 * 4]); // float depthLinearizeMul = ( clipFar * clipNear ) / ( clipFar - clipNear );
float depthLinearizeAdd = (rowMajor)?( projMatrix[2 * 4 + 2]):( projMatrix[2 + 2 * 4]); // float depthLinearizeAdd = clipFar / ( clipFar - clipNear );
// correct the handedness issue. need to make sure this below is correct, but I think it is.
if( depthLinearizeMul * depthLinearizeAdd < 0 )
depthLinearizeAdd = -depthLinearizeAdd;
consts.DepthUnpackConsts = { -depthLinearizeMul, depthLinearizeAdd };
float tanHalfFOVY = 1.0f / ((rowMajor)?(projMatrix[1 * 4 + 1]):(projMatrix[1 + 1 * 4])); // = tanf( drawContext.Camera.GetYFOV( ) * 0.5f );
float tanHalfFOVX = 1.0F / ((rowMajor)?(projMatrix[0 * 4 + 0]):(projMatrix[0 + 0 * 4])); // = tanHalfFOVY * drawContext.Camera.GetAspect( );
consts.CameraTanHalfFOV = { tanHalfFOVX, tanHalfFOVY };
consts.NDCToViewMul = { consts.CameraTanHalfFOV.x * 2.0f, consts.CameraTanHalfFOV.y * -2.0f };
consts.NDCToViewAdd = { consts.CameraTanHalfFOV.x * -1.0f, consts.CameraTanHalfFOV.y * 1.0f };
consts.NDCToViewMul_x_PixelSize = { consts.NDCToViewMul.x * consts.ViewportPixelSize.x, consts.NDCToViewMul.y * consts.ViewportPixelSize.y };
consts.EffectRadius = settings.Radius;
consts.EffectFalloffRange = settings.FalloffRange;
consts.DenoiseBlurBeta = (settings.DenoisePasses==0)?(1e4f):(1.2f); // high value disables denoise - more elegant & correct way would be do set all edges to 0
consts.RadiusMultiplier = settings.RadiusMultiplier;
consts.SampleDistributionPower = settings.SampleDistributionPower;
consts.ThinOccluderCompensation = settings.ThinOccluderCompensation;
consts.FinalValuePower = settings.FinalValuePower;
consts.DepthMIPSamplingOffset = settings.DepthMIPSamplingOffset;
consts.NoiseIndex = (settings.DenoisePasses>0)?(frameCounter % 64):(0);
consts.Padding0 = 0;
}
#ifdef IMGUI_API
inline bool GTAOImGuiSettings( XeGTAO::GTAOSettings & settings )
{
bool hadChanges = false;
ImGui::PushItemWidth( 120.0f );
ImGui::Text( "Performance/quality settings:" );
ImGui::Combo( "Quality Level", &settings.QualityLevel, "Low\0Medium\0High\0Ultra\00");
if( ImGui::IsItemHovered( ) ) ImGui::SetTooltip( "Higher quality settings use more samples per pixel but are slower" );
settings.QualityLevel = clamp( settings.QualityLevel , 0, 3 );
ImGui::Combo( "Denoising level", &settings.DenoisePasses, "Disabled\0Sharp\0Medium\0Soft\00");
if( ImGui::IsItemHovered( ) ) ImGui::SetTooltip( "The amount of edge-aware spatial denoise applied" );
settings.DenoisePasses = clamp( settings.DenoisePasses , 0, 3 );
ImGui::Text( "Visual settings:" );
settings.Radius = clamp( settings.Radius, 0.0f, 100000.0f );
hadChanges |= ImGui::InputFloat( "Effect radius", &settings.Radius , 0.05f, 0.0f, "%.2f" );
if( ImGui::IsItemHovered( ) ) ImGui::SetTooltip( "World (viewspace) effect radius\nExpected range: depends on the scene & requirements, anything from 0.01 to 1000+" );
settings.Radius = clamp( settings.Radius , 0.0f, 10000.0f );
if( ImGui::CollapsingHeader( "Auto-tuned settings (heuristics)" ) )
{
hadChanges |= ImGui::InputFloat( "Radius multiplier", &settings.RadiusMultiplier , 0.05f, 0.0f, "%.2f" );
if( ImGui::IsItemHovered( ) ) ImGui::SetTooltip( "Multiplies the 'Effect Radius' - used by the auto-tune to best match raytraced ground truth\nExpected range: [0.3, 3.0], defaults to %.3f", XE_GTAO_DEFAULT_RADIUS_MULTIPLIER );
settings.RadiusMultiplier = clamp( settings.RadiusMultiplier , 0.3f, 3.0f );
hadChanges |= ImGui::InputFloat( "Falloff range", &settings.FalloffRange , 0.05f, 0.0f, "%.2f" );
if( ImGui::IsItemHovered( ) ) ImGui::SetTooltip( "Gently reduce sample impact as it gets out of 'Effect radius' bounds\nExpected range: [0.0, 1.0], defaults to %.3f", XE_GTAO_DEFAULT_FALLOFF_RANGE );
settings.FalloffRange = clamp( settings.FalloffRange , 0.0f, 1.0f );
hadChanges |= ImGui::InputFloat( "Sample distribution power", &settings.SampleDistributionPower , 0.05f, 0.0f, "%.2f" );
if( ImGui::IsItemHovered( ) ) ImGui::SetTooltip( "Make samples on a slice equally distributed (1.0) or focus more towards the center (>1.0)\nExpected range: [1.0, 3.0], 2defaults to %.3f", XE_GTAO_DEFAULT_SAMPLE_DISTRIBUTION_POWER );
settings.SampleDistributionPower = clamp( settings.SampleDistributionPower , 1.0f, 3.0f );
hadChanges |= ImGui::InputFloat( "Thin occluder compensation", &settings.ThinOccluderCompensation, 0.05f, 0.0f, "%.2f" );
if( ImGui::IsItemHovered( ) ) ImGui::SetTooltip( "Slightly reduce impact of samples further back to counter the bias from depth-based (incomplete) input scene geometry data\nExpected range: [0.0, 0.7], defaults to %.3f", XE_GTAO_DEFAULT_THIN_OCCLUDER_COMPENSATION );
settings.ThinOccluderCompensation = clamp( settings.ThinOccluderCompensation , 0.0f, 0.7f );
hadChanges |= ImGui::InputFloat( "Final power", &settings.FinalValuePower, 0.05f, 0.0f, "%.2f" );
if( ImGui::IsItemHovered( ) ) ImGui::SetTooltip( "Applies power function to the final value: occlusion = pow( occlusion, finalPower )\nExpected range: [0.5, 5.0], defaults to %.3f", XE_GTAO_DEFAULT_FINAL_VALUE_POWER );
settings.FinalValuePower = clamp( settings.FinalValuePower , 0.5f, 5.0f );
hadChanges |= ImGui::InputFloat( "Depth MIP sampling offset", &settings.DepthMIPSamplingOffset, 0.05f, 0.0f, "%.2f" );
if( ImGui::IsItemHovered( ) ) ImGui::SetTooltip( "Mainly performance (texture memory bandwidth) setting but as a side-effect reduces overshadowing by thin objects and increases temporal instability\nExpected range: [2.0, 6.0], defaults to %.3f", XE_GTAO_DEFAULT_DEPTH_MIP_SAMPLING_OFFSET );
settings.DepthMIPSamplingOffset = clamp( settings.DepthMIPSamplingOffset , 0.0f, 30.0f );
}
ImGui::PopItemWidth( );
return hadChanges;
}
#endif // IMGUI_API
} // close the namespace
#endif // #ifdef __cplusplus
#endif // __XE_GTAO_TYPES_H__

View File

@@ -0,0 +1,113 @@
#include "auto_exposure.h"
#include <nvrhi/utils.h>
#include "config.h"
#include "nvrenderer/nvrenderer.h"
#include "nvrendererbackend.h"
MaAutoExposure::MaAutoExposure(NvRenderer *renderer)
: MaResourceRegistry(renderer), m_backend(renderer->GetBackend()) {}
void MaAutoExposure::Render(nvrhi::ICommandList *command_list) {
PushConstantsAutoExposure push_constants{};
push_constants.m_delta_time = .01f;
push_constants.m_num_mips = m_num_mips;
push_constants.m_num_workgroups =
((m_width + 63) / 64) * ((m_height + 64) / 64);
push_constants.m_min_luminance_ev =
NvRenderer::Config()->m_lighting.m_min_luminance_ev;
push_constants.m_max_luminance_ev =
NvRenderer::Config()->m_lighting.m_max_luminance_ev;
command_list->setComputeState(nvrhi::ComputeState()
.addBindingSet(m_bindings_auto_exposure)
.setPipeline(m_pso_auto_exposure));
command_list->setPushConstants(&push_constants, sizeof(push_constants));
command_list->dispatch((m_width + 63) / 64, (m_height + 64) / 64, 1);
command_list->setComputeState(
nvrhi::ComputeState()
.addBindingSet(m_bindings_auto_exposure_apply)
.setPipeline(m_pso_auto_exposure_apply));
command_list->dispatch((m_width + 7) / 8, (m_height + 7) / 8, 1);
}
void MaAutoExposure::Init() {
nvrhi::TextureHandle input_texture = dynamic_cast<nvrhi::ITexture *>(
GetResource("scene_lit_texture", nvrhi::ResourceType::Texture_SRV)
.m_resource);
auto desc = input_texture->getDesc();
m_width = desc.width;
m_height = desc.height;
m_num_mips =
glm::floor(glm::log2(static_cast<float>(glm::max(m_width, m_height))));
m_6th_mip_img = m_backend->GetDevice()->createTexture(
nvrhi::TextureDesc()
.setFormat(nvrhi::Format::R32_FLOAT)
.setWidth(m_width >> 6)
.setHeight(m_height >> 6)
.setIsUAV(true)
.setInitialState(nvrhi::ResourceStates::UnorderedAccess)
.setKeepInitialState(true)
.setDebugName("Auto Exposure 6th Mip"));
m_output_exposure_fsr2 = m_backend->GetDevice()->createTexture(
nvrhi::TextureDesc()
.setFormat(nvrhi::Format::RG16_FLOAT)
.setWidth(1)
.setHeight(1)
.setIsUAV(true)
.setInitialState(nvrhi::ResourceStates::ShaderResource)
.setKeepInitialState(true)
.setDebugName("Auto Exposure Output (FSR2)"));
m_atomic_counter = m_backend->GetDevice()->createBuffer(
nvrhi::BufferDesc()
.setByteSize(sizeof(uint32_t))
.setStructStride(sizeof(uint32_t))
.setCanHaveUAVs(true)
.setInitialState(nvrhi::ResourceStates::UnorderedAccess)
.setKeepInitialState(true)
.setDebugName("Auto Exposure Atomic Counter"));
auto cs_auto_exposure = m_backend->CreateShader(
"autoexp_compute_avg_luminance", nvrhi::ShaderType::Compute);
nvrhi::BindingLayoutHandle binding_layout;
nvrhi::utils::CreateBindingSetAndLayout(
m_backend->GetDevice(), nvrhi::ShaderType::Compute, 0,
nvrhi::BindingSetDesc()
.addItem(nvrhi::BindingSetItem::PushConstants(
1, sizeof(PushConstantsAutoExposure)))
.addItem(nvrhi::BindingSetItem::Texture_SRV(0, input_texture))
.addItem(
nvrhi::BindingSetItem::Texture_UAV(0, m_output_exposure_fsr2))
.addItem(
nvrhi::BindingSetItem::StructuredBuffer_UAV(2, m_atomic_counter))
.addItem(nvrhi::BindingSetItem::Texture_UAV(3, m_6th_mip_img)),
binding_layout, m_bindings_auto_exposure);
m_pso_auto_exposure = m_backend->GetDevice()->createComputePipeline(
nvrhi::ComputePipelineDesc()
.addBindingLayout(binding_layout)
.setComputeShader(cs_auto_exposure));
auto cs_auto_exposure_apply =
m_backend->CreateShader("autoexp_apply", nvrhi::ShaderType::Compute);
nvrhi::BindingLayoutHandle binding_layout_apply;
nvrhi::utils::CreateBindingSetAndLayout(
m_backend->GetDevice(), nvrhi::ShaderType::Compute, 0,
nvrhi::BindingSetDesc()
.addItem(
nvrhi::BindingSetItem::Texture_SRV(0, m_output_exposure_fsr2))
.addItem(nvrhi::BindingSetItem::Texture_UAV(0, input_texture)),
binding_layout_apply, m_bindings_auto_exposure_apply);
m_pso_auto_exposure_apply = m_backend->GetDevice()->createComputePipeline(
nvrhi::ComputePipelineDesc()
.addBindingLayout(binding_layout_apply)
.setComputeShader(cs_auto_exposure_apply));
}

View File

@@ -0,0 +1,39 @@
#pragma once
#include "nvrenderer/resource_registry.h"
#include <nvrhi/nvrhi.h>
class NvRenderer;
class NvRendererBackend;
struct MaAutoExposure : public MaResourceRegistry {
MaAutoExposure(NvRenderer* renderer);
void Init();
void Render(nvrhi::ICommandList* command_list);
uint32_t m_width;
uint32_t m_height;
uint32_t m_num_mips;
struct PushConstantsAutoExposure {
uint32_t m_num_mips;
uint32_t m_num_workgroups;
float m_delta_time;
float m_min_luminance_ev;
float m_max_luminance_ev;
};
NvRendererBackend* m_backend;
nvrhi::TextureHandle m_output_exposure_fsr2;
nvrhi::TextureHandle m_6th_mip_img;
nvrhi::BufferHandle m_atomic_counter;
nvrhi::BindingSetHandle m_bindings_auto_exposure;
nvrhi::ComputePipelineHandle m_pso_auto_exposure;
nvrhi::BindingSetHandle m_bindings_auto_exposure_apply;
nvrhi::ComputePipelineHandle m_pso_auto_exposure_apply;
};

View File

@@ -0,0 +1,178 @@
#include "bloom.h"
#include <nvrhi/utils.h>
#include "nvrendererbackend.h"
void Bloom::UpdateConstants(nvrhi::ICommandList* command_list) {
BloomConstants constants{};
constants.m_prefilter_vector =
glm::vec4{m_threshold, m_threshold - m_knee, m_knee * 2.f, .25f / m_knee};
constants.m_mix_factor = m_mix_factor;
command_list->writeBuffer(m_constant_buffer, &constants, sizeof(constants));
}
void Bloom::OnGui() {
if (!m_gui_active) return;
if (ImGui::Begin("Bloom settings", &m_gui_active,
ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::SliderFloat("Threshold", &m_threshold, 0.f, 100.f);
ImGui::SliderFloat("Knee", &m_knee, 0.f, 100.f);
ImGui::SliderFloat("Mix Factor", &m_mix_factor, 0.f, 100.f);
ImGui::End();
}
}
void Bloom::ShowGui() { m_gui_active = true; }
void Bloom::Init(nvrhi::ITexture* texture) {
auto source_desc = texture->getDesc();
int mip_count = 7;
m_working_texture = m_backend->GetDevice()->createTexture(
nvrhi::TextureDesc()
.setWidth(source_desc.width)
.setHeight(source_desc.height)
.setFormat(nvrhi::Format::RGBA16_FLOAT)
.setMipLevels(mip_count)
.setIsUAV(true)
.setInitialState(nvrhi::ResourceStates::UnorderedAccess)
.setKeepInitialState(true));
m_constant_buffer = m_backend->GetDevice()->createBuffer(
nvrhi::utils::CreateVolatileConstantBufferDesc(sizeof(BloomConstants),
"Bloom Constants", 16)
.setInitialState(nvrhi::ResourceStates::ConstantBuffer)
.setKeepInitialState(true));
auto shader_prefilter =
m_backend->CreateShader("bloom_prefilter", nvrhi::ShaderType::Compute);
auto shader_downsample =
m_backend->CreateShader("bloom_downsample", nvrhi::ShaderType::Compute);
auto shader_upsample =
m_backend->CreateShader("bloom_upsample", nvrhi::ShaderType::Compute);
auto shader_final =
m_backend->CreateShader("bloom_apply", nvrhi::ShaderType::Compute);
auto binding_layout = m_backend->GetDevice()->createBindingLayout(
nvrhi::BindingLayoutDesc()
.setVisibility(nvrhi::ShaderType::Compute)
.addItem(nvrhi::BindingLayoutItem::VolatileConstantBuffer(0))
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(0))
.addItem(nvrhi::BindingLayoutItem::Texture_UAV(0))
.addItem(nvrhi::BindingLayoutItem::Sampler(0)));
auto binding_layout_final = m_backend->GetDevice()->createBindingLayout(
nvrhi::BindingLayoutDesc()
.setVisibility(nvrhi::ShaderType::Compute)
.addItem(nvrhi::BindingLayoutItem::VolatileConstantBuffer(0))
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(0))
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(1))
.addItem(nvrhi::BindingLayoutItem::Texture_UAV(0))
.addItem(nvrhi::BindingLayoutItem::Sampler(0)));
auto pso_prefilter = m_backend->GetDevice()->createComputePipeline(
nvrhi::ComputePipelineDesc()
.setComputeShader(shader_prefilter)
.addBindingLayout(binding_layout));
auto pso_downsample = m_backend->GetDevice()->createComputePipeline(
nvrhi::ComputePipelineDesc()
.setComputeShader(shader_downsample)
.addBindingLayout(binding_layout));
auto pso_upsample = m_backend->GetDevice()->createComputePipeline(
nvrhi::ComputePipelineDesc()
.setComputeShader(shader_upsample)
.addBindingLayout(binding_layout));
auto pso_final = m_backend->GetDevice()->createComputePipeline(
nvrhi::ComputePipelineDesc()
.setComputeShader(shader_final)
.addBindingLayout(binding_layout_final));
auto sampler_linear_clamp = m_backend->GetDevice()->createSampler(
nvrhi::SamplerDesc().setAllFilters(true).setAllAddressModes(
nvrhi::SamplerAddressMode::ClampToEdge));
{
auto& stage = m_stages.emplace_back();
stage.m_width = source_desc.width;
stage.m_height = source_desc.height;
stage.m_pso = pso_prefilter;
stage.m_binding_set = m_backend->GetDevice()->createBindingSet(
nvrhi::BindingSetDesc()
.addItem(
nvrhi::BindingSetItem::ConstantBuffer(0, m_constant_buffer))
.addItem(nvrhi::BindingSetItem::Texture_SRV(
0, texture, nvrhi::Format::UNKNOWN,
nvrhi::TextureSubresourceSet(0, 1, 0, 1)))
.addItem(nvrhi::BindingSetItem::Texture_UAV(
0, m_working_texture, nvrhi::Format::UNKNOWN,
nvrhi::TextureSubresourceSet(0, 1, 0, 1)))
.addItem(nvrhi::BindingSetItem::Sampler(0, sampler_linear_clamp)),
binding_layout);
}
for (int i = 1; i < mip_count; ++i) {
auto& stage = m_stages.emplace_back();
stage.m_width = glm::max(1u, source_desc.width >> i);
stage.m_height = glm::max(1u, source_desc.height >> i);
stage.m_pso = pso_downsample;
stage.m_binding_set = m_backend->GetDevice()->createBindingSet(
nvrhi::BindingSetDesc()
.addItem(
nvrhi::BindingSetItem::ConstantBuffer(0, m_constant_buffer))
.addItem(nvrhi::BindingSetItem::Texture_SRV(
0, m_working_texture, nvrhi::Format::UNKNOWN,
nvrhi::TextureSubresourceSet(glm::max(0, i - 1), 1, 0, 1)))
.addItem(nvrhi::BindingSetItem::Texture_UAV(
0, m_working_texture, nvrhi::Format::UNKNOWN,
nvrhi::TextureSubresourceSet(i, 1, 0, 1)))
.addItem(nvrhi::BindingSetItem::Sampler(0, sampler_linear_clamp)),
binding_layout);
}
for (int i = mip_count - 2; i >= 1; --i) {
auto& stage = m_stages.emplace_back();
stage.m_width = glm::max(1u, source_desc.width >> i);
stage.m_height = glm::max(1u, source_desc.height >> i);
stage.m_pso = pso_upsample;
stage.m_binding_set = m_backend->GetDevice()->createBindingSet(
nvrhi::BindingSetDesc()
.addItem(
nvrhi::BindingSetItem::ConstantBuffer(0, m_constant_buffer))
.addItem(nvrhi::BindingSetItem::Texture_SRV(
0, m_working_texture, nvrhi::Format::UNKNOWN,
nvrhi::TextureSubresourceSet(i + 1, 1, 0, 1)))
.addItem(nvrhi::BindingSetItem::Texture_UAV(
0, m_working_texture, nvrhi::Format::UNKNOWN,
nvrhi::TextureSubresourceSet(i, 1, 0, 1)))
.addItem(nvrhi::BindingSetItem::Sampler(0, sampler_linear_clamp)),
binding_layout);
}
{
auto& stage = m_stages.emplace_back();
stage.m_width = source_desc.width;
stage.m_height = source_desc.height;
stage.m_pso = pso_final;
stage.m_binding_set = m_backend->GetDevice()->createBindingSet(
nvrhi::BindingSetDesc()
.addItem(
nvrhi::BindingSetItem::ConstantBuffer(0, m_constant_buffer))
.addItem(nvrhi::BindingSetItem::Texture_SRV(
0, m_working_texture, nvrhi::Format::UNKNOWN,
nvrhi::TextureSubresourceSet(1, 1, 0, 1)))
.addItem(nvrhi::BindingSetItem::Texture_SRV(
1, texture, nvrhi::Format::UNKNOWN,
nvrhi::TextureSubresourceSet(0, 1, 0, 1)))
.addItem(nvrhi::BindingSetItem::Texture_UAV(
0, m_working_texture, nvrhi::Format::UNKNOWN,
nvrhi::TextureSubresourceSet(0, 1, 0, 1)))
.addItem(nvrhi::BindingSetItem::Sampler(0, sampler_linear_clamp)),
binding_layout_final);
}
}
void Bloom::Render(nvrhi::ICommandList* command_list) {
command_list->beginMarker("Bloom");
UpdateConstants(command_list);
for (const auto& stage : m_stages) {
command_list->setComputeState(nvrhi::ComputeState()
.setPipeline(stage.m_pso)
.addBindingSet(stage.m_binding_set));
command_list->dispatch((stage.m_width + 7) / 8,
(stage.m_height + 7) / 8, 1);
}
command_list->endMarker();
}

View File

@@ -0,0 +1,36 @@
#pragma once
#include <glm/glm.hpp>
#include <nvrhi/nvrhi.h>
struct Bloom {
struct BloomConstants {
glm::vec4 m_prefilter_vector;
float m_mix_factor;
};
float m_threshold = 3.f;
float m_knee = .6f;
float m_mix_factor = .04f;
bool m_gui_active = false;
void UpdateConstants(nvrhi::ICommandList* command_list);
void OnGui();
void ShowGui();
Bloom(class NvRendererBackend* backend) : m_backend(backend) {}
void Init(nvrhi::ITexture* texture);
void Render(nvrhi::ICommandList* command_list);
nvrhi::TextureHandle m_working_texture;
nvrhi::BufferHandle m_constant_buffer;
struct BloomStage {
int m_width;
int m_height;
nvrhi::BindingSetHandle m_binding_set;
nvrhi::ComputePipelineHandle m_pso;
};
std::vector<BloomStage> m_stages;
class NvRendererBackend* m_backend;
};

View File

@@ -0,0 +1,55 @@
#include "config.h"
#include "nvrenderer/nvrenderer.h"
#include "nvrendererbackend.h"
void MaConfig::Init(const std::filesystem::path& config_file) {
Init(YAML::LoadFile(config_file.generic_string()));
}
void MaConfig::Init(const YAML::Node& node) {
m_display.Init(node["display"]);
m_shadow.Init(node["shadows"]);
m_lighting.Init(node["lighting"]);
m_weight_lines = node["wire_diameter_lines"].as<float>(.01f);
m_weight_tractions = node["wire_diameter_tractions"].as<float>(.005f);
m_envmap_resolution = node["envmap_resolution"].as<int>(256);
}
void MaConfig::MaConfigDisplay::Init(const YAML::Node& node) {
m_hdr_configs[0].Init(node["hdr_config"]["sdr"]);
m_hdr_configs[0].m_tonemap_function = MaTonemapType::Default_Srgb;
m_hdr_configs[1].Init(node["hdr_config"]["hdr"]);
m_hdr_configs[1].m_tonemap_function = MaTonemapType::Linear_Rec2020;
m_adapter_index = node["adapter"].as<int>();
m_output_index = node["output"].as<int>();
m_disable_hdr = node["disable_hdr"].as<bool>(false);
}
MaConfig::MaConfigDisplay::MaConfigHDR MaConfig::MaConfigDisplay::GetHDRConfig()
const {
auto renderer = dynamic_cast<NvRenderer*>(GfxRenderer.get());
return GetHDRConfig(renderer ? renderer->GetBackend()->IsHDRDisplay()
: false);
}
void MaConfig::MaConfigDisplay::MaConfigHDR::Init(const YAML::Node& node) {
m_scene_exposure = node["pre_tonemap_exposure"].as<float>();
m_ui_nits = node["ui_nits"].as<float>();
m_scene_nits = node["scene_nits"].as<float>();
m_scene_gamma = node["scene_gamma"].as<float>();
}
void MaConfig::MaConfigShadow::Init(const YAML::Node& node) {
m_resolution = node["resolution"].as<int>();
m_cascade_distances = node["cascades"].as<std::vector<double>>();
}
void MaConfig::MaConfigLighting::Init(const YAML::Node& node) {
m_max_lights_per_scene = node["max_lights_per_scene"].as<int>(2048);
m_selfillum_multiplier = node["selfillum_factor"].as<float>(1.f);
m_freespot_multiplier = node["freespot_factor"].as<float>(10.f);
m_luminance_threshold = node["luminance_threshold"].as<float>(.01f);
m_min_luminance_ev = node["min_luminance_ev"].as<float>(-3.f);
m_max_luminance_ev = node["max_luminance_ev"].as<float>(3.f);
}

View File

@@ -0,0 +1,49 @@
#pragma once
#include <yaml-cpp/yaml.h>
#include <filesystem>
struct MaConfig {
enum MaTonemapType { Default_Srgb = 0, Linear_Rec2020 };
void Init(const std::filesystem::path& config_file);
void Init(const YAML::Node& node);
struct MaConfigDisplay {
void Init(const YAML::Node& node);
struct MaConfigHDR {
void Init(const YAML::Node& node);
MaTonemapType m_tonemap_function;
float m_ui_nits;
float m_scene_nits;
float m_scene_exposure;
float m_scene_gamma;
float GetMaxOutputNits() const {
return std::max(m_ui_nits, m_scene_nits);
}
float GetMinOutputNits() const { return 1.e-3f; }
};
std::array<MaConfigHDR, 2> m_hdr_configs;
int m_adapter_index;
int m_output_index;
bool m_disable_hdr = false;
MaConfigHDR GetHDRConfig(bool is_hdr) const { return m_hdr_configs[is_hdr]; }
MaConfigHDR GetHDRConfig() const;
} m_display;
struct MaConfigLighting{
void Init(const YAML::Node& node);
int m_max_lights_per_scene;
float m_selfillum_multiplier;
float m_freespot_multiplier;
float m_luminance_threshold;
float m_min_luminance_ev;
float m_max_luminance_ev;
} m_lighting;
int m_envmap_resolution;
float m_weight_lines;
float m_weight_tractions;
struct MaConfigShadow {
void Init(const YAML::Node& node);
int m_resolution;
std::vector<double> m_cascade_distances;
} m_shadow;
};

View File

@@ -0,0 +1,76 @@
#include "contactshadows.h"
#include <nvrhi/utils.h>
#include "gbuffer.h"
#include "nvrenderer/nvrenderer.h"
#include "nvrendererbackend.h"
MaContactShadows::MaContactShadows(NvRenderer* renderer, NvGbuffer* gbuffer)
: m_backend(renderer->GetBackend()),
m_gbuffer(gbuffer),
m_width(0),
m_height(0) {}
void MaContactShadows::Init() {
m_width = m_gbuffer->m_gbuffer_depth->getDesc().width;
m_height = m_gbuffer->m_gbuffer_depth->getDesc().height;
m_compute_shader = m_backend->CreateShader("contact_shadows",
nvrhi::ShaderType::Compute);
m_constant_buffer = m_backend->GetDevice()->createBuffer(
nvrhi::utils::CreateStaticConstantBufferDesc(sizeof(ComputeConstants),
"Contact shadow constants")
.setInitialState(nvrhi::ResourceStates::ConstantBuffer)
.setKeepInitialState(true));
m_output_texture = m_backend->GetDevice()->createTexture(
nvrhi::TextureDesc()
.setFormat(nvrhi::Format::R8_UNORM)
.setWidth(m_width)
.setHeight(m_height)
.setIsUAV(true)
.setInitialState(nvrhi::ResourceStates::ShaderResource)
.setKeepInitialState(true));
nvrhi::utils::CreateBindingSetAndLayout(
m_backend->GetDevice(), nvrhi::ShaderType::Compute, 0,
nvrhi::BindingSetDesc()
.addItem(
nvrhi::BindingSetItem::Texture_SRV(0, m_gbuffer->m_gbuffer_depth))
.addItem(nvrhi::BindingSetItem::Texture_UAV(0, m_output_texture))
.addItem(nvrhi::BindingSetItem::ConstantBuffer(0, m_constant_buffer))
.addItem(nvrhi::BindingSetItem::Sampler(
0, m_backend->GetDevice()->createSampler(
nvrhi::SamplerDesc()
.setAllFilters(true)
.setAllAddressModes(
nvrhi::SamplerAddressMode::ClampToEdge)
.setReductionType(
nvrhi::SamplerReductionType::Maximum)))),
m_binding_layout, m_binding_set);
m_pipeline = m_backend->GetDevice()->createComputePipeline(
nvrhi::ComputePipelineDesc()
.setComputeShader(m_compute_shader)
.addBindingLayout(m_binding_layout));
}
void MaContactShadows::Render(nvrhi::ICommandList* command_list) {
command_list->setComputeState(nvrhi::ComputeState()
.setPipeline(m_pipeline)
.addBindingSet(m_binding_set));
command_list->dispatch((m_width + 7) / 8, (m_height + 7) / 8, 1);
}
void MaContactShadows::UpdateConstants(nvrhi::ICommandList* command_list,
const glm::dmat4& projection,
const glm::dmat4& view,
const glm::dvec3& light,
uint64_t frame_index) {
ComputeConstants constants{};
constants.g_InverseProjection = glm::inverse(projection);
constants.g_Projection = projection;
constants.g_LightDirView = view * glm::dvec4(-light, 0.);
constants.g_NumSamples = 16;
constants.g_SampleRange = .1;
constants.g_Thickness = .01;
constants.g_FrameIndex = frame_index % 64;
command_list->writeBuffer(m_constant_buffer, &constants, sizeof(constants));
}

View File

@@ -0,0 +1,36 @@
#pragma once
#include <nvrhi/nvrhi.h>
#include <glm/glm.hpp>
struct MaContactShadows {
MaContactShadows(class NvRenderer* renderer, class NvGbuffer* gbuffer);
void Init();
void Render(nvrhi::ICommandList* command_list);
void UpdateConstants(nvrhi::ICommandList* command_list,
const glm::dmat4& projection, const glm::dmat4& view,
const glm::dvec3& light, uint64_t frame_index);
struct ComputeConstants {
glm::mat4 g_Projection;
glm::mat4 g_InverseProjection;
glm::vec3 g_LightDirView;
float g_NumSamples;
float g_SampleRange;
float g_Thickness;
uint32_t g_FrameIndex;
};
nvrhi::BindingLayoutHandle m_binding_layout;
nvrhi::BindingSetHandle m_binding_set;
nvrhi::ComputePipelineHandle m_pipeline;
nvrhi::ShaderHandle m_compute_shader;
nvrhi::BufferHandle m_constant_buffer;
nvrhi::TextureHandle m_output_texture;
class NvRendererBackend* m_backend;
class NvGbuffer* m_gbuffer;
int m_width;
int m_height;
};

View File

@@ -0,0 +1,37 @@
#include "copypass.h"
#include <nvrhi/utils.h>
#include "nvrendererbackend.h"
void CopyPass::Init() {
nvrhi::utils::CreateBindingSetAndLayout(
m_backend->GetDevice(), nvrhi::ShaderType::Pixel, 0,
nvrhi::BindingSetDesc()
.addItem(nvrhi::BindingSetItem::Texture_SRV(0, m_source))
.addItem(nvrhi::BindingSetItem::Sampler(
0, m_backend->GetDevice()->createSampler(nvrhi::SamplerDesc()))),
m_binding_layout, m_binding_set);
m_pixel_shader = m_backend->CreateShader("copy", nvrhi::ShaderType::Pixel);
m_framebuffer = m_backend->GetDevice()->createFramebuffer(
nvrhi::FramebufferDesc().addColorAttachment(m_output));
FullScreenPass::Init();
}
void CopyPass::CreatePipelineDesc(nvrhi::GraphicsPipelineDesc& pipeline_desc) {
FullScreenPass::CreatePipelineDesc(pipeline_desc);
pipeline_desc.addBindingLayout(m_binding_layout);
pipeline_desc.setPixelShader(m_pixel_shader);
}
void CopyPass::Render(nvrhi::ICommandList* command_list) {
nvrhi::GraphicsState graphics_state;
InitState(graphics_state);
graphics_state.addBindingSet(m_binding_set);
command_list->setGraphicsState(graphics_state);
Draw(command_list);
}
nvrhi::IFramebuffer* CopyPass::GetFramebuffer() {
return m_framebuffer;
}

View File

@@ -0,0 +1,28 @@
#pragma once
#include <nvrhi/nvrhi.h>
#include "fullscreenpass.h"
struct CopyPass : public FullScreenPass {
CopyPass(class NvRendererBackend* backend, nvrhi::ITexture* source, nvrhi::ITexture* output)
: FullScreenPass(backend),
m_source(source), m_output(output) {}
virtual void Init() override;
virtual void CreatePipelineDesc(
nvrhi::GraphicsPipelineDesc& pipeline_desc) override;
virtual void Render(nvrhi::ICommandList* command_list) override;
virtual nvrhi::IFramebuffer* GetFramebuffer() override;
nvrhi::BindingLayoutHandle m_binding_layout;
nvrhi::BindingSetHandle m_binding_set;
nvrhi::ShaderHandle m_pixel_shader;
nvrhi::FramebufferHandle m_framebuffer;
nvrhi::ITexture* m_source;
nvrhi::ITexture* m_output;
};

View File

@@ -0,0 +1,188 @@
#include "csm.h"
#include <nvrhi/utils.h>
#include "config.h"
#include "nvrenderer/nvrenderer.h"
#include "nvrendererbackend.h"
MaShadowMap::MaShadowMap(NvRenderer* renderer)
: MaResourceRegistry(renderer), m_backend(renderer->m_backend.get()) {}
void MaShadowMap::Init() {
InitResourceRegistry();
const auto& shadow_config = NvRenderer::Config()->m_shadow;
int num_cascades = shadow_config.m_cascade_distances.size();
m_cascade_matrices.resize(num_cascades);
m_cascade_matrices_clamped.resize(num_cascades);
m_shadow_planes.resize(num_cascades + 1);
m_far_plane_first = shadow_config.m_cascade_distances.front();
m_far_plane_last = shadow_config.m_cascade_distances.back();
m_projection_buffer = m_backend->GetDevice()->createBuffer(
nvrhi::utils::CreateStaticConstantBufferDesc(
sizeof(ShadowProjectionConstants), "Shadow projections")
.setInitialState(nvrhi::ResourceStates::ConstantBuffer)
.setKeepInitialState(true));
m_inverse_projection_buffer = m_backend->GetDevice()->createBuffer(
nvrhi::utils::CreateStaticConstantBufferDesc(
sizeof(ShadowProjectionConstants), "Inverse shadow projections")
.setInitialState(nvrhi::ResourceStates::ConstantBuffer)
.setKeepInitialState(true));
RegisterResource(true, "shadow_projection_buffer", m_projection_buffer,
nvrhi::ResourceType::ConstantBuffer);
}
void MaShadowMap::CalcCascades(const glm::dmat4& projection,
const glm::dmat4& view,
const glm::dvec3& light) {
const auto& shadow_config = NvRenderer::Config()->m_shadow;
glm::dmat4 inverse_projection = glm::inverse(projection);
glm::dmat4 inverse_view = glm::inverse(view);
double projection_near, projection_far;
{
glm::dvec4 ndc_near{0., 0., 0., 1.};
glm::dvec4 ndc_far{0., 0., 1., 1.};
ndc_near = inverse_projection * ndc_near;
ndc_far = inverse_projection * ndc_far;
projection_near = -ndc_near.z / ndc_near.w;
projection_far = -ndc_far.z / ndc_far.w;
}
// Set near and far plane offset
m_shadow_planes.front() = projection_near;
for (int i = 1; i < shadow_config.m_cascade_distances.size(); ++i)
m_shadow_planes[i] = shadow_config.m_cascade_distances[i];
// m_shadow_planes.back() = std::min(m_far_plane_last, projection_far);
// m_shadow_planes[1] = std::min(m_far_plane_first, m_shadow_planes.back());
//
//// Fill intermediate plane offsets
// double base = m_shadow_planes.back() / m_shadow_planes[1];
// double denom = 1. / (m_shadow_planes.size() - 2.);
// for (int i = 2; i < m_shadow_planes.size() - 1; ++i) {
// m_shadow_planes[i] = m_shadow_planes[1] * glm::pow(base, (i - 1) *
// denom);
// }
static std::vector<glm::dvec3> plane_corners;
plane_corners.resize(4 * (m_shadow_planes.size()));
// for (int i = 0; i < m_shadow_planes.size(); ++i) {
// glm::dvec3* plane = &plane_corners[i * 4];
// GetViewPlaneCornersWorldSpace(projection, inverse_projection,
// inverse_view,
// m_shadow_planes[i], plane[0], plane[1],
// plane[2], plane[3]);
// }
glm::dvec3 up = glm::abs(light.y) > .999 ? glm::dvec3{0., 0., 1.}
: glm::dvec3{0., 1., 0.};
for (int i = 0; i < m_cascade_matrices.size(); ++i) {
glm::dvec3 plane[8];
GetViewPlaneCornersWorldSpace(projection, inverse_projection, inverse_view,
m_shadow_planes[i], true, plane[0], plane[1],
plane[2], plane[3]);
GetViewPlaneCornersWorldSpace(projection, inverse_projection, inverse_view,
m_shadow_planes[i + 1], false, plane[4],
plane[5], plane[6], plane[7]);
glm::dvec3 world_min{std::numeric_limits<double>::max()};
glm::dvec3 world_max{-std::numeric_limits<double>::max()};
double offset = 0.;
for (int j = 0; j < 8; ++j) {
world_min = glm::min(world_min, plane[j]);
world_max = glm::max(world_max, plane[j]);
}
double actual_size = glm::max(glm::distance(plane[0], plane[7]),
glm::distance(plane[4], plane[7]));
glm::dvec3 center = -Global.pCamera.Pos;
glm::dmat4 shadow_view = glm::lookAtRH(center, center - light, up);
glm::dvec3 min{std::numeric_limits<double>::max()};
glm::dvec3 max{-std::numeric_limits<double>::max()};
double frustumchunkdepth =
m_shadow_planes[i + 1] -
(m_shadow_planes[i] > 1.f ? m_shadow_planes[i] : 0.f);
double quantizationstep{glm::min(frustumchunkdepth, 50.)};
for (int j = 0; j < 8; ++j) {
glm::dvec3 v = shadow_view * glm::dvec4(plane[j], 1.f);
min = glm::min(min, v);
max = glm::max(max, v);
}
glm::dvec2 size = max - min;
glm::dvec2 diff = static_cast<glm::dvec2>(actual_size) - size;
max.xy += glm::max(diff * .5, 0.);
min.xy -= glm::max(diff * .5, 0.);
glm::dvec2 pixel_size =
static_cast<glm::dvec2>(actual_size / shadow_config.m_resolution);
min.xy = glm::round(min.xy / pixel_size) * pixel_size;
max.xy = glm::round(max.xy / pixel_size) * pixel_size;
// max = quantizationstep * glm::ceil(max / quantizationstep);
// min = quantizationstep * glm::floor(min / quantizationstep);
glm::dmat4 shadow_proj = glm::orthoRH_ZO(min.x, max.x, min.y, max.y,
-max.z - 500., -min.z + 500.);
glm::dmat4 shadow_proj_clamped = glm::orthoRH_ZO(
min.x, max.x, min.y, max.y, -max.z - 500., -min.z + 500.);
m_cascade_matrices[i] = shadow_proj * shadow_view;
m_cascade_matrices_clamped[i] = shadow_proj_clamped * shadow_view;
}
}
void MaShadowMap::UpdateConstants(nvrhi::ICommandList* command_list) {
{
ShadowProjectionConstants constants{};
for (int i = 0; i < std::min(m_cascade_matrices.size(),
constants.m_shadow_projection.size());
++i) {
constants.m_shadow_projection[i] = m_cascade_matrices[i];
constants.m_shadow_planes[i] =
m_shadow_planes[i + 1] * m_shadow_planes[i + 1];
}
command_list->writeBuffer(m_projection_buffer, &constants,
sizeof(constants));
}
}
void MaShadowMap::GetViewPlaneCornersWorldSpace(
const glm::dmat4& projection, const glm::dmat4& inverse_projection,
const glm::dmat4& inverse_view, const double& d, bool normalize,
glm::dvec3& ll, glm::dvec3& lr, glm::dvec3& ul, glm::dvec3& ur) {
glm::dvec4 lll{-1., -1., 1., 1.};
glm::dvec4 llr{1., -1., 1., 1.};
glm::dvec4 lul{-1., 1., 1., 1.};
glm::dvec4 lur{1., 1., 1., 1.};
auto unproject = [&](glm::dvec4& v) -> void {
v = inverse_projection * v;
if (normalize) {
v /= v.w;
v *= d / glm::length(glm::dvec3(v.xyz));
} else {
v *= d / -v.z;
}
v.w = 1.;
};
unproject(lll);
unproject(llr);
unproject(lul);
unproject(lur);
ll = inverse_view * lll;
lr = inverse_view * llr;
ul = inverse_view * lul;
ur = inverse_view * lur;
}

View File

@@ -0,0 +1,30 @@
#pragma once
#include <nvrhi/nvrhi.h>
#include "nvrenderer/resource_registry.h"
struct MaShadowMap : public MaResourceRegistry {
MaShadowMap(class NvRenderer* renderer);
void Init();
std::vector<double> m_shadow_planes;
std::vector<glm::dmat4> m_cascade_matrices;
std::vector<glm::dmat4> m_cascade_matrices_clamped;
double m_far_plane_first;
double m_far_plane_last;
double m_shadow_far_plane;
nvrhi::BufferHandle m_projection_buffer;
nvrhi::BufferHandle m_inverse_projection_buffer;
class NvRendererBackend* m_backend;
void CalcCascades(const glm::dmat4& projection, const glm::dmat4& view,
const glm::dvec3& light);
void UpdateConstants(nvrhi::ICommandList* command_list);
static void GetViewPlaneCornersWorldSpace(
const glm::dmat4& projection, const glm::dmat4& inverse_projection,
const glm::dmat4& inverse_view, const double& d, bool normalize,
glm::dvec3& ll, glm::dvec3& lr, glm::dvec3& ul, glm::dvec3& ur);
struct ShadowProjectionConstants {
std::array<glm::mat4, 8> m_shadow_projection;
std::array<float, 8> m_shadow_planes;
};
};

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,942 @@
#include "environment.h"
#include <fmt/format.h>
#include <nvrhi/utils.h>
#include "config.h"
#include "gbuffer.h"
#include "nvrenderer/nvrenderer.h"
#include "nvrendererbackend.h"
#include "simulationenvironment.h"
#include "sky.h"
int MaEnvironment::GetCurrentSetIndex() {
if (m_locked_set_index >= 0 && IsReady()) {
m_current_set_index = std::exchange(m_locked_set_index, -1);
}
return m_current_set_index;
}
nvrhi::BindingSetHandle MaEnvironment::GetBindingSet(int pass, int mip, int set,
NvGbuffer* gbuffer_cube) {
using namespace nvrhi;
static std::map<int, BindingSetHandle> cache{};
auto& handle =
cache[((set & 0xf) << 28) | ((pass & 0xfff) << 16) | (mip & 0xffff)];
if (!handle) {
switch (pass) {
case 1:
if (!mip) {
int previous_set = set - 1;
while (previous_set < 0)
previous_set += std::size(m_dynamic_envmap_diffuse);
handle = m_backend->GetDevice()->createBindingSet(
BindingSetDesc()
.addItem(BindingSetItem::Texture_SRV(0, m_skybox))
.addItem(BindingSetItem::Texture_SRV(
1, gbuffer_cube->m_gbuffer_diffuse, Format::UNKNOWN,
AllSubresources))
.addItem(BindingSetItem::Texture_SRV(
2, gbuffer_cube->m_gbuffer_normal, Format::UNKNOWN,
AllSubresources))
.addItem(BindingSetItem::Texture_SRV(
3, gbuffer_cube->m_gbuffer_params, Format::UNKNOWN,
AllSubresources))
.addItem(BindingSetItem::Texture_SRV(
4, gbuffer_cube->m_gbuffer_depth, Format::UNKNOWN,
AllSubresources))
.addItem(BindingSetItem::Texture_SRV(
5, m_dynamic_envmap_diffuse[previous_set],
Format::UNKNOWN, AllSubresources))
.addItem(BindingSetItem::Texture_SRV(13, m_sky_texture))
.addItem(BindingSetItem::Texture_SRV(14, m_aerial_lut))
.addItem(BindingSetItem::Texture_UAV(
0, m_dynamic_skybox[set], Format::UNKNOWN,
TextureSubresourceSet(
0, 1, 0, TextureSubresourceSet::AllArraySlices),
TextureDimension::Texture2DArray))
.addItem(BindingSetItem::Sampler(0, m_sampler_linear_clamp))
.addItem(BindingSetItem::Sampler(1, m_sampler_point_clamp))
.addItem(BindingSetItem::Sampler(13, m_sampler_linear_clamp))
.addItem(BindingSetItem::ConstantBuffer(
13, m_sky->m_sky_constants))
.addItem(BindingSetItem::ConstantBuffer(
1, m_face_inverse_projection_buffer))
.addItem(BindingSetItem::PushConstants(
0, sizeof(CubeLightingConstants))),
m_bl_gbuffer_lighting);
} else {
handle = m_backend->GetDevice()->createBindingSet(
BindingSetDesc()
.addItem(BindingSetItem::Texture_SRV(
1, m_dynamic_skybox[set], Format::UNKNOWN,
TextureSubresourceSet(
mip - 1, 1, 0,
TextureSubresourceSet::AllArraySlices)))
.addItem(BindingSetItem::Texture_UAV(
0, m_dynamic_skybox[set], Format::UNKNOWN,
TextureSubresourceSet(
mip, 1, 0, TextureSubresourceSet::AllArraySlices),
TextureDimension::Texture2DArray))
.addItem(BindingSetItem::Sampler(1, m_sampler_linear_clamp))
.addItem(BindingSetItem::PushConstants(
0, sizeof(EnvironmentFilterConstants))),
m_bl_diffuse_ibl);
}
break;
case 2:
handle = m_backend->GetDevice()->createBindingSet(
BindingSetDesc()
.addItem(BindingSetItem::Texture_SRV(
1, m_dynamic_skybox[set], Format::UNKNOWN,
TextureSubresourceSet(
0, TextureSubresourceSet::AllMipLevels, 0,
TextureSubresourceSet::AllArraySlices)))
.addItem(BindingSetItem::Texture_UAV(
0, m_dynamic_envmap_diffuse[set], Format::UNKNOWN,
AllSubresources, TextureDimension::Texture2DArray))
.addItem(BindingSetItem::Sampler(1, m_sampler_linear_clamp))
.addItem(BindingSetItem::PushConstants(
0, sizeof(EnvironmentFilterConstants))),
m_bl_diffuse_ibl);
break;
case 3:
handle = m_backend->GetDevice()->createBindingSet(
BindingSetDesc()
.addItem(BindingSetItem::Texture_SRV(
1, m_dynamic_skybox[set], Format::UNKNOWN,
TextureSubresourceSet(
0, TextureSubresourceSet::AllMipLevels, 0,
TextureSubresourceSet::AllArraySlices)))
.addItem(BindingSetItem::Texture_UAV(
0, m_dynamic_envmap_specular[set], Format::UNKNOWN,
TextureSubresourceSet(
mip, 1, 0, TextureSubresourceSet::AllArraySlices),
TextureDimension::Texture2DArray))
.addItem(BindingSetItem::Sampler(1, m_sampler_linear_clamp))
.addItem(BindingSetItem::PushConstants(
0, sizeof(EnvironmentFilterConstants))),
m_bl_diffuse_ibl);
break;
}
}
return handle;
}
MaEnvironment::MaEnvironment(NvRenderer* renderer)
: MaResourceRegistry(renderer),
m_backend(renderer->m_backend.get()),
m_gbuffer(renderer->m_gbuffer.get()),
m_sky(renderer->m_sky.get()) {}
void MaEnvironment::Init(const std::string& texture, float pre_exposure) {
InitResourceRegistry();
using namespace nvrhi;
m_cs_sample_equirectangular = m_backend->CreateShader(
"envmap_CSSampleEquirectangular", ShaderType::Compute);
m_cs_diffuse_ibl =
m_backend->CreateShader("envmap_CSDiffuseIBL", ShaderType::Compute);
m_cs_specular_ibl =
m_backend->CreateShader("envmap_CSSpecularIBL", ShaderType::Compute);
m_cs_generate_mip =
m_backend->CreateShader("envmap_CSGenerateCubeMip", ShaderType::Compute);
m_cs_integrate_brdf =
m_backend->CreateShader("envmap_CSIntegrateBRDF", ShaderType::Compute);
m_cs_gbuffer_lighting =
m_backend->CreateShader("gbuffer_cube_lighting", ShaderType::Compute);
m_face_inverse_projection_buffer = m_backend->GetDevice()->createBuffer(
nvrhi::utils::CreateStaticConstantBufferDesc(
sizeof(glm::mat4), "Inverse Face Projection Buffer")
.setInitialState(nvrhi::ResourceStates::ConstantBuffer)
.setKeepInitialState(true));
{
auto command_list = m_backend->GetDevice()->createCommandList();
glm::mat4 inverse_projection = glm::inverse(
glm::perspectiveFovRH_ZO(glm::radians(90.f), 1.f, 1.f, .1f,
Global.reflectiontune.range_instances));
command_list->open();
command_list->writeBuffer(m_face_inverse_projection_buffer,
&inverse_projection, sizeof(inverse_projection));
command_list->close();
m_backend->GetDevice()->executeCommandList(command_list);
}
m_bl_gbuffer_lighting = m_backend->GetDevice()->createBindingLayout(
BindingLayoutDesc()
.setVisibility(ShaderType::Compute)
.addItem(BindingLayoutItem::Texture_SRV(0))
.addItem(BindingLayoutItem::Texture_SRV(1))
.addItem(BindingLayoutItem::Texture_SRV(2))
.addItem(BindingLayoutItem::Texture_SRV(3))
.addItem(BindingLayoutItem::Texture_SRV(4))
.addItem(BindingLayoutItem::Texture_SRV(5))
.addItem(BindingLayoutItem::Texture_SRV(13))
.addItem(BindingLayoutItem::Texture_SRV(14))
.addItem(BindingLayoutItem::Texture_UAV(0))
.addItem(BindingLayoutItem::Sampler(0))
.addItem(BindingLayoutItem::Sampler(1))
.addItem(BindingLayoutItem::Sampler(13))
.addItem(BindingLayoutItem::VolatileConstantBuffer(13))
.addItem(BindingLayoutItem::ConstantBuffer(1))
.addItem(BindingLayoutItem::PushConstants(
0, sizeof(CubeLightingConstants))));
m_pso_gbuffer_lighting = m_backend->GetDevice()->createComputePipeline(
ComputePipelineDesc()
.addBindingLayout(m_bl_gbuffer_lighting)
.setComputeShader(m_cs_gbuffer_lighting));
m_bl_sample_equirectangular = m_backend->GetDevice()->createBindingLayout(
BindingLayoutDesc()
.setVisibility(ShaderType::Compute)
.addItem(BindingLayoutItem::Texture_SRV(0))
.addItem(BindingLayoutItem::Texture_UAV(0))
.addItem(BindingLayoutItem::Sampler(0))
.addItem(BindingLayoutItem::PushConstants(
0, sizeof(EnvironmentFilterConstants))));
m_pso_sample_equirectangular = m_backend->GetDevice()->createComputePipeline(
ComputePipelineDesc()
.addBindingLayout(m_bl_sample_equirectangular)
.setComputeShader(m_cs_sample_equirectangular));
m_bl_diffuse_ibl = m_backend->GetDevice()->createBindingLayout(
BindingLayoutDesc()
.setVisibility(ShaderType::Compute)
.addItem(BindingLayoutItem::Texture_SRV(1))
.addItem(BindingLayoutItem::Texture_UAV(0))
.addItem(BindingLayoutItem::Sampler(1))
.addItem(BindingLayoutItem::PushConstants(
0, sizeof(EnvironmentFilterConstants))));
m_pso_diffuse_ibl = m_backend->GetDevice()->createComputePipeline(
ComputePipelineDesc()
.addBindingLayout(m_bl_diffuse_ibl)
.setComputeShader(m_cs_diffuse_ibl));
m_pso_generate_mip = m_backend->GetDevice()->createComputePipeline(
ComputePipelineDesc()
.addBindingLayout(m_bl_diffuse_ibl)
.setComputeShader(m_cs_generate_mip));
m_pso_specular_ibl = m_backend->GetDevice()->createComputePipeline(
ComputePipelineDesc()
.addBindingLayout(m_bl_diffuse_ibl)
.setComputeShader(m_cs_specular_ibl));
m_bl_integrate_brdf = m_backend->GetDevice()->createBindingLayout(
BindingLayoutDesc()
.setVisibility(ShaderType::Compute)
.addItem(BindingLayoutItem::Texture_UAV(1))
.addItem(BindingLayoutItem::PushConstants(
0, sizeof(EnvironmentFilterConstants))));
m_pso_integrate_brdf = m_backend->GetDevice()->createComputePipeline(
ComputePipelineDesc()
.addBindingLayout(m_bl_integrate_brdf)
.setComputeShader(m_cs_integrate_brdf));
int skybox_width = NvRenderer::Config()->m_envmap_resolution;
int dynamic_skybox_width = NvRenderer::Config()->m_envmap_resolution;
int skybox_mips = static_cast<int>(floor(log2(skybox_width))) + 1;
int dynamic_skybox_mips =
static_cast<int>(floor(log2(dynamic_skybox_width))) + 1;
int diffuse_width = 64;
int specular_width = NvRenderer::Config()->m_envmap_resolution;
int dynamic_specular_width = NvRenderer::Config()->m_envmap_resolution;
int specular_mips = 6;
m_skybox = m_backend->GetDevice()->createTexture(
TextureDesc()
.setDimension(TextureDimension::TextureCube)
.setFormat(Format::R11G11B10_FLOAT)
.setArraySize(6)
.setWidth(skybox_width)
.setHeight(skybox_width)
.setMipLevels(skybox_mips)
.setIsUAV(true)
.setInitialState(ResourceStates::ShaderResource)
.setKeepInitialState(true)
.setDebugName("Skybox"));
m_dynamic_skybox[0] = m_backend->GetDevice()->createTexture(
TextureDesc()
.setDimension(TextureDimension::TextureCube)
.setFormat(Format::R11G11B10_FLOAT)
.setArraySize(6)
.setWidth(dynamic_skybox_width)
.setHeight(dynamic_skybox_width)
.setMipLevels(dynamic_skybox_mips)
.setIsUAV(true)
.setInitialState(ResourceStates::UnorderedAccess)
.setKeepInitialState(true)
.setDebugName("Dynamic Skybox"));
m_dynamic_skybox[1] = m_backend->GetDevice()->createTexture(
TextureDesc()
.setDimension(TextureDimension::TextureCube)
.setFormat(Format::R11G11B10_FLOAT)
.setArraySize(6)
.setWidth(dynamic_skybox_width)
.setHeight(dynamic_skybox_width)
.setMipLevels(dynamic_skybox_mips)
.setIsUAV(true)
.setInitialState(ResourceStates::UnorderedAccess)
.setKeepInitialState(true)
.setDebugName("Dynamic Skybox"));
m_sampler_linear_clamp_v_repeat_h = m_backend->GetDevice()->createSampler(
SamplerDesc()
.setAllAddressModes(SamplerAddressMode::Clamp)
.setAddressU(SamplerAddressMode::Repeat)
.setAllFilters(true));
m_sampler_linear_clamp = m_backend->GetDevice()->createSampler(
SamplerDesc()
.setAllAddressModes(SamplerAddressMode::Clamp)
.setAllFilters(true));
m_sampler_point_clamp = m_backend->GetDevice()->createSampler(
SamplerDesc()
.setAllAddressModes(SamplerAddressMode::Clamp)
.setAllFilters(false));
CommandListHandle command_list = m_backend->GetDevice()->createCommandList();
command_list->open();
RegisterResource(true, "sampler_linear_clamp_v_repeat_h",
m_sampler_linear_clamp_v_repeat_h, ResourceType::Sampler);
{
m_brdf_lut = m_backend->GetDevice()->createTexture(
TextureDesc()
.setFormat(Format::RG16_FLOAT)
.setDimension(TextureDimension::Texture2D)
.setWidth(512)
.setHeight(512)
.setMipLevels(1)
.setIsUAV(true)
.setInitialState(ResourceStates::ShaderResource)
.setKeepInitialState(true)
.setDebugName("BRDF LUT"));
RegisterResource(true, "env_brdf_lut", m_brdf_lut,
ResourceType::Texture_SRV);
BindingSetHandle binding_set = m_backend->GetDevice()->createBindingSet(
BindingSetDesc()
.addItem(BindingSetItem::Texture_UAV(1, m_brdf_lut))
.addItem(BindingSetItem::PushConstants(
0, sizeof(EnvironmentFilterConstants))),
m_bl_integrate_brdf);
ComputeState compute_state = ComputeState()
.setPipeline(m_pso_integrate_brdf)
.addBindingSet(binding_set);
int target_width = m_brdf_lut->getDesc().width;
int target_height = m_brdf_lut->getDesc().height;
command_list->setComputeState(compute_state);
EnvironmentFilterConstants filter_constants{};
filter_constants.m_sample_count = 4096;
command_list->setPushConstants(&filter_constants, sizeof(filter_constants));
command_list->dispatch((target_width + 31) / 32, (target_height + 31) / 32,
1);
}
{
m_envmap_diffuse = m_backend->GetDevice()->createTexture(
TextureDesc()
.setArraySize(6)
.setFormat(Format::R11G11B10_FLOAT)
.setDimension(TextureDimension::TextureCube)
.setWidth(diffuse_width)
.setHeight(diffuse_width)
.setMipLevels(1)
.setIsUAV(true)
.setInitialState(ResourceStates::ShaderResource)
.setKeepInitialState(true)
.setDebugName("Diffuse IBL"));
for (int i = 0; i < std::size(m_dynamic_envmap_diffuse); ++i) {
auto debug_name = fmt::format("Dynamic Diffuse IBL[{}]", i);
auto resource_name = fmt::format("env_dynamic_diffuse_{}", i);
m_dynamic_envmap_diffuse[i] = m_backend->GetDevice()->createTexture(
TextureDesc()
.setArraySize(6)
.setFormat(Format::R11G11B10_FLOAT)
.setDimension(TextureDimension::TextureCube)
.setWidth(diffuse_width)
.setHeight(diffuse_width)
.setMipLevels(1)
.setIsUAV(true)
.setInitialState(i ? ResourceStates::UnorderedAccess
: ResourceStates::ShaderResource)
.setKeepInitialState(true)
.setDebugName(debug_name.c_str()));
}
RegisterResource(true, "env_dynamic_diffuse", m_dynamic_envmap_diffuse[0],
ResourceType::Texture_SRV);
}
{
m_envmap_specular = m_backend->GetDevice()->createTexture(
TextureDesc()
.setArraySize(6)
.setFormat(Format::R11G11B10_FLOAT)
.setDimension(TextureDimension::TextureCube)
.setWidth(specular_width)
.setHeight(specular_width)
.setMipLevels(specular_mips)
.setIsUAV(true)
.setInitialState(ResourceStates::ShaderResource)
.setKeepInitialState(true)
.setDebugName("Specular IBL"));
for (int i = 0; i < std::size(m_dynamic_envmap_specular); ++i) {
auto debug_name = fmt::format("Dynamic Specular IBL[{}]", i);
auto resource_name = fmt::format("env_dynamic_specular_{}", i);
m_dynamic_envmap_specular[i] = m_backend->GetDevice()->createTexture(
TextureDesc()
.setArraySize(6)
.setFormat(Format::R11G11B10_FLOAT)
.setDimension(TextureDimension::TextureCube)
.setWidth(dynamic_specular_width)
.setHeight(dynamic_specular_width)
.setMipLevels(specular_mips)
.setIsUAV(true)
.setInitialState(i ? ResourceStates::UnorderedAccess
: ResourceStates::ShaderResource)
.setKeepInitialState(true)
.setDebugName(debug_name.c_str()));
}
RegisterResource(true, "env_dynamic_specular", m_dynamic_envmap_specular[0],
ResourceType::Texture_SRV);
}
m_sky_texture = m_backend->GetDevice()->createTexture(
TextureDesc(m_sky->m_aerial_lut->m_sky_texture->getDesc())
.setDebugName("Envmap Sky Texture"));
m_aerial_lut = m_backend->GetDevice()->createTexture(
TextureDesc(m_sky->m_aerial_lut->m_lut->getDesc())
.setDebugName("Envmap Aerial LUT"));
command_list->close();
m_backend->GetDevice()->executeCommandList(command_list);
m_environment_pass = std::make_shared<EnvironmentRenderPass>(this);
m_environment_pass->Init();
}
void MaEnvironment::Render(nvrhi::ICommandList* command_list,
glm::dmat4 const& jitter,
glm::dmat4 const& projection, glm::dmat4 const& view,
glm::dmat4 const& previous_view_projection) {
m_environment_pass->UpdateConstants(command_list, jitter, projection, view,
previous_view_projection);
m_environment_pass->Render(command_list);
}
void MaEnvironment::Filter(uint64_t sky_instance_id, NvGbuffer* gbuffer_cube) {
using namespace nvrhi;
int pixels_per_pass_sqr = m_pixels_per_pass * m_pixels_per_pass;
int target_set_index = 1;
int source_set_index = 0;
static uint64_t prev_instance = -1;
if (filter_step == 0) {
if (!IsReady()) {
return;
}
// m_locked_set_index = (GetCurrentSetIndex() + 1) % 2;
m_ready_handle = m_backend->GetDevice()->createEventQuery();
// CommandListHandle transition_command_list =
// m_backend->GetDevice()->createCommandList();
// transition_command_list->open();
//
// transition_command_list->setTextureState(m_sky->m_aerial_lut->m_sky_texture,
// nvrhi::AllSubresources,
// nvrhi::ResourceStates::Common);
// transition_command_list->setTextureState(m_sky->m_aerial_lut->m_lut,
// nvrhi::AllSubresources,
// nvrhi::ResourceStates::Common);
//
// transition_command_list->close();
// auto transition_id =
// m_backend->GetDevice()->executeCommandList(transition_command_list);
//
// m_backend->GetDevice()->queueWaitForCommandList(
// nvrhi::CommandQueue::Copy, nvrhi::CommandQueue::Graphics,
// transition_id);
CommandListHandle command_list = m_backend->GetDevice()->createCommandList(
CommandListParameters()
.setQueueType(CommandQueue::Graphics)
.setEnableImmediateExecution(false));
command_list->open();
{
auto diff_desc = m_sky_texture->getDesc();
for (int i = 0; i < diff_desc.arraySize; ++i) {
for (int j = 0; j < diff_desc.mipLevels; ++j) {
auto slice = nvrhi::TextureSlice()
.setArraySlice(i)
.setMipLevel(j)
.setWidth(-1)
.setHeight(-1)
.resolve(diff_desc);
command_list->copyTexture(m_sky_texture, slice,
m_sky->m_aerial_lut->m_sky_texture, slice);
}
}
}
{
auto diff_desc = m_aerial_lut->getDesc();
for (int i = 0; i < diff_desc.arraySize; ++i) {
for (int j = 0; j < diff_desc.mipLevels; ++j) {
auto slice = nvrhi::TextureSlice()
.setArraySlice(i)
.setMipLevel(j)
.setWidth(-1)
.setHeight(-1)
.resolve(diff_desc);
command_list->copyTexture(m_aerial_lut, slice,
m_sky->m_aerial_lut->m_lut, slice);
}
}
}
// auto diff_desc = m_dynamic_envmap_diffuse[0]->getDesc();
// for (int i = 0; i < 6; ++i) {
// for (int j = 0; j < diff_desc.mipLevels; ++j) {
// auto slice = nvrhi::TextureSlice()
// .setArraySlice(i)
// .setMipLevel(j)
// .setWidth(-1)
// .setHeight(-1)
// .resolve(diff_desc);
// command_list->copyTexture(m_dynamic_envmap_diffuse[0], slice,
// m_dynamic_envmap_diffuse[1], slice);
// }
// }
//
// auto spec_desc = m_dynamic_envmap_specular[0]->getDesc();
// for (int i = 0; i < 6; ++i) {
// for (int j = 0; j < spec_desc.mipLevels; ++j) {
// auto slice = nvrhi::TextureSlice()
// .setArraySlice(i)
// .setMipLevel(j)
// .setWidth(-1)
// .setHeight(-1)
// .resolve(spec_desc);
// command_list->copyTexture(m_dynamic_envmap_specular[0], slice,
// m_dynamic_envmap_specular[1], slice);
// }
//}
// command_list->setTextureState(m_sky_texture, nvrhi::AllSubresources,
// nvrhi::ResourceStates::Common);
// command_list->setTextureState(m_aerial_lut, nvrhi::AllSubresources,
// nvrhi::ResourceStates::Common);
command_list->close();
prev_instance = m_backend->GetDevice()->executeCommandList(
command_list, nvrhi::CommandQueue::Graphics);
// CommandListHandle transition_command_list =
// m_backend->GetDevice()->createCommandList();
// transition_command_list->open();
//
// transition_command_list->setTextureState(m_skybox, AllSubresources,
// ResourceStates::UnorderedAccess);
// transition_command_list->setTextureState(m_dynamic_skybox[target_set_index],
// AllSubresources,
// ResourceStates::UnorderedAccess);
// transition_command_list->setTextureState(
// m_dynamic_envmap_diffuse[target_set_index], AllSubresources,
// ResourceStates::UnorderedAccess);
// transition_command_list->setTextureState(
// m_dynamic_envmap_specular[target_set_index], AllSubresources,
// ResourceStates::UnorderedAccess);
// transition_command_list->setTextureState(
// m_dynamic_envmap_diffuse[source_set_index], AllSubresources,
// ResourceStates::ShaderResource);
// transition_command_list->setTextureState(
// m_dynamic_envmap_specular[source_set_index], AllSubresources,
// ResourceStates::ShaderResource);
// transition_command_list->setTextureState(gbuffer_cube->m_gbuffer_diffuse,
// AllSubresources,
// ResourceStates::ShaderResource);
// transition_command_list->setTextureState(gbuffer_cube->m_gbuffer_normal,
// AllSubresources,
// ResourceStates::ShaderResource);
// transition_command_list->setTextureState(gbuffer_cube->m_gbuffer_params,
// AllSubresources,
// ResourceStates::ShaderResource);
// transition_command_list->setTextureState(gbuffer_cube->m_gbuffer_depth,
// AllSubresources,
// ResourceStates::ShaderResource);
// transition_command_list->commitBarriers();
//
// transition_command_list->close();
// auto instance =
// m_backend->GetDevice()->executeCommandList(transition_command_list);
// m_backend->GetDevice()->queueWaitForCommandList(
// nvrhi::CommandQueue::Compute, nvrhi::CommandQueue::Graphics,
// instance);
++filter_step;
mip = 0;
return;
}
if (filter_step == 1) {
// CommandListHandle command_list_transition =
// m_backend->GetDevice()->createCommandList();
// command_list_transition->open();
//
// command_list_transition->setTextureState(
// m_sky->m_aerial_lut->m_sky_texture, nvrhi::AllSubresources,
// nvrhi::ResourceStates::ShaderResource);
//
// command_list_transition->close();
// auto instance =
// m_backend->GetDevice()->executeCommandList(command_list_transition);
if (prev_instance != -1) {
// m_backend->GetDevice()->queueWaitForCommandList(
// nvrhi::CommandQueue::Compute, nvrhi::CommandQueue::Graphics,
// prev_instance);
// prev_instance = -1;
}
CommandListHandle command_list = m_backend->GetDevice()->createCommandList(
CommandListParameters()
.setQueueType(CommandQueue::Graphics)
.setEnableImmediateExecution(false));
// m_backend->GetDevice()->queueWaitForCommandList(
// nvrhi::CommandQueue::Compute, nvrhi::CommandQueue::Graphics,
// instance);
command_list->open();
while (true) {
int target_width = std::max(
m_dynamic_skybox[target_set_index]->getDesc().width >> mip, 1u);
int target_height = std::max(
m_dynamic_skybox[target_set_index]->getDesc().height >> mip, 1u);
int pixels_x = std::min(m_pixels_per_pass, target_width - x);
int pixels_y = std::min(m_pixels_per_pass, target_height - y);
if (pixels_x * pixels_y > pixels_per_pass_sqr) {
break;
}
pixels_per_pass_sqr -= pixels_x * pixels_y;
if (mip >= m_dynamic_skybox[target_set_index]->getDesc().mipLevels) {
break;
}
if (!mip) m_sky->UpdateConstants(command_list);
ComputeState compute_state;
compute_state
.setPipeline(mip ? m_pso_generate_mip : m_pso_gbuffer_lighting)
.addBindingSet(
GetBindingSet(filter_step, mip, target_set_index, gbuffer_cube));
command_list->setComputeState(compute_state);
if (!mip) {
CubeLightingConstants filter_constants{};
const auto& daylight = Global.DayLight;
filter_constants.m_offset_x = x;
filter_constants.m_offset_y = y;
filter_constants.m_offset_z = 0;
filter_constants.m_light_direction = -daylight.direction;
filter_constants.m_height = Global.pCamera.Pos.y;
m_sky->CalcLighting(filter_constants.m_light_direction,
filter_constants.m_light_color);
filter_constants.m_light_color = glm::vec4(m_sky->CalcSunColor(), 1.f);
command_list->setPushConstants(&filter_constants,
sizeof(filter_constants));
} else {
EnvironmentFilterConstants filter_constants{};
filter_constants.m_offset_x = x;
filter_constants.m_offset_y = y;
filter_constants.m_offset_z = 0;
command_list->setPushConstants(&filter_constants,
sizeof(filter_constants));
}
command_list->dispatch((pixels_x + 31) / 32, (pixels_y + 31) / 32, 6);
x += m_pixels_per_pass;
if (x >= target_width) {
x = 0;
y += m_pixels_per_pass;
}
if (y >= target_height) {
y = 0;
++mip;
}
}
if (mip >= m_dynamic_skybox[target_set_index]->getDesc().mipLevels) {
mip = 0;
++filter_step;
}
command_list->close();
m_backend->GetDevice()->executeCommandList(command_list,
nvrhi::CommandQueue::Graphics);
}
if (filter_step == 2) {
CommandListHandle command_list = m_backend->GetDevice()->createCommandList(
CommandListParameters()
.setQueueType(CommandQueue::Graphics)
.setEnableImmediateExecution(false));
command_list->open();
ComputeState compute_state =
ComputeState()
.setPipeline(m_pso_diffuse_ibl)
.addBindingSet(
GetBindingSet(filter_step, 0, target_set_index, gbuffer_cube));
int target_width =
m_dynamic_envmap_diffuse[target_set_index]->getDesc().width;
int target_height =
m_dynamic_envmap_diffuse[target_set_index]->getDesc().height;
command_list->setComputeState(compute_state);
EnvironmentFilterConstants filter_constants{};
filter_constants.m_sample_count = 32;
filter_constants.m_source_width =
m_dynamic_skybox[target_set_index]->getDesc().width;
filter_constants.m_offset_x = 0;
filter_constants.m_offset_y = 0;
filter_constants.m_offset_z = 0;
filter_constants.m_mip_bias = 1.;
command_list->setPushConstants(&filter_constants, sizeof(filter_constants));
command_list->dispatch((target_width + 31) / 32, (target_height + 31) / 32,
6);
command_list->close();
m_backend->GetDevice()->executeCommandList(command_list,
nvrhi::CommandQueue::Graphics);
++filter_step;
}
if (filter_step == 3) {
CommandListHandle command_list = m_backend->GetDevice()->createCommandList(
CommandListParameters()
.setQueueType(CommandQueue::Graphics)
.setEnableImmediateExecution(false));
command_list->open();
while (true) {
int target_width = std::max(
1u,
m_dynamic_envmap_specular[target_set_index]->getDesc().width >> mip);
int target_height = std::max(
1u,
m_dynamic_envmap_specular[target_set_index]->getDesc().height >> mip);
int pixels_x = std::min(m_pixels_per_pass, target_width - x);
int pixels_y = std::min(m_pixels_per_pass, target_height - y);
if (pixels_x * pixels_y > pixels_per_pass_sqr) {
break;
}
if (mip >=
m_dynamic_envmap_specular[target_set_index]->getDesc().mipLevels) {
break;
}
pixels_per_pass_sqr -= pixels_x * pixels_y;
ComputeState compute_state =
ComputeState()
.setPipeline(m_pso_specular_ibl)
.addBindingSet(GetBindingSet(filter_step, mip, target_set_index,
gbuffer_cube));
EnvironmentFilterConstants filter_constants{};
filter_constants.m_sample_count = 32;
filter_constants.m_roughness =
glm::pow(static_cast<double>(mip) /
(m_dynamic_envmap_specular[target_set_index]
->getDesc()
.mipLevels -
1.),
2.);
filter_constants.m_source_width =
m_dynamic_skybox[target_set_index]->getDesc().width;
filter_constants.m_mip_bias =
mip ? 1.
: log2(target_width /
static_cast<double>(filter_constants.m_source_width));
filter_constants.m_offset_x = x;
filter_constants.m_offset_y = y;
filter_constants.m_offset_z = 0;
command_list->setComputeState(compute_state);
command_list->setPushConstants(&filter_constants,
sizeof(filter_constants));
command_list->dispatch((pixels_x + 31) / 32, (pixels_y + 31) / 32, 6);
x += m_pixels_per_pass;
if (x >= target_width) {
x = 0;
y += m_pixels_per_pass;
}
if (y >= target_height) {
y = 0;
++mip;
}
}
command_list->close();
m_backend->GetDevice()->executeCommandList(command_list,
nvrhi::CommandQueue::Graphics);
if (mip >=
m_dynamic_envmap_specular[target_set_index]->getDesc().mipLevels) {
mip = 0;
filter_step = 4;
}
}
if (filter_step == 4) {
CommandListHandle command_list = m_backend->GetDevice()->createCommandList(
CommandListParameters()
.setQueueType(CommandQueue::Graphics)
.setEnableImmediateExecution(false));
command_list->open();
auto diff_desc = m_dynamic_envmap_diffuse[0]->getDesc();
for (int i = 0; i < 6; ++i) {
for (int j = 0; j < diff_desc.mipLevels; ++j) {
auto slice = nvrhi::TextureSlice()
.setArraySlice(i)
.setMipLevel(j)
.setWidth(-1)
.setHeight(-1)
.resolve(diff_desc);
command_list->copyTexture(m_dynamic_envmap_diffuse[0], slice,
m_dynamic_envmap_diffuse[1], slice);
}
}
auto spec_desc = m_dynamic_envmap_specular[0]->getDesc();
for (int i = 0; i < 6; ++i) {
for (int j = 0; j < spec_desc.mipLevels; ++j) {
auto slice = nvrhi::TextureSlice()
.setArraySlice(i)
.setMipLevel(j)
.setWidth(-1)
.setHeight(-1)
.resolve(spec_desc);
command_list->copyTexture(m_dynamic_envmap_specular[0], slice,
m_dynamic_envmap_specular[1], slice);
}
}
command_list->close();
m_backend->GetDevice()->executeCommandList(command_list,
nvrhi::CommandQueue::Graphics);
filter_step = 0;
m_backend->GetDevice()->setEventQuery(m_ready_handle,
nvrhi::CommandQueue::Graphics);
}
}
bool MaEnvironment::IsReady() const {
return !m_ready_handle ||
m_backend->GetDevice()->pollEventQuery(m_ready_handle);
}
EnvironmentRenderPass::EnvironmentRenderPass(MaEnvironment* environment)
: FullScreenPass(environment->m_backend),
m_environment(environment),
m_gbuffer(environment->m_gbuffer),
m_skybox_texture(environment->m_skybox) {}
void EnvironmentRenderPass::Init() {
m_environment_constants = m_backend->GetDevice()->createBuffer(
nvrhi::utils::CreateVolatileConstantBufferDesc(
sizeof(EnvironmentConstants), "Skybox Draw Constants", 16)
.setInitialState(nvrhi::ResourceStates::ConstantBuffer)
.setKeepInitialState(true));
m_binding_layout = m_backend->GetDevice()->createBindingLayout(
nvrhi::BindingLayoutDesc()
.setVisibility(nvrhi::ShaderType::Pixel)
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(0))
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(13))
.addItem(nvrhi::BindingLayoutItem::Sampler(0))
.addItem(nvrhi::BindingLayoutItem::Sampler(13))
.addItem(nvrhi::BindingLayoutItem::VolatileConstantBuffer(0)));
auto sampler = m_backend->GetDevice()->createSampler(
nvrhi::SamplerDesc()
.setAllAddressModes(nvrhi::SamplerAddressMode::Clamp)
.setAllFilters(true));
for (int i = 0; i < 2; ++i) {
m_binding_set[i] = m_backend->GetDevice()->createBindingSet(
nvrhi::BindingSetDesc()
.addItem(
nvrhi::BindingSetItem::Texture_SRV(0, m_environment->m_skybox))
.addItem(nvrhi::BindingSetItem::Texture_SRV(
13, m_environment->m_sky->m_aerial_lut->m_sky_texture))
.addItem(nvrhi::BindingSetItem::Sampler(
0, m_environment->m_sampler_linear_clamp))
.addItem(nvrhi::BindingSetItem::Sampler(
13, m_environment->m_sampler_linear_clamp_v_repeat_h))
.addItem(nvrhi::BindingSetItem::ConstantBuffer(
0, m_environment_constants)),
m_binding_layout);
}
m_pixel_shader = m_backend->CreateShader("skybox", nvrhi::ShaderType::Pixel);
FullScreenPass::Init();
}
void EnvironmentRenderPass::CreatePipelineDesc(
nvrhi::GraphicsPipelineDesc& pipeline_desc) {
FullScreenPass::CreatePipelineDesc(pipeline_desc);
pipeline_desc.addBindingLayout(m_binding_layout);
pipeline_desc.setPixelShader(m_pixel_shader);
pipeline_desc.renderState.setDepthStencilState(
nvrhi::DepthStencilState()
.enableDepthTest()
.disableDepthWrite()
.setDepthFunc(nvrhi::ComparisonFunc::GreaterOrEqual)
.disableStencil());
}
void EnvironmentRenderPass::UpdateConstants(
nvrhi::ICommandList* command_list, glm::dmat4 const& jitter,
glm::dmat4 const& projection, glm::dmat4 const& view,
glm::dmat4 const& previous_view_projection) {
EnvironmentConstants constants{};
constants.m_reproject_matrix =
glm::inverse(projection * view) * previous_view_projection;
constants.m_inverse_view_projection =
glm::inverse(jitter * projection * glm::dmat4(glm::dmat3(view)));
constants.m_sun_direction = simulation::Environment.sun().getDirection();
constants.m_moon_direction = simulation::Environment.moon().getDirection();
constants.m_height = Global.pCamera.Pos.y;
command_list->writeBuffer(m_environment_constants, &constants,
sizeof(constants));
}
void EnvironmentRenderPass::Render(nvrhi::ICommandList* command_list) {
nvrhi::GraphicsState graphics_state;
InitState(graphics_state);
graphics_state.addBindingSet(
m_binding_set[m_environment->GetCurrentSetIndex()]);
command_list->setGraphicsState(graphics_state);
Draw(command_list);
}
nvrhi::IFramebuffer* EnvironmentRenderPass::GetFramebuffer() {
return m_gbuffer->m_framebuffer;
}

View File

@@ -0,0 +1,140 @@
#pragma once
#include "fullscreenpass.h"
#include "nvrenderer/resource_registry.h"
#include "nvrhi/nvrhi.h"
struct MaEnvironment : public MaResourceRegistry {
nvrhi::TextureHandle m_skybox;
nvrhi::TextureHandle m_dynamic_skybox[2];
nvrhi::TextureHandle m_envmap_specular;
nvrhi::TextureHandle m_dynamic_envmap_specular[2];
nvrhi::TextureHandle m_envmap_diffuse;
nvrhi::TextureHandle m_dynamic_envmap_diffuse[2];
nvrhi::TextureHandle m_hdri;
nvrhi::TextureHandle m_brdf_lut;
nvrhi::TextureHandle m_sky_texture;
nvrhi::TextureHandle m_aerial_lut;
nvrhi::SamplerHandle m_sampler_linear_clamp_v_repeat_h;
nvrhi::SamplerHandle m_sampler_linear_clamp;
nvrhi::SamplerHandle m_sampler_point_clamp;
nvrhi::ShaderHandle m_cs_sample_equirectangular;
nvrhi::ComputePipelineHandle m_pso_sample_equirectangular;
nvrhi::BindingLayoutHandle m_bl_sample_equirectangular;
nvrhi::ShaderHandle m_cs_gbuffer_lighting;
nvrhi::ComputePipelineHandle m_pso_gbuffer_lighting;
nvrhi::BindingLayoutHandle m_bl_gbuffer_lighting;
nvrhi::ShaderHandle m_cs_diffuse_ibl;
nvrhi::ComputePipelineHandle m_pso_diffuse_ibl;
nvrhi::BindingLayoutHandle m_bl_diffuse_ibl;
nvrhi::ShaderHandle m_cs_specular_ibl;
nvrhi::ComputePipelineHandle m_pso_specular_ibl;
nvrhi::ShaderHandle m_cs_generate_mip;
nvrhi::ComputePipelineHandle m_pso_generate_mip;
nvrhi::ShaderHandle m_cs_integrate_brdf;
nvrhi::ComputePipelineHandle m_pso_integrate_brdf;
nvrhi::BindingLayoutHandle m_bl_integrate_brdf;
nvrhi::EventQueryHandle m_ready_handle;
nvrhi::BufferHandle m_face_inverse_projection_buffer;
class NvRendererBackend* m_backend;
struct NvGbuffer* m_gbuffer;
struct Sky* m_sky;
int filter_step = 0;
int mip = 0;
int x = 0;
int y = 0;
int m_current_set_index = 0;
int m_locked_set_index = 0;
int GetCurrentSetIndex();
std::shared_ptr<struct EnvironmentRenderPass> m_environment_pass;
const int m_pixels_per_pass = 256;
nvrhi::BindingSetHandle GetBindingSet(int pass, int mip, int set,
NvGbuffer* gbuffer_cube);
struct EnvironmentFilterConstants {
EnvironmentFilterConstants() {
m_pre_exposure_mul = 1.f;
m_roughness = 0.f;
m_sample_count = 32.f;
m_source_width = 2048.f;
m_mip_bias = 0.f;
m_offset_x = 0;
m_offset_y = 0;
m_offset_z = 0;
}
float m_pre_exposure_mul;
float m_roughness;
float m_sample_count;
float m_source_width;
float m_mip_bias;
uint32_t m_offset_x;
uint32_t m_offset_y;
uint32_t m_offset_z;
};
struct CubeLightingConstants {
uint32_t m_offset_x;
uint32_t m_offset_y;
uint32_t m_offset_z;
uint32_t m_unused;
glm::vec3 m_light_direction;
float m_height;
glm::vec3 m_light_color;
};
MaEnvironment(class NvRenderer* renderer);
void Init(const std::string& texture, float pre_exposure);
void Render(nvrhi::ICommandList* command_list, glm::dmat4 const& jitter,
glm::dmat4 const& projection, glm::dmat4 const& view,
glm::dmat4 const& previous_view_projection);
void Filter(uint64_t sky_instance_id, NvGbuffer* gbuffer_cube);
bool IsReady() const;
};
struct EnvironmentRenderPass : public FullScreenPass {
EnvironmentRenderPass(MaEnvironment* environment);
struct EnvironmentConstants {
glm::mat4 m_inverse_view_projection;
glm::mat4 m_reproject_matrix;
glm::vec3 m_sun_direction;
float m_height;
glm::vec3 m_moon_direction;
};
virtual void Init() override;
virtual void CreatePipelineDesc(
nvrhi::GraphicsPipelineDesc& pipeline_desc) override;
void UpdateConstants(nvrhi::ICommandList* command_list,
glm::dmat4 const& jitter, glm::dmat4 const& projection,
glm::dmat4 const& view,
glm::dmat4 const& previous_view_projection);
virtual void Render(nvrhi::ICommandList* command_list) override;
virtual nvrhi::IFramebuffer* GetFramebuffer() override;
struct NvGbuffer* m_gbuffer;
MaEnvironment* m_environment;
nvrhi::BufferHandle m_environment_constants;
nvrhi::TextureHandle m_skybox_texture;
nvrhi::BindingLayoutHandle m_binding_layout;
nvrhi::BindingSetHandle m_binding_set[2];
nvrhi::ShaderHandle m_pixel_shader;
nvrhi::FramebufferHandle m_framebuffer;
};

View File

@@ -0,0 +1,201 @@
#include "fsr.h"
#if LIBMANUL_WITH_D3D12
#include <ffx-fsr2-api/dx12/ffx_fsr2_dx12.h>
#include "nvrenderer_d3d12.h"
#endif
#if LIBMANUL_WITH_VULKAN
#include <ffx-fsr2-api/vk/ffx_fsr2_vk.h>
#include <nvrhi/vulkan.h>
#include "nvrenderer_vulkan.h"
#endif
#include "Globals.h"
#include "gbuffer.h"
#include "gbufferblitpass.h"
NvFSR::NvFSR(NvRenderer* renderer)
: m_backend(renderer->m_backend.get()),
m_inputpass(renderer->m_gbuffer_blit.get()),
m_gbuffer(renderer->m_gbuffer.get()) {}
void OnFsrMessage(FfxFsr2MsgType type, const wchar_t* msg) {
//__debugbreak();
}
void NvFSR::Init() {
m_width = m_backend->GetCurrentFramebuffer()->getFramebufferInfo().width;
m_height = m_backend->GetCurrentFramebuffer()->getFramebufferInfo().height;
m_input_width = m_gbuffer->m_framebuffer->getFramebufferInfo().width;
m_input_height = m_gbuffer->m_framebuffer->getFramebufferInfo().height;
m_color = m_inputpass->m_output;
m_depth = m_gbuffer->m_gbuffer_depth;
m_motion = m_gbuffer->m_gbuffer_motion;
m_output = m_backend->GetDevice()->createTexture(
nvrhi::TextureDesc()
.setFormat(nvrhi::Format::RGBA16_FLOAT)
.setWidth(m_width)
.setHeight(m_height)
.setIsUAV(true)
.setDebugName("FSR Output")
.setInitialState(nvrhi::ResourceStates::ShaderResource)
.setKeepInitialState(true));
m_auto_exposure_buffer = m_backend->GetDevice()->createTexture(
nvrhi::TextureDesc()
.setFormat(nvrhi::Format::RG32_FLOAT)
.setWidth(1)
.setHeight(1)
.setIsUAV(true)
.setDebugName("FSR Auto Exposure Buffer")
.setInitialState(nvrhi::ResourceStates::UnorderedAccess)
.setKeepInitialState(true));
auto device = m_backend->GetDevice();
FfxFsr2ContextDescription context_description{};
switch (device->getGraphicsAPI()) {
#if LIBMANUL_WITH_D3D12
case nvrhi::GraphicsAPI::D3D12:
m_scratch_buffer.resize(ffxFsr2GetScratchMemorySizeDX12());
ffxFsr2GetInterfaceDX12(&m_fsr2_interface,
m_backend->GetDevice()->getNativeObject(
nvrhi::ObjectTypes::D3D12_Device),
m_scratch_buffer.data(), m_scratch_buffer.size());
context_description.device = m_backend->GetDevice()->getNativeObject(
nvrhi::ObjectTypes::D3D12_Device);
break;
#endif
#if LIBMANUL_WITH_VULKAN
case nvrhi::GraphicsAPI::VULKAN:
m_scratch_buffer.resize(
ffxFsr2GetScratchMemorySizeVK(m_backend->GetDevice()->getNativeObject(
nvrhi::ObjectTypes::VK_PhysicalDevice)));
ffxFsr2GetInterfaceVK(&m_fsr2_interface, m_scratch_buffer.data(),
m_scratch_buffer.size(),
m_backend->GetDevice()->getNativeObject(
nvrhi::ObjectTypes::VK_PhysicalDevice),
dynamic_cast<NvRendererBackend_Vulkan*>(m_backend)
->GetDeviceProcAddr());
context_description.device =
ffxGetDeviceVK(m_backend->GetDevice()->getNativeObject(
nvrhi::ObjectTypes::VK_Device));
break;
#endif
default:
break;
}
context_description.callbacks = m_fsr2_interface;
context_description.displaySize.width = m_width;
context_description.displaySize.height = m_height;
context_description.maxRenderSize.width = 3440;
context_description.maxRenderSize.height = 1440;
context_description.flags = // FFX_FSR2_ENABLE_DEBUG_CHECKING |
FFX_FSR2_ENABLE_AUTO_EXPOSURE | FFX_FSR2_ENABLE_HIGH_DYNAMIC_RANGE |
FFX_FSR2_ENABLE_DEPTH_INVERTED;
context_description.fpMessage = &OnFsrMessage;
ffxFsr2ContextCreate(&m_fsr2_context, &context_description);
}
void NvFSR::Render(nvrhi::ICommandList* command_list, double far_plane,
double near_plane, double fov) {
FfxFsr2DispatchDescription dispatch_description{};
switch (m_backend->GetDevice()->getGraphicsAPI()) {
#if LIBMANUL_WITH_D3D12
case nvrhi::GraphicsAPI::D3D12:
dispatch_description.commandList =
ffxGetCommandListDX12(command_list->getNativeObject(
nvrhi::ObjectTypes::D3D12_GraphicsCommandList));
break;
#endif
#if LIBMANUL_WITH_VULKAN
case nvrhi::GraphicsAPI::VULKAN:
dispatch_description.commandList = ffxGetCommandListVK(
command_list->getNativeObject(nvrhi::ObjectTypes::VK_CommandBuffer));
break;
#endif
default:
break;
}
dispatch_description.color = GetFfxResource(m_color, L"FSR2 Input Color");
dispatch_description.depth = GetFfxResource(m_depth, L"FSR2 Input Depth");
dispatch_description.motionVectors =
GetFfxResource(m_motion, L"FSR2 Input Motion");
dispatch_description.exposure =
GetFfxResource(nullptr, L"FSR2 Empty Auto Exposure");
dispatch_description.reactive =
GetFfxResource(nullptr, L"FSR2 Empty Input Reactive Map");
dispatch_description.transparencyAndComposition =
GetFfxResource(nullptr, L"FSR2 Empty Transparency And Composition Map");
dispatch_description.output = GetFfxResource(
m_output, L"FSR2 Output", FFX_RESOURCE_STATE_UNORDERED_ACCESS);
dispatch_description.jitterOffset.x = m_current_jitter.x;
dispatch_description.jitterOffset.y = m_current_jitter.y;
dispatch_description.motionVectorScale.x = static_cast<float>(m_input_width);
dispatch_description.motionVectorScale.y = static_cast<float>(m_input_height);
dispatch_description.reset = false;
dispatch_description.enableSharpening = true;
dispatch_description.sharpness = .4;
dispatch_description.frameTimeDelta = 10.f;
dispatch_description.preExposure = 1.0f;
dispatch_description.renderSize.width = m_input_width;
dispatch_description.renderSize.height = m_input_height;
dispatch_description.cameraFar = far_plane;
dispatch_description.cameraNear = near_plane;
dispatch_description.cameraFovAngleVertical = fov;
ffxFsr2ContextDispatch(&m_fsr2_context, &dispatch_description);
command_list->clearState();
}
FfxResource NvFSR::GetFfxResource(nvrhi::ITexture* texture, wchar_t const* name,
FfxResourceStates state) {
switch (m_backend->GetDevice()->getGraphicsAPI()) {
#if LIBMANUL_WITH_D3D12
case nvrhi::GraphicsAPI::D3D12:
if (!texture) {
return ffxGetResourceDX12(&m_fsr2_context, nullptr, name, state);
}
return ffxGetResourceDX12(
&m_fsr2_context,
texture->getNativeObject(nvrhi::ObjectTypes::D3D12_Resource), name,
state);
#endif
#if LIBMANUL_WITH_VULKAN
case nvrhi::GraphicsAPI::VULKAN: {
if (!texture) {
return ffxGetTextureResourceVK(&m_fsr2_context, nullptr, nullptr, 1, 1,
VK_FORMAT_UNDEFINED, name, state);
}
const auto& desc = texture->getDesc();
return ffxGetTextureResourceVK(
&m_fsr2_context,
texture->getNativeObject(nvrhi::ObjectTypes::VK_Image),
texture->getNativeView(nvrhi::ObjectTypes::VK_ImageView), desc.width,
desc.height, nvrhi::vulkan::convertFormat(desc.format), name, state);
}
#endif
default:
return {};
}
}
void NvFSR::BeginFrame() {
++m_current_frame;
ComputeJitter();
}
glm::dmat4 NvFSR::GetJitterMatrix() const {
return glm::translate(glm::dvec3(2. * m_current_jitter.x / m_input_width,
-2. * m_current_jitter.y / m_input_height,
0.));
}
void NvFSR::ComputeJitter() {
int phase_count = ffxFsr2GetJitterPhaseCount(m_input_width, m_width);
ffxFsr2GetJitterOffset(&m_current_jitter.x, &m_current_jitter.y,
m_current_frame % phase_count, phase_count);
}

View File

@@ -0,0 +1,45 @@
#pragma once
#include <ffx-fsr2-api/ffx_fsr2.h>
#include <glm/glm.hpp>
#include "nvrendererbackend.h"
struct NvFSR {
NvFSR(class NvRenderer* renderer);
void Init();
void Render(nvrhi::ICommandList* command_list, double far_plane,
double near_plane, double fov);
FfxFsr2Interface m_fsr2_interface;
FfxFsr2Context m_fsr2_context;
std::string m_scratch_buffer;
nvrhi::ITexture* m_color;
nvrhi::ITexture* m_auto_exposure_buffer;
nvrhi::ITexture* m_depth;
nvrhi::ITexture* m_motion;
nvrhi::TextureHandle m_output;
FfxResource GetFfxResource(
nvrhi::ITexture* texture, wchar_t const* name = nullptr,
FfxResourceStates state = FFX_RESOURCE_STATE_COMPUTE_READ);
int m_input_width;
int m_input_height;
int m_width;
int m_height;
glm::vec2 m_current_jitter;
class NvRendererBackend* m_backend;
struct NvGbuffer* m_gbuffer;
struct GbufferBlitPass* m_inputpass;
struct MaAutoExposure* m_auto_exposure;
size_t m_current_frame = 0;
void BeginFrame();
glm::dmat4 GetJitterMatrix() const;
void ComputeJitter();
};

View File

@@ -0,0 +1,96 @@
#include "nvrenderer/nvrenderer.h"
#include "fullscreenpass.h"
#include "nvrendererbackend.h"
FullScreenPass::FullScreenPass(NvRendererBackend* backend)
: m_backend(backend) {}
nvrhi::IShader* FullScreenPass::GetVertexShader() {
static nvrhi::ShaderHandle vertex_shader = nullptr;
if (!vertex_shader)
vertex_shader = m_backend->CreateShader("fx_vertex", nvrhi::ShaderType::Vertex);
return vertex_shader;
}
nvrhi::IInputLayout* FullScreenPass::GetInputLayout() {
static nvrhi::InputLayoutHandle input_layout = nullptr;
if (!input_layout) {
nvrhi::VertexAttributeDesc attribute_desc =
nvrhi::VertexAttributeDesc()
.setName("Position")
.setElementStride(sizeof(glm::vec2))
.setOffset(0)
.setFormat(nvrhi::Format::RG32_FLOAT)
.setBufferIndex(0)
.setIsInstanced(false);
input_layout = m_backend->GetDevice()->createInputLayout(&attribute_desc, 1,
GetVertexShader());
}
return input_layout;
}
nvrhi::IBuffer* FullScreenPass::GetVertexBuffer() {
static nvrhi::BufferHandle vertex_buffer = nullptr;
if (!vertex_buffer) {
glm::vec2 vertices[]{{0.f, 0.f}, {1.f, 0.f}, {0.f, 1.f}, {1.f, 1.f}};
vertex_buffer = m_backend->GetDevice()->createBuffer(
nvrhi::BufferDesc()
.setIsVertexBuffer(true)
.setByteSize(sizeof(vertices))
.setInitialState(nvrhi::ResourceStates::VertexBuffer)
.setKeepInitialState(true)
.setCpuAccess(nvrhi::CpuAccessMode::Write));
void* map = m_backend->GetDevice()->mapBuffer(vertex_buffer,
nvrhi::CpuAccessMode::Write);
memcpy(map, vertices, sizeof(vertices));
m_backend->GetDevice()->unmapBuffer(vertex_buffer);
}
return vertex_buffer;
}
void FullScreenPass::Init() { CreatePipeline(); }
void FullScreenPass::CreatePipeline() {
nvrhi::GraphicsPipelineDesc pipeline_desc;
CreatePipelineDesc(pipeline_desc);
m_pipeline = m_backend->GetDevice()->createGraphicsPipeline(pipeline_desc,
GetFramebuffer());
}
void FullScreenPass::CreatePipelineDesc(
nvrhi::GraphicsPipelineDesc& pipeline_desc) {
pipeline_desc.setVertexShader(GetVertexShader())
.setInputLayout(GetInputLayout())
.setPrimType(nvrhi::PrimitiveType::TriangleStrip)
.setRenderState(
nvrhi::RenderState()
.setBlendState(nvrhi::BlendState().setRenderTarget(
0, nvrhi::BlendState::RenderTarget().disableBlend()))
.setRasterState(nvrhi::RasterState()
.setCullNone()
.disableScissor()
.disableDepthClip()
.setFillSolid())
.setDepthStencilState(nvrhi::DepthStencilState()
.disableDepthTest()
.disableDepthWrite()
.disableStencil()));
}
nvrhi::IFramebuffer* FullScreenPass::GetFramebuffer() { return m_backend->GetCurrentFramebuffer(); }
void FullScreenPass::InitState(nvrhi::GraphicsState& render_state) {
nvrhi::IFramebuffer* framebuffer = GetFramebuffer();
render_state.setFramebuffer(framebuffer);
render_state.setViewport(nvrhi::ViewportState().addViewportAndScissorRect(
framebuffer->getFramebufferInfo().getViewport()));
render_state.setPipeline(m_pipeline);
render_state.addVertexBuffer(
nvrhi::VertexBufferBinding().setSlot(0).setBuffer(GetVertexBuffer()));
}
void FullScreenPass::Draw(nvrhi::ICommandList* command_list) {
command_list->draw(nvrhi::DrawArguments().setVertexCount(4));
}

View File

@@ -0,0 +1,21 @@
#pragma once
#include <nvrhi/nvrhi.h>
struct FullScreenPass {
FullScreenPass(class NvRendererBackend* backend);
virtual ~FullScreenPass() = default;
nvrhi::IShader* GetVertexShader();
nvrhi::IInputLayout* GetInputLayout();
nvrhi::IBuffer* GetVertexBuffer();
virtual void Init();
void CreatePipeline();
virtual void CreatePipelineDesc(nvrhi::GraphicsPipelineDesc& pipeline_desc);
nvrhi::GraphicsPipelineHandle m_pipeline;
class NvRendererBackend* m_backend;
virtual nvrhi::IFramebuffer* GetFramebuffer();
void InitState(nvrhi::GraphicsState& render_state);
void Draw(nvrhi::ICommandList* command_list);
virtual void Render(nvrhi::ICommandList* command_list) = 0;
};

View File

@@ -0,0 +1,117 @@
#include "gbuffer.h"
#include <nvrhi/utils.h>
#include "nvrenderer/nvrenderer.h"
#include "nvrendererbackend.h"
NvGbuffer::NvGbuffer(NvRenderer* renderer)
: m_backend(renderer->m_backend.get()) {}
void NvGbuffer::Init(int width, int height, bool is_cube, bool depth_only,
int slices) {
m_depth_only = depth_only;
nvrhi::TextureDesc buffer_desc =
nvrhi::TextureDesc()
.setWidth(width)
.setHeight(height)
.setInitialState(nvrhi::ResourceStates::ShaderResource)
.setIsRenderTarget(true)
.setKeepInitialState(true)
.setClearValue(nvrhi::Color(0.f, 0.f, 0.f, 0.f))
.setUseClearValue(true);
if (is_cube) {
buffer_desc.setDimension(nvrhi::TextureDimension::TextureCube);
buffer_desc.setArraySize(6);
} else if (slices > 1) {
buffer_desc.setDimension(nvrhi::TextureDimension::Texture2DArray);
buffer_desc.setArraySize(slices);
}
if (!depth_only) {
m_gbuffer_diffuse = m_backend->GetDevice()->createTexture(
nvrhi::TextureDesc(buffer_desc)
.setFormat(nvrhi::Format::R10G10B10A2_UNORM)
.setDebugName("GBuffer Diffuse"));
m_gbuffer_emission = m_backend->GetDevice()->createTexture(
nvrhi::TextureDesc(buffer_desc)
.setFormat(nvrhi::Format::RGBA16_FLOAT)
.setDebugName("GBuffer Emission"));
m_gbuffer_params = m_backend->GetDevice()->createTexture(
nvrhi::TextureDesc(buffer_desc)
.setFormat(nvrhi::Format::RGBA8_UNORM)
.setDebugName("GBuffer Params"));
m_gbuffer_normal = m_backend->GetDevice()->createTexture(
nvrhi::TextureDesc(buffer_desc)
.setFormat(nvrhi::Format::R10G10B10A2_UNORM)
.setClearValue(nvrhi::Color(.5f, .5f, 1.f, 0.f))
.setDebugName("GBuffer Normal"));
m_gbuffer_motion = m_backend->GetDevice()->createTexture(
nvrhi::TextureDesc(buffer_desc)
.setFormat(nvrhi::Format::RG16_FLOAT)
.setClearValue(nvrhi::Color(0.f, 0.f, 0.f, 0.f))
.setDebugName("GBuffer Motion"));
}
m_gbuffer_depth = m_backend->GetDevice()->createTexture(
nvrhi::TextureDesc(buffer_desc)
.setIsTypeless(true)
.setInitialState(nvrhi::ResourceStates::ShaderResource)
.setFormat(nvrhi::Format::D32)
.setDebugName("GBuffer Deph"));
nvrhi::TextureSubresourceSet subresource_set =
nvrhi::TextureSubresourceSet(0, 1, 0, buffer_desc.arraySize);
nvrhi::FramebufferDesc fb_desc = nvrhi::FramebufferDesc().setDepthAttachment(
m_gbuffer_depth, subresource_set);
if (!depth_only) {
fb_desc.addColorAttachment(m_gbuffer_diffuse, subresource_set)
.addColorAttachment(m_gbuffer_emission, subresource_set)
.addColorAttachment(m_gbuffer_params, subresource_set)
.addColorAttachment(m_gbuffer_normal, subresource_set)
.addColorAttachment(m_gbuffer_motion, subresource_set);
}
m_framebuffer = m_backend->GetDevice()->createFramebuffer(fb_desc);
m_slice_framebuffers.resize(buffer_desc.arraySize);
for (int i = 0; i < buffer_desc.arraySize; ++i) {
nvrhi::TextureSubresourceSet subresource_set =
nvrhi::TextureSubresourceSet(0, 1, i, 1);
nvrhi::FramebufferDesc fb_desc =
nvrhi::FramebufferDesc().setDepthAttachment(m_gbuffer_depth,
subresource_set);
if (!depth_only) {
fb_desc.addColorAttachment(m_gbuffer_diffuse, subresource_set)
.addColorAttachment(m_gbuffer_emission, subresource_set)
.addColorAttachment(m_gbuffer_params, subresource_set)
.addColorAttachment(m_gbuffer_normal, subresource_set)
.addColorAttachment(m_gbuffer_motion, subresource_set);
}
m_slice_framebuffers[i] =
m_backend->GetDevice()->createFramebuffer(fb_desc);
}
}
void NvGbuffer::Clear(nvrhi::ICommandList* command_list) {
if (!m_depth_only) {
nvrhi::utils::ClearColorAttachment(command_list, m_framebuffer, 0,
nvrhi::Color(0.f, 0.f, 0.f, 0.f));
nvrhi::utils::ClearColorAttachment(command_list, m_framebuffer, 1,
nvrhi::Color(0.f, 0.f, 0.f, 0.f));
nvrhi::utils::ClearColorAttachment(command_list, m_framebuffer, 2,
nvrhi::Color(0.f, 0.f, 0.f, 0.f));
nvrhi::utils::ClearColorAttachment(command_list, m_framebuffer, 3,
nvrhi::Color(.5f, .5f, 1.f, 0.f));
nvrhi::utils::ClearColorAttachment(command_list, m_framebuffer, 4,
nvrhi::Color(0.f, 0.f, 0.f, 0.f));
}
ClearDepthStencilAttachment(command_list, m_framebuffer,
m_depth_only ? 0.f : 0.f, 0);
}
void NvGbuffer::ClearDepthStencilAttachment(nvrhi::ICommandList* commandList,
nvrhi::IFramebuffer* framebuffer,
float depth, uint32_t stencil) {
const nvrhi::FramebufferAttachment& att =
framebuffer->getDesc().depthAttachment;
if (att.texture) {
commandList->clearDepthStencilTexture(att.texture, att.subresources, true,
depth, false, stencil);
}
}

View File

@@ -0,0 +1,27 @@
#pragma once
#include <nvrhi/nvrhi.h>
struct NvGbuffer {
NvGbuffer(class NvRenderer* renderer);
void Init(int width, int height, bool is_cube, bool depth_only = false, int slices = 1);
void Clear(nvrhi::ICommandList* command_list);
nvrhi::TextureHandle m_gbuffer_diffuse;
nvrhi::TextureHandle m_gbuffer_emission;
nvrhi::TextureHandle m_gbuffer_normal;
nvrhi::TextureHandle m_gbuffer_params;
nvrhi::TextureHandle m_gbuffer_motion;
nvrhi::TextureHandle m_gbuffer_depth;
nvrhi::FramebufferHandle m_framebuffer;
std::vector<nvrhi::FramebufferHandle> m_slice_framebuffers;
class NvRendererBackend* m_backend;
bool m_depth_only;
static void ClearDepthStencilAttachment(nvrhi::ICommandList* commandList,
nvrhi::IFramebuffer* framebuffer,
float depth, uint32_t stencil);
};

View File

@@ -0,0 +1,218 @@
#include "gbufferblitpass.h"
#include <chrono>
#include <Timer.h>
#include <nvrhi/utils.h>
#include "contactshadows.h"
#include "csm.h"
#include "environment.h"
#include "gbuffer.h"
#include "nvrendererbackend.h"
#include "sky.h"
#include "ssao.h"
GbufferBlitPass::GbufferBlitPass(NvRenderer* renderer, NvGbuffer* gbuffer,
NvGbuffer* gbuffer_shadow, NvSsao* ssao,
MaEnvironment* environment,
MaShadowMap* shadow_map,
MaContactShadows* contact_shadows, Sky* sky)
: FullScreenPass(renderer->GetBackend()),
MaResourceRegistry(renderer),
m_gbuffer(gbuffer),
m_gbuffer_shadow(gbuffer_shadow),
m_ssao(ssao),
m_environment(environment),
m_shadow_map(shadow_map),
m_contact_shadows(contact_shadows),
m_sky(sky) {}
void GbufferBlitPass::Init() {
InitResourceRegistry();
m_draw_constants = m_backend->GetDevice()->createBuffer(
nvrhi::utils::CreateVolatileConstantBufferDesc(
sizeof(DrawConstants), "GBuffer Lighting Constants", 16)
.setInitialState(nvrhi::ResourceStates::ConstantBuffer)
.setKeepInitialState(true));
RegisterResource(true, "gbuffer_lighting_constants", m_draw_constants,
nvrhi::ResourceType::VolatileConstantBuffer);
const nvrhi::SamplerHandle sampler_shadow_comp =
m_backend->GetDevice()->createSampler(
nvrhi::SamplerDesc()
.setReductionType(nvrhi::SamplerReductionType::Comparison)
.setComparisonFunc(nvrhi::ComparisonFunc::Greater)
.setAllAddressModes(nvrhi::SamplerAddressMode::ClampToEdge)
.setAllFilters(true));
const nvrhi::SamplerHandle sampler_linear =
m_backend->GetDevice()->createSampler(
nvrhi::SamplerDesc().setAllFilters(true));
const nvrhi::SamplerHandle sampler_linear_clamp =
m_backend->GetDevice()->createSampler(
nvrhi::SamplerDesc()
.setAllAddressModes(nvrhi::SamplerAddressMode::ClampToEdge)
.setAllFilters(true));
const nvrhi::SamplerHandle sampler_linear_clamp_v_repeat_h =
m_backend->GetDevice()->createSampler(
nvrhi::SamplerDesc()
.setAllAddressModes(nvrhi::SamplerAddressMode::ClampToEdge)
.setAddressU(nvrhi::SamplerAddressMode::Repeat)
.setAllFilters(true));
RegisterResource(true, "shadow_sampler_comp", sampler_shadow_comp,
nvrhi::ResourceType::Sampler);
RegisterResource(true, "sampler_linear_wrap", sampler_linear,
nvrhi::ResourceType::Sampler);
RegisterResource(true, "sampler_linear_clamp", sampler_linear_clamp,
nvrhi::ResourceType::Sampler);
RegisterResource(true, "sampler_linear_clamp_v_repeat_h", sampler_linear_clamp_v_repeat_h,
nvrhi::ResourceType::Sampler);
m_scene_depth = m_backend->GetDevice()->createTexture(
nvrhi::TextureDesc(m_gbuffer->m_gbuffer_depth->getDesc())
.setFormat(nvrhi::Format::R32_FLOAT)
.setDebugName("Scene Depth")
.setInitialState(nvrhi::ResourceStates::ShaderResource)
.setUseClearValue(false)
.setIsRenderTarget(false));
RegisterResource(true, "gbuffer_depth", m_scene_depth,
nvrhi::ResourceType::Texture_SRV);
m_output = m_backend->GetDevice()->createTexture(
nvrhi::TextureDesc()
.setWidth(m_gbuffer->m_framebuffer->getFramebufferInfo().width)
.setHeight(m_gbuffer->m_framebuffer->getFramebufferInfo().height)
.setFormat(nvrhi::Format::RGBA16_FLOAT)
.setIsUAV(true)
.setIsRenderTarget(true)
.setInitialState(nvrhi::ResourceStates::ShaderResource)
.setKeepInitialState(true));
RegisterResource(true, "scene_lit_texture", m_output,
nvrhi::ResourceType::Texture_SRV);
m_binding_layout = m_backend->GetDevice()->createBindingLayout(
nvrhi::BindingLayoutDesc()
.addItem(nvrhi::BindingLayoutItem::VolatileConstantBuffer(2))
.addItem(nvrhi::BindingLayoutItem::ConstantBuffer(11))
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(0))
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(1))
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(2))
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(3))
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(4))
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(5))
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(8))
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(9))
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(10))
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(11))
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(12))
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(14))
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(16))
.addItem(nvrhi::BindingLayoutItem::StructuredBuffer_SRV(17))
.addItem(nvrhi::BindingLayoutItem::StructuredBuffer_SRV(18))
.addItem(nvrhi::BindingLayoutItem::Sampler(8))
.addItem(nvrhi::BindingLayoutItem::Sampler(11))
.addItem(nvrhi::BindingLayoutItem::Sampler(13))
.addItem(nvrhi::BindingLayoutItem::Texture_UAV(0))
.setVisibility(nvrhi::ShaderType::Compute));
for (int i = 0; i < std::size(m_binding_set); ++i) {
m_binding_set[i] = m_backend->GetDevice()->createBindingSet(
nvrhi::BindingSetDesc()
.addItem(nvrhi::BindingSetItem::ConstantBuffer(2, m_draw_constants))
.addItem(nvrhi::BindingSetItem::ConstantBuffer(
11, m_shadow_map->m_projection_buffer))
.addItem(nvrhi::BindingSetItem::Texture_SRV(
0, m_gbuffer->m_gbuffer_diffuse))
.addItem(nvrhi::BindingSetItem::Texture_SRV(
1, m_gbuffer->m_gbuffer_emission))
.addItem(nvrhi::BindingSetItem::Texture_SRV(
2, m_gbuffer->m_gbuffer_params))
.addItem(nvrhi::BindingSetItem::Texture_SRV(
3, m_gbuffer->m_gbuffer_normal))
.addItem(nvrhi::BindingSetItem::Texture_SRV(
4, m_gbuffer->m_gbuffer_depth))
.addItem(nvrhi::BindingSetItem::Texture_SRV(5, m_ssao->m_outputAO))
.addItem(nvrhi::BindingSetItem::Texture_SRV(
8, m_environment->m_dynamic_envmap_diffuse[0]))
.addItem(nvrhi::BindingSetItem::Texture_SRV(
9, m_environment->m_dynamic_envmap_specular[0]))
.addItem(nvrhi::BindingSetItem::Texture_SRV(
10, m_environment->m_brdf_lut))
.addItem(nvrhi::BindingSetItem::Texture_SRV(
11, m_gbuffer_shadow->m_gbuffer_depth))
.addItem(nvrhi::BindingSetItem::Texture_SRV(
12, m_contact_shadows->m_output_texture))
.addItem(nvrhi::BindingSetItem::Texture_SRV(
14, m_sky->m_aerial_lut->m_lut))
.addItem(nvrhi::BindingSetItem::Texture_SRV(
16, static_cast<nvrhi::ITexture*>(
GetResource("forwardplus_index_grid_opaque", nvrhi::ResourceType::Texture_SRV)
.m_resource)))
.addItem(nvrhi::BindingSetItem::StructuredBuffer_SRV(
17, static_cast<nvrhi::IBuffer*>(
GetResource("forwardplus_index_buffer_opaque", nvrhi::ResourceType::StructuredBuffer_SRV)
.m_resource)))
.addItem(nvrhi::BindingSetItem::StructuredBuffer_SRV(
18, static_cast<nvrhi::IBuffer*>(
GetResource("forwardplus_light_buffer", nvrhi::ResourceType::StructuredBuffer_SRV)
.m_resource)))
.addItem(nvrhi::BindingSetItem::Sampler(8, sampler_linear))
.addItem(nvrhi::BindingSetItem::Sampler(11, sampler_shadow_comp))
.addItem(nvrhi::BindingSetItem::Sampler(13, sampler_linear_clamp_v_repeat_h))
.addItem(nvrhi::BindingSetItem::Texture_UAV(0, m_output)),
m_binding_layout);
}
m_pixel_shader =
m_backend->CreateShader("gbuffer_lighting", nvrhi::ShaderType::Compute);
m_pso = m_backend->GetDevice()->createComputePipeline(
nvrhi::ComputePipelineDesc()
.setComputeShader(m_pixel_shader)
.addBindingLayout(m_binding_layout));
// m_framebuffer = m_backend->GetDevice()->createFramebuffer(
// nvrhi::FramebufferDesc().addColorAttachment(m_output));
// FullScreenPass::Init();
}
void GbufferBlitPass::CreatePipelineDesc(
nvrhi::GraphicsPipelineDesc& pipeline_desc) {
FullScreenPass::CreatePipelineDesc(pipeline_desc);
pipeline_desc.addBindingLayout(m_binding_layout);
pipeline_desc.setPixelShader(m_pixel_shader);
}
void GbufferBlitPass::UpdateConstants(nvrhi::ICommandList* command_list,
glm::dmat4& view,
const glm::dmat4& projection) {
DrawConstants constants{};
constants.m_inverse_model_view = glm::inverse(glm::dmat4(glm::dmat3(view)));
constants.m_inverse_projection = glm::inverse(projection);
const auto& daylight = Global.DayLight;
m_sky->CalcLighting(constants.m_light_dir,
constants.m_light_color);
constants.m_altitude = Global.pCamera.Pos.y;
constants.m_time = Timer::GetTime();
command_list->writeBuffer(m_draw_constants, &constants, sizeof(constants));
}
void GbufferBlitPass::Render(nvrhi::ICommandList* command_list,
glm::dmat4& view, const glm::dmat4& projection) {
UpdateConstants(command_list, view, projection);
Render(command_list);
command_list->copyTexture(
m_scene_depth, nvrhi::TextureSlice().resolve(m_scene_depth->getDesc()),
m_gbuffer->m_gbuffer_depth,
nvrhi::TextureSlice().resolve(m_scene_depth->getDesc()));
}
void GbufferBlitPass::Render(nvrhi::ICommandList* command_list) {
nvrhi::ComputeState graphics_state;
auto desc = m_output->getDesc();
// InitState(graphics_state);
graphics_state.setPipeline(m_pso);
graphics_state.addBindingSet(
m_binding_set[m_environment->GetCurrentSetIndex()]);
command_list->setComputeState(graphics_state);
#define DISPATCH_SIZE(size, groupsize) ((size + groupsize - 1) / groupsize)
command_list->dispatch(DISPATCH_SIZE(desc.width, 8),
DISPATCH_SIZE(desc.height, 8), 1);
// Draw(command_list);
}
nvrhi::IFramebuffer* GbufferBlitPass::GetFramebuffer() { return m_framebuffer; }

View File

@@ -0,0 +1,53 @@
#pragma once
#include "fullscreenpass.h"
#include "nvrenderer/resource_registry.h"
struct GbufferBlitPass : public FullScreenPass, public MaResourceRegistry {
GbufferBlitPass(class NvRenderer* renderer, struct NvGbuffer* gbuffer,
struct NvGbuffer* gbuffer_shadow, struct NvSsao* ssao,
struct MaEnvironment* environment,
struct MaShadowMap* shadow_map,
struct MaContactShadows* contact_shadows, struct Sky* sky);
virtual void Init() override;
virtual void CreatePipelineDesc(
nvrhi::GraphicsPipelineDesc& pipeline_desc) override;
void UpdateConstants(nvrhi::ICommandList* command_list, glm::dmat4& view,
const glm::dmat4& projection);
void Render(nvrhi::ICommandList* command_list, glm::dmat4& view,
const glm::dmat4& projection);
virtual void Render(nvrhi::ICommandList* command_list) override;
struct DrawConstants {
glm::mat4 m_inverse_model_view;
glm::mat4 m_inverse_projection;
glm::vec3 m_light_dir;
float m_altitude;
glm::vec3 m_light_color;
float m_time;
};
nvrhi::BindingLayoutHandle m_binding_layout;
nvrhi::BindingSetHandle m_binding_set[2];
nvrhi::ShaderHandle m_pixel_shader;
nvrhi::FramebufferHandle m_framebuffer;
nvrhi::BufferHandle m_draw_constants;
nvrhi::TextureHandle m_scene_depth;
NvGbuffer* m_gbuffer;
NvGbuffer* m_gbuffer_shadow;
NvSsao* m_ssao;
MaEnvironment* m_environment;
MaShadowMap* m_shadow_map;
MaContactShadows* m_contact_shadows;
Sky* m_sky;
nvrhi::ComputePipelineHandle m_pso;
nvrhi::TextureHandle m_output;
virtual nvrhi::IFramebuffer* GetFramebuffer() override;
};

View File

@@ -0,0 +1,365 @@
#include "gbufferlighting.h"
#include <nvrhi/utils.h>
#include <glm/gtc/packing.hpp>
#include "gbuffer.h"
#include "nvrendererbackend.h"
#include "ssao.h"
#include "config.h"
void GbufferLighting::Init() {
auto fb_info = m_renderer->m_gbuffer->m_framebuffer->getFramebufferInfo();
m_width = fb_info.width;
m_height = fb_info.height;
m_tiles_x = (m_width + 7) / 8;
m_tiles_y = (m_height + 7) / 8;
m_frustum_buffer = m_renderer->GetBackend()->GetDevice()->createBuffer(
nvrhi::BufferDesc()
.setDebugName("Forward Plus Frustum Buffer")
.setStructStride(4 * sizeof(glm::vec4) + 10 * sizeof(float))
.setByteSize(m_tiles_x * m_tiles_y * (4 * sizeof(glm::vec4) + 10 * sizeof(float)))
.setCanHaveUAVs(true)
.setInitialState(nvrhi::ResourceStates::UnorderedAccess)
.setKeepInitialState(true));
m_view_params_buffer = m_renderer->GetBackend()->GetDevice()->createBuffer(
nvrhi::utils::CreateVolatileConstantBufferDesc(
sizeof(ViewParams), "Forward Plus View Parameters", 16)
.setInitialState(nvrhi::ResourceStates::ConstantBuffer)
.setKeepInitialState(true));
m_dispatch_params_buffer =
m_renderer->GetBackend()->GetDevice()->createBuffer(
nvrhi::utils::CreateVolatileConstantBufferDesc(
sizeof(ViewParams), "Forward Plus Dispatch Parameters", 16)
.setInitialState(nvrhi::ResourceStates::ConstantBuffer)
.setKeepInitialState(true));
{
auto atomic_counter_desc =
nvrhi::BufferDesc()
.setInitialState(nvrhi::ResourceStates::UnorderedAccess)
.setStructStride(sizeof(uint32_t))
.setKeepInitialState(true)
.setByteSize(sizeof(uint32_t))
.setCanHaveUAVs(true);
m_atomic_counter_lights_opaque =
m_renderer->GetBackend()->GetDevice()->createBuffer(
nvrhi::BufferDesc(atomic_counter_desc)
.setDebugName("Forward Plus Atomic Counter Lights Opaque"));
m_atomic_counter_lights_transparent =
m_renderer->GetBackend()->GetDevice()->createBuffer(
nvrhi::BufferDesc(atomic_counter_desc)
.setDebugName(
"Forward Plus Atomic Counter Lights Transparent"));
}
{
auto index_buffer_desc =
nvrhi::BufferDesc()
.setInitialState(nvrhi::ResourceStates::UnorderedAccess)
.setStructStride(sizeof(uint32_t))
.setKeepInitialState(true)
.setByteSize(sizeof(uint32_t) * m_tiles_x * m_tiles_y * 64)
.setCanHaveUAVs(true);
m_index_buffer_lights_opaque =
m_renderer->GetBackend()->GetDevice()->createBuffer(
nvrhi::BufferDesc(index_buffer_desc)
.setDebugName("Forward Plus Index Buffer Lights Opaque"));
m_index_buffer_lights_transparent =
m_renderer->GetBackend()->GetDevice()->createBuffer(
nvrhi::BufferDesc(index_buffer_desc)
.setDebugName("Forward Plus Index Buffer Lights Transparent"));
}
{
auto index_grid_desc =
nvrhi::TextureDesc()
.setInitialState(nvrhi::ResourceStates::UnorderedAccess)
.setFormat(nvrhi::Format::RG32_UINT)
.setKeepInitialState(true)
.setWidth(m_tiles_x)
.setHeight(m_tiles_y)
.setIsUAV(true);
m_index_grid_lights_opaque =
m_renderer->GetBackend()->GetDevice()->createTexture(
nvrhi::TextureDesc(index_grid_desc)
.setDebugName("Forward Plus Index Grid Lights Opaque"));
m_index_grid_lights_transparent =
m_renderer->GetBackend()->GetDevice()->createTexture(
nvrhi::TextureDesc(index_grid_desc)
.setDebugName("Forward Plus Index Grid Lights Transparent"));
}
{
m_lighting_buffer_capacity =
NvRenderer::Config()->m_lighting.m_max_lights_per_scene;
m_light_buffer = m_renderer->GetBackend()->GetDevice()->createBuffer(
nvrhi::BufferDesc()
.setInitialState(nvrhi::ResourceStates::ShaderResource)
.setKeepInitialState(true)
.setByteSize(m_lighting_buffer_capacity *
sizeof(m_packed_buffer[0]))
.setStructStride(sizeof(m_packed_buffer[0]))
.setDebugName("Light Buffer"));
}
RegisterResource(true, "forwardplus_light_buffer", m_light_buffer,
nvrhi::ResourceType::StructuredBuffer_SRV);
RegisterResource(true, "forwardplus_index_grid_opaque",
m_index_grid_lights_opaque,
nvrhi::ResourceType::Texture_SRV);
RegisterResource(true, "forwardplus_index_grid_transparent",
m_index_grid_lights_transparent,
nvrhi::ResourceType::Texture_SRV);
RegisterResource(true, "forwardplus_index_buffer_opaque",
m_index_buffer_lights_opaque,
nvrhi::ResourceType::StructuredBuffer_SRV);
RegisterResource(true, "forwardplus_index_buffer_transparent",
m_index_buffer_lights_transparent,
nvrhi::ResourceType::StructuredBuffer_SRV);
{
nvrhi::BindingLayoutHandle binding_layout_comp_frustums;
nvrhi::utils::CreateBindingSetAndLayout(
m_renderer->GetBackend()->GetDevice(), nvrhi::ShaderType::Compute, 0,
nvrhi::BindingSetDesc()
.addItem(
nvrhi::BindingSetItem::ConstantBuffer(3, m_view_params_buffer))
.addItem(nvrhi::BindingSetItem::ConstantBuffer(
4, m_dispatch_params_buffer))
.addItem(nvrhi::BindingSetItem::StructuredBuffer_UAV(
0, m_frustum_buffer)),
binding_layout_comp_frustums, m_bindings_comp_frustums);
auto cs_comp_frustums = m_renderer->m_backend->CreateShader(
"forwardplus_compute_frustums", nvrhi::ShaderType::Compute);
m_pso_comp_frustums =
m_renderer->GetBackend()->GetDevice()->createComputePipeline(
nvrhi::ComputePipelineDesc()
.addBindingLayout(binding_layout_comp_frustums)
.setComputeShader(cs_comp_frustums));
}
{
nvrhi::BindingLayoutHandle binding_layout_cull_lights;
nvrhi::utils::CreateBindingSetAndLayout(
m_renderer->GetBackend()->GetDevice(), nvrhi::ShaderType::Compute, 0,
nvrhi::BindingSetDesc()
.addItem(
nvrhi::BindingSetItem::ConstantBuffer(3, m_view_params_buffer))
.addItem(nvrhi::BindingSetItem::ConstantBuffer(
4, m_dispatch_params_buffer))
.addItem(nvrhi::BindingSetItem::Texture_SRV(
3, m_renderer->m_gbuffer->m_gbuffer_depth))
.addItem(
nvrhi::BindingSetItem::StructuredBuffer_SRV(8, m_light_buffer))
.addItem(nvrhi::BindingSetItem::StructuredBuffer_SRV(
9, m_frustum_buffer))
.addItem(nvrhi::BindingSetItem::StructuredBuffer_UAV(
1, m_atomic_counter_lights_opaque))
.addItem(nvrhi::BindingSetItem::StructuredBuffer_UAV(
2, m_atomic_counter_lights_transparent))
.addItem(nvrhi::BindingSetItem::StructuredBuffer_UAV(
3, m_index_buffer_lights_opaque))
.addItem(nvrhi::BindingSetItem::StructuredBuffer_UAV(
4, m_index_buffer_lights_transparent))
.addItem(nvrhi::BindingSetItem::Texture_UAV(
5, m_index_grid_lights_opaque))
.addItem(nvrhi::BindingSetItem::Texture_UAV(
6, m_index_grid_lights_transparent)),
binding_layout_cull_lights, m_bindings_cull_lights);
auto cs_cull_lights = m_renderer->m_backend->CreateShader(
"forwardplus_cull_lights", nvrhi::ShaderType::Compute);
m_pso_cull_lights =
m_renderer->GetBackend()->GetDevice()->createComputePipeline(
nvrhi::ComputePipelineDesc()
.addBindingLayout(binding_layout_cull_lights)
.setComputeShader(cs_cull_lights));
}
// auto texture_desc =
// nvrhi::TextureDesc()
// .setWidth(m_width)
// .setHeight(m_height)
// .setFormat(nvrhi::Format::R11G11B10_FLOAT)
// .setIsUAV(true)
// .setInitialState(nvrhi::ResourceStates::ShaderResource)
// .setKeepInitialState(true);
//texture_desc
// texture_desc.setDebugName("Deferred Diffuse Buffer");
// m_diffuse_texture =
// m_renderer->GetBackend()->GetDevice()->createTexture(texture_desc);
// RegisterResource(true, "deferred_lights", m_diffuse_texture,
// nvrhi::ResourceType::Texture_SRV);
//
// m_view_constants = m_renderer->GetBackend()->GetDevice()->createBuffer(
// nvrhi::utils::CreateVolatileConstantBufferDesc(
// sizeof(ViewConstants), "Deferred Lighting Constants", 16)
// .setInitialState(nvrhi::ResourceStates::ConstantBuffer)
// .setKeepInitialState(true));
//
// nvrhi::BindingLayoutHandle binding_layout_spot;
// nvrhi::BindingLayoutHandle binding_layout_clear;
//
// nvrhi::utils::CreateBindingSetAndLayout(
// m_renderer->GetBackend()->GetDevice(), nvrhi::ShaderType::Compute, 0,
// nvrhi::BindingSetDesc().addItem(
// nvrhi::BindingSetItem::Texture_UAV(0, m_diffuse_texture)),
// binding_layout_clear, m_binding_set_clear);
//
// nvrhi::utils::CreateBindingSetAndLayout(
// m_renderer->GetBackend()->GetDevice(), nvrhi::ShaderType::Compute, 0,
// nvrhi::BindingSetDesc()
// .addItem(nvrhi::BindingSetItem::ConstantBuffer(0, m_view_constants))
// .addItem(nvrhi::BindingSetItem::PushConstants(
// 1, sizeof(PushConstantsSpotLight)))
// .addItem(nvrhi::BindingSetItem::Texture_UAV(0, m_diffuse_texture))
// .addItem(nvrhi::BindingSetItem::Texture_SRV(
// 0, m_renderer->m_gbuffer->m_gbuffer_depth))
// .addItem(nvrhi::BindingSetItem::Texture_SRV(
// 1, m_renderer->m_gbuffer->m_gbuffer_normal))
// .addItem(nvrhi::BindingSetItem::Texture_SRV(
// 2, m_renderer->m_gbuffer->m_gbuffer_params))
// .addItem(nvrhi::BindingSetItem::Texture_SRV(
// 3, m_renderer->m_gbuffer->m_gbuffer_diffuse))
// .addItem(nvrhi::BindingSetItem::Texture_SRV(
// 5, m_renderer->m_ssao->m_outputAO)),
// binding_layout_spot, m_binding_set_spotlight);
//
// auto clear_shader = m_renderer->m_backend->CreateShader(
// "gbuffer_light_clear", nvrhi::ShaderType::Compute);
// auto spotlight_shader = m_renderer->m_backend->CreateShader(
// "gbuffer_light_spot", nvrhi::ShaderType::Compute);
//
// m_pso_clear = m_renderer->m_backend->GetDevice()->createComputePipeline(
// nvrhi::ComputePipelineDesc()
// .setComputeShader(clear_shader)
// .addBindingLayout(binding_layout_clear));
//
// m_pso_spotlight = m_renderer->m_backend->GetDevice()->createComputePipeline(
// nvrhi::ComputePipelineDesc()
// .setComputeShader(spotlight_shader)
// .addBindingLayout(binding_layout_spot));
}
void GbufferLighting::Render(const NvRenderer::RenderPass& pass) {
// ViewConstants view_constants{};
// view_constants.m_inverse_model_view =
// static_cast<glm::mat4>(glm::inverse(pass.m_transform));
// view_constants.m_inverse_projection =
// static_cast<glm::mat4>(glm::inverse(pass.m_projection));
// pass.m_command_list_draw->writeBuffer(m_view_constants, &view_constants,
// sizeof(view_constants));
m_packed_buffer.clear();
for (int i = 0; i < m_renderer->m_renderable_spotlights.size(); ++i) {
if (i >= m_lighting_buffer_capacity) break;
auto& light = m_packed_buffer.emplace_back(
m_renderer->m_renderable_spotlights[i], pass.m_origin);
}
pass.m_command_list_draw->writeBuffer(
m_light_buffer, m_packed_buffer.data(),
m_packed_buffer.size() * sizeof(m_packed_buffer[0]));
ViewParams view_params{};
view_params.m_inverse_projection =
static_cast<glm::mat4>(glm::inverse(pass.m_projection));
view_params.m_view = static_cast<glm::mat4>(pass.m_transform);
view_params.m_screen_dimensions = {m_width, m_height};
view_params.m_num_lights = m_packed_buffer.size();
pass.m_command_list_draw->writeBuffer(m_view_params_buffer, &view_params,
sizeof(view_params));
{
DispatchParams dispatch_params{};
dispatch_params.m_num_threads = {m_tiles_x, m_tiles_y, 1u};
dispatch_params.m_num_thread_groups =
(dispatch_params.m_num_threads + 7u) / 8u;
pass.m_command_list_draw->writeBuffer(
m_dispatch_params_buffer, &dispatch_params, sizeof(dispatch_params));
pass.m_command_list_draw->setComputeState(
nvrhi::ComputeState()
.setPipeline(m_pso_comp_frustums)
.addBindingSet(m_bindings_comp_frustums));
pass.m_command_list_draw->dispatch(dispatch_params.m_num_thread_groups.x,
dispatch_params.m_num_thread_groups.y,
dispatch_params.m_num_thread_groups.z);
}
{
DispatchParams dispatch_params{};
dispatch_params.m_num_thread_groups = {m_tiles_x, m_tiles_y, 1u};
dispatch_params.m_num_threads = dispatch_params.m_num_thread_groups * 8u;
pass.m_command_list_draw->writeBuffer(
m_dispatch_params_buffer, &dispatch_params, sizeof(dispatch_params));
uint32_t zero = 0;
pass.m_command_list_draw->writeBuffer(m_atomic_counter_lights_opaque, &zero,
sizeof(zero));
pass.m_command_list_draw->writeBuffer(m_atomic_counter_lights_transparent,
&zero, sizeof(zero));
pass.m_command_list_draw->setComputeState(
nvrhi::ComputeState()
.setPipeline(m_pso_cull_lights)
.addBindingSet(m_bindings_cull_lights));
pass.m_command_list_draw->dispatch(dispatch_params.m_num_thread_groups.x,
dispatch_params.m_num_thread_groups.y,
dispatch_params.m_num_thread_groups.z);
}
// {
// pass.m_command_list_draw->setComputeState(
// nvrhi::ComputeState()
// .setPipeline(m_pso_clear)
// .addBindingSet(m_binding_set_clear));
// pass.m_command_list_draw->dispatch((m_width + 7) / 8, (m_height + 7) / 8);
// }
//
// for (const auto& spotlight : m_renderer->m_renderable_spotlights) {
// PushConstantsSpotLight push_constants{};
// push_constants.m_origin = spotlight.m_origin - pass.m_origin;
// push_constants.m_radius_sqr = spotlight.m_radius * spotlight.m_radius;
// push_constants.m_direction = spotlight.m_direction;
// push_constants.m_cos_inner_cone = spotlight.m_cos_inner_cone;
// push_constants.m_cos_outer_cone = spotlight.m_cos_outer_cone;
// push_constants.m_color = spotlight.m_color;
//
// pass.m_command_list_draw->setComputeState(
// nvrhi::ComputeState()
// .setPipeline(m_pso_spotlight)
// .addBindingSet(m_binding_set_spotlight));
// pass.m_command_list_draw->setPushConstants(&push_constants,
// sizeof(push_constants));
// pass.m_command_list_draw->dispatch((m_width + 7) / 8, (m_height + 7) / 8);
// }
}
GbufferLighting::PackedSpotLight::PackedSpotLight(
const NvRenderer::Renderable::SpotLight& spotlight,
glm::dvec3 const& origin) {
m_origin = spotlight.m_origin - origin;
m_radius = spotlight.m_radius;
m_direction_cos_inner =
glm::packHalf4x16({spotlight.m_direction, spotlight.m_cos_inner_cone});
glm::vec3 color =
spotlight.m_color.r * glm::vec3(0.4123908f, 0.21263901f, 0.01933082f) +
spotlight.m_color.g * glm::vec3(0.35758434f, 0.71516868f, 0.11919478f) +
spotlight.m_color.b * glm::vec3(0.18048079f, 0.07219232f, 0.95053215f);
m_color_cos_outer = glm::packHalf4x16({color, spotlight.m_cos_outer_cone});
}

View File

@@ -0,0 +1,84 @@
#pragma once
#include <nvrenderer/nvrenderer.h>
#include <nvrhi/nvrhi.h>
#include <glm/glm.hpp>
struct GbufferLighting : public MaResourceRegistry {
GbufferLighting(NvRenderer* renderer)
: MaResourceRegistry(renderer), m_renderer(renderer) {}
struct ViewConstants {
glm::mat4 m_inverse_model_view;
glm::mat4 m_inverse_projection;
};
struct PushConstantsSpotLight {
glm::vec3 m_origin;
float m_radius_sqr;
glm::vec3 m_direction;
float m_cos_inner_cone;
glm::vec3 m_color;
float m_cos_outer_cone;
};
struct PackedSpotLight {
explicit PackedSpotLight(NvRenderer::Renderable::SpotLight const& spotlight,
glm::dvec3 const& origin);
glm::vec3 m_origin;
float m_radius;
uint64_t m_direction_cos_inner;
uint64_t m_color_cos_outer;
};
struct DispatchParams {
alignas(sizeof(glm::vec4)) glm::uvec3 m_num_thread_groups;
alignas(sizeof(glm::vec4)) glm::uvec3 m_num_threads;
};
struct ViewParams {
glm::mat4 m_inverse_projection;
glm::mat4 m_view;
glm::vec2 m_screen_dimensions;
uint32_t m_num_lights;
};
std::vector<PackedSpotLight> m_packed_buffer;
size_t m_lighting_buffer_capacity;
nvrhi::BindingSetHandle m_bindings_cull_lights;
nvrhi::ComputePipelineHandle m_pso_cull_lights;
nvrhi::BindingSetHandle m_bindings_comp_frustums;
nvrhi::ComputePipelineHandle m_pso_comp_frustums;
nvrhi::BufferHandle m_atomic_counter_lights_opaque;
nvrhi::BufferHandle m_atomic_counter_lights_transparent;
nvrhi::BufferHandle m_index_buffer_lights_opaque;
nvrhi::BufferHandle m_index_buffer_lights_transparent;
nvrhi::TextureHandle m_index_grid_lights_opaque;
nvrhi::TextureHandle m_index_grid_lights_transparent;
nvrhi::TextureHandle m_diffuse_texture;
nvrhi::TextureHandle m_specular_texture;
nvrhi::BufferHandle m_light_buffer;
nvrhi::BufferHandle m_view_constants;
nvrhi::BufferHandle m_frustum_buffer;
nvrhi::BufferHandle m_dispatch_params_buffer;
nvrhi::BufferHandle m_view_params_buffer;
nvrhi::BindingSetHandle m_binding_set_spotlight;
nvrhi::ComputePipelineHandle m_pso_spotlight;
nvrhi::BindingSetHandle m_binding_set_clear;
nvrhi::ComputePipelineHandle m_pso_clear;
int m_width;
int m_height;
int m_tiles_x;
int m_tiles_y;
void Init();
void Render(const NvRenderer::RenderPass& pass);
NvRenderer* m_renderer;
};

View File

@@ -0,0 +1,408 @@
/*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
/*
License for Dear ImGui
Copyright (c) 2014-2019 Omar Cornut
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <stddef.h>
#include <imgui.h>
#include <nvrhi/nvrhi.h>
#include "imgui_nvrhi.h"
#include "nvrendererbackend.h"
#include "config.h"
struct VERTEX_CONSTANT_BUFFER
{
float mvp[4][4];
};
struct ImguiPushConstants {
glm::vec2 m_inv_display_size;
float m_ui_nits;
MaConfig::MaTonemapType m_tonemap_function;
};
bool ImGui_NVRHI::createFontTexture(nvrhi::ICommandList* commandList)
{
ImGuiIO& io = ImGui::GetIO();
unsigned char *pixels;
int width, height;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
{
nvrhi::TextureDesc desc;
desc.width = width;
desc.height = height;
desc.format = nvrhi::Format::RGBA8_UNORM;
desc.debugName = "ImGui font texture";
fontTexture = renderer->createTexture(desc);
commandList->beginTrackingTextureState(fontTexture, nvrhi::AllSubresources, nvrhi::ResourceStates::Common);
if (fontTexture == nullptr)
return false;
commandList->writeTexture(fontTexture, 0, 0, pixels, width * 4);
commandList->setPermanentTextureState(fontTexture, nvrhi::ResourceStates::ShaderResource);
commandList->commitBarriers();
io.Fonts->TexID = fontTexture;
}
{
const auto desc = nvrhi::SamplerDesc()
.setAllAddressModes(nvrhi::SamplerAddressMode::Wrap)
.setAllFilters(true);
fontSampler = renderer->createSampler(desc);
if (fontSampler == nullptr)
return false;
}
return true;
}
bool ImGui_NVRHI::init(NvRendererBackend *backend)
{
this->renderer = renderer;
m_commandList = renderer->createCommandList();
m_commandList->open();
vertexShader = backend->CreateShader("imgui_vertex", nvrhi::ShaderType::Vertex);
if (vertexShader == nullptr)
{
printf("error creating NVRHI vertex shader object\n");
assert(0);
return false;
}
pixelShader = backend->CreateShader("imgui_pixel", nvrhi::ShaderType::Pixel);
if (pixelShader == nullptr)
{
printf("error creating NVRHI pixel shader object\n");
assert(0);
return false;
}
// create attribute layout object
nvrhi::VertexAttributeDesc vertexAttribLayout[] = {
{ "POSITION", nvrhi::Format::RG32_FLOAT, 1, 0, offsetof(ImDrawVert,pos), sizeof(ImDrawVert), false },
{ "TEXCOORD", nvrhi::Format::RG32_FLOAT, 1, 0, offsetof(ImDrawVert,uv), sizeof(ImDrawVert), false },
{ "COLOR", nvrhi::Format::RGBA8_UNORM, 1, 0, offsetof(ImDrawVert,col), sizeof(ImDrawVert), false },
};
shaderAttribLayout = renderer->createInputLayout(vertexAttribLayout, sizeof(vertexAttribLayout) / sizeof(vertexAttribLayout[0]), vertexShader);
// add the default font - before creating the font texture
auto& io = ImGui::GetIO();
io.Fonts->AddFontDefault();
// create font texture
if (!createFontTexture(m_commandList))
{
return false;
}
// create PSO
{
nvrhi::BlendState blendState;
blendState.targets[0].setBlendEnable(true)
.setSrcBlend(nvrhi::BlendFactor::SrcAlpha)
.setDestBlend(nvrhi::BlendFactor::InvSrcAlpha)
.setSrcBlendAlpha(nvrhi::BlendFactor::InvSrcAlpha)
.setDestBlendAlpha(nvrhi::BlendFactor::Zero);
auto rasterState = nvrhi::RasterState()
.setFillSolid()
.setCullNone()
.setScissorEnable(true)
.setDepthClipEnable(true);
auto depthStencilState = nvrhi::DepthStencilState()
.disableDepthTest()
.enableDepthWrite()
.disableStencil()
.setDepthFunc(nvrhi::ComparisonFunc::Always);
nvrhi::RenderState renderState;
renderState.blendState = blendState;
renderState.depthStencilState = depthStencilState;
renderState.rasterState = rasterState;
nvrhi::BindingLayoutDesc layoutDesc;
layoutDesc.visibility = nvrhi::ShaderType::All;
layoutDesc.bindings = {
nvrhi::BindingLayoutItem::PushConstants(0, sizeof(ImguiPushConstants)),
nvrhi::BindingLayoutItem::Texture_SRV(0),
nvrhi::BindingLayoutItem::Sampler(0)
};
bindingLayout = renderer->createBindingLayout(layoutDesc);
basePSODesc.primType = nvrhi::PrimitiveType::TriangleList;
basePSODesc.inputLayout = shaderAttribLayout;
basePSODesc.VS = vertexShader;
basePSODesc.PS = pixelShader;
basePSODesc.renderState = renderState;
basePSODesc.bindingLayouts = { bindingLayout };
}
m_commandList->close();
renderer->executeCommandList(m_commandList);
return true;
}
bool ImGui_NVRHI::reallocateBuffer(nvrhi::BufferHandle& buffer, size_t requiredSize, size_t reallocateSize, const bool indexBuffer)
{
if (buffer == nullptr || size_t(buffer->getDesc().byteSize) < requiredSize)
{
nvrhi::BufferDesc desc;
desc.byteSize = uint32_t(reallocateSize);
desc.structStride = 0;
desc.debugName = indexBuffer ? "ImGui index buffer" : "ImGui vertex buffer";
desc.canHaveUAVs = false;
desc.isVertexBuffer = !indexBuffer;
desc.isIndexBuffer = indexBuffer;
desc.isDrawIndirectArgs = false;
desc.isVolatile = false;
desc.initialState = indexBuffer ? nvrhi::ResourceStates::IndexBuffer : nvrhi::ResourceStates::VertexBuffer;
desc.keepInitialState = true;
buffer = renderer->createBuffer(desc);
if (!buffer)
{
return false;
}
}
return true;
}
bool ImGui_NVRHI::beginFrame()
{
return true;
}
nvrhi::IGraphicsPipeline* ImGui_NVRHI::getPSO(nvrhi::IFramebuffer* fb)
{
if (pso)
return pso;
pso = renderer->createGraphicsPipeline(basePSODesc, fb);
assert(pso);
return pso;
}
nvrhi::IBindingSet* ImGui_NVRHI::getBindingSet(void* handle)
{
nvrhi::ITexture* texture = reinterpret_cast<nvrhi::ITexture*>(handle);
auto iter = bindingsCache.find(texture);
if (iter != bindingsCache.end())
{
return iter->second;
}
nvrhi::BindingSetDesc desc;
desc.bindings = {
nvrhi::BindingSetItem::PushConstants(0, sizeof(ImguiPushConstants)),
nvrhi::BindingSetItem::Texture_SRV(0, texture),
nvrhi::BindingSetItem::Sampler(0, fontSampler)
};
nvrhi::BindingSetHandle binding;
binding = renderer->createBindingSet(desc, bindingLayout);
assert(binding);
bindingsCache[texture] = binding;
return binding;
}
bool ImGui_NVRHI::updateGeometry(nvrhi::ICommandList* commandList)
{
ImDrawData *drawData = ImGui::GetDrawData();
// create/resize vertex and index buffers if needed
if (!reallocateBuffer(vertexBuffer,
drawData->TotalVtxCount * sizeof(ImDrawVert),
(drawData->TotalVtxCount + 5000) * sizeof(ImDrawVert),
false))
{
return false;
}
if (!reallocateBuffer(indexBuffer,
drawData->TotalIdxCount * sizeof(ImDrawIdx),
(drawData->TotalIdxCount + 5000) * sizeof(ImDrawIdx),
true))
{
return false;
}
vtxBuffer.resize(vertexBuffer->getDesc().byteSize / sizeof(ImDrawVert));
idxBuffer.resize(indexBuffer->getDesc().byteSize / sizeof(ImDrawIdx));
// copy and convert all vertices into a single contiguous buffer
ImDrawVert *vtxDst = &vtxBuffer[0];
ImDrawIdx *idxDst = &idxBuffer[0];
for(int n = 0; n < drawData->CmdListsCount; n++)
{
const ImDrawList *cmdList = drawData->CmdLists[n];
memcpy(vtxDst, cmdList->VtxBuffer.Data, cmdList->VtxBuffer.Size * sizeof(ImDrawVert));
memcpy(idxDst, cmdList->IdxBuffer.Data, cmdList->IdxBuffer.Size * sizeof(ImDrawIdx));
vtxDst += cmdList->VtxBuffer.Size;
idxDst += cmdList->IdxBuffer.Size;
}
commandList->writeBuffer(vertexBuffer, &vtxBuffer[0], vertexBuffer->getDesc().byteSize);
commandList->writeBuffer(indexBuffer, &idxBuffer[0], indexBuffer->getDesc().byteSize);
return true;
}
bool ImGui_NVRHI::render(nvrhi::IFramebuffer* framebuffer)
{
ImDrawData *drawData = ImGui::GetDrawData();
const auto& io = ImGui::GetIO();
m_commandList->open();
m_commandList->beginMarker("ImGUI");
if (!updateGeometry(m_commandList))
{
return false;
}
// handle DPI scaling
drawData->ScaleClipRects(io.DisplayFramebufferScale);
ImguiPushConstants push_constants{};
push_constants.m_inv_display_size = {1.f / io.DisplaySize.x, 1.f / io.DisplaySize.y};
push_constants.m_ui_nits = NvRenderer::Config()->m_display.GetHDRConfig().m_ui_nits;
push_constants.m_tonemap_function = NvRenderer::Config()->m_display.GetHDRConfig().m_tonemap_function;
// set up graphics state
nvrhi::GraphicsState drawState;
drawState.framebuffer = framebuffer;
assert(drawState.framebuffer);
drawState.pipeline = getPSO(drawState.framebuffer);
drawState.viewport.viewports.push_back(nvrhi::Viewport(io.DisplaySize.x * io.DisplayFramebufferScale.x,
io.DisplaySize.y * io.DisplayFramebufferScale.y));
drawState.viewport.scissorRects.resize(1); // updated below
nvrhi::VertexBufferBinding vbufBinding;
vbufBinding.buffer = vertexBuffer;
vbufBinding.slot = 0;
vbufBinding.offset = 0;
drawState.vertexBuffers.push_back(vbufBinding);
drawState.indexBuffer.buffer = indexBuffer;
drawState.indexBuffer.format = (sizeof(ImDrawIdx) == 2 ? nvrhi::Format::R16_UINT : nvrhi::Format::R32_UINT);
drawState.indexBuffer.offset = 0;
// render command lists
int vtxOffset = 0;
int idxOffset = 0;
for(int n = 0; n < drawData->CmdListsCount; n++)
{
const ImDrawList *cmdList = drawData->CmdLists[n];
for(int i = 0; i < cmdList->CmdBuffer.Size; i++)
{
const ImDrawCmd *pCmd = &cmdList->CmdBuffer[i];
if (pCmd->UserCallback)
{
pCmd->UserCallback(cmdList, pCmd);
} else {
drawState.bindings = { getBindingSet(pCmd->TextureId) };
assert(drawState.bindings[0]);
drawState.viewport.scissorRects[0] = nvrhi::Rect(int(pCmd->ClipRect.x),
int(pCmd->ClipRect.z),
int(pCmd->ClipRect.y),
int(pCmd->ClipRect.w));
nvrhi::DrawArguments drawArguments;
drawArguments.vertexCount = pCmd->ElemCount;
drawArguments.startIndexLocation = idxOffset;
drawArguments.startVertexLocation = vtxOffset;
m_commandList->setGraphicsState(drawState);
m_commandList->setPushConstants(&push_constants, sizeof(push_constants));
m_commandList->drawIndexed(drawArguments);
}
idxOffset += pCmd->ElemCount;
}
vtxOffset += cmdList->VtxBuffer.Size;
}
m_commandList->endMarker();
m_commandList->close();
renderer->executeCommandList(m_commandList);
return true;
}
void ImGui_NVRHI::backbufferResizing()
{
pso = nullptr;
}

View File

@@ -0,0 +1,98 @@
/*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
/*
License for Dear ImGui
Copyright (c) 2014-2019 Omar Cornut
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#pragma once
#include <memory>
#include <vector>
#include <unordered_map>
#include <stdint.h>
#include <nvrhi/nvrhi.h>
#include <imgui/imgui.h>
#include "nvtexture.h"
struct ImGui_NVRHI
{
nvrhi::DeviceHandle renderer;
nvrhi::CommandListHandle m_commandList;
nvrhi::ShaderHandle vertexShader;
nvrhi::ShaderHandle pixelShader;
nvrhi::InputLayoutHandle shaderAttribLayout;
nvrhi::TextureHandle fontTexture;
nvrhi::SamplerHandle fontSampler;
nvrhi::BufferHandle vertexBuffer;
nvrhi::BufferHandle indexBuffer;
nvrhi::BindingLayoutHandle bindingLayout;
nvrhi::GraphicsPipelineDesc basePSODesc;
nvrhi::GraphicsPipelineHandle pso;
std::unordered_map<nvrhi::ITexture*, nvrhi::BindingSetHandle> bindingsCache;
std::vector<ImDrawVert> vtxBuffer;
std::vector<ImDrawIdx> idxBuffer;
NvTextureManager* textureManager;
bool init(class NvRendererBackend *backend);
bool beginFrame();
bool render(nvrhi::IFramebuffer* framebuffer);
void backbufferResizing();
private:
bool reallocateBuffer(nvrhi::BufferHandle& buffer, size_t requiredSize, size_t reallocateSize, bool isIndexBuffer);
bool createFontTexture(nvrhi::ICommandList* commandList);
nvrhi::IGraphicsPipeline* getPSO(nvrhi::IFramebuffer* fb);
nvrhi::IBindingSet* getBindingSet(void* handle);
bool updateGeometry(nvrhi::ICommandList* commandList);
};

View File

@@ -0,0 +1 @@
#include "instancecache.h"

View File

@@ -0,0 +1,54 @@
#pragma once
#include "nvrenderer/nvrenderer.h"
struct InstanceCacheId {
gfx::geometry_handle m_geometry;
material_handle m_material;
InstanceCacheId(gfx::geometry_handle geometry, material_handle material)
: m_geometry(geometry), m_material(material) {}
};
template <>
struct std::hash<InstanceCacheId> {
std::size_t operator()(const InstanceCacheId& op) const {
size_t hash_geometry = std::hash<uint64_t>()(op.m_geometry);
size_t hash_material = std::hash<uint32_t>()(op.m_material);
return hash_geometry ^ hash_material + 0x9e3779b9 + (hash_geometry << 6) +
(hash_geometry >> 2);
}
};
template <>
struct std::equal_to<InstanceCacheId> {
bool operator()(const InstanceCacheId& lhs,
const InstanceCacheId& rhs) const {
return lhs.m_geometry == rhs.m_geometry && lhs.m_material == rhs.m_material;
}
};
struct InstanceCache {
struct Instance {
glm::mat3x4 m_transform;
glm::mat3x4 m_transform_history;
Instance(const glm::dmat4& transform, const glm::dmat4& transform_history)
: m_transform(static_cast<glm::mat3x4>(glm::transpose(transform))),
m_transform_history(
static_cast<glm::mat3x4>(glm::transpose(transform_history))) {}
};
std::vector<Instance> m_instances;
};
struct InstanceBank {
std::unordered_map<InstanceCacheId, InstanceCache> m_cache;
void Clear() {
for (auto& [id, cache] : m_cache) {
cache.m_instances.clear();
}
}
void AddInstance(gfx::geometry_handle geometry, material_handle material,
const glm::dmat4& transform, const glm::dmat4& history) {
auto& cache = m_cache[{geometry, material}];
cache.m_instances.emplace_back(transform, history);
}
};

View File

@@ -0,0 +1,149 @@
#include "ddspp/ddspp.h"
#include "nvtexture.h"
#include <sstream>
bool NvTexture::LoadDDS(std::string &data, int size_bias) {
ddspp::Descriptor desc{};
if (ddspp::decode_header(reinterpret_cast<uint8_t *>(data.data()), desc) !=
ddspp::Success)
return false;
std::stringstream ss(data, std::ios::in | std::ios::binary);
// auto sd = opengl_texture::deserialize_ddsd(ss);
// int blksize = 16;
// m_bc = true;
m_has_alpha = false;
switch (desc.format) {
case ddspp::DXGIFormat::BC1_UNORM:
case ddspp::DXGIFormat::BC1_UNORM_SRGB:
m_format =
m_srgb ? nvrhi::Format::BC1_UNORM_SRGB : nvrhi::Format::BC1_UNORM;
break;
case ddspp::DXGIFormat::BC2_UNORM:
case ddspp::DXGIFormat::BC2_UNORM_SRGB:
m_format =
m_srgb ? nvrhi::Format::BC2_UNORM_SRGB : nvrhi::Format::BC2_UNORM;
break;
case ddspp::DXGIFormat::BC3_UNORM:
case ddspp::DXGIFormat::BC3_UNORM_SRGB:
m_format =
m_srgb ? nvrhi::Format::BC3_UNORM_SRGB : nvrhi::Format::BC3_UNORM;
break;
case ddspp::DXGIFormat::BC4_UNORM:
m_format = nvrhi::Format::BC4_UNORM;
break;
case ddspp::DXGIFormat::BC4_SNORM:
m_format = nvrhi::Format::BC4_SNORM;
break;
case ddspp::DXGIFormat::BC5_UNORM:
m_format = nvrhi::Format::BC5_UNORM;
break;
case ddspp::DXGIFormat::BC5_SNORM:
m_format = nvrhi::Format::BC5_SNORM;
break;
case ddspp::DXGIFormat::BC6H_SF16:
m_format = nvrhi::Format::BC6H_SFLOAT;
break;
case ddspp::DXGIFormat::BC6H_UF16:
m_format = nvrhi::Format::BC6H_UFLOAT;
break;
case ddspp::DXGIFormat::BC7_UNORM:
case ddspp::DXGIFormat::BC7_UNORM_SRGB:
m_format =
m_srgb ? nvrhi::Format::BC7_UNORM_SRGB : nvrhi::Format::BC7_UNORM;
break;
default:
return false;
}
//
m_width = desc.width;
m_height = desc.height;
struct Bc2Block {
uint64_t alpha;
uint16_t color0;
uint16_t color1;
uint32_t pixels;
[[nodiscard]] bool HasTransparency() const {
return alpha != static_cast<uint64_t>(-1);
}
};
static_assert(sizeof(Bc2Block) == 16);
struct Bc3Block {
uint16_t alpha;
uint16_t pixels0;
uint16_t pixels1;
uint16_t pixels2;
uint64_t color_block;
[[nodiscard]] bool HasTransparency() const {
return alpha != static_cast<uint16_t>(-1);
}
};
struct Bc7Block {
uint8_t data[16];
[[nodiscard]] bool HasTransparency() const {
int mode = 0;
if (!data[0]) return false;
for (; !(data[0] & (1 << (7 - mode))); ++mode)
;
return mode > 3;
}
};
static_assert(sizeof(Bc3Block) == 16);
for (int mip = 0; mip < desc.numMips; ++mip) {
int width = std::max(m_width >> mip, 1);
int height = std::max(m_height >> mip, 1);
int blkx = (width + desc.blockWidth - 1) / desc.blockWidth;
int blky = (height + desc.blockHeight - 1) / desc.blockHeight;
auto &[pitch, mip_data] = m_data.emplace_back();
pitch = ddspp::get_row_pitch(desc, mip);
mip_data.resize(pitch * blky, '\0');
ss.seekg(desc.headerSize + ddspp::get_offset(desc, mip, 0));
ss.read(mip_data.data(), mip_data.size());
if (!mip) {
switch (desc.format) {
case ddspp::DXGIFormat::BC2_UNORM:
case ddspp::DXGIFormat::BC2_UNORM_SRGB:
for (size_t j = 0; !m_has_alpha && j < blkx * blky; ++j) {
const Bc2Block &block =
reinterpret_cast<const Bc2Block *>(data.data())[j];
if (block.HasTransparency()) m_has_alpha = true;
}
break;
case ddspp::DXGIFormat::BC3_UNORM:
case ddspp::DXGIFormat::BC3_UNORM_SRGB:
for (size_t j = 0; !m_has_alpha && j < blkx * blky; ++j) {
const Bc3Block &block =
reinterpret_cast<const Bc3Block *>(data.data())[j];
if (block.HasTransparency()) m_has_alpha = true;
}
break;
case ddspp::DXGIFormat::BC7_UNORM:
case ddspp::DXGIFormat::BC7_UNORM_SRGB:
for (size_t j = 0; !m_has_alpha && j < blkx * blky; ++j) {
const Bc7Block &block =
reinterpret_cast<const Bc7Block *>(data.data())[j];
if (block.HasTransparency()) m_has_alpha = true;
}
break;
default:
break;
}
}
}
return true;
}

View File

@@ -0,0 +1,363 @@
#include "materialparser.h"
#include "nvrenderer/nvrenderer.h"
#include <Globals.h>
#include <Logs.h>
#include <fmt/format.h>
#include <material.h>
#include <parser.h>
#include <utilities.h>
#include <yaml-cpp/yaml.h>
#include <string>
std::string_view MaterialAdapter::GetTexturePathForEntry(
std::string_view key) const {
if (m_material_internal) {
return m_material_internal->GetTexturePathForEntry(key);
}
return std::string_view();
}
int MaterialAdapter::GetTextureSizeBiasForEntry(std::string_view key) const {
if (m_material_internal) {
return m_material_internal->GetTextureSizeBiasForEntry(key);
}
return 0;
}
std::string_view MaterialAdapter::GetShader() const {
if (m_material_internal) {
return m_material_internal->GetShader();
}
return std::string_view();
}
int MaterialAdapter::GetShadowRank() const {
if (m_material_internal) {
return m_material_internal->GetShadowRank();
}
return 0;
}
glm::dvec2 MaterialAdapter::GetSize() const {
if (m_material_internal) {
return m_material_internal->GetSize();
}
return {-1., -1.};
}
std::optional<float> MaterialAdapter::GetOpacity() const {
if (m_material_internal) {
return m_material_internal->GetOpacity();
}
return std::nullopt;
}
bool MaterialAdapter::Parse(std::string_view filepath) {
if (m_material_internal = MaterialAdapterManul::Parse(filepath);
m_material_internal) {
return true;
}
if (m_material_internal = MaterialAdapterLegacyMatFile::Parse(filepath);
m_material_internal) {
return true;
}
if (m_material_internal = MaterialAdapterSingleTexture::Parse(filepath);
m_material_internal) {
return true;
}
return false;
}
std::optional<bool> MaterialAdapter::GetMaskedShadowOverride() const {
if (m_material_internal) {
return m_material_internal->GetMaskedShadowOverride();
}
return std::nullopt;
}
std::shared_ptr<MaterialAdapterInternal> MaterialAdapterLegacyMatFile::Parse(
std::string_view path) {
auto const locator{material_manager::find_on_disk(std::string(path))};
if (locator.first.size()) {
// try to parse located file resource
cParser materialparser(locator.first + locator.second,
cParser::buffer_FILE);
auto material = std::make_shared<MaterialAdapterLegacyMatFile>();
while (material->ParseLevel(materialparser, 0)) {
}
return material;
} else {
return nullptr;
}
}
std::string_view MaterialAdapterLegacyMatFile::GetTexturePathForEntry(
std::string_view key) const {
if (key == "diffuse") {
if (auto it = m_texture_mapping.find("_diffuse:");
it != m_texture_mapping.end()) {
return it->second.m_path;
}
if (auto it = m_texture_mapping.find("1:"); it != m_texture_mapping.end()) {
return it->second.m_path;
}
} else if (key == "normal") {
if (auto it = m_texture_mapping.find("_normals:");
it != m_texture_mapping.end()) {
return it->second.m_path;
}
if (auto it = m_texture_mapping.find("_normalmap:");
it != m_texture_mapping.end()) {
return it->second.m_path;
}
if (auto it = m_texture_mapping.find("_reflmap:");
it != m_texture_mapping.end()) {
return it->second.m_path;
}
if (auto it = m_texture_mapping.find("2:"); it != m_texture_mapping.end()) {
return it->second.m_path;
}
} else {
if (auto it = m_texture_mapping.find(fmt::format("_{}:", key));
it != m_texture_mapping.end()) {
return it->second.m_path;
}
}
return std::string_view();
}
std::string_view MaterialAdapterLegacyMatFile::GetShader() const {
if (m_shader == "water") {
return "legacy_water";
}
if (IsSpecGlossShader() && HasSpecGlossMap()) {
if (IsNormalMapShader() && HasNormalMap()) {
return "legacy_normalmap_specgloss";
}
return "legacy_specgloss";
}
if (IsNormalMapShader() && HasNormalMap()) {
return "legacy_normalmap";
}
if (HasNormalMap()) {
return "legacy_reflection";
}
return "legacy";
}
bool MaterialAdapterLegacyMatFile::ParseLevel(cParser& parser, int priority) {
// NOTE: comma can be part of legacy file names, so we don't treat it as a
// separator here
auto key{parser.getToken<std::string>(true, "\n\r\t ;[]")};
// key can be an actual key or block end
if ((true == key.empty()) || (key == "}")) {
return false;
}
if (priority != -1) {
// regular attribute processing mode
// mark potential material change
// update_on_weather_change |= is_weather(key);
// update_on_season_change |= is_season(key);
if (key == Global.Weather) {
// weather textures override generic (pri 0) and seasonal (pri 1) textures
// seasonal weather textures (pri 1+2=3) override generic weather (pri 2)
// textures skip the opening bracket
auto const value{parser.getToken<std::string>(true, "\n\r\t ;")};
while (ParseLevel(parser, priority + 2)) {
; // all work is done in the header
}
} else if (key == Global.Season) {
// seasonal textures override generic textures
// skip the opening bracket
auto const value{parser.getToken<std::string>(true, "\n\r\t ;")};
while (ParseLevel(parser, priority + 1)) {
; // all work is done in the header
}
}
else if (key.compare(0, 7, "texture") == 0) {
key.erase(0, 7);
auto value{deserialize_random_set(parser)};
replace_slashes(value);
auto& [tex_priority, texture] = m_texture_mapping[key];
if (tex_priority < priority) {
texture = value;
tex_priority = priority;
}
} else if (key.compare(0, 5, "param") == 0) {
// key.erase(0, 5);
//
parser.getToken<std::string>(true, "\n\r\t;");
// std::istringstream stream(value);
// glm::vec4 data;
// stream >> data.r;
// stream >> data.g;
// stream >> data.b;
// stream >> data.a;
//
// auto it = parse_info->param_mapping.find(key);
// if (it == parse_info->param_mapping.end())
// parse_info->param_mapping.emplace(
// std::make_pair(key, parse_info_s::param_def({data, Priority})));
// else if (Priority > it->second.priority) {
// parse_info->param_mapping.erase(it);
// parse_info->param_mapping.emplace(
// std::make_pair(key, parse_info_s::param_def({data, Priority})));
// }
} else if (key == "shader:" &&
(m_shader.empty() || priority > m_shader_priority)) {
m_shader = deserialize_random_set(parser);
m_shader_priority = priority;
} else if (key == "opacity:" && priority > m_opacity_priority) {
std::string value = deserialize_random_set(parser);
m_opacity = std::stof(value); // m7t: handle exception
m_opacity_priority = priority;
} else if (key == "selfillum:" && priority > m_selfillum_priority) {
std::string value = deserialize_random_set(parser);
m_selfillum = std::stof(value); // m7t: handle exception
m_selfillum_priority = priority;
} else if (key == "glossiness:" /* && Priority > m_glossiness_priority*/) {
deserialize_random_set(parser);
// glossiness = std::stof(value); // m7t: handle exception
// m_glossiness_priority = Priority;
} else if (key == "shadow_rank:") {
std::string value = deserialize_random_set(parser);
m_shadow_rank = std::stoi(value); // m7t: handle exception
} else if (key == "size:") {
parser.getTokens(2);
parser >> m_size.x >> m_size.y;
} else {
auto const value = parser.getToken<std::string>(true, "\n\r\t ;");
if (value == "{") {
// unrecognized or ignored token, but comes with attribute block and
// potential further nesting go through it and discard the content
while (ParseLevel(parser, -1)) {
; // all work is done in the header
}
}
}
} else {
// discard mode; ignores all retrieved tokens
auto const value{parser.getToken<std::string>(true, "\n\r\t ;")};
if (value == "{") {
// ignored tokens can come with their own blocks, ignore these recursively
// go through it and discard the content
while (true == ParseLevel(parser, -1)) {
; // all work is done in the header
}
}
}
return true; // return value marks a key: value pair was extracted, nothing
// about whether it's recognized
}
bool MaterialAdapterLegacyMatFile::HasNormalMap() const {
return m_texture_mapping.find("_normals:") != m_texture_mapping.end() ||
m_texture_mapping.find("_normalmap:") != m_texture_mapping.end() ||
m_texture_mapping.find("_reflmap:") != m_texture_mapping.end() ||
m_texture_mapping.find("2:") != m_texture_mapping.end();
}
bool MaterialAdapterLegacyMatFile::HasSpecGlossMap() const {
return m_texture_mapping.find("_specgloss:") != m_texture_mapping.end();
}
bool MaterialAdapterLegacyMatFile::IsNormalMapShader() const {
return m_shader.find("normalmap") != std::string::npos ||
m_shader.find("parallax") != std::string::npos;
}
bool MaterialAdapterLegacyMatFile::IsReflectionShader() const {
return m_shader.find("reflmap") != std::string::npos;
}
bool MaterialAdapterLegacyMatFile::IsSpecGlossShader() const {
return m_shader.find("specgloss") != std::string::npos;
}
std::optional<float> MaterialAdapterLegacyMatFile::GetOpacity() const {
return m_opacity;
}
std::shared_ptr<MaterialAdapterInternal> MaterialAdapterSingleTexture::Parse(
std::string_view path) {
return std::make_shared<MaterialAdapterSingleTexture>(path);
}
std::string_view MaterialAdapterSingleTexture::GetTexturePathForEntry(
std::string_view key) const {
if (key == "diffuse") {
return m_tex_diffuse;
}
return std::string_view();
}
std::string_view MaterialAdapterSingleTexture::GetShader() const {
return "legacy";
}
std::optional<float> MaterialAdapterSingleTexture::GetOpacity() const {
return std::nullopt;
}
std::shared_ptr<MaterialAdapterInternal> MaterialAdapterManul::Parse(
std::string_view path) {
std::string entries[]{
Global.asCurrentTexturePath + std::string(path) + ".manul",
std::string(path) + ".manul"};
for (auto it = std::begin(entries); it != std::end(entries); ++it) {
if (!std::filesystem::exists(*it)) {
continue;
}
auto root = YAML::LoadFile(*it);
auto material = std::make_shared<MaterialAdapterManul>();
material->m_shader = root["shader"].as<std::string>();
for (const auto it : root["textures"]) {
material->m_textures[it.first.as<std::string>()] =
it.second.as<std::string>();
}
if (auto alpha_threshold = root["alpha_threshold"];
alpha_threshold.IsScalar()) {
material->m_alpha_threshold = alpha_threshold.as<float>();
}
if (auto size_bias = root["max_resolution_bias"]; size_bias.IsScalar())
material->m_size_bias = size_bias.as<int>();
return material;
}
return nullptr;
}
std::string_view MaterialAdapterManul::GetTexturePathForEntry(
std::string_view key) const {
if (auto it = m_textures.find(std::string(key)); it != m_textures.end()) {
return it->second;
}
return std::string_view();
}
int MaterialAdapterManul::GetTextureSizeBiasForEntry(
std::string_view key) const {
return m_size_bias;
}
std::string_view MaterialAdapterManul::GetShader() const { return m_shader; }
std::optional<float> MaterialAdapterManul::GetOpacity() const {
return m_alpha_threshold;
}
std::optional<bool> MaterialAdapterInternal::GetMaskedShadowOverride() const {
auto threshold = GetOpacity();
if (threshold.has_value()) {
return !!*threshold;
}
return std::nullopt;
}

View File

@@ -0,0 +1,99 @@
#pragma once
#include <memory>
#include <string>
#include <string_view>
#include <optional>
#include <unordered_map>
#include <glm/glm.hpp>
#include <string>
struct MaterialAdapterInternal {
virtual ~MaterialAdapterInternal() = default;
virtual std::string_view GetTexturePathForEntry(
std::string_view key) const = 0;
virtual int GetTextureSizeBiasForEntry(
std::string_view key) const = 0;
virtual std::string_view GetShader() const = 0;
virtual int GetShadowRank() const { return 0; }
virtual glm::dvec2 GetSize() const { return {-1., -1.}; }
virtual std::optional<float> GetOpacity() const = 0;
virtual std::optional<bool> GetMaskedShadowOverride() const;
};
struct MaterialAdapter {
std::string_view GetTexturePathForEntry(std::string_view key) const;
int GetTextureSizeBiasForEntry(std::string_view key) const;
std::string_view GetShader() const;
int GetShadowRank() const;
glm::dvec2 GetSize() const;
std::optional<float> GetOpacity() const;
bool Parse(std::string_view filepath);
std::shared_ptr<MaterialAdapterInternal> m_material_internal;
std::optional<bool> GetMaskedShadowOverride() const;
};
struct MaterialAdapterManul : public MaterialAdapterInternal {
static std::shared_ptr<MaterialAdapterInternal> Parse(std::string_view path);
virtual std::string_view GetTexturePathForEntry(
std::string_view key) const override;
virtual int GetTextureSizeBiasForEntry(std::string_view key) const override;
virtual std::string_view GetShader() const override;
virtual std::optional<float> GetOpacity() const override;
std::string m_shader;
std::unordered_map<std::string, std::string> m_textures;
int m_size_bias;
std::optional<float> m_alpha_threshold = std::nullopt;
glm::dvec2 m_size{-1., -1.};
virtual glm::dvec2 GetSize() const override { return m_size; }
};
struct MaterialAdapterLegacyMatFile : public MaterialAdapterInternal {
static std::shared_ptr<MaterialAdapterInternal> Parse(std::string_view path);
virtual std::string_view GetTexturePathForEntry(
std::string_view key) const override;
virtual int GetTextureSizeBiasForEntry(std::string_view key) const override {
return 0;
}
virtual std::string_view GetShader() const override;
bool ParseLevel(class cParser& parser, int priority);
std::optional<float> m_opacity = std::nullopt;
int m_shadow_rank;
float m_selfillum;
std::string m_tex_diffuse;
std::string m_tex_normal;
std::string m_shader;
int m_shader_priority = -1;
struct TextureEntry {
int m_priority = -1;
std::string m_path = "";
};
glm::dvec2 m_size{-1., -1.};
virtual glm::dvec2 GetSize() const override { return m_size; }
bool HasNormalMap() const;
bool HasSpecGlossMap() const;
bool IsSpecGlossShader() const;
bool IsNormalMapShader() const;
bool IsReflectionShader() const;
std::unordered_map<std::string, TextureEntry> m_texture_mapping;
virtual int GetShadowRank() const override { return m_shadow_rank; }
virtual std::optional<float> GetOpacity() const override;
int m_opacity_priority = -1;
int m_selfillum_priority = -1;
int m_diffuse_priority = -1;
int m_normal_priority = -1;
};
struct MaterialAdapterSingleTexture : public MaterialAdapterInternal {
static std::shared_ptr<MaterialAdapterInternal> Parse(std::string_view path);
MaterialAdapterSingleTexture(std::string_view path) : m_tex_diffuse(path){};
virtual std::string_view GetTexturePathForEntry(
std::string_view key) const override;
virtual int GetTextureSizeBiasForEntry(std::string_view key) const override {
return 0;
}
virtual std::string_view GetShader() const override;
virtual std::optional<float> GetOpacity() const override;
std::string m_tex_diffuse;
};

View File

@@ -0,0 +1,10 @@
#include "motioncache.h"
MotionCacheScope::MotionCacheScope(void const* instance, MotionCache* cache)
: m_instance(instance), m_cache(cache) {
m_cache->SetActiveScope(this);
}
MotionCacheScope::~MotionCacheScope() {
m_cache->UnsetActiveScope(this);
}

View File

@@ -0,0 +1,76 @@
#pragma once
#include <cstdint>
#include <functional>
#include <glm/glm.hpp>
struct MotionCacheId {
union {
struct {
void const* m_instance;
void const* m_sub;
};
struct {
size_t m_key[2];
};
};
MotionCacheId(void const* instance, void const* sub)
: m_instance(instance), m_sub(sub) {}
};
template <>
struct std::hash<MotionCacheId> {
std::size_t operator()(const MotionCacheId& k) const {
using hash = std::hash<size_t>;
size_t hash_instance = hash()(k.m_key[0]);
size_t hash_sub = hash()(k.m_key[1]);
return hash_instance ^
hash_sub + 0x9e3779b9 + (hash_instance << 6) + (hash_instance >> 2);
}
};
template <>
struct std::equal_to<MotionCacheId> {
bool operator()(const MotionCacheId& lhs, const MotionCacheId& rhs) const{
return lhs.m_key[0] == rhs.m_key[0] && lhs.m_key[1] == rhs.m_key[1];
}
};
struct MotionCacheEntry {
glm::dmat4x3 m_cached_transform{};
glm::dmat4x3 m_history_transform{};
size_t m_cached_frame = static_cast<size_t>(-1);
};
struct MotionCacheScope {
friend struct MotionCache;
~MotionCacheScope();
private:
MotionCacheScope(void const* instance, struct MotionCache* cache);
void const* m_instance;
MotionCache* m_cache;
};
struct MotionCache {
friend struct MotionCacheScope;
[[nodiscard]] MotionCacheScope SetInstance(void const* instance) {
return {instance, this};
}
MotionCacheEntry& Get(void const* sub) {
assert(m_active_scope);
return m_cache[MotionCacheId{m_active_scope->m_instance, sub}];
}
private:
std::unordered_map<MotionCacheId, MotionCacheEntry> m_cache{};
MotionCacheScope* m_active_scope{};
void SetActiveScope(MotionCacheScope* scope) {
assert(!m_active_scope);
m_active_scope = scope;
}
void UnsetActiveScope(const MotionCacheScope* scope) {
assert(m_active_scope == scope);
m_active_scope = nullptr;
}
};

View File

@@ -0,0 +1,590 @@
#include "nvmaterial.h"
#include "nvrenderer/nvrenderer.h"
#include <fmt/format.h>
#include <nvrhi/utils.h>
#include "Logs.h"
#include "csm.h"
#include "environment.h"
#include "gbuffer.h"
#include "gbufferblitpass.h"
#include "nvrendererbackend.h"
#include "nvtexture.h"
#include "sky.h"
bool NvRenderer::MaterialTemplate::CreateBindingSet(size_t pipeline_index,
MaterialCache &cache) {
// for (size_t i = 0; i < cache.m_pipelines.size(); ++i) {
auto pipeline = cache.m_pipelines[pipeline_index];
if (!pipeline) return false;
nvrhi::BindingSetDesc binding_set_desc{};
auto binding_layout = pipeline->getDesc().bindingLayouts[0];
if (!m_resource_mappings[pipeline_index].ToBindingSet(
binding_set_desc, &cache, binding_layout->getDesc()))
return false;
cache.m_binding_sets[pipeline_index] =
m_renderer->GetBackend()->GetDevice()->createBindingSet(binding_set_desc,
binding_layout);
//}
// nvrhi::BindingSetDesc binding_set_desc = nvrhi::BindingSetDesc();
// nvrhi::BindingSetDesc binding_set_desc_cube = nvrhi::BindingSetDesc();
// nvrhi::BindingSetDesc binding_set_desc_shadow = nvrhi::BindingSetDesc();
// nvrhi::static_vector<nvrhi::SamplerHandle, 8 + 1> samplers{};
// for (int i = 0; i < m_texture_bindings.size(); ++i) {
// const auto &binding = m_texture_bindings[i];
// if (binding.m_name.empty()) continue;
// auto texture = m_renderer->m_texture_manager->GetRhiTexture(
// cache.m_textures[i], pass.m_command_list_preparation);
// if (!texture) {
// texture = m_renderer->m_texture_manager->GetRhiTexture(
// 1, pass.m_command_list_preparation);
// }
// auto &sampler = samplers.emplace_back();
// auto &sampler_cubemap = samplers.emplace_back();
// sampler = m_renderer->m_texture_manager->GetSamplerForTraits(
// m_renderer->m_texture_manager->GetTraits(cache.m_textures[i]),
// RenderPassType::Deferred);
// sampler_cubemap = m_renderer->m_texture_manager->GetSamplerForTraits(
// m_renderer->m_texture_manager->GetTraits(cache.m_textures[i]),
// RenderPassType::CubeMap);
// binding_set_desc.addItem(nvrhi::BindingSetItem::Texture_SRV(i, texture));
// binding_set_desc.addItem(nvrhi::BindingSetItem::Sampler(i, sampler));
// binding_set_desc_cube.addItem(
// nvrhi::BindingSetItem::Texture_SRV(i, texture));
// binding_set_desc_cube.addItem(
// nvrhi::BindingSetItem::Sampler(i, sampler_cubemap));
// if (cache.m_masked_shadow && i == m_masked_shadow_texture) {
// auto &shadow_sampler = samplers.emplace_back();
// shadow_sampler = m_renderer->m_texture_manager->GetSamplerForTraits(
// m_renderer->m_texture_manager->GetTraits(cache.m_textures[i]),
// RenderPassType::ShadowMap);
// binding_set_desc_shadow.addItem(
// nvrhi::BindingSetItem::Texture_SRV(0, texture));
// binding_set_desc_shadow.addItem(
// nvrhi::BindingSetItem::Sampler(0, shadow_sampler));
// }
// }
// cache.m_binding_set =
// m_renderer->GetBackend()->GetDevice()->createBindingSet(
// binding_set_desc, m_binding_layout);
// cache.m_binding_set_cubemap =
// m_renderer->GetBackend()->GetDevice()->createBindingSet(
// binding_set_desc_cube, m_binding_layout);
// if (cache.m_masked_shadow) {
// cache.m_binding_set_shadow =
// m_renderer->GetBackend()->GetDevice()->createBindingSet(
// binding_set_desc_shadow,
// m_renderer->m_binding_layout_shadow_masked);
// }
cache.m_last_texture_updates[pipeline_index] = NvTexture::s_change_counter;
return true;
}
nvrhi::IGraphicsPipeline *NvRenderer::MaterialTemplate::GetPipeline(
RenderPassType pass_type, DrawType draw_type, bool alpha_masked) const {
if (auto pipeline =
m_pipelines[Constants::GetPipelineIndex(pass_type, draw_type)])
return pipeline;
if (pass_type == RenderPassType::ShadowMap) {
return m_renderer->m_pso_shadow[Constants::GetDefaultShadowPipelineIndex(
draw_type, alpha_masked)];
}
return nullptr;
}
void NvRenderer::MaterialTemplate::Init(const YAML::Node &conf) {
InitResourceRegistry();
std::vector<MaResourceMapping> texture_mappings{};
std::vector<MaResourceMapping> sampler_mappings{};
MaResourceMapping texture_mapping_shadow{};
MaResourceMapping sampler_mapping_shadow{};
m_pipelines.fill(nullptr);
m_masked_shadow_texture = conf["masked_shadow_texture"].as<std::string>();
for (const auto it : conf["textures"]) {
auto index = it.second["binding"].as<int>();
m_texture_bindings.resize(
glm::max(m_texture_bindings.size(), static_cast<size_t>(index) + 1));
auto &[name, sampler_name, hint, default_texture] =
m_texture_bindings[index];
name = it.first.as<std::string>();
sampler_name = fmt::format("{:s}_sampler", name.c_str());
texture_mappings.emplace_back(
MaResourceMapping::Texture_SRV(index, name.c_str()));
sampler_mappings.emplace_back(
MaResourceMapping::Sampler(index, sampler_name.c_str()));
RegisterTexture(name.c_str(), 1);
RegisterResource(
false, "masked_shadow_sampler",
GetTextureManager()->GetSamplerForTraits(0, RenderPassType::ShadowMap),
nvrhi::ResourceType::Sampler);
const static std::unordered_map<std::string_view, int> hints{
{"color", GL_SRGB_ALPHA}, {"linear", GL_RGBA}, {"normalmap", GL_RG}};
hint = hints.at(it.second["hint"].as<std::string_view>());
if (it.first.Scalar() == conf["masked_shadow_texture"].Scalar()) {
texture_mapping_shadow = MaResourceMapping::Texture_SRV(0, name.c_str());
sampler_mapping_shadow =
MaResourceMapping::Sampler(0, "masked_shadow_sampler");
}
}
std::array<nvrhi::ShaderHandle, static_cast<size_t>(RenderPassType::Num)>
pixel_shaders;
for (int i = 0; i < pixel_shaders.size(); ++i) {
pixel_shaders[i] = m_renderer->GetBackend()->CreateMaterialShader(
m_name, static_cast<RenderPassType>(i));
}
{
nvrhi::BindingLayoutDesc binding_layout_desc;
binding_layout_desc.setVisibility(nvrhi::ShaderType::Pixel);
for (int i = 0; i < m_texture_bindings.size(); ++i) {
if (m_texture_bindings[i].m_name.empty()) continue;
binding_layout_desc.addItem(nvrhi::BindingLayoutItem::Texture_SRV(i))
.addItem(nvrhi::BindingLayoutItem::Sampler(i));
}
m_binding_layout =
m_renderer->GetBackend()->GetDevice()->createBindingLayout(
binding_layout_desc);
}
for (size_t i = 0; i < Constants::NumMaterialPasses(); ++i) {
auto pass = static_cast<RenderPassType>(i);
for (size_t j = 0; j < Constants::NumDrawTypes(); ++j) {
auto draw = static_cast<DrawType>(j);
auto &mappings =
m_resource_mappings[Constants::GetPipelineIndex(pass, draw)];
nvrhi::GraphicsPipelineDesc pipeline_desc{};
auto pixel_shader = pixel_shaders[static_cast<size_t>(pass)];
if (!pixel_shader) {
if (pass == RenderPassType::ShadowMap) {
mappings.Add(MaResourceMapping::PushConstants<PushConstantsShadow>(1))
.Add(texture_mapping_shadow)
.Add(sampler_mapping_shadow);
}
continue;
}
pipeline_desc.setPixelShader(pixel_shader)
.setPrimType(nvrhi::PrimitiveType::TriangleList);
for (const auto &mapping : texture_mappings) mappings.Add(mapping);
for (const auto &mapping : sampler_mappings) mappings.Add(mapping);
nvrhi::IFramebuffer *framebuffer;
pipeline_desc.setInputLayout(
m_renderer->m_input_layout[static_cast<size_t>(draw)]);
switch (pass) {
case RenderPassType::Deferred:
case RenderPassType::Forward:
pipeline_desc.setVertexShader(
m_renderer->m_vertex_shader[static_cast<size_t>(draw)]);
break;
case RenderPassType::CubeMap:
pipeline_desc.setVertexShader(
m_renderer->m_vertex_shader_cubemap[static_cast<size_t>(draw)]);
break;
default:;
}
switch (pass) {
case RenderPassType::Deferred:
case RenderPassType::Forward:
mappings.Add(MaResourceMapping::PushConstants<PushConstantsDraw>(1))
.Add(MaResourceMapping::ConstantBuffer(
0, "cb_draw_constants_static"));
break;
case RenderPassType::CubeMap:
mappings
.Add(MaResourceMapping::PushConstants<PushConstantsCubemap>(1))
.Add(MaResourceMapping::ConstantBuffer(
0, "cb_draw_constants_static_cubemap"));
break;
default:;
}
switch (pass) {
case RenderPassType::Forward:
mappings
.Add(MaResourceMapping::VolatileConstantBuffer(
2, "gbuffer_lighting_constants"))
.Add(MaResourceMapping::ConstantBuffer(
11, "shadow_projection_buffer"))
.Add(MaResourceMapping::Texture_SRV(8, "env_dynamic_diffuse"))
.Add(MaResourceMapping::Texture_SRV(9, "env_dynamic_specular"))
.Add(MaResourceMapping::Texture_SRV(10, "env_brdf_lut"))
.Add(MaResourceMapping::Texture_SRV(11, "shadow_depths"))
.Add(MaResourceMapping::Texture_SRV(12, "gbuffer_depth"))
.Add(MaResourceMapping::Texture_SRV(14, "sky_aerial_lut"))
.Add(MaResourceMapping::Texture_SRV(16, "forwardplus_index_grid_transparent"))
.Add(MaResourceMapping::StructuredBuffer_SRV(17, "forwardplus_index_buffer_transparent"))
.Add(MaResourceMapping::StructuredBuffer_SRV(18, "forwardplus_light_buffer"))
.Add(MaResourceMapping::Sampler(8, "sampler_linear_wrap"))
.Add(MaResourceMapping::Sampler(11, "shadow_sampler_comp"))
.Add(MaResourceMapping::Sampler(13, "sampler_linear_clamp_v_repeat_h"));
break;
default:;
}
switch (pass) {
case RenderPassType::Deferred:
framebuffer = m_renderer->m_gbuffer->m_framebuffer;
break;
case RenderPassType::Forward:
framebuffer = m_renderer->m_framebuffer_forward;
break;
case RenderPassType::CubeMap:
framebuffer = m_renderer->m_gbuffer_cube->m_framebuffer;
break;
default:;
}
switch (pass) {
case RenderPassType::Deferred:
case RenderPassType::CubeMap:
pipeline_desc.setRenderState(
nvrhi::RenderState()
.setDepthStencilState(
nvrhi::DepthStencilState()
.enableDepthTest()
.enableDepthWrite()
.disableStencil()
.setDepthFunc(nvrhi::ComparisonFunc::Greater))
.setRasterState(nvrhi::RasterState()
.setFillSolid()
.enableDepthClip()
.disableScissor()
.setCullFront())
.setBlendState(nvrhi::BlendState().setRenderTarget(
0, nvrhi::BlendState::RenderTarget().disableBlend())));
break;
case RenderPassType::Forward:
pipeline_desc.setRenderState(
nvrhi::RenderState()
.setDepthStencilState(
nvrhi::DepthStencilState()
.enableDepthTest()
.disableDepthWrite()
.disableStencil()
.setDepthFunc(nvrhi::ComparisonFunc::Greater))
.setRasterState(nvrhi::RasterState()
.setFillSolid()
.enableDepthClip()
.disableScissor()
.setCullFront())
.setBlendState(
nvrhi::BlendState()
.setRenderTarget(
0, nvrhi::BlendState::RenderTarget()
.enableBlend()
.setBlendOp(nvrhi::BlendOp::Add)
.setSrcBlend(nvrhi::BlendFactor::One)
.setDestBlend(
nvrhi::BlendFactor::OneMinusSrcAlpha))
.setRenderTarget(1, nvrhi::BlendState::RenderTarget()
.disableBlend())));
break;
default:;
}
nvrhi::BindingLayoutDesc bl_desc{};
bl_desc.visibility = nvrhi::ShaderType::All;
// for (const auto &bl : pipeline_desc.bindingLayouts) {
// bl_desc.visibility = bl_desc.visibility | bl->getDesc()->visibility;
// for (const auto &element : bl->getDesc()->bindings) {
// bl_desc.addItem(element);
// }
// }
mappings.ToBindingLayout(bl_desc);
pipeline_desc.bindingLayouts.resize(0);
pipeline_desc.addBindingLayout(
m_renderer->GetBackend()->GetDevice()->createBindingLayout(bl_desc));
m_pipelines[Constants::GetPipelineIndex(pass, draw)] =
m_renderer->GetBackend()->GetDevice()->createGraphicsPipeline(
pipeline_desc, framebuffer);
}
}
}
bool NvRenderer::InitMaterials() {
m_drawconstant_buffer = m_backend->GetDevice()->createBuffer(
nvrhi::utils::CreateStaticConstantBufferDesc(sizeof(DrawConstants),
"Draw Constants")
.setInitialState(nvrhi::ResourceStates::ConstantBuffer)
.setKeepInitialState(true));
m_cubedrawconstant_buffer = m_backend->GetDevice()->createBuffer(
nvrhi::utils::CreateStaticConstantBufferDesc(sizeof(CubeDrawConstants),
"Draw Data")
.setInitialState(nvrhi::ResourceStates::ConstantBuffer)
.setKeepInitialState(true));
RegisterResource(true, "cb_draw_constants_static", m_drawconstant_buffer,
nvrhi::ResourceType::ConstantBuffer);
RegisterResource(true, "cb_draw_constants_static_cubemap",
m_cubedrawconstant_buffer,
nvrhi::ResourceType::ConstantBuffer);
{
nvrhi::CommandListHandle command_list =
GetBackend()->GetDevice()->createCommandList();
command_list->open();
CubeDrawConstants data;
data.m_face_projection = glm::perspectiveFovRH_ZO(
M_PI_2, 1., 1., .1,
static_cast<double>(Global.reflectiontune.range_instances));
command_list->writeBuffer(m_cubedrawconstant_buffer, &data, sizeof(data));
command_list->close();
GetBackend()->GetDevice()->executeCommandList(command_list);
}
nvrhi::utils::CreateBindingSetAndLayout(
m_backend->GetDevice(), nvrhi::ShaderType::AllGraphics, 0,
nvrhi::BindingSetDesc()
.addItem(
nvrhi::BindingSetItem::ConstantBuffer(0, m_drawconstant_buffer))
.addItem(nvrhi::BindingSetItem::PushConstants(
1, sizeof(PushConstantsDraw))),
m_binding_layout_drawconstants, m_binding_set_drawconstants);
nvrhi::utils::CreateBindingSetAndLayout(
m_backend->GetDevice(), nvrhi::ShaderType::AllGraphics, 0,
nvrhi::BindingSetDesc()
.addItem(nvrhi::BindingSetItem::ConstantBuffer(
0, m_cubedrawconstant_buffer))
.addItem(nvrhi::BindingSetItem::PushConstants(
1, sizeof(PushConstantsCubemap))),
m_binding_layout_cubedrawconstants, m_binding_set_cubedrawconstants);
nvrhi::utils::CreateBindingSetAndLayout(
m_backend->GetDevice(), nvrhi::ShaderType::AllGraphics, 0,
nvrhi::BindingSetDesc().addItem(
nvrhi::BindingSetItem::PushConstants(1, sizeof(PushConstantsShadow))),
m_binding_layout_shadowdrawconstants, m_binding_set_shadowdrawconstants);
m_binding_layout_forward = GetBackend()->GetDevice()->createBindingLayout(
nvrhi::BindingLayoutDesc()
.setVisibility(nvrhi::ShaderType::Pixel)
.addItem(nvrhi::BindingLayoutItem::VolatileConstantBuffer(2))
.addItem(nvrhi::BindingLayoutItem::ConstantBuffer(11))
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(8))
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(9))
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(10))
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(11))
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(12))
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(14))
.addItem(nvrhi::BindingLayoutItem::Sampler(8))
.addItem(nvrhi::BindingLayoutItem::Sampler(11))
.addItem(nvrhi::BindingLayoutItem::Sampler(13)));
for (int i = 0; i < m_binding_set_forward.size(); ++i) {
m_binding_set_forward[i] = m_backend->GetDevice()->createBindingSet(
nvrhi::BindingSetDesc()
.addItem(nvrhi::BindingSetItem::ConstantBuffer(
2, m_gbuffer_blit->m_draw_constants))
.addItem(nvrhi::BindingSetItem::ConstantBuffer(
11, m_shadow_map->m_projection_buffer))
.addItem(nvrhi::BindingSetItem::Texture_SRV(
8, m_environment->m_dynamic_envmap_diffuse[0]))
.addItem(nvrhi::BindingSetItem::Texture_SRV(
9, m_environment->m_dynamic_envmap_specular[0]))
.addItem(nvrhi::BindingSetItem::Texture_SRV(
10, m_environment->m_brdf_lut))
.addItem(nvrhi::BindingSetItem::Texture_SRV(
11, m_gbuffer_shadow->m_gbuffer_depth))
.addItem(nvrhi::BindingSetItem::Texture_SRV(
12, m_gbuffer->m_gbuffer_depth))
.addItem(nvrhi::BindingSetItem::Texture_SRV(
14, m_sky->m_aerial_lut->m_lut))
.addItem(nvrhi::BindingSetItem::Sampler(
8, m_backend->GetDevice()->createSampler(
nvrhi::SamplerDesc().setAllFilters(true))))
.addItem(nvrhi::BindingSetItem::Sampler(
11, m_backend->GetDevice()->createSampler(
nvrhi::SamplerDesc()
.setReductionType(
nvrhi::SamplerReductionType::Comparison)
.setComparisonFunc(nvrhi::ComparisonFunc::Greater)
.setAllAddressModes(
nvrhi::SamplerAddressMode::ClampToEdge)
.setAllFilters(true))))
.addItem(nvrhi::BindingSetItem::Sampler(
13, m_backend->GetDevice()->createSampler(
nvrhi::SamplerDesc()
.setAllAddressModes(
nvrhi::SamplerAddressMode::ClampToEdge)
.setAllFilters(true)))),
m_binding_layout_forward);
}
m_vertex_shader[static_cast<size_t>(DrawType::Model)] =
m_backend->CreateShader("default_vertex", nvrhi::ShaderType::Vertex);
m_vertex_shader[static_cast<size_t>(DrawType::InstancedModel)] =
m_backend->CreateShader("instanced_vertex", nvrhi::ShaderType::Vertex);
m_vertex_shader_shadow[static_cast<size_t>(DrawType::Model)] =
m_backend->CreateShader("shadow_vertex", nvrhi::ShaderType::Vertex);
m_vertex_shader_shadow[static_cast<size_t>(DrawType::InstancedModel)] =
m_backend->CreateShader("instanced_shadow_vertex",
nvrhi::ShaderType::Vertex);
m_vertex_shader_cubemap[static_cast<size_t>(DrawType::Model)] =
m_backend->CreateShader("cubemap_vertex", nvrhi::ShaderType::Vertex);
m_vertex_shader_cubemap[static_cast<size_t>(DrawType::InstancedModel)] =
m_backend->CreateShader("instanced_cubemap_vertex",
nvrhi::ShaderType::Vertex);
m_pixel_shader_shadow_masked =
m_backend->CreateShader("shadow_masked", nvrhi::ShaderType::Pixel);
nvrhi::VertexAttributeDesc desc[]{
nvrhi::VertexAttributeDesc()
.setBufferIndex(0)
.setElementStride(sizeof(gfx::basic_vertex))
.setFormat(nvrhi::Format::RGB32_FLOAT)
.setName("Position")
.setIsInstanced(false)
.setOffset(offsetof(gfx::basic_vertex, position)),
nvrhi::VertexAttributeDesc()
.setBufferIndex(0)
.setElementStride(sizeof(gfx::basic_vertex))
.setFormat(nvrhi::Format::RGB32_FLOAT)
.setName("Normal")
.setIsInstanced(false)
.setOffset(offsetof(gfx::basic_vertex, normal)),
nvrhi::VertexAttributeDesc()
.setBufferIndex(0)
.setElementStride(sizeof(gfx::basic_vertex))
.setFormat(nvrhi::Format::RG32_FLOAT)
.setName("TexCoord")
.setIsInstanced(false)
.setOffset(offsetof(gfx::basic_vertex, texture)),
nvrhi::VertexAttributeDesc()
.setBufferIndex(0)
.setElementStride(sizeof(gfx::basic_vertex))
.setFormat(nvrhi::Format::RGBA32_FLOAT)
.setName("Tangent")
.setIsInstanced(false)
.setOffset(offsetof(gfx::basic_vertex, tangent)),
nvrhi::VertexAttributeDesc()
.setBufferIndex(1)
.setElementStride(sizeof(glm::mat3x4))
.setFormat(nvrhi::Format::RGBA32_FLOAT)
.setName("InstanceTransform")
.setIsInstanced(true)
.setArraySize(3)
.setOffset(0)};
m_input_layout[static_cast<size_t>(DrawType::Model)] =
m_backend->GetDevice()->createInputLayout(
desc, std::size(desc) - 1,
m_vertex_shader[static_cast<size_t>(DrawType::Model)]);
m_input_layout[static_cast<size_t>(DrawType::InstancedModel)] =
m_backend->GetDevice()->createInputLayout(
desc, std::size(desc),
m_vertex_shader[static_cast<size_t>(DrawType::InstancedModel)]);
{
m_binding_layout_shadow_masked =
m_backend->GetDevice()->createBindingLayout(
nvrhi::BindingLayoutDesc()
.setVisibility(nvrhi::ShaderType::Pixel)
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(0))
.addItem(nvrhi::BindingLayoutItem::Sampler(0)));
for (int i = 0; i < Constants::NumDrawTypes(); ++i) {
for (int j = 0; j < 2; ++j) {
DrawType draw = static_cast<DrawType>(i);
bool masked = j;
nvrhi::BindingLayoutDesc desc =
*m_binding_layout_shadowdrawconstants->getDesc();
desc.visibility = nvrhi::ShaderType::All;
if (masked)
desc.addItem(nvrhi::BindingLayoutItem::Texture_SRV(0))
.addItem(nvrhi::BindingLayoutItem::Sampler(0));
auto binding_layout =
GetBackend()->GetDevice()->createBindingLayout(desc);
auto pipeline_desc =
nvrhi::GraphicsPipelineDesc()
.setRenderState(
nvrhi::RenderState()
.setDepthStencilState(
nvrhi::DepthStencilState()
.enableDepthTest()
.enableDepthWrite()
.disableStencil()
.setDepthFunc(nvrhi::ComparisonFunc::Greater))
.setRasterState(nvrhi::RasterState()
.setFillSolid()
.disableDepthClip()
.disableScissor()
.setCullBack()
.setDepthBias(1)
.setSlopeScaleDepthBias(-4.f))
.setBlendState(nvrhi::BlendState().setRenderTarget(
0,
nvrhi::BlendState::RenderTarget().disableBlend())))
.setVertexShader(
m_vertex_shader_shadow[static_cast<size_t>(draw)])
.setInputLayout(m_input_layout[static_cast<size_t>(draw)]);
if (masked) {
pipeline_desc.setPixelShader(m_pixel_shader_shadow_masked);
}
pipeline_desc.addBindingLayout(binding_layout);
m_pso_shadow[Constants::GetDefaultShadowPipelineIndex(draw, masked)] =
GetBackend()->GetDevice()->createGraphicsPipeline(
pipeline_desc, m_gbuffer_shadow->m_framebuffer);
}
}
}
{
auto vs_line =
m_backend->CreateShader("vtx_line", nvrhi::ShaderType::Vertex);
auto gs_line =
m_backend->CreateShader("geo_line", nvrhi::ShaderType::Geometry);
auto ps_line =
m_backend->CreateShader("pix_line", nvrhi::ShaderType::Pixel);
nvrhi::BindingLayoutHandle binding_layout_line;
nvrhi::utils::CreateBindingSetAndLayout(
GetBackend()->GetDevice(), nvrhi::ShaderType::AllGraphics, 0,
nvrhi::BindingSetDesc()
.addItem(
nvrhi::BindingSetItem::ConstantBuffer(0, m_drawconstant_buffer))
.addItem(nvrhi::BindingSetItem::PushConstants(
1, sizeof(PushConstantsLine))),
binding_layout_line, m_binding_set_line);
m_pso_line = GetBackend()->GetDevice()->createGraphicsPipeline(
nvrhi::GraphicsPipelineDesc()
.setRenderState(
nvrhi::RenderState()
.setDepthStencilState(
nvrhi::DepthStencilState()
.enableDepthTest()
.enableDepthWrite()
.disableStencil()
.setDepthFunc(nvrhi::ComparisonFunc::Greater))
.setRasterState(nvrhi::RasterState()
.setFillSolid()
.enableDepthClip()
.disableScissor()
.setCullFront())
.setBlendState(nvrhi::BlendState().setRenderTarget(
0, nvrhi::BlendState::RenderTarget().disableBlend())))
.setPrimType(nvrhi::PrimitiveType::LineList)
.setVertexShader(vs_line)
.setGeometryShader(gs_line)
.setPixelShader(ps_line)
.setInputLayout(
m_input_layout[static_cast<size_t>(DrawType::Model)])
.addBindingLayout(binding_layout_line),
m_gbuffer->m_framebuffer);
}
auto config = YAML::LoadFile("shaders/project.manul");
for (const auto item : config["shaders"]["materials"]) {
auto material =
std::make_shared<MaterialTemplate>(item.first.as<std::string>(), this);
m_material_templates[material->m_name] = material;
material->Init(item.second);
}
return true;
}

View File

@@ -0,0 +1,7 @@
#pragma once
struct NvMaterial {
};

View File

@@ -0,0 +1 @@
#include "nvmaterial.h"

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,413 @@
#include "nvrenderer_d3d12.h"
#include <fmt/format.h>
#include "Globals.h"
#include "Logs.h"
#include "config.h"
#include "nvrenderer/nvrenderer.h"
#if LIBMANUL_WITH_D3D12
#include <GLFW/glfw3native.h>
#include <nvrhi/validation.h>
#include "nvrendererbackend.h"
#define HR_RETURN(op) \
hr = (HRESULT)(op); \
if (FAILED(hr)) { \
ErrorLog(fmt::format(#op " returned HRESULT={}", hr)); \
return false; \
}
bool NvRenderer::InitForD3D12(GLFWwindow *Window) {
// Create d3d device
std::shared_ptr<NvRendererBackend_D3D12> Backend =
std::make_shared<NvRendererBackend_D3D12>();
m_backend = Backend;
Backend->m_renderer = this;
Backend->m_hwnd = glfwGetWin32Window(Window);
if (!Backend->CreateDevice()) {
return false;
}
if (!Backend->CreateSwapChain(Window)) {
return false;
}
if (!Backend->CreateRenderTarget()) {
return false;
}
return true;
}
std::string NvRendererBackend_D3D12::LoadShaderBlob(
const std::string &filename, const std::string &entrypoint) {
std::string path = "shaders/shaders_dxil/" + filename;
if (entrypoint != "main") path += "_" + entrypoint;
path += ".bin";
std::ifstream ifs(path, std::ios::binary | std::ios::ate);
std::string blob(ifs.tellg(), '\0');
ifs.seekg(0, std::ios_base::beg);
ifs.read(blob.data(), blob.size());
return blob;
}
int NvRendererBackend_D3D12::GetCurrentBackBufferIndex() {
return m_swapchain->GetCurrentBackBufferIndex();
}
int NvRendererBackend_D3D12::GetBackBufferCount() {
return m_swapchain_desc.BufferCount;
}
nvrhi::TextureHandle NvRendererBackend_D3D12::GetBackBuffer(int index) {
return m_RhiSwapChainBuffers[index];
}
bool NvRendererBackend_D3D12::BeginFrame() {
WaitForSingleObject(
m_FrameFenceEvents[m_swapchain->GetCurrentBackBufferIndex()], INFINITE);
return true;
}
bool NvRendererBackend_D3D12::SetHDRMetaData(float MaxOutputNits /*=1000.0f*/,
float MinOutputNits /*=0.001f*/,
float MaxCLL /*=2000.0f*/,
float MaxFALL /*=500.0f*/) {
HRESULT hr;
struct DisplayChromaticities {
float RedX;
float RedY;
float GreenX;
float GreenY;
float BlueX;
float BlueY;
float WhiteX;
float WhiteY;
};
if (!m_swapchain) {
return false;
}
auto hdr_config =
NvRenderer::Config()->m_display.GetHDRConfig(IsHDRDisplay());
// Clean the hdr metadata if the display doesn't support HDR
// if (!m_hdrSupport) {
// ThrowIfFailed(
// m_swapChain->SetHDRMetaData(DXGI_HDR_METADATA_TYPE_NONE, 0, nullptr));
// return;
//}
static const DisplayChromaticities DisplayChromaticityList[] = {
{0.64000f, 0.33000f, 0.30000f, 0.60000f, 0.15000f, 0.06000f, 0.31270f,
0.32900f}, // Display Gamut Rec709
{0.70800f, 0.29200f, 0.17000f, 0.79700f, 0.13100f, 0.04600f, 0.31270f,
0.32900f}, // Display Gamut Rec2020
};
// Select the chromaticity based on HDR format of the DWM.
int selectedChroma = 1;
// if (true//m_currentSwapChainBitDepth == _16 &&
// m_currentSwapChainColorSpace ==
// DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709) {
// selectedChroma = 0;
// } else if (m_currentSwapChainBitDepth == _10 &&
// m_currentSwapChainColorSpace ==
// DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020) {
// selectedChroma = 1;
// } else {
// // Reset the metadata since this is not a supported HDR format.
// HR_RETURN(
// m_swapChain->SetHDRMetaData(DXGI_HDR_METADATA_TYPE_NONE, 0,
// nullptr));
// return;
// }
// Set HDR meta data
const DisplayChromaticities &Chroma = DisplayChromaticityList[selectedChroma];
DXGI_HDR_METADATA_HDR10 HDR10MetaData = {};
HDR10MetaData.RedPrimary[0] = static_cast<UINT16>(Chroma.RedX * 50000.0f);
HDR10MetaData.RedPrimary[1] = static_cast<UINT16>(Chroma.RedY * 50000.0f);
HDR10MetaData.GreenPrimary[0] = static_cast<UINT16>(Chroma.GreenX * 50000.0f);
HDR10MetaData.GreenPrimary[1] = static_cast<UINT16>(Chroma.GreenY * 50000.0f);
HDR10MetaData.BluePrimary[0] = static_cast<UINT16>(Chroma.BlueX * 50000.0f);
HDR10MetaData.BluePrimary[1] = static_cast<UINT16>(Chroma.BlueY * 50000.0f);
HDR10MetaData.WhitePoint[0] = static_cast<UINT16>(Chroma.WhiteX * 50000.0f);
HDR10MetaData.WhitePoint[1] = static_cast<UINT16>(Chroma.WhiteY * 50000.0f);
HDR10MetaData.MaxMasteringLuminance =
static_cast<UINT>(hdr_config.GetMaxOutputNits() * 10000.0f);
HDR10MetaData.MinMasteringLuminance =
static_cast<UINT>(hdr_config.GetMinOutputNits() * 10000.0f);
HDR10MetaData.MaxContentLightLevel = static_cast<UINT16>(MaxCLL);
HDR10MetaData.MaxFrameAverageLightLevel = static_cast<UINT16>(MaxFALL);
if (IsHDRDisplay()) {
HR_RETURN(m_swapchain->SetHDRMetaData(DXGI_HDR_METADATA_TYPE_HDR10,
sizeof(DXGI_HDR_METADATA_HDR10),
&HDR10MetaData))
HR_RETURN(
m_swapchain->SetColorSpace1(DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020))
} else {
HR_RETURN(
m_swapchain->SetColorSpace1(DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709))
}
return true;
}
void NvRendererBackend_D3D12::Present() {
// if (!m_windowVisible)
// return;
auto bufferIndex = m_swapchain->GetCurrentBackBufferIndex();
UINT presentFlags = 0;
if (false /*!vsync_enabled*/ && !Global.bFullScreen && m_TearingSupported)
presentFlags |= DXGI_PRESENT_ALLOW_TEARING;
m_swapchain->Present(1, presentFlags);
m_FrameFence->SetEventOnCompletion(m_FrameCount,
m_FrameFenceEvents[bufferIndex]);
m_graphics_queue->Signal(m_FrameFence, m_FrameCount);
m_FrameCount++;
}
bool NvRendererBackend_D3D12::IsHDRDisplay() const {
return m_output_desc.BitsPerColor > 8 &&
!NvRenderer::Config()->m_display.m_disable_hdr;
}
bool NvRendererBackend_D3D12::CreateDevice() {
HRESULT hr;
HR_RETURN(
CreateDXGIFactory2(Global.gfx_gldebug ? DXGI_CREATE_FACTORY_DEBUG : 0,
IID_PPV_ARGS(&m_dxgi_factory)))
if (Global.gfx_gldebug) {
nvrhi::RefCountPtr<ID3D12Debug> pDebug;
HR_RETURN(D3D12GetDebugInterface(IID_PPV_ARGS(&pDebug)))
pDebug->EnableDebugLayer();
}
WriteLog("Available graphics adapters:");
for (int i = 0;; ++i) {
nvrhi::RefCountPtr<IDXGIAdapter> adapter = nullptr;
if (FAILED(m_dxgi_factory->EnumAdapters(i, &adapter))) break;
DXGI_ADAPTER_DESC desc{};
HR_RETURN(adapter->GetDesc(&desc));
std::string str_desc{};
str_desc.resize(
wcstombs(nullptr, desc.Description, std::size(desc.Description)));
wcstombs(str_desc.data(), desc.Description, str_desc.size());
WriteLog(fmt::format("- adapter{}: {}", i, str_desc));
}
if (FAILED(m_dxgi_factory->EnumAdapters(
NvRenderer::Config()->m_display.m_adapter_index, &m_dxgi_adapter))) {
ErrorLog(fmt::format("Invalid adapter index: {}",
NvRenderer::Config()->m_display.m_adapter_index));
return false;
}
nvrhi::RefCountPtr<IDXGIOutput> output;
nvrhi::RefCountPtr<IDXGIOutput6> output6;
if (FAILED(m_dxgi_adapter->EnumOutputs(
NvRenderer::Config()->m_display.m_output_index, &output))) {
ErrorLog(fmt::format("Invalid output index: {}",
NvRenderer::Config()->m_display.m_output_index));
}
HR_RETURN(output->QueryInterface<IDXGIOutput6>(&output6));
HR_RETURN(output6->GetDesc1(&m_output_desc));
// {
// DXGI_ADAPTER_DESC aDesc;
// m_DxgiAdapter->GetDesc(&aDesc);
//
// m_RendererString = GetAdapterName(aDesc);
// m_IsNvidia = IsNvDeviceID(aDesc.VendorId);
// }
HR_RETURN(D3D12CreateDevice(m_dxgi_adapter, D3D_FEATURE_LEVEL_12_0,
IID_PPV_ARGS(&m_d3d_device)))
if (Global.gfx_gldebug) {
nvrhi::RefCountPtr<ID3D12InfoQueue> pInfoQueue;
m_d3d_device->QueryInterface(&pInfoQueue);
if (pInfoQueue) {
pInfoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_CORRUPTION, true);
pInfoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, true);
D3D12_MESSAGE_ID disableMessageIDs[] = {
D3D12_MESSAGE_ID_CLEARDEPTHSTENCILVIEW_MISMATCHINGCLEARVALUE,
D3D12_MESSAGE_ID_COMMAND_LIST_STATIC_DESCRIPTOR_RESOURCE_DIMENSION_MISMATCH, // descriptor validation doesn't understand acceleration structures
};
D3D12_INFO_QUEUE_FILTER filter = {};
filter.DenyList.pIDList = disableMessageIDs;
filter.DenyList.NumIDs =
sizeof(disableMessageIDs) / sizeof(disableMessageIDs[0]);
pInfoQueue->AddStorageFilterEntries(&filter);
}
}
D3D12_COMMAND_QUEUE_DESC queueDesc;
ZeroMemory(&queueDesc, sizeof(queueDesc));
queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
queueDesc.Priority = D3D12_COMMAND_QUEUE_PRIORITY_HIGH;
queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
queueDesc.NodeMask = 1;
HR_RETURN(m_d3d_device->CreateCommandQueue(&queueDesc,
IID_PPV_ARGS(&m_graphics_queue)))
m_graphics_queue->SetName(L"Graphics Queue");
if (true) {
queueDesc.Type = D3D12_COMMAND_LIST_TYPE_COMPUTE;
queueDesc.Priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL;
HR_RETURN(m_d3d_device->CreateCommandQueue(&queueDesc,
IID_PPV_ARGS(&m_compute_queue)))
m_compute_queue->SetName(L"Compute Queue");
}
if (true) {
queueDesc.Type = D3D12_COMMAND_LIST_TYPE_COPY;
queueDesc.Priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL;
HR_RETURN(m_d3d_device->CreateCommandQueue(&queueDesc,
IID_PPV_ARGS(&m_copy_queue)))
m_copy_queue->SetName(L"Copy Queue");
}
nvrhi::d3d12::DeviceDesc deviceDesc;
deviceDesc.errorCB = GetMessageCallback();
deviceDesc.pDevice = m_d3d_device;
deviceDesc.pGraphicsCommandQueue = m_graphics_queue;
deviceDesc.pComputeCommandQueue = m_compute_queue;
deviceDesc.pCopyCommandQueue = m_copy_queue;
m_device = nvrhi::d3d12::createDevice(deviceDesc);
if (Global.gfx_gldebug) {
m_device = nvrhi::validation::createValidationLayer(m_device);
}
return true;
}
bool NvRendererBackend_D3D12::CreateSwapChain(GLFWwindow *window) {
HRESULT hr;
ZeroMemory(&m_swapchain_desc, sizeof(m_swapchain_desc));
if (Global.bFullScreen) {
const GLFWvidmode *mode = glfwGetVideoMode(glfwGetWindowMonitor(window));
m_swapchain_desc.Width = mode->width;
m_swapchain_desc.Height = mode->height;
} else {
int fbWidth = 0, fbHeight = 0;
glfwGetFramebufferSize(window, &fbWidth, &fbHeight);
m_swapchain_desc.Width = fbWidth;
m_swapchain_desc.Height = fbHeight;
}
m_swapchain_desc.BufferCount = 3;
m_swapchain_desc.BufferUsage =
DXGI_USAGE_SHADER_INPUT | DXGI_USAGE_RENDER_TARGET_OUTPUT;
m_swapchain_desc.SampleDesc.Count = 1;
m_swapchain_desc.SampleDesc.Quality = 0;
m_swapchain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
m_swapchain_desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
m_swapchain_desc.Format = IsHDRDisplay() ? DXGI_FORMAT_R10G10B10A2_UNORM : DXGI_FORMAT_R8G8B8A8_UNORM;
nvrhi::RefCountPtr<IDXGIFactory5> pDxgiFactory5;
if (SUCCEEDED(m_dxgi_factory->QueryInterface(IID_PPV_ARGS(&pDxgiFactory5)))) {
BOOL supported = 0;
if (SUCCEEDED(pDxgiFactory5->CheckFeatureSupport(
DXGI_FEATURE_PRESENT_ALLOW_TEARING, &supported, sizeof(supported))))
m_TearingSupported = (supported != 0);
}
if (m_TearingSupported) {
m_swapchain_desc.Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
}
m_fullscreen_desc = {};
m_fullscreen_desc.RefreshRate.Numerator = 0;
m_fullscreen_desc.RefreshRate.Denominator = 1;
m_fullscreen_desc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_PROGRESSIVE;
m_fullscreen_desc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
m_fullscreen_desc.Windowed = !Global.bFullScreen;
nvrhi::RefCountPtr<IDXGISwapChain1> pSwapChain1;
HR_RETURN(m_dxgi_factory->CreateSwapChainForHwnd(
m_graphics_queue, m_hwnd, &m_swapchain_desc, &m_fullscreen_desc, nullptr,
&pSwapChain1))
HR_RETURN(pSwapChain1->QueryInterface(IID_PPV_ARGS(&m_swapchain)))
if (!CreateRenderTarget()) return false;
HR_RETURN(m_d3d_device->CreateFence(0, D3D12_FENCE_FLAG_NONE,
IID_PPV_ARGS(&m_FrameFence)));
for (UINT bufferIndex = 0; bufferIndex < m_swapchain_desc.BufferCount;
bufferIndex++) {
m_FrameFenceEvents.push_back(CreateEvent(nullptr, false, true, nullptr));
}
if (!SetHDRMetaData()) return false;
return true;
}
bool NvRendererBackend_D3D12::CreateRenderTarget() {
m_SwapChainBuffers.resize(m_swapchain_desc.BufferCount);
m_RhiSwapChainBuffers.resize(m_swapchain_desc.BufferCount);
HRESULT hr;
for (UINT n = 0; n < m_swapchain_desc.BufferCount; n++) {
HR_RETURN(m_swapchain->GetBuffer(n, IID_PPV_ARGS(&m_SwapChainBuffers[n])))
nvrhi::TextureDesc textureDesc;
textureDesc.width = m_swapchain_desc.Width;
textureDesc.height = m_swapchain_desc.Height;
textureDesc.sampleCount = m_swapchain_desc.SampleDesc.Count;
textureDesc.sampleQuality = m_swapchain_desc.SampleDesc.Quality;
textureDesc.format = IsHDRDisplay() ? nvrhi::Format::R10G10B10A2_UNORM : nvrhi::Format::RGBA8_UNORM;
textureDesc.debugName = "SwapChainBuffer";
textureDesc.isRenderTarget = true;
textureDesc.isUAV = false;
textureDesc.initialState = nvrhi::ResourceStates::Present;
textureDesc.keepInitialState = true;
m_RhiSwapChainBuffers[n] = m_device->createHandleForNativeTexture(
nvrhi::ObjectTypes::D3D12_Resource,
nvrhi::Object(m_SwapChainBuffers[n]), textureDesc);
}
return true;
}
void NvRendererBackend_D3D12::ReleaseRenderTarget() {
// Make sure that all frames have finished rendering
m_device->waitForIdle();
// Release all in-flight references to the render targets
m_device->runGarbageCollection();
// Set the events so that WaitForSingleObject in OneFrame will not hang later
for (auto e : m_FrameFenceEvents) SetEvent(e);
// Release the old buffers because ResizeBuffers requires that
m_RhiSwapChainBuffers.clear();
m_SwapChainBuffers.clear();
}
#endif

View File

@@ -0,0 +1,72 @@
#pragma once
#include <nvrhi/nvrhi.h>
#include "nvrenderer/nvrenderer.h"
#include "nvrendererbackend.h"
#if LIBMANUL_WITH_D3D12
#include <Windows.h>
#include <dxgi1_6.h>
#include <dxgidebug.h>
#include <nvrhi/d3d12.h>
class NvRendererBackend_D3D12 : public NvRendererBackend {
friend class NvRenderer;
virtual std::string LoadShaderBlob(const std::string &filename,
const std::string &entrypoint) override;
virtual int GetCurrentBackBufferIndex() override;
virtual int GetBackBufferCount() override;
virtual nvrhi::TextureHandle GetBackBuffer(int index) override;
virtual bool BeginFrame() override;
virtual void Present() override;
virtual bool IsHDRDisplay() const override;
virtual void UpdateWindowSize() override{}
bool SetHDRMetaData(float MaxOutputNits = 1000.0f,
float MinOutputNits = 0.001f, float MaxCLL = 2000.0f,
float MaxFALL = 500.0f);
// Window handle
HWND m_hwnd;
// Direct3D resources
nvrhi::RefCountPtr<ID3D12Device> m_d3d_device;
nvrhi::RefCountPtr<ID3D12CommandQueue> m_graphics_queue;
nvrhi::RefCountPtr<ID3D12CommandQueue> m_aux_graphics_queue;
nvrhi::RefCountPtr<ID3D12CommandQueue> m_compute_queue;
nvrhi::RefCountPtr<ID3D12CommandQueue> m_copy_queue;
nvrhi::RefCountPtr<IDXGIFactory2> m_dxgi_factory;
nvrhi::RefCountPtr<IDXGIAdapter> m_dxgi_adapter;
nvrhi::RefCountPtr<IDXGISwapChain4> m_swapchain;
DXGI_SWAP_CHAIN_DESC1 m_swapchain_desc;
DXGI_SWAP_CHAIN_FULLSCREEN_DESC m_fullscreen_desc;
DXGI_OUTPUT_DESC1 m_output_desc;
bool m_TearingSupported = false;
std::vector<nvrhi::RefCountPtr<ID3D12Resource>> m_SwapChainBuffers;
std::vector<nvrhi::TextureHandle> m_RhiSwapChainBuffers;
nvrhi::RefCountPtr<ID3D12Fence> m_FrameFence;
std::vector<HANDLE> m_FrameFenceEvents;
UINT64 m_FrameCount = 1;
// RHI resources
// Utility functions
bool CreateDevice();
bool CreateSwapChain(GLFWwindow *window);
bool CreateRenderTarget();
void ReleaseRenderTarget();
virtual void LoadShaderBlobs() override;
nvrhi::DeviceHandle m_device;
public:
nvrhi::IDevice *GetDevice() override { return m_device.Get(); }
};
#endif

View File

@@ -0,0 +1,19 @@
#include "nvrenderer_imgui.h"
#include "nvrendererbackend.h"
bool NvImguiRenderer::Init() { return m_rhi.init(m_backend.get()); }
void NvImguiRenderer::Shutdown() {}
void NvImguiRenderer::BeginFrame() { m_rhi.beginFrame(); }
void NvImguiRenderer::Render() {
m_rhi.render(m_backend->GetCurrentFramebuffer());
}
NvImguiRenderer::NvImguiRenderer(NvRenderer *renderer)
: m_backend(renderer->m_backend) {
m_rhi.renderer = m_backend->GetDevice();
m_rhi.textureManager = renderer->GetTextureManager();
}

View File

@@ -0,0 +1,17 @@
#pragma once
#include "imgui_nvrhi.h"
#include "nvrenderer/nvrenderer.h"
class NvImguiRenderer : public imgui_renderer {
virtual bool Init() override;
virtual void Shutdown() override;
virtual void BeginFrame() override;
virtual void Render() override;
std::shared_ptr<class NvRendererBackend> m_backend;
ImGui_NVRHI m_rhi;
public:
NvImguiRenderer(class NvRenderer *renderer);
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,182 @@
#pragma once
#include <nvrhi/nvrhi.h>
#include "nvrenderer/nvrenderer.h"
#include "nvrendererbackend.h"
#if LIBMANUL_WITH_VULKAN
#define VULKAN_HPP_DISPATCH_LOADER_DYNAMIC 1
#include <nvrhi/vulkan.h>
#include <vulkan/vulkan.hpp>
class NvRendererBackend_Vulkan : public NvRendererBackend {
friend class NvRenderer;
nvrhi::IDevice *GetDevice() override;
std::string LoadShaderBlob(const std::string &filename,
const std::string &entrypoint) override;
virtual int GetCurrentBackBufferIndex() override;
virtual int GetBackBufferCount() override;
virtual nvrhi::TextureHandle GetBackBuffer(int index) override;
virtual bool BeginFrame() override;
virtual void Present() override;
virtual void UpdateWindowSize() override;
bool IsHDRDisplay() const override;
// bool SetHDRMetaData(float MaxOutputNits = 1000.0f,
// float MinOutputNits = 0.001f, float MaxCLL = 2000.0f,
// float MaxFALL = 500.0f);
//// Window handle
// HWND m_hwnd;
//
//// Direct3D resources
// nvrhi::RefCountPtr<ID3D12Device> m_d3d_device;
// nvrhi::RefCountPtr<ID3D12CommandQueue> m_graphics_queue;
// nvrhi::RefCountPtr<ID3D12CommandQueue> m_aux_graphics_queue;
// nvrhi::RefCountPtr<ID3D12CommandQueue> m_compute_queue;
// nvrhi::RefCountPtr<ID3D12CommandQueue> m_copy_queue;
// nvrhi::RefCountPtr<IDXGIFactory2> m_dxgi_factory;
// nvrhi::RefCountPtr<IDXGIAdapter> m_dxgi_adapter;
// nvrhi::RefCountPtr<IDXGISwapChain4> m_swapchain;
// DXGI_SWAP_CHAIN_DESC1 m_swapchain_desc;
// DXGI_SWAP_CHAIN_FULLSCREEN_DESC m_fullscreen_desc;
// DXGI_OUTPUT_DESC1 m_output_desc;
// bool m_TearingSupported = false;
//
// std::vector<nvrhi::RefCountPtr<ID3D12Resource>> m_SwapChainBuffers;
// std::vector<nvrhi::TextureHandle> m_RhiSwapChainBuffers;
// nvrhi::RefCountPtr<ID3D12Fence> m_FrameFence;
// std::vector<HANDLE> m_FrameFenceEvents;
// UINT64 m_FrameCount = 1;
// RHI resources
// Utility functions
// bool CreateDevice();
// bool CreateSwapChain(GLFWwindow *window);
// bool CreateRenderTarget();
// void ReleaseRenderTarget();
virtual void LoadShaderBlobs() override;
public:
bool CreateInstance();
bool CreateDevice();
bool CreateSwapChain();
void ResizeSwapChain();
[[nodiscard]] PFN_vkGetDeviceProcAddr GetDeviceProcAddr();
private:
int32_t m_BackBufferWidth;
int32_t m_BackBufferHeight;
bool m_VsyncEnabled;
bool m_EnableNvrhiValidationLayer;
bool m_EnableDebugRuntime = false;
bool createInstance();
bool createWindowSurface();
void installDebugCallback();
bool pickPhysicalDevice();
bool findQueueFamilies(vk::PhysicalDevice physicalDevice);
bool createDevice();
bool createSwapChain();
void destroySwapChain();
struct VulkanExtensionSet {
std::unordered_set<std::string> instance;
std::unordered_set<std::string> layers;
std::unordered_set<std::string> device;
};
// minimal set of required extensions
VulkanExtensionSet enabledExtensions = {
// instance
{
VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME,
VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME,
},
// layers
{},
// device
{
VK_KHR_MAINTENANCE1_EXTENSION_NAME,
VK_EXT_SAMPLER_FILTER_MINMAX_EXTENSION_NAME,
},
};
// optional extensions
VulkanExtensionSet optionalExtensions = {
// instance
{VK_EXT_DEBUG_UTILS_EXTENSION_NAME},
// layers
{},
// device
{VK_EXT_DEBUG_MARKER_EXTENSION_NAME,
VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME,
VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME,
VK_NV_MESH_SHADER_EXTENSION_NAME,
VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME,
VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME,
VK_KHR_MAINTENANCE_4_EXTENSION_NAME,
VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME},
};
std::string m_RendererString;
vk::DynamicLoader m_dynamicLoader;
vk::Instance m_VulkanInstance;
vk::DebugReportCallbackEXT m_DebugReportCallback;
vk::SurfaceKHR m_WindowSurface;
vk::PhysicalDevice m_VulkanPhysicalDevice;
vk::Device m_VulkanDevice;
vk::Queue m_GraphicsQueue;
vk::Queue m_ComputeQueue;
vk::Queue m_TransferQueue;
vk::Queue m_PresentQueue;
int m_GraphicsQueueFamily = -1;
int m_ComputeQueueFamily = -1;
int m_TransferQueueFamily = -1;
int m_PresentQueueFamily = -1;
int m_SwapChainBufferCount;
vk::SurfaceFormatKHR m_SwapChainFormat;
vk::SwapchainKHR m_SwapChain;
struct SwapChainImage {
vk::Image image;
nvrhi::TextureHandle rhiHandle;
};
std::vector<SwapChainImage> m_SwapChainImages;
uint32_t m_SwapChainIndex = static_cast<uint32_t>(-1);
int m_MaxFramesInFlight = 2;
nvrhi::vulkan::DeviceHandle m_NvrhiDevice;
nvrhi::DeviceHandle m_ValidationLayer;
nvrhi::CommandListHandle m_BarrierCommandList;
std::vector<vk::Semaphore> m_PresentSemaphores;
uint32_t m_PresentSemaphoreIndex = 0;
std::queue<nvrhi::EventQueryHandle> m_FramesInFlight;
std::vector<nvrhi::EventQueryHandle> m_QueryPool;
GLFWwindow *m_Window;
bool m_EnableComputeQueue;
bool m_EnableCopyQueue;
bool m_SwapChainMutableFormatSupported = false;
bool m_BufferDeviceAddressSupported = false;
};
#endif

View File

@@ -0,0 +1,86 @@
#include "nvrendererbackend.h"
#include "Logs.h"
#include <fmt/format.h>
void NvRendererMessageCallback::message(nvrhi::MessageSeverity severity,
const char *messageText) {
switch (severity) {
case nvrhi::MessageSeverity::Info:
case nvrhi::MessageSeverity::Warning:
WriteLog(messageText);
break;
case nvrhi::MessageSeverity::Error:
ErrorLog(messageText);
//__debugbreak();
break;
case nvrhi::MessageSeverity::Fatal:
ErrorLog(messageText);
//__debugbreak();
break;
}
}
void NvRendererBackend::Init() {
LoadShaderBlobs();
for (int i = 0; i < GetBackBufferCount(); ++i) {
m_framebuffers.emplace_back(GetDevice()->createFramebuffer(
nvrhi::FramebufferDesc().addColorAttachment(
nvrhi::FramebufferAttachment().setTexture(GetBackBuffer(i)))));
}
}
nvrhi::FramebufferHandle NvRendererBackend::GetCurrentFramebuffer() {
return m_framebuffers[GetCurrentBackBufferIndex()];
}
nvrhi::ShaderHandle NvRendererBackend::CreateShader(const std::string &name,
nvrhi::ShaderType type) {
if (auto blob_found = m_shader_blobs.find(name);
blob_found != m_shader_blobs.end()) {
const auto &blob = blob_found->second;
nvrhi::ShaderDesc shader_desc{};
shader_desc.shaderType = type;
shader_desc.debugName = name;
shader_desc.entryName = blob.m_entrypoint;
return GetDevice()->createShader(shader_desc, blob.m_blob.data(),
blob.m_blob.size());
}
return nullptr;
}
nvrhi::ShaderHandle NvRendererBackend::CreateMaterialShader(
const std::string &name, NvRenderer::RenderPassType pass) {
if (auto blob_found = m_material_blobs.find(name);
blob_found != m_material_blobs.end()) {
const auto &blob = blob_found->second[static_cast<size_t>(pass)];
WriteLog(name);
WriteLog(blob.m_entrypoint);
WriteLog(fmt::format("{:d}", static_cast<int>(pass)));
if (blob.m_blob.empty()) return nullptr;
nvrhi::ShaderDesc shader_desc{};
shader_desc.shaderType = nvrhi::ShaderType::Pixel;
shader_desc.debugName = name;
shader_desc.entryName = blob.m_entrypoint;
return GetDevice()->createShader(shader_desc, blob.m_blob.data(),
blob.m_blob.size());
}
return nullptr;
}
void NvRendererBackend::AddMaterialShaderBlob(std::string_view material,
std::string_view pass,
std::string blob,
const std::string &entrypoint) {
const static std::unordered_map<std::string_view, NvRenderer::RenderPassType>
material_passes{{"deferred", NvRenderer::RenderPassType::Deferred},
{"forward", NvRenderer::RenderPassType::Forward},
{"cubemap", NvRenderer::RenderPassType::CubeMap}};
m_material_blobs[std::string(material)][static_cast<size_t>(
material_passes.at(pass))] = {entrypoint, blob};
}
void NvRendererBackend::AddShaderBlob(std::string_view shader, std::string blob,
const std::string &entrypoint) {
m_shader_blobs[std::string(shader)] = {entrypoint, blob};
}

View File

@@ -0,0 +1,58 @@
#pragma once
#include <nvrhi/nvrhi.h>
#include "nvrenderer/nvrenderer.h"
struct ShaderDefine {
std::string m_name;
std::string m_definition;
};
class NvRendererBackend {
protected:
friend class NvRenderer;
NvRenderer *m_renderer;
virtual std::string LoadShaderBlob(const std::string &fileName,
const std::string &entryName) = 0;
virtual bool BeginFrame() = 0;
virtual void Present() = 0;
virtual void UpdateWindowSize() = 0;
virtual int GetCurrentBackBufferIndex() = 0;
virtual int GetBackBufferCount() = 0;
virtual nvrhi::TextureHandle GetBackBuffer(int index) = 0;
class NvRendererMessageCallback *GetMessageCallback() const {
return m_renderer->m_message_callback.get();
}
void AddMaterialShaderBlob(std::string_view material, std::string_view pass,
std::string blob, const std::string &entrypoint);
void AddShaderBlob(std::string_view shader, std::string blob,
const std::string &entrypoint);
virtual void LoadShaderBlobs() {}
struct ShaderBlob {
std::string m_entrypoint;
std::string m_blob;
};
std::unordered_map<std::string, ShaderBlob> m_shader_blobs;
std::unordered_map<
std::string,
std::array<ShaderBlob, NvRenderer::Constants::NumMaterialPasses()>>
m_material_blobs;
public:
void Init();
virtual bool IsHDRDisplay() const { return false; }
// nvrhi::DeviceHandle m_device;
virtual nvrhi::IDevice *GetDevice() { return nullptr; };
std::vector<nvrhi::FramebufferHandle> m_framebuffers;
nvrhi::FramebufferHandle GetCurrentFramebuffer();
nvrhi::ShaderHandle CreateShader(const std::string &name,
nvrhi::ShaderType type);
nvrhi::ShaderHandle CreateMaterialShader(const std::string &name,
NvRenderer::RenderPassType pass);
};
class NvRendererMessageCallback : public nvrhi::IMessageCallback {
virtual void message(nvrhi::MessageSeverity severity,
const char *messageText) override;
};

View File

@@ -0,0 +1,512 @@
#include "nvtexture.h"
#include "nvrenderer/nvrenderer.h"
#include <Globals.h>
#include <PyInt.h>
#include <application.h>
#include <dictionary.h>
#include <fmt/format.h>
#include <utilities.h>
#include "Logs.h"
// #include "Texture.h"
#include "nvrendererbackend.h"
#include "stbi/stb_image.h"
#define STB_IMAGE_RESIZE2_IMPLEMENTATION
#include "stbi/stb_image_resize2.h"
#undef STB_IMAGE_RESIZE2_IMPLEMENTATION
uint64_t NvTexture::s_change_counter = 0;
void NvTexture::Load(int size_bias) {
if (m_sz_texture->is_stub()) {
m_width = 2;
m_height = 2;
auto &[pitch, data] = m_data.emplace_back();
pitch = m_width * 4;
m_format = nvrhi::Format::RGBA8_UNORM;
data.resize(pitch * m_height, '\0');
return;
}
std::string buf{};
{
std::ifstream file(fmt::format("{}{}", m_sz_texture->get_name().data(),
m_sz_texture->get_type().data()),
std::ios::binary | std::ios::ate);
buf.resize(file.tellg(), '\0');
file.seekg(0);
file.read(buf.data(), buf.size());
};
stbi_set_flip_vertically_on_load(true);
if (LoadDDS(buf, size_bias)) {
} else if (int channels; stbi_uc *source = stbi_load_from_memory(
reinterpret_cast<stbi_uc const *>(buf.data()),
buf.size(), &m_width, &m_height, &channels, 4)) {
int mipcount =
static_cast<int>(floor(log2(std::max(m_width, m_height)))) + 1;
m_has_alpha = channels == 4;
m_format =
m_srgb ? nvrhi::Format::SRGBA8_UNORM : nvrhi::Format::RGBA8_UNORM;
int width = m_width, prev_width = m_width, height = m_height,
prev_height = m_height;
for (int i = 0; i < mipcount; ++i) {
auto &[pitch, data] = m_data.emplace_back();
pitch = width * 4;
data.resize(pitch * height, '\0');
if (!i) {
memcpy(data.data(), source, data.size());
} else {
stbir_resize(m_data[i - 1].second.data(), prev_width, prev_height,
prev_width * 4, data.data(), width, height, width * 4,
STBIR_4CHANNEL,
m_srgb ? STBIR_TYPE_UINT8_SRGB : STBIR_TYPE_UINT8,
STBIR_EDGE_WRAP, STBIR_FILTER_MITCHELL);
}
prev_width = std::exchange(width, std::max(1, width >> 1));
prev_height = std::exchange(height, std::max(1, height >> 1));
}
stbi_image_free(source);
} else if (m_sz_texture->get_type() == ".tga") {
std::stringstream ss(buf, std::ios::in | std::ios::binary);
// Read the header of the TGA, compare it with the known headers for
// compressed and uncompressed TGAs
unsigned char tgaheader[18];
ss.read((char *)tgaheader, sizeof(unsigned char) * 18);
while (tgaheader[0] > 0) {
--tgaheader[0];
unsigned char temp;
ss.read((char *)&temp, sizeof(unsigned char));
}
m_width = tgaheader[13] * 256 + tgaheader[12];
m_height = tgaheader[15] * 256 + tgaheader[14];
m_format =
m_srgb ? nvrhi::Format::SRGBA8_UNORM : nvrhi::Format::RGBA8_UNORM;
int const bytesperpixel = tgaheader[16] / 8;
// check whether width, height an BitsPerPixel are valid
if ((m_width <= 0) || (m_height <= 0) ||
((bytesperpixel != 1) && (bytesperpixel != 3) &&
(bytesperpixel != 4))) {
return;
}
m_has_alpha = false;
auto &[pitch, data] = m_data.emplace_back();
pitch = m_width * 4;
// allocate the data buffer
int const datasize = pitch * m_height;
data.resize(datasize);
// call the appropriate loader-routine
if (tgaheader[2] == 2) {
// uncompressed TGA
// rgb or greyscale image, expand to bgra
unsigned char buffer[4] = {255, 255, 255,
255}; // alpha channel will be white
unsigned int *datapointer = (unsigned int *)&data[0];
unsigned int *bufferpointer = (unsigned int *)&buffer[0];
int const pixelcount = m_width * m_height;
for (int i = 0; i < pixelcount; ++i) {
ss.read((char *)&buffer[0], sizeof(unsigned char) * bytesperpixel);
if (bytesperpixel == 1) {
// expand greyscale data
buffer[1] = buffer[0];
buffer[2] = buffer[0];
}
std::swap(buffer[0], buffer[2]);
// copy all four values in one operation
(*datapointer) = (*bufferpointer);
++datapointer;
}
} else if (tgaheader[2] == 10) {
// compressed TGA
int currentpixel = 0;
unsigned char buffer[4] = {255, 255, 255, 255};
const int pixelcount = m_width * m_height;
unsigned int *datapointer = (unsigned int *)&data[0];
unsigned int *bufferpointer = (unsigned int *)&buffer[0];
do {
unsigned char chunkheader = 0;
ss.read((char *)&chunkheader, sizeof(unsigned char));
if ((chunkheader & 0x80) == 0) {
// if the high bit is not set, it means it is the number of RAW color
// packets, plus 1
for (int i = 0; i <= chunkheader; ++i) {
ss.read((char *)&buffer[0], bytesperpixel);
m_has_alpha |= bytesperpixel == 4 && buffer[3] != 255;
if (bytesperpixel == 1) {
// expand greyscale data
buffer[1] = buffer[0];
buffer[2] = buffer[0];
}
std::swap(buffer[0], buffer[2]);
// copy all four values in one operation
(*datapointer) = (*bufferpointer);
++datapointer;
++currentpixel;
}
} else {
// rle chunk, the color supplied afterwards is reapeated header + 1
// times (not including the highest bit)
chunkheader &= ~0x80;
// read the current color
ss.read((char *)&buffer[0], bytesperpixel);
m_has_alpha |= bytesperpixel == 4 && buffer[3] != 255;
if (bytesperpixel == 1) {
// expand greyscale data
buffer[1] = buffer[0];
buffer[2] = buffer[0];
}
std::swap(buffer[0], buffer[2]);
// copy the color into the image data as many times as dictated
for (int i = 0; i <= chunkheader; ++i) {
(*datapointer) = (*bufferpointer);
++datapointer;
++currentpixel;
}
}
} while (currentpixel < pixelcount);
}
}
int max_size = Global.iMaxTextureSize >> size_bias;
while (m_data.size() > 1) {
if (m_width <= max_size && m_height <= max_size) {
break;
}
m_width = std::max(m_width >> 1, 1);
m_height = std::max(m_height >> 1, 1);
m_data.erase(m_data.begin());
}
}
void NvTexture::set_components_hint(int format_hint) {
m_srgb = format_hint == GL_SRGB || format_hint == GL_SRGB8 ||
format_hint == GL_SRGB_ALPHA || format_hint == GL_SRGB8_ALPHA8;
}
void NvTexture::make_from_memory(size_t width, size_t height,
const uint8_t *data) {
m_data.clear();
m_rhi_texture = nullptr;
auto &[pitch, slice] = m_data.emplace_back();
m_format = nvrhi::Format::RGBA8_UNORM;
pitch = 4 * width;
slice.resize(pitch * height);
memcpy(slice.data(), data, slice.size());
}
void NvTexture::update_from_memory(size_t width, size_t height,
const uint8_t *data) {
auto renderer = dynamic_cast<NvRenderer *>(GfxRenderer.get());
if (!renderer) return;
auto backend = renderer->GetBackend();
if (width != m_width || height != m_height ||
m_format != nvrhi::Format::RGBA8_UNORM || !m_rhi_texture) {
m_data.clear();
m_width = width;
m_height = height;
auto &[pitch, slice] = m_data.emplace_back();
m_format = nvrhi::Format::RGBA8_UNORM;
pitch = 4 * width;
slice.resize(pitch * height);
m_rhi_texture = backend->GetDevice()->createTexture(
nvrhi::TextureDesc()
.setDebugName(std::string(m_sz_texture->get_name()))
.setWidth(m_width)
.setHeight(m_height)
.setMipLevels(m_data.size())
.setFormat(m_format)
.setInitialState(nvrhi::ResourceStates::ShaderResource)
.setKeepInitialState(true));
m_last_change = ++s_change_counter;
}
auto &slice = std::get<1>(m_data.front());
memcpy(slice.data(), data, slice.size());
nvrhi::CommandListHandle command_list =
backend->GetDevice()->createCommandList(
nvrhi::CommandListParameters()
.setQueueType(nvrhi::CommandQueue::Graphics)
.setEnableImmediateExecution(false));
command_list->open();
for (int mip = 0; mip < m_data.size(); ++mip) {
const auto &[pitch, data] = m_data[mip];
command_list->writeTexture(m_rhi_texture, 0, mip, data.data(), pitch);
}
command_list->close();
backend->GetDevice()->executeCommandList(command_list,
nvrhi::CommandQueue::Graphics);
}
bool NvTexture::IsLoaded() const { return false; }
NvTextureManager::NvTextureManager(NvRenderer *renderer)
: m_backend(renderer->m_backend.get()) {}
size_t NvTextureManager::FetchTexture(std::string path, int format_hint,
int size_bias, bool unload_on_location) {
if (contains(path, '|')) {
path.erase(path.find('|')); // po | może być nazwa kolejnej tekstury
}
std::pair<std::string, std::string> locator; // resource name, resource type
std::string traits;
// discern textures generated by a script
// TBD: support file: for file resources?
auto const isgenerated{path.find("make:") == 0};
auto const isinternalsrc{path.find("internal_src:") == 0};
// process supplied resource name
if (isgenerated || isinternalsrc) {
// generated resource
// scheme:(user@)path?query
// remove scheme indicator
path.erase(0, path.find(':') + 1);
// TODO: extract traits specification from the query
// clean up slashes
erase_leading_slashes(path);
} else {
// regular file resource
// (filepath/)(#)filename.extension(:traits)
// extract trait specifications
auto const traitpos = path.rfind(':');
if (traitpos != std::string::npos) {
// po dwukropku mogą być podane dodatkowe informacje niebędące nazwą
// tekstury
if (path.size() > traitpos + 1) {
traits = path.substr(traitpos + 1);
}
path.erase(traitpos);
}
// potentially trim file type indicator since we check for multiple types
erase_extension(path);
// clean up slashes
erase_leading_slashes(path);
path = ToLower(path);
// temporary code for legacy assets -- textures with names beginning with #
// are to be sharpened
if ((starts_with(path, "#")) || (contains(path, "/#"))) {
traits += '#';
}
}
// try to locate requested texture in the databank
// TBD, TODO: include trait specification in the resource id in the databank?
{
const std::array<std::string, 3> filenames{
Global.asCurrentTexturePath + path, path, szTexturePath + path};
for (const auto &filename : filenames) {
if (auto found = m_texture_map.find(filename);
found != m_texture_map.end())
return found->second;
}
}
// if the lookup fails...
if (isgenerated) {
// TODO: verify presence of the generator script
locator.first = path;
locator.second = "make:";
} else if (isinternalsrc) {
locator.first = path;
locator.second = "internalsrc:";
} else {
// ...for file resources check if it's on disk
locator = texture_manager::find_on_disk(path);
if (true == locator.first.empty()) {
// there's nothing matching in the databank nor on the disk, report
// failure
ErrorLog("Bad file: failed to locate texture file \"" + path + "\"",
logtype::file);
return 0;
}
}
auto &new_texture = m_texture_cache.emplace_back();
auto index = m_texture_cache.size();
m_texture_map.emplace(locator.first, index);
new_texture = std::make_shared<NvTexture>();
auto sz_texture = std::make_shared<opengl_texture>();
new_texture->m_sz_texture = sz_texture;
sz_texture->is_texstub = isgenerated;
sz_texture->name = locator.first;
sz_texture->type = locator.second;
sz_texture->traits = traits;
sz_texture->components_hint = format_hint;
new_texture->set_components_hint(format_hint);
new_texture->Load(size_bias);
WriteLog("Created texture object for \"" + locator.first + "\"",
logtype::texture);
if (unload_on_location) {
new_texture->m_gc_slot = m_unloadable_textures.size();
auto &gc_slot = m_unloadable_textures.emplace_back();
gc_slot.m_index = index - 1;
} else {
new_texture->m_gc_slot = static_cast<size_t>(-1);
}
return index;
}
bool NvTexture::CreateRhiTexture() {
if (m_rhi_texture) return true;
m_last_change = ++s_change_counter;
auto renderer = dynamic_cast<NvRenderer *>(GfxRenderer.get());
if (!renderer) return false;
auto backend = renderer->GetBackend();
m_rhi_texture = renderer->GetBackend()->GetDevice()->createTexture(
nvrhi::TextureDesc()
.setDebugName(std::string(m_sz_texture->get_name()))
.setWidth(m_width)
.setHeight(m_height)
.setMipLevels(m_data.size())
.setFormat(m_format)
.setInitialState(nvrhi::ResourceStates::CopyDest)
.setKeepInitialState(true));
nvrhi::CommandListHandle command_list =
backend->GetDevice()->createCommandList(
nvrhi::CommandListParameters()
.setQueueType(nvrhi::CommandQueue::Copy)
.setEnableImmediateExecution(false));
command_list->open();
for (int mip = 0; mip < m_data.size(); ++mip) {
const auto &[pitch, data] = m_data[mip];
command_list->writeTexture(m_rhi_texture, 0, mip, data.data(), pitch);
}
command_list->close();
backend->GetDevice()->executeCommandList(command_list,
nvrhi::CommandQueue::Copy);
if (m_sz_texture->get_type() == "make:") {
auto const components{Split(std::string(m_sz_texture->get_name()), '?')};
auto dictionary = std::make_shared<dictionary_source>(components.back());
auto rt = std::make_shared<python_rt>();
rt->shared_tex = this;
if (!Application.request(
{ToLower(components.front()), dictionary, rt})) /*__debugbreak()*/
;
}
return true;
}
bool NvTextureManager::IsValidHandle(size_t handle) {
return handle > 0 && handle <= m_texture_cache.size();
}
NvTexture *NvTextureManager::GetTexture(size_t handle) {
if (!handle || handle > m_texture_cache.size()) {
return nullptr;
}
return m_texture_cache[handle - 1].get();
}
nvrhi::ITexture *NvTextureManager::GetRhiTexture(
size_t handle, nvrhi::ICommandList *command_list) {
if (!IsValidHandle(handle)) {
return nullptr;
}
const auto &texture = m_texture_cache[handle - 1];
// if (!texture.m_rhi_texture) {
// texture.m_rhi_texture = m_backend->GetDevice()->createTexture(
// nvrhi::TextureDesc()
// .setDebugName(texture.m_sz_texture.name)
// .setWidth(texture.m_width)
// .setHeight(texture.m_height)
// .setMipLevels(texture.m_mipcount)
// .setFormat(texture.m_format)
// .setInitialState(nvrhi::ResourceStates::ShaderResource)
// .setKeepInitialState(true));
//
// for (int mip = 0; mip < texture.m_mipcount; ++mip) {
// const auto &[pitch, data] = texture.m_data[mip];
// command_list->writeTexture(texture.m_rhi_texture, 0, mip, data.data(),
// pitch);
// }
// }
texture->CreateRhiTexture();
return texture->m_rhi_texture;
}
TextureTraitFlags NvTextureManager::GetTraits(size_t handle) {
if (!IsValidHandle(handle)) {
return 0;
}
return m_texture_cache[handle - 1]->GetTraits();
}
void NvTextureManager::UpdateLastUse(size_t handle,
const glm::dvec3 &location) {
if (!IsValidHandle(handle)) {
return;
}
const auto &texture = m_texture_cache[handle - 1];
if (texture->m_gc_slot < m_unloadable_textures.size()) {
m_unloadable_textures[texture->m_gc_slot].m_last_position_requested =
location;
}
}
nvrhi::SamplerHandle NvTextureManager::GetSamplerForTraits(
TextureTraitFlags traits, NvRenderer::RenderPassType pass) {
switch (pass) {
case NvRenderer::RenderPassType::CubeMap:
case NvRenderer::RenderPassType::ShadowMap:
traits[MaTextureTraits_Sharpen] = false;
traits[MaTextureTraits_NoAnisotropy] = true;
traits[MaTextureTraits_NoMipBias] = true;
break;
}
auto &sampler = m_samplers[traits];
if (!sampler) {
nvrhi::SamplerDesc desc =
nvrhi::SamplerDesc()
.setAddressU(traits[MaTextureTraits_ClampS]
? nvrhi::SamplerAddressMode::Clamp
: nvrhi::SamplerAddressMode::Wrap)
.setAddressV(traits[MaTextureTraits_ClampT]
? nvrhi::SamplerAddressMode::Clamp
: nvrhi::SamplerAddressMode::Wrap)
.setAllFilters(true)
.setMipFilter(false)
.setMaxAnisotropy(traits[MaTextureTraits_NoAnisotropy] ? 0.f : 16.f)
.setMipBias(traits[MaTextureTraits_NoMipBias] ? 0.f : -1.76f);
sampler = m_backend->GetDevice()->createSampler(desc);
}
return sampler;
}
void NvTextureManager::Cleanup(const glm::dvec3 &location) {
for (const auto &slot : m_unloadable_textures) {
if (glm::distance2(slot.m_last_position_requested, location) >
Global.BaseDrawRange * Global.BaseDrawRange) {
m_texture_cache[slot.m_index]->m_rhi_texture = nullptr;
}
}
}

View File

@@ -0,0 +1,102 @@
#pragma once
#include <bitset>
#include <memory>
#include <unordered_map>
#include <nvrhi/nvrhi.h>
#include <glm/glm.hpp>
#include "interfaces/ITexture.h"
#include "nvrenderer/nvrenderer_enums.h"
enum MaTextureTraits {
MaTextureTraits_Sharpen,
MaTextureTraits_ClampS,
MaTextureTraits_ClampT,
MaTextureTraits_NoMipBias,
MaTextureTraits_NoAnisotropy,
MaTextureTraits_Num
};
using TextureTraitFlags = std::bitset<MaTextureTraits_Num>;
struct opengl_texture;
struct NvTexture : public ITexture {
virtual bool create(bool Static = false) override {
return CreateRhiTexture();
}
virtual int get_width() const override { return m_width; }
virtual int get_height() const override { return m_height; }
virtual size_t get_id() const override {
return reinterpret_cast<size_t>(m_rhi_texture.Get());
}
virtual void release() override {}
virtual void make_stub() override {}
virtual std::string_view get_traits() const override { return ""; }
virtual std::string_view get_name() const override {
return m_sz_texture->get_name();
}
virtual std::string_view get_type() const override {
return m_sz_texture->get_type();
}
virtual bool is_stub() const override { return false; }
virtual bool get_has_alpha() const override { return m_has_alpha; }
virtual bool get_is_ready() const override { return m_rhi_texture; }
virtual void set_components_hint(int format_hint) override;
virtual void make_from_memory(size_t width, size_t height,
const uint8_t* data) override;
virtual void update_from_memory(size_t width, size_t height,
const uint8_t* data) override;
void Load(int size_bias);
bool LoadDDS(std::string& data, int size_bias);
bool IsLoaded() const;
std::shared_ptr<ITexture> m_sz_texture;
nvrhi::TextureHandle m_rhi_texture;
std::vector<std::pair<int, std::string>> m_data;
TextureTraitFlags GetTraits() const { return 0; }
int m_width;
int m_height;
int m_mipcount;
int m_slicecount;
bool m_srgb;
bool m_bc;
bool m_has_alpha;
nvrhi::Format m_format;
size_t m_gc_slot;
bool CreateRhiTexture();
uint64_t m_last_change;
static uint64_t s_change_counter;
uint64_t m_own_handle;
};
class NvTextureManager {
public:
struct TextureGCSlot {
size_t m_index;
glm::dvec3 m_last_position_requested;
};
NvTextureManager(class NvRenderer* renderer);
size_t FetchTexture(std::string path, int format_hint, int size_bias,
bool unload_on_location);
void UpdateLastUse(size_t handle, const glm::dvec3& location);
bool IsValidHandle(size_t handle);
NvTexture* GetTexture(size_t handle);
nvrhi::ITexture* GetRhiTexture(size_t handle,
nvrhi::ICommandList* command_list = nullptr);
TextureTraitFlags GetTraits(size_t handle);
nvrhi::SamplerHandle GetSamplerForTraits(TextureTraitFlags traits,
RendererEnums::RenderPassType pass);
std::vector<TextureGCSlot> m_unloadable_textures;
void Cleanup(const glm::dvec3& location);
private:
std::unordered_map<TextureTraitFlags, nvrhi::SamplerHandle> m_samplers;
class NvRendererBackend* m_backend;
std::unordered_map<std::string, size_t> m_texture_map;
std::vector<std::shared_ptr<NvTexture>> m_texture_cache;
};

View File

@@ -0,0 +1,94 @@
#include "nvrenderer/quadtree.h"
void QuadTreeBuilder::Node::Insert(uint32_t index, glm::dvec3 min_,
glm::dvec3 max_, int level) {
if (level > 0) {
int child = GetChildIndex(.5 * (min_ + max_));
if (!m_children[child]) {
m_children[child] = std::make_shared<Node>(GetChildBounds(child));
}
m_children[child]->Insert(index, min_, max_, level - 1);
} else {
m_leaves.emplace_back(.5 * (max_ + min_), .5 * (max_ - min_), index);
}
#ifndef NDEBUG
if (compMin(m_actual_min) < -1.e10) {
__debugbreak();
}
if (compMax(m_actual_max) > 1.e10) {
__debugbreak();
}
#endif
m_actual_min = min(min_, m_actual_min);
m_actual_max = max(max_, m_actual_max);
#ifndef NDEBUG
if (compMax(glm::abs(m_actual_min)) > 1.e10) {
__debugbreak();
}
if (compMax(glm::abs(m_actual_max)) > 1.e10) {
__debugbreak();
}
#endif
}
QuadTreeBuilder::QuadTreeBuilder(double min_x, double min_z, double max_x,
double max_z)
: m_root(std::make_shared<Node>(min_x, min_z, max_x, max_z)),
m_min(min_x, -std::numeric_limits<double>::max(), min_z),
m_max(max_x, std::numeric_limits<double>::max(), max_z),
m_max_levels(32) {
// Ensure outer bounds are square
glm::dvec2 origin = .5 * (m_max + m_min).xz;
glm::dvec2 extent = .5 * (m_max - m_min).xz;
extent = glm::dvec2(glm::compMax(extent));
m_min.xz = origin - extent;
m_max.xz = origin + extent;
// glm::dvec3 scale_v = (m_max - m_min) / 50.;
// double scale = glm::min(scale_v.z, scale_v.x);
// m_max_levels = glm::max(1, (int)glm::ceil(glm::log2(scale)));
}
void QuadTreeBuilder::Insert(uint32_t index, glm::dvec3 min,
glm::dvec3 max) const {
glm::dvec3 scale_v = (m_max - m_min) / (max - min);
double scale = glm::min(scale_v.z, scale_v.x);
int level =
glm::clamp((int)glm::floor(glm::log2(scale)), 0, m_max_levels - 1);
m_root->Insert(index, min, max, level);
}
void QuadTree::Build(const QuadTreeBuilder& builder) {
Add(m_nodes.emplace_back(), *builder.m_root);
}
void QuadTree::Add(Node& node, const QuadTreeBuilder::Node& item) {
if (item.m_leaves.empty() && item.NumChildren() == 1) {
return Add(node, *item.FirstChild());
}
node.m_origin = .5 * (item.m_actual_max + item.m_actual_min);
node.m_extent = .5 * (item.m_actual_max - item.m_actual_min);
node.m_first_leave = m_leaves.size();
node.m_leave_count = item.m_leaves.size();
m_leaves.insert(m_leaves.end(), item.m_leaves.begin(), item.m_leaves.end());
uint32_t first_child = m_nodes.size();
uint32_t child_count = 0;
for (const auto& child : item.m_children) {
if (child) ++child_count;
}
node.m_first_child = first_child;
node.m_child_count = child_count;
// node reference invalidated from here
for (int i = 0; i < child_count; ++i) {
m_nodes.emplace_back();
}
{
int child_index = first_child;
for (const auto& child : item.m_children) {
if (!child) continue;
Add(m_nodes[child_index++], *child);
}
}
}

View File

@@ -0,0 +1,181 @@
#include "nvrenderer/resource_registry.h"
#include "Logs.h"
#include "fmt/compile.h"
#include "nvtexture.h"
#include "nvrenderer/nvrenderer.h"
MaResourceMappingSet& MaResourceMappingSet::Add(
const MaResourceMapping& mapping) {
m_mappings.push_back(mapping);
return *this;
}
void MaResourceMappingSet::ToBindingLayout(
nvrhi::BindingLayoutDesc& desc) const {
for (const auto& [slot, size, key, type] : m_mappings) {
nvrhi::BindingLayoutItem item{};
item.type = type;
item.slot = slot;
item.size = size;
desc.addItem(item);
}
}
bool MaResourceMappingSet::ToBindingSet(
nvrhi::BindingSetDesc& desc, const MaResourceRegistry* registry,
const nvrhi::BindingLayoutDesc* reference_layout) const {
bool is_valid = true;
for (const auto& [slot, size, key, type] : m_mappings) {
MaResourceView resource{};
switch (type) {
case nvrhi::ResourceType::PushConstants:
case nvrhi::ResourceType::None:
break;
default:
resource = registry->GetResource(key, type);
}
bool item_is_valid = resource.IsValid();
bool item_is_present = true;
nvrhi::BindingSetItem item{};
item.slot = slot;
item.type = type;
item.resourceHandle = resource.m_resource;
item.dimension = nvrhi::TextureDimension::Unknown;
item.format = nvrhi::Format::UNKNOWN;
switch (type) {
case nvrhi::ResourceType::PushConstants:
item.range.byteOffset = 0;
item.range.byteSize = size;
item_is_valid = true;
break;
case nvrhi::ResourceType::RawBuffer_SRV:
case nvrhi::ResourceType::RawBuffer_UAV:
case nvrhi::ResourceType::StructuredBuffer_SRV:
case nvrhi::ResourceType::StructuredBuffer_UAV:
case nvrhi::ResourceType::TypedBuffer_SRV:
case nvrhi::ResourceType::TypedBuffer_UAV:
case nvrhi::ResourceType::ConstantBuffer:
case nvrhi::ResourceType::VolatileConstantBuffer:
item.range = nvrhi::EntireBuffer;
break;
case nvrhi::ResourceType::Texture_SRV:
case nvrhi::ResourceType::Texture_UAV:
item.subresources = nvrhi::AllSubresources;
default:;
}
if (reference_layout) {
item_is_present = false;
for (const auto& binding : reference_layout->bindings) {
if (binding.type != item.type || binding.slot != item.slot) continue;
item_is_present = true;
break;
}
}
if (item_is_valid && item_is_present) desc.addItem(item);
is_valid &= item_is_valid;
}
return is_valid;
}
MaResourceView MaResourceRegistry::GetResource(
const entt::hashed_string& key,
const nvrhi::ResourceType requested_type) const {
auto registry = this;
while (registry) {
if (const auto resource = registry->GetResourceLocal(key, requested_type);
resource.IsValid())
return resource;
registry = registry->m_parent;
}
std::string what = fmt::format("Unable to locate resource {:s} [{:#x}]",
key.data(), key.value());
ErrorLog(fmt::format("Unable to locate resource {:s} [{:#x}]", key.data(),
key.value()));
throw std::runtime_error(what.c_str());
return {};
}
MaResourceRegistry::MaResourceRegistry(MaResourceRegistry* parent)
: m_parent(parent) {}
void MaResourceRegistry::InitResourceRegistry() {
if (m_parent) {
m_texture_manager =
std::shared_ptr<NvTextureManager>(m_parent->GetTextureManager());
} else {
m_texture_manager =
std::make_shared<NvTextureManager>(dynamic_cast<NvRenderer*>(this));
}
}
void MaResourceRegistry::RegisterResource(const bool global,
const entt::hashed_string& key,
nvrhi::IResource* resource,
const nvrhi::ResourceType type) {
auto registry = this;
while (global && registry->m_parent) registry = registry->m_parent;
registry->m_resource_views[key] = {resource, type};
}
void MaResourceRegistry::RegisterTexture(const entt::hashed_string& key,
const size_t handle) {
m_streaming_textures[key] = handle;
}
void MaResourceRegistry::RegisterResource(const bool global, const char* key,
nvrhi::IResource* resource,
const nvrhi::ResourceType type) {
RegisterResource(global, static_cast<entt::hashed_string>(key), resource,
type);
}
void MaResourceRegistry::RegisterTexture(const char* key, const size_t handle) {
RegisterTexture(static_cast<entt::hashed_string>(key), handle);
}
MaResourceView MaResourceRegistry::GetResourceLocal(
const entt::hashed_string& key,
const nvrhi::ResourceType requested_type) const {
// Attempt to fetch streaming texture
if (requested_type == nvrhi::ResourceType::Texture_SRV) {
if (const auto it = m_streaming_textures.find(key);
it != m_streaming_textures.end()) {
if (const auto texture_manager = GetTextureManager();
texture_manager && texture_manager->IsValidHandle(it->second))
return {texture_manager->GetRhiTexture(it->second),
nvrhi::ResourceType::Texture_SRV};
}
}
// Attempt to fetch static resource
if (const auto it = m_resource_views.find(key);
it != m_resource_views.end() && requested_type == it->second.m_type) {
return it->second;
}
return {};
}
NvTextureManager* MaResourceRegistry::GetTextureManager() const {
auto registry = this;
while (!registry->m_texture_manager && registry->m_parent)
registry = registry->m_parent;
return registry->m_texture_manager.get();
}
uint64_t MaResourceRegistry::GetLastStreamingTextureUpdate() const {
uint64_t last_update = 1;
const auto texture_manager = GetTextureManager();
for (const auto& [key, texture] : m_streaming_textures) {
if (!texture_manager->IsValidHandle(texture)) continue;
last_update = std::max(last_update,
texture_manager->GetTexture(texture)->m_last_change);
}
return last_update;
}
void MaResourceRegistry::UpdateLastStreamingTextureUse(
const glm::dvec3& location) const {
const auto texture_manager = GetTextureManager();
for (const auto& [key, texture] : m_streaming_textures) {
texture_manager->UpdateLastUse(texture, location);
}
}

View File

@@ -0,0 +1,31 @@
#include <yaml-cpp/yaml.h>
#include "nvrenderer_d3d12.h"
#if LIBMANUL_WITH_D3D12
void NvRendererBackend_D3D12::LoadShaderBlobs() {
auto root = YAML::LoadFile("shaders/shaders_dxil.manul");
for (const auto entry : root["materials"]) {
std::string_view material = entry.first.as<std::string_view>();
for (const auto shader : entry.second) {
auto binary = shader.second["binary"].as<YAML::Binary>();
auto entrypoint = shader.second["entrypoint"].as<std::string>();
AddMaterialShaderBlob(
material, shader.first.as<std::string_view>(),
std::string(binary.data(), binary.data() + binary.size()),
entrypoint);
}
}
for (const auto shader : root["utility"]) {
auto binary = shader.second["binary"].as<YAML::Binary>();
auto entrypoint = shader.second["entrypoint"].as<std::string>();
AddShaderBlob(shader.first.as<std::string_view>(),
std::string(binary.data(), binary.data() + binary.size()),
entrypoint);
}
}
#endif

View File

@@ -0,0 +1,31 @@
#include <yaml-cpp/yaml.h>
#include "nvrenderer_vulkan.h"
#if LIBMANUL_WITH_VULKAN
void NvRendererBackend_Vulkan::LoadShaderBlobs() {
auto root = YAML::LoadFile("shaders/shaders_spirv.manul");
for (const auto entry : root["materials"]) {
std::string_view material = entry.first.as<std::string_view>();
for (const auto shader : entry.second) {
auto binary = shader.second["binary"].as<YAML::Binary>();
auto entrypoint = shader.second["entrypoint"].as<std::string>();
AddMaterialShaderBlob(
material, shader.first.as<std::string_view>(),
std::string(binary.data(), binary.data() + binary.size()),
entrypoint);
}
}
for (const auto shader : root["utility"]) {
auto binary = shader.second["binary"].as<YAML::Binary>();
auto entrypoint = shader.second["entrypoint"].as<std::string>();
AddShaderBlob(shader.first.as<std::string_view>(),
std::string(binary.data(), binary.data() + binary.size()),
entrypoint);
}
}
#endif

View File

@@ -0,0 +1,466 @@
#include "sky.h"
#include <nvrhi/utils.h>
#include "nvrendererbackend.h"
#include "simulationenvironment.h"
#include "simulationtime.h"
namespace {
static const float PI = 3.14159265358979323846f;
static const float INV_PI = 0.31830988618379067154f;
static const float INV_4PI = 0.25f * INV_PI;
static const float PHASE_ISOTROPIC = INV_4PI;
static const float RAYLEIGH_PHASE_SCALE = (3.f / 16.f) * INV_PI;
static const float g = 0.8f;
static const float gg = g * g;
static const float EARTH_RADIUS = 6371.0e3f; // km
static const float ATMOSPHERE_THICKNESS = 100.0e3f; // km
static const float ATMOSPHERE_RADIUS = EARTH_RADIUS + ATMOSPHERE_THICKNESS;
static const float EXPOSURE = -4.0;
// Extraterrestial Solar Irradiance Spectra, units W * m^-2 * nm^-1
// https://www.nrel.gov/grid/solar-resource/spectra.html
static const glm::vec4 sun_spectral_irradiance =
glm::vec4(1.679, 1.828, 1.986, 1.307);
// Rayleigh scattering coefficient at sea level, units km^-1
// "Rayleigh-scattering calculations for the terrestrial atmosphere"
// by Anthony Bucholtz (1995).
static const glm::vec4 molecular_scattering_coefficient_base =
glm::vec4(6.605e-6, 1.067e-5, 1.842e-5, 3.156e-5);
// Fog scattering/extinction cross section, units m^2 / molecules
// Mie theory results for IOR of 1.333. Particle size is a log normal
// distribution of mean diameter=15 and std deviation=0.4
static const glm::vec4 fog_scattering_cross_section =
glm::vec4(5.015e-10, 4.987e-10, 4.966e-10, 4.949e-10);
// Ozone absorption cross section, units m^2 / molecules
// "High spectral resolution ozone absorption cross-sections"
// by V. Gorshelev et al. (2014).
static const glm::vec4 ozone_absorption_cross_section =
glm::vec4(3.472e-25, 3.914e-25, 1.349e-25, 11.03e-27);
static const glm::mat4x3 M = {
137.672389239975, -8.632904716299537, -1.7181567391931372,
32.549094028629234, 91.29801417199785, -12.005406444382531,
-38.91428392614275, 34.31665471469816, 29.89044807197628,
8.572844237945445, -11.103384660054624, 117.47585277566478};
} // namespace
Sky::Sky(NvRenderer* renderer)
: m_backend(renderer->GetBackend()), MaResourceRegistry(renderer) {
m_transmittance_pass = std::make_shared<SkyTransmittancePass>(this);
m_aerial_lut = std::make_shared<SkyAerialLut>(this);
}
void Sky::Init() {
InitResourceRegistry();
m_sky_constants = m_backend->GetDevice()->createBuffer(
nvrhi::utils::CreateVolatileConstantBufferDesc(sizeof(SkyConstants),
"Sky constants", 16)
.setInitialState(nvrhi::ResourceStates::ConstantBuffer)
.setKeepInitialState(true));
m_transmittance_pass->Init();
m_aerial_lut->Init();
RegisterResource(true, "sky_aerial_lut", m_aerial_lut->m_lut,
nvrhi::ResourceType::Texture_SRV);
}
void Sky::Render(nvrhi::ICommandList* command_list,
const glm::dmat4& projection, const glm::dmat4& view) {
UpdateConstants(command_list);
m_transmittance_pass->Render(command_list);
m_aerial_lut->Render(command_list, projection, view);
}
float Sky::GetOzoneMean() const {
float month = glm::saturate(simulation::Time.year_day() / 366.f) * 12.f + .5f;
float month_fract = glm::fract(month);
int month_l = glm::floor(month);
static std::array<float, 12> ozone_means{347.f, 370.f, 381.f, 384.f,
372.f, 352.f, 333.f, 317.f,
298.f, 285.f, 290.f, 315.f};
return glm::lerp(ozone_means[month_l], ozone_means[(month_l + 1) % 12],
month_fract);
}
glm::vec4 Sky::GetAerosolAbsorptionCrossSection() const {
switch (m_aerosol_preset) {
case AerosolPreset::Rural:
return {5.0393e-23f, 8.0765e-23f, 1.3823e-22f, 2.3383e-22f};
case AerosolPreset::Urban:
return {2.8722e-24f, 4.6168e-24f, 7.9706e-24f, 1.3578e-23f};
default:
return glm::vec4(0.f);
}
}
glm::vec4 Sky::GetAerosolScatteringCrossSection() const {
switch (m_aerosol_preset) {
case AerosolPreset::Rural:
return {2.6004e-22f, 2.4844e-22f, 2.8362e-22f, 2.7494e-22f};
case AerosolPreset::Urban:
return {1.5908e-22f, 1.7711e-22f, 2.0942e-22f, 2.4033e-22f};
default:
return glm::vec4(0.f);
}
}
float Sky::GetAerosolBaseDensity() const {
switch (m_aerosol_preset) {
case AerosolPreset::Rural:
return 8.544e15f;
case AerosolPreset::Urban:
return 1.3681e17f;
default:
return 0.f;
}
}
float Sky::GetAerosolBackgroundDividedByBaseDensity() const {
switch (m_aerosol_preset) {
case AerosolPreset::Rural:
return 2e3f / GetAerosolBaseDensity();
case AerosolPreset::Urban:
return 2e3f / GetAerosolBaseDensity();
default:
return 0.f;
}
}
float Sky::GetAerosolHeightScale() const {
switch (m_aerosol_preset) {
case AerosolPreset::Rural:
return .73f;
case AerosolPreset::Urban:
return .73f;
default:
return 0.f;
}
}
float Sky::GetFogDensity() const {
return 3e7f * glm::pow(0.99937f, glm::min(3e7f, m_visibility));
}
void Sky::UpdateConstants(nvrhi::ICommandList* command_list) {
SkyConstants constants{};
constants.g_AerosolAbsorptionCrossSection =
GetAerosolAbsorptionCrossSection();
constants.g_AerosolScatteringCrossSection =
GetAerosolScatteringCrossSection();
constants.g_AerosolHeightScale = GetAerosolHeightScale();
constants.g_AerosolBaseDensity = GetAerosolBaseDensity();
constants.g_AerosolBackgroundDividedByBaseDensity =
GetAerosolBackgroundDividedByBaseDensity();
constants.g_GroundAlbedo = glm::vec4{m_ground_albedo};
constants.g_AerosolTurbidity = m_aerosol_turbidity;
constants.g_OzoneMean = GetOzoneMean();
constants.g_FogDensity = GetFogDensity();
constants.g_FogHeightOffset = m_fog_height_offset;
constants.g_FogHeightScale = m_fog_height_scale;
command_list->writeBuffer(m_sky_constants, &constants, sizeof(constants));
}
void Sky::OnGui() {
if (!m_gui_active) return;
if (ImGui::Begin("Sky settings", &m_gui_active,
ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::SliderFloat("Visibility", &m_visibility, 10.f, 3e4f);
ImGui::SliderFloat("Fog Offset", &m_fog_height_offset, 0.f, 10.f);
ImGui::SliderFloat("Fog Height", &m_fog_height_scale, .01f, 10.f);
if (ImGui::BeginCombo("Aerosol Type",
GetAerosolTypeDesc(m_aerosol_preset))) {
for (int i = 0; i < static_cast<int>(AerosolPreset::Num); ++i) {
AerosolPreset preset = static_cast<AerosolPreset>(i);
if (ImGui::Selectable(GetAerosolTypeDesc(preset),
preset == m_aerosol_preset)) {
m_aerosol_preset = preset;
}
}
ImGui::EndCombo();
}
ImGui::SliderFloat("Aerosol Turbidity", &m_aerosol_turbidity, 0., 10.);
ImGui::SliderFloat("Ground Albedo", &m_ground_albedo, 0., 10.);
ImGui::End();
}
}
void Sky::ShowGui() { m_gui_active = true; }
const char* Sky::GetAerosolTypeDesc(AerosolPreset preset) {
switch (preset) {
case AerosolPreset::Rural:
return "Rural";
case AerosolPreset::Urban:
return "Urban";
default:
return "INVALID";
}
}
glm::vec4 Sky::GetMolecularScatteringCoefficient(float h) const {
return molecular_scattering_coefficient_base *
glm::exp(-0.07771971f * glm::pow(h, 1.16364243f));
}
glm::vec4 Sky::GetMolecularAbsorptionCoefficient(float h) const {
h += 1e-4f; // Avoid division by 0
float t = glm::log(h) - 3.22261f;
float density = 3.78547397e17f * (1.f / h) * glm::exp(-t * t * 5.55555555f);
return ozone_absorption_cross_section * GetOzoneMean() * density;
}
glm::vec4 Sky::GetFogScatteringCoefficient(float h) const {
if (GetFogDensity() > 0.f) {
return fog_scattering_cross_section * GetFogDensity() *
glm::min(1.f,
glm::exp((-h + m_fog_height_offset) / m_fog_height_scale));
} else {
return glm::vec4{0.f};
}
}
glm::vec3 Sky::CalcSunColor() const {
glm::vec4 transmittance =
ComputeTransmittance(simulation::Environment.sun().getDirection(), 32);
return LinearSrgbFromSpectralSamples(sun_spectral_irradiance *
transmittance) *
glm::exp(EXPOSURE);
}
glm::vec3 Sky::CalcMoonColor() const {
float phase = glm::acos(dot(simulation::Environment.sun().getDirection(),
simulation::Environment.moon().getDirection())) *
glm::one_over_pi<float>();
glm::vec4 transmittance =
ComputeTransmittance(simulation::Environment.moon().getDirection(), 32);
return .07f * phase *
LinearSrgbFromSpectralSamples(sun_spectral_irradiance *
transmittance) *
glm::exp(EXPOSURE);
}
void Sky::CalcLighting(glm::vec3& direction, glm::vec3& color) const {
const glm::vec3 moon = CalcMoonColor();
const glm::vec3 sun = CalcSunColor();
if (dot(moon, moon) > dot(sun, sun)) {
direction = simulation::Environment.moon().getDirection();
color = moon;
} else {
direction = simulation::Environment.sun().getDirection();
color = sun;
}
}
glm::vec3 Sky::LinearSrgbFromSpectralSamples(glm::vec4 L) {
glm::vec3 c = M * L;
return c.r * glm::vec3(0.4123908f, 0.21263901f, 0.01933082f) +
c.g * glm::vec3(0.35758434f, 0.71516868f, 0.11919478f) +
c.b * glm::vec3(0.18048079f, 0.07219232f, 0.95053215f);
}
float Sky::RaySphereIntersection(glm::vec3 ro, glm::vec3 rd, float radius) {
float b = glm::dot(ro, rd);
float c = glm::dot(ro, ro) - radius * radius;
if (c > 0.f && b > 0.f) return -1.f;
float d = b * b - c;
if (d < 0.f) return -1.f;
if (d > b * b) return (-b + glm::sqrt(d));
return (-b - glm::sqrt(d));
}
float Sky::GetAerosolDensity(float h) const {
return GetAerosolBaseDensity() * (exp(-h / GetAerosolHeightScale()) +
GetAerosolBackgroundDividedByBaseDensity());
}
void Sky::GetAtmosphereCollisionCoefficients(
float h, glm::vec4& aerosol_absorption, glm::vec4& aerosol_scattering,
glm::vec4& molecular_absorption, glm::vec4& molecular_scattering,
glm::vec4& fog_scattering, glm::vec4& extinction) const {
h = glm::max(h, 1.e-3f); // In case height is negative
h *= 1.e-3f;
float aerosol_density = GetAerosolDensity(h);
aerosol_absorption = GetAerosolAbsorptionCrossSection() * aerosol_density *
m_aerosol_turbidity;
aerosol_scattering = GetAerosolScatteringCrossSection() * aerosol_density *
m_aerosol_turbidity;
molecular_absorption = GetMolecularAbsorptionCoefficient(h);
molecular_scattering = GetMolecularScatteringCoefficient(h);
fog_scattering = GetFogScatteringCoefficient(h);
extinction = aerosol_absorption + aerosol_scattering + molecular_absorption +
molecular_scattering + fog_scattering;
}
glm::vec4 Sky::ComputeTransmittance(const glm::vec3& ray_dir, int steps) const {
glm::vec3 ray_origin{0.f, EARTH_RADIUS + Global.pCamera.Pos.y, 0.f};
glm::vec4 transmittance{1.f};
float t_max = std::numeric_limits<float>::max();
float atmos_dist =
RaySphereIntersection(ray_origin, ray_dir, ATMOSPHERE_RADIUS);
if (RaySphereIntersection(ray_origin, ray_dir, EARTH_RADIUS) >= 0.0) {
return glm::vec4(0.f, 0.f, 0.f, 0.f);
}
t_max = glm::min(t_max, atmos_dist);
float dt = t_max / float(steps);
for (int i = 0; i < steps; ++i) {
float t = (float(i) + 0.5) * dt;
glm::vec3 x_t = ray_origin + ray_dir * t;
float distance_to_earth_center = glm::length(x_t);
float altitude = distance_to_earth_center - EARTH_RADIUS;
glm::vec4 aerosol_absorption, aerosol_scattering;
glm::vec4 molecular_absorption, molecular_scattering;
glm::vec4 fog_scattering;
glm::vec4 extinction;
GetAtmosphereCollisionCoefficients(
altitude, aerosol_absorption, aerosol_scattering, molecular_absorption,
molecular_scattering, fog_scattering, extinction);
glm::vec4 step_transmittance = glm::exp(-dt * extinction);
transmittance *= step_transmittance;
}
return transmittance;
}
void SkyTransmittancePass::Init() {
m_pixel_shader =
m_backend->CreateShader("sky_transmittance", nvrhi::ShaderType::Pixel);
m_output = m_backend->GetDevice()->createTexture(
nvrhi::TextureDesc()
.setFormat(nvrhi::Format::RGBA8_UNORM)
.setWidth(128)
.setHeight(128)
.setIsRenderTarget(true)
.setInitialState(nvrhi::ResourceStates::ShaderResource)
.setKeepInitialState(true));
nvrhi::utils::CreateBindingSetAndLayout(
m_backend->GetDevice(), nvrhi::ShaderType::Pixel, 0,
nvrhi::BindingSetDesc().addItem(
nvrhi::BindingSetItem::ConstantBuffer(13, m_sky->m_sky_constants)),
m_binding_layout, m_binding_set);
m_framebuffer = m_backend->GetDevice()->createFramebuffer(
nvrhi::FramebufferDesc().addColorAttachment(m_output));
FullScreenPass::Init();
}
void SkyTransmittancePass::CreatePipelineDesc(
nvrhi::GraphicsPipelineDesc& pipeline_desc) {
FullScreenPass::CreatePipelineDesc(pipeline_desc);
pipeline_desc.setPixelShader(m_pixel_shader);
pipeline_desc.addBindingLayout(m_binding_layout);
}
void SkyTransmittancePass::Render(nvrhi::ICommandList* command_list) {
command_list->beginMarker("Sky transmittance");
nvrhi::GraphicsState graphics_state;
InitState(graphics_state);
graphics_state.addBindingSet(m_binding_set);
command_list->setGraphicsState(graphics_state);
Draw(command_list);
command_list->endMarker();
}
nvrhi::IFramebuffer* SkyTransmittancePass::GetFramebuffer() {
return m_framebuffer;
}
void SkyAerialLut::Init() {
m_sky_width = 256;
m_sky_height = 512;
m_lut_width = 128;
m_lut_height = 256;
m_lut_slices = 16;
m_constant_buffer = m_sky->m_backend->GetDevice()->createBuffer(
nvrhi::utils::CreateVolatileConstantBufferDesc(
sizeof(DispatchConstants), "Sky Aerial LUT Dispatch Constants", 16));
m_lut = m_sky->m_backend->GetDevice()->createTexture(
nvrhi::TextureDesc()
.setDimension(nvrhi::TextureDimension::Texture2DArray)
.setWidth(m_lut_width)
.setHeight(m_lut_height)
.setArraySize(m_lut_slices)
.setFormat(nvrhi::Format::RGBA16_FLOAT)
.setIsUAV(true)
.setInitialState(nvrhi::ResourceStates::Common)
.setKeepInitialState(true)
.setDebugName("Sky Aerial LUT"));
m_sky_texture = m_sky->m_backend->GetDevice()->createTexture(
nvrhi::TextureDesc()
.setWidth(m_sky_width)
.setHeight(m_sky_height)
.setFormat(nvrhi::Format::RGBA16_FLOAT)
.setIsUAV(true)
.setInitialState(nvrhi::ResourceStates::Common)
.setKeepInitialState(true)
.setDebugName("Sky Texture"));
auto shader_lut = m_sky->m_backend->CreateShader("sky_aerial_lut",
nvrhi::ShaderType::Compute);
auto shader_sky =
m_sky->m_backend->CreateShader("sky", nvrhi::ShaderType::Compute);
auto sampler = m_sky->m_backend->GetDevice()->createSampler(
nvrhi::SamplerDesc()
.setAllAddressModes(nvrhi::SamplerAddressMode::ClampToEdge)
.setAllFilters(true));
nvrhi::BindingLayoutHandle binding_layout_lut;
nvrhi::BindingLayoutHandle binding_layout_sky;
nvrhi::utils::CreateBindingSetAndLayout(
m_sky->m_backend->GetDevice(), nvrhi::ShaderType::Compute, 0,
nvrhi::BindingSetDesc()
.addItem(nvrhi::BindingSetItem::ConstantBuffer(0, m_constant_buffer))
.addItem(
nvrhi::BindingSetItem::ConstantBuffer(13, m_sky->m_sky_constants))
.addItem(nvrhi::BindingSetItem::Texture_UAV(0, m_lut))
.addItem(nvrhi::BindingSetItem::Texture_SRV(
13, m_sky->m_transmittance_pass->m_output))
.addItem(nvrhi::BindingSetItem::Sampler(13, sampler)),
binding_layout_lut, m_bindings_lut);
nvrhi::utils::CreateBindingSetAndLayout(
m_sky->m_backend->GetDevice(), nvrhi::ShaderType::Compute, 0,
nvrhi::BindingSetDesc()
.addItem(nvrhi::BindingSetItem::ConstantBuffer(0, m_constant_buffer))
.addItem(
nvrhi::BindingSetItem::ConstantBuffer(13, m_sky->m_sky_constants))
.addItem(nvrhi::BindingSetItem::Texture_UAV(1, m_sky_texture))
.addItem(nvrhi::BindingSetItem::Texture_SRV(
13, m_sky->m_transmittance_pass->m_output))
.addItem(nvrhi::BindingSetItem::Sampler(13, sampler)),
binding_layout_sky, m_bindings_sky);
m_pso_lut = m_sky->m_backend->GetDevice()->createComputePipeline(
nvrhi::ComputePipelineDesc()
.addBindingLayout(binding_layout_lut)
.setComputeShader(shader_lut));
m_pso_sky = m_sky->m_backend->GetDevice()->createComputePipeline(
nvrhi::ComputePipelineDesc()
.addBindingLayout(binding_layout_sky)
.setComputeShader(shader_sky));
}
void SkyAerialLut::Render(nvrhi::ICommandList* command_list,
const glm::dmat4& projection,
const glm::dmat4& view) {
{
DispatchConstants constants{};
constants.g_InverseView = static_cast<glm::mat3>(glm::inverse(view));
constants.g_InverseProjection = glm::inverse(projection);
constants.g_SunDir = simulation::Environment.sun().getDirection();
constants.g_MoonDir = simulation::Environment.moon().getDirection();
constants.g_Altitude = Global.pCamera.Pos.y;
constants.g_MaxDepth = Global.BaseDrawRange * Global.fDistanceFactor;
command_list->writeBuffer(m_constant_buffer, &constants, sizeof(constants));
}
command_list->setComputeState(
nvrhi::ComputeState().setPipeline(m_pso_lut).addBindingSet(
m_bindings_lut));
command_list->dispatch((m_lut_width + 7) / 8, (m_lut_height + 7) / 8, 1);
command_list->setComputeState(
nvrhi::ComputeState().setPipeline(m_pso_sky).addBindingSet(
m_bindings_sky));
command_list->dispatch((m_sky_width + 7) / 8, (m_sky_height + 7) / 8, 1);
}

View File

@@ -0,0 +1,118 @@
#pragma once
#include "fullscreenpass.h"
#include "nvrenderer/resource_registry.h"
struct Sky : public MaResourceRegistry {
Sky(class NvRenderer* renderer);
void Init();
void Render(nvrhi::ICommandList* command_list, const glm::dmat4& projection,
const glm::dmat4& view);
struct SkyConstants {
glm::vec4 g_AerosolAbsorptionCrossSection;
glm::vec4 g_AerosolScatteringCrossSection;
glm::vec4 g_GroundAlbedo;
float g_AerosolHeightScale;
float g_AerosolTurbidity;
float g_AerosolBaseDensity;
float g_AerosolBackgroundDividedByBaseDensity;
float g_OzoneMean;
float g_FogDensity;
float g_FogHeightOffset;
float g_FogHeightScale;
};
nvrhi::BufferHandle m_sky_constants;
enum class AerosolPreset {
Rural,
Urban,
Num
} m_aerosol_preset = AerosolPreset::Rural;
float m_visibility = 1e4f;
float m_fog_height_offset = 0.f;
float m_fog_height_scale = .1f;
float m_aerosol_turbidity = 1.f;
float m_ground_albedo = .3f;
bool m_gui_active = false;
void UpdateConstants(nvrhi::ICommandList* command_list);
void OnGui();
void ShowGui();
static const char* GetAerosolTypeDesc(AerosolPreset preset);
class NvRendererBackend* m_backend;
std::shared_ptr<struct SkyTransmittancePass> m_transmittance_pass;
std::shared_ptr<struct SkyAerialLut> m_aerial_lut;
float GetOzoneMean() const;
glm::vec4 GetAerosolAbsorptionCrossSection() const;
glm::vec4 GetAerosolScatteringCrossSection() const;
float GetAerosolBaseDensity() const;
float GetAerosolBackgroundDividedByBaseDensity() const;
float GetAerosolHeightScale() const;
float GetFogDensity() const;
glm::vec4 GetMolecularScatteringCoefficient(float h) const;
glm::vec4 GetMolecularAbsorptionCoefficient(float h) const;
glm::vec4 GetFogScatteringCoefficient(float h) const;
glm::vec3 CalcSunColor() const;
glm::vec3 CalcMoonColor() const;
void CalcLighting(glm::vec3& direction, glm::vec3& color) const;
static glm::vec3 LinearSrgbFromSpectralSamples(glm::vec4 L);
static float RaySphereIntersection(glm::vec3 ro, glm::vec3 rd, float radius);
float GetAerosolDensity(float h) const;
void GetAtmosphereCollisionCoefficients(
float h, glm::vec4& aerosol_absorption, glm::vec4& aerosol_scattering,
glm::vec4& molecular_absorption, glm::vec4& molecular_scattering,
glm::vec4& fog_scattering, glm::vec4& extinction) const;
glm::vec4 ComputeTransmittance(const glm::vec3& dir, int steps) const;
};
struct SkyTransmittancePass : public FullScreenPass {
SkyTransmittancePass(Sky* sky) : FullScreenPass(sky->m_backend), m_sky(sky) {}
virtual void Init() override;
virtual void CreatePipelineDesc(
nvrhi::GraphicsPipelineDesc& pipeline_desc) override;
virtual void Render(nvrhi::ICommandList* command_list) override;
virtual nvrhi::IFramebuffer* GetFramebuffer() override;
nvrhi::BindingLayoutHandle m_binding_layout;
nvrhi::BindingSetHandle m_binding_set;
nvrhi::ShaderHandle m_pixel_shader;
nvrhi::FramebufferHandle m_framebuffer;
nvrhi::ITexture* m_source;
nvrhi::Format m_output_format;
nvrhi::TextureHandle m_output;
Sky* m_sky;
};
struct SkyAerialLut {
struct DispatchConstants {
glm::mat4 g_InverseView; // DO NOT TRANSPOSE
glm::mat4 g_InverseProjection;
glm::vec3 g_SunDir;
float g_Altitude;
glm::vec3 g_MoonDir;
float g_MaxDepth;
};
SkyAerialLut(Sky* sky) : m_sky(sky) {}
void Init();
void Render(nvrhi::ICommandList* command_list, const glm::dmat4& projection,
const glm::dmat4& view);
nvrhi::TextureHandle m_lut;
nvrhi::TextureHandle m_sky_texture;
nvrhi::BufferHandle m_constant_buffer;
nvrhi::ComputePipelineHandle m_pso_lut;
nvrhi::ComputePipelineHandle m_pso_sky;
int m_sky_width;
int m_sky_height;
int m_lut_width;
int m_lut_height;
int m_lut_slices;
nvrhi::BindingSetHandle m_bindings_lut;
nvrhi::BindingSetHandle m_bindings_sky;
Sky* m_sky;
};

View File

@@ -0,0 +1,262 @@
#include "ssao.h"
#include <nvrhi/nvrhi.h>
#include <nvrhi/utils.h>
#include <glm/glm.hpp>
#include "XeGTAO.h"
#include "gbuffer.h"
#include "nvrenderer/nvrenderer.h"
#include "nvrendererbackend.h"
NvSsao::NvSsao(NvRenderer* renderer)
: m_backend(renderer->m_backend.get()),
m_gbuffer(renderer->m_gbuffer.get()),
m_width(0),
m_height(0) {}
void NvSsao::Init() {
using namespace nvrhi;
m_CSPrefilterDepths16x16 = m_backend->CreateShader(
"gtao_CSPrefilterDepths16x16", ShaderType::Compute);
m_CSGTAOLow = m_backend->CreateShader("gtao_CSGTAOLow", ShaderType::Compute);
m_CSGTAOMedium =
m_backend->CreateShader("gtao_CSGTAOMedium", ShaderType::Compute);
m_CSGTAOHigh =
m_backend->CreateShader("gtao_CSGTAOHigh", ShaderType::Compute);
m_CSGTAOUltra =
m_backend->CreateShader("gtao_CSGTAOUltra", ShaderType::Compute);
m_CSDenoisePass =
m_backend->CreateShader("gtao_CSDenoisePass", ShaderType::Compute);
m_CSDenoiseLastPass =
m_backend->CreateShader("gtao_CSDenoiseLastPass", ShaderType::Compute);
m_width = m_gbuffer->m_framebuffer->getFramebufferInfo().width;
m_height = m_gbuffer->m_framebuffer->getFramebufferInfo().height;
m_constantBuffer = m_backend->GetDevice()->createBuffer(
nvrhi::utils::CreateStaticConstantBufferDesc(
sizeof(XeGTAO::GTAOConstants), "XeGTAO Constants")
.setInitialState(ResourceStates::ConstantBuffer)
.setKeepInitialState(true));
m_workingDepths = m_backend->GetDevice()->createTexture(
nvrhi::TextureDesc()
.setDebugName("XeGTAO Working Depths")
.setWidth(m_width)
.setHeight(m_height)
.setIsUAV(true)
.setFormat(Format::R32_FLOAT)
.setMipLevels(XE_GTAO_DEPTH_MIP_LEVELS)
.setInitialState(ResourceStates::ShaderResource)
.setKeepInitialState(true));
m_workingEdges = m_backend->GetDevice()->createTexture(
nvrhi::TextureDesc()
.setDebugName("XeGTAO Working Edges")
.setWidth(m_width)
.setHeight(m_height)
.setIsUAV(true)
.setFormat(Format::R8_UNORM)
.setInitialState(ResourceStates::ShaderResource)
.setKeepInitialState(true));
m_debugImage = m_backend->GetDevice()->createTexture(
nvrhi::TextureDesc()
.setDebugName("XeGTAO Debug Image")
.setWidth(m_width)
.setHeight(m_height)
.setIsUAV(true)
.setFormat(Format::R11G11B10_FLOAT)
.setInitialState(ResourceStates::ShaderResource)
.setKeepInitialState(true));
m_workingAOTerm = m_backend->GetDevice()->createTexture(
nvrhi::TextureDesc()
.setDebugName("XeGTAO Working AO Term A")
.setWidth(m_width)
.setHeight(m_height)
.setIsUAV(true)
.setFormat(Format::R8_UINT)
.setInitialState(ResourceStates::ShaderResource)
.setKeepInitialState(true));
m_workingAOTermPong = m_backend->GetDevice()->createTexture(
nvrhi::TextureDesc()
.setDebugName("XeGTAO Working AO Term B")
.setWidth(m_width)
.setHeight(m_height)
.setIsUAV(true)
.setFormat(Format::R8_UINT)
.setInitialState(ResourceStates::ShaderResource)
.setKeepInitialState(true));
m_outputAO = m_backend->GetDevice()->createTexture(
nvrhi::TextureDesc()
.setDebugName("XeGTAO Output AO")
.setWidth(m_width)
.setHeight(m_height)
.setIsUAV(true)
.setFormat(Format::R8_UNORM)
.setIsTypeless(true)
.setInitialState(ResourceStates::ShaderResource)
.setKeepInitialState(true));
SamplerHandle sampler_point = m_SamplerPoint = m_backend->GetDevice()->createSampler(
nvrhi::SamplerDesc()
.setAllAddressModes(SamplerAddressMode::Clamp)
.setAllFilters(false));
{
BindingLayoutHandle blPrefilterDepths;
BindingSetDesc bsDescPrefilter =
BindingSetDesc()
.addItem(BindingSetItem::ConstantBuffer(0, m_constantBuffer))
.addItem(BindingSetItem::Texture_SRV(0, m_gbuffer->m_gbuffer_depth))
.addItem(BindingSetItem::Sampler(0, sampler_point));
for (int i = 0; i < XE_GTAO_DEPTH_MIP_LEVELS; ++i) {
bsDescPrefilter.addItem(
BindingSetItem::Texture_UAV(i, m_workingDepths, Format::UNKNOWN,
TextureSubresourceSet(i, 1, 0, 1)));
}
utils::CreateBindingSetAndLayout(m_backend->GetDevice(),
ShaderType::Compute, 0, bsDescPrefilter,
blPrefilterDepths, m_BSPrefilterDepths);
m_PSOPrefilterDepths = m_backend->GetDevice()->createComputePipeline(
ComputePipelineDesc()
.setComputeShader(m_CSPrefilterDepths16x16)
.addBindingLayout(blPrefilterDepths));
}
{
BindingLayoutHandle blGTAO;
utils::CreateBindingSetAndLayout(
m_backend->GetDevice(), ShaderType::Compute, 0,
BindingSetDesc()
.addItem(BindingSetItem::ConstantBuffer(0, m_constantBuffer))
.addItem(BindingSetItem ::Texture_SRV(0, m_workingDepths))
.addItem(
BindingSetItem::Texture_SRV(1, m_gbuffer->m_gbuffer_normal))
.addItem(BindingSetItem::Texture_UAV(0, m_workingAOTerm))
.addItem(BindingSetItem::Texture_UAV(1, m_workingEdges))
.addItem(BindingSetItem::Texture_UAV(2, m_debugImage))
.addItem(BindingSetItem::Sampler(0, sampler_point)),
blGTAO, m_BSGTAO);
m_PSOGTAO = m_backend->GetDevice()->createComputePipeline(
ComputePipelineDesc()
.setComputeShader(m_CSGTAOHigh)
.addBindingLayout(blGTAO));
}
{
BindingLayoutHandle blDenoise;
m_BLDenoise = m_backend->GetDevice()->createBindingLayout(
BindingLayoutDesc()
.setVisibility(nvrhi::ShaderType::Compute)
.addItem(BindingLayoutItem::ConstantBuffer(0))
.addItem(BindingLayoutItem::Texture_SRV(0))
.addItem(BindingLayoutItem::Texture_SRV(1))
.addItem(BindingLayoutItem::Texture_UAV(0))
.addItem(BindingLayoutItem::Texture_UAV(1))
.addItem(BindingLayoutItem::Texture_UAV(2))
.addItem(BindingLayoutItem::Sampler(0)));
utils::CreateBindingSetAndLayout(
m_backend->GetDevice(), ShaderType::Compute, 0,
BindingSetDesc()
.addItem(BindingSetItem::ConstantBuffer(0, m_constantBuffer))
.addItem(BindingSetItem::Texture_SRV(1, m_workingEdges))
.addItem(BindingSetItem::Texture_UAV(2, m_debugImage))
.addItem(BindingSetItem::Sampler(0, sampler_point)),
blDenoise, m_BSDenoise);
m_PSODenoise = m_backend->GetDevice()->createComputePipeline(
ComputePipelineDesc()
.setComputeShader(m_CSDenoisePass)
//.addBindingLayout(blDenoise)
.addBindingLayout(m_BLDenoise));
m_PSODenoiseLastPass = m_backend->GetDevice()->createComputePipeline(
ComputePipelineDesc()
.setComputeShader(m_CSDenoiseLastPass)
//.addBindingLayout(blDenoise)
.addBindingLayout(m_BLDenoise));
}
}
void NvSsao::Render(nvrhi::ICommandList* command_list,
const glm::mat4& projection, size_t frame_index) {
command_list->beginMarker("XeGTAO");
XeGTAO::GTAOConstants constants{};
XeGTAO::GTAOSettings settings{};
settings.QualityLevel = 2;
settings.Radius = 10.f;
settings.ThinOccluderCompensation = .7f;
XeGTAO::GTAOUpdateConstants(constants, m_width, m_height, settings,
glm::value_ptr(projection), false, frame_index);
command_list->writeBuffer(m_constantBuffer, &constants, sizeof(constants));
{
command_list->beginMarker("Prefilter Depth");
nvrhi::ComputeState state;
state.setPipeline(m_PSOPrefilterDepths);
state.addBindingSet(m_BSPrefilterDepths);
command_list->setComputeState(state);
command_list->dispatch((m_width + 16 - 1) / 16, (m_height + 16 - 1) / 16,
1);
command_list->endMarker();
}
{
command_list->beginMarker("Main Pass");
nvrhi::ComputeState state;
state.setPipeline(m_PSOGTAO);
state.addBindingSet(m_BSGTAO);
command_list->setComputeState(state);
command_list->dispatch(
(m_width + XE_GTAO_NUMTHREADS_X - 1) / XE_GTAO_NUMTHREADS_X,
(m_height + XE_GTAO_NUMTHREADS_Y - 1) / XE_GTAO_NUMTHREADS_Y, 1);
command_list->endMarker();
}
{
command_list->beginMarker("Denoise Pass");
int num_passes = 4;
nvrhi::ITexture* ping = m_workingAOTerm;
nvrhi::ITexture* pong = m_workingAOTermPong;
for (int i = 0; i < num_passes; ++i) {
nvrhi::ComputeState state;
bool last_pass = i == num_passes - 1;
auto bindingSet = m_backend->GetDevice()->createBindingSet(
nvrhi::BindingSetDesc()
.addItem(
nvrhi::BindingSetItem::ConstantBuffer(0, m_constantBuffer))
.addItem(nvrhi::BindingSetItem::Texture_SRV(0, ping))
.addItem(nvrhi::BindingSetItem::Texture_SRV(
1, m_gbuffer->m_gbuffer_normal))
.addItem(nvrhi::BindingSetItem::Texture_UAV(
0, last_pass ? m_outputAO.Get() : pong,
nvrhi::Format::R8_UINT))
.addItem(nvrhi::BindingSetItem::Texture_UAV(1, m_workingEdges))
.addItem(nvrhi::BindingSetItem::Texture_UAV(2, m_debugImage))
.addItem(nvrhi::BindingSetItem::Sampler(0, m_SamplerPoint)),
m_BLDenoise);
state.setPipeline(last_pass ? m_PSODenoiseLastPass : m_PSODenoise);
// state.addBindingSet(m_BSDenoise);
state.addBindingSet(bindingSet);
command_list->setComputeState(state);
command_list->dispatch(
(m_width + (XE_GTAO_NUMTHREADS_X * 2) - 1) /
(XE_GTAO_NUMTHREADS_X * 2),
(m_height + XE_GTAO_NUMTHREADS_Y - 1) / XE_GTAO_NUMTHREADS_Y, 1);
std::swap(ping, pong);
}
command_list->endMarker();
command_list->setTextureState(m_outputAO, nvrhi::AllSubresources,
nvrhi::ResourceStates::ShaderResource);
}
command_list->endMarker();
}

View File

@@ -0,0 +1,55 @@
#pragma once
#include <memory>
#include <glm/glm.hpp>
#include <nvrhi/nvrhi.h>
struct NvSsao {
NvSsao(class NvRenderer* renderer);
void Init();
class NvRendererBackend* m_backend;
struct NvGbuffer* m_gbuffer;
nvrhi::ShaderHandle m_CSPrefilterDepths16x16;
nvrhi::ComputePipelineHandle m_PSOPrefilterDepths;
nvrhi::BindingSetHandle m_BSPrefilterDepths;
nvrhi::SamplerHandle m_SamplerPoint;
nvrhi::ShaderHandle m_CSGTAOLow;
nvrhi::ShaderHandle m_CSGTAOMedium;
nvrhi::ShaderHandle m_CSGTAOHigh;
nvrhi::ShaderHandle m_CSGTAOUltra;
nvrhi::ComputePipelineHandle m_PSOGTAO;
nvrhi::BindingSetHandle m_BSGTAO;
nvrhi::ShaderHandle m_CSDenoisePass;
nvrhi::ShaderHandle m_CSDenoiseLastPass;
nvrhi::ComputePipelineHandle m_PSODenoise;
nvrhi::ComputePipelineHandle m_PSODenoiseLastPass;
nvrhi::BindingLayoutHandle m_BLDenoise;
nvrhi::BindingSetHandle m_BSDenoise;
nvrhi::ShaderHandle m_CSGenerateNormals;
nvrhi::ComputePipelineHandle m_PSOGenerateNormals;
nvrhi::BindingSetHandle m_BSGenerateNormals;
nvrhi::TextureHandle m_workingDepths;
nvrhi::TextureHandle m_workingEdges;
nvrhi::TextureHandle m_workingAOTerm;
nvrhi::TextureHandle m_workingAOTermPong;
nvrhi::TextureHandle m_workingNormals;
nvrhi::TextureHandle m_debugImage;
nvrhi::TextureHandle m_outputAO;
nvrhi::BufferHandle m_constantBuffer;
int m_width;
int m_height;
void Render(nvrhi::ICommandList* command_list, const glm::mat4& projection,
size_t frame_index);
};

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,68 @@
#include "tonemap.h"
#include <nvrhi/utils.h>
#include "config.h"
#include "nvrendererbackend.h"
void TonemapPass::Init() {
nvrhi::utils::CreateBindingSetAndLayout(
m_backend->GetDevice(), nvrhi::ShaderType::Pixel, 0,
nvrhi::BindingSetDesc()
.addItem(nvrhi::BindingSetItem::Texture_SRV(0, m_source))
.addItem(nvrhi::BindingSetItem::Texture_SRV(
1, static_cast<nvrhi::ITexture*>(
m_renderer
->GetResource("noise_2d_ldr",
nvrhi::ResourceType::Texture_SRV)
.m_resource)))
.addItem(nvrhi::BindingSetItem::Sampler(
0, m_backend->GetDevice()->createSampler(nvrhi::SamplerDesc())))
.addItem(nvrhi::BindingSetItem::PushConstants(0, sizeof(glm::vec4))),
m_binding_layout, m_binding_set);
m_pixel_shader = m_backend->CreateShader("tonemap", nvrhi::ShaderType::Pixel);
m_output = m_backend->GetDevice()->createTexture(
nvrhi::TextureDesc()
.setFormat(m_output_format)
.setWidth(m_source->getDesc().width)
.setHeight(m_source->getDesc().height)
.setIsRenderTarget(true)
.setInitialState(nvrhi::ResourceStates::ShaderResource)
.setKeepInitialState(true));
m_framebuffer = m_backend->GetDevice()->createFramebuffer(
nvrhi::FramebufferDesc().addColorAttachment(m_output));
FullScreenPass::Init();
}
void TonemapPass::CreatePipelineDesc(
nvrhi::GraphicsPipelineDesc& pipeline_desc) {
FullScreenPass::CreatePipelineDesc(pipeline_desc);
pipeline_desc.addBindingLayout(m_binding_layout);
pipeline_desc.setPixelShader(m_pixel_shader);
}
void TonemapPass::Render(nvrhi::ICommandList* command_list) {
nvrhi::GraphicsState graphics_state;
InitState(graphics_state);
graphics_state.addBindingSet(m_binding_set);
command_list->setGraphicsState(graphics_state);
auto hdr_config = NvRenderer::Config()->m_display.GetHDRConfig();
struct TonemapConstants {
MaConfig::MaTonemapType m_tonemap_function;
float m_scene_exposure;
float m_scene_nits;
float m_scene_gamma;
} tonemap_constants{hdr_config.m_tonemap_function,
glm::pow(2.f, hdr_config.m_scene_exposure), hdr_config.m_scene_nits,
hdr_config.m_scene_gamma};
command_list->setPushConstants(&tonemap_constants, sizeof(tonemap_constants));
Draw(command_list);
}
nvrhi::IFramebuffer* TonemapPass::GetFramebuffer() {
return FullScreenPass::GetFramebuffer(); // m_framebuffer;
}

View File

@@ -0,0 +1,39 @@
#pragma once
#include <nvrhi/nvrhi.h>
#include "nvrenderer/nvrenderer.h"
#include "fullscreenpass.h"
struct TonemapPass : public FullScreenPass {
TonemapPass(class NvRenderer* renderer, nvrhi::ITexture* source,
nvrhi::Format output_format = nvrhi::Format::UNKNOWN)
: FullScreenPass(renderer->GetBackend()),
m_renderer(renderer),
m_source(source),
m_output_format(output_format == nvrhi::Format::UNKNOWN
? source->getDesc().format
: output_format) {}
virtual void Init() override;
virtual void CreatePipelineDesc(
nvrhi::GraphicsPipelineDesc& pipeline_desc) override;
virtual void Render(nvrhi::ICommandList* command_list) override;
virtual nvrhi::IFramebuffer* GetFramebuffer() override;
nvrhi::BindingLayoutHandle m_binding_layout;
nvrhi::BindingSetHandle m_binding_set;
nvrhi::ShaderHandle m_pixel_shader;
nvrhi::FramebufferHandle m_framebuffer;
nvrhi::ITexture* m_source;
nvrhi::Format m_output_format;
nvrhi::TextureHandle m_output;
class NvRenderer* m_renderer;
};

View File

@@ -0,0 +1,10 @@
#pragma once
#include <Model3d.h>
struct MaTopologyUtils {
void ConvertTopology(gfx::index_array &Indices, gfx::vertex_array &Vertices,
int const Typ) const;
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,35 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2016-2021, Intel Corporation
//
// SPDX-License-Identifier: MIT
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Author(s): Lukasz, Migas (Lukasz.Migas@intel.com) - TAA code, Filip Strugar (filip.strugar@intel.com) - integration
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifndef __TAA_SHARED_H__
#define __TAA_SHARED_H__
#include "IntelTAA.h"
#ifdef __cplusplus
namespace IntelTAA
{
#endif // #ifdef __cplusplus
#define TAA_CONSTANTSBUFFERSLOT 0
// used in a generic way depending on the shader
struct TAAConstants
{
Matrix4x4 ReprojectionMatrix;
FTAAResolve Consts;
};
#ifdef __cplusplus
} // close the namespace
#endif // #ifdef __cplusplus
#endif // __TAA_SHARED_H__

View File

@@ -0,0 +1,10 @@
file(GLOB_RECURSE src "*.hlsl*" "*.h")
list(APPEND src "project.manul")
get_target_property(dxil_path MaSzynaShaderCompiler dxil_path)
add_custom_target(
${LIBMANUL_NAME}_shaders
MaSzynaShaderCompiler "${dxil_path}" "${CMAKE_CURRENT_SOURCE_DIR}/project.manul" "$<TARGET_FILE_DIR:${PROJECT_NAME}>/shaders"
DEPENDS ${src})

View File

@@ -0,0 +1,162 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2016-2021, Intel Corporation
//
// SPDX-License-Identifier: MIT
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// XeGTAO is based on GTAO/GTSO "Jimenez et al. / Practical Real-Time Strategies for Accurate Indirect Occlusion",
// https://www.activision.com/cdn/research/Practical_Real_Time_Strategies_for_Accurate_Indirect_Occlusion_NEW%20VERSION_COLOR.pdf
//
// Implementation: Filip Strugar (filip.strugar@intel.com), Steve Mccalla <stephen.mccalla@intel.com> (\_/)
// Version: (see XeGTAO.h) (='.'=)
// Details: https://github.com/GameTechDev/XeGTAO (")_(")
//
// Version history: see XeGTAO.h
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//#include "vaShared.hlsl"
//#include "vaNoise.hlsl"
#ifndef __INTELLISENSE__ // avoids some pesky intellisense errors
#include "XeGTAO.h"
#endif
cbuffer GTAOConstantBuffer : register( b0 )
{
GTAOConstants g_GTAOConsts;
}
#include "XeGTAO.hlsli"
// input output textures for the first pass (XeGTAO_PrefilterDepths16x16)
Texture2D<float> g_srcRawDepth : register( t0 ); // source depth buffer data (in NDC space in DirectX)
RWTexture2D<lpfloat> g_outWorkingDepthMIP0 : register( u0 ); // output viewspace depth MIP (these are views into g_srcWorkingDepth MIP levels)
RWTexture2D<lpfloat> g_outWorkingDepthMIP1 : register( u1 ); // output viewspace depth MIP (these are views into g_srcWorkingDepth MIP levels)
RWTexture2D<lpfloat> g_outWorkingDepthMIP2 : register( u2 ); // output viewspace depth MIP (these are views into g_srcWorkingDepth MIP levels)
RWTexture2D<lpfloat> g_outWorkingDepthMIP3 : register( u3 ); // output viewspace depth MIP (these are views into g_srcWorkingDepth MIP levels)
RWTexture2D<lpfloat> g_outWorkingDepthMIP4 : register( u4 ); // output viewspace depth MIP (these are views into g_srcWorkingDepth MIP levels)
// input output textures for the second pass (XeGTAO_MainPass)
Texture2D<lpfloat> g_srcWorkingDepth : register( t0 ); // viewspace depth with MIPs, output by XeGTAO_PrefilterDepths16x16 and consumed by XeGTAO_MainPass
Texture2D<lpfloat3> g_srcNormalmap : register( t1 ); // source normal map (if used)
Texture2D<uint> g_srcHilbertLUT : register( t5 ); // hilbert lookup table (if any)
RWTexture2D<uint> g_outWorkingAOTerm : register( u0 ); // output AO term (includes bent normals if enabled - packed as R11G11B10 scaled by AO)
RWTexture2D<unorm float> g_outWorkingEdges : register( u1 ); // output depth-based edges used by the denoiser
RWTexture2D<uint> g_outNormalmap : register( u0 ); // output viewspace normals if generating from depth
// input output textures for the third pass (XeGTAO_Denoise)
Texture2D<uint> g_srcWorkingAOTerm : register( t0 ); // coming from previous pass
Texture2D<lpfloat> g_srcWorkingEdges : register( t1 ); // coming from previous pass
RWTexture2D<uint> g_outFinalAOTerm : register( u0 ); // final AO term - just 'visibility' or 'visibility + bent normals'
SamplerState g_samplerPointClamp : register( s0 );
// Engine-specific normal map loader
lpfloat3 LoadNormal( int2 pos )
{
#if 0
// special decoding for external normals stored in 11_11_10 unorm - modify appropriately to support your own encoding
uint packedInput = g_srcNormalmap.Load( int3(pos, 0) ).x;
float3 unpackedOutput = XeGTAO_R11G11B10_UNORM_to_FLOAT3( packedInput );
float3 normal = normalize(unpackedOutput * 2.0.xxx - 1.0.xxx);
#else
// example of a different encoding
float3 encodedNormal = g_srcNormalmap.Load(int3(pos, 0)).xyz;
float3 normal = normalize(encodedNormal * 2.0.xxx - 1.0.xxx);
normal.z *= -1.0;
#endif
#if 0 // compute worldspace to viewspace here if your engine stores normals in worldspace; if generating normals from depth here, they're already in viewspace
normal = mul( (float3x3)g_globals.View, normal );
#endif
return (lpfloat3)normal;
}
// Engine-specific screen & temporal noise loader
lpfloat2 SpatioTemporalNoise( uint2 pixCoord, uint temporalIndex ) // without TAA, temporalIndex is always 0
{
float2 noise;
#if 1 // Hilbert curve driving R2 (see https://www.shadertoy.com/view/3tB3z3)
#ifdef XE_GTAO_HILBERT_LUT_AVAILABLE // load from lookup texture...
uint index = g_srcHilbertLUT.Load( uint3( pixCoord % 64, 0 ) ).x;
#else // ...or generate in-place?
uint index = HilbertIndex( pixCoord.x, pixCoord.y );
#endif
index += 288*(temporalIndex%64); // why 288? tried out a few and that's the best so far (with XE_HILBERT_LEVEL 6U) - but there's probably better :)
// R2 sequence - see http://extremelearning.com.au/unreasonable-effectiveness-of-quasirandom-sequences/
return lpfloat2( frac( 0.5 + index * float2(0.75487766624669276005, 0.5698402909980532659114) ) );
#else // Pseudo-random (fastest but looks bad - not a good choice)
uint baseHash = Hash32( pixCoord.x + (pixCoord.y << 15) );
baseHash = Hash32Combine( baseHash, temporalIndex );
return lpfloat2( Hash32ToFloat( baseHash ), Hash32ToFloat( Hash32( baseHash ) ) );
#endif
}
// Engine-specific entry point for the first pass
[numthreads(8, 8, 1)] // <- hard coded to 8x8; each thread computes 2x2 blocks so processing 16x16 block: Dispatch needs to be called with (width + 16-1) / 16, (height + 16-1) / 16
void CSPrefilterDepths16x16( uint2 dispatchThreadID : SV_DispatchThreadID, uint2 groupThreadID : SV_GroupThreadID )
{
XeGTAO_PrefilterDepths16x16( dispatchThreadID, groupThreadID, g_GTAOConsts, g_srcRawDepth, g_samplerPointClamp, g_outWorkingDepthMIP0, g_outWorkingDepthMIP1, g_outWorkingDepthMIP2, g_outWorkingDepthMIP3, g_outWorkingDepthMIP4 );
}
// Engine-specific entry point for the second pass
[numthreads(XE_GTAO_NUMTHREADS_X, XE_GTAO_NUMTHREADS_Y, 1)]
void CSGTAOLow( const uint2 pixCoord : SV_DispatchThreadID )
{
// g_samplerPointClamp is a sampler with D3D12_FILTER_MIN_MAG_MIP_POINT filter and D3D12_TEXTURE_ADDRESS_MODE_CLAMP addressing mode
XeGTAO_MainPass( pixCoord, 1, 2, SpatioTemporalNoise(pixCoord, g_GTAOConsts.NoiseIndex), LoadNormal(pixCoord), g_GTAOConsts, g_srcWorkingDepth, g_samplerPointClamp, g_outWorkingAOTerm, g_outWorkingEdges );
}
// Engine-specific entry point for the second pass
[numthreads(XE_GTAO_NUMTHREADS_X, XE_GTAO_NUMTHREADS_Y, 1)]
void CSGTAOMedium( const uint2 pixCoord : SV_DispatchThreadID )
{
// g_samplerPointClamp is a sampler with D3D12_FILTER_MIN_MAG_MIP_POINT filter and D3D12_TEXTURE_ADDRESS_MODE_CLAMP addressing mode
XeGTAO_MainPass( pixCoord, 2, 2, SpatioTemporalNoise(pixCoord, g_GTAOConsts.NoiseIndex), LoadNormal(pixCoord), g_GTAOConsts, g_srcWorkingDepth, g_samplerPointClamp, g_outWorkingAOTerm, g_outWorkingEdges );
}
// Engine-specific entry point for the second pass
[numthreads(XE_GTAO_NUMTHREADS_X, XE_GTAO_NUMTHREADS_Y, 1)]
void CSGTAOHigh( const uint2 pixCoord : SV_DispatchThreadID )
{
// g_samplerPointClamp is a sampler with D3D12_FILTER_MIN_MAG_MIP_POINT filter and D3D12_TEXTURE_ADDRESS_MODE_CLAMP addressing mode
XeGTAO_MainPass( pixCoord, 3, 3, SpatioTemporalNoise(pixCoord, g_GTAOConsts.NoiseIndex), LoadNormal(pixCoord), g_GTAOConsts, g_srcWorkingDepth, g_samplerPointClamp, g_outWorkingAOTerm, g_outWorkingEdges );
}
// Engine-specific entry point for the second pass
[numthreads(XE_GTAO_NUMTHREADS_X, XE_GTAO_NUMTHREADS_Y, 1)]
void CSGTAOUltra( const uint2 pixCoord : SV_DispatchThreadID )
{
// g_samplerPointClamp is a sampler with D3D12_FILTER_MIN_MAG_MIP_POINT filter and D3D12_TEXTURE_ADDRESS_MODE_CLAMP addressing mode
XeGTAO_MainPass( pixCoord, 9, 3, SpatioTemporalNoise( pixCoord, g_GTAOConsts.NoiseIndex ), LoadNormal( pixCoord ), g_GTAOConsts, g_srcWorkingDepth, g_samplerPointClamp, g_outWorkingAOTerm, g_outWorkingEdges );
}
// Engine-specific entry point for the third pass
[numthreads(XE_GTAO_NUMTHREADS_X, XE_GTAO_NUMTHREADS_Y, 1)]
void CSDenoisePass( const uint2 dispatchThreadID : SV_DispatchThreadID )
{
const uint2 pixCoordBase = dispatchThreadID * uint2( 2, 1 ); // we're computing 2 horizontal pixels at a time (performance optimization)
// g_samplerPointClamp is a sampler with D3D12_FILTER_MIN_MAG_MIP_POINT filter and D3D12_TEXTURE_ADDRESS_MODE_CLAMP addressing mode
XeGTAO_Denoise( pixCoordBase, g_GTAOConsts, g_srcWorkingAOTerm, g_srcWorkingEdges, g_samplerPointClamp, g_outFinalAOTerm, false );
}
[numthreads(XE_GTAO_NUMTHREADS_X, XE_GTAO_NUMTHREADS_Y, 1)]
void CSDenoiseLastPass( const uint2 dispatchThreadID : SV_DispatchThreadID )
{
const uint2 pixCoordBase = dispatchThreadID * uint2( 2, 1 ); // we're computing 2 horizontal pixels at a time (performance optimization)
// g_samplerPointClamp is a sampler with D3D12_FILTER_MIN_MAG_MIP_POINT filter and D3D12_TEXTURE_ADDRESS_MODE_CLAMP addressing mode
XeGTAO_Denoise( pixCoordBase, g_GTAOConsts, g_srcWorkingAOTerm, g_srcWorkingEdges, g_samplerPointClamp, g_outFinalAOTerm, true );
}
// Optional screen space viewspace normals from depth generation
[numthreads(XE_GTAO_NUMTHREADS_X, XE_GTAO_NUMTHREADS_Y, 1)]
void CSGenerateNormals( const uint2 pixCoord : SV_DispatchThreadID )
{
float3 viewspaceNormal = XeGTAO_ComputeViewspaceNormal( pixCoord, g_GTAOConsts, g_srcRawDepth, g_samplerPointClamp );
// pack from [-1, 1] to [0, 1] and then to R11G11B10_UNORM
g_outNormalmap[ pixCoord ] = XeGTAO_FLOAT3_to_R11G11B10_UNORM( saturate( viewspaceNormal * 0.5 + 0.5 ) );
}
///
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,263 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2016-2021, Intel Corporation
//
// SPDX-License-Identifier: MIT
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// XeGTAO is based on GTAO/GTSO "Jimenez et al. / Practical Real-Time Strategies for Accurate Indirect Occlusion",
// https://www.activision.com/cdn/research/Practical_Real_Time_Strategies_for_Accurate_Indirect_Occlusion_NEW%20VERSION_COLOR.pdf
//
// Implementation: Filip Strugar (filip.strugar@intel.com), Steve Mccalla <stephen.mccalla@intel.com> (\_/)
// Version: 1.02 (='.'=)
// Details: https://github.com/GameTechDev/XeGTAO (")_(")
//
// Version history:
// 1.00 (2021-08-09): Initial release
// 1.01 (2021-09-02): Fix for depth going to inf for 'far' depth buffer values that are out of fp16 range
// 1.02 (2021-09-03): More fast_acos use and made final horizon cos clamping optional (off by default): 3-4% perf boost
// 1.10 (2021-09-03): Added a couple of heuristics to combat over-darkening errors in certain scenarios
// 1.20 (2021-09-06): Optional normal from depth generation is now a standalone pass: no longer integrated into
// main XeGTAO pass to reduce complexity and allow reuse; also quality of generated normals improved
// 1.21 (2021-09-28): Replaced 'groupshared'-based denoiser with a slightly slower multi-pass one where a 2-pass new
// equals 1-pass old. However, 1-pass new is faster than the 1-pass old and enough when TAA enabled.
// 1.22 (2021-09-28): Added 'XeGTAO_' prefix to all local functions to avoid name clashes with various user codebases.
// 1.30 (2021-10-10): Added support for directional component (bent normals).
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifndef __XE_GTAO_TYPES_H__
#define __XE_GTAO_TYPES_H__
#ifdef __cplusplus
#include <cmath>
namespace XeGTAO
{
// cpp<->hlsl mapping
struct Matrix4x4 { float m[16]; };
struct Vector3 { float x,y,z; };
struct Vector2 { float x,y; };
struct Vector2i { int x,y; };
typedef unsigned int uint;
#else // #ifdef __cplusplus
// cpp<->hlsl mapping
#define Matrix4x4 float4x4
#define Vector3 float3
#define Vector2 float2
#define Vector2i int2
#endif
// Global consts that need to be visible from both shader and cpp side
#define XE_GTAO_DEPTH_MIP_LEVELS 5 // this one is hard-coded to 5 for now
#define XE_GTAO_NUMTHREADS_X 8 // these can be changed
#define XE_GTAO_NUMTHREADS_Y 8 // these can be changed
struct GTAOConstants
{
Vector2i ViewportSize;
Vector2 ViewportPixelSize; // .zw == 1.0 / ViewportSize.xy
Vector2 DepthUnpackConsts;
Vector2 CameraTanHalfFOV;
Vector2 NDCToViewMul;
Vector2 NDCToViewAdd;
Vector2 NDCToViewMul_x_PixelSize;
float EffectRadius; // world (viewspace) maximum size of the shadow
float EffectFalloffRange;
float RadiusMultiplier;
float Padding0;
float FinalValuePower;
float DenoiseBlurBeta;
float SampleDistributionPower;
float ThinOccluderCompensation;
float DepthMIPSamplingOffset;
int NoiseIndex; // frameIndex % 64 if using TAA or 0 otherwise
};
// This is used only for the development (ray traced ground truth).
struct ReferenceRTAOConstants
{
float TotalRaysLength ; // similar to Radius from GTAO
float Albedo ; // the assumption on the average material albedo
int MaxBounces ; // how many rays to recurse before stopping
int AccumulatedFrames ; // how many frames have we accumulated so far (after resetting/clearing). If 0 - this is the first.
int AccumulateFrameMax ; // how many frames are we aiming to accumulate; stop when we hit!
int Padding0;
int Padding1;
int Padding2;
#ifdef __cplusplus
ReferenceRTAOConstants( ) { TotalRaysLength = 1.0f; Albedo = 0.0f; MaxBounces = 1; AccumulatedFrames = 0; AccumulateFrameMax = 0; }
#endif
};
#ifndef XE_GTAO_USE_DEFAULT_CONSTANTS
#define XE_GTAO_USE_DEFAULT_CONSTANTS 1
#endif
// some constants reduce performance if provided as dynamic values; if these constants are not required to be dynamic and they match default values,
// set XE_GTAO_USE_DEFAULT_CONSTANTS and the code will compile into a more efficient shader
#define XE_GTAO_DEFAULT_RADIUS_MULTIPLIER (1.457f ) // allows us to use different value as compared to ground truth radius to counter inherent screen space biases
#define XE_GTAO_DEFAULT_FALLOFF_RANGE (0.615f ) // distant samples contribute less
#define XE_GTAO_DEFAULT_SAMPLE_DISTRIBUTION_POWER (2.0f ) // small crevices more important than big surfaces
#define XE_GTAO_DEFAULT_THIN_OCCLUDER_COMPENSATION (0.0f ) // the new 'thickness heuristic' approach
#define XE_GTAO_DEFAULT_FINAL_VALUE_POWER (2.2f ) // modifies the final ambient occlusion value using power function - this allows some of the above heuristics to do different things
#define XE_GTAO_DEFAULT_DEPTH_MIP_SAMPLING_OFFSET (3.30f ) // main trade-off between performance (memory bandwidth) and quality (temporal stability is the first affected, thin objects next)
#define XE_GTAO_OCCLUSION_TERM_SCALE (1.5f) // for packing in UNORM (because raw, pre-denoised occlusion term can overshoot 1 but will later average out to 1)
// From https://www.shadertoy.com/view/3tB3z3 - except we're using R2 here
#define XE_HILBERT_LEVEL 6U
#define XE_HILBERT_WIDTH ( (1U << XE_HILBERT_LEVEL) )
#define XE_HILBERT_AREA ( XE_HILBERT_WIDTH * XE_HILBERT_WIDTH )
inline uint HilbertIndex( uint posX, uint posY )
{
uint index = 0U;
for( uint curLevel = XE_HILBERT_WIDTH/2U; curLevel > 0U; curLevel /= 2U )
{
uint regionX = ( posX & curLevel ) > 0U;
uint regionY = ( posY & curLevel ) > 0U;
index += curLevel * curLevel * ( (3U * regionX) ^ regionY);
if( regionY == 0U )
{
if( regionX == 1U )
{
posX = uint( (XE_HILBERT_WIDTH - 1U) ) - posX;
posY = uint( (XE_HILBERT_WIDTH - 1U) ) - posY;
}
uint temp = posX;
posX = posY;
posY = temp;
}
}
return index;
}
#ifdef __cplusplus
struct GTAOSettings
{
int QualityLevel = 2; // 0: low; 1: medium; 2: high; 3: ultra
int DenoisePasses = 1; // 0: disabled; 1: sharp; 2: medium; 3: soft
float Radius = 0.5f; // [0.0, ~ ] World (view) space size of the occlusion sphere.
// auto-tune-d settings
float RadiusMultiplier = XE_GTAO_DEFAULT_RADIUS_MULTIPLIER;
float FalloffRange = XE_GTAO_DEFAULT_FALLOFF_RANGE;
float SampleDistributionPower = XE_GTAO_DEFAULT_SAMPLE_DISTRIBUTION_POWER;
float ThinOccluderCompensation = XE_GTAO_DEFAULT_THIN_OCCLUDER_COMPENSATION;
float FinalValuePower = XE_GTAO_DEFAULT_FINAL_VALUE_POWER;
float DepthMIPSamplingOffset = XE_GTAO_DEFAULT_DEPTH_MIP_SAMPLING_OFFSET;
};
template<class T> inline T clamp( T const & v, T const & min, T const & max ) { assert( max >= min ); if( v < min ) return min; if( v > max ) return max; return v; }
// If using TAA then set noiseIndex to frameIndex % 64 - otherwise use 0
inline void GTAOUpdateConstants( XeGTAO::GTAOConstants& consts, int viewportWidth, int viewportHeight, const XeGTAO::GTAOSettings & settings, const float projMatrix[16], bool rowMajor, unsigned int frameCounter )
{
consts.ViewportSize = { viewportWidth, viewportHeight };
consts.ViewportPixelSize = { 1.0f / (float)viewportWidth, 1.0f / (float)viewportHeight };
float depthLinearizeMul = (rowMajor)?(-projMatrix[3 * 4 + 2]):(-projMatrix[3 + 2 * 4]); // float depthLinearizeMul = ( clipFar * clipNear ) / ( clipFar - clipNear );
float depthLinearizeAdd = (rowMajor)?( projMatrix[2 * 4 + 2]):( projMatrix[2 + 2 * 4]); // float depthLinearizeAdd = clipFar / ( clipFar - clipNear );
// correct the handedness issue. need to make sure this below is correct, but I think it is.
if( depthLinearizeMul * depthLinearizeAdd < 0 )
depthLinearizeAdd = -depthLinearizeAdd;
consts.DepthUnpackConsts = { depthLinearizeMul, depthLinearizeAdd };
float tanHalfFOVY = 1.0f / ((rowMajor)?(projMatrix[1 * 4 + 1]):(projMatrix[1 + 1 * 4])); // = tanf( drawContext.Camera.GetYFOV( ) * 0.5f );
float tanHalfFOVX = 1.0F / ((rowMajor)?(projMatrix[0 * 4 + 0]):(projMatrix[0 + 0 * 4])); // = tanHalfFOVY * drawContext.Camera.GetAspect( );
consts.CameraTanHalfFOV = { tanHalfFOVX, tanHalfFOVY };
consts.NDCToViewMul = { consts.CameraTanHalfFOV.x * 2.0f, consts.CameraTanHalfFOV.y * -2.0f };
consts.NDCToViewAdd = { consts.CameraTanHalfFOV.x * -1.0f, consts.CameraTanHalfFOV.y * 1.0f };
consts.NDCToViewMul_x_PixelSize = { consts.NDCToViewMul.x * consts.ViewportPixelSize.x, consts.NDCToViewMul.y * consts.ViewportPixelSize.y };
consts.EffectRadius = settings.Radius;
consts.EffectFalloffRange = settings.FalloffRange;
consts.DenoiseBlurBeta = (settings.DenoisePasses==0)?(1e4f):(1.2f); // high value disables denoise - more elegant & correct way would be do set all edges to 0
consts.RadiusMultiplier = settings.RadiusMultiplier;
consts.SampleDistributionPower = settings.SampleDistributionPower;
consts.ThinOccluderCompensation = settings.ThinOccluderCompensation;
consts.FinalValuePower = settings.FinalValuePower;
consts.DepthMIPSamplingOffset = settings.DepthMIPSamplingOffset;
consts.NoiseIndex = (settings.DenoisePasses>0)?(frameCounter % 64):(0);
consts.Padding0 = 0;
}
#ifdef IMGUI_API
inline bool GTAOImGuiSettings( XeGTAO::GTAOSettings & settings )
{
bool hadChanges = false;
ImGui::PushItemWidth( 120.0f );
ImGui::Text( "Performance/quality settings:" );
ImGui::Combo( "Quality Level", &settings.QualityLevel, "Low\0Medium\0High\0Ultra\00");
if( ImGui::IsItemHovered( ) ) ImGui::SetTooltip( "Higher quality settings use more samples per pixel but are slower" );
settings.QualityLevel = clamp( settings.QualityLevel , 0, 3 );
ImGui::Combo( "Denoising level", &settings.DenoisePasses, "Disabled\0Sharp\0Medium\0Soft\00");
if( ImGui::IsItemHovered( ) ) ImGui::SetTooltip( "The amount of edge-aware spatial denoise applied" );
settings.DenoisePasses = clamp( settings.DenoisePasses , 0, 3 );
ImGui::Text( "Visual settings:" );
settings.Radius = clamp( settings.Radius, 0.0f, 100000.0f );
hadChanges |= ImGui::InputFloat( "Effect radius", &settings.Radius , 0.05f, 0.0f, "%.2f" );
if( ImGui::IsItemHovered( ) ) ImGui::SetTooltip( "World (viewspace) effect radius\nExpected range: depends on the scene & requirements, anything from 0.01 to 1000+" );
settings.Radius = clamp( settings.Radius , 0.0f, 10000.0f );
if( ImGui::CollapsingHeader( "Auto-tuned settings (heuristics)" ) )
{
hadChanges |= ImGui::InputFloat( "Radius multiplier", &settings.RadiusMultiplier , 0.05f, 0.0f, "%.2f" );
if( ImGui::IsItemHovered( ) ) ImGui::SetTooltip( "Multiplies the 'Effect Radius' - used by the auto-tune to best match raytraced ground truth\nExpected range: [0.3, 3.0], defaults to %.3f", XE_GTAO_DEFAULT_RADIUS_MULTIPLIER );
settings.RadiusMultiplier = clamp( settings.RadiusMultiplier , 0.3f, 3.0f );
hadChanges |= ImGui::InputFloat( "Falloff range", &settings.FalloffRange , 0.05f, 0.0f, "%.2f" );
if( ImGui::IsItemHovered( ) ) ImGui::SetTooltip( "Gently reduce sample impact as it gets out of 'Effect radius' bounds\nExpected range: [0.0, 1.0], defaults to %.3f", XE_GTAO_DEFAULT_FALLOFF_RANGE );
settings.FalloffRange = clamp( settings.FalloffRange , 0.0f, 1.0f );
hadChanges |= ImGui::InputFloat( "Sample distribution power", &settings.SampleDistributionPower , 0.05f, 0.0f, "%.2f" );
if( ImGui::IsItemHovered( ) ) ImGui::SetTooltip( "Make samples on a slice equally distributed (1.0) or focus more towards the center (>1.0)\nExpected range: [1.0, 3.0], 2defaults to %.3f", XE_GTAO_DEFAULT_SAMPLE_DISTRIBUTION_POWER );
settings.SampleDistributionPower = clamp( settings.SampleDistributionPower , 1.0f, 3.0f );
hadChanges |= ImGui::InputFloat( "Thin occluder compensation", &settings.ThinOccluderCompensation, 0.05f, 0.0f, "%.2f" );
if( ImGui::IsItemHovered( ) ) ImGui::SetTooltip( "Slightly reduce impact of samples further back to counter the bias from depth-based (incomplete) input scene geometry data\nExpected range: [0.0, 0.7], defaults to %.3f", XE_GTAO_DEFAULT_THIN_OCCLUDER_COMPENSATION );
settings.ThinOccluderCompensation = clamp( settings.ThinOccluderCompensation , 0.0f, 0.7f );
hadChanges |= ImGui::InputFloat( "Final power", &settings.FinalValuePower, 0.05f, 0.0f, "%.2f" );
if( ImGui::IsItemHovered( ) ) ImGui::SetTooltip( "Applies power function to the final value: occlusion = pow( occlusion, finalPower )\nExpected range: [0.5, 5.0], defaults to %.3f", XE_GTAO_DEFAULT_FINAL_VALUE_POWER );
settings.FinalValuePower = clamp( settings.FinalValuePower , 0.5f, 5.0f );
hadChanges |= ImGui::InputFloat( "Depth MIP sampling offset", &settings.DepthMIPSamplingOffset, 0.05f, 0.0f, "%.2f" );
if( ImGui::IsItemHovered( ) ) ImGui::SetTooltip( "Mainly performance (texture memory bandwidth) setting but as a side-effect reduces overshadowing by thin objects and increases temporal instability\nExpected range: [2.0, 6.0], defaults to %.3f", XE_GTAO_DEFAULT_DEPTH_MIP_SAMPLING_OFFSET );
settings.DepthMIPSamplingOffset = clamp( settings.DepthMIPSamplingOffset , 0.0f, 30.0f );
}
ImGui::PopItemWidth( );
return hadChanges;
}
#endif // IMGUI_API
} // close the namespace
#endif // #ifdef __cplusplus
#endif // __XE_GTAO_TYPES_H__

View File

@@ -0,0 +1,855 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2016-2021, Intel Corporation
//
// SPDX-License-Identifier: MIT
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// XeGTAO is based on GTAO/GTSO "Jimenez et al. / Practical Real-Time Strategies for Accurate Indirect Occlusion",
// https://www.activision.com/cdn/research/Practical_Real_Time_Strategies_for_Accurate_Indirect_Occlusion_NEW%20VERSION_COLOR.pdf
//
// Implementation: Filip Strugar (filip.strugar@intel.com), Steve Mccalla <stephen.mccalla@intel.com> (\_/)
// Version: (see XeGTAO.h) (='.'=)
// Details: https://github.com/GameTechDev/XeGTAO (")_(")
//
// Version history: see XeGTAO.h
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifdef XE_GTAO_SHOW_DEBUG_VIZ
#include "vaShared.hlsl"
#endif
#if defined( XE_GTAO_SHOW_NORMALS ) || defined( XE_GTAO_SHOW_EDGES ) || defined( XE_GTAO_SHOW_BENT_NORMALS )
RWTexture2D<float4> g_outputDbgImage : register( u2 );
#endif
#include "XeGTAO.h"
#define XE_GTAO_PI (3.1415926535897932384626433832795)
#define XE_GTAO_PI_HALF (1.5707963267948966192313216916398)
#ifndef XE_GTAO_USE_HALF_FLOAT_PRECISION
#define XE_GTAO_USE_HALF_FLOAT_PRECISION 1
#endif
#if defined(XE_GTAO_FP32_DEPTHS) && XE_GTAO_USE_HALF_FLOAT_PRECISION
#error Using XE_GTAO_USE_HALF_FLOAT_PRECISION with 32bit depths is not supported yet unfortunately (it is possible to apply fp16 on parts not related to depth but this has not been done yet)
#endif
#if (XE_GTAO_USE_HALF_FLOAT_PRECISION != 0)
#if 1 // old fp16 approach (<SM6.2)
typedef min16float lpfloat;
typedef min16float2 lpfloat2;
typedef min16float3 lpfloat3;
typedef min16float4 lpfloat4;
typedef min16float3x3 lpfloat3x3;
#else // new fp16 approach (requires SM6.2 and -enable-16bit-types) - WARNING: perf degradation noticed on some HW, while the old (min16float) path is mostly at least a minor perf gain so this is more useful for quality testing
typedef float16_t lpfloat;
typedef float16_t2 lpfloat2;
typedef float16_t3 lpfloat3;
typedef float16_t4 lpfloat4;
typedef float16_t3x3 lpfloat3x3;
#endif
#else
typedef float lpfloat;
typedef float2 lpfloat2;
typedef float3 lpfloat3;
typedef float4 lpfloat4;
typedef float3x3 lpfloat3x3;
#endif
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// R11G11B10_UNORM <-> float3
float3 XeGTAO_R11G11B10_UNORM_to_FLOAT3( uint packedInput )
{
float3 unpackedOutput;
unpackedOutput.x = (float)( ( packedInput ) & 0x000007ff ) / 2047.0f;
unpackedOutput.y = (float)( ( packedInput >> 11 ) & 0x000007ff ) / 2047.0f;
unpackedOutput.z = (float)( ( packedInput >> 22 ) & 0x000003ff ) / 1023.0f;
return unpackedOutput;
}
// 'unpackedInput' is float3 and not float3 on purpose as half float lacks precision for below!
uint XeGTAO_FLOAT3_to_R11G11B10_UNORM( float3 unpackedInput )
{
uint packedOutput;
packedOutput =( ( uint( VA_SATURATE( unpackedInput.x ) * 2047 + 0.5f ) ) |
( uint( VA_SATURATE( unpackedInput.y ) * 2047 + 0.5f ) << 11 ) |
( uint( VA_SATURATE( unpackedInput.z ) * 1023 + 0.5f ) << 22 ) );
return packedOutput;
}
//
lpfloat4 XeGTAO_R8G8B8A8_UNORM_to_FLOAT4( uint packedInput )
{
lpfloat4 unpackedOutput;
unpackedOutput.x = (lpfloat)( packedInput & 0x000000ff ) / (lpfloat)255;
unpackedOutput.y = (lpfloat)( ( ( packedInput >> 8 ) & 0x000000ff ) ) / (lpfloat)255;
unpackedOutput.z = (lpfloat)( ( ( packedInput >> 16 ) & 0x000000ff ) ) / (lpfloat)255;
unpackedOutput.w = (lpfloat)( packedInput >> 24 ) / (lpfloat)255;
return unpackedOutput;
}
//
uint XeGTAO_FLOAT4_to_R8G8B8A8_UNORM( lpfloat4 unpackedInput )
{
return (( uint( saturate( unpackedInput.x ) * (lpfloat)255 + (lpfloat)0.5 ) ) |
( uint( saturate( unpackedInput.y ) * (lpfloat)255 + (lpfloat)0.5 ) << 8 ) |
( uint( saturate( unpackedInput.z ) * (lpfloat)255 + (lpfloat)0.5 ) << 16 ) |
( uint( saturate( unpackedInput.w ) * (lpfloat)255 + (lpfloat)0.5 ) << 24 ) );
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Inputs are screen XY and viewspace depth, output is viewspace position
float3 XeGTAO_ComputeViewspacePosition( const float2 screenPos, const float viewspaceDepth, const GTAOConstants consts )
{
float3 ret;
ret.xy = (consts.NDCToViewMul * screenPos.xy + consts.NDCToViewAdd) * viewspaceDepth;
ret.z = viewspaceDepth;
return ret;
}
float XeGTAO_ScreenSpaceToViewSpaceDepth( const float screenDepth, const GTAOConstants consts )
{
float depthLinearizeMul = consts.DepthUnpackConsts.x;
float depthLinearizeAdd = consts.DepthUnpackConsts.y;
// Optimised version of "-cameraClipNear / (cameraClipFar - projDepth * (cameraClipFar - cameraClipNear)) * cameraClipFar"
//return 2500. / (.1 - screenDepth * (.1 - 2500.)) * .1;
return depthLinearizeMul / (depthLinearizeAdd - screenDepth);
}
lpfloat4 XeGTAO_CalculateEdges( const lpfloat centerZ, const lpfloat leftZ, const lpfloat rightZ, const lpfloat topZ, const lpfloat bottomZ )
{
lpfloat4 edgesLRTB = lpfloat4( leftZ, rightZ, topZ, bottomZ ) - (lpfloat)centerZ;
lpfloat slopeLR = (edgesLRTB.y - edgesLRTB.x) * 0.5;
lpfloat slopeTB = (edgesLRTB.w - edgesLRTB.z) * 0.5;
lpfloat4 edgesLRTBSlopeAdjusted = edgesLRTB + lpfloat4( slopeLR, -slopeLR, slopeTB, -slopeTB );
edgesLRTB = min( abs( edgesLRTB ), abs( edgesLRTBSlopeAdjusted ) );
return lpfloat4(saturate( ( 1.25 - edgesLRTB / (centerZ * 0.011) ) ));
}
// packing/unpacking for edges; 2 bits per edge mean 4 gradient values (0, 0.33, 0.66, 1) for smoother transitions!
lpfloat XeGTAO_PackEdges( lpfloat4 edgesLRTB )
{
// integer version:
// edgesLRTB = saturate(edgesLRTB) * 2.9.xxxx + 0.5.xxxx;
// return (((uint)edgesLRTB.x) << 6) + (((uint)edgesLRTB.y) << 4) + (((uint)edgesLRTB.z) << 2) + (((uint)edgesLRTB.w));
//
// optimized, should be same as above
edgesLRTB = round( saturate( edgesLRTB ) * 2.9 );
return dot( edgesLRTB, lpfloat4( 64.0 / 255.0, 16.0 / 255.0, 4.0 / 255.0, 1.0 / 255.0 ) ) ;
}
float3 XeGTAO_CalculateNormal( const float4 edgesLRTB, float3 pixCenterPos, float3 pixLPos, float3 pixRPos, float3 pixTPos, float3 pixBPos )
{
// Get this pixel's viewspace normal
float4 acceptedNormals = saturate( float4( edgesLRTB.x*edgesLRTB.z, edgesLRTB.z*edgesLRTB.y, edgesLRTB.y*edgesLRTB.w, edgesLRTB.w*edgesLRTB.x ) + 0.01 );
pixLPos = normalize(pixLPos - pixCenterPos);
pixRPos = normalize(pixRPos - pixCenterPos);
pixTPos = normalize(pixTPos - pixCenterPos);
pixBPos = normalize(pixBPos - pixCenterPos);
float3 pixelNormal = acceptedNormals.x * cross( pixLPos, pixTPos ) +
+ acceptedNormals.y * cross( pixTPos, pixRPos ) +
+ acceptedNormals.z * cross( pixRPos, pixBPos ) +
+ acceptedNormals.w * cross( pixBPos, pixLPos );
pixelNormal = normalize( pixelNormal );
return pixelNormal;
}
#ifdef XE_GTAO_SHOW_DEBUG_VIZ
float4 DbgGetSliceColor(int slice, int sliceCount, bool mirror)
{
float red = (float)slice / (float)sliceCount; float green = 0.01; float blue = 1.0 - (float)slice / (float)sliceCount;
return (mirror)?(float4(blue, green, red, 0.9)):(float4(red, green, blue, 0.9));
}
#endif
// http://h14s.p5r.org/2012/09/0x5f3759df.html, [Drobot2014a] Low Level Optimizations for GCN, https://blog.selfshadow.com/publications/s2016-shading-course/activision/s2016_pbs_activision_occlusion.pdf slide 63
lpfloat XeGTAO_FastSqrt( float x )
{
return (lpfloat)(asfloat( 0x1fbd1df5 + ( asint( x ) >> 1 ) ));
}
// input [-1, 1] and output [0, PI], from https://seblagarde.wordpress.com/2014/12/01/inverse-trigonometric-functions-gpu-optimization-for-amd-gcn-architecture/
lpfloat XeGTAO_FastACos( lpfloat inX )
{
const lpfloat PI = 3.141593;
const lpfloat HALF_PI = 1.570796;
lpfloat x = abs(inX);
lpfloat res = -0.156583 * x + HALF_PI;
res *= XeGTAO_FastSqrt(1.0 - x);
return (inX >= 0) ? res : PI - res;
}
uint XeGTAO_EncodeVisibilityBentNormal( lpfloat visibility, lpfloat3 bentNormal )
{
return XeGTAO_FLOAT4_to_R8G8B8A8_UNORM( lpfloat4( bentNormal * 0.5 + 0.5, visibility ) );
}
void XeGTAO_DecodeVisibilityBentNormal( const uint packedValue, out lpfloat visibility, out lpfloat3 bentNormal )
{
lpfloat4 decoded = XeGTAO_R8G8B8A8_UNORM_to_FLOAT4( packedValue );
bentNormal = decoded.xyz * 2.0.xxx - 1.0.xxx; // could normalize - don't want to since it's done so many times, better to do it at the final step only
visibility = decoded.w;
}
void XeGTAO_OutputWorkingTerm( const uint2 pixCoord, lpfloat visibility, lpfloat3 bentNormal, RWTexture2D<uint> outWorkingAOTerm )
{
visibility = saturate( visibility / lpfloat(XE_GTAO_OCCLUSION_TERM_SCALE) );
#ifdef XE_GTAO_COMPUTE_BENT_NORMALS
outWorkingAOTerm[pixCoord] = XeGTAO_EncodeVisibilityBentNormal( visibility, bentNormal );
#else
outWorkingAOTerm[pixCoord] = uint(visibility * 255.0 + 0.5);
#endif
}
// "Efficiently building a matrix to rotate one vector to another"
// http://cs.brown.edu/research/pubs/pdfs/1999/Moller-1999-EBA.pdf / https://dl.acm.org/doi/10.1080/10867651.1999.10487509
// (using https://github.com/assimp/assimp/blob/master/include/assimp/matrix3x3.inl#L275 as a code reference as it seems to be best)
lpfloat3x3 XeGTAO_RotFromToMatrix( lpfloat3 from, lpfloat3 to )
{
const lpfloat e = dot(from, to);
const lpfloat f = abs(e); //(e < 0)? -e:e;
// WARNING: This has not been tested/worked through, especially not for 16bit floats; seems to work in our special use case (from is always {0, 0, -1}) but wouldn't use it in general
if( f > lpfloat( 1.0 - 0.0003 ) )
return lpfloat3x3( 1, 0, 0, 0, 1, 0, 0, 0, 1 );
const lpfloat3 v = cross( from, to );
/* ... use this hand optimized version (9 mults less) */
const lpfloat h = (1.0)/(1.0 + e); /* optimization by Gottfried Chen */
const lpfloat hvx = h * v.x;
const lpfloat hvz = h * v.z;
const lpfloat hvxy = hvx * v.y;
const lpfloat hvxz = hvx * v.z;
const lpfloat hvyz = hvz * v.y;
lpfloat3x3 mtx;
mtx[0][0] = e + hvx * v.x;
mtx[0][1] = hvxy - v.z;
mtx[0][2] = hvxz + v.y;
mtx[1][0] = hvxy + v.z;
mtx[1][1] = e + h * v.y * v.y;
mtx[1][2] = hvyz - v.x;
mtx[2][0] = hvxz - v.y;
mtx[2][1] = hvyz + v.x;
mtx[2][2] = e + hvz * v.z;
return mtx;
}
void XeGTAO_MainPass( const uint2 pixCoord, lpfloat sliceCount, lpfloat stepsPerSlice, const lpfloat2 localNoise, lpfloat3 viewspaceNormal, const GTAOConstants consts,
Texture2D<lpfloat> sourceViewspaceDepth, SamplerState depthSampler, RWTexture2D<uint> outWorkingAOTerm, RWTexture2D<unorm float> outWorkingEdges )
{
float2 normalizedScreenPos = (pixCoord + 0.5.xx) * consts.ViewportPixelSize;
lpfloat4 valuesUL = sourceViewspaceDepth.GatherRed( depthSampler, float2( pixCoord * consts.ViewportPixelSize ) );
lpfloat4 valuesBR = sourceViewspaceDepth.GatherRed( depthSampler, float2( pixCoord * consts.ViewportPixelSize ), int2( 1, 1 ) );
// viewspace Z at the center
lpfloat viewspaceZ = valuesUL.y; //sourceViewspaceDepth.SampleLevel( depthSampler, normalizedScreenPos, 0 ).x;
// viewspace Zs left top right bottom
const lpfloat pixLZ = valuesUL.x;
const lpfloat pixTZ = valuesUL.z;
const lpfloat pixRZ = valuesBR.z;
const lpfloat pixBZ = valuesBR.x;
lpfloat4 edgesLRTB = XeGTAO_CalculateEdges( (lpfloat)viewspaceZ, (lpfloat)pixLZ, (lpfloat)pixRZ, (lpfloat)pixTZ, (lpfloat)pixBZ );
outWorkingEdges[pixCoord] = XeGTAO_PackEdges(edgesLRTB);
// Generating screen space normals in-place is faster than generating normals in a separate pass but requires
// use of 32bit depth buffer (16bit works but visibly degrades quality) which in turn slows everything down. So to
// reduce complexity and allow for screen space normal reuse by other effects, we've pulled it out into a separate
// pass.
// However, we leave this code in, in case anyone has a use-case where it fits better.
#ifdef XE_GTAO_GENERATE_NORMALS_INPLACE
float3 CENTER = XeGTAO_ComputeViewspacePosition( normalizedScreenPos, viewspaceZ, consts );
float3 LEFT = XeGTAO_ComputeViewspacePosition( normalizedScreenPos + float2(-1, 0) * consts.ViewportPixelSize, pixLZ, consts );
float3 RIGHT = XeGTAO_ComputeViewspacePosition( normalizedScreenPos + float2( 1, 0) * consts.ViewportPixelSize, pixRZ, consts );
float3 TOP = XeGTAO_ComputeViewspacePosition( normalizedScreenPos + float2( 0, -1) * consts.ViewportPixelSize, pixTZ, consts );
float3 BOTTOM = XeGTAO_ComputeViewspacePosition( normalizedScreenPos + float2( 0, 1) * consts.ViewportPixelSize, pixBZ, consts );
viewspaceNormal = (lpfloat3)XeGTAO_CalculateNormal( edgesLRTB, CENTER, LEFT, RIGHT, TOP, BOTTOM );
#endif
// Move center pixel slightly towards camera to avoid imprecision artifacts due to depth buffer imprecision; offset depends on depth texture format used
#ifdef XE_GTAO_FP32_DEPTHS
viewspaceZ *= 0.99999; // this is good for FP32 depth buffer
#else
viewspaceZ *= 0.99920; // this is good for FP16 depth buffer
#endif
const float3 pixCenterPos = XeGTAO_ComputeViewspacePosition( normalizedScreenPos, viewspaceZ, consts );
const lpfloat3 viewVec = (lpfloat3)normalize(-pixCenterPos);
// prevents normals that are facing away from the view vector - xeGTAO struggles with extreme cases, but in Vanilla it seems rare so it's disabled by default
// viewspaceNormal = normalize( viewspaceNormal + max( 0, -dot( viewspaceNormal, viewVec ) ) * viewVec );
#ifdef XE_GTAO_SHOW_NORMALS
g_outputDbgImage[pixCoord] = float4( DisplayNormalSRGB( viewspaceNormal.xyz ), 1 );
#endif
#ifdef XE_GTAO_SHOW_EDGES
g_outputDbgImage[pixCoord] = 1.0 - float4( edgesLRTB.x, edgesLRTB.y * 0.5 + edgesLRTB.w * 0.5, edgesLRTB.z, 1.0 );
#endif
#if XE_GTAO_USE_DEFAULT_CONSTANTS != 0
const lpfloat effectRadius = (lpfloat)consts.EffectRadius * (lpfloat)XE_GTAO_DEFAULT_RADIUS_MULTIPLIER;
const lpfloat sampleDistributionPower = (lpfloat)XE_GTAO_DEFAULT_SAMPLE_DISTRIBUTION_POWER;
const lpfloat thinOccluderCompensation = (lpfloat)XE_GTAO_DEFAULT_THIN_OCCLUDER_COMPENSATION;
const lpfloat falloffRange = (lpfloat)XE_GTAO_DEFAULT_FALLOFF_RANGE * effectRadius;
#else
const lpfloat effectRadius = (lpfloat)consts.EffectRadius * (lpfloat)consts.RadiusMultiplier;
const lpfloat sampleDistributionPower = (lpfloat)consts.SampleDistributionPower;
const lpfloat thinOccluderCompensation = (lpfloat)consts.ThinOccluderCompensation;
const lpfloat falloffRange = (lpfloat)consts.EffectFalloffRange * effectRadius;
#endif
const lpfloat falloffFrom = effectRadius * ((lpfloat)1-(lpfloat)consts.EffectFalloffRange);
// fadeout precompute optimisation
const lpfloat falloffMul = (lpfloat)-1.0 / ( falloffRange );
const lpfloat falloffAdd = falloffFrom / ( falloffRange ) + (lpfloat)1.0;
lpfloat visibility = 0;
#ifdef XE_GTAO_COMPUTE_BENT_NORMALS
lpfloat3 bentNormal = 0;
#else
lpfloat3 bentNormal = viewspaceNormal;
#endif
#ifdef XE_GTAO_SHOW_DEBUG_VIZ
float3 dbgWorldPos = mul(g_globals.ViewInv, float4(pixCenterPos, 1)).xyz;
#endif
// see "Algorithm 1" in https://www.activision.com/cdn/research/Practical_Real_Time_Strategies_for_Accurate_Indirect_Occlusion_NEW%20VERSION_COLOR.pdf
{
const lpfloat noiseSlice = (lpfloat)localNoise.x;
const lpfloat noiseSample = (lpfloat)localNoise.y;
// quality settings / tweaks / hacks
const lpfloat pixelTooCloseThreshold = 1.3; // if the offset is under approx pixel size (pixelTooCloseThreshold), push it out to the minimum distance
// approx viewspace pixel size at pixCoord; approximation of NDCToViewspace( normalizedScreenPos.xy + consts.ViewportPixelSize.xy, pixCenterPos.z ).xy - pixCenterPos.xy;
const float2 pixelDirRBViewspaceSizeAtCenterZ = viewspaceZ.xx * consts.NDCToViewMul_x_PixelSize;
lpfloat screenspaceRadius = effectRadius / (lpfloat)pixelDirRBViewspaceSizeAtCenterZ.x;
// fade out for small screen radii
visibility += saturate((10 - screenspaceRadius)/100)*0.5;
#if 0 // sensible early-out for even more performance; disabled because not yet tested
[branch]
if( screenspaceRadius < pixelTooCloseThreshold )
{
XeGTAO_OutputWorkingTerm( pixCoord, 1, viewspaceNormal, outWorkingAOTerm );
return;
}
#endif
#ifdef XE_GTAO_SHOW_DEBUG_VIZ
[branch] if (IsUnderCursorRange(pixCoord, int2(1, 1)))
{
float3 dbgWorldNorm = mul((float3x3)g_globals.ViewInv, viewspaceNormal).xyz;
float3 dbgWorldViewVec = mul((float3x3)g_globals.ViewInv, viewVec).xyz;
//DebugDraw3DArrow(dbgWorldPos, dbgWorldPos + 0.5 * dbgWorldViewVec, 0.02, float4(0, 1, 0, 0.95));
//DebugDraw2DCircle(pixCoord, screenspaceRadius, float4(1, 0, 0.2, 1));
DebugDraw3DSphere(dbgWorldPos, effectRadius, float4(1, 0.2, 0, 0.1));
//DebugDraw3DText(dbgWorldPos, float2(0, 0), float4(0.6, 0.3, 0.3, 1), float4( pixelDirRBViewspaceSizeAtCenterZ.xy, 0, screenspaceRadius) );
}
#endif
// this is the min distance to start sampling from to avoid sampling from the center pixel (no useful data obtained from sampling center pixel)
const lpfloat minS = (lpfloat)pixelTooCloseThreshold / screenspaceRadius;
//[unroll]
for( lpfloat slice = 0; slice < sliceCount; slice++ )
{
lpfloat sliceK = (slice+noiseSlice) / sliceCount;
// lines 5, 6 from the paper
lpfloat phi = sliceK * XE_GTAO_PI;
lpfloat cosPhi = cos(phi);
lpfloat sinPhi = sin(phi);
lpfloat2 omega = lpfloat2(cosPhi, -sinPhi); //lpfloat2 on omega causes issues with big radii
// convert to screen units (pixels) for later use
omega *= screenspaceRadius;
// line 8 from the paper
const lpfloat3 directionVec = lpfloat3(cosPhi, sinPhi, 0);
// line 9 from the paper
const lpfloat3 orthoDirectionVec = directionVec - (dot(directionVec, viewVec) * viewVec);
// line 10 from the paper
//axisVec is orthogonal to directionVec and viewVec, used to define projectedNormal
const lpfloat3 axisVec = normalize( cross(orthoDirectionVec, viewVec) );
// alternative line 9 from the paper
// float3 orthoDirectionVec = cross( viewVec, axisVec );
// line 11 from the paper
lpfloat3 projectedNormalVec = viewspaceNormal - axisVec * dot(viewspaceNormal, axisVec);
// line 13 from the paper
lpfloat signNorm = (lpfloat)sign( dot( orthoDirectionVec, projectedNormalVec ) );
// line 14 from the paper
lpfloat projectedNormalVecLength = length(projectedNormalVec);
lpfloat cosNorm = (lpfloat)saturate(dot(projectedNormalVec, viewVec) / projectedNormalVecLength);
// line 15 from the paper
lpfloat n = signNorm * XeGTAO_FastACos(cosNorm);
// this is a lower weight target; not using -1 as in the original paper because it is under horizon, so a 'weight' has different meaning based on the normal
const lpfloat lowHorizonCos0 = cos(n+XE_GTAO_PI_HALF);
const lpfloat lowHorizonCos1 = cos(n-XE_GTAO_PI_HALF);
// lines 17, 18 from the paper, manually unrolled the 'side' loop
lpfloat horizonCos0 = lowHorizonCos0; //-1;
lpfloat horizonCos1 = lowHorizonCos1; //-1;
[unroll]
for( lpfloat step = 0; step < stepsPerSlice; step++ )
{
// R1 sequence (http://extremelearning.com.au/unreasonable-effectiveness-of-quasirandom-sequences/)
const lpfloat stepBaseNoise = lpfloat(slice + step * stepsPerSlice) * 0.6180339887498948482; // <- this should unroll
lpfloat stepNoise = frac(noiseSample + stepBaseNoise);
// approx line 20 from the paper, with added noise
lpfloat s = (step+stepNoise) / (stepsPerSlice); // + (lpfloat2)1e-6f);
// additional distribution modifier
s = (lpfloat)pow( s, (lpfloat)sampleDistributionPower );
// avoid sampling center pixel
s += minS;
// approx lines 21-22 from the paper, unrolled
lpfloat2 sampleOffset = s * omega;
lpfloat sampleOffsetLength = length( sampleOffset );
// note: when sampling, using point_point_point or point_point_linear sampler works, but linear_linear_linear will cause unwanted interpolation between neighbouring depth values on the same MIP level!
const lpfloat mipLevel = (lpfloat)clamp( log2( sampleOffsetLength ) - consts.DepthMIPSamplingOffset, 0, XE_GTAO_DEPTH_MIP_LEVELS );
// Snap to pixel center (more correct direction math, avoids artifacts due to sampling pos not matching depth texel center - messes up slope - but adds other
// artifacts due to them being pushed off the slice). Also use full precision for high res cases.
sampleOffset = round(sampleOffset) * (lpfloat2)consts.ViewportPixelSize;
#ifdef XE_GTAO_SHOW_DEBUG_VIZ
int mipLevelU = (int)round(mipLevel);
float4 mipColor = saturate( float4( mipLevelU>=3, mipLevelU>=1 && mipLevelU<=3, mipLevelU<=1, 1.0 ) );
if( all( sampleOffset == 0 ) )
DebugDraw2DText( pixCoord, float4( 1, 0, 0, 1), pixelTooCloseThreshold );
[branch] if (IsUnderCursorRange(pixCoord, int2(1, 1)))
{
//DebugDraw2DText( (normalizedScreenPos + sampleOffset) * consts.ViewportSize, mipColor, mipLevelU );
//DebugDraw2DText( (normalizedScreenPos + sampleOffset) * consts.ViewportSize, mipColor, (uint)slice );
//DebugDraw2DText( (normalizedScreenPos - sampleOffset) * consts.ViewportSize, mipColor, (uint)slice );
//DebugDraw2DText( (normalizedScreenPos - sampleOffset) * consts.ViewportSize, saturate( float4( mipLevelU>=3, mipLevelU>=1 && mipLevelU<=3, mipLevelU<=1, 1.0 ) ), mipLevelU );
}
#endif
float2 sampleScreenPos0 = normalizedScreenPos + sampleOffset;
float SZ0 = sourceViewspaceDepth.SampleLevel( depthSampler, sampleScreenPos0, mipLevel ).x;
float3 samplePos0 = XeGTAO_ComputeViewspacePosition( sampleScreenPos0, SZ0, consts );
float2 sampleScreenPos1 = normalizedScreenPos - sampleOffset;
float SZ1 = sourceViewspaceDepth.SampleLevel( depthSampler, sampleScreenPos1, mipLevel ).x;
float3 samplePos1 = XeGTAO_ComputeViewspacePosition( sampleScreenPos1, SZ1, consts );
float3 sampleDelta0 = (samplePos0 - float3(pixCenterPos)); // using lpfloat for sampleDelta causes precision issues
float3 sampleDelta1 = (samplePos1 - float3(pixCenterPos)); // using lpfloat for sampleDelta causes precision issues
lpfloat sampleDist0 = (lpfloat)length( sampleDelta0 );
lpfloat sampleDist1 = (lpfloat)length( sampleDelta1 );
// approx lines 23, 24 from the paper, unrolled
lpfloat3 sampleHorizonVec0 = (lpfloat3)(sampleDelta0 / sampleDist0);
lpfloat3 sampleHorizonVec1 = (lpfloat3)(sampleDelta1 / sampleDist1);
// any sample out of radius should be discarded - also use fallof range for smooth transitions; this is a modified idea from "4.3 Implementation details, Bounding the sampling area"
#if XE_GTAO_USE_DEFAULT_CONSTANTS != 0 && XE_GTAO_DEFAULT_THIN_OBJECT_HEURISTIC == 0
lpfloat weight0 = saturate( sampleDist0 * falloffMul + falloffAdd );
lpfloat weight1 = saturate( sampleDist1 * falloffMul + falloffAdd );
#else
// this is our own thickness heuristic that relies on sooner discarding samples behind the center
lpfloat falloffBase0 = length( lpfloat3(sampleDelta0.x, sampleDelta0.y, sampleDelta0.z * (1+thinOccluderCompensation) ) );
lpfloat falloffBase1 = length( lpfloat3(sampleDelta1.x, sampleDelta1.y, sampleDelta1.z * (1+thinOccluderCompensation) ) );
lpfloat weight0 = saturate( falloffBase0 * falloffMul + falloffAdd );
lpfloat weight1 = saturate( falloffBase1 * falloffMul + falloffAdd );
#endif
// sample horizon cos
lpfloat shc0 = (lpfloat)dot(sampleHorizonVec0, viewVec);
lpfloat shc1 = (lpfloat)dot(sampleHorizonVec1, viewVec);
// discard unwanted samples
shc0 = lerp( lowHorizonCos0, shc0, weight0 ); // this would be more correct but too expensive: cos(lerp( acos(lowHorizonCos0), acos(shc0), weight0 ));
shc1 = lerp( lowHorizonCos1, shc1, weight1 ); // this would be more correct but too expensive: cos(lerp( acos(lowHorizonCos1), acos(shc1), weight1 ));
// thickness heuristic - see "4.3 Implementation details, Height-field assumption considerations"
#if 0 // (disabled, not used) this should match the paper
lpfloat newhorizonCos0 = max( horizonCos0, shc0 );
lpfloat newhorizonCos1 = max( horizonCos1, shc1 );
horizonCos0 = (horizonCos0 > shc0)?( lerp( newhorizonCos0, shc0, thinOccluderCompensation ) ):( newhorizonCos0 );
horizonCos1 = (horizonCos1 > shc1)?( lerp( newhorizonCos1, shc1, thinOccluderCompensation ) ):( newhorizonCos1 );
#elif 0 // (disabled, not used) this is slightly different from the paper but cheaper and provides very similar results
horizonCos0 = lerp( max( horizonCos0, shc0 ), shc0, thinOccluderCompensation );
horizonCos1 = lerp( max( horizonCos1, shc1 ), shc1, thinOccluderCompensation );
#else // this is a version where thicknessHeuristic is completely disabled
horizonCos0 = max( horizonCos0, shc0 );
horizonCos1 = max( horizonCos1, shc1 );
#endif
#ifdef XE_GTAO_SHOW_DEBUG_VIZ
[branch] if (IsUnderCursorRange(pixCoord, int2(1, 1)))
{
float3 WS_samplePos0 = mul(g_globals.ViewInv, float4(samplePos0, 1)).xyz;
float3 WS_samplePos1 = mul(g_globals.ViewInv, float4(samplePos1, 1)).xyz;
float3 WS_sampleHorizonVec0 = mul( (float3x3)g_globals.ViewInv, sampleHorizonVec0).xyz;
float3 WS_sampleHorizonVec1 = mul( (float3x3)g_globals.ViewInv, sampleHorizonVec1).xyz;
// DebugDraw3DSphere( WS_samplePos0, effectRadius * 0.02, DbgGetSliceColor(slice, sliceCount, false) );
// DebugDraw3DSphere( WS_samplePos1, effectRadius * 0.02, DbgGetSliceColor(slice, sliceCount, true) );
DebugDraw3DSphere( WS_samplePos0, effectRadius * 0.02, mipColor );
DebugDraw3DSphere( WS_samplePos1, effectRadius * 0.02, mipColor );
// DebugDraw3DArrow( WS_samplePos0, WS_samplePos0 - WS_sampleHorizonVec0, 0.002, float4(1, 0, 0, 1 ) );
// DebugDraw3DArrow( WS_samplePos1, WS_samplePos1 - WS_sampleHorizonVec1, 0.002, float4(1, 0, 0, 1 ) );
// DebugDraw3DText( WS_samplePos0, float2(0, 0), float4( 1, 0, 0, 1), weight0 );
// DebugDraw3DText( WS_samplePos1, float2(0, 0), float4( 1, 0, 0, 1), weight1 );
// DebugDraw2DText( float2( 500, 94+(step+slice*3)*12 ), float4( 0, 1, 0, 1 ), float4( projectedNormalVecLength, 0, horizonCos0, horizonCos1 ) );
}
#endif
}
#if 1 // I can't figure out the slight overdarkening on high slopes, so I'm adding this fudge - in the training set, 0.05 is close (PSNR 21.34) to disabled (PSNR 21.45)
projectedNormalVecLength = lerp( projectedNormalVecLength, 1, 0.05 );
#endif
// line ~27, unrolled
lpfloat h0 = -XeGTAO_FastACos((lpfloat)horizonCos1);
lpfloat h1 = XeGTAO_FastACos((lpfloat)horizonCos0);
#if 0 // we can skip clamping for a tiny little bit more performance
h0 = n + clamp( h0-n, (lpfloat)-XE_GTAO_PI_HALF, (lpfloat)XE_GTAO_PI_HALF );
h1 = n + clamp( h1-n, (lpfloat)-XE_GTAO_PI_HALF, (lpfloat)XE_GTAO_PI_HALF );
#endif
lpfloat iarc0 = ((lpfloat)cosNorm + (lpfloat)2 * (lpfloat)h0 * (lpfloat)sin(n)-(lpfloat)cos((lpfloat)2 * (lpfloat)h0-n))/(lpfloat)4;
lpfloat iarc1 = ((lpfloat)cosNorm + (lpfloat)2 * (lpfloat)h1 * (lpfloat)sin(n)-(lpfloat)cos((lpfloat)2 * (lpfloat)h1-n))/(lpfloat)4;
lpfloat localVisibility = (lpfloat)projectedNormalVecLength * (lpfloat)(iarc0+iarc1);
visibility += localVisibility;
#ifdef XE_GTAO_COMPUTE_BENT_NORMALS
// see "Algorithm 2 Extension that computes bent normals b."
lpfloat t0 = (6*sin(h0-n)-sin(3*h0-n)+6*sin(h1-n)-sin(3*h1-n)+16*sin(n)-3*(sin(h0+n)+sin(h1+n)))/12;
lpfloat t1 = (-cos(3 * h0-n)-cos(3 * h1-n) +8 * cos(n)-3 * (cos(h0+n) +cos(h1+n)))/12;
lpfloat3 localBentNormal = lpfloat3( directionVec.x * (lpfloat)t0, directionVec.y * (lpfloat)t0, -lpfloat(t1) );
localBentNormal = (lpfloat3)mul( XeGTAO_RotFromToMatrix( lpfloat3(0,0,-1), viewVec ), localBentNormal ) * projectedNormalVecLength;
bentNormal += localBentNormal;
#endif
}
visibility /= (lpfloat)sliceCount;
visibility = pow( visibility, (lpfloat)consts.FinalValuePower );
visibility = max( (lpfloat)0.03, visibility ); // disallow total occlusion (which wouldn't make any sense anyhow since pixel is visible but also helps with packing bent normals)
#ifdef XE_GTAO_COMPUTE_BENT_NORMALS
bentNormal = normalize(bentNormal) ;
#endif
}
#if defined(XE_GTAO_SHOW_DEBUG_VIZ) && defined(XE_GTAO_COMPUTE_BENT_NORMALS)
[branch] if (IsUnderCursorRange(pixCoord, int2(1, 1)))
{
float3 dbgWorldViewNorm = mul((float3x3)g_globals.ViewInv, viewspaceNormal).xyz;
float3 dbgWorldBentNorm = mul((float3x3)g_globals.ViewInv, bentNormal).xyz;
DebugDraw3DSphereCone( dbgWorldPos, dbgWorldViewNorm, 0.3, VA_PI*0.5 - acos(saturate(visibility)), float4( 0.2, 0.2, 0.2, 0.5 ) );
DebugDraw3DSphereCone( dbgWorldPos, dbgWorldBentNorm, 0.3, VA_PI*0.5 - acos(saturate(visibility)), float4( 0.0, 1.0, 0.0, 0.7 ) );
}
#endif
XeGTAO_OutputWorkingTerm( pixCoord, visibility, bentNormal, outWorkingAOTerm );
}
// weighted average depth filter
lpfloat XeGTAO_DepthMIPFilter( lpfloat depth0, lpfloat depth1, lpfloat depth2, lpfloat depth3, const GTAOConstants consts )
{
lpfloat maxDepth = max( max( depth0, depth1 ), max( depth2, depth3 ) );
const lpfloat depthRangeScaleFactor = 0.75; // found empirically :)
#if XE_GTAO_USE_DEFAULT_CONSTANTS != 0
const lpfloat effectRadius = depthRangeScaleFactor * (lpfloat)consts.EffectRadius * (lpfloat)XE_GTAO_DEFAULT_RADIUS_MULTIPLIER;
const lpfloat falloffRange = (lpfloat)XE_GTAO_DEFAULT_FALLOFF_RANGE * effectRadius;
#else
const lpfloat effectRadius = depthRangeScaleFactor * (lpfloat)consts.EffectRadius * (lpfloat)consts.RadiusMultiplier;
const lpfloat falloffRange = (lpfloat)consts.EffectFalloffRange * effectRadius;
#endif
const lpfloat falloffFrom = effectRadius * ((lpfloat)1-(lpfloat)consts.EffectFalloffRange);
// fadeout precompute optimisation
const lpfloat falloffMul = (lpfloat)-1.0 / ( falloffRange );
const lpfloat falloffAdd = falloffFrom / ( falloffRange ) + (lpfloat)1.0;
lpfloat weight0 = saturate( (maxDepth-depth0) * falloffMul + falloffAdd );
lpfloat weight1 = saturate( (maxDepth-depth1) * falloffMul + falloffAdd );
lpfloat weight2 = saturate( (maxDepth-depth2) * falloffMul + falloffAdd );
lpfloat weight3 = saturate( (maxDepth-depth3) * falloffMul + falloffAdd );
lpfloat weightSum = weight0 + weight1 + weight2 + weight3;
return (weight0 * depth0 + weight1 * depth1 + weight2 * depth2 + weight3 * depth3) / weightSum;
}
// This is also a good place to do non-linear depth conversion for cases where one wants the 'radius' (effectively the threshold between near-field and far-field GI),
// is required to be non-linear (i.e. very large outdoors environments).
lpfloat XeGTAO_ClampDepth( float depth )
{
#ifdef XE_GTAO_USE_HALF_FLOAT_PRECISION
return (lpfloat)clamp( depth, 0.0, 65504.0 );
#else
return clamp( depth, 0.0, 3.402823466e+38 );
#endif
}
groupshared lpfloat g_scratchDepths[8][8];
void XeGTAO_PrefilterDepths16x16( uint2 dispatchThreadID /*: SV_DispatchThreadID*/, uint2 groupThreadID /*: SV_GroupThreadID*/, const GTAOConstants consts, Texture2D<float> sourceNDCDepth, SamplerState depthSampler, RWTexture2D<lpfloat> outDepth0, RWTexture2D<lpfloat> outDepth1, RWTexture2D<lpfloat> outDepth2, RWTexture2D<lpfloat> outDepth3, RWTexture2D<lpfloat> outDepth4 )
{
// MIP 0
const uint2 baseCoord = dispatchThreadID;
const uint2 pixCoord = baseCoord * 2;
float4 depths4 = sourceNDCDepth.GatherRed( depthSampler, float2( pixCoord * consts.ViewportPixelSize ), int2(1,1) );
lpfloat depth0 = XeGTAO_ClampDepth( XeGTAO_ScreenSpaceToViewSpaceDepth( depths4.w, consts ) );
lpfloat depth1 = XeGTAO_ClampDepth( XeGTAO_ScreenSpaceToViewSpaceDepth( depths4.z, consts ) );
lpfloat depth2 = XeGTAO_ClampDepth( XeGTAO_ScreenSpaceToViewSpaceDepth( depths4.x, consts ) );
lpfloat depth3 = XeGTAO_ClampDepth( XeGTAO_ScreenSpaceToViewSpaceDepth( depths4.y, consts ) );
outDepth0[ pixCoord + uint2(0, 0) ] = (lpfloat)depth0;
outDepth0[ pixCoord + uint2(1, 0) ] = (lpfloat)depth1;
outDepth0[ pixCoord + uint2(0, 1) ] = (lpfloat)depth2;
outDepth0[ pixCoord + uint2(1, 1) ] = (lpfloat)depth3;
// MIP 1
lpfloat dm1 = XeGTAO_DepthMIPFilter( depth0, depth1, depth2, depth3, consts );
outDepth1[ baseCoord ] = (lpfloat)dm1;
g_scratchDepths[ groupThreadID.x ][ groupThreadID.y ] = dm1;
GroupMemoryBarrierWithGroupSync( );
// MIP 2
[branch]
if( all( ( groupThreadID.xy % 2.xx ) == 0 ) )
{
lpfloat inTL = g_scratchDepths[groupThreadID.x+0][groupThreadID.y+0];
lpfloat inTR = g_scratchDepths[groupThreadID.x+1][groupThreadID.y+0];
lpfloat inBL = g_scratchDepths[groupThreadID.x+0][groupThreadID.y+1];
lpfloat inBR = g_scratchDepths[groupThreadID.x+1][groupThreadID.y+1];
lpfloat dm2 = XeGTAO_DepthMIPFilter( inTL, inTR, inBL, inBR, consts );
outDepth2[ baseCoord / 2 ] = (lpfloat)dm2;
g_scratchDepths[ groupThreadID.x ][ groupThreadID.y ] = dm2;
}
GroupMemoryBarrierWithGroupSync( );
// MIP 3
[branch]
if( all( ( groupThreadID.xy % 4.xx ) == 0 ) )
{
lpfloat inTL = g_scratchDepths[groupThreadID.x+0][groupThreadID.y+0];
lpfloat inTR = g_scratchDepths[groupThreadID.x+2][groupThreadID.y+0];
lpfloat inBL = g_scratchDepths[groupThreadID.x+0][groupThreadID.y+2];
lpfloat inBR = g_scratchDepths[groupThreadID.x+2][groupThreadID.y+2];
lpfloat dm3 = XeGTAO_DepthMIPFilter( inTL, inTR, inBL, inBR, consts );
outDepth3[ baseCoord / 4 ] = (lpfloat)dm3;
g_scratchDepths[ groupThreadID.x ][ groupThreadID.y ] = dm3;
}
GroupMemoryBarrierWithGroupSync( );
// MIP 4
[branch]
if( all( ( groupThreadID.xy % 8.xx ) == 0 ) )
{
lpfloat inTL = g_scratchDepths[groupThreadID.x+0][groupThreadID.y+0];
lpfloat inTR = g_scratchDepths[groupThreadID.x+4][groupThreadID.y+0];
lpfloat inBL = g_scratchDepths[groupThreadID.x+0][groupThreadID.y+4];
lpfloat inBR = g_scratchDepths[groupThreadID.x+4][groupThreadID.y+4];
lpfloat dm4 = XeGTAO_DepthMIPFilter( inTL, inTR, inBL, inBR, consts );
outDepth4[ baseCoord / 8 ] = (lpfloat)dm4;
//g_scratchDepths[ groupThreadID.x ][ groupThreadID.y ] = dm4;
}
}
lpfloat4 XeGTAO_UnpackEdges( lpfloat _packedVal )
{
uint packedVal = (uint)(_packedVal * 255.5);
lpfloat4 edgesLRTB;
edgesLRTB.x = lpfloat((packedVal >> 6) & 0x03) / 3.0; // there's really no need for mask (as it's an 8 bit input) but I'll leave it in so it doesn't cause any trouble in the future
edgesLRTB.y = lpfloat((packedVal >> 4) & 0x03) / 3.0;
edgesLRTB.z = lpfloat((packedVal >> 2) & 0x03) / 3.0;
edgesLRTB.w = lpfloat((packedVal >> 0) & 0x03) / 3.0;
return saturate( edgesLRTB );
}
#ifdef XE_GTAO_COMPUTE_BENT_NORMALS
typedef lpfloat4 AOTermType; // .xyz is bent normal, .w is visibility term
#else
typedef lpfloat AOTermType; // .x is visibility term
#endif
void XeGTAO_AddSample( AOTermType ssaoValue, lpfloat edgeValue, inout AOTermType sum, inout lpfloat sumWeight )
{
lpfloat weight = edgeValue;
sum += (weight * ssaoValue);
sumWeight += weight;
}
void XeGTAO_Output( uint2 pixCoord, RWTexture2D<uint> outputTexture, AOTermType outputValue, const uniform bool finalApply )
{
#ifdef XE_GTAO_COMPUTE_BENT_NORMALS
lpfloat visibility = outputValue.w * ((finalApply)?((lpfloat)XE_GTAO_OCCLUSION_TERM_SCALE):(1));
lpfloat3 bentNormal = normalize(outputValue.xyz);
outputTexture[pixCoord.xy] = XeGTAO_EncodeVisibilityBentNormal( visibility, bentNormal );
#else
outputValue *= (finalApply)?((lpfloat)XE_GTAO_OCCLUSION_TERM_SCALE):(1);
outputTexture[pixCoord.xy] = uint(outputValue * 255.0 + 0.5);
#endif
}
void XeGTAO_DecodeGatherPartial( const uint4 packedValue, out AOTermType outDecoded[4] )
{
for( int i = 0; i < 4; i++ )
#ifdef XE_GTAO_COMPUTE_BENT_NORMALS
XeGTAO_DecodeVisibilityBentNormal( packedValue[i], outDecoded[i].w, outDecoded[i].xyz );
#else
outDecoded[i] = lpfloat(packedValue[i]) / lpfloat(255.0);
#endif
}
void XeGTAO_Denoise( const uint2 pixCoordBase, const GTAOConstants consts, Texture2D<uint> sourceAOTerm, Texture2D<lpfloat> sourceEdges, SamplerState texSampler, RWTexture2D<uint> outputTexture, const uniform bool finalApply )
{
const lpfloat blurAmount = (finalApply)?((lpfloat)consts.DenoiseBlurBeta):((lpfloat)consts.DenoiseBlurBeta/(lpfloat)5.0);
const lpfloat diagWeight = 0.85 * 0.5;
AOTermType aoTerm[2]; // pixel pixCoordBase and pixel pixCoordBase + int2( 1, 0 )
lpfloat4 edgesC_LRTB[2];
lpfloat weightTL[2];
lpfloat weightTR[2];
lpfloat weightBL[2];
lpfloat weightBR[2];
// gather edge and visibility quads, used later
const float2 gatherCenter = float2( pixCoordBase.x, pixCoordBase.y ) * consts.ViewportPixelSize;
lpfloat4 edgesQ0 = sourceEdges.GatherRed( texSampler, gatherCenter, int2( 0, 0 ) );
lpfloat4 edgesQ1 = sourceEdges.GatherRed( texSampler, gatherCenter, int2( 2, 0 ) );
lpfloat4 edgesQ2 = sourceEdges.GatherRed( texSampler, gatherCenter, int2( 1, 2 ) );
AOTermType visQ0[4]; XeGTAO_DecodeGatherPartial( sourceAOTerm.GatherRed( texSampler, gatherCenter, int2( 0, 0 ) ), visQ0 );
AOTermType visQ1[4]; XeGTAO_DecodeGatherPartial( sourceAOTerm.GatherRed( texSampler, gatherCenter, int2( 2, 0 ) ), visQ1 );
AOTermType visQ2[4]; XeGTAO_DecodeGatherPartial( sourceAOTerm.GatherRed( texSampler, gatherCenter, int2( 0, 2 ) ), visQ2 );
AOTermType visQ3[4]; XeGTAO_DecodeGatherPartial( sourceAOTerm.GatherRed( texSampler, gatherCenter, int2( 2, 2 ) ), visQ3 );
for( int side = 0; side < 2; side++ )
{
const int2 pixCoord = int2( pixCoordBase.x + side, pixCoordBase.y );
lpfloat4 edgesL_LRTB = XeGTAO_UnpackEdges( (side==0)?(edgesQ0.x):(edgesQ0.y) );
lpfloat4 edgesT_LRTB = XeGTAO_UnpackEdges( (side==0)?(edgesQ0.z):(edgesQ1.w) );
lpfloat4 edgesR_LRTB = XeGTAO_UnpackEdges( (side==0)?(edgesQ1.x):(edgesQ1.y) );
lpfloat4 edgesB_LRTB = XeGTAO_UnpackEdges( (side==0)?(edgesQ2.w):(edgesQ2.z) );
edgesC_LRTB[side] = XeGTAO_UnpackEdges( (side==0)?(edgesQ0.y):(edgesQ1.x) );
// Edges aren't perfectly symmetrical: edge detection algorithm does not guarantee that a left edge on the right pixel will match the right edge on the left pixel (although
// they will match in majority of cases). This line further enforces the symmetricity, creating a slightly sharper blur. Works real nice with TAA.
edgesC_LRTB[side] *= lpfloat4( edgesL_LRTB.y, edgesR_LRTB.x, edgesT_LRTB.w, edgesB_LRTB.z );
#if 1 // this allows some small amount of AO leaking from neighbours if there are 3 or 4 edges; this reduces both spatial and temporal aliasing
const lpfloat leak_threshold = 2.5; const lpfloat leak_strength = 0.5;
lpfloat edginess = (saturate(4.0 - leak_threshold - dot( edgesC_LRTB[side], 1.xxxx )) / (4-leak_threshold)) * leak_strength;
edgesC_LRTB[side] = saturate( edgesC_LRTB[side] + edginess );
#endif
#ifdef XE_GTAO_SHOW_EDGES
g_outputDbgImage[pixCoord] = 1.0 - lpfloat4( edgesC_LRTB[side].x, edgesC_LRTB[side].y * 0.5 + edgesC_LRTB[side].w * 0.5, edgesC_LRTB[side].z, 1.0 );
//g_outputDbgImage[pixCoord] = 1 - float4( edgesC_LRTB[side].z, edgesC_LRTB[side].w , 1, 0 );
//g_outputDbgImage[pixCoord] = edginess.xxxx;
#endif
// for diagonals; used by first and second pass
weightTL[side] = diagWeight * (edgesC_LRTB[side].x * edgesL_LRTB.z + edgesC_LRTB[side].z * edgesT_LRTB.x);
weightTR[side] = diagWeight * (edgesC_LRTB[side].z * edgesT_LRTB.y + edgesC_LRTB[side].y * edgesR_LRTB.z);
weightBL[side] = diagWeight * (edgesC_LRTB[side].w * edgesB_LRTB.x + edgesC_LRTB[side].x * edgesL_LRTB.w);
weightBR[side] = diagWeight * (edgesC_LRTB[side].y * edgesR_LRTB.w + edgesC_LRTB[side].w * edgesB_LRTB.y);
// first pass
AOTermType ssaoValue = (side==0)?(visQ0[1]):(visQ1[0]);
AOTermType ssaoValueL = (side==0)?(visQ0[0]):(visQ0[1]);
AOTermType ssaoValueT = (side==0)?(visQ0[2]):(visQ1[3]);
AOTermType ssaoValueR = (side==0)?(visQ1[0]):(visQ1[1]);
AOTermType ssaoValueB = (side==0)?(visQ2[2]):(visQ3[3]);
AOTermType ssaoValueTL = (side==0)?(visQ0[3]):(visQ0[2]);
AOTermType ssaoValueBR = (side==0)?(visQ3[3]):(visQ3[2]);
AOTermType ssaoValueTR = (side==0)?(visQ1[3]):(visQ1[2]);
AOTermType ssaoValueBL = (side==0)?(visQ2[3]):(visQ2[2]);
lpfloat sumWeight = blurAmount;
AOTermType sum = ssaoValue * sumWeight;
XeGTAO_AddSample( ssaoValueL, edgesC_LRTB[side].x, sum, sumWeight );
XeGTAO_AddSample( ssaoValueR, edgesC_LRTB[side].y, sum, sumWeight );
XeGTAO_AddSample( ssaoValueT, edgesC_LRTB[side].z, sum, sumWeight );
XeGTAO_AddSample( ssaoValueB, edgesC_LRTB[side].w, sum, sumWeight );
XeGTAO_AddSample( ssaoValueTL, weightTL[side], sum, sumWeight );
XeGTAO_AddSample( ssaoValueTR, weightTR[side], sum, sumWeight );
XeGTAO_AddSample( ssaoValueBL, weightBL[side], sum, sumWeight );
XeGTAO_AddSample( ssaoValueBR, weightBR[side], sum, sumWeight );
aoTerm[side] = sum / sumWeight;
XeGTAO_Output( pixCoord, outputTexture, aoTerm[side], finalApply );
#ifdef XE_GTAO_SHOW_BENT_NORMALS
if( finalApply )
{
g_outputDbgImage[pixCoord] = float4( DisplayNormalSRGB( aoTerm[side].xyz /** aoTerm[side].www*/ ), 1 );
}
#endif
}
}
// Generic viewspace normal generate pass
float3 XeGTAO_ComputeViewspaceNormal( const uint2 pixCoord, const GTAOConstants consts, Texture2D<float> sourceNDCDepth, SamplerState depthSampler )
{
float2 normalizedScreenPos = (pixCoord + 0.5.xx) * consts.ViewportPixelSize;
float4 valuesUL = sourceNDCDepth.GatherRed( depthSampler, float2( pixCoord * consts.ViewportPixelSize ) );
float4 valuesBR = sourceNDCDepth.GatherRed( depthSampler, float2( pixCoord * consts.ViewportPixelSize ), int2( 1, 1 ) );
// viewspace Z at the center
float viewspaceZ = XeGTAO_ScreenSpaceToViewSpaceDepth( valuesUL.y, consts ); //sourceViewspaceDepth.SampleLevel( depthSampler, normalizedScreenPos, 0 ).x;
// viewspace Zs left top right bottom
const float pixLZ = XeGTAO_ScreenSpaceToViewSpaceDepth( valuesUL.x, consts );
const float pixTZ = XeGTAO_ScreenSpaceToViewSpaceDepth( valuesUL.z, consts );
const float pixRZ = XeGTAO_ScreenSpaceToViewSpaceDepth( valuesBR.z, consts );
const float pixBZ = XeGTAO_ScreenSpaceToViewSpaceDepth( valuesBR.x, consts );
lpfloat4 edgesLRTB = XeGTAO_CalculateEdges( (lpfloat)viewspaceZ, (lpfloat)pixLZ, (lpfloat)pixRZ, (lpfloat)pixTZ, (lpfloat)pixBZ );
float3 CENTER = XeGTAO_ComputeViewspacePosition( normalizedScreenPos, viewspaceZ, consts );
float3 LEFT = XeGTAO_ComputeViewspacePosition( normalizedScreenPos + float2(-1, 0) * consts.ViewportPixelSize, pixLZ, consts );
float3 RIGHT = XeGTAO_ComputeViewspacePosition( normalizedScreenPos + float2( 1, 0) * consts.ViewportPixelSize, pixRZ, consts );
float3 TOP = XeGTAO_ComputeViewspacePosition( normalizedScreenPos + float2( 0, -1) * consts.ViewportPixelSize, pixTZ, consts );
float3 BOTTOM = XeGTAO_ComputeViewspacePosition( normalizedScreenPos + float2( 0, 1) * consts.ViewportPixelSize, pixBZ, consts );
return XeGTAO_CalculateNormal( edgesLRTB, CENTER, LEFT, RIGHT, TOP, BOTTOM );
}

View File

@@ -0,0 +1,82 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2016-2021, Intel Corporation
//
// SPDX-License-Identifier: MIT
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Author(s): Filip Strugar (filip.strugar@intel.com)
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifndef VA_SHADER_CORE_H
#define VA_SHADER_CORE_H
#ifndef VA_COMPILED_AS_SHADER_CODE
namespace Vanilla
{
#define VA_SATURATE vaMath::Saturate
#define VA_MIN vaComponentMin
#define VA_MAX vaComponentMax
#define VA_LENGTH vaLength
#define VA_INLINE inline
#define VA_REFERENCE &
#define VA_CONST const
}
#else
#define VA_SATURATE saturate
#define VA_MIN min
#define VA_MAX max
#define VA_LENGTH length
#define VA_INLINE
#define VA_REFERENCE
#define VA_CONST
#endif
#ifndef VA_COMPILED_AS_SHADER_CODE
#include "Core/vaCoreIncludes.h"
#else
// Vanilla-specific; this include gets intercepted and macros are provided through it to allow for some macro
// shenanigans that don't work through the normal macro string pairs (like #include macros!).
#include "MagicMacrosMagicFile.h"
// Vanilla defaults to column-major matrices in shaders because that is the DXC default with no arguments, and it
// seems to be more common* in general. (*AFAIK)
// This is in contrast to the C++ side, which is row-major, so the ordering of matrix operations in shaders needs
// to be inverted, which is fine, for ex., "projectedPos = mul( g_globals.ViewProj, worldspacePos )"
// One nice side-effect is that it's easy to drop the 4th column for 4x3 matrix (on c++ side), which becomes 3x4
// (on the shader side), which is useful for reducing memory traffic for affine transforms.
// See https://github.com/microsoft/DirectXShaderCompiler/blob/master/docs/SPIR-V.rst#appendix-a-matrix-representation
// and http://www.mindcontrol.org/~hplus/graphics/matrix-layout.html for more detail.
#define vaMatrix4x4 column_major float4x4
#define vaMatrix4x3 column_major float3x4
#define vaMatrix3x3 column_major float3x3
#define vaVector4 float4
#define vaVector3 float3
#define vaVector2 float2
#define vaVector2i int2
#define vaVector2ui uint2
#define vaVector4i int4
#define vaVector4ui uint4
#define CONCATENATE_HELPER(a, b) a##b
#define CONCATENATE(a, b) CONCATENATE_HELPER(a, b)
#define B_CONCATENATER(x) CONCATENATE(b,x)
#define S_CONCATENATER(x) CONCATENATE(s,x)
#define T_CONCATENATER(x) CONCATENATE(t,x)
#define U_CONCATENATER(x) CONCATENATE(u,x)
#define ShaderMin( x, y ) min( x, y )
#endif
#endif // VA_SHADER_CORE_H

View File

@@ -0,0 +1,149 @@
Texture2D<float> g_DepthTexture : register(t0);
RWTexture2D<float> g_Output : register(u0);
sampler g_SamplerLinearClamp : register(s0);
// From https://www.shadertoy.com/view/3tB3z3 - except we're using R2 here
#define XE_HILBERT_LEVEL 6U
#define XE_HILBERT_WIDTH ( (1U << XE_HILBERT_LEVEL) )
#define XE_HILBERT_AREA ( XE_HILBERT_WIDTH * XE_HILBERT_WIDTH )
inline uint HilbertIndex( uint posX, uint posY )
{
uint index = 0U;
for( uint curLevel = XE_HILBERT_WIDTH/2U; curLevel > 0U; curLevel /= 2U )
{
uint regionX = ( posX & curLevel ) > 0U;
uint regionY = ( posY & curLevel ) > 0U;
index += curLevel * curLevel * ( (3U * regionX) ^ regionY);
if( regionY == 0U )
{
if( regionX == 1U )
{
posX = uint( (XE_HILBERT_WIDTH - 1U) ) - posX;
posY = uint( (XE_HILBERT_WIDTH - 1U) ) - posY;
}
uint temp = posX;
posX = posY;
posY = temp;
}
}
return index;
}
// Engine-specific screen & temporal noise loader
float2 SpatioTemporalNoise( uint2 pixCoord, uint temporalIndex ) // without TAA, temporalIndex is always 0
{
float2 noise;
#if 1 // Hilbert curve driving R2 (see https://www.shadertoy.com/view/3tB3z3)
#ifdef XE_GTAO_HILBERT_LUT_AVAILABLE // load from lookup texture...
uint index = g_srcHilbertLUT.Load( uint3( pixCoord % 64, 0 ) ).x;
#else // ...or generate in-place?
uint index = HilbertIndex( pixCoord.x, pixCoord.y );
#endif
index += 288*(temporalIndex%64); // why 288? tried out a few and that's the best so far (with XE_HILBERT_LEVEL 6U) - but there's probably better :)
// R2 sequence - see http://extremelearning.com.au/unreasonable-effectiveness-of-quasirandom-sequences/
return float2( frac( 0.5 + index * float2(0.75487766624669276005, 0.5698402909980532659114) ) );
#else // Pseudo-random (fastest but looks bad - not a good choice)
uint baseHash = Hash32( pixCoord.x + (pixCoord.y << 15) );
baseHash = Hash32Combine( baseHash, temporalIndex );
return float2( Hash32ToFloat( baseHash ), Hash32ToFloat( Hash32( baseHash ) ) );
#endif
}
cbuffer DrawConstants : register(b0) {
float4x4 g_Projection;
float4x4 g_InverseProjection;
float3 g_LightDirView;
float g_NumSamples;
float g_SampleRange;
float g_Thickness;
uint g_FrameIndex;
}
uint2 NdcToPixel(in float2 size, in float4 ndc);
float2 NdcToUv(in float4 ndc);
float4 PixelToNdc(in float2 size, in uint2 pixel, in float depth);
float4 UvToNdc(in float2 uv, in float depth);
uint2 ViewToPixel(in float2 size, in float3 view);
float2 ViewToUv(in float3 view);
float GetPixelDepth(in float2 size, in uint2 pixel);
float GetUvDepth(in float2 uv);
float3 PixelToViewWithDepth(in float2 size, in uint2 pixel);
float3 UvToViewWithDepth(in float2 uv);
[numthreads(8, 8, 1)]
void main(uint3 PixCoord : SV_DispatchThreadID) {
float2 size;
g_DepthTexture.GetDimensions(size.x, size.y);
uint2 pixel = PixCoord.xy;
float occlusion = 0;
g_Output[pixel] = 1.;
float2 noise = SpatioTemporalNoise(PixCoord.xy, g_FrameIndex);
float3 viewPosition = PixelToViewWithDepth(size, pixel);
float3 sample = viewPosition;
float3 step = g_LightDirView * g_SampleRange / g_NumSamples;
sample += noise.x * step;
for(int i = 0; i < g_NumSamples; ++i) {
sample += step;
float2 sample_uv = ViewToUv(sample);
if(all(and(sample_uv > 0, sample_uv < 1.))) {
float depthDelta = sample.z * .998 - GetUvDepth(sample_uv);
if(depthDelta < 0. && depthDelta > -g_Thickness) {
occlusion = 1.;
break;
}
}
}
g_Output[pixel] = 1. - occlusion;
}
uint2 NdcToPixel(in float2 size, in float4 ndc) {
return (uint2)floor(NdcToUv(ndc) * size);
}
float2 NdcToUv(in float4 ndc) {
return ndc.xy * float2(.5, -.5) + .5;
}
float4 PixelToNdc(in float2 size, in uint2 pixel, in float ndc_depth) {
return float4(float2(2., -2.) * ((((float2)pixel + .5) / size) - .5), ndc_depth, 1.);
}
float4 UvToNdc(in float2 uv, in float ndc_depth) {
return float4(float2(2., -2.) * (uv - .5), ndc_depth, 1.);
}
uint2 ViewToPixel(in float2 size, in float3 view) {
float4 ndc = mul(g_Projection, float4(view, 1.));
ndc /= ndc.w;
return NdcToPixel(size, ndc);
}
float2 ViewToUv(in float3 view) {
float4 ndc = mul(g_Projection, float4(view, 1.));
ndc /= ndc.w;
return NdcToUv(ndc);
}
float GetPixelDepth(in float2 size, in uint2 pixel) {
return PixelToViewWithDepth(size, pixel).z;
}
float GetUvDepth(in float2 uv) {
return UvToViewWithDepth(uv).z;
}
float3 PixelToViewWithDepth(in float2 size, in uint2 pixel) {
float4 ndc = PixelToNdc(size, pixel, g_DepthTexture[pixel]);
ndc = mul(g_InverseProjection, ndc);
return ndc.xyz / ndc.w;
}
float3 UvToViewWithDepth(in float2 uv) {
float4 ndc = UvToNdc(uv, g_DepthTexture.SampleLevel(g_SamplerLinearClamp, uv, 0.));
ndc = mul(g_InverseProjection, ndc);
return ndc.xyz / ndc.w;
}

View File

@@ -0,0 +1,18 @@
struct VertexOutput {
float2 m_Position : Position;
float4 m_PositionSV : SV_Position;
};
struct PixelOutput {
float4 m_Output : SV_Target0;
};
sampler source_sampler : register(s0);
Texture2D source : register(t0);
PixelOutput main(VertexOutput ps_in) {
PixelOutput result;
result.m_Output = source.Sample(source_sampler, ps_in.m_Position);
return result;
}

View File

@@ -0,0 +1,52 @@
#define PI 3.1415926535897932384626433832795
#define TWO_PI 6.283185307179586476925286766559
#define ONE_OVER_PI 0.31830988618379067153776752674503
#define TWO_OVER_PI 0.63661977236758134307553505349006
#define ONE_OVER_TWO_PI 0.15915494309189533576888376337251
float3 CalcNormal(in uint3 PixCoord);
float2 EquirectFromNormal(in float3 Normal);
float2 PixCoordToFloat(in uint2 Coord, in uint2 Size);
float2 PixCoordToFloat(in uint2 Coord, in uint2 Size) {
return ((float2)Coord + .5.xx) / (float2)Size;
}
float3 CalcNormal(in uint3 PixCoord) {
static const float3x3 FaceTransform[6] = {
// +X
float3x3( 0., 0., -1.,
0., -1., 0.,
1., 0., 0. ),
// -X
float3x3( 0., 0., 1.,
0., -1., 0.,
-1., 0., 0. ),
// +Y
float3x3( 1., 0., 0.,
0., 0., 1.,
0., 1., 0. ),
// -Y
float3x3( 1., 0., 0.,
0., 0., -1.,
0., -1., 0. ),
// +Z
float3x3( 1., 0., 0.,
0., -1., 0.,
0., 0., 1. ),
// -Z
float3x3( -1., 0., 0.,
0., -1., 0.,
0., 0., -1. )
};
uint2 FaceSize;
uint Elements;
g_OutCubemap.GetDimensions(FaceSize.x, FaceSize.y, Elements);
return normalize(mul(float3(PixCoordToFloat(PixCoord.xy, FaceSize) * 2. - 1., 1.), FaceTransform[PixCoord.z]));
}
float2 EquirectFromNormal(in float3 Normal) {
return float2(atan2(Normal.x, Normal.z) * ONE_OVER_TWO_PI, -asin(Normal.y) * ONE_OVER_PI) + .5.xx;
}

View File

@@ -0,0 +1,39 @@
#pragma pack_matrix(column_major)
struct VertexInput {
float3 m_Position : Position;
float3 m_Normal : Normal;
float2 m_TexCoord : TexCoord;
float4 m_Tangent : Tangent;
};
struct VertexOutput {
float3 m_Position : Position;
float3 m_Normal : Normal;
float2 m_TexCoord : TexCoord;
float4 m_Tangent : Tangent;
float4 m_PositionSV : SV_Position;
float4 m_PositionCS : PositionCS;
float4 m_HistoryPositionCS : HistoryPositionCS;
};
cbuffer VertexConstants : register(b0) {
float4x4 g_FaceProjection;
};
#include "manul/draw_constants.hlsli"
VertexOutput main(in VertexInput vs_in) {
VertexOutput result;
float4x3 model_view = GetModelView();
result.m_Position = mul(float4(vs_in.m_Position, 1.), model_view).xyz;
result.m_Normal = mul(float4(vs_in.m_Normal, 0.), model_view).xyz;
result.m_Normal = vs_in.m_Normal;
result.m_TexCoord = vs_in.m_TexCoord;
result.m_Tangent.xyz = vs_in.m_Tangent.xyz;
result.m_Tangent.w = vs_in.m_Tangent.w;
result.m_PositionSV = mul(g_FaceProjection, float4(result.m_Position, 1.));
result.m_PositionCS = result.m_PositionSV;
result.m_HistoryPositionCS = result.m_PositionSV;
return result;
}

View File

@@ -0,0 +1,43 @@
StructuredBuffer<float4x3> g_InTransforms : register(t0);
RWStructuredBuffer<float4x3> g_OutCulledTransforms : register(u0);
struct CommandBuffer {
uint g_IndexCountPerInstance;
uint g_InstanceCount;
uint g_StartIndexLocation;
int g_BaseVertexLocation;
uint g_StartInstanceLocation;
};
RWStructuredBuffer<CommandBuffer> g_CommandBuffer : register(u1);
cbuffer ViewData : register(b0) {
float4 g_FrustumPlanes[6];
float4x4 g_Projection;
}
cbuffer PushConstants : register(b1) {
float3 g_Origin;
float g_InstanceRadius;
float g_MinRadius;
float g_MaxRadius;
uint g_NumInstances;
}
[numthreads(64, 1, 1)]
void main(uint3 DispatchId : SV_DispatchThreadID) {
if(DispatchId.x >= g_NumInstances) return;
float4x3 instance = g_InTransforms[DispatchId.x];
//float3 position = mul(float4(0., 0., 0., 1.), instance) + g_Origin;
//for(int i = 0; i < 6; ++i) {
// if(dot(float4(position, 1.), g_FrustumPlanes[i]) < -g_InstanceRadius) return;
//}
//float4 ndc = mul(g_Projection, float4(0., g_InstanceRadius, -length(position), 1.));
//float radius = ndc.y / ndc.w;
//if(radius > g_MinRadius && radius <= g_MaxRadius) {
uint index;
InterlockedAdd(g_CommandBuffer[0].g_InstanceCount, 1, index);
g_OutCulledTransforms[index] = instance;
//}
}

View File

@@ -0,0 +1,42 @@
#pragma pack_matrix(column_major)
struct VertexInput {
float3 m_Position : Position;
float3 m_Normal : Normal;
float2 m_TexCoord : TexCoord;
float4 m_Tangent : Tangent;
};
struct VertexOutput {
float3 m_Position : Position;
float3 m_Normal : Normal;
float2 m_TexCoord : TexCoord;
float4 m_Tangent : Tangent;
float4 m_PositionSV : SV_Position;
float4 m_PositionCS : PositionCS;
float4 m_HistoryPositionCS : HistoryPositionCS;
};
cbuffer VertexConstants : register(b0) {
float4x4 g_JitteredProjection;
float4x4 g_Projection;
float4x4 g_ProjectionHistory;
};
#include "manul/draw_constants.hlsli"
VertexOutput main(in VertexInput vs_in) {
VertexOutput result;
float4x3 model_view = GetModelView();
float4x3 model_view_history = GetModelViewHistory();
result.m_Position = mul(float4(vs_in.m_Position, 1.), model_view).xyz;
result.m_Normal = mul(float4(vs_in.m_Normal, 0.), model_view).xyz;
result.m_TexCoord = vs_in.m_TexCoord;
result.m_Tangent.xyz = mul(float4(vs_in.m_Tangent.xyz, 0.), model_view).xyz;
result.m_Tangent.w = vs_in.m_Tangent.w;
result.m_PositionSV = mul(g_JitteredProjection, float4(result.m_Position, 1.));
result.m_PositionCS = mul(g_Projection, float4(result.m_Position, 1.));
float3 history_position = mul(float4(vs_in.m_Position, 1.), model_view_history);
result.m_HistoryPositionCS = mul(g_ProjectionHistory, float4(history_position, 1.));
return result;
}

View File

@@ -0,0 +1,213 @@
RWTexture2DArray<float4> g_OutCubemap : register(u0);
RWTexture2D<float2> g_OutBRDF : register(u1);
Texture2D<float3> g_SourceEquirect : register(t0);
TextureCube<float3> g_SourceCube : register(t1);
Texture1D<float2> g_SampleKernel : register(t2);
SamplerState g_SamplerLinearClampV : register(s0);
SamplerState g_SamplerLinearClamp : register(s1);
#include "cubemap_utils.hlsli"
float DistributionGGX(float NdotH, float roughness);
float GeometrySchlickGGX(float NdotV, float roughness);
float GeometrySmith(float roughness, float NdotV, float NdotL);
float3 ImportanceSampleGGX(in float2 Xi,in float Roughness , in float3 N);
void ImportanceSampleCosDir(in float2 u, in float3 N, out float3 L, out float NdotL, out float pdf);
float2 GetSample(uint sample, uint sampleCount);
struct FilterParameters {
float m_PreExposureMul;
float m_Roughness;
float m_SampleCount;
float m_SourceWidth;
float m_MipBias;
uint3 m_Offset;
};
#ifdef SPIRV
[[vk::push_constant]] ConstantBuffer<FilterParameters> g_FilterParams;
#else
cbuffer g_Const : register(b0) { FilterParameters g_FilterParams; }
#endif
#include "manul/sky.hlsli"
[numthreads(32, 32, 1)]
void CSSampleEquirectangular(uint3 PixCoord : SV_DispatchThreadID) {
//Sky(g_OutCubemap[PixCoord + g_FilterParams.m_Offset], CalcNormal(PixCoord + g_FilterParams.m_Offset), normalize(float3(0., .1, 1.)), 0.);
g_OutCubemap[PixCoord + g_FilterParams.m_Offset].rgb = clamp(g_SourceEquirect.SampleLevel(g_SamplerLinearClampV, EquirectFromNormal(CalcNormal(PixCoord)), 0.) * g_FilterParams.m_PreExposureMul, 0., 65535.);
}
[numthreads(32, 32, 1)]
void CSDiffuseIBL(uint3 PixCoord : SV_DispatchThreadID) {
float3 N = CalcNormal(PixCoord + g_FilterParams.m_Offset);
float sampleCount = g_FilterParams.m_SampleCount;
float width = g_FilterParams.m_SourceWidth;
float mipBias = g_FilterParams.m_MipBias;
float3 accBrdf = 0;
for ( uint i =0; i < sampleCount ; ++ i ) {
float2 eta = GetSample (i , sampleCount ) ;
float3 L ;
float NdotL ;
float pdf ;
ImportanceSampleCosDir ( eta , N , L , NdotL , pdf ) ;
if ( NdotL >0) {
float omegaS = 1. / ( sampleCount * pdf );
float omegaP = 4. * PI / (6. * width * width );
float mipLevel = max(0., mipBias + .5 * log2 ( omegaS / omegaP ));
accBrdf += g_SourceCube . SampleLevel ( g_SamplerLinearClamp , L, mipLevel ) ;
}
}
g_OutCubemap[PixCoord + g_FilterParams.m_Offset].rgb = accBrdf * (1. / sampleCount);
}
[numthreads(32, 32, 1)]
void CSSpecularIBL(uint3 PixCoord : SV_DispatchThreadID) {
float3 N = CalcNormal(PixCoord + g_FilterParams.m_Offset);
float roughness = g_FilterParams.m_Roughness;
float width = g_FilterParams.m_SourceWidth;
float sampleCount = g_FilterParams.m_SampleCount;
float mipBias = g_FilterParams.m_MipBias;
float3 accBrdf = 0;
float accWeight = 0;
for ( uint i =0; i < sampleCount ; ++ i ) {
float2 eta = GetSample (i , sampleCount ) ;
float pdf ;
float3 H = ImportanceSampleGGX(eta, roughness, N);
float3 L = normalize(2 * dot( N, H ) * H - N);
float NdotL = dot(N, L);
float NdotH = dot(N, H);
if(NdotL > 0) {
float D = DistributionGGX(NdotH, roughness);
float pdf = D / 4.;
float omegaS = 1. / ( sampleCount * pdf );
float omegaP = 4. * PI / (6. * width * width );
float mipLevel = roughness == 0. ? mipBias : max(0., mipBias + .5 * log2 ( omegaS / omegaP ));
accBrdf += g_SourceCube . SampleLevel ( g_SamplerLinearClamp , L, mipLevel ) * NdotL;
accWeight += NdotL;
}
}
g_OutCubemap[PixCoord + g_FilterParams.m_Offset].rgb = accBrdf * (1. / accWeight);
}
[numthreads(32, 32, 1)]
void CSIntegrateBRDF(uint3 PixCoord : SV_DispatchThreadID) {
uint2 FaceSize;
g_OutBRDF.GetDimensions(FaceSize.x, FaceSize.y);
float NoV = ((float)PixCoord.x + .5) / (float)FaceSize.x;
float Roughness = ((float)PixCoord.y + .5) / (float)FaceSize.y;
float3 N = float3(0., 0., 1.);
float3 V;
V.x = sqrt( 1. - NoV * NoV ); // sin
V.y = 0.;
V.z = NoV; // cos
float A = 0.;
float B = 0.;
float sampleCount = 1024.;
for( uint i = 0; i < sampleCount; i++ )
{
float2 Xi = GetSample (i , sampleCount );
float3 H = ImportanceSampleGGX(Xi, Roughness, N);
float3 L = normalize(2. * dot( V, H ) * H - V);
float NoL = saturate( L.z );
float NoH = saturate( H.z );
float VoH = saturate( dot( V, H ) );
if( NoL > 0. )
{
float G = GeometrySmith( Roughness, NoV, NoL );
float G_Vis = G * VoH / (NoH * NoV);
float Fc = pow( 1. - VoH, 5. );
A += (1. - Fc) * G_Vis;
B += Fc * G_Vis;
}
}
g_OutBRDF[PixCoord.xy] = float2( A, B ) / sampleCount;
}
[numthreads(32, 32, 1)]
void CSGenerateCubeMip(uint3 PixCoord : SV_DispatchThreadID) {
g_OutCubemap[PixCoord + g_FilterParams.m_Offset].rgb = g_SourceCube.SampleLevel(g_SamplerLinearClamp, CalcNormal(PixCoord + g_FilterParams.m_Offset), 0.);
}
float GeometrySchlickGGX(float NdotV, float roughness)
{
// note that we use a different k for IBL
float a = roughness;
float k = (a * a) / 2.0;
float nom = NdotV;
float denom = NdotV * (1.0 - k) + k;
return nom / denom;
}
float GeometrySmith(float roughness, float NdotV, float NdotL)
{
float ggx2 = GeometrySchlickGGX(NdotV, roughness);
float ggx1 = GeometrySchlickGGX(NdotL, roughness);
return ggx1 * ggx2;
}
float DistributionGGX(float NdotH, float roughness)
{
float a = roughness*roughness;
float a2 = a*a;
float nom = a2;
float denom = (NdotH*NdotH * (a2 - 1.0) + 1.0);
denom = PI * denom * denom;
return nom / denom;
}
void ImportanceSampleCosDir (in float2 Xi, in float3 N, out float3 L, out float NdotL, out float pdf) {
// Local referencial
float3 upVector = abs (N.z) < .999 ? float3 (0., 0., 1.) : float3 (1., 0., 0.) ;
float3 tangentX = normalize ( cross ( upVector , N ) ) ;
float3 tangentY = cross ( N , tangentX ) ;
float r = sqrt ( Xi.x ) ;
float phi = Xi.y * TWO_PI;
L = float3 ( r * cos ( phi ) , r * sin ( phi ) , sqrt ( max (0. ,1. - Xi.x ) ) ) ;
L = normalize ( tangentX * L . y + tangentY * L . x + N * L . z ) ;
NdotL = dot (L , N ) ;
pdf = NdotL * ONE_OVER_PI ;
}
float3 ImportanceSampleGGX(in float2 Xi,in float Roughness , in float3 N)
{
double a = Roughness * Roughness;
float Phi = TWO_PI * Xi.x;
float CosTheta = sqrt( (float)((1. - Xi.y) / ( 1. + (a*a - 1.) * Xi.y ) ));
float SinTheta = sqrt( 1. - CosTheta * CosTheta );
float3 H = float3( SinTheta * cos( Phi ), SinTheta * sin( Phi ), CosTheta);
float3 UpVector = abs(N.z) < .999 ? float3(0.,0.,1.) : float3(1.,0.,0.);
float3 TangentX = normalize( cross( UpVector , N ) );
float3 TangentY = cross( N, TangentX );
// Tangent to world space
return normalize( TangentX * H.x + TangentY * H.y + N * H.z );
}
float RadicalInverse_VdC(uint bits)
{
return float(reversebits(bits)) * 2.3283064365386963e-10; // / 0x100000000
}
float2 GetSample(uint i, uint N) {
return float2((float)i/(float)N, RadicalInverse_VdC(i));
}

View File

@@ -0,0 +1,16 @@
struct VertexInput {
float2 m_Position : Position;
};
struct VertexOutput {
float2 m_Position : Position;
float4 m_PositionSV : SV_Position;
};
VertexOutput main(in VertexInput vs_in) {
VertexOutput result;
result.m_Position = vs_in.m_Position;
result.m_PositionSV = float4(vs_in.m_Position * float2(2., -2.) + float2(-1., 1.), 0., 1.);
return result;
}

View File

@@ -0,0 +1,69 @@
#define PI 3.1415926535897932384626433832795
#define TWO_PI 6.283185307179586476925286766559
#define ONE_OVER_PI 0.31830988618379067153776752674503
#define TWO_OVER_PI 0.63661977236758134307553505349006
#define ONE_OVER_TWO_PI 0.15915494309189533576888376337251
RWTexture2DArray<float4> g_OutCubemap : register(u0);
TextureCube<float3> g_Skybox : register(t0);
TextureCube<float3> g_Diffuse : register(t1);
TextureCube<float3> g_Normal : register(t2);
TextureCube<float4> g_Params : register(t3);
TextureCube<float> g_Depth : register(t4);
TextureCube<float3> g_DiffuseIBL : register(t5);
SamplerState g_SamplerLinearClamp : register(s0);
SamplerState g_SamplerPointClamp : register(s1);
#include "cubemap_utils.hlsli"
#include "manul/sky.hlsli"
struct FilterParameters {
uint3 m_Offset;
uint unused;
float3 m_LightVector;
float m_Altitude;
float3 m_LightColor;
};
#ifdef SPIRV
[[vk::push_constant]] ConstantBuffer<FilterParameters> g_FilterParams;
#else
cbuffer g_Const : register(b0) { FilterParameters g_FilterParams; }
#endif
cbuffer FilterConstants : register(b1) {
float4x4 g_InverseProjection;
};
[numthreads(32, 32, 1)]
void main(uint3 PixCoord : SV_DispatchThreadID) {
float3 normal = CalcNormal(PixCoord + g_FilterParams.m_Offset);
float3 size;
g_OutCubemap.GetDimensions(size.x, size.y, size.z);
//g_OutCubemap[PixCoord + g_Offset] = g_Skybox.SampleLevel(g_SamplerLinearClamp, normal, 0.);
float3 color = 1.e-7;
CalcAtmosphere(color, normal, g_FilterParams.m_LightVector);
//CalcAtmosphere(g_OutCubemap[PixCoord + g_Offset], 1., normal, g_LightVector, g_Altitude, SKY_INF, g_LightColor.rgb, 10);
float3 normal_flipped = normal * float3(-1., 1., 1.);
float depth = g_Depth.SampleLevel(g_SamplerPointClamp, normal_flipped, 0.);
float linear_depth = 2500.;
if(depth > 0.) {
float3 material_albedo = g_Diffuse.SampleLevel(g_SamplerPointClamp, normal_flipped, 0.);
float4 material_params = g_Params.SampleLevel(g_SamplerPointClamp, normal_flipped, 0.);
float3 material_normal = (g_Normal.SampleLevel(g_SamplerPointClamp, normal_flipped, 0.) - .5) * 2.;
float NdotL = max(dot(material_normal, g_FilterParams.m_LightVector), 0.);
color = material_albedo * (g_DiffuseIBL.SampleLevel(g_SamplerPointClamp, material_normal, 0.) + ONE_OVER_PI * NdotL * g_FilterParams.m_LightColor.rgb) * material_params.b;
float4 position_ndc = mul(g_InverseProjection, float4(PixCoordToFloat(PixCoord.xy + g_FilterParams.m_Offset.xy, size.xy) * 2. - 1., depth, 1.));
position_ndc /= position_ndc.w;
linear_depth = length(position_ndc.xyz);
}
ApplyAerialPerspective(color, 1., normal, g_FilterParams.m_LightVector, linear_depth/2500.);
g_OutCubemap[PixCoord + g_FilterParams.m_Offset].rgb = color;
}

View File

@@ -0,0 +1,118 @@
#include "manul/math.hlsli"
struct VertexOutput {
float2 m_Position : Position;
float4 m_PositionSV : SV_Position;
};
struct PixelOutput {
float4 m_Color : SV_Target0;
};
Texture2D<float3> gbuffer_diffuse : register(t0);
Texture2D<float3> gbuffer_emission : register(t1);
Texture2D<float4> gbuffer_params : register(t2);
Texture2D<float3> gbuffer_normal : register(t3);
Texture2D<float> gbuffer_depth : register(t4);
RWTexture2D<float4> output : register(u0);
#define DEFERRED_LIGHTING_PASS
#include "manul/gbuffer_ssao.hlsli"
//#include "manul/gbuffer_contact_shadows.hlsli"
#include "manul/shadow.hlsli"
#include "manul/lighting.hlsli"
#include "manul/sky.hlsli"
#define BLOCK_SIZE 8
#define TILE_BORDER 1
#define TILE_SIZE (BLOCK_SIZE + 2 * TILE_BORDER)
groupshared float2 tile_XY[TILE_SIZE*TILE_SIZE];
groupshared float tile_Z[TILE_SIZE*TILE_SIZE];
uint2 unflatten2D(uint idx, uint2 dim)
{
return uint2(idx % dim.x, idx / dim.x);
}
uint flatten2D(uint2 coord, uint2 dim)
{
return coord.x + coord.y * dim.x;
}
[numthreads(BLOCK_SIZE, BLOCK_SIZE, 1)]
void main(uint3 PixCoord : SV_DispatchThreadID, uint3 GroupID : SV_GroupID, uint GroupIndex : SV_GroupIndex) {
uint2 gbuffer_dimensions;
gbuffer_depth.GetDimensions(gbuffer_dimensions.x, gbuffer_dimensions.y);
const int2 tile_upperleft = GroupID.xy * BLOCK_SIZE - TILE_BORDER;
for (uint t = GroupIndex; t < TILE_SIZE * TILE_SIZE; t += BLOCK_SIZE * BLOCK_SIZE)
{
const uint2 pixel = tile_upperleft + unflatten2D(t, TILE_SIZE);
const float depth = gbuffer_depth[pixel];
const float3 position = ReconstructPos(PixelToCS(pixel, gbuffer_dimensions), depth);
tile_XY[t] = position.xy;
tile_Z[t] = position.z;
}
GroupMemoryBarrierWithGroupSync();
// Decode material data
MaterialData material;
material.m_PixelCoord = PixCoord.xy;
float2 uv = ( material.m_PixelCoord + .5) / gbuffer_dimensions;
uint2 tile_co = material.m_PixelCoord - tile_upperleft;
//float depth = gbuffer_depth[ material.m_PixelCoord];
uint co = flatten2D(tile_co, TILE_SIZE);
uint co_px = flatten2D(tile_co + int2(1, 0), TILE_SIZE);
uint co_nx = flatten2D(tile_co + int2(-1, 0), TILE_SIZE);
uint co_py = flatten2D(tile_co + int2(0, 1), TILE_SIZE);
uint co_ny = flatten2D(tile_co + int2(0, -1), TILE_SIZE);
float depth = tile_Z[co];
float depth_px = tile_Z[co_px];
float depth_nx = tile_Z[co_nx];
float depth_py = tile_Z[co_py];
float depth_ny = tile_Z[co_ny];
material.m_Position = float3(tile_XY[co], depth);
if(abs(depth_px - depth) < abs(depth_nx - depth)) {
material.m_PositionDDX.xy = tile_XY[co_px];
material.m_PositionDDX.z = depth_px;
}
else{
material.m_PositionDDX.xy = tile_XY[co_nx];
material.m_PositionDDX.z = depth_nx;
}
if(abs(depth_py - depth) < abs(depth_ny - depth)) {
material.m_PositionDDY.xy = tile_XY[co_py];
material.m_PositionDDY.z = depth_py;
}
else{
material.m_PositionDDY.xy = tile_XY[co_ny];
material.m_PositionDDY.z = depth_ny;
}
material.m_MaterialAlbedoAlpha.rgb = gbuffer_diffuse[ material.m_PixelCoord];
material.m_MaterialAlbedoAlpha.a = 1.;
material.m_MaterialEmission = gbuffer_emission[ material.m_PixelCoord];
material.m_MaterialParams = gbuffer_params[ material.m_PixelCoord];
material.m_MaterialNormal = UnpackNormalXYZ(gbuffer_normal[ material.m_PixelCoord]);
PixelOutput ps_out;
#if LIGHTING_NEEDS_PIXELPOSITION
ApplyMaterialLighting(output[material.m_PixelCoord], material, material.m_PixelCoord);
#else
ApplyMaterialLighting(output[material.m_PixelCoord], material);
#endif
float3 view_world = mul((float3x3)g_InverseModelView, material.m_Position);
ApplyAerialPerspective(output[material.m_PixelCoord].rgb, 1., normalize(view_world), g_LightDir, length(view_world)/2500.);
//if(depth < 1.){
// CalcAtmosphere(ps_out.m_Color.rgb, 1., normalize(mul((float3x3) g_InverseModelView, material.m_Position)), g_LightDir.xyz, g_Altitude, length(view.xyz), g_LightColor.rgb, 10);
//}
//return ps_out;
}

View File

@@ -0,0 +1,46 @@
#pragma pack_matrix(column_major)
struct VertexInput {
float3 m_Position : Position;
float4 m_PositionCS : PositionCS;
float4 m_HistoryPositionCS : HistoryPositionCS;
float3 m_Normal : Normal;
float2 m_TexCoord : TexCoord;
float4 m_Tangent : Tangent;
float4 m_PositionSV : SV_Position;
};
struct VertexOutput {
float3 m_Position : Position;
float4 m_PositionCS : PositionCS;
float4 m_HistoryPositionCS : HistoryPositionCS;
float3 m_Normal : Normal;
float2 m_TexCoord : TexCoord;
float4 m_Tangent : Tangent;
float4 m_PositionSV : SV_Position;
uint m_ViewportIndex : SV_RenderTargetArrayIndex;
};
cbuffer CubeDrawConstants : register(b0) {
float4x4 g_FaceProjections[6];
};
[maxvertexcount(18)]
void main(triangle in VertexInput gs_in[3], inout TriangleStream<VertexOutput> gs_out) {
for(uint vp = 0; vp < 6; ++vp) {
for(uint vertex = 0; vertex < 3; ++vertex) {
VertexOutput output;
VertexInput input = gs_in[2 - vertex];
output.m_Position = input.m_Position;
output.m_PositionCS = mul(g_FaceProjections[vp], float4(output.m_Position * float3(-1., 1., 1.), 1.));
output.m_HistoryPositionCS = output.m_PositionCS;
output.m_PositionSV = output.m_PositionCS;
output.m_Normal = input.m_Normal;
output.m_TexCoord = input.m_TexCoord;
output.m_Tangent = input.m_Tangent;
output.m_ViewportIndex = vp;
gs_out.Append(output);
}
gs_out.RestartStrip();
}
}

View File

@@ -0,0 +1,48 @@
#pragma pack_matrix(column_major)
struct VertexInput {
float3 m_Position : Position;
float4 m_PositionCS : PositionCS;
float4 m_HistoryPositionCS : HistoryPositionCS;
float3 m_Normal : Normal;
float2 m_TexCoord : TexCoord;
float4 m_Tangent : Tangent;
float4 m_PositionSV : SV_Position;
};
struct VertexOutput {
float3 m_Position : Position;
float4 m_PositionCS : PositionCS;
float4 m_HistoryPositionCS : HistoryPositionCS;
float3 m_Normal : Normal;
float2 m_TexCoord : TexCoord;
float4 m_Tangent : Tangent;
float4 m_PositionSV : SV_Position;
uint m_ViewportIndex : SV_RenderTargetArrayIndex;
};
#define CASCADE_COUNT 4
cbuffer CubeDrawConstants : register(b0) {
float4x4 g_CascadeProjections[CASCADE_COUNT];
};
[maxvertexcount(CASCADE_COUNT*3)]
void main(triangle in VertexInput gs_in[3], inout TriangleStream<VertexOutput> gs_out) {
for(uint vp = 0; vp < CASCADE_COUNT; ++vp) {
for(uint vertex = 0; vertex < 3; ++vertex) {
VertexOutput output;
VertexInput input = gs_in[vertex];
output.m_Position = input.m_Position;
output.m_PositionCS = mul(g_CascadeProjections[vp], float4(output.m_Position, 1.));
output.m_HistoryPositionCS = output.m_PositionCS;
output.m_PositionSV = output.m_PositionCS;
output.m_Normal = input.m_Normal;
output.m_TexCoord = input.m_TexCoord;
output.m_Tangent = input.m_Tangent;
output.m_ViewportIndex = vp;
gs_out.Append(output);
}
gs_out.RestartStrip();
}
}

View File

@@ -0,0 +1,109 @@
/*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#define MA_TONEMAP_SRGB_REC709 0
#define MA_TONEMAP_LINEAR_REC2020 1
struct Constants
{
float2 invDisplaySize;
float outputNits;
int m_TonemapFunction;
};
#ifdef SPIRV
[[vk::push_constant]] ConstantBuffer<Constants> g_Const;
#else
cbuffer g_Const : register(b0) { Constants g_Const; }
#endif
struct PS_INPUT
{
float4 pos : SV_POSITION;
float4 col : COLOR0;
float2 uv : TEXCOORD0;
};
float3 LinearToSRGB(float3 color)
{
// Approximately pow(color, 1.0 / 2.2)
return select(color < 0.0031308, 12.92 * color, 1.055 * pow(abs(color), 1.0 / 2.4) - 0.055);
}
float3 SRGBToLinear(float3 color)
{
// Approximately pow(color, 2.2)
return select(color < 0.04045, color / 12.92, pow(abs(color + 0.055) / 1.055, 2.4));
}
float3 Rec709ToRec2020(float3 color)
{
static const float3x3 conversion =
{
0.627402, 0.329292, 0.043306,
0.069095, 0.919544, 0.011360,
0.016394, 0.088028, 0.895578
};
return mul(conversion, color);
}
float3 LinearToST2084(float3 color)
{
float m1 = 2610.0 / 4096.0 / 4;
float m2 = 2523.0 / 4096.0 * 128;
float c1 = 3424.0 / 4096.0;
float c2 = 2413.0 / 4096.0 * 32;
float c3 = 2392.0 / 4096.0 * 32;
float3 cp = pow(abs(color), m1);
return pow((c1 + c2 * cp) / (1 + c3 * cp), m2);
}
sampler sampler0 : register(s0);
Texture2D texture0 : register(t0);
float4 main(PS_INPUT input) : SV_Target
{
float4 tex_col = texture0.Sample(sampler0, input.uv);
float4 input_col = input.col;
switch(g_Const.m_TonemapFunction) {
case MA_TONEMAP_LINEAR_REC2020:
{
input_col.rgb = SRGBToLinear(input_col.rgb);
float4 out_col = input_col * tex_col;
const float st2084max = 10000.;
const float hdrScalar = g_Const.outputNits / st2084max;
//out_col.rgb = Rec709ToRec2020(out_col.rgb);
out_col.rgb *= hdrScalar;
out_col.rgb = LinearToST2084(out_col.rgb);
return out_col;
}
default:
{
tex_col.rgb = LinearToSRGB(tex_col.rgb);
return input_col * tex_col;
}
}
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
struct Constants
{
float2 invDisplaySize;
float outputNits;
};
#ifdef SPIRV
[[vk::push_constant]] ConstantBuffer<Constants> g_Const;
#else
cbuffer g_Const : register(b0) { Constants g_Const; }
#endif
struct VS_INPUT
{
float2 pos : POSITION;
float2 uv : TEXCOORD0;
float4 col : COLOR0;
};
struct PS_INPUT
{
float4 out_pos : SV_POSITION;
float4 out_col : COLOR0;
float2 out_uv : TEXCOORD0;
};
PS_INPUT main(VS_INPUT input)
{
PS_INPUT output;
output.out_pos.xy = input.pos.xy * g_Const.invDisplaySize * float2(2.0, -2.0) + float2(-1.0, 1.0);
output.out_pos.zw = float2(0, 1);
output.out_col = input.col;
output.out_uv = input.uv;
return output;
}

View File

@@ -0,0 +1,41 @@
#pragma pack_matrix(column_major)
struct VertexInput {
float3 m_Position : Position;
float3 m_Normal : Normal;
float2 m_TexCoord : TexCoord;
float4 m_Tangent : Tangent;
float4 m_InstanceTransform[3] : InstanceTransform;
};
struct VertexOutput {
float3 m_Position : Position;
float3 m_Normal : Normal;
float2 m_TexCoord : TexCoord;
float4 m_Tangent : Tangent;
float4 m_PositionSV : SV_Position;
float4 m_PositionCS : PositionCS;
float4 m_HistoryPositionCS : HistoryPositionCS;
};
cbuffer VertexConstants : register(b0) {
float4x4 g_FaceProjection;
};
#include "manul/draw_constants.hlsli"
VertexOutput main(in VertexInput vs_in) {
VertexOutput result;
float4x3 model_view = GetModelView();
float4x4 instance_transform = transpose(float4x4(vs_in.m_InstanceTransform[0], vs_in.m_InstanceTransform[1], vs_in.m_InstanceTransform[2], float4(0., 0., 0., 1.)));
float3 position = mul(float4(vs_in.m_Position, 1.), instance_transform).xyz;
result.m_Position = mul(float4(position, 1.), model_view).xyz;
result.m_Normal = mul(float4(mul(float4(vs_in.m_Normal, 0.), instance_transform)), model_view).xyz;
result.m_TexCoord = vs_in.m_TexCoord;
result.m_Tangent.xyz = mul(float4(mul(float4(vs_in.m_Tangent.xyz, 0.), instance_transform)), model_view).xyz;
result.m_Tangent.w = vs_in.m_Tangent.w;
result.m_PositionSV = mul(g_FaceProjection, float4(result.m_Position, 1.));
result.m_PositionCS = result.m_PositionSV;
result.m_HistoryPositionCS = result.m_PositionSV;
return result;
}

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